diff --git a/.cache/YAPF/0.40.2/Grammar-py3.10.13.final.0.pickle b/.cache/YAPF/0.40.2/Grammar-py3.10.13.final.0.pickle new file mode 100644 index 0000000..20238c4 Binary files /dev/null and b/.cache/YAPF/0.40.2/Grammar-py3.10.13.final.0.pickle differ diff --git a/.cache/YAPF/0.40.2/PatternGrammar-py3.10.13.final.0.pickle b/.cache/YAPF/0.40.2/PatternGrammar-py3.10.13.final.0.pickle new file mode 100644 index 0000000..0ef11fa Binary files /dev/null and b/.cache/YAPF/0.40.2/PatternGrammar-py3.10.13.final.0.pickle differ diff --git a/.cache/nix/binary-cache-v6.sqlite b/.cache/nix/binary-cache-v6.sqlite new file mode 100644 index 0000000..87edd48 Binary files /dev/null and b/.cache/nix/binary-cache-v6.sqlite differ diff --git a/.cache/nix/binary-cache-v6.sqlite-journal b/.cache/nix/binary-cache-v6.sqlite-journal new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b b/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b new file mode 100644 index 0000000..bbed315 Binary files /dev/null and b/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b differ diff --git a/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b.body b/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b.body new file mode 100644 index 0000000..88968ae --- /dev/null +++ b/.cache/pip/http-v2/0/5/d/2/b/05d2b64c05880a9f3c9fc979c15cf2e6a96deb4d9953f4cf863dd87b.body @@ -0,0 +1,104 @@ +Metadata-Version: 2.1 +Name: sniffio +Version: 1.3.1 +Summary: Sniff out which async library your code is running under +Author-email: "Nathaniel J. Smith" +License: MIT OR Apache-2.0 +Project-URL: Homepage, https://github.com/python-trio/sniffio +Project-URL: Documentation, https://sniffio.readthedocs.io/ +Project-URL: Changelog, https://sniffio.readthedocs.io/en/latest/history.html +Keywords: async,trio,asyncio +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Framework :: Trio +Classifier: Framework :: AsyncIO +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Intended Audience :: Developers +Classifier: Development Status :: 5 - Production/Stable +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: LICENSE.APACHE2 +License-File: LICENSE.MIT + +.. image:: https://img.shields.io/badge/chat-join%20now-blue.svg + :target: https://gitter.im/python-trio/general + :alt: Join chatroom + +.. image:: https://img.shields.io/badge/docs-read%20now-blue.svg + :target: https://sniffio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/pypi/v/sniffio.svg + :target: https://pypi.org/project/sniffio + :alt: Latest PyPi version + +.. image:: https://img.shields.io/conda/vn/conda-forge/sniffio.svg + :target: https://anaconda.org/conda-forge/sniffio + :alt: Latest conda-forge version + +.. image:: https://travis-ci.org/python-trio/sniffio.svg?branch=master + :target: https://travis-ci.org/python-trio/sniffio + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-trio/sniffio/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-trio/sniffio + :alt: Test coverage + +================================================================= +sniffio: Sniff out which async library your code is running under +================================================================= + +You're writing a library. You've decided to be ambitious, and support +multiple async I/O packages, like `Trio +`__, and `asyncio +`__, and ... You've +written a bunch of clever code to handle all the differences. But... +how do you know *which* piece of clever code to run? + +This is a tiny package whose only purpose is to let you detect which +async library your code is running under. + +* Documentation: https://sniffio.readthedocs.io + +* Bug tracker and source code: https://github.com/python-trio/sniffio + +* License: MIT or Apache License 2.0, your choice + +* Contributor guide: https://trio.readthedocs.io/en/latest/contributing.html + +* Code of conduct: Contributors are requested to follow our `code of + conduct + `_ + in all project spaces. + +This library is maintained by the Trio project, as a service to the +async Python community as a whole. + + +Quickstart +---------- + +.. code-block:: python3 + + from sniffio import current_async_library + import trio + import asyncio + + async def print_library(): + library = current_async_library() + print("This is:", library) + + # Prints "This is trio" + trio.run(print_library) + + # Prints "This is asyncio" + asyncio.run(print_library()) + +For more details, including how to add support to new async libraries, +`please peruse our fine manual `__. diff --git a/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2 b/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2 new file mode 100644 index 0000000..1d2d160 Binary files /dev/null and b/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2 differ diff --git a/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2.body b/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2.body new file mode 100644 index 0000000..9da6d09 --- /dev/null +++ b/.cache/pip/http-v2/0/8/c/d/4/08cd4a2f60f94aa783b4d52764bea890f13d4d3c4d5bc9340e1216f2.body @@ -0,0 +1,154 @@ +Metadata-Version: 2.1 +Name: urllib3 +Version: 2.2.1 +Summary: HTTP library with thread-safe connection pooling, file post, and more. +Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst +Project-URL: Documentation, https://urllib3.readthedocs.io +Project-URL: Code, https://github.com/urllib3/urllib3 +Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues +Author-email: Andrey Petrov +Maintainer-email: Seth Michael Larson , Quentin Pradet , Illia Volochii +License-File: LICENSE.txt +Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.8 +Provides-Extra: brotli +Requires-Dist: brotli>=1.0.9; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi>=0.8.0; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: h2 +Requires-Dist: h2<5,>=4; extra == 'h2' +Provides-Extra: socks +Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' +Provides-Extra: zstd +Requires-Dist: zstandard>=0.18.0; extra == 'zstd' +Description-Content-Type: text/markdown + +

+ +![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) + +

+ +

+ PyPI Version + Python Versions + Join our Discord + Coverage Status + Build Status on GitHub + Documentation Status
+ OpenSSF Scorecard + SLSA 3 + CII Best Practices +

+ +urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the +Python ecosystem already uses urllib3 and you should too. +urllib3 brings many critical features that are missing from the Python +standard libraries: + +- Thread safety. +- Connection pooling. +- Client-side SSL/TLS verification. +- File uploads with multipart encoding. +- Helpers for retrying requests and dealing with HTTP redirects. +- Support for gzip, deflate, brotli, and zstd encoding. +- Proxy support for HTTP and SOCKS. +- 100% test coverage. + +urllib3 is powerful and easy to use: + +```python3 +>>> import urllib3 +>>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") +>>> resp.status +200 +>>> resp.data +b"User-agent: *\nDisallow: /deny\n" +``` + +## Installing + +urllib3 can be installed with [pip](https://pip.pypa.io): + +```bash +$ python -m pip install urllib3 +``` + +Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): + +```bash +$ git clone https://github.com/urllib3/urllib3.git +$ cd urllib3 +$ pip install . +``` + + +## Documentation + +urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). + + +## Community + +urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and +collaborating with other contributors. Drop by and say hello 👋 + + +## Contributing + +urllib3 happily accepts contributions. Please see our +[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) +for some tips on getting started. + + +## Security Disclosures + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure with maintainers. + + +## Maintainers + +- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) +- [@pquentin](https://github.com/pquentin) (Quentin Pradet) +- [@illia-v](https://github.com/illia-v) (Illia Volochii) +- [@theacodes](https://github.com/theacodes) (Thea Flowers) +- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) +- [@lukasa](https://github.com/lukasa) (Cory Benfield) +- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) +- [@shazow](https://github.com/shazow) (Andrey Petrov) + +👋 + + +## Sponsorship + +If your company benefits from this library, please consider [sponsoring its +development](https://urllib3.readthedocs.io/en/latest/sponsors.html). + + +## For Enterprise + +Professional support for urllib3 is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme diff --git a/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26 b/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26 new file mode 100644 index 0000000..439bf60 Binary files /dev/null and b/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26 differ diff --git a/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26.body b/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26.body new file mode 100644 index 0000000..f739c3d Binary files /dev/null and b/.cache/pip/http-v2/0/9/e/f/5/09ef58260179e8db2673665919e5556942a78290f8c59e57867a7a26.body differ diff --git a/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4 b/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4 new file mode 100644 index 0000000..95b0c6b Binary files /dev/null and b/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4 differ diff --git a/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4.body b/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4.body new file mode 100644 index 0000000..7a4250e Binary files /dev/null and b/.cache/pip/http-v2/0/9/f/8/9/09f89c3ce88983d8d9b404612eb53deb91ab35d4b1e2372a172624a4.body differ diff --git a/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05 b/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05 new file mode 100644 index 0000000..79ae4e3 Binary files /dev/null and b/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05 differ diff --git a/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05.body b/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05.body new file mode 100644 index 0000000..27626d7 Binary files /dev/null and b/.cache/pip/http-v2/0/a/e/8/4/0ae84cda1068eb8069d0d031ff1b592b6ce0646c279bf59396ff2d05.body differ diff --git a/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e b/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e new file mode 100644 index 0000000..708f194 Binary files /dev/null and b/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e differ diff --git a/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e.body b/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e.body new file mode 100644 index 0000000..25e0662 Binary files /dev/null and b/.cache/pip/http-v2/1/4/f/1/2/14f121091f25d366fa1bc14c275f2a189a39685dc00e522abbca921e.body differ diff --git a/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00 b/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00 new file mode 100644 index 0000000..417ce32 Binary files /dev/null and b/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00 differ diff --git a/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00.body b/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00.body new file mode 100644 index 0000000..d7cca6a Binary files /dev/null and b/.cache/pip/http-v2/1/6/d/c/5/16dc56ee28351df8921e7f9bf0a91c25e55f32362acd800d8f824e00.body differ diff --git a/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5 b/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5 new file mode 100644 index 0000000..538ccb2 Binary files /dev/null and b/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5 differ diff --git a/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5.body b/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5.body new file mode 100644 index 0000000..4202988 Binary files /dev/null and b/.cache/pip/http-v2/1/7/d/7/f/17d7f49ecab097b2b1e43090bbed868dcda629b86f1bb146723783c5.body differ diff --git a/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c b/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c new file mode 100644 index 0000000..0dc7db8 Binary files /dev/null and b/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c differ diff --git a/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c.body b/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c.body new file mode 100644 index 0000000..5089b4d --- /dev/null +++ b/.cache/pip/http-v2/1/b/e/a/3/1bea3dfa7a2a5aa432222b0751bb09891641b3dd8f005ac2b1176c2c.body @@ -0,0 +1,66 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.11.0 +Summary: Backported and Experimental Type Hints for Python 3.8+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing-extensions.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) – +[PyPI](https://pypi.org/project/typing-extensions/) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +`typing_extensions` is treated specially by static type checkers such as +mypy and pyright. Objects defined in `typing_extensions` are treated the same +way as equivalent forms in `typing`. + +`typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version will be incremented only for backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`, +where `x.y` is the first version that includes all features you need. + +## Included items + +See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a +complete listing of module contents. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md) +for how to contribute to `typing_extensions`. + diff --git a/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c b/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c new file mode 100644 index 0000000..8f59d44 Binary files /dev/null and b/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c differ diff --git a/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c.body b/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c.body new file mode 100644 index 0000000..38e84ca Binary files /dev/null and b/.cache/pip/http-v2/1/e/7/0/5/1e705f80aeff7402c2f592879a76b7b250000ddda9724b97e4f4b54c.body differ diff --git a/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa b/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa new file mode 100644 index 0000000..8917dc0 Binary files /dev/null and b/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa differ diff --git a/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa.body b/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa.body new file mode 100644 index 0000000..5be93d5 --- /dev/null +++ b/.cache/pip/http-v2/2/1/c/5/5/21c55d680710dd45fcb6d00ef75d913918cb99632afe02745c9b6baa.body @@ -0,0 +1,677 @@ +Metadata-Version: 2.3 +Name: openai +Version: 1.23.6 +Summary: The official Python library for the openai API +Project-URL: Homepage, https://github.com/openai/openai-python +Project-URL: Repository, https://github.com/openai/openai-python +Author-email: OpenAI +License-Expression: Apache-2.0 +License-File: LICENSE +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.7.1 +Requires-Dist: anyio<5,>=3.5.0 +Requires-Dist: cached-property; python_version < '3.8' +Requires-Dist: distro<2,>=1.7.0 +Requires-Dist: httpx<1,>=0.23.0 +Requires-Dist: pydantic<3,>=1.9.0 +Requires-Dist: sniffio +Requires-Dist: tqdm>4 +Requires-Dist: typing-extensions<5,>=4.7 +Provides-Extra: datalib +Requires-Dist: numpy>=1; extra == 'datalib' +Requires-Dist: pandas-stubs>=1.1.0.11; extra == 'datalib' +Requires-Dist: pandas>=1.2.3; extra == 'datalib' +Description-Content-Type: text/markdown + +# OpenAI Python API library + +[![PyPI version](https://img.shields.io/pypi/v/openai.svg)](https://pypi.org/project/openai/) + +The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.7+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). + +It is generated from our [OpenAPI specification](https://github.com/openai/openai-openapi) with [Stainless](https://stainlessapi.com/). + +## Documentation + +The REST API documentation can be found [on platform.openai.com](https://platform.openai.com/docs). The full API of this library can be found in [api.md](https://github.com/openai/openai-python/tree/main/api.md). + +## Installation + +> [!IMPORTANT] +> The SDK was rewritten in v1, which was released November 6th 2023. See the [v1 migration guide](https://github.com/openai/openai-python/discussions/742), which includes scripts to automatically update your code. + +```sh +# install from PyPI +pip install openai +``` + +## Usage + +The full API of this library can be found in [api.md](https://github.com/openai/openai-python/tree/main/api.md). + +```python +import os +from openai import OpenAI + +client = OpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), +) + +chat_completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", +) +``` + +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `OPENAI_API_KEY="My API Key"` to your `.env` file +so that your API Key is not stored in source control. + +### Polling Helpers + +When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes +helper functions which will poll the status until it reaches a terminal state and then return the resulting object. +If an API method results in an action which could benefit from polling there will be a corresponding version of the +method ending in '\_and_poll'. + +For instance to create a Run and poll until it reaches a terminal state you can run: + +```python +run = client.beta.threads.runs.create_and_poll( + thread_id=thread.id, + assistant_id=assistant.id, +) +``` + +More information on the lifecycle of a Run can be found in the [Run Lifecycle Documentation](https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle) + +### Bulk Upload Helpers + +When creating an interacting with vector stores, you can use the polling helpers to monitor the status of operations. +For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once. + +```python +sample_files = [Path("sample-paper.pdf"), ...] + +batch = await client.vector_stores.file_batches.upload_and_poll( + store.id, + files=sample_files, +) +``` + +### Streaming Helpers + +The SDK also includes helpers to process streams and handle the incoming events. + +```python +with client.beta.threads.runs.stream( + thread_id=thread.id, + assistant_id=assistant.id, + instructions="Please address the user as Jane Doe. The user has a premium account.", +) as stream: + for event in stream: + # Print the text from text delta events + if event.type == "thread.message.delta" and event.data.delta.content: + print(event.data.delta.content[0].text) +``` + +More information on streaming helpers can be found in the dedicated documentation: [helpers.md](https://github.com/openai/openai-python/tree/main/helpers.md) + +## Async usage + +Simply import `AsyncOpenAI` instead of `OpenAI` and use `await` with each API call: + +```python +import os +import asyncio +from openai import AsyncOpenAI + +client = AsyncOpenAI( + # This is the default and can be omitted + api_key=os.environ.get("OPENAI_API_KEY"), +) + + +async def main() -> None: + chat_completion = await client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", + ) + + +asyncio.run(main()) +``` + +Functionality between the synchronous and asynchronous clients is otherwise identical. + +## Streaming responses + +We provide support for streaming responses using Server Side Events (SSE). + +```python +from openai import OpenAI + +client = OpenAI() + +stream = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Say this is a test"}], + stream=True, +) +for chunk in stream: + print(chunk.choices[0].delta.content or "", end="") +``` + +The async client uses the exact same interface. + +```python +from openai import AsyncOpenAI + +client = AsyncOpenAI() + + +async def main(): + stream = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Say this is a test"}], + stream=True, + ) + async for chunk in stream: + print(chunk.choices[0].delta.content or "", end="") + + +asyncio.run(main()) +``` + +## Module-level client + +> [!IMPORTANT] +> We highly recommend instantiating client instances instead of relying on the global client. + +We also expose a global client instance that is accessible in a similar fashion to versions prior to v1. + +```py +import openai + +# optional; defaults to `os.environ['OPENAI_API_KEY']` +openai.api_key = '...' + +# all client options can be configured just like the `OpenAI` instantiation counterpart +openai.base_url = "https://..." +openai.default_headers = {"x-foo": "true"} + +completion = openai.chat.completions.create( + model="gpt-4", + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.choices[0].message.content) +``` + +The API is the exact same as the standard client instance based API. + +This is intended to be used within REPLs or notebooks for faster iteration, **not** in application code. + +We recommend that you always instantiate a client (e.g., with `client = OpenAI()`) in application code because: + +- It can be difficult to reason about where client options are configured +- It's not possible to change certain client options without potentially causing race conditions +- It's harder to mock for testing purposes +- It's not possible to control cleanup of network connections + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Pagination + +List methods in the OpenAI API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +import openai + +client = OpenAI() + +all_jobs = [] +# Automatically fetches more pages as needed. +for job in client.fine_tuning.jobs.list( + limit=20, +): + # Do something with job here + all_jobs.append(job) +print(all_jobs) +``` + +Or, asynchronously: + +```python +import asyncio +import openai + +client = AsyncOpenAI() + + +async def main() -> None: + all_jobs = [] + # Iterate through items across all pages, issuing requests as needed. + async for job in client.fine_tuning.jobs.list( + limit=20, + ): + all_jobs.append(job) + print(all_jobs) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.fine_tuning.jobs.list( + limit=20, +) + +print(f"next page cursor: {first_page.after}") # => "next page cursor: ..." +for job in first_page.data: + print(job.id) + +# Remove `await` for non-async usage. +``` + +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from openai import OpenAI + +client = OpenAI() + +completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Can you generate an example json object describing a fruit?", + } + ], + model="gpt-3.5-turbo-1106", + response_format={"type": "json_object"}, +) +``` + +## File uploads + +Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. + +```python +from pathlib import Path +from openai import OpenAI + +client = OpenAI() + +client.files.create( + file=Path("input.jsonl"), + purpose="fine-tune", +) +``` + +The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `openai.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `openai.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `openai.APIError`. + +```python +import openai +from openai import OpenAI + +client = OpenAI() + +try: + client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123", + ) +except openai.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except openai.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except openai.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) +``` + +Error codes are as followed: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +### Retries + +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. + +You can use the `max_retries` option to configure or disable retry settings: + +```python +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I get the name of the current day in Node.js?", + } + ], + model="gpt-3.5-turbo", +) +``` + +### Timeouts + +By default requests time out after 10 minutes. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: + +```python +from openai import OpenAI + +# Configure the default for all requests: +client = OpenAI( + # 20 seconds (default is 10 minutes) + timeout=20.0, +) + +# More granular control: +client = OpenAI( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5 * 1000).chat.completions.create( + messages=[ + { + "role": "user", + "content": "How can I list all files in a directory using Python?", + } + ], + model="gpt-3.5-turbo", +) +``` + +On timeout, an `APITimeoutError` is thrown. + +Note that requests that time out are [retried twice by default](https://github.com/openai/openai-python/tree/main/#retries). + +## Advanced + +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `OPENAI_LOG` to `debug`. + +```shell +$ export OPENAI_LOG=debug +``` + +### How to tell whether `None` means `null` or missing + +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: + +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` + +### Accessing raw response data (e.g. headers) + +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., + +```py +from openai import OpenAI + +client = OpenAI() +response = client.chat.completions.with_raw_response.create( + messages=[{ + "role": "user", + "content": "Say this is a test", + }], + model="gpt-3.5-turbo", +) +print(response.headers.get('X-My-Header')) + +completion = response.parse() # get the object that `chat.completions.create()` would have returned +print(completion) +``` + +These methods return an [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version. + +For the sync client this will mostly be the same with the exception +of `content` & `text` will be methods instead of properties. In the +async client, all methods will be async. + +A migration script will be provided & the migration in general should +be smooth. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +As such, `.with_streaming_response` methods return a different [`APIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object, and the async client returns an [`AsyncAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_response.py) object. + +```python +with client.chat.completions.with_streaming_response.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model="gpt-3.5-turbo", +) as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. + +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) will be respected when making this +request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + +### Configuring the HTTP client + +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: + +- Support for proxies +- Custom transports +- Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality + +```python +from openai import OpenAI, DefaultHttpxClient + +client = OpenAI( + # Or use the `OPENAI_BASE_URL` env var + base_url="http://my.test.server.example.com:8083", + http_client=DefaultHttpxClient( + proxies="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +## Microsoft Azure OpenAI + +To use this library with [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview), use the `AzureOpenAI` +class instead of the `OpenAI` class. + +> [!IMPORTANT] +> The Azure API shape differs from the core API shape which means that the static types for responses / params +> won't always be correct. + +```py +from openai import AzureOpenAI + +# gets the API Key from environment variable AZURE_OPENAI_API_KEY +client = AzureOpenAI( + # https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning + api_version="2023-07-01-preview", + # https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource + azure_endpoint="https://example-endpoint.openai.azure.com", +) + +completion = client.chat.completions.create( + model="deployment-name", # e.g. gpt-35-instant + messages=[ + { + "role": "user", + "content": "How do I output all files in a directory using Python?", + }, + ], +) +print(completion.to_json()) +``` + +In addition to the options provided in the base `OpenAI` client, the following options are provided: + +- `azure_endpoint` (or the `AZURE_OPENAI_ENDPOINT` environment variable) +- `azure_deployment` +- `api_version` (or the `OPENAI_API_VERSION` environment variable) +- `azure_ad_token` (or the `AZURE_OPENAI_AD_TOKEN` environment variable) +- `azure_ad_token_provider` + +An example of using the client with Azure Active Directory can be found [here](https://github.com/openai/openai-python/blob/main/examples/azure_ad.py). + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_. +3. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-python/issues) with questions, bugs, or suggestions. + +## Requirements + +Python 3.7 or higher. diff --git a/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65 b/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65 new file mode 100644 index 0000000..22e238d Binary files /dev/null and b/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65 differ diff --git a/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65.body b/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65.body new file mode 100644 index 0000000..e447de3 Binary files /dev/null and b/.cache/pip/http-v2/2/5/4/7/1/254717192984c8b0151ae5d45497468402578c2b32d6685780c94c65.body differ diff --git a/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8 b/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8 new file mode 100644 index 0000000..92342dc Binary files /dev/null and b/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8 differ diff --git a/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8.body b/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8.body new file mode 100644 index 0000000..e1f91b4 Binary files /dev/null and b/.cache/pip/http-v2/2/6/4/1/7/264179aeab1d8942e5f72419f2c39e8a5577779f5155b081e3ad0fd8.body differ diff --git a/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257 b/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257 new file mode 100644 index 0000000..f002157 Binary files /dev/null and b/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257 differ diff --git a/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257.body b/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257.body new file mode 100644 index 0000000..b711aef Binary files /dev/null and b/.cache/pip/http-v2/2/a/4/1/c/2a41cb8e405637e51ada370419ba2d90cff5ec7779741a4b3b53a257.body differ diff --git a/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe b/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe new file mode 100644 index 0000000..9491f54 Binary files /dev/null and b/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe differ diff --git a/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe.body b/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe.body new file mode 100644 index 0000000..4e1f286 --- /dev/null +++ b/.cache/pip/http-v2/2/b/a/0/7/2ba07654a377166798ecf423f7da2210f0e2e245b7b70480ceb852fe.body @@ -0,0 +1,262 @@ +Metadata-Version: 2.1 +Name: aiohttp-retry +Version: 2.8.3 +Summary: Simple retry client for aiohttp +Home-page: https://github.com/inyutin/aiohttp_retry +Author: Dmitry Inyutin +Author-email: inyutin.da@gmail.com +License: MIT +Keywords: aiohttp retry client +Platform: any +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: aiohttp + +# Simple aiohttp retry client + +[![codecov](https://codecov.io/gh/inyutin/aiohttp_retry/branch/master/graph/badge.svg?token=ZWGAXSF1SP)](https://codecov.io/gh/inyutin/aiohttp_retry) + +Python 3.7 or higher. + +**Install**: `pip install aiohttp-retry`. + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/inyutin) + + +### Breaking API changes +- Everything between [2.7.0 - 2.8.3) is yanked. +There is a bug with evaluate_response_callback, it led to infinite retries + +- 2.8.0 is incorrect and yanked. +https://github.com/inyutin/aiohttp_retry/issues/79 + +- Since 2.5.6 this is a new parameter in ```get_timeout``` func called "response". +If you have defined your own ```RetryOptions```, you should add this param into it. +Issue about this: https://github.com/inyutin/aiohttp_retry/issues/59 + +### Examples of usage: +```python +from aiohttp_retry import RetryClient, ExponentialRetry + +async def main(): + retry_options = ExponentialRetry(attempts=1) + retry_client = RetryClient(raise_for_status=False, retry_options=retry_options) + async with retry_client.get('https://ya.ru') as response: + print(response.status) + + await retry_client.close() +``` + +```python +from aiohttp import ClientSession +from aiohttp_retry import RetryClient + +async def main(): + client_session = ClientSession() + retry_client = RetryClient(client_session=client_session) + async with retry_client.get('https://ya.ru') as response: + print(response.status) + + await client_session.close() +``` + +```python +from aiohttp_retry import RetryClient, RandomRetry + +async def main(): + retry_options = RandomRetry(attempts=1) + retry_client = RetryClient(raise_for_status=False, retry_options=retry_options) + + response = await retry_client.get('/ping') + print(response.status) + + await retry_client.close() +``` + +```python +from aiohttp_retry import RetryClient + +async def main(): + async with RetryClient() as client: + async with client.get('https://ya.ru') as response: + print(response.status) +``` + +You can change parameters between attempts by passing multiple requests params: +```python +from aiohttp_retry import RetryClient, RequestParams, ExponentialRetry + +async def main(): + retry_client = RetryClient(raise_for_status=False) + + async with retry_client.requests( + params_list=[ + RequestParams( + method='GET', + url='https://ya.ru', + ), + RequestParams( + method='GET', + url='https://ya.ru', + headers={'some_header': 'some_value'}, + ), + ] + ) as response: + print(response.status) + + await retry_client.close() +``` + +You can also add some logic, F.E. logging, on failures by using trace mechanic. +```python +import logging +import sys +from types import SimpleNamespace + +from aiohttp import ClientSession, TraceConfig, TraceRequestStartParams + +from aiohttp_retry import RetryClient, ExponentialRetry + + +handler = logging.StreamHandler(sys.stdout) +logging.basicConfig(handlers=[handler]) +logger = logging.getLogger(__name__) +retry_options = ExponentialRetry(attempts=2) + + +async def on_request_start( + session: ClientSession, + trace_config_ctx: SimpleNamespace, + params: TraceRequestStartParams, +) -> None: + current_attempt = trace_config_ctx.trace_request_ctx['current_attempt'] + if retry_options.attempts <= current_attempt: + logger.warning('Wow! We are in last attempt') + + +async def main(): + trace_config = TraceConfig() + trace_config.on_request_start.append(on_request_start) + retry_client = RetryClient(retry_options=retry_options, trace_configs=[trace_config]) + + response = await retry_client.get('https://httpstat.us/503', ssl=False) + print(response.status) + + await retry_client.close() +``` +Look tests for more examples. \ +**Be aware: last request returns as it is.** +**If the last request ended with exception, that this exception will be raised from RetryClient request** + +### Documentation +`RetryClient` takes the same arguments as ClientSession[[docs](https://docs.aiohttp.org/en/stable/client_reference.html)] \ +`RetryClient` has methods: +- request +- get +- options +- head +- post +- put +- patch +- put +- delete + +They are same as for `ClientSession`, but take one possible additional argument: +```python +class RetryOptionsBase: + def __init__( + self, + attempts: int = 3, # How many times we should retry + statuses: Optional[Iterable[int]] = None, # On which statuses we should retry + exceptions: Optional[Iterable[Type[Exception]]] = None, # On which exceptions we should retry + retry_all_server_errors: bool = True, # If should retry all 500 errors or not + # a callback that will run on response to decide if retry + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + ... + + @abc.abstractmethod + def get_timeout(self, attempt: int, response: Optional[Response] = None) -> float: + raise NotImplementedError + +``` +You can specify `RetryOptions` both for `RetryClient` and it's methods. +`RetryOptions` in methods override `RetryOptions` defined in `RetryClient` constructor. + +**Important**: by default all 5xx responses are retried + statuses you specified as ```statuses``` param +If you will pass ```retry_all_server_errors=False``` than you can manually set what 5xx errors to retry. + +You can define your own timeouts logic or use: +- ```ExponentialRetry``` with exponential backoff +- ```RandomRetry``` for random backoff +- ```ListRetry``` with backoff you predefine by list +- ```FibonacciRetry``` with backoff that looks like fibonacci sequence +- ```JitterRetry``` exponential retry with a bit of randomness + +**Important**: you can proceed server response as an parameter for calculating next timeout. +However this response can be None, server didn't make a response or you have set up ```raise_for_status=True``` +Look here for an example: https://github.com/inyutin/aiohttp_retry/issues/59 + +Additionally, you can specify ```evaluate_response_callback```. It receive a ```ClientResponse``` and decide to retry or not by returning a bool. +It can be useful, if server API sometimes response with malformed data. + +#### Request Trace Context +`RetryClient` add *current attempt number* to `request_trace_ctx` (see examples, +for more info see [aiohttp doc](https://docs.aiohttp.org/en/stable/client_advanced.html#aiohttp-client-tracing)). + +### Change parameters between retries +`RetryClient` also has a method called `requests`. This method should be used if you want to make requests with different params. +```python +@dataclass +class RequestParams: + method: str + url: _RAW_URL_TYPE + trace_request_ctx: Optional[Dict[str, Any]] = None + kwargs: Optional[Dict[str, Any]] = None +``` + +```python +def requests( + self, + params_list: List[RequestParams], + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, +) -> _RequestContext: +``` + +You can find an example of usage above or in tests. +But basically `RequestParams` is a structure to define params for `ClientSession.request` func. +`method`, `url`, `headers` `trace_request_ctx` defined outside kwargs, because they are popular. + +There is also an old way to change URL between retries by specifying ```url``` as list of urls. Example: +```python +from aiohttp_retry import RetryClient + +retry_client = RetryClient() +async with retry_client.get(url=['/internal_error', '/ping']) as response: + text = await response.text() + assert response.status == 200 + assert text == 'Ok!' + +await retry_client.close() +``` + +In this example we request ```/interval_error```, fail and then successfully request ```/ping```. +If you specify less urls than ```attempts``` number in ```RetryOptions```, ```RetryClient``` will request last url at last attempts. +This means that in example above we would request ```/ping``` once again in case of failure. + +### Types + +`aiohttp_retry` is a typed project. It should be fully compatablie with mypy. + +It also introduce one special type: +``` +ClientType = Union[ClientSession, RetryClient] +``` + +This type can be imported by ```from aiohttp_retry.types import ClientType``` diff --git a/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d b/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d new file mode 100644 index 0000000..5e2ea57 Binary files /dev/null and b/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d differ diff --git a/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d.body b/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d.body new file mode 100644 index 0000000..5e8a550 Binary files /dev/null and b/.cache/pip/http-v2/2/e/2/d/8/2e2d841f57ca6a43dfc96af93374304dda5b757176df2cce7ca2b18d.body differ diff --git a/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199 b/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199 new file mode 100644 index 0000000..aaf3630 Binary files /dev/null and b/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199 differ diff --git a/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199.body b/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199.body new file mode 100644 index 0000000..af6339a Binary files /dev/null and b/.cache/pip/http-v2/3/0/9/6/6/3096620e700ab3c794191e9340f3b1d079af5462000376c484596199.body differ diff --git a/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d b/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d new file mode 100644 index 0000000..15443b8 Binary files /dev/null and b/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d differ diff --git a/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d.body b/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d.body new file mode 100644 index 0000000..0e20095 --- /dev/null +++ b/.cache/pip/http-v2/3/1/4/0/8/314089b255e89c0174c64ebd09b09a15c0497725835d97cd788fb78d.body @@ -0,0 +1,607 @@ +Metadata-Version: 2.3 +Name: httpcore +Version: 1.0.5 +Summary: A minimal low-level HTTP client. +Project-URL: Documentation, https://www.encode.io/httpcore +Project-URL: Homepage, https://www.encode.io/httpcore/ +Project-URL: Source, https://github.com/encode/httpcore +Author-email: Tom Christie +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 3 - Alpha +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: certifi +Requires-Dist: h11<0.15,>=0.13 +Provides-Extra: asyncio +Requires-Dist: anyio<5.0,>=4.0; extra == 'asyncio' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: trio +Requires-Dist: trio<0.26.0,>=0.22.0; extra == 'trio' +Description-Content-Type: text/markdown + +# HTTP Core + +[![Test Suite](https://github.com/encode/httpcore/workflows/Test%20Suite/badge.svg)](https://github.com/encode/httpcore/actions) +[![Package version](https://badge.fury.io/py/httpcore.svg)](https://pypi.org/project/httpcore/) + +> *Do one thing, and do it well.* + +The HTTP Core package provides a minimal low-level HTTP client, which does +one thing only. Sending HTTP requests. + +It does not provide any high level model abstractions over the API, +does not handle redirects, multipart uploads, building authentication headers, +transparent HTTP caching, URL parsing, session cookie handling, +content or charset decoding, handling JSON, environment based configuration +defaults, or any of that Jazz. + +Some things HTTP Core does do: + +* Sending HTTP requests. +* Thread-safe / task-safe connection pooling. +* HTTP(S) proxy & SOCKS proxy support. +* Supports HTTP/1.1 and HTTP/2. +* Provides both sync and async interfaces. +* Async backend support for `asyncio` and `trio`. + +## Requirements + +Python 3.8+ + +## Installation + +For HTTP/1.1 only support, install with: + +```shell +$ pip install httpcore +``` + +There are also a number of optional extras available... + +```shell +$ pip install httpcore['asyncio,trio,http2,socks'] +``` + +# Sending requests + +Send an HTTP request: + +```python +import httpcore + +response = httpcore.request("GET", "https://www.example.com/") + +print(response) +# +print(response.status) +# 200 +print(response.headers) +# [(b'Accept-Ranges', b'bytes'), (b'Age', b'557328'), (b'Cache-Control', b'max-age=604800'), ...] +print(response.content) +# b'\n\n\nExample Domain\n\n\n ...' +``` + +The top-level `httpcore.request()` function is provided for convenience. In practice whenever you're working with `httpcore` you'll want to use the connection pooling functionality that it provides. + +```python +import httpcore + +http = httpcore.ConnectionPool() +response = http.request("GET", "https://www.example.com/") +``` + +Once you're ready to get going, [head over to the documentation](https://www.encode.io/httpcore/). + +## Motivation + +You *probably* don't want to be using HTTP Core directly. It might make sense if +you're writing something like a proxy service in Python, and you just want +something at the lowest possible level, but more typically you'll want to use +a higher level client library, such as `httpx`. + +The motivation for `httpcore` is: + +* To provide a reusable low-level client library, that other packages can then build on top of. +* To provide a *really clear interface split* between the networking code and client logic, + so that each is easier to understand and reason about in isolation. + +## Dependencies + +The `httpcore` package has the following dependencies... + +* `h11` +* `certifi` + +And the following optional extras... + +* `anyio` - Required by `pip install httpcore['asyncio']`. +* `trio` - Required by `pip install httpcore['trio']`. +* `h2` - Required by `pip install httpcore['http2']`. +* `socksio` - Required by `pip install httpcore['socks']`. + +## Versioning + +We use [SEMVER for our versioning policy](https://semver.org/). + +For changes between package versions please see our [project changelog](CHANGELOG.md). + +We recommend pinning your requirements either the most current major version, or a more specific version range: + +```python +pip install 'httpcore==1.*' +``` +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## 1.0.5 (March 27th, 2024) + +- Handle `EndOfStream` exception for anyio backend. (#899) +- Allow trio `0.25.*` series in package dependancies. (#903) + +## 1.0.4 (February 21st, 2024) + +- Add `target` request extension. (#888) +- Fix support for connection `Upgrade` and `CONNECT` when some data in the stream has been read. (#882) + +## 1.0.3 (February 13th, 2024) + +- Fix support for async cancellations. (#880) +- Fix trace extension when used with socks proxy. (#849) +- Fix SSL context for connections using the "wss" scheme (#869) + +## 1.0.2 (November 10th, 2023) + +- Fix `float("inf")` timeouts in `Event.wait` function. (#846) + +## 1.0.1 (November 3rd, 2023) + +- Fix pool timeout to account for the total time spent retrying. (#823) +- Raise a neater RuntimeError when the correct async deps are not installed. (#826) +- Add support for synchronous TLS-in-TLS streams. (#840) + +## 1.0.0 (October 6th, 2023) + +From version 1.0 our async support is now optional, as the package has minimal dependencies by default. + +For async support use either `pip install 'httpcore[asyncio]'` or `pip install 'httpcore[trio]'`. + +The project versioning policy is now explicitly governed by SEMVER. See https://semver.org/. + +- Async support becomes fully optional. (#809) +- Add support for Python 3.12. (#807) + +## 0.18.0 (September 8th, 2023) + +- Add support for HTTPS proxies. (#745, #786) +- Drop Python 3.7 support. (#727) +- Handle `sni_hostname` extension with SOCKS proxy. (#774) +- Handle HTTP/1.1 half-closed connections gracefully. (#641) +- Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#762) + +## 0.17.3 (July 5th, 2023) + +- Support async cancellations, ensuring that the connection pool is left in a clean state when cancellations occur. (#726) +- The networking backend interface has [been added to the public API](https://www.encode.io/httpcore/network-backends). Some classes which were previously private implementation detail are now part of the top-level public API. (#699) +- Graceful handling of HTTP/2 GoAway frames, with requests being transparently retried on a new connection. (#730) +- Add exceptions when a synchronous `trace callback` is passed to an asynchronous request or an asynchronous `trace callback` is passed to a synchronous request. (#717) +- Drop Python 3.7 support. (#727) + +## 0.17.2 (May 23th, 2023) + +- Add `socket_options` argument to `ConnectionPool` and `HTTProxy` classes. (#668) +- Improve logging with per-module logger names. (#690) +- Add `sni_hostname` request extension. (#696) +- Resolve race condition during import of `anyio` package. (#692) +- Enable TCP_NODELAY for all synchronous sockets. (#651) + +## 0.17.1 (May 17th, 2023) + +- If 'retries' is set, then allow retries if an SSL handshake error occurs. (#669) +- Improve correctness of tracebacks on network exceptions, by raising properly chained exceptions. (#678) +- Prevent connection-hanging behaviour when HTTP/2 connections are closed by a server-sent 'GoAway' frame. (#679) +- Fix edge-case exception when removing requests from the connection pool. (#680) +- Fix pool timeout edge-case. (#688) + +## 0.17.0 (March 16th, 2023) + +- Add DEBUG level logging. (#648) +- Respect HTTP/2 max concurrent streams when settings updates are sent by server. (#652) +- Increase the allowable HTTP header size to 100kB. (#647) +- Add `retries` option to SOCKS proxy classes. (#643) + +## 0.16.3 (December 20th, 2022) + +- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625) +- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637) +- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631) +- Lazy import `anyio`, so that it's no longer a hard dependancy, and isn't imported if unused. (#639) + +## 0.16.2 (November 25th, 2022) + +- Revert 'Fix async cancellation behaviour', which introduced race conditions. (#627) +- Raise `RuntimeError` if attempting to us UNIX domain sockets on Windows. (#619) + +## 0.16.1 (November 17th, 2022) + +- Fix HTTP/1.1 interim informational responses, such as "100 Continue". (#605) + +## 0.16.0 (October 11th, 2022) + +- Support HTTP/1.1 informational responses. (#581) +- Fix async cancellation behaviour. (#580) +- Support `h11` 0.14. (#579) + +## 0.15.0 (May 17th, 2022) + +- Drop Python 3.6 support (#535) +- Ensure HTTP proxy CONNECT requests include `timeout` configuration. (#506) +- Switch to explicit `typing.Optional` for type hints. (#513) +- For `trio` map OSError exceptions to `ConnectError`. (#543) + +## 0.14.7 (February 4th, 2022) + +- Requests which raise a PoolTimeout need to be removed from the pool queue. (#502) +- Fix AttributeError that happened when Socks5Connection were terminated. (#501) + +## 0.14.6 (February 1st, 2022) + +- Fix SOCKS support for `http://` URLs. (#492) +- Resolve race condition around exceptions during streaming a response. (#491) + +## 0.14.5 (January 18th, 2022) + +- SOCKS proxy support. (#478) +- Add proxy_auth argument to HTTPProxy. (#481) +- Improve error message on 'RemoteProtocolError' exception when server disconnects without sending a response. (#479) + +## 0.14.4 (January 5th, 2022) + +- Support HTTP/2 on HTTPS tunnelling proxies. (#468) +- Fix proxy headers missing on HTTP forwarding. (#456) +- Only instantiate SSL context if required. (#457) +- More robust HTTP/2 handling. (#253, #439, #440, #441) + +## 0.14.3 (November 17th, 2021) + +- Fix race condition when removing closed connections from the pool. (#437) + +## 0.14.2 (November 16th, 2021) + +- Failed connections no longer remain in the pool. (Pull #433) + +## 0.14.1 (November 12th, 2021) + +- `max_connections` becomes optional. (Pull #429) +- `certifi` is now included in the install dependancies. (Pull #428) +- `h2` is now strictly optional. (Pull #428) + +## 0.14.0 (November 11th, 2021) + +The 0.14 release is a complete reworking of `httpcore`, comprehensively addressing some underlying issues in the connection pooling, as well as substantially redesigning the API to be more user friendly. + +Some of the lower-level API design also makes the components more easily testable in isolation, and the package now has 100% test coverage. + +See [discussion #419](https://github.com/encode/httpcore/discussions/419) for a little more background. + +There's some other neat bits in there too, such as the "trace" extension, which gives a hook into inspecting the internal events that occur during the request/response cycle. This extension is needed for the HTTPX cli, in order to... + +* Log the point at which the connection is established, and the IP/port on which it is made. +* Determine if the outgoing request should log as HTTP/1.1 or HTTP/2, rather than having to assume it's HTTP/2 if the --http2 flag was passed. (Which may not actually be true.) +* Log SSL version info / certificate info. + +Note that `curio` support is not currently available in 0.14.0. If you're using `httpcore` with `curio` please get in touch, so we can assess if we ought to prioritize it as a feature or not. + +## 0.13.7 (September 13th, 2021) + +- Fix broken error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #403) + +## 0.13.6 (June 15th, 2021) + +### Fixed + +- Close sockets when read or write timeouts occur. (Pull #365) + +## 0.13.5 (June 14th, 2021) + +### Fixed + +- Resolved niggles with AnyIO EOF behaviours. (Pull #358, #362) + +## 0.13.4 (June 9th, 2021) + +### Added + +- Improved error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #354) + +### Fixed + +- Switched to `anyio` as the default backend implementation when running with `asyncio`. Resolves some awkward [TLS timeout issues](https://github.com/encode/httpx/discussions/1511). + +## 0.13.3 (May 6th, 2021) + +### Added + +- Support HTTP/2 prior knowledge, using `httpcore.SyncConnectionPool(http1=False)`. (Pull #333) + +### Fixed + +- Handle cases where environment does not provide `select.poll` support. (Pull #331) + +## 0.13.2 (April 29th, 2021) + +### Added + +- Improve error message for specific case of `RemoteProtocolError` where server disconnects without sending a response. (Pull #313) + +## 0.13.1 (April 28th, 2021) + +### Fixed + +- More resiliant testing for closed connections. (Pull #311) +- Don't raise exceptions on ungraceful connection closes. (Pull #310) + +## 0.13.0 (April 21st, 2021) + +The 0.13 release updates the core API in order to match the HTTPX Transport API, +introduced in HTTPX 0.18 onwards. + +An example of making requests with the new interface is: + +```python +with httpcore.SyncConnectionPool() as http: + status_code, headers, stream, extensions = http.handle_request( + method=b'GET', + url=(b'https', b'example.org', 443, b'/'), + headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')] + stream=httpcore.ByteStream(b''), + extensions={} + ) + body = stream.read() + print(status_code, body) +``` + +### Changed + +- The `.request()` method is now `handle_request()`. (Pull #296) +- The `.arequest()` method is now `.handle_async_request()`. (Pull #296) +- The `headers` argument is no longer optional. (Pull #296) +- The `stream` argument is no longer optional. (Pull #296) +- The `ext` argument is now named `extensions`, and is no longer optional. (Pull #296) +- The `"reason"` extension keyword is now named `"reason_phrase"`. (Pull #296) +- The `"reason_phrase"` and `"http_version"` extensions now use byte strings for their values. (Pull #296) +- The `httpcore.PlainByteStream()` class becomes `httpcore.ByteStream()`. (Pull #296) + +### Added + +- Streams now support a `.read()` interface. (Pull #296) + +### Fixed + +- Task cancellation no longer leaks connections from the connection pool. (Pull #305) + +## 0.12.3 (December 7th, 2020) + +### Fixed + +- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`. (Pull #167) +- Fix exception raised in case of connect timeouts when using the `anyio` backend. (Pull #236) +- Fix `Host` header precedence for `:authority` in HTTP/2. (Pull #241, #243) +- Handle extra edge case when detecting for socket readability when using `asyncio`. (Pull #242, #244) +- Fix `asyncio` SSL warning when using proxy tunneling. (Pull #249) + +## 0.12.2 (November 20th, 2020) + +### Fixed + +- Properly wrap connect errors on the asyncio backend. (Pull #235) +- Fix `ImportError` occurring on Python 3.9 when using the HTTP/1.1 sync client in a multithreaded context. (Pull #237) + +## 0.12.1 (November 7th, 2020) + +### Added + +- Add connect retries. (Pull #221) + +### Fixed + +- Tweak detection of dropped connections, resolving an issue with open files limits on Linux. (Pull #185) +- Avoid leaking connections when establishing an HTTP tunnel to a proxy has failed. (Pull #223) +- Properly wrap OS errors when using `trio`. (Pull #225) + +## 0.12.0 (October 6th, 2020) + +### Changed + +- HTTP header casing is now preserved, rather than always sent in lowercase. (#216 and python-hyper/h11#104) + +### Added + +- Add Python 3.9 to officially supported versions. + +### Fixed + +- Gracefully handle a stdlib asyncio bug when a connection is closed while it is in a paused-for-reading state. (#201) + +## 0.11.1 (September 28nd, 2020) + +### Fixed + +- Add await to async semaphore release() coroutine (#197) +- Drop incorrect curio classifier (#192) + +## 0.11.0 (September 22nd, 2020) + +The Transport API with 0.11.0 has a couple of significant changes. + +Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features +such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections. + +The interface changes from: + +```python +def request(method, url, headers, stream, timeout): + return (http_version, status_code, reason, headers, stream) +``` + +To instead including an optional dictionary of extensions on the request and response: + +```python +def request(method, url, headers, stream, ext): + return (status_code, headers, stream, ext) +``` + +Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes. + +In particular: + +* Trailing headers support. +* HTTP/2 Server Push +* sendfile. +* Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming. +* Exposing debug information out of the API, including template name, template context. + +Currently extensions are limited to: + +* request: `timeout` - Optional. Timeout dictionary. +* response: `http_version` - Optional. Include the HTTP version used on the response. +* response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*. + +See https://github.com/encode/httpx/issues/1274#issuecomment-694884553 for the history behind this. + +Secondly, the async version of `request` is now namespaced as `arequest`. + +This allows concrete transports to support both sync and async implementations on the same class. + +### Added + +- Add curio support. (Pull #168) +- Add anyio support, with `backend="anyio"`. (Pull #169) + +### Changed + +- Update the Transport API to use 'ext' for optional extensions. (Pull #190) +- Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. (Pull #189) + +## 0.10.2 (August 20th, 2020) + +### Added + +- Added Unix Domain Socket support. (Pull #139) + +### Fixed + +- Always include the port on proxy CONNECT requests. (Pull #154) +- Fix `max_keepalive_connections` configuration. (Pull #153) +- Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. (Pull #164) + +## 0.10.1 (August 7th, 2020) + +- Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes. + +## 0.10.0 (August 7th, 2020) + +The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional. + +Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support. + +### Added + +- HTTP/2 support becomes optional. (Pull #121, #130) +- Add `local_address=...` support. (Pull #100, #134) +- Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. (#133) +- Add `LocalProtocolError`, `RemoteProtocolError` exceptions. (Pull #129) +- Add `UnsupportedProtocol` exception. (Pull #128) +- Add `.get_connection_info()` method. (Pull #102, #137) +- Add better TRACE logs. (Pull #101) + +### Changed + +- `max_keepalive` is deprecated in favour of `max_keepalive_connections`. (Pull #140) + +### Fixed + +- Improve handling of server disconnects. (Pull #112) + +## 0.9.1 (May 27th, 2020) + +### Fixed + +- Proper host resolution for sync case, including IPv6 support. (Pull #97) +- Close outstanding connections when connection pool is closed. (Pull #98) + +## 0.9.0 (May 21th, 2020) + +### Changed + +- URL port becomes an `Optional[int]` instead of `int`. (Pull #92) + +### Fixed + +- Honor HTTP/2 max concurrent streams settings. (Pull #89, #90) +- Remove incorrect debug log. (Pull #83) + +## 0.8.4 (May 11th, 2020) + +### Added + +- Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables +and TRACE level logging. (Pull #79) + +### Fixed + +- Reuse of connections on HTTP/2 in close concurrency situations. (Pull #81) + +## 0.8.3 (May 6rd, 2020) + +### Fixed + +- Include `Host` and `Accept` headers on proxy "CONNECT" requests. +- De-duplicate any headers also contained in proxy_headers. +- HTTP/2 flag not being passed down to proxy connections. + +## 0.8.2 (May 3rd, 2020) + +### Fixed + +- Fix connections using proxy forwarding requests not being added to the +connection pool properly. (Pull #70) + +## 0.8.1 (April 30th, 2020) + +### Changed + +- Allow inherintance of both `httpcore.AsyncByteStream`, `httpcore.SyncByteStream` without type conflicts. + +## 0.8.0 (April 30th, 2020) + +### Fixed + +- Fixed tunnel proxy support. + +### Added + +- New `TimeoutException` base class. + +## 0.7.0 (March 5th, 2020) + +- First integration with HTTPX. diff --git a/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2 b/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2 new file mode 100644 index 0000000..d474e36 Binary files /dev/null and b/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2 differ diff --git a/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2.body b/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2.body new file mode 100644 index 0000000..7148511 Binary files /dev/null and b/.cache/pip/http-v2/3/1/5/3/9/31539552f2303282ae40da968a51b4e53cadfeaff6b349f1c1a078d2.body differ diff --git a/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139 b/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139 new file mode 100644 index 0000000..3a60f95 Binary files /dev/null and b/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139 differ diff --git a/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139.body b/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139.body new file mode 100644 index 0000000..9312e8e --- /dev/null +++ b/.cache/pip/http-v2/3/1/e/0/0/31e0063c274aa3545f95e68aeecec7e139aff757a5a545bccd447139.body @@ -0,0 +1,184 @@ +Metadata-Version: 2.1 +Name: distro +Version: 1.9.0 +Summary: Distro - an OS platform information API +Home-page: https://github.com/python-distro/distro +Author: Nir Cohen +Author-email: nir36g@gmail.com +License: Apache License, Version 2.0 +Platform: All +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: BSD :: NetBSD +Classifier: Operating System :: POSIX :: BSD :: OpenBSD +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Operating System +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE + +Distro - an OS platform information API +======================================= + +[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml) +[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro) +[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg) +[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master) +[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro) +[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/) + +`distro` provides information about the +OS distribution it runs on, such as a reliable machine-readable ID, or +version information. + +It is the recommended replacement for Python's original +[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution) +function (removed in Python 3.8). It also provides much more functionality +which isn't necessarily Python bound, like a command-line interface. + +Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned. + +For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support + +## Installation + +Installation of the latest released version from PyPI: + +```shell +pip install distro +``` + +Installation of the latest development version: + +```shell +pip install https://github.com/python-distro/distro/archive/master.tar.gz +``` + +To use as a standalone script, download `distro.py` directly: + +```shell +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +python distro.py +``` + +``distro`` is safe to vendor within projects that do not wish to add +dependencies. + +```shell +cd myproject +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +``` + +## Usage + +```bash +$ distro +Name: Antergos Linux +Version: 2015.10 (ISO-Rolling) +Codename: ISO-Rolling + +$ distro -j +{ + "codename": "ISO-Rolling", + "id": "antergos", + "like": "arch", + "version": "16.9", + "version_parts": { + "build_number": "", + "major": "16", + "minor": "9" + } +} + + +$ python +>>> import distro +>>> distro.name(pretty=True) +'CentOS Linux 8' +>>> distro.id() +'centos' +>>> distro.version(best=True) +'8.4.2105' +``` + + +## Documentation + +On top of the aforementioned API, several more functions are available. For a complete description of the +API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/). + +## Background + +An alternative implementation became necessary because Python 3.5 deprecated +this function, and Python 3.8 removed it altogether. Its predecessor function +[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist) +was already deprecated since Python 2.6 and removed in Python 3.8. Still, there +are many cases in which access to that information is needed. See [Python issue +1322](https://bugs.python.org/issue1322) for more information. + +The `distro` package implements a robust and inclusive way of retrieving the +information about a distribution based on new standards and old methods, +namely from these data sources (from high to low precedence): + +* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed. +* The output of the `lsb_release` command, if available. +* The distro release file (`/etc/*(-|_)(release|version)`), if present. +* The `uname` command for BSD based distrubtions. + + +## Python and Distribution Support + +`distro` is supported and tested on Python 3.6+ and PyPy and on any +distribution that provides one or more of the data sources covered. + +This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros). + + +## Testing + +```shell +git clone git@github.com:python-distro/distro.git +cd distro +pip install tox +tox +``` + + +## Contributions + +Pull requests are always welcome to deal with specific distributions or just +for general merriment. + +See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info. + +Reference implementations for supporting additional distributions and file +formats can be found here: + +* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172 +* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb +* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py +* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc + +## Package manager distributions + +* https://src.fedoraproject.org/rpms/python-distro +* https://www.archlinux.org/packages/community/any/python-distro/ +* https://launchpad.net/ubuntu/+source/python-distro +* https://packages.debian.org/stable/python3-distro +* https://packages.gentoo.org/packages/dev-python/distro +* https://pkgs.org/download/python3-distro +* https://slackbuilds.org/repository/14.2/python/python-distro/ diff --git a/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf b/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf new file mode 100644 index 0000000..c019eef Binary files /dev/null and b/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf differ diff --git a/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf.body b/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf.body new file mode 100644 index 0000000..b28f6ec --- /dev/null +++ b/.cache/pip/http-v2/3/2/1/6/9/32169eaef21061a199b3d81dfc47cad6dc9d1be8ce789e5c747392cf.body @@ -0,0 +1,243 @@ +Metadata-Version: 2.1 +Name: idna +Version: 3.7 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.5 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for the Internationalized Domain Names in +Applications (IDNA) protocol as specified in `RFC 5891 +`_. This is the latest version of +the protocol and is sometimes referred to as “IDNA 2008”. + +This library also provides support for Unicode Technical +Standard 46, `Unicode IDNA Compatibility Processing +`_. + +This acts as a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports the older superseded IDNA specification (`RFC 3490 +`_). + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to A-labels or U-labels +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +You may use the codec encoding and decoding methods using the +``idna.codec`` module: + +.. code-block:: pycon + + >>> import idna.codec + >>> print('домен.испытание'.encode('idna2008')) + b'xn--d1acufc.xn--80akhbyknj4f' + >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) + домен.испытание + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +As described in `RFC 5895 `_, the +IDNA specification does not normalize input from different potential +ways a user may input a domain name. This functionality, known as +a “mapping”, is considered by the specification to be a local +user-interface issue distinct from IDNA conversion functionality. + +This library provides one such mapping that was developed by the +Unicode Consortium. Known as `Unicode IDNA Compatibility Processing +`_, it provides for both a regular +mapping for typical applications, as well as a transitional mapping to +help migrate from older IDNA 2003 applications. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + +Transitional processing provides conversions to help transition from +the older 2003 standard to the current standard. For example, in the +original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was +converted into two *LATIN SMALL LETTER S* (ss), whereas in the current +IDNA specification this conversion is not performed. + +.. code-block:: pycon + + >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) + 'xn--knigsgsschen-lcb0w' + +Implementers should use transitional processing with caution, only in +rare cases where conversion from legacy labels to current labels must be +performed (i.e. IDNA implementations that pre-date 2008). For typical +applications that just need to convert labels, transitional processing +is unlikely to be beneficial and could produce unexpected incompatible +results. + +``encodings.idna`` Compatibility +++++++++++++++++++++++++++++++++ + +Function calls from the Python built-in ``encodings.idna`` module are +mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. +Simply substitute the ``import`` clause in your code to refer to the new +module name. + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards. These tables are +computed using the command-line script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.5 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Removing support for older versions should be well justified in that the + maintenance burden has become too high. + +* **Python 2**. Python 2 is supported by version 2.x of this library. + While active development of the version 2.x series has ended, notable + issues being corrected may be backported to 2.x. Use "idna<3" in your + requirements file if you need this library for a Python 2 application. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + diff --git a/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a b/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a new file mode 100644 index 0000000..9b82640 Binary files /dev/null and b/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a differ diff --git a/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a.body b/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a.body new file mode 100644 index 0000000..1dfdf20 --- /dev/null +++ b/.cache/pip/http-v2/3/5/e/a/8/35ea8d9460817348cb0c12aa785127e7641de282bcf2ed7ff28a053a.body @@ -0,0 +1,361 @@ +Metadata-Version: 2.1 +Name: openai +Version: 0.28.0 +Summary: Python client library for the OpenAI API +Home-page: https://github.com/openai/openai-python +Author: OpenAI +Author-email: support@openai.com +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7.1 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: requests >=2.20 +Requires-Dist: tqdm +Requires-Dist: aiohttp +Requires-Dist: typing-extensions ; python_version < "3.8" +Provides-Extra: datalib +Requires-Dist: numpy ; extra == 'datalib' +Requires-Dist: pandas >=1.2.3 ; extra == 'datalib' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'datalib' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'datalib' +Provides-Extra: dev +Requires-Dist: black ~=21.6b0 ; extra == 'dev' +Requires-Dist: pytest ==6.* ; extra == 'dev' +Requires-Dist: pytest-asyncio ; extra == 'dev' +Requires-Dist: pytest-mock ; extra == 'dev' +Provides-Extra: embeddings +Requires-Dist: scikit-learn >=1.0.2 ; extra == 'embeddings' +Requires-Dist: tenacity >=8.0.1 ; extra == 'embeddings' +Requires-Dist: matplotlib ; extra == 'embeddings' +Requires-Dist: plotly ; extra == 'embeddings' +Requires-Dist: numpy ; extra == 'embeddings' +Requires-Dist: scipy ; extra == 'embeddings' +Requires-Dist: pandas >=1.2.3 ; extra == 'embeddings' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'embeddings' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'embeddings' +Provides-Extra: wandb +Requires-Dist: wandb ; extra == 'wandb' +Requires-Dist: numpy ; extra == 'wandb' +Requires-Dist: pandas >=1.2.3 ; extra == 'wandb' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'wandb' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'wandb' + +# OpenAI Python Library + +The OpenAI Python library provides convenient access to the OpenAI API +from applications written in the Python language. It includes a +pre-defined set of classes for API resources that initialize +themselves dynamically from API responses which makes it compatible +with a wide range of versions of the OpenAI API. + +You can find usage examples for the OpenAI Python library in our [API reference](https://platform.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). + +## Installation + +You don't need this source code unless you want to modify the package. If you just +want to use the package, just run: + +```sh +pip install --upgrade openai +``` + +Install from source with: + +```sh +python setup.py install +``` + +### Optional dependencies + +Install dependencies for [`openai.embeddings_utils`](openai/embeddings_utils.py): + +```sh +pip install openai[embeddings] +``` + +Install support for [Weights & Biases](https://wandb.me/openai-docs): + +``` +pip install openai[wandb] +``` + +Data libraries like `numpy` and `pandas` are not installed by default due to their size. They’re needed for some functionality of this library, but generally not for talking to the API. If you encounter a `MissingDependencyError`, install them with: + +```sh +pip install openai[datalib] +``` + +## Usage + +The library needs to be configured with your account's secret key which is available on the [website](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: + +```bash +export OPENAI_API_KEY='sk-...' +``` + +Or set `openai.api_key` to its value: + +```python +import openai +openai.api_key = "sk-..." + +# list models +models = openai.Model.list() + +# print the first model's id +print(models.data[0].id) + +# create a chat completion +chat_completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +# print the chat completion +print(chat_completion.choices[0].message.content) +``` + +### Params + +All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). + +### Microsoft Azure Endpoints + +In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. +In addition, the deployment name must be passed as the engine parameter. + +```python +import openai +openai.api_type = "azure" +openai.api_key = "..." +openai.api_base = "https://example-endpoint.openai.azure.com" +openai.api_version = "2023-05-15" + +# create a chat completion +chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +# print the completion +print(chat_completion.choices[0].message.content) +``` + +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. +For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: + +- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) +- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) + +### Microsoft Azure Active Directory Authentication + +In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. + +```python +from azure.identity import DefaultAzureCredential +import openai + +# Request credential +default_credential = DefaultAzureCredential() +token = default_credential.get_token("https://cognitiveservices.azure.com/.default") + +# Setup parameters +openai.api_type = "azure_ad" +openai.api_key = token.token +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2023-05-15" + +# ... +``` + +### Command-line interface + +This library additionally provides an `openai` command-line utility +which makes it easy to interact with the API from your terminal. Run +`openai api -h` for usage. + +```sh +# list models +openai api models.list + +# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) +openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" + +# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) +openai api completions.create -m ada -p "Hello world" + +# generate images via DALL·E API +openai api image.create -p "two dogs playing chess, cartoon" -n 1 + +# using openai through a proxy +openai --proxy=http://proxy.com api models.list +``` + +## Example code + +Examples of how to use this Python library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: + +- Classification using fine-tuning +- Clustering +- Code search +- Customizing embeddings +- Question answering from a corpus of documents +- Recommendations +- Visualization of embeddings +- And more + +Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). + +### Chat Completions + +Conversational models such as `gpt-3.5-turbo` can be called using the chat completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) +print(completion.choices[0].message.content) +``` + +### Completions + +Text models such as `babbage-002` or `davinci-002` (and our [legacy completions models](https://platform.openai.com/docs/deprecations/deprecation-history)) can be called using the completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.Completion.create(model="davinci-002", prompt="Hello world") +print(completion.choices[0].text) +``` + +### Embeddings + +In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. + +To get an embedding for a text string, you can use the embeddings method as follows in Python: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +# choose text to embed +text_string = "sample text" + +# choose an embedding +model_id = "text-embedding-ada-002" + +# compute the embedding of the text +embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] +``` + +An example of how to call the embeddings method is shown in this [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). + +Examples of how to use embeddings are shared in the following Jupyter notebooks: + +- [Classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb) +- [Clustering using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb) +- [Code search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb) +- [Semantic text search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Semantic_text_search_using_embeddings.ipynb) +- [User and product embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb) +- [Zero-shot classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb) +- [Recommendation using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb) + +For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://platform.openai.com/docs/guides/embeddings) in the OpenAI documentation. + +### Fine-tuning + +Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and lower the cost/latency of API calls by reducing the need to include training examples in prompts. + +```python +# Create a fine-tuning job with an already uploaded file +openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo") + +# List 10 fine-tuning jobs +openai.FineTuningJob.list(limit=10) + +# Retrieve the state of a fine-tune +openai.FineTuningJob.retrieve("ft-abc123") + +# Cancel a job +openai.FineTuningJob.cancel("ft-abc123") + +# List up to 10 events from a fine-tuning job +openai.FineTuningJob.list_events(id="ft-abc123", limit=10) + +# Delete a fine-tuned model (must be an owner of the org the model was created in) +openai.Model.delete("ft-abc123") +``` + +For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. + +### Moderation + +OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") +``` + +See the [moderation guide](https://platform.openai.com/docs/guides/moderation) for more details. + +## Image generation (DALL·E) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", n=4, size="512x512") + +``` + +## Audio transcription (Whisper) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose +f = open("path/to/file.mp3", "rb") +transcript = openai.Audio.transcribe("whisper-1", f) + +``` + +## Async API + +Async support is available in the API by prepending `a` to a network-bound method: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +async def create_chat_completion(): + chat_completion_resp = await openai.ChatCompletion.acreate(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +``` + +To make async requests more efficient, you can pass in your own +`aiohttp.ClientSession`, but you must manually close the client session at the end +of your program/event loop: + +```python +import openai +from aiohttp import ClientSession + +openai.aiosession.set(ClientSession()) +# At the end of your program, close the http session +await openai.aiosession.get().close() +``` + +See the [usage guide](https://platform.openai.com/docs/guides/images) for more details. + +## Requirements + +- Python 3.7.1+ + +In general, we want to support the versions of Python that our +customers are using. If you run into problems with any version +issues, please let us know on our [support page](https://help.openai.com/en/). + +## Credit + +This library is forked from the [Stripe Python Library](https://github.com/stripe/stripe-python). diff --git a/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5 b/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5 new file mode 100644 index 0000000..ef8dbce Binary files /dev/null and b/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5 differ diff --git a/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5.body b/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5.body new file mode 100644 index 0000000..1176514 Binary files /dev/null and b/.cache/pip/http-v2/3/6/5/6/1/365612f0a6b560fde2089cb6e2426f2a1497663e05a8a77a260c16f5.body differ diff --git a/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae b/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae new file mode 100644 index 0000000..5403a64 Binary files /dev/null and b/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae differ diff --git a/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae.body b/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae.body new file mode 100644 index 0000000..2e8819b --- /dev/null +++ b/.cache/pip/http-v2/3/6/6/5/7/366578263d203fe492bd8d4245f037d852f35951b4e3b50fbe6849ae.body @@ -0,0 +1,157 @@ +Metadata-Version: 2.1 +Name: exceptiongroup +Version: 1.2.1 +Summary: Backport of PEP 654 (exception groups) +Author-email: Alex Grönholm +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Typing :: Typed +Requires-Dist: pytest >= 6 ; extra == "test" +Project-URL: Changelog, https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst +Project-URL: Issue Tracker, https://github.com/agronholm/exceptiongroup/issues +Project-URL: Source code, https://github.com/agronholm/exceptiongroup +Provides-Extra: test + +.. image:: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/exceptiongroup/badge.svg?branch=main + :target: https://coveralls.io/github/agronholm/exceptiongroup?branch=main + :alt: Code Coverage + +This is a backport of the ``BaseExceptionGroup`` and ``ExceptionGroup`` classes from +Python 3.11. + +It contains the following: + +* The ``exceptiongroup.BaseExceptionGroup`` and ``exceptiongroup.ExceptionGroup`` + classes +* A utility function (``exceptiongroup.catch()``) for catching exceptions possibly + nested in an exception group +* Patches to the ``TracebackException`` class that properly formats exception groups + (installed on import) +* An exception hook that handles formatting of exception groups through + ``TracebackException`` (installed on import) +* Special versions of some of the functions from the ``traceback`` module, modified to + correctly handle exception groups even when monkey patching is disabled, or blocked by + another custom exception hook: + + * ``traceback.format_exception()`` + * ``traceback.format_exception_only()`` + * ``traceback.print_exception()`` + * ``traceback.print_exc()`` +* A backported version of ``contextlib.suppress()`` from Python 3.12.1 which also + handles suppressing exceptions inside exception groups + +If this package is imported on Python 3.11 or later, the built-in implementations of the +exception group classes are used instead, ``TracebackException`` is not monkey patched +and the exception hook won't be installed. + +See the `standard library documentation`_ for more information on exception groups. + +.. _standard library documentation: https://docs.python.org/3/library/exceptions.html + +Catching exceptions +=================== + +Due to the lack of the ``except*`` syntax introduced by `PEP 654`_ in earlier Python +versions, you need to use ``exceptiongroup.catch()`` to catch exceptions that are +potentially nested inside an exception group. This function returns a context manager +that calls the given handler for any exceptions matching the sole argument. + +The argument to ``catch()`` must be a dict (or any ``Mapping``) where each key is either +an exception class or an iterable of exception classes. Each value must be a callable +that takes a single positional argument. The handler will be called at most once, with +an exception group as an argument which will contain all the exceptions that are any +of the given types, or their subclasses. The exception group may contain nested groups +containing more matching exceptions. + +Thus, the following Python 3.11+ code: + +.. code-block:: python + + try: + ... + except* (ValueError, KeyError) as excgroup: + for exc in excgroup.exceptions: + print('Caught exception:', type(exc)) + except* RuntimeError: + print('Caught runtime error') + +would be written with this backport like this: + +.. code-block:: python + + from exceptiongroup import BaseExceptionGroup, catch + + def value_key_err_handler(excgroup: BaseExceptionGroup) -> None: + for exc in excgroup.exceptions: + print('Caught exception:', type(exc)) + + def runtime_err_handler(exc: BaseExceptionGroup) -> None: + print('Caught runtime error') + + with catch({ + (ValueError, KeyError): value_key_err_handler, + RuntimeError: runtime_err_handler + }): + ... + +**NOTE**: Just like with ``except*``, you cannot handle ``BaseExceptionGroup`` or +``ExceptionGroup`` with ``catch()``. + +Suppressing exceptions +====================== + +This library contains a backport of the ``contextlib.suppress()`` context manager from +Python 3.12.1. It allows you to selectively ignore certain exceptions, even when they're +inside exception groups: + +.. code-block:: python + + from exceptiongroup import suppress + + with suppress(RuntimeError): + raise ExceptionGroup("", [RuntimeError("boo")]) + +Notes on monkey patching +======================== + +To make exception groups render properly when an unhandled exception group is being +printed out, this package does two things when it is imported on any Python version +earlier than 3.11: + +#. The ``traceback.TracebackException`` class is monkey patched to store extra + information about exception groups (in ``__init__()``) and properly format them (in + ``format()``) +#. An exception hook is installed at ``sys.excepthook``, provided that no other hook is + already present. This hook causes the exception to be formatted using + ``traceback.TracebackException`` rather than the built-in rendered. + +If ``sys.exceptionhook`` is found to be set to something else than the default when +``exceptiongroup`` is imported, no monkeypatching is done at all. + +To prevent the exception hook and patches from being installed, set the environment +variable ``EXCEPTIONGROUP_NO_PATCH`` to ``1``. + +Formatting exception groups +--------------------------- + +Normally, the monkey patching applied by this library on import will cause exception +groups to be printed properly in tracebacks. But in cases when the monkey patching is +blocked by a third party exception hook, or monkey patching is explicitly disabled, +you can still manually format exceptions using the special versions of the ``traceback`` +functions, like ``format_exception()``, listed at the top of this page. They work just +like their counterparts in the ``traceback`` module, except that they use a separately +patched subclass of ``TracebackException`` to perform the rendering. + +Particularly in cases where a library installs its own exception hook, it is recommended +to use these special versions to do the actual formatting of exceptions/tracebacks. + +.. _PEP 654: https://www.python.org/dev/peps/pep-0654/ + diff --git a/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d b/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d new file mode 100644 index 0000000..450a2b7 Binary files /dev/null and b/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d differ diff --git a/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d.body b/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d.body new file mode 100644 index 0000000..c848334 Binary files /dev/null and b/.cache/pip/http-v2/3/8/6/0/e/3860e4de9ae53c79d2fd61419e9049df314ccc8b640782c02c6e2e2d.body differ diff --git a/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061 b/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061 new file mode 100644 index 0000000..23a0aeb Binary files /dev/null and b/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061 differ diff --git a/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061.body b/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061.body new file mode 100644 index 0000000..7a9f297 Binary files /dev/null and b/.cache/pip/http-v2/3/d/b/a/4/3dba48e33e4deae5b8e0b5d8173f7d8b9ede86f6e2f3b423cb213061.body differ diff --git a/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494 b/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494 new file mode 100644 index 0000000..ad5b067 Binary files /dev/null and b/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494 differ diff --git a/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494.body b/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494.body new file mode 100644 index 0000000..c688a62 --- /dev/null +++ b/.cache/pip/http-v2/3/f/6/6/d/3f66d434e0f39e97300f0ffb2d06dd18a093bf1883626d85879cb494.body @@ -0,0 +1,66 @@ +Metadata-Version: 2.1 +Name: certifi +Version: 2024.2.2 +Summary: Python package for providing Mozilla's CA Bundle. +Home-page: https://github.com/certifi/python-certifi +Author: Kenneth Reitz +Author-email: me@kennethreitz.com +License: MPL-2.0 +Project-URL: Source, https://github.com/certifi/python-certifi +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=3.6 +License-File: LICENSE + +Certifi: Python SSL Certificates +================================ + +Certifi provides Mozilla's carefully curated collection of Root Certificates for +validating the trustworthiness of SSL certificates while verifying the identity +of TLS hosts. It has been extracted from the `Requests`_ project. + +Installation +------------ + +``certifi`` is available on PyPI. Simply install it with ``pip``:: + + $ pip install certifi + +Usage +----- + +To reference the installed certificate authority (CA) bundle, you can use the +built-in function:: + + >>> import certifi + + >>> certifi.where() + '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' + +Or from the command line:: + + $ python -m certifi + /usr/local/lib/python3.7/site-packages/certifi/cacert.pem + +Enjoy! + +.. _`Requests`: https://requests.readthedocs.io/en/master/ + +Addition/Removal of Certificates +-------------------------------- + +Certifi does not support any addition/removal or other modification of the +CA trust store content. This project is intended to provide a reliable and +highly portable root of trust to python deployments. Look to upstream projects +for methods to use alternate trust. diff --git a/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334 b/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334 new file mode 100644 index 0000000..56a5c28 Binary files /dev/null and b/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334 differ diff --git a/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334.body b/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334.body new file mode 100644 index 0000000..c587434 Binary files /dev/null and b/.cache/pip/http-v2/4/4/e/7/d/44e7d0f6565b3195207c0e4f42ba8354af57e808bdcfe550a7dc7334.body differ diff --git a/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a b/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a new file mode 100644 index 0000000..c6fba45 Binary files /dev/null and b/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a differ diff --git a/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a.body b/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a.body new file mode 100644 index 0000000..8d36350 Binary files /dev/null and b/.cache/pip/http-v2/4/5/6/0/9/45609a98def18e658161b2f6581d765d16405b388807ea3c5127b09a.body differ diff --git a/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb b/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb new file mode 100644 index 0000000..dcfac29 Binary files /dev/null and b/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb differ diff --git a/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb.body b/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb.body new file mode 100644 index 0000000..9d9b4a7 --- /dev/null +++ b/.cache/pip/http-v2/4/7/b/9/7/47b976ad1f5fe20fbce880f20d89f1b6515b51fbc6a38debccfe59bb.body @@ -0,0 +1,132 @@ +Metadata-Version: 2.1 +Name: multidict +Version: 6.0.5 +Summary: multidict implementation +Home-page: https://github.com/aio-libs/multidict +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +License: Apache 2 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub, https://github.com/aio-libs/multidict/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/multidict +Project-URL: Docs: RTD, https://multidict.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/multidict/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/multidict +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE + +========= +multidict +========= + +.. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg + :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/multidict + :alt: Coverage metrics + +.. image:: https://img.shields.io/pypi/v/multidict.svg + :target: https://pypi.org/project/multidict + :alt: PyPI + +.. image:: https://readthedocs.org/projects/multidict/badge/?version=latest + :target: http://multidict.aio-libs.org/en/latest/?badge=latest + :alt: Documentation + +.. image:: https://img.shields.io/pypi/pyversions/multidict.svg + :target: https://pypi.org/project/multidict + :alt: Python versions + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +Multidict is dict-like collection of *key-value pairs* where key +might occur more than once in the container. + +Introduction +------------ + +*HTTP Headers* and *URL query string* require specific data structure: +*multidict*. It behaves mostly like a regular ``dict`` but it may have +several *values* for the same *key* and *preserves insertion ordering*. + +The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries). + +``multidict`` has four multidict classes: +``MultiDict``, ``MultiDictProxy``, ``CIMultiDict`` +and ``CIMultiDictProxy``. + +Immutable proxies (``MultiDictProxy`` and +``CIMultiDictProxy``) provide a dynamic view for the +proxied multidict, the view reflects underlying collection changes. They +implement the ``collections.abc.Mapping`` interface. + +Regular mutable (``MultiDict`` and ``CIMultiDict``) classes +implement ``collections.abc.MutableMapping`` and allows them to change +their own content. + + +*Case insensitive* (``CIMultiDict`` and +``CIMultiDictProxy``) assume the *keys* are case +insensitive, e.g.:: + + >>> dct = CIMultiDict(key='val') + >>> 'Key' in dct + True + >>> dct['Key'] + 'val' + +*Keys* should be ``str`` or ``istr`` instances. + +The library has optional C Extensions for speed. + + +License +------- + +Apache 2 + +Library Installation +-------------------- + +.. code-block:: bash + + $ pip install multidict + +The library is Python 3 only! + +PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install +``multidict`` on another operating system (or *Alpine Linux* inside a Docker) the +tarball will be used to compile the library from source. It requires a C compiler and +Python headers to be installed. + +To skip the compilation, please use the `MULTIDICT_NO_EXTENSIONS` environment variable, +e.g.: + +.. code-block:: bash + + $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict + +Please note, the pure Python (uncompiled) version is about 20-50 times slower depending on +the usage scenario!!! + + + +Changelog +--------- +See `RTD page `_. diff --git a/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1 b/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1 new file mode 100644 index 0000000..e77ceda Binary files /dev/null and b/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1 differ diff --git a/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1.body b/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1.body new file mode 100644 index 0000000..e6bb31d Binary files /dev/null and b/.cache/pip/http-v2/4/b/2/6/3/4b26377b80f2228b4f7da76f70379ef2c40634df685edba0ff982fe1.body differ diff --git a/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0 b/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0 new file mode 100644 index 0000000..b1b4a30 Binary files /dev/null and b/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0 differ diff --git a/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0.body b/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0.body new file mode 100644 index 0000000..acb80e7 Binary files /dev/null and b/.cache/pip/http-v2/4/b/9/5/f/4b95f342ad96b3f181c58ecc742a5263d12dfc47d5658390eb0f30e0.body differ diff --git a/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 b/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 new file mode 100644 index 0000000..b524004 Binary files /dev/null and b/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 differ diff --git a/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9.body b/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9.body new file mode 100644 index 0000000..afc100c Binary files /dev/null and b/.cache/pip/http-v2/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9.body differ diff --git a/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 b/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 new file mode 100644 index 0000000..5ac72f5 Binary files /dev/null and b/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 differ diff --git a/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40.body b/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40.body new file mode 100644 index 0000000..72ceba9 Binary files /dev/null and b/.cache/pip/http-v2/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40.body differ diff --git a/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8 b/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8 new file mode 100644 index 0000000..20365b9 Binary files /dev/null and b/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8 differ diff --git a/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8.body b/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8.body new file mode 100644 index 0000000..494f262 Binary files /dev/null and b/.cache/pip/http-v2/4/e/9/6/6/4e966e3df04323a5e0681113448e8322d42435d7a5d99b4053c52dd8.body differ diff --git a/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c b/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c new file mode 100644 index 0000000..e32dd0b Binary files /dev/null and b/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c differ diff --git a/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c.body b/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c.body new file mode 100644 index 0000000..35238b4 Binary files /dev/null and b/.cache/pip/http-v2/5/0/2/8/a/5028ab03cd5c26383d3042bca39f513db3395458d0493738e9382b3c.body differ diff --git a/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0 b/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0 new file mode 100644 index 0000000..3471c26 Binary files /dev/null and b/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0 differ diff --git a/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0.body b/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0.body new file mode 100644 index 0000000..b329a46 --- /dev/null +++ b/.cache/pip/http-v2/5/0/d/a/4/50da49580af7dcbcc111cd276ac3009c0f0fc95948f679bf3a3dd3b0.body @@ -0,0 +1,107 @@ +Metadata-Version: 2.1 +Name: PyJWT +Version: 2.8.0 +Summary: JSON Web Token implementation in Python +Home-page: https://github.com/jpadilla/pyjwt +Author: Jose Padilla +Author-email: hello@jpadilla.com +License: MIT +Keywords: json,jwt,security,signing,token,web +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS.rst +Requires-Dist: typing-extensions ; python_version <= "3.7" +Provides-Extra: crypto +Requires-Dist: cryptography (>=3.4.0) ; extra == 'crypto' +Provides-Extra: dev +Requires-Dist: sphinx (<5.0.0,>=4.5.0) ; extra == 'dev' +Requires-Dist: sphinx-rtd-theme ; extra == 'dev' +Requires-Dist: zope.interface ; extra == 'dev' +Requires-Dist: cryptography (>=3.4.0) ; extra == 'dev' +Requires-Dist: pytest (<7.0.0,>=6.0.0) ; extra == 'dev' +Requires-Dist: coverage[toml] (==5.0.4) ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Provides-Extra: docs +Requires-Dist: sphinx (<5.0.0,>=4.5.0) ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme ; extra == 'docs' +Requires-Dist: zope.interface ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: pytest (<7.0.0,>=6.0.0) ; extra == 'tests' +Requires-Dist: coverage[toml] (==5.0.4) ; extra == 'tests' + +PyJWT +===== + +.. image:: https://github.com/jpadilla/pyjwt/workflows/CI/badge.svg + :target: https://github.com/jpadilla/pyjwt/actions?query=workflow%3ACI + +.. image:: https://img.shields.io/pypi/v/pyjwt.svg + :target: https://pypi.python.org/pypi/pyjwt + +.. image:: https://codecov.io/gh/jpadilla/pyjwt/branch/master/graph/badge.svg + :target: https://codecov.io/gh/jpadilla/pyjwt + +.. image:: https://readthedocs.org/projects/pyjwt/badge/?version=stable + :target: https://pyjwt.readthedocs.io/en/stable/ + +A Python implementation of `RFC 7519 `_. Original implementation was written by `@progrium `_. + +Sponsor +------- + ++--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| |auth0-logo| | If you want to quickly add secure token-based authentication to Python projects, feel free to check Auth0's Python SDK and free plan at `auth0.com/developers `_. | ++--------------+-----------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. |auth0-logo| image:: https://user-images.githubusercontent.com/83319/31722733-de95bbde-b3ea-11e7-96bf-4f4e8f915588.png + +Installing +---------- + +Install with **pip**: + +.. code-block:: console + + $ pip install PyJWT + + +Usage +----- + +.. code-block:: pycon + + >>> import jwt + >>> encoded = jwt.encode({"some": "payload"}, "secret", algorithm="HS256") + >>> print(encoded) + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg + >>> jwt.decode(encoded, "secret", algorithms=["HS256"]) + {'some': 'payload'} + +Documentation +------------- + +View the full docs online at https://pyjwt.readthedocs.io/en/stable/ + + +Tests +----- + +You can run tests from the project root after cloning with: + +.. code-block:: console + + $ tox diff --git a/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133 b/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133 new file mode 100644 index 0000000..1d5591f Binary files /dev/null and b/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133 differ diff --git a/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133.body b/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133.body new file mode 100644 index 0000000..19a91a2 Binary files /dev/null and b/.cache/pip/http-v2/5/b/4/2/0/5b4204bcd3256bc6840609839425131ec8e156d23f3167449ff6d133.body differ diff --git a/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 b/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 new file mode 100644 index 0000000..ce83781 Binary files /dev/null and b/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 differ diff --git a/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077.body b/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077.body new file mode 100644 index 0000000..faa23e3 Binary files /dev/null and b/.cache/pip/http-v2/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077.body differ diff --git a/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead b/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead new file mode 100644 index 0000000..f9e9443 Binary files /dev/null and b/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead differ diff --git a/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead.body b/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead.body new file mode 100644 index 0000000..b9e2782 Binary files /dev/null and b/.cache/pip/http-v2/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead.body differ diff --git a/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c b/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c new file mode 100644 index 0000000..5dbebb7 Binary files /dev/null and b/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c differ diff --git a/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c.body b/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c.body new file mode 100644 index 0000000..78a8563 --- /dev/null +++ b/.cache/pip/http-v2/6/0/6/2/4/6062416ca885c964f9c59ad2e853ea05f16b92ffec58e684342e664c.body @@ -0,0 +1,1076 @@ +Metadata-Version: 2.1 +Name: regex +Version: 2024.4.16 +Summary: Alternative regular expression module, to replace re. +Home-page: https://github.com/mrabarnett/mrab-regex +Author: Matthew Barnett +Author-email: regex@mrabarnett.plus.com +License: Apache Software License +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: General +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt + +Introduction +------------ + +This regex implementation is backwards-compatible with the standard 're' module, but offers additional functionality. + +Note +---- + +The re module's behaviour with zero-width matches changed in Python 3.7, and this module follows that behaviour when compiled for Python 3.7. + +Python 2 +-------- + +Python 2 is no longer supported. The last release that supported Python 2 was 2021.11.10. + +PyPy +---- + +This module is targeted at CPython. It expects that all codepoints are the same width, so it won't behave properly with PyPy outside U+0000..U+007F because PyPy stores strings as UTF-8. + +Multithreading +-------------- + +The regex module releases the GIL during matching on instances of the built-in (immutable) string classes, enabling other Python threads to run concurrently. It is also possible to force the regex module to release the GIL during matching by calling the matching methods with the keyword argument ``concurrent=True``. The behaviour is undefined if the string changes during matching, so use it *only* when it is guaranteed that that won't happen. + +Unicode +------- + +This module supports Unicode 15.1.0. Full Unicode case-folding is supported. + +Flags +----- + +There are 2 kinds of flag: scoped and global. Scoped flags can apply to only part of a pattern and can be turned on or off; global flags apply to the entire pattern and can only be turned on. + +The scoped flags are: ``ASCII (?a)``, ``FULLCASE (?f)``, ``IGNORECASE (?i)``, ``LOCALE (?L)``, ``MULTILINE (?m)``, ``DOTALL (?s)``, ``UNICODE (?u)``, ``VERBOSE (?x)``, ``WORD (?w)``. + +The global flags are: ``BESTMATCH (?b)``, ``ENHANCEMATCH (?e)``, ``POSIX (?p)``, ``REVERSE (?r)``, ``VERSION0 (?V0)``, ``VERSION1 (?V1)``. + +If neither the ``ASCII``, ``LOCALE`` nor ``UNICODE`` flag is specified, it will default to ``UNICODE`` if the regex pattern is a Unicode string and ``ASCII`` if it's a bytestring. + +The ``ENHANCEMATCH`` flag makes fuzzy matching attempt to improve the fit of the next match that it finds. + +The ``BESTMATCH`` flag makes fuzzy matching search for the best match instead of the next match. + +Old vs new behaviour +-------------------- + +In order to be compatible with the re module, this module has 2 behaviours: + +* **Version 0** behaviour (old behaviour, compatible with the re module): + + Please note that the re module's behaviour may change over time, and I'll endeavour to match that behaviour in version 0. + + * Indicated by the ``VERSION0`` flag. + + * Zero-width matches are not handled correctly in the re module before Python 3.7. The behaviour in those earlier versions is: + + * ``.split`` won't split a string at a zero-width match. + + * ``.sub`` will advance by one character after a zero-width match. + + * Inline flags apply to the entire pattern, and they can't be turned off. + + * Only simple sets are supported. + + * Case-insensitive matches in Unicode use simple case-folding by default. + +* **Version 1** behaviour (new behaviour, possibly different from the re module): + + * Indicated by the ``VERSION1`` flag. + + * Zero-width matches are handled correctly. + + * Inline flags apply to the end of the group or pattern, and they can be turned off. + + * Nested sets and set operations are supported. + + * Case-insensitive matches in Unicode use full case-folding by default. + +If no version is specified, the regex module will default to ``regex.DEFAULT_VERSION``. + +Case-insensitive matches in Unicode +----------------------------------- + +The regex module supports both simple and full case-folding for case-insensitive matches in Unicode. Use of full case-folding can be turned on using the ``FULLCASE`` flag. Please note that this flag affects how the ``IGNORECASE`` flag works; the ``FULLCASE`` flag itself does not turn on case-insensitive matching. + +Version 0 behaviour: the flag is off by default. + +Version 1 behaviour: the flag is on by default. + +Nested sets and set operations +------------------------------ + +It's not possible to support both simple sets, as used in the re module, and nested sets at the same time because of a difference in the meaning of an unescaped ``"["`` in a set. + +For example, the pattern ``[[a-z]--[aeiou]]`` is treated in the version 0 behaviour (simple sets, compatible with the re module) as: + +* Set containing "[" and the letters "a" to "z" + +* Literal "--" + +* Set containing letters "a", "e", "i", "o", "u" + +* Literal "]" + +but in the version 1 behaviour (nested sets, enhanced behaviour) as: + +* Set which is: + + * Set containing the letters "a" to "z" + +* but excluding: + + * Set containing the letters "a", "e", "i", "o", "u" + +Version 0 behaviour: only simple sets are supported. + +Version 1 behaviour: nested sets and set operations are supported. + +Notes on named groups +--------------------- + +All groups have a group number, starting from 1. + +Groups with the same group name will have the same group number, and groups with a different group name will have a different group number. + +The same name can be used by more than one group, with later captures 'overwriting' earlier captures. All the captures of the group will be available from the ``captures`` method of the match object. + +Group numbers will be reused across different branches of a branch reset, eg. ``(?|(first)|(second))`` has only group 1. If groups have different group names then they will, of course, have different group numbers, eg. ``(?|(?Pfirst)|(?Psecond))`` has group 1 ("foo") and group 2 ("bar"). + +In the regex ``(\s+)(?|(?P[A-Z]+)|(\w+) (?P[0-9]+)`` there are 2 groups: + +* ``(\s+)`` is group 1. + +* ``(?P[A-Z]+)`` is group 2, also called "foo". + +* ``(\w+)`` is group 2 because of the branch reset. + +* ``(?P[0-9]+)`` is group 2 because it's called "foo". + +If you want to prevent ``(\w+)`` from being group 2, you need to name it (different name, different group number). + +Additional features +------------------- + +The issue numbers relate to the Python bug tracker, except where listed otherwise. + +Added ``\p{Horiz_Space}`` and ``\p{Vert_Space}`` (`GitHub issue 477 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{Horiz_Space}`` or ``\p{H}`` matches horizontal whitespace and ``\p{Vert_Space}`` or ``\p{V}`` matches vertical whitespace. + +Added support for lookaround in conditional pattern (`Hg issue 163 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The test of a conditional pattern can be a lookaround. + +.. sourcecode:: python + + >>> regex.match(r'(?(?=\d)\d+|\w+)', '123abc') + + >>> regex.match(r'(?(?=\d)\d+|\w+)', 'abc123') + + +This is not quite the same as putting a lookaround in the first branch of a pair of alternatives. + +.. sourcecode:: python + + >>> print(regex.match(r'(?:(?=\d)\d+\b|\w+)', '123abc')) + + >>> print(regex.match(r'(?(?=\d)\d+\b|\w+)', '123abc')) + None + +In the first example, the lookaround matched, but the remainder of the first branch failed to match, and so the second branch was attempted, whereas in the second example, the lookaround matched, and the first branch failed to match, but the second branch was **not** attempted. + +Added POSIX matching (leftmost longest) (`Hg issue 150 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The POSIX standard for regex is to return the leftmost longest match. This can be turned on using the ``POSIX`` flag. + +.. sourcecode:: python + + >>> # Normal matching. + >>> regex.search(r'Mr|Mrs', 'Mrs') + + >>> regex.search(r'one(self)?(selfsufficient)?', 'oneselfsufficient') + + >>> # POSIX matching. + >>> regex.search(r'(?p)Mr|Mrs', 'Mrs') + + >>> regex.search(r'(?p)one(self)?(selfsufficient)?', 'oneselfsufficient') + + +Note that it will take longer to find matches because when it finds a match at a certain position, it won't return that immediately, but will keep looking to see if there's another longer match there. + +Added ``(?(DEFINE)...)`` (`Hg issue 152 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If there's no group called "DEFINE", then ... will be ignored except that any groups defined within it can be called and that the normal rules for numbering groups still apply. + +.. sourcecode:: python + + >>> regex.search(r'(?(DEFINE)(?P\d+)(?P\w+))(?&quant) (?&item)', '5 elephants') + + +Added ``(*PRUNE)``, ``(*SKIP)`` and ``(*FAIL)`` (`Hg issue 153 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(*PRUNE)`` discards the backtracking info up to that point. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*SKIP)`` is similar to ``(*PRUNE)``, except that it also sets where in the text the next attempt to match will start. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*FAIL)`` causes immediate backtracking. ``(*F)`` is a permitted abbreviation. + +Added ``\K`` (`Hg issue 151 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Keeps the part of the entire match after the position where ``\K`` occurred; the part before it is discarded. + +It does not affect what groups return. + +.. sourcecode:: python + + >>> m = regex.search(r'(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'cde' + >>> m[1] + 'abcde' + >>> + >>> m = regex.search(r'(?r)(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'bc' + >>> m[1] + 'bcdef' + +Added capture subscripting for ``expandf`` and ``subf``/``subfn`` (`Hg issue 133 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can use subscripting to get the captures of a repeated group. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w)+", "abc") + >>> m.expandf("{1}") + 'c' + >>> m.expandf("{1[0]} {1[1]} {1[2]}") + 'a b c' + >>> m.expandf("{1[-1]} {1[-2]} {1[-3]}") + 'c b a' + >>> + >>> m = regex.match(r"(?P\w)+", "abc") + >>> m.expandf("{letter}") + 'c' + >>> m.expandf("{letter[0]} {letter[1]} {letter[2]}") + 'a b c' + >>> m.expandf("{letter[-1]} {letter[-2]} {letter[-3]}") + 'c b a' + +Added support for referring to a group by number using ``(?P=...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is in addition to the existing ``\g<...>``. + +Fixed the handling of locale-sensitive regexes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``LOCALE`` flag is intended for legacy code and has limited support. You're still recommended to use Unicode instead. + +Added partial matches (`Hg issue 102 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A partial match is one that matches up to the end of string, but that string has been truncated and you want to know whether a complete match could be possible if the string had not been truncated. + +Partial matches are supported by ``match``, ``search``, ``fullmatch`` and ``finditer`` with the ``partial`` keyword argument. + +Match objects have a ``partial`` attribute, which is ``True`` if it's a partial match. + +For example, if you wanted a user to enter a 4-digit number and check it character by character as it was being entered: + +.. sourcecode:: python + + >>> pattern = regex.compile(r'\d{4}') + + >>> # Initially, nothing has been entered: + >>> print(pattern.fullmatch('', partial=True)) + + + >>> # An empty string is OK, but it's only a partial match. + >>> # The user enters a letter: + >>> print(pattern.fullmatch('a', partial=True)) + None + >>> # It'll never match. + + >>> # The user deletes that and enters a digit: + >>> print(pattern.fullmatch('1', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters 2 more digits: + >>> print(pattern.fullmatch('123', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters another digit: + >>> print(pattern.fullmatch('1234', partial=True)) + + >>> # It's a complete match. + + >>> # If the user enters another digit: + >>> print(pattern.fullmatch('12345', partial=True)) + None + >>> # It's no longer a match. + + >>> # This is a partial match: + >>> pattern.match('123', partial=True).partial + True + + >>> # This is a complete match: + >>> pattern.match('1233', partial=True).partial + False + +``*`` operator not working correctly with sub() (`Hg issue 106 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it's not clear how zero-width matches should be handled. For example, should ``.*`` match 0 characters directly after matching >0 characters? + +.. sourcecode:: python + + # Python 3.7 and later + >>> regex.sub('.*', 'x', 'test') + 'xx' + >>> regex.sub('.*?', '|', 'test') + '|||||||||' + + # Python 3.6 and earlier + >>> regex.sub('(?V0).*', 'x', 'test') + 'x' + >>> regex.sub('(?V1).*', 'x', 'test') + 'xx' + >>> regex.sub('(?V0).*?', '|', 'test') + '|t|e|s|t|' + >>> regex.sub('(?V1).*?', '|', 'test') + '|||||||||' + +Added ``capturesdict`` (`Hg issue 86 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``capturesdict`` is a combination of ``groupdict`` and ``captures``: + +``groupdict`` returns a dict of the named groups and the last capture of those groups. + +``captures`` returns a list of all the captures of a group + +``capturesdict`` returns a dict of the named groups and lists of all the captures of those groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.groupdict() + {'word': 'three', 'digits': '3'} + >>> m.captures("word") + ['one', 'two', 'three'] + >>> m.captures("digits") + ['1', '2', '3'] + >>> m.capturesdict() + {'word': ['one', 'two', 'three'], 'digits': ['1', '2', '3']} + +Added ``allcaptures`` and ``allspans`` (`Git issue 474 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``allcaptures`` returns a list of all the captures of all the groups. + +``allspans`` returns a list of all the spans of the all captures of all the groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.allcaptures() + (['one 1\ntwo 2\nthree 3\n'], ['one', 'two', 'three'], ['1', '2', '3']) + >>> m.allspans() + ([(0, 20)], [(0, 3), (6, 9), (12, 17)], [(4, 5), (10, 11), (18, 19)]) + +Allow duplicate names of groups (`Hg issue 87 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group names can be duplicated. + +.. sourcecode:: python + + >>> # With optional groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Only the second group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['second'] + >>> # Only the first group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or ") + >>> m.group("item") + 'first' + >>> m.captures("item") + ['first'] + >>> + >>> # With mandatory groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['', 'second'] + >>> # And yet again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", "first or ") + >>> m.group("item") + '' + >>> m.captures("item") + ['first', ''] + +Added ``fullmatch`` (`issue #16203 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``fullmatch`` behaves like ``match``, except that it must match all of the string. + +.. sourcecode:: python + + >>> print(regex.fullmatch(r"abc", "abc").span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "abcx")) + None + >>> print(regex.fullmatch(r"abc", "abcx", endpos=3).span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "xabcy", pos=1, endpos=4).span()) + (1, 4) + >>> + >>> regex.match(r"a.*?", "abcd").group(0) + 'a' + >>> regex.fullmatch(r"a.*?", "abcd").group(0) + 'abcd' + +Added ``subf`` and ``subfn`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``subf`` and ``subfn`` are alternatives to ``sub`` and ``subn`` respectively. When passed a replacement string, they treat it as a format string. + +.. sourcecode:: python + + >>> regex.subf(r"(\w+) (\w+)", "{0} => {2} {1}", "foo bar") + 'foo bar => bar foo' + >>> regex.subf(r"(?P\w+) (?P\w+)", "{word2} {word1}", "foo bar") + 'bar foo' + +Added ``expandf`` to match object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``expandf`` is an alternative to ``expand``. When passed a replacement string, it treats it as a format string. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w+) (\w+)", "foo bar") + >>> m.expandf("{0} => {2} {1}") + 'foo bar => bar foo' + >>> + >>> m = regex.match(r"(?P\w+) (?P\w+)", "foo bar") + >>> m.expandf("{word2} {word1}") + 'bar foo' + +Detach searched string +^^^^^^^^^^^^^^^^^^^^^^ + +A match object contains a reference to the string that was searched, via its ``string`` attribute. The ``detach_string`` method will 'detach' that string, making it available for garbage collection, which might save valuable memory if that string is very large. + +.. sourcecode:: python + + >>> m = regex.search(r"\w+", "Hello world") + >>> print(m.group()) + Hello + >>> print(m.string) + Hello world + >>> m.detach_string() + >>> print(m.group()) + Hello + >>> print(m.string) + None + +Recursive patterns (`Hg issue 27 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Recursive and repeated patterns are supported. + +``(?R)`` or ``(?0)`` tries to match the entire regex recursively. ``(?1)``, ``(?2)``, etc, try to match the relevant group. + +``(?&name)`` tries to match the named group. + +.. sourcecode:: python + + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Tarzan loves Jane").groups() + ('Tarzan',) + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Jane loves Tarzan").groups() + ('Jane',) + + >>> m = regex.search(r"(\w)(?:(?R)|(\w?))\1", "kayak") + >>> m.group(0, 1, 2) + ('kayak', 'k', None) + +The first two examples show how the subpattern within the group is reused, but is _not_ itself a group. In other words, ``"(Tarzan|Jane) loves (?1)"`` is equivalent to ``"(Tarzan|Jane) loves (?:Tarzan|Jane)"``. + +It's possible to backtrack into a recursed or repeated group. + +You can't call a group if there is more than one group with that group name or group number (``"ambiguous group reference"``). + +The alternative forms ``(?P>name)`` and ``(?P&name)`` are also supported. + +Full Unicode case-folding is supported +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In version 1 behaviour, the regex module uses full case-folding when performing case-insensitive matches in Unicode. + +.. sourcecode:: python + + >>> regex.match(r"(?iV1)strasse", "stra\N{LATIN SMALL LETTER SHARP S}e").span() + (0, 6) + >>> regex.match(r"(?iV1)stra\N{LATIN SMALL LETTER SHARP S}e", "STRASSE").span() + (0, 7) + +In version 0 behaviour, it uses simple case-folding for backward compatibility with the re module. + +Approximate "fuzzy" matching (`Hg issue 12 `_, `Hg issue 41 `_, `Hg issue 109 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Regex usually attempts an exact match, but sometimes an approximate, or "fuzzy", match is needed, for those cases where the text being searched may contain errors in the form of inserted, deleted or substituted characters. + +A fuzzy regex specifies which types of errors are permitted, and, optionally, either the minimum and maximum or only the maximum permitted number of each type. (You cannot specify only a minimum.) + +The 3 types of error are: + +* Insertion, indicated by "i" + +* Deletion, indicated by "d" + +* Substitution, indicated by "s" + +In addition, "e" indicates any type of error. + +The fuzziness of a regex item is specified between "{" and "}" after the item. + +Examples: + +* ``foo`` match "foo" exactly + +* ``(?:foo){i}`` match "foo", permitting insertions + +* ``(?:foo){d}`` match "foo", permitting deletions + +* ``(?:foo){s}`` match "foo", permitting substitutions + +* ``(?:foo){i,s}`` match "foo", permitting insertions and substitutions + +* ``(?:foo){e}`` match "foo", permitting errors + +If a certain type of error is specified, then any type not specified will **not** be permitted. + +In the following examples I'll omit the item and write only the fuzziness: + +* ``{d<=3}`` permit at most 3 deletions, but no other types + +* ``{i<=1,s<=2}`` permit at most 1 insertion and at most 2 substitutions, but no deletions + +* ``{1<=e<=3}`` permit at least 1 and at most 3 errors + +* ``{i<=2,d<=2,e<=3}`` permit at most 2 insertions, at most 2 deletions, at most 3 errors in total, but no substitutions + +It's also possible to state the costs of each type of error and the maximum permitted total cost. + +Examples: + +* ``{2i+2d+1s<=4}`` each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +* ``{i<=1,d<=1,s<=1,2i+2d+1s<=4}`` at most 1 insertion, at most 1 deletion, at most 1 substitution; each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +You can also use "<" instead of "<=" if you want an exclusive minimum or maximum. + +You can add a test to perform on a character that's substituted or inserted. + +Examples: + +* ``{s<=2:[a-z]}`` at most 2 substitutions, which must be in the character set ``[a-z]``. + +* ``{s<=2,i<=3:\d}`` at most 2 substitutions, at most 3 insertions, which must be digits. + +By default, fuzzy matching searches for the first match that meets the given constraints. The ``ENHANCEMATCH`` flag will cause it to attempt to improve the fit (i.e. reduce the number of errors) of the match that it has found. + +The ``BESTMATCH`` flag will make it search for the best match instead. + +Further examples to note: + +* ``regex.search("(dog){e}", "cat and dog")[1]`` returns ``"cat"`` because that matches ``"dog"`` with 3 errors (an unlimited number of errors is permitted). + +* ``regex.search("(dog){e<=1}", "cat and dog")[1]`` returns ``" dog"`` (with a leading space) because that matches ``"dog"`` with 1 error, which is within the limit. + +* ``regex.search("(?e)(dog){e<=1}", "cat and dog")[1]`` returns ``"dog"`` (without a leading space) because the fuzzy search matches ``" dog"`` with 1 error, which is within the limit, and the ``(?e)`` then it attempts a better fit. + +In the first two examples there are perfect matches later in the string, but in neither case is it the first possible match. + +The match object has an attribute ``fuzzy_counts`` which gives the total number of substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> # A 'raw' fuzzy match: + >>> regex.fullmatch(r"(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 1) + >>> # 0 substitutions, 0 insertions, 1 deletion. + + >>> # A better match might be possible if the ENHANCEMATCH flag used: + >>> regex.fullmatch(r"(?e)(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 0) + >>> # 0 substitutions, 0 insertions, 0 deletions. + +The match object also has an attribute ``fuzzy_changes`` which gives a tuple of the positions of the substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> m = regex.search('(fuu){i<=2,d<=2,e<=5}', 'anaconda foo bar') + >>> m + + >>> m.fuzzy_changes + ([], [7, 8], [10, 11]) + +What this means is that if the matched part of the string had been: + +.. sourcecode:: python + + 'anacondfuuoo bar' + +it would've been an exact match. + +However, there were insertions at positions 7 and 8: + +.. sourcecode:: python + + 'anaconda fuuoo bar' + ^^ + +and deletions at positions 10 and 11: + +.. sourcecode:: python + + 'anaconda f~~oo bar' + ^^ + +So the actual string was: + +.. sourcecode:: python + + 'anaconda foo bar' + +Named lists ``\L`` (`Hg issue 11 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are occasions where you may want to include a list (actually, a set) of options in a regex. + +One way is to build the pattern like this: + +.. sourcecode:: python + + >>> p = regex.compile(r"first|second|third|fourth|fifth") + +but if the list is large, parsing the resulting regex can take considerable time, and care must also be taken that the strings are properly escaped and properly ordered, for example, "cats" before "cat". + +The new alternative is to use a named list: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set) + +The order of the items is irrelevant, they are treated as a set. The named lists are available as the ``.named_lists`` attribute of the pattern object : + +.. sourcecode:: python + + >>> print(p.named_lists) + {'options': frozenset({'third', 'first', 'fifth', 'fourth', 'second'})} + +If there are any unused keyword arguments, ``ValueError`` will be raised unless you tell it otherwise: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set, other_options=[]) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=True) + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=False) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> + +Start and end of word +^^^^^^^^^^^^^^^^^^^^^ + +``\m`` matches at the start of a word. + +``\M`` matches at the end of a word. + +Compare with ``\b``, which matches at the start or end of a word. + +Unicode line separators +^^^^^^^^^^^^^^^^^^^^^^^ + +Normally the only line separator is ``\n`` (``\x0A``), but if the ``WORD`` flag is turned on then the line separators are ``\x0D\x0A``, ``\x0A``, ``\x0B``, ``\x0C`` and ``\x0D``, plus ``\x85``, ``\u2028`` and ``\u2029`` when working with Unicode. + +This affects the regex dot ``"."``, which, with the ``DOTALL`` flag turned off, matches any character except a line separator. It also affects the line anchors ``^`` and ``$`` (in multiline mode). + +Set operators +^^^^^^^^^^^^^ + +**Version 1 behaviour only** + +Set operators have been added, and a set ``[...]`` can include nested sets. + +The operators, in order of increasing precedence, are: + +* ``||`` for union ("x||y" means "x or y") + +* ``~~`` (double tilde) for symmetric difference ("x~~y" means "x or y, but not both") + +* ``&&`` for intersection ("x&&y" means "x and y") + +* ``--`` (double dash) for difference ("x--y" means "x but not y") + +Implicit union, ie, simple juxtaposition like in ``[ab]``, has the highest precedence. Thus, ``[ab&&cd]`` is the same as ``[[a||b]&&[c||d]]``. + +Examples: + +* ``[ab]`` # Set containing 'a' and 'b' + +* ``[a-z]`` # Set containing 'a' .. 'z' + +* ``[[a-z]--[qw]]`` # Set containing 'a' .. 'z', but not 'q' or 'w' + +* ``[a-z--qw]`` # Same as above + +* ``[\p{L}--QW]`` # Set containing all letters except 'Q' and 'W' + +* ``[\p{N}--[0-9]]`` # Set containing all numbers except '0' .. '9' + +* ``[\p{ASCII}&&\p{Letter}]`` # Set containing all characters which are ASCII and letter + +regex.escape (`issue #2650 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``special_only``. When True, only 'special' regex characters, such as '?', are escaped. + +.. sourcecode:: python + + >>> regex.escape("foo!?", special_only=False) + 'foo\\!\\?' + >>> regex.escape("foo!?", special_only=True) + 'foo!\\?' + +regex.escape (`Hg issue 249 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``literal_spaces``. When True, spaces are not escaped. + +.. sourcecode:: python + + >>> regex.escape("foo bar!?", literal_spaces=False) + 'foo\\ bar!\\?' + >>> regex.escape("foo bar!?", literal_spaces=True) + 'foo bar!\\?' + +Repeated captures (`issue #7132 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object has additional methods which return information on all the successful matches of a repeated group. These methods are: + +* ``matchobject.captures([group1, ...])`` + + * Returns a list of the strings matched in a group or groups. Compare with ``matchobject.group([group1, ...])``. + +* ``matchobject.starts([group])`` + + * Returns a list of the start positions. Compare with ``matchobject.start([group])``. + +* ``matchobject.ends([group])`` + + * Returns a list of the end positions. Compare with ``matchobject.end([group])``. + +* ``matchobject.spans([group])`` + + * Returns a list of the spans. Compare with ``matchobject.span([group])``. + +.. sourcecode:: python + + >>> m = regex.search(r"(\w{3})+", "123456789") + >>> m.group(1) + '789' + >>> m.captures(1) + ['123', '456', '789'] + >>> m.start(1) + 6 + >>> m.starts(1) + [0, 3, 6] + >>> m.end(1) + 9 + >>> m.ends(1) + [3, 6, 9] + >>> m.span(1) + (6, 9) + >>> m.spans(1) + [(0, 3), (3, 6), (6, 9)] + +Atomic grouping ``(?>...)`` (`issue #433030 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the following pattern subsequently fails, then the subpattern as a whole will fail. + +Possessive quantifiers +^^^^^^^^^^^^^^^^^^^^^^ + +``(?:...)?+`` ; ``(?:...)*+`` ; ``(?:...)++`` ; ``(?:...){min,max}+`` + +The subpattern is matched up to 'max' times. If the following pattern subsequently fails, then all the repeated subpatterns will fail as a whole. For example, ``(?:...)++`` is equivalent to ``(?>(?:...)+)``. + +Scoped flags (`issue #433028 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(?flags-flags:...)`` + +The flags will apply only to the subpattern. Flags can be turned on or off. + +Definition of 'word' character (`issue #1693050 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The definition of a 'word' character has been expanded for Unicode. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Variable-length lookbehind +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A lookbehind can match a variable-length string. + +Flags argument for regex.split, regex.sub and regex.subn (`issue #3482 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.split``, ``regex.sub`` and ``regex.subn`` support a 'flags' argument. + +Pos and endpos arguments for regex.sub and regex.subn +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.sub`` and ``regex.subn`` support 'pos' and 'endpos' arguments. + +'Overlapped' argument for regex.findall and regex.finditer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.findall`` and ``regex.finditer`` support an 'overlapped' flag which permits overlapped matches. + +Splititer +^^^^^^^^^ + +``regex.splititer`` has been added. It's a generator equivalent of ``regex.split``. + +Subscripting match objects for groups +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object accepts access to the groups via subscripting and slicing: + +.. sourcecode:: python + + >>> m = regex.search(r"(?P.*?)(?P\d+)(?P.*)", "pqr123stu") + >>> print(m["before"]) + pqr + >>> print(len(m)) + 4 + >>> print(m[:]) + ('pqr123stu', 'pqr', '123', 'stu') + +Named groups +^^^^^^^^^^^^ + +Groups can be named with ``(?...)`` as well as the existing ``(?P...)``. + +Group references +^^^^^^^^^^^^^^^^ + +Groups can be referenced within a pattern with ``\g``. This also allows there to be more than 99 groups. + +Named characters ``\N{name}`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Named characters are supported. Note that only those known by Python's Unicode database will be recognised. + +Unicode codepoint properties, including scripts and blocks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{property=value}``; ``\P{property=value}``; ``\p{value}`` ; ``\P{value}`` + +Many Unicode properties are supported, including blocks and scripts. ``\p{property=value}`` or ``\p{property:value}`` matches a character whose property ``property`` has value ``value``. The inverse of ``\p{property=value}`` is ``\P{property=value}`` or ``\p{^property=value}``. + +If the short form ``\p{value}`` is used, the properties are checked in the order: ``General_Category``, ``Script``, ``Block``, binary property: + +* ``Latin``, the 'Latin' script (``Script=Latin``). + +* ``BasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +* ``Alphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``Is`` indicates a script or binary property: + +* ``IsLatin``, the 'Latin' script (``Script=Latin``). + +* ``IsAlphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``In`` indicates a block property: + +* ``InBasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +POSIX character classes +^^^^^^^^^^^^^^^^^^^^^^^ + +``[[:alpha:]]``; ``[[:^alpha:]]`` + +POSIX character classes are supported. These are normally treated as an alternative form of ``\p{...}``. + +The exceptions are ``alnum``, ``digit``, ``punct`` and ``xdigit``, whose definitions are different from those of Unicode. + +``[[:alnum:]]`` is equivalent to ``\p{posix_alnum}``. + +``[[:digit:]]`` is equivalent to ``\p{posix_digit}``. + +``[[:punct:]]`` is equivalent to ``\p{posix_punct}``. + +``[[:xdigit:]]`` is equivalent to ``\p{posix_xdigit}``. + +Search anchor ``\G`` +^^^^^^^^^^^^^^^^^^^^ + +A search anchor has been added. It matches at the position where each search started/continued and can be used for contiguous matches or in negative variable-length lookbehinds to limit how far back the lookbehind goes: + +.. sourcecode:: python + + >>> regex.findall(r"\w{2}", "abcd ef") + ['ab', 'cd', 'ef'] + >>> regex.findall(r"\G\w{2}", "abcd ef") + ['ab', 'cd'] + +* The search starts at position 0 and matches 'ab'. + +* The search continues at position 2 and matches 'cd'. + +* The search continues at position 4 and fails to match any letters. + +* The anchor stops the search start position from being advanced, so there are no more results. + +Reverse searching +^^^^^^^^^^^^^^^^^ + +Searches can also work backwards: + +.. sourcecode:: python + + >>> regex.findall(r".", "abc") + ['a', 'b', 'c'] + >>> regex.findall(r"(?r).", "abc") + ['c', 'b', 'a'] + +Note that the result of a reverse search is not necessarily the reverse of a forward search: + +.. sourcecode:: python + + >>> regex.findall(r"..", "abcde") + ['ab', 'cd'] + >>> regex.findall(r"(?r)..", "abcde") + ['de', 'bc'] + +Matching a single grapheme ``\X`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The grapheme matcher is supported. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Branch reset ``(?|...|...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group numbers will be reused across the alternatives, but groups with different names will have different group numbers. + +.. sourcecode:: python + + >>> regex.match(r"(?|(first)|(second))", "first").groups() + ('first',) + >>> regex.match(r"(?|(first)|(second))", "second").groups() + ('second',) + +Note that there is only one group. + +Default Unicode word boundary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``WORD`` flag changes the definition of a 'word boundary' to that of a default Unicode word boundary. This applies to ``\b`` and ``\B``. + +Timeout +^^^^^^^ + +The matching methods and functions support timeouts. The timeout (in seconds) applies to the entire operation: + +.. sourcecode:: python + + >>> from time import sleep + >>> + >>> def fast_replace(m): + ... return 'X' + ... + >>> def slow_replace(m): + ... sleep(0.5) + ... return 'X' + ... + >>> regex.sub(r'[a-z]', fast_replace, 'abcde', timeout=2) + 'XXXXX' + >>> regex.sub(r'[a-z]', slow_replace, 'abcde', timeout=2) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 278, in sub + return pat.sub(repl, string, count, pos, endpos, concurrent, timeout) + TimeoutError: regex timed out diff --git a/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 b/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 new file mode 100644 index 0000000..208b16f Binary files /dev/null and b/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 differ diff --git a/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051.body b/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051.body new file mode 100644 index 0000000..86630aa Binary files /dev/null and b/.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051.body differ diff --git a/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f b/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f new file mode 100644 index 0000000..eb0badc Binary files /dev/null and b/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f differ diff --git a/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f.body b/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f.body new file mode 100644 index 0000000..0ea9893 Binary files /dev/null and b/.cache/pip/http-v2/6/1/f/e/2/61fe2e5f081c64cf7fadfec126f43ef78e23ea1062168f3c05bb456f.body differ diff --git a/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32 b/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32 new file mode 100644 index 0000000..2004cd8 Binary files /dev/null and b/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32 differ diff --git a/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32.body b/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32.body new file mode 100644 index 0000000..36ec9b0 --- /dev/null +++ b/.cache/pip/http-v2/6/3/b/3/9/63b397b6e418cefba7703ea1b0e3a39d3b83617c9c506bfaca239a32.body @@ -0,0 +1,242 @@ +Metadata-Version: 2.1 +Name: annotated-types +Version: 0.6.0 +Summary: Reusable constraint types to use with typing.Annotated +Author-email: Samuel Colvin , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Zac Hatfield-Dodds +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.8 +Requires-Dist: typing-extensions>=4.0.0; python_version < '3.9' +Description-Content-Type: text/markdown + +# annotated-types + +[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) +[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) +[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) + +[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of +adding context-specific metadata to existing types, and specifies that +`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special +logic for `x`. + +This package provides metadata objects which can be used to represent common +constraints such as upper and lower bounds on scalar values and collection sizes, +a `Predicate` marker for runtime checks, and +descriptions of how we intend these metadata to be interpreted. In some cases, +we also note alternative representations which do not require this package. + +## Install + +```bash +pip install annotated-types +``` + +## Examples + +```python +from typing import Annotated +from annotated_types import Gt, Len, Predicate + +class MyClass: + age: Annotated[int, Gt(18)] # Valid: 19, 20, ... + # Invalid: 17, 18, "19", 19.0, ... + factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... + # Invalid: 4, 8, -2, 5.0, "prime", ... + + my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] + # Invalid: (1, 2), ["abc"], [0] * 20 +``` + +## Documentation + +_While `annotated-types` avoids runtime checks for performance, users should not +construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. +Downstream implementors may choose to raise an error, emit a warning, silently ignore +a metadata item, etc., if the metadata objects described below are used with an +incompatible type - or for any other reason!_ + +### Gt, Ge, Lt, Le + +Express inclusive and/or exclusive bounds on orderable values - which may be numbers, +dates, times, strings, sets, etc. Note that the boundary value need not be of the +same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` +is fine, for example, and implies that the value is an integer x such that `x > 1.5`. + +We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` +as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on +the `annotated-types` package. + +To be explicit, these types have the following meanings: + +* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum +* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum +* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum +* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum + +### Interval + +`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single +metadata object. `None` attributes should be ignored, and non-`None` attributes +treated as per the single bounds above. + +### MultipleOf + +`MultipleOf(multiple_of=x)` might be interpreted in two ways: + +1. Python semantics, implying `value % multiple_of == 0`, or +2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), + where `int(value / multiple_of) == value / multiple_of`. + +We encourage users to be aware of these two common interpretations and their +distinct behaviours, especially since very large or non-integer numbers make +it easy to cause silent data corruption due to floating-point imprecision. + +We encourage libraries to carefully document which interpretation they implement. + +### MinLen, MaxLen, Len + +`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. + +As well as `Len()` which can optionally include upper and lower bounds, we also +provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` +and `Len(max_length=y)` respectively. + +`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. + +Examples of usage: + +* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10))`) - list must have a length of 10 or less +* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less +* `Annotated[list, MinLen(3))` (or `Annotated[list, Len(min_length=3))`) - list must have a length of 3 or more +* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 +* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 + +#### Changed in v0.4.0 + +* `min_inclusive` has been renamed to `min_length`, no change in meaning +* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** +* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic + meaning of the upper bound in slices vs. `Len` + +See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. + +### Timezone + +`Timezone` can be used with a `datetime` or a `time` to express which timezones +are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. +`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) +expresses that any timezone-aware datetime is allowed. You may also pass a specific +timezone string or `timezone` object such as `Timezone(timezone.utc)` or +`Timezone("Africa/Abidjan")` to express that you only allow a specific timezone, +though we note that this is often a symptom of fragile design. + +### Predicate + +`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. +Users should prefer the statically inspectable metadata above, but if you need +the full power and flexibility of arbitrary runtime predicates... here it is. + +We provide a few predefined predicates for common string constraints: + +* `IsLower = Predicate(str.islower)` +* `IsUpper = Predicate(str.isupper)` +* `IsDigit = Predicate(str.isdigit)` +* `IsFinite = Predicate(math.isfinite)` +* `IsNotFinite = Predicate(Not(math.isfinite))` +* `IsNan = Predicate(math.isnan)` +* `IsNotNan = Predicate(Not(math.isnan))` +* `IsInfinite = Predicate(math.isinf)` +* `IsNotInfinite = Predicate(Not(math.isinf))` + +Some libraries might have special logic to handle known or understandable predicates, +for example by checking for `str.isdigit` and using its presence to both call custom +logic to enforce digit-only strings, and customise some generated external schema. +Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in +favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. + +To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. + +We do not specify what behaviour should be expected for predicates that raise +an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently +skip invalid constraints, or statically raise an error; or it might try calling it +and then propogate or discard the resulting +`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` +exception. We encourage libraries to document the behaviour they choose. + +### Doc + +`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. + +It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. + +It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. + +This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). + +### Integrating downstream types with `GroupedMetadata` + +Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. +This can help reduce verbosity and cognitive overhead for users. +For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: + +```python +from dataclasses import dataclass +from typing import Iterator +from annotated_types import GroupedMetadata, Ge + +@dataclass +class Field(GroupedMetadata): + ge: int | None = None + description: str | None = None + + def __iter__(self) -> Iterator[object]: + # Iterating over a GroupedMetadata object should yield annotated-types + # constraint metadata objects which describe it as fully as possible, + # and may include other unknown objects too. + if self.ge is not None: + yield Ge(self.ge) + if self.description is not None: + yield Description(self.description) +``` + +Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. + +Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. + +Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. + +### Consuming metadata + +We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). + +It is up to the implementer to determine how this metadata is used. +You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. + +## Design & History + +This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic +and Hypothesis, with the goal of making it as easy as possible for end-users to +provide more informative annotations for use by runtime libraries. + +It is deliberately minimal, and following PEP-593 allows considerable downstream +discretion in what (if anything!) they choose to support. Nonetheless, we expect +that staying simple and covering _only_ the most common use-cases will give users +and maintainers the best experience we can. If you'd like more constraints for your +types - follow our lead, by defining them and documenting them downstream! diff --git a/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1 b/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1 new file mode 100644 index 0000000..d7c934f Binary files /dev/null and b/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1 differ diff --git a/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1.body b/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1.body new file mode 100644 index 0000000..04f44e4 Binary files /dev/null and b/.cache/pip/http-v2/6/6/8/0/4/6680465c66f90d5dba7423aa175d72f390d4883736cee302491f10f1.body differ diff --git a/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0 b/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0 new file mode 100644 index 0000000..8c7d3ad Binary files /dev/null and b/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0 differ diff --git a/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0.body b/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0.body new file mode 100644 index 0000000..a02c8de Binary files /dev/null and b/.cache/pip/http-v2/6/6/8/2/6/66826e0bc2503bb8d4293b2cfa918e3e23e0c16d4c172feb7fd8d9d0.body differ diff --git a/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6 b/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6 new file mode 100644 index 0000000..78e62b2 Binary files /dev/null and b/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6 differ diff --git a/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6.body b/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6.body new file mode 100644 index 0000000..fc3e4c3 Binary files /dev/null and b/.cache/pip/http-v2/6/6/d/7/0/66d70ae2c53f7d298f561c6c62e85ea9b0768217b42025ef0e30c6c6.body differ diff --git a/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883 b/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883 new file mode 100644 index 0000000..ba590e2 Binary files /dev/null and b/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883 differ diff --git a/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883.body b/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883.body new file mode 100644 index 0000000..450e3bd Binary files /dev/null and b/.cache/pip/http-v2/6/9/8/c/7/698c7c11b59e422d3f535a8b92e8203524b386f4f2292205cebce883.body differ diff --git a/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c b/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c new file mode 100644 index 0000000..d9d4a02 Binary files /dev/null and b/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c differ diff --git a/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c.body b/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c.body new file mode 100644 index 0000000..43339c5 Binary files /dev/null and b/.cache/pip/http-v2/6/b/0/2/9/6b02925e31ee401036522de24d0ac2f63669b5979d0f861a7a7a715c.body differ diff --git a/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71 b/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71 new file mode 100644 index 0000000..7c4284c Binary files /dev/null and b/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71 differ diff --git a/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71.body b/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71.body new file mode 100644 index 0000000..fa4c95b Binary files /dev/null and b/.cache/pip/http-v2/6/e/d/1/d/6ed1d2a7ae12b1f6b0f5bfa40dd227697b2b07bbf33eeb1e7a086f71.body differ diff --git a/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0 b/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0 new file mode 100644 index 0000000..42f9b4b Binary files /dev/null and b/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0 differ diff --git a/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0.body b/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0.body new file mode 100644 index 0000000..cad1d0d --- /dev/null +++ b/.cache/pip/http-v2/7/a/c/e/0/7ace04a02115042694251acd714fbf2e50c4d24f9a521043845b18d0.body @@ -0,0 +1,67 @@ +Metadata-Version: 2.1 +Name: nltk +Version: 3.8.1 +Summary: Natural Language Toolkit +Home-page: https://www.nltk.org/ +Author: NLTK Team +Author-email: nltk.team@gmail.com +Maintainer: NLTK Team +Maintainer-email: nltk.team@gmail.com +License: Apache License, Version 2.0 +Project-URL: Documentation, https://www.nltk.org/ +Project-URL: Source Code, https://github.com/nltk/nltk +Project-URL: Issue Tracker, https://github.com/nltk/nltk/issues +Keywords: NLP,CL,natural language processing,computational linguistics,parsing,tagging,tokenizing,syntax,linguistics,language,natural language,text analytics +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces +Classifier: Topic :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Text Processing :: General +Classifier: Topic :: Text Processing :: Indexing +Classifier: Topic :: Text Processing :: Linguistic +Requires-Python: >=3.7 +Requires-Dist: click +Requires-Dist: joblib +Requires-Dist: regex (>=2021.8.3) +Requires-Dist: tqdm +Provides-Extra: all +Requires-Dist: scikit-learn ; extra == 'all' +Requires-Dist: python-crfsuite ; extra == 'all' +Requires-Dist: requests ; extra == 'all' +Requires-Dist: numpy ; extra == 'all' +Requires-Dist: pyparsing ; extra == 'all' +Requires-Dist: twython ; extra == 'all' +Requires-Dist: scipy ; extra == 'all' +Requires-Dist: matplotlib ; extra == 'all' +Provides-Extra: corenlp +Requires-Dist: requests ; extra == 'corenlp' +Provides-Extra: machine_learning +Requires-Dist: numpy ; extra == 'machine_learning' +Requires-Dist: python-crfsuite ; extra == 'machine_learning' +Requires-Dist: scikit-learn ; extra == 'machine_learning' +Requires-Dist: scipy ; extra == 'machine_learning' +Provides-Extra: plot +Requires-Dist: matplotlib ; extra == 'plot' +Provides-Extra: tgrep +Requires-Dist: pyparsing ; extra == 'tgrep' +Provides-Extra: twitter +Requires-Dist: twython ; extra == 'twitter' + +The Natural Language Toolkit (NLTK) is a Python package for +natural language processing. NLTK requires Python 3.7, 3.8, 3.9, 3.10 or 3.11. + diff --git a/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab b/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab new file mode 100644 index 0000000..b5c2f14 Binary files /dev/null and b/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab differ diff --git a/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab.body b/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab.body new file mode 100644 index 0000000..5148621 Binary files /dev/null and b/.cache/pip/http-v2/7/c/7/2/f/7c72f1a6398a6349002190641317f2afd704b2e599125914d7ebbaab.body differ diff --git a/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558 b/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558 new file mode 100644 index 0000000..499783e Binary files /dev/null and b/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558 differ diff --git a/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558.body b/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558.body new file mode 100644 index 0000000..f70ad70 Binary files /dev/null and b/.cache/pip/http-v2/7/e/e/4/5/7ee451bcf126fca6db4237c6412ec820f86390fb4a69b9e075168558.body differ diff --git a/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939 b/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939 new file mode 100644 index 0000000..78be9c2 Binary files /dev/null and b/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939 differ diff --git a/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939.body b/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939.body new file mode 100644 index 0000000..b9d8286 --- /dev/null +++ b/.cache/pip/http-v2/8/0/9/0/0/80900f1d6dbeabac2fe9b179009308c9bee1d1af0a9fcd4d8923b939.body @@ -0,0 +1,325 @@ +Metadata-Version: 2.1 +Name: twilio +Version: 9.0.5 +Summary: Twilio API client and TwiML generator +Home-page: https://github.com/twilio/twilio-python/ +Author: Twilio +Keywords: twilio,twiml +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Communications :: Telephony +Requires-Python: >=3.7.0 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS.md +Requires-Dist: requests >=2.0.0 +Requires-Dist: PyJWT <3.0.0,>=2.0.0 +Requires-Dist: aiohttp >=3.8.4 +Requires-Dist: aiohttp-retry >=2.8.3 + +# twilio-python + +[![Tests](https://github.com/twilio/twilio-python/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/twilio/twilio-python/actions/workflows/test-and-deploy.yml) +[![PyPI](https://img.shields.io/pypi/v/twilio.svg)](https://pypi.python.org/pypi/twilio) +[![PyPI](https://img.shields.io/pypi/pyversions/twilio.svg)](https://pypi.python.org/pypi/twilio) +[![Learn OSS Contribution in TwilioQuest](https://img.shields.io/static/v1?label=TwilioQuest&message=Learn%20to%20contribute%21&color=F22F46&labelColor=1f243c&style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAASFBMVEUAAAAZGRkcHBwjIyMoKCgAAABgYGBoaGiAgICMjIyzs7PJycnMzMzNzc3UoBfd3d3m5ubqrhfrMEDu7u739/f4vSb/3AD///9tbdyEAAAABXRSTlMAAAAAAMJrBrEAAAKoSURBVHgB7ZrRcuI6EESdyxXGYoNFvMD//+l2bSszRgyUYpFAsXOeiJGmj4NkuWx1Qeh+Ekl9DgEXOBwOx+Px5xyQhDykfgq4wG63MxxaR4ddIkg6Ul3g84vCIcjPBA5gmUMeXESrlukuoK33+33uID8TWeLAdOWsKpJYzwVMB7bOzYSGOciyUlXSn0/ABXTosJ1M1SbypZ4O4MbZuIDMU02PMbauhhHMHXbmebmALIiEbbbbbUrpF1gwE9kFfRNAJaP+FQEXCCTGyJ4ngDrjOFo3jEL5JdqjF/pueR4cCeCGgAtwmuRS6gDwaRiGvu+DMFwSBLTE3+jF8JyuV1okPZ+AC4hDFhCHyHQjdjPHUKFDlHSJkHQXMB3KpSwXNGJPcwwTdZiXlRN0gSp0zpWxNtM0beYE0nRH6QIbO7rawwXaBYz0j78gxjokDuv12gVeUuBD0MDi0OQCLvDaAho4juP1Q/jkAncXqIcCfd+7gAu4QLMACCLxpRsSuQh0igu0C9Svhi7weAGZg50L3IE3cai4IfkNZAC8dfdhsUD3CgKBVC9JE5ABAFzg4QL/taYPAAWrHdYcgfLaIgAXWJ7OV38n1LEF8tt2TH29E+QAoDoO5Ve/LtCQDmKM9kPbvCEBApK+IXzbcSJ0cIGF6e8gpcRhUDogWZ8JnaWjPXc/fNnBBUKRngiHgTUSivSzDRDgHZQOLvBQgf8rRt+VdBUUhwkU6VpJ+xcOwQUqZr+mR0kvBUgv6cB4+37hQAkXqE8PwGisGhJtN4xAHMzrsgvI7rccXqSvKh6jltGlrOHA3Xk1At3LC4QiPdX9/0ndHpGVvTjR4bZA1ypAKgVcwE5vx74ulwIugDt8e/X7JgfkucBMIAr26ndnB4UCLnDOqvteQsHlgX9N4A+c4cW3DXSPbwAAAABJRU5ErkJggg==)](https://twil.io/learn-open-source) + +## Documentation + +The documentation for the Twilio API can be found [here][apidocs]. + +The Python library documentation can be found [here][libdocs]. + +## Versions + +`twilio-python` uses a modified version of [Semantic Versioning](https://semver.org) for all changes. [See this document](VERSIONS.md) for details. + +### Supported Python Versions + +This library supports the following Python implementations: + +- Python 3.7 +- Python 3.8 +- Python 3.9 +- Python 3.10 +- Python 3.11 + +## Installation + +Install from PyPi using [pip](https://pip.pypa.io/en/latest/), a +package manager for Python. + +```shell +pip3 install twilio +``` + +If pip install fails on Windows, check the path length of the directory. If it is greater 260 characters then enable [Long Paths](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) or choose other shorter location. + +Don't have pip installed? Try installing it, by running this from the command +line: + +```shell +curl https://bootstrap.pypa.io/get-pip.py | python +``` + +Or, you can [download the source code +(ZIP)](https://github.com/twilio/twilio-python/zipball/main 'twilio-python +source code') for `twilio-python`, and then run: + +```shell +python3 setup.py install +``` + +> **Info** +> If the command line gives you an error message that says Permission Denied, try running the above commands with `sudo` (e.g., `sudo pip3 install twilio`). + +### Test your installation + +Try sending yourself an SMS message. Save the following code sample to your computer with a text editor. Be sure to update the `account_sid`, `auth_token`, and `from_` phone number with values from your [Twilio account](https://console.twilio.com). The `to` phone number will be your own mobile phone. + +```python +from twilio.rest import Client + +# Your Account SID and Auth Token from console.twilio.com +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" + +client = Client(account_sid, auth_token) + +message = client.messages.create( + to="+15558675309", + from_="+15017250604", + body="Hello from Python!") + +print(message.sid) +``` + +Save the file as `send_sms.py`. In the terminal, `cd` to the directory containing the file you just saved then run: + +```shell +python3 send_sms.py +``` + +After a brief delay, you will receive the text message on your phone. + +> **Warning** +> It's okay to hardcode your credentials when testing locally, but you should use environment variables to keep them secret before committing any code or deploying to production. Check out [How to Set Environment Variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) for more information. + +## Use the helper library + +### API Credentials + +The `Twilio` client needs your Twilio credentials. You can either pass these directly to the constructor (see the code below) or via environment variables. + +Authenticating with Account SID and Auth Token: + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) +``` + +Authenticating with API Key and API Secret: + +```python +from twilio.rest import Client + +api_key = "XXXXXXXXXXXXXXXXX" +api_secret = "YYYYYYYYYYYYYYYYYY" +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +client = Client(api_key, api_secret, account_sid) +``` + +Alternatively, a `Client` constructor without these parameters will +look for `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` variables inside the +current environment. + +We suggest storing your credentials as environment variables. Why? You'll never +have to worry about committing your credentials and accidentally posting them +somewhere public. + +```python +from twilio.rest import Client +client = Client() +``` + +### Specify Region and/or Edge + +To take advantage of Twilio's [Global Infrastructure](https://www.twilio.com/docs/global-infrastructure), specify the target Region and/or Edge for the client: + +```python +from twilio.rest import Client + +client = Client(region='au1', edge='sydney') +``` + +A `Client` constructor without these parameters will also look for `TWILIO_REGION` and `TWILIO_EDGE` variables inside the current environment. + +Alternatively, you may specify the edge and/or region after constructing the Twilio client: + +```python +from twilio.rest import Client + +client = Client() +client.region = 'au1' +client.edge = 'sydney' +``` + +This will result in the `hostname` transforming from `api.twilio.com` to `api.sydney.au1.twilio.com`. + +### Make a Call + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +call = client.calls.create(to="9991231234", + from_="9991231234", + url="http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient") +print(call.sid) +``` + +### Get data about an existing call + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +call = client.calls.get("CA42ed11f93dc08b952027ffbc406d0868") +print(call.to) +``` + +### Iterate through records + +The library automatically handles paging for you. Collections, such as `calls` and `messages`, have `list` and `stream` methods that page under the hood. With both `list` and `stream`, you can specify the number of records you want to receive (`limit`) and the maximum size you want each page fetch to be (`page_size`). The library will then handle the task for you. + +`list` eagerly fetches all records and returns them as a list, whereas `stream` returns an iterator and lazily retrieves pages of records as you iterate over the collection. You can also page manually using the `page` method. + +#### Use the `list` method + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +for sms in client.messages.list(): + print(sms.to) +``` + +### Asynchronous API Requests + +By default, the Twilio Client will make synchronous requests to the Twilio API. To allow for asynchronous, non-blocking requests, we've included an optional asynchronous HTTP client. When used with the Client and the accompanying `*_async` methods, requests made to the Twilio API will be performed asynchronously. + +```python +from twilio.http.async_http_client import AsyncTwilioHttpClient +from twilio.rest import Client + +async def main(): + account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + auth_token = "your_auth_token" + http_client = AsyncTwilioHttpClient() + client = Client(account_sid, auth_token, http_client=http_client) + + message = await client.messages.create_async(to="+12316851234", from_="+15555555555", + body="Hello there!") + +asyncio.run(main()) +``` + +### Enable Debug Logging + +Log the API request and response data to the console: + +```python +import logging + +client = Client(account_sid, auth_token) +logging.basicConfig() +client.http_client.logger.setLevel(logging.INFO) +``` + +Log the API request and response data to a file: + +```python +import logging + +client = Client(account_sid, auth_token) +logging.basicConfig(filename='./log.txt') +client.http_client.logger.setLevel(logging.INFO) +``` + +### Handling Exceptions + +Version 8.x of `twilio-python` exports an exception class to help you handle exceptions that are specific to Twilio methods. To use it, import `TwilioRestException` and catch exceptions as follows: + +```python +from twilio.rest import Client +from twilio.base.exceptions import TwilioRestException + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +try: + message = client.messages.create(to="+12316851234", from_="+15555555555", + body="Hello there!") +except TwilioRestException as e: + print(e) +``` + +### Generating TwiML + +To control phone calls, your application needs to output [TwiML][twiml]. + +Use `twilio.twiml.Response` to easily create such responses. + +```python +from twilio.twiml.voice_response import VoiceResponse + +r = VoiceResponse() +r.say("Welcome to twilio!") +print(str(r)) +``` + +```xml + +Welcome to twilio! +``` + +### Other advanced examples + +- [Learn how to create your own custom HTTP client](./advanced-examples/custom-http-client.md) + +### Docker Image + +The `Dockerfile` present in this repository and its respective `twilio/twilio-python` Docker image are currently used by Twilio for testing purposes only. + +### Getting help + +If you need help installing or using the library, please check the [Twilio Support Help Center](https://support.twilio.com) first, and [file a support ticket](https://twilio.com/help/contact) if you don't find an answer to your question. + +If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo! + +[apidocs]: https://www.twilio.com/docs/api +[twiml]: https://www.twilio.com/docs/api/twiml +[libdocs]: https://twilio.github.io/twilio-python diff --git a/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec b/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec new file mode 100644 index 0000000..0587a96 Binary files /dev/null and b/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec differ diff --git a/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec.body b/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec.body new file mode 100644 index 0000000..72b268d Binary files /dev/null and b/.cache/pip/http-v2/8/1/3/c/c/813cc7096c25ba43edba14a4ab40832db33af638c43e975795995aec.body differ diff --git a/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370 b/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370 new file mode 100644 index 0000000..c6536a9 Binary files /dev/null and b/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370 differ diff --git a/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370.body b/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370.body new file mode 100644 index 0000000..1954f47 Binary files /dev/null and b/.cache/pip/http-v2/8/8/f/7/d/88f7d71f2d1361f8fb38230bb8ae05bdb624c08a2414e1c535731370.body differ diff --git a/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8 b/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8 new file mode 100644 index 0000000..cf0ffe0 Binary files /dev/null and b/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8 differ diff --git a/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8.body b/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8.body new file mode 100644 index 0000000..cf12a82 --- /dev/null +++ b/.cache/pip/http-v2/8/9/8/6/3/898634e8915cd16e9cc06d9d4530915d56ea2b4313596d73a3b33ee8.body @@ -0,0 +1,193 @@ +Metadata-Version: 2.1 +Name: h11 +Version: 0.14.0 +Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 +Home-page: https://github.com/python-hyper/h11 +Author: Nathaniel J. Smith +Author-email: njs@pobox.com +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.7 +License-File: LICENSE.txt +Requires-Dist: typing-extensions ; python_version < "3.8" + +h11 +=== + +.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master + :target: https://travis-ci.org/python-hyper/h11 + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-hyper/h11 + :alt: Test coverage + +.. image:: https://readthedocs.org/projects/h11/badge/?version=latest + :target: http://h11.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +This is a little HTTP/1.1 library written from scratch in Python, +heavily inspired by `hyper-h2 `_. + +It's a "bring-your-own-I/O" library; h11 contains no IO code +whatsoever. This means you can hook h11 up to your favorite network +API, and that could be anything you want: synchronous, threaded, +asynchronous, or your own implementation of `RFC 6214 +`_ -- h11 won't judge you. +(Compare this to the current state of the art, where every time a `new +network API `_ comes along then someone +gets to start over reimplementing the entire HTTP protocol from +scratch.) Cory Benfield made an `excellent blog post describing the +benefits of this approach +`_, or if you like video +then here's his `PyCon 2016 talk on the same theme +`_. + +This also means that h11 is not immediately useful out of the box: +it's a toolkit for building programs that speak HTTP, not something +that could directly replace ``requests`` or ``twisted.web`` or +whatever. But h11 makes it much easier to implement something like +``requests`` or ``twisted.web``. + +At a high level, working with h11 goes like this: + +1) First, create an ``h11.Connection`` object to track the state of a + single HTTP/1.1 connection. + +2) When you read data off the network, pass it to + ``conn.receive_data(...)``; you'll get back a list of objects + representing high-level HTTP "events". + +3) When you want to send a high-level HTTP event, create the + corresponding "event" object and pass it to ``conn.send(...)``; + this will give you back some bytes that you can then push out + through the network. + +For example, a client might instantiate and then send a +``h11.Request`` object, then zero or more ``h11.Data`` objects for the +request body (e.g., if this is a POST), and then a +``h11.EndOfMessage`` to indicate the end of the message. Then the +server would then send back a ``h11.Response``, some ``h11.Data``, and +its own ``h11.EndOfMessage``. If either side violates the protocol, +you'll get a ``h11.ProtocolError`` exception. + +h11 is suitable for implementing both servers and clients, and has a +pleasantly symmetric API: the events you send as a client are exactly +the ones that you receive as a server and vice-versa. + +`Here's an example of a tiny HTTP client +`_ + +It also has `a fine manual `_. + +FAQ +--- + +*Whyyyyy?* + +I wanted to play with HTTP in `Curio +`__ and `Trio +`__, which at the time didn't have any +HTTP libraries. So I thought, no big deal, Python has, like, a dozen +different implementations of HTTP, surely I can find one that's +reusable. I didn't find one, but I did find Cory's call-to-arms +blog-post. So I figured, well, fine, if I have to implement HTTP from +scratch, at least I can make sure no-one *else* has to ever again. + +*Should I use it?* + +Maybe. You should be aware that it's a very young project. But, it's +feature complete and has an exhaustive test-suite and complete docs, +so the next step is for people to try using it and see how it goes +:-). If you do then please let us know -- if nothing else we'll want +to talk to you before making any incompatible changes! + +*What are the features/limitations?* + +Roughly speaking, it's trying to be a robust, complete, and non-hacky +implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: +HTTP/1.1 Message Syntax and Routing +`_. That is, it mostly focuses on +implementing HTTP at the level of taking bytes on and off the wire, +and the headers related to that, and tries to be anal about spec +conformance. It doesn't know about higher-level concerns like URL +routing, conditional GETs, cross-origin cookie policies, or content +negotiation. But it does know how to take care of framing, +cross-version differences in keep-alive handling, and the "obsolete +line folding" rule, so you can focus your energies on the hard / +interesting parts for your application, and it tries to support the +full specification in the sense that any useful HTTP/1.1 conformant +application should be able to use h11. + +It's pure Python, and has no dependencies outside of the standard +library. + +It has a test suite with 100.0% coverage for both statements and +branches. + +Currently it supports Python 3 (testing on 3.7-3.10) and PyPy 3. +The last Python 2-compatible version was h11 0.11.x. +(Originally it had a Cython wrapper for `http-parser +`_ and a beautiful nested state +machine implemented with ``yield from`` to postprocess the output. But +I had to take these out -- the new *parser* needs fewer lines-of-code +than the old *parser wrapper*, is written in pure Python, uses no +exotic language syntax, and has more features. It's sad, really; that +old state machine was really slick. I just need a few sentences here +to mourn that.) + +I don't know how fast it is. I haven't benchmarked or profiled it yet, +so it's probably got a few pointless hot spots, and I've been trying +to err on the side of simplicity and robustness instead of +micro-optimization. But at the architectural level I tried hard to +avoid fundamentally bad decisions, e.g., I believe that all the +parsing algorithms remain linear-time even in the face of pathological +input like slowloris, and there are no byte-by-byte loops. (I also +believe that it maintains bounded memory usage in the face of +arbitrary/pathological input.) + +The whole library is ~800 lines-of-code. You can read and understand +the whole thing in less than an hour. Most of the energy invested in +this so far has been spent on trying to keep things simple by +minimizing special-cases and ad hoc state manipulation; even though it +is now quite small and simple, I'm still annoyed that I haven't +figured out how to make it even smaller and simpler. (Unfortunately, +HTTP does not lend itself to simplicity.) + +The API is ~feature complete and I don't expect the general outlines +to change much, but you can't judge an API's ergonomics until you +actually document and use it, so I'd expect some changes in the +details. + +*How do I try it?* + +.. code-block:: sh + + $ pip install h11 + $ git clone git@github.com:python-hyper/h11 + $ cd h11/examples + $ python basic-client.py + +and go from there. + +*License?* + +MIT + +*Code of conduct?* + +Contributors are requested to follow our `code of conduct +`_ in +all project spaces. diff --git a/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b b/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b new file mode 100644 index 0000000..be6560a Binary files /dev/null and b/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b differ diff --git a/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b.body b/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b.body new file mode 100644 index 0000000..318d2c0 Binary files /dev/null and b/.cache/pip/http-v2/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b.body differ diff --git a/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091 b/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091 new file mode 100644 index 0000000..f93d361 Binary files /dev/null and b/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091 differ diff --git a/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091.body b/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091.body new file mode 100644 index 0000000..3e95a11 Binary files /dev/null and b/.cache/pip/http-v2/8/a/d/9/8/8ad98ba95cbb3affc5cc95b8295de46584e521fb318b9a5899dc3091.body differ diff --git a/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c b/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c new file mode 100644 index 0000000..c66ee8e Binary files /dev/null and b/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c differ diff --git a/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c.body b/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c.body new file mode 100644 index 0000000..949c802 Binary files /dev/null and b/.cache/pip/http-v2/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c.body differ diff --git a/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa b/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa new file mode 100644 index 0000000..1f734e5 Binary files /dev/null and b/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa differ diff --git a/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa.body b/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa.body new file mode 100644 index 0000000..bfd5d2e Binary files /dev/null and b/.cache/pip/http-v2/8/b/7/f/d/8b7fd633e25691e0bc17b57c37da71f10f31d4b10f0ca7ba9e5df1fa.body differ diff --git a/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49 b/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49 new file mode 100644 index 0000000..f2d5d87 Binary files /dev/null and b/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49 differ diff --git a/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49.body b/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49.body new file mode 100644 index 0000000..7a6bbb2 --- /dev/null +++ b/.cache/pip/http-v2/8/c/3/e/c/8c3ecf585c815a73af16247323d407e99afb8fdc40cee3542187ad49.body @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.7 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Chat: https://discord.gg/pallets diff --git a/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 b/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 new file mode 100644 index 0000000..f777733 Binary files /dev/null and b/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 differ diff --git a/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9.body b/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9.body new file mode 100644 index 0000000..d8a5fba Binary files /dev/null and b/.cache/pip/http-v2/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9.body differ diff --git a/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a b/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a new file mode 100644 index 0000000..643c496 Binary files /dev/null and b/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a differ diff --git a/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a.body b/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a.body new file mode 100644 index 0000000..24ec80e Binary files /dev/null and b/.cache/pip/http-v2/9/1/0/a/3/910a34a200b7d0937d776a3a18a9596fb2163b443301e08a7ae6239a.body differ diff --git a/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b b/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b new file mode 100644 index 0000000..32e5862 Binary files /dev/null and b/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b differ diff --git a/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b.body b/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b.body new file mode 100644 index 0000000..32a413f Binary files /dev/null and b/.cache/pip/http-v2/9/1/a/c/4/91ac4141ee513b48d6e2106b8504eedf08543a9ac70e664c84a86c9b.body differ diff --git a/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f b/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f new file mode 100644 index 0000000..e78d775 Binary files /dev/null and b/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f differ diff --git a/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f.body b/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f.body new file mode 100644 index 0000000..41a992b Binary files /dev/null and b/.cache/pip/http-v2/9/7/2/2/f/9722fb20e3ea9cc35611a7af210f3a3c5763e70a73b0619f2dce743f.body differ diff --git a/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016 b/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016 new file mode 100644 index 0000000..c1cd392 Binary files /dev/null and b/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016 differ diff --git a/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016.body b/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016.body new file mode 100644 index 0000000..05779fa --- /dev/null +++ b/.cache/pip/http-v2/9/9/4/1/2/99412fc32269b7ad0e422d90fb80e27b631d156cfbf7c60691b9a016.body @@ -0,0 +1,122 @@ +Metadata-Version: 2.1 +Name: requests +Version: 2.31.0 +Summary: Python HTTP for Humans. +Home-page: https://requests.readthedocs.io +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +License: Apache 2.0 +Project-URL: Documentation, https://requests.readthedocs.io +Project-URL: Source, https://github.com/psf/requests +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: charset-normalizer (<4,>=2) +Requires-Dist: idna (<4,>=2.5) +Requires-Dist: urllib3 (<3,>=1.21.1) +Requires-Dist: certifi (>=2017.4.17) +Provides-Extra: security +Provides-Extra: socks +Requires-Dist: PySocks (!=1.5.7,>=1.5.6) ; extra == 'socks' +Provides-Extra: use_chardet_on_py3 +Requires-Dist: chardet (<6,>=3.0.2) ; extra == 'use_chardet_on_py3' + +# Requests + +**Requests** is a simple, yet elegant, HTTP library. + +```python +>>> import requests +>>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) +>>> r.status_code +200 +>>> r.headers['content-type'] +'application/json; charset=utf8' +>>> r.encoding +'utf-8' +>>> r.text +'{"authenticated": true, ...' +>>> r.json() +{'authenticated': True, ...} +``` + +Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! + +Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. + +[![Downloads](https://pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) +[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) +[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) + +## Installing Requests and Supported Versions + +Requests is available on PyPI: + +```console +$ python -m pip install requests +``` + +Requests officially supports Python 3.7+. + +## Supported Features & Best–Practices + +Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. + +- Keep-Alive & Connection Pooling +- International Domains and URLs +- Sessions with Cookie Persistence +- Browser-style TLS/SSL Verification +- Basic & Digest Authentication +- Familiar `dict`–like Cookies +- Automatic Content Decompression and Decoding +- Multi-part File Uploads +- SOCKS Proxy Support +- Connection Timeouts +- Streaming Downloads +- Automatic honoring of `.netrc` +- Chunked HTTP Requests + +## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) + +[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) + +## Cloning the repository + +When cloning the Requests repository, you may need to add the `-c +fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see +[this issue](https://github.com/psf/requests/issues/2690) for more background): + +```shell +git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git +``` + +You can also apply this setting to your global Git config: + +```shell +git config --global fetch.fsck.badTimezone ignore +``` + +--- + +[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) + + diff --git a/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa b/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa new file mode 100644 index 0000000..46eccce Binary files /dev/null and b/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa differ diff --git a/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa.body b/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa.body new file mode 100644 index 0000000..b5ec37c --- /dev/null +++ b/.cache/pip/http-v2/9/b/4/4/d/9b44ddb3ce19176840f0d81308ba3e957400e170196d3c17aac15dfa.body @@ -0,0 +1,209 @@ +Metadata-Version: 2.1 +Name: httpx +Version: 0.27.0 +Summary: The next generation HTTP client. +Project-URL: Changelog, https://github.com/encode/httpx/blob/master/CHANGELOG.md +Project-URL: Documentation, https://www.python-httpx.org +Project-URL: Homepage, https://github.com/encode/httpx +Project-URL: Source, https://github.com/encode/httpx +Author-email: Tom Christie +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: anyio +Requires-Dist: certifi +Requires-Dist: httpcore==1.* +Requires-Dist: idna +Requires-Dist: sniffio +Provides-Extra: brotli +Requires-Dist: brotli; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: cli +Requires-Dist: click==8.*; extra == 'cli' +Requires-Dist: pygments==2.*; extra == 'cli' +Requires-Dist: rich<14,>=10; extra == 'cli' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Description-Content-Type: text/markdown + +

+ HTTPX +

+ +

HTTPX - A next-generation HTTP client for Python.

+ +

+ + Test Suite + + + Package version + +

+ +HTTPX is a fully featured HTTP client library for Python 3. It includes **an integrated +command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync +and async APIs**. + +--- + +Install HTTPX using pip: + +```shell +$ pip install httpx +``` + +Now, let's get started: + +```pycon +>>> import httpx +>>> r = httpx.get('https://www.example.org/') +>>> r + +>>> r.status_code +200 +>>> r.headers['content-type'] +'text/html; charset=UTF-8' +>>> r.text +'\n\n\nExample Domain...' +``` + +Or, using the command-line client. + +```shell +$ pip install 'httpx[cli]' # The command line client is an optional dependency. +``` + +Which now allows us to use HTTPX directly from the command-line... + +

+ httpx --help +

+ +Sending a request... + +

+ httpx http://httpbin.org/json +

+ +## Features + +HTTPX builds on the well-established usability of `requests`, and gives you: + +* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/). +* An integrated command-line client. +* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/). +* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/). +* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/#calling-into-python-web-apps) or [ASGI applications](https://www.python-httpx.org/async/#calling-into-python-web-apps). +* Strict timeouts everywhere. +* Fully type annotated. +* 100% test coverage. + +Plus all the standard features of `requests`... + +* International Domains and URLs +* Keep-Alive & Connection Pooling +* Sessions with Cookie Persistence +* Browser-style SSL Verification +* Basic/Digest Authentication +* Elegant Key/Value Cookies +* Automatic Decompression +* Automatic Content Decoding +* Unicode Response Bodies +* Multipart File Uploads +* HTTP(S) Proxy Support +* Connection Timeouts +* Streaming Downloads +* .netrc Support +* Chunked Requests + +## Installation + +Install with pip: + +```shell +$ pip install httpx +``` + +Or, to include the optional HTTP/2 support, use: + +```shell +$ pip install httpx[http2] +``` + +HTTPX requires Python 3.8+. + +## Documentation + +Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/). + +For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/). + +For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section. + +The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference. + +To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/). + +## Contribute + +If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start. + +## Dependencies + +The HTTPX project relies on these excellent libraries: + +* `httpcore` - The underlying transport implementation for `httpx`. + * `h11` - HTTP/1.1 support. +* `certifi` - SSL certificates. +* `idna` - Internationalized domain name support. +* `sniffio` - Async library autodetection. + +As well as these optional installs: + +* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)* +* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)* +* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)* +* `click` - Command line client support. *(Optional, with `httpx[cli]`)* +* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)* + +A huge amount of credit is due to `requests` for the API layout that +much of this work follows, as well as to `urllib3` for plenty of design +inspiration around the lower-level networking details. + +--- + +

HTTPX is BSD licensed code.
Designed & crafted with care.

— 🦋 —

+ +## Release Information + +### Deprecated + +* The `app=...` shortcut has been deprecated. Use the explicit style of `transport=httpx.WSGITransport()` or `transport=httpx.ASGITransport()` instead. + +### Fixed + +* Respect the `http1` argument while configuring proxy transports. (#3023) +* Fix RFC 2069 mode digest authentication. (#3045) + + +--- + +[Full changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md) diff --git a/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30 b/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30 new file mode 100644 index 0000000..7f59a75 Binary files /dev/null and b/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30 differ diff --git a/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30.body b/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30.body new file mode 100644 index 0000000..cc725e8 --- /dev/null +++ b/.cache/pip/http-v2/a/b/6/a/5/ab6a54368522d8c0b057c68413a836962e4e9b02fc5c7f58a2549e30.body @@ -0,0 +1,160 @@ +Metadata-Version: 2.3 +Name: pydantic_core +Version: 2.18.2 +Classifier: Development Status :: 3 - Alpha +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Rust +Classifier: Framework :: Pydantic +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS +Classifier: Typing :: Typed +Requires-Dist: typing-extensions >=4.6.0, !=4.7.0 +License-File: LICENSE +Summary: Core functionality for Pydantic validation and serialization +Home-Page: https://github.com/pydantic/pydantic-core +Author-email: Samuel Colvin +License: MIT +Requires-Python: >=3.8 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM +Project-URL: Homepage, https://github.com/pydantic/pydantic-core +Project-URL: Funding, https://github.com/sponsors/samuelcolvin +Project-URL: Source, https://github.com/pydantic/pydantic-core + +# pydantic-core + +[![CI](https://github.com/pydantic/pydantic-core/workflows/ci/badge.svg?event=push)](https://github.com/pydantic/pydantic-core/actions?query=event%3Apush+branch%3Amain+workflow%3Aci) +[![Coverage](https://codecov.io/gh/pydantic/pydantic-core/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/pydantic-core) +[![pypi](https://img.shields.io/pypi/v/pydantic-core.svg)](https://pypi.python.org/pypi/pydantic-core) +[![versions](https://img.shields.io/pypi/pyversions/pydantic-core.svg)](https://github.com/pydantic/pydantic-core) +[![license](https://img.shields.io/github/license/pydantic/pydantic-core.svg)](https://github.com/pydantic/pydantic-core/blob/main/LICENSE) + +This package provides the core functionality for [pydantic](https://docs.pydantic.dev) validation and serialization. + +Pydantic-core is currently around 17x faster than pydantic V1. +See [`tests/benchmarks/`](./tests/benchmarks/) for details. + +## Example of direct usage + +_NOTE: You should not need to use pydantic-core directly; instead, use pydantic, which in turn uses pydantic-core._ + +```py +from pydantic_core import SchemaValidator, ValidationError + + +v = SchemaValidator( + { + 'type': 'typed-dict', + 'fields': { + 'name': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'str', + }, + }, + 'age': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'int', + 'ge': 18, + }, + }, + 'is_developer': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'default', + 'schema': {'type': 'bool'}, + 'default': True, + }, + }, + }, + } +) + +r1 = v.validate_python({'name': 'Samuel', 'age': 35}) +assert r1 == {'name': 'Samuel', 'age': 35, 'is_developer': True} + +# pydantic-core can also validate JSON directly +r2 = v.validate_json('{"name": "Samuel", "age": 35}') +assert r1 == r2 + +try: + v.validate_python({'name': 'Samuel', 'age': 11}) +except ValidationError as e: + print(e) + """ + 1 validation error for model + age + Input should be greater than or equal to 18 + [type=greater_than_equal, context={ge: 18}, input_value=11, input_type=int] + """ +``` + +## Getting Started + +You'll need rust stable [installed](https://rustup.rs/), or rust nightly if you want to generate accurate coverage. + +With rust and python 3.8+ installed, compiling pydantic-core should be possible with roughly the following: + +```bash +# clone this repo or your fork +git clone git@github.com:pydantic/pydantic-core.git +cd pydantic-core +# create a new virtual env +python3 -m venv env +source env/bin/activate +# install dependencies and install pydantic-core +make install +``` + +That should be it, the example shown above should now run. + +You might find it useful to look at [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) and +[`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) for more information on the python API, +beyond that, [`tests/`](./tests) provide a large number of examples of usage. + +If you want to contribute to pydantic-core, you'll want to use some other make commands: +* `make build-dev` to build the package during development +* `make build-prod` to perform an optimised build for benchmarking +* `make test` to run the tests +* `make testcov` to run the tests and generate a coverage report +* `make lint` to run the linter +* `make format` to format python and rust code +* `make` to run `format build-dev lint test` + +## Profiling + +It's possible to profile the code using the [`flamegraph` utility from `flamegraph-rs`](https://github.com/flamegraph-rs/flamegraph). (Tested on Linux.) You can install this with `cargo install flamegraph`. + +Run `make build-profiling` to install a release build with debugging symbols included (needed for profiling). + +Once that is built, you can profile pytest benchmarks with (e.g.): + +```bash +flamegraph -- pytest tests/benchmarks/test_micro_benchmarks.py -k test_list_of_ints_core_py --benchmark-enable +``` +The `flamegraph` command will produce an interactive SVG at `flamegraph.svg`. + +## Releasing + +1. Bump package version locally. Do not just edit `Cargo.toml` on Github, you need both `Cargo.toml` and `Cargo.lock` to be updated. +2. Make a PR for the version bump and merge it. +3. Go to https://github.com/pydantic/pydantic-core/releases and click "Draft a new release" +4. In the "Choose a tag" dropdown enter the new tag `v` and select "Create new tag on publish" when the option appears. +5. Enter the release title in the form "v " +6. Click Generate release notes button +7. Click Publish release +8. Go to https://github.com/pydantic/pydantic-core/actions and ensure that all build for release are done successfully. +9. Go to https://pypi.org/project/pydantic-core/ and ensure that the latest release is published. +10. Done 🎉 + diff --git a/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c b/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c new file mode 100644 index 0000000..7abadad Binary files /dev/null and b/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c differ diff --git a/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c.body b/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c.body new file mode 100644 index 0000000..bd81273 Binary files /dev/null and b/.cache/pip/http-v2/a/c/5/4/0/ac54069ce3005f186e18ca5efecdb2f814d717ad8b72989b4c98c88c.body differ diff --git a/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f b/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f new file mode 100644 index 0000000..9b537d1 Binary files /dev/null and b/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f differ diff --git a/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f.body b/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f.body new file mode 100644 index 0000000..d802d01 Binary files /dev/null and b/.cache/pip/http-v2/a/e/1/3/6/ae1360498e2a7e78c3695d71a4132c94b805a9c41d71ad8f62ad998f.body differ diff --git a/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303 b/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303 new file mode 100644 index 0000000..7ef1b4b Binary files /dev/null and b/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303 differ diff --git a/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303.body b/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303.body new file mode 100644 index 0000000..fdd704b Binary files /dev/null and b/.cache/pip/http-v2/b/0/7/8/4/b078435f1af3934531a099e2234b9bfa25b392a3335bc0785af7f303.body differ diff --git a/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68 b/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68 new file mode 100644 index 0000000..b76009a Binary files /dev/null and b/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68 differ diff --git a/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68.body b/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68.body new file mode 100644 index 0000000..c20be76 --- /dev/null +++ b/.cache/pip/http-v2/b/6/5/f/5/b65f54b37b9c38d0fd7e5a2e29442e042337680acc6fbd04ec4fba68.body @@ -0,0 +1,202 @@ +Metadata-Version: 2.1 +Name: attrs +Version: 23.2.0 +Summary: Classes Without Boilerplate +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: GitHub, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Author-email: Hynek Schlawack +License-Expression: MIT +License-File: LICENSE +Keywords: attribute,boilerplate,class +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Requires-Dist: importlib-metadata; python_version < '3.8' +Provides-Extra: cov +Requires-Dist: attrs[tests]; extra == 'cov' +Requires-Dist: coverage[toml]>=5.3; extra == 'cov' +Provides-Extra: dev +Requires-Dist: attrs[tests]; extra == 'dev' +Requires-Dist: pre-commit; extra == 'dev' +Provides-Extra: docs +Requires-Dist: furo; extra == 'docs' +Requires-Dist: myst-parser; extra == 'docs' +Requires-Dist: sphinx; extra == 'docs' +Requires-Dist: sphinx-notfound-page; extra == 'docs' +Requires-Dist: sphinxcontrib-towncrier; extra == 'docs' +Requires-Dist: towncrier; extra == 'docs' +Requires-Dist: zope-interface; extra == 'docs' +Provides-Extra: tests +Requires-Dist: attrs[tests-no-zope]; extra == 'tests' +Requires-Dist: zope-interface; extra == 'tests' +Provides-Extra: tests-mypy +Requires-Dist: mypy>=1.6; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy' +Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy' +Provides-Extra: tests-no-zope +Requires-Dist: attrs[tests-mypy]; extra == 'tests-no-zope' +Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'tests-no-zope' +Requires-Dist: hypothesis; extra == 'tests-no-zope' +Requires-Dist: pympler; extra == 'tests-no-zope' +Requires-Dist: pytest-xdist[psutil]; extra == 'tests-no-zope' +Requires-Dist: pytest>=4.3.0; extra == 'tests-no-zope' +Description-Content-Type: text/markdown + +

+ + attrs + +

+ + +*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). +[Trusted by NASA](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile#list-of-qualifying-repositories-for-mars-2020-helicopter-contributor-achievement) for Mars missions since 2020! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + + +## Sponsors + +*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). +Especially those generously supporting us at the *The Organization* tier and higher: + +

+ + + +

+ +

+ Please consider joining them to help make attrs’s maintenance more sustainable! +

+ + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +**Hate type annotations**!? +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Please check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for a more in-depth explanation. + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes). + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: please use the `python-attrs` tag on [StackOverflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. +[Learn more.](https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +## Release Information + +### Changes + +- The type annotation for `attrs.resolve_types()` is now correct. + [#1141](https://github.com/python-attrs/attrs/issues/1141) +- Type stubs now use `typing.dataclass_transform` to decorate dataclass-like decorators, instead of the non-standard `__dataclass_transform__` special form, which is only supported by Pyright. + [#1158](https://github.com/python-attrs/attrs/issues/1158) +- Fixed serialization of namedtuple fields using `attrs.asdict/astuple()` with `retain_collection_types=True`. + [#1165](https://github.com/python-attrs/attrs/issues/1165) +- `attrs.AttrsInstance` is now a `typing.Protocol` in both type hints and code. + This allows you to subclass it along with another `Protocol`. + [#1172](https://github.com/python-attrs/attrs/issues/1172) +- If *attrs* detects that `__attrs_pre_init__` accepts more than just `self`, it will call it with the same arguments as `__init__` was called. + This allows you to, for example, pass arguments to `super().__init__()`. + [#1187](https://github.com/python-attrs/attrs/issues/1187) +- Slotted classes now transform `functools.cached_property` decorated methods to support equivalent semantics. + [#1200](https://github.com/python-attrs/attrs/issues/1200) +- Added *class_body* argument to `attrs.make_class()` to provide additional attributes for newly created classes. + It is, for example, now possible to attach methods. + [#1203](https://github.com/python-attrs/attrs/issues/1203) + + +--- + +[Full changelog](https://www.attrs.org/en/stable/changelog.html) diff --git a/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80 b/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80 new file mode 100644 index 0000000..5d758d0 Binary files /dev/null and b/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80 differ diff --git a/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80.body b/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80.body new file mode 100644 index 0000000..5d1d348 Binary files /dev/null and b/.cache/pip/http-v2/b/f/b/b/b/bfbbb64251bb29d7400e962e6937ccfc7d6cf8628b719dcd859d1c80.body differ diff --git a/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc b/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc new file mode 100644 index 0000000..e265167 Binary files /dev/null and b/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc differ diff --git a/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc.body b/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc.body new file mode 100644 index 0000000..f67e29a Binary files /dev/null and b/.cache/pip/http-v2/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc.body differ diff --git a/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363 b/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363 new file mode 100644 index 0000000..7b3d1cd Binary files /dev/null and b/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363 differ diff --git a/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363.body b/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363.body new file mode 100644 index 0000000..de3a11e Binary files /dev/null and b/.cache/pip/http-v2/c/3/7/e/d/c37ede983887072d346d55bb44a2255855276b7e571a9740c09ab363.body differ diff --git a/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c b/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c new file mode 100644 index 0000000..cc867f6 Binary files /dev/null and b/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c differ diff --git a/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c.body b/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c.body new file mode 100644 index 0000000..3ee82bb Binary files /dev/null and b/.cache/pip/http-v2/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c.body differ diff --git a/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 b/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 new file mode 100644 index 0000000..23b99f4 Binary files /dev/null and b/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 differ diff --git a/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00.body b/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00.body new file mode 100644 index 0000000..63e04c3 Binary files /dev/null and b/.cache/pip/http-v2/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00.body differ diff --git a/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c b/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c new file mode 100644 index 0000000..6f42a0e Binary files /dev/null and b/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c differ diff --git a/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c.body b/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c.body new file mode 100644 index 0000000..dca1d32 Binary files /dev/null and b/.cache/pip/http-v2/c/b/5/d/d/cb5ddbdc2e83dee401eb4890530d679897c52a62f4550660f05c418c.body differ diff --git a/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2 b/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2 new file mode 100644 index 0000000..c64d862 Binary files /dev/null and b/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2 differ diff --git a/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2.body b/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2.body new file mode 100644 index 0000000..8064269 Binary files /dev/null and b/.cache/pip/http-v2/d/3/5/7/7/d357749e65aa5b8ea4cc8a97ac07a264ad9371f274544c5a8587bfa2.body differ diff --git a/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67 b/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67 new file mode 100644 index 0000000..2728a31 Binary files /dev/null and b/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67 differ diff --git a/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67.body b/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67.body new file mode 100644 index 0000000..0ca5d79 --- /dev/null +++ b/.cache/pip/http-v2/d/4/f/9/5/d4f95de6f509c2183092873ff2ae35c6ffcb3c039540db5bf2d5fc67.body @@ -0,0 +1,165 @@ +Metadata-Version: 2.1 +Name: joblib +Version: 1.4.0 +Summary: Lightweight pipelining with Python functions +Author-email: Gael Varoquaux +License: BSD 3-Clause +Project-URL: Homepage, https://joblib.readthedocs.io +Project-URL: Source, https://github.com/joblib/joblib +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: Intended Audience :: Education +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Utilities +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt + +|PyPi| |Azure| |ReadTheDocs| |Codecov| + +.. |PyPi| image:: https://badge.fury.io/py/joblib.svg + :target: https://badge.fury.io/py/joblib + :alt: Joblib version + +.. |Azure| image:: https://dev.azure.com/joblib/joblib/_apis/build/status/joblib.joblib?branchName=master + :target: https://dev.azure.com/joblib/joblib/_build?definitionId=3&_a=summary&branchFilter=40 + :alt: Azure CI status + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/joblib/badge/?version=latest + :target: https://joblib.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. |Codecov| image:: https://codecov.io/gh/joblib/joblib/branch/master/graph/badge.svg + :target: https://codecov.io/gh/joblib/joblib + :alt: Codecov coverage + + +The homepage of joblib with user documentation is located on: + +https://joblib.readthedocs.io + +Getting the latest code +======================= + +To get the latest code using git, simply type:: + + git clone https://github.com/joblib/joblib.git + +If you don't have git installed, you can download a zip +of the latest code: https://github.com/joblib/joblib/archive/refs/heads/master.zip + +Installing +========== + +You can use `pip` to install joblib:: + + pip install joblib + +from any directory or:: + + python setup.py install + +from the source directory. + +Dependencies +============ + +- Joblib has no mandatory dependencies besides Python (supported versions are + 3.8+). +- Joblib has an optional dependency on Numpy (at least version 1.6.1) for array + manipulation. +- Joblib includes its own vendored copy of + `loky `_ for process management. +- Joblib can efficiently dump and load numpy arrays but does not require numpy + to be installed. +- Joblib has an optional dependency on + `python-lz4 `_ as a faster alternative to + zlib and gzip for compressed serialization. +- Joblib has an optional dependency on psutil to mitigate memory leaks in + parallel worker processes. +- Some examples require external dependencies such as pandas. See the + instructions in the `Building the docs`_ section for details. + +Workflow to contribute +====================== + +To contribute to joblib, first create an account on `github +`_. Once this is done, fork the `joblib repository +`_ to have your own repository, +clone it using 'git clone' on the computers where you want to work. Make +your changes in your clone, push them to your github account, test them +on several computers, and when you are happy with them, send a pull +request to the main repository. + +Running the test suite +====================== + +To run the test suite, you need the pytest (version >= 3) and coverage modules. +Run the test suite using:: + + pytest joblib + +from the root of the project. + +Building the docs +================= + +To build the docs you need to have sphinx (>=1.4) and some dependencies +installed:: + + pip install -U -r .readthedocs-requirements.txt + +The docs can then be built with the following command:: + + make doc + +The html docs are located in the ``doc/_build/html`` directory. + + +Making a source tarball +======================= + +To create a source tarball, eg for packaging or distributing, run the +following command:: + + python setup.py sdist + +The tarball will be created in the `dist` directory. This command will +compile the docs, and the resulting tarball can be installed with +no extra dependencies than the Python standard library. You will need +setuptool and sphinx. + +Making a release and uploading it to PyPI +========================================= + +This command is only run by project manager, to make a release, and +upload in to PyPI:: + + python setup.py sdist bdist_wheel + twine upload dist/* + + +Note that the documentation should automatically get updated at each git +push. If that is not the case, try building th doc locally and resolve +any doc build error (in particular when running the examples). + +Updating the changelog +====================== + +Changes are listed in the CHANGES.rst file. They must be manually updated +but, the following git command may be used to generate the lines:: + + git log --abbrev-commit --date=short --no-merges --sparse + diff --git a/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b b/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b new file mode 100644 index 0000000..515c746 Binary files /dev/null and b/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b differ diff --git a/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b.body b/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b.body new file mode 100644 index 0000000..e02715c --- /dev/null +++ b/.cache/pip/http-v2/d/5/a/9/a/d5a9a2dd8ade2361f2dc7b31ab80c1dd6e5372e33cec7d2daec32a4b.body @@ -0,0 +1,104 @@ +Metadata-Version: 2.1 +Name: anyio +Version: 4.3.0 +Summary: High level compatibility layer for multiple asynchronous event loop implementations +Author-email: Alex Grönholm +License: MIT +Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ +Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html +Project-URL: Source code, https://github.com/agronholm/anyio +Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: idna >=2.8 +Requires-Dist: sniffio >=1.1 +Requires-Dist: exceptiongroup >=1.0.2 ; python_version < "3.11" +Requires-Dist: typing-extensions >=4.1 ; python_version < "3.11" +Provides-Extra: doc +Requires-Dist: packaging ; extra == 'doc' +Requires-Dist: Sphinx >=7 ; extra == 'doc' +Requires-Dist: sphinx-rtd-theme ; extra == 'doc' +Requires-Dist: sphinx-autodoc-typehints >=1.2.0 ; extra == 'doc' +Provides-Extra: test +Requires-Dist: anyio[trio] ; extra == 'test' +Requires-Dist: coverage[toml] >=7 ; extra == 'test' +Requires-Dist: exceptiongroup >=1.2.0 ; extra == 'test' +Requires-Dist: hypothesis >=4.0 ; extra == 'test' +Requires-Dist: psutil >=5.9 ; extra == 'test' +Requires-Dist: pytest >=7.0 ; extra == 'test' +Requires-Dist: pytest-mock >=3.6.1 ; extra == 'test' +Requires-Dist: trustme ; extra == 'test' +Requires-Dist: uvloop >=0.17 ; (platform_python_implementation == "CPython" and platform_system != "Windows") and extra == 'test' +Provides-Extra: trio +Requires-Dist: trio >=0.23 ; extra == 'trio' + +.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/anyio/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/anyio?branch=master + :alt: Code Coverage +.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest + :target: https://anyio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation +.. image:: https://badges.gitter.im/gitterHQ/gitter.svg + :target: https://gitter.im/python-trio/AnyIO + :alt: Gitter chat + +AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or +trio_. It implements trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony +with the native SC of trio itself. + +Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or +trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full +refactoring necessary. It will blend in with the native libraries of your chosen backend. + +Documentation +------------- + +View full documentation at: https://anyio.readthedocs.io/ + +Features +-------- + +AnyIO offers the following functionality: + +* Task groups (nurseries_ in trio terminology) +* High-level networking (TCP, UDP and UNIX sockets) + + * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python + 3.8) + * async/await style UDP sockets (unlike asyncio where you still have to use Transports and + Protocols) + +* A versatile API for byte streams and object streams +* Inter-task synchronization and communication (locks, conditions, events, semaphores, object + streams) +* Worker threads +* Subprocesses +* Asynchronous file I/O (using worker threads) +* Signal handling + +AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. +It even works with the popular Hypothesis_ library. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _trio: https://github.com/python-trio/trio +.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency +.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning +.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs +.. _pytest: https://docs.pytest.org/en/latest/ +.. _Hypothesis: https://hypothesis.works/ diff --git a/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc b/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc new file mode 100644 index 0000000..bfb74ff Binary files /dev/null and b/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc differ diff --git a/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc.body b/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc.body new file mode 100644 index 0000000..fe67a0c Binary files /dev/null and b/.cache/pip/http-v2/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc.body differ diff --git a/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 b/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 new file mode 100644 index 0000000..39568a6 Binary files /dev/null and b/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 differ diff --git a/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8.body b/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8.body new file mode 100644 index 0000000..dd7fb3b Binary files /dev/null and b/.cache/pip/http-v2/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8.body differ diff --git a/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc b/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc new file mode 100644 index 0000000..ed798b5 Binary files /dev/null and b/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc differ diff --git a/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc.body b/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc.body new file mode 100644 index 0000000..4c81993 Binary files /dev/null and b/.cache/pip/http-v2/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc.body differ diff --git a/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87 b/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87 new file mode 100644 index 0000000..9f04d80 Binary files /dev/null and b/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87 differ diff --git a/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87.body b/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87.body new file mode 100644 index 0000000..548567b --- /dev/null +++ b/.cache/pip/http-v2/e/2/1/3/6/e21360abe1c43397cb20a687ec7ccd5adc2022e5b623da3094f74b87.body @@ -0,0 +1,1086 @@ +Metadata-Version: 2.1 +Name: yarl +Version: 1.9.4 +Summary: Yet another URL library +Home-page: https://github.com/aio-libs/yarl +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache-2.0 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Workflows, https://github.com/aio-libs/yarl/actions?query=branch:master +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/yarl +Project-URL: Docs: Changelog, https://yarl.aio-libs.org/en/latest/changes/ +Project-URL: Docs: RTD, https://yarl.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/yarl/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/yarl +Keywords: cython,cext,yarl +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: NOTICE +Requires-Dist: idna >=2.0 +Requires-Dist: multidict >=4.0 +Requires-Dist: typing-extensions >=3.7.4 ; python_version < "3.8" + +yarl +==== + +The module provides handy URL class for URL parsing and changing. + +.. image:: https://github.com/aio-libs/yarl/workflows/CI/badge.svg + :target: https://github.com/aio-libs/yarl/actions?query=workflow%3ACI + :align: right + +.. image:: https://codecov.io/gh/aio-libs/yarl/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/yarl + +.. image:: https://badge.fury.io/py/yarl.svg + :target: https://badge.fury.io/py/yarl + + +.. image:: https://readthedocs.org/projects/yarl/badge/?version=latest + :target: https://yarl.aio-libs.org + + +.. image:: https://img.shields.io/pypi/pyversions/yarl.svg + :target: https://pypi.python.org/pypi/yarl + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +Url is constructed from ``str``: + +.. code-block:: pycon + + >>> from yarl import URL + >>> url = URL('https://www.python.org/~guido?arg=1#frag') + >>> url + URL('https://www.python.org/~guido?arg=1#frag') + +All url parts: *scheme*, *user*, *password*, *host*, *port*, *path*, +*query* and *fragment* are accessible by properties: + +.. code-block:: pycon + + >>> url.scheme + 'https' + >>> url.host + 'www.python.org' + >>> url.path + '/~guido' + >>> url.query_string + 'arg=1' + >>> url.query + + >>> url.fragment + 'frag' + +All url manipulations produce a new url object: + +.. code-block:: pycon + + >>> url = URL('https://www.python.org') + >>> url / 'foo' / 'bar' + URL('https://www.python.org/foo/bar') + >>> url / 'foo' % {'bar': 'baz'} + URL('https://www.python.org/foo?bar=baz') + +Strings passed to constructor and modification methods are +automatically encoded giving canonical representation as result: + +.. code-block:: pycon + + >>> url = URL('https://www.python.org/шлях') + >>> url + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') + +Regular properties are *percent-decoded*, use ``raw_`` versions for +getting *encoded* strings: + +.. code-block:: pycon + + >>> url.path + '/шлях' + + >>> url.raw_path + '/%D1%88%D0%BB%D1%8F%D1%85' + +Human readable representation of URL is available as ``.human_repr()``: + +.. code-block:: pycon + + >>> url.human_repr() + 'https://www.python.org/шлях' + +For full documentation please read https://yarl.aio-libs.org. + + +Installation +------------ + +:: + + $ pip install yarl + +The library is Python 3 only! + +PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install +``yarl`` on another operating system (like *Alpine Linux*, which is not +manylinux-compliant because of the missing glibc and therefore, cannot be +used with our wheels) the the tarball will be used to compile the library from +the source code. It requires a C compiler and and Python headers installed. + +To skip the compilation you must explicitly opt-in by using a PEP 517 +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +environment variable to a non-empty value, e.g.: + +.. code-block:: console + + $ pip install yarl --config-settings=pure-python=false + +Please note that the pure-Python (uncompiled) version is much slower. However, +PyPy always uses a pure-Python implementation, and, as such, it is unaffected +by this variable. + +Dependencies +------------ + +YARL requires multidict_ library. + + +API documentation +------------------ + +The documentation is located at https://yarl.aio-libs.org. + + +Why isn't boolean supported by the URL query API? +------------------------------------------------- + +There is no standard for boolean representation of boolean values. + +Some systems prefer ``true``/``false``, others like ``yes``/``no``, ``on``/``off``, +``Y``/``N``, ``1``/``0``, etc. + +``yarl`` cannot make an unambiguous decision on how to serialize ``bool`` values because +it is specific to how the end-user's application is built and would be different for +different apps. The library doesn't accept booleans in the API; a user should convert +bools into strings using own preferred translation protocol. + + +Comparison with other URL libraries +------------------------------------ + +* furl (https://pypi.python.org/pypi/furl) + + The library has rich functionality but the ``furl`` object is mutable. + + I'm afraid to pass this object into foreign code: who knows if the + code will modify my url in a terrible way while I just want to send URL + with handy helpers for accessing URL properties. + + ``furl`` has other non-obvious tricky things but the main objection + is mutability. + +* URLObject (https://pypi.python.org/pypi/URLObject) + + URLObject is immutable, that's pretty good. + + Every URL change generates a new URL object. + + But the library doesn't do any decode/encode transformations leaving the + end user to cope with these gory details. + + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue on the `bug tracker +`_ if you have found a bug +or have some suggestion in order to improve the library. + +The library uses `Azure Pipelines `_ for +Continuous Integration. + +Discussion list +--------------- + +*aio-libs* google group: https://groups.google.com/forum/#!forum/aio-libs + +Feel free to post your questions and ideas here. + + +Authors and License +------------------- + +The ``yarl`` package is written by Andrew Svetlov. + +It's *Apache 2* licensed and freely available. + + +.. _GitHub: https://github.com/aio-libs/yarl + +.. _multidict: https://github.com/aio-libs/multidict + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/#adding-a-news-entry + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +1.9.4 (2023-12-06) +================== + +Bug fixes +--------- + +- Started raising ``TypeError`` when a string value is passed into + ``yarl.URL.build()`` as the ``port`` argument -- by `@commonism `__. + + Previously the empty string as port would create malformed URLs when rendered as string representations. (`#883 `__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- The leading ``--`` has been dropped from the `PEP 517 `__ in-tree build + backend config setting names. ``--pure-python`` is now just ``pure-python`` + -- by `@webknjaz `__. + + The usage now looks as follows: + + .. code-block:: console + + $ python -m build \ + --config-setting=pure-python=true \ + --config-setting=with-cython-tracing=true + + (`#963 `__) + + +Contributor-facing changes +-------------------------- + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *yarl* -- by `@webknjaz `__. + + This is primarily targeting maintainers. (`#960 `__) +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + To measure coverage in a development environment, *yarl* can be + installed in editable mode, which requires an environment variable + ``YARL_CYTHON_TRACING=1`` to be set: + + .. code-block:: console + + $ YARL_CYTHON_TRACING=1 python -Im pip install -e . + + Editable install produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files. (`#961 `__) +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. (`#962 `__) + + +1.9.3 (2023-11-20) +================== + +Bug fixes +--------- + +- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@gmacon `__. (`#862 `__, `#866 `__) +- Started accepting string subclasses in ``__truediv__()`` operations (``URL / segment``) -- by `@mjpieters `__. (`#871 `__, `#884 `__) +- Fixed the human representation of URLs with square brackets in usernames and passwords -- by `@mjpieters `__. (`#876 `__, `#882 `__) +- Updated type hints to include ``URL.missing_port()``, ``URL.__bytes__()`` + and the ``encoding`` argument to ``yarl.URL.joinpath()`` + -- by `@mjpieters `__. (`#891 `__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Integrated Cython 3 to enable building *yarl* under Python 3.12 -- by `@mjpieters `__. (`#829 `__, `#881 `__) +- Declared modern ``setuptools.build_meta`` as the `PEP 517 `__ build + backend in ``pyproject.toml`` explicitly -- by `@webknjaz `__. (`#886 `__) +- Converted most of the packaging setup into a declarative ``setup.cfg`` + config -- by `@webknjaz `__. (`#890 `__) +- The packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``yarl`` from + source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``--pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python -m pip install . --config-settings=--pure-python=false + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python -m build --config-setting=--pure-python=false + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. (`#893 `__) + + .. attention:: + + v1.9.3 was the only version using the ``--pure-python`` setting name. + Later versions dropped the ``--`` prefix, making it just ``pure-python``. + +- Declared Python 3.12 supported officially in the distribution package metadata + -- by `@edgarrmondragon `__. (`#942 `__) + + +Contributor-facing changes +-------------------------- + +- A regression test for no-host URLs was added per `#821 `__ + and ``3986`` -- by `@kenballus `__. (`#821 `__, `#822 `__) +- Started testing *yarl* against Python 3.12 in CI -- by `@mjpieters `__. (`#881 `__) +- All Python 3.12 jobs are now marked as required to pass in CI + -- by `@edgarrmondragon `__. (`#942 `__) +- MyST is now integrated in Sphinx -- by `@webknjaz `__. + + This allows the contributors to author new documents in Markdown + when they have difficulties with going straight RST. (`#953 `__) + + +1.9.2 (2023-04-25) +================== + +Bugfixes +-------- + +- Fix regression with ``__truediv__`` and absolute URLs with empty paths causing the raw path to lack the leading ``/``. + (`#854 `_) + + +1.9.1 (2023-04-21) +================== + +Bugfixes +-------- + +- Marked tests that fail on older Python patch releases (< 3.7.10, < 3.8.8 and < 3.9.2) as expected to fail due to missing a security fix for CVE-2021-23336. (`#850 `_) + + +1.9.0 (2023-04-19) +================== + +This release was never published to PyPI, due to issues with the build process. + +Features +-------- + +- Added ``URL.joinpath(*elements)``, to create a new URL appending multiple path elements. (`#704 `_) +- Made ``URL.__truediv__()`` return ``NotImplemented`` if called with an + unsupported type — by `@michaeljpeters `__. + (`#832 `_) + + +Bugfixes +-------- + +- Path normalization for absolute URLs no longer raises a ValueError exception + when ``..`` segments would otherwise go beyond the URL path root. + (`#536 `_) +- Fixed an issue with update_query() not getting rid of the query when argument is None. (`#792 `_) +- Added some input restrictions on with_port() function to prevent invalid boolean inputs or out of valid port inputs; handled incorrect 0 port representation. (`#793 `_) +- Made ``yarl.URL.build()`` raise a ``TypeError`` if the ``host`` argument is ``None`` — by `@paulpapacz `__. (`#808 `_) +- Fixed an issue with ``update_query()`` getting rid of the query when the argument + is empty but not ``None``. (`#845 `_) + + +Misc +---- + +- `#220 `_ + + +1.8.2 (2022-12-03) +================== + +This is the first release that started shipping wheels for Python 3.11. + + +1.8.1 (2022-08-01) +================== + +Misc +---- + +- `#694 `_, `#699 `_, `#700 `_, `#701 `_, `#702 `_, `#703 `_, `#739 `_ + + +1.8.0 (2022-08-01) +================== + +Features +-------- + +- Added ``URL.raw_suffix``, ``URL.suffix``, ``URL.raw_suffixes``, ``URL.suffixes``, ``URL.with_suffix``. (`#613 `_) + + +Improved Documentation +---------------------- + +- Fixed broken internal references to ``yarl.URL.human_repr()``. + (`#665 `_) +- Fixed broken external references to ``multidict:index`` docs. (`#665 `_) + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support. (`#672 `_) + + +Misc +---- + +- `#646 `_, `#699 `_, `#701 `_ + + +1.7.2 (2021-11-01) +================== + +Bugfixes +-------- + +- Changed call in ``with_port()`` to stop reencoding parts of the URL that were already encoded. (`#623 `_) + + +1.7.1 (2021-10-07) +================== + +Bugfixes +-------- + +- Fix 1.7.0 build error + +1.7.0 (2021-10-06) +================== + +Features +-------- + +- Add ``__bytes__()`` magic method so that ``bytes(url)`` will work and use optimal ASCII encoding. + (`#582 `_) +- Started shipping platform-specific arm64 wheels for Apple Silicon. (`#622 `_) +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. (`#622 `_) +- Added support for Python 3.10. (`#622 `_) + + +1.6.3 (2020-11-14) +================== + +Bugfixes +-------- + +- No longer loose characters when decoding incorrect percent-sequences (like ``%e2%82%f8``). All non-decodable percent-sequences are now preserved. + `#517 `_ +- Provide x86 Windows wheels. + `#535 `_ + + +---- + + +1.6.2 (2020-10-12) +================== + + +Bugfixes +-------- + +- Provide generated ``.c`` files in TarBall distribution. + `#530 `_ + +1.6.1 (2020-10-12) +================== + +Features +-------- + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + `#507 `_ +- Provide wheels for Python 3.9. + `#526 `_ + +Bugfixes +-------- + +- ``human_repr()`` now always produces valid representation equivalent to the original URL (if the original URL is valid). + `#511 `_ +- Fixed requoting a single percent followed by a percent-encoded character in the Cython implementation. + `#514 `_ +- Fix ValueError when decoding ``%`` which is not followed by two hexadecimal digits. + `#516 `_ +- Fix decoding ``%`` followed by a space and hexadecimal digit. + `#520 `_ +- Fix annotation of ``with_query()``/``update_query()`` methods for ``key=[val1, val2]`` case. + `#528 `_ + +Removal +------- + +- Drop Python 3.5 support; Python 3.6 is the minimal supported Python version. + + +---- + + +1.6.0 (2020-09-23) +================== + +Features +-------- + +- Allow for int and float subclasses in query, while still denying bool. + `#492 `_ + + +Bugfixes +-------- + +- Do not requote arguments in ``URL.build()``, ``with_xxx()`` and in ``/`` operator. + `#502 `_ +- Keep IPv6 brackets in ``origin()``. + `#504 `_ + + +---- + + +1.5.1 (2020-08-01) +================== + +Bugfixes +-------- + +- Fix including relocated internal ``yarl._quoting_c`` C-extension into published PyPI dists. + `#485 `_ + + +Misc +---- + +- `#484 `_ + + +---- + + +1.5.0 (2020-07-26) +================== + +Features +-------- + +- Convert host to lowercase on URL building. + `#386 `_ +- Allow using ``mod`` operator (``%``) for updating query string (an alias for ``update_query()`` method). + `#435 `_ +- Allow use of sequences such as ``list`` and ``tuple`` in the values + of a mapping such as ``dict`` to represent that a key has many values:: + + url = URL("http://example.com") + assert url.with_query({"a": [1, 2]}) == URL("http://example.com/?a=1&a=2") + + `#443 `_ +- Support ``URL.build()`` with scheme and path (creates a relative URL). + `#464 `_ +- Cache slow IDNA encode/decode calls. + `#476 `_ +- Add ``@final`` / ``Final`` type hints + `#477 `_ +- Support URL authority/raw_authority properties and authority argument of ``URL.build()`` method. + `#478 `_ +- Hide the library implementation details, make the exposed public list very clean. + `#483 `_ + + +Bugfixes +-------- + +- Fix tests with newer Python (3.7.6, 3.8.1 and 3.9.0+). + `#409 `_ +- Fix a bug where query component, passed in a form of mapping or sequence, is unquoted in unexpected way. + `#426 `_ +- Hide ``Query`` and ``QueryVariable`` type aliases in ``__init__.pyi``, now they are prefixed with underscore. + `#431 `_ +- Keep IPv6 brackets after updating port/user/password. + `#451 `_ + + +---- + + +1.4.2 (2019-12-05) +================== + +Features +-------- + +- Workaround for missing ``str.isascii()`` in Python 3.6 + `#389 `_ + + +---- + + +1.4.1 (2019-11-29) +================== + +* Fix regression, make the library work on Python 3.5 and 3.6 again. + +1.4.0 (2019-11-29) +================== + +* Distinguish an empty password in URL from a password not provided at all (#262) + +* Fixed annotations for optional parameters of ``URL.build`` (#309) + +* Use None as default value of ``user`` parameter of ``URL.build`` (#309) + +* Enforce building C Accelerated modules when installing from source tarball, use + ``YARL_NO_EXTENSIONS`` environment variable for falling back to (slower) Pure Python + implementation (#329) + +* Drop Python 3.5 support + +* Fix quoting of plus in path by pure python version (#339) + +* Don't create a new URL if fragment is unchanged (#292) + +* Included in error message the path that produces starting slash forbidden error (#376) + +* Skip slow IDNA encoding for ASCII-only strings (#387) + + +1.3.0 (2018-12-11) +================== + +* Fix annotations for ``query`` parameter (#207) + +* An incoming query sequence can have int variables (the same as for + Mapping type) (#208) + +* Add ``URL.explicit_port`` property (#218) + +* Give a friendlier error when port can't be converted to int (#168) + +* ``bool(URL())`` now returns ``False`` (#272) + +1.2.6 (2018-06-14) +================== + +* Drop Python 3.4 trove classifier (#205) + +1.2.5 (2018-05-23) +================== + +* Fix annotations for ``build`` (#199) + +1.2.4 (2018-05-08) +================== + +* Fix annotations for ``cached_property`` (#195) + +1.2.3 (2018-05-03) +================== + +* Accept ``str`` subclasses in ``URL`` constructor (#190) + +1.2.2 (2018-05-01) +================== + +* Fix build + +1.2.1 (2018-04-30) +================== + +* Pin minimal required Python to 3.5.3 (#189) + +1.2.0 (2018-04-30) +================== + +* Forbid inheritance, replace ``__init__`` with ``__new__`` (#171) + +* Support PEP-561 (provide type hinting marker) (#182) + +1.1.1 (2018-02-17) +================== + +* Fix performance regression: don't encode empty ``netloc`` (#170) + +1.1.0 (2018-01-21) +================== + +* Make pure Python quoter consistent with Cython version (#162) + +1.0.0 (2018-01-15) +================== + +* Use fast path if quoted string does not need requoting (#154) + +* Speed up quoting/unquoting by ``_Quoter`` and ``_Unquoter`` classes (#155) + +* Drop ``yarl.quote`` and ``yarl.unquote`` public functions (#155) + +* Add custom string writer, reuse static buffer if available (#157) + Code is 50-80 times faster than Pure Python version (was 4-5 times faster) + +* Don't recode IP zone (#144) + +* Support ``encoded=True`` in ``yarl.URL.build()`` (#158) + +* Fix updating query with multiple keys (#160) + +0.18.0 (2018-01-10) +=================== + +* Fallback to IDNA 2003 if domain name is not IDNA 2008 compatible (#152) + +0.17.0 (2017-12-30) +=================== + +* Use IDNA 2008 for domain name processing (#149) + +0.16.0 (2017-12-07) +=================== + +* Fix raising ``TypeError`` by ``url.query_string()`` after + ``url.with_query({})`` (empty mapping) (#141) + +0.15.0 (2017-11-23) +=================== + +* Add ``raw_path_qs`` attribute (#137) + +0.14.2 (2017-11-14) +=================== + +* Restore ``strict`` parameter as no-op in ``quote`` / ``unquote`` + +0.14.1 (2017-11-13) +=================== + +* Restore ``strict`` parameter as no-op for sake of compatibility with + aiohttp 2.2 + +0.14.0 (2017-11-11) +=================== + +* Drop strict mode (#123) + +* Fix ``"ValueError: Unallowed PCT %"`` when there's a ``"%"`` in the URL (#124) + +0.13.0 (2017-10-01) +=================== + +* Document ``encoded`` parameter (#102) + +* Support relative URLs like ``'?key=value'`` (#100) + +* Unsafe encoding for QS fixed. Encode ``;`` character in value parameter (#104) + +* Process passwords without user names (#95) + +0.12.0 (2017-06-26) +=================== + +* Properly support paths without leading slash in ``URL.with_path()`` (#90) + +* Enable type annotation checks + +0.11.0 (2017-06-26) +=================== + +* Normalize path (#86) + +* Clear query and fragment parts in ``.with_path()`` (#85) + +0.10.3 (2017-06-13) +=================== + +* Prevent double URL arguments unquoting (#83) + +0.10.2 (2017-05-05) +=================== + +* Unexpected hash behavior (#75) + + +0.10.1 (2017-05-03) +=================== + +* Unexpected compare behavior (#73) + +* Do not quote or unquote + if not a query string. (#74) + + +0.10.0 (2017-03-14) +=================== + +* Added ``URL.build`` class method (#58) + +* Added ``path_qs`` attribute (#42) + + +0.9.8 (2017-02-16) +================== + +* Do not quote ``:`` in path + + +0.9.7 (2017-02-16) +================== + +* Load from pickle without _cache (#56) + +* Percent-encoded pluses in path variables become spaces (#59) + + +0.9.6 (2017-02-15) +================== + +* Revert backward incompatible change (BaseURL) + + +0.9.5 (2017-02-14) +================== + +* Fix BaseURL rich comparison support + + +0.9.4 (2017-02-14) +================== + +* Use BaseURL + + +0.9.3 (2017-02-14) +================== + +* Added BaseURL + + +0.9.2 (2017-02-08) +================== + +* Remove debug print + + +0.9.1 (2017-02-07) +================== + +* Do not lose tail chars (#45) + + +0.9.0 (2017-02-07) +================== + +* Allow to quote ``%`` in non strict mode (#21) + +* Incorrect parsing of query parameters with %3B (;) inside (#34) + +* Fix core dumps (#41) + +* ``tmpbuf`` - compiling error (#43) + +* Added ``URL.update_path()`` method + +* Added ``URL.update_query()`` method (#47) + + +0.8.1 (2016-12-03) +================== + +* Fix broken aiohttp: revert back ``quote`` / ``unquote``. + + +0.8.0 (2016-12-03) +================== + +* Support more verbose error messages in ``.with_query()`` (#24) + +* Don't percent-encode ``@`` and ``:`` in path (#32) + +* Don't expose ``yarl.quote`` and ``yarl.unquote``, these functions are + part of private API + +0.7.1 (2016-11-18) +================== + +* Accept not only ``str`` but all classes inherited from ``str`` also (#25) + +0.7.0 (2016-11-07) +================== + +* Accept ``int`` as value for ``.with_query()`` + +0.6.0 (2016-11-07) +================== + +* Explicitly use UTF8 encoding in ``setup.py`` (#20) +* Properly unquote non-UTF8 strings (#19) + +0.5.3 (2016-11-02) +================== + +* Don't use ``typing.NamedTuple`` fields but indexes on URL construction + +0.5.2 (2016-11-02) +================== + +* Inline ``_encode`` class method + +0.5.1 (2016-11-02) +================== + +* Make URL construction faster by removing extra classmethod calls + +0.5.0 (2016-11-02) +================== + +* Add Cython optimization for quoting/unquoting +* Provide binary wheels + +0.4.3 (2016-09-29) +================== + +* Fix typing stubs + +0.4.2 (2016-09-29) +================== + +* Expose ``quote()`` and ``unquote()`` as public API + +0.4.1 (2016-09-28) +================== + +* Support empty values in query (``'/path?arg'``) + +0.4.0 (2016-09-27) +================== + +* Introduce ``relative()`` (#16) + +0.3.2 (2016-09-27) +================== + +* Typo fixes #15 + +0.3.1 (2016-09-26) +================== + +* Support sequence of pairs as ``with_query()`` parameter + +0.3.0 (2016-09-26) +================== + +* Introduce ``is_default_port()`` + +0.2.1 (2016-09-26) +================== + +* Raise ValueError for URLs like 'http://:8080/' + +0.2.0 (2016-09-18) +================== + +* Avoid doubling slashes when joining paths (#13) + +* Appending path starting from slash is forbidden (#12) + +0.1.4 (2016-09-09) +================== + +* Add ``kwargs`` support for ``with_query()`` (#10) + +0.1.3 (2016-09-07) +================== + +* Document ``with_query()``, ``with_fragment()`` and ``origin()`` + +* Allow ``None`` for ``with_query()`` and ``with_fragment()`` + +0.1.2 (2016-09-07) +================== + +* Fix links, tune docs theme. + +0.1.1 (2016-09-06) +================== + +* Update README, old version used obsolete API + +0.1.0 (2016-09-06) +================== + +* The library was deeply refactored, bytes are gone away but all + accepted strings are encoded if needed. + +0.0.1 (2016-08-30) +================== + +* The first release. diff --git a/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782 b/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782 new file mode 100644 index 0000000..ff336d3 Binary files /dev/null and b/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782 differ diff --git a/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782.body b/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782.body new file mode 100644 index 0000000..db9a731 Binary files /dev/null and b/.cache/pip/http-v2/e/3/5/1/4/e3514f4c1fd1aff417bcf78a683f64e5f53dccc56b1bf8878447a782.body differ diff --git a/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971 b/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971 new file mode 100644 index 0000000..b335ba8 Binary files /dev/null and b/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971 differ diff --git a/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971.body b/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971.body new file mode 100644 index 0000000..cd31264 --- /dev/null +++ b/.cache/pip/http-v2/e/6/d/1/2/e6d124fd93ad889e0efd08e45f1c0327a5d32945d7081732a1e98971.body @@ -0,0 +1,245 @@ +Metadata-Version: 2.1 +Name: aiohttp +Version: 3.9.5 +Summary: Async http client/server framework (asyncio) +Home-page: https://github.com/aio-libs/aiohttp +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp +Project-URL: Docs: Changelog, https://docs.aiohttp.org/en/stable/changes.html +Project-URL: Docs: RTD, https://docs.aiohttp.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Requires-Dist: aiosignal >=1.1.2 +Requires-Dist: attrs >=17.3.0 +Requires-Dist: frozenlist >=1.1.1 +Requires-Dist: multidict <7.0,>=4.5 +Requires-Dist: yarl <2.0,>=1.0 +Requires-Dist: async-timeout <5.0,>=4.0 ; python_version < "3.11" +Provides-Extra: speedups +Requires-Dist: brotlicffi ; (platform_python_implementation != "CPython") and extra == 'speedups' +Requires-Dist: Brotli ; (platform_python_implementation == "CPython") and extra == 'speedups' +Requires-Dist: aiodns ; (sys_platform == "linux" or sys_platform == "darwin") and extra == 'speedups' + +================================== +Async http client/server framework +================================== + +.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg + :height: 64px + :width: 64px + :alt: aiohttp logo + +| + +.. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI + :alt: GitHub Actions status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiohttp + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiohttp.svg + :target: https://pypi.org/project/aiohttp + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest + :target: https://docs.aiohttp.org/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + + +Key Features +============ + +- Supports both client and server side of HTTP protocol. +- Supports both client and server Web-Sockets out-of-the-box and avoids + Callback Hell. +- Provides Web-server with middleware and pluggable routing. + + +Getting started +=============== + +Client +------ + +To get something from the web: + +.. code-block:: python + + import aiohttp + import asyncio + + async def main(): + + async with aiohttp.ClientSession() as session: + async with session.get('http://python.org') as response: + + print("Status:", response.status) + print("Content-type:", response.headers['content-type']) + + html = await response.text() + print("Body:", html[:15], "...") + + asyncio.run(main()) + +This prints: + +.. code-block:: + + Status: 200 + Content-type: text/html; charset=utf-8 + Body: ... + +Coming from `requests `_ ? Read `why we need so many lines `_. + +Server +------ + +An example using a simple server: + +.. code-block:: python + + # examples/server_simple.py + from aiohttp import web + + async def handle(request): + name = request.match_info.get('name', "Anonymous") + text = "Hello, " + name + return web.Response(text=text) + + async def wshandle(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + + async for msg in ws: + if msg.type == web.WSMsgType.text: + await ws.send_str("Hello, {}".format(msg.data)) + elif msg.type == web.WSMsgType.binary: + await ws.send_bytes(msg.data) + elif msg.type == web.WSMsgType.close: + break + + return ws + + + app = web.Application() + app.add_routes([web.get('/', handle), + web.get('/echo', wshandle), + web.get('/{name}', handle)]) + + if __name__ == '__main__': + web.run_app(app) + + +Documentation +============= + +https://aiohttp.readthedocs.io/ + + +Demos +===== + +https://github.com/aio-libs/aiohttp-demos + + +External links +============== + +* `Third party libraries + `_ +* `Built with aiohttp + `_ +* `Powered by aiohttp + `_ + +Feel free to make a Pull Request for adding your link to these pages! + + +Communication channels +====================== + +*aio-libs Discussions*: https://github.com/aio-libs/aiohttp/discussions + +*gitter chat* https://gitter.im/aio-libs/Lobby + +We support `Stack Overflow +`_. +Please add *aiohttp* tag to your question there. + +Requirements +============ + +- async-timeout_ +- attrs_ +- multidict_ +- yarl_ +- frozenlist_ + +Optionally you may install the aiodns_ library (highly recommended for sake of speed). + +.. _aiodns: https://pypi.python.org/pypi/aiodns +.. _attrs: https://github.com/python-attrs/attrs +.. _multidict: https://pypi.python.org/pypi/multidict +.. _frozenlist: https://pypi.org/project/frozenlist/ +.. _yarl: https://pypi.python.org/pypi/yarl +.. _async-timeout: https://pypi.python.org/pypi/async_timeout + +License +======= + +``aiohttp`` is offered under the Apache 2 license. + + +Keepsafe +======== + +The aiohttp community would like to thank Keepsafe +(https://www.getkeepsafe.com) for its support in the early days of +the project. + + +Source code +=========== + +The latest developer version is available in a GitHub repository: +https://github.com/aio-libs/aiohttp + +Benchmarks +========== + +If you are interested in efficiency, the AsyncIO community maintains a +list of benchmarks on the official wiki: +https://github.com/python/asyncio/wiki/Benchmarks diff --git a/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77 b/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77 new file mode 100644 index 0000000..bb62b7f Binary files /dev/null and b/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77 differ diff --git a/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77.body b/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77.body new file mode 100644 index 0000000..3dd9013 --- /dev/null +++ b/.cache/pip/http-v2/e/a/0/4/e/ea04e1b7b200f2c002ee6991f0a4b2a2c0e11914ef72434384437c77.body @@ -0,0 +1,1590 @@ +Metadata-Version: 2.1 +Name: tqdm +Version: 4.66.2 +Summary: Fast, Extensible Progress Meter +Maintainer-email: tqdm developers +License: MPL-2.0 AND MIT +Project-URL: homepage, https://tqdm.github.io +Project-URL: repository, https://github.com/tqdm/tqdm +Project-URL: changelog, https://tqdm.github.io/releases +Project-URL: wiki, https://github.com/tqdm/tqdm/wiki +Keywords: progressbar,progressmeter,progress,bar,meter,rate,eta,console,terminal,time +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Environment :: Other Environment +Classifier: Environment :: Win32 (MS Windows) +Classifier: Environment :: X11 Applications +Classifier: Framework :: IPython +Classifier: Framework :: Jupyter +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: Other Audience +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Operating System :: MacOS +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft +Classifier: Operating System :: Microsoft :: MS-DOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: SunOS/Solaris +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation +Classifier: Programming Language :: Python :: Implementation :: IronPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Unix Shell +Classifier: Topic :: Desktop Environment +Classifier: Topic :: Education :: Computer Aided Instruction (CAI) +Classifier: Topic :: Education :: Testing +Classifier: Topic :: Office/Business +Classifier: Topic :: Other/Nonlisted Topic +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Pre-processors +Classifier: Topic :: Software Development :: User Interfaces +Classifier: Topic :: System :: Installation/Setup +Classifier: Topic :: System :: Logging +Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Shells +Classifier: Topic :: Terminals +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENCE +Requires-Dist: colorama ; platform_system == "Windows" +Provides-Extra: dev +Requires-Dist: pytest >=6 ; extra == 'dev' +Requires-Dist: pytest-cov ; extra == 'dev' +Requires-Dist: pytest-timeout ; extra == 'dev' +Requires-Dist: pytest-xdist ; extra == 'dev' +Provides-Extra: notebook +Requires-Dist: ipywidgets >=6 ; extra == 'notebook' +Provides-Extra: slack +Requires-Dist: slack-sdk ; extra == 'slack' +Provides-Extra: telegram +Requires-Dist: requests ; extra == 'telegram' + +|Logo| + +tqdm +==== + +|Py-Versions| |Versions| |Conda-Forge-Status| |Docker| |Snapcraft| + +|Build-Status| |Coverage-Status| |Branch-Coverage-Status| |Codacy-Grade| |Libraries-Rank| |PyPI-Downloads| + +|LICENCE| |OpenHub-Status| |binder-demo| |awesome-python| + +``tqdm`` derives from the Arabic word *taqaddum* (تقدّم) which can mean "progress," +and is an abbreviation for "I love you so much" in Spanish (*te quiero demasiado*). + +Instantly make your loops show a smart progress meter - just wrap any +iterable with ``tqdm(iterable)``, and you're done! + +.. code:: python + + from tqdm import tqdm + for i in tqdm(range(10000)): + ... + +``76%|████████████████████████        | 7568/10000 [00:33<00:10, 229.00it/s]`` + +``trange(N)`` can be also used as a convenient shortcut for +``tqdm(range(N))``. + +|Screenshot| + |Video| |Slides| |Merch| + +It can also be executed as a module with pipes: + +.. code:: sh + + $ seq 9999999 | tqdm --bytes | wc -l + 75.2MB [00:00, 217MB/s] + 9999999 + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 32%|██████████▍ | 8.89G/27.9G [00:42<01:31, 223MB/s] + +Overhead is low -- about 60ns per iteration (80ns with ``tqdm.gui``), and is +unit tested against performance regression. +By comparison, the well-established +`ProgressBar `__ has +an 800ns/iter overhead. + +In addition to its low overhead, ``tqdm`` uses smart algorithms to predict +the remaining time and to skip unnecessary iteration displays, which allows +for a negligible overhead in most cases. + +``tqdm`` works on any platform +(Linux, Windows, Mac, FreeBSD, NetBSD, Solaris/SunOS), +in any console or in a GUI, and is also friendly with IPython/Jupyter notebooks. + +``tqdm`` does not require any dependencies (not even ``curses``!), just +Python and an environment supporting ``carriage return \r`` and +``line feed \n`` control characters. + +------------------------------------------ + +.. contents:: Table of contents + :backlinks: top + :local: + + +Installation +------------ + +Latest PyPI stable release +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|Versions| |PyPI-Downloads| |Libraries-Dependents| + +.. code:: sh + + pip install tqdm + +Latest development release on GitHub +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|GitHub-Status| |GitHub-Stars| |GitHub-Commits| |GitHub-Forks| |GitHub-Updated| + +Pull and install pre-release ``devel`` branch: + +.. code:: sh + + pip install "git+https://github.com/tqdm/tqdm.git@devel#egg=tqdm" + +Latest Conda release +~~~~~~~~~~~~~~~~~~~~ + +|Conda-Forge-Status| + +.. code:: sh + + conda install -c conda-forge tqdm + +Latest Snapcraft release +~~~~~~~~~~~~~~~~~~~~~~~~ + +|Snapcraft| + +There are 3 channels to choose from: + +.. code:: sh + + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch + +Note that ``snap`` binaries are purely for CLI use (not ``import``-able), and +automatically set up ``bash`` tab-completion. + +Latest Docker release +~~~~~~~~~~~~~~~~~~~~~ + +|Docker| + +.. code:: sh + + docker pull tqdm/tqdm + docker run -i --rm tqdm/tqdm --help + +Other +~~~~~ + +There are other (unofficial) places where ``tqdm`` may be downloaded, particularly for CLI use: + +|Repology| + +.. |Repology| image:: https://repology.org/badge/tiny-repos/python:tqdm.svg + :target: https://repology.org/project/python:tqdm/versions + +Changelog +--------- + +The list of all changes is available either on GitHub's Releases: +|GitHub-Status|, on the +`wiki `__, or on the +`website `__. + + +Usage +----- + +``tqdm`` is very versatile and can be used in a number of ways. +The three main ones are given below. + +Iterable-based +~~~~~~~~~~~~~~ + +Wrap ``tqdm()`` around any iterable: + +.. code:: python + + from tqdm import tqdm + from time import sleep + + text = "" + for char in tqdm(["a", "b", "c", "d"]): + sleep(0.25) + text = text + char + +``trange(i)`` is a special optimised instance of ``tqdm(range(i))``: + +.. code:: python + + from tqdm import trange + + for i in trange(100): + sleep(0.01) + +Instantiation outside of the loop allows for manual control over ``tqdm()``: + +.. code:: python + + pbar = tqdm(["a", "b", "c", "d"]) + for char in pbar: + sleep(0.25) + pbar.set_description("Processing %s" % char) + +Manual +~~~~~~ + +Manual control of ``tqdm()`` updates using a ``with`` statement: + +.. code:: python + + with tqdm(total=100) as pbar: + for i in range(10): + sleep(0.1) + pbar.update(10) + +If the optional variable ``total`` (or an iterable with ``len()``) is +provided, predictive stats are displayed. + +``with`` is also optional (you can just assign ``tqdm()`` to a variable, +but in this case don't forget to ``del`` or ``close()`` at the end: + +.. code:: python + + pbar = tqdm(total=100) + for i in range(10): + sleep(0.1) + pbar.update(10) + pbar.close() + +Module +~~~~~~ + +Perhaps the most wonderful use of ``tqdm`` is in a script or on the command +line. Simply inserting ``tqdm`` (or ``python -m tqdm``) between pipes will pass +through all ``stdin`` to ``stdout`` while printing progress to ``stderr``. + +The example below demonstrate counting the number of lines in all Python files +in the current directory, with timing information included. + +.. code:: sh + + $ time find . -name '*.py' -type f -exec cat \{} \; | wc -l + 857365 + + real 0m3.458s + user 0m0.274s + sys 0m3.325s + + $ time find . -name '*.py' -type f -exec cat \{} \; | tqdm | wc -l + 857366it [00:03, 246471.31it/s] + 857365 + + real 0m3.585s + user 0m0.862s + sys 0m3.358s + +Note that the usual arguments for ``tqdm`` can also be specified. + +.. code:: sh + + $ find . -name '*.py' -type f -exec cat \{} \; | + tqdm --unit loc --unit_scale --total 857366 >> /dev/null + 100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s] + +Backing up a large directory? + +.. code:: sh + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 44%|██████████████▊ | 153M/352M [00:14<00:18, 11.0MB/s] + +This can be beautified further: + +.. code:: sh + + $ BYTES=$(du -sb docs/ | cut -f1) + $ tar -cf - docs/ \ + | tqdm --bytes --total "$BYTES" --desc Processing | gzip \ + | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \ + > ~/backup.tgz + Processing: 100%|██████████████████████| 352M/352M [00:14<00:00, 30.2MB/s] + Compressed: 42%|█████████▎ | 148M/352M [00:14<00:19, 10.9MB/s] + +Or done on a file level using 7-zip: + +.. code:: sh + + $ 7z a -bd -r backup.7z docs/ | grep Compressing \ + | tqdm --total $(find docs/ -type f | wc -l) --unit files \ + | grep -v Compressing + 100%|██████████████████████████▉| 15327/15327 [01:00<00:00, 712.96files/s] + +Pre-existing CLI programs already outputting basic progress information will +benefit from ``tqdm``'s ``--update`` and ``--update_to`` flags: + +.. code:: sh + + $ seq 3 0.1 5 | tqdm --total 5 --update_to --null + 100%|████████████████████████████████████| 5.0/5 [00:00<00:00, 9673.21it/s] + $ seq 10 | tqdm --update --null # 1 + 2 + ... + 10 = 55 iterations + 55it [00:00, 90006.52it/s] + +FAQ and Known Issues +-------------------- + +|GitHub-Issues| + +The most common issues relate to excessive output on multiple lines, instead +of a neat one-line progress bar. + +- Consoles in general: require support for carriage return (``CR``, ``\r``). + + * Some cloud logging consoles which don't support ``\r`` properly + (`cloudwatch `__, + `K8s `__) may benefit from + ``export TQDM_POSITION=-1``. + +- Nested progress bars: + + * Consoles in general: require support for moving cursors up to the + previous line. For example, + `IDLE `__, + `ConEmu `__ and + `PyCharm `__ (also + `here `__, + `here `__, and + `here `__) + lack full support. + * Windows: additionally may require the Python module ``colorama`` + to ensure nested bars stay within their respective lines. + +- Unicode: + + * Environments which report that they support unicode will have solid smooth + progressbars. The fallback is an ``ascii``-only bar. + * Windows consoles often only partially support unicode and thus + `often require explicit ascii=True `__ + (also `here `__). This is due to + either normal-width unicode characters being incorrectly displayed as + "wide", or some unicode characters not rendering. + +- Wrapping generators: + + * Generator wrapper functions tend to hide the length of iterables. + ``tqdm`` does not. + * Replace ``tqdm(enumerate(...))`` with ``enumerate(tqdm(...))`` or + ``tqdm(enumerate(x), total=len(x), ...)``. + The same applies to ``numpy.ndenumerate``. + * Replace ``tqdm(zip(a, b))`` with ``zip(tqdm(a), b)`` or even + ``zip(tqdm(a), tqdm(b))``. + * The same applies to ``itertools``. + * Some useful convenience functions can be found under ``tqdm.contrib``. + +- `No intermediate output in docker-compose `__: + use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. + +- Overriding defaults via environment variables: + e.g. in CI/cloud jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam. + This override logic is handled by the ``tqdm.utils.envwrap`` decorator + (useful independent of ``tqdm``). + +If you come across any other difficulties, browse and file |GitHub-Issues|. + +Documentation +------------- + +|Py-Versions| |README-Hits| (Since 19 May 2016) + +.. code:: python + + class tqdm(): + """ + Decorate an iterable object, returning an iterator which acts exactly + like the original iterable, but prints a dynamically updating + progressbar every time a value is requested. + """ + + @envwrap("TQDM_") # override defaults via env vars + def __init__(self, iterable=None, desc=None, total=None, leave=True, + file=None, ncols=None, mininterval=0.1, + maxinterval=10.0, miniters=None, ascii=None, disable=False, + unit='it', unit_scale=False, dynamic_ncols=False, + smoothing=0.3, bar_format=None, initial=0, position=None, + postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0): + +Parameters +~~~~~~~~~~ + +* iterable : iterable, optional + Iterable to decorate with a progressbar. + Leave blank to manually manage the updates. +* desc : str, optional + Prefix for the progressbar. +* total : int or float, optional + The number of expected iterations. If unspecified, + len(iterable) is used if possible. If float("inf") or as a last + resort, only basic progress statistics are displayed + (no ETA, no progressbar). + If ``gui`` is True and this parameter needs subsequent updating, + specify an initial arbitrary large positive number, + e.g. 9e9. +* leave : bool, optional + If [default: True], keeps all traces of the progressbar + upon termination of iteration. + If ``None``, will leave only if ``position`` is ``0``. +* file : ``io.TextIOWrapper`` or ``io.StringIO``, optional + Specifies where to output the progress messages + (default: sys.stderr). Uses ``file.write(str)`` and ``file.flush()`` + methods. For encoding, see ``write_bytes``. +* ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes the progressbar to stay within this bound. + If unspecified, attempts to use environment width. The + fallback is a meter width of 10 and no limit for the counter and + statistics. If 0, will not print any meter (only stats). +* mininterval : float, optional + Minimum progress display update interval [default: 0.1] seconds. +* maxinterval : float, optional + Maximum progress display update interval [default: 10] seconds. + Automatically adjusts ``miniters`` to correspond to ``mininterval`` + after long display update lag. Only works if ``dynamic_miniters`` + or monitor thread is enabled. +* miniters : int or float, optional + Minimum progress display update interval, in iterations. + If 0 and ``dynamic_miniters``, will automatically adjust to equal + ``mininterval`` (more CPU efficient, good for tight loops). + If > 0, will skip display of specified number of iterations. + Tweak this and ``mininterval`` to get very efficient loops. + If your progress is erratic with both fast and slow iterations + (network, skipping items, etc) you should set miniters=1. +* ascii : bool or str, optional + If unspecified or False, use unicode (smooth blocks) to fill + the meter. The fallback is to use ASCII characters " 123456789#". +* disable : bool, optional + Whether to disable the entire progressbar wrapper + [default: False]. If set to None, disable on non-TTY. +* unit : str, optional + String that will be used to define the unit of each iteration + [default: it]. +* unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled + automatically and a metric prefix following the + International System of Units standard will be added + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale ``total`` and ``n``. +* dynamic_ncols : bool, optional + If set, constantly alters ``ncols`` and ``nrows`` to the + environment (allowing for window resizes) [default: False]. +* smoothing : float, optional + Exponential moving average smoothing factor for speed estimates + (ignored in GUI mode). Ranges from 0 (average speed) to 1 + (current/instantaneous speed) [default: 0.3]. +* bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. +* initial : int or float, optional + The initial counter value. Useful when restarting a progress + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. +* position : int, optional + Specify the line offset to print this bar (starting from 0) + Automatic if unspecified. + Useful to manage multiple bars at once (eg, from threads). +* postfix : dict or ``*``, optional + Specify additional stats to display at the end of the bar. + Calls ``set_postfix(**postfix)`` if possible (dict). +* unit_divisor : float, optional + [default: 1000], ignored unless ``unit_scale`` is True. +* write_bytes : bool, optional + Whether to write bytes. If (default: False) will write unicode. +* lock_args : tuple, optional + Passed to ``refresh`` for intermediate output + (initialisation, iterating, and updating). +* nrows : int, optional + The screen height. If specified, hides nested bars outside this + bound. If unspecified, attempts to use environment height. + The fallback is 20. +* colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). +* delay : float, optional + Don't display until [default: 0] seconds have elapsed. + +Extra CLI Options +~~~~~~~~~~~~~~~~~ + +* delim : chr, optional + Delimiting character [default: '\n']. Use '\0' for null. + N.B.: on Windows systems, Python converts '\n' to '\r\n'. +* buf_size : int, optional + String buffer size in bytes [default: 256] + used when ``delim`` is specified. +* bytes : bool, optional + If true, will count bytes, ignore ``delim``, and default + ``unit_scale`` to True, ``unit_divisor`` to 1024, and ``unit`` to 'B'. +* tee : bool, optional + If true, passes ``stdin`` to both ``stderr`` and ``stdout``. +* update : bool, optional + If true, will treat input as newly elapsed iterations, + i.e. numbers to pass to ``update()``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* update_to : bool, optional + If true, will treat input as total elapsed iterations, + i.e. numbers to assign to ``self.n``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* null : bool, optional + If true, will discard input (no stdout). +* manpath : str, optional + Directory in which to install tqdm man pages. +* comppath : str, optional + Directory in which to place tqdm completion. +* log : str, optional + CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET. + +Returns +~~~~~~~ + +* out : decorated iterator. + +.. code:: python + + class tqdm(): + def update(self, n=1): + """ + Manually update the progress bar, useful for streams + such as reading files. + E.g.: + >>> t = tqdm(total=filesize) # Initialise + >>> for current_buffer in stream: + ... ... + ... t.update(len(current_buffer)) + >>> t.close() + The last line is highly recommended, but possibly not necessary if + ``t.update()`` will be called in such a way that ``filesize`` will be + exactly reached and printed. + + Parameters + ---------- + n : int or float, optional + Increment to add to the internal counter of iterations + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. + + Returns + ------- + out : bool or None + True if a ``display()`` was triggered. + """ + + def close(self): + """Cleanup and (if leave=False) close the progressbar.""" + + def clear(self, nomove=False): + """Clear current bar display.""" + + def refresh(self): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + If ``True``, does not lock. + If [default: ``False``]: calls ``acquire()`` on internal lock. + lock_args : tuple, optional + Passed to internal lock's ``acquire()``. + If specified, will only ``display()`` if ``acquire()`` returns ``True``. + """ + + def unpause(self): + """Restart tqdm timer from last print time.""" + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Consider combining with ``leave=True``. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + + def set_description(self, desc=None, refresh=True): + """ + Set/modify description of the progress bar. + + Parameters + ---------- + desc : str, optional + refresh : bool, optional + Forces refresh [default: True]. + """ + + def set_postfix(self, ordered_dict=None, refresh=True, **tqdm_kwargs): + """ + Set/modify postfix (additional stats) + with automatic formatting based on datatype. + + Parameters + ---------- + ordered_dict : dict or OrderedDict, optional + refresh : bool, optional + Forces refresh [default: True]. + kwargs : dict, optional + """ + + @classmethod + def write(cls, s, file=sys.stdout, end="\n"): + """Print a message via tqdm (without overlap with bars).""" + + @property + def format_dict(self): + """Public API for read-only member access.""" + + def display(self, msg=None, pos=None): + """ + Use ``self.sp`` to display ``msg`` in the specified ``pos``. + + Consider overloading this function when inheriting to use e.g.: + ``self.some_frontend(**self.format_dict)`` instead of ``self.sp``. + + Parameters + ---------- + msg : str, optional. What to display (default: ``repr(self)``). + pos : int, optional. Position to ``moveto`` + (default: ``abs(self.pos)``). + """ + + @classmethod + @contextmanager + def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs): + """ + stream : file-like object. + method : str, "read" or "write". The result of ``read()`` and + the first argument of ``write()`` should have a ``len()``. + + >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj: + ... while True: + ... chunk = fobj.read(chunk_size) + ... if not chunk: + ... break + """ + + @classmethod + def pandas(cls, *targs, **tqdm_kwargs): + """Registers the current `tqdm` class with `pandas`.""" + + def trange(*args, **tqdm_kwargs): + """Shortcut for `tqdm(range(*args), **tqdm_kwargs)`.""" + +Convenience Functions +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + def tqdm.contrib.tenumerate(iterable, start=0, total=None, + tqdm_class=tqdm.auto.tqdm, **tqdm_kwargs): + """Equivalent of `numpy.ndenumerate` or builtin `enumerate`.""" + + def tqdm.contrib.tzip(iter1, *iter2plus, **tqdm_kwargs): + """Equivalent of builtin `zip`.""" + + def tqdm.contrib.tmap(function, *sequences, **tqdm_kwargs): + """Equivalent of builtin `map`.""" + +Submodules +~~~~~~~~~~ + +.. code:: python + + class tqdm.notebook.tqdm(tqdm.tqdm): + """IPython/Jupyter Notebook widget.""" + + class tqdm.auto.tqdm(tqdm.tqdm): + """Automatically chooses beween `tqdm.notebook` and `tqdm.tqdm`.""" + + class tqdm.asyncio.tqdm(tqdm.tqdm): + """Asynchronous version.""" + @classmethod + def as_completed(cls, fs, *, loop=None, timeout=None, total=None, + **tqdm_kwargs): + """Wrapper for `asyncio.as_completed`.""" + + class tqdm.gui.tqdm(tqdm.tqdm): + """Matplotlib GUI version.""" + + class tqdm.tk.tqdm(tqdm.tqdm): + """Tkinter GUI version.""" + + class tqdm.rich.tqdm(tqdm.tqdm): + """`rich.progress` version.""" + + class tqdm.keras.TqdmCallback(keras.callbacks.Callback): + """Keras callback for epoch and batch progress.""" + + class tqdm.dask.TqdmCallback(dask.callbacks.Callback): + """Dask callback for task progress.""" + + +``contrib`` ++++++++++++ + +The ``tqdm.contrib`` package also contains experimental modules: + +- ``tqdm.contrib.itertools``: Thin wrappers around ``itertools`` +- ``tqdm.contrib.concurrent``: Thin wrappers around ``concurrent.futures`` +- ``tqdm.contrib.slack``: Posts to `Slack `__ bots +- ``tqdm.contrib.discord``: Posts to `Discord `__ bots +- ``tqdm.contrib.telegram``: Posts to `Telegram `__ bots +- ``tqdm.contrib.bells``: Automagically enables all optional features + + * ``auto``, ``pandas``, ``slack``, ``discord``, ``telegram`` + +Examples and Advanced Usage +--------------------------- + +- See the `examples `__ + folder; +- import the module and run ``help()``; +- consult the `wiki `__; + + * this has an + `excellent article `__ + on how to make a **great** progressbar; + +- check out the `slides from PyData London `__, or +- run the |binder-demo|. + +Description and additional stats +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Custom information can be displayed and updated dynamically on ``tqdm`` bars +with the ``desc`` and ``postfix`` arguments: + +.. code:: python + + from tqdm import tqdm, trange + from random import random, randint + from time import sleep + + with trange(10) as t: + for i in t: + # Description will be displayed on the left + t.set_description('GEN %i' % i) + # Postfix will be displayed on the right, + # formatted automatically based on argument's datatype + t.set_postfix(loss=random(), gen=randint(1,999), str='h', + lst=[1, 2]) + sleep(0.1) + + with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}", + postfix=["Batch", {"value": 0}]) as t: + for i in range(10): + sleep(0.1) + t.postfix[1]["value"] = i / 2 + t.update() + +Points to remember when using ``{postfix[...]}`` in the ``bar_format`` string: + +- ``postfix`` also needs to be passed as an initial argument in a compatible + format, and +- ``postfix`` will be auto-converted to a string if it is a ``dict``-like + object. To prevent this behaviour, insert an extra item into the dictionary + where the key is not a string. + +Additional ``bar_format`` parameters may also be defined by overriding +``format_dict``, and the bar itself may be modified using ``ascii``: + +.. code:: python + + from tqdm import tqdm + class TqdmExtraFormat(tqdm): + """Provides a `total_time` format parameter""" + @property + def format_dict(self): + d = super(TqdmExtraFormat, self).format_dict + total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) + d.update(total_time=self.format_interval(total_time) + " in total") + return d + + for i in TqdmExtraFormat( + range(9), ascii=" .oO0", + bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"): + if i == 4: + break + +.. code:: + + 00:00 in total: 44%|0000. | 4/9 [00:00<00:00, 962.93it/s] + +Note that ``{bar}`` also supports a format specifier ``[width][type]``. + +- ``width`` + + * unspecified (default): automatic to fill ``ncols`` + * ``int >= 0``: fixed width overriding ``ncols`` logic + * ``int < 0``: subtract from the automatic default + +- ``type`` + + * ``a``: ascii (``ascii=True`` override) + * ``u``: unicode (``ascii=False`` override) + * ``b``: blank (``ascii=" "`` override) + +This means a fixed bar with right-justified text may be created by using: +``bar_format="{l_bar}{bar:10}|{bar:-10b}right-justified"`` + +Nested progress bars +~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` supports nested progress bars. Here's an example: + +.. code:: python + + from tqdm.auto import trange + from time import sleep + + for i in trange(4, desc='1st loop'): + for j in trange(5, desc='2nd loop'): + for k in trange(50, desc='3rd loop', leave=False): + sleep(0.01) + +For manual control over positioning (e.g. for multi-processing use), +you may specify ``position=n`` where ``n=0`` for the outermost bar, +``n=1`` for the next, and so on. +However, it's best to check if ``tqdm`` can work without manual ``position`` +first. + +.. code:: python + + from time import sleep + from tqdm import trange, tqdm + from multiprocessing import Pool, RLock, freeze_support + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text, position=n): + sleep(interval) + + if __name__ == '__main__': + freeze_support() # for Windows support + tqdm.set_lock(RLock()) # for managing output contention + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p.map(progresser, L) + +Note that in Python 3, ``tqdm.write`` is thread-safe: + +.. code:: python + + from time import sleep + from tqdm import tqdm, trange + from concurrent.futures import ThreadPoolExecutor + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text): + sleep(interval) + if n == 6: + tqdm.write("n == 6 completed.") + tqdm.write("`tqdm.write()` is thread-safe in py3!") + + if __name__ == '__main__': + with ThreadPoolExecutor() as p: + p.map(progresser, L) + +Hooks and callbacks +~~~~~~~~~~~~~~~~~~~ + +``tqdm`` can easily support callbacks/hooks and manual updates. +Here's an example with ``urllib``: + +**``urllib.urlretrieve`` documentation** + + | [...] + | If present, the hook function will be called once + | on establishment of the network connection and once after each block read + | thereafter. The hook will be passed three arguments; a count of blocks + | transferred so far, a block size in bytes, and the total size of the file. + | [...] + +.. code:: python + + import urllib, os + from tqdm import tqdm + urllib = getattr(urllib, 'request', urllib) + + class TqdmUpTo(tqdm): + """Provides `update_to(n)` which uses `tqdm.update(delta_n)`.""" + def update_to(self, b=1, bsize=1, tsize=None): + """ + b : int, optional + Number of blocks transferred so far [default: 1]. + bsize : int, optional + Size of each block (in tqdm units) [default: 1]. + tsize : int, optional + Total size (in tqdm units). If [default: None] remains unchanged. + """ + if tsize is not None: + self.total = tsize + return self.update(b * bsize - self.n) # also sets self.n = b * bsize + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1, + desc=eg_link.split('/')[-1]) as t: # all optional kwargs + urllib.urlretrieve(eg_link, filename=os.devnull, + reporthook=t.update_to, data=None) + t.total = t.n + +Inspired by `twine#242 `__. +Functional alternative in +`examples/tqdm_wget.py `__. + +It is recommend to use ``miniters=1`` whenever there is potentially +large differences in iteration speed (e.g. downloading a file over +a patchy connection). + +**Wrapping read/write methods** + +To measure throughput through a file-like object's ``read`` or ``write`` +methods, use ``CallbackIOWrapper``: + +.. code:: python + + from tqdm.auto import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + +Alternatively, use the even simpler ``wrapattr`` convenience function, +which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples +down to: + +.. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = getattr(urllib, 'request', urllib).urlopen(eg_link) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=getattr(response, 'length', None)) as fout: + for chunk in response: + fout.write(chunk) + +The ``requests`` equivalent is nearly identical: + +.. code:: python + + import requests, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = requests.get(eg_link, stream=True) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=int(response.headers.get('content-length', 0))) as fout: + for chunk in response.iter_content(chunk_size=4096): + fout.write(chunk) + +**Custom callback** + +``tqdm`` is known for intelligently skipping unnecessary displays. To make a +custom callback take advantage of this, simply use the return value of +``update()``. This is set to ``True`` if a ``display()`` was triggered. + +.. code:: python + + from tqdm.auto import tqdm as std_tqdm + + def external_callback(*args, **kwargs): + ... + + class TqdmExt(std_tqdm): + def update(self, n=1): + displayed = super(TqdmExt, self).update(n) + if displayed: + external_callback(**self.format_dict) + return displayed + +``asyncio`` +~~~~~~~~~~~ + +Note that ``break`` isn't currently caught by asynchronous iterators. +This means that ``tqdm`` cannot clean up after itself in this case: + +.. code:: python + + from tqdm.asyncio import tqdm + + async for i in tqdm(range(9)): + if i == 2: + break + +Instead, either call ``pbar.close()`` manually or use the context manager syntax: + +.. code:: python + + from tqdm.asyncio import tqdm + + with tqdm(range(9)) as pbar: + async for i in pbar: + if i == 2: + break + +Pandas Integration +~~~~~~~~~~~~~~~~~~ + +Due to popular demand we've added support for ``pandas`` -- here's an example +for ``DataFrame.progress_apply`` and ``DataFrameGroupBy.progress_apply``: + +.. code:: python + + import pandas as pd + import numpy as np + from tqdm import tqdm + + df = pd.DataFrame(np.random.randint(0, 100, (100000, 6))) + + # Register `pandas.progress_apply` and `pandas.Series.map_apply` with `tqdm` + # (can use `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, optional kwargs, etc.) + tqdm.pandas(desc="my bar!") + + # Now you can use `progress_apply` instead of `apply` + # and `progress_map` instead of `map` + df.progress_apply(lambda x: x**2) + # can also groupby: + # df.groupby(0).progress_apply(lambda x: x**2) + +In case you're interested in how this works (and how to modify it for your +own callbacks), see the +`examples `__ +folder or import the module and run ``help()``. + +Keras Integration +~~~~~~~~~~~~~~~~~ + +A ``keras`` callback is also available: + +.. code:: python + + from tqdm.keras import TqdmCallback + + ... + + model.fit(..., verbose=0, callbacks=[TqdmCallback()]) + +Dask Integration +~~~~~~~~~~~~~~~~ + +A ``dask`` callback is also available: + +.. code:: python + + from tqdm.dask import TqdmCallback + + with TqdmCallback(desc="compute"): + ... + arr.compute() + + # or use callback globally + cb = TqdmCallback(desc="global") + cb.register() + arr.compute() + +IPython/Jupyter Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +IPython/Jupyter is supported via the ``tqdm.notebook`` submodule: + +.. code:: python + + from tqdm.notebook import trange, tqdm + from time import sleep + + for i in trange(3, desc='1st loop'): + for j in tqdm(range(100), desc='2nd loop'): + sleep(0.01) + +In addition to ``tqdm`` features, the submodule provides a native Jupyter +widget (compatible with IPython v1-v4 and Jupyter), fully working nested bars +and colour hints (blue: normal, green: completed, red: error/interrupt, +light blue: no ETA); as demonstrated below. + +|Screenshot-Jupyter1| +|Screenshot-Jupyter2| +|Screenshot-Jupyter3| + +The ``notebook`` version supports percentage or pixels for overall width +(e.g.: ``ncols='100%'`` or ``ncols='480px'``). + +It is also possible to let ``tqdm`` automatically choose between +console or notebook versions by using the ``autonotebook`` submodule: + +.. code:: python + + from tqdm.autonotebook import tqdm + tqdm.pandas() + +Note that this will issue a ``TqdmExperimentalWarning`` if run in a notebook +since it is not meant to be possible to distinguish between ``jupyter notebook`` +and ``jupyter console``. Use ``auto`` instead of ``autonotebook`` to suppress +this warning. + +Note that notebooks will display the bar in the cell where it was created. +This may be a different cell from the one where it is used. +If this is not desired, either + +- delay the creation of the bar to the cell where it must be displayed, or +- create the bar with ``display=False``, and in a later cell call + ``display(bar.container)``: + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm(..., display=False) + +.. code:: python + + # different cell + display(pbar.container) + +The ``keras`` callback has a ``display()`` method which can be used likewise: + +.. code:: python + + from tqdm.keras import TqdmCallback + cbk = TqdmCallback(display=False) + +.. code:: python + + # different cell + cbk.display() + model.fit(..., verbose=0, callbacks=[cbk]) + +Another possibility is to have a single bar (near the top of the notebook) +which is constantly re-used (using ``reset()`` rather than ``close()``). +For this reason, the notebook version (unlike the CLI version) does not +automatically call ``close()`` upon ``Exception``. + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm() + +.. code:: python + + # different cell + iterable = range(100) + pbar.reset(total=len(iterable)) # initialise with new `total` + for i in iterable: + pbar.update() + pbar.refresh() # force print final status but don't `close()` + +Custom Integration +~~~~~~~~~~~~~~~~~~ + +To change the default arguments (such as making ``dynamic_ncols=True``), +simply use built-in Python magic: + +.. code:: python + + from functools import partial + from tqdm import tqdm as std_tqdm + tqdm = partial(std_tqdm, dynamic_ncols=True) + +For further customisation, +``tqdm`` may be inherited from to create custom callbacks (as with the +``TqdmUpTo`` example `above <#hooks-and-callbacks>`__) or for custom frontends +(e.g. GUIs such as notebook or plotting packages). In the latter case: + +1. ``def __init__()`` to call ``super().__init__(..., gui=True)`` to disable + terminal ``status_printer`` creation. +2. Redefine: ``close()``, ``clear()``, ``display()``. + +Consider overloading ``display()`` to use e.g. +``self.frontend(**self.format_dict)`` instead of ``self.sp(repr(self))``. + +Some submodule examples of inheritance: + +- `tqdm/notebook.py `__ +- `tqdm/gui.py `__ +- `tqdm/tk.py `__ +- `tqdm/contrib/slack.py `__ +- `tqdm/contrib/discord.py `__ +- `tqdm/contrib/telegram.py `__ + +Dynamic Monitor/Meter +~~~~~~~~~~~~~~~~~~~~~ + +You can use a ``tqdm`` as a meter which is not monotonically increasing. +This could be because ``n`` decreases (e.g. a CPU usage monitor) or ``total`` +changes. + +One example would be recursively searching for files. The ``total`` is the +number of objects found so far, while ``n`` is the number of those objects which +are files (rather than folders): + +.. code:: python + + from tqdm import tqdm + import os.path + + def find_files_recursively(path, show_progress=True): + files = [] + # total=1 assumes `path` is a file + t = tqdm(total=1, unit="file", disable=not show_progress) + if not os.path.exists(path): + raise IOError("Cannot find:" + path) + + def append_found_file(f): + files.append(f) + t.update() + + def list_found_dir(path): + """returns os.listdir(path) assuming os.path.isdir(path)""" + listing = os.listdir(path) + # subtract 1 since a "file" we found was actually this directory + t.total += len(listing) - 1 + # fancy way to give info without forcing a refresh + t.set_postfix(dir=path[-10:], refresh=False) + t.update(0) # may trigger a refresh + return listing + + def recursively_search(path): + if os.path.isdir(path): + for f in list_found_dir(path): + recursively_search(os.path.join(path, f)) + else: + append_found_file(path) + + recursively_search(path) + t.set_postfix(dir=path) + t.close() + return files + +Using ``update(0)`` is a handy way to let ``tqdm`` decide when to trigger a +display refresh to avoid console spamming. + +Writing messages +~~~~~~~~~~~~~~~~ + +This is a work in progress (see +`#737 `__). + +Since ``tqdm`` uses a simple printing mechanism to display progress bars, +you should not write any message in the terminal using ``print()`` while +a progressbar is open. + +To write messages in the terminal without any collision with ``tqdm`` bar +display, a ``.write()`` method is provided: + +.. code:: python + + from tqdm.auto import tqdm, trange + from time import sleep + + bar = trange(10) + for i in bar: + # Print using tqdm class method .write() + sleep(0.1) + if not (i % 3): + tqdm.write("Done task %i" % i) + # Can also use bar.write() + +By default, this will print to standard output ``sys.stdout``. but you can +specify any file-like object using the ``file`` argument. For example, this +can be used to redirect the messages writing to a log file or class. + +Redirecting writing +~~~~~~~~~~~~~~~~~~~ + +If using a library that can print messages to the console, editing the library +by replacing ``print()`` with ``tqdm.write()`` may not be desirable. +In that case, redirecting ``sys.stdout`` to ``tqdm.write()`` is an option. + +To redirect ``sys.stdout``, create a file-like class that will write +any input string to ``tqdm.write()``, and supply the arguments +``file=sys.stdout, dynamic_ncols=True``. + +A reusable canonical example is given below: + +.. code:: python + + from time import sleep + import contextlib + import sys + from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile + + + @contextlib.contextmanager + def std_out_err_redirect_tqdm(): + orig_out_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = map(DummyTqdmFile, orig_out_err) + yield orig_out_err[0] + # Relay exceptions + except Exception as exc: + raise exc + # Always restore sys.stdout/err if necessary + finally: + sys.stdout, sys.stderr = orig_out_err + + def some_fun(i): + print("Fee, fi, fo,".split()[i]) + + # Redirect stdout to tqdm.write() (don't forget the `as save_stdout`) + with std_out_err_redirect_tqdm() as orig_stdout: + # tqdm needs the original stdout + # and dynamic_ncols=True to autodetect console width + for i in tqdm(range(3), file=orig_stdout, dynamic_ncols=True): + sleep(.5) + some_fun(i) + + # After the `with`, printing is restored + print("Done!") + +Redirecting ``logging`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` +may also be redirected to ``tqdm.write()``. + +Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to +redirect ``logging`` first if needed. + +Helper methods are available in ``tqdm.contrib.logging``. For example: + +.. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + +Monitoring thread, intervals and miniters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` implements a few tricks to increase efficiency and reduce overhead. + +- Avoid unnecessary frequent bar refreshing: ``mininterval`` defines how long + to wait between each refresh. ``tqdm`` always gets updated in the background, + but it will display only every ``mininterval``. +- Reduce number of calls to check system clock/time. +- ``mininterval`` is more intuitive to configure than ``miniters``. + A clever adjustment system ``dynamic_miniters`` will automatically adjust + ``miniters`` to the amount of iterations that fit into time ``mininterval``. + Essentially, ``tqdm`` will check if it's time to print without actually + checking time. This behaviour can be still be bypassed by manually setting + ``miniters``. + +However, consider a case with a combination of fast and slow iterations. +After a few fast iterations, ``dynamic_miniters`` will set ``miniters`` to a +large number. When iteration rate subsequently slows, ``miniters`` will +remain large and thus reduce display update frequency. To address this: + +- ``maxinterval`` defines the maximum time between display refreshes. + A concurrent monitoring thread checks for overdue updates and forces one + where necessary. + +The monitoring thread should not have a noticeable overhead, and guarantees +updates at least every 10 seconds by default. +This value can be directly changed by setting the ``monitor_interval`` of +any ``tqdm`` instance (i.e. ``t = tqdm.tqdm(...); t.monitor_interval = 2``). +The monitor thread may be disabled application-wide by setting +``tqdm.tqdm.monitor_interval = 0`` before instantiation of any ``tqdm`` bar. + + +Merch +----- + +You can buy `tqdm branded merch `__ now! + +Contributions +------------- + +|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| |CII Best Practices| + +All source code is hosted on `GitHub `__. +Contributions are welcome. + +See the +`CONTRIBUTING `__ +file for more information. + +Developers who have made significant contributions, ranked by *SLoC* +(surviving lines of code, +`git fame `__ ``-wMC --excl '\.(png|gif|jpg)$'``), +are: + +==================== ======================================================== ==== ================================ +Name ID SLoC Notes +==================== ======================================================== ==== ================================ +Casper da Costa-Luis `casperdcl `__ ~80% primary maintainer |Gift-Casper| +Stephen Larroque `lrq3000 `__ ~9% team member +Martin Zugnoni `martinzugnoni `__ ~3% +Daniel Ecer `de-code `__ ~2% +Richard Sheridan `richardsheridan `__ ~1% +Guangshuo Chen `chengs `__ ~1% +Helio Machado `0x2b3bfa0 `__ ~1% +Kyle Altendorf `altendky `__ <1% +Noam Yorav-Raphael `noamraph `__ <1% original author +Matthew Stevens `mjstevens777 `__ <1% +Hadrien Mary `hadim `__ <1% team member +Mikhail Korobov `kmike `__ <1% team member +==================== ======================================================== ==== ================================ + +Ports to Other Languages +~~~~~~~~~~~~~~~~~~~~~~~~ + +A list is available on +`this wiki page `__. + + +LICENCE +------- + +Open Source (OSI approved): |LICENCE| + +Citation information: |DOI| + +|README-Hits| (Since 19 May 2016) + +.. |Logo| image:: https://tqdm.github.io/img/logo.gif +.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif +.. |Video| image:: https://tqdm.github.io/img/video.jpg + :target: https://tqdm.github.io/video +.. |Slides| image:: https://tqdm.github.io/img/slides.jpg + :target: https://tqdm.github.io/PyData2019/slides.html +.. |Merch| image:: https://tqdm.github.io/img/merch.jpg + :target: https://tqdm.github.io/merch +.. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub + :target: https://github.com/tqdm/tqdm/actions/workflows/test.yml +.. |Coverage-Status| image:: https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls + :target: https://coveralls.io/github/tqdm/tqdm +.. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg + :target: https://codecov.io/gh/tqdm/tqdm +.. |Codacy-Grade| image:: https://app.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 + :target: https://www.codacy.com/gh/tqdm/tqdm/dashboard +.. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/3264/badge + :target: https://bestpractices.coreinfrastructure.org/projects/3264 +.. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/releases +.. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/network +.. |GitHub-Stars| image:: https://img.shields.io/github/stars/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/stargazers +.. |GitHub-Commits| image:: https://img.shields.io/github/commit-activity/y/tqdm/tqdm.svg?logo=git&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/commit-activity +.. |GitHub-Issues| image:: https://img.shields.io/github/issues-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/issues?q= +.. |GitHub-PRs| image:: https://img.shields.io/github/issues-pr-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/pulls +.. |GitHub-Contributions| image:: https://img.shields.io/github/contributors/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/contributors +.. |GitHub-Updated| image:: https://img.shields.io/github/last-commit/tqdm/tqdm/master.svg?logo=github&logoColor=white&label=pushed + :target: https://github.com/tqdm/tqdm/pulse +.. |Gift-Casper| image:: https://img.shields.io/badge/dynamic/json.svg?color=ff69b4&label=gifts%20received&prefix=%C2%A3&query=%24..sum&url=https%3A%2F%2Fcaspersci.uk.to%2Fgifts.json + :target: https://cdcl.ml/sponsor +.. |Versions| image:: https://img.shields.io/pypi/v/tqdm.svg + :target: https://tqdm.github.io/releases +.. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/tqdm.svg?label=pypi%20downloads&logo=PyPI&logoColor=white + :target: https://pepy.tech/project/tqdm +.. |Py-Versions| image:: https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white + :target: https://pypi.org/project/tqdm +.. |Conda-Forge-Status| image:: https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge + :target: https://anaconda.org/conda-forge/tqdm +.. |Snapcraft| image:: https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft + :target: https://snapcraft.io/tqdm +.. |Docker| image:: https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white + :target: https://hub.docker.com/r/tqdm/tqdm +.. |Libraries-Rank| image:: https://img.shields.io/librariesio/sourcerank/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://libraries.io/pypi/tqdm +.. |Libraries-Dependents| image:: https://img.shields.io/librariesio/dependent-repos/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://github.com/tqdm/tqdm/network/dependents +.. |OpenHub-Status| image:: https://www.openhub.net/p/tqdm/widgets/project_thin_badge?format=gif + :target: https://www.openhub.net/p/tqdm?ref=Thin+badge +.. |awesome-python| image:: https://awesome.re/mentioned-badge.svg + :target: https://github.com/vinta/awesome-python +.. |LICENCE| image:: https://img.shields.io/pypi/l/tqdm.svg + :target: https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE +.. |DOI| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg + :target: https://doi.org/10.5281/zenodo.595120 +.. |binder-demo| image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb +.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif +.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif +.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif +.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif + :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social diff --git a/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 b/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 new file mode 100644 index 0000000..0d61ad7 Binary files /dev/null and b/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 differ diff --git a/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4.body b/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4.body new file mode 100644 index 0000000..ae235e3 Binary files /dev/null and b/.cache/pip/http-v2/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4.body differ diff --git a/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe b/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe new file mode 100644 index 0000000..2c1acbd Binary files /dev/null and b/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe differ diff --git a/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe.body b/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe.body new file mode 100644 index 0000000..15b59a3 Binary files /dev/null and b/.cache/pip/http-v2/e/b/9/7/6/eb97658ffb63e3939cb3dc97efc785dbe1e790b6a3500bac0ec647fe.body differ diff --git a/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95 b/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95 new file mode 100644 index 0000000..858fc3e Binary files /dev/null and b/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95 differ diff --git a/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95.body b/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95.body new file mode 100644 index 0000000..822550e --- /dev/null +++ b/.cache/pip/http-v2/f/2/f/c/9/f2fc91e4292d5b4c33d11c5156fa81bd1929b3ffe39b333d345b2d95.body @@ -0,0 +1,683 @@ +Metadata-Version: 2.1 +Name: charset-normalizer +Version: 3.3.2 +Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. +Home-page: https://github.com/Ousret/charset_normalizer +Author: Ahmed TAHRI +Author-email: ahmed.tahri@cloudnursery.dev +License: MIT +Project-URL: Bug Reports, https://github.com/Ousret/charset_normalizer/issues +Project-URL: Documentation, https://charset-normalizer.readthedocs.io/en/latest +Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Linguistic +Classifier: Topic :: Utilities +Classifier: Typing :: Typed +Requires-Python: >=3.7.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: unicode_backport + +

Charset Detection, for Everyone 👋

+ +

+ The Real First Universal Charset Detector
+ + + + + Download Count Total + + + + +

+

+ Featured Packages
+ + Static Badge + + + Static Badge + +

+

+ In other language (unofficial port - by the community)
+ + Static Badge + +

+ +> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, +> I'm trying to resolve the issue by taking a new approach. +> All IANA character set names for which the Python core library provides codecs are supported. + +

+ >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< +

+ +This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. + +| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | +|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| +| `Fast` | ❌ | ✅ | ✅ | +| `Universal**` | ❌ | ✅ | ❌ | +| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | +| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | +| `License` | LGPL-2.1
_restrictive_ | MIT | MPL-1.1
_restrictive_ | +| `Native Python` | ✅ | ✅ | ❌ | +| `Detect spoken language` | ❌ | ✅ | N/A | +| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | +| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | +| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | + +

+Reading Normalized TextCat Reading Text +

+ +*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
+Did you got there because of the logs? See [https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html](https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html) + +## ⚡ Performance + +This package offer better performance than its counterpart Chardet. Here are some numbers. + +| Package | Accuracy | Mean per file (ms) | File per sec (est) | +|-----------------------------------------------|:--------:|:------------------:|:------------------:| +| [chardet](https://github.com/chardet/chardet) | 86 % | 200 ms | 5 file/sec | +| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | + +| Package | 99th percentile | 95th percentile | 50th percentile | +|-----------------------------------------------|:---------------:|:---------------:|:---------------:| +| [chardet](https://github.com/chardet/chardet) | 1200 ms | 287 ms | 23 ms | +| charset-normalizer | 100 ms | 50 ms | 5 ms | + +Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. + +> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. +> And yes, these results might change at any time. The dataset can be updated to include more files. +> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. +> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability +> (eg. Supported Encoding) Challenge-them if you want. + +## ✨ Installation + +Using pip: + +```sh +pip install charset-normalizer -U +``` + +## 🚀 Basic Usage + +### CLI +This package comes with a CLI. + +``` +usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] + file [file ...] + +The Real First Universal Charset Detector. Discover originating encoding used +on text file. Normalize text to unicode. + +positional arguments: + files File(s) to be analysed + +optional arguments: + -h, --help show this help message and exit + -v, --verbose Display complementary information about file if any. + Stdout will contain logs about the detection process. + -a, --with-alternative + Output complementary possibilities if any. Top-level + JSON WILL be a list. + -n, --normalize Permit to normalize input file. If not set, program + does not write anything. + -m, --minimal Only output the charset detected to STDOUT. Disabling + JSON output. + -r, --replace Replace file when trying to normalize it instead of + creating a new one. + -f, --force Replace file without asking if you are sure, use this + flag with caution. + -t THRESHOLD, --threshold THRESHOLD + Define a custom maximum amount of chaos allowed in + decoded content. 0. <= chaos <= 1. + --version Show version information and exit. +``` + +```bash +normalizer ./data/sample.1.fr.srt +``` + +or + +```bash +python -m charset_normalizer ./data/sample.1.fr.srt +``` + +🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. + +```json +{ + "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", + "encoding": "cp1252", + "encoding_aliases": [ + "1252", + "windows_1252" + ], + "alternative_encodings": [ + "cp1254", + "cp1256", + "cp1258", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + "mbcs" + ], + "language": "French", + "alphabets": [ + "Basic Latin", + "Latin-1 Supplement" + ], + "has_sig_or_bom": false, + "chaos": 0.149, + "coherence": 97.152, + "unicode_path": null, + "is_preferred": true +} +``` + +### Python +*Just print out normalized text* +```python +from charset_normalizer import from_path + +results = from_path('./my_subtitle.srt') + +print(str(results.best())) +``` + +*Upgrade your code without effort* +```python +from charset_normalizer import detect +``` + +The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. + +See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) + +## 😇 Why + +When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a +reliable alternative using a completely different method. Also! I never back down on a good challenge! + +I **don't care** about the **originating charset** encoding, because **two different tables** can +produce **two identical rendered string.** +What I want is to get readable text, the best I can. + +In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 + +Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. + +## 🍰 How + + - Discard all charset encoding table that could not fit the binary content. + - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. + - Extract matches with the lowest mess detected. + - Additionally, we measure coherence / probe for a language. + +**Wait a minute**, what is noise/mess and coherence according to **YOU ?** + +*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then +**I established** some ground rules about **what is obvious** when **it seems like** a mess. + I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to + improve or rewrite it. + +*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought +that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. + +## ⚡ Known limitations + + - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) + - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. + +## ⚠️ About Python EOLs + +**If you are running:** + +- Python >=2.7,<3.5: Unsupported +- Python 3.5: charset-normalizer < 2.1 +- Python 3.6: charset-normalizer < 3.1 +- Python 3.7: charset-normalizer < 4.0 + +Upgrade your Python interpreter as soon as possible. + +## 👤 Contributing + +Contributions, issues and feature requests are very much welcome.
+Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. + +## 📝 License + +Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
+This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. + +Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) + +## 💼 For Enterprise + +Professional support for charset-normalizer is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme + +# Changelog +All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) + +### Fixed +- Unintentional memory usage regression when using large payload that match several encoding (#376) +- Regression on some detection case showcased in the documentation (#371) + +### Added +- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) + +## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) + +### Changed +- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 +- Improved the general detection reliability based on reports from the community + +## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) + +### Added +- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` +- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) + +### Removed +- (internal) Redundant utils.is_ascii function and unused function is_private_use_only +- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant + +### Changed +- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection +- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 + +### Fixed +- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) + +## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) + +### Changed +- Typehint for function `from_path` no longer enforce `PathLike` as its first argument +- Minor improvement over the global detection reliability + +### Added +- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries +- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) +- Explicit support for Python 3.12 + +### Fixed +- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) + +## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) + +### Added +- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) + +### Removed +- Support for Python 3.6 (PR #260) + +### Changed +- Optional speedup provided by mypy/c 1.0.1 + +## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) + +### Fixed +- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) + +### Changed +- Speedup provided by mypy/c 0.990 on Python >= 3.7 + +## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it +- Sphinx warnings when generating the documentation + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' + +## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) + +### Added +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Removed +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) + +### Fixed +- Sphinx warnings when generating the documentation + +## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) + +### Changed +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Removed +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) + +### Deprecated +- Function `normalize` scheduled for removal in 3.0 + +### Changed +- Removed useless call to decode in fn is_unprintable (#206) + +### Fixed +- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) + +## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) + +### Added +- Output the Unicode table version when running the CLI with `--version` (PR #194) + +### Changed +- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) +- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) + +### Fixed +- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) +- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) + +### Removed +- Support for Python 3.5 (PR #192) + +### Deprecated +- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) + +## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) + +### Fixed +- ASCII miss-detection on rare cases (PR #170) + +## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) + +### Added +- Explicit support for Python 3.11 (PR #164) + +### Changed +- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) + +## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) + +### Fixed +- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) + +### Changed +- Skipping the language-detection (CD) on ASCII (PR #155) + +## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) + +### Changed +- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) + +### Fixed +- Wrong logging level applied when setting kwarg `explain` to True (PR #146) + +## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) +### Changed +- Improvement over Vietnamese detection (PR #126) +- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) +- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) +- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) +- Code style as refactored by Sourcery-AI (PR #131) +- Minor adjustment on the MD around european words (PR #133) +- Remove and replace SRTs from assets / tests (PR #139) +- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) + +### Fixed +- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) +- Avoid using too insignificant chunk (PR #137) + +### Added +- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) + +## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) +### Added +- Add support for Kazakh (Cyrillic) language detection (PR #109) + +### Changed +- Further, improve inferring the language from a given single-byte code page (PR #112) +- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) +- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) +- Various detection improvement (MD+CD) (PR #117) + +### Removed +- Remove redundant logging entry about detected language(s) (PR #115) + +### Fixed +- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) + +## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) +### Fixed +- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) +- Fix CLI crash when using --minimal output in certain cases (PR #103) + +### Changed +- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) + +## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) +### Changed +- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) +- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) +- The Unicode detection is slightly improved (PR #93) +- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) + +### Removed +- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) + +### Fixed +- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) +- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) +- The MANIFEST.in was not exhaustive (PR #78) + +## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) +### Fixed +- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) +- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) +- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) +- Submatch factoring could be wrong in rare edge cases (PR #72) +- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) +- Fix line endings from CRLF to LF for certain project files (PR #67) + +### Changed +- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) +- Allow fallback on specified encoding if any (PR #71) + +## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) +### Changed +- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) +- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) + +## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) +### Fixed +- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) + +### Changed +- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) + +## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) +### Fixed +- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) +- Using explain=False permanently disable the verbose output in the current runtime (PR #47) +- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) +- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) + +### Changed +- Public function normalize default args values were not aligned with from_bytes (PR #53) + +### Added +- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) + +## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) +### Changed +- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. +- Accent has been made on UTF-8 detection, should perform rather instantaneous. +- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. +- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) +- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ +- utf_7 detection has been reinstated. + +### Removed +- This package no longer require anything when used with Python 3.5 (Dropped cached_property) +- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. +- The exception hook on UnicodeDecodeError has been removed. + +### Deprecated +- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 + +### Fixed +- The CLI output used the relative path of the file(s). Should be absolute. + +## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) +### Fixed +- Logger configuration/usage no longer conflict with others (PR #44) + +## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) +### Removed +- Using standard logging instead of using the package loguru. +- Dropping nose test framework in favor of the maintained pytest. +- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. +- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. +- Stop support for UTF-7 that does not contain a SIG. +- Dropping PrettyTable, replaced with pure JSON output in CLI. + +### Fixed +- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. +- Not searching properly for the BOM when trying utf32/16 parent codec. + +### Changed +- Improving the package final size by compressing frequencies.json. +- Huge improvement over the larges payload. + +### Added +- CLI now produces JSON consumable output. +- Return ASCII if given sequences fit. Given reasonable confidence. + +## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) + +### Fixed +- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) + +## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) + +### Fixed +- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) + +## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) + +### Fixed +- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) + +## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) + +### Changed +- Amend the previous release to allow prettytable 2.0 (PR #35) + +## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) + +### Fixed +- Fix error while using the package with a python pre-release interpreter (PR #33) + +### Changed +- Dependencies refactoring, constraints revised. + +### Added +- Add python 3.9 and 3.10 to the supported interpreters + +MIT License + +Copyright (c) 2019 TAHRI Ahmed R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b b/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b new file mode 100644 index 0000000..a5bd23d Binary files /dev/null and b/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b differ diff --git a/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b.body b/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b.body new file mode 100644 index 0000000..d8dd6d1 --- /dev/null +++ b/.cache/pip/http-v2/f/4/3/c/e/f43ce2cb74a8c7ad77da70314e043a539a7f01416550c3167312132b.body @@ -0,0 +1,131 @@ +Metadata-Version: 2.1 +Name: async-timeout +Version: 4.0.3 +Summary: Timeout context manager for asyncio programs +Home-page: https://github.com/aio-libs/async-timeout +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +License: Apache 2 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/async-timeout/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/async-timeout +Project-URL: GitHub: issues, https://github.com/aio-libs/async-timeout/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/async-timeout +Classifier: Development Status :: 5 - Production/Stable +Classifier: Topic :: Software Development :: Libraries +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: typing-extensions >=3.6.5 ; python_version < "3.8" + +async-timeout +============= +.. image:: https://travis-ci.com/aio-libs/async-timeout.svg?branch=master + :target: https://travis-ci.com/aio-libs/async-timeout +.. image:: https://codecov.io/gh/aio-libs/async-timeout/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/async-timeout +.. image:: https://img.shields.io/pypi/v/async-timeout.svg + :target: https://pypi.python.org/pypi/async-timeout +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +asyncio-compatible timeout context manager. + + +Usage example +------------- + + +The context manager is useful in cases when you want to apply timeout +logic around block of code or in cases when ``asyncio.wait_for()`` is +not suitable. Also it's much faster than ``asyncio.wait_for()`` +because ``timeout`` doesn't create a new task. + +The ``timeout(delay, *, loop=None)`` call returns a context manager +that cancels a block on *timeout* expiring:: + + from async_timeout import timeout + async with timeout(1.5): + await inner() + +1. If ``inner()`` is executed faster than in ``1.5`` seconds nothing + happens. +2. Otherwise ``inner()`` is cancelled internally by sending + ``asyncio.CancelledError`` into but ``asyncio.TimeoutError`` is + raised outside of context manager scope. + +*timeout* parameter could be ``None`` for skipping timeout functionality. + + +Alternatively, ``timeout_at(when)`` can be used for scheduling +at the absolute time:: + + loop = asyncio.get_event_loop() + now = loop.time() + + async with timeout_at(now + 1.5): + await inner() + + +Please note: it is not POSIX time but a time with +undefined starting base, e.g. the time of the system power on. + + +Context manager has ``.expired`` property for check if timeout happens +exactly in context manager:: + + async with timeout(1.5) as cm: + await inner() + print(cm.expired) + +The property is ``True`` if ``inner()`` execution is cancelled by +timeout context manager. + +If ``inner()`` call explicitly raises ``TimeoutError`` ``cm.expired`` +is ``False``. + +The scheduled deadline time is available as ``.deadline`` property:: + + async with timeout(1.5) as cm: + cm.deadline + +Not finished yet timeout can be rescheduled by ``shift_by()`` +or ``shift_to()`` methods:: + + async with timeout(1.5) as cm: + cm.shift(1) # add another second on waiting + cm.update(loop.time() + 5) # reschedule to now+5 seconds + +Rescheduling is forbidden if the timeout is expired or after exit from ``async with`` +code block. + + +Installation +------------ + +:: + + $ pip install async-timeout + +The library is Python 3 only! + + + +Authors and License +------------------- + +The module is written by Andrew Svetlov. + +It's *Apache 2* licensed and freely available. diff --git a/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe b/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe new file mode 100644 index 0000000..66783df Binary files /dev/null and b/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe differ diff --git a/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe.body b/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe.body new file mode 100644 index 0000000..b9daf19 --- /dev/null +++ b/.cache/pip/http-v2/f/a/e/c/c/faecc59d80e764879d3eafca5d7666de46ff3a7f0cea7a0527a07cbe.body @@ -0,0 +1,1077 @@ +Metadata-Version: 2.3 +Name: pydantic +Version: 2.7.1 +Summary: Data validation using Python type hints +Project-URL: Homepage, https://github.com/pydantic/pydantic +Project-URL: Documentation, https://docs.pydantic.dev +Project-URL: Funding, https://github.com/sponsors/samuelcolvin +Project-URL: Source, https://github.com/pydantic/pydantic +Project-URL: Changelog, https://docs.pydantic.dev/latest/changelog/ +Author-email: Samuel Colvin , Eric Jolibois , Hasan Ramezani , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Terrence Dorsey , David Montague , Serge Matveenko , Marcelo Trylesinski , Sydney Runkle , David Hewitt , Alex Hall +License-Expression: MIT +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Framework :: Hypothesis +Classifier: Framework :: Pydantic +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +Requires-Dist: annotated-types>=0.4.0 +Requires-Dist: pydantic-core==2.18.2 +Requires-Dist: typing-extensions>=4.6.1 +Provides-Extra: email +Requires-Dist: email-validator>=2.0.0; extra == 'email' +Description-Content-Type: text/markdown + +# Pydantic + +[![CI](https://img.shields.io/github/actions/workflow/status/pydantic/pydantic/ci.yml?branch=main&logo=github&label=CI)](https://github.com/pydantic/pydantic/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![Coverage](https://coverage-badge.samuelcolvin.workers.dev/pydantic/pydantic.svg)](https://coverage-badge.samuelcolvin.workers.dev/redirect/pydantic/pydantic) +[![pypi](https://img.shields.io/pypi/v/pydantic.svg)](https://pypi.python.org/pypi/pydantic) +[![CondaForge](https://img.shields.io/conda/v/conda-forge/pydantic.svg)](https://anaconda.org/conda-forge/pydantic) +[![downloads](https://static.pepy.tech/badge/pydantic/month)](https://pepy.tech/project/pydantic) +[![versions](https://img.shields.io/pypi/pyversions/pydantic.svg)](https://github.com/pydantic/pydantic) +[![license](https://img.shields.io/github/license/pydantic/pydantic.svg)](https://github.com/pydantic/pydantic/blob/main/LICENSE) +[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/main/docs/badge/v2.json)](https://docs.pydantic.dev/latest/contributing/#badges) + +Data validation using Python type hints. + +Fast and extensible, Pydantic plays nicely with your linters/IDE/brain. +Define how data should be in pure, canonical Python 3.8+; validate it with Pydantic. + +## Pydantic Company :rocket: + +We've started a company based on the principles that I believe have led to Pydantic's success. +Learn more from the [Company Announcement](https://blog.pydantic.dev/blog/2023/02/16/company-announcement--pydantic/). + +## Pydantic V1.10 vs. V2 + +Pydantic V2 is a ground-up rewrite that offers many new features, performance improvements, and some breaking changes compared to Pydantic V1. + +If you're using Pydantic V1 you may want to look at the +[pydantic V1.10 Documentation](https://docs.pydantic.dev/) or, +[`1.10.X-fixes` git branch](https://github.com/pydantic/pydantic/tree/1.10.X-fixes). Pydantic V2 also ships with the latest version of Pydantic V1 built in so that you can incrementally upgrade your code base and projects: `from pydantic import v1 as pydantic_v1`. + +## Help + +See [documentation](https://docs.pydantic.dev/) for more details. + +## Installation + +Install using `pip install -U pydantic` or `conda install pydantic -c conda-forge`. +For more installation options to make Pydantic even faster, +see the [Install](https://docs.pydantic.dev/install/) section in the documentation. + +## A Simple Example + +```py +from datetime import datetime +from typing import List, Optional +from pydantic import BaseModel + +class User(BaseModel): + id: int + name: str = 'John Doe' + signup_ts: Optional[datetime] = None + friends: List[int] = [] + +external_data = {'id': '123', 'signup_ts': '2017-06-01 12:22', 'friends': [1, '2', b'3']} +user = User(**external_data) +print(user) +#> User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3] +print(user.id) +#> 123 +``` + +## Contributing + +For guidance on setting up a development environment and how to make a +contribution to Pydantic, see +[Contributing to Pydantic](https://docs.pydantic.dev/contributing/). + +## Reporting a Security Vulnerability + +See our [security policy](https://github.com/pydantic/pydantic/security/policy). + +## Changelog + +## v2.7.1 (2024-04-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.1) + +### What's Changed + +#### Packaging + +* Bump `pydantic-core` to `v2.18.2` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9307](https://github.com/pydantic/pydantic/pull/9307) + +#### New Features + +* Ftp and Websocket connection strings support by [@CherrySuryp](https://github.com/CherrySuryp) in [#9205](https://github.com/pydantic/pydantic/pull/9205) + +#### Changes + +* Use field description for RootModel schema description when there is `…` by [@LouisGobert](https://github.com/LouisGobert) in [#9214](https://github.com/pydantic/pydantic/pull/9214) + +#### Fixes + +* Fix `validation_alias` behavior with `model_construct` for `AliasChoices` and `AliasPath` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9223](https://github.com/pydantic/pydantic/pull/9223) +* Revert `typing.Literal` and import it outside the TYPE_CHECKING block by [@frost-nzcr4](https://github.com/frost-nzcr4) in [#9232](https://github.com/pydantic/pydantic/pull/9232) +* Fix `Secret` serialization schema, applicable for unions by [@sydney-runkle](https://github.com/sydney-runkle) in [#9240](https://github.com/pydantic/pydantic/pull/9240) +* Fix `strict` application to `function-after` with `use_enum_values` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9279](https://github.com/pydantic/pydantic/pull/9279) +* Address case where `model_construct` on a class which defines `model_post_init` fails with `AttributeError` by [@babygrimes](https://github.com/babygrimes) in [#9168](https://github.com/pydantic/pydantic/pull/9168) +* Fix `model_json_schema` with config types by [@NeevCohen](https://github.com/NeevCohen) in [#9287](https://github.com/pydantic/pydantic/pull/9287) +* Support multiple zeros as an `int` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1269](https://github.com/pydantic/pydantic-core/pull/1269) +* Fix validation of `int`s with leading unary plus by [@cknv](https://github.com/cknv) in [pydantic/pydantic-core#1272](https://github.com/pydantic/pydantic-core/pull/1272) +* Fix interaction between `extra != 'ignore'` and `from_attributes=True` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1276](https://github.com/pydantic/pydantic-core/pull/1276) +* Handle error from `Enum`'s `missing` function as `ValidationError` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1274](https://github.com/pydantic/pydantic-core/pull/1754) +* Fix memory leak with `Iterable` validation by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1271](https://github.com/pydantic/pydantic-core/pull/1751) + +### New Contributors + +* [@zzstoatzz](https://github.com/zzstoatzz) made their first contribution in [#9219](https://github.com/pydantic/pydantic/pull/9219) +* [@frost-nzcr4](https://github.com/frost-nzcr4) made their first contribution in [#9232](https://github.com/pydantic/pydantic/pull/9232) +* [@CherrySuryp](https://github.com/CherrySuryp) made their first contribution in [#9205](https://github.com/pydantic/pydantic/pull/9205) +* [@vagenas](https://github.com/vagenas) made their first contribution in [#9268](https://github.com/pydantic/pydantic/pull/9268) +* [@ollz272](https://github.com/ollz272) made their first contribution in [#9262](https://github.com/pydantic/pydantic/pull/9262) +* [@babygrimes](https://github.com/babygrimes) made their first contribution in [#9168](https://github.com/pydantic/pydantic/pull/9168) +* [@swelborn](https://github.com/swelborn) made their first contribution in [#9296](https://github.com/pydantic/pydantic/pull/9296) +* [@kf-novi](https://github.com/kf-novi) made their first contribution in [#9236](https://github.com/pydantic/pydantic/pull/9236) +* [@lgeiger](https://github.com/lgeiger) made their first contribution in [#9288](https://github.com/pydantic/pydantic/pull/9288) + +## v2.7.0 (2024-04-11) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.0) + +The code released in v2.7.0 is practically identical to that of v2.7.0b1. + +### What's Changed + +#### Packaging + +* Reorganize `pyproject.toml` sections by [@Viicos](https://github.com/Viicos) in [#8899](https://github.com/pydantic/pydantic/pull/8899) +* Bump `pydantic-core` to `v2.18.1` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9211](https://github.com/pydantic/pydantic/pull/9211) +* Adopt `jiter` `v0.2.0` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1250](https://github.com/pydantic/pydantic-core/pull/1250) + +#### New Features + +* Extract attribute docstrings from `FieldInfo.description` by [@Viicos](https://github.com/Viicos) in [#6563](https://github.com/pydantic/pydantic/pull/6563) +* Add a `with_config` decorator to comply with typing spec by [@Viicos](https://github.com/Viicos) in [#8611](https://github.com/pydantic/pydantic/pull/8611) +* Allow an optional separator splitting the value and unit of the result of `ByteSize.human_readable` by [@jks15satoshi](https://github.com/jks15satoshi) in [#8706](https://github.com/pydantic/pydantic/pull/8706) +* Add generic `Secret` base type by [@conradogarciaberrotaran](https://github.com/conradogarciaberrotaran) in [#8519](https://github.com/pydantic/pydantic/pull/8519) +* Make use of `Sphinx` inventories for cross references in docs by [@Viicos](https://github.com/Viicos) in [#8682](https://github.com/pydantic/pydantic/pull/8682) +* Add environment variable to disable plugins by [@geospackle](https://github.com/geospackle) in [#8767](https://github.com/pydantic/pydantic/pull/8767) +* Add support for `deprecated` fields by [@Viicos](https://github.com/Viicos) in [#8237](https://github.com/pydantic/pydantic/pull/8237) +* Allow `field_serializer('*')` by [@ornariece](https://github.com/ornariece) in [#9001](https://github.com/pydantic/pydantic/pull/9001) +* Handle a case when `model_config` is defined as a model property by [@alexeyt101](https://github.com/alexeyt101) in [#9004](https://github.com/pydantic/pydantic/pull/9004) +* Update `create_model()` to support `typing.Annotated` as input by [@wannieman98](https://github.com/wannieman98) in [#8947](https://github.com/pydantic/pydantic/pull/8947) +* Add `ClickhouseDsn` support by [@solidguy7](https://github.com/solidguy7) in [#9062](https://github.com/pydantic/pydantic/pull/9062) +* Add support for `re.Pattern[str]` to `pattern` field by [@jag-k](https://github.com/jag-k) in [#9053](https://github.com/pydantic/pydantic/pull/9053) +* Support for `serialize_as_any` runtime setting by [@sydney-runkle](https://github.com/sydney-runkle) in [#8830](https://github.com/pydantic/pydantic/pull/8830) +* Add support for `typing.Self` by [@Youssefares](https://github.com/Youssefares) in [#9023](https://github.com/pydantic/pydantic/pull/9023) +* Ability to pass `context` to serialization by [@ornariece](https://github.com/ornariece) in [#8965](https://github.com/pydantic/pydantic/pull/8965) +* Add feedback widget to docs with flarelytics integration by [@sydney-runkle](https://github.com/sydney-runkle) in [#9129](https://github.com/pydantic/pydantic/pull/9129) +* Support for parsing partial JSON strings in Python by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/jiter#66](https://github.com/pydantic/jiter/pull/66) + +**Finalized in v2.7.0, rather than v2.7.0b1:** +* Add support for field level number to str coercion option by [@NeevCohen](https://github.com/NeevCohen) in [#9137](https://github.com/pydantic/pydantic/pull/9137) +* Update `warnings` parameter for serialization utilities to allow raising a warning by [@Lance-Drane](https://github.com/Lance-Drane) in [#9166](https://github.com/pydantic/pydantic/pull/9166) + +#### Changes + +* Correct docs, logic for `model_construct` behavior with `extra` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8807](https://github.com/pydantic/pydantic/pull/8807) +* Improve error message for improper `RootModel` subclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8857](https://github.com/pydantic/pydantic/pull/8857) +* Use `PEP570` syntax by [@Viicos](https://github.com/Viicos) in [#8940](https://github.com/pydantic/pydantic/pull/8940) +* Add `enum` and `type` to the JSON schema for single item literals by [@dmontagu](https://github.com/dmontagu) in [#8944](https://github.com/pydantic/pydantic/pull/8944) +* Deprecate `update_json_schema` internal function by [@sydney-runkle](https://github.com/sydney-runkle) in [#9125](https://github.com/pydantic/pydantic/pull/9125) +* Serialize duration to hour minute second, instead of just seconds by [@kakilangit](https://github.com/kakilangit) in [pydantic/speedate#50](https://github.com/pydantic/speedate/pull/50) +* Trimming str before parsing to int and float by [@hungtsetse](https://github.com/hungtsetse) in [pydantic/pydantic-core#1203](https://github.com/pydantic/pydantic-core/pull/1203) + +#### Performance + +* `enum` validator improvements by [@samuelcolvin](https://github.com/samuelcolvin) in [#9045](https://github.com/pydantic/pydantic/pull/9045) +* Move `enum` validation and serialization to Rust by [@samuelcolvin](https://github.com/samuelcolvin) in [#9064](https://github.com/pydantic/pydantic/pull/9064) +* Improve schema generation for nested dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#9114](https://github.com/pydantic/pydantic/pull/9114) +* Fast path for ASCII python string creation in JSON by [@samuelcolvin](https://github.com/samuelcolvin) in in [pydantic/jiter#72](https://github.com/pydantic/jiter/pull/72) +* SIMD integer and string JSON parsing on `aarch64`(**Note:** SIMD on x86 will be implemented in a future release) by [@samuelcolvin](https://github.com/samuelcolvin) in in [pydantic/jiter#65](https://github.com/pydantic/jiter/pull/65) +* Support JSON `Cow` from `jiter` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1231](https://github.com/pydantic/pydantic-core/pull/1231) +* MAJOR performance improvement: update to PyO3 0.21 final by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1248](https://github.com/pydantic/pydantic-core/pull/1248) +* cache Python strings by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1240](https://github.com/pydantic/pydantic-core/pull/1240) + +#### Fixes + +* Fix strict parsing for some `Sequence`s by [@sydney-runkle](https://github.com/sydney-runkle) in [#8614](https://github.com/pydantic/pydantic/pull/8614) +* Add a check on the existence of `__qualname__` by [@anci3ntr0ck](https://github.com/anci3ntr0ck) in [#8642](https://github.com/pydantic/pydantic/pull/8642) +* Handle `__pydantic_extra__` annotation being a string or inherited by [@alexmojaki](https://github.com/alexmojaki) in [#8659](https://github.com/pydantic/pydantic/pull/8659) +* Fix json validation for `NameEmail` by [@Holi0317](https://github.com/Holi0317) in [#8650](https://github.com/pydantic/pydantic/pull/8650) +* Fix type-safety of attribute access in `BaseModel` by [@bluenote10](https://github.com/bluenote10) in [#8651](https://github.com/pydantic/pydantic/pull/8651) +* Fix bug with `mypy` plugin and `no_strict_optional = True` by [@dmontagu](https://github.com/dmontagu) in [#8666](https://github.com/pydantic/pydantic/pull/8666) +* Fix `ByteSize` error `type` change by [@sydney-runkle](https://github.com/sydney-runkle) in [#8681](https://github.com/pydantic/pydantic/pull/8681) +* Fix inheriting annotations in dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8679](https://github.com/pydantic/pydantic/pull/8679) +* Fix regression in core schema generation for indirect definition references by [@dmontagu](https://github.com/dmontagu) in [#8702](https://github.com/pydantic/pydantic/pull/8702) +* Fix unsupported types bug with plain validator by [@sydney-runkle](https://github.com/sydney-runkle) in [#8710](https://github.com/pydantic/pydantic/pull/8710) +* Reverting problematic fix from 2.6 release, fixing schema building bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8718](https://github.com/pydantic/pydantic/pull/8718) +* fixes `__pydantic_config__` ignored for TypeDict by [@13sin](https://github.com/13sin) in [#8734](https://github.com/pydantic/pydantic/pull/8734) +* Fix test failures with `pytest v8.0.0` due to `pytest.warns()` starting to work inside `pytest.raises()` by [@mgorny](https://github.com/mgorny) in [#8678](https://github.com/pydantic/pydantic/pull/8678) +* Use `is_valid_field` from 1.x for `mypy` plugin by [@DanielNoord](https://github.com/DanielNoord) in [#8738](https://github.com/pydantic/pydantic/pull/8738) +* Better-support `mypy` strict equality flag by [@dmontagu](https://github.com/dmontagu) in [#8799](https://github.com/pydantic/pydantic/pull/8799) +* model_json_schema export with Annotated types misses 'required' parameters by [@LouisGobert](https://github.com/LouisGobert) in [#8793](https://github.com/pydantic/pydantic/pull/8793) +* Fix default inclusion in `FieldInfo.__repr_args__` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8801](https://github.com/pydantic/pydantic/pull/8801) +* Fix resolution of forward refs in dataclass base classes that are not present in the subclass module namespace by [@matsjoyce-refeyn](https://github.com/matsjoyce-refeyn) in [#8751](https://github.com/pydantic/pydantic/pull/8751) +* Fix `BaseModel` type annotations to be resolvable by `typing.get_type_hints` by [@devmonkey22](https://github.com/devmonkey22) in [#7680](https://github.com/pydantic/pydantic/pull/7680) +* Fix: allow empty string aliases with `AliasGenerator` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8810](https://github.com/pydantic/pydantic/pull/8810) +* Fix test along with `date` -> `datetime` timezone assumption fix by [@sydney-runkle](https://github.com/sydney-runkle) in [#8823](https://github.com/pydantic/pydantic/pull/8823) +* Fix deprecation warning with usage of `ast.Str` by [@Viicos](https://github.com/Viicos) in [#8837](https://github.com/pydantic/pydantic/pull/8837) +* Add missing `deprecated` decorators by [@Viicos](https://github.com/Viicos) in [#8877](https://github.com/pydantic/pydantic/pull/8877) +* Fix serialization of `NameEmail` if name includes an email address by [@NeevCohen](https://github.com/NeevCohen) in [#8860](https://github.com/pydantic/pydantic/pull/8860) +* Add information about class in error message of schema generation by [@Czaki](https://github.com/Czaki) in [#8917](https://github.com/pydantic/pydantic/pull/8917) +* Make `TypeAdapter`'s typing compatible with special forms by [@adriangb](https://github.com/adriangb) in [#8923](https://github.com/pydantic/pydantic/pull/8923) +* Fix issue with config behavior being baked into the ref schema for `enum`s by [@dmontagu](https://github.com/dmontagu) in [#8920](https://github.com/pydantic/pydantic/pull/8920) +* More helpful error re wrong `model_json_schema` usage by [@sydney-runkle](https://github.com/sydney-runkle) in [#8928](https://github.com/pydantic/pydantic/pull/8928) +* Fix nested discriminated union schema gen, pt 2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8932](https://github.com/pydantic/pydantic/pull/8932) +* Fix schema build for nested dataclasses / TypedDicts with discriminators by [@sydney-runkle](https://github.com/sydney-runkle) in [#8950](https://github.com/pydantic/pydantic/pull/8950) +* Remove unnecessary logic for definitions schema gen with discriminated unions by [@sydney-runkle](https://github.com/sydney-runkle) in [#8951](https://github.com/pydantic/pydantic/pull/8951) +* Fix handling of optionals in `mypy` plugin by [@dmontagu](https://github.com/dmontagu) in [#9008](https://github.com/pydantic/pydantic/pull/9008) +* Fix `PlainSerializer` usage with std type constructor by [@sydney-runkle](https://github.com/sydney-runkle) in [#9031](https://github.com/pydantic/pydantic/pull/9031) +* Remove unnecessary warning for config in plugin by [@dmontagu](https://github.com/dmontagu) in [#9039](https://github.com/pydantic/pydantic/pull/9039) +* Fix default value serializing by [@NeevCohen](https://github.com/NeevCohen) in [#9066](https://github.com/pydantic/pydantic/pull/9066) +* Fix extra fields check in `Model.__getattr__()` by [@NeevCohen](https://github.com/NeevCohen) in [#9082](https://github.com/pydantic/pydantic/pull/9082) +* Fix `ClassVar` forward ref inherited from parent class by [@alexmojaki](https://github.com/alexmojaki) in [#9097](https://github.com/pydantic/pydantic/pull/9097) +* fix sequence like validator with strict `True` by [@andresliszt](https://github.com/andresliszt) in [#8977](https://github.com/pydantic/pydantic/pull/8977) +* Improve warning message when a field name shadows a field in a parent model by [@chan-vince](https://github.com/chan-vince) in [#9105](https://github.com/pydantic/pydantic/pull/9105) +* Do not warn about shadowed fields if they are not redefined in a child class by [@chan-vince](https://github.com/chan-vince) in [#9111](https://github.com/pydantic/pydantic/pull/9111) +* Fix discriminated union bug with unsubstituted type var by [@sydney-runkle](https://github.com/sydney-runkle) in [#9124](https://github.com/pydantic/pydantic/pull/9124) +* Support serialization of `deque` when passed to `Sequence[blah blah blah]` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9128](https://github.com/pydantic/pydantic/pull/9128) +* Init private attributes from super-types in `model_post_init` by [@Viicos](https://github.com/Viicos) in [#9134](https://github.com/pydantic/pydantic/pull/9134) +* fix `model_construct` with `validation_alias` by [@ornariece](https://github.com/ornariece) in [#9144](https://github.com/pydantic/pydantic/pull/9144) +* Ensure json-schema generator handles `Literal` `null` types by [@bruno-f-cruz](https://github.com/bruno-f-cruz) in [#9135](https://github.com/pydantic/pydantic/pull/9135) +* **Fixed in v2.7.0**: Fix allow extra generic by [@dmontagu](https://github.com/dmontagu) in [#9193](https://github.com/pydantic/pydantic/pull/9193) + +### New Contributors + +* [@hungtsetse](https://github.com/hungtsetse) made their first contribution in [#8546](https://github.com/pydantic/pydantic/pull/8546) +* [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) made their first contribution in [#8583](https://github.com/pydantic/pydantic/pull/8583) +* [@anci3ntr0ck](https://github.com/anci3ntr0ck) made their first contribution in [#8642](https://github.com/pydantic/pydantic/pull/8642) +* [@Holi0317](https://github.com/Holi0317) made their first contribution in [#8650](https://github.com/pydantic/pydantic/pull/8650) +* [@bluenote10](https://github.com/bluenote10) made their first contribution in [#8651](https://github.com/pydantic/pydantic/pull/8651) +* [@ADSteele916](https://github.com/ADSteele916) made their first contribution in [#8703](https://github.com/pydantic/pydantic/pull/8703) +* [@musicinmybrain](https://github.com/musicinmybrain) made their first contribution in [#8731](https://github.com/pydantic/pydantic/pull/8731) +* [@jks15satoshi](https://github.com/jks15satoshi) made their first contribution in [#8706](https://github.com/pydantic/pydantic/pull/8706) +* [@13sin](https://github.com/13sin) made their first contribution in [#8734](https://github.com/pydantic/pydantic/pull/8734) +* [@DanielNoord](https://github.com/DanielNoord) made their first contribution in [#8738](https://github.com/pydantic/pydantic/pull/8738) +* [@conradogarciaberrotaran](https://github.com/conradogarciaberrotaran) made their first contribution in [#8519](https://github.com/pydantic/pydantic/pull/8519) +* [@chris-griffin](https://github.com/chris-griffin) made their first contribution in [#8775](https://github.com/pydantic/pydantic/pull/8775) +* [@LouisGobert](https://github.com/LouisGobert) made their first contribution in [#8793](https://github.com/pydantic/pydantic/pull/8793) +* [@matsjoyce-refeyn](https://github.com/matsjoyce-refeyn) made their first contribution in [#8751](https://github.com/pydantic/pydantic/pull/8751) +* [@devmonkey22](https://github.com/devmonkey22) made their first contribution in [#7680](https://github.com/pydantic/pydantic/pull/7680) +* [@adamency](https://github.com/adamency) made their first contribution in [#8847](https://github.com/pydantic/pydantic/pull/8847) +* [@MamfTheKramf](https://github.com/MamfTheKramf) made their first contribution in [#8851](https://github.com/pydantic/pydantic/pull/8851) +* [@ornariece](https://github.com/ornariece) made their first contribution in [#9001](https://github.com/pydantic/pydantic/pull/9001) +* [@alexeyt101](https://github.com/alexeyt101) made their first contribution in [#9004](https://github.com/pydantic/pydantic/pull/9004) +* [@wannieman98](https://github.com/wannieman98) made their first contribution in [#8947](https://github.com/pydantic/pydantic/pull/8947) +* [@solidguy7](https://github.com/solidguy7) made their first contribution in [#9062](https://github.com/pydantic/pydantic/pull/9062) +* [@kloczek](https://github.com/kloczek) made their first contribution in [#9047](https://github.com/pydantic/pydantic/pull/9047) +* [@jag-k](https://github.com/jag-k) made their first contribution in [#9053](https://github.com/pydantic/pydantic/pull/9053) +* [@priya-gitTest](https://github.com/priya-gitTest) made their first contribution in [#9088](https://github.com/pydantic/pydantic/pull/9088) +* [@Youssefares](https://github.com/Youssefares) made their first contribution in [#9023](https://github.com/pydantic/pydantic/pull/9023) +* [@chan-vince](https://github.com/chan-vince) made their first contribution in [#9105](https://github.com/pydantic/pydantic/pull/9105) +* [@bruno-f-cruz](https://github.com/bruno-f-cruz) made their first contribution in [#9135](https://github.com/pydantic/pydantic/pull/9135) +* [@Lance-Drane](https://github.com/Lance-Drane) made their first contribution in [#9166](https://github.com/pydantic/pydantic/pull/9166) + +## v2.7.0b1 (2024-04-03) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.0b1) for details. + +## v2.6.4 (2024-03-12) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.4) + +### What's Changed + +#### Fixes + +* Fix usage of `AliasGenerator` with `computed_field` decorator by [@sydney-runkle](https://github.com/sydney-runkle) in [#8806](https://github.com/pydantic/pydantic/pull/8806) +* Fix nested discriminated union schema gen, pt 2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8932](https://github.com/pydantic/pydantic/pull/8932) +* Fix bug with no_strict_optional=True caused by API deferral by [@dmontagu](https://github.com/dmontagu) in [#8826](https://github.com/pydantic/pydantic/pull/8826) + + +## v2.6.3 (2024-02-27) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.3) + +### What's Changed + +#### Packaging + +* Update `pydantic-settings` version in the docs by [@hramezani](https://github.com/hramezani) in [#8906](https://github.com/pydantic/pydantic/pull/8906) + +#### Fixes + +* Fix discriminated union schema gen bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8904](https://github.com/pydantic/pydantic/pull/8904) + + +## v2.6.2 (2024-02-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.2) + +### What's Changed + +#### Packaging + +* Upgrade to `pydantic-core` 2.16.3 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8879](https://github.com/pydantic/pydantic/pull/8879) + +#### Fixes + +* 'YYYY-MM-DD' date string coerced to datetime shouldn't infer timezone by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1193](https://github.com/pydantic/pydantic-core/pull/1193) + + +## v2.6.1 (2024-02-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.1) + +### What's Changed + +#### Packaging + +* Upgrade to `pydantic-core` 2.16.2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8717](https://github.com/pydantic/pydantic/pull/8717) + +#### Fixes + +* Fix bug with `mypy` plugin and `no_strict_optional = True` by [@dmontagu](https://github.com/dmontagu) in [#8666](https://github.com/pydantic/pydantic/pull/8666) +* Fix `ByteSize` error `type` change by [@sydney-runkle](https://github.com/sydney-runkle) in [#8681](https://github.com/pydantic/pydantic/pull/8681) +* Fix inheriting `Field` annotations in dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8679](https://github.com/pydantic/pydantic/pull/8679) +* Fix regression in core schema generation for indirect definition references by [@dmontagu](https://github.com/dmontagu) in [#8702](https://github.com/pydantic/pydantic/pull/8702) +* Fix unsupported types bug with `PlainValidator` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8710](https://github.com/pydantic/pydantic/pull/8710) +* Reverting problematic fix from 2.6 release, fixing schema building bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8718](https://github.com/pydantic/pydantic/pull/8718) +* Fix warning for tuple of wrong size in `Union` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1174](https://github.com/pydantic/pydantic-core/pull/1174) +* Fix `computed_field` JSON serializer `exclude_none` behavior by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1187](https://github.com/pydantic/pydantic-core/pull/1187) + + +## v2.6.0 (2024-01-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.0) + +The code released in v2.6.0 is practically identical to that of v2.6.0b1. + +### What's Changed + +#### Packaging + +* Check for `email-validator` version >= 2.0 by [@commonism](https://github.com/commonism) in [#6033](https://github.com/pydantic/pydantic/pull/6033) +* Upgrade `ruff`` target version to Python 3.8 by [@Elkiwa](https://github.com/Elkiwa) in [#8341](https://github.com/pydantic/pydantic/pull/8341) +* Update to `pydantic-extra-types==2.4.1` by [@yezz123](https://github.com/yezz123) in [#8478](https://github.com/pydantic/pydantic/pull/8478) +* Update to `pyright==1.1.345` by [@Viicos](https://github.com/Viicos) in [#8453](https://github.com/pydantic/pydantic/pull/8453) +* Update pydantic-core from 2.14.6 to 2.16.1, significant changes from these updates are described below, full changelog [here](https://github.com/pydantic/pydantic-core/compare/v2.14.6...v2.16.1) + +#### New Features + +* Add `NatsDsn` by [@ekeew](https://github.com/ekeew) in [#6874](https://github.com/pydantic/pydantic/pull/6874) +* Add `ConfigDict.ser_json_inf_nan` by [@davidhewitt](https://github.com/davidhewitt) in [#8159](https://github.com/pydantic/pydantic/pull/8159) +* Add `types.OnErrorOmit` by [@adriangb](https://github.com/adriangb) in [#8222](https://github.com/pydantic/pydantic/pull/8222) +* Support `AliasGenerator` usage by [@sydney-runkle](https://github.com/sydney-runkle) in [#8282](https://github.com/pydantic/pydantic/pull/8282) +* Add Pydantic People Page to docs by [@sydney-runkle](https://github.com/sydney-runkle) in [#8345](https://github.com/pydantic/pydantic/pull/8345) +* Support `yyyy-MM-DD` datetime parsing by [@sydney-runkle](https://github.com/sydney-runkle) in [#8404](https://github.com/pydantic/pydantic/pull/8404) +* Added bits conversions to the `ByteSize` class [#8415](https://github.com/pydantic/pydantic/issues/8415) by [@luca-matei](https://github.com/luca-matei) in [#8507](https://github.com/pydantic/pydantic/pull/8507) +* Enable json schema creation with type `ByteSize` by [@geospackle](https://github.com/geospackle) in [#8537](https://github.com/pydantic/pydantic/pull/8537) +* Add `eval_type_backport` to handle union operator and builtin generic subscripting in older Pythons by [@alexmojaki](https://github.com/alexmojaki) in [#8209](https://github.com/pydantic/pydantic/pull/8209) +* Add support for `dataclass` fields `init` by [@dmontagu](https://github.com/dmontagu) in [#8552](https://github.com/pydantic/pydantic/pull/8552) +* Implement pickling for `ValidationError` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1119](https://github.com/pydantic/pydantic-core/pull/1119) +* Add unified tuple validator that can handle "variadic" tuples via PEP-646 by [@dmontagu](https://github.com/dmontagu) in [pydantic/pydantic-core#865](https://github.com/pydantic/pydantic-core/pull/865) + +#### Changes + +* Drop Python3.7 support by [@hramezani](https://github.com/hramezani) in [#7188](https://github.com/pydantic/pydantic/pull/7188) +* Drop Python 3.7, and PyPy 3.7 and 3.8 by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1129](https://github.com/pydantic/pydantic-core/pull/1129) +* Use positional-only `self` in `BaseModel` constructor, so no field name can ever conflict with it by [@ariebovenberg](https://github.com/ariebovenberg) in [#8072](https://github.com/pydantic/pydantic/pull/8072) +* Make `@validate_call` return a function instead of a custom descriptor - fixes binding issue with inheritance and adds `self/cls` argument to validation errors by [@alexmojaki](https://github.com/alexmojaki) in [#8268](https://github.com/pydantic/pydantic/pull/8268) +* Exclude `BaseModel` docstring from JSON schema description by [@sydney-runkle](https://github.com/sydney-runkle) in [#8352](https://github.com/pydantic/pydantic/pull/8352) +* Introducing `classproperty` decorator for `model_computed_fields` by [@Jocelyn-Gas](https://github.com/Jocelyn-Gas) in [#8437](https://github.com/pydantic/pydantic/pull/8437) +* Explicitly raise an error if field names clashes with types by [@Viicos](https://github.com/Viicos) in [#8243](https://github.com/pydantic/pydantic/pull/8243) +* Use stricter serializer for unions of simple types by [@alexdrydew](https://github.com/alexdrydew) [pydantic/pydantic-core#1132](https://github.com/pydantic/pydantic-core/pull/1132) + +#### Performance + +* Add Codspeed profiling Actions workflow by [@lambertsbennett](https://github.com/lambertsbennett) in [#8054](https://github.com/pydantic/pydantic/pull/8054) +* Improve `int` extraction by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1155](https://github.com/pydantic/pydantic-core/pull/1155) +* Improve performance of recursion guard by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1156](https://github.com/pydantic/pydantic-core/pull/1156) +* `dataclass` serialization speedups by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1162](https://github.com/pydantic/pydantic-core/pull/1162) +* Avoid `HashMap` creation when looking up small JSON objects in `LazyIndexMaps` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/jiter#55](https://github.com/pydantic/jiter/pull/55) +* use hashbrown to speedup python string caching by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#51](https://github.com/pydantic/jiter/pull/51) +* Replace `Peak` with more efficient `Peek` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#48](https://github.com/pydantic/jiter/pull/48) + +#### Fixes + +* Move `getattr` warning in deprecated `BaseConfig` by [@tlambert03](https://github.com/tlambert03) in [#7183](https://github.com/pydantic/pydantic/pull/7183) +* Only hash `model_fields`, not whole `__dict__` by [@alexmojaki](https://github.com/alexmojaki) in [#7786](https://github.com/pydantic/pydantic/pull/7786) +* Fix mishandling of unions while freezing types in the `mypy` plugin by [@dmontagu](https://github.com/dmontagu) in [#7411](https://github.com/pydantic/pydantic/pull/7411) +* Fix `mypy` error on untyped `ClassVar` by [@vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) in [#8138](https://github.com/pydantic/pydantic/pull/8138) +* Only compare pydantic fields in `BaseModel.__eq__` instead of whole `__dict__` by [@QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) in [#7825](https://github.com/pydantic/pydantic/pull/7825) +* Update `strict` docstring in `model_validate` method. by [@LukeTonin](https://github.com/LukeTonin) in [#8223](https://github.com/pydantic/pydantic/pull/8223) +* Fix overload position of `computed_field` by [@Viicos](https://github.com/Viicos) in [#8227](https://github.com/pydantic/pydantic/pull/8227) +* Fix custom type type casting used in multiple attributes by [@ianhfc](https://github.com/ianhfc) in [#8066](https://github.com/pydantic/pydantic/pull/8066) +* Fix issue not allowing `validate_call` decorator to be dynamically assigned to a class method by [@jusexton](https://github.com/jusexton) in [#8249](https://github.com/pydantic/pydantic/pull/8249) +* Fix issue `unittest.mock` deprecation warnings by [@ibleedicare](https://github.com/ibleedicare) in [#8262](https://github.com/pydantic/pydantic/pull/8262) +* Added tests for the case `JsonValue` contains subclassed primitive values by [@jusexton](https://github.com/jusexton) in [#8286](https://github.com/pydantic/pydantic/pull/8286) +* Fix `mypy` error on free before validator (classmethod) by [@sydney-runkle](https://github.com/sydney-runkle) in [#8285](https://github.com/pydantic/pydantic/pull/8285) +* Fix `to_snake` conversion by [@jevins09](https://github.com/jevins09) in [#8316](https://github.com/pydantic/pydantic/pull/8316) +* Fix type annotation of `ModelMetaclass.__prepare__` by [@slanzmich](https://github.com/slanzmich) in [#8305](https://github.com/pydantic/pydantic/pull/8305) +* Disallow `config` specification when initializing a `TypeAdapter` when the annotated type has config already by [@sydney-runkle](https://github.com/sydney-runkle) in [#8365](https://github.com/pydantic/pydantic/pull/8365) +* Fix a naming issue with JSON schema for generics parametrized by recursive type aliases by [@dmontagu](https://github.com/dmontagu) in [#8389](https://github.com/pydantic/pydantic/pull/8389) +* Fix type annotation in pydantic people script by [@shenxiangzhuang](https://github.com/shenxiangzhuang) in [#8402](https://github.com/pydantic/pydantic/pull/8402) +* Add support for field `alias` in `dataclass` signature by [@NeevCohen](https://github.com/NeevCohen) in [#8387](https://github.com/pydantic/pydantic/pull/8387) +* Fix bug with schema generation with `Field(...)` in a forward ref by [@dmontagu](https://github.com/dmontagu) in [#8494](https://github.com/pydantic/pydantic/pull/8494) +* Fix ordering of keys in `__dict__` with `model_construct` call by [@sydney-runkle](https://github.com/sydney-runkle) in [#8500](https://github.com/pydantic/pydantic/pull/8500) +* Fix module `path_type` creation when globals does not contain `__name__` by [@hramezani](https://github.com/hramezani) in [#8470](https://github.com/pydantic/pydantic/pull/8470) +* Fix for namespace issue with dataclasses with `from __future__ import annotations` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8513](https://github.com/pydantic/pydantic/pull/8513) +* Fix: make function validator types positional-only by [@pmmmwh](https://github.com/pmmmwh) in [#8479](https://github.com/pydantic/pydantic/pull/8479) +* Fix usage of `@deprecated` by [@Viicos](https://github.com/Viicos) in [#8294](https://github.com/pydantic/pydantic/pull/8294) +* Add more support for private attributes in `model_construct` call by [@sydney-runkle](https://github.com/sydney-runkle) in [#8525](https://github.com/pydantic/pydantic/pull/8525) +* Use a stack for the types namespace by [@dmontagu](https://github.com/dmontagu) in [#8378](https://github.com/pydantic/pydantic/pull/8378) +* Fix schema-building bug with `TypeAliasType` for types with refs by [@dmontagu](https://github.com/dmontagu) in [#8526](https://github.com/pydantic/pydantic/pull/8526) +* Support `pydantic.Field(repr=False)` in dataclasses by [@tigeryy2](https://github.com/tigeryy2) in [#8511](https://github.com/pydantic/pydantic/pull/8511) +* Override `dataclass_transform` behavior for `RootModel` by [@Viicos](https://github.com/Viicos) in [#8163](https://github.com/pydantic/pydantic/pull/8163) +* Refactor signature generation for simplicity by [@sydney-runkle](https://github.com/sydney-runkle) in [#8572](https://github.com/pydantic/pydantic/pull/8572) +* Fix ordering bug of PlainValidator annotation by [@Anvil](https://github.com/Anvil) in [#8567](https://github.com/pydantic/pydantic/pull/8567) +* Fix `exclude_none` for json serialization of `computed_field`s by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1098](https://github.com/pydantic/pydantic-core/pull/1098) +* Support yyyy-MM-DD string for datetimes by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1124](https://github.com/pydantic/pydantic-core/pull/1124) +* Tweak ordering of definitions in generated schemas by [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) in [#8583](https://github.com/pydantic/pydantic/pull/8583) + + +### New Contributors + +#### `pydantic` +* [@ekeew](https://github.com/ekeew) made their first contribution in [#6874](https://github.com/pydantic/pydantic/pull/6874) +* [@lambertsbennett](https://github.com/lambertsbennett) made their first contribution in [#8054](https://github.com/pydantic/pydantic/pull/8054) +* [@vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) made their first contribution in [#8138](https://github.com/pydantic/pydantic/pull/8138) +* [@QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) made their first contribution in [#7825](https://github.com/pydantic/pydantic/pull/7825) +* [@ariebovenberg](https://github.com/ariebovenberg) made their first contribution in [#8072](https://github.com/pydantic/pydantic/pull/8072) +* [@LukeTonin](https://github.com/LukeTonin) made their first contribution in [#8223](https://github.com/pydantic/pydantic/pull/8223) +* [@denisart](https://github.com/denisart) made their first contribution in [#8231](https://github.com/pydantic/pydantic/pull/8231) +* [@ianhfc](https://github.com/ianhfc) made their first contribution in [#8066](https://github.com/pydantic/pydantic/pull/8066) +* [@eonu](https://github.com/eonu) made their first contribution in [#8255](https://github.com/pydantic/pydantic/pull/8255) +* [@amandahla](https://github.com/amandahla) made their first contribution in [#8263](https://github.com/pydantic/pydantic/pull/8263) +* [@ibleedicare](https://github.com/ibleedicare) made their first contribution in [#8262](https://github.com/pydantic/pydantic/pull/8262) +* [@jevins09](https://github.com/jevins09) made their first contribution in [#8316](https://github.com/pydantic/pydantic/pull/8316) +* [@cuu508](https://github.com/cuu508) made their first contribution in [#8322](https://github.com/pydantic/pydantic/pull/8322) +* [@slanzmich](https://github.com/slanzmich) made their first contribution in [#8305](https://github.com/pydantic/pydantic/pull/8305) +* [@jensenbox](https://github.com/jensenbox) made their first contribution in [#8331](https://github.com/pydantic/pydantic/pull/8331) +* [@szepeviktor](https://github.com/szepeviktor) made their first contribution in [#8356](https://github.com/pydantic/pydantic/pull/8356) +* [@Elkiwa](https://github.com/Elkiwa) made their first contribution in [#8341](https://github.com/pydantic/pydantic/pull/8341) +* [@parhamfh](https://github.com/parhamfh) made their first contribution in [#8395](https://github.com/pydantic/pydantic/pull/8395) +* [@shenxiangzhuang](https://github.com/shenxiangzhuang) made their first contribution in [#8402](https://github.com/pydantic/pydantic/pull/8402) +* [@NeevCohen](https://github.com/NeevCohen) made their first contribution in [#8387](https://github.com/pydantic/pydantic/pull/8387) +* [@zby](https://github.com/zby) made their first contribution in [#8497](https://github.com/pydantic/pydantic/pull/8497) +* [@patelnets](https://github.com/patelnets) made their first contribution in [#8491](https://github.com/pydantic/pydantic/pull/8491) +* [@edwardwli](https://github.com/edwardwli) made their first contribution in [#8503](https://github.com/pydantic/pydantic/pull/8503) +* [@luca-matei](https://github.com/luca-matei) made their first contribution in [#8507](https://github.com/pydantic/pydantic/pull/8507) +* [@Jocelyn-Gas](https://github.com/Jocelyn-Gas) made their first contribution in [#8437](https://github.com/pydantic/pydantic/pull/8437) +* [@bL34cHig0](https://github.com/bL34cHig0) made their first contribution in [#8501](https://github.com/pydantic/pydantic/pull/8501) +* [@tigeryy2](https://github.com/tigeryy2) made their first contribution in [#8511](https://github.com/pydantic/pydantic/pull/8511) +* [@geospackle](https://github.com/geospackle) made their first contribution in [#8537](https://github.com/pydantic/pydantic/pull/8537) +* [@Anvil](https://github.com/Anvil) made their first contribution in [#8567](https://github.com/pydantic/pydantic/pull/8567) +* [@hungtsetse](https://github.com/hungtsetse) made their first contribution in [#8546](https://github.com/pydantic/pydantic/pull/8546) +* [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) made their first contribution in [#8583](https://github.com/pydantic/pydantic/pull/8583) + +#### `pydantic-core` +* [@mariuswinger](https://github.com/mariuswinger) made their first contribution in [pydantic/pydantic-core#1087](https://github.com/pydantic/pydantic-core/pull/1087) +* [@adamchainz](https://github.com/adamchainz) made their first contribution in [pydantic/pydantic-core#1090](https://github.com/pydantic/pydantic-core/pull/1090) +* [@akx](https://github.com/akx) made their first contribution in [pydantic/pydantic-core#1123](https://github.com/pydantic/pydantic-core/pull/1123) + +## v2.6.0b1 (2024-01-19) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.0b1) for details. + +## v2.5.3 (2023-12-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.3) + +### What's Changed + +#### Packaging + +* uprev `pydantic-core` to 2.14.6 + +#### Fixes + +* Fix memory leak with recursive definitions creating reference cycles by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1125](https://github.com/pydantic/pydantic-core/pull/1125) + +## v2.5.2 (2023-11-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.2) + +### What's Changed + +#### Packaging + +* uprev `pydantic-core` to 2.14.5 + +#### New Features + +* Add `ConfigDict.ser_json_inf_nan` by [@davidhewitt](https://github.com/davidhewitt) in [#8159](https://github.com/pydantic/pydantic/pull/8159) + +#### Fixes + +* Fix validation of `Literal` from JSON keys when used as `dict` key by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1075](https://github.com/pydantic/pydantic-core/pull/1075) +* Fix bug re `custom_init` on members of `Union` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1076](https://github.com/pydantic/pydantic-core/pull/1076) +* Fix `JsonValue` `bool` serialization by [@sydney-runkle](https://github.com/sydney-runkle) in [#8190](https://github.com/pydantic/pydantic/pull/8159) +* Fix handling of unhashable inputs with `Literal` in `Union`s by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1089](https://github.com/pydantic/pydantic-core/pull/1089) + +## v2.5.1 (2023-11-15) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.1) + +### What's Changed + +#### Packaging + +* uprev pydantic-core to 2.14.3 by [@samuelcolvin](https://github.com/samuelcolvin) in [#8120](https://github.com/pydantic/pydantic/pull/8120) + +#### Fixes + +* Fix package description limit by [@dmontagu](https://github.com/dmontagu) in [#8097](https://github.com/pydantic/pydantic/pull/8097) +* Fix `ValidateCallWrapper` error when creating a model which has a [@validate_call](https://github.com/validate_call) wrapped field annotation by [@sydney-runkle](https://github.com/sydney-runkle) in [#8110](https://github.com/pydantic/pydantic/pull/8110) + +## v2.5.0 (2023-11-13) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.0) + +The code released in v2.5.0 is functionally identical to that of v2.5.0b1. + +### What's Changed + +#### Packaging + +* Update pydantic-core from 2.10.1 to 2.14.1, significant changes from these updates are described below, full changelog [here](https://github.com/pydantic/pydantic-core/compare/v2.10.1...v2.14.1) +* Update to `pyright==1.1.335` by [@Viicos](https://github.com/Viicos) in [#8075](https://github.com/pydantic/pydantic/pull/8075) + +#### New Features + +* Allow plugins to catch non `ValidationError` errors by [@adriangb](https://github.com/adriangb) in [#7806](https://github.com/pydantic/pydantic/pull/7806) +* Support `__doc__` argument in `create_model()` by [@chris-spann](https://github.com/chris-spann) in [#7863](https://github.com/pydantic/pydantic/pull/7863) +* Expose `regex_engine` flag - meaning you can use with the Rust or Python regex libraries in constraints by [@utkini](https://github.com/utkini) in [#7768](https://github.com/pydantic/pydantic/pull/7768) +* Save return type generated from type annotation in `ComputedFieldInfo` by [@alexmojaki](https://github.com/alexmojaki) in [#7889](https://github.com/pydantic/pydantic/pull/7889) +* Adopting `ruff` formatter by [@Luca-Blight](https://github.com/Luca-Blight) in [#7930](https://github.com/pydantic/pydantic/pull/7930) +* Added `validation_error_cause` to config by [@zakstucke](https://github.com/zakstucke) in [#7626](https://github.com/pydantic/pydantic/pull/7626) +* Make path of the item to validate available in plugin by [@hramezani](https://github.com/hramezani) in [#7861](https://github.com/pydantic/pydantic/pull/7861) +* Add `CallableDiscriminator` and `Tag` by [@dmontagu](https://github.com/dmontagu) in [#7983](https://github.com/pydantic/pydantic/pull/7983) + * `CallableDiscriminator` renamed to `Discriminator` by [@dmontagu](https://github.com/dmontagu) in [#8047](https://github.com/pydantic/pydantic/pull/8047) +* Make union case tags affect union error messages by [@dmontagu](https://github.com/dmontagu) in [#8001](https://github.com/pydantic/pydantic/pull/8001) +* Add `examples` and `json_schema_extra` to `@computed_field` by [@alexmojaki](https://github.com/alexmojaki) in [#8013](https://github.com/pydantic/pydantic/pull/8013) +* Add `JsonValue` type by [@dmontagu](https://github.com/dmontagu) in [#7998](https://github.com/pydantic/pydantic/pull/7998) +* Allow `str` as argument to `Discriminator` by [@dmontagu](https://github.com/dmontagu) in [#8047](https://github.com/pydantic/pydantic/pull/8047) +* Add `SchemaSerializer.__reduce__` method to enable pickle serialization by [@edoakes](https://github.com/edoakes) in [pydantic/pydantic-core#1006](https://github.com/pydantic/pydantic-core/pull/1006) + +#### Changes + +* **Significant Change:** replace `ultra_strict` with new smart union implementation, the way unions are validated has changed significantly to improve performance and correctness, we have worked hard to absolutely minimise the number of cases where behaviour has changed, see the PR for details - by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#867](https://github.com/pydantic/pydantic-core/pull/867) +* Add support for instance method reassignment when `extra='allow'` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7683](https://github.com/pydantic/pydantic/pull/7683) +* Support JSON schema generation for `Enum` types with no cases by [@sydney-runkle](https://github.com/sydney-runkle) in [#7927](https://github.com/pydantic/pydantic/pull/7927) +* Warn if a class inherits from `Generic` before `BaseModel` by [@alexmojaki](https://github.com/alexmojaki) in [#7891](https://github.com/pydantic/pydantic/pull/7891) + +#### Performance + +* New custom JSON parser, `jiter` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#974](https://github.com/pydantic/pydantic-core/pull/974) +* PGO build for MacOS M1 by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1063](https://github.com/pydantic/pydantic-core/pull/1063) +* Use `__getattr__` for all package imports, improve import time by [@samuelcolvin](https://github.com/samuelcolvin) in [#7947](https://github.com/pydantic/pydantic/pull/7947) + +#### Fixes + +* Fix `mypy` issue with subclasses of `RootModel` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7677](https://github.com/pydantic/pydantic/pull/7677) +* Properly rebuild the `FieldInfo` when a forward ref gets evaluated by [@dmontagu](https://github.com/dmontagu) in [#7698](https://github.com/pydantic/pydantic/pull/7698) +* Fix failure to load `SecretStr` from JSON (regression in v2.4) by [@sydney-runkle](https://github.com/sydney-runkle) in [#7729](https://github.com/pydantic/pydantic/pull/7729) +* Fix `defer_build` behavior with `TypeAdapter` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7736](https://github.com/pydantic/pydantic/pull/7736) +* Improve compatibility with legacy `mypy` versions by [@dmontagu](https://github.com/dmontagu) in [#7742](https://github.com/pydantic/pydantic/pull/7742) +* Fix: update `TypeVar` handling when default is not set by [@pmmmwh](https://github.com/pmmmwh) in [#7719](https://github.com/pydantic/pydantic/pull/7719) +* Support specification of `strict` on `Enum` type fields by [@sydney-runkle](https://github.com/sydney-runkle) in [#7761](https://github.com/pydantic/pydantic/pull/7761) +* Wrap `weakref.ref` instead of subclassing to fix `cloudpickle` serialization by [@edoakes](https://github.com/edoakes) in [#7780](https://github.com/pydantic/pydantic/pull/7780) +* Keep values of private attributes set within `model_post_init` in subclasses by [@alexmojaki](https://github.com/alexmojaki) in [#7775](https://github.com/pydantic/pydantic/pull/7775) +* Add more specific type for non-callable `json_schema_extra` by [@alexmojaki](https://github.com/alexmojaki) in [#7803](https://github.com/pydantic/pydantic/pull/7803) +* Raise an error when deleting frozen (model) fields by [@alexmojaki](https://github.com/alexmojaki) in [#7800](https://github.com/pydantic/pydantic/pull/7800) +* Fix schema sorting bug with default values by [@sydney-runkle](https://github.com/sydney-runkle) in [#7817](https://github.com/pydantic/pydantic/pull/7817) +* Use generated alias for aliases that are not specified otherwise by [@alexmojaki](https://github.com/alexmojaki) in [#7802](https://github.com/pydantic/pydantic/pull/7802) +* Support `strict` specification for `UUID` types by [@sydney-runkle](https://github.com/sydney-runkle) in [#7865](https://github.com/pydantic/pydantic/pull/7865) +* JSON schema: fix extra parameter handling by [@me-and](https://github.com/me-and) in [#7810](https://github.com/pydantic/pydantic/pull/7810) +* Fix: support `pydantic.Field(kw_only=True)` with inherited dataclasses by [@PrettyWood](https://github.com/PrettyWood) in [#7827](https://github.com/pydantic/pydantic/pull/7827) +* Support `validate_call` decorator for methods in classes with `__slots__` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7883](https://github.com/pydantic/pydantic/pull/7883) +* Fix pydantic dataclass problem with `dataclasses.field` default by [@hramezani](https://github.com/hramezani) in [#7898](https://github.com/pydantic/pydantic/pull/7898) +* Fix schema generation for generics with union type bounds by [@sydney-runkle](https://github.com/sydney-runkle) in [#7899](https://github.com/pydantic/pydantic/pull/7899) +* Fix version for `importlib_metadata` on python 3.7 by [@sydney-runkle](https://github.com/sydney-runkle) in [#7904](https://github.com/pydantic/pydantic/pull/7904) +* Support `|` operator (Union) in PydanticRecursiveRef by [@alexmojaki](https://github.com/alexmojaki) in [#7892](https://github.com/pydantic/pydantic/pull/7892) +* Fix `display_as_type` for `TypeAliasType` in python 3.12 by [@dmontagu](https://github.com/dmontagu) in [#7929](https://github.com/pydantic/pydantic/pull/7929) +* Add support for `NotRequired` generics in `TypedDict` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7932](https://github.com/pydantic/pydantic/pull/7932) +* Make generic `TypeAliasType` specifications produce different schema definitions by [@alexdrydew](https://github.com/alexdrydew) in [#7893](https://github.com/pydantic/pydantic/pull/7893) +* Added fix for signature of inherited dataclass by [@howsunjow](https://github.com/howsunjow) in [#7925](https://github.com/pydantic/pydantic/pull/7925) +* Make the model name generation more robust in JSON schema by [@joakimnordling](https://github.com/joakimnordling) in [#7881](https://github.com/pydantic/pydantic/pull/7881) +* Fix plurals in validation error messages (in tests) by [@Iipin](https://github.com/Iipin) in [#7972](https://github.com/pydantic/pydantic/pull/7972) +* `PrivateAttr` is passed from `Annotated` default position by [@tabassco](https://github.com/tabassco) in [#8004](https://github.com/pydantic/pydantic/pull/8004) +* Don't decode bytes (which may not be UTF8) when displaying SecretBytes by [@alexmojaki](https://github.com/alexmojaki) in [#8012](https://github.com/pydantic/pydantic/pull/8012) +* Use `classmethod` instead of `classmethod[Any, Any, Any]` by [@Mr-Pepe](https://github.com/Mr-Pepe) in [#7979](https://github.com/pydantic/pydantic/pull/7979) +* Clearer error on invalid Plugin by [@samuelcolvin](https://github.com/samuelcolvin) in [#8023](https://github.com/pydantic/pydantic/pull/8023) +* Correct pydantic dataclasses import by [@samuelcolvin](https://github.com/samuelcolvin) in [#8027](https://github.com/pydantic/pydantic/pull/8027) +* Fix misbehavior for models referencing redefined type aliases by [@dmontagu](https://github.com/dmontagu) in [#8050](https://github.com/pydantic/pydantic/pull/8050) +* Fix `Optional` field with `validate_default` only performing one field validation by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1002](https://github.com/pydantic/pydantic-core/pull/1002) +* Fix `definition-ref` bug with `Dict` keys by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1014](https://github.com/pydantic/pydantic-core/pull/1014) +* Fix bug allowing validation of `bool` types with `coerce_numbers_to_str=True` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1017](https://github.com/pydantic/pydantic-core/pull/1017) +* Don't accept `NaN` in float and decimal constraints by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1037](https://github.com/pydantic/pydantic-core/pull/1037) +* Add `lax_str` and `lax_int` support for enum values not inherited from str/int by [@michaelhly](https://github.com/michaelhly) in [pydantic/pydantic-core#1015](https://github.com/pydantic/pydantic-core/pull/1015) +* Support subclasses in lists in `Union` of `List` types by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1039](https://github.com/pydantic/pydantic-core/pull/1039) +* Allow validation against `max_digits` and `decimals` to pass if normalized or non-normalized input is valid by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1049](https://github.com/pydantic/pydantic-core/pull/1049) +* Fix: proper pluralization in `ValidationError` messages by [@Iipin](https://github.com/Iipin) in [pydantic/pydantic-core#1050](https://github.com/pydantic/pydantic-core/pull/1050) +* Disallow the string `'-'` as `datetime` input by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/speedate#52](https://github.com/pydantic/speedate/pull/52) & [pydantic/pydantic-core#1060](https://github.com/pydantic/pydantic-core/pull/1060) +* Fix: NaN and Inf float serialization by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1062](https://github.com/pydantic/pydantic-core/pull/1062) +* Restore manylinux-compatible PGO builds by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1068](https://github.com/pydantic/pydantic-core/pull/1068) + +### New Contributors + +#### `pydantic` +* [@schneebuzz](https://github.com/schneebuzz) made their first contribution in [#7699](https://github.com/pydantic/pydantic/pull/7699) +* [@edoakes](https://github.com/edoakes) made their first contribution in [#7780](https://github.com/pydantic/pydantic/pull/7780) +* [@alexmojaki](https://github.com/alexmojaki) made their first contribution in [#7775](https://github.com/pydantic/pydantic/pull/7775) +* [@NickG123](https://github.com/NickG123) made their first contribution in [#7751](https://github.com/pydantic/pydantic/pull/7751) +* [@gowthamgts](https://github.com/gowthamgts) made their first contribution in [#7830](https://github.com/pydantic/pydantic/pull/7830) +* [@jamesbraza](https://github.com/jamesbraza) made their first contribution in [#7848](https://github.com/pydantic/pydantic/pull/7848) +* [@laundmo](https://github.com/laundmo) made their first contribution in [#7850](https://github.com/pydantic/pydantic/pull/7850) +* [@rahmatnazali](https://github.com/rahmatnazali) made their first contribution in [#7870](https://github.com/pydantic/pydantic/pull/7870) +* [@waterfountain1996](https://github.com/waterfountain1996) made their first contribution in [#7878](https://github.com/pydantic/pydantic/pull/7878) +* [@chris-spann](https://github.com/chris-spann) made their first contribution in [#7863](https://github.com/pydantic/pydantic/pull/7863) +* [@me-and](https://github.com/me-and) made their first contribution in [#7810](https://github.com/pydantic/pydantic/pull/7810) +* [@utkini](https://github.com/utkini) made their first contribution in [#7768](https://github.com/pydantic/pydantic/pull/7768) +* [@bn-l](https://github.com/bn-l) made their first contribution in [#7744](https://github.com/pydantic/pydantic/pull/7744) +* [@alexdrydew](https://github.com/alexdrydew) made their first contribution in [#7893](https://github.com/pydantic/pydantic/pull/7893) +* [@Luca-Blight](https://github.com/Luca-Blight) made their first contribution in [#7930](https://github.com/pydantic/pydantic/pull/7930) +* [@howsunjow](https://github.com/howsunjow) made their first contribution in [#7925](https://github.com/pydantic/pydantic/pull/7925) +* [@joakimnordling](https://github.com/joakimnordling) made their first contribution in [#7881](https://github.com/pydantic/pydantic/pull/7881) +* [@icfly2](https://github.com/icfly2) made their first contribution in [#7976](https://github.com/pydantic/pydantic/pull/7976) +* [@Yummy-Yums](https://github.com/Yummy-Yums) made their first contribution in [#8003](https://github.com/pydantic/pydantic/pull/8003) +* [@Iipin](https://github.com/Iipin) made their first contribution in [#7972](https://github.com/pydantic/pydantic/pull/7972) +* [@tabassco](https://github.com/tabassco) made their first contribution in [#8004](https://github.com/pydantic/pydantic/pull/8004) +* [@Mr-Pepe](https://github.com/Mr-Pepe) made their first contribution in [#7979](https://github.com/pydantic/pydantic/pull/7979) +* [@0x00cl](https://github.com/0x00cl) made their first contribution in [#8010](https://github.com/pydantic/pydantic/pull/8010) +* [@barraponto](https://github.com/barraponto) made their first contribution in [#8032](https://github.com/pydantic/pydantic/pull/8032) + +#### `pydantic-core` +* [@sisp](https://github.com/sisp) made their first contribution in [pydantic/pydantic-core#995](https://github.com/pydantic/pydantic-core/pull/995) +* [@michaelhly](https://github.com/michaelhly) made their first contribution in [pydantic/pydantic-core#1015](https://github.com/pydantic/pydantic-core/pull/1015) + +## v2.5.0b1 (2023-11-09) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.0b1) for details. + +## v2.4.2 (2023-09-27) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.2) + +### What's Changed + +#### Fixes + +* Fix bug with JSON schema for sequence of discriminated union by [@dmontagu](https://github.com/dmontagu) in [#7647](https://github.com/pydantic/pydantic/pull/7647) +* Fix schema references in discriminated unions by [@adriangb](https://github.com/adriangb) in [#7646](https://github.com/pydantic/pydantic/pull/7646) +* Fix json schema generation for recursive models by [@adriangb](https://github.com/adriangb) in [#7653](https://github.com/pydantic/pydantic/pull/7653) +* Fix `models_json_schema` for generic models by [@adriangb](https://github.com/adriangb) in [#7654](https://github.com/pydantic/pydantic/pull/7654) +* Fix xfailed test for generic model signatures by [@adriangb](https://github.com/adriangb) in [#7658](https://github.com/pydantic/pydantic/pull/7658) + +### New Contributors + +* [@austinorr](https://github.com/austinorr) made their first contribution in [#7657](https://github.com/pydantic/pydantic/pull/7657) +* [@peterHoburg](https://github.com/peterHoburg) made their first contribution in [#7670](https://github.com/pydantic/pydantic/pull/7670) + +## v2.4.1 (2023-09-26) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.1) + +### What's Changed + +#### Packaging + +* Update pydantic-core to 2.10.1 by [@davidhewitt](https://github.com/davidhewitt) in [#7633](https://github.com/pydantic/pydantic/pull/7633) + +#### Fixes + +* Serialize unsubstituted type vars as `Any` by [@adriangb](https://github.com/adriangb) in [#7606](https://github.com/pydantic/pydantic/pull/7606) +* Remove schema building caches by [@adriangb](https://github.com/adriangb) in [#7624](https://github.com/pydantic/pydantic/pull/7624) +* Fix an issue where JSON schema extras weren't JSON encoded by [@dmontagu](https://github.com/dmontagu) in [#7625](https://github.com/pydantic/pydantic/pull/7625) + +## v2.4.0 (2023-09-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.0) + +### What's Changed + +#### Packaging + +* Update pydantic-core to 2.10.0 by [@samuelcolvin](https://github.com/samuelcolvin) in [#7542](https://github.com/pydantic/pydantic/pull/7542) + +#### New Features + +* Add `Base64Url` types by [@dmontagu](https://github.com/dmontagu) in [#7286](https://github.com/pydantic/pydantic/pull/7286) +* Implement optional `number` to `str` coercion by [@lig](https://github.com/lig) in [#7508](https://github.com/pydantic/pydantic/pull/7508) +* Allow access to `field_name` and `data` in all validators if there is data and a field name by [@samuelcolvin](https://github.com/samuelcolvin) in [#7542](https://github.com/pydantic/pydantic/pull/7542) +* Add `BaseModel.model_validate_strings` and `TypeAdapter.validate_strings` by [@hramezani](https://github.com/hramezani) in [#7552](https://github.com/pydantic/pydantic/pull/7552) +* Add Pydantic `plugins` experimental implementation by [@lig](https://github.com/lig) [@samuelcolvin](https://github.com/samuelcolvin) and [@Kludex](https://github.com/Kludex) in [#6820](https://github.com/pydantic/pydantic/pull/6820) + +#### Changes + +* Do not override `model_post_init` in subclass with private attrs by [@Viicos](https://github.com/Viicos) in [#7302](https://github.com/pydantic/pydantic/pull/7302) +* Make fields with defaults not required in the serialization schema by default by [@dmontagu](https://github.com/dmontagu) in [#7275](https://github.com/pydantic/pydantic/pull/7275) +* Mark `Extra` as deprecated by [@disrupted](https://github.com/disrupted) in [#7299](https://github.com/pydantic/pydantic/pull/7299) +* Make `EncodedStr` a dataclass by [@Kludex](https://github.com/Kludex) in [#7396](https://github.com/pydantic/pydantic/pull/7396) +* Move `annotated_handlers` to be public by [@samuelcolvin](https://github.com/samuelcolvin) in [#7569](https://github.com/pydantic/pydantic/pull/7569) + +#### Performance + +* Simplify flattening and inlining of `CoreSchema` by [@adriangb](https://github.com/adriangb) in [#7523](https://github.com/pydantic/pydantic/pull/7523) +* Remove unused copies in `CoreSchema` walking by [@adriangb](https://github.com/adriangb) in [#7528](https://github.com/pydantic/pydantic/pull/7528) +* Add caches for collecting definitions and invalid schemas from a CoreSchema by [@adriangb](https://github.com/adriangb) in [#7527](https://github.com/pydantic/pydantic/pull/7527) +* Eagerly resolve discriminated unions and cache cases where we can't by [@adriangb](https://github.com/adriangb) in [#7529](https://github.com/pydantic/pydantic/pull/7529) +* Replace `dict.get` and `dict.setdefault` with more verbose versions in `CoreSchema` building hot paths by [@adriangb](https://github.com/adriangb) in [#7536](https://github.com/pydantic/pydantic/pull/7536) +* Cache invalid `CoreSchema` discovery by [@adriangb](https://github.com/adriangb) in [#7535](https://github.com/pydantic/pydantic/pull/7535) +* Allow disabling `CoreSchema` validation for faster startup times by [@adriangb](https://github.com/adriangb) in [#7565](https://github.com/pydantic/pydantic/pull/7565) + +#### Fixes + +* Fix config detection for `TypedDict` from grandparent classes by [@dmontagu](https://github.com/dmontagu) in [#7272](https://github.com/pydantic/pydantic/pull/7272) +* Fix hash function generation for frozen models with unusual MRO by [@dmontagu](https://github.com/dmontagu) in [#7274](https://github.com/pydantic/pydantic/pull/7274) +* Make `strict` config overridable in field for Path by [@hramezani](https://github.com/hramezani) in [#7281](https://github.com/pydantic/pydantic/pull/7281) +* Use `ser_json_` on default in `GenerateJsonSchema` by [@Kludex](https://github.com/Kludex) in [#7269](https://github.com/pydantic/pydantic/pull/7269) +* Adding a check that alias is validated as an identifier for Python by [@andree0](https://github.com/andree0) in [#7319](https://github.com/pydantic/pydantic/pull/7319) +* Raise an error when computed field overrides field by [@sydney-runkle](https://github.com/sydney-runkle) in [#7346](https://github.com/pydantic/pydantic/pull/7346) +* Fix applying `SkipValidation` to referenced schemas by [@adriangb](https://github.com/adriangb) in [#7381](https://github.com/pydantic/pydantic/pull/7381) +* Enforce behavior of private attributes having double leading underscore by [@lig](https://github.com/lig) in [#7265](https://github.com/pydantic/pydantic/pull/7265) +* Standardize `__get_pydantic_core_schema__` signature by [@hramezani](https://github.com/hramezani) in [#7415](https://github.com/pydantic/pydantic/pull/7415) +* Fix generic dataclass fields mutation bug (when using `TypeAdapter`) by [@sydney-runkle](https://github.com/sydney-runkle) in [#7435](https://github.com/pydantic/pydantic/pull/7435) +* Fix `TypeError` on `model_validator` in `wrap` mode by [@pmmmwh](https://github.com/pmmmwh) in [#7496](https://github.com/pydantic/pydantic/pull/7496) +* Improve enum error message by [@hramezani](https://github.com/hramezani) in [#7506](https://github.com/pydantic/pydantic/pull/7506) +* Make `repr` work for instances that failed initialization when handling `ValidationError`s by [@dmontagu](https://github.com/dmontagu) in [#7439](https://github.com/pydantic/pydantic/pull/7439) +* Fixed a regular expression denial of service issue by limiting whitespaces by [@prodigysml](https://github.com/prodigysml) in [#7360](https://github.com/pydantic/pydantic/pull/7360) +* Fix handling of `UUID` values having `UUID.version=None` by [@lig](https://github.com/lig) in [#7566](https://github.com/pydantic/pydantic/pull/7566) +* Fix `__iter__` returning private `cached_property` info by [@sydney-runkle](https://github.com/sydney-runkle) in [#7570](https://github.com/pydantic/pydantic/pull/7570) +* Improvements to version info message by [@samuelcolvin](https://github.com/samuelcolvin) in [#7594](https://github.com/pydantic/pydantic/pull/7594) + +### New Contributors +* [@15498th](https://github.com/15498th) made their first contribution in [#7238](https://github.com/pydantic/pydantic/pull/7238) +* [@GabrielCappelli](https://github.com/GabrielCappelli) made their first contribution in [#7213](https://github.com/pydantic/pydantic/pull/7213) +* [@tobni](https://github.com/tobni) made their first contribution in [#7184](https://github.com/pydantic/pydantic/pull/7184) +* [@redruin1](https://github.com/redruin1) made their first contribution in [#7282](https://github.com/pydantic/pydantic/pull/7282) +* [@FacerAin](https://github.com/FacerAin) made their first contribution in [#7288](https://github.com/pydantic/pydantic/pull/7288) +* [@acdha](https://github.com/acdha) made their first contribution in [#7297](https://github.com/pydantic/pydantic/pull/7297) +* [@andree0](https://github.com/andree0) made their first contribution in [#7319](https://github.com/pydantic/pydantic/pull/7319) +* [@gordonhart](https://github.com/gordonhart) made their first contribution in [#7375](https://github.com/pydantic/pydantic/pull/7375) +* [@pmmmwh](https://github.com/pmmmwh) made their first contribution in [#7496](https://github.com/pydantic/pydantic/pull/7496) +* [@disrupted](https://github.com/disrupted) made their first contribution in [#7299](https://github.com/pydantic/pydantic/pull/7299) +* [@prodigysml](https://github.com/prodigysml) made their first contribution in [#7360](https://github.com/pydantic/pydantic/pull/7360) + +## v2.3.0 (2023-08-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.3.0) + +* 🔥 Remove orphaned changes file from repo by [@lig](https://github.com/lig) in [#7168](https://github.com/pydantic/pydantic/pull/7168) +* Add copy button on documentation by [@Kludex](https://github.com/Kludex) in [#7190](https://github.com/pydantic/pydantic/pull/7190) +* Fix docs on JSON type by [@Kludex](https://github.com/Kludex) in [#7189](https://github.com/pydantic/pydantic/pull/7189) +* Update mypy 1.5.0 to 1.5.1 in CI by [@hramezani](https://github.com/hramezani) in [#7191](https://github.com/pydantic/pydantic/pull/7191) +* fix download links badge by [@samuelcolvin](https://github.com/samuelcolvin) in [#7200](https://github.com/pydantic/pydantic/pull/7200) +* add 2.2.1 to changelog by [@samuelcolvin](https://github.com/samuelcolvin) in [#7212](https://github.com/pydantic/pydantic/pull/7212) +* Make ModelWrapValidator protocols generic by [@dmontagu](https://github.com/dmontagu) in [#7154](https://github.com/pydantic/pydantic/pull/7154) +* Correct `Field(..., exclude: bool)` docs by [@samuelcolvin](https://github.com/samuelcolvin) in [#7214](https://github.com/pydantic/pydantic/pull/7214) +* Make shadowing attributes a warning instead of an error by [@adriangb](https://github.com/adriangb) in [#7193](https://github.com/pydantic/pydantic/pull/7193) +* Document `Base64Str` and `Base64Bytes` by [@Kludex](https://github.com/Kludex) in [#7192](https://github.com/pydantic/pydantic/pull/7192) +* Fix `config.defer_build` for serialization first cases by [@samuelcolvin](https://github.com/samuelcolvin) in [#7024](https://github.com/pydantic/pydantic/pull/7024) +* clean Model docstrings in JSON Schema by [@samuelcolvin](https://github.com/samuelcolvin) in [#7210](https://github.com/pydantic/pydantic/pull/7210) +* fix [#7228](https://github.com/pydantic/pydantic/pull/7228) (typo): docs in `validators.md` to correct `validate_default` kwarg by [@lmmx](https://github.com/lmmx) in [#7229](https://github.com/pydantic/pydantic/pull/7229) +* ✅ Implement `tzinfo.fromutc` method for `TzInfo` in `pydantic-core` by [@lig](https://github.com/lig) in [#7019](https://github.com/pydantic/pydantic/pull/7019) +* Support `__get_validators__` by [@hramezani](https://github.com/hramezani) in [#7197](https://github.com/pydantic/pydantic/pull/7197) + +## v2.2.1 (2023-08-18) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.2.1) + +* Make `xfail`ing test for root model extra stop `xfail`ing by [@dmontagu](https://github.com/dmontagu) in [#6937](https://github.com/pydantic/pydantic/pull/6937) +* Optimize recursion detection by stopping on the second visit for the same object by [@mciucu](https://github.com/mciucu) in [#7160](https://github.com/pydantic/pydantic/pull/7160) +* fix link in docs by [@tlambert03](https://github.com/tlambert03) in [#7166](https://github.com/pydantic/pydantic/pull/7166) +* Replace MiMalloc w/ default allocator by [@adriangb](https://github.com/adriangb) in [pydantic/pydantic-core#900](https://github.com/pydantic/pydantic-core/pull/900) +* Bump pydantic-core to 2.6.1 and prepare 2.2.1 release by [@adriangb](https://github.com/adriangb) in [#7176](https://github.com/pydantic/pydantic/pull/7176) + +## v2.2.0 (2023-08-17) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.2.0) + +* Split "pipx install" setup command into two commands on the documentation site by [@nomadmtb](https://github.com/nomadmtb) in [#6869](https://github.com/pydantic/pydantic/pull/6869) +* Deprecate `Field.include` by [@hramezani](https://github.com/hramezani) in [#6852](https://github.com/pydantic/pydantic/pull/6852) +* Fix typo in default factory error msg by [@hramezani](https://github.com/hramezani) in [#6880](https://github.com/pydantic/pydantic/pull/6880) +* Simplify handling of typing.Annotated in GenerateSchema by [@dmontagu](https://github.com/dmontagu) in [#6887](https://github.com/pydantic/pydantic/pull/6887) +* Re-enable fastapi tests in CI by [@dmontagu](https://github.com/dmontagu) in [#6883](https://github.com/pydantic/pydantic/pull/6883) +* Make it harder to hit collisions with json schema defrefs by [@dmontagu](https://github.com/dmontagu) in [#6566](https://github.com/pydantic/pydantic/pull/6566) +* Cleaner error for invalid input to `Path` fields by [@samuelcolvin](https://github.com/samuelcolvin) in [#6903](https://github.com/pydantic/pydantic/pull/6903) +* :memo: support Coordinate Type by [@yezz123](https://github.com/yezz123) in [#6906](https://github.com/pydantic/pydantic/pull/6906) +* Fix `ForwardRef` wrapper for py 3.10.0 (shim until bpo-45166) by [@randomir](https://github.com/randomir) in [#6919](https://github.com/pydantic/pydantic/pull/6919) +* Fix misbehavior related to copying of RootModel by [@dmontagu](https://github.com/dmontagu) in [#6918](https://github.com/pydantic/pydantic/pull/6918) +* Fix issue with recursion error caused by ParamSpec by [@dmontagu](https://github.com/dmontagu) in [#6923](https://github.com/pydantic/pydantic/pull/6923) +* Add section about Constrained classes to the Migration Guide by [@Kludex](https://github.com/Kludex) in [#6924](https://github.com/pydantic/pydantic/pull/6924) +* Use `main` branch for badge links by [@Viicos](https://github.com/Viicos) in [#6925](https://github.com/pydantic/pydantic/pull/6925) +* Add test for v1/v2 Annotated discrepancy by [@carlbordum](https://github.com/carlbordum) in [#6926](https://github.com/pydantic/pydantic/pull/6926) +* Make the v1 mypy plugin work with both v1 and v2 by [@dmontagu](https://github.com/dmontagu) in [#6921](https://github.com/pydantic/pydantic/pull/6921) +* Fix issue where generic models couldn't be parametrized with BaseModel by [@dmontagu](https://github.com/dmontagu) in [#6933](https://github.com/pydantic/pydantic/pull/6933) +* Remove xfail for discriminated union with alias by [@dmontagu](https://github.com/dmontagu) in [#6938](https://github.com/pydantic/pydantic/pull/6938) +* add field_serializer to computed_field by [@andresliszt](https://github.com/andresliszt) in [#6965](https://github.com/pydantic/pydantic/pull/6965) +* Use union_schema with Type[Union[...]] by [@JeanArhancet](https://github.com/JeanArhancet) in [#6952](https://github.com/pydantic/pydantic/pull/6952) +* Fix inherited typeddict attributes / config by [@adriangb](https://github.com/adriangb) in [#6981](https://github.com/pydantic/pydantic/pull/6981) +* fix dataclass annotated before validator called twice by [@davidhewitt](https://github.com/davidhewitt) in [#6998](https://github.com/pydantic/pydantic/pull/6998) +* Update test-fastapi deselected tests by [@hramezani](https://github.com/hramezani) in [#7014](https://github.com/pydantic/pydantic/pull/7014) +* Fix validator doc format by [@hramezani](https://github.com/hramezani) in [#7015](https://github.com/pydantic/pydantic/pull/7015) +* Fix typo in docstring of model_json_schema by [@AdamVinch-Federated](https://github.com/AdamVinch-Federated) in [#7032](https://github.com/pydantic/pydantic/pull/7032) +* remove unused "type ignores" with pyright by [@samuelcolvin](https://github.com/samuelcolvin) in [#7026](https://github.com/pydantic/pydantic/pull/7026) +* Add benchmark representing FastAPI startup time by [@adriangb](https://github.com/adriangb) in [#7030](https://github.com/pydantic/pydantic/pull/7030) +* Fix json_encoders for Enum subclasses by [@adriangb](https://github.com/adriangb) in [#7029](https://github.com/pydantic/pydantic/pull/7029) +* Update docstring of `ser_json_bytes` regarding base64 encoding by [@Viicos](https://github.com/Viicos) in [#7052](https://github.com/pydantic/pydantic/pull/7052) +* Allow `@validate_call` to work on async methods by [@adriangb](https://github.com/adriangb) in [#7046](https://github.com/pydantic/pydantic/pull/7046) +* Fix: mypy error with `Settings` and `SettingsConfigDict` by [@JeanArhancet](https://github.com/JeanArhancet) in [#7002](https://github.com/pydantic/pydantic/pull/7002) +* Fix some typos (repeated words and it's/its) by [@eumiro](https://github.com/eumiro) in [#7063](https://github.com/pydantic/pydantic/pull/7063) +* Fix the typo in docstring by [@harunyasar](https://github.com/harunyasar) in [#7062](https://github.com/pydantic/pydantic/pull/7062) +* Docs: Fix broken URL in the pydantic-settings package recommendation by [@swetjen](https://github.com/swetjen) in [#6995](https://github.com/pydantic/pydantic/pull/6995) +* Handle constraints being applied to schemas that don't accept it by [@adriangb](https://github.com/adriangb) in [#6951](https://github.com/pydantic/pydantic/pull/6951) +* Replace almost_equal_floats with math.isclose by [@eumiro](https://github.com/eumiro) in [#7082](https://github.com/pydantic/pydantic/pull/7082) +* bump pydantic-core to 2.5.0 by [@davidhewitt](https://github.com/davidhewitt) in [#7077](https://github.com/pydantic/pydantic/pull/7077) +* Add `short_version` and use it in links by [@hramezani](https://github.com/hramezani) in [#7115](https://github.com/pydantic/pydantic/pull/7115) +* 📝 Add usage link to `RootModel` by [@Kludex](https://github.com/Kludex) in [#7113](https://github.com/pydantic/pydantic/pull/7113) +* Revert "Fix default port for mongosrv DSNs (#6827)" by [@Kludex](https://github.com/Kludex) in [#7116](https://github.com/pydantic/pydantic/pull/7116) +* Clarify validate_default and _Unset handling in usage docs and migration guide by [@benbenbang](https://github.com/benbenbang) in [#6950](https://github.com/pydantic/pydantic/pull/6950) +* Tweak documentation of `Field.exclude` by [@Viicos](https://github.com/Viicos) in [#7086](https://github.com/pydantic/pydantic/pull/7086) +* Do not require `validate_assignment` to use `Field.frozen` by [@Viicos](https://github.com/Viicos) in [#7103](https://github.com/pydantic/pydantic/pull/7103) +* tweaks to `_core_utils` by [@samuelcolvin](https://github.com/samuelcolvin) in [#7040](https://github.com/pydantic/pydantic/pull/7040) +* Make DefaultDict working with set by [@hramezani](https://github.com/hramezani) in [#7126](https://github.com/pydantic/pydantic/pull/7126) +* Don't always require typing.Generic as a base for partially parametrized models by [@dmontagu](https://github.com/dmontagu) in [#7119](https://github.com/pydantic/pydantic/pull/7119) +* Fix issue with JSON schema incorrectly using parent class core schema by [@dmontagu](https://github.com/dmontagu) in [#7020](https://github.com/pydantic/pydantic/pull/7020) +* Fix xfailed test related to TypedDict and alias_generator by [@dmontagu](https://github.com/dmontagu) in [#6940](https://github.com/pydantic/pydantic/pull/6940) +* Improve error message for NameEmail by [@dmontagu](https://github.com/dmontagu) in [#6939](https://github.com/pydantic/pydantic/pull/6939) +* Fix generic computed fields by [@dmontagu](https://github.com/dmontagu) in [#6988](https://github.com/pydantic/pydantic/pull/6988) +* Reflect namedtuple default values during validation by [@dmontagu](https://github.com/dmontagu) in [#7144](https://github.com/pydantic/pydantic/pull/7144) +* Update dependencies, fix pydantic-core usage, fix CI issues by [@dmontagu](https://github.com/dmontagu) in [#7150](https://github.com/pydantic/pydantic/pull/7150) +* Add mypy 1.5.0 by [@hramezani](https://github.com/hramezani) in [#7118](https://github.com/pydantic/pydantic/pull/7118) +* Handle non-json native enum values by [@adriangb](https://github.com/adriangb) in [#7056](https://github.com/pydantic/pydantic/pull/7056) +* document `round_trip` in Json type documentation by [@jc-louis](https://github.com/jc-louis) in [#7137](https://github.com/pydantic/pydantic/pull/7137) +* Relax signature checks to better support builtins and C extension functions as validators by [@adriangb](https://github.com/adriangb) in [#7101](https://github.com/pydantic/pydantic/pull/7101) +* add union_mode='left_to_right' by [@davidhewitt](https://github.com/davidhewitt) in [#7151](https://github.com/pydantic/pydantic/pull/7151) +* Include an error message hint for inherited ordering by [@yvalencia91](https://github.com/yvalencia91) in [#7124](https://github.com/pydantic/pydantic/pull/7124) +* Fix one docs link and resolve some warnings for two others by [@dmontagu](https://github.com/dmontagu) in [#7153](https://github.com/pydantic/pydantic/pull/7153) +* Include Field extra keys name in warning by [@hramezani](https://github.com/hramezani) in [#7136](https://github.com/pydantic/pydantic/pull/7136) + +## v2.1.1 (2023-07-25) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.1.1) + +* Skip FieldInfo merging when unnecessary by [@dmontagu](https://github.com/dmontagu) in [#6862](https://github.com/pydantic/pydantic/pull/6862) + +## v2.1.0 (2023-07-25) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.1.0) + +* Add `StringConstraints` for use as Annotated metadata by [@adriangb](https://github.com/adriangb) in [#6605](https://github.com/pydantic/pydantic/pull/6605) +* Try to fix intermittently failing CI by [@adriangb](https://github.com/adriangb) in [#6683](https://github.com/pydantic/pydantic/pull/6683) +* Remove redundant example of optional vs default. by [@ehiggs-deliverect](https://github.com/ehiggs-deliverect) in [#6676](https://github.com/pydantic/pydantic/pull/6676) +* Docs update by [@samuelcolvin](https://github.com/samuelcolvin) in [#6692](https://github.com/pydantic/pydantic/pull/6692) +* Remove the Validate always section in validator docs by [@adriangb](https://github.com/adriangb) in [#6679](https://github.com/pydantic/pydantic/pull/6679) +* Fix recursion error in json schema generation by [@adriangb](https://github.com/adriangb) in [#6720](https://github.com/pydantic/pydantic/pull/6720) +* Fix incorrect subclass check for secretstr by [@AlexVndnblcke](https://github.com/AlexVndnblcke) in [#6730](https://github.com/pydantic/pydantic/pull/6730) +* update pdm / pdm lockfile to 2.8.0 by [@davidhewitt](https://github.com/davidhewitt) in [#6714](https://github.com/pydantic/pydantic/pull/6714) +* unpin pdm on more CI jobs by [@davidhewitt](https://github.com/davidhewitt) in [#6755](https://github.com/pydantic/pydantic/pull/6755) +* improve source locations for auxiliary packages in docs by [@davidhewitt](https://github.com/davidhewitt) in [#6749](https://github.com/pydantic/pydantic/pull/6749) +* Assume builtins don't accept an info argument by [@adriangb](https://github.com/adriangb) in [#6754](https://github.com/pydantic/pydantic/pull/6754) +* Fix bug where calling `help(BaseModelSubclass)` raises errors by [@hramezani](https://github.com/hramezani) in [#6758](https://github.com/pydantic/pydantic/pull/6758) +* Fix mypy plugin handling of `@model_validator(mode="after")` by [@ljodal](https://github.com/ljodal) in [#6753](https://github.com/pydantic/pydantic/pull/6753) +* update pydantic-core to 2.3.1 by [@davidhewitt](https://github.com/davidhewitt) in [#6756](https://github.com/pydantic/pydantic/pull/6756) +* Mypy plugin for settings by [@hramezani](https://github.com/hramezani) in [#6760](https://github.com/pydantic/pydantic/pull/6760) +* Use `contentSchema` keyword for JSON schema by [@dmontagu](https://github.com/dmontagu) in [#6715](https://github.com/pydantic/pydantic/pull/6715) +* fast-path checking finite decimals by [@davidhewitt](https://github.com/davidhewitt) in [#6769](https://github.com/pydantic/pydantic/pull/6769) +* Docs update by [@samuelcolvin](https://github.com/samuelcolvin) in [#6771](https://github.com/pydantic/pydantic/pull/6771) +* Improve json schema doc by [@hramezani](https://github.com/hramezani) in [#6772](https://github.com/pydantic/pydantic/pull/6772) +* Update validator docs by [@adriangb](https://github.com/adriangb) in [#6695](https://github.com/pydantic/pydantic/pull/6695) +* Fix typehint for wrap validator by [@dmontagu](https://github.com/dmontagu) in [#6788](https://github.com/pydantic/pydantic/pull/6788) +* 🐛 Fix validation warning for unions of Literal and other type by [@lig](https://github.com/lig) in [#6628](https://github.com/pydantic/pydantic/pull/6628) +* Update documentation for generics support in V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6685](https://github.com/pydantic/pydantic/pull/6685) +* add pydantic-core build info to `version_info()` by [@samuelcolvin](https://github.com/samuelcolvin) in [#6785](https://github.com/pydantic/pydantic/pull/6785) +* Fix pydantic dataclasses that use slots with default values by [@dmontagu](https://github.com/dmontagu) in [#6796](https://github.com/pydantic/pydantic/pull/6796) +* Fix inheritance of hash function for frozen models by [@dmontagu](https://github.com/dmontagu) in [#6789](https://github.com/pydantic/pydantic/pull/6789) +* ✨ Add `SkipJsonSchema` annotation by [@Kludex](https://github.com/Kludex) in [#6653](https://github.com/pydantic/pydantic/pull/6653) +* Error if an invalid field name is used with Field by [@dmontagu](https://github.com/dmontagu) in [#6797](https://github.com/pydantic/pydantic/pull/6797) +* Add `GenericModel` to `MOVED_IN_V2` by [@adriangb](https://github.com/adriangb) in [#6776](https://github.com/pydantic/pydantic/pull/6776) +* Remove unused code from `docs/usage/types/custom.md` by [@hramezani](https://github.com/hramezani) in [#6803](https://github.com/pydantic/pydantic/pull/6803) +* Fix `float` -> `Decimal` coercion precision loss by [@adriangb](https://github.com/adriangb) in [#6810](https://github.com/pydantic/pydantic/pull/6810) +* remove email validation from the north star benchmark by [@davidhewitt](https://github.com/davidhewitt) in [#6816](https://github.com/pydantic/pydantic/pull/6816) +* Fix link to mypy by [@progsmile](https://github.com/progsmile) in [#6824](https://github.com/pydantic/pydantic/pull/6824) +* Improve initialization hooks example by [@hramezani](https://github.com/hramezani) in [#6822](https://github.com/pydantic/pydantic/pull/6822) +* Fix default port for mongosrv DSNs by [@dmontagu](https://github.com/dmontagu) in [#6827](https://github.com/pydantic/pydantic/pull/6827) +* Improve API documentation, in particular more links between usage and API docs by [@samuelcolvin](https://github.com/samuelcolvin) in [#6780](https://github.com/pydantic/pydantic/pull/6780) +* update pydantic-core to 2.4.0 by [@davidhewitt](https://github.com/davidhewitt) in [#6831](https://github.com/pydantic/pydantic/pull/6831) +* Fix `annotated_types.MaxLen` validator for custom sequence types by [@ImogenBits](https://github.com/ImogenBits) in [#6809](https://github.com/pydantic/pydantic/pull/6809) +* Update V1 by [@hramezani](https://github.com/hramezani) in [#6833](https://github.com/pydantic/pydantic/pull/6833) +* Make it so callable JSON schema extra works by [@dmontagu](https://github.com/dmontagu) in [#6798](https://github.com/pydantic/pydantic/pull/6798) +* Fix serialization issue with `InstanceOf` by [@dmontagu](https://github.com/dmontagu) in [#6829](https://github.com/pydantic/pydantic/pull/6829) +* Add back support for `json_encoders` by [@adriangb](https://github.com/adriangb) in [#6811](https://github.com/pydantic/pydantic/pull/6811) +* Update field annotations when building the schema by [@dmontagu](https://github.com/dmontagu) in [#6838](https://github.com/pydantic/pydantic/pull/6838) +* Use `WeakValueDictionary` to fix generic memory leak by [@dmontagu](https://github.com/dmontagu) in [#6681](https://github.com/pydantic/pydantic/pull/6681) +* Add `config.defer_build` to optionally make model building lazy by [@samuelcolvin](https://github.com/samuelcolvin) in [#6823](https://github.com/pydantic/pydantic/pull/6823) +* delegate `UUID` serialization to pydantic-core by [@davidhewitt](https://github.com/davidhewitt) in [#6850](https://github.com/pydantic/pydantic/pull/6850) +* Update `json_encoders` docs by [@adriangb](https://github.com/adriangb) in [#6848](https://github.com/pydantic/pydantic/pull/6848) +* Fix error message for `staticmethod`/`classmethod` order with validate_call by [@dmontagu](https://github.com/dmontagu) in [#6686](https://github.com/pydantic/pydantic/pull/6686) +* Improve documentation for `Config` by [@samuelcolvin](https://github.com/samuelcolvin) in [#6847](https://github.com/pydantic/pydantic/pull/6847) +* Update serialization doc to mention `Field.exclude` takes priority over call-time `include/exclude` by [@hramezani](https://github.com/hramezani) in [#6851](https://github.com/pydantic/pydantic/pull/6851) +* Allow customizing core schema generation by making `GenerateSchema` public by [@adriangb](https://github.com/adriangb) in [#6737](https://github.com/pydantic/pydantic/pull/6737) + +## v2.0.3 (2023-07-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.3) + +* Mention PyObject (v1) moving to ImportString (v2) in migration doc by [@slafs](https://github.com/slafs) in [#6456](https://github.com/pydantic/pydantic/pull/6456) +* Fix release-tweet CI by [@Kludex](https://github.com/Kludex) in [#6461](https://github.com/pydantic/pydantic/pull/6461) +* Revise the section on required / optional / nullable fields. by [@ybressler](https://github.com/ybressler) in [#6468](https://github.com/pydantic/pydantic/pull/6468) +* Warn if a type hint is not in fact a type by [@adriangb](https://github.com/adriangb) in [#6479](https://github.com/pydantic/pydantic/pull/6479) +* Replace TransformSchema with GetPydanticSchema by [@dmontagu](https://github.com/dmontagu) in [#6484](https://github.com/pydantic/pydantic/pull/6484) +* Fix the un-hashability of various annotation types, for use in caching generic containers by [@dmontagu](https://github.com/dmontagu) in [#6480](https://github.com/pydantic/pydantic/pull/6480) +* PYD-164: Rework custom types docs by [@adriangb](https://github.com/adriangb) in [#6490](https://github.com/pydantic/pydantic/pull/6490) +* Fix ci by [@adriangb](https://github.com/adriangb) in [#6507](https://github.com/pydantic/pydantic/pull/6507) +* Fix forward ref in generic by [@adriangb](https://github.com/adriangb) in [#6511](https://github.com/pydantic/pydantic/pull/6511) +* Fix generation of serialization JSON schemas for core_schema.ChainSchema by [@dmontagu](https://github.com/dmontagu) in [#6515](https://github.com/pydantic/pydantic/pull/6515) +* Document the change in `Field.alias` behavior in Pydantic V2 by [@hramezani](https://github.com/hramezani) in [#6508](https://github.com/pydantic/pydantic/pull/6508) +* Give better error message attempting to compute the json schema of a model with undefined fields by [@dmontagu](https://github.com/dmontagu) in [#6519](https://github.com/pydantic/pydantic/pull/6519) +* Document `alias_priority` by [@tpdorsey](https://github.com/tpdorsey) in [#6520](https://github.com/pydantic/pydantic/pull/6520) +* Add redirect for types documentation by [@tpdorsey](https://github.com/tpdorsey) in [#6513](https://github.com/pydantic/pydantic/pull/6513) +* Allow updating docs without release by [@samuelcolvin](https://github.com/samuelcolvin) in [#6551](https://github.com/pydantic/pydantic/pull/6551) +* Ensure docs tests always run in the right folder by [@dmontagu](https://github.com/dmontagu) in [#6487](https://github.com/pydantic/pydantic/pull/6487) +* Defer evaluation of return type hints for serializer functions by [@dmontagu](https://github.com/dmontagu) in [#6516](https://github.com/pydantic/pydantic/pull/6516) +* Disable E501 from Ruff and rely on just Black by [@adriangb](https://github.com/adriangb) in [#6552](https://github.com/pydantic/pydantic/pull/6552) +* Update JSON Schema documentation for V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6492](https://github.com/pydantic/pydantic/pull/6492) +* Add documentation of cyclic reference handling by [@dmontagu](https://github.com/dmontagu) in [#6493](https://github.com/pydantic/pydantic/pull/6493) +* Remove the need for change files by [@samuelcolvin](https://github.com/samuelcolvin) in [#6556](https://github.com/pydantic/pydantic/pull/6556) +* add "north star" benchmark by [@davidhewitt](https://github.com/davidhewitt) in [#6547](https://github.com/pydantic/pydantic/pull/6547) +* Update Dataclasses docs by [@tpdorsey](https://github.com/tpdorsey) in [#6470](https://github.com/pydantic/pydantic/pull/6470) +* ♻️ Use different error message on v1 redirects by [@Kludex](https://github.com/Kludex) in [#6595](https://github.com/pydantic/pydantic/pull/6595) +* ⬆ Upgrade `pydantic-core` to v2.2.0 by [@lig](https://github.com/lig) in [#6589](https://github.com/pydantic/pydantic/pull/6589) +* Fix serialization for IPvAny by [@dmontagu](https://github.com/dmontagu) in [#6572](https://github.com/pydantic/pydantic/pull/6572) +* Improve CI by using PDM instead of pip to install typing-extensions by [@adriangb](https://github.com/adriangb) in [#6602](https://github.com/pydantic/pydantic/pull/6602) +* Add `enum` error type docs by [@lig](https://github.com/lig) in [#6603](https://github.com/pydantic/pydantic/pull/6603) +* 🐛 Fix `max_length` for unicode strings by [@lig](https://github.com/lig) in [#6559](https://github.com/pydantic/pydantic/pull/6559) +* Add documentation for accessing features via `pydantic.v1` by [@tpdorsey](https://github.com/tpdorsey) in [#6604](https://github.com/pydantic/pydantic/pull/6604) +* Include extra when iterating over a model by [@adriangb](https://github.com/adriangb) in [#6562](https://github.com/pydantic/pydantic/pull/6562) +* Fix typing of model_validator by [@adriangb](https://github.com/adriangb) in [#6514](https://github.com/pydantic/pydantic/pull/6514) +* Touch up Decimal validator by [@adriangb](https://github.com/adriangb) in [#6327](https://github.com/pydantic/pydantic/pull/6327) +* Fix various docstrings using fixed pytest-examples by [@dmontagu](https://github.com/dmontagu) in [#6607](https://github.com/pydantic/pydantic/pull/6607) +* Handle function validators in a discriminated union by [@dmontagu](https://github.com/dmontagu) in [#6570](https://github.com/pydantic/pydantic/pull/6570) +* Review json_schema.md by [@tpdorsey](https://github.com/tpdorsey) in [#6608](https://github.com/pydantic/pydantic/pull/6608) +* Make validate_call work on basemodel methods by [@dmontagu](https://github.com/dmontagu) in [#6569](https://github.com/pydantic/pydantic/pull/6569) +* add test for big int json serde by [@davidhewitt](https://github.com/davidhewitt) in [#6614](https://github.com/pydantic/pydantic/pull/6614) +* Fix pydantic dataclass problem with dataclasses.field default_factory by [@hramezani](https://github.com/hramezani) in [#6616](https://github.com/pydantic/pydantic/pull/6616) +* Fixed mypy type inference for TypeAdapter by [@zakstucke](https://github.com/zakstucke) in [#6617](https://github.com/pydantic/pydantic/pull/6617) +* Make it work to use None as a generic parameter by [@dmontagu](https://github.com/dmontagu) in [#6609](https://github.com/pydantic/pydantic/pull/6609) +* Make it work to use `$ref` as an alias by [@dmontagu](https://github.com/dmontagu) in [#6568](https://github.com/pydantic/pydantic/pull/6568) +* add note to migration guide about changes to `AnyUrl` etc by [@davidhewitt](https://github.com/davidhewitt) in [#6618](https://github.com/pydantic/pydantic/pull/6618) +* 🐛 Support defining `json_schema_extra` on `RootModel` using `Field` by [@lig](https://github.com/lig) in [#6622](https://github.com/pydantic/pydantic/pull/6622) +* Update pre-commit to prevent commits to main branch on accident by [@dmontagu](https://github.com/dmontagu) in [#6636](https://github.com/pydantic/pydantic/pull/6636) +* Fix PDM CI for python 3.7 on MacOS/windows by [@dmontagu](https://github.com/dmontagu) in [#6627](https://github.com/pydantic/pydantic/pull/6627) +* Produce more accurate signatures for pydantic dataclasses by [@dmontagu](https://github.com/dmontagu) in [#6633](https://github.com/pydantic/pydantic/pull/6633) +* Updates to Url types for Pydantic V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6638](https://github.com/pydantic/pydantic/pull/6638) +* Fix list markdown in `transform` docstring by [@StefanBRas](https://github.com/StefanBRas) in [#6649](https://github.com/pydantic/pydantic/pull/6649) +* simplify slots_dataclass construction to appease mypy by [@davidhewitt](https://github.com/davidhewitt) in [#6639](https://github.com/pydantic/pydantic/pull/6639) +* Update TypedDict schema generation docstring by [@adriangb](https://github.com/adriangb) in [#6651](https://github.com/pydantic/pydantic/pull/6651) +* Detect and lint-error for prints by [@dmontagu](https://github.com/dmontagu) in [#6655](https://github.com/pydantic/pydantic/pull/6655) +* Add xfailing test for pydantic-core PR 766 by [@dmontagu](https://github.com/dmontagu) in [#6641](https://github.com/pydantic/pydantic/pull/6641) +* Ignore unrecognized fields from dataclasses metadata by [@dmontagu](https://github.com/dmontagu) in [#6634](https://github.com/pydantic/pydantic/pull/6634) +* Make non-existent class getattr a mypy error by [@dmontagu](https://github.com/dmontagu) in [#6658](https://github.com/pydantic/pydantic/pull/6658) +* Update pydantic-core to 2.3.0 by [@hramezani](https://github.com/hramezani) in [#6648](https://github.com/pydantic/pydantic/pull/6648) +* Use OrderedDict from typing_extensions by [@dmontagu](https://github.com/dmontagu) in [#6664](https://github.com/pydantic/pydantic/pull/6664) +* Fix typehint for JSON schema extra callable by [@dmontagu](https://github.com/dmontagu) in [#6659](https://github.com/pydantic/pydantic/pull/6659) + +## v2.0.2 (2023-07-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.2) + +* Fix bug where round-trip pickling/unpickling a `RootModel` would change the value of `__dict__`, [#6457](https://github.com/pydantic/pydantic/pull/6457) by [@dmontagu](https://github.com/dmontagu) +* Allow single-item discriminated unions, [#6405](https://github.com/pydantic/pydantic/pull/6405) by [@dmontagu](https://github.com/dmontagu) +* Fix issue with union parsing of enums, [#6440](https://github.com/pydantic/pydantic/pull/6440) by [@dmontagu](https://github.com/dmontagu) +* Docs: Fixed `constr` documentation, renamed old `regex` to new `pattern`, [#6452](https://github.com/pydantic/pydantic/pull/6452) by [@miili](https://github.com/miili) +* Change `GenerateJsonSchema.generate_definitions` signature, [#6436](https://github.com/pydantic/pydantic/pull/6436) by [@dmontagu](https://github.com/dmontagu) + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0.2) + +## v2.0.1 (2023-07-04) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.1) + +First patch release of Pydantic V2 + +* Extra fields added via `setattr` (i.e. `m.some_extra_field = 'extra_value'`) + are added to `.model_extra` if `model_config` `extra='allowed'`. Fixed [#6333](https://github.com/pydantic/pydantic/pull/6333), [#6365](https://github.com/pydantic/pydantic/pull/6365) by [@aaraney](https://github.com/aaraney) +* Automatically unpack JSON schema '$ref' for custom types, [#6343](https://github.com/pydantic/pydantic/pull/6343) by [@adriangb](https://github.com/adriangb) +* Fix tagged unions multiple processing in submodels, [#6340](https://github.com/pydantic/pydantic/pull/6340) by [@suharnikov](https://github.com/suharnikov) + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0.1) + +## v2.0 (2023-06-30) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0) + +Pydantic V2 is here! :tada: + +See [this post](https://docs.pydantic.dev/2.0/blog/pydantic-v2-final/) for more details. + +## v2.0b3 (2023-06-16) + +Third beta pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b3) + +## v2.0b2 (2023-06-03) + +Add `from_attributes` runtime flag to `TypeAdapter.validate_python` and `BaseModel.model_validate`. + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b2) + +## v2.0b1 (2023-06-01) + +First beta pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b1) + +## v2.0a4 (2023-05-05) + +Fourth pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a4) + +## v2.0a3 (2023-04-20) + +Third pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a3) + +## v2.0a2 (2023-04-12) + +Second pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a2) + +## v2.0a1 (2023-04-03) + +First pre-release of Pydantic V2! + +See [this post](https://docs.pydantic.dev/blog/pydantic-v2-alpha/) for more details. + + +... see [here](https://docs.pydantic.dev/changelog/#v0322-2019-08-17) for earlier changes. diff --git a/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766 b/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766 new file mode 100644 index 0000000..edead78 Binary files /dev/null and b/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766 differ diff --git a/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766.body b/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766.body new file mode 100644 index 0000000..fc96452 --- /dev/null +++ b/.cache/pip/http-v2/f/b/e/3/a/fbe3a5eb3735a745707ab3bf39e17d76ea29c7cd1eaa7763f11e1766.body @@ -0,0 +1,128 @@ +Metadata-Version: 2.1 +Name: aiosignal +Version: 1.3.1 +Summary: aiosignal: a list of registered asynchronous callbacks +Home-page: https://github.com/aio-libs/aiosignal +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2.0 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiosignal/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiosignal +Project-URL: Docs: RTD, https://docs.aiosignal.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiosignal/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiosignal +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Framework :: AsyncIO +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: frozenlist (>=1.1.0) + +========= +aiosignal +========= + +.. image:: https://github.com/aio-libs/aiosignal/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiosignal + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiosignal.svg + :target: https://pypi.org/project/aiosignal + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiosignal/badge/?version=latest + :target: https://aiosignal.readthedocs.io/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/discourse/topics?server=https%3A%2F%2Faio-libs.discourse.group%2F + :target: https://aio-libs.discourse.group/ + :alt: Discourse group for io-libs + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +Introduction +============ + +A project to manage callbacks in `asyncio` projects. + +``Signal`` is a list of registered asynchronous callbacks. + +The signal's life-cycle has two stages: after creation its content +could be filled by using standard list operations: ``sig.append()`` +etc. + +After you call ``sig.freeze()`` the signal is *frozen*: adding, removing +and dropping callbacks is forbidden. + +The only available operation is calling the previously registered +callbacks by using ``await sig.send(data)``. + +For concrete usage examples see the `Signals + +section of the `Web Server Advanced +` chapter of the `aiohttp +documentation`_. + + +Installation +------------ + +:: + + $ pip install aiosignal + +The library requires Python 3.6 or newer. + + +Documentation +============= + +https://aiosignal.readthedocs.io/ + +Communication channels +====================== + +*gitter chat* https://gitter.im/aio-libs/Lobby + +Requirements +============ + +- Python >= 3.6 +- frozenlist >= 1.0.0 + +License +======= + +``aiosignal`` is offered under the Apache 2 license. + +Source code +=========== + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/aiosignal +.. _aiohttp documentation: https://docs.aiohttp.org/ diff --git a/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213 b/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213 new file mode 100644 index 0000000..3321d8a Binary files /dev/null and b/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213 differ diff --git a/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213.body b/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213.body new file mode 100644 index 0000000..60603d5 --- /dev/null +++ b/.cache/pip/http-v2/f/c/a/5/b/fca5b18fd877930924e4c3ed44a7de4862861d131c1248697b107213.body @@ -0,0 +1,420 @@ +Metadata-Version: 2.1 +Name: frozenlist +Version: 1.4.1 +Summary: A list-like structure which implements collections.abc.MutableSequence +Home-page: https://github.com/aio-libs/frozenlist +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: Github Actions, https://github.com/aio-libs/frozenlist/actions +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/frozenlist +Project-URL: Docs: Changelog, https://github.com/aio-libs/frozenlist/blob/master/CHANGES.rst#changelog +Project-URL: Docs: RTD, https://frozenlist.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/frozenlist/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/frozenlist +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE + +frozenlist +========== + +.. image:: https://github.com/aio-libs/frozenlist/workflows/CI/badge.svg + :target: https://github.com/aio-libs/frozenlist/actions + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/frozenlist/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/frozenlist + :alt: codecov.io status for master branch + +.. image:: https://img.shields.io/pypi/v/frozenlist.svg?logo=Python&logoColor=white + :target: https://pypi.org/project/frozenlist + :alt: frozenlist @ PyPI + +.. image:: https://readthedocs.org/projects/frozenlist/badge/?version=latest + :target: https://frozenlist.aio-libs.org + :alt: Read The Docs build status badge + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +``frozenlist.FrozenList`` is a list-like structure which implements +``collections.abc.MutableSequence``. The list is *mutable* until ``FrozenList.freeze`` +is called, after which list modifications raise ``RuntimeError``: + + +>>> from frozenlist import FrozenList +>>> fl = FrozenList([17, 42]) +>>> fl.append('spam') +>>> fl.append('Vikings') +>>> fl + +>>> fl.freeze() +>>> fl + +>>> fl.frozen +True +>>> fl.append("Monty") +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 97, in frozenlist._frozenlist.FrozenList.append + self._check_frozen() + File "frozenlist/_frozenlist.pyx", line 19, in frozenlist._frozenlist.FrozenList._check_frozen + raise RuntimeError("Cannot modify frozen list.") +RuntimeError: Cannot modify frozen list. + + +FrozenList is also hashable, but only when frozen. Otherwise it also throws a RuntimeError: + + +>>> fl = FrozenList([17, 42, 'spam']) +>>> hash(fl) +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 111, in frozenlist._frozenlist.FrozenList.__hash__ + raise RuntimeError("Cannot hash unfrozen list.") +RuntimeError: Cannot hash unfrozen list. +>>> fl.freeze() +>>> hash(fl) +3713081631934410656 +>>> dictionary = {fl: 'Vikings'} # frozen fl can be a dict key +>>> dictionary +{: 'Vikings'} + + +Installation +------------ + +:: + + $ pip install frozenlist + +The library requires Python 3.8 or newer. + + +Documentation +------------- + +https://frozenlist.aio-libs.org + +Communication channels +---------------------- + +We have a *Matrix Space* `#aio-libs-space:matrix.org +`_ which is +also accessible via Gitter. + +Requirements +------------ + +- Python >= 3.8 + +License +------- + +``frozenlist`` is offered under the Apache 2 license. + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/frozenlist + +========= +Changelog +========= + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/contributing/#news-entries + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +1.4.1 (2023-12-15) +================== + +Packaging updates and notes for downstreams +------------------------------------------- + +- Declared Python 3.12 and PyPy 3.8-3.10 supported officially + in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#553 `__. + +- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``frozenlist`` + from source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python3 -m pip install . --config-settings=pure-python= + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python3 -m build --config-setting=pure-python= + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + + +Contributor-facing changes +-------------------------- + +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python3 -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. + + The following produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files: + + .. code-block:: console + + $ python -Im pip install -e . + + Alternatively, the ``FROZENLIST_CYTHON_TRACING=1`` environment variable + can be set to do the same as the `PEP 517 `__ config setting. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + + *Related issues and pull requests on GitHub:* + `#561 `__. + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *frozenlist* -- by `@webknjaz `__. + + This is primarily targeting the maintainers. + + + *Related issues and pull requests on GitHub:* + `#563 `__. + +- Detailed ``Contributing Guidelines`` on + authoring the changelog fragments have been published in the + documentation -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#564 `__. + + +---- + + +1.4.0 (2023-07-12) +================== + +The published source distribution package became buildable +under Python 3.12. + + +---- + + +Bugfixes +-------- + +- Removed an unused ``typing.Tuple`` import + `#411 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.7 support. + `#413 `_ + + +Misc +---- + +- `#410 `_, `#433 `_ + + +---- + + +1.3.3 (2022-11-08) +================== + +- Fixed CI runs when creating a new release, where new towncrier versions + fail when the current version section is already present. + + +---- + + +1.3.2 (2022-11-08) +================== + +Misc +---- + +- Updated the CI runs to better check for test results and to avoid deprecated syntax. `#327 `_ + + +---- + + +1.3.1 (2022-08-02) +================== + +The published source distribution package became buildable +under Python 3.11. + + +---- + + +1.3.0 (2022-01-18) +================== + +Bugfixes +-------- + +- Do not install C sources with binary distributions. + `#250 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support + `#274 `_ + + +---- + + +1.2.0 (2021-10-16) +================== + +Features +-------- + +- ``FrozenList`` now supports being used as a generic type as per PEP 585, e.g. ``frozen_int_list: FrozenList[int]`` (requires Python 3.9 or newer). + `#172 `_ +- Added support for Python 3.10. + `#227 `_ +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. + `#227 `_ +- Started shipping platform-specific arm64 wheels for Apple Silicon. + `#227 `_ + + +---- + + +1.1.1 (2020-11-14) +================== + +Bugfixes +-------- + +- Provide x86 Windows wheels. + `#169 `_ + + +---- + + +1.1.0 (2020-10-13) +================== + +Features +-------- + +- Add support for hashing of a frozen list. + `#136 `_ + +- Support Python 3.8 and 3.9. + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + + +---- + + +1.0.0 (2019-11-09) +================== + +Deprecations and Removals +------------------------- + +- Dropped support for Python 3.5; only 3.6, 3.7 and 3.8 are supported going forward. + `#24 `_ diff --git a/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8 b/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8 new file mode 100644 index 0000000..e2aaac9 Binary files /dev/null and b/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8 differ diff --git a/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8.body b/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8.body new file mode 100644 index 0000000..8b66b3f Binary files /dev/null and b/.cache/pip/http-v2/f/d/8/4/c/fd84c46b9ca35d1b3dffcde8cf808cf28a8ce0d20a74e8bc1c4b59f8.body differ diff --git a/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928 b/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928 new file mode 100644 index 0000000..901cf23 Binary files /dev/null and b/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928 differ diff --git a/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928.body b/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928.body new file mode 100644 index 0000000..11021aa Binary files /dev/null and b/.cache/pip/http-v2/f/f/5/7/0/ff57044fbdd8366a0aafc1423058140ac7d29cfda72f3b6a82557928.body differ diff --git a/.cache/pypoetry/artifacts/25/28/f8/e3d6cf13e82883cf499c0b90a601def2252eec921092483d9c29263203/protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/25/28/f8/e3d6cf13e82883cf499c0b90a601def2252eec921092483d9c29263203/protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl new file mode 100644 index 0000000..d6e23ae Binary files /dev/null and b/.cache/pypoetry/artifacts/25/28/f8/e3d6cf13e82883cf499c0b90a601def2252eec921092483d9c29263203/protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/2e/89/c7/3ca60719b822812f35a23d75c0d7ed0e3add76d191c76d8de15a03a86e/werkzeug-3.0.1-py3-none-any.whl b/.cache/pypoetry/artifacts/2e/89/c7/3ca60719b822812f35a23d75c0d7ed0e3add76d191c76d8de15a03a86e/werkzeug-3.0.1-py3-none-any.whl new file mode 100644 index 0000000..973268c Binary files /dev/null and b/.cache/pypoetry/artifacts/2e/89/c7/3ca60719b822812f35a23d75c0d7ed0e3add76d191c76d8de15a03a86e/werkzeug-3.0.1-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/2f/c4/c7/ba38fd559e21a1650f4b1bd4dcaee6de755b5c31bc44236af1e9849d33/aiohttp_retry-2.8.3-py3-none-any.whl b/.cache/pypoetry/artifacts/2f/c4/c7/ba38fd559e21a1650f4b1bd4dcaee6de755b5c31bc44236af1e9849d33/aiohttp_retry-2.8.3-py3-none-any.whl new file mode 100644 index 0000000..e447de3 Binary files /dev/null and b/.cache/pypoetry/artifacts/2f/c4/c7/ba38fd559e21a1650f4b1bd4dcaee6de755b5c31bc44236af1e9849d33/aiohttp_retry-2.8.3-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/31/89/36/b10046cde108a2b6b235053e443dd9ec1454f36cea2e802c7751a329cf/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/31/89/36/b10046cde108a2b6b235053e443dd9ec1454f36cea2e802c7751a329cf/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..aed9072 Binary files /dev/null and b/.cache/pypoetry/artifacts/31/89/36/b10046cde108a2b6b235053e443dd9ec1454f36cea2e802c7751a329cf/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/32/18/5a/319ab8649009a54201add7897256650e0873aa30240ca3dd622cb5789d/pycparser-2.22-py3-none-any.whl b/.cache/pypoetry/artifacts/32/18/5a/319ab8649009a54201add7897256650e0873aa30240ca3dd622cb5789d/pycparser-2.22-py3-none-any.whl new file mode 100644 index 0000000..b478187 Binary files /dev/null and b/.cache/pypoetry/artifacts/32/18/5a/319ab8649009a54201add7897256650e0873aa30240ca3dd622cb5789d/pycparser-2.22-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/32/98/55/90d57741c22ad0f000d610f3f722b771e75726538dd4a6226f9f6d7994/urllib3-2.2.1-py3-none-any.whl b/.cache/pypoetry/artifacts/32/98/55/90d57741c22ad0f000d610f3f722b771e75726538dd4a6226f9f6d7994/urllib3-2.2.1-py3-none-any.whl new file mode 100644 index 0000000..d7cca6a Binary files /dev/null and b/.cache/pypoetry/artifacts/32/98/55/90d57741c22ad0f000d610f3f722b771e75726538dd4a6226f9f6d7994/urllib3-2.2.1-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/39/62/70/443952414505d2a757ad47acd8eecf4c9d7ab2cac831ac8b94c4058eba/async_timeout-4.0.3-py3-none-any.whl b/.cache/pypoetry/artifacts/39/62/70/443952414505d2a757ad47acd8eecf4c9d7ab2cac831ac8b94c4058eba/async_timeout-4.0.3-py3-none-any.whl new file mode 100644 index 0000000..d802d01 Binary files /dev/null and b/.cache/pypoetry/artifacts/39/62/70/443952414505d2a757ad47acd8eecf4c9d7ab2cac831ac8b94c4058eba/async_timeout-4.0.3-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/47/0f/18/7de9a17e4f9336a271624c93cd67e9bb725ff4b119911c3d7e1391a5ee/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/47/0f/18/7de9a17e4f9336a271624c93cd67e9bb725ff4b119911c3d7e1391a5ee/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..a276053 Binary files /dev/null and b/.cache/pypoetry/artifacts/47/0f/18/7de9a17e4f9336a271624c93cd67e9bb725ff4b119911c3d7e1391a5ee/MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/4a/2d/be/139797a826440cf6e0927225f8898e84678bddf5803555332b9b382980/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/4a/2d/be/139797a826440cf6e0927225f8898e84678bddf5803555332b9b382980/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..ef8ce24 Binary files /dev/null and b/.cache/pypoetry/artifacts/4a/2d/be/139797a826440cf6e0927225f8898e84678bddf5803555332b9b382980/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/54/85/dc/371c905841d3ecf182a5d9023e5dba4e82055f470cf54563d96dc6aca8/aiosignal-1.3.1-py3-none-any.whl b/.cache/pypoetry/artifacts/54/85/dc/371c905841d3ecf182a5d9023e5dba4e82055f470cf54563d96dc6aca8/aiosignal-1.3.1-py3-none-any.whl new file mode 100644 index 0000000..41a992b Binary files /dev/null and b/.cache/pypoetry/artifacts/54/85/dc/371c905841d3ecf182a5d9023e5dba4e82055f470cf54563d96dc6aca8/aiosignal-1.3.1-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/55/88/44/2a41b377d5737eaec4a19d1a9bfa0f54ddd819afdcac253dbcf17c54b1/replit-3.6.1-py3-none-any.whl b/.cache/pypoetry/artifacts/55/88/44/2a41b377d5737eaec4a19d1a9bfa0f54ddd819afdcac253dbcf17c54b1/replit-3.6.1-py3-none-any.whl new file mode 100644 index 0000000..62f5791 Binary files /dev/null and b/.cache/pypoetry/artifacts/55/88/44/2a41b377d5737eaec4a19d1a9bfa0f54ddd819afdcac253dbcf17c54b1/replit-3.6.1-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/57/ac/4f/de6267eefc0c735d8dbf73fa23e03fc2f945aa4893a64d8007577c20d9/flask-3.0.0-py3-none-any.whl b/.cache/pypoetry/artifacts/57/ac/4f/de6267eefc0c735d8dbf73fa23e03fc2f945aa4893a64d8007577c20d9/flask-3.0.0-py3-none-any.whl new file mode 100644 index 0000000..f4343d2 Binary files /dev/null and b/.cache/pypoetry/artifacts/57/ac/4f/de6267eefc0c735d8dbf73fa23e03fc2f945aa4893a64d8007577c20d9/flask-3.0.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/58/0f/a5/3b28a6a4a1301a29957b6d76d0694641de0fea0bd6cf88292cdc4dc00c/werkzeug-3.0.2-py3-none-any.whl b/.cache/pypoetry/artifacts/58/0f/a5/3b28a6a4a1301a29957b6d76d0694641de0fea0bd6cf88292cdc4dc00c/werkzeug-3.0.2-py3-none-any.whl new file mode 100644 index 0000000..c692d80 Binary files /dev/null and b/.cache/pypoetry/artifacts/58/0f/a5/3b28a6a4a1301a29957b6d76d0694641de0fea0bd6cf88292cdc4dc00c/werkzeug-3.0.2-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/5c/c2/e2/e772753ccd41aaa07caa51116d596baa6a2628c065fdb77f41a510966d/click-8.1.7-py3-none-any.whl b/.cache/pypoetry/artifacts/5c/c2/e2/e772753ccd41aaa07caa51116d596baa6a2628c065fdb77f41a510966d/click-8.1.7-py3-none-any.whl new file mode 100644 index 0000000..5e8a550 Binary files /dev/null and b/.cache/pypoetry/artifacts/5c/c2/e2/e772753ccd41aaa07caa51116d596baa6a2628c065fdb77f41a510966d/click-8.1.7-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/79/2c/a9/0b9caa750c1c41565df4efc42dda2f57a4249d8eeb4cb8b6703bcfb518/certifi-2024.2.2-py3-none-any.whl b/.cache/pypoetry/artifacts/79/2c/a9/0b9caa750c1c41565df4efc42dda2f57a4249d8eeb4cb8b6703bcfb518/certifi-2024.2.2-py3-none-any.whl new file mode 100644 index 0000000..450e3bd Binary files /dev/null and b/.cache/pypoetry/artifacts/79/2c/a9/0b9caa750c1c41565df4efc42dda2f57a4249d8eeb4cb8b6703bcfb518/certifi-2024.2.2-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/7b/71/3f/fc1e9775780fa4bddc92bce2a2815a9fc61982dd3dac3cb73169d6a8b7/pyseto-1.7.8-py3-none-any.whl b/.cache/pypoetry/artifacts/7b/71/3f/fc1e9775780fa4bddc92bce2a2815a9fc61982dd3dac3cb73169d6a8b7/pyseto-1.7.8-py3-none-any.whl new file mode 100644 index 0000000..7e6e834 Binary files /dev/null and b/.cache/pypoetry/artifacts/7b/71/3f/fc1e9775780fa4bddc92bce2a2815a9fc61982dd3dac3cb73169d6a8b7/pyseto-1.7.8-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/7c/a4/82/0d1c5ce0cdbad1a9ae2a60a22a8b1d8745c627ee81fc113198b1a00666/cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/7c/a4/82/0d1c5ce0cdbad1a9ae2a60a22a8b1d8745c627ee81fc113198b1a00666/cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..bb16d17 Binary files /dev/null and b/.cache/pypoetry/artifacts/7c/a4/82/0d1c5ce0cdbad1a9ae2a60a22a8b1d8745c627ee81fc113198b1a00666/cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/87/ce/f7/70a3bc10b5cf18ae37d8838f324dd9b4a1034d94afbef0f796122ed084/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/87/ce/f7/70a3bc10b5cf18ae37d8838f324dd9b4a1034d94afbef0f796122ed084/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..19a91a2 Binary files /dev/null and b/.cache/pypoetry/artifacts/87/ce/f7/70a3bc10b5cf18ae37d8838f324dd9b4a1034d94afbef0f796122ed084/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/8c/b6/e3/2a9afdb73a2ce6138bdf34b193dafa3104f241e418d7d5025628b0dd4e/flask-3.0.2-py3-none-any.whl b/.cache/pypoetry/artifacts/8c/b6/e3/2a9afdb73a2ce6138bdf34b193dafa3104f241e418d7d5025628b0dd4e/flask-3.0.2-py3-none-any.whl new file mode 100644 index 0000000..0e45a3a Binary files /dev/null and b/.cache/pypoetry/artifacts/8c/b6/e3/2a9afdb73a2ce6138bdf34b193dafa3104f241e418d7d5025628b0dd4e/flask-3.0.2-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/9a/2c/43/02814710e352b4ee9eaa30a2dbba395ed20d0dcc19c47b60faea9de927/Jinja2-3.1.2-py3-none-any.whl b/.cache/pypoetry/artifacts/9a/2c/43/02814710e352b4ee9eaa30a2dbba395ed20d0dcc19c47b60faea9de927/Jinja2-3.1.2-py3-none-any.whl new file mode 100644 index 0000000..d271ac1 Binary files /dev/null and b/.cache/pypoetry/artifacts/9a/2c/43/02814710e352b4ee9eaa30a2dbba395ed20d0dcc19c47b60faea9de927/Jinja2-3.1.2-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/9b/b9/69/ec59c0741a30a7a9b9af43442d346b9473509c4ff23580108c991c6fba/flask-3.0.3-py3-none-any.whl b/.cache/pypoetry/artifacts/9b/b9/69/ec59c0741a30a7a9b9af43442d346b9473509c4ff23580108c991c6fba/flask-3.0.3-py3-none-any.whl new file mode 100644 index 0000000..cc25189 Binary files /dev/null and b/.cache/pypoetry/artifacts/9b/b9/69/ec59c0741a30a7a9b9af43442d346b9473509c4ff23580108c991c6fba/flask-3.0.3-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/9e/09/f6/e1a7e627ca2c3727b1d510f547791c57ec3707447838fe7b58d177875f/blinker-1.7.0-py3-none-any.whl b/.cache/pypoetry/artifacts/9e/09/f6/e1a7e627ca2c3727b1d510f547791c57ec3707447838fe7b58d177875f/blinker-1.7.0-py3-none-any.whl new file mode 100644 index 0000000..0ab35b9 Binary files /dev/null and b/.cache/pypoetry/artifacts/9e/09/f6/e1a7e627ca2c3727b1d510f547791c57ec3707447838fe7b58d177875f/blinker-1.7.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/a6/54/9c/65882877a500dd316c9031b9425df7cb143df6b7eb5bb3c653af1e1f74/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/a6/54/9c/65882877a500dd316c9031b9425df7cb143df6b7eb5bb3c653af1e1f74/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..24ec80e Binary files /dev/null and b/.cache/pypoetry/artifacts/a6/54/9c/65882877a500dd316c9031b9425df7cb143df6b7eb5bb3c653af1e1f74/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/ac/93/04/13105de15dcca2d2b091beb89057a8717ef155753d4fcd1ef1a3cd3010/multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/ac/93/04/13105de15dcca2d2b091beb89057a8717ef155753d4fcd1ef1a3cd3010/multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..c587434 Binary files /dev/null and b/.cache/pypoetry/artifacts/ac/93/04/13105de15dcca2d2b091beb89057a8717ef155753d4fcd1ef1a3cd3010/multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/b6/c1/f2/0b86989f0e293f83bad6e9c5ad8ad883a57b8a2fdbed58fb67bb162a3d/aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/b6/c1/f2/0b86989f0e293f83bad6e9c5ad8ad883a57b8a2fdbed58fb67bb162a3d/aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..301493e Binary files /dev/null and b/.cache/pypoetry/artifacts/b6/c1/f2/0b86989f0e293f83bad6e9c5ad8ad883a57b8a2fdbed58fb67bb162a3d/aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/ba/c1/04/990523e55eb864c93e633edd157c5872fff916bc1eee0017b20a367df8/attrs-23.2.0-py3-none-any.whl b/.cache/pypoetry/artifacts/ba/c1/04/990523e55eb864c93e633edd157c5872fff916bc1eee0017b20a367df8/attrs-23.2.0-py3-none-any.whl new file mode 100644 index 0000000..db9a731 Binary files /dev/null and b/.cache/pypoetry/artifacts/ba/c1/04/990523e55eb864c93e633edd157c5872fff916bc1eee0017b20a367df8/attrs-23.2.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/ba/de/64/6ef0aa013b01c05c1aea88732f242a5a9660c1408bce5dcdbbd250aacb/cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl b/.cache/pypoetry/artifacts/ba/de/64/6ef0aa013b01c05c1aea88732f242a5a9660c1408bce5dcdbbd250aacb/cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl new file mode 100644 index 0000000..9fd8131 Binary files /dev/null and b/.cache/pypoetry/artifacts/ba/de/64/6ef0aa013b01c05c1aea88732f242a5a9660c1408bce5dcdbbd250aacb/cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/bd/21/1c/829dc6ce56002ed62cb33bb03d6b51f9524564a0c9b35874a5cdaca64b/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/bd/21/1c/829dc6ce56002ed62cb33bb03d6b51f9524564a0c9b35874a5cdaca64b/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..021420b Binary files /dev/null and b/.cache/pypoetry/artifacts/bd/21/1c/829dc6ce56002ed62cb33bb03d6b51f9524564a0c9b35874a5cdaca64b/pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/c2/67/f4/3ac2d51a1770c6519e5f77bed72f6afbee0e9d4e4c37fe0a62f3a043eb/requests-2.31.0-py3-none-any.whl b/.cache/pypoetry/artifacts/c2/67/f4/3ac2d51a1770c6519e5f77bed72f6afbee0e9d4e4c37fe0a62f3a043eb/requests-2.31.0-py3-none-any.whl new file mode 100644 index 0000000..bfd5d2e Binary files /dev/null and b/.cache/pypoetry/artifacts/c2/67/f4/3ac2d51a1770c6519e5f77bed72f6afbee0e9d4e4c37fe0a62f3a043eb/requests-2.31.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/c7/73/62/3e3950e08ae836e82d28b1a73512e7e630e149ddd15dbbc6f26379f976/iso8601-2.1.0-py3-none-any.whl b/.cache/pypoetry/artifacts/c7/73/62/3e3950e08ae836e82d28b1a73512e7e630e149ddd15dbbc6f26379f976/iso8601-2.1.0-py3-none-any.whl new file mode 100644 index 0000000..39b6446 Binary files /dev/null and b/.cache/pypoetry/artifacts/c7/73/62/3e3950e08ae836e82d28b1a73512e7e630e149ddd15dbbc6f26379f976/iso8601-2.1.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/dd/26/3f/10df02ca72a154cf83c892ba78e53c126f47b0372123f6f9aa6189a9d7/idna-3.6-py3-none-any.whl b/.cache/pypoetry/artifacts/dd/26/3f/10df02ca72a154cf83c892ba78e53c126f47b0372123f6f9aa6189a9d7/idna-3.6-py3-none-any.whl new file mode 100644 index 0000000..fdf65ae Binary files /dev/null and b/.cache/pypoetry/artifacts/dd/26/3f/10df02ca72a154cf83c892ba78e53c126f47b0372123f6f9aa6189a9d7/idna-3.6-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/e1/e3/ae/6ef8b971fc9f26a8f9109de46b22b83d67b615a21be31a1c3d53841807/argon2_cffi-23.1.0-py3-none-any.whl b/.cache/pypoetry/artifacts/e1/e3/ae/6ef8b971fc9f26a8f9109de46b22b83d67b615a21be31a1c3d53841807/argon2_cffi-23.1.0-py3-none-any.whl new file mode 100644 index 0000000..0daabca Binary files /dev/null and b/.cache/pypoetry/artifacts/e1/e3/ae/6ef8b971fc9f26a8f9109de46b22b83d67b615a21be31a1c3d53841807/argon2_cffi-23.1.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/e8/81/51/2125fdce167a9ecf83379504f659e5cb74080e501c624c60013cdcf45c/itsdangerous-2.1.2-py3-none-any.whl b/.cache/pypoetry/artifacts/e8/81/51/2125fdce167a9ecf83379504f659e5cb74080e501c624c60013cdcf45c/itsdangerous-2.1.2-py3-none-any.whl new file mode 100644 index 0000000..2260642 Binary files /dev/null and b/.cache/pypoetry/artifacts/e8/81/51/2125fdce167a9ecf83379504f659e5cb74080e501c624c60013cdcf45c/itsdangerous-2.1.2-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/ea/7c/0d/7c6c1a0aeb5ec7c8eaa6e1e4f97c70e4c940239560e9cbd361fd8835f5/yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl b/.cache/pypoetry/artifacts/ea/7c/0d/7c6c1a0aeb5ec7c8eaa6e1e4f97c70e4c940239560e9cbd361fd8835f5/yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl new file mode 100644 index 0000000..e6bb31d Binary files /dev/null and b/.cache/pypoetry/artifacts/ea/7c/0d/7c6c1a0aeb5ec7c8eaa6e1e4f97c70e4c940239560e9cbd361fd8835f5/yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl differ diff --git a/.cache/pypoetry/artifacts/ed/9c/82/4780d7a00aaafb34d1d56b4fa651ea7f03970a72ff06dff3f7a79c8f9a/typing_extensions-4.11.0-py3-none-any.whl b/.cache/pypoetry/artifacts/ed/9c/82/4780d7a00aaafb34d1d56b4fa651ea7f03970a72ff06dff3f7a79c8f9a/typing_extensions-4.11.0-py3-none-any.whl new file mode 100644 index 0000000..1176514 Binary files /dev/null and b/.cache/pypoetry/artifacts/ed/9c/82/4780d7a00aaafb34d1d56b4fa651ea7f03970a72ff06dff3f7a79c8f9a/typing_extensions-4.11.0-py3-none-any.whl differ diff --git a/.cache/pypoetry/artifacts/fc/ea/19/431c0003de6ff73ab02130c21ec5b13fde1bf9c7f1d4cf716c8c4658bd/Jinja2-3.1.3-py3-none-any.whl b/.cache/pypoetry/artifacts/fc/ea/19/431c0003de6ff73ab02130c21ec5b13fde1bf9c7f1d4cf716c8c4658bd/Jinja2-3.1.3-py3-none-any.whl new file mode 100644 index 0000000..eeb2bc7 Binary files /dev/null and b/.cache/pypoetry/artifacts/fc/ea/19/431c0003de6ff73ab02130c21ec5b13fde1bf9c7f1d4cf716c8c4658bd/Jinja2-3.1.3-py3-none-any.whl differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/03/93/17/f7/47/6f/f1/79/039317f7476ff179b9fff5e21cdd6a7be073d86b1e9ca8e9198b11cf08e5e0ac b/.cache/pypoetry/cache/repositories/PyPI/03/93/17/f7/47/6f/f1/79/039317f7476ff179b9fff5e21cdd6a7be073d86b1e9ca8e9198b11cf08e5e0ac new file mode 100644 index 0000000..fad663c --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/03/93/17/f7/47/6f/f1/79/039317f7476ff179b9fff5e21cdd6a7be073d86b1e9ca8e9198b11cf08e5e0ac @@ -0,0 +1 @@ +9999999999{"name": "idna", "version": "3.6", "summary": "Internationalized Domain Names in Applications (IDNA)", "requires_dist": null, "requires_python": ">=3.5", "files": [{"file": "idna-3.6-py3-none-any.whl", "hash": "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {"file": "idna-3.6.tar.gz", "hash": "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/09/c5/93/51/9a/29/37/cd/09c593519a2937cd40c83e3ce4e872775e30ab4b5e675f3b82a1e3c0e0821235 b/.cache/pypoetry/cache/repositories/PyPI/09/c5/93/51/9a/29/37/cd/09c593519a2937cd40c83e3ce4e872775e30ab4b5e675f3b82a1e3c0e0821235 new file mode 100644 index 0000000..a355d8e --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/09/c5/93/51/9a/29/37/cd/09c593519a2937cd40c83e3ce4e872775e30ab4b5e675f3b82a1e3c0e0821235 @@ -0,0 +1 @@ +9999999999{"name": "aiosignal", "version": "1.3.1", "summary": "aiosignal: a list of registered asynchronous callbacks", "requires_dist": ["frozenlist (>=1.1.0)"], "requires_python": ">=3.7", "files": [{"file": "aiosignal-1.3.1-py3-none-any.whl", "hash": "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {"file": "aiosignal-1.3.1.tar.gz", "hash": "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/1e/0b/52/18/e0/72/46/d0/1e0b5218e07246d05542c4effe27e7506ef289925adb640d9e31a568ad7ff8c7 b/.cache/pypoetry/cache/repositories/PyPI/1e/0b/52/18/e0/72/46/d0/1e0b5218e07246d05542c4effe27e7506ef289925adb640d9e31a568ad7ff8c7 new file mode 100644 index 0000000..023148e --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/1e/0b/52/18/e0/72/46/d0/1e0b5218e07246d05542c4effe27e7506ef289925adb640d9e31a568ad7ff8c7 @@ -0,0 +1 @@ +9999999999{"name": "async-timeout", "version": "4.0.3", "summary": "Timeout context manager for asyncio programs", "requires_dist": ["typing-extensions >=3.6.5 ; python_version < \"3.8\""], "requires_python": ">=3.7", "files": [{"file": "async_timeout-4.0.3-py3-none-any.whl", "hash": "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, {"file": "async-timeout-4.0.3.tar.gz", "hash": "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/29/e7/a5/a8/69/f6/51/ad/29e7a5a869f651adf46b9e8c0eea2cca1a4fd6fc43a38917c92752d75590f7ee b/.cache/pypoetry/cache/repositories/PyPI/29/e7/a5/a8/69/f6/51/ad/29e7a5a869f651adf46b9e8c0eea2cca1a4fd6fc43a38917c92752d75590f7ee new file mode 100644 index 0000000..b626461 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/29/e7/a5/a8/69/f6/51/ad/29e7a5a869f651adf46b9e8c0eea2cca1a4fd6fc43a38917c92752d75590f7ee @@ -0,0 +1 @@ +9999999999{"name": "aiohttp-retry", "version": "2.8.3", "summary": "Simple retry client for aiohttp", "requires_dist": ["aiohttp"], "requires_python": ">=3.7", "files": [{"file": "aiohttp_retry-2.8.3-py3-none-any.whl", "hash": "sha256:3aeeead8f6afe48272db93ced9440cf4eda8b6fd7ee2abb25357b7eb28525b45"}, {"file": "aiohttp_retry-2.8.3.tar.gz", "hash": "sha256:9a8e637e31682ad36e1ff9f8bcba912fcfc7d7041722bc901a4b948da4d71ea9"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/33/20/51/3b/f1/db/59/a3/3320513bf1db59a37551ea098621e624d12c2880a1c279c3de0ed452410863e5 b/.cache/pypoetry/cache/repositories/PyPI/33/20/51/3b/f1/db/59/a3/3320513bf1db59a37551ea098621e624d12c2880a1c279c3de0ed452410863e5 new file mode 100644 index 0000000..bf20627 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/33/20/51/3b/f1/db/59/a3/3320513bf1db59a37551ea098621e624d12c2880a1c279c3de0ed452410863e5 @@ -0,0 +1 @@ +9999999999{"name": "Jinja2", "version": "3.1.2", "summary": "A very fast and expressive template engine.", "requires_dist": ["MarkupSafe (>=2.0)", "Babel (>=2.7) ; extra == 'i18n'"], "requires_python": ">=3.7", "files": [{"file": "Jinja2-3.1.2-py3-none-any.whl", "hash": "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {"file": "Jinja2-3.1.2.tar.gz", "hash": "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/43/88/fe/f6/70/59/20/f7/4388fef6705920f7e38014ee473386c941190127fe1862a9ff14a1507e315864 b/.cache/pypoetry/cache/repositories/PyPI/43/88/fe/f6/70/59/20/f7/4388fef6705920f7e38014ee473386c941190127fe1862a9ff14a1507e315864 new file mode 100644 index 0000000..d6b5189 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/43/88/fe/f6/70/59/20/f7/4388fef6705920f7e38014ee473386c941190127fe1862a9ff14a1507e315864 @@ -0,0 +1 @@ +9999999999{"name": "pycryptodomex", "version": "3.20.0", "summary": "Cryptographic library for Python", "requires_dist": null, "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", "files": [{"file": "pycryptodomex-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", "hash": "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_i686.whl", "hash": "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", "hash": "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", "hash": "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc"}, {"file": "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", "hash": "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64"}, {"file": "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", "hash": "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa"}, {"file": "pycryptodomex-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079"}, {"file": "pycryptodomex-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", "hash": "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-win32.whl", "hash": "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8"}, {"file": "pycryptodomex-3.20.0-cp27-cp27m-win_amd64.whl", "hash": "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", "hash": "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", "hash": "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", "hash": "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", "hash": "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", "hash": "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-win32.whl", "hash": "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e"}, {"file": "pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", "hash": "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc"}, {"file": "pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", "hash": "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458"}, {"file": "pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", "hash": "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c"}, {"file": "pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", "hash": "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b"}, {"file": "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea"}, {"file": "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781"}, {"file": "pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", "hash": "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499"}, {"file": "pycryptodomex-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", "hash": "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794"}, {"file": "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1"}, {"file": "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc"}, {"file": "pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", "hash": "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427"}, {"file": "pycryptodomex-3.20.0.tar.gz", "hash": "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/47/6e/a3/9d/53/01/ab/69/476ea39d5301ab69032055e9e62fb4297cee6eb27f83384aadbb70dcef16bb2b b/.cache/pypoetry/cache/repositories/PyPI/47/6e/a3/9d/53/01/ab/69/476ea39d5301ab69032055e9e62fb4297cee6eb27f83384aadbb70dcef16bb2b new file mode 100644 index 0000000..1172a5a --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/47/6e/a3/9d/53/01/ab/69/476ea39d5301ab69032055e9e62fb4297cee6eb27f83384aadbb70dcef16bb2b @@ -0,0 +1 @@ +9999999999{"name": "Flask", "version": "3.0.3", "summary": "A simple framework for building complex web applications.", "requires_dist": ["Werkzeug>=3.0.0", "Jinja2>=3.1.2", "itsdangerous>=2.1.2", "click>=8.1.3", "blinker>=1.6.2", "importlib-metadata>=3.6.0; python_version < \"3.10\"", "asgiref>=3.2; extra == \"async\"", "python-dotenv; extra == \"dotenv\""], "requires_python": ">=3.8", "files": [{"file": "flask-3.0.3-py3-none-any.whl", "hash": "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, {"file": "flask-3.0.3.tar.gz", "hash": "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/4e/37/5d/4c/68/52/eb/fa/4e375d4c6852ebfa23ec5202078be40729016247bead003507b6c2073e97f00c b/.cache/pypoetry/cache/repositories/PyPI/4e/37/5d/4c/68/52/eb/fa/4e375d4c6852ebfa23ec5202078be40729016247bead003507b6c2073e97f00c new file mode 100644 index 0000000..bec9d75 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/4e/37/5d/4c/68/52/eb/fa/4e375d4c6852ebfa23ec5202078be40729016247bead003507b6c2073e97f00c @@ -0,0 +1 @@ +9999999999{"name": "cffi", "version": "1.16.0", "summary": "Foreign Function Interface for Python calling C code.", "requires_dist": ["pycparser"], "requires_python": ">=3.8", "files": [{"file": "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, {"file": "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, {"file": "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, {"file": "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, {"file": "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, {"file": "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, {"file": "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, {"file": "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, {"file": "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, {"file": "cffi-1.16.0-cp310-cp310-win32.whl", "hash": "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, {"file": "cffi-1.16.0-cp310-cp310-win_amd64.whl", "hash": "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, {"file": "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, {"file": "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, {"file": "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, {"file": "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, {"file": "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, {"file": "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, {"file": "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, {"file": "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, {"file": "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, {"file": "cffi-1.16.0-cp311-cp311-win32.whl", "hash": "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, {"file": "cffi-1.16.0-cp311-cp311-win_amd64.whl", "hash": "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, {"file": "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, {"file": "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, {"file": "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, {"file": "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, {"file": "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, {"file": "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, {"file": "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, {"file": "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, {"file": "cffi-1.16.0-cp312-cp312-win32.whl", "hash": "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, {"file": "cffi-1.16.0-cp312-cp312-win_amd64.whl", "hash": "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, {"file": "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, {"file": "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, {"file": "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, {"file": "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, {"file": "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, {"file": "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, {"file": "cffi-1.16.0-cp38-cp38-win32.whl", "hash": "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, {"file": "cffi-1.16.0-cp38-cp38-win_amd64.whl", "hash": "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, {"file": "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, {"file": "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, {"file": "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, {"file": "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, {"file": "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, {"file": "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, {"file": "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, {"file": "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, {"file": "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, {"file": "cffi-1.16.0-cp39-cp39-win32.whl", "hash": "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, {"file": "cffi-1.16.0-cp39-cp39-win_amd64.whl", "hash": "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, {"file": "cffi-1.16.0.tar.gz", "hash": "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/52/3f/d2/4f/c1/a3/9f/27/523fd24fc1a39f27d83b980bebde7ab593d60de8b8db1dd0679e9a7fea3783ef b/.cache/pypoetry/cache/repositories/PyPI/52/3f/d2/4f/c1/a3/9f/27/523fd24fc1a39f27d83b980bebde7ab593d60de8b8db1dd0679e9a7fea3783ef new file mode 100644 index 0000000..9322091 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/52/3f/d2/4f/c1/a3/9f/27/523fd24fc1a39f27d83b980bebde7ab593d60de8b8db1dd0679e9a7fea3783ef @@ -0,0 +1 @@ +9999999999{"name": "typing-extensions", "version": "4.11.0", "summary": "Backported and Experimental Type Hints for Python 3.8+", "requires_dist": null, "requires_python": ">=3.8", "files": [{"file": "typing_extensions-4.11.0-py3-none-any.whl", "hash": "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, {"file": "typing_extensions-4.11.0.tar.gz", "hash": "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/56/63/94/d7/3a/ad/67/35/566394d73aad673514aa70f3fe2e993d1f9f776cac0de2fb33c662d3fe371d53 b/.cache/pypoetry/cache/repositories/PyPI/56/63/94/d7/3a/ad/67/35/566394d73aad673514aa70f3fe2e993d1f9f776cac0de2fb33c662d3fe371d53 new file mode 100644 index 0000000..ad698fd --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/56/63/94/d7/3a/ad/67/35/566394d73aad673514aa70f3fe2e993d1f9f776cac0de2fb33c662d3fe371d53 @@ -0,0 +1 @@ +9999999999{"name": "Flask", "version": "3.0.0", "summary": "A simple framework for building complex web applications.", "requires_dist": ["Werkzeug>=3.0.0", "Jinja2>=3.1.2", "itsdangerous>=2.1.2", "click>=8.1.3", "blinker>=1.6.2", "importlib-metadata>=3.6.0; python_version < '3.10'", "asgiref>=3.2 ; extra == \"async\"", "python-dotenv ; extra == \"dotenv\""], "requires_python": ">=3.8", "files": [{"file": "flask-3.0.0-py3-none-any.whl", "hash": "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"}, {"file": "flask-3.0.0.tar.gz", "hash": "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/62/63/3a/12/cf/28/c6/2c/62633a12cf28c62c87aa00562c28367b9fa185b07b5e6b682b7a0a9f64872c7b b/.cache/pypoetry/cache/repositories/PyPI/62/63/3a/12/cf/28/c6/2c/62633a12cf28c62c87aa00562c28367b9fa185b07b5e6b682b7a0a9f64872c7b new file mode 100644 index 0000000..e3d2213 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/62/63/3a/12/cf/28/c6/2c/62633a12cf28c62c87aa00562c28367b9fa185b07b5e6b682b7a0a9f64872c7b @@ -0,0 +1 @@ +9999999999{"name": "frozenlist", "version": "1.4.1", "summary": "A list-like structure which implements collections.abc.MutableSequence", "requires_dist": null, "requires_python": ">=3.8", "files": [{"file": "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, {"file": "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, {"file": "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, {"file": "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, {"file": "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, {"file": "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, {"file": "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, {"file": "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, {"file": "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, {"file": "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, {"file": "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", "hash": "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, {"file": "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", "hash": "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, {"file": "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, {"file": "frozenlist-1.4.1-cp310-cp310-win32.whl", "hash": "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, {"file": "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", "hash": "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, {"file": "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, {"file": "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, {"file": "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, {"file": "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, {"file": "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, {"file": "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, {"file": "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, {"file": "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, {"file": "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, {"file": "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, {"file": "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", "hash": "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, {"file": "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", "hash": "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, {"file": "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, {"file": "frozenlist-1.4.1-cp311-cp311-win32.whl", "hash": "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, {"file": "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", "hash": "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, {"file": "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, {"file": "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, {"file": "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, {"file": "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, {"file": "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, {"file": "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, {"file": "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, {"file": "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, {"file": "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, {"file": "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, {"file": "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", "hash": "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, {"file": "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", "hash": "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, {"file": "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, {"file": "frozenlist-1.4.1-cp312-cp312-win32.whl", "hash": "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, {"file": "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", "hash": "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, {"file": "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, {"file": "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, {"file": "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", "hash": "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, {"file": "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, {"file": "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, {"file": "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, {"file": "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, {"file": "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, {"file": "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, {"file": "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, {"file": "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", "hash": "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, {"file": "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", "hash": "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, {"file": "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, {"file": "frozenlist-1.4.1-cp38-cp38-win32.whl", "hash": "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, {"file": "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", "hash": "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, {"file": "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, {"file": "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, {"file": "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, {"file": "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, {"file": "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, {"file": "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, {"file": "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, {"file": "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, {"file": "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, {"file": "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, {"file": "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", "hash": "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, {"file": "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", "hash": "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, {"file": "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, {"file": "frozenlist-1.4.1-cp39-cp39-win32.whl", "hash": "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, {"file": "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", "hash": "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, {"file": "frozenlist-1.4.1-py3-none-any.whl", "hash": "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, {"file": "frozenlist-1.4.1.tar.gz", "hash": "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/63/4d/dd/3b/b8/30/0d/ec/634ddd3bb8300dec588277e4541f8997b8fea3c186b8d42b2d304bc5ee2730da b/.cache/pypoetry/cache/repositories/PyPI/63/4d/dd/3b/b8/30/0d/ec/634ddd3bb8300dec588277e4541f8997b8fea3c186b8d42b2d304bc5ee2730da new file mode 100644 index 0000000..47e58e4 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/63/4d/dd/3b/b8/30/0d/ec/634ddd3bb8300dec588277e4541f8997b8fea3c186b8d42b2d304bc5ee2730da @@ -0,0 +1 @@ +9999999999{"name": "cryptography", "version": "42.0.5", "summary": "cryptography is a package which provides cryptographic recipes and primitives to Python developers.", "requires_dist": ["cffi >=1.12 ; platform_python_implementation != \"PyPy\"", "sphinx >=5.3.0 ; extra == 'docs'", "sphinx-rtd-theme >=1.1.1 ; extra == 'docs'", "pyenchant >=1.6.11 ; extra == 'docstest'", "readme-renderer ; extra == 'docstest'", "sphinxcontrib-spelling >=4.0.1 ; extra == 'docstest'", "nox ; extra == 'nox'", "ruff ; extra == 'pep8test'", "mypy ; extra == 'pep8test'", "check-sdist ; extra == 'pep8test'", "click ; extra == 'pep8test'", "build ; extra == 'sdist'", "bcrypt >=3.1.5 ; extra == 'ssh'", "pytest >=6.2.0 ; extra == 'test'", "pytest-benchmark ; extra == 'test'", "pytest-cov ; extra == 'test'", "pytest-xdist ; extra == 'test'", "pretend ; extra == 'test'", "certifi ; extra == 'test'", "pytest-randomly ; extra == 'test-randomorder'"], "requires_python": ">=3.7", "files": [{"file": "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", "hash": "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, {"file": "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", "hash": "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, {"file": "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, {"file": "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, {"file": "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", "hash": "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, {"file": "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", "hash": "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, {"file": "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", "hash": "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, {"file": "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", "hash": "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, {"file": "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", "hash": "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, {"file": "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", "hash": "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, {"file": "cryptography-42.0.5-cp37-abi3-win32.whl", "hash": "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, {"file": "cryptography-42.0.5-cp37-abi3-win_amd64.whl", "hash": "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, {"file": "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", "hash": "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, {"file": "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, {"file": "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, {"file": "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", "hash": "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, {"file": "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", "hash": "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, {"file": "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", "hash": "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, {"file": "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", "hash": "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, {"file": "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", "hash": "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, {"file": "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", "hash": "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, {"file": "cryptography-42.0.5-cp39-abi3-win32.whl", "hash": "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, {"file": "cryptography-42.0.5-cp39-abi3-win_amd64.whl", "hash": "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, {"file": "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", "hash": "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, {"file": "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", "hash": "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, {"file": "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", "hash": "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, {"file": "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", "hash": "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, {"file": "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", "hash": "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, {"file": "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", "hash": "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, {"file": "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", "hash": "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, {"file": "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", "hash": "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, {"file": "cryptography-42.0.5.tar.gz", "hash": "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/67/23/d2/b3/18/9c/a1/62/6723d2b3189ca1625016c4abe9ae40112db30922ed05794ca637bb76cf720cf5 b/.cache/pypoetry/cache/repositories/PyPI/67/23/d2/b3/18/9c/a1/62/6723d2b3189ca1625016c4abe9ae40112db30922ed05794ca637bb76cf720cf5 new file mode 100644 index 0000000..79e5841 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/67/23/d2/b3/18/9c/a1/62/6723d2b3189ca1625016c4abe9ae40112db30922ed05794ca637bb76cf720cf5 @@ -0,0 +1 @@ +9999999999{"name": "MarkupSafe", "version": "2.1.5", "summary": "Safely add untrusted strings to HTML/XML markup.", "requires_dist": null, "requires_python": ">=3.7", "files": [{"file": "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-win32.whl", "hash": "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {"file": "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", "hash": "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-win32.whl", "hash": "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {"file": "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", "hash": "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-win32.whl", "hash": "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {"file": "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", "hash": "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", "hash": "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", "hash": "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", "hash": "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", "hash": "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", "hash": "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {"file": "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", "hash": "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-win32.whl", "hash": "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {"file": "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", "hash": "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-win32.whl", "hash": "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {"file": "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", "hash": "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {"file": "MarkupSafe-2.1.5.tar.gz", "hash": "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/6b/2f/7c/07/7a/43/a2/5e/6b2f7c077a43a25e6bda0b9dd4f75c11860d01e92aee74df68e97f8e939fb452 b/.cache/pypoetry/cache/repositories/PyPI/6b/2f/7c/07/7a/43/a2/5e/6b2f7c077a43a25e6bda0b9dd4f75c11860d01e92aee74df68e97f8e939fb452 new file mode 100644 index 0000000..58241fd --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/6b/2f/7c/07/7a/43/a2/5e/6b2f7c077a43a25e6bda0b9dd4f75c11860d01e92aee74df68e97f8e939fb452 @@ -0,0 +1 @@ +9999999999{"name": "pyseto", "version": "1.7.8", "summary": "A Python implementation of PASETO/PASERK.", "requires_dist": ["cryptography (>=42.0.1,<43)", "pycryptodomex (>=3.18.0)", "iso8601 (>=1.0.2,<3.0.0)", "Sphinx[docs] (>=7.1,<8) ; extra == \"docs\"", "sphinx-autodoc-typehints[docs] (>=1.25.2) ; extra == \"docs\"", "sphinx-rtd-theme[docs] (>=1.2.1,<3.0.0) ; extra == \"docs\"", "argon2-cffi (>=23.1.0)"], "requires_python": ">=3.8,<4.0", "files": [{"file": "pyseto-1.7.8-py3-none-any.whl", "hash": "sha256:59dba2071d77bf1f9f17075f5b8a585cf64b64ccb286c255d1fe423bc8e5ccd7"}, {"file": "pyseto-1.7.8.tar.gz", "hash": "sha256:cd095d9587e343e56ff088c40ddb4ae1de51eadef34644aaf27b1f752492bfeb"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/7f/3e/46/83/3c/09/db/51/7f3e46833c09db5137b7c1bcfb37cc679f515ff2b8d295a886c6c128ae7fa5eb b/.cache/pypoetry/cache/repositories/PyPI/7f/3e/46/83/3c/09/db/51/7f3e46833c09db5137b7c1bcfb37cc679f515ff2b8d295a886c6c128ae7fa5eb new file mode 100644 index 0000000..dd24fa1 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/7f/3e/46/83/3c/09/db/51/7f3e46833c09db5137b7c1bcfb37cc679f515ff2b8d295a886c6c128ae7fa5eb @@ -0,0 +1 @@ +9999999999{"name": "urllib3", "version": "2.2.1", "summary": "HTTP library with thread-safe connection pooling, file post, and more.", "requires_dist": ["brotli>=1.0.9; (platform_python_implementation == 'CPython') and extra == 'brotli'", "brotlicffi>=0.8.0; (platform_python_implementation != 'CPython') and extra == 'brotli'", "h2<5,>=4; extra == 'h2'", "pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks'", "zstandard>=0.18.0; extra == 'zstd'"], "requires_python": ">=3.8", "files": [{"file": "urllib3-2.2.1-py3-none-any.whl", "hash": "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {"file": "urllib3-2.2.1.tar.gz", "hash": "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/88/f2/c3/f0/11/05/66/7f/88f2c3f01105667fe60cf97b0ee488de6f16d7591f524de753d4b79149920358 b/.cache/pypoetry/cache/repositories/PyPI/88/f2/c3/f0/11/05/66/7f/88f2c3f01105667fe60cf97b0ee488de6f16d7591f524de753d4b79149920358 new file mode 100644 index 0000000..b8c7fba --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/88/f2/c3/f0/11/05/66/7f/88f2c3f01105667fe60cf97b0ee488de6f16d7591f524de753d4b79149920358 @@ -0,0 +1 @@ +9999999999{"name": "certifi", "version": "2024.2.2", "summary": "Python package for providing Mozilla's CA Bundle.", "requires_dist": null, "requires_python": ">=3.6", "files": [{"file": "certifi-2024.2.2-py3-none-any.whl", "hash": "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {"file": "certifi-2024.2.2.tar.gz", "hash": "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/91/07/a6/d3/07/35/02/ae/9107a6d3073502ae0ec7b95fbb7b2e042e615618c62d6f3a30f540ea140d6eb3 b/.cache/pypoetry/cache/repositories/PyPI/91/07/a6/d3/07/35/02/ae/9107a6d3073502ae0ec7b95fbb7b2e042e615618c62d6f3a30f540ea140d6eb3 new file mode 100644 index 0000000..f32c142 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/91/07/a6/d3/07/35/02/ae/9107a6d3073502ae0ec7b95fbb7b2e042e615618c62d6f3a30f540ea140d6eb3 @@ -0,0 +1 @@ +9999999999{"name": "blinker", "version": "1.7.0", "summary": "Fast, simple object-to-object and broadcast signaling", "requires_dist": null, "requires_python": ">=3.8", "files": [{"file": "blinker-1.7.0-py3-none-any.whl", "hash": "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, {"file": "blinker-1.7.0.tar.gz", "hash": "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/96/5c/82/bd/9a/c4/e6/33/965c82bd9ac4e6331081ce8de8f65ce6e146761ffec89dc7d7b8ef7ba9478371 b/.cache/pypoetry/cache/repositories/PyPI/96/5c/82/bd/9a/c4/e6/33/965c82bd9ac4e6331081ce8de8f65ce6e146761ffec89dc7d7b8ef7ba9478371 new file mode 100644 index 0000000..4e538cd --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/96/5c/82/bd/9a/c4/e6/33/965c82bd9ac4e6331081ce8de8f65ce6e146761ffec89dc7d7b8ef7ba9478371 @@ -0,0 +1 @@ +9999999999{"name": "replit", "version": "3.6.1", "summary": "A library for interacting with features of Replit", "requires_dist": ["typing_extensions (>=4,<5)", "Flask (>=2.0.0)", "Werkzeug (>=2,<4)", "aiohttp (>=3.6.2)", "requests (>=2.25.1,<3.0.0)", "pyseto (>=1.6.11,<2.0.0)", "protobuf (>=4.21.8,<5.0.0)", "urllib3 (>=1.26,<3)", "aiohttp-retry (>=2.8.3,<3.0.0)"], "requires_python": ">=3.9,<4.0", "files": [{"file": "replit-3.6.1-py3-none-any.whl", "hash": "sha256:77209cb1738cc8e4229b7f8d374170ff2705190b4c41c7d0085855ffa64a5a66"}, {"file": "replit-3.6.1.tar.gz", "hash": "sha256:25ad7b6b5eb5b805f11fbbb8a2ec6912259a193dcf6461a39631d5d6d875d8dd"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/99/ba/3a/d1/f8/5d/a1/c8/99ba3ad1f85da1c8a32978ef3a3f85918a7f920fb93f16c9999539ebd82eb58d b/.cache/pypoetry/cache/repositories/PyPI/99/ba/3a/d1/f8/5d/a1/c8/99ba3ad1f85da1c8a32978ef3a3f85918a7f920fb93f16c9999539ebd82eb58d new file mode 100644 index 0000000..89b4704 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/99/ba/3a/d1/f8/5d/a1/c8/99ba3ad1f85da1c8a32978ef3a3f85918a7f920fb93f16c9999539ebd82eb58d @@ -0,0 +1 @@ +9999999999{"name": "Flask", "version": "3.0.2", "summary": "A simple framework for building complex web applications.", "requires_dist": ["Werkzeug>=3.0.0", "Jinja2>=3.1.2", "itsdangerous>=2.1.2", "click>=8.1.3", "blinker>=1.6.2", "importlib-metadata>=3.6.0; python_version < '3.10'", "asgiref>=3.2 ; extra == \"async\"", "python-dotenv ; extra == \"dotenv\""], "requires_python": ">=3.8", "files": [{"file": "flask-3.0.2-py3-none-any.whl", "hash": "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, {"file": "flask-3.0.2.tar.gz", "hash": "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/9f/9d/44/55/c2/8a/d6/e2/9f9d4455c28ad6e29e29b72090faeb759cea2a164b496f9c3175c8cc21808779 b/.cache/pypoetry/cache/repositories/PyPI/9f/9d/44/55/c2/8a/d6/e2/9f9d4455c28ad6e29e29b72090faeb759cea2a164b496f9c3175c8cc21808779 new file mode 100644 index 0000000..ef8d982 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/9f/9d/44/55/c2/8a/d6/e2/9f9d4455c28ad6e29e29b72090faeb759cea2a164b496f9c3175c8cc21808779 @@ -0,0 +1 @@ +9999999999{"name": "Jinja2", "version": "3.1.3", "summary": "A very fast and expressive template engine.", "requires_dist": ["MarkupSafe >=2.0", "Babel >=2.7 ; extra == 'i18n'"], "requires_python": ">=3.7", "files": [{"file": "Jinja2-3.1.3-py3-none-any.whl", "hash": "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {"file": "Jinja2-3.1.3.tar.gz", "hash": "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/0/4/e/0/1/04e017851dbfe81db92d092eaaf2526622c3950bcb8f2696e57143da b/.cache/pypoetry/cache/repositories/PyPI/_http/0/4/e/0/1/04e017851dbfe81db92d092eaaf2526622c3950bcb8f2696e57143da new file mode 100644 index 0000000..50cd91b Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/0/4/e/0/1/04e017851dbfe81db92d092eaaf2526622c3950bcb8f2696e57143da differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/0/4/e/0/1/04e017851dbfe81db92d092eaaf2526622c3950bcb8f2696e57143da.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/0/4/e/0/1/04e017851dbfe81db92d092eaaf2526622c3950bcb8f2696e57143da.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/0/d/7/6/f/0d76fef7d8eef3a681cac54fa801434b324ccd9485677d706883646c b/.cache/pypoetry/cache/repositories/PyPI/_http/0/d/7/6/f/0d76fef7d8eef3a681cac54fa801434b324ccd9485677d706883646c new file mode 100644 index 0000000..1b9e64d Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/0/d/7/6/f/0d76fef7d8eef3a681cac54fa801434b324ccd9485677d706883646c differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/0/d/7/6/f/0d76fef7d8eef3a681cac54fa801434b324ccd9485677d706883646c.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/0/d/7/6/f/0d76fef7d8eef3a681cac54fa801434b324ccd9485677d706883646c.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/3/6/7/c/1367c1047a68f6bcf5a42d9c108d23bc1527ebca4c215f01d6c094a3 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/3/6/7/c/1367c1047a68f6bcf5a42d9c108d23bc1527ebca4c215f01d6c094a3 new file mode 100644 index 0000000..e4b26c6 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/3/6/7/c/1367c1047a68f6bcf5a42d9c108d23bc1527ebca4c215f01d6c094a3 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/3/6/7/c/1367c1047a68f6bcf5a42d9c108d23bc1527ebca4c215f01d6c094a3.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/3/6/7/c/1367c1047a68f6bcf5a42d9c108d23bc1527ebca4c215f01d6c094a3.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/6/9/3/2/1693297fb9daf7bfe370bf51d371acfeb8ff40759bf8650dfd404ba4 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/6/9/3/2/1693297fb9daf7bfe370bf51d371acfeb8ff40759bf8650dfd404ba4 new file mode 100644 index 0000000..bdec704 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/6/9/3/2/1693297fb9daf7bfe370bf51d371acfeb8ff40759bf8650dfd404ba4 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/6/9/3/2/1693297fb9daf7bfe370bf51d371acfeb8ff40759bf8650dfd404ba4.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/6/9/3/2/1693297fb9daf7bfe370bf51d371acfeb8ff40759bf8650dfd404ba4.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/2/d/3/1a2d39595dc616d36195328fa59aa988a3f9bce0c80d10d30278d542 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/2/d/3/1a2d39595dc616d36195328fa59aa988a3f9bce0c80d10d30278d542 new file mode 100644 index 0000000..f095bfa Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/2/d/3/1a2d39595dc616d36195328fa59aa988a3f9bce0c80d10d30278d542 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/2/d/3/1a2d39595dc616d36195328fa59aa988a3f9bce0c80d10d30278d542.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/2/d/3/1a2d39595dc616d36195328fa59aa988a3f9bce0c80d10d30278d542.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/4/c/1a84c7ae0bb2d2769201a9b0fd31566bb1a2eae3ccc6fe61ceb246aa b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/4/c/1a84c7ae0bb2d2769201a9b0fd31566bb1a2eae3ccc6fe61ceb246aa new file mode 100644 index 0000000..20c3cc1 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/4/c/1a84c7ae0bb2d2769201a9b0fd31566bb1a2eae3ccc6fe61ceb246aa differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/4/c/1a84c7ae0bb2d2769201a9b0fd31566bb1a2eae3ccc6fe61ceb246aa.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/4/c/1a84c7ae0bb2d2769201a9b0fd31566bb1a2eae3ccc6fe61ceb246aa.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/f/f/1a8ff2f282c087cdf9cf873cda00681c0b40ab3160af3b1797069bd7 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/f/f/1a8ff2f282c087cdf9cf873cda00681c0b40ab3160af3b1797069bd7 new file mode 100644 index 0000000..44578d3 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/f/f/1a8ff2f282c087cdf9cf873cda00681c0b40ab3160af3b1797069bd7 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/f/f/1a8ff2f282c087cdf9cf873cda00681c0b40ab3160af3b1797069bd7.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/a/8/f/f/1a8ff2f282c087cdf9cf873cda00681c0b40ab3160af3b1797069bd7.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/d/8/2/8/1d828a3e277c37626c3a342a9e4e7dcd90bd247c5e0570fa37909ff5 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/d/8/2/8/1d828a3e277c37626c3a342a9e4e7dcd90bd247c5e0570fa37909ff5 new file mode 100644 index 0000000..efb8172 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/d/8/2/8/1d828a3e277c37626c3a342a9e4e7dcd90bd247c5e0570fa37909ff5 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/d/8/2/8/1d828a3e277c37626c3a342a9e4e7dcd90bd247c5e0570fa37909ff5.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/d/8/2/8/1d828a3e277c37626c3a342a9e4e7dcd90bd247c5e0570fa37909ff5.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/e/6/f/2/1e6f2fa1119d47b853219feb91c3867d411e0c8c02476d1e769171e8 b/.cache/pypoetry/cache/repositories/PyPI/_http/1/e/6/f/2/1e6f2fa1119d47b853219feb91c3867d411e0c8c02476d1e769171e8 new file mode 100644 index 0000000..9f849d1 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/1/e/6/f/2/1e6f2fa1119d47b853219feb91c3867d411e0c8c02476d1e769171e8 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/1/e/6/f/2/1e6f2fa1119d47b853219feb91c3867d411e0c8c02476d1e769171e8.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/1/e/6/f/2/1e6f2fa1119d47b853219feb91c3867d411e0c8c02476d1e769171e8.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/2/6/e/2/226e20ef37baae18486c4b142f19362b0176ef112b8135637169c623 b/.cache/pypoetry/cache/repositories/PyPI/_http/2/2/6/e/2/226e20ef37baae18486c4b142f19362b0176ef112b8135637169c623 new file mode 100644 index 0000000..971df67 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/2/2/6/e/2/226e20ef37baae18486c4b142f19362b0176ef112b8135637169c623 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/2/6/e/2/226e20ef37baae18486c4b142f19362b0176ef112b8135637169c623.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/2/2/6/e/2/226e20ef37baae18486c4b142f19362b0176ef112b8135637169c623.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/3/3/2/d/2332da5ae322eda2d300109efbb9a3f7c5a5f271def9b21f15ef9bc2 b/.cache/pypoetry/cache/repositories/PyPI/_http/2/3/3/2/d/2332da5ae322eda2d300109efbb9a3f7c5a5f271def9b21f15ef9bc2 new file mode 100644 index 0000000..e8dd41f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/2/3/3/2/d/2332da5ae322eda2d300109efbb9a3f7c5a5f271def9b21f15ef9bc2 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/3/3/2/d/2332da5ae322eda2d300109efbb9a3f7c5a5f271def9b21f15ef9bc2.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/2/3/3/2/d/2332da5ae322eda2d300109efbb9a3f7c5a5f271def9b21f15ef9bc2.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/a/a/e/b/2aaebf6b506fe2a373b26f648dfc83bfc153f7671297de820141e25a b/.cache/pypoetry/cache/repositories/PyPI/_http/2/a/a/e/b/2aaebf6b506fe2a373b26f648dfc83bfc153f7671297de820141e25a new file mode 100644 index 0000000..50970e5 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/2/a/a/e/b/2aaebf6b506fe2a373b26f648dfc83bfc153f7671297de820141e25a differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/a/a/e/b/2aaebf6b506fe2a373b26f648dfc83bfc153f7671297de820141e25a.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/2/a/a/e/b/2aaebf6b506fe2a373b26f648dfc83bfc153f7671297de820141e25a.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/b/0/b/3/2b0b3274f952f1ccb69a056b0f414c74a63cdf6658180795ba506c03 b/.cache/pypoetry/cache/repositories/PyPI/_http/2/b/0/b/3/2b0b3274f952f1ccb69a056b0f414c74a63cdf6658180795ba506c03 new file mode 100644 index 0000000..edebbc9 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/2/b/0/b/3/2b0b3274f952f1ccb69a056b0f414c74a63cdf6658180795ba506c03 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/b/0/b/3/2b0b3274f952f1ccb69a056b0f414c74a63cdf6658180795ba506c03.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/2/b/0/b/3/2b0b3274f952f1ccb69a056b0f414c74a63cdf6658180795ba506c03.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/e/1/8/4/2e184af86130f8c08775638bebc5ccc3418fae465912473baa679650 b/.cache/pypoetry/cache/repositories/PyPI/_http/2/e/1/8/4/2e184af86130f8c08775638bebc5ccc3418fae465912473baa679650 new file mode 100644 index 0000000..1492a0a Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/2/e/1/8/4/2e184af86130f8c08775638bebc5ccc3418fae465912473baa679650 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/2/e/1/8/4/2e184af86130f8c08775638bebc5ccc3418fae465912473baa679650.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/2/e/1/8/4/2e184af86130f8c08775638bebc5ccc3418fae465912473baa679650.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/3/6/f/9/336f9f13c7628bedacf7a6885f9d2b066972c99c0d09bf7d2d2d757c b/.cache/pypoetry/cache/repositories/PyPI/_http/3/3/6/f/9/336f9f13c7628bedacf7a6885f9d2b066972c99c0d09bf7d2d2d757c new file mode 100644 index 0000000..d6b1020 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/3/6/f/9/336f9f13c7628bedacf7a6885f9d2b066972c99c0d09bf7d2d2d757c differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/3/6/f/9/336f9f13c7628bedacf7a6885f9d2b066972c99c0d09bf7d2d2d757c.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/3/6/f/9/336f9f13c7628bedacf7a6885f9d2b066972c99c0d09bf7d2d2d757c.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/6/1/b/f/361bf053ab706a1212f013301d35a0c4b0af9cd8cc559d24c73d6a6f b/.cache/pypoetry/cache/repositories/PyPI/_http/3/6/1/b/f/361bf053ab706a1212f013301d35a0c4b0af9cd8cc559d24c73d6a6f new file mode 100644 index 0000000..e2ec81e Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/6/1/b/f/361bf053ab706a1212f013301d35a0c4b0af9cd8cc559d24c73d6a6f differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/6/1/b/f/361bf053ab706a1212f013301d35a0c4b0af9cd8cc559d24c73d6a6f.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/6/1/b/f/361bf053ab706a1212f013301d35a0c4b0af9cd8cc559d24c73d6a6f.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/7/1/4/f/3714f30e73096a644e391e0e450b37feb25efbd3d8f92311b4349dcf b/.cache/pypoetry/cache/repositories/PyPI/_http/3/7/1/4/f/3714f30e73096a644e391e0e450b37feb25efbd3d8f92311b4349dcf new file mode 100644 index 0000000..8c06a29 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/7/1/4/f/3714f30e73096a644e391e0e450b37feb25efbd3d8f92311b4349dcf differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/7/1/4/f/3714f30e73096a644e391e0e450b37feb25efbd3d8f92311b4349dcf.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/7/1/4/f/3714f30e73096a644e391e0e450b37feb25efbd3d8f92311b4349dcf.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/8/2/e/8/382e88e4c73e414191e8cb826e376246d0561d91b3595d3d620e4589 b/.cache/pypoetry/cache/repositories/PyPI/_http/3/8/2/e/8/382e88e4c73e414191e8cb826e376246d0561d91b3595d3d620e4589 new file mode 100644 index 0000000..3aec979 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/8/2/e/8/382e88e4c73e414191e8cb826e376246d0561d91b3595d3d620e4589 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/8/2/e/8/382e88e4c73e414191e8cb826e376246d0561d91b3595d3d620e4589.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/8/2/e/8/382e88e4c73e414191e8cb826e376246d0561d91b3595d3d620e4589.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/d/1/d/a/3d1da2fa07a4bf6ecc43788ce620181d5ed184e3f8a3f3f25a5c3344 b/.cache/pypoetry/cache/repositories/PyPI/_http/3/d/1/d/a/3d1da2fa07a4bf6ecc43788ce620181d5ed184e3f8a3f3f25a5c3344 new file mode 100644 index 0000000..f498f61 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/d/1/d/a/3d1da2fa07a4bf6ecc43788ce620181d5ed184e3f8a3f3f25a5c3344 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/d/1/d/a/3d1da2fa07a4bf6ecc43788ce620181d5ed184e3f8a3f3f25a5c3344.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/d/1/d/a/3d1da2fa07a4bf6ecc43788ce620181d5ed184e3f8a3f3f25a5c3344.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/f/d/d/2/3fdd2b71a28cf7bb5ce73cb583d37608c207d0c18b05e0b512299e62 b/.cache/pypoetry/cache/repositories/PyPI/_http/3/f/d/d/2/3fdd2b71a28cf7bb5ce73cb583d37608c207d0c18b05e0b512299e62 new file mode 100644 index 0000000..3b8c546 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/3/f/d/d/2/3fdd2b71a28cf7bb5ce73cb583d37608c207d0c18b05e0b512299e62 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/3/f/d/d/2/3fdd2b71a28cf7bb5ce73cb583d37608c207d0c18b05e0b512299e62.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/3/f/d/d/2/3fdd2b71a28cf7bb5ce73cb583d37608c207d0c18b05e0b512299e62.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/5/9/7/6/45976c194ebb9180bf7410d422bcad653ce13bcb64dda0d07d72b09d b/.cache/pypoetry/cache/repositories/PyPI/_http/4/5/9/7/6/45976c194ebb9180bf7410d422bcad653ce13bcb64dda0d07d72b09d new file mode 100644 index 0000000..c6b1f88 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/4/5/9/7/6/45976c194ebb9180bf7410d422bcad653ce13bcb64dda0d07d72b09d differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/5/9/7/6/45976c194ebb9180bf7410d422bcad653ce13bcb64dda0d07d72b09d.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/4/5/9/7/6/45976c194ebb9180bf7410d422bcad653ce13bcb64dda0d07d72b09d.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/7/c/0/3/47c03a077e5bd515b19009580cb593c38214dc364f848f6489d9568e b/.cache/pypoetry/cache/repositories/PyPI/_http/4/7/c/0/3/47c03a077e5bd515b19009580cb593c38214dc364f848f6489d9568e new file mode 100644 index 0000000..08b4488 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/4/7/c/0/3/47c03a077e5bd515b19009580cb593c38214dc364f848f6489d9568e differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/7/c/0/3/47c03a077e5bd515b19009580cb593c38214dc364f848f6489d9568e.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/4/7/c/0/3/47c03a077e5bd515b19009580cb593c38214dc364f848f6489d9568e.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 b/.cache/pypoetry/cache/repositories/PyPI/_http/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 new file mode 100644 index 0000000..dbdd852 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/4/b/e/0/7/4be07d3ac353e38d2c9e3a257cad36ee2a758fc88b4cd5f0d479a5a9.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 b/.cache/pypoetry/cache/repositories/PyPI/_http/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 new file mode 100644 index 0000000..5204c34 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/4/d/c/0/7/4dc07ffa7cae87ee16d69571b2c74399b5b61681aa545c7822dd5b40.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/f/b/8/2/4fb82b8ca70b2cdc81634bb562c2843c21856fdb7e8af70131ff293a b/.cache/pypoetry/cache/repositories/PyPI/_http/4/f/b/8/2/4fb82b8ca70b2cdc81634bb562c2843c21856fdb7e8af70131ff293a new file mode 100644 index 0000000..d81b5fd Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/4/f/b/8/2/4fb82b8ca70b2cdc81634bb562c2843c21856fdb7e8af70131ff293a differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/4/f/b/8/2/4fb82b8ca70b2cdc81634bb562c2843c21856fdb7e8af70131ff293a.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/4/f/b/8/2/4fb82b8ca70b2cdc81634bb562c2843c21856fdb7e8af70131ff293a.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/6/9/9/0/56990a4cac41e1869b6cb22f73df04219326c4eff431c47e6ebee12f b/.cache/pypoetry/cache/repositories/PyPI/_http/5/6/9/9/0/56990a4cac41e1869b6cb22f73df04219326c4eff431c47e6ebee12f new file mode 100644 index 0000000..73700a1 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/5/6/9/9/0/56990a4cac41e1869b6cb22f73df04219326c4eff431c47e6ebee12f differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/6/9/9/0/56990a4cac41e1869b6cb22f73df04219326c4eff431c47e6ebee12f.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/5/6/9/9/0/56990a4cac41e1869b6cb22f73df04219326c4eff431c47e6ebee12f.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/5/7/6/5b576ddc69cb2cda42f61333b85f066e4990a5eab5c96e0670cbb445 b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/5/7/6/5b576ddc69cb2cda42f61333b85f066e4990a5eab5c96e0670cbb445 new file mode 100644 index 0000000..4696fd9 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/5/7/6/5b576ddc69cb2cda42f61333b85f066e4990a5eab5c96e0670cbb445 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/5/7/6/5b576ddc69cb2cda42f61333b85f066e4990a5eab5c96e0670cbb445.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/5/7/6/5b576ddc69cb2cda42f61333b85f066e4990a5eab5c96e0670cbb445.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 new file mode 100644 index 0000000..c4d066f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/5/b/d/8/9/5bd894eeb3dfe1c8aaee1daecdfb74bbb314293813a730238621f077.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead b/.cache/pypoetry/cache/repositories/PyPI/_http/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead new file mode 100644 index 0000000..1e050a0 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/5/c/4/0/d/5c40d879a553c082925ea33febe3c13e7621ebe162c95d31a21b2ead.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/f/1/8/c/5f18c440a962338cd7865a20cbf7a8caf757a7349ab50d374d168b4d b/.cache/pypoetry/cache/repositories/PyPI/_http/5/f/1/8/c/5f18c440a962338cd7865a20cbf7a8caf757a7349ab50d374d168b4d new file mode 100644 index 0000000..9ba8e66 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/5/f/1/8/c/5f18c440a962338cd7865a20cbf7a8caf757a7349ab50d374d168b4d differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/5/f/1/8/c/5f18c440a962338cd7865a20cbf7a8caf757a7349ab50d374d168b4d.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/5/f/1/8/c/5f18c440a962338cd7865a20cbf7a8caf757a7349ab50d374d168b4d.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 new file mode 100644 index 0000000..7aeb7cf Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/7/4/8/61748e2ceaf79fce181eac361dbebb2ef6577d27355dc28fdc216790 b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/7/4/8/61748e2ceaf79fce181eac361dbebb2ef6577d27355dc28fdc216790 new file mode 100644 index 0000000..46698fc Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/7/4/8/61748e2ceaf79fce181eac361dbebb2ef6577d27355dc28fdc216790 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/7/4/8/61748e2ceaf79fce181eac361dbebb2ef6577d27355dc28fdc216790.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/6/1/7/4/8/61748e2ceaf79fce181eac361dbebb2ef6577d27355dc28fdc216790.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/3/9/7/d/6397df560c064e29bbe8227193da91ad752f036dabfe75cd7707e250 b/.cache/pypoetry/cache/repositories/PyPI/_http/6/3/9/7/d/6397df560c064e29bbe8227193da91ad752f036dabfe75cd7707e250 new file mode 100644 index 0000000..e91e1e6 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/6/3/9/7/d/6397df560c064e29bbe8227193da91ad752f036dabfe75cd7707e250 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/3/9/7/d/6397df560c064e29bbe8227193da91ad752f036dabfe75cd7707e250.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/6/3/9/7/d/6397df560c064e29bbe8227193da91ad752f036dabfe75cd7707e250.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/9/7/3/6/69736ceec14c1619097216c13161f4c5b961f886f410d18cddb89b31 b/.cache/pypoetry/cache/repositories/PyPI/_http/6/9/7/3/6/69736ceec14c1619097216c13161f4c5b961f886f410d18cddb89b31 new file mode 100644 index 0000000..0fdace2 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/6/9/7/3/6/69736ceec14c1619097216c13161f4c5b961f886f410d18cddb89b31 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/6/9/7/3/6/69736ceec14c1619097216c13161f4c5b961f886f410d18cddb89b31.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/6/9/7/3/6/69736ceec14c1619097216c13161f4c5b961f886f410d18cddb89b31.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/7/4/c/2/3/74c23efaba0aefeec9df19b9bcb8d8a54489122b5468dda04fb92810 b/.cache/pypoetry/cache/repositories/PyPI/_http/7/4/c/2/3/74c23efaba0aefeec9df19b9bcb8d8a54489122b5468dda04fb92810 new file mode 100644 index 0000000..1daccaf Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/7/4/c/2/3/74c23efaba0aefeec9df19b9bcb8d8a54489122b5468dda04fb92810 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/7/4/c/2/3/74c23efaba0aefeec9df19b9bcb8d8a54489122b5468dda04fb92810.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/7/4/c/2/3/74c23efaba0aefeec9df19b9bcb8d8a54489122b5468dda04fb92810.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/7/6/0/e/8/760e82d05ae1c5a0dc2aceab8cd6f19922aba519a6de6de2028fa9c6 b/.cache/pypoetry/cache/repositories/PyPI/_http/7/6/0/e/8/760e82d05ae1c5a0dc2aceab8cd6f19922aba519a6de6de2028fa9c6 new file mode 100644 index 0000000..b1dc16a Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/7/6/0/e/8/760e82d05ae1c5a0dc2aceab8cd6f19922aba519a6de6de2028fa9c6 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/7/6/0/e/8/760e82d05ae1c5a0dc2aceab8cd6f19922aba519a6de6de2028fa9c6.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/7/6/0/e/8/760e82d05ae1c5a0dc2aceab8cd6f19922aba519a6de6de2028fa9c6.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/5/3/8/5/85385fd8a53d3d88ac67db4bf015be7ba931f024227668d42969ea92 b/.cache/pypoetry/cache/repositories/PyPI/_http/8/5/3/8/5/85385fd8a53d3d88ac67db4bf015be7ba931f024227668d42969ea92 new file mode 100644 index 0000000..b484241 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/8/5/3/8/5/85385fd8a53d3d88ac67db4bf015be7ba931f024227668d42969ea92 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/5/3/8/5/85385fd8a53d3d88ac67db4bf015be7ba931f024227668d42969ea92.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/8/5/3/8/5/85385fd8a53d3d88ac67db4bf015be7ba931f024227668d42969ea92.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b b/.cache/pypoetry/cache/repositories/PyPI/_http/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b new file mode 100644 index 0000000..f058a7c Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/8/a/c/4/d/8ac4d14dc45e27d21da49fb515570b6f875b78707de9b08ce1088d1b.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c b/.cache/pypoetry/cache/repositories/PyPI/_http/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c new file mode 100644 index 0000000..0fa872f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/8/b/2/4/2/8b24226e2da88df4abeee0d8ca6bce79b19ca2bcd5f94b543939c66c.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/c/6/9/3/8c69355026e9628255b060ae6953b74c192c8dd878dbae81cd0b0ffa b/.cache/pypoetry/cache/repositories/PyPI/_http/8/c/6/9/3/8c69355026e9628255b060ae6953b74c192c8dd878dbae81cd0b0ffa new file mode 100644 index 0000000..2a79479 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/8/c/6/9/3/8c69355026e9628255b060ae6953b74c192c8dd878dbae81cd0b0ffa differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/c/6/9/3/8c69355026e9628255b060ae6953b74c192c8dd878dbae81cd0b0ffa.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/8/c/6/9/3/8c69355026e9628255b060ae6953b74c192c8dd878dbae81cd0b0ffa.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 b/.cache/pypoetry/cache/repositories/PyPI/_http/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 new file mode 100644 index 0000000..5def2d0 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/8/d/0/e/1/8d0e104919449355aedb55b5b546bb9fd53f0e1a8ca1b082109464e9.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/9/4/5/2/4/9452417670b2e75527d793717779fce5d54e5f21adb3344c273876fa b/.cache/pypoetry/cache/repositories/PyPI/_http/9/4/5/2/4/9452417670b2e75527d793717779fce5d54e5f21adb3344c273876fa new file mode 100644 index 0000000..7991f6d Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/9/4/5/2/4/9452417670b2e75527d793717779fce5d54e5f21adb3344c273876fa differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/9/4/5/2/4/9452417670b2e75527d793717779fce5d54e5f21adb3344c273876fa.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/9/4/5/2/4/9452417670b2e75527d793717779fce5d54e5f21adb3344c273876fa.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/9/c/c/6/9/9cc6939c61442ece13bdcac1a4f63ac9be8b2e7a5a8148e85ca678e2 b/.cache/pypoetry/cache/repositories/PyPI/_http/9/c/c/6/9/9cc6939c61442ece13bdcac1a4f63ac9be8b2e7a5a8148e85ca678e2 new file mode 100644 index 0000000..a4b7a50 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/9/c/c/6/9/9cc6939c61442ece13bdcac1a4f63ac9be8b2e7a5a8148e85ca678e2 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/9/c/c/6/9/9cc6939c61442ece13bdcac1a4f63ac9be8b2e7a5a8148e85ca678e2.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/9/c/c/6/9/9cc6939c61442ece13bdcac1a4f63ac9be8b2e7a5a8148e85ca678e2.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/2/3/c/d/a23cd4a4fae73d66673863723f969bb7a50a9ef420ff98b7dceb7cdf b/.cache/pypoetry/cache/repositories/PyPI/_http/a/2/3/c/d/a23cd4a4fae73d66673863723f969bb7a50a9ef420ff98b7dceb7cdf new file mode 100644 index 0000000..8015d5d Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/a/2/3/c/d/a23cd4a4fae73d66673863723f969bb7a50a9ef420ff98b7dceb7cdf differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/2/3/c/d/a23cd4a4fae73d66673863723f969bb7a50a9ef420ff98b7dceb7cdf.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/a/2/3/c/d/a23cd4a4fae73d66673863723f969bb7a50a9ef420ff98b7dceb7cdf.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/3/0/8/7/a30872b5716950b4b29b0627ee9679eeefc2f88505234033431621a1 b/.cache/pypoetry/cache/repositories/PyPI/_http/a/3/0/8/7/a30872b5716950b4b29b0627ee9679eeefc2f88505234033431621a1 new file mode 100644 index 0000000..bea06ff Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/a/3/0/8/7/a30872b5716950b4b29b0627ee9679eeefc2f88505234033431621a1 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/3/0/8/7/a30872b5716950b4b29b0627ee9679eeefc2f88505234033431621a1.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/a/3/0/8/7/a30872b5716950b4b29b0627ee9679eeefc2f88505234033431621a1.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/4/6/b/7/a46b74c1407dd55ebf9eeb7eb2c73000028b7639a6ed9edc7981950c b/.cache/pypoetry/cache/repositories/PyPI/_http/a/4/6/b/7/a46b74c1407dd55ebf9eeb7eb2c73000028b7639a6ed9edc7981950c new file mode 100644 index 0000000..beb04d7 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/a/4/6/b/7/a46b74c1407dd55ebf9eeb7eb2c73000028b7639a6ed9edc7981950c differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/4/6/b/7/a46b74c1407dd55ebf9eeb7eb2c73000028b7639a6ed9edc7981950c.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/a/4/6/b/7/a46b74c1407dd55ebf9eeb7eb2c73000028b7639a6ed9edc7981950c.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/0/2/4/a9024e4fd61712bdecba6efabce67d49059b91f9c0282300361cd476 b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/0/2/4/a9024e4fd61712bdecba6efabce67d49059b91f9c0282300361cd476 new file mode 100644 index 0000000..baa5931 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/0/2/4/a9024e4fd61712bdecba6efabce67d49059b91f9c0282300361cd476 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/0/2/4/a9024e4fd61712bdecba6efabce67d49059b91f9c0282300361cd476.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/0/2/4/a9024e4fd61712bdecba6efabce67d49059b91f9c0282300361cd476.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/5/f/3/a95f362f2902910922431ce7b76d7c68de2e5cc12088dff7535174b1 b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/5/f/3/a95f362f2902910922431ce7b76d7c68de2e5cc12088dff7535174b1 new file mode 100644 index 0000000..51acdd6 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/5/f/3/a95f362f2902910922431ce7b76d7c68de2e5cc12088dff7535174b1 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/5/f/3/a95f362f2902910922431ce7b76d7c68de2e5cc12088dff7535174b1.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/a/9/5/f/3/a95f362f2902910922431ce7b76d7c68de2e5cc12088dff7535174b1.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/3/6/8/a/b368ad2366cf29335388a6d0c366d3ad5d990d0fe960c1677d5e51b8 b/.cache/pypoetry/cache/repositories/PyPI/_http/b/3/6/8/a/b368ad2366cf29335388a6d0c366d3ad5d990d0fe960c1677d5e51b8 new file mode 100644 index 0000000..6f2ebe6 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/b/3/6/8/a/b368ad2366cf29335388a6d0c366d3ad5d990d0fe960c1677d5e51b8 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/3/6/8/a/b368ad2366cf29335388a6d0c366d3ad5d990d0fe960c1677d5e51b8.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/b/3/6/8/a/b368ad2366cf29335388a6d0c366d3ad5d990d0fe960c1677d5e51b8.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/8/6/9/e/b869e106dba85c81d88c22370d8a79d9d5a91114b5ee15fc05ca6970 b/.cache/pypoetry/cache/repositories/PyPI/_http/b/8/6/9/e/b869e106dba85c81d88c22370d8a79d9d5a91114b5ee15fc05ca6970 new file mode 100644 index 0000000..be79e84 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/b/8/6/9/e/b869e106dba85c81d88c22370d8a79d9d5a91114b5ee15fc05ca6970 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/8/6/9/e/b869e106dba85c81d88c22370d8a79d9d5a91114b5ee15fc05ca6970.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/b/8/6/9/e/b869e106dba85c81d88c22370d8a79d9d5a91114b5ee15fc05ca6970.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc b/.cache/pypoetry/cache/repositories/PyPI/_http/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc new file mode 100644 index 0000000..1e9451a Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/b/f/d/6/3/bfd6322b8f4c82d6b7ed54608512187e126478b3b0851b74973f4adc.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/1/1/5/a/c115af6e1dcb6f8536a6040907a266f2edede58de741819d6eccc5ee b/.cache/pypoetry/cache/repositories/PyPI/_http/c/1/1/5/a/c115af6e1dcb6f8536a6040907a266f2edede58de741819d6eccc5ee new file mode 100644 index 0000000..f94164a Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/c/1/1/5/a/c115af6e1dcb6f8536a6040907a266f2edede58de741819d6eccc5ee differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/1/1/5/a/c115af6e1dcb6f8536a6040907a266f2edede58de741819d6eccc5ee.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/c/1/1/5/a/c115af6e1dcb6f8536a6040907a266f2edede58de741819d6eccc5ee.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c b/.cache/pypoetry/cache/repositories/PyPI/_http/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c new file mode 100644 index 0000000..a858b6b Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/c/4/2/0/3/c4203c5060d0f778eafd521050eab23e6aaa0f9eefa17eb1e671212c.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/a/1/1/a/ca11a1ad84a648c9a7ed607c40e4e702086d4fc18cef6171e93e142d b/.cache/pypoetry/cache/repositories/PyPI/_http/c/a/1/1/a/ca11a1ad84a648c9a7ed607c40e4e702086d4fc18cef6171e93e142d new file mode 100644 index 0000000..ab4f75b Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/c/a/1/1/a/ca11a1ad84a648c9a7ed607c40e4e702086d4fc18cef6171e93e142d differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/a/1/1/a/ca11a1ad84a648c9a7ed607c40e4e702086d4fc18cef6171e93e142d.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/c/a/1/1/a/ca11a1ad84a648c9a7ed607c40e4e702086d4fc18cef6171e93e142d.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 b/.cache/pypoetry/cache/repositories/PyPI/_http/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 new file mode 100644 index 0000000..a9449e0 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/c/b/5/2/a/cb52a2d86b549893dbebd3c41cb632b74900e6546f238508e1029e00.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/0/f/1/2/d0f123fd04f4d39dc01d91a96a47c3af51dfcd3b03a212cabe3463b2 b/.cache/pypoetry/cache/repositories/PyPI/_http/d/0/f/1/2/d0f123fd04f4d39dc01d91a96a47c3af51dfcd3b03a212cabe3463b2 new file mode 100644 index 0000000..a0fb876 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/d/0/f/1/2/d0f123fd04f4d39dc01d91a96a47c3af51dfcd3b03a212cabe3463b2 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/0/f/1/2/d0f123fd04f4d39dc01d91a96a47c3af51dfcd3b03a212cabe3463b2.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/d/0/f/1/2/d0f123fd04f4d39dc01d91a96a47c3af51dfcd3b03a212cabe3463b2.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/5/6/9/6/d5696d321049dcbc935d461f2b4c811a847de11bae7c3098e3048d66 b/.cache/pypoetry/cache/repositories/PyPI/_http/d/5/6/9/6/d5696d321049dcbc935d461f2b4c811a847de11bae7c3098e3048d66 new file mode 100644 index 0000000..b0ddf19 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/d/5/6/9/6/d5696d321049dcbc935d461f2b4c811a847de11bae7c3098e3048d66 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/5/6/9/6/d5696d321049dcbc935d461f2b4c811a847de11bae7c3098e3048d66.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/d/5/6/9/6/d5696d321049dcbc935d461f2b4c811a847de11bae7c3098e3048d66.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc b/.cache/pypoetry/cache/repositories/PyPI/_http/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc new file mode 100644 index 0000000..8c8ebd3 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/d/b/8/2/4/db824c526d765b86c60e3a8f549e4d9f6b2c1e8876b000670174e0bc.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 b/.cache/pypoetry/cache/repositories/PyPI/_http/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 new file mode 100644 index 0000000..5bf294f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/d/e/4/3/7/de437938ef85929246af957191da12fbc37876e8e6bf1a090f59e1d8.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/f/f/5/e/dff5e4c5cea174493abc3c33b4db9275d06319301697144eda7e3e82 b/.cache/pypoetry/cache/repositories/PyPI/_http/d/f/f/5/e/dff5e4c5cea174493abc3c33b4db9275d06319301697144eda7e3e82 new file mode 100644 index 0000000..4ad72bb Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/d/f/f/5/e/dff5e4c5cea174493abc3c33b4db9275d06319301697144eda7e3e82 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/d/f/f/5/e/dff5e4c5cea174493abc3c33b4db9275d06319301697144eda7e3e82.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/d/f/f/5/e/dff5e4c5cea174493abc3c33b4db9275d06319301697144eda7e3e82.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/1/3/f/a/e13fa9b0f8346e0f061ccb4309b60f319a230b2995f2ef4ca6bd28e5 b/.cache/pypoetry/cache/repositories/PyPI/_http/e/1/3/f/a/e13fa9b0f8346e0f061ccb4309b60f319a230b2995f2ef4ca6bd28e5 new file mode 100644 index 0000000..dc4dafd Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/e/1/3/f/a/e13fa9b0f8346e0f061ccb4309b60f319a230b2995f2ef4ca6bd28e5 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/1/3/f/a/e13fa9b0f8346e0f061ccb4309b60f319a230b2995f2ef4ca6bd28e5.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/e/1/3/f/a/e13fa9b0f8346e0f061ccb4309b60f319a230b2995f2ef4ca6bd28e5.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc b/.cache/pypoetry/cache/repositories/PyPI/_http/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc new file mode 100644 index 0000000..55bdf3f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/e/2/0/7/4/e207426cbcf220918fdf354826064190a066f96cd6ad8830f1f11fdc.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 b/.cache/pypoetry/cache/repositories/PyPI/_http/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 new file mode 100644 index 0000000..6791f11 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/e/a/c/6/1/eac61126daf80149d2a016f12a54eab5e3b5c1dbc77410ff1a97edc4.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/c/e/a/d/ecead28af6a7c1785ea4b039c03190e283fa5a04a7d62bd69a19525e b/.cache/pypoetry/cache/repositories/PyPI/_http/e/c/e/a/d/ecead28af6a7c1785ea4b039c03190e283fa5a04a7d62bd69a19525e new file mode 100644 index 0000000..6d0a0d9 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/e/c/e/a/d/ecead28af6a7c1785ea4b039c03190e283fa5a04a7d62bd69a19525e differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/c/e/a/d/ecead28af6a7c1785ea4b039c03190e283fa5a04a7d62bd69a19525e.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/e/c/e/a/d/ecead28af6a7c1785ea4b039c03190e283fa5a04a7d62bd69a19525e.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/e/2/4/b/ee24bc3de6d4893488df4593880132e374d595c0a3124d1ca1e62cf7 b/.cache/pypoetry/cache/repositories/PyPI/_http/e/e/2/4/b/ee24bc3de6d4893488df4593880132e374d595c0a3124d1ca1e62cf7 new file mode 100644 index 0000000..59a8129 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/e/e/2/4/b/ee24bc3de6d4893488df4593880132e374d595c0a3124d1ca1e62cf7 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/e/e/2/4/b/ee24bc3de6d4893488df4593880132e374d595c0a3124d1ca1e62cf7.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/e/e/2/4/b/ee24bc3de6d4893488df4593880132e374d595c0a3124d1ca1e62cf7.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/1/f/a/1/f1fa10111568cf3977cbca1285a636edbe346634bc9150a243be83a6 b/.cache/pypoetry/cache/repositories/PyPI/_http/f/1/f/a/1/f1fa10111568cf3977cbca1285a636edbe346634bc9150a243be83a6 new file mode 100644 index 0000000..876cfac Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/f/1/f/a/1/f1fa10111568cf3977cbca1285a636edbe346634bc9150a243be83a6 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/1/f/a/1/f1fa10111568cf3977cbca1285a636edbe346634bc9150a243be83a6.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/f/1/f/a/1/f1fa10111568cf3977cbca1285a636edbe346634bc9150a243be83a6.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/2/a/2/3/f2a2370e1b94dff837e3d43805ca85317ab1e38b93a46eb9d36c2d94 b/.cache/pypoetry/cache/repositories/PyPI/_http/f/2/a/2/3/f2a2370e1b94dff837e3d43805ca85317ab1e38b93a46eb9d36c2d94 new file mode 100644 index 0000000..5af2d1f Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/f/2/a/2/3/f2a2370e1b94dff837e3d43805ca85317ab1e38b93a46eb9d36c2d94 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/2/a/2/3/f2a2370e1b94dff837e3d43805ca85317ab1e38b93a46eb9d36c2d94.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/f/2/a/2/3/f2a2370e1b94dff837e3d43805ca85317ab1e38b93a46eb9d36c2d94.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/7/6/5/3/f765391cde3eb8159dd2a8c476b022a7696cfd9495bf129ab8f32a5b b/.cache/pypoetry/cache/repositories/PyPI/_http/f/7/6/5/3/f765391cde3eb8159dd2a8c476b022a7696cfd9495bf129ab8f32a5b new file mode 100644 index 0000000..55e37ad Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/f/7/6/5/3/f765391cde3eb8159dd2a8c476b022a7696cfd9495bf129ab8f32a5b differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/7/6/5/3/f765391cde3eb8159dd2a8c476b022a7696cfd9495bf129ab8f32a5b.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/f/7/6/5/3/f765391cde3eb8159dd2a8c476b022a7696cfd9495bf129ab8f32a5b.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/9/b/4/d/f9b4da9cfb3237f2ef95003914fb133219d929f5f348fc09620676a9 b/.cache/pypoetry/cache/repositories/PyPI/_http/f/9/b/4/d/f9b4da9cfb3237f2ef95003914fb133219d929f5f348fc09620676a9 new file mode 100644 index 0000000..2dcc253 Binary files /dev/null and b/.cache/pypoetry/cache/repositories/PyPI/_http/f/9/b/4/d/f9b4da9cfb3237f2ef95003914fb133219d929f5f348fc09620676a9 differ diff --git a/.cache/pypoetry/cache/repositories/PyPI/_http/f/9/b/4/d/f9b4da9cfb3237f2ef95003914fb133219d929f5f348fc09620676a9.lock b/.cache/pypoetry/cache/repositories/PyPI/_http/f/9/b/4/d/f9b4da9cfb3237f2ef95003914fb133219d929f5f348fc09620676a9.lock new file mode 100644 index 0000000..e69de29 diff --git a/.cache/pypoetry/cache/repositories/PyPI/a4/f1/40/59/b7/00/eb/ff/a4f14059b700ebff01c7b3b8441e623ec02ba897f8412e6fd8a673a5712dceca b/.cache/pypoetry/cache/repositories/PyPI/a4/f1/40/59/b7/00/eb/ff/a4f14059b700ebff01c7b3b8441e623ec02ba897f8412e6fd8a673a5712dceca new file mode 100644 index 0000000..c1e06b5 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/a4/f1/40/59/b7/00/eb/ff/a4f14059b700ebff01c7b3b8441e623ec02ba897f8412e6fd8a673a5712dceca @@ -0,0 +1 @@ +9999999999{"name": "colorama", "version": "0.4.6", "summary": "Cross-platform colored terminal text.", "requires_dist": null, "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7", "files": [{"file": "colorama-0.4.6-py2.py3-none-any.whl", "hash": "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {"file": "colorama-0.4.6.tar.gz", "hash": "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/a8/42/65/0b/9b/e5/3f/fe/a842650b9be53ffe25c41924cb1a0c5d16d95836f148f58f2bc17a1e1ec27a85 b/.cache/pypoetry/cache/repositories/PyPI/a8/42/65/0b/9b/e5/3f/fe/a842650b9be53ffe25c41924cb1a0c5d16d95836f148f58f2bc17a1e1ec27a85 new file mode 100644 index 0000000..c099d97 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/a8/42/65/0b/9b/e5/3f/fe/a842650b9be53ffe25c41924cb1a0c5d16d95836f148f58f2bc17a1e1ec27a85 @@ -0,0 +1 @@ +9999999999{"name": "multidict", "version": "6.0.5", "summary": "multidict implementation", "requires_dist": null, "requires_python": ">=3.7", "files": [{"file": "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, {"file": "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, {"file": "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, {"file": "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, {"file": "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, {"file": "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, {"file": "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, {"file": "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, {"file": "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, {"file": "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, {"file": "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", "hash": "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, {"file": "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", "hash": "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, {"file": "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, {"file": "multidict-6.0.5-cp310-cp310-win32.whl", "hash": "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, {"file": "multidict-6.0.5-cp310-cp310-win_amd64.whl", "hash": "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, {"file": "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, {"file": "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, {"file": "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, {"file": "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, {"file": "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, {"file": "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, {"file": "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, {"file": "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, {"file": "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, {"file": "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, {"file": "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", "hash": "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, {"file": "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", "hash": "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, {"file": "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, {"file": "multidict-6.0.5-cp311-cp311-win32.whl", "hash": "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, {"file": "multidict-6.0.5-cp311-cp311-win_amd64.whl", "hash": "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, {"file": "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, {"file": "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, {"file": "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, {"file": "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, {"file": "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, {"file": "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, {"file": "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, {"file": "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, {"file": "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, {"file": "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, {"file": "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", "hash": "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, {"file": "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", "hash": "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, {"file": "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, {"file": "multidict-6.0.5-cp312-cp312-win32.whl", "hash": "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, {"file": "multidict-6.0.5-cp312-cp312-win_amd64.whl", "hash": "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, {"file": "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", "hash": "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, {"file": "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, {"file": "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, {"file": "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, {"file": "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, {"file": "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, {"file": "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", "hash": "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, {"file": "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", "hash": "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, {"file": "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", "hash": "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, {"file": "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", "hash": "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, {"file": "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", "hash": "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, {"file": "multidict-6.0.5-cp37-cp37m-win32.whl", "hash": "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, {"file": "multidict-6.0.5-cp37-cp37m-win_amd64.whl", "hash": "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, {"file": "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, {"file": "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, {"file": "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", "hash": "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, {"file": "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, {"file": "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, {"file": "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, {"file": "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, {"file": "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, {"file": "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, {"file": "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, {"file": "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", "hash": "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, {"file": "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", "hash": "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, {"file": "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, {"file": "multidict-6.0.5-cp38-cp38-win32.whl", "hash": "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, {"file": "multidict-6.0.5-cp38-cp38-win_amd64.whl", "hash": "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, {"file": "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, {"file": "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, {"file": "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, {"file": "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, {"file": "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, {"file": "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, {"file": "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, {"file": "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, {"file": "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, {"file": "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, {"file": "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", "hash": "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, {"file": "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", "hash": "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, {"file": "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, {"file": "multidict-6.0.5-cp39-cp39-win32.whl", "hash": "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, {"file": "multidict-6.0.5-cp39-cp39-win_amd64.whl", "hash": "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, {"file": "multidict-6.0.5-py3-none-any.whl", "hash": "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, {"file": "multidict-6.0.5.tar.gz", "hash": "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/ae/6d/94/ee/70/5c/f7/f9/ae6d94ee705cf7f92a7ae0fecc52170d77f722ebdede0ba07c994628c1b3601f b/.cache/pypoetry/cache/repositories/PyPI/ae/6d/94/ee/70/5c/f7/f9/ae6d94ee705cf7f92a7ae0fecc52170d77f722ebdede0ba07c994628c1b3601f new file mode 100644 index 0000000..52083b5 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/ae/6d/94/ee/70/5c/f7/f9/ae6d94ee705cf7f92a7ae0fecc52170d77f722ebdede0ba07c994628c1b3601f @@ -0,0 +1 @@ +9999999999{"name": "aiohttp", "version": "3.9.3", "summary": "Async http client/server framework (asyncio)", "requires_dist": ["aiosignal >=1.1.2", "attrs >=17.3.0", "frozenlist >=1.1.1", "multidict <7.0,>=4.5", "yarl <2.0,>=1.0", "async-timeout <5.0,>=4.0 ; python_version < \"3.11\"", "brotlicffi ; (platform_python_implementation != \"CPython\") and extra == 'speedups'", "Brotli ; (platform_python_implementation == \"CPython\") and extra == 'speedups'", "aiodns ; (sys_platform == \"linux\" or sys_platform == \"darwin\") and extra == 'speedups'"], "requires_python": ">=3.8", "files": [{"file": "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, {"file": "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, {"file": "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, {"file": "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, {"file": "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, {"file": "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, {"file": "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, {"file": "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, {"file": "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, {"file": "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, {"file": "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", "hash": "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, {"file": "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", "hash": "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, {"file": "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, {"file": "aiohttp-3.9.3-cp310-cp310-win32.whl", "hash": "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, {"file": "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", "hash": "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, {"file": "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, {"file": "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, {"file": "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, {"file": "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, {"file": "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, {"file": "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, {"file": "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, {"file": "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, {"file": "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, {"file": "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, {"file": "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", "hash": "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, {"file": "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", "hash": "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, {"file": "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, {"file": "aiohttp-3.9.3-cp311-cp311-win32.whl", "hash": "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, {"file": "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", "hash": "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, {"file": "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, {"file": "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, {"file": "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, {"file": "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, {"file": "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, {"file": "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, {"file": "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, {"file": "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, {"file": "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, {"file": "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, {"file": "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", "hash": "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, {"file": "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", "hash": "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, {"file": "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, {"file": "aiohttp-3.9.3-cp312-cp312-win32.whl", "hash": "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, {"file": "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", "hash": "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, {"file": "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, {"file": "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, {"file": "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", "hash": "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, {"file": "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, {"file": "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, {"file": "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, {"file": "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, {"file": "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, {"file": "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, {"file": "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, {"file": "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", "hash": "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, {"file": "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", "hash": "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, {"file": "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, {"file": "aiohttp-3.9.3-cp38-cp38-win32.whl", "hash": "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, {"file": "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", "hash": "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, {"file": "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, {"file": "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, {"file": "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, {"file": "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, {"file": "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, {"file": "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, {"file": "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, {"file": "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, {"file": "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, {"file": "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, {"file": "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", "hash": "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, {"file": "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", "hash": "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, {"file": "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, {"file": "aiohttp-3.9.3-cp39-cp39-win32.whl", "hash": "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, {"file": "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", "hash": "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, {"file": "aiohttp-3.9.3.tar.gz", "hash": "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/c3/22/1a/3a/b9/13/8f/92/c3221a3ab9138f9297a102fc860464e73ca101e9cf5759b6fe2a5cc4a8699ffc b/.cache/pypoetry/cache/repositories/PyPI/c3/22/1a/3a/b9/13/8f/92/c3221a3ab9138f9297a102fc860464e73ca101e9cf5759b6fe2a5cc4a8699ffc new file mode 100644 index 0000000..6486f5a --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/c3/22/1a/3a/b9/13/8f/92/c3221a3ab9138f9297a102fc860464e73ca101e9cf5759b6fe2a5cc4a8699ffc @@ -0,0 +1 @@ +9999999999{"name": "attrs", "version": "23.2.0", "summary": "Classes Without Boilerplate", "requires_dist": ["importlib-metadata; python_version < '3.8'", "attrs[tests]; extra == 'cov'", "coverage[toml]>=5.3; extra == 'cov'", "attrs[tests]; extra == 'dev'", "pre-commit; extra == 'dev'", "furo; extra == 'docs'", "myst-parser; extra == 'docs'", "sphinx; extra == 'docs'", "sphinx-notfound-page; extra == 'docs'", "sphinxcontrib-towncrier; extra == 'docs'", "towncrier; extra == 'docs'", "zope-interface; extra == 'docs'", "attrs[tests-no-zope]; extra == 'tests'", "zope-interface; extra == 'tests'", "mypy>=1.6; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy'", "pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy'", "attrs[tests-mypy]; extra == 'tests-no-zope'", "cloudpickle; (platform_python_implementation == 'CPython') and extra == 'tests-no-zope'", "hypothesis; extra == 'tests-no-zope'", "pympler; extra == 'tests-no-zope'", "pytest-xdist[psutil]; extra == 'tests-no-zope'", "pytest>=4.3.0; extra == 'tests-no-zope'"], "requires_python": ">=3.7", "files": [{"file": "attrs-23.2.0-py3-none-any.whl", "hash": "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {"file": "attrs-23.2.0.tar.gz", "hash": "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/c9/23/e6/6c/9b/e8/43/93/c923e66c9be84393ea484083f2544b12d97adea910daa62432e68c49ee2a06ec b/.cache/pypoetry/cache/repositories/PyPI/c9/23/e6/6c/9b/e8/43/93/c923e66c9be84393ea484083f2544b12d97adea910daa62432e68c49ee2a06ec new file mode 100644 index 0000000..03d7a2d --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/c9/23/e6/6c/9b/e8/43/93/c923e66c9be84393ea484083f2544b12d97adea910daa62432e68c49ee2a06ec @@ -0,0 +1 @@ +9999999999{"name": "Werkzeug", "version": "3.0.2", "summary": "The comprehensive WSGI web application library.", "requires_dist": ["MarkupSafe>=2.1.1", "watchdog>=2.3; extra == \"watchdog\""], "requires_python": ">=3.8", "files": [{"file": "werkzeug-3.0.2-py3-none-any.whl", "hash": "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"}, {"file": "werkzeug-3.0.2.tar.gz", "hash": "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/d0/10/8d/4d/be/56/8f/7d/d0108d4dbe568f7d353815b238031cfea408dad47ed5bb23ee1a755e1c6dfc42 b/.cache/pypoetry/cache/repositories/PyPI/d0/10/8d/4d/be/56/8f/7d/d0108d4dbe568f7d353815b238031cfea408dad47ed5bb23ee1a755e1c6dfc42 new file mode 100644 index 0000000..fcf985c --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/d0/10/8d/4d/be/56/8f/7d/d0108d4dbe568f7d353815b238031cfea408dad47ed5bb23ee1a755e1c6dfc42 @@ -0,0 +1 @@ +9999999999{"name": "pycparser", "version": "2.22", "summary": "C parser in Python", "requires_dist": null, "requires_python": ">=3.8", "files": [{"file": "pycparser-2.22-py3-none-any.whl", "hash": "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {"file": "pycparser-2.22.tar.gz", "hash": "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/d9/d7/5a/5f/19/03/65/3c/d9d75a5f1903653c35696a7e695dfb40e7a15ad319533aca9a58ed9b576de275 b/.cache/pypoetry/cache/repositories/PyPI/d9/d7/5a/5f/19/03/65/3c/d9d75a5f1903653c35696a7e695dfb40e7a15ad319533aca9a58ed9b576de275 new file mode 100644 index 0000000..ea6326d --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/d9/d7/5a/5f/19/03/65/3c/d9d75a5f1903653c35696a7e695dfb40e7a15ad319533aca9a58ed9b576de275 @@ -0,0 +1 @@ +9999999999{"name": "argon2-cffi-bindings", "version": "21.2.0", "summary": "Low-level CFFI bindings for Argon2", "requires_dist": ["cffi (>=1.0.1)", "pytest ; extra == 'dev'", "cogapp ; extra == 'dev'", "pre-commit ; extra == 'dev'", "wheel ; extra == 'dev'", "pytest ; extra == 'tests'"], "requires_python": ">=3.6", "files": [{"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", "hash": "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", "hash": "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", "hash": "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", "hash": "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", "hash": "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, {"file": "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", "hash": "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, {"file": "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", "hash": "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, {"file": "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", "hash": "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, {"file": "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, {"file": "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, {"file": "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, {"file": "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", "hash": "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, {"file": "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", "hash": "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, {"file": "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, {"file": "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, {"file": "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, {"file": "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", "hash": "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, {"file": "argon2-cffi-bindings-21.2.0.tar.gz", "hash": "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/da/35/58/ac/a6/34/48/fb/da3558aca63448fb0561a01aec5edc704be381290bbb9184116c87cfeb1b08ab b/.cache/pypoetry/cache/repositories/PyPI/da/35/58/ac/a6/34/48/fb/da3558aca63448fb0561a01aec5edc704be381290bbb9184116c87cfeb1b08ab new file mode 100644 index 0000000..3f54c62 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/da/35/58/ac/a6/34/48/fb/da3558aca63448fb0561a01aec5edc704be381290bbb9184116c87cfeb1b08ab @@ -0,0 +1 @@ +9999999999{"name": "Werkzeug", "version": "3.0.1", "summary": "The comprehensive WSGI web application library.", "requires_dist": ["MarkupSafe>=2.1.1", "watchdog>=2.3 ; extra == \"watchdog\""], "requires_python": ">=3.8", "files": [{"file": "werkzeug-3.0.1-py3-none-any.whl", "hash": "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, {"file": "werkzeug-3.0.1.tar.gz", "hash": "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/e3/44/94/19/45/4a/4a/95/e3449419454a4a957f7441748b42d7e8f299a80b957b91180b31d33f00e565ff b/.cache/pypoetry/cache/repositories/PyPI/e3/44/94/19/45/4a/4a/95/e3449419454a4a957f7441748b42d7e8f299a80b957b91180b31d33f00e565ff new file mode 100644 index 0000000..bab2b2a --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/e3/44/94/19/45/4a/4a/95/e3449419454a4a957f7441748b42d7e8f299a80b957b91180b31d33f00e565ff @@ -0,0 +1 @@ +9999999999{"name": "requests", "version": "2.31.0", "summary": "Python HTTP for Humans.", "requires_dist": ["charset-normalizer (<4,>=2)", "idna (<4,>=2.5)", "urllib3 (<3,>=1.21.1)", "certifi (>=2017.4.17)", "PySocks (!=1.5.7,>=1.5.6) ; extra == 'socks'", "chardet (<6,>=3.0.2) ; extra == 'use_chardet_on_py3'"], "requires_python": ">=3.7", "files": [{"file": "requests-2.31.0-py3-none-any.whl", "hash": "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {"file": "requests-2.31.0.tar.gz", "hash": "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/e5/65/38/8d/bb/07/80/bf/e565388dbb0780bf35847a0365d0510683fa7a78b2759c174b47419b7b96fa5e b/.cache/pypoetry/cache/repositories/PyPI/e5/65/38/8d/bb/07/80/bf/e565388dbb0780bf35847a0365d0510683fa7a78b2759c174b47419b7b96fa5e new file mode 100644 index 0000000..503a61b --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/e5/65/38/8d/bb/07/80/bf/e565388dbb0780bf35847a0365d0510683fa7a78b2759c174b47419b7b96fa5e @@ -0,0 +1 @@ +9999999999{"name": "protobuf", "version": "4.25.3", "summary": "", "requires_dist": null, "requires_python": ">=3.8", "files": [{"file": "protobuf-4.25.3-cp310-abi3-win32.whl", "hash": "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, {"file": "protobuf-4.25.3-cp310-abi3-win_amd64.whl", "hash": "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, {"file": "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", "hash": "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, {"file": "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", "hash": "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, {"file": "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", "hash": "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, {"file": "protobuf-4.25.3-cp38-cp38-win32.whl", "hash": "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, {"file": "protobuf-4.25.3-cp38-cp38-win_amd64.whl", "hash": "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, {"file": "protobuf-4.25.3-cp39-cp39-win32.whl", "hash": "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, {"file": "protobuf-4.25.3-cp39-cp39-win_amd64.whl", "hash": "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, {"file": "protobuf-4.25.3-py3-none-any.whl", "hash": "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, {"file": "protobuf-4.25.3.tar.gz", "hash": "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/e6/e2/8e/f9/bd/33/57/b0/e6e28ef9bd3357b0be923c1785eb62139eb7ee2985bd14ce44f49da32f429954 b/.cache/pypoetry/cache/repositories/PyPI/e6/e2/8e/f9/bd/33/57/b0/e6e28ef9bd3357b0be923c1785eb62139eb7ee2985bd14ce44f49da32f429954 new file mode 100644 index 0000000..c965bc5 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/e6/e2/8e/f9/bd/33/57/b0/e6e28ef9bd3357b0be923c1785eb62139eb7ee2985bd14ce44f49da32f429954 @@ -0,0 +1 @@ +9999999999{"name": "charset-normalizer", "version": "3.3.2", "summary": "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet.", "requires_dist": null, "requires_python": ">=3.7.0", "files": [{"file": "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", "hash": "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", "hash": "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-win32.whl", "hash": "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {"file": "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", "hash": "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", "hash": "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", "hash": "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-win32.whl", "hash": "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {"file": "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", "hash": "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", "hash": "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", "hash": "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-win32.whl", "hash": "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {"file": "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", "hash": "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", "hash": "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", "hash": "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", "hash": "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", "hash": "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", "hash": "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", "hash": "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", "hash": "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {"file": "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", "hash": "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", "hash": "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", "hash": "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", "hash": "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-win32.whl", "hash": "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {"file": "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", "hash": "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", "hash": "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", "hash": "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-win32.whl", "hash": "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {"file": "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", "hash": "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {"file": "charset_normalizer-3.3.2-py3-none-any.whl", "hash": "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, {"file": "charset-normalizer-3.3.2.tar.gz", "hash": "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/e6/e6/bf/9b/b8/b1/79/0d/e6e6bf9bb8b1790db4a19d6bb41109538b3f5a02551df3596ecf3e7937a853e2 b/.cache/pypoetry/cache/repositories/PyPI/e6/e6/bf/9b/b8/b1/79/0d/e6e6bf9bb8b1790db4a19d6bb41109538b3f5a02551df3596ecf3e7937a853e2 new file mode 100644 index 0000000..fe794c8 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/e6/e6/bf/9b/b8/b1/79/0d/e6e6bf9bb8b1790db4a19d6bb41109538b3f5a02551df3596ecf3e7937a853e2 @@ -0,0 +1 @@ +9999999999{"name": "MarkupSafe", "version": "2.1.3", "summary": "Safely add untrusted strings to HTML/XML markup.", "requires_dist": null, "requires_python": ">=3.7", "files": [{"file": "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-win32.whl", "hash": "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, {"file": "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", "hash": "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-win32.whl", "hash": "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {"file": "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", "hash": "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-win32.whl", "hash": "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, {"file": "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", "hash": "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", "hash": "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", "hash": "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", "hash": "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", "hash": "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", "hash": "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, {"file": "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", "hash": "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-win32.whl", "hash": "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, {"file": "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", "hash": "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-win32.whl", "hash": "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, {"file": "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", "hash": "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, {"file": "MarkupSafe-2.1.3.tar.gz", "hash": "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/f0/40/0a/16/f0/fd/fc/c3/f0400a16f0fdfcc39f591849b8e9f903aca41140c7ba49eb8fb42dda814f4f4f b/.cache/pypoetry/cache/repositories/PyPI/f0/40/0a/16/f0/fd/fc/c3/f0400a16f0fdfcc39f591849b8e9f903aca41140c7ba49eb8fb42dda814f4f4f new file mode 100644 index 0000000..9e0cfcc --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/f0/40/0a/16/f0/fd/fc/c3/f0400a16f0fdfcc39f591849b8e9f903aca41140c7ba49eb8fb42dda814f4f4f @@ -0,0 +1 @@ +9999999999{"name": "iso8601", "version": "2.1.0", "summary": "Simple module to parse ISO 8601 dates", "requires_dist": null, "requires_python": ">=3.7,<4.0", "files": [{"file": "iso8601-2.1.0-py3-none-any.whl", "hash": "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242"}, {"file": "iso8601-2.1.0.tar.gz", "hash": "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/f1/ef/2b/37/e1/c1/27/07/f1ef2b37e1c127073cec189e5d4f36a61db12cdfee0cd0a658cda59eab0f24be b/.cache/pypoetry/cache/repositories/PyPI/f1/ef/2b/37/e1/c1/27/07/f1ef2b37e1c127073cec189e5d4f36a61db12cdfee0cd0a658cda59eab0f24be new file mode 100644 index 0000000..3a04af6 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/f1/ef/2b/37/e1/c1/27/07/f1ef2b37e1c127073cec189e5d4f36a61db12cdfee0cd0a658cda59eab0f24be @@ -0,0 +1 @@ +9999999999{"name": "argon2-cffi", "version": "23.1.0", "summary": "Argon2 for Python", "requires_dist": ["argon2-cffi-bindings", "typing-extensions; python_version < '3.8'", "argon2-cffi[tests,typing]; extra == 'dev'", "tox>4; extra == 'dev'", "furo; extra == 'docs'", "myst-parser; extra == 'docs'", "sphinx; extra == 'docs'", "sphinx-copybutton; extra == 'docs'", "sphinx-notfound-page; extra == 'docs'", "hypothesis; extra == 'tests'", "pytest; extra == 'tests'", "mypy; extra == 'typing'"], "requires_python": ">=3.7", "files": [{"file": "argon2_cffi-23.1.0-py3-none-any.whl", "hash": "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, {"file": "argon2_cffi-23.1.0.tar.gz", "hash": "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/f9/c3/ab/f7/0b/72/b5/6c/f9c3abf70b72b56c3981053da41520401bd136c669872b1b24bd9afe133b1ba7 b/.cache/pypoetry/cache/repositories/PyPI/f9/c3/ab/f7/0b/72/b5/6c/f9c3abf70b72b56c3981053da41520401bd136c669872b1b24bd9afe133b1ba7 new file mode 100644 index 0000000..b4cf503 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/f9/c3/ab/f7/0b/72/b5/6c/f9c3abf70b72b56c3981053da41520401bd136c669872b1b24bd9afe133b1ba7 @@ -0,0 +1 @@ +9999999999{"name": "click", "version": "8.1.7", "summary": "Composable command line interface toolkit", "requires_dist": ["colorama ; platform_system == \"Windows\"", "importlib-metadata ; python_version < \"3.8\""], "requires_python": ">=3.7", "files": [{"file": "click-8.1.7-py3-none-any.whl", "hash": "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {"file": "click-8.1.7.tar.gz", "hash": "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/f9/e6/bb/47/5f/4e/a0/07/f9e6bb475f4ea0075105c08662ac51adc811e99a5c7985faf9405785b8890a44 b/.cache/pypoetry/cache/repositories/PyPI/f9/e6/bb/47/5f/4e/a0/07/f9e6bb475f4ea0075105c08662ac51adc811e99a5c7985faf9405785b8890a44 new file mode 100644 index 0000000..732eaa1 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/f9/e6/bb/47/5f/4e/a0/07/f9e6bb475f4ea0075105c08662ac51adc811e99a5c7985faf9405785b8890a44 @@ -0,0 +1 @@ +9999999999{"name": "yarl", "version": "1.9.4", "summary": "Yet another URL library", "requires_dist": ["idna >=2.0", "multidict >=4.0", "typing-extensions >=3.7.4 ; python_version < \"3.8\""], "requires_python": ">=3.7", "files": [{"file": "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", "hash": "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, {"file": "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", "hash": "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, {"file": "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", "hash": "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, {"file": "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, {"file": "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, {"file": "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, {"file": "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, {"file": "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, {"file": "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", "hash": "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, {"file": "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", "hash": "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, {"file": "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", "hash": "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, {"file": "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", "hash": "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, {"file": "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", "hash": "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, {"file": "yarl-1.9.4-cp310-cp310-win32.whl", "hash": "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, {"file": "yarl-1.9.4-cp310-cp310-win_amd64.whl", "hash": "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, {"file": "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", "hash": "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, {"file": "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", "hash": "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, {"file": "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", "hash": "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, {"file": "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, {"file": "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, {"file": "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, {"file": "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, {"file": "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, {"file": "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", "hash": "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, {"file": "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", "hash": "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, {"file": "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", "hash": "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, {"file": "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", "hash": "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, {"file": "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", "hash": "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, {"file": "yarl-1.9.4-cp311-cp311-win32.whl", "hash": "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, {"file": "yarl-1.9.4-cp311-cp311-win_amd64.whl", "hash": "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, {"file": "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", "hash": "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, {"file": "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", "hash": "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, {"file": "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", "hash": "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, {"file": "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, {"file": "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, {"file": "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, {"file": "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, {"file": "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, {"file": "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", "hash": "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, {"file": "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", "hash": "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, {"file": "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", "hash": "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, {"file": "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", "hash": "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, {"file": "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", "hash": "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, {"file": "yarl-1.9.4-cp312-cp312-win32.whl", "hash": "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, {"file": "yarl-1.9.4-cp312-cp312-win_amd64.whl", "hash": "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, {"file": "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", "hash": "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, {"file": "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, {"file": "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, {"file": "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, {"file": "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, {"file": "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, {"file": "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", "hash": "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, {"file": "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", "hash": "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, {"file": "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", "hash": "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, {"file": "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", "hash": "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, {"file": "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", "hash": "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, {"file": "yarl-1.9.4-cp37-cp37m-win32.whl", "hash": "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, {"file": "yarl-1.9.4-cp37-cp37m-win_amd64.whl", "hash": "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, {"file": "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", "hash": "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, {"file": "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", "hash": "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, {"file": "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", "hash": "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, {"file": "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, {"file": "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, {"file": "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, {"file": "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, {"file": "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, {"file": "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", "hash": "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, {"file": "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", "hash": "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, {"file": "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", "hash": "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, {"file": "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", "hash": "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, {"file": "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", "hash": "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, {"file": "yarl-1.9.4-cp38-cp38-win32.whl", "hash": "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, {"file": "yarl-1.9.4-cp38-cp38-win_amd64.whl", "hash": "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, {"file": "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", "hash": "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, {"file": "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", "hash": "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, {"file": "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", "hash": "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, {"file": "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", "hash": "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, {"file": "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", "hash": "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, {"file": "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", "hash": "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, {"file": "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", "hash": "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, {"file": "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", "hash": "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, {"file": "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", "hash": "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, {"file": "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", "hash": "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, {"file": "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", "hash": "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, {"file": "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", "hash": "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, {"file": "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", "hash": "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, {"file": "yarl-1.9.4-cp39-cp39-win32.whl", "hash": "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, {"file": "yarl-1.9.4-cp39-cp39-win_amd64.whl", "hash": "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, {"file": "yarl-1.9.4-py3-none-any.whl", "hash": "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, {"file": "yarl-1.9.4.tar.gz", "hash": "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/pypoetry/cache/repositories/PyPI/fc/be/c9/63/66/ee/56/f9/fcbec96366ee56f96c6085438023374b84300fe071cf2f487868ba7d6d8db5d2 b/.cache/pypoetry/cache/repositories/PyPI/fc/be/c9/63/66/ee/56/f9/fcbec96366ee56f96c6085438023374b84300fe071cf2f487868ba7d6d8db5d2 new file mode 100644 index 0000000..8ebbbb9 --- /dev/null +++ b/.cache/pypoetry/cache/repositories/PyPI/fc/be/c9/63/66/ee/56/f9/fcbec96366ee56f96c6085438023374b84300fe071cf2f487868ba7d6d8db5d2 @@ -0,0 +1 @@ +9999999999{"name": "itsdangerous", "version": "2.1.2", "summary": "Safely pass data to untrusted environments and back.", "requires_dist": null, "requires_python": ">=3.7", "files": [{"file": "itsdangerous-2.1.2-py3-none-any.whl", "hash": "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, {"file": "itsdangerous-2.1.2.tar.gz", "hash": "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}], "yanked": false, "_cache_version": "2.0.0"} \ No newline at end of file diff --git a/.cache/replit/__replit_disk_meta.json b/.cache/replit/__replit_disk_meta.json new file mode 100644 index 0000000..43c83c6 --- /dev/null +++ b/.cache/replit/__replit_disk_meta.json @@ -0,0 +1 @@ +{"nonce":5730672987478511783,"last_updated":{"seconds":1699459121,"nanos":158469000}} diff --git a/.cache/replit/modules.stamp b/.cache/replit/modules.stamp new file mode 100644 index 0000000..e69de29 diff --git a/.cache/replit/modules/python-3.10_v59-20240329-787bc7d.res b/.cache/replit/modules/python-3.10_v59-20240329-787bc7d.res new file mode 100644 index 0000000..f5e108e --- /dev/null +++ b/.cache/replit/modules/python-3.10_v59-20240329-787bc7d.res @@ -0,0 +1 @@ +{"type":"resolve","resolvedModuleId":"python-3.10:v59-20240329-787bc7d","inputHash":"","resolutionPath":["python-3.10:v18-20230807-322e88b","python-3.10:v19-20230816-9932e6a","python-3.10:v20-20230824-f46249a","python-3.10:v21-20230831-f4ed402","python-3.10:v22-20230914-1095880","python-3.10:v23-20230918-15fb6e7","python-3.10:v24-20230920-bd784b9","python-3.10:v25-20230920-d4ad2e4","python-3.10:v26-20230926-7459cc2","python-3.10:v27-20231026-3dd69ae","python-3.10:v28-20231031-d0fd0d7","python-3.10:v29-20231107-a9f4961","python-3.10:v30-20231125-92f4109","python-3.10:v31-20231130-59c1f61","python-3.10:v32-20231201-3b22c78","python-3.10:v33-20231207-2f65342","python-3.10:v34-20231216-fb57c71","python-3.10:v35-20231229-5b1e996","python-3.10:v36-20240102-fd07a42","python-3.10:v37-20240103-6eb2819","python-3.10:v38-20240103-2da4911","python-3.10:v39-20240105-957ab14","python-3.10:v40-20240117-0bd73cd","python-3.10:v41-20240117-c740790","python-3.10:v42-20240125-d91747b","python-3.10:v43-20240201-2f48633","python-3.10:v44-20240206-fdbd396","python-3.10:v45-20240209-9e3a339","python-3.10:v46-20240214-9292ce3","python-3.10:v47-20240215-6dbeaf8","python-3.10:v48-20240216-a5950e9","python-3.10:v49-20240222-aba8eb6","python-3.10:v50-20240229-2250a9a","python-3.10:v51-20240301-fc137ca","python-3.10:v52-20240304-83361d1","python-3.10:v53-20240311-46fe793","python-3.10:v54-20240315-89a269f","python-3.10:v55-20240315-3def760","python-3.10:v56-20240318-b7170e3","python-3.10:v57-20240319-38caddd","python-3.10:v58-20240322-590b495","python-3.10:v59-20240329-787bc7d"],"error":"","Changed":true} diff --git a/.cache/replit/modules/replit_v8-20240329-787bc7d.res b/.cache/replit/modules/replit_v8-20240329-787bc7d.res new file mode 100644 index 0000000..b9d629a --- /dev/null +++ b/.cache/replit/modules/replit_v8-20240329-787bc7d.res @@ -0,0 +1 @@ +{"type":"resolve","resolvedModuleId":"replit:v8-20240329-787bc7d","inputHash":"","resolutionPath":["replit","replit:v1-20231211-d5ddcff","replit:v2-20240117-0bd73cd","replit:v3-20240123-c72e35a","replit:v4-20240206-fdbd396","replit:v5-20240209-9e3a339","replit:v6-20240315-3def760","replit:v7-20240319-38caddd","replit:v8-20240329-787bc7d"],"error":"","Changed":true} diff --git a/.cache/replit/nix/env.json b/.cache/replit/nix/env.json new file mode 100644 index 0000000..2980afa --- /dev/null +++ b/.cache/replit/nix/env.json @@ -0,0 +1 @@ +{"entries":{"replit.nix":{"env":{"AR":"ar","AS":"as","CC":"gcc","CONFIG_SHELL":"/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15/bin/bash","CXX":"g++","HOST_PATH":"/nix/store/s9wpkkhhzss2gcfmmcjkbhdhl1qxki6g-nano-7.2/bin:/nix/store/ayw03i36v8lqmfbs698wac8pg2wck9r2-attr-2.5.1-bin/bin:/nix/store/d3i54hjcw7xpcxi6c72vj009jp2qxnmy-libcap-2.68/bin:/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1/bin:/nix/store/f11ibsj5vmqcy8ihfa8mzvpfs4af7cw5-coreutils-9.1/bin:/nix/store/jvh4fbqfxwwn162k5hb8ndc4h5555wfa-findutils-4.9.0/bin:/nix/store/w4pi0awp7mkfwwqi64bhy83srwkcs9af-diffutils-3.9/bin:/nix/store/w64nwxs3r6cyqgy6ssxib5i2r6k8yfc2-gnused-4.9/bin:/nix/store/rn5b13lbsslbvmmbqnqxdcagzqp4435w-gnugrep-3.7/bin:/nix/store/2hd6hniab7x4k5vlnxl27vcfm99h4qjm-gawk-5.2.1/bin:/nix/store/g04kanq0g4a9vdlw9mqayvcqfy71r19r-gnutar-1.34/bin:/nix/store/5z4s362vmm6l0qf6x5rjr39y6mf35xjn-gzip-1.12/bin:/nix/store/vhpdjfcb7bdpzgqnlivb8629g02257f9-bzip2-1.0.8-bin/bin:/nix/store/v379zfyn6473hcy2nvlam0vkn120gfbp-gnumake-4.4.1/bin:/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15/bin:/nix/store/2mwcbkx6br4nfk1gljc62vmll80l20y3-patch-2.7.6/bin:/nix/store/z9355bsrkh1as20pil82nnvn5fc14gbr-xz-5.4.3-bin/bin:/nix/store/087609j187svcvxi5rn0jk3b9rh9pm24-file-5.44/bin","LD":"ld","LIBGL_DRIVERS_PATH":"/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers/lib/dri","LOCALE_ARCHIVE":"/usr/lib/locale/locale-archive","NIX_BINTOOLS":"/nix/store/2fr6714pqybnns0sfm9swa4dlaxg8sjz-binutils-wrapper-2.40","NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu":"1","NIX_BUILD_CORES":"1","NIX_BUILD_TOP":"/tmp","NIX_CC":"/nix/store/d9fndiing52fkalp5knfalrvlb3isi6w-gcc-wrapper-12.2.0","NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu":"1","NIX_CFLAGS_COMPILE":" -frandom-seed=2r8409769x -isystem /nix/store/06va7ydcmgibg7a54dbc16m8pgxm6s55-libglvnd-1.6.0-dev/include -isystem /nix/store/4bah1j03adl517aninfl4ciikzhvxvsl-pulseaudio-16.1-dev/include -isystem /nix/store/q571w9qfwi3l81lsj6ip9pd1kvdg4ls5-libcap-2.68-dev/include -isystem /nix/store/0rvdy2h1zbg377b2dr9sgvg842sfd1ys-attr-2.5.1-dev/include -isystem /nix/store/06va7ydcmgibg7a54dbc16m8pgxm6s55-libglvnd-1.6.0-dev/include -isystem /nix/store/4bah1j03adl517aninfl4ciikzhvxvsl-pulseaudio-16.1-dev/include -isystem /nix/store/q571w9qfwi3l81lsj6ip9pd1kvdg4ls5-libcap-2.68-dev/include -isystem /nix/store/0rvdy2h1zbg377b2dr9sgvg842sfd1ys-attr-2.5.1-dev/include","NIX_ENFORCE_NO_NATIVE":"1","NIX_HARDENING_ENABLE":"fortify stackprotector pic strictoverflow format relro bindnow","NIX_LDFLAGS":"-rpath /nix/store/2r8409769xsw1h971iqk1dwf05232hzk-nix-shell/lib64 -rpath /nix/store/2r8409769xsw1h971iqk1dwf05232hzk-nix-shell/lib -L/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers/lib -L/nix/store/q89g63r98d8hxf9mynvngc8zcwfhxash-libglvnd-1.6.0/lib -L/nix/store/m9c4c5sf4h49na44ih20qkxa71wavbg6-attr-2.5.1/lib -L/nix/store/xv4jzaqbdm17iq616gf0vspj24ngxg93-libcap-2.68-lib/lib -L/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1/lib -L/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers/lib -L/nix/store/q89g63r98d8hxf9mynvngc8zcwfhxash-libglvnd-1.6.0/lib -L/nix/store/m9c4c5sf4h49na44ih20qkxa71wavbg6-attr-2.5.1/lib -L/nix/store/xv4jzaqbdm17iq616gf0vspj24ngxg93-libcap-2.68-lib/lib -L/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1/lib","NIX_STORE":"/nix/store","NM":"nm","OBJCOPY":"objcopy","OBJDUMP":"objdump","PATH":"/nix/store/5d0bgm1j7nwan9z0gkv7lfsm5chz0jwq-bash-interactive-5.2-p15/bin:/nix/store/xxpqzipy93i49sgvm6rl8b9lm1p0bx9b-patchelf-0.15.0/bin:/nix/store/d9fndiing52fkalp5knfalrvlb3isi6w-gcc-wrapper-12.2.0/bin:/nix/store/hqbh8ibqaq8x6riwz48xvyx4dvvldd9f-gcc-12.2.0/bin:/nix/store/d9phlk9dq80grqczgbyi1z2w570bbnz8-glibc-2.37-8-bin/bin:/nix/store/f11ibsj5vmqcy8ihfa8mzvpfs4af7cw5-coreutils-9.1/bin:/nix/store/2fr6714pqybnns0sfm9swa4dlaxg8sjz-binutils-wrapper-2.40/bin:/nix/store/dx8hynidprz3kf4ngcjipnwaxp6h229f-binutils-2.40/bin:/nix/store/s9wpkkhhzss2gcfmmcjkbhdhl1qxki6g-nano-7.2/bin:/nix/store/ayw03i36v8lqmfbs698wac8pg2wck9r2-attr-2.5.1-bin/bin:/nix/store/d3i54hjcw7xpcxi6c72vj009jp2qxnmy-libcap-2.68/bin:/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1/bin:/nix/store/f11ibsj5vmqcy8ihfa8mzvpfs4af7cw5-coreutils-9.1/bin:/nix/store/jvh4fbqfxwwn162k5hb8ndc4h5555wfa-findutils-4.9.0/bin:/nix/store/w4pi0awp7mkfwwqi64bhy83srwkcs9af-diffutils-3.9/bin:/nix/store/w64nwxs3r6cyqgy6ssxib5i2r6k8yfc2-gnused-4.9/bin:/nix/store/rn5b13lbsslbvmmbqnqxdcagzqp4435w-gnugrep-3.7/bin:/nix/store/2hd6hniab7x4k5vlnxl27vcfm99h4qjm-gawk-5.2.1/bin:/nix/store/g04kanq0g4a9vdlw9mqayvcqfy71r19r-gnutar-1.34/bin:/nix/store/5z4s362vmm6l0qf6x5rjr39y6mf35xjn-gzip-1.12/bin:/nix/store/vhpdjfcb7bdpzgqnlivb8629g02257f9-bzip2-1.0.8-bin/bin:/nix/store/v379zfyn6473hcy2nvlam0vkn120gfbp-gnumake-4.4.1/bin:/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15/bin:/nix/store/2mwcbkx6br4nfk1gljc62vmll80l20y3-patch-2.7.6/bin:/nix/store/z9355bsrkh1as20pil82nnvn5fc14gbr-xz-5.4.3-bin/bin:/nix/store/087609j187svcvxi5rn0jk3b9rh9pm24-file-5.44/bin","RANLIB":"ranlib","READELF":"readelf","REPLIT_LD_LIBRARY_PATH":"/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers/lib:/nix/store/q89g63r98d8hxf9mynvngc8zcwfhxash-libglvnd-1.6.0/lib:/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1/lib","SIZE":"size","SOURCE_DATE_EPOCH":"315532800","STRINGS":"strings","STRIP":"strip","XDG_DATA_DIRS":"/nix/store/xxpqzipy93i49sgvm6rl8b9lm1p0bx9b-patchelf-0.15.0/share","_":"/nix/store/f11ibsj5vmqcy8ihfa8mzvpfs4af7cw5-coreutils-9.1/bin/env","__EGL_VENDOR_LIBRARY_FILENAMES":"/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers/share/glvnd/egl_vendor.d/50_mesa.json","__ETC_PROFILE_SOURCED":"1","__structuredAttrs":"","buildInputs":"/nix/store/s9wpkkhhzss2gcfmmcjkbhdhl1qxki6g-nano-7.2 /nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers /nix/store/06va7ydcmgibg7a54dbc16m8pgxm6s55-libglvnd-1.6.0-dev /nix/store/4bah1j03adl517aninfl4ciikzhvxvsl-pulseaudio-16.1-dev","buildPhase":"{ echo \"------------------------------------------------------------\";\n echo \" WARNING: the existence of this path is not guaranteed.\";\n echo \" It is an internal implementation detail for pkgs.mkShell.\";\n echo \"------------------------------------------------------------\";\n echo;\n # Record all build inputs as runtime dependencies\n export;\n} \u003e\u003e \"$out\"\n","builder":"/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15/bin/bash","cmakeFlags":"","configureFlags":"","depsBuildBuild":"","depsBuildBuildPropagated":"","depsBuildTarget":"","depsBuildTargetPropagated":"","depsHostHost":"","depsHostHostPropagated":"","depsTargetTarget":"","depsTargetTargetPropagated":"","doCheck":"","doInstallCheck":"","mesonFlags":"","nativeBuildInputs":"","out":"/nix/store/2r8409769xsw1h971iqk1dwf05232hzk-nix-shell","outputs":"out","patches":"","phases":"buildPhase","preferLocalBuild":"1","propagatedBuildInputs":"","propagatedNativeBuildInputs":"","shell":"/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15/bin/bash","shellHook":"","stdenv":"/nix/store/5wdlffjhcr4i43sfajxcvx3lvnkryh5y-stdenv-linux","strictDeps":"","system":"x86_64-linux"},"dependencies":[{"path":"replit.nix","mod_time":"2024-04-18T03:58:59.71182303Z"}],"closure":["/nix/store/567zfi9026lp2q6v97vwn640rv6i3n4c-libunistring-1.1","/nix/store/4563gldw8ibz76f1a3x69zq3a1vhdpz9-libidn2-2.3.4","/nix/store/jd99cyc0251p0i5y69w8mqjcai8mcq7h-xgcc-12.2.0-libgcc","/nix/store/dg8mpqqykmw9c7l0bgzzb5znkymlbfjw-glibc-2.37-8","/nix/store/3b2b6k6qnvw1lqzpidc552ifaavvw2wh-libXau-1.0.9","/nix/store/kz306i112jcl32m87sb7sbc7034sjjdv-libXdmcp-1.1.3","/nix/store/j88q9cqwhw5kmhh1w6px3mcbdgnrsdpf-libxcb-1.14","/nix/store/gzs13l92w3308ichywy2013bw2n6l413-libX11-1.8.4","/nix/store/pfn3d53934dswdsf269kmi2acbcjkq2p-libXext-1.3.4","/nix/store/q89g63r98d8hxf9mynvngc8zcwfhxash-libglvnd-1.6.0","/nix/store/06va7ydcmgibg7a54dbc16m8pgxm6s55-libglvnd-1.6.0-dev","/nix/store/fxq6yn4mfnsnh418l2k218j7sp365sa9-zlib-1.2.13","/nix/store/087609j187svcvxi5rn0jk3b9rh9pm24-file-5.44","/nix/store/5sx2z8i9nlyihc8pd86flmr3bifiwkkm-gfortran-12.2.0-libgcc","/nix/store/f0s8s8mcssm07dw6vj4jyl82gwpcyrgy-gfortran-12.2.0-lib","/nix/store/cx3bi1k7c3ssv9ybxj8587nqy1yxjvag-fftw-single-3.3.10","/nix/store/0dgxk63dxv1ab2naypknkwzfgkamgnwn-speexdsp-1.2.1","/nix/store/h9a9hrrvdwg7dbh69w9yp2z3prvdnw48-gcc-12.2.0-libgcc","/nix/store/sm14bmd3l61p5m0q7wa5g7rz2bl6azqf-gcc-12.2.0-lib","/nix/store/0h2qlf5y50h7g3ir92pr91sjig6nhdhp-gmp-with-cxx-6.2.1","/nix/store/m9c4c5sf4h49na44ih20qkxa71wavbg6-attr-2.5.1","/nix/store/ayw03i36v8lqmfbs698wac8pg2wck9r2-attr-2.5.1-bin","/nix/store/0rvdy2h1zbg377b2dr9sgvg842sfd1ys-attr-2.5.1-dev","/nix/store/10hjw3li7h1wjmq2m8mzgs5l136cprb1-gdbm-1.23","/nix/store/1jp1nyfpv3cmy73qcffwvlvzk65i6r92-libevent-2.1.12","/nix/store/7q1b1bsmxi91zci6g8714rcljl620y7f-bash-5.2-p15","/nix/store/d9phlk9dq80grqczgbyi1z2w570bbnz8-glibc-2.37-8-bin","/nix/store/qs605c91wv5fr9rny4mmhpdna6i59kdk-linux-headers-6.2","/nix/store/dpk5m64n0axk01fq8h2m0yl9hhpq2nqk-glibc-2.37-8-dev","/nix/store/3w02zcld9m60jkjvwmrs2c5g330qrvjq-binutils-2.40-lib","/nix/store/dx8hynidprz3kf4ngcjipnwaxp6h229f-binutils-2.40","/nix/store/9pg1y0wx907vgi0kpw10gwdblv6v84a8-gmp-with-cxx-6.2.1","/nix/store/knkqz46i3k0y1f53hlnkg09cqb4nr09b-acl-2.3.1","/nix/store/f11ibsj5vmqcy8ihfa8mzvpfs4af7cw5-coreutils-9.1","/nix/store/kv5bkjsxp75dvz31bvb4gmamycxyz959-expand-response-params","/nix/store/2fr6714pqybnns0sfm9swa4dlaxg8sjz-binutils-wrapper-2.40","/nix/store/2hd6hniab7x4k5vlnxl27vcfm99h4qjm-gawk-5.2.1","/nix/store/gmhs6qkxmh5p76xn52jq0yl8liq4qdd3-hwdata-0.370","/nix/store/hgc1s85v7la6pw096wbkc4x1vgh5hbcm-libpciaccess-0.16","/nix/store/2jdx0r0yiz1k38ra0diwqm5akb0k1rjh-libdrm-2.4.115","/nix/store/d6d7rhq8hl6bpkgwp356pq5j2sd6wfgm-ed-1.19","/nix/store/2mwcbkx6br4nfk1gljc62vmll80l20y3-patch-2.7.6","/nix/store/f7iajml4n02ljhx4kxnfwcj5aikkg9n2-libnfnetlink-1.0.2","/nix/store/fkn0a6y2ss9xjbx1jdsjbrj3lmb3l19p-libmnl-1.0.5","/nix/store/3g4qvf35dhjps8srrrn7c0c5jiq30764-libnetfilter_conntrack-1.0.9","/nix/store/6j93cr35jnhf4md1rr9w4b5mhilc9203-libxcrypt-4.4.33","/nix/store/67hs1a1wzvm07m2xydp7838wm6gkrp6p-db-4.8.30","/nix/store/vxdfbf94zm0d8bc5s65xww5vpcwm0yw8-audit-3.1","/nix/store/adhvlxv9vqqkqdjm8xlvqp1c1brhbrwb-linux-pam-1.5.2","/nix/store/axiq8byiinp1975knfnc705jj964d5ha-libcap-ng-0.8.3","/nix/store/wgz9gka0f5d7gbpslw7pfl095pja4dv0-tcb-1.2","/nix/store/wm5z03m8vbnfa0fy0ga1yvi21j0sw8kp-shadow-4.13","/nix/store/xpsf303y8d91vil4lplzkwjrs25fdrpq-util-linux-minimal-2.38.1-lib","/nix/store/3vi5chjbcg93vq3bn301iskb709lzzrj-util-linux-minimal-2.38.1-bin","/nix/store/af5wghs97a9m7495cn08qa936pp1vvnc-keyutils-1.6.3-lib","/nix/store/3xxq8w3qi8jdckzrgnhaxhyx8si85qbw-libkrb5-1.20.1","/nix/store/62358752brshhp1qnhz9km0rg799li8m-lame-3.100-lib","/nix/store/fgbjymmg5l23xlj5k6qjxg3qgv2n2jm2-libogg-1.3.5","/nix/store/jjlfjjxhlszl9q29qb11sw9qxh5h66vi-libmpg123-1.31.3","/nix/store/kr6x9b08gw2p4ivas4nz3yl8sw3bv4r9-flac-1.4.2","/nix/store/mxr1nfqrynldrprlkj0bmggbq2zpj0i9-libopus-1.3.1","/nix/store/rrvph1wd50cv12xg27c8i3pv4r0zb31s-libvorbis-1.3.7","/nix/store/87lj1wib5q3k28vhaxljd1pkw3s6hd7m-libsndfile-1.2.0","/nix/store/h3vl9lsxmk1p9jmx7g41znyxxnlqf5pc-libgpg-error-1.47","/nix/store/9w338q4ppxcamgcwq8hzw5x2v8mhlbp9-libgcrypt-1.10.2","/nix/store/p6jswlsw1cxshfsdl0gzd7m7lf5hab9r-npth-1.6","/nix/store/vz0zl7dy162r3k5i7r0pz2akpxsvxss9-libassuan-2.5.5","/nix/store/50frbwq501j6lc32dn0bn82dgfdg6jaw-gnupg-2.4.0","/nix/store/6km8pvikj73s842ragyj1ifarnyk5rd4-nghttp2-1.51.0-lib","/nix/store/i5rs6i65r51hw53ia5nn5gl477fx822r-zstd-1.5.5","/nix/store/yyg2hm5yyx7q5psfqqhqqmd49yp7vx3g-openssl-3.0.9","/nix/store/iglh3lb1cdhk1vn7dh9yk2f337fv80dn-libssh2-1.11.0","/nix/store/xffl1sa1dq4ya69lkygmfqcg5jj7bzfs-brotli-1.0.9-lib","/nix/store/8kcrj71h1xrfi56wh8f2p95j117ygbqi-curl-8.1.1","/nix/store/c8g0di38wqryn4mir8a2l960qgbil05x-xz-5.4.3","/nix/store/gxa1k0wg4fiy33rsh1hhizbdi5xp1s6c-bzip2-1.0.8","/nix/store/dbvlgp6fsp0ar1pn9chyc41j8y2lh2jx-elfutils-0.189","/nix/store/5a4k9mksdxqrgnz74y1743hkv7a0455r-libbpf-1.2.0","/nix/store/4yx5cv12dyswnaiyfbafymmhl3h26dgi-ncurses-6.4","/nix/store/kzaliadzvxczqdlbahf3mm20cyvj3pbr-readline-8.2p1","/nix/store/5d0bgm1j7nwan9z0gkv7lfsm5chz0jwq-bash-interactive-5.2-p15","/nix/store/5z4s362vmm6l0qf6x5rjr39y6mf35xjn-gzip-1.12","/nix/store/wkp4n3pwapq9pxv9f0341n5l9im4im1v-pcre-8.45","/nix/store/rn5b13lbsslbvmmbqnqxdcagzqp4435w-gnugrep-3.7","/nix/store/mzrjy5d84fzd2l3l4f2s3mbr1sfyk1r1-zstd-1.5.5-bin","/nix/store/vhpdjfcb7bdpzgqnlivb8629g02257f9-bzip2-1.0.8-bin","/nix/store/z9355bsrkh1as20pil82nnvn5fc14gbr-xz-5.4.3-bin","/nix/store/5frdvfsms0w8q3v4f97d9a43ssnzx257-kbd-2.5.1","/nix/store/ckpvw5rm7wb89c3dl2kl4hk83w5v4ag8-libtasn1-4.19.0","/nix/store/pq9vbv9q0q1vsv6yamqyrfv4qkzz0487-libffi-3.4.4","/nix/store/5r3kiw2valkaqxfippsyhkjwr4lx3m9n-p11-kit-0.24.1","/nix/store/bkw7l82nzbyb9v36cpg2lw2p6rg9vnw8-lz4-1.9.4","/nix/store/g04kanq0g4a9vdlw9mqayvcqfy71r19r-gnutar-1.34","/nix/store/7mwz5b683gwc9bnlnpbnsqlifw5c4xlg-pcsclite-1.9.5","/nix/store/hmdbnlr2cszzallgn85dvxvhkizpwhp3-libseccomp-2.5.4-lib","/nix/store/l0ay226aban2brd8cfllczsml3q3cvn8-kmod-30-lib","/nix/store/lir8z3cq7qm6dr1a6znkjsl9ar4ksbgg-kmod-30","/nix/store/v9vf1r0c9fbapgb9j9jy47q2wrrv5gv0-kexec-tools-2.0.26","/nix/store/wlqkw1588b21c9ikdhvb4zfalnc1xyiv-getent-glibc-2.37-8","/nix/store/xv4jzaqbdm17iq616gf0vspj24ngxg93-libcap-2.68-lib","/nix/store/gyywvddfjzskj6hxcg7hp75cpf2vw0zc-systemd-minimal-253.5","/nix/store/rrvi5h5qg41mpws1pvc1yp4k6agq9y94-libcbor-unstable-2023-01-29","/nix/store/gn78dkvhc4kdrkjjfjvaspdmnzk49x2s-libfido2-1.13.0","/nix/store/g3rf7ya41my9wdd043ryvpaf6lkbadyg-nettle-3.8.1","/nix/store/fiqxwqri2kryldcv23yvmjmsy98yazcc-unbound-1.17.1-lib","/nix/store/lpg2q8kvlmnbdlg5jd35i1dqjbanv9sk-dns-root-data-2019-01-11","/nix/store/74idx1k0b1qhhcn41wi8rdigc1pyw9gr-gnutls-3.8.0","/nix/store/k5sd9rh9n1lqs4m2ns683fwva60x8ahf-libmicrohttpd-0.9.71","/nix/store/8gl24b2kabsm0r88ffmwqk9mq3crf0a4-libnftnl-1.2.5","/nix/store/z2rk1d30aaz4xf7ayq66fwlan71ilqsl-libnl-3.7.0","/nix/store/ivp0xnnvz4plpg97mk9w3pswl0qhidal-libpcap-1.10.4","/nix/store/kqzbyqsdg5jp23zykp0j6rfdb4dpajxf-iptables-1.8.9","/nix/store/dar83k9hmwp4bdld7vgh4826yk9v906g-json-c-0.16","/nix/store/r6i6xjznjjbxkqs7p795v3jz9jwrvbv0-tpm2-tss-3.2.0","/nix/store/s52f3ixpj1dchrpmqvpm5p78hml4zhg3-pcre2-10.42","/nix/store/ywwj0y4q8hk41acgc5zgkj4fvwfmh14s-libapparmor-3.1.4","/nix/store/6d7mmllqc13ijf1hacs5zqnslspxkjc3-lvm2-2.03.21-lib","/nix/store/zvym229wqabl27nl98r0lzkzcj96kby3-cryptsetup-2.6.1","/nix/store/8lgs0dqh9ks1164fp4g14gq7w1ihjbf0-systemd-253.5","/nix/store/b0xr3yj79hk2hp1p3xh7k1wnnncn11fk-libtool-2.4.7-lib","/nix/store/bhd4fn9brvp180jm5k2787m4i8dfvpr1-webrtc-audio-processing-0.3.1","/nix/store/bsw3h8kjziyn2bsj5195idlngi4ral57-soxr-0.1.3","/nix/store/ddxp5axa5z2xl1j7x3xhjji82410shf6-libasyncns-0.8","/nix/store/br5frhvy6k1kbcxnmyc3j1l5d4cwzvgh-alsa-topology-conf-1.2.5.1","/nix/store/k2cd93ncadd3fzpzibcjxb8ki02vg30v-alsa-ucm-conf-1.2.9","/nix/store/jkr66ma8frpfabsz6fysxh2zh550d0hp-alsa-lib-1.2.8","/nix/store/k2b3xdkdflrv38wz8a311dhvac0v1rrb-libselinux-3.3","/nix/store/rympfmzvl838agp9xlkyi0w48wjb9p3a-glib-2.76.2","/nix/store/snp2mn1sksi0piwjw4wqxyl2n1765wbx-dbus-1.14.6-lib","/nix/store/sy4ynhxnb33pnyz5vgzn28cbvw9s8akp-sbc-1.4","/nix/store/x25aqs4ygzdpnx05flibijxmwkn874z6-dconf-0.40.0-lib","/nix/store/fqhpkz7qilf4ajjq46brjgnhivl565rh-pulseaudio-16.1","/nix/store/d3i54hjcw7xpcxi6c72vj009jp2qxnmy-libcap-2.68","/nix/store/q571w9qfwi3l81lsj6ip9pd1kvdg4ls5-libcap-2.68-dev","/nix/store/4bah1j03adl517aninfl4ciikzhvxvsl-pulseaudio-16.1-dev","/nix/store/4faw3w020cjxvd1dnxhg73mi10wcxvpw-tzdata-2023c","/nix/store/5yzw0vhkyszf2d179m0qfkgxmp5wjjx4-move-docs.sh","/nix/store/a9ndjg0b1ivi0av9m93vfkrndp7fqbw1-strip.sh","/nix/store/bxsly8a56yb8kyrq03s82a3vyc8fqrb3-multiple-outputs.sh","/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh","/nix/store/d2skw3y1jcg27ml4rgz38lrn18q0wrs1-gmp-6.2.1","/nix/store/88dbrfrmj0lqxwyk4q6h7w5glkgkfvmn-mpfr-4.2.0","/nix/store/dg38bhsf0pl4m7qp65cf3aw7xyyw5g8d-libmpc-1.3.1","/nix/store/f2z9hlzn0ajm4i296r77v3ihcfi77ld8-isl-0.20","/nix/store/hqbh8ibqaq8x6riwz48xvyx4dvvldd9f-gcc-12.2.0","/nix/store/d9fndiing52fkalp5knfalrvlb3isi6w-gcc-wrapper-12.2.0","/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh","/nix/store/h9lc1dpi14z7is86ffhl3ld569138595-audit-tmpdir.sh","/nix/store/jvh4fbqfxwwn162k5hb8ndc4h5555wfa-findutils-4.9.0","/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh","/nix/store/m54bmrhj6fqz8nds5zcj97w9s9bckc9v-compress-man-pages.sh","/nix/store/nf1lkdrhapsx5lr6diyxyjr7pb7r20gr-patch-shebangs.sh","/nix/store/ngg1cv31c8c7bcm2n8ww4g06nq7s4zhm-set-source-date-epoch-to-latest.sh","/nix/store/pag6l61paj1dc9sv15l7bm5c17xn5kyk-move-systemd-user-units.sh","/nix/store/v379zfyn6473hcy2nvlam0vkn120gfbp-gnumake-4.4.1","/nix/store/w4pi0awp7mkfwwqi64bhy83srwkcs9af-diffutils-3.9","/nix/store/w64nwxs3r6cyqgy6ssxib5i2r6k8yfc2-gnused-4.9","/nix/store/wgrbkkaldkrlrni33ccvm3b6vbxzb656-make-symlinks-relative.sh","/nix/store/xxpqzipy93i49sgvm6rl8b9lm1p0bx9b-patchelf-0.15.0","/nix/store/xyff06pkhki3qy1ls77w10s0v79c9il0-reproducible-builds.sh","/nix/store/5wdlffjhcr4i43sfajxcvx3lvnkryh5y-stdenv-linux","/nix/store/hyn81nswiyrkgd2365zjgyml05zf58vf-libxml2-2.10.4","/nix/store/5zzbdhg945bafahn0x9xwsx8rm1f615d-llvm-15.0.7-lib","/nix/store/6xg259477c90a229xwmb53pdfkn6ig3g-default-builder.sh","/nix/store/8ifq85rba90gas7gxrrs2h2wjd20lzd3-vulkan-loader-1.3.249","/nix/store/b0bw49b9rfgg55i739dgkdfk8y6vb4hh-libXxf86vm-1.1.5","/nix/store/bf1crzzrppnlab8cwfp78dcspapjby6i-libXfixes-6.0.0","/nix/store/dwq697bxfhp7hpcq2mkd9m9469cqm7nm-mailcap-2.1.53","/nix/store/f2n21q5hzr6pkpva1372cc1p0yr7acgn-sqlite-3.41.2","/nix/store/s9wpkkhhzss2gcfmmcjkbhdhl1qxki6g-nano-7.2","/nix/store/ha4v5qbv2vcnk3bpw9jmv4iwyi08nyzg-libxshmfence-1.3","/nix/store/n5c1h1zfk0636hcjcddi8sb8iyd9rzx5-expat-2.5.0","/nix/store/pp7zlpdxb14w08ayv15zafi063zxm7jn-wayland-1.22.0","/nix/store/r5zga8mzxfj7ml58c4csnlzs571y5wmm-libelf-0.8.13","/nix/store/pfv27gsa3cr8zs3mx9nxh3xr6bmvcnn8-openssl-3.0.9","/nix/store/lwzzgbnj41d657lpxczk6l5f7d5zcnj1-python3-3.10.11","/nix/store/wab20c3qfn3635ag84q12bc4b7ysvw0a-mesa-23.0.3","/nix/store/w8dpqsrd670k6zaq90hj7g68im4gljnl-mesa-23.0.3-drivers","/nix/store/h98lg1j76psnpqbr1mmcbhm7jiilad8a-nix-shell"],"channel":"stable-23_05","channel_nix_path":"/nix/store/qv8ilmw19rf0vng64qp86mb18kkr6h3z-nixpkgs-stable-23_05-23.05.tar.gz/nixpkgs-stable-23_05","production":null}}} \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 6769e21..0000000 --- a/.gitignore +++ /dev/null @@ -1,160 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/AUTHORS.rst b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/AUTHORS.rst new file mode 100644 index 0000000..88e2b6a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/AUTHORS.rst @@ -0,0 +1,7 @@ +Authors +======= + +``pyjwt`` is currently written and maintained by `Jose Padilla `_. +Originally written and maintained by `Jeff Lindsay `_. + +A full list of contributors can be found on GitHub’s `overview `_. diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/LICENSE new file mode 100644 index 0000000..fd0ecbc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2022 José Padilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/METADATA new file mode 100644 index 0000000..b329a46 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/METADATA @@ -0,0 +1,107 @@ +Metadata-Version: 2.1 +Name: PyJWT +Version: 2.8.0 +Summary: JSON Web Token implementation in Python +Home-page: https://github.com/jpadilla/pyjwt +Author: Jose Padilla +Author-email: hello@jpadilla.com +License: MIT +Keywords: json,jwt,security,signing,token,web +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: AUTHORS.rst +Requires-Dist: typing-extensions ; python_version <= "3.7" +Provides-Extra: crypto +Requires-Dist: cryptography (>=3.4.0) ; extra == 'crypto' +Provides-Extra: dev +Requires-Dist: sphinx (<5.0.0,>=4.5.0) ; extra == 'dev' +Requires-Dist: sphinx-rtd-theme ; extra == 'dev' +Requires-Dist: zope.interface ; extra == 'dev' +Requires-Dist: cryptography (>=3.4.0) ; extra == 'dev' +Requires-Dist: pytest (<7.0.0,>=6.0.0) ; extra == 'dev' +Requires-Dist: coverage[toml] (==5.0.4) ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Provides-Extra: docs +Requires-Dist: sphinx (<5.0.0,>=4.5.0) ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme ; extra == 'docs' +Requires-Dist: zope.interface ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: pytest (<7.0.0,>=6.0.0) ; extra == 'tests' +Requires-Dist: coverage[toml] (==5.0.4) ; extra == 'tests' + +PyJWT +===== + +.. image:: https://github.com/jpadilla/pyjwt/workflows/CI/badge.svg + :target: https://github.com/jpadilla/pyjwt/actions?query=workflow%3ACI + +.. image:: https://img.shields.io/pypi/v/pyjwt.svg + :target: https://pypi.python.org/pypi/pyjwt + +.. image:: https://codecov.io/gh/jpadilla/pyjwt/branch/master/graph/badge.svg + :target: https://codecov.io/gh/jpadilla/pyjwt + +.. image:: https://readthedocs.org/projects/pyjwt/badge/?version=stable + :target: https://pyjwt.readthedocs.io/en/stable/ + +A Python implementation of `RFC 7519 `_. Original implementation was written by `@progrium `_. + +Sponsor +------- + ++--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| |auth0-logo| | If you want to quickly add secure token-based authentication to Python projects, feel free to check Auth0's Python SDK and free plan at `auth0.com/developers `_. | ++--------------+-----------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. |auth0-logo| image:: https://user-images.githubusercontent.com/83319/31722733-de95bbde-b3ea-11e7-96bf-4f4e8f915588.png + +Installing +---------- + +Install with **pip**: + +.. code-block:: console + + $ pip install PyJWT + + +Usage +----- + +.. code-block:: pycon + + >>> import jwt + >>> encoded = jwt.encode({"some": "payload"}, "secret", algorithm="HS256") + >>> print(encoded) + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg + >>> jwt.decode(encoded, "secret", algorithms=["HS256"]) + {'some': 'payload'} + +Documentation +------------- + +View the full docs online at https://pyjwt.readthedocs.io/en/stable/ + + +Tests +----- + +You can run tests from the project root after cloning with: + +.. code-block:: console + + $ tox diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/RECORD new file mode 100644 index 0000000..7621ed1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/RECORD @@ -0,0 +1,32 @@ +PyJWT-2.8.0.dist-info/AUTHORS.rst,sha256=klzkNGECnu2_VY7At89_xLBF3vUSDruXk3xwgUBxzwc,322 +PyJWT-2.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +PyJWT-2.8.0.dist-info/LICENSE,sha256=eXp6ICMdTEM-nxkR2xcx0GtYKLmPSZgZoDT3wPVvXOU,1085 +PyJWT-2.8.0.dist-info/METADATA,sha256=pV2XZjvithGcVesLHWAv0J4T5t8Qc66fip2sbxwoz1o,4160 +PyJWT-2.8.0.dist-info/RECORD,, +PyJWT-2.8.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +PyJWT-2.8.0.dist-info/top_level.txt,sha256=RP5DHNyJbMq2ka0FmfTgoSaQzh7e3r5XuCWCO8a00k8,4 +jwt/__init__.py,sha256=mV9lg6n4-0xiqCKaE1eEPC9a4j6sEkEYQcKghULE7kU,1670 +jwt/__pycache__/__init__.cpython-310.pyc,, +jwt/__pycache__/algorithms.cpython-310.pyc,, +jwt/__pycache__/api_jwk.cpython-310.pyc,, +jwt/__pycache__/api_jws.cpython-310.pyc,, +jwt/__pycache__/api_jwt.cpython-310.pyc,, +jwt/__pycache__/exceptions.cpython-310.pyc,, +jwt/__pycache__/help.cpython-310.pyc,, +jwt/__pycache__/jwk_set_cache.cpython-310.pyc,, +jwt/__pycache__/jwks_client.cpython-310.pyc,, +jwt/__pycache__/types.cpython-310.pyc,, +jwt/__pycache__/utils.cpython-310.pyc,, +jwt/__pycache__/warnings.cpython-310.pyc,, +jwt/algorithms.py,sha256=RDsv5Lm3bzwsiWT3TynT7JR41R6H6s_fWUGOIqd9x_I,29800 +jwt/api_jwk.py,sha256=HPxVqgBZm7RTaEXydciNBCuYNKDYOC_prTdaN9toGbo,4196 +jwt/api_jws.py,sha256=da17RrDe0PDccTbx3rx2lLezEG_c_YGw_vVHa335IOk,11099 +jwt/api_jwt.py,sha256=yF9DwF1kt3PA5n_TiU0OmHd0LtPHfe4JCE1XOfKPjw0,12638 +jwt/exceptions.py,sha256=KDC3M7cTrpR4OQXVURlVMThem0pfANSgBxRz-ttivmo,1046 +jwt/help.py,sha256=Jrp84fG43sCwmSIaDtY08I6ZR2VE7NhrTff89tYSE40,1749 +jwt/jwk_set_cache.py,sha256=hBKmN-giU7-G37L_XKgc_OZu2ah4wdbj1ZNG_GkoSE8,959 +jwt/jwks_client.py,sha256=9W8JVyGByQgoLbBN1u5iY1_jlgfnnukeOBTpqaM_9SE,4222 +jwt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jwt/types.py,sha256=VnhGv_VFu5a7_mrPoSCB7HaNLrJdhM8Sq1sSfEg0gLU,99 +jwt/utils.py,sha256=PAI05_8MHQCxWQTDlwN0hTtTIT2DTTZ28mm1x6-26UY,3903 +jwt/warnings.py,sha256=50XWOnyNsIaqzUJTk6XHNiIDykiL763GYA92MjTKmok,59 diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/top_level.txt new file mode 100644 index 0000000..27ccc9b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/PyJWT-2.8.0.dist-info/top_level.txt @@ -0,0 +1 @@ +jwt diff --git a/.pythonlibs/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc new file mode 100644 index 0000000..3ec1984 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/LICENSE.txt new file mode 100644 index 0000000..e497a32 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/LICENSE.txt @@ -0,0 +1,13 @@ + Copyright aio-libs contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/METADATA new file mode 100644 index 0000000..cd31264 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/METADATA @@ -0,0 +1,245 @@ +Metadata-Version: 2.1 +Name: aiohttp +Version: 3.9.5 +Summary: Async http client/server framework (asyncio) +Home-page: https://github.com/aio-libs/aiohttp +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp +Project-URL: Docs: Changelog, https://docs.aiohttp.org/en/stable/changes.html +Project-URL: Docs: RTD, https://docs.aiohttp.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Requires-Dist: aiosignal >=1.1.2 +Requires-Dist: attrs >=17.3.0 +Requires-Dist: frozenlist >=1.1.1 +Requires-Dist: multidict <7.0,>=4.5 +Requires-Dist: yarl <2.0,>=1.0 +Requires-Dist: async-timeout <5.0,>=4.0 ; python_version < "3.11" +Provides-Extra: speedups +Requires-Dist: brotlicffi ; (platform_python_implementation != "CPython") and extra == 'speedups' +Requires-Dist: Brotli ; (platform_python_implementation == "CPython") and extra == 'speedups' +Requires-Dist: aiodns ; (sys_platform == "linux" or sys_platform == "darwin") and extra == 'speedups' + +================================== +Async http client/server framework +================================== + +.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg + :height: 64px + :width: 64px + :alt: aiohttp logo + +| + +.. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI + :alt: GitHub Actions status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiohttp + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiohttp.svg + :target: https://pypi.org/project/aiohttp + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest + :target: https://docs.aiohttp.org/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + + +Key Features +============ + +- Supports both client and server side of HTTP protocol. +- Supports both client and server Web-Sockets out-of-the-box and avoids + Callback Hell. +- Provides Web-server with middleware and pluggable routing. + + +Getting started +=============== + +Client +------ + +To get something from the web: + +.. code-block:: python + + import aiohttp + import asyncio + + async def main(): + + async with aiohttp.ClientSession() as session: + async with session.get('http://python.org') as response: + + print("Status:", response.status) + print("Content-type:", response.headers['content-type']) + + html = await response.text() + print("Body:", html[:15], "...") + + asyncio.run(main()) + +This prints: + +.. code-block:: + + Status: 200 + Content-type: text/html; charset=utf-8 + Body: ... + +Coming from `requests `_ ? Read `why we need so many lines `_. + +Server +------ + +An example using a simple server: + +.. code-block:: python + + # examples/server_simple.py + from aiohttp import web + + async def handle(request): + name = request.match_info.get('name', "Anonymous") + text = "Hello, " + name + return web.Response(text=text) + + async def wshandle(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + + async for msg in ws: + if msg.type == web.WSMsgType.text: + await ws.send_str("Hello, {}".format(msg.data)) + elif msg.type == web.WSMsgType.binary: + await ws.send_bytes(msg.data) + elif msg.type == web.WSMsgType.close: + break + + return ws + + + app = web.Application() + app.add_routes([web.get('/', handle), + web.get('/echo', wshandle), + web.get('/{name}', handle)]) + + if __name__ == '__main__': + web.run_app(app) + + +Documentation +============= + +https://aiohttp.readthedocs.io/ + + +Demos +===== + +https://github.com/aio-libs/aiohttp-demos + + +External links +============== + +* `Third party libraries + `_ +* `Built with aiohttp + `_ +* `Powered by aiohttp + `_ + +Feel free to make a Pull Request for adding your link to these pages! + + +Communication channels +====================== + +*aio-libs Discussions*: https://github.com/aio-libs/aiohttp/discussions + +*gitter chat* https://gitter.im/aio-libs/Lobby + +We support `Stack Overflow +`_. +Please add *aiohttp* tag to your question there. + +Requirements +============ + +- async-timeout_ +- attrs_ +- multidict_ +- yarl_ +- frozenlist_ + +Optionally you may install the aiodns_ library (highly recommended for sake of speed). + +.. _aiodns: https://pypi.python.org/pypi/aiodns +.. _attrs: https://github.com/python-attrs/attrs +.. _multidict: https://pypi.python.org/pypi/multidict +.. _frozenlist: https://pypi.org/project/frozenlist/ +.. _yarl: https://pypi.python.org/pypi/yarl +.. _async-timeout: https://pypi.python.org/pypi/async_timeout + +License +======= + +``aiohttp`` is offered under the Apache 2 license. + + +Keepsafe +======== + +The aiohttp community would like to thank Keepsafe +(https://www.getkeepsafe.com) for its support in the early days of +the project. + + +Source code +=========== + +The latest developer version is available in a GitHub repository: +https://github.com/aio-libs/aiohttp + +Benchmarks +========== + +If you are interested in efficiency, the AsyncIO community maintains a +list of benchmarks on the official wiki: +https://github.com/python/asyncio/wiki/Benchmarks diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/RECORD new file mode 100644 index 0000000..892bec6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/RECORD @@ -0,0 +1,119 @@ +aiohttp-3.9.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiohttp-3.9.5.dist-info/LICENSE.txt,sha256=n4DQ2311WpQdtFchcsJw7L2PCCuiFd3QlZhZQu2Uqes,588 +aiohttp-3.9.5.dist-info/METADATA,sha256=TD5idczLj39MQNfk4DX6Dg33oZrl1YHK1vSnxNt5lOQ,7459 +aiohttp-3.9.5.dist-info/RECORD,, +aiohttp-3.9.5.dist-info/WHEEL,sha256=CzQQWV-lNyM92gr3iaBk8dvO35YDHRxgzkZ-dxumUIM,152 +aiohttp-3.9.5.dist-info/top_level.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8 +aiohttp/.hash/_cparser.pxd.hash,sha256=hYa9Vje-oMs2eh_7MfCPOh2QW_1x1yCjcZuc7AmwLd0,121 +aiohttp/.hash/_find_header.pxd.hash,sha256=_mbpD6vM-CVCKq3ulUvsOAz5Wdo88wrDzfpOsMQaMNA,125 +aiohttp/.hash/_helpers.pyi.hash,sha256=Ew4BZDc2LqFwszgZZUHHrJvw5P8HBhJ700n1Ntg52hE,121 +aiohttp/.hash/_helpers.pyx.hash,sha256=5JQ6BlMBE4HnRaCGdkK9_wpL3ZSWpU1gyLYva0Wwx2c,121 +aiohttp/.hash/_http_parser.pyx.hash,sha256=4RMfISkoa9dJKvYXpa_Qe7b_32v4k7HXpaGhgXcNK4k,125 +aiohttp/.hash/_http_writer.pyx.hash,sha256=3Qg3T3D-Ud73elzPHBufK0yEu9tP5jsu6g-aPKQY9gE,125 +aiohttp/.hash/_websocket.pyx.hash,sha256=M97f-Yti-4vnE4GNTD1s_DzKs-fG_ww3jle6EUvixnE,123 +aiohttp/.hash/hdrs.py.hash,sha256=2oEszMWjYFTHoF2w4OcFCoM7osv4vY9KLLJCu9HP0xI,116 +aiohttp/__init__.py,sha256=f4PQQkLHI2og_KChLyaIzT1iOTIajtmT62yHfpn-XhU,7762 +aiohttp/__pycache__/__init__.cpython-310.pyc,, +aiohttp/__pycache__/abc.cpython-310.pyc,, +aiohttp/__pycache__/base_protocol.cpython-310.pyc,, +aiohttp/__pycache__/client.cpython-310.pyc,, +aiohttp/__pycache__/client_exceptions.cpython-310.pyc,, +aiohttp/__pycache__/client_proto.cpython-310.pyc,, +aiohttp/__pycache__/client_reqrep.cpython-310.pyc,, +aiohttp/__pycache__/client_ws.cpython-310.pyc,, +aiohttp/__pycache__/compression_utils.cpython-310.pyc,, +aiohttp/__pycache__/connector.cpython-310.pyc,, +aiohttp/__pycache__/cookiejar.cpython-310.pyc,, +aiohttp/__pycache__/formdata.cpython-310.pyc,, +aiohttp/__pycache__/hdrs.cpython-310.pyc,, +aiohttp/__pycache__/helpers.cpython-310.pyc,, +aiohttp/__pycache__/http.cpython-310.pyc,, +aiohttp/__pycache__/http_exceptions.cpython-310.pyc,, +aiohttp/__pycache__/http_parser.cpython-310.pyc,, +aiohttp/__pycache__/http_websocket.cpython-310.pyc,, +aiohttp/__pycache__/http_writer.cpython-310.pyc,, +aiohttp/__pycache__/locks.cpython-310.pyc,, +aiohttp/__pycache__/log.cpython-310.pyc,, +aiohttp/__pycache__/multipart.cpython-310.pyc,, +aiohttp/__pycache__/payload.cpython-310.pyc,, +aiohttp/__pycache__/payload_streamer.cpython-310.pyc,, +aiohttp/__pycache__/pytest_plugin.cpython-310.pyc,, +aiohttp/__pycache__/resolver.cpython-310.pyc,, +aiohttp/__pycache__/streams.cpython-310.pyc,, +aiohttp/__pycache__/tcp_helpers.cpython-310.pyc,, +aiohttp/__pycache__/test_utils.cpython-310.pyc,, +aiohttp/__pycache__/tracing.cpython-310.pyc,, +aiohttp/__pycache__/typedefs.cpython-310.pyc,, +aiohttp/__pycache__/web.cpython-310.pyc,, +aiohttp/__pycache__/web_app.cpython-310.pyc,, +aiohttp/__pycache__/web_exceptions.cpython-310.pyc,, +aiohttp/__pycache__/web_fileresponse.cpython-310.pyc,, +aiohttp/__pycache__/web_log.cpython-310.pyc,, +aiohttp/__pycache__/web_middlewares.cpython-310.pyc,, +aiohttp/__pycache__/web_protocol.cpython-310.pyc,, +aiohttp/__pycache__/web_request.cpython-310.pyc,, +aiohttp/__pycache__/web_response.cpython-310.pyc,, +aiohttp/__pycache__/web_routedef.cpython-310.pyc,, +aiohttp/__pycache__/web_runner.cpython-310.pyc,, +aiohttp/__pycache__/web_server.cpython-310.pyc,, +aiohttp/__pycache__/web_urldispatcher.cpython-310.pyc,, +aiohttp/__pycache__/web_ws.cpython-310.pyc,, +aiohttp/__pycache__/worker.cpython-310.pyc,, +aiohttp/_cparser.pxd,sha256=8jGIg-VJ9p3llwCakUYDsPGxA4HiZe9dmK9Jmtlz-5g,4318 +aiohttp/_find_header.pxd,sha256=0GfwFCPN2zxEKTO1_MA5sYq2UfzsG8kcV3aTqvwlz3g,68 +aiohttp/_headers.pxi,sha256=n701k28dVPjwRnx5j6LpJhLTfj7dqu2vJt7f0O60Oyg,2007 +aiohttp/_helpers.cpython-310-x86_64-linux-gnu.so,sha256=KZA_we6lVYYLcxtV5fNq7f72xOwd6kqp5YzJEJT9wMI,508784 +aiohttp/_helpers.pyi,sha256=ZoKiJSS51PxELhI2cmIr5737YjjZcJt7FbIRO3ym1Ss,202 +aiohttp/_helpers.pyx,sha256=XeLbNft5X_4ifi8QB8i6TyrRuayijMSO3IDHeSA89uM,1049 +aiohttp/_http_parser.cpython-310-x86_64-linux-gnu.so,sha256=6BvwDWVHOAaETC4YAOd8I9YqEYJVq3yEjGYZtETeSm4,2586576 +aiohttp/_http_parser.pyx,sha256=q68Rq06MpW-QwLxriE3hIJmWIKc4lVFaWHU3clsHd4Y,28125 +aiohttp/_http_writer.cpython-310-x86_64-linux-gnu.so,sha256=llvQZrZI6yQIHp0lP-dgdlOWnLIIPLLWNhgEi9RsDI0,459368 +aiohttp/_http_writer.pyx,sha256=aIHAp8g4ZV5kbGRdmZce-vXjELw2M6fGKyJuOdgYQqw,4575 +aiohttp/_websocket.cpython-310-x86_64-linux-gnu.so,sha256=xO73eZVpxho_Omoz3Bh9muMnRL-7qPGEL5BTAyOTEeQ,234032 +aiohttp/_websocket.pyx,sha256=1XuOSNDCbyDrzF5uMA2isqausSs8l2jWTLDlNDLM9Io,1561 +aiohttp/abc.py,sha256=WGZ5HH0hoCH77qaISTb689ygpS9CxfKkgUCOcgjU2lo,5500 +aiohttp/base_protocol.py,sha256=HJ5SxzbzYewj-sjoKMbD6i5rDYEv9Zo7Q_cyV3Wvn6o,2876 +aiohttp/client.py,sha256=PPUMNromtwqttMy5rzxmZ9ZjMFvqglvPdYG-A96lHhU,47499 +aiohttp/client_exceptions.py,sha256=7lx_YWAauUQVOxg_RehW9HZE344ak3lGmVJHfCrmb-A,9411 +aiohttp/client_proto.py,sha256=kCRlCOYxiuUv83cHz-gDYF0bK4Ye_KgkhYibjqTpN_M,9910 +aiohttp/client_reqrep.py,sha256=bt5woKRdhImzsEqg39a-O0yqswY8adguODtE-ui2RxE,40075 +aiohttp/client_ws.py,sha256=nNrwu1wA0U3B0cNsVr61QfV2S60bbKfaZXHfW7klFl4,11010 +aiohttp/compression_utils.py,sha256=GCkBNJqrybMhiTQGwqqhORnaTLpRFZD_-UvRtnZ5lEQ,5015 +aiohttp/connector.py,sha256=meq8urjMWelJnG26VgkA3ibrIioaEWqujg3jjNAKG28,53796 +aiohttp/cookiejar.py,sha256=PdvsOiDasDYYUOPaaAfuuFJzR4CJyHHjut02YiZ_N8M,14015 +aiohttp/formdata.py,sha256=WjHA1mieKlWwI5O3hi3-siqN0dWz_X04oXNNZje2z7Q,6521 +aiohttp/hdrs.py,sha256=uzn5agn_jXid2h-ky6Y0ZAQ8BrPeTGLDGr-weiMctso,4613 +aiohttp/helpers.py,sha256=EAZ1V0pGfv2xRiWfhjubBqgLBI0aK-CXlUlYss3EYzo,30988 +aiohttp/http.py,sha256=8o8j8xH70OWjnfTWA9V44NR785QPxEPrUtzMXiAVpwc,1842 +aiohttp/http_exceptions.py,sha256=7LOFFUwq04fZsnZA-NP5nukd6c2i8daM8-ejj3ndbSQ,2716 +aiohttp/http_parser.py,sha256=zuG3C-WOUVjoNTqgsrdCorzCGoXbR0gHhma3G___zOA,36507 +aiohttp/http_websocket.py,sha256=9Kfp5e4TU1JJfEvJ7l1Kt6Cr2HZX3z_RIojN8BAriPI,26732 +aiohttp/http_writer.py,sha256=fxpyRj_S3WcBl9fxxF05t8YYAUA-0jW5b_PjVSluT3Y,5933 +aiohttp/locks.py,sha256=wRYFo1U82LwBBdqwU24JEPaoTAlKaaJd2FtfDKhkTb4,1136 +aiohttp/log.py,sha256=BbNKx9e3VMIm0xYjZI0IcBBoS7wjdeIeSaiJE7-qK2g,325 +aiohttp/multipart.py,sha256=xBmudauxLHuXajWQMVl647sNS9IzzMYeEMKay4C0qVw,34937 +aiohttp/payload.py,sha256=xK04Z-TSao-qiYVMnphKG9-6yOvoqGsZBM7egUS4n9A,13542 +aiohttp/payload_streamer.py,sha256=eAS8S-UWfLkEMavRjP2Uu9amC3PnbV79wHTNDoRmYn8,2087 +aiohttp/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +aiohttp/pytest_plugin.py,sha256=3IwpuxtFiUVFGS_ZitWuqvECSGgXQWvCW312B2TaVLY,11605 +aiohttp/resolver.py,sha256=8peXjB482v0hg1ESn87op6f-UeLXk_fAMxQo_23Ek6M,5070 +aiohttp/streams.py,sha256=LWlr0gE44cjKzBU9I15vWwlorPW8ZAU-M2Sgz_UdjWM,21128 +aiohttp/tcp_helpers.py,sha256=BSadqVWaBpMFDRWnhaaR941N9MiDZ7bdTrxgCb0CW-M,961 +aiohttp/test_utils.py,sha256=8-McpBCAzFbA17yeEW9UYVKBypu-Hm_407ppQy76XWU,20475 +aiohttp/tracing.py,sha256=W94gFgxFtXSBWMU4ajbrOH61mJ4mElRmfyxNUw6FwIA,15132 +aiohttp/typedefs.py,sha256=f-EzBBgQAxNLiTUtkjgMAL5LQt81HloYTesxnhNM03U,1471 +aiohttp/web.py,sha256=HFTQaoYVK5pM3YmxNJtZl9fGrRIdFs_Nhloxe7_lJj0,19263 +aiohttp/web_app.py,sha256=4cXDqZV-KR0xMnUhQ471bsEACIsoI4_BkDJ3haXyG_I,18311 +aiohttp/web_exceptions.py,sha256=7nIuiwhZ39vJJ9KrWqArA5QcWbUdqkz2CLwEpJapeN8,10360 +aiohttp/web_fileresponse.py,sha256=33VS-6CQd4ZiezNBVZaVxWBCLuOUK_vPMNTU1ojiV80,11569 +aiohttp/web_log.py,sha256=DOfOxGyh2U7K5K_w6O7ILdfGcs4qOdzHxOwj2-k3c6c,7801 +aiohttp/web_middlewares.py,sha256=q6i0GGiVvUlpGtsbZmp88-zFIKQHwYtDd5SpBvKFdEY,4032 +aiohttp/web_protocol.py,sha256=8kAxmDpRYczyCFtUS4vDEIORgbD4WV0CTjVi-fZVykE,23060 +aiohttp/web_request.py,sha256=UyDR4JQwogyX12FS8PpMl-1d6ZG-TE02Bt2nPOEk0HI,28986 +aiohttp/web_response.py,sha256=3jfYnRpsNnxGRUAm-VNGu18Ekw5XyuYp7c7fzbOwbqY,27858 +aiohttp/web_routedef.py,sha256=Y5DPVa7D1uJp37HP6YXrO8Cd1BrEtDyS-fljOUdPk30,6132 +aiohttp/web_runner.py,sha256=rGI6zeIXZNDepvJajc8ZXue9hn0O2wSmh8S7CuXhkUI,11951 +aiohttp/web_server.py,sha256=5P-9uPCoPEDkK9ILbvEXmkkJWPhnTxBzdwAXwveyyDk,2587 +aiohttp/web_urldispatcher.py,sha256=e9QueGUecnOZq44CpfKJCjWi_IXHqHADUmIh_mzli18,40132 +aiohttp/web_ws.py,sha256=eiLuPZnB6HFXagcdZzU9jD9aKXP3K6YNDXDIoOpN8co,18960 +aiohttp/worker.py,sha256=bkozEd2rAzQS0qs4knnnplOmaZ4TNdYtqWXSXx9djEc,7965 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/WHEEL new file mode 100644 index 0000000..9bb86cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/top_level.txt new file mode 100644 index 0000000..ee4ba4f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp-3.9.5.dist-info/top_level.txt @@ -0,0 +1 @@ +aiohttp diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_cparser.pxd.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_cparser.pxd.hash new file mode 100644 index 0000000..65e3d4b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_cparser.pxd.hash @@ -0,0 +1 @@ +f2318883e549f69de597009a914603b0f1b10381e265ef5d98af499ad973fb98 /home/runner/work/aiohttp/aiohttp/aiohttp/_cparser.pxd diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_find_header.pxd.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_find_header.pxd.hash new file mode 100644 index 0000000..f006c2d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_find_header.pxd.hash @@ -0,0 +1 @@ +d067f01423cddb3c442933b5fcc039b18ab651fcec1bc91c577693aafc25cf78 /home/runner/work/aiohttp/aiohttp/aiohttp/_find_header.pxd diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyi.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyi.hash new file mode 100644 index 0000000..6a30d63 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyi.hash @@ -0,0 +1 @@ +6682a22524b9d4fc442e123672622be7bdfb6238d9709b7b15b2113b7ca6d52b /home/runner/work/aiohttp/aiohttp/aiohttp/_helpers.pyi diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyx.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyx.hash new file mode 100644 index 0000000..8f38727 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_helpers.pyx.hash @@ -0,0 +1 @@ +5de2db35fb795ffe227e2f1007c8ba4f2ad1b9aca28cc48edc80c779203cf6e3 /home/runner/work/aiohttp/aiohttp/aiohttp/_helpers.pyx diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_parser.pyx.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_parser.pyx.hash new file mode 100644 index 0000000..215a0da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_parser.pyx.hash @@ -0,0 +1 @@ +abaf11ab4e8ca56f90c0bc6b884de120999620a73895515a587537725b077786 /home/runner/work/aiohttp/aiohttp/aiohttp/_http_parser.pyx diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_writer.pyx.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_writer.pyx.hash new file mode 100644 index 0000000..8e1aaab --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_http_writer.pyx.hash @@ -0,0 +1 @@ +6881c0a7c838655e646c645d99971efaf5e310bc3633a7c62b226e39d81842ac /home/runner/work/aiohttp/aiohttp/aiohttp/_http_writer.pyx diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_websocket.pyx.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_websocket.pyx.hash new file mode 100644 index 0000000..ddbb4c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/_websocket.pyx.hash @@ -0,0 +1 @@ +d57b8e48d0c26f20ebcc5e6e300da2b2a6aeb12b3c9768d64cb0e53432ccf48a /home/runner/work/aiohttp/aiohttp/aiohttp/_websocket.pyx diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/hdrs.py.hash b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/hdrs.py.hash new file mode 100644 index 0000000..e0b81d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/.hash/hdrs.py.hash @@ -0,0 +1 @@ +bb39f96a09ff8d789dda1fa4cba63464043c06b3de4c62c31abfb07a231cb6ca /home/runner/work/aiohttp/aiohttp/aiohttp/hdrs.py diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__init__.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__init__.py new file mode 100644 index 0000000..e82e790 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__init__.py @@ -0,0 +1,240 @@ +__version__ = "3.9.5" + +from typing import TYPE_CHECKING, Tuple + +from . import hdrs as hdrs +from .client import ( + BaseConnector as BaseConnector, + ClientConnectionError as ClientConnectionError, + ClientConnectorCertificateError as ClientConnectorCertificateError, + ClientConnectorError as ClientConnectorError, + ClientConnectorSSLError as ClientConnectorSSLError, + ClientError as ClientError, + ClientHttpProxyError as ClientHttpProxyError, + ClientOSError as ClientOSError, + ClientPayloadError as ClientPayloadError, + ClientProxyConnectionError as ClientProxyConnectionError, + ClientRequest as ClientRequest, + ClientResponse as ClientResponse, + ClientResponseError as ClientResponseError, + ClientSession as ClientSession, + ClientSSLError as ClientSSLError, + ClientTimeout as ClientTimeout, + ClientWebSocketResponse as ClientWebSocketResponse, + ContentTypeError as ContentTypeError, + Fingerprint as Fingerprint, + InvalidURL as InvalidURL, + NamedPipeConnector as NamedPipeConnector, + RequestInfo as RequestInfo, + ServerConnectionError as ServerConnectionError, + ServerDisconnectedError as ServerDisconnectedError, + ServerFingerprintMismatch as ServerFingerprintMismatch, + ServerTimeoutError as ServerTimeoutError, + TCPConnector as TCPConnector, + TooManyRedirects as TooManyRedirects, + UnixConnector as UnixConnector, + WSServerHandshakeError as WSServerHandshakeError, + request as request, +) +from .cookiejar import CookieJar as CookieJar, DummyCookieJar as DummyCookieJar +from .formdata import FormData as FormData +from .helpers import BasicAuth, ChainMapProxy, ETag +from .http import ( + HttpVersion as HttpVersion, + HttpVersion10 as HttpVersion10, + HttpVersion11 as HttpVersion11, + WebSocketError as WebSocketError, + WSCloseCode as WSCloseCode, + WSMessage as WSMessage, + WSMsgType as WSMsgType, +) +from .multipart import ( + BadContentDispositionHeader as BadContentDispositionHeader, + BadContentDispositionParam as BadContentDispositionParam, + BodyPartReader as BodyPartReader, + MultipartReader as MultipartReader, + MultipartWriter as MultipartWriter, + content_disposition_filename as content_disposition_filename, + parse_content_disposition as parse_content_disposition, +) +from .payload import ( + PAYLOAD_REGISTRY as PAYLOAD_REGISTRY, + AsyncIterablePayload as AsyncIterablePayload, + BufferedReaderPayload as BufferedReaderPayload, + BytesIOPayload as BytesIOPayload, + BytesPayload as BytesPayload, + IOBasePayload as IOBasePayload, + JsonPayload as JsonPayload, + Payload as Payload, + StringIOPayload as StringIOPayload, + StringPayload as StringPayload, + TextIOPayload as TextIOPayload, + get_payload as get_payload, + payload_type as payload_type, +) +from .payload_streamer import streamer as streamer +from .resolver import ( + AsyncResolver as AsyncResolver, + DefaultResolver as DefaultResolver, + ThreadedResolver as ThreadedResolver, +) +from .streams import ( + EMPTY_PAYLOAD as EMPTY_PAYLOAD, + DataQueue as DataQueue, + EofStream as EofStream, + FlowControlDataQueue as FlowControlDataQueue, + StreamReader as StreamReader, +) +from .tracing import ( + TraceConfig as TraceConfig, + TraceConnectionCreateEndParams as TraceConnectionCreateEndParams, + TraceConnectionCreateStartParams as TraceConnectionCreateStartParams, + TraceConnectionQueuedEndParams as TraceConnectionQueuedEndParams, + TraceConnectionQueuedStartParams as TraceConnectionQueuedStartParams, + TraceConnectionReuseconnParams as TraceConnectionReuseconnParams, + TraceDnsCacheHitParams as TraceDnsCacheHitParams, + TraceDnsCacheMissParams as TraceDnsCacheMissParams, + TraceDnsResolveHostEndParams as TraceDnsResolveHostEndParams, + TraceDnsResolveHostStartParams as TraceDnsResolveHostStartParams, + TraceRequestChunkSentParams as TraceRequestChunkSentParams, + TraceRequestEndParams as TraceRequestEndParams, + TraceRequestExceptionParams as TraceRequestExceptionParams, + TraceRequestRedirectParams as TraceRequestRedirectParams, + TraceRequestStartParams as TraceRequestStartParams, + TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams, +) + +if TYPE_CHECKING: + # At runtime these are lazy-loaded at the bottom of the file. + from .worker import ( + GunicornUVLoopWebWorker as GunicornUVLoopWebWorker, + GunicornWebWorker as GunicornWebWorker, + ) + +__all__: Tuple[str, ...] = ( + "hdrs", + # client + "BaseConnector", + "ClientConnectionError", + "ClientConnectorCertificateError", + "ClientConnectorError", + "ClientConnectorSSLError", + "ClientError", + "ClientHttpProxyError", + "ClientOSError", + "ClientPayloadError", + "ClientProxyConnectionError", + "ClientResponse", + "ClientRequest", + "ClientResponseError", + "ClientSSLError", + "ClientSession", + "ClientTimeout", + "ClientWebSocketResponse", + "ContentTypeError", + "Fingerprint", + "InvalidURL", + "RequestInfo", + "ServerConnectionError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ServerTimeoutError", + "TCPConnector", + "TooManyRedirects", + "UnixConnector", + "NamedPipeConnector", + "WSServerHandshakeError", + "request", + # cookiejar + "CookieJar", + "DummyCookieJar", + # formdata + "FormData", + # helpers + "BasicAuth", + "ChainMapProxy", + "ETag", + # http + "HttpVersion", + "HttpVersion10", + "HttpVersion11", + "WSMsgType", + "WSCloseCode", + "WSMessage", + "WebSocketError", + # multipart + "BadContentDispositionHeader", + "BadContentDispositionParam", + "BodyPartReader", + "MultipartReader", + "MultipartWriter", + "content_disposition_filename", + "parse_content_disposition", + # payload + "AsyncIterablePayload", + "BufferedReaderPayload", + "BytesIOPayload", + "BytesPayload", + "IOBasePayload", + "JsonPayload", + "PAYLOAD_REGISTRY", + "Payload", + "StringIOPayload", + "StringPayload", + "TextIOPayload", + "get_payload", + "payload_type", + # payload_streamer + "streamer", + # resolver + "AsyncResolver", + "DefaultResolver", + "ThreadedResolver", + # streams + "DataQueue", + "EMPTY_PAYLOAD", + "EofStream", + "FlowControlDataQueue", + "StreamReader", + # tracing + "TraceConfig", + "TraceConnectionCreateEndParams", + "TraceConnectionCreateStartParams", + "TraceConnectionQueuedEndParams", + "TraceConnectionQueuedStartParams", + "TraceConnectionReuseconnParams", + "TraceDnsCacheHitParams", + "TraceDnsCacheMissParams", + "TraceDnsResolveHostEndParams", + "TraceDnsResolveHostStartParams", + "TraceRequestChunkSentParams", + "TraceRequestEndParams", + "TraceRequestExceptionParams", + "TraceRequestRedirectParams", + "TraceRequestStartParams", + "TraceResponseChunkReceivedParams", + # workers (imported lazily with __getattr__) + "GunicornUVLoopWebWorker", + "GunicornWebWorker", +) + + +def __dir__() -> Tuple[str, ...]: + return __all__ + ("__author__", "__doc__") + + +def __getattr__(name: str) -> object: + global GunicornUVLoopWebWorker, GunicornWebWorker + + # Importing gunicorn takes a long time (>100ms), so only import if actually needed. + if name in ("GunicornUVLoopWebWorker", "GunicornWebWorker"): + try: + from .worker import GunicornUVLoopWebWorker as guv, GunicornWebWorker as gw + except ImportError: + return None + + GunicornUVLoopWebWorker = guv # type: ignore[misc] + GunicornWebWorker = gw # type: ignore[misc] + return guv if name == "GunicornUVLoopWebWorker" else gw + + raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3b036e3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/abc.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/abc.cpython-310.pyc new file mode 100644 index 0000000..cf4c67c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/abc.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/base_protocol.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/base_protocol.cpython-310.pyc new file mode 100644 index 0000000..489d76c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/base_protocol.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000..786c416 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_exceptions.cpython-310.pyc new file mode 100644 index 0000000..75331a9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_proto.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_proto.cpython-310.pyc new file mode 100644 index 0000000..30bdb3f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_proto.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_reqrep.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_reqrep.cpython-310.pyc new file mode 100644 index 0000000..14ffd4d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_reqrep.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_ws.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_ws.cpython-310.pyc new file mode 100644 index 0000000..5eda6be Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/client_ws.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/compression_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/compression_utils.cpython-310.pyc new file mode 100644 index 0000000..bd96a32 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/compression_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/connector.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/connector.cpython-310.pyc new file mode 100644 index 0000000..3b78343 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/connector.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/cookiejar.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/cookiejar.cpython-310.pyc new file mode 100644 index 0000000..36fa707 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/cookiejar.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/formdata.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/formdata.cpython-310.pyc new file mode 100644 index 0000000..003ec54 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/formdata.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/hdrs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/hdrs.cpython-310.pyc new file mode 100644 index 0000000..9875752 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/hdrs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/helpers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 0000000..0c59c4a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/helpers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http.cpython-310.pyc new file mode 100644 index 0000000..b48dbbe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_exceptions.cpython-310.pyc new file mode 100644 index 0000000..b514eb9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_parser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_parser.cpython-310.pyc new file mode 100644 index 0000000..7357ded Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_parser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_websocket.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_websocket.cpython-310.pyc new file mode 100644 index 0000000..15d2bf3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_websocket.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_writer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_writer.cpython-310.pyc new file mode 100644 index 0000000..2d23708 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/http_writer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/locks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/locks.cpython-310.pyc new file mode 100644 index 0000000..0da9f5e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/locks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/log.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/log.cpython-310.pyc new file mode 100644 index 0000000..79cc1db Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/log.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/multipart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/multipart.cpython-310.pyc new file mode 100644 index 0000000..e012e64 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/multipart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload.cpython-310.pyc new file mode 100644 index 0000000..13f49d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload_streamer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload_streamer.cpython-310.pyc new file mode 100644 index 0000000..16d47c6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/payload_streamer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/pytest_plugin.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/pytest_plugin.cpython-310.pyc new file mode 100644 index 0000000..4cc756b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/pytest_plugin.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/resolver.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/resolver.cpython-310.pyc new file mode 100644 index 0000000..0a1e150 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/resolver.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/streams.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/streams.cpython-310.pyc new file mode 100644 index 0000000..803b022 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/streams.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tcp_helpers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tcp_helpers.cpython-310.pyc new file mode 100644 index 0000000..27ba163 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tcp_helpers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/test_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/test_utils.cpython-310.pyc new file mode 100644 index 0000000..113e540 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/test_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tracing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tracing.cpython-310.pyc new file mode 100644 index 0000000..158a42f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/tracing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/typedefs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/typedefs.cpython-310.pyc new file mode 100644 index 0000000..bff6af5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/typedefs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web.cpython-310.pyc new file mode 100644 index 0000000..a875a4c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_app.cpython-310.pyc new file mode 100644 index 0000000..bae27d8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_exceptions.cpython-310.pyc new file mode 100644 index 0000000..c384c5e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_fileresponse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_fileresponse.cpython-310.pyc new file mode 100644 index 0000000..e1ee8d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_fileresponse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_log.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_log.cpython-310.pyc new file mode 100644 index 0000000..3ada059 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_log.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_middlewares.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_middlewares.cpython-310.pyc new file mode 100644 index 0000000..07f19b2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_middlewares.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_protocol.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_protocol.cpython-310.pyc new file mode 100644 index 0000000..4f58683 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_protocol.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_request.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_request.cpython-310.pyc new file mode 100644 index 0000000..b7f5203 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_request.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_response.cpython-310.pyc new file mode 100644 index 0000000..592f594 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_routedef.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_routedef.cpython-310.pyc new file mode 100644 index 0000000..ff381bd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_routedef.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_runner.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_runner.cpython-310.pyc new file mode 100644 index 0000000..9a9875b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_runner.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_server.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_server.cpython-310.pyc new file mode 100644 index 0000000..f7546bd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_server.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_urldispatcher.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_urldispatcher.cpython-310.pyc new file mode 100644 index 0000000..8153d4c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_urldispatcher.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_ws.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_ws.cpython-310.pyc new file mode 100644 index 0000000..2cb4f77 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/web_ws.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/worker.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/worker.cpython-310.pyc new file mode 100644 index 0000000..66624f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/__pycache__/worker.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_cparser.pxd b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_cparser.pxd new file mode 100644 index 0000000..c2cd5a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_cparser.pxd @@ -0,0 +1,158 @@ +from libc.stdint cimport int32_t, uint8_t, uint16_t, uint64_t + + +cdef extern from "../vendor/llhttp/build/llhttp.h": + + struct llhttp__internal_s: + int32_t _index + void* _span_pos0 + void* _span_cb0 + int32_t error + const char* reason + const char* error_pos + void* data + void* _current + uint64_t content_length + uint8_t type + uint8_t method + uint8_t http_major + uint8_t http_minor + uint8_t header_state + uint8_t lenient_flags + uint8_t upgrade + uint8_t finish + uint16_t flags + uint16_t status_code + void* settings + + ctypedef llhttp__internal_s llhttp__internal_t + ctypedef llhttp__internal_t llhttp_t + + ctypedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length) except -1 + ctypedef int (*llhttp_cb)(llhttp_t*) except -1 + + struct llhttp_settings_s: + llhttp_cb on_message_begin + llhttp_data_cb on_url + llhttp_data_cb on_status + llhttp_data_cb on_header_field + llhttp_data_cb on_header_value + llhttp_cb on_headers_complete + llhttp_data_cb on_body + llhttp_cb on_message_complete + llhttp_cb on_chunk_header + llhttp_cb on_chunk_complete + + llhttp_cb on_url_complete + llhttp_cb on_status_complete + llhttp_cb on_header_field_complete + llhttp_cb on_header_value_complete + + ctypedef llhttp_settings_s llhttp_settings_t + + enum llhttp_errno: + HPE_OK, + HPE_INTERNAL, + HPE_STRICT, + HPE_LF_EXPECTED, + HPE_UNEXPECTED_CONTENT_LENGTH, + HPE_CLOSED_CONNECTION, + HPE_INVALID_METHOD, + HPE_INVALID_URL, + HPE_INVALID_CONSTANT, + HPE_INVALID_VERSION, + HPE_INVALID_HEADER_TOKEN, + HPE_INVALID_CONTENT_LENGTH, + HPE_INVALID_CHUNK_SIZE, + HPE_INVALID_STATUS, + HPE_INVALID_EOF_STATE, + HPE_INVALID_TRANSFER_ENCODING, + HPE_CB_MESSAGE_BEGIN, + HPE_CB_HEADERS_COMPLETE, + HPE_CB_MESSAGE_COMPLETE, + HPE_CB_CHUNK_HEADER, + HPE_CB_CHUNK_COMPLETE, + HPE_PAUSED, + HPE_PAUSED_UPGRADE, + HPE_USER + + ctypedef llhttp_errno llhttp_errno_t + + enum llhttp_flags: + F_CHUNKED, + F_CONTENT_LENGTH + + enum llhttp_type: + HTTP_REQUEST, + HTTP_RESPONSE, + HTTP_BOTH + + enum llhttp_method: + HTTP_DELETE, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_CONNECT, + HTTP_OPTIONS, + HTTP_TRACE, + HTTP_COPY, + HTTP_LOCK, + HTTP_MKCOL, + HTTP_MOVE, + HTTP_PROPFIND, + HTTP_PROPPATCH, + HTTP_SEARCH, + HTTP_UNLOCK, + HTTP_BIND, + HTTP_REBIND, + HTTP_UNBIND, + HTTP_ACL, + HTTP_REPORT, + HTTP_MKACTIVITY, + HTTP_CHECKOUT, + HTTP_MERGE, + HTTP_MSEARCH, + HTTP_NOTIFY, + HTTP_SUBSCRIBE, + HTTP_UNSUBSCRIBE, + HTTP_PATCH, + HTTP_PURGE, + HTTP_MKCALENDAR, + HTTP_LINK, + HTTP_UNLINK, + HTTP_SOURCE, + HTTP_PRI, + HTTP_DESCRIBE, + HTTP_ANNOUNCE, + HTTP_SETUP, + HTTP_PLAY, + HTTP_PAUSE, + HTTP_TEARDOWN, + HTTP_GET_PARAMETER, + HTTP_SET_PARAMETER, + HTTP_REDIRECT, + HTTP_RECORD, + HTTP_FLUSH + + ctypedef llhttp_method llhttp_method_t; + + void llhttp_settings_init(llhttp_settings_t* settings) + void llhttp_init(llhttp_t* parser, llhttp_type type, + const llhttp_settings_t* settings) + + llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) + + int llhttp_should_keep_alive(const llhttp_t* parser) + + void llhttp_resume_after_upgrade(llhttp_t* parser) + + llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) + const char* llhttp_get_error_reason(const llhttp_t* parser) + const char* llhttp_get_error_pos(const llhttp_t* parser) + + const char* llhttp_method_name(llhttp_method_t method) + + void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) + void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) + void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_find_header.pxd b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_find_header.pxd new file mode 100644 index 0000000..37a6c37 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_find_header.pxd @@ -0,0 +1,2 @@ +cdef extern from "_find_header.h": + int find_header(char *, int) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_headers.pxi b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_headers.pxi new file mode 100644 index 0000000..3744721 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_headers.pxi @@ -0,0 +1,83 @@ +# The file is autogenerated from aiohttp/hdrs.py +# Run ./tools/gen.py to update it after the origin changing. + +from . import hdrs +cdef tuple headers = ( + hdrs.ACCEPT, + hdrs.ACCEPT_CHARSET, + hdrs.ACCEPT_ENCODING, + hdrs.ACCEPT_LANGUAGE, + hdrs.ACCEPT_RANGES, + hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS, + hdrs.ACCESS_CONTROL_ALLOW_HEADERS, + hdrs.ACCESS_CONTROL_ALLOW_METHODS, + hdrs.ACCESS_CONTROL_ALLOW_ORIGIN, + hdrs.ACCESS_CONTROL_EXPOSE_HEADERS, + hdrs.ACCESS_CONTROL_MAX_AGE, + hdrs.ACCESS_CONTROL_REQUEST_HEADERS, + hdrs.ACCESS_CONTROL_REQUEST_METHOD, + hdrs.AGE, + hdrs.ALLOW, + hdrs.AUTHORIZATION, + hdrs.CACHE_CONTROL, + hdrs.CONNECTION, + hdrs.CONTENT_DISPOSITION, + hdrs.CONTENT_ENCODING, + hdrs.CONTENT_LANGUAGE, + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_LOCATION, + hdrs.CONTENT_MD5, + hdrs.CONTENT_RANGE, + hdrs.CONTENT_TRANSFER_ENCODING, + hdrs.CONTENT_TYPE, + hdrs.COOKIE, + hdrs.DATE, + hdrs.DESTINATION, + hdrs.DIGEST, + hdrs.ETAG, + hdrs.EXPECT, + hdrs.EXPIRES, + hdrs.FORWARDED, + hdrs.FROM, + hdrs.HOST, + hdrs.IF_MATCH, + hdrs.IF_MODIFIED_SINCE, + hdrs.IF_NONE_MATCH, + hdrs.IF_RANGE, + hdrs.IF_UNMODIFIED_SINCE, + hdrs.KEEP_ALIVE, + hdrs.LAST_EVENT_ID, + hdrs.LAST_MODIFIED, + hdrs.LINK, + hdrs.LOCATION, + hdrs.MAX_FORWARDS, + hdrs.ORIGIN, + hdrs.PRAGMA, + hdrs.PROXY_AUTHENTICATE, + hdrs.PROXY_AUTHORIZATION, + hdrs.RANGE, + hdrs.REFERER, + hdrs.RETRY_AFTER, + hdrs.SEC_WEBSOCKET_ACCEPT, + hdrs.SEC_WEBSOCKET_EXTENSIONS, + hdrs.SEC_WEBSOCKET_KEY, + hdrs.SEC_WEBSOCKET_KEY1, + hdrs.SEC_WEBSOCKET_PROTOCOL, + hdrs.SEC_WEBSOCKET_VERSION, + hdrs.SERVER, + hdrs.SET_COOKIE, + hdrs.TE, + hdrs.TRAILER, + hdrs.TRANSFER_ENCODING, + hdrs.URI, + hdrs.UPGRADE, + hdrs.USER_AGENT, + hdrs.VARY, + hdrs.VIA, + hdrs.WWW_AUTHENTICATE, + hdrs.WANT_DIGEST, + hdrs.WARNING, + hdrs.X_FORWARDED_FOR, + hdrs.X_FORWARDED_HOST, + hdrs.X_FORWARDED_PROTO, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..4bb9f4e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyi b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyi new file mode 100644 index 0000000..1e35893 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyi @@ -0,0 +1,6 @@ +from typing import Any + +class reify: + def __init__(self, wrapped: Any) -> None: ... + def __get__(self, inst: Any, owner: Any) -> Any: ... + def __set__(self, inst: Any, value: Any) -> None: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyx b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyx new file mode 100644 index 0000000..665f367 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_helpers.pyx @@ -0,0 +1,35 @@ +cdef class reify: + """Use as a class method decorator. It operates almost exactly like + the Python `@property` decorator, but it puts the result of the + method it decorates into the instance dict after the first call, + effectively replacing the function it decorates with an instance + variable. It is, in Python parlance, a data descriptor. + + """ + + cdef object wrapped + cdef object name + + def __init__(self, wrapped): + self.wrapped = wrapped + self.name = wrapped.__name__ + + @property + def __doc__(self): + return self.wrapped.__doc__ + + def __get__(self, inst, owner): + try: + try: + return inst._cache[self.name] + except KeyError: + val = self.wrapped(inst) + inst._cache[self.name] = val + return val + except AttributeError: + if inst is None: + return self + raise + + def __set__(self, inst, value): + raise AttributeError("reified property is read-only") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..2563102 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.pyx b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.pyx new file mode 100644 index 0000000..7ea9b32 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_parser.pyx @@ -0,0 +1,838 @@ +#cython: language_level=3 +# +# Based on https://github.com/MagicStack/httptools +# + +from cpython cimport ( + Py_buffer, + PyBUF_SIMPLE, + PyBuffer_Release, + PyBytes_AsString, + PyBytes_AsStringAndSize, + PyObject_GetBuffer, +) +from cpython.mem cimport PyMem_Free, PyMem_Malloc +from libc.limits cimport ULLONG_MAX +from libc.string cimport memcpy + +from multidict import CIMultiDict as _CIMultiDict, CIMultiDictProxy as _CIMultiDictProxy +from yarl import URL as _URL + +from aiohttp import hdrs +from aiohttp.helpers import DEBUG, set_exception + +from .http_exceptions import ( + BadHttpMessage, + BadStatusLine, + ContentLengthError, + InvalidHeader, + InvalidURLError, + LineTooLong, + PayloadEncodingError, + TransferEncodingError, +) +from .http_parser import DeflateBuffer as _DeflateBuffer +from .http_writer import ( + HttpVersion as _HttpVersion, + HttpVersion10 as _HttpVersion10, + HttpVersion11 as _HttpVersion11, +) +from .streams import EMPTY_PAYLOAD as _EMPTY_PAYLOAD, StreamReader as _StreamReader + +cimport cython + +from aiohttp cimport _cparser as cparser + +include "_headers.pxi" + +from aiohttp cimport _find_header + +DEF DEFAULT_FREELIST_SIZE = 250 + +cdef extern from "Python.h": + int PyByteArray_Resize(object, Py_ssize_t) except -1 + Py_ssize_t PyByteArray_Size(object) except -1 + char* PyByteArray_AsString(object) + +__all__ = ('HttpRequestParser', 'HttpResponseParser', + 'RawRequestMessage', 'RawResponseMessage') + +cdef object URL = _URL +cdef object URL_build = URL.build +cdef object CIMultiDict = _CIMultiDict +cdef object CIMultiDictProxy = _CIMultiDictProxy +cdef object HttpVersion = _HttpVersion +cdef object HttpVersion10 = _HttpVersion10 +cdef object HttpVersion11 = _HttpVersion11 +cdef object SEC_WEBSOCKET_KEY1 = hdrs.SEC_WEBSOCKET_KEY1 +cdef object CONTENT_ENCODING = hdrs.CONTENT_ENCODING +cdef object EMPTY_PAYLOAD = _EMPTY_PAYLOAD +cdef object StreamReader = _StreamReader +cdef object DeflateBuffer = _DeflateBuffer + + +cdef inline object extend(object buf, const char* at, size_t length): + cdef Py_ssize_t s + cdef char* ptr + s = PyByteArray_Size(buf) + PyByteArray_Resize(buf, s + length) + ptr = PyByteArray_AsString(buf) + memcpy(ptr + s, at, length) + + +DEF METHODS_COUNT = 46; + +cdef list _http_method = [] + +for i in range(METHODS_COUNT): + _http_method.append( + cparser.llhttp_method_name( i).decode('ascii')) + + +cdef inline str http_method_str(int i): + if i < METHODS_COUNT: + return _http_method[i] + else: + return "" + +cdef inline object find_header(bytes raw_header): + cdef Py_ssize_t size + cdef char *buf + cdef int idx + PyBytes_AsStringAndSize(raw_header, &buf, &size) + idx = _find_header.find_header(buf, size) + if idx == -1: + return raw_header.decode('utf-8', 'surrogateescape') + return headers[idx] + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawRequestMessage: + cdef readonly str method + cdef readonly str path + cdef readonly object version # HttpVersion + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + cdef readonly object url # yarl.URL + + def __init__(self, method, path, version, headers, raw_headers, + should_close, compression, upgrade, chunked, url): + self.method = method + self.path = path + self.version = version + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + self.url = url + + def __repr__(self): + info = [] + info.append(("method", self.method)) + info.append(("path", self.path)) + info.append(("version", self.version)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + info.append(("url", self.url)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + def _replace(self, **dct): + cdef RawRequestMessage ret + ret = _new_request_message(self.method, + self.path, + self.version, + self.headers, + self.raw_headers, + self.should_close, + self.compression, + self.upgrade, + self.chunked, + self.url) + if "method" in dct: + ret.method = dct["method"] + if "path" in dct: + ret.path = dct["path"] + if "version" in dct: + ret.version = dct["version"] + if "headers" in dct: + ret.headers = dct["headers"] + if "raw_headers" in dct: + ret.raw_headers = dct["raw_headers"] + if "should_close" in dct: + ret.should_close = dct["should_close"] + if "compression" in dct: + ret.compression = dct["compression"] + if "upgrade" in dct: + ret.upgrade = dct["upgrade"] + if "chunked" in dct: + ret.chunked = dct["chunked"] + if "url" in dct: + ret.url = dct["url"] + return ret + +cdef _new_request_message(str method, + str path, + object version, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked, + object url): + cdef RawRequestMessage ret + ret = RawRequestMessage.__new__(RawRequestMessage) + ret.method = method + ret.path = path + ret.version = version + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + ret.url = url + return ret + + +@cython.freelist(DEFAULT_FREELIST_SIZE) +cdef class RawResponseMessage: + cdef readonly object version # HttpVersion + cdef readonly int code + cdef readonly str reason + cdef readonly object headers # CIMultiDict + cdef readonly object raw_headers # tuple + cdef readonly object should_close + cdef readonly object compression + cdef readonly object upgrade + cdef readonly object chunked + + def __init__(self, version, code, reason, headers, raw_headers, + should_close, compression, upgrade, chunked): + self.version = version + self.code = code + self.reason = reason + self.headers = headers + self.raw_headers = raw_headers + self.should_close = should_close + self.compression = compression + self.upgrade = upgrade + self.chunked = chunked + + def __repr__(self): + info = [] + info.append(("version", self.version)) + info.append(("code", self.code)) + info.append(("reason", self.reason)) + info.append(("headers", self.headers)) + info.append(("raw_headers", self.raw_headers)) + info.append(("should_close", self.should_close)) + info.append(("compression", self.compression)) + info.append(("upgrade", self.upgrade)) + info.append(("chunked", self.chunked)) + sinfo = ', '.join(name + '=' + repr(val) for name, val in info) + return '' + + +cdef _new_response_message(object version, + int code, + str reason, + object headers, + object raw_headers, + bint should_close, + object compression, + bint upgrade, + bint chunked): + cdef RawResponseMessage ret + ret = RawResponseMessage.__new__(RawResponseMessage) + ret.version = version + ret.code = code + ret.reason = reason + ret.headers = headers + ret.raw_headers = raw_headers + ret.should_close = should_close + ret.compression = compression + ret.upgrade = upgrade + ret.chunked = chunked + return ret + + +@cython.internal +cdef class HttpParser: + + cdef: + cparser.llhttp_t* _cparser + cparser.llhttp_settings_t* _csettings + + bytearray _raw_name + bytearray _raw_value + bint _has_value + + object _protocol + object _loop + object _timer + + size_t _max_line_size + size_t _max_field_size + size_t _max_headers + bint _response_with_body + bint _read_until_eof + + bint _started + object _url + bytearray _buf + str _path + str _reason + object _headers + list _raw_headers + bint _upgraded + list _messages + object _payload + bint _payload_error + object _payload_exception + object _last_error + bint _auto_decompress + int _limit + + str _content_encoding + + Py_buffer py_buf + + def __cinit__(self): + self._cparser = \ + PyMem_Malloc(sizeof(cparser.llhttp_t)) + if self._cparser is NULL: + raise MemoryError() + + self._csettings = \ + PyMem_Malloc(sizeof(cparser.llhttp_settings_t)) + if self._csettings is NULL: + raise MemoryError() + + def __dealloc__(self): + PyMem_Free(self._cparser) + PyMem_Free(self._csettings) + + cdef _init( + self, cparser.llhttp_type mode, + object protocol, object loop, int limit, + object timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + cparser.llhttp_settings_init(self._csettings) + cparser.llhttp_init(self._cparser, mode, self._csettings) + self._cparser.data = self + self._cparser.content_length = 0 + + self._protocol = protocol + self._loop = loop + self._timer = timer + + self._buf = bytearray() + self._payload = None + self._payload_error = 0 + self._payload_exception = payload_exception + self._messages = [] + + self._raw_name = bytearray() + self._raw_value = bytearray() + self._has_value = False + + self._max_line_size = max_line_size + self._max_headers = max_headers + self._max_field_size = max_field_size + self._response_with_body = response_with_body + self._read_until_eof = read_until_eof + self._upgraded = False + self._auto_decompress = auto_decompress + self._content_encoding = None + + self._csettings.on_url = cb_on_url + self._csettings.on_status = cb_on_status + self._csettings.on_header_field = cb_on_header_field + self._csettings.on_header_value = cb_on_header_value + self._csettings.on_headers_complete = cb_on_headers_complete + self._csettings.on_body = cb_on_body + self._csettings.on_message_begin = cb_on_message_begin + self._csettings.on_message_complete = cb_on_message_complete + self._csettings.on_chunk_header = cb_on_chunk_header + self._csettings.on_chunk_complete = cb_on_chunk_complete + + self._last_error = None + self._limit = limit + + cdef _process_header(self): + if self._raw_name: + raw_name = bytes(self._raw_name) + raw_value = bytes(self._raw_value) + + name = find_header(raw_name) + value = raw_value.decode('utf-8', 'surrogateescape') + + self._headers.add(name, value) + + if name is CONTENT_ENCODING: + self._content_encoding = value + + PyByteArray_Resize(self._raw_name, 0) + PyByteArray_Resize(self._raw_value, 0) + self._has_value = False + self._raw_headers.append((raw_name, raw_value)) + + cdef _on_header_field(self, char* at, size_t length): + cdef Py_ssize_t size + cdef char *buf + if self._has_value: + self._process_header() + + size = PyByteArray_Size(self._raw_name) + PyByteArray_Resize(self._raw_name, size + length) + buf = PyByteArray_AsString(self._raw_name) + memcpy(buf + size, at, length) + + cdef _on_header_value(self, char* at, size_t length): + cdef Py_ssize_t size + cdef char *buf + + size = PyByteArray_Size(self._raw_value) + PyByteArray_Resize(self._raw_value, size + length) + buf = PyByteArray_AsString(self._raw_value) + memcpy(buf + size, at, length) + self._has_value = True + + cdef _on_headers_complete(self): + self._process_header() + + method = http_method_str(self._cparser.method) + should_close = not cparser.llhttp_should_keep_alive(self._cparser) + upgrade = self._cparser.upgrade + chunked = self._cparser.flags & cparser.F_CHUNKED + + raw_headers = tuple(self._raw_headers) + headers = CIMultiDictProxy(self._headers) + + if upgrade or self._cparser.method == cparser.HTTP_CONNECT: + self._upgraded = True + + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + encoding = None + enc = self._content_encoding + if enc is not None: + self._content_encoding = None + enc = enc.lower() + if enc in ('gzip', 'deflate', 'br'): + encoding = enc + + if self._cparser.type == cparser.HTTP_REQUEST: + msg = _new_request_message( + method, self._path, + self.http_version(), headers, raw_headers, + should_close, encoding, upgrade, chunked, self._url) + else: + msg = _new_response_message( + self.http_version(), self._cparser.status_code, self._reason, + headers, raw_headers, should_close, encoding, + upgrade, chunked) + + if ( + ULLONG_MAX > self._cparser.content_length > 0 or chunked or + self._cparser.method == cparser.HTTP_CONNECT or + (self._cparser.status_code >= 199 and + self._cparser.content_length == 0 and + self._read_until_eof) + ): + payload = StreamReader( + self._protocol, timer=self._timer, loop=self._loop, + limit=self._limit) + else: + payload = EMPTY_PAYLOAD + + self._payload = payload + if encoding is not None and self._auto_decompress: + self._payload = DeflateBuffer(payload, encoding) + + if not self._response_with_body: + payload = EMPTY_PAYLOAD + + self._messages.append((msg, payload)) + + cdef _on_message_complete(self): + self._payload.feed_eof() + self._payload = None + + cdef _on_chunk_header(self): + self._payload.begin_http_chunk_receiving() + + cdef _on_chunk_complete(self): + self._payload.end_http_chunk_receiving() + + cdef object _on_status_complete(self): + pass + + cdef inline http_version(self): + cdef cparser.llhttp_t* parser = self._cparser + + if parser.http_major == 1: + if parser.http_minor == 0: + return HttpVersion10 + elif parser.http_minor == 1: + return HttpVersion11 + + return HttpVersion(parser.http_major, parser.http_minor) + + ### Public API ### + + def feed_eof(self): + cdef bytes desc + + if self._payload is not None: + if self._cparser.flags & cparser.F_CHUNKED: + raise TransferEncodingError( + "Not enough data for satisfy transfer length header.") + elif self._cparser.flags & cparser.F_CONTENT_LENGTH: + raise ContentLengthError( + "Not enough data for satisfy content length header.") + elif cparser.llhttp_get_errno(self._cparser) != cparser.HPE_OK: + desc = cparser.llhttp_get_error_reason(self._cparser) + raise PayloadEncodingError(desc.decode('latin-1')) + else: + self._payload.feed_eof() + elif self._started: + self._on_headers_complete() + if self._messages: + return self._messages[-1][0] + + def feed_data(self, data): + cdef: + size_t data_len + size_t nb + cdef cparser.llhttp_errno_t errno + + PyObject_GetBuffer(data, &self.py_buf, PyBUF_SIMPLE) + data_len = self.py_buf.len + + errno = cparser.llhttp_execute( + self._cparser, + self.py_buf.buf, + data_len) + + if errno is cparser.HPE_PAUSED_UPGRADE: + cparser.llhttp_resume_after_upgrade(self._cparser) + + nb = cparser.llhttp_get_error_pos(self._cparser) - self.py_buf.buf + + PyBuffer_Release(&self.py_buf) + + if errno not in (cparser.HPE_OK, cparser.HPE_PAUSED_UPGRADE): + if self._payload_error == 0: + if self._last_error is not None: + ex = self._last_error + self._last_error = None + else: + after = cparser.llhttp_get_error_pos(self._cparser) + before = data[:after - self.py_buf.buf] + after_b = after.split(b"\r\n", 1)[0] + before = before.rsplit(b"\r\n", 1)[-1] + data = before + after_b + pointer = " " * (len(repr(before))-1) + "^" + ex = parser_error_from_errno(self._cparser, data, pointer) + self._payload = None + raise ex + + if self._messages: + messages = self._messages + self._messages = [] + else: + messages = () + + if self._upgraded: + return messages, True, data[nb:] + else: + return messages, False, b'' + + def set_upgraded(self, val): + self._upgraded = val + + +cdef class HttpRequestParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True, + ): + self._init(cparser.HTTP_REQUEST, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + + cdef object _on_status_complete(self): + cdef int idx1, idx2 + if not self._buf: + return + self._path = self._buf.decode('utf-8', 'surrogateescape') + try: + idx3 = len(self._path) + if self._cparser.method == cparser.HTTP_CONNECT: + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + self._url = URL.build(authority=self._path, encoded=True) + elif idx3 > 1 and self._path[0] == '/': + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + idx1 = self._path.find("?") + if idx1 == -1: + query = "" + idx2 = self._path.find("#") + if idx2 == -1: + path = self._path + fragment = "" + else: + path = self._path[0: idx2] + fragment = self._path[idx2+1:] + + else: + path = self._path[0:idx1] + idx1 += 1 + idx2 = self._path.find("#", idx1+1) + if idx2 == -1: + query = self._path[idx1:] + fragment = "" + else: + query = self._path[idx1: idx2] + fragment = self._path[idx2+1:] + + self._url = URL.build( + path=path, + query_string=query, + fragment=fragment, + encoded=True, + ) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + self._url = URL(self._path, encoded=True) + finally: + PyByteArray_Resize(self._buf, 0) + + +cdef class HttpResponseParser(HttpParser): + + def __init__( + self, protocol, loop, int limit, timer=None, + size_t max_line_size=8190, size_t max_headers=32768, + size_t max_field_size=8190, payload_exception=None, + bint response_with_body=True, bint read_until_eof=False, + bint auto_decompress=True + ): + self._init(cparser.HTTP_RESPONSE, protocol, loop, limit, timer, + max_line_size, max_headers, max_field_size, + payload_exception, response_with_body, read_until_eof, + auto_decompress) + # Use strict parsing on dev mode, so users are warned about broken servers. + if not DEBUG: + cparser.llhttp_set_lenient_headers(self._cparser, 1) + cparser.llhttp_set_lenient_optional_cr_before_lf(self._cparser, 1) + cparser.llhttp_set_lenient_spaces_after_chunk_size(self._cparser, 1) + + cdef object _on_status_complete(self): + if self._buf: + self._reason = self._buf.decode('utf-8', 'surrogateescape') + PyByteArray_Resize(self._buf, 0) + else: + self._reason = self._reason or '' + +cdef int cb_on_message_begin(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + + pyparser._started = True + pyparser._headers = CIMultiDict() + pyparser._raw_headers = [] + PyByteArray_Resize(pyparser._buf, 0) + pyparser._path = None + pyparser._reason = None + return 0 + + +cdef int cb_on_url(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + try: + if length > pyparser._max_line_size: + raise LineTooLong( + 'Status line is too long', pyparser._max_line_size, length) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_status(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef str reason + try: + if length > pyparser._max_line_size: + raise LineTooLong( + 'Status line is too long', pyparser._max_line_size, length) + extend(pyparser._buf, at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_field(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + pyparser._on_status_complete() + size = len(pyparser._raw_name) + length + if size > pyparser._max_field_size: + raise LineTooLong( + 'Header name is too long', pyparser._max_field_size, size) + pyparser._on_header_field(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_header_value(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef Py_ssize_t size + try: + size = len(pyparser._raw_value) + length + if size > pyparser._max_field_size: + raise LineTooLong( + 'Header value is too long', pyparser._max_field_size, size) + pyparser._on_header_value(at, length) + except BaseException as ex: + pyparser._last_error = ex + return -1 + else: + return 0 + + +cdef int cb_on_headers_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_status_complete() + pyparser._on_headers_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + if ( + pyparser._cparser.upgrade or + pyparser._cparser.method == cparser.HTTP_CONNECT + ): + return 2 + else: + return 0 + + +cdef int cb_on_body(cparser.llhttp_t* parser, + const char *at, size_t length) except -1: + cdef HttpParser pyparser = parser.data + cdef bytes body = at[:length] + try: + pyparser._payload.feed_data(body, length) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if pyparser._payload_exception is not None: + reraised_exc = pyparser._payload_exception(str(underlying_exc)) + + set_exception(pyparser._payload, reraised_exc, underlying_exc) + + pyparser._payload_error = 1 + return -1 + else: + return 0 + + +cdef int cb_on_message_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._started = False + pyparser._on_message_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_header(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_header() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef int cb_on_chunk_complete(cparser.llhttp_t* parser) except -1: + cdef HttpParser pyparser = parser.data + try: + pyparser._on_chunk_complete() + except BaseException as exc: + pyparser._last_error = exc + return -1 + else: + return 0 + + +cdef parser_error_from_errno(cparser.llhttp_t* parser, data, pointer): + cdef cparser.llhttp_errno_t errno = cparser.llhttp_get_errno(parser) + cdef bytes desc = cparser.llhttp_get_error_reason(parser) + + err_msg = "{}:\n\n {!r}\n {}".format(desc.decode("latin-1"), data, pointer) + + if errno in {cparser.HPE_CB_MESSAGE_BEGIN, + cparser.HPE_CB_HEADERS_COMPLETE, + cparser.HPE_CB_MESSAGE_COMPLETE, + cparser.HPE_CB_CHUNK_HEADER, + cparser.HPE_CB_CHUNK_COMPLETE, + cparser.HPE_INVALID_CONSTANT, + cparser.HPE_INVALID_HEADER_TOKEN, + cparser.HPE_INVALID_CONTENT_LENGTH, + cparser.HPE_INVALID_CHUNK_SIZE, + cparser.HPE_INVALID_EOF_STATE, + cparser.HPE_INVALID_TRANSFER_ENCODING}: + return BadHttpMessage(err_msg) + elif errno in {cparser.HPE_INVALID_STATUS, + cparser.HPE_INVALID_METHOD, + cparser.HPE_INVALID_VERSION}: + return BadStatusLine(error=err_msg) + elif errno == cparser.HPE_INVALID_URL: + return InvalidURLError(err_msg) + + return BadHttpMessage(err_msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..4b5b80b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.pyx b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.pyx new file mode 100644 index 0000000..eff8521 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_http_writer.pyx @@ -0,0 +1,163 @@ +from cpython.bytes cimport PyBytes_FromStringAndSize +from cpython.exc cimport PyErr_NoMemory +from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc +from cpython.object cimport PyObject_Str +from libc.stdint cimport uint8_t, uint64_t +from libc.string cimport memcpy + +from multidict import istr + +DEF BUF_SIZE = 16 * 1024 # 16KiB +cdef char BUFFER[BUF_SIZE] + +cdef object _istr = istr + + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + Py_ssize_t size + Py_ssize_t pos + + +cdef inline void _init_writer(Writer* writer): + writer.buf = &BUFFER[0] + writer.size = BUF_SIZE + writer.pos = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.buf != BUFFER: + PyMem_Free(writer.buf) + + +cdef inline int _write_byte(Writer* writer, uint8_t ch): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if writer.buf == BUFFER: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.buf[writer.pos] = ch + writer.pos += 1 + return 0 + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_byte(writer, utf) + elif utf < 0x800: + if _write_byte(writer, (0xc0 | (utf >> 6))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_byte(writer, (0xe0 | (utf >> 12))) < 0: + return -1 + if _write_byte(writer, (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_byte(writer, (0xf0 | (utf >> 18))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 12) & 0x3f))) < 0: + return -1 + if _write_byte(writer, + (0x80 | ((utf >> 6) & 0x3f))) < 0: + return -1 + return _write_byte(writer, (0x80 | (utf & 0x3f))) + + +cdef inline int _write_str(Writer* writer, str s): + cdef Py_UCS4 ch + for ch in s: + if _write_utf8(writer, ch) < 0: + return -1 + + +# --------------- _serialize_headers ---------------------- + +cdef str to_str(object s): + typ = type(s) + if typ is str: + return s + elif typ is _istr: + return PyObject_Str(s) + elif not isinstance(s, str): + raise TypeError("Cannot serialize non-str key {!r}".format(s)) + else: + return str(s) + + +cdef void _safe_header(str string) except *: + if "\r" in string or "\n" in string: + raise ValueError( + "Newline or carriage return character detected in HTTP status message or " + "header. This is a potential security issue." + ) + + +def _serialize_headers(str status_line, headers): + cdef Writer writer + cdef object key + cdef object val + cdef bytes ret + + _init_writer(&writer) + + for key, val in headers.items(): + _safe_header(to_str(key)) + _safe_header(to_str(val)) + + try: + if _write_str(&writer, status_line) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + for key, val in headers.items(): + if _write_str(&writer, to_str(key)) < 0: + raise + if _write_byte(&writer, b':') < 0: + raise + if _write_byte(&writer, b' ') < 0: + raise + if _write_str(&writer, to_str(val)) < 0: + raise + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + if _write_byte(&writer, b'\r') < 0: + raise + if _write_byte(&writer, b'\n') < 0: + raise + + return PyBytes_FromStringAndSize(writer.buf, writer.pos) + finally: + _release_writer(&writer) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..8c58740 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.pyx b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.pyx new file mode 100644 index 0000000..94318d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/_websocket.pyx @@ -0,0 +1,56 @@ +from cpython cimport PyBytes_AsString + + +#from cpython cimport PyByteArray_AsString # cython still not exports that +cdef extern from "Python.h": + char* PyByteArray_AsString(bytearray ba) except NULL + +from libc.stdint cimport uint32_t, uint64_t, uintmax_t + + +def _websocket_mask_cython(object mask, object data): + """Note, this function mutates its `data` argument + """ + cdef: + Py_ssize_t data_len, i + # bit operations on signed integers are implementation-specific + unsigned char * in_buf + const unsigned char * mask_buf + uint32_t uint32_msk + uint64_t uint64_msk + + assert len(mask) == 4 + + if not isinstance(mask, bytes): + mask = bytes(mask) + + if isinstance(data, bytearray): + data = data + else: + data = bytearray(data) + + data_len = len(data) + in_buf = PyByteArray_AsString(data) + mask_buf = PyBytes_AsString(mask) + uint32_msk = (mask_buf)[0] + + # TODO: align in_data ptr to achieve even faster speeds + # does it need in python ?! malloc() always aligns to sizeof(long) bytes + + if sizeof(size_t) >= 8: + uint64_msk = uint32_msk + uint64_msk = (uint64_msk << 32) | uint32_msk + + while data_len >= 8: + (in_buf)[0] ^= uint64_msk + in_buf += 8 + data_len -= 8 + + + while data_len >= 4: + (in_buf)[0] ^= uint32_msk + in_buf += 4 + data_len -= 4 + + for i in range(0, data_len): + in_buf[i] ^= mask_buf[i] diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/abc.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/abc.py new file mode 100644 index 0000000..ee83899 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/abc.py @@ -0,0 +1,209 @@ +import asyncio +import logging +from abc import ABC, abstractmethod +from collections.abc import Sized +from http.cookies import BaseCookie, Morsel +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Tuple, +) + +from multidict import CIMultiDict +from yarl import URL + +from .helpers import get_running_loop +from .typedefs import LooseCookies + +if TYPE_CHECKING: + from .web_app import Application + from .web_exceptions import HTTPException + from .web_request import BaseRequest, Request + from .web_response import StreamResponse +else: + BaseRequest = Request = Application = StreamResponse = None + HTTPException = None + + +class AbstractRouter(ABC): + def __init__(self) -> None: + self._frozen = False + + def post_init(self, app: Application) -> None: + """Post init stage. + + Not an abstract method for sake of backward compatibility, + but if the router wants to be aware of the application + it can override this. + """ + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + """Freeze router.""" + self._frozen = True + + @abstractmethod + async def resolve(self, request: Request) -> "AbstractMatchInfo": + """Return MATCH_INFO for given request""" + + +class AbstractMatchInfo(ABC): + @property # pragma: no branch + @abstractmethod + def handler(self) -> Callable[[Request], Awaitable[StreamResponse]]: + """Execute matched request handler""" + + @property + @abstractmethod + def expect_handler( + self, + ) -> Callable[[Request], Awaitable[Optional[StreamResponse]]]: + """Expect handler for 100-continue processing""" + + @property # pragma: no branch + @abstractmethod + def http_exception(self) -> Optional[HTTPException]: + """HTTPException instance raised on router's resolving, or None""" + + @abstractmethod # pragma: no branch + def get_info(self) -> Dict[str, Any]: + """Return a dict with additional info useful for introspection""" + + @property # pragma: no branch + @abstractmethod + def apps(self) -> Tuple[Application, ...]: + """Stack of nested applications. + + Top level application is left-most element. + + """ + + @abstractmethod + def add_app(self, app: Application) -> None: + """Add application to the nested apps stack.""" + + @abstractmethod + def freeze(self) -> None: + """Freeze the match info. + + The method is called after route resolution. + + After the call .add_app() is forbidden. + + """ + + +class AbstractView(ABC): + """Abstract class based view.""" + + def __init__(self, request: Request) -> None: + self._request = request + + @property + def request(self) -> Request: + """Request instance.""" + return self._request + + @abstractmethod + def __await__(self) -> Generator[Any, None, StreamResponse]: + """Execute the view handler.""" + + +class AbstractResolver(ABC): + """Abstract DNS resolver.""" + + @abstractmethod + async def resolve(self, host: str, port: int, family: int) -> List[Dict[str, Any]]: + """Return IP address for given hostname""" + + @abstractmethod + async def close(self) -> None: + """Release resolver""" + + +if TYPE_CHECKING: + IterableBase = Iterable[Morsel[str]] +else: + IterableBase = Iterable + + +ClearCookiePredicate = Callable[["Morsel[str]"], bool] + + +class AbstractCookieJar(Sized, IterableBase): + """Abstract Cookie Jar.""" + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = get_running_loop(loop) + + @abstractmethod + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + """Clear all cookies if no predicate is passed.""" + + @abstractmethod + def clear_domain(self, domain: str) -> None: + """Clear all cookies for domain and all subdomains.""" + + @abstractmethod + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + + @abstractmethod + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + """Return the jar's cookies filtered by their attributes.""" + + +class AbstractStreamWriter(ABC): + """Abstract stream writer.""" + + buffer_size = 0 + output_size = 0 + length: Optional[int] = 0 + + @abstractmethod + async def write(self, chunk: bytes) -> None: + """Write chunk into stream.""" + + @abstractmethod + async def write_eof(self, chunk: bytes = b"") -> None: + """Write last chunk.""" + + @abstractmethod + async def drain(self) -> None: + """Flush the write buffer.""" + + @abstractmethod + def enable_compression(self, encoding: str = "deflate") -> None: + """Enable HTTP body compression""" + + @abstractmethod + def enable_chunking(self) -> None: + """Enable HTTP chunked mode""" + + @abstractmethod + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write HTTP headers""" + + +class AbstractAccessLogger(ABC): + """Abstract writer to access log.""" + + def __init__(self, logger: logging.Logger, log_format: str) -> None: + self.logger = logger + self.log_format = log_format + + @abstractmethod + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + """Emit log to logger.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/base_protocol.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/base_protocol.py new file mode 100644 index 0000000..dc1f24f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/base_protocol.py @@ -0,0 +1,95 @@ +import asyncio +from typing import Optional, cast + +from .helpers import set_exception +from .tcp_helpers import tcp_nodelay + + +class BaseProtocol(asyncio.Protocol): + __slots__ = ( + "_loop", + "_paused", + "_drain_waiter", + "_connection_lost", + "_reading_paused", + "transport", + ) + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop: asyncio.AbstractEventLoop = loop + self._paused = False + self._drain_waiter: Optional[asyncio.Future[None]] = None + self._reading_paused = False + + self.transport: Optional[asyncio.Transport] = None + + @property + def connected(self) -> bool: + """Return True if the connection is open.""" + return self.transport is not None + + def pause_writing(self) -> None: + assert not self._paused + self._paused = True + + def resume_writing(self) -> None: + assert self._paused + self._paused = False + + waiter = self._drain_waiter + if waiter is not None: + self._drain_waiter = None + if not waiter.done(): + waiter.set_result(None) + + def pause_reading(self) -> None: + if not self._reading_paused and self.transport is not None: + try: + self.transport.pause_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = True + + def resume_reading(self) -> None: + if self._reading_paused and self.transport is not None: + try: + self.transport.resume_reading() + except (AttributeError, NotImplementedError, RuntimeError): + pass + self._reading_paused = False + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + tr = cast(asyncio.Transport, transport) + tcp_nodelay(tr, True) + self.transport = tr + + def connection_lost(self, exc: Optional[BaseException]) -> None: + # Wake up the writer if currently paused. + self.transport = None + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + return + self._drain_waiter = None + if waiter.done(): + return + if exc is None: + waiter.set_result(None) + else: + set_exception( + waiter, + ConnectionError("Connection lost"), + exc, + ) + + async def _drain_helper(self) -> None: + if not self.connected: + raise ConnectionResetError("Connection lost") + if not self._paused: + return + waiter = self._drain_waiter + if waiter is None: + waiter = self._loop.create_future() + self._drain_waiter = waiter + await asyncio.shield(waiter) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/client.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client.py new file mode 100644 index 0000000..32d2c3b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client.py @@ -0,0 +1,1366 @@ +"""HTTP Client for asyncio.""" + +import asyncio +import base64 +import hashlib +import json +import os +import sys +import traceback +import warnings +from contextlib import suppress +from types import SimpleNamespace, TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Coroutine, + Final, + FrozenSet, + Generator, + Generic, + Iterable, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +import attr +from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr +from yarl import URL + +from . import hdrs, http, payload +from .abc import AbstractCookieJar +from .client_exceptions import ( + ClientConnectionError as ClientConnectionError, + ClientConnectorCertificateError as ClientConnectorCertificateError, + ClientConnectorError as ClientConnectorError, + ClientConnectorSSLError as ClientConnectorSSLError, + ClientError as ClientError, + ClientHttpProxyError as ClientHttpProxyError, + ClientOSError as ClientOSError, + ClientPayloadError as ClientPayloadError, + ClientProxyConnectionError as ClientProxyConnectionError, + ClientResponseError as ClientResponseError, + ClientSSLError as ClientSSLError, + ContentTypeError as ContentTypeError, + InvalidURL as InvalidURL, + ServerConnectionError as ServerConnectionError, + ServerDisconnectedError as ServerDisconnectedError, + ServerFingerprintMismatch as ServerFingerprintMismatch, + ServerTimeoutError as ServerTimeoutError, + TooManyRedirects as TooManyRedirects, + WSServerHandshakeError as WSServerHandshakeError, +) +from .client_reqrep import ( + ClientRequest as ClientRequest, + ClientResponse as ClientResponse, + Fingerprint as Fingerprint, + RequestInfo as RequestInfo, + _merge_ssl_params, +) +from .client_ws import ClientWebSocketResponse as ClientWebSocketResponse +from .connector import ( + BaseConnector as BaseConnector, + NamedPipeConnector as NamedPipeConnector, + TCPConnector as TCPConnector, + UnixConnector as UnixConnector, +) +from .cookiejar import CookieJar +from .helpers import ( + _SENTINEL, + DEBUG, + BasicAuth, + TimeoutHandle, + ceil_timeout, + get_env_proxy_for_url, + get_running_loop, + method_must_be_empty_body, + sentinel, + strip_auth_from_url, +) +from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter +from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse +from .streams import FlowControlDataQueue +from .tracing import Trace, TraceConfig +from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, StrOrURL + +__all__ = ( + # client_exceptions + "ClientConnectionError", + "ClientConnectorCertificateError", + "ClientConnectorError", + "ClientConnectorSSLError", + "ClientError", + "ClientHttpProxyError", + "ClientOSError", + "ClientPayloadError", + "ClientProxyConnectionError", + "ClientResponseError", + "ClientSSLError", + "ContentTypeError", + "InvalidURL", + "ServerConnectionError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ServerTimeoutError", + "TooManyRedirects", + "WSServerHandshakeError", + # client_reqrep + "ClientRequest", + "ClientResponse", + "Fingerprint", + "RequestInfo", + # connector + "BaseConnector", + "TCPConnector", + "UnixConnector", + "NamedPipeConnector", + # client_ws + "ClientWebSocketResponse", + # client + "ClientSession", + "ClientTimeout", + "request", +) + + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ClientTimeout: + total: Optional[float] = None + connect: Optional[float] = None + sock_read: Optional[float] = None + sock_connect: Optional[float] = None + ceil_threshold: float = 5 + + # pool_queue_timeout: Optional[float] = None + # dns_resolution_timeout: Optional[float] = None + # socket_connect_timeout: Optional[float] = None + # connection_acquiring_timeout: Optional[float] = None + # new_connection_timeout: Optional[float] = None + # http_header_timeout: Optional[float] = None + # response_body_timeout: Optional[float] = None + + # to create a timeout specific for a single request, either + # - create a completely new one to overwrite the default + # - or use http://www.attrs.org/en/stable/api.html#attr.evolve + # to overwrite the defaults + + +# 5 Minute default read timeout +DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60) + +_RetType = TypeVar("_RetType") +_CharsetResolver = Callable[[ClientResponse, bytes], str] + + +class ClientSession: + """First-class interface for making HTTP requests.""" + + ATTRS = frozenset( + [ + "_base_url", + "_source_traceback", + "_connector", + "requote_redirect_url", + "_loop", + "_cookie_jar", + "_connector_owner", + "_default_auth", + "_version", + "_json_serialize", + "_requote_redirect_url", + "_timeout", + "_raise_for_status", + "_auto_decompress", + "_trust_env", + "_default_headers", + "_skip_auto_headers", + "_request_class", + "_response_class", + "_ws_response_class", + "_trace_configs", + "_read_bufsize", + "_max_line_size", + "_max_field_size", + "_resolve_charset", + ] + ) + + _source_traceback: Optional[traceback.StackSummary] = None + _connector: Optional[BaseConnector] = None + + def __init__( + self, + base_url: Optional[StrOrURL] = None, + *, + connector: Optional[BaseConnector] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + json_serialize: JSONEncoder = json.dumps, + request_class: Type[ClientRequest] = ClientRequest, + response_class: Type[ClientResponse] = ClientResponse, + ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, + version: HttpVersion = http.HttpVersion11, + cookie_jar: Optional[AbstractCookieJar] = None, + connector_owner: bool = True, + raise_for_status: Union[ + bool, Callable[[ClientResponse], Awaitable[None]] + ] = False, + read_timeout: Union[float, _SENTINEL] = sentinel, + conn_timeout: Optional[float] = None, + timeout: Union[object, ClientTimeout] = sentinel, + auto_decompress: bool = True, + trust_env: bool = False, + requote_redirect_url: bool = True, + trace_configs: Optional[List[TraceConfig]] = None, + read_bufsize: int = 2**16, + max_line_size: int = 8190, + max_field_size: int = 8190, + fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", + ) -> None: + # We initialise _connector to None immediately, as it's referenced in __del__() + # and could cause issues if an exception occurs during initialisation. + self._connector: Optional[BaseConnector] = None + if timeout is sentinel or timeout is None: + self._timeout = DEFAULT_TIMEOUT + if read_timeout is not sentinel: + warnings.warn( + "read_timeout is deprecated, " "use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + self._timeout = attr.evolve(self._timeout, total=read_timeout) + if conn_timeout is not None: + self._timeout = attr.evolve(self._timeout, connect=conn_timeout) + warnings.warn( + "conn_timeout is deprecated, " "use timeout argument instead", + DeprecationWarning, + stacklevel=2, + ) + else: + if not isinstance(timeout, ClientTimeout): + raise ValueError( + f"timeout parameter cannot be of {type(timeout)} type, " + "please use 'timeout=ClientTimeout(...)'", + ) + self._timeout = timeout + if read_timeout is not sentinel: + raise ValueError( + "read_timeout and timeout parameters " + "conflict, please setup " + "timeout.read" + ) + if conn_timeout is not None: + raise ValueError( + "conn_timeout and timeout parameters " + "conflict, please setup " + "timeout.connect" + ) + if loop is None: + if connector is not None: + loop = connector._loop + + loop = get_running_loop(loop) + + if base_url is None or isinstance(base_url, URL): + self._base_url: Optional[URL] = base_url + else: + self._base_url = URL(base_url) + assert ( + self._base_url.origin() == self._base_url + ), "Only absolute URLs without path part are supported" + + if connector is None: + connector = TCPConnector(loop=loop) + + if connector._loop is not loop: + raise RuntimeError("Session and connector has to use same event loop") + + self._loop = loop + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + if cookie_jar is None: + cookie_jar = CookieJar(loop=loop) + self._cookie_jar = cookie_jar + + if cookies is not None: + self._cookie_jar.update_cookies(cookies) + + self._connector = connector + self._connector_owner = connector_owner + self._default_auth = auth + self._version = version + self._json_serialize = json_serialize + self._raise_for_status = raise_for_status + self._auto_decompress = auto_decompress + self._trust_env = trust_env + self._requote_redirect_url = requote_redirect_url + self._read_bufsize = read_bufsize + self._max_line_size = max_line_size + self._max_field_size = max_field_size + + # Convert to list of tuples + if headers: + real_headers: CIMultiDict[str] = CIMultiDict(headers) + else: + real_headers = CIMultiDict() + self._default_headers: CIMultiDict[str] = real_headers + if skip_auto_headers is not None: + self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers) + else: + self._skip_auto_headers = frozenset() + + self._request_class = request_class + self._response_class = response_class + self._ws_response_class = ws_response_class + + self._trace_configs = trace_configs or [] + for trace_config in self._trace_configs: + trace_config.freeze() + + self._resolve_charset = fallback_charset_resolver + + def __init_subclass__(cls: Type["ClientSession"]) -> None: + warnings.warn( + "Inheritance class {} from ClientSession " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=2, + ) + + if DEBUG: + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom ClientSession.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + def __del__(self, _warnings: Any = warnings) -> None: + if not self.closed: + kwargs = {"source": self} + _warnings.warn( + f"Unclosed client session {self!r}", ResourceWarning, **kwargs + ) + context = {"client_session": self, "message": "Unclosed client session"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def request( + self, method: str, url: StrOrURL, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP request.""" + return _RequestContextManager(self._request(method, url, **kwargs)) + + def _build_url(self, str_or_url: StrOrURL) -> URL: + url = URL(str_or_url) + if self._base_url is None: + return url + else: + assert not url.is_absolute() and url.path.startswith("/") + return self._base_url.join(url) + + async def _request( + self, + method: str, + str_or_url: StrOrURL, + *, + params: Optional[Mapping[str, str]] = None, + data: Any = None, + json: Any = None, + cookies: Optional[LooseCookies] = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + allow_redirects: bool = True, + max_redirects: int = 10, + compress: Optional[str] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + raise_for_status: Union[ + None, bool, Callable[[ClientResponse], Awaitable[None]] + ] = None, + read_until_eof: bool = True, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + timeout: Union[ClientTimeout, _SENTINEL] = sentinel, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + server_hostname: Optional[str] = None, + proxy_headers: Optional[LooseHeaders] = None, + trace_request_ctx: Optional[SimpleNamespace] = None, + read_bufsize: Optional[int] = None, + auto_decompress: Optional[bool] = None, + max_line_size: Optional[int] = None, + max_field_size: Optional[int] = None, + ) -> ClientResponse: + + # NOTE: timeout clamps existing connect and read timeouts. We cannot + # set the default to None because we need to detect if the user wants + # to use the existing timeouts by setting timeout to None. + + if self.closed: + raise RuntimeError("Session is closed") + + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + if data is not None and json is not None: + raise ValueError( + "data and json parameters can not be used at the same time" + ) + elif json is not None: + data = payload.JsonPayload(json, dumps=self._json_serialize) + + if not isinstance(chunked, bool) and chunked is not None: + warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) + + redirects = 0 + history = [] + version = self._version + params = params or {} + + # Merge with default headers and transform to CIMultiDict + headers = self._prepare_headers(headers) + proxy_headers = self._prepare_headers(proxy_headers) + + try: + url = self._build_url(str_or_url) + except ValueError as e: + raise InvalidURL(str_or_url) from e + + skip_headers = set(self._skip_auto_headers) + if skip_auto_headers is not None: + for i in skip_auto_headers: + skip_headers.add(istr(i)) + + if proxy is not None: + try: + proxy = URL(proxy) + except ValueError as e: + raise InvalidURL(proxy) from e + + if timeout is sentinel: + real_timeout: ClientTimeout = self._timeout + else: + if not isinstance(timeout, ClientTimeout): + real_timeout = ClientTimeout(total=timeout) + else: + real_timeout = timeout + # timeout is cumulative for all request operations + # (request, redirects, responses, data consuming) + tm = TimeoutHandle( + self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold + ) + handle = tm.start() + + if read_bufsize is None: + read_bufsize = self._read_bufsize + + if auto_decompress is None: + auto_decompress = self._auto_decompress + + if max_line_size is None: + max_line_size = self._max_line_size + + if max_field_size is None: + max_field_size = self._max_field_size + + traces = [ + Trace( + self, + trace_config, + trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), + ) + for trace_config in self._trace_configs + ] + + for trace in traces: + await trace.send_request_start(method, url.update_query(params), headers) + + timer = tm.timer() + try: + with timer: + while True: + url, auth_from_url = strip_auth_from_url(url) + if auth and auth_from_url: + raise ValueError( + "Cannot combine AUTH argument with " + "credentials encoded in URL" + ) + + if auth is None: + auth = auth_from_url + if auth is None: + auth = self._default_auth + # It would be confusing if we support explicit + # Authorization header with auth argument + if ( + headers is not None + and auth is not None + and hdrs.AUTHORIZATION in headers + ): + raise ValueError( + "Cannot combine AUTHORIZATION header " + "with AUTH argument or credentials " + "encoded in URL" + ) + + all_cookies = self._cookie_jar.filter_cookies(url) + + if cookies is not None: + tmp_cookie_jar = CookieJar() + tmp_cookie_jar.update_cookies(cookies) + req_cookies = tmp_cookie_jar.filter_cookies(url) + if req_cookies: + all_cookies.load(req_cookies) + + if proxy is not None: + proxy = URL(proxy) + elif self._trust_env: + with suppress(LookupError): + proxy, proxy_auth = get_env_proxy_for_url(url) + + req = self._request_class( + method, + url, + params=params, + headers=headers, + skip_auto_headers=skip_headers, + data=data, + cookies=all_cookies, + auth=auth, + version=version, + compress=compress, + chunked=chunked, + expect100=expect100, + loop=self._loop, + response_class=self._response_class, + proxy=proxy, + proxy_auth=proxy_auth, + timer=timer, + session=self, + ssl=ssl if ssl is not None else True, + server_hostname=server_hostname, + proxy_headers=proxy_headers, + traces=traces, + trust_env=self.trust_env, + ) + + # connection timeout + try: + async with ceil_timeout( + real_timeout.connect, + ceil_threshold=real_timeout.ceil_threshold, + ): + assert self._connector is not None + conn = await self._connector.connect( + req, traces=traces, timeout=real_timeout + ) + except asyncio.TimeoutError as exc: + raise ServerTimeoutError( + "Connection timeout " "to host {}".format(url) + ) from exc + + assert conn.transport is not None + + assert conn.protocol is not None + conn.protocol.set_response_params( + timer=timer, + skip_payload=method_must_be_empty_body(method), + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + read_timeout=real_timeout.sock_read, + read_bufsize=read_bufsize, + timeout_ceil_threshold=self._connector._timeout_ceil_threshold, + max_line_size=max_line_size, + max_field_size=max_field_size, + ) + + try: + try: + resp = await req.send(conn) + try: + await resp.start(conn) + except BaseException: + resp.close() + raise + except BaseException: + conn.close() + raise + except ClientError: + raise + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientOSError(*exc.args) from exc + + self._cookie_jar.update_cookies(resp.cookies, resp.url) + + # redirects + if resp.status in (301, 302, 303, 307, 308) and allow_redirects: + + for trace in traces: + await trace.send_request_redirect( + method, url.update_query(params), headers, resp + ) + + redirects += 1 + history.append(resp) + if max_redirects and redirects >= max_redirects: + resp.close() + raise TooManyRedirects( + history[0].request_info, tuple(history) + ) + + # For 301 and 302, mimic IE, now changed in RFC + # https://github.com/kennethreitz/requests/pull/269 + if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or ( + resp.status in (301, 302) and resp.method == hdrs.METH_POST + ): + method = hdrs.METH_GET + data = None + if headers.get(hdrs.CONTENT_LENGTH): + headers.pop(hdrs.CONTENT_LENGTH) + + r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get( + hdrs.URI + ) + if r_url is None: + # see github.com/aio-libs/aiohttp/issues/2022 + break + else: + # reading from correct redirection + # response is forbidden + resp.release() + + try: + parsed_url = URL( + r_url, encoded=not self._requote_redirect_url + ) + + except ValueError as e: + raise InvalidURL(r_url) from e + + scheme = parsed_url.scheme + if scheme not in ("http", "https", ""): + resp.close() + raise ValueError("Can redirect only to http or https") + elif not scheme: + parsed_url = url.join(parsed_url) + + if url.origin() != parsed_url.origin(): + auth = None + headers.pop(hdrs.AUTHORIZATION, None) + + url = parsed_url + params = {} + resp.release() + continue + + break + + # check response status + if raise_for_status is None: + raise_for_status = self._raise_for_status + + if raise_for_status is None: + pass + elif callable(raise_for_status): + await raise_for_status(resp) + elif raise_for_status: + resp.raise_for_status() + + # register connection + if handle is not None: + if resp.connection is not None: + resp.connection.add_callback(handle.cancel) + else: + handle.cancel() + + resp._history = tuple(history) + + for trace in traces: + await trace.send_request_end( + method, url.update_query(params), headers, resp + ) + return resp + + except BaseException as e: + # cleanup timer + tm.close() + if handle: + handle.cancel() + handle = None + + for trace in traces: + await trace.send_request_exception( + method, url.update_query(params), headers, e + ) + raise + + def ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: float = 10.0, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Optional[Mapping[str, str]] = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Union[SSLContext, bool, None, Fingerprint] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> "_WSRequestContextManager": + """Initiate websocket connection.""" + return _WSRequestContextManager( + self._ws_connect( + url, + method=method, + protocols=protocols, + timeout=timeout, + receive_timeout=receive_timeout, + autoclose=autoclose, + autoping=autoping, + heartbeat=heartbeat, + auth=auth, + origin=origin, + params=params, + headers=headers, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + verify_ssl=verify_ssl, + fingerprint=fingerprint, + ssl_context=ssl_context, + proxy_headers=proxy_headers, + compress=compress, + max_msg_size=max_msg_size, + ) + ) + + async def _ws_connect( + self, + url: StrOrURL, + *, + method: str = hdrs.METH_GET, + protocols: Iterable[str] = (), + timeout: float = 10.0, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + auth: Optional[BasicAuth] = None, + origin: Optional[str] = None, + params: Optional[Mapping[str, str]] = None, + headers: Optional[LooseHeaders] = None, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + ssl: Optional[Union[SSLContext, bool, Fingerprint]] = True, + verify_ssl: Optional[bool] = None, + fingerprint: Optional[bytes] = None, + ssl_context: Optional[SSLContext] = None, + proxy_headers: Optional[LooseHeaders] = None, + compress: int = 0, + max_msg_size: int = 4 * 1024 * 1024, + ) -> ClientWebSocketResponse: + + if headers is None: + real_headers: CIMultiDict[str] = CIMultiDict() + else: + real_headers = CIMultiDict(headers) + + default_headers = { + hdrs.UPGRADE: "websocket", + hdrs.CONNECTION: "Upgrade", + hdrs.SEC_WEBSOCKET_VERSION: "13", + } + + for key, value in default_headers.items(): + real_headers.setdefault(key, value) + + sec_key = base64.b64encode(os.urandom(16)) + real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode() + + if protocols: + real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols) + if origin is not None: + real_headers[hdrs.ORIGIN] = origin + if compress: + extstr = ws_ext_gen(compress=compress) + real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr + + # For the sake of backward compatibility, if user passes in None, convert it to True + if ssl is None: + ssl = True + ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + + # send request + resp = await self.request( + method, + url, + params=params, + headers=real_headers, + read_until_eof=False, + auth=auth, + proxy=proxy, + proxy_auth=proxy_auth, + ssl=ssl, + proxy_headers=proxy_headers, + ) + + try: + # check handshake + if resp.status != 101: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid response status", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid upgrade header", + status=resp.status, + headers=resp.headers, + ) + + if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade": + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid connection header", + status=resp.status, + headers=resp.headers, + ) + + # key calculation + r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "") + match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode() + if r_key != match: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message="Invalid challenge response", + status=resp.status, + headers=resp.headers, + ) + + # websocket protocol + protocol = None + if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers: + resp_protocols = [ + proto.strip() + for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + + for proto in resp_protocols: + if proto in protocols: + protocol = proto + break + + # websocket compress + notakeover = False + if compress: + compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) + if compress_hdrs: + try: + compress, notakeover = ws_ext_parse(compress_hdrs) + except WSHandshakeError as exc: + raise WSServerHandshakeError( + resp.request_info, + resp.history, + message=exc.args[0], + status=resp.status, + headers=resp.headers, + ) from exc + else: + compress = 0 + notakeover = False + + conn = resp.connection + assert conn is not None + conn_proto = conn.protocol + assert conn_proto is not None + transport = conn.transport + assert transport is not None + reader: FlowControlDataQueue[WSMessage] = FlowControlDataQueue( + conn_proto, 2**16, loop=self._loop + ) + conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) + writer = WebSocketWriter( + conn_proto, + transport, + use_mask=True, + compress=compress, + notakeover=notakeover, + ) + except BaseException: + resp.close() + raise + else: + return self._ws_response_class( + reader, + writer, + protocol, + resp, + timeout, + autoclose, + autoping, + self._loop, + receive_timeout=receive_timeout, + heartbeat=heartbeat, + compress=compress, + client_notakeover=notakeover, + ) + + def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]": + """Add default headers and transform it to CIMultiDict""" + # Convert headers to MultiDict + result = CIMultiDict(self._default_headers) + if headers: + if not isinstance(headers, (MultiDictProxy, MultiDict)): + headers = CIMultiDict(headers) + added_names: Set[str] = set() + for key, value in headers.items(): + if key in added_names: + result.add(key, value) + else: + result[key] = value + added_names.add(key) + return result + + def get( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP GET request.""" + return _RequestContextManager( + self._request(hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs) + ) + + def options( + self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP OPTIONS request.""" + return _RequestContextManager( + self._request( + hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def head( + self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP HEAD request.""" + return _RequestContextManager( + self._request( + hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs + ) + ) + + def post( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP POST request.""" + return _RequestContextManager( + self._request(hdrs.METH_POST, url, data=data, **kwargs) + ) + + def put( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PUT request.""" + return _RequestContextManager( + self._request(hdrs.METH_PUT, url, data=data, **kwargs) + ) + + def patch( + self, url: StrOrURL, *, data: Any = None, **kwargs: Any + ) -> "_RequestContextManager": + """Perform HTTP PATCH request.""" + return _RequestContextManager( + self._request(hdrs.METH_PATCH, url, data=data, **kwargs) + ) + + def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager": + """Perform HTTP DELETE request.""" + return _RequestContextManager(self._request(hdrs.METH_DELETE, url, **kwargs)) + + async def close(self) -> None: + """Close underlying connector. + + Release all acquired resources. + """ + if not self.closed: + if self._connector is not None and self._connector_owner: + await self._connector.close() + self._connector = None + + @property + def closed(self) -> bool: + """Is client session closed. + + A readonly property. + """ + return self._connector is None or self._connector.closed + + @property + def connector(self) -> Optional[BaseConnector]: + """Connector instance used for the session.""" + return self._connector + + @property + def cookie_jar(self) -> AbstractCookieJar: + """The session cookies.""" + return self._cookie_jar + + @property + def version(self) -> Tuple[int, int]: + """The session HTTP protocol version.""" + return self._version + + @property + def requote_redirect_url(self) -> bool: + """Do URL requoting on redirection handling.""" + return self._requote_redirect_url + + @requote_redirect_url.setter + def requote_redirect_url(self, val: bool) -> None: + """Do URL requoting on redirection handling.""" + warnings.warn( + "session.requote_redirect_url modification " "is deprecated #2778", + DeprecationWarning, + stacklevel=2, + ) + self._requote_redirect_url = val + + @property + def loop(self) -> asyncio.AbstractEventLoop: + """Session's loop.""" + warnings.warn( + "client.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def timeout(self) -> ClientTimeout: + """Timeout for the session.""" + return self._timeout + + @property + def headers(self) -> "CIMultiDict[str]": + """The default headers of the client session.""" + return self._default_headers + + @property + def skip_auto_headers(self) -> FrozenSet[istr]: + """Headers for which autogeneration should be skipped""" + return self._skip_auto_headers + + @property + def auth(self) -> Optional[BasicAuth]: + """An object that represents HTTP Basic Authorization""" + return self._default_auth + + @property + def json_serialize(self) -> JSONEncoder: + """Json serializer callable""" + return self._json_serialize + + @property + def connector_owner(self) -> bool: + """Should connector be closed on session closing""" + return self._connector_owner + + @property + def raise_for_status( + self, + ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: + """Should `ClientResponse.raise_for_status()` be called for each response.""" + return self._raise_for_status + + @property + def auto_decompress(self) -> bool: + """Should the body response be automatically decompressed.""" + return self._auto_decompress + + @property + def trust_env(self) -> bool: + """ + Should proxies information from environment or netrc be trusted. + + Information is from HTTP_PROXY / HTTPS_PROXY environment variables + or ~/.netrc file if present. + """ + return self._trust_env + + @property + def trace_configs(self) -> List[TraceConfig]: + """A list of TraceConfig instances used for client tracing""" + return self._trace_configs + + def detach(self) -> None: + """Detach connector from session without closing the former. + + Session is switched to closed state anyway. + """ + self._connector = None + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "ClientSession": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]): + + __slots__ = ("_coro", "_resp") + + def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: + self._coro = coro + + def send(self, arg: None) -> "asyncio.Future[Any]": + return self._coro.send(arg) + + def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]": + return self._coro.throw(*args, **kwargs) + + def close(self) -> None: + return self._coro.close() + + def __await__(self) -> Generator[Any, None, _RetType]: + ret = self._coro.__await__() + return ret + + def __iter__(self) -> Generator[Any, None, _RetType]: + return self.__await__() + + async def __aenter__(self) -> _RetType: + self._resp = await self._coro + return self._resp + + +class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): + __slots__ = () + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + # We're basing behavior on the exception as it can be caused by + # user code unrelated to the status of the connection. If you + # would like to close a connection you must do that + # explicitly. Otherwise connection error handling should kick in + # and close/recycle the connection as required. + self._resp.release() + await self._resp.wait_for_close() + + +class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): + __slots__ = () + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self._resp.close() + + +class _SessionRequestContextManager: + + __slots__ = ("_coro", "_resp", "_session") + + def __init__( + self, + coro: Coroutine["asyncio.Future[Any]", None, ClientResponse], + session: ClientSession, + ) -> None: + self._coro = coro + self._resp: Optional[ClientResponse] = None + self._session = session + + async def __aenter__(self) -> ClientResponse: + try: + self._resp = await self._coro + except BaseException: + await self._session.close() + raise + else: + return self._resp + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + assert self._resp is not None + self._resp.close() + await self._session.close() + + +def request( + method: str, + url: StrOrURL, + *, + params: Optional[Mapping[str, str]] = None, + data: Any = None, + json: Any = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Optional[Iterable[str]] = None, + auth: Optional[BasicAuth] = None, + allow_redirects: bool = True, + max_redirects: int = 10, + compress: Optional[str] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + raise_for_status: Optional[bool] = None, + read_until_eof: bool = True, + proxy: Optional[StrOrURL] = None, + proxy_auth: Optional[BasicAuth] = None, + timeout: Union[ClientTimeout, object] = sentinel, + cookies: Optional[LooseCookies] = None, + version: HttpVersion = http.HttpVersion11, + connector: Optional[BaseConnector] = None, + read_bufsize: Optional[int] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + max_line_size: int = 8190, + max_field_size: int = 8190, +) -> _SessionRequestContextManager: + """Constructs and sends a request. + + Returns response object. + method - HTTP method + url - request url + params - (optional) Dictionary or bytes to be sent in the query + string of the new request + data - (optional) Dictionary, bytes, or file-like object to + send in the body of the request + json - (optional) Any json compatible python object + headers - (optional) Dictionary of HTTP Headers to send with + the request + cookies - (optional) Dict object to send with the request + auth - (optional) BasicAuth named tuple represent HTTP Basic Auth + auth - aiohttp.helpers.BasicAuth + allow_redirects - (optional) If set to False, do not follow + redirects + version - Request HTTP version. + compress - Set to True if request has to be compressed + with deflate encoding. + chunked - Set to chunk size for chunked transfer encoding. + expect100 - Expect 100-continue response from server. + connector - BaseConnector sub-class instance to support + connection pooling. + read_until_eof - Read response until eof if response + does not have Content-Length header. + loop - Optional event loop. + timeout - Optional ClientTimeout settings structure, 5min + total timeout by default. + Usage:: + >>> import aiohttp + >>> resp = await aiohttp.request('GET', 'http://python.org/') + >>> resp + + >>> data = await resp.read() + """ + connector_owner = False + if connector is None: + connector_owner = True + connector = TCPConnector(loop=loop, force_close=True) + + session = ClientSession( + loop=loop, + cookies=cookies, + version=version, + timeout=timeout, + connector=connector, + connector_owner=connector_owner, + ) + + return _SessionRequestContextManager( + session._request( + method, + url, + params=params, + data=data, + json=json, + headers=headers, + skip_auto_headers=skip_auto_headers, + auth=auth, + allow_redirects=allow_redirects, + max_redirects=max_redirects, + compress=compress, + chunked=chunked, + expect100=expect100, + raise_for_status=raise_for_status, + read_until_eof=read_until_eof, + proxy=proxy, + proxy_auth=proxy_auth, + read_bufsize=read_bufsize, + max_line_size=max_line_size, + max_field_size=max_field_size, + ), + session, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_exceptions.py new file mode 100644 index 0000000..9b6e442 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_exceptions.py @@ -0,0 +1,346 @@ +"""HTTP related errors.""" + +import asyncio +import warnings +from typing import TYPE_CHECKING, Any, Optional, Tuple, Union + +from .http_parser import RawResponseMessage +from .typedefs import LooseHeaders + +try: + import ssl + + SSLContext = ssl.SSLContext +except ImportError: # pragma: no cover + ssl = SSLContext = None # type: ignore[assignment] + + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo +else: + RequestInfo = ClientResponse = ConnectionKey = None + +__all__ = ( + "ClientError", + "ClientConnectionError", + "ClientOSError", + "ClientConnectorError", + "ClientProxyConnectionError", + "ClientSSLError", + "ClientConnectorSSLError", + "ClientConnectorCertificateError", + "ServerConnectionError", + "ServerTimeoutError", + "ServerDisconnectedError", + "ServerFingerprintMismatch", + "ClientResponseError", + "ClientHttpProxyError", + "WSServerHandshakeError", + "ContentTypeError", + "ClientPayloadError", + "InvalidURL", +) + + +class ClientError(Exception): + """Base class for client connection errors.""" + + +class ClientResponseError(ClientError): + """Base class for exceptions that occur after getting a response. + + request_info: An instance of RequestInfo. + history: A sequence of responses, if redirects occurred. + status: HTTP status code. + message: Error message. + headers: Response headers. + """ + + def __init__( + self, + request_info: RequestInfo, + history: Tuple[ClientResponse, ...], + *, + code: Optional[int] = None, + status: Optional[int] = None, + message: str = "", + headers: Optional[LooseHeaders] = None, + ) -> None: + self.request_info = request_info + if code is not None: + if status is not None: + raise ValueError( + "Both code and status arguments are provided; " + "code is deprecated, use status instead" + ) + warnings.warn( + "code argument is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + if status is not None: + self.status = status + elif code is not None: + self.status = code + else: + self.status = 0 + self.message = message + self.headers = headers + self.history = history + self.args = (request_info, history) + + def __str__(self) -> str: + return "{}, message={!r}, url={!r}".format( + self.status, + self.message, + self.request_info.real_url, + ) + + def __repr__(self) -> str: + args = f"{self.request_info!r}, {self.history!r}" + if self.status != 0: + args += f", status={self.status!r}" + if self.message != "": + args += f", message={self.message!r}" + if self.headers is not None: + args += f", headers={self.headers!r}" + return f"{type(self).__name__}({args})" + + @property + def code(self) -> int: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + return self.status + + @code.setter + def code(self, value: int) -> None: + warnings.warn( + "code property is deprecated, use status instead", + DeprecationWarning, + stacklevel=2, + ) + self.status = value + + +class ContentTypeError(ClientResponseError): + """ContentType found is not valid.""" + + +class WSServerHandshakeError(ClientResponseError): + """websocket server handshake error.""" + + +class ClientHttpProxyError(ClientResponseError): + """HTTP proxy error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + proxy responds with status other than ``200 OK`` + on ``CONNECT`` request. + """ + + +class TooManyRedirects(ClientResponseError): + """Client was redirected too many times.""" + + +class ClientConnectionError(ClientError): + """Base class for client socket errors.""" + + +class ClientOSError(ClientConnectionError, OSError): + """OSError error.""" + + +class ClientConnectorError(ClientOSError): + """Client connector error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + a connection can not be established. + """ + + def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: + self._conn_key = connection_key + self._os_error = os_error + super().__init__(os_error.errno, os_error.strerror) + self.args = (connection_key, os_error) + + @property + def os_error(self) -> OSError: + return self._os_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]: + return self._conn_key.ssl + + def __str__(self) -> str: + return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + # OSError.__reduce__ does too much black magick + __reduce__ = BaseException.__reduce__ + + +class ClientProxyConnectionError(ClientConnectorError): + """Proxy connection error. + + Raised in :class:`aiohttp.connector.TCPConnector` if + connection to proxy can not be established. + """ + + +class UnixClientConnectorError(ClientConnectorError): + """Unix connector error. + + Raised in :py:class:`aiohttp.connector.UnixConnector` + if connection to unix socket can not be established. + """ + + def __init__( + self, path: str, connection_key: ConnectionKey, os_error: OSError + ) -> None: + self._path = path + super().__init__(connection_key, os_error) + + @property + def path(self) -> str: + return self._path + + def __str__(self) -> str: + return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( + self, "default" if self.ssl is True else self.ssl, self.strerror + ) + + +class ServerConnectionError(ClientConnectionError): + """Server connection errors.""" + + +class ServerDisconnectedError(ServerConnectionError): + """Server disconnected.""" + + def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: + if message is None: + message = "Server disconnected" + + self.args = (message,) + self.message = message + + +class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): + """Server timeout error.""" + + +class ServerFingerprintMismatch(ServerConnectionError): + """SSL certificate does not match expected fingerprint.""" + + def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: + self.expected = expected + self.got = got + self.host = host + self.port = port + self.args = (expected, got, host, port) + + def __repr__(self) -> str: + return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( + self.__class__.__name__, self.expected, self.got, self.host, self.port + ) + + +class ClientPayloadError(ClientError): + """Response payload error.""" + + +class InvalidURL(ClientError, ValueError): + """Invalid URL. + + URL used for fetching is malformed, e.g. it doesn't contains host + part. + """ + + # Derive from ValueError for backward compatibility + + def __init__(self, url: Any) -> None: + # The type of url is not yarl.URL because the exception can be raised + # on URL(url) call + super().__init__(url) + + @property + def url(self) -> Any: + return self.args[0] + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.url}>" + + +class ClientSSLError(ClientConnectorError): + """Base error for ssl.*Errors.""" + + +if ssl is not None: + cert_errors = (ssl.CertificateError,) + cert_errors_bases = ( + ClientSSLError, + ssl.CertificateError, + ) + + ssl_errors = (ssl.SSLError,) + ssl_error_bases = (ClientSSLError, ssl.SSLError) +else: # pragma: no cover + cert_errors = tuple() + cert_errors_bases = ( + ClientSSLError, + ValueError, + ) + + ssl_errors = tuple() + ssl_error_bases = (ClientSSLError,) + + +class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc] + """Response ssl error.""" + + +class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc] + """Response certificate error.""" + + def __init__( + self, connection_key: ConnectionKey, certificate_error: Exception + ) -> None: + self._conn_key = connection_key + self._certificate_error = certificate_error + self.args = (connection_key, certificate_error) + + @property + def certificate_error(self) -> Exception: + return self._certificate_error + + @property + def host(self) -> str: + return self._conn_key.host + + @property + def port(self) -> Optional[int]: + return self._conn_key.port + + @property + def ssl(self) -> bool: + return self._conn_key.is_ssl + + def __str__(self) -> str: + return ( + "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " + "[{0.certificate_error.__class__.__name__}: " + "{0.certificate_error.args}]".format(self) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_proto.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_proto.py new file mode 100644 index 0000000..723f5aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_proto.py @@ -0,0 +1,296 @@ +import asyncio +from contextlib import suppress +from typing import Any, Optional, Tuple + +from .base_protocol import BaseProtocol +from .client_exceptions import ( + ClientOSError, + ClientPayloadError, + ServerDisconnectedError, + ServerTimeoutError, +) +from .helpers import ( + _EXC_SENTINEL, + BaseTimerContext, + set_exception, + status_code_must_be_empty_body, +) +from .http import HttpResponseParser, RawResponseMessage +from .http_exceptions import HttpProcessingError +from .streams import EMPTY_PAYLOAD, DataQueue, StreamReader + + +class ResponseHandler(BaseProtocol, DataQueue[Tuple[RawResponseMessage, StreamReader]]): + """Helper class to adapt between Protocol and StreamReader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + BaseProtocol.__init__(self, loop=loop) + DataQueue.__init__(self, loop) + + self._should_close = False + + self._payload: Optional[StreamReader] = None + self._skip_payload = False + self._payload_parser = None + + self._timer = None + + self._tail = b"" + self._upgraded = False + self._parser: Optional[HttpResponseParser] = None + + self._read_timeout: Optional[float] = None + self._read_timeout_handle: Optional[asyncio.TimerHandle] = None + + self._timeout_ceil_threshold: Optional[float] = 5 + + @property + def upgraded(self) -> bool: + return self._upgraded + + @property + def should_close(self) -> bool: + if self._payload is not None and not self._payload.is_eof() or self._upgraded: + return True + + return ( + self._should_close + or self._upgraded + or self.exception() is not None + or self._payload_parser is not None + or len(self) > 0 + or bool(self._tail) + ) + + def force_close(self) -> None: + self._should_close = True + + def close(self) -> None: + transport = self.transport + if transport is not None: + transport.close() + self.transport = None + self._payload = None + self._drop_timeout() + + def is_connected(self) -> bool: + return self.transport is not None and not self.transport.is_closing() + + def connection_lost(self, exc: Optional[BaseException]) -> None: + self._drop_timeout() + + original_connection_error = exc + reraised_exc = original_connection_error + + connection_closed_cleanly = original_connection_error is None + + if self._payload_parser is not None: + with suppress(Exception): # FIXME: log this somehow? + self._payload_parser.feed_eof() + + uncompleted = None + if self._parser is not None: + try: + uncompleted = self._parser.feed_eof() + except Exception as underlying_exc: + if self._payload is not None: + client_payload_exc_msg = ( + f"Response payload is not completed: {underlying_exc !r}" + ) + if not connection_closed_cleanly: + client_payload_exc_msg = ( + f"{client_payload_exc_msg !s}. " + f"{original_connection_error !r}" + ) + set_exception( + self._payload, + ClientPayloadError(client_payload_exc_msg), + underlying_exc, + ) + + if not self.is_eof(): + if isinstance(original_connection_error, OSError): + reraised_exc = ClientOSError(*original_connection_error.args) + if connection_closed_cleanly: + reraised_exc = ServerDisconnectedError(uncompleted) + # assigns self._should_close to True as side effect, + # we do it anyway below + underlying_non_eof_exc = ( + _EXC_SENTINEL + if connection_closed_cleanly + else original_connection_error + ) + assert underlying_non_eof_exc is not None + assert reraised_exc is not None + self.set_exception(reraised_exc, underlying_non_eof_exc) + + self._should_close = True + self._parser = None + self._payload = None + self._payload_parser = None + self._reading_paused = False + + super().connection_lost(reraised_exc) + + def eof_received(self) -> None: + # should call parser.feed_eof() most likely + self._drop_timeout() + + def pause_reading(self) -> None: + super().pause_reading() + self._drop_timeout() + + def resume_reading(self) -> None: + super().resume_reading() + self._reschedule_timeout() + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._should_close = True + self._drop_timeout() + super().set_exception(exc, exc_cause) + + def set_parser(self, parser: Any, payload: Any) -> None: + # TODO: actual types are: + # parser: WebSocketReader + # payload: FlowControlDataQueue + # but they are not generi enough + # Need an ABC for both types + self._payload = payload + self._payload_parser = parser + + self._drop_timeout() + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def set_response_params( + self, + *, + timer: Optional[BaseTimerContext] = None, + skip_payload: bool = False, + read_until_eof: bool = False, + auto_decompress: bool = True, + read_timeout: Optional[float] = None, + read_bufsize: int = 2**16, + timeout_ceil_threshold: float = 5, + max_line_size: int = 8190, + max_field_size: int = 8190, + ) -> None: + self._skip_payload = skip_payload + + self._read_timeout = read_timeout + + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._parser = HttpResponseParser( + self, + self._loop, + read_bufsize, + timer=timer, + payload_exception=ClientPayloadError, + response_with_body=not skip_payload, + read_until_eof=read_until_eof, + auto_decompress=auto_decompress, + max_line_size=max_line_size, + max_field_size=max_field_size, + ) + + if self._tail: + data, self._tail = self._tail, b"" + self.data_received(data) + + def _drop_timeout(self) -> None: + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + self._read_timeout_handle = None + + def _reschedule_timeout(self) -> None: + timeout = self._read_timeout + if self._read_timeout_handle is not None: + self._read_timeout_handle.cancel() + + if timeout: + self._read_timeout_handle = self._loop.call_later( + timeout, self._on_read_timeout + ) + else: + self._read_timeout_handle = None + + def start_timeout(self) -> None: + self._reschedule_timeout() + + def _on_read_timeout(self) -> None: + exc = ServerTimeoutError("Timeout on reading data from socket") + self.set_exception(exc) + if self._payload is not None: + set_exception(self._payload, exc) + + def data_received(self, data: bytes) -> None: + self._reschedule_timeout() + + if not data: + return + + # custom payload parser + if self._payload_parser is not None: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self._payload = None + self._payload_parser = None + + if tail: + self.data_received(tail) + return + else: + if self._upgraded or self._parser is None: + # i.e. websocket connection, websocket parser is not set yet + self._tail += data + else: + # parse http messages + try: + messages, upgraded, tail = self._parser.feed_data(data) + except BaseException as underlying_exc: + if self.transport is not None: + # connection.release() could be called BEFORE + # data_received(), the transport is already + # closed in this case + self.transport.close() + # should_close is True after the call + self.set_exception(HttpProcessingError(), underlying_exc) + return + + self._upgraded = upgraded + + payload: Optional[StreamReader] = None + for message, payload in messages: + if message.should_close: + self._should_close = True + + self._payload = payload + + if self._skip_payload or status_code_must_be_empty_body( + message.code + ): + self.feed_data((message, EMPTY_PAYLOAD), 0) + else: + self.feed_data((message, payload), 0) + if payload is not None: + # new message(s) was processed + # register timeout handler unsubscribing + # either on end-of-stream or immediately for + # EMPTY_PAYLOAD + if payload is not EMPTY_PAYLOAD: + payload.on_eof(self._drop_timeout) + else: + self._drop_timeout() + + if tail: + if upgraded: + self.data_received(tail) + else: + self._tail = tail diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_reqrep.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_reqrep.py new file mode 100644 index 0000000..afe719d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_reqrep.py @@ -0,0 +1,1207 @@ +import asyncio +import codecs +import contextlib +import functools +import io +import re +import sys +import traceback +import warnings +from hashlib import md5, sha1, sha256 +from http.cookies import CookieError, Morsel, SimpleCookie +from types import MappingProxyType, TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Mapping, + Optional, + Tuple, + Type, + Union, + cast, +) + +import attr +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy +from yarl import URL + +from . import hdrs, helpers, http, multipart, payload +from .abc import AbstractStreamWriter +from .client_exceptions import ( + ClientConnectionError, + ClientOSError, + ClientResponseError, + ContentTypeError, + InvalidURL, + ServerFingerprintMismatch, +) +from .compression_utils import HAS_BROTLI +from .formdata import FormData +from .helpers import ( + BaseTimerContext, + BasicAuth, + HeadersMixin, + TimerNoop, + basicauth_from_netrc, + netrc_from_env, + noop, + reify, + set_exception, + set_result, +) +from .http import ( + SERVER_SOFTWARE, + HttpVersion, + HttpVersion10, + HttpVersion11, + StreamWriter, +) +from .log import client_logger +from .streams import StreamReader +from .typedefs import ( + DEFAULT_JSON_DECODER, + JSONDecoder, + LooseCookies, + LooseHeaders, + RawHeaders, +) + +try: + import ssl + from ssl import SSLContext +except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint") + + +if TYPE_CHECKING: + from .client import ClientSession + from .connector import Connection + from .tracing import Trace + + +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") + + +def _gen_default_accept_encoding() -> str: + return "gzip, deflate, br" if HAS_BROTLI else "gzip, deflate" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ContentDisposition: + type: Optional[str] + parameters: "MappingProxyType[str, str]" + filename: Optional[str] + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class RequestInfo: + url: URL + method: str + headers: "CIMultiDictProxy[str]" + real_url: URL = attr.ib() + + @real_url.default + def real_url_default(self) -> URL: + return self.url + + +class Fingerprint: + HASHFUNC_BY_DIGESTLEN = { + 16: md5, + 20: sha1, + 32: sha256, + } + + def __init__(self, fingerprint: bytes) -> None: + digestlen = len(fingerprint) + hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen) + if not hashfunc: + raise ValueError("fingerprint has invalid length") + elif hashfunc is md5 or hashfunc is sha1: + raise ValueError( + "md5 and sha1 are insecure and " "not supported. Use sha256." + ) + self._hashfunc = hashfunc + self._fingerprint = fingerprint + + @property + def fingerprint(self) -> bytes: + return self._fingerprint + + def check(self, transport: asyncio.Transport) -> None: + if not transport.get_extra_info("sslcontext"): + return + sslobj = transport.get_extra_info("ssl_object") + cert = sslobj.getpeercert(binary_form=True) + got = self._hashfunc(cert).digest() + if got != self._fingerprint: + host, port, *_ = transport.get_extra_info("peername") + raise ServerFingerprintMismatch(self._fingerprint, got, host, port) + + +if ssl is not None: + SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None)) +else: # pragma: no cover + SSL_ALLOWED_TYPES = (bool, type(None)) + + +def _merge_ssl_params( + ssl: Union["SSLContext", bool, Fingerprint], + verify_ssl: Optional[bool], + ssl_context: Optional["SSLContext"], + fingerprint: Optional[bytes], +) -> Union["SSLContext", bool, Fingerprint]: + if ssl is None: + ssl = True # Double check for backwards compatibility + if verify_ssl is not None and not verify_ssl: + warnings.warn( + "verify_ssl is deprecated, use ssl=False instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = False + if ssl_context is not None: + warnings.warn( + "ssl_context is deprecated, use ssl=context instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = ssl_context + if fingerprint is not None: + warnings.warn( + "fingerprint is deprecated, " "use ssl=Fingerprint(fingerprint) instead", + DeprecationWarning, + stacklevel=3, + ) + if ssl is not True: + raise ValueError( + "verify_ssl, ssl_context, fingerprint and ssl " + "parameters are mutually exclusive" + ) + else: + ssl = Fingerprint(fingerprint) + if not isinstance(ssl, SSL_ALLOWED_TYPES): + raise TypeError( + "ssl should be SSLContext, bool, Fingerprint or None, " + "got {!r} instead.".format(ssl) + ) + return ssl + + +@attr.s(auto_attribs=True, slots=True, frozen=True) +class ConnectionKey: + # the key should contain an information about used proxy / TLS + # to prevent reusing wrong connections from a pool + host: str + port: Optional[int] + is_ssl: bool + ssl: Union[SSLContext, bool, Fingerprint] + proxy: Optional[URL] + proxy_auth: Optional[BasicAuth] + proxy_headers_hash: Optional[int] # hash(CIMultiDict) + + +def _is_expected_content_type( + response_content_type: str, expected_content_type: str +) -> bool: + if expected_content_type == "application/json": + return json_re.match(response_content_type) is not None + return expected_content_type in response_content_type + + +class ClientRequest: + GET_METHODS = { + hdrs.METH_GET, + hdrs.METH_HEAD, + hdrs.METH_OPTIONS, + hdrs.METH_TRACE, + } + POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT} + ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE}) + + DEFAULT_HEADERS = { + hdrs.ACCEPT: "*/*", + hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(), + } + + body = b"" + auth = None + response = None + + __writer = None # async task for streaming data + _continue = None # waiter future for '100 Continue' response + + # N.B. + # Adding __del__ method with self._writer closing doesn't make sense + # because _writer is instance method, thus it keeps a reference to self. + # Until writer has finished finalizer will not be called. + + def __init__( + self, + method: str, + url: URL, + *, + params: Optional[Mapping[str, str]] = None, + headers: Optional[LooseHeaders] = None, + skip_auto_headers: Iterable[str] = frozenset(), + data: Any = None, + cookies: Optional[LooseCookies] = None, + auth: Optional[BasicAuth] = None, + version: http.HttpVersion = http.HttpVersion11, + compress: Optional[str] = None, + chunked: Optional[bool] = None, + expect100: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + response_class: Optional[Type["ClientResponse"]] = None, + proxy: Optional[URL] = None, + proxy_auth: Optional[BasicAuth] = None, + timer: Optional[BaseTimerContext] = None, + session: Optional["ClientSession"] = None, + ssl: Union[SSLContext, bool, Fingerprint] = True, + proxy_headers: Optional[LooseHeaders] = None, + traces: Optional[List["Trace"]] = None, + trust_env: bool = False, + server_hostname: Optional[str] = None, + ): + if loop is None: + loop = asyncio.get_event_loop() + + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + f"Method cannot contain non-token characters {method!r} " + "(found at least {match.group()!r})" + ) + + assert isinstance(url, URL), url + assert isinstance(proxy, (URL, type(None))), proxy + # FIXME: session is None in tests only, need to fix tests + # assert session is not None + self._session = cast("ClientSession", session) + if params: + q = MultiDict(url.query) + url2 = url.with_query(params) + q.extend(url2.query) + url = url.with_query(q) + self.original_url = url + self.url = url.with_fragment(None) + self.method = method.upper() + self.chunked = chunked + self.compress = compress + self.loop = loop + self.length = None + if response_class is None: + real_response_class = ClientResponse + else: + real_response_class = response_class + self.response_class: Type[ClientResponse] = real_response_class + self._timer = timer if timer is not None else TimerNoop() + self._ssl = ssl if ssl is not None else True + self.server_hostname = server_hostname + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + self.update_version(version) + self.update_host(url) + self.update_headers(headers) + self.update_auto_headers(skip_auto_headers) + self.update_cookies(cookies) + self.update_content_encoding(data) + self.update_auth(auth, trust_env) + self.update_proxy(proxy, proxy_auth, proxy_headers) + + self.update_body_from_data(data) + if data is not None or self.method not in self.GET_METHODS: + self.update_transfer_encoding() + self.update_expect_continue(expect100) + if traces is None: + traces = [] + self._traces = traces + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + return self.__writer + + @_writer.setter + def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None: + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + if writer is not None: + writer.add_done_callback(self.__reset_writer) + + def is_ssl(self) -> bool: + return self.url.scheme in ("https", "wss") + + @property + def ssl(self) -> Union["SSLContext", bool, Fingerprint]: + return self._ssl + + @property + def connection_key(self) -> ConnectionKey: + proxy_headers = self.proxy_headers + if proxy_headers: + h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items())) + else: + h = None + return ConnectionKey( + self.host, + self.port, + self.is_ssl(), + self.ssl, + self.proxy, + self.proxy_auth, + h, + ) + + @property + def host(self) -> str: + ret = self.url.raw_host + assert ret is not None + return ret + + @property + def port(self) -> Optional[int]: + return self.url.port + + @property + def request_info(self) -> RequestInfo: + headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers) + return RequestInfo(self.url, self.method, headers, self.original_url) + + def update_host(self, url: URL) -> None: + """Update destination host, port and connection type (ssl).""" + # get host/port + if not url.raw_host: + raise InvalidURL(url) + + # basic auth info + username, password = url.user, url.password + if username: + self.auth = helpers.BasicAuth(username, password or "") + + def update_version(self, version: Union[http.HttpVersion, str]) -> None: + """Convert request version to two elements tuple. + + parser HTTP version '1.1' => (1, 1) + """ + if isinstance(version, str): + v = [part.strip() for part in version.split(".", 1)] + try: + version = http.HttpVersion(int(v[0]), int(v[1])) + except ValueError: + raise ValueError( + f"Can not parse http version number: {version}" + ) from None + self.version = version + + def update_headers(self, headers: Optional[LooseHeaders]) -> None: + """Update request headers.""" + self.headers: CIMultiDict[str] = CIMultiDict() + + # add host + netloc = cast(str, self.url.raw_host) + if helpers.is_ipv6_address(netloc): + netloc = f"[{netloc}]" + # See https://github.com/aio-libs/aiohttp/issues/3636. + netloc = netloc.rstrip(".") + if self.url.port is not None and not self.url.is_default_port(): + netloc += ":" + str(self.url.port) + self.headers[hdrs.HOST] = netloc + + if headers: + if isinstance(headers, (dict, MultiDictProxy, MultiDict)): + headers = headers.items() # type: ignore[assignment] + + for key, value in headers: # type: ignore[misc] + # A special case for Host header + if key.lower() == "host": + self.headers[key] = value + else: + self.headers.add(key, value) + + def update_auto_headers(self, skip_auto_headers: Iterable[str]) -> None: + self.skip_auto_headers = CIMultiDict( + (hdr, None) for hdr in sorted(skip_auto_headers) + ) + used_headers = self.headers.copy() + used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] + + for hdr, val in self.DEFAULT_HEADERS.items(): + if hdr not in used_headers: + self.headers.add(hdr, val) + + if hdrs.USER_AGENT not in used_headers: + self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE + + def update_cookies(self, cookies: Optional[LooseCookies]) -> None: + """Update request cookies header.""" + if not cookies: + return + + c = SimpleCookie() + if hdrs.COOKIE in self.headers: + c.load(self.headers.get(hdrs.COOKIE, "")) + del self.headers[hdrs.COOKIE] + + if isinstance(cookies, Mapping): + iter_cookies = cookies.items() + else: + iter_cookies = cookies # type: ignore[assignment] + for name, value in iter_cookies: + if isinstance(value, Morsel): + # Preserve coded_value + mrsl_val = value.get(value.key, Morsel()) + mrsl_val.set(value.key, value.value, value.coded_value) + c[name] = mrsl_val + else: + c[name] = value # type: ignore[assignment] + + self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip() + + def update_content_encoding(self, data: Any) -> None: + """Set request content encoding.""" + if data is None: + return + + enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() + if enc: + if self.compress: + raise ValueError( + "compress can not be set " "if Content-Encoding header is set" + ) + elif self.compress: + if not isinstance(self.compress, str): + self.compress = "deflate" + self.headers[hdrs.CONTENT_ENCODING] = self.compress + self.chunked = True # enable chunked, no need to deal with length + + def update_transfer_encoding(self) -> None: + """Analyze transfer-encoding header.""" + te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + if "chunked" in te: + if self.chunked: + raise ValueError( + "chunked can not be set " + 'if "Transfer-Encoding: chunked" header is set' + ) + + elif self.chunked: + if hdrs.CONTENT_LENGTH in self.headers: + raise ValueError( + "chunked can not be set " "if Content-Length header is set" + ) + + self.headers[hdrs.TRANSFER_ENCODING] = "chunked" + else: + if hdrs.CONTENT_LENGTH not in self.headers: + self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body)) + + def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None: + """Set basic auth.""" + if auth is None: + auth = self.auth + if auth is None and trust_env and self.url.host is not None: + netrc_obj = netrc_from_env() + with contextlib.suppress(LookupError): + auth = basicauth_from_netrc(netrc_obj, self.url.host) + if auth is None: + return + + if not isinstance(auth, helpers.BasicAuth): + raise TypeError("BasicAuth() tuple is required instead") + + self.headers[hdrs.AUTHORIZATION] = auth.encode() + + def update_body_from_data(self, body: Any) -> None: + if body is None: + return + + # FormData + if isinstance(body, FormData): + body = body() + + try: + body = payload.PAYLOAD_REGISTRY.get(body, disposition=None) + except payload.LookupError: + body = FormData(body)() + + self.body = body + + # enable chunked encoding if needed + if not self.chunked: + if hdrs.CONTENT_LENGTH not in self.headers: + size = body.size + if size is None: + self.chunked = True + else: + if hdrs.CONTENT_LENGTH not in self.headers: + self.headers[hdrs.CONTENT_LENGTH] = str(size) + + # copy payload headers + assert body.headers + for (key, value) in body.headers.items(): + if key in self.headers: + continue + if key in self.skip_auto_headers: + continue + self.headers[key] = value + + def update_expect_continue(self, expect: bool = False) -> None: + if expect: + self.headers[hdrs.EXPECT] = "100-continue" + elif self.headers.get(hdrs.EXPECT, "").lower() == "100-continue": + expect = True + + if expect: + self._continue = self.loop.create_future() + + def update_proxy( + self, + proxy: Optional[URL], + proxy_auth: Optional[BasicAuth], + proxy_headers: Optional[LooseHeaders], + ) -> None: + if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth): + raise ValueError("proxy_auth must be None or BasicAuth() tuple") + self.proxy = proxy + self.proxy_auth = proxy_auth + self.proxy_headers = proxy_headers + + def keep_alive(self) -> bool: + if self.version < HttpVersion10: + # keep alive not supported at all + return False + if self.version == HttpVersion10: + if self.headers.get(hdrs.CONNECTION) == "keep-alive": + return True + else: # no headers means we close for Http 1.0 + return False + elif self.headers.get(hdrs.CONNECTION) == "close": + return False + + return True + + async def write_bytes( + self, writer: AbstractStreamWriter, conn: "Connection" + ) -> None: + """Support coroutines that yields bytes objects.""" + # 100 response + if self._continue is not None: + try: + await writer.drain() + await self._continue + except asyncio.CancelledError: + return + + protocol = conn.protocol + assert protocol is not None + try: + if isinstance(self.body, payload.Payload): + await self.body.write(writer) + else: + if isinstance(self.body, (bytes, bytearray)): + self.body = (self.body,) # type: ignore[assignment] + + for chunk in self.body: + await writer.write(chunk) # type: ignore[arg-type] + except OSError as underlying_exc: + reraised_exc = underlying_exc + + exc_is_not_timeout = underlying_exc.errno is not None or not isinstance( + underlying_exc, asyncio.TimeoutError + ) + if exc_is_not_timeout: + reraised_exc = ClientOSError( + underlying_exc.errno, + f"Can not write request body for {self.url !s}", + ) + + set_exception(protocol, reraised_exc, underlying_exc) + except asyncio.CancelledError: + await writer.write_eof() + except Exception as underlying_exc: + set_exception( + protocol, + ClientConnectionError( + f"Failed to send bytes into the underlying connection {conn !s}", + ), + underlying_exc, + ) + else: + await writer.write_eof() + protocol.start_timeout() + + async def send(self, conn: "Connection") -> "ClientResponse": + # Specify request target: + # - CONNECT request must send authority form URI + # - not CONNECT proxy must send absolute form URI + # - most common is origin form URI + if self.method == hdrs.METH_CONNECT: + connect_host = self.url.raw_host + assert connect_host is not None + if helpers.is_ipv6_address(connect_host): + connect_host = f"[{connect_host}]" + path = f"{connect_host}:{self.url.port}" + elif self.proxy and not self.is_ssl(): + path = str(self.url) + else: + path = self.url.raw_path + if self.url.raw_query_string: + path += "?" + self.url.raw_query_string + + protocol = conn.protocol + assert protocol is not None + writer = StreamWriter( + protocol, + self.loop, + on_chunk_sent=functools.partial( + self._on_chunk_request_sent, self.method, self.url + ), + on_headers_sent=functools.partial( + self._on_headers_request_sent, self.method, self.url + ), + ) + + if self.compress: + writer.enable_compression(self.compress) + + if self.chunked is not None: + writer.enable_chunking() + + # set default content-type + if ( + self.method in self.POST_METHODS + and hdrs.CONTENT_TYPE not in self.skip_auto_headers + and hdrs.CONTENT_TYPE not in self.headers + ): + self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream" + + # set the connection header + connection = self.headers.get(hdrs.CONNECTION) + if not connection: + if self.keep_alive(): + if self.version == HttpVersion10: + connection = "keep-alive" + else: + if self.version == HttpVersion11: + connection = "close" + + if connection is not None: + self.headers[hdrs.CONNECTION] = connection + + # status + headers + status_line = "{0} {1} HTTP/{v.major}.{v.minor}".format( + self.method, path, v=self.version + ) + await writer.write_headers(status_line, self.headers) + + self._writer = self.loop.create_task(self.write_bytes(writer, conn)) + + response_class = self.response_class + assert response_class is not None + self.response = response_class( + self.method, + self.original_url, + writer=self._writer, + continue100=self._continue, + timer=self._timer, + request_info=self.request_info, + traces=self._traces, + loop=self.loop, + session=self._session, + ) + return self.response + + async def close(self) -> None: + if self._writer is not None: + with contextlib.suppress(asyncio.CancelledError): + await self._writer + + def terminate(self) -> None: + if self._writer is not None: + if not self.loop.is_closed(): + self._writer.cancel() + self._writer.remove_done_callback(self.__reset_writer) + self._writer = None + + async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None: + for trace in self._traces: + await trace.send_request_chunk_sent(method, url, chunk) + + async def _on_headers_request_sent( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + for trace in self._traces: + await trace.send_request_headers(method, url, headers) + + +class ClientResponse(HeadersMixin): + + # Some of these attributes are None when created, + # but will be set by the start() method. + # As the end user will likely never see the None values, we cheat the types below. + # from the Status-Line of the response + version: Optional[HttpVersion] = None # HTTP-Version + status: int = None # type: ignore[assignment] # Status-Code + reason: Optional[str] = None # Reason-Phrase + + content: StreamReader = None # type: ignore[assignment] # Payload stream + _headers: CIMultiDictProxy[str] = None # type: ignore[assignment] + _raw_headers: RawHeaders = None # type: ignore[assignment] + + _connection = None # current connection + _source_traceback: Optional[traceback.StackSummary] = None + # set up by ClientRequest after ClientResponse object creation + # post-init stage allows to not change ctor signature + _closed = True # to allow __del__ for non-initialized properly response + _released = False + __writer = None + + def __init__( + self, + method: str, + url: URL, + *, + writer: "asyncio.Task[None]", + continue100: Optional["asyncio.Future[bool]"], + timer: BaseTimerContext, + request_info: RequestInfo, + traces: List["Trace"], + loop: asyncio.AbstractEventLoop, + session: "ClientSession", + ) -> None: + assert isinstance(url, URL) + + self.method = method + self.cookies = SimpleCookie() + + self._real_url = url + self._url = url.with_fragment(None) + self._body: Any = None + self._writer: Optional[asyncio.Task[None]] = writer + self._continue = continue100 # None by default + self._closed = True + self._history: Tuple[ClientResponse, ...] = () + self._request_info = request_info + self._timer = timer if timer is not None else TimerNoop() + self._cache: Dict[str, Any] = {} + self._traces = traces + self._loop = loop + # store a reference to session #1985 + self._session: Optional[ClientSession] = session + # Save reference to _resolve_charset, so that get_encoding() will still + # work after the response has finished reading the body. + if session is None: + # TODO: Fix session=None in tests (see ClientRequest.__init__). + self._resolve_charset: Callable[ + ["ClientResponse", bytes], str + ] = lambda *_: "utf-8" + else: + self._resolve_charset = session._resolve_charset + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __reset_writer(self, _: object = None) -> None: + self.__writer = None + + @property + def _writer(self) -> Optional["asyncio.Task[None]"]: + return self.__writer + + @_writer.setter + def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None: + if self.__writer is not None: + self.__writer.remove_done_callback(self.__reset_writer) + self.__writer = writer + if writer is not None: + writer.add_done_callback(self.__reset_writer) + + @reify + def url(self) -> URL: + return self._url + + @reify + def url_obj(self) -> URL: + warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2) + return self._url + + @reify + def real_url(self) -> URL: + return self._real_url + + @reify + def host(self) -> str: + assert self._url.host is not None + return self._url.host + + @reify + def headers(self) -> "CIMultiDictProxy[str]": + return self._headers + + @reify + def raw_headers(self) -> RawHeaders: + return self._raw_headers + + @reify + def request_info(self) -> RequestInfo: + return self._request_info + + @reify + def content_disposition(self) -> Optional[ContentDisposition]: + raw = self._headers.get(hdrs.CONTENT_DISPOSITION) + if raw is None: + return None + disposition_type, params_dct = multipart.parse_content_disposition(raw) + params = MappingProxyType(params_dct) + filename = multipart.content_disposition_filename(params) + return ContentDisposition(disposition_type, params, filename) + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + + if self._connection is not None: + self._connection.release() + self._cleanup_writer() + + if self._loop.get_debug(): + kwargs = {"source": self} + _warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs) + context = {"client_response": self, "message": "Unclosed response"} + if self._source_traceback: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __repr__(self) -> str: + out = io.StringIO() + ascii_encodable_url = str(self.url) + if self.reason: + ascii_encodable_reason = self.reason.encode( + "ascii", "backslashreplace" + ).decode("ascii") + else: + ascii_encodable_reason = "None" + print( + "".format( + ascii_encodable_url, self.status, ascii_encodable_reason + ), + file=out, + ) + print(self.headers, file=out) + return out.getvalue() + + @property + def connection(self) -> Optional["Connection"]: + return self._connection + + @reify + def history(self) -> Tuple["ClientResponse", ...]: + """A sequence of of responses, if redirects occurred.""" + return self._history + + @reify + def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]": + links_str = ", ".join(self.headers.getall("link", [])) + + if not links_str: + return MultiDictProxy(MultiDict()) + + links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict() + + for val in re.split(r",(?=\s*<)", links_str): + match = re.match(r"\s*<(.*)>(.*)", val) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + url, params_str = match.groups() + params = params_str.split(";")[1:] + + link: MultiDict[Union[str, URL]] = MultiDict() + + for param in params: + match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M) + if match is None: # pragma: no cover + # the check exists to suppress mypy error + continue + key, _, value, _ = match.groups() + + link.add(key, value) + + key = link.get("rel", url) + + link.add("url", self.url.join(URL(url))) + + links.add(str(key), MultiDictProxy(link)) + + return MultiDictProxy(links) + + async def start(self, connection: "Connection") -> "ClientResponse": + """Start response processing.""" + self._closed = False + self._protocol = connection.protocol + self._connection = connection + + with self._timer: + while True: + # read response + try: + protocol = self._protocol + message, payload = await protocol.read() # type: ignore[union-attr] + except http.HttpProcessingError as exc: + raise ClientResponseError( + self.request_info, + self.history, + status=exc.code, + message=exc.message, + headers=exc.headers, + ) from exc + + if message.code < 100 or message.code > 199 or message.code == 101: + break + + if self._continue is not None: + set_result(self._continue, True) + self._continue = None + + # payload eof handler + payload.on_eof(self._response_eof) + + # response status + self.version = message.version + self.status = message.code + self.reason = message.reason + + # headers + self._headers = message.headers # type is CIMultiDictProxy + self._raw_headers = message.raw_headers # type is Tuple[bytes, bytes] + + # payload + self.content = payload + + # cookies + for hdr in self.headers.getall(hdrs.SET_COOKIE, ()): + try: + self.cookies.load(hdr) + except CookieError as exc: + client_logger.warning("Can not load response cookies: %s", exc) + return self + + def _response_eof(self) -> None: + if self._closed: + return + + # protocol could be None because connection could be detached + protocol = self._connection and self._connection.protocol + if protocol is not None and protocol.upgraded: + return + + self._closed = True + self._cleanup_writer() + self._release_connection() + + @property + def closed(self) -> bool: + return self._closed + + def close(self) -> None: + if not self._released: + self._notify_content() + + self._closed = True + if self._loop is None or self._loop.is_closed(): + return + + self._cleanup_writer() + if self._connection is not None: + self._connection.close() + self._connection = None + + def release(self) -> Any: + if not self._released: + self._notify_content() + + self._closed = True + + self._cleanup_writer() + self._release_connection() + return noop() + + @property + def ok(self) -> bool: + """Returns ``True`` if ``status`` is less than ``400``, ``False`` if not. + + This is **not** a check for ``200 OK`` but a check that the response + status is under 400. + """ + return 400 > self.status + + def raise_for_status(self) -> None: + if not self.ok: + # reason should always be not None for a started response + assert self.reason is not None + self.release() + raise ClientResponseError( + self.request_info, + self.history, + status=self.status, + message=self.reason, + headers=self.headers, + ) + + def _release_connection(self) -> None: + if self._connection is not None: + if self._writer is None: + self._connection.release() + self._connection = None + else: + self._writer.add_done_callback(lambda f: self._release_connection()) + + async def _wait_released(self) -> None: + if self._writer is not None: + await self._writer + self._release_connection() + + def _cleanup_writer(self) -> None: + if self._writer is not None: + self._writer.cancel() + self._session = None + + def _notify_content(self) -> None: + content = self.content + if content and content.exception() is None: + set_exception(content, ClientConnectionError("Connection closed")) + self._released = True + + async def wait_for_close(self) -> None: + if self._writer is not None: + await self._writer + self.release() + + async def read(self) -> bytes: + """Read response payload.""" + if self._body is None: + try: + self._body = await self.content.read() + for trace in self._traces: + await trace.send_response_chunk_received( + self.method, self.url, self._body + ) + except BaseException: + self.close() + raise + elif self._released: # Response explicitly released + raise ClientConnectionError("Connection closed") + + protocol = self._connection and self._connection.protocol + if protocol is None or not protocol.upgraded: + await self._wait_released() # Underlying connection released + return self._body # type: ignore[no-any-return] + + def get_encoding(self) -> str: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + mimetype = helpers.parse_mimetype(ctype) + + encoding = mimetype.parameters.get("charset") + if encoding: + with contextlib.suppress(LookupError): + return codecs.lookup(encoding).name + + if mimetype.type == "application" and ( + mimetype.subtype == "json" or mimetype.subtype == "rdap" + ): + # RFC 7159 states that the default encoding is UTF-8. + # RFC 7483 defines application/rdap+json + return "utf-8" + + if self._body is None: + raise RuntimeError( + "Cannot compute fallback encoding of a not yet read body" + ) + + return self._resolve_charset(self, self._body) + + async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str: + """Read response payload and decode.""" + if self._body is None: + await self.read() + + if encoding is None: + encoding = self.get_encoding() + + return self._body.decode( # type: ignore[no-any-return,union-attr] + encoding, errors=errors + ) + + async def json( + self, + *, + encoding: Optional[str] = None, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + content_type: Optional[str] = "application/json", + ) -> Any: + """Read and decodes JSON response.""" + if self._body is None: + await self.read() + + if content_type: + ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower() + if not _is_expected_content_type(ctype, content_type): + raise ContentTypeError( + self.request_info, + self.history, + message=( + "Attempt to decode JSON with " "unexpected mimetype: %s" % ctype + ), + headers=self.headers, + ) + + stripped = self._body.strip() # type: ignore[union-attr] + if not stripped: + return None + + if encoding is None: + encoding = self.get_encoding() + + return loads(stripped.decode(encoding)) + + async def __aenter__(self) -> "ClientResponse": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + # similar to _RequestContextManager, we do not need to check + # for exceptions, response object can close connection + # if state is broken + self.release() + await self.wait_for_close() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_ws.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_ws.py new file mode 100644 index 0000000..d9c74a3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/client_ws.py @@ -0,0 +1,315 @@ +"""WebSocket client for asyncio.""" + +import asyncio +import sys +from typing import Any, Optional, cast + +from .client_exceptions import ClientError +from .client_reqrep import ClientResponse +from .helpers import call_later, set_result +from .http import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WebSocketError, + WSCloseCode, + WSMessage, + WSMsgType, +) +from .http_websocket import WebSocketWriter # WSMessage +from .streams import EofStream, FlowControlDataQueue +from .typedefs import ( + DEFAULT_JSON_DECODER, + DEFAULT_JSON_ENCODER, + JSONDecoder, + JSONEncoder, +) + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + + +class ClientWebSocketResponse: + def __init__( + self, + reader: "FlowControlDataQueue[WSMessage]", + writer: WebSocketWriter, + protocol: Optional[str], + response: ClientResponse, + timeout: float, + autoclose: bool, + autoping: bool, + loop: asyncio.AbstractEventLoop, + *, + receive_timeout: Optional[float] = None, + heartbeat: Optional[float] = None, + compress: int = 0, + client_notakeover: bool = False, + ) -> None: + self._response = response + self._conn = response.connection + + self._writer = writer + self._reader = reader + self._protocol = protocol + self._closed = False + self._closing = False + self._close_code: Optional[int] = None + self._timeout = timeout + self._receive_timeout = receive_timeout + self._autoclose = autoclose + self._autoping = autoping + self._heartbeat = heartbeat + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None + if heartbeat is not None: + self._pong_heartbeat = heartbeat / 2.0 + self._pong_response_cb: Optional[asyncio.TimerHandle] = None + self._loop = loop + self._waiting: Optional[asyncio.Future[bool]] = None + self._exception: Optional[BaseException] = None + self._compress = compress + self._client_notakeover = client_notakeover + + self._reset_heartbeat() + + def _cancel_heartbeat(self) -> None: + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = None + + if self._heartbeat_cb is not None: + self._heartbeat_cb.cancel() + self._heartbeat_cb = None + + def _reset_heartbeat(self) -> None: + self._cancel_heartbeat() + + if self._heartbeat is not None: + self._heartbeat_cb = call_later( + self._send_heartbeat, + self._heartbeat, + self._loop, + timeout_ceil_threshold=self._conn._connector._timeout_ceil_threshold + if self._conn is not None + else 5, + ) + + def _send_heartbeat(self) -> None: + if self._heartbeat is not None and not self._closed: + # fire-and-forget a task is not perfect but maybe ok for + # sending ping. Otherwise we need a long-living heartbeat + # task in the class. + self._loop.create_task(self._writer.ping()) + + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = call_later( + self._pong_not_received, + self._pong_heartbeat, + self._loop, + timeout_ceil_threshold=self._conn._connector._timeout_ceil_threshold + if self._conn is not None + else 5, + ) + + def _pong_not_received(self) -> None: + if not self._closed: + self._closed = True + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = asyncio.TimeoutError() + self._response.close() + + @property + def closed(self) -> bool: + return self._closed + + @property + def close_code(self) -> Optional[int]: + return self._close_code + + @property + def protocol(self) -> Optional[str]: + return self._protocol + + @property + def compress(self) -> int: + return self._compress + + @property + def client_notakeover(self) -> bool: + return self._client_notakeover + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """extra info from connection transport""" + conn = self._response.connection + if conn is None: + return default + transport = conn.transport + if transport is None: + return default + return transport.get_extra_info(name, default) + + def exception(self) -> Optional[BaseException]: + return self._exception + + async def ping(self, message: bytes = b"") -> None: + await self._writer.ping(message) + + async def pong(self, message: bytes = b"") -> None: + await self._writer.pong(message) + + async def send_str(self, data: str, compress: Optional[int] = None) -> None: + if not isinstance(data, str): + raise TypeError("data argument must be str (%r)" % type(data)) + await self._writer.send(data, binary=False, compress=compress) + + async def send_bytes(self, data: bytes, compress: Optional[int] = None) -> None: + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError("data argument must be byte-ish (%r)" % type(data)) + await self._writer.send(data, binary=True, compress=compress) + + async def send_json( + self, + data: Any, + compress: Optional[int] = None, + *, + dumps: JSONEncoder = DEFAULT_JSON_ENCODER, + ) -> None: + await self.send_str(dumps(data), compress=compress) + + async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool: + # we need to break `receive()` cycle first, + # `close()` may be called from different task + if self._waiting is not None and not self._closing: + self._closing = True + self._reader.feed_data(WS_CLOSING_MESSAGE, 0) + await self._waiting + + if not self._closed: + self._cancel_heartbeat() + self._closed = True + try: + await self._writer.close(code, message) + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if self._close_code: + self._response.close() + return True + + while True: + try: + async with async_timeout.timeout(self._timeout): + msg = await self._reader.read() + except asyncio.CancelledError: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._response.close() + raise + except Exception as exc: + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + self._exception = exc + self._response.close() + return True + + if msg.type == WSMsgType.CLOSE: + self._close_code = msg.data + self._response.close() + return True + else: + return False + + async def receive(self, timeout: Optional[float] = None) -> WSMessage: + while True: + if self._waiting is not None: + raise RuntimeError("Concurrent call to receive() is not allowed") + + if self._closed: + return WS_CLOSED_MESSAGE + elif self._closing: + await self.close() + return WS_CLOSED_MESSAGE + + try: + self._waiting = self._loop.create_future() + try: + async with async_timeout.timeout(timeout or self._receive_timeout): + msg = await self._reader.read() + self._reset_heartbeat() + finally: + waiter = self._waiting + self._waiting = None + set_result(waiter, True) + except (asyncio.CancelledError, asyncio.TimeoutError): + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + raise + except EofStream: + self._close_code = WSCloseCode.OK + await self.close() + return WSMessage(WSMsgType.CLOSED, None, None) + except ClientError: + self._closed = True + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + return WS_CLOSED_MESSAGE + except WebSocketError as exc: + self._close_code = exc.code + await self.close(code=exc.code) + return WSMessage(WSMsgType.ERROR, exc, None) + except Exception as exc: + self._exception = exc + self._closing = True + self._close_code = WSCloseCode.ABNORMAL_CLOSURE + await self.close() + return WSMessage(WSMsgType.ERROR, exc, None) + + if msg.type == WSMsgType.CLOSE: + self._closing = True + self._close_code = msg.data + if not self._closed and self._autoclose: + await self.close() + elif msg.type == WSMsgType.CLOSING: + self._closing = True + elif msg.type == WSMsgType.PING and self._autoping: + await self.pong(msg.data) + continue + elif msg.type == WSMsgType.PONG and self._autoping: + continue + + return msg + + async def receive_str(self, *, timeout: Optional[float] = None) -> str: + msg = await self.receive(timeout) + if msg.type != WSMsgType.TEXT: + raise TypeError(f"Received message {msg.type}:{msg.data!r} is not str") + return cast(str, msg.data) + + async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: + msg = await self.receive(timeout) + if msg.type != WSMsgType.BINARY: + raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") + return cast(bytes, msg.data) + + async def receive_json( + self, + *, + loads: JSONDecoder = DEFAULT_JSON_DECODER, + timeout: Optional[float] = None, + ) -> Any: + data = await self.receive_str(timeout=timeout) + return loads(data) + + def __aiter__(self) -> "ClientWebSocketResponse": + return self + + async def __anext__(self) -> WSMessage: + msg = await self.receive() + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + raise StopAsyncIteration + return msg diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/compression_utils.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/compression_utils.py new file mode 100644 index 0000000..9631d37 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/compression_utils.py @@ -0,0 +1,157 @@ +import asyncio +import zlib +from concurrent.futures import Executor +from typing import Optional, cast + +try: + try: + import brotlicffi as brotli + except ImportError: + import brotli + + HAS_BROTLI = True +except ImportError: # pragma: no cover + HAS_BROTLI = False + +MAX_SYNC_CHUNK_SIZE = 1024 + + +def encoding_to_mode( + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, +) -> int: + if encoding == "gzip": + return 16 + zlib.MAX_WBITS + + return -zlib.MAX_WBITS if suppress_deflate_header else zlib.MAX_WBITS + + +class ZlibBaseHandler: + def __init__( + self, + mode: int, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + self._mode = mode + self._executor = executor + self._max_sync_chunk_size = max_sync_chunk_size + + +class ZLibCompressor(ZlibBaseHandler): + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + level: Optional[int] = None, + wbits: Optional[int] = None, + strategy: int = zlib.Z_DEFAULT_STRATEGY, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + super().__init__( + mode=encoding_to_mode(encoding, suppress_deflate_header) + if wbits is None + else wbits, + executor=executor, + max_sync_chunk_size=max_sync_chunk_size, + ) + if level is None: + self._compressor = zlib.compressobj(wbits=self._mode, strategy=strategy) + else: + self._compressor = zlib.compressobj( + wbits=self._mode, strategy=strategy, level=level + ) + self._compress_lock = asyncio.Lock() + + def compress_sync(self, data: bytes) -> bytes: + return self._compressor.compress(data) + + async def compress(self, data: bytes) -> bytes: + async with self._compress_lock: + # To ensure the stream is consistent in the event + # there are multiple writers, we need to lock + # the compressor so that only one writer can + # compress at a time. + if ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ): + return await asyncio.get_event_loop().run_in_executor( + self._executor, self.compress_sync, data + ) + return self.compress_sync(data) + + def flush(self, mode: int = zlib.Z_FINISH) -> bytes: + return self._compressor.flush(mode) + + +class ZLibDecompressor(ZlibBaseHandler): + def __init__( + self, + encoding: Optional[str] = None, + suppress_deflate_header: bool = False, + executor: Optional[Executor] = None, + max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE, + ): + super().__init__( + mode=encoding_to_mode(encoding, suppress_deflate_header), + executor=executor, + max_sync_chunk_size=max_sync_chunk_size, + ) + self._decompressor = zlib.decompressobj(wbits=self._mode) + + def decompress_sync(self, data: bytes, max_length: int = 0) -> bytes: + return self._decompressor.decompress(data, max_length) + + async def decompress(self, data: bytes, max_length: int = 0) -> bytes: + if ( + self._max_sync_chunk_size is not None + and len(data) > self._max_sync_chunk_size + ): + return await asyncio.get_event_loop().run_in_executor( + self._executor, self.decompress_sync, data, max_length + ) + return self.decompress_sync(data, max_length) + + def flush(self, length: int = 0) -> bytes: + return ( + self._decompressor.flush(length) + if length > 0 + else self._decompressor.flush() + ) + + @property + def eof(self) -> bool: + return self._decompressor.eof + + @property + def unconsumed_tail(self) -> bytes: + return self._decompressor.unconsumed_tail + + @property + def unused_data(self) -> bytes: + return self._decompressor.unused_data + + +class BrotliDecompressor: + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__(self) -> None: + if not HAS_BROTLI: + raise RuntimeError( + "The brotli decompression is not available. " + "Please install `Brotli` module" + ) + self._obj = brotli.Decompressor() + + def decompress_sync(self, data: bytes) -> bytes: + if hasattr(self._obj, "decompress"): + return cast(bytes, self._obj.decompress(data)) + return cast(bytes, self._obj.process(data)) + + def flush(self) -> bytes: + if hasattr(self._obj, "flush"): + return cast(bytes, self._obj.flush()) + return b"" diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/connector.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/connector.py new file mode 100644 index 0000000..f95ebe8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/connector.py @@ -0,0 +1,1511 @@ +import asyncio +import functools +import random +import sys +import traceback +import warnings +from collections import defaultdict, deque +from contextlib import suppress +from http import HTTPStatus +from http.cookies import SimpleCookie +from itertools import cycle, islice +from time import monotonic +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + DefaultDict, + Dict, + Iterator, + List, + Literal, + Optional, + Set, + Tuple, + Type, + Union, + cast, +) + +import attr + +from . import hdrs, helpers +from .abc import AbstractResolver +from .client_exceptions import ( + ClientConnectionError, + ClientConnectorCertificateError, + ClientConnectorError, + ClientConnectorSSLError, + ClientHttpProxyError, + ClientProxyConnectionError, + ServerFingerprintMismatch, + UnixClientConnectorError, + cert_errors, + ssl_errors, +) +from .client_proto import ResponseHandler +from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params +from .helpers import ceil_timeout, get_running_loop, is_ip_address, noop, sentinel +from .locks import EventResultOrError +from .resolver import DefaultResolver + +try: + import ssl + + SSLContext = ssl.SSLContext +except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("BaseConnector", "TCPConnector", "UnixConnector", "NamedPipeConnector") + + +if TYPE_CHECKING: + from .client import ClientTimeout + from .client_reqrep import ConnectionKey + from .tracing import Trace + + +class _DeprecationWaiter: + __slots__ = ("_awaitable", "_awaited") + + def __init__(self, awaitable: Awaitable[Any]) -> None: + self._awaitable = awaitable + self._awaited = False + + def __await__(self) -> Any: + self._awaited = True + return self._awaitable.__await__() + + def __del__(self) -> None: + if not self._awaited: + warnings.warn( + "Connector.close() is a coroutine, " + "please use await connector.close()", + DeprecationWarning, + ) + + +class Connection: + + _source_traceback = None + _transport = None + + def __init__( + self, + connector: "BaseConnector", + key: "ConnectionKey", + protocol: ResponseHandler, + loop: asyncio.AbstractEventLoop, + ) -> None: + self._key = key + self._connector = connector + self._loop = loop + self._protocol: Optional[ResponseHandler] = protocol + self._callbacks: List[Callable[[], None]] = [] + + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def __repr__(self) -> str: + return f"Connection<{self._key}>" + + def __del__(self, _warnings: Any = warnings) -> None: + if self._protocol is not None: + kwargs = {"source": self} + _warnings.warn(f"Unclosed connection {self!r}", ResourceWarning, **kwargs) + if self._loop.is_closed(): + return + + self._connector._release(self._key, self._protocol, should_close=True) + + context = {"client_connection": self, "message": "Unclosed connection"} + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __bool__(self) -> Literal[True]: + """Force subclasses to not be falsy, to make checks simpler.""" + return True + + @property + def loop(self) -> asyncio.AbstractEventLoop: + warnings.warn( + "connector.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + @property + def transport(self) -> Optional[asyncio.Transport]: + if self._protocol is None: + return None + return self._protocol.transport + + @property + def protocol(self) -> Optional[ResponseHandler]: + return self._protocol + + def add_callback(self, callback: Callable[[], None]) -> None: + if callback is not None: + self._callbacks.append(callback) + + def _notify_release(self) -> None: + callbacks, self._callbacks = self._callbacks[:], [] + + for cb in callbacks: + with suppress(Exception): + cb() + + def close(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release(self._key, self._protocol, should_close=True) + self._protocol = None + + def release(self) -> None: + self._notify_release() + + if self._protocol is not None: + self._connector._release( + self._key, self._protocol, should_close=self._protocol.should_close + ) + self._protocol = None + + @property + def closed(self) -> bool: + return self._protocol is None or not self._protocol.is_connected() + + +class _TransportPlaceholder: + """placeholder for BaseConnector.connect function""" + + def close(self) -> None: + pass + + +class BaseConnector: + """Base connector class. + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + timeout_ceil_threshold - Trigger ceiling of timeout values when + it's above timeout_ceil_threshold. + loop - Optional event loop. + """ + + _closed = True # prevent AttributeError in __del__ if ctor was failed + _source_traceback = None + + # abort transport after 2 seconds (cleanup broken connections) + _cleanup_closed_period = 2.0 + + def __init__( + self, + *, + keepalive_timeout: Union[object, None, float] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + ) -> None: + + if force_close: + if keepalive_timeout is not None and keepalive_timeout is not sentinel: + raise ValueError( + "keepalive_timeout cannot " "be set if force_close is True" + ) + else: + if keepalive_timeout is sentinel: + keepalive_timeout = 15.0 + + loop = get_running_loop(loop) + self._timeout_ceil_threshold = timeout_ceil_threshold + + self._closed = False + if loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + self._conns: Dict[ConnectionKey, List[Tuple[ResponseHandler, float]]] = {} + self._limit = limit + self._limit_per_host = limit_per_host + self._acquired: Set[ResponseHandler] = set() + self._acquired_per_host: DefaultDict[ + ConnectionKey, Set[ResponseHandler] + ] = defaultdict(set) + self._keepalive_timeout = cast(float, keepalive_timeout) + self._force_close = force_close + + # {host_key: FIFO list of waiters} + self._waiters = defaultdict(deque) # type: ignore[var-annotated] + + self._loop = loop + self._factory = functools.partial(ResponseHandler, loop=loop) + + self.cookies = SimpleCookie() + + # start keep-alive connection cleanup task + self._cleanup_handle: Optional[asyncio.TimerHandle] = None + + # start cleanup closed transports task + self._cleanup_closed_handle: Optional[asyncio.TimerHandle] = None + self._cleanup_closed_disabled = not enable_cleanup_closed + self._cleanup_closed_transports: List[Optional[asyncio.Transport]] = [] + self._cleanup_closed() + + def __del__(self, _warnings: Any = warnings) -> None: + if self._closed: + return + if not self._conns: + return + + conns = [repr(c) for c in self._conns.values()] + + self._close() + + kwargs = {"source": self} + _warnings.warn(f"Unclosed connector {self!r}", ResourceWarning, **kwargs) + context = { + "connector": self, + "connections": conns, + "message": "Unclosed connector", + } + if self._source_traceback is not None: + context["source_traceback"] = self._source_traceback + self._loop.call_exception_handler(context) + + def __enter__(self) -> "BaseConnector": + warnings.warn( + '"with Connector():" is deprecated, ' + 'use "async with Connector():" instead', + DeprecationWarning, + ) + return self + + def __exit__(self, *exc: Any) -> None: + self._close() + + async def __aenter__(self) -> "BaseConnector": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + exc_traceback: Optional[TracebackType] = None, + ) -> None: + await self.close() + + @property + def force_close(self) -> bool: + """Ultimately close connection on releasing if True.""" + return self._force_close + + @property + def limit(self) -> int: + """The total number for simultaneous connections. + + If limit is 0 the connector has no limit. + The default limit size is 100. + """ + return self._limit + + @property + def limit_per_host(self) -> int: + """The limit for simultaneous connections to the same endpoint. + + Endpoints are the same if they are have equal + (host, port, is_ssl) triple. + """ + return self._limit_per_host + + def _cleanup(self) -> None: + """Cleanup unused transports.""" + if self._cleanup_handle: + self._cleanup_handle.cancel() + # _cleanup_handle should be unset, otherwise _release() will not + # recreate it ever! + self._cleanup_handle = None + + now = self._loop.time() + timeout = self._keepalive_timeout + + if self._conns: + connections = {} + deadline = now - timeout + for key, conns in self._conns.items(): + alive = [] + for proto, use_time in conns: + if proto.is_connected(): + if use_time - deadline < 0: + transport = proto.transport + proto.close() + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + else: + alive.append((proto, use_time)) + else: + transport = proto.transport + proto.close() + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + + if alive: + connections[key] = alive + + self._conns = connections + + if self._conns: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def _drop_acquired_per_host( + self, key: "ConnectionKey", val: ResponseHandler + ) -> None: + acquired_per_host = self._acquired_per_host + if key not in acquired_per_host: + return + conns = acquired_per_host[key] + conns.remove(val) + if not conns: + del self._acquired_per_host[key] + + def _cleanup_closed(self) -> None: + """Double confirmation for transport close. + + Some broken ssl servers may leave socket open without proper close. + """ + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + self._cleanup_closed_transports = [] + + if not self._cleanup_closed_disabled: + self._cleanup_closed_handle = helpers.weakref_handle( + self, + "_cleanup_closed", + self._cleanup_closed_period, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + def close(self) -> Awaitable[None]: + """Close all opened transports.""" + self._close() + return _DeprecationWaiter(noop()) + + def _close(self) -> None: + if self._closed: + return + + self._closed = True + + try: + if self._loop.is_closed(): + return + + # cancel cleanup task + if self._cleanup_handle: + self._cleanup_handle.cancel() + + # cancel cleanup close task + if self._cleanup_closed_handle: + self._cleanup_closed_handle.cancel() + + for data in self._conns.values(): + for proto, t0 in data: + proto.close() + + for proto in self._acquired: + proto.close() + + for transport in self._cleanup_closed_transports: + if transport is not None: + transport.abort() + + finally: + self._conns.clear() + self._acquired.clear() + self._waiters.clear() + self._cleanup_handle = None + self._cleanup_closed_transports.clear() + self._cleanup_closed_handle = None + + @property + def closed(self) -> bool: + """Is connector closed. + + A readonly property. + """ + return self._closed + + def _available_connections(self, key: "ConnectionKey") -> int: + """ + Return number of available connections. + + The limit, limit_per_host and the connection key are taken into account. + + If it returns less than 1 means that there are no connections + available. + """ + if self._limit: + # total calc available connections + available = self._limit - len(self._acquired) + + # check limit per host + if ( + self._limit_per_host + and available > 0 + and key in self._acquired_per_host + ): + acquired = self._acquired_per_host.get(key) + assert acquired is not None + available = self._limit_per_host - len(acquired) + + elif self._limit_per_host and key in self._acquired_per_host: + # check limit per host + acquired = self._acquired_per_host.get(key) + assert acquired is not None + available = self._limit_per_host - len(acquired) + else: + available = 1 + + return available + + async def connect( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Connection: + """Get from pool or create new connection.""" + key = req.connection_key + available = self._available_connections(key) + + # Wait if there are no available connections or if there are/were + # waiters (i.e. don't steal connection from a waiter about to wake up) + if available <= 0 or key in self._waiters: + fut = self._loop.create_future() + + # This connection will now count towards the limit. + self._waiters[key].append(fut) + + if traces: + for trace in traces: + await trace.send_connection_queued_start() + + try: + await fut + except BaseException as e: + if key in self._waiters: + # remove a waiter even if it was cancelled, normally it's + # removed when it's notified + try: + self._waiters[key].remove(fut) + except ValueError: # fut may no longer be in list + pass + + raise e + finally: + if key in self._waiters and not self._waiters[key]: + del self._waiters[key] + + if traces: + for trace in traces: + await trace.send_connection_queued_end() + + proto = self._get(key) + if proto is None: + placeholder = cast(ResponseHandler, _TransportPlaceholder()) + self._acquired.add(placeholder) + self._acquired_per_host[key].add(placeholder) + + if traces: + for trace in traces: + await trace.send_connection_create_start() + + try: + proto = await self._create_connection(req, traces, timeout) + if self._closed: + proto.close() + raise ClientConnectionError("Connector is closed.") + except BaseException: + if not self._closed: + self._acquired.remove(placeholder) + self._drop_acquired_per_host(key, placeholder) + self._release_waiter() + raise + else: + if not self._closed: + self._acquired.remove(placeholder) + self._drop_acquired_per_host(key, placeholder) + + if traces: + for trace in traces: + await trace.send_connection_create_end() + else: + if traces: + # Acquire the connection to prevent race conditions with limits + placeholder = cast(ResponseHandler, _TransportPlaceholder()) + self._acquired.add(placeholder) + self._acquired_per_host[key].add(placeholder) + for trace in traces: + await trace.send_connection_reuseconn() + self._acquired.remove(placeholder) + self._drop_acquired_per_host(key, placeholder) + + self._acquired.add(proto) + self._acquired_per_host[key].add(proto) + return Connection(self, key, proto, self._loop) + + def _get(self, key: "ConnectionKey") -> Optional[ResponseHandler]: + try: + conns = self._conns[key] + except KeyError: + return None + + t1 = self._loop.time() + while conns: + proto, t0 = conns.pop() + if proto.is_connected(): + if t1 - t0 > self._keepalive_timeout: + transport = proto.transport + proto.close() + # only for SSL transports + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + else: + if not conns: + # The very last connection was reclaimed: drop the key + del self._conns[key] + return proto + else: + transport = proto.transport + proto.close() + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + + # No more connections: drop the key + del self._conns[key] + return None + + def _release_waiter(self) -> None: + """ + Iterates over all waiters until one to be released is found. + + The one to be released is not finished and + belongs to a host that has available connections. + """ + if not self._waiters: + return + + # Having the dict keys ordered this avoids to iterate + # at the same order at each call. + queues = list(self._waiters.keys()) + random.shuffle(queues) + + for key in queues: + if self._available_connections(key) < 1: + continue + + waiters = self._waiters[key] + while waiters: + waiter = waiters.popleft() + if not waiter.done(): + waiter.set_result(None) + return + + def _release_acquired(self, key: "ConnectionKey", proto: ResponseHandler) -> None: + if self._closed: + # acquired connection is already released on connector closing + return + + try: + self._acquired.remove(proto) + self._drop_acquired_per_host(key, proto) + except KeyError: # pragma: no cover + # this may be result of undetermenistic order of objects + # finalization due garbage collection. + pass + else: + self._release_waiter() + + def _release( + self, + key: "ConnectionKey", + protocol: ResponseHandler, + *, + should_close: bool = False, + ) -> None: + if self._closed: + # acquired connection is already released on connector closing + return + + self._release_acquired(key, protocol) + + if self._force_close: + should_close = True + + if should_close or protocol.should_close: + transport = protocol.transport + protocol.close() + + if key.is_ssl and not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transport) + else: + conns = self._conns.get(key) + if conns is None: + conns = self._conns[key] = [] + conns.append((protocol, self._loop.time())) + + if self._cleanup_handle is None: + self._cleanup_handle = helpers.weakref_handle( + self, + "_cleanup", + self._keepalive_timeout, + self._loop, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + raise NotImplementedError() + + +class _DNSCacheTable: + def __init__(self, ttl: Optional[float] = None) -> None: + self._addrs_rr: Dict[Tuple[str, int], Tuple[Iterator[Dict[str, Any]], int]] = {} + self._timestamps: Dict[Tuple[str, int], float] = {} + self._ttl = ttl + + def __contains__(self, host: object) -> bool: + return host in self._addrs_rr + + def add(self, key: Tuple[str, int], addrs: List[Dict[str, Any]]) -> None: + self._addrs_rr[key] = (cycle(addrs), len(addrs)) + + if self._ttl is not None: + self._timestamps[key] = monotonic() + + def remove(self, key: Tuple[str, int]) -> None: + self._addrs_rr.pop(key, None) + + if self._ttl is not None: + self._timestamps.pop(key, None) + + def clear(self) -> None: + self._addrs_rr.clear() + self._timestamps.clear() + + def next_addrs(self, key: Tuple[str, int]) -> List[Dict[str, Any]]: + loop, length = self._addrs_rr[key] + addrs = list(islice(loop, length)) + # Consume one more element to shift internal state of `cycle` + next(loop) + return addrs + + def expired(self, key: Tuple[str, int]) -> bool: + if self._ttl is None: + return False + + return self._timestamps[key] + self._ttl < monotonic() + + +class TCPConnector(BaseConnector): + """TCP connector. + + verify_ssl - Set to True to check ssl certifications. + fingerprint - Pass the binary sha256 + digest of the expected certificate in DER format to verify + that the certificate the server presents matches. See also + https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning + resolver - Enable DNS lookups and use this + resolver + use_dns_cache - Use memory cache for DNS lookups. + ttl_dns_cache - Max seconds having cached a DNS entry, None forever. + family - socket address family + local_addr - local tuple of (host, port) to bind socket to + + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + enable_cleanup_closed - Enables clean-up closed ssl transports. + Disabled by default. + loop - Optional event loop. + """ + + def __init__( + self, + *, + verify_ssl: bool = True, + fingerprint: Optional[bytes] = None, + use_dns_cache: bool = True, + ttl_dns_cache: Optional[int] = 10, + family: int = 0, + ssl_context: Optional[SSLContext] = None, + ssl: Union[bool, Fingerprint, SSLContext] = True, + local_addr: Optional[Tuple[str, int]] = None, + resolver: Optional[AbstractResolver] = None, + keepalive_timeout: Union[None, float, object] = sentinel, + force_close: bool = False, + limit: int = 100, + limit_per_host: int = 0, + enable_cleanup_closed: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + timeout_ceil_threshold: float = 5, + ): + super().__init__( + keepalive_timeout=keepalive_timeout, + force_close=force_close, + limit=limit, + limit_per_host=limit_per_host, + enable_cleanup_closed=enable_cleanup_closed, + loop=loop, + timeout_ceil_threshold=timeout_ceil_threshold, + ) + + self._ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) + if resolver is None: + resolver = DefaultResolver(loop=self._loop) + self._resolver = resolver + + self._use_dns_cache = use_dns_cache + self._cached_hosts = _DNSCacheTable(ttl=ttl_dns_cache) + self._throttle_dns_events: Dict[Tuple[str, int], EventResultOrError] = {} + self._family = family + self._local_addr = local_addr + + def close(self) -> Awaitable[None]: + """Close all ongoing DNS calls.""" + for ev in self._throttle_dns_events.values(): + ev.cancel() + + return super().close() + + @property + def family(self) -> int: + """Socket family like AF_INET.""" + return self._family + + @property + def use_dns_cache(self) -> bool: + """True if local DNS caching is enabled.""" + return self._use_dns_cache + + def clear_dns_cache( + self, host: Optional[str] = None, port: Optional[int] = None + ) -> None: + """Remove specified host/port or clear all dns local cache.""" + if host is not None and port is not None: + self._cached_hosts.remove((host, port)) + elif host is not None or port is not None: + raise ValueError("either both host and port " "or none of them are allowed") + else: + self._cached_hosts.clear() + + async def _resolve_host( + self, host: str, port: int, traces: Optional[List["Trace"]] = None + ) -> List[Dict[str, Any]]: + """Resolve host and return list of addresses.""" + if is_ip_address(host): + return [ + { + "hostname": host, + "host": host, + "port": port, + "family": self._family, + "proto": 0, + "flags": 0, + } + ] + + if not self._use_dns_cache: + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + res = await self._resolver.resolve(host, port, family=self._family) + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + return res + + key = (host, port) + if key in self._cached_hosts and not self._cached_hosts.expired(key): + # get result early, before any await (#4014) + result = self._cached_hosts.next_addrs(key) + + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + return result + + # + # If multiple connectors are resolving the same host, we wait + # for the first one to resolve and then use the result for all of them. + # We use a throttle event to ensure that we only resolve the host once + # and then use the result for all the waiters. + # + # In this case we need to create a task to ensure that we can shield + # the task from cancellation as cancelling this lookup should not cancel + # the underlying lookup or else the cancel event will get broadcast to + # all the waiters across all connections. + # + resolved_host_task = asyncio.create_task( + self._resolve_host_with_throttle(key, host, port, traces) + ) + try: + return await asyncio.shield(resolved_host_task) + except asyncio.CancelledError: + + def drop_exception(fut: "asyncio.Future[List[Dict[str, Any]]]") -> None: + with suppress(Exception, asyncio.CancelledError): + fut.result() + + resolved_host_task.add_done_callback(drop_exception) + raise + + async def _resolve_host_with_throttle( + self, + key: Tuple[str, int], + host: str, + port: int, + traces: Optional[List["Trace"]], + ) -> List[Dict[str, Any]]: + """Resolve host with a dns events throttle.""" + if key in self._throttle_dns_events: + # get event early, before any await (#4014) + event = self._throttle_dns_events[key] + if traces: + for trace in traces: + await trace.send_dns_cache_hit(host) + await event.wait() + else: + # update dict early, before any await (#4014) + self._throttle_dns_events[key] = EventResultOrError(self._loop) + if traces: + for trace in traces: + await trace.send_dns_cache_miss(host) + try: + + if traces: + for trace in traces: + await trace.send_dns_resolvehost_start(host) + + addrs = await self._resolver.resolve(host, port, family=self._family) + if traces: + for trace in traces: + await trace.send_dns_resolvehost_end(host) + + self._cached_hosts.add(key, addrs) + self._throttle_dns_events[key].set() + except BaseException as e: + # any DNS exception, independently of the implementation + # is set for the waiters to raise the same exception. + self._throttle_dns_events[key].set(exc=e) + raise + finally: + self._throttle_dns_events.pop(key) + + return self._cached_hosts.next_addrs(key) + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + """Create connection. + + Has same keyword arguments as BaseEventLoop.create_connection. + """ + if req.proxy: + _, proto = await self._create_proxy_connection(req, traces, timeout) + else: + _, proto = await self._create_direct_connection(req, traces, timeout) + + return proto + + @staticmethod + @functools.lru_cache(None) + def _make_ssl_context(verified: bool) -> SSLContext: + if verified: + return ssl.create_default_context() + else: + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + try: + sslcontext.options |= ssl.OP_NO_COMPRESSION + except AttributeError as attr_err: + warnings.warn( + "{!s}: The Python interpreter is compiled " + "against OpenSSL < 1.0.0. Ref: " + "https://docs.python.org/3/library/ssl.html" + "#ssl.OP_NO_COMPRESSION".format(attr_err), + ) + sslcontext.set_default_verify_paths() + return sslcontext + + def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: + """Logic to get the correct SSL context + + 0. if req.ssl is false, return None + + 1. if ssl_context is specified in req, use it + 2. if _ssl_context is specified in self, use it + 3. otherwise: + 1. if verify_ssl is not specified in req, use self.ssl_context + (will generate a default context according to self.verify_ssl) + 2. if verify_ssl is True in req, generate a default SSL context + 3. if verify_ssl is False in req, generate a SSL context that + won't verify + """ + if req.is_ssl(): + if ssl is None: # pragma: no cover + raise RuntimeError("SSL is not supported.") + sslcontext = req.ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return self._make_ssl_context(False) + sslcontext = self._ssl + if isinstance(sslcontext, ssl.SSLContext): + return sslcontext + if sslcontext is not True: + # not verified or fingerprinted + return self._make_ssl_context(False) + return self._make_ssl_context(True) + else: + return None + + def _get_fingerprint(self, req: ClientRequest) -> Optional["Fingerprint"]: + ret = req.ssl + if isinstance(ret, Fingerprint): + return ret + ret = self._ssl + if isinstance(ret, Fingerprint): + return ret + return None + + async def _wrap_create_connection( + self, + *args: Any, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + **kwargs: Any, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + return await self._loop.create_connection(*args, **kwargs) + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + + def _fail_on_no_start_tls(self, req: "ClientRequest") -> None: + """Raise a :py:exc:`RuntimeError` on missing ``start_tls()``. + + It is necessary for TLS-in-TLS so that it is possible to + send HTTPS queries through HTTPS proxies. + + This doesn't affect regular HTTP requests, though. + """ + if not req.is_ssl(): + return + + proxy_url = req.proxy + assert proxy_url is not None + if proxy_url.scheme != "https": + return + + self._check_loop_for_start_tls() + + def _check_loop_for_start_tls(self) -> None: + try: + self._loop.start_tls + except AttributeError as attr_exc: + raise RuntimeError( + "An HTTPS request is being sent through an HTTPS proxy. " + "This needs support for TLS in TLS but it is not implemented " + "in your runtime for the stdlib asyncio.\n\n" + "Please upgrade to Python 3.11 or higher. For more details, " + "please see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n" + "* https://docs.aiohttp.org/en/stable/" + "client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + ) from attr_exc + + def _loop_supports_start_tls(self) -> bool: + try: + self._check_loop_for_start_tls() + except RuntimeError: + return False + else: + return True + + def _warn_about_tls_in_tls( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + ) -> None: + """Issue a warning if the requested URL has HTTPS scheme.""" + if req.request_info.url.scheme != "https": + return + + asyncio_supports_tls_in_tls = getattr( + underlying_transport, + "_start_tls_compatible", + False, + ) + + if asyncio_supports_tls_in_tls: + return + + warnings.warn( + "An HTTPS request is being sent through an HTTPS proxy. " + "This support for TLS in TLS is known to be disabled " + "in the stdlib asyncio (Python <3.11). This is why you'll probably see " + "an error in the log below.\n\n" + "It is possible to enable it via monkeypatching. " + "For more details, see:\n" + "* https://bugs.python.org/issue37179\n" + "* https://github.com/python/cpython/pull/28073\n\n" + "You can temporarily patch this as follows:\n" + "* https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support\n" + "* https://github.com/aio-libs/aiohttp/discussions/6044\n", + RuntimeWarning, + source=self, + # Why `4`? At least 3 of the calls in the stack originate + # from the methods in this class. + stacklevel=3, + ) + + async def _start_tls_connection( + self, + underlying_transport: asyncio.Transport, + req: ClientRequest, + timeout: "ClientTimeout", + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + """Wrap the raw TCP transport with TLS.""" + tls_proto = self._factory() # Create a brand new proto for TLS + + # Safety of the `cast()` call here is based on the fact that + # internally `_get_ssl_context()` only returns `None` when + # `req.is_ssl()` evaluates to `False` which is never gonna happen + # in this code path. Of course, it's rather fragile + # maintainability-wise but this is to be solved separately. + sslcontext = cast(ssl.SSLContext, self._get_ssl_context(req)) + + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + try: + tls_transport = await self._loop.start_tls( + underlying_transport, + tls_proto, + sslcontext, + server_hostname=req.server_hostname or req.host, + ssl_handshake_timeout=timeout.total, + ) + except BaseException: + # We need to close the underlying transport since + # `start_tls()` probably failed before it had a + # chance to do this: + underlying_transport.close() + raise + except cert_errors as exc: + raise ClientConnectorCertificateError(req.connection_key, exc) from exc + except ssl_errors as exc: + raise ClientConnectorSSLError(req.connection_key, exc) from exc + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise client_error(req.connection_key, exc) from exc + except TypeError as type_err: + # Example cause looks like this: + # TypeError: transport is not supported by start_tls() + + raise ClientConnectionError( + "Cannot initialize a TLS-in-TLS connection to host " + f"{req.host!s}:{req.port:d} through an underlying connection " + f"to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} " + f"[{type_err!s}]" + ) from type_err + else: + if tls_transport is None: + msg = "Failed to start TLS (possibly caused by closing transport)" + raise client_error(req.connection_key, OSError(msg)) + tls_proto.connection_made( + tls_transport + ) # Kick the state machine of the new TLS protocol + + return tls_transport, tls_proto + + async def _create_direct_connection( + self, + req: ClientRequest, + traces: List["Trace"], + timeout: "ClientTimeout", + *, + client_error: Type[Exception] = ClientConnectorError, + ) -> Tuple[asyncio.Transport, ResponseHandler]: + sslcontext = self._get_ssl_context(req) + fingerprint = self._get_fingerprint(req) + + host = req.url.raw_host + assert host is not None + # Replace multiple trailing dots with a single one. + # A trailing dot is only present for fully-qualified domain names. + # See https://github.com/aio-libs/aiohttp/pull/7364. + if host.endswith(".."): + host = host.rstrip(".") + "." + port = req.port + assert port is not None + try: + # Cancelling this lookup should not cancel the underlying lookup + # or else the cancel event will get broadcast to all the waiters + # across all connections. + hosts = await self._resolve_host(host, port, traces=traces) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + # in case of proxy it is not ClientProxyConnectionError + # it is problem of resolving proxy ip itself + raise ClientConnectorError(req.connection_key, exc) from exc + + last_exc: Optional[Exception] = None + + for hinfo in hosts: + host = hinfo["host"] + port = hinfo["port"] + + # Strip trailing dots, certificates contain FQDN without dots. + # See https://github.com/aio-libs/aiohttp/issues/3636 + server_hostname = ( + (req.server_hostname or hinfo["hostname"]).rstrip(".") + if sslcontext + else None + ) + + try: + transp, proto = await self._wrap_create_connection( + self._factory, + host, + port, + timeout=timeout, + ssl=sslcontext, + family=hinfo["family"], + proto=hinfo["proto"], + flags=hinfo["flags"], + server_hostname=server_hostname, + local_addr=self._local_addr, + req=req, + client_error=client_error, + ) + except ClientConnectorError as exc: + last_exc = exc + continue + + if req.is_ssl() and fingerprint: + try: + fingerprint.check(transp) + except ServerFingerprintMismatch as exc: + transp.close() + if not self._cleanup_closed_disabled: + self._cleanup_closed_transports.append(transp) + last_exc = exc + continue + + return transp, proto + else: + assert last_exc is not None + raise last_exc + + async def _create_proxy_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: + self._fail_on_no_start_tls(req) + runtime_has_start_tls = self._loop_supports_start_tls() + + headers: Dict[str, str] = {} + if req.proxy_headers is not None: + headers = req.proxy_headers # type: ignore[assignment] + headers[hdrs.HOST] = req.headers[hdrs.HOST] + + url = req.proxy + assert url is not None + proxy_req = ClientRequest( + hdrs.METH_GET, + url, + headers=headers, + auth=req.proxy_auth, + loop=self._loop, + ssl=req.ssl, + ) + + # create connection to proxy server + transport, proto = await self._create_direct_connection( + proxy_req, [], timeout, client_error=ClientProxyConnectionError + ) + + # Many HTTP proxies has buggy keepalive support. Let's not + # reuse connection but close it after processing every + # response. + proto.force_close() + + auth = proxy_req.headers.pop(hdrs.AUTHORIZATION, None) + if auth is not None: + if not req.is_ssl(): + req.headers[hdrs.PROXY_AUTHORIZATION] = auth + else: + proxy_req.headers[hdrs.PROXY_AUTHORIZATION] = auth + + if req.is_ssl(): + if runtime_has_start_tls: + self._warn_about_tls_in_tls(transport, req) + + # For HTTPS requests over HTTP proxy + # we must notify proxy to tunnel connection + # so we send CONNECT command: + # CONNECT www.python.org:443 HTTP/1.1 + # Host: www.python.org + # + # next we must do TLS handshake and so on + # to do this we must wrap raw socket into secure one + # asyncio handles this perfectly + proxy_req.method = hdrs.METH_CONNECT + proxy_req.url = req.url + key = attr.evolve( + req.connection_key, proxy=None, proxy_auth=None, proxy_headers_hash=None + ) + conn = Connection(self, key, proto, self._loop) + proxy_resp = await proxy_req.send(conn) + try: + protocol = conn._protocol + assert protocol is not None + + # read_until_eof=True will ensure the connection isn't closed + # once the response is received and processed allowing + # START_TLS to work on the connection below. + protocol.set_response_params( + read_until_eof=runtime_has_start_tls, + timeout_ceil_threshold=self._timeout_ceil_threshold, + ) + resp = await proxy_resp.start(conn) + except BaseException: + proxy_resp.close() + conn.close() + raise + else: + conn._protocol = None + conn._transport = None + try: + if resp.status != 200: + message = resp.reason + if message is None: + message = HTTPStatus(resp.status).phrase + raise ClientHttpProxyError( + proxy_resp.request_info, + resp.history, + status=resp.status, + message=message, + headers=resp.headers, + ) + if not runtime_has_start_tls: + rawsock = transport.get_extra_info("socket", default=None) + if rawsock is None: + raise RuntimeError( + "Transport does not expose socket instance" + ) + # Duplicate the socket, so now we can close proxy transport + rawsock = rawsock.dup() + except BaseException: + # It shouldn't be closed in `finally` because it's fed to + # `loop.start_tls()` and the docs say not to touch it after + # passing there. + transport.close() + raise + finally: + if not runtime_has_start_tls: + transport.close() + + if not runtime_has_start_tls: + # HTTP proxy with support for upgrade to HTTPS + sslcontext = self._get_ssl_context(req) + return await self._wrap_create_connection( + self._factory, + timeout=timeout, + ssl=sslcontext, + sock=rawsock, + server_hostname=req.host, + req=req, + ) + + return await self._start_tls_connection( + # Access the old transport for the last time before it's + # closed and forgotten forever: + transport, + req=req, + timeout=timeout, + ) + finally: + proxy_resp.close() + + return transport, proto + + +class UnixConnector(BaseConnector): + """Unix socket connector. + + path - Unix socket path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + self._path = path + + @property + def path(self) -> str: + """Path to unix socket.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_unix_connection( + self._factory, self._path + ) + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise UnixClientConnectorError(self.path, req.connection_key, exc) from exc + + return proto + + +class NamedPipeConnector(BaseConnector): + """Named pipe connector. + + Only supported by the proactor event loop. + See also: https://docs.python.org/3/library/asyncio-eventloop.html + + path - Windows named pipe path. + keepalive_timeout - (optional) Keep-alive timeout. + force_close - Set to True to force close and do reconnect + after each request (and between redirects). + limit - The total number of simultaneous connections. + limit_per_host - Number of simultaneous connections to one host. + loop - Optional event loop. + """ + + def __init__( + self, + path: str, + force_close: bool = False, + keepalive_timeout: Union[object, float, None] = sentinel, + limit: int = 100, + limit_per_host: int = 0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__( + force_close=force_close, + keepalive_timeout=keepalive_timeout, + limit=limit, + limit_per_host=limit_per_host, + loop=loop, + ) + if not isinstance( + self._loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] + ): + raise RuntimeError( + "Named Pipes only available in proactor " "loop under windows" + ) + self._path = path + + @property + def path(self) -> str: + """Path to the named pipe.""" + return self._path + + async def _create_connection( + self, req: ClientRequest, traces: List["Trace"], timeout: "ClientTimeout" + ) -> ResponseHandler: + try: + async with ceil_timeout( + timeout.sock_connect, ceil_threshold=timeout.ceil_threshold + ): + _, proto = await self._loop.create_pipe_connection( # type: ignore[attr-defined] + self._factory, self._path + ) + # the drain is required so that the connection_made is called + # and transport is set otherwise it is not set before the + # `assert conn.transport is not None` + # in client.py's _request method + await asyncio.sleep(0) + # other option is to manually set transport like + # `proto.transport = trans` + except OSError as exc: + if exc.errno is None and isinstance(exc, asyncio.TimeoutError): + raise + raise ClientConnectorError(req.connection_key, exc) from exc + + return cast(ResponseHandler, proto) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/cookiejar.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/cookiejar.py new file mode 100644 index 0000000..a348f11 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/cookiejar.py @@ -0,0 +1,419 @@ +import asyncio +import calendar +import contextlib +import datetime +import os # noqa +import pathlib +import pickle +import re +import time +from collections import defaultdict +from http.cookies import BaseCookie, Morsel, SimpleCookie +from math import ceil +from typing import ( # noqa + DefaultDict, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Set, + Tuple, + Union, + cast, +) + +from yarl import URL + +from .abc import AbstractCookieJar, ClearCookiePredicate +from .helpers import is_ip_address +from .typedefs import LooseCookies, PathLike, StrOrURL + +__all__ = ("CookieJar", "DummyCookieJar") + + +CookieItem = Union[str, "Morsel[str]"] + + +class CookieJar(AbstractCookieJar): + """Implements cookie storage adhering to RFC 6265.""" + + DATE_TOKENS_RE = re.compile( + r"[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]*" + r"(?P[\x00-\x08\x0A-\x1F\d:a-zA-Z\x7F-\xFF]+)" + ) + + DATE_HMS_TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})") + + DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})") + + DATE_MONTH_RE = re.compile( + "(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|" "(aug)|(sep)|(oct)|(nov)|(dec)", + re.I, + ) + + DATE_YEAR_RE = re.compile(r"(\d{2,4})") + + # calendar.timegm() fails for timestamps after datetime.datetime.max + # Minus one as a loss of precision occurs when timestamp() is called. + MAX_TIME = ( + int(datetime.datetime.max.replace(tzinfo=datetime.timezone.utc).timestamp()) - 1 + ) + try: + calendar.timegm(time.gmtime(MAX_TIME)) + except (OSError, ValueError): + # Hit the maximum representable time on Windows + # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64 + # Throws ValueError on PyPy 3.8 and 3.9, OSError elsewhere + MAX_TIME = calendar.timegm((3000, 12, 31, 23, 59, 59, -1, -1, -1)) + except OverflowError: + # #4515: datetime.max may not be representable on 32-bit platforms + MAX_TIME = 2**31 - 1 + # Avoid minuses in the future, 3x faster + SUB_MAX_TIME = MAX_TIME - 1 + + def __init__( + self, + *, + unsafe: bool = False, + quote_cookie: bool = True, + treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + super().__init__(loop=loop) + self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict( + SimpleCookie + ) + self._host_only_cookies: Set[Tuple[str, str]] = set() + self._unsafe = unsafe + self._quote_cookie = quote_cookie + if treat_as_secure_origin is None: + treat_as_secure_origin = [] + elif isinstance(treat_as_secure_origin, URL): + treat_as_secure_origin = [treat_as_secure_origin.origin()] + elif isinstance(treat_as_secure_origin, str): + treat_as_secure_origin = [URL(treat_as_secure_origin).origin()] + else: + treat_as_secure_origin = [ + URL(url).origin() if isinstance(url, str) else url.origin() + for url in treat_as_secure_origin + ] + self._treat_as_secure_origin = treat_as_secure_origin + self._next_expiration: float = ceil(time.time()) + self._expirations: Dict[Tuple[str, str, str], float] = {} + + def save(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="wb") as f: + pickle.dump(self._cookies, f, pickle.HIGHEST_PROTOCOL) + + def load(self, file_path: PathLike) -> None: + file_path = pathlib.Path(file_path) + with file_path.open(mode="rb") as f: + self._cookies = pickle.load(f) + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + if predicate is None: + self._next_expiration = ceil(time.time()) + self._cookies.clear() + self._host_only_cookies.clear() + self._expirations.clear() + return + + to_del = [] + now = time.time() + for (domain, path), cookie in self._cookies.items(): + for name, morsel in cookie.items(): + key = (domain, path, name) + if ( + key in self._expirations and self._expirations[key] <= now + ) or predicate(morsel): + to_del.append(key) + + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + key = (domain, path, name) + if key in self._expirations: + del self._expirations[(domain, path, name)] + self._cookies[(domain, path)].pop(name, None) + + self._next_expiration = ( + min(*self._expirations.values(), self.SUB_MAX_TIME) + 1 + if self._expirations + else self.MAX_TIME + ) + + def clear_domain(self, domain: str) -> None: + self.clear(lambda x: self._is_domain_match(domain, x["domain"])) + + def __iter__(self) -> "Iterator[Morsel[str]]": + self._do_expiration() + for val in self._cookies.values(): + yield from val.values() + + def __len__(self) -> int: + return sum(1 for i in self) + + def _do_expiration(self) -> None: + self.clear(lambda x: False) + + def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: + self._next_expiration = min(self._next_expiration, when) + self._expirations[(domain, path, name)] = when + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + """Update cookies.""" + hostname = response_url.raw_host + + if not self._unsafe and is_ip_address(hostname): + # Don't accept cookies from IPs + return + + if isinstance(cookies, Mapping): + cookies = cookies.items() + + for name, cookie in cookies: + if not isinstance(cookie, Morsel): + tmp = SimpleCookie() + tmp[name] = cookie # type: ignore[assignment] + cookie = tmp[name] + + domain = cookie["domain"] + + # ignore domains with trailing dots + if domain.endswith("."): + domain = "" + del cookie["domain"] + + if not domain and hostname is not None: + # Set the cookie's domain to the response hostname + # and set its host-only-flag + self._host_only_cookies.add((hostname, name)) + domain = cookie["domain"] = hostname + + if domain.startswith("."): + # Remove leading dot + domain = domain[1:] + cookie["domain"] = domain + + if hostname and not self._is_domain_match(domain, hostname): + # Setting cookies for different domains is not allowed + continue + + path = cookie["path"] + if not path or not path.startswith("/"): + # Set the cookie's path to the response path + path = response_url.path + if not path.startswith("/"): + path = "/" + else: + # Cut everything from the last slash to the end + path = "/" + path[1 : path.rfind("/")] + cookie["path"] = path + + max_age = cookie["max-age"] + if max_age: + try: + delta_seconds = int(max_age) + max_age_expiration = min(time.time() + delta_seconds, self.MAX_TIME) + self._expire_cookie(max_age_expiration, domain, path, name) + except ValueError: + cookie["max-age"] = "" + + else: + expires = cookie["expires"] + if expires: + expire_time = self._parse_date(expires) + if expire_time: + self._expire_cookie(expire_time, domain, path, name) + else: + cookie["expires"] = "" + + self._cookies[(domain, path)][name] = cookie + + self._do_expiration() + + def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": + """Returns this jar's cookies filtered by their attributes.""" + filtered: Union[SimpleCookie, "BaseCookie[str]"] = ( + SimpleCookie() if self._quote_cookie else BaseCookie() + ) + if not self._cookies: + # Skip do_expiration() if there are no cookies. + return filtered + self._do_expiration() + if not self._cookies: + # Skip rest of function if no non-expired cookies. + return filtered + request_url = URL(request_url) + hostname = request_url.raw_host or "" + + is_not_secure = request_url.scheme not in ("https", "wss") + if is_not_secure and self._treat_as_secure_origin: + request_origin = URL() + with contextlib.suppress(ValueError): + request_origin = request_url.origin() + is_not_secure = request_origin not in self._treat_as_secure_origin + + # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 + for cookie in sorted(self, key=lambda c: len(c["path"])): + name = cookie.key + domain = cookie["domain"] + + # Send shared cookies + if not domain: + filtered[name] = cookie.value + continue + + if not self._unsafe and is_ip_address(hostname): + continue + + if (domain, name) in self._host_only_cookies: + if domain != hostname: + continue + elif not self._is_domain_match(domain, hostname): + continue + + if not self._is_path_match(request_url.path, cookie["path"]): + continue + + if is_not_secure and cookie["secure"]: + continue + + # It's critical we use the Morsel so the coded_value + # (based on cookie version) is preserved + mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) + mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) + filtered[name] = mrsl_val + + return filtered + + @staticmethod + def _is_domain_match(domain: str, hostname: str) -> bool: + """Implements domain matching adhering to RFC 6265.""" + if hostname == domain: + return True + + if not hostname.endswith(domain): + return False + + non_matching = hostname[: -len(domain)] + + if not non_matching.endswith("."): + return False + + return not is_ip_address(hostname) + + @staticmethod + def _is_path_match(req_path: str, cookie_path: str) -> bool: + """Implements path matching adhering to RFC 6265.""" + if not req_path.startswith("/"): + req_path = "/" + + if req_path == cookie_path: + return True + + if not req_path.startswith(cookie_path): + return False + + if cookie_path.endswith("/"): + return True + + non_matching = req_path[len(cookie_path) :] + + return non_matching.startswith("/") + + @classmethod + def _parse_date(cls, date_str: str) -> Optional[int]: + """Implements date string parsing adhering to RFC 6265.""" + if not date_str: + return None + + found_time = False + found_day = False + found_month = False + found_year = False + + hour = minute = second = 0 + day = 0 + month = 0 + year = 0 + + for token_match in cls.DATE_TOKENS_RE.finditer(date_str): + + token = token_match.group("token") + + if not found_time: + time_match = cls.DATE_HMS_TIME_RE.match(token) + if time_match: + found_time = True + hour, minute, second = (int(s) for s in time_match.groups()) + continue + + if not found_day: + day_match = cls.DATE_DAY_OF_MONTH_RE.match(token) + if day_match: + found_day = True + day = int(day_match.group()) + continue + + if not found_month: + month_match = cls.DATE_MONTH_RE.match(token) + if month_match: + found_month = True + assert month_match.lastindex is not None + month = month_match.lastindex + continue + + if not found_year: + year_match = cls.DATE_YEAR_RE.match(token) + if year_match: + found_year = True + year = int(year_match.group()) + + if 70 <= year <= 99: + year += 1900 + elif 0 <= year <= 69: + year += 2000 + + if False in (found_day, found_month, found_year, found_time): + return None + + if not 1 <= day <= 31: + return None + + if year < 1601 or hour > 23 or minute > 59 or second > 59: + return None + + return calendar.timegm((year, month, day, hour, minute, second, -1, -1, -1)) + + +class DummyCookieJar(AbstractCookieJar): + """Implements a dummy cookie storage. + + It can be used with the ClientSession when no cookie processing is needed. + + """ + + def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + super().__init__(loop=loop) + + def __iter__(self) -> "Iterator[Morsel[str]]": + while False: + yield None + + def __len__(self) -> int: + return 0 + + def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: + pass + + def clear_domain(self, domain: str) -> None: + pass + + def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: + pass + + def filter_cookies(self, request_url: URL) -> "BaseCookie[str]": + return SimpleCookie() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/formdata.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/formdata.py new file mode 100644 index 0000000..2b75b3d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/formdata.py @@ -0,0 +1,182 @@ +import io +import warnings +from typing import Any, Iterable, List, Optional +from urllib.parse import urlencode + +from multidict import MultiDict, MultiDictProxy + +from . import hdrs, multipart, payload +from .helpers import guess_filename +from .payload import Payload + +__all__ = ("FormData",) + + +class FormData: + """Helper class for form body generation. + + Supports multipart/form-data and application/x-www-form-urlencoded. + """ + + def __init__( + self, + fields: Iterable[Any] = (), + quote_fields: bool = True, + charset: Optional[str] = None, + ) -> None: + self._writer = multipart.MultipartWriter("form-data") + self._fields: List[Any] = [] + self._is_multipart = False + self._is_processed = False + self._quote_fields = quote_fields + self._charset = charset + + if isinstance(fields, dict): + fields = list(fields.items()) + elif not isinstance(fields, (list, tuple)): + fields = (fields,) + self.add_fields(*fields) + + @property + def is_multipart(self) -> bool: + return self._is_multipart + + def add_field( + self, + name: str, + value: Any, + *, + content_type: Optional[str] = None, + filename: Optional[str] = None, + content_transfer_encoding: Optional[str] = None, + ) -> None: + + if isinstance(value, io.IOBase): + self._is_multipart = True + elif isinstance(value, (bytes, bytearray, memoryview)): + msg = ( + "In v4, passing bytes will no longer create a file field. " + "Please explicitly use the filename parameter or pass a BytesIO object." + ) + if filename is None and content_transfer_encoding is None: + warnings.warn(msg, DeprecationWarning) + filename = name + + type_options: MultiDict[str] = MultiDict({"name": name}) + if filename is not None and not isinstance(filename, str): + raise TypeError( + "filename must be an instance of str. " "Got: %s" % filename + ) + if filename is None and isinstance(value, io.IOBase): + filename = guess_filename(value, name) + if filename is not None: + type_options["filename"] = filename + self._is_multipart = True + + headers = {} + if content_type is not None: + if not isinstance(content_type, str): + raise TypeError( + "content_type must be an instance of str. " "Got: %s" % content_type + ) + headers[hdrs.CONTENT_TYPE] = content_type + self._is_multipart = True + if content_transfer_encoding is not None: + if not isinstance(content_transfer_encoding, str): + raise TypeError( + "content_transfer_encoding must be an instance" + " of str. Got: %s" % content_transfer_encoding + ) + msg = ( + "content_transfer_encoding is deprecated. " + "To maintain compatibility with v4 please pass a BytesPayload." + ) + warnings.warn(msg, DeprecationWarning) + self._is_multipart = True + + self._fields.append((type_options, headers, value)) + + def add_fields(self, *fields: Any) -> None: + to_add = list(fields) + + while to_add: + rec = to_add.pop(0) + + if isinstance(rec, io.IOBase): + k = guess_filename(rec, "unknown") + self.add_field(k, rec) # type: ignore[arg-type] + + elif isinstance(rec, (MultiDictProxy, MultiDict)): + to_add.extend(rec.items()) + + elif isinstance(rec, (list, tuple)) and len(rec) == 2: + k, fp = rec + self.add_field(k, fp) # type: ignore[arg-type] + + else: + raise TypeError( + "Only io.IOBase, multidict and (name, file) " + "pairs allowed, use .add_field() for passing " + "more complex parameters, got {!r}".format(rec) + ) + + def _gen_form_urlencoded(self) -> payload.BytesPayload: + # form data (x-www-form-urlencoded) + data = [] + for type_options, _, value in self._fields: + data.append((type_options["name"], value)) + + charset = self._charset if self._charset is not None else "utf-8" + + if charset == "utf-8": + content_type = "application/x-www-form-urlencoded" + else: + content_type = "application/x-www-form-urlencoded; " "charset=%s" % charset + + return payload.BytesPayload( + urlencode(data, doseq=True, encoding=charset).encode(), + content_type=content_type, + ) + + def _gen_form_data(self) -> multipart.MultipartWriter: + """Encode a list of fields using the multipart/form-data MIME format""" + if self._is_processed: + raise RuntimeError("Form data has been processed already") + for dispparams, headers, value in self._fields: + try: + if hdrs.CONTENT_TYPE in headers: + part = payload.get_payload( + value, + content_type=headers[hdrs.CONTENT_TYPE], + headers=headers, + encoding=self._charset, + ) + else: + part = payload.get_payload( + value, headers=headers, encoding=self._charset + ) + except Exception as exc: + raise TypeError( + "Can not serialize value type: %r\n " + "headers: %r\n value: %r" % (type(value), headers, value) + ) from exc + + if dispparams: + part.set_content_disposition( + "form-data", quote_fields=self._quote_fields, **dispparams + ) + # FIXME cgi.FieldStorage doesn't likes body parts with + # Content-Length which were sent via chunked transfer encoding + assert part.headers is not None + part.headers.popall(hdrs.CONTENT_LENGTH, None) + + self._writer.append_payload(part) + + self._is_processed = True + return self._writer + + def __call__(self) -> Payload: + if self._is_multipart: + return self._gen_form_data() + else: + return self._gen_form_urlencoded() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/hdrs.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/hdrs.py new file mode 100644 index 0000000..2f1f5e0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/hdrs.py @@ -0,0 +1,108 @@ +"""HTTP Headers constants.""" + +# After changing the file content call ./tools/gen.py +# to regenerate the headers parser +from typing import Final, Set + +from multidict import istr + +METH_ANY: Final[str] = "*" +METH_CONNECT: Final[str] = "CONNECT" +METH_HEAD: Final[str] = "HEAD" +METH_GET: Final[str] = "GET" +METH_DELETE: Final[str] = "DELETE" +METH_OPTIONS: Final[str] = "OPTIONS" +METH_PATCH: Final[str] = "PATCH" +METH_POST: Final[str] = "POST" +METH_PUT: Final[str] = "PUT" +METH_TRACE: Final[str] = "TRACE" + +METH_ALL: Final[Set[str]] = { + METH_CONNECT, + METH_HEAD, + METH_GET, + METH_DELETE, + METH_OPTIONS, + METH_PATCH, + METH_POST, + METH_PUT, + METH_TRACE, +} + +ACCEPT: Final[istr] = istr("Accept") +ACCEPT_CHARSET: Final[istr] = istr("Accept-Charset") +ACCEPT_ENCODING: Final[istr] = istr("Accept-Encoding") +ACCEPT_LANGUAGE: Final[istr] = istr("Accept-Language") +ACCEPT_RANGES: Final[istr] = istr("Accept-Ranges") +ACCESS_CONTROL_MAX_AGE: Final[istr] = istr("Access-Control-Max-Age") +ACCESS_CONTROL_ALLOW_CREDENTIALS: Final[istr] = istr("Access-Control-Allow-Credentials") +ACCESS_CONTROL_ALLOW_HEADERS: Final[istr] = istr("Access-Control-Allow-Headers") +ACCESS_CONTROL_ALLOW_METHODS: Final[istr] = istr("Access-Control-Allow-Methods") +ACCESS_CONTROL_ALLOW_ORIGIN: Final[istr] = istr("Access-Control-Allow-Origin") +ACCESS_CONTROL_EXPOSE_HEADERS: Final[istr] = istr("Access-Control-Expose-Headers") +ACCESS_CONTROL_REQUEST_HEADERS: Final[istr] = istr("Access-Control-Request-Headers") +ACCESS_CONTROL_REQUEST_METHOD: Final[istr] = istr("Access-Control-Request-Method") +AGE: Final[istr] = istr("Age") +ALLOW: Final[istr] = istr("Allow") +AUTHORIZATION: Final[istr] = istr("Authorization") +CACHE_CONTROL: Final[istr] = istr("Cache-Control") +CONNECTION: Final[istr] = istr("Connection") +CONTENT_DISPOSITION: Final[istr] = istr("Content-Disposition") +CONTENT_ENCODING: Final[istr] = istr("Content-Encoding") +CONTENT_LANGUAGE: Final[istr] = istr("Content-Language") +CONTENT_LENGTH: Final[istr] = istr("Content-Length") +CONTENT_LOCATION: Final[istr] = istr("Content-Location") +CONTENT_MD5: Final[istr] = istr("Content-MD5") +CONTENT_RANGE: Final[istr] = istr("Content-Range") +CONTENT_TRANSFER_ENCODING: Final[istr] = istr("Content-Transfer-Encoding") +CONTENT_TYPE: Final[istr] = istr("Content-Type") +COOKIE: Final[istr] = istr("Cookie") +DATE: Final[istr] = istr("Date") +DESTINATION: Final[istr] = istr("Destination") +DIGEST: Final[istr] = istr("Digest") +ETAG: Final[istr] = istr("Etag") +EXPECT: Final[istr] = istr("Expect") +EXPIRES: Final[istr] = istr("Expires") +FORWARDED: Final[istr] = istr("Forwarded") +FROM: Final[istr] = istr("From") +HOST: Final[istr] = istr("Host") +IF_MATCH: Final[istr] = istr("If-Match") +IF_MODIFIED_SINCE: Final[istr] = istr("If-Modified-Since") +IF_NONE_MATCH: Final[istr] = istr("If-None-Match") +IF_RANGE: Final[istr] = istr("If-Range") +IF_UNMODIFIED_SINCE: Final[istr] = istr("If-Unmodified-Since") +KEEP_ALIVE: Final[istr] = istr("Keep-Alive") +LAST_EVENT_ID: Final[istr] = istr("Last-Event-ID") +LAST_MODIFIED: Final[istr] = istr("Last-Modified") +LINK: Final[istr] = istr("Link") +LOCATION: Final[istr] = istr("Location") +MAX_FORWARDS: Final[istr] = istr("Max-Forwards") +ORIGIN: Final[istr] = istr("Origin") +PRAGMA: Final[istr] = istr("Pragma") +PROXY_AUTHENTICATE: Final[istr] = istr("Proxy-Authenticate") +PROXY_AUTHORIZATION: Final[istr] = istr("Proxy-Authorization") +RANGE: Final[istr] = istr("Range") +REFERER: Final[istr] = istr("Referer") +RETRY_AFTER: Final[istr] = istr("Retry-After") +SEC_WEBSOCKET_ACCEPT: Final[istr] = istr("Sec-WebSocket-Accept") +SEC_WEBSOCKET_VERSION: Final[istr] = istr("Sec-WebSocket-Version") +SEC_WEBSOCKET_PROTOCOL: Final[istr] = istr("Sec-WebSocket-Protocol") +SEC_WEBSOCKET_EXTENSIONS: Final[istr] = istr("Sec-WebSocket-Extensions") +SEC_WEBSOCKET_KEY: Final[istr] = istr("Sec-WebSocket-Key") +SEC_WEBSOCKET_KEY1: Final[istr] = istr("Sec-WebSocket-Key1") +SERVER: Final[istr] = istr("Server") +SET_COOKIE: Final[istr] = istr("Set-Cookie") +TE: Final[istr] = istr("TE") +TRAILER: Final[istr] = istr("Trailer") +TRANSFER_ENCODING: Final[istr] = istr("Transfer-Encoding") +UPGRADE: Final[istr] = istr("Upgrade") +URI: Final[istr] = istr("URI") +USER_AGENT: Final[istr] = istr("User-Agent") +VARY: Final[istr] = istr("Vary") +VIA: Final[istr] = istr("Via") +WANT_DIGEST: Final[istr] = istr("Want-Digest") +WARNING: Final[istr] = istr("Warning") +WWW_AUTHENTICATE: Final[istr] = istr("WWW-Authenticate") +X_FORWARDED_FOR: Final[istr] = istr("X-Forwarded-For") +X_FORWARDED_HOST: Final[istr] = istr("X-Forwarded-Host") +X_FORWARDED_PROTO: Final[istr] = istr("X-Forwarded-Proto") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/helpers.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/helpers.py new file mode 100644 index 0000000..284033b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/helpers.py @@ -0,0 +1,1029 @@ +"""Various helper functions""" + +import asyncio +import base64 +import binascii +import contextlib +import datetime +import enum +import functools +import inspect +import netrc +import os +import platform +import re +import sys +import time +import warnings +import weakref +from collections import namedtuple +from contextlib import suppress +from email.parser import HeaderParser +from email.utils import parsedate +from math import ceil +from pathlib import Path +from types import TracebackType +from typing import ( + Any, + Callable, + ContextManager, + Dict, + Generator, + Generic, + Iterable, + Iterator, + List, + Mapping, + Optional, + Pattern, + Protocol, + Tuple, + Type, + TypeVar, + Union, + get_args, + overload, +) +from urllib.parse import quote +from urllib.request import getproxies, proxy_bypass + +import attr +from multidict import MultiDict, MultiDictProxy, MultiMapping +from yarl import URL + +from . import hdrs +from .log import client_logger, internal_logger + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + +__all__ = ("BasicAuth", "ChainMapProxy", "ETag") + +IS_MACOS = platform.system() == "Darwin" +IS_WINDOWS = platform.system() == "Windows" + +PY_310 = sys.version_info >= (3, 10) +PY_311 = sys.version_info >= (3, 11) + + +_T = TypeVar("_T") +_S = TypeVar("_S") + +_SENTINEL = enum.Enum("_SENTINEL", "sentinel") +sentinel = _SENTINEL.sentinel + +NO_EXTENSIONS = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS")) + +DEBUG = sys.flags.dev_mode or ( + not sys.flags.ignore_environment and bool(os.environ.get("PYTHONASYNCIODEBUG")) +) + + +CHAR = {chr(i) for i in range(0, 128)} +CTL = {chr(i) for i in range(0, 32)} | { + chr(127), +} +SEPARATORS = { + "(", + ")", + "<", + ">", + "@", + ",", + ";", + ":", + "\\", + '"', + "/", + "[", + "]", + "?", + "=", + "{", + "}", + " ", + chr(9), +} +TOKEN = CHAR ^ CTL ^ SEPARATORS + + +class noop: + def __await__(self) -> Generator[None, None, None]: + yield + + +class BasicAuth(namedtuple("BasicAuth", ["login", "password", "encoding"])): + """Http basic authentication helper.""" + + def __new__( + cls, login: str, password: str = "", encoding: str = "latin1" + ) -> "BasicAuth": + if login is None: + raise ValueError("None is not allowed as login value") + + if password is None: + raise ValueError("None is not allowed as password value") + + if ":" in login: + raise ValueError('A ":" is not allowed in login (RFC 1945#section-11.1)') + + return super().__new__(cls, login, password, encoding) + + @classmethod + def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth": + """Create a BasicAuth object from an Authorization HTTP header.""" + try: + auth_type, encoded_credentials = auth_header.split(" ", 1) + except ValueError: + raise ValueError("Could not parse authorization header.") + + if auth_type.lower() != "basic": + raise ValueError("Unknown authorization method %s" % auth_type) + + try: + decoded = base64.b64decode( + encoded_credentials.encode("ascii"), validate=True + ).decode(encoding) + except binascii.Error: + raise ValueError("Invalid base64 encoding.") + + try: + # RFC 2617 HTTP Authentication + # https://www.ietf.org/rfc/rfc2617.txt + # the colon must be present, but the username and password may be + # otherwise blank. + username, password = decoded.split(":", 1) + except ValueError: + raise ValueError("Invalid credentials.") + + return cls(username, password, encoding=encoding) + + @classmethod + def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth"]: + """Create BasicAuth from url.""" + if not isinstance(url, URL): + raise TypeError("url should be yarl.URL instance") + if url.user is None: + return None + return cls(url.user, url.password or "", encoding=encoding) + + def encode(self) -> str: + """Encode credentials.""" + creds = (f"{self.login}:{self.password}").encode(self.encoding) + return "Basic %s" % base64.b64encode(creds).decode(self.encoding) + + +def strip_auth_from_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + auth = BasicAuth.from_url(url) + if auth is None: + return url, None + else: + return url.with_user(None), auth + + +def netrc_from_env() -> Optional[netrc.netrc]: + """Load netrc from file. + + Attempt to load it from the path specified by the env-var + NETRC or in the default location in the user's home directory. + + Returns None if it couldn't be found or fails to parse. + """ + netrc_env = os.environ.get("NETRC") + + if netrc_env is not None: + netrc_path = Path(netrc_env) + else: + try: + home_dir = Path.home() + except RuntimeError as e: # pragma: no cover + # if pathlib can't resolve home, it may raise a RuntimeError + client_logger.debug( + "Could not resolve home directory when " + "trying to look for .netrc file: %s", + e, + ) + return None + + netrc_path = home_dir / ("_netrc" if IS_WINDOWS else ".netrc") + + try: + return netrc.netrc(str(netrc_path)) + except netrc.NetrcParseError as e: + client_logger.warning("Could not parse .netrc file: %s", e) + except OSError as e: + netrc_exists = False + with contextlib.suppress(OSError): + netrc_exists = netrc_path.is_file() + # we couldn't read the file (doesn't exist, permissions, etc.) + if netrc_env or netrc_exists: + # only warn if the environment wanted us to load it, + # or it appears like the default file does actually exist + client_logger.warning("Could not read .netrc file: %s", e) + + return None + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ProxyInfo: + proxy: URL + proxy_auth: Optional[BasicAuth] + + +def basicauth_from_netrc(netrc_obj: Optional[netrc.netrc], host: str) -> BasicAuth: + """ + Return :py:class:`~aiohttp.BasicAuth` credentials for ``host`` from ``netrc_obj``. + + :raises LookupError: if ``netrc_obj`` is :py:data:`None` or if no + entry is found for the ``host``. + """ + if netrc_obj is None: + raise LookupError("No .netrc file found") + auth_from_netrc = netrc_obj.authenticators(host) + + if auth_from_netrc is None: + raise LookupError(f"No entry for {host!s} found in the `.netrc` file.") + login, account, password = auth_from_netrc + + # TODO(PY311): username = login or account + # Up to python 3.10, account could be None if not specified, + # and login will be empty string if not specified. From 3.11, + # login and account will be empty string if not specified. + username = login if (login or account is None) else account + + # TODO(PY311): Remove this, as password will be empty string + # if not specified + if password is None: + password = "" + + return BasicAuth(username, password) + + +def proxies_from_env() -> Dict[str, ProxyInfo]: + proxy_urls = { + k: URL(v) + for k, v in getproxies().items() + if k in ("http", "https", "ws", "wss") + } + netrc_obj = netrc_from_env() + stripped = {k: strip_auth_from_url(v) for k, v in proxy_urls.items()} + ret = {} + for proto, val in stripped.items(): + proxy, auth = val + if proxy.scheme in ("https", "wss"): + client_logger.warning( + "%s proxies %s are not supported, ignoring", proxy.scheme.upper(), proxy + ) + continue + if netrc_obj and auth is None: + if proxy.host is not None: + try: + auth = basicauth_from_netrc(netrc_obj, proxy.host) + except LookupError: + auth = None + ret[proto] = ProxyInfo(proxy, auth) + return ret + + +def current_task( + loop: Optional[asyncio.AbstractEventLoop] = None, +) -> "Optional[asyncio.Task[Any]]": + return asyncio.current_task(loop=loop) + + +def get_running_loop( + loop: Optional[asyncio.AbstractEventLoop] = None, +) -> asyncio.AbstractEventLoop: + if loop is None: + loop = asyncio.get_event_loop() + if not loop.is_running(): + warnings.warn( + "The object should be created within an async function", + DeprecationWarning, + stacklevel=3, + ) + if loop.get_debug(): + internal_logger.warning( + "The object should be created within an async function", stack_info=True + ) + return loop + + +def isasyncgenfunction(obj: Any) -> bool: + func = getattr(inspect, "isasyncgenfunction", None) + if func is not None: + return func(obj) # type: ignore[no-any-return] + else: + return False + + +def get_env_proxy_for_url(url: URL) -> Tuple[URL, Optional[BasicAuth]]: + """Get a permitted proxy for the given URL from the env.""" + if url.host is not None and proxy_bypass(url.host): + raise LookupError(f"Proxying is disallowed for `{url.host!r}`") + + proxies_in_env = proxies_from_env() + try: + proxy_info = proxies_in_env[url.scheme] + except KeyError: + raise LookupError(f"No proxies found for `{url!s}` in the env") + else: + return proxy_info.proxy, proxy_info.proxy_auth + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class MimeType: + type: str + subtype: str + suffix: str + parameters: "MultiDictProxy[str]" + + +@functools.lru_cache(maxsize=56) +def parse_mimetype(mimetype: str) -> MimeType: + """Parses a MIME type into its components. + + mimetype is a MIME type string. + + Returns a MimeType object. + + Example: + + >>> parse_mimetype('text/html; charset=utf-8') + MimeType(type='text', subtype='html', suffix='', + parameters={'charset': 'utf-8'}) + + """ + if not mimetype: + return MimeType( + type="", subtype="", suffix="", parameters=MultiDictProxy(MultiDict()) + ) + + parts = mimetype.split(";") + params: MultiDict[str] = MultiDict() + for item in parts[1:]: + if not item: + continue + key, _, value = item.partition("=") + params.add(key.lower().strip(), value.strip(' "')) + + fulltype = parts[0].strip().lower() + if fulltype == "*": + fulltype = "*/*" + + mtype, _, stype = fulltype.partition("/") + stype, _, suffix = stype.partition("+") + + return MimeType( + type=mtype, subtype=stype, suffix=suffix, parameters=MultiDictProxy(params) + ) + + +def guess_filename(obj: Any, default: Optional[str] = None) -> Optional[str]: + name = getattr(obj, "name", None) + if name and isinstance(name, str) and name[0] != "<" and name[-1] != ">": + return Path(name).name + return default + + +not_qtext_re = re.compile(r"[^\041\043-\133\135-\176]") +QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {"\t"} + + +def quoted_string(content: str) -> str: + """Return 7-bit content as quoted-string. + + Format content into a quoted-string as defined in RFC5322 for + Internet Message Format. Notice that this is not the 8-bit HTTP + format, but the 7-bit email format. Content must be in usascii or + a ValueError is raised. + """ + if not (QCONTENT > set(content)): + raise ValueError(f"bad content for quoted-string {content!r}") + return not_qtext_re.sub(lambda x: "\\" + x.group(0), content) + + +def content_disposition_header( + disptype: str, quote_fields: bool = True, _charset: str = "utf-8", **params: str +) -> str: + """Sets ``Content-Disposition`` header for MIME. + + This is the MIME payload Content-Disposition header from RFC 2183 + and RFC 7579 section 4.2, not the HTTP Content-Disposition from + RFC 6266. + + disptype is a disposition type: inline, attachment, form-data. + Should be valid extension token (see RFC 2183) + + quote_fields performs value quoting to 7-bit MIME headers + according to RFC 7578. Set to quote_fields to False if recipient + can take 8-bit file names and field values. + + _charset specifies the charset to use when quote_fields is True. + + params is a dict with disposition params. + """ + if not disptype or not (TOKEN > set(disptype)): + raise ValueError("bad content disposition type {!r}" "".format(disptype)) + + value = disptype + if params: + lparams = [] + for key, val in params.items(): + if not key or not (TOKEN > set(key)): + raise ValueError( + "bad content disposition parameter" " {!r}={!r}".format(key, val) + ) + if quote_fields: + if key.lower() == "filename": + qval = quote(val, "", encoding=_charset) + lparams.append((key, '"%s"' % qval)) + else: + try: + qval = quoted_string(val) + except ValueError: + qval = "".join( + (_charset, "''", quote(val, "", encoding=_charset)) + ) + lparams.append((key + "*", qval)) + else: + lparams.append((key, '"%s"' % qval)) + else: + qval = val.replace("\\", "\\\\").replace('"', '\\"') + lparams.append((key, '"%s"' % qval)) + sparams = "; ".join("=".join(pair) for pair in lparams) + value = "; ".join((value, sparams)) + return value + + +class _TSelf(Protocol, Generic[_T]): + _cache: Dict[str, _T] + + +class reify(Generic[_T]): + """Use as a class method decorator. + + It operates almost exactly like + the Python `@property` decorator, but it puts the result of the + method it decorates into the instance dict after the first call, + effectively replacing the function it decorates with an instance + variable. It is, in Python parlance, a data descriptor. + """ + + def __init__(self, wrapped: Callable[..., _T]) -> None: + self.wrapped = wrapped + self.__doc__ = wrapped.__doc__ + self.name = wrapped.__name__ + + def __get__(self, inst: _TSelf[_T], owner: Optional[Type[Any]] = None) -> _T: + try: + try: + return inst._cache[self.name] + except KeyError: + val = self.wrapped(inst) + inst._cache[self.name] = val + return val + except AttributeError: + if inst is None: + return self + raise + + def __set__(self, inst: _TSelf[_T], value: _T) -> None: + raise AttributeError("reified property is read-only") + + +reify_py = reify + +try: + from ._helpers import reify as reify_c + + if not NO_EXTENSIONS: + reify = reify_c # type: ignore[misc,assignment] +except ImportError: + pass + +_ipv4_pattern = ( + r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}" + r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" +) +_ipv6_pattern = ( + r"^(?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}" + r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}$)(([0-9A-F]{1,4}:){0,5}|:)" + r"((:[0-9A-F]{1,4}){1,5}:|:)|::(?:[A-F0-9]{1,4}:){5})" + r"(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}" + r"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])|(?:[A-F0-9]{1,4}:){7}" + r"[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4}$)" + r"(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:)|(?:[A-F0-9]{1,4}:){7}" + r":|:(:[A-F0-9]{1,4}){7})$" +) +_ipv4_regex = re.compile(_ipv4_pattern) +_ipv6_regex = re.compile(_ipv6_pattern, flags=re.IGNORECASE) +_ipv4_regexb = re.compile(_ipv4_pattern.encode("ascii")) +_ipv6_regexb = re.compile(_ipv6_pattern.encode("ascii"), flags=re.IGNORECASE) + + +def _is_ip_address( + regex: Pattern[str], regexb: Pattern[bytes], host: Optional[Union[str, bytes]] +) -> bool: + if host is None: + return False + if isinstance(host, str): + return bool(regex.match(host)) + elif isinstance(host, (bytes, bytearray, memoryview)): + return bool(regexb.match(host)) + else: + raise TypeError(f"{host} [{type(host)}] is not a str or bytes") + + +is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb) +is_ipv6_address = functools.partial(_is_ip_address, _ipv6_regex, _ipv6_regexb) + + +def is_ip_address(host: Optional[Union[str, bytes, bytearray, memoryview]]) -> bool: + return is_ipv4_address(host) or is_ipv6_address(host) + + +_cached_current_datetime: Optional[int] = None +_cached_formatted_datetime = "" + + +def rfc822_formatted_time() -> str: + global _cached_current_datetime + global _cached_formatted_datetime + + now = int(time.time()) + if now != _cached_current_datetime: + # Weekday and month names for HTTP date/time formatting; + # always English! + # Tuples are constants stored in codeobject! + _weekdayname = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + _monthname = ( + "", # Dummy so we can use 1-based month numbers + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ) + + year, month, day, hh, mm, ss, wd, *tail = time.gmtime(now) + _cached_formatted_datetime = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + _weekdayname[wd], + day, + _monthname[month], + year, + hh, + mm, + ss, + ) + _cached_current_datetime = now + return _cached_formatted_datetime + + +def _weakref_handle(info: "Tuple[weakref.ref[object], str]") -> None: + ref, name = info + ob = ref() + if ob is not None: + with suppress(Exception): + getattr(ob, name)() + + +def weakref_handle( + ob: object, + name: str, + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is not None and timeout > 0: + when = loop.time() + timeout + if timeout >= timeout_ceil_threshold: + when = ceil(when) + + return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name)) + return None + + +def call_later( + cb: Callable[[], Any], + timeout: float, + loop: asyncio.AbstractEventLoop, + timeout_ceil_threshold: float = 5, +) -> Optional[asyncio.TimerHandle]: + if timeout is not None and timeout > 0: + when = loop.time() + timeout + if timeout > timeout_ceil_threshold: + when = ceil(when) + return loop.call_at(when, cb) + return None + + +class TimeoutHandle: + """Timeout handle""" + + def __init__( + self, + loop: asyncio.AbstractEventLoop, + timeout: Optional[float], + ceil_threshold: float = 5, + ) -> None: + self._timeout = timeout + self._loop = loop + self._ceil_threshold = ceil_threshold + self._callbacks: List[ + Tuple[Callable[..., None], Tuple[Any, ...], Dict[str, Any]] + ] = [] + + def register( + self, callback: Callable[..., None], *args: Any, **kwargs: Any + ) -> None: + self._callbacks.append((callback, args, kwargs)) + + def close(self) -> None: + self._callbacks.clear() + + def start(self) -> Optional[asyncio.Handle]: + timeout = self._timeout + if timeout is not None and timeout > 0: + when = self._loop.time() + timeout + if timeout >= self._ceil_threshold: + when = ceil(when) + return self._loop.call_at(when, self.__call__) + else: + return None + + def timer(self) -> "BaseTimerContext": + if self._timeout is not None and self._timeout > 0: + timer = TimerContext(self._loop) + self.register(timer.timeout) + return timer + else: + return TimerNoop() + + def __call__(self) -> None: + for cb, args, kwargs in self._callbacks: + with suppress(Exception): + cb(*args, **kwargs) + + self._callbacks.clear() + + +class BaseTimerContext(ContextManager["BaseTimerContext"]): + def assert_timeout(self) -> None: + """Raise TimeoutError if timeout has been exceeded.""" + + +class TimerNoop(BaseTimerContext): + def __enter__(self) -> BaseTimerContext: + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + return + + +class TimerContext(BaseTimerContext): + """Low resolution timeout context manager""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._tasks: List[asyncio.Task[Any]] = [] + self._cancelled = False + + def assert_timeout(self) -> None: + """Raise TimeoutError if timer has already been cancelled.""" + if self._cancelled: + raise asyncio.TimeoutError from None + + def __enter__(self) -> BaseTimerContext: + task = current_task(loop=self._loop) + + if task is None: + raise RuntimeError( + "Timeout context manager should be used " "inside a task" + ) + + if self._cancelled: + raise asyncio.TimeoutError from None + + self._tasks.append(task) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + if self._tasks: + self._tasks.pop() + + if exc_type is asyncio.CancelledError and self._cancelled: + raise asyncio.TimeoutError from None + return None + + def timeout(self) -> None: + if not self._cancelled: + for task in set(self._tasks): + task.cancel() + + self._cancelled = True + + +def ceil_timeout( + delay: Optional[float], ceil_threshold: float = 5 +) -> async_timeout.Timeout: + if delay is None or delay <= 0: + return async_timeout.timeout(None) + + loop = get_running_loop() + now = loop.time() + when = now + delay + if delay > ceil_threshold: + when = ceil(when) + return async_timeout.timeout_at(when) + + +class HeadersMixin: + ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"]) + + _headers: MultiMapping[str] + + _content_type: Optional[str] = None + _content_dict: Optional[Dict[str, str]] = None + _stored_content_type: Union[str, None, _SENTINEL] = sentinel + + def _parse_content_type(self, raw: Optional[str]) -> None: + self._stored_content_type = raw + if raw is None: + # default value according to RFC 2616 + self._content_type = "application/octet-stream" + self._content_dict = {} + else: + msg = HeaderParser().parsestr("Content-Type: " + raw) + self._content_type = msg.get_content_type() + params = msg.get_params(()) + self._content_dict = dict(params[1:]) # First element is content type again + + @property + def content_type(self) -> str: + """The value of content part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + return self._content_type # type: ignore[return-value] + + @property + def charset(self) -> Optional[str]: + """The value of charset part for Content-Type HTTP header.""" + raw = self._headers.get(hdrs.CONTENT_TYPE) + if self._stored_content_type != raw: + self._parse_content_type(raw) + return self._content_dict.get("charset") # type: ignore[union-attr] + + @property + def content_length(self) -> Optional[int]: + """The value of Content-Length HTTP header.""" + content_length = self._headers.get(hdrs.CONTENT_LENGTH) + + if content_length is not None: + return int(content_length) + else: + return None + + +def set_result(fut: "asyncio.Future[_T]", result: _T) -> None: + if not fut.done(): + fut.set_result(result) + + +_EXC_SENTINEL = BaseException() + + +class ErrorableProtocol(Protocol): + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = ..., + ) -> None: + ... # pragma: no cover + + +def set_exception( + fut: "asyncio.Future[_T] | ErrorableProtocol", + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, +) -> None: + """Set future exception. + + If the future is marked as complete, this function is a no-op. + + :param exc_cause: An exception that is a direct cause of ``exc``. + Only set if provided. + """ + if asyncio.isfuture(fut) and fut.done(): + return + + exc_is_sentinel = exc_cause is _EXC_SENTINEL + exc_causes_itself = exc is exc_cause + if not exc_is_sentinel and not exc_causes_itself: + exc.__cause__ = exc_cause + + fut.set_exception(exc) + + +@functools.total_ordering +class AppKey(Generic[_T]): + """Keys for static typing support in Application.""" + + __slots__ = ("_name", "_t", "__orig_class__") + + # This may be set by Python when instantiating with a generic type. We need to + # support this, in order to support types that are not concrete classes, + # like Iterable, which can't be passed as the second parameter to __init__. + __orig_class__: Type[object] + + def __init__(self, name: str, t: Optional[Type[_T]] = None): + # Prefix with module name to help deduplicate key names. + frame = inspect.currentframe() + while frame: + if frame.f_code.co_name == "": + module: str = frame.f_globals["__name__"] + break + frame = frame.f_back + + self._name = module + "." + name + self._t = t + + def __lt__(self, other: object) -> bool: + if isinstance(other, AppKey): + return self._name < other._name + return True # Order AppKey above other types. + + def __repr__(self) -> str: + t = self._t + if t is None: + with suppress(AttributeError): + # Set to type arg. + t = get_args(self.__orig_class__)[0] + + if t is None: + t_repr = "<>" + elif isinstance(t, type): + if t.__module__ == "builtins": + t_repr = t.__qualname__ + else: + t_repr = f"{t.__module__}.{t.__qualname__}" + else: + t_repr = repr(t) + return f"" + + +class ChainMapProxy(Mapping[Union[str, AppKey[Any]], Any]): + __slots__ = ("_maps",) + + def __init__(self, maps: Iterable[Mapping[Union[str, AppKey[Any]], Any]]) -> None: + self._maps = tuple(maps) + + def __init_subclass__(cls) -> None: + raise TypeError( + "Inheritance class {} from ChainMapProxy " + "is forbidden".format(cls.__name__) + ) + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: + ... + + @overload + def __getitem__(self, key: str) -> Any: + ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + for mapping in self._maps: + try: + return mapping[key] + except KeyError: + pass + raise KeyError(key) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: _S) -> Union[_T, _S]: + ... + + @overload + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: + ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: + ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + try: + return self[key] + except KeyError: + return default + + def __len__(self) -> int: + # reuses stored hash values if possible + return len(set().union(*self._maps)) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + d: Dict[Union[str, AppKey[Any]], Any] = {} + for mapping in reversed(self._maps): + # reuses stored hash values if possible + d.update(mapping) + return iter(d) + + def __contains__(self, key: object) -> bool: + return any(key in m for m in self._maps) + + def __bool__(self) -> bool: + return any(self._maps) + + def __repr__(self) -> str: + content = ", ".join(map(repr, self._maps)) + return f"ChainMapProxy({content})" + + +# https://tools.ietf.org/html/rfc7232#section-2.3 +_ETAGC = r"[!\x23-\x7E\x80-\xff]+" +_ETAGC_RE = re.compile(_ETAGC) +_QUOTED_ETAG = rf'(W/)?"({_ETAGC})"' +QUOTED_ETAG_RE = re.compile(_QUOTED_ETAG) +LIST_QUOTED_ETAG_RE = re.compile(rf"({_QUOTED_ETAG})(?:\s*,\s*|$)|(.)") + +ETAG_ANY = "*" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class ETag: + value: str + is_weak: bool = False + + +def validate_etag_value(value: str) -> None: + if value != ETAG_ANY and not _ETAGC_RE.fullmatch(value): + raise ValueError( + f"Value {value!r} is not a valid etag. Maybe it contains '\"'?" + ) + + +def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]: + """Process a date string, return a datetime object""" + if date_str is not None: + timetuple = parsedate(date_str) + if timetuple is not None: + with suppress(ValueError): + return datetime.datetime(*timetuple[:6], tzinfo=datetime.timezone.utc) + return None + + +def must_be_empty_body(method: str, code: int) -> bool: + """Check if a request must return an empty body.""" + return ( + status_code_must_be_empty_body(code) + or method_must_be_empty_body(method) + or (200 <= code < 300 and method.upper() == hdrs.METH_CONNECT) + ) + + +def method_must_be_empty_body(method: str) -> bool: + """Check if a method must return an empty body.""" + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.2 + return method.upper() == hdrs.METH_HEAD + + +def status_code_must_be_empty_body(code: int) -> bool: + """Check if a status code must return an empty body.""" + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.3-2.1 + return code in {204, 304} or 100 <= code < 200 + + +def should_remove_content_length(method: str, code: int) -> bool: + """Check if a Content-Length header should be removed. + + This should always be a subset of must_be_empty_body + """ + # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-8 + # https://www.rfc-editor.org/rfc/rfc9110.html#section-15.4.5-4 + return ( + code in {204, 304} + or 100 <= code < 200 + or (200 <= code < 300 and method.upper() == hdrs.METH_CONNECT) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/http.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http.py new file mode 100644 index 0000000..a1feae2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http.py @@ -0,0 +1,72 @@ +import sys +from http import HTTPStatus +from typing import Mapping, Tuple + +from . import __version__ +from .http_exceptions import HttpProcessingError as HttpProcessingError +from .http_parser import ( + HeadersParser as HeadersParser, + HttpParser as HttpParser, + HttpRequestParser as HttpRequestParser, + HttpResponseParser as HttpResponseParser, + RawRequestMessage as RawRequestMessage, + RawResponseMessage as RawResponseMessage, +) +from .http_websocket import ( + WS_CLOSED_MESSAGE as WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE as WS_CLOSING_MESSAGE, + WS_KEY as WS_KEY, + WebSocketError as WebSocketError, + WebSocketReader as WebSocketReader, + WebSocketWriter as WebSocketWriter, + WSCloseCode as WSCloseCode, + WSMessage as WSMessage, + WSMsgType as WSMsgType, + ws_ext_gen as ws_ext_gen, + ws_ext_parse as ws_ext_parse, +) +from .http_writer import ( + HttpVersion as HttpVersion, + HttpVersion10 as HttpVersion10, + HttpVersion11 as HttpVersion11, + StreamWriter as StreamWriter, +) + +__all__ = ( + "HttpProcessingError", + "RESPONSES", + "SERVER_SOFTWARE", + # .http_writer + "StreamWriter", + "HttpVersion", + "HttpVersion10", + "HttpVersion11", + # .http_parser + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", + # .http_websocket + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "ws_ext_gen", + "ws_ext_parse", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", +) + + +SERVER_SOFTWARE: str = "Python/{0[0]}.{0[1]} aiohttp/{1}".format( + sys.version_info, __version__ +) + +RESPONSES: Mapping[int, Tuple[str, str]] = { + v: (v.phrase, v.description) for v in HTTPStatus.__members__.values() +} diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_exceptions.py new file mode 100644 index 0000000..72eac3a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_exceptions.py @@ -0,0 +1,106 @@ +"""Low-level http related exceptions.""" + + +from textwrap import indent +from typing import Optional, Union + +from .typedefs import _CIMultiDict + +__all__ = ("HttpProcessingError",) + + +class HttpProcessingError(Exception): + """HTTP error. + + Shortcut for raising HTTP errors with custom code, message and headers. + + code: HTTP Error code. + message: (optional) Error message. + headers: (optional) Headers to be sent in response, a list of pairs + """ + + code = 0 + message = "" + headers = None + + def __init__( + self, + *, + code: Optional[int] = None, + message: str = "", + headers: Optional[_CIMultiDict] = None, + ) -> None: + if code is not None: + self.code = code + self.headers = headers + self.message = message + + def __str__(self) -> str: + msg = indent(self.message, " ") + return f"{self.code}, message:\n{msg}" + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: {self.code}, message={self.message!r}>" + + +class BadHttpMessage(HttpProcessingError): + + code = 400 + message = "Bad Request" + + def __init__(self, message: str, *, headers: Optional[_CIMultiDict] = None) -> None: + super().__init__(message=message, headers=headers) + self.args = (message,) + + +class HttpBadRequest(BadHttpMessage): + + code = 400 + message = "Bad Request" + + +class PayloadEncodingError(BadHttpMessage): + """Base class for payload errors""" + + +class ContentEncodingError(PayloadEncodingError): + """Content encoding error.""" + + +class TransferEncodingError(PayloadEncodingError): + """transfer encoding error.""" + + +class ContentLengthError(PayloadEncodingError): + """Not enough data for satisfy content length header.""" + + +class LineTooLong(BadHttpMessage): + def __init__( + self, line: str, limit: str = "Unknown", actual_size: str = "Unknown" + ) -> None: + super().__init__( + f"Got more than {limit} bytes ({actual_size}) when reading {line}." + ) + self.args = (line, limit, actual_size) + + +class InvalidHeader(BadHttpMessage): + def __init__(self, hdr: Union[bytes, str]) -> None: + hdr_s = hdr.decode(errors="backslashreplace") if isinstance(hdr, bytes) else hdr + super().__init__(f"Invalid HTTP header: {hdr!r}") + self.hdr = hdr_s + self.args = (hdr,) + + +class BadStatusLine(BadHttpMessage): + def __init__(self, line: str = "", error: Optional[str] = None) -> None: + if not isinstance(line, str): + line = repr(line) + super().__init__(error or f"Bad status line {line!r}") + self.args = (line,) + self.line = line + + +class InvalidURLError(BadHttpMessage): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_parser.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_parser.py new file mode 100644 index 0000000..0135119 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_parser.py @@ -0,0 +1,1041 @@ +import abc +import asyncio +import re +import string +from contextlib import suppress +from enum import IntEnum +from typing import ( + Any, + ClassVar, + Final, + Generic, + List, + Literal, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, istr +from yarl import URL + +from . import hdrs +from .base_protocol import BaseProtocol +from .compression_utils import HAS_BROTLI, BrotliDecompressor, ZLibDecompressor +from .helpers import ( + _EXC_SENTINEL, + DEBUG, + NO_EXTENSIONS, + BaseTimerContext, + method_must_be_empty_body, + set_exception, + status_code_must_be_empty_body, +) +from .http_exceptions import ( + BadHttpMessage, + BadStatusLine, + ContentEncodingError, + ContentLengthError, + InvalidHeader, + InvalidURLError, + LineTooLong, + TransferEncodingError, +) +from .http_writer import HttpVersion, HttpVersion10 +from .log import internal_logger +from .streams import EMPTY_PAYLOAD, StreamReader +from .typedefs import RawHeaders + +__all__ = ( + "HeadersParser", + "HttpParser", + "HttpRequestParser", + "HttpResponseParser", + "RawRequestMessage", + "RawResponseMessage", +) + +_SEP = Literal[b"\r\n", b"\n"] + +ASCIISET: Final[Set[str]] = set(string.printable) + +# See https://www.rfc-editor.org/rfc/rfc9110.html#name-overview +# and https://www.rfc-editor.org/rfc/rfc9110.html#name-tokens +# +# method = token +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +# "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +# token = 1*tchar +_TCHAR_SPECIALS: Final[str] = re.escape("!#$%&'*+-.^_`|~") +TOKENRE: Final[Pattern[str]] = re.compile(f"[0-9A-Za-z{_TCHAR_SPECIALS}]+") +VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d)\.(\d)", re.ASCII) +DIGITS: Final[Pattern[str]] = re.compile(r"\d+", re.ASCII) +HEXDIGITS: Final[Pattern[bytes]] = re.compile(rb"[0-9a-fA-F]+") + + +class RawRequestMessage(NamedTuple): + method: str + path: str + version: HttpVersion + headers: "CIMultiDictProxy[str]" + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + url: URL + + +class RawResponseMessage(NamedTuple): + version: HttpVersion + code: int + reason: str + headers: CIMultiDictProxy[str] + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + + +_MsgT = TypeVar("_MsgT", RawRequestMessage, RawResponseMessage) + + +class ParseState(IntEnum): + + PARSE_NONE = 0 + PARSE_LENGTH = 1 + PARSE_CHUNKED = 2 + PARSE_UNTIL_EOF = 3 + + +class ChunkState(IntEnum): + PARSE_CHUNKED_SIZE = 0 + PARSE_CHUNKED_CHUNK = 1 + PARSE_CHUNKED_CHUNK_EOF = 2 + PARSE_MAYBE_TRAILERS = 3 + PARSE_TRAILERS = 4 + + +class HeadersParser: + def __init__( + self, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + lax: bool = False, + ) -> None: + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self._lax = lax + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple["CIMultiDictProxy[str]", RawHeaders]: + headers: CIMultiDict[str] = CIMultiDict() + # note: "raw" does not mean inclusion of OWS before/after the field value + raw_headers = [] + + lines_idx = 1 + line = lines[1] + line_count = len(lines) + + while line: + # Parse initial header name : value pair. + try: + bname, bvalue = line.split(b":", 1) + except ValueError: + raise InvalidHeader(line) from None + + if len(bname) == 0: + raise InvalidHeader(bname) + + # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2 + if {bname[0], bname[-1]} & {32, 9}: # {" ", "\t"} + raise InvalidHeader(line) + + bvalue = bvalue.lstrip(b" \t") + if len(bname) > self.max_field_size: + raise LineTooLong( + "request header name {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(len(bname)), + ) + name = bname.decode("utf-8", "surrogateescape") + if not TOKENRE.fullmatch(name): + raise InvalidHeader(bname) + + header_length = len(bvalue) + + # next line + lines_idx += 1 + line = lines[lines_idx] + + # consume continuation lines + continuation = self._lax and line and line[0] in (32, 9) # (' ', '\t') + + # Deprecated: https://www.rfc-editor.org/rfc/rfc9112.html#name-obsolete-line-folding + if continuation: + bvalue_lst = [bvalue] + while continuation: + header_length += len(line) + if header_length > self.max_field_size: + raise LineTooLong( + "request header field {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(header_length), + ) + bvalue_lst.append(line) + + # next line + lines_idx += 1 + if lines_idx < line_count: + line = lines[lines_idx] + if line: + continuation = line[0] in (32, 9) # (' ', '\t') + else: + line = b"" + break + bvalue = b"".join(bvalue_lst) + else: + if header_length > self.max_field_size: + raise LineTooLong( + "request header field {}".format( + bname.decode("utf8", "backslashreplace") + ), + str(self.max_field_size), + str(header_length), + ) + + bvalue = bvalue.strip(b" \t") + value = bvalue.decode("utf-8", "surrogateescape") + + # https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-5 + if "\n" in value or "\r" in value or "\x00" in value: + raise InvalidHeader(bvalue) + + headers.add(name, value) + raw_headers.append((bname, bvalue)) + + return (CIMultiDictProxy(headers), tuple(raw_headers)) + + +def _is_supported_upgrade(headers: CIMultiDictProxy[str]) -> bool: + """Check if the upgrade header is supported.""" + return headers.get(hdrs.UPGRADE, "").lower() in {"tcp", "websocket"} + + +class HttpParser(abc.ABC, Generic[_MsgT]): + lax: ClassVar[bool] = False + + def __init__( + self, + protocol: Optional[BaseProtocol] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + limit: int = 2**16, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + timer: Optional[BaseTimerContext] = None, + code: Optional[int] = None, + method: Optional[str] = None, + readall: bool = False, + payload_exception: Optional[Type[BaseException]] = None, + response_with_body: bool = True, + read_until_eof: bool = False, + auto_decompress: bool = True, + ) -> None: + self.protocol = protocol + self.loop = loop + self.max_line_size = max_line_size + self.max_headers = max_headers + self.max_field_size = max_field_size + self.timer = timer + self.code = code + self.method = method + self.readall = readall + self.payload_exception = payload_exception + self.response_with_body = response_with_body + self.read_until_eof = read_until_eof + + self._lines: List[bytes] = [] + self._tail = b"" + self._upgraded = False + self._payload = None + self._payload_parser: Optional[HttpPayloadParser] = None + self._auto_decompress = auto_decompress + self._limit = limit + self._headers_parser = HeadersParser( + max_line_size, max_headers, max_field_size, self.lax + ) + + @abc.abstractmethod + def parse_message(self, lines: List[bytes]) -> _MsgT: + pass + + def feed_eof(self) -> Optional[_MsgT]: + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + else: + # try to extract partial message + if self._tail: + self._lines.append(self._tail) + + if self._lines: + if self._lines[-1] != "\r\n": + self._lines.append(b"") + with suppress(Exception): + return self.parse_message(self._lines) + return None + + def feed_data( + self, + data: bytes, + SEP: _SEP = b"\r\n", + EMPTY: bytes = b"", + CONTENT_LENGTH: istr = hdrs.CONTENT_LENGTH, + METH_CONNECT: str = hdrs.METH_CONNECT, + SEC_WEBSOCKET_KEY1: istr = hdrs.SEC_WEBSOCKET_KEY1, + ) -> Tuple[List[Tuple[_MsgT, StreamReader]], bool, bytes]: + + messages = [] + + if self._tail: + data, self._tail = self._tail + data, b"" + + data_len = len(data) + start_pos = 0 + loop = self.loop + + while start_pos < data_len: + + # read HTTP message (request/response line + headers), \r\n\r\n + # and split by lines + if self._payload_parser is None and not self._upgraded: + pos = data.find(SEP, start_pos) + # consume \r\n + if pos == start_pos and not self._lines: + start_pos = pos + len(SEP) + continue + + if pos >= start_pos: + # line found + line = data[start_pos:pos] + if SEP == b"\n": # For lax response parsing + line = line.rstrip(b"\r") + self._lines.append(line) + start_pos = pos + len(SEP) + + # \r\n\r\n found + if self._lines[-1] == EMPTY: + try: + msg: _MsgT = self.parse_message(self._lines) + finally: + self._lines.clear() + + def get_content_length() -> Optional[int]: + # payload length + length_hdr = msg.headers.get(CONTENT_LENGTH) + if length_hdr is None: + return None + + # Shouldn't allow +/- or other number formats. + # https://www.rfc-editor.org/rfc/rfc9110#section-8.6-2 + # msg.headers is already stripped of leading/trailing wsp + if not DIGITS.fullmatch(length_hdr): + raise InvalidHeader(CONTENT_LENGTH) + + return int(length_hdr) + + length = get_content_length() + # do not support old websocket spec + if SEC_WEBSOCKET_KEY1 in msg.headers: + raise InvalidHeader(SEC_WEBSOCKET_KEY1) + + self._upgraded = msg.upgrade and _is_supported_upgrade( + msg.headers + ) + + method = getattr(msg, "method", self.method) + # code is only present on responses + code = getattr(msg, "code", 0) + + assert self.protocol is not None + # calculate payload + empty_body = status_code_must_be_empty_body(code) or bool( + method and method_must_be_empty_body(method) + ) + if not empty_body and ( + ((length is not None and length > 0) or msg.chunked) + and not self._upgraded + ): + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + readall=self.readall, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + elif method == METH_CONNECT: + assert isinstance(msg, RawRequestMessage) + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + self._upgraded = True + self._payload_parser = HttpPayloadParser( + payload, + method=msg.method, + compression=msg.compression, + readall=True, + auto_decompress=self._auto_decompress, + lax=self.lax, + ) + elif not empty_body and length is None and self.read_until_eof: + payload = StreamReader( + self.protocol, + timer=self.timer, + loop=loop, + limit=self._limit, + ) + payload_parser = HttpPayloadParser( + payload, + length=length, + chunked=msg.chunked, + method=method, + compression=msg.compression, + code=self.code, + readall=True, + response_with_body=self.response_with_body, + auto_decompress=self._auto_decompress, + lax=self.lax, + ) + if not payload_parser.done: + self._payload_parser = payload_parser + else: + payload = EMPTY_PAYLOAD + + messages.append((msg, payload)) + else: + self._tail = data[start_pos:] + data = EMPTY + break + + # no parser, just store + elif self._payload_parser is None and self._upgraded: + assert not self._lines + break + + # feed payload + elif data and start_pos < data_len: + assert not self._lines + assert self._payload_parser is not None + try: + eof, data = self._payload_parser.feed_data(data[start_pos:], SEP) + except BaseException as underlying_exc: + reraised_exc = underlying_exc + if self.payload_exception is not None: + reraised_exc = self.payload_exception(str(underlying_exc)) + + set_exception( + self._payload_parser.payload, + reraised_exc, + underlying_exc, + ) + + eof = True + data = b"" + + if eof: + start_pos = 0 + data_len = len(data) + self._payload_parser = None + continue + else: + break + + if data and start_pos < data_len: + data = data[start_pos:] + else: + data = EMPTY + + return messages, self._upgraded, data + + def parse_headers( + self, lines: List[bytes] + ) -> Tuple[ + "CIMultiDictProxy[str]", RawHeaders, Optional[bool], Optional[str], bool, bool + ]: + """Parses RFC 5322 headers from a stream. + + Line continuations are supported. Returns list of header name + and value pairs. Header name is in upper case. + """ + headers, raw_headers = self._headers_parser.parse_headers(lines) + close_conn = None + encoding = None + upgrade = False + chunked = False + + # https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-6 + # https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + singletons = ( + hdrs.CONTENT_LENGTH, + hdrs.CONTENT_LOCATION, + hdrs.CONTENT_RANGE, + hdrs.CONTENT_TYPE, + hdrs.ETAG, + hdrs.HOST, + hdrs.MAX_FORWARDS, + hdrs.SERVER, + hdrs.TRANSFER_ENCODING, + hdrs.USER_AGENT, + ) + bad_hdr = next((h for h in singletons if len(headers.getall(h, ())) > 1), None) + if bad_hdr is not None: + raise BadHttpMessage(f"Duplicate '{bad_hdr}' header found.") + + # keep-alive + conn = headers.get(hdrs.CONNECTION) + if conn: + v = conn.lower() + if v == "close": + close_conn = True + elif v == "keep-alive": + close_conn = False + # https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols + elif v == "upgrade" and headers.get(hdrs.UPGRADE): + upgrade = True + + # encoding + enc = headers.get(hdrs.CONTENT_ENCODING) + if enc: + enc = enc.lower() + if enc in ("gzip", "deflate", "br"): + encoding = enc + + # chunking + te = headers.get(hdrs.TRANSFER_ENCODING) + if te is not None: + if "chunked" == te.lower(): + chunked = True + else: + raise BadHttpMessage("Request has invalid `Transfer-Encoding`") + + if hdrs.CONTENT_LENGTH in headers: + raise BadHttpMessage( + "Transfer-Encoding can't be present with Content-Length", + ) + + return (headers, raw_headers, close_conn, encoding, upgrade, chunked) + + def set_upgraded(self, val: bool) -> None: + """Set connection upgraded (to websocket) mode. + + :param bool val: new state. + """ + self._upgraded = val + + +class HttpRequestParser(HttpParser[RawRequestMessage]): + """Read request status line. + + Exception .http_exceptions.BadStatusLine + could be raised in case of any errors in status line. + Returns RawRequestMessage. + """ + + def parse_message(self, lines: List[bytes]) -> RawRequestMessage: + # request line + line = lines[0].decode("utf-8", "surrogateescape") + try: + method, path, version = line.split(" ", maxsplit=2) + except ValueError: + raise BadStatusLine(line) from None + + if len(path) > self.max_line_size: + raise LineTooLong( + "Status line is too long", str(self.max_line_size), str(len(path)) + ) + + # method + if not TOKENRE.fullmatch(method): + raise BadStatusLine(method) + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + if method == "CONNECT": + # authority-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3 + url = URL.build(authority=path, encoded=True) + elif path.startswith("/"): + # origin-form, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 + path_part, _hash_separator, url_fragment = path.partition("#") + path_part, _question_mark_separator, qs_part = path_part.partition("?") + + # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based + # NOTE: parser does, otherwise it results into the same + # NOTE: HTTP Request-Line input producing different + # NOTE: `yarl.URL()` objects + url = URL.build( + path=path_part, + query_string=qs_part, + fragment=url_fragment, + encoded=True, + ) + elif path == "*" and method == "OPTIONS": + # asterisk-form, + url = URL(path, encoded=True) + else: + # absolute-form for proxy maybe, + # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2 + url = URL(path, encoded=True) + if url.scheme == "": + # not absolute-form + raise InvalidURLError( + path.encode(errors="surrogateescape").decode("latin1") + ) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines) + + if close is None: # then the headers weren't set in the request + if version_o <= HttpVersion10: # HTTP 1.0 must asks to not close + close = True + else: # HTTP 1.1 must ask to close. + close = False + + return RawRequestMessage( + method, + path, + version_o, + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + url, + ) + + +class HttpResponseParser(HttpParser[RawResponseMessage]): + """Read response status line and headers. + + BadStatusLine could be raised in case of any errors in status line. + Returns RawResponseMessage. + """ + + # Lax mode should only be enabled on response parser. + lax = not DEBUG + + def feed_data( + self, + data: bytes, + SEP: Optional[_SEP] = None, + *args: Any, + **kwargs: Any, + ) -> Tuple[List[Tuple[RawResponseMessage, StreamReader]], bool, bytes]: + if SEP is None: + SEP = b"\r\n" if DEBUG else b"\n" + return super().feed_data(data, SEP, *args, **kwargs) + + def parse_message(self, lines: List[bytes]) -> RawResponseMessage: + line = lines[0].decode("utf-8", "surrogateescape") + try: + version, status = line.split(maxsplit=1) + except ValueError: + raise BadStatusLine(line) from None + + try: + status, reason = status.split(maxsplit=1) + except ValueError: + status = status.strip() + reason = "" + + if len(reason) > self.max_line_size: + raise LineTooLong( + "Status line is too long", str(self.max_line_size), str(len(reason)) + ) + + # version + match = VERSRE.fullmatch(version) + if match is None: + raise BadStatusLine(line) + version_o = HttpVersion(int(match.group(1)), int(match.group(2))) + + # The status code is a three-digit ASCII number, no padding + if len(status) != 3 or not DIGITS.fullmatch(status): + raise BadStatusLine(line) + status_i = int(status) + + # read headers + ( + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) = self.parse_headers(lines) + + if close is None: + if version_o <= HttpVersion10: + close = True + # https://www.rfc-editor.org/rfc/rfc9112.html#name-message-body-length + elif 100 <= status_i < 200 or status_i in {204, 304}: + close = False + elif hdrs.CONTENT_LENGTH in headers or hdrs.TRANSFER_ENCODING in headers: + close = False + else: + # https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3-2.8 + close = True + + return RawResponseMessage( + version_o, + status_i, + reason.strip(), + headers, + raw_headers, + close, + compression, + upgrade, + chunked, + ) + + +class HttpPayloadParser: + def __init__( + self, + payload: StreamReader, + length: Optional[int] = None, + chunked: bool = False, + compression: Optional[str] = None, + code: Optional[int] = None, + method: Optional[str] = None, + readall: bool = False, + response_with_body: bool = True, + auto_decompress: bool = True, + lax: bool = False, + ) -> None: + self._length = 0 + self._type = ParseState.PARSE_NONE + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + self._chunk_size = 0 + self._chunk_tail = b"" + self._auto_decompress = auto_decompress + self._lax = lax + self.done = False + + # payload decompression wrapper + if response_with_body and compression and self._auto_decompress: + real_payload: Union[StreamReader, DeflateBuffer] = DeflateBuffer( + payload, compression + ) + else: + real_payload = payload + + # payload parser + if not response_with_body: + # don't parse payload if it's not expected to be received + self._type = ParseState.PARSE_NONE + real_payload.feed_eof() + self.done = True + + elif chunked: + self._type = ParseState.PARSE_CHUNKED + elif length is not None: + self._type = ParseState.PARSE_LENGTH + self._length = length + if self._length == 0: + real_payload.feed_eof() + self.done = True + else: + if readall and code != 204: + self._type = ParseState.PARSE_UNTIL_EOF + elif method in ("PUT", "POST"): + internal_logger.warning( # pragma: no cover + "Content-Length or Transfer-Encoding header is required" + ) + self._type = ParseState.PARSE_NONE + real_payload.feed_eof() + self.done = True + + self.payload = real_payload + + def feed_eof(self) -> None: + if self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_eof() + elif self._type == ParseState.PARSE_LENGTH: + raise ContentLengthError( + "Not enough data for satisfy content length header." + ) + elif self._type == ParseState.PARSE_CHUNKED: + raise TransferEncodingError( + "Not enough data for satisfy transfer length header." + ) + + def feed_data( + self, chunk: bytes, SEP: _SEP = b"\r\n", CHUNK_EXT: bytes = b";" + ) -> Tuple[bool, bytes]: + # Read specified amount of bytes + if self._type == ParseState.PARSE_LENGTH: + required = self._length + chunk_len = len(chunk) + + if required >= chunk_len: + self._length = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + if self._length == 0: + self.payload.feed_eof() + return True, b"" + else: + self._length = 0 + self.payload.feed_data(chunk[:required], required) + self.payload.feed_eof() + return True, chunk[required:] + + # Chunked transfer encoding parser + elif self._type == ParseState.PARSE_CHUNKED: + if self._chunk_tail: + chunk = self._chunk_tail + chunk + self._chunk_tail = b"" + + while chunk: + + # read next chunk size + if self._chunk == ChunkState.PARSE_CHUNKED_SIZE: + pos = chunk.find(SEP) + if pos >= 0: + i = chunk.find(CHUNK_EXT, 0, pos) + if i >= 0: + size_b = chunk[:i] # strip chunk-extensions + else: + size_b = chunk[:pos] + + if self._lax: # Allow whitespace in lax mode. + size_b = size_b.strip() + + if not re.fullmatch(HEXDIGITS, size_b): + exc = TransferEncodingError( + chunk[:pos].decode("ascii", "surrogateescape") + ) + set_exception(self.payload, exc) + raise exc + size = int(bytes(size_b), 16) + + chunk = chunk[pos + len(SEP) :] + if size == 0: # eof marker + self._chunk = ChunkState.PARSE_MAYBE_TRAILERS + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + else: + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK + self._chunk_size = size + self.payload.begin_http_chunk_receiving() + else: + self._chunk_tail = chunk + return False, b"" + + # read chunk and feed buffer + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK: + required = self._chunk_size + chunk_len = len(chunk) + + if required > chunk_len: + self._chunk_size = required - chunk_len + self.payload.feed_data(chunk, chunk_len) + return False, b"" + else: + self._chunk_size = 0 + self.payload.feed_data(chunk[:required], required) + chunk = chunk[required:] + if self._lax and chunk.startswith(b"\r"): + chunk = chunk[1:] + self._chunk = ChunkState.PARSE_CHUNKED_CHUNK_EOF + self.payload.end_http_chunk_receiving() + + # toss the CRLF at the end of the chunk + if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK_EOF: + if chunk[: len(SEP)] == SEP: + chunk = chunk[len(SEP) :] + self._chunk = ChunkState.PARSE_CHUNKED_SIZE + else: + self._chunk_tail = chunk + return False, b"" + + # if stream does not contain trailer, after 0\r\n + # we should get another \r\n otherwise + # trailers needs to be skipped until \r\n\r\n + if self._chunk == ChunkState.PARSE_MAYBE_TRAILERS: + head = chunk[: len(SEP)] + if head == SEP: + # end of stream + self.payload.feed_eof() + return True, chunk[len(SEP) :] + # Both CR and LF, or only LF may not be received yet. It is + # expected that CRLF or LF will be shown at the very first + # byte next time, otherwise trailers should come. The last + # CRLF which marks the end of response might not be + # contained in the same TCP segment which delivered the + # size indicator. + if not head: + return False, b"" + if head == SEP[:1]: + self._chunk_tail = head + return False, b"" + self._chunk = ChunkState.PARSE_TRAILERS + + # read and discard trailer up to the CRLF terminator + if self._chunk == ChunkState.PARSE_TRAILERS: + pos = chunk.find(SEP) + if pos >= 0: + chunk = chunk[pos + len(SEP) :] + self._chunk = ChunkState.PARSE_MAYBE_TRAILERS + else: + self._chunk_tail = chunk + return False, b"" + + # Read all bytes until eof + elif self._type == ParseState.PARSE_UNTIL_EOF: + self.payload.feed_data(chunk, len(chunk)) + + return False, b"" + + +class DeflateBuffer: + """DeflateStream decompress stream and feed data into specified stream.""" + + decompressor: Any + + def __init__(self, out: StreamReader, encoding: Optional[str]) -> None: + self.out = out + self.size = 0 + self.encoding = encoding + self._started_decoding = False + + self.decompressor: Union[BrotliDecompressor, ZLibDecompressor] + if encoding == "br": + if not HAS_BROTLI: # pragma: no cover + raise ContentEncodingError( + "Can not decode content-encoding: brotli (br). " + "Please install `Brotli`" + ) + self.decompressor = BrotliDecompressor() + else: + self.decompressor = ZLibDecompressor(encoding=encoding) + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + set_exception(self.out, exc, exc_cause) + + def feed_data(self, chunk: bytes, size: int) -> None: + if not size: + return + + self.size += size + + # RFC1950 + # bits 0..3 = CM = 0b1000 = 8 = "deflate" + # bits 4..7 = CINFO = 1..7 = windows size. + if ( + not self._started_decoding + and self.encoding == "deflate" + and chunk[0] & 0xF != 8 + ): + # Change the decoder to decompress incorrectly compressed data + # Actually we should issue a warning about non-RFC-compliant data. + self.decompressor = ZLibDecompressor( + encoding=self.encoding, suppress_deflate_header=True + ) + + try: + chunk = self.decompressor.decompress_sync(chunk) + except Exception: + raise ContentEncodingError( + "Can not decode content-encoding: %s" % self.encoding + ) + + self._started_decoding = True + + if chunk: + self.out.feed_data(chunk, len(chunk)) + + def feed_eof(self) -> None: + chunk = self.decompressor.flush() + + if chunk or self.size > 0: + self.out.feed_data(chunk, len(chunk)) + if self.encoding == "deflate" and not self.decompressor.eof: + raise ContentEncodingError("deflate") + + self.out.feed_eof() + + def begin_http_chunk_receiving(self) -> None: + self.out.begin_http_chunk_receiving() + + def end_http_chunk_receiving(self) -> None: + self.out.end_http_chunk_receiving() + + +HttpRequestParserPy = HttpRequestParser +HttpResponseParserPy = HttpResponseParser +RawRequestMessagePy = RawRequestMessage +RawResponseMessagePy = RawResponseMessage + +try: + if not NO_EXTENSIONS: + from ._http_parser import ( # type: ignore[import-not-found,no-redef] + HttpRequestParser, + HttpResponseParser, + RawRequestMessage, + RawResponseMessage, + ) + + HttpRequestParserC = HttpRequestParser + HttpResponseParserC = HttpResponseParser + RawRequestMessageC = RawRequestMessage + RawResponseMessageC = RawResponseMessage +except ImportError: # pragma: no cover + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_websocket.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_websocket.py new file mode 100644 index 0000000..39f2e4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_websocket.py @@ -0,0 +1,740 @@ +"""WebSocket protocol versions 13 and 8.""" + +import asyncio +import functools +import json +import random +import re +import sys +import zlib +from enum import IntEnum +from struct import Struct +from typing import ( + Any, + Callable, + Final, + List, + NamedTuple, + Optional, + Pattern, + Set, + Tuple, + Union, + cast, +) + +from .base_protocol import BaseProtocol +from .compression_utils import ZLibCompressor, ZLibDecompressor +from .helpers import NO_EXTENSIONS, set_exception +from .streams import DataQueue + +__all__ = ( + "WS_CLOSED_MESSAGE", + "WS_CLOSING_MESSAGE", + "WS_KEY", + "WebSocketReader", + "WebSocketWriter", + "WSMessage", + "WebSocketError", + "WSMsgType", + "WSCloseCode", +) + + +class WSCloseCode(IntEnum): + OK = 1000 + GOING_AWAY = 1001 + PROTOCOL_ERROR = 1002 + UNSUPPORTED_DATA = 1003 + ABNORMAL_CLOSURE = 1006 + INVALID_TEXT = 1007 + POLICY_VIOLATION = 1008 + MESSAGE_TOO_BIG = 1009 + MANDATORY_EXTENSION = 1010 + INTERNAL_ERROR = 1011 + SERVICE_RESTART = 1012 + TRY_AGAIN_LATER = 1013 + BAD_GATEWAY = 1014 + + +ALLOWED_CLOSE_CODES: Final[Set[int]] = {int(i) for i in WSCloseCode} + +# For websockets, keeping latency low is extremely important as implementations +# generally expect to be able to send and receive messages quickly. We use a +# larger chunk size than the default to reduce the number of executor calls +# since the executor is a significant source of latency and overhead when +# the chunks are small. A size of 5KiB was chosen because it is also the +# same value python-zlib-ng choose to use as the threshold to release the GIL. + +WEBSOCKET_MAX_SYNC_CHUNK_SIZE = 5 * 1024 + + +class WSMsgType(IntEnum): + # websocket spec types + CONTINUATION = 0x0 + TEXT = 0x1 + BINARY = 0x2 + PING = 0x9 + PONG = 0xA + CLOSE = 0x8 + + # aiohttp specific types + CLOSING = 0x100 + CLOSED = 0x101 + ERROR = 0x102 + + text = TEXT + binary = BINARY + ping = PING + pong = PONG + close = CLOSE + closing = CLOSING + closed = CLOSED + error = ERROR + + +WS_KEY: Final[bytes] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + + +UNPACK_LEN2 = Struct("!H").unpack_from +UNPACK_LEN3 = Struct("!Q").unpack_from +UNPACK_CLOSE_CODE = Struct("!H").unpack +PACK_LEN1 = Struct("!BB").pack +PACK_LEN2 = Struct("!BBH").pack +PACK_LEN3 = Struct("!BBQ").pack +PACK_CLOSE_CODE = Struct("!H").pack +MSG_SIZE: Final[int] = 2**14 +DEFAULT_LIMIT: Final[int] = 2**16 + + +class WSMessage(NamedTuple): + type: WSMsgType + # To type correctly, this would need some kind of tagged union for each type. + data: Any + extra: Optional[str] + + def json(self, *, loads: Callable[[Any], Any] = json.loads) -> Any: + """Return parsed JSON data. + + .. versionadded:: 0.22 + """ + return loads(self.data) + + +WS_CLOSED_MESSAGE = WSMessage(WSMsgType.CLOSED, None, None) +WS_CLOSING_MESSAGE = WSMessage(WSMsgType.CLOSING, None, None) + + +class WebSocketError(Exception): + """WebSocket protocol parser error.""" + + def __init__(self, code: int, message: str) -> None: + self.code = code + super().__init__(code, message) + + def __str__(self) -> str: + return cast(str, self.args[1]) + + +class WSHandshakeError(Exception): + """WebSocket protocol handshake error.""" + + +native_byteorder: Final[str] = sys.byteorder + + +# Used by _websocket_mask_python +@functools.lru_cache +def _xor_table() -> List[bytes]: + return [bytes(a ^ b for a in range(256)) for b in range(256)] + + +def _websocket_mask_python(mask: bytes, data: bytearray) -> None: + """Websocket masking function. + + `mask` is a `bytes` object of length 4; `data` is a `bytearray` + object of any length. The contents of `data` are masked with `mask`, + as specified in section 5.3 of RFC 6455. + + Note that this function mutates the `data` argument. + + This pure-python implementation may be replaced by an optimized + version when available. + + """ + assert isinstance(data, bytearray), data + assert len(mask) == 4, mask + + if data: + _XOR_TABLE = _xor_table() + a, b, c, d = (_XOR_TABLE[n] for n in mask) + data[::4] = data[::4].translate(a) + data[1::4] = data[1::4].translate(b) + data[2::4] = data[2::4].translate(c) + data[3::4] = data[3::4].translate(d) + + +if NO_EXTENSIONS: # pragma: no cover + _websocket_mask = _websocket_mask_python +else: + try: + from ._websocket import _websocket_mask_cython # type: ignore[import-not-found] + + _websocket_mask = _websocket_mask_cython + except ImportError: # pragma: no cover + _websocket_mask = _websocket_mask_python + +_WS_DEFLATE_TRAILING: Final[bytes] = bytes([0x00, 0x00, 0xFF, 0xFF]) + + +_WS_EXT_RE: Final[Pattern[str]] = re.compile( + r"^(?:;\s*(?:" + r"(server_no_context_takeover)|" + r"(client_no_context_takeover)|" + r"(server_max_window_bits(?:=(\d+))?)|" + r"(client_max_window_bits(?:=(\d+))?)))*$" +) + +_WS_EXT_RE_SPLIT: Final[Pattern[str]] = re.compile(r"permessage-deflate([^,]+)?") + + +def ws_ext_parse(extstr: Optional[str], isserver: bool = False) -> Tuple[int, bool]: + if not extstr: + return 0, False + + compress = 0 + notakeover = False + for ext in _WS_EXT_RE_SPLIT.finditer(extstr): + defext = ext.group(1) + # Return compress = 15 when get `permessage-deflate` + if not defext: + compress = 15 + break + match = _WS_EXT_RE.match(defext) + if match: + compress = 15 + if isserver: + # Server never fail to detect compress handshake. + # Server does not need to send max wbit to client + if match.group(4): + compress = int(match.group(4)) + # Group3 must match if group4 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # CONTINUE to next extension + if compress > 15 or compress < 9: + compress = 0 + continue + if match.group(1): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + else: + if match.group(6): + compress = int(match.group(6)) + # Group5 must match if group6 matches + # Compress wbit 8 does not support in zlib + # If compress level not support, + # FAIL the parse progress + if compress > 15 or compress < 9: + raise WSHandshakeError("Invalid window size") + if match.group(2): + notakeover = True + # Ignore regex group 5 & 6 for client_max_window_bits + break + # Return Fail if client side and not match + elif not isserver: + raise WSHandshakeError("Extension for deflate not supported" + ext.group(1)) + + return compress, notakeover + + +def ws_ext_gen( + compress: int = 15, isserver: bool = False, server_notakeover: bool = False +) -> str: + # client_notakeover=False not used for server + # compress wbit 8 does not support in zlib + if compress < 9 or compress > 15: + raise ValueError( + "Compress wbits must between 9 and 15, " "zlib does not support wbits=8" + ) + enabledext = ["permessage-deflate"] + if not isserver: + enabledext.append("client_max_window_bits") + + if compress < 15: + enabledext.append("server_max_window_bits=" + str(compress)) + if server_notakeover: + enabledext.append("server_no_context_takeover") + # if client_notakeover: + # enabledext.append('client_no_context_takeover') + return "; ".join(enabledext) + + +class WSParserState(IntEnum): + READ_HEADER = 1 + READ_PAYLOAD_LENGTH = 2 + READ_PAYLOAD_MASK = 3 + READ_PAYLOAD = 4 + + +class WebSocketReader: + def __init__( + self, queue: DataQueue[WSMessage], max_msg_size: int, compress: bool = True + ) -> None: + self.queue = queue + self._max_msg_size = max_msg_size + + self._exc: Optional[BaseException] = None + self._partial = bytearray() + self._state = WSParserState.READ_HEADER + + self._opcode: Optional[int] = None + self._frame_fin = False + self._frame_opcode: Optional[int] = None + self._frame_payload = bytearray() + + self._tail = b"" + self._has_mask = False + self._frame_mask: Optional[bytes] = None + self._payload_length = 0 + self._payload_length_flag = 0 + self._compressed: Optional[bool] = None + self._decompressobj: Optional[ZLibDecompressor] = None + self._compress = compress + + def feed_eof(self) -> None: + self.queue.feed_eof() + + def feed_data(self, data: bytes) -> Tuple[bool, bytes]: + if self._exc: + return True, data + + try: + return self._feed_data(data) + except Exception as exc: + self._exc = exc + set_exception(self.queue, exc) + return True, b"" + + def _feed_data(self, data: bytes) -> Tuple[bool, bytes]: + for fin, opcode, payload, compressed in self.parse_frame(data): + if compressed and not self._decompressobj: + self._decompressobj = ZLibDecompressor(suppress_deflate_header=True) + if opcode == WSMsgType.CLOSE: + if len(payload) >= 2: + close_code = UNPACK_CLOSE_CODE(payload[:2])[0] + if close_code < 3000 and close_code not in ALLOWED_CLOSE_CODES: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close code: {close_code}", + ) + try: + close_message = payload[2:].decode("utf-8") + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + msg = WSMessage(WSMsgType.CLOSE, close_code, close_message) + elif payload: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + f"Invalid close frame: {fin} {opcode} {payload!r}", + ) + else: + msg = WSMessage(WSMsgType.CLOSE, 0, "") + + self.queue.feed_data(msg, 0) + + elif opcode == WSMsgType.PING: + self.queue.feed_data( + WSMessage(WSMsgType.PING, payload, ""), len(payload) + ) + + elif opcode == WSMsgType.PONG: + self.queue.feed_data( + WSMessage(WSMsgType.PONG, payload, ""), len(payload) + ) + + elif ( + opcode not in (WSMsgType.TEXT, WSMsgType.BINARY) + and self._opcode is None + ): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, f"Unexpected opcode={opcode!r}" + ) + else: + # load text/binary + if not fin: + # got partial frame payload + if opcode != WSMsgType.CONTINUATION: + self._opcode = opcode + self._partial.extend(payload) + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + "Message size {} exceeds limit {}".format( + len(self._partial), self._max_msg_size + ), + ) + else: + # previous frame was non finished + # we should get continuation opcode + if self._partial: + if opcode != WSMsgType.CONTINUATION: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "The opcode in non-fin frame is expected " + "to be zero, got {!r}".format(opcode), + ) + + if opcode == WSMsgType.CONTINUATION: + assert self._opcode is not None + opcode = self._opcode + self._opcode = None + + self._partial.extend(payload) + if self._max_msg_size and len(self._partial) >= self._max_msg_size: + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + "Message size {} exceeds limit {}".format( + len(self._partial), self._max_msg_size + ), + ) + + # Decompress process must to be done after all packets + # received. + if compressed: + assert self._decompressobj is not None + self._partial.extend(_WS_DEFLATE_TRAILING) + payload_merged = self._decompressobj.decompress_sync( + self._partial, self._max_msg_size + ) + if self._decompressobj.unconsumed_tail: + left = len(self._decompressobj.unconsumed_tail) + raise WebSocketError( + WSCloseCode.MESSAGE_TOO_BIG, + "Decompressed message size {} exceeds limit {}".format( + self._max_msg_size + left, self._max_msg_size + ), + ) + else: + payload_merged = bytes(self._partial) + + self._partial.clear() + + if opcode == WSMsgType.TEXT: + try: + text = payload_merged.decode("utf-8") + self.queue.feed_data( + WSMessage(WSMsgType.TEXT, text, ""), len(text) + ) + except UnicodeDecodeError as exc: + raise WebSocketError( + WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message" + ) from exc + else: + self.queue.feed_data( + WSMessage(WSMsgType.BINARY, payload_merged, ""), + len(payload_merged), + ) + + return False, b"" + + def parse_frame( + self, buf: bytes + ) -> List[Tuple[bool, Optional[int], bytearray, Optional[bool]]]: + """Return the next frame from the socket.""" + frames = [] + if self._tail: + buf, self._tail = self._tail + buf, b"" + + start_pos = 0 + buf_length = len(buf) + + while True: + # read header + if self._state == WSParserState.READ_HEADER: + if buf_length - start_pos >= 2: + data = buf[start_pos : start_pos + 2] + start_pos += 2 + first_byte, second_byte = data + + fin = (first_byte >> 7) & 1 + rsv1 = (first_byte >> 6) & 1 + rsv2 = (first_byte >> 5) & 1 + rsv3 = (first_byte >> 4) & 1 + opcode = first_byte & 0xF + + # frame-fin = %x0 ; more frames of this message follow + # / %x1 ; final frame of this message + # frame-rsv1 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv2 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # frame-rsv3 = %x0 ; + # 1 bit, MUST be 0 unless negotiated otherwise + # + # Remove rsv1 from this test for deflate development + if rsv2 or rsv3 or (rsv1 and not self._compress): + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + if opcode > 0x7 and fin == 0: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received fragmented control frame", + ) + + has_mask = (second_byte >> 7) & 1 + length = second_byte & 0x7F + + # Control frames MUST have a payload + # length of 125 bytes or less + if opcode > 0x7 and length > 125: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Control frame payload cannot be " "larger than 125 bytes", + ) + + # Set compress status if last package is FIN + # OR set compress status if this is first fragment + # Raise error if not first fragment with rsv1 = 0x1 + if self._frame_fin or self._compressed is None: + self._compressed = True if rsv1 else False + elif rsv1: + raise WebSocketError( + WSCloseCode.PROTOCOL_ERROR, + "Received frame with non-zero reserved bits", + ) + + self._frame_fin = bool(fin) + self._frame_opcode = opcode + self._has_mask = bool(has_mask) + self._payload_length_flag = length + self._state = WSParserState.READ_PAYLOAD_LENGTH + else: + break + + # read payload length + if self._state == WSParserState.READ_PAYLOAD_LENGTH: + length = self._payload_length_flag + if length == 126: + if buf_length - start_pos >= 2: + data = buf[start_pos : start_pos + 2] + start_pos += 2 + length = UNPACK_LEN2(data)[0] + self._payload_length = length + self._state = ( + WSParserState.READ_PAYLOAD_MASK + if self._has_mask + else WSParserState.READ_PAYLOAD + ) + else: + break + elif length > 126: + if buf_length - start_pos >= 8: + data = buf[start_pos : start_pos + 8] + start_pos += 8 + length = UNPACK_LEN3(data)[0] + self._payload_length = length + self._state = ( + WSParserState.READ_PAYLOAD_MASK + if self._has_mask + else WSParserState.READ_PAYLOAD + ) + else: + break + else: + self._payload_length = length + self._state = ( + WSParserState.READ_PAYLOAD_MASK + if self._has_mask + else WSParserState.READ_PAYLOAD + ) + + # read payload mask + if self._state == WSParserState.READ_PAYLOAD_MASK: + if buf_length - start_pos >= 4: + self._frame_mask = buf[start_pos : start_pos + 4] + start_pos += 4 + self._state = WSParserState.READ_PAYLOAD + else: + break + + if self._state == WSParserState.READ_PAYLOAD: + length = self._payload_length + payload = self._frame_payload + + chunk_len = buf_length - start_pos + if length >= chunk_len: + self._payload_length = length - chunk_len + payload.extend(buf[start_pos:]) + start_pos = buf_length + else: + self._payload_length = 0 + payload.extend(buf[start_pos : start_pos + length]) + start_pos = start_pos + length + + if self._payload_length == 0: + if self._has_mask: + assert self._frame_mask is not None + _websocket_mask(self._frame_mask, payload) + + frames.append( + (self._frame_fin, self._frame_opcode, payload, self._compressed) + ) + + self._frame_payload = bytearray() + self._state = WSParserState.READ_HEADER + else: + break + + self._tail = buf[start_pos:] + + return frames + + +class WebSocketWriter: + def __init__( + self, + protocol: BaseProtocol, + transport: asyncio.Transport, + *, + use_mask: bool = False, + limit: int = DEFAULT_LIMIT, + random: random.Random = random.Random(), + compress: int = 0, + notakeover: bool = False, + ) -> None: + self.protocol = protocol + self.transport = transport + self.use_mask = use_mask + self.randrange = random.randrange + self.compress = compress + self.notakeover = notakeover + self._closing = False + self._limit = limit + self._output_size = 0 + self._compressobj: Any = None # actually compressobj + + async def _send_frame( + self, message: bytes, opcode: int, compress: Optional[int] = None + ) -> None: + """Send a frame over the websocket with message as its payload.""" + if self._closing and not (opcode & WSMsgType.CLOSE): + raise ConnectionResetError("Cannot write to closing transport") + + rsv = 0 + + # Only compress larger packets (disabled) + # Does small packet needs to be compressed? + # if self.compress and opcode < 8 and len(message) > 124: + if (compress or self.compress) and opcode < 8: + if compress: + # Do not set self._compress if compressing is for this frame + compressobj = self._make_compress_obj(compress) + else: # self.compress + if not self._compressobj: + self._compressobj = self._make_compress_obj(self.compress) + compressobj = self._compressobj + + message = await compressobj.compress(message) + # Its critical that we do not return control to the event + # loop until we have finished sending all the compressed + # data. Otherwise we could end up mixing compressed frames + # if there are multiple coroutines compressing data. + message += compressobj.flush( + zlib.Z_FULL_FLUSH if self.notakeover else zlib.Z_SYNC_FLUSH + ) + if message.endswith(_WS_DEFLATE_TRAILING): + message = message[:-4] + rsv = rsv | 0x40 + + msg_length = len(message) + + use_mask = self.use_mask + if use_mask: + mask_bit = 0x80 + else: + mask_bit = 0 + + if msg_length < 126: + header = PACK_LEN1(0x80 | rsv | opcode, msg_length | mask_bit) + elif msg_length < (1 << 16): + header = PACK_LEN2(0x80 | rsv | opcode, 126 | mask_bit, msg_length) + else: + header = PACK_LEN3(0x80 | rsv | opcode, 127 | mask_bit, msg_length) + if use_mask: + mask_int = self.randrange(0, 0xFFFFFFFF) + mask = mask_int.to_bytes(4, "big") + message = bytearray(message) + _websocket_mask(mask, message) + self._write(header + mask + message) + self._output_size += len(header) + len(mask) + msg_length + else: + if msg_length > MSG_SIZE: + self._write(header) + self._write(message) + else: + self._write(header + message) + + self._output_size += len(header) + msg_length + + # It is safe to return control to the event loop when using compression + # after this point as we have already sent or buffered all the data. + + if self._output_size > self._limit: + self._output_size = 0 + await self.protocol._drain_helper() + + def _make_compress_obj(self, compress: int) -> ZLibCompressor: + return ZLibCompressor( + level=zlib.Z_BEST_SPEED, + wbits=-compress, + max_sync_chunk_size=WEBSOCKET_MAX_SYNC_CHUNK_SIZE, + ) + + def _write(self, data: bytes) -> None: + if self.transport is None or self.transport.is_closing(): + raise ConnectionResetError("Cannot write to closing transport") + self.transport.write(data) + + async def pong(self, message: Union[bytes, str] = b"") -> None: + """Send pong message.""" + if isinstance(message, str): + message = message.encode("utf-8") + await self._send_frame(message, WSMsgType.PONG) + + async def ping(self, message: Union[bytes, str] = b"") -> None: + """Send ping message.""" + if isinstance(message, str): + message = message.encode("utf-8") + await self._send_frame(message, WSMsgType.PING) + + async def send( + self, + message: Union[str, bytes], + binary: bool = False, + compress: Optional[int] = None, + ) -> None: + """Send a frame over the websocket with message as its payload.""" + if isinstance(message, str): + message = message.encode("utf-8") + if binary: + await self._send_frame(message, WSMsgType.BINARY, compress) + else: + await self._send_frame(message, WSMsgType.TEXT, compress) + + async def close(self, code: int = 1000, message: Union[bytes, str] = b"") -> None: + """Close the websocket, sending the specified code and message.""" + if isinstance(message, str): + message = message.encode("utf-8") + try: + await self._send_frame( + PACK_CLOSE_CODE(code) + message, opcode=WSMsgType.CLOSE + ) + finally: + self._closing = True diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_writer.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_writer.py new file mode 100644 index 0000000..d6b02e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/http_writer.py @@ -0,0 +1,198 @@ +"""Http related parsers and protocol.""" + +import asyncio +import zlib +from typing import Any, Awaitable, Callable, NamedTuple, Optional, Union # noqa + +from multidict import CIMultiDict + +from .abc import AbstractStreamWriter +from .base_protocol import BaseProtocol +from .compression_utils import ZLibCompressor +from .helpers import NO_EXTENSIONS + +__all__ = ("StreamWriter", "HttpVersion", "HttpVersion10", "HttpVersion11") + + +class HttpVersion(NamedTuple): + major: int + minor: int + + +HttpVersion10 = HttpVersion(1, 0) +HttpVersion11 = HttpVersion(1, 1) + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] +_T_OnHeadersSent = Optional[Callable[["CIMultiDict[str]"], Awaitable[None]]] + + +class StreamWriter(AbstractStreamWriter): + def __init__( + self, + protocol: BaseProtocol, + loop: asyncio.AbstractEventLoop, + on_chunk_sent: _T_OnChunkSent = None, + on_headers_sent: _T_OnHeadersSent = None, + ) -> None: + self._protocol = protocol + + self.loop = loop + self.length = None + self.chunked = False + self.buffer_size = 0 + self.output_size = 0 + + self._eof = False + self._compress: Optional[ZLibCompressor] = None + self._drain_waiter = None + + self._on_chunk_sent: _T_OnChunkSent = on_chunk_sent + self._on_headers_sent: _T_OnHeadersSent = on_headers_sent + + @property + def transport(self) -> Optional[asyncio.Transport]: + return self._protocol.transport + + @property + def protocol(self) -> BaseProtocol: + return self._protocol + + def enable_chunking(self) -> None: + self.chunked = True + + def enable_compression( + self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY + ) -> None: + self._compress = ZLibCompressor(encoding=encoding, strategy=strategy) + + def _write(self, chunk: bytes) -> None: + size = len(chunk) + self.buffer_size += size + self.output_size += size + transport = self.transport + if not self._protocol.connected or transport is None or transport.is_closing(): + raise ConnectionResetError("Cannot write to closing transport") + transport.write(chunk) + + async def write( + self, chunk: bytes, *, drain: bool = True, LIMIT: int = 0x10000 + ) -> None: + """Writes chunk of data to a stream. + + write_eof() indicates end of stream. + writer can't be used after write_eof() method being called. + write() return drain future. + """ + if self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + if isinstance(chunk, memoryview): + if chunk.nbytes != len(chunk): + # just reshape it + chunk = chunk.cast("c") + + if self._compress is not None: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self.length is not None: + chunk_len = len(chunk) + if self.length >= chunk_len: + self.length = self.length - chunk_len + else: + chunk = chunk[: self.length] + self.length = 0 + if not chunk: + return + + if chunk: + if self.chunked: + chunk_len_pre = ("%x\r\n" % len(chunk)).encode("ascii") + chunk = chunk_len_pre + chunk + b"\r\n" + + self._write(chunk) + + if self.buffer_size > LIMIT and drain: + self.buffer_size = 0 + await self.drain() + + async def write_headers( + self, status_line: str, headers: "CIMultiDict[str]" + ) -> None: + """Write request/response status and headers.""" + if self._on_headers_sent is not None: + await self._on_headers_sent(headers) + + # status + headers + buf = _serialize_headers(status_line, headers) + self._write(buf) + + async def write_eof(self, chunk: bytes = b"") -> None: + if self._eof: + return + + if chunk and self._on_chunk_sent is not None: + await self._on_chunk_sent(chunk) + + if self._compress: + if chunk: + chunk = await self._compress.compress(chunk) + + chunk += self._compress.flush() + if chunk and self.chunked: + chunk_len = ("%x\r\n" % len(chunk)).encode("ascii") + chunk = chunk_len + chunk + b"\r\n0\r\n\r\n" + else: + if self.chunked: + if chunk: + chunk_len = ("%x\r\n" % len(chunk)).encode("ascii") + chunk = chunk_len + chunk + b"\r\n0\r\n\r\n" + else: + chunk = b"0\r\n\r\n" + + if chunk: + self._write(chunk) + + await self.drain() + + self._eof = True + + async def drain(self) -> None: + """Flush the write buffer. + + The intended use is to write + + await w.write(data) + await w.drain() + """ + if self._protocol.transport is not None: + await self._protocol._drain_helper() + + +def _safe_header(string: str) -> str: + if "\r" in string or "\n" in string: + raise ValueError( + "Newline or carriage return detected in headers. " + "Potential header injection attack." + ) + return string + + +def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> bytes: + headers_gen = (_safe_header(k) + ": " + _safe_header(v) for k, v in headers.items()) + line = status_line + "\r\n" + "\r\n".join(headers_gen) + "\r\n\r\n" + return line.encode("utf-8") + + +_serialize_headers = _py_serialize_headers + +try: + import aiohttp._http_writer as _http_writer # type: ignore[import-not-found] + + _c_serialize_headers = _http_writer._serialize_headers + if not NO_EXTENSIONS: + _serialize_headers = _c_serialize_headers +except ImportError: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/locks.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/locks.py new file mode 100644 index 0000000..de2dc83 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/locks.py @@ -0,0 +1,41 @@ +import asyncio +import collections +from typing import Any, Deque, Optional + + +class EventResultOrError: + """Event asyncio lock helper class. + + Wraps the Event asyncio lock allowing either to awake the + locked Tasks without any error or raising an exception. + + thanks to @vorpalsmith for the simple design. + """ + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._exc: Optional[BaseException] = None + self._event = asyncio.Event() + self._waiters: Deque[asyncio.Future[Any]] = collections.deque() + + def set(self, exc: Optional[BaseException] = None) -> None: + self._exc = exc + self._event.set() + + async def wait(self) -> Any: + waiter = self._loop.create_task(self._event.wait()) + self._waiters.append(waiter) + try: + val = await waiter + finally: + self._waiters.remove(waiter) + + if self._exc is not None: + raise self._exc + + return val + + def cancel(self) -> None: + """Cancel all waiters""" + for waiter in self._waiters: + waiter.cancel() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/log.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/log.py new file mode 100644 index 0000000..3cecea2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/log.py @@ -0,0 +1,8 @@ +import logging + +access_logger = logging.getLogger("aiohttp.access") +client_logger = logging.getLogger("aiohttp.client") +internal_logger = logging.getLogger("aiohttp.internal") +server_logger = logging.getLogger("aiohttp.server") +web_logger = logging.getLogger("aiohttp.web") +ws_logger = logging.getLogger("aiohttp.websocket") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/multipart.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/multipart.py new file mode 100644 index 0000000..71fc265 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/multipart.py @@ -0,0 +1,1015 @@ +import base64 +import binascii +import json +import re +import uuid +import warnings +import zlib +from collections import deque +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterator, + Deque, + Dict, + Iterator, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) +from urllib.parse import parse_qsl, unquote, urlencode + +from multidict import CIMultiDict, CIMultiDictProxy + +from .compression_utils import ZLibCompressor, ZLibDecompressor +from .hdrs import ( + CONTENT_DISPOSITION, + CONTENT_ENCODING, + CONTENT_LENGTH, + CONTENT_TRANSFER_ENCODING, + CONTENT_TYPE, +) +from .helpers import CHAR, TOKEN, parse_mimetype, reify +from .http import HeadersParser +from .payload import ( + JsonPayload, + LookupError, + Order, + Payload, + StringPayload, + get_payload, + payload_type, +) +from .streams import StreamReader + +__all__ = ( + "MultipartReader", + "MultipartWriter", + "BodyPartReader", + "BadContentDispositionHeader", + "BadContentDispositionParam", + "parse_content_disposition", + "content_disposition_filename", +) + + +if TYPE_CHECKING: + from .client_reqrep import ClientResponse + + +class BadContentDispositionHeader(RuntimeWarning): + pass + + +class BadContentDispositionParam(RuntimeWarning): + pass + + +def parse_content_disposition( + header: Optional[str], +) -> Tuple[Optional[str], Dict[str, str]]: + def is_token(string: str) -> bool: + return bool(string) and TOKEN >= set(string) + + def is_quoted(string: str) -> bool: + return string[0] == string[-1] == '"' + + def is_rfc5987(string: str) -> bool: + return is_token(string) and string.count("'") == 2 + + def is_extended_param(string: str) -> bool: + return string.endswith("*") + + def is_continuous_param(string: str) -> bool: + pos = string.find("*") + 1 + if not pos: + return False + substring = string[pos:-1] if string.endswith("*") else string[pos:] + return substring.isdigit() + + def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str: + return re.sub(f"\\\\([{chars}])", "\\1", text) + + if not header: + return None, {} + + disptype, *parts = header.split(";") + if not is_token(disptype): + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params: Dict[str, str] = {} + while parts: + item = parts.pop(0) + + if "=" not in item: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + key, value = item.split("=", 1) + key = key.lower().strip() + value = value.lstrip() + + if key in params: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + if not is_token(key): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_continuous_param(key): + if is_quoted(value): + value = unescape(value[1:-1]) + elif not is_token(value): + warnings.warn(BadContentDispositionParam(item)) + continue + + elif is_extended_param(key): + if is_rfc5987(value): + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + else: + warnings.warn(BadContentDispositionParam(item)) + continue + + try: + value = unquote(value, encoding, "strict") + except UnicodeDecodeError: # pragma: nocover + warnings.warn(BadContentDispositionParam(item)) + continue + + else: + failed = True + if is_quoted(value): + failed = False + value = unescape(value[1:-1].lstrip("\\/")) + elif is_token(value): + failed = False + elif parts: + # maybe just ; in filename, in any case this is just + # one case fix, for proper fix we need to redesign parser + _value = f"{value};{parts[0]}" + if is_quoted(_value): + parts.pop(0) + value = unescape(_value[1:-1].lstrip("\\/")) + failed = False + + if failed: + warnings.warn(BadContentDispositionHeader(header)) + return None, {} + + params[key] = value + + return disptype.lower(), params + + +def content_disposition_filename( + params: Mapping[str, str], name: str = "filename" +) -> Optional[str]: + name_suf = "%s*" % name + if not params: + return None + elif name_suf in params: + return params[name_suf] + elif name in params: + return params[name] + else: + parts = [] + fnparams = sorted( + (key, value) for key, value in params.items() if key.startswith(name_suf) + ) + for num, (key, value) in enumerate(fnparams): + _, tail = key.split("*", 1) + if tail.endswith("*"): + tail = tail[:-1] + if tail == str(num): + parts.append(value) + else: + break + if not parts: + return None + value = "".join(parts) + if "'" in value: + encoding, _, value = value.split("'", 2) + encoding = encoding or "utf-8" + return unquote(value, encoding, "strict") + return value + + +class MultipartResponseWrapper: + """Wrapper around the MultipartReader. + + It takes care about + underlying connection and close it when it needs in. + """ + + def __init__( + self, + resp: "ClientResponse", + stream: "MultipartReader", + ) -> None: + self.resp = resp + self.stream = stream + + def __aiter__(self) -> "MultipartResponseWrapper": + return self + + async def __anext__( + self, + ) -> Union["MultipartReader", "BodyPartReader"]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + def at_eof(self) -> bool: + """Returns True when all response data had been read.""" + return self.resp.content.at_eof() + + async def next( + self, + ) -> Optional[Union["MultipartReader", "BodyPartReader"]]: + """Emits next multipart reader object.""" + item = await self.stream.next() + if self.stream.at_eof(): + await self.release() + return item + + async def release(self) -> None: + """Release the connection gracefully. + + All remaining content is read to the void. + """ + await self.resp.release() + + +class BodyPartReader: + """Multipart reader for single body part.""" + + chunk_size = 8192 + + def __init__( + self, + boundary: bytes, + headers: "CIMultiDictProxy[str]", + content: StreamReader, + *, + subtype: str = "mixed", + default_charset: Optional[str] = None, + ) -> None: + self.headers = headers + self._boundary = boundary + self._content = content + self._default_charset = default_charset + self._at_eof = False + self._is_form_data = subtype == "form-data" + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None) + self._length = int(length) if length is not None else None + self._read_bytes = 0 + self._unread: Deque[bytes] = deque() + self._prev_chunk: Optional[bytes] = None + self._content_eof = 0 + self._cache: Dict[str, Any] = {} + + def __aiter__(self) -> AsyncIterator["BodyPartReader"]: + return self # type: ignore[return-value] + + async def __anext__(self) -> bytes: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + async def next(self) -> Optional[bytes]: + item = await self.read() + if not item: + return None + return item + + async def read(self, *, decode: bool = False) -> bytes: + """Reads body part data. + + decode: Decodes data following by encoding + method from Content-Encoding header. If it missed + data remains untouched + """ + if self._at_eof: + return b"" + data = bytearray() + while not self._at_eof: + data.extend(await self.read_chunk(self.chunk_size)) + if decode: + return self.decode(data) + return data + + async def read_chunk(self, size: int = chunk_size) -> bytes: + """Reads body part content chunk of the specified size. + + size: chunk size + """ + if self._at_eof: + return b"" + if self._length: + chunk = await self._read_chunk_from_length(size) + else: + chunk = await self._read_chunk_from_stream(size) + + self._read_bytes += len(chunk) + if self._read_bytes == self._length: + self._at_eof = True + if self._at_eof: + clrf = await self._content.readline() + assert ( + b"\r\n" == clrf + ), "reader did not read all the data or it is malformed" + return chunk + + async def _read_chunk_from_length(self, size: int) -> bytes: + # Reads body part content chunk of the specified size. + # The body part must has Content-Length header with proper value. + assert self._length is not None, "Content-Length required for chunked read" + chunk_size = min(size, self._length - self._read_bytes) + chunk = await self._content.read(chunk_size) + if self._content.at_eof(): + self._at_eof = True + return chunk + + async def _read_chunk_from_stream(self, size: int) -> bytes: + # Reads content chunk of body part with unknown length. + # The Content-Length header for body part is not necessary. + assert ( + size >= len(self._boundary) + 2 + ), "Chunk size must be greater or equal than boundary length + 2" + first_chunk = self._prev_chunk is None + if first_chunk: + self._prev_chunk = await self._content.read(size) + + chunk = await self._content.read(size) + self._content_eof += int(self._content.at_eof()) + assert self._content_eof < 3, "Reading after EOF" + assert self._prev_chunk is not None + window = self._prev_chunk + chunk + sub = b"\r\n" + self._boundary + if first_chunk: + idx = window.find(sub) + else: + idx = window.find(sub, max(0, len(self._prev_chunk) - len(sub))) + if idx >= 0: + # pushing boundary back to content + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + self._content.unread_data(window[idx:]) + if size > idx: + self._prev_chunk = self._prev_chunk[:idx] + chunk = window[len(self._prev_chunk) : idx] + if not chunk: + self._at_eof = True + result = self._prev_chunk + self._prev_chunk = chunk + return result + + async def readline(self) -> bytes: + """Reads body part by line by line.""" + if self._at_eof: + return b"" + + if self._unread: + line = self._unread.popleft() + else: + line = await self._content.readline() + + if line.startswith(self._boundary): + # the very last boundary may not come with \r\n, + # so set single rules for everyone + sline = line.rstrip(b"\r\n") + boundary = self._boundary + last_boundary = self._boundary + b"--" + # ensure that we read exactly the boundary, not something alike + if sline == boundary or sline == last_boundary: + self._at_eof = True + self._unread.append(line) + return b"" + else: + next_line = await self._content.readline() + if next_line.startswith(self._boundary): + line = line[:-2] # strip CRLF but only once + self._unread.append(next_line) + + return line + + async def release(self) -> None: + """Like read(), but reads all the data to the void.""" + if self._at_eof: + return + while not self._at_eof: + await self.read_chunk(self.chunk_size) + + async def text(self, *, encoding: Optional[str] = None) -> str: + """Like read(), but assumes that body part contains text data.""" + data = await self.read(decode=True) + # see https://www.w3.org/TR/html5/forms.html#multipart/form-data-encoding-algorithm + # and https://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-send + encoding = encoding or self.get_charset(default="utf-8") + return data.decode(encoding) + + async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, Any]]: + """Like read(), but assumes that body parts contains JSON data.""" + data = await self.read(decode=True) + if not data: + return None + encoding = encoding or self.get_charset(default="utf-8") + return cast(Dict[str, Any], json.loads(data.decode(encoding))) + + async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]: + """Like read(), but assumes that body parts contain form urlencoded data.""" + data = await self.read(decode=True) + if not data: + return [] + if encoding is not None: + real_encoding = encoding + else: + real_encoding = self.get_charset(default="utf-8") + try: + decoded_data = data.rstrip().decode(real_encoding) + except UnicodeDecodeError: + raise ValueError("data cannot be decoded with %s encoding" % real_encoding) + + return parse_qsl( + decoded_data, + keep_blank_values=True, + encoding=real_encoding, + ) + + def at_eof(self) -> bool: + """Returns True if the boundary was reached or False otherwise.""" + return self._at_eof + + def decode(self, data: bytes) -> bytes: + """Decodes data. + + Decoding is done according the specified Content-Encoding + or Content-Transfer-Encoding headers value. + """ + if CONTENT_TRANSFER_ENCODING in self.headers: + data = self._decode_content_transfer(data) + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + if not self._is_form_data and CONTENT_ENCODING in self.headers: + return self._decode_content(data) + return data + + def _decode_content(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_ENCODING, "").lower() + if encoding == "identity": + return data + if encoding in {"deflate", "gzip"}: + return ZLibDecompressor( + encoding=encoding, + suppress_deflate_header=True, + ).decompress_sync(data) + + raise RuntimeError(f"unknown content encoding: {encoding}") + + def _decode_content_transfer(self, data: bytes) -> bytes: + encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + + if encoding == "base64": + return base64.b64decode(data) + elif encoding == "quoted-printable": + return binascii.a2b_qp(data) + elif encoding in ("binary", "8bit", "7bit"): + return data + else: + raise RuntimeError( + "unknown content transfer encoding: {}" "".format(encoding) + ) + + def get_charset(self, default: str) -> str: + """Returns charset parameter from Content-Type header or default.""" + ctype = self.headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + return mimetype.parameters.get("charset", self._default_charset or default) + + @reify + def name(self) -> Optional[str]: + """Returns name specified in Content-Disposition header. + + If the header is missing or malformed, returns None. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "name") + + @reify + def filename(self) -> Optional[str]: + """Returns filename specified in Content-Disposition header. + + Returns None if the header is missing or malformed. + """ + _, params = parse_content_disposition(self.headers.get(CONTENT_DISPOSITION)) + return content_disposition_filename(params, "filename") + + +@payload_type(BodyPartReader, order=Order.try_first) +class BodyPartReaderPayload(Payload): + def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value, *args, **kwargs) + + params: Dict[str, str] = {} + if value.name is not None: + params["name"] = value.name + if value.filename is not None: + params["filename"] = value.filename + + if params: + self.set_content_disposition("attachment", True, **params) + + async def write(self, writer: Any) -> None: + field = self._value + chunk = await field.read_chunk(size=2**16) + while chunk: + await writer.write(field.decode(chunk)) + chunk = await field.read_chunk(size=2**16) + + +class MultipartReader: + """Multipart body reader.""" + + #: Response wrapper, used when multipart readers constructs from response. + response_wrapper_cls = MultipartResponseWrapper + #: Multipart reader class, used to handle multipart/* body parts. + #: None points to type(self) + multipart_reader_cls = None + #: Body part reader class for non multipart/* content types. + part_reader_cls = BodyPartReader + + def __init__(self, headers: Mapping[str, str], content: StreamReader) -> None: + self._mimetype = parse_mimetype(headers[CONTENT_TYPE]) + assert self._mimetype.type == "multipart", "multipart/* content type expected" + if "boundary" not in self._mimetype.parameters: + raise ValueError( + "boundary missed for Content-Type: %s" % headers[CONTENT_TYPE] + ) + + self.headers = headers + self._boundary = ("--" + self._get_boundary()).encode() + self._content = content + self._default_charset: Optional[str] = None + self._last_part: Optional[Union["MultipartReader", BodyPartReader]] = None + self._at_eof = False + self._at_bof = True + self._unread: List[bytes] = [] + + def __aiter__( + self, + ) -> AsyncIterator["BodyPartReader"]: + return self # type: ignore[return-value] + + async def __anext__( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + part = await self.next() + if part is None: + raise StopAsyncIteration + return part + + @classmethod + def from_response( + cls, + response: "ClientResponse", + ) -> MultipartResponseWrapper: + """Constructs reader instance from HTTP response. + + :param response: :class:`~aiohttp.client.ClientResponse` instance + """ + obj = cls.response_wrapper_cls( + response, cls(response.headers, response.content) + ) + return obj + + def at_eof(self) -> bool: + """Returns True if the final boundary was reached, false otherwise.""" + return self._at_eof + + async def next( + self, + ) -> Optional[Union["MultipartReader", BodyPartReader]]: + """Emits the next multipart body part.""" + # So, if we're at BOF, we need to skip till the boundary. + if self._at_eof: + return None + await self._maybe_release_last_part() + if self._at_bof: + await self._read_until_first_boundary() + self._at_bof = False + else: + await self._read_boundary() + if self._at_eof: # we just read the last boundary, nothing to do there + return None + + part = await self.fetch_next_part() + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.6 + if ( + self._last_part is None + and self._mimetype.subtype == "form-data" + and isinstance(part, BodyPartReader) + ): + _, params = parse_content_disposition(part.headers.get(CONTENT_DISPOSITION)) + if params.get("name") == "_charset_": + # Longest encoding in https://encoding.spec.whatwg.org/encodings.json + # is 19 characters, so 32 should be more than enough for any valid encoding. + charset = await part.read_chunk(32) + if len(charset) > 31: + raise RuntimeError("Invalid default charset") + self._default_charset = charset.strip().decode() + part = await self.fetch_next_part() + self._last_part = part + return self._last_part + + async def release(self) -> None: + """Reads all the body parts to the void till the final boundary.""" + while not self._at_eof: + item = await self.next() + if item is None: + break + await item.release() + + async def fetch_next_part( + self, + ) -> Union["MultipartReader", BodyPartReader]: + """Returns the next body part reader.""" + headers = await self._read_headers() + return self._get_part_reader(headers) + + def _get_part_reader( + self, + headers: "CIMultiDictProxy[str]", + ) -> Union["MultipartReader", BodyPartReader]: + """Dispatches the response by the `Content-Type` header. + + Returns a suitable reader instance. + + :param dict headers: Response headers + """ + ctype = headers.get(CONTENT_TYPE, "") + mimetype = parse_mimetype(ctype) + + if mimetype.type == "multipart": + if self.multipart_reader_cls is None: + return type(self)(headers, self._content) + return self.multipart_reader_cls(headers, self._content) + else: + return self.part_reader_cls( + self._boundary, + headers, + self._content, + subtype=self._mimetype.subtype, + default_charset=self._default_charset, + ) + + def _get_boundary(self) -> str: + boundary = self._mimetype.parameters["boundary"] + if len(boundary) > 70: + raise ValueError("boundary %r is too long (70 chars max)" % boundary) + + return boundary + + async def _readline(self) -> bytes: + if self._unread: + return self._unread.pop() + return await self._content.readline() + + async def _read_until_first_boundary(self) -> None: + while True: + chunk = await self._readline() + if chunk == b"": + raise ValueError( + "Could not find starting boundary %r" % (self._boundary) + ) + chunk = chunk.rstrip() + if chunk == self._boundary: + return + elif chunk == self._boundary + b"--": + self._at_eof = True + return + + async def _read_boundary(self) -> None: + chunk = (await self._readline()).rstrip() + if chunk == self._boundary: + pass + elif chunk == self._boundary + b"--": + self._at_eof = True + epilogue = await self._readline() + next_line = await self._readline() + + # the epilogue is expected and then either the end of input or the + # parent multipart boundary, if the parent boundary is found then + # it should be marked as unread and handed to the parent for + # processing + if next_line[:2] == b"--": + self._unread.append(next_line) + # otherwise the request is likely missing an epilogue and both + # lines should be passed to the parent for processing + # (this handles the old behavior gracefully) + else: + self._unread.extend([next_line, epilogue]) + else: + raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}") + + async def _read_headers(self) -> "CIMultiDictProxy[str]": + lines = [b""] + while True: + chunk = await self._content.readline() + chunk = chunk.strip() + lines.append(chunk) + if not chunk: + break + parser = HeadersParser() + headers, raw_headers = parser.parse_headers(lines) + return headers + + async def _maybe_release_last_part(self) -> None: + """Ensures that the last read body part is read completely.""" + if self._last_part is not None: + if not self._last_part.at_eof(): + await self._last_part.release() + self._unread.extend(self._last_part._unread) + self._last_part = None + + +_Part = Tuple[Payload, str, str] + + +class MultipartWriter(Payload): + """Multipart body writer.""" + + def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None: + boundary = boundary if boundary is not None else uuid.uuid4().hex + # The underlying Payload API demands a str (utf-8), not bytes, + # so we need to ensure we don't lose anything during conversion. + # As a result, require the boundary to be ASCII only. + # In both situations. + + try: + self._boundary = boundary.encode("ascii") + except UnicodeEncodeError: + raise ValueError("boundary should contain ASCII only chars") from None + ctype = f"multipart/{subtype}; boundary={self._boundary_value}" + + super().__init__(None, content_type=ctype) + + self._parts: List[_Part] = [] + self._is_form_data = subtype == "form-data" + + def __enter__(self) -> "MultipartWriter": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + pass + + def __iter__(self) -> Iterator[_Part]: + return iter(self._parts) + + def __len__(self) -> int: + return len(self._parts) + + def __bool__(self) -> bool: + return True + + _valid_tchar_regex = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z") + _invalid_qdtext_char_regex = re.compile(rb"[\x00-\x08\x0A-\x1F\x7F]") + + @property + def _boundary_value(self) -> str: + """Wrap boundary parameter value in quotes, if necessary. + + Reads self.boundary and returns a unicode string. + """ + # Refer to RFCs 7231, 7230, 5234. + # + # parameter = token "=" ( token / quoted-string ) + # token = 1*tchar + # quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + # qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text + # obs-text = %x80-FF + # quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" + # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" + # / DIGIT / ALPHA + # ; any VCHAR, except delimiters + # VCHAR = %x21-7E + value = self._boundary + if re.match(self._valid_tchar_regex, value): + return value.decode("ascii") # cannot fail + + if re.search(self._invalid_qdtext_char_regex, value): + raise ValueError("boundary value contains invalid characters") + + # escape %x5C and %x22 + quoted_value_content = value.replace(b"\\", b"\\\\") + quoted_value_content = quoted_value_content.replace(b'"', b'\\"') + + return '"' + quoted_value_content.decode("ascii") + '"' + + @property + def boundary(self) -> str: + return self._boundary.decode("ascii") + + def append(self, obj: Any, headers: Optional[Mapping[str, str]] = None) -> Payload: + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Payload): + obj.headers.update(headers) + return self.append_payload(obj) + else: + try: + payload = get_payload(obj, headers=headers) + except LookupError: + raise TypeError("Cannot create payload from %r" % obj) + else: + return self.append_payload(payload) + + def append_payload(self, payload: Payload) -> Payload: + """Adds a new body part to multipart writer.""" + encoding: Optional[str] = None + te_encoding: Optional[str] = None + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.7 + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.8 + assert ( + not {CONTENT_ENCODING, CONTENT_LENGTH, CONTENT_TRANSFER_ENCODING} + & payload.headers.keys() + ) + # Set default Content-Disposition in case user doesn't create one + if CONTENT_DISPOSITION not in payload.headers: + name = f"section-{len(self._parts)}" + payload.set_content_disposition("form-data", name=name) + else: + # compression + encoding = payload.headers.get(CONTENT_ENCODING, "").lower() + if encoding and encoding not in ("deflate", "gzip", "identity"): + raise RuntimeError(f"unknown content encoding: {encoding}") + if encoding == "identity": + encoding = None + + # te encoding + te_encoding = payload.headers.get(CONTENT_TRANSFER_ENCODING, "").lower() + if te_encoding not in ("", "base64", "quoted-printable", "binary"): + raise RuntimeError(f"unknown content transfer encoding: {te_encoding}") + if te_encoding == "binary": + te_encoding = None + + # size + size = payload.size + if size is not None and not (encoding or te_encoding): + payload.headers[CONTENT_LENGTH] = str(size) + + self._parts.append((payload, encoding, te_encoding)) # type: ignore[arg-type] + return payload + + def append_json( + self, obj: Any, headers: Optional[Mapping[str, str]] = None + ) -> Payload: + """Helper to append JSON part.""" + if headers is None: + headers = CIMultiDict() + + return self.append_payload(JsonPayload(obj, headers=headers)) + + def append_form( + self, + obj: Union[Sequence[Tuple[str, str]], Mapping[str, str]], + headers: Optional[Mapping[str, str]] = None, + ) -> Payload: + """Helper to append form urlencoded part.""" + assert isinstance(obj, (Sequence, Mapping)) + + if headers is None: + headers = CIMultiDict() + + if isinstance(obj, Mapping): + obj = list(obj.items()) + data = urlencode(obj, doseq=True) + + return self.append_payload( + StringPayload( + data, headers=headers, content_type="application/x-www-form-urlencoded" + ) + ) + + @property + def size(self) -> Optional[int]: + """Size of the payload.""" + total = 0 + for part, encoding, te_encoding in self._parts: + if encoding or te_encoding or part.size is None: + return None + + total += int( + 2 + + len(self._boundary) + + 2 + + part.size # b'--'+self._boundary+b'\r\n' + + len(part._binary_headers) + + 2 # b'\r\n' + ) + + total += 2 + len(self._boundary) + 4 # b'--'+self._boundary+b'--\r\n' + return total + + async def write(self, writer: Any, close_boundary: bool = True) -> None: + """Write body.""" + for part, encoding, te_encoding in self._parts: + if self._is_form_data: + # https://datatracker.ietf.org/doc/html/rfc7578#section-4.2 + assert CONTENT_DISPOSITION in part.headers + assert "name=" in part.headers[CONTENT_DISPOSITION] + + await writer.write(b"--" + self._boundary + b"\r\n") + await writer.write(part._binary_headers) + + if encoding or te_encoding: + w = MultipartPayloadWriter(writer) + if encoding: + w.enable_compression(encoding) + if te_encoding: + w.enable_encoding(te_encoding) + await part.write(w) # type: ignore[arg-type] + await w.write_eof() + else: + await part.write(writer) + + await writer.write(b"\r\n") + + if close_boundary: + await writer.write(b"--" + self._boundary + b"--\r\n") + + +class MultipartPayloadWriter: + def __init__(self, writer: Any) -> None: + self._writer = writer + self._encoding: Optional[str] = None + self._compress: Optional[ZLibCompressor] = None + self._encoding_buffer: Optional[bytearray] = None + + def enable_encoding(self, encoding: str) -> None: + if encoding == "base64": + self._encoding = encoding + self._encoding_buffer = bytearray() + elif encoding == "quoted-printable": + self._encoding = "quoted-printable" + + def enable_compression( + self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY + ) -> None: + self._compress = ZLibCompressor( + encoding=encoding, + suppress_deflate_header=True, + strategy=strategy, + ) + + async def write_eof(self) -> None: + if self._compress is not None: + chunk = self._compress.flush() + if chunk: + self._compress = None + await self.write(chunk) + + if self._encoding == "base64": + if self._encoding_buffer: + await self._writer.write(base64.b64encode(self._encoding_buffer)) + + async def write(self, chunk: bytes) -> None: + if self._compress is not None: + if chunk: + chunk = await self._compress.compress(chunk) + if not chunk: + return + + if self._encoding == "base64": + buf = self._encoding_buffer + assert buf is not None + buf.extend(chunk) + + if buf: + div, mod = divmod(len(buf), 3) + enc_chunk, self._encoding_buffer = (buf[: div * 3], buf[div * 3 :]) + if enc_chunk: + b64chunk = base64.b64encode(enc_chunk) + await self._writer.write(b64chunk) + elif self._encoding == "quoted-printable": + await self._writer.write(binascii.b2a_qp(chunk)) + else: + await self._writer.write(chunk) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload.py new file mode 100644 index 0000000..6593b05 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload.py @@ -0,0 +1,463 @@ +import asyncio +import enum +import io +import json +import mimetypes +import os +import warnings +from abc import ABC, abstractmethod +from itertools import chain +from typing import ( + IO, + TYPE_CHECKING, + Any, + ByteString, + Dict, + Final, + Iterable, + Optional, + TextIO, + Tuple, + Type, + Union, +) + +from multidict import CIMultiDict + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ( + _SENTINEL, + content_disposition_header, + guess_filename, + parse_mimetype, + sentinel, +) +from .streams import StreamReader +from .typedefs import JSONEncoder, _CIMultiDict + +__all__ = ( + "PAYLOAD_REGISTRY", + "get_payload", + "payload_type", + "Payload", + "BytesPayload", + "StringPayload", + "IOBasePayload", + "BytesIOPayload", + "BufferedReaderPayload", + "TextIOPayload", + "StringIOPayload", + "JsonPayload", + "AsyncIterablePayload", +) + +TOO_LARGE_BYTES_BODY: Final[int] = 2**20 # 1 MB + +if TYPE_CHECKING: + from typing import List + + +class LookupError(Exception): + pass + + +class Order(str, enum.Enum): + normal = "normal" + try_first = "try_first" + try_last = "try_last" + + +def get_payload(data: Any, *args: Any, **kwargs: Any) -> "Payload": + return PAYLOAD_REGISTRY.get(data, *args, **kwargs) + + +def register_payload( + factory: Type["Payload"], type: Any, *, order: Order = Order.normal +) -> None: + PAYLOAD_REGISTRY.register(factory, type, order=order) + + +class payload_type: + def __init__(self, type: Any, *, order: Order = Order.normal) -> None: + self.type = type + self.order = order + + def __call__(self, factory: Type["Payload"]) -> Type["Payload"]: + register_payload(factory, self.type, order=self.order) + return factory + + +PayloadType = Type["Payload"] +_PayloadRegistryItem = Tuple[PayloadType, Any] + + +class PayloadRegistry: + """Payload registry. + + note: we need zope.interface for more efficient adapter search + """ + + def __init__(self) -> None: + self._first: List[_PayloadRegistryItem] = [] + self._normal: List[_PayloadRegistryItem] = [] + self._last: List[_PayloadRegistryItem] = [] + + def get( + self, + data: Any, + *args: Any, + _CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain, + **kwargs: Any, + ) -> "Payload": + if isinstance(data, Payload): + return data + for factory, type in _CHAIN(self._first, self._normal, self._last): + if isinstance(data, type): + return factory(data, *args, **kwargs) + + raise LookupError() + + def register( + self, factory: PayloadType, type: Any, *, order: Order = Order.normal + ) -> None: + if order is Order.try_first: + self._first.append((factory, type)) + elif order is Order.normal: + self._normal.append((factory, type)) + elif order is Order.try_last: + self._last.append((factory, type)) + else: + raise ValueError(f"Unsupported order {order!r}") + + +class Payload(ABC): + + _default_content_type: str = "application/octet-stream" + _size: Optional[int] = None + + def __init__( + self, + value: Any, + headers: Optional[ + Union[_CIMultiDict, Dict[str, str], Iterable[Tuple[str, str]]] + ] = None, + content_type: Union[str, None, _SENTINEL] = sentinel, + filename: Optional[str] = None, + encoding: Optional[str] = None, + **kwargs: Any, + ) -> None: + self._encoding = encoding + self._filename = filename + self._headers: _CIMultiDict = CIMultiDict() + self._value = value + if content_type is not sentinel and content_type is not None: + self._headers[hdrs.CONTENT_TYPE] = content_type + elif self._filename is not None: + content_type = mimetypes.guess_type(self._filename)[0] + if content_type is None: + content_type = self._default_content_type + self._headers[hdrs.CONTENT_TYPE] = content_type + else: + self._headers[hdrs.CONTENT_TYPE] = self._default_content_type + self._headers.update(headers or {}) + + @property + def size(self) -> Optional[int]: + """Size of the payload.""" + return self._size + + @property + def filename(self) -> Optional[str]: + """Filename of the payload.""" + return self._filename + + @property + def headers(self) -> _CIMultiDict: + """Custom item headers""" + return self._headers + + @property + def _binary_headers(self) -> bytes: + return ( + "".join([k + ": " + v + "\r\n" for k, v in self.headers.items()]).encode( + "utf-8" + ) + + b"\r\n" + ) + + @property + def encoding(self) -> Optional[str]: + """Payload encoding""" + return self._encoding + + @property + def content_type(self) -> str: + """Content type""" + return self._headers[hdrs.CONTENT_TYPE] + + def set_content_disposition( + self, + disptype: str, + quote_fields: bool = True, + _charset: str = "utf-8", + **params: Any, + ) -> None: + """Sets ``Content-Disposition`` header.""" + self._headers[hdrs.CONTENT_DISPOSITION] = content_disposition_header( + disptype, quote_fields=quote_fields, _charset=_charset, **params + ) + + @abstractmethod + async def write(self, writer: AbstractStreamWriter) -> None: + """Write payload. + + writer is an AbstractStreamWriter instance: + """ + + +class BytesPayload(Payload): + def __init__(self, value: ByteString, *args: Any, **kwargs: Any) -> None: + if not isinstance(value, (bytes, bytearray, memoryview)): + raise TypeError(f"value argument must be byte-ish, not {type(value)!r}") + + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + if isinstance(value, memoryview): + self._size = value.nbytes + else: + self._size = len(value) + + if self._size > TOO_LARGE_BYTES_BODY: + kwargs = {"source": self} + warnings.warn( + "Sending a large body directly with raw bytes might" + " lock the event loop. You should probably pass an " + "io.BytesIO object instead", + ResourceWarning, + **kwargs, + ) + + async def write(self, writer: AbstractStreamWriter) -> None: + await writer.write(self._value) + + +class StringPayload(BytesPayload): + def __init__( + self, + value: str, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + real_encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + real_encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + real_encoding = encoding + + super().__init__( + value.encode(real_encoding), + encoding=real_encoding, + content_type=content_type, + *args, + **kwargs, + ) + + +class StringIOPayload(StringPayload): + def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: + super().__init__(value.read(), *args, **kwargs) + + +class IOBasePayload(Payload): + _value: IO[Any] + + def __init__( + self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any + ) -> None: + if "filename" not in kwargs: + kwargs["filename"] = guess_filename(value) + + super().__init__(value, *args, **kwargs) + + if self._filename is not None and disposition is not None: + if hdrs.CONTENT_DISPOSITION not in self.headers: + self.set_content_disposition(disposition, filename=self._filename) + + async def write(self, writer: AbstractStreamWriter) -> None: + loop = asyncio.get_event_loop() + try: + chunk = await loop.run_in_executor(None, self._value.read, 2**16) + while chunk: + await writer.write(chunk) + chunk = await loop.run_in_executor(None, self._value.read, 2**16) + finally: + await loop.run_in_executor(None, self._value.close) + + +class TextIOPayload(IOBasePayload): + _value: TextIO + + def __init__( + self, + value: TextIO, + *args: Any, + encoding: Optional[str] = None, + content_type: Optional[str] = None, + **kwargs: Any, + ) -> None: + + if encoding is None: + if content_type is None: + encoding = "utf-8" + content_type = "text/plain; charset=utf-8" + else: + mimetype = parse_mimetype(content_type) + encoding = mimetype.parameters.get("charset", "utf-8") + else: + if content_type is None: + content_type = "text/plain; charset=%s" % encoding + + super().__init__( + value, + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + @property + def size(self) -> Optional[int]: + try: + return os.fstat(self._value.fileno()).st_size - self._value.tell() + except OSError: + return None + + async def write(self, writer: AbstractStreamWriter) -> None: + loop = asyncio.get_event_loop() + try: + chunk = await loop.run_in_executor(None, self._value.read, 2**16) + while chunk: + data = ( + chunk.encode(encoding=self._encoding) + if self._encoding + else chunk.encode() + ) + await writer.write(data) + chunk = await loop.run_in_executor(None, self._value.read, 2**16) + finally: + await loop.run_in_executor(None, self._value.close) + + +class BytesIOPayload(IOBasePayload): + @property + def size(self) -> int: + position = self._value.tell() + end = self._value.seek(0, os.SEEK_END) + self._value.seek(position) + return end - position + + +class BufferedReaderPayload(IOBasePayload): + @property + def size(self) -> Optional[int]: + try: + return os.fstat(self._value.fileno()).st_size - self._value.tell() + except OSError: + # data.fileno() is not supported, e.g. + # io.BufferedReader(io.BytesIO(b'data')) + return None + + +class JsonPayload(BytesPayload): + def __init__( + self, + value: Any, + encoding: str = "utf-8", + content_type: str = "application/json", + dumps: JSONEncoder = json.dumps, + *args: Any, + **kwargs: Any, + ) -> None: + + super().__init__( + dumps(value).encode(encoding), + content_type=content_type, + encoding=encoding, + *args, + **kwargs, + ) + + +if TYPE_CHECKING: + from typing import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator[bytes] + _AsyncIterable = AsyncIterable[bytes] +else: + from collections.abc import AsyncIterable, AsyncIterator + + _AsyncIterator = AsyncIterator + _AsyncIterable = AsyncIterable + + +class AsyncIterablePayload(Payload): + + _iter: Optional[_AsyncIterator] = None + + def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: + if not isinstance(value, AsyncIterable): + raise TypeError( + "value argument must support " + "collections.abc.AsyncIterable interface, " + "got {!r}".format(type(value)) + ) + + if "content_type" not in kwargs: + kwargs["content_type"] = "application/octet-stream" + + super().__init__(value, *args, **kwargs) + + self._iter = value.__aiter__() + + async def write(self, writer: AbstractStreamWriter) -> None: + if self._iter: + try: + # iter is not None check prevents rare cases + # when the case iterable is used twice + while True: + chunk = await self._iter.__anext__() + await writer.write(chunk) + except StopAsyncIteration: + self._iter = None + + +class StreamReaderPayload(AsyncIterablePayload): + def __init__(self, value: StreamReader, *args: Any, **kwargs: Any) -> None: + super().__init__(value.iter_any(), *args, **kwargs) + + +PAYLOAD_REGISTRY = PayloadRegistry() +PAYLOAD_REGISTRY.register(BytesPayload, (bytes, bytearray, memoryview)) +PAYLOAD_REGISTRY.register(StringPayload, str) +PAYLOAD_REGISTRY.register(StringIOPayload, io.StringIO) +PAYLOAD_REGISTRY.register(TextIOPayload, io.TextIOBase) +PAYLOAD_REGISTRY.register(BytesIOPayload, io.BytesIO) +PAYLOAD_REGISTRY.register(BufferedReaderPayload, (io.BufferedReader, io.BufferedRandom)) +PAYLOAD_REGISTRY.register(IOBasePayload, io.IOBase) +PAYLOAD_REGISTRY.register(StreamReaderPayload, StreamReader) +# try_last for giving a chance to more specialized async interables like +# multidict.BodyPartReaderPayload override the default +PAYLOAD_REGISTRY.register(AsyncIterablePayload, AsyncIterable, order=Order.try_last) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload_streamer.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload_streamer.py new file mode 100644 index 0000000..364f763 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/payload_streamer.py @@ -0,0 +1,75 @@ +""" +Payload implementation for coroutines as data provider. + +As a simple case, you can upload data from file:: + + @aiohttp.streamer + async def file_sender(writer, file_name=None): + with open(file_name, 'rb') as f: + chunk = f.read(2**16) + while chunk: + await writer.write(chunk) + + chunk = f.read(2**16) + +Then you can use `file_sender` like this: + + async with session.post('http://httpbin.org/post', + data=file_sender(file_name='huge_file')) as resp: + print(await resp.text()) + +..note:: Coroutine must accept `writer` as first argument + +""" + +import types +import warnings +from typing import Any, Awaitable, Callable, Dict, Tuple + +from .abc import AbstractStreamWriter +from .payload import Payload, payload_type + +__all__ = ("streamer",) + + +class _stream_wrapper: + def __init__( + self, + coro: Callable[..., Awaitable[None]], + args: Tuple[Any, ...], + kwargs: Dict[str, Any], + ) -> None: + self.coro = types.coroutine(coro) + self.args = args + self.kwargs = kwargs + + async def __call__(self, writer: AbstractStreamWriter) -> None: + await self.coro(writer, *self.args, **self.kwargs) + + +class streamer: + def __init__(self, coro: Callable[..., Awaitable[None]]) -> None: + warnings.warn( + "@streamer is deprecated, use async generators instead", + DeprecationWarning, + stacklevel=2, + ) + self.coro = coro + + def __call__(self, *args: Any, **kwargs: Any) -> _stream_wrapper: + return _stream_wrapper(self.coro, args, kwargs) + + +@payload_type(_stream_wrapper) +class StreamWrapperPayload(Payload): + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) + + +@payload_type(streamer) +class StreamPayload(StreamWrapperPayload): + def __init__(self, value: Any, *args: Any, **kwargs: Any) -> None: + super().__init__(value(), *args, **kwargs) + + async def write(self, writer: AbstractStreamWriter) -> None: + await self._value(writer) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/py.typed b/.pythonlibs/lib/python3.10/site-packages/aiohttp/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/pytest_plugin.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/pytest_plugin.py new file mode 100644 index 0000000..5754747 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/pytest_plugin.py @@ -0,0 +1,381 @@ +import asyncio +import contextlib +import warnings +from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Type, Union + +import pytest + +from aiohttp.helpers import isasyncgenfunction +from aiohttp.web import Application + +from .test_utils import ( + BaseTestServer, + RawTestServer, + TestClient, + TestServer, + loop_context, + setup_test_loop, + teardown_test_loop, + unused_port as _unused_port, +) + +try: + import uvloop +except ImportError: # pragma: no cover + uvloop = None # type: ignore[assignment] + +AiohttpClient = Callable[[Union[Application, BaseTestServer]], Awaitable[TestClient]] +AiohttpRawServer = Callable[[Application], Awaitable[RawTestServer]] +AiohttpServer = Callable[[Application], Awaitable[TestServer]] + + +def pytest_addoption(parser): # type: ignore[no-untyped-def] + parser.addoption( + "--aiohttp-fast", + action="store_true", + default=False, + help="run tests faster by disabling extra checks", + ) + parser.addoption( + "--aiohttp-loop", + action="store", + default="pyloop", + help="run tests with specific loop: pyloop, uvloop or all", + ) + parser.addoption( + "--aiohttp-enable-loop-debug", + action="store_true", + default=False, + help="enable event loop debug mode", + ) + + +def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def] + """Set up pytest fixture. + + Allow fixtures to be coroutines. Run coroutine fixtures in an event loop. + """ + func = fixturedef.func + + if isasyncgenfunction(func): + # async generator fixture + is_async_gen = True + elif asyncio.iscoroutinefunction(func): + # regular async fixture + is_async_gen = False + else: + # not an async fixture, nothing to do + return + + strip_request = False + if "request" not in fixturedef.argnames: + fixturedef.argnames += ("request",) + strip_request = True + + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] + request = kwargs["request"] + if strip_request: + del kwargs["request"] + + # if neither the fixture nor the test use the 'loop' fixture, + # 'getfixturevalue' will fail because the test is not parameterized + # (this can be removed someday if 'loop' is no longer parameterized) + if "loop" not in request.fixturenames: + raise Exception( + "Asynchronous fixtures must depend on the 'loop' fixture or " + "be used in tests depending from it." + ) + + _loop = request.getfixturevalue("loop") + + if is_async_gen: + # for async generators, we need to advance the generator once, + # then advance it again in a finalizer + gen = func(*args, **kwargs) + + def finalizer(): # type: ignore[no-untyped-def] + try: + return _loop.run_until_complete(gen.__anext__()) + except StopAsyncIteration: + pass + + request.addfinalizer(finalizer) + return _loop.run_until_complete(gen.__anext__()) + else: + return _loop.run_until_complete(func(*args, **kwargs)) + + fixturedef.func = wrapper + + +@pytest.fixture +def fast(request): # type: ignore[no-untyped-def] + """--fast config option""" + return request.config.getoption("--aiohttp-fast") + + +@pytest.fixture +def loop_debug(request): # type: ignore[no-untyped-def] + """--enable-loop-debug config option""" + return request.config.getoption("--aiohttp-enable-loop-debug") + + +@contextlib.contextmanager +def _runtime_warning_context(): # type: ignore[no-untyped-def] + """Context manager which checks for RuntimeWarnings. + + This exists specifically to + avoid "coroutine 'X' was never awaited" warnings being missed. + + If RuntimeWarnings occur in the context a RuntimeError is raised. + """ + with warnings.catch_warnings(record=True) as _warnings: + yield + rw = [ + "{w.filename}:{w.lineno}:{w.message}".format(w=w) + for w in _warnings + if w.category == RuntimeWarning + ] + if rw: + raise RuntimeError( + "{} Runtime Warning{},\n{}".format( + len(rw), "" if len(rw) == 1 else "s", "\n".join(rw) + ) + ) + + +@contextlib.contextmanager +def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def] + """Passthrough loop context. + + Sets up and tears down a loop unless one is passed in via the loop + argument when it's passed straight through. + """ + if loop: + # loop already exists, pass it straight through + yield loop + else: + # this shadows loop_context's standard behavior + loop = setup_test_loop() + yield loop + teardown_test_loop(loop, fast=fast) + + +def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def] + """Fix pytest collecting for coroutines.""" + if collector.funcnamefilter(name) and asyncio.iscoroutinefunction(obj): + return list(collector._genfunctions(name, obj)) + + +def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def] + """Run coroutines in an event loop instead of a normal function call.""" + fast = pyfuncitem.config.getoption("--aiohttp-fast") + if asyncio.iscoroutinefunction(pyfuncitem.function): + existing_loop = pyfuncitem.funcargs.get( + "proactor_loop" + ) or pyfuncitem.funcargs.get("loop", None) + with _runtime_warning_context(): + with _passthrough_loop_context(existing_loop, fast=fast) as _loop: + testargs = { + arg: pyfuncitem.funcargs[arg] + for arg in pyfuncitem._fixtureinfo.argnames + } + _loop.run_until_complete(pyfuncitem.obj(**testargs)) + + return True + + +def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def] + if "loop_factory" not in metafunc.fixturenames: + return + + loops = metafunc.config.option.aiohttp_loop + avail_factories: Dict[str, Type[asyncio.AbstractEventLoopPolicy]] + avail_factories = {"pyloop": asyncio.DefaultEventLoopPolicy} + + if uvloop is not None: # pragma: no cover + avail_factories["uvloop"] = uvloop.EventLoopPolicy + + if loops == "all": + loops = "pyloop,uvloop?" + + factories = {} # type: ignore[var-annotated] + for name in loops.split(","): + required = not name.endswith("?") + name = name.strip(" ?") + if name not in avail_factories: # pragma: no cover + if required: + raise ValueError( + "Unknown loop '%s', available loops: %s" + % (name, list(factories.keys())) + ) + else: + continue + factories[name] = avail_factories[name] + metafunc.parametrize( + "loop_factory", list(factories.values()), ids=list(factories.keys()) + ) + + +@pytest.fixture +def loop(loop_factory, fast, loop_debug): # type: ignore[no-untyped-def] + """Return an instance of the event loop.""" + policy = loop_factory() + asyncio.set_event_loop_policy(policy) + with loop_context(fast=fast) as _loop: + if loop_debug: + _loop.set_debug(True) # pragma: no cover + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def proactor_loop(): # type: ignore[no-untyped-def] + policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore[attr-defined] + asyncio.set_event_loop_policy(policy) + + with loop_context(policy.new_event_loop) as _loop: + asyncio.set_event_loop(_loop) + yield _loop + + +@pytest.fixture +def unused_port(aiohttp_unused_port: Callable[[], int]) -> Callable[[], int]: + warnings.warn( + "Deprecated, use aiohttp_unused_port fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_unused_port + + +@pytest.fixture +def aiohttp_unused_port() -> Callable[[], int]: + """Return a port that is unused on the current host.""" + return _unused_port + + +@pytest.fixture +def aiohttp_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpServer]: + """Factory to create a TestServer instance, given an app. + + aiohttp_server(app, **kwargs) + """ + servers = [] + + async def go(app, *, port=None, **kwargs): # type: ignore[no-untyped-def] + server = TestServer(app, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_server(aiohttp_server): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_server + + +@pytest.fixture +def aiohttp_raw_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpRawServer]: + """Factory to create a RawTestServer instance, given a web handler. + + aiohttp_raw_server(handler, **kwargs) + """ + servers = [] + + async def go(handler, *, port=None, **kwargs): # type: ignore[no-untyped-def] + server = RawTestServer(handler, port=port) + await server.start_server(loop=loop, **kwargs) + servers.append(server) + return server + + yield go + + async def finalize() -> None: + while servers: + await servers.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def raw_test_server( # type: ignore[no-untyped-def] # pragma: no cover + aiohttp_raw_server, +): + warnings.warn( + "Deprecated, use aiohttp_raw_server fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_raw_server + + +@pytest.fixture +def aiohttp_client( + loop: asyncio.AbstractEventLoop, +) -> Iterator[AiohttpClient]: + """Factory to create a TestClient instance. + + aiohttp_client(app, **kwargs) + aiohttp_client(server, **kwargs) + aiohttp_client(raw_server, **kwargs) + """ + clients = [] + + async def go( + __param: Union[Application, BaseTestServer], + *args: Any, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any + ) -> TestClient: + + if isinstance(__param, Callable) and not isinstance( # type: ignore[arg-type] + __param, (Application, BaseTestServer) + ): + __param = __param(loop, *args, **kwargs) + kwargs = {} + else: + assert not args, "args should be empty" + + if isinstance(__param, Application): + server_kwargs = server_kwargs or {} + server = TestServer(__param, loop=loop, **server_kwargs) + client = TestClient(server, loop=loop, **kwargs) + elif isinstance(__param, BaseTestServer): + client = TestClient(__param, loop=loop, **kwargs) + else: + raise ValueError("Unknown argument type: %r" % type(__param)) + + await client.start_server() + clients.append(client) + return client + + yield go + + async def finalize() -> None: + while clients: + await clients.pop().close() + + loop.run_until_complete(finalize()) + + +@pytest.fixture +def test_client(aiohttp_client): # type: ignore[no-untyped-def] # pragma: no cover + warnings.warn( + "Deprecated, use aiohttp_client fixture instead", + DeprecationWarning, + stacklevel=2, + ) + return aiohttp_client diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/resolver.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/resolver.py new file mode 100644 index 0000000..6c17b1e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/resolver.py @@ -0,0 +1,160 @@ +import asyncio +import socket +from typing import Any, Dict, List, Optional, Type, Union + +from .abc import AbstractResolver +from .helpers import get_running_loop + +__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver") + +try: + import aiodns + + # aiodns_default = hasattr(aiodns.DNSResolver, 'gethostbyname') +except ImportError: # pragma: no cover + aiodns = None + +aiodns_default = False + + +class ThreadedResolver(AbstractResolver): + """Threaded resolver. + + Uses an Executor for synchronous getaddrinfo() calls. + concurrent.futures.ThreadPoolExecutor is used by default. + """ + + def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + self._loop = get_running_loop(loop) + + async def resolve( + self, hostname: str, port: int = 0, family: int = socket.AF_INET + ) -> List[Dict[str, Any]]: + infos = await self._loop.getaddrinfo( + hostname, + port, + type=socket.SOCK_STREAM, + family=family, + flags=socket.AI_ADDRCONFIG, + ) + + hosts = [] + for family, _, proto, _, address in infos: + if family == socket.AF_INET6: + if len(address) < 3: + # IPv6 is not supported by Python build, + # or IPv6 is not enabled in the host + continue + if address[3]: + # This is essential for link-local IPv6 addresses. + # LL IPv6 is a VERY rare case. Strictly speaking, we should use + # getnameinfo() unconditionally, but performance makes sense. + host, _port = socket.getnameinfo( + address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV + ) + port = int(_port) + else: + host, port = address[:2] + else: # IPv4 + assert family == socket.AF_INET + host, port = address # type: ignore[misc] + hosts.append( + { + "hostname": hostname, + "host": host, + "port": port, + "family": family, + "proto": proto, + "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, + } + ) + + return hosts + + async def close(self) -> None: + pass + + +class AsyncResolver(AbstractResolver): + """Use the `aiodns` package to make asynchronous DNS lookups""" + + def __init__( + self, + loop: Optional[asyncio.AbstractEventLoop] = None, + *args: Any, + **kwargs: Any + ) -> None: + if aiodns is None: + raise RuntimeError("Resolver requires aiodns library") + + self._loop = get_running_loop(loop) + self._resolver = aiodns.DNSResolver(*args, loop=loop, **kwargs) + + if not hasattr(self._resolver, "gethostbyname"): + # aiodns 1.1 is not available, fallback to DNSResolver.query + self.resolve = self._resolve_with_query # type: ignore + + async def resolve( + self, host: str, port: int = 0, family: int = socket.AF_INET + ) -> List[Dict[str, Any]]: + try: + resp = await self._resolver.gethostbyname(host, family) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(msg) from exc + hosts = [] + for address in resp.addresses: + hosts.append( + { + "hostname": host, + "host": address, + "port": port, + "family": family, + "proto": 0, + "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, + } + ) + + if not hosts: + raise OSError("DNS lookup failed") + + return hosts + + async def _resolve_with_query( + self, host: str, port: int = 0, family: int = socket.AF_INET + ) -> List[Dict[str, Any]]: + if family == socket.AF_INET6: + qtype = "AAAA" + else: + qtype = "A" + + try: + resp = await self._resolver.query(host, qtype) + except aiodns.error.DNSError as exc: + msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed" + raise OSError(msg) from exc + + hosts = [] + for rr in resp: + hosts.append( + { + "hostname": host, + "host": rr.host, + "port": port, + "family": family, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + ) + + if not hosts: + raise OSError("DNS lookup failed") + + return hosts + + async def close(self) -> None: + self._resolver.cancel() + + +_DefaultType = Type[Union[AsyncResolver, ThreadedResolver]] +DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/streams.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/streams.py new file mode 100644 index 0000000..b9b9c3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/streams.py @@ -0,0 +1,684 @@ +import asyncio +import collections +import warnings +from typing import ( + Awaitable, + Callable, + Deque, + Final, + Generic, + List, + Optional, + Tuple, + TypeVar, +) + +from .base_protocol import BaseProtocol +from .helpers import ( + _EXC_SENTINEL, + BaseTimerContext, + TimerNoop, + set_exception, + set_result, +) +from .log import internal_logger + +__all__ = ( + "EMPTY_PAYLOAD", + "EofStream", + "StreamReader", + "DataQueue", + "FlowControlDataQueue", +) + +_T = TypeVar("_T") + + +class EofStream(Exception): + """eof stream indication.""" + + +class AsyncStreamIterator(Generic[_T]): + def __init__(self, read_func: Callable[[], Awaitable[_T]]) -> None: + self.read_func = read_func + + def __aiter__(self) -> "AsyncStreamIterator[_T]": + return self + + async def __anext__(self) -> _T: + try: + rv = await self.read_func() + except EofStream: + raise StopAsyncIteration + if rv == b"": + raise StopAsyncIteration + return rv + + +class ChunkTupleAsyncStreamIterator: + def __init__(self, stream: "StreamReader") -> None: + self._stream = stream + + def __aiter__(self) -> "ChunkTupleAsyncStreamIterator": + return self + + async def __anext__(self) -> Tuple[bytes, bool]: + rv = await self._stream.readchunk() + if rv == (b"", False): + raise StopAsyncIteration + return rv + + +class AsyncStreamReaderMixin: + def __aiter__(self) -> AsyncStreamIterator[bytes]: + return AsyncStreamIterator(self.readline) # type: ignore[attr-defined] + + def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]: + """Returns an asynchronous iterator that yields chunks of size n.""" + return AsyncStreamIterator(lambda: self.read(n)) # type: ignore[attr-defined] + + def iter_any(self) -> AsyncStreamIterator[bytes]: + """Yield all available data as soon as it is received.""" + return AsyncStreamIterator(self.readany) # type: ignore[attr-defined] + + def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: + """Yield chunks of data as they are received by the server. + + The yielded objects are tuples + of (bytes, bool) as returned by the StreamReader.readchunk method. + """ + return ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type] + + +class StreamReader(AsyncStreamReaderMixin): + """An enhancement of asyncio.StreamReader. + + Supports asynchronous iteration by line, chunk or as available:: + + async for line in reader: + ... + async for chunk in reader.iter_chunked(1024): + ... + async for slice in reader.iter_any(): + ... + + """ + + total_bytes = 0 + + def __init__( + self, + protocol: BaseProtocol, + limit: int, + *, + timer: Optional[BaseTimerContext] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + self._protocol = protocol + self._low_water = limit + self._high_water = limit * 2 + if loop is None: + loop = asyncio.get_event_loop() + self._loop = loop + self._size = 0 + self._cursor = 0 + self._http_chunk_splits: Optional[List[int]] = None + self._buffer: Deque[bytes] = collections.deque() + self._buffer_offset = 0 + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._eof_waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._timer = TimerNoop() if timer is None else timer + self._eof_callbacks: List[Callable[[], None]] = [] + + def __repr__(self) -> str: + info = [self.__class__.__name__] + if self._size: + info.append("%d bytes" % self._size) + if self._eof: + info.append("eof") + if self._low_water != 2**16: # default limit + info.append("low=%d high=%d" % (self._low_water, self._high_water)) + if self._waiter: + info.append("w=%r" % self._waiter) + if self._exception: + info.append("e=%r" % self._exception) + return "<%s>" % " ".join(info) + + def get_read_buffer_limits(self) -> Tuple[int, int]: + return (self._low_water, self._high_water) + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._exception = exc + self._eof_callbacks.clear() + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_exception(waiter, exc, exc_cause) + + def on_eof(self, callback: Callable[[], None]) -> None: + if self._eof: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + else: + self._eof_callbacks.append(callback) + + def feed_eof(self) -> None: + self._eof = True + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + waiter = self._eof_waiter + if waiter is not None: + self._eof_waiter = None + set_result(waiter, None) + + for cb in self._eof_callbacks: + try: + cb() + except Exception: + internal_logger.exception("Exception in eof callback") + + self._eof_callbacks.clear() + + def is_eof(self) -> bool: + """Return True if 'feed_eof' was called.""" + return self._eof + + def at_eof(self) -> bool: + """Return True if the buffer is empty and 'feed_eof' was called.""" + return self._eof and not self._buffer + + async def wait_eof(self) -> None: + if self._eof: + return + + assert self._eof_waiter is None + self._eof_waiter = self._loop.create_future() + try: + await self._eof_waiter + finally: + self._eof_waiter = None + + def unread_data(self, data: bytes) -> None: + """rollback reading some data from stream, inserting it to buffer head.""" + warnings.warn( + "unread_data() is deprecated " + "and will be removed in future releases (#3260)", + DeprecationWarning, + stacklevel=2, + ) + if not data: + return + + if self._buffer_offset: + self._buffer[0] = self._buffer[0][self._buffer_offset :] + self._buffer_offset = 0 + self._size += len(data) + self._cursor -= len(data) + self._buffer.appendleft(data) + self._eof_counter = 0 + + # TODO: size is ignored, remove the param later + def feed_data(self, data: bytes, size: int = 0) -> None: + assert not self._eof, "feed_data after feed_eof" + + if not data: + return + + self._size += len(data) + self._buffer.append(data) + self.total_bytes += len(data) + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + if self._size > self._high_water and not self._protocol._reading_paused: + self._protocol.pause_reading() + + def begin_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + if self.total_bytes: + raise RuntimeError( + "Called begin_http_chunk_receiving when" "some data was already fed" + ) + self._http_chunk_splits = [] + + def end_http_chunk_receiving(self) -> None: + if self._http_chunk_splits is None: + raise RuntimeError( + "Called end_chunk_receiving without calling " + "begin_chunk_receiving first" + ) + + # self._http_chunk_splits contains logical byte offsets from start of + # the body transfer. Each offset is the offset of the end of a chunk. + # "Logical" means bytes, accessible for a user. + # If no chunks containing logical data were received, current position + # is difinitely zero. + pos = self._http_chunk_splits[-1] if self._http_chunk_splits else 0 + + if self.total_bytes == pos: + # We should not add empty chunks here. So we check for that. + # Note, when chunked + gzip is used, we can receive a chunk + # of compressed data, but that data may not be enough for gzip FSM + # to yield any uncompressed data. That's why current position may + # not change after receiving a chunk. + return + + self._http_chunk_splits.append(self.total_bytes) + + # wake up readchunk when end of http chunk received + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + async def _wait(self, func_name: str) -> None: + # StreamReader uses a future to link the protocol feed_data() method + # to a read coroutine. Running two read coroutines at the same time + # would have an unexpected behaviour. It would not possible to know + # which coroutine would get the next data. + if self._waiter is not None: + raise RuntimeError( + "%s() called while another coroutine is " + "already waiting for incoming data" % func_name + ) + + waiter = self._waiter = self._loop.create_future() + try: + with self._timer: + await waiter + finally: + self._waiter = None + + async def readline(self) -> bytes: + return await self.readuntil() + + async def readuntil(self, separator: bytes = b"\n") -> bytes: + seplen = len(separator) + if seplen == 0: + raise ValueError("Separator should be at least one-byte string") + + if self._exception is not None: + raise self._exception + + chunk = b"" + chunk_size = 0 + not_enough = True + + while not_enough: + while self._buffer and not_enough: + offset = self._buffer_offset + ichar = self._buffer[0].find(separator, offset) + 1 + # Read from current offset to found separator or to the end. + data = self._read_nowait_chunk( + ichar - offset + seplen - 1 if ichar else -1 + ) + chunk += data + chunk_size += len(data) + if ichar: + not_enough = False + + if chunk_size > self._high_water: + raise ValueError("Chunk too big") + + if self._eof: + break + + if not_enough: + await self._wait("readuntil") + + return chunk + + async def read(self, n: int = -1) -> bytes: + if self._exception is not None: + raise self._exception + + # migration problem; with DataQueue you have to catch + # EofStream exception, so common way is to run payload.read() inside + # infinite loop. what can cause real infinite loop with StreamReader + # lets keep this code one major release. + if __debug__: + if self._eof and not self._buffer: + self._eof_counter = getattr(self, "_eof_counter", 0) + 1 + if self._eof_counter > 5: + internal_logger.warning( + "Multiple access to StreamReader in eof state, " + "might be infinite loop.", + stack_info=True, + ) + + if not n: + return b"" + + if n < 0: + # This used to just loop creating a new waiter hoping to + # collect everything in self._buffer, but that would + # deadlock if the subprocess sends more than self.limit + # bytes. So just call self.readany() until EOF. + blocks = [] + while True: + block = await self.readany() + if not block: + break + blocks.append(block) + return b"".join(blocks) + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("read") + + return self._read_nowait(n) + + async def readany(self) -> bytes: + if self._exception is not None: + raise self._exception + + # TODO: should be `if` instead of `while` + # because waiter maybe triggered on chunk end, + # without feeding any data + while not self._buffer and not self._eof: + await self._wait("readany") + + return self._read_nowait(-1) + + async def readchunk(self) -> Tuple[bytes, bool]: + """Returns a tuple of (data, end_of_http_chunk). + + When chunked transfer + encoding is used, end_of_http_chunk is a boolean indicating if the end + of the data corresponds to the end of a HTTP chunk , otherwise it is + always False. + """ + while True: + if self._exception is not None: + raise self._exception + + while self._http_chunk_splits: + pos = self._http_chunk_splits.pop(0) + if pos == self._cursor: + return (b"", True) + if pos > self._cursor: + return (self._read_nowait(pos - self._cursor), True) + internal_logger.warning( + "Skipping HTTP chunk end due to data " + "consumption beyond chunk boundary" + ) + + if self._buffer: + return (self._read_nowait_chunk(-1), False) + # return (self._read_nowait(-1), False) + + if self._eof: + # Special case for signifying EOF. + # (b'', True) is not a final return value actually. + return (b"", False) + + await self._wait("readchunk") + + async def readexactly(self, n: int) -> bytes: + if self._exception is not None: + raise self._exception + + blocks: List[bytes] = [] + while n > 0: + block = await self.read(n) + if not block: + partial = b"".join(blocks) + raise asyncio.IncompleteReadError(partial, len(partial) + n) + blocks.append(block) + n -= len(block) + + return b"".join(blocks) + + def read_nowait(self, n: int = -1) -> bytes: + # default was changed to be consistent with .read(-1) + # + # I believe the most users don't know about the method and + # they are not affected. + if self._exception is not None: + raise self._exception + + if self._waiter and not self._waiter.done(): + raise RuntimeError( + "Called while some coroutine is waiting for incoming data." + ) + + return self._read_nowait(n) + + def _read_nowait_chunk(self, n: int) -> bytes: + first_buffer = self._buffer[0] + offset = self._buffer_offset + if n != -1 and len(first_buffer) - offset > n: + data = first_buffer[offset : offset + n] + self._buffer_offset += n + + elif offset: + self._buffer.popleft() + data = first_buffer[offset:] + self._buffer_offset = 0 + + else: + data = self._buffer.popleft() + + self._size -= len(data) + self._cursor += len(data) + + chunk_splits = self._http_chunk_splits + # Prevent memory leak: drop useless chunk splits + while chunk_splits and chunk_splits[0] < self._cursor: + chunk_splits.pop(0) + + if self._size < self._low_water and self._protocol._reading_paused: + self._protocol.resume_reading() + return data + + def _read_nowait(self, n: int) -> bytes: + """Read not more than n bytes, or whole buffer if n == -1""" + self._timer.assert_timeout() + + chunks = [] + while self._buffer: + chunk = self._read_nowait_chunk(n) + chunks.append(chunk) + if n != -1: + n -= len(chunk) + if n == 0: + break + + return b"".join(chunks) if chunks else b"" + + +class EmptyStreamReader(StreamReader): # lgtm [py/missing-call-to-init] + def __init__(self) -> None: + self._read_eof_chunk = False + + def __repr__(self) -> str: + return "<%s>" % self.__class__.__name__ + + def exception(self) -> Optional[BaseException]: + return None + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + pass + + def on_eof(self, callback: Callable[[], None]) -> None: + try: + callback() + except Exception: + internal_logger.exception("Exception in eof callback") + + def feed_eof(self) -> None: + pass + + def is_eof(self) -> bool: + return True + + def at_eof(self) -> bool: + return True + + async def wait_eof(self) -> None: + return + + def feed_data(self, data: bytes, n: int = 0) -> None: + pass + + async def readline(self) -> bytes: + return b"" + + async def read(self, n: int = -1) -> bytes: + return b"" + + # TODO add async def readuntil + + async def readany(self) -> bytes: + return b"" + + async def readchunk(self) -> Tuple[bytes, bool]: + if not self._read_eof_chunk: + self._read_eof_chunk = True + return (b"", False) + + return (b"", True) + + async def readexactly(self, n: int) -> bytes: + raise asyncio.IncompleteReadError(b"", n) + + def read_nowait(self, n: int = -1) -> bytes: + return b"" + + +EMPTY_PAYLOAD: Final[StreamReader] = EmptyStreamReader() + + +class DataQueue(Generic[_T]): + """DataQueue is a general-purpose blocking queue with one reader.""" + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + self._loop = loop + self._eof = False + self._waiter: Optional[asyncio.Future[None]] = None + self._exception: Optional[BaseException] = None + self._size = 0 + self._buffer: Deque[Tuple[_T, int]] = collections.deque() + + def __len__(self) -> int: + return len(self._buffer) + + def is_eof(self) -> bool: + return self._eof + + def at_eof(self) -> bool: + return self._eof and not self._buffer + + def exception(self) -> Optional[BaseException]: + return self._exception + + def set_exception( + self, + exc: BaseException, + exc_cause: BaseException = _EXC_SENTINEL, + ) -> None: + self._eof = True + self._exception = exc + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_exception(waiter, exc, exc_cause) + + def feed_data(self, data: _T, size: int = 0) -> None: + self._size += size + self._buffer.append((data, size)) + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + def feed_eof(self) -> None: + self._eof = True + + waiter = self._waiter + if waiter is not None: + self._waiter = None + set_result(waiter, None) + + async def read(self) -> _T: + if not self._buffer and not self._eof: + assert not self._waiter + self._waiter = self._loop.create_future() + try: + await self._waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._waiter = None + raise + + if self._buffer: + data, size = self._buffer.popleft() + self._size -= size + return data + else: + if self._exception is not None: + raise self._exception + else: + raise EofStream + + def __aiter__(self) -> AsyncStreamIterator[_T]: + return AsyncStreamIterator(self.read) + + +class FlowControlDataQueue(DataQueue[_T]): + """FlowControlDataQueue resumes and pauses an underlying stream. + + It is a destination for parsed data. + """ + + def __init__( + self, protocol: BaseProtocol, limit: int, *, loop: asyncio.AbstractEventLoop + ) -> None: + super().__init__(loop=loop) + + self._protocol = protocol + self._limit = limit * 2 + + def feed_data(self, data: _T, size: int = 0) -> None: + super().feed_data(data, size) + + if self._size > self._limit and not self._protocol._reading_paused: + self._protocol.pause_reading() + + async def read(self) -> _T: + try: + return await super().read() + finally: + if self._size < self._limit and self._protocol._reading_paused: + self._protocol.resume_reading() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/tcp_helpers.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/tcp_helpers.py new file mode 100644 index 0000000..88b2442 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/tcp_helpers.py @@ -0,0 +1,37 @@ +"""Helper methods to tune a TCP connection""" + +import asyncio +import socket +from contextlib import suppress +from typing import Optional # noqa + +__all__ = ("tcp_keepalive", "tcp_nodelay") + + +if hasattr(socket, "SO_KEEPALIVE"): + + def tcp_keepalive(transport: asyncio.Transport) -> None: + sock = transport.get_extra_info("socket") + if sock is not None: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + +else: + + def tcp_keepalive(transport: asyncio.Transport) -> None: # pragma: no cover + pass + + +def tcp_nodelay(transport: asyncio.Transport, value: bool) -> None: + sock = transport.get_extra_info("socket") + + if sock is None: + return + + if sock.family not in (socket.AF_INET, socket.AF_INET6): + return + + value = bool(value) + + # socket may be closed already, on windows OSError get raised + with suppress(OSError): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, value) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/test_utils.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/test_utils.py new file mode 100644 index 0000000..a36e859 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/test_utils.py @@ -0,0 +1,682 @@ +"""Utilities shared by tests.""" + +import asyncio +import contextlib +import gc +import inspect +import ipaddress +import os +import socket +import sys +import warnings +from abc import ABC, abstractmethod +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Iterator, + List, + Optional, + Type, + Union, + cast, +) +from unittest import IsolatedAsyncioTestCase, mock + +from aiosignal import Signal +from multidict import CIMultiDict, CIMultiDictProxy +from yarl import URL + +import aiohttp +from aiohttp.client import _RequestContextManager, _WSRequestContextManager + +from . import ClientSession, hdrs +from .abc import AbstractCookieJar +from .client_reqrep import ClientResponse +from .client_ws import ClientWebSocketResponse +from .helpers import sentinel +from .http import HttpVersion, RawRequestMessage +from .typedefs import StrOrURL +from .web import ( + Application, + AppRunner, + BaseRunner, + Request, + Server, + ServerRunner, + SockSite, + UrlMappingMatchInfo, +) +from .web_protocol import _RequestHandler + +if TYPE_CHECKING: + from ssl import SSLContext +else: + SSLContext = None + +REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" + + +def get_unused_port_socket( + host: str, family: socket.AddressFamily = socket.AF_INET +) -> socket.socket: + return get_port_socket(host, 0, family) + + +def get_port_socket( + host: str, port: int, family: socket.AddressFamily +) -> socket.socket: + s = socket.socket(family, socket.SOCK_STREAM) + if REUSE_ADDRESS: + # Windows has different semantics for SO_REUSEADDR, + # so don't set it. Ref: + # https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((host, port)) + return s + + +def unused_port() -> int: + """Return a port that is unused on the current host.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("127.0.0.1", 0)) + return cast(int, s.getsockname()[1]) + + +class BaseTestServer(ABC): + __test__ = False + + def __init__( + self, + *, + scheme: Union[str, object] = sentinel, + loop: Optional[asyncio.AbstractEventLoop] = None, + host: str = "127.0.0.1", + port: Optional[int] = None, + skip_url_asserts: bool = False, + socket_factory: Callable[ + [str, int, socket.AddressFamily], socket.socket + ] = get_port_socket, + **kwargs: Any, + ) -> None: + self._loop = loop + self.runner: Optional[BaseRunner] = None + self._root: Optional[URL] = None + self.host = host + self.port = port + self._closed = False + self.scheme = scheme + self.skip_url_asserts = skip_url_asserts + self.socket_factory = socket_factory + + async def start_server( + self, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs: Any + ) -> None: + if self.runner: + return + self._loop = loop + self._ssl = kwargs.pop("ssl", None) + self.runner = await self._make_runner(handler_cancellation=True, **kwargs) + await self.runner.setup() + if not self.port: + self.port = 0 + try: + version = ipaddress.ip_address(self.host).version + except ValueError: + version = 4 + family = socket.AF_INET6 if version == 6 else socket.AF_INET + _sock = self.socket_factory(self.host, self.port, family) + self.host, self.port = _sock.getsockname()[:2] + site = SockSite(self.runner, sock=_sock, ssl_context=self._ssl) + await site.start() + server = site._server + assert server is not None + sockets = server.sockets # type: ignore[attr-defined] + assert sockets is not None + self.port = sockets[0].getsockname()[1] + if self.scheme is sentinel: + if self._ssl: + scheme = "https" + else: + scheme = "http" + self.scheme = scheme + self._root = URL(f"{self.scheme}://{self.host}:{self.port}") + + @abstractmethod # pragma: no cover + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + pass + + def make_url(self, path: StrOrURL) -> URL: + assert self._root is not None + url = URL(path) + if not self.skip_url_asserts: + assert not url.is_absolute() + return self._root.join(url) + else: + return URL(str(self._root) + str(path)) + + @property + def started(self) -> bool: + return self.runner is not None + + @property + def closed(self) -> bool: + return self._closed + + @property + def handler(self) -> Server: + # for backward compatibility + # web.Server instance + runner = self.runner + assert runner is not None + assert runner.server is not None + return runner.server + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run when the object is garbage collected, and on + exit when used as a context manager. + + """ + if self.started and not self.closed: + assert self.runner is not None + await self.runner.cleanup() + self._root = None + self.port = None + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "BaseTestServer": + await self.start_server(loop=self._loop) + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + await self.close() + + +class TestServer(BaseTestServer): + def __init__( + self, + app: Application, + *, + scheme: Union[str, object] = sentinel, + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ): + self.app = app + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, **kwargs: Any) -> BaseRunner: + return AppRunner(self.app, **kwargs) + + +class RawTestServer(BaseTestServer): + def __init__( + self, + handler: _RequestHandler, + *, + scheme: Union[str, object] = sentinel, + host: str = "127.0.0.1", + port: Optional[int] = None, + **kwargs: Any, + ) -> None: + self._handler = handler + super().__init__(scheme=scheme, host=host, port=port, **kwargs) + + async def _make_runner(self, debug: bool = True, **kwargs: Any) -> ServerRunner: + srv = Server(self._handler, loop=self._loop, debug=debug, **kwargs) + return ServerRunner(srv, debug=debug, **kwargs) + + +class TestClient: + """ + A test client implementation. + + To write functional tests for aiohttp based servers. + + """ + + __test__ = False + + def __init__( + self, + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any, + ) -> None: + if not isinstance(server, BaseTestServer): + raise TypeError( + "server must be TestServer " "instance, found type: %r" % type(server) + ) + self._server = server + self._loop = loop + if cookie_jar is None: + cookie_jar = aiohttp.CookieJar(unsafe=True, loop=loop) + self._session = ClientSession(loop=loop, cookie_jar=cookie_jar, **kwargs) + self._closed = False + self._responses: List[ClientResponse] = [] + self._websockets: List[ClientWebSocketResponse] = [] + + async def start_server(self) -> None: + await self._server.start_server(loop=self._loop) + + @property + def host(self) -> str: + return self._server.host + + @property + def port(self) -> Optional[int]: + return self._server.port + + @property + def server(self) -> BaseTestServer: + return self._server + + @property + def app(self) -> Optional[Application]: + return cast(Optional[Application], getattr(self._server, "app", None)) + + @property + def session(self) -> ClientSession: + """An internal aiohttp.ClientSession. + + Unlike the methods on the TestClient, client session requests + do not automatically include the host in the url queried, and + will require an absolute path to the resource. + + """ + return self._session + + def make_url(self, path: StrOrURL) -> URL: + return self._server.make_url(path) + + async def _request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> ClientResponse: + resp = await self._session.request(method, self.make_url(path), **kwargs) + # save it to close later + self._responses.append(resp) + return resp + + def request( + self, method: str, path: StrOrURL, **kwargs: Any + ) -> _RequestContextManager: + """Routes a request to tested http server. + + The interface is identical to aiohttp.ClientSession.request, + except the loop kwarg is overridden by the instance used by the + test server. + + """ + return _RequestContextManager(self._request(method, path, **kwargs)) + + def get(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP GET request.""" + return _RequestContextManager(self._request(hdrs.METH_GET, path, **kwargs)) + + def post(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP POST request.""" + return _RequestContextManager(self._request(hdrs.METH_POST, path, **kwargs)) + + def options(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP OPTIONS request.""" + return _RequestContextManager(self._request(hdrs.METH_OPTIONS, path, **kwargs)) + + def head(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP HEAD request.""" + return _RequestContextManager(self._request(hdrs.METH_HEAD, path, **kwargs)) + + def put(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PUT request.""" + return _RequestContextManager(self._request(hdrs.METH_PUT, path, **kwargs)) + + def patch(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager(self._request(hdrs.METH_PATCH, path, **kwargs)) + + def delete(self, path: StrOrURL, **kwargs: Any) -> _RequestContextManager: + """Perform an HTTP PATCH request.""" + return _RequestContextManager(self._request(hdrs.METH_DELETE, path, **kwargs)) + + def ws_connect(self, path: StrOrURL, **kwargs: Any) -> _WSRequestContextManager: + """Initiate websocket connection. + + The api corresponds to aiohttp.ClientSession.ws_connect. + + """ + return _WSRequestContextManager(self._ws_connect(path, **kwargs)) + + async def _ws_connect( + self, path: StrOrURL, **kwargs: Any + ) -> ClientWebSocketResponse: + ws = await self._session.ws_connect(self.make_url(path), **kwargs) + self._websockets.append(ws) + return ws + + async def close(self) -> None: + """Close all fixtures created by the test client. + + After that point, the TestClient is no longer usable. + + This is an idempotent function: running close multiple times + will not have any additional effects. + + close is also run on exit when used as a(n) (asynchronous) + context manager. + + """ + if not self._closed: + for resp in self._responses: + resp.close() + for ws in self._websockets: + await ws.close() + await self._session.close() + await self._server.close() + self._closed = True + + def __enter__(self) -> None: + raise TypeError("Use async with instead") + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + # __exit__ should exist in pair with __enter__ but never executed + pass # pragma: no cover + + async def __aenter__(self) -> "TestClient": + await self.start_server() + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: + await self.close() + + +class AioHTTPTestCase(IsolatedAsyncioTestCase): + """A base class to allow for unittest web applications using aiohttp. + + Provides the following: + + * self.client (aiohttp.test_utils.TestClient): an aiohttp test client. + * self.loop (asyncio.BaseEventLoop): the event loop in which the + application and server are running. + * self.app (aiohttp.web.Application): the application returned by + self.get_application() + + Note that the TestClient's methods are asynchronous: you have to + execute function on the test client using asynchronous methods. + """ + + async def get_application(self) -> Application: + """Get application. + + This method should be overridden + to return the aiohttp.web.Application + object to test. + """ + return self.get_app() + + def get_app(self) -> Application: + """Obsolete method used to constructing web application. + + Use .get_application() coroutine instead. + """ + raise RuntimeError("Did you forget to define get_application()?") + + async def asyncSetUp(self) -> None: + self.loop = asyncio.get_running_loop() + return await self.setUpAsync() + + async def setUpAsync(self) -> None: + self.app = await self.get_application() + self.server = await self.get_server(self.app) + self.client = await self.get_client(self.server) + + await self.client.start_server() + + async def asyncTearDown(self) -> None: + return await self.tearDownAsync() + + async def tearDownAsync(self) -> None: + await self.client.close() + + async def get_server(self, app: Application) -> TestServer: + """Return a TestServer instance.""" + return TestServer(app, loop=self.loop) + + async def get_client(self, server: TestServer) -> TestClient: + """Return a TestClient instance.""" + return TestClient(server, loop=self.loop) + + +def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any: + """ + A decorator dedicated to use with asynchronous AioHTTPTestCase test methods. + + In 3.8+, this does nothing. + """ + warnings.warn( + "Decorator `@unittest_run_loop` is no longer needed in aiohttp 3.8+", + DeprecationWarning, + stacklevel=2, + ) + return func + + +_LOOP_FACTORY = Callable[[], asyncio.AbstractEventLoop] + + +@contextlib.contextmanager +def loop_context( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, fast: bool = False +) -> Iterator[asyncio.AbstractEventLoop]: + """A contextmanager that creates an event_loop, for test purposes. + + Handles the creation and cleanup of a test loop. + """ + loop = setup_test_loop(loop_factory) + yield loop + teardown_test_loop(loop, fast=fast) + + +def setup_test_loop( + loop_factory: _LOOP_FACTORY = asyncio.new_event_loop, +) -> asyncio.AbstractEventLoop: + """Create and return an asyncio.BaseEventLoop instance. + + The caller should also call teardown_test_loop, + once they are done with the loop. + """ + loop = loop_factory() + asyncio.set_event_loop(loop) + return loop + + +def teardown_test_loop(loop: asyncio.AbstractEventLoop, fast: bool = False) -> None: + """Teardown and cleanup an event_loop created by setup_test_loop.""" + closed = loop.is_closed() + if not closed: + loop.call_soon(loop.stop) + loop.run_forever() + loop.close() + + if not fast: + gc.collect() + + asyncio.set_event_loop(None) + + +def _create_app_mock() -> mock.MagicMock: + def get_dict(app: Any, key: str) -> Any: + return app.__app_dict[key] + + def set_dict(app: Any, key: str, value: Any) -> None: + app.__app_dict[key] = value + + app = mock.MagicMock(spec=Application) + app.__app_dict = {} + app.__getitem__ = get_dict + app.__setitem__ = set_dict + + app._debug = False + app.on_response_prepare = Signal(app) + app.on_response_prepare.freeze() + return app + + +def _create_transport(sslcontext: Optional[SSLContext] = None) -> mock.Mock: + transport = mock.Mock() + + def get_extra_info(key: str) -> Optional[SSLContext]: + if key == "sslcontext": + return sslcontext + else: + return None + + transport.get_extra_info.side_effect = get_extra_info + return transport + + +def make_mocked_request( + method: str, + path: str, + headers: Any = None, + *, + match_info: Any = sentinel, + version: HttpVersion = HttpVersion(1, 1), + closing: bool = False, + app: Any = None, + writer: Any = sentinel, + protocol: Any = sentinel, + transport: Any = sentinel, + payload: Any = sentinel, + sslcontext: Optional[SSLContext] = None, + client_max_size: int = 1024**2, + loop: Any = ..., +) -> Request: + """Creates mocked web.Request testing purposes. + + Useful in unit tests, when spinning full web server is overkill or + specific conditions and errors are hard to trigger. + """ + task = mock.Mock() + if loop is ...: + # no loop passed, try to get the current one if + # its is running as we need a real loop to create + # executor jobs to be able to do testing + # with a real executor + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = mock.Mock() + loop.create_future.return_value = () + + if version < HttpVersion(1, 1): + closing = True + + if headers: + headers = CIMultiDictProxy(CIMultiDict(headers)) + raw_hdrs = tuple( + (k.encode("utf-8"), v.encode("utf-8")) for k, v in headers.items() + ) + else: + headers = CIMultiDictProxy(CIMultiDict()) + raw_hdrs = () + + chunked = "chunked" in headers.get(hdrs.TRANSFER_ENCODING, "").lower() + + message = RawRequestMessage( + method, + path, + version, + headers, + raw_hdrs, + closing, + None, + False, + chunked, + URL(path), + ) + if app is None: + app = _create_app_mock() + + if transport is sentinel: + transport = _create_transport(sslcontext) + + if protocol is sentinel: + protocol = mock.Mock() + protocol.transport = transport + + if writer is sentinel: + writer = mock.Mock() + writer.write_headers = make_mocked_coro(None) + writer.write = make_mocked_coro(None) + writer.write_eof = make_mocked_coro(None) + writer.drain = make_mocked_coro(None) + writer.transport = transport + + protocol.transport = transport + protocol.writer = writer + + if payload is sentinel: + payload = mock.Mock() + + req = Request( + message, payload, protocol, writer, task, loop, client_max_size=client_max_size + ) + + match_info = UrlMappingMatchInfo( + {} if match_info is sentinel else match_info, mock.Mock() + ) + match_info.add_app(app) + req._match_info = match_info + + return req + + +def make_mocked_coro( + return_value: Any = sentinel, raise_exception: Any = sentinel +) -> Any: + """Creates a coroutine mock.""" + + async def mock_coro(*args: Any, **kwargs: Any) -> Any: + if raise_exception is not sentinel: + raise raise_exception + if not inspect.isawaitable(return_value): + return return_value + await return_value + + return mock.Mock(wraps=mock_coro) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/tracing.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/tracing.py new file mode 100644 index 0000000..62847a0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/tracing.py @@ -0,0 +1,471 @@ +from types import SimpleNamespace +from typing import TYPE_CHECKING, Awaitable, Optional, Protocol, Type, TypeVar + +import attr +from aiosignal import Signal +from multidict import CIMultiDict +from yarl import URL + +from .client_reqrep import ClientResponse + +if TYPE_CHECKING: + from .client import ClientSession + + _ParamT_contra = TypeVar("_ParamT_contra", contravariant=True) + + class _SignalCallback(Protocol[_ParamT_contra]): + def __call__( + self, + __client_session: ClientSession, + __trace_config_ctx: SimpleNamespace, + __params: _ParamT_contra, + ) -> Awaitable[None]: + ... + + +__all__ = ( + "TraceConfig", + "TraceRequestStartParams", + "TraceRequestEndParams", + "TraceRequestExceptionParams", + "TraceConnectionQueuedStartParams", + "TraceConnectionQueuedEndParams", + "TraceConnectionCreateStartParams", + "TraceConnectionCreateEndParams", + "TraceConnectionReuseconnParams", + "TraceDnsResolveHostStartParams", + "TraceDnsResolveHostEndParams", + "TraceDnsCacheHitParams", + "TraceDnsCacheMissParams", + "TraceRequestRedirectParams", + "TraceRequestChunkSentParams", + "TraceResponseChunkReceivedParams", + "TraceRequestHeadersSentParams", +) + + +class TraceConfig: + """First-class used to trace requests launched via ClientSession objects.""" + + def __init__( + self, trace_config_ctx_factory: Type[SimpleNamespace] = SimpleNamespace + ) -> None: + self._on_request_start: Signal[ + _SignalCallback[TraceRequestStartParams] + ] = Signal(self) + self._on_request_chunk_sent: Signal[ + _SignalCallback[TraceRequestChunkSentParams] + ] = Signal(self) + self._on_response_chunk_received: Signal[ + _SignalCallback[TraceResponseChunkReceivedParams] + ] = Signal(self) + self._on_request_end: Signal[_SignalCallback[TraceRequestEndParams]] = Signal( + self + ) + self._on_request_exception: Signal[ + _SignalCallback[TraceRequestExceptionParams] + ] = Signal(self) + self._on_request_redirect: Signal[ + _SignalCallback[TraceRequestRedirectParams] + ] = Signal(self) + self._on_connection_queued_start: Signal[ + _SignalCallback[TraceConnectionQueuedStartParams] + ] = Signal(self) + self._on_connection_queued_end: Signal[ + _SignalCallback[TraceConnectionQueuedEndParams] + ] = Signal(self) + self._on_connection_create_start: Signal[ + _SignalCallback[TraceConnectionCreateStartParams] + ] = Signal(self) + self._on_connection_create_end: Signal[ + _SignalCallback[TraceConnectionCreateEndParams] + ] = Signal(self) + self._on_connection_reuseconn: Signal[ + _SignalCallback[TraceConnectionReuseconnParams] + ] = Signal(self) + self._on_dns_resolvehost_start: Signal[ + _SignalCallback[TraceDnsResolveHostStartParams] + ] = Signal(self) + self._on_dns_resolvehost_end: Signal[ + _SignalCallback[TraceDnsResolveHostEndParams] + ] = Signal(self) + self._on_dns_cache_hit: Signal[ + _SignalCallback[TraceDnsCacheHitParams] + ] = Signal(self) + self._on_dns_cache_miss: Signal[ + _SignalCallback[TraceDnsCacheMissParams] + ] = Signal(self) + self._on_request_headers_sent: Signal[ + _SignalCallback[TraceRequestHeadersSentParams] + ] = Signal(self) + + self._trace_config_ctx_factory = trace_config_ctx_factory + + def trace_config_ctx( + self, trace_request_ctx: Optional[SimpleNamespace] = None + ) -> SimpleNamespace: + """Return a new trace_config_ctx instance""" + return self._trace_config_ctx_factory(trace_request_ctx=trace_request_ctx) + + def freeze(self) -> None: + self._on_request_start.freeze() + self._on_request_chunk_sent.freeze() + self._on_response_chunk_received.freeze() + self._on_request_end.freeze() + self._on_request_exception.freeze() + self._on_request_redirect.freeze() + self._on_connection_queued_start.freeze() + self._on_connection_queued_end.freeze() + self._on_connection_create_start.freeze() + self._on_connection_create_end.freeze() + self._on_connection_reuseconn.freeze() + self._on_dns_resolvehost_start.freeze() + self._on_dns_resolvehost_end.freeze() + self._on_dns_cache_hit.freeze() + self._on_dns_cache_miss.freeze() + self._on_request_headers_sent.freeze() + + @property + def on_request_start(self) -> "Signal[_SignalCallback[TraceRequestStartParams]]": + return self._on_request_start + + @property + def on_request_chunk_sent( + self, + ) -> "Signal[_SignalCallback[TraceRequestChunkSentParams]]": + return self._on_request_chunk_sent + + @property + def on_response_chunk_received( + self, + ) -> "Signal[_SignalCallback[TraceResponseChunkReceivedParams]]": + return self._on_response_chunk_received + + @property + def on_request_end(self) -> "Signal[_SignalCallback[TraceRequestEndParams]]": + return self._on_request_end + + @property + def on_request_exception( + self, + ) -> "Signal[_SignalCallback[TraceRequestExceptionParams]]": + return self._on_request_exception + + @property + def on_request_redirect( + self, + ) -> "Signal[_SignalCallback[TraceRequestRedirectParams]]": + return self._on_request_redirect + + @property + def on_connection_queued_start( + self, + ) -> "Signal[_SignalCallback[TraceConnectionQueuedStartParams]]": + return self._on_connection_queued_start + + @property + def on_connection_queued_end( + self, + ) -> "Signal[_SignalCallback[TraceConnectionQueuedEndParams]]": + return self._on_connection_queued_end + + @property + def on_connection_create_start( + self, + ) -> "Signal[_SignalCallback[TraceConnectionCreateStartParams]]": + return self._on_connection_create_start + + @property + def on_connection_create_end( + self, + ) -> "Signal[_SignalCallback[TraceConnectionCreateEndParams]]": + return self._on_connection_create_end + + @property + def on_connection_reuseconn( + self, + ) -> "Signal[_SignalCallback[TraceConnectionReuseconnParams]]": + return self._on_connection_reuseconn + + @property + def on_dns_resolvehost_start( + self, + ) -> "Signal[_SignalCallback[TraceDnsResolveHostStartParams]]": + return self._on_dns_resolvehost_start + + @property + def on_dns_resolvehost_end( + self, + ) -> "Signal[_SignalCallback[TraceDnsResolveHostEndParams]]": + return self._on_dns_resolvehost_end + + @property + def on_dns_cache_hit(self) -> "Signal[_SignalCallback[TraceDnsCacheHitParams]]": + return self._on_dns_cache_hit + + @property + def on_dns_cache_miss(self) -> "Signal[_SignalCallback[TraceDnsCacheMissParams]]": + return self._on_dns_cache_miss + + @property + def on_request_headers_sent( + self, + ) -> "Signal[_SignalCallback[TraceRequestHeadersSentParams]]": + return self._on_request_headers_sent + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestStartParams: + """Parameters sent by the `on_request_start` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestChunkSentParams: + """Parameters sent by the `on_request_chunk_sent` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceResponseChunkReceivedParams: + """Parameters sent by the `on_response_chunk_received` signal""" + + method: str + url: URL + chunk: bytes + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestEndParams: + """Parameters sent by the `on_request_end` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestExceptionParams: + """Parameters sent by the `on_request_exception` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + exception: BaseException + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestRedirectParams: + """Parameters sent by the `on_request_redirect` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + response: ClientResponse + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedStartParams: + """Parameters sent by the `on_connection_queued_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionQueuedEndParams: + """Parameters sent by the `on_connection_queued_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateStartParams: + """Parameters sent by the `on_connection_create_start` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionCreateEndParams: + """Parameters sent by the `on_connection_create_end` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceConnectionReuseconnParams: + """Parameters sent by the `on_connection_reuseconn` signal""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostStartParams: + """Parameters sent by the `on_dns_resolvehost_start` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsResolveHostEndParams: + """Parameters sent by the `on_dns_resolvehost_end` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheHitParams: + """Parameters sent by the `on_dns_cache_hit` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceDnsCacheMissParams: + """Parameters sent by the `on_dns_cache_miss` signal""" + + host: str + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class TraceRequestHeadersSentParams: + """Parameters sent by the `on_request_headers_sent` signal""" + + method: str + url: URL + headers: "CIMultiDict[str]" + + +class Trace: + """Internal dependency holder class. + + Used to keep together the main dependencies used + at the moment of send a signal. + """ + + def __init__( + self, + session: "ClientSession", + trace_config: TraceConfig, + trace_config_ctx: SimpleNamespace, + ) -> None: + self._trace_config = trace_config + self._trace_config_ctx = trace_config_ctx + self._session = session + + async def send_request_start( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config.on_request_start.send( + self._session, + self._trace_config_ctx, + TraceRequestStartParams(method, url, headers), + ) + + async def send_request_chunk_sent( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_request_chunk_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestChunkSentParams(method, url, chunk), + ) + + async def send_response_chunk_received( + self, method: str, url: URL, chunk: bytes + ) -> None: + return await self._trace_config.on_response_chunk_received.send( + self._session, + self._trace_config_ctx, + TraceResponseChunkReceivedParams(method, url, chunk), + ) + + async def send_request_end( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config.on_request_end.send( + self._session, + self._trace_config_ctx, + TraceRequestEndParams(method, url, headers, response), + ) + + async def send_request_exception( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + exception: BaseException, + ) -> None: + return await self._trace_config.on_request_exception.send( + self._session, + self._trace_config_ctx, + TraceRequestExceptionParams(method, url, headers, exception), + ) + + async def send_request_redirect( + self, + method: str, + url: URL, + headers: "CIMultiDict[str]", + response: ClientResponse, + ) -> None: + return await self._trace_config._on_request_redirect.send( + self._session, + self._trace_config_ctx, + TraceRequestRedirectParams(method, url, headers, response), + ) + + async def send_connection_queued_start(self) -> None: + return await self._trace_config.on_connection_queued_start.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedStartParams() + ) + + async def send_connection_queued_end(self) -> None: + return await self._trace_config.on_connection_queued_end.send( + self._session, self._trace_config_ctx, TraceConnectionQueuedEndParams() + ) + + async def send_connection_create_start(self) -> None: + return await self._trace_config.on_connection_create_start.send( + self._session, self._trace_config_ctx, TraceConnectionCreateStartParams() + ) + + async def send_connection_create_end(self) -> None: + return await self._trace_config.on_connection_create_end.send( + self._session, self._trace_config_ctx, TraceConnectionCreateEndParams() + ) + + async def send_connection_reuseconn(self) -> None: + return await self._trace_config.on_connection_reuseconn.send( + self._session, self._trace_config_ctx, TraceConnectionReuseconnParams() + ) + + async def send_dns_resolvehost_start(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_start.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostStartParams(host) + ) + + async def send_dns_resolvehost_end(self, host: str) -> None: + return await self._trace_config.on_dns_resolvehost_end.send( + self._session, self._trace_config_ctx, TraceDnsResolveHostEndParams(host) + ) + + async def send_dns_cache_hit(self, host: str) -> None: + return await self._trace_config.on_dns_cache_hit.send( + self._session, self._trace_config_ctx, TraceDnsCacheHitParams(host) + ) + + async def send_dns_cache_miss(self, host: str) -> None: + return await self._trace_config.on_dns_cache_miss.send( + self._session, self._trace_config_ctx, TraceDnsCacheMissParams(host) + ) + + async def send_request_headers( + self, method: str, url: URL, headers: "CIMultiDict[str]" + ) -> None: + return await self._trace_config._on_request_headers_sent.send( + self._session, + self._trace_config_ctx, + TraceRequestHeadersSentParams(method, url, headers), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/typedefs.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/typedefs.py new file mode 100644 index 0000000..5e963e1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/typedefs.py @@ -0,0 +1,54 @@ +import json +import os +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + Mapping, + Tuple, + Union, +) + +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr +from yarl import URL + +DEFAULT_JSON_ENCODER = json.dumps +DEFAULT_JSON_DECODER = json.loads + +if TYPE_CHECKING: + _CIMultiDict = CIMultiDict[str] + _CIMultiDictProxy = CIMultiDictProxy[str] + _MultiDict = MultiDict[str] + _MultiDictProxy = MultiDictProxy[str] + from http.cookies import BaseCookie, Morsel + + from .web import Request, StreamResponse +else: + _CIMultiDict = CIMultiDict + _CIMultiDictProxy = CIMultiDictProxy + _MultiDict = MultiDict + _MultiDictProxy = MultiDictProxy + +Byteish = Union[bytes, bytearray, memoryview] +JSONEncoder = Callable[[Any], str] +JSONDecoder = Callable[[str], Any] +LooseHeaders = Union[Mapping[Union[str, istr], str], _CIMultiDict, _CIMultiDictProxy] +RawHeaders = Tuple[Tuple[bytes, bytes], ...] +StrOrURL = Union[str, URL] + +LooseCookiesMappings = Mapping[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +LooseCookiesIterables = Iterable[ + Tuple[str, Union[str, "BaseCookie[str]", "Morsel[Any]"]] +] +LooseCookies = Union[ + LooseCookiesMappings, + LooseCookiesIterables, + "BaseCookie[str]", +] + +Handler = Callable[["Request"], Awaitable["StreamResponse"]] +Middleware = Callable[["Request", Handler], Awaitable["StreamResponse"]] + +PathLike = Union[str, "os.PathLike[str]"] diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web.py new file mode 100644 index 0000000..e911650 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web.py @@ -0,0 +1,616 @@ +import asyncio +import logging +import os +import socket +import sys +import warnings +from argparse import ArgumentParser +from collections.abc import Iterable +from contextlib import suppress +from functools import partial +from importlib import import_module +from typing import ( + Any, + Awaitable, + Callable, + Iterable as TypingIterable, + List, + Optional, + Set, + Type, + Union, + cast, +) +from weakref import WeakSet + +from .abc import AbstractAccessLogger +from .helpers import AppKey as AppKey +from .log import access_logger +from .typedefs import PathLike +from .web_app import Application as Application, CleanupError as CleanupError +from .web_exceptions import ( + HTTPAccepted as HTTPAccepted, + HTTPBadGateway as HTTPBadGateway, + HTTPBadRequest as HTTPBadRequest, + HTTPClientError as HTTPClientError, + HTTPConflict as HTTPConflict, + HTTPCreated as HTTPCreated, + HTTPError as HTTPError, + HTTPException as HTTPException, + HTTPExpectationFailed as HTTPExpectationFailed, + HTTPFailedDependency as HTTPFailedDependency, + HTTPForbidden as HTTPForbidden, + HTTPFound as HTTPFound, + HTTPGatewayTimeout as HTTPGatewayTimeout, + HTTPGone as HTTPGone, + HTTPInsufficientStorage as HTTPInsufficientStorage, + HTTPInternalServerError as HTTPInternalServerError, + HTTPLengthRequired as HTTPLengthRequired, + HTTPMethodNotAllowed as HTTPMethodNotAllowed, + HTTPMisdirectedRequest as HTTPMisdirectedRequest, + HTTPMove as HTTPMove, + HTTPMovedPermanently as HTTPMovedPermanently, + HTTPMultipleChoices as HTTPMultipleChoices, + HTTPNetworkAuthenticationRequired as HTTPNetworkAuthenticationRequired, + HTTPNoContent as HTTPNoContent, + HTTPNonAuthoritativeInformation as HTTPNonAuthoritativeInformation, + HTTPNotAcceptable as HTTPNotAcceptable, + HTTPNotExtended as HTTPNotExtended, + HTTPNotFound as HTTPNotFound, + HTTPNotImplemented as HTTPNotImplemented, + HTTPNotModified as HTTPNotModified, + HTTPOk as HTTPOk, + HTTPPartialContent as HTTPPartialContent, + HTTPPaymentRequired as HTTPPaymentRequired, + HTTPPermanentRedirect as HTTPPermanentRedirect, + HTTPPreconditionFailed as HTTPPreconditionFailed, + HTTPPreconditionRequired as HTTPPreconditionRequired, + HTTPProxyAuthenticationRequired as HTTPProxyAuthenticationRequired, + HTTPRedirection as HTTPRedirection, + HTTPRequestEntityTooLarge as HTTPRequestEntityTooLarge, + HTTPRequestHeaderFieldsTooLarge as HTTPRequestHeaderFieldsTooLarge, + HTTPRequestRangeNotSatisfiable as HTTPRequestRangeNotSatisfiable, + HTTPRequestTimeout as HTTPRequestTimeout, + HTTPRequestURITooLong as HTTPRequestURITooLong, + HTTPResetContent as HTTPResetContent, + HTTPSeeOther as HTTPSeeOther, + HTTPServerError as HTTPServerError, + HTTPServiceUnavailable as HTTPServiceUnavailable, + HTTPSuccessful as HTTPSuccessful, + HTTPTemporaryRedirect as HTTPTemporaryRedirect, + HTTPTooManyRequests as HTTPTooManyRequests, + HTTPUnauthorized as HTTPUnauthorized, + HTTPUnavailableForLegalReasons as HTTPUnavailableForLegalReasons, + HTTPUnprocessableEntity as HTTPUnprocessableEntity, + HTTPUnsupportedMediaType as HTTPUnsupportedMediaType, + HTTPUpgradeRequired as HTTPUpgradeRequired, + HTTPUseProxy as HTTPUseProxy, + HTTPVariantAlsoNegotiates as HTTPVariantAlsoNegotiates, + HTTPVersionNotSupported as HTTPVersionNotSupported, + NotAppKeyWarning as NotAppKeyWarning, +) +from .web_fileresponse import FileResponse as FileResponse +from .web_log import AccessLogger +from .web_middlewares import ( + middleware as middleware, + normalize_path_middleware as normalize_path_middleware, +) +from .web_protocol import ( + PayloadAccessError as PayloadAccessError, + RequestHandler as RequestHandler, + RequestPayloadError as RequestPayloadError, +) +from .web_request import ( + BaseRequest as BaseRequest, + FileField as FileField, + Request as Request, +) +from .web_response import ( + ContentCoding as ContentCoding, + Response as Response, + StreamResponse as StreamResponse, + json_response as json_response, +) +from .web_routedef import ( + AbstractRouteDef as AbstractRouteDef, + RouteDef as RouteDef, + RouteTableDef as RouteTableDef, + StaticDef as StaticDef, + delete as delete, + get as get, + head as head, + options as options, + patch as patch, + post as post, + put as put, + route as route, + static as static, + view as view, +) +from .web_runner import ( + AppRunner as AppRunner, + BaseRunner as BaseRunner, + BaseSite as BaseSite, + GracefulExit as GracefulExit, + NamedPipeSite as NamedPipeSite, + ServerRunner as ServerRunner, + SockSite as SockSite, + TCPSite as TCPSite, + UnixSite as UnixSite, +) +from .web_server import Server as Server +from .web_urldispatcher import ( + AbstractResource as AbstractResource, + AbstractRoute as AbstractRoute, + DynamicResource as DynamicResource, + PlainResource as PlainResource, + PrefixedSubAppResource as PrefixedSubAppResource, + Resource as Resource, + ResourceRoute as ResourceRoute, + StaticResource as StaticResource, + UrlDispatcher as UrlDispatcher, + UrlMappingMatchInfo as UrlMappingMatchInfo, + View as View, +) +from .web_ws import ( + WebSocketReady as WebSocketReady, + WebSocketResponse as WebSocketResponse, + WSMsgType as WSMsgType, +) + +__all__ = ( + # web_app + "AppKey", + "Application", + "CleanupError", + # web_exceptions + "NotAppKeyWarning", + "HTTPAccepted", + "HTTPBadGateway", + "HTTPBadRequest", + "HTTPClientError", + "HTTPConflict", + "HTTPCreated", + "HTTPError", + "HTTPException", + "HTTPExpectationFailed", + "HTTPFailedDependency", + "HTTPForbidden", + "HTTPFound", + "HTTPGatewayTimeout", + "HTTPGone", + "HTTPInsufficientStorage", + "HTTPInternalServerError", + "HTTPLengthRequired", + "HTTPMethodNotAllowed", + "HTTPMisdirectedRequest", + "HTTPMove", + "HTTPMovedPermanently", + "HTTPMultipleChoices", + "HTTPNetworkAuthenticationRequired", + "HTTPNoContent", + "HTTPNonAuthoritativeInformation", + "HTTPNotAcceptable", + "HTTPNotExtended", + "HTTPNotFound", + "HTTPNotImplemented", + "HTTPNotModified", + "HTTPOk", + "HTTPPartialContent", + "HTTPPaymentRequired", + "HTTPPermanentRedirect", + "HTTPPreconditionFailed", + "HTTPPreconditionRequired", + "HTTPProxyAuthenticationRequired", + "HTTPRedirection", + "HTTPRequestEntityTooLarge", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPRequestRangeNotSatisfiable", + "HTTPRequestTimeout", + "HTTPRequestURITooLong", + "HTTPResetContent", + "HTTPSeeOther", + "HTTPServerError", + "HTTPServiceUnavailable", + "HTTPSuccessful", + "HTTPTemporaryRedirect", + "HTTPTooManyRequests", + "HTTPUnauthorized", + "HTTPUnavailableForLegalReasons", + "HTTPUnprocessableEntity", + "HTTPUnsupportedMediaType", + "HTTPUpgradeRequired", + "HTTPUseProxy", + "HTTPVariantAlsoNegotiates", + "HTTPVersionNotSupported", + # web_fileresponse + "FileResponse", + # web_middlewares + "middleware", + "normalize_path_middleware", + # web_protocol + "PayloadAccessError", + "RequestHandler", + "RequestPayloadError", + # web_request + "BaseRequest", + "FileField", + "Request", + # web_response + "ContentCoding", + "Response", + "StreamResponse", + "json_response", + # web_routedef + "AbstractRouteDef", + "RouteDef", + "RouteTableDef", + "StaticDef", + "delete", + "get", + "head", + "options", + "patch", + "post", + "put", + "route", + "static", + "view", + # web_runner + "AppRunner", + "BaseRunner", + "BaseSite", + "GracefulExit", + "ServerRunner", + "SockSite", + "TCPSite", + "UnixSite", + "NamedPipeSite", + # web_server + "Server", + # web_urldispatcher + "AbstractResource", + "AbstractRoute", + "DynamicResource", + "PlainResource", + "PrefixedSubAppResource", + "Resource", + "ResourceRoute", + "StaticResource", + "UrlDispatcher", + "UrlMappingMatchInfo", + "View", + # web_ws + "WebSocketReady", + "WebSocketResponse", + "WSMsgType", + # web + "run_app", +) + + +try: + from ssl import SSLContext +except ImportError: # pragma: no cover + SSLContext = Any # type: ignore[misc,assignment] + +# Only display warning when using -Wdefault, -We, -X dev or similar. +warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True) + +HostSequence = TypingIterable[str] + + +async def _run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + shutdown_timeout: float = 60.0, + keepalive_timeout: float = 75.0, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log_format: str = AccessLogger.LOG_FORMAT, + access_log: Optional[logging.Logger] = access_logger, + handle_signals: bool = True, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + handler_cancellation: bool = False, +) -> None: + async def wait( + starting_tasks: "WeakSet[asyncio.Task[object]]", shutdown_timeout: float + ) -> None: + # Wait for pending tasks for a given time limit. + t = asyncio.current_task() + assert t is not None + starting_tasks.add(t) + with suppress(asyncio.TimeoutError): + await asyncio.wait_for(_wait(starting_tasks), timeout=shutdown_timeout) + + async def _wait(exclude: "WeakSet[asyncio.Task[object]]") -> None: + t = asyncio.current_task() + assert t is not None + exclude.add(t) + while tasks := asyncio.all_tasks().difference(exclude): + await asyncio.wait(tasks) + + # An internal function to actually do all dirty job for application running + if asyncio.iscoroutine(app): + app = await app + + app = cast(Application, app) + + runner = AppRunner( + app, + handle_signals=handle_signals, + access_log_class=access_log_class, + access_log_format=access_log_format, + access_log=access_log, + keepalive_timeout=keepalive_timeout, + shutdown_timeout=shutdown_timeout, + handler_cancellation=handler_cancellation, + ) + + await runner.setup() + # On shutdown we want to avoid waiting on tasks which run forever. + # It's very likely that all tasks which run forever will have been created by + # the time we have completed the application startup (in runner.setup()), + # so we just record all running tasks here and exclude them later. + starting_tasks: "WeakSet[asyncio.Task[object]]" = WeakSet(asyncio.all_tasks()) + runner.shutdown_callback = partial(wait, starting_tasks, shutdown_timeout) + + sites: List[BaseSite] = [] + + try: + if host is not None: + if isinstance(host, (str, bytes, bytearray, memoryview)): + sites.append( + TCPSite( + runner, + host, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + else: + for h in host: + sites.append( + TCPSite( + runner, + h, + port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + elif path is None and sock is None or port is not None: + sites.append( + TCPSite( + runner, + port=port, + ssl_context=ssl_context, + backlog=backlog, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + + if path is not None: + if isinstance(path, (str, os.PathLike)): + sites.append( + UnixSite( + runner, + path, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for p in path: + sites.append( + UnixSite( + runner, + p, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + + if sock is not None: + if not isinstance(sock, Iterable): + sites.append( + SockSite( + runner, + sock, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + else: + for s in sock: + sites.append( + SockSite( + runner, + s, + ssl_context=ssl_context, + backlog=backlog, + ) + ) + for site in sites: + await site.start() + + if print: # pragma: no branch + names = sorted(str(s.name) for s in runner.sites) + print( + "======== Running on {} ========\n" + "(Press CTRL+C to quit)".format(", ".join(names)) + ) + + # sleep forever by 1 hour intervals, + while True: + await asyncio.sleep(3600) + finally: + await runner.cleanup() + + +def _cancel_tasks( + to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop +) -> None: + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + +def run_app( + app: Union[Application, Awaitable[Application]], + *, + host: Optional[Union[str, HostSequence]] = None, + port: Optional[int] = None, + path: Union[PathLike, TypingIterable[PathLike], None] = None, + sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, + shutdown_timeout: float = 60.0, + keepalive_timeout: float = 75.0, + ssl_context: Optional[SSLContext] = None, + print: Optional[Callable[..., None]] = print, + backlog: int = 128, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log_format: str = AccessLogger.LOG_FORMAT, + access_log: Optional[logging.Logger] = access_logger, + handle_signals: bool = True, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + handler_cancellation: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, +) -> None: + """Run an app locally""" + if loop is None: + loop = asyncio.new_event_loop() + + # Configure if and only if in debugging mode and using the default logger + if loop.get_debug() and access_log and access_log.name == "aiohttp.access": + if access_log.level == logging.NOTSET: + access_log.setLevel(logging.DEBUG) + if not access_log.hasHandlers(): + access_log.addHandler(logging.StreamHandler()) + + main_task = loop.create_task( + _run_app( + app, + host=host, + port=port, + path=path, + sock=sock, + shutdown_timeout=shutdown_timeout, + keepalive_timeout=keepalive_timeout, + ssl_context=ssl_context, + print=print, + backlog=backlog, + access_log_class=access_log_class, + access_log_format=access_log_format, + access_log=access_log, + handle_signals=handle_signals, + reuse_address=reuse_address, + reuse_port=reuse_port, + handler_cancellation=handler_cancellation, + ) + ) + + try: + asyncio.set_event_loop(loop) + loop.run_until_complete(main_task) + except (GracefulExit, KeyboardInterrupt): # pragma: no cover + pass + finally: + _cancel_tasks({main_task}, loop) + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() + + +def main(argv: List[str]) -> None: + arg_parser = ArgumentParser( + description="aiohttp.web Application server", prog="aiohttp.web" + ) + arg_parser.add_argument( + "entry_func", + help=( + "Callable returning the `aiohttp.web.Application` instance to " + "run. Should be specified in the 'module:function' syntax." + ), + metavar="entry-func", + ) + arg_parser.add_argument( + "-H", + "--hostname", + help="TCP/IP hostname to serve on (default: %(default)r)", + default="localhost", + ) + arg_parser.add_argument( + "-P", + "--port", + help="TCP/IP port to serve on (default: %(default)r)", + type=int, + default="8080", + ) + arg_parser.add_argument( + "-U", + "--path", + help="Unix file system path to serve on. Specifying a path will cause " + "hostname and port arguments to be ignored.", + ) + args, extra_argv = arg_parser.parse_known_args(argv) + + # Import logic + mod_str, _, func_str = args.entry_func.partition(":") + if not func_str or not mod_str: + arg_parser.error("'entry-func' not in 'module:function' syntax") + if mod_str.startswith("."): + arg_parser.error("relative module names not supported") + try: + module = import_module(mod_str) + except ImportError as ex: + arg_parser.error(f"unable to import {mod_str}: {ex}") + try: + func = getattr(module, func_str) + except AttributeError: + arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}") + + # Compatibility logic + if args.path is not None and not hasattr(socket, "AF_UNIX"): + arg_parser.error( + "file system paths not supported by your operating" " environment" + ) + + logging.basicConfig(level=logging.DEBUG) + + app = func(extra_argv) + run_app(app, host=args.hostname, port=args.port, path=args.path) + arg_parser.exit(message="Stopped\n") + + +if __name__ == "__main__": # pragma: no branch + main(sys.argv[1:]) # pragma: no cover diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_app.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_app.py new file mode 100644 index 0000000..91bf5fd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_app.py @@ -0,0 +1,596 @@ +import asyncio +import logging +import warnings +from functools import partial, update_wrapper +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterator, + Awaitable, + Callable, + Dict, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) + +from aiosignal import Signal +from frozenlist import FrozenList + +from . import hdrs +from .abc import ( + AbstractAccessLogger, + AbstractMatchInfo, + AbstractRouter, + AbstractStreamWriter, +) +from .helpers import DEBUG, AppKey +from .http_parser import RawRequestMessage +from .log import web_logger +from .streams import StreamReader +from .typedefs import Middleware +from .web_exceptions import NotAppKeyWarning +from .web_log import AccessLogger +from .web_middlewares import _fix_request_current_app +from .web_protocol import RequestHandler +from .web_request import Request +from .web_response import StreamResponse +from .web_routedef import AbstractRouteDef +from .web_server import Server +from .web_urldispatcher import ( + AbstractResource, + AbstractRoute, + Domain, + MaskDomain, + MatchedSubAppResource, + PrefixedSubAppResource, + UrlDispatcher, +) + +__all__ = ("Application", "CleanupError") + + +if TYPE_CHECKING: + _AppSignal = Signal[Callable[["Application"], Awaitable[None]]] + _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]] + _Middlewares = FrozenList[Middleware] + _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]] + _Subapps = List["Application"] +else: + # No type checker mode, skip types + _AppSignal = Signal + _RespPrepareSignal = Signal + _Middlewares = FrozenList + _MiddlewaresHandlers = Optional[Sequence] + _Subapps = List + +_T = TypeVar("_T") +_U = TypeVar("_U") + + +class Application(MutableMapping[Union[str, AppKey[Any]], Any]): + ATTRS = frozenset( + [ + "logger", + "_debug", + "_router", + "_loop", + "_handler_args", + "_middlewares", + "_middlewares_handlers", + "_run_middlewares", + "_state", + "_frozen", + "_pre_frozen", + "_subapps", + "_on_response_prepare", + "_on_startup", + "_on_shutdown", + "_on_cleanup", + "_client_max_size", + "_cleanup_ctx", + ] + ) + + def __init__( + self, + *, + logger: logging.Logger = web_logger, + router: Optional[UrlDispatcher] = None, + middlewares: Iterable[Middleware] = (), + handler_args: Optional[Mapping[str, Any]] = None, + client_max_size: int = 1024**2, + loop: Optional[asyncio.AbstractEventLoop] = None, + debug: Any = ..., # mypy doesn't support ellipsis + ) -> None: + if router is None: + router = UrlDispatcher() + else: + warnings.warn( + "router argument is deprecated", DeprecationWarning, stacklevel=2 + ) + assert isinstance(router, AbstractRouter), router + + if loop is not None: + warnings.warn( + "loop argument is deprecated", DeprecationWarning, stacklevel=2 + ) + + if debug is not ...: + warnings.warn( + "debug argument is deprecated", DeprecationWarning, stacklevel=2 + ) + self._debug = debug + self._router: UrlDispatcher = router + self._loop = loop + self._handler_args = handler_args + self.logger = logger + + self._middlewares: _Middlewares = FrozenList(middlewares) + + # initialized on freezing + self._middlewares_handlers: _MiddlewaresHandlers = None + # initialized on freezing + self._run_middlewares: Optional[bool] = None + + self._state: Dict[Union[AppKey[Any], str], object] = {} + self._frozen = False + self._pre_frozen = False + self._subapps: _Subapps = [] + + self._on_response_prepare: _RespPrepareSignal = Signal(self) + self._on_startup: _AppSignal = Signal(self) + self._on_shutdown: _AppSignal = Signal(self) + self._on_cleanup: _AppSignal = Signal(self) + self._cleanup_ctx = CleanupContext() + self._on_startup.append(self._cleanup_ctx._on_startup) + self._on_cleanup.append(self._cleanup_ctx._on_cleanup) + self._client_max_size = client_max_size + + def __init_subclass__(cls: Type["Application"]) -> None: + warnings.warn( + "Inheritance class {} from web.Application " + "is discouraged".format(cls.__name__), + DeprecationWarning, + stacklevel=3, + ) + + if DEBUG: # pragma: no cover + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom web.Application.{} attribute " + "is discouraged".format(name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + # MutableMapping API + + def __eq__(self, other: object) -> bool: + return self is other + + @overload # type: ignore[override] + def __getitem__(self, key: AppKey[_T]) -> _T: + ... + + @overload + def __getitem__(self, key: str) -> Any: + ... + + def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: + return self._state[key] + + def _check_frozen(self) -> None: + if self._frozen: + warnings.warn( + "Changing state of started or joined " "application is deprecated", + DeprecationWarning, + stacklevel=3, + ) + + @overload # type: ignore[override] + def __setitem__(self, key: AppKey[_T], value: _T) -> None: + ... + + @overload + def __setitem__(self, key: str, value: Any) -> None: + ... + + def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None: + self._check_frozen() + if not isinstance(key, AppKey): + warnings.warn( + "It is recommended to use web.AppKey instances for keys.\n" + + "https://docs.aiohttp.org/en/stable/web_advanced.html" + + "#application-s-config", + category=NotAppKeyWarning, + stacklevel=2, + ) + self._state[key] = value + + def __delitem__(self, key: Union[str, AppKey[_T]]) -> None: + self._check_frozen() + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: + return iter(self._state) + + @overload # type: ignore[override] + def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: + ... + + @overload + def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: + ... + + @overload + def get(self, key: str, default: Any = ...) -> Any: + ... + + def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any: + return self._state.get(key, default) + + ######## + @property + def loop(self) -> asyncio.AbstractEventLoop: + # Technically the loop can be None + # but we mask it by explicit type cast + # to provide more convenient type annotation + warnings.warn("loop property is deprecated", DeprecationWarning, stacklevel=2) + return cast(asyncio.AbstractEventLoop, self._loop) + + def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None: + if loop is None: + loop = asyncio.get_event_loop() + if self._loop is not None and self._loop is not loop: + raise RuntimeError( + "web.Application instance initialized with different loop" + ) + + self._loop = loop + + # set loop debug + if self._debug is ...: + self._debug = loop.get_debug() + + # set loop to sub applications + for subapp in self._subapps: + subapp._set_loop(loop) + + @property + def pre_frozen(self) -> bool: + return self._pre_frozen + + def pre_freeze(self) -> None: + if self._pre_frozen: + return + + self._pre_frozen = True + self._middlewares.freeze() + self._router.freeze() + self._on_response_prepare.freeze() + self._cleanup_ctx.freeze() + self._on_startup.freeze() + self._on_shutdown.freeze() + self._on_cleanup.freeze() + self._middlewares_handlers = tuple(self._prepare_middleware()) + + # If current app and any subapp do not have middlewares avoid run all + # of the code footprint that it implies, which have a middleware + # hardcoded per app that sets up the current_app attribute. If no + # middlewares are configured the handler will receive the proper + # current_app without needing all of this code. + self._run_middlewares = True if self.middlewares else False + + for subapp in self._subapps: + subapp.pre_freeze() + self._run_middlewares = self._run_middlewares or subapp._run_middlewares + + @property + def frozen(self) -> bool: + return self._frozen + + def freeze(self) -> None: + if self._frozen: + return + + self.pre_freeze() + self._frozen = True + for subapp in self._subapps: + subapp.freeze() + + @property + def debug(self) -> bool: + warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2) + return self._debug # type: ignore[no-any-return] + + def _reg_subapp_signals(self, subapp: "Application") -> None: + def reg_handler(signame: str) -> None: + subsig = getattr(subapp, signame) + + async def handler(app: "Application") -> None: + await subsig.send(subapp) + + appsig = getattr(self, signame) + appsig.append(handler) + + reg_handler("on_startup") + reg_handler("on_shutdown") + reg_handler("on_cleanup") + + def add_subapp(self, prefix: str, subapp: "Application") -> AbstractResource: + if not isinstance(prefix, str): + raise TypeError("Prefix must be str") + prefix = prefix.rstrip("/") + if not prefix: + raise ValueError("Prefix cannot be empty") + factory = partial(PrefixedSubAppResource, prefix, subapp) + return self._add_subapp(factory, subapp) + + def _add_subapp( + self, resource_factory: Callable[[], AbstractResource], subapp: "Application" + ) -> AbstractResource: + if self.frozen: + raise RuntimeError("Cannot add sub application to frozen application") + if subapp.frozen: + raise RuntimeError("Cannot add frozen application") + resource = resource_factory() + self.router.register_resource(resource) + self._reg_subapp_signals(subapp) + self._subapps.append(subapp) + subapp.pre_freeze() + if self._loop is not None: + subapp._set_loop(self._loop) + return resource + + def add_domain(self, domain: str, subapp: "Application") -> AbstractResource: + if not isinstance(domain, str): + raise TypeError("Domain must be str") + elif "*" in domain: + rule: Domain = MaskDomain(domain) + else: + rule = Domain(domain) + factory = partial(MatchedSubAppResource, rule, subapp) + return self._add_subapp(factory, subapp) + + def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + return self.router.add_routes(routes) + + @property + def on_response_prepare(self) -> _RespPrepareSignal: + return self._on_response_prepare + + @property + def on_startup(self) -> _AppSignal: + return self._on_startup + + @property + def on_shutdown(self) -> _AppSignal: + return self._on_shutdown + + @property + def on_cleanup(self) -> _AppSignal: + return self._on_cleanup + + @property + def cleanup_ctx(self) -> "CleanupContext": + return self._cleanup_ctx + + @property + def router(self) -> UrlDispatcher: + return self._router + + @property + def middlewares(self) -> _Middlewares: + return self._middlewares + + def _make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + if not issubclass(access_log_class, AbstractAccessLogger): + raise TypeError( + "access_log_class must be subclass of " + "aiohttp.abc.AbstractAccessLogger, got {}".format(access_log_class) + ) + + self._set_loop(loop) + self.freeze() + + kwargs["debug"] = self._debug + kwargs["access_log_class"] = access_log_class + if self._handler_args: + for k, v in self._handler_args.items(): + kwargs[k] = v + + return Server( + self._handle, # type: ignore[arg-type] + request_factory=self._make_request, + loop=self._loop, + **kwargs, + ) + + def make_handler( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + **kwargs: Any, + ) -> Server: + + warnings.warn( + "Application.make_handler(...) is deprecated, " "use AppRunner API instead", + DeprecationWarning, + stacklevel=2, + ) + + return self._make_handler( + loop=loop, access_log_class=access_log_class, **kwargs + ) + + async def startup(self) -> None: + """Causes on_startup signal + + Should be called in the event loop along with the request handler. + """ + await self.on_startup.send(self) + + async def shutdown(self) -> None: + """Causes on_shutdown signal + + Should be called before cleanup() + """ + await self.on_shutdown.send(self) + + async def cleanup(self) -> None: + """Causes on_cleanup signal + + Should be called after shutdown() + """ + if self.on_cleanup.frozen: + await self.on_cleanup.send(self) + else: + # If an exception occurs in startup, ensure cleanup contexts are completed. + await self._cleanup_ctx._on_cleanup(self) + + def _make_request( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: RequestHandler, + writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + _cls: Type[Request] = Request, + ) -> Request: + return _cls( + message, + payload, + protocol, + writer, + task, + self._loop, + client_max_size=self._client_max_size, + ) + + def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]: + for m in reversed(self._middlewares): + if getattr(m, "__middleware_version__", None) == 1: + yield m, True + else: + warnings.warn( + 'old-style middleware "{!r}" deprecated, ' "see #2252".format(m), + DeprecationWarning, + stacklevel=2, + ) + yield m, False + + yield _fix_request_current_app(self), True + + async def _handle(self, request: Request) -> StreamResponse: + loop = asyncio.get_event_loop() + debug = loop.get_debug() + match_info = await self._router.resolve(request) + if debug: # pragma: no cover + if not isinstance(match_info, AbstractMatchInfo): + raise TypeError( + "match_info should be AbstractMatchInfo " + "instance, not {!r}".format(match_info) + ) + match_info.add_app(self) + + match_info.freeze() + + resp = None + request._match_info = match_info + expect = request.headers.get(hdrs.EXPECT) + if expect: + resp = await match_info.expect_handler(request) + await request.writer.drain() + + if resp is None: + handler = match_info.handler + + if self._run_middlewares: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] + + resp = await handler(request) + + return resp + + def __call__(self) -> "Application": + """gunicorn compatibility""" + return self + + def __repr__(self) -> str: + return f"" + + def __bool__(self) -> bool: + return True + + +class CleanupError(RuntimeError): + @property + def exceptions(self) -> List[BaseException]: + return cast(List[BaseException], self.args[1]) + + +if TYPE_CHECKING: + _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]] +else: + _CleanupContextBase = FrozenList + + +class CleanupContext(_CleanupContextBase): + def __init__(self) -> None: + super().__init__() + self._exits: List[AsyncIterator[None]] = [] + + async def _on_startup(self, app: Application) -> None: + for cb in self: + it = cb(app).__aiter__() + await it.__anext__() + self._exits.append(it) + + async def _on_cleanup(self, app: Application) -> None: + errors = [] + for it in reversed(self._exits): + try: + await it.__anext__() + except StopAsyncIteration: + pass + except Exception as exc: + errors.append(exc) + else: + errors.append(RuntimeError(f"{it!r} has more than one 'yield'")) + if errors: + if len(errors) == 1: + raise errors[0] + else: + raise CleanupError("Multiple errors on cleanup stage", errors) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_exceptions.py new file mode 100644 index 0000000..ee2c1e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_exceptions.py @@ -0,0 +1,452 @@ +import warnings +from typing import Any, Dict, Iterable, List, Optional, Set # noqa + +from yarl import URL + +from .typedefs import LooseHeaders, StrOrURL +from .web_response import Response + +__all__ = ( + "HTTPException", + "HTTPError", + "HTTPRedirection", + "HTTPSuccessful", + "HTTPOk", + "HTTPCreated", + "HTTPAccepted", + "HTTPNonAuthoritativeInformation", + "HTTPNoContent", + "HTTPResetContent", + "HTTPPartialContent", + "HTTPMove", + "HTTPMultipleChoices", + "HTTPMovedPermanently", + "HTTPFound", + "HTTPSeeOther", + "HTTPNotModified", + "HTTPUseProxy", + "HTTPTemporaryRedirect", + "HTTPPermanentRedirect", + "HTTPClientError", + "HTTPBadRequest", + "HTTPUnauthorized", + "HTTPPaymentRequired", + "HTTPForbidden", + "HTTPNotFound", + "HTTPMethodNotAllowed", + "HTTPNotAcceptable", + "HTTPProxyAuthenticationRequired", + "HTTPRequestTimeout", + "HTTPConflict", + "HTTPGone", + "HTTPLengthRequired", + "HTTPPreconditionFailed", + "HTTPRequestEntityTooLarge", + "HTTPRequestURITooLong", + "HTTPUnsupportedMediaType", + "HTTPRequestRangeNotSatisfiable", + "HTTPExpectationFailed", + "HTTPMisdirectedRequest", + "HTTPUnprocessableEntity", + "HTTPFailedDependency", + "HTTPUpgradeRequired", + "HTTPPreconditionRequired", + "HTTPTooManyRequests", + "HTTPRequestHeaderFieldsTooLarge", + "HTTPUnavailableForLegalReasons", + "HTTPServerError", + "HTTPInternalServerError", + "HTTPNotImplemented", + "HTTPBadGateway", + "HTTPServiceUnavailable", + "HTTPGatewayTimeout", + "HTTPVersionNotSupported", + "HTTPVariantAlsoNegotiates", + "HTTPInsufficientStorage", + "HTTPNotExtended", + "HTTPNetworkAuthenticationRequired", +) + + +class NotAppKeyWarning(UserWarning): + """Warning when not using AppKey in Application.""" + + +############################################################ +# HTTP Exceptions +############################################################ + + +class HTTPException(Response, Exception): + + # You should set in subclasses: + # status = 200 + + status_code = -1 + empty_body = False + + __http_exception__ = True + + def __init__( + self, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if body is not None: + warnings.warn( + "body argument is deprecated for http web exceptions", + DeprecationWarning, + ) + Response.__init__( + self, + status=self.status_code, + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + Exception.__init__(self, self.reason) + if self.body is None and not self.empty_body: + self.text = f"{self.status}: {self.reason}" + + def __bool__(self) -> bool: + return True + + +class HTTPError(HTTPException): + """Base class for exceptions with status codes in the 400s and 500s.""" + + +class HTTPRedirection(HTTPException): + """Base class for exceptions with status codes in the 300s.""" + + +class HTTPSuccessful(HTTPException): + """Base class for exceptions with status codes in the 200s.""" + + +class HTTPOk(HTTPSuccessful): + status_code = 200 + + +class HTTPCreated(HTTPSuccessful): + status_code = 201 + + +class HTTPAccepted(HTTPSuccessful): + status_code = 202 + + +class HTTPNonAuthoritativeInformation(HTTPSuccessful): + status_code = 203 + + +class HTTPNoContent(HTTPSuccessful): + status_code = 204 + empty_body = True + + +class HTTPResetContent(HTTPSuccessful): + status_code = 205 + empty_body = True + + +class HTTPPartialContent(HTTPSuccessful): + status_code = 206 + + +############################################################ +# 3xx redirection +############################################################ + + +class HTTPMove(HTTPRedirection): + def __init__( + self, + location: StrOrURL, + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + if not location: + raise ValueError("HTTP redirects need a location to redirect to.") + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Location"] = str(URL(location)) + self.location = location + + +class HTTPMultipleChoices(HTTPMove): + status_code = 300 + + +class HTTPMovedPermanently(HTTPMove): + status_code = 301 + + +class HTTPFound(HTTPMove): + status_code = 302 + + +# This one is safe after a POST (the redirected location will be +# retrieved with GET): +class HTTPSeeOther(HTTPMove): + status_code = 303 + + +class HTTPNotModified(HTTPRedirection): + # FIXME: this should include a date or etag header + status_code = 304 + empty_body = True + + +class HTTPUseProxy(HTTPMove): + # Not a move, but looks a little like one + status_code = 305 + + +class HTTPTemporaryRedirect(HTTPMove): + status_code = 307 + + +class HTTPPermanentRedirect(HTTPMove): + status_code = 308 + + +############################################################ +# 4xx client error +############################################################ + + +class HTTPClientError(HTTPError): + pass + + +class HTTPBadRequest(HTTPClientError): + status_code = 400 + + +class HTTPUnauthorized(HTTPClientError): + status_code = 401 + + +class HTTPPaymentRequired(HTTPClientError): + status_code = 402 + + +class HTTPForbidden(HTTPClientError): + status_code = 403 + + +class HTTPNotFound(HTTPClientError): + status_code = 404 + + +class HTTPMethodNotAllowed(HTTPClientError): + status_code = 405 + + def __init__( + self, + method: str, + allowed_methods: Iterable[str], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + allow = ",".join(sorted(allowed_methods)) + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self.headers["Allow"] = allow + self.allowed_methods: Set[str] = set(allowed_methods) + self.method = method.upper() + + +class HTTPNotAcceptable(HTTPClientError): + status_code = 406 + + +class HTTPProxyAuthenticationRequired(HTTPClientError): + status_code = 407 + + +class HTTPRequestTimeout(HTTPClientError): + status_code = 408 + + +class HTTPConflict(HTTPClientError): + status_code = 409 + + +class HTTPGone(HTTPClientError): + status_code = 410 + + +class HTTPLengthRequired(HTTPClientError): + status_code = 411 + + +class HTTPPreconditionFailed(HTTPClientError): + status_code = 412 + + +class HTTPRequestEntityTooLarge(HTTPClientError): + status_code = 413 + + def __init__(self, max_size: float, actual_size: float, **kwargs: Any) -> None: + kwargs.setdefault( + "text", + "Maximum request body size {} exceeded, " + "actual body size {}".format(max_size, actual_size), + ) + super().__init__(**kwargs) + + +class HTTPRequestURITooLong(HTTPClientError): + status_code = 414 + + +class HTTPUnsupportedMediaType(HTTPClientError): + status_code = 415 + + +class HTTPRequestRangeNotSatisfiable(HTTPClientError): + status_code = 416 + + +class HTTPExpectationFailed(HTTPClientError): + status_code = 417 + + +class HTTPMisdirectedRequest(HTTPClientError): + status_code = 421 + + +class HTTPUnprocessableEntity(HTTPClientError): + status_code = 422 + + +class HTTPFailedDependency(HTTPClientError): + status_code = 424 + + +class HTTPUpgradeRequired(HTTPClientError): + status_code = 426 + + +class HTTPPreconditionRequired(HTTPClientError): + status_code = 428 + + +class HTTPTooManyRequests(HTTPClientError): + status_code = 429 + + +class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): + status_code = 431 + + +class HTTPUnavailableForLegalReasons(HTTPClientError): + status_code = 451 + + def __init__( + self, + link: Optional[StrOrURL], + *, + headers: Optional[LooseHeaders] = None, + reason: Optional[str] = None, + body: Any = None, + text: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + super().__init__( + headers=headers, + reason=reason, + body=body, + text=text, + content_type=content_type, + ) + self._link = None + if link: + self._link = URL(link) + self.headers["Link"] = f'<{str(self._link)}>; rel="blocked-by"' + + @property + def link(self) -> Optional[URL]: + return self._link + + +############################################################ +# 5xx Server Error +############################################################ +# Response status codes beginning with the digit "5" indicate cases in +# which the server is aware that it has erred or is incapable of +# performing the request. Except when responding to a HEAD request, the +# server SHOULD include an entity containing an explanation of the error +# situation, and whether it is a temporary or permanent condition. User +# agents SHOULD display any included entity to the user. These response +# codes are applicable to any request method. + + +class HTTPServerError(HTTPError): + pass + + +class HTTPInternalServerError(HTTPServerError): + status_code = 500 + + +class HTTPNotImplemented(HTTPServerError): + status_code = 501 + + +class HTTPBadGateway(HTTPServerError): + status_code = 502 + + +class HTTPServiceUnavailable(HTTPServerError): + status_code = 503 + + +class HTTPGatewayTimeout(HTTPServerError): + status_code = 504 + + +class HTTPVersionNotSupported(HTTPServerError): + status_code = 505 + + +class HTTPVariantAlsoNegotiates(HTTPServerError): + status_code = 506 + + +class HTTPInsufficientStorage(HTTPServerError): + status_code = 507 + + +class HTTPNotExtended(HTTPServerError): + status_code = 510 + + +class HTTPNetworkAuthenticationRequired(HTTPServerError): + status_code = 511 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_fileresponse.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_fileresponse.py new file mode 100644 index 0000000..7dbe50f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_fileresponse.py @@ -0,0 +1,305 @@ +import asyncio +import mimetypes +import os +import pathlib +from typing import ( # noqa + IO, + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Final, + Iterator, + List, + Optional, + Tuple, + Union, + cast, +) + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ETAG_ANY, ETag, must_be_empty_body +from .typedefs import LooseHeaders, PathLike +from .web_exceptions import ( + HTTPNotModified, + HTTPPartialContent, + HTTPPreconditionFailed, + HTTPRequestRangeNotSatisfiable, +) +from .web_response import StreamResponse + +__all__ = ("FileResponse",) + +if TYPE_CHECKING: + from .web_request import BaseRequest + + +_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]] + + +NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE")) + + +class FileResponse(StreamResponse): + """A response object can be used to send files.""" + + def __init__( + self, + path: PathLike, + chunk_size: int = 256 * 1024, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + ) -> None: + super().__init__(status=status, reason=reason, headers=headers) + + self._path = pathlib.Path(path) + self._chunk_size = chunk_size + + async def _sendfile_fallback( + self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + # To keep memory usage low,fobj is transferred in chunks + # controlled by the constructor's chunk_size argument. + + chunk_size = self._chunk_size + loop = asyncio.get_event_loop() + + await loop.run_in_executor(None, fobj.seek, offset) + + chunk = await loop.run_in_executor(None, fobj.read, chunk_size) + while chunk: + await writer.write(chunk) + count = count - chunk_size + if count <= 0: + break + chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count)) + + await writer.drain() + return writer + + async def _sendfile( + self, request: "BaseRequest", fobj: IO[Any], offset: int, count: int + ) -> AbstractStreamWriter: + writer = await super().prepare(request) + assert writer is not None + + if NOSENDFILE or self.compression: + return await self._sendfile_fallback(writer, fobj, offset, count) + + loop = request._loop + transport = request.transport + assert transport is not None + + try: + await loop.sendfile(transport, fobj, offset, count) + except NotImplementedError: + return await self._sendfile_fallback(writer, fobj, offset, count) + + await super().write_eof() + return writer + + @staticmethod + def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool: + if len(etags) == 1 and etags[0].value == ETAG_ANY: + return True + return any(etag.value == etag_value for etag in etags if not etag.is_weak) + + async def _not_modified( + self, request: "BaseRequest", etag_value: str, last_modified: float + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPNotModified.status_code) + self._length_check = False + self.etag = etag_value # type: ignore[assignment] + self.last_modified = last_modified # type: ignore[assignment] + # Delete any Content-Length headers provided by user. HTTP 304 + # should always have empty response body + return await super().prepare(request) + + async def _precondition_failed( + self, request: "BaseRequest" + ) -> Optional[AbstractStreamWriter]: + self.set_status(HTTPPreconditionFailed.status_code) + self.content_length = 0 + return await super().prepare(request) + + def _get_file_path_stat_and_gzip( + self, check_for_gzipped_file: bool + ) -> Tuple[pathlib.Path, os.stat_result, bool]: + """Return the file path, stat result, and gzip status. + + This method should be called from a thread executor + since it calls os.stat which may block. + """ + filepath = self._path + if check_for_gzipped_file: + gzip_path = filepath.with_name(filepath.name + ".gz") + try: + return gzip_path, gzip_path.stat(), True + except OSError: + # Fall through and try the non-gzipped file + pass + + return filepath, filepath.stat(), False + + async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: + loop = asyncio.get_event_loop() + # Encoding comparisons should be case-insensitive + # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 + check_for_gzipped_file = ( + "gzip" in request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() + ) + filepath, st, gzip = await loop.run_in_executor( + None, self._get_file_path_stat_and_gzip, check_for_gzipped_file + ) + + etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" + last_modified = st.st_mtime + + # https://tools.ietf.org/html/rfc7232#section-6 + ifmatch = request.if_match + if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch): + return await self._precondition_failed(request) + + unmodsince = request.if_unmodified_since + if ( + unmodsince is not None + and ifmatch is None + and st.st_mtime > unmodsince.timestamp() + ): + return await self._precondition_failed(request) + + ifnonematch = request.if_none_match + if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch): + return await self._not_modified(request, etag_value, last_modified) + + modsince = request.if_modified_since + if ( + modsince is not None + and ifnonematch is None + and st.st_mtime <= modsince.timestamp() + ): + return await self._not_modified(request, etag_value, last_modified) + + if hdrs.CONTENT_TYPE not in self.headers: + ct, encoding = mimetypes.guess_type(str(filepath)) + if not ct: + ct = "application/octet-stream" + should_set_ct = True + else: + encoding = "gzip" if gzip else None + should_set_ct = False + + status = self._status + file_size = st.st_size + count = file_size + + start = None + + ifrange = request.if_range + if ifrange is None or st.st_mtime <= ifrange.timestamp(): + # If-Range header check: + # condition = cached date >= last modification date + # return 206 if True else 200. + # if False: + # Range header would not be processed, return 200 + # if True but Range header missing + # return 200 + try: + rng = request.http_range + start = rng.start + end = rng.stop + except ValueError: + # https://tools.ietf.org/html/rfc7233: + # A server generating a 416 (Range Not Satisfiable) response to + # a byte-range request SHOULD send a Content-Range header field + # with an unsatisfied-range value. + # The complete-length in a 416 response indicates the current + # length of the selected representation. + # + # Will do the same below. Many servers ignore this and do not + # send a Content-Range header with HTTP 416 + self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + # If a range request has been made, convert start, end slice + # notation into file pointer offset and count + if start is not None or end is not None: + if start < 0 and end is None: # return tail of file + start += file_size + if start < 0: + # if Range:bytes=-1000 in request header but file size + # is only 200, there would be trouble without this + start = 0 + count = file_size - start + else: + # rfc7233:If the last-byte-pos value is + # absent, or if the value is greater than or equal to + # the current length of the representation data, + # the byte range is interpreted as the remainder + # of the representation (i.e., the server replaces the + # value of last-byte-pos with a value that is one less than + # the current length of the selected representation). + count = ( + min(end if end is not None else file_size, file_size) - start + ) + + if start >= file_size: + # HTTP 416 should be returned in this case. + # + # According to https://tools.ietf.org/html/rfc7233: + # If a valid byte-range-set includes at least one + # byte-range-spec with a first-byte-pos that is less than + # the current length of the representation, or at least one + # suffix-byte-range-spec with a non-zero suffix-length, + # then the byte-range-set is satisfiable. Otherwise, the + # byte-range-set is unsatisfiable. + self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}" + self.set_status(HTTPRequestRangeNotSatisfiable.status_code) + return await super().prepare(request) + + status = HTTPPartialContent.status_code + # Even though you are sending the whole file, you should still + # return a HTTP 206 for a Range request. + self.set_status(status) + + if should_set_ct: + self.content_type = ct # type: ignore[assignment] + if encoding: + self.headers[hdrs.CONTENT_ENCODING] = encoding + if gzip: + self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING + # Disable compression if we are already sending + # a compressed file since we don't want to double + # compress. + self._compression = False + + self.etag = etag_value # type: ignore[assignment] + self.last_modified = st.st_mtime # type: ignore[assignment] + self.content_length = count + + self.headers[hdrs.ACCEPT_RANGES] = "bytes" + + real_start = cast(int, start) + + if status == HTTPPartialContent.status_code: + self.headers[hdrs.CONTENT_RANGE] = "bytes {}-{}/{}".format( + real_start, real_start + count - 1, file_size + ) + + # If we are sending 0 bytes calling sendfile() will throw a ValueError + if count == 0 or must_be_empty_body(request.method, self.status): + return await super().prepare(request) + + fobj = await loop.run_in_executor(None, filepath.open, "rb") + if start: # be aware that start could be None or int=0 here. + offset = start + else: + offset = 0 + + try: + return await self._sendfile(request, fobj, offset, count) + finally: + await asyncio.shield(loop.run_in_executor(None, fobj.close)) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_log.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_log.py new file mode 100644 index 0000000..633e9e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_log.py @@ -0,0 +1,213 @@ +import datetime +import functools +import logging +import os +import re +import time as time_mod +from collections import namedtuple +from typing import Any, Callable, Dict, Iterable, List, Tuple # noqa + +from .abc import AbstractAccessLogger +from .web_request import BaseRequest +from .web_response import StreamResponse + +KeyMethod = namedtuple("KeyMethod", "key method") + + +class AccessLogger(AbstractAccessLogger): + """Helper object to log access. + + Usage: + log = logging.getLogger("spam") + log_format = "%a %{User-Agent}i" + access_logger = AccessLogger(log, log_format) + access_logger.log(request, response, time) + + Format: + %% The percent sign + %a Remote IP-address (IP-address of proxy if using reverse proxy) + %t Time when the request was started to process + %P The process ID of the child that serviced the request + %r First line of request + %s Response status code + %b Size of response in bytes, including HTTP headers + %T Time taken to serve the request, in seconds + %Tf Time taken to serve the request, in seconds with floating fraction + in .06f format + %D Time taken to serve the request, in microseconds + %{FOO}i request.headers['FOO'] + %{FOO}o response.headers['FOO'] + %{FOO}e os.environ['FOO'] + + """ + + LOG_FORMAT_MAP = { + "a": "remote_address", + "t": "request_start_time", + "P": "process_id", + "r": "first_request_line", + "s": "response_status", + "b": "response_size", + "T": "request_time", + "Tf": "request_time_frac", + "D": "request_time_micro", + "i": "request_header", + "o": "response_header", + } + + LOG_FORMAT = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"' + FORMAT_RE = re.compile(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbOD]|Tf?)") + CLEANUP_RE = re.compile(r"(%[^s])") + _FORMAT_CACHE: Dict[str, Tuple[str, List[KeyMethod]]] = {} + + def __init__(self, logger: logging.Logger, log_format: str = LOG_FORMAT) -> None: + """Initialise the logger. + + logger is a logger object to be used for logging. + log_format is a string with apache compatible log format description. + + """ + super().__init__(logger, log_format=log_format) + + _compiled_format = AccessLogger._FORMAT_CACHE.get(log_format) + if not _compiled_format: + _compiled_format = self.compile_format(log_format) + AccessLogger._FORMAT_CACHE[log_format] = _compiled_format + + self._log_format, self._methods = _compiled_format + + def compile_format(self, log_format: str) -> Tuple[str, List[KeyMethod]]: + """Translate log_format into form usable by modulo formatting + + All known atoms will be replaced with %s + Also methods for formatting of those atoms will be added to + _methods in appropriate order + + For example we have log_format = "%a %t" + This format will be translated to "%s %s" + Also contents of _methods will be + [self._format_a, self._format_t] + These method will be called and results will be passed + to translated string format. + + Each _format_* method receive 'args' which is list of arguments + given to self.log + + Exceptions are _format_e, _format_i and _format_o methods which + also receive key name (by functools.partial) + + """ + # list of (key, method) tuples, we don't use an OrderedDict as users + # can repeat the same key more than once + methods = list() + + for atom in self.FORMAT_RE.findall(log_format): + if atom[1] == "": + format_key1 = self.LOG_FORMAT_MAP[atom[0]] + m = getattr(AccessLogger, "_format_%s" % atom[0]) + key_method = KeyMethod(format_key1, m) + else: + format_key2 = (self.LOG_FORMAT_MAP[atom[2]], atom[1]) + m = getattr(AccessLogger, "_format_%s" % atom[2]) + key_method = KeyMethod(format_key2, functools.partial(m, atom[1])) + + methods.append(key_method) + + log_format = self.FORMAT_RE.sub(r"%s", log_format) + log_format = self.CLEANUP_RE.sub(r"%\1", log_format) + return log_format, methods + + @staticmethod + def _format_i( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + if request is None: + return "(no headers)" + + # suboptimal, make istr(key) once + return request.headers.get(key, "-") + + @staticmethod + def _format_o( + key: str, request: BaseRequest, response: StreamResponse, time: float + ) -> str: + # suboptimal, make istr(key) once + return response.headers.get(key, "-") + + @staticmethod + def _format_a(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + ip = request.remote + return ip if ip is not None else "-" + + @staticmethod + def _format_t(request: BaseRequest, response: StreamResponse, time: float) -> str: + tz = datetime.timezone(datetime.timedelta(seconds=-time_mod.timezone)) + now = datetime.datetime.now(tz) + start_time = now - datetime.timedelta(seconds=time) + return start_time.strftime("[%d/%b/%Y:%H:%M:%S %z]") + + @staticmethod + def _format_P(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "<%s>" % os.getpid() + + @staticmethod + def _format_r(request: BaseRequest, response: StreamResponse, time: float) -> str: + if request is None: + return "-" + return "{} {} HTTP/{}.{}".format( + request.method, + request.path_qs, + request.version.major, + request.version.minor, + ) + + @staticmethod + def _format_s(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.status + + @staticmethod + def _format_b(request: BaseRequest, response: StreamResponse, time: float) -> int: + return response.body_length + + @staticmethod + def _format_T(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time)) + + @staticmethod + def _format_Tf(request: BaseRequest, response: StreamResponse, time: float) -> str: + return "%06f" % time + + @staticmethod + def _format_D(request: BaseRequest, response: StreamResponse, time: float) -> str: + return str(round(time * 1000000)) + + def _format_line( + self, request: BaseRequest, response: StreamResponse, time: float + ) -> Iterable[Tuple[str, Callable[[BaseRequest, StreamResponse, float], str]]]: + return [(key, method(request, response, time)) for key, method in self._methods] + + def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: + if not self.logger.isEnabledFor(logging.INFO): + # Avoid formatting the log line if it will not be emitted. + return + try: + fmt_info = self._format_line(request, response, time) + + values = list() + extra = dict() + for key, value in fmt_info: + values.append(value) + + if key.__class__ is str: + extra[key] = value + else: + k1, k2 = key # type: ignore[misc] + dct = extra.get(k1, {}) # type: ignore[var-annotated,has-type] + dct[k2] = value # type: ignore[index,has-type] + extra[k1] = dct # type: ignore[has-type,assignment] + + self.logger.info(self._log_format % tuple(values), extra=extra) + except Exception: + self.logger.exception("Error in logging") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_middlewares.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_middlewares.py new file mode 100644 index 0000000..5da1533 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_middlewares.py @@ -0,0 +1,116 @@ +import re +from typing import TYPE_CHECKING, Tuple, Type, TypeVar + +from .typedefs import Handler, Middleware +from .web_exceptions import HTTPMove, HTTPPermanentRedirect +from .web_request import Request +from .web_response import StreamResponse +from .web_urldispatcher import SystemRoute + +__all__ = ( + "middleware", + "normalize_path_middleware", +) + +if TYPE_CHECKING: + from .web_app import Application + +_Func = TypeVar("_Func") + + +async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Request]: + alt_request = request.clone(rel_url=path) + + match_info = await request.app.router.resolve(alt_request) + alt_request._match_info = match_info + + if match_info.http_exception is None: + return True, alt_request + + return False, request + + +def middleware(f: _Func) -> _Func: + f.__middleware_version__ = 1 # type: ignore[attr-defined] + return f + + +def normalize_path_middleware( + *, + append_slash: bool = True, + remove_slash: bool = False, + merge_slashes: bool = True, + redirect_class: Type[HTTPMove] = HTTPPermanentRedirect, +) -> Middleware: + """Factory for producing a middleware that normalizes the path of a request. + + Normalizing means: + - Add or remove a trailing slash to the path. + - Double slashes are replaced by one. + + The middleware returns as soon as it finds a path that resolves + correctly. The order if both merge and append/remove are enabled is + 1) merge slashes + 2) append/remove slash + 3) both merge slashes and append/remove slash. + If the path resolves with at least one of those conditions, it will + redirect to the new path. + + Only one of `append_slash` and `remove_slash` can be enabled. If both + are `True` the factory will raise an assertion error + + If `append_slash` is `True` the middleware will append a slash when + needed. If a resource is defined with trailing slash and the request + comes without it, it will append it automatically. + + If `remove_slash` is `True`, `append_slash` must be `False`. When enabled + the middleware will remove trailing slashes and redirect if the resource + is defined + + If merge_slashes is True, merge multiple consecutive slashes in the + path into one. + """ + correct_configuration = not (append_slash and remove_slash) + assert correct_configuration, "Cannot both remove and append slash" + + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + if isinstance(request.match_info.route, SystemRoute): + paths_to_check = [] + if "?" in request.raw_path: + path, query = request.raw_path.split("?", 1) + query = "?" + query + else: + query = "" + path = request.raw_path + + if merge_slashes: + paths_to_check.append(re.sub("//+", "/", path)) + if append_slash and not request.path.endswith("/"): + paths_to_check.append(path + "/") + if remove_slash and request.path.endswith("/"): + paths_to_check.append(path[:-1]) + if merge_slashes and append_slash: + paths_to_check.append(re.sub("//+", "/", path + "/")) + if merge_slashes and remove_slash: + merged_slashes = re.sub("//+", "/", path) + paths_to_check.append(merged_slashes[:-1]) + + for path in paths_to_check: + path = re.sub("^//+", "/", path) # SECURITY: GHSA-v6wp-4m6f-gcjg + resolves, request = await _check_request_resolves(request, path) + if resolves: + raise redirect_class(request.raw_path + query) + + return await handler(request) + + return impl + + +def _fix_request_current_app(app: "Application") -> Middleware: + @middleware + async def impl(request: Request, handler: Handler) -> StreamResponse: + with request.match_info.set_current_app(app): + return await handler(request) + + return impl diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_protocol.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_protocol.py new file mode 100644 index 0000000..f083b13 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_protocol.py @@ -0,0 +1,698 @@ +import asyncio +import asyncio.streams +import traceback +import warnings +from collections import deque +from contextlib import suppress +from html import escape as html_escape +from http import HTTPStatus +from logging import Logger +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Deque, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) + +import attr +import yarl + +from .abc import AbstractAccessLogger, AbstractStreamWriter +from .base_protocol import BaseProtocol +from .helpers import ceil_timeout, set_exception +from .http import ( + HttpProcessingError, + HttpRequestParser, + HttpVersion10, + RawRequestMessage, + StreamWriter, +) +from .log import access_logger, server_logger +from .streams import EMPTY_PAYLOAD, StreamReader +from .tcp_helpers import tcp_keepalive +from .web_exceptions import HTTPException +from .web_log import AccessLogger +from .web_request import BaseRequest +from .web_response import Response, StreamResponse + +__all__ = ("RequestHandler", "RequestPayloadError", "PayloadAccessError") + +if TYPE_CHECKING: + from .web_server import Server + + +_RequestFactory = Callable[ + [ + RawRequestMessage, + StreamReader, + "RequestHandler", + AbstractStreamWriter, + "asyncio.Task[None]", + ], + BaseRequest, +] + +_RequestHandler = Callable[[BaseRequest], Awaitable[StreamResponse]] + +ERROR = RawRequestMessage( + "UNKNOWN", + "/", + HttpVersion10, + {}, # type: ignore[arg-type] + {}, # type: ignore[arg-type] + True, + None, + False, + False, + yarl.URL("/"), +) + + +class RequestPayloadError(Exception): + """Payload parsing error.""" + + +class PayloadAccessError(Exception): + """Payload was accessed after response was sent.""" + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class _ErrInfo: + status: int + exc: BaseException + message: str + + +_MsgType = Tuple[Union[RawRequestMessage, _ErrInfo], StreamReader] + + +class RequestHandler(BaseProtocol): + """HTTP protocol implementation. + + RequestHandler handles incoming HTTP request. It reads request line, + request headers and request payload and calls handle_request() method. + By default it always returns with 404 response. + + RequestHandler handles errors in incoming request, like bad + status line, bad headers or incomplete payload. If any error occurs, + connection gets closed. + + keepalive_timeout -- number of seconds before closing + keep-alive connection + + tcp_keepalive -- TCP keep-alive is on, default is on + + debug -- enable debug mode + + logger -- custom logger object + + access_log_class -- custom class for access_logger + + access_log -- custom logging object + + access_log_format -- access log format string + + loop -- Optional event loop + + max_line_size -- Optional maximum header line size + + max_field_size -- Optional maximum header field size + + max_headers -- Optional maximum header size + + timeout_ceil_threshold -- Optional value to specify + threshold to ceil() timeout + values + + """ + + KEEPALIVE_RESCHEDULE_DELAY = 1 + + __slots__ = ( + "_request_count", + "_keepalive", + "_manager", + "_request_handler", + "_request_factory", + "_tcp_keepalive", + "_keepalive_time", + "_keepalive_handle", + "_keepalive_timeout", + "_lingering_time", + "_messages", + "_message_tail", + "_waiter", + "_task_handler", + "_upgrade", + "_payload_parser", + "_request_parser", + "_reading_paused", + "logger", + "debug", + "access_log", + "access_logger", + "_close", + "_force_close", + "_current_request", + "_timeout_ceil_threshold", + ) + + def __init__( + self, + manager: "Server", + *, + loop: asyncio.AbstractEventLoop, + keepalive_timeout: float = 75.0, # NGINX default is 75 secs + tcp_keepalive: bool = True, + logger: Logger = server_logger, + access_log_class: Type[AbstractAccessLogger] = AccessLogger, + access_log: Logger = access_logger, + access_log_format: str = AccessLogger.LOG_FORMAT, + debug: bool = False, + max_line_size: int = 8190, + max_headers: int = 32768, + max_field_size: int = 8190, + lingering_time: float = 10.0, + read_bufsize: int = 2**16, + auto_decompress: bool = True, + timeout_ceil_threshold: float = 5, + ): + super().__init__(loop) + + self._request_count = 0 + self._keepalive = False + self._current_request: Optional[BaseRequest] = None + self._manager: Optional[Server] = manager + self._request_handler: Optional[_RequestHandler] = manager.request_handler + self._request_factory: Optional[_RequestFactory] = manager.request_factory + + self._tcp_keepalive = tcp_keepalive + # placeholder to be replaced on keepalive timeout setup + self._keepalive_time = 0.0 + self._keepalive_handle: Optional[asyncio.Handle] = None + self._keepalive_timeout = keepalive_timeout + self._lingering_time = float(lingering_time) + + self._messages: Deque[_MsgType] = deque() + self._message_tail = b"" + + self._waiter: Optional[asyncio.Future[None]] = None + self._task_handler: Optional[asyncio.Task[None]] = None + + self._upgrade = False + self._payload_parser: Any = None + self._request_parser: Optional[HttpRequestParser] = HttpRequestParser( + self, + loop, + read_bufsize, + max_line_size=max_line_size, + max_field_size=max_field_size, + max_headers=max_headers, + payload_exception=RequestPayloadError, + auto_decompress=auto_decompress, + ) + + self._timeout_ceil_threshold: float = 5 + try: + self._timeout_ceil_threshold = float(timeout_ceil_threshold) + except (TypeError, ValueError): + pass + + self.logger = logger + self.debug = debug + self.access_log = access_log + if access_log: + self.access_logger: Optional[AbstractAccessLogger] = access_log_class( + access_log, access_log_format + ) + else: + self.access_logger = None + + self._close = False + self._force_close = False + + def __repr__(self) -> str: + return "<{} {}>".format( + self.__class__.__name__, + "connected" if self.transport is not None else "disconnected", + ) + + @property + def keepalive_timeout(self) -> float: + return self._keepalive_timeout + + async def shutdown(self, timeout: Optional[float] = 15.0) -> None: + """Do worker process exit preparations. + + We need to clean up everything and stop accepting requests. + It is especially important for keep-alive connections. + """ + self._force_close = True + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + if self._waiter: + self._waiter.cancel() + + # wait for handlers + with suppress(asyncio.CancelledError, asyncio.TimeoutError): + async with ceil_timeout(timeout): + if self._current_request is not None: + self._current_request._cancel(asyncio.CancelledError()) + + if self._task_handler is not None and not self._task_handler.done(): + await self._task_handler + + # force-close non-idle handler + if self._task_handler is not None: + self._task_handler.cancel() + + if self.transport is not None: + self.transport.close() + self.transport = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + super().connection_made(transport) + + real_transport = cast(asyncio.Transport, transport) + if self._tcp_keepalive: + tcp_keepalive(real_transport) + + self._task_handler = self._loop.create_task(self.start()) + assert self._manager is not None + self._manager.connection_made(self, real_transport) + + def connection_lost(self, exc: Optional[BaseException]) -> None: + if self._manager is None: + return + self._manager.connection_lost(self, exc) + + super().connection_lost(exc) + + # Grab value before setting _manager to None. + handler_cancellation = self._manager.handler_cancellation + + self._manager = None + self._force_close = True + self._request_factory = None + self._request_handler = None + self._request_parser = None + + if self._keepalive_handle is not None: + self._keepalive_handle.cancel() + + if self._current_request is not None: + if exc is None: + exc = ConnectionResetError("Connection lost") + self._current_request._cancel(exc) + + if self._waiter is not None: + self._waiter.cancel() + + if handler_cancellation and self._task_handler is not None: + self._task_handler.cancel() + + self._task_handler = None + + if self._payload_parser is not None: + self._payload_parser.feed_eof() + self._payload_parser = None + + def set_parser(self, parser: Any) -> None: + # Actual type is WebReader + assert self._payload_parser is None + + self._payload_parser = parser + + if self._message_tail: + self._payload_parser.feed_data(self._message_tail) + self._message_tail = b"" + + def eof_received(self) -> None: + pass + + def data_received(self, data: bytes) -> None: + if self._force_close or self._close: + return + # parse http messages + messages: Sequence[_MsgType] + if self._payload_parser is None and not self._upgrade: + assert self._request_parser is not None + try: + messages, upgraded, tail = self._request_parser.feed_data(data) + except HttpProcessingError as exc: + messages = [ + (_ErrInfo(status=400, exc=exc, message=exc.message), EMPTY_PAYLOAD) + ] + upgraded = False + tail = b"" + + for msg, payload in messages or (): + self._request_count += 1 + self._messages.append((msg, payload)) + + waiter = self._waiter + if messages and waiter is not None and not waiter.done(): + # don't set result twice + waiter.set_result(None) + + self._upgrade = upgraded + if upgraded and tail: + self._message_tail = tail + + # no parser, just store + elif self._payload_parser is None and self._upgrade and data: + self._message_tail += data + + # feed payload + elif data: + eof, tail = self._payload_parser.feed_data(data) + if eof: + self.close() + + def keep_alive(self, val: bool) -> None: + """Set keep-alive connection mode. + + :param bool val: new state. + """ + self._keepalive = val + if self._keepalive_handle: + self._keepalive_handle.cancel() + self._keepalive_handle = None + + def close(self) -> None: + """Close connection. + + Stop accepting new pipelining messages and close + connection when handlers done processing messages. + """ + self._close = True + if self._waiter: + self._waiter.cancel() + + def force_close(self) -> None: + """Forcefully close connection.""" + self._force_close = True + if self._waiter: + self._waiter.cancel() + if self.transport is not None: + self.transport.close() + self.transport = None + + def log_access( + self, request: BaseRequest, response: StreamResponse, time: float + ) -> None: + if self.access_logger is not None: + self.access_logger.log(request, response, self._loop.time() - time) + + def log_debug(self, *args: Any, **kw: Any) -> None: + if self.debug: + self.logger.debug(*args, **kw) + + def log_exception(self, *args: Any, **kw: Any) -> None: + self.logger.exception(*args, **kw) + + def _process_keepalive(self) -> None: + if self._force_close or not self._keepalive: + return + + next = self._keepalive_time + self._keepalive_timeout + + # handler in idle state + if self._waiter: + if self._loop.time() > next: + self.force_close() + return + + # not all request handlers are done, + # reschedule itself to next second + self._keepalive_handle = self._loop.call_later( + self.KEEPALIVE_RESCHEDULE_DELAY, + self._process_keepalive, + ) + + async def _handle_request( + self, + request: BaseRequest, + start_time: float, + request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]], + ) -> Tuple[StreamResponse, bool]: + assert self._request_handler is not None + try: + try: + self._current_request = request + resp = await request_handler(request) + finally: + self._current_request = None + except HTTPException as exc: + resp = exc + reset = await self.finish_response(request, resp, start_time) + except asyncio.CancelledError: + raise + except asyncio.TimeoutError as exc: + self.log_debug("Request handler timed out.", exc_info=exc) + resp = self.handle_error(request, 504) + reset = await self.finish_response(request, resp, start_time) + except Exception as exc: + resp = self.handle_error(request, 500, exc) + reset = await self.finish_response(request, resp, start_time) + else: + # Deprecation warning (See #2415) + if getattr(resp, "__http_exception__", False): + warnings.warn( + "returning HTTPException object is deprecated " + "(#2415) and will be removed, " + "please raise the exception instead", + DeprecationWarning, + ) + + reset = await self.finish_response(request, resp, start_time) + + return resp, reset + + async def start(self) -> None: + """Process incoming request. + + It reads request line, request headers and request payload, then + calls handle_request() method. Subclass has to override + handle_request(). start() handles various exceptions in request + or response handling. Connection is being closed always unless + keep_alive(True) specified. + """ + loop = self._loop + handler = self._task_handler + assert handler is not None + manager = self._manager + assert manager is not None + keepalive_timeout = self._keepalive_timeout + resp = None + assert self._request_factory is not None + assert self._request_handler is not None + + while not self._force_close: + if not self._messages: + try: + # wait for next request + self._waiter = loop.create_future() + await self._waiter + except asyncio.CancelledError: + break + finally: + self._waiter = None + + message, payload = self._messages.popleft() + + start = loop.time() + + manager.requests_count += 1 + writer = StreamWriter(self, loop) + if isinstance(message, _ErrInfo): + # make request_factory work + request_handler = self._make_error_handler(message) + message = ERROR + else: + request_handler = self._request_handler + + request = self._request_factory(message, payload, self, writer, handler) + try: + # a new task is used for copy context vars (#3406) + task = self._loop.create_task( + self._handle_request(request, start, request_handler) + ) + try: + resp, reset = await task + except (asyncio.CancelledError, ConnectionError): + self.log_debug("Ignored premature client disconnection") + break + + # Drop the processed task from asyncio.Task.all_tasks() early + del task + if reset: + self.log_debug("Ignored premature client disconnection 2") + break + + # notify server about keep-alive + self._keepalive = bool(resp.keep_alive) + + # check payload + if not payload.is_eof(): + lingering_time = self._lingering_time + if not self._force_close and lingering_time: + self.log_debug( + "Start lingering close timer for %s sec.", lingering_time + ) + + now = loop.time() + end_t = now + lingering_time + + with suppress(asyncio.TimeoutError, asyncio.CancelledError): + while not payload.is_eof() and now < end_t: + async with ceil_timeout(end_t - now): + # read and ignore + await payload.readany() + now = loop.time() + + # if payload still uncompleted + if not payload.is_eof() and not self._force_close: + self.log_debug("Uncompleted request.") + self.close() + + set_exception(payload, PayloadAccessError()) + + except asyncio.CancelledError: + self.log_debug("Ignored premature client disconnection ") + break + except RuntimeError as exc: + if self.debug: + self.log_exception("Unhandled runtime exception", exc_info=exc) + self.force_close() + except Exception as exc: + self.log_exception("Unhandled exception", exc_info=exc) + self.force_close() + finally: + if self.transport is None and resp is not None: + self.log_debug("Ignored premature client disconnection.") + elif not self._force_close: + if self._keepalive and not self._close: + # start keep-alive timer + if keepalive_timeout is not None: + now = self._loop.time() + self._keepalive_time = now + if self._keepalive_handle is None: + self._keepalive_handle = loop.call_at( + now + keepalive_timeout, self._process_keepalive + ) + else: + break + + # remove handler, close transport if no handlers left + if not self._force_close: + self._task_handler = None + if self.transport is not None: + self.transport.close() + + async def finish_response( + self, request: BaseRequest, resp: StreamResponse, start_time: float + ) -> bool: + """Prepare the response and write_eof, then log access. + + This has to + be called within the context of any exception so the access logger + can get exception information. Returns True if the client disconnects + prematurely. + """ + if self._request_parser is not None: + self._request_parser.set_upgraded(False) + self._upgrade = False + if self._message_tail: + self._request_parser.feed_data(self._message_tail) + self._message_tail = b"" + try: + prepare_meth = resp.prepare + except AttributeError: + if resp is None: + raise RuntimeError("Missing return " "statement on request handler") + else: + raise RuntimeError( + "Web-handler should return " + "a response instance, " + "got {!r}".format(resp) + ) + try: + await prepare_meth(request) + await resp.write_eof() + except ConnectionError: + self.log_access(request, resp, start_time) + return True + else: + self.log_access(request, resp, start_time) + return False + + def handle_error( + self, + request: BaseRequest, + status: int = 500, + exc: Optional[BaseException] = None, + message: Optional[str] = None, + ) -> StreamResponse: + """Handle errors. + + Returns HTTP response with specific status code. Logs additional + information. It always closes current connection. + """ + self.log_exception("Error handling request", exc_info=exc) + + # some data already got sent, connection is broken + if request.writer.output_size > 0: + raise ConnectionError( + "Response is sent already, cannot send another response " + "with the error message" + ) + + ct = "text/plain" + if status == HTTPStatus.INTERNAL_SERVER_ERROR: + title = "{0.value} {0.phrase}".format(HTTPStatus.INTERNAL_SERVER_ERROR) + msg = HTTPStatus.INTERNAL_SERVER_ERROR.description + tb = None + if self.debug: + with suppress(Exception): + tb = traceback.format_exc() + + if "text/html" in request.headers.get("Accept", ""): + if tb: + tb = html_escape(tb) + msg = f"

Traceback:

\n
{tb}
" + message = ( + "" + "{title}" + "\n

{title}

" + "\n{msg}\n\n" + ).format(title=title, msg=msg) + ct = "text/html" + else: + if tb: + msg = tb + message = title + "\n\n" + msg + + resp = Response(status=status, text=message, content_type=ct) + resp.force_close() + + return resp + + def _make_error_handler( + self, err_info: _ErrInfo + ) -> Callable[[BaseRequest], Awaitable[StreamResponse]]: + async def handler(request: BaseRequest) -> StreamResponse: + return self.handle_error( + request, err_info.status, err_info.exc, err_info.message + ) + + return handler diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_request.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_request.py new file mode 100644 index 0000000..4bc670a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_request.py @@ -0,0 +1,901 @@ +import asyncio +import datetime +import io +import re +import socket +import string +import tempfile +import types +import warnings +from http.cookies import SimpleCookie +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Final, + Iterator, + Mapping, + MutableMapping, + Optional, + Pattern, + Tuple, + Union, + cast, +) +from urllib.parse import parse_qsl + +import attr +from multidict import ( + CIMultiDict, + CIMultiDictProxy, + MultiDict, + MultiDictProxy, + MultiMapping, +) +from yarl import URL + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import ( + _SENTINEL, + DEBUG, + ETAG_ANY, + LIST_QUOTED_ETAG_RE, + ChainMapProxy, + ETag, + HeadersMixin, + parse_http_date, + reify, + sentinel, + set_exception, +) +from .http_parser import RawRequestMessage +from .http_writer import HttpVersion +from .multipart import BodyPartReader, MultipartReader +from .streams import EmptyStreamReader, StreamReader +from .typedefs import ( + DEFAULT_JSON_DECODER, + JSONDecoder, + LooseHeaders, + RawHeaders, + StrOrURL, +) +from .web_exceptions import HTTPRequestEntityTooLarge +from .web_response import StreamResponse + +__all__ = ("BaseRequest", "FileField", "Request") + + +if TYPE_CHECKING: + from .web_app import Application + from .web_protocol import RequestHandler + from .web_urldispatcher import UrlMappingMatchInfo + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class FileField: + name: str + filename: str + file: io.BufferedReader + content_type: str + headers: "CIMultiDictProxy[str]" + + +_TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-" +# '-' at the end to prevent interpretation as range in a char class + +_TOKEN: Final[str] = rf"[{_TCHAR}]+" + +_QDTEXT: Final[str] = r"[{}]".format( + r"".join(chr(c) for c in (0x09, 0x20, 0x21) + tuple(range(0x23, 0x7F))) +) +# qdtext includes 0x5C to escape 0x5D ('\]') +# qdtext excludes obs-text (because obsoleted, and encoding not specified) + +_QUOTED_PAIR: Final[str] = r"\\[\t !-~]" + +_QUOTED_STRING: Final[str] = r'"(?:{quoted_pair}|{qdtext})*"'.format( + qdtext=_QDTEXT, quoted_pair=_QUOTED_PAIR +) + +_FORWARDED_PAIR: Final[ + str +] = r"({token})=({token}|{quoted_string})(:\d{{1,4}})?".format( + token=_TOKEN, quoted_string=_QUOTED_STRING +) + +_QUOTED_PAIR_REPLACE_RE: Final[Pattern[str]] = re.compile(r"\\([\t !-~])") +# same pattern as _QUOTED_PAIR but contains a capture group + +_FORWARDED_PAIR_RE: Final[Pattern[str]] = re.compile(_FORWARDED_PAIR) + +############################################################ +# HTTP Request +############################################################ + + +class BaseRequest(MutableMapping[str, Any], HeadersMixin): + + POST_METHODS = { + hdrs.METH_PATCH, + hdrs.METH_POST, + hdrs.METH_PUT, + hdrs.METH_TRACE, + hdrs.METH_DELETE, + } + + ATTRS = HeadersMixin.ATTRS | frozenset( + [ + "_message", + "_protocol", + "_payload_writer", + "_payload", + "_headers", + "_method", + "_version", + "_rel_url", + "_post", + "_read_bytes", + "_state", + "_cache", + "_task", + "_client_max_size", + "_loop", + "_transport_sslcontext", + "_transport_peername", + ] + ) + + def __init__( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: "RequestHandler", + payload_writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + loop: asyncio.AbstractEventLoop, + *, + client_max_size: int = 1024**2, + state: Optional[Dict[str, Any]] = None, + scheme: Optional[str] = None, + host: Optional[str] = None, + remote: Optional[str] = None, + ) -> None: + if state is None: + state = {} + self._message = message + self._protocol = protocol + self._payload_writer = payload_writer + + self._payload = payload + self._headers = message.headers + self._method = message.method + self._version = message.version + self._cache: Dict[str, Any] = {} + url = message.url + if url.is_absolute(): + # absolute URL is given, + # override auto-calculating url, host, and scheme + # all other properties should be good + self._cache["url"] = url + self._cache["host"] = url.host + self._cache["scheme"] = url.scheme + self._rel_url = url.relative() + else: + self._rel_url = message.url + self._post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None + self._read_bytes: Optional[bytes] = None + + self._state = state + self._task = task + self._client_max_size = client_max_size + self._loop = loop + + transport = self._protocol.transport + assert transport is not None + self._transport_sslcontext = transport.get_extra_info("sslcontext") + self._transport_peername = transport.get_extra_info("peername") + + if scheme is not None: + self._cache["scheme"] = scheme + if host is not None: + self._cache["host"] = host + if remote is not None: + self._cache["remote"] = remote + + def clone( + self, + *, + method: Union[str, _SENTINEL] = sentinel, + rel_url: Union[StrOrURL, _SENTINEL] = sentinel, + headers: Union[LooseHeaders, _SENTINEL] = sentinel, + scheme: Union[str, _SENTINEL] = sentinel, + host: Union[str, _SENTINEL] = sentinel, + remote: Union[str, _SENTINEL] = sentinel, + client_max_size: Union[int, _SENTINEL] = sentinel, + ) -> "BaseRequest": + """Clone itself with replacement some attributes. + + Creates and returns a new instance of Request object. If no parameters + are given, an exact copy is returned. If a parameter is not passed, it + will reuse the one from the current request object. + """ + if self._read_bytes: + raise RuntimeError("Cannot clone request " "after reading its content") + + dct: Dict[str, Any] = {} + if method is not sentinel: + dct["method"] = method + if rel_url is not sentinel: + new_url: URL = URL(rel_url) + dct["url"] = new_url + dct["path"] = str(new_url) + if headers is not sentinel: + # a copy semantic + dct["headers"] = CIMultiDictProxy(CIMultiDict(headers)) + dct["raw_headers"] = tuple( + (k.encode("utf-8"), v.encode("utf-8")) for k, v in headers.items() + ) + + message = self._message._replace(**dct) + + kwargs = {} + if scheme is not sentinel: + kwargs["scheme"] = scheme + if host is not sentinel: + kwargs["host"] = host + if remote is not sentinel: + kwargs["remote"] = remote + if client_max_size is sentinel: + client_max_size = self._client_max_size + + return self.__class__( + message, + self._payload, + self._protocol, + self._payload_writer, + self._task, + self._loop, + client_max_size=client_max_size, + state=self._state.copy(), + **kwargs, + ) + + @property + def task(self) -> "asyncio.Task[None]": + return self._task + + @property + def protocol(self) -> "RequestHandler": + return self._protocol + + @property + def transport(self) -> Optional[asyncio.Transport]: + if self._protocol is None: + return None + return self._protocol.transport + + @property + def writer(self) -> AbstractStreamWriter: + return self._payload_writer + + @property + def client_max_size(self) -> int: + return self._client_max_size + + @reify + def message(self) -> RawRequestMessage: + warnings.warn("Request.message is deprecated", DeprecationWarning, stacklevel=3) + return self._message + + @reify + def rel_url(self) -> URL: + return self._rel_url + + @reify + def loop(self) -> asyncio.AbstractEventLoop: + warnings.warn( + "request.loop property is deprecated", DeprecationWarning, stacklevel=2 + ) + return self._loop + + # MutableMapping API + + def __getitem__(self, key: str) -> Any: + return self._state[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._state[key] = value + + def __delitem__(self, key: str) -> None: + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[str]: + return iter(self._state) + + ######## + + @reify + def secure(self) -> bool: + """A bool indicating if the request is handled with SSL.""" + return self.scheme == "https" + + @reify + def forwarded(self) -> Tuple[Mapping[str, str], ...]: + """A tuple containing all parsed Forwarded header(s). + + Makes an effort to parse Forwarded headers as specified by RFC 7239: + + - It adds one (immutable) dictionary per Forwarded 'field-value', ie + per proxy. The element corresponds to the data in the Forwarded + field-value added by the first proxy encountered by the client. Each + subsequent item corresponds to those added by later proxies. + - It checks that every value has valid syntax in general as specified + in section 4: either a 'token' or a 'quoted-string'. + - It un-escapes found escape sequences. + - It does NOT validate 'by' and 'for' contents as specified in section + 6. + - It does NOT validate 'host' contents (Host ABNF). + - It does NOT validate 'proto' contents for valid URI scheme names. + + Returns a tuple containing one or more immutable dicts + """ + elems = [] + for field_value in self._message.headers.getall(hdrs.FORWARDED, ()): + length = len(field_value) + pos = 0 + need_separator = False + elem: Dict[str, str] = {} + elems.append(types.MappingProxyType(elem)) + while 0 <= pos < length: + match = _FORWARDED_PAIR_RE.match(field_value, pos) + if match is not None: # got a valid forwarded-pair + if need_separator: + # bad syntax here, skip to next comma + pos = field_value.find(",", pos) + else: + name, value, port = match.groups() + if value[0] == '"': + # quoted string: remove quotes and unescape + value = _QUOTED_PAIR_REPLACE_RE.sub(r"\1", value[1:-1]) + if port: + value += port + elem[name.lower()] = value + pos += len(match.group(0)) + need_separator = True + elif field_value[pos] == ",": # next forwarded-element + need_separator = False + elem = {} + elems.append(types.MappingProxyType(elem)) + pos += 1 + elif field_value[pos] == ";": # next forwarded-pair + need_separator = False + pos += 1 + elif field_value[pos] in " \t": + # Allow whitespace even between forwarded-pairs, though + # RFC 7239 doesn't. This simplifies code and is in line + # with Postel's law. + pos += 1 + else: + # bad syntax here, skip to next comma + pos = field_value.find(",", pos) + return tuple(elems) + + @reify + def scheme(self) -> str: + """A string representing the scheme of the request. + + Hostname is resolved in this order: + + - overridden value by .clone(scheme=new_scheme) call. + - type of connection to peer: HTTPS if socket is SSL, HTTP otherwise. + + 'http' or 'https'. + """ + if self._transport_sslcontext: + return "https" + else: + return "http" + + @reify + def method(self) -> str: + """Read only property for getting HTTP method. + + The value is upper-cased str like 'GET', 'POST', 'PUT' etc. + """ + return self._method + + @reify + def version(self) -> HttpVersion: + """Read only property for getting HTTP version of request. + + Returns aiohttp.protocol.HttpVersion instance. + """ + return self._version + + @reify + def host(self) -> str: + """Hostname of the request. + + Hostname is resolved in this order: + + - overridden value by .clone(host=new_host) call. + - HOST HTTP header + - socket.getfqdn() value + """ + host = self._message.headers.get(hdrs.HOST) + if host is not None: + return host + return socket.getfqdn() + + @reify + def remote(self) -> Optional[str]: + """Remote IP of client initiated HTTP request. + + The IP is resolved in this order: + + - overridden value by .clone(remote=new_remote) call. + - peername of opened socket + """ + if self._transport_peername is None: + return None + if isinstance(self._transport_peername, (list, tuple)): + return str(self._transport_peername[0]) + return str(self._transport_peername) + + @reify + def url(self) -> URL: + url = URL.build(scheme=self.scheme, host=self.host) + return url.join(self._rel_url) + + @reify + def path(self) -> str: + """The URL including *PATH INFO* without the host or scheme. + + E.g., ``/app/blog`` + """ + return self._rel_url.path + + @reify + def path_qs(self) -> str: + """The URL including PATH_INFO and the query string. + + E.g, /app/blog?id=10 + """ + return str(self._rel_url) + + @reify + def raw_path(self) -> str: + """The URL including raw *PATH INFO* without the host or scheme. + + Warning, the path is unquoted and may contains non valid URL characters + + E.g., ``/my%2Fpath%7Cwith%21some%25strange%24characters`` + """ + return self._message.path + + @reify + def query(self) -> "MultiMapping[str]": + """A multidict with all the variables in the query string.""" + return MultiDictProxy(self._rel_url.query) + + @reify + def query_string(self) -> str: + """The query string in the URL. + + E.g., id=10 + """ + return self._rel_url.query_string + + @reify + def headers(self) -> "MultiMapping[str]": + """A case-insensitive multidict proxy with all headers.""" + return self._headers + + @reify + def raw_headers(self) -> RawHeaders: + """A sequence of pairs for all headers.""" + return self._message.raw_headers + + @reify + def if_modified_since(self) -> Optional[datetime.datetime]: + """The value of If-Modified-Since HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_MODIFIED_SINCE)) + + @reify + def if_unmodified_since(self) -> Optional[datetime.datetime]: + """The value of If-Unmodified-Since HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_UNMODIFIED_SINCE)) + + @staticmethod + def _etag_values(etag_header: str) -> Iterator[ETag]: + """Extract `ETag` objects from raw header.""" + if etag_header == ETAG_ANY: + yield ETag( + is_weak=False, + value=ETAG_ANY, + ) + else: + for match in LIST_QUOTED_ETAG_RE.finditer(etag_header): + is_weak, value, garbage = match.group(2, 3, 4) + # Any symbol captured by 4th group means + # that the following sequence is invalid. + if garbage: + break + + yield ETag( + is_weak=bool(is_weak), + value=value, + ) + + @classmethod + def _if_match_or_none_impl( + cls, header_value: Optional[str] + ) -> Optional[Tuple[ETag, ...]]: + if not header_value: + return None + + return tuple(cls._etag_values(header_value)) + + @reify + def if_match(self) -> Optional[Tuple[ETag, ...]]: + """The value of If-Match HTTP header, or None. + + This header is represented as a `tuple` of `ETag` objects. + """ + return self._if_match_or_none_impl(self.headers.get(hdrs.IF_MATCH)) + + @reify + def if_none_match(self) -> Optional[Tuple[ETag, ...]]: + """The value of If-None-Match HTTP header, or None. + + This header is represented as a `tuple` of `ETag` objects. + """ + return self._if_match_or_none_impl(self.headers.get(hdrs.IF_NONE_MATCH)) + + @reify + def if_range(self) -> Optional[datetime.datetime]: + """The value of If-Range HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self.headers.get(hdrs.IF_RANGE)) + + @reify + def keep_alive(self) -> bool: + """Is keepalive enabled by client?""" + return not self._message.should_close + + @reify + def cookies(self) -> Mapping[str, str]: + """Return request cookies. + + A read-only dictionary-like object. + """ + raw = self.headers.get(hdrs.COOKIE, "") + parsed = SimpleCookie(raw) + return MappingProxyType({key: val.value for key, val in parsed.items()}) + + @reify + def http_range(self) -> slice: + """The content of Range HTTP header. + + Return a slice instance. + + """ + rng = self._headers.get(hdrs.RANGE) + start, end = None, None + if rng is not None: + try: + pattern = r"^bytes=(\d*)-(\d*)$" + start, end = re.findall(pattern, rng)[0] + except IndexError: # pattern was not found in header + raise ValueError("range not in acceptable format") + + end = int(end) if end else None + start = int(start) if start else None + + if start is None and end is not None: + # end with no start is to return tail of content + start = -end + end = None + + if start is not None and end is not None: + # end is inclusive in range header, exclusive for slice + end += 1 + + if start >= end: + raise ValueError("start cannot be after end") + + if start is end is None: # No valid range supplied + raise ValueError("No start or end of range specified") + + return slice(start, end, 1) + + @reify + def content(self) -> StreamReader: + """Return raw payload stream.""" + return self._payload + + @property + def has_body(self) -> bool: + """Return True if request's HTTP BODY can be read, False otherwise.""" + warnings.warn( + "Deprecated, use .can_read_body #2005", DeprecationWarning, stacklevel=2 + ) + return not self._payload.at_eof() + + @property + def can_read_body(self) -> bool: + """Return True if request's HTTP BODY can be read, False otherwise.""" + return not self._payload.at_eof() + + @reify + def body_exists(self) -> bool: + """Return True if request has HTTP BODY, False otherwise.""" + return type(self._payload) is not EmptyStreamReader + + async def release(self) -> None: + """Release request. + + Eat unread part of HTTP BODY if present. + """ + while not self._payload.at_eof(): + await self._payload.readany() + + async def read(self) -> bytes: + """Read request body if present. + + Returns bytes object with full request content. + """ + if self._read_bytes is None: + body = bytearray() + while True: + chunk = await self._payload.readany() + body.extend(chunk) + if self._client_max_size: + body_size = len(body) + if body_size >= self._client_max_size: + raise HTTPRequestEntityTooLarge( + max_size=self._client_max_size, actual_size=body_size + ) + if not chunk: + break + self._read_bytes = bytes(body) + return self._read_bytes + + async def text(self) -> str: + """Return BODY as text using encoding from .charset.""" + bytes_body = await self.read() + encoding = self.charset or "utf-8" + return bytes_body.decode(encoding) + + async def json(self, *, loads: JSONDecoder = DEFAULT_JSON_DECODER) -> Any: + """Return BODY as JSON.""" + body = await self.text() + return loads(body) + + async def multipart(self) -> MultipartReader: + """Return async iterator to process BODY as multipart.""" + return MultipartReader(self._headers, self._payload) + + async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]": + """Return POST parameters.""" + if self._post is not None: + return self._post + if self._method not in self.POST_METHODS: + self._post = MultiDictProxy(MultiDict()) + return self._post + + content_type = self.content_type + if content_type not in ( + "", + "application/x-www-form-urlencoded", + "multipart/form-data", + ): + self._post = MultiDictProxy(MultiDict()) + return self._post + + out: MultiDict[Union[str, bytes, FileField]] = MultiDict() + + if content_type == "multipart/form-data": + multipart = await self.multipart() + max_size = self._client_max_size + + field = await multipart.next() + while field is not None: + size = 0 + field_ct = field.headers.get(hdrs.CONTENT_TYPE) + + if isinstance(field, BodyPartReader): + assert field.name is not None + + # Note that according to RFC 7578, the Content-Type header + # is optional, even for files, so we can't assume it's + # present. + # https://tools.ietf.org/html/rfc7578#section-4.4 + if field.filename: + # store file in temp file + tmp = await self._loop.run_in_executor( + None, tempfile.TemporaryFile + ) + chunk = await field.read_chunk(size=2**16) + while chunk: + chunk = field.decode(chunk) + await self._loop.run_in_executor(None, tmp.write, chunk) + size += len(chunk) + if 0 < max_size < size: + await self._loop.run_in_executor(None, tmp.close) + raise HTTPRequestEntityTooLarge( + max_size=max_size, actual_size=size + ) + chunk = await field.read_chunk(size=2**16) + await self._loop.run_in_executor(None, tmp.seek, 0) + + if field_ct is None: + field_ct = "application/octet-stream" + + ff = FileField( + field.name, + field.filename, + cast(io.BufferedReader, tmp), + field_ct, + field.headers, + ) + out.add(field.name, ff) + else: + # deal with ordinary data + value = await field.read(decode=True) + if field_ct is None or field_ct.startswith("text/"): + charset = field.get_charset(default="utf-8") + out.add(field.name, value.decode(charset)) + else: + out.add(field.name, value) + size += len(value) + if 0 < max_size < size: + raise HTTPRequestEntityTooLarge( + max_size=max_size, actual_size=size + ) + else: + raise ValueError( + "To decode nested multipart you need " "to use custom reader", + ) + + field = await multipart.next() + else: + data = await self.read() + if data: + charset = self.charset or "utf-8" + out.extend( + parse_qsl( + data.rstrip().decode(charset), + keep_blank_values=True, + encoding=charset, + ) + ) + + self._post = MultiDictProxy(out) + return self._post + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """Extra info from protocol transport""" + protocol = self._protocol + if protocol is None: + return default + + transport = protocol.transport + if transport is None: + return default + + return transport.get_extra_info(name, default) + + def __repr__(self) -> str: + ascii_encodable_path = self.path.encode("ascii", "backslashreplace").decode( + "ascii" + ) + return "<{} {} {} >".format( + self.__class__.__name__, self._method, ascii_encodable_path + ) + + def __eq__(self, other: object) -> bool: + return id(self) == id(other) + + def __bool__(self) -> bool: + return True + + async def _prepare_hook(self, response: StreamResponse) -> None: + return + + def _cancel(self, exc: BaseException) -> None: + set_exception(self._payload, exc) + + +class Request(BaseRequest): + + ATTRS = BaseRequest.ATTRS | frozenset(["_match_info"]) + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + # matchdict, route_name, handler + # or information about traversal lookup + + # initialized after route resolving + self._match_info: Optional[UrlMappingMatchInfo] = None + + if DEBUG: + + def __setattr__(self, name: str, val: Any) -> None: + if name not in self.ATTRS: + warnings.warn( + "Setting custom {}.{} attribute " + "is discouraged".format(self.__class__.__name__, name), + DeprecationWarning, + stacklevel=2, + ) + super().__setattr__(name, val) + + def clone( + self, + *, + method: Union[str, _SENTINEL] = sentinel, + rel_url: Union[StrOrURL, _SENTINEL] = sentinel, + headers: Union[LooseHeaders, _SENTINEL] = sentinel, + scheme: Union[str, _SENTINEL] = sentinel, + host: Union[str, _SENTINEL] = sentinel, + remote: Union[str, _SENTINEL] = sentinel, + client_max_size: Union[int, _SENTINEL] = sentinel, + ) -> "Request": + ret = super().clone( + method=method, + rel_url=rel_url, + headers=headers, + scheme=scheme, + host=host, + remote=remote, + client_max_size=client_max_size, + ) + new_ret = cast(Request, ret) + new_ret._match_info = self._match_info + return new_ret + + @reify + def match_info(self) -> "UrlMappingMatchInfo": + """Result of route resolving.""" + match_info = self._match_info + assert match_info is not None + return match_info + + @property + def app(self) -> "Application": + """Application instance.""" + match_info = self._match_info + assert match_info is not None + return match_info.current_app + + @property + def config_dict(self) -> ChainMapProxy: + match_info = self._match_info + assert match_info is not None + lst = match_info.apps + app = self.app + idx = lst.index(app) + sublist = list(reversed(lst[: idx + 1])) + return ChainMapProxy(sublist) + + async def _prepare_hook(self, response: StreamResponse) -> None: + match_info = self._match_info + if match_info is None: + return + for app in match_info._apps: + await app.on_response_prepare.send(self, response) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_response.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_response.py new file mode 100644 index 0000000..40d6f01 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_response.py @@ -0,0 +1,819 @@ +import asyncio +import collections.abc +import datetime +import enum +import json +import math +import time +import warnings +from concurrent.futures import Executor +from http import HTTPStatus +from http.cookies import SimpleCookie +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterator, + MutableMapping, + Optional, + Union, + cast, +) + +from multidict import CIMultiDict, istr + +from . import hdrs, payload +from .abc import AbstractStreamWriter +from .compression_utils import ZLibCompressor +from .helpers import ( + ETAG_ANY, + QUOTED_ETAG_RE, + ETag, + HeadersMixin, + must_be_empty_body, + parse_http_date, + rfc822_formatted_time, + sentinel, + should_remove_content_length, + validate_etag_value, +) +from .http import SERVER_SOFTWARE, HttpVersion10, HttpVersion11 +from .payload import Payload +from .typedefs import JSONEncoder, LooseHeaders + +__all__ = ("ContentCoding", "StreamResponse", "Response", "json_response") + + +if TYPE_CHECKING: + from .web_request import BaseRequest + + BaseClass = MutableMapping[str, Any] +else: + BaseClass = collections.abc.MutableMapping + + +class ContentCoding(enum.Enum): + # The content codings that we have support for. + # + # Additional registered codings are listed at: + # https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding + deflate = "deflate" + gzip = "gzip" + identity = "identity" + + +############################################################ +# HTTP Response classes +############################################################ + + +class StreamResponse(BaseClass, HeadersMixin): + + _length_check = True + + def __init__( + self, + *, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + ) -> None: + self._body = None + self._keep_alive: Optional[bool] = None + self._chunked = False + self._compression = False + self._compression_force: Optional[ContentCoding] = None + self._cookies = SimpleCookie() + + self._req: Optional[BaseRequest] = None + self._payload_writer: Optional[AbstractStreamWriter] = None + self._eof_sent = False + self._must_be_empty_body: Optional[bool] = None + self._body_length = 0 + self._state: Dict[str, Any] = {} + + if headers is not None: + self._headers: CIMultiDict[str] = CIMultiDict(headers) + else: + self._headers = CIMultiDict() + + self.set_status(status, reason) + + @property + def prepared(self) -> bool: + return self._payload_writer is not None + + @property + def task(self) -> "Optional[asyncio.Task[None]]": + if self._req: + return self._req.task + else: + return None + + @property + def status(self) -> int: + return self._status + + @property + def chunked(self) -> bool: + return self._chunked + + @property + def compression(self) -> bool: + return self._compression + + @property + def reason(self) -> str: + return self._reason + + def set_status( + self, + status: int, + reason: Optional[str] = None, + ) -> None: + assert not self.prepared, ( + "Cannot change the response status code after " "the headers have been sent" + ) + self._status = int(status) + if reason is None: + try: + reason = HTTPStatus(self._status).phrase + except ValueError: + reason = "" + self._reason = reason + + @property + def keep_alive(self) -> Optional[bool]: + return self._keep_alive + + def force_close(self) -> None: + self._keep_alive = False + + @property + def body_length(self) -> int: + return self._body_length + + @property + def output_length(self) -> int: + warnings.warn("output_length is deprecated", DeprecationWarning) + assert self._payload_writer + return self._payload_writer.buffer_size + + def enable_chunked_encoding(self, chunk_size: Optional[int] = None) -> None: + """Enables automatic chunked transfer encoding.""" + self._chunked = True + + if hdrs.CONTENT_LENGTH in self._headers: + raise RuntimeError( + "You can't enable chunked encoding when " "a content length is set" + ) + if chunk_size is not None: + warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) + + def enable_compression( + self, force: Optional[Union[bool, ContentCoding]] = None + ) -> None: + """Enables response compression encoding.""" + # Backwards compatibility for when force was a bool <0.17. + if type(force) == bool: + force = ContentCoding.deflate if force else ContentCoding.identity + warnings.warn( + "Using boolean for force is deprecated #3318", DeprecationWarning + ) + elif force is not None: + assert isinstance(force, ContentCoding), ( + "force should one of " "None, bool or " "ContentEncoding" + ) + + self._compression = True + self._compression_force = force + + @property + def headers(self) -> "CIMultiDict[str]": + return self._headers + + @property + def cookies(self) -> SimpleCookie: + return self._cookies + + def set_cookie( + self, + name: str, + value: str, + *, + expires: Optional[str] = None, + domain: Optional[str] = None, + max_age: Optional[Union[int, str]] = None, + path: str = "/", + secure: Optional[bool] = None, + httponly: Optional[bool] = None, + version: Optional[str] = None, + samesite: Optional[str] = None, + ) -> None: + """Set or update response cookie. + + Sets new cookie or updates existent with new value. + Also updates only those params which are not None. + """ + old = self._cookies.get(name) + if old is not None and old.coded_value == "": + # deleted cookie + self._cookies.pop(name, None) + + self._cookies[name] = value + c = self._cookies[name] + + if expires is not None: + c["expires"] = expires + elif c.get("expires") == "Thu, 01 Jan 1970 00:00:00 GMT": + del c["expires"] + + if domain is not None: + c["domain"] = domain + + if max_age is not None: + c["max-age"] = str(max_age) + elif "max-age" in c: + del c["max-age"] + + c["path"] = path + + if secure is not None: + c["secure"] = secure + if httponly is not None: + c["httponly"] = httponly + if version is not None: + c["version"] = version + if samesite is not None: + c["samesite"] = samesite + + def del_cookie( + self, name: str, *, domain: Optional[str] = None, path: str = "/" + ) -> None: + """Delete cookie. + + Creates new empty expired cookie. + """ + # TODO: do we need domain/path here? + self._cookies.pop(name, None) + self.set_cookie( + name, + "", + max_age=0, + expires="Thu, 01 Jan 1970 00:00:00 GMT", + domain=domain, + path=path, + ) + + @property + def content_length(self) -> Optional[int]: + # Just a placeholder for adding setter + return super().content_length + + @content_length.setter + def content_length(self, value: Optional[int]) -> None: + if value is not None: + value = int(value) + if self._chunked: + raise RuntimeError( + "You can't set content length when " "chunked encoding is enable" + ) + self._headers[hdrs.CONTENT_LENGTH] = str(value) + else: + self._headers.pop(hdrs.CONTENT_LENGTH, None) + + @property + def content_type(self) -> str: + # Just a placeholder for adding setter + return super().content_type + + @content_type.setter + def content_type(self, value: str) -> None: + self.content_type # read header values if needed + self._content_type = str(value) + self._generate_content_type_header() + + @property + def charset(self) -> Optional[str]: + # Just a placeholder for adding setter + return super().charset + + @charset.setter + def charset(self, value: Optional[str]) -> None: + ctype = self.content_type # read header values if needed + if ctype == "application/octet-stream": + raise RuntimeError( + "Setting charset for application/octet-stream " + "doesn't make sense, setup content_type first" + ) + assert self._content_dict is not None + if value is None: + self._content_dict.pop("charset", None) + else: + self._content_dict["charset"] = str(value).lower() + self._generate_content_type_header() + + @property + def last_modified(self) -> Optional[datetime.datetime]: + """The value of Last-Modified HTTP header, or None. + + This header is represented as a `datetime` object. + """ + return parse_http_date(self._headers.get(hdrs.LAST_MODIFIED)) + + @last_modified.setter + def last_modified( + self, value: Optional[Union[int, float, datetime.datetime, str]] + ) -> None: + if value is None: + self._headers.pop(hdrs.LAST_MODIFIED, None) + elif isinstance(value, (int, float)): + self._headers[hdrs.LAST_MODIFIED] = time.strftime( + "%a, %d %b %Y %H:%M:%S GMT", time.gmtime(math.ceil(value)) + ) + elif isinstance(value, datetime.datetime): + self._headers[hdrs.LAST_MODIFIED] = time.strftime( + "%a, %d %b %Y %H:%M:%S GMT", value.utctimetuple() + ) + elif isinstance(value, str): + self._headers[hdrs.LAST_MODIFIED] = value + + @property + def etag(self) -> Optional[ETag]: + quoted_value = self._headers.get(hdrs.ETAG) + if not quoted_value: + return None + elif quoted_value == ETAG_ANY: + return ETag(value=ETAG_ANY) + match = QUOTED_ETAG_RE.fullmatch(quoted_value) + if not match: + return None + is_weak, value = match.group(1, 2) + return ETag( + is_weak=bool(is_weak), + value=value, + ) + + @etag.setter + def etag(self, value: Optional[Union[ETag, str]]) -> None: + if value is None: + self._headers.pop(hdrs.ETAG, None) + elif (isinstance(value, str) and value == ETAG_ANY) or ( + isinstance(value, ETag) and value.value == ETAG_ANY + ): + self._headers[hdrs.ETAG] = ETAG_ANY + elif isinstance(value, str): + validate_etag_value(value) + self._headers[hdrs.ETAG] = f'"{value}"' + elif isinstance(value, ETag) and isinstance(value.value, str): + validate_etag_value(value.value) + hdr_value = f'W/"{value.value}"' if value.is_weak else f'"{value.value}"' + self._headers[hdrs.ETAG] = hdr_value + else: + raise ValueError( + f"Unsupported etag type: {type(value)}. " + f"etag must be str, ETag or None" + ) + + def _generate_content_type_header( + self, CONTENT_TYPE: istr = hdrs.CONTENT_TYPE + ) -> None: + assert self._content_dict is not None + assert self._content_type is not None + params = "; ".join(f"{k}={v}" for k, v in self._content_dict.items()) + if params: + ctype = self._content_type + "; " + params + else: + ctype = self._content_type + self._headers[CONTENT_TYPE] = ctype + + async def _do_start_compression(self, coding: ContentCoding) -> None: + if coding != ContentCoding.identity: + assert self._payload_writer is not None + self._headers[hdrs.CONTENT_ENCODING] = coding.value + self._payload_writer.enable_compression(coding.value) + # Compressed payload may have different content length, + # remove the header + self._headers.popall(hdrs.CONTENT_LENGTH, None) + + async def _start_compression(self, request: "BaseRequest") -> None: + if self._compression_force: + await self._do_start_compression(self._compression_force) + else: + # Encoding comparisons should be case-insensitive + # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 + accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() + for coding in ContentCoding: + if coding.value in accept_encoding: + await self._do_start_compression(coding) + return + + async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]: + if self._eof_sent: + return None + if self._payload_writer is not None: + return self._payload_writer + self._must_be_empty_body = must_be_empty_body(request.method, self.status) + return await self._start(request) + + async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: + self._req = request + writer = self._payload_writer = request._payload_writer + + await self._prepare_headers() + await request._prepare_hook(self) + await self._write_headers() + + return writer + + async def _prepare_headers(self) -> None: + request = self._req + assert request is not None + writer = self._payload_writer + assert writer is not None + keep_alive = self._keep_alive + if keep_alive is None: + keep_alive = request.keep_alive + self._keep_alive = keep_alive + + version = request.version + + headers = self._headers + for cookie in self._cookies.values(): + value = cookie.output(header="")[1:] + headers.add(hdrs.SET_COOKIE, value) + + if self._compression: + await self._start_compression(request) + + if self._chunked: + if version != HttpVersion11: + raise RuntimeError( + "Using chunked encoding is forbidden " + "for HTTP/{0.major}.{0.minor}".format(request.version) + ) + if not self._must_be_empty_body: + writer.enable_chunking() + headers[hdrs.TRANSFER_ENCODING] = "chunked" + if hdrs.CONTENT_LENGTH in headers: + del headers[hdrs.CONTENT_LENGTH] + elif self._length_check: + writer.length = self.content_length + if writer.length is None: + if version >= HttpVersion11: + if not self._must_be_empty_body: + writer.enable_chunking() + headers[hdrs.TRANSFER_ENCODING] = "chunked" + elif not self._must_be_empty_body: + keep_alive = False + + # HTTP 1.1: https://tools.ietf.org/html/rfc7230#section-3.3.2 + # HTTP 1.0: https://tools.ietf.org/html/rfc1945#section-10.4 + if self._must_be_empty_body: + if hdrs.CONTENT_LENGTH in headers and should_remove_content_length( + request.method, self.status + ): + del headers[hdrs.CONTENT_LENGTH] + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-10 + # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-13 + if hdrs.TRANSFER_ENCODING in headers: + del headers[hdrs.TRANSFER_ENCODING] + else: + headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream") + headers.setdefault(hdrs.DATE, rfc822_formatted_time()) + headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE) + + # connection header + if hdrs.CONNECTION not in headers: + if keep_alive: + if version == HttpVersion10: + headers[hdrs.CONNECTION] = "keep-alive" + else: + if version == HttpVersion11: + headers[hdrs.CONNECTION] = "close" + + async def _write_headers(self) -> None: + request = self._req + assert request is not None + writer = self._payload_writer + assert writer is not None + # status line + version = request.version + status_line = "HTTP/{}.{} {} {}".format( + version[0], version[1], self._status, self._reason + ) + await writer.write_headers(status_line, self._headers) + + async def write(self, data: bytes) -> None: + assert isinstance( + data, (bytes, bytearray, memoryview) + ), "data argument must be byte-ish (%r)" % type(data) + + if self._eof_sent: + raise RuntimeError("Cannot call write() after write_eof()") + if self._payload_writer is None: + raise RuntimeError("Cannot call write() before prepare()") + + await self._payload_writer.write(data) + + async def drain(self) -> None: + assert not self._eof_sent, "EOF has already been sent" + assert self._payload_writer is not None, "Response has not been started" + warnings.warn( + "drain method is deprecated, use await resp.write()", + DeprecationWarning, + stacklevel=2, + ) + await self._payload_writer.drain() + + async def write_eof(self, data: bytes = b"") -> None: + assert isinstance( + data, (bytes, bytearray, memoryview) + ), "data argument must be byte-ish (%r)" % type(data) + + if self._eof_sent: + return + + assert self._payload_writer is not None, "Response has not been started" + + await self._payload_writer.write_eof(data) + self._eof_sent = True + self._req = None + self._body_length = self._payload_writer.output_size + self._payload_writer = None + + def __repr__(self) -> str: + if self._eof_sent: + info = "eof" + elif self.prepared: + assert self._req is not None + info = f"{self._req.method} {self._req.path} " + else: + info = "not prepared" + return f"<{self.__class__.__name__} {self.reason} {info}>" + + def __getitem__(self, key: str) -> Any: + return self._state[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._state[key] = value + + def __delitem__(self, key: str) -> None: + del self._state[key] + + def __len__(self) -> int: + return len(self._state) + + def __iter__(self) -> Iterator[str]: + return iter(self._state) + + def __hash__(self) -> int: + return hash(id(self)) + + def __eq__(self, other: object) -> bool: + return self is other + + +class Response(StreamResponse): + def __init__( + self, + *, + body: Any = None, + status: int = 200, + reason: Optional[str] = None, + text: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + content_type: Optional[str] = None, + charset: Optional[str] = None, + zlib_executor_size: Optional[int] = None, + zlib_executor: Optional[Executor] = None, + ) -> None: + if body is not None and text is not None: + raise ValueError("body and text are not allowed together") + + if headers is None: + real_headers: CIMultiDict[str] = CIMultiDict() + elif not isinstance(headers, CIMultiDict): + real_headers = CIMultiDict(headers) + else: + real_headers = headers # = cast('CIMultiDict[str]', headers) + + if content_type is not None and "charset" in content_type: + raise ValueError("charset must not be in content_type " "argument") + + if text is not None: + if hdrs.CONTENT_TYPE in real_headers: + if content_type or charset: + raise ValueError( + "passing both Content-Type header and " + "content_type or charset params " + "is forbidden" + ) + else: + # fast path for filling headers + if not isinstance(text, str): + raise TypeError("text argument must be str (%r)" % type(text)) + if content_type is None: + content_type = "text/plain" + if charset is None: + charset = "utf-8" + real_headers[hdrs.CONTENT_TYPE] = content_type + "; charset=" + charset + body = text.encode(charset) + text = None + else: + if hdrs.CONTENT_TYPE in real_headers: + if content_type is not None or charset is not None: + raise ValueError( + "passing both Content-Type header and " + "content_type or charset params " + "is forbidden" + ) + else: + if content_type is not None: + if charset is not None: + content_type += "; charset=" + charset + real_headers[hdrs.CONTENT_TYPE] = content_type + + super().__init__(status=status, reason=reason, headers=real_headers) + + if text is not None: + self.text = text + else: + self.body = body + + self._compressed_body: Optional[bytes] = None + self._zlib_executor_size = zlib_executor_size + self._zlib_executor = zlib_executor + + @property + def body(self) -> Optional[Union[bytes, Payload]]: + return self._body + + @body.setter + def body(self, body: bytes) -> None: + if body is None: + self._body: Optional[bytes] = None + self._body_payload: bool = False + elif isinstance(body, (bytes, bytearray)): + self._body = body + self._body_payload = False + else: + try: + self._body = body = payload.PAYLOAD_REGISTRY.get(body) + except payload.LookupError: + raise ValueError("Unsupported body type %r" % type(body)) + + self._body_payload = True + + headers = self._headers + + # set content-type + if hdrs.CONTENT_TYPE not in headers: + headers[hdrs.CONTENT_TYPE] = body.content_type + + # copy payload headers + if body.headers: + for (key, value) in body.headers.items(): + if key not in headers: + headers[key] = value + + self._compressed_body = None + + @property + def text(self) -> Optional[str]: + if self._body is None: + return None + return self._body.decode(self.charset or "utf-8") + + @text.setter + def text(self, text: str) -> None: + assert text is None or isinstance( + text, str + ), "text argument must be str (%r)" % type(text) + + if self.content_type == "application/octet-stream": + self.content_type = "text/plain" + if self.charset is None: + self.charset = "utf-8" + + self._body = text.encode(self.charset) + self._body_payload = False + self._compressed_body = None + + @property + def content_length(self) -> Optional[int]: + if self._chunked: + return None + + if hdrs.CONTENT_LENGTH in self._headers: + return super().content_length + + if self._compressed_body is not None: + # Return length of the compressed body + return len(self._compressed_body) + elif self._body_payload: + # A payload without content length, or a compressed payload + return None + elif self._body is not None: + return len(self._body) + else: + return 0 + + @content_length.setter + def content_length(self, value: Optional[int]) -> None: + raise RuntimeError("Content length is set automatically") + + async def write_eof(self, data: bytes = b"") -> None: + if self._eof_sent: + return + if self._compressed_body is None: + body: Optional[Union[bytes, Payload]] = self._body + else: + body = self._compressed_body + assert not data, f"data arg is not supported, got {data!r}" + assert self._req is not None + assert self._payload_writer is not None + if body is not None: + if self._must_be_empty_body: + await super().write_eof() + elif self._body_payload: + payload = cast(Payload, body) + await payload.write(self._payload_writer) + await super().write_eof() + else: + await super().write_eof(cast(bytes, body)) + else: + await super().write_eof() + + async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: + if should_remove_content_length(request.method, self.status): + if hdrs.CONTENT_LENGTH in self._headers: + del self._headers[hdrs.CONTENT_LENGTH] + elif not self._chunked and hdrs.CONTENT_LENGTH not in self._headers: + if self._body_payload: + size = cast(Payload, self._body).size + if size is not None: + self._headers[hdrs.CONTENT_LENGTH] = str(size) + else: + body_len = len(self._body) if self._body else "0" + # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-7 + if body_len != "0" or ( + self.status != 304 and request.method.upper() != hdrs.METH_HEAD + ): + self._headers[hdrs.CONTENT_LENGTH] = str(body_len) + + return await super()._start(request) + + async def _do_start_compression(self, coding: ContentCoding) -> None: + if self._body_payload or self._chunked: + return await super()._do_start_compression(coding) + + if coding != ContentCoding.identity: + # Instead of using _payload_writer.enable_compression, + # compress the whole body + compressor = ZLibCompressor( + encoding=str(coding.value), + max_sync_chunk_size=self._zlib_executor_size, + executor=self._zlib_executor, + ) + assert self._body is not None + if self._zlib_executor_size is None and len(self._body) > 1024 * 1024: + warnings.warn( + "Synchronous compression of large response bodies " + f"({len(self._body)} bytes) might block the async event loop. " + "Consider providing a custom value to zlib_executor_size/" + "zlib_executor response properties or disabling compression on it." + ) + self._compressed_body = ( + await compressor.compress(self._body) + compressor.flush() + ) + assert self._compressed_body is not None + + self._headers[hdrs.CONTENT_ENCODING] = coding.value + self._headers[hdrs.CONTENT_LENGTH] = str(len(self._compressed_body)) + + +def json_response( + data: Any = sentinel, + *, + text: Optional[str] = None, + body: Optional[bytes] = None, + status: int = 200, + reason: Optional[str] = None, + headers: Optional[LooseHeaders] = None, + content_type: str = "application/json", + dumps: JSONEncoder = json.dumps, +) -> Response: + if data is not sentinel: + if text or body: + raise ValueError("only one of data, text, or body should be specified") + else: + text = dumps(data) + return Response( + text=text, + body=body, + status=status, + reason=reason, + headers=headers, + content_type=content_type, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_routedef.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_routedef.py new file mode 100644 index 0000000..d79cd32 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_routedef.py @@ -0,0 +1,216 @@ +import abc +import os # noqa +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterator, + List, + Optional, + Sequence, + Type, + Union, + overload, +) + +import attr + +from . import hdrs +from .abc import AbstractView +from .typedefs import Handler, PathLike + +if TYPE_CHECKING: + from .web_request import Request + from .web_response import StreamResponse + from .web_urldispatcher import AbstractRoute, UrlDispatcher +else: + Request = StreamResponse = UrlDispatcher = AbstractRoute = None + + +__all__ = ( + "AbstractRouteDef", + "RouteDef", + "StaticDef", + "RouteTableDef", + "head", + "options", + "get", + "post", + "patch", + "put", + "delete", + "route", + "view", + "static", +) + + +class AbstractRouteDef(abc.ABC): + @abc.abstractmethod + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + pass # pragma: no cover + + +_HandlerType = Union[Type[AbstractView], Handler] + + +@attr.s(auto_attribs=True, frozen=True, repr=False, slots=True) +class RouteDef(AbstractRouteDef): + method: str + path: str + handler: _HandlerType + kwargs: Dict[str, Any] + + def __repr__(self) -> str: + info = [] + for name, value in sorted(self.kwargs.items()): + info.append(f", {name}={value!r}") + return " {handler.__name__!r}" "{info}>".format( + method=self.method, path=self.path, handler=self.handler, info="".join(info) + ) + + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + if self.method in hdrs.METH_ALL: + reg = getattr(router, "add_" + self.method.lower()) + return [reg(self.path, self.handler, **self.kwargs)] + else: + return [ + router.add_route(self.method, self.path, self.handler, **self.kwargs) + ] + + +@attr.s(auto_attribs=True, frozen=True, repr=False, slots=True) +class StaticDef(AbstractRouteDef): + prefix: str + path: PathLike + kwargs: Dict[str, Any] + + def __repr__(self) -> str: + info = [] + for name, value in sorted(self.kwargs.items()): + info.append(f", {name}={value!r}") + return " {path}" "{info}>".format( + prefix=self.prefix, path=self.path, info="".join(info) + ) + + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + resource = router.add_static(self.prefix, self.path, **self.kwargs) + routes = resource.get_info().get("routes", {}) + return list(routes.values()) + + +def route(method: str, path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return RouteDef(method, path, handler, kwargs) + + +def head(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_HEAD, path, handler, **kwargs) + + +def options(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_OPTIONS, path, handler, **kwargs) + + +def get( + path: str, + handler: _HandlerType, + *, + name: Optional[str] = None, + allow_head: bool = True, + **kwargs: Any, +) -> RouteDef: + return route( + hdrs.METH_GET, path, handler, name=name, allow_head=allow_head, **kwargs + ) + + +def post(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_POST, path, handler, **kwargs) + + +def put(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_PUT, path, handler, **kwargs) + + +def patch(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_PATCH, path, handler, **kwargs) + + +def delete(path: str, handler: _HandlerType, **kwargs: Any) -> RouteDef: + return route(hdrs.METH_DELETE, path, handler, **kwargs) + + +def view(path: str, handler: Type[AbstractView], **kwargs: Any) -> RouteDef: + return route(hdrs.METH_ANY, path, handler, **kwargs) + + +def static(prefix: str, path: PathLike, **kwargs: Any) -> StaticDef: + return StaticDef(prefix, path, kwargs) + + +_Deco = Callable[[_HandlerType], _HandlerType] + + +class RouteTableDef(Sequence[AbstractRouteDef]): + """Route definition table""" + + def __init__(self) -> None: + self._items: List[AbstractRouteDef] = [] + + def __repr__(self) -> str: + return f"" + + @overload + def __getitem__(self, index: int) -> AbstractRouteDef: + ... + + @overload + def __getitem__(self, index: slice) -> List[AbstractRouteDef]: + ... + + def __getitem__(self, index): # type: ignore[no-untyped-def] + return self._items[index] + + def __iter__(self) -> Iterator[AbstractRouteDef]: + return iter(self._items) + + def __len__(self) -> int: + return len(self._items) + + def __contains__(self, item: object) -> bool: + return item in self._items + + def route(self, method: str, path: str, **kwargs: Any) -> _Deco: + def inner(handler: _HandlerType) -> _HandlerType: + self._items.append(RouteDef(method, path, handler, kwargs)) + return handler + + return inner + + def head(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_HEAD, path, **kwargs) + + def get(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_GET, path, **kwargs) + + def post(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_POST, path, **kwargs) + + def put(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_PUT, path, **kwargs) + + def patch(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_PATCH, path, **kwargs) + + def delete(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_DELETE, path, **kwargs) + + def options(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_OPTIONS, path, **kwargs) + + def view(self, path: str, **kwargs: Any) -> _Deco: + return self.route(hdrs.METH_ANY, path, **kwargs) + + def static(self, prefix: str, path: PathLike, **kwargs: Any) -> None: + self._items.append(StaticDef(prefix, path, kwargs)) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_runner.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_runner.py new file mode 100644 index 0000000..19a4441 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_runner.py @@ -0,0 +1,409 @@ +import asyncio +import signal +import socket +import warnings +from abc import ABC, abstractmethod +from typing import Any, Awaitable, Callable, List, Optional, Set + +from yarl import URL + +from .typedefs import PathLike +from .web_app import Application +from .web_server import Server + +try: + from ssl import SSLContext +except ImportError: + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ( + "BaseSite", + "TCPSite", + "UnixSite", + "NamedPipeSite", + "SockSite", + "BaseRunner", + "AppRunner", + "ServerRunner", + "GracefulExit", +) + + +class GracefulExit(SystemExit): + code = 1 + + +def _raise_graceful_exit() -> None: + raise GracefulExit() + + +class BaseSite(ABC): + __slots__ = ("_runner", "_ssl_context", "_backlog", "_server") + + def __init__( + self, + runner: "BaseRunner", + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + if runner.server is None: + raise RuntimeError("Call runner.setup() before making a site") + if shutdown_timeout != 60.0: + msg = "shutdown_timeout should be set on BaseRunner" + warnings.warn(msg, DeprecationWarning, stacklevel=2) + runner._shutdown_timeout = shutdown_timeout + self._runner = runner + self._ssl_context = ssl_context + self._backlog = backlog + self._server: Optional[asyncio.AbstractServer] = None + + @property + @abstractmethod + def name(self) -> str: + pass # pragma: no cover + + @abstractmethod + async def start(self) -> None: + self._runner._reg_site(self) + + async def stop(self) -> None: + self._runner._check_site(self) + if self._server is not None: # Maybe not started yet + self._server.close() + + self._runner._unreg_site(self) + + +class TCPSite(BaseSite): + __slots__ = ("_host", "_port", "_reuse_address", "_reuse_port") + + def __init__( + self, + runner: "BaseRunner", + host: Optional[str] = None, + port: Optional[int] = None, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + reuse_address: Optional[bool] = None, + reuse_port: Optional[bool] = None, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._host = host + if port is None: + port = 8443 if self._ssl_context else 8080 + self._port = port + self._reuse_address = reuse_address + self._reuse_port = reuse_port + + @property + def name(self) -> str: + scheme = "https" if self._ssl_context else "http" + host = "0.0.0.0" if self._host is None else self._host + return str(URL.build(scheme=scheme, host=host, port=self._port)) + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_server( + server, + self._host, + self._port, + ssl=self._ssl_context, + backlog=self._backlog, + reuse_address=self._reuse_address, + reuse_port=self._reuse_port, + ) + + +class UnixSite(BaseSite): + __slots__ = ("_path",) + + def __init__( + self, + runner: "BaseRunner", + path: PathLike, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._path = path + + @property + def name(self) -> str: + scheme = "https" if self._ssl_context else "http" + return f"{scheme}://unix:{self._path}:" + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_unix_server( + server, + self._path, + ssl=self._ssl_context, + backlog=self._backlog, + ) + + +class NamedPipeSite(BaseSite): + __slots__ = ("_path",) + + def __init__( + self, runner: "BaseRunner", path: str, *, shutdown_timeout: float = 60.0 + ) -> None: + loop = asyncio.get_event_loop() + if not isinstance( + loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] + ): + raise RuntimeError( + "Named Pipes only available in proactor" "loop under windows" + ) + super().__init__(runner, shutdown_timeout=shutdown_timeout) + self._path = path + + @property + def name(self) -> str: + return self._path + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + _server = await loop.start_serving_pipe( # type: ignore[attr-defined] + server, self._path + ) + self._server = _server[0] + + +class SockSite(BaseSite): + __slots__ = ("_sock", "_name") + + def __init__( + self, + runner: "BaseRunner", + sock: socket.socket, + *, + shutdown_timeout: float = 60.0, + ssl_context: Optional[SSLContext] = None, + backlog: int = 128, + ) -> None: + super().__init__( + runner, + shutdown_timeout=shutdown_timeout, + ssl_context=ssl_context, + backlog=backlog, + ) + self._sock = sock + scheme = "https" if self._ssl_context else "http" + if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: + name = f"{scheme}://unix:{sock.getsockname()}:" + else: + host, port = sock.getsockname()[:2] + name = str(URL.build(scheme=scheme, host=host, port=port)) + self._name = name + + @property + def name(self) -> str: + return self._name + + async def start(self) -> None: + await super().start() + loop = asyncio.get_event_loop() + server = self._runner.server + assert server is not None + self._server = await loop.create_server( + server, sock=self._sock, ssl=self._ssl_context, backlog=self._backlog + ) + + +class BaseRunner(ABC): + __slots__ = ( + "shutdown_callback", + "_handle_signals", + "_kwargs", + "_server", + "_sites", + "_shutdown_timeout", + ) + + def __init__( + self, + *, + handle_signals: bool = False, + shutdown_timeout: float = 60.0, + **kwargs: Any, + ) -> None: + self.shutdown_callback: Optional[Callable[[], Awaitable[None]]] = None + self._handle_signals = handle_signals + self._kwargs = kwargs + self._server: Optional[Server] = None + self._sites: List[BaseSite] = [] + self._shutdown_timeout = shutdown_timeout + + @property + def server(self) -> Optional[Server]: + return self._server + + @property + def addresses(self) -> List[Any]: + ret: List[Any] = [] + for site in self._sites: + server = site._server + if server is not None: + sockets = server.sockets # type: ignore[attr-defined] + if sockets is not None: + for sock in sockets: + ret.append(sock.getsockname()) + return ret + + @property + def sites(self) -> Set[BaseSite]: + return set(self._sites) + + async def setup(self) -> None: + loop = asyncio.get_event_loop() + + if self._handle_signals: + try: + loop.add_signal_handler(signal.SIGINT, _raise_graceful_exit) + loop.add_signal_handler(signal.SIGTERM, _raise_graceful_exit) + except NotImplementedError: # pragma: no cover + # add_signal_handler is not implemented on Windows + pass + + self._server = await self._make_server() + + @abstractmethod + async def shutdown(self) -> None: + """Call any shutdown hooks to help server close gracefully.""" + + async def cleanup(self) -> None: + # The loop over sites is intentional, an exception on gather() + # leaves self._sites in unpredictable state. + # The loop guaranties that a site is either deleted on success or + # still present on failure + for site in list(self._sites): + await site.stop() + + if self._server: # If setup succeeded + # Yield to event loop to ensure incoming requests prior to stopping the sites + # have all started to be handled before we proceed to close idle connections. + await asyncio.sleep(0) + self._server.pre_shutdown() + await self.shutdown() + + if self.shutdown_callback: + await self.shutdown_callback() + + await self._server.shutdown(self._shutdown_timeout) + await self._cleanup_server() + + self._server = None + if self._handle_signals: + loop = asyncio.get_running_loop() + try: + loop.remove_signal_handler(signal.SIGINT) + loop.remove_signal_handler(signal.SIGTERM) + except NotImplementedError: # pragma: no cover + # remove_signal_handler is not implemented on Windows + pass + + @abstractmethod + async def _make_server(self) -> Server: + pass # pragma: no cover + + @abstractmethod + async def _cleanup_server(self) -> None: + pass # pragma: no cover + + def _reg_site(self, site: BaseSite) -> None: + if site in self._sites: + raise RuntimeError(f"Site {site} is already registered in runner {self}") + self._sites.append(site) + + def _check_site(self, site: BaseSite) -> None: + if site not in self._sites: + raise RuntimeError(f"Site {site} is not registered in runner {self}") + + def _unreg_site(self, site: BaseSite) -> None: + if site not in self._sites: + raise RuntimeError(f"Site {site} is not registered in runner {self}") + self._sites.remove(site) + + +class ServerRunner(BaseRunner): + """Low-level web server runner""" + + __slots__ = ("_web_server",) + + def __init__( + self, web_server: Server, *, handle_signals: bool = False, **kwargs: Any + ) -> None: + super().__init__(handle_signals=handle_signals, **kwargs) + self._web_server = web_server + + async def shutdown(self) -> None: + pass + + async def _make_server(self) -> Server: + return self._web_server + + async def _cleanup_server(self) -> None: + pass + + +class AppRunner(BaseRunner): + """Web Application runner""" + + __slots__ = ("_app",) + + def __init__( + self, app: Application, *, handle_signals: bool = False, **kwargs: Any + ) -> None: + super().__init__(handle_signals=handle_signals, **kwargs) + if not isinstance(app, Application): + raise TypeError( + "The first argument should be web.Application " + "instance, got {!r}".format(app) + ) + self._app = app + + @property + def app(self) -> Application: + return self._app + + async def shutdown(self) -> None: + await self._app.shutdown() + + async def _make_server(self) -> Server: + loop = asyncio.get_event_loop() + self._app._set_loop(loop) + self._app.on_startup.freeze() + await self._app.startup() + self._app.freeze() + + return self._app._make_handler(loop=loop, **self._kwargs) + + async def _cleanup_server(self) -> None: + await self._app.cleanup() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_server.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_server.py new file mode 100644 index 0000000..52faacb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_server.py @@ -0,0 +1,77 @@ +"""Low level HTTP server.""" +import asyncio +from typing import Any, Awaitable, Callable, Dict, List, Optional # noqa + +from .abc import AbstractStreamWriter +from .helpers import get_running_loop +from .http_parser import RawRequestMessage +from .streams import StreamReader +from .web_protocol import RequestHandler, _RequestFactory, _RequestHandler +from .web_request import BaseRequest + +__all__ = ("Server",) + + +class Server: + def __init__( + self, + handler: _RequestHandler, + *, + request_factory: Optional[_RequestFactory] = None, + handler_cancellation: bool = False, + loop: Optional[asyncio.AbstractEventLoop] = None, + **kwargs: Any + ) -> None: + self._loop = get_running_loop(loop) + self._connections: Dict[RequestHandler, asyncio.Transport] = {} + self._kwargs = kwargs + self.requests_count = 0 + self.request_handler = handler + self.request_factory = request_factory or self._make_request + self.handler_cancellation = handler_cancellation + + @property + def connections(self) -> List[RequestHandler]: + return list(self._connections.keys()) + + def connection_made( + self, handler: RequestHandler, transport: asyncio.Transport + ) -> None: + self._connections[handler] = transport + + def connection_lost( + self, handler: RequestHandler, exc: Optional[BaseException] = None + ) -> None: + if handler in self._connections: + del self._connections[handler] + + def _make_request( + self, + message: RawRequestMessage, + payload: StreamReader, + protocol: RequestHandler, + writer: AbstractStreamWriter, + task: "asyncio.Task[None]", + ) -> BaseRequest: + return BaseRequest(message, payload, protocol, writer, task, self._loop) + + def pre_shutdown(self) -> None: + for conn in self._connections: + conn.close() + + async def shutdown(self, timeout: Optional[float] = None) -> None: + coros = (conn.shutdown(timeout) for conn in self._connections) + await asyncio.gather(*coros) + self._connections.clear() + + def __call__(self) -> RequestHandler: + try: + return RequestHandler(self, loop=self._loop, **self._kwargs) + except TypeError: + # Failsafe creation: remove all custom handler_args + kwargs = { + k: v + for k, v in self._kwargs.items() + if k in ["debug", "access_log_class"] + } + return RequestHandler(self, loop=self._loop, **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_urldispatcher.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_urldispatcher.py new file mode 100644 index 0000000..954291f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_urldispatcher.py @@ -0,0 +1,1234 @@ +import abc +import asyncio +import base64 +import functools +import hashlib +import html +import inspect +import keyword +import os +import re +import warnings +from contextlib import contextmanager +from functools import wraps +from pathlib import Path +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Container, + Dict, + Final, + Generator, + Iterable, + Iterator, + List, + Mapping, + NoReturn, + Optional, + Pattern, + Set, + Sized, + Tuple, + Type, + TypedDict, + Union, + cast, +) + +from yarl import URL, __version__ as yarl_version # type: ignore[attr-defined] + +from . import hdrs +from .abc import AbstractMatchInfo, AbstractRouter, AbstractView +from .helpers import DEBUG +from .http import HttpVersion11 +from .typedefs import Handler, PathLike +from .web_exceptions import ( + HTTPException, + HTTPExpectationFailed, + HTTPForbidden, + HTTPMethodNotAllowed, + HTTPNotFound, +) +from .web_fileresponse import FileResponse +from .web_request import Request +from .web_response import Response, StreamResponse +from .web_routedef import AbstractRouteDef + +__all__ = ( + "UrlDispatcher", + "UrlMappingMatchInfo", + "AbstractResource", + "Resource", + "PlainResource", + "DynamicResource", + "AbstractRoute", + "ResourceRoute", + "StaticResource", + "View", +) + + +if TYPE_CHECKING: + from .web_app import Application + + BaseDict = Dict[str, str] +else: + BaseDict = dict + +YARL_VERSION: Final[Tuple[int, ...]] = tuple(map(int, yarl_version.split(".")[:2])) + +HTTP_METHOD_RE: Final[Pattern[str]] = re.compile( + r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$" +) +ROUTE_RE: Final[Pattern[str]] = re.compile( + r"(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})" +) +PATH_SEP: Final[str] = re.escape("/") + + +_ExpectHandler = Callable[[Request], Awaitable[Optional[StreamResponse]]] +_Resolve = Tuple[Optional["UrlMappingMatchInfo"], Set[str]] + +html_escape = functools.partial(html.escape, quote=True) + + +class _InfoDict(TypedDict, total=False): + path: str + + formatter: str + pattern: Pattern[str] + + directory: Path + prefix: str + routes: Mapping[str, "AbstractRoute"] + + app: "Application" + + domain: str + + rule: "AbstractRuleMatching" + + http_exception: HTTPException + + +class AbstractResource(Sized, Iterable["AbstractRoute"]): + def __init__(self, *, name: Optional[str] = None) -> None: + self._name = name + + @property + def name(self) -> Optional[str]: + return self._name + + @property + @abc.abstractmethod + def canonical(self) -> str: + """Exposes the resource's canonical path. + + For example '/foo/bar/{name}' + + """ + + @abc.abstractmethod # pragma: no branch + def url_for(self, **kwargs: str) -> URL: + """Construct url for resource with additional params.""" + + @abc.abstractmethod # pragma: no branch + async def resolve(self, request: Request) -> _Resolve: + """Resolve resource. + + Return (UrlMappingMatchInfo, allowed_methods) pair. + """ + + @abc.abstractmethod + def add_prefix(self, prefix: str) -> None: + """Add a prefix to processed URLs. + + Required for subapplications support. + """ + + @abc.abstractmethod + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + def freeze(self) -> None: + pass + + @abc.abstractmethod + def raw_match(self, path: str) -> bool: + """Perform a raw match against path""" + + +class AbstractRoute(abc.ABC): + def __init__( + self, + method: str, + handler: Union[Handler, Type[AbstractView]], + *, + expect_handler: Optional[_ExpectHandler] = None, + resource: Optional[AbstractResource] = None, + ) -> None: + + if expect_handler is None: + expect_handler = _default_expect_handler + + assert asyncio.iscoroutinefunction( + expect_handler + ), f"Coroutine is expected, got {expect_handler!r}" + + method = method.upper() + if not HTTP_METHOD_RE.match(method): + raise ValueError(f"{method} is not allowed HTTP method") + + assert callable(handler), handler + if asyncio.iscoroutinefunction(handler): + pass + elif inspect.isgeneratorfunction(handler): + warnings.warn( + "Bare generators are deprecated, " "use @coroutine wrapper", + DeprecationWarning, + ) + elif isinstance(handler, type) and issubclass(handler, AbstractView): + pass + else: + warnings.warn( + "Bare functions are deprecated, " "use async ones", DeprecationWarning + ) + + @wraps(handler) + async def handler_wrapper(request: Request) -> StreamResponse: + result = old_handler(request) + if asyncio.iscoroutine(result): + result = await result + assert isinstance(result, StreamResponse) + return result + + old_handler = handler + handler = handler_wrapper + + self._method = method + self._handler = handler + self._expect_handler = expect_handler + self._resource = resource + + @property + def method(self) -> str: + return self._method + + @property + def handler(self) -> Handler: + return self._handler + + @property + @abc.abstractmethod + def name(self) -> Optional[str]: + """Optional route's name, always equals to resource's name.""" + + @property + def resource(self) -> Optional[AbstractResource]: + return self._resource + + @abc.abstractmethod + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + @abc.abstractmethod # pragma: no branch + def url_for(self, *args: str, **kwargs: str) -> URL: + """Construct url for route with additional params.""" + + async def handle_expect_header(self, request: Request) -> Optional[StreamResponse]: + return await self._expect_handler(request) + + +class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo): + def __init__(self, match_dict: Dict[str, str], route: AbstractRoute): + super().__init__(match_dict) + self._route = route + self._apps: List[Application] = [] + self._current_app: Optional[Application] = None + self._frozen = False + + @property + def handler(self) -> Handler: + return self._route.handler + + @property + def route(self) -> AbstractRoute: + return self._route + + @property + def expect_handler(self) -> _ExpectHandler: + return self._route.handle_expect_header + + @property + def http_exception(self) -> Optional[HTTPException]: + return None + + def get_info(self) -> _InfoDict: # type: ignore[override] + return self._route.get_info() + + @property + def apps(self) -> Tuple["Application", ...]: + return tuple(self._apps) + + def add_app(self, app: "Application") -> None: + if self._frozen: + raise RuntimeError("Cannot change apps stack after .freeze() call") + if self._current_app is None: + self._current_app = app + self._apps.insert(0, app) + + @property + def current_app(self) -> "Application": + app = self._current_app + assert app is not None + return app + + @contextmanager + def set_current_app(self, app: "Application") -> Generator[None, None, None]: + if DEBUG: # pragma: no cover + if app not in self._apps: + raise RuntimeError( + "Expected one of the following apps {!r}, got {!r}".format( + self._apps, app + ) + ) + prev = self._current_app + self._current_app = app + try: + yield + finally: + self._current_app = prev + + def freeze(self) -> None: + self._frozen = True + + def __repr__(self) -> str: + return f"" + + +class MatchInfoError(UrlMappingMatchInfo): + def __init__(self, http_exception: HTTPException) -> None: + self._exception = http_exception + super().__init__({}, SystemRoute(self._exception)) + + @property + def http_exception(self) -> HTTPException: + return self._exception + + def __repr__(self) -> str: + return "".format( + self._exception.status, self._exception.reason + ) + + +async def _default_expect_handler(request: Request) -> None: + """Default handler for Expect header. + + Just send "100 Continue" to client. + raise HTTPExpectationFailed if value of header is not "100-continue" + """ + expect = request.headers.get(hdrs.EXPECT, "") + if request.version == HttpVersion11: + if expect.lower() == "100-continue": + await request.writer.write(b"HTTP/1.1 100 Continue\r\n\r\n") + else: + raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect) + + +class Resource(AbstractResource): + def __init__(self, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + self._routes: List[ResourceRoute] = [] + + def add_route( + self, + method: str, + handler: Union[Type[AbstractView], Handler], + *, + expect_handler: Optional[_ExpectHandler] = None, + ) -> "ResourceRoute": + + for route_obj in self._routes: + if route_obj.method == method or route_obj.method == hdrs.METH_ANY: + raise RuntimeError( + "Added route will never be executed, " + "method {route.method} is already " + "registered".format(route=route_obj) + ) + + route_obj = ResourceRoute(method, handler, self, expect_handler=expect_handler) + self.register_route(route_obj) + return route_obj + + def register_route(self, route: "ResourceRoute") -> None: + assert isinstance( + route, ResourceRoute + ), f"Instance of Route class is required, got {route!r}" + self._routes.append(route) + + async def resolve(self, request: Request) -> _Resolve: + allowed_methods: Set[str] = set() + + match_dict = self._match(request.rel_url.raw_path) + if match_dict is None: + return None, allowed_methods + + for route_obj in self._routes: + route_method = route_obj.method + allowed_methods.add(route_method) + + if route_method == request.method or route_method == hdrs.METH_ANY: + return (UrlMappingMatchInfo(match_dict, route_obj), allowed_methods) + else: + return None, allowed_methods + + @abc.abstractmethod + def _match(self, path: str) -> Optional[Dict[str, str]]: + pass # pragma: no cover + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator["ResourceRoute"]: + return iter(self._routes) + + # TODO: implement all abstract methods + + +class PlainResource(Resource): + def __init__(self, path: str, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + assert not path or path.startswith("/") + self._path = path + + @property + def canonical(self) -> str: + return self._path + + def freeze(self) -> None: + if not self._path: + self._path = "/" + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._path = prefix + self._path + + def _match(self, path: str) -> Optional[Dict[str, str]]: + # string comparison is about 10 times faster than regexp matching + if self._path == path: + return {} + else: + return None + + def raw_match(self, path: str) -> bool: + return self._path == path + + def get_info(self) -> _InfoDict: + return {"path": self._path} + + def url_for(self) -> URL: # type: ignore[override] + return URL.build(path=self._path, encoded=True) + + def __repr__(self) -> str: + name = "'" + self.name + "' " if self.name is not None else "" + return f"" + + +class DynamicResource(Resource): + + DYN = re.compile(r"\{(?P[_a-zA-Z][_a-zA-Z0-9]*)\}") + DYN_WITH_RE = re.compile(r"\{(?P[_a-zA-Z][_a-zA-Z0-9]*):(?P.+)\}") + GOOD = r"[^{}/]+" + + def __init__(self, path: str, *, name: Optional[str] = None) -> None: + super().__init__(name=name) + pattern = "" + formatter = "" + for part in ROUTE_RE.split(path): + match = self.DYN.fullmatch(part) + if match: + pattern += "(?P<{}>{})".format(match.group("var"), self.GOOD) + formatter += "{" + match.group("var") + "}" + continue + + match = self.DYN_WITH_RE.fullmatch(part) + if match: + pattern += "(?P<{var}>{re})".format(**match.groupdict()) + formatter += "{" + match.group("var") + "}" + continue + + if "{" in part or "}" in part: + raise ValueError(f"Invalid path '{path}'['{part}']") + + part = _requote_path(part) + formatter += part + pattern += re.escape(part) + + try: + compiled = re.compile(pattern) + except re.error as exc: + raise ValueError(f"Bad pattern '{pattern}': {exc}") from None + assert compiled.pattern.startswith(PATH_SEP) + assert formatter.startswith("/") + self._pattern = compiled + self._formatter = formatter + + @property + def canonical(self) -> str: + return self._formatter + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._pattern = re.compile(re.escape(prefix) + self._pattern.pattern) + self._formatter = prefix + self._formatter + + def _match(self, path: str) -> Optional[Dict[str, str]]: + match = self._pattern.fullmatch(path) + if match is None: + return None + else: + return { + key: _unquote_path(value) for key, value in match.groupdict().items() + } + + def raw_match(self, path: str) -> bool: + return self._formatter == path + + def get_info(self) -> _InfoDict: + return {"formatter": self._formatter, "pattern": self._pattern} + + def url_for(self, **parts: str) -> URL: + url = self._formatter.format_map({k: _quote_path(v) for k, v in parts.items()}) + return URL.build(path=url, encoded=True) + + def __repr__(self) -> str: + name = "'" + self.name + "' " if self.name is not None else "" + return "".format( + name=name, formatter=self._formatter + ) + + +class PrefixResource(AbstractResource): + def __init__(self, prefix: str, *, name: Optional[str] = None) -> None: + assert not prefix or prefix.startswith("/"), prefix + assert prefix in ("", "/") or not prefix.endswith("/"), prefix + super().__init__(name=name) + self._prefix = _requote_path(prefix) + self._prefix2 = self._prefix + "/" + + @property + def canonical(self) -> str: + return self._prefix + + def add_prefix(self, prefix: str) -> None: + assert prefix.startswith("/") + assert not prefix.endswith("/") + assert len(prefix) > 1 + self._prefix = prefix + self._prefix + self._prefix2 = self._prefix + "/" + + def raw_match(self, prefix: str) -> bool: + return False + + # TODO: impl missing abstract methods + + +class StaticResource(PrefixResource): + VERSION_KEY = "v" + + def __init__( + self, + prefix: str, + directory: PathLike, + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + chunk_size: int = 256 * 1024, + show_index: bool = False, + follow_symlinks: bool = False, + append_version: bool = False, + ) -> None: + super().__init__(prefix, name=name) + try: + directory = Path(directory) + if str(directory).startswith("~"): + directory = Path(os.path.expanduser(str(directory))) + directory = directory.resolve() + if not directory.is_dir(): + raise ValueError("Not a directory") + except (FileNotFoundError, ValueError) as error: + raise ValueError(f"No directory exists at '{directory}'") from error + self._directory = directory + self._show_index = show_index + self._chunk_size = chunk_size + self._follow_symlinks = follow_symlinks + self._expect_handler = expect_handler + self._append_version = append_version + + self._routes = { + "GET": ResourceRoute( + "GET", self._handle, self, expect_handler=expect_handler + ), + "HEAD": ResourceRoute( + "HEAD", self._handle, self, expect_handler=expect_handler + ), + } + + def url_for( # type: ignore[override] + self, + *, + filename: PathLike, + append_version: Optional[bool] = None, + ) -> URL: + if append_version is None: + append_version = self._append_version + filename = str(filename).lstrip("/") + + url = URL.build(path=self._prefix, encoded=True) + # filename is not encoded + if YARL_VERSION < (1, 6): + url = url / filename.replace("%", "%25") + else: + url = url / filename + + if append_version: + unresolved_path = self._directory.joinpath(filename) + try: + if self._follow_symlinks: + normalized_path = Path(os.path.normpath(unresolved_path)) + normalized_path.relative_to(self._directory) + filepath = normalized_path.resolve() + else: + filepath = unresolved_path.resolve() + filepath.relative_to(self._directory) + except (ValueError, FileNotFoundError): + # ValueError for case when path point to symlink + # with follow_symlinks is False + return url # relatively safe + if filepath.is_file(): + # TODO cache file content + # with file watcher for cache invalidation + with filepath.open("rb") as f: + file_bytes = f.read() + h = self._get_file_hash(file_bytes) + url = url.with_query({self.VERSION_KEY: h}) + return url + return url + + @staticmethod + def _get_file_hash(byte_array: bytes) -> str: + m = hashlib.sha256() # todo sha256 can be configurable param + m.update(byte_array) + b64 = base64.urlsafe_b64encode(m.digest()) + return b64.decode("ascii") + + def get_info(self) -> _InfoDict: + return { + "directory": self._directory, + "prefix": self._prefix, + "routes": self._routes, + } + + def set_options_route(self, handler: Handler) -> None: + if "OPTIONS" in self._routes: + raise RuntimeError("OPTIONS route was set already") + self._routes["OPTIONS"] = ResourceRoute( + "OPTIONS", handler, self, expect_handler=self._expect_handler + ) + + async def resolve(self, request: Request) -> _Resolve: + path = request.rel_url.raw_path + method = request.method + allowed_methods = set(self._routes) + if not path.startswith(self._prefix2) and path != self._prefix: + return None, set() + + if method not in allowed_methods: + return None, allowed_methods + + match_dict = {"filename": _unquote_path(path[len(self._prefix) + 1 :])} + return (UrlMappingMatchInfo(match_dict, self._routes[method]), allowed_methods) + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator[AbstractRoute]: + return iter(self._routes.values()) + + async def _handle(self, request: Request) -> StreamResponse: + rel_url = request.match_info["filename"] + try: + filename = Path(rel_url) + if filename.anchor: + # rel_url is an absolute name like + # /static/\\machine_name\c$ or /static/D:\path + # where the static dir is totally different + raise HTTPForbidden() + unresolved_path = self._directory.joinpath(filename) + if self._follow_symlinks: + normalized_path = Path(os.path.normpath(unresolved_path)) + normalized_path.relative_to(self._directory) + filepath = normalized_path.resolve() + else: + filepath = unresolved_path.resolve() + filepath.relative_to(self._directory) + except (ValueError, FileNotFoundError) as error: + # relatively safe + raise HTTPNotFound() from error + except HTTPForbidden: + raise + except Exception as error: + # perm error or other kind! + request.app.logger.exception(error) + raise HTTPNotFound() from error + + # on opening a dir, load its contents if allowed + if filepath.is_dir(): + if self._show_index: + try: + return Response( + text=self._directory_as_html(filepath), content_type="text/html" + ) + except PermissionError: + raise HTTPForbidden() + else: + raise HTTPForbidden() + elif filepath.is_file(): + return FileResponse(filepath, chunk_size=self._chunk_size) + else: + raise HTTPNotFound + + def _directory_as_html(self, filepath: Path) -> str: + # returns directory's index as html + + # sanity check + assert filepath.is_dir() + + relative_path_to_dir = filepath.relative_to(self._directory).as_posix() + index_of = f"Index of /{html_escape(relative_path_to_dir)}" + h1 = f"

{index_of}

" + + index_list = [] + dir_index = filepath.iterdir() + for _file in sorted(dir_index): + # show file url as relative to static path + rel_path = _file.relative_to(self._directory).as_posix() + quoted_file_url = _quote_path(f"{self._prefix}/{rel_path}") + + # if file is a directory, add '/' to the end of the name + if _file.is_dir(): + file_name = f"{_file.name}/" + else: + file_name = _file.name + + index_list.append( + f'
  • {html_escape(file_name)}
  • ' + ) + ul = "
      \n{}\n
    ".format("\n".join(index_list)) + body = f"\n{h1}\n{ul}\n" + + head_str = f"\n{index_of}\n" + html = f"\n{head_str}\n{body}\n" + + return html + + def __repr__(self) -> str: + name = "'" + self.name + "'" if self.name is not None else "" + return " {directory!r}>".format( + name=name, path=self._prefix, directory=self._directory + ) + + +class PrefixedSubAppResource(PrefixResource): + def __init__(self, prefix: str, app: "Application") -> None: + super().__init__(prefix) + self._app = app + for resource in app.router.resources(): + resource.add_prefix(prefix) + + def add_prefix(self, prefix: str) -> None: + super().add_prefix(prefix) + for resource in self._app.router.resources(): + resource.add_prefix(prefix) + + def url_for(self, *args: str, **kwargs: str) -> URL: + raise RuntimeError(".url_for() is not supported " "by sub-application root") + + def get_info(self) -> _InfoDict: + return {"app": self._app, "prefix": self._prefix} + + async def resolve(self, request: Request) -> _Resolve: + if ( + not request.url.raw_path.startswith(self._prefix2) + and request.url.raw_path != self._prefix + ): + return None, set() + match_info = await self._app.router.resolve(request) + match_info.add_app(self._app) + if isinstance(match_info.http_exception, HTTPMethodNotAllowed): + methods = match_info.http_exception.allowed_methods + else: + methods = set() + return match_info, methods + + def __len__(self) -> int: + return len(self._app.router.routes()) + + def __iter__(self) -> Iterator[AbstractRoute]: + return iter(self._app.router.routes()) + + def __repr__(self) -> str: + return " {app!r}>".format( + prefix=self._prefix, app=self._app + ) + + +class AbstractRuleMatching(abc.ABC): + @abc.abstractmethod # pragma: no branch + async def match(self, request: Request) -> bool: + """Return bool if the request satisfies the criteria""" + + @abc.abstractmethod # pragma: no branch + def get_info(self) -> _InfoDict: + """Return a dict with additional info useful for introspection""" + + @property + @abc.abstractmethod # pragma: no branch + def canonical(self) -> str: + """Return a str""" + + +class Domain(AbstractRuleMatching): + re_part = re.compile(r"(?!-)[a-z\d-]{1,63}(? None: + super().__init__() + self._domain = self.validation(domain) + + @property + def canonical(self) -> str: + return self._domain + + def validation(self, domain: str) -> str: + if not isinstance(domain, str): + raise TypeError("Domain must be str") + domain = domain.rstrip(".").lower() + if not domain: + raise ValueError("Domain cannot be empty") + elif "://" in domain: + raise ValueError("Scheme not supported") + url = URL("http://" + domain) + assert url.raw_host is not None + if not all(self.re_part.fullmatch(x) for x in url.raw_host.split(".")): + raise ValueError("Domain not valid") + if url.port == 80: + return url.raw_host + return f"{url.raw_host}:{url.port}" + + async def match(self, request: Request) -> bool: + host = request.headers.get(hdrs.HOST) + if not host: + return False + return self.match_domain(host) + + def match_domain(self, host: str) -> bool: + return host.lower() == self._domain + + def get_info(self) -> _InfoDict: + return {"domain": self._domain} + + +class MaskDomain(Domain): + re_part = re.compile(r"(?!-)[a-z\d\*-]{1,63}(? None: + super().__init__(domain) + mask = self._domain.replace(".", r"\.").replace("*", ".*") + self._mask = re.compile(mask) + + @property + def canonical(self) -> str: + return self._mask.pattern + + def match_domain(self, host: str) -> bool: + return self._mask.fullmatch(host) is not None + + +class MatchedSubAppResource(PrefixedSubAppResource): + def __init__(self, rule: AbstractRuleMatching, app: "Application") -> None: + AbstractResource.__init__(self) + self._prefix = "" + self._app = app + self._rule = rule + + @property + def canonical(self) -> str: + return self._rule.canonical + + def get_info(self) -> _InfoDict: + return {"app": self._app, "rule": self._rule} + + async def resolve(self, request: Request) -> _Resolve: + if not await self._rule.match(request): + return None, set() + match_info = await self._app.router.resolve(request) + match_info.add_app(self._app) + if isinstance(match_info.http_exception, HTTPMethodNotAllowed): + methods = match_info.http_exception.allowed_methods + else: + methods = set() + return match_info, methods + + def __repr__(self) -> str: + return " {app!r}>" "".format(app=self._app) + + +class ResourceRoute(AbstractRoute): + """A route with resource""" + + def __init__( + self, + method: str, + handler: Union[Handler, Type[AbstractView]], + resource: AbstractResource, + *, + expect_handler: Optional[_ExpectHandler] = None, + ) -> None: + super().__init__( + method, handler, expect_handler=expect_handler, resource=resource + ) + + def __repr__(self) -> str: + return " {handler!r}".format( + method=self.method, resource=self._resource, handler=self.handler + ) + + @property + def name(self) -> Optional[str]: + if self._resource is None: + return None + return self._resource.name + + def url_for(self, *args: str, **kwargs: str) -> URL: + """Construct url for route with additional params.""" + assert self._resource is not None + return self._resource.url_for(*args, **kwargs) + + def get_info(self) -> _InfoDict: + assert self._resource is not None + return self._resource.get_info() + + +class SystemRoute(AbstractRoute): + def __init__(self, http_exception: HTTPException) -> None: + super().__init__(hdrs.METH_ANY, self._handle) + self._http_exception = http_exception + + def url_for(self, *args: str, **kwargs: str) -> URL: + raise RuntimeError(".url_for() is not allowed for SystemRoute") + + @property + def name(self) -> Optional[str]: + return None + + def get_info(self) -> _InfoDict: + return {"http_exception": self._http_exception} + + async def _handle(self, request: Request) -> StreamResponse: + raise self._http_exception + + @property + def status(self) -> int: + return self._http_exception.status + + @property + def reason(self) -> str: + return self._http_exception.reason + + def __repr__(self) -> str: + return "".format(self=self) + + +class View(AbstractView): + async def _iter(self) -> StreamResponse: + if self.request.method not in hdrs.METH_ALL: + self._raise_allowed_methods() + method: Optional[Callable[[], Awaitable[StreamResponse]]] + method = getattr(self, self.request.method.lower(), None) + if method is None: + self._raise_allowed_methods() + ret = await method() + assert isinstance(ret, StreamResponse) + return ret + + def __await__(self) -> Generator[Any, None, StreamResponse]: + return self._iter().__await__() + + def _raise_allowed_methods(self) -> NoReturn: + allowed_methods = {m for m in hdrs.METH_ALL if hasattr(self, m.lower())} + raise HTTPMethodNotAllowed(self.request.method, allowed_methods) + + +class ResourcesView(Sized, Iterable[AbstractResource], Container[AbstractResource]): + def __init__(self, resources: List[AbstractResource]) -> None: + self._resources = resources + + def __len__(self) -> int: + return len(self._resources) + + def __iter__(self) -> Iterator[AbstractResource]: + yield from self._resources + + def __contains__(self, resource: object) -> bool: + return resource in self._resources + + +class RoutesView(Sized, Iterable[AbstractRoute], Container[AbstractRoute]): + def __init__(self, resources: List[AbstractResource]): + self._routes: List[AbstractRoute] = [] + for resource in resources: + for route in resource: + self._routes.append(route) + + def __len__(self) -> int: + return len(self._routes) + + def __iter__(self) -> Iterator[AbstractRoute]: + yield from self._routes + + def __contains__(self, route: object) -> bool: + return route in self._routes + + +class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]): + + NAME_SPLIT_RE = re.compile(r"[.:-]") + + def __init__(self) -> None: + super().__init__() + self._resources: List[AbstractResource] = [] + self._named_resources: Dict[str, AbstractResource] = {} + + async def resolve(self, request: Request) -> UrlMappingMatchInfo: + method = request.method + allowed_methods: Set[str] = set() + + for resource in self._resources: + match_dict, allowed = await resource.resolve(request) + if match_dict is not None: + return match_dict + else: + allowed_methods |= allowed + + if allowed_methods: + return MatchInfoError(HTTPMethodNotAllowed(method, allowed_methods)) + else: + return MatchInfoError(HTTPNotFound()) + + def __iter__(self) -> Iterator[str]: + return iter(self._named_resources) + + def __len__(self) -> int: + return len(self._named_resources) + + def __contains__(self, resource: object) -> bool: + return resource in self._named_resources + + def __getitem__(self, name: str) -> AbstractResource: + return self._named_resources[name] + + def resources(self) -> ResourcesView: + return ResourcesView(self._resources) + + def routes(self) -> RoutesView: + return RoutesView(self._resources) + + def named_resources(self) -> Mapping[str, AbstractResource]: + return MappingProxyType(self._named_resources) + + def register_resource(self, resource: AbstractResource) -> None: + assert isinstance( + resource, AbstractResource + ), f"Instance of AbstractResource class is required, got {resource!r}" + if self.frozen: + raise RuntimeError("Cannot register a resource into frozen router.") + + name = resource.name + + if name is not None: + parts = self.NAME_SPLIT_RE.split(name) + for part in parts: + if keyword.iskeyword(part): + raise ValueError( + f"Incorrect route name {name!r}, " + "python keywords cannot be used " + "for route name" + ) + if not part.isidentifier(): + raise ValueError( + "Incorrect route name {!r}, " + "the name should be a sequence of " + "python identifiers separated " + "by dash, dot or column".format(name) + ) + if name in self._named_resources: + raise ValueError( + "Duplicate {!r}, " + "already handled by {!r}".format(name, self._named_resources[name]) + ) + self._named_resources[name] = resource + self._resources.append(resource) + + def add_resource(self, path: str, *, name: Optional[str] = None) -> Resource: + if path and not path.startswith("/"): + raise ValueError("path should be started with / or be empty") + # Reuse last added resource if path and name are the same + if self._resources: + resource = self._resources[-1] + if resource.name == name and resource.raw_match(path): + return cast(Resource, resource) + if not ("{" in path or "}" in path or ROUTE_RE.search(path)): + resource = PlainResource(_requote_path(path), name=name) + self.register_resource(resource) + return resource + resource = DynamicResource(path, name=name) + self.register_resource(resource) + return resource + + def add_route( + self, + method: str, + path: str, + handler: Union[Handler, Type[AbstractView]], + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + ) -> AbstractRoute: + resource = self.add_resource(path, name=name) + return resource.add_route(method, handler, expect_handler=expect_handler) + + def add_static( + self, + prefix: str, + path: PathLike, + *, + name: Optional[str] = None, + expect_handler: Optional[_ExpectHandler] = None, + chunk_size: int = 256 * 1024, + show_index: bool = False, + follow_symlinks: bool = False, + append_version: bool = False, + ) -> AbstractResource: + """Add static files view. + + prefix - url prefix + path - folder with files + + """ + assert prefix.startswith("/") + if prefix.endswith("/"): + prefix = prefix[:-1] + resource = StaticResource( + prefix, + path, + name=name, + expect_handler=expect_handler, + chunk_size=chunk_size, + show_index=show_index, + follow_symlinks=follow_symlinks, + append_version=append_version, + ) + self.register_resource(resource) + return resource + + def add_head(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method HEAD.""" + return self.add_route(hdrs.METH_HEAD, path, handler, **kwargs) + + def add_options(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method OPTIONS.""" + return self.add_route(hdrs.METH_OPTIONS, path, handler, **kwargs) + + def add_get( + self, + path: str, + handler: Handler, + *, + name: Optional[str] = None, + allow_head: bool = True, + **kwargs: Any, + ) -> AbstractRoute: + """Shortcut for add_route with method GET. + + If allow_head is true, another + route is added allowing head requests to the same endpoint. + """ + resource = self.add_resource(path, name=name) + if allow_head: + resource.add_route(hdrs.METH_HEAD, handler, **kwargs) + return resource.add_route(hdrs.METH_GET, handler, **kwargs) + + def add_post(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method POST.""" + return self.add_route(hdrs.METH_POST, path, handler, **kwargs) + + def add_put(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method PUT.""" + return self.add_route(hdrs.METH_PUT, path, handler, **kwargs) + + def add_patch(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method PATCH.""" + return self.add_route(hdrs.METH_PATCH, path, handler, **kwargs) + + def add_delete(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: + """Shortcut for add_route with method DELETE.""" + return self.add_route(hdrs.METH_DELETE, path, handler, **kwargs) + + def add_view( + self, path: str, handler: Type[AbstractView], **kwargs: Any + ) -> AbstractRoute: + """Shortcut for add_route with ANY methods for a class-based view.""" + return self.add_route(hdrs.METH_ANY, path, handler, **kwargs) + + def freeze(self) -> None: + super().freeze() + for resource in self._resources: + resource.freeze() + + def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + """Append routes to route table. + + Parameter should be a sequence of RouteDef objects. + + Returns a list of registered AbstractRoute instances. + """ + registered_routes = [] + for route_def in routes: + registered_routes.extend(route_def.register(self)) + return registered_routes + + +def _quote_path(value: str) -> str: + if YARL_VERSION < (1, 6): + value = value.replace("%", "%25") + return URL.build(path=value, encoded=False).raw_path + + +def _unquote_path(value: str) -> str: + return URL.build(path=value, encoded=True).path + + +def _requote_path(value: str) -> str: + # Quote non-ascii characters and other characters which must be quoted, + # but preserve existing %-sequences. + result = _quote_path(value) + if "%" in value: + result = result.replace("%25", "%") + return result diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_ws.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_ws.py new file mode 100644 index 0000000..9fe6652 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/web_ws.py @@ -0,0 +1,539 @@ +import asyncio +import base64 +import binascii +import hashlib +import json +import sys +from typing import Any, Final, Iterable, Optional, Tuple, cast + +import attr +from multidict import CIMultiDict + +from . import hdrs +from .abc import AbstractStreamWriter +from .helpers import call_later, set_exception, set_result +from .http import ( + WS_CLOSED_MESSAGE, + WS_CLOSING_MESSAGE, + WS_KEY, + WebSocketError, + WebSocketReader, + WebSocketWriter, + WSCloseCode, + WSMessage, + WSMsgType as WSMsgType, + ws_ext_gen, + ws_ext_parse, +) +from .log import ws_logger +from .streams import EofStream, FlowControlDataQueue +from .typedefs import JSONDecoder, JSONEncoder +from .web_exceptions import HTTPBadRequest, HTTPException +from .web_request import BaseRequest +from .web_response import StreamResponse + +if sys.version_info >= (3, 11): + import asyncio as async_timeout +else: + import async_timeout + +__all__ = ( + "WebSocketResponse", + "WebSocketReady", + "WSMsgType", +) + +THRESHOLD_CONNLOST_ACCESS: Final[int] = 5 + + +@attr.s(auto_attribs=True, frozen=True, slots=True) +class WebSocketReady: + ok: bool + protocol: Optional[str] + + def __bool__(self) -> bool: + return self.ok + + +class WebSocketResponse(StreamResponse): + + _length_check = False + + def __init__( + self, + *, + timeout: float = 10.0, + receive_timeout: Optional[float] = None, + autoclose: bool = True, + autoping: bool = True, + heartbeat: Optional[float] = None, + protocols: Iterable[str] = (), + compress: bool = True, + max_msg_size: int = 4 * 1024 * 1024, + ) -> None: + super().__init__(status=101) + self._protocols = protocols + self._ws_protocol: Optional[str] = None + self._writer: Optional[WebSocketWriter] = None + self._reader: Optional[FlowControlDataQueue[WSMessage]] = None + self._closed = False + self._closing = False + self._conn_lost = 0 + self._close_code: Optional[int] = None + self._loop: Optional[asyncio.AbstractEventLoop] = None + self._waiting: Optional[asyncio.Future[bool]] = None + self._exception: Optional[BaseException] = None + self._timeout = timeout + self._receive_timeout = receive_timeout + self._autoclose = autoclose + self._autoping = autoping + self._heartbeat = heartbeat + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None + if heartbeat is not None: + self._pong_heartbeat = heartbeat / 2.0 + self._pong_response_cb: Optional[asyncio.TimerHandle] = None + self._compress = compress + self._max_msg_size = max_msg_size + + def _cancel_heartbeat(self) -> None: + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = None + + if self._heartbeat_cb is not None: + self._heartbeat_cb.cancel() + self._heartbeat_cb = None + + def _reset_heartbeat(self) -> None: + self._cancel_heartbeat() + + if self._heartbeat is not None: + assert self._loop is not None + self._heartbeat_cb = call_later( + self._send_heartbeat, + self._heartbeat, + self._loop, + timeout_ceil_threshold=self._req._protocol._timeout_ceil_threshold + if self._req is not None + else 5, + ) + + def _send_heartbeat(self) -> None: + if self._heartbeat is not None and not self._closed: + assert self._loop is not None + # fire-and-forget a task is not perfect but maybe ok for + # sending ping. Otherwise we need a long-living heartbeat + # task in the class. + self._loop.create_task(self._writer.ping()) # type: ignore[union-attr] + + if self._pong_response_cb is not None: + self._pong_response_cb.cancel() + self._pong_response_cb = call_later( + self._pong_not_received, + self._pong_heartbeat, + self._loop, + timeout_ceil_threshold=self._req._protocol._timeout_ceil_threshold + if self._req is not None + else 5, + ) + + def _pong_not_received(self) -> None: + if self._req is not None and self._req.transport is not None: + self._closed = True + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + self._exception = asyncio.TimeoutError() + + async def prepare(self, request: BaseRequest) -> AbstractStreamWriter: + # make pre-check to don't hide it by do_handshake() exceptions + if self._payload_writer is not None: + return self._payload_writer + + protocol, writer = self._pre_start(request) + payload_writer = await super().prepare(request) + assert payload_writer is not None + self._post_start(request, protocol, writer) + await payload_writer.drain() + return payload_writer + + def _handshake( + self, request: BaseRequest + ) -> Tuple["CIMultiDict[str]", str, bool, bool]: + headers = request.headers + if "websocket" != headers.get(hdrs.UPGRADE, "").lower().strip(): + raise HTTPBadRequest( + text=( + "No WebSocket UPGRADE hdr: {}\n Can " + '"Upgrade" only to "WebSocket".' + ).format(headers.get(hdrs.UPGRADE)) + ) + + if "upgrade" not in headers.get(hdrs.CONNECTION, "").lower(): + raise HTTPBadRequest( + text="No CONNECTION upgrade hdr: {}".format( + headers.get(hdrs.CONNECTION) + ) + ) + + # find common sub-protocol between client and server + protocol = None + if hdrs.SEC_WEBSOCKET_PROTOCOL in headers: + req_protocols = [ + str(proto.strip()) + for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") + ] + + for proto in req_protocols: + if proto in self._protocols: + protocol = proto + break + else: + # No overlap found: Return no protocol as per spec + ws_logger.warning( + "Client protocols %r don’t overlap server-known ones %r", + req_protocols, + self._protocols, + ) + + # check supported version + version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, "") + if version not in ("13", "8", "7"): + raise HTTPBadRequest(text=f"Unsupported version: {version}") + + # check client handshake for validity + key = headers.get(hdrs.SEC_WEBSOCKET_KEY) + try: + if not key or len(base64.b64decode(key)) != 16: + raise HTTPBadRequest(text=f"Handshake error: {key!r}") + except binascii.Error: + raise HTTPBadRequest(text=f"Handshake error: {key!r}") from None + + accept_val = base64.b64encode( + hashlib.sha1(key.encode() + WS_KEY).digest() + ).decode() + response_headers = CIMultiDict( + { + hdrs.UPGRADE: "websocket", + hdrs.CONNECTION: "upgrade", + hdrs.SEC_WEBSOCKET_ACCEPT: accept_val, + } + ) + + notakeover = False + compress = 0 + if self._compress: + extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) + # Server side always get return with no exception. + # If something happened, just drop compress extension + compress, notakeover = ws_ext_parse(extensions, isserver=True) + if compress: + enabledext = ws_ext_gen( + compress=compress, isserver=True, server_notakeover=notakeover + ) + response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext + + if protocol: + response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol + return ( + response_headers, + protocol, + compress, + notakeover, + ) # type: ignore[return-value] + + def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]: + self._loop = request._loop + + headers, protocol, compress, notakeover = self._handshake(request) + + self.set_status(101) + self.headers.update(headers) + self.force_close() + self._compress = compress + transport = request._protocol.transport + assert transport is not None + writer = WebSocketWriter( + request._protocol, transport, compress=compress, notakeover=notakeover + ) + + return protocol, writer + + def _post_start( + self, request: BaseRequest, protocol: str, writer: WebSocketWriter + ) -> None: + self._ws_protocol = protocol + self._writer = writer + + self._reset_heartbeat() + + loop = self._loop + assert loop is not None + self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop) + request.protocol.set_parser( + WebSocketReader(self._reader, self._max_msg_size, compress=self._compress) + ) + # disable HTTP keepalive for WebSocket + request.protocol.keep_alive(False) + + def can_prepare(self, request: BaseRequest) -> WebSocketReady: + if self._writer is not None: + raise RuntimeError("Already started") + try: + _, protocol, _, _ = self._handshake(request) + except HTTPException: + return WebSocketReady(False, None) + else: + return WebSocketReady(True, protocol) + + @property + def closed(self) -> bool: + return self._closed + + @property + def close_code(self) -> Optional[int]: + return self._close_code + + @property + def ws_protocol(self) -> Optional[str]: + return self._ws_protocol + + @property + def compress(self) -> bool: + return self._compress + + def get_extra_info(self, name: str, default: Any = None) -> Any: + """Get optional transport information. + + If no value associated with ``name`` is found, ``default`` is returned. + """ + writer = self._writer + if writer is None: + return default + transport = writer.transport + if transport is None: + return default + return transport.get_extra_info(name, default) + + def exception(self) -> Optional[BaseException]: + return self._exception + + async def ping(self, message: bytes = b"") -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + await self._writer.ping(message) + + async def pong(self, message: bytes = b"") -> None: + # unsolicited pong + if self._writer is None: + raise RuntimeError("Call .prepare() first") + await self._writer.pong(message) + + async def send_str(self, data: str, compress: Optional[bool] = None) -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + if not isinstance(data, str): + raise TypeError("data argument must be str (%r)" % type(data)) + await self._writer.send(data, binary=False, compress=compress) + + async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None: + if self._writer is None: + raise RuntimeError("Call .prepare() first") + if not isinstance(data, (bytes, bytearray, memoryview)): + raise TypeError("data argument must be byte-ish (%r)" % type(data)) + await self._writer.send(data, binary=True, compress=compress) + + async def send_json( + self, + data: Any, + compress: Optional[bool] = None, + *, + dumps: JSONEncoder = json.dumps, + ) -> None: + await self.send_str(dumps(data), compress=compress) + + async def write_eof(self) -> None: # type: ignore[override] + if self._eof_sent: + return + if self._payload_writer is None: + raise RuntimeError("Response has not been started") + + await self.close() + self._eof_sent = True + + async def close( + self, *, code: int = WSCloseCode.OK, message: bytes = b"", drain: bool = True + ) -> bool: + """Close websocket connection.""" + if self._writer is None: + raise RuntimeError("Call .prepare() first") + + self._cancel_heartbeat() + reader = self._reader + assert reader is not None + + # we need to break `receive()` cycle first, + # `close()` may be called from different task + if self._waiting is not None and not self._closed: + reader.feed_data(WS_CLOSING_MESSAGE, 0) + await self._waiting + + if self._closed: + return False + + self._closed = True + try: + await self._writer.close(code, message) + writer = self._payload_writer + assert writer is not None + if drain: + await writer.drain() + except (asyncio.CancelledError, asyncio.TimeoutError): + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + raise + except Exception as exc: + self._exception = exc + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + return True + + if self._closing: + self._close_transport() + return True + + reader = self._reader + assert reader is not None + try: + async with async_timeout.timeout(self._timeout): + msg = await reader.read() + except asyncio.CancelledError: + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + raise + except Exception as exc: + self._exception = exc + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + return True + + if msg.type == WSMsgType.CLOSE: + self._set_code_close_transport(msg.data) + return True + + self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE) + self._exception = asyncio.TimeoutError() + return True + + def _set_closing(self, code: WSCloseCode) -> None: + """Set the close code and mark the connection as closing.""" + self._closing = True + self._close_code = code + + def _set_code_close_transport(self, code: WSCloseCode) -> None: + """Set the close code and close the transport.""" + self._close_code = code + self._close_transport() + + def _close_transport(self) -> None: + """Close the transport.""" + if self._req is not None and self._req.transport is not None: + self._req.transport.close() + + async def receive(self, timeout: Optional[float] = None) -> WSMessage: + if self._reader is None: + raise RuntimeError("Call .prepare() first") + + loop = self._loop + assert loop is not None + while True: + if self._waiting is not None: + raise RuntimeError("Concurrent call to receive() is not allowed") + + if self._closed: + self._conn_lost += 1 + if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS: + raise RuntimeError("WebSocket connection is closed.") + return WS_CLOSED_MESSAGE + elif self._closing: + return WS_CLOSING_MESSAGE + + try: + self._waiting = loop.create_future() + try: + async with async_timeout.timeout(timeout or self._receive_timeout): + msg = await self._reader.read() + self._reset_heartbeat() + finally: + waiter = self._waiting + set_result(waiter, True) + self._waiting = None + except asyncio.TimeoutError: + raise + except EofStream: + self._close_code = WSCloseCode.OK + await self.close() + return WSMessage(WSMsgType.CLOSED, None, None) + except WebSocketError as exc: + self._close_code = exc.code + await self.close(code=exc.code) + return WSMessage(WSMsgType.ERROR, exc, None) + except Exception as exc: + self._exception = exc + self._set_closing(WSCloseCode.ABNORMAL_CLOSURE) + await self.close() + return WSMessage(WSMsgType.ERROR, exc, None) + + if msg.type == WSMsgType.CLOSE: + self._set_closing(msg.data) + # Could be closed while awaiting reader. + if not self._closed and self._autoclose: + # The client is likely going to close the + # connection out from under us so we do not + # want to drain any pending writes as it will + # likely result writing to a broken pipe. + await self.close(drain=False) + elif msg.type == WSMsgType.CLOSING: + self._set_closing(WSCloseCode.OK) + elif msg.type == WSMsgType.PING and self._autoping: + await self.pong(msg.data) + continue + elif msg.type == WSMsgType.PONG and self._autoping: + continue + + return msg + + async def receive_str(self, *, timeout: Optional[float] = None) -> str: + msg = await self.receive(timeout) + if msg.type != WSMsgType.TEXT: + raise TypeError( + "Received message {}:{!r} is not WSMsgType.TEXT".format( + msg.type, msg.data + ) + ) + return cast(str, msg.data) + + async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: + msg = await self.receive(timeout) + if msg.type != WSMsgType.BINARY: + raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") + return cast(bytes, msg.data) + + async def receive_json( + self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None + ) -> Any: + data = await self.receive_str(timeout=timeout) + return loads(data) + + async def write(self, data: bytes) -> None: + raise RuntimeError("Cannot call .write() for websocket") + + def __aiter__(self) -> "WebSocketResponse": + return self + + async def __anext__(self) -> WSMessage: + msg = await self.receive() + if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): + raise StopAsyncIteration + return msg + + def _cancel(self, exc: BaseException) -> None: + # web_protocol calls this from connection_lost + # or when the server is shutting down. + self._closing = True + if self._reader is not None: + set_exception(self._reader, exc) diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp/worker.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp/worker.py new file mode 100644 index 0000000..9b30769 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp/worker.py @@ -0,0 +1,247 @@ +"""Async gunicorn worker for aiohttp.web""" + +import asyncio +import os +import re +import signal +import sys +from types import FrameType +from typing import Any, Awaitable, Callable, Optional, Union # noqa + +from gunicorn.config import AccessLogFormat as GunicornAccessLogFormat +from gunicorn.workers import base + +from aiohttp import web + +from .helpers import set_result +from .web_app import Application +from .web_log import AccessLogger + +try: + import ssl + + SSLContext = ssl.SSLContext +except ImportError: # pragma: no cover + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] + + +__all__ = ("GunicornWebWorker", "GunicornUVLoopWebWorker") + + +class GunicornWebWorker(base.Worker): # type: ignore[misc,no-any-unimported] + + DEFAULT_AIOHTTP_LOG_FORMAT = AccessLogger.LOG_FORMAT + DEFAULT_GUNICORN_LOG_FORMAT = GunicornAccessLogFormat.default + + def __init__(self, *args: Any, **kw: Any) -> None: # pragma: no cover + super().__init__(*args, **kw) + + self._task: Optional[asyncio.Task[None]] = None + self.exit_code = 0 + self._notify_waiter: Optional[asyncio.Future[bool]] = None + + def init_process(self) -> None: + # create new event_loop after fork + asyncio.get_event_loop().close() + + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.loop) + + super().init_process() + + def run(self) -> None: + self._task = self.loop.create_task(self._run()) + + try: # ignore all finalization problems + self.loop.run_until_complete(self._task) + except Exception: + self.log.exception("Exception in gunicorn worker") + self.loop.run_until_complete(self.loop.shutdown_asyncgens()) + self.loop.close() + + sys.exit(self.exit_code) + + async def _run(self) -> None: + runner = None + if isinstance(self.wsgi, Application): + app = self.wsgi + elif asyncio.iscoroutinefunction(self.wsgi): + wsgi = await self.wsgi() + if isinstance(wsgi, web.AppRunner): + runner = wsgi + app = runner.app + else: + app = wsgi + else: + raise RuntimeError( + "wsgi app should be either Application or " + "async function returning Application, got {}".format(self.wsgi) + ) + + if runner is None: + access_log = self.log.access_log if self.cfg.accesslog else None + runner = web.AppRunner( + app, + logger=self.log, + keepalive_timeout=self.cfg.keepalive, + access_log=access_log, + access_log_format=self._get_valid_log_format( + self.cfg.access_log_format + ), + shutdown_timeout=self.cfg.graceful_timeout / 100 * 95, + ) + await runner.setup() + + ctx = self._create_ssl_context(self.cfg) if self.cfg.is_ssl else None + + runner = runner + assert runner is not None + server = runner.server + assert server is not None + for sock in self.sockets: + site = web.SockSite( + runner, + sock, + ssl_context=ctx, + ) + await site.start() + + # If our parent changed then we shut down. + pid = os.getpid() + try: + while self.alive: # type: ignore[has-type] + self.notify() + + cnt = server.requests_count + if self.max_requests and cnt > self.max_requests: + self.alive = False + self.log.info("Max requests, shutting down: %s", self) + + elif pid == os.getpid() and self.ppid != os.getppid(): + self.alive = False + self.log.info("Parent changed, shutting down: %s", self) + else: + await self._wait_next_notify() + except BaseException: + pass + + await runner.cleanup() + + def _wait_next_notify(self) -> "asyncio.Future[bool]": + self._notify_waiter_done() + + loop = self.loop + assert loop is not None + self._notify_waiter = waiter = loop.create_future() + self.loop.call_later(1.0, self._notify_waiter_done, waiter) + + return waiter + + def _notify_waiter_done( + self, waiter: Optional["asyncio.Future[bool]"] = None + ) -> None: + if waiter is None: + waiter = self._notify_waiter + if waiter is not None: + set_result(waiter, True) + + if waiter is self._notify_waiter: + self._notify_waiter = None + + def init_signals(self) -> None: + # Set up signals through the event loop API. + + self.loop.add_signal_handler( + signal.SIGQUIT, self.handle_quit, signal.SIGQUIT, None + ) + + self.loop.add_signal_handler( + signal.SIGTERM, self.handle_exit, signal.SIGTERM, None + ) + + self.loop.add_signal_handler( + signal.SIGINT, self.handle_quit, signal.SIGINT, None + ) + + self.loop.add_signal_handler( + signal.SIGWINCH, self.handle_winch, signal.SIGWINCH, None + ) + + self.loop.add_signal_handler( + signal.SIGUSR1, self.handle_usr1, signal.SIGUSR1, None + ) + + self.loop.add_signal_handler( + signal.SIGABRT, self.handle_abort, signal.SIGABRT, None + ) + + # Don't let SIGTERM and SIGUSR1 disturb active requests + # by interrupting system calls + signal.siginterrupt(signal.SIGTERM, False) + signal.siginterrupt(signal.SIGUSR1, False) + # Reset signals so Gunicorn doesn't swallow subprocess return codes + # See: https://github.com/aio-libs/aiohttp/issues/6130 + + def handle_quit(self, sig: int, frame: Optional[FrameType]) -> None: + self.alive = False + + # worker_int callback + self.cfg.worker_int(self) + + # wakeup closing process + self._notify_waiter_done() + + def handle_abort(self, sig: int, frame: Optional[FrameType]) -> None: + self.alive = False + self.exit_code = 1 + self.cfg.worker_abort(self) + sys.exit(1) + + @staticmethod + def _create_ssl_context(cfg: Any) -> "SSLContext": + """Creates SSLContext instance for usage in asyncio.create_server. + + See ssl.SSLSocket.__init__ for more details. + """ + if ssl is None: # pragma: no cover + raise RuntimeError("SSL is not supported.") + + ctx = ssl.SSLContext(cfg.ssl_version) + ctx.load_cert_chain(cfg.certfile, cfg.keyfile) + ctx.verify_mode = cfg.cert_reqs + if cfg.ca_certs: + ctx.load_verify_locations(cfg.ca_certs) + if cfg.ciphers: + ctx.set_ciphers(cfg.ciphers) + return ctx + + def _get_valid_log_format(self, source_format: str) -> str: + if source_format == self.DEFAULT_GUNICORN_LOG_FORMAT: + return self.DEFAULT_AIOHTTP_LOG_FORMAT + elif re.search(r"%\([^\)]+\)", source_format): + raise ValueError( + "Gunicorn's style options in form of `%(name)s` are not " + "supported for the log formatting. Please use aiohttp's " + "format specification to configure access log formatting: " + "http://docs.aiohttp.org/en/stable/logging.html" + "#format-specification" + ) + else: + return source_format + + +class GunicornUVLoopWebWorker(GunicornWebWorker): + def init_process(self) -> None: + import uvloop + + # Close any existing event loop before setting a + # new policy. + asyncio.get_event_loop().close() + + # Setup uvloop policy, so that every + # asyncio.get_event_loop() will create an instance + # of uvloop event loop. + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + + super().init_process() diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/AUTHORS b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/AUTHORS new file mode 100644 index 0000000..4ba0953 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/AUTHORS @@ -0,0 +1 @@ +Dmitry Inyutin diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/LICENSE new file mode 100644 index 0000000..175ff4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2020 aiohttp_retry Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/METADATA new file mode 100644 index 0000000..4e1f286 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/METADATA @@ -0,0 +1,262 @@ +Metadata-Version: 2.1 +Name: aiohttp-retry +Version: 2.8.3 +Summary: Simple retry client for aiohttp +Home-page: https://github.com/inyutin/aiohttp_retry +Author: Dmitry Inyutin +Author-email: inyutin.da@gmail.com +License: MIT +Keywords: aiohttp retry client +Platform: any +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: aiohttp + +# Simple aiohttp retry client + +[![codecov](https://codecov.io/gh/inyutin/aiohttp_retry/branch/master/graph/badge.svg?token=ZWGAXSF1SP)](https://codecov.io/gh/inyutin/aiohttp_retry) + +Python 3.7 or higher. + +**Install**: `pip install aiohttp-retry`. + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/inyutin) + + +### Breaking API changes +- Everything between [2.7.0 - 2.8.3) is yanked. +There is a bug with evaluate_response_callback, it led to infinite retries + +- 2.8.0 is incorrect and yanked. +https://github.com/inyutin/aiohttp_retry/issues/79 + +- Since 2.5.6 this is a new parameter in ```get_timeout``` func called "response". +If you have defined your own ```RetryOptions```, you should add this param into it. +Issue about this: https://github.com/inyutin/aiohttp_retry/issues/59 + +### Examples of usage: +```python +from aiohttp_retry import RetryClient, ExponentialRetry + +async def main(): + retry_options = ExponentialRetry(attempts=1) + retry_client = RetryClient(raise_for_status=False, retry_options=retry_options) + async with retry_client.get('https://ya.ru') as response: + print(response.status) + + await retry_client.close() +``` + +```python +from aiohttp import ClientSession +from aiohttp_retry import RetryClient + +async def main(): + client_session = ClientSession() + retry_client = RetryClient(client_session=client_session) + async with retry_client.get('https://ya.ru') as response: + print(response.status) + + await client_session.close() +``` + +```python +from aiohttp_retry import RetryClient, RandomRetry + +async def main(): + retry_options = RandomRetry(attempts=1) + retry_client = RetryClient(raise_for_status=False, retry_options=retry_options) + + response = await retry_client.get('/ping') + print(response.status) + + await retry_client.close() +``` + +```python +from aiohttp_retry import RetryClient + +async def main(): + async with RetryClient() as client: + async with client.get('https://ya.ru') as response: + print(response.status) +``` + +You can change parameters between attempts by passing multiple requests params: +```python +from aiohttp_retry import RetryClient, RequestParams, ExponentialRetry + +async def main(): + retry_client = RetryClient(raise_for_status=False) + + async with retry_client.requests( + params_list=[ + RequestParams( + method='GET', + url='https://ya.ru', + ), + RequestParams( + method='GET', + url='https://ya.ru', + headers={'some_header': 'some_value'}, + ), + ] + ) as response: + print(response.status) + + await retry_client.close() +``` + +You can also add some logic, F.E. logging, on failures by using trace mechanic. +```python +import logging +import sys +from types import SimpleNamespace + +from aiohttp import ClientSession, TraceConfig, TraceRequestStartParams + +from aiohttp_retry import RetryClient, ExponentialRetry + + +handler = logging.StreamHandler(sys.stdout) +logging.basicConfig(handlers=[handler]) +logger = logging.getLogger(__name__) +retry_options = ExponentialRetry(attempts=2) + + +async def on_request_start( + session: ClientSession, + trace_config_ctx: SimpleNamespace, + params: TraceRequestStartParams, +) -> None: + current_attempt = trace_config_ctx.trace_request_ctx['current_attempt'] + if retry_options.attempts <= current_attempt: + logger.warning('Wow! We are in last attempt') + + +async def main(): + trace_config = TraceConfig() + trace_config.on_request_start.append(on_request_start) + retry_client = RetryClient(retry_options=retry_options, trace_configs=[trace_config]) + + response = await retry_client.get('https://httpstat.us/503', ssl=False) + print(response.status) + + await retry_client.close() +``` +Look tests for more examples. \ +**Be aware: last request returns as it is.** +**If the last request ended with exception, that this exception will be raised from RetryClient request** + +### Documentation +`RetryClient` takes the same arguments as ClientSession[[docs](https://docs.aiohttp.org/en/stable/client_reference.html)] \ +`RetryClient` has methods: +- request +- get +- options +- head +- post +- put +- patch +- put +- delete + +They are same as for `ClientSession`, but take one possible additional argument: +```python +class RetryOptionsBase: + def __init__( + self, + attempts: int = 3, # How many times we should retry + statuses: Optional[Iterable[int]] = None, # On which statuses we should retry + exceptions: Optional[Iterable[Type[Exception]]] = None, # On which exceptions we should retry + retry_all_server_errors: bool = True, # If should retry all 500 errors or not + # a callback that will run on response to decide if retry + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + ... + + @abc.abstractmethod + def get_timeout(self, attempt: int, response: Optional[Response] = None) -> float: + raise NotImplementedError + +``` +You can specify `RetryOptions` both for `RetryClient` and it's methods. +`RetryOptions` in methods override `RetryOptions` defined in `RetryClient` constructor. + +**Important**: by default all 5xx responses are retried + statuses you specified as ```statuses``` param +If you will pass ```retry_all_server_errors=False``` than you can manually set what 5xx errors to retry. + +You can define your own timeouts logic or use: +- ```ExponentialRetry``` with exponential backoff +- ```RandomRetry``` for random backoff +- ```ListRetry``` with backoff you predefine by list +- ```FibonacciRetry``` with backoff that looks like fibonacci sequence +- ```JitterRetry``` exponential retry with a bit of randomness + +**Important**: you can proceed server response as an parameter for calculating next timeout. +However this response can be None, server didn't make a response or you have set up ```raise_for_status=True``` +Look here for an example: https://github.com/inyutin/aiohttp_retry/issues/59 + +Additionally, you can specify ```evaluate_response_callback```. It receive a ```ClientResponse``` and decide to retry or not by returning a bool. +It can be useful, if server API sometimes response with malformed data. + +#### Request Trace Context +`RetryClient` add *current attempt number* to `request_trace_ctx` (see examples, +for more info see [aiohttp doc](https://docs.aiohttp.org/en/stable/client_advanced.html#aiohttp-client-tracing)). + +### Change parameters between retries +`RetryClient` also has a method called `requests`. This method should be used if you want to make requests with different params. +```python +@dataclass +class RequestParams: + method: str + url: _RAW_URL_TYPE + trace_request_ctx: Optional[Dict[str, Any]] = None + kwargs: Optional[Dict[str, Any]] = None +``` + +```python +def requests( + self, + params_list: List[RequestParams], + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, +) -> _RequestContext: +``` + +You can find an example of usage above or in tests. +But basically `RequestParams` is a structure to define params for `ClientSession.request` func. +`method`, `url`, `headers` `trace_request_ctx` defined outside kwargs, because they are popular. + +There is also an old way to change URL between retries by specifying ```url``` as list of urls. Example: +```python +from aiohttp_retry import RetryClient + +retry_client = RetryClient() +async with retry_client.get(url=['/internal_error', '/ping']) as response: + text = await response.text() + assert response.status == 200 + assert text == 'Ok!' + +await retry_client.close() +``` + +In this example we request ```/interval_error```, fail and then successfully request ```/ping```. +If you specify less urls than ```attempts``` number in ```RetryOptions```, ```RetryClient``` will request last url at last attempts. +This means that in example above we would request ```/ping``` once again in case of failure. + +### Types + +`aiohttp_retry` is a typed project. It should be fully compatablie with mypy. + +It also introduce one special type: +``` +ClientType = Union[ClientSession, RetryClient] +``` + +This type can be imported by ```from aiohttp_retry.types import ClientType``` diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/RECORD new file mode 100644 index 0000000..2d038e2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/RECORD @@ -0,0 +1,16 @@ +aiohttp_retry-2.8.3.dist-info/AUTHORS,sha256=GlJRXBnogod3eAzFrL3YBj8lrNy1PVoeaKCnNm2zN5M,38 +aiohttp_retry-2.8.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiohttp_retry-2.8.3.dist-info/LICENSE,sha256=rKYSbPXF6EyrQeo7xh9TV4YWMWqD6-EwzeDUt2boof4,1087 +aiohttp_retry-2.8.3.dist-info/METADATA,sha256=YRgGNYLp9f6d75or7k63LGvLgPy7nxpo_HVTF_9mcy4,8862 +aiohttp_retry-2.8.3.dist-info/RECORD,, +aiohttp_retry-2.8.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +aiohttp_retry-2.8.3.dist-info/top_level.txt,sha256=b-4ybD7H7PD7sNUDod6pes6gfHuEde1xETO4fHqAMog,14 +aiohttp_retry/__init__.py,sha256=MHo-L_5Ygcl0r_9hP76cdhr0rOYq77UxP5SLNSoB8q0,91 +aiohttp_retry/__pycache__/__init__.cpython-310.pyc,, +aiohttp_retry/__pycache__/client.cpython-310.pyc,, +aiohttp_retry/__pycache__/retry_options.cpython-310.pyc,, +aiohttp_retry/__pycache__/types.cpython-310.pyc,, +aiohttp_retry/client.py,sha256=oE-7TR1IOKlqrIIRlom5ayh5vGjmCee1IenUWqWkVXY,12463 +aiohttp_retry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +aiohttp_retry/retry_options.py,sha256=IPb_2006qLZjiDqvmOOT_hzRImUrjCb2vy_yy_-VHAs,7893 +aiohttp_retry/types.py,sha256=lO3nb__G8p1jWBcD9ptKh_YHgEy6pPOH_HCNM69BH8s,141 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/top_level.txt new file mode 100644 index 0000000..ea6602f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry-2.8.3.dist-info/top_level.txt @@ -0,0 +1 @@ +aiohttp_retry diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__init__.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__init__.py new file mode 100644 index 0000000..fd91ba3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__init__.py @@ -0,0 +1,2 @@ +from .client import * # noqa: F401, F403 +from .retry_options import * # noqa: F401, F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..51aa485 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000..aaf3656 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/retry_options.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/retry_options.cpython-310.pyc new file mode 100644 index 0000000..5c097a6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/retry_options.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000..fc4d1b5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/__pycache__/types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/client.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/client.py new file mode 100644 index 0000000..c156cb7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/client.py @@ -0,0 +1,401 @@ +import asyncio +import logging +import sys +from abc import abstractmethod +from dataclasses import dataclass +from types import TracebackType +from typing import ( + Any, + Awaitable, + Callable, + Dict, + Generator, + List, + Optional, + Tuple, + Type, + Union, +) + +from aiohttp import ClientResponse, ClientSession, hdrs +from aiohttp.typedefs import StrOrURL +from yarl import URL as YARL_URL + +from .retry_options import ExponentialRetry, RetryOptionsBase + +if sys.version_info >= (3, 8): + from typing import Protocol +else: + from typing_extensions import Protocol + + +class _Logger(Protocol): + """ + _Logger defines which methods logger object should have + """ + + @abstractmethod + def debug(self, msg: str, *args: Any, **kwargs: Any) -> None: pass + + @abstractmethod + def warning(self, msg: str, *args: Any, **kwargs: Any) -> None: pass + + @abstractmethod + def exception(self, msg: str, *args: Any, **kwargs: Any) -> None: pass + + +# url itself or list of urls for changing between retries +_RAW_URL_TYPE = Union[StrOrURL, YARL_URL] +_URL_TYPE = Union[_RAW_URL_TYPE, List[_RAW_URL_TYPE], Tuple[_RAW_URL_TYPE, ...]] +_LoggerType = Union[_Logger, logging.Logger] + +RequestFunc = Callable[..., Awaitable[ClientResponse]] + + +@dataclass +class RequestParams: + method: str + url: _RAW_URL_TYPE + headers: Optional[Dict[str, Any]] = None + trace_request_ctx: Optional[Dict[str, Any]] = None + kwargs: Optional[Dict[str, Any]] = None + + +class _RequestContext: + def __init__( + self, + request_func: RequestFunc, + params_list: List[RequestParams], + logger: _LoggerType, + retry_options: RetryOptionsBase, + raise_for_status: bool = False, + ) -> None: + assert len(params_list) > 0 + + self._request_func = request_func + self._params_list = params_list + self._logger = logger + self._retry_options = retry_options + self._raise_for_status = raise_for_status + + self._response: Optional[ClientResponse] = None + + def _is_status_code_ok(self, code: int) -> bool: + if code >= 500 and self._retry_options.retry_all_server_errors: + return False + return code not in self._retry_options.statuses + + async def _do_request(self) -> ClientResponse: + current_attempt = 0 + while True: + self._logger.debug(f"Attempt {current_attempt+1} out of {self._retry_options.attempts}") + + current_attempt += 1 + try: + try: + params = self._params_list[current_attempt - 1] + except IndexError: + params = self._params_list[-1] + + response: ClientResponse = await self._request_func( + params.method, + params.url, + headers=params.headers, + trace_request_ctx={ + 'current_attempt': current_attempt, + **(params.trace_request_ctx or {}), + }, + **(params.kwargs or {}), + ) + + if self._is_status_code_ok(response.status) or current_attempt == self._retry_options.attempts: + if self._raise_for_status: + response.raise_for_status() + + if self._retry_options.evaluate_response_callback is not None: + try: + is_response_correct = await self._retry_options.evaluate_response_callback(response) + except Exception: + self._logger.exception('while evaluating response an exception occurred') + is_response_correct = False + else: + is_response_correct = True + + if is_response_correct or current_attempt == self._retry_options.attempts: + self._response = response + return response + else: + self._logger.debug(f"Retrying after evaluate response callback check") + else: + self._logger.debug(f"Retrying after response code: {response.status}") + retry_wait = self._retry_options.get_timeout(attempt=current_attempt, response=response) + except Exception as e: + if current_attempt >= self._retry_options.attempts: + raise e + + is_exc_valid = any([isinstance(e, exc) for exc in self._retry_options.exceptions]) + if not is_exc_valid: + raise e + + self._logger.debug(f"Retrying after exception: {repr(e)}") + retry_wait = self._retry_options.get_timeout(attempt=current_attempt, response=None) + + await asyncio.sleep(retry_wait) + + def __await__(self) -> Generator[Any, None, ClientResponse]: + return self.__aenter__().__await__() + + async def __aenter__(self) -> ClientResponse: + return await self._do_request() + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + if self._response is not None: + if not self._response.closed: + self._response.close() + + +def _url_to_urls(url: _URL_TYPE) -> Tuple[StrOrURL, ...]: + if isinstance(url, str) or isinstance(url, YARL_URL): + return (url,) + + if isinstance(url, list): + urls = tuple(url) + elif isinstance(url, tuple): + urls = url + else: + raise ValueError("you can pass url only by str or list/tuple") + + if len(urls) == 0: + raise ValueError("you can pass url by str or list/tuple with attempts count size") + + return urls + + +class RetryClient: + def __init__( + self, + client_session: Optional[ClientSession] = None, + logger: Optional[_LoggerType] = None, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: bool = False, + *args: Any, + **kwargs: Any, + ) -> None: + if client_session is not None: + client = client_session + closed = None + else: + client = ClientSession(*args, **kwargs) + closed = False + + self._client = client + self._closed = closed + + self._logger: _LoggerType = logger or logging.getLogger("aiohttp_retry") + self._retry_options: RetryOptionsBase = retry_options or ExponentialRetry() + self._raise_for_status = raise_for_status + + @property + def retry_options(self) -> RetryOptionsBase: + return self._retry_options + + def requests( + self, + params_list: List[RequestParams], + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + ) -> _RequestContext: + return self._make_requests( + params_list=params_list, + retry_options=retry_options, + raise_for_status=raise_for_status, + ) + + def request( + self, + method: str, + url: StrOrURL, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=method, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def get( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_GET, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def options( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_OPTIONS, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def head( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_HEAD, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def post( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_POST, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def put( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_PUT, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def patch( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_PATCH, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + def delete( + self, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + return self._make_request( + method=hdrs.METH_DELETE, + url=url, + retry_options=retry_options, + raise_for_status=raise_for_status, + **kwargs, + ) + + async def close(self) -> None: + await self._client.close() + self._closed = True + + def _make_request( + self, + method: str, + url: _URL_TYPE, + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + **kwargs: Any, + ) -> _RequestContext: + url_list = _url_to_urls(url) + params_list = [RequestParams( + method=method, + url=url, + headers=kwargs.pop('headers', {}), + trace_request_ctx=kwargs.pop('trace_request_ctx', None), + kwargs=kwargs, + ) for url in url_list] + + return self._make_requests( + params_list=params_list, + retry_options=retry_options, + raise_for_status=raise_for_status, + ) + + def _make_requests( + self, + params_list: List[RequestParams], + retry_options: Optional[RetryOptionsBase] = None, + raise_for_status: Optional[bool] = None, + ) -> _RequestContext: + if retry_options is None: + retry_options = self._retry_options + if raise_for_status is None: + raise_for_status = self._raise_for_status + return _RequestContext( + request_func=self._client.request, + params_list=params_list, + logger=self._logger, + retry_options=retry_options, + raise_for_status=raise_for_status, + ) + + async def __aenter__(self) -> 'RetryClient': + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() + + def __del__(self) -> None: + if getattr(self, '_closed', None) is None: + # in case object was not initialized (__init__ raised an exception) + return + + if not self._closed: + self._logger.warning("Aiohttp retry client was not closed") diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/py.typed b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/retry_options.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/retry_options.py new file mode 100644 index 0000000..83ca6d9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/retry_options.py @@ -0,0 +1,191 @@ +import abc +import random +from typing import Any, Awaitable, Callable, Iterable, List, Optional, Set, Type +from warnings import warn + +from aiohttp import ClientResponse + +EvaluateResponseCallbackType = Callable[[ClientResponse], Awaitable[bool]] + + +class RetryOptionsBase: + def __init__( + self, + attempts: int = 3, # How many times we should retry + statuses: Optional[Iterable[int]] = None, # On which statuses we should retry + exceptions: Optional[Iterable[Type[Exception]]] = None, # On which exceptions we should retry + retry_all_server_errors: bool = True, # If should retry all 500 errors or not + # a callback that will run on response to decide if retry + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + self.attempts: int = attempts + if statuses is None: + statuses = set() + self.statuses: Iterable[int] = statuses + + if exceptions is None: + exceptions = set() + self.exceptions: Iterable[Type[Exception]] = exceptions + + self.retry_all_server_errors = retry_all_server_errors + self.evaluate_response_callback = evaluate_response_callback + + @abc.abstractmethod + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + raise NotImplementedError + + +class ExponentialRetry(RetryOptionsBase): + def __init__( + self, + attempts: int = 3, # How many times we should retry + start_timeout: float = 0.1, # Base timeout time, then it exponentially grow + max_timeout: float = 30.0, # Max possible timeout between tries + factor: float = 2.0, # How much we increase timeout each time + statuses: Optional[Set[int]] = None, # On which statuses we should retry + exceptions: Optional[Set[Type[Exception]]] = None, # On which exceptions we should retry + retry_all_server_errors: bool = True, + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + super().__init__( + attempts=attempts, + statuses=statuses, + exceptions=exceptions, + retry_all_server_errors=retry_all_server_errors, + evaluate_response_callback=evaluate_response_callback, + ) + + self._start_timeout: float = start_timeout + self._max_timeout: float = max_timeout + self._factor: float = factor + + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + """Return timeout with exponential backoff.""" + timeout = self._start_timeout * (self._factor ** attempt) + return min(timeout, self._max_timeout) + + +def RetryOptions(*args: Any, **kwargs: Any) -> ExponentialRetry: + warn("RetryOptions is deprecated, use ExponentialRetry") + return ExponentialRetry(*args, **kwargs) + + +class RandomRetry(RetryOptionsBase): + def __init__( + self, + attempts: int = 3, # How many times we should retry + statuses: Optional[Iterable[int]] = None, # On which statuses we should retry + exceptions: Optional[Iterable[Type[Exception]]] = None, # On which exceptions we should retry + min_timeout: float = 0.1, # Minimum possible timeout + max_timeout: float = 3.0, # Maximum possible timeout between tries + random_func: Callable[[], float] = random.random, # Random number generator + retry_all_server_errors: bool = True, + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + super().__init__( + attempts=attempts, + statuses=statuses, + exceptions=exceptions, + retry_all_server_errors=retry_all_server_errors, + evaluate_response_callback=evaluate_response_callback, + ) + + self.attempts: int = attempts + self.min_timeout: float = min_timeout + self.max_timeout: float = max_timeout + self.random = random_func + + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + """Generate random timeouts.""" + return self.min_timeout + self.random() * (self.max_timeout - self.min_timeout) + + +class ListRetry(RetryOptionsBase): + def __init__( + self, + timeouts: List[float], + statuses: Optional[Iterable[int]] = None, # On which statuses we should retry + exceptions: Optional[Iterable[Type[Exception]]] = None, # On which exceptions we should retry + retry_all_server_errors: bool = True, + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + super().__init__( + attempts=len(timeouts), + statuses=statuses, + exceptions=exceptions, + retry_all_server_errors=retry_all_server_errors, + evaluate_response_callback=evaluate_response_callback, + ) + self.timeouts = timeouts + + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + """timeouts from a defined list.""" + return self.timeouts[attempt] + + +class FibonacciRetry(RetryOptionsBase): + def __init__( + self, + attempts: int = 3, + multiplier: float = 1.0, + statuses: Optional[Iterable[int]] = None, + exceptions: Optional[Iterable[Type[Exception]]] = None, + max_timeout: float = 3.0, # Maximum possible timeout between tries + retry_all_server_errors: bool = True, + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + super().__init__( + attempts=attempts, + statuses=statuses, + exceptions=exceptions, + retry_all_server_errors=retry_all_server_errors, + evaluate_response_callback=evaluate_response_callback, + ) + + self.max_timeout = max_timeout + self.multiplier = multiplier + self.prev_step = 1.0 + self.current_step = 1.0 + + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + new_current_step = self.prev_step + self.current_step + self.prev_step = self.current_step + self.current_step = new_current_step + + return min(self.multiplier * new_current_step, self.max_timeout) + + +class JitterRetry(ExponentialRetry): + """https://github.com/inyutin/aiohttp_retry/issues/44""" + + def __init__( + self, + attempts: int = 3, # How many times we should retry + start_timeout: float = 0.1, # Base timeout time, then it exponentially grow + max_timeout: float = 30.0, # Max possible timeout between tries + factor: float = 2.0, # How much we increase timeout each time + statuses: Optional[Set[int]] = None, # On which statuses we should retry + exceptions: Optional[Set[Type[Exception]]] = None, # On which exceptions we should retry + random_interval_size: float = 2.0, # size of interval for random component + retry_all_server_errors: bool = True, + evaluate_response_callback: Optional[EvaluateResponseCallbackType] = None, + ): + super().__init__( + attempts=attempts, + start_timeout=start_timeout, + max_timeout=max_timeout, + factor=factor, + statuses=statuses, + exceptions=exceptions, + retry_all_server_errors=retry_all_server_errors, + evaluate_response_callback=evaluate_response_callback, + ) + + self._start_timeout: float = start_timeout + self._max_timeout: float = max_timeout + self._factor: float = factor + self._random_interval_size = random_interval_size + + def get_timeout(self, attempt: int, response: Optional[ClientResponse] = None) -> float: + timeout: float = super().get_timeout(attempt) + random.uniform(0, self._random_interval_size) ** self._factor + return timeout diff --git a/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/types.py b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/types.py new file mode 100644 index 0000000..b14af2a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiohttp_retry/types.py @@ -0,0 +1,7 @@ +from typing import Union + +from aiohttp import ClientSession + +from .client import RetryClient + +ClientType = Union[ClientSession, RetryClient] diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/LICENSE new file mode 100644 index 0000000..7082a2d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013-2019 Nikolay Kim and Andrew Svetlov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/METADATA new file mode 100644 index 0000000..fc96452 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/METADATA @@ -0,0 +1,128 @@ +Metadata-Version: 2.1 +Name: aiosignal +Version: 1.3.1 +Summary: aiosignal: a list of registered asynchronous callbacks +Home-page: https://github.com/aio-libs/aiosignal +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2.0 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiosignal/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiosignal +Project-URL: Docs: RTD, https://docs.aiosignal.org +Project-URL: GitHub: issues, https://github.com/aio-libs/aiosignal/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/aiosignal +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Framework :: AsyncIO +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: frozenlist (>=1.1.0) + +========= +aiosignal +========= + +.. image:: https://github.com/aio-libs/aiosignal/workflows/CI/badge.svg + :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/aiosignal + :alt: codecov.io status for master branch + +.. image:: https://badge.fury.io/py/aiosignal.svg + :target: https://pypi.org/project/aiosignal + :alt: Latest PyPI package version + +.. image:: https://readthedocs.org/projects/aiosignal/badge/?version=latest + :target: https://aiosignal.readthedocs.io/ + :alt: Latest Read The Docs + +.. image:: https://img.shields.io/discourse/topics?server=https%3A%2F%2Faio-libs.discourse.group%2F + :target: https://aio-libs.discourse.group/ + :alt: Discourse group for io-libs + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +Introduction +============ + +A project to manage callbacks in `asyncio` projects. + +``Signal`` is a list of registered asynchronous callbacks. + +The signal's life-cycle has two stages: after creation its content +could be filled by using standard list operations: ``sig.append()`` +etc. + +After you call ``sig.freeze()`` the signal is *frozen*: adding, removing +and dropping callbacks is forbidden. + +The only available operation is calling the previously registered +callbacks by using ``await sig.send(data)``. + +For concrete usage examples see the `Signals + +section of the `Web Server Advanced +` chapter of the `aiohttp +documentation`_. + + +Installation +------------ + +:: + + $ pip install aiosignal + +The library requires Python 3.6 or newer. + + +Documentation +============= + +https://aiosignal.readthedocs.io/ + +Communication channels +====================== + +*gitter chat* https://gitter.im/aio-libs/Lobby + +Requirements +============ + +- Python >= 3.6 +- frozenlist >= 1.0.0 + +License +======= + +``aiosignal`` is offered under the Apache 2 license. + +Source code +=========== + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/aiosignal +.. _aiohttp documentation: https://docs.aiohttp.org/ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/RECORD new file mode 100644 index 0000000..c881a1b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/RECORD @@ -0,0 +1,10 @@ +aiosignal-1.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiosignal-1.3.1.dist-info/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +aiosignal-1.3.1.dist-info/METADATA,sha256=c0HRnlYzfXKztZPTFDlPfygizTherhG5WdwXlvco0Ug,4008 +aiosignal-1.3.1.dist-info/RECORD,, +aiosignal-1.3.1.dist-info/WHEEL,sha256=ZL1lC_LiPDNRgDnOl2taCMc83aPEUZgHHv2h-LDgdiM,92 +aiosignal-1.3.1.dist-info/top_level.txt,sha256=z45aNOKGDdrI1roqZY3BGXQ22kJFPHBmVdwtLYLtXC0,10 +aiosignal/__init__.py,sha256=zQNfFYRSd84bswvpFv8ZWjEr5DeYwV3LXbMSyo2222s,867 +aiosignal/__init__.pyi,sha256=xeCddYSS8fZAkz8S4HuKSR2IDe3N7RW_LKcXDPPA1Xk,311 +aiosignal/__pycache__/__init__.cpython-310.pyc,, +aiosignal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/WHEEL new file mode 100644 index 0000000..5e1f087 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/top_level.txt new file mode 100644 index 0000000..ac6df3a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal-1.3.1.dist-info/top_level.txt @@ -0,0 +1 @@ +aiosignal diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.py b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.py new file mode 100644 index 0000000..3d288e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.py @@ -0,0 +1,36 @@ +from frozenlist import FrozenList + +__version__ = "1.3.1" + +__all__ = ("Signal",) + + +class Signal(FrozenList): + """Coroutine-based signal implementation. + + To connect a callback to a signal, use any list method. + + Signals are fired using the send() coroutine, which takes named + arguments. + """ + + __slots__ = ("_owner",) + + def __init__(self, owner): + super().__init__() + self._owner = owner + + def __repr__(self): + return "".format( + self._owner, self.frozen, list(self) + ) + + async def send(self, *args, **kwargs): + """ + Sends data to all registered receivers. + """ + if not self.frozen: + raise RuntimeError("Cannot send non-frozen signal.") + + for receiver in self: + await receiver(*args, **kwargs) # type: ignore diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.pyi new file mode 100644 index 0000000..d4e3416 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__init__.pyi @@ -0,0 +1,12 @@ +from typing import Any, Generic, TypeVar + +from frozenlist import FrozenList + +__all__ = ("Signal",) + +_T = TypeVar("_T") + +class Signal(FrozenList[_T], Generic[_T]): + def __init__(self, owner: Any) -> None: ... + def __repr__(self) -> str: ... + async def send(self, *args: Any, **kwargs: Any) -> None: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d49983f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/aiosignal/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/aiosignal/py.typed b/.pythonlibs/lib/python3.10/site-packages/aiosignal/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/METADATA new file mode 100644 index 0000000..36ec9b0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/METADATA @@ -0,0 +1,242 @@ +Metadata-Version: 2.1 +Name: annotated-types +Version: 0.6.0 +Summary: Reusable constraint types to use with typing.Annotated +Author-email: Samuel Colvin , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Zac Hatfield-Dodds +License-File: LICENSE +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Typing :: Typed +Requires-Python: >=3.8 +Requires-Dist: typing-extensions>=4.0.0; python_version < '3.9' +Description-Content-Type: text/markdown + +# annotated-types + +[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) +[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) +[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) + +[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of +adding context-specific metadata to existing types, and specifies that +`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special +logic for `x`. + +This package provides metadata objects which can be used to represent common +constraints such as upper and lower bounds on scalar values and collection sizes, +a `Predicate` marker for runtime checks, and +descriptions of how we intend these metadata to be interpreted. In some cases, +we also note alternative representations which do not require this package. + +## Install + +```bash +pip install annotated-types +``` + +## Examples + +```python +from typing import Annotated +from annotated_types import Gt, Len, Predicate + +class MyClass: + age: Annotated[int, Gt(18)] # Valid: 19, 20, ... + # Invalid: 17, 18, "19", 19.0, ... + factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... + # Invalid: 4, 8, -2, 5.0, "prime", ... + + my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] + # Invalid: (1, 2), ["abc"], [0] * 20 +``` + +## Documentation + +_While `annotated-types` avoids runtime checks for performance, users should not +construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. +Downstream implementors may choose to raise an error, emit a warning, silently ignore +a metadata item, etc., if the metadata objects described below are used with an +incompatible type - or for any other reason!_ + +### Gt, Ge, Lt, Le + +Express inclusive and/or exclusive bounds on orderable values - which may be numbers, +dates, times, strings, sets, etc. Note that the boundary value need not be of the +same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` +is fine, for example, and implies that the value is an integer x such that `x > 1.5`. + +We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` +as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on +the `annotated-types` package. + +To be explicit, these types have the following meanings: + +* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum +* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum +* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum +* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum + +### Interval + +`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single +metadata object. `None` attributes should be ignored, and non-`None` attributes +treated as per the single bounds above. + +### MultipleOf + +`MultipleOf(multiple_of=x)` might be interpreted in two ways: + +1. Python semantics, implying `value % multiple_of == 0`, or +2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), + where `int(value / multiple_of) == value / multiple_of`. + +We encourage users to be aware of these two common interpretations and their +distinct behaviours, especially since very large or non-integer numbers make +it easy to cause silent data corruption due to floating-point imprecision. + +We encourage libraries to carefully document which interpretation they implement. + +### MinLen, MaxLen, Len + +`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. + +As well as `Len()` which can optionally include upper and lower bounds, we also +provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` +and `Len(max_length=y)` respectively. + +`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. + +Examples of usage: + +* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10))`) - list must have a length of 10 or less +* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less +* `Annotated[list, MinLen(3))` (or `Annotated[list, Len(min_length=3))`) - list must have a length of 3 or more +* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 +* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 + +#### Changed in v0.4.0 + +* `min_inclusive` has been renamed to `min_length`, no change in meaning +* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** +* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic + meaning of the upper bound in slices vs. `Len` + +See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. + +### Timezone + +`Timezone` can be used with a `datetime` or a `time` to express which timezones +are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. +`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) +expresses that any timezone-aware datetime is allowed. You may also pass a specific +timezone string or `timezone` object such as `Timezone(timezone.utc)` or +`Timezone("Africa/Abidjan")` to express that you only allow a specific timezone, +though we note that this is often a symptom of fragile design. + +### Predicate + +`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. +Users should prefer the statically inspectable metadata above, but if you need +the full power and flexibility of arbitrary runtime predicates... here it is. + +We provide a few predefined predicates for common string constraints: + +* `IsLower = Predicate(str.islower)` +* `IsUpper = Predicate(str.isupper)` +* `IsDigit = Predicate(str.isdigit)` +* `IsFinite = Predicate(math.isfinite)` +* `IsNotFinite = Predicate(Not(math.isfinite))` +* `IsNan = Predicate(math.isnan)` +* `IsNotNan = Predicate(Not(math.isnan))` +* `IsInfinite = Predicate(math.isinf)` +* `IsNotInfinite = Predicate(Not(math.isinf))` + +Some libraries might have special logic to handle known or understandable predicates, +for example by checking for `str.isdigit` and using its presence to both call custom +logic to enforce digit-only strings, and customise some generated external schema. +Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in +favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. + +To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. + +We do not specify what behaviour should be expected for predicates that raise +an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently +skip invalid constraints, or statically raise an error; or it might try calling it +and then propogate or discard the resulting +`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` +exception. We encourage libraries to document the behaviour they choose. + +### Doc + +`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. + +It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. + +It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. + +This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). + +### Integrating downstream types with `GroupedMetadata` + +Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. +This can help reduce verbosity and cognitive overhead for users. +For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: + +```python +from dataclasses import dataclass +from typing import Iterator +from annotated_types import GroupedMetadata, Ge + +@dataclass +class Field(GroupedMetadata): + ge: int | None = None + description: str | None = None + + def __iter__(self) -> Iterator[object]: + # Iterating over a GroupedMetadata object should yield annotated-types + # constraint metadata objects which describe it as fully as possible, + # and may include other unknown objects too. + if self.ge is not None: + yield Ge(self.ge) + if self.description is not None: + yield Description(self.description) +``` + +Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. + +Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. + +Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. + +### Consuming metadata + +We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). + +It is up to the implementer to determine how this metadata is used. +You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. + +## Design & History + +This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic +and Hypothesis, with the goal of making it as easy as possible for end-users to +provide more informative annotations for use by runtime libraries. + +It is deliberately minimal, and following PEP-593 allows considerable downstream +discretion in what (if anything!) they choose to support. Nonetheless, we expect +that staying simple and covering _only_ the most common use-cases will give users +and maintainers the best experience we can. If you'd like more constraints for your +types - follow our lead, by defining them and documenting them downstream! diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/RECORD new file mode 100644 index 0000000..52ddf7b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/RECORD @@ -0,0 +1,10 @@ +annotated_types-0.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +annotated_types-0.6.0.dist-info/METADATA,sha256=78YeruT3b_doh8TbsK7qcbqY4cvCMz_QQRCGUbyAo6M,12879 +annotated_types-0.6.0.dist-info/RECORD,, +annotated_types-0.6.0.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87 +annotated_types-0.6.0.dist-info/licenses/LICENSE,sha256=_hBJiEsaDZNCkB6I4H8ykl0ksxIdmXK2poBfuYJLCV0,1083 +annotated_types/__init__.py,sha256=txLoRt8iSiAc-Su37esWpMI0wFjN92V7gE5HdFIeeKI,12151 +annotated_types/__pycache__/__init__.cpython-310.pyc,, +annotated_types/__pycache__/test_cases.cpython-310.pyc,, +annotated_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +annotated_types/test_cases.py,sha256=LfFyURZwr7X3NVfoCrSXSMMxTxJD4o8Xz-Y8qbmY7JU,6327 diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/WHEEL new file mode 100644 index 0000000..ba1a8af --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.18.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/licenses/LICENSE b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..d99323a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types-0.6.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 the contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types/__init__.py b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__init__.py new file mode 100644 index 0000000..2f98950 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__init__.py @@ -0,0 +1,396 @@ +import math +import sys +from dataclasses import dataclass +from datetime import timezone +from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, SupportsFloat, SupportsIndex, TypeVar, Union + +if sys.version_info < (3, 8): + from typing_extensions import Protocol, runtime_checkable +else: + from typing import Protocol, runtime_checkable + +if sys.version_info < (3, 9): + from typing_extensions import Annotated, Literal +else: + from typing import Annotated, Literal + +if sys.version_info < (3, 10): + EllipsisType = type(Ellipsis) + KW_ONLY = {} + SLOTS = {} +else: + from types import EllipsisType + + KW_ONLY = {"kw_only": True} + SLOTS = {"slots": True} + + +__all__ = ( + 'BaseMetadata', + 'GroupedMetadata', + 'Gt', + 'Ge', + 'Lt', + 'Le', + 'Interval', + 'MultipleOf', + 'MinLen', + 'MaxLen', + 'Len', + 'Timezone', + 'Predicate', + 'LowerCase', + 'UpperCase', + 'IsDigits', + 'IsFinite', + 'IsNotFinite', + 'IsNan', + 'IsNotNan', + 'IsInfinite', + 'IsNotInfinite', + 'doc', + 'DocInfo', + '__version__', +) + +__version__ = '0.6.0' + + +T = TypeVar('T') + + +# arguments that start with __ are considered +# positional only +# see https://peps.python.org/pep-0484/#positional-only-arguments + + +class SupportsGt(Protocol): + def __gt__(self: T, __other: T) -> bool: + ... + + +class SupportsGe(Protocol): + def __ge__(self: T, __other: T) -> bool: + ... + + +class SupportsLt(Protocol): + def __lt__(self: T, __other: T) -> bool: + ... + + +class SupportsLe(Protocol): + def __le__(self: T, __other: T) -> bool: + ... + + +class SupportsMod(Protocol): + def __mod__(self: T, __other: T) -> T: + ... + + +class SupportsDiv(Protocol): + def __div__(self: T, __other: T) -> T: + ... + + +class BaseMetadata: + """Base class for all metadata. + + This exists mainly so that implementers + can do `isinstance(..., BaseMetadata)` while traversing field annotations. + """ + + __slots__ = () + + +@dataclass(frozen=True, **SLOTS) +class Gt(BaseMetadata): + """Gt(gt=x) implies that the value must be greater than x. + + It can be used with any type that supports the ``>`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + gt: SupportsGt + + +@dataclass(frozen=True, **SLOTS) +class Ge(BaseMetadata): + """Ge(ge=x) implies that the value must be greater than or equal to x. + + It can be used with any type that supports the ``>=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + ge: SupportsGe + + +@dataclass(frozen=True, **SLOTS) +class Lt(BaseMetadata): + """Lt(lt=x) implies that the value must be less than x. + + It can be used with any type that supports the ``<`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + lt: SupportsLt + + +@dataclass(frozen=True, **SLOTS) +class Le(BaseMetadata): + """Le(le=x) implies that the value must be less than or equal to x. + + It can be used with any type that supports the ``<=`` operator, + including numbers, dates and times, strings, sets, and so on. + """ + + le: SupportsLe + + +@runtime_checkable +class GroupedMetadata(Protocol): + """A grouping of multiple BaseMetadata objects. + + `GroupedMetadata` on its own is not metadata and has no meaning. + All it the the constraint and metadata should be fully expressable + in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`. + + Concrete implementations should override `GroupedMetadata.__iter__()` + to add their own metadata. + For example: + + >>> @dataclass + >>> class Field(GroupedMetadata): + >>> gt: float | None = None + >>> description: str | None = None + ... + >>> def __iter__(self) -> Iterable[BaseMetadata]: + >>> if self.gt is not None: + >>> yield Gt(self.gt) + >>> if self.description is not None: + >>> yield Description(self.gt) + + Also see the implementation of `Interval` below for an example. + + Parsers should recognize this and unpack it so that it can be used + both with and without unpacking: + + - `Annotated[int, Field(...)]` (parser must unpack Field) + - `Annotated[int, *Field(...)]` (PEP-646) + """ # noqa: trailing-whitespace + + @property + def __is_annotated_types_grouped_metadata__(self) -> Literal[True]: + return True + + def __iter__(self) -> Iterator[BaseMetadata]: + ... + + if not TYPE_CHECKING: + __slots__ = () # allow subclasses to use slots + + def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: + # Basic ABC like functionality without the complexity of an ABC + super().__init_subclass__(*args, **kwargs) + if cls.__iter__ is GroupedMetadata.__iter__: + raise TypeError("Can't subclass GroupedMetadata without implementing __iter__") + + def __iter__(self) -> Iterator[BaseMetadata]: # noqa: F811 + raise NotImplementedError # more helpful than "None has no attribute..." type errors + + +@dataclass(frozen=True, **KW_ONLY, **SLOTS) +class Interval(GroupedMetadata): + """Interval can express inclusive or exclusive bounds with a single object. + + It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which + are interpreted the same way as the single-bound constraints. + """ + + gt: Union[SupportsGt, None] = None + ge: Union[SupportsGe, None] = None + lt: Union[SupportsLt, None] = None + le: Union[SupportsLe, None] = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack an Interval into zero or more single-bounds.""" + if self.gt is not None: + yield Gt(self.gt) + if self.ge is not None: + yield Ge(self.ge) + if self.lt is not None: + yield Lt(self.lt) + if self.le is not None: + yield Le(self.le) + + +@dataclass(frozen=True, **SLOTS) +class MultipleOf(BaseMetadata): + """MultipleOf(multiple_of=x) might be interpreted in two ways: + + 1. Python semantics, implying ``value % multiple_of == 0``, or + 2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of`` + + We encourage users to be aware of these two common interpretations, + and libraries to carefully document which they implement. + """ + + multiple_of: Union[SupportsDiv, SupportsMod] + + +@dataclass(frozen=True, **SLOTS) +class MinLen(BaseMetadata): + """ + MinLen() implies minimum inclusive length, + e.g. ``len(value) >= min_length``. + """ + + min_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, **SLOTS) +class MaxLen(BaseMetadata): + """ + MaxLen() implies maximum inclusive length, + e.g. ``len(value) <= max_length``. + """ + + max_length: Annotated[int, Ge(0)] + + +@dataclass(frozen=True, **SLOTS) +class Len(GroupedMetadata): + """ + Len() implies that ``min_length <= len(value) <= max_length``. + + Upper bound may be omitted or ``None`` to indicate no upper length bound. + """ + + min_length: Annotated[int, Ge(0)] = 0 + max_length: Optional[Annotated[int, Ge(0)]] = None + + def __iter__(self) -> Iterator[BaseMetadata]: + """Unpack a Len into zone or more single-bounds.""" + if self.min_length > 0: + yield MinLen(self.min_length) + if self.max_length is not None: + yield MaxLen(self.max_length) + + +@dataclass(frozen=True, **SLOTS) +class Timezone(BaseMetadata): + """Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive). + + ``Annotated[datetime, Timezone(None)]`` must be a naive datetime. + ``Timezone[...]`` (the ellipsis literal) expresses that the datetime must be + tz-aware but any timezone is allowed. + + You may also pass a specific timezone string or timezone object such as + ``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that + you only allow a specific timezone, though we note that this is often + a symptom of poor design. + """ + + tz: Union[str, timezone, EllipsisType, None] + + +@dataclass(frozen=True, **SLOTS) +class Predicate(BaseMetadata): + """``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values. + + Users should prefer statically inspectable metadata, but if you need the full + power and flexibility of arbitrary runtime predicates... here it is. + + We provide a few predefined predicates for common string constraints: + ``IsLower = Predicate(str.islower)``, ``IsUpper = Predicate(str.isupper)``, and + ``IsDigit = Predicate(str.isdigit)``. Users are encouraged to use methods which + can be given special handling, and avoid indirection like ``lambda s: s.lower()``. + + Some libraries might have special logic to handle certain predicates, e.g. by + checking for `str.isdigit` and using its presence to both call custom logic to + enforce digit-only strings, and customise some generated external schema. + + We do not specify what behaviour should be expected for predicates that raise + an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently + skip invalid constraints, or statically raise an error; or it might try calling it + and then propogate or discard the resulting exception. + """ + + func: Callable[[Any], bool] + + +@dataclass +class Not: + func: Callable[[Any], bool] + + def __call__(self, __v: Any) -> bool: + return not self.func(__v) + + +_StrType = TypeVar("_StrType", bound=str) + +LowerCase = Annotated[_StrType, Predicate(str.islower)] +""" +Return True if the string is a lowercase string, False otherwise. + +A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string. +""" # noqa: E501 +UpperCase = Annotated[_StrType, Predicate(str.isupper)] +""" +Return True if the string is an uppercase string, False otherwise. + +A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string. +""" # noqa: E501 +IsDigits = Annotated[_StrType, Predicate(str.isdigit)] +""" +Return True if the string is a digit string, False otherwise. + +A string is a digit string if all characters in the string are digits and there is at least one character in the string. +""" # noqa: E501 +IsAscii = Annotated[_StrType, Predicate(str.isascii)] +""" +Return True if all characters in the string are ASCII, False otherwise. + +ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too. +""" + +_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex]) +IsFinite = Annotated[_NumericType, Predicate(math.isfinite)] +"""Return True if x is neither an infinity nor a NaN, and False otherwise.""" +IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))] +"""Return True if x is one of infinity or NaN, and False otherwise""" +IsNan = Annotated[_NumericType, Predicate(math.isnan)] +"""Return True if x is a NaN (not a number), and False otherwise.""" +IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))] +"""Return True if x is anything but NaN (not a number), and False otherwise.""" +IsInfinite = Annotated[_NumericType, Predicate(math.isinf)] +"""Return True if x is a positive or negative infinity, and False otherwise.""" +IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))] +"""Return True if x is neither a positive or negative infinity, and False otherwise.""" + +try: + from typing_extensions import DocInfo, doc # type: ignore [attr-defined] +except ImportError: + + @dataclass(frozen=True, **SLOTS) + class DocInfo: # type: ignore [no-redef] + """ " + The return value of doc(), mainly to be used by tools that want to extract the + Annotated documentation at runtime. + """ + + documentation: str + """The documentation string passed to doc().""" + + def doc( + documentation: str, + ) -> DocInfo: + """ + Add documentation to a type annotation inside of Annotated. + + For example: + + >>> def hi(name: Annotated[int, doc("The name of the user")]) -> None: ... + """ + return DocInfo(documentation) diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..759dce9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/test_cases.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/test_cases.cpython-310.pyc new file mode 100644 index 0000000..19d9817 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/annotated_types/__pycache__/test_cases.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types/py.typed b/.pythonlibs/lib/python3.10/site-packages/annotated_types/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/annotated_types/test_cases.py b/.pythonlibs/lib/python3.10/site-packages/annotated_types/test_cases.py new file mode 100644 index 0000000..f54df70 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/annotated_types/test_cases.py @@ -0,0 +1,147 @@ +import math +import sys +from datetime import date, datetime, timedelta, timezone +from decimal import Decimal +from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple + +if sys.version_info < (3, 9): + from typing_extensions import Annotated +else: + from typing import Annotated + +import annotated_types as at + + +class Case(NamedTuple): + """ + A test case for `annotated_types`. + """ + + annotation: Any + valid_cases: Iterable[Any] + invalid_cases: Iterable[Any] + + +def cases() -> Iterable[Case]: + # Gt, Ge, Lt, Le + yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Gt(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(date(2000, 1, 1))], + [date(2000, 1, 2), date(2000, 1, 3)], + [date(2000, 1, 1), date(1999, 12, 31)], + ) + yield Case( + Annotated[datetime, at.Gt(Decimal('1.123'))], + [Decimal('1.1231'), Decimal('123')], + [Decimal('1.123'), Decimal('0')], + ) + + yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) + yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) + yield Case( + Annotated[datetime, at.Ge(datetime(2000, 1, 1))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(1998, 1, 1), datetime(1999, 12, 31)], + ) + + yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) + yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Lt(datetime(2000, 1, 1))], + [datetime(1999, 12, 31), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) + yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) + yield Case( + Annotated[datetime, at.Le(datetime(2000, 1, 1))], + [datetime(2000, 1, 1), datetime(1999, 12, 31)], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + ) + + # Interval + yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) + yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) + yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) + yield Case( + Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], + [datetime(2000, 1, 2), datetime(2000, 1, 3)], + [datetime(2000, 1, 1), datetime(2000, 1, 4)], + ) + + yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) + yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) + + # lengths + + yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) + yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) + + yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) + yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) + + yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) + yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) + + yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) + yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) + yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) + + # Timezone + + yield Case( + Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] + ) + yield Case( + Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] + ) + yield Case( + Annotated[datetime, at.Timezone(timezone.utc)], + [datetime(2000, 1, 1, tzinfo=timezone.utc)], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + yield Case( + Annotated[datetime, at.Timezone('Europe/London')], + [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], + [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], + ) + + # predicate types + + yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) + yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) + yield Case(at.IsDigits[str], ['123'], ['', 'ab', 'a1b2']) + yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) + + yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) + + yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) + yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) + yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) + yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) + yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) + yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) + + # check stacked predicates + yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) + + # doc + yield Case(Annotated[int, at.doc("A number")], [1, 2], []) + + # custom GroupedMetadata + class MyCustomGroupedMetadata(at.GroupedMetadata): + def __iter__(self) -> Iterator[at.Predicate]: + yield at.Predicate(lambda x: float(x).is_integer()) + + yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/LICENSE new file mode 100644 index 0000000..104eebf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 Alex Grönholm + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/METADATA new file mode 100644 index 0000000..e02715c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/METADATA @@ -0,0 +1,104 @@ +Metadata-Version: 2.1 +Name: anyio +Version: 4.3.0 +Summary: High level compatibility layer for multiple asynchronous event loop implementations +Author-email: Alex Grönholm +License: MIT +Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ +Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html +Project-URL: Source code, https://github.com/agronholm/anyio +Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: idna >=2.8 +Requires-Dist: sniffio >=1.1 +Requires-Dist: exceptiongroup >=1.0.2 ; python_version < "3.11" +Requires-Dist: typing-extensions >=4.1 ; python_version < "3.11" +Provides-Extra: doc +Requires-Dist: packaging ; extra == 'doc' +Requires-Dist: Sphinx >=7 ; extra == 'doc' +Requires-Dist: sphinx-rtd-theme ; extra == 'doc' +Requires-Dist: sphinx-autodoc-typehints >=1.2.0 ; extra == 'doc' +Provides-Extra: test +Requires-Dist: anyio[trio] ; extra == 'test' +Requires-Dist: coverage[toml] >=7 ; extra == 'test' +Requires-Dist: exceptiongroup >=1.2.0 ; extra == 'test' +Requires-Dist: hypothesis >=4.0 ; extra == 'test' +Requires-Dist: psutil >=5.9 ; extra == 'test' +Requires-Dist: pytest >=7.0 ; extra == 'test' +Requires-Dist: pytest-mock >=3.6.1 ; extra == 'test' +Requires-Dist: trustme ; extra == 'test' +Requires-Dist: uvloop >=0.17 ; (platform_python_implementation == "CPython" and platform_system != "Windows") and extra == 'test' +Provides-Extra: trio +Requires-Dist: trio >=0.23 ; extra == 'trio' + +.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/anyio/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/anyio?branch=master + :alt: Code Coverage +.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest + :target: https://anyio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation +.. image:: https://badges.gitter.im/gitterHQ/gitter.svg + :target: https://gitter.im/python-trio/AnyIO + :alt: Gitter chat + +AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or +trio_. It implements trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony +with the native SC of trio itself. + +Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or +trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full +refactoring necessary. It will blend in with the native libraries of your chosen backend. + +Documentation +------------- + +View full documentation at: https://anyio.readthedocs.io/ + +Features +-------- + +AnyIO offers the following functionality: + +* Task groups (nurseries_ in trio terminology) +* High-level networking (TCP, UDP and UNIX sockets) + + * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python + 3.8) + * async/await style UDP sockets (unlike asyncio where you still have to use Transports and + Protocols) + +* A versatile API for byte streams and object streams +* Inter-task synchronization and communication (locks, conditions, events, semaphores, object + streams) +* Worker threads +* Subprocesses +* Asynchronous file I/O (using worker threads) +* Signal handling + +AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. +It even works with the popular Hypothesis_ library. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html +.. _trio: https://github.com/python-trio/trio +.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency +.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning +.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs +.. _pytest: https://docs.pytest.org/en/latest/ +.. _Hypothesis: https://hypothesis.works/ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/RECORD new file mode 100644 index 0000000..610303b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/RECORD @@ -0,0 +1,82 @@ +anyio-4.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +anyio-4.3.0.dist-info/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 +anyio-4.3.0.dist-info/METADATA,sha256=Xy5N0vn6s_rjN1C0EbX_z1N_lf3bZIJu88RAQxVAsrI,4599 +anyio-4.3.0.dist-info/RECORD,, +anyio-4.3.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +anyio-4.3.0.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 +anyio-4.3.0.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 +anyio/__init__.py,sha256=CxUxIHOIONI3KpsDLCg-dI6lQaDkW_4Zhtu5jWt1XO8,4344 +anyio/__pycache__/__init__.cpython-310.pyc,, +anyio/__pycache__/from_thread.cpython-310.pyc,, +anyio/__pycache__/lowlevel.cpython-310.pyc,, +anyio/__pycache__/pytest_plugin.cpython-310.pyc,, +anyio/__pycache__/to_process.cpython-310.pyc,, +anyio/__pycache__/to_thread.cpython-310.pyc,, +anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_backends/__pycache__/__init__.cpython-310.pyc,, +anyio/_backends/__pycache__/_asyncio.cpython-310.pyc,, +anyio/_backends/__pycache__/_trio.cpython-310.pyc,, +anyio/_backends/_asyncio.py,sha256=D1ME_t4zfei7sm7Ic8jEQBsAc3uMykvsQjZxhT5aTNg,81968 +anyio/_backends/_trio.py,sha256=87ahML2dsX3uQuCkyIB4E7XFK6jAGpxs5hVlGmHHd8Q,35642 +anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_core/__pycache__/__init__.cpython-310.pyc,, +anyio/_core/__pycache__/_eventloop.cpython-310.pyc,, +anyio/_core/__pycache__/_exceptions.cpython-310.pyc,, +anyio/_core/__pycache__/_fileio.cpython-310.pyc,, +anyio/_core/__pycache__/_resources.cpython-310.pyc,, +anyio/_core/__pycache__/_signals.cpython-310.pyc,, +anyio/_core/__pycache__/_sockets.cpython-310.pyc,, +anyio/_core/__pycache__/_streams.cpython-310.pyc,, +anyio/_core/__pycache__/_subprocesses.cpython-310.pyc,, +anyio/_core/__pycache__/_synchronization.cpython-310.pyc,, +anyio/_core/__pycache__/_tasks.cpython-310.pyc,, +anyio/_core/__pycache__/_testing.cpython-310.pyc,, +anyio/_core/__pycache__/_typedattr.cpython-310.pyc,, +anyio/_core/_eventloop.py,sha256=uCwWwGtN9Tf46nkcWLyku8iYEFWCkSjPW0AkDbnpCM0,4408 +anyio/_core/_exceptions.py,sha256=wUmhDu80qEB7z9EdCqUwVEhNUlNEok4_W2-rC6sCAUQ,2078 +anyio/_core/_fileio.py,sha256=Zdp3L0_T7mMCaYq3bFTCb-udTnIknKpzcLEvHK-Tmbc,19512 +anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 +anyio/_core/_signals.py,sha256=rDOVxtugZDgC5AhfW3lrwsre2n9Pj_adoRUidBiF6dA,878 +anyio/_core/_sockets.py,sha256=HwOMg0xUPw0T7N-aipxq_4OEM703llh3I9_YIg9a4XM,24048 +anyio/_core/_streams.py,sha256=Z8ZlTY6xom5EszrMsgCT3TphiT4JIlQG-y33CrD0NQY,1811 +anyio/_core/_subprocesses.py,sha256=ZLLNXAtlRGfbyC4sOIltYB1k3NJa3tqk_x_Fsnbcs1M,5272 +anyio/_core/_synchronization.py,sha256=h3o6dWWbzVrcNmi7i2mQjEgRtnIxkGtjmYK7KMpdlaE,18444 +anyio/_core/_tasks.py,sha256=pvVEX2Fw159sf0ypAPerukKsZgRRwvFFedVW52nR2Vk,4764 +anyio/_core/_testing.py,sha256=i97S5rSWIFqfCGPm4mEMdiJaUpVskk-cWEjarWTeXXs,1964 +anyio/_core/_typedattr.py,sha256=QTbaIwZEewhwAKvbBHFBcO_cRhNP_lXjAobEldzExCU,2499 +anyio/abc/__init__.py,sha256=U44_s3BglL8BojWQiq0KuokvCqkunIp-ySH3GyRXxAc,2681 +anyio/abc/__pycache__/__init__.cpython-310.pyc,, +anyio/abc/__pycache__/_eventloop.cpython-310.pyc,, +anyio/abc/__pycache__/_resources.cpython-310.pyc,, +anyio/abc/__pycache__/_sockets.cpython-310.pyc,, +anyio/abc/__pycache__/_streams.cpython-310.pyc,, +anyio/abc/__pycache__/_subprocesses.cpython-310.pyc,, +anyio/abc/__pycache__/_tasks.cpython-310.pyc,, +anyio/abc/__pycache__/_testing.cpython-310.pyc,, +anyio/abc/_eventloop.py,sha256=QOtkEHCkoE8czGu4RNzZ_q-xNjC0nRyoS0QQJ5KTvYU,10097 +anyio/abc/_resources.py,sha256=KBJP3wGbvSfKfTjfOLL4QCJdeiaNwqqF_6FwPsmQssM,763 +anyio/abc/_sockets.py,sha256=XdZ42TQ1omZN9Ec3HUfTMWG_i-21yMjXQ_FFslAZtzQ,6269 +anyio/abc/_streams.py,sha256=GzST5Q2zQmxVzdrAqtbSyHNxkPlIC9AzeZJg_YyPAXw,6598 +anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 +anyio/abc/_tasks.py,sha256=q3bEbCF46I2tQjYSbRdbaavq0R_HOV9JAjzQr8biprU,2747 +anyio/abc/_testing.py,sha256=EiWEaIVy15lHszO000Xp4FsB13NbBvC1BpUci47B5zs,1829 +anyio/from_thread.py,sha256=UTEY_NsiqQRukO3L3riQx4Eegulj3RyLlbITJz7pvLM,15749 +anyio/lowlevel.py,sha256=0awnMh05kA5WUNaOBoQZSImBj0xLNRlYOuMGGiztWnM,4185 +anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/pytest_plugin.py,sha256=TBgRAfT-Oxy6efhO1Tziq54NND3Jy4dRmwkMmQXSvhI,5386 +anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/streams/__pycache__/__init__.cpython-310.pyc,, +anyio/streams/__pycache__/buffered.cpython-310.pyc,, +anyio/streams/__pycache__/file.cpython-310.pyc,, +anyio/streams/__pycache__/memory.cpython-310.pyc,, +anyio/streams/__pycache__/stapled.cpython-310.pyc,, +anyio/streams/__pycache__/text.cpython-310.pyc,, +anyio/streams/__pycache__/tls.cpython-310.pyc,, +anyio/streams/buffered.py,sha256=UCldKC168YuLvT7n3HtNPnQ2iWAMSTYQWbZvzLwMwkM,4500 +anyio/streams/file.py,sha256=6uoTNb5KbMoj-6gS3_xrrL8uZN8Q4iIvOS1WtGyFfKw,4383 +anyio/streams/memory.py,sha256=bqN9YwAPA6ZtdohOsq_YBpLFlRHR5k-W8y0pD_jznb8,9296 +anyio/streams/stapled.py,sha256=U09pCrmOw9kkNhe6tKopsm1QIMT1lFTFvtb-A7SIe4k,4302 +anyio/streams/text.py,sha256=6x8w8xlfCZKTUWQoJiMPoMhSSJFUBRKgoBNSBtbd9yg,5094 +anyio/streams/tls.py,sha256=ev-6yNOGcIkziIkcIfKj8VmLqQJW-iDBJttaKgKDsF4,12752 +anyio/to_process.py,sha256=lx_bt0CUJsS1eSlraw662OpCjRgGXowoyf1Q-i-kOxo,9535 +anyio/to_thread.py,sha256=WM2JQ2MbVsd5D5CM08bQiTwzZIvpsGjfH1Fy247KoDQ,2396 diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/entry_points.txt new file mode 100644 index 0000000..44dd9bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +anyio = anyio.pytest_plugin diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/top_level.txt new file mode 100644 index 0000000..c77c069 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio-4.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +anyio diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__init__.py b/.pythonlibs/lib/python3.10/site-packages/anyio/__init__.py new file mode 100644 index 0000000..7bfe231 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/__init__.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import Any + +from ._core._eventloop import current_time as current_time +from ._core._eventloop import get_all_backends as get_all_backends +from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class +from ._core._eventloop import run as run +from ._core._eventloop import sleep as sleep +from ._core._eventloop import sleep_forever as sleep_forever +from ._core._eventloop import sleep_until as sleep_until +from ._core._exceptions import BrokenResourceError as BrokenResourceError +from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess +from ._core._exceptions import BusyResourceError as BusyResourceError +from ._core._exceptions import ClosedResourceError as ClosedResourceError +from ._core._exceptions import DelimiterNotFound as DelimiterNotFound +from ._core._exceptions import EndOfStream as EndOfStream +from ._core._exceptions import IncompleteRead as IncompleteRead +from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError +from ._core._exceptions import WouldBlock as WouldBlock +from ._core._fileio import AsyncFile as AsyncFile +from ._core._fileio import Path as Path +from ._core._fileio import open_file as open_file +from ._core._fileio import wrap_file as wrap_file +from ._core._resources import aclose_forcefully as aclose_forcefully +from ._core._signals import open_signal_receiver as open_signal_receiver +from ._core._sockets import connect_tcp as connect_tcp +from ._core._sockets import connect_unix as connect_unix +from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket +from ._core._sockets import ( + create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, +) +from ._core._sockets import create_tcp_listener as create_tcp_listener +from ._core._sockets import create_udp_socket as create_udp_socket +from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket +from ._core._sockets import create_unix_listener as create_unix_listener +from ._core._sockets import getaddrinfo as getaddrinfo +from ._core._sockets import getnameinfo as getnameinfo +from ._core._sockets import wait_socket_readable as wait_socket_readable +from ._core._sockets import wait_socket_writable as wait_socket_writable +from ._core._streams import create_memory_object_stream as create_memory_object_stream +from ._core._subprocesses import open_process as open_process +from ._core._subprocesses import run_process as run_process +from ._core._synchronization import CapacityLimiter as CapacityLimiter +from ._core._synchronization import ( + CapacityLimiterStatistics as CapacityLimiterStatistics, +) +from ._core._synchronization import Condition as Condition +from ._core._synchronization import ConditionStatistics as ConditionStatistics +from ._core._synchronization import Event as Event +from ._core._synchronization import EventStatistics as EventStatistics +from ._core._synchronization import Lock as Lock +from ._core._synchronization import LockStatistics as LockStatistics +from ._core._synchronization import ResourceGuard as ResourceGuard +from ._core._synchronization import Semaphore as Semaphore +from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics +from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED +from ._core._tasks import CancelScope as CancelScope +from ._core._tasks import create_task_group as create_task_group +from ._core._tasks import current_effective_deadline as current_effective_deadline +from ._core._tasks import fail_after as fail_after +from ._core._tasks import move_on_after as move_on_after +from ._core._testing import TaskInfo as TaskInfo +from ._core._testing import get_current_task as get_current_task +from ._core._testing import get_running_tasks as get_running_tasks +from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked +from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider +from ._core._typedattr import TypedAttributeSet as TypedAttributeSet +from ._core._typedattr import typed_attribute as typed_attribute + +# Re-export imports so they look like they live directly in this package +key: str +value: Any +for key, value in list(locals().items()): + if getattr(value, "__module__", "").startswith("anyio."): + value.__module__ = __name__ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5b90986 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/from_thread.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/from_thread.cpython-310.pyc new file mode 100644 index 0000000..45ae9b5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/from_thread.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/lowlevel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/lowlevel.cpython-310.pyc new file mode 100644 index 0000000..e0658b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/lowlevel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/pytest_plugin.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/pytest_plugin.cpython-310.pyc new file mode 100644 index 0000000..360bc78 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/pytest_plugin.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_process.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_process.cpython-310.pyc new file mode 100644 index 0000000..7c80488 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_process.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_thread.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_thread.cpython-310.pyc new file mode 100644 index 0000000..c472188 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/__pycache__/to_thread.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__init__.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ebce9a1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-310.pyc new file mode 100644 index 0000000..ca51abd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_asyncio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_trio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_trio.cpython-310.pyc new file mode 100644 index 0000000..c4491d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/__pycache__/_trio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_asyncio.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_asyncio.py new file mode 100644 index 0000000..2699bf8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_asyncio.py @@ -0,0 +1,2478 @@ +from __future__ import annotations + +import array +import asyncio +import concurrent.futures +import math +import socket +import sys +import threading +from asyncio import ( + AbstractEventLoop, + CancelledError, + all_tasks, + create_task, + current_task, + get_running_loop, + sleep, +) +from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] +from collections import OrderedDict, deque +from collections.abc import AsyncIterator, Generator, Iterable +from concurrent.futures import Future +from contextlib import suppress +from contextvars import Context, copy_context +from dataclasses import dataclass +from functools import partial, wraps +from inspect import ( + CORO_RUNNING, + CORO_SUSPENDED, + getcoroutinestate, + iscoroutine, +) +from io import IOBase +from os import PathLike +from queue import Queue +from signal import Signals +from socket import AddressFamily, SocketKind +from threading import Thread +from types import TracebackType +from typing import ( + IO, + Any, + AsyncGenerator, + Awaitable, + Callable, + Collection, + ContextManager, + Coroutine, + Mapping, + Optional, + Sequence, + Tuple, + TypeVar, + cast, +) +from weakref import WeakKeyDictionary + +import sniffio + +from .. import CapacityLimiterStatistics, EventStatistics, TaskInfo, abc +from .._core._eventloop import claim_worker_thread, threadlocals +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import CapacityLimiter as BaseCapacityLimiter +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import ResourceGuard +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import ( + AsyncBackend, + IPSockAddrType, + SocketListener, + UDPPacketType, + UNIXDatagramPacketType, +) +from ..lowlevel import RunVar +from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +if sys.version_info >= (3, 11): + from asyncio import Runner + from typing import TypeVarTuple, Unpack +else: + import contextvars + import enum + import signal + from asyncio import coroutines, events, exceptions, tasks + + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + + class _State(enum.Enum): + CREATED = "created" + INITIALIZED = "initialized" + CLOSED = "closed" + + class Runner: + # Copied from CPython 3.11 + def __init__( + self, + *, + debug: bool | None = None, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ): + self._state = _State.CREATED + self._debug = debug + self._loop_factory = loop_factory + self._loop: AbstractEventLoop | None = None + self._context = None + self._interrupt_count = 0 + self._set_event_loop = False + + def __enter__(self) -> Runner: + self._lazy_init() + return self + + def __exit__( + self, + exc_type: type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: + self.close() + + def close(self) -> None: + """Shutdown and close event loop.""" + if self._state is not _State.INITIALIZED: + return + try: + loop = self._loop + _cancel_all_tasks(loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + if hasattr(loop, "shutdown_default_executor"): + loop.run_until_complete(loop.shutdown_default_executor()) + else: + loop.run_until_complete(_shutdown_default_executor(loop)) + finally: + if self._set_event_loop: + events.set_event_loop(None) + loop.close() + self._loop = None + self._state = _State.CLOSED + + def get_loop(self) -> AbstractEventLoop: + """Return embedded event loop.""" + self._lazy_init() + return self._loop + + def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: + """Run a coroutine inside the embedded event loop.""" + if not coroutines.iscoroutine(coro): + raise ValueError(f"a coroutine was expected, got {coro!r}") + + if events._get_running_loop() is not None: + # fail fast with short traceback + raise RuntimeError( + "Runner.run() cannot be called from a running event loop" + ) + + self._lazy_init() + + if context is None: + context = self._context + task = context.run(self._loop.create_task, coro) + + if ( + threading.current_thread() is threading.main_thread() + and signal.getsignal(signal.SIGINT) is signal.default_int_handler + ): + sigint_handler = partial(self._on_sigint, main_task=task) + try: + signal.signal(signal.SIGINT, sigint_handler) + except ValueError: + # `signal.signal` may throw if `threading.main_thread` does + # not support signals (e.g. embedded interpreter with signals + # not registered - see gh-91880) + sigint_handler = None + else: + sigint_handler = None + + self._interrupt_count = 0 + try: + return self._loop.run_until_complete(task) + except exceptions.CancelledError: + if self._interrupt_count > 0: + uncancel = getattr(task, "uncancel", None) + if uncancel is not None and uncancel() == 0: + raise KeyboardInterrupt() + raise # CancelledError + finally: + if ( + sigint_handler is not None + and signal.getsignal(signal.SIGINT) is sigint_handler + ): + signal.signal(signal.SIGINT, signal.default_int_handler) + + def _lazy_init(self) -> None: + if self._state is _State.CLOSED: + raise RuntimeError("Runner is closed") + if self._state is _State.INITIALIZED: + return + if self._loop_factory is None: + self._loop = events.new_event_loop() + if not self._set_event_loop: + # Call set_event_loop only once to avoid calling + # attach_loop multiple times on child watchers + events.set_event_loop(self._loop) + self._set_event_loop = True + else: + self._loop = self._loop_factory() + if self._debug is not None: + self._loop.set_debug(self._debug) + self._context = contextvars.copy_context() + self._state = _State.INITIALIZED + + def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: + self._interrupt_count += 1 + if self._interrupt_count == 1 and not main_task.done(): + main_task.cancel() + # wakeup loop if it is blocked by select() with long timeout + self._loop.call_soon_threadsafe(lambda: None) + return + raise KeyboardInterrupt() + + def _cancel_all_tasks(loop: AbstractEventLoop) -> None: + to_cancel = tasks.all_tasks(loop) + if not to_cancel: + return + + for task in to_cancel: + task.cancel() + + loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) + + for task in to_cancel: + if task.cancelled(): + continue + if task.exception() is not None: + loop.call_exception_handler( + { + "message": "unhandled exception during asyncio.run() shutdown", + "exception": task.exception(), + "task": task, + } + ) + + async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: + """Schedule the shutdown of the default executor.""" + + def _do_shutdown(future: asyncio.futures.Future) -> None: + try: + loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] + loop.call_soon_threadsafe(future.set_result, None) + except Exception as ex: + loop.call_soon_threadsafe(future.set_exception, ex) + + loop._executor_shutdown_called = True + if loop._default_executor is None: + return + future = loop.create_future() + thread = threading.Thread(target=_do_shutdown, args=(future,)) + thread.start() + try: + await future + finally: + thread.join() + + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + +_root_task: RunVar[asyncio.Task | None] = RunVar("_root_task") + + +def find_root_task() -> asyncio.Task: + root_task = _root_task.get(None) + if root_task is not None and not root_task.done(): + return root_task + + # Look for a task that has been started via run_until_complete() + for task in all_tasks(): + if task._callbacks and not task.done(): + callbacks = [cb for cb, context in task._callbacks] + for cb in callbacks: + if ( + cb is _run_until_complete_cb + or getattr(cb, "__module__", None) == "uvloop.loop" + ): + _root_task.set(task) + return task + + # Look up the topmost task in the AnyIO task tree, if possible + task = cast(asyncio.Task, current_task()) + state = _task_states.get(task) + if state: + cancel_scope = state.cancel_scope + while cancel_scope and cancel_scope._parent_scope is not None: + cancel_scope = cancel_scope._parent_scope + + if cancel_scope is not None: + return cast(asyncio.Task, cancel_scope._host_task) + + return task + + +def get_callable_name(func: Callable) -> str: + module = getattr(func, "__module__", None) + qualname = getattr(func, "__qualname__", None) + return ".".join([x for x in (module, qualname) if x]) + + +# +# Event loop +# + +_run_vars: WeakKeyDictionary[asyncio.AbstractEventLoop, Any] = WeakKeyDictionary() + + +def _task_started(task: asyncio.Task) -> bool: + """Return ``True`` if the task has been started and has not finished.""" + try: + return getcoroutinestate(task.get_coro()) in (CORO_RUNNING, CORO_SUSPENDED) + except AttributeError: + # task coro is async_genenerator_asend https://bugs.python.org/issue37771 + raise Exception(f"Cannot determine if task {task} has started or not") from None + + +# +# Timeouts and cancellation +# + + +class CancelScope(BaseCancelScope): + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, deadline: float = math.inf, shield: bool = False): + self._deadline = deadline + self._shield = shield + self._parent_scope: CancelScope | None = None + self._child_scopes: set[CancelScope] = set() + self._cancel_called = False + self._cancelled_caught = False + self._active = False + self._timeout_handle: asyncio.TimerHandle | None = None + self._cancel_handle: asyncio.Handle | None = None + self._tasks: set[asyncio.Task] = set() + self._host_task: asyncio.Task | None = None + self._cancel_calls: int = 0 + self._cancelling: int | None = None + + def __enter__(self) -> CancelScope: + if self._active: + raise RuntimeError( + "Each CancelScope may only be used for a single 'with' block" + ) + + self._host_task = host_task = cast(asyncio.Task, current_task()) + self._tasks.add(host_task) + try: + task_state = _task_states[host_task] + except KeyError: + task_state = TaskState(None, self) + _task_states[host_task] = task_state + else: + self._parent_scope = task_state.cancel_scope + task_state.cancel_scope = self + if self._parent_scope is not None: + self._parent_scope._child_scopes.add(self) + self._parent_scope._tasks.remove(host_task) + + self._timeout() + self._active = True + if sys.version_info >= (3, 11): + self._cancelling = self._host_task.cancelling() + + # Start cancelling the host task if the scope was cancelled before entering + if self._cancel_called: + self._deliver_cancellation(self) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + if not self._active: + raise RuntimeError("This cancel scope is not active") + if current_task() is not self._host_task: + raise RuntimeError( + "Attempted to exit cancel scope in a different task than it was " + "entered in" + ) + + assert self._host_task is not None + host_task_state = _task_states.get(self._host_task) + if host_task_state is None or host_task_state.cancel_scope is not self: + raise RuntimeError( + "Attempted to exit a cancel scope that isn't the current tasks's " + "current cancel scope" + ) + + self._active = False + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._tasks.remove(self._host_task) + if self._parent_scope is not None: + self._parent_scope._child_scopes.remove(self) + self._parent_scope._tasks.add(self._host_task) + + host_task_state.cancel_scope = self._parent_scope + + # Restart the cancellation effort in the closest directly cancelled parent + # scope if this one was shielded + self._restart_cancellation_in_parent() + + if self._cancel_called and exc_val is not None: + for exc in iterate_exceptions(exc_val): + if isinstance(exc, CancelledError): + self._cancelled_caught = self._uncancel(exc) + if self._cancelled_caught: + break + + return self._cancelled_caught + + return None + + def _uncancel(self, cancelled_exc: CancelledError) -> bool: + if sys.version_info < (3, 9) or self._host_task is None: + self._cancel_calls = 0 + return True + + # Undo all cancellations done by this scope + if self._cancelling is not None: + while self._cancel_calls: + self._cancel_calls -= 1 + if self._host_task.uncancel() <= self._cancelling: + return True + + self._cancel_calls = 0 + return f"Cancelled by cancel scope {id(self):x}" in cancelled_exc.args + + def _timeout(self) -> None: + if self._deadline != math.inf: + loop = get_running_loop() + if loop.time() >= self._deadline: + self.cancel() + else: + self._timeout_handle = loop.call_at(self._deadline, self._timeout) + + def _deliver_cancellation(self, origin: CancelScope) -> bool: + """ + Deliver cancellation to directly contained tasks and nested cancel scopes. + + Schedule another run at the end if we still have tasks eligible for + cancellation. + + :param origin: the cancel scope that originated the cancellation + :return: ``True`` if the delivery needs to be retried on the next cycle + + """ + should_retry = False + current = current_task() + for task in self._tasks: + if task._must_cancel: # type: ignore[attr-defined] + continue + + # The task is eligible for cancellation if it has started + should_retry = True + if task is not current and (task is self._host_task or _task_started(task)): + waiter = task._fut_waiter # type: ignore[attr-defined] + if not isinstance(waiter, asyncio.Future) or not waiter.done(): + self._cancel_calls += 1 + if sys.version_info >= (3, 9): + task.cancel(f"Cancelled by cancel scope {id(origin):x}") + else: + task.cancel() + + # Deliver cancellation to child scopes that aren't shielded or running their own + # cancellation callbacks + for scope in self._child_scopes: + if not scope._shield and not scope.cancel_called: + should_retry = scope._deliver_cancellation(origin) or should_retry + + # Schedule another callback if there are still tasks left + if origin is self: + if should_retry: + self._cancel_handle = get_running_loop().call_soon( + self._deliver_cancellation, origin + ) + else: + self._cancel_handle = None + + return should_retry + + def _restart_cancellation_in_parent(self) -> None: + """ + Restart the cancellation effort in the closest directly cancelled parent scope. + + """ + scope = self._parent_scope + while scope is not None: + if scope._cancel_called: + if scope._cancel_handle is None: + scope._deliver_cancellation(scope) + + break + + # No point in looking beyond any shielded scope + if scope._shield: + break + + scope = scope._parent_scope + + def _parent_cancelled(self) -> bool: + # Check whether any parent has been cancelled + cancel_scope = self._parent_scope + while cancel_scope is not None and not cancel_scope._shield: + if cancel_scope._cancel_called: + return True + else: + cancel_scope = cancel_scope._parent_scope + + return False + + def cancel(self) -> None: + if not self._cancel_called: + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._cancel_called = True + if self._host_task is not None: + self._deliver_cancellation(self) + + @property + def deadline(self) -> float: + return self._deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self._deadline = float(value) + if self._timeout_handle is not None: + self._timeout_handle.cancel() + self._timeout_handle = None + + if self._active and not self._cancel_called: + self._timeout() + + @property + def cancel_called(self) -> bool: + return self._cancel_called + + @property + def cancelled_caught(self) -> bool: + return self._cancelled_caught + + @property + def shield(self) -> bool: + return self._shield + + @shield.setter + def shield(self, value: bool) -> None: + if self._shield != value: + self._shield = value + if not value: + self._restart_cancellation_in_parent() + + +# +# Task states +# + + +class TaskState: + """ + Encapsulates auxiliary task information that cannot be added to the Task instance + itself because there are no guarantees about its implementation. + """ + + __slots__ = "parent_id", "cancel_scope" + + def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): + self.parent_id = parent_id + self.cancel_scope = cancel_scope + + +_task_states = WeakKeyDictionary() # type: WeakKeyDictionary[asyncio.Task, TaskState] + + +# +# Task groups +# + + +class _AsyncioTaskStatus(abc.TaskStatus): + def __init__(self, future: asyncio.Future, parent_id: int): + self._future = future + self._parent_id = parent_id + + def started(self, value: T_contra | None = None) -> None: + try: + self._future.set_result(value) + except asyncio.InvalidStateError: + raise RuntimeError( + "called 'started' twice on the same task status" + ) from None + + task = cast(asyncio.Task, current_task()) + _task_states[task].parent_id = self._parent_id + + +def iterate_exceptions( + exception: BaseException, +) -> Generator[BaseException, None, None]: + if isinstance(exception, BaseExceptionGroup): + for exc in exception.exceptions: + yield from iterate_exceptions(exc) + else: + yield exception + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self.cancel_scope: CancelScope = CancelScope() + self._active = False + self._exceptions: list[BaseException] = [] + self._tasks: set[asyncio.Task] = set() + + async def __aenter__(self) -> TaskGroup: + self.cancel_scope.__enter__() + self._active = True + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + ignore_exception = self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) + if exc_val is not None: + self.cancel_scope.cancel() + if not isinstance(exc_val, CancelledError): + self._exceptions.append(exc_val) + + cancelled_exc_while_waiting_tasks: CancelledError | None = None + while self._tasks: + try: + await asyncio.wait(self._tasks) + except CancelledError as exc: + # This task was cancelled natively; reraise the CancelledError later + # unless this task was already interrupted by another exception + self.cancel_scope.cancel() + if cancelled_exc_while_waiting_tasks is None: + cancelled_exc_while_waiting_tasks = exc + + self._active = False + if self._exceptions: + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) + + # Raise the CancelledError received while waiting for child tasks to exit, + # unless the context manager itself was previously exited with another + # exception, or if any of the child tasks raised an exception other than + # CancelledError + if cancelled_exc_while_waiting_tasks: + if exc_val is None or ignore_exception: + raise cancelled_exc_while_waiting_tasks + + return ignore_exception + + def _spawn( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + args: tuple[Unpack[PosArgsT]], + name: object, + task_status_future: asyncio.Future | None = None, + ) -> asyncio.Task: + def task_done(_task: asyncio.Task) -> None: + task_state = _task_states[_task] + assert task_state.cancel_scope is not None + assert _task in task_state.cancel_scope._tasks + task_state.cancel_scope._tasks.remove(_task) + self._tasks.remove(task) + del _task_states[_task] + + try: + exc = _task.exception() + except CancelledError as e: + while isinstance(e.__context__, CancelledError): + e = e.__context__ + + exc = e + + if exc is not None: + if task_status_future is None or task_status_future.done(): + if not isinstance(exc, CancelledError): + self._exceptions.append(exc) + + if not self.cancel_scope._parent_cancelled(): + self.cancel_scope.cancel() + else: + task_status_future.set_exception(exc) + elif task_status_future is not None and not task_status_future.done(): + task_status_future.set_exception( + RuntimeError("Child exited without calling task_status.started()") + ) + + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + kwargs = {} + if task_status_future: + parent_id = id(current_task()) + kwargs["task_status"] = _AsyncioTaskStatus( + task_status_future, id(self.cancel_scope._host_task) + ) + else: + parent_id = id(self.cancel_scope._host_task) + + coro = func(*args, **kwargs) + if not iscoroutine(coro): + prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" + raise TypeError( + f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " + f"the return value ({coro!r}) is not a coroutine object" + ) + + name = get_callable_name(func) if name is None else str(name) + task = create_task(coro, name=name) + task.add_done_callback(task_done) + + # Make the spawned task inherit the task group's cancel scope + _task_states[task] = TaskState( + parent_id=parent_id, cancel_scope=self.cancel_scope + ) + self.cancel_scope._tasks.add(task) + self._tasks.add(task) + return task + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + self._spawn(func, args, name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + future: asyncio.Future = asyncio.Future() + task = self._spawn(func, args, name, future) + + # If the task raises an exception after sending a start value without a switch + # point between, the task group is cancelled and this method never proceeds to + # process the completed future. That's why we have to have a shielded cancel + # scope here. + try: + return await future + except CancelledError: + # Cancel the task and wait for it to exit before returning + task.cancel() + with CancelScope(shield=True), suppress(CancelledError): + await task + + raise + + +# +# Threads +# + +_Retval_Queue_Type = Tuple[Optional[T_Retval], Optional[BaseException]] + + +class WorkerThread(Thread): + MAX_IDLE_TIME = 10 # seconds + + def __init__( + self, + root_task: asyncio.Task, + workers: set[WorkerThread], + idle_workers: deque[WorkerThread], + ): + super().__init__(name="AnyIO worker thread") + self.root_task = root_task + self.workers = workers + self.idle_workers = idle_workers + self.loop = root_task._loop + self.queue: Queue[ + tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None + ] = Queue(2) + self.idle_since = AsyncIOBackend.current_time() + self.stopping = False + + def _report_result( + self, future: asyncio.Future, result: Any, exc: BaseException | None + ) -> None: + self.idle_since = AsyncIOBackend.current_time() + if not self.stopping: + self.idle_workers.append(self) + + if not future.cancelled(): + if exc is not None: + if isinstance(exc, StopIteration): + new_exc = RuntimeError("coroutine raised StopIteration") + new_exc.__cause__ = exc + exc = new_exc + + future.set_exception(exc) + else: + future.set_result(result) + + def run(self) -> None: + with claim_worker_thread(AsyncIOBackend, self.loop): + while True: + item = self.queue.get() + if item is None: + # Shutdown command received + return + + context, func, args, future, cancel_scope = item + if not future.cancelled(): + result = None + exception: BaseException | None = None + threadlocals.current_cancel_scope = cancel_scope + try: + result = context.run(func, *args) + except BaseException as exc: + exception = exc + finally: + del threadlocals.current_cancel_scope + + if not self.loop.is_closed(): + self.loop.call_soon_threadsafe( + self._report_result, future, result, exception + ) + + self.queue.task_done() + + def stop(self, f: asyncio.Task | None = None) -> None: + self.stopping = True + self.queue.put_nowait(None) + self.workers.discard(self) + try: + self.idle_workers.remove(self) + except ValueError: + pass + + +_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( + "_threadpool_idle_workers" +) +_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") + + +class BlockingPortal(abc.BlockingPortal): + def __new__(cls) -> BlockingPortal: + return object.__new__(cls) + + def __init__(self) -> None: + super().__init__() + self._loop = get_running_loop() + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + AsyncIOBackend.run_sync_from_thread( + partial(self._task_group.start_soon, name=name), + (self._call_func, func, args, kwargs, future), + self._loop, + ) + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class StreamReaderWrapper(abc.ByteReceiveStream): + _stream: asyncio.StreamReader + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._stream.read(max_bytes) + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + self._stream.feed_eof() + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class StreamWriterWrapper(abc.ByteSendStream): + _stream: asyncio.StreamWriter + + async def send(self, item: bytes) -> None: + self._stream.write(item) + await self._stream.drain() + + async def aclose(self) -> None: + self._stream.close() + await AsyncIOBackend.checkpoint() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: asyncio.subprocess.Process + _stdin: StreamWriterWrapper | None + _stdout: StreamReaderWrapper | None + _stderr: StreamReaderWrapper | None + + async def aclose(self) -> None: + with CancelScope(shield=True): + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): + await self.wait() + + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: int) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +def _forcibly_shutdown_process_pool_on_exit( + workers: set[Process], _task: object +) -> None: + """ + Forcibly shuts down worker processes belonging to this event loop.""" + child_watcher: asyncio.AbstractChildWatcher | None = None + if sys.version_info < (3, 12): + try: + child_watcher = asyncio.get_event_loop_policy().get_child_watcher() + except NotImplementedError: + pass + + # Close as much as possible (w/o async/await) to avoid warnings + for process in workers: + if process.returncode is None: + continue + + process._stdin._stream._transport.close() # type: ignore[union-attr] + process._stdout._stream._transport.close() # type: ignore[union-attr] + process._stderr._stream._transport.close() # type: ignore[union-attr] + process.kill() + if child_watcher: + child_watcher.remove_child_handler(process.pid) + + +async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: + """ + Shuts down worker processes belonging to this event loop. + + NOTE: this only works when the event loop was started using asyncio.run() or + anyio.run(). + + """ + process: abc.Process + try: + await sleep(math.inf) + except asyncio.CancelledError: + for process in workers: + if process.returncode is None: + process.kill() + + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class StreamProtocol(asyncio.Protocol): + read_queue: deque[bytes] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque() + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + cast(asyncio.Transport, transport).set_write_buffer_limits(0) + + def connection_lost(self, exc: Exception | None) -> None: + if exc: + self.exception = BrokenResourceError() + self.exception.__cause__ = exc + + self.read_event.set() + self.write_event.set() + + def data_received(self, data: bytes) -> None: + self.read_queue.append(data) + self.read_event.set() + + def eof_received(self) -> bool | None: + self.read_event.set() + return True + + def pause_writing(self) -> None: + self.write_event = asyncio.Event() + + def resume_writing(self) -> None: + self.write_event.set() + + +class DatagramProtocol(asyncio.DatagramProtocol): + read_queue: deque[tuple[bytes, IPSockAddrType]] + read_event: asyncio.Event + write_event: asyncio.Event + exception: Exception | None = None + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + self.read_queue = deque(maxlen=100) # arbitrary value + self.read_event = asyncio.Event() + self.write_event = asyncio.Event() + self.write_event.set() + + def connection_lost(self, exc: Exception | None) -> None: + self.read_event.set() + self.write_event.set() + + def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: + addr = convert_ipv6_sockaddr(addr) + self.read_queue.append((data, addr)) + self.read_event.set() + + def error_received(self, exc: Exception) -> None: + self.exception = exc + + def pause_writing(self) -> None: + self.write_event.clear() + + def resume_writing(self) -> None: + self.write_event.set() + + +class SocketStream(abc.SocketStream): + def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + if ( + not self._protocol.read_event.is_set() + and not self._transport.is_closing() + ): + self._transport.resume_reading() + await self._protocol.read_event.wait() + self._transport.pause_reading() + + try: + chunk = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + elif self._protocol.exception: + raise self._protocol.exception from None + else: + raise EndOfStream from None + + if len(chunk) > max_bytes: + # Split the oversized chunk + chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] + self._protocol.read_queue.appendleft(leftover) + + # If the read queue is empty, clear the flag so that the next call will + # block until data is available + if not self._protocol.read_queue: + self._protocol.read_event.clear() + + return chunk + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + + if self._closed: + raise ClosedResourceError + elif self._protocol.exception is not None: + raise self._protocol.exception + + try: + self._transport.write(item) + except RuntimeError as exc: + if self._transport.is_closing(): + raise BrokenResourceError from exc + else: + raise + + await self._protocol.write_event.wait() + + async def send_eof(self) -> None: + try: + self._transport.write_eof() + except OSError: + pass + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + try: + self._transport.write_eof() + except OSError: + pass + + self._transport.close() + await sleep(0) + self._transport.abort() + + +class _RawSocketMixin: + _receive_future: asyncio.Future | None = None + _send_future: asyncio.Future | None = None + _closing = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._receive_future + loop.remove_reader(self.__raw_socket) + + f = self._receive_future = asyncio.Future() + loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: + def callback(f: object) -> None: + del self._send_future + loop.remove_writer(self.__raw_socket) + + f = self._send_future = asyncio.Future() + loop.add_writer(self.__raw_socket, f.set_result, None) + f.add_done_callback(callback) + return f + + async def aclose(self) -> None: + if not self._closing: + self._closing = True + if self.__raw_socket.fileno() != -1: + self.__raw_socket.close() + + if self._receive_future: + self._receive_future.set_result(None) + if self._send_future: + self._send_future.set_result(None) + + +class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): + async def send_eof(self) -> None: + with self._send_guard: + self._raw_socket.shutdown(socket.SHUT_WR) + + async def receive(self, max_bytes: int = 65536) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(max_bytes) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = self._raw_socket.send(view) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + view = view[bytes_sent:] + + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + loop = get_running_loop() + fds = array.array("i") + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = self._raw_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + loop = get_running_loop() + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + # The ignore can be removed after mypy picks up + # https://github.com/python/typeshed/pull/5545 + self._raw_socket.sendmsg( + [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] + ) + break + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + +class TCPSocketListener(abc.SocketListener): + _accept_scope: CancelScope | None = None + _closed = False + + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) + self._accept_guard = ResourceGuard("accepting connections from") + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + async def accept(self) -> abc.SocketStream: + if self._closed: + raise ClosedResourceError + + with self._accept_guard: + await AsyncIOBackend.checkpoint() + with CancelScope() as self._accept_scope: + try: + client_sock, _addr = await self._loop.sock_accept(self._raw_socket) + except asyncio.CancelledError: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + if self._closed: + raise ClosedResourceError from None + + raise + finally: + self._accept_scope = None + + client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + transport, protocol = await self._loop.connect_accepted_socket( + StreamProtocol, client_sock + ) + return SocketStream(transport, protocol) + + async def aclose(self) -> None: + if self._closed: + return + + self._closed = True + if self._accept_scope: + # Workaround for https://bugs.python.org/issue41317 + try: + self._loop.remove_reader(self._raw_socket) + except (ValueError, NotImplementedError): + pass + + self._accept_scope.cancel() + await sleep(0) + + self._raw_socket.close() + + +class UNIXSocketListener(abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + self.__raw_socket = raw_socket + self._loop = get_running_loop() + self._accept_guard = ResourceGuard("accepting connections from") + self._closed = False + + async def accept(self) -> abc.SocketStream: + await AsyncIOBackend.checkpoint() + with self._accept_guard: + while True: + try: + client_sock, _ = self.__raw_socket.accept() + client_sock.setblocking(False) + return UNIXSocketStream(client_sock) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + self._loop.add_reader(self.__raw_socket, f.set_result, None) + f.add_done_callback( + lambda _: self._loop.remove_reader(self.__raw_socket) + ) + await f + except OSError as exc: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + + async def aclose(self) -> None: + self._closed = True + self.__raw_socket.close() + + @property + def _raw_socket(self) -> socket.socket: + return self.__raw_socket + + +class UDPSocket(abc.UDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + self._transport.close() + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + return self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(*item) + + +class ConnectedUDPSocket(abc.ConnectedUDPSocket): + def __init__( + self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol + ): + self._transport = transport + self._protocol = protocol + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + self._closed = False + + @property + def _raw_socket(self) -> socket.socket: + return self._transport.get_extra_info("socket") + + async def aclose(self) -> None: + if not self._transport.is_closing(): + self._closed = True + self._transport.close() + + async def receive(self) -> bytes: + with self._receive_guard: + await AsyncIOBackend.checkpoint() + + # If the buffer is empty, ask for more data + if not self._protocol.read_queue and not self._transport.is_closing(): + self._protocol.read_event.clear() + await self._protocol.read_event.wait() + + try: + packet = self._protocol.read_queue.popleft() + except IndexError: + if self._closed: + raise ClosedResourceError from None + else: + raise BrokenResourceError from None + + return packet[0] + + async def send(self, item: bytes) -> None: + with self._send_guard: + await AsyncIOBackend.checkpoint() + await self._protocol.write_event.wait() + if self._closed: + raise ClosedResourceError + elif self._transport.is_closing(): + raise BrokenResourceError + else: + self._transport.sendto(item) + + +class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): + async def receive(self) -> UNIXDatagramPacketType: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recvfrom(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: UNIXDatagramPacketType) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.sendto(*item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): + async def receive(self) -> bytes: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._receive_guard: + while True: + try: + data = self._raw_socket.recv(65536) + except BlockingIOError: + await self._wait_until_readable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return data + + async def send(self, item: bytes) -> None: + loop = get_running_loop() + await AsyncIOBackend.checkpoint() + with self._send_guard: + while True: + try: + self._raw_socket.send(item) + except BlockingIOError: + await self._wait_until_writable(loop) + except OSError as exc: + if self._closing: + raise ClosedResourceError from None + else: + raise BrokenResourceError from exc + else: + return + + +_read_events: RunVar[dict[Any, asyncio.Event]] = RunVar("read_events") +_write_events: RunVar[dict[Any, asyncio.Event]] = RunVar("write_events") + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self._event = asyncio.Event() + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._event.is_set() + + async def wait(self) -> None: + if self.is_set(): + await AsyncIOBackend.checkpoint() + else: + await self._event.wait() + + def statistics(self) -> EventStatistics: + return EventStatistics(len(self._event._waiters)) # type: ignore[attr-defined] + + +class CapacityLimiter(BaseCapacityLimiter): + _total_tokens: float = 0 + + def __new__(cls, total_tokens: float) -> CapacityLimiter: + return object.__new__(cls) + + def __init__(self, total_tokens: float): + self._borrowers: set[Any] = set() + self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() + self.total_tokens = total_tokens + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + @property + def total_tokens(self) -> float: + return self._total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and not math.isinf(value): + raise TypeError("total_tokens must be an int or math.inf") + if value < 1: + raise ValueError("total_tokens must be >= 1") + + waiters_to_notify = max(value - self._total_tokens, 0) + self._total_tokens = value + + # Notify waiting tasks that they have acquired the limiter + while self._wait_queue and waiters_to_notify: + event = self._wait_queue.popitem(last=False)[1] + event.set() + waiters_to_notify -= 1 + + @property + def borrowed_tokens(self) -> int: + return len(self._borrowers) + + @property + def available_tokens(self) -> float: + return self._total_tokens - len(self._borrowers) + + def acquire_nowait(self) -> None: + self.acquire_on_behalf_of_nowait(current_task()) + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + if borrower in self._borrowers: + raise RuntimeError( + "this borrower is already holding one of this CapacityLimiter's " + "tokens" + ) + + if self._wait_queue or len(self._borrowers) >= self._total_tokens: + raise WouldBlock + + self._borrowers.add(borrower) + + async def acquire(self) -> None: + return await self.acquire_on_behalf_of(current_task()) + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + try: + self.acquire_on_behalf_of_nowait(borrower) + except WouldBlock: + event = asyncio.Event() + self._wait_queue[borrower] = event + try: + await event.wait() + except BaseException: + self._wait_queue.pop(borrower, None) + raise + + self._borrowers.add(borrower) + else: + try: + await AsyncIOBackend.cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def release(self) -> None: + self.release_on_behalf_of(current_task()) + + def release_on_behalf_of(self, borrower: object) -> None: + try: + self._borrowers.remove(borrower) + except KeyError: + raise RuntimeError( + "this borrower isn't holding any of this CapacityLimiter's " "tokens" + ) from None + + # Notify the next task in line if this limiter has free capacity now + if self._wait_queue and len(self._borrowers) < self._total_tokens: + event = self._wait_queue.popitem(last=False)[1] + event.set() + + def statistics(self) -> CapacityLimiterStatistics: + return CapacityLimiterStatistics( + self.borrowed_tokens, + self.total_tokens, + tuple(self._borrowers), + len(self._wait_queue), + ) + + +_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") + + +# +# Operating system signals +# + + +class _SignalReceiver: + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + self._loop = get_running_loop() + self._signal_queue: deque[Signals] = deque() + self._future: asyncio.Future = asyncio.Future() + self._handled_signals: set[Signals] = set() + + def _deliver(self, signum: Signals) -> None: + self._signal_queue.append(signum) + if not self._future.done(): + self._future.set_result(None) + + def __enter__(self) -> _SignalReceiver: + for sig in set(self._signals): + self._loop.add_signal_handler(sig, self._deliver, sig) + self._handled_signals.add(sig) + + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + for sig in self._handled_signals: + self._loop.remove_signal_handler(sig) + return None + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + await AsyncIOBackend.checkpoint() + if not self._signal_queue: + self._future = asyncio.Future() + await self._future + + return self._signal_queue.popleft() + + +# +# Testing and debugging +# + + +def _create_task_info(task: asyncio.Task) -> TaskInfo: + task_state = _task_states.get(task) + if task_state is None: + parent_id = None + else: + parent_id = task_state.parent_id + + return TaskInfo(id(task), parent_id, task.get_name(), task.get_coro()) + + +class TestRunner(abc.TestRunner): + _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] + + def __init__( + self, + *, + debug: bool | None = None, + use_uvloop: bool = False, + loop_factory: Callable[[], AbstractEventLoop] | None = None, + ) -> None: + if use_uvloop and loop_factory is None: + import uvloop + + loop_factory = uvloop.new_event_loop + + self._runner = Runner(debug=debug, loop_factory=loop_factory) + self._exceptions: list[BaseException] = [] + self._runner_task: asyncio.Task | None = None + + def __enter__(self) -> TestRunner: + self._runner.__enter__() + self.get_loop().set_exception_handler(self._exception_handler) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._runner.__exit__(exc_type, exc_val, exc_tb) + + def get_loop(self) -> AbstractEventLoop: + return self._runner.get_loop() + + def _exception_handler( + self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] + ) -> None: + if isinstance(context.get("exception"), Exception): + self._exceptions.append(context["exception"]) + else: + loop.default_exception_handler(context) + + def _raise_async_exceptions(self) -> None: + # Re-raise any exceptions raised in asynchronous callbacks + if self._exceptions: + exceptions, self._exceptions = self._exceptions, [] + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup( + "Multiple exceptions occurred in asynchronous callbacks", exceptions + ) + + @staticmethod + async def _run_tests_and_fixtures( + receive_stream: MemoryObjectReceiveStream[ + tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] + ], + ) -> None: + with receive_stream: + async for coro, future in receive_stream: + try: + retval = await coro + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + else: + if not future.cancelled(): + future.set_result(retval) + + async def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if not self._runner_task: + self._send_stream, receive_stream = create_memory_object_stream[ + Tuple[Awaitable[Any], asyncio.Future] + ](1) + self._runner_task = self.get_loop().create_task( + self._run_tests_and_fixtures(receive_stream) + ) + + coro = func(*args, **kwargs) + future: asyncio.Future[T_Retval] = self.get_loop().create_future() + self._send_stream.send_nowait((coro, future)) + return await future + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + self._raise_async_exceptions() + + yield fixturevalue + + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(asyncgen.asend, None) + ) + except StopAsyncIteration: + self._raise_async_exceptions() + else: + self.get_loop().run_until_complete(asyncgen.aclose()) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + retval = self.get_loop().run_until_complete( + self._call_in_runner_task(fixture_func, **kwargs) + ) + self._raise_async_exceptions() + return retval + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + try: + self.get_loop().run_until_complete( + self._call_in_runner_task(test_func, **kwargs) + ) + except Exception as exc: + self._exceptions.append(exc) + + self._raise_async_exceptions() + + +class AsyncIOBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + @wraps(func) + async def wrapper() -> T_Retval: + task = cast(asyncio.Task, current_task()) + task.set_name(get_callable_name(func)) + _task_states[task] = TaskState(None, None) + + try: + return await func(*args) + finally: + del _task_states[task] + + debug = options.get("debug", False) + loop_factory = options.get("loop_factory", None) + if loop_factory is None and options.get("use_uvloop", False): + import uvloop + + loop_factory = uvloop.new_event_loop + + with Runner(debug=debug, loop_factory=loop_factory) as runner: + return runner.run(wrapper()) + + @classmethod + def current_token(cls) -> object: + return get_running_loop() + + @classmethod + def current_time(cls) -> float: + return get_running_loop().time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return CancelledError + + @classmethod + async def checkpoint(cls) -> None: + await sleep(0) + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + task = current_task() + if task is None: + return + + try: + cancel_scope = _task_states[task].cancel_scope + except KeyError: + return + + while cancel_scope: + if cancel_scope.cancel_called: + await sleep(0) + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + with CancelScope(shield=True): + await sleep(0) + + @classmethod + async def sleep(cls, delay: float) -> None: + await sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + try: + cancel_scope = _task_states[ + current_task() # type: ignore[index] + ].cancel_scope + except KeyError: + return math.inf + + deadline = math.inf + while cancel_scope: + deadline = min(deadline, cancel_scope.deadline) + if cancel_scope._cancel_called: + deadline = -math.inf + break + elif cancel_scope.shield: + break + else: + cancel_scope = cancel_scope._parent_scope + + return deadline + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> abc.CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + await cls.checkpoint() + + # If this is the first run in this event loop thread, set up the necessary + # variables + try: + idle_workers = _threadpool_idle_workers.get() + workers = _threadpool_workers.get() + except LookupError: + idle_workers = deque() + workers = set() + _threadpool_idle_workers.set(idle_workers) + _threadpool_workers.set(workers) + + async with limiter or cls.current_default_thread_limiter(): + with CancelScope(shield=not abandon_on_cancel) as scope: + future: asyncio.Future = asyncio.Future() + root_task = find_root_task() + if not idle_workers: + worker = WorkerThread(root_task, workers, idle_workers) + worker.start() + workers.add(worker) + root_task.add_done_callback(worker.stop) + else: + worker = idle_workers.pop() + + # Prune any other workers that have been idle for MAX_IDLE_TIME + # seconds or longer + now = cls.current_time() + while idle_workers: + if ( + now - idle_workers[0].idle_since + < WorkerThread.MAX_IDLE_TIME + ): + break + + expired_worker = idle_workers.popleft() + expired_worker.root_task.remove_done_callback( + expired_worker.stop + ) + expired_worker.stop() + + context = copy_context() + context.run(sniffio.current_async_library_cvar.set, None) + if abandon_on_cancel or scope._parent_scope is None: + worker_scope = scope + else: + worker_scope = scope._parent_scope + + worker.queue.put_nowait((context, func, args, future, worker_scope)) + return await future + + @classmethod + def check_cancelled(cls) -> None: + scope: CancelScope | None = threadlocals.current_cancel_scope + while scope is not None: + if scope.cancel_called: + raise CancelledError(f"Cancelled by cancel scope {id(scope):x}") + + if scope.shield: + return + + scope = scope._parent_scope + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + async def task_wrapper(scope: CancelScope) -> T_Retval: + __tracebackhide__ = True + task = cast(asyncio.Task, current_task()) + _task_states[task] = TaskState(None, scope) + scope._tasks.add(task) + try: + return await func(*args) + except CancelledError as exc: + raise concurrent.futures.CancelledError(str(exc)) from None + finally: + scope._tasks.discard(task) + + loop = cast(AbstractEventLoop, token) + context = copy_context() + context.run(sniffio.current_async_library_cvar.set, "asyncio") + wrapper = task_wrapper(threadlocals.current_cancel_scope) + f: concurrent.futures.Future[T_Retval] = context.run( + asyncio.run_coroutine_threadsafe, wrapper, loop + ) + return f.result() + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + @wraps(func) + def wrapper() -> None: + try: + sniffio.current_async_library_cvar.set("asyncio") + f.set_result(func(*args)) + except BaseException as exc: + f.set_exception(exc) + if not isinstance(exc, Exception): + raise + + f: concurrent.futures.Future[T_Retval] = Future() + loop = cast(AbstractEventLoop, token) + loop.call_soon_threadsafe(wrapper) + return f.result() + + @classmethod + def create_blocking_portal(cls) -> abc.BlockingPortal: + return BlockingPortal() + + @classmethod + async def open_process( + cls, + command: str | bytes | Sequence[str | bytes], + *, + shell: bool, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: str | bytes | PathLike | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, + ) -> Process: + await cls.checkpoint() + if shell: + process = await asyncio.create_subprocess_shell( + cast("str | bytes", command), + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) + else: + process = await asyncio.create_subprocess_exec( + *command, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) + + stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None + stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None + stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + create_task( + _shutdown_process_pool_on_exit(workers), + name="AnyIO process pool shutdown task", + ) + find_root_task().add_done_callback( + partial(_forcibly_shutdown_process_pool_on_exit, workers) + ) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> abc.SocketStream: + transport, protocol = cast( + Tuple[asyncio.Transport, StreamProtocol], + await get_running_loop().create_connection( + StreamProtocol, host, port, local_addr=local_address + ), + ) + transport.pause_reading() + return SocketStream(transport, protocol) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + await cls.checkpoint() + loop = get_running_loop() + raw_socket = socket.socket(socket.AF_UNIX) + raw_socket.setblocking(False) + while True: + try: + raw_socket.connect(path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return UNIXSocketStream(raw_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, + local_addr=local_address, + remote_addr=remote_address, + family=family, + reuse_port=reuse_port, + ) + if protocol.exception: + transport.close() + raise protocol.exception + + if not remote_address: + return UDPSocket(transport, protocol) + else: + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def create_unix_datagram_socket( # type: ignore[override] + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + await cls.checkpoint() + loop = get_running_loop() + + if remote_path: + while True: + try: + raw_socket.connect(remote_path) + except BlockingIOError: + f: asyncio.Future = asyncio.Future() + loop.add_writer(raw_socket, f.set_result, None) + f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) + await f + except BaseException: + raw_socket.close() + raise + else: + return ConnectedUNIXDatagramSocket(raw_socket) + else: + return UNIXDatagramSocket(raw_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + return await get_running_loop().getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags + ) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await get_running_loop().getnameinfo(sockaddr, flags) + + @classmethod + async def wait_socket_readable(cls, sock: socket.socket) -> None: + await cls.checkpoint() + try: + read_events = _read_events.get() + except LookupError: + read_events = {} + _read_events.set(read_events) + + if read_events.get(sock): + raise BusyResourceError("reading from") from None + + loop = get_running_loop() + event = read_events[sock] = asyncio.Event() + loop.add_reader(sock, event.set) + try: + await event.wait() + finally: + if read_events.pop(sock, None) is not None: + loop.remove_reader(sock) + readable = True + else: + readable = False + + if not readable: + raise ClosedResourceError + + @classmethod + async def wait_socket_writable(cls, sock: socket.socket) -> None: + await cls.checkpoint() + try: + write_events = _write_events.get() + except LookupError: + write_events = {} + _write_events.set(write_events) + + if write_events.get(sock): + raise BusyResourceError("writing to") from None + + loop = get_running_loop() + event = write_events[sock] = asyncio.Event() + loop.add_writer(sock.fileno(), event.set) + try: + await event.wait() + finally: + if write_events.pop(sock, None) is not None: + loop.remove_writer(sock) + writable = True + else: + writable = False + + if not writable: + raise ClosedResourceError + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _default_thread_limiter.get() + except LookupError: + limiter = CapacityLimiter(40) + _default_thread_limiter.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> ContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + return _create_task_info(current_task()) # type: ignore[arg-type] + + @classmethod + def get_running_tasks(cls) -> list[TaskInfo]: + return [_create_task_info(task) for task in all_tasks() if not task.done()] + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + await cls.checkpoint() + this_task = current_task() + while True: + for task in all_tasks(): + if task is this_task: + continue + + waiter = task._fut_waiter # type: ignore[attr-defined] + if waiter is None or waiter.done(): + await sleep(0.1) + break + else: + return + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = AsyncIOBackend diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_trio.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_trio.py new file mode 100644 index 0000000..1a47192 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_backends/_trio.py @@ -0,0 +1,1169 @@ +from __future__ import annotations + +import array +import math +import socket +import sys +import types +from collections.abc import AsyncIterator, Iterable +from concurrent.futures import Future +from dataclasses import dataclass +from functools import partial +from io import IOBase +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind +from types import TracebackType +from typing import ( + IO, + Any, + AsyncGenerator, + Awaitable, + Callable, + Collection, + ContextManager, + Coroutine, + Generic, + Mapping, + NoReturn, + Sequence, + TypeVar, + cast, + overload, +) + +import trio.from_thread +import trio.lowlevel +from outcome import Error, Outcome, Value +from trio.lowlevel import ( + current_root_task, + current_task, + wait_readable, + wait_writable, +) +from trio.socket import SocketType as TrioSocketType +from trio.to_thread import run_sync + +from .. import CapacityLimiterStatistics, EventStatistics, TaskInfo, abc +from .._core._eventloop import claim_worker_thread +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, +) +from .._core._sockets import convert_ipv6_sockaddr +from .._core._streams import create_memory_object_stream +from .._core._synchronization import CapacityLimiter as BaseCapacityLimiter +from .._core._synchronization import Event as BaseEvent +from .._core._synchronization import ResourceGuard +from .._core._tasks import CancelScope as BaseCancelScope +from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType +from ..abc._eventloop import AsyncBackend +from ..streams.memory import MemoryObjectSendStream + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from exceptiongroup import BaseExceptionGroup + from typing_extensions import TypeVarTuple, Unpack + +T = TypeVar("T") +T_Retval = TypeVar("T_Retval") +T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) +PosArgsT = TypeVarTuple("PosArgsT") +P = ParamSpec("P") + + +# +# Event loop +# + +RunVar = trio.lowlevel.RunVar + + +# +# Timeouts and cancellation +# + + +class CancelScope(BaseCancelScope): + def __new__( + cls, original: trio.CancelScope | None = None, **kwargs: object + ) -> CancelScope: + return object.__new__(cls) + + def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: + self.__original = original or trio.CancelScope(**kwargs) + + def __enter__(self) -> CancelScope: + self.__original.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + # https://github.com/python-trio/trio-typing/pull/79 + return self.__original.__exit__(exc_type, exc_val, exc_tb) + + def cancel(self) -> None: + self.__original.cancel() + + @property + def deadline(self) -> float: + return self.__original.deadline + + @deadline.setter + def deadline(self, value: float) -> None: + self.__original.deadline = value + + @property + def cancel_called(self) -> bool: + return self.__original.cancel_called + + @property + def cancelled_caught(self) -> bool: + return self.__original.cancelled_caught + + @property + def shield(self) -> bool: + return self.__original.shield + + @shield.setter + def shield(self, value: bool) -> None: + self.__original.shield = value + + +# +# Task groups +# + + +class TaskGroup(abc.TaskGroup): + def __init__(self) -> None: + self._active = False + self._nursery_manager = trio.open_nursery(strict_exception_groups=True) + self.cancel_scope = None # type: ignore[assignment] + + async def __aenter__(self) -> TaskGroup: + self._active = True + self._nursery = await self._nursery_manager.__aenter__() + self.cancel_scope = CancelScope(self._nursery.cancel_scope) + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + try: + return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) + except BaseExceptionGroup as exc: + _, rest = exc.split(trio.Cancelled) + if not rest: + cancelled_exc = trio.Cancelled._create() + raise cancelled_exc from exc + + raise + finally: + self._active = False + + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + self._nursery.start_soon(func, *args, name=name) + + async def start( + self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None + ) -> Any: + if not self._active: + raise RuntimeError( + "This task group is not active; no new tasks can be started." + ) + + return await self._nursery.start(func, *args, name=name) + + +# +# Threads +# + + +class BlockingPortal(abc.BlockingPortal): + def __new__(cls) -> BlockingPortal: + return object.__new__(cls) + + def __init__(self) -> None: + super().__init__() + self._token = trio.lowlevel.current_trio_token() + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + trio.from_thread.run_sync( + partial(self._task_group.start_soon, name=name), + self._call_func, + func, + args, + kwargs, + future, + trio_token=self._token, + ) + + +# +# Subprocesses +# + + +@dataclass(eq=False) +class ReceiveStreamWrapper(abc.ByteReceiveStream): + _stream: trio.abc.ReceiveStream + + async def receive(self, max_bytes: int | None = None) -> bytes: + try: + data = await self._stream.receive_some(max_bytes) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + if data: + return data + else: + raise EndOfStream + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class SendStreamWrapper(abc.ByteSendStream): + _stream: trio.abc.SendStream + + async def send(self, item: bytes) -> None: + try: + await self._stream.send_all(item) + except trio.ClosedResourceError as exc: + raise ClosedResourceError from exc.__cause__ + except trio.BrokenResourceError as exc: + raise BrokenResourceError from exc.__cause__ + + async def aclose(self) -> None: + await self._stream.aclose() + + +@dataclass(eq=False) +class Process(abc.Process): + _process: trio.Process + _stdin: abc.ByteSendStream | None + _stdout: abc.ByteReceiveStream | None + _stderr: abc.ByteReceiveStream | None + + async def aclose(self) -> None: + with CancelScope(shield=True): + if self._stdin: + await self._stdin.aclose() + if self._stdout: + await self._stdout.aclose() + if self._stderr: + await self._stderr.aclose() + + try: + await self.wait() + except BaseException: + self.kill() + with CancelScope(shield=True): + await self.wait() + raise + + async def wait(self) -> int: + return await self._process.wait() + + def terminate(self) -> None: + self._process.terminate() + + def kill(self) -> None: + self._process.kill() + + def send_signal(self, signal: Signals) -> None: + self._process.send_signal(signal) + + @property + def pid(self) -> int: + return self._process.pid + + @property + def returncode(self) -> int | None: + return self._process.returncode + + @property + def stdin(self) -> abc.ByteSendStream | None: + return self._stdin + + @property + def stdout(self) -> abc.ByteReceiveStream | None: + return self._stdout + + @property + def stderr(self) -> abc.ByteReceiveStream | None: + return self._stderr + + +class _ProcessPoolShutdownInstrument(trio.abc.Instrument): + def after_run(self) -> None: + super().after_run() + + +current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( + "current_default_worker_process_limiter" +) + + +async def _shutdown_process_pool(workers: set[abc.Process]) -> None: + try: + await trio.sleep(math.inf) + except trio.Cancelled: + for process in workers: + if process.returncode is None: + process.kill() + + with CancelScope(shield=True): + for process in workers: + await process.aclose() + + +# +# Sockets and networking +# + + +class _TrioSocketMixin(Generic[T_SockAddr]): + def __init__(self, trio_socket: TrioSocketType) -> None: + self._trio_socket = trio_socket + self._closed = False + + def _check_closed(self) -> None: + if self._closed: + raise ClosedResourceError + if self._trio_socket.fileno() < 0: + raise BrokenResourceError + + @property + def _raw_socket(self) -> socket.socket: + return self._trio_socket._sock # type: ignore[attr-defined] + + async def aclose(self) -> None: + if self._trio_socket.fileno() >= 0: + self._closed = True + self._trio_socket.close() + + def _convert_socket_error(self, exc: BaseException) -> NoReturn: + if isinstance(exc, trio.ClosedResourceError): + raise ClosedResourceError from exc + elif self._trio_socket.fileno() < 0 and self._closed: + raise ClosedResourceError from None + elif isinstance(exc, OSError): + raise BrokenResourceError from exc + else: + raise exc + + +class SocketStream(_TrioSocketMixin, abc.SocketStream): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self, max_bytes: int = 65536) -> bytes: + with self._receive_guard: + try: + data = await self._trio_socket.recv(max_bytes) + except BaseException as exc: + self._convert_socket_error(exc) + + if data: + return data + else: + raise EndOfStream + + async def send(self, item: bytes) -> None: + with self._send_guard: + view = memoryview(item) + while view: + try: + bytes_sent = await self._trio_socket.send(view) + except BaseException as exc: + self._convert_socket_error(exc) + + view = view[bytes_sent:] + + async def send_eof(self) -> None: + self._trio_socket.shutdown(socket.SHUT_WR) + + +class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + if not isinstance(msglen, int) or msglen < 0: + raise ValueError("msglen must be a non-negative integer") + if not isinstance(maxfds, int) or maxfds < 1: + raise ValueError("maxfds must be a positive integer") + + fds = array.array("i") + await trio.lowlevel.checkpoint() + with self._receive_guard: + while True: + try: + message, ancdata, flags, addr = await self._trio_socket.recvmsg( + msglen, socket.CMSG_LEN(maxfds * fds.itemsize) + ) + except BaseException as exc: + self._convert_socket_error(exc) + else: + if not message and not ancdata: + raise EndOfStream + + break + + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: + raise RuntimeError( + f"Received unexpected ancillary data; message = {message!r}, " + f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" + ) + + fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + + return message, list(fds) + + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + if not message: + raise ValueError("message must not be empty") + if not fds: + raise ValueError("fds must not be empty") + + filenos: list[int] = [] + for fd in fds: + if isinstance(fd, int): + filenos.append(fd) + elif isinstance(fd, IOBase): + filenos.append(fd.fileno()) + + fdarray = array.array("i", filenos) + await trio.lowlevel.checkpoint() + with self._send_guard: + while True: + try: + await self._trio_socket.sendmsg( + [message], + [ + ( + socket.SOL_SOCKET, + socket.SCM_RIGHTS, + fdarray, + ) + ], + ) + break + except BaseException as exc: + self._convert_socket_error(exc) + + +class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> SocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SocketStream(trio_socket) + + +class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): + def __init__(self, raw_socket: socket.socket): + super().__init__(trio.socket.from_stdlib_socket(raw_socket)) + self._accept_guard = ResourceGuard("accepting connections from") + + async def accept(self) -> UNIXSocketStream: + with self._accept_guard: + try: + trio_socket, _addr = await self._trio_socket.accept() + except BaseException as exc: + self._convert_socket_error(exc) + + return UNIXSocketStream(trio_socket) + + +class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> tuple[bytes, IPSockAddrType]: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, convert_ipv6_sockaddr(addr) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UDPPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> UNIXDatagramPacketType: + with self._receive_guard: + try: + data, addr = await self._trio_socket.recvfrom(65536) + return data, addr + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: UNIXDatagramPacketType) -> None: + with self._send_guard: + try: + await self._trio_socket.sendto(*item) + except BaseException as exc: + self._convert_socket_error(exc) + + +class ConnectedUNIXDatagramSocket( + _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket +): + def __init__(self, trio_socket: TrioSocketType) -> None: + super().__init__(trio_socket) + self._receive_guard = ResourceGuard("reading from") + self._send_guard = ResourceGuard("writing to") + + async def receive(self) -> bytes: + with self._receive_guard: + try: + return await self._trio_socket.recv(65536) + except BaseException as exc: + self._convert_socket_error(exc) + + async def send(self, item: bytes) -> None: + with self._send_guard: + try: + await self._trio_socket.send(item) + except BaseException as exc: + self._convert_socket_error(exc) + + +# +# Synchronization +# + + +class Event(BaseEvent): + def __new__(cls) -> Event: + return object.__new__(cls) + + def __init__(self) -> None: + self.__original = trio.Event() + + def is_set(self) -> bool: + return self.__original.is_set() + + async def wait(self) -> None: + return await self.__original.wait() + + def statistics(self) -> EventStatistics: + orig_statistics = self.__original.statistics() + return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) + + def set(self) -> None: + self.__original.set() + + +class CapacityLimiter(BaseCapacityLimiter): + def __new__( + cls, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> CapacityLimiter: + return object.__new__(cls) + + def __init__( + self, + total_tokens: float | None = None, + *, + original: trio.CapacityLimiter | None = None, + ) -> None: + if original is not None: + self.__original = original + else: + assert total_tokens is not None + self.__original = trio.CapacityLimiter(total_tokens) + + async def __aenter__(self) -> None: + return await self.__original.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.__original.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + return self.__original.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + self.__original.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + return self.__original.borrowed_tokens + + @property + def available_tokens(self) -> float: + return self.__original.available_tokens + + def acquire_nowait(self) -> None: + self.__original.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self.__original.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self.__original.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self.__original.acquire_on_behalf_of(borrower) + + def release(self) -> None: + return self.__original.release() + + def release_on_behalf_of(self, borrower: object) -> None: + return self.__original.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + orig = self.__original.statistics() + return CapacityLimiterStatistics( + borrowed_tokens=orig.borrowed_tokens, + total_tokens=orig.total_tokens, + borrowers=tuple(orig.borrowers), + tasks_waiting=orig.tasks_waiting, + ) + + +_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") + + +# +# Signal handling +# + + +class _SignalReceiver: + _iterator: AsyncIterator[int] + + def __init__(self, signals: tuple[Signals, ...]): + self._signals = signals + + def __enter__(self) -> _SignalReceiver: + self._cm = trio.open_signal_receiver(*self._signals) + self._iterator = self._cm.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return self._cm.__exit__(exc_type, exc_val, exc_tb) + + def __aiter__(self) -> _SignalReceiver: + return self + + async def __anext__(self) -> Signals: + signum = await self._iterator.__anext__() + return Signals(signum) + + +# +# Testing and debugging +# + + +class TestRunner(abc.TestRunner): + def __init__(self, **options: Any) -> None: + from queue import Queue + + self._call_queue: Queue[Callable[[], object]] = Queue() + self._send_stream: MemoryObjectSendStream | None = None + self._options = options + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + if self._send_stream: + self._send_stream.close() + while self._send_stream is not None: + self._call_queue.get()() + + async def _run_tests_and_fixtures(self) -> None: + self._send_stream, receive_stream = create_memory_object_stream(1) + with receive_stream: + async for coro, outcome_holder in receive_stream: + try: + retval = await coro + except BaseException as exc: + outcome_holder.append(Error(exc)) + else: + outcome_holder.append(Value(retval)) + + def _main_task_finished(self, outcome: object) -> None: + self._send_stream = None + + def _call_in_runner_task( + self, + func: Callable[P, Awaitable[T_Retval]], + *args: P.args, + **kwargs: P.kwargs, + ) -> T_Retval: + if self._send_stream is None: + trio.lowlevel.start_guest_run( + self._run_tests_and_fixtures, + run_sync_soon_threadsafe=self._call_queue.put, + done_callback=self._main_task_finished, + **self._options, + ) + while self._send_stream is None: + self._call_queue.get()() + + outcome_holder: list[Outcome] = [] + self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) + while not outcome_holder: + self._call_queue.get()() + + return outcome_holder[0].unwrap() + + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], + kwargs: dict[str, Any], + ) -> Iterable[T_Retval]: + asyncgen = fixture_func(**kwargs) + fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) + + yield fixturevalue + + try: + self._call_in_runner_task(asyncgen.asend, None) + except StopAsyncIteration: + pass + else: + self._call_in_runner_task(asyncgen.aclose) + raise RuntimeError("Async generator fixture did not stop") + + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], + kwargs: dict[str, Any], + ) -> T_Retval: + return self._call_in_runner_task(fixture_func, **kwargs) + + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + self._call_in_runner_task(test_func, **kwargs) + + +class TrioBackend(AsyncBackend): + @classmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + return trio.run(func, *args) + + @classmethod + def current_token(cls) -> object: + return trio.lowlevel.current_trio_token() + + @classmethod + def current_time(cls) -> float: + return trio.current_time() + + @classmethod + def cancelled_exception_class(cls) -> type[BaseException]: + return trio.Cancelled + + @classmethod + async def checkpoint(cls) -> None: + await trio.lowlevel.checkpoint() + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + await trio.lowlevel.checkpoint_if_cancelled() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + await trio.lowlevel.cancel_shielded_checkpoint() + + @classmethod + async def sleep(cls, delay: float) -> None: + await trio.sleep(delay) + + @classmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> abc.CancelScope: + return CancelScope(deadline=deadline, shield=shield) + + @classmethod + def current_effective_deadline(cls) -> float: + return trio.current_effective_deadline() + + @classmethod + def create_task_group(cls) -> abc.TaskGroup: + return TaskGroup() + + @classmethod + def create_event(cls) -> abc.Event: + return Event() + + @classmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + return CapacityLimiter(total_tokens) + + @classmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: abc.CapacityLimiter | None = None, + ) -> T_Retval: + def wrapper() -> T_Retval: + with claim_worker_thread(TrioBackend, token): + return func(*args) + + token = TrioBackend.current_token() + return await run_sync( + wrapper, + abandon_on_cancel=abandon_on_cancel, + limiter=cast(trio.CapacityLimiter, limiter), + ) + + @classmethod + def check_cancelled(cls) -> None: + trio.from_thread.check_cancelled() + + @classmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + return trio.from_thread.run(func, *args) + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + return trio.from_thread.run_sync(func, *args) + + @classmethod + def create_blocking_portal(cls) -> abc.BlockingPortal: + return BlockingPortal() + + @classmethod + async def open_process( + cls, + command: str | bytes | Sequence[str | bytes], + *, + shell: bool, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: str | bytes | PathLike | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, + ) -> Process: + process = await trio.lowlevel.open_process( # type: ignore[misc] + command, # type: ignore[arg-type] + stdin=stdin, + stdout=stdout, + stderr=stderr, + shell=shell, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) + stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None + stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None + stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None + return Process(process, stdin_stream, stdout_stream, stderr_stream) + + @classmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: + trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) + + @classmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + family = socket.AF_INET6 if ":" in host else socket.AF_INET + trio_socket = trio.socket.socket(family) + trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + if local_address: + await trio_socket.bind(local_address) + + try: + await trio_socket.connect((host, port)) + except BaseException: + trio_socket.close() + raise + + return SocketStream(trio_socket) + + @classmethod + async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: + trio_socket = trio.socket.socket(socket.AF_UNIX) + try: + await trio_socket.connect(path) + except BaseException: + trio_socket.close() + raise + + return UNIXSocketStream(trio_socket) + + @classmethod + def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: + return UNIXSocketListener(sock) + + @classmethod + async def create_udp_socket( + cls, + family: socket.AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) + + if reuse_port: + trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + if local_address: + await trio_socket.bind(local_address) + + if remote_address: + await trio_socket.connect(remote_address) + return ConnectedUDPSocket(trio_socket) + else: + return UDPSocket(trio_socket) + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: None + ) -> abc.UNIXDatagramSocket: + ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes + ) -> abc.ConnectedUNIXDatagramSocket: + ... + + @classmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket.socket, remote_path: str | bytes | None + ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: + trio_socket = trio.socket.from_stdlib_socket(raw_socket) + + if remote_path: + await trio_socket.connect(remote_path) + return ConnectedUNIXDatagramSocket(trio_socket) + else: + return UNIXDatagramSocket(trio_socket) + + @classmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) + + @classmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + return await trio.socket.getnameinfo(sockaddr, flags) + + @classmethod + async def wait_socket_readable(cls, sock: socket.socket) -> None: + try: + await wait_readable(sock) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("reading from") from None + + @classmethod + async def wait_socket_writable(cls, sock: socket.socket) -> None: + try: + await wait_writable(sock) + except trio.ClosedResourceError as exc: + raise ClosedResourceError().with_traceback(exc.__traceback__) from None + except trio.BusyResourceError: + raise BusyResourceError("writing to") from None + + @classmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + try: + return _capacity_limiter_wrapper.get() + except LookupError: + limiter = CapacityLimiter( + original=trio.to_thread.current_default_thread_limiter() + ) + _capacity_limiter_wrapper.set(limiter) + return limiter + + @classmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> ContextManager[AsyncIterator[Signals]]: + return _SignalReceiver(signals) + + @classmethod + def get_current_task(cls) -> TaskInfo: + task = current_task() + + parent_id = None + if task.parent_nursery and task.parent_nursery.parent_task: + parent_id = id(task.parent_nursery.parent_task) + + return TaskInfo(id(task), parent_id, task.name, task.coro) + + @classmethod + def get_running_tasks(cls) -> list[TaskInfo]: + root_task = current_root_task() + assert root_task + task_infos = [TaskInfo(id(root_task), None, root_task.name, root_task.coro)] + nurseries = root_task.child_nurseries + while nurseries: + new_nurseries: list[trio.Nursery] = [] + for nursery in nurseries: + for task in nursery.child_tasks: + task_infos.append( + TaskInfo( + id(task), id(nursery.parent_task), task.name, task.coro + ) + ) + new_nurseries.extend(task.child_nurseries) + + nurseries = new_nurseries + + return task_infos + + @classmethod + async def wait_all_tasks_blocked(cls) -> None: + from trio.testing import wait_all_tasks_blocked + + await wait_all_tasks_blocked() + + @classmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + return TestRunner(**options) + + +backend_class = TrioBackend diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__init__.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9d4dc07 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_eventloop.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_eventloop.cpython-310.pyc new file mode 100644 index 0000000..758daf1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_eventloop.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_exceptions.cpython-310.pyc new file mode 100644 index 0000000..23afc69 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_fileio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_fileio.cpython-310.pyc new file mode 100644 index 0000000..eaec7d6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_fileio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_resources.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_resources.cpython-310.pyc new file mode 100644 index 0000000..bfae722 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_resources.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_signals.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_signals.cpython-310.pyc new file mode 100644 index 0000000..07ad434 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_signals.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_sockets.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_sockets.cpython-310.pyc new file mode 100644 index 0000000..9bfe049 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_sockets.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_streams.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_streams.cpython-310.pyc new file mode 100644 index 0000000..a9c0f5a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_streams.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-310.pyc new file mode 100644 index 0000000..8105eb2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_subprocesses.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_synchronization.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_synchronization.cpython-310.pyc new file mode 100644 index 0000000..1804f43 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_synchronization.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_tasks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_tasks.cpython-310.pyc new file mode 100644 index 0000000..b0fb586 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_tasks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_testing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_testing.cpython-310.pyc new file mode 100644 index 0000000..61af184 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_testing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_typedattr.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_typedattr.cpython-310.pyc new file mode 100644 index 0000000..b91d023 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/__pycache__/_typedattr.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_eventloop.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_eventloop.py new file mode 100644 index 0000000..a9c6e82 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_eventloop.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import math +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from contextlib import contextmanager +from importlib import import_module +from typing import TYPE_CHECKING, Any, TypeVar + +import sniffio + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from ..abc import AsyncBackend + +# This must be updated when new backends are introduced +BACKENDS = "asyncio", "trio" + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +threadlocals = threading.local() + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + backend: str = "asyncio", + backend_options: dict[str, Any] | None = None, +) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param backend: name of the asynchronous event loop implementation – currently + either ``asyncio`` or ``trio`` + :param backend_options: keyword arguments to call the backend ``run()`` + implementation with (documented :ref:`here `) + :return: the return value of the coroutine function + :raises RuntimeError: if an asynchronous event loop is already running in this + thread + :raises LookupError: if the named backend is not found + + """ + try: + asynclib_name = sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + pass + else: + raise RuntimeError(f"Already running {asynclib_name} in this thread") + + try: + async_backend = get_async_backend(backend) + except ImportError as exc: + raise LookupError(f"No such backend: {backend}") from exc + + token = None + if sniffio.current_async_library_cvar.get(None) is None: + # Since we're in control of the event loop, we can cache the name of the async + # library + token = sniffio.current_async_library_cvar.set(backend) + + try: + backend_options = backend_options or {} + return async_backend.run(func, args, {}, backend_options) + finally: + if token: + sniffio.current_async_library_cvar.reset(token) + + +async def sleep(delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + + """ + return await get_async_backend().sleep(delay) + + +async def sleep_forever() -> None: + """ + Pause the current task until it's cancelled. + + This is a shortcut for ``sleep(math.inf)``. + + .. versionadded:: 3.1 + + """ + await sleep(math.inf) + + +async def sleep_until(deadline: float) -> None: + """ + Pause the current task until the given time. + + :param deadline: the absolute time to wake up at (according to the internal + monotonic clock of the event loop) + + .. versionadded:: 3.1 + + """ + now = current_time() + await sleep(max(deadline - now, 0)) + + +def current_time() -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + + """ + return get_async_backend().current_time() + + +def get_all_backends() -> tuple[str, ...]: + """Return a tuple of the names of all built-in backends.""" + return BACKENDS + + +def get_cancelled_exc_class() -> type[BaseException]: + """Return the current async library's cancellation exception class.""" + return get_async_backend().cancelled_exception_class() + + +# +# Private API +# + + +@contextmanager +def claim_worker_thread( + backend_class: type[AsyncBackend], token: object +) -> Generator[Any, None, None]: + threadlocals.current_async_backend = backend_class + threadlocals.current_token = token + try: + yield + finally: + del threadlocals.current_async_backend + del threadlocals.current_token + + +def get_async_backend(asynclib_name: str | None = None) -> AsyncBackend: + if asynclib_name is None: + asynclib_name = sniffio.current_async_library() + + modulename = "anyio._backends._" + asynclib_name + try: + module = sys.modules[modulename] + except KeyError: + module = import_module(modulename) + + return getattr(module, "backend_class") diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_exceptions.py new file mode 100644 index 0000000..571c3b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_exceptions.py @@ -0,0 +1,73 @@ +from __future__ import annotations + + +class BrokenResourceError(Exception): + """ + Raised when trying to use a resource that has been rendered unusable due to external + causes (e.g. a send stream whose peer has disconnected). + """ + + +class BrokenWorkerProcess(Exception): + """ + Raised by :func:`run_sync_in_process` if the worker process terminates abruptly or + otherwise misbehaves. + """ + + +class BusyResourceError(Exception): + """ + Raised when two tasks are trying to read from or write to the same resource + concurrently. + """ + + def __init__(self, action: str): + super().__init__(f"Another task is already {action} this resource") + + +class ClosedResourceError(Exception): + """Raised when trying to use a resource that has been closed.""" + + +class DelimiterNotFound(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + maximum number of bytes has been read without the delimiter being found. + """ + + def __init__(self, max_bytes: int) -> None: + super().__init__( + f"The delimiter was not found among the first {max_bytes} bytes" + ) + + +class EndOfStream(Exception): + """ + Raised when trying to read from a stream that has been closed from the other end. + """ + + +class IncompleteRead(Exception): + """ + Raised during + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or + :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the + connection is closed before the requested amount of bytes has been read. + """ + + def __init__(self) -> None: + super().__init__( + "The stream was closed before the read operation could be completed" + ) + + +class TypedAttributeLookupError(LookupError): + """ + Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute + is not found and no default value has been given. + """ + + +class WouldBlock(Exception): + """Raised by ``X_nowait`` functions if ``X()`` would block.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_fileio.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_fileio.py new file mode 100644 index 0000000..d054be6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_fileio.py @@ -0,0 +1,645 @@ +from __future__ import annotations + +import os +import pathlib +import sys +from collections.abc import Callable, Iterable, Iterator, Sequence +from dataclasses import dataclass +from functools import partial +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + AnyStr, + AsyncIterator, + Final, + Generic, + overload, +) + +from .. import to_thread +from ..abc import AsyncResource + +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer +else: + ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object + + +class AsyncFile(AsyncResource, Generic[AnyStr]): + """ + An asynchronous file object. + + This class wraps a standard file object and provides async friendly versions of the + following blocking methods (where available on the original file object): + + * read + * read1 + * readline + * readlines + * readinto + * readinto1 + * write + * writelines + * truncate + * seek + * tell + * flush + + All other methods are directly passed through. + + This class supports the asynchronous context manager protocol which closes the + underlying file at the end of the context block. + + This class also supports asynchronous iteration:: + + async with await open_file(...) as f: + async for line in f: + print(line) + """ + + def __init__(self, fp: IO[AnyStr]) -> None: + self._fp: Any = fp + + def __getattr__(self, name: str) -> object: + return getattr(self._fp, name) + + @property + def wrapped(self) -> IO[AnyStr]: + """The wrapped file object.""" + return self._fp + + async def __aiter__(self) -> AsyncIterator[AnyStr]: + while True: + line = await self.readline() + if line: + yield line + else: + break + + async def aclose(self) -> None: + return await to_thread.run_sync(self._fp.close) + + async def read(self, size: int = -1) -> AnyStr: + return await to_thread.run_sync(self._fp.read, size) + + async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: + return await to_thread.run_sync(self._fp.read1, size) + + async def readline(self) -> AnyStr: + return await to_thread.run_sync(self._fp.readline) + + async def readlines(self) -> list[AnyStr]: + return await to_thread.run_sync(self._fp.readlines) + + async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> bytes: + return await to_thread.run_sync(self._fp.readinto, b) + + async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> bytes: + return await to_thread.run_sync(self._fp.readinto1, b) + + @overload + async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: + ... + + @overload + async def write(self: AsyncFile[str], b: str) -> int: + ... + + async def write(self, b: ReadableBuffer | str) -> int: + return await to_thread.run_sync(self._fp.write, b) + + @overload + async def writelines( + self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: + ... + + @overload + async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: + ... + + async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: + return await to_thread.run_sync(self._fp.writelines, lines) + + async def truncate(self, size: int | None = None) -> int: + return await to_thread.run_sync(self._fp.truncate, size) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + return await to_thread.run_sync(self._fp.seek, offset, whence) + + async def tell(self) -> int: + return await to_thread.run_sync(self._fp.tell) + + async def flush(self) -> None: + return await to_thread.run_sync(self._fp.flush) + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[bytes]: + ... + + +@overload +async def open_file( + file: str | PathLike[str] | int, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] | None = ..., +) -> AsyncFile[str]: + ... + + +async def open_file( + file: str | PathLike[str] | int, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + closefd: bool = True, + opener: Callable[[str, int], int] | None = None, +) -> AsyncFile[Any]: + """ + Open a file asynchronously. + + The arguments are exactly the same as for the builtin :func:`open`. + + :return: an asynchronous file object + + """ + fp = await to_thread.run_sync( + open, file, mode, buffering, encoding, errors, newline, closefd, opener + ) + return AsyncFile(fp) + + +def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]: + """ + Wrap an existing file as an asynchronous file. + + :param file: an existing file-like object + :return: an asynchronous file object + + """ + return AsyncFile(file) + + +@dataclass(eq=False) +class _PathIterator(AsyncIterator["Path"]): + iterator: Iterator[PathLike[str]] + + async def __anext__(self) -> Path: + nextval = await to_thread.run_sync( + next, self.iterator, None, abandon_on_cancel=True + ) + if nextval is None: + raise StopAsyncIteration from None + + return Path(nextval) + + +class Path: + """ + An asynchronous version of :class:`pathlib.Path`. + + This class cannot be substituted for :class:`pathlib.Path` or + :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` + interface. + + It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for + the deprecated :meth:`~pathlib.Path.link_to` method. + + Any methods that do disk I/O need to be awaited on. These methods are: + + * :meth:`~pathlib.Path.absolute` + * :meth:`~pathlib.Path.chmod` + * :meth:`~pathlib.Path.cwd` + * :meth:`~pathlib.Path.exists` + * :meth:`~pathlib.Path.expanduser` + * :meth:`~pathlib.Path.group` + * :meth:`~pathlib.Path.hardlink_to` + * :meth:`~pathlib.Path.home` + * :meth:`~pathlib.Path.is_block_device` + * :meth:`~pathlib.Path.is_char_device` + * :meth:`~pathlib.Path.is_dir` + * :meth:`~pathlib.Path.is_fifo` + * :meth:`~pathlib.Path.is_file` + * :meth:`~pathlib.Path.is_mount` + * :meth:`~pathlib.Path.lchmod` + * :meth:`~pathlib.Path.lstat` + * :meth:`~pathlib.Path.mkdir` + * :meth:`~pathlib.Path.open` + * :meth:`~pathlib.Path.owner` + * :meth:`~pathlib.Path.read_bytes` + * :meth:`~pathlib.Path.read_text` + * :meth:`~pathlib.Path.readlink` + * :meth:`~pathlib.Path.rename` + * :meth:`~pathlib.Path.replace` + * :meth:`~pathlib.Path.rmdir` + * :meth:`~pathlib.Path.samefile` + * :meth:`~pathlib.Path.stat` + * :meth:`~pathlib.Path.touch` + * :meth:`~pathlib.Path.unlink` + * :meth:`~pathlib.Path.write_bytes` + * :meth:`~pathlib.Path.write_text` + + Additionally, the following methods return an async iterator yielding + :class:`~.Path` objects: + + * :meth:`~pathlib.Path.glob` + * :meth:`~pathlib.Path.iterdir` + * :meth:`~pathlib.Path.rglob` + """ + + __slots__ = "_path", "__weakref__" + + __weakref__: Any + + def __init__(self, *args: str | PathLike[str]) -> None: + self._path: Final[pathlib.Path] = pathlib.Path(*args) + + def __fspath__(self) -> str: + return self._path.__fspath__() + + def __str__(self) -> str: + return self._path.__str__() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.as_posix()!r})" + + def __bytes__(self) -> bytes: + return self._path.__bytes__() + + def __hash__(self) -> int: + return self._path.__hash__() + + def __eq__(self, other: object) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__eq__(target) + + def __lt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__lt__(target) + + def __le__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__le__(target) + + def __gt__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__gt__(target) + + def __ge__(self, other: pathlib.PurePath | Path) -> bool: + target = other._path if isinstance(other, Path) else other + return self._path.__ge__(target) + + def __truediv__(self, other: str | PathLike[str]) -> Path: + return Path(self._path / other) + + def __rtruediv__(self, other: str | PathLike[str]) -> Path: + return Path(other) / self + + @property + def parts(self) -> tuple[str, ...]: + return self._path.parts + + @property + def drive(self) -> str: + return self._path.drive + + @property + def root(self) -> str: + return self._path.root + + @property + def anchor(self) -> str: + return self._path.anchor + + @property + def parents(self) -> Sequence[Path]: + return tuple(Path(p) for p in self._path.parents) + + @property + def parent(self) -> Path: + return Path(self._path.parent) + + @property + def name(self) -> str: + return self._path.name + + @property + def suffix(self) -> str: + return self._path.suffix + + @property + def suffixes(self) -> list[str]: + return self._path.suffixes + + @property + def stem(self) -> str: + return self._path.stem + + async def absolute(self) -> Path: + path = await to_thread.run_sync(self._path.absolute) + return Path(path) + + def as_posix(self) -> str: + return self._path.as_posix() + + def as_uri(self) -> str: + return self._path.as_uri() + + def match(self, path_pattern: str) -> bool: + return self._path.match(path_pattern) + + def is_relative_to(self, other: str | PathLike[str]) -> bool: + try: + self.relative_to(other) + return True + except ValueError: + return False + + async def is_junction(self) -> bool: + return await to_thread.run_sync(self._path.is_junction) + + async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: + func = partial(os.chmod, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, mode) + + @classmethod + async def cwd(cls) -> Path: + path = await to_thread.run_sync(pathlib.Path.cwd) + return cls(path) + + async def exists(self) -> bool: + return await to_thread.run_sync(self._path.exists, abandon_on_cancel=True) + + async def expanduser(self) -> Path: + return Path( + await to_thread.run_sync(self._path.expanduser, abandon_on_cancel=True) + ) + + def glob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.glob(pattern) + return _PathIterator(gen) + + async def group(self) -> str: + return await to_thread.run_sync(self._path.group, abandon_on_cancel=True) + + async def hardlink_to( + self, target: str | bytes | PathLike[str] | PathLike[bytes] + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(os.link, target, self) + + @classmethod + async def home(cls) -> Path: + home_path = await to_thread.run_sync(pathlib.Path.home) + return cls(home_path) + + def is_absolute(self) -> bool: + return self._path.is_absolute() + + async def is_block_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_block_device, abandon_on_cancel=True + ) + + async def is_char_device(self) -> bool: + return await to_thread.run_sync( + self._path.is_char_device, abandon_on_cancel=True + ) + + async def is_dir(self) -> bool: + return await to_thread.run_sync(self._path.is_dir, abandon_on_cancel=True) + + async def is_fifo(self) -> bool: + return await to_thread.run_sync(self._path.is_fifo, abandon_on_cancel=True) + + async def is_file(self) -> bool: + return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True) + + async def is_mount(self) -> bool: + return await to_thread.run_sync( + os.path.ismount, self._path, abandon_on_cancel=True + ) + + def is_reserved(self) -> bool: + return self._path.is_reserved() + + async def is_socket(self) -> bool: + return await to_thread.run_sync(self._path.is_socket, abandon_on_cancel=True) + + async def is_symlink(self) -> bool: + return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True) + + def iterdir(self) -> AsyncIterator[Path]: + gen = self._path.iterdir() + return _PathIterator(gen) + + def joinpath(self, *args: str | PathLike[str]) -> Path: + return Path(self._path.joinpath(*args)) + + async def lchmod(self, mode: int) -> None: + await to_thread.run_sync(self._path.lchmod, mode) + + async def lstat(self) -> os.stat_result: + return await to_thread.run_sync(self._path.lstat, abandon_on_cancel=True) + + async def mkdir( + self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False + ) -> None: + await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok) + + @overload + async def open( + self, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[bytes]: + ... + + @overload + async def open( + self, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + errors: str | None = ..., + newline: str | None = ..., + ) -> AsyncFile[str]: + ... + + async def open( + self, + mode: str = "r", + buffering: int = -1, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> AsyncFile[Any]: + fp = await to_thread.run_sync( + self._path.open, mode, buffering, encoding, errors, newline + ) + return AsyncFile(fp) + + async def owner(self) -> str: + return await to_thread.run_sync(self._path.owner, abandon_on_cancel=True) + + async def read_bytes(self) -> bytes: + return await to_thread.run_sync(self._path.read_bytes) + + async def read_text( + self, encoding: str | None = None, errors: str | None = None + ) -> str: + return await to_thread.run_sync(self._path.read_text, encoding, errors) + + if sys.version_info >= (3, 12): + + def relative_to( + self, *other: str | PathLike[str], walk_up: bool = False + ) -> Path: + return Path(self._path.relative_to(*other, walk_up=walk_up)) + + else: + + def relative_to(self, *other: str | PathLike[str]) -> Path: + return Path(self._path.relative_to(*other)) + + async def readlink(self) -> Path: + target = await to_thread.run_sync(os.readlink, self._path) + return Path(target) + + async def rename(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.rename, target) + return Path(target) + + async def replace(self, target: str | pathlib.PurePath | Path) -> Path: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.replace, target) + return Path(target) + + async def resolve(self, strict: bool = False) -> Path: + func = partial(self._path.resolve, strict=strict) + return Path(await to_thread.run_sync(func, abandon_on_cancel=True)) + + def rglob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern) + return _PathIterator(gen) + + async def rmdir(self) -> None: + await to_thread.run_sync(self._path.rmdir) + + async def samefile(self, other_path: str | PathLike[str]) -> bool: + if isinstance(other_path, Path): + other_path = other_path._path + + return await to_thread.run_sync( + self._path.samefile, other_path, abandon_on_cancel=True + ) + + async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: + func = partial(os.stat, follow_symlinks=follow_symlinks) + return await to_thread.run_sync(func, self._path, abandon_on_cancel=True) + + async def symlink_to( + self, + target: str | bytes | PathLike[str] | PathLike[bytes], + target_is_directory: bool = False, + ) -> None: + if isinstance(target, Path): + target = target._path + + await to_thread.run_sync(self._path.symlink_to, target, target_is_directory) + + async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: + await to_thread.run_sync(self._path.touch, mode, exist_ok) + + async def unlink(self, missing_ok: bool = False) -> None: + try: + await to_thread.run_sync(self._path.unlink) + except FileNotFoundError: + if not missing_ok: + raise + + if sys.version_info >= (3, 12): + + async def walk( + self, + top_down: bool = True, + on_error: Callable[[OSError], object] | None = None, + follow_symlinks: bool = False, + ) -> AsyncIterator[tuple[Path, list[str], list[str]]]: + def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: + try: + return next(gen) + except StopIteration: + return None + + gen = self._path.walk(top_down, on_error, follow_symlinks) + while True: + value = await to_thread.run_sync(get_next_value) + if value is None: + return + + root, dirs, paths = value + yield Path(root), dirs, paths + + def with_name(self, name: str) -> Path: + return Path(self._path.with_name(name)) + + def with_stem(self, stem: str) -> Path: + return Path(self._path.with_name(stem + self._path.suffix)) + + def with_suffix(self, suffix: str) -> Path: + return Path(self._path.with_suffix(suffix)) + + def with_segments(self, *pathsegments: str | PathLike[str]) -> Path: + return Path(*pathsegments) + + async def write_bytes(self, data: bytes) -> int: + return await to_thread.run_sync(self._path.write_bytes, data) + + async def write_text( + self, + data: str, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> int: + # Path.write_text() does not support the "newline" parameter before Python 3.10 + def sync_write_text() -> int: + with self._path.open( + "w", encoding=encoding, errors=errors, newline=newline + ) as fp: + return fp.write(data) + + return await to_thread.run_sync(sync_write_text) + + +PathLike.register(Path) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_resources.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_resources.py new file mode 100644 index 0000000..b9a5344 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_resources.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from ..abc import AsyncResource +from ._tasks import CancelScope + + +async def aclose_forcefully(resource: AsyncResource) -> None: + """ + Close an asynchronous resource in a cancelled scope. + + Doing this closes the resource without waiting on anything. + + :param resource: the resource to close + + """ + with CancelScope() as scope: + scope.cancel() + await resource.aclose() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_signals.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_signals.py new file mode 100644 index 0000000..115c749 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_signals.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from signal import Signals +from typing import ContextManager + +from ._eventloop import get_async_backend + + +def open_signal_receiver(*signals: Signals) -> ContextManager[AsyncIterator[Signals]]: + """ + Start receiving operating system signals. + + :param signals: signals to receive (e.g. ``signal.SIGINT``) + :return: an asynchronous context manager for an asynchronous iterator which yields + signal numbers + + .. warning:: Windows does not support signals natively so it is best to avoid + relying on this in cross-platform applications. + + .. warning:: On asyncio, this permanently replaces any previous signal handler for + the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. + + """ + return get_async_backend().open_signal_receiver(*signals) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_sockets.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_sockets.py new file mode 100644 index 0000000..0f0a314 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_sockets.py @@ -0,0 +1,716 @@ +from __future__ import annotations + +import errno +import os +import socket +import ssl +import stat +import sys +from collections.abc import Awaitable +from ipaddress import IPv6Address, ip_address +from os import PathLike, chmod +from socket import AddressFamily, SocketKind +from typing import Any, Literal, cast, overload + +from .. import to_thread +from ..abc import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPAddressType, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, +) +from ..streams.stapled import MultiListener +from ..streams.tls import TLSStream +from ._eventloop import get_async_backend +from ._resources import aclose_forcefully +from ._synchronization import Event +from ._tasks import create_task_group, move_on_after + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + +IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 + +AnyIPAddressFamily = Literal[ + AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 +] +IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] + + +# tls_hostname given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str, + happy_eyeballs_delay: float = ..., +) -> TLSStream: + ... + + +# ssl_context given +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + ssl_context: ssl.SSLContext, + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: + ... + + +# tls=True +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[True], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> TLSStream: + ... + + +# tls=False +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + tls: Literal[False], + ssl_context: ssl.SSLContext | None = ..., + tls_standard_compatible: bool = ..., + tls_hostname: str | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: + ... + + +# No TLS arguments +@overload +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = ..., + happy_eyeballs_delay: float = ..., +) -> SocketStream: + ... + + +async def connect_tcp( + remote_host: IPAddressType, + remote_port: int, + *, + local_host: IPAddressType | None = None, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_standard_compatible: bool = True, + tls_hostname: str | None = None, + happy_eyeballs_delay: float = 0.25, +) -> SocketStream | TLSStream: + """ + Connect to a host using the TCP protocol. + + This function implements the stateless version of the Happy Eyeballs algorithm (RFC + 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, + each one is tried until one connection attempt succeeds. If the first attempt does + not connected within 250 milliseconds, a second attempt is started using the next + address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if + available) is tried first. + + When the connection has been established, a TLS handshake will be done if either + ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. + + :param remote_host: the IP address or host name to connect to + :param remote_port: port on the target host to connect to + :param local_host: the interface address or name to bind the socket to before + connecting + :param tls: ``True`` to do a TLS handshake with the connected stream and return a + :class:`~anyio.streams.tls.TLSStream` instead + :param ssl_context: the SSL context object to use (if omitted, a default context is + created) + :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake + before closing the stream and requires that the server does this as well. + Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. + Some protocols, such as HTTP, require this option to be ``False``. + See :meth:`~ssl.SSLContext.wrap_socket` for details. + :param tls_hostname: host name to check the server certificate against (defaults to + the value of ``remote_host``) + :param happy_eyeballs_delay: delay (in seconds) before starting the next connection + attempt + :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream + :raises OSError: if the connection attempt fails + + """ + # Placed here due to https://github.com/python/mypy/issues/7057 + connected_stream: SocketStream | None = None + + async def try_connect(remote_host: str, event: Event) -> None: + nonlocal connected_stream + try: + stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) + except OSError as exc: + oserrors.append(exc) + return + else: + if connected_stream is None: + connected_stream = stream + tg.cancel_scope.cancel() + else: + await stream.aclose() + finally: + event.set() + + asynclib = get_async_backend() + local_address: IPSockAddrType | None = None + family = socket.AF_UNSPEC + if local_host: + gai_res = await getaddrinfo(str(local_host), None) + family, *_, local_address = gai_res[0] + + target_host = str(remote_host) + try: + addr_obj = ip_address(remote_host) + except ValueError: + # getaddrinfo() will raise an exception if name resolution fails + gai_res = await getaddrinfo( + target_host, remote_port, family=family, type=socket.SOCK_STREAM + ) + + # Organize the list so that the first address is an IPv6 address (if available) + # and the second one is an IPv4 addresses. The rest can be in whatever order. + v6_found = v4_found = False + target_addrs: list[tuple[socket.AddressFamily, str]] = [] + for af, *rest, sa in gai_res: + if af == socket.AF_INET6 and not v6_found: + v6_found = True + target_addrs.insert(0, (af, sa[0])) + elif af == socket.AF_INET and not v4_found and v6_found: + v4_found = True + target_addrs.insert(1, (af, sa[0])) + else: + target_addrs.append((af, sa[0])) + else: + if isinstance(addr_obj, IPv6Address): + target_addrs = [(socket.AF_INET6, addr_obj.compressed)] + else: + target_addrs = [(socket.AF_INET, addr_obj.compressed)] + + oserrors: list[OSError] = [] + async with create_task_group() as tg: + for i, (af, addr) in enumerate(target_addrs): + event = Event() + tg.start_soon(try_connect, addr, event) + with move_on_after(happy_eyeballs_delay): + await event.wait() + + if connected_stream is None: + cause = ( + oserrors[0] + if len(oserrors) == 1 + else ExceptionGroup("multiple connection attempts failed", oserrors) + ) + raise OSError("All connection attempts failed") from cause + + if tls or tls_hostname or ssl_context: + try: + return await TLSStream.wrap( + connected_stream, + server_side=False, + hostname=tls_hostname or str(remote_host), + ssl_context=ssl_context, + standard_compatible=tls_standard_compatible, + ) + except BaseException: + await aclose_forcefully(connected_stream) + raise + + return connected_stream + + +async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: + """ + Connect to the given UNIX socket. + + Not available on Windows. + + :param path: path to the socket + :return: a socket stream object + + """ + path = os.fspath(path) + return await get_async_backend().connect_unix(path) + + +async def create_tcp_listener( + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, + backlog: int = 65536, + reuse_port: bool = False, +) -> MultiListener[SocketStream]: + """ + Create a TCP socket listener. + + :param local_port: port number to listen on + :param local_host: IP address of the interface to listen on. If omitted, listen on + all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address + family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. + :param family: address family (used if ``local_host`` was omitted) + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a list of listener objects + + """ + asynclib = get_async_backend() + backlog = min(backlog, 65536) + local_host = str(local_host) if local_host is not None else None + gai_res = await getaddrinfo( + local_host, + local_port, + family=family, + type=socket.SocketKind.SOCK_STREAM if sys.platform == "win32" else 0, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + listeners: list[SocketListener] = [] + try: + # The set() is here to work around a glibc bug: + # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 + sockaddr: tuple[str, int] | tuple[str, int, int, int] + for fam, kind, *_, sockaddr in sorted(set(gai_res)): + # Workaround for an uvloop bug where we don't get the correct scope ID for + # IPv6 link-local addresses when passing type=socket.SOCK_STREAM to + # getaddrinfo(): https://github.com/MagicStack/uvloop/issues/539 + if sys.platform != "win32" and kind is not SocketKind.SOCK_STREAM: + continue + + raw_socket = socket.socket(fam) + raw_socket.setblocking(False) + + # For Windows, enable exclusive address use. For others, enable address + # reuse. + if sys.platform == "win32": + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) + else: + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if reuse_port: + raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + # If only IPv6 was requested, disable dual stack operation + if fam == socket.AF_INET6: + raw_socket.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) + + # Workaround for #554 + if "%" in sockaddr[0]: + addr, scope_id = sockaddr[0].split("%", 1) + sockaddr = (addr, sockaddr[1], 0, int(scope_id)) + + raw_socket.bind(sockaddr) + raw_socket.listen(backlog) + listener = asynclib.create_tcp_listener(raw_socket) + listeners.append(listener) + except BaseException: + for listener in listeners: + await listener.aclose() + + raise + + return MultiListener(listeners) + + +async def create_unix_listener( + path: str | bytes | PathLike[Any], + *, + mode: int | None = None, + backlog: int = 65536, +) -> SocketListener: + """ + Create a UNIX socket listener. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param backlog: maximum number of queued incoming connections (up to a maximum of + 2**16, or 65536) + :return: a listener object + + .. versionchanged:: 3.0 + If a socket already exists on the file system in the given path, it will be + removed first. + + """ + backlog = min(backlog, 65536) + raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) + try: + raw_socket.listen(backlog) + return get_async_backend().create_unix_listener(raw_socket) + except BaseException: + raw_socket.close() + raise + + +async def create_udp_socket( + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + *, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> UDPSocket: + """ + Create a UDP socket. + + If ``port`` has been given, the socket will be bound to this port on the local + machine, making this socket suitable for providing UDP based services. + + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a UDP socket + + """ + if family is AddressFamily.AF_UNSPEC and not local_host: + raise ValueError('Either "family" or "local_host" must be given') + + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + elif family is AddressFamily.AF_INET6: + local_address = ("::", 0) + else: + local_address = ("0.0.0.0", 0) + + sock = await get_async_backend().create_udp_socket( + family, local_address, None, reuse_port + ) + return cast(UDPSocket, sock) + + +async def create_connected_udp_socket( + remote_host: IPAddressType, + remote_port: int, + *, + family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, + local_host: IPAddressType | None = None, + local_port: int = 0, + reuse_port: bool = False, +) -> ConnectedUDPSocket: + """ + Create a connected UDP socket. + + Connected UDP sockets can only communicate with the specified remote host/port, an + any packets sent from other sources are dropped. + + :param remote_host: remote host to set as the default target + :param remote_port: port on the remote host to set as the default target + :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically + determined from ``local_host`` or ``remote_host`` if omitted + :param local_host: IP address or host name of the local interface to bind to + :param local_port: local port to bind to + :param reuse_port: ``True`` to allow multiple sockets to bind to the same + address/port (not supported on Windows) + :return: a connected UDP socket + + """ + local_address = None + if local_host: + gai_res = await getaddrinfo( + str(local_host), + local_port, + family=family, + type=socket.SOCK_DGRAM, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + local_address = gai_res[0][-1] + + gai_res = await getaddrinfo( + str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM + ) + family = cast(AnyIPAddressFamily, gai_res[0][0]) + remote_address = gai_res[0][-1] + + sock = await get_async_backend().create_udp_socket( + family, local_address, remote_address, reuse_port + ) + return cast(ConnectedUDPSocket, sock) + + +async def create_unix_datagram_socket( + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> UNIXDatagramSocket: + """ + Create a UNIX datagram socket. + + Not available on Windows. + + If ``local_path`` has been given, the socket will be bound to this path, making this + socket suitable for receiving datagrams from other processes. Other processes can + send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a UNIX datagram socket + + """ + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket(raw_socket, None) + + +async def create_connected_unix_datagram_socket( + remote_path: str | bytes | PathLike[Any], + *, + local_path: None | str | bytes | PathLike[Any] = None, + local_mode: int | None = None, +) -> ConnectedUNIXDatagramSocket: + """ + Create a connected UNIX datagram socket. + + Connected datagram sockets can only communicate with the specified remote path. + + If ``local_path`` has been given, the socket will be bound to this path, making + this socket suitable for receiving datagrams from other processes. Other processes + can send datagrams to this socket only if ``local_path`` is set. + + If a socket already exists on the file system in the ``local_path``, it will be + removed first. + + :param remote_path: the path to set as the default target + :param local_path: the path on which to bind to + :param local_mode: permissions to set on the local socket + :return: a connected UNIX datagram socket + + """ + remote_path = os.fspath(remote_path) + raw_socket = await setup_unix_local_socket( + local_path, local_mode, socket.SOCK_DGRAM + ) + return await get_async_backend().create_unix_datagram_socket( + raw_socket, remote_path + ) + + +async def getaddrinfo( + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, +) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: + """ + Look up a numeric IP address given a host name. + + Internationalized domain names are translated according to the (non-transitional) + IDNA 2008 standard. + + .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of + (host, port), unlike what :func:`socket.getaddrinfo` does. + + :param host: host name + :param port: port number + :param family: socket family (`'AF_INET``, ...) + :param type: socket type (``SOCK_STREAM``, ...) + :param proto: protocol number + :param flags: flags to pass to upstream ``getaddrinfo()`` + :return: list of tuples containing (family, type, proto, canonname, sockaddr) + + .. seealso:: :func:`socket.getaddrinfo` + + """ + # Handle unicode hostnames + if isinstance(host, str): + try: + encoded_host: bytes | None = host.encode("ascii") + except UnicodeEncodeError: + import idna + + encoded_host = idna.encode(host, uts46=True) + else: + encoded_host = host + + gai_res = await get_async_backend().getaddrinfo( + encoded_host, port, family=family, type=type, proto=proto, flags=flags + ) + return [ + (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) + for family, type, proto, canonname, sockaddr in gai_res + ] + + +def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: + """ + Look up the host name of an IP address. + + :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) + :param flags: flags to pass to upstream ``getnameinfo()`` + :return: a tuple of (host name, service name) + + .. seealso:: :func:`socket.getnameinfo` + + """ + return get_async_backend().getnameinfo(sockaddr, flags) + + +def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: + """ + Wait until the given socket has data to be read. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become readable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become readable + + """ + return get_async_backend().wait_socket_readable(sock) + + +def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: + """ + Wait until the given socket can be written to. + + This does **NOT** work on Windows when using the asyncio backend with a proactor + event loop (default on py3.8+). + + .. warning:: Only use this on raw sockets that have not been wrapped by any higher + level constructs like socket streams! + + :param sock: a socket object + :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the + socket to become writable + :raises ~anyio.BusyResourceError: if another task is already waiting for the socket + to become writable + + """ + return get_async_backend().wait_socket_writable(sock) + + +# +# Private API +# + + +def convert_ipv6_sockaddr( + sockaddr: tuple[str, int, int, int] | tuple[str, int], +) -> tuple[str, int]: + """ + Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. + + If the scope ID is nonzero, it is added to the address, separated with ``%``. + Otherwise the flow id and scope id are simply cut off from the tuple. + Any other kinds of socket addresses are returned as-is. + + :param sockaddr: the result of :meth:`~socket.socket.getsockname` + :return: the converted socket address + + """ + # This is more complicated than it should be because of MyPy + if isinstance(sockaddr, tuple) and len(sockaddr) == 4: + host, port, flowinfo, scope_id = sockaddr + if scope_id: + # PyPy (as of v7.3.11) leaves the interface name in the result, so + # we discard it and only get the scope ID from the end + # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) + host = host.split("%")[0] + + # Add scope_id to the address + return f"{host}%{scope_id}", port + else: + return host, port + else: + return sockaddr + + +async def setup_unix_local_socket( + path: None | str | bytes | PathLike[Any], + mode: int | None, + socktype: int, +) -> socket.socket: + """ + Create a UNIX local socket object, deleting the socket at the given path if it + exists. + + Not available on Windows. + + :param path: path of the socket + :param mode: permissions to set on the socket + :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM + + """ + path_str: str | bytes | None + if path is not None: + path_str = os.fspath(path) + + # Copied from pathlib... + try: + stat_result = os.stat(path) + except OSError as e: + if e.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EBADF, errno.ELOOP): + raise + else: + if stat.S_ISSOCK(stat_result.st_mode): + os.unlink(path) + else: + path_str = None + + raw_socket = socket.socket(socket.AF_UNIX, socktype) + raw_socket.setblocking(False) + + if path_str is not None: + try: + await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) + if mode is not None: + await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) + except BaseException: + raw_socket.close() + raise + + return raw_socket diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_streams.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_streams.py new file mode 100644 index 0000000..aa6b0c2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_streams.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import math +from typing import Tuple, TypeVar +from warnings import warn + +from ..streams.memory import ( + MemoryObjectReceiveStream, + MemoryObjectSendStream, + MemoryObjectStreamState, +) + +T_Item = TypeVar("T_Item") + + +class create_memory_object_stream( + Tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], +): + """ + Create a memory object stream. + + The stream's item type can be annotated like + :func:`create_memory_object_stream[T_Item]`. + + :param max_buffer_size: number of items held in the buffer until ``send()`` starts + blocking + :param item_type: old way of marking the streams with the right generic type for + static typing (does nothing on AnyIO 4) + + .. deprecated:: 4.0 + Use ``create_memory_object_stream[YourItemType](...)`` instead. + :return: a tuple of (send stream, receive stream) + + """ + + def __new__( # type: ignore[misc] + cls, max_buffer_size: float = 0, item_type: object = None + ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: + if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): + raise ValueError("max_buffer_size must be either an integer or math.inf") + if max_buffer_size < 0: + raise ValueError("max_buffer_size cannot be negative") + if item_type is not None: + warn( + "The item_type argument has been deprecated in AnyIO 4.0. " + "Use create_memory_object_stream[YourItemType](...) instead.", + DeprecationWarning, + stacklevel=2, + ) + + state = MemoryObjectStreamState[T_Item](max_buffer_size) + return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_subprocesses.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_subprocesses.py new file mode 100644 index 0000000..5d5d7b7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_subprocesses.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, Mapping, Sequence +from io import BytesIO +from os import PathLike +from subprocess import DEVNULL, PIPE, CalledProcessError, CompletedProcess +from typing import IO, Any, cast + +from ..abc import Process +from ._eventloop import get_async_backend +from ._tasks import create_task_group + + +async def run_process( + command: str | bytes | Sequence[str | bytes], + *, + input: bytes | None = None, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + check: bool = True, + cwd: str | bytes | PathLike[str] | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, +) -> CompletedProcess[bytes]: + """ + Run an external command in a subprocess and wait until it completes. + + .. seealso:: :func:`subprocess.run` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param input: bytes passed to the standard input of the subprocess + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or `None` + :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the + process terminates with a return code other than 0 + :param cwd: If not ``None``, change the working directory to this before running the + command + :param env: if not ``None``, this mapping replaces the inherited environment + variables from the parent process + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :return: an object representing the completed process + :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process + exits with a nonzero return code + + """ + + async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: + buffer = BytesIO() + async for chunk in stream: + buffer.write(chunk) + + stream_contents[index] = buffer.getvalue() + + async with await open_process( + command, + stdin=PIPE if input else DEVNULL, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) as process: + stream_contents: list[bytes | None] = [None, None] + async with create_task_group() as tg: + if process.stdout: + tg.start_soon(drain_stream, process.stdout, 0) + + if process.stderr: + tg.start_soon(drain_stream, process.stderr, 1) + + if process.stdin and input: + await process.stdin.send(input) + await process.stdin.aclose() + + await process.wait() + + output, errors = stream_contents + if check and process.returncode != 0: + raise CalledProcessError(cast(int, process.returncode), command, output, errors) + + return CompletedProcess(command, cast(int, process.returncode), output, errors) + + +async def open_process( + command: str | bytes | Sequence[str | bytes], + *, + stdin: int | IO[Any] | None = PIPE, + stdout: int | IO[Any] | None = PIPE, + stderr: int | IO[Any] | None = PIPE, + cwd: str | bytes | PathLike[str] | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, +) -> Process: + """ + Start an external command in a subprocess. + + .. seealso:: :class:`subprocess.Popen` + + :param command: either a string to pass to the shell, or an iterable of strings + containing the executable name or path and its arguments + :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a + file-like object, or ``None`` + :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or ``None`` + :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + :data:`subprocess.STDOUT`, a file-like object, or ``None`` + :param cwd: If not ``None``, the working directory is changed before executing + :param env: If env is not ``None``, it must be a mapping that defines the + environment variables for the new process + :param start_new_session: if ``true`` the setsid() system call will be made in the + child process prior to the execution of the subprocess. (POSIX only) + :return: an asynchronous process object + + """ + if isinstance(command, (str, bytes)): + return await get_async_backend().open_process( + command, + shell=True, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) + else: + return await get_async_backend().open_process( + command, + shell=False, + stdin=stdin, + stdout=stdout, + stderr=stderr, + cwd=cwd, + env=env, + start_new_session=start_new_session, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_synchronization.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_synchronization.py new file mode 100644 index 0000000..b274a31 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_synchronization.py @@ -0,0 +1,649 @@ +from __future__ import annotations + +import math +from collections import deque +from dataclasses import dataclass +from types import TracebackType + +from sniffio import AsyncLibraryNotFoundError + +from ..lowlevel import cancel_shielded_checkpoint, checkpoint, checkpoint_if_cancelled +from ._eventloop import get_async_backend +from ._exceptions import BusyResourceError, WouldBlock +from ._tasks import CancelScope +from ._testing import TaskInfo, get_current_task + + +@dataclass(frozen=True) +class EventStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` + """ + + tasks_waiting: int + + +@dataclass(frozen=True) +class CapacityLimiterStatistics: + """ + :ivar int borrowed_tokens: number of tokens currently borrowed by tasks + :ivar float total_tokens: total number of available tokens + :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from + this limiter + :ivar int tasks_waiting: number of tasks waiting on + :meth:`~.CapacityLimiter.acquire` or + :meth:`~.CapacityLimiter.acquire_on_behalf_of` + """ + + borrowed_tokens: int + total_tokens: float + borrowers: tuple[object, ...] + tasks_waiting: int + + +@dataclass(frozen=True) +class LockStatistics: + """ + :ivar bool locked: flag indicating if this lock is locked or not + :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the + lock is not held by any task) + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` + """ + + locked: bool + owner: TaskInfo | None + tasks_waiting: int + + +@dataclass(frozen=True) +class ConditionStatistics: + """ + :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` + :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying + :class:`~.Lock` + """ + + tasks_waiting: int + lock_statistics: LockStatistics + + +@dataclass(frozen=True) +class SemaphoreStatistics: + """ + :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` + + """ + + tasks_waiting: int + + +class Event: + def __new__(cls) -> Event: + try: + return get_async_backend().create_event() + except AsyncLibraryNotFoundError: + return EventAdapter() + + def set(self) -> None: + """Set the flag, notifying all listeners.""" + raise NotImplementedError + + def is_set(self) -> bool: + """Return ``True`` if the flag is set, ``False`` if not.""" + raise NotImplementedError + + async def wait(self) -> None: + """ + Wait until the flag has been set. + + If the flag has already been set when this method is called, it returns + immediately. + + """ + raise NotImplementedError + + def statistics(self) -> EventStatistics: + """Return statistics about the current state of this event.""" + raise NotImplementedError + + +class EventAdapter(Event): + _internal_event: Event | None = None + + def __new__(cls) -> EventAdapter: + return object.__new__(cls) + + @property + def _event(self) -> Event: + if self._internal_event is None: + self._internal_event = get_async_backend().create_event() + + return self._internal_event + + def set(self) -> None: + self._event.set() + + def is_set(self) -> bool: + return self._internal_event is not None and self._internal_event.is_set() + + async def wait(self) -> None: + await self._event.wait() + + def statistics(self) -> EventStatistics: + if self._internal_event is None: + return EventStatistics(tasks_waiting=0) + + return self._internal_event.statistics() + + +class Lock: + _owner_task: TaskInfo | None = None + + def __init__(self) -> None: + self._waiters: deque[tuple[TaskInfo, Event]] = deque() + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Acquire the lock.""" + await checkpoint_if_cancelled() + try: + self.acquire_nowait() + except WouldBlock: + task = get_current_task() + event = Event() + token = task, event + self._waiters.append(token) + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(token) + elif self._owner_task == task: + self.release() + + raise + + assert self._owner_task == task + else: + try: + await cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def acquire_nowait(self) -> None: + """ + Acquire the lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + task = get_current_task() + if self._owner_task == task: + raise RuntimeError("Attempted to acquire an already held Lock") + + if self._owner_task is not None: + raise WouldBlock + + self._owner_task = task + + def release(self) -> None: + """Release the lock.""" + if self._owner_task != get_current_task(): + raise RuntimeError("The current task is not holding this lock") + + if self._waiters: + self._owner_task, event = self._waiters.popleft() + event.set() + else: + del self._owner_task + + def locked(self) -> bool: + """Return True if the lock is currently held.""" + return self._owner_task is not None + + def statistics(self) -> LockStatistics: + """ + Return statistics about the current state of this lock. + + .. versionadded:: 3.0 + """ + return LockStatistics(self.locked(), self._owner_task, len(self._waiters)) + + +class Condition: + _owner_task: TaskInfo | None = None + + def __init__(self, lock: Lock | None = None): + self._lock = lock or Lock() + self._waiters: deque[Event] = deque() + + async def __aenter__(self) -> None: + await self.acquire() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + def _check_acquired(self) -> None: + if self._owner_task != get_current_task(): + raise RuntimeError("The current task is not holding the underlying lock") + + async def acquire(self) -> None: + """Acquire the underlying lock.""" + await self._lock.acquire() + self._owner_task = get_current_task() + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + self._lock.acquire_nowait() + self._owner_task = get_current_task() + + def release(self) -> None: + """Release the underlying lock.""" + self._lock.release() + + def locked(self) -> bool: + """Return True if the lock is set.""" + return self._lock.locked() + + def notify(self, n: int = 1) -> None: + """Notify exactly n listeners.""" + self._check_acquired() + for _ in range(n): + try: + event = self._waiters.popleft() + except IndexError: + break + + event.set() + + def notify_all(self) -> None: + """Notify all the listeners.""" + self._check_acquired() + for event in self._waiters: + event.set() + + self._waiters.clear() + + async def wait(self) -> None: + """Wait for a notification.""" + await checkpoint() + event = Event() + self._waiters.append(event) + self.release() + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + + raise + finally: + with CancelScope(shield=True): + await self.acquire() + + def statistics(self) -> ConditionStatistics: + """ + Return statistics about the current state of this condition. + + .. versionadded:: 3.0 + """ + return ConditionStatistics(len(self._waiters), self._lock.statistics()) + + +class Semaphore: + def __init__(self, initial_value: int, *, max_value: int | None = None): + if not isinstance(initial_value, int): + raise TypeError("initial_value must be an integer") + if initial_value < 0: + raise ValueError("initial_value must be >= 0") + if max_value is not None: + if not isinstance(max_value, int): + raise TypeError("max_value must be an integer or None") + if max_value < initial_value: + raise ValueError( + "max_value must be equal to or higher than initial_value" + ) + + self._value = initial_value + self._max_value = max_value + self._waiters: deque[Event] = deque() + + async def __aenter__(self) -> Semaphore: + await self.acquire() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.release() + + async def acquire(self) -> None: + """Decrement the semaphore value, blocking if necessary.""" + await checkpoint_if_cancelled() + try: + self.acquire_nowait() + except WouldBlock: + event = Event() + self._waiters.append(event) + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + else: + self.release() + + raise + else: + try: + await cancel_shielded_checkpoint() + except BaseException: + self.release() + raise + + def acquire_nowait(self) -> None: + """ + Acquire the underlying lock, without blocking. + + :raises ~anyio.WouldBlock: if the operation would block + + """ + if self._value == 0: + raise WouldBlock + + self._value -= 1 + + def release(self) -> None: + """Increment the semaphore value.""" + if self._max_value is not None and self._value == self._max_value: + raise ValueError("semaphore released too many times") + + if self._waiters: + self._waiters.popleft().set() + else: + self._value += 1 + + @property + def value(self) -> int: + """The current value of the semaphore.""" + return self._value + + @property + def max_value(self) -> int | None: + """The maximum value of the semaphore.""" + return self._max_value + + def statistics(self) -> SemaphoreStatistics: + """ + Return statistics about the current state of this semaphore. + + .. versionadded:: 3.0 + """ + return SemaphoreStatistics(len(self._waiters)) + + +class CapacityLimiter: + def __new__(cls, total_tokens: float) -> CapacityLimiter: + try: + return get_async_backend().create_capacity_limiter(total_tokens) + except AsyncLibraryNotFoundError: + return CapacityLimiterAdapter(total_tokens) + + async def __aenter__(self) -> None: + raise NotImplementedError + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + raise NotImplementedError + + @property + def total_tokens(self) -> float: + """ + The total number of tokens available for borrowing. + + This is a read-write property. If the total number of tokens is increased, the + proportionate number of tasks waiting on this limiter will be granted their + tokens. + + .. versionchanged:: 3.0 + The property is now writable. + + """ + raise NotImplementedError + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + raise NotImplementedError + + @property + def borrowed_tokens(self) -> int: + """The number of tokens that have currently been borrowed.""" + raise NotImplementedError + + @property + def available_tokens(self) -> float: + """The number of tokens currently available to be borrowed""" + raise NotImplementedError + + def acquire_nowait(self) -> None: + """ + Acquire a token for the current task without waiting for one to become + available. + + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + """ + Acquire a token without waiting for one to become available. + + :param borrower: the entity borrowing a token + :raises ~anyio.WouldBlock: if there are no tokens available for borrowing + + """ + raise NotImplementedError + + async def acquire(self) -> None: + """ + Acquire a token for the current task, waiting if necessary for one to become + available. + + """ + raise NotImplementedError + + async def acquire_on_behalf_of(self, borrower: object) -> None: + """ + Acquire a token, waiting if necessary for one to become available. + + :param borrower: the entity borrowing a token + + """ + raise NotImplementedError + + def release(self) -> None: + """ + Release the token held by the current task. + + :raises RuntimeError: if the current task has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def release_on_behalf_of(self, borrower: object) -> None: + """ + Release the token held by the given borrower. + + :raises RuntimeError: if the borrower has not borrowed a token from this + limiter. + + """ + raise NotImplementedError + + def statistics(self) -> CapacityLimiterStatistics: + """ + Return statistics about the current state of this limiter. + + .. versionadded:: 3.0 + + """ + raise NotImplementedError + + +class CapacityLimiterAdapter(CapacityLimiter): + _internal_limiter: CapacityLimiter | None = None + + def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter: + return object.__new__(cls) + + def __init__(self, total_tokens: float) -> None: + self.total_tokens = total_tokens + + @property + def _limiter(self) -> CapacityLimiter: + if self._internal_limiter is None: + self._internal_limiter = get_async_backend().create_capacity_limiter( + self._total_tokens + ) + + return self._internal_limiter + + async def __aenter__(self) -> None: + await self._limiter.__aenter__() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) + + @property + def total_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.total_tokens + + @total_tokens.setter + def total_tokens(self, value: float) -> None: + if not isinstance(value, int) and value is not math.inf: + raise TypeError("total_tokens must be an int or math.inf") + elif value < 1: + raise ValueError("total_tokens must be >= 1") + + if self._internal_limiter is None: + self._total_tokens = value + return + + self._limiter.total_tokens = value + + @property + def borrowed_tokens(self) -> int: + if self._internal_limiter is None: + return 0 + + return self._internal_limiter.borrowed_tokens + + @property + def available_tokens(self) -> float: + if self._internal_limiter is None: + return self._total_tokens + + return self._internal_limiter.available_tokens + + def acquire_nowait(self) -> None: + self._limiter.acquire_nowait() + + def acquire_on_behalf_of_nowait(self, borrower: object) -> None: + self._limiter.acquire_on_behalf_of_nowait(borrower) + + async def acquire(self) -> None: + await self._limiter.acquire() + + async def acquire_on_behalf_of(self, borrower: object) -> None: + await self._limiter.acquire_on_behalf_of(borrower) + + def release(self) -> None: + self._limiter.release() + + def release_on_behalf_of(self, borrower: object) -> None: + self._limiter.release_on_behalf_of(borrower) + + def statistics(self) -> CapacityLimiterStatistics: + if self._internal_limiter is None: + return CapacityLimiterStatistics( + borrowed_tokens=0, + total_tokens=self.total_tokens, + borrowers=(), + tasks_waiting=0, + ) + + return self._internal_limiter.statistics() + + +class ResourceGuard: + """ + A context manager for ensuring that a resource is only used by a single task at a + time. + + Entering this context manager while the previous has not exited it yet will trigger + :exc:`BusyResourceError`. + + :param action: the action to guard against (visible in the :exc:`BusyResourceError` + when triggered, e.g. "Another task is already {action} this resource") + + .. versionadded:: 4.1 + """ + + __slots__ = "action", "_guarded" + + def __init__(self, action: str = "using"): + self.action: str = action + self._guarded = False + + def __enter__(self) -> None: + if self._guarded: + raise BusyResourceError(self.action) + + self._guarded = True + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + self._guarded = False + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_tasks.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_tasks.py new file mode 100644 index 0000000..2f21ea2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_tasks.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import math +from collections.abc import Generator +from contextlib import contextmanager +from types import TracebackType + +from ..abc._tasks import TaskGroup, TaskStatus +from ._eventloop import get_async_backend + + +class _IgnoredTaskStatus(TaskStatus[object]): + def started(self, value: object = None) -> None: + pass + + +TASK_STATUS_IGNORED = _IgnoredTaskStatus() + + +class CancelScope: + """ + Wraps a unit of work that can be made separately cancellable. + + :param deadline: The time (clock value) when this scope is cancelled automatically + :param shield: ``True`` to shield the cancel scope from external cancellation + """ + + def __new__( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) + + def cancel(self) -> None: + """Cancel this scope immediately.""" + raise NotImplementedError + + @property + def deadline(self) -> float: + """ + The time (clock value) when this scope is cancelled automatically. + + Will be ``float('inf')`` if no timeout has been set. + + """ + raise NotImplementedError + + @deadline.setter + def deadline(self, value: float) -> None: + raise NotImplementedError + + @property + def cancel_called(self) -> bool: + """``True`` if :meth:`cancel` has been called.""" + raise NotImplementedError + + @property + def cancelled_caught(self) -> bool: + """ + ``True`` if this scope suppressed a cancellation exception it itself raised. + + This is typically used to check if any work was interrupted, or to see if the + scope was cancelled due to its deadline being reached. The value will, however, + only be ``True`` if the cancellation was triggered by the scope itself (and not + an outer scope). + + """ + raise NotImplementedError + + @property + def shield(self) -> bool: + """ + ``True`` if this scope is shielded from external cancellation. + + While a scope is shielded, it will not receive cancellations from outside. + + """ + raise NotImplementedError + + @shield.setter + def shield(self, value: bool) -> None: + raise NotImplementedError + + def __enter__(self) -> CancelScope: + raise NotImplementedError + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + raise NotImplementedError + + +@contextmanager +def fail_after( + delay: float | None, shield: bool = False +) -> Generator[CancelScope, None, None]: + """ + Create a context manager which raises a :class:`TimeoutError` if does not finish in + time. + + :param delay: maximum allowed time (in seconds) before raising the exception, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a context manager that yields a cancel scope + :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] + + """ + current_time = get_async_backend().current_time + deadline = (current_time() + delay) if delay is not None else math.inf + with get_async_backend().create_cancel_scope( + deadline=deadline, shield=shield + ) as cancel_scope: + yield cancel_scope + + if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: + raise TimeoutError + + +def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: + """ + Create a cancel scope with a deadline that expires after the given delay. + + :param delay: maximum allowed time (in seconds) before exiting the context block, or + ``None`` to disable the timeout + :param shield: ``True`` to shield the cancel scope from external cancellation + :return: a cancel scope + + """ + deadline = ( + (get_async_backend().current_time() + delay) if delay is not None else math.inf + ) + return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) + + +def current_effective_deadline() -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the current + task. + + :return: a clock value from the event loop's internal clock (or ``float('inf')`` if + there is no deadline in effect, or ``float('-inf')`` if the current scope has + been cancelled) + :rtype: float + + """ + return get_async_backend().current_effective_deadline() + + +def create_task_group() -> TaskGroup: + """ + Create a task group. + + :return: a task group + + """ + return get_async_backend().create_task_group() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_testing.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_testing.py new file mode 100644 index 0000000..1dae3b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_testing.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Generator +from typing import Any + +from ._eventloop import get_async_backend + + +class TaskInfo: + """ + Represents an asynchronous task. + + :ivar int id: the unique identifier of the task + :ivar parent_id: the identifier of the parent task, if any + :vartype parent_id: Optional[int] + :ivar str name: the description of the task (if any) + :ivar ~collections.abc.Coroutine coro: the coroutine object of the task + """ + + __slots__ = "_name", "id", "parent_id", "name", "coro" + + def __init__( + self, + id: int, + parent_id: int | None, + name: str | None, + coro: Generator[Any, Any, Any] | Awaitable[Any], + ): + func = get_current_task + self._name = f"{func.__module__}.{func.__qualname__}" + self.id: int = id + self.parent_id: int | None = parent_id + self.name: str | None = name + self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro + + def __eq__(self, other: object) -> bool: + if isinstance(other, TaskInfo): + return self.id == other.id + + return NotImplemented + + def __hash__(self) -> int: + return hash(self.id) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" + + def _unwrap(self) -> TaskInfo: + return self + + +def get_current_task() -> TaskInfo: + """ + Return the current task. + + :return: a representation of the current task + + """ + return get_async_backend().get_current_task() + + +def get_running_tasks() -> list[TaskInfo]: + """ + Return a list of running tasks in the current event loop. + + :return: a list of task info objects + + """ + return get_async_backend().get_running_tasks() + + +async def wait_all_tasks_blocked() -> None: + """Wait until all other tasks are waiting for something.""" + await get_async_backend().wait_all_tasks_blocked() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_typedattr.py b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_typedattr.py new file mode 100644 index 0000000..74c6b8f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/_core/_typedattr.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from typing import Any, TypeVar, final, overload + +from ._exceptions import TypedAttributeLookupError + +T_Attr = TypeVar("T_Attr") +T_Default = TypeVar("T_Default") +undefined = object() + + +def typed_attribute() -> Any: + """Return a unique object, used to mark typed attributes.""" + return object() + + +class TypedAttributeSet: + """ + Superclass for typed attribute collections. + + Checks that every public attribute of every subclass has a type annotation. + """ + + def __init_subclass__(cls) -> None: + annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) + for attrname in dir(cls): + if not attrname.startswith("_") and attrname not in annotations: + raise TypeError( + f"Attribute {attrname!r} is missing its type annotation" + ) + + super().__init_subclass__() + + +class TypedAttributeProvider: + """Base class for classes that wish to provide typed extra attributes.""" + + @property + def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: + """ + A mapping of the extra attributes to callables that return the corresponding + values. + + If the provider wraps another provider, the attributes from that wrapper should + also be included in the returned mapping (but the wrapper may override the + callables from the wrapped instance). + + """ + return {} + + @overload + def extra(self, attribute: T_Attr) -> T_Attr: + ... + + @overload + def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: + ... + + @final + def extra(self, attribute: Any, default: object = undefined) -> object: + """ + extra(attribute, default=undefined) + + Return the value of the given typed extra attribute. + + :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to + look for + :param default: the value that should be returned if no value is found for the + attribute + :raises ~anyio.TypedAttributeLookupError: if the search failed and no default + value was given + + """ + try: + return self.extra_attributes[attribute]() + except KeyError: + if default is undefined: + raise TypedAttributeLookupError("Attribute not found") from None + else: + return default diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__init__.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__init__.py new file mode 100644 index 0000000..1ca0fcf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__init__.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from typing import Any + +from ._eventloop import AsyncBackend as AsyncBackend +from ._resources import AsyncResource as AsyncResource +from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket +from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket +from ._sockets import IPAddressType as IPAddressType +from ._sockets import IPSockAddrType as IPSockAddrType +from ._sockets import SocketAttribute as SocketAttribute +from ._sockets import SocketListener as SocketListener +from ._sockets import SocketStream as SocketStream +from ._sockets import UDPPacketType as UDPPacketType +from ._sockets import UDPSocket as UDPSocket +from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType +from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket +from ._sockets import UNIXSocketStream as UNIXSocketStream +from ._streams import AnyByteReceiveStream as AnyByteReceiveStream +from ._streams import AnyByteSendStream as AnyByteSendStream +from ._streams import AnyByteStream as AnyByteStream +from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream +from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream +from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream +from ._streams import ByteReceiveStream as ByteReceiveStream +from ._streams import ByteSendStream as ByteSendStream +from ._streams import ByteStream as ByteStream +from ._streams import Listener as Listener +from ._streams import ObjectReceiveStream as ObjectReceiveStream +from ._streams import ObjectSendStream as ObjectSendStream +from ._streams import ObjectStream as ObjectStream +from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream +from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream +from ._streams import UnreliableObjectStream as UnreliableObjectStream +from ._subprocesses import Process as Process +from ._tasks import TaskGroup as TaskGroup +from ._tasks import TaskStatus as TaskStatus +from ._testing import TestRunner as TestRunner + +# Re-exported here, for backwards compatibility +# isort: off +from .._core._synchronization import ( + CapacityLimiter as CapacityLimiter, + Condition as Condition, + Event as Event, + Lock as Lock, + Semaphore as Semaphore, +) +from .._core._tasks import CancelScope as CancelScope +from ..from_thread import BlockingPortal as BlockingPortal + +# Re-export imports so they look like they live directly in this package +key: str +value: Any +for key, value in list(locals().items()): + if getattr(value, "__module__", "").startswith("anyio.abc."): + value.__module__ = __name__ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a6b829e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_eventloop.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_eventloop.cpython-310.pyc new file mode 100644 index 0000000..ea88592 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_eventloop.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_resources.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_resources.cpython-310.pyc new file mode 100644 index 0000000..320f4fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_resources.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_sockets.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_sockets.cpython-310.pyc new file mode 100644 index 0000000..55c7ad5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_sockets.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_streams.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_streams.cpython-310.pyc new file mode 100644 index 0000000..06b0444 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_streams.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-310.pyc new file mode 100644 index 0000000..5197433 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_subprocesses.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_tasks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_tasks.cpython-310.pyc new file mode 100644 index 0000000..175a6fc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_tasks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_testing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_testing.cpython-310.pyc new file mode 100644 index 0000000..6c721f6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/__pycache__/_testing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_eventloop.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_eventloop.py new file mode 100644 index 0000000..4470d83 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_eventloop.py @@ -0,0 +1,392 @@ +from __future__ import annotations + +import math +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncIterator, Awaitable, Mapping +from os import PathLike +from signal import Signals +from socket import AddressFamily, SocketKind, socket +from typing import ( + IO, + TYPE_CHECKING, + Any, + Callable, + ContextManager, + Sequence, + TypeVar, + overload, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from typing import Literal + + from .._core._synchronization import CapacityLimiter, Event + from .._core._tasks import CancelScope + from .._core._testing import TaskInfo + from ..from_thread import BlockingPortal + from ._sockets import ( + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, + ) + from ._subprocesses import Process + from ._tasks import TaskGroup + from ._testing import TestRunner + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + + +class AsyncBackend(metaclass=ABCMeta): + @classmethod + @abstractmethod + def run( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + options: dict[str, Any], + ) -> T_Retval: + """ + Run the given coroutine function in an asynchronous event loop. + + The current thread must not be already running an event loop. + + :param func: a coroutine function + :param args: positional arguments to ``func`` + :param kwargs: positional arguments to ``func`` + :param options: keyword arguments to call the backend ``run()`` implementation + with + :return: the return value of the coroutine function + """ + + @classmethod + @abstractmethod + def current_token(cls) -> object: + """ + + :return: + """ + + @classmethod + @abstractmethod + def current_time(cls) -> float: + """ + Return the current value of the event loop's internal clock. + + :return: the clock value (seconds) + """ + + @classmethod + @abstractmethod + def cancelled_exception_class(cls) -> type[BaseException]: + """Return the exception class that is raised in a task if it's cancelled.""" + + @classmethod + @abstractmethod + async def checkpoint(cls) -> None: + """ + Check if the task has been cancelled, and allow rescheduling of other tasks. + + This is effectively the same as running :meth:`checkpoint_if_cancelled` and then + :meth:`cancel_shielded_checkpoint`. + """ + + @classmethod + async def checkpoint_if_cancelled(cls) -> None: + """ + Check if the current task group has been cancelled. + + This will check if the task has been cancelled, but will not allow other tasks + to be scheduled if not. + + """ + if cls.current_effective_deadline() == -math.inf: + await cls.checkpoint() + + @classmethod + async def cancel_shielded_checkpoint(cls) -> None: + """ + Allow the rescheduling of other tasks. + + This will give other tasks the opportunity to run, but without checking if the + current task group has been cancelled, unlike with :meth:`checkpoint`. + + """ + with cls.create_cancel_scope(shield=True): + await cls.sleep(0) + + @classmethod + @abstractmethod + async def sleep(cls, delay: float) -> None: + """ + Pause the current task for the specified duration. + + :param delay: the duration, in seconds + """ + + @classmethod + @abstractmethod + def create_cancel_scope( + cls, *, deadline: float = math.inf, shield: bool = False + ) -> CancelScope: + pass + + @classmethod + @abstractmethod + def current_effective_deadline(cls) -> float: + """ + Return the nearest deadline among all the cancel scopes effective for the + current task. + + :return: + - a clock value from the event loop's internal clock + - ``inf`` if there is no deadline in effect + - ``-inf`` if the current scope has been cancelled + :rtype: float + """ + + @classmethod + @abstractmethod + def create_task_group(cls) -> TaskGroup: + pass + + @classmethod + @abstractmethod + def create_event(cls) -> Event: + pass + + @classmethod + @abstractmethod + def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + async def run_sync_in_worker_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + abandon_on_cancel: bool = False, + limiter: CapacityLimiter | None = None, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def check_cancelled(cls) -> None: + pass + + @classmethod + @abstractmethod + def run_async_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + pass + + @classmethod + @abstractmethod + def create_blocking_portal(cls) -> BlockingPortal: + pass + + @classmethod + @overload + async def open_process( + cls, + command: str | bytes, + *, + shell: Literal[True], + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: str | bytes | PathLike[str] | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, + ) -> Process: + pass + + @classmethod + @overload + async def open_process( + cls, + command: Sequence[str | bytes], + *, + shell: Literal[False], + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: str | bytes | PathLike[str] | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, + ) -> Process: + pass + + @classmethod + @abstractmethod + async def open_process( + cls, + command: str | bytes | Sequence[str | bytes], + *, + shell: bool, + stdin: int | IO[Any] | None, + stdout: int | IO[Any] | None, + stderr: int | IO[Any] | None, + cwd: str | bytes | PathLike[str] | None = None, + env: Mapping[str, str] | None = None, + start_new_session: bool = False, + ) -> Process: + pass + + @classmethod + @abstractmethod + def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: + pass + + @classmethod + @abstractmethod + async def connect_tcp( + cls, host: str, port: int, local_address: IPSockAddrType | None = None + ) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + def create_tcp_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + def create_unix_listener(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def create_udp_socket( + cls, + family: AddressFamily, + local_address: IPSockAddrType | None, + remote_address: IPSockAddrType | None, + reuse_port: bool, + ) -> UDPSocket | ConnectedUDPSocket: + pass + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: None + ) -> UNIXDatagramSocket: + ... + + @classmethod + @overload + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes + ) -> ConnectedUNIXDatagramSocket: + ... + + @classmethod + @abstractmethod + async def create_unix_datagram_socket( + cls, raw_socket: socket, remote_path: str | bytes | None + ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def getaddrinfo( + cls, + host: bytes | str | None, + port: str | int | None, + *, + family: int | AddressFamily = 0, + type: int | SocketKind = 0, + proto: int = 0, + flags: int = 0, + ) -> list[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int], + ] + ]: + pass + + @classmethod + @abstractmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + pass + + @classmethod + @abstractmethod + async def wait_socket_readable(cls, sock: socket) -> None: + pass + + @classmethod + @abstractmethod + async def wait_socket_writable(cls, sock: socket) -> None: + pass + + @classmethod + @abstractmethod + def current_default_thread_limiter(cls) -> CapacityLimiter: + pass + + @classmethod + @abstractmethod + def open_signal_receiver( + cls, *signals: Signals + ) -> ContextManager[AsyncIterator[Signals]]: + pass + + @classmethod + @abstractmethod + def get_current_task(cls) -> TaskInfo: + pass + + @classmethod + @abstractmethod + def get_running_tasks(cls) -> list[TaskInfo]: + pass + + @classmethod + @abstractmethod + async def wait_all_tasks_blocked(cls) -> None: + pass + + @classmethod + @abstractmethod + def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_resources.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_resources.py new file mode 100644 index 0000000..9693835 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_resources.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from types import TracebackType +from typing import TypeVar + +T = TypeVar("T") + + +class AsyncResource(metaclass=ABCMeta): + """ + Abstract base class for all closeable asynchronous resources. + + Works as an asynchronous context manager which returns the instance itself on enter, + and calls :meth:`aclose` on exit. + """ + + async def __aenter__(self: T) -> T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.aclose() + + @abstractmethod + async def aclose(self) -> None: + """Close the resource.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_sockets.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_sockets.py new file mode 100644 index 0000000..b321225 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_sockets.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +import socket +from abc import abstractmethod +from collections.abc import Callable, Collection, Mapping +from contextlib import AsyncExitStack +from io import IOBase +from ipaddress import IPv4Address, IPv6Address +from socket import AddressFamily +from types import TracebackType +from typing import Any, Tuple, TypeVar, Union + +from .._core._typedattr import ( + TypedAttributeProvider, + TypedAttributeSet, + typed_attribute, +) +from ._streams import ByteStream, Listener, UnreliableObjectStream +from ._tasks import TaskGroup + +IPAddressType = Union[str, IPv4Address, IPv6Address] +IPSockAddrType = Tuple[str, int] +SockAddrType = Union[IPSockAddrType, str] +UDPPacketType = Tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType = Tuple[bytes, str] +T_Retval = TypeVar("T_Retval") + + +class _NullAsyncContextManager: + async def __aenter__(self) -> None: + pass + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + return None + + +class SocketAttribute(TypedAttributeSet): + #: the address family of the underlying socket + family: AddressFamily = typed_attribute() + #: the local socket address of the underlying socket + local_address: SockAddrType = typed_attribute() + #: for IP addresses, the local port the underlying socket is bound to + local_port: int = typed_attribute() + #: the underlying stdlib socket object + raw_socket: socket.socket = typed_attribute() + #: the remote address the underlying socket is connected to + remote_address: SockAddrType = typed_attribute() + #: for IP addresses, the remote port the underlying socket is connected to + remote_port: int = typed_attribute() + + +class _SocketProvider(TypedAttributeProvider): + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + from .._core._sockets import convert_ipv6_sockaddr as convert + + attributes: dict[Any, Callable[[], Any]] = { + SocketAttribute.family: lambda: self._raw_socket.family, + SocketAttribute.local_address: lambda: convert( + self._raw_socket.getsockname() + ), + SocketAttribute.raw_socket: lambda: self._raw_socket, + } + try: + peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) + except OSError: + peername = None + + # Provide the remote address for connected sockets + if peername is not None: + attributes[SocketAttribute.remote_address] = lambda: peername + + # Provide local and remote ports for IP based sockets + if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): + attributes[SocketAttribute.local_port] = ( + lambda: self._raw_socket.getsockname()[1] + ) + if peername is not None: + remote_port = peername[1] + attributes[SocketAttribute.remote_port] = lambda: remote_port + + return attributes + + @property + @abstractmethod + def _raw_socket(self) -> socket.socket: + pass + + +class SocketStream(ByteStream, _SocketProvider): + """ + Transports bytes over a socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + +class UNIXSocketStream(SocketStream): + @abstractmethod + async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: + """ + Send file descriptors along with a message to the peer. + + :param message: a non-empty bytestring + :param fds: a collection of files (either numeric file descriptors or open file + or socket objects) + """ + + @abstractmethod + async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: + """ + Receive file descriptors along with a message from the peer. + + :param msglen: length of the message to expect from the peer + :param maxfds: maximum number of file descriptors to expect from the peer + :return: a tuple of (message, file descriptors) + """ + + +class SocketListener(Listener[SocketStream], _SocketProvider): + """ + Listens to incoming socket connections. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @abstractmethod + async def accept(self) -> SocketStream: + """Accept an incoming connection.""" + + async def serve( + self, + handler: Callable[[SocketStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + from .. import create_task_group + + async with AsyncExitStack() as stack: + if task_group is None: + task_group = await stack.enter_async_context(create_task_group()) + + while True: + stream = await self.accept() + task_group.start_soon(handler, stream) + + +class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): + """ + Represents an unconnected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + async def sendto(self, data: bytes, host: str, port: int) -> None: + """ + Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). + + """ + return await self.send((data, (host, port))) + + +class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents an connected UDP socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + +class UNIXDatagramSocket( + UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider +): + """ + Represents an unconnected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + async def sendto(self, data: bytes, path: str) -> None: + """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" + return await self.send((data, path)) + + +class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): + """ + Represents a connected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_streams.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_streams.py new file mode 100644 index 0000000..8c63868 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_streams.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +from abc import abstractmethod +from collections.abc import Callable +from typing import Any, Generic, TypeVar, Union + +from .._core._exceptions import EndOfStream +from .._core._typedattr import TypedAttributeProvider +from ._resources import AsyncResource +from ._tasks import TaskGroup + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class UnreliableObjectReceiveStream( + Generic[T_co], AsyncResource, TypedAttributeProvider +): + """ + An interface for receiving objects. + + This interface makes no guarantees that the received messages arrive in the order in + which they were sent, or that no messages are missed. + + Asynchronously iterating over objects of this type will yield objects matching the + given type parameter. + """ + + def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: + return self + + async def __anext__(self) -> T_co: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration + + @abstractmethod + async def receive(self) -> T_co: + """ + Receive the next item. + + :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly + closed + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectSendStream( + Generic[T_contra], AsyncResource, TypedAttributeProvider +): + """ + An interface for sending objects. + + This interface makes no guarantees that the messages sent will reach the + recipient(s) in the same order in which they were sent, or at all. + """ + + @abstractmethod + async def send(self, item: T_contra) -> None: + """ + Send an item to the peer(s). + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if the send stream has been explicitly + closed + :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable + due to external causes + """ + + +class UnreliableObjectStream( + UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] +): + """ + A bidirectional message stream which does not guarantee the order or reliability of + message delivery. + """ + + +class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): + """ + A receive message stream which guarantees that messages are received in the same + order in which they were sent, and that no messages are missed. + """ + + +class ObjectSendStream(UnreliableObjectSendStream[T_contra]): + """ + A send message stream which guarantees that messages are delivered in the same order + in which they were sent, without missing any messages in the middle. + """ + + +class ObjectStream( + ObjectReceiveStream[T_Item], + ObjectSendStream[T_Item], + UnreliableObjectStream[T_Item], +): + """ + A bidirectional message stream which guarantees the order and reliability of message + delivery. + """ + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +class ByteReceiveStream(AsyncResource, TypedAttributeProvider): + """ + An interface for receiving bytes from a single peer. + + Iterating this byte stream will yield a byte string of arbitrary length, but no more + than 65536 bytes. + """ + + def __aiter__(self) -> ByteReceiveStream: + return self + + async def __anext__(self) -> bytes: + try: + return await self.receive() + except EndOfStream: + raise StopAsyncIteration + + @abstractmethod + async def receive(self, max_bytes: int = 65536) -> bytes: + """ + Receive at most ``max_bytes`` bytes from the peer. + + .. note:: Implementors of this interface should not return an empty + :class:`bytes` object, and users should ignore them. + + :param max_bytes: maximum number of bytes to receive + :return: the received bytes + :raises ~anyio.EndOfStream: if this stream has been closed from the other end + """ + + +class ByteSendStream(AsyncResource, TypedAttributeProvider): + """An interface for sending bytes to a single peer.""" + + @abstractmethod + async def send(self, item: bytes) -> None: + """ + Send the given bytes to the peer. + + :param item: the bytes to send + """ + + +class ByteStream(ByteReceiveStream, ByteSendStream): + """A bidirectional byte stream.""" + + @abstractmethod + async def send_eof(self) -> None: + """ + Send an end-of-file indication to the peer. + + You should not try to send any further data to this stream after calling this + method. This method is idempotent (does nothing on successive calls). + """ + + +#: Type alias for all unreliable bytes-oriented receive streams. +AnyUnreliableByteReceiveStream = Union[ + UnreliableObjectReceiveStream[bytes], ByteReceiveStream +] +#: Type alias for all unreliable bytes-oriented send streams. +AnyUnreliableByteSendStream = Union[UnreliableObjectSendStream[bytes], ByteSendStream] +#: Type alias for all unreliable bytes-oriented streams. +AnyUnreliableByteStream = Union[UnreliableObjectStream[bytes], ByteStream] +#: Type alias for all bytes-oriented receive streams. +AnyByteReceiveStream = Union[ObjectReceiveStream[bytes], ByteReceiveStream] +#: Type alias for all bytes-oriented send streams. +AnyByteSendStream = Union[ObjectSendStream[bytes], ByteSendStream] +#: Type alias for all bytes-oriented streams. +AnyByteStream = Union[ObjectStream[bytes], ByteStream] + + +class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider): + """An interface for objects that let you accept incoming connections.""" + + @abstractmethod + async def serve( + self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None + ) -> None: + """ + Accept incoming connections as they come in and start tasks to handle them. + + :param handler: a callable that will be used to handle each accepted connection + :param task_group: the task group that will be used to start tasks for handling + each accepted connection (if omitted, an ad-hoc task group will be created) + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_subprocesses.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_subprocesses.py new file mode 100644 index 0000000..ce0564c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_subprocesses.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from abc import abstractmethod +from signal import Signals + +from ._resources import AsyncResource +from ._streams import ByteReceiveStream, ByteSendStream + + +class Process(AsyncResource): + """An asynchronous version of :class:`subprocess.Popen`.""" + + @abstractmethod + async def wait(self) -> int: + """ + Wait until the process exits. + + :return: the exit code of the process + """ + + @abstractmethod + def terminate(self) -> None: + """ + Terminates the process, gracefully if possible. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGTERM`` to the process. + + .. seealso:: :meth:`subprocess.Popen.terminate` + """ + + @abstractmethod + def kill(self) -> None: + """ + Kills the process. + + On Windows, this calls ``TerminateProcess()``. + On POSIX systems, this sends ``SIGKILL`` to the process. + + .. seealso:: :meth:`subprocess.Popen.kill` + """ + + @abstractmethod + def send_signal(self, signal: Signals) -> None: + """ + Send a signal to the subprocess. + + .. seealso:: :meth:`subprocess.Popen.send_signal` + + :param signal: the signal number (e.g. :data:`signal.SIGHUP`) + """ + + @property + @abstractmethod + def pid(self) -> int: + """The process ID of the process.""" + + @property + @abstractmethod + def returncode(self) -> int | None: + """ + The return code of the process. If the process has not yet terminated, this will + be ``None``. + """ + + @property + @abstractmethod + def stdin(self) -> ByteSendStream | None: + """The stream for the standard input of the process.""" + + @property + @abstractmethod + def stdout(self) -> ByteReceiveStream | None: + """The stream for the standard output of the process.""" + + @property + @abstractmethod + def stderr(self) -> ByteReceiveStream | None: + """The stream for the standard error output of the process.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_tasks.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_tasks.py new file mode 100644 index 0000000..7ad4938 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_tasks.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import sys +from abc import ABCMeta, abstractmethod +from collections.abc import Awaitable, Callable +from types import TracebackType +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from .._core._tasks import CancelScope + +T_Retval = TypeVar("T_Retval") +T_contra = TypeVar("T_contra", contravariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +class TaskStatus(Protocol[T_contra]): + @overload + def started(self: TaskStatus[None]) -> None: + ... + + @overload + def started(self, value: T_contra) -> None: + ... + + def started(self, value: T_contra | None = None) -> None: + """ + Signal that the task has started. + + :param value: object passed back to the starter of the task + """ + + +class TaskGroup(metaclass=ABCMeta): + """ + Groups several asynchronous tasks together. + + :ivar cancel_scope: the cancel scope inherited by all child tasks + :vartype cancel_scope: CancelScope + """ + + cancel_scope: CancelScope + + @abstractmethod + def start_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> None: + """ + Start a new task in this task group. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def start( + self, + func: Callable[..., Awaitable[Any]], + *args: object, + name: object = None, + ) -> Any: + """ + Start a new task and wait until it signals for readiness. + + :param func: a coroutine function + :param args: positional arguments to call the function with + :param name: name of the task, for the purposes of introspection and debugging + :return: the value passed to ``task_status.started()`` + :raises RuntimeError: if the task finishes without calling + ``task_status.started()`` + + .. versionadded:: 3.0 + """ + + @abstractmethod + async def __aenter__(self) -> TaskGroup: + """Enter the task group context and allow starting new tasks.""" + + @abstractmethod + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + """Exit the task group context waiting for all tasks to finish.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_testing.py b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_testing.py new file mode 100644 index 0000000..4d70b9e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/abc/_testing.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +import types +from abc import ABCMeta, abstractmethod +from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable +from typing import Any, TypeVar + +_T = TypeVar("_T") + + +class TestRunner(metaclass=ABCMeta): + """ + Encapsulates a running event loop. Every call made through this object will use the + same event loop. + """ + + def __enter__(self) -> TestRunner: + return self + + @abstractmethod + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool | None: + ... + + @abstractmethod + def run_asyncgen_fixture( + self, + fixture_func: Callable[..., AsyncGenerator[_T, Any]], + kwargs: dict[str, Any], + ) -> Iterable[_T]: + """ + Run an async generator fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: an iterator yielding the value yielded from the async generator + """ + + @abstractmethod + def run_fixture( + self, + fixture_func: Callable[..., Coroutine[Any, Any, _T]], + kwargs: dict[str, Any], + ) -> _T: + """ + Run an async fixture. + + :param fixture_func: the fixture function + :param kwargs: keyword arguments to call the fixture function with + :return: the return value of the fixture function + """ + + @abstractmethod + def run_test( + self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] + ) -> None: + """ + Run an async test function. + + :param test_func: the test function + :param kwargs: keyword arguments to call the test function with + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/from_thread.py b/.pythonlibs/lib/python3.10/site-packages/anyio/from_thread.py new file mode 100644 index 0000000..4a98703 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/from_thread.py @@ -0,0 +1,476 @@ +from __future__ import annotations + +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from concurrent.futures import FIRST_COMPLETED, Future, ThreadPoolExecutor, wait +from contextlib import AbstractContextManager, contextmanager +from inspect import isawaitable +from types import TracebackType +from typing import ( + Any, + AsyncContextManager, + ContextManager, + Generic, + Iterable, + TypeVar, + cast, + overload, +) + +from ._core import _eventloop +from ._core._eventloop import get_async_backend, get_cancelled_exc_class, threadlocals +from ._core._synchronization import Event +from ._core._tasks import CancelScope, create_task_group +from .abc import AsyncBackend +from .abc._tasks import TaskStatus + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +T_co = TypeVar("T_co", covariant=True) +PosArgsT = TypeVarTuple("PosArgsT") + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT] +) -> T_Retval: + """ + Call a coroutine function from a worker thread. + + :param func: a coroutine function + :param args: positional arguments for the callable + :return: the return value of the coroutine function + + """ + try: + async_backend = threadlocals.current_async_backend + token = threadlocals.current_token + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + return async_backend.run_async_from_thread(func, args, token=token) + + +def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] +) -> T_Retval: + """ + Call a function in the event loop thread from a worker thread. + + :param func: a callable + :param args: positional arguments for the callable + :return: the return value of the callable + + """ + try: + async_backend = threadlocals.current_async_backend + token = threadlocals.current_token + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + return async_backend.run_sync_from_thread(func, args, token=token) + + +class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): + _enter_future: Future[T_co] + _exit_future: Future[bool | None] + _exit_event: Event + _exit_exc_info: tuple[ + type[BaseException] | None, BaseException | None, TracebackType | None + ] = (None, None, None) + + def __init__(self, async_cm: AsyncContextManager[T_co], portal: BlockingPortal): + self._async_cm = async_cm + self._portal = portal + + async def run_async_cm(self) -> bool | None: + try: + self._exit_event = Event() + value = await self._async_cm.__aenter__() + except BaseException as exc: + self._enter_future.set_exception(exc) + raise + else: + self._enter_future.set_result(value) + + try: + # Wait for the sync context manager to exit. + # This next statement can raise `get_cancelled_exc_class()` if + # something went wrong in a task group in this async context + # manager. + await self._exit_event.wait() + finally: + # In case of cancellation, it could be that we end up here before + # `_BlockingAsyncContextManager.__exit__` is called, and an + # `_exit_exc_info` has been set. + result = await self._async_cm.__aexit__(*self._exit_exc_info) + return result + + def __enter__(self) -> T_co: + self._enter_future = Future() + self._exit_future = self._portal.start_task_soon(self.run_async_cm) + return self._enter_future.result() + + def __exit__( + self, + __exc_type: type[BaseException] | None, + __exc_value: BaseException | None, + __traceback: TracebackType | None, + ) -> bool | None: + self._exit_exc_info = __exc_type, __exc_value, __traceback + self._portal.call(self._exit_event.set) + return self._exit_future.result() + + +class _BlockingPortalTaskStatus(TaskStatus): + def __init__(self, future: Future): + self._future = future + + def started(self, value: object = None) -> None: + self._future.set_result(value) + + +class BlockingPortal: + """An object that lets external threads run code in an asynchronous event loop.""" + + def __new__(cls) -> BlockingPortal: + return get_async_backend().create_blocking_portal() + + def __init__(self) -> None: + self._event_loop_thread_id: int | None = threading.get_ident() + self._stop_event = Event() + self._task_group = create_task_group() + self._cancelled_exc_class = get_cancelled_exc_class() + + async def __aenter__(self) -> BlockingPortal: + await self._task_group.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool | None: + await self.stop() + return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) + + def _check_running(self) -> None: + if self._event_loop_thread_id is None: + raise RuntimeError("This portal is not running") + if self._event_loop_thread_id == threading.get_ident(): + raise RuntimeError( + "This method cannot be called from the event loop thread" + ) + + async def sleep_until_stopped(self) -> None: + """Sleep until :meth:`stop` is called.""" + await self._stop_event.wait() + + async def stop(self, cancel_remaining: bool = False) -> None: + """ + Signal the portal to shut down. + + This marks the portal as no longer accepting new calls and exits from + :meth:`sleep_until_stopped`. + + :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` + to let them finish before returning + + """ + self._event_loop_thread_id = None + self._stop_event.set() + if cancel_remaining: + self._task_group.cancel_scope.cancel() + + async def _call_func( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + future: Future[T_Retval], + ) -> None: + def callback(f: Future[T_Retval]) -> None: + if f.cancelled() and self._event_loop_thread_id not in ( + None, + threading.get_ident(), + ): + self.call(scope.cancel) + + try: + retval_or_awaitable = func(*args, **kwargs) + if isawaitable(retval_or_awaitable): + with CancelScope() as scope: + if future.cancelled(): + scope.cancel() + else: + future.add_done_callback(callback) + + retval = await retval_or_awaitable + else: + retval = retval_or_awaitable + except self._cancelled_exc_class: + future.cancel() + future.set_running_or_notify_cancel() + except BaseException as exc: + if not future.cancelled(): + future.set_exception(exc) + + # Let base exceptions fall through + if not isinstance(exc, Exception): + raise + else: + if not future.cancelled(): + future.set_result(retval) + finally: + scope = None # type: ignore[assignment] + + def _spawn_task_from_thread( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + args: tuple[Unpack[PosArgsT]], + kwargs: dict[str, Any], + name: object, + future: Future[T_Retval], + ) -> None: + """ + Spawn a new task using the given callable. + + Implementors must ensure that the future is resolved when the task finishes. + + :param func: a callable + :param args: positional arguments to be passed to the callable + :param kwargs: keyword arguments to be passed to the callable + :param name: name of the task (will be coerced to a string if not ``None``) + :param future: a future that will resolve to the return value of the callable, + or the exception raised during its execution + + """ + raise NotImplementedError + + @overload + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + ) -> T_Retval: + ... + + @overload + def call( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: + ... + + def call( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + ) -> T_Retval: + """ + Call the given function in the event loop thread. + + If the callable returns a coroutine object, it is awaited on. + + :param func: any callable + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + + """ + return cast(T_Retval, self.start_task_soon(func, *args).result()) + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + ... + + @overload + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + ... + + def start_task_soon( + self, + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], + *args: Unpack[PosArgsT], + name: object = None, + ) -> Future[T_Retval]: + """ + Start a task in the portal's task group. + + The task will be run inside a cancel scope which can be cancelled by cancelling + the returned future. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a future that resolves with the return value of the callable if the + task completes successfully, or with the exception raised in the task + :raises RuntimeError: if the portal is not running or if this method is called + from within the event loop thread + :rtype: concurrent.futures.Future[T_Retval] + + .. versionadded:: 3.0 + + """ + self._check_running() + f: Future[T_Retval] = Future() + self._spawn_task_from_thread(func, args, {}, name, f) + return f + + def start_task( + self, + func: Callable[..., Awaitable[T_Retval]], + *args: object, + name: object = None, + ) -> tuple[Future[T_Retval], Any]: + """ + Start a task in the portal's task group and wait until it signals for readiness. + + This method works the same way as :meth:`.abc.TaskGroup.start`. + + :param func: the target function + :param args: positional arguments passed to ``func`` + :param name: name of the task (will be coerced to a string if not ``None``) + :return: a tuple of (future, task_status_value) where the ``task_status_value`` + is the value passed to ``task_status.started()`` from within the target + function + :rtype: tuple[concurrent.futures.Future[T_Retval], Any] + + .. versionadded:: 3.0 + + """ + + def task_done(future: Future[T_Retval]) -> None: + if not task_status_future.done(): + if future.cancelled(): + task_status_future.cancel() + elif future.exception(): + task_status_future.set_exception(future.exception()) + else: + exc = RuntimeError( + "Task exited without calling task_status.started()" + ) + task_status_future.set_exception(exc) + + self._check_running() + task_status_future: Future = Future() + task_status = _BlockingPortalTaskStatus(task_status_future) + f: Future = Future() + f.add_done_callback(task_done) + self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) + return f, task_status_future.result() + + def wrap_async_context_manager( + self, cm: AsyncContextManager[T_co] + ) -> ContextManager[T_co]: + """ + Wrap an async context manager as a synchronous context manager via this portal. + + Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping + in the middle until the synchronous context manager exits. + + :param cm: an asynchronous context manager + :return: a synchronous context manager + + .. versionadded:: 2.1 + + """ + return _BlockingAsyncContextManager(cm, self) + + +@contextmanager +def start_blocking_portal( + backend: str = "asyncio", backend_options: dict[str, Any] | None = None +) -> Generator[BlockingPortal, Any, None]: + """ + Start a new event loop in a new thread and run a blocking portal in its main task. + + The parameters are the same as for :func:`~anyio.run`. + + :param backend: name of the backend + :param backend_options: backend options + :return: a context manager that yields a blocking portal + + .. versionchanged:: 3.0 + Usage as a context manager is now required. + + """ + + async def run_portal() -> None: + async with BlockingPortal() as portal_: + if future.set_running_or_notify_cancel(): + future.set_result(portal_) + await portal_.sleep_until_stopped() + + future: Future[BlockingPortal] = Future() + with ThreadPoolExecutor(1) as executor: + run_future = executor.submit( + _eventloop.run, # type: ignore[arg-type] + run_portal, + backend=backend, + backend_options=backend_options, + ) + try: + wait( + cast(Iterable[Future], [run_future, future]), + return_when=FIRST_COMPLETED, + ) + except BaseException: + future.cancel() + run_future.cancel() + raise + + if future.done(): + portal = future.result() + cancel_remaining_tasks = False + try: + yield portal + except BaseException: + cancel_remaining_tasks = True + raise + finally: + try: + portal.call(portal.stop, cancel_remaining_tasks) + except RuntimeError: + pass + + run_future.result() + + +def check_cancelled() -> None: + """ + Check if the cancel scope of the host task's running the current worker thread has + been cancelled. + + If the host task's current cancel scope has indeed been cancelled, the + backend-specific cancellation exception will be raised. + + :raises RuntimeError: if the current thread was not spawned by + :func:`.to_thread.run_sync` + + """ + try: + async_backend: AsyncBackend = threadlocals.current_async_backend + except AttributeError: + raise RuntimeError( + "This function can only be run from an AnyIO worker thread" + ) from None + + async_backend.check_cancelled() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/lowlevel.py b/.pythonlibs/lib/python3.10/site-packages/anyio/lowlevel.py new file mode 100644 index 0000000..a9e10f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/lowlevel.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import enum +from dataclasses import dataclass +from typing import Any, Generic, Literal, TypeVar, overload +from weakref import WeakKeyDictionary + +from ._core._eventloop import get_async_backend + +T = TypeVar("T") +D = TypeVar("D") + + +async def checkpoint() -> None: + """ + Check for cancellation and allow the scheduler to switch to another task. + + Equivalent to (but more efficient than):: + + await checkpoint_if_cancelled() + await cancel_shielded_checkpoint() + + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint() + + +async def checkpoint_if_cancelled() -> None: + """ + Enter a checkpoint if the enclosing cancel scope has been cancelled. + + This does not allow the scheduler to switch to a different task. + + .. versionadded:: 3.0 + + """ + await get_async_backend().checkpoint_if_cancelled() + + +async def cancel_shielded_checkpoint() -> None: + """ + Allow the scheduler to switch to another task but without checking for cancellation. + + Equivalent to (but potentially more efficient than):: + + with CancelScope(shield=True): + await checkpoint() + + + .. versionadded:: 3.0 + + """ + await get_async_backend().cancel_shielded_checkpoint() + + +def current_token() -> object: + """ + Return a backend specific token object that can be used to get back to the event + loop. + + """ + return get_async_backend().current_token() + + +_run_vars: WeakKeyDictionary[Any, dict[str, Any]] = WeakKeyDictionary() +_token_wrappers: dict[Any, _TokenWrapper] = {} + + +@dataclass(frozen=True) +class _TokenWrapper: + __slots__ = "_token", "__weakref__" + _token: object + + +class _NoValueSet(enum.Enum): + NO_VALUE_SET = enum.auto() + + +class RunvarToken(Generic[T]): + __slots__ = "_var", "_value", "_redeemed" + + def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): + self._var = var + self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value + self._redeemed = False + + +class RunVar(Generic[T]): + """ + Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. + """ + + __slots__ = "_name", "_default" + + NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET + + _token_wrappers: set[_TokenWrapper] = set() + + def __init__( + self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ): + self._name = name + self._default = default + + @property + def _current_vars(self) -> dict[str, T]: + token = current_token() + try: + return _run_vars[token] + except KeyError: + run_vars = _run_vars[token] = {} + return run_vars + + @overload + def get(self, default: D) -> T | D: + ... + + @overload + def get(self) -> T: + ... + + def get( + self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET + ) -> T | D: + try: + return self._current_vars[self._name] + except KeyError: + if default is not RunVar.NO_VALUE_SET: + return default + elif self._default is not RunVar.NO_VALUE_SET: + return self._default + + raise LookupError( + f'Run variable "{self._name}" has no value and no default set' + ) + + def set(self, value: T) -> RunvarToken[T]: + current_vars = self._current_vars + token = RunvarToken(self, current_vars.get(self._name, RunVar.NO_VALUE_SET)) + current_vars[self._name] = value + return token + + def reset(self, token: RunvarToken[T]) -> None: + if token._var is not self: + raise ValueError("This token does not belong to this RunVar") + + if token._redeemed: + raise ValueError("This token has already been used") + + if token._value is _NoValueSet.NO_VALUE_SET: + try: + del self._current_vars[self._name] + except KeyError: + pass + else: + self._current_vars[self._name] = token._value + + token._redeemed = True + + def __repr__(self) -> str: + return f"" diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/py.typed b/.pythonlibs/lib/python3.10/site-packages/anyio/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/pytest_plugin.py b/.pythonlibs/lib/python3.10/site-packages/anyio/pytest_plugin.py new file mode 100644 index 0000000..a8dd6f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/pytest_plugin.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +from collections.abc import Iterator +from contextlib import ExitStack, contextmanager +from inspect import isasyncgenfunction, iscoroutinefunction +from typing import Any, Dict, Tuple, cast + +import pytest +import sniffio + +from ._core._eventloop import get_all_backends, get_async_backend +from .abc import TestRunner + +_current_runner: TestRunner | None = None +_runner_stack: ExitStack | None = None +_runner_leases = 0 + + +def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: + if isinstance(backend, str): + return backend, {} + elif isinstance(backend, tuple) and len(backend) == 2: + if isinstance(backend[0], str) and isinstance(backend[1], dict): + return cast(Tuple[str, Dict[str, Any]], backend) + + raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") + + +@contextmanager +def get_runner( + backend_name: str, backend_options: dict[str, Any] +) -> Iterator[TestRunner]: + global _current_runner, _runner_leases, _runner_stack + if _current_runner is None: + asynclib = get_async_backend(backend_name) + _runner_stack = ExitStack() + if sniffio.current_async_library_cvar.get(None) is None: + # Since we're in control of the event loop, we can cache the name of the + # async library + token = sniffio.current_async_library_cvar.set(backend_name) + _runner_stack.callback(sniffio.current_async_library_cvar.reset, token) + + backend_options = backend_options or {} + _current_runner = _runner_stack.enter_context( + asynclib.create_test_runner(backend_options) + ) + + _runner_leases += 1 + try: + yield _current_runner + finally: + _runner_leases -= 1 + if not _runner_leases: + assert _runner_stack is not None + _runner_stack.close() + _runner_stack = _current_runner = None + + +def pytest_configure(config: Any) -> None: + config.addinivalue_line( + "markers", + "anyio: mark the (coroutine function) test to be run " + "asynchronously via anyio.", + ) + + +def pytest_fixture_setup(fixturedef: Any, request: Any) -> None: + def wrapper(*args, anyio_backend, **kwargs): # type: ignore[no-untyped-def] + backend_name, backend_options = extract_backend_and_options(anyio_backend) + if has_backend_arg: + kwargs["anyio_backend"] = anyio_backend + + with get_runner(backend_name, backend_options) as runner: + if isasyncgenfunction(func): + yield from runner.run_asyncgen_fixture(func, kwargs) + else: + yield runner.run_fixture(func, kwargs) + + # Only apply this to coroutine functions and async generator functions in requests + # that involve the anyio_backend fixture + func = fixturedef.func + if isasyncgenfunction(func) or iscoroutinefunction(func): + if "anyio_backend" in request.fixturenames: + has_backend_arg = "anyio_backend" in fixturedef.argnames + fixturedef.func = wrapper + if not has_backend_arg: + fixturedef.argnames += ("anyio_backend",) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None: + if collector.istestfunction(obj, name): + inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj + if iscoroutinefunction(inner_func): + marker = collector.get_closest_marker("anyio") + own_markers = getattr(obj, "pytestmark", ()) + if marker or any(marker.name == "anyio" for marker in own_markers): + pytest.mark.usefixtures("anyio_backend")(obj) + + +@pytest.hookimpl(tryfirst=True) +def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: + def run_with_hypothesis(**kwargs: Any) -> None: + with get_runner(backend_name, backend_options) as runner: + runner.run_test(original_func, kwargs) + + backend = pyfuncitem.funcargs.get("anyio_backend") + if backend: + backend_name, backend_options = extract_backend_and_options(backend) + + if hasattr(pyfuncitem.obj, "hypothesis"): + # Wrap the inner test function unless it's already wrapped + original_func = pyfuncitem.obj.hypothesis.inner_test + if original_func.__qualname__ != run_with_hypothesis.__qualname__: + if iscoroutinefunction(original_func): + pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis + + return None + + if iscoroutinefunction(pyfuncitem.obj): + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + with get_runner(backend_name, backend_options) as runner: + runner.run_test(pyfuncitem.obj, testargs) + + return True + + return None + + +@pytest.fixture(scope="module", params=get_all_backends()) +def anyio_backend(request: Any) -> Any: + return request.param + + +@pytest.fixture +def anyio_backend_name(anyio_backend: Any) -> str: + if isinstance(anyio_backend, str): + return anyio_backend + else: + return anyio_backend[0] + + +@pytest.fixture +def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: + if isinstance(anyio_backend, str): + return {} + else: + return anyio_backend[1] diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__init__.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..523d025 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/buffered.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/buffered.cpython-310.pyc new file mode 100644 index 0000000..99d7d9c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/buffered.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/file.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/file.cpython-310.pyc new file mode 100644 index 0000000..49fea03 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/file.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/memory.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/memory.cpython-310.pyc new file mode 100644 index 0000000..4e48b8d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/memory.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/stapled.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/stapled.cpython-310.pyc new file mode 100644 index 0000000..1c9103e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/stapled.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/text.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/text.cpython-310.pyc new file mode 100644 index 0000000..030e8bd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/text.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/tls.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/tls.cpython-310.pyc new file mode 100644 index 0000000..ad4b6d8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/__pycache__/tls.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/buffered.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/buffered.py new file mode 100644 index 0000000..f5d5e83 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/buffered.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field +from typing import Any + +from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead +from ..abc import AnyByteReceiveStream, ByteReceiveStream + + +@dataclass(eq=False) +class BufferedByteReceiveStream(ByteReceiveStream): + """ + Wraps any bytes-based receive stream and uses a buffer to provide sophisticated + receiving capabilities in the form of a byte stream. + """ + + receive_stream: AnyByteReceiveStream + _buffer: bytearray = field(init=False, default_factory=bytearray) + _closed: bool = field(init=False, default=False) + + async def aclose(self) -> None: + await self.receive_stream.aclose() + self._closed = True + + @property + def buffer(self) -> bytes: + """The bytes currently in the buffer.""" + return bytes(self._buffer) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.receive_stream.extra_attributes + + async def receive(self, max_bytes: int = 65536) -> bytes: + if self._closed: + raise ClosedResourceError + + if self._buffer: + chunk = bytes(self._buffer[:max_bytes]) + del self._buffer[:max_bytes] + return chunk + elif isinstance(self.receive_stream, ByteReceiveStream): + return await self.receive_stream.receive(max_bytes) + else: + # With a bytes-oriented object stream, we need to handle any surplus bytes + # we get from the receive() call + chunk = await self.receive_stream.receive() + if len(chunk) > max_bytes: + # Save the surplus bytes in the buffer + self._buffer.extend(chunk[max_bytes:]) + return chunk[:max_bytes] + else: + return chunk + + async def receive_exactly(self, nbytes: int) -> bytes: + """ + Read exactly the given amount of bytes from the stream. + + :param nbytes: the number of bytes to read + :return: the bytes read + :raises ~anyio.IncompleteRead: if the stream was closed before the requested + amount of bytes could be read from the stream + + """ + while True: + remaining = nbytes - len(self._buffer) + if remaining <= 0: + retval = self._buffer[:nbytes] + del self._buffer[:nbytes] + return bytes(retval) + + try: + if isinstance(self.receive_stream, ByteReceiveStream): + chunk = await self.receive_stream.receive(remaining) + else: + chunk = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + self._buffer.extend(chunk) + + async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: + """ + Read from the stream until the delimiter is found or max_bytes have been read. + + :param delimiter: the marker to look for in the stream + :param max_bytes: maximum number of bytes that will be read before raising + :exc:`~anyio.DelimiterNotFound` + :return: the bytes read (not including the delimiter) + :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter + was found + :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the + bytes read up to the maximum allowed + + """ + delimiter_size = len(delimiter) + offset = 0 + while True: + # Check if the delimiter can be found in the current buffer + index = self._buffer.find(delimiter, offset) + if index >= 0: + found = self._buffer[:index] + del self._buffer[: index + len(delimiter) :] + return bytes(found) + + # Check if the buffer is already at or over the limit + if len(self._buffer) >= max_bytes: + raise DelimiterNotFound(max_bytes) + + # Read more data into the buffer from the socket + try: + data = await self.receive_stream.receive() + except EndOfStream as exc: + raise IncompleteRead from exc + + # Move the offset forward and add the new data to the buffer + offset = max(len(self._buffer) - delimiter_size + 1, 0) + self._buffer.extend(data) diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/file.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/file.py new file mode 100644 index 0000000..f492464 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/file.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping +from io import SEEK_SET, UnsupportedOperation +from os import PathLike +from pathlib import Path +from typing import Any, BinaryIO, cast + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + TypedAttributeSet, + to_thread, + typed_attribute, +) +from ..abc import ByteReceiveStream, ByteSendStream + + +class FileStreamAttribute(TypedAttributeSet): + #: the open file descriptor + file: BinaryIO = typed_attribute() + #: the path of the file on the file system, if available (file must be a real file) + path: Path = typed_attribute() + #: the file number, if available (file must be a real file or a TTY) + fileno: int = typed_attribute() + + +class _BaseFileStream: + def __init__(self, file: BinaryIO): + self._file = file + + async def aclose(self) -> None: + await to_thread.run_sync(self._file.close) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict[Any, Callable[[], Any]] = { + FileStreamAttribute.file: lambda: self._file, + } + + if hasattr(self._file, "name"): + attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) + + try: + self._file.fileno() + except UnsupportedOperation: + pass + else: + attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() + + return attributes + + +class FileReadStream(_BaseFileStream, ByteReceiveStream): + """ + A byte stream that reads from a file in the file system. + + :param file: a file that has been opened for reading in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: + """ + Create a file read stream by opening the given file. + + :param path: path of the file to read from + + """ + file = await to_thread.run_sync(Path(path).open, "rb") + return cls(cast(BinaryIO, file)) + + async def receive(self, max_bytes: int = 65536) -> bytes: + try: + data = await to_thread.run_sync(self._file.read, max_bytes) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc + + if data: + return data + else: + raise EndOfStream + + async def seek(self, position: int, whence: int = SEEK_SET) -> int: + """ + Seek the file to the given position. + + .. seealso:: :meth:`io.IOBase.seek` + + .. note:: Not all file descriptors are seekable. + + :param position: position to seek the file to + :param whence: controls how ``position`` is interpreted + :return: the new absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.seek, position, whence) + + async def tell(self) -> int: + """ + Return the current stream position. + + .. note:: Not all file descriptors are seekable. + + :return: the current absolute position + :raises OSError: if the file is not seekable + + """ + return await to_thread.run_sync(self._file.tell) + + +class FileWriteStream(_BaseFileStream, ByteSendStream): + """ + A byte stream that writes to a file in the file system. + + :param file: a file that has been opened for writing in binary mode + + .. versionadded:: 3.0 + """ + + @classmethod + async def from_path( + cls, path: str | PathLike[str], append: bool = False + ) -> FileWriteStream: + """ + Create a file write stream by opening the given file for writing. + + :param path: path of the file to write to + :param append: if ``True``, open the file for appending; if ``False``, any + existing file at the given path will be truncated + + """ + mode = "ab" if append else "wb" + file = await to_thread.run_sync(Path(path).open, mode) + return cls(cast(BinaryIO, file)) + + async def send(self, item: bytes) -> None: + try: + await to_thread.run_sync(self._file.write, item) + except ValueError: + raise ClosedResourceError from None + except OSError as exc: + raise BrokenResourceError from exc diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/memory.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/memory.py new file mode 100644 index 0000000..bc2425b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/memory.py @@ -0,0 +1,283 @@ +from __future__ import annotations + +from collections import OrderedDict, deque +from dataclasses import dataclass, field +from types import TracebackType +from typing import Generic, NamedTuple, TypeVar + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + WouldBlock, +) +from ..abc import Event, ObjectReceiveStream, ObjectSendStream +from ..lowlevel import checkpoint + +T_Item = TypeVar("T_Item") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class MemoryObjectStreamStatistics(NamedTuple): + current_buffer_used: int #: number of items stored in the buffer + #: maximum number of items that can be stored on this stream (or :data:`math.inf`) + max_buffer_size: float + open_send_streams: int #: number of unclosed clones of the send stream + open_receive_streams: int #: number of unclosed clones of the receive stream + #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` + tasks_waiting_send: int + #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` + tasks_waiting_receive: int + + +@dataclass(eq=False) +class MemoryObjectStreamState(Generic[T_Item]): + max_buffer_size: float = field() + buffer: deque[T_Item] = field(init=False, default_factory=deque) + open_send_channels: int = field(init=False, default=0) + open_receive_channels: int = field(init=False, default=0) + waiting_receivers: OrderedDict[Event, list[T_Item]] = field( + init=False, default_factory=OrderedDict + ) + waiting_senders: OrderedDict[Event, T_Item] = field( + init=False, default_factory=OrderedDict + ) + + def statistics(self) -> MemoryObjectStreamStatistics: + return MemoryObjectStreamStatistics( + len(self.buffer), + self.max_buffer_size, + self.open_send_channels, + self.open_receive_channels, + len(self.waiting_senders), + len(self.waiting_receivers), + ) + + +@dataclass(eq=False) +class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]): + _state: MemoryObjectStreamState[T_co] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_receive_channels += 1 + + def receive_nowait(self) -> T_co: + """ + Receive the next item if it can be done without waiting. + + :return: the received item + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been + closed from the sending end + :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks + waiting to send + + """ + if self._closed: + raise ClosedResourceError + + if self._state.waiting_senders: + # Get the item from the next sender + send_event, item = self._state.waiting_senders.popitem(last=False) + self._state.buffer.append(item) + send_event.set() + + if self._state.buffer: + return self._state.buffer.popleft() + elif not self._state.open_send_channels: + raise EndOfStream + + raise WouldBlock + + async def receive(self) -> T_co: + await checkpoint() + try: + return self.receive_nowait() + except WouldBlock: + # Add ourselves in the queue + receive_event = Event() + container: list[T_co] = [] + self._state.waiting_receivers[receive_event] = container + + try: + await receive_event.wait() + finally: + self._state.waiting_receivers.pop(receive_event, None) + + if container: + return container[0] + else: + raise EndOfStream + + def clone(self) -> MemoryObjectReceiveStream[T_co]: + """ + Create a clone of this receive stream. + + Each clone can be closed separately. Only when all clones have been closed will + the receiving end of the memory stream be considered closed by the sending ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectReceiveStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_receive_channels -= 1 + if self._state.open_receive_channels == 0: + send_events = list(self._state.waiting_senders.keys()) + for event in send_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectReceiveStream[T_co]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + +@dataclass(eq=False) +class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]): + _state: MemoryObjectStreamState[T_contra] + _closed: bool = field(init=False, default=False) + + def __post_init__(self) -> None: + self._state.open_send_channels += 1 + + def send_nowait(self, item: T_contra) -> None: + """ + Send an item immediately if it can be done without waiting. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting + to receive + + """ + if self._closed: + raise ClosedResourceError + if not self._state.open_receive_channels: + raise BrokenResourceError + + if self._state.waiting_receivers: + receive_event, container = self._state.waiting_receivers.popitem(last=False) + container.append(item) + receive_event.set() + elif len(self._state.buffer) < self._state.max_buffer_size: + self._state.buffer.append(item) + else: + raise WouldBlock + + async def send(self, item: T_contra) -> None: + """ + Send an item to the stream. + + If the buffer is full, this method blocks until there is again room in the + buffer or the item can be sent directly to a receiver. + + :param item: the item to send + :raises ~anyio.ClosedResourceError: if this send stream has been closed + :raises ~anyio.BrokenResourceError: if the stream has been closed from the + receiving end + + """ + await checkpoint() + try: + self.send_nowait(item) + except WouldBlock: + # Wait until there's someone on the receiving end + send_event = Event() + self._state.waiting_senders[send_event] = item + try: + await send_event.wait() + except BaseException: + self._state.waiting_senders.pop(send_event, None) + raise + + if self._state.waiting_senders.pop(send_event, None): + raise BrokenResourceError from None + + def clone(self) -> MemoryObjectSendStream[T_contra]: + """ + Create a clone of this send stream. + + Each clone can be closed separately. Only when all clones have been closed will + the sending end of the memory stream be considered closed by the receiving ends. + + :return: the cloned stream + + """ + if self._closed: + raise ClosedResourceError + + return MemoryObjectSendStream(_state=self._state) + + def close(self) -> None: + """ + Close the stream. + + This works the exact same way as :meth:`aclose`, but is provided as a special + case for the benefit of synchronous callbacks. + + """ + if not self._closed: + self._closed = True + self._state.open_send_channels -= 1 + if self._state.open_send_channels == 0: + receive_events = list(self._state.waiting_receivers.keys()) + self._state.waiting_receivers.clear() + for event in receive_events: + event.set() + + async def aclose(self) -> None: + self.close() + + def statistics(self) -> MemoryObjectStreamStatistics: + """ + Return statistics about the current state of this stream. + + .. versionadded:: 3.0 + """ + return self._state.statistics() + + def __enter__(self) -> MemoryObjectSendStream[T_contra]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/stapled.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/stapled.py new file mode 100644 index 0000000..80f64a2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/stapled.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from typing import Any, Generic, TypeVar + +from ..abc import ( + ByteReceiveStream, + ByteSendStream, + ByteStream, + Listener, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + TaskGroup, +) + +T_Item = TypeVar("T_Item") +T_Stream = TypeVar("T_Stream") + + +@dataclass(eq=False) +class StapledByteStream(ByteStream): + """ + Combines two byte streams into a single, bidirectional byte stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ByteSendStream send_stream: the sending byte stream + :param ByteReceiveStream receive_stream: the receiving byte stream + """ + + send_stream: ByteSendStream + receive_stream: ByteReceiveStream + + async def receive(self, max_bytes: int = 65536) -> bytes: + return await self.receive_stream.receive(max_bytes) + + async def send(self, item: bytes) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]): + """ + Combines two object streams into a single, bidirectional object stream. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param ObjectSendStream send_stream: the sending object stream + :param ObjectReceiveStream receive_stream: the receiving object stream + """ + + send_stream: ObjectSendStream[T_Item] + receive_stream: ObjectReceiveStream[T_Item] + + async def receive(self) -> T_Item: + return await self.receive_stream.receive() + + async def send(self, item: T_Item) -> None: + await self.send_stream.send(item) + + async def send_eof(self) -> None: + await self.send_stream.aclose() + + async def aclose(self) -> None: + await self.send_stream.aclose() + await self.receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.send_stream.extra_attributes, + **self.receive_stream.extra_attributes, + } + + +@dataclass(eq=False) +class MultiListener(Generic[T_Stream], Listener[T_Stream]): + """ + Combines multiple listeners into one, serving connections from all of them at once. + + Any MultiListeners in the given collection of listeners will have their listeners + moved into this one. + + Extra attributes are provided from each listener, with each successive listener + overriding any conflicting attributes from the previous one. + + :param listeners: listeners to serve + :type listeners: Sequence[Listener[T_Stream]] + """ + + listeners: Sequence[Listener[T_Stream]] + + def __post_init__(self) -> None: + listeners: list[Listener[T_Stream]] = [] + for listener in self.listeners: + if isinstance(listener, MultiListener): + listeners.extend(listener.listeners) + del listener.listeners[:] # type: ignore[attr-defined] + else: + listeners.append(listener) + + self.listeners = listeners + + async def serve( + self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None + ) -> None: + from .. import create_task_group + + async with create_task_group() as tg: + for listener in self.listeners: + tg.start_soon(listener.serve, handler, task_group) + + async def aclose(self) -> None: + for listener in self.listeners: + await listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + attributes: dict = {} + for listener in self.listeners: + attributes.update(listener.extra_attributes) + + return attributes diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/text.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/text.py new file mode 100644 index 0000000..f1a1127 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/text.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import codecs +from collections.abc import Callable, Mapping +from dataclasses import InitVar, dataclass, field +from typing import Any + +from ..abc import ( + AnyByteReceiveStream, + AnyByteSendStream, + AnyByteStream, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, +) + + +@dataclass(eq=False) +class TextReceiveStream(ObjectReceiveStream[str]): + """ + Stream wrapper that decodes bytes to strings using the given encoding. + + Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any + completely received unicode characters as soon as they come in. + + :param transport_stream: any bytes-based receive stream + :param encoding: character encoding to use for decoding bytes to strings (defaults + to ``utf-8``) + :param errors: handling scheme for decoding errors (defaults to ``strict``; see the + `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteReceiveStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _decoder: codecs.IncrementalDecoder = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + decoder_class = codecs.getincrementaldecoder(encoding) + self._decoder = decoder_class(errors=errors) + + async def receive(self) -> str: + while True: + chunk = await self.transport_stream.receive() + decoded = self._decoder.decode(chunk) + if decoded: + return decoded + + async def aclose(self) -> None: + await self.transport_stream.aclose() + self._decoder.reset() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextSendStream(ObjectSendStream[str]): + """ + Sends strings to the wrapped stream as bytes using the given encoding. + + :param AnyByteSendStream transport_stream: any bytes-based send stream + :param str encoding: character encoding to use for encoding strings to bytes + (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteSendStream + encoding: InitVar[str] = "utf-8" + errors: str = "strict" + _encoder: Callable[..., tuple[bytes, int]] = field(init=False) + + def __post_init__(self, encoding: str) -> None: + self._encoder = codecs.getencoder(encoding) + + async def send(self, item: str) -> None: + encoded = self._encoder(item, self.errors)[0] + await self.transport_stream.send(encoded) + + async def aclose(self) -> None: + await self.transport_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return self.transport_stream.extra_attributes + + +@dataclass(eq=False) +class TextStream(ObjectStream[str]): + """ + A bidirectional stream that decodes bytes to strings on receive and encodes strings + to bytes on send. + + Extra attributes will be provided from both streams, with the receive stream + providing the values in case of a conflict. + + :param AnyByteStream transport_stream: any bytes-based stream + :param str encoding: character encoding to use for encoding/decoding strings to/from + bytes (defaults to ``utf-8``) + :param str errors: handling scheme for encoding errors (defaults to ``strict``; see + the `codecs module documentation`_ for a comprehensive list of options) + + .. _codecs module documentation: + https://docs.python.org/3/library/codecs.html#codec-objects + """ + + transport_stream: AnyByteStream + encoding: InitVar[str] = "utf-8" + errors: InitVar[str] = "strict" + _receive_stream: TextReceiveStream = field(init=False) + _send_stream: TextSendStream = field(init=False) + + def __post_init__(self, encoding: str, errors: str) -> None: + self._receive_stream = TextReceiveStream( + self.transport_stream, encoding=encoding, errors=errors + ) + self._send_stream = TextSendStream( + self.transport_stream, encoding=encoding, errors=errors + ) + + async def receive(self) -> str: + return await self._receive_stream.receive() + + async def send(self, item: str) -> None: + await self._send_stream.send(item) + + async def send_eof(self) -> None: + await self.transport_stream.send_eof() + + async def aclose(self) -> None: + await self._send_stream.aclose() + await self._receive_stream.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self._send_stream.extra_attributes, + **self._receive_stream.extra_attributes, + } diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/streams/tls.py b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/tls.py new file mode 100644 index 0000000..e913eed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/streams/tls.py @@ -0,0 +1,338 @@ +from __future__ import annotations + +import logging +import re +import ssl +import sys +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from functools import wraps +from typing import Any, Tuple, TypeVar + +from .. import ( + BrokenResourceError, + EndOfStream, + aclose_forcefully, + get_cancelled_exc_class, +) +from .._core._typedattr import TypedAttributeSet, typed_attribute +from ..abc import AnyByteStream, ByteStream, Listener, TaskGroup + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +_PCTRTT = Tuple[Tuple[str, str], ...] +_PCTRTTT = Tuple[_PCTRTT, ...] + + +class TLSAttribute(TypedAttributeSet): + """Contains Transport Layer Security related attributes.""" + + #: the selected ALPN protocol + alpn_protocol: str | None = typed_attribute() + #: the channel binding for type ``tls-unique`` + channel_binding_tls_unique: bytes = typed_attribute() + #: the selected cipher + cipher: tuple[str, str, int] = typed_attribute() + #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` + # for more information) + peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() + #: the peer certificate in binary form + peer_certificate_binary: bytes | None = typed_attribute() + #: ``True`` if this is the server side of the connection + server_side: bool = typed_attribute() + #: ciphers shared by the client during the TLS handshake (``None`` if this is the + #: client side) + shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() + #: the :class:`~ssl.SSLObject` used for encryption + ssl_object: ssl.SSLObject = typed_attribute() + #: ``True`` if this stream does (and expects) a closing TLS handshake when the + #: stream is being closed + standard_compatible: bool = typed_attribute() + #: the TLS protocol version (e.g. ``TLSv1.2``) + tls_version: str = typed_attribute() + + +@dataclass(eq=False) +class TLSStream(ByteStream): + """ + A stream wrapper that encrypts all sent data and decrypts received data. + + This class has no public initializer; use :meth:`wrap` instead. + All extra attributes from :class:`~TLSAttribute` are supported. + + :var AnyByteStream transport_stream: the wrapped stream + + """ + + transport_stream: AnyByteStream + standard_compatible: bool + _ssl_object: ssl.SSLObject + _read_bio: ssl.MemoryBIO + _write_bio: ssl.MemoryBIO + + @classmethod + async def wrap( + cls, + transport_stream: AnyByteStream, + *, + server_side: bool | None = None, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> TLSStream: + """ + Wrap an existing stream with Transport Layer Security. + + This performs a TLS handshake with the peer. + + :param transport_stream: a bytes-transporting stream to wrap + :param server_side: ``True`` if this is the server side of the connection, + ``False`` if this is the client side (if omitted, will be set to ``False`` + if ``hostname`` has been provided, ``False`` otherwise). Used only to create + a default context when an explicit context has not been provided. + :param hostname: host name of the peer (if host name checking is desired) + :param ssl_context: the SSLContext object to use (if not provided, a secure + default will be created) + :param standard_compatible: if ``False``, skip the closing handshake when + closing the connection, and don't raise an exception if the peer does the + same + :raises ~ssl.SSLError: if the TLS handshake fails + + """ + if server_side is None: + server_side = not hostname + + if not ssl_context: + purpose = ( + ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH + ) + ssl_context = ssl.create_default_context(purpose) + + # Re-enable detection of unexpected EOFs if it was disabled by Python + if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): + ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF + + bio_in = ssl.MemoryBIO() + bio_out = ssl.MemoryBIO() + ssl_object = ssl_context.wrap_bio( + bio_in, bio_out, server_side=server_side, server_hostname=hostname + ) + wrapper = cls( + transport_stream=transport_stream, + standard_compatible=standard_compatible, + _ssl_object=ssl_object, + _read_bio=bio_in, + _write_bio=bio_out, + ) + await wrapper._call_sslobject_method(ssl_object.do_handshake) + return wrapper + + async def _call_sslobject_method( + self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] + ) -> T_Retval: + while True: + try: + result = func(*args) + except ssl.SSLWantReadError: + try: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + data = await self.transport_stream.receive() + except EndOfStream: + self._read_bio.write_eof() + except OSError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + else: + self._read_bio.write(data) + except ssl.SSLWantWriteError: + await self.transport_stream.send(self._write_bio.read()) + except ssl.SSLSyscallError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + raise BrokenResourceError from exc + except ssl.SSLError as exc: + self._read_bio.write_eof() + self._write_bio.write_eof() + if ( + isinstance(exc, ssl.SSLEOFError) + or "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + ): + if self.standard_compatible: + raise BrokenResourceError from exc + else: + raise EndOfStream from None + + raise + else: + # Flush any pending writes first + if self._write_bio.pending: + await self.transport_stream.send(self._write_bio.read()) + + return result + + async def unwrap(self) -> tuple[AnyByteStream, bytes]: + """ + Does the TLS closing handshake. + + :return: a tuple of (wrapped byte stream, bytes left in the read buffer) + + """ + await self._call_sslobject_method(self._ssl_object.unwrap) + self._read_bio.write_eof() + self._write_bio.write_eof() + return self.transport_stream, self._read_bio.read() + + async def aclose(self) -> None: + if self.standard_compatible: + try: + await self.unwrap() + except BaseException: + await aclose_forcefully(self.transport_stream) + raise + + await self.transport_stream.aclose() + + async def receive(self, max_bytes: int = 65536) -> bytes: + data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) + if not data: + raise EndOfStream + + return data + + async def send(self, item: bytes) -> None: + await self._call_sslobject_method(self._ssl_object.write, item) + + async def send_eof(self) -> None: + tls_version = self.extra(TLSAttribute.tls_version) + match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) + if match: + major, minor = int(match.group(1)), int(match.group(2) or 0) + if (major, minor) < (1, 3): + raise NotImplementedError( + f"send_eof() requires at least TLSv1.3; current " + f"session uses {tls_version}" + ) + + raise NotImplementedError( + "send_eof() has not yet been implemented for TLS streams" + ) + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + **self.transport_stream.extra_attributes, + TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, + TLSAttribute.channel_binding_tls_unique: ( + self._ssl_object.get_channel_binding + ), + TLSAttribute.cipher: self._ssl_object.cipher, + TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), + TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( + True + ), + TLSAttribute.server_side: lambda: self._ssl_object.server_side, + TLSAttribute.shared_ciphers: lambda: self._ssl_object.shared_ciphers() + if self._ssl_object.server_side + else None, + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + TLSAttribute.ssl_object: lambda: self._ssl_object, + TLSAttribute.tls_version: self._ssl_object.version, + } + + +@dataclass(eq=False) +class TLSListener(Listener[TLSStream]): + """ + A convenience listener that wraps another listener and auto-negotiates a TLS session + on every accepted connection. + + If the TLS handshake times out or raises an exception, + :meth:`handle_handshake_error` is called to do whatever post-mortem processing is + deemed necessary. + + Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. + + :param Listener listener: the listener to wrap + :param ssl_context: the SSL context object + :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` + :param handshake_timeout: time limit for the TLS handshake + (passed to :func:`~anyio.fail_after`) + """ + + listener: Listener[Any] + ssl_context: ssl.SSLContext + standard_compatible: bool = True + handshake_timeout: float = 30 + + @staticmethod + async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: + """ + Handle an exception raised during the TLS handshake. + + This method does 3 things: + + #. Forcefully closes the original stream + #. Logs the exception (unless it was a cancellation exception) using the + ``anyio.streams.tls`` logger + #. Reraises the exception if it was a base exception or a cancellation exception + + :param exc: the exception + :param stream: the original stream + + """ + await aclose_forcefully(stream) + + # Log all except cancellation exceptions + if not isinstance(exc, get_cancelled_exc_class()): + # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using + # any asyncio implementation, so we explicitly pass the exception to log + # (https://github.com/python/cpython/issues/108668). Trio does not have this + # issue because it works around the CPython bug. + logging.getLogger(__name__).exception( + "Error during TLS handshake", exc_info=exc + ) + + # Only reraise base exceptions and cancellation exceptions + if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): + raise + + async def serve( + self, + handler: Callable[[TLSStream], Any], + task_group: TaskGroup | None = None, + ) -> None: + @wraps(handler) + async def handler_wrapper(stream: AnyByteStream) -> None: + from .. import fail_after + + try: + with fail_after(self.handshake_timeout): + wrapped_stream = await TLSStream.wrap( + stream, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException as exc: + await self.handle_handshake_error(exc, stream) + else: + await handler(wrapped_stream) + + await self.listener.serve(handler_wrapper, task_group) + + async def aclose(self) -> None: + await self.listener.aclose() + + @property + def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: + return { + TLSAttribute.standard_compatible: lambda: self.standard_compatible, + } diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/to_process.py b/.pythonlibs/lib/python3.10/site-packages/anyio/to_process.py new file mode 100644 index 0000000..1ff06f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/to_process.py @@ -0,0 +1,259 @@ +from __future__ import annotations + +import os +import pickle +import subprocess +import sys +from collections import deque +from collections.abc import Callable +from importlib.util import module_from_spec, spec_from_file_location +from typing import TypeVar, cast + +from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class +from ._core._exceptions import BrokenWorkerProcess +from ._core._subprocesses import open_process +from ._core._synchronization import CapacityLimiter +from ._core._tasks import CancelScope, fail_after +from .abc import ByteReceiveStream, ByteSendStream, Process +from .lowlevel import RunVar, checkpoint_if_cancelled +from .streams.buffered import BufferedByteReceiveStream + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +WORKER_MAX_IDLE_TIME = 300 # 5 minutes + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") +_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( + "_process_pool_idle_workers" +) +_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + cancellable: bool = False, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker process. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the worker process running it will be abruptly terminated using SIGKILL + (or ``terminateProcess()`` on Windows). + + :param func: a callable + :param args: positional arguments for the callable + :param cancellable: ``True`` to allow cancellation of the operation while it's + running + :param limiter: capacity limiter to use to limit the total amount of processes + running (if omitted, the default limiter is used) + :return: an awaitable that yields the return value of the function. + + """ + + async def send_raw_command(pickled_cmd: bytes) -> object: + try: + await stdin.send(pickled_cmd) + response = await buffered.receive_until(b"\n", 50) + status, length = response.split(b" ") + if status not in (b"RETURN", b"EXCEPTION"): + raise RuntimeError( + f"Worker process returned unexpected response: {response!r}" + ) + + pickled_response = await buffered.receive_exactly(int(length)) + except BaseException as exc: + workers.discard(process) + try: + process.kill() + with CancelScope(shield=True): + await process.aclose() + except ProcessLookupError: + pass + + if isinstance(exc, get_cancelled_exc_class()): + raise + else: + raise BrokenWorkerProcess from exc + + retval = pickle.loads(pickled_response) + if status == b"EXCEPTION": + assert isinstance(retval, BaseException) + raise retval + else: + return retval + + # First pickle the request before trying to reserve a worker process + await checkpoint_if_cancelled() + request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) + + # If this is the first run in this event loop thread, set up the necessary variables + try: + workers = _process_pool_workers.get() + idle_workers = _process_pool_idle_workers.get() + except LookupError: + workers = set() + idle_workers = deque() + _process_pool_workers.set(workers) + _process_pool_idle_workers.set(idle_workers) + get_async_backend().setup_process_pool_exit_at_shutdown(workers) + + async with limiter or current_default_process_limiter(): + # Pop processes from the pool (starting from the most recently used) until we + # find one that hasn't exited yet + process: Process + while idle_workers: + process, idle_since = idle_workers.pop() + if process.returncode is None: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + + # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME + # seconds or longer + now = current_time() + killed_processes: list[Process] = [] + while idle_workers: + if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: + break + + process_to_kill, idle_since = idle_workers.popleft() + process_to_kill.kill() + workers.remove(process_to_kill) + killed_processes.append(process_to_kill) + + with CancelScope(shield=True): + for killed_process in killed_processes: + await killed_process.aclose() + + break + + workers.remove(process) + else: + command = [sys.executable, "-u", "-m", __name__] + process = await open_process( + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + try: + stdin = cast(ByteSendStream, process.stdin) + buffered = BufferedByteReceiveStream( + cast(ByteReceiveStream, process.stdout) + ) + with fail_after(20): + message = await buffered.receive(6) + + if message != b"READY\n": + raise BrokenWorkerProcess( + f"Worker process returned unexpected response: {message!r}" + ) + + main_module_path = getattr(sys.modules["__main__"], "__file__", None) + pickled = pickle.dumps( + ("init", sys.path, main_module_path), + protocol=pickle.HIGHEST_PROTOCOL, + ) + await send_raw_command(pickled) + except (BrokenWorkerProcess, get_cancelled_exc_class()): + raise + except BaseException as exc: + process.kill() + raise BrokenWorkerProcess( + "Error during worker process initialization" + ) from exc + + workers.add(process) + + with CancelScope(shield=not cancellable): + try: + return cast(T_Retval, await send_raw_command(request)) + finally: + if process in workers: + idle_workers.append((process, current_time())) + + +def current_default_process_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of worker + processes. + + :return: a capacity limiter object + + """ + try: + return _default_process_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or 2) + _default_process_limiter.set(limiter) + return limiter + + +def process_worker() -> None: + # Redirect standard streams to os.devnull so that user code won't interfere with the + # parent-worker communication + stdin = sys.stdin + stdout = sys.stdout + sys.stdin = open(os.devnull) + sys.stdout = open(os.devnull, "w") + + stdout.buffer.write(b"READY\n") + while True: + retval = exception = None + try: + command, *args = pickle.load(stdin.buffer) + except EOFError: + return + except BaseException as exc: + exception = exc + else: + if command == "run": + func, args = args + try: + retval = func(*args) + except BaseException as exc: + exception = exc + elif command == "init": + main_module_path: str | None + sys.path, main_module_path = args + del sys.modules["__main__"] + if main_module_path: + # Load the parent's main module but as __mp_main__ instead of + # __main__ (like multiprocessing does) to avoid infinite recursion + try: + spec = spec_from_file_location("__mp_main__", main_module_path) + if spec and spec.loader: + main = module_from_spec(spec) + spec.loader.exec_module(main) + sys.modules["__main__"] = main + except BaseException as exc: + exception = exc + + try: + if exception is not None: + status = b"EXCEPTION" + pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) + else: + status = b"RETURN" + pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) + except BaseException as exc: + exception = exc + status = b"EXCEPTION" + pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) + + stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) + stdout.buffer.write(pickled) + + # Respect SIGTERM + if isinstance(exception, SystemExit): + raise exception + + +if __name__ == "__main__": + process_worker() diff --git a/.pythonlibs/lib/python3.10/site-packages/anyio/to_thread.py b/.pythonlibs/lib/python3.10/site-packages/anyio/to_thread.py new file mode 100644 index 0000000..5070516 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/anyio/to_thread.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import sys +from collections.abc import Callable +from typing import TypeVar +from warnings import warn + +from ._core._eventloop import get_async_backend +from .abc import CapacityLimiter + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + abandon_on_cancel: bool = False, + cancellable: bool | None = None, + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a worker thread. + + If the ``cancellable`` option is enabled and the task waiting for its completion is + cancelled, the thread will still run its course but its return value (or any raised + exception) will be ignored. + + :param func: a callable + :param args: positional arguments for the callable + :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run + unchecked on own) if the host task is cancelled, ``False`` to ignore + cancellations in the host task until the operation has completed in the worker + thread + :param cancellable: deprecated alias of ``abandon_on_cancel``; will override + ``abandon_on_cancel`` if both parameters are passed + :param limiter: capacity limiter to use to limit the total amount of threads running + (if omitted, the default limiter is used) + :return: an awaitable that yields the return value of the function. + + """ + if cancellable is not None: + abandon_on_cancel = cancellable + warn( + "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " + "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", + DeprecationWarning, + stacklevel=2, + ) + + return await get_async_backend().run_sync_in_worker_thread( + func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter + ) + + +def current_default_thread_limiter() -> CapacityLimiter: + """ + Return the capacity limiter that is used by default to limit the number of + concurrent threads. + + :return: a capacity limiter object + + """ + return get_async_backend().current_default_thread_limiter() diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/LICENSE new file mode 100644 index 0000000..033c86b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/LICENSE @@ -0,0 +1,13 @@ +Copyright 2016-2020 aio-libs collaboration. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/METADATA new file mode 100644 index 0000000..d8dd6d1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/METADATA @@ -0,0 +1,131 @@ +Metadata-Version: 2.1 +Name: async-timeout +Version: 4.0.3 +Summary: Timeout context manager for asyncio programs +Home-page: https://github.com/aio-libs/async-timeout +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +License: Apache 2 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub Actions, https://github.com/aio-libs/async-timeout/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/async-timeout +Project-URL: GitHub: issues, https://github.com/aio-libs/async-timeout/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/async-timeout +Classifier: Development Status :: 5 - Production/Stable +Classifier: Topic :: Software Development :: Libraries +Classifier: Framework :: AsyncIO +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: typing-extensions >=3.6.5 ; python_version < "3.8" + +async-timeout +============= +.. image:: https://travis-ci.com/aio-libs/async-timeout.svg?branch=master + :target: https://travis-ci.com/aio-libs/async-timeout +.. image:: https://codecov.io/gh/aio-libs/async-timeout/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/async-timeout +.. image:: https://img.shields.io/pypi/v/async-timeout.svg + :target: https://pypi.python.org/pypi/async-timeout +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +asyncio-compatible timeout context manager. + + +Usage example +------------- + + +The context manager is useful in cases when you want to apply timeout +logic around block of code or in cases when ``asyncio.wait_for()`` is +not suitable. Also it's much faster than ``asyncio.wait_for()`` +because ``timeout`` doesn't create a new task. + +The ``timeout(delay, *, loop=None)`` call returns a context manager +that cancels a block on *timeout* expiring:: + + from async_timeout import timeout + async with timeout(1.5): + await inner() + +1. If ``inner()`` is executed faster than in ``1.5`` seconds nothing + happens. +2. Otherwise ``inner()`` is cancelled internally by sending + ``asyncio.CancelledError`` into but ``asyncio.TimeoutError`` is + raised outside of context manager scope. + +*timeout* parameter could be ``None`` for skipping timeout functionality. + + +Alternatively, ``timeout_at(when)`` can be used for scheduling +at the absolute time:: + + loop = asyncio.get_event_loop() + now = loop.time() + + async with timeout_at(now + 1.5): + await inner() + + +Please note: it is not POSIX time but a time with +undefined starting base, e.g. the time of the system power on. + + +Context manager has ``.expired`` property for check if timeout happens +exactly in context manager:: + + async with timeout(1.5) as cm: + await inner() + print(cm.expired) + +The property is ``True`` if ``inner()`` execution is cancelled by +timeout context manager. + +If ``inner()`` call explicitly raises ``TimeoutError`` ``cm.expired`` +is ``False``. + +The scheduled deadline time is available as ``.deadline`` property:: + + async with timeout(1.5) as cm: + cm.deadline + +Not finished yet timeout can be rescheduled by ``shift_by()`` +or ``shift_to()`` methods:: + + async with timeout(1.5) as cm: + cm.shift(1) # add another second on waiting + cm.update(loop.time() + 5) # reschedule to now+5 seconds + +Rescheduling is forbidden if the timeout is expired or after exit from ``async with`` +code block. + + +Installation +------------ + +:: + + $ pip install async-timeout + +The library is Python 3 only! + + + +Authors and License +------------------- + +The module is written by Andrew Svetlov. + +It's *Apache 2* licensed and freely available. diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/RECORD new file mode 100644 index 0000000..835c41b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/RECORD @@ -0,0 +1,10 @@ +async_timeout-4.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +async_timeout-4.0.3.dist-info/LICENSE,sha256=4Y17uPUT4sRrtYXJS1hb0wcg3TzLId2weG9y0WZY-Sw,568 +async_timeout-4.0.3.dist-info/METADATA,sha256=WQVcnDIXQ2ntebcm-vYjhNLg_VMeTWw13_ReT-U36J4,4209 +async_timeout-4.0.3.dist-info/RECORD,, +async_timeout-4.0.3.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92 +async_timeout-4.0.3.dist-info/top_level.txt,sha256=9oM4e7Twq8iD_7_Q3Mz0E6GPIB6vJvRFo-UBwUQtBDU,14 +async_timeout-4.0.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +async_timeout/__init__.py,sha256=A0VOqDGQ3cCPFp0NZJKIbx_VRP1Y2xPtQOZebVIUB88,7242 +async_timeout/__pycache__/__init__.cpython-310.pyc,, +async_timeout/py.typed,sha256=tyozzRT1fziXETDxokmuyt6jhOmtjUbnVNJdZcG7ik0,12 diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/WHEEL new file mode 100644 index 0000000..2c08da0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..ad29955 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +async_timeout diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/zip-safe b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout-4.0.3.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout/__init__.py b/.pythonlibs/lib/python3.10/site-packages/async_timeout/__init__.py new file mode 100644 index 0000000..1ffb069 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout/__init__.py @@ -0,0 +1,239 @@ +import asyncio +import enum +import sys +import warnings +from types import TracebackType +from typing import Optional, Type + + +if sys.version_info >= (3, 8): + from typing import final +else: + from typing_extensions import final + + +if sys.version_info >= (3, 11): + + def _uncancel_task(task: "asyncio.Task[object]") -> None: + task.uncancel() + +else: + + def _uncancel_task(task: "asyncio.Task[object]") -> None: + pass + + +__version__ = "4.0.3" + + +__all__ = ("timeout", "timeout_at", "Timeout") + + +def timeout(delay: Optional[float]) -> "Timeout": + """timeout context manager. + + Useful in cases when you want to apply timeout logic around block + of code or in cases when asyncio.wait_for is not suitable. For example: + + >>> async with timeout(0.001): + ... async with aiohttp.get('https://github.com') as r: + ... await r.text() + + + delay - value in seconds or None to disable timeout logic + """ + loop = asyncio.get_running_loop() + if delay is not None: + deadline = loop.time() + delay # type: Optional[float] + else: + deadline = None + return Timeout(deadline, loop) + + +def timeout_at(deadline: Optional[float]) -> "Timeout": + """Schedule the timeout at absolute time. + + deadline argument points on the time in the same clock system + as loop.time(). + + Please note: it is not POSIX time but a time with + undefined starting base, e.g. the time of the system power on. + + >>> async with timeout_at(loop.time() + 10): + ... async with aiohttp.get('https://github.com') as r: + ... await r.text() + + + """ + loop = asyncio.get_running_loop() + return Timeout(deadline, loop) + + +class _State(enum.Enum): + INIT = "INIT" + ENTER = "ENTER" + TIMEOUT = "TIMEOUT" + EXIT = "EXIT" + + +@final +class Timeout: + # Internal class, please don't instantiate it directly + # Use timeout() and timeout_at() public factories instead. + # + # Implementation note: `async with timeout()` is preferred + # over `with timeout()`. + # While technically the Timeout class implementation + # doesn't need to be async at all, + # the `async with` statement explicitly points that + # the context manager should be used from async function context. + # + # This design allows to avoid many silly misusages. + # + # TimeoutError is raised immediately when scheduled + # if the deadline is passed. + # The purpose is to time out as soon as possible + # without waiting for the next await expression. + + __slots__ = ("_deadline", "_loop", "_state", "_timeout_handler", "_task") + + def __init__( + self, deadline: Optional[float], loop: asyncio.AbstractEventLoop + ) -> None: + self._loop = loop + self._state = _State.INIT + + self._task: Optional["asyncio.Task[object]"] = None + self._timeout_handler = None # type: Optional[asyncio.Handle] + if deadline is None: + self._deadline = None # type: Optional[float] + else: + self.update(deadline) + + def __enter__(self) -> "Timeout": + warnings.warn( + "with timeout() is deprecated, use async with timeout() instead", + DeprecationWarning, + stacklevel=2, + ) + self._do_enter() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + self._do_exit(exc_type) + return None + + async def __aenter__(self) -> "Timeout": + self._do_enter() + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Optional[bool]: + self._do_exit(exc_type) + return None + + @property + def expired(self) -> bool: + """Is timeout expired during execution?""" + return self._state == _State.TIMEOUT + + @property + def deadline(self) -> Optional[float]: + return self._deadline + + def reject(self) -> None: + """Reject scheduled timeout if any.""" + # cancel is maybe better name but + # task.cancel() raises CancelledError in asyncio world. + if self._state not in (_State.INIT, _State.ENTER): + raise RuntimeError(f"invalid state {self._state.value}") + self._reject() + + def _reject(self) -> None: + self._task = None + if self._timeout_handler is not None: + self._timeout_handler.cancel() + self._timeout_handler = None + + def shift(self, delay: float) -> None: + """Advance timeout on delay seconds. + + The delay can be negative. + + Raise RuntimeError if shift is called when deadline is not scheduled + """ + deadline = self._deadline + if deadline is None: + raise RuntimeError("cannot shift timeout if deadline is not scheduled") + self.update(deadline + delay) + + def update(self, deadline: float) -> None: + """Set deadline to absolute value. + + deadline argument points on the time in the same clock system + as loop.time(). + + If new deadline is in the past the timeout is raised immediately. + + Please note: it is not POSIX time but a time with + undefined starting base, e.g. the time of the system power on. + """ + if self._state == _State.EXIT: + raise RuntimeError("cannot reschedule after exit from context manager") + if self._state == _State.TIMEOUT: + raise RuntimeError("cannot reschedule expired timeout") + if self._timeout_handler is not None: + self._timeout_handler.cancel() + self._deadline = deadline + if self._state != _State.INIT: + self._reschedule() + + def _reschedule(self) -> None: + assert self._state == _State.ENTER + deadline = self._deadline + if deadline is None: + return + + now = self._loop.time() + if self._timeout_handler is not None: + self._timeout_handler.cancel() + + self._task = asyncio.current_task() + if deadline <= now: + self._timeout_handler = self._loop.call_soon(self._on_timeout) + else: + self._timeout_handler = self._loop.call_at(deadline, self._on_timeout) + + def _do_enter(self) -> None: + if self._state != _State.INIT: + raise RuntimeError(f"invalid state {self._state.value}") + self._state = _State.ENTER + self._reschedule() + + def _do_exit(self, exc_type: Optional[Type[BaseException]]) -> None: + if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT: + assert self._task is not None + _uncancel_task(self._task) + self._timeout_handler = None + self._task = None + raise asyncio.TimeoutError + # timeout has not expired + self._state = _State.EXIT + self._reject() + return None + + def _on_timeout(self) -> None: + assert self._task is not None + self._task.cancel() + self._state = _State.TIMEOUT + # drop the reference early + self._timeout_handler = None diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/async_timeout/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4861937 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/async_timeout/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/async_timeout/py.typed b/.pythonlibs/lib/python3.10/site-packages/async_timeout/py.typed new file mode 100644 index 0000000..3b94f91 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/async_timeout/py.typed @@ -0,0 +1 @@ +Placeholder diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__init__.py b/.pythonlibs/lib/python3.10/site-packages/attr/__init__.py new file mode 100644 index 0000000..9226258 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/__init__.py @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: MIT + +""" +Classes Without Boilerplate +""" + +from functools import partial +from typing import Callable + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._compat import Protocol +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Factory, + attrib, + attrs, + fields, + fields_dict, + make_class, + validate, +) +from ._next_gen import define, field, frozen, mutable +from ._version_info import VersionInfo + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + + +class AttrsInstance(Protocol): + pass + + +__all__ = [ + "Attribute", + "AttrsInstance", + "Factory", + "NOTHING", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "field", + "fields", + "fields_dict", + "filters", + "frozen", + "get_run_validators", + "has", + "ib", + "make_class", + "mutable", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + + +def _make_getattr(mod_name: str) -> Callable: + """ + Create a metadata proxy for packaging information that uses *mod_name* in + its warnings and errors. + """ + + def __getattr__(name: str) -> str: + dunder_to_metadata = { + "__title__": "Name", + "__copyright__": "", + "__version__": "version", + "__version_info__": "version", + "__description__": "summary", + "__uri__": "", + "__url__": "", + "__author__": "", + "__email__": "", + "__license__": "license", + } + if name not in dunder_to_metadata: + msg = f"module {mod_name} has no attribute {name}" + raise AttributeError(msg) + + import sys + import warnings + + if sys.version_info < (3, 8): + from importlib_metadata import metadata + else: + from importlib.metadata import metadata + + if name not in ("__version__", "__version_info__"): + warnings.warn( + f"Accessing {mod_name}.{name} is deprecated and will be " + "removed in a future release. Use importlib.metadata directly " + "to query for attrs's packaging metadata.", + DeprecationWarning, + stacklevel=2, + ) + + meta = metadata("attrs") + if name == "__license__": + return "MIT" + if name == "__copyright__": + return "Copyright (c) 2015 Hynek Schlawack" + if name in ("__uri__", "__url__"): + return meta["Project-URL"].split(" ", 1)[-1] + if name == "__version_info__": + return VersionInfo._from_version_string(meta["version"]) + if name == "__author__": + return meta["Author-email"].rsplit(" ", 1)[0] + if name == "__email__": + return meta["Author-email"].rsplit("<", 1)[1][:-1] + + return meta[dunder_to_metadata[name]] + + return __getattr__ + + +__getattr__ = _make_getattr(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/__init__.pyi new file mode 100644 index 0000000..37a2087 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/__init__.pyi @@ -0,0 +1,555 @@ +import enum +import sys + +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Mapping, + Optional, + Protocol, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._cmp import cmp_using as cmp_using +from ._typing_compat import AttrsInstance_ +from ._version_info import VersionInfo + +if sys.version_info >= (3, 10): + from typing import TypeGuard +else: + from typing_extensions import TypeGuard + +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = Union[bool, Callable[[Any], Any]] +_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] +_ConverterType = Callable[[Any], Any] +_FilterType = Callable[["Attribute[_T]", _T], bool] +_ReprType = Callable[[Any], str] +_ReprArgType = Union[bool, _ReprType] +_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] +_OnSetAttrArgType = Union[ + _OnSetAttrType, List[_OnSetAttrType], setters._NoOpType +] +_FieldTransformer = Callable[ + [type, List["Attribute[Any]"]], List["Attribute[Any]"] +] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]] + +# We subclass this here to keep the protocol's qualified name clean. +class AttrsInstance(AttrsInstance_, Protocol): + pass + +_A = TypeVar("_A", bound=type[AttrsInstance]) + +class _Nothing(enum.Enum): + NOTHING = enum.auto() + +NOTHING = _Nothing.NOTHING + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. +if sys.version_info >= (3, 8): + from typing import Literal + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], + ) -> _T: ... + @overload + def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], + ) -> _T: ... + +else: + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Union[Callable[[Any], _T], Callable[[], _T]], + takes_self: bool = ..., + ) -> _T: ... + +class Attribute(Generic[_T]): + name: str + default: Optional[_T] + validator: Optional[_ValidatorType[_T]] + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: Optional[bool] + init: bool + converter: Optional[_ConverterType] + metadata: Dict[Any, Any] + type: Optional[Type[_T]] + kw_only: bool + on_setattr: _OnSetAttrType + alias: Optional[str] + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: object = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., +) -> Any: ... +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., + type: Optional[type] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., + type: Optional[type] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., + type: Optional[type] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + alias: Optional[str] = ..., + type: Optional[type] = ..., +) -> Any: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: _C, + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., + unsafe_hash: Optional[bool] = ..., +) -> _C: ... +@overload +@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., + unsafe_hash: Optional[bool] = ..., +) -> Callable[[_C], _C]: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + unsafe_hash: Optional[bool] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(field_specifiers=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + unsafe_hash: Optional[bool] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... + +mutable = define + +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + unsafe_hash: Optional[bool] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> _C: ... +@overload +@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) +def frozen( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + unsafe_hash: Optional[bool] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., + match_args: bool = ..., +) -> Callable[[_C], _C]: ... +def fields(cls: Type[AttrsInstance]) -> Any: ... +def fields_dict(cls: Type[AttrsInstance]) -> Dict[str, Attribute[Any]]: ... +def validate(inst: AttrsInstance) -> None: ... +def resolve_types( + cls: _A, + globalns: Optional[Dict[str, Any]] = ..., + localns: Optional[Dict[str, Any]] = ..., + attribs: Optional[List[Attribute[Any]]] = ..., + include_extras: bool = ..., +) -> _A: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]], + bases: Tuple[type, ...] = ..., + class_body: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + collect_by_mro: bool = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +# XXX: remember to fix attrs.asdict/astuple too! +def asdict( + inst: AttrsInstance, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[ + Callable[[type, Attribute[Any], Any], Any] + ] = ..., + tuple_keys: Optional[bool] = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: AttrsInstance, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... +def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..363b64d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_cmp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_cmp.cpython-310.pyc new file mode 100644 index 0000000..8b576aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_cmp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000..a77f846 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_config.cpython-310.pyc new file mode 100644 index 0000000..8c90ae9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_funcs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_funcs.cpython-310.pyc new file mode 100644 index 0000000..4c9a8b9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_funcs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_make.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_make.cpython-310.pyc new file mode 100644 index 0000000..da24517 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_make.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_next_gen.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_next_gen.cpython-310.pyc new file mode 100644 index 0000000..9f7bf8f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_next_gen.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_version_info.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_version_info.cpython-310.pyc new file mode 100644 index 0000000..dd6e72f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/_version_info.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/converters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/converters.cpython-310.pyc new file mode 100644 index 0000000..016af57 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/converters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..0fa6d7b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/filters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/filters.cpython-310.pyc new file mode 100644 index 0000000..554cac4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/filters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/setters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/setters.cpython-310.pyc new file mode 100644 index 0000000..8ee94e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/setters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/validators.cpython-310.pyc new file mode 100644 index 0000000..b1985f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attr/__pycache__/validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.py b/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.py new file mode 100644 index 0000000..a4a35e0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.py @@ -0,0 +1,150 @@ +# SPDX-License-Identifier: MIT + + +import functools +import types + +from ._make import _make_ne + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, + and ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if at least + one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + :param Optional[callable] eq: `callable` used to evaluate equality of two + objects. + :param Optional[callable] lt: `callable` used to evaluate whether one + object is less than another object. + :param Optional[callable] le: `callable` used to evaluate whether one + object is less than or equal to another object. + :param Optional[callable] gt: `callable` used to evaluate whether one + object is greater than another object. + :param Optional[callable] ge: `callable` used to evaluate whether one + object is greater than or equal to another object. + + :param bool require_same_type: When `True`, equality and ordering methods + will return `NotImplemented` if objects are not of the same type. + + :param Optional[str] class_name: Name of class. Defaults to 'Comparable'. + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = _make_ne() + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = types.new_class( + class_name, (object,), {}, lambda ns: ns.update(body) + ) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + msg = "eq must be define is order to complete ordering from lt, le, gt, ge." + raise ValueError(msg) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = f"__{name}__" + method.__doc__ = ( + f"Return a {_operation_names[name]} b. Computed by attrs." + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + return all(func(self, other) for func in self._requirements) + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.pyi new file mode 100644 index 0000000..f3dcdc1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_cmp.pyi @@ -0,0 +1,13 @@ +from typing import Any, Callable, Optional, Type + +_CompareWithType = Callable[[Any, Any], bool] + +def cmp_using( + eq: Optional[_CompareWithType] = ..., + lt: Optional[_CompareWithType] = ..., + le: Optional[_CompareWithType] = ..., + gt: Optional[_CompareWithType] = ..., + ge: Optional[_CompareWithType] = ..., + require_same_type: bool = ..., + class_name: str = ..., +) -> Type: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_compat.py b/.pythonlibs/lib/python3.10/site-packages/attr/_compat.py new file mode 100644 index 0000000..46b05ca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_compat.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: MIT + +import inspect +import platform +import sys +import threading + +from collections.abc import Mapping, Sequence # noqa: F401 +from typing import _GenericAlias + + +PYPY = platform.python_implementation() == "PyPy" +PY_3_8_PLUS = sys.version_info[:2] >= (3, 8) +PY_3_9_PLUS = sys.version_info[:2] >= (3, 9) +PY310 = sys.version_info[:2] >= (3, 10) +PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) + + +if sys.version_info < (3, 8): + try: + from typing_extensions import Protocol + except ImportError: # pragma: no cover + Protocol = object +else: + from typing import Protocol # noqa: F401 + + +class _AnnotationExtractor: + """ + Extract type annotations from a callable, returning None whenever there + is none. + """ + + __slots__ = ["sig"] + + def __init__(self, callable): + try: + self.sig = inspect.signature(callable) + except (ValueError, TypeError): # inspect failed + self.sig = None + + def get_first_param_type(self): + """ + Return the type annotation of the first argument if it's not empty. + """ + if not self.sig: + return None + + params = list(self.sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + return params[0].annotation + + return None + + def get_return_type(self): + """ + Return the return type if it's not empty. + """ + if ( + self.sig + and self.sig.return_annotation is not inspect.Signature.empty + ): + return self.sig.return_annotation + + return None + + +# Thread-local global to track attrs instances which are already being repr'd. +# This is needed because there is no other (thread-safe) way to pass info +# about the instances that are already being repr'd through the call stack +# in order to ensure we don't perform infinite recursion. +# +# For instance, if an instance contains a dict which contains that instance, +# we need to know that we're already repr'ing the outside instance from within +# the dict's repr() call. +# +# This lives here rather than in _make.py so that the functions in _make.py +# don't have a direct reference to the thread-local in their globals dict. +# If they have such a reference, it breaks cloudpickle. +repr_context = threading.local() + + +def get_generic_base(cl): + """If this is a generic class (A[str]), return the generic base for it.""" + if cl.__class__ is _GenericAlias: + return cl.__origin__ + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_config.py b/.pythonlibs/lib/python3.10/site-packages/attr/_config.py new file mode 100644 index 0000000..9c245b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_config.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: MIT + +__all__ = ["set_run_validators", "get_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` + instead. + """ + if not isinstance(run, bool): + msg = "'run' must be bool." + raise TypeError(msg) + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + + .. deprecated:: 21.3.0 It will not be removed, but it also will not be + moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` + instead. + """ + return _run_validators diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_funcs.py b/.pythonlibs/lib/python3.10/site-packages/attr/_funcs.py new file mode 100644 index 0000000..a888991 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_funcs.py @@ -0,0 +1,483 @@ +# SPDX-License-Identifier: MIT + + +import copy + +from ._compat import PY_3_9_PLUS, get_generic_base +from ._make import NOTHING, _obj_setattr, fields +from .exceptions import AttrsAttributeNotFoundError + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the *attrs* attribute values of *inst* as a dict. + + Optionally recurse into other *attrs*-decorated classes. + + :param inst: Instance of an *attrs*-decorated class. + :param bool recurse: Recurse into classes that are also + *attrs*-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attrs.Attribute` as the first argument and the + value as the second argument. + :param callable dict_factory: A callable to produce dictionaries from. For + example, to produce ordered dictionaries instead of normal Python + dictionaries, pass in ``collections.OrderedDict``. + :param bool retain_collection_types: Do not convert to ``list`` when + encountering an attribute whose type is ``tuple`` or ``set``. Only + meaningful if ``recurse`` is ``True``. + :param Optional[callable] value_serializer: A hook that is called for every + attribute or dict key/value. It receives the current instance, field + and value and must return the (updated) value. The hook is run *after* + the optional *filter* has been applied. + + :rtype: return type of *dict_factory* + + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + .. versionadded:: 21.3.0 If a dict has a collection for a key, it is + serialized as a tuple. + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + if has(v.__class__): + rv[a.name] = asdict( + v, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain_collection_types is True else list + items = [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in v + ] + try: + rv[a.name] = cf(items) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv[a.name] = cf(*items) + elif isinstance(v, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in v.items() + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + is_key, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + if getattr(val.__class__, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + recurse=True, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + elif isinstance(val, (tuple, list, set, frozenset)): + if retain_collection_types is True: + cf = val.__class__ + elif is_key: + cf = tuple + else: + cf = list + + rv = cf( + [ + _asdict_anything( + i, + is_key=False, + filter=filter, + dict_factory=dict_factory, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ) + for i in val + ] + ) + elif isinstance(val, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, + is_key=True, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + _asdict_anything( + vv, + is_key=False, + filter=filter, + dict_factory=df, + retain_collection_types=retain_collection_types, + value_serializer=value_serializer, + ), + ) + for kk, vv in val.items() + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the *attrs* attribute values of *inst* as a tuple. + + Optionally recurse into other *attrs*-decorated classes. + + :param inst: Instance of an *attrs*-decorated class. + :param bool recurse: Recurse into classes that are also + *attrs*-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attrs.Attribute` as the first argument and the + value as the second argument. + :param callable tuple_factory: A callable to produce tuples from. For + example, to produce lists instead of tuples. + :param bool retain_collection_types: Do not convert to ``list`` + or ``dict`` when encountering an attribute which type is + ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is + ``True``. + + :rtype: return type of *tuple_factory* + + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + if recurse is True: + if has(v.__class__): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + items = [ + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + for j in v + ] + try: + rv.append(cf(items)) + except TypeError: + if not issubclass(cf, tuple): + raise + # Workaround for TypeError: cf.__new__() missing 1 required + # positional argument (which appears, for a namedturle) + rv.append(cf(*items)) + elif isinstance(v, dict): + df = v.__class__ if retain is True else dict + rv.append( + df( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk, + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv, + ) + for kk, vv in v.items() + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with *attrs* attributes. + + :param type cls: Class to introspect. + :raise TypeError: If *cls* is not a class. + + :rtype: bool + """ + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is not None: + return True + + # No attrs, maybe it's a specialized generic (A[str])? + generic_base = get_generic_base(cls) + if generic_base is not None: + generic_attrs = getattr(generic_base, "__attrs_attrs__", None) + if generic_attrs is not None: + # Stick it on here for speed next time. + cls.__attrs_attrs__ = generic_attrs + return generic_attrs is not None + return False + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + This is different from `evolve` that applies the changes to the arguments + that create the new instance. + + `evolve`'s behavior is preferable, but there are `edge cases`_ where it + doesn't work. Therefore `assoc` is deprecated, but will not be removed. + + .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251 + + :param inst: Instance of a class with *attrs* attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise attrs.exceptions.AttrsAttributeNotFoundError: If *attr_name* + couldn't be found on *cls*. + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + .. deprecated:: 17.1.0 + Use `attrs.evolve` instead if you can. + This function will not be removed du to the slightly different approach + compared to `attrs.evolve`. + """ + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in changes.items(): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + msg = f"{k} is not an attrs attribute on {new.__class__}." + raise AttrsAttributeNotFoundError(msg) + _obj_setattr(new, k, v) + return new + + +def evolve(*args, **changes): + """ + Create a new instance, based on the first positional argument with + *changes* applied. + + :param inst: Instance of a class with *attrs* attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise TypeError: If *attr_name* couldn't be found in the class + ``__init__``. + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + .. versionadded:: 17.1.0 + .. deprecated:: 23.1.0 + It is now deprecated to pass the instance using the keyword argument + *inst*. It will raise a warning until at least April 2024, after which + it will become an error. Always pass the instance as a positional + argument. + """ + # Try to get instance by positional argument first. + # Use changes otherwise and warn it'll break. + if args: + try: + (inst,) = args + except ValueError: + msg = f"evolve() takes 1 positional argument, but {len(args)} were given" + raise TypeError(msg) from None + else: + try: + inst = changes.pop("inst") + except KeyError: + msg = "evolve() missing 1 required positional argument: 'inst'" + raise TypeError(msg) from None + + import warnings + + warnings.warn( + "Passing the instance per keyword argument is deprecated and " + "will stop working in, or after, April 2024.", + DeprecationWarning, + stacklevel=2, + ) + + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = a.alias + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +def resolve_types( + cls, globalns=None, localns=None, attribs=None, include_extras=True +): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in `Attribute`'s *type* + field. In other words, you don't need to resolve your types if you only + use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, e.g. if the name only exists + inside a method, you may pass *globalns* or *localns* to specify other + dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + :param type cls: Class to resolve. + :param Optional[dict] globalns: Dictionary containing global variables. + :param Optional[dict] localns: Dictionary containing local variables. + :param Optional[list] attribs: List of attribs for the given class. + This is necessary when calling from inside a ``field_transformer`` + since *cls* is not an *attrs* class yet. + :param bool include_extras: Resolve more accurately, if possible. + Pass ``include_extras`` to ``typing.get_hints``, if supported by the + typing module. On supported Python versions (3.9+), this resolves the + types more accurately. + + :raise TypeError: If *cls* is not a class. + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class and you didn't pass any attribs. + :raise NameError: If types cannot be resolved because of missing variables. + + :returns: *cls* so you can use this function also as a class decorator. + Please note that you have to apply it **after** `attrs.define`. That + means the decorator has to come in the line **before** `attrs.define`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + .. versionadded:: 23.1.0 *include_extras* + + """ + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + if getattr(cls, "__attrs_types_resolved__", None) != cls: + import typing + + kwargs = {"globalns": globalns, "localns": localns} + + if PY_3_9_PLUS: + kwargs["include_extras"] = include_extras + + hints = typing.get_type_hints(cls, **kwargs) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _obj_setattr(field, "type", hints[field.name]) + # We store the class we resolved so that subclasses know they haven't + # been resolved. + cls.__attrs_types_resolved__ = cls + + # Return the class so you can use it as a decorator too. + return cls diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_make.py b/.pythonlibs/lib/python3.10/site-packages/attr/_make.py new file mode 100644 index 0000000..10b4eca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_make.py @@ -0,0 +1,3119 @@ +# SPDX-License-Identifier: MIT + +import contextlib +import copy +import enum +import functools +import inspect +import itertools +import linecache +import sys +import types +import typing + +from operator import itemgetter + +# We need to import _compat itself in addition to the _compat members to avoid +# having the thread-local in the globals here. +from . import _compat, _config, setters +from ._compat import ( + PY310, + PY_3_8_PLUS, + _AnnotationExtractor, + get_generic_base, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + UnannotatedAttributeError, +) + + +# This is used at least twice, so cache it here. +_obj_setattr = object.__setattr__ +_init_converter_pat = "__attr_converter_%s" +_init_factory_pat = "__attr_factory_%s" +_classvar_prefixes = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_hash_cache_field = "_attrs_cached_hash" + +_empty_metadata_singleton = types.MappingProxyType({}) + +# Unique object for unequivocal getattr() defaults. +_sentinel = object() + +_ng_default_on_setattr = setters.pipe(setters.convert, setters.validate) + + +class _Nothing(enum.Enum): + """ + Sentinel to indicate the lack of a value when ``None`` is ambiguous. + + If extending attrs, you can use ``typing.Literal[NOTHING]`` to show + that a value may be ``NOTHING``. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. + """ + + NOTHING = enum.auto() + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + +NOTHING = _Nothing.NOTHING +""" +Sentinel to indicate the lack of a value when ``None`` is ambiguous. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since ``None`` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Create a new attribute on a class. + + .. warning:: + + Does *not* do anything unless the class is also decorated with `attr.s` + / `attrs.define` / and so on! + + Please consider using `attrs.field` in new code (``attr.ib`` will *never* + go away, though). + + :param default: A value that is used if an *attrs*-generated ``__init__`` + is used and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `attrs.Factory`, its callable will be + used to construct a new value (useful for mutable data types like lists + or dicts). + + If a default is not set (or set manually to `attrs.NOTHING`), a value + *must* be supplied when instantiating; otherwise a `TypeError` will be + raised. + + The default can also be set using decorator notation as shown below. + + .. seealso:: `defaults` + + :param callable factory: Syntactic sugar for + ``default=attr.Factory(factory)``. + + :param validator: `callable` that is called by *attrs*-generated + ``__init__`` methods after the instance has been initialized. They + receive the initialized instance, the :func:`~attrs.Attribute`, and the + passed value. + + The return value is *not* inspected so the validator has to throw an + exception itself. + + If a `list` is passed, its items are treated as validators and must all + pass. + + Validators can be globally disabled and re-enabled using + `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. + + The validator can also be set using decorator notation as shown below. + + .. seealso:: :ref:`validators` + + :type validator: `callable` or a `list` of `callable`\\ s. + + :param repr: Include this attribute in the generated ``__repr__`` method. + If ``True``, include the attribute; if ``False``, omit it. By default, + the built-in ``repr()`` function is used. To override how the attribute + value is formatted, pass a ``callable`` that takes a single value and + returns a string. Note that the resulting string is used as-is, i.e. it + will be used directly *instead* of calling ``repr()`` (the default). + :type repr: a `bool` or a `callable` to use a custom function. + + :param eq: If ``True`` (default), include this attribute in the generated + ``__eq__`` and ``__ne__`` methods that check two instances for + equality. To override how the attribute value is compared, pass a + ``callable`` that takes a single value and returns the value to be + compared. + + .. seealso:: `comparison` + :type eq: a `bool` or a `callable`. + + :param order: If ``True`` (default), include this attributes in the + generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To + override how the attribute value is ordered, pass a ``callable`` that + takes a single value and returns the value to be ordered. + + .. seealso:: `comparison` + :type order: a `bool` or a `callable`. + + :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the + same value. Must not be mixed with *eq* or *order*. + + .. seealso:: `comparison` + :type cmp: a `bool` or a `callable`. + + :param bool | None hash: Include this attribute in the generated + ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This + is the correct behavior according the Python spec. Setting this value + to anything else than ``None`` is *discouraged*. + + .. seealso:: `hashing` + :param bool init: Include this attribute in the generated ``__init__`` + method. It is possible to set this to ``False`` and set a default + value. In that case this attributed is unconditionally initialized + with the specified default value or factory. + + .. seealso:: `init` + :param callable converter: `callable` that is called by *attrs*-generated + ``__init__`` methods to convert attribute's value to the desired + format. It is given the passed-in value, and the returned value will + be used as the new value of the attribute. The value is converted + before being passed to the validator, if any. + + .. seealso:: :ref:`converters` + :param dict | None metadata: An arbitrary mapping, to be used by + third-party components. See `extending-metadata`. + + :param type: The type of the attribute. Nowadays, the preferred method to + specify the type is using a variable annotation (see :pep:`526`). This + argument is provided for backward compatibility. Regardless of the + approach used, the type will be stored on ``Attribute.type``. + + Please note that *attrs* doesn't do anything with this metadata by + itself. You can use it as part of your own code or for `static type + checking `. + :param bool kw_only: Make this attribute keyword-only in the generated + ``__init__`` (if ``init`` is ``False``, this parameter is ignored). + :param on_setattr: Allows to overwrite the *on_setattr* setting from + `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. + Set to `attrs.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `attr.s`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attrs.setters.NO_OP` + :param str | None alias: Override this attribute's parameter name in the + generated ``__init__`` method. If left `None`, default to ``name`` + stripped of leading underscores. See `private-attributes`. + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is ``None`` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated + *convert* to achieve consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 22.2.0 *alias* + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + if factory is not None: + if default is not NOTHING: + msg = ( + "The `default` and `factory` arguments are mutually exclusive." + ) + raise ValueError(msg) + if not callable(factory): + msg = "The `factory` argument must be a callable." + raise ValueError(msg) + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + alias=alias, + ) + + +def _compile_and_eval(script, globs, locs=None, filename=""): + """ + "Exec" the script with the given global (globs) and local (locs) variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _make_method(name, script, filename, globs): + """ + Create the method with the script given and return the method object. + """ + locs = {} + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + count = 1 + base_filename = filename + while True: + linecache_tuple = ( + len(script), + None, + script.splitlines(True), + filename, + ) + old_val = linecache.cache.setdefault(filename, linecache_tuple) + if old_val == linecache_tuple: + break + + filename = f"{base_filename[:-1]}-{count}>" + count += 1 + + _compile_and_eval(script, globs, locs, filename) + + return locs[name] + + +def _make_attr_tuple_class(cls_name, attr_names): + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = f"{cls_name}Attributes" + attr_class_template = [ + f"class {attr_class_name}(tuple):", + " __slots__ = ()", + ] + if attr_names: + for i, attr_name in enumerate(attr_names): + attr_class_template.append( + f" {attr_name} = _attrs_property(_attrs_itemgetter({i}))" + ) + else: + attr_class_template.append(" pass") + globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} + _compile_and_eval("\n".join(attr_class_template), globs) + return globs[attr_class_name] + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +_Attributes = _make_attr_tuple_class( + "_Attributes", + [ + # all attributes to build dunder methods for + "attrs", + # attributes that have been inherited + "base_attrs", + # map inherited attributes to their originating classes + "base_attrs_map", + ], +) + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_classvar_prefixes) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + """ + attr = getattr(cls, attrib_name, _sentinel) + if attr is _sentinel: + return False + + for base_cls in cls.__mro__[1:]: + a = getattr(base_cls, attrib_name, None) + if attr is a: + return False + + return True + + +def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + if _has_own_attribute(cls, "__annotations__"): + return cls.__annotations__ + + return {} + + +def _collect_base_attrs(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) # noqa: PLW2901 + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + *collect_by_mro* is True, collect them in the correct MRO order, otherwise + use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = list(these.items()) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if not isinstance(a, _CountingAttr): + a = attrib() if a is NOTHING else attrib(default=a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if len(unannotated) > 0: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + ), + key=lambda e: e[1].counter, + ) + + own_attrs = [ + Attribute.from_counting_attr( + name=attr_name, ca=ca, type=anns.get(attr_name) + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + if kw_only: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = base_attrs + own_attrs + + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" + raise ValueError(msg) + + if had_default is False and a.default is not NOTHING: + had_default = True + + if field_transformer is not None: + attrs = field_transformer(cls, attrs) + + # Resolve default field alias after executing field_transformer. + # This allows field_transformer to differentiate between explicit vs + # default aliases and supply their own defaults. + attrs = [ + a.evolve(alias=_default_init_alias_for(a.name)) if not a.alias else a + for a in attrs + ] + + # Create AttrsClass *after* applying the field_transformer since it may + # add or remove attributes! + attr_names = [a.name for a in attrs] + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map)) + + +def _make_cached_property_getattr( + cached_properties, + original_getattr, + cls, +): + lines = [ + # Wrapped to get `__class__` into closure cell for super() + # (It will be replaced with the newly constructed class after construction). + "def wrapper():", + " __class__ = _cls", + " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", + " func = cached_properties.get(item)", + " if func is not None:", + " result = func(self)", + " _setter = _cached_setattr_get(self)", + " _setter(item, result)", + " return result", + ] + if original_getattr is not None: + lines.append( + " return original_getattr(self, item)", + ) + else: + lines.extend( + [ + " if hasattr(super(), '__getattr__'):", + " return super().__getattr__(item)", + " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", + " raise AttributeError(original_error)", + ] + ) + + lines.extend( + [ + " return __getattr__", + "__getattr__ = wrapper()", + ] + ) + + unique_filename = _generate_unique_filename(cls, "getattr") + + glob = { + "cached_properties": cached_properties, + "_cached_setattr_get": _obj_setattr.__get__, + "_cls": cls, + "original_getattr": original_getattr, + } + + return _make_method( + "__getattr__", + "\n".join(lines), + unique_filename, + glob, + ) + + +def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + "__traceback__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + raise FrozenInstanceError() + + +class _ClassBuilder: + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_pre_init", + "_pre_init_has_args", + "_has_post_init", + "_is_exc", + "_on_setattr", + "_slots", + "_weakref_slot", + "_wrote_own_setattr", + "_has_custom_setattr", + ) + + def __init__( + self, + cls, + these, + slots, + frozen, + weakref_slot, + getstate_setstate, + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_custom_setattr, + field_transformer, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if slots else {} + self._attrs = attrs + self._base_names = {a.name for a in base_attrs} + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = slots + self._frozen = frozen + self._weakref_slot = weakref_slot + self._cache_hash = cache_hash + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._pre_init_has_args = False + if self._has_pre_init: + # Check if the pre init method has more arguments than just `self` + # We want to pass arguments if pre init expects arguments + pre_init_func = cls.__attrs_pre_init__ + pre_init_signature = inspect.signature(pre_init_func) + self._pre_init_has_args = len(pre_init_signature.parameters) > 1 + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = is_exc + self._on_setattr = on_setattr + + self._has_custom_setattr = has_custom_setattr + self._wrote_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + + if frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._wrote_own_setattr = True + elif on_setattr in ( + _ng_default_on_setattr, + setters.validate, + setters.convert, + ): + has_validator = has_converter = False + for a in attrs: + if a.validator is not None: + has_validator = True + if a.converter is not None: + has_converter = True + + if has_validator and has_converter: + break + if ( + ( + on_setattr == _ng_default_on_setattr + and not (has_validator or has_converter) + ) + or (on_setattr == setters.validate and not has_validator) + or (on_setattr == setters.convert and not has_converter) + ): + # If class-level on_setattr is set to convert + validate, but + # there's no field to convert or validate, pretend like there's + # no on_setattr. + self._on_setattr = None + + if getstate_setstate: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + def __repr__(self): + return f"<_ClassBuilder(cls={self._cls.__name__})>" + + if PY310: + import abc + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + if self._slots is True: + return self._create_slots_class() + + return self.abc.update_abstractmethods( + self._patch_original_class() + ) + + else: + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + if self._slots is True: + return self._create_slots_class() + + return self._patch_original_class() + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _sentinel) is not _sentinel + ): + # An AttributeError can happen if a base class defines a + # class variable and we want to set an attribute with the + # same name by using only a type annotation. + with contextlib.suppress(AttributeError): + delattr(cls, name) + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._wrote_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = _obj_setattr + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in self._cls_dict.items() + if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") + } + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._wrote_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = _obj_setattr + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = {} + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + if PY_3_8_PLUS: + cached_properties = { + name: cached_property.func + for name, cached_property in cd.items() + if isinstance(cached_property, functools.cached_property) + } + else: + # `functools.cached_property` was introduced in 3.8. + # So can't be used before this. + cached_properties = {} + + # Collect methods with a `__class__` reference that are shadowed in the new class. + # To know to update them. + additional_closure_functions_to_update = [] + if cached_properties: + # Add cached properties to names for slotting. + names += tuple(cached_properties.keys()) + + for name in cached_properties: + # Clear out function from class to avoid clashing. + del cd[name] + + class_annotations = _get_annotations(self._cls) + for name, func in cached_properties.items(): + annotation = inspect.signature(func).return_annotation + if annotation is not inspect.Parameter.empty: + class_annotations[name] = annotation + + original_getattr = cd.get("__getattr__") + if original_getattr is not None: + additional_closure_functions_to_update.append(original_getattr) + + cd["__getattr__"] = _make_cached_property_getattr( + cached_properties, original_getattr, self._cls + ) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overridden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in existing_slots.items() + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_hash_cache_field) + + cd["__slots__"] = tuple(slot_names) + + cd["__qualname__"] = self._cls.__qualname__ + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # . + # If a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in itertools.chain( + cls.__dict__.values(), additional_closure_functions_to_update + ): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # noqa: PERF203 + # ValueError: Cell is empty + pass + else: + if match: + cell.cell_contents = cls + return cls + + def add_repr(self, ns): + self._cls_dict["__repr__"] = self._add_method_dunders( + _make_repr(self._attrs, ns, self._cls) + ) + return self + + def add_str(self): + repr = self._cls_dict.get("__repr__") + if repr is None: + msg = "__str__ can only be generated if a __repr__ exists." + raise ValueError(msg) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return {name: getattr(self, name) for name in state_attr_names} + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _obj_setattr.__get__(self) + if isinstance(state, tuple): + # Backward compatibility with attrs instances pickled with + # attrs versions before v22.2.0 which stored tuples. + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + else: + for name in state_attr_names: + if name in state: + __bound_setattr(name, state[name]) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_hash_cache_field, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + self._cls_dict["__hash__"] = self._add_method_dunders( + _make_hash( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + ) + + return self + + def add_init(self): + self._cls_dict["__init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=False, + ) + ) + + return self + + def add_match_args(self): + self._cls_dict["__match_args__"] = tuple( + field.name + for field in self._attrs + if field.init and not field.kw_only + ) + + def add_attrs_init(self): + self._cls_dict["__attrs_init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._pre_init_has_args, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr, + attrs_init=True, + ) + ) + + return self + + def add_eq(self): + cd = self._cls_dict + + cd["__eq__"] = self._add_method_dunders( + _make_eq(self._cls, self._attrs) + ) + cd["__ne__"] = self._add_method_dunders(_make_ne()) + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + if self._frozen: + return self + + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + msg = "Can't combine custom __setattr__ with on_setattr hooks." + raise ValueError(msg) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _obj_setattr(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._wrote_own_setattr = True + + return self + + def _add_method_dunders(self, method): + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + with contextlib.suppress(AttributeError): + method.__module__ = self._cls.__module__ + + with contextlib.suppress(AttributeError): + method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" + + with contextlib.suppress(AttributeError): + method.__doc__ = ( + "Method generated by attrs for class " + f"{self._cls.__qualname__}." + ) + + return method + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + msg = "Don't mix `cmp` with `eq' and `order`." + raise ValueError(msg) + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + msg = "`order` can only be True if `eq` is True too." + raise ValueError(msg) + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, + unsafe_hash=None, +): + r""" + A class decorator that adds :term:`dunder methods` according to the + specified attributes using `attr.ib` or the *these* argument. + + Please consider using `attrs.define` / `attrs.frozen` in new code + (``attr.s`` will *never* go away, though). + + :param these: A dictionary of name to `attr.ib` mappings. This is useful + to avoid the definition of your attributes within the class body + because you can't (e.g. if you want to add ``__repr__`` methods to + Django models) or don't want to. + + If *these* is not ``None``, *attrs* will *not* search the class body + for attributes and will *not* remove any attributes from it. + + The order is deduced from the order of the attributes inside *these*. + + :type these: `dict` of `str` to `attr.ib` + + :param str repr_ns: When using nested classes, there's no way in Python 2 + to automatically detect that. Therefore it's possible to set the + namespace explicitly for a more meaningful ``repr`` output. + :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, + *order*, and *hash* arguments explicitly, assume they are set to + ``True`` **unless any** of the involved methods for one of the + arguments is implemented in the *current* class (i.e. it is *not* + inherited from some base class). + + So for example by implementing ``__eq__`` on a class yourself, *attrs* + will deduce ``eq=False`` and will create *neither* ``__eq__`` *nor* + ``__ne__`` (but Python classes come with a sensible ``__ne__`` by + default, so it *should* be enough to only implement ``__eq__`` in most + cases). + + .. warning:: + + If you prevent *attrs* from creating the ordering methods for you + (``order=False``, e.g. by implementing ``__le__``), it becomes + *your* responsibility to make sure its ordering is sound. The best + way is to use the `functools.total_ordering` decorator. + + + Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, *cmp*, + or *hash* overrides whatever *auto_detect* would determine. + + :param bool repr: Create a ``__repr__`` method with a human readable + representation of *attrs* attributes.. + :param bool str: Create a ``__str__`` method that is identical to + ``__repr__``. This is usually not necessary except for `Exception`\ s. + :param bool | None eq: If ``True`` or ``None`` (default), add ``__eq__`` + and ``__ne__`` methods that check two instances for equality. + + They compare the instances as if they were tuples of their *attrs* + attributes if and only if the types of both classes are *identical*! + + .. seealso:: `comparison` + :param bool | None order: If ``True``, add ``__lt__``, ``__le__``, + ``__gt__``, and ``__ge__`` methods that behave like *eq* above and + allow instances to be ordered. If ``None`` (default) mirror value of + *eq*. + + .. seealso:: `comparison` + :param bool | None cmp: Setting *cmp* is equivalent to setting *eq* and + *order* to the same value. Must not be mixed with *eq* or *order*. + + .. seealso:: `comparison` + :param bool | None unsafe_hash: If ``None`` (default), the ``__hash__`` + method is generated according how *eq* and *frozen* are set. + + 1. If *both* are True, *attrs* will generate a ``__hash__`` for you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to + None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning the + ``__hash__`` method of the base class will be used (if base class is + ``object``, this means it will fall back to id-based hashing.). + + Although not recommended, you can decide for yourself and force *attrs* + to create one (e.g. if the class is immutable even though you didn't + freeze it programmatically) by passing ``True`` or not. Both of these + cases are rather special and should be used carefully. + + .. seealso:: + + - Our documentation on `hashing`, + - Python's documentation on `object.__hash__`, + - and the `GitHub issue that led to the default \ + behavior `_ for + more details. + + :param bool | None hash: Alias for *unsafe_hash*. *unsafe_hash* takes + precedence. + :param bool init: Create a ``__init__`` method that initializes the *attrs* + attributes. Leading underscores are stripped for the argument name. If + a ``__attrs_pre_init__`` method exists on the class, it will be called + before the class is initialized. If a ``__attrs_post_init__`` method + exists on the class, it will be called after the class is fully + initialized. + + If ``init`` is ``False``, an ``__attrs_init__`` method will be injected + instead. This allows you to define a custom ``__init__`` method that + can do pre-init work such as ``super().__init__()``, and then call + ``__attrs_init__()`` and ``__attrs_post_init__()``. + + .. seealso:: `init` + :param bool slots: Create a :term:`slotted class ` that's + more memory-efficient. Slotted classes are generally superior to the + default dict classes, but have some gotchas you should know about, so + we encourage you to read the :term:`glossary entry `. + :param bool frozen: Make instances immutable after initialization. If + someone attempts to modify a frozen instance, + `attrs.exceptions.FrozenInstanceError` is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` method + on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other words: + ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You can + circumvent that limitation by using ``object.__setattr__(self, + "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + :param bool weakref_slot: Make instances weak-referenceable. This has no + effect unless ``slots`` is also enabled. + :param bool auto_attribs: If ``True``, collect :pep:`526`-annotated + attributes from the class body. + + In this case, you **must** annotate every field. If *attrs* encounters + a field that is set to an `attr.ib` but lacks a type annotation, an + `attr.exceptions.UnannotatedAttributeError` is raised. Use + ``field_name: typing.Any = attr.ib(...)`` if you don't want to set a + type. + + If you assign a value to those attributes (e.g. ``x: int = 42``), that + value becomes the default value like if it were passed using + ``attr.ib(default=42)``. Passing an instance of `attrs.Factory` also + works as expected in most cases (see warning below). + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `attr.ib` are **ignored**. + + .. warning:: + For features that use the attribute name to create decorators (e.g. + :ref:`validators `), you still *must* assign `attr.ib` + to them. Otherwise Python will either not find the name or try to + use the default value to call e.g. ``validator`` on it. + + These errors can be quite confusing and probably the most common bug + report on our bug tracker. + + :param bool kw_only: Make all attributes keyword-only in the generated + ``__init__`` (if ``init`` is ``False``, this parameter is ignored). + :param bool cache_hash: Ensure that the object's hash code is computed only + once and stored on the object. If this is set to ``True``, hashing + must be either explicitly or implicitly enabled for this class. If the + hash code is cached, avoid any reassignments of fields involved in hash + code computation or mutations of the objects those fields point to + after object creation. If such changes occur, the behavior of the + object's hash code is undefined. + :param bool auto_exc: If the class subclasses `BaseException` (which + implicitly includes any subclass of any exception), the following + happens to behave like a well-behaved Python exceptions class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids (N.B. *attrs* will + *not* remove existing implementations of ``__hash__`` or the equality + methods. It just won't add own ones.), + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the ``args`` + attribute, + - the value of *str* is ignored leaving ``__str__`` to base classes. + :param bool collect_by_mro: Setting this to `True` fixes the way *attrs* + collects attributes from base classes. The default behavior is + incorrect in certain cases of multiple inheritance. It should be on by + default but is kept off for backward-compatibility. + + .. seealso:: + Issue `#428 `_ + + :param bool | None getstate_setstate: + .. note:: + This is usually only interesting for slotted classes and you should + probably just set *auto_detect* to `True`. + + If `True`, ``__getstate__`` and ``__setstate__`` are generated and + attached to the class. This is necessary for slotted classes to be + pickleable. If left `None`, it's `True` by default for slotted classes + and ``False`` for dict classes. + + If *auto_detect* is `True`, and *getstate_setstate* is left `None`, and + **either** ``__getstate__`` or ``__setstate__`` is detected directly on + the class (i.e. not inherited), it is set to `False` (this is usually + what you want). + + :param on_setattr: A callable that is run whenever the user attempts to set + an attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + as validators: the instance, the attribute that is being modified, and + the new value. + + If no exception is raised, the attribute is set to the return value of + the callable. + + If a list of callables is passed, they're automatically wrapped in an + `attrs.setters.pipe`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attrs.setters.NO_OP` + + :param callable | None field_transformer: + A function that is called with the original class object and all fields + right before *attrs* finalizes the class. You can use this, e.g., to + automatically add converters or validators to fields based on their + types. + + .. seealso:: `transform-fields` + + :param bool match_args: + If `True` (default), set ``__match_args__`` on the class to support + :pep:`634` (Structural Pattern Matching). It is a tuple of all + non-keyword-only ``__init__`` parameter names on Python 3.10 and later. + Ignored on older Python versions. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports ``None`` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + .. versionadded:: 21.3.0 *match_args* + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + """ + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + + # unsafe_hash takes precedence due to PEP 681. + if unsafe_hash is not None: + hash = unsafe_hash + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + msg = "Can't freeze a class with a custom __setattr__." + raise ValueError(msg) + + builder = _ClassBuilder( + cls, + these, + slots, + is_frozen, + weakref_slot, + _determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_own_setattr, + field_transformer, + ) + if _determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ): + builder.add_repr(repr_ns) + if str is True: + builder.add_str() + + eq = _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + if not is_exc and eq is True: + builder.add_eq() + if not is_exc and _determine_whether_to_implement( + cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") + ): + builder.add_order() + + builder.add_setattr() + + nonlocal hash + if ( + hash is None + and auto_detect is True + and _has_own_attribute(cls, "__hash__") + ): + hash = False + + if hash is not True and hash is not False and hash is not None: + # Can't use `hash in` because 1 == True for example. + msg = "Invalid value for hash. Must be True, False, or None." + raise TypeError(msg) + + if hash is False or (hash is None and eq is False) or is_exc: + # Don't do anything. Should fall back to __object__'s __hash__ + # which is by id. + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." + raise TypeError(msg) + elif hash is True or ( + hash is None and eq is True and is_frozen is True + ): + # Build a __hash__ if told so, or if it's safe. + builder.add_hash() + else: + # Raise TypeError on attempts to hash. + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." + raise TypeError(msg) + builder.make_unhashable() + + if _determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ): + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + msg = "Invalid value for cache_hash. To use hash caching, init must be True." + raise TypeError(msg) + + if ( + PY310 + and match_args + and not _has_own_attribute(cls, "__match_args__") + ): + builder.add_match_args() + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ is _frozen_setattrs + + +def _generate_unique_filename(cls, func_name): + """ + Create a "filename" suitable for a function being generated. + """ + return ( + f"" + ) + + +def _make_hash(cls, attrs, frozen, cache_hash): + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + unique_filename = _generate_unique_filename(cls, "hash") + type_hash = hash(unique_filename) + # If eq is custom generated, we need to include the functions in globs + globs = {} + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + hash_def += ", *" + + hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + f" {type_hash},", + ] + ) + + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + globs[cmp_name] = a.eq_key + method_lines.append( + indent + f" {cmp_name}(self.{a.name})," + ) + else: + method_lines.append(indent + f" self.{a.name},") + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + f"if self.{_hash_cache_field} is None:") + if frozen: + append_hash_computation_lines( + f"object.__setattr__(self, '{_hash_cache_field}', ", tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + f"self.{_hash_cache_field} = ", tab * 2 + ) + method_lines.append(tab + f"return self.{_hash_cache_field}") + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return _make_method("__hash__", script, unique_filename, globs) + + +def _add_hash(cls, attrs): + """ + Add a hash method to *cls*. + """ + cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False) + return cls + + +def _make_ne(): + """ + Create __ne__ method. + """ + + def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + return __ne__ + + +def _make_eq(cls, attrs): + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + unique_filename = _generate_unique_filename(cls, "eq") + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + # We can't just do a big self.x = other.x and... clause due to + # irregularities like nan == nan is false but (nan,) == (nan,) is true. + globs = {} + if attrs: + lines.append(" return (") + others = [" ) == ("] + for a in attrs: + if a.eq_key: + cmp_name = f"_{a.name}_key" + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append(f" {cmp_name}(self.{a.name}),") + others.append(f" {cmp_name}(other.{a.name}),") + else: + lines.append(f" self.{a.name},") + others.append(f" other.{a.name},") + + lines += [*others, " )"] + else: + lines.append(" return True") + + script = "\n".join(lines) + + return _make_method("__eq__", script, unique_filename, globs) + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__eq__ = _make_eq(cls, attrs) + cls.__ne__ = _make_ne() + + return cls + + +def _make_repr(attrs, ns, cls): + unique_filename = _generate_unique_filename(cls, "repr") + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom + # callable. + attr_names_with_reprs = tuple( + (a.name, (repr if a.repr is True else a.repr), a.init) + for a in attrs + if a.repr is not False + ) + globs = { + name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr + } + globs["_compat"] = _compat + globs["AttributeError"] = AttributeError + globs["NOTHING"] = NOTHING + attribute_fragments = [] + for name, r, i in attr_names_with_reprs: + accessor = ( + "self." + name if i else 'getattr(self, "' + name + '", NOTHING)' + ) + fragment = ( + "%s={%s!r}" % (name, accessor) + if r == repr + else "%s={%s_repr(%s)}" % (name, name, accessor) + ) + attribute_fragments.append(fragment) + repr_fragment = ", ".join(attribute_fragments) + + if ns is None: + cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' + else: + cls_name_fragment = ns + ".{self.__class__.__name__}" + + lines = [ + "def __repr__(self):", + " try:", + " already_repring = _compat.repr_context.already_repring", + " except AttributeError:", + " already_repring = {id(self),}", + " _compat.repr_context.already_repring = already_repring", + " else:", + " if id(self) in already_repring:", + " return '...'", + " else:", + " already_repring.add(id(self))", + " try:", + f" return f'{cls_name_fragment}({repr_fragment})'", + " finally:", + " already_repring.remove(id(self))", + ] + + return _make_method( + "__repr__", "\n".join(lines), unique_filename, globs=globs + ) + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__repr__ = _make_repr(attrs, ns, cls) + return cls + + +def fields(cls): + """ + Return the tuple of *attrs* attributes for a class. + + The tuple also allows accessing the fields by their names (see below for + examples). + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + :rtype: tuple (with name accessors) of `attrs.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + .. versionchanged:: 23.1.0 Add support for generic classes. + """ + generic_base = get_generic_base(cls) + + if generic_base is None and not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + + attrs = getattr(cls, "__attrs_attrs__", None) + + if attrs is None: + if generic_base is not None: + attrs = getattr(generic_base, "__attrs_attrs__", None) + if attrs is not None: + # Even though this is global state, stick it on here to speed + # it up. We rely on `cls` being cached for this to be + # efficient. + cls.__attrs_attrs__ = attrs + return attrs + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of *attrs* attributes for a class, whose + keys are the attribute names. + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs* + class. + + :rtype: dict + + .. versionadded:: 18.1.0 + """ + if not isinstance(cls, type): + msg = "Passed object must be a class." + raise TypeError(msg) + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + msg = f"{cls!r} is not an attrs-decorated class." + raise NotAnAttrsClassError(msg) + return {a.name: a for a in attrs} + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + :param inst: Instance of a class with *attrs* attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_cls(cls): + return "__slots__" in cls.__dict__ + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) + + +def _make_init( + cls, + attrs, + pre_init, + pre_init_has_args, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + cls_on_setattr, + attrs_init, +): + has_cls_on_setattr = ( + cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP + ) + + if frozen and has_cls_on_setattr: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + msg = "Frozen classes can't use on_setattr." + raise ValueError(msg) + + needs_cached_setattr = True + elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: + needs_cached_setattr = True + + unique_filename = _generate_unique_filename(cls, "init") + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + pre_init_has_args, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + attrs_init, + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr_get"] = _obj_setattr.__get__ + + init = _make_method( + "__attrs_init__" if attrs_init else "__init__", + script, + unique_filename, + globs, + ) + init.__annotations__ = annotations + + return init + + +def _setattr(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return f"_setattr('{attr_name}', {value_var})" + + +def _setattr_with_converter(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return "_setattr('%s', %s(%s))" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _assign(attr_name, value, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return f"self.{attr_name} = {value}" + + +def _assign_with_converter(attr_name, value_var, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True) + + return "self.%s = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _attrs_to_init_script( + attrs, + frozen, + slots, + pre_init, + pre_init_has_args, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_cls_on_setattr, + attrs_init, +): + """ + Return a script of an initializer for *attrs* and a dict of globals. + + The globals are expected by the generated script. + + If *frozen* is True, we cannot set the attributes directly so we use + a cached ``object.__setattr__``. + """ + lines = [] + if pre_init: + lines.append("self.__attrs_pre_init__()") + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. + # Note _setattr will be used again below if cache_hash is True + "_setattr = _cached_setattr_get(self)" + ) + + if frozen is True: + if slots is True: + fmt_setter = _setattr + fmt_setter_with_converter = _setattr_with_converter + else: + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + lines.append("_inst_dict = self.__dict__") + + def fmt_setter(attr_name, value_var, has_on_setattr): + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return f"_inst_dict['{attr_name}'] = {value_var}" + + def fmt_setter_with_converter( + attr_name, value_var, has_on_setattr + ): + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr + ) + + return "_inst_dict['%s'] = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + else: + # Not frozen. + fmt_setter = _assign + fmt_setter_with_converter = _assign_with_converter + + args = [] + kw_only_args = [] + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_cls_on_setattr + ) + # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not + # explicitly provided + arg_name = a.alias + + has_factory = isinstance(a.default, Factory) + maybe_self = "self" if has_factory and a.default.takes_self else "" + + if a.init is False: + if has_factory: + init_factory_name = _init_factory_pat % (a.name,) + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + f"({maybe_self})", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + elif a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + f"attr_dict['{attr_name}'].default", + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = f"{arg_name}=attr_dict['{attr_name}'].default" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = f"{arg_name}=NOTHING" + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + lines.append(f"if {arg_name} is not NOTHING:") + + init_factory_name = _init_factory_pat % (a.name,) + if a.converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and a.converter is None: + annotations[arg_name] = a.type + elif a.converter is not None: + # Try to get the type from the converter. + t = _AnnotationExtractor(a.converter).get_first_param_type() + if t: + annotations[arg_name] = t + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append(f" {val_name}(self, {attr_name}, self.{a.name})") + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if post_init: + lines.append("self.__attrs_post_init__()") + + # because this is set only after __attrs_post_init__ is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to + # field values during post-init combined with post-init accessing the + # hash code would result in silent bugs. + if cache_hash: + if frozen: + if slots: # noqa: SIM108 + # if frozen and slots, then _setattr defined above + init_hash_cache = "_setattr('%s', %s)" + else: + # if frozen and not slots, then _inst_dict defined above + init_hash_cache = "_inst_dict['%s'] = %s" + else: + init_hash_cache = "self.%s = %s" + lines.append(init_hash_cache % (_hash_cache_field, "None")) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join(f"self.{a.name}" for a in attrs if a.init) + + lines.append(f"BaseException.__init__(self, {vals})") + + args = ", ".join(args) + pre_init_args = args + if kw_only_args: + args += "%s*, %s" % ( + ", " if args else "", # leading comma + ", ".join(kw_only_args), # kw_only args + ) + pre_init_kw_only_args = ", ".join( + ["%s=%s" % (kw_arg, kw_arg) for kw_arg in kw_only_args] + ) + pre_init_args += ( + ", " if pre_init_args else "" + ) # handle only kwargs and no regular args + pre_init_args += pre_init_kw_only_args + + if pre_init and pre_init_has_args: + # If pre init method has arguments, pass same arguments as `__init__` + lines[0] = "self.__attrs_pre_init__(%s)" % pre_init_args + + return ( + "def %s(self, %s):\n %s\n" + % ( + ("__attrs_init__" if attrs_init else "__init__"), + args, + "\n ".join(lines) if lines else "pass", + ), + names_for_globals, + annotations, + ) + + +def _default_init_alias_for(name: str) -> str: + """ + The default __init__ parameter name for a field. + + This performs private-name adjustment via leading-unscore stripping, + and is the default value of Attribute.alias if not provided. + """ + + return name.lstrip("_") + + +class Attribute: + """ + *Read-only* representation of an attribute. + + .. warning:: + + You should never instantiate this class yourself. + + The class has *all* arguments of `attr.ib` (except for ``factory`` + which is only syntactic sugar for ``default=Factory(...)`` plus the + following: + + - ``name`` (`str`): The name of the attribute. + - ``alias`` (`str`): The __init__ parameter name of the attribute, after + any explicit overrides and default private-attribute-name handling. + - ``inherited`` (`bool`): Whether or not that attribute has been inherited + from a base class. + - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The callables + that are used for comparing and ordering objects by this attribute, + respectively. These are set by passing a callable to `attr.ib`'s ``eq``, + ``order``, or ``cmp`` arguments. See also :ref:`comparison customization + `. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The :ref:`field transformer ` hook receives a list of + them. + - The ``alias`` property exposes the __init__ parameter name of the field, + with any overrides and default private-attribute handling applied. + + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + .. versionadded:: 22.2.0 *alias* + + For the full version history of the fields, see `attr.ib`. + """ + + __slots__ = ( + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + "alias", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + alias=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _obj_setattr.__get__(self) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + types.MappingProxyType(dict(metadata)) # Shallow copy + if metadata + else _empty_metadata_singleton + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + bound_setattr("alias", alias) + + def __setattr__(self, name, value): + raise FrozenInstanceError() + + @classmethod + def from_counting_attr(cls, name, ca, type=None): + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + msg = "Type annotation and type argument cannot both be present" + raise ValueError(msg) + inst_dict = { + k: getattr(ca, k) + for k in Attribute.__slots__ + if k + not in ( + "name", + "validator", + "default", + "type", + "inherited", + ) # exclude methods and deprecated alias + } + return cls( + name=name, + validator=ca._validator, + default=ca._default, + type=type, + cmp=None, + inherited=False, + **inst_dict, + ) + + # Don't use attrs.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attrs.evolve` but that function does not work + with `Attribute`. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _obj_setattr.__get__(self) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + types.MappingProxyType(dict(value)) + if value + else _empty_metadata_singleton, + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + alias=_default_init_alias_for(name), + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr: + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "counter", + "_default", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "_validator", + "converter", + "type", + "kw_only", + "on_setattr", + "alias", + ) + __attrs_attrs__ = ( + *tuple( + Attribute( + name=name, + alias=_default_init_alias_for(name), + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + "alias", + ) + ), + Attribute( + name="metadata", + alias="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + alias, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + self.alias = alias + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + :raises DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError() + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class Factory: + """ + Stores a factory callable. + + If passed as the default value to `attrs.field`, the factory is used to + generate a new value. + + :param callable factory: A callable that takes either none or exactly one + mandatory positional argument depending on *takes_self*. + :param bool takes_self: Pass the partially initialized instance that is + being initialized as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +def make_class( + name, attrs, bases=(object,), class_body=None, **attributes_arguments +): + r""" + A quick way to create a new class called *name* with *attrs*. + + :param str name: The name for the new class. + + :param attrs: A list of names or a dictionary of mappings of names to + `attr.ib`\ s / `attrs.field`\ s. + + The order is deduced from the order of the names or attributes inside + *attrs*. Otherwise the order of the definition of the attributes is + used. + :type attrs: `list` or `dict` + + :param tuple bases: Classes that the new class will subclass. + + :param dict class_body: An optional dictionary of class attributes for the new class. + + :param attributes_arguments: Passed unmodified to `attr.s`. + + :return: A new class with *attrs*. + :rtype: type + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + .. versionchanged:: 23.2.0 *class_body* + """ + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = {a: attrib() for a in attrs} + else: + msg = "attrs argument must be a dict or a list." + raise TypeError(msg) + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if class_body is not None: + body.update(class_body) + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + with contextlib.suppress(AttributeError, ValueError): + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + return _attrs(these=cls_dict, **attributes_arguments)(type_) + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, hash=True) +class _AndValidator: + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + :param callables validators: Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if + they have any. + + :param callables converters: Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + def pipe_converter(val): + for converter in converters: + val = converter(val) + + return val + + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = typing.TypeVar("A") + pipe_converter.__annotations__ = {"val": A, "return": A} + else: + # Get parameter type from first converter. + t = _AnnotationExtractor(converters[0]).get_first_param_type() + if t: + pipe_converter.__annotations__["val"] = t + + # Get return type from last converter. + rt = _AnnotationExtractor(converters[-1]).get_return_type() + if rt: + pipe_converter.__annotations__["return"] = rt + + return pipe_converter diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_next_gen.py b/.pythonlibs/lib/python3.10/site-packages/attr/_next_gen.py new file mode 100644 index 0000000..1fb9f25 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_next_gen.py @@ -0,0 +1,229 @@ +# SPDX-License-Identifier: MIT + +""" +These are keyword-only APIs that call `attr.s` and `attr.ib` with different +default values. +""" + + +from functools import partial + +from . import setters +from ._funcs import asdict as _asdict +from ._funcs import astuple as _astuple +from ._make import ( + NOTHING, + _frozen_setattrs, + _ng_default_on_setattr, + attrib, + attrs, +) +from .exceptions import UnannotatedAttributeError + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + unsafe_hash=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, + match_args=True, +): + r""" + Define an *attrs* class. + + Differences to the classic `attr.s` that it uses underneath: + + - Automatically detect whether or not *auto_attribs* should be `True` (c.f. + *auto_attribs* parameter). + - Converters and validators run when attributes are set by default -- if + *frozen* is `False`. + - *slots=True* + + .. caution:: + + Usually this has only upsides and few visible effects in everyday + programming. But it *can* lead to some surprising behaviors, so please + make sure to read :term:`slotted classes`. + - *auto_exc=True* + - *auto_detect=True* + - *order=False* + - Some options that were only relevant on Python 2 or were kept around for + backwards-compatibility have been removed. + + Please note that these are all defaults and you can change them as you + wish. + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If any attributes are annotated and no unannotated `attrs.fields`\ s + are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attrs.fields`\ s. + + For now, please refer to `attr.s` for the rest of the parameters. + + .. versionadded:: 20.1.0 + .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. + .. versionadded:: 22.2.0 + *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + unsafe_hash=unsafe_hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + match_args=match_args, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes convert & validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = _ng_default_on_setattr + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." + raise ValueError(msg) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, + alias=None, +): + """ + Identical to `attr.ib`, except keyword-only and with some arguments + removed. + + .. versionadded:: 23.1.0 + The *type* parameter has been re-added; mostly for `attrs.make_class`. + Please note that type checkers ignore this metadata. + .. versionadded:: 20.1.0 + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + type=type, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + alias=alias, + ) + + +def asdict(inst, *, recurse=True, filter=None, value_serializer=None): + """ + Same as `attr.asdict`, except that collections types are always retained + and dict is always used as *dict_factory*. + + .. versionadded:: 21.3.0 + """ + return _asdict( + inst=inst, + recurse=recurse, + filter=filter, + value_serializer=value_serializer, + retain_collection_types=True, + ) + + +def astuple(inst, *, recurse=True, filter=None): + """ + Same as `attr.astuple`, except that collections types are always retained + and `tuple` is always used as the *tuple_factory*. + + .. versionadded:: 21.3.0 + """ + return _astuple( + inst=inst, recurse=recurse, filter=filter, retain_collection_types=True + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_typing_compat.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/_typing_compat.pyi new file mode 100644 index 0000000..ca7b71e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_typing_compat.pyi @@ -0,0 +1,15 @@ +from typing import Any, ClassVar, Protocol + +# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`. +MYPY = False + +if MYPY: + # A protocol to be able to statically accept an attrs class. + class AttrsInstance_(Protocol): + __attrs_attrs__: ClassVar[Any] + +else: + # For type checkers without plug-in support use an empty protocol that + # will (hopefully) be combined into a union. + class AttrsInstance_(Protocol): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.py b/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.py new file mode 100644 index 0000000..51a1312 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.py @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: MIT + + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo: + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.pyi new file mode 100644 index 0000000..45ced08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/converters.py b/.pythonlibs/lib/python3.10/site-packages/attr/converters.py new file mode 100644 index 0000000..2bf4c90 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/converters.py @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful converters. +""" + + +import typing + +from ._compat import _AnnotationExtractor +from ._make import NOTHING, Factory, pipe + + +__all__ = [ + "default_if_none", + "optional", + "pipe", + "to_bool", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to ``None``. + + Type annotations will be inferred from the wrapped converter's, if it + has any. + + :param callable converter: the converter that is used for non-``None`` + values. + + .. versionadded:: 17.1.0 + """ + + def optional_converter(val): + if val is None: + return None + return converter(val) + + xtr = _AnnotationExtractor(converter) + + t = xtr.get_first_param_type() + if t: + optional_converter.__annotations__["val"] = typing.Optional[t] + + rt = xtr.get_return_type() + if rt: + optional_converter.__annotations__["return"] = typing.Optional[rt] + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace ``None`` values by *default* or the + result of *factory*. + + :param default: Value to be used if ``None`` is passed. Passing an instance + of `attrs.Factory` is supported, however the ``takes_self`` option + is *not*. + :param callable factory: A callable that takes no parameters whose result + is used if ``None`` is passed. + + :raises TypeError: If **neither** *default* or *factory* is passed. + :raises TypeError: If **both** *default* and *factory* are passed. + :raises ValueError: If an instance of `attrs.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + msg = "Must pass either `default` or `factory`." + raise TypeError(msg) + + if default is not NOTHING and factory is not None: + msg = "Must pass either `default` or `factory` but not both." + raise TypeError(msg) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + msg = "`takes_self` is not supported by default_if_none." + raise ValueError(msg) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter + + +def to_bool(val): + """ + Convert "boolean" strings (e.g., from env. vars.) to real booleans. + + Values mapping to :code:`True`: + + - :code:`True` + - :code:`"true"` / :code:`"t"` + - :code:`"yes"` / :code:`"y"` + - :code:`"on"` + - :code:`"1"` + - :code:`1` + + Values mapping to :code:`False`: + + - :code:`False` + - :code:`"false"` / :code:`"f"` + - :code:`"no"` / :code:`"n"` + - :code:`"off"` + - :code:`"0"` + - :code:`0` + + :raises ValueError: for any other value. + + .. versionadded:: 21.3.0 + """ + if isinstance(val, str): + val = val.lower() + truthy = {True, "true", "t", "yes", "y", "on", "1", 1} + falsy = {False, "false", "f", "no", "n", "off", "0", 0} + try: + if val in truthy: + return True + if val in falsy: + return False + except TypeError: + # Raised when "val" is not hashable (e.g., lists) + pass + msg = f"Cannot convert value to bool: {val}" + raise ValueError(msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/converters.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/converters.pyi new file mode 100644 index 0000000..5abb49f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/converters.pyi @@ -0,0 +1,13 @@ +from typing import Callable, TypeVar, overload + +from . import _ConverterType + +_T = TypeVar("_T") + +def pipe(*validators: _ConverterType) -> _ConverterType: ... +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: _T) -> _ConverterType: ... +@overload +def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ... +def to_bool(val: str) -> bool: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.py new file mode 100644 index 0000000..3b7abb8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +from typing import ClassVar + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + msg = "can't set attribute" + args: ClassVar[tuple[str]] = [msg] + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An *attrs* function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-*attrs* class has been passed into an *attrs* function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set when defining the field and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has a field without a type annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an *attrs* feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A field requiring a callable has been set with a value that is not + callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.pyi new file mode 100644 index 0000000..f268011 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/exceptions.pyi @@ -0,0 +1,17 @@ +from typing import Any + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/filters.py b/.pythonlibs/lib/python3.10/site-packages/attr/filters.py new file mode 100644 index 0000000..a1e40c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/filters.py @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful filters for `attr.asdict`. +""" + +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isinstance(cls, type)), + frozenset(cls for cls in what if isinstance(cls, str)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Include *what*. + + :param what: What to include. + :type what: `list` of classes `type`, field names `str` or + `attrs.Attribute`\\ s + + :rtype: `callable` + + .. versionchanged:: 23.1.0 Accept strings with field names. + """ + cls, names, attrs = _split_what(what) + + def include_(attribute, value): + return ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return include_ + + +def exclude(*what): + """ + Exclude *what*. + + :param what: What to exclude. + :type what: `list` of classes `type`, field names `str` or + `attrs.Attribute`\\ s. + + :rtype: `callable` + + .. versionchanged:: 23.3.0 Accept field name string as input argument + """ + cls, names, attrs = _split_what(what) + + def exclude_(attribute, value): + return not ( + value.__class__ in cls + or attribute.name in names + or attribute in attrs + ) + + return exclude_ diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/filters.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/filters.pyi new file mode 100644 index 0000000..8a02fa0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/filters.pyi @@ -0,0 +1,6 @@ +from typing import Any, Union + +from . import Attribute, _FilterType + +def include(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ... +def exclude(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/py.typed b/.pythonlibs/lib/python3.10/site-packages/attr/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/setters.py b/.pythonlibs/lib/python3.10/site-packages/attr/setters.py new file mode 100644 index 0000000..12ed675 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/setters.py @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly used hooks for on_setattr. +""" + + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError() + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + return c(new_value) + + return new_value + + +# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. +# autodata stopped working, so the docstring is inlined in the API docs. +NO_OP = object() diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/setters.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/setters.pyi new file mode 100644 index 0000000..72f7ce4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/setters.pyi @@ -0,0 +1,19 @@ +from typing import Any, NewType, NoReturn, TypeVar + +from . import Attribute, _OnSetAttrType + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/validators.py b/.pythonlibs/lib/python3.10/site-packages/attr/validators.py new file mode 100644 index 0000000..34d6b76 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/validators.py @@ -0,0 +1,681 @@ +# SPDX-License-Identifier: MIT + +""" +Commonly useful validators. +""" + + +import operator +import re + +from contextlib import contextmanager +from re import Pattern + +from ._config import get_run_validators, set_run_validators +from ._make import _AndValidator, and_, attrib, attrs +from .converters import default_if_none +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "disabled", + "ge", + "get_disabled", + "gt", + "in_", + "instance_of", + "is_callable", + "le", + "lt", + "matches_re", + "max_len", + "min_len", + "not_", + "optional", + "provides", + "set_disabled", +] + + +def set_disabled(disabled): + """ + Globally disable or enable running validators. + + By default, they are run. + + :param disabled: If ``True``, disable running all validators. + :type disabled: bool + + .. warning:: + + This function is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(not disabled) + + +def get_disabled(): + """ + Return a bool indicating whether validators are currently disabled or not. + + :return: ``True`` if validators are currently disabled. + :rtype: bool + + .. versionadded:: 21.3.0 + """ + return not get_run_validators() + + +@contextmanager +def disabled(): + """ + Context manager that disables running validators within its context. + + .. warning:: + + This context manager is not thread-safe! + + .. versionadded:: 21.3.0 + """ + set_run_validators(False) + try: + yield + finally: + set_run_validators(True) + + +@attrs(repr=False, slots=True, hash=True) +class _InstanceOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + msg = "'{name}' must be {type!r} (got {value!r} that is a {actual!r}).".format( + name=attr.name, + type=self.type, + actual=value.__class__, + value=value, + ) + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called + with a wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + :param type: The type to check for. + :type type: type or tuple of type + + :raises TypeError: With a human readable error message, the attribute + (of type `attrs.Attribute`), the expected type, and the value it + got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator: + pattern = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + msg = "'{name}' must match regex {pattern!r} ({value!r} doesn't)".format( + name=attr.name, pattern=self.pattern.pattern, value=value + ) + raise ValueError( + msg, + attr, + self.pattern, + value, + ) + + def __repr__(self): + return f"" + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called + with a string that doesn't match *regex*. + + :param regex: a regex string or precompiled pattern to match against + :param int flags: flags that will be passed to the underlying re function + (default 0) + :param callable func: which underlying `re` function to call. Valid options + are `re.fullmatch`, `re.search`, and `re.match`; the default ``None`` + means `re.fullmatch`. For performance reasons, the pattern is always + precompiled using `re.compile`. + + .. versionadded:: 19.2.0 + .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. + """ + valid_funcs = (re.fullmatch, None, re.search, re.match) + if func not in valid_funcs: + msg = "'func' must be one of {}.".format( + ", ".join( + sorted(e and e.__name__ or "None" for e in set(valid_funcs)) + ) + ) + raise ValueError(msg) + + if isinstance(regex, Pattern): + if flags: + msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead" + raise TypeError(msg) + pattern = regex + else: + pattern = re.compile(regex, flags) + + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + match_func = pattern.fullmatch + + return _MatchesReValidator(pattern, match_func) + + +@attrs(repr=False, slots=True, hash=True) +class _ProvidesValidator: + interface = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.interface.providedBy(value): + msg = "'{name}' must provide {interface!r} which {value!r} doesn't.".format( + name=attr.name, interface=self.interface, value=value + ) + raise TypeError( + msg, + attr, + self.interface, + value, + ) + + def __repr__(self): + return f"" + + +def provides(interface): + """ + A validator that raises a `TypeError` if the initializer is called + with an object that does not provide the requested *interface* (checks are + performed using ``interface.providedBy(value)`` (see `zope.interface + `_). + + :param interface: The interface to check for. + :type interface: ``zope.interface.Interface`` + + :raises TypeError: With a human readable error message, the attribute + (of type `attrs.Attribute`), the expected interface, and the + value it got. + + .. deprecated:: 23.1.0 + """ + import warnings + + warnings.warn( + "attrs's zope-interface support is deprecated and will be removed in, " + "or after, April 2024.", + DeprecationWarning, + stacklevel=2, + ) + return _ProvidesValidator(interface) + + +@attrs(repr=False, slots=True, hash=True) +class _OptionalValidator: + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return f"" + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to ``None`` in addition to satisfying the requirements of + the sub-validator. + + :param Callable | tuple[Callable] | list[Callable] validator: A validator + (or validators) that is used for non-``None`` values. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators. + """ + if isinstance(validator, (list, tuple)): + return _OptionalValidator(_AndValidator(validator)) + + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, hash=True) +class _InValidator: + options = attrib() + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + msg = f"'{attr.name}' must be in {self.options!r} (got {value!r})" + raise ValueError( + msg, + attr, + self.options, + value, + ) + + def __repr__(self): + return f"" + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called + with a value that does not belong in the options provided. The check is + performed using ``value in options``. + + :param options: Allowed options. + :type options: list, tuple, `enum.Enum`, ... + + :raises ValueError: With a human readable error message, the attribute (of + type `attrs.Attribute`), the expected options, and the value it + got. + + .. versionadded:: 17.1.0 + .. versionchanged:: 22.1.0 + The ValueError was incomplete until now and only contained the human + readable error message. Now it contains all the information that has + been promised since 17.1.0. + """ + return _InValidator(options) + + +@attrs(repr=False, slots=False, hash=True) +class _IsCallableValidator: + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attrs.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute + that is not callable. + + .. versionadded:: 19.1.0 + + :raises attrs.exceptions.NotCallableError: With a human readable error + message containing the attribute (`attrs.Attribute`) name, + and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, hash=True) +class _DeepIterable: + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else f" {self.iterable_validator!r}" + ) + return ( + f"" + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + :param member_validator: Validator(s) to apply to iterable members + :param iterable_validator: Validator to apply to iterable itself + (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + if isinstance(member_validator, (list, tuple)): + member_validator = and_(*member_validator) + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, hash=True) +class _DeepMapping: + key_validator = attrib(validator=is_callable()) + value_validator = attrib(validator=is_callable()) + mapping_validator = attrib(default=None, validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + self.key_validator(inst, attr, key) + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return ( + "" + ).format(key=self.key_validator, value=self.value_validator) + + +def deep_mapping(key_validator, value_validator, mapping_validator=None): + """ + A validator that performs deep validation of a dictionary. + + :param key_validator: Validator to apply to dictionary keys + :param value_validator: Validator to apply to dictionary values + :param mapping_validator: Validator to apply to top-level mapping + attribute (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepMapping(key_validator, value_validator, mapping_validator) + + +@attrs(repr=False, frozen=True, slots=True) +class _NumberValidator: + bound = attrib() + compare_op = attrib() + compare_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.compare_func(value, self.bound): + msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def lt(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number larger or equal to *val*. + + :param val: Exclusive upper bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<", operator.lt) + + +def le(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number greater than *val*. + + :param val: Inclusive upper bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, "<=", operator.le) + + +def ge(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number smaller than *val*. + + :param val: Inclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">=", operator.ge) + + +def gt(val): + """ + A validator that raises `ValueError` if the initializer is called + with a number smaller or equal to *val*. + + :param val: Exclusive lower bound for values + + .. versionadded:: 21.3.0 + """ + return _NumberValidator(val, ">", operator.gt) + + +@attrs(repr=False, frozen=True, slots=True) +class _MaxLengthValidator: + max_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) > self.max_length: + msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def max_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is longer than *length*. + + :param int length: Maximum length of the string or iterable + + .. versionadded:: 21.3.0 + """ + return _MaxLengthValidator(length) + + +@attrs(repr=False, frozen=True, slots=True) +class _MinLengthValidator: + min_length = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if len(value) < self.min_length: + msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}" + raise ValueError(msg) + + def __repr__(self): + return f"" + + +def min_len(length): + """ + A validator that raises `ValueError` if the initializer is called + with a string or iterable that is shorter than *length*. + + :param int length: Minimum length of the string or iterable + + .. versionadded:: 22.1.0 + """ + return _MinLengthValidator(length) + + +@attrs(repr=False, slots=True, hash=True) +class _SubclassOfValidator: + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not issubclass(value, self.type): + msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})." + raise TypeError( + msg, + attr, + self.type, + value, + ) + + def __repr__(self): + return f"" + + +def _subclass_of(type): + """ + A validator that raises a `TypeError` if the initializer is called + with a wrong type for this particular attribute (checks are performed using + `issubclass` therefore it's also valid to pass a tuple of types). + + :param type: The type to check for. + :type type: type or tuple of types + + :raises TypeError: With a human readable error message, the attribute + (of type `attrs.Attribute`), the expected type, and the value it + got. + """ + return _SubclassOfValidator(type) + + +@attrs(repr=False, slots=True, hash=True) +class _NotValidator: + validator = attrib() + msg = attrib( + converter=default_if_none( + "not_ validator child '{validator!r}' " + "did not raise a captured error" + ) + ) + exc_types = attrib( + validator=deep_iterable( + member_validator=_subclass_of(Exception), + iterable_validator=instance_of(tuple), + ), + ) + + def __call__(self, inst, attr, value): + try: + self.validator(inst, attr, value) + except self.exc_types: + pass # suppress error to invert validity + else: + raise ValueError( + self.msg.format( + validator=self.validator, + exc_types=self.exc_types, + ), + attr, + self.validator, + value, + self.exc_types, + ) + + def __repr__(self): + return ( + "" + ).format( + what=self.validator, + exc_types=self.exc_types, + ) + + +def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)): + """ + A validator that wraps and logically 'inverts' the validator passed to it. + It will raise a `ValueError` if the provided validator *doesn't* raise a + `ValueError` or `TypeError` (by default), and will suppress the exception + if the provided validator *does*. + + Intended to be used with existing validators to compose logic without + needing to create inverted variants, for example, ``not_(in_(...))``. + + :param validator: A validator to be logically inverted. + :param msg: Message to raise if validator fails. + Formatted with keys ``exc_types`` and ``validator``. + :type msg: str + :param exc_types: Exception type(s) to capture. + Other types raised by child validators will not be intercepted and + pass through. + + :raises ValueError: With a human readable error message, + the attribute (of type `attrs.Attribute`), + the validator that failed to raise an exception, + the value it got, + and the expected exception types. + + .. versionadded:: 22.2.0 + """ + try: + exc_types = tuple(exc_types) + except TypeError: + exc_types = (exc_types,) + return _NotValidator(validator, msg, exc_types) diff --git a/.pythonlibs/lib/python3.10/site-packages/attr/validators.pyi b/.pythonlibs/lib/python3.10/site-packages/attr/validators.pyi new file mode 100644 index 0000000..d194a75 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attr/validators.pyi @@ -0,0 +1,88 @@ +from typing import ( + Any, + AnyStr, + Callable, + Container, + ContextManager, + Iterable, + List, + Mapping, + Match, + Optional, + Pattern, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +from . import _ValidatorType +from . import _ValidatorArgType + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +def set_disabled(run: bool) -> None: ... +def get_disabled() -> bool: ... +def disabled() -> ContextManager[None]: ... + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2]] +) -> _ValidatorType[Union[_T1, _T2]]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2], Type[_T3]] +) -> _ValidatorType[Union[_T1, _T2, _T3]]: ... +@overload +def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ... +def provides(interface: Any) -> _ValidatorType[Any]: ... +def optional( + validator: Union[ + _ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T]] + ] +) -> _ValidatorType[Optional[_T]]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: Union[Pattern[AnyStr], AnyStr], + flags: int = ..., + func: Optional[ + Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]] + ] = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorArgType[_T], + iterable_validator: Optional[_ValidatorType[_I]] = ..., +) -> _ValidatorType[_I]: ... +def deep_mapping( + key_validator: _ValidatorType[_K], + value_validator: _ValidatorType[_V], + mapping_validator: Optional[_ValidatorType[_M]] = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... +def lt(val: _T) -> _ValidatorType[_T]: ... +def le(val: _T) -> _ValidatorType[_T]: ... +def ge(val: _T) -> _ValidatorType[_T]: ... +def gt(val: _T) -> _ValidatorType[_T]: ... +def max_len(length: int) -> _ValidatorType[_T]: ... +def min_len(length: int) -> _ValidatorType[_T]: ... +def not_( + validator: _ValidatorType[_T], + *, + msg: Optional[str] = None, + exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ..., +) -> _ValidatorType[_T]: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/METADATA new file mode 100644 index 0000000..c20be76 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/METADATA @@ -0,0 +1,202 @@ +Metadata-Version: 2.1 +Name: attrs +Version: 23.2.0 +Summary: Classes Without Boilerplate +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: GitHub, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Author-email: Hynek Schlawack +License-Expression: MIT +License-File: LICENSE +Keywords: attribute,boilerplate,class +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Requires-Dist: importlib-metadata; python_version < '3.8' +Provides-Extra: cov +Requires-Dist: attrs[tests]; extra == 'cov' +Requires-Dist: coverage[toml]>=5.3; extra == 'cov' +Provides-Extra: dev +Requires-Dist: attrs[tests]; extra == 'dev' +Requires-Dist: pre-commit; extra == 'dev' +Provides-Extra: docs +Requires-Dist: furo; extra == 'docs' +Requires-Dist: myst-parser; extra == 'docs' +Requires-Dist: sphinx; extra == 'docs' +Requires-Dist: sphinx-notfound-page; extra == 'docs' +Requires-Dist: sphinxcontrib-towncrier; extra == 'docs' +Requires-Dist: towncrier; extra == 'docs' +Requires-Dist: zope-interface; extra == 'docs' +Provides-Extra: tests +Requires-Dist: attrs[tests-no-zope]; extra == 'tests' +Requires-Dist: zope-interface; extra == 'tests' +Provides-Extra: tests-mypy +Requires-Dist: mypy>=1.6; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy' +Requires-Dist: pytest-mypy-plugins; (platform_python_implementation == 'CPython' and python_version >= '3.8') and extra == 'tests-mypy' +Provides-Extra: tests-no-zope +Requires-Dist: attrs[tests-mypy]; extra == 'tests-no-zope' +Requires-Dist: cloudpickle; (platform_python_implementation == 'CPython') and extra == 'tests-no-zope' +Requires-Dist: hypothesis; extra == 'tests-no-zope' +Requires-Dist: pympler; extra == 'tests-no-zope' +Requires-Dist: pytest-xdist[psutil]; extra == 'tests-no-zope' +Requires-Dist: pytest>=4.3.0; extra == 'tests-no-zope' +Description-Content-Type: text/markdown + +

    + + attrs + +

    + + +*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). +[Trusted by NASA](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile#list-of-qualifying-repositories-for-mars-2020-helicopter-contributor-achievement) for Mars missions since 2020! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + + +## Sponsors + +*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). +Especially those generously supporting us at the *The Organization* tier and higher: + +

    + + + +

    + +

    + Please consider joining them to help make attrs’s maintenance more sustainable! +

    + + + +## Example + +*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: + + + +```pycon +>>> from attrs import asdict, define, make_class, Factory + +>>> @define +... class SomeClass: +... a_number: int = 42 +... list_of_numbers: list[int] = Factory(list) +... +... def hard_math(self, another_number): +... return self.a_number + sum(self.list_of_numbers) * another_number + + +>>> sc = SomeClass(1, [1, 2, 3]) +>>> sc +SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + +>>> sc.hard_math(3) +19 +>>> sc == SomeClass(1, [1, 2, 3]) +True +>>> sc != SomeClass(2, [3, 2, 1]) +True + +>>> asdict(sc) +{'a_number': 1, 'list_of_numbers': [1, 2, 3]} + +>>> SomeClass() +SomeClass(a_number=42, list_of_numbers=[]) + +>>> C = make_class("C", ["a", "b"]) +>>> C("foo", "bar") +C(a='foo', b='bar') +``` + +After *declaring* your attributes, *attrs* gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable `__repr__`, +- equality-checking methods, +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +**Hate type annotations**!? +No problem! +Types are entirely **optional** with *attrs*. +Simply assign `attrs.field()` to the attributes instead of annotating them with types. + +--- + +This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. +The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. + +Please check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for a more in-depth explanation. + + +## Data Classes + +On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). +In practice it does a lot more and is more flexible. +For instance it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), and allows for stepping through the generated methods using a debugger. + +For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes). + + +## Project Information + +- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) +- [**Documentation**](https://www.attrs.org/) +- [**PyPI**](https://pypi.org/project/attrs/) +- [**Source Code**](https://github.com/python-attrs/attrs) +- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) +- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) +- **Get Help**: please use the `python-attrs` tag on [StackOverflow](https://stackoverflow.com/questions/tagged/python-attrs) + + +### *attrs* for Enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. +[Learn more.](https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +## Release Information + +### Changes + +- The type annotation for `attrs.resolve_types()` is now correct. + [#1141](https://github.com/python-attrs/attrs/issues/1141) +- Type stubs now use `typing.dataclass_transform` to decorate dataclass-like decorators, instead of the non-standard `__dataclass_transform__` special form, which is only supported by Pyright. + [#1158](https://github.com/python-attrs/attrs/issues/1158) +- Fixed serialization of namedtuple fields using `attrs.asdict/astuple()` with `retain_collection_types=True`. + [#1165](https://github.com/python-attrs/attrs/issues/1165) +- `attrs.AttrsInstance` is now a `typing.Protocol` in both type hints and code. + This allows you to subclass it along with another `Protocol`. + [#1172](https://github.com/python-attrs/attrs/issues/1172) +- If *attrs* detects that `__attrs_pre_init__` accepts more than just `self`, it will call it with the same arguments as `__init__` was called. + This allows you to, for example, pass arguments to `super().__init__()`. + [#1187](https://github.com/python-attrs/attrs/issues/1187) +- Slotted classes now transform `functools.cached_property` decorated methods to support equivalent semantics. + [#1200](https://github.com/python-attrs/attrs/issues/1200) +- Added *class_body* argument to `attrs.make_class()` to provide additional attributes for newly created classes. + It is, for example, now possible to attach methods. + [#1203](https://github.com/python-attrs/attrs/issues/1203) + + +--- + +[Full changelog](https://www.attrs.org/en/stable/changelog.html) diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/RECORD new file mode 100644 index 0000000..584771b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/RECORD @@ -0,0 +1,55 @@ +attr/__init__.py,sha256=WlXJN6ICB0Y_HZ0lmuTUgia0kuSdn2p67d4N6cYxNZM,3307 +attr/__init__.pyi,sha256=u08EujYHy_rSyebNn-I9Xv2S_cXmtA9xWGc0cBsyl18,16976 +attr/__pycache__/__init__.cpython-310.pyc,, +attr/__pycache__/_cmp.cpython-310.pyc,, +attr/__pycache__/_compat.cpython-310.pyc,, +attr/__pycache__/_config.cpython-310.pyc,, +attr/__pycache__/_funcs.cpython-310.pyc,, +attr/__pycache__/_make.cpython-310.pyc,, +attr/__pycache__/_next_gen.cpython-310.pyc,, +attr/__pycache__/_version_info.cpython-310.pyc,, +attr/__pycache__/converters.cpython-310.pyc,, +attr/__pycache__/exceptions.cpython-310.pyc,, +attr/__pycache__/filters.cpython-310.pyc,, +attr/__pycache__/setters.cpython-310.pyc,, +attr/__pycache__/validators.cpython-310.pyc,, +attr/_cmp.py,sha256=OQZlWdFX74z18adGEUp40Ojqm0NNu1Flqnv2JE8B2ng,4025 +attr/_cmp.pyi,sha256=sGQmOM0w3_K4-X8cTXR7g0Hqr290E8PTObA9JQxWQqc,399 +attr/_compat.py,sha256=QmRyxii295wcQfaugWqxuIumAPsNQ2-RUF82QZPqMKw,2540 +attr/_config.py,sha256=z81Vt-GeT_2taxs1XZfmHx9TWlSxjPb6eZH1LTGsS54,843 +attr/_funcs.py,sha256=VBTUFKLklsmqxys3qWSTK_Ac9Z4s0mAJWwgW9nA7Llk,17173 +attr/_make.py,sha256=LnVy2e0HygoqaZknhC19z7JmOt7qGkAadf2LZgWVJWI,101923 +attr/_next_gen.py,sha256=as1voi8siAI_o2OQG8YIiZvmn0G7-S3_j_774rnoZ_g,6203 +attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 +attr/_version_info.py,sha256=exSqb3b5E-fMSsgZAlEw9XcLpEgobPORCZpcaEglAM4,2121 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=Kyw5MY0yfnUR_RwN1Vydf0EiE---htDxOgSc_-NYL6A,3622 +attr/converters.pyi,sha256=jKlpHBEt6HVKJvgrMFJRrHq8p61GXg4-Nd5RZWKJX7M,406 +attr/exceptions.py,sha256=HRFq4iybmv7-DcZwyjl6M1euM2YeJVK_hFxuaBGAngI,1977 +attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 +attr/filters.py,sha256=9pYvXqdg6mtLvKIIb56oALRMoHFnQTcGCO4EXTc1qyM,1470 +attr/filters.pyi,sha256=0mRCjLKxdcvAo0vD-Cr81HfRXXCp9j_cAXjOoAHtPGM,225 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=pbCZQ-pE6ZxjDqZfWWUhUFefXtpekIU4qS_YDMLPQ50,1400 +attr/setters.pyi,sha256=pyY8TVNBu8TWhOldv_RxHzmGvdgFQH981db70r0fn5I,567 +attr/validators.py,sha256=LGVpbiNg_KGzYrKUD5JPiZkx8TMfynDZGoQoLJNCIMo,19676 +attr/validators.pyi,sha256=167Dl9nt7NUhE9wht1I-buo039qyUT1nEUT_nKjSWr4,2580 +attrs-23.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-23.2.0.dist-info/METADATA,sha256=WwvG7OHyKjEPpyFUZCCYt1n0E_CcqdRb7bliGEdcm-A,9531 +attrs-23.2.0.dist-info/RECORD,, +attrs-23.2.0.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87 +attrs-23.2.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 +attrs/__init__.py,sha256=9_5waVbFs7rLqtXZ73tNDrxhezyZ8VZeX4BbvQ3EeJw,1039 +attrs/__init__.pyi,sha256=s_ajQ_U14DOsOz0JbmAKDOi46B3v2PcdO0UAV1MY6Ek,2168 +attrs/__pycache__/__init__.cpython-310.pyc,, +attrs/__pycache__/converters.cpython-310.pyc,, +attrs/__pycache__/exceptions.cpython-310.pyc,, +attrs/__pycache__/filters.cpython-310.pyc,, +attrs/__pycache__/setters.cpython-310.pyc,, +attrs/__pycache__/validators.cpython-310.pyc,, +attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 +attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 +attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 +attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 +attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/WHEEL new file mode 100644 index 0000000..2860816 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.21.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/licenses/LICENSE b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..2bd6453 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs-23.2.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack and the attrs contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.py b/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.py new file mode 100644 index 0000000..0c24815 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: MIT + +from attr import ( + NOTHING, + Attribute, + AttrsInstance, + Factory, + _make_getattr, + assoc, + cmp_using, + define, + evolve, + field, + fields, + fields_dict, + frozen, + has, + make_class, + mutable, + resolve_types, + validate, +) +from attr._next_gen import asdict, astuple + +from . import converters, exceptions, filters, setters, validators + + +__all__ = [ + "__author__", + "__copyright__", + "__description__", + "__doc__", + "__email__", + "__license__", + "__title__", + "__url__", + "__version__", + "__version_info__", + "asdict", + "assoc", + "astuple", + "Attribute", + "AttrsInstance", + "cmp_using", + "converters", + "define", + "evolve", + "exceptions", + "Factory", + "field", + "fields_dict", + "fields", + "filters", + "frozen", + "has", + "make_class", + "mutable", + "NOTHING", + "resolve_types", + "setters", + "validate", + "validators", +] + +__getattr__ = _make_getattr(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.pyi new file mode 100644 index 0000000..9372cfe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/__init__.pyi @@ -0,0 +1,67 @@ +from typing import ( + Any, + Callable, + Dict, + Mapping, + Optional, + Sequence, + Tuple, + Type, +) + +# Because we need to type our own stuff, we have to make everything from +# attr explicitly public too. +from attr import __author__ as __author__ +from attr import __copyright__ as __copyright__ +from attr import __description__ as __description__ +from attr import __email__ as __email__ +from attr import __license__ as __license__ +from attr import __title__ as __title__ +from attr import __url__ as __url__ +from attr import __version__ as __version__ +from attr import __version_info__ as __version_info__ +from attr import _FilterType +from attr import assoc as assoc +from attr import Attribute as Attribute +from attr import AttrsInstance as AttrsInstance +from attr import cmp_using as cmp_using +from attr import converters as converters +from attr import define as define +from attr import evolve as evolve +from attr import exceptions as exceptions +from attr import Factory as Factory +from attr import field as field +from attr import fields as fields +from attr import fields_dict as fields_dict +from attr import filters as filters +from attr import frozen as frozen +from attr import has as has +from attr import make_class as make_class +from attr import mutable as mutable +from attr import NOTHING as NOTHING +from attr import resolve_types as resolve_types +from attr import setters as setters +from attr import validate as validate +from attr import validators as validators + +# TODO: see definition of attr.asdict/astuple +def asdict( + inst: AttrsInstance, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[ + Callable[[type, Attribute[Any], Any], Any] + ] = ..., + tuple_keys: bool = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: AttrsInstance, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8f1f6f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/converters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/converters.cpython-310.pyc new file mode 100644 index 0000000..621910b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/converters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..a336a17 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/filters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/filters.cpython-310.pyc new file mode 100644 index 0000000..2f2e9d7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/filters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/setters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/setters.cpython-310.pyc new file mode 100644 index 0000000..1c24fda Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/setters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/validators.cpython-310.pyc new file mode 100644 index 0000000..847bd76 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/attrs/__pycache__/validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/converters.py b/.pythonlibs/lib/python3.10/site-packages/attrs/converters.py new file mode 100644 index 0000000..7821f6c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/converters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.converters import * # noqa: F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/attrs/exceptions.py new file mode 100644 index 0000000..3323f9d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/exceptions.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.exceptions import * # noqa: F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/filters.py b/.pythonlibs/lib/python3.10/site-packages/attrs/filters.py new file mode 100644 index 0000000..3080f48 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/filters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.filters import * # noqa: F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/py.typed b/.pythonlibs/lib/python3.10/site-packages/attrs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/setters.py b/.pythonlibs/lib/python3.10/site-packages/attrs/setters.py new file mode 100644 index 0000000..f3d73bb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/setters.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.setters import * # noqa: F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/attrs/validators.py b/.pythonlibs/lib/python3.10/site-packages/attrs/validators.py new file mode 100644 index 0000000..037e124 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/attrs/validators.py @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT + +from attr.validators import * # noqa: F403 diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/LICENSE new file mode 100644 index 0000000..62b076c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/LICENSE @@ -0,0 +1,20 @@ +This package contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/METADATA new file mode 100644 index 0000000..c688a62 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/METADATA @@ -0,0 +1,66 @@ +Metadata-Version: 2.1 +Name: certifi +Version: 2024.2.2 +Summary: Python package for providing Mozilla's CA Bundle. +Home-page: https://github.com/certifi/python-certifi +Author: Kenneth Reitz +Author-email: me@kennethreitz.com +License: MPL-2.0 +Project-URL: Source, https://github.com/certifi/python-certifi +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=3.6 +License-File: LICENSE + +Certifi: Python SSL Certificates +================================ + +Certifi provides Mozilla's carefully curated collection of Root Certificates for +validating the trustworthiness of SSL certificates while verifying the identity +of TLS hosts. It has been extracted from the `Requests`_ project. + +Installation +------------ + +``certifi`` is available on PyPI. Simply install it with ``pip``:: + + $ pip install certifi + +Usage +----- + +To reference the installed certificate authority (CA) bundle, you can use the +built-in function:: + + >>> import certifi + + >>> certifi.where() + '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' + +Or from the command line:: + + $ python -m certifi + /usr/local/lib/python3.7/site-packages/certifi/cacert.pem + +Enjoy! + +.. _`Requests`: https://requests.readthedocs.io/en/master/ + +Addition/Removal of Certificates +-------------------------------- + +Certifi does not support any addition/removal or other modification of the +CA trust store content. This project is intended to provide a reliable and +highly portable root of trust to python deployments. Look to upstream projects +for methods to use alternate trust. diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/RECORD new file mode 100644 index 0000000..cfe6989 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/RECORD @@ -0,0 +1,14 @@ +certifi-2024.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +certifi-2024.2.2.dist-info/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 +certifi-2024.2.2.dist-info/METADATA,sha256=1noreLRChpOgeSj0uJT1mehiBl8ngh33Guc7KdvzYYM,2170 +certifi-2024.2.2.dist-info/RECORD,, +certifi-2024.2.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +certifi-2024.2.2.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 +certifi/__init__.py,sha256=ljtEx-EmmPpTe2SOd5Kzsujm_lUD0fKJVnE9gzce320,94 +certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 +certifi/__pycache__/__init__.cpython-310.pyc,, +certifi/__pycache__/__main__.cpython-310.pyc,, +certifi/__pycache__/core.cpython-310.pyc,, +certifi/cacert.pem,sha256=ejR8qP724p-CtuR4U1WmY1wX-nVeCUD2XxWqj8e9f5I,292541 +certifi/core.py,sha256=qRDDFyXVJwTB_EmoGppaXU_R9qCZvhl-EzxPMuV3nTA,4426 +certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/top_level.txt new file mode 100644 index 0000000..963eac5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi-2024.2.2.dist-info/top_level.txt @@ -0,0 +1 @@ +certifi diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/__init__.py b/.pythonlibs/lib/python3.10/site-packages/certifi/__init__.py new file mode 100644 index 0000000..1c91f3e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi/__init__.py @@ -0,0 +1,4 @@ +from .core import contents, where + +__all__ = ["contents", "where"] +__version__ = "2024.02.02" diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/__main__.py b/.pythonlibs/lib/python3.10/site-packages/certifi/__main__.py new file mode 100644 index 0000000..8945b5d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from certifi import contents, where + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--contents", action="store_true") +args = parser.parse_args() + +if args.contents: + print(contents()) +else: + print(where()) diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a497e8c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__main__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000..cc92680 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/__main__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/core.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000..25a0868 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/certifi/__pycache__/core.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/cacert.pem b/.pythonlibs/lib/python3.10/site-packages/certifi/cacert.pem new file mode 100644 index 0000000..fac3c31 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi/cacert.pem @@ -0,0 +1,4814 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G4" +# Serial: 289383649854506086828220374796556676440 +# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88 +# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01 +# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88 +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global Certification Authority" +# Serial: 1846098327275375458322922162 +# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e +# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 +# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P256 Certification Authority" +# Serial: 4151900041497450638097112925 +# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 +# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf +# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P384 Certification Authority" +# Serial: 2704997926503831671788816187 +# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 +# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 +# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Label: "GLOBALTRUST 2020" +# Serial: 109160994242082918454945253 +# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 +# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 +# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R2 O=Google Trust Services LLC +# Subject: CN=GTS Root R2 O=Google Trust Services LLC +# Label: "GTS Root R2" +# Serial: 159662449406622349769042896298 +# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc +# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 +# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication RootCA3" +# Serial: 16247922307909811815 +# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26 +# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a +# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94 +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV +BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw +JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2 +MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg +Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r +CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA +lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG +TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7 +9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7 +8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4 +g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we +GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst ++3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M +0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ +T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw +HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA +FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd +9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI +UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+ +OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke +gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf +iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV +nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD +2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI// +1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad +TdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root E46" +# Serial: 88989738453351742415770396670917916916 +# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01 +# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a +# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83 +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root R46" +# Serial: 156256931880233212765902055439220583700 +# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5 +# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38 +# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06 +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS RSA Root CA 2022" +# Serial: 148535279242832292258835760425842727825 +# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da +# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca +# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS ECC Root CA 2022" +# Serial: 26605119622390491762507526719404364228 +# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5 +# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39 +# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43 +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA ECC TLS 2021" +# Serial: 81873346711060652204712539181482831616 +# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8 +# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd +# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8 +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA RSA TLS 2021" +# Serial: 111436099570196163832749341232207667876 +# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2 +# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48 +# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G3 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G3" +# Serial: 576386314500428537169965010905813481816650257167 +# MD5 Fingerprint: 30:42:1b:b7:bb:81:75:35:e4:16:4f:53:d2:94:de:04 +# SHA1 Fingerprint: 63:cf:b6:c1:27:2b:56:e4:88:8e:1c:23:9a:b6:2e:81:47:24:c3:c7 +# SHA256 Fingerprint: e0:d3:22:6a:eb:11:63:c2:e4:8f:f9:be:3b:50:b4:c6:43:1b:e7:bb:1e:ac:c5:c3:6b:5d:5e:c5:09:03:9a:08 +-----BEGIN CERTIFICATE----- +MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEM +BQAwWjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dp +ZXMsIEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAe +Fw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEwMTlaMFoxCzAJBgNVBAYTAkNOMSUw +IwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtU +cnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNS +T1QY4SxzlZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqK +AtCWHwDNBSHvBm3dIZwZQ0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1 +nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/VP68czH5GX6zfZBCK70bwkPAPLfSIC7Ep +qq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1AgdB4SQXMeJNnKziyhWTXA +yB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm9WAPzJMs +hH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gX +zhqcD0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAv +kV34PmVACxmZySYgWmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msT +f9FkPz2ccEblooV7WIQn3MSAPmeamseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jA +uPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCFTIcQcf+eQxuulXUtgQIDAQAB +o2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj7zjKsK5Xf/Ih +MBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E +BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4 +wM8zAQLpw6o1D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2 +XFNFV1pF1AWZLy4jVe5jaN/TG3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1 +JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNjduMNhXJEIlU/HHzp/LgV6FL6qj6j +ITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstlcHboCoWASzY9M/eV +VHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys+TIx +xHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1on +AX1daBli2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d +7XB4tmBZrOFdRWOPyN9yaFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2Ntjj +gKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsASZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV ++Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFRJQJ6+N1rZdVtTTDIZbpo +FGWsJwt0ivKH +-----END CERTIFICATE----- + +# Issuer: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Subject: CN=TrustAsia Global Root CA G4 O=TrustAsia Technologies, Inc. +# Label: "TrustAsia Global Root CA G4" +# Serial: 451799571007117016466790293371524403291602933463 +# MD5 Fingerprint: 54:dd:b2:d7:5f:d8:3e:ed:7c:e0:0b:2e:cc:ed:eb:eb +# SHA1 Fingerprint: 57:73:a5:61:5d:80:b2:e6:ac:38:82:fc:68:07:31:ac:9f:b5:92:5a +# SHA256 Fingerprint: be:4b:56:cb:50:56:c0:13:6a:52:6d:f4:44:50:8d:aa:36:a0:b5:4f:42:e4:ac:38:f7:2a:f4:70:e4:79:65:4c +-----BEGIN CERTIFICATE----- +MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMw +WjELMAkGA1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs +IEluYy4xJDAiBgNVBAMMG1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0y +MTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJaMFoxCzAJBgNVBAYTAkNOMSUwIwYD +VQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQDDBtUcnVz +dEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATx +s8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbw +LxYI+hW8m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJij +YzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mD +pm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/pDHel4NZg6ZvccveMA4GA1UdDwEB/wQE +AwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AAbbd+NvBNEU/zy4k6LHiR +UKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xkdUfFVZDj +/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== +-----END CERTIFICATE----- + +# Issuer: CN=CommScope Public Trust ECC Root-01 O=CommScope +# Subject: CN=CommScope Public Trust ECC Root-01 O=CommScope +# Label: "CommScope Public Trust ECC Root-01" +# Serial: 385011430473757362783587124273108818652468453534 +# MD5 Fingerprint: 3a:40:a7:fc:03:8c:9c:38:79:2f:3a:a2:6c:b6:0a:16 +# SHA1 Fingerprint: 07:86:c0:d8:dd:8e:c0:80:98:06:98:d0:58:7a:ef:de:a6:cc:a2:5d +# SHA256 Fingerprint: 11:43:7c:da:7b:b4:5e:41:36:5f:45:b3:9a:38:98:6b:0d:e0:0d:ef:34:8e:0c:7b:b0:87:36:33:80:0b:c3:8b +-----BEGIN CERTIFICATE----- +MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMw +TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t +bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNa +Fw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv +cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDEw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLxeP0C +flfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJE +hRGnSjot6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggq +hkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg +2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liWpDVfG2XqYZpwI7UNo5uS +Um9poIyNStDuiw7LR47QjRE= +-----END CERTIFICATE----- + +# Issuer: CN=CommScope Public Trust ECC Root-02 O=CommScope +# Subject: CN=CommScope Public Trust ECC Root-02 O=CommScope +# Label: "CommScope Public Trust ECC Root-02" +# Serial: 234015080301808452132356021271193974922492992893 +# MD5 Fingerprint: 59:b0:44:d5:65:4d:b8:5c:55:19:92:02:b6:d1:94:b2 +# SHA1 Fingerprint: 3c:3f:ef:57:0f:fe:65:93:86:9e:a0:fe:b0:f6:ed:8e:d1:13:c7:e5 +# SHA256 Fingerprint: 2f:fb:7f:81:3b:bb:b3:c8:9a:b4:e8:16:2d:0f:16:d7:15:09:a8:30:cc:9d:73:c2:62:e5:14:08:75:d1:ad:4a +-----BEGIN CERTIFICATE----- +MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMw +TjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29t +bVNjb3BlIFB1YmxpYyBUcnVzdCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRa +Fw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21tU2Nv +cGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgRUNDIFJvb3QtMDIw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/MMDAL +j2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmU +v4RDsNuESgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggq +hkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/n +ich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs73u1Z/GtMMH9ZzkXpc2AV +mkzw5l4lIhVtwodZ0LKOag== +-----END CERTIFICATE----- + +# Issuer: CN=CommScope Public Trust RSA Root-01 O=CommScope +# Subject: CN=CommScope Public Trust RSA Root-01 O=CommScope +# Label: "CommScope Public Trust RSA Root-01" +# Serial: 354030733275608256394402989253558293562031411421 +# MD5 Fingerprint: 0e:b4:15:bc:87:63:5d:5d:02:73:d4:26:38:68:73:d8 +# SHA1 Fingerprint: 6d:0a:5f:f7:b4:23:06:b4:85:b3:b7:97:64:fc:ac:75:f5:33:f2:93 +# SHA256 Fingerprint: 02:bd:f9:6e:2a:45:dd:9b:f1:8f:c7:e1:db:df:21:a0:37:9b:a3:c9:c2:61:03:44:cf:d8:d6:06:fe:c1:ed:81 +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi +Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1 +NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t +U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt +MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45FtnYSk +YZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslh +suitQDy6uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0al +DrJLpA6lfO741GIDuZNqihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3Oj +WiE260f6GBfZumbCk6SP/F2krfxQapWsvCQz0b2If4b19bJzKo98rwjyGpg/qYFl +P8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/cZip8UlF1y5mO6D1cv547 +KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTifBSeolz7p +UcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/ +kQO9lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JO +Hg9O5j9ZpSPcPYeoKFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkB +Ea801M/XrmLTBQe0MXXgDW1XT2mH+VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6U +CBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm45P3luG0wDQYJ +KoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6 +NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQ +nmhUQo8mUuJM3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+ +QgvfKNmwrZggvkN80V4aCRckjXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2v +trV0KnahP/t1MJ+UXjulYPPLXAziDslg+MkfFoom3ecnf+slpoq9uC02EJqxWE2a +aE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/WNyVntHKLr4W96ioD +j8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+o/E4 +Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0w +lREQKC6/oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHn +YfkUyq+Dj7+vsQpZXdxc1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVoc +icCMb3SgazNNtQEo/a2tiRc7ppqEvOuM6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw +-----END CERTIFICATE----- + +# Issuer: CN=CommScope Public Trust RSA Root-02 O=CommScope +# Subject: CN=CommScope Public Trust RSA Root-02 O=CommScope +# Label: "CommScope Public Trust RSA Root-02" +# Serial: 480062499834624527752716769107743131258796508494 +# MD5 Fingerprint: e1:29:f9:62:7b:76:e2:96:6d:f3:d4:d7:0f:ae:1f:aa +# SHA1 Fingerprint: ea:b0:e2:52:1b:89:93:4c:11:68:f2:d8:9a:ac:22:4c:a3:8a:57:ae +# SHA256 Fingerprint: ff:e9:43:d7:93:42:4b:4f:7c:44:0c:1c:3d:64:8d:53:63:f3:4b:82:dc:87:aa:7a:9f:11:8f:c5:de:e1:01:f1 +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwi +Q29tbVNjb3BlIFB1YmxpYyBUcnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2 +NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlDb21t +U2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3QgUlNBIFJvb3Qt +MDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3VrCLE +NQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0 +kyI9p+Kx7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1C +rWDaSWqVcN3SAOLMV2MCe5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxz +hkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2WWy09X6GDRl224yW4fKcZgBzqZUPckXk2 +LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rpM9kzXzehxfCrPfp4sOcs +n/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIfhs1w/tku +FT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5 +kQMreyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3 +wNemKfrb3vOTlycEVS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6v +wQcQeKwRoi9C8DfF8rhW3Q5iLc4tVn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs +5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7GxcJXvYXowDQYJ +KoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB +KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3 ++VGXu6TwYofF1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbyme +APnCKfWxkxlSaRosTKCL4BWaMS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3Nyq +pgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xdgSGn2rtO/+YHqP65DSdsu3BaVXoT +6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2OHG1QAk8mGEPej1WF +sQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+NmYWvt +PjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2d +lklyALKrdVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670 +v64fG9PiO/yzcnMcmyiQiRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17O +rg3bhzjlP1v9mxnhMUF6cKojawHhRUzNlM47ni3niAIi9G7oyOzWPPO5std3eqx7 +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS ECC Root 2020 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS ECC Root 2020" +# Serial: 72082518505882327255703894282316633856 +# MD5 Fingerprint: c1:ab:fe:6a:10:2c:03:8d:bc:1c:22:32:c0:85:a7:fd +# SHA1 Fingerprint: c0:f8:96:c5:a9:3b:01:06:21:07:da:18:42:48:bc:e9:9d:88:d5:ec +# SHA256 Fingerprint: 57:8a:f4:de:d0:85:3f:4e:59:98:db:4a:ea:f9:cb:ea:8d:94:5f:60:b6:20:a3:8d:1a:3c:13:b2:bc:7b:a8:e1 +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQsw +CQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBH +bWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIw +MB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIzNTk1OVowYzELMAkGA1UEBhMCREUx +JzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkGA1UE +AwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/O +tdKPD/M12kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDP +f8iAC8GXs7s1J8nCG6NCMEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6f +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA +MGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZMo7k+5Dck2TOrbRBR2Di +z6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdUga/sf+Rn +27iQ7t0l +-----END CERTIFICATE----- + +# Issuer: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Subject: CN=Telekom Security TLS RSA Root 2023 O=Deutsche Telekom Security GmbH +# Label: "Telekom Security TLS RSA Root 2023" +# Serial: 44676229530606711399881795178081572759 +# MD5 Fingerprint: bf:5b:eb:54:40:cd:48:71:c4:20:8d:7d:de:0a:42:f2 +# SHA1 Fingerprint: 54:d3:ac:b3:bd:57:56:f6:85:9d:ce:e5:c3:21:e2:d4:ad:83:d0:93 +# SHA256 Fingerprint: ef:c6:5c:ad:bb:59:ad:b6:ef:e8:4d:a2:23:11:b3:56:24:b7:1b:3b:1e:a0:da:8b:66:55:17:4e:c8:97:86:46 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBj +MQswCQYDVQQGEwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0 +eSBHbWJIMSswKQYDVQQDDCJUZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAy +MDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMyNzIzNTk1OVowYzELMAkGA1UEBhMC +REUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkgR21iSDErMCkG +A1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9 +cUD/h3VCKSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHV +cp6R+SPWcHu79ZvB7JPPGeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMA +U6DksquDOFczJZSfvkgdmOGjup5czQRxUX11eKvzWarE4GC+j4NSuHUaQTXtvPM6 +Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWol8hHD/BeEIvnHRz+sTug +BTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9FIS3R/qy +8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73J +co4vzLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg +8qKrBC7m8kwOFjQgrIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8 +rFEz0ciD0cmfHdRHNCk+y7AO+oMLKFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12 +mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7SWWO/gLCMk3PLNaaZlSJhZQNg ++y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtqeX +gj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 +p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQ +pGv7qHBFfLp+sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm +9S3ul0A8Yute1hTWjOKWi0FpkzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErw +M807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy/SKE8YXJN3nptT+/XOR0so8RYgDd +GGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4mZqTuXNnQkYRIer+ +CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtzaL1t +xKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+ +w6jv/naaoqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aK +L4x35bcF7DvB7L6Gs4a8wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+lj +X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q +ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm +dTdmQRCsu/WU48IxK63nI1bMNSWSs1A= +-----END CERTIFICATE----- diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/core.py b/.pythonlibs/lib/python3.10/site-packages/certifi/core.py new file mode 100644 index 0000000..91f538b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/certifi/core.py @@ -0,0 +1,114 @@ +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem or its contents. +""" +import sys +import atexit + +def exit_cacert_ctx() -> None: + _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] + + +if sys.version_info >= (3, 11): + + from importlib.resources import as_file, files + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") + +elif sys.version_info >= (3, 7): + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the + # file in cases where we're inside of a zipimport situation until + # someone actually calls where(), but we don't want to re-extract + # the file on every call of where(), so we'll do it once then store + # it in a global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you + # to manage the cleanup of this file, so it doesn't actually + # return a path, it returns a context manager that will give + # you the path when you enter it and will do any cleanup when + # you leave it. In the common case of not needing a temporary + # file, it will just return the file system location and the + # __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") + +else: + import os + import types + from typing import Union + + Package = Union[types.ModuleType, str] + Resource = Union[str, "os.PathLike"] + + # This fallback will work for Python versions prior to 3.7 that lack the + # importlib.resources module but relies on the existing `where` function + # so won't address issues with environments like PyOxidizer that don't set + # __file__ on modules. + def read_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict' + ) -> str: + with open(where(), encoding=encoding) as data: + return data.read() + + # If we don't have importlib.resources, then we will just do the old logic + # of assuming we're on the filesystem and munge the path directly. + def where() -> str: + f = os.path.dirname(__file__) + + return os.path.join(f, "cacert.pem") + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/.pythonlibs/lib/python3.10/site-packages/certifi/py.typed b/.pythonlibs/lib/python3.10/site-packages/certifi/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE new file mode 100644 index 0000000..ad82355 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 TAHRI Ahmed R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/METADATA new file mode 100644 index 0000000..822550e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/METADATA @@ -0,0 +1,683 @@ +Metadata-Version: 2.1 +Name: charset-normalizer +Version: 3.3.2 +Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. +Home-page: https://github.com/Ousret/charset_normalizer +Author: Ahmed TAHRI +Author-email: ahmed.tahri@cloudnursery.dev +License: MIT +Project-URL: Bug Reports, https://github.com/Ousret/charset_normalizer/issues +Project-URL: Documentation, https://charset-normalizer.readthedocs.io/en/latest +Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Linguistic +Classifier: Topic :: Utilities +Classifier: Typing :: Typed +Requires-Python: >=3.7.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: unicode_backport + +

    Charset Detection, for Everyone 👋

    + +

    + The Real First Universal Charset Detector
    + + + + + Download Count Total + + + + +

    +

    + Featured Packages
    + + Static Badge + + + Static Badge + +

    +

    + In other language (unofficial port - by the community)
    + + Static Badge + +

    + +> A library that helps you read text from an unknown charset encoding.
    Motivated by `chardet`, +> I'm trying to resolve the issue by taking a new approach. +> All IANA character set names for which the Python core library provides codecs are supported. + +

    + >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< +

    + +This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. + +| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | +|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| +| `Fast` | ❌ | ✅ | ✅ | +| `Universal**` | ❌ | ✅ | ❌ | +| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | +| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | +| `License` | LGPL-2.1
    _restrictive_ | MIT | MPL-1.1
    _restrictive_ | +| `Native Python` | ✅ | ✅ | ❌ | +| `Detect spoken language` | ❌ | ✅ | N/A | +| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | +| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | +| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | + +

    +Reading Normalized TextCat Reading Text +

    + +*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
    +Did you got there because of the logs? See [https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html](https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html) + +## ⚡ Performance + +This package offer better performance than its counterpart Chardet. Here are some numbers. + +| Package | Accuracy | Mean per file (ms) | File per sec (est) | +|-----------------------------------------------|:--------:|:------------------:|:------------------:| +| [chardet](https://github.com/chardet/chardet) | 86 % | 200 ms | 5 file/sec | +| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | + +| Package | 99th percentile | 95th percentile | 50th percentile | +|-----------------------------------------------|:---------------:|:---------------:|:---------------:| +| [chardet](https://github.com/chardet/chardet) | 1200 ms | 287 ms | 23 ms | +| charset-normalizer | 100 ms | 50 ms | 5 ms | + +Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. + +> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. +> And yes, these results might change at any time. The dataset can be updated to include more files. +> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. +> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability +> (eg. Supported Encoding) Challenge-them if you want. + +## ✨ Installation + +Using pip: + +```sh +pip install charset-normalizer -U +``` + +## 🚀 Basic Usage + +### CLI +This package comes with a CLI. + +``` +usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] + file [file ...] + +The Real First Universal Charset Detector. Discover originating encoding used +on text file. Normalize text to unicode. + +positional arguments: + files File(s) to be analysed + +optional arguments: + -h, --help show this help message and exit + -v, --verbose Display complementary information about file if any. + Stdout will contain logs about the detection process. + -a, --with-alternative + Output complementary possibilities if any. Top-level + JSON WILL be a list. + -n, --normalize Permit to normalize input file. If not set, program + does not write anything. + -m, --minimal Only output the charset detected to STDOUT. Disabling + JSON output. + -r, --replace Replace file when trying to normalize it instead of + creating a new one. + -f, --force Replace file without asking if you are sure, use this + flag with caution. + -t THRESHOLD, --threshold THRESHOLD + Define a custom maximum amount of chaos allowed in + decoded content. 0. <= chaos <= 1. + --version Show version information and exit. +``` + +```bash +normalizer ./data/sample.1.fr.srt +``` + +or + +```bash +python -m charset_normalizer ./data/sample.1.fr.srt +``` + +🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. + +```json +{ + "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", + "encoding": "cp1252", + "encoding_aliases": [ + "1252", + "windows_1252" + ], + "alternative_encodings": [ + "cp1254", + "cp1256", + "cp1258", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + "mbcs" + ], + "language": "French", + "alphabets": [ + "Basic Latin", + "Latin-1 Supplement" + ], + "has_sig_or_bom": false, + "chaos": 0.149, + "coherence": 97.152, + "unicode_path": null, + "is_preferred": true +} +``` + +### Python +*Just print out normalized text* +```python +from charset_normalizer import from_path + +results = from_path('./my_subtitle.srt') + +print(str(results.best())) +``` + +*Upgrade your code without effort* +```python +from charset_normalizer import detect +``` + +The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. + +See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) + +## 😇 Why + +When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a +reliable alternative using a completely different method. Also! I never back down on a good challenge! + +I **don't care** about the **originating charset** encoding, because **two different tables** can +produce **two identical rendered string.** +What I want is to get readable text, the best I can. + +In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 + +Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. + +## 🍰 How + + - Discard all charset encoding table that could not fit the binary content. + - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. + - Extract matches with the lowest mess detected. + - Additionally, we measure coherence / probe for a language. + +**Wait a minute**, what is noise/mess and coherence according to **YOU ?** + +*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then +**I established** some ground rules about **what is obvious** when **it seems like** a mess. + I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to + improve or rewrite it. + +*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought +that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. + +## ⚡ Known limitations + + - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) + - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. + +## ⚠️ About Python EOLs + +**If you are running:** + +- Python >=2.7,<3.5: Unsupported +- Python 3.5: charset-normalizer < 2.1 +- Python 3.6: charset-normalizer < 3.1 +- Python 3.7: charset-normalizer < 4.0 + +Upgrade your Python interpreter as soon as possible. + +## 👤 Contributing + +Contributions, issues and feature requests are very much welcome.
    +Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. + +## 📝 License + +Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
    +This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. + +Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) + +## 💼 For Enterprise + +Professional support for charset-normalizer is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme + +# Changelog +All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) + +### Fixed +- Unintentional memory usage regression when using large payload that match several encoding (#376) +- Regression on some detection case showcased in the documentation (#371) + +### Added +- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) + +## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) + +### Changed +- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 +- Improved the general detection reliability based on reports from the community + +## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) + +### Added +- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` +- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) + +### Removed +- (internal) Redundant utils.is_ascii function and unused function is_private_use_only +- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant + +### Changed +- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection +- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 + +### Fixed +- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) + +## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) + +### Changed +- Typehint for function `from_path` no longer enforce `PathLike` as its first argument +- Minor improvement over the global detection reliability + +### Added +- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries +- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) +- Explicit support for Python 3.12 + +### Fixed +- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) + +## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) + +### Added +- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) + +### Removed +- Support for Python 3.6 (PR #260) + +### Changed +- Optional speedup provided by mypy/c 1.0.1 + +## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) + +### Fixed +- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) + +### Changed +- Speedup provided by mypy/c 0.990 on Python >= 3.7 + +## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it +- Sphinx warnings when generating the documentation + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' + +## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) + +### Added +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Removed +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) + +### Fixed +- Sphinx warnings when generating the documentation + +## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) + +### Changed +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Removed +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) + +### Deprecated +- Function `normalize` scheduled for removal in 3.0 + +### Changed +- Removed useless call to decode in fn is_unprintable (#206) + +### Fixed +- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) + +## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) + +### Added +- Output the Unicode table version when running the CLI with `--version` (PR #194) + +### Changed +- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) +- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) + +### Fixed +- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) +- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) + +### Removed +- Support for Python 3.5 (PR #192) + +### Deprecated +- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) + +## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) + +### Fixed +- ASCII miss-detection on rare cases (PR #170) + +## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) + +### Added +- Explicit support for Python 3.11 (PR #164) + +### Changed +- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) + +## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) + +### Fixed +- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) + +### Changed +- Skipping the language-detection (CD) on ASCII (PR #155) + +## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) + +### Changed +- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) + +### Fixed +- Wrong logging level applied when setting kwarg `explain` to True (PR #146) + +## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) +### Changed +- Improvement over Vietnamese detection (PR #126) +- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) +- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) +- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) +- Code style as refactored by Sourcery-AI (PR #131) +- Minor adjustment on the MD around european words (PR #133) +- Remove and replace SRTs from assets / tests (PR #139) +- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) + +### Fixed +- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) +- Avoid using too insignificant chunk (PR #137) + +### Added +- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) + +## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) +### Added +- Add support for Kazakh (Cyrillic) language detection (PR #109) + +### Changed +- Further, improve inferring the language from a given single-byte code page (PR #112) +- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) +- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) +- Various detection improvement (MD+CD) (PR #117) + +### Removed +- Remove redundant logging entry about detected language(s) (PR #115) + +### Fixed +- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) + +## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) +### Fixed +- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) +- Fix CLI crash when using --minimal output in certain cases (PR #103) + +### Changed +- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) + +## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) +### Changed +- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) +- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) +- The Unicode detection is slightly improved (PR #93) +- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) + +### Removed +- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) + +### Fixed +- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) +- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) +- The MANIFEST.in was not exhaustive (PR #78) + +## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) +### Fixed +- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) +- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) +- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) +- Submatch factoring could be wrong in rare edge cases (PR #72) +- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) +- Fix line endings from CRLF to LF for certain project files (PR #67) + +### Changed +- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) +- Allow fallback on specified encoding if any (PR #71) + +## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) +### Changed +- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) +- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) + +## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) +### Fixed +- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) + +### Changed +- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) + +## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) +### Fixed +- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) +- Using explain=False permanently disable the verbose output in the current runtime (PR #47) +- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) +- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) + +### Changed +- Public function normalize default args values were not aligned with from_bytes (PR #53) + +### Added +- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) + +## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) +### Changed +- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. +- Accent has been made on UTF-8 detection, should perform rather instantaneous. +- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. +- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) +- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ +- utf_7 detection has been reinstated. + +### Removed +- This package no longer require anything when used with Python 3.5 (Dropped cached_property) +- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. +- The exception hook on UnicodeDecodeError has been removed. + +### Deprecated +- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 + +### Fixed +- The CLI output used the relative path of the file(s). Should be absolute. + +## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) +### Fixed +- Logger configuration/usage no longer conflict with others (PR #44) + +## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) +### Removed +- Using standard logging instead of using the package loguru. +- Dropping nose test framework in favor of the maintained pytest. +- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. +- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. +- Stop support for UTF-7 that does not contain a SIG. +- Dropping PrettyTable, replaced with pure JSON output in CLI. + +### Fixed +- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. +- Not searching properly for the BOM when trying utf32/16 parent codec. + +### Changed +- Improving the package final size by compressing frequencies.json. +- Huge improvement over the larges payload. + +### Added +- CLI now produces JSON consumable output. +- Return ASCII if given sequences fit. Given reasonable confidence. + +## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) + +### Fixed +- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) + +## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) + +### Fixed +- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) + +## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) + +### Fixed +- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) + +## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) + +### Changed +- Amend the previous release to allow prettytable 2.0 (PR #35) + +## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) + +### Fixed +- Fix error while using the package with a python pre-release interpreter (PR #33) + +### Changed +- Dependencies refactoring, constraints revised. + +### Added +- Add python 3.9 and 3.10 to the supported interpreters + +MIT License + +Copyright (c) 2019 TAHRI Ahmed R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/RECORD new file mode 100644 index 0000000..c34e732 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/RECORD @@ -0,0 +1,35 @@ +../../../bin/normalizer,sha256=m8YQDc9HvCL-oaHuSvL4NVrJCZjAKltfNUGMSPXeegs,237 +charset_normalizer-3.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +charset_normalizer-3.3.2.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070 +charset_normalizer-3.3.2.dist-info/METADATA,sha256=cfLhl5A6SI-F0oclm8w8ux9wshL1nipdeCdVnYb4AaA,33550 +charset_normalizer-3.3.2.dist-info/RECORD,, +charset_normalizer-3.3.2.dist-info/WHEEL,sha256=cD39NF6a3hkhaWoPQJng7gnGZRIfQsUCtwcedITCPtg,152 +charset_normalizer-3.3.2.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 +charset_normalizer-3.3.2.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 +charset_normalizer/__init__.py,sha256=UzI3xC8PhmcLRMzSgPb6minTmRq0kWznnCBJ8ZCc2XI,1577 +charset_normalizer/__main__.py,sha256=JxY8bleaENOFlLRb9HfoeZCzAMnn2A1oGR5Xm2eyqg0,73 +charset_normalizer/__pycache__/__init__.cpython-310.pyc,, +charset_normalizer/__pycache__/__main__.cpython-310.pyc,, +charset_normalizer/__pycache__/api.cpython-310.pyc,, +charset_normalizer/__pycache__/cd.cpython-310.pyc,, +charset_normalizer/__pycache__/constant.cpython-310.pyc,, +charset_normalizer/__pycache__/legacy.cpython-310.pyc,, +charset_normalizer/__pycache__/md.cpython-310.pyc,, +charset_normalizer/__pycache__/models.cpython-310.pyc,, +charset_normalizer/__pycache__/utils.cpython-310.pyc,, +charset_normalizer/__pycache__/version.cpython-310.pyc,, +charset_normalizer/api.py,sha256=WOlWjy6wT8SeMYFpaGbXZFN1TMXa-s8vZYfkL4G29iQ,21097 +charset_normalizer/cd.py,sha256=xwZliZcTQFA3jU0c00PRiu9MNxXTFxQkFLWmMW24ZzI,12560 +charset_normalizer/cli/__init__.py,sha256=D5ERp8P62llm2FuoMzydZ7d9rs8cvvLXqE-1_6oViPc,100 +charset_normalizer/cli/__main__.py,sha256=2F-xURZJzo063Ye-2RLJ2wcmURpbKeAzKwpiws65dAs,9744 +charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc,, +charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc,, +charset_normalizer/constant.py,sha256=p0IsOVcEbPWYPOdWhnhRbjK1YVBy6fs05C5vKC-zoxU,40481 +charset_normalizer/legacy.py,sha256=T-QuVMsMeDiQEk8WSszMrzVJg_14AMeSkmHdRYhdl1k,2071 +charset_normalizer/md.cpython-310-x86_64-linux-gnu.so,sha256=Y7QSLD5QLoSFAWys0-tL7R6QB7oi5864zM6zr7RWek4,16064 +charset_normalizer/md.py,sha256=NkSuVLK13_a8c7BxZ4cGIQ5vOtGIWOdh22WZEvjp-7U,19624 +charset_normalizer/md__mypyc.cpython-310-x86_64-linux-gnu.so,sha256=y2N-LgwRp7TCdgRqsmIM8UvKeavC0t8kx_hdRvaSfcY,268472 +charset_normalizer/models.py,sha256=I5i0s4aKCCgLPY2tUY3pwkgFA-BUbbNxQ7hVkVTt62s,11624 +charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +charset_normalizer/utils.py,sha256=teiosMqzKjXyAHXnGdjSBOgnBZwx-SkBbCLrx0UXy8M,11894 +charset_normalizer/version.py,sha256=iHKUfHD3kDRSyrh_BN2ojh43TA5-UZQjvbVIEFfpHDs,79 diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL new file mode 100644 index 0000000..7b52c3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.2) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt new file mode 100644 index 0000000..65619e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +normalizer = charset_normalizer.cli:cli_detect diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt new file mode 100644 index 0000000..66958f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt @@ -0,0 +1 @@ +charset_normalizer diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__init__.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__init__.py new file mode 100644 index 0000000..55991fc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__init__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" +Charset-Normalizer +~~~~~~~~~~~~~~ +The Real First Universal Charset Detector. +A library that helps you read text from an unknown charset encoding. +Motivated by chardet, This package is trying to resolve the issue by taking a new approach. +All IANA character set names for which the Python core library provides codecs are supported. + +Basic usage: + >>> from charset_normalizer import from_bytes + >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) + >>> best_guess = results.best() + >>> str(best_guess) + 'Bсеки човек има право на образование. Oбразованието!' + +Others methods and usages are available - see the full documentation +at . +:copyright: (c) 2021 by Ahmed TAHRI +:license: MIT, see LICENSE for more details. +""" +import logging + +from .api import from_bytes, from_fp, from_path, is_binary +from .legacy import detect +from .models import CharsetMatch, CharsetMatches +from .utils import set_logging_handler +from .version import VERSION, __version__ + +__all__ = ( + "from_fp", + "from_path", + "from_bytes", + "is_binary", + "detect", + "CharsetMatch", + "CharsetMatches", + "__version__", + "VERSION", + "set_logging_handler", +) + +# Attach a NullHandler to the top level logger by default +# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library + +logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__main__.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__main__.py new file mode 100644 index 0000000..beae2ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__main__.py @@ -0,0 +1,4 @@ +from .cli import cli_detect + +if __name__ == "__main__": + cli_detect() diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..cab7dff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000..2034ac4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/__main__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..e57d7e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc new file mode 100644 index 0000000..a96d23b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/cd.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc new file mode 100644 index 0000000..9f06715 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/constant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc new file mode 100644 index 0000000..08e2ecd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/legacy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc new file mode 100644 index 0000000..da679dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/md.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000..ba74783 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..0ea172e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..74afd5c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/api.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/api.py new file mode 100644 index 0000000..0ba08e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/api.py @@ -0,0 +1,626 @@ +import logging +from os import PathLike +from typing import BinaryIO, List, Optional, Set, Union + +from .cd import ( + coherence_ratio, + encoding_languages, + mb_encoding_languages, + merge_coherence_ratios, +) +from .constant import IANA_SUPPORTED, TOO_BIG_SEQUENCE, TOO_SMALL_SEQUENCE, TRACE +from .md import mess_ratio +from .models import CharsetMatch, CharsetMatches +from .utils import ( + any_specified_encoding, + cut_sequence_chunks, + iana_name, + identify_sig_or_bom, + is_cp_similar, + is_multi_byte_encoding, + should_strip_sig_or_bom, +) + +# Will most likely be controversial +# logging.addLevelName(TRACE, "TRACE") +logger = logging.getLogger("charset_normalizer") +explain_handler = logging.StreamHandler() +explain_handler.setFormatter( + logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") +) + + +def from_bytes( + sequences: Union[bytes, bytearray], + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.2, + cp_isolation: Optional[List[str]] = None, + cp_exclusion: Optional[List[str]] = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Given a raw bytes sequence, return the best possibles charset usable to render str objects. + If there is no results, it is a strong indicator that the source is binary/not text. + By default, the process will extract 5 blocks of 512o each to assess the mess and coherence of a given sequence. + And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. + + The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page + but never take it for granted. Can improve the performance. + + You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that + purpose. + + This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. + By default the library does not setup any handler other than the NullHandler, if you choose to set the 'explain' + toggle to True it will alter the logger configuration to add a StreamHandler that is suitable for debugging. + Custom logging format and handler can be set manually. + """ + + if not isinstance(sequences, (bytearray, bytes)): + raise TypeError( + "Expected object of type bytes or bytearray, got: {0}".format( + type(sequences) + ) + ) + + if explain: + previous_logger_level: int = logger.level + logger.addHandler(explain_handler) + logger.setLevel(TRACE) + + length: int = len(sequences) + + if length == 0: + logger.debug("Encoding detection on empty bytes, assuming utf_8 intention.") + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level or logging.WARNING) + return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) + + if cp_isolation is not None: + logger.log( + TRACE, + "cp_isolation is set. use this flag for debugging purpose. " + "limited list of encoding allowed : %s.", + ", ".join(cp_isolation), + ) + cp_isolation = [iana_name(cp, False) for cp in cp_isolation] + else: + cp_isolation = [] + + if cp_exclusion is not None: + logger.log( + TRACE, + "cp_exclusion is set. use this flag for debugging purpose. " + "limited list of encoding excluded : %s.", + ", ".join(cp_exclusion), + ) + cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] + else: + cp_exclusion = [] + + if length <= (chunk_size * steps): + logger.log( + TRACE, + "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", + steps, + chunk_size, + length, + ) + steps = 1 + chunk_size = length + + if steps > 1 and length / steps < chunk_size: + chunk_size = int(length / steps) + + is_too_small_sequence: bool = len(sequences) < TOO_SMALL_SEQUENCE + is_too_large_sequence: bool = len(sequences) >= TOO_BIG_SEQUENCE + + if is_too_small_sequence: + logger.log( + TRACE, + "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( + length + ), + ) + elif is_too_large_sequence: + logger.log( + TRACE, + "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( + length + ), + ) + + prioritized_encodings: List[str] = [] + + specified_encoding: Optional[str] = ( + any_specified_encoding(sequences) if preemptive_behaviour else None + ) + + if specified_encoding is not None: + prioritized_encodings.append(specified_encoding) + logger.log( + TRACE, + "Detected declarative mark in sequence. Priority +1 given for %s.", + specified_encoding, + ) + + tested: Set[str] = set() + tested_but_hard_failure: List[str] = [] + tested_but_soft_failure: List[str] = [] + + fallback_ascii: Optional[CharsetMatch] = None + fallback_u8: Optional[CharsetMatch] = None + fallback_specified: Optional[CharsetMatch] = None + + results: CharsetMatches = CharsetMatches() + + sig_encoding, sig_payload = identify_sig_or_bom(sequences) + + if sig_encoding is not None: + prioritized_encodings.append(sig_encoding) + logger.log( + TRACE, + "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", + len(sig_payload), + sig_encoding, + ) + + prioritized_encodings.append("ascii") + + if "utf_8" not in prioritized_encodings: + prioritized_encodings.append("utf_8") + + for encoding_iana in prioritized_encodings + IANA_SUPPORTED: + if cp_isolation and encoding_iana not in cp_isolation: + continue + + if cp_exclusion and encoding_iana in cp_exclusion: + continue + + if encoding_iana in tested: + continue + + tested.add(encoding_iana) + + decoded_payload: Optional[str] = None + bom_or_sig_available: bool = sig_encoding == encoding_iana + strip_sig_or_bom: bool = bom_or_sig_available and should_strip_sig_or_bom( + encoding_iana + ) + + if encoding_iana in {"utf_16", "utf_32"} and not bom_or_sig_available: + logger.log( + TRACE, + "Encoding %s won't be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", + encoding_iana, + ) + continue + if encoding_iana in {"utf_7"} and not bom_or_sig_available: + logger.log( + TRACE, + "Encoding %s won't be tested as-is because detection is unreliable without BOM/SIG.", + encoding_iana, + ) + continue + + try: + is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) + except (ModuleNotFoundError, ImportError): + logger.log( + TRACE, + "Encoding %s does not provide an IncrementalDecoder", + encoding_iana, + ) + continue + + try: + if is_too_large_sequence and is_multi_byte_decoder is False: + str( + sequences[: int(50e4)] + if strip_sig_or_bom is False + else sequences[len(sig_payload) : int(50e4)], + encoding=encoding_iana, + ) + else: + decoded_payload = str( + sequences + if strip_sig_or_bom is False + else sequences[len(sig_payload) :], + encoding=encoding_iana, + ) + except (UnicodeDecodeError, LookupError) as e: + if not isinstance(e, LookupError): + logger.log( + TRACE, + "Code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + tested_but_hard_failure.append(encoding_iana) + continue + + similar_soft_failure_test: bool = False + + for encoding_soft_failed in tested_but_soft_failure: + if is_cp_similar(encoding_iana, encoding_soft_failed): + similar_soft_failure_test = True + break + + if similar_soft_failure_test: + logger.log( + TRACE, + "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", + encoding_iana, + encoding_soft_failed, + ) + continue + + r_ = range( + 0 if not bom_or_sig_available else len(sig_payload), + length, + int(length / steps), + ) + + multi_byte_bonus: bool = ( + is_multi_byte_decoder + and decoded_payload is not None + and len(decoded_payload) < length + ) + + if multi_byte_bonus: + logger.log( + TRACE, + "Code page %s is a multi byte encoding table and it appear that at least one character " + "was encoded using n-bytes.", + encoding_iana, + ) + + max_chunk_gave_up: int = int(len(r_) / 4) + + max_chunk_gave_up = max(max_chunk_gave_up, 2) + early_stop_count: int = 0 + lazy_str_hard_failure = False + + md_chunks: List[str] = [] + md_ratios = [] + + try: + for chunk in cut_sequence_chunks( + sequences, + encoding_iana, + r_, + chunk_size, + bom_or_sig_available, + strip_sig_or_bom, + sig_payload, + is_multi_byte_decoder, + decoded_payload, + ): + md_chunks.append(chunk) + + md_ratios.append( + mess_ratio( + chunk, + threshold, + explain is True and 1 <= len(cp_isolation) <= 2, + ) + ) + + if md_ratios[-1] >= threshold: + early_stop_count += 1 + + if (early_stop_count >= max_chunk_gave_up) or ( + bom_or_sig_available and strip_sig_or_bom is False + ): + break + except ( + UnicodeDecodeError + ) as e: # Lazy str loading may have missed something there + logger.log( + TRACE, + "LazyStr Loading: After MD chunk decode, code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + early_stop_count = max_chunk_gave_up + lazy_str_hard_failure = True + + # We might want to check the sequence again with the whole content + # Only if initial MD tests passes + if ( + not lazy_str_hard_failure + and is_too_large_sequence + and not is_multi_byte_decoder + ): + try: + sequences[int(50e3) :].decode(encoding_iana, errors="strict") + except UnicodeDecodeError as e: + logger.log( + TRACE, + "LazyStr Loading: After final lookup, code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + tested_but_hard_failure.append(encoding_iana) + continue + + mean_mess_ratio: float = sum(md_ratios) / len(md_ratios) if md_ratios else 0.0 + if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: + tested_but_soft_failure.append(encoding_iana) + logger.log( + TRACE, + "%s was excluded because of initial chaos probing. Gave up %i time(s). " + "Computed mean chaos is %f %%.", + encoding_iana, + early_stop_count, + round(mean_mess_ratio * 100, ndigits=3), + ) + # Preparing those fallbacks in case we got nothing. + if ( + enable_fallback + and encoding_iana in ["ascii", "utf_8", specified_encoding] + and not lazy_str_hard_failure + ): + fallback_entry = CharsetMatch( + sequences, encoding_iana, threshold, False, [], decoded_payload + ) + if encoding_iana == specified_encoding: + fallback_specified = fallback_entry + elif encoding_iana == "ascii": + fallback_ascii = fallback_entry + else: + fallback_u8 = fallback_entry + continue + + logger.log( + TRACE, + "%s passed initial chaos probing. Mean measured chaos is %f %%", + encoding_iana, + round(mean_mess_ratio * 100, ndigits=3), + ) + + if not is_multi_byte_decoder: + target_languages: List[str] = encoding_languages(encoding_iana) + else: + target_languages = mb_encoding_languages(encoding_iana) + + if target_languages: + logger.log( + TRACE, + "{} should target any language(s) of {}".format( + encoding_iana, str(target_languages) + ), + ) + + cd_ratios = [] + + # We shall skip the CD when its about ASCII + # Most of the time its not relevant to run "language-detection" on it. + if encoding_iana != "ascii": + for chunk in md_chunks: + chunk_languages = coherence_ratio( + chunk, + language_threshold, + ",".join(target_languages) if target_languages else None, + ) + + cd_ratios.append(chunk_languages) + + cd_ratios_merged = merge_coherence_ratios(cd_ratios) + + if cd_ratios_merged: + logger.log( + TRACE, + "We detected language {} using {}".format( + cd_ratios_merged, encoding_iana + ), + ) + + results.append( + CharsetMatch( + sequences, + encoding_iana, + mean_mess_ratio, + bom_or_sig_available, + cd_ratios_merged, + decoded_payload, + ) + ) + + if ( + encoding_iana in [specified_encoding, "ascii", "utf_8"] + and mean_mess_ratio < 0.1 + ): + logger.debug( + "Encoding detection: %s is most likely the one.", encoding_iana + ) + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([results[encoding_iana]]) + + if encoding_iana == sig_encoding: + logger.debug( + "Encoding detection: %s is most likely the one as we detected a BOM or SIG within " + "the beginning of the sequence.", + encoding_iana, + ) + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([results[encoding_iana]]) + + if len(results) == 0: + if fallback_u8 or fallback_ascii or fallback_specified: + logger.log( + TRACE, + "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.", + ) + + if fallback_specified: + logger.debug( + "Encoding detection: %s will be used as a fallback match", + fallback_specified.encoding, + ) + results.append(fallback_specified) + elif ( + (fallback_u8 and fallback_ascii is None) + or ( + fallback_u8 + and fallback_ascii + and fallback_u8.fingerprint != fallback_ascii.fingerprint + ) + or (fallback_u8 is not None) + ): + logger.debug("Encoding detection: utf_8 will be used as a fallback match") + results.append(fallback_u8) + elif fallback_ascii: + logger.debug("Encoding detection: ascii will be used as a fallback match") + results.append(fallback_ascii) + + if results: + logger.debug( + "Encoding detection: Found %s as plausible (best-candidate) for content. With %i alternatives.", + results.best().encoding, # type: ignore + len(results) - 1, + ) + else: + logger.debug("Encoding detection: Unable to determine any suitable charset.") + + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + + return results + + +def from_fp( + fp: BinaryIO, + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: Optional[List[str]] = None, + cp_exclusion: Optional[List[str]] = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Same thing than the function from_bytes but using a file pointer that is already ready. + Will not close the file pointer. + """ + return from_bytes( + fp.read(), + steps, + chunk_size, + threshold, + cp_isolation, + cp_exclusion, + preemptive_behaviour, + explain, + language_threshold, + enable_fallback, + ) + + +def from_path( + path: Union[str, bytes, PathLike], # type: ignore[type-arg] + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: Optional[List[str]] = None, + cp_exclusion: Optional[List[str]] = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. + Can raise IOError. + """ + with open(path, "rb") as fp: + return from_fp( + fp, + steps, + chunk_size, + threshold, + cp_isolation, + cp_exclusion, + preemptive_behaviour, + explain, + language_threshold, + enable_fallback, + ) + + +def is_binary( + fp_or_path_or_payload: Union[PathLike, str, BinaryIO, bytes], # type: ignore[type-arg] + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: Optional[List[str]] = None, + cp_exclusion: Optional[List[str]] = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = False, +) -> bool: + """ + Detect if the given input (file, bytes, or path) points to a binary file. aka. not a string. + Based on the same main heuristic algorithms and default kwargs at the sole exception that fallbacks match + are disabled to be stricter around ASCII-compatible but unlikely to be a string. + """ + if isinstance(fp_or_path_or_payload, (str, PathLike)): + guesses = from_path( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + elif isinstance( + fp_or_path_or_payload, + ( + bytes, + bytearray, + ), + ): + guesses = from_bytes( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + else: + guesses = from_fp( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + + return not guesses diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cd.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cd.py new file mode 100644 index 0000000..4ea6760 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cd.py @@ -0,0 +1,395 @@ +import importlib +from codecs import IncrementalDecoder +from collections import Counter +from functools import lru_cache +from typing import Counter as TypeCounter, Dict, List, Optional, Tuple + +from .constant import ( + FREQUENCIES, + KO_NAMES, + LANGUAGE_SUPPORTED_COUNT, + TOO_SMALL_SEQUENCE, + ZH_NAMES, +) +from .md import is_suspiciously_successive_range +from .models import CoherenceMatches +from .utils import ( + is_accentuated, + is_latin, + is_multi_byte_encoding, + is_unicode_range_secondary, + unicode_range, +) + + +def encoding_unicode_range(iana_name: str) -> List[str]: + """ + Return associated unicode ranges in a single byte code page. + """ + if is_multi_byte_encoding(iana_name): + raise IOError("Function not supported on multi-byte code page") + + decoder = importlib.import_module( + "encodings.{}".format(iana_name) + ).IncrementalDecoder + + p: IncrementalDecoder = decoder(errors="ignore") + seen_ranges: Dict[str, int] = {} + character_count: int = 0 + + for i in range(0x40, 0xFF): + chunk: str = p.decode(bytes([i])) + + if chunk: + character_range: Optional[str] = unicode_range(chunk) + + if character_range is None: + continue + + if is_unicode_range_secondary(character_range) is False: + if character_range not in seen_ranges: + seen_ranges[character_range] = 0 + seen_ranges[character_range] += 1 + character_count += 1 + + return sorted( + [ + character_range + for character_range in seen_ranges + if seen_ranges[character_range] / character_count >= 0.15 + ] + ) + + +def unicode_range_languages(primary_range: str) -> List[str]: + """ + Return inferred languages used with a unicode range. + """ + languages: List[str] = [] + + for language, characters in FREQUENCIES.items(): + for character in characters: + if unicode_range(character) == primary_range: + languages.append(language) + break + + return languages + + +@lru_cache() +def encoding_languages(iana_name: str) -> List[str]: + """ + Single-byte encoding language association. Some code page are heavily linked to particular language(s). + This function does the correspondence. + """ + unicode_ranges: List[str] = encoding_unicode_range(iana_name) + primary_range: Optional[str] = None + + for specified_range in unicode_ranges: + if "Latin" not in specified_range: + primary_range = specified_range + break + + if primary_range is None: + return ["Latin Based"] + + return unicode_range_languages(primary_range) + + +@lru_cache() +def mb_encoding_languages(iana_name: str) -> List[str]: + """ + Multi-byte encoding language association. Some code page are heavily linked to particular language(s). + This function does the correspondence. + """ + if ( + iana_name.startswith("shift_") + or iana_name.startswith("iso2022_jp") + or iana_name.startswith("euc_j") + or iana_name == "cp932" + ): + return ["Japanese"] + if iana_name.startswith("gb") or iana_name in ZH_NAMES: + return ["Chinese"] + if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: + return ["Korean"] + + return [] + + +@lru_cache(maxsize=LANGUAGE_SUPPORTED_COUNT) +def get_target_features(language: str) -> Tuple[bool, bool]: + """ + Determine main aspects from a supported language if it contains accents and if is pure Latin. + """ + target_have_accents: bool = False + target_pure_latin: bool = True + + for character in FREQUENCIES[language]: + if not target_have_accents and is_accentuated(character): + target_have_accents = True + if target_pure_latin and is_latin(character) is False: + target_pure_latin = False + + return target_have_accents, target_pure_latin + + +def alphabet_languages( + characters: List[str], ignore_non_latin: bool = False +) -> List[str]: + """ + Return associated languages associated to given characters. + """ + languages: List[Tuple[str, float]] = [] + + source_have_accents = any(is_accentuated(character) for character in characters) + + for language, language_characters in FREQUENCIES.items(): + target_have_accents, target_pure_latin = get_target_features(language) + + if ignore_non_latin and target_pure_latin is False: + continue + + if target_have_accents is False and source_have_accents: + continue + + character_count: int = len(language_characters) + + character_match_count: int = len( + [c for c in language_characters if c in characters] + ) + + ratio: float = character_match_count / character_count + + if ratio >= 0.2: + languages.append((language, ratio)) + + languages = sorted(languages, key=lambda x: x[1], reverse=True) + + return [compatible_language[0] for compatible_language in languages] + + +def characters_popularity_compare( + language: str, ordered_characters: List[str] +) -> float: + """ + Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. + The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). + Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) + """ + if language not in FREQUENCIES: + raise ValueError("{} not available".format(language)) + + character_approved_count: int = 0 + FREQUENCIES_language_set = set(FREQUENCIES[language]) + + ordered_characters_count: int = len(ordered_characters) + target_language_characters_count: int = len(FREQUENCIES[language]) + + large_alphabet: bool = target_language_characters_count > 26 + + for character, character_rank in zip( + ordered_characters, range(0, ordered_characters_count) + ): + if character not in FREQUENCIES_language_set: + continue + + character_rank_in_language: int = FREQUENCIES[language].index(character) + expected_projection_ratio: float = ( + target_language_characters_count / ordered_characters_count + ) + character_rank_projection: int = int(character_rank * expected_projection_ratio) + + if ( + large_alphabet is False + and abs(character_rank_projection - character_rank_in_language) > 4 + ): + continue + + if ( + large_alphabet is True + and abs(character_rank_projection - character_rank_in_language) + < target_language_characters_count / 3 + ): + character_approved_count += 1 + continue + + characters_before_source: List[str] = FREQUENCIES[language][ + 0:character_rank_in_language + ] + characters_after_source: List[str] = FREQUENCIES[language][ + character_rank_in_language: + ] + characters_before: List[str] = ordered_characters[0:character_rank] + characters_after: List[str] = ordered_characters[character_rank:] + + before_match_count: int = len( + set(characters_before) & set(characters_before_source) + ) + + after_match_count: int = len( + set(characters_after) & set(characters_after_source) + ) + + if len(characters_before_source) == 0 and before_match_count <= 4: + character_approved_count += 1 + continue + + if len(characters_after_source) == 0 and after_match_count <= 4: + character_approved_count += 1 + continue + + if ( + before_match_count / len(characters_before_source) >= 0.4 + or after_match_count / len(characters_after_source) >= 0.4 + ): + character_approved_count += 1 + continue + + return character_approved_count / len(ordered_characters) + + +def alpha_unicode_split(decoded_sequence: str) -> List[str]: + """ + Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. + Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; + One containing the latin letters and the other hebrew. + """ + layers: Dict[str, str] = {} + + for character in decoded_sequence: + if character.isalpha() is False: + continue + + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + continue + + layer_target_range: Optional[str] = None + + for discovered_range in layers: + if ( + is_suspiciously_successive_range(discovered_range, character_range) + is False + ): + layer_target_range = discovered_range + break + + if layer_target_range is None: + layer_target_range = character_range + + if layer_target_range not in layers: + layers[layer_target_range] = character.lower() + continue + + layers[layer_target_range] += character.lower() + + return list(layers.values()) + + +def merge_coherence_ratios(results: List[CoherenceMatches]) -> CoherenceMatches: + """ + This function merge results previously given by the function coherence_ratio. + The return type is the same as coherence_ratio. + """ + per_language_ratios: Dict[str, List[float]] = {} + for result in results: + for sub_result in result: + language, ratio = sub_result + if language not in per_language_ratios: + per_language_ratios[language] = [ratio] + continue + per_language_ratios[language].append(ratio) + + merge = [ + ( + language, + round( + sum(per_language_ratios[language]) / len(per_language_ratios[language]), + 4, + ), + ) + for language in per_language_ratios + ] + + return sorted(merge, key=lambda x: x[1], reverse=True) + + +def filter_alt_coherence_matches(results: CoherenceMatches) -> CoherenceMatches: + """ + We shall NOT return "English—" in CoherenceMatches because it is an alternative + of "English". This function only keeps the best match and remove the em-dash in it. + """ + index_results: Dict[str, List[float]] = dict() + + for result in results: + language, ratio = result + no_em_name: str = language.replace("—", "") + + if no_em_name not in index_results: + index_results[no_em_name] = [] + + index_results[no_em_name].append(ratio) + + if any(len(index_results[e]) > 1 for e in index_results): + filtered_results: CoherenceMatches = [] + + for language in index_results: + filtered_results.append((language, max(index_results[language]))) + + return filtered_results + + return results + + +@lru_cache(maxsize=2048) +def coherence_ratio( + decoded_sequence: str, threshold: float = 0.1, lg_inclusion: Optional[str] = None +) -> CoherenceMatches: + """ + Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. + A layer = Character extraction by alphabets/ranges. + """ + + results: List[Tuple[str, float]] = [] + ignore_non_latin: bool = False + + sufficient_match_count: int = 0 + + lg_inclusion_list = lg_inclusion.split(",") if lg_inclusion is not None else [] + if "Latin Based" in lg_inclusion_list: + ignore_non_latin = True + lg_inclusion_list.remove("Latin Based") + + for layer in alpha_unicode_split(decoded_sequence): + sequence_frequencies: TypeCounter[str] = Counter(layer) + most_common = sequence_frequencies.most_common() + + character_count: int = sum(o for c, o in most_common) + + if character_count <= TOO_SMALL_SEQUENCE: + continue + + popular_character_ordered: List[str] = [c for c, o in most_common] + + for language in lg_inclusion_list or alphabet_languages( + popular_character_ordered, ignore_non_latin + ): + ratio: float = characters_popularity_compare( + language, popular_character_ordered + ) + + if ratio < threshold: + continue + elif ratio >= 0.8: + sufficient_match_count += 1 + + results.append((language, round(ratio, 4))) + + if sufficient_match_count >= 3: + break + + return sorted( + filter_alt_coherence_matches(results), key=lambda x: x[1], reverse=True + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__init__.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__init__.py new file mode 100644 index 0000000..d95fedf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__init__.py @@ -0,0 +1,6 @@ +from .__main__ import cli_detect, query_yes_no + +__all__ = ( + "cli_detect", + "query_yes_no", +) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__main__.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__main__.py new file mode 100644 index 0000000..f4bcbaa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__main__.py @@ -0,0 +1,296 @@ +import argparse +import sys +from json import dumps +from os.path import abspath, basename, dirname, join, realpath +from platform import python_version +from typing import List, Optional +from unicodedata import unidata_version + +import charset_normalizer.md as md_module +from charset_normalizer import from_fp +from charset_normalizer.models import CliDetectionResult +from charset_normalizer.version import __version__ + + +def query_yes_no(question: str, default: str = "yes") -> bool: + """Ask a yes/no question via input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is True for "yes" or False for "no". + + Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input + """ + valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} + if default is None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + sys.stdout.write(question + prompt) + choice = input().lower() + if default is not None and choice == "": + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n") + + +def cli_detect(argv: Optional[List[str]] = None) -> int: + """ + CLI assistant using ARGV and ArgumentParser + :param argv: + :return: 0 if everything is fine, anything else equal trouble + """ + parser = argparse.ArgumentParser( + description="The Real First Universal Charset Detector. " + "Discover originating encoding used on text file. " + "Normalize text to unicode." + ) + + parser.add_argument( + "files", type=argparse.FileType("rb"), nargs="+", help="File(s) to be analysed" + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=False, + dest="verbose", + help="Display complementary information about file if any. " + "Stdout will contain logs about the detection process.", + ) + parser.add_argument( + "-a", + "--with-alternative", + action="store_true", + default=False, + dest="alternatives", + help="Output complementary possibilities if any. Top-level JSON WILL be a list.", + ) + parser.add_argument( + "-n", + "--normalize", + action="store_true", + default=False, + dest="normalize", + help="Permit to normalize input file. If not set, program does not write anything.", + ) + parser.add_argument( + "-m", + "--minimal", + action="store_true", + default=False, + dest="minimal", + help="Only output the charset detected to STDOUT. Disabling JSON output.", + ) + parser.add_argument( + "-r", + "--replace", + action="store_true", + default=False, + dest="replace", + help="Replace file when trying to normalize it instead of creating a new one.", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + default=False, + dest="force", + help="Replace file without asking if you are sure, use this flag with caution.", + ) + parser.add_argument( + "-t", + "--threshold", + action="store", + default=0.2, + type=float, + dest="threshold", + help="Define a custom maximum amount of chaos allowed in decoded content. 0. <= chaos <= 1.", + ) + parser.add_argument( + "--version", + action="version", + version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( + __version__, + python_version(), + unidata_version, + "OFF" if md_module.__file__.lower().endswith(".py") else "ON", + ), + help="Show version information and exit.", + ) + + args = parser.parse_args(argv) + + if args.replace is True and args.normalize is False: + print("Use --replace in addition of --normalize only.", file=sys.stderr) + return 1 + + if args.force is True and args.replace is False: + print("Use --force in addition of --replace only.", file=sys.stderr) + return 1 + + if args.threshold < 0.0 or args.threshold > 1.0: + print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) + return 1 + + x_ = [] + + for my_file in args.files: + matches = from_fp(my_file, threshold=args.threshold, explain=args.verbose) + + best_guess = matches.best() + + if best_guess is None: + print( + 'Unable to identify originating encoding for "{}". {}'.format( + my_file.name, + "Maybe try increasing maximum amount of chaos." + if args.threshold < 1.0 + else "", + ), + file=sys.stderr, + ) + x_.append( + CliDetectionResult( + abspath(my_file.name), + None, + [], + [], + "Unknown", + [], + False, + 1.0, + 0.0, + None, + True, + ) + ) + else: + x_.append( + CliDetectionResult( + abspath(my_file.name), + best_guess.encoding, + best_guess.encoding_aliases, + [ + cp + for cp in best_guess.could_be_from_charset + if cp != best_guess.encoding + ], + best_guess.language, + best_guess.alphabets, + best_guess.bom, + best_guess.percent_chaos, + best_guess.percent_coherence, + None, + True, + ) + ) + + if len(matches) > 1 and args.alternatives: + for el in matches: + if el != best_guess: + x_.append( + CliDetectionResult( + abspath(my_file.name), + el.encoding, + el.encoding_aliases, + [ + cp + for cp in el.could_be_from_charset + if cp != el.encoding + ], + el.language, + el.alphabets, + el.bom, + el.percent_chaos, + el.percent_coherence, + None, + False, + ) + ) + + if args.normalize is True: + if best_guess.encoding.startswith("utf") is True: + print( + '"{}" file does not need to be normalized, as it already came from unicode.'.format( + my_file.name + ), + file=sys.stderr, + ) + if my_file.closed is False: + my_file.close() + continue + + dir_path = dirname(realpath(my_file.name)) + file_name = basename(realpath(my_file.name)) + + o_: List[str] = file_name.split(".") + + if args.replace is False: + o_.insert(-1, best_guess.encoding) + if my_file.closed is False: + my_file.close() + elif ( + args.force is False + and query_yes_no( + 'Are you sure to normalize "{}" by replacing it ?'.format( + my_file.name + ), + "no", + ) + is False + ): + if my_file.closed is False: + my_file.close() + continue + + try: + x_[0].unicode_path = join(dir_path, ".".join(o_)) + + with open(x_[0].unicode_path, "w", encoding="utf-8") as fp: + fp.write(str(best_guess)) + except IOError as e: + print(str(e), file=sys.stderr) + if my_file.closed is False: + my_file.close() + return 2 + + if my_file.closed is False: + my_file.close() + + if args.minimal is False: + print( + dumps( + [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, + ensure_ascii=True, + indent=4, + ) + ) + else: + for my_file in args.files: + print( + ", ".join( + [ + el.encoding or "undefined" + for el in x_ + if el.path == abspath(my_file.name) + ] + ) + ) + + return 0 + + +if __name__ == "__main__": + cli_detect() diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..17ebf41 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000..5ad8b2f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/constant.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/constant.py new file mode 100644 index 0000000..8634904 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/constant.py @@ -0,0 +1,1995 @@ +# -*- coding: utf-8 -*- +from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE +from encodings.aliases import aliases +from re import IGNORECASE, compile as re_compile +from typing import Dict, List, Set, Union + +# Contain for each eligible encoding a list of/item bytes SIG/BOM +ENCODING_MARKS: Dict[str, Union[bytes, List[bytes]]] = { + "utf_8": BOM_UTF8, + "utf_7": [ + b"\x2b\x2f\x76\x38", + b"\x2b\x2f\x76\x39", + b"\x2b\x2f\x76\x2b", + b"\x2b\x2f\x76\x2f", + b"\x2b\x2f\x76\x38\x2d", + ], + "gb18030": b"\x84\x31\x95\x33", + "utf_32": [BOM_UTF32_BE, BOM_UTF32_LE], + "utf_16": [BOM_UTF16_BE, BOM_UTF16_LE], +} + +TOO_SMALL_SEQUENCE: int = 32 +TOO_BIG_SEQUENCE: int = int(10e6) + +UTF8_MAXIMAL_ALLOCATION: int = 1_112_064 + +# Up-to-date Unicode ucd/15.0.0 +UNICODE_RANGES_COMBINED: Dict[str, range] = { + "Control character": range(32), + "Basic Latin": range(32, 128), + "Latin-1 Supplement": range(128, 256), + "Latin Extended-A": range(256, 384), + "Latin Extended-B": range(384, 592), + "IPA Extensions": range(592, 688), + "Spacing Modifier Letters": range(688, 768), + "Combining Diacritical Marks": range(768, 880), + "Greek and Coptic": range(880, 1024), + "Cyrillic": range(1024, 1280), + "Cyrillic Supplement": range(1280, 1328), + "Armenian": range(1328, 1424), + "Hebrew": range(1424, 1536), + "Arabic": range(1536, 1792), + "Syriac": range(1792, 1872), + "Arabic Supplement": range(1872, 1920), + "Thaana": range(1920, 1984), + "NKo": range(1984, 2048), + "Samaritan": range(2048, 2112), + "Mandaic": range(2112, 2144), + "Syriac Supplement": range(2144, 2160), + "Arabic Extended-B": range(2160, 2208), + "Arabic Extended-A": range(2208, 2304), + "Devanagari": range(2304, 2432), + "Bengali": range(2432, 2560), + "Gurmukhi": range(2560, 2688), + "Gujarati": range(2688, 2816), + "Oriya": range(2816, 2944), + "Tamil": range(2944, 3072), + "Telugu": range(3072, 3200), + "Kannada": range(3200, 3328), + "Malayalam": range(3328, 3456), + "Sinhala": range(3456, 3584), + "Thai": range(3584, 3712), + "Lao": range(3712, 3840), + "Tibetan": range(3840, 4096), + "Myanmar": range(4096, 4256), + "Georgian": range(4256, 4352), + "Hangul Jamo": range(4352, 4608), + "Ethiopic": range(4608, 4992), + "Ethiopic Supplement": range(4992, 5024), + "Cherokee": range(5024, 5120), + "Unified Canadian Aboriginal Syllabics": range(5120, 5760), + "Ogham": range(5760, 5792), + "Runic": range(5792, 5888), + "Tagalog": range(5888, 5920), + "Hanunoo": range(5920, 5952), + "Buhid": range(5952, 5984), + "Tagbanwa": range(5984, 6016), + "Khmer": range(6016, 6144), + "Mongolian": range(6144, 6320), + "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6400), + "Limbu": range(6400, 6480), + "Tai Le": range(6480, 6528), + "New Tai Lue": range(6528, 6624), + "Khmer Symbols": range(6624, 6656), + "Buginese": range(6656, 6688), + "Tai Tham": range(6688, 6832), + "Combining Diacritical Marks Extended": range(6832, 6912), + "Balinese": range(6912, 7040), + "Sundanese": range(7040, 7104), + "Batak": range(7104, 7168), + "Lepcha": range(7168, 7248), + "Ol Chiki": range(7248, 7296), + "Cyrillic Extended-C": range(7296, 7312), + "Georgian Extended": range(7312, 7360), + "Sundanese Supplement": range(7360, 7376), + "Vedic Extensions": range(7376, 7424), + "Phonetic Extensions": range(7424, 7552), + "Phonetic Extensions Supplement": range(7552, 7616), + "Combining Diacritical Marks Supplement": range(7616, 7680), + "Latin Extended Additional": range(7680, 7936), + "Greek Extended": range(7936, 8192), + "General Punctuation": range(8192, 8304), + "Superscripts and Subscripts": range(8304, 8352), + "Currency Symbols": range(8352, 8400), + "Combining Diacritical Marks for Symbols": range(8400, 8448), + "Letterlike Symbols": range(8448, 8528), + "Number Forms": range(8528, 8592), + "Arrows": range(8592, 8704), + "Mathematical Operators": range(8704, 8960), + "Miscellaneous Technical": range(8960, 9216), + "Control Pictures": range(9216, 9280), + "Optical Character Recognition": range(9280, 9312), + "Enclosed Alphanumerics": range(9312, 9472), + "Box Drawing": range(9472, 9600), + "Block Elements": range(9600, 9632), + "Geometric Shapes": range(9632, 9728), + "Miscellaneous Symbols": range(9728, 9984), + "Dingbats": range(9984, 10176), + "Miscellaneous Mathematical Symbols-A": range(10176, 10224), + "Supplemental Arrows-A": range(10224, 10240), + "Braille Patterns": range(10240, 10496), + "Supplemental Arrows-B": range(10496, 10624), + "Miscellaneous Mathematical Symbols-B": range(10624, 10752), + "Supplemental Mathematical Operators": range(10752, 11008), + "Miscellaneous Symbols and Arrows": range(11008, 11264), + "Glagolitic": range(11264, 11360), + "Latin Extended-C": range(11360, 11392), + "Coptic": range(11392, 11520), + "Georgian Supplement": range(11520, 11568), + "Tifinagh": range(11568, 11648), + "Ethiopic Extended": range(11648, 11744), + "Cyrillic Extended-A": range(11744, 11776), + "Supplemental Punctuation": range(11776, 11904), + "CJK Radicals Supplement": range(11904, 12032), + "Kangxi Radicals": range(12032, 12256), + "Ideographic Description Characters": range(12272, 12288), + "CJK Symbols and Punctuation": range(12288, 12352), + "Hiragana": range(12352, 12448), + "Katakana": range(12448, 12544), + "Bopomofo": range(12544, 12592), + "Hangul Compatibility Jamo": range(12592, 12688), + "Kanbun": range(12688, 12704), + "Bopomofo Extended": range(12704, 12736), + "CJK Strokes": range(12736, 12784), + "Katakana Phonetic Extensions": range(12784, 12800), + "Enclosed CJK Letters and Months": range(12800, 13056), + "CJK Compatibility": range(13056, 13312), + "CJK Unified Ideographs Extension A": range(13312, 19904), + "Yijing Hexagram Symbols": range(19904, 19968), + "CJK Unified Ideographs": range(19968, 40960), + "Yi Syllables": range(40960, 42128), + "Yi Radicals": range(42128, 42192), + "Lisu": range(42192, 42240), + "Vai": range(42240, 42560), + "Cyrillic Extended-B": range(42560, 42656), + "Bamum": range(42656, 42752), + "Modifier Tone Letters": range(42752, 42784), + "Latin Extended-D": range(42784, 43008), + "Syloti Nagri": range(43008, 43056), + "Common Indic Number Forms": range(43056, 43072), + "Phags-pa": range(43072, 43136), + "Saurashtra": range(43136, 43232), + "Devanagari Extended": range(43232, 43264), + "Kayah Li": range(43264, 43312), + "Rejang": range(43312, 43360), + "Hangul Jamo Extended-A": range(43360, 43392), + "Javanese": range(43392, 43488), + "Myanmar Extended-B": range(43488, 43520), + "Cham": range(43520, 43616), + "Myanmar Extended-A": range(43616, 43648), + "Tai Viet": range(43648, 43744), + "Meetei Mayek Extensions": range(43744, 43776), + "Ethiopic Extended-A": range(43776, 43824), + "Latin Extended-E": range(43824, 43888), + "Cherokee Supplement": range(43888, 43968), + "Meetei Mayek": range(43968, 44032), + "Hangul Syllables": range(44032, 55216), + "Hangul Jamo Extended-B": range(55216, 55296), + "High Surrogates": range(55296, 56192), + "High Private Use Surrogates": range(56192, 56320), + "Low Surrogates": range(56320, 57344), + "Private Use Area": range(57344, 63744), + "CJK Compatibility Ideographs": range(63744, 64256), + "Alphabetic Presentation Forms": range(64256, 64336), + "Arabic Presentation Forms-A": range(64336, 65024), + "Variation Selectors": range(65024, 65040), + "Vertical Forms": range(65040, 65056), + "Combining Half Marks": range(65056, 65072), + "CJK Compatibility Forms": range(65072, 65104), + "Small Form Variants": range(65104, 65136), + "Arabic Presentation Forms-B": range(65136, 65280), + "Halfwidth and Fullwidth Forms": range(65280, 65520), + "Specials": range(65520, 65536), + "Linear B Syllabary": range(65536, 65664), + "Linear B Ideograms": range(65664, 65792), + "Aegean Numbers": range(65792, 65856), + "Ancient Greek Numbers": range(65856, 65936), + "Ancient Symbols": range(65936, 66000), + "Phaistos Disc": range(66000, 66048), + "Lycian": range(66176, 66208), + "Carian": range(66208, 66272), + "Coptic Epact Numbers": range(66272, 66304), + "Old Italic": range(66304, 66352), + "Gothic": range(66352, 66384), + "Old Permic": range(66384, 66432), + "Ugaritic": range(66432, 66464), + "Old Persian": range(66464, 66528), + "Deseret": range(66560, 66640), + "Shavian": range(66640, 66688), + "Osmanya": range(66688, 66736), + "Osage": range(66736, 66816), + "Elbasan": range(66816, 66864), + "Caucasian Albanian": range(66864, 66928), + "Vithkuqi": range(66928, 67008), + "Linear A": range(67072, 67456), + "Latin Extended-F": range(67456, 67520), + "Cypriot Syllabary": range(67584, 67648), + "Imperial Aramaic": range(67648, 67680), + "Palmyrene": range(67680, 67712), + "Nabataean": range(67712, 67760), + "Hatran": range(67808, 67840), + "Phoenician": range(67840, 67872), + "Lydian": range(67872, 67904), + "Meroitic Hieroglyphs": range(67968, 68000), + "Meroitic Cursive": range(68000, 68096), + "Kharoshthi": range(68096, 68192), + "Old South Arabian": range(68192, 68224), + "Old North Arabian": range(68224, 68256), + "Manichaean": range(68288, 68352), + "Avestan": range(68352, 68416), + "Inscriptional Parthian": range(68416, 68448), + "Inscriptional Pahlavi": range(68448, 68480), + "Psalter Pahlavi": range(68480, 68528), + "Old Turkic": range(68608, 68688), + "Old Hungarian": range(68736, 68864), + "Hanifi Rohingya": range(68864, 68928), + "Rumi Numeral Symbols": range(69216, 69248), + "Yezidi": range(69248, 69312), + "Arabic Extended-C": range(69312, 69376), + "Old Sogdian": range(69376, 69424), + "Sogdian": range(69424, 69488), + "Old Uyghur": range(69488, 69552), + "Chorasmian": range(69552, 69600), + "Elymaic": range(69600, 69632), + "Brahmi": range(69632, 69760), + "Kaithi": range(69760, 69840), + "Sora Sompeng": range(69840, 69888), + "Chakma": range(69888, 69968), + "Mahajani": range(69968, 70016), + "Sharada": range(70016, 70112), + "Sinhala Archaic Numbers": range(70112, 70144), + "Khojki": range(70144, 70224), + "Multani": range(70272, 70320), + "Khudawadi": range(70320, 70400), + "Grantha": range(70400, 70528), + "Newa": range(70656, 70784), + "Tirhuta": range(70784, 70880), + "Siddham": range(71040, 71168), + "Modi": range(71168, 71264), + "Mongolian Supplement": range(71264, 71296), + "Takri": range(71296, 71376), + "Ahom": range(71424, 71504), + "Dogra": range(71680, 71760), + "Warang Citi": range(71840, 71936), + "Dives Akuru": range(71936, 72032), + "Nandinagari": range(72096, 72192), + "Zanabazar Square": range(72192, 72272), + "Soyombo": range(72272, 72368), + "Unified Canadian Aboriginal Syllabics Extended-A": range(72368, 72384), + "Pau Cin Hau": range(72384, 72448), + "Devanagari Extended-A": range(72448, 72544), + "Bhaiksuki": range(72704, 72816), + "Marchen": range(72816, 72896), + "Masaram Gondi": range(72960, 73056), + "Gunjala Gondi": range(73056, 73136), + "Makasar": range(73440, 73472), + "Kawi": range(73472, 73568), + "Lisu Supplement": range(73648, 73664), + "Tamil Supplement": range(73664, 73728), + "Cuneiform": range(73728, 74752), + "Cuneiform Numbers and Punctuation": range(74752, 74880), + "Early Dynastic Cuneiform": range(74880, 75088), + "Cypro-Minoan": range(77712, 77824), + "Egyptian Hieroglyphs": range(77824, 78896), + "Egyptian Hieroglyph Format Controls": range(78896, 78944), + "Anatolian Hieroglyphs": range(82944, 83584), + "Bamum Supplement": range(92160, 92736), + "Mro": range(92736, 92784), + "Tangsa": range(92784, 92880), + "Bassa Vah": range(92880, 92928), + "Pahawh Hmong": range(92928, 93072), + "Medefaidrin": range(93760, 93856), + "Miao": range(93952, 94112), + "Ideographic Symbols and Punctuation": range(94176, 94208), + "Tangut": range(94208, 100352), + "Tangut Components": range(100352, 101120), + "Khitan Small Script": range(101120, 101632), + "Tangut Supplement": range(101632, 101760), + "Kana Extended-B": range(110576, 110592), + "Kana Supplement": range(110592, 110848), + "Kana Extended-A": range(110848, 110896), + "Small Kana Extension": range(110896, 110960), + "Nushu": range(110960, 111360), + "Duployan": range(113664, 113824), + "Shorthand Format Controls": range(113824, 113840), + "Znamenny Musical Notation": range(118528, 118736), + "Byzantine Musical Symbols": range(118784, 119040), + "Musical Symbols": range(119040, 119296), + "Ancient Greek Musical Notation": range(119296, 119376), + "Kaktovik Numerals": range(119488, 119520), + "Mayan Numerals": range(119520, 119552), + "Tai Xuan Jing Symbols": range(119552, 119648), + "Counting Rod Numerals": range(119648, 119680), + "Mathematical Alphanumeric Symbols": range(119808, 120832), + "Sutton SignWriting": range(120832, 121520), + "Latin Extended-G": range(122624, 122880), + "Glagolitic Supplement": range(122880, 122928), + "Cyrillic Extended-D": range(122928, 123024), + "Nyiakeng Puachue Hmong": range(123136, 123216), + "Toto": range(123536, 123584), + "Wancho": range(123584, 123648), + "Nag Mundari": range(124112, 124160), + "Ethiopic Extended-B": range(124896, 124928), + "Mende Kikakui": range(124928, 125152), + "Adlam": range(125184, 125280), + "Indic Siyaq Numbers": range(126064, 126144), + "Ottoman Siyaq Numbers": range(126208, 126288), + "Arabic Mathematical Alphabetic Symbols": range(126464, 126720), + "Mahjong Tiles": range(126976, 127024), + "Domino Tiles": range(127024, 127136), + "Playing Cards": range(127136, 127232), + "Enclosed Alphanumeric Supplement": range(127232, 127488), + "Enclosed Ideographic Supplement": range(127488, 127744), + "Miscellaneous Symbols and Pictographs": range(127744, 128512), + "Emoticons range(Emoji)": range(128512, 128592), + "Ornamental Dingbats": range(128592, 128640), + "Transport and Map Symbols": range(128640, 128768), + "Alchemical Symbols": range(128768, 128896), + "Geometric Shapes Extended": range(128896, 129024), + "Supplemental Arrows-C": range(129024, 129280), + "Supplemental Symbols and Pictographs": range(129280, 129536), + "Chess Symbols": range(129536, 129648), + "Symbols and Pictographs Extended-A": range(129648, 129792), + "Symbols for Legacy Computing": range(129792, 130048), + "CJK Unified Ideographs Extension B": range(131072, 173792), + "CJK Unified Ideographs Extension C": range(173824, 177984), + "CJK Unified Ideographs Extension D": range(177984, 178208), + "CJK Unified Ideographs Extension E": range(178208, 183984), + "CJK Unified Ideographs Extension F": range(183984, 191472), + "CJK Compatibility Ideographs Supplement": range(194560, 195104), + "CJK Unified Ideographs Extension G": range(196608, 201552), + "CJK Unified Ideographs Extension H": range(201552, 205744), + "Tags": range(917504, 917632), + "Variation Selectors Supplement": range(917760, 918000), + "Supplementary Private Use Area-A": range(983040, 1048576), + "Supplementary Private Use Area-B": range(1048576, 1114112), +} + + +UNICODE_SECONDARY_RANGE_KEYWORD: List[str] = [ + "Supplement", + "Extended", + "Extensions", + "Modifier", + "Marks", + "Punctuation", + "Symbols", + "Forms", + "Operators", + "Miscellaneous", + "Drawing", + "Block", + "Shapes", + "Supplemental", + "Tags", +] + +RE_POSSIBLE_ENCODING_INDICATION = re_compile( + r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", + IGNORECASE, +) + +IANA_NO_ALIASES = [ + "cp720", + "cp737", + "cp856", + "cp874", + "cp875", + "cp1006", + "koi8_r", + "koi8_t", + "koi8_u", +] + +IANA_SUPPORTED: List[str] = sorted( + filter( + lambda x: x.endswith("_codec") is False + and x not in {"rot_13", "tactis", "mbcs"}, + list(set(aliases.values())) + IANA_NO_ALIASES, + ) +) + +IANA_SUPPORTED_COUNT: int = len(IANA_SUPPORTED) + +# pre-computed code page that are similar using the function cp_similarity. +IANA_SUPPORTED_SIMILAR: Dict[str, List[str]] = { + "cp037": ["cp1026", "cp1140", "cp273", "cp500"], + "cp1026": ["cp037", "cp1140", "cp273", "cp500"], + "cp1125": ["cp866"], + "cp1140": ["cp037", "cp1026", "cp273", "cp500"], + "cp1250": ["iso8859_2"], + "cp1251": ["kz1048", "ptcp154"], + "cp1252": ["iso8859_15", "iso8859_9", "latin_1"], + "cp1253": ["iso8859_7"], + "cp1254": ["iso8859_15", "iso8859_9", "latin_1"], + "cp1257": ["iso8859_13"], + "cp273": ["cp037", "cp1026", "cp1140", "cp500"], + "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], + "cp500": ["cp037", "cp1026", "cp1140", "cp273"], + "cp850": ["cp437", "cp857", "cp858", "cp865"], + "cp857": ["cp850", "cp858", "cp865"], + "cp858": ["cp437", "cp850", "cp857", "cp865"], + "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], + "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], + "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], + "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], + "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], + "cp866": ["cp1125"], + "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], + "iso8859_11": ["tis_620"], + "iso8859_13": ["cp1257"], + "iso8859_14": [ + "iso8859_10", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_15": [ + "cp1252", + "cp1254", + "iso8859_10", + "iso8859_14", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_16": [ + "iso8859_14", + "iso8859_15", + "iso8859_2", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], + "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], + "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], + "iso8859_7": ["cp1253"], + "iso8859_9": [ + "cp1252", + "cp1254", + "cp1258", + "iso8859_10", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_4", + "latin_1", + ], + "kz1048": ["cp1251", "ptcp154"], + "latin_1": [ + "cp1252", + "cp1254", + "cp1258", + "iso8859_10", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_4", + "iso8859_9", + ], + "mac_iceland": ["mac_roman", "mac_turkish"], + "mac_roman": ["mac_iceland", "mac_turkish"], + "mac_turkish": ["mac_iceland", "mac_roman"], + "ptcp154": ["cp1251", "kz1048"], + "tis_620": ["iso8859_11"], +} + + +CHARDET_CORRESPONDENCE: Dict[str, str] = { + "iso2022_kr": "ISO-2022-KR", + "iso2022_jp": "ISO-2022-JP", + "euc_kr": "EUC-KR", + "tis_620": "TIS-620", + "utf_32": "UTF-32", + "euc_jp": "EUC-JP", + "koi8_r": "KOI8-R", + "iso8859_1": "ISO-8859-1", + "iso8859_2": "ISO-8859-2", + "iso8859_5": "ISO-8859-5", + "iso8859_6": "ISO-8859-6", + "iso8859_7": "ISO-8859-7", + "iso8859_8": "ISO-8859-8", + "utf_16": "UTF-16", + "cp855": "IBM855", + "mac_cyrillic": "MacCyrillic", + "gb2312": "GB2312", + "gb18030": "GB18030", + "cp932": "CP932", + "cp866": "IBM866", + "utf_8": "utf-8", + "utf_8_sig": "UTF-8-SIG", + "shift_jis": "SHIFT_JIS", + "big5": "Big5", + "cp1250": "windows-1250", + "cp1251": "windows-1251", + "cp1252": "Windows-1252", + "cp1253": "windows-1253", + "cp1255": "windows-1255", + "cp1256": "windows-1256", + "cp1254": "Windows-1254", + "cp949": "CP949", +} + + +COMMON_SAFE_ASCII_CHARACTERS: Set[str] = { + "<", + ">", + "=", + ":", + "/", + "&", + ";", + "{", + "}", + "[", + "]", + ",", + "|", + '"', + "-", +} + + +KO_NAMES: Set[str] = {"johab", "cp949", "euc_kr"} +ZH_NAMES: Set[str] = {"big5", "cp950", "big5hkscs", "hz"} + +# Logging LEVEL below DEBUG +TRACE: int = 5 + + +# Language label that contain the em dash "—" +# character are to be considered alternative seq to origin +FREQUENCIES: Dict[str, List[str]] = { + "English": [ + "e", + "a", + "t", + "i", + "o", + "n", + "s", + "r", + "h", + "l", + "d", + "c", + "u", + "m", + "f", + "p", + "g", + "w", + "y", + "b", + "v", + "k", + "x", + "j", + "z", + "q", + ], + "English—": [ + "e", + "a", + "t", + "i", + "o", + "n", + "s", + "r", + "h", + "l", + "d", + "c", + "m", + "u", + "f", + "p", + "g", + "w", + "b", + "y", + "v", + "k", + "j", + "x", + "z", + "q", + ], + "German": [ + "e", + "n", + "i", + "r", + "s", + "t", + "a", + "d", + "h", + "u", + "l", + "g", + "o", + "c", + "m", + "b", + "f", + "k", + "w", + "z", + "p", + "v", + "ü", + "ä", + "ö", + "j", + ], + "French": [ + "e", + "a", + "s", + "n", + "i", + "t", + "r", + "l", + "u", + "o", + "d", + "c", + "p", + "m", + "é", + "v", + "g", + "f", + "b", + "h", + "q", + "à", + "x", + "è", + "y", + "j", + ], + "Dutch": [ + "e", + "n", + "a", + "i", + "r", + "t", + "o", + "d", + "s", + "l", + "g", + "h", + "v", + "m", + "u", + "k", + "c", + "p", + "b", + "w", + "j", + "z", + "f", + "y", + "x", + "ë", + ], + "Italian": [ + "e", + "i", + "a", + "o", + "n", + "l", + "t", + "r", + "s", + "c", + "d", + "u", + "p", + "m", + "g", + "v", + "f", + "b", + "z", + "h", + "q", + "è", + "à", + "k", + "y", + "ò", + ], + "Polish": [ + "a", + "i", + "o", + "e", + "n", + "r", + "z", + "w", + "s", + "c", + "t", + "k", + "y", + "d", + "p", + "m", + "u", + "l", + "j", + "ł", + "g", + "b", + "h", + "ą", + "ę", + "ó", + ], + "Spanish": [ + "e", + "a", + "o", + "n", + "s", + "r", + "i", + "l", + "d", + "t", + "c", + "u", + "m", + "p", + "b", + "g", + "v", + "f", + "y", + "ó", + "h", + "q", + "í", + "j", + "z", + "á", + ], + "Russian": [ + "о", + "а", + "е", + "и", + "н", + "с", + "т", + "р", + "в", + "л", + "к", + "м", + "д", + "п", + "у", + "г", + "я", + "ы", + "з", + "б", + "й", + "ь", + "ч", + "х", + "ж", + "ц", + ], + # Jap-Kanji + "Japanese": [ + "人", + "一", + "大", + "亅", + "丁", + "丨", + "竹", + "笑", + "口", + "日", + "今", + "二", + "彳", + "行", + "十", + "土", + "丶", + "寸", + "寺", + "時", + "乙", + "丿", + "乂", + "气", + "気", + "冂", + "巾", + "亠", + "市", + "目", + "儿", + "見", + "八", + "小", + "凵", + "県", + "月", + "彐", + "門", + "間", + "木", + "東", + "山", + "出", + "本", + "中", + "刀", + "分", + "耳", + "又", + "取", + "最", + "言", + "田", + "心", + "思", + "刂", + "前", + "京", + "尹", + "事", + "生", + "厶", + "云", + "会", + "未", + "来", + "白", + "冫", + "楽", + "灬", + "馬", + "尸", + "尺", + "駅", + "明", + "耂", + "者", + "了", + "阝", + "都", + "高", + "卜", + "占", + "厂", + "广", + "店", + "子", + "申", + "奄", + "亻", + "俺", + "上", + "方", + "冖", + "学", + "衣", + "艮", + "食", + "自", + ], + # Jap-Katakana + "Japanese—": [ + "ー", + "ン", + "ス", + "・", + "ル", + "ト", + "リ", + "イ", + "ア", + "ラ", + "ッ", + "ク", + "ド", + "シ", + "レ", + "ジ", + "タ", + "フ", + "ロ", + "カ", + "テ", + "マ", + "ィ", + "グ", + "バ", + "ム", + "プ", + "オ", + "コ", + "デ", + "ニ", + "ウ", + "メ", + "サ", + "ビ", + "ナ", + "ブ", + "ャ", + "エ", + "ュ", + "チ", + "キ", + "ズ", + "ダ", + "パ", + "ミ", + "ェ", + "ョ", + "ハ", + "セ", + "ベ", + "ガ", + "モ", + "ツ", + "ネ", + "ボ", + "ソ", + "ノ", + "ァ", + "ヴ", + "ワ", + "ポ", + "ペ", + "ピ", + "ケ", + "ゴ", + "ギ", + "ザ", + "ホ", + "ゲ", + "ォ", + "ヤ", + "ヒ", + "ユ", + "ヨ", + "ヘ", + "ゼ", + "ヌ", + "ゥ", + "ゾ", + "ヶ", + "ヂ", + "ヲ", + "ヅ", + "ヵ", + "ヱ", + "ヰ", + "ヮ", + "ヽ", + "゠", + "ヾ", + "ヷ", + "ヿ", + "ヸ", + "ヹ", + "ヺ", + ], + # Jap-Hiragana + "Japanese——": [ + "の", + "に", + "る", + "た", + "と", + "は", + "し", + "い", + "を", + "で", + "て", + "が", + "な", + "れ", + "か", + "ら", + "さ", + "っ", + "り", + "す", + "あ", + "も", + "こ", + "ま", + "う", + "く", + "よ", + "き", + "ん", + "め", + "お", + "け", + "そ", + "つ", + "だ", + "や", + "え", + "ど", + "わ", + "ち", + "み", + "せ", + "じ", + "ば", + "へ", + "び", + "ず", + "ろ", + "ほ", + "げ", + "む", + "べ", + "ひ", + "ょ", + "ゆ", + "ぶ", + "ご", + "ゃ", + "ね", + "ふ", + "ぐ", + "ぎ", + "ぼ", + "ゅ", + "づ", + "ざ", + "ぞ", + "ぬ", + "ぜ", + "ぱ", + "ぽ", + "ぷ", + "ぴ", + "ぃ", + "ぁ", + "ぇ", + "ぺ", + "ゞ", + "ぢ", + "ぉ", + "ぅ", + "ゐ", + "ゝ", + "ゑ", + "゛", + "゜", + "ゎ", + "ゔ", + "゚", + "ゟ", + "゙", + "ゕ", + "ゖ", + ], + "Portuguese": [ + "a", + "e", + "o", + "s", + "i", + "r", + "d", + "n", + "t", + "m", + "u", + "c", + "l", + "p", + "g", + "v", + "b", + "f", + "h", + "ã", + "q", + "é", + "ç", + "á", + "z", + "í", + ], + "Swedish": [ + "e", + "a", + "n", + "r", + "t", + "s", + "i", + "l", + "d", + "o", + "m", + "k", + "g", + "v", + "h", + "f", + "u", + "p", + "ä", + "c", + "b", + "ö", + "å", + "y", + "j", + "x", + ], + "Chinese": [ + "的", + "一", + "是", + "不", + "了", + "在", + "人", + "有", + "我", + "他", + "这", + "个", + "们", + "中", + "来", + "上", + "大", + "为", + "和", + "国", + "地", + "到", + "以", + "说", + "时", + "要", + "就", + "出", + "会", + "可", + "也", + "你", + "对", + "生", + "能", + "而", + "子", + "那", + "得", + "于", + "着", + "下", + "自", + "之", + "年", + "过", + "发", + "后", + "作", + "里", + "用", + "道", + "行", + "所", + "然", + "家", + "种", + "事", + "成", + "方", + "多", + "经", + "么", + "去", + "法", + "学", + "如", + "都", + "同", + "现", + "当", + "没", + "动", + "面", + "起", + "看", + "定", + "天", + "分", + "还", + "进", + "好", + "小", + "部", + "其", + "些", + "主", + "样", + "理", + "心", + "她", + "本", + "前", + "开", + "但", + "因", + "只", + "从", + "想", + "实", + ], + "Ukrainian": [ + "о", + "а", + "н", + "і", + "и", + "р", + "в", + "т", + "е", + "с", + "к", + "л", + "у", + "д", + "м", + "п", + "з", + "я", + "ь", + "б", + "г", + "й", + "ч", + "х", + "ц", + "ї", + ], + "Norwegian": [ + "e", + "r", + "n", + "t", + "a", + "s", + "i", + "o", + "l", + "d", + "g", + "k", + "m", + "v", + "f", + "p", + "u", + "b", + "h", + "å", + "y", + "j", + "ø", + "c", + "æ", + "w", + ], + "Finnish": [ + "a", + "i", + "n", + "t", + "e", + "s", + "l", + "o", + "u", + "k", + "ä", + "m", + "r", + "v", + "j", + "h", + "p", + "y", + "d", + "ö", + "g", + "c", + "b", + "f", + "w", + "z", + ], + "Vietnamese": [ + "n", + "h", + "t", + "i", + "c", + "g", + "a", + "o", + "u", + "m", + "l", + "r", + "à", + "đ", + "s", + "e", + "v", + "p", + "b", + "y", + "ư", + "d", + "á", + "k", + "ộ", + "ế", + ], + "Czech": [ + "o", + "e", + "a", + "n", + "t", + "s", + "i", + "l", + "v", + "r", + "k", + "d", + "u", + "m", + "p", + "í", + "c", + "h", + "z", + "á", + "y", + "j", + "b", + "ě", + "é", + "ř", + ], + "Hungarian": [ + "e", + "a", + "t", + "l", + "s", + "n", + "k", + "r", + "i", + "o", + "z", + "á", + "é", + "g", + "m", + "b", + "y", + "v", + "d", + "h", + "u", + "p", + "j", + "ö", + "f", + "c", + ], + "Korean": [ + "이", + "다", + "에", + "의", + "는", + "로", + "하", + "을", + "가", + "고", + "지", + "서", + "한", + "은", + "기", + "으", + "년", + "대", + "사", + "시", + "를", + "리", + "도", + "인", + "스", + "일", + ], + "Indonesian": [ + "a", + "n", + "e", + "i", + "r", + "t", + "u", + "s", + "d", + "k", + "m", + "l", + "g", + "p", + "b", + "o", + "h", + "y", + "j", + "c", + "w", + "f", + "v", + "z", + "x", + "q", + ], + "Turkish": [ + "a", + "e", + "i", + "n", + "r", + "l", + "ı", + "k", + "d", + "t", + "s", + "m", + "y", + "u", + "o", + "b", + "ü", + "ş", + "v", + "g", + "z", + "h", + "c", + "p", + "ç", + "ğ", + ], + "Romanian": [ + "e", + "i", + "a", + "r", + "n", + "t", + "u", + "l", + "o", + "c", + "s", + "d", + "p", + "m", + "ă", + "f", + "v", + "î", + "g", + "b", + "ș", + "ț", + "z", + "h", + "â", + "j", + ], + "Farsi": [ + "ا", + "ی", + "ر", + "د", + "ن", + "ه", + "و", + "م", + "ت", + "ب", + "س", + "ل", + "ک", + "ش", + "ز", + "ف", + "گ", + "ع", + "خ", + "ق", + "ج", + "آ", + "پ", + "ح", + "ط", + "ص", + ], + "Arabic": [ + "ا", + "ل", + "ي", + "م", + "و", + "ن", + "ر", + "ت", + "ب", + "ة", + "ع", + "د", + "س", + "ف", + "ه", + "ك", + "ق", + "أ", + "ح", + "ج", + "ش", + "ط", + "ص", + "ى", + "خ", + "إ", + ], + "Danish": [ + "e", + "r", + "n", + "t", + "a", + "i", + "s", + "d", + "l", + "o", + "g", + "m", + "k", + "f", + "v", + "u", + "b", + "h", + "p", + "å", + "y", + "ø", + "æ", + "c", + "j", + "w", + ], + "Serbian": [ + "а", + "и", + "о", + "е", + "н", + "р", + "с", + "у", + "т", + "к", + "ј", + "в", + "д", + "м", + "п", + "л", + "г", + "з", + "б", + "a", + "i", + "e", + "o", + "n", + "ц", + "ш", + ], + "Lithuanian": [ + "i", + "a", + "s", + "o", + "r", + "e", + "t", + "n", + "u", + "k", + "m", + "l", + "p", + "v", + "d", + "j", + "g", + "ė", + "b", + "y", + "ų", + "š", + "ž", + "c", + "ą", + "į", + ], + "Slovene": [ + "e", + "a", + "i", + "o", + "n", + "r", + "s", + "l", + "t", + "j", + "v", + "k", + "d", + "p", + "m", + "u", + "z", + "b", + "g", + "h", + "č", + "c", + "š", + "ž", + "f", + "y", + ], + "Slovak": [ + "o", + "a", + "e", + "n", + "i", + "r", + "v", + "t", + "s", + "l", + "k", + "d", + "m", + "p", + "u", + "c", + "h", + "j", + "b", + "z", + "á", + "y", + "ý", + "í", + "č", + "é", + ], + "Hebrew": [ + "י", + "ו", + "ה", + "ל", + "ר", + "ב", + "ת", + "מ", + "א", + "ש", + "נ", + "ע", + "ם", + "ד", + "ק", + "ח", + "פ", + "ס", + "כ", + "ג", + "ט", + "צ", + "ן", + "ז", + "ך", + ], + "Bulgarian": [ + "а", + "и", + "о", + "е", + "н", + "т", + "р", + "с", + "в", + "л", + "к", + "д", + "п", + "м", + "з", + "г", + "я", + "ъ", + "у", + "б", + "ч", + "ц", + "й", + "ж", + "щ", + "х", + ], + "Croatian": [ + "a", + "i", + "o", + "e", + "n", + "r", + "j", + "s", + "t", + "u", + "k", + "l", + "v", + "d", + "m", + "p", + "g", + "z", + "b", + "c", + "č", + "h", + "š", + "ž", + "ć", + "f", + ], + "Hindi": [ + "क", + "र", + "स", + "न", + "त", + "म", + "ह", + "प", + "य", + "ल", + "व", + "ज", + "द", + "ग", + "ब", + "श", + "ट", + "अ", + "ए", + "थ", + "भ", + "ड", + "च", + "ध", + "ष", + "इ", + ], + "Estonian": [ + "a", + "i", + "e", + "s", + "t", + "l", + "u", + "n", + "o", + "k", + "r", + "d", + "m", + "v", + "g", + "p", + "j", + "h", + "ä", + "b", + "õ", + "ü", + "f", + "c", + "ö", + "y", + ], + "Thai": [ + "า", + "น", + "ร", + "อ", + "ก", + "เ", + "ง", + "ม", + "ย", + "ล", + "ว", + "ด", + "ท", + "ส", + "ต", + "ะ", + "ป", + "บ", + "ค", + "ห", + "แ", + "จ", + "พ", + "ช", + "ข", + "ใ", + ], + "Greek": [ + "α", + "τ", + "ο", + "ι", + "ε", + "ν", + "ρ", + "σ", + "κ", + "η", + "π", + "ς", + "υ", + "μ", + "λ", + "ί", + "ό", + "ά", + "γ", + "έ", + "δ", + "ή", + "ω", + "χ", + "θ", + "ύ", + ], + "Tamil": [ + "க", + "த", + "ப", + "ட", + "ர", + "ம", + "ல", + "ன", + "வ", + "ற", + "ய", + "ள", + "ச", + "ந", + "இ", + "ண", + "அ", + "ஆ", + "ழ", + "ங", + "எ", + "உ", + "ஒ", + "ஸ", + ], + "Kazakh": [ + "а", + "ы", + "е", + "н", + "т", + "р", + "л", + "і", + "д", + "с", + "м", + "қ", + "к", + "о", + "б", + "и", + "у", + "ғ", + "ж", + "ң", + "з", + "ш", + "й", + "п", + "г", + "ө", + ], +} + +LANGUAGE_SUPPORTED_COUNT: int = len(FREQUENCIES) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/legacy.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/legacy.py new file mode 100644 index 0000000..43aad21 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/legacy.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, Optional, Union +from warnings import warn + +from .api import from_bytes +from .constant import CHARDET_CORRESPONDENCE + + +def detect( + byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any +) -> Dict[str, Optional[Union[str, float]]]: + """ + chardet legacy method + Detect the encoding of the given byte string. It should be mostly backward-compatible. + Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) + This function is deprecated and should be used to migrate your project easily, consult the documentation for + further information. Not planned for removal. + + :param byte_str: The byte sequence to examine. + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + """ + if len(kwargs): + warn( + f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" + ) + + if not isinstance(byte_str, (bytearray, bytes)): + raise TypeError( # pragma: nocover + "Expected object of type bytes or bytearray, got: " + "{0}".format(type(byte_str)) + ) + + if isinstance(byte_str, bytearray): + byte_str = bytes(byte_str) + + r = from_bytes(byte_str).best() + + encoding = r.encoding if r is not None else None + language = r.language if r is not None and r.language != "Unknown" else "" + confidence = 1.0 - r.chaos if r is not None else None + + # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process + # but chardet does return 'utf-8-sig' and it is a valid codec name. + if r is not None and encoding == "utf_8" and r.bom: + encoding += "_sig" + + if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: + encoding = CHARDET_CORRESPONDENCE[encoding] + + return { + "encoding": encoding, + "language": language, + "confidence": confidence, + } diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..3824a42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.py new file mode 100644 index 0000000..77897aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md.py @@ -0,0 +1,615 @@ +from functools import lru_cache +from logging import getLogger +from typing import List, Optional + +from .constant import ( + COMMON_SAFE_ASCII_CHARACTERS, + TRACE, + UNICODE_SECONDARY_RANGE_KEYWORD, +) +from .utils import ( + is_accentuated, + is_arabic, + is_arabic_isolated_form, + is_case_variable, + is_cjk, + is_emoticon, + is_hangul, + is_hiragana, + is_katakana, + is_latin, + is_punctuation, + is_separator, + is_symbol, + is_thai, + is_unprintable, + remove_accent, + unicode_range, +) + + +class MessDetectorPlugin: + """ + Base abstract class used for mess detection plugins. + All detectors MUST extend and implement given methods. + """ + + def eligible(self, character: str) -> bool: + """ + Determine if given character should be fed in. + """ + raise NotImplementedError # pragma: nocover + + def feed(self, character: str) -> None: + """ + The main routine to be executed upon character. + Insert the logic in witch the text would be considered chaotic. + """ + raise NotImplementedError # pragma: nocover + + def reset(self) -> None: # pragma: no cover + """ + Permit to reset the plugin to the initial state. + """ + raise NotImplementedError + + @property + def ratio(self) -> float: + """ + Compute the chaos ratio based on what your feed() has seen. + Must NOT be lower than 0.; No restriction gt 0. + """ + raise NotImplementedError # pragma: nocover + + +class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._punctuation_count: int = 0 + self._symbol_count: int = 0 + self._character_count: int = 0 + + self._last_printable_char: Optional[str] = None + self._frenzy_symbol_in_word: bool = False + + def eligible(self, character: str) -> bool: + return character.isprintable() + + def feed(self, character: str) -> None: + self._character_count += 1 + + if ( + character != self._last_printable_char + and character not in COMMON_SAFE_ASCII_CHARACTERS + ): + if is_punctuation(character): + self._punctuation_count += 1 + elif ( + character.isdigit() is False + and is_symbol(character) + and is_emoticon(character) is False + ): + self._symbol_count += 2 + + self._last_printable_char = character + + def reset(self) -> None: # pragma: no cover + self._punctuation_count = 0 + self._character_count = 0 + self._symbol_count = 0 + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + ratio_of_punctuation: float = ( + self._punctuation_count + self._symbol_count + ) / self._character_count + + return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 + + +class TooManyAccentuatedPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._character_count: int = 0 + self._accentuated_count: int = 0 + + def eligible(self, character: str) -> bool: + return character.isalpha() + + def feed(self, character: str) -> None: + self._character_count += 1 + + if is_accentuated(character): + self._accentuated_count += 1 + + def reset(self) -> None: # pragma: no cover + self._character_count = 0 + self._accentuated_count = 0 + + @property + def ratio(self) -> float: + if self._character_count < 8: + return 0.0 + + ratio_of_accentuation: float = self._accentuated_count / self._character_count + return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 + + +class UnprintablePlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._unprintable_count: int = 0 + self._character_count: int = 0 + + def eligible(self, character: str) -> bool: + return True + + def feed(self, character: str) -> None: + if is_unprintable(character): + self._unprintable_count += 1 + self._character_count += 1 + + def reset(self) -> None: # pragma: no cover + self._unprintable_count = 0 + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + return (self._unprintable_count * 8) / self._character_count + + +class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._successive_count: int = 0 + self._character_count: int = 0 + + self._last_latin_character: Optional[str] = None + + def eligible(self, character: str) -> bool: + return character.isalpha() and is_latin(character) + + def feed(self, character: str) -> None: + self._character_count += 1 + if ( + self._last_latin_character is not None + and is_accentuated(character) + and is_accentuated(self._last_latin_character) + ): + if character.isupper() and self._last_latin_character.isupper(): + self._successive_count += 1 + # Worse if its the same char duplicated with different accent. + if remove_accent(character) == remove_accent(self._last_latin_character): + self._successive_count += 1 + self._last_latin_character = character + + def reset(self) -> None: # pragma: no cover + self._successive_count = 0 + self._character_count = 0 + self._last_latin_character = None + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + return (self._successive_count * 2) / self._character_count + + +class SuspiciousRange(MessDetectorPlugin): + def __init__(self) -> None: + self._suspicious_successive_range_count: int = 0 + self._character_count: int = 0 + self._last_printable_seen: Optional[str] = None + + def eligible(self, character: str) -> bool: + return character.isprintable() + + def feed(self, character: str) -> None: + self._character_count += 1 + + if ( + character.isspace() + or is_punctuation(character) + or character in COMMON_SAFE_ASCII_CHARACTERS + ): + self._last_printable_seen = None + return + + if self._last_printable_seen is None: + self._last_printable_seen = character + return + + unicode_range_a: Optional[str] = unicode_range(self._last_printable_seen) + unicode_range_b: Optional[str] = unicode_range(character) + + if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): + self._suspicious_successive_range_count += 1 + + self._last_printable_seen = character + + def reset(self) -> None: # pragma: no cover + self._character_count = 0 + self._suspicious_successive_range_count = 0 + self._last_printable_seen = None + + @property + def ratio(self) -> float: + if self._character_count <= 24: + return 0.0 + + ratio_of_suspicious_range_usage: float = ( + self._suspicious_successive_range_count * 2 + ) / self._character_count + + return ratio_of_suspicious_range_usage + + +class SuperWeirdWordPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._word_count: int = 0 + self._bad_word_count: int = 0 + self._foreign_long_count: int = 0 + + self._is_current_word_bad: bool = False + self._foreign_long_watch: bool = False + + self._character_count: int = 0 + self._bad_character_count: int = 0 + + self._buffer: str = "" + self._buffer_accent_count: int = 0 + + def eligible(self, character: str) -> bool: + return True + + def feed(self, character: str) -> None: + if character.isalpha(): + self._buffer += character + if is_accentuated(character): + self._buffer_accent_count += 1 + if ( + self._foreign_long_watch is False + and (is_latin(character) is False or is_accentuated(character)) + and is_cjk(character) is False + and is_hangul(character) is False + and is_katakana(character) is False + and is_hiragana(character) is False + and is_thai(character) is False + ): + self._foreign_long_watch = True + return + if not self._buffer: + return + if ( + character.isspace() or is_punctuation(character) or is_separator(character) + ) and self._buffer: + self._word_count += 1 + buffer_length: int = len(self._buffer) + + self._character_count += buffer_length + + if buffer_length >= 4: + if self._buffer_accent_count / buffer_length > 0.34: + self._is_current_word_bad = True + # Word/Buffer ending with an upper case accentuated letter are so rare, + # that we will consider them all as suspicious. Same weight as foreign_long suspicious. + if ( + is_accentuated(self._buffer[-1]) + and self._buffer[-1].isupper() + and all(_.isupper() for _ in self._buffer) is False + ): + self._foreign_long_count += 1 + self._is_current_word_bad = True + if buffer_length >= 24 and self._foreign_long_watch: + camel_case_dst = [ + i + for c, i in zip(self._buffer, range(0, buffer_length)) + if c.isupper() + ] + probable_camel_cased: bool = False + + if camel_case_dst and (len(camel_case_dst) / buffer_length <= 0.3): + probable_camel_cased = True + + if not probable_camel_cased: + self._foreign_long_count += 1 + self._is_current_word_bad = True + + if self._is_current_word_bad: + self._bad_word_count += 1 + self._bad_character_count += len(self._buffer) + self._is_current_word_bad = False + + self._foreign_long_watch = False + self._buffer = "" + self._buffer_accent_count = 0 + elif ( + character not in {"<", ">", "-", "=", "~", "|", "_"} + and character.isdigit() is False + and is_symbol(character) + ): + self._is_current_word_bad = True + self._buffer += character + + def reset(self) -> None: # pragma: no cover + self._buffer = "" + self._is_current_word_bad = False + self._foreign_long_watch = False + self._bad_word_count = 0 + self._word_count = 0 + self._character_count = 0 + self._bad_character_count = 0 + self._foreign_long_count = 0 + + @property + def ratio(self) -> float: + if self._word_count <= 10 and self._foreign_long_count == 0: + return 0.0 + + return self._bad_character_count / self._character_count + + +class CjkInvalidStopPlugin(MessDetectorPlugin): + """ + GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and + can be easily detected. Searching for the overuse of '丅' and '丄'. + """ + + def __init__(self) -> None: + self._wrong_stop_count: int = 0 + self._cjk_character_count: int = 0 + + def eligible(self, character: str) -> bool: + return True + + def feed(self, character: str) -> None: + if character in {"丅", "丄"}: + self._wrong_stop_count += 1 + return + if is_cjk(character): + self._cjk_character_count += 1 + + def reset(self) -> None: # pragma: no cover + self._wrong_stop_count = 0 + self._cjk_character_count = 0 + + @property + def ratio(self) -> float: + if self._cjk_character_count < 16: + return 0.0 + return self._wrong_stop_count / self._cjk_character_count + + +class ArchaicUpperLowerPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._buf: bool = False + + self._character_count_since_last_sep: int = 0 + + self._successive_upper_lower_count: int = 0 + self._successive_upper_lower_count_final: int = 0 + + self._character_count: int = 0 + + self._last_alpha_seen: Optional[str] = None + self._current_ascii_only: bool = True + + def eligible(self, character: str) -> bool: + return True + + def feed(self, character: str) -> None: + is_concerned = character.isalpha() and is_case_variable(character) + chunk_sep = is_concerned is False + + if chunk_sep and self._character_count_since_last_sep > 0: + if ( + self._character_count_since_last_sep <= 64 + and character.isdigit() is False + and self._current_ascii_only is False + ): + self._successive_upper_lower_count_final += ( + self._successive_upper_lower_count + ) + + self._successive_upper_lower_count = 0 + self._character_count_since_last_sep = 0 + self._last_alpha_seen = None + self._buf = False + self._character_count += 1 + self._current_ascii_only = True + + return + + if self._current_ascii_only is True and character.isascii() is False: + self._current_ascii_only = False + + if self._last_alpha_seen is not None: + if (character.isupper() and self._last_alpha_seen.islower()) or ( + character.islower() and self._last_alpha_seen.isupper() + ): + if self._buf is True: + self._successive_upper_lower_count += 2 + self._buf = False + else: + self._buf = True + else: + self._buf = False + + self._character_count += 1 + self._character_count_since_last_sep += 1 + self._last_alpha_seen = character + + def reset(self) -> None: # pragma: no cover + self._character_count = 0 + self._character_count_since_last_sep = 0 + self._successive_upper_lower_count = 0 + self._successive_upper_lower_count_final = 0 + self._last_alpha_seen = None + self._buf = False + self._current_ascii_only = True + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + return self._successive_upper_lower_count_final / self._character_count + + +class ArabicIsolatedFormPlugin(MessDetectorPlugin): + def __init__(self) -> None: + self._character_count: int = 0 + self._isolated_form_count: int = 0 + + def reset(self) -> None: # pragma: no cover + self._character_count = 0 + self._isolated_form_count = 0 + + def eligible(self, character: str) -> bool: + return is_arabic(character) + + def feed(self, character: str) -> None: + self._character_count += 1 + + if is_arabic_isolated_form(character): + self._isolated_form_count += 1 + + @property + def ratio(self) -> float: + if self._character_count < 8: + return 0.0 + + isolated_form_usage: float = self._isolated_form_count / self._character_count + + return isolated_form_usage + + +@lru_cache(maxsize=1024) +def is_suspiciously_successive_range( + unicode_range_a: Optional[str], unicode_range_b: Optional[str] +) -> bool: + """ + Determine if two Unicode range seen next to each other can be considered as suspicious. + """ + if unicode_range_a is None or unicode_range_b is None: + return True + + if unicode_range_a == unicode_range_b: + return False + + if "Latin" in unicode_range_a and "Latin" in unicode_range_b: + return False + + if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: + return False + + # Latin characters can be accompanied with a combining diacritical mark + # eg. Vietnamese. + if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and ( + "Combining" in unicode_range_a or "Combining" in unicode_range_b + ): + return False + + keywords_range_a, keywords_range_b = unicode_range_a.split( + " " + ), unicode_range_b.split(" ") + + for el in keywords_range_a: + if el in UNICODE_SECONDARY_RANGE_KEYWORD: + continue + if el in keywords_range_b: + return False + + # Japanese Exception + range_a_jp_chars, range_b_jp_chars = ( + unicode_range_a + in ( + "Hiragana", + "Katakana", + ), + unicode_range_b in ("Hiragana", "Katakana"), + ) + if (range_a_jp_chars or range_b_jp_chars) and ( + "CJK" in unicode_range_a or "CJK" in unicode_range_b + ): + return False + if range_a_jp_chars and range_b_jp_chars: + return False + + if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: + if "CJK" in unicode_range_a or "CJK" in unicode_range_b: + return False + if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": + return False + + # Chinese/Japanese use dedicated range for punctuation and/or separators. + if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( + unicode_range_a in ["Katakana", "Hiragana"] + and unicode_range_b in ["Katakana", "Hiragana"] + ): + if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: + return False + if "Forms" in unicode_range_a or "Forms" in unicode_range_b: + return False + if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": + return False + + return True + + +@lru_cache(maxsize=2048) +def mess_ratio( + decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False +) -> float: + """ + Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. + """ + + detectors: List[MessDetectorPlugin] = [ + md_class() for md_class in MessDetectorPlugin.__subclasses__() + ] + + length: int = len(decoded_sequence) + 1 + + mean_mess_ratio: float = 0.0 + + if length < 512: + intermediary_mean_mess_ratio_calc: int = 32 + elif length <= 1024: + intermediary_mean_mess_ratio_calc = 64 + else: + intermediary_mean_mess_ratio_calc = 128 + + for character, index in zip(decoded_sequence + "\n", range(length)): + for detector in detectors: + if detector.eligible(character): + detector.feed(character) + + if ( + index > 0 and index % intermediary_mean_mess_ratio_calc == 0 + ) or index == length - 1: + mean_mess_ratio = sum(dt.ratio for dt in detectors) + + if mean_mess_ratio >= maximum_threshold: + break + + if debug: + logger = getLogger("charset_normalizer") + + logger.log( + TRACE, + "Mess-detector extended-analysis start. " + f"intermediary_mean_mess_ratio_calc={intermediary_mean_mess_ratio_calc} mean_mess_ratio={mean_mess_ratio} " + f"maximum_threshold={maximum_threshold}", + ) + + if len(decoded_sequence) > 16: + logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") + logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") + + for dt in detectors: # pragma: nocover + logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") + + return round(mean_mess_ratio, 3) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md__mypyc.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md__mypyc.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..7ebead5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/md__mypyc.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/models.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/models.py new file mode 100644 index 0000000..a760b9c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/models.py @@ -0,0 +1,340 @@ +from encodings.aliases import aliases +from hashlib import sha256 +from json import dumps +from typing import Any, Dict, Iterator, List, Optional, Tuple, Union + +from .constant import TOO_BIG_SEQUENCE +from .utils import iana_name, is_multi_byte_encoding, unicode_range + + +class CharsetMatch: + def __init__( + self, + payload: bytes, + guessed_encoding: str, + mean_mess_ratio: float, + has_sig_or_bom: bool, + languages: "CoherenceMatches", + decoded_payload: Optional[str] = None, + ): + self._payload: bytes = payload + + self._encoding: str = guessed_encoding + self._mean_mess_ratio: float = mean_mess_ratio + self._languages: CoherenceMatches = languages + self._has_sig_or_bom: bool = has_sig_or_bom + self._unicode_ranges: Optional[List[str]] = None + + self._leaves: List[CharsetMatch] = [] + self._mean_coherence_ratio: float = 0.0 + + self._output_payload: Optional[bytes] = None + self._output_encoding: Optional[str] = None + + self._string: Optional[str] = decoded_payload + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CharsetMatch): + raise TypeError( + "__eq__ cannot be invoked on {} and {}.".format( + str(other.__class__), str(self.__class__) + ) + ) + return self.encoding == other.encoding and self.fingerprint == other.fingerprint + + def __lt__(self, other: object) -> bool: + """ + Implemented to make sorted available upon CharsetMatches items. + """ + if not isinstance(other, CharsetMatch): + raise ValueError + + chaos_difference: float = abs(self.chaos - other.chaos) + coherence_difference: float = abs(self.coherence - other.coherence) + + # Below 1% difference --> Use Coherence + if chaos_difference < 0.01 and coherence_difference > 0.02: + return self.coherence > other.coherence + elif chaos_difference < 0.01 and coherence_difference <= 0.02: + # When having a difficult decision, use the result that decoded as many multi-byte as possible. + # preserve RAM usage! + if len(self._payload) >= TOO_BIG_SEQUENCE: + return self.chaos < other.chaos + return self.multi_byte_usage > other.multi_byte_usage + + return self.chaos < other.chaos + + @property + def multi_byte_usage(self) -> float: + return 1.0 - (len(str(self)) / len(self.raw)) + + def __str__(self) -> str: + # Lazy Str Loading + if self._string is None: + self._string = str(self._payload, self._encoding, "strict") + return self._string + + def __repr__(self) -> str: + return "".format(self.encoding, self.fingerprint) + + def add_submatch(self, other: "CharsetMatch") -> None: + if not isinstance(other, CharsetMatch) or other == self: + raise ValueError( + "Unable to add instance <{}> as a submatch of a CharsetMatch".format( + other.__class__ + ) + ) + + other._string = None # Unload RAM usage; dirty trick. + self._leaves.append(other) + + @property + def encoding(self) -> str: + return self._encoding + + @property + def encoding_aliases(self) -> List[str]: + """ + Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. + """ + also_known_as: List[str] = [] + for u, p in aliases.items(): + if self.encoding == u: + also_known_as.append(p) + elif self.encoding == p: + also_known_as.append(u) + return also_known_as + + @property + def bom(self) -> bool: + return self._has_sig_or_bom + + @property + def byte_order_mark(self) -> bool: + return self._has_sig_or_bom + + @property + def languages(self) -> List[str]: + """ + Return the complete list of possible languages found in decoded sequence. + Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. + """ + return [e[0] for e in self._languages] + + @property + def language(self) -> str: + """ + Most probable language found in decoded sequence. If none were detected or inferred, the property will return + "Unknown". + """ + if not self._languages: + # Trying to infer the language based on the given encoding + # Its either English or we should not pronounce ourselves in certain cases. + if "ascii" in self.could_be_from_charset: + return "English" + + # doing it there to avoid circular import + from charset_normalizer.cd import encoding_languages, mb_encoding_languages + + languages = ( + mb_encoding_languages(self.encoding) + if is_multi_byte_encoding(self.encoding) + else encoding_languages(self.encoding) + ) + + if len(languages) == 0 or "Latin Based" in languages: + return "Unknown" + + return languages[0] + + return self._languages[0][0] + + @property + def chaos(self) -> float: + return self._mean_mess_ratio + + @property + def coherence(self) -> float: + if not self._languages: + return 0.0 + return self._languages[0][1] + + @property + def percent_chaos(self) -> float: + return round(self.chaos * 100, ndigits=3) + + @property + def percent_coherence(self) -> float: + return round(self.coherence * 100, ndigits=3) + + @property + def raw(self) -> bytes: + """ + Original untouched bytes. + """ + return self._payload + + @property + def submatch(self) -> List["CharsetMatch"]: + return self._leaves + + @property + def has_submatch(self) -> bool: + return len(self._leaves) > 0 + + @property + def alphabets(self) -> List[str]: + if self._unicode_ranges is not None: + return self._unicode_ranges + # list detected ranges + detected_ranges: List[Optional[str]] = [ + unicode_range(char) for char in str(self) + ] + # filter and sort + self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) + return self._unicode_ranges + + @property + def could_be_from_charset(self) -> List[str]: + """ + The complete list of encoding that output the exact SAME str result and therefore could be the originating + encoding. + This list does include the encoding available in property 'encoding'. + """ + return [self._encoding] + [m.encoding for m in self._leaves] + + def output(self, encoding: str = "utf_8") -> bytes: + """ + Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. + Any errors will be simply ignored by the encoder NOT replaced. + """ + if self._output_encoding is None or self._output_encoding != encoding: + self._output_encoding = encoding + self._output_payload = str(self).encode(encoding, "replace") + + return self._output_payload # type: ignore + + @property + def fingerprint(self) -> str: + """ + Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. + """ + return sha256(self.output()).hexdigest() + + +class CharsetMatches: + """ + Container with every CharsetMatch items ordered by default from most probable to the less one. + Act like a list(iterable) but does not implements all related methods. + """ + + def __init__(self, results: Optional[List[CharsetMatch]] = None): + self._results: List[CharsetMatch] = sorted(results) if results else [] + + def __iter__(self) -> Iterator[CharsetMatch]: + yield from self._results + + def __getitem__(self, item: Union[int, str]) -> CharsetMatch: + """ + Retrieve a single item either by its position or encoding name (alias may be used here). + Raise KeyError upon invalid index or encoding not present in results. + """ + if isinstance(item, int): + return self._results[item] + if isinstance(item, str): + item = iana_name(item, False) + for result in self._results: + if item in result.could_be_from_charset: + return result + raise KeyError + + def __len__(self) -> int: + return len(self._results) + + def __bool__(self) -> bool: + return len(self._results) > 0 + + def append(self, item: CharsetMatch) -> None: + """ + Insert a single match. Will be inserted accordingly to preserve sort. + Can be inserted as a submatch. + """ + if not isinstance(item, CharsetMatch): + raise ValueError( + "Cannot append instance '{}' to CharsetMatches".format( + str(item.__class__) + ) + ) + # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) + if len(item.raw) <= TOO_BIG_SEQUENCE: + for match in self._results: + if match.fingerprint == item.fingerprint and match.chaos == item.chaos: + match.add_submatch(item) + return + self._results.append(item) + self._results = sorted(self._results) + + def best(self) -> Optional["CharsetMatch"]: + """ + Simply return the first match. Strict equivalent to matches[0]. + """ + if not self._results: + return None + return self._results[0] + + def first(self) -> Optional["CharsetMatch"]: + """ + Redundant method, call the method best(). Kept for BC reasons. + """ + return self.best() + + +CoherenceMatch = Tuple[str, float] +CoherenceMatches = List[CoherenceMatch] + + +class CliDetectionResult: + def __init__( + self, + path: str, + encoding: Optional[str], + encoding_aliases: List[str], + alternative_encodings: List[str], + language: str, + alphabets: List[str], + has_sig_or_bom: bool, + chaos: float, + coherence: float, + unicode_path: Optional[str], + is_preferred: bool, + ): + self.path: str = path + self.unicode_path: Optional[str] = unicode_path + self.encoding: Optional[str] = encoding + self.encoding_aliases: List[str] = encoding_aliases + self.alternative_encodings: List[str] = alternative_encodings + self.language: str = language + self.alphabets: List[str] = alphabets + self.has_sig_or_bom: bool = has_sig_or_bom + self.chaos: float = chaos + self.coherence: float = coherence + self.is_preferred: bool = is_preferred + + @property + def __dict__(self) -> Dict[str, Any]: # type: ignore + return { + "path": self.path, + "encoding": self.encoding, + "encoding_aliases": self.encoding_aliases, + "alternative_encodings": self.alternative_encodings, + "language": self.language, + "alphabets": self.alphabets, + "has_sig_or_bom": self.has_sig_or_bom, + "chaos": self.chaos, + "coherence": self.coherence, + "unicode_path": self.unicode_path, + "is_preferred": self.is_preferred, + } + + def to_json(self) -> str: + return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/py.typed b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/utils.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/utils.py new file mode 100644 index 0000000..e5cbbf4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/utils.py @@ -0,0 +1,421 @@ +import importlib +import logging +import unicodedata +from codecs import IncrementalDecoder +from encodings.aliases import aliases +from functools import lru_cache +from re import findall +from typing import Generator, List, Optional, Set, Tuple, Union + +from _multibytecodec import MultibyteIncrementalDecoder + +from .constant import ( + ENCODING_MARKS, + IANA_SUPPORTED_SIMILAR, + RE_POSSIBLE_ENCODING_INDICATION, + UNICODE_RANGES_COMBINED, + UNICODE_SECONDARY_RANGE_KEYWORD, + UTF8_MAXIMAL_ALLOCATION, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: + return False + return ( + "WITH GRAVE" in description + or "WITH ACUTE" in description + or "WITH CEDILLA" in description + or "WITH DIAERESIS" in description + or "WITH CIRCUMFLEX" in description + or "WITH TILDE" in description + or "WITH MACRON" in description + or "WITH RING ABOVE" in description + ) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def remove_accent(character: str) -> str: + decomposed: str = unicodedata.decomposition(character) + if not decomposed: + return character + + codes: List[str] = decomposed.split(" ") + + return chr(int(codes[0], 16)) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def unicode_range(character: str) -> Optional[str]: + """ + Retrieve the Unicode range official name from a single character. + """ + character_ord: int = ord(character) + + for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): + if character_ord in ord_range: + return range_name + + return None + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_latin(character: str) -> bool: + try: + description: str = unicodedata.name(character) + except ValueError: + return False + return "LATIN" in description + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_punctuation(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "P" in character_category: + return True + + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + return False + + return "Punctuation" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_symbol(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "S" in character_category or "N" in character_category: + return True + + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + return False + + return "Forms" in character_range and character_category != "Lo" + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_emoticon(character: str) -> bool: + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + return False + + return "Emoticons" in character_range or "Pictographs" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_separator(character: str) -> bool: + if character.isspace() or character in {"|", "+", "<", ">"}: + return True + + character_category: str = unicodedata.category(character) + + return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_case_variable(character: str) -> bool: + return character.islower() != character.isupper() + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "CJK" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hiragana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "HIRAGANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_katakana(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "KATAKANA" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hangul(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "HANGUL" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_thai(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "THAI" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "ARABIC" in character_name + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic_isolated_form(character: str) -> bool: + try: + character_name = unicodedata.name(character) + except ValueError: + return False + + return "ARABIC" in character_name and "ISOLATED FORM" in character_name + + +@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) +def is_unicode_range_secondary(range_name: str) -> bool: + return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_unprintable(character: str) -> bool: + return ( + character.isspace() is False # includes \n \t \r \v + and character.isprintable() is False + and character != "\x1A" # Why? Its the ASCII substitute character. + and character != "\ufeff" # bug discovered in Python, + # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. + ) + + +def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> Optional[str]: + """ + Extract using ASCII-only decoder any specified encoding in the first n-bytes. + """ + if not isinstance(sequence, bytes): + raise TypeError + + seq_len: int = len(sequence) + + results: List[str] = findall( + RE_POSSIBLE_ENCODING_INDICATION, + sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), + ) + + if len(results) == 0: + return None + + for specified_encoding in results: + specified_encoding = specified_encoding.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if encoding_alias == specified_encoding: + return encoding_iana + if encoding_iana == specified_encoding: + return encoding_iana + + return None + + +@lru_cache(maxsize=128) +def is_multi_byte_encoding(name: str) -> bool: + """ + Verify is a specific encoding is a multi byte one based on it IANA name + """ + return name in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_le", + "utf_32_be", + "utf_7", + } or issubclass( + importlib.import_module("encodings.{}".format(name)).IncrementalDecoder, + MultibyteIncrementalDecoder, + ) + + +def identify_sig_or_bom(sequence: bytes) -> Tuple[Optional[str], bytes]: + """ + Identify and extract SIG/BOM in given sequence. + """ + + for iana_encoding in ENCODING_MARKS: + marks: Union[bytes, List[bytes]] = ENCODING_MARKS[iana_encoding] + + if isinstance(marks, bytes): + marks = [marks] + + for mark in marks: + if sequence.startswith(mark): + return iana_encoding, mark + + return None, b"" + + +def should_strip_sig_or_bom(iana_encoding: str) -> bool: + return iana_encoding not in {"utf_16", "utf_32"} + + +def iana_name(cp_name: str, strict: bool = True) -> str: + cp_name = cp_name.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if cp_name in [encoding_alias, encoding_iana]: + return encoding_iana + + if strict: + raise ValueError("Unable to retrieve IANA for '{}'".format(cp_name)) + + return cp_name + + +def range_scan(decoded_sequence: str) -> List[str]: + ranges: Set[str] = set() + + for character in decoded_sequence: + character_range: Optional[str] = unicode_range(character) + + if character_range is None: + continue + + ranges.add(character_range) + + return list(ranges) + + +def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: + if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): + return 0.0 + + decoder_a = importlib.import_module( + "encodings.{}".format(iana_name_a) + ).IncrementalDecoder + decoder_b = importlib.import_module( + "encodings.{}".format(iana_name_b) + ).IncrementalDecoder + + id_a: IncrementalDecoder = decoder_a(errors="ignore") + id_b: IncrementalDecoder = decoder_b(errors="ignore") + + character_match_count: int = 0 + + for i in range(255): + to_be_decoded: bytes = bytes([i]) + if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): + character_match_count += 1 + + return character_match_count / 254 + + +def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: + """ + Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using + the function cp_similarity. + """ + return ( + iana_name_a in IANA_SUPPORTED_SIMILAR + and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] + ) + + +def set_logging_handler( + name: str = "charset_normalizer", + level: int = logging.INFO, + format_string: str = "%(asctime)s | %(levelname)s | %(message)s", +) -> None: + logger = logging.getLogger(name) + logger.setLevel(level) + + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(format_string)) + logger.addHandler(handler) + + +def cut_sequence_chunks( + sequences: bytes, + encoding_iana: str, + offsets: range, + chunk_size: int, + bom_or_sig_available: bool, + strip_sig_or_bom: bool, + sig_payload: bytes, + is_multi_byte_decoder: bool, + decoded_payload: Optional[str] = None, +) -> Generator[str, None, None]: + if decoded_payload and is_multi_byte_decoder is False: + for i in offsets: + chunk = decoded_payload[i : i + chunk_size] + if not chunk: + break + yield chunk + else: + for i in offsets: + chunk_end = i + chunk_size + if chunk_end > len(sequences) + 8: + continue + + cut_sequence = sequences[i : i + chunk_size] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode( + encoding_iana, + errors="ignore" if is_multi_byte_decoder else "strict", + ) + + # multi-byte bad cutting detector and adjustment + # not the cleanest way to perform that fix but clever enough for now. + if is_multi_byte_decoder and i > 0: + chunk_partial_size_chk: int = min(chunk_size, 16) + + if ( + decoded_payload + and chunk[:chunk_partial_size_chk] not in decoded_payload + ): + for j in range(i, i - 4, -1): + cut_sequence = sequences[j:chunk_end] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode(encoding_iana, errors="ignore") + + if chunk[:chunk_partial_size_chk] in decoded_payload: + break + + yield chunk diff --git a/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/version.py b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/version.py new file mode 100644 index 0000000..5a4da4f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/charset_normalizer/version.py @@ -0,0 +1,6 @@ +""" +Expose version +""" + +__version__ = "3.3.2" +VERSION = __version__.split(".") diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA new file mode 100644 index 0000000..7a6bbb2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.7 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Chat: https://discord.gg/pallets diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD new file mode 100644 index 0000000..7bc97df --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD @@ -0,0 +1,39 @@ +click-8.1.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.7.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.7.dist-info/METADATA,sha256=qIMevCxGA9yEmJOM_4WHuUJCwWpsIEVbCPOhs45YPN4,3014 +click-8.1.7.dist-info/RECORD,, +click-8.1.7.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92 +click-8.1.7.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=YDDbjm406dTOA0V8bTtdGnhN7zj5j-_dFRewZF_pLvw,3138 +click/__pycache__/__init__.cpython-310.pyc,, +click/__pycache__/_compat.cpython-310.pyc,, +click/__pycache__/_termui_impl.cpython-310.pyc,, +click/__pycache__/_textwrap.cpython-310.pyc,, +click/__pycache__/_winconsole.cpython-310.pyc,, +click/__pycache__/core.cpython-310.pyc,, +click/__pycache__/decorators.cpython-310.pyc,, +click/__pycache__/exceptions.cpython-310.pyc,, +click/__pycache__/formatting.cpython-310.pyc,, +click/__pycache__/globals.cpython-310.pyc,, +click/__pycache__/parser.cpython-310.pyc,, +click/__pycache__/shell_completion.cpython-310.pyc,, +click/__pycache__/termui.cpython-310.pyc,, +click/__pycache__/testing.cpython-310.pyc,, +click/__pycache__/types.cpython-310.pyc,, +click/__pycache__/utils.cpython-310.pyc,, +click/_compat.py,sha256=5318agQpbt4kroKsbqDOYpTSWzL_YCZVUQiTT04yXmc,18744 +click/_termui_impl.py,sha256=3dFYv4445Nw-rFvZOTBMBPYwB1bxnmNk9Du6Dm_oBSU,24069 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=j6oEWtGgGna8JarD6WxhXmNnxLnfRjwXglbBc-8jr7U,114086 +click/decorators.py,sha256=-ZlbGYgV-oI8jr_oH4RpuL1PFS-5QmeuEAsLDAYgxtw,18719 +click/exceptions.py,sha256=fyROO-47HWFDjt2qupo7A3J32VlpM-ovJnfowu92K3s,9273 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=LKyYQE9ZLj5KgIDXkrcTHQRXIggfoivX14_UVIn56YA,19067 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Ty3VM_ts0sQhj6u7eFTiLwHPoTgcXTGEAUg2OpLqYKw,18460 +click/termui.py,sha256=H7Q8FpmPelhJ2ovOhfCRhjMtCpNyjFXryAMLZODqsdc,28324 +click/testing.py,sha256=1Qd4kS5bucn1hsNIRryd0WtTMuCpkA93grkWxT8POsU,16084 +click/types.py,sha256=TZvz3hKvBztf-Hpa2enOmP4eznSPLzijjig5b_0XMxE,36391 +click/utils.py,sha256=1476UduUNY6UePGU4m18uzVHLt1sKM2PP3yWsQhbItM,20298 diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL new file mode 100644 index 0000000..2c08da0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__init__.py b/.pythonlibs/lib/python3.10/site-packages/click/__init__.py new file mode 100644 index 0000000..9a1dab0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/__init__.py @@ -0,0 +1,73 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.7" diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..86cc170 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000..0536323 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc new file mode 100644 index 0000000..0d4a6d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc new file mode 100644 index 0000000..eba86dd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc new file mode 100644 index 0000000..2394ac9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000..c0dec4b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc new file mode 100644 index 0000000..14680b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..937ccc9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc new file mode 100644 index 0000000..1695cce Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc new file mode 100644 index 0000000..c170ae7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc new file mode 100644 index 0000000..1b5a2e9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc new file mode 100644 index 0000000..81dc718 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc new file mode 100644 index 0000000..857c82e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc new file mode 100644 index 0000000..0c2918c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000..b812d34 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..b5720bc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/click/_compat.py b/.pythonlibs/lib/python3.10/site-packages/click/_compat.py new file mode 100644 index 0000000..23f8866 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/_compat.py @@ -0,0 +1,623 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, "os.PathLike[str]", int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: "t.Union[str, os.PathLike[str]]", + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( # noqa: F811 + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.Optional[t.TextIO]], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.Optional[t.TextIO]]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.Optional[t.TextIO]: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/.pythonlibs/lib/python3.10/site-packages/click/_termui_impl.py b/.pythonlibs/lib/python3.10/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..f744657 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/_termui_impl.py @@ -0,0 +1,739 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter: t.Iterable[V] = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: t.Optional[int] = None + self.entered: bool = False + self.current_item: t.Optional[V] = None + self.is_hidden: bool = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar[V]": + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/.pythonlibs/lib/python3.10/site-packages/click/_textwrap.py b/.pythonlibs/lib/python3.10/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/_winconsole.py b/.pythonlibs/lib/python3.10/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/core.py b/.pythonlibs/lib/python3.10/site-packages/click/core.py new file mode 100644 index 0000000..cc65e89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/core.py @@ -0,0 +1,3042 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "t.Callable[..., V]", + *args: t.Any, + **kwargs: t.Any, + ) -> V: + ... + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "Command", + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + ... + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", "t.Callable[..., V]"], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Union[t.Any, V]: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.MutableMapping[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invocable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + :param attrs: Other command arguments described in :class:`Command`. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[ + t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] + ] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.MutableMapping[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: + ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'command(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> "Group": + ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + + See :class:`MultiCommand` and :class:`Command` for the description of + ``name`` and ``attrs``. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name: t.Optional[str] + self.opts: t.List[str] + self.secondary_opts: t.List[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page. Normally, environment variables are not + shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Union[t.Any, t.Callable[[], t.Any]] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/decorators.py b/.pythonlibs/lib/python3.10/site-packages/click/decorators.py new file mode 100644 index 0000000..d9bba95 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/decorators.py @@ -0,0 +1,561 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) + + +def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: t.Type[T], ensure: bool = False +) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": + ctx = get_current_context() + + obj: t.Optional[T] + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator # type: ignore[return-value] + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator # type: ignore[return-value] + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: + ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: t.Optional[str], + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: + ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: + ... + + +def command( + name: t.Union[t.Optional[str], _AnyCallable] = None, + cls: t.Optional[t.Type[CmdType]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast(t.Type[CmdType], Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + cmd = cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: + ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: t.Optional[str], + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: + ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: + ... + + +def group( + name: t.Union[str, _AnyCallable, None] = None, + cls: t.Optional[t.Type[GrpType]] = None, + **attrs: t.Any, +) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast(t.Type[GrpType], Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/click/exceptions.py new file mode 100644 index 0000000..fe68a36 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/exceptions.py @@ -0,0 +1,288 @@ +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: t.Optional["Command"] = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/.pythonlibs/lib/python3.10/site-packages/click/formatting.py b/.pythonlibs/lib/python3.10/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/.pythonlibs/lib/python3.10/site-packages/click/globals.py b/.pythonlibs/lib/python3.10/site-packages/click/globals.py new file mode 100644 index 0000000..480058f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/click/parser.py b/.pythonlibs/lib/python3.10/site-packages/click/parser.py new file mode 100644 index 0000000..5fa7adf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: t.Set[str] = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/py.typed b/.pythonlibs/lib/python3.10/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/click/shell_completion.py b/.pythonlibs/lib/python3.10/site-packages/click/shell_completion.py new file mode 100644 index 0000000..dc9e00b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/shell_completion.py @@ -0,0 +1,596 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: t.Optional[str] = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import subprocess + + output = subprocess.run( + ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: t.Optional[str] = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + args: t.List[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/.pythonlibs/lib/python3.10/site-packages/click/termui.py b/.pythonlibs/lib/python3.10/site-packages/click/termui.py new file mode 100644 index 0000000..db7a4b2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/termui.py @@ -0,0 +1,784 @@ +import inspect +import io +import itertools +import sys +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/.pythonlibs/lib/python3.10/site-packages/click/testing.py b/.pythonlibs/lib/python3.10/site-packages/click/testing.py new file mode 100644 index 0000000..e0df0d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env: t.Mapping[str, t.Optional[str]] = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/click/types.py b/.pythonlibs/lib/python3.10/site-packages/click/types.py new file mode 100644 index 0000000..2b1d179 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/types.py @@ -0,0 +1,1089 @@ +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats: t.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type[t.Any]] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("t.Union[str, os.PathLike[str]]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast(t.IO[t.Any], lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type[t.Any]] = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: "t.Union[str, os.PathLike[str]]" + ) -> "t.Union[str, bytes, os.PathLike[str]]": + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: "t.Union[str, os.PathLike[str]]", + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> "t.Union[str, bytes, os.PathLike[str]]": + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} '{filename}' is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: + self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/.pythonlibs/lib/python3.10/site-packages/click/utils.py b/.pythonlibs/lib/python3.10/site-packages/click/utils.py new file mode 100644 index 0000000..d536434 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/click/utils.py @@ -0,0 +1,624 @@ +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO[t.Any]] + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast(t.IO[t.Any], KeepOpenFile(f)) + + return f + + +def format_filename( + filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict". This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/LICENSE new file mode 100644 index 0000000..e06d208 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/METADATA new file mode 100644 index 0000000..9312e8e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/METADATA @@ -0,0 +1,184 @@ +Metadata-Version: 2.1 +Name: distro +Version: 1.9.0 +Summary: Distro - an OS platform information API +Home-page: https://github.com/python-distro/distro +Author: Nir Cohen +Author-email: nir36g@gmail.com +License: Apache License, Version 2.0 +Platform: All +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: BSD :: NetBSD +Classifier: Operating System :: POSIX :: BSD :: OpenBSD +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Operating System +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE + +Distro - an OS platform information API +======================================= + +[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml) +[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro) +[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg) +[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master) +[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro) +[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/) + +`distro` provides information about the +OS distribution it runs on, such as a reliable machine-readable ID, or +version information. + +It is the recommended replacement for Python's original +[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution) +function (removed in Python 3.8). It also provides much more functionality +which isn't necessarily Python bound, like a command-line interface. + +Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned. + +For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support + +## Installation + +Installation of the latest released version from PyPI: + +```shell +pip install distro +``` + +Installation of the latest development version: + +```shell +pip install https://github.com/python-distro/distro/archive/master.tar.gz +``` + +To use as a standalone script, download `distro.py` directly: + +```shell +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +python distro.py +``` + +``distro`` is safe to vendor within projects that do not wish to add +dependencies. + +```shell +cd myproject +curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py +``` + +## Usage + +```bash +$ distro +Name: Antergos Linux +Version: 2015.10 (ISO-Rolling) +Codename: ISO-Rolling + +$ distro -j +{ + "codename": "ISO-Rolling", + "id": "antergos", + "like": "arch", + "version": "16.9", + "version_parts": { + "build_number": "", + "major": "16", + "minor": "9" + } +} + + +$ python +>>> import distro +>>> distro.name(pretty=True) +'CentOS Linux 8' +>>> distro.id() +'centos' +>>> distro.version(best=True) +'8.4.2105' +``` + + +## Documentation + +On top of the aforementioned API, several more functions are available. For a complete description of the +API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/). + +## Background + +An alternative implementation became necessary because Python 3.5 deprecated +this function, and Python 3.8 removed it altogether. Its predecessor function +[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist) +was already deprecated since Python 2.6 and removed in Python 3.8. Still, there +are many cases in which access to that information is needed. See [Python issue +1322](https://bugs.python.org/issue1322) for more information. + +The `distro` package implements a robust and inclusive way of retrieving the +information about a distribution based on new standards and old methods, +namely from these data sources (from high to low precedence): + +* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed. +* The output of the `lsb_release` command, if available. +* The distro release file (`/etc/*(-|_)(release|version)`), if present. +* The `uname` command for BSD based distrubtions. + + +## Python and Distribution Support + +`distro` is supported and tested on Python 3.6+ and PyPy and on any +distribution that provides one or more of the data sources covered. + +This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros). + + +## Testing + +```shell +git clone git@github.com:python-distro/distro.git +cd distro +pip install tox +tox +``` + + +## Contributions + +Pull requests are always welcome to deal with specific distributions or just +for general merriment. + +See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info. + +Reference implementations for supporting additional distributions and file +formats can be found here: + +* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172 +* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb +* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py +* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc + +## Package manager distributions + +* https://src.fedoraproject.org/rpms/python-distro +* https://www.archlinux.org/packages/community/any/python-distro/ +* https://launchpad.net/ubuntu/+source/python-distro +* https://packages.debian.org/stable/python3-distro +* https://packages.gentoo.org/packages/dev-python/distro +* https://pkgs.org/download/python3-distro +* https://slackbuilds.org/repository/14.2/python/python-distro/ diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/RECORD new file mode 100644 index 0000000..ff08d12 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/RECORD @@ -0,0 +1,15 @@ +../../../bin/distro,sha256=AGE1nqx-Gs23UkVfySzpG-ob1UmMnEP3nt-hn4ne-ms,216 +distro-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +distro-1.9.0.dist-info/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325 +distro-1.9.0.dist-info/METADATA,sha256=MWMqst5VkRMQkbM5e9zfeXcYV52Fp1GG8Gg53QwJ6B0,6791 +distro-1.9.0.dist-info/RECORD,, +distro-1.9.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +distro-1.9.0.dist-info/entry_points.txt,sha256=3ObjqQMbh1xeQQwsWtgbfDNDMDD-EbggR1Oj_z8s9hc,46 +distro-1.9.0.dist-info/top_level.txt,sha256=ikde_V_XEdSBqaGd5tEriN_wzYHLgTX_zVtlsGLHvwQ,7 +distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981 +distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64 +distro/__pycache__/__init__.cpython-310.pyc,, +distro/__pycache__/__main__.cpython-310.pyc,, +distro/__pycache__/distro.cpython-310.pyc,, +distro/distro.py,sha256=XqbefacAhDT4zr_trnbA15eY8vdK4GTghgmvUGrEM_4,49430 +distro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/entry_points.txt new file mode 100644 index 0000000..08d29c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +distro = distro.distro:main diff --git a/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/top_level.txt new file mode 100644 index 0000000..0e09331 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro-1.9.0.dist-info/top_level.txt @@ -0,0 +1 @@ +distro diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/__init__.py b/.pythonlibs/lib/python3.10/site-packages/distro/__init__.py new file mode 100644 index 0000000..7686fe8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro/__init__.py @@ -0,0 +1,54 @@ +from .distro import ( + NORMALIZED_DISTRO_ID, + NORMALIZED_LSB_ID, + NORMALIZED_OS_ID, + LinuxDistribution, + __version__, + build_number, + codename, + distro_release_attr, + distro_release_info, + id, + info, + like, + linux_distribution, + lsb_release_attr, + lsb_release_info, + major_version, + minor_version, + name, + os_release_attr, + os_release_info, + uname_attr, + uname_info, + version, + version_parts, +) + +__all__ = [ + "NORMALIZED_DISTRO_ID", + "NORMALIZED_LSB_ID", + "NORMALIZED_OS_ID", + "LinuxDistribution", + "build_number", + "codename", + "distro_release_attr", + "distro_release_info", + "id", + "info", + "like", + "linux_distribution", + "lsb_release_attr", + "lsb_release_info", + "major_version", + "minor_version", + "name", + "os_release_attr", + "os_release_info", + "uname_attr", + "uname_info", + "version", + "version_parts", +] + +__version__ = __version__ diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/__main__.py b/.pythonlibs/lib/python3.10/site-packages/distro/__main__.py new file mode 100644 index 0000000..0c01d5b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro/__main__.py @@ -0,0 +1,4 @@ +from .distro import main + +if __name__ == "__main__": + main() diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..214af11 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__main__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000..cdbe3e5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/__main__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/distro.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/distro.cpython-310.pyc new file mode 100644 index 0000000..6dec580 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/distro/__pycache__/distro.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/distro.py b/.pythonlibs/lib/python3.10/site-packages/distro/distro.py new file mode 100644 index 0000000..78ccdfa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/distro/distro.py @@ -0,0 +1,1403 @@ +#!/usr/bin/env python +# Copyright 2015-2021 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is the recommended replacement for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.8 removed it altogether. Its +predecessor function :py:func:`platform.dist` was already deprecated since +Python 2.6 and removed in Python 3.8. Still, there are many cases in which +access to OS distribution information is needed. See `Python issue 1322 +`_ for more information. +""" + +import argparse +import json +import logging +import os +import re +import shlex +import subprocess +import sys +import warnings +from typing import ( + Any, + Callable, + Dict, + Iterable, + Optional, + Sequence, + TextIO, + Tuple, + Type, +) + +try: + from typing import TypedDict +except ImportError: + # Python 3.7 + TypedDict = dict + +__version__ = "1.9.0" + + +class VersionDict(TypedDict): + major: str + minor: str + build_number: str + + +class InfoDict(TypedDict): + id: str + version: str + version_parts: VersionDict + like: str + codename: str + + +_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") +_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") +_OS_RELEASE_BASENAME = "os-release" + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = { + "ol": "oracle", # Oracle Linux + "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap +} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 + "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 + "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation + "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server + "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + "redhat": "rhel", # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" +) + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") + +# Base file names to be looked up for if _UNIXCONFDIR is not readable. +_DISTRO_RELEASE_BASENAMES = [ + "SuSE-release", + "altlinux-release", + "arch-release", + "base-release", + "centos-release", + "fedora-release", + "gentoo-release", + "mageia-release", + "mandrake-release", + "mandriva-release", + "mandrivalinux-release", + "manjaro-release", + "oracle-release", + "redhat-release", + "rocky-release", + "sl-release", + "slackware-version", +] + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + "debian_version", + "lsb-release", + "oem-release", + _OS_RELEASE_BASENAME, + "system-release", + "plesk-release", + "iredmail-release", + "board-release", + "ec2_version", +) + + +def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]: + """ + .. deprecated:: 1.6.0 + + :func:`distro.linux_distribution()` is deprecated. It should only be + used as a compatibility shim with Python's + :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, + :func:`distro.version` and :func:`distro.name` instead. + + Return information about the current OS distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The extra item (usually in parentheses) after the + os-release version number, or the result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the OS distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular OS distributions. + """ + warnings.warn( + "distro.linux_distribution() is deprecated. It should only be used as a " + "compatibility shim with Python's platform.linux_distribution(). Please use " + "distro.id(), distro.version() and distro.name() instead.", + DeprecationWarning, + stacklevel=2, + ) + return _distro.linux_distribution(full_distribution_name) + + +def id() -> str: + """ + Return the distro ID of the current distribution, as a + machine-readable string. + + For a number of OS distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amzn" Amazon Linux + "arch" Arch Linux + "buildroot" Buildroot + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + "openbsd" OpenBSD + "netbsd" NetBSD + "freebsd" FreeBSD + "midnightbsd" MidnightBSD + "rocky" Rocky Linux + "aix" AIX + "guix" Guix System + "altlinux" ALT Linux + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the OS distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty: bool = False) -> str: + """ + Return the name of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "" field of the distro release file, appended + with the value of the pretty version ("" and "" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty: bool = False, best: bool = False) -> str: + """ + Return the version of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + Some other distributions may not provide this kind of information. In these + cases, an empty string would be returned. This behavior can be observed + with rolling releases distributions (e.g. Arch Linux). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best: bool = False) -> Tuple[str, str, str]: + """ + Return the version of the current OS distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best: bool = False) -> str: + """ + Return the major version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best: bool = False) -> str: + """ + Return the minor version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best: bool = False) -> str: + """ + Return the build number of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like() -> str: + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current OS distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + `_. + """ + return _distro.like() + + +def codename() -> str: + """ + Return the codename for the release of the current OS distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty: bool = False, best: bool = False) -> InfoDict: + """ + Return certain machine-readable information items about the current OS + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current OS distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current OS distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def uname_info() -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + """ + return _distro.uname_info() + + +def os_release_attr(attribute: str) -> str: + """ + Return a single named information item from the os-release file data source + of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute: str) -> str: + """ + Return a single named information item from the lsb_release command output + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +def uname_attr(attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + """ + return _distro.uname_attr(attribute) + + +try: + from functools import cached_property +except ImportError: + # Python < 3.8 + class cached_property: # type: ignore + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + + def __init__(self, f: Callable[[Any], Any]) -> None: + self._fname = f.__name__ + self._f = f + + def __get__(self, obj: Any, owner: Type[Any]) -> Any: + assert obj is not None, f"call {self._fname} on an instance" + ret = obj.__dict__[self._fname] = self._f(obj) + return ret + + +class LinuxDistribution: + """ + Provides information about a OS distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current OS distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__( + self, + include_lsb: Optional[bool] = None, + os_release_file: str = "", + distro_release_file: str = "", + include_uname: Optional[bool] = None, + root_dir: Optional[str] = None, + include_oslevel: Optional[bool] = None, + ) -> None: + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + * ``include_uname`` (bool): Controls whether uname command output is + included as a data source. If the uname command is not available in + the program execution path the data source for the uname command will + be empty. + + * ``root_dir`` (string): The absolute path to the root directory to use + to find distro-related information files. Note that ``include_*`` + parameters must not be enabled in combination with ``root_dir``. + + * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command + output is included as a data source. If the oslevel command is not + available in the program execution path the data source will be + empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. + This controls whether the lsb information will be loaded. + + * ``include_uname`` (bool): The result of the ``include_uname`` + parameter. This controls whether the uname information will + be loaded. + + * ``include_oslevel`` (bool): The result of the ``include_oslevel`` + parameter. This controls whether (AIX) oslevel information will be + loaded. + + * ``root_dir`` (string): The result of the ``root_dir`` parameter. + The absolute path to the root directory to use to find distro-related + information files. + + Raises: + + * :py:exc:`ValueError`: Initialization parameters combination is not + supported. + + * :py:exc:`OSError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.root_dir = root_dir + self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR + self.usr_lib_dir = ( + os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR + ) + + if os_release_file: + self.os_release_file = os_release_file + else: + etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) + usr_lib_os_release_file = os.path.join( + self.usr_lib_dir, _OS_RELEASE_BASENAME + ) + + # NOTE: The idea is to respect order **and** have it set + # at all times for API backwards compatibility. + if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( + usr_lib_os_release_file + ): + self.os_release_file = etc_dir_os_release_file + else: + self.os_release_file = usr_lib_os_release_file + + self.distro_release_file = distro_release_file or "" # updated later + + is_root_dir_defined = root_dir is not None + if is_root_dir_defined and (include_lsb or include_uname or include_oslevel): + raise ValueError( + "Including subprocess data sources from specific root_dir is disallowed" + " to prevent false information" + ) + self.include_lsb = ( + include_lsb if include_lsb is not None else not is_root_dir_defined + ) + self.include_uname = ( + include_uname if include_uname is not None else not is_root_dir_defined + ) + self.include_oslevel = ( + include_oslevel if include_oslevel is not None else not is_root_dir_defined + ) + + def __repr__(self) -> str: + """Return repr of all info""" + return ( + "LinuxDistribution(" + "os_release_file={self.os_release_file!r}, " + "distro_release_file={self.distro_release_file!r}, " + "include_lsb={self.include_lsb!r}, " + "include_uname={self.include_uname!r}, " + "include_oslevel={self.include_oslevel!r}, " + "root_dir={self.root_dir!r}, " + "_os_release_info={self._os_release_info!r}, " + "_lsb_release_info={self._lsb_release_info!r}, " + "_distro_release_info={self._distro_release_info!r}, " + "_uname_info={self._uname_info!r}, " + "_oslevel_info={self._oslevel_info!r})".format(self=self) + ) + + def linux_distribution( + self, full_distribution_name: bool = True + ) -> Tuple[str, str, str]: + """ + Return information about the OS distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self._os_release_info.get("release_codename") or self.codename(), + ) + + def id(self) -> str: + """Return the distro ID of the OS distribution, as a string. + + For details, see :func:`distro.id`. + """ + + def normalize(distro_id: str, table: Dict[str, str]) -> str: + distro_id = distro_id.lower().replace(" ", "_") + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr("distributor_id") + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + distro_id = self.uname_attr("id") + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return "" + + def name(self, pretty: bool = False) -> str: + """ + Return the name of the OS distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = ( + self.os_release_attr("name") + or self.lsb_release_attr("distributor_id") + or self.distro_release_attr("name") + or self.uname_attr("name") + ) + if pretty: + name = self.os_release_attr("pretty_name") or self.lsb_release_attr( + "description" + ) + if not name: + name = self.distro_release_attr("name") or self.uname_attr("name") + version = self.version(pretty=True) + if version: + name = f"{name} {version}" + return name or "" + + def version(self, pretty: bool = False, best: bool = False) -> str: + """ + Return the version of the OS distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr("version_id"), + self.lsb_release_attr("release"), + self.distro_release_attr("version_id"), + self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( + "version_id", "" + ), + self._parse_distro_release_content( + self.lsb_release_attr("description") + ).get("version_id", ""), + self.uname_attr("release"), + ] + if self.uname_attr("id").startswith("aix"): + # On AIX platforms, prefer oslevel command output. + versions.insert(0, self.oslevel_info()) + elif self.id() == "debian" or "debian" in self.like().split(): + # On Debian-like, add debian_version file content to candidates list. + versions.append(self._debian_version) + version = "" + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == "": + version = v + else: + for v in versions: + if v != "": + version = v + break + if pretty and version and self.codename(): + version = f"{version} ({self.codename()})" + return version + + def version_parts(self, best: bool = False) -> Tuple[str, str, str]: + """ + Return the version of the OS distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or "", build_number or "" + return "", "", "" + + def major_version(self, best: bool = False) -> str: + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best: bool = False) -> str: + """ + Return the minor version number of the current distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best: bool = False) -> str: + """ + Return the build number of the current distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self) -> str: + """ + Return the IDs of distributions that are like the OS distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr("id_like") or "" + + def codename(self) -> str: + """ + Return the codename of the OS distribution. + + For details, see :func:`distro.codename`. + """ + try: + # Handle os_release specially since distros might purposefully set + # this to empty string to have no codename + return self._os_release_info["codename"] + except KeyError: + return ( + self.lsb_release_attr("codename") + or self.distro_release_attr("codename") + or "" + ) + + def info(self, pretty: bool = False, best: bool = False) -> InfoDict: + """ + Return certain machine-readable information about the OS + distribution. + + For details, see :func:`distro.info`. + """ + return InfoDict( + id=self.id(), + version=self.version(pretty, best), + version_parts=VersionDict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best), + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the OS distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the OS + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the OS + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def uname_info(self) -> Dict[str, str]: + """ + Return a dictionary containing key-value pairs for the information + items from the uname command data source of the OS distribution. + + For details, see :func:`distro.uname_info`. + """ + return self._uname_info + + def oslevel_info(self) -> str: + """ + Return AIX' oslevel command output. + """ + return self._oslevel_info + + def os_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the os-release file data + source of the OS distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, "") + + def lsb_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the lsb_release command + output data source of the OS distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, "") + + def distro_release_attr(self, attribute: str) -> str: + """ + Return a single named information item from the distro release file + data source of the OS distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, "") + + def uname_attr(self, attribute: str) -> str: + """ + Return a single named information item from the uname command + output data source of the OS distribution. + + For details, see :func:`distro.uname_attr`. + """ + return self._uname_info.get(attribute, "") + + @cached_property + def _os_release_info(self) -> Dict[str, str]: + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file, encoding="utf-8") as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines: TextIO) -> Dict[str, str]: + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + # Ignore any tokens that are not variable assignments + if "=" in token: + k, v = token.split("=", 1) + props[k.lower()] = v + + if "version" in props: + # extract release codename (if any) from version attribute + match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"]) + if match: + release_codename = match.group(1) or match.group(2) + props["codename"] = props["release_codename"] = release_codename + + if "version_codename" in props: + # os-release added a version_codename field. Use that in + # preference to anything else Note that some distros purposefully + # do not have code names. They should be setting + # version_codename="" + props["codename"] = props["version_codename"] + elif "ubuntu_codename" in props: + # Same as above but a non-standard field name used on older Ubuntus + props["codename"] = props["ubuntu_codename"] + + return props + + @cached_property + def _lsb_release_info(self) -> Dict[str, str]: + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + if not self.include_lsb: + return {} + try: + cmd = ("lsb_release", "-a") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + # Command not found or lsb_release returned error + except (OSError, subprocess.CalledProcessError): + return {} + content = self._to_str(stdout).splitlines() + return self._parse_lsb_release_content(content) + + @staticmethod + def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]: + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + kv = line.strip("\n").split(":", 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(" ", "_").lower(): v.strip()}) + return props + + @cached_property + def _uname_info(self) -> Dict[str, str]: + if not self.include_uname: + return {} + try: + cmd = ("uname", "-rs") + stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + except OSError: + return {} + content = self._to_str(stdout).splitlines() + return self._parse_uname_content(content) + + @cached_property + def _oslevel_info(self) -> str: + if not self.include_oslevel: + return "" + try: + stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL) + except (OSError, subprocess.CalledProcessError): + return "" + return self._to_str(stdout).strip() + + @cached_property + def _debian_version(self) -> str: + try: + with open( + os.path.join(self.etc_dir, "debian_version"), encoding="ascii" + ) as fp: + return fp.readline().rstrip() + except FileNotFoundError: + return "" + + @staticmethod + def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: + if not lines: + return {} + props = {} + match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) + if match: + name, version = match.groups() + + # This is to prevent the Linux kernel version from + # appearing as the 'best' version on otherwise + # identifiable distributions. + if name == "Linux": + return {} + props["id"] = name.lower() + props["name"] = name + props["release"] = version + return props + + @staticmethod + def _to_str(bytestring: bytes) -> str: + encoding = sys.getfilesystemencoding() + return bytestring.decode(encoding) + + @cached_property + def _distro_release_info(self) -> Dict[str, str]: + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file(self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + else: + try: + basenames = [ + basename + for basename in os.listdir(self.etc_dir) + if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES + and os.path.isfile(os.path.join(self.etc_dir, basename)) + ] + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + except OSError: + # This may occur when /etc is not readable but we can't be + # sure about the *-release files. Check common entries of + # /etc for information. If they turn out to not be there the + # error is handled in `_parse_distro_release_file()`. + basenames = _DISTRO_RELEASE_BASENAMES + for basename in basenames: + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match is None: + continue + filepath = os.path.join(self.etc_dir, basename) + distro_info = self._parse_distro_release_file(filepath) + # The name is always present if the pattern matches. + if "name" not in distro_info: + continue + self.distro_release_file = filepath + break + else: # the loop didn't "break": no candidate. + return {} + + if match is not None: + distro_info["id"] = match.group(1) + + # CloudLinux < 7: manually enrich info with proper id. + if "cloudlinux" in distro_info.get("name", "").lower(): + distro_info["id"] = "cloudlinux" + + return distro_info + + def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + try: + with open(filepath, encoding="utf-8") as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + except OSError: + # Ignore not being able to read a specific, seemingly version + # related file. + # See https://github.com/python-distro/distro/issues/162 + return {} + + @staticmethod + def _parse_distro_release_content(line: str) -> Dict[str, str]: + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info["name"] = matches.group(3)[::-1] + if matches.group(2): + distro_info["version_id"] = matches.group(2)[::-1] + if matches.group(1): + distro_info["codename"] = matches.group(1)[::-1] + elif line: + distro_info["name"] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main() -> None: + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="OS distro info tool") + parser.add_argument( + "--json", "-j", help="Output in machine readable format", action="store_true" + ) + + parser.add_argument( + "--root-dir", + "-r", + type=str, + dest="root_dir", + help="Path to the root filesystem directory (defaults to /)", + ) + + args = parser.parse_args() + + if args.root_dir: + dist = LinuxDistribution( + include_lsb=False, + include_uname=False, + include_oslevel=False, + root_dir=args.root_dir, + ) + else: + dist = _distro + + if args.json: + logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) + else: + logger.info("Name: %s", dist.name(pretty=True)) + distribution_version = dist.version(pretty=True) + logger.info("Version: %s", distribution_version) + distribution_codename = dist.codename() + logger.info("Codename: %s", distribution_codename) + + +if __name__ == "__main__": + main() diff --git a/.pythonlibs/lib/python3.10/site-packages/distro/py.typed b/.pythonlibs/lib/python3.10/site-packages/distro/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/LICENSE new file mode 100644 index 0000000..50d4fa5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/LICENSE @@ -0,0 +1,73 @@ +The MIT License (MIT) + +Copyright (c) 2022 Alex Grönholm + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This project contains code copied from the Python standard library. +The following is the required license notice for those parts. + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/METADATA new file mode 100644 index 0000000..2e8819b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/METADATA @@ -0,0 +1,157 @@ +Metadata-Version: 2.1 +Name: exceptiongroup +Version: 1.2.1 +Summary: Backport of PEP 654 (exception groups) +Author-email: Alex Grönholm +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Typing :: Typed +Requires-Dist: pytest >= 6 ; extra == "test" +Project-URL: Changelog, https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst +Project-URL: Issue Tracker, https://github.com/agronholm/exceptiongroup/issues +Project-URL: Source code, https://github.com/agronholm/exceptiongroup +Provides-Extra: test + +.. image:: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml/badge.svg + :target: https://github.com/agronholm/exceptiongroup/actions/workflows/test.yml + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/exceptiongroup/badge.svg?branch=main + :target: https://coveralls.io/github/agronholm/exceptiongroup?branch=main + :alt: Code Coverage + +This is a backport of the ``BaseExceptionGroup`` and ``ExceptionGroup`` classes from +Python 3.11. + +It contains the following: + +* The ``exceptiongroup.BaseExceptionGroup`` and ``exceptiongroup.ExceptionGroup`` + classes +* A utility function (``exceptiongroup.catch()``) for catching exceptions possibly + nested in an exception group +* Patches to the ``TracebackException`` class that properly formats exception groups + (installed on import) +* An exception hook that handles formatting of exception groups through + ``TracebackException`` (installed on import) +* Special versions of some of the functions from the ``traceback`` module, modified to + correctly handle exception groups even when monkey patching is disabled, or blocked by + another custom exception hook: + + * ``traceback.format_exception()`` + * ``traceback.format_exception_only()`` + * ``traceback.print_exception()`` + * ``traceback.print_exc()`` +* A backported version of ``contextlib.suppress()`` from Python 3.12.1 which also + handles suppressing exceptions inside exception groups + +If this package is imported on Python 3.11 or later, the built-in implementations of the +exception group classes are used instead, ``TracebackException`` is not monkey patched +and the exception hook won't be installed. + +See the `standard library documentation`_ for more information on exception groups. + +.. _standard library documentation: https://docs.python.org/3/library/exceptions.html + +Catching exceptions +=================== + +Due to the lack of the ``except*`` syntax introduced by `PEP 654`_ in earlier Python +versions, you need to use ``exceptiongroup.catch()`` to catch exceptions that are +potentially nested inside an exception group. This function returns a context manager +that calls the given handler for any exceptions matching the sole argument. + +The argument to ``catch()`` must be a dict (or any ``Mapping``) where each key is either +an exception class or an iterable of exception classes. Each value must be a callable +that takes a single positional argument. The handler will be called at most once, with +an exception group as an argument which will contain all the exceptions that are any +of the given types, or their subclasses. The exception group may contain nested groups +containing more matching exceptions. + +Thus, the following Python 3.11+ code: + +.. code-block:: python + + try: + ... + except* (ValueError, KeyError) as excgroup: + for exc in excgroup.exceptions: + print('Caught exception:', type(exc)) + except* RuntimeError: + print('Caught runtime error') + +would be written with this backport like this: + +.. code-block:: python + + from exceptiongroup import BaseExceptionGroup, catch + + def value_key_err_handler(excgroup: BaseExceptionGroup) -> None: + for exc in excgroup.exceptions: + print('Caught exception:', type(exc)) + + def runtime_err_handler(exc: BaseExceptionGroup) -> None: + print('Caught runtime error') + + with catch({ + (ValueError, KeyError): value_key_err_handler, + RuntimeError: runtime_err_handler + }): + ... + +**NOTE**: Just like with ``except*``, you cannot handle ``BaseExceptionGroup`` or +``ExceptionGroup`` with ``catch()``. + +Suppressing exceptions +====================== + +This library contains a backport of the ``contextlib.suppress()`` context manager from +Python 3.12.1. It allows you to selectively ignore certain exceptions, even when they're +inside exception groups: + +.. code-block:: python + + from exceptiongroup import suppress + + with suppress(RuntimeError): + raise ExceptionGroup("", [RuntimeError("boo")]) + +Notes on monkey patching +======================== + +To make exception groups render properly when an unhandled exception group is being +printed out, this package does two things when it is imported on any Python version +earlier than 3.11: + +#. The ``traceback.TracebackException`` class is monkey patched to store extra + information about exception groups (in ``__init__()``) and properly format them (in + ``format()``) +#. An exception hook is installed at ``sys.excepthook``, provided that no other hook is + already present. This hook causes the exception to be formatted using + ``traceback.TracebackException`` rather than the built-in rendered. + +If ``sys.exceptionhook`` is found to be set to something else than the default when +``exceptiongroup`` is imported, no monkeypatching is done at all. + +To prevent the exception hook and patches from being installed, set the environment +variable ``EXCEPTIONGROUP_NO_PATCH`` to ``1``. + +Formatting exception groups +--------------------------- + +Normally, the monkey patching applied by this library on import will cause exception +groups to be printed properly in tracebacks. But in cases when the monkey patching is +blocked by a third party exception hook, or monkey patching is explicitly disabled, +you can still manually format exceptions using the special versions of the ``traceback`` +functions, like ``format_exception()``, listed at the top of this page. They work just +like their counterparts in the ``traceback`` module, except that they use a separately +patched subclass of ``TracebackException`` to perform the rendering. + +Particularly in cases where a library installs its own exception hook, it is recommended +to use these special versions to do the actual formatting of exceptions/tracebacks. + +.. _PEP 654: https://www.python.org/dev/peps/pep-0654/ + diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/RECORD new file mode 100644 index 0000000..4cc0b3e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/RECORD @@ -0,0 +1,18 @@ +exceptiongroup-1.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +exceptiongroup-1.2.1.dist-info/LICENSE,sha256=blBw12UDHgrUA6HL-Qrm0ZoCKPgC4yC3rP9GCqcu1Hw,3704 +exceptiongroup-1.2.1.dist-info/METADATA,sha256=bvH0-TM29NqYhaRMSixNkbfWAXYEXwhnoOmbkVTqVXo,6636 +exceptiongroup-1.2.1.dist-info/RECORD,, +exceptiongroup-1.2.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +exceptiongroup/__init__.py,sha256=7DHS0hDk-RIs3IQc3SbZVB0-1MhiSCJ9XgvEyEloL7M,1049 +exceptiongroup/__pycache__/__init__.cpython-310.pyc,, +exceptiongroup/__pycache__/_catch.cpython-310.pyc,, +exceptiongroup/__pycache__/_exceptions.cpython-310.pyc,, +exceptiongroup/__pycache__/_formatting.cpython-310.pyc,, +exceptiongroup/__pycache__/_suppress.cpython-310.pyc,, +exceptiongroup/__pycache__/_version.cpython-310.pyc,, +exceptiongroup/_catch.py,sha256=CaJez3E-Jkr-7B7RT3fzusdLWnuyeekooSFn7KyWt9s,4680 +exceptiongroup/_exceptions.py,sha256=h9vJO-Z8ufTr2iAb0S5mrKvkNtWZlqsWYHbNQjzC23I,11144 +exceptiongroup/_formatting.py,sha256=mumIgRk61thv7wP6WzCpVNsGPhYZGWffJeGzQngFL0s,21005 +exceptiongroup/_suppress.py,sha256=LX11PRNpchwfNWwEMY92nYN1F_5qFenQcS8EjIONXKE,1772 +exceptiongroup/_version.py,sha256=2U0Gn26fYI3Vgj5hgkLM8I3wI6YEVdffJGllaVW-sSc,411 +exceptiongroup/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup-1.2.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__init__.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__init__.py new file mode 100644 index 0000000..d8e36b2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__init__.py @@ -0,0 +1,46 @@ +__all__ = [ + "BaseExceptionGroup", + "ExceptionGroup", + "catch", + "format_exception", + "format_exception_only", + "print_exception", + "print_exc", + "suppress", +] + +import os +import sys + +from ._catch import catch +from ._version import version as __version__ # noqa: F401 + +if sys.version_info < (3, 11): + from ._exceptions import BaseExceptionGroup, ExceptionGroup + from ._formatting import ( + format_exception, + format_exception_only, + print_exc, + print_exception, + ) + + if os.getenv("EXCEPTIONGROUP_NO_PATCH") != "1": + from . import _formatting # noqa: F401 + + BaseExceptionGroup.__module__ = __name__ + ExceptionGroup.__module__ = __name__ +else: + from traceback import ( + format_exception, + format_exception_only, + print_exc, + print_exception, + ) + + BaseExceptionGroup = BaseExceptionGroup + ExceptionGroup = ExceptionGroup + +if sys.version_info < (3, 12, 1): + from ._suppress import suppress +else: + from contextlib import suppress diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..dc8eacb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_catch.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_catch.cpython-310.pyc new file mode 100644 index 0000000..77f7d23 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_catch.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_exceptions.cpython-310.pyc new file mode 100644 index 0000000..d188d34 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_formatting.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_formatting.cpython-310.pyc new file mode 100644 index 0000000..de194e2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_formatting.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_suppress.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_suppress.cpython-310.pyc new file mode 100644 index 0000000..2bad184 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_suppress.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_version.cpython-310.pyc new file mode 100644 index 0000000..c37543a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/__pycache__/_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_catch.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_catch.py new file mode 100644 index 0000000..0246568 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_catch.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +import inspect +import sys +from collections.abc import Callable, Iterable, Mapping +from contextlib import AbstractContextManager +from types import TracebackType +from typing import TYPE_CHECKING, Any + +if sys.version_info < (3, 11): + from ._exceptions import BaseExceptionGroup + +if TYPE_CHECKING: + _Handler = Callable[[BaseExceptionGroup[Any]], Any] + + +class _Catcher: + def __init__(self, handler_map: Mapping[tuple[type[BaseException], ...], _Handler]): + self._handler_map = handler_map + + def __enter__(self) -> None: + pass + + def __exit__( + self, + etype: type[BaseException] | None, + exc: BaseException | None, + tb: TracebackType | None, + ) -> bool: + if exc is not None: + unhandled = self.handle_exception(exc) + if unhandled is exc: + return False + elif unhandled is None: + return True + else: + if isinstance(exc, BaseExceptionGroup): + try: + raise unhandled from exc.__cause__ + except BaseExceptionGroup: + # Change __context__ to __cause__ because Python 3.11 does this + # too + unhandled.__context__ = exc.__cause__ + raise + + raise unhandled from exc + + return False + + def handle_exception(self, exc: BaseException) -> BaseException | None: + excgroup: BaseExceptionGroup | None + if isinstance(exc, BaseExceptionGroup): + excgroup = exc + else: + excgroup = BaseExceptionGroup("", [exc]) + + new_exceptions: list[BaseException] = [] + for exc_types, handler in self._handler_map.items(): + matched, excgroup = excgroup.split(exc_types) + if matched: + try: + try: + raise matched + except BaseExceptionGroup: + result = handler(matched) + except BaseExceptionGroup as new_exc: + if new_exc is matched: + new_exceptions.append(new_exc) + else: + new_exceptions.extend(new_exc.exceptions) + except BaseException as new_exc: + new_exceptions.append(new_exc) + else: + if inspect.iscoroutine(result): + raise TypeError( + f"Error trying to handle {matched!r} with {handler!r}. " + "Exception handler must be a sync function." + ) from exc + + if not excgroup: + break + + if new_exceptions: + if len(new_exceptions) == 1: + return new_exceptions[0] + + return BaseExceptionGroup("", new_exceptions) + elif ( + excgroup and len(excgroup.exceptions) == 1 and excgroup.exceptions[0] is exc + ): + return exc + else: + return excgroup + + +def catch( + __handlers: Mapping[type[BaseException] | Iterable[type[BaseException]], _Handler], +) -> AbstractContextManager[None]: + if not isinstance(__handlers, Mapping): + raise TypeError("the argument must be a mapping") + + handler_map: dict[ + tuple[type[BaseException], ...], Callable[[BaseExceptionGroup]] + ] = {} + for type_or_iterable, handler in __handlers.items(): + iterable: tuple[type[BaseException]] + if isinstance(type_or_iterable, type) and issubclass( + type_or_iterable, BaseException + ): + iterable = (type_or_iterable,) + elif isinstance(type_or_iterable, Iterable): + iterable = tuple(type_or_iterable) + else: + raise TypeError( + "each key must be either an exception classes or an iterable thereof" + ) + + if not callable(handler): + raise TypeError("handlers must be callable") + + for exc_type in iterable: + if not isinstance(exc_type, type) or not issubclass( + exc_type, BaseException + ): + raise TypeError( + "each key must be either an exception classes or an iterable " + "thereof" + ) + + if issubclass(exc_type, BaseExceptionGroup): + raise TypeError( + "catching ExceptionGroup with catch() is not allowed. " + "Use except instead." + ) + + handler_map[iterable] = handler + + return _Catcher(handler_map) diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_exceptions.py new file mode 100644 index 0000000..a4a7ace --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_exceptions.py @@ -0,0 +1,321 @@ +from __future__ import annotations + +from collections.abc import Callable, Sequence +from functools import partial +from inspect import getmro, isclass +from typing import TYPE_CHECKING, Generic, Type, TypeVar, cast, overload + +_BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True) +_BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException) +_ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True) +_ExceptionT = TypeVar("_ExceptionT", bound=Exception) +# using typing.Self would require a typing_extensions dependency on py<3.11 +_ExceptionGroupSelf = TypeVar("_ExceptionGroupSelf", bound="ExceptionGroup") +_BaseExceptionGroupSelf = TypeVar("_BaseExceptionGroupSelf", bound="BaseExceptionGroup") + + +def check_direct_subclass( + exc: BaseException, parents: tuple[type[BaseException]] +) -> bool: + for cls in getmro(exc.__class__)[:-1]: + if cls in parents: + return True + + return False + + +def get_condition_filter( + condition: type[_BaseExceptionT] + | tuple[type[_BaseExceptionT], ...] + | Callable[[_BaseExceptionT_co], bool], +) -> Callable[[_BaseExceptionT_co], bool]: + if isclass(condition) and issubclass( + cast(Type[BaseException], condition), BaseException + ): + return partial(check_direct_subclass, parents=(condition,)) + elif isinstance(condition, tuple): + if all(isclass(x) and issubclass(x, BaseException) for x in condition): + return partial(check_direct_subclass, parents=condition) + elif callable(condition): + return cast("Callable[[BaseException], bool]", condition) + + raise TypeError("expected a function, exception type or tuple of exception types") + + +def _derive_and_copy_attributes(self, excs): + eg = self.derive(excs) + eg.__cause__ = self.__cause__ + eg.__context__ = self.__context__ + eg.__traceback__ = self.__traceback__ + if hasattr(self, "__notes__"): + # Create a new list so that add_note() only affects one exceptiongroup + eg.__notes__ = list(self.__notes__) + return eg + + +class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): + """A combination of multiple unrelated exceptions.""" + + def __new__( + cls: type[_BaseExceptionGroupSelf], + __message: str, + __exceptions: Sequence[_BaseExceptionT_co], + ) -> _BaseExceptionGroupSelf: + if not isinstance(__message, str): + raise TypeError(f"argument 1 must be str, not {type(__message)}") + if not isinstance(__exceptions, Sequence): + raise TypeError("second argument (exceptions) must be a sequence") + if not __exceptions: + raise ValueError( + "second argument (exceptions) must be a non-empty sequence" + ) + + for i, exc in enumerate(__exceptions): + if not isinstance(exc, BaseException): + raise ValueError( + f"Item {i} of second argument (exceptions) is not an exception" + ) + + if cls is BaseExceptionGroup: + if all(isinstance(exc, Exception) for exc in __exceptions): + cls = ExceptionGroup + + if issubclass(cls, Exception): + for exc in __exceptions: + if not isinstance(exc, Exception): + if cls is ExceptionGroup: + raise TypeError( + "Cannot nest BaseExceptions in an ExceptionGroup" + ) + else: + raise TypeError( + f"Cannot nest BaseExceptions in {cls.__name__!r}" + ) + + instance = super().__new__(cls, __message, __exceptions) + instance._message = __message + instance._exceptions = __exceptions + return instance + + def add_note(self, note: str) -> None: + if not isinstance(note, str): + raise TypeError( + f"Expected a string, got note={note!r} (type {type(note).__name__})" + ) + + if not hasattr(self, "__notes__"): + self.__notes__: list[str] = [] + + self.__notes__.append(note) + + @property + def message(self) -> str: + return self._message + + @property + def exceptions( + self, + ) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]: + return tuple(self._exceptions) + + @overload + def subgroup( + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> ExceptionGroup[_ExceptionT] | None: ... + + @overload + def subgroup( + self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + ) -> BaseExceptionGroup[_BaseExceptionT] | None: ... + + @overload + def subgroup( + self, + __condition: Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool], + ) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ... + + def subgroup( + self, + __condition: type[_BaseExceptionT] + | tuple[type[_BaseExceptionT], ...] + | Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool], + ) -> BaseExceptionGroup[_BaseExceptionT] | None: + condition = get_condition_filter(__condition) + modified = False + if condition(self): + return self + + exceptions: list[BaseException] = [] + for exc in self.exceptions: + if isinstance(exc, BaseExceptionGroup): + subgroup = exc.subgroup(__condition) + if subgroup is not None: + exceptions.append(subgroup) + + if subgroup is not exc: + modified = True + elif condition(exc): + exceptions.append(exc) + else: + modified = True + + if not modified: + return self + elif exceptions: + group = _derive_and_copy_attributes(self, exceptions) + return group + else: + return None + + @overload + def split( + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> tuple[ + ExceptionGroup[_ExceptionT] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ]: ... + + @overload + def split( + self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + ) -> tuple[ + BaseExceptionGroup[_BaseExceptionT] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ]: ... + + @overload + def split( + self, + __condition: Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool], + ) -> tuple[ + BaseExceptionGroup[_BaseExceptionT_co] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ]: ... + + def split( + self, + __condition: type[_BaseExceptionT] + | tuple[type[_BaseExceptionT], ...] + | Callable[[_BaseExceptionT_co], bool], + ) -> ( + tuple[ + ExceptionGroup[_ExceptionT] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ] + | tuple[ + BaseExceptionGroup[_BaseExceptionT] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ] + | tuple[ + BaseExceptionGroup[_BaseExceptionT_co] | None, + BaseExceptionGroup[_BaseExceptionT_co] | None, + ] + ): + condition = get_condition_filter(__condition) + if condition(self): + return self, None + + matching_exceptions: list[BaseException] = [] + nonmatching_exceptions: list[BaseException] = [] + for exc in self.exceptions: + if isinstance(exc, BaseExceptionGroup): + matching, nonmatching = exc.split(condition) + if matching is not None: + matching_exceptions.append(matching) + + if nonmatching is not None: + nonmatching_exceptions.append(nonmatching) + elif condition(exc): + matching_exceptions.append(exc) + else: + nonmatching_exceptions.append(exc) + + matching_group: _BaseExceptionGroupSelf | None = None + if matching_exceptions: + matching_group = _derive_and_copy_attributes(self, matching_exceptions) + + nonmatching_group: _BaseExceptionGroupSelf | None = None + if nonmatching_exceptions: + nonmatching_group = _derive_and_copy_attributes( + self, nonmatching_exceptions + ) + + return matching_group, nonmatching_group + + @overload + def derive(self, __excs: Sequence[_ExceptionT]) -> ExceptionGroup[_ExceptionT]: ... + + @overload + def derive( + self, __excs: Sequence[_BaseExceptionT] + ) -> BaseExceptionGroup[_BaseExceptionT]: ... + + def derive( + self, __excs: Sequence[_BaseExceptionT] + ) -> BaseExceptionGroup[_BaseExceptionT]: + return BaseExceptionGroup(self.message, __excs) + + def __str__(self) -> str: + suffix = "" if len(self._exceptions) == 1 else "s" + return f"{self.message} ({len(self._exceptions)} sub-exception{suffix})" + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self._exceptions!r})" + + +class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): + def __new__( + cls: type[_ExceptionGroupSelf], + __message: str, + __exceptions: Sequence[_ExceptionT_co], + ) -> _ExceptionGroupSelf: + return super().__new__(cls, __message, __exceptions) + + if TYPE_CHECKING: + + @property + def exceptions( + self, + ) -> tuple[_ExceptionT_co | ExceptionGroup[_ExceptionT_co], ...]: ... + + @overload # type: ignore[override] + def subgroup( + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> ExceptionGroup[_ExceptionT] | None: ... + + @overload + def subgroup( + self, __condition: Callable[[_ExceptionT_co | _ExceptionGroupSelf], bool] + ) -> ExceptionGroup[_ExceptionT_co] | None: ... + + def subgroup( + self, + __condition: type[_ExceptionT] + | tuple[type[_ExceptionT], ...] + | Callable[[_ExceptionT_co], bool], + ) -> ExceptionGroup[_ExceptionT] | None: + return super().subgroup(__condition) + + @overload + def split( + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> tuple[ + ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None + ]: ... + + @overload + def split( + self, __condition: Callable[[_ExceptionT_co | _ExceptionGroupSelf], bool] + ) -> tuple[ + ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None + ]: ... + + def split( + self: _ExceptionGroupSelf, + __condition: type[_ExceptionT] + | tuple[type[_ExceptionT], ...] + | Callable[[_ExceptionT_co], bool], + ) -> tuple[ + ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None + ]: + return super().split(__condition) diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_formatting.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_formatting.py new file mode 100644 index 0000000..e3835e4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_formatting.py @@ -0,0 +1,603 @@ +# traceback_exception_init() adapted from trio +# +# _ExceptionPrintContext and traceback_exception_format() copied from the standard +# library +from __future__ import annotations + +import collections.abc +import sys +import textwrap +import traceback +from functools import singledispatch +from types import TracebackType +from typing import Any, List, Optional + +from ._exceptions import BaseExceptionGroup + +max_group_width = 15 +max_group_depth = 10 +_cause_message = ( + "\nThe above exception was the direct cause of the following exception:\n\n" +) + +_context_message = ( + "\nDuring handling of the above exception, another exception occurred:\n\n" +) + + +def _format_final_exc_line(etype, value): + valuestr = _safe_string(value, "exception") + if value is None or not valuestr: + line = f"{etype}\n" + else: + line = f"{etype}: {valuestr}\n" + + return line + + +def _safe_string(value, what, func=str): + try: + return func(value) + except BaseException: + return f"<{what} {func.__name__}() failed>" + + +class _ExceptionPrintContext: + def __init__(self): + self.seen = set() + self.exception_group_depth = 0 + self.need_close = False + + def indent(self): + return " " * (2 * self.exception_group_depth) + + def emit(self, text_gen, margin_char=None): + if margin_char is None: + margin_char = "|" + indent_str = self.indent() + if self.exception_group_depth: + indent_str += margin_char + " " + + if isinstance(text_gen, str): + yield textwrap.indent(text_gen, indent_str, lambda line: True) + else: + for text in text_gen: + yield textwrap.indent(text, indent_str, lambda line: True) + + +def exceptiongroup_excepthook( + etype: type[BaseException], value: BaseException, tb: TracebackType | None +) -> None: + sys.stderr.write("".join(traceback.format_exception(etype, value, tb))) + + +class PatchedTracebackException(traceback.TracebackException): + def __init__( + self, + exc_type: type[BaseException], + exc_value: BaseException, + exc_traceback: TracebackType | None, + *, + limit: int | None = None, + lookup_lines: bool = True, + capture_locals: bool = False, + compact: bool = False, + _seen: set[int] | None = None, + ) -> None: + kwargs: dict[str, Any] = {} + if sys.version_info >= (3, 10): + kwargs["compact"] = compact + + is_recursive_call = _seen is not None + if _seen is None: + _seen = set() + _seen.add(id(exc_value)) + + self.stack = traceback.StackSummary.extract( + traceback.walk_tb(exc_traceback), + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + ) + self.exc_type = exc_type + # Capture now to permit freeing resources: only complication is in the + # unofficial API _format_final_exc_line + self._str = _safe_string(exc_value, "exception") + try: + self.__notes__ = getattr(exc_value, "__notes__", None) + except KeyError: + # Workaround for https://github.com/python/cpython/issues/98778 on Python + # <= 3.9, and some 3.10 and 3.11 patch versions. + HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) + if sys.version_info[:2] <= (3, 11) and isinstance(exc_value, HTTPError): + self.__notes__ = None + else: + raise + + if exc_type and issubclass(exc_type, SyntaxError): + # Handle SyntaxError's specially + self.filename = exc_value.filename + lno = exc_value.lineno + self.lineno = str(lno) if lno is not None else None + self.text = exc_value.text + self.offset = exc_value.offset + self.msg = exc_value.msg + if sys.version_info >= (3, 10): + end_lno = exc_value.end_lineno + self.end_lineno = str(end_lno) if end_lno is not None else None + self.end_offset = exc_value.end_offset + elif ( + exc_type + and issubclass(exc_type, (NameError, AttributeError)) + and getattr(exc_value, "name", None) is not None + ): + suggestion = _compute_suggestion_error(exc_value, exc_traceback) + if suggestion: + self._str += f". Did you mean: '{suggestion}'?" + + if lookup_lines: + # Force all lines in the stack to be loaded + for frame in self.stack: + frame.line + + self.__suppress_context__ = ( + exc_value.__suppress_context__ if exc_value is not None else False + ) + + # Convert __cause__ and __context__ to `TracebackExceptions`s, use a + # queue to avoid recursion (only the top-level call gets _seen == None) + if not is_recursive_call: + queue = [(self, exc_value)] + while queue: + te, e = queue.pop() + + if e and e.__cause__ is not None and id(e.__cause__) not in _seen: + cause = PatchedTracebackException( + type(e.__cause__), + e.__cause__, + e.__cause__.__traceback__, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + _seen=_seen, + ) + else: + cause = None + + if compact: + need_context = ( + cause is None and e is not None and not e.__suppress_context__ + ) + else: + need_context = True + if ( + e + and e.__context__ is not None + and need_context + and id(e.__context__) not in _seen + ): + context = PatchedTracebackException( + type(e.__context__), + e.__context__, + e.__context__.__traceback__, + limit=limit, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + _seen=_seen, + ) + else: + context = None + + # Capture each of the exceptions in the ExceptionGroup along with each + # of their causes and contexts + if e and isinstance(e, BaseExceptionGroup): + exceptions = [] + for exc in e.exceptions: + texc = PatchedTracebackException( + type(exc), + exc, + exc.__traceback__, + lookup_lines=lookup_lines, + capture_locals=capture_locals, + _seen=_seen, + ) + exceptions.append(texc) + else: + exceptions = None + + te.__cause__ = cause + te.__context__ = context + te.exceptions = exceptions + if cause: + queue.append((te.__cause__, e.__cause__)) + if context: + queue.append((te.__context__, e.__context__)) + if exceptions: + queue.extend(zip(te.exceptions, e.exceptions)) + + def format(self, *, chain=True, _ctx=None): + if _ctx is None: + _ctx = _ExceptionPrintContext() + + output = [] + exc = self + if chain: + while exc: + if exc.__cause__ is not None: + chained_msg = _cause_message + chained_exc = exc.__cause__ + elif exc.__context__ is not None and not exc.__suppress_context__: + chained_msg = _context_message + chained_exc = exc.__context__ + else: + chained_msg = None + chained_exc = None + + output.append((chained_msg, exc)) + exc = chained_exc + else: + output.append((None, exc)) + + for msg, exc in reversed(output): + if msg is not None: + yield from _ctx.emit(msg) + if exc.exceptions is None: + if exc.stack: + yield from _ctx.emit("Traceback (most recent call last):\n") + yield from _ctx.emit(exc.stack.format()) + yield from _ctx.emit(exc.format_exception_only()) + elif _ctx.exception_group_depth > max_group_depth: + # exception group, but depth exceeds limit + yield from _ctx.emit(f"... (max_group_depth is {max_group_depth})\n") + else: + # format exception group + is_toplevel = _ctx.exception_group_depth == 0 + if is_toplevel: + _ctx.exception_group_depth += 1 + + if exc.stack: + yield from _ctx.emit( + "Exception Group Traceback (most recent call last):\n", + margin_char="+" if is_toplevel else None, + ) + yield from _ctx.emit(exc.stack.format()) + + yield from _ctx.emit(exc.format_exception_only()) + num_excs = len(exc.exceptions) + if num_excs <= max_group_width: + n = num_excs + else: + n = max_group_width + 1 + _ctx.need_close = False + for i in range(n): + last_exc = i == n - 1 + if last_exc: + # The closing frame may be added by a recursive call + _ctx.need_close = True + + if max_group_width is not None: + truncated = i >= max_group_width + else: + truncated = False + title = f"{i + 1}" if not truncated else "..." + yield ( + _ctx.indent() + + ("+-" if i == 0 else " ") + + f"+---------------- {title} ----------------\n" + ) + _ctx.exception_group_depth += 1 + if not truncated: + yield from exc.exceptions[i].format(chain=chain, _ctx=_ctx) + else: + remaining = num_excs - max_group_width + plural = "s" if remaining > 1 else "" + yield from _ctx.emit( + f"and {remaining} more exception{plural}\n" + ) + + if last_exc and _ctx.need_close: + yield _ctx.indent() + "+------------------------------------\n" + _ctx.need_close = False + _ctx.exception_group_depth -= 1 + + if is_toplevel: + assert _ctx.exception_group_depth == 1 + _ctx.exception_group_depth = 0 + + def format_exception_only(self): + """Format the exception part of the traceback. + The return value is a generator of strings, each ending in a newline. + Normally, the generator emits a single string; however, for + SyntaxError exceptions, it emits several lines that (when + printed) display detailed information about where the syntax + error occurred. + The message indicating which exception occurred is always the last + string in the output. + """ + if self.exc_type is None: + yield traceback._format_final_exc_line(None, self._str) + return + + stype = self.exc_type.__qualname__ + smod = self.exc_type.__module__ + if smod not in ("__main__", "builtins"): + if not isinstance(smod, str): + smod = "" + stype = smod + "." + stype + + if not issubclass(self.exc_type, SyntaxError): + yield _format_final_exc_line(stype, self._str) + elif traceback_exception_format_syntax_error is not None: + yield from traceback_exception_format_syntax_error(self, stype) + else: + yield from traceback_exception_original_format_exception_only(self) + + if isinstance(self.__notes__, collections.abc.Sequence): + for note in self.__notes__: + note = _safe_string(note, "note") + yield from [line + "\n" for line in note.split("\n")] + elif self.__notes__ is not None: + yield _safe_string(self.__notes__, "__notes__", func=repr) + + +traceback_exception_original_format = traceback.TracebackException.format +traceback_exception_original_format_exception_only = ( + traceback.TracebackException.format_exception_only +) +traceback_exception_format_syntax_error = getattr( + traceback.TracebackException, "_format_syntax_error", None +) +if sys.excepthook is sys.__excepthook__: + traceback.TracebackException.__init__ = ( # type: ignore[assignment] + PatchedTracebackException.__init__ + ) + traceback.TracebackException.format = ( # type: ignore[assignment] + PatchedTracebackException.format + ) + traceback.TracebackException.format_exception_only = ( # type: ignore[assignment] + PatchedTracebackException.format_exception_only + ) + sys.excepthook = exceptiongroup_excepthook + +# Ubuntu's system Python has a sitecustomize.py file that imports +# apport_python_hook and replaces sys.excepthook. +# +# The custom hook captures the error for crash reporting, and then calls +# sys.__excepthook__ to actually print the error. +# +# We don't mind it capturing the error for crash reporting, but we want to +# take over printing the error. So we monkeypatch the apport_python_hook +# module so that instead of calling sys.__excepthook__, it calls our custom +# hook. +# +# More details: https://github.com/python-trio/trio/issues/1065 +if getattr(sys.excepthook, "__name__", None) in ( + "apport_excepthook", + # on ubuntu 22.10 the hook was renamed to partial_apport_excepthook + "partial_apport_excepthook", +): + # patch traceback like above + traceback.TracebackException.__init__ = ( # type: ignore[assignment] + PatchedTracebackException.__init__ + ) + traceback.TracebackException.format = ( # type: ignore[assignment] + PatchedTracebackException.format + ) + traceback.TracebackException.format_exception_only = ( # type: ignore[assignment] + PatchedTracebackException.format_exception_only + ) + + from types import ModuleType + + import apport_python_hook + + assert sys.excepthook is apport_python_hook.apport_excepthook + + # monkeypatch the sys module that apport has imported + fake_sys = ModuleType("exceptiongroup_fake_sys") + fake_sys.__dict__.update(sys.__dict__) + fake_sys.__excepthook__ = exceptiongroup_excepthook + apport_python_hook.sys = fake_sys + + +@singledispatch +def format_exception_only(__exc: BaseException) -> List[str]: + return list( + PatchedTracebackException( + type(__exc), __exc, None, compact=True + ).format_exception_only() + ) + + +@format_exception_only.register +def _(__exc: type, value: BaseException) -> List[str]: + return format_exception_only(value) + + +@singledispatch +def format_exception( + __exc: BaseException, + limit: Optional[int] = None, + chain: bool = True, +) -> List[str]: + return list( + PatchedTracebackException( + type(__exc), __exc, __exc.__traceback__, limit=limit, compact=True + ).format(chain=chain) + ) + + +@format_exception.register +def _( + __exc: type, + value: BaseException, + tb: TracebackType, + limit: Optional[int] = None, + chain: bool = True, +) -> List[str]: + return format_exception(value, limit, chain) + + +@singledispatch +def print_exception( + __exc: BaseException, + limit: Optional[int] = None, + file: Any = None, + chain: bool = True, +) -> None: + if file is None: + file = sys.stderr + + for line in PatchedTracebackException( + type(__exc), __exc, __exc.__traceback__, limit=limit + ).format(chain=chain): + print(line, file=file, end="") + + +@print_exception.register +def _( + __exc: type, + value: BaseException, + tb: TracebackType, + limit: Optional[int] = None, + file: Any = None, + chain: bool = True, +) -> None: + print_exception(value, limit, file, chain) + + +def print_exc( + limit: Optional[int] = None, + file: Any | None = None, + chain: bool = True, +) -> None: + value = sys.exc_info()[1] + print_exception(value, limit, file, chain) + + +# Python levenshtein edit distance code for NameError/AttributeError +# suggestions, backported from 3.12 + +_MAX_CANDIDATE_ITEMS = 750 +_MAX_STRING_SIZE = 40 +_MOVE_COST = 2 +_CASE_COST = 1 +_SENTINEL = object() + + +def _substitution_cost(ch_a, ch_b): + if ch_a == ch_b: + return 0 + if ch_a.lower() == ch_b.lower(): + return _CASE_COST + return _MOVE_COST + + +def _compute_suggestion_error(exc_value, tb): + wrong_name = getattr(exc_value, "name", None) + if wrong_name is None or not isinstance(wrong_name, str): + return None + if isinstance(exc_value, AttributeError): + obj = getattr(exc_value, "obj", _SENTINEL) + if obj is _SENTINEL: + return None + obj = exc_value.obj + try: + d = dir(obj) + except Exception: + return None + else: + assert isinstance(exc_value, NameError) + # find most recent frame + if tb is None: + return None + while tb.tb_next is not None: + tb = tb.tb_next + frame = tb.tb_frame + + d = list(frame.f_locals) + list(frame.f_globals) + list(frame.f_builtins) + if len(d) > _MAX_CANDIDATE_ITEMS: + return None + wrong_name_len = len(wrong_name) + if wrong_name_len > _MAX_STRING_SIZE: + return None + best_distance = wrong_name_len + suggestion = None + for possible_name in d: + if possible_name == wrong_name: + # A missing attribute is "found". Don't suggest it (see GH-88821). + continue + # No more than 1/3 of the involved characters should need changed. + max_distance = (len(possible_name) + wrong_name_len + 3) * _MOVE_COST // 6 + # Don't take matches we've already beaten. + max_distance = min(max_distance, best_distance - 1) + current_distance = _levenshtein_distance( + wrong_name, possible_name, max_distance + ) + if current_distance > max_distance: + continue + if not suggestion or current_distance < best_distance: + suggestion = possible_name + best_distance = current_distance + return suggestion + + +def _levenshtein_distance(a, b, max_cost): + # A Python implementation of Python/suggestions.c:levenshtein_distance. + + # Both strings are the same + if a == b: + return 0 + + # Trim away common affixes + pre = 0 + while a[pre:] and b[pre:] and a[pre] == b[pre]: + pre += 1 + a = a[pre:] + b = b[pre:] + post = 0 + while a[: post or None] and b[: post or None] and a[post - 1] == b[post - 1]: + post -= 1 + a = a[: post or None] + b = b[: post or None] + if not a or not b: + return _MOVE_COST * (len(a) + len(b)) + if len(a) > _MAX_STRING_SIZE or len(b) > _MAX_STRING_SIZE: + return max_cost + 1 + + # Prefer shorter buffer + if len(b) < len(a): + a, b = b, a + + # Quick fail when a match is impossible + if (len(b) - len(a)) * _MOVE_COST > max_cost: + return max_cost + 1 + + # Instead of producing the whole traditional len(a)-by-len(b) + # matrix, we can update just one row in place. + # Initialize the buffer row + row = list(range(_MOVE_COST, _MOVE_COST * (len(a) + 1), _MOVE_COST)) + + result = 0 + for bindex in range(len(b)): + bchar = b[bindex] + distance = result = bindex * _MOVE_COST + minimum = sys.maxsize + for index in range(len(a)): + # 1) Previous distance in this row is cost(b[:b_index], a[:index]) + substitute = distance + _substitution_cost(bchar, a[index]) + # 2) cost(b[:b_index], a[:index+1]) from previous row + distance = row[index] + # 3) existing result is cost(b[:b_index+1], a[index]) + + insert_delete = min(result, distance) + _MOVE_COST + result = min(insert_delete, substitute) + + # cost(b[:b_index+1], a[:index+1]) + row[index] = result + if result < minimum: + minimum = result + if minimum > max_cost: + # Everything in this row is too big, so bail early. + return max_cost + 1 + return result diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_suppress.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_suppress.py new file mode 100644 index 0000000..11467ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_suppress.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import sys +from contextlib import AbstractContextManager +from types import TracebackType +from typing import TYPE_CHECKING, Optional, Type, cast + +if sys.version_info < (3, 11): + from ._exceptions import BaseExceptionGroup + +if TYPE_CHECKING: + # requires python 3.9 + BaseClass = AbstractContextManager[None] +else: + BaseClass = AbstractContextManager + + +class suppress(BaseClass): + """Backport of :class:`contextlib.suppress` from Python 3.12.1.""" + + def __init__(self, *exceptions: type[BaseException]): + self._exceptions = exceptions + + def __enter__(self) -> None: + pass + + def __exit__( + self, + exctype: Optional[Type[BaseException]], + excinst: Optional[BaseException], + exctb: Optional[TracebackType], + ) -> bool: + # Unlike isinstance and issubclass, CPython exception handling + # currently only looks at the concrete type hierarchy (ignoring + # the instance and subclass checking hooks). While Guido considers + # that a bug rather than a feature, it's a fairly hard one to fix + # due to various internal implementation details. suppress provides + # the simpler issubclass based semantics, rather than trying to + # exactly reproduce the limitations of the CPython interpreter. + # + # See http://bugs.python.org/issue12029 for more details + if exctype is None: + return False + + if issubclass(exctype, self._exceptions): + return True + + if issubclass(exctype, BaseExceptionGroup): + match, rest = cast(BaseExceptionGroup, excinst).split(self._exceptions) + if rest is None: + return True + + raise rest + + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_version.py b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_version.py new file mode 100644 index 0000000..9e1bb0b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/_version.py @@ -0,0 +1,16 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = '1.2.1' +__version_tuple__ = version_tuple = (1, 2, 1) diff --git a/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/py.typed b/.pythonlibs/lib/python3.10/site-packages/exceptiongroup/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/LICENSE new file mode 100644 index 0000000..7082a2d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013-2019 Nikolay Kim and Andrew Svetlov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/METADATA new file mode 100644 index 0000000..60603d5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/METADATA @@ -0,0 +1,420 @@ +Metadata-Version: 2.1 +Name: frozenlist +Version: 1.4.1 +Summary: A list-like structure which implements collections.abc.MutableSequence +Home-page: https://github.com/aio-libs/frozenlist +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache 2 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: Github Actions, https://github.com/aio-libs/frozenlist/actions +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/frozenlist +Project-URL: Docs: Changelog, https://github.com/aio-libs/frozenlist/blob/master/CHANGES.rst#changelog +Project-URL: Docs: RTD, https://frozenlist.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/frozenlist/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/frozenlist +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE + +frozenlist +========== + +.. image:: https://github.com/aio-libs/frozenlist/workflows/CI/badge.svg + :target: https://github.com/aio-libs/frozenlist/actions + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/frozenlist/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/frozenlist + :alt: codecov.io status for master branch + +.. image:: https://img.shields.io/pypi/v/frozenlist.svg?logo=Python&logoColor=white + :target: https://pypi.org/project/frozenlist + :alt: frozenlist @ PyPI + +.. image:: https://readthedocs.org/projects/frozenlist/badge/?version=latest + :target: https://frozenlist.aio-libs.org + :alt: Read The Docs build status badge + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +``frozenlist.FrozenList`` is a list-like structure which implements +``collections.abc.MutableSequence``. The list is *mutable* until ``FrozenList.freeze`` +is called, after which list modifications raise ``RuntimeError``: + + +>>> from frozenlist import FrozenList +>>> fl = FrozenList([17, 42]) +>>> fl.append('spam') +>>> fl.append('Vikings') +>>> fl + +>>> fl.freeze() +>>> fl + +>>> fl.frozen +True +>>> fl.append("Monty") +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 97, in frozenlist._frozenlist.FrozenList.append + self._check_frozen() + File "frozenlist/_frozenlist.pyx", line 19, in frozenlist._frozenlist.FrozenList._check_frozen + raise RuntimeError("Cannot modify frozen list.") +RuntimeError: Cannot modify frozen list. + + +FrozenList is also hashable, but only when frozen. Otherwise it also throws a RuntimeError: + + +>>> fl = FrozenList([17, 42, 'spam']) +>>> hash(fl) +Traceback (most recent call last): + File "", line 1, in + File "frozenlist/_frozenlist.pyx", line 111, in frozenlist._frozenlist.FrozenList.__hash__ + raise RuntimeError("Cannot hash unfrozen list.") +RuntimeError: Cannot hash unfrozen list. +>>> fl.freeze() +>>> hash(fl) +3713081631934410656 +>>> dictionary = {fl: 'Vikings'} # frozen fl can be a dict key +>>> dictionary +{: 'Vikings'} + + +Installation +------------ + +:: + + $ pip install frozenlist + +The library requires Python 3.8 or newer. + + +Documentation +------------- + +https://frozenlist.aio-libs.org + +Communication channels +---------------------- + +We have a *Matrix Space* `#aio-libs-space:matrix.org +`_ which is +also accessible via Gitter. + +Requirements +------------ + +- Python >= 3.8 + +License +------- + +``frozenlist`` is offered under the Apache 2 license. + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue in the `bug tracker +`_ if you have found a bug +or have some suggestions to improve the library. + +.. _GitHub: https://github.com/aio-libs/frozenlist + +========= +Changelog +========= + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/contributing/#news-entries + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +1.4.1 (2023-12-15) +================== + +Packaging updates and notes for downstreams +------------------------------------------- + +- Declared Python 3.12 and PyPy 3.8-3.10 supported officially + in the distribution package metadata. + + + *Related issues and pull requests on GitHub:* + `#553 `__. + +- Replaced the packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``frozenlist`` + from source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python3 -m pip install . --config-settings=pure-python= + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python3 -m build --config-setting=pure-python= + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + + +Contributor-facing changes +-------------------------- + +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python3 -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. + + The following produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files: + + .. code-block:: console + + $ python -Im pip install -e . + + Alternatively, the ``FROZENLIST_CYTHON_TRACING=1`` environment variable + can be set to do the same as the `PEP 517 `__ config setting. + + + *Related issues and pull requests on GitHub:* + `#560 `__. + +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + + *Related issues and pull requests on GitHub:* + `#561 `__. + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *frozenlist* -- by `@webknjaz `__. + + This is primarily targeting the maintainers. + + + *Related issues and pull requests on GitHub:* + `#563 `__. + +- Detailed ``Contributing Guidelines`` on + authoring the changelog fragments have been published in the + documentation -- by `@webknjaz `__. + + + *Related issues and pull requests on GitHub:* + `#564 `__. + + +---- + + +1.4.0 (2023-07-12) +================== + +The published source distribution package became buildable +under Python 3.12. + + +---- + + +Bugfixes +-------- + +- Removed an unused ``typing.Tuple`` import + `#411 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.7 support. + `#413 `_ + + +Misc +---- + +- `#410 `_, `#433 `_ + + +---- + + +1.3.3 (2022-11-08) +================== + +- Fixed CI runs when creating a new release, where new towncrier versions + fail when the current version section is already present. + + +---- + + +1.3.2 (2022-11-08) +================== + +Misc +---- + +- Updated the CI runs to better check for test results and to avoid deprecated syntax. `#327 `_ + + +---- + + +1.3.1 (2022-08-02) +================== + +The published source distribution package became buildable +under Python 3.11. + + +---- + + +1.3.0 (2022-01-18) +================== + +Bugfixes +-------- + +- Do not install C sources with binary distributions. + `#250 `_ + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support + `#274 `_ + + +---- + + +1.2.0 (2021-10-16) +================== + +Features +-------- + +- ``FrozenList`` now supports being used as a generic type as per PEP 585, e.g. ``frozen_int_list: FrozenList[int]`` (requires Python 3.9 or newer). + `#172 `_ +- Added support for Python 3.10. + `#227 `_ +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. + `#227 `_ +- Started shipping platform-specific arm64 wheels for Apple Silicon. + `#227 `_ + + +---- + + +1.1.1 (2020-11-14) +================== + +Bugfixes +-------- + +- Provide x86 Windows wheels. + `#169 `_ + + +---- + + +1.1.0 (2020-10-13) +================== + +Features +-------- + +- Add support for hashing of a frozen list. + `#136 `_ + +- Support Python 3.8 and 3.9. + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + + +---- + + +1.0.0 (2019-11-09) +================== + +Deprecations and Removals +------------------------- + +- Dropped support for Python 3.5; only 3.6, 3.7 and 3.8 are supported going forward. + `#24 `_ diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/RECORD new file mode 100644 index 0000000..3b9de97 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/RECORD @@ -0,0 +1,12 @@ +frozenlist-1.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +frozenlist-1.4.1.dist-info/LICENSE,sha256=b9UkPpLdf5jsacesN3co50kFcJ_1J6W_mNbQJjwE9bY,11332 +frozenlist-1.4.1.dist-info/METADATA,sha256=i-BTxwxpJrHm-I8DMpFRvXVXJiqXwUcsPiUR47NY6eg,12136 +frozenlist-1.4.1.dist-info/RECORD,, +frozenlist-1.4.1.dist-info/WHEEL,sha256=vV-3gZY0A60KvZ7BiFVUnyR1ohNO_OSjBIJIoRfk_rE,225 +frozenlist-1.4.1.dist-info/top_level.txt,sha256=jivtxsPXA3nK3WBWW2LW5Mtu_GHt8UZA13NeCs2cKuA,11 +frozenlist/__init__.py,sha256=hrSQhfujMaz1BWlHfwuVFrPD02-pCK7jG4e9FaCZCmM,2316 +frozenlist/__init__.pyi,sha256=vMEoES1xGegPtVXoCi9XydEeHsyuIq-KdeXwP5PdsaA,1470 +frozenlist/__pycache__/__init__.cpython-310.pyc,, +frozenlist/_frozenlist.cpython-310-x86_64-linux-gnu.so,sha256=NlvcEP-slIw1H6qbBejxVzEOxKG33BnttUAdSoP1wA4,766040 +frozenlist/_frozenlist.pyx,sha256=9V4Z1En6TZwgFD26d-sjxyhUzUm338H1Qiz4-i5ukv0,2983 +frozenlist/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/WHEEL new file mode 100644 index 0000000..09a4f39 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/WHEEL @@ -0,0 +1,8 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_5_x86_64 +Tag: cp310-cp310-manylinux1_x86_64 +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/top_level.txt new file mode 100644 index 0000000..52f13fc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist-1.4.1.dist-info/top_level.txt @@ -0,0 +1 @@ +frozenlist diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.py b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.py new file mode 100644 index 0000000..c0f7136 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.py @@ -0,0 +1,95 @@ +import os +import sys +import types +from collections.abc import MutableSequence +from functools import total_ordering +from typing import Type + +__version__ = "1.4.1" + +__all__ = ("FrozenList", "PyFrozenList") # type: Tuple[str, ...] + + +NO_EXTENSIONS = bool(os.environ.get("FROZENLIST_NO_EXTENSIONS")) # type: bool + + +@total_ordering +class FrozenList(MutableSequence): + __slots__ = ("_frozen", "_items") + + if sys.version_info >= (3, 9): + __class_getitem__ = classmethod(types.GenericAlias) + else: + + @classmethod + def __class_getitem__(cls: Type["FrozenList"]) -> Type["FrozenList"]: + return cls + + def __init__(self, items=None): + self._frozen = False + if items is not None: + items = list(items) + else: + items = [] + self._items = items + + @property + def frozen(self): + return self._frozen + + def freeze(self): + self._frozen = True + + def __getitem__(self, index): + return self._items[index] + + def __setitem__(self, index, value): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + self._items[index] = value + + def __delitem__(self, index): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + del self._items[index] + + def __len__(self): + return self._items.__len__() + + def __iter__(self): + return self._items.__iter__() + + def __reversed__(self): + return self._items.__reversed__() + + def __eq__(self, other): + return list(self) == other + + def __le__(self, other): + return list(self) <= other + + def insert(self, pos, item): + if self._frozen: + raise RuntimeError("Cannot modify frozen list.") + self._items.insert(pos, item) + + def __repr__(self): + return f"" + + def __hash__(self): + if self._frozen: + return hash(tuple(self)) + else: + raise RuntimeError("Cannot hash unfrozen list.") + + +PyFrozenList = FrozenList + + +if not NO_EXTENSIONS: + try: + from ._frozenlist import FrozenList as CFrozenList # type: ignore + except ImportError: # pragma: no cover + pass + else: + FrozenList = CFrozenList # type: ignore diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.pyi new file mode 100644 index 0000000..ae803ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__init__.pyi @@ -0,0 +1,47 @@ +from typing import ( + Generic, + Iterable, + Iterator, + List, + MutableSequence, + Optional, + TypeVar, + Union, + overload, +) + +_T = TypeVar("_T") +_Arg = Union[List[_T], Iterable[_T]] + +class FrozenList(MutableSequence[_T], Generic[_T]): + def __init__(self, items: Optional[_Arg[_T]] = None) -> None: ... + @property + def frozen(self) -> bool: ... + def freeze(self) -> None: ... + @overload + def __getitem__(self, i: int) -> _T: ... + @overload + def __getitem__(self, s: slice) -> FrozenList[_T]: ... + @overload + def __setitem__(self, i: int, o: _T) -> None: ... + @overload + def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ... + @overload + def __delitem__(self, i: int) -> None: ... + @overload + def __delitem__(self, i: slice) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __reversed__(self) -> Iterator[_T]: ... + def __eq__(self, other: object) -> bool: ... + def __le__(self, other: FrozenList[_T]) -> bool: ... + def __ne__(self, other: object) -> bool: ... + def __lt__(self, other: FrozenList[_T]) -> bool: ... + def __ge__(self, other: FrozenList[_T]) -> bool: ... + def __gt__(self, other: FrozenList[_T]) -> bool: ... + def insert(self, pos: int, item: _T) -> None: ... + def __repr__(self) -> str: ... + def __hash__(self) -> int: ... + +# types for C accelerators are the same +CFrozenList = PyFrozenList = FrozenList diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5511cd7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/frozenlist/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..cf57bc3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.pyx b/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.pyx new file mode 100644 index 0000000..9ee846c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist/_frozenlist.pyx @@ -0,0 +1,123 @@ +import sys +import types +from collections.abc import MutableSequence + + +cdef class FrozenList: + + if sys.version_info >= (3, 9): + __class_getitem__ = classmethod(types.GenericAlias) + else: + @classmethod + def __class_getitem__(cls): + return cls + + cdef readonly bint frozen + cdef list _items + + def __init__(self, items=None): + self.frozen = False + if items is not None: + items = list(items) + else: + items = [] + self._items = items + + cdef object _check_frozen(self): + if self.frozen: + raise RuntimeError("Cannot modify frozen list.") + + cdef inline object _fast_len(self): + return len(self._items) + + def freeze(self): + self.frozen = True + + def __getitem__(self, index): + return self._items[index] + + def __setitem__(self, index, value): + self._check_frozen() + self._items[index] = value + + def __delitem__(self, index): + self._check_frozen() + del self._items[index] + + def __len__(self): + return self._fast_len() + + def __iter__(self): + return self._items.__iter__() + + def __reversed__(self): + return self._items.__reversed__() + + def __richcmp__(self, other, op): + if op == 0: # < + return list(self) < other + if op == 1: # <= + return list(self) <= other + if op == 2: # == + return list(self) == other + if op == 3: # != + return list(self) != other + if op == 4: # > + return list(self) > other + if op == 5: # => + return list(self) >= other + + def insert(self, pos, item): + self._check_frozen() + self._items.insert(pos, item) + + def __contains__(self, item): + return item in self._items + + def __iadd__(self, items): + self._check_frozen() + self._items += list(items) + return self + + def index(self, item): + return self._items.index(item) + + def remove(self, item): + self._check_frozen() + self._items.remove(item) + + def clear(self): + self._check_frozen() + self._items.clear() + + def extend(self, items): + self._check_frozen() + self._items += list(items) + + def reverse(self): + self._check_frozen() + self._items.reverse() + + def pop(self, index=-1): + self._check_frozen() + return self._items.pop(index) + + def append(self, item): + self._check_frozen() + return self._items.append(item) + + def count(self, item): + return self._items.count(item) + + def __repr__(self): + return ''.format(self.frozen, + self._items) + + def __hash__(self): + if self.frozen: + return hash(tuple(self._items)) + else: + raise RuntimeError("Cannot hash unfrozen list.") + + +MutableSequence.register(FrozenList) diff --git a/.pythonlibs/lib/python3.10/site-packages/frozenlist/py.typed b/.pythonlibs/lib/python3.10/site-packages/frozenlist/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/frozenlist/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..8f080ea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nathaniel J. Smith and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/METADATA new file mode 100644 index 0000000..cf12a82 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/METADATA @@ -0,0 +1,193 @@ +Metadata-Version: 2.1 +Name: h11 +Version: 0.14.0 +Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 +Home-page: https://github.com/python-hyper/h11 +Author: Nathaniel J. Smith +Author-email: njs@pobox.com +License: MIT +Classifier: Development Status :: 3 - Alpha +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.7 +License-File: LICENSE.txt +Requires-Dist: typing-extensions ; python_version < "3.8" + +h11 +=== + +.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master + :target: https://travis-ci.org/python-hyper/h11 + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-hyper/h11 + :alt: Test coverage + +.. image:: https://readthedocs.org/projects/h11/badge/?version=latest + :target: http://h11.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +This is a little HTTP/1.1 library written from scratch in Python, +heavily inspired by `hyper-h2 `_. + +It's a "bring-your-own-I/O" library; h11 contains no IO code +whatsoever. This means you can hook h11 up to your favorite network +API, and that could be anything you want: synchronous, threaded, +asynchronous, or your own implementation of `RFC 6214 +`_ -- h11 won't judge you. +(Compare this to the current state of the art, where every time a `new +network API `_ comes along then someone +gets to start over reimplementing the entire HTTP protocol from +scratch.) Cory Benfield made an `excellent blog post describing the +benefits of this approach +`_, or if you like video +then here's his `PyCon 2016 talk on the same theme +`_. + +This also means that h11 is not immediately useful out of the box: +it's a toolkit for building programs that speak HTTP, not something +that could directly replace ``requests`` or ``twisted.web`` or +whatever. But h11 makes it much easier to implement something like +``requests`` or ``twisted.web``. + +At a high level, working with h11 goes like this: + +1) First, create an ``h11.Connection`` object to track the state of a + single HTTP/1.1 connection. + +2) When you read data off the network, pass it to + ``conn.receive_data(...)``; you'll get back a list of objects + representing high-level HTTP "events". + +3) When you want to send a high-level HTTP event, create the + corresponding "event" object and pass it to ``conn.send(...)``; + this will give you back some bytes that you can then push out + through the network. + +For example, a client might instantiate and then send a +``h11.Request`` object, then zero or more ``h11.Data`` objects for the +request body (e.g., if this is a POST), and then a +``h11.EndOfMessage`` to indicate the end of the message. Then the +server would then send back a ``h11.Response``, some ``h11.Data``, and +its own ``h11.EndOfMessage``. If either side violates the protocol, +you'll get a ``h11.ProtocolError`` exception. + +h11 is suitable for implementing both servers and clients, and has a +pleasantly symmetric API: the events you send as a client are exactly +the ones that you receive as a server and vice-versa. + +`Here's an example of a tiny HTTP client +`_ + +It also has `a fine manual `_. + +FAQ +--- + +*Whyyyyy?* + +I wanted to play with HTTP in `Curio +`__ and `Trio +`__, which at the time didn't have any +HTTP libraries. So I thought, no big deal, Python has, like, a dozen +different implementations of HTTP, surely I can find one that's +reusable. I didn't find one, but I did find Cory's call-to-arms +blog-post. So I figured, well, fine, if I have to implement HTTP from +scratch, at least I can make sure no-one *else* has to ever again. + +*Should I use it?* + +Maybe. You should be aware that it's a very young project. But, it's +feature complete and has an exhaustive test-suite and complete docs, +so the next step is for people to try using it and see how it goes +:-). If you do then please let us know -- if nothing else we'll want +to talk to you before making any incompatible changes! + +*What are the features/limitations?* + +Roughly speaking, it's trying to be a robust, complete, and non-hacky +implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: +HTTP/1.1 Message Syntax and Routing +`_. That is, it mostly focuses on +implementing HTTP at the level of taking bytes on and off the wire, +and the headers related to that, and tries to be anal about spec +conformance. It doesn't know about higher-level concerns like URL +routing, conditional GETs, cross-origin cookie policies, or content +negotiation. But it does know how to take care of framing, +cross-version differences in keep-alive handling, and the "obsolete +line folding" rule, so you can focus your energies on the hard / +interesting parts for your application, and it tries to support the +full specification in the sense that any useful HTTP/1.1 conformant +application should be able to use h11. + +It's pure Python, and has no dependencies outside of the standard +library. + +It has a test suite with 100.0% coverage for both statements and +branches. + +Currently it supports Python 3 (testing on 3.7-3.10) and PyPy 3. +The last Python 2-compatible version was h11 0.11.x. +(Originally it had a Cython wrapper for `http-parser +`_ and a beautiful nested state +machine implemented with ``yield from`` to postprocess the output. But +I had to take these out -- the new *parser* needs fewer lines-of-code +than the old *parser wrapper*, is written in pure Python, uses no +exotic language syntax, and has more features. It's sad, really; that +old state machine was really slick. I just need a few sentences here +to mourn that.) + +I don't know how fast it is. I haven't benchmarked or profiled it yet, +so it's probably got a few pointless hot spots, and I've been trying +to err on the side of simplicity and robustness instead of +micro-optimization. But at the architectural level I tried hard to +avoid fundamentally bad decisions, e.g., I believe that all the +parsing algorithms remain linear-time even in the face of pathological +input like slowloris, and there are no byte-by-byte loops. (I also +believe that it maintains bounded memory usage in the face of +arbitrary/pathological input.) + +The whole library is ~800 lines-of-code. You can read and understand +the whole thing in less than an hour. Most of the energy invested in +this so far has been spent on trying to keep things simple by +minimizing special-cases and ad hoc state manipulation; even though it +is now quite small and simple, I'm still annoyed that I haven't +figured out how to make it even smaller and simpler. (Unfortunately, +HTTP does not lend itself to simplicity.) + +The API is ~feature complete and I don't expect the general outlines +to change much, but you can't judge an API's ergonomics until you +actually document and use it, so I'd expect some changes in the +details. + +*How do I try it?* + +.. code-block:: sh + + $ pip install h11 + $ git clone git@github.com:python-hyper/h11 + $ cd h11/examples + $ python basic-client.py + +and go from there. + +*License?* + +MIT + +*Code of conduct?* + +Contributors are requested to follow our `code of conduct +`_ in +all project spaces. diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/RECORD new file mode 100644 index 0000000..eeaa1c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/RECORD @@ -0,0 +1,52 @@ +h11-0.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +h11-0.14.0.dist-info/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 +h11-0.14.0.dist-info/METADATA,sha256=B7pZ0m7WBXNs17vl6hUH9bJTL9s37DaGvY31w7jNxSg,8175 +h11-0.14.0.dist-info/RECORD,, +h11-0.14.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +h11-0.14.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 +h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 +h11/__pycache__/__init__.cpython-310.pyc,, +h11/__pycache__/_abnf.cpython-310.pyc,, +h11/__pycache__/_connection.cpython-310.pyc,, +h11/__pycache__/_events.cpython-310.pyc,, +h11/__pycache__/_headers.cpython-310.pyc,, +h11/__pycache__/_readers.cpython-310.pyc,, +h11/__pycache__/_receivebuffer.cpython-310.pyc,, +h11/__pycache__/_state.cpython-310.pyc,, +h11/__pycache__/_util.cpython-310.pyc,, +h11/__pycache__/_version.cpython-310.pyc,, +h11/__pycache__/_writers.cpython-310.pyc,, +h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 +h11/_connection.py,sha256=eS2sorMD0zKLCFiB9lW9W9F_Nzny2tjHa4e6s1ujr1c,26539 +h11/_events.py,sha256=LEfuvg1AbhHaVRwxCd0I-pFn9-ezUOaoL8o2Kvy1PBA,11816 +h11/_headers.py,sha256=RqB8cd8CN0blYPzcLe5qeCh-phv6D1U_CHj4hs67lgQ,10230 +h11/_readers.py,sha256=EbSed0jzwVUiD1nOPAeUcVE4Flf3wXkxfb8c06-OTBM,8383 +h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 +h11/_state.py,sha256=k1VL6SDbaPkSrZ-49ewCXDpuiUS69_46YhbWjuV1qEY,13300 +h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 +h11/_version.py,sha256=LVyTdiZRzIIEv79UyOgbM5iUrJUllEzlCWaJEYBY1zc,686 +h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 +h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 +h11/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +h11/tests/__pycache__/__init__.cpython-310.pyc,, +h11/tests/__pycache__/helpers.cpython-310.pyc,, +h11/tests/__pycache__/test_against_stdlib_http.cpython-310.pyc,, +h11/tests/__pycache__/test_connection.cpython-310.pyc,, +h11/tests/__pycache__/test_events.cpython-310.pyc,, +h11/tests/__pycache__/test_headers.cpython-310.pyc,, +h11/tests/__pycache__/test_helpers.cpython-310.pyc,, +h11/tests/__pycache__/test_io.cpython-310.pyc,, +h11/tests/__pycache__/test_receivebuffer.cpython-310.pyc,, +h11/tests/__pycache__/test_state.cpython-310.pyc,, +h11/tests/__pycache__/test_util.cpython-310.pyc,, +h11/tests/data/test-file,sha256=ZJ03Rqs98oJw29OHzJg7LlMzyGQaRAY0r3AqBeM2wVU,65 +h11/tests/helpers.py,sha256=a1EVG_p7xU4wRsa3tMPTRxuaKCmretok9sxXWvqfmQA,3355 +h11/tests/test_against_stdlib_http.py,sha256=cojCHgHXFQ8gWhNlEEwl3trmOpN-5uDukRoHnElqo3A,3995 +h11/tests/test_connection.py,sha256=ZbPLDPclKvjgjAhgk-WlCPBaf17c4XUIV2tpaW08jOI,38720 +h11/tests/test_events.py,sha256=LPVLbcV-NvPNK9fW3rraR6Bdpz1hAlsWubMtNaJ5gHg,4657 +h11/tests/test_headers.py,sha256=qd8T1Zenuz5GbD6wklSJ5G8VS7trrYgMV0jT-SMvqg8,5612 +h11/tests/test_helpers.py,sha256=kAo0CEM4LGqmyyP2ZFmhsyq3UFJqoFfAbzu3hbWreRM,794 +h11/tests/test_io.py,sha256=uCZVnjarkRBkudfC1ij-KSCQ71XWJhnkgkgWWkKgYPQ,16386 +h11/tests/test_receivebuffer.py,sha256=3jGbeJM36Akqg_pAhPb7XzIn2NS6RhPg-Ryg8Eu6ytk,3454 +h11/tests/test_state.py,sha256=rqll9WqFsJPE0zSrtCn9LH659mPKsDeXZ-DwXwleuBQ,8928 +h11/tests/test_util.py,sha256=VO5L4nSFe4pgtSwKuv6u_6l0H7UeizF5WKuHTWreg70,2970 diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/top_level.txt new file mode 100644 index 0000000..0d24def --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11-0.14.0.dist-info/top_level.txt @@ -0,0 +1 @@ +h11 diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__init__.py b/.pythonlibs/lib/python3.10/site-packages/h11/__init__.py new file mode 100644 index 0000000..989e92c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/__init__.py @@ -0,0 +1,62 @@ +# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230), +# containing no networking code at all, loosely modelled on hyper-h2's generic +# implementation of HTTP/2 (and in particular the h2.connection.H2Connection +# class). There's still a bunch of subtle details you need to get right if you +# want to make this actually useful, because it doesn't implement all the +# semantics to check that what you're asking to write to the wire is sensible, +# but at least it gets you out of dealing with the wire itself. + +from h11._connection import Connection, NEED_DATA, PAUSED +from h11._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from h11._state import ( + CLIENT, + CLOSED, + DONE, + ERROR, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError +from h11._version import __version__ + +PRODUCT_ID = "python-h11/" + __version__ + + +__all__ = ( + "Connection", + "NEED_DATA", + "PAUSED", + "ConnectionClosed", + "Data", + "EndOfMessage", + "Event", + "InformationalResponse", + "Request", + "Response", + "CLIENT", + "CLOSED", + "DONE", + "ERROR", + "IDLE", + "MUST_CLOSE", + "SEND_BODY", + "SEND_RESPONSE", + "SERVER", + "SWITCHED_PROTOCOL", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", +) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9938e26 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_abnf.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_abnf.cpython-310.pyc new file mode 100644 index 0000000..4a2ee5b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_abnf.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_connection.cpython-310.pyc new file mode 100644 index 0000000..23c7d50 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_events.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_events.cpython-310.pyc new file mode 100644 index 0000000..39724bc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_events.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_headers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_headers.cpython-310.pyc new file mode 100644 index 0000000..b3d50bb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_headers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_readers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_readers.cpython-310.pyc new file mode 100644 index 0000000..01b99bf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_readers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_receivebuffer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_receivebuffer.cpython-310.pyc new file mode 100644 index 0000000..6990587 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_receivebuffer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_state.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_state.cpython-310.pyc new file mode 100644 index 0000000..e57e4c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_state.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_util.cpython-310.pyc new file mode 100644 index 0000000..e4f8c7a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_version.cpython-310.pyc new file mode 100644 index 0000000..44cb8ff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_writers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_writers.cpython-310.pyc new file mode 100644 index 0000000..e12396a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/__pycache__/_writers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_abnf.py b/.pythonlibs/lib/python3.10/site-packages/h11/_abnf.py new file mode 100644 index 0000000..933587f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_abnf.py @@ -0,0 +1,132 @@ +# We use native strings for all the re patterns, to take advantage of string +# formatting, and then convert to bytestrings when compiling the final re +# objects. + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace +# OWS = *( SP / HTAB ) +# ; optional whitespace +OWS = r"[ \t]*" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators +# token = 1*tchar +# +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# ; any VCHAR, except delimiters +token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+" + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields +# field-name = token +field_name = token + +# The standard says: +# +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# obs-fold = CRLF 1*( SP / HTAB ) +# ; obsolete line folding +# ; see Section 3.2.4 +# +# https://tools.ietf.org/html/rfc5234#appendix-B.1 +# +# VCHAR = %x21-7E +# ; visible (printing) characters +# +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string +# obs-text = %x80-FF +# +# However, the standard definition of field-content is WRONG! It disallows +# fields containing a single visible character surrounded by whitespace, +# e.g. "foo a bar". +# +# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 +# +# So our definition of field_content attempts to fix it up... +# +# Also, we allow lots of control characters, because apparently people assume +# that they're legal in practice (e.g., google analytics makes cookies with +# \x01 in them!): +# https://github.com/python-hyper/h11/issues/57 +# We still don't allow NUL or whitespace, because those are often treated as +# meta-characters and letting them through can lead to nasty issues like SSRF. +vchar = r"[\x21-\x7e]" +vchar_or_obs_text = r"[^\x00\s]" +field_vchar = vchar_or_obs_text +field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals()) + +# We handle obs-fold at a different level, and our fixed-up field_content +# already grows to swallow the whole value, so ? instead of * +field_value = r"({field_content})?".format(**globals()) + +# header-field = field-name ":" OWS field-value OWS +header_field = ( + r"(?P{field_name})" + r":" + r"{OWS}" + r"(?P{field_value})" + r"{OWS}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line +# +# request-line = method SP request-target SP HTTP-version CRLF +# method = token +# HTTP-version = HTTP-name "/" DIGIT "." DIGIT +# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive +# +# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full +# URL, host+port (for connect), or even "*", but in any case we are guaranteed +# that it contists of the visible printing characters. +method = token +request_target = r"{vchar}+".format(**globals()) +http_version = r"HTTP/(?P[0-9]\.[0-9])" +request_line = ( + r"(?P{method})" + r" " + r"(?P{request_target})" + r" " + r"{http_version}".format(**globals()) +) + +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line +# +# status-line = HTTP-version SP status-code SP reason-phrase CRLF +# status-code = 3DIGIT +# reason-phrase = *( HTAB / SP / VCHAR / obs-text ) +status_code = r"[0-9]{3}" +reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals()) +status_line = ( + r"{http_version}" + r" " + r"(?P{status_code})" + # However, there are apparently a few too many servers out there that just + # leave out the reason phrase: + # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036 + # https://github.com/seanmonstar/httparse/issues/29 + # so make it optional. ?: is a non-capturing group. + r"(?: (?P{reason_phrase}))?".format(**globals()) +) + +HEXDIG = r"[0-9A-Fa-f]" +# Actually +# +# chunk-size = 1*HEXDIG +# +# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20 +chunk_size = r"({HEXDIG}){{1,20}}".format(**globals()) +# Actually +# +# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) +# +# but we aren't parsing the things so we don't really care. +chunk_ext = r";.*" +chunk_header = ( + r"(?P{chunk_size})" + r"(?P{chunk_ext})?" + r"{OWS}\r\n".format( + **globals() + ) # Even though the specification does not allow for extra whitespaces, + # we are lenient with trailing whitespaces because some servers on the wild use it. +) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_connection.py b/.pythonlibs/lib/python3.10/site-packages/h11/_connection.py new file mode 100644 index 0000000..d175270 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_connection.py @@ -0,0 +1,633 @@ +# This contains the main Connection class. Everything in h11 revolves around +# this. +from typing import Any, Callable, cast, Dict, List, Optional, Tuple, Type, Union + +from ._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from ._headers import get_comma_header, has_expect_100_continue, set_comma_header +from ._readers import READERS, ReadersType +from ._receivebuffer import ReceiveBuffer +from ._state import ( + _SWITCH_CONNECT, + _SWITCH_UPGRADE, + CLIENT, + ConnectionState, + DONE, + ERROR, + MIGHT_SWITCH_PROTOCOL, + SEND_BODY, + SERVER, + SWITCHED_PROTOCOL, +) +from ._util import ( # Import the internal things we need + LocalProtocolError, + RemoteProtocolError, + Sentinel, +) +from ._writers import WRITERS, WritersType + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = ["Connection", "NEED_DATA", "PAUSED"] + + +class NEED_DATA(Sentinel, metaclass=Sentinel): + pass + + +class PAUSED(Sentinel, metaclass=Sentinel): + pass + + +# If we ever have this much buffered without it making a complete parseable +# event, we error out. The only time we really buffer is when reading the +# request/response line + headers together, so this is effectively the limit on +# the size of that. +# +# Some precedents for defaults: +# - node.js: 80 * 1024 +# - tomcat: 8 * 1024 +# - IIS: 16 * 1024 +# - Apache: <8 KiB per line> +DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024 + +# RFC 7230's rules for connection lifecycles: +# - If either side says they want to close the connection, then the connection +# must close. +# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close +# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive +# (and even this is a mess -- e.g. if you're implementing a proxy then +# sending Connection: keep-alive is forbidden). +# +# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So +# our rule is: +# - If someone says Connection: close, we will close +# - If someone uses HTTP/1.0, we will close. +def _keep_alive(event: Union[Request, Response]) -> bool: + connection = get_comma_header(event.headers, b"connection") + if b"close" in connection: + return False + if getattr(event, "http_version", b"1.1") < b"1.1": + return False + return True + + +def _body_framing( + request_method: bytes, event: Union[Request, Response] +) -> Tuple[str, Union[Tuple[()], Tuple[int]]]: + # Called when we enter SEND_BODY to figure out framing information for + # this body. + # + # These are the only two events that can trigger a SEND_BODY state: + assert type(event) in (Request, Response) + # Returns one of: + # + # ("content-length", count) + # ("chunked", ()) + # ("http/1.0", ()) + # + # which are (lookup key, *args) for constructing body reader/writer + # objects. + # + # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3 + # + # Step 1: some responses always have an empty body, regardless of what the + # headers say. + if type(event) is Response: + if ( + event.status_code in (204, 304) + or request_method == b"HEAD" + or (request_method == b"CONNECT" and 200 <= event.status_code < 300) + ): + return ("content-length", (0,)) + # Section 3.3.3 also lists another case -- responses with status_code + # < 200. For us these are InformationalResponses, not Responses, so + # they can't get into this function in the first place. + assert event.status_code >= 200 + + # Step 2: check for Transfer-Encoding (T-E beats C-L): + transfer_encodings = get_comma_header(event.headers, b"transfer-encoding") + if transfer_encodings: + assert transfer_encodings == [b"chunked"] + return ("chunked", ()) + + # Step 3: check for Content-Length + content_lengths = get_comma_header(event.headers, b"content-length") + if content_lengths: + return ("content-length", (int(content_lengths[0]),)) + + # Step 4: no applicable headers; fallback/default depends on type + if type(event) is Request: + return ("content-length", (0,)) + else: + return ("http/1.0", ()) + + +################################################################ +# +# The main Connection class +# +################################################################ + + +class Connection: + """An object encapsulating the state of an HTTP connection. + + Args: + our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If + you're implementing a server, pass :data:`h11.SERVER`. + + max_incomplete_event_size (int): + The maximum number of bytes we're willing to buffer of an + incomplete event. In practice this mostly sets a limit on the + maximum size of the request/response line + headers. If this is + exceeded, then :meth:`next_event` will raise + :exc:`RemoteProtocolError`. + + """ + + def __init__( + self, + our_role: Type[Sentinel], + max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE, + ) -> None: + self._max_incomplete_event_size = max_incomplete_event_size + # State and role tracking + if our_role not in (CLIENT, SERVER): + raise ValueError("expected CLIENT or SERVER, not {!r}".format(our_role)) + self.our_role = our_role + self.their_role: Type[Sentinel] + if our_role is CLIENT: + self.their_role = SERVER + else: + self.their_role = CLIENT + self._cstate = ConnectionState() + + # Callables for converting data->events or vice-versa given the + # current state + self._writer = self._get_io_object(self.our_role, None, WRITERS) + self._reader = self._get_io_object(self.their_role, None, READERS) + + # Holds any unprocessed received data + self._receive_buffer = ReceiveBuffer() + # If this is true, then it indicates that the incoming connection was + # closed *after* the end of whatever's in self._receive_buffer: + self._receive_buffer_closed = False + + # Extra bits of state that don't fit into the state machine. + # + # These two are only used to interpret framing headers for figuring + # out how to read/write response bodies. their_http_version is also + # made available as a convenient public API. + self.their_http_version: Optional[bytes] = None + self._request_method: Optional[bytes] = None + # This is pure flow-control and doesn't at all affect the set of legal + # transitions, so no need to bother ConnectionState with it: + self.client_is_waiting_for_100_continue = False + + @property + def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]: + """A dictionary like:: + + {CLIENT: , SERVER: } + + See :ref:`state-machine` for details. + + """ + return dict(self._cstate.states) + + @property + def our_state(self) -> Type[Sentinel]: + """The current state of whichever role we are playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.our_role] + + @property + def their_state(self) -> Type[Sentinel]: + """The current state of whichever role we are NOT playing. See + :ref:`state-machine` for details. + """ + return self._cstate.states[self.their_role] + + @property + def they_are_waiting_for_100_continue(self) -> bool: + return self.their_role is CLIENT and self.client_is_waiting_for_100_continue + + def start_next_cycle(self) -> None: + """Attempt to reset our connection state for a new request/response + cycle. + + If both client and server are in :data:`DONE` state, then resets them + both to :data:`IDLE` state in preparation for a new request/response + cycle on this same connection. Otherwise, raises a + :exc:`LocalProtocolError`. + + See :ref:`keepalive-and-pipelining`. + + """ + old_states = dict(self._cstate.states) + self._cstate.start_next_cycle() + self._request_method = None + # self.their_http_version gets left alone, since it presumably lasts + # beyond a single request/response cycle + assert not self.client_is_waiting_for_100_continue + self._respond_to_state_changes(old_states) + + def _process_error(self, role: Type[Sentinel]) -> None: + old_states = dict(self._cstate.states) + self._cstate.process_error(role) + self._respond_to_state_changes(old_states) + + def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]: + if type(event) is InformationalResponse and event.status_code == 101: + return _SWITCH_UPGRADE + if type(event) is Response: + if ( + _SWITCH_CONNECT in self._cstate.pending_switch_proposals + and 200 <= event.status_code < 300 + ): + return _SWITCH_CONNECT + return None + + # All events go through here + def _process_event(self, role: Type[Sentinel], event: Event) -> None: + # First, pass the event through the state machine to make sure it + # succeeds. + old_states = dict(self._cstate.states) + if role is CLIENT and type(event) is Request: + if event.method == b"CONNECT": + self._cstate.process_client_switch_proposal(_SWITCH_CONNECT) + if get_comma_header(event.headers, b"upgrade"): + self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE) + server_switch_event = None + if role is SERVER: + server_switch_event = self._server_switch_event(event) + self._cstate.process_event(role, type(event), server_switch_event) + + # Then perform the updates triggered by it. + + if type(event) is Request: + self._request_method = event.method + + if role is self.their_role and type(event) in ( + Request, + Response, + InformationalResponse, + ): + event = cast(Union[Request, Response, InformationalResponse], event) + self.their_http_version = event.http_version + + # Keep alive handling + # + # RFC 7230 doesn't really say what one should do if Connection: close + # shows up on a 1xx InformationalResponse. I think the idea is that + # this is not supposed to happen. In any case, if it does happen, we + # ignore it. + if type(event) in (Request, Response) and not _keep_alive( + cast(Union[Request, Response], event) + ): + self._cstate.process_keep_alive_disabled() + + # 100-continue + if type(event) is Request and has_expect_100_continue(event): + self.client_is_waiting_for_100_continue = True + if type(event) in (InformationalResponse, Response): + self.client_is_waiting_for_100_continue = False + if role is CLIENT and type(event) in (Data, EndOfMessage): + self.client_is_waiting_for_100_continue = False + + self._respond_to_state_changes(old_states, event) + + def _get_io_object( + self, + role: Type[Sentinel], + event: Optional[Event], + io_dict: Union[ReadersType, WritersType], + ) -> Optional[Callable[..., Any]]: + # event may be None; it's only used when entering SEND_BODY + state = self._cstate.states[role] + if state is SEND_BODY: + # Special case: the io_dict has a dict of reader/writer factories + # that depend on the request/response framing. + framing_type, args = _body_framing( + cast(bytes, self._request_method), cast(Union[Request, Response], event) + ) + return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index] + else: + # General case: the io_dict just has the appropriate reader/writer + # for this state + return io_dict.get((role, state)) # type: ignore[return-value] + + # This must be called after any action that might have caused + # self._cstate.states to change. + def _respond_to_state_changes( + self, + old_states: Dict[Type[Sentinel], Type[Sentinel]], + event: Optional[Event] = None, + ) -> None: + # Update reader/writer + if self.our_state != old_states[self.our_role]: + self._writer = self._get_io_object(self.our_role, event, WRITERS) + if self.their_state != old_states[self.their_role]: + self._reader = self._get_io_object(self.their_role, event, READERS) + + @property + def trailing_data(self) -> Tuple[bytes, bool]: + """Data that has been received, but not yet processed, represented as + a tuple with two elements, where the first is a byte-string containing + the unprocessed data itself, and the second is a bool that is True if + the receive connection was closed. + + See :ref:`switching-protocols` for discussion of why you'd want this. + """ + return (bytes(self._receive_buffer), self._receive_buffer_closed) + + def receive_data(self, data: bytes) -> None: + """Add data to our internal receive buffer. + + This does not actually do any processing on the data, just stores + it. To trigger processing, you have to call :meth:`next_event`. + + Args: + data (:term:`bytes-like object`): + The new data that was just received. + + Special case: If *data* is an empty byte-string like ``b""``, + then this indicates that the remote side has closed the + connection (end of file). Normally this is convenient, because + standard Python APIs like :meth:`file.read` or + :meth:`socket.recv` use ``b""`` to indicate end-of-file, while + other failures to read are indicated using other mechanisms + like raising :exc:`TimeoutError`. When using such an API you + can just blindly pass through whatever you get from ``read`` + to :meth:`receive_data`, and everything will work. + + But, if you have an API where reading an empty string is a + valid non-EOF condition, then you need to be aware of this and + make sure to check for such strings and avoid passing them to + :meth:`receive_data`. + + Returns: + Nothing, but after calling this you should call :meth:`next_event` + to parse the newly received data. + + Raises: + RuntimeError: + Raised if you pass an empty *data*, indicating EOF, and then + pass a non-empty *data*, indicating more data that somehow + arrived after the EOF. + + (Calling ``receive_data(b"")`` multiple times is fine, + and equivalent to calling it once.) + + """ + if data: + if self._receive_buffer_closed: + raise RuntimeError("received close, then received more data?") + self._receive_buffer += data + else: + self._receive_buffer_closed = True + + def _extract_next_receive_event( + self, + ) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + state = self.their_state + # We don't pause immediately when they enter DONE, because even in + # DONE state we can still process a ConnectionClosed() event. But + # if we have data in our buffer, then we definitely aren't getting + # a ConnectionClosed() immediately and we need to pause. + if state is DONE and self._receive_buffer: + return PAUSED + if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL: + return PAUSED + assert self._reader is not None + event = self._reader(self._receive_buffer) + if event is None: + if not self._receive_buffer and self._receive_buffer_closed: + # In some unusual cases (basically just HTTP/1.0 bodies), EOF + # triggers an actual protocol event; in that case, we want to + # return that event, and then the state will change and we'll + # get called again to generate the actual ConnectionClosed(). + if hasattr(self._reader, "read_eof"): + event = self._reader.read_eof() # type: ignore[attr-defined] + else: + event = ConnectionClosed() + if event is None: + event = NEED_DATA + return event # type: ignore[no-any-return] + + def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: + """Parse the next event out of our receive buffer, update our internal + state, and return it. + + This is a mutating operation -- think of it like calling :func:`next` + on an iterator. + + Returns: + : One of three things: + + 1) An event object -- see :ref:`events`. + + 2) The special constant :data:`NEED_DATA`, which indicates that + you need to read more data from your socket and pass it to + :meth:`receive_data` before this method will be able to return + any more events. + + 3) The special constant :data:`PAUSED`, which indicates that we + are not in a state where we can process incoming data (usually + because the peer has finished their part of the current + request/response cycle, and you have not yet called + :meth:`start_next_cycle`). See :ref:`flow-control` for details. + + Raises: + RemoteProtocolError: + The peer has misbehaved. You should close the connection + (possibly after sending some kind of 4xx response). + + Once this method returns :class:`ConnectionClosed` once, then all + subsequent calls will also return :class:`ConnectionClosed`. + + If this method raises any exception besides :exc:`RemoteProtocolError` + then that's a bug -- if it happens please file a bug report! + + If this method raises any exception then it also sets + :attr:`Connection.their_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + + if self.their_state is ERROR: + raise RemoteProtocolError("Can't receive data when peer state is ERROR") + try: + event = self._extract_next_receive_event() + if event not in [NEED_DATA, PAUSED]: + self._process_event(self.their_role, cast(Event, event)) + if event is NEED_DATA: + if len(self._receive_buffer) > self._max_incomplete_event_size: + # 431 is "Request header fields too large" which is pretty + # much the only situation where we can get here + raise RemoteProtocolError( + "Receive buffer too long", error_status_hint=431 + ) + if self._receive_buffer_closed: + # We're still trying to complete some event, but that's + # never going to happen because no more data is coming + raise RemoteProtocolError("peer unexpectedly closed connection") + return event + except BaseException as exc: + self._process_error(self.their_role) + if isinstance(exc, LocalProtocolError): + exc._reraise_as_remote_protocol_error() + else: + raise + + def send(self, event: Event) -> Optional[bytes]: + """Convert a high-level event into bytes that can be sent to the peer, + while updating our internal state machine. + + Args: + event: The :ref:`event ` to send. + + Returns: + If ``type(event) is ConnectionClosed``, then returns + ``None``. Otherwise, returns a :term:`bytes-like object`. + + Raises: + LocalProtocolError: + Sending this event at this time would violate our + understanding of the HTTP/1.1 protocol. + + If this method raises any exception then it also sets + :attr:`Connection.our_state` to :data:`ERROR` -- see + :ref:`error-handling` for discussion. + + """ + data_list = self.send_with_data_passthrough(event) + if data_list is None: + return None + else: + return b"".join(data_list) + + def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]: + """Identical to :meth:`send`, except that in situations where + :meth:`send` returns a single :term:`bytes-like object`, this instead + returns a list of them -- and when sending a :class:`Data` event, this + list is guaranteed to contain the exact object you passed in as + :attr:`Data.data`. See :ref:`sendfile` for discussion. + + """ + if self.our_state is ERROR: + raise LocalProtocolError("Can't send data when our state is ERROR") + try: + if type(event) is Response: + event = self._clean_up_response_headers_for_sending(event) + # We want to call _process_event before calling the writer, + # because if someone tries to do something invalid then this will + # give a sensible error message, while our writers all just assume + # they will only receive valid events. But, _process_event might + # change self._writer. So we have to do a little dance: + writer = self._writer + self._process_event(self.our_role, event) + if type(event) is ConnectionClosed: + return None + else: + # In any situation where writer is None, process_event should + # have raised ProtocolError + assert writer is not None + data_list: List[bytes] = [] + writer(event, data_list.append) + return data_list + except: + self._process_error(self.our_role) + raise + + def send_failed(self) -> None: + """Notify the state machine that we failed to send the data it gave + us. + + This causes :attr:`Connection.our_state` to immediately become + :data:`ERROR` -- see :ref:`error-handling` for discussion. + + """ + self._process_error(self.our_role) + + # When sending a Response, we take responsibility for a few things: + # + # - Sometimes you MUST set Connection: close. We take care of those + # times. (You can also set it yourself if you want, and if you do then + # we'll respect that and close the connection at the right time. But you + # don't have to worry about that unless you want to.) + # + # - The user has to set Content-Length if they want it. Otherwise, for + # responses that have bodies (e.g. not HEAD), then we will automatically + # select the right mechanism for streaming a body of unknown length, + # which depends on depending on the peer's HTTP version. + # + # This function's *only* responsibility is making sure headers are set up + # right -- everything downstream just looks at the headers. There are no + # side channels. + def _clean_up_response_headers_for_sending(self, response: Response) -> Response: + assert type(response) is Response + + headers = response.headers + need_close = False + + # HEAD requests need some special handling: they always act like they + # have Content-Length: 0, and that's how _body_framing treats + # them. But their headers are supposed to match what we would send if + # the request was a GET. (Technically there is one deviation allowed: + # we're allowed to leave out the framing headers -- see + # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as + # easy to get them right.) + method_for_choosing_headers = cast(bytes, self._request_method) + if method_for_choosing_headers == b"HEAD": + method_for_choosing_headers = b"GET" + framing_type, _ = _body_framing(method_for_choosing_headers, response) + if framing_type in ("chunked", "http/1.0"): + # This response has a body of unknown length. + # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked + # If our peer is HTTP/1.0, we use no framing headers, and close the + # connection afterwards. + # + # Make sure to clear Content-Length (in principle user could have + # set both and then we ignored Content-Length b/c + # Transfer-Encoding overwrote it -- this would be naughty of them, + # but the HTTP spec says that if our peer does this then we have + # to fix it instead of erroring out, so we'll accord the user the + # same respect). + headers = set_comma_header(headers, b"content-length", []) + if self.their_http_version is None or self.their_http_version < b"1.1": + # Either we never got a valid request and are sending back an + # error (their_http_version is None), so we assume the worst; + # or else we did get a valid HTTP/1.0 request, so we know that + # they don't understand chunked encoding. + headers = set_comma_header(headers, b"transfer-encoding", []) + # This is actually redundant ATM, since currently we + # unconditionally disable keep-alive when talking to HTTP/1.0 + # peers. But let's be defensive just in case we add + # Connection: keep-alive support later: + if self._request_method != b"HEAD": + need_close = True + else: + headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"]) + + if not self._cstate.keep_alive or need_close: + # Make sure Connection: close is set + connection = set(get_comma_header(headers, b"connection")) + connection.discard(b"keep-alive") + connection.add(b"close") + headers = set_comma_header(headers, b"connection", sorted(connection)) + + return Response( + headers=headers, + status_code=response.status_code, + http_version=response.http_version, + reason=response.reason, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_events.py b/.pythonlibs/lib/python3.10/site-packages/h11/_events.py new file mode 100644 index 0000000..075bf8a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_events.py @@ -0,0 +1,369 @@ +# High level events that make up HTTP/1.1 conversations. Loosely inspired by +# the corresponding events in hyper-h2: +# +# http://python-hyper.org/h2/en/stable/api.html#events +# +# Don't subclass these. Stuff will break. + +import re +from abc import ABC +from dataclasses import dataclass, field +from typing import Any, cast, Dict, List, Tuple, Union + +from ._abnf import method, request_target +from ._headers import Headers, normalize_and_validate +from ._util import bytesify, LocalProtocolError, validate + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "Event", + "Request", + "InformationalResponse", + "Response", + "Data", + "EndOfMessage", + "ConnectionClosed", +] + +method_re = re.compile(method.encode("ascii")) +request_target_re = re.compile(request_target.encode("ascii")) + + +class Event(ABC): + """ + Base class for h11 events. + """ + + __slots__ = () + + +@dataclass(init=False, frozen=True) +class Request(Event): + """The beginning of an HTTP request. + + Fields: + + .. attribute:: method + + An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: target + + The target of an HTTP request, e.g. ``b"/index.html"``, or one of the + more exotic formats described in `RFC 7320, section 5.3 + `_. Always a byte + string. :term:`Bytes-like objects ` and native + strings containing only ascii characters will be automatically + converted to byte strings. + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + """ + + __slots__ = ("method", "headers", "target", "http_version") + + method: bytes + headers: Headers + target: bytes + http_version: bytes + + def __init__( + self, + *, + method: Union[bytes, str], + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + target: Union[bytes, str], + http_version: Union[bytes, str] = b"1.1", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "method", bytesify(method)) + object.__setattr__(self, "target", bytesify(target)) + object.__setattr__(self, "http_version", bytesify(http_version)) + else: + object.__setattr__(self, "method", method) + object.__setattr__(self, "target", target) + object.__setattr__(self, "http_version", http_version) + + # "A server MUST respond with a 400 (Bad Request) status code to any + # HTTP/1.1 request message that lacks a Host header field and to any + # request message that contains more than one Host header field or a + # Host header field with an invalid field-value." + # -- https://tools.ietf.org/html/rfc7230#section-5.4 + host_count = 0 + for name, value in self.headers: + if name == b"host": + host_count += 1 + if self.http_version == b"1.1" and host_count == 0: + raise LocalProtocolError("Missing mandatory Host: header") + if host_count > 1: + raise LocalProtocolError("Found multiple Host: headers") + + validate(method_re, self.method, "Illegal method characters") + validate(request_target_re, self.target, "Illegal target characters") + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class _ResponseBase(Event): + __slots__ = ("headers", "http_version", "reason", "status_code") + + headers: Headers + http_version: bytes + reason: bytes + status_code: int + + def __init__( + self, + *, + headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], + status_code: int, + http_version: Union[bytes, str] = b"1.1", + reason: Union[bytes, str] = b"", + _parsed: bool = False, + ) -> None: + super().__init__() + if isinstance(headers, Headers): + object.__setattr__(self, "headers", headers) + else: + object.__setattr__( + self, "headers", normalize_and_validate(headers, _parsed=_parsed) + ) + if not _parsed: + object.__setattr__(self, "reason", bytesify(reason)) + object.__setattr__(self, "http_version", bytesify(http_version)) + if not isinstance(status_code, int): + raise LocalProtocolError("status code must be integer") + # Because IntEnum objects are instances of int, but aren't + # duck-compatible (sigh), see gh-72. + object.__setattr__(self, "status_code", int(status_code)) + else: + object.__setattr__(self, "reason", reason) + object.__setattr__(self, "http_version", http_version) + object.__setattr__(self, "status_code", status_code) + + self.__post_init__() + + def __post_init__(self) -> None: + pass + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class InformationalResponse(_ResponseBase): + """An HTTP informational response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`InformationalResponse`, this is always in the range [100, + 200). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for + details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (100 <= self.status_code < 200): + raise LocalProtocolError( + "InformationalResponse status_code should be in range " + "[100, 200), not {}".format(self.status_code) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Response(_ResponseBase): + """The beginning of an HTTP response. + + Fields: + + .. attribute:: status_code + + The status code of this response, as an integer. For an + :class:`Response`, this is always in the range [200, + 1000). + + .. attribute:: headers + + Request headers, represented as a list of (name, value) pairs. See + :ref:`the header normalization rules ` for details. + + .. attribute:: http_version + + The HTTP protocol version, represented as a byte string like + ``b"1.1"``. See :ref:`the HTTP version normalization rules + ` for details. + + .. attribute:: reason + + The reason phrase of this response, as a byte string. For example: + ``b"OK"``, or ``b"Not Found"``. + + """ + + def __post_init__(self) -> None: + if not (200 <= self.status_code < 1000): + raise LocalProtocolError( + "Response status_code should be in range [200, 1000), not {}".format( + self.status_code + ) + ) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(init=False, frozen=True) +class Data(Event): + """Part of an HTTP message body. + + Fields: + + .. attribute:: data + + A :term:`bytes-like object` containing part of a message body. Or, if + using the ``combine=False`` argument to :meth:`Connection.send`, then + any object that your socket writing code knows what to do with, and for + which calling :func:`len` returns the number of bytes that will be + written -- see :ref:`sendfile` for details. + + .. attribute:: chunk_start + + A marker that indicates whether this data object is from the start of a + chunked transfer encoding chunk. This field is ignored when when a Data + event is provided to :meth:`Connection.send`: it is only valid on + events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + .. attribute:: chunk_end + + A marker that indicates whether this data object is the last for a + given chunked transfer encoding chunk. This field is ignored when when + a Data event is provided to :meth:`Connection.send`: it is only valid + on events emitted from :meth:`Connection.next_event`. You probably + shouldn't use this attribute at all; see + :ref:`chunk-delimiters-are-bad` for details. + + """ + + __slots__ = ("data", "chunk_start", "chunk_end") + + data: bytes + chunk_start: bool + chunk_end: bool + + def __init__( + self, data: bytes, chunk_start: bool = False, chunk_end: bool = False + ) -> None: + object.__setattr__(self, "data", data) + object.__setattr__(self, "chunk_start", chunk_start) + object.__setattr__(self, "chunk_end", chunk_end) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that +# are forbidden to be sent in a trailer, since processing them as if they were +# present in the header section might bypass external security filters." +# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part +# Unfortunately, the list of forbidden fields is long and vague :-/ +@dataclass(init=False, frozen=True) +class EndOfMessage(Event): + """The end of an HTTP message. + + Fields: + + .. attribute:: headers + + Default value: ``[]`` + + Any trailing headers attached to this message, represented as a list of + (name, value) pairs. See :ref:`the header normalization rules + ` for details. + + Must be empty unless ``Transfer-Encoding: chunked`` is in use. + + """ + + __slots__ = ("headers",) + + headers: Headers + + def __init__( + self, + *, + headers: Union[ + Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None + ] = None, + _parsed: bool = False, + ) -> None: + super().__init__() + if headers is None: + headers = Headers([]) + elif not isinstance(headers, Headers): + headers = normalize_and_validate(headers, _parsed=_parsed) + + object.__setattr__(self, "headers", headers) + + # This is an unhashable type. + __hash__ = None # type: ignore + + +@dataclass(frozen=True) +class ConnectionClosed(Event): + """This event indicates that the sender has closed their outgoing + connection. + + Note that this does not necessarily mean that they can't *receive* further + data, because TCP connections are composed to two one-way channels which + can be closed independently. See :ref:`closing` for details. + + No fields. + """ + + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_headers.py b/.pythonlibs/lib/python3.10/site-packages/h11/_headers.py new file mode 100644 index 0000000..b97d020 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_headers.py @@ -0,0 +1,278 @@ +import re +from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union + +from ._abnf import field_name, field_value +from ._util import bytesify, LocalProtocolError, validate + +if TYPE_CHECKING: + from ._events import Request + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal # type: ignore + + +# Facts +# ----- +# +# Headers are: +# keys: case-insensitive ascii +# values: mixture of ascii and raw bytes +# +# "Historically, HTTP has allowed field content with text in the ISO-8859-1 +# charset [ISO-8859-1], supporting other charsets only through use of +# [RFC2047] encoding. In practice, most HTTP header field values use only a +# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD +# limit their field values to US-ASCII octets. A recipient SHOULD treat other +# octets in field content (obs-text) as opaque data." +# And it deprecates all non-ascii values +# +# Leading/trailing whitespace in header names is forbidden +# +# Values get leading/trailing whitespace stripped +# +# Content-Disposition actually needs to contain unicode semantically; to +# accomplish this it has a terrifically weird way of encoding the filename +# itself as ascii (and even this still has lots of cross-browser +# incompatibilities) +# +# Order is important: +# "a proxy MUST NOT change the order of these field values when forwarding a +# message" +# (and there are several headers where the order indicates a preference) +# +# Multiple occurences of the same header: +# "A sender MUST NOT generate multiple header fields with the same field name +# in a message unless either the entire field value for that header field is +# defined as a comma-separated list [or the header is Set-Cookie which gets a +# special exception]" - RFC 7230. (cookies are in RFC 6265) +# +# So every header aside from Set-Cookie can be merged by b", ".join if it +# occurs repeatedly. But, of course, they can't necessarily be split by +# .split(b","), because quoting. +# +# Given all this mess (case insensitive, duplicates allowed, order is +# important, ...), there doesn't appear to be any standard way to handle +# headers in Python -- they're almost like dicts, but... actually just +# aren't. For now we punt and just use a super simple representation: headers +# are a list of pairs +# +# [(name1, value1), (name2, value2), ...] +# +# where all entries are bytestrings, names are lowercase and have no +# leading/trailing whitespace, and values are bytestrings with no +# leading/trailing whitespace. Searching and updating are done via naive O(n) +# methods. +# +# Maybe a dict-of-lists would be better? + +_content_length_re = re.compile(rb"[0-9]+") +_field_name_re = re.compile(field_name.encode("ascii")) +_field_value_re = re.compile(field_value.encode("ascii")) + + +class Headers(Sequence[Tuple[bytes, bytes]]): + """ + A list-like interface that allows iterating over headers as byte-pairs + of (lowercased-name, value). + + Internally we actually store the representation as three-tuples, + including both the raw original casing, in order to preserve casing + over-the-wire, and the lowercased name, for case-insensitive comparisions. + + r = Request( + method="GET", + target="/", + headers=[("Host", "example.org"), ("Connection", "keep-alive")], + http_version="1.1", + ) + assert r.headers == [ + (b"host", b"example.org"), + (b"connection", b"keep-alive") + ] + assert r.headers.raw_items() == [ + (b"Host", b"example.org"), + (b"Connection", b"keep-alive") + ] + """ + + __slots__ = "_full_items" + + def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None: + self._full_items = full_items + + def __bool__(self) -> bool: + return bool(self._full_items) + + def __eq__(self, other: object) -> bool: + return list(self) == list(other) # type: ignore + + def __len__(self) -> int: + return len(self._full_items) + + def __repr__(self) -> str: + return "" % repr(list(self)) + + def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override] + _, name, value = self._full_items[idx] + return (name, value) + + def raw_items(self) -> List[Tuple[bytes, bytes]]: + return [(raw_name, value) for raw_name, _, value in self._full_items] + + +HeaderTypes = Union[ + List[Tuple[bytes, bytes]], + List[Tuple[bytes, str]], + List[Tuple[str, bytes]], + List[Tuple[str, str]], +] + + +@overload +def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers: + ... + + +@overload +def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers: + ... + + +@overload +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + ... + + +def normalize_and_validate( + headers: Union[Headers, HeaderTypes], _parsed: bool = False +) -> Headers: + new_headers = [] + seen_content_length = None + saw_transfer_encoding = False + for name, value in headers: + # For headers coming out of the parser, we can safely skip some steps, + # because it always returns bytes and has already run these regexes + # over the data: + if not _parsed: + name = bytesify(name) + value = bytesify(value) + validate(_field_name_re, name, "Illegal header name {!r}", name) + validate(_field_value_re, value, "Illegal header value {!r}", value) + assert isinstance(name, bytes) + assert isinstance(value, bytes) + + raw_name = name + name = name.lower() + if name == b"content-length": + lengths = {length.strip() for length in value.split(b",")} + if len(lengths) != 1: + raise LocalProtocolError("conflicting Content-Length headers") + value = lengths.pop() + validate(_content_length_re, value, "bad Content-Length") + if seen_content_length is None: + seen_content_length = value + new_headers.append((raw_name, name, value)) + elif seen_content_length != value: + raise LocalProtocolError("conflicting Content-Length headers") + elif name == b"transfer-encoding": + # "A server that receives a request message with a transfer coding + # it does not understand SHOULD respond with 501 (Not + # Implemented)." + # https://tools.ietf.org/html/rfc7230#section-3.3.1 + if saw_transfer_encoding: + raise LocalProtocolError( + "multiple Transfer-Encoding headers", error_status_hint=501 + ) + # "All transfer-coding names are case-insensitive" + # -- https://tools.ietf.org/html/rfc7230#section-4 + value = value.lower() + if value != b"chunked": + raise LocalProtocolError( + "Only Transfer-Encoding: chunked is supported", + error_status_hint=501, + ) + saw_transfer_encoding = True + new_headers.append((raw_name, name, value)) + else: + new_headers.append((raw_name, name, value)) + return Headers(new_headers) + + +def get_comma_header(headers: Headers, name: bytes) -> List[bytes]: + # Should only be used for headers whose value is a list of + # comma-separated, case-insensitive values. + # + # The header name `name` is expected to be lower-case bytes. + # + # Connection: meets these criteria (including cast insensitivity). + # + # Content-Length: technically is just a single value (1*DIGIT), but the + # standard makes reference to implementations that do multiple values, and + # using this doesn't hurt. Ditto, case insensitivity doesn't things either + # way. + # + # Transfer-Encoding: is more complex (allows for quoted strings), so + # splitting on , is actually wrong. For example, this is legal: + # + # Transfer-Encoding: foo; options="1,2", chunked + # + # and should be parsed as + # + # foo; options="1,2" + # chunked + # + # but this naive function will parse it as + # + # foo; options="1 + # 2" + # chunked + # + # However, this is okay because the only thing we are going to do with + # any Transfer-Encoding is reject ones that aren't just "chunked", so + # both of these will be treated the same anyway. + # + # Expect: the only legal value is the literal string + # "100-continue". Splitting on commas is harmless. Case insensitive. + # + out: List[bytes] = [] + for _, found_name, found_raw_value in headers._full_items: + if found_name == name: + found_raw_value = found_raw_value.lower() + for found_split_value in found_raw_value.split(b","): + found_split_value = found_split_value.strip() + if found_split_value: + out.append(found_split_value) + return out + + +def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers: + # The header name `name` is expected to be lower-case bytes. + # + # Note that when we store the header we use title casing for the header + # names, in order to match the conventional HTTP header style. + # + # Simply calling `.title()` is a blunt approach, but it's correct + # here given the cases where we're using `set_comma_header`... + # + # Connection, Content-Length, Transfer-Encoding. + new_headers: List[Tuple[bytes, bytes]] = [] + for found_raw_name, found_name, found_raw_value in headers._full_items: + if found_name != name: + new_headers.append((found_raw_name, found_raw_value)) + for new_value in new_values: + new_headers.append((name.title(), new_value)) + return normalize_and_validate(new_headers) + + +def has_expect_100_continue(request: "Request") -> bool: + # https://tools.ietf.org/html/rfc7231#section-5.1.1 + # "A server that receives a 100-continue expectation in an HTTP/1.0 request + # MUST ignore that expectation." + if request.http_version < b"1.1": + return False + expect = get_comma_header(request.headers, b"expect") + return b"100-continue" in expect diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_readers.py b/.pythonlibs/lib/python3.10/site-packages/h11/_readers.py new file mode 100644 index 0000000..08a9574 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_readers.py @@ -0,0 +1,247 @@ +# Code to read HTTP data +# +# Strategy: each reader is a callable which takes a ReceiveBuffer object, and +# either: +# 1) consumes some of it and returns an Event +# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate() +# and it might raise a LocalProtocolError, so simpler just to always use +# this) +# 3) returns None, meaning "I need more data" +# +# If they have a .read_eof attribute, then this will be called if an EOF is +# received -- but this is optional. Either way, the actual ConnectionClosed +# event will be generated afterwards. +# +# READERS is a dict describing how to pick a reader. It maps states to either: +# - a reader +# - or, for body readers, a dict of per-framing reader factories + +import re +from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union + +from ._abnf import chunk_header, header_field, request_line, status_line +from ._events import Data, EndOfMessage, InformationalResponse, Request, Response +from ._receivebuffer import ReceiveBuffer +from ._state import ( + CLIENT, + CLOSED, + DONE, + IDLE, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, +) +from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate + +__all__ = ["READERS"] + +header_field_re = re.compile(header_field.encode("ascii")) +obs_fold_re = re.compile(rb"[ \t]+") + + +def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]: + it = iter(lines) + last: Optional[bytes] = None + for line in it: + match = obs_fold_re.match(line) + if match: + if last is None: + raise LocalProtocolError("continuation line at start of headers") + if not isinstance(last, bytearray): + # Cast to a mutable type, avoiding copy on append to ensure O(n) time + last = bytearray(last) + last += b" " + last += line[match.end() :] + else: + if last is not None: + yield last + last = line + if last is not None: + yield last + + +def _decode_header_lines( + lines: Iterable[bytes], +) -> Iterable[Tuple[bytes, bytes]]: + for line in _obsolete_line_fold(lines): + matches = validate(header_field_re, line, "illegal header line: {!r}", line) + yield (matches["field_name"], matches["field_value"]) + + +request_line_re = re.compile(request_line.encode("ascii")) + + +def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no request line received") + matches = validate( + request_line_re, lines[0], "illegal request line: {!r}", lines[0] + ) + return Request( + headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches + ) + + +status_line_re = re.compile(status_line.encode("ascii")) + + +def maybe_read_from_SEND_RESPONSE_server( + buf: ReceiveBuffer, +) -> Union[InformationalResponse, Response, None]: + lines = buf.maybe_extract_lines() + if lines is None: + if buf.is_next_line_obviously_invalid_request_line(): + raise LocalProtocolError("illegal request line") + return None + if not lines: + raise LocalProtocolError("no response line received") + matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0]) + http_version = ( + b"1.1" if matches["http_version"] is None else matches["http_version"] + ) + reason = b"" if matches["reason"] is None else matches["reason"] + status_code = int(matches["status_code"]) + class_: Union[Type[InformationalResponse], Type[Response]] = ( + InformationalResponse if status_code < 200 else Response + ) + return class_( + headers=list(_decode_header_lines(lines[1:])), + _parsed=True, + status_code=status_code, + reason=reason, + http_version=http_version, + ) + + +class ContentLengthReader: + def __init__(self, length: int) -> None: + self._length = length + self._remaining = length + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._remaining == 0: + return EndOfMessage() + data = buf.maybe_extract_at_most(self._remaining) + if data is None: + return None + self._remaining -= len(data) + return Data(data=data) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(received {} bytes, expected {})".format( + self._length - self._remaining, self._length + ) + ) + + +chunk_header_re = re.compile(chunk_header.encode("ascii")) + + +class ChunkedReader: + def __init__(self) -> None: + self._bytes_in_chunk = 0 + # After reading a chunk, we have to throw away the trailing \r\n; if + # this is >0 then we discard that many bytes before resuming regular + # de-chunkification. + self._bytes_to_discard = 0 + self._reading_trailer = False + + def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: + if self._reading_trailer: + lines = buf.maybe_extract_lines() + if lines is None: + return None + return EndOfMessage(headers=list(_decode_header_lines(lines))) + if self._bytes_to_discard > 0: + data = buf.maybe_extract_at_most(self._bytes_to_discard) + if data is None: + return None + self._bytes_to_discard -= len(data) + if self._bytes_to_discard > 0: + return None + # else, fall through and read some more + assert self._bytes_to_discard == 0 + if self._bytes_in_chunk == 0: + # We need to refill our chunk count + chunk_header = buf.maybe_extract_next_line() + if chunk_header is None: + return None + matches = validate( + chunk_header_re, + chunk_header, + "illegal chunk header: {!r}", + chunk_header, + ) + # XX FIXME: we discard chunk extensions. Does anyone care? + self._bytes_in_chunk = int(matches["chunk_size"], base=16) + if self._bytes_in_chunk == 0: + self._reading_trailer = True + return self(buf) + chunk_start = True + else: + chunk_start = False + assert self._bytes_in_chunk > 0 + data = buf.maybe_extract_at_most(self._bytes_in_chunk) + if data is None: + return None + self._bytes_in_chunk -= len(data) + if self._bytes_in_chunk == 0: + self._bytes_to_discard = 2 + chunk_end = True + else: + chunk_end = False + return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end) + + def read_eof(self) -> NoReturn: + raise RemoteProtocolError( + "peer closed connection without sending complete message body " + "(incomplete chunked read)" + ) + + +class Http10Reader: + def __call__(self, buf: ReceiveBuffer) -> Optional[Data]: + data = buf.maybe_extract_at_most(999999999) + if data is None: + return None + return Data(data=data) + + def read_eof(self) -> EndOfMessage: + return EndOfMessage() + + +def expect_nothing(buf: ReceiveBuffer) -> None: + if buf: + raise LocalProtocolError("Got data when expecting EOF") + return None + + +ReadersType = Dict[ + Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]], + Union[Callable[..., Any], Dict[str, Callable[..., Any]]], +] + +READERS: ReadersType = { + (CLIENT, IDLE): maybe_read_from_IDLE_client, + (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server, + (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server, + (CLIENT, DONE): expect_nothing, + (CLIENT, MUST_CLOSE): expect_nothing, + (CLIENT, CLOSED): expect_nothing, + (SERVER, DONE): expect_nothing, + (SERVER, MUST_CLOSE): expect_nothing, + (SERVER, CLOSED): expect_nothing, + SEND_BODY: { + "chunked": ChunkedReader, + "content-length": ContentLengthReader, + "http/1.0": Http10Reader, + }, +} diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_receivebuffer.py b/.pythonlibs/lib/python3.10/site-packages/h11/_receivebuffer.py new file mode 100644 index 0000000..e5c4e08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_receivebuffer.py @@ -0,0 +1,153 @@ +import re +import sys +from typing import List, Optional, Union + +__all__ = ["ReceiveBuffer"] + + +# Operations we want to support: +# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable), +# or wait until there is one +# - read at-most-N bytes +# Goals: +# - on average, do this fast +# - worst case, do this in O(n) where n is the number of bytes processed +# Plan: +# - store bytearray, offset, how far we've searched for a separator token +# - use the how-far-we've-searched data to avoid rescanning +# - while doing a stream of uninterrupted processing, advance offset instead +# of constantly copying +# WARNING: +# - I haven't benchmarked or profiled any of this yet. +# +# Note that starting in Python 3.4, deleting the initial n bytes from a +# bytearray is amortized O(n), thanks to some excellent work by Antoine +# Martin: +# +# https://bugs.python.org/issue19087 +# +# This means that if we only supported 3.4+, we could get rid of the code here +# involving self._start and self.compress, because it's doing exactly the same +# thing that bytearray now does internally. +# +# BUT unfortunately, we still support 2.7, and reading short segments out of a +# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually +# delete this code. Yet: +# +# https://pythonclock.org/ +# +# (Two things to double-check first though: make sure PyPy also has the +# optimization, and benchmark to make sure it's a win, since we do have a +# slightly clever thing where we delay calling compress() until we've +# processed a whole event, which could in theory be slightly more efficient +# than the internal bytearray support.) +blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE) + + +class ReceiveBuffer: + def __init__(self) -> None: + self._data = bytearray() + self._next_line_search = 0 + self._multiple_lines_search = 0 + + def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer": + self._data += byteslike + return self + + def __bool__(self) -> bool: + return bool(len(self)) + + def __len__(self) -> int: + return len(self._data) + + # for @property unprocessed_data + def __bytes__(self) -> bytes: + return bytes(self._data) + + def _extract(self, count: int) -> bytearray: + # extracting an initial slice of the data buffer and return it + out = self._data[:count] + del self._data[:count] + + self._next_line_search = 0 + self._multiple_lines_search = 0 + + return out + + def maybe_extract_at_most(self, count: int) -> Optional[bytearray]: + """ + Extract a fixed number of bytes from the buffer. + """ + out = self._data[:count] + if not out: + return None + + return self._extract(count) + + def maybe_extract_next_line(self) -> Optional[bytearray]: + """ + Extract the first line, if it is completed in the buffer. + """ + # Only search in buffer space that we've not already looked at. + search_start_index = max(0, self._next_line_search - 1) + partial_idx = self._data.find(b"\r\n", search_start_index) + + if partial_idx == -1: + self._next_line_search = len(self._data) + return None + + # + 2 is to compensate len(b"\r\n") + idx = partial_idx + 2 + + return self._extract(idx) + + def maybe_extract_lines(self) -> Optional[List[bytearray]]: + """ + Extract everything up to the first blank line, and return a list of lines. + """ + # Handle the case where we have an immediate empty line. + if self._data[:1] == b"\n": + self._extract(1) + return [] + + if self._data[:2] == b"\r\n": + self._extract(2) + return [] + + # Only search in buffer space that we've not already looked at. + match = blank_line_regex.search(self._data, self._multiple_lines_search) + if match is None: + self._multiple_lines_search = max(0, len(self._data) - 2) + return None + + # Truncate the buffer and return it. + idx = match.span(0)[-1] + out = self._extract(idx) + lines = out.split(b"\n") + + for line in lines: + if line.endswith(b"\r"): + del line[-1] + + assert lines[-2] == lines[-1] == b"" + + del lines[-2:] + + return lines + + # In theory we should wait until `\r\n` before starting to validate + # incoming data. However it's interesting to detect (very) invalid data + # early given they might not even contain `\r\n` at all (hence only + # timeout will get rid of them). + # This is not a 100% effective detection but more of a cheap sanity check + # allowing for early abort in some useful cases. + # This is especially interesting when peer is messing up with HTTPS and + # sent us a TLS stream where we were expecting plain HTTP given all + # versions of TLS so far start handshake with a 0x16 message type code. + def is_next_line_obviously_invalid_request_line(self) -> bool: + try: + # HTTP header line must not contain non-printable characters + # and should not start with a space + return self._data[0] < 0x21 + except IndexError: + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_state.py b/.pythonlibs/lib/python3.10/site-packages/h11/_state.py new file mode 100644 index 0000000..3593430 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_state.py @@ -0,0 +1,367 @@ +################################################################ +# The core state machine +################################################################ +# +# Rule 1: everything that affects the state machine and state transitions must +# live here in this file. As much as possible goes into the table-based +# representation, but for the bits that don't quite fit, the actual code and +# state must nonetheless live here. +# +# Rule 2: this file does not know about what role we're playing; it only knows +# about HTTP request/response cycles in the abstract. This ensures that we +# don't cheat and apply different rules to local and remote parties. +# +# +# Theory of operation +# =================== +# +# Possibly the simplest way to think about this is that we actually have 5 +# different state machines here. Yes, 5. These are: +# +# 1) The client state, with its complicated automaton (see the docs) +# 2) The server state, with its complicated automaton (see the docs) +# 3) The keep-alive state, with possible states {True, False} +# 4) The SWITCH_CONNECT state, with possible states {False, True} +# 5) The SWITCH_UPGRADE state, with possible states {False, True} +# +# For (3)-(5), the first state listed is the initial state. +# +# (1)-(3) are stored explicitly in member variables. The last +# two are stored implicitly in the pending_switch_proposals set as: +# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals) +# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals) +# +# And each of these machines has two different kinds of transitions: +# +# a) Event-triggered +# b) State-triggered +# +# Event triggered is the obvious thing that you'd think it is: some event +# happens, and if it's the right event at the right time then a transition +# happens. But there are somewhat complicated rules for which machines can +# "see" which events. (As a rule of thumb, if a machine "sees" an event, this +# means two things: the event can affect the machine, and if the machine is +# not in a state where it expects that event then it's an error.) These rules +# are: +# +# 1) The client machine sees all h11.events objects emitted by the client. +# +# 2) The server machine sees all h11.events objects emitted by the server. +# +# It also sees the client's Request event. +# +# And sometimes, server events are annotated with a _SWITCH_* event. For +# example, we can have a (Response, _SWITCH_CONNECT) event, which is +# different from a regular Response event. +# +# 3) The keep-alive machine sees the process_keep_alive_disabled() event +# (which is derived from Request/Response events), and this event +# transitions it from True -> False, or from False -> False. There's no way +# to transition back. +# +# 4&5) The _SWITCH_* machines transition from False->True when we get a +# Request that proposes the relevant type of switch (via +# process_client_switch_proposals), and they go from True->False when we +# get a Response that has no _SWITCH_* annotation. +# +# So that's event-triggered transitions. +# +# State-triggered transitions are less standard. What they do here is couple +# the machines together. The way this works is, when certain *joint* +# configurations of states are achieved, then we automatically transition to a +# new *joint* state. So, for example, if we're ever in a joint state with +# +# client: DONE +# keep-alive: False +# +# then the client state immediately transitions to: +# +# client: MUST_CLOSE +# +# This is fundamentally different from an event-based transition, because it +# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state +# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive +# transitioned True -> False. Either way, once this precondition is satisfied, +# this transition is immediately triggered. +# +# What if two conflicting state-based transitions get enabled at the same +# time? In practice there's only one case where this arises (client DONE -> +# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by +# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition. +# +# Implementation +# -------------- +# +# The event-triggered transitions for the server and client machines are all +# stored explicitly in a table. Ditto for the state-triggered transitions that +# involve just the server and client state. +# +# The transitions for the other machines, and the state-triggered transitions +# that involve the other machines, are written out as explicit Python code. +# +# It'd be nice if there were some cleaner way to do all this. This isn't +# *too* terrible, but I feel like it could probably be better. +# +# WARNING +# ------- +# +# The script that generates the state machine diagrams for the docs knows how +# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS +# tables. But it can't automatically read the transitions that are written +# directly in Python code. So if you touch those, you need to also update the +# script to keep it in sync! +from typing import cast, Dict, Optional, Set, Tuple, Type, Union + +from ._events import * +from ._util import LocalProtocolError, Sentinel + +# Everything in __all__ gets re-exported as part of the h11 public API. +__all__ = [ + "CLIENT", + "SERVER", + "IDLE", + "SEND_RESPONSE", + "SEND_BODY", + "DONE", + "MUST_CLOSE", + "CLOSED", + "MIGHT_SWITCH_PROTOCOL", + "SWITCHED_PROTOCOL", + "ERROR", +] + + +class CLIENT(Sentinel, metaclass=Sentinel): + pass + + +class SERVER(Sentinel, metaclass=Sentinel): + pass + + +# States +class IDLE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_RESPONSE(Sentinel, metaclass=Sentinel): + pass + + +class SEND_BODY(Sentinel, metaclass=Sentinel): + pass + + +class DONE(Sentinel, metaclass=Sentinel): + pass + + +class MUST_CLOSE(Sentinel, metaclass=Sentinel): + pass + + +class CLOSED(Sentinel, metaclass=Sentinel): + pass + + +class ERROR(Sentinel, metaclass=Sentinel): + pass + + +# Switch types +class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel): + pass + + +class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel): + pass + + +EventTransitionType = Dict[ + Type[Sentinel], + Dict[ + Type[Sentinel], + Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]], + ], +] + +EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = { + CLIENT: { + IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED}, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + MIGHT_SWITCH_PROTOCOL: {}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, + SERVER: { + IDLE: { + ConnectionClosed: CLOSED, + Response: SEND_BODY, + # Special case: server sees client Request events, in this form + (Request, CLIENT): SEND_RESPONSE, + }, + SEND_RESPONSE: { + InformationalResponse: SEND_RESPONSE, + Response: SEND_BODY, + (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL, + (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL, + }, + SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, + DONE: {ConnectionClosed: CLOSED}, + MUST_CLOSE: {ConnectionClosed: CLOSED}, + CLOSED: {ConnectionClosed: CLOSED}, + SWITCHED_PROTOCOL: {}, + ERROR: {}, + }, +} + +StateTransitionType = Dict[ + Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]] +] + +# NB: there are also some special-case state-triggered transitions hard-coded +# into _fire_state_triggered_transitions below. +STATE_TRIGGERED_TRANSITIONS: StateTransitionType = { + # (Client state, Server state) -> new states + # Protocol negotiation + (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL}, + # Socket shutdown + (CLOSED, DONE): {SERVER: MUST_CLOSE}, + (CLOSED, IDLE): {SERVER: MUST_CLOSE}, + (ERROR, DONE): {SERVER: MUST_CLOSE}, + (DONE, CLOSED): {CLIENT: MUST_CLOSE}, + (IDLE, CLOSED): {CLIENT: MUST_CLOSE}, + (DONE, ERROR): {CLIENT: MUST_CLOSE}, +} + + +class ConnectionState: + def __init__(self) -> None: + # Extra bits of state that don't quite fit into the state model. + + # If this is False then it enables the automatic DONE -> MUST_CLOSE + # transition. Don't set this directly; call .keep_alive_disabled() + self.keep_alive = True + + # This is a subset of {UPGRADE, CONNECT}, containing the proposals + # made by the client for switching protocols. + self.pending_switch_proposals: Set[Type[Sentinel]] = set() + + self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE} + + def process_error(self, role: Type[Sentinel]) -> None: + self.states[role] = ERROR + self._fire_state_triggered_transitions() + + def process_keep_alive_disabled(self) -> None: + self.keep_alive = False + self._fire_state_triggered_transitions() + + def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None: + self.pending_switch_proposals.add(switch_event) + self._fire_state_triggered_transitions() + + def process_event( + self, + role: Type[Sentinel], + event_type: Type[Event], + server_switch_event: Optional[Type[Sentinel]] = None, + ) -> None: + _event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type + if server_switch_event is not None: + assert role is SERVER + if server_switch_event not in self.pending_switch_proposals: + raise LocalProtocolError( + "Received server {} event without a pending proposal".format( + server_switch_event + ) + ) + _event_type = (event_type, server_switch_event) + if server_switch_event is None and _event_type is Response: + self.pending_switch_proposals = set() + self._fire_event_triggered_transitions(role, _event_type) + # Special case: the server state does get to see Request + # events. + if _event_type is Request: + assert role is CLIENT + self._fire_event_triggered_transitions(SERVER, (Request, CLIENT)) + self._fire_state_triggered_transitions() + + def _fire_event_triggered_transitions( + self, + role: Type[Sentinel], + event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], + ) -> None: + state = self.states[role] + try: + new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type] + except KeyError: + event_type = cast(Type[Event], event_type) + raise LocalProtocolError( + "can't handle event type {} when role={} and state={}".format( + event_type.__name__, role, self.states[role] + ) + ) from None + self.states[role] = new_state + + def _fire_state_triggered_transitions(self) -> None: + # We apply these rules repeatedly until converging on a fixed point + while True: + start_states = dict(self.states) + + # It could happen that both these special-case transitions are + # enabled at the same time: + # + # DONE -> MIGHT_SWITCH_PROTOCOL + # DONE -> MUST_CLOSE + # + # For example, this will always be true of a HTTP/1.0 client + # requesting CONNECT. If this happens, the protocol switch takes + # priority. From there the client will either go to + # SWITCHED_PROTOCOL, in which case it's none of our business when + # they close the connection, or else the server will deny the + # request, in which case the client will go back to DONE and then + # from there to MUST_CLOSE. + if self.pending_switch_proposals: + if self.states[CLIENT] is DONE: + self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL + + if not self.pending_switch_proposals: + if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL: + self.states[CLIENT] = DONE + + if not self.keep_alive: + for role in (CLIENT, SERVER): + if self.states[role] is DONE: + self.states[role] = MUST_CLOSE + + # Tabular state-triggered transitions + joint_state = (self.states[CLIENT], self.states[SERVER]) + changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {}) + self.states.update(changes) + + if self.states == start_states: + # Fixed point reached + return + + def start_next_cycle(self) -> None: + if self.states != {CLIENT: DONE, SERVER: DONE}: + raise LocalProtocolError( + "not in a reusable state. self.states={}".format(self.states) + ) + # Can't reach DONE/DONE with any of these active, but still, let's be + # sure. + assert self.keep_alive + assert not self.pending_switch_proposals + self.states = {CLIENT: IDLE, SERVER: IDLE} diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_util.py b/.pythonlibs/lib/python3.10/site-packages/h11/_util.py new file mode 100644 index 0000000..6718445 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_util.py @@ -0,0 +1,135 @@ +from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union + +__all__ = [ + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "validate", + "bytesify", +] + + +class ProtocolError(Exception): + """Exception indicating a violation of the HTTP/1.1 protocol. + + This as an abstract base class, with two concrete base classes: + :exc:`LocalProtocolError`, which indicates that you tried to do something + that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which + indicates that the remote peer tried to do something that HTTP/1.1 says is + illegal. See :ref:`error-handling` for details. + + In addition to the normal :exc:`Exception` features, it has one attribute: + + .. attribute:: error_status_hint + + This gives a suggestion as to what status code a server might use if + this error occurred as part of a request. + + For a :exc:`RemoteProtocolError`, this is useful as a suggestion for + how you might want to respond to a misbehaving peer, if you're + implementing a server. + + For a :exc:`LocalProtocolError`, this can be taken as a suggestion for + how your peer might have responded to *you* if h11 had allowed you to + continue. + + The default is 400 Bad Request, a generic catch-all for protocol + violations. + + """ + + def __init__(self, msg: str, error_status_hint: int = 400) -> None: + if type(self) is ProtocolError: + raise TypeError("tried to directly instantiate ProtocolError") + Exception.__init__(self, msg) + self.error_status_hint = error_status_hint + + +# Strategy: there are a number of public APIs where a LocalProtocolError can +# be raised (send(), all the different event constructors, ...), and only one +# public API where RemoteProtocolError can be raised +# (receive_data()). Therefore we always raise LocalProtocolError internally, +# and then receive_data will translate this into a RemoteProtocolError. +# +# Internally: +# LocalProtocolError is the generic "ProtocolError". +# Externally: +# LocalProtocolError is for local errors and RemoteProtocolError is for +# remote errors. +class LocalProtocolError(ProtocolError): + def _reraise_as_remote_protocol_error(self) -> NoReturn: + # After catching a LocalProtocolError, use this method to re-raise it + # as a RemoteProtocolError. This method must be called from inside an + # except: block. + # + # An easy way to get an equivalent RemoteProtocolError is just to + # modify 'self' in place. + self.__class__ = RemoteProtocolError # type: ignore + # But the re-raising is somewhat non-trivial -- you might think that + # now that we've modified the in-flight exception object, that just + # doing 'raise' to re-raise it would be enough. But it turns out that + # this doesn't work, because Python tracks the exception type + # (exc_info[0]) separately from the exception object (exc_info[1]), + # and we only modified the latter. So we really do need to re-raise + # the new type explicitly. + # On py3, the traceback is part of the exception object, so our + # in-place modification preserved it and we can just re-raise: + raise self + + +class RemoteProtocolError(ProtocolError): + pass + + +def validate( + regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any +) -> Dict[str, bytes]: + match = regex.fullmatch(data) + if not match: + if format_args: + msg = msg.format(*format_args) + raise LocalProtocolError(msg) + return match.groupdict() + + +# Sentinel values +# +# - Inherit identity-based comparison and hashing from object +# - Have a nice repr +# - Have a *bonus property*: type(sentinel) is sentinel +# +# The bonus property is useful if you want to take the return value from +# next_event() and do some sort of dispatch based on type(event). + +_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel") + + +class Sentinel(type): + def __new__( + cls: Type[_T_Sentinel], + name: str, + bases: Tuple[type, ...], + namespace: Dict[str, Any], + **kwds: Any + ) -> _T_Sentinel: + assert bases == (Sentinel,) + v = super().__new__(cls, name, bases, namespace, **kwds) + v.__class__ = v # type: ignore + return v + + def __repr__(self) -> str: + return self.__name__ + + +# Used for methods, request targets, HTTP versions, header names, and header +# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always +# returns bytes. +def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes: + # Fast-path: + if type(s) is bytes: + return s + if isinstance(s, str): + s = s.encode("ascii") + if isinstance(s, int): + raise TypeError("expected bytes-like object, not int") + return bytes(s) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_version.py b/.pythonlibs/lib/python3.10/site-packages/h11/_version.py new file mode 100644 index 0000000..4c89113 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_version.py @@ -0,0 +1,16 @@ +# This file must be kept very simple, because it is consumed from several +# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc. + +# We use a simple scheme: +# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev +# where the +dev versions are never released into the wild, they're just what +# we stick into the VCS in between releases. +# +# This is compatible with PEP 440: +# http://legacy.python.org/dev/peps/pep-0440/ +# via the use of the "local suffix" "+dev", which is disallowed on index +# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we +# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before* +# 1.0.0.) + +__version__ = "0.14.0" diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/_writers.py b/.pythonlibs/lib/python3.10/site-packages/h11/_writers.py new file mode 100644 index 0000000..939cdb9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/_writers.py @@ -0,0 +1,145 @@ +# Code to read HTTP data +# +# Strategy: each writer takes an event + a write-some-bytes function, which is +# calls. +# +# WRITERS is a dict describing how to pick a reader. It maps states to either: +# - a writer +# - or, for body writers, a dict of framin-dependent writer factories + +from typing import Any, Callable, Dict, List, Tuple, Type, Union + +from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response +from ._headers import Headers +from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER +from ._util import LocalProtocolError, Sentinel + +__all__ = ["WRITERS"] + +Writer = Callable[[bytes], Any] + + +def write_headers(headers: Headers, write: Writer) -> None: + # "Since the Host field-value is critical information for handling a + # request, a user agent SHOULD generate Host as the first header field + # following the request-line." - RFC 7230 + raw_items = headers._full_items + for raw_name, name, value in raw_items: + if name == b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + for raw_name, name, value in raw_items: + if name != b"host": + write(b"%s: %s\r\n" % (raw_name, value)) + write(b"\r\n") + + +def write_request(request: Request, write: Writer) -> None: + if request.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target)) + write_headers(request.headers, write) + + +# Shared between InformationalResponse and Response +def write_any_response( + response: Union[InformationalResponse, Response], write: Writer +) -> None: + if response.http_version != b"1.1": + raise LocalProtocolError("I only send HTTP/1.1") + status_bytes = str(response.status_code).encode("ascii") + # We don't bother sending ascii status messages like "OK"; they're + # optional and ignored by the protocol. (But the space after the numeric + # status code is mandatory.) + # + # XX FIXME: could at least make an effort to pull out the status message + # from stdlib's http.HTTPStatus table. Or maybe just steal their enums + # (either by import or copy/paste). We already accept them as status codes + # since they're of type IntEnum < int. + write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason)) + write_headers(response.headers, write) + + +class BodyWriter: + def __call__(self, event: Event, write: Writer) -> None: + if type(event) is Data: + self.send_data(event.data, write) + elif type(event) is EndOfMessage: + self.send_eom(event.headers, write) + else: # pragma: no cover + assert False + + def send_data(self, data: bytes, write: Writer) -> None: + pass + + def send_eom(self, headers: Headers, write: Writer) -> None: + pass + + +# +# These are all careful not to do anything to 'data' except call len(data) and +# write(data). This allows us to transparently pass-through funny objects, +# like placeholder objects referring to files on disk that will be sent via +# sendfile(2). +# +class ContentLengthWriter(BodyWriter): + def __init__(self, length: int) -> None: + self._length = length + + def send_data(self, data: bytes, write: Writer) -> None: + self._length -= len(data) + if self._length < 0: + raise LocalProtocolError("Too much data for declared Content-Length") + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if self._length != 0: + raise LocalProtocolError("Too little data for declared Content-Length") + if headers: + raise LocalProtocolError("Content-Length and trailers don't mix") + + +class ChunkedWriter(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + # if we encoded 0-length data in the naive way, it would look like an + # end-of-message. + if not data: + return + write(b"%x\r\n" % len(data)) + write(data) + write(b"\r\n") + + def send_eom(self, headers: Headers, write: Writer) -> None: + write(b"0\r\n") + write_headers(headers, write) + + +class Http10Writer(BodyWriter): + def send_data(self, data: bytes, write: Writer) -> None: + write(data) + + def send_eom(self, headers: Headers, write: Writer) -> None: + if headers: + raise LocalProtocolError("can't send trailers to HTTP/1.0 client") + # no need to close the socket ourselves, that will be taken care of by + # Connection: close machinery + + +WritersType = Dict[ + Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]], + Union[ + Dict[str, Type[BodyWriter]], + Callable[[Union[InformationalResponse, Response], Writer], None], + Callable[[Request, Writer], None], + ], +] + +WRITERS: WritersType = { + (CLIENT, IDLE): write_request, + (SERVER, IDLE): write_any_response, + (SERVER, SEND_RESPONSE): write_any_response, + SEND_BODY: { + "chunked": ChunkedWriter, + "content-length": ContentLengthWriter, + "http/1.0": Http10Writer, + }, +} diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/py.typed b/.pythonlibs/lib/python3.10/site-packages/h11/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__init__.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6bc0460 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/helpers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 0000000..87789e9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/helpers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-310.pyc new file mode 100644 index 0000000..7b0fec5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_connection.cpython-310.pyc new file mode 100644 index 0000000..3c30299 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_events.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_events.cpython-310.pyc new file mode 100644 index 0000000..a42a281 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_events.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_headers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_headers.cpython-310.pyc new file mode 100644 index 0000000..e1395a7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_headers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_helpers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_helpers.cpython-310.pyc new file mode 100644 index 0000000..d473eac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_helpers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_io.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_io.cpython-310.pyc new file mode 100644 index 0000000..8edc796 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_io.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-310.pyc new file mode 100644 index 0000000..f955b04 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_state.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_state.cpython-310.pyc new file mode 100644 index 0000000..082f64d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_state.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_util.cpython-310.pyc new file mode 100644 index 0000000..1de5e58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/h11/tests/__pycache__/test_util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/data/test-file b/.pythonlibs/lib/python3.10/site-packages/h11/tests/data/test-file new file mode 100644 index 0000000..d0be0a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/data/test-file @@ -0,0 +1 @@ +92b12bc045050b55b848d37167a1a63947c364579889ce1d39788e45e9fac9e5 diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/helpers.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/helpers.py new file mode 100644 index 0000000..571be44 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/helpers.py @@ -0,0 +1,101 @@ +from typing import cast, List, Type, Union, ValuesView + +from .._connection import Connection, NEED_DATA, PAUSED +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .._state import CLIENT, CLOSED, DONE, MUST_CLOSE, SERVER +from .._util import Sentinel + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal # type: ignore + + +def get_all_events(conn: Connection) -> List[Event]: + got_events = [] + while True: + event = conn.next_event() + if event in (NEED_DATA, PAUSED): + break + event = cast(Event, event) + got_events.append(event) + if type(event) is ConnectionClosed: + break + return got_events + + +def receive_and_get(conn: Connection, data: bytes) -> List[Event]: + conn.receive_data(data) + return get_all_events(conn) + + +# Merges adjacent Data events, converts payloads to bytestrings, and removes +# chunk boundaries. +def normalize_data_events(in_events: List[Event]) -> List[Event]: + out_events: List[Event] = [] + for event in in_events: + if type(event) is Data: + event = Data(data=bytes(event.data), chunk_start=False, chunk_end=False) + if out_events and type(out_events[-1]) is type(event) is Data: + out_events[-1] = Data( + data=out_events[-1].data + event.data, + chunk_start=out_events[-1].chunk_start, + chunk_end=out_events[-1].chunk_end, + ) + else: + out_events.append(event) + return out_events + + +# Given that we want to write tests that push some events through a Connection +# and check that its state updates appropriately... we might as make a habit +# of pushing them through two Connections with a fake network link in +# between. +class ConnectionPair: + def __init__(self) -> None: + self.conn = {CLIENT: Connection(CLIENT), SERVER: Connection(SERVER)} + self.other = {CLIENT: SERVER, SERVER: CLIENT} + + @property + def conns(self) -> ValuesView[Connection]: + return self.conn.values() + + # expect="match" if expect=send_events; expect=[...] to say what expected + def send( + self, + role: Type[Sentinel], + send_events: Union[List[Event], Event], + expect: Union[List[Event], Event, Literal["match"]] = "match", + ) -> bytes: + if not isinstance(send_events, list): + send_events = [send_events] + data = b"" + closed = False + for send_event in send_events: + new_data = self.conn[role].send(send_event) + if new_data is None: + closed = True + else: + data += new_data + # send uses b"" to mean b"", and None to mean closed + # receive uses b"" to mean closed, and None to mean "try again" + # so we have to translate between the two conventions + if data: + self.conn[self.other[role]].receive_data(data) + if closed: + self.conn[self.other[role]].receive_data(b"") + got_events = get_all_events(self.conn[self.other[role]]) + if expect == "match": + expect = send_events + if not isinstance(expect, list): + expect = [expect] + assert got_events == expect + return data diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_against_stdlib_http.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_against_stdlib_http.py new file mode 100644 index 0000000..d2ee131 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_against_stdlib_http.py @@ -0,0 +1,115 @@ +import json +import os.path +import socket +import socketserver +import threading +from contextlib import closing, contextmanager +from http.server import SimpleHTTPRequestHandler +from typing import Callable, Generator +from urllib.request import urlopen + +import h11 + + +@contextmanager +def socket_server( + handler: Callable[..., socketserver.BaseRequestHandler] +) -> Generator[socketserver.TCPServer, None, None]: + httpd = socketserver.TCPServer(("127.0.0.1", 0), handler) + thread = threading.Thread( + target=httpd.serve_forever, kwargs={"poll_interval": 0.01} + ) + thread.daemon = True + try: + thread.start() + yield httpd + finally: + httpd.shutdown() + + +test_file_path = os.path.join(os.path.dirname(__file__), "data/test-file") +with open(test_file_path, "rb") as f: + test_file_data = f.read() + + +class SingleMindedRequestHandler(SimpleHTTPRequestHandler): + def translate_path(self, path: str) -> str: + return test_file_path + + +def test_h11_as_client() -> None: + with socket_server(SingleMindedRequestHandler) as httpd: + with closing(socket.create_connection(httpd.server_address)) as s: + c = h11.Connection(h11.CLIENT) + + s.sendall( + c.send( # type: ignore[arg-type] + h11.Request( + method="GET", target="/foo", headers=[("Host", "localhost")] + ) + ) + ) + s.sendall(c.send(h11.EndOfMessage())) # type: ignore[arg-type] + + data = bytearray() + while True: + event = c.next_event() + print(event) + if event is h11.NEED_DATA: + # Use a small read buffer to make things more challenging + # and exercise more paths :-) + c.receive_data(s.recv(10)) + continue + if type(event) is h11.Response: + assert event.status_code == 200 + if type(event) is h11.Data: + data += event.data + if type(event) is h11.EndOfMessage: + break + assert bytes(data) == test_file_data + + +class H11RequestHandler(socketserver.BaseRequestHandler): + def handle(self) -> None: + with closing(self.request) as s: + c = h11.Connection(h11.SERVER) + request = None + while True: + event = c.next_event() + if event is h11.NEED_DATA: + # Use a small read buffer to make things more challenging + # and exercise more paths :-) + c.receive_data(s.recv(10)) + continue + if type(event) is h11.Request: + request = event + if type(event) is h11.EndOfMessage: + break + assert request is not None + info = json.dumps( + { + "method": request.method.decode("ascii"), + "target": request.target.decode("ascii"), + "headers": { + name.decode("ascii"): value.decode("ascii") + for (name, value) in request.headers + }, + } + ) + s.sendall(c.send(h11.Response(status_code=200, headers=[]))) # type: ignore[arg-type] + s.sendall(c.send(h11.Data(data=info.encode("ascii")))) + s.sendall(c.send(h11.EndOfMessage())) + + +def test_h11_as_server() -> None: + with socket_server(H11RequestHandler) as httpd: + host, port = httpd.server_address + url = "http://{}:{}/some-path".format(host, port) + with closing(urlopen(url)) as f: + assert f.getcode() == 200 + data = f.read() + info = json.loads(data.decode("ascii")) + print(info) + assert info["method"] == "GET" + assert info["target"] == "/some-path" + assert "urllib" in info["headers"]["user-agent"] diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_connection.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_connection.py new file mode 100644 index 0000000..73a27b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_connection.py @@ -0,0 +1,1122 @@ +from typing import Any, cast, Dict, List, Optional, Tuple, Type + +import pytest + +from .._connection import _body_framing, _keep_alive, Connection, NEED_DATA, PAUSED +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .._state import ( + CLIENT, + CLOSED, + DONE, + ERROR, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from .._util import LocalProtocolError, RemoteProtocolError, Sentinel +from .helpers import ConnectionPair, get_all_events, receive_and_get + + +def test__keep_alive() -> None: + assert _keep_alive( + Request(method="GET", target="/", headers=[("Host", "Example.com")]) + ) + assert not _keep_alive( + Request( + method="GET", + target="/", + headers=[("Host", "Example.com"), ("Connection", "close")], + ) + ) + assert not _keep_alive( + Request( + method="GET", + target="/", + headers=[("Host", "Example.com"), ("Connection", "a, b, cLOse, foo")], + ) + ) + assert not _keep_alive( + Request(method="GET", target="/", headers=[], http_version="1.0") # type: ignore[arg-type] + ) + + assert _keep_alive(Response(status_code=200, headers=[])) # type: ignore[arg-type] + assert not _keep_alive(Response(status_code=200, headers=[("Connection", "close")])) + assert not _keep_alive( + Response(status_code=200, headers=[("Connection", "a, b, cLOse, foo")]) + ) + assert not _keep_alive(Response(status_code=200, headers=[], http_version="1.0")) # type: ignore[arg-type] + + +def test__body_framing() -> None: + def headers(cl: Optional[int], te: bool) -> List[Tuple[str, str]]: + headers = [] + if cl is not None: + headers.append(("Content-Length", str(cl))) + if te: + headers.append(("Transfer-Encoding", "chunked")) + return headers + + def resp( + status_code: int = 200, cl: Optional[int] = None, te: bool = False + ) -> Response: + return Response(status_code=status_code, headers=headers(cl, te)) + + def req(cl: Optional[int] = None, te: bool = False) -> Request: + h = headers(cl, te) + h += [("Host", "example.com")] + return Request(method="GET", target="/", headers=h) + + # Special cases where the headers are ignored: + for kwargs in [{}, {"cl": 100}, {"te": True}, {"cl": 100, "te": True}]: + kwargs = cast(Dict[str, Any], kwargs) + for meth, r in [ + (b"HEAD", resp(**kwargs)), + (b"GET", resp(status_code=204, **kwargs)), + (b"GET", resp(status_code=304, **kwargs)), + ]: + assert _body_framing(meth, r) == ("content-length", (0,)) + + # Transfer-encoding + for kwargs in [{"te": True}, {"cl": 100, "te": True}]: + kwargs = cast(Dict[str, Any], kwargs) + for meth, r in [(None, req(**kwargs)), (b"GET", resp(**kwargs))]: # type: ignore + assert _body_framing(meth, r) == ("chunked", ()) + + # Content-Length + for meth, r in [(None, req(cl=100)), (b"GET", resp(cl=100))]: # type: ignore + assert _body_framing(meth, r) == ("content-length", (100,)) + + # No headers + assert _body_framing(None, req()) == ("content-length", (0,)) # type: ignore + assert _body_framing(b"GET", resp()) == ("http/1.0", ()) + + +def test_Connection_basics_and_content_length() -> None: + with pytest.raises(ValueError): + Connection("CLIENT") # type: ignore + + p = ConnectionPair() + assert p.conn[CLIENT].our_role is CLIENT + assert p.conn[CLIENT].their_role is SERVER + assert p.conn[SERVER].our_role is SERVER + assert p.conn[SERVER].their_role is CLIENT + + data = p.send( + CLIENT, + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Content-Length", "10")], + ), + ) + assert data == ( + b"GET / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 10\r\n\r\n" + ) + + for conn in p.conns: + assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + assert p.conn[CLIENT].our_state is SEND_BODY + assert p.conn[CLIENT].their_state is SEND_RESPONSE + assert p.conn[SERVER].our_state is SEND_RESPONSE + assert p.conn[SERVER].their_state is SEND_BODY + + assert p.conn[CLIENT].their_http_version is None + assert p.conn[SERVER].their_http_version == b"1.1" + + data = p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type] + assert data == b"HTTP/1.1 100 \r\n\r\n" + + data = p.send(SERVER, Response(status_code=200, headers=[("Content-Length", "11")])) + assert data == b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n" + + for conn in p.conns: + assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY} + + assert p.conn[CLIENT].their_http_version == b"1.1" + assert p.conn[SERVER].their_http_version == b"1.1" + + data = p.send(CLIENT, Data(data=b"12345")) + assert data == b"12345" + data = p.send( + CLIENT, Data(data=b"67890"), expect=[Data(data=b"67890"), EndOfMessage()] + ) + assert data == b"67890" + data = p.send(CLIENT, EndOfMessage(), expect=[]) + assert data == b"" + + for conn in p.conns: + assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY} + + data = p.send(SERVER, Data(data=b"1234567890")) + assert data == b"1234567890" + data = p.send(SERVER, Data(data=b"1"), expect=[Data(data=b"1"), EndOfMessage()]) + assert data == b"1" + data = p.send(SERVER, EndOfMessage(), expect=[]) + assert data == b"" + + for conn in p.conns: + assert conn.states == {CLIENT: DONE, SERVER: DONE} + + +def test_chunked() -> None: + p = ConnectionPair() + + p.send( + CLIENT, + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")], + ), + ) + data = p.send(CLIENT, Data(data=b"1234567890", chunk_start=True, chunk_end=True)) + assert data == b"a\r\n1234567890\r\n" + data = p.send(CLIENT, Data(data=b"abcde", chunk_start=True, chunk_end=True)) + assert data == b"5\r\nabcde\r\n" + data = p.send(CLIENT, Data(data=b""), expect=[]) + assert data == b"" + data = p.send(CLIENT, EndOfMessage(headers=[("hello", "there")])) + assert data == b"0\r\nhello: there\r\n\r\n" + + p.send( + SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")]) + ) + p.send(SERVER, Data(data=b"54321", chunk_start=True, chunk_end=True)) + p.send(SERVER, Data(data=b"12345", chunk_start=True, chunk_end=True)) + p.send(SERVER, EndOfMessage()) + + for conn in p.conns: + assert conn.states == {CLIENT: DONE, SERVER: DONE} + + +def test_chunk_boundaries() -> None: + conn = Connection(our_role=SERVER) + + request = ( + b"POST / HTTP/1.1\r\n" + b"Host: example.com\r\n" + b"Transfer-Encoding: chunked\r\n" + b"\r\n" + ) + conn.receive_data(request) + assert conn.next_event() == Request( + method="POST", + target="/", + headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")], + ) + assert conn.next_event() is NEED_DATA + + conn.receive_data(b"5\r\nhello\r\n") + assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True) + + conn.receive_data(b"5\r\nhel") + assert conn.next_event() == Data(data=b"hel", chunk_start=True, chunk_end=False) + + conn.receive_data(b"l") + assert conn.next_event() == Data(data=b"l", chunk_start=False, chunk_end=False) + + conn.receive_data(b"o\r\n") + assert conn.next_event() == Data(data=b"o", chunk_start=False, chunk_end=True) + + conn.receive_data(b"5\r\nhello") + assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True) + + conn.receive_data(b"\r\n") + assert conn.next_event() == NEED_DATA + + conn.receive_data(b"0\r\n\r\n") + assert conn.next_event() == EndOfMessage() + + +def test_client_talking_to_http10_server() -> None: + c = Connection(CLIENT) + c.send(Request(method="GET", target="/", headers=[("Host", "example.com")])) + c.send(EndOfMessage()) + assert c.our_state is DONE + # No content-length, so Http10 framing for body + assert receive_and_get(c, b"HTTP/1.0 200 OK\r\n\r\n") == [ + Response(status_code=200, headers=[], http_version="1.0", reason=b"OK") # type: ignore[arg-type] + ] + assert c.our_state is MUST_CLOSE + assert receive_and_get(c, b"12345") == [Data(data=b"12345")] + assert receive_and_get(c, b"67890") == [Data(data=b"67890")] + assert receive_and_get(c, b"") == [EndOfMessage(), ConnectionClosed()] + assert c.their_state is CLOSED + + +def test_server_talking_to_http10_client() -> None: + c = Connection(SERVER) + # No content-length, so no body + # NB: no host header + assert receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") == [ + Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] + EndOfMessage(), + ] + assert c.their_state is MUST_CLOSE + + # We automatically Connection: close back at them + assert ( + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" + ) + + assert c.send(Data(data=b"12345")) == b"12345" + assert c.send(EndOfMessage()) == b"" + assert c.our_state is MUST_CLOSE + + # Check that it works if they do send Content-Length + c = Connection(SERVER) + # NB: no host header + assert receive_and_get(c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1") == [ + Request( + method="POST", + target="/", + headers=[("Content-Length", "10")], + http_version="1.0", + ), + Data(data=b"1"), + ] + assert receive_and_get(c, b"234567890") == [Data(data=b"234567890"), EndOfMessage()] + assert c.their_state is MUST_CLOSE + assert receive_and_get(c, b"") == [ConnectionClosed()] + + +def test_automatic_transfer_encoding_in_response() -> None: + # Check that in responses, the user can specify either Transfer-Encoding: + # chunked or no framing at all, and in both cases we automatically select + # the right option depending on whether the peer speaks HTTP/1.0 or + # HTTP/1.1 + for user_headers in [ + [("Transfer-Encoding", "chunked")], + [], + # In fact, this even works if Content-Length is set, + # because if both are set then Transfer-Encoding wins + [("Transfer-Encoding", "chunked"), ("Content-Length", "100")], + ]: + user_headers = cast(List[Tuple[str, str]], user_headers) + p = ConnectionPair() + p.send( + CLIENT, + [ + Request(method="GET", target="/", headers=[("Host", "example.com")]), + EndOfMessage(), + ], + ) + # When speaking to HTTP/1.1 client, all of the above cases get + # normalized to Transfer-Encoding: chunked + p.send( + SERVER, + Response(status_code=200, headers=user_headers), + expect=Response( + status_code=200, headers=[("Transfer-Encoding", "chunked")] + ), + ) + + # When speaking to HTTP/1.0 client, all of the above cases get + # normalized to no-framing-headers + c = Connection(SERVER) + receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") + assert ( + c.send(Response(status_code=200, headers=user_headers)) + == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" + ) + assert c.send(Data(data=b"12345")) == b"12345" + + +def test_automagic_connection_close_handling() -> None: + p = ConnectionPair() + # If the user explicitly sets Connection: close, then we notice and + # respect it + p.send( + CLIENT, + [ + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Connection", "close")], + ), + EndOfMessage(), + ], + ) + for conn in p.conns: + assert conn.states[CLIENT] is MUST_CLOSE + # And if the client sets it, the server automatically echoes it back + p.send( + SERVER, + # no header here... + [Response(status_code=204, headers=[]), EndOfMessage()], # type: ignore[arg-type] + # ...but oh look, it arrived anyway + expect=[ + Response(status_code=204, headers=[("connection", "close")]), + EndOfMessage(), + ], + ) + for conn in p.conns: + assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} + + +def test_100_continue() -> None: + def setup() -> ConnectionPair: + p = ConnectionPair() + p.send( + CLIENT, + Request( + method="GET", + target="/", + headers=[ + ("Host", "example.com"), + ("Content-Length", "100"), + ("Expect", "100-continue"), + ], + ), + ) + for conn in p.conns: + assert conn.client_is_waiting_for_100_continue + assert not p.conn[CLIENT].they_are_waiting_for_100_continue + assert p.conn[SERVER].they_are_waiting_for_100_continue + return p + + # Disabled by 100 Continue + p = setup() + p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type] + for conn in p.conns: + assert not conn.client_is_waiting_for_100_continue + assert not conn.they_are_waiting_for_100_continue + + # Disabled by a real response + p = setup() + p.send( + SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")]) + ) + for conn in p.conns: + assert not conn.client_is_waiting_for_100_continue + assert not conn.they_are_waiting_for_100_continue + + # Disabled by the client going ahead and sending stuff anyway + p = setup() + p.send(CLIENT, Data(data=b"12345")) + for conn in p.conns: + assert not conn.client_is_waiting_for_100_continue + assert not conn.they_are_waiting_for_100_continue + + +def test_max_incomplete_event_size_countermeasure() -> None: + # Infinitely long headers are definitely not okay + c = Connection(SERVER) + c.receive_data(b"GET / HTTP/1.0\r\nEndless: ") + assert c.next_event() is NEED_DATA + with pytest.raises(RemoteProtocolError): + while True: + c.receive_data(b"a" * 1024) + c.next_event() + + # Checking that the same header is accepted / rejected depending on the + # max_incomplete_event_size setting: + c = Connection(SERVER, max_incomplete_event_size=5000) + c.receive_data(b"GET / HTTP/1.0\r\nBig: ") + c.receive_data(b"a" * 4000) + c.receive_data(b"\r\n\r\n") + assert get_all_events(c) == [ + Request( + method="GET", target="/", http_version="1.0", headers=[("big", "a" * 4000)] + ), + EndOfMessage(), + ] + + c = Connection(SERVER, max_incomplete_event_size=4000) + c.receive_data(b"GET / HTTP/1.0\r\nBig: ") + c.receive_data(b"a" * 4000) + with pytest.raises(RemoteProtocolError): + c.next_event() + + # Temporarily exceeding the size limit is fine, as long as its done with + # complete events: + c = Connection(SERVER, max_incomplete_event_size=5000) + c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000") + c.receive_data(b"\r\n\r\n" + b"a" * 10000) + assert get_all_events(c) == [ + Request( + method="GET", + target="/", + http_version="1.0", + headers=[("Content-Length", "10000")], + ), + Data(data=b"a" * 10000), + EndOfMessage(), + ] + + c = Connection(SERVER, max_incomplete_event_size=100) + # Two pipelined requests to create a way-too-big receive buffer... but + # it's fine because we're not checking + c.receive_data( + b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n" + b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n" + b"X" * 1000 + ) + assert get_all_events(c) == [ + Request(method="GET", target="/1", headers=[("host", "a")]), + EndOfMessage(), + ] + # Even more data comes in, still no problem + c.receive_data(b"X" * 1000) + # We can respond and reuse to get the second pipelined request + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + c.start_next_cycle() + assert get_all_events(c) == [ + Request(method="GET", target="/2", headers=[("host", "b")]), + EndOfMessage(), + ] + # But once we unpause and try to read the next message, and find that it's + # incomplete and the buffer is *still* way too large, then *that's* a + # problem: + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + c.start_next_cycle() + with pytest.raises(RemoteProtocolError): + c.next_event() + + +def test_reuse_simple() -> None: + p = ConnectionPair() + p.send( + CLIENT, + [Request(method="GET", target="/", headers=[("Host", "a")]), EndOfMessage()], + ) + p.send( + SERVER, + [ + Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), + EndOfMessage(), + ], + ) + for conn in p.conns: + assert conn.states == {CLIENT: DONE, SERVER: DONE} + conn.start_next_cycle() + + p.send( + CLIENT, + [ + Request(method="DELETE", target="/foo", headers=[("Host", "a")]), + EndOfMessage(), + ], + ) + p.send( + SERVER, + [ + Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), + EndOfMessage(), + ], + ) + + +def test_pipelining() -> None: + # Client doesn't support pipelining, so we have to do this by hand + c = Connection(SERVER) + assert c.next_event() is NEED_DATA + # 3 requests all bunched up + c.receive_data( + b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" + b"12345" + b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" + b"67890" + b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n" + ) + assert get_all_events(c) == [ + Request( + method="GET", + target="/1", + headers=[("Host", "a.com"), ("Content-Length", "5")], + ), + Data(data=b"12345"), + EndOfMessage(), + ] + assert c.their_state is DONE + assert c.our_state is SEND_RESPONSE + + assert c.next_event() is PAUSED + + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + assert c.their_state is DONE + assert c.our_state is DONE + + c.start_next_cycle() + + assert get_all_events(c) == [ + Request( + method="GET", + target="/2", + headers=[("Host", "a.com"), ("Content-Length", "5")], + ), + Data(data=b"67890"), + EndOfMessage(), + ] + assert c.next_event() is PAUSED + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + c.start_next_cycle() + + assert get_all_events(c) == [ + Request(method="GET", target="/3", headers=[("Host", "a.com")]), + EndOfMessage(), + ] + # Doesn't pause this time, no trailing data + assert c.next_event() is NEED_DATA + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + + # Arrival of more data triggers pause + assert c.next_event() is NEED_DATA + c.receive_data(b"SADF") + assert c.next_event() is PAUSED + assert c.trailing_data == (b"SADF", False) + # If EOF arrives while paused, we don't see that either: + c.receive_data(b"") + assert c.trailing_data == (b"SADF", True) + assert c.next_event() is PAUSED + c.receive_data(b"") + assert c.next_event() is PAUSED + # Can't call receive_data with non-empty buf after closing it + with pytest.raises(RuntimeError): + c.receive_data(b"FDSA") + + +def test_protocol_switch() -> None: + for (req, deny, accept) in [ + ( + Request( + method="CONNECT", + target="example.com:443", + headers=[("Host", "foo"), ("Content-Length", "1")], + ), + Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), + Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), + ), + ( + Request( + method="GET", + target="/", + headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], + ), + Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), + InformationalResponse(status_code=101, headers=[("Upgrade", "a")]), + ), + ( + Request( + method="CONNECT", + target="example.com:443", + headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], + ), + Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), + # Accept CONNECT, not upgrade + Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), + ), + ( + Request( + method="CONNECT", + target="example.com:443", + headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], + ), + Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), + # Accept Upgrade, not CONNECT + InformationalResponse(status_code=101, headers=[("Upgrade", "b")]), + ), + ]: + + def setup() -> ConnectionPair: + p = ConnectionPair() + p.send(CLIENT, req) + # No switch-related state change stuff yet; the client has to + # finish the request before that kicks in + for conn in p.conns: + assert conn.states[CLIENT] is SEND_BODY + p.send(CLIENT, [Data(data=b"1"), EndOfMessage()]) + for conn in p.conns: + assert conn.states[CLIENT] is MIGHT_SWITCH_PROTOCOL + assert p.conn[SERVER].next_event() is PAUSED + return p + + # Test deny case + p = setup() + p.send(SERVER, deny) + for conn in p.conns: + assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY} + p.send(SERVER, EndOfMessage()) + # Check that re-use is still allowed after a denial + for conn in p.conns: + conn.start_next_cycle() + + # Test accept case + p = setup() + p.send(SERVER, accept) + for conn in p.conns: + assert conn.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} + conn.receive_data(b"123") + assert conn.next_event() is PAUSED + conn.receive_data(b"456") + assert conn.next_event() is PAUSED + assert conn.trailing_data == (b"123456", False) + + # Pausing in might-switch, then recovery + # (weird artificial case where the trailing data actually is valid + # HTTP for some reason, because this makes it easier to test the state + # logic) + p = setup() + sc = p.conn[SERVER] + sc.receive_data(b"GET / HTTP/1.0\r\n\r\n") + assert sc.next_event() is PAUSED + assert sc.trailing_data == (b"GET / HTTP/1.0\r\n\r\n", False) + sc.send(deny) + assert sc.next_event() is PAUSED + sc.send(EndOfMessage()) + sc.start_next_cycle() + assert get_all_events(sc) == [ + Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] + EndOfMessage(), + ] + + # When we're DONE, have no trailing data, and the connection gets + # closed, we report ConnectionClosed(). When we're in might-switch or + # switched, we don't. + p = setup() + sc = p.conn[SERVER] + sc.receive_data(b"") + assert sc.next_event() is PAUSED + assert sc.trailing_data == (b"", True) + p.send(SERVER, accept) + assert sc.next_event() is PAUSED + + p = setup() + sc = p.conn[SERVER] + sc.receive_data(b"") + assert sc.next_event() is PAUSED + sc.send(deny) + assert sc.next_event() == ConnectionClosed() + + # You can't send after switching protocols, or while waiting for a + # protocol switch + p = setup() + with pytest.raises(LocalProtocolError): + p.conn[CLIENT].send( + Request(method="GET", target="/", headers=[("Host", "a")]) + ) + p = setup() + p.send(SERVER, accept) + with pytest.raises(LocalProtocolError): + p.conn[SERVER].send(Data(data=b"123")) + + +def test_close_simple() -> None: + # Just immediately closing a new connection without anything having + # happened yet. + for (who_shot_first, who_shot_second) in [(CLIENT, SERVER), (SERVER, CLIENT)]: + + def setup() -> ConnectionPair: + p = ConnectionPair() + p.send(who_shot_first, ConnectionClosed()) + for conn in p.conns: + assert conn.states == { + who_shot_first: CLOSED, + who_shot_second: MUST_CLOSE, + } + return p + + # You can keep putting b"" into a closed connection, and you keep + # getting ConnectionClosed() out: + p = setup() + assert p.conn[who_shot_second].next_event() == ConnectionClosed() + assert p.conn[who_shot_second].next_event() == ConnectionClosed() + p.conn[who_shot_second].receive_data(b"") + assert p.conn[who_shot_second].next_event() == ConnectionClosed() + # Second party can close... + p = setup() + p.send(who_shot_second, ConnectionClosed()) + for conn in p.conns: + assert conn.our_state is CLOSED + assert conn.their_state is CLOSED + # But trying to receive new data on a closed connection is a + # RuntimeError (not ProtocolError, because the problem here isn't + # violation of HTTP, it's violation of physics) + p = setup() + with pytest.raises(RuntimeError): + p.conn[who_shot_second].receive_data(b"123") + # And receiving new data on a MUST_CLOSE connection is a ProtocolError + p = setup() + p.conn[who_shot_first].receive_data(b"GET") + with pytest.raises(RemoteProtocolError): + p.conn[who_shot_first].next_event() + + +def test_close_different_states() -> None: + req = [ + Request(method="GET", target="/foo", headers=[("Host", "a")]), + EndOfMessage(), + ] + resp = [ + Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), + EndOfMessage(), + ] + + # Client before request + p = ConnectionPair() + p.send(CLIENT, ConnectionClosed()) + for conn in p.conns: + assert conn.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} + + # Client after request + p = ConnectionPair() + p.send(CLIENT, req) + p.send(CLIENT, ConnectionClosed()) + for conn in p.conns: + assert conn.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} + + # Server after request -> not allowed + p = ConnectionPair() + p.send(CLIENT, req) + with pytest.raises(LocalProtocolError): + p.conn[SERVER].send(ConnectionClosed()) + p.conn[CLIENT].receive_data(b"") + with pytest.raises(RemoteProtocolError): + p.conn[CLIENT].next_event() + + # Server after response + p = ConnectionPair() + p.send(CLIENT, req) + p.send(SERVER, resp) + p.send(SERVER, ConnectionClosed()) + for conn in p.conns: + assert conn.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} + + # Both after closing (ConnectionClosed() is idempotent) + p = ConnectionPair() + p.send(CLIENT, req) + p.send(SERVER, resp) + p.send(CLIENT, ConnectionClosed()) + p.send(SERVER, ConnectionClosed()) + p.send(CLIENT, ConnectionClosed()) + p.send(SERVER, ConnectionClosed()) + + # In the middle of sending -> not allowed + p = ConnectionPair() + p.send( + CLIENT, + Request( + method="GET", target="/", headers=[("Host", "a"), ("Content-Length", "10")] + ), + ) + with pytest.raises(LocalProtocolError): + p.conn[CLIENT].send(ConnectionClosed()) + p.conn[SERVER].receive_data(b"") + with pytest.raises(RemoteProtocolError): + p.conn[SERVER].next_event() + + +# Receive several requests and then client shuts down their side of the +# connection; we can respond to each +def test_pipelined_close() -> None: + c = Connection(SERVER) + # 2 requests then a close + c.receive_data( + b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" + b"12345" + b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" + b"67890" + ) + c.receive_data(b"") + assert get_all_events(c) == [ + Request( + method="GET", + target="/1", + headers=[("host", "a.com"), ("content-length", "5")], + ), + Data(data=b"12345"), + EndOfMessage(), + ] + assert c.states[CLIENT] is DONE + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + assert c.states[SERVER] is DONE + c.start_next_cycle() + assert get_all_events(c) == [ + Request( + method="GET", + target="/2", + headers=[("host", "a.com"), ("content-length", "5")], + ), + Data(data=b"67890"), + EndOfMessage(), + ConnectionClosed(), + ] + assert c.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} + c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] + c.send(EndOfMessage()) + assert c.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} + c.send(ConnectionClosed()) + assert c.states == {CLIENT: CLOSED, SERVER: CLOSED} + + +def test_sendfile() -> None: + class SendfilePlaceholder: + def __len__(self) -> int: + return 10 + + placeholder = SendfilePlaceholder() + + def setup( + header: Tuple[str, str], http_version: str + ) -> Tuple[Connection, Optional[List[bytes]]]: + c = Connection(SERVER) + receive_and_get( + c, "GET / HTTP/{}\r\nHost: a\r\n\r\n".format(http_version).encode("ascii") + ) + headers = [] + if header: + headers.append(header) + c.send(Response(status_code=200, headers=headers)) + return c, c.send_with_data_passthrough(Data(data=placeholder)) # type: ignore + + c, data = setup(("Content-Length", "10"), "1.1") + assert data == [placeholder] # type: ignore + # Raises an error if the connection object doesn't think we've sent + # exactly 10 bytes + c.send(EndOfMessage()) + + _, data = setup(("Transfer-Encoding", "chunked"), "1.1") + assert placeholder in data # type: ignore + data[data.index(placeholder)] = b"x" * 10 # type: ignore + assert b"".join(data) == b"a\r\nxxxxxxxxxx\r\n" # type: ignore + + c, data = setup(None, "1.0") # type: ignore + assert data == [placeholder] # type: ignore + assert c.our_state is SEND_BODY + + +def test_errors() -> None: + # After a receive error, you can't receive + for role in [CLIENT, SERVER]: + c = Connection(our_role=role) + c.receive_data(b"gibberish\r\n\r\n") + with pytest.raises(RemoteProtocolError): + c.next_event() + # Now any attempt to receive continues to raise + assert c.their_state is ERROR + assert c.our_state is not ERROR + print(c._cstate.states) + with pytest.raises(RemoteProtocolError): + c.next_event() + # But we can still yell at the client for sending us gibberish + if role is SERVER: + assert ( + c.send(Response(status_code=400, headers=[])) # type: ignore[arg-type] + == b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n" + ) + + # After an error sending, you can no longer send + # (This is especially important for things like content-length errors, + # where there's complex internal state being modified) + def conn(role: Type[Sentinel]) -> Connection: + c = Connection(our_role=role) + if role is SERVER: + # Put it into the state where it *could* send a response... + receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") + assert c.our_state is SEND_RESPONSE + return c + + for role in [CLIENT, SERVER]: + if role is CLIENT: + # This HTTP/1.0 request won't be detected as bad until after we go + # through the state machine and hit the writing code + good = Request(method="GET", target="/", headers=[("Host", "example.com")]) + bad = Request( + method="GET", + target="/", + headers=[("Host", "example.com")], + http_version="1.0", + ) + elif role is SERVER: + good = Response(status_code=200, headers=[]) # type: ignore[arg-type,assignment] + bad = Response(status_code=200, headers=[], http_version="1.0") # type: ignore[arg-type,assignment] + # Make sure 'good' actually is good + c = conn(role) + c.send(good) + assert c.our_state is not ERROR + # Do that again, but this time sending 'bad' first + c = conn(role) + with pytest.raises(LocalProtocolError): + c.send(bad) + assert c.our_state is ERROR + assert c.their_state is not ERROR + # Now 'good' is not so good + with pytest.raises(LocalProtocolError): + c.send(good) + + # And check send_failed() too + c = conn(role) + c.send_failed() + assert c.our_state is ERROR + assert c.their_state is not ERROR + # This is idempotent + c.send_failed() + assert c.our_state is ERROR + assert c.their_state is not ERROR + + +def test_idle_receive_nothing() -> None: + # At one point this incorrectly raised an error + for role in [CLIENT, SERVER]: + c = Connection(role) + assert c.next_event() is NEED_DATA + + +def test_connection_drop() -> None: + c = Connection(SERVER) + c.receive_data(b"GET /") + assert c.next_event() is NEED_DATA + c.receive_data(b"") + with pytest.raises(RemoteProtocolError): + c.next_event() + + +def test_408_request_timeout() -> None: + # Should be able to send this spontaneously as a server without seeing + # anything from client + p = ConnectionPair() + p.send(SERVER, Response(status_code=408, headers=[(b"connection", b"close")])) + + +# This used to raise IndexError +def test_empty_request() -> None: + c = Connection(SERVER) + c.receive_data(b"\r\n") + with pytest.raises(RemoteProtocolError): + c.next_event() + + +# This used to raise IndexError +def test_empty_response() -> None: + c = Connection(CLIENT) + c.send(Request(method="GET", target="/", headers=[("Host", "a")])) + c.receive_data(b"\r\n") + with pytest.raises(RemoteProtocolError): + c.next_event() + + +@pytest.mark.parametrize( + "data", + [ + b"\x00", + b"\x20", + b"\x16\x03\x01\x00\xa5", # Typical start of a TLS Client Hello + ], +) +def test_early_detection_of_invalid_request(data: bytes) -> None: + c = Connection(SERVER) + # Early detection should occur before even receiving a `\r\n` + c.receive_data(data) + with pytest.raises(RemoteProtocolError): + c.next_event() + + +@pytest.mark.parametrize( + "data", + [ + b"\x00", + b"\x20", + b"\x16\x03\x03\x00\x31", # Typical start of a TLS Server Hello + ], +) +def test_early_detection_of_invalid_response(data: bytes) -> None: + c = Connection(CLIENT) + # Early detection should occur before even receiving a `\r\n` + c.receive_data(data) + with pytest.raises(RemoteProtocolError): + c.next_event() + + +# This used to give different headers for HEAD and GET. +# The correct way to handle HEAD is to put whatever headers we *would* have +# put if it were a GET -- even though we know that for HEAD, those headers +# will be ignored. +def test_HEAD_framing_headers() -> None: + def setup(method: bytes, http_version: bytes) -> Connection: + c = Connection(SERVER) + c.receive_data( + method + b" / HTTP/" + http_version + b"\r\n" + b"Host: example.com\r\n\r\n" + ) + assert type(c.next_event()) is Request + assert type(c.next_event()) is EndOfMessage + return c + + for method in [b"GET", b"HEAD"]: + # No Content-Length, HTTP/1.1 peer, should use chunked + c = setup(method, b"1.1") + assert ( + c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] + b"Transfer-Encoding: chunked\r\n\r\n" + ) + + # No Content-Length, HTTP/1.0 peer, frame with connection: close + c = setup(method, b"1.0") + assert ( + c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] + b"Connection: close\r\n\r\n" + ) + + # Content-Length + Transfer-Encoding, TE wins + c = setup(method, b"1.1") + assert ( + c.send( + Response( + status_code=200, + headers=[ + ("Content-Length", "100"), + ("Transfer-Encoding", "chunked"), + ], + ) + ) + == b"HTTP/1.1 200 \r\n" + b"Transfer-Encoding: chunked\r\n\r\n" + ) + + +def test_special_exceptions_for_lost_connection_in_message_body() -> None: + c = Connection(SERVER) + c.receive_data( + b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 100\r\n\r\n" + ) + assert type(c.next_event()) is Request + assert c.next_event() is NEED_DATA + c.receive_data(b"12345") + assert c.next_event() == Data(data=b"12345") + c.receive_data(b"") + with pytest.raises(RemoteProtocolError) as excinfo: + c.next_event() + assert "received 5 bytes" in str(excinfo.value) + assert "expected 100" in str(excinfo.value) + + c = Connection(SERVER) + c.receive_data( + b"POST / HTTP/1.1\r\n" + b"Host: example.com\r\n" + b"Transfer-Encoding: chunked\r\n\r\n" + ) + assert type(c.next_event()) is Request + assert c.next_event() is NEED_DATA + c.receive_data(b"8\r\n012345") + assert c.next_event().data == b"012345" # type: ignore + c.receive_data(b"") + with pytest.raises(RemoteProtocolError) as excinfo: + c.next_event() + assert "incomplete chunked read" in str(excinfo.value) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_events.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_events.py new file mode 100644 index 0000000..bc6c313 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_events.py @@ -0,0 +1,150 @@ +from http import HTTPStatus + +import pytest + +from .. import _events +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .._util import LocalProtocolError + + +def test_events() -> None: + with pytest.raises(LocalProtocolError): + # Missing Host: + req = Request( + method="GET", target="/", headers=[("a", "b")], http_version="1.1" + ) + # But this is okay (HTTP/1.0) + req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0") + # fields are normalized + assert req.method == b"GET" + assert req.target == b"/" + assert req.headers == [(b"a", b"b")] + assert req.http_version == b"1.0" + + # This is also okay -- has a Host (with weird capitalization, which is ok) + req = Request( + method="GET", + target="/", + headers=[("a", "b"), ("hOSt", "example.com")], + http_version="1.1", + ) + # we normalize header capitalization + assert req.headers == [(b"a", b"b"), (b"host", b"example.com")] + + # Multiple host is bad too + with pytest.raises(LocalProtocolError): + req = Request( + method="GET", + target="/", + headers=[("Host", "a"), ("Host", "a")], + http_version="1.1", + ) + # Even for HTTP/1.0 + with pytest.raises(LocalProtocolError): + req = Request( + method="GET", + target="/", + headers=[("Host", "a"), ("Host", "a")], + http_version="1.0", + ) + + # Header values are validated + for bad_char in "\x00\r\n\f\v": + with pytest.raises(LocalProtocolError): + req = Request( + method="GET", + target="/", + headers=[("Host", "a"), ("Foo", "asd" + bad_char)], + http_version="1.0", + ) + + # But for compatibility we allow non-whitespace control characters, even + # though they're forbidden by the spec. + Request( + method="GET", + target="/", + headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")], + http_version="1.0", + ) + + # Request target is validated + for bad_byte in b"\x00\x20\x7f\xee": + target = bytearray(b"/") + target.append(bad_byte) + with pytest.raises(LocalProtocolError): + Request( + method="GET", target=target, headers=[("Host", "a")], http_version="1.1" + ) + + # Request method is validated + with pytest.raises(LocalProtocolError): + Request( + method="GET / HTTP/1.1", + target=target, + headers=[("Host", "a")], + http_version="1.1", + ) + + ir = InformationalResponse(status_code=100, headers=[("Host", "a")]) + assert ir.status_code == 100 + assert ir.headers == [(b"host", b"a")] + assert ir.http_version == b"1.1" + + with pytest.raises(LocalProtocolError): + InformationalResponse(status_code=200, headers=[("Host", "a")]) + + resp = Response(status_code=204, headers=[], http_version="1.0") # type: ignore[arg-type] + assert resp.status_code == 204 + assert resp.headers == [] + assert resp.http_version == b"1.0" + + with pytest.raises(LocalProtocolError): + resp = Response(status_code=100, headers=[], http_version="1.0") # type: ignore[arg-type] + + with pytest.raises(LocalProtocolError): + Response(status_code="100", headers=[], http_version="1.0") # type: ignore[arg-type] + + with pytest.raises(LocalProtocolError): + InformationalResponse(status_code=b"100", headers=[], http_version="1.0") # type: ignore[arg-type] + + d = Data(data=b"asdf") + assert d.data == b"asdf" + + eom = EndOfMessage() + assert eom.headers == [] + + cc = ConnectionClosed() + assert repr(cc) == "ConnectionClosed()" + + +def test_intenum_status_code() -> None: + # https://github.com/python-hyper/h11/issues/72 + + r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0") # type: ignore[arg-type] + assert r.status_code == HTTPStatus.OK + assert type(r.status_code) is not type(HTTPStatus.OK) + assert type(r.status_code) is int + + +def test_header_casing() -> None: + r = Request( + method="GET", + target="/", + headers=[("Host", "example.org"), ("Connection", "keep-alive")], + http_version="1.1", + ) + assert len(r.headers) == 2 + assert r.headers[0] == (b"host", b"example.org") + assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")] + assert r.headers.raw_items() == [ + (b"Host", b"example.org"), + (b"Connection", b"keep-alive"), + ] diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_headers.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_headers.py new file mode 100644 index 0000000..ba53d08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_headers.py @@ -0,0 +1,157 @@ +import pytest + +from .._events import Request +from .._headers import ( + get_comma_header, + has_expect_100_continue, + Headers, + normalize_and_validate, + set_comma_header, +) +from .._util import LocalProtocolError + + +def test_normalize_and_validate() -> None: + assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")] + assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")] + + # no leading/trailing whitespace in names + with pytest.raises(LocalProtocolError): + normalize_and_validate([(b"foo ", "bar")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([(b" foo", "bar")]) + + # no weird characters in names + with pytest.raises(LocalProtocolError) as excinfo: + normalize_and_validate([(b"foo bar", b"baz")]) + assert "foo bar" in str(excinfo.value) + with pytest.raises(LocalProtocolError): + normalize_and_validate([(b"foo\x00bar", b"baz")]) + # Not even 8-bit characters: + with pytest.raises(LocalProtocolError): + normalize_and_validate([(b"foo\xffbar", b"baz")]) + # And not even the control characters we allow in values: + with pytest.raises(LocalProtocolError): + normalize_and_validate([(b"foo\x01bar", b"baz")]) + + # no return or NUL characters in values + with pytest.raises(LocalProtocolError) as excinfo: + normalize_and_validate([("foo", "bar\rbaz")]) + assert "bar\\rbaz" in str(excinfo.value) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", "bar\nbaz")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", "bar\x00baz")]) + # no leading/trailing whitespace + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", "barbaz ")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", " barbaz")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", "barbaz\t")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("foo", "\tbarbaz")]) + + # content-length + assert normalize_and_validate([("Content-Length", "1")]) == [ + (b"content-length", b"1") + ] + with pytest.raises(LocalProtocolError): + normalize_and_validate([("Content-Length", "asdf")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("Content-Length", "1x")]) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")]) + assert normalize_and_validate( + [("Content-Length", "0"), ("Content-Length", "0")] + ) == [(b"content-length", b"0")] + assert normalize_and_validate([("Content-Length", "0 , 0")]) == [ + (b"content-length", b"0") + ] + with pytest.raises(LocalProtocolError): + normalize_and_validate( + [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")] + ) + with pytest.raises(LocalProtocolError): + normalize_and_validate([("Content-Length", "1 , 1,2")]) + + # transfer-encoding + assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [ + (b"transfer-encoding", b"chunked") + ] + assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [ + (b"transfer-encoding", b"chunked") + ] + with pytest.raises(LocalProtocolError) as excinfo: + normalize_and_validate([("Transfer-Encoding", "gzip")]) + assert excinfo.value.error_status_hint == 501 # Not Implemented + with pytest.raises(LocalProtocolError) as excinfo: + normalize_and_validate( + [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")] + ) + assert excinfo.value.error_status_hint == 501 # Not Implemented + + +def test_get_set_comma_header() -> None: + headers = normalize_and_validate( + [ + ("Connection", "close"), + ("whatever", "something"), + ("connectiON", "fOo,, , BAR"), + ] + ) + + assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"] + + headers = set_comma_header(headers, b"newthing", ["a", "b"]) # type: ignore + + with pytest.raises(LocalProtocolError): + set_comma_header(headers, b"newthing", [" a", "b"]) # type: ignore + + assert headers == [ + (b"connection", b"close"), + (b"whatever", b"something"), + (b"connection", b"fOo,, , BAR"), + (b"newthing", b"a"), + (b"newthing", b"b"), + ] + + headers = set_comma_header(headers, b"whatever", ["different thing"]) # type: ignore + + assert headers == [ + (b"connection", b"close"), + (b"connection", b"fOo,, , BAR"), + (b"newthing", b"a"), + (b"newthing", b"b"), + (b"whatever", b"different thing"), + ] + + +def test_has_100_continue() -> None: + assert has_expect_100_continue( + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Expect", "100-continue")], + ) + ) + assert not has_expect_100_continue( + Request(method="GET", target="/", headers=[("Host", "example.com")]) + ) + # Case insensitive + assert has_expect_100_continue( + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Expect", "100-Continue")], + ) + ) + # Doesn't work in HTTP/1.0 + assert not has_expect_100_continue( + Request( + method="GET", + target="/", + headers=[("Host", "example.com"), ("Expect", "100-continue")], + http_version="1.0", + ) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_helpers.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_helpers.py new file mode 100644 index 0000000..c329c76 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_helpers.py @@ -0,0 +1,32 @@ +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .helpers import normalize_data_events + + +def test_normalize_data_events() -> None: + assert normalize_data_events( + [ + Data(data=bytearray(b"1")), + Data(data=b"2"), + Response(status_code=200, headers=[]), # type: ignore[arg-type] + Data(data=b"3"), + Data(data=b"4"), + EndOfMessage(), + Data(data=b"5"), + Data(data=b"6"), + Data(data=b"7"), + ] + ) == [ + Data(data=b"12"), + Response(status_code=200, headers=[]), # type: ignore[arg-type] + Data(data=b"34"), + EndOfMessage(), + Data(data=b"567"), + ] diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_io.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_io.py new file mode 100644 index 0000000..2b47c0e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_io.py @@ -0,0 +1,572 @@ +from typing import Any, Callable, Generator, List + +import pytest + +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .._headers import Headers, normalize_and_validate +from .._readers import ( + _obsolete_line_fold, + ChunkedReader, + ContentLengthReader, + Http10Reader, + READERS, +) +from .._receivebuffer import ReceiveBuffer +from .._state import ( + CLIENT, + CLOSED, + DONE, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from .._util import LocalProtocolError +from .._writers import ( + ChunkedWriter, + ContentLengthWriter, + Http10Writer, + write_any_response, + write_headers, + write_request, + WRITERS, +) +from .helpers import normalize_data_events + +SIMPLE_CASES = [ + ( + (CLIENT, IDLE), + Request( + method="GET", + target="/a", + headers=[("Host", "foo"), ("Connection", "close")], + ), + b"GET /a HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n", + ), + ( + (SERVER, SEND_RESPONSE), + Response(status_code=200, headers=[("Connection", "close")], reason=b"OK"), + b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", + ), + ( + (SERVER, SEND_RESPONSE), + Response(status_code=200, headers=[], reason=b"OK"), # type: ignore[arg-type] + b"HTTP/1.1 200 OK\r\n\r\n", + ), + ( + (SERVER, SEND_RESPONSE), + InformationalResponse( + status_code=101, headers=[("Upgrade", "websocket")], reason=b"Upgrade" + ), + b"HTTP/1.1 101 Upgrade\r\nUpgrade: websocket\r\n\r\n", + ), + ( + (SERVER, SEND_RESPONSE), + InformationalResponse(status_code=101, headers=[], reason=b"Upgrade"), # type: ignore[arg-type] + b"HTTP/1.1 101 Upgrade\r\n\r\n", + ), +] + + +def dowrite(writer: Callable[..., None], obj: Any) -> bytes: + got_list: List[bytes] = [] + writer(obj, got_list.append) + return b"".join(got_list) + + +def tw(writer: Any, obj: Any, expected: Any) -> None: + got = dowrite(writer, obj) + assert got == expected + + +def makebuf(data: bytes) -> ReceiveBuffer: + buf = ReceiveBuffer() + buf += data + return buf + + +def tr(reader: Any, data: bytes, expected: Any) -> None: + def check(got: Any) -> None: + assert got == expected + # Headers should always be returned as bytes, not e.g. bytearray + # https://github.com/python-hyper/wsproto/pull/54#issuecomment-377709478 + for name, value in getattr(got, "headers", []): + assert type(name) is bytes + assert type(value) is bytes + + # Simple: consume whole thing + buf = makebuf(data) + check(reader(buf)) + assert not buf + + # Incrementally growing buffer + buf = ReceiveBuffer() + for i in range(len(data)): + assert reader(buf) is None + buf += data[i : i + 1] + check(reader(buf)) + + # Trailing data + buf = makebuf(data) + buf += b"trailing" + check(reader(buf)) + assert bytes(buf) == b"trailing" + + +def test_writers_simple() -> None: + for ((role, state), event, binary) in SIMPLE_CASES: + tw(WRITERS[role, state], event, binary) + + +def test_readers_simple() -> None: + for ((role, state), event, binary) in SIMPLE_CASES: + tr(READERS[role, state], binary, event) + + +def test_writers_unusual() -> None: + # Simple test of the write_headers utility routine + tw( + write_headers, + normalize_and_validate([("foo", "bar"), ("baz", "quux")]), + b"foo: bar\r\nbaz: quux\r\n\r\n", + ) + tw(write_headers, Headers([]), b"\r\n") + + # We understand HTTP/1.0, but we don't speak it + with pytest.raises(LocalProtocolError): + tw( + write_request, + Request( + method="GET", + target="/", + headers=[("Host", "foo"), ("Connection", "close")], + http_version="1.0", + ), + None, + ) + with pytest.raises(LocalProtocolError): + tw( + write_any_response, + Response( + status_code=200, headers=[("Connection", "close")], http_version="1.0" + ), + None, + ) + + +def test_readers_unusual() -> None: + # Reading HTTP/1.0 + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.0\r\nSome: header\r\n\r\n", + Request( + method="HEAD", + target="/foo", + headers=[("Some", "header")], + http_version="1.0", + ), + ) + + # check no-headers, since it's only legal with HTTP/1.0 + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.0\r\n\r\n", + Request(method="HEAD", target="/foo", headers=[], http_version="1.0"), # type: ignore[arg-type] + ) + + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.0 200 OK\r\nSome: header\r\n\r\n", + Response( + status_code=200, + headers=[("Some", "header")], + http_version="1.0", + reason=b"OK", + ), + ) + + # single-character header values (actually disallowed by the ABNF in RFC + # 7230 -- this is a bug in the standard that we originally copied...) + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.0 200 OK\r\n" b"Foo: a a a a a \r\n\r\n", + Response( + status_code=200, + headers=[("Foo", "a a a a a")], + http_version="1.0", + reason=b"OK", + ), + ) + + # Empty headers -- also legal + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.0 200 OK\r\n" b"Foo:\r\n\r\n", + Response( + status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK" + ), + ) + + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.0 200 OK\r\n" b"Foo: \t \t \r\n\r\n", + Response( + status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK" + ), + ) + + # Tolerate broken servers that leave off the response code + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.0 200\r\n" b"Foo: bar\r\n\r\n", + Response( + status_code=200, headers=[("Foo", "bar")], http_version="1.0", reason=b"" + ), + ) + + # Tolerate headers line endings (\r\n and \n) + # \n\r\b between headers and body + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.1 200 OK\r\nSomeHeader: val\n\r\n", + Response( + status_code=200, + headers=[("SomeHeader", "val")], + http_version="1.1", + reason="OK", + ), + ) + + # delimited only with \n + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.1 200 OK\nSomeHeader1: val1\nSomeHeader2: val2\n\n", + Response( + status_code=200, + headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")], + http_version="1.1", + reason="OK", + ), + ) + + # mixed \r\n and \n + tr( + READERS[SERVER, SEND_RESPONSE], + b"HTTP/1.1 200 OK\r\nSomeHeader1: val1\nSomeHeader2: val2\n\r\n", + Response( + status_code=200, + headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")], + http_version="1.1", + reason="OK", + ), + ) + + # obsolete line folding + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" + b"Host: example.com\r\n" + b"Some: multi-line\r\n" + b" header\r\n" + b"\tnonsense\r\n" + b" \t \t\tI guess\r\n" + b"Connection: close\r\n" + b"More-nonsense: in the\r\n" + b" last header \r\n\r\n", + Request( + method="HEAD", + target="/foo", + headers=[ + ("Host", "example.com"), + ("Some", "multi-line header nonsense I guess"), + ("Connection", "close"), + ("More-nonsense", "in the last header"), + ], + ), + ) + + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" b" folded: line\r\n\r\n", + None, + ) + + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" b"foo : line\r\n\r\n", + None, + ) + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n", + None, + ) + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n", + None, + ) + with pytest.raises(LocalProtocolError): + tr(READERS[CLIENT, IDLE], b"HEAD /foo HTTP/1.1\r\n" b": line\r\n\r\n", None) + + +def test__obsolete_line_fold_bytes() -> None: + # _obsolete_line_fold has a defensive cast to bytearray, which is + # necessary to protect against O(n^2) behavior in case anyone ever passes + # in regular bytestrings... but right now we never pass in regular + # bytestrings. so this test just exists to get some coverage on that + # defensive cast. + assert list(_obsolete_line_fold([b"aaa", b"bbb", b" ccc", b"ddd"])) == [ + b"aaa", + bytearray(b"bbb ccc"), + b"ddd", + ] + + +def _run_reader_iter( + reader: Any, buf: bytes, do_eof: bool +) -> Generator[Any, None, None]: + while True: + event = reader(buf) + if event is None: + break + yield event + # body readers have undefined behavior after returning EndOfMessage, + # because this changes the state so they don't get called again + if type(event) is EndOfMessage: + break + if do_eof: + assert not buf + yield reader.read_eof() + + +def _run_reader(*args: Any) -> List[Event]: + events = list(_run_reader_iter(*args)) + return normalize_data_events(events) + + +def t_body_reader(thunk: Any, data: bytes, expected: Any, do_eof: bool = False) -> None: + # Simple: consume whole thing + print("Test 1") + buf = makebuf(data) + assert _run_reader(thunk(), buf, do_eof) == expected + + # Incrementally growing buffer + print("Test 2") + reader = thunk() + buf = ReceiveBuffer() + events = [] + for i in range(len(data)): + events += _run_reader(reader, buf, False) + buf += data[i : i + 1] + events += _run_reader(reader, buf, do_eof) + assert normalize_data_events(events) == expected + + is_complete = any(type(event) is EndOfMessage for event in expected) + if is_complete and not do_eof: + buf = makebuf(data + b"trailing") + assert _run_reader(thunk(), buf, False) == expected + + +def test_ContentLengthReader() -> None: + t_body_reader(lambda: ContentLengthReader(0), b"", [EndOfMessage()]) + + t_body_reader( + lambda: ContentLengthReader(10), + b"0123456789", + [Data(data=b"0123456789"), EndOfMessage()], + ) + + +def test_Http10Reader() -> None: + t_body_reader(Http10Reader, b"", [EndOfMessage()], do_eof=True) + t_body_reader(Http10Reader, b"asdf", [Data(data=b"asdf")], do_eof=False) + t_body_reader( + Http10Reader, b"asdf", [Data(data=b"asdf"), EndOfMessage()], do_eof=True + ) + + +def test_ChunkedReader() -> None: + t_body_reader(ChunkedReader, b"0\r\n\r\n", [EndOfMessage()]) + + t_body_reader( + ChunkedReader, + b"0\r\nSome: header\r\n\r\n", + [EndOfMessage(headers=[("Some", "header")])], + ) + + t_body_reader( + ChunkedReader, + b"5\r\n01234\r\n" + + b"10\r\n0123456789abcdef\r\n" + + b"0\r\n" + + b"Some: header\r\n\r\n", + [ + Data(data=b"012340123456789abcdef"), + EndOfMessage(headers=[("Some", "header")]), + ], + ) + + t_body_reader( + ChunkedReader, + b"5\r\n01234\r\n" + b"10\r\n0123456789abcdef\r\n" + b"0\r\n\r\n", + [Data(data=b"012340123456789abcdef"), EndOfMessage()], + ) + + # handles upper and lowercase hex + t_body_reader( + ChunkedReader, + b"aA\r\n" + b"x" * 0xAA + b"\r\n" + b"0\r\n\r\n", + [Data(data=b"x" * 0xAA), EndOfMessage()], + ) + + # refuses arbitrarily long chunk integers + with pytest.raises(LocalProtocolError): + # Technically this is legal HTTP/1.1, but we refuse to process chunk + # sizes that don't fit into 20 characters of hex + t_body_reader(ChunkedReader, b"9" * 100 + b"\r\nxxx", [Data(data=b"xxx")]) + + # refuses garbage in the chunk count + with pytest.raises(LocalProtocolError): + t_body_reader(ChunkedReader, b"10\x00\r\nxxx", None) + + # handles (and discards) "chunk extensions" omg wtf + t_body_reader( + ChunkedReader, + b"5; hello=there\r\n" + + b"xxxxx" + + b"\r\n" + + b'0; random="junk"; some=more; canbe=lonnnnngg\r\n\r\n', + [Data(data=b"xxxxx"), EndOfMessage()], + ) + + t_body_reader( + ChunkedReader, + b"5 \r\n01234\r\n" + b"0\r\n\r\n", + [Data(data=b"01234"), EndOfMessage()], + ) + + +def test_ContentLengthWriter() -> None: + w = ContentLengthWriter(5) + assert dowrite(w, Data(data=b"123")) == b"123" + assert dowrite(w, Data(data=b"45")) == b"45" + assert dowrite(w, EndOfMessage()) == b"" + + w = ContentLengthWriter(5) + with pytest.raises(LocalProtocolError): + dowrite(w, Data(data=b"123456")) + + w = ContentLengthWriter(5) + dowrite(w, Data(data=b"123")) + with pytest.raises(LocalProtocolError): + dowrite(w, Data(data=b"456")) + + w = ContentLengthWriter(5) + dowrite(w, Data(data=b"123")) + with pytest.raises(LocalProtocolError): + dowrite(w, EndOfMessage()) + + w = ContentLengthWriter(5) + dowrite(w, Data(data=b"123")) == b"123" + dowrite(w, Data(data=b"45")) == b"45" + with pytest.raises(LocalProtocolError): + dowrite(w, EndOfMessage(headers=[("Etag", "asdf")])) + + +def test_ChunkedWriter() -> None: + w = ChunkedWriter() + assert dowrite(w, Data(data=b"aaa")) == b"3\r\naaa\r\n" + assert dowrite(w, Data(data=b"a" * 20)) == b"14\r\n" + b"a" * 20 + b"\r\n" + + assert dowrite(w, Data(data=b"")) == b"" + + assert dowrite(w, EndOfMessage()) == b"0\r\n\r\n" + + assert ( + dowrite(w, EndOfMessage(headers=[("Etag", "asdf"), ("a", "b")])) + == b"0\r\nEtag: asdf\r\na: b\r\n\r\n" + ) + + +def test_Http10Writer() -> None: + w = Http10Writer() + assert dowrite(w, Data(data=b"1234")) == b"1234" + assert dowrite(w, EndOfMessage()) == b"" + + with pytest.raises(LocalProtocolError): + dowrite(w, EndOfMessage(headers=[("Etag", "asdf")])) + + +def test_reject_garbage_after_request_line() -> None: + with pytest.raises(LocalProtocolError): + tr(READERS[SERVER, SEND_RESPONSE], b"HTTP/1.0 200 OK\x00xxxx\r\n\r\n", None) + + +def test_reject_garbage_after_response_line() -> None: + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1 xxxxxx\r\n" b"Host: a\r\n\r\n", + None, + ) + + +def test_reject_garbage_in_header_line() -> None: + with pytest.raises(LocalProtocolError): + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" b"Host: foo\x00bar\r\n\r\n", + None, + ) + + +def test_reject_non_vchar_in_path() -> None: + for bad_char in b"\x00\x20\x7f\xee": + message = bytearray(b"HEAD /") + message.append(bad_char) + message.extend(b" HTTP/1.1\r\nHost: foobar\r\n\r\n") + with pytest.raises(LocalProtocolError): + tr(READERS[CLIENT, IDLE], message, None) + + +# https://github.com/python-hyper/h11/issues/57 +def test_allow_some_garbage_in_cookies() -> None: + tr( + READERS[CLIENT, IDLE], + b"HEAD /foo HTTP/1.1\r\n" + b"Host: foo\r\n" + b"Set-Cookie: ___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900\r\n" + b"\r\n", + Request( + method="HEAD", + target="/foo", + headers=[ + ("Host", "foo"), + ("Set-Cookie", "___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900"), + ], + ), + ) + + +def test_host_comes_first() -> None: + tw( + write_headers, + normalize_and_validate([("foo", "bar"), ("Host", "example.com")]), + b"Host: example.com\r\nfoo: bar\r\n\r\n", + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_receivebuffer.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_receivebuffer.py new file mode 100644 index 0000000..21a3870 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_receivebuffer.py @@ -0,0 +1,135 @@ +import re +from typing import Tuple + +import pytest + +from .._receivebuffer import ReceiveBuffer + + +def test_receivebuffer() -> None: + b = ReceiveBuffer() + assert not b + assert len(b) == 0 + assert bytes(b) == b"" + + b += b"123" + assert b + assert len(b) == 3 + assert bytes(b) == b"123" + + assert bytes(b) == b"123" + + assert b.maybe_extract_at_most(2) == b"12" + assert b + assert len(b) == 1 + assert bytes(b) == b"3" + + assert bytes(b) == b"3" + + assert b.maybe_extract_at_most(10) == b"3" + assert bytes(b) == b"" + + assert b.maybe_extract_at_most(10) is None + assert not b + + ################################################################ + # maybe_extract_until_next + ################################################################ + + b += b"123\n456\r\n789\r\n" + + assert b.maybe_extract_next_line() == b"123\n456\r\n" + assert bytes(b) == b"789\r\n" + + assert b.maybe_extract_next_line() == b"789\r\n" + assert bytes(b) == b"" + + b += b"12\r" + assert b.maybe_extract_next_line() is None + assert bytes(b) == b"12\r" + + b += b"345\n\r" + assert b.maybe_extract_next_line() is None + assert bytes(b) == b"12\r345\n\r" + + # here we stopped at the middle of b"\r\n" delimiter + + b += b"\n6789aaa123\r\n" + assert b.maybe_extract_next_line() == b"12\r345\n\r\n" + assert b.maybe_extract_next_line() == b"6789aaa123\r\n" + assert b.maybe_extract_next_line() is None + assert bytes(b) == b"" + + ################################################################ + # maybe_extract_lines + ################################################################ + + b += b"123\r\na: b\r\nfoo:bar\r\n\r\ntrailing" + lines = b.maybe_extract_lines() + assert lines == [b"123", b"a: b", b"foo:bar"] + assert bytes(b) == b"trailing" + + assert b.maybe_extract_lines() is None + + b += b"\r\n\r" + assert b.maybe_extract_lines() is None + + assert b.maybe_extract_at_most(100) == b"trailing\r\n\r" + assert not b + + # Empty body case (as happens at the end of chunked encoding if there are + # no trailing headers, e.g.) + b += b"\r\ntrailing" + assert b.maybe_extract_lines() == [] + assert bytes(b) == b"trailing" + + +@pytest.mark.parametrize( + "data", + [ + pytest.param( + ( + b"HTTP/1.1 200 OK\r\n", + b"Content-type: text/plain\r\n", + b"Connection: close\r\n", + b"\r\n", + b"Some body", + ), + id="with_crlf_delimiter", + ), + pytest.param( + ( + b"HTTP/1.1 200 OK\n", + b"Content-type: text/plain\n", + b"Connection: close\n", + b"\n", + b"Some body", + ), + id="with_lf_only_delimiter", + ), + pytest.param( + ( + b"HTTP/1.1 200 OK\n", + b"Content-type: text/plain\r\n", + b"Connection: close\n", + b"\n", + b"Some body", + ), + id="with_mixed_crlf_and_lf", + ), + ], +) +def test_receivebuffer_for_invalid_delimiter(data: Tuple[bytes]) -> None: + b = ReceiveBuffer() + + for line in data: + b += line + + lines = b.maybe_extract_lines() + + assert lines == [ + b"HTTP/1.1 200 OK", + b"Content-type: text/plain", + b"Connection: close", + ] + assert bytes(b) == b"Some body" diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_state.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_state.py new file mode 100644 index 0000000..bc974e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_state.py @@ -0,0 +1,271 @@ +import pytest + +from .._events import ( + ConnectionClosed, + Data, + EndOfMessage, + Event, + InformationalResponse, + Request, + Response, +) +from .._state import ( + _SWITCH_CONNECT, + _SWITCH_UPGRADE, + CLIENT, + CLOSED, + ConnectionState, + DONE, + IDLE, + MIGHT_SWITCH_PROTOCOL, + MUST_CLOSE, + SEND_BODY, + SEND_RESPONSE, + SERVER, + SWITCHED_PROTOCOL, +) +from .._util import LocalProtocolError + + +def test_ConnectionState() -> None: + cs = ConnectionState() + + # Basic event-triggered transitions + + assert cs.states == {CLIENT: IDLE, SERVER: IDLE} + + cs.process_event(CLIENT, Request) + # The SERVER-Request special case: + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + # Illegal transitions raise an error and nothing happens + with pytest.raises(LocalProtocolError): + cs.process_event(CLIENT, Request) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + cs.process_event(SERVER, InformationalResponse) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + cs.process_event(SERVER, Response) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY} + + cs.process_event(CLIENT, EndOfMessage) + cs.process_event(SERVER, EndOfMessage) + assert cs.states == {CLIENT: DONE, SERVER: DONE} + + # State-triggered transition + + cs.process_event(SERVER, ConnectionClosed) + assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} + + +def test_ConnectionState_keep_alive() -> None: + # keep_alive = False + cs = ConnectionState() + cs.process_event(CLIENT, Request) + cs.process_keep_alive_disabled() + cs.process_event(CLIENT, EndOfMessage) + assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE} + + cs.process_event(SERVER, Response) + cs.process_event(SERVER, EndOfMessage) + assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} + + +def test_ConnectionState_keep_alive_in_DONE() -> None: + # Check that if keep_alive is disabled when the CLIENT is already in DONE, + # then this is sufficient to immediately trigger the DONE -> MUST_CLOSE + # transition + cs = ConnectionState() + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + assert cs.states[CLIENT] is DONE + cs.process_keep_alive_disabled() + assert cs.states[CLIENT] is MUST_CLOSE + + +def test_ConnectionState_switch_denied() -> None: + for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE): + for deny_early in (True, False): + cs = ConnectionState() + cs.process_client_switch_proposal(switch_type) + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, Data) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + assert switch_type in cs.pending_switch_proposals + + if deny_early: + # before client reaches DONE + cs.process_event(SERVER, Response) + assert not cs.pending_switch_proposals + + cs.process_event(CLIENT, EndOfMessage) + + if deny_early: + assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} + else: + assert cs.states == { + CLIENT: MIGHT_SWITCH_PROTOCOL, + SERVER: SEND_RESPONSE, + } + + cs.process_event(SERVER, InformationalResponse) + assert cs.states == { + CLIENT: MIGHT_SWITCH_PROTOCOL, + SERVER: SEND_RESPONSE, + } + + cs.process_event(SERVER, Response) + assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} + assert not cs.pending_switch_proposals + + +_response_type_for_switch = { + _SWITCH_UPGRADE: InformationalResponse, + _SWITCH_CONNECT: Response, + None: Response, +} + + +def test_ConnectionState_protocol_switch_accepted() -> None: + for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]: + cs = ConnectionState() + cs.process_client_switch_proposal(switch_event) + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, Data) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + cs.process_event(CLIENT, EndOfMessage) + assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} + + cs.process_event(SERVER, InformationalResponse) + assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} + + cs.process_event(SERVER, _response_type_for_switch[switch_event], switch_event) + assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} + + +def test_ConnectionState_double_protocol_switch() -> None: + # CONNECT + Upgrade is legal! Very silly, but legal. So we support + # it. Because sometimes doing the silly thing is easier than not. + for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]: + cs = ConnectionState() + cs.process_client_switch_proposal(_SWITCH_UPGRADE) + cs.process_client_switch_proposal(_SWITCH_CONNECT) + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} + cs.process_event( + SERVER, _response_type_for_switch[server_switch], server_switch + ) + if server_switch is None: + assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} + else: + assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} + + +def test_ConnectionState_inconsistent_protocol_switch() -> None: + for client_switches, server_switch in [ + ([], _SWITCH_CONNECT), + ([], _SWITCH_UPGRADE), + ([_SWITCH_UPGRADE], _SWITCH_CONNECT), + ([_SWITCH_CONNECT], _SWITCH_UPGRADE), + ]: + cs = ConnectionState() + for client_switch in client_switches: # type: ignore[attr-defined] + cs.process_client_switch_proposal(client_switch) + cs.process_event(CLIENT, Request) + with pytest.raises(LocalProtocolError): + cs.process_event(SERVER, Response, server_switch) + + +def test_ConnectionState_keepalive_protocol_switch_interaction() -> None: + # keep_alive=False + pending_switch_proposals + cs = ConnectionState() + cs.process_client_switch_proposal(_SWITCH_UPGRADE) + cs.process_event(CLIENT, Request) + cs.process_keep_alive_disabled() + cs.process_event(CLIENT, Data) + assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} + + # the protocol switch "wins" + cs.process_event(CLIENT, EndOfMessage) + assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} + + # but when the server denies the request, keep_alive comes back into play + cs.process_event(SERVER, Response) + assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY} + + +def test_ConnectionState_reuse() -> None: + cs = ConnectionState() + + with pytest.raises(LocalProtocolError): + cs.start_next_cycle() + + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + + with pytest.raises(LocalProtocolError): + cs.start_next_cycle() + + cs.process_event(SERVER, Response) + cs.process_event(SERVER, EndOfMessage) + + cs.start_next_cycle() + assert cs.states == {CLIENT: IDLE, SERVER: IDLE} + + # No keepalive + + cs.process_event(CLIENT, Request) + cs.process_keep_alive_disabled() + cs.process_event(CLIENT, EndOfMessage) + cs.process_event(SERVER, Response) + cs.process_event(SERVER, EndOfMessage) + + with pytest.raises(LocalProtocolError): + cs.start_next_cycle() + + # One side closed + + cs = ConnectionState() + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + cs.process_event(CLIENT, ConnectionClosed) + cs.process_event(SERVER, Response) + cs.process_event(SERVER, EndOfMessage) + + with pytest.raises(LocalProtocolError): + cs.start_next_cycle() + + # Succesful protocol switch + + cs = ConnectionState() + cs.process_client_switch_proposal(_SWITCH_UPGRADE) + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE) + + with pytest.raises(LocalProtocolError): + cs.start_next_cycle() + + # Failed protocol switch + + cs = ConnectionState() + cs.process_client_switch_proposal(_SWITCH_UPGRADE) + cs.process_event(CLIENT, Request) + cs.process_event(CLIENT, EndOfMessage) + cs.process_event(SERVER, Response) + cs.process_event(SERVER, EndOfMessage) + + cs.start_next_cycle() + assert cs.states == {CLIENT: IDLE, SERVER: IDLE} + + +def test_server_request_is_illegal() -> None: + # There used to be a bug in how we handled the Request special case that + # made this allowed... + cs = ConnectionState() + with pytest.raises(LocalProtocolError): + cs.process_event(SERVER, Request) diff --git a/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_util.py b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_util.py new file mode 100644 index 0000000..79bc095 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/h11/tests/test_util.py @@ -0,0 +1,112 @@ +import re +import sys +import traceback +from typing import NoReturn + +import pytest + +from .._util import ( + bytesify, + LocalProtocolError, + ProtocolError, + RemoteProtocolError, + Sentinel, + validate, +) + + +def test_ProtocolError() -> None: + with pytest.raises(TypeError): + ProtocolError("abstract base class") + + +def test_LocalProtocolError() -> None: + try: + raise LocalProtocolError("foo") + except LocalProtocolError as e: + assert str(e) == "foo" + assert e.error_status_hint == 400 + + try: + raise LocalProtocolError("foo", error_status_hint=418) + except LocalProtocolError as e: + assert str(e) == "foo" + assert e.error_status_hint == 418 + + def thunk() -> NoReturn: + raise LocalProtocolError("a", error_status_hint=420) + + try: + try: + thunk() + except LocalProtocolError as exc1: + orig_traceback = "".join(traceback.format_tb(sys.exc_info()[2])) + exc1._reraise_as_remote_protocol_error() + except RemoteProtocolError as exc2: + assert type(exc2) is RemoteProtocolError + assert exc2.args == ("a",) + assert exc2.error_status_hint == 420 + new_traceback = "".join(traceback.format_tb(sys.exc_info()[2])) + assert new_traceback.endswith(orig_traceback) + + +def test_validate() -> None: + my_re = re.compile(rb"(?P[0-9]+)\.(?P[0-9]+)") + with pytest.raises(LocalProtocolError): + validate(my_re, b"0.") + + groups = validate(my_re, b"0.1") + assert groups == {"group1": b"0", "group2": b"1"} + + # successful partial matches are an error - must match whole string + with pytest.raises(LocalProtocolError): + validate(my_re, b"0.1xx") + with pytest.raises(LocalProtocolError): + validate(my_re, b"0.1\n") + + +def test_validate_formatting() -> None: + my_re = re.compile(rb"foo") + + with pytest.raises(LocalProtocolError) as excinfo: + validate(my_re, b"", "oops") + assert "oops" in str(excinfo.value) + + with pytest.raises(LocalProtocolError) as excinfo: + validate(my_re, b"", "oops {}") + assert "oops {}" in str(excinfo.value) + + with pytest.raises(LocalProtocolError) as excinfo: + validate(my_re, b"", "oops {} xx", 10) + assert "oops 10 xx" in str(excinfo.value) + + +def test_make_sentinel() -> None: + class S(Sentinel, metaclass=Sentinel): + pass + + assert repr(S) == "S" + assert S == S + assert type(S).__name__ == "S" + assert S in {S} + assert type(S) is S + + class S2(Sentinel, metaclass=Sentinel): + pass + + assert repr(S2) == "S2" + assert S != S2 + assert S not in {S2} + assert type(S) is not type(S2) + + +def test_bytesify() -> None: + assert bytesify(b"123") == b"123" + assert bytesify(bytearray(b"123")) == b"123" + assert bytesify("123") == b"123" + + with pytest.raises(UnicodeEncodeError): + bytesify("\u1234") + + with pytest.raises(TypeError): + bytesify(10) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/METADATA new file mode 100644 index 0000000..0e20095 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/METADATA @@ -0,0 +1,607 @@ +Metadata-Version: 2.3 +Name: httpcore +Version: 1.0.5 +Summary: A minimal low-level HTTP client. +Project-URL: Documentation, https://www.encode.io/httpcore +Project-URL: Homepage, https://www.encode.io/httpcore/ +Project-URL: Source, https://github.com/encode/httpcore +Author-email: Tom Christie +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 3 - Alpha +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: certifi +Requires-Dist: h11<0.15,>=0.13 +Provides-Extra: asyncio +Requires-Dist: anyio<5.0,>=4.0; extra == 'asyncio' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Provides-Extra: trio +Requires-Dist: trio<0.26.0,>=0.22.0; extra == 'trio' +Description-Content-Type: text/markdown + +# HTTP Core + +[![Test Suite](https://github.com/encode/httpcore/workflows/Test%20Suite/badge.svg)](https://github.com/encode/httpcore/actions) +[![Package version](https://badge.fury.io/py/httpcore.svg)](https://pypi.org/project/httpcore/) + +> *Do one thing, and do it well.* + +The HTTP Core package provides a minimal low-level HTTP client, which does +one thing only. Sending HTTP requests. + +It does not provide any high level model abstractions over the API, +does not handle redirects, multipart uploads, building authentication headers, +transparent HTTP caching, URL parsing, session cookie handling, +content or charset decoding, handling JSON, environment based configuration +defaults, or any of that Jazz. + +Some things HTTP Core does do: + +* Sending HTTP requests. +* Thread-safe / task-safe connection pooling. +* HTTP(S) proxy & SOCKS proxy support. +* Supports HTTP/1.1 and HTTP/2. +* Provides both sync and async interfaces. +* Async backend support for `asyncio` and `trio`. + +## Requirements + +Python 3.8+ + +## Installation + +For HTTP/1.1 only support, install with: + +```shell +$ pip install httpcore +``` + +There are also a number of optional extras available... + +```shell +$ pip install httpcore['asyncio,trio,http2,socks'] +``` + +# Sending requests + +Send an HTTP request: + +```python +import httpcore + +response = httpcore.request("GET", "https://www.example.com/") + +print(response) +# +print(response.status) +# 200 +print(response.headers) +# [(b'Accept-Ranges', b'bytes'), (b'Age', b'557328'), (b'Cache-Control', b'max-age=604800'), ...] +print(response.content) +# b'\n\n\nExample Domain\n\n\n ...' +``` + +The top-level `httpcore.request()` function is provided for convenience. In practice whenever you're working with `httpcore` you'll want to use the connection pooling functionality that it provides. + +```python +import httpcore + +http = httpcore.ConnectionPool() +response = http.request("GET", "https://www.example.com/") +``` + +Once you're ready to get going, [head over to the documentation](https://www.encode.io/httpcore/). + +## Motivation + +You *probably* don't want to be using HTTP Core directly. It might make sense if +you're writing something like a proxy service in Python, and you just want +something at the lowest possible level, but more typically you'll want to use +a higher level client library, such as `httpx`. + +The motivation for `httpcore` is: + +* To provide a reusable low-level client library, that other packages can then build on top of. +* To provide a *really clear interface split* between the networking code and client logic, + so that each is easier to understand and reason about in isolation. + +## Dependencies + +The `httpcore` package has the following dependencies... + +* `h11` +* `certifi` + +And the following optional extras... + +* `anyio` - Required by `pip install httpcore['asyncio']`. +* `trio` - Required by `pip install httpcore['trio']`. +* `h2` - Required by `pip install httpcore['http2']`. +* `socksio` - Required by `pip install httpcore['socks']`. + +## Versioning + +We use [SEMVER for our versioning policy](https://semver.org/). + +For changes between package versions please see our [project changelog](CHANGELOG.md). + +We recommend pinning your requirements either the most current major version, or a more specific version range: + +```python +pip install 'httpcore==1.*' +``` +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## 1.0.5 (March 27th, 2024) + +- Handle `EndOfStream` exception for anyio backend. (#899) +- Allow trio `0.25.*` series in package dependancies. (#903) + +## 1.0.4 (February 21st, 2024) + +- Add `target` request extension. (#888) +- Fix support for connection `Upgrade` and `CONNECT` when some data in the stream has been read. (#882) + +## 1.0.3 (February 13th, 2024) + +- Fix support for async cancellations. (#880) +- Fix trace extension when used with socks proxy. (#849) +- Fix SSL context for connections using the "wss" scheme (#869) + +## 1.0.2 (November 10th, 2023) + +- Fix `float("inf")` timeouts in `Event.wait` function. (#846) + +## 1.0.1 (November 3rd, 2023) + +- Fix pool timeout to account for the total time spent retrying. (#823) +- Raise a neater RuntimeError when the correct async deps are not installed. (#826) +- Add support for synchronous TLS-in-TLS streams. (#840) + +## 1.0.0 (October 6th, 2023) + +From version 1.0 our async support is now optional, as the package has minimal dependencies by default. + +For async support use either `pip install 'httpcore[asyncio]'` or `pip install 'httpcore[trio]'`. + +The project versioning policy is now explicitly governed by SEMVER. See https://semver.org/. + +- Async support becomes fully optional. (#809) +- Add support for Python 3.12. (#807) + +## 0.18.0 (September 8th, 2023) + +- Add support for HTTPS proxies. (#745, #786) +- Drop Python 3.7 support. (#727) +- Handle `sni_hostname` extension with SOCKS proxy. (#774) +- Handle HTTP/1.1 half-closed connections gracefully. (#641) +- Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#762) + +## 0.17.3 (July 5th, 2023) + +- Support async cancellations, ensuring that the connection pool is left in a clean state when cancellations occur. (#726) +- The networking backend interface has [been added to the public API](https://www.encode.io/httpcore/network-backends). Some classes which were previously private implementation detail are now part of the top-level public API. (#699) +- Graceful handling of HTTP/2 GoAway frames, with requests being transparently retried on a new connection. (#730) +- Add exceptions when a synchronous `trace callback` is passed to an asynchronous request or an asynchronous `trace callback` is passed to a synchronous request. (#717) +- Drop Python 3.7 support. (#727) + +## 0.17.2 (May 23th, 2023) + +- Add `socket_options` argument to `ConnectionPool` and `HTTProxy` classes. (#668) +- Improve logging with per-module logger names. (#690) +- Add `sni_hostname` request extension. (#696) +- Resolve race condition during import of `anyio` package. (#692) +- Enable TCP_NODELAY for all synchronous sockets. (#651) + +## 0.17.1 (May 17th, 2023) + +- If 'retries' is set, then allow retries if an SSL handshake error occurs. (#669) +- Improve correctness of tracebacks on network exceptions, by raising properly chained exceptions. (#678) +- Prevent connection-hanging behaviour when HTTP/2 connections are closed by a server-sent 'GoAway' frame. (#679) +- Fix edge-case exception when removing requests from the connection pool. (#680) +- Fix pool timeout edge-case. (#688) + +## 0.17.0 (March 16th, 2023) + +- Add DEBUG level logging. (#648) +- Respect HTTP/2 max concurrent streams when settings updates are sent by server. (#652) +- Increase the allowable HTTP header size to 100kB. (#647) +- Add `retries` option to SOCKS proxy classes. (#643) + +## 0.16.3 (December 20th, 2022) + +- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625) +- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637) +- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631) +- Lazy import `anyio`, so that it's no longer a hard dependancy, and isn't imported if unused. (#639) + +## 0.16.2 (November 25th, 2022) + +- Revert 'Fix async cancellation behaviour', which introduced race conditions. (#627) +- Raise `RuntimeError` if attempting to us UNIX domain sockets on Windows. (#619) + +## 0.16.1 (November 17th, 2022) + +- Fix HTTP/1.1 interim informational responses, such as "100 Continue". (#605) + +## 0.16.0 (October 11th, 2022) + +- Support HTTP/1.1 informational responses. (#581) +- Fix async cancellation behaviour. (#580) +- Support `h11` 0.14. (#579) + +## 0.15.0 (May 17th, 2022) + +- Drop Python 3.6 support (#535) +- Ensure HTTP proxy CONNECT requests include `timeout` configuration. (#506) +- Switch to explicit `typing.Optional` for type hints. (#513) +- For `trio` map OSError exceptions to `ConnectError`. (#543) + +## 0.14.7 (February 4th, 2022) + +- Requests which raise a PoolTimeout need to be removed from the pool queue. (#502) +- Fix AttributeError that happened when Socks5Connection were terminated. (#501) + +## 0.14.6 (February 1st, 2022) + +- Fix SOCKS support for `http://` URLs. (#492) +- Resolve race condition around exceptions during streaming a response. (#491) + +## 0.14.5 (January 18th, 2022) + +- SOCKS proxy support. (#478) +- Add proxy_auth argument to HTTPProxy. (#481) +- Improve error message on 'RemoteProtocolError' exception when server disconnects without sending a response. (#479) + +## 0.14.4 (January 5th, 2022) + +- Support HTTP/2 on HTTPS tunnelling proxies. (#468) +- Fix proxy headers missing on HTTP forwarding. (#456) +- Only instantiate SSL context if required. (#457) +- More robust HTTP/2 handling. (#253, #439, #440, #441) + +## 0.14.3 (November 17th, 2021) + +- Fix race condition when removing closed connections from the pool. (#437) + +## 0.14.2 (November 16th, 2021) + +- Failed connections no longer remain in the pool. (Pull #433) + +## 0.14.1 (November 12th, 2021) + +- `max_connections` becomes optional. (Pull #429) +- `certifi` is now included in the install dependancies. (Pull #428) +- `h2` is now strictly optional. (Pull #428) + +## 0.14.0 (November 11th, 2021) + +The 0.14 release is a complete reworking of `httpcore`, comprehensively addressing some underlying issues in the connection pooling, as well as substantially redesigning the API to be more user friendly. + +Some of the lower-level API design also makes the components more easily testable in isolation, and the package now has 100% test coverage. + +See [discussion #419](https://github.com/encode/httpcore/discussions/419) for a little more background. + +There's some other neat bits in there too, such as the "trace" extension, which gives a hook into inspecting the internal events that occur during the request/response cycle. This extension is needed for the HTTPX cli, in order to... + +* Log the point at which the connection is established, and the IP/port on which it is made. +* Determine if the outgoing request should log as HTTP/1.1 or HTTP/2, rather than having to assume it's HTTP/2 if the --http2 flag was passed. (Which may not actually be true.) +* Log SSL version info / certificate info. + +Note that `curio` support is not currently available in 0.14.0. If you're using `httpcore` with `curio` please get in touch, so we can assess if we ought to prioritize it as a feature or not. + +## 0.13.7 (September 13th, 2021) + +- Fix broken error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #403) + +## 0.13.6 (June 15th, 2021) + +### Fixed + +- Close sockets when read or write timeouts occur. (Pull #365) + +## 0.13.5 (June 14th, 2021) + +### Fixed + +- Resolved niggles with AnyIO EOF behaviours. (Pull #358, #362) + +## 0.13.4 (June 9th, 2021) + +### Added + +- Improved error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #354) + +### Fixed + +- Switched to `anyio` as the default backend implementation when running with `asyncio`. Resolves some awkward [TLS timeout issues](https://github.com/encode/httpx/discussions/1511). + +## 0.13.3 (May 6th, 2021) + +### Added + +- Support HTTP/2 prior knowledge, using `httpcore.SyncConnectionPool(http1=False)`. (Pull #333) + +### Fixed + +- Handle cases where environment does not provide `select.poll` support. (Pull #331) + +## 0.13.2 (April 29th, 2021) + +### Added + +- Improve error message for specific case of `RemoteProtocolError` where server disconnects without sending a response. (Pull #313) + +## 0.13.1 (April 28th, 2021) + +### Fixed + +- More resiliant testing for closed connections. (Pull #311) +- Don't raise exceptions on ungraceful connection closes. (Pull #310) + +## 0.13.0 (April 21st, 2021) + +The 0.13 release updates the core API in order to match the HTTPX Transport API, +introduced in HTTPX 0.18 onwards. + +An example of making requests with the new interface is: + +```python +with httpcore.SyncConnectionPool() as http: + status_code, headers, stream, extensions = http.handle_request( + method=b'GET', + url=(b'https', b'example.org', 443, b'/'), + headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')] + stream=httpcore.ByteStream(b''), + extensions={} + ) + body = stream.read() + print(status_code, body) +``` + +### Changed + +- The `.request()` method is now `handle_request()`. (Pull #296) +- The `.arequest()` method is now `.handle_async_request()`. (Pull #296) +- The `headers` argument is no longer optional. (Pull #296) +- The `stream` argument is no longer optional. (Pull #296) +- The `ext` argument is now named `extensions`, and is no longer optional. (Pull #296) +- The `"reason"` extension keyword is now named `"reason_phrase"`. (Pull #296) +- The `"reason_phrase"` and `"http_version"` extensions now use byte strings for their values. (Pull #296) +- The `httpcore.PlainByteStream()` class becomes `httpcore.ByteStream()`. (Pull #296) + +### Added + +- Streams now support a `.read()` interface. (Pull #296) + +### Fixed + +- Task cancellation no longer leaks connections from the connection pool. (Pull #305) + +## 0.12.3 (December 7th, 2020) + +### Fixed + +- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`. (Pull #167) +- Fix exception raised in case of connect timeouts when using the `anyio` backend. (Pull #236) +- Fix `Host` header precedence for `:authority` in HTTP/2. (Pull #241, #243) +- Handle extra edge case when detecting for socket readability when using `asyncio`. (Pull #242, #244) +- Fix `asyncio` SSL warning when using proxy tunneling. (Pull #249) + +## 0.12.2 (November 20th, 2020) + +### Fixed + +- Properly wrap connect errors on the asyncio backend. (Pull #235) +- Fix `ImportError` occurring on Python 3.9 when using the HTTP/1.1 sync client in a multithreaded context. (Pull #237) + +## 0.12.1 (November 7th, 2020) + +### Added + +- Add connect retries. (Pull #221) + +### Fixed + +- Tweak detection of dropped connections, resolving an issue with open files limits on Linux. (Pull #185) +- Avoid leaking connections when establishing an HTTP tunnel to a proxy has failed. (Pull #223) +- Properly wrap OS errors when using `trio`. (Pull #225) + +## 0.12.0 (October 6th, 2020) + +### Changed + +- HTTP header casing is now preserved, rather than always sent in lowercase. (#216 and python-hyper/h11#104) + +### Added + +- Add Python 3.9 to officially supported versions. + +### Fixed + +- Gracefully handle a stdlib asyncio bug when a connection is closed while it is in a paused-for-reading state. (#201) + +## 0.11.1 (September 28nd, 2020) + +### Fixed + +- Add await to async semaphore release() coroutine (#197) +- Drop incorrect curio classifier (#192) + +## 0.11.0 (September 22nd, 2020) + +The Transport API with 0.11.0 has a couple of significant changes. + +Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features +such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections. + +The interface changes from: + +```python +def request(method, url, headers, stream, timeout): + return (http_version, status_code, reason, headers, stream) +``` + +To instead including an optional dictionary of extensions on the request and response: + +```python +def request(method, url, headers, stream, ext): + return (status_code, headers, stream, ext) +``` + +Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes. + +In particular: + +* Trailing headers support. +* HTTP/2 Server Push +* sendfile. +* Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming. +* Exposing debug information out of the API, including template name, template context. + +Currently extensions are limited to: + +* request: `timeout` - Optional. Timeout dictionary. +* response: `http_version` - Optional. Include the HTTP version used on the response. +* response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*. + +See https://github.com/encode/httpx/issues/1274#issuecomment-694884553 for the history behind this. + +Secondly, the async version of `request` is now namespaced as `arequest`. + +This allows concrete transports to support both sync and async implementations on the same class. + +### Added + +- Add curio support. (Pull #168) +- Add anyio support, with `backend="anyio"`. (Pull #169) + +### Changed + +- Update the Transport API to use 'ext' for optional extensions. (Pull #190) +- Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. (Pull #189) + +## 0.10.2 (August 20th, 2020) + +### Added + +- Added Unix Domain Socket support. (Pull #139) + +### Fixed + +- Always include the port on proxy CONNECT requests. (Pull #154) +- Fix `max_keepalive_connections` configuration. (Pull #153) +- Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. (Pull #164) + +## 0.10.1 (August 7th, 2020) + +- Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes. + +## 0.10.0 (August 7th, 2020) + +The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional. + +Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support. + +### Added + +- HTTP/2 support becomes optional. (Pull #121, #130) +- Add `local_address=...` support. (Pull #100, #134) +- Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. (#133) +- Add `LocalProtocolError`, `RemoteProtocolError` exceptions. (Pull #129) +- Add `UnsupportedProtocol` exception. (Pull #128) +- Add `.get_connection_info()` method. (Pull #102, #137) +- Add better TRACE logs. (Pull #101) + +### Changed + +- `max_keepalive` is deprecated in favour of `max_keepalive_connections`. (Pull #140) + +### Fixed + +- Improve handling of server disconnects. (Pull #112) + +## 0.9.1 (May 27th, 2020) + +### Fixed + +- Proper host resolution for sync case, including IPv6 support. (Pull #97) +- Close outstanding connections when connection pool is closed. (Pull #98) + +## 0.9.0 (May 21th, 2020) + +### Changed + +- URL port becomes an `Optional[int]` instead of `int`. (Pull #92) + +### Fixed + +- Honor HTTP/2 max concurrent streams settings. (Pull #89, #90) +- Remove incorrect debug log. (Pull #83) + +## 0.8.4 (May 11th, 2020) + +### Added + +- Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables +and TRACE level logging. (Pull #79) + +### Fixed + +- Reuse of connections on HTTP/2 in close concurrency situations. (Pull #81) + +## 0.8.3 (May 6rd, 2020) + +### Fixed + +- Include `Host` and `Accept` headers on proxy "CONNECT" requests. +- De-duplicate any headers also contained in proxy_headers. +- HTTP/2 flag not being passed down to proxy connections. + +## 0.8.2 (May 3rd, 2020) + +### Fixed + +- Fix connections using proxy forwarding requests not being added to the +connection pool properly. (Pull #70) + +## 0.8.1 (April 30th, 2020) + +### Changed + +- Allow inherintance of both `httpcore.AsyncByteStream`, `httpcore.SyncByteStream` without type conflicts. + +## 0.8.0 (April 30th, 2020) + +### Fixed + +- Fixed tunnel proxy support. + +### Added + +- New `TimeoutException` base class. + +## 0.7.0 (March 5th, 2020) + +- First integration with HTTPX. diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/RECORD new file mode 100644 index 0000000..1c09ef7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/RECORD @@ -0,0 +1,68 @@ +httpcore-1.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpcore-1.0.5.dist-info/METADATA,sha256=YgW4guGB2OBYCvLOVSIgUoXRbjO-aWDbHXnYIUaJzTU,20969 +httpcore-1.0.5.dist-info/RECORD,, +httpcore-1.0.5.dist-info/WHEEL,sha256=uNdcs2TADwSd5pVaP0Z_kcjcvvTUklh2S7bxZMF8Uj0,87 +httpcore-1.0.5.dist-info/licenses/LICENSE.md,sha256=_ctZFUx0y6uhahEkL3dAvqnyPW_rVUeRfYxflKgDkqU,1518 +httpcore/__init__.py,sha256=fBzHStfNOukxTWXQFx6W_fv0MAcKntslGJqhHliu45E,3337 +httpcore/__pycache__/__init__.cpython-310.pyc,, +httpcore/__pycache__/_api.cpython-310.pyc,, +httpcore/__pycache__/_exceptions.cpython-310.pyc,, +httpcore/__pycache__/_models.cpython-310.pyc,, +httpcore/__pycache__/_ssl.cpython-310.pyc,, +httpcore/__pycache__/_synchronization.cpython-310.pyc,, +httpcore/__pycache__/_trace.cpython-310.pyc,, +httpcore/__pycache__/_utils.cpython-310.pyc,, +httpcore/_api.py,sha256=IBR18qZQ8ETcghJXC1Gd-30WuKYRS0EyF2eC80_OBQ8,3167 +httpcore/_async/__init__.py,sha256=EWdl2v4thnAHzJpqjU4h2a8DUiGAvNiWrkii9pfhTf0,1221 +httpcore/_async/__pycache__/__init__.cpython-310.pyc,, +httpcore/_async/__pycache__/connection.cpython-310.pyc,, +httpcore/_async/__pycache__/connection_pool.cpython-310.pyc,, +httpcore/_async/__pycache__/http11.cpython-310.pyc,, +httpcore/_async/__pycache__/http2.cpython-310.pyc,, +httpcore/_async/__pycache__/http_proxy.cpython-310.pyc,, +httpcore/_async/__pycache__/interfaces.cpython-310.pyc,, +httpcore/_async/__pycache__/socks_proxy.cpython-310.pyc,, +httpcore/_async/connection.py,sha256=63vgzLIgX3bjq-RsjK68UWS_DWNkdvnKP0vJRK3Prfs,8484 +httpcore/_async/connection_pool.py,sha256=vjyIrwkj3QfzndhXOBrlyplJidWnhrj0foAmIpSj5tA,15609 +httpcore/_async/http11.py,sha256=yvohHUXwdJv9gN-dEJv4C5F8_NiyOtcflua1Q3BRjew,13978 +httpcore/_async/http2.py,sha256=_AgUDRcjAIlbncbOjW0I-iqYN1PDgRcFUIGfzZ2fKcI,23881 +httpcore/_async/http_proxy.py,sha256=hl4t-PahlAuCGtKNYRx4LSgjx1ZuspE9oDBaL6BOess,14851 +httpcore/_async/interfaces.py,sha256=J2iq9rs7x3nKS6iCfntjHY0Woast6V_HuXuE8rs3HmA,4486 +httpcore/_async/socks_proxy.py,sha256=T8y927RATyy4A9GMduRVUh13ZeRq8Ts8JP24bFVQ6n8,13934 +httpcore/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpcore/_backends/__pycache__/__init__.cpython-310.pyc,, +httpcore/_backends/__pycache__/anyio.cpython-310.pyc,, +httpcore/_backends/__pycache__/auto.cpython-310.pyc,, +httpcore/_backends/__pycache__/base.cpython-310.pyc,, +httpcore/_backends/__pycache__/mock.cpython-310.pyc,, +httpcore/_backends/__pycache__/sync.cpython-310.pyc,, +httpcore/_backends/__pycache__/trio.cpython-310.pyc,, +httpcore/_backends/anyio.py,sha256=3SbX3SMTwT4SYmoTWbzWazwZg0mfQ3jDR8lI_n9JKr8,5295 +httpcore/_backends/auto.py,sha256=Q_iQjNuwJseqBxeYJYtiaGzFs08_LGI3K_egYrixEqE,1683 +httpcore/_backends/base.py,sha256=Qsb8b_PSiVP1ldHHGXHxQzJ1Qlzj2r8KR9KQeANkSbE,3218 +httpcore/_backends/mock.py,sha256=S4IADhC6kE22ge_jR_WHlEUkD6QAsXnwz26DSWZLcG4,4179 +httpcore/_backends/sync.py,sha256=LAomvc-MAlot5-S9CCFxnr561aDp9yhyfs_65WeCkZ4,8086 +httpcore/_backends/trio.py,sha256=INOeHEkA8pO6AsSqjColWcayM0FQSyGi1hpaQghjrCs,6078 +httpcore/_exceptions.py,sha256=7zb3KNiG0qmfUNIdFgdaUSbn2Pu3oztghi6Vg7i-LJU,1185 +httpcore/_models.py,sha256=7DlYrkyc2z-orQrnztCUmtBY4gLMz18FjPP9e5Q-fFg,16614 +httpcore/_ssl.py,sha256=srqmSNU4iOUvWF-SrJvb8G_YEbHFELOXQOwdDIBTS9c,187 +httpcore/_sync/__init__.py,sha256=JBDIgXt5la1LCJ1sLQeKhjKFpLnpNr8Svs6z2ni3fgg,1141 +httpcore/_sync/__pycache__/__init__.cpython-310.pyc,, +httpcore/_sync/__pycache__/connection.cpython-310.pyc,, +httpcore/_sync/__pycache__/connection_pool.cpython-310.pyc,, +httpcore/_sync/__pycache__/http11.cpython-310.pyc,, +httpcore/_sync/__pycache__/http2.cpython-310.pyc,, +httpcore/_sync/__pycache__/http_proxy.cpython-310.pyc,, +httpcore/_sync/__pycache__/interfaces.cpython-310.pyc,, +httpcore/_sync/__pycache__/socks_proxy.cpython-310.pyc,, +httpcore/_sync/connection.py,sha256=n7YFLjYsRv4cf0CXEIqNsUqR_NPNvFQN8dGqjj0mv9U,8273 +httpcore/_sync/connection_pool.py,sha256=lglrm_FWU9J_fwCNFqgcaWCebgZw49V7KhcS0wBZ-Ok,15277 +httpcore/_sync/http11.py,sha256=9-IgEawTTbkHuOE8O3LODhp3KCJ4tAo5vmyA4UE66pU,13564 +httpcore/_sync/http2.py,sha256=_fPbMtCAVqGXKFYo3OmNNkucDuVTF69vMEbSFE2Jodo,23345 +httpcore/_sync/http_proxy.py,sha256=82oin8vjt2a7YmmVvz7sXEZSBuajK-mHDF-EwnR_pJ0,14613 +httpcore/_sync/interfaces.py,sha256=EM4PTf-rgkclzisFcrTyx1G8FwraoffE8rbckOznX_o,4365 +httpcore/_sync/socks_proxy.py,sha256=T13QSceeEAg1PM9Yh7Nk-DoqI28TIUqDS-9O3OSC9Uc,13707 +httpcore/_synchronization.py,sha256=HjyPscK40YPQ-_nTcoBXd3S6IqbHTNuw7lTaqtgA3s4,9464 +httpcore/_trace.py,sha256=akf5PsWVq3rZjqmXniomU59OY37K7JHoeNDCQ4GU84E,3954 +httpcore/_utils.py,sha256=9QPh5ib4JilWX4dBCC_XO6wdBY4b0kbUGgfV3QfBANc,1525 +httpcore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/WHEEL new file mode 100644 index 0000000..0309176 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.22.4 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/licenses/LICENSE.md b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..311b2b5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore-1.0.5.dist-info/licenses/LICENSE.md @@ -0,0 +1,27 @@ +Copyright © 2020, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/__init__.py new file mode 100644 index 0000000..014213b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/__init__.py @@ -0,0 +1,139 @@ +from ._api import request, stream +from ._async import ( + AsyncConnectionInterface, + AsyncConnectionPool, + AsyncHTTP2Connection, + AsyncHTTP11Connection, + AsyncHTTPConnection, + AsyncHTTPProxy, + AsyncSOCKSProxy, +) +from ._backends.base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) +from ._backends.mock import AsyncMockBackend, AsyncMockStream, MockBackend, MockStream +from ._backends.sync import SyncBackend +from ._exceptions import ( + ConnectError, + ConnectionNotAvailable, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from ._models import URL, Origin, Request, Response +from ._ssl import default_ssl_context +from ._sync import ( + ConnectionInterface, + ConnectionPool, + HTTP2Connection, + HTTP11Connection, + HTTPConnection, + HTTPProxy, + SOCKSProxy, +) + +# The 'httpcore.AnyIOBackend' class is conditional on 'anyio' being installed. +try: + from ._backends.anyio import AnyIOBackend +except ImportError: # pragma: nocover + + class AnyIOBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = ( + "Attempted to use 'httpcore.AnyIOBackend' but 'anyio' is not installed." + ) + raise RuntimeError(msg) + + +# The 'httpcore.TrioBackend' class is conditional on 'trio' being installed. +try: + from ._backends.trio import TrioBackend +except ImportError: # pragma: nocover + + class TrioBackend: # type: ignore + def __init__(self, *args, **kwargs): # type: ignore + msg = "Attempted to use 'httpcore.TrioBackend' but 'trio' is not installed." + raise RuntimeError(msg) + + +__all__ = [ + # top-level requests + "request", + "stream", + # models + "Origin", + "URL", + "Request", + "Response", + # async + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", + # sync + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", + # network backends, implementations + "SyncBackend", + "AnyIOBackend", + "TrioBackend", + # network backends, mock implementations + "AsyncMockBackend", + "AsyncMockStream", + "MockBackend", + "MockStream", + # network backends, interface + "AsyncNetworkStream", + "AsyncNetworkBackend", + "NetworkStream", + "NetworkBackend", + # util + "default_ssl_context", + "SOCKET_OPTION", + # exceptions + "ConnectionNotAvailable", + "ProxyError", + "ProtocolError", + "LocalProtocolError", + "RemoteProtocolError", + "UnsupportedProtocol", + "TimeoutException", + "PoolTimeout", + "ConnectTimeout", + "ReadTimeout", + "WriteTimeout", + "NetworkError", + "ConnectError", + "ReadError", + "WriteError", +] + +__version__ = "1.0.5" + + +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + setattr(__locals[__name], "__module__", "httpcore") # noqa diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..af8332d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_api.cpython-310.pyc new file mode 100644 index 0000000..219bb12 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_exceptions.cpython-310.pyc new file mode 100644 index 0000000..9eb9cef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_models.cpython-310.pyc new file mode 100644 index 0000000..7d0fb09 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_ssl.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_ssl.cpython-310.pyc new file mode 100644 index 0000000..408dcac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_ssl.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_synchronization.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_synchronization.cpython-310.pyc new file mode 100644 index 0000000..7bc2909 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_synchronization.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_trace.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_trace.cpython-310.pyc new file mode 100644 index 0000000..3c8d832 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_trace.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000..eb557fc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/__pycache__/_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_api.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_api.py new file mode 100644 index 0000000..854235f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_api.py @@ -0,0 +1,92 @@ +from contextlib import contextmanager +from typing import Iterator, Optional, Union + +from ._models import URL, Extensions, HeaderTypes, Response +from ._sync.connection_pool import ConnectionPool + + +def request( + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterator[bytes], None] = None, + extensions: Optional[Extensions] = None, +) -> Response: + """ + Sends an HTTP request, returning the response. + + ``` + response = httpcore.request("GET", "https://www.example.com/") + ``` + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + return pool.request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + + +@contextmanager +def stream( + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterator[bytes], None] = None, + extensions: Optional[Extensions] = None, +) -> Iterator[Response]: + """ + Sends an HTTP request, returning the response within a content manager. + + ``` + with httpcore.stream("GET", "https://www.example.com/") as response: + ... + ``` + + When using the `stream()` function, the body of the response will not be + automatically read. If you want to access the response body you should + either use `content = response.read()`, or `for chunk in response.iter_content()`. + + Arguments: + method: The HTTP method for the request. Typically one of `"GET"`, + `"OPTIONS"`, `"HEAD"`, `"POST"`, `"PUT"`, `"PATCH"`, or `"DELETE"`. + url: The URL of the HTTP request. Either as an instance of `httpcore.URL`, + or as str/bytes. + headers: The HTTP request headers. Either as a dictionary of str/bytes, + or as a list of two-tuples of str/bytes. + content: The content of the request body. Either as bytes, + or as a bytes iterator. + extensions: A dictionary of optional extra information included on the request. + Possible keys include `"timeout"`. + + Returns: + An instance of `httpcore.Response`. + """ + with ConnectionPool() as pool: + with pool.stream( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) as response: + yield response diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__init__.py new file mode 100644 index 0000000..88dc7f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__init__.py @@ -0,0 +1,39 @@ +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .http_proxy import AsyncHTTPProxy +from .interfaces import AsyncConnectionInterface + +try: + from .http2 import AsyncHTTP2Connection +except ImportError: # pragma: nocover + + class AsyncHTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import AsyncSOCKSProxy +except ImportError: # pragma: nocover + + class AsyncSOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "AsyncHTTPConnection", + "AsyncConnectionPool", + "AsyncHTTPProxy", + "AsyncHTTP11Connection", + "AsyncHTTP2Connection", + "AsyncConnectionInterface", + "AsyncSOCKSProxy", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0399841 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000..36dfe0d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-310.pyc new file mode 100644 index 0000000..bfe193e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http11.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http11.cpython-310.pyc new file mode 100644 index 0000000..6d86875 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http11.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http2.cpython-310.pyc new file mode 100644 index 0000000..43d0ad5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-310.pyc new file mode 100644 index 0000000..f855989 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/interfaces.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/interfaces.cpython-310.pyc new file mode 100644 index 0000000..773ddbf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/interfaces.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-310.pyc new file mode 100644 index 0000000..ae5fc7f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/__pycache__/socks_proxy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection.py new file mode 100644 index 0000000..2f439cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection.py @@ -0,0 +1,220 @@ +import itertools +import logging +import ssl +from types import TracebackType +from typing import Iterable, Iterator, Optional, Type + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class AsyncHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: Optional[ssl.SSLContext] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[AsyncNetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connection: Optional[AsyncConnectionInterface] = None + self._connect_failed: bool = False + self._request_lock = AsyncLock() + self._socket_options = socket_options + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + async with self._request_lock: + if self._connection is None: + stream = await self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return await self._connection.handle_async_request(request) + + async def _connect(self, request: Request) -> AsyncNetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + async with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = await self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + async with Trace("retry", logger, request, kwargs) as trace: + await self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + async def aclose(self) -> None: + if self._connection is not None: + async with Trace("close", logger, None, {}): + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> "AsyncHTTPConnection": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + await self.aclose() diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection_pool.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection_pool.py new file mode 100644 index 0000000..018b0ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/connection_pool.py @@ -0,0 +1,380 @@ +import ssl +import sys +from types import TracebackType +from typing import AsyncIterable, AsyncIterator, Iterable, List, Optional, Type + +from .._backends.auto import AutoBackend +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Request, Response +from .._synchronization import AsyncEvent, AsyncShieldCancellation, AsyncThreadLock +from .connection import AsyncHTTPConnection +from .interfaces import AsyncConnectionInterface, AsyncRequestInterface + + +class AsyncPoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: Optional[AsyncConnectionInterface] = None + self._connection_acquired = AsyncEvent() + + def assign_to_connection( + self, connection: Optional[AsyncConnectionInterface] + ) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = AsyncEvent() + + async def wait_for_connection( + self, timeout: Optional[float] = None + ) -> AsyncConnectionInterface: + if self.connection is None: + await self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class AsyncConnectionPool(AsyncRequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: Optional[ssl.SSLContext] = None, + max_connections: Optional[int] = 10, + max_keepalive_connections: Optional[int] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[AsyncNetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: List[AsyncConnectionInterface] = [] + self._requests: List[AsyncPoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = AsyncThreadLock() + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncHTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> List[AsyncConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + async def handle_async_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = AsyncPoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + await self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = await pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = await connection.handle_async_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + await self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, AsyncIterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> List[AsyncConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + avilable_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if avilable_connections: + # log: "reusing existing connection" + connection = avilable_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + async def _close_connections(self, closing: List[AsyncConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with AsyncShieldCancellation(): + for connection in closing: + await connection.aclose() + + async def aclose(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + await self._close_connections(closing_connections) + + async def __aenter__(self) -> "AsyncConnectionPool": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + await self.aclose() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: AsyncIterable[bytes], + pool_request: AsyncPoolRequest, + pool: AsyncConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + async def __aiter__(self) -> AsyncIterator[bytes]: + try: + async for part in self._stream: + yield part + except BaseException as exc: + await self.aclose() + raise exc from None + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + with AsyncShieldCancellation(): + if hasattr(self._stream, "aclose"): + await self._stream.aclose() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + await self._pool._close_connections(closing) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http11.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http11.py new file mode 100644 index 0000000..0493a92 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http11.py @@ -0,0 +1,386 @@ +import enum +import logging +import ssl +import time +from types import TracebackType +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + List, + Optional, + Tuple, + Type, + Union, +) + +import h11 + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP11Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: Optional[float] = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: Optional[float] = keepalive_expiry + self._expire_at: Optional[float] = None + self._state = HTTPConnectionState.NEW + self._state_lock = AsyncLock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + async with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + await self._send_request_headers(**kwargs) + async with Trace("send_request_body", logger, request, kwargs) as trace: + await self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = await self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = AsyncHTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with AsyncShieldCancellation(): + async with Trace("response_closed", logger, request) as trace: + await self._response_closed() + raise exc + + # Sending the request... + + async def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + await self._send_event(event, timeout=timeout) + + async def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, AsyncIterable) + async for chunk in request.stream: + event = h11.Data(data=chunk) + await self._send_event(event, timeout=timeout) + + await self._send_event(h11.EndOfMessage(), timeout=timeout) + + async def _send_event( + self, event: h11.Event, timeout: Optional[float] = None + ) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + await self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + async def _receive_response_headers( + self, request: Request + ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + async def _receive_response_body(self, request: Request) -> AsyncIterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = await self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + async def _receive_event( + self, timeout: Optional[float] = None + ) -> Union[h11.Event, Type[h11.PAUSED]]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = await self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + async def _response_closed(self) -> None: + async with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + await self.aclose() + + # Once the connection is no longer required... + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # The AsyncConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> "AsyncHTTP11Connection": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + await self.aclose() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: AsyncHTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + async def __aiter__(self) -> AsyncIterator[bytes]: + kwargs = {"request": self._request} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + async with Trace("response_closed", logger, self._request): + await self._connection._response_closed() + + +class AsyncHTTP11UpgradeStream(AsyncNetworkStream): + def __init__(self, stream: AsyncNetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + async def read(self, max_bytes: int, timeout: Optional[float] = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return await self._stream.read(max_bytes, timeout) + + async def write(self, buffer: bytes, timeout: Optional[float] = None) -> None: + await self._stream.write(buffer, timeout) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: Optional[str] = None, + timeout: Optional[float] = None, + ) -> AsyncNetworkStream: + return await self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> Any: + return self._stream.get_extra_info(info) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http2.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http2.py new file mode 100644 index 0000000..c201ee4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http2.py @@ -0,0 +1,589 @@ +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import AsyncNetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import AsyncLock, AsyncSemaphore, AsyncShieldCancellation +from .._trace import Trace +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class AsyncHTTP2Connection(AsyncConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: AsyncNetworkStream, + keepalive_expiry: typing.Optional[float] = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: typing.Optional[float] = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: typing.Optional[float] = None + self._request_count = 0 + self._init_lock = AsyncLock() + self._state_lock = AsyncLock() + self._read_lock = AsyncLock() + self._write_lock = AsyncLock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: typing.Dict[ + int, + typing.Union[ + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: typing.Optional[h2.events.ConnectionTerminated] = ( + None + ) + + self._read_exception: typing.Optional[Exception] = None + self._write_exception: typing.Optional[Exception] = None + + async def handle_async_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + async with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + async with self._init_lock: + if not self._sent_connection_init: + try: + kwargs = {"request": request} + async with Trace("send_connection_init", logger, request, kwargs): + await self._send_connection_init(**kwargs) + except BaseException as exc: + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = AsyncSemaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + await self._max_streams_semaphore.acquire() + + await self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + async with Trace("send_request_headers", logger, request, kwargs): + await self._send_request_headers(request=request, stream_id=stream_id) + async with Trace("send_request_body", logger, request, kwargs): + await self._send_request_body(request=request, stream_id=stream_id) + async with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = await self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with AsyncShieldCancellation(): + kwargs = {"stream_id": stream_id} + async with Trace("response_closed", logger, request, kwargs): + await self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + async def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + await self._write_outgoing_data(request) + + # Sending the request... + + async def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + await self._write_outgoing_data(request) + + async def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.AsyncIterable) + async for data in request.stream: + await self._send_stream_data(request, stream_id, data) + await self._send_end_stream(request, stream_id) + + async def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = await self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + await self._write_outgoing_data(request) + + async def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + await self._write_outgoing_data(request) + + # Receiving the response... + + async def _receive_response( + self, request: Request, stream_id: int + ) -> typing.Tuple[int, typing.List[typing.Tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + async def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.AsyncIterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = await self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + await self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + async def _receive_stream_event( + self, request: Request, stream_id: int + ) -> typing.Union[ + h2.events.ResponseReceived, h2.events.DataReceived, h2.events.StreamEnded + ]: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + await self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + async def _receive_events( + self, request: Request, stream_id: typing.Optional[int] = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + async with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = await self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + async with Trace( + "receive_remote_settings", logger, request + ) as trace: + await self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + await self._write_outgoing_data(request) + + async def _receive_remote_settings_change(self, event: h2.events.Event) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + await self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + await self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + async def _response_closed(self, stream_id: int) -> None: + await self._max_streams_semaphore.release() + del self._events[stream_id] + async with self._state_lock: + if self._connection_terminated and not self._events: + await self.aclose() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + await self.aclose() + + async def aclose(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + await self._network_stream.aclose() + + # Wrappers around network read/write operations... + + async def _read_incoming_data( + self, request: Request + ) -> typing.List[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = await self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: typing.List[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + async def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + async with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + await self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + async def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + await self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + async def __aenter__(self) -> "AsyncHTTP2Connection": + return self + + async def __aexit__( + self, + exc_type: typing.Optional[typing.Type[BaseException]] = None, + exc_value: typing.Optional[BaseException] = None, + traceback: typing.Optional[types.TracebackType] = None, + ) -> None: + await self.aclose() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: AsyncHTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + async with Trace("receive_response_body", logger, self._request, kwargs): + async for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with AsyncShieldCancellation(): + await self.aclose() + raise exc + + async def aclose(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + async with Trace("response_closed", logger, self._request, kwargs): + await self._connection._response_closed(stream_id=self._stream_id) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http_proxy.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http_proxy.py new file mode 100644 index 0000000..4aa7d87 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/http_proxy.py @@ -0,0 +1,368 @@ +import logging +import ssl +from base64 import b64encode +from typing import Iterable, List, Mapping, Optional, Sequence, Tuple, Union + +from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection import AsyncHTTPConnection +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +HeadersAsSequence = Sequence[Tuple[Union[bytes, str], Union[bytes, str]]] +HeadersAsMapping = Mapping[Union[bytes, str], Union[bytes, str]] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, + override_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, +) -> List[Tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +def build_auth_header(username: bytes, password: bytes) -> bytes: + userpass = username + b":" + password + return b"Basic " + b64encode(userpass) + + +class AsyncHTTPProxy(AsyncConnectionPool): + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: Union[URL, bytes, str], + proxy_auth: Optional[Tuple[Union[bytes, str], Union[bytes, str]]] = None, + proxy_headers: Union[HeadersAsMapping, HeadersAsSequence, None] = None, + ssl_context: Optional[ssl.SSLContext] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + max_connections: Optional[int] = 10, + max_keepalive_connections: Optional[int] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[AsyncNetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + authorization = build_auth_header(username, password) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + if origin.scheme == b"http": + return AsyncForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return AsyncTunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncForwardHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: Union[HeadersAsMapping, HeadersAsSequence, None] = None, + keepalive_expiry: Optional[float] = None, + network_backend: Optional[AsyncNetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + ) -> None: + self._connection = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + async def handle_async_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return await self._connection.handle_async_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class AsyncTunnelHTTPConnection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: Optional[ssl.SSLContext] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + proxy_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + network_backend: Optional[AsyncNetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + self._connection: AsyncConnectionInterface = AsyncHTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = AsyncLock() + self._connected = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = await self._connection.handle_async_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + await self._connection.aclose() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + await self._connection.aclose() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/interfaces.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/interfaces.py new file mode 100644 index 0000000..c998dd2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/interfaces.py @@ -0,0 +1,135 @@ +from contextlib import asynccontextmanager +from typing import AsyncIterator, Optional, Union + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class AsyncRequestInterface: + async def request( + self, + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, AsyncIterator[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + await response.aread() + finally: + await response.aclose() + return response + + @asynccontextmanager + async def stream( + self, + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, AsyncIterator[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> AsyncIterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = await self.handle_async_request(request) + try: + yield response + finally: + await response.aclose() + + async def handle_async_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class AsyncConnectionInterface(AsyncRequestInterface): + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/socks_proxy.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/socks_proxy.py new file mode 100644 index 0000000..f839603 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_async/socks_proxy.py @@ -0,0 +1,342 @@ +import logging +import ssl +import typing + +from socksio import socks5 + +from .._backends.auto import AutoBackend +from .._backends.base import AsyncNetworkBackend, AsyncNetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import AsyncLock +from .._trace import Trace +from .connection_pool import AsyncConnectionPool +from .http11 import AsyncHTTP11Connection +from .interfaces import AsyncConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +async def _init_socks5_connection( + stream: AsyncNetworkStream, + *, + host: bytes, + port: int, + auth: typing.Optional[typing.Tuple[bytes, bytes]] = None, +) -> None: + conn = socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socks5.SOCKS5CommandRequest.from_address( + socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + await stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = await stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5Reply) + if response.reply_code != socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class AsyncSOCKSProxy(AsyncConnectionPool): + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: typing.Union[URL, bytes, str], + proxy_auth: typing.Optional[ + typing.Tuple[typing.Union[bytes, str], typing.Union[bytes, str]] + ] = None, + ssl_context: typing.Optional[ssl.SSLContext] = None, + max_connections: typing.Optional[int] = 10, + max_keepalive_connections: typing.Optional[int] = None, + keepalive_expiry: typing.Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: typing.Optional[AsyncNetworkBackend] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: typing.Optional[typing.Tuple[bytes, bytes]] = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncSocks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class AsyncSocks5Connection(AsyncConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: typing.Optional[typing.Tuple[bytes, bytes]] = None, + ssl_context: typing.Optional[ssl.SSLContext] = None, + keepalive_expiry: typing.Optional[float] = None, + http1: bool = True, + http2: bool = False, + network_backend: typing.Optional[AsyncNetworkBackend] = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: AsyncNetworkBackend = ( + AutoBackend() if network_backend is None else network_backend + ) + self._connect_lock = AsyncLock() + self._connection: typing.Optional[AsyncConnectionInterface] = None + self._connect_failed = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + async with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + async with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = await self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + async with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + await _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return await self._connection.handle_async_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + async def aclose(self) -> None: + if self._connection is not None: + await self._connection.aclose() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a72ff63 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/anyio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/anyio.cpython-310.pyc new file mode 100644 index 0000000..51b5a4f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/anyio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/auto.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/auto.cpython-310.pyc new file mode 100644 index 0000000..caade25 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/auto.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/base.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000..ad420ec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/base.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/mock.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/mock.cpython-310.pyc new file mode 100644 index 0000000..0489cf3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/mock.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/sync.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/sync.cpython-310.pyc new file mode 100644 index 0000000..2e95218 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/sync.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/trio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/trio.cpython-310.pyc new file mode 100644 index 0000000..e7615a1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/__pycache__/trio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/anyio.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/anyio.py new file mode 100644 index 0000000..5731f5e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/anyio.py @@ -0,0 +1,147 @@ +import ssl +import typing + +import anyio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AnyIOStream(AsyncNetworkStream): + def __init__(self, stream: anyio.abc.ByteStream) -> None: + self._stream = stream + + async def read( + self, max_bytes: int, timeout: typing.Optional[float] = None + ) -> bytes: + exc_map = { + TimeoutError: ReadTimeout, + anyio.BrokenResourceError: ReadError, + anyio.ClosedResourceError: ReadError, + anyio.EndOfStream: ReadError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: nocover + return b"" + + async def write( + self, buffer: bytes, timeout: typing.Optional[float] = None + ) -> None: + if not buffer: + return + + exc_map = { + TimeoutError: WriteTimeout, + anyio.BrokenResourceError: WriteError, + anyio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + await self._stream.send(item=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> AsyncNetworkStream: + exc_map = { + TimeoutError: ConnectTimeout, + anyio.BrokenResourceError: ConnectError, + anyio.EndOfStream: ConnectError, + } + with map_exceptions(exc_map): + try: + with anyio.fail_after(timeout): + ssl_stream = await anyio.streams.tls.TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=server_hostname, + standard_compatible=False, + server_side=False, + ) + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return AnyIOStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self._stream.extra(anyio.streams.tls.TLSAttribute.ssl_object, None) + if info == "client_addr": + return self._stream.extra(anyio.abc.SocketAttribute.local_address, None) + if info == "server_addr": + return self._stream.extra(anyio.abc.SocketAttribute.remote_address, None) + if info == "socket": + return self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + if info == "is_readable": + sock = self._stream.extra(anyio.abc.SocketAttribute.raw_socket, None) + return is_socket_readable(sock) + return None + + +class AnyIOBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: typing.Optional[float] = None, + local_address: typing.Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + if socket_options is None: + socket_options = [] # pragma: no cover + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_address, + ) + # By default TCP sockets opened in `asyncio` include TCP_NODELAY. + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: typing.Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + exc_map = { + TimeoutError: ConnectTimeout, + OSError: ConnectError, + anyio.BrokenResourceError: ConnectError, + } + with map_exceptions(exc_map): + with anyio.fail_after(timeout): + stream: anyio.abc.ByteStream = await anyio.connect_unix(path) + for option in socket_options: + stream._raw_socket.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return AnyIOStream(stream) + + async def sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) # pragma: nocover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/auto.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/auto.py new file mode 100644 index 0000000..3ac05f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/auto.py @@ -0,0 +1,51 @@ +import typing +from typing import Optional + +from .._synchronization import current_async_library +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class AutoBackend(AsyncNetworkBackend): + async def _init_backend(self) -> None: + if not (hasattr(self, "_backend")): + backend = current_async_library() + if backend == "trio": + from .trio import TrioBackend + + self._backend: AsyncNetworkBackend = TrioBackend() + else: + from .anyio import AnyIOBackend + + self._backend = AnyIOBackend() + + async def connect_tcp( + self, + host: str, + port: int, + timeout: Optional[float] = None, + local_address: Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + await self._init_backend() + return await self._backend.connect_tcp( + host, + port, + timeout=timeout, + local_address=local_address, + socket_options=socket_options, + ) + + async def connect_unix_socket( + self, + path: str, + timeout: Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: # pragma: nocover + await self._init_backend() + return await self._backend.connect_unix_socket( + path, timeout=timeout, socket_options=socket_options + ) + + async def sleep(self, seconds: float) -> None: # pragma: nocover + await self._init_backend() + return await self._backend.sleep(seconds) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/base.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/base.py new file mode 100644 index 0000000..6cadedb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/base.py @@ -0,0 +1,103 @@ +import ssl +import time +import typing + +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + + +class NetworkStream: + def read(self, max_bytes: int, timeout: typing.Optional[float] = None) -> bytes: + raise NotImplementedError() # pragma: nocover + + def write(self, buffer: bytes, timeout: typing.Optional[float] = None) -> None: + raise NotImplementedError() # pragma: nocover + + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> "NetworkStream": + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class NetworkBackend: + def connect_tcp( + self, + host: str, + port: int, + timeout: typing.Optional[float] = None, + local_address: typing.Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def connect_unix_socket( + self, + path: str, + timeout: typing.Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: + raise NotImplementedError() # pragma: nocover + + def sleep(self, seconds: float) -> None: + time.sleep(seconds) # pragma: nocover + + +class AsyncNetworkStream: + async def read( + self, max_bytes: int, timeout: typing.Optional[float] = None + ) -> bytes: + raise NotImplementedError() # pragma: nocover + + async def write( + self, buffer: bytes, timeout: typing.Optional[float] = None + ) -> None: + raise NotImplementedError() # pragma: nocover + + async def aclose(self) -> None: + raise NotImplementedError() # pragma: nocover + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> "AsyncNetworkStream": + raise NotImplementedError() # pragma: nocover + + def get_extra_info(self, info: str) -> typing.Any: + return None # pragma: nocover + + +class AsyncNetworkBackend: + async def connect_tcp( + self, + host: str, + port: int, + timeout: typing.Optional[float] = None, + local_address: typing.Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def connect_unix_socket( + self, + path: str, + timeout: typing.Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + raise NotImplementedError() # pragma: nocover + + async def sleep(self, seconds: float) -> None: + raise NotImplementedError() # pragma: nocover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/mock.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/mock.py new file mode 100644 index 0000000..f7aefeb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/mock.py @@ -0,0 +1,142 @@ +import ssl +import typing +from typing import Optional + +from .._exceptions import ReadError +from .base import ( + SOCKET_OPTION, + AsyncNetworkBackend, + AsyncNetworkStream, + NetworkBackend, + NetworkStream, +) + + +class MockSSLObject: + def __init__(self, http2: bool): + self._http2 = http2 + + def selected_alpn_protocol(self) -> str: + return "h2" if self._http2 else "http/1.1" + + +class MockStream(NetworkStream): + def __init__(self, buffer: typing.List[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + def read(self, max_bytes: int, timeout: Optional[float] = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + def write(self, buffer: bytes, timeout: Optional[float] = None) -> None: + pass + + def close(self) -> None: + self._closed = True + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: Optional[str] = None, + timeout: Optional[float] = None, + ) -> NetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class MockBackend(NetworkBackend): + def __init__(self, buffer: typing.List[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + def connect_tcp( + self, + host: str, + port: int, + timeout: Optional[float] = None, + local_address: Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def connect_unix_socket( + self, + path: str, + timeout: Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: + return MockStream(list(self._buffer), http2=self._http2) + + def sleep(self, seconds: float) -> None: + pass + + +class AsyncMockStream(AsyncNetworkStream): + def __init__(self, buffer: typing.List[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + self._closed = False + + async def read(self, max_bytes: int, timeout: Optional[float] = None) -> bytes: + if self._closed: + raise ReadError("Connection closed") + if not self._buffer: + return b"" + return self._buffer.pop(0) + + async def write(self, buffer: bytes, timeout: Optional[float] = None) -> None: + pass + + async def aclose(self) -> None: + self._closed = True + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: Optional[str] = None, + timeout: Optional[float] = None, + ) -> AsyncNetworkStream: + return self + + def get_extra_info(self, info: str) -> typing.Any: + return MockSSLObject(http2=self._http2) if info == "ssl_object" else None + + def __repr__(self) -> str: + return "" + + +class AsyncMockBackend(AsyncNetworkBackend): + def __init__(self, buffer: typing.List[bytes], http2: bool = False) -> None: + self._buffer = buffer + self._http2 = http2 + + async def connect_tcp( + self, + host: str, + port: int, + timeout: Optional[float] = None, + local_address: Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def connect_unix_socket( + self, + path: str, + timeout: Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + return AsyncMockStream(list(self._buffer), http2=self._http2) + + async def sleep(self, seconds: float) -> None: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/sync.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/sync.py new file mode 100644 index 0000000..7b7b417 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/sync.py @@ -0,0 +1,239 @@ +import socket +import ssl +import sys +import typing +from functools import partial + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .._utils import is_socket_readable +from .base import SOCKET_OPTION, NetworkBackend, NetworkStream + + +class TLSinTLSStream(NetworkStream): # pragma: no cover + """ + Because the standard `SSLContext.wrap_socket` method does + not work for `SSLSocket` objects, we need this class + to implement TLS stream using an underlying `SSLObject` + instance in order to support TLS on top of TLS. + """ + + # Defined in RFC 8449 + TLS_RECORD_SIZE = 16384 + + def __init__( + self, + sock: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ): + self._sock = sock + self._incoming = ssl.MemoryBIO() + self._outgoing = ssl.MemoryBIO() + + self.ssl_obj = ssl_context.wrap_bio( + incoming=self._incoming, + outgoing=self._outgoing, + server_hostname=server_hostname, + ) + + self._sock.settimeout(timeout) + self._perform_io(self.ssl_obj.do_handshake) + + def _perform_io( + self, + func: typing.Callable[..., typing.Any], + ) -> typing.Any: + ret = None + + while True: + errno = None + try: + ret = func() + except (ssl.SSLWantReadError, ssl.SSLWantWriteError) as e: + errno = e.errno + + self._sock.sendall(self._outgoing.read()) + + if errno == ssl.SSL_ERROR_WANT_READ: + buf = self._sock.recv(self.TLS_RECORD_SIZE) + + if buf: + self._incoming.write(buf) + else: + self._incoming.write_eof() + if errno is None: + return ret + + def read(self, max_bytes: int, timeout: typing.Optional[float] = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return typing.cast( + bytes, self._perform_io(partial(self.ssl_obj.read, max_bytes)) + ) + + def write(self, buffer: bytes, timeout: typing.Optional[float] = None) -> None: + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + while buffer: + nsent = self._perform_io(partial(self.ssl_obj.write, buffer)) + buffer = buffer[nsent:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> "NetworkStream": + raise NotImplementedError() + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + return self.ssl_obj + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncStream(NetworkStream): + def __init__(self, sock: socket.socket) -> None: + self._sock = sock + + def read(self, max_bytes: int, timeout: typing.Optional[float] = None) -> bytes: + exc_map: ExceptionMapping = {socket.timeout: ReadTimeout, OSError: ReadError} + with map_exceptions(exc_map): + self._sock.settimeout(timeout) + return self._sock.recv(max_bytes) + + def write(self, buffer: bytes, timeout: typing.Optional[float] = None) -> None: + if not buffer: + return + + exc_map: ExceptionMapping = {socket.timeout: WriteTimeout, OSError: WriteError} + with map_exceptions(exc_map): + while buffer: + self._sock.settimeout(timeout) + n = self._sock.send(buffer) + buffer = buffer[n:] + + def close(self) -> None: + self._sock.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> NetworkStream: + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + try: + if isinstance(self._sock, ssl.SSLSocket): # pragma: no cover + # If the underlying socket has already been upgraded + # to the TLS layer (i.e. is an instance of SSLSocket), + # we need some additional smarts to support TLS-in-TLS. + return TLSinTLSStream( + self._sock, ssl_context, server_hostname, timeout + ) + else: + self._sock.settimeout(timeout) + sock = ssl_context.wrap_socket( + self._sock, server_hostname=server_hostname + ) + except Exception as exc: # pragma: nocover + self.close() + raise exc + return SyncStream(sock) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._sock, ssl.SSLSocket): + return self._sock._sslobj # type: ignore + if info == "client_addr": + return self._sock.getsockname() + if info == "server_addr": + return self._sock.getpeername() + if info == "socket": + return self._sock + if info == "is_readable": + return is_socket_readable(self._sock) + return None + + +class SyncBackend(NetworkBackend): + def connect_tcp( + self, + host: str, + port: int, + timeout: typing.Optional[float] = None, + local_address: typing.Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: + # Note that we automatically include `TCP_NODELAY` + # in addition to any other custom socket options. + if socket_options is None: + socket_options = [] # pragma: no cover + address = (host, port) + source_address = None if local_address is None else (local_address, 0) + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + + with map_exceptions(exc_map): + sock = socket.create_connection( + address, + timeout, + source_address=source_address, + ) + for option in socket_options: + sock.setsockopt(*option) # pragma: no cover + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + return SyncStream(sock) + + def connect_unix_socket( + self, + path: str, + timeout: typing.Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> NetworkStream: # pragma: nocover + if sys.platform == "win32": + raise RuntimeError( + "Attempted to connect to a UNIX socket on a Windows system." + ) + if socket_options is None: + socket_options = [] + + exc_map: ExceptionMapping = { + socket.timeout: ConnectTimeout, + OSError: ConnectError, + } + with map_exceptions(exc_map): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + for option in socket_options: + sock.setsockopt(*option) + sock.settimeout(timeout) + sock.connect(path) + return SyncStream(sock) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/trio.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/trio.py new file mode 100644 index 0000000..b1626d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_backends/trio.py @@ -0,0 +1,161 @@ +import ssl +import typing + +import trio + +from .._exceptions import ( + ConnectError, + ConnectTimeout, + ExceptionMapping, + ReadError, + ReadTimeout, + WriteError, + WriteTimeout, + map_exceptions, +) +from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream + + +class TrioStream(AsyncNetworkStream): + def __init__(self, stream: trio.abc.Stream) -> None: + self._stream = stream + + async def read( + self, max_bytes: int, timeout: typing.Optional[float] = None + ) -> bytes: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ReadTimeout, + trio.BrokenResourceError: ReadError, + trio.ClosedResourceError: ReadError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + data: bytes = await self._stream.receive_some(max_bytes=max_bytes) + return data + + async def write( + self, buffer: bytes, timeout: typing.Optional[float] = None + ) -> None: + if not buffer: + return + + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: WriteTimeout, + trio.BrokenResourceError: WriteError, + trio.ClosedResourceError: WriteError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + await self._stream.send_all(data=buffer) + + async def aclose(self) -> None: + await self._stream.aclose() + + async def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: typing.Optional[str] = None, + timeout: typing.Optional[float] = None, + ) -> AsyncNetworkStream: + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + } + ssl_stream = trio.SSLStream( + self._stream, + ssl_context=ssl_context, + server_hostname=server_hostname, + https_compatible=True, + server_side=False, + ) + with map_exceptions(exc_map): + try: + with trio.fail_after(timeout_or_inf): + await ssl_stream.do_handshake() + except Exception as exc: # pragma: nocover + await self.aclose() + raise exc + return TrioStream(ssl_stream) + + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object" and isinstance(self._stream, trio.SSLStream): + # Type checkers cannot see `_ssl_object` attribute because trio._ssl.SSLStream uses __getattr__/__setattr__. + # Tracked at https://github.com/python-trio/trio/issues/542 + return self._stream._ssl_object # type: ignore[attr-defined] + if info == "client_addr": + return self._get_socket_stream().socket.getsockname() + if info == "server_addr": + return self._get_socket_stream().socket.getpeername() + if info == "socket": + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream.socket + if info == "is_readable": + socket = self.get_extra_info("socket") + return socket.is_readable() + return None + + def _get_socket_stream(self) -> trio.SocketStream: + stream = self._stream + while isinstance(stream, trio.SSLStream): + stream = stream.transport_stream + assert isinstance(stream, trio.SocketStream) + return stream + + +class TrioBackend(AsyncNetworkBackend): + async def connect_tcp( + self, + host: str, + port: int, + timeout: typing.Optional[float] = None, + local_address: typing.Optional[str] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: + # By default for TCP sockets, trio enables TCP_NODELAY. + # https://trio.readthedocs.io/en/stable/reference-io.html#trio.SocketStream + if socket_options is None: + socket_options = [] # pragma: no cover + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_tcp_stream( + host=host, port=port, local_address=local_address + ) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def connect_unix_socket( + self, + path: str, + timeout: typing.Optional[float] = None, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, + ) -> AsyncNetworkStream: # pragma: nocover + if socket_options is None: + socket_options = [] + timeout_or_inf = float("inf") if timeout is None else timeout + exc_map: ExceptionMapping = { + trio.TooSlowError: ConnectTimeout, + trio.BrokenResourceError: ConnectError, + OSError: ConnectError, + } + with map_exceptions(exc_map): + with trio.fail_after(timeout_or_inf): + stream: trio.abc.Stream = await trio.open_unix_socket(path) + for option in socket_options: + stream.setsockopt(*option) # type: ignore[attr-defined] # pragma: no cover + return TrioStream(stream) + + async def sleep(self, seconds: float) -> None: + await trio.sleep(seconds) # pragma: nocover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_exceptions.py new file mode 100644 index 0000000..81e7fc6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_exceptions.py @@ -0,0 +1,81 @@ +import contextlib +from typing import Iterator, Mapping, Type + +ExceptionMapping = Mapping[Type[Exception], Type[Exception]] + + +@contextlib.contextmanager +def map_exceptions(map: ExceptionMapping) -> Iterator[None]: + try: + yield + except Exception as exc: # noqa: PIE786 + for from_exc, to_exc in map.items(): + if isinstance(exc, from_exc): + raise to_exc(exc) from exc + raise # pragma: nocover + + +class ConnectionNotAvailable(Exception): + pass + + +class ProxyError(Exception): + pass + + +class UnsupportedProtocol(Exception): + pass + + +class ProtocolError(Exception): + pass + + +class RemoteProtocolError(ProtocolError): + pass + + +class LocalProtocolError(ProtocolError): + pass + + +# Timeout errors + + +class TimeoutException(Exception): + pass + + +class PoolTimeout(TimeoutException): + pass + + +class ConnectTimeout(TimeoutException): + pass + + +class ReadTimeout(TimeoutException): + pass + + +class WriteTimeout(TimeoutException): + pass + + +# Network errors + + +class NetworkError(Exception): + pass + + +class ConnectError(NetworkError): + pass + + +class ReadError(NetworkError): + pass + + +class WriteError(NetworkError): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_models.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_models.py new file mode 100644 index 0000000..dadee79 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_models.py @@ -0,0 +1,492 @@ +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + Optional, + Sequence, + Tuple, + Union, +) +from urllib.parse import urlparse + +# Functions for typechecking... + + +HeadersAsSequence = Sequence[Tuple[Union[bytes, str], Union[bytes, str]]] +HeadersAsMapping = Mapping[Union[bytes, str], Union[bytes, str]] +HeaderTypes = Union[HeadersAsSequence, HeadersAsMapping, None] + +Extensions = MutableMapping[str, Any] + + +def enforce_bytes(value: Union[bytes, str], *, name: str) -> bytes: + """ + Any arguments that are ultimately represented as bytes can be specified + either as bytes or as strings. + + However we enforce that any string arguments must only contain characters in + the plain ASCII range. chr(0)...chr(127). If you need to use characters + outside that range then be precise, and use a byte-wise argument. + """ + if isinstance(value, str): + try: + return value.encode("ascii") + except UnicodeEncodeError: + raise TypeError(f"{name} strings may not include unicode characters.") + elif isinstance(value, bytes): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be bytes or str, but got {seen_type}.") + + +def enforce_url(value: Union["URL", bytes, str], *, name: str) -> "URL": + """ + Type check for URL parameters. + """ + if isinstance(value, (bytes, str)): + return URL(value) + elif isinstance(value, URL): + return value + + seen_type = type(value).__name__ + raise TypeError(f"{name} must be a URL, bytes, or str, but got {seen_type}.") + + +def enforce_headers( + value: Union[HeadersAsMapping, HeadersAsSequence, None] = None, *, name: str +) -> List[Tuple[bytes, bytes]]: + """ + Convienence function that ensure all items in request or response headers + are either bytes or strings in the plain ASCII range. + """ + if value is None: + return [] + elif isinstance(value, Mapping): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value.items() + ] + elif isinstance(value, Sequence): + return [ + ( + enforce_bytes(k, name="header name"), + enforce_bytes(v, name="header value"), + ) + for k, v in value + ] + + seen_type = type(value).__name__ + raise TypeError( + f"{name} must be a mapping or sequence of two-tuples, but got {seen_type}." + ) + + +def enforce_stream( + value: Union[bytes, Iterable[bytes], AsyncIterable[bytes], None], *, name: str +) -> Union[Iterable[bytes], AsyncIterable[bytes]]: + if value is None: + return ByteStream(b"") + elif isinstance(value, bytes): + return ByteStream(value) + return value + + +# * https://tools.ietf.org/html/rfc3986#section-3.2.3 +# * https://url.spec.whatwg.org/#url-miscellaneous +# * https://url.spec.whatwg.org/#scheme-state +DEFAULT_PORTS = { + b"ftp": 21, + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, +} + + +def include_request_headers( + headers: List[Tuple[bytes, bytes]], + *, + url: "URL", + content: Union[None, bytes, Iterable[bytes], AsyncIterable[bytes]], +) -> List[Tuple[bytes, bytes]]: + headers_set = set(k.lower() for k, v in headers) + + if b"host" not in headers_set: + default_port = DEFAULT_PORTS.get(url.scheme) + if url.port is None or url.port == default_port: + header_value = url.host + else: + header_value = b"%b:%d" % (url.host, url.port) + headers = [(b"Host", header_value)] + headers + + if ( + content is not None + and b"content-length" not in headers_set + and b"transfer-encoding" not in headers_set + ): + if isinstance(content, bytes): + content_length = str(len(content)).encode("ascii") + headers += [(b"Content-Length", content_length)] + else: + headers += [(b"Transfer-Encoding", b"chunked")] # pragma: nocover + + return headers + + +# Interfaces for byte streams... + + +class ByteStream: + """ + A container for non-streaming content, and that supports both sync and async + stream iteration. + """ + + def __init__(self, content: bytes) -> None: + self._content = content + + def __iter__(self) -> Iterator[bytes]: + yield self._content + + async def __aiter__(self) -> AsyncIterator[bytes]: + yield self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{len(self._content)} bytes]>" + + +class Origin: + def __init__(self, scheme: bytes, host: bytes, port: int) -> None: + self.scheme = scheme + self.host = host + self.port = port + + def __eq__(self, other: Any) -> bool: + return ( + isinstance(other, Origin) + and self.scheme == other.scheme + and self.host == other.host + and self.port == other.port + ) + + def __str__(self) -> str: + scheme = self.scheme.decode("ascii") + host = self.host.decode("ascii") + port = str(self.port) + return f"{scheme}://{host}:{port}" + + +class URL: + """ + Represents the URL against which an HTTP request may be made. + + The URL may either be specified as a plain string, for convienence: + + ```python + url = httpcore.URL("https://www.example.com/") + ``` + + Or be constructed with explicitily pre-parsed components: + + ```python + url = httpcore.URL(scheme=b'https', host=b'www.example.com', port=None, target=b'/') + ``` + + Using this second more explicit style allows integrations that are using + `httpcore` to pass through URLs that have already been parsed in order to use + libraries such as `rfc-3986` rather than relying on the stdlib. It also ensures + that URL parsing is treated identically at both the networking level and at any + higher layers of abstraction. + + The four components are important here, as they allow the URL to be precisely + specified in a pre-parsed format. They also allow certain types of request to + be created that could not otherwise be expressed. + + For example, an HTTP request to `http://www.example.com/` forwarded via a proxy + at `http://localhost:8080`... + + ```python + # Constructs an HTTP request with a complete URL as the target: + # GET https://www.example.com/ HTTP/1.1 + url = httpcore.URL( + scheme=b'http', + host=b'localhost', + port=8080, + target=b'https://www.example.com/' + ) + request = httpcore.Request( + method="GET", + url=url + ) + ``` + + Another example is constructing an `OPTIONS *` request... + + ```python + # Constructs an 'OPTIONS *' HTTP request: + # OPTIONS * HTTP/1.1 + url = httpcore.URL(scheme=b'https', host=b'www.example.com', target=b'*') + request = httpcore.Request(method="OPTIONS", url=url) + ``` + + This kind of request is not possible to formulate with a URL string, + because the `/` delimiter is always used to demark the target from the + host/port portion of the URL. + + For convenience, string-like arguments may be specified either as strings or + as bytes. However, once a request is being issue over-the-wire, the URL + components are always ultimately required to be a bytewise representation. + + In order to avoid any ambiguity over character encodings, when strings are used + as arguments, they must be strictly limited to the ASCII range `chr(0)`-`chr(127)`. + If you require a bytewise representation that is outside this range you must + handle the character encoding directly, and pass a bytes instance. + """ + + def __init__( + self, + url: Union[bytes, str] = "", + *, + scheme: Union[bytes, str] = b"", + host: Union[bytes, str] = b"", + port: Optional[int] = None, + target: Union[bytes, str] = b"", + ) -> None: + """ + Parameters: + url: The complete URL as a string or bytes. + scheme: The URL scheme as a string or bytes. + Typically either `"http"` or `"https"`. + host: The URL host as a string or bytes. Such as `"www.example.com"`. + port: The port to connect to. Either an integer or `None`. + target: The target of the HTTP request. Such as `"/items?search=red"`. + """ + if url: + parsed = urlparse(enforce_bytes(url, name="url")) + self.scheme = parsed.scheme + self.host = parsed.hostname or b"" + self.port = parsed.port + self.target = (parsed.path or b"/") + ( + b"?" + parsed.query if parsed.query else b"" + ) + else: + self.scheme = enforce_bytes(scheme, name="scheme") + self.host = enforce_bytes(host, name="host") + self.port = port + self.target = enforce_bytes(target, name="target") + + @property + def origin(self) -> Origin: + default_port = { + b"http": 80, + b"https": 443, + b"ws": 80, + b"wss": 443, + b"socks5": 1080, + }[self.scheme] + return Origin( + scheme=self.scheme, host=self.host, port=self.port or default_port + ) + + def __eq__(self, other: Any) -> bool: + return ( + isinstance(other, URL) + and other.scheme == self.scheme + and other.host == self.host + and other.port == self.port + and other.target == self.target + ) + + def __bytes__(self) -> bytes: + if self.port is None: + return b"%b://%b%b" % (self.scheme, self.host, self.target) + return b"%b://%b:%d%b" % (self.scheme, self.host, self.port, self.target) + + def __repr__(self) -> str: + return ( + f"{self.__class__.__name__}(scheme={self.scheme!r}, " + f"host={self.host!r}, port={self.port!r}, target={self.target!r})" + ) + + +class Request: + """ + An HTTP request. + """ + + def __init__( + self, + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterable[bytes], AsyncIterable[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> None: + """ + Parameters: + method: The HTTP request method, either as a string or bytes. + For example: `GET`. + url: The request URL, either as a `URL` instance, or as a string or bytes. + For example: `"https://www.example.com".` + headers: The HTTP request headers. + content: The content of the request body. + extensions: A dictionary of optional extra information included on + the request. Possible keys include `"timeout"`, and `"trace"`. + """ + self.method: bytes = enforce_bytes(method, name="method") + self.url: URL = enforce_url(url, name="url") + self.headers: List[Tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: Union[Iterable[bytes], AsyncIterable[bytes]] = enforce_stream( + content, name="content" + ) + self.extensions = {} if extensions is None else extensions + + if "target" in self.extensions: + self.url = URL( + scheme=self.url.scheme, + host=self.url.host, + port=self.url.port, + target=self.extensions["target"], + ) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.method!r}]>" + + +class Response: + """ + An HTTP response. + """ + + def __init__( + self, + status: int, + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterable[bytes], AsyncIterable[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> None: + """ + Parameters: + status: The HTTP status code of the response. For example `200`. + headers: The HTTP response headers. + content: The content of the response body. + extensions: A dictionary of optional extra information included on + the responseself.Possible keys include `"http_version"`, + `"reason_phrase"`, and `"network_stream"`. + """ + self.status: int = status + self.headers: List[Tuple[bytes, bytes]] = enforce_headers( + headers, name="headers" + ) + self.stream: Union[Iterable[bytes], AsyncIterable[bytes]] = enforce_stream( + content, name="content" + ) + self.extensions = {} if extensions is None else extensions + + self._stream_consumed = False + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + if isinstance(self.stream, Iterable): + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'response.read()' first." + ) + else: + raise RuntimeError( + "Attempted to access 'response.content' on a streaming response. " + "Call 'await response.aread()' first." + ) + return self._content + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.status}]>" + + # Sync interface... + + def read(self) -> bytes: + if not isinstance(self.stream, Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an asynchronous response using 'response.read()'. " + "You should use 'await response.aread()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part for part in self.iter_stream()]) + return self._content + + def iter_stream(self) -> Iterator[bytes]: + if not isinstance(self.stream, Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an asynchronous response using 'for ... in " + "response.iter_stream()'. " + "You should use 'async for ... in response.aiter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'for ... in response.iter_stream()' more than once." + ) + self._stream_consumed = True + for chunk in self.stream: + yield chunk + + def close(self) -> None: + if not isinstance(self.stream, Iterable): # pragma: nocover + raise RuntimeError( + "Attempted to close an asynchronous response using 'response.close()'. " + "You should use 'await response.aclose()' instead." + ) + if hasattr(self.stream, "close"): + self.stream.close() + + # Async interface... + + async def aread(self) -> bytes: + if not isinstance(self.stream, AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to read an synchronous response using " + "'await response.aread()'. " + "You should use 'response.read()' instead." + ) + if not hasattr(self, "_content"): + self._content = b"".join([part async for part in self.aiter_stream()]) + return self._content + + async def aiter_stream(self) -> AsyncIterator[bytes]: + if not isinstance(self.stream, AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to stream an synchronous response using 'async for ... in " + "response.aiter_stream()'. " + "You should use 'for ... in response.iter_stream()' instead." + ) + if self._stream_consumed: + raise RuntimeError( + "Attempted to call 'async for ... in response.aiter_stream()' " + "more than once." + ) + self._stream_consumed = True + async for chunk in self.stream: + yield chunk + + async def aclose(self) -> None: + if not isinstance(self.stream, AsyncIterable): # pragma: nocover + raise RuntimeError( + "Attempted to close a synchronous response using " + "'await response.aclose()'. " + "You should use 'response.close()' instead." + ) + if hasattr(self.stream, "aclose"): + await self.stream.aclose() diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_ssl.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_ssl.py new file mode 100644 index 0000000..c99c5a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_ssl.py @@ -0,0 +1,9 @@ +import ssl + +import certifi + + +def default_ssl_context() -> ssl.SSLContext: + context = ssl.create_default_context() + context.load_verify_locations(certifi.where()) + return context diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__init__.py new file mode 100644 index 0000000..b476d76 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__init__.py @@ -0,0 +1,39 @@ +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .http_proxy import HTTPProxy +from .interfaces import ConnectionInterface + +try: + from .http2 import HTTP2Connection +except ImportError: # pragma: nocover + + class HTTP2Connection: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use http2 support, but the `h2` package is not " + "installed. Use 'pip install httpcore[http2]'." + ) + + +try: + from .socks_proxy import SOCKSProxy +except ImportError: # pragma: nocover + + class SOCKSProxy: # type: ignore + def __init__(self, *args, **kwargs) -> None: # type: ignore + raise RuntimeError( + "Attempted to use SOCKS support, but the `socksio` package is not " + "installed. Use 'pip install httpcore[socks]'." + ) + + +__all__ = [ + "HTTPConnection", + "ConnectionPool", + "HTTPProxy", + "HTTP11Connection", + "HTTP2Connection", + "ConnectionInterface", + "SOCKSProxy", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6cac71b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000..6138533 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-310.pyc new file mode 100644 index 0000000..1737754 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http11.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http11.cpython-310.pyc new file mode 100644 index 0000000..4ed2961 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http11.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http2.cpython-310.pyc new file mode 100644 index 0000000..88a4981 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-310.pyc new file mode 100644 index 0000000..3345648 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-310.pyc new file mode 100644 index 0000000..abce4f2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/interfaces.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-310.pyc new file mode 100644 index 0000000..0359f5a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/__pycache__/socks_proxy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection.py new file mode 100644 index 0000000..c3890f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection.py @@ -0,0 +1,220 @@ +import itertools +import logging +import ssl +from types import TracebackType +from typing import Iterable, Iterator, Optional, Type + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend, NetworkStream +from .._exceptions import ConnectError, ConnectTimeout +from .._models import Origin, Request, Response +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc. + + +logger = logging.getLogger("httpcore.connection") + + +def exponential_backoff(factor: float) -> Iterator[float]: + """ + Generate a geometric sequence that has a ratio of 2 and starts with 0. + + For example: + - `factor = 2`: `0, 2, 4, 8, 16, 32, 64, ...` + - `factor = 3`: `0, 3, 6, 12, 24, 48, 96, ...` + """ + yield 0 + for n in itertools.count(): + yield factor * 2**n + + +class HTTPConnection(ConnectionInterface): + def __init__( + self, + origin: Origin, + ssl_context: Optional[ssl.SSLContext] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[NetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + self._origin = origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connection: Optional[ConnectionInterface] = None + self._connect_failed: bool = False + self._request_lock = Lock() + self._socket_options = socket_options + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection to {self._origin}" + ) + + try: + with self._request_lock: + if self._connection is None: + stream = self._connect(request) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except BaseException as exc: + self._connect_failed = True + raise exc + + return self._connection.handle_request(request) + + def _connect(self, request: Request) -> NetworkStream: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + retries_left = self._retries + delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR) + + while True: + try: + if self._uds is None: + kwargs = { + "host": self._origin.host.decode("ascii"), + "port": self._origin.port, + "local_address": self._local_address, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + else: + kwargs = { + "path": self._uds, + "timeout": timeout, + "socket_options": self._socket_options, + } + with Trace( + "connect_unix_socket", logger, request, kwargs + ) as trace: + stream = self._network_backend.connect_unix_socket( + **kwargs + ) + trace.return_value = stream + + if self._origin.scheme in (b"https", b"wss"): + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + return stream + except (ConnectError, ConnectTimeout): + if retries_left <= 0: + raise + retries_left -= 1 + delay = next(delays) + with Trace("retry", logger, request, kwargs) as trace: + self._network_backend.sleep(delay) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def close(self) -> None: + if self._connection is not None: + with Trace("close", logger, None, {}): + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> "HTTPConnection": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self.close() diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py new file mode 100644 index 0000000..8dcf348 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py @@ -0,0 +1,380 @@ +import ssl +import sys +from types import TracebackType +from typing import Iterable, Iterator, Iterable, List, Optional, Type + +from .._backends.sync import SyncBackend +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol +from .._models import Origin, Request, Response +from .._synchronization import Event, ShieldCancellation, ThreadLock +from .connection import HTTPConnection +from .interfaces import ConnectionInterface, RequestInterface + + +class PoolRequest: + def __init__(self, request: Request) -> None: + self.request = request + self.connection: Optional[ConnectionInterface] = None + self._connection_acquired = Event() + + def assign_to_connection( + self, connection: Optional[ConnectionInterface] + ) -> None: + self.connection = connection + self._connection_acquired.set() + + def clear_connection(self) -> None: + self.connection = None + self._connection_acquired = Event() + + def wait_for_connection( + self, timeout: Optional[float] = None + ) -> ConnectionInterface: + if self.connection is None: + self._connection_acquired.wait(timeout=timeout) + assert self.connection is not None + return self.connection + + def is_queued(self) -> bool: + return self.connection is None + + +class ConnectionPool(RequestInterface): + """ + A connection pool for making HTTP requests. + """ + + def __init__( + self, + ssl_context: Optional[ssl.SSLContext] = None, + max_connections: Optional[int] = 10, + max_keepalive_connections: Optional[int] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[NetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish a + connection. + local_address: Local address to connect from. Can also be used to connect + using a particular address family. Using `local_address="0.0.0.0"` + will connect using an `AF_INET` address (IPv4), while using + `local_address="::"` will connect using an `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + socket_options: Socket options that have to be included + in the TCP socket when the connection was established. + """ + self._ssl_context = ssl_context + + self._max_connections = ( + sys.maxsize if max_connections is None else max_connections + ) + self._max_keepalive_connections = ( + sys.maxsize + if max_keepalive_connections is None + else max_keepalive_connections + ) + self._max_keepalive_connections = min( + self._max_connections, self._max_keepalive_connections + ) + + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._retries = retries + self._local_address = local_address + self._uds = uds + + self._network_backend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._socket_options = socket_options + + # The mutable state on a connection pool is the queue of incoming requests, + # and the set of connections that are servicing those requests. + self._connections: List[ConnectionInterface] = [] + self._requests: List[PoolRequest] = [] + + # We only mutate the state of the connection pool within an 'optional_thread_lock' + # context. This holds a threading lock unless we're running in async mode, + # in which case it is a no-op. + self._optional_thread_lock = ThreadLock() + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return HTTPConnection( + origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + retries=self._retries, + local_address=self._local_address, + uds=self._uds, + network_backend=self._network_backend, + socket_options=self._socket_options, + ) + + @property + def connections(self) -> List[ConnectionInterface]: + """ + Return a list of the connections currently in the pool. + + For example: + + ```python + >>> pool.connections + [ + , + , + , + ] + ``` + """ + return list(self._connections) + + def handle_request(self, request: Request) -> Response: + """ + Send an HTTP request, and return an HTTP response. + + This is the core implementation that is called into by `.request()` or `.stream()`. + """ + scheme = request.url.scheme.decode() + if scheme == "": + raise UnsupportedProtocol( + "Request URL is missing an 'http://' or 'https://' protocol." + ) + if scheme not in ("http", "https", "ws", "wss"): + raise UnsupportedProtocol( + f"Request URL has an unsupported protocol '{scheme}://'." + ) + + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("pool", None) + + with self._optional_thread_lock: + # Add the incoming request to our request queue. + pool_request = PoolRequest(request) + self._requests.append(pool_request) + + try: + while True: + with self._optional_thread_lock: + # Assign incoming requests to available connections, + # closing or creating new connections as required. + closing = self._assign_requests_to_connections() + self._close_connections(closing) + + # Wait until this request has an assigned connection. + connection = pool_request.wait_for_connection(timeout=timeout) + + try: + # Send the request on the assigned connection. + response = connection.handle_request( + pool_request.request + ) + except ConnectionNotAvailable: + # In some cases a connection may initially be available to + # handle a request, but then become unavailable. + # + # In this case we clear the connection and try again. + pool_request.clear_connection() + else: + break # pragma: nocover + + except BaseException as exc: + with self._optional_thread_lock: + # For any exception or cancellation we remove the request from + # the queue, and then re-assign requests to connections. + self._requests.remove(pool_request) + closing = self._assign_requests_to_connections() + + self._close_connections(closing) + raise exc from None + + # Return the response. Note that in this case we still have to manage + # the point at which the response is closed. + assert isinstance(response.stream, Iterable) + return Response( + status=response.status, + headers=response.headers, + content=PoolByteStream( + stream=response.stream, pool_request=pool_request, pool=self + ), + extensions=response.extensions, + ) + + def _assign_requests_to_connections(self) -> List[ConnectionInterface]: + """ + Manage the state of the connection pool, assigning incoming + requests to connections as available. + + Called whenever a new request is added or removed from the pool. + + Any closing connections are returned, allowing the I/O for closing + those connections to be handled seperately. + """ + closing_connections = [] + + # First we handle cleaning up any connections that are closed, + # have expired their keep-alive, or surplus idle connections. + for connection in list(self._connections): + if connection.is_closed(): + # log: "removing closed connection" + self._connections.remove(connection) + elif connection.has_expired(): + # log: "closing expired connection" + self._connections.remove(connection) + closing_connections.append(connection) + elif ( + connection.is_idle() + and len([connection.is_idle() for connection in self._connections]) + > self._max_keepalive_connections + ): + # log: "closing idle connection" + self._connections.remove(connection) + closing_connections.append(connection) + + # Assign queued requests to connections. + queued_requests = [request for request in self._requests if request.is_queued()] + for pool_request in queued_requests: + origin = pool_request.request.url.origin + avilable_connections = [ + connection + for connection in self._connections + if connection.can_handle_request(origin) and connection.is_available() + ] + idle_connections = [ + connection for connection in self._connections if connection.is_idle() + ] + + # There are three cases for how we may be able to handle the request: + # + # 1. There is an existing connection that can handle the request. + # 2. We can create a new connection to handle the request. + # 3. We can close an idle connection and then create a new connection + # to handle the request. + if avilable_connections: + # log: "reusing existing connection" + connection = avilable_connections[0] + pool_request.assign_to_connection(connection) + elif len(self._connections) < self._max_connections: + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + elif idle_connections: + # log: "closing idle connection" + connection = idle_connections[0] + self._connections.remove(connection) + closing_connections.append(connection) + # log: "creating new connection" + connection = self.create_connection(origin) + self._connections.append(connection) + pool_request.assign_to_connection(connection) + + return closing_connections + + def _close_connections(self, closing: List[ConnectionInterface]) -> None: + # Close connections which have been removed from the pool. + with ShieldCancellation(): + for connection in closing: + connection.close() + + def close(self) -> None: + # Explicitly close the connection pool. + # Clears all existing requests and connections. + with self._optional_thread_lock: + closing_connections = list(self._connections) + self._connections = [] + self._close_connections(closing_connections) + + def __enter__(self) -> "ConnectionPool": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self.close() + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + with self._optional_thread_lock: + request_is_queued = [request.is_queued() for request in self._requests] + connection_is_idle = [ + connection.is_idle() for connection in self._connections + ] + + num_active_requests = request_is_queued.count(False) + num_queued_requests = request_is_queued.count(True) + num_active_connections = connection_is_idle.count(False) + num_idle_connections = connection_is_idle.count(True) + + requests_info = ( + f"Requests: {num_active_requests} active, {num_queued_requests} queued" + ) + connection_info = ( + f"Connections: {num_active_connections} active, {num_idle_connections} idle" + ) + + return f"<{class_name} [{requests_info} | {connection_info}]>" + + +class PoolByteStream: + def __init__( + self, + stream: Iterable[bytes], + pool_request: PoolRequest, + pool: ConnectionPool, + ) -> None: + self._stream = stream + self._pool_request = pool_request + self._pool = pool + self._closed = False + + def __iter__(self) -> Iterator[bytes]: + try: + for part in self._stream: + yield part + except BaseException as exc: + self.close() + raise exc from None + + def close(self) -> None: + if not self._closed: + self._closed = True + with ShieldCancellation(): + if hasattr(self._stream, "close"): + self._stream.close() + + with self._pool._optional_thread_lock: + self._pool._requests.remove(self._pool_request) + closing = self._pool._assign_requests_to_connections() + + self._pool._close_connections(closing) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http11.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http11.py new file mode 100644 index 0000000..a74ff8e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http11.py @@ -0,0 +1,386 @@ +import enum +import logging +import ssl +import time +from types import TracebackType +from typing import ( + Any, + Iterable, + Iterator, + List, + Optional, + Tuple, + Type, + Union, +) + +import h11 + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, + WriteError, + map_exceptions, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http11") + + +# A subset of `h11.Event` types supported by `_send_event` +H11SendEvent = Union[ + h11.Request, + h11.Data, + h11.EndOfMessage, +] + + +class HTTPConnectionState(enum.IntEnum): + NEW = 0 + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP11Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + MAX_INCOMPLETE_EVENT_SIZE = 100 * 1024 + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: Optional[float] = None, + ) -> None: + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: Optional[float] = keepalive_expiry + self._expire_at: Optional[float] = None + self._state = HTTPConnectionState.NEW + self._state_lock = Lock() + self._request_count = 0 + self._h11_state = h11.Connection( + our_role=h11.CLIENT, + max_incomplete_event_size=self.MAX_INCOMPLETE_EVENT_SIZE, + ) + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE): + self._request_count += 1 + self._state = HTTPConnectionState.ACTIVE + self._expire_at = None + else: + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request} + try: + with Trace( + "send_request_headers", logger, request, kwargs + ) as trace: + self._send_request_headers(**kwargs) + with Trace("send_request_body", logger, request, kwargs) as trace: + self._send_request_body(**kwargs) + except WriteError: + # If we get a write error while we're writing the request, + # then we supress this error and move on to attempting to + # read the response. Servers can sometimes close the request + # pre-emptively and then respond with a well formed HTTP + # error response. + pass + + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + ( + http_version, + status, + reason_phrase, + headers, + trailing_data, + ) = self._receive_response_headers(**kwargs) + trace.return_value = ( + http_version, + status, + reason_phrase, + headers, + ) + + network_stream = self._network_stream + + # CONNECT or Upgrade request + if (status == 101) or ( + (request.method == b"CONNECT") and (200 <= status < 300) + ): + network_stream = HTTP11UpgradeStream(network_stream, trailing_data) + + return Response( + status=status, + headers=headers, + content=HTTP11ConnectionByteStream(self, request), + extensions={ + "http_version": http_version, + "reason_phrase": reason_phrase, + "network_stream": network_stream, + }, + ) + except BaseException as exc: + with ShieldCancellation(): + with Trace("response_closed", logger, request) as trace: + self._response_closed() + raise exc + + # Sending the request... + + def _send_request_headers(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with map_exceptions({h11.LocalProtocolError: LocalProtocolError}): + event = h11.Request( + method=request.method, + target=request.url.target, + headers=request.headers, + ) + self._send_event(event, timeout=timeout) + + def _send_request_body(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + assert isinstance(request.stream, Iterable) + for chunk in request.stream: + event = h11.Data(data=chunk) + self._send_event(event, timeout=timeout) + + self._send_event(h11.EndOfMessage(), timeout=timeout) + + def _send_event( + self, event: h11.Event, timeout: Optional[float] = None + ) -> None: + bytes_to_send = self._h11_state.send(event) + if bytes_to_send is not None: + self._network_stream.write(bytes_to_send, timeout=timeout) + + # Receiving the response... + + def _receive_response_headers( + self, request: Request + ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]], bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Response): + break + if ( + isinstance(event, h11.InformationalResponse) + and event.status_code == 101 + ): + break + + http_version = b"HTTP/" + event.http_version + + # h11 version 0.11+ supports a `raw_items` interface to get the + # raw header casing, rather than the enforced lowercase headers. + headers = event.headers.raw_items() + + trailing_data, _ = self._h11_state.trailing_data + + return http_version, event.status_code, event.reason, headers, trailing_data + + def _receive_response_body(self, request: Request) -> Iterator[bytes]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + while True: + event = self._receive_event(timeout=timeout) + if isinstance(event, h11.Data): + yield bytes(event.data) + elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)): + break + + def _receive_event( + self, timeout: Optional[float] = None + ) -> Union[h11.Event, Type[h11.PAUSED]]: + while True: + with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}): + event = self._h11_state.next_event() + + if event is h11.NEED_DATA: + data = self._network_stream.read( + self.READ_NUM_BYTES, timeout=timeout + ) + + # If we feed this case through h11 we'll raise an exception like: + # + # httpcore.RemoteProtocolError: can't handle event type + # ConnectionClosed when role=SERVER and state=SEND_RESPONSE + # + # Which is accurate, but not very informative from an end-user + # perspective. Instead we handle this case distinctly and treat + # it as a ConnectError. + if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE: + msg = "Server disconnected without sending a response." + raise RemoteProtocolError(msg) + + self._h11_state.receive_data(data) + else: + # mypy fails to narrow the type in the above if statement above + return event # type: ignore[return-value] + + def _response_closed(self) -> None: + with self._state_lock: + if ( + self._h11_state.our_state is h11.DONE + and self._h11_state.their_state is h11.DONE + ): + self._state = HTTPConnectionState.IDLE + self._h11_state.start_next_cycle() + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + else: + self.close() + + # Once the connection is no longer required... + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # The ConnectionInterface methods provide information about the state of + # the connection, allowing for a connection pooling implementation to + # determine when to reuse and when to close the connection... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + # Note that HTTP/1.1 connections in the "NEW" state are not treated as + # being "available". The control flow which created the connection will + # be able to send an outgoing request, but the connection will not be + # acquired from the connection pool for any other request. + return self._state == HTTPConnectionState.IDLE + + def has_expired(self) -> bool: + now = time.monotonic() + keepalive_expired = self._expire_at is not None and now > self._expire_at + + # If the HTTP connection is idle but the socket is readable, then the + # only valid state is that the socket is about to return b"", indicating + # a server-initiated disconnect. + server_disconnected = ( + self._state == HTTPConnectionState.IDLE + and self._network_stream.get_extra_info("is_readable") + ) + + return keepalive_expired or server_disconnected + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/1.1, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> "HTTP11Connection": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self.close() + + +class HTTP11ConnectionByteStream: + def __init__(self, connection: HTTP11Connection, request: Request) -> None: + self._connection = connection + self._request = request + self._closed = False + + def __iter__(self) -> Iterator[bytes]: + kwargs = {"request": self._request} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body(**kwargs): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + with Trace("response_closed", logger, self._request): + self._connection._response_closed() + + +class HTTP11UpgradeStream(NetworkStream): + def __init__(self, stream: NetworkStream, leading_data: bytes) -> None: + self._stream = stream + self._leading_data = leading_data + + def read(self, max_bytes: int, timeout: Optional[float] = None) -> bytes: + if self._leading_data: + buffer = self._leading_data[:max_bytes] + self._leading_data = self._leading_data[max_bytes:] + return buffer + else: + return self._stream.read(max_bytes, timeout) + + def write(self, buffer: bytes, timeout: Optional[float] = None) -> None: + self._stream.write(buffer, timeout) + + def close(self) -> None: + self._stream.close() + + def start_tls( + self, + ssl_context: ssl.SSLContext, + server_hostname: Optional[str] = None, + timeout: Optional[float] = None, + ) -> NetworkStream: + return self._stream.start_tls(ssl_context, server_hostname, timeout) + + def get_extra_info(self, info: str) -> Any: + return self._stream.get_extra_info(info) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http2.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http2.py new file mode 100644 index 0000000..1ee4bbb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http2.py @@ -0,0 +1,589 @@ +import enum +import logging +import time +import types +import typing + +import h2.config +import h2.connection +import h2.events +import h2.exceptions +import h2.settings + +from .._backends.base import NetworkStream +from .._exceptions import ( + ConnectionNotAvailable, + LocalProtocolError, + RemoteProtocolError, +) +from .._models import Origin, Request, Response +from .._synchronization import Lock, Semaphore, ShieldCancellation +from .._trace import Trace +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.http2") + + +def has_body_headers(request: Request) -> bool: + return any( + k.lower() == b"content-length" or k.lower() == b"transfer-encoding" + for k, v in request.headers + ) + + +class HTTPConnectionState(enum.IntEnum): + ACTIVE = 1 + IDLE = 2 + CLOSED = 3 + + +class HTTP2Connection(ConnectionInterface): + READ_NUM_BYTES = 64 * 1024 + CONFIG = h2.config.H2Configuration(validate_inbound_headers=False) + + def __init__( + self, + origin: Origin, + stream: NetworkStream, + keepalive_expiry: typing.Optional[float] = None, + ): + self._origin = origin + self._network_stream = stream + self._keepalive_expiry: typing.Optional[float] = keepalive_expiry + self._h2_state = h2.connection.H2Connection(config=self.CONFIG) + self._state = HTTPConnectionState.IDLE + self._expire_at: typing.Optional[float] = None + self._request_count = 0 + self._init_lock = Lock() + self._state_lock = Lock() + self._read_lock = Lock() + self._write_lock = Lock() + self._sent_connection_init = False + self._used_all_stream_ids = False + self._connection_error = False + + # Mapping from stream ID to response stream events. + self._events: typing.Dict[ + int, + typing.Union[ + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ], + ] = {} + + # Connection terminated events are stored as state since + # we need to handle them for all streams. + self._connection_terminated: typing.Optional[h2.events.ConnectionTerminated] = ( + None + ) + + self._read_exception: typing.Optional[Exception] = None + self._write_exception: typing.Optional[Exception] = None + + def handle_request(self, request: Request) -> Response: + if not self.can_handle_request(request.url.origin): + # This cannot occur in normal operation, since the connection pool + # will only send requests on connections that handle them. + # It's in place simply for resilience as a guard against incorrect + # usage, for anyone working directly with httpcore connections. + raise RuntimeError( + f"Attempted to send request to {request.url.origin} on connection " + f"to {self._origin}" + ) + + with self._state_lock: + if self._state in (HTTPConnectionState.ACTIVE, HTTPConnectionState.IDLE): + self._request_count += 1 + self._expire_at = None + self._state = HTTPConnectionState.ACTIVE + else: + raise ConnectionNotAvailable() + + with self._init_lock: + if not self._sent_connection_init: + try: + kwargs = {"request": request} + with Trace("send_connection_init", logger, request, kwargs): + self._send_connection_init(**kwargs) + except BaseException as exc: + with ShieldCancellation(): + self.close() + raise exc + + self._sent_connection_init = True + + # Initially start with just 1 until the remote server provides + # its max_concurrent_streams value + self._max_streams = 1 + + local_settings_max_streams = ( + self._h2_state.local_settings.max_concurrent_streams + ) + self._max_streams_semaphore = Semaphore(local_settings_max_streams) + + for _ in range(local_settings_max_streams - self._max_streams): + self._max_streams_semaphore.acquire() + + self._max_streams_semaphore.acquire() + + try: + stream_id = self._h2_state.get_next_available_stream_id() + self._events[stream_id] = [] + except h2.exceptions.NoAvailableStreamIDError: # pragma: nocover + self._used_all_stream_ids = True + self._request_count -= 1 + raise ConnectionNotAvailable() + + try: + kwargs = {"request": request, "stream_id": stream_id} + with Trace("send_request_headers", logger, request, kwargs): + self._send_request_headers(request=request, stream_id=stream_id) + with Trace("send_request_body", logger, request, kwargs): + self._send_request_body(request=request, stream_id=stream_id) + with Trace( + "receive_response_headers", logger, request, kwargs + ) as trace: + status, headers = self._receive_response( + request=request, stream_id=stream_id + ) + trace.return_value = (status, headers) + + return Response( + status=status, + headers=headers, + content=HTTP2ConnectionByteStream(self, request, stream_id=stream_id), + extensions={ + "http_version": b"HTTP/2", + "network_stream": self._network_stream, + "stream_id": stream_id, + }, + ) + except BaseException as exc: # noqa: PIE786 + with ShieldCancellation(): + kwargs = {"stream_id": stream_id} + with Trace("response_closed", logger, request, kwargs): + self._response_closed(stream_id=stream_id) + + if isinstance(exc, h2.exceptions.ProtocolError): + # One case where h2 can raise a protocol error is when a + # closed frame has been seen by the state machine. + # + # This happens when one stream is reading, and encounters + # a GOAWAY event. Other flows of control may then raise + # a protocol error at any point they interact with the 'h2_state'. + # + # In this case we'll have stored the event, and should raise + # it as a RemoteProtocolError. + if self._connection_terminated: # pragma: nocover + raise RemoteProtocolError(self._connection_terminated) + # If h2 raises a protocol error in some other state then we + # must somehow have made a protocol violation. + raise LocalProtocolError(exc) # pragma: nocover + + raise exc + + def _send_connection_init(self, request: Request) -> None: + """ + The HTTP/2 connection requires some initial setup before we can start + using individual request/response streams on it. + """ + # Need to set these manually here instead of manipulating via + # __setitem__() otherwise the H2Connection will emit SettingsUpdate + # frames in addition to sending the undesired defaults. + self._h2_state.local_settings = h2.settings.Settings( + client=True, + initial_values={ + # Disable PUSH_PROMISE frames from the server since we don't do anything + # with them for now. Maybe when we support caching? + h2.settings.SettingCodes.ENABLE_PUSH: 0, + # These two are taken from h2 for safe defaults + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100, + h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 65536, + }, + ) + + # Some websites (*cough* Yahoo *cough*) balk at this setting being + # present in the initial handshake since it's not defined in the original + # RFC despite the RFC mandating ignoring settings you don't know about. + del self._h2_state.local_settings[ + h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL + ] + + self._h2_state.initiate_connection() + self._h2_state.increment_flow_control_window(2**24) + self._write_outgoing_data(request) + + # Sending the request... + + def _send_request_headers(self, request: Request, stream_id: int) -> None: + """ + Send the request headers to a given stream ID. + """ + end_stream = not has_body_headers(request) + + # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'. + # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require + # HTTP/1.1 style headers, and map them appropriately if we end up on + # an HTTP/2 connection. + authority = [v for k, v in request.headers if k.lower() == b"host"][0] + + headers = [ + (b":method", request.method), + (b":authority", authority), + (b":scheme", request.url.scheme), + (b":path", request.url.target), + ] + [ + (k.lower(), v) + for k, v in request.headers + if k.lower() + not in ( + b"host", + b"transfer-encoding", + ) + ] + + self._h2_state.send_headers(stream_id, headers, end_stream=end_stream) + self._h2_state.increment_flow_control_window(2**24, stream_id=stream_id) + self._write_outgoing_data(request) + + def _send_request_body(self, request: Request, stream_id: int) -> None: + """ + Iterate over the request body sending it to a given stream ID. + """ + if not has_body_headers(request): + return + + assert isinstance(request.stream, typing.Iterable) + for data in request.stream: + self._send_stream_data(request, stream_id, data) + self._send_end_stream(request, stream_id) + + def _send_stream_data( + self, request: Request, stream_id: int, data: bytes + ) -> None: + """ + Send a single chunk of data in one or more data frames. + """ + while data: + max_flow = self._wait_for_outgoing_flow(request, stream_id) + chunk_size = min(len(data), max_flow) + chunk, data = data[:chunk_size], data[chunk_size:] + self._h2_state.send_data(stream_id, chunk) + self._write_outgoing_data(request) + + def _send_end_stream(self, request: Request, stream_id: int) -> None: + """ + Send an empty data frame on on a given stream ID with the END_STREAM flag set. + """ + self._h2_state.end_stream(stream_id) + self._write_outgoing_data(request) + + # Receiving the response... + + def _receive_response( + self, request: Request, stream_id: int + ) -> typing.Tuple[int, typing.List[typing.Tuple[bytes, bytes]]]: + """ + Return the response status code and headers for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.ResponseReceived): + break + + status_code = 200 + headers = [] + for k, v in event.headers: + if k == b":status": + status_code = int(v.decode("ascii", errors="ignore")) + elif not k.startswith(b":"): + headers.append((k, v)) + + return (status_code, headers) + + def _receive_response_body( + self, request: Request, stream_id: int + ) -> typing.Iterator[bytes]: + """ + Iterator that returns the bytes of the response body for a given stream ID. + """ + while True: + event = self._receive_stream_event(request, stream_id) + if isinstance(event, h2.events.DataReceived): + amount = event.flow_controlled_length + self._h2_state.acknowledge_received_data(amount, stream_id) + self._write_outgoing_data(request) + yield event.data + elif isinstance(event, h2.events.StreamEnded): + break + + def _receive_stream_event( + self, request: Request, stream_id: int + ) -> typing.Union[ + h2.events.ResponseReceived, h2.events.DataReceived, h2.events.StreamEnded + ]: + """ + Return the next available event for a given stream ID. + + Will read more data from the network if required. + """ + while not self._events.get(stream_id): + self._receive_events(request, stream_id) + event = self._events[stream_id].pop(0) + if isinstance(event, h2.events.StreamReset): + raise RemoteProtocolError(event) + return event + + def _receive_events( + self, request: Request, stream_id: typing.Optional[int] = None + ) -> None: + """ + Read some data from the network until we see one or more events + for a given stream ID. + """ + with self._read_lock: + if self._connection_terminated is not None: + last_stream_id = self._connection_terminated.last_stream_id + if stream_id and last_stream_id and stream_id > last_stream_id: + self._request_count -= 1 + raise ConnectionNotAvailable() + raise RemoteProtocolError(self._connection_terminated) + + # This conditional is a bit icky. We don't want to block reading if we've + # actually got an event to return for a given stream. We need to do that + # check *within* the atomic read lock. Though it also need to be optional, + # because when we call it from `_wait_for_outgoing_flow` we *do* want to + # block until we've available flow control, event when we have events + # pending for the stream ID we're attempting to send on. + if stream_id is None or not self._events.get(stream_id): + events = self._read_incoming_data(request) + for event in events: + if isinstance(event, h2.events.RemoteSettingsChanged): + with Trace( + "receive_remote_settings", logger, request + ) as trace: + self._receive_remote_settings_change(event) + trace.return_value = event + + elif isinstance( + event, + ( + h2.events.ResponseReceived, + h2.events.DataReceived, + h2.events.StreamEnded, + h2.events.StreamReset, + ), + ): + if event.stream_id in self._events: + self._events[event.stream_id].append(event) + + elif isinstance(event, h2.events.ConnectionTerminated): + self._connection_terminated = event + + self._write_outgoing_data(request) + + def _receive_remote_settings_change(self, event: h2.events.Event) -> None: + max_concurrent_streams = event.changed_settings.get( + h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS + ) + if max_concurrent_streams: + new_max_streams = min( + max_concurrent_streams.new_value, + self._h2_state.local_settings.max_concurrent_streams, + ) + if new_max_streams and new_max_streams != self._max_streams: + while new_max_streams > self._max_streams: + self._max_streams_semaphore.release() + self._max_streams += 1 + while new_max_streams < self._max_streams: + self._max_streams_semaphore.acquire() + self._max_streams -= 1 + + def _response_closed(self, stream_id: int) -> None: + self._max_streams_semaphore.release() + del self._events[stream_id] + with self._state_lock: + if self._connection_terminated and not self._events: + self.close() + + elif self._state == HTTPConnectionState.ACTIVE and not self._events: + self._state = HTTPConnectionState.IDLE + if self._keepalive_expiry is not None: + now = time.monotonic() + self._expire_at = now + self._keepalive_expiry + if self._used_all_stream_ids: # pragma: nocover + self.close() + + def close(self) -> None: + # Note that this method unilaterally closes the connection, and does + # not have any kind of locking in place around it. + self._h2_state.close_connection() + self._state = HTTPConnectionState.CLOSED + self._network_stream.close() + + # Wrappers around network read/write operations... + + def _read_incoming_data( + self, request: Request + ) -> typing.List[h2.events.Event]: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("read", None) + + if self._read_exception is not None: + raise self._read_exception # pragma: nocover + + try: + data = self._network_stream.read(self.READ_NUM_BYTES, timeout) + if data == b"": + raise RemoteProtocolError("Server disconnected") + except Exception as exc: + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future reads. + # (For example, this means that a single read timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._read_exception = exc + self._connection_error = True + raise exc + + events: typing.List[h2.events.Event] = self._h2_state.receive_data(data) + + return events + + def _write_outgoing_data(self, request: Request) -> None: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("write", None) + + with self._write_lock: + data_to_send = self._h2_state.data_to_send() + + if self._write_exception is not None: + raise self._write_exception # pragma: nocover + + try: + self._network_stream.write(data_to_send, timeout) + except Exception as exc: # pragma: nocover + # If we get a network error we should: + # + # 1. Save the exception and just raise it immediately on any future write. + # (For example, this means that a single write timeout or disconnect will + # immediately close all pending streams. Without requiring multiple + # sequential timeouts.) + # 2. Mark the connection as errored, so that we don't accept any other + # incoming requests. + self._write_exception = exc + self._connection_error = True + raise exc + + # Flow control... + + def _wait_for_outgoing_flow(self, request: Request, stream_id: int) -> int: + """ + Returns the maximum allowable outgoing flow for a given stream. + + If the allowable flow is zero, then waits on the network until + WindowUpdated frames have increased the flow rate. + https://tools.ietf.org/html/rfc7540#section-6.9 + """ + local_flow: int = self._h2_state.local_flow_control_window(stream_id) + max_frame_size: int = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + while flow == 0: + self._receive_events(request) + local_flow = self._h2_state.local_flow_control_window(stream_id) + max_frame_size = self._h2_state.max_outbound_frame_size + flow = min(local_flow, max_frame_size) + return flow + + # Interface for connection pooling... + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._origin + + def is_available(self) -> bool: + return ( + self._state != HTTPConnectionState.CLOSED + and not self._connection_error + and not self._used_all_stream_ids + and not ( + self._h2_state.state_machine.state + == h2.connection.ConnectionState.CLOSED + ) + ) + + def has_expired(self) -> bool: + now = time.monotonic() + return self._expire_at is not None and now > self._expire_at + + def is_idle(self) -> bool: + return self._state == HTTPConnectionState.IDLE + + def is_closed(self) -> bool: + return self._state == HTTPConnectionState.CLOSED + + def info(self) -> str: + origin = str(self._origin) + return ( + f"{origin!r}, HTTP/2, {self._state.name}, " + f"Request Count: {self._request_count}" + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + origin = str(self._origin) + return ( + f"<{class_name} [{origin!r}, {self._state.name}, " + f"Request Count: {self._request_count}]>" + ) + + # These context managers are not used in the standard flow, but are + # useful for testing or working with connection instances directly. + + def __enter__(self) -> "HTTP2Connection": + return self + + def __exit__( + self, + exc_type: typing.Optional[typing.Type[BaseException]] = None, + exc_value: typing.Optional[BaseException] = None, + traceback: typing.Optional[types.TracebackType] = None, + ) -> None: + self.close() + + +class HTTP2ConnectionByteStream: + def __init__( + self, connection: HTTP2Connection, request: Request, stream_id: int + ) -> None: + self._connection = connection + self._request = request + self._stream_id = stream_id + self._closed = False + + def __iter__(self) -> typing.Iterator[bytes]: + kwargs = {"request": self._request, "stream_id": self._stream_id} + try: + with Trace("receive_response_body", logger, self._request, kwargs): + for chunk in self._connection._receive_response_body( + request=self._request, stream_id=self._stream_id + ): + yield chunk + except BaseException as exc: + # If we get an exception while streaming the response, + # we want to close the response (and possibly the connection) + # before raising that exception. + with ShieldCancellation(): + self.close() + raise exc + + def close(self) -> None: + if not self._closed: + self._closed = True + kwargs = {"stream_id": self._stream_id} + with Trace("response_closed", logger, self._request, kwargs): + self._connection._response_closed(stream_id=self._stream_id) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http_proxy.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http_proxy.py new file mode 100644 index 0000000..6acac9a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/http_proxy.py @@ -0,0 +1,368 @@ +import logging +import ssl +from base64 import b64encode +from typing import Iterable, List, Mapping, Optional, Sequence, Tuple, Union + +from .._backends.base import SOCKET_OPTION, NetworkBackend +from .._exceptions import ProxyError +from .._models import ( + URL, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, +) +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection import HTTPConnection +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +HeadersAsSequence = Sequence[Tuple[Union[bytes, str], Union[bytes, str]]] +HeadersAsMapping = Mapping[Union[bytes, str], Union[bytes, str]] + + +logger = logging.getLogger("httpcore.proxy") + + +def merge_headers( + default_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, + override_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, +) -> List[Tuple[bytes, bytes]]: + """ + Append default_headers and override_headers, de-duplicating if a key exists + in both cases. + """ + default_headers = [] if default_headers is None else list(default_headers) + override_headers = [] if override_headers is None else list(override_headers) + has_override = set(key.lower() for key, value in override_headers) + default_headers = [ + (key, value) + for key, value in default_headers + if key.lower() not in has_override + ] + return default_headers + override_headers + + +def build_auth_header(username: bytes, password: bytes) -> bytes: + userpass = username + b":" + password + return b"Basic " + b64encode(userpass) + + +class HTTPProxy(ConnectionPool): + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: Union[URL, bytes, str], + proxy_auth: Optional[Tuple[Union[bytes, str], Union[bytes, str]]] = None, + proxy_headers: Union[HeadersAsMapping, HeadersAsSequence, None] = None, + ssl_context: Optional[ssl.SSLContext] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + max_connections: Optional[int] = 10, + max_keepalive_connections: Optional[int] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + local_address: Optional[str] = None, + uds: Optional[str] = None, + network_backend: Optional[NetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + proxy_auth: Any proxy authentication as a two-tuple of + (username, password). May be either bytes or ascii-only str. + proxy_headers: Any HTTP headers to use for the proxy requests. + For example `{"Proxy-Authorization": "Basic :"}`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + proxy_ssl_context: The same as `ssl_context`, but for a proxy server rather than a remote origin. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + local_address=local_address, + uds=uds, + socket_options=socket_options, + ) + + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if ( + self._proxy_url.scheme == b"http" and proxy_ssl_context is not None + ): # pragma: no cover + raise RuntimeError( + "The `proxy_ssl_context` argument is not allowed for the http scheme" + ) + + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + if proxy_auth is not None: + username = enforce_bytes(proxy_auth[0], name="proxy_auth") + password = enforce_bytes(proxy_auth[1], name="proxy_auth") + authorization = build_auth_header(username, password) + self._proxy_headers = [ + (b"Proxy-Authorization", authorization) + ] + self._proxy_headers + + def create_connection(self, origin: Origin) -> ConnectionInterface: + if origin.scheme == b"http": + return ForwardHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + keepalive_expiry=self._keepalive_expiry, + network_backend=self._network_backend, + proxy_ssl_context=self._proxy_ssl_context, + ) + return TunnelHTTPConnection( + proxy_origin=self._proxy_url.origin, + proxy_headers=self._proxy_headers, + remote_origin=origin, + ssl_context=self._ssl_context, + proxy_ssl_context=self._proxy_ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class ForwardHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_headers: Union[HeadersAsMapping, HeadersAsSequence, None] = None, + keepalive_expiry: Optional[float] = None, + network_backend: Optional[NetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + ) -> None: + self._connection = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._remote_origin = remote_origin + + def handle_request(self, request: Request) -> Response: + headers = merge_headers(self._proxy_headers, request.headers) + url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=bytes(request.url), + ) + proxy_request = Request( + method=request.method, + url=url, + headers=headers, + content=request.stream, + extensions=request.extensions, + ) + return self._connection.handle_request(proxy_request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" + + +class TunnelHTTPConnection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + ssl_context: Optional[ssl.SSLContext] = None, + proxy_ssl_context: Optional[ssl.SSLContext] = None, + proxy_headers: Optional[Sequence[Tuple[bytes, bytes]]] = None, + keepalive_expiry: Optional[float] = None, + http1: bool = True, + http2: bool = False, + network_backend: Optional[NetworkBackend] = None, + socket_options: Optional[Iterable[SOCKET_OPTION]] = None, + ) -> None: + self._connection: ConnectionInterface = HTTPConnection( + origin=proxy_origin, + keepalive_expiry=keepalive_expiry, + network_backend=network_backend, + socket_options=socket_options, + ssl_context=proxy_ssl_context, + ) + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._proxy_ssl_context = proxy_ssl_context + self._proxy_headers = enforce_headers(proxy_headers, name="proxy_headers") + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + self._connect_lock = Lock() + self._connected = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if not self._connected: + target = b"%b:%d" % (self._remote_origin.host, self._remote_origin.port) + + connect_url = URL( + scheme=self._proxy_origin.scheme, + host=self._proxy_origin.host, + port=self._proxy_origin.port, + target=target, + ) + connect_headers = merge_headers( + [(b"Host", target), (b"Accept", b"*/*")], self._proxy_headers + ) + connect_request = Request( + method=b"CONNECT", + url=connect_url, + headers=connect_headers, + extensions=request.extensions, + ) + connect_response = self._connection.handle_request( + connect_request + ) + + if connect_response.status < 200 or connect_response.status > 299: + reason_bytes = connect_response.extensions.get("reason_phrase", b"") + reason_str = reason_bytes.decode("ascii", errors="ignore") + msg = "%d %s" % (connect_response.status, reason_str) + self._connection.close() + raise ProxyError(msg) + + stream = connect_response.extensions["network_stream"] + + # Upgrade the stream to SSL + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or (self._http2 and not self._http1): + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + + self._connected = True + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + self._connection.close() + + def info(self) -> str: + return self._connection.info() + + def is_available(self) -> bool: + return self._connection.is_available() + + def has_expired(self) -> bool: + return self._connection.has_expired() + + def is_idle(self) -> bool: + return self._connection.is_idle() + + def is_closed(self) -> bool: + return self._connection.is_closed() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/interfaces.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/interfaces.py new file mode 100644 index 0000000..5e95be1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/interfaces.py @@ -0,0 +1,135 @@ +from contextlib import contextmanager +from typing import Iterator, Optional, Union + +from .._models import ( + URL, + Extensions, + HeaderTypes, + Origin, + Request, + Response, + enforce_bytes, + enforce_headers, + enforce_url, + include_request_headers, +) + + +class RequestInterface: + def request( + self, + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterator[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> Response: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + response.read() + finally: + response.close() + return response + + @contextmanager + def stream( + self, + method: Union[bytes, str], + url: Union[URL, bytes, str], + *, + headers: HeaderTypes = None, + content: Union[bytes, Iterator[bytes], None] = None, + extensions: Optional[Extensions] = None, + ) -> Iterator[Response]: + # Strict type checking on our parameters. + method = enforce_bytes(method, name="method") + url = enforce_url(url, name="url") + headers = enforce_headers(headers, name="headers") + + # Include Host header, and optionally Content-Length or Transfer-Encoding. + headers = include_request_headers(headers, url=url, content=content) + + request = Request( + method=method, + url=url, + headers=headers, + content=content, + extensions=extensions, + ) + response = self.handle_request(request) + try: + yield response + finally: + response.close() + + def handle_request(self, request: Request) -> Response: + raise NotImplementedError() # pragma: nocover + + +class ConnectionInterface(RequestInterface): + def close(self) -> None: + raise NotImplementedError() # pragma: nocover + + def info(self) -> str: + raise NotImplementedError() # pragma: nocover + + def can_handle_request(self, origin: Origin) -> bool: + raise NotImplementedError() # pragma: nocover + + def is_available(self) -> bool: + """ + Return `True` if the connection is currently able to accept an + outgoing request. + + An HTTP/1.1 connection will only be available if it is currently idle. + + An HTTP/2 connection will be available so long as the stream ID space is + not yet exhausted, and the connection is not in an error state. + + While the connection is being established we may not yet know if it is going + to result in an HTTP/1.1 or HTTP/2 connection. The connection should be + treated as being available, but might ultimately raise `NewConnectionRequired` + required exceptions if multiple requests are attempted over a connection + that ends up being established as HTTP/1.1. + """ + raise NotImplementedError() # pragma: nocover + + def has_expired(self) -> bool: + """ + Return `True` if the connection is in a state where it should be closed. + + This either means that the connection is idle and it has passed the + expiry time on its keep-alive, or that server has sent an EOF. + """ + raise NotImplementedError() # pragma: nocover + + def is_idle(self) -> bool: + """ + Return `True` if the connection is currently idle. + """ + raise NotImplementedError() # pragma: nocover + + def is_closed(self) -> bool: + """ + Return `True` if the connection has been closed. + + Used when a response is closed to determine if the connection may be + returned to the connection pool or not. + """ + raise NotImplementedError() # pragma: nocover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/socks_proxy.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/socks_proxy.py new file mode 100644 index 0000000..502e4d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_sync/socks_proxy.py @@ -0,0 +1,342 @@ +import logging +import ssl +import typing + +from socksio import socks5 + +from .._backends.sync import SyncBackend +from .._backends.base import NetworkBackend, NetworkStream +from .._exceptions import ConnectionNotAvailable, ProxyError +from .._models import URL, Origin, Request, Response, enforce_bytes, enforce_url +from .._ssl import default_ssl_context +from .._synchronization import Lock +from .._trace import Trace +from .connection_pool import ConnectionPool +from .http11 import HTTP11Connection +from .interfaces import ConnectionInterface + +logger = logging.getLogger("httpcore.socks") + + +AUTH_METHODS = { + b"\x00": "NO AUTHENTICATION REQUIRED", + b"\x01": "GSSAPI", + b"\x02": "USERNAME/PASSWORD", + b"\xff": "NO ACCEPTABLE METHODS", +} + +REPLY_CODES = { + b"\x00": "Succeeded", + b"\x01": "General SOCKS server failure", + b"\x02": "Connection not allowed by ruleset", + b"\x03": "Network unreachable", + b"\x04": "Host unreachable", + b"\x05": "Connection refused", + b"\x06": "TTL expired", + b"\x07": "Command not supported", + b"\x08": "Address type not supported", +} + + +def _init_socks5_connection( + stream: NetworkStream, + *, + host: bytes, + port: int, + auth: typing.Optional[typing.Tuple[bytes, bytes]] = None, +) -> None: + conn = socks5.SOCKS5Connection() + + # Auth method request + auth_method = ( + socks5.SOCKS5AuthMethod.NO_AUTH_REQUIRED + if auth is None + else socks5.SOCKS5AuthMethod.USERNAME_PASSWORD + ) + conn.send(socks5.SOCKS5AuthMethodsRequest([auth_method])) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Auth method response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5AuthReply) + if response.method != auth_method: + requested = AUTH_METHODS.get(auth_method, "UNKNOWN") + responded = AUTH_METHODS.get(response.method, "UNKNOWN") + raise ProxyError( + f"Requested {requested} from proxy server, but got {responded}." + ) + + if response.method == socks5.SOCKS5AuthMethod.USERNAME_PASSWORD: + # Username/password request + assert auth is not None + username, password = auth + conn.send(socks5.SOCKS5UsernamePasswordRequest(username, password)) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Username/password response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5UsernamePasswordReply) + if not response.success: + raise ProxyError("Invalid username/password") + + # Connect request + conn.send( + socks5.SOCKS5CommandRequest.from_address( + socks5.SOCKS5Command.CONNECT, (host, port) + ) + ) + outgoing_bytes = conn.data_to_send() + stream.write(outgoing_bytes) + + # Connect response + incoming_bytes = stream.read(max_bytes=4096) + response = conn.receive_data(incoming_bytes) + assert isinstance(response, socks5.SOCKS5Reply) + if response.reply_code != socks5.SOCKS5ReplyCode.SUCCEEDED: + reply_code = REPLY_CODES.get(response.reply_code, "UNKOWN") + raise ProxyError(f"Proxy Server could not connect: {reply_code}.") + + +class SOCKSProxy(ConnectionPool): + """ + A connection pool that sends requests via an HTTP proxy. + """ + + def __init__( + self, + proxy_url: typing.Union[URL, bytes, str], + proxy_auth: typing.Optional[ + typing.Tuple[typing.Union[bytes, str], typing.Union[bytes, str]] + ] = None, + ssl_context: typing.Optional[ssl.SSLContext] = None, + max_connections: typing.Optional[int] = 10, + max_keepalive_connections: typing.Optional[int] = None, + keepalive_expiry: typing.Optional[float] = None, + http1: bool = True, + http2: bool = False, + retries: int = 0, + network_backend: typing.Optional[NetworkBackend] = None, + ) -> None: + """ + A connection pool for making HTTP requests. + + Parameters: + proxy_url: The URL to use when connecting to the proxy server. + For example `"http://127.0.0.1:8080/"`. + ssl_context: An SSL context to use for verifying connections. + If not specified, the default `httpcore.default_ssl_context()` + will be used. + max_connections: The maximum number of concurrent HTTP connections that + the pool should allow. Any attempt to send a request on a pool that + would exceed this amount will block until a connection is available. + max_keepalive_connections: The maximum number of idle HTTP connections + that will be maintained in the pool. + keepalive_expiry: The duration in seconds that an idle HTTP connection + may be maintained for before being expired from the pool. + http1: A boolean indicating if HTTP/1.1 requests should be supported + by the connection pool. Defaults to True. + http2: A boolean indicating if HTTP/2 requests should be supported by + the connection pool. Defaults to False. + retries: The maximum number of retries when trying to establish + a connection. + local_address: Local address to connect from. Can also be used to + connect using a particular address family. Using + `local_address="0.0.0.0"` will connect using an `AF_INET` address + (IPv4), while using `local_address="::"` will connect using an + `AF_INET6` address (IPv6). + uds: Path to a Unix Domain Socket to use instead of TCP sockets. + network_backend: A backend instance to use for handling network I/O. + """ + super().__init__( + ssl_context=ssl_context, + max_connections=max_connections, + max_keepalive_connections=max_keepalive_connections, + keepalive_expiry=keepalive_expiry, + http1=http1, + http2=http2, + network_backend=network_backend, + retries=retries, + ) + self._ssl_context = ssl_context + self._proxy_url = enforce_url(proxy_url, name="proxy_url") + if proxy_auth is not None: + username, password = proxy_auth + username_bytes = enforce_bytes(username, name="proxy_auth") + password_bytes = enforce_bytes(password, name="proxy_auth") + self._proxy_auth: typing.Optional[typing.Tuple[bytes, bytes]] = ( + username_bytes, + password_bytes, + ) + else: + self._proxy_auth = None + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return Socks5Connection( + proxy_origin=self._proxy_url.origin, + remote_origin=origin, + proxy_auth=self._proxy_auth, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + network_backend=self._network_backend, + ) + + +class Socks5Connection(ConnectionInterface): + def __init__( + self, + proxy_origin: Origin, + remote_origin: Origin, + proxy_auth: typing.Optional[typing.Tuple[bytes, bytes]] = None, + ssl_context: typing.Optional[ssl.SSLContext] = None, + keepalive_expiry: typing.Optional[float] = None, + http1: bool = True, + http2: bool = False, + network_backend: typing.Optional[NetworkBackend] = None, + ) -> None: + self._proxy_origin = proxy_origin + self._remote_origin = remote_origin + self._proxy_auth = proxy_auth + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._network_backend: NetworkBackend = ( + SyncBackend() if network_backend is None else network_backend + ) + self._connect_lock = Lock() + self._connection: typing.Optional[ConnectionInterface] = None + self._connect_failed = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) + timeout = timeouts.get("connect", None) + + with self._connect_lock: + if self._connection is None: + try: + # Connect to the proxy + kwargs = { + "host": self._proxy_origin.host.decode("ascii"), + "port": self._proxy_origin.port, + "timeout": timeout, + } + with Trace("connect_tcp", logger, request, kwargs) as trace: + stream = self._network_backend.connect_tcp(**kwargs) + trace.return_value = stream + + # Connect to the remote host using socks5 + kwargs = { + "stream": stream, + "host": self._remote_origin.host.decode("ascii"), + "port": self._remote_origin.port, + "auth": self._proxy_auth, + } + with Trace( + "setup_socks5_connection", logger, request, kwargs + ) as trace: + _init_socks5_connection(**kwargs) + trace.return_value = stream + + # Upgrade the stream to SSL + if self._remote_origin.scheme == b"https": + ssl_context = ( + default_ssl_context() + if self._ssl_context is None + else self._ssl_context + ) + alpn_protocols = ( + ["http/1.1", "h2"] if self._http2 else ["http/1.1"] + ) + ssl_context.set_alpn_protocols(alpn_protocols) + + kwargs = { + "ssl_context": ssl_context, + "server_hostname": sni_hostname + or self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + + # Determine if we should be using HTTP/1.1 or HTTP/2 + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None + and ssl_object.selected_alpn_protocol() == "h2" + ) + + # Create the HTTP/1.1 or HTTP/2 connection + if http2_negotiated or ( + self._http2 and not self._http1 + ): # pragma: nocover + from .http2 import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + except Exception as exc: + self._connect_failed = True + raise exc + elif not self._connection.is_available(): # pragma: nocover + raise ConnectionNotAvailable() + + return self._connection.handle_request(request) + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def close(self) -> None: + if self._connection is not None: + self._connection.close() + + def is_available(self) -> bool: + if self._connection is None: # pragma: nocover + # If HTTP/2 support is enabled, and the resulting connection could + # end up as HTTP/2 then we should indicate the connection as being + # available to service multiple requests. + return ( + self._http2 + and (self._remote_origin.scheme == b"https" or not self._http1) + and not self._connect_failed + ) + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: # pragma: nocover + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: + if self._connection is None: # pragma: nocover + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} [{self.info()}]>" diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_synchronization.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_synchronization.py new file mode 100644 index 0000000..9619a39 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_synchronization.py @@ -0,0 +1,317 @@ +import threading +from types import TracebackType +from typing import Optional, Type + +from ._exceptions import ExceptionMapping, PoolTimeout, map_exceptions + +# Our async synchronization primatives use either 'anyio' or 'trio' depending +# on if they're running under asyncio or trio. + +try: + import trio +except ImportError: # pragma: nocover + trio = None # type: ignore + +try: + import anyio +except ImportError: # pragma: nocover + anyio = None # type: ignore + + +def current_async_library() -> str: + # Determine if we're running under trio or asyncio. + # See https://sniffio.readthedocs.io/en/latest/ + try: + import sniffio + except ImportError: # pragma: nocover + environment = "asyncio" + else: + environment = sniffio.current_async_library() + + if environment not in ("asyncio", "trio"): # pragma: nocover + raise RuntimeError("Running under an unsupported async environment.") + + if environment == "asyncio" and anyio is None: # pragma: nocover + raise RuntimeError( + "Running with asyncio requires installation of 'httpcore[asyncio]'." + ) + + if environment == "trio" and trio is None: # pragma: nocover + raise RuntimeError( + "Running with trio requires installation of 'httpcore[trio]'." + ) + + return environment + + +class AsyncLock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_lock = trio.Lock() + elif self._backend == "asyncio": + self._anyio_lock = anyio.Lock() + + async def __aenter__(self) -> "AsyncLock": + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_lock.acquire() + elif self._backend == "asyncio": + await self._anyio_lock.acquire() + + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + if self._backend == "trio": + self._trio_lock.release() + elif self._backend == "asyncio": + self._anyio_lock.release() + + +class AsyncThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __enter__(self) -> "AsyncThreadLock": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + pass + + +class AsyncEvent: + def __init__(self) -> None: + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a lock with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_event = trio.Event() + elif self._backend == "asyncio": + self._anyio_event = anyio.Event() + + def set(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + self._trio_event.set() + elif self._backend == "asyncio": + self._anyio_event.set() + + async def wait(self, timeout: Optional[float] = None) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout} + timeout_or_inf = float("inf") if timeout is None else timeout + with map_exceptions(trio_exc_map): + with trio.fail_after(timeout_or_inf): + await self._trio_event.wait() + elif self._backend == "asyncio": + anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout} + with map_exceptions(anyio_exc_map): + with anyio.fail_after(timeout): + await self._anyio_event.wait() + + +class AsyncSemaphore: + def __init__(self, bound: int) -> None: + self._bound = bound + self._backend = "" + + def setup(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a semaphore with the correct implementation. + """ + self._backend = current_async_library() + if self._backend == "trio": + self._trio_semaphore = trio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + elif self._backend == "asyncio": + self._anyio_semaphore = anyio.Semaphore( + initial_value=self._bound, max_value=self._bound + ) + + async def acquire(self) -> None: + if not self._backend: + self.setup() + + if self._backend == "trio": + await self._trio_semaphore.acquire() + elif self._backend == "asyncio": + await self._anyio_semaphore.acquire() + + async def release(self) -> None: + if self._backend == "trio": + self._trio_semaphore.release() + elif self._backend == "asyncio": + self._anyio_semaphore.release() + + +class AsyncShieldCancellation: + # For certain portions of our codebase where we're dealing with + # closing connections during exception handling we want to shield + # the operation from being cancelled. + # + # with AsyncShieldCancellation(): + # ... # clean-up operations, shielded from cancellation. + + def __init__(self) -> None: + """ + Detect if we're running under 'asyncio' or 'trio' and create + a shielded scope with the correct implementation. + """ + self._backend = current_async_library() + + if self._backend == "trio": + self._trio_shield = trio.CancelScope(shield=True) + elif self._backend == "asyncio": + self._anyio_shield = anyio.CancelScope(shield=True) + + def __enter__(self) -> "AsyncShieldCancellation": + if self._backend == "trio": + self._trio_shield.__enter__() + elif self._backend == "asyncio": + self._anyio_shield.__enter__() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + if self._backend == "trio": + self._trio_shield.__exit__(exc_type, exc_value, traceback) + elif self._backend == "asyncio": + self._anyio_shield.__exit__(exc_type, exc_value, traceback) + + +# Our thread-based synchronization primitives... + + +class Lock: + """ + This is a standard lock. + + In the sync case `Lock` provides thread locking. + In the async case `AsyncLock` provides async locking. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> "Lock": + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self._lock.release() + + +class ThreadLock: + """ + This is a threading-only lock for no-I/O contexts. + + In the sync case `ThreadLock` provides thread locking. + In the async case `AsyncThreadLock` is a no-op. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + + def __enter__(self) -> "ThreadLock": + self._lock.acquire() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + self._lock.release() + + +class Event: + def __init__(self) -> None: + self._event = threading.Event() + + def set(self) -> None: + self._event.set() + + def wait(self, timeout: Optional[float] = None) -> None: + if timeout == float("inf"): # pragma: no cover + timeout = None + if not self._event.wait(timeout=timeout): + raise PoolTimeout() # pragma: nocover + + +class Semaphore: + def __init__(self, bound: int) -> None: + self._semaphore = threading.Semaphore(value=bound) + + def acquire(self) -> None: + self._semaphore.acquire() + + def release(self) -> None: + self._semaphore.release() + + +class ShieldCancellation: + # Thread-synchronous codebases don't support cancellation semantics. + # We have this class because we need to mirror the async and sync + # cases within our package, but it's just a no-op. + def __enter__(self) -> "ShieldCancellation": + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_trace.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_trace.py new file mode 100644 index 0000000..b122a53 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_trace.py @@ -0,0 +1,105 @@ +import inspect +import logging +from types import TracebackType +from typing import Any, Dict, Optional, Type + +from ._models import Request + + +class Trace: + def __init__( + self, + name: str, + logger: logging.Logger, + request: Optional[Request] = None, + kwargs: Optional[Dict[str, Any]] = None, + ) -> None: + self.name = name + self.logger = logger + self.trace_extension = ( + None if request is None else request.extensions.get("trace") + ) + self.debug = self.logger.isEnabledFor(logging.DEBUG) + self.kwargs = kwargs or {} + self.return_value: Any = None + self.should_trace = self.debug or self.trace_extension is not None + self.prefix = self.logger.name.split(".")[-1] + + def trace(self, name: str, info: Dict[str, Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + ret = self.trace_extension(prefix_and_name, info) + if inspect.iscoroutine(ret): # pragma: no cover + raise TypeError( + "If you are using a synchronous interface, " + "the callback of the `trace` extension should " + "be a normal function instead of an asynchronous function." + ) + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + def __enter__(self) -> "Trace": + if self.should_trace: + info = self.kwargs + self.trace(f"{self.name}.started", info) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + self.trace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + self.trace(f"{self.name}.failed", info) + + async def atrace(self, name: str, info: Dict[str, Any]) -> None: + if self.trace_extension is not None: + prefix_and_name = f"{self.prefix}.{name}" + coro = self.trace_extension(prefix_and_name, info) + if not inspect.iscoroutine(coro): # pragma: no cover + raise TypeError( + "If you're using an asynchronous interface, " + "the callback of the `trace` extension should " + "be an asynchronous function rather than a normal function." + ) + await coro + + if self.debug: + if not info or "return_value" in info and info["return_value"] is None: + message = name + else: + args = " ".join([f"{key}={value!r}" for key, value in info.items()]) + message = f"{name} {args}" + self.logger.debug(message) + + async def __aenter__(self) -> "Trace": + if self.should_trace: + info = self.kwargs + await self.atrace(f"{self.name}.started", info) + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]] = None, + exc_value: Optional[BaseException] = None, + traceback: Optional[TracebackType] = None, + ) -> None: + if self.should_trace: + if exc_value is None: + info = {"return_value": self.return_value} + await self.atrace(f"{self.name}.complete", info) + else: + info = {"exception": exc_value} + await self.atrace(f"{self.name}.failed", info) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/_utils.py b/.pythonlibs/lib/python3.10/site-packages/httpcore/_utils.py new file mode 100644 index 0000000..df5dea8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpcore/_utils.py @@ -0,0 +1,36 @@ +import select +import socket +import sys +import typing + + +def is_socket_readable(sock: typing.Optional[socket.socket]) -> bool: + """ + Return whether a socket, as identifed by its file descriptor, is readable. + "A socket is readable" means that the read buffer isn't empty, i.e. that calling + .recv() on it would immediately return some data. + """ + # NOTE: we want check for readability without actually attempting to read, because + # we don't want to block forever if it's not readable. + + # In the case that the socket no longer exists, or cannot return a file + # descriptor, we treat it as being readable, as if it the next read operation + # on it is ready to return the terminating `b""`. + sock_fd = None if sock is None else sock.fileno() + if sock_fd is None or sock_fd < 0: # pragma: nocover + return True + + # The implementation below was stolen from: + # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478 + # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316 + + # Use select.select on Windows, and when poll is unavailable and select.poll + # everywhere else. (E.g. When eventlet is in use. See #327) + if ( + sys.platform == "win32" or getattr(select, "poll", None) is None + ): # pragma: nocover + rready, _, _ = select.select([sock_fd], [], [], 0) + return bool(rready) + p = select.poll() + p.register(sock_fd, select.POLLIN) + return bool(p.poll(0)) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpcore/py.typed b/.pythonlibs/lib/python3.10/site-packages/httpcore/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/METADATA new file mode 100644 index 0000000..b5ec37c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/METADATA @@ -0,0 +1,209 @@ +Metadata-Version: 2.1 +Name: httpx +Version: 0.27.0 +Summary: The next generation HTTP client. +Project-URL: Changelog, https://github.com/encode/httpx/blob/master/CHANGELOG.md +Project-URL: Documentation, https://www.python-httpx.org +Project-URL: Homepage, https://github.com/encode/httpx +Project-URL: Source, https://github.com/encode/httpx +Author-email: Tom Christie +License-Expression: BSD-3-Clause +License-File: LICENSE.md +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Web Environment +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Requires-Python: >=3.8 +Requires-Dist: anyio +Requires-Dist: certifi +Requires-Dist: httpcore==1.* +Requires-Dist: idna +Requires-Dist: sniffio +Provides-Extra: brotli +Requires-Dist: brotli; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: cli +Requires-Dist: click==8.*; extra == 'cli' +Requires-Dist: pygments==2.*; extra == 'cli' +Requires-Dist: rich<14,>=10; extra == 'cli' +Provides-Extra: http2 +Requires-Dist: h2<5,>=3; extra == 'http2' +Provides-Extra: socks +Requires-Dist: socksio==1.*; extra == 'socks' +Description-Content-Type: text/markdown + +

    + HTTPX +

    + +

    HTTPX - A next-generation HTTP client for Python.

    + +

    + + Test Suite + + + Package version + +

    + +HTTPX is a fully featured HTTP client library for Python 3. It includes **an integrated +command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync +and async APIs**. + +--- + +Install HTTPX using pip: + +```shell +$ pip install httpx +``` + +Now, let's get started: + +```pycon +>>> import httpx +>>> r = httpx.get('https://www.example.org/') +>>> r + +>>> r.status_code +200 +>>> r.headers['content-type'] +'text/html; charset=UTF-8' +>>> r.text +'\n\n\nExample Domain...' +``` + +Or, using the command-line client. + +```shell +$ pip install 'httpx[cli]' # The command line client is an optional dependency. +``` + +Which now allows us to use HTTPX directly from the command-line... + +

    + httpx --help +

    + +Sending a request... + +

    + httpx http://httpbin.org/json +

    + +## Features + +HTTPX builds on the well-established usability of `requests`, and gives you: + +* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/). +* An integrated command-line client. +* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/). +* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/). +* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/#calling-into-python-web-apps) or [ASGI applications](https://www.python-httpx.org/async/#calling-into-python-web-apps). +* Strict timeouts everywhere. +* Fully type annotated. +* 100% test coverage. + +Plus all the standard features of `requests`... + +* International Domains and URLs +* Keep-Alive & Connection Pooling +* Sessions with Cookie Persistence +* Browser-style SSL Verification +* Basic/Digest Authentication +* Elegant Key/Value Cookies +* Automatic Decompression +* Automatic Content Decoding +* Unicode Response Bodies +* Multipart File Uploads +* HTTP(S) Proxy Support +* Connection Timeouts +* Streaming Downloads +* .netrc Support +* Chunked Requests + +## Installation + +Install with pip: + +```shell +$ pip install httpx +``` + +Or, to include the optional HTTP/2 support, use: + +```shell +$ pip install httpx[http2] +``` + +HTTPX requires Python 3.8+. + +## Documentation + +Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/). + +For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/). + +For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section. + +The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference. + +To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/). + +## Contribute + +If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start. + +## Dependencies + +The HTTPX project relies on these excellent libraries: + +* `httpcore` - The underlying transport implementation for `httpx`. + * `h11` - HTTP/1.1 support. +* `certifi` - SSL certificates. +* `idna` - Internationalized domain name support. +* `sniffio` - Async library autodetection. + +As well as these optional installs: + +* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)* +* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)* +* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)* +* `click` - Command line client support. *(Optional, with `httpx[cli]`)* +* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)* + +A huge amount of credit is due to `requests` for the API layout that +much of this work follows, as well as to `urllib3` for plenty of design +inspiration around the lower-level networking details. + +--- + +

    HTTPX is BSD licensed code.
    Designed & crafted with care.

    — 🦋 —

    + +## Release Information + +### Deprecated + +* The `app=...` shortcut has been deprecated. Use the explicit style of `transport=httpx.WSGITransport()` or `transport=httpx.ASGITransport()` instead. + +### Fixed + +* Respect the `http1` argument while configuring proxy transports. (#3023) +* Fix RFC 2069 mode digest authentication. (#3045) + + +--- + +[Full changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/RECORD new file mode 100644 index 0000000..4e25bf6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/RECORD @@ -0,0 +1,56 @@ +../../../bin/httpx,sha256=_YY9NoOQLxjo--SSN3jzZ5-bLif802itgPYWLVNOkd8,208 +httpx-0.27.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpx-0.27.0.dist-info/METADATA,sha256=IU6gbQ_C5MYQcso6ftA80CYv5PFomXiTV4telUS5oRk,7184 +httpx-0.27.0.dist-info/RECORD,, +httpx-0.27.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87 +httpx-0.27.0.dist-info/entry_points.txt,sha256=2lVkdQmxLA1pNMgSN2eV89o90HCZezhmNwsy6ryKDSA,37 +httpx-0.27.0.dist-info/licenses/LICENSE.md,sha256=TsWdVE8StfU5o6cW_TIaxYzNgDC0ZSIfLIgCAM3yjY0,1508 +httpx/__init__.py,sha256=oCxVAsePEy5DE9eLhGAAq9H3RBGZUDaUROtGEyzbBRo,3210 +httpx/__pycache__/__init__.cpython-310.pyc,, +httpx/__pycache__/__version__.cpython-310.pyc,, +httpx/__pycache__/_api.cpython-310.pyc,, +httpx/__pycache__/_auth.cpython-310.pyc,, +httpx/__pycache__/_client.cpython-310.pyc,, +httpx/__pycache__/_compat.cpython-310.pyc,, +httpx/__pycache__/_config.cpython-310.pyc,, +httpx/__pycache__/_content.cpython-310.pyc,, +httpx/__pycache__/_decoders.cpython-310.pyc,, +httpx/__pycache__/_exceptions.cpython-310.pyc,, +httpx/__pycache__/_main.cpython-310.pyc,, +httpx/__pycache__/_models.cpython-310.pyc,, +httpx/__pycache__/_multipart.cpython-310.pyc,, +httpx/__pycache__/_status_codes.cpython-310.pyc,, +httpx/__pycache__/_types.cpython-310.pyc,, +httpx/__pycache__/_urlparse.cpython-310.pyc,, +httpx/__pycache__/_urls.cpython-310.pyc,, +httpx/__pycache__/_utils.cpython-310.pyc,, +httpx/__version__.py,sha256=IAHwuJkw3XUFRtOMccAyFbrjpZ3C0udEyrIq2Yrtk5k,108 +httpx/_api.py,sha256=G0EqHYvmusoCiDYvn5i45lJOaQsorhKqLvhnLITCn0Y,12928 +httpx/_auth.py,sha256=G3ithlfScnw0qV2uLmcvQ9iYSpnlV1y72Jk5WXKl5ns,11830 +httpx/_client.py,sha256=YzAIIQkS0iK2mbEjHmMZZm7SOvnxXX4W8N8SpFveuYk,67490 +httpx/_compat.py,sha256=rJERfjHkRvvHFVfltbHyCVcAboNsfEeN6j_00Z2C4k8,1563 +httpx/_config.py,sha256=M5BHACPDt_ZzcKYdqMM1YiS6uHzuWhtbqpu1VL2rKsQ,12204 +httpx/_content.py,sha256=nuK0vo7FLdablYSjXg7kDN6Qh25mTgLxXu5JDoXvQ8U,8047 +httpx/_decoders.py,sha256=HyX51vac2bRb9XWvYpYxXVIQkQpYJuB6ZMAWJhjdNzg,9904 +httpx/_exceptions.py,sha256=vnB78_LL-JhjQKP9nbU6lmxmMyI3b4ImkZc3B_Ik3Ho,7922 +httpx/_main.py,sha256=LcRXtGghiTux7yj0pGXQXx7PNfr3EHE3VcxBcCY4RcE,15635 +httpx/_models.py,sha256=4X3uCAiCm1e7gwnBh777L3lWV6NdjdgfXSg1Nk47JsQ,42303 +httpx/_multipart.py,sha256=CkS8cH5Nau1YrvizDSCRhdK13fltMV2-GnvM3msGRzw,8885 +httpx/_status_codes.py,sha256=n-6km_2zd2bn175zxug8L8xy16Eyx2v0KowqGrhSNf4,5618 +httpx/_transports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpx/_transports/__pycache__/__init__.cpython-310.pyc,, +httpx/_transports/__pycache__/asgi.cpython-310.pyc,, +httpx/_transports/__pycache__/base.cpython-310.pyc,, +httpx/_transports/__pycache__/default.cpython-310.pyc,, +httpx/_transports/__pycache__/mock.cpython-310.pyc,, +httpx/_transports/__pycache__/wsgi.cpython-310.pyc,, +httpx/_transports/asgi.py,sha256=TplpV_7STqay5uvPN_Q1c-K0i9Loy1LX4W0ndBg9fXI,5490 +httpx/_transports/base.py,sha256=Iv2ioumj7e-UBd_wU2i01_3Y4vAJRKLy8t0V99OF3Vc,2472 +httpx/_transports/default.py,sha256=lt9V3TJbSVoWmh4CktSI9Q1eFllFtOxNp7CsP0ZWNzM,13290 +httpx/_transports/mock.py,sha256=I_re3UXInPkN11eA2zACJzBJDvXEEtj-4eral48I7zs,1202 +httpx/_transports/wsgi.py,sha256=HeYO7Th2vKbbE5O3c5Z6Q_si77SF9OLOCW9FVTGZdO0,4795 +httpx/_types.py,sha256=lveH-nW6V3VLKeY-EffHVDkOxCe94Irg-ebo3LxQ13s,3391 +httpx/_urlparse.py,sha256=z1ZFA2PClbW0-TQEh9M_oyctpxuW9nc1ZXDcA9Sv6bs,17720 +httpx/_urls.py,sha256=yCaLvbmxI1j7gWSEYffKvYG2j749OmqI5sFCQrsB2Vk,21783 +httpx/_utils.py,sha256=lByQlK36pmXTJa7WxlWEfB48tcKb9fxI8xEO4ayi1JM,13858 +httpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/WHEEL new file mode 100644 index 0000000..5998f3a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.21.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/entry_points.txt new file mode 100644 index 0000000..8ae9600 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +httpx = httpx:main diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/licenses/LICENSE.md b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/licenses/LICENSE.md new file mode 100644 index 0000000..ab79d16 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx-0.27.0.dist-info/licenses/LICENSE.md @@ -0,0 +1,12 @@ +Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/). +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpx/__init__.py new file mode 100644 index 0000000..f61112f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/__init__.py @@ -0,0 +1,138 @@ +from .__version__ import __description__, __title__, __version__ +from ._api import delete, get, head, options, patch, post, put, request, stream +from ._auth import Auth, BasicAuth, DigestAuth, NetRCAuth +from ._client import USE_CLIENT_DEFAULT, AsyncClient, Client +from ._config import Limits, Proxy, Timeout, create_ssl_context +from ._content import ByteStream +from ._exceptions import ( + CloseError, + ConnectError, + ConnectTimeout, + CookieConflict, + DecodingError, + HTTPError, + HTTPStatusError, + InvalidURL, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + RequestError, + RequestNotRead, + ResponseNotRead, + StreamClosed, + StreamConsumed, + StreamError, + TimeoutException, + TooManyRedirects, + TransportError, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from ._models import Cookies, Headers, Request, Response +from ._status_codes import codes +from ._transports.asgi import ASGITransport +from ._transports.base import AsyncBaseTransport, BaseTransport +from ._transports.default import AsyncHTTPTransport, HTTPTransport +from ._transports.mock import MockTransport +from ._transports.wsgi import WSGITransport +from ._types import AsyncByteStream, SyncByteStream +from ._urls import URL, QueryParams + +try: + from ._main import main +except ImportError: # pragma: no cover + + def main() -> None: # type: ignore + import sys + + print( + "The httpx command line client could not run because the required " + "dependencies were not installed.\nMake sure you've installed " + "everything with: pip install 'httpx[cli]'" + ) + sys.exit(1) + + +__all__ = [ + "__description__", + "__title__", + "__version__", + "ASGITransport", + "AsyncBaseTransport", + "AsyncByteStream", + "AsyncClient", + "AsyncHTTPTransport", + "Auth", + "BaseTransport", + "BasicAuth", + "ByteStream", + "Client", + "CloseError", + "codes", + "ConnectError", + "ConnectTimeout", + "CookieConflict", + "Cookies", + "create_ssl_context", + "DecodingError", + "delete", + "DigestAuth", + "get", + "head", + "Headers", + "HTTPError", + "HTTPStatusError", + "HTTPTransport", + "InvalidURL", + "Limits", + "LocalProtocolError", + "main", + "MockTransport", + "NetRCAuth", + "NetworkError", + "options", + "patch", + "PoolTimeout", + "post", + "ProtocolError", + "Proxy", + "ProxyError", + "put", + "QueryParams", + "ReadError", + "ReadTimeout", + "RemoteProtocolError", + "request", + "Request", + "RequestError", + "RequestNotRead", + "Response", + "ResponseNotRead", + "stream", + "StreamClosed", + "StreamConsumed", + "StreamError", + "SyncByteStream", + "Timeout", + "TimeoutException", + "TooManyRedirects", + "TransportError", + "UnsupportedProtocol", + "URL", + "USE_CLIENT_DEFAULT", + "WriteError", + "WriteTimeout", + "WSGITransport", +] + + +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + setattr(__locals[__name], "__module__", "httpx") # noqa diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..592d05f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__version__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__version__.cpython-310.pyc new file mode 100644 index 0000000..87e52d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/__version__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_api.cpython-310.pyc new file mode 100644 index 0000000..9f0f4d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_auth.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_auth.cpython-310.pyc new file mode 100644 index 0000000..56ecc22 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_auth.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_client.cpython-310.pyc new file mode 100644 index 0000000..7788a0f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000..69bb186 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_config.cpython-310.pyc new file mode 100644 index 0000000..93a749f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_content.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_content.cpython-310.pyc new file mode 100644 index 0000000..048ab38 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_content.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_decoders.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_decoders.cpython-310.pyc new file mode 100644 index 0000000..81ea3a8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_decoders.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_exceptions.cpython-310.pyc new file mode 100644 index 0000000..ee6272e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_main.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_main.cpython-310.pyc new file mode 100644 index 0000000..a6928df Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_main.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_models.cpython-310.pyc new file mode 100644 index 0000000..e48225c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_multipart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_multipart.cpython-310.pyc new file mode 100644 index 0000000..0a35aaa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_multipart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_status_codes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_status_codes.cpython-310.pyc new file mode 100644 index 0000000..2861acf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_status_codes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_types.cpython-310.pyc new file mode 100644 index 0000000..025e1d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urlparse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urlparse.cpython-310.pyc new file mode 100644 index 0000000..bd0eeee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urlparse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urls.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urls.cpython-310.pyc new file mode 100644 index 0000000..052241b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_urls.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000..9637ac1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/__pycache__/_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/__version__.py b/.pythonlibs/lib/python3.10/site-packages/httpx/__version__.py new file mode 100644 index 0000000..c121a89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/__version__.py @@ -0,0 +1,3 @@ +__title__ = "httpx" +__description__ = "A next generation HTTP client, for Python 3." +__version__ = "0.27.0" diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_api.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_api.py new file mode 100644 index 0000000..b5821cc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_api.py @@ -0,0 +1,467 @@ +from __future__ import annotations + +import typing +from contextlib import contextmanager + +from ._client import Client +from ._config import DEFAULT_TIMEOUT_CONFIG +from ._models import Response +from ._types import ( + AuthTypes, + CertTypes, + CookieTypes, + HeaderTypes, + ProxiesTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestFiles, + TimeoutTypes, + URLTypes, + VerifyTypes, +) + + +def request( + method: str, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + trust_env: bool = True, +) -> Response: + """ + Sends an HTTP request. + + **Parameters:** + + * **method** - HTTP method for the new `Request` object: `GET`, `OPTIONS`, + `HEAD`, `POST`, `PUT`, `PATCH`, or `DELETE`. + * **url** - URL for the new `Request` object. + * **params** - *(optional)* Query parameters to include in the URL, as a + string, dictionary, or sequence of two-tuples. + * **content** - *(optional)* Binary content to include in the body of the + request, as bytes or a byte iterator. + * **data** - *(optional)* Form data to include in the body of the request, + as a dictionary. + * **files** - *(optional)* A dictionary of upload files to include in the + body of the request. + * **json** - *(optional)* A JSON serializable object to include in the body + of the request. + * **headers** - *(optional)* Dictionary of HTTP headers to include in the + request. + * **cookies** - *(optional)* Dictionary of Cookie items to include in the + request. + * **auth** - *(optional)* An authentication class to use when sending the + request. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **proxies** - *(optional)* A dictionary mapping proxy keys to proxy URLs. + * **timeout** - *(optional)* The timeout configuration to use when sending + the request. + * **follow_redirects** - *(optional)* Enables or disables HTTP redirects. + * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to + verify the identity of requested hosts. Either `True` (default CA bundle), + a path to an SSL certificate file, an `ssl.SSLContext`, or `False` + (which will disable verification). + * **cert** - *(optional)* An SSL certificate used by the requested host + to authenticate the client. Either a path to an SSL certificate file, or + two-tuple of (certificate file, key file), or a three-tuple of (certificate + file, key file, password). + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + + **Returns:** `Response` + + Usage: + + ``` + >>> import httpx + >>> response = httpx.request('GET', 'https://httpbin.org/get') + >>> response + + ``` + """ + with Client( + cookies=cookies, + proxy=proxy, + proxies=proxies, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + return client.request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) + + +@contextmanager +def stream( + method: str, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + trust_env: bool = True, +) -> typing.Iterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + with Client( + cookies=cookies, + proxy=proxy, + proxies=proxies, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) as client: + with client.stream( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + auth=auth, + follow_redirects=follow_redirects, + ) as response: + yield response + + +def get( + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `GET` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `GET` requests should not include a request body. + """ + return request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def options( + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `OPTIONS` requests should not include a request body. + """ + return request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def head( + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `HEAD` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `HEAD` requests should not include a request body. + """ + return request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def post( + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def put( + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def patch( + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) + + +def delete( + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | None = None, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + follow_redirects: bool = False, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + trust_env: bool = True, +) -> Response: + """ + Sends a `DELETE` request. + + **Parameters**: See `httpx.request`. + + Note that the `data`, `files`, `json` and `content` parameters are not available + on this function, as `DELETE` requests should not include a request body. + """ + return request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + proxy=proxy, + proxies=proxies, + follow_redirects=follow_redirects, + cert=cert, + verify=verify, + timeout=timeout, + trust_env=trust_env, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_auth.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_auth.py new file mode 100644 index 0000000..903e399 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_auth.py @@ -0,0 +1,345 @@ +from __future__ import annotations + +import hashlib +import os +import re +import time +import typing +from base64 import b64encode +from urllib.request import parse_http_list + +from ._exceptions import ProtocolError +from ._models import Cookies, Request, Response +from ._utils import to_bytes, to_str, unquote + +if typing.TYPE_CHECKING: # pragma: no cover + from hashlib import _Hash + + +class Auth: + """ + Base class for all authentication schemes. + + To implement a custom authentication scheme, subclass `Auth` and override + the `.auth_flow()` method. + + If the authentication scheme does I/O such as disk access or network calls, or uses + synchronization primitives such as locks, you should override `.sync_auth_flow()` + and/or `.async_auth_flow()` instead of `.auth_flow()` to provide specialized + implementations that will be used by `Client` and `AsyncClient` respectively. + """ + + requires_request_body = False + requires_response_body = False + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow. + + To dispatch a request, `yield` it: + + ``` + yield request + ``` + + The client will `.send()` the response back into the flow generator. You can + access it like so: + + ``` + response = yield request + ``` + + A `return` (or reaching the end of the generator) will result in the + client returning the last response obtained from the server. + + You can dispatch as many requests as is necessary. + """ + yield request + + def sync_auth_flow( + self, request: Request + ) -> typing.Generator[Request, Response, None]: + """ + Execute the authentication flow synchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + request.read() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + response.read() + + try: + request = flow.send(response) + except StopIteration: + break + + async def async_auth_flow( + self, request: Request + ) -> typing.AsyncGenerator[Request, Response]: + """ + Execute the authentication flow asynchronously. + + By default, this defers to `.auth_flow()`. You should override this method + when the authentication scheme does I/O and/or uses concurrency primitives. + """ + if self.requires_request_body: + await request.aread() + + flow = self.auth_flow(request) + request = next(flow) + + while True: + response = yield request + if self.requires_response_body: + await response.aread() + + try: + request = flow.send(response) + except StopIteration: + break + + +class FunctionAuth(Auth): + """ + Allows the 'auth' argument to be passed as a simple callable function, + that takes the request, and returns a new, modified request. + """ + + def __init__(self, func: typing.Callable[[Request], Request]) -> None: + self._func = func + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + yield self._func(request) + + +class BasicAuth(Auth): + """ + Allows the 'auth' argument to be passed as a (username, password) pair, + and uses HTTP Basic authentication. + """ + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._auth_header = self._build_auth_header(username, password) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + request.headers["Authorization"] = self._auth_header + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class NetRCAuth(Auth): + """ + Use a 'netrc' file to lookup basic auth credentials based on the url host. + """ + + def __init__(self, file: str | None = None) -> None: + # Lazily import 'netrc'. + # There's no need for us to load this module unless 'NetRCAuth' is being used. + import netrc + + self._netrc_info = netrc.netrc(file) + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + auth_info = self._netrc_info.authenticators(request.url.host) + if auth_info is None or not auth_info[2]: + # The netrc file did not have authentication credentials for this host. + yield request + else: + # Build a basic auth header with credentials from the netrc file. + request.headers["Authorization"] = self._build_auth_header( + username=auth_info[0], password=auth_info[2] + ) + yield request + + def _build_auth_header(self, username: str | bytes, password: str | bytes) -> str: + userpass = b":".join((to_bytes(username), to_bytes(password))) + token = b64encode(userpass).decode() + return f"Basic {token}" + + +class DigestAuth(Auth): + _ALGORITHM_TO_HASH_FUNCTION: dict[str, typing.Callable[[bytes], _Hash]] = { + "MD5": hashlib.md5, + "MD5-SESS": hashlib.md5, + "SHA": hashlib.sha1, + "SHA-SESS": hashlib.sha1, + "SHA-256": hashlib.sha256, + "SHA-256-SESS": hashlib.sha256, + "SHA-512": hashlib.sha512, + "SHA-512-SESS": hashlib.sha512, + } + + def __init__(self, username: str | bytes, password: str | bytes) -> None: + self._username = to_bytes(username) + self._password = to_bytes(password) + self._last_challenge: _DigestAuthChallenge | None = None + self._nonce_count = 1 + + def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: + if self._last_challenge: + request.headers["Authorization"] = self._build_auth_header( + request, self._last_challenge + ) + + response = yield request + + if response.status_code != 401 or "www-authenticate" not in response.headers: + # If the response is not a 401 then we don't + # need to build an authenticated request. + return + + for auth_header in response.headers.get_list("www-authenticate"): + if auth_header.lower().startswith("digest "): + break + else: + # If the response does not include a 'WWW-Authenticate: Digest ...' + # header, then we don't need to build an authenticated request. + return + + self._last_challenge = self._parse_challenge(request, response, auth_header) + self._nonce_count = 1 + + request.headers["Authorization"] = self._build_auth_header( + request, self._last_challenge + ) + if response.cookies: + Cookies(response.cookies).set_cookie_header(request=request) + yield request + + def _parse_challenge( + self, request: Request, response: Response, auth_header: str + ) -> _DigestAuthChallenge: + """ + Returns a challenge from a Digest WWW-Authenticate header. + These take the form of: + `Digest realm="realm@host.com",qop="auth,auth-int",nonce="abc",opaque="xyz"` + """ + scheme, _, fields = auth_header.partition(" ") + + # This method should only ever have been called with a Digest auth header. + assert scheme.lower() == "digest" + + header_dict: dict[str, str] = {} + for field in parse_http_list(fields): + key, value = field.strip().split("=", 1) + header_dict[key] = unquote(value) + + try: + realm = header_dict["realm"].encode() + nonce = header_dict["nonce"].encode() + algorithm = header_dict.get("algorithm", "MD5") + opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None + qop = header_dict["qop"].encode() if "qop" in header_dict else None + return _DigestAuthChallenge( + realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop + ) + except KeyError as exc: + message = "Malformed Digest WWW-Authenticate header" + raise ProtocolError(message, request=request) from exc + + def _build_auth_header( + self, request: Request, challenge: _DigestAuthChallenge + ) -> str: + hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()] + + def digest(data: bytes) -> bytes: + return hash_func(data).hexdigest().encode() + + A1 = b":".join((self._username, challenge.realm, self._password)) + + path = request.url.raw_path + A2 = b":".join((request.method.encode(), path)) + # TODO: implement auth-int + HA2 = digest(A2) + + nc_value = b"%08x" % self._nonce_count + cnonce = self._get_client_nonce(self._nonce_count, challenge.nonce) + self._nonce_count += 1 + + HA1 = digest(A1) + if challenge.algorithm.lower().endswith("-sess"): + HA1 = digest(b":".join((HA1, challenge.nonce, cnonce))) + + qop = self._resolve_qop(challenge.qop, request=request) + if qop is None: + # Following RFC 2069 + digest_data = [HA1, challenge.nonce, HA2] + else: + # Following RFC 2617/7616 + digest_data = [HA1, challenge.nonce, nc_value, cnonce, qop, HA2] + + format_args = { + "username": self._username, + "realm": challenge.realm, + "nonce": challenge.nonce, + "uri": path, + "response": digest(b":".join(digest_data)), + "algorithm": challenge.algorithm.encode(), + } + if challenge.opaque: + format_args["opaque"] = challenge.opaque + if qop: + format_args["qop"] = b"auth" + format_args["nc"] = nc_value + format_args["cnonce"] = cnonce + + return "Digest " + self._get_header_value(format_args) + + def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes: + s = str(nonce_count).encode() + s += nonce + s += time.ctime().encode() + s += os.urandom(8) + + return hashlib.sha1(s).hexdigest()[:16].encode() + + def _get_header_value(self, header_fields: dict[str, bytes]) -> str: + NON_QUOTED_FIELDS = ("algorithm", "qop", "nc") + QUOTED_TEMPLATE = '{}="{}"' + NON_QUOTED_TEMPLATE = "{}={}" + + header_value = "" + for i, (field, value) in enumerate(header_fields.items()): + if i > 0: + header_value += ", " + template = ( + QUOTED_TEMPLATE + if field not in NON_QUOTED_FIELDS + else NON_QUOTED_TEMPLATE + ) + header_value += template.format(field, to_str(value)) + + return header_value + + def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes | None: + if qop is None: + return None + qops = re.split(b", ?", qop) + if b"auth" in qops: + return b"auth" + + if qops == [b"auth-int"]: + raise NotImplementedError("Digest auth-int support is not yet implemented") + + message = f'Unexpected qop value "{qop!r}" in digest auth' + raise ProtocolError(message, request=request) + + +class _DigestAuthChallenge(typing.NamedTuple): + realm: bytes + nonce: bytes + algorithm: str + opaque: bytes | None + qop: bytes | None diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_client.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_client.py new file mode 100644 index 0000000..e2c6702 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_client.py @@ -0,0 +1,2052 @@ +from __future__ import annotations + +import datetime +import enum +import logging +import typing +import warnings +from contextlib import asynccontextmanager, contextmanager +from types import TracebackType + +from .__version__ import __version__ +from ._auth import Auth, BasicAuth, FunctionAuth +from ._config import ( + DEFAULT_LIMITS, + DEFAULT_MAX_REDIRECTS, + DEFAULT_TIMEOUT_CONFIG, + Limits, + Proxy, + Timeout, +) +from ._decoders import SUPPORTED_DECODERS +from ._exceptions import ( + InvalidURL, + RemoteProtocolError, + TooManyRedirects, + request_context, +) +from ._models import Cookies, Headers, Request, Response +from ._status_codes import codes +from ._transports.asgi import ASGITransport +from ._transports.base import AsyncBaseTransport, BaseTransport +from ._transports.default import AsyncHTTPTransport, HTTPTransport +from ._transports.wsgi import WSGITransport +from ._types import ( + AsyncByteStream, + AuthTypes, + CertTypes, + CookieTypes, + HeaderTypes, + ProxiesTypes, + ProxyTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + SyncByteStream, + TimeoutTypes, + URLTypes, + VerifyTypes, +) +from ._urls import URL, QueryParams +from ._utils import ( + Timer, + URLPattern, + get_environment_proxies, + is_https_redirect, + same_origin, +) + +# The type annotation for @classmethod and context managers here follows PEP 484 +# https://www.python.org/dev/peps/pep-0484/#annotating-instance-and-class-methods +T = typing.TypeVar("T", bound="Client") +U = typing.TypeVar("U", bound="AsyncClient") + + +class UseClientDefault: + """ + For some parameters such as `auth=...` and `timeout=...` we need to be able + to indicate the default "unset" state, in a way that is distinctly different + to using `None`. + + The default "unset" state indicates that whatever default is set on the + client should be used. This is different to setting `None`, which + explicitly disables the parameter, possibly overriding a client default. + + For example we use `timeout=USE_CLIENT_DEFAULT` in the `request()` signature. + Omitting the `timeout` parameter will send a request using whatever default + timeout has been configured on the client. Including `timeout=None` will + ensure no timeout is used. + + Note that user code shouldn't need to use the `USE_CLIENT_DEFAULT` constant, + but it is used internally when a parameter is not included. + """ + + +USE_CLIENT_DEFAULT = UseClientDefault() + + +logger = logging.getLogger("httpx") + +USER_AGENT = f"python-httpx/{__version__}" +ACCEPT_ENCODING = ", ".join( + [key for key in SUPPORTED_DECODERS.keys() if key != "identity"] +) + + +class ClientState(enum.Enum): + # UNOPENED: + # The client has been instantiated, but has not been used to send a request, + # or been opened by entering the context of a `with` block. + UNOPENED = 1 + # OPENED: + # The client has either sent a request, or is within a `with` block. + OPENED = 2 + # CLOSED: + # The client has either exited the `with` block, or `close()` has + # been called explicitly. + CLOSED = 3 + + +class BoundSyncStream(SyncByteStream): + """ + A byte stream that is bound to a given response instance, and that + ensures the `response.elapsed` is set once the response is closed. + """ + + def __init__( + self, stream: SyncByteStream, response: Response, timer: Timer + ) -> None: + self._stream = stream + self._response = response + self._timer = timer + + def __iter__(self) -> typing.Iterator[bytes]: + for chunk in self._stream: + yield chunk + + def close(self) -> None: + seconds = self._timer.sync_elapsed() + self._response.elapsed = datetime.timedelta(seconds=seconds) + self._stream.close() + + +class BoundAsyncStream(AsyncByteStream): + """ + An async byte stream that is bound to a given response instance, and that + ensures the `response.elapsed` is set once the response is closed. + """ + + def __init__( + self, stream: AsyncByteStream, response: Response, timer: Timer + ) -> None: + self._stream = stream + self._response = response + self._timer = timer + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + async for chunk in self._stream: + yield chunk + + async def aclose(self) -> None: + seconds = await self._timer.async_elapsed() + self._response.elapsed = datetime.timedelta(seconds=seconds) + await self._stream.aclose() + + +EventHook = typing.Callable[..., typing.Any] + + +class BaseClient: + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URLTypes = "", + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + event_hooks = {} if event_hooks is None else event_hooks + + self._base_url = self._enforce_trailing_slash(URL(base_url)) + + self._auth = self._build_auth(auth) + self._params = QueryParams(params) + self.headers = Headers(headers) + self._cookies = Cookies(cookies) + self._timeout = Timeout(timeout) + self.follow_redirects = follow_redirects + self.max_redirects = max_redirects + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + self._trust_env = trust_env + self._default_encoding = default_encoding + self._state = ClientState.UNOPENED + + @property + def is_closed(self) -> bool: + """ + Check if the client being closed + """ + return self._state == ClientState.CLOSED + + @property + def trust_env(self) -> bool: + return self._trust_env + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _get_proxy_map( + self, proxies: ProxiesTypes | None, allow_env_proxies: bool + ) -> dict[str, Proxy | None]: + if proxies is None: + if allow_env_proxies: + return { + key: None if url is None else Proxy(url=url) + for key, url in get_environment_proxies().items() + } + return {} + if isinstance(proxies, dict): + new_proxies = {} + for key, value in proxies.items(): + proxy = Proxy(url=value) if isinstance(value, (str, URL)) else value + new_proxies[str(key)] = proxy + return new_proxies + else: + proxy = Proxy(url=proxies) if isinstance(proxies, (str, URL)) else proxies + return {"all://": proxy} + + @property + def timeout(self) -> Timeout: + return self._timeout + + @timeout.setter + def timeout(self, timeout: TimeoutTypes) -> None: + self._timeout = Timeout(timeout) + + @property + def event_hooks(self) -> dict[str, list[EventHook]]: + return self._event_hooks + + @event_hooks.setter + def event_hooks(self, event_hooks: dict[str, list[EventHook]]) -> None: + self._event_hooks = { + "request": list(event_hooks.get("request", [])), + "response": list(event_hooks.get("response", [])), + } + + @property + def auth(self) -> Auth | None: + """ + Authentication class used when none is passed at the request-level. + + See also [Authentication][0]. + + [0]: /quickstart/#authentication + """ + return self._auth + + @auth.setter + def auth(self, auth: AuthTypes) -> None: + self._auth = self._build_auth(auth) + + @property + def base_url(self) -> URL: + """ + Base URL to use when sending requests with relative URLs. + """ + return self._base_url + + @base_url.setter + def base_url(self, url: URLTypes) -> None: + self._base_url = self._enforce_trailing_slash(URL(url)) + + @property + def headers(self) -> Headers: + """ + HTTP headers to include when sending requests. + """ + return self._headers + + @headers.setter + def headers(self, headers: HeaderTypes) -> None: + client_headers = Headers( + { + b"Accept": b"*/*", + b"Accept-Encoding": ACCEPT_ENCODING.encode("ascii"), + b"Connection": b"keep-alive", + b"User-Agent": USER_AGENT.encode("ascii"), + } + ) + client_headers.update(headers) + self._headers = client_headers + + @property + def cookies(self) -> Cookies: + """ + Cookie values to include when sending requests. + """ + return self._cookies + + @cookies.setter + def cookies(self, cookies: CookieTypes) -> None: + self._cookies = Cookies(cookies) + + @property + def params(self) -> QueryParams: + """ + Query parameters to include in the URL when sending requests. + """ + return self._params + + @params.setter + def params(self, params: QueryParamTypes) -> None: + self._params = QueryParams(params) + + def build_request( + self, + method: str, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Request: + """ + Build and return a request instance. + + * The `params`, `headers` and `cookies` arguments + are merged with any values set on the client. + * The `url` argument is merged with any `base_url` set on the client. + + See also: [Request instances][0] + + [0]: /advanced/#request-instances + """ + url = self._merge_url(url) + headers = self._merge_headers(headers) + cookies = self._merge_cookies(cookies) + params = self._merge_queryparams(params) + extensions = {} if extensions is None else extensions + if "timeout" not in extensions: + timeout = ( + self.timeout + if isinstance(timeout, UseClientDefault) + else Timeout(timeout) + ) + extensions = dict(**extensions, timeout=timeout.as_dict()) + return Request( + method, + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + extensions=extensions, + ) + + def _merge_url(self, url: URLTypes) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + merge_url = URL(url) + if merge_url.is_relative_url: + # To merge URLs we always append to the base URL. To get this + # behaviour correct we always ensure the base URL ends in a '/' + # separator, and strip any leading '/' from the merge URL. + # + # So, eg... + # + # >>> client = Client(base_url="https://www.example.com/subpath") + # >>> client.base_url + # URL('https://www.example.com/subpath/') + # >>> client.build_request("GET", "/path").url + # URL('https://www.example.com/subpath/path') + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + return merge_url + + def _merge_cookies(self, cookies: CookieTypes | None = None) -> CookieTypes | None: + """ + Merge a cookies argument together with any cookies on the client, + to create the cookies used for the outgoing request. + """ + if cookies or self.cookies: + merged_cookies = Cookies(self.cookies) + merged_cookies.update(cookies) + return merged_cookies + return cookies + + def _merge_headers(self, headers: HeaderTypes | None = None) -> HeaderTypes | None: + """ + Merge a headers argument together with any headers on the client, + to create the headers used for the outgoing request. + """ + merged_headers = Headers(self.headers) + merged_headers.update(headers) + return merged_headers + + def _merge_queryparams( + self, params: QueryParamTypes | None = None + ) -> QueryParamTypes | None: + """ + Merge a queryparams argument together with any queryparams on the client, + to create the queryparams used for the outgoing request. + """ + if params or self.params: + merged_queryparams = QueryParams(self.params) + return merged_queryparams.merge(params) + return params + + def _build_auth(self, auth: AuthTypes | None) -> Auth | None: + if auth is None: + return None + elif isinstance(auth, tuple): + return BasicAuth(username=auth[0], password=auth[1]) + elif isinstance(auth, Auth): + return auth + elif callable(auth): + return FunctionAuth(func=auth) + else: + raise TypeError(f'Invalid "auth" argument: {auth!r}') + + def _build_request_auth( + self, + request: Request, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + ) -> Auth: + auth = ( + self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth) + ) + + if auth is not None: + return auth + + username, password = request.url.username, request.url.password + if username or password: + return BasicAuth(username=username, password=password) + + return Auth() + + def _build_redirect_request(self, request: Request, response: Response) -> Request: + """ + Given a request and a redirect response, return a new request that + should be used to effect the redirect. + """ + method = self._redirect_method(request, response) + url = self._redirect_url(request, response) + headers = self._redirect_headers(request, url, method) + stream = self._redirect_stream(request, method) + cookies = Cookies(self.cookies) + return Request( + method=method, + url=url, + headers=headers, + cookies=cookies, + stream=stream, + extensions=request.extensions, + ) + + def _redirect_method(self, request: Request, response: Response) -> str: + """ + When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.SEE_OTHER and method != "HEAD": + method = "GET" + + # Do what the browsers do, despite standards... + # Turn 302s into GETs. + if response.status_code == codes.FOUND and method != "HEAD": + method = "GET" + + # If a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in 'requests' issue 1704. + if response.status_code == codes.MOVED_PERMANENTLY and method == "POST": + method = "GET" + + return method + + def _redirect_url(self, request: Request, response: Response) -> URL: + """ + Return the URL for the redirect to follow. + """ + location = response.headers["Location"] + + try: + url = URL(location) + except InvalidURL as exc: + raise RemoteProtocolError( + f"Invalid URL in location header: {exc}.", request=request + ) from None + + # Handle malformed 'Location' headers that are "absolute" form, have no host. + # See: https://github.com/encode/httpx/issues/771 + if url.scheme and not url.host: + url = url.copy_with(host=request.url.host) + + # Facilitate relative 'Location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + if url.is_relative_url: + url = request.url.join(url) + + # Attach previous fragment if needed (RFC 7231 7.1.2) + if request.url.fragment and not url.fragment: + url = url.copy_with(fragment=request.url.fragment) + + return url + + def _redirect_headers(self, request: Request, url: URL, method: str) -> Headers: + """ + Return the headers that should be used for the redirect request. + """ + headers = Headers(request.headers) + + if not same_origin(url, request.url): + if not is_https_redirect(request.url, url): + # Strip Authorization headers when responses are redirected + # away from the origin. (Except for direct HTTP to HTTPS redirects.) + headers.pop("Authorization", None) + + # Update the Host header. + headers["Host"] = url.netloc.decode("ascii") + + if method != request.method and method == "GET": + # If we've switch to a 'GET' request, then strip any headers which + # are only relevant to the request body. + headers.pop("Content-Length", None) + headers.pop("Transfer-Encoding", None) + + # We should use the client cookie store to determine any cookie header, + # rather than whatever was on the original outgoing request. + headers.pop("Cookie", None) + + return headers + + def _redirect_stream( + self, request: Request, method: str + ) -> SyncByteStream | AsyncByteStream | None: + """ + Return the body that should be used for the redirect request. + """ + if method != request.method and method == "GET": + return None + + return request.stream + + +class Client(BaseClient): + """ + An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc. + + It can be shared between threads. + + Usage: + + ```python + >>> client = httpx.Client() + >>> response = client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to + verify the identity of requested hosts. Either `True` (default CA bundle), + a path to an SSL certificate file, an `ssl.SSLContext`, or `False` + (which will disable verification). + * **cert** - *(optional)* An SSL certificate used by the requested host + to authenticate the client. Either a path to an SSL certificate file, or + two-tuple of (certificate file, key file), or a three-tuple of (certificate + file, key file, password). + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **proxies** - *(optional)* A dictionary mapping proxy keys to proxy + URLs. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **app** - *(optional)* An WSGI application to send requests to, + rather than sending actual network requests. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + mounts: None | (typing.Mapping[str, BaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None, + base_url: URLTypes = "", + transport: BaseTransport | None = None, + app: typing.Callable[..., typing.Any] | None = None, + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + if proxies: + message = ( + "The 'proxies' argument is now deprecated." + " Use 'proxy' or 'mounts' instead." + ) + warnings.warn(message, DeprecationWarning) + if proxy: + raise RuntimeError("Use either `proxy` or 'proxies', not both.") + + if app: + message = ( + "The 'app' shortcut is now deprecated." + " Use the explicit style 'transport=WSGITransport(app=...)' instead." + ) + warnings.warn(message, DeprecationWarning) + + allow_env_proxies = trust_env and app is None and transport is None + proxy_map = self._get_proxy_map(proxies or proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + app=app, + trust_env=trust_env, + ) + self._mounts: dict[URLPattern, BaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update( + {URLPattern(key): transport for key, transport in mounts.items()} + ) + + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: BaseTransport | None = None, + app: typing.Callable[..., typing.Any] | None = None, + trust_env: bool = True, + ) -> BaseTransport: + if transport is not None: + return transport + + if app is not None: + return WSGITransport(app=app) + + return HTTPTransport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + trust_env: bool = True, + ) -> BaseTransport: + return HTTPTransport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> BaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + def request( + self, + method: str, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = client.send(request, ...) + ``` + + See `Client.build_request()`, `Client.send()` and + [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: /advanced/#merging-of-configuration + """ + if cookies is not None: + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return self.send(request, auth=auth, follow_redirects=follow_redirects) + + @contextmanager + def stream( + self, + method: str, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.Iterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + response.close() + + def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `Client.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: /advanced/#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = ( + self.follow_redirects + if isinstance(follow_redirects, UseClientDefault) + else follow_redirects + ) + + auth = self._build_request_auth(request, auth) + + response = self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + response.read() + + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.sync_auth_flow(request) + try: + request = next(auth_flow) + + while True: + response = self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = auth_flow.send(response) + except StopIteration: + return response + + response.history = list(history) + response.read() + request = next_request + history.append(response) + + except BaseException as exc: + response.close() + raise exc + finally: + auth_flow.close() + + def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects( + "Exceeded maximum allowed redirects.", request=request + ) + + for hook in self._event_hooks["request"]: + hook(request) + + response = self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + hook(response) + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + response.read() + else: + response.next_request = request + return response + + except BaseException as exc: + response.close() + raise exc + + def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + timer = Timer() + timer.sync_start() + + if not isinstance(request.stream, SyncByteStream): + raise RuntimeError( + "Attempted to send an async request with a sync Client instance." + ) + + with request_context(request=request): + response = transport.handle_request(request) + + assert isinstance(response.stream, SyncByteStream) + + response.request = request + response.stream = BoundSyncStream( + response.stream, response=response, timer=timer + ) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + def get( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def options( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def head( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def post( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def put( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def patch( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def delete( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx.request`. + """ + return self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + def close(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + self._transport.close() + for transport in self._mounts.values(): + if transport is not None: + transport.close() + + def __enter__(self: T) -> T: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ( + "Cannot reopen a client instance, once it has been closed." + ), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + self._transport.__enter__() + for transport in self._mounts.values(): + if transport is not None: + transport.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + self._transport.__exit__(exc_type, exc_value, traceback) + for transport in self._mounts.values(): + if transport is not None: + transport.__exit__(exc_type, exc_value, traceback) + + +class AsyncClient(BaseClient): + """ + An asynchronous HTTP client, with connection pooling, HTTP/2, redirects, + cookie persistence, etc. + + It can be shared between tasks. + + Usage: + + ```python + >>> async with httpx.AsyncClient() as client: + >>> response = await client.get('https://example.org') + ``` + + **Parameters:** + + * **auth** - *(optional)* An authentication class to use when sending + requests. + * **params** - *(optional)* Query parameters to include in request URLs, as + a string, dictionary, or sequence of two-tuples. + * **headers** - *(optional)* Dictionary of HTTP headers to include when + sending requests. + * **cookies** - *(optional)* Dictionary of Cookie items to include when + sending requests. + * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to + verify the identity of requested hosts. Either `True` (default CA bundle), + a path to an SSL certificate file, an `ssl.SSLContext`, or `False` + (which will disable verification). + * **cert** - *(optional)* An SSL certificate used by the requested host + to authenticate the client. Either a path to an SSL certificate file, or + two-tuple of (certificate file, key file), or a three-tuple of (certificate + file, key file, password). + * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be + enabled. Defaults to `False`. + * **proxy** - *(optional)* A proxy URL where all the traffic should be routed. + * **proxies** - *(optional)* A dictionary mapping HTTP protocols to proxy + URLs. + * **timeout** - *(optional)* The timeout configuration to use when sending + requests. + * **limits** - *(optional)* The limits configuration to use. + * **max_redirects** - *(optional)* The maximum number of redirect responses + that should be followed. + * **base_url** - *(optional)* A URL to use as the base when building + request URLs. + * **transport** - *(optional)* A transport class to use for sending requests + over the network. + * **app** - *(optional)* An ASGI application to send requests to, + rather than sending actual network requests. + * **trust_env** - *(optional)* Enables or disables usage of environment + variables for configuration. + * **default_encoding** - *(optional)* The default encoding to use for decoding + response text, if no charset information is included in a response Content-Type + header. Set to a callable for automatic character set detection. Default: "utf-8". + """ + + def __init__( + self, + *, + auth: AuthTypes | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + proxy: ProxyTypes | None = None, + proxies: ProxiesTypes | None = None, + mounts: None | (typing.Mapping[str, AsyncBaseTransport | None]) = None, + timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, + follow_redirects: bool = False, + limits: Limits = DEFAULT_LIMITS, + max_redirects: int = DEFAULT_MAX_REDIRECTS, + event_hooks: None + | (typing.Mapping[str, list[typing.Callable[..., typing.Any]]]) = None, + base_url: URLTypes = "", + transport: AsyncBaseTransport | None = None, + app: typing.Callable[..., typing.Any] | None = None, + trust_env: bool = True, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + super().__init__( + auth=auth, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + follow_redirects=follow_redirects, + max_redirects=max_redirects, + event_hooks=event_hooks, + base_url=base_url, + trust_env=trust_env, + default_encoding=default_encoding, + ) + + if http2: + try: + import h2 # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using http2=True, but the 'h2' package is not installed. " + "Make sure to install httpx using `pip install httpx[http2]`." + ) from None + + if proxies: + message = ( + "The 'proxies' argument is now deprecated." + " Use 'proxy' or 'mounts' instead." + ) + warnings.warn(message, DeprecationWarning) + if proxy: + raise RuntimeError("Use either `proxy` or 'proxies', not both.") + + if app: + message = ( + "The 'app' shortcut is now deprecated." + " Use the explicit style 'transport=ASGITransport(app=...)' instead." + ) + warnings.warn(message, DeprecationWarning) + + allow_env_proxies = trust_env and transport is None + proxy_map = self._get_proxy_map(proxies or proxy, allow_env_proxies) + + self._transport = self._init_transport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + transport=transport, + app=app, + trust_env=trust_env, + ) + + self._mounts: dict[URLPattern, AsyncBaseTransport | None] = { + URLPattern(key): None + if proxy is None + else self._init_proxy_transport( + proxy, + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + ) + for key, proxy in proxy_map.items() + } + if mounts is not None: + self._mounts.update( + {URLPattern(key): transport for key, transport in mounts.items()} + ) + self._mounts = dict(sorted(self._mounts.items())) + + def _init_transport( + self, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + transport: AsyncBaseTransport | None = None, + app: typing.Callable[..., typing.Any] | None = None, + trust_env: bool = True, + ) -> AsyncBaseTransport: + if transport is not None: + return transport + + if app is not None: + return ASGITransport(app=app) + + return AsyncHTTPTransport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + ) + + def _init_proxy_transport( + self, + proxy: Proxy, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + trust_env: bool = True, + ) -> AsyncBaseTransport: + return AsyncHTTPTransport( + verify=verify, + cert=cert, + http1=http1, + http2=http2, + limits=limits, + trust_env=trust_env, + proxy=proxy, + ) + + def _transport_for_url(self, url: URL) -> AsyncBaseTransport: + """ + Returns the transport instance that should be used for a given URL. + This will either be the standard connection pool, or a proxy. + """ + for pattern, transport in self._mounts.items(): + if pattern.matches(url): + return self._transport if transport is None else transport + + return self._transport + + async def request( + self, + method: str, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Build and send a request. + + Equivalent to: + + ```python + request = client.build_request(...) + response = await client.send(request, ...) + ``` + + See `AsyncClient.build_request()`, `AsyncClient.send()` + and [Merging of configuration][0] for how the various parameters + are merged with client-level configuration. + + [0]: /advanced/#merging-of-configuration + """ + + if cookies is not None: # pragma: no cover + message = ( + "Setting per-request cookies=<...> is being deprecated, because " + "the expected behaviour on cookie persistence is ambiguous. Set " + "cookies directly on the client instance instead." + ) + warnings.warn(message, DeprecationWarning) + + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + return await self.send(request, auth=auth, follow_redirects=follow_redirects) + + @asynccontextmanager + async def stream( + self, + method: str, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> typing.AsyncIterator[Response]: + """ + Alternative to `httpx.request()` that streams the response body + instead of loading it into memory at once. + + **Parameters**: See `httpx.request`. + + See also: [Streaming Responses][0] + + [0]: /quickstart#streaming-responses + """ + request = self.build_request( + method=method, + url=url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + timeout=timeout, + extensions=extensions, + ) + response = await self.send( + request=request, + auth=auth, + follow_redirects=follow_redirects, + stream=True, + ) + try: + yield response + finally: + await response.aclose() + + async def send( + self, + request: Request, + *, + stream: bool = False, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + ) -> Response: + """ + Send a request. + + The request is sent as-is, unmodified. + + Typically you'll want to build one with `AsyncClient.build_request()` + so that any client-level configuration is merged into the request, + but passing an explicit `httpx.Request()` is supported as well. + + See also: [Request instances][0] + + [0]: /advanced/#request-instances + """ + if self._state == ClientState.CLOSED: + raise RuntimeError("Cannot send a request, as the client has been closed.") + + self._state = ClientState.OPENED + follow_redirects = ( + self.follow_redirects + if isinstance(follow_redirects, UseClientDefault) + else follow_redirects + ) + + auth = self._build_request_auth(request, auth) + + response = await self._send_handling_auth( + request, + auth=auth, + follow_redirects=follow_redirects, + history=[], + ) + try: + if not stream: + await response.aread() + + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_handling_auth( + self, + request: Request, + auth: Auth, + follow_redirects: bool, + history: list[Response], + ) -> Response: + auth_flow = auth.async_auth_flow(request) + try: + request = await auth_flow.__anext__() + + while True: + response = await self._send_handling_redirects( + request, + follow_redirects=follow_redirects, + history=history, + ) + try: + try: + next_request = await auth_flow.asend(response) + except StopAsyncIteration: + return response + + response.history = list(history) + await response.aread() + request = next_request + history.append(response) + + except BaseException as exc: + await response.aclose() + raise exc + finally: + await auth_flow.aclose() + + async def _send_handling_redirects( + self, + request: Request, + follow_redirects: bool, + history: list[Response], + ) -> Response: + while True: + if len(history) > self.max_redirects: + raise TooManyRedirects( + "Exceeded maximum allowed redirects.", request=request + ) + + for hook in self._event_hooks["request"]: + await hook(request) + + response = await self._send_single_request(request) + try: + for hook in self._event_hooks["response"]: + await hook(response) + + response.history = list(history) + + if not response.has_redirect_location: + return response + + request = self._build_redirect_request(request, response) + history = history + [response] + + if follow_redirects: + await response.aread() + else: + response.next_request = request + return response + + except BaseException as exc: + await response.aclose() + raise exc + + async def _send_single_request(self, request: Request) -> Response: + """ + Sends a single request, without handling any redirections. + """ + transport = self._transport_for_url(request.url) + timer = Timer() + await timer.async_start() + + if not isinstance(request.stream, AsyncByteStream): + raise RuntimeError( + "Attempted to send an sync request with an AsyncClient instance." + ) + + with request_context(request=request): + response = await transport.handle_async_request(request) + + assert isinstance(response.stream, AsyncByteStream) + response.request = request + response.stream = BoundAsyncStream( + response.stream, response=response, timer=timer + ) + self.cookies.extract_cookies(response) + response.default_encoding = self._default_encoding + + logger.info( + 'HTTP Request: %s %s "%s %d %s"', + request.method, + request.url, + response.http_version, + response.status_code, + response.reason_phrase, + ) + + return response + + async def get( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault | None = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `GET` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "GET", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def options( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send an `OPTIONS` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "OPTIONS", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def head( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `HEAD` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "HEAD", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def post( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `POST` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "POST", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def put( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PUT` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "PUT", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def patch( + self, + url: URLTypes, + *, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `PATCH` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "PATCH", + url, + content=content, + data=data, + files=files, + json=json, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def delete( + self, + url: URLTypes, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + auth: AuthTypes | UseClientDefault = USE_CLIENT_DEFAULT, + follow_redirects: bool | UseClientDefault = USE_CLIENT_DEFAULT, + timeout: TimeoutTypes | UseClientDefault = USE_CLIENT_DEFAULT, + extensions: RequestExtensions | None = None, + ) -> Response: + """ + Send a `DELETE` request. + + **Parameters**: See `httpx.request`. + """ + return await self.request( + "DELETE", + url, + params=params, + headers=headers, + cookies=cookies, + auth=auth, + follow_redirects=follow_redirects, + timeout=timeout, + extensions=extensions, + ) + + async def aclose(self) -> None: + """ + Close transport and proxies. + """ + if self._state != ClientState.CLOSED: + self._state = ClientState.CLOSED + + await self._transport.aclose() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.aclose() + + async def __aenter__(self: U) -> U: + if self._state != ClientState.UNOPENED: + msg = { + ClientState.OPENED: "Cannot open a client instance more than once.", + ClientState.CLOSED: ( + "Cannot reopen a client instance, once it has been closed." + ), + }[self._state] + raise RuntimeError(msg) + + self._state = ClientState.OPENED + + await self._transport.__aenter__() + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self._state = ClientState.CLOSED + + await self._transport.__aexit__(exc_type, exc_value, traceback) + for proxy in self._mounts.values(): + if proxy is not None: + await proxy.__aexit__(exc_type, exc_value, traceback) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_compat.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_compat.py new file mode 100644 index 0000000..493e621 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_compat.py @@ -0,0 +1,41 @@ +""" +The _compat module is used for code which requires branching between different +Python environments. It is excluded from the code coverage checks. +""" +import ssl +import sys + +# Brotli support is optional +# The C bindings in `brotli` are recommended for CPython. +# The CFFI bindings in `brotlicffi` are recommended for PyPy and everything else. +try: + import brotlicffi as brotli +except ImportError: # pragma: no cover + try: + import brotli + except ImportError: + brotli = None + +if sys.version_info >= (3, 10) or ssl.OPENSSL_VERSION_INFO >= (1, 1, 0, 7): + + def set_minimum_tls_version_1_2(context: ssl.SSLContext) -> None: + # The OP_NO_SSL* and OP_NO_TLS* become deprecated in favor of + # 'SSLContext.minimum_version' from Python 3.7 onwards, however + # this attribute is not available unless the ssl module is compiled + # with OpenSSL 1.1.0g or newer. + # https://docs.python.org/3.10/library/ssl.html#ssl.SSLContext.minimum_version + # https://docs.python.org/3.7/library/ssl.html#ssl.SSLContext.minimum_version + context.minimum_version = ssl.TLSVersion.TLSv1_2 + +else: + + def set_minimum_tls_version_1_2(context: ssl.SSLContext) -> None: + # If 'minimum_version' isn't available, we configure these options with + # the older deprecated variants. + context.options |= ssl.OP_NO_SSLv2 + context.options |= ssl.OP_NO_SSLv3 + context.options |= ssl.OP_NO_TLSv1 + context.options |= ssl.OP_NO_TLSv1_1 + + +__all__ = ["brotli", "set_minimum_tls_version_1_2"] diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_config.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_config.py new file mode 100644 index 0000000..7636a5d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_config.py @@ -0,0 +1,370 @@ +from __future__ import annotations + +import logging +import os +import ssl +import typing +from pathlib import Path + +import certifi + +from ._compat import set_minimum_tls_version_1_2 +from ._models import Headers +from ._types import CertTypes, HeaderTypes, TimeoutTypes, URLTypes, VerifyTypes +from ._urls import URL +from ._utils import get_ca_bundle_from_env + +DEFAULT_CIPHERS = ":".join( + [ + "ECDHE+AESGCM", + "ECDHE+CHACHA20", + "DHE+AESGCM", + "DHE+CHACHA20", + "ECDH+AESGCM", + "DH+AESGCM", + "ECDH+AES", + "DH+AES", + "RSA+AESGCM", + "RSA+AES", + "!aNULL", + "!eNULL", + "!MD5", + "!DSS", + ] +) + + +logger = logging.getLogger("httpx") + + +class UnsetType: + pass # pragma: no cover + + +UNSET = UnsetType() + + +def create_ssl_context( + cert: CertTypes | None = None, + verify: VerifyTypes = True, + trust_env: bool = True, + http2: bool = False, +) -> ssl.SSLContext: + return SSLConfig( + cert=cert, verify=verify, trust_env=trust_env, http2=http2 + ).ssl_context + + +class SSLConfig: + """ + SSL Configuration. + """ + + DEFAULT_CA_BUNDLE_PATH = Path(certifi.where()) + + def __init__( + self, + *, + cert: CertTypes | None = None, + verify: VerifyTypes = True, + trust_env: bool = True, + http2: bool = False, + ) -> None: + self.cert = cert + self.verify = verify + self.trust_env = trust_env + self.http2 = http2 + self.ssl_context = self.load_ssl_context() + + def load_ssl_context(self) -> ssl.SSLContext: + logger.debug( + "load_ssl_context verify=%r cert=%r trust_env=%r http2=%r", + self.verify, + self.cert, + self.trust_env, + self.http2, + ) + + if self.verify: + return self.load_ssl_context_verify() + return self.load_ssl_context_no_verify() + + def load_ssl_context_no_verify(self) -> ssl.SSLContext: + """ + Return an SSL context for unverified connections. + """ + context = self._create_default_ssl_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + self._load_client_certs(context) + return context + + def load_ssl_context_verify(self) -> ssl.SSLContext: + """ + Return an SSL context for verified connections. + """ + if self.trust_env and self.verify is True: + ca_bundle = get_ca_bundle_from_env() + if ca_bundle is not None: + self.verify = ca_bundle + + if isinstance(self.verify, ssl.SSLContext): + # Allow passing in our own SSLContext object that's pre-configured. + context = self.verify + self._load_client_certs(context) + return context + elif isinstance(self.verify, bool): + ca_bundle_path = self.DEFAULT_CA_BUNDLE_PATH + elif Path(self.verify).exists(): + ca_bundle_path = Path(self.verify) + else: + raise IOError( + "Could not find a suitable TLS CA certificate bundle, " + "invalid path: {}".format(self.verify) + ) + + context = self._create_default_ssl_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + + # Signal to server support for PHA in TLS 1.3. Raises an + # AttributeError if only read-only access is implemented. + try: + context.post_handshake_auth = True + except AttributeError: # pragma: no cover + pass + + # Disable using 'commonName' for SSLContext.check_hostname + # when the 'subjectAltName' extension isn't available. + try: + context.hostname_checks_common_name = False + except AttributeError: # pragma: no cover + pass + + if ca_bundle_path.is_file(): + cafile = str(ca_bundle_path) + logger.debug("load_verify_locations cafile=%r", cafile) + context.load_verify_locations(cafile=cafile) + elif ca_bundle_path.is_dir(): + capath = str(ca_bundle_path) + logger.debug("load_verify_locations capath=%r", capath) + context.load_verify_locations(capath=capath) + + self._load_client_certs(context) + + return context + + def _create_default_ssl_context(self) -> ssl.SSLContext: + """ + Creates the default SSLContext object that's used for both verified + and unverified connections. + """ + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + set_minimum_tls_version_1_2(context) + context.options |= ssl.OP_NO_COMPRESSION + context.set_ciphers(DEFAULT_CIPHERS) + + if ssl.HAS_ALPN: + alpn_idents = ["http/1.1", "h2"] if self.http2 else ["http/1.1"] + context.set_alpn_protocols(alpn_idents) + + keylogfile = os.environ.get("SSLKEYLOGFILE") + if keylogfile and self.trust_env: + context.keylog_filename = keylogfile + + return context + + def _load_client_certs(self, ssl_context: ssl.SSLContext) -> None: + """ + Loads client certificates into our SSLContext object + """ + if self.cert is not None: + if isinstance(self.cert, str): + ssl_context.load_cert_chain(certfile=self.cert) + elif isinstance(self.cert, tuple) and len(self.cert) == 2: + ssl_context.load_cert_chain(certfile=self.cert[0], keyfile=self.cert[1]) + elif isinstance(self.cert, tuple) and len(self.cert) == 3: + ssl_context.load_cert_chain( + certfile=self.cert[0], + keyfile=self.cert[1], + password=self.cert[2], + ) + + +class Timeout: + """ + Timeout configuration. + + **Usage**: + + Timeout(None) # No timeouts. + Timeout(5.0) # 5s timeout on all operations. + Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts. + Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere. + Timeout(5.0, pool=None) # No timeout on acquiring connection from pool. + # 5s timeout elsewhere. + """ + + def __init__( + self, + timeout: TimeoutTypes | UnsetType = UNSET, + *, + connect: None | float | UnsetType = UNSET, + read: None | float | UnsetType = UNSET, + write: None | float | UnsetType = UNSET, + pool: None | float | UnsetType = UNSET, + ) -> None: + if isinstance(timeout, Timeout): + # Passed as a single explicit Timeout. + assert connect is UNSET + assert read is UNSET + assert write is UNSET + assert pool is UNSET + self.connect = timeout.connect # type: typing.Optional[float] + self.read = timeout.read # type: typing.Optional[float] + self.write = timeout.write # type: typing.Optional[float] + self.pool = timeout.pool # type: typing.Optional[float] + elif isinstance(timeout, tuple): + # Passed as a tuple. + self.connect = timeout[0] + self.read = timeout[1] + self.write = None if len(timeout) < 3 else timeout[2] + self.pool = None if len(timeout) < 4 else timeout[3] + elif not ( + isinstance(connect, UnsetType) + or isinstance(read, UnsetType) + or isinstance(write, UnsetType) + or isinstance(pool, UnsetType) + ): + self.connect = connect + self.read = read + self.write = write + self.pool = pool + else: + if isinstance(timeout, UnsetType): + raise ValueError( + "httpx.Timeout must either include a default, or set all " + "four parameters explicitly." + ) + self.connect = timeout if isinstance(connect, UnsetType) else connect + self.read = timeout if isinstance(read, UnsetType) else read + self.write = timeout if isinstance(write, UnsetType) else write + self.pool = timeout if isinstance(pool, UnsetType) else pool + + def as_dict(self) -> dict[str, float | None]: + return { + "connect": self.connect, + "read": self.read, + "write": self.write, + "pool": self.pool, + } + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, self.__class__) + and self.connect == other.connect + and self.read == other.read + and self.write == other.write + and self.pool == other.pool + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + if len({self.connect, self.read, self.write, self.pool}) == 1: + return f"{class_name}(timeout={self.connect})" + return ( + f"{class_name}(connect={self.connect}, " + f"read={self.read}, write={self.write}, pool={self.pool})" + ) + + +class Limits: + """ + Configuration for limits to various client behaviors. + + **Parameters:** + + * **max_connections** - The maximum number of concurrent connections that may be + established. + * **max_keepalive_connections** - Allow the connection pool to maintain + keep-alive connections below this point. Should be less than or equal + to `max_connections`. + * **keepalive_expiry** - Time limit on idle keep-alive connections in seconds. + """ + + def __init__( + self, + *, + max_connections: int | None = None, + max_keepalive_connections: int | None = None, + keepalive_expiry: float | None = 5.0, + ) -> None: + self.max_connections = max_connections + self.max_keepalive_connections = max_keepalive_connections + self.keepalive_expiry = keepalive_expiry + + def __eq__(self, other: typing.Any) -> bool: + return ( + isinstance(other, self.__class__) + and self.max_connections == other.max_connections + and self.max_keepalive_connections == other.max_keepalive_connections + and self.keepalive_expiry == other.keepalive_expiry + ) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + return ( + f"{class_name}(max_connections={self.max_connections}, " + f"max_keepalive_connections={self.max_keepalive_connections}, " + f"keepalive_expiry={self.keepalive_expiry})" + ) + + +class Proxy: + def __init__( + self, + url: URLTypes, + *, + ssl_context: ssl.SSLContext | None = None, + auth: tuple[str, str] | None = None, + headers: HeaderTypes | None = None, + ) -> None: + url = URL(url) + headers = Headers(headers) + + if url.scheme not in ("http", "https", "socks5"): + raise ValueError(f"Unknown scheme for proxy URL {url!r}") + + if url.username or url.password: + # Remove any auth credentials from the URL. + auth = (url.username, url.password) + url = url.copy_with(username=None, password=None) + + self.url = url + self.auth = auth + self.headers = headers + self.ssl_context = ssl_context + + @property + def raw_auth(self) -> tuple[bytes, bytes] | None: + # The proxy authentication as raw bytes. + return ( + None + if self.auth is None + else (self.auth[0].encode("utf-8"), self.auth[1].encode("utf-8")) + ) + + def __repr__(self) -> str: + # The authentication is represented with the password component masked. + auth = (self.auth[0], "********") if self.auth else None + + # Build a nice concise representation. + url_str = f"{str(self.url)!r}" + auth_str = f", auth={auth!r}" if auth else "" + headers_str = f", headers={dict(self.headers)!r}" if self.headers else "" + return f"Proxy({url_str}{auth_str}{headers_str})" + + +DEFAULT_TIMEOUT_CONFIG = Timeout(timeout=5.0) +DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20) +DEFAULT_MAX_REDIRECTS = 20 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_content.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_content.py new file mode 100644 index 0000000..10b574b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_content.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +import inspect +import warnings +from json import dumps as json_dumps +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + Iterable, + Iterator, + Mapping, +) +from urllib.parse import urlencode + +from ._exceptions import StreamClosed, StreamConsumed +from ._multipart import MultipartStream +from ._types import ( + AsyncByteStream, + RequestContent, + RequestData, + RequestFiles, + ResponseContent, + SyncByteStream, +) +from ._utils import peek_filelike_length, primitive_value_to_str + + +class ByteStream(AsyncByteStream, SyncByteStream): + def __init__(self, stream: bytes) -> None: + self._stream = stream + + def __iter__(self) -> Iterator[bytes]: + yield self._stream + + async def __aiter__(self) -> AsyncIterator[bytes]: + yield self._stream + + +class IteratorByteStream(SyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: Iterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isgenerator(stream) + + def __iter__(self) -> Iterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "read"): + # File-like interfaces should use 'read' directly. + chunk = self._stream.read(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = self._stream.read(self.CHUNK_SIZE) + else: + # Otherwise iterate. + for part in self._stream: + yield part + + +class AsyncIteratorByteStream(AsyncByteStream): + CHUNK_SIZE = 65_536 + + def __init__(self, stream: AsyncIterable[bytes]) -> None: + self._stream = stream + self._is_stream_consumed = False + self._is_generator = inspect.isasyncgen(stream) + + async def __aiter__(self) -> AsyncIterator[bytes]: + if self._is_stream_consumed and self._is_generator: + raise StreamConsumed() + + self._is_stream_consumed = True + if hasattr(self._stream, "aread"): + # File-like interfaces should use 'aread' directly. + chunk = await self._stream.aread(self.CHUNK_SIZE) + while chunk: + yield chunk + chunk = await self._stream.aread(self.CHUNK_SIZE) + else: + # Otherwise iterate. + async for part in self._stream: + yield part + + +class UnattachedStream(AsyncByteStream, SyncByteStream): + """ + If a request or response is serialized using pickle, then it is no longer + attached to a stream for I/O purposes. Any stream operations should result + in `httpx.StreamClosed`. + """ + + def __iter__(self) -> Iterator[bytes]: + raise StreamClosed() + + async def __aiter__(self) -> AsyncIterator[bytes]: + raise StreamClosed() + yield b"" # pragma: no cover + + +def encode_content( + content: str | bytes | Iterable[bytes] | AsyncIterable[bytes], +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + if isinstance(content, (bytes, str)): + body = content.encode("utf-8") if isinstance(content, str) else content + content_length = len(body) + headers = {"Content-Length": str(content_length)} if body else {} + return headers, ByteStream(body) + + elif isinstance(content, Iterable) and not isinstance(content, dict): + # `not isinstance(content, dict)` is a bit oddly specific, but it + # catches a case that's easy for users to make in error, and would + # otherwise pass through here, like any other bytes-iterable, + # because `dict` happens to be iterable. See issue #2491. + content_length_or_none = peek_filelike_length(content) + + if content_length_or_none is None: + headers = {"Transfer-Encoding": "chunked"} + else: + headers = {"Content-Length": str(content_length_or_none)} + return headers, IteratorByteStream(content) # type: ignore + + elif isinstance(content, AsyncIterable): + headers = {"Transfer-Encoding": "chunked"} + return headers, AsyncIteratorByteStream(content) + + raise TypeError(f"Unexpected type for 'content', {type(content)!r}") + + +def encode_urlencoded_data( + data: RequestData, +) -> tuple[dict[str, str], ByteStream]: + plain_data = [] + for key, value in data.items(): + if isinstance(value, (list, tuple)): + plain_data.extend([(key, primitive_value_to_str(item)) for item in value]) + else: + plain_data.append((key, primitive_value_to_str(value))) + body = urlencode(plain_data, doseq=True).encode("utf-8") + content_length = str(len(body)) + content_type = "application/x-www-form-urlencoded" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_multipart_data( + data: RequestData, files: RequestFiles, boundary: bytes | None +) -> tuple[dict[str, str], MultipartStream]: + multipart = MultipartStream(data=data, files=files, boundary=boundary) + headers = multipart.get_headers() + return headers, multipart + + +def encode_text(text: str) -> tuple[dict[str, str], ByteStream]: + body = text.encode("utf-8") + content_length = str(len(body)) + content_type = "text/plain; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_html(html: str) -> tuple[dict[str, str], ByteStream]: + body = html.encode("utf-8") + content_length = str(len(body)) + content_type = "text/html; charset=utf-8" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_json(json: Any) -> tuple[dict[str, str], ByteStream]: + body = json_dumps(json).encode("utf-8") + content_length = str(len(body)) + content_type = "application/json" + headers = {"Content-Length": content_length, "Content-Type": content_type} + return headers, ByteStream(body) + + +def encode_request( + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: Any | None = None, + boundary: bytes | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, `data`, `files`, and `json`, + returning a two-tuple of (, ). + """ + if data is not None and not isinstance(data, Mapping): + # We prefer to separate `content=` + # for raw request content, and `data=
    ` for url encoded or + # multipart form content. + # + # However for compat with requests, we *do* still support + # `data=` usages. We deal with that case here, treating it + # as if `content=<...>` had been supplied instead. + message = "Use 'content=<...>' to upload raw bytes/text content." + warnings.warn(message, DeprecationWarning) + return encode_content(data) + + if content is not None: + return encode_content(content) + elif files: + return encode_multipart_data(data or {}, files, boundary) + elif data: + return encode_urlencoded_data(data) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") + + +def encode_response( + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: Any | None = None, +) -> tuple[dict[str, str], SyncByteStream | AsyncByteStream]: + """ + Handles encoding the given `content`, returning a two-tuple of + (, ). + """ + if content is not None: + return encode_content(content) + elif text is not None: + return encode_text(text) + elif html is not None: + return encode_html(html) + elif json is not None: + return encode_json(json) + + return {}, ByteStream(b"") diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_decoders.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_decoders.py new file mode 100644 index 0000000..31c72c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_decoders.py @@ -0,0 +1,329 @@ +""" +Handlers for Content-Encoding. + +See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding +""" +from __future__ import annotations + +import codecs +import io +import typing +import zlib + +from ._compat import brotli +from ._exceptions import DecodingError + + +class ContentDecoder: + def decode(self, data: bytes) -> bytes: + raise NotImplementedError() # pragma: no cover + + def flush(self) -> bytes: + raise NotImplementedError() # pragma: no cover + + +class IdentityDecoder(ContentDecoder): + """ + Handle unencoded data. + """ + + def decode(self, data: bytes) -> bytes: + return data + + def flush(self) -> bytes: + return b"" + + +class DeflateDecoder(ContentDecoder): + """ + Handle 'deflate' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.first_attempt = True + self.decompressor = zlib.decompressobj() + + def decode(self, data: bytes) -> bytes: + was_first_attempt = self.first_attempt + self.first_attempt = False + try: + return self.decompressor.decompress(data) + except zlib.error as exc: + if was_first_attempt: + self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS) + return self.decode(data) + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + try: + return self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class GZipDecoder(ContentDecoder): + """ + Handle 'gzip' decoding. + + See: https://stackoverflow.com/questions/1838699 + """ + + def __init__(self) -> None: + self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16) + + def decode(self, data: bytes) -> bytes: + try: + return self.decompressor.decompress(data) + except zlib.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + try: + return self.decompressor.flush() + except zlib.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class BrotliDecoder(ContentDecoder): + """ + Handle 'brotli' decoding. + + Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/ + or `pip install brotli`. See https://github.com/google/brotli + Supports both 'brotlipy' and 'Brotli' packages since they share an import + name. The top branches are for 'brotlipy' and bottom branches for 'Brotli' + """ + + def __init__(self) -> None: + if brotli is None: # pragma: no cover + raise ImportError( + "Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' " + "packages have been installed. " + "Make sure to install httpx using `pip install httpx[brotli]`." + ) from None + + self.decompressor = brotli.Decompressor() + self.seen_data = False + self._decompress: typing.Callable[[bytes], bytes] + if hasattr(self.decompressor, "decompress"): + # The 'brotlicffi' package. + self._decompress = self.decompressor.decompress # pragma: no cover + else: + # The 'brotli' package. + self._decompress = self.decompressor.process # pragma: no cover + + def decode(self, data: bytes) -> bytes: + if not data: + return b"" + self.seen_data = True + try: + return self._decompress(data) + except brotli.error as exc: + raise DecodingError(str(exc)) from exc + + def flush(self) -> bytes: + if not self.seen_data: + return b"" + try: + if hasattr(self.decompressor, "finish"): + # Only available in the 'brotlicffi' package. + + # As the decompressor decompresses eagerly, this + # will never actually emit any data. However, it will potentially throw + # errors if a truncated or damaged data stream has been used. + self.decompressor.finish() # pragma: no cover + return b"" + except brotli.error as exc: # pragma: no cover + raise DecodingError(str(exc)) from exc + + +class MultiDecoder(ContentDecoder): + """ + Handle the case where multiple encodings have been applied. + """ + + def __init__(self, children: typing.Sequence[ContentDecoder]) -> None: + """ + 'children' should be a sequence of decoders in the order in which + each was applied. + """ + # Note that we reverse the order for decoding. + self.children = list(reversed(children)) + + def decode(self, data: bytes) -> bytes: + for child in self.children: + data = child.decode(data) + return data + + def flush(self) -> bytes: + data = b"" + for child in self.children: + data = child.decode(data) + child.flush() + return data + + +class ByteChunker: + """ + Handles returning byte content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.BytesIO() + self._chunk_size = chunk_size + + def decode(self, content: bytes) -> list[bytes]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [ + value[i : i + self._chunk_size] + for i in range(0, len(value), self._chunk_size) + ] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[bytes]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextChunker: + """ + Handles returning text content in fixed-size chunks. + """ + + def __init__(self, chunk_size: int | None = None) -> None: + self._buffer = io.StringIO() + self._chunk_size = chunk_size + + def decode(self, content: str) -> list[str]: + if self._chunk_size is None: + return [content] if content else [] + + self._buffer.write(content) + if self._buffer.tell() >= self._chunk_size: + value = self._buffer.getvalue() + chunks = [ + value[i : i + self._chunk_size] + for i in range(0, len(value), self._chunk_size) + ] + if len(chunks[-1]) == self._chunk_size: + self._buffer.seek(0) + self._buffer.truncate() + return chunks + else: + self._buffer.seek(0) + self._buffer.write(chunks[-1]) + self._buffer.truncate() + return chunks[:-1] + else: + return [] + + def flush(self) -> list[str]: + value = self._buffer.getvalue() + self._buffer.seek(0) + self._buffer.truncate() + return [value] if value else [] + + +class TextDecoder: + """ + Handles incrementally decoding bytes into text + """ + + def __init__(self, encoding: str = "utf-8") -> None: + self.decoder = codecs.getincrementaldecoder(encoding)(errors="replace") + + def decode(self, data: bytes) -> str: + return self.decoder.decode(data) + + def flush(self) -> str: + return self.decoder.decode(b"", True) + + +class LineDecoder: + """ + Handles incrementally reading lines from text. + + Has the same behaviour as the stdllib splitlines, + but handling the input iteratively. + """ + + def __init__(self) -> None: + self.buffer: list[str] = [] + self.trailing_cr: bool = False + + def decode(self, text: str) -> list[str]: + # See https://docs.python.org/3/library/stdtypes.html#str.splitlines + NEWLINE_CHARS = "\n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029" + + # We always push a trailing `\r` into the next decode iteration. + if self.trailing_cr: + text = "\r" + text + self.trailing_cr = False + if text.endswith("\r"): + self.trailing_cr = True + text = text[:-1] + + if not text: + # NOTE: the edge case input of empty text doesn't occur in practice, + # because other httpx internals filter out this value + return [] # pragma: no cover + + trailing_newline = text[-1] in NEWLINE_CHARS + lines = text.splitlines() + + if len(lines) == 1 and not trailing_newline: + # No new lines, buffer the input and continue. + self.buffer.append(lines[0]) + return [] + + if self.buffer: + # Include any existing buffer in the first portion of the + # splitlines result. + lines = ["".join(self.buffer) + lines[0]] + lines[1:] + self.buffer = [] + + if not trailing_newline: + # If the last segment of splitlines is not newline terminated, + # then drop it from our output and start a new buffer. + self.buffer = [lines.pop()] + + return lines + + def flush(self) -> list[str]: + if not self.buffer and not self.trailing_cr: + return [] + + lines = ["".join(self.buffer)] + self.buffer = [] + self.trailing_cr = False + return lines + + +SUPPORTED_DECODERS = { + "identity": IdentityDecoder, + "gzip": GZipDecoder, + "deflate": DeflateDecoder, + "br": BrotliDecoder, +} + + +if brotli is None: + SUPPORTED_DECODERS.pop("br") # pragma: no cover diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_exceptions.py new file mode 100644 index 0000000..1142462 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_exceptions.py @@ -0,0 +1,347 @@ +""" +Our exception hierarchy: + +* HTTPError + x RequestError + + TransportError + - TimeoutException + · ConnectTimeout + · ReadTimeout + · WriteTimeout + · PoolTimeout + - NetworkError + · ConnectError + · ReadError + · WriteError + · CloseError + - ProtocolError + · LocalProtocolError + · RemoteProtocolError + - ProxyError + - UnsupportedProtocol + + DecodingError + + TooManyRedirects + x HTTPStatusError +* InvalidURL +* CookieConflict +* StreamError + x StreamConsumed + x StreamClosed + x ResponseNotRead + x RequestNotRead +""" +from __future__ import annotations + +import contextlib +import typing + +if typing.TYPE_CHECKING: + from ._models import Request, Response # pragma: no cover + + +class HTTPError(Exception): + """ + Base class for `RequestError` and `HTTPStatusError`. + + Useful for `try...except` blocks when issuing a request, + and then calling `.raise_for_status()`. + + For example: + + ``` + try: + response = httpx.get("https://www.example.com") + response.raise_for_status() + except httpx.HTTPError as exc: + print(f"HTTP Exception for {exc.request.url} - {exc}") + ``` + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + self._request: Request | None = None + + @property + def request(self) -> Request: + if self._request is None: + raise RuntimeError("The .request property has not been set.") + return self._request + + @request.setter + def request(self, request: Request) -> None: + self._request = request + + +class RequestError(HTTPError): + """ + Base class for all exceptions that may occur when issuing a `.request()`. + """ + + def __init__(self, message: str, *, request: Request | None = None) -> None: + super().__init__(message) + # At the point an exception is raised we won't typically have a request + # instance to associate it with. + # + # The 'request_context' context manager is used within the Client and + # Response methods in order to ensure that any raised exceptions + # have a `.request` property set on them. + self._request = request + + +class TransportError(RequestError): + """ + Base class for all exceptions that occur at the level of the Transport API. + """ + + +# Timeout exceptions... + + +class TimeoutException(TransportError): + """ + The base class for timeout errors. + + An operation has timed out. + """ + + +class ConnectTimeout(TimeoutException): + """ + Timed out while connecting to the host. + """ + + +class ReadTimeout(TimeoutException): + """ + Timed out while receiving data from the host. + """ + + +class WriteTimeout(TimeoutException): + """ + Timed out while sending data to the host. + """ + + +class PoolTimeout(TimeoutException): + """ + Timed out waiting to acquire a connection from the pool. + """ + + +# Core networking exceptions... + + +class NetworkError(TransportError): + """ + The base class for network-related errors. + + An error occurred while interacting with the network. + """ + + +class ReadError(NetworkError): + """ + Failed to receive data from the network. + """ + + +class WriteError(NetworkError): + """ + Failed to send data through the network. + """ + + +class ConnectError(NetworkError): + """ + Failed to establish a connection. + """ + + +class CloseError(NetworkError): + """ + Failed to close a connection. + """ + + +# Other transport exceptions... + + +class ProxyError(TransportError): + """ + An error occurred while establishing a proxy connection. + """ + + +class UnsupportedProtocol(TransportError): + """ + Attempted to make a request to an unsupported protocol. + + For example issuing a request to `ftp://www.example.com`. + """ + + +class ProtocolError(TransportError): + """ + The protocol was violated. + """ + + +class LocalProtocolError(ProtocolError): + """ + A protocol was violated by the client. + + For example if the user instantiated a `Request` instance explicitly, + failed to include the mandatory `Host:` header, and then issued it directly + using `client.send()`. + """ + + +class RemoteProtocolError(ProtocolError): + """ + The protocol was violated by the server. + + For example, returning malformed HTTP. + """ + + +# Other request exceptions... + + +class DecodingError(RequestError): + """ + Decoding of the response failed, due to a malformed encoding. + """ + + +class TooManyRedirects(RequestError): + """ + Too many redirects. + """ + + +# Client errors + + +class HTTPStatusError(HTTPError): + """ + The response had an error HTTP status of 4xx or 5xx. + + May be raised when calling `response.raise_for_status()` + """ + + def __init__(self, message: str, *, request: Request, response: Response) -> None: + super().__init__(message) + self.request = request + self.response = response + + +class InvalidURL(Exception): + """ + URL is improperly formed or cannot be parsed. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class CookieConflict(Exception): + """ + Attempted to lookup a cookie by name, but multiple cookies existed. + + Can occur when calling `response.cookies.get(...)`. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +# Stream exceptions... + +# These may occur as the result of a programming error, by accessing +# the request/response stream in an invalid manner. + + +class StreamError(RuntimeError): + """ + The base class for stream exceptions. + + The developer made an error in accessing the request stream in + an invalid way. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) + + +class StreamConsumed(StreamError): + """ + Attempted to read or stream content, but the content has already + been streamed. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. For requests, this could be due to passing " + "a generator as request content, and then receiving a redirect " + "response or a secondary request as part of an authentication flow." + "For responses, this could be due to attempting to stream the response " + "content more than once." + ) + super().__init__(message) + + +class StreamClosed(StreamError): + """ + Attempted to read or stream response content, but the request has been + closed. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream content, but the stream has " "been closed." + ) + super().__init__(message) + + +class ResponseNotRead(StreamError): + """ + Attempted to access streaming response content, without having called `read()`. + """ + + def __init__(self) -> None: + message = ( + "Attempted to access streaming response content," + " without having called `read()`." + ) + super().__init__(message) + + +class RequestNotRead(StreamError): + """ + Attempted to access streaming request content, without having called `read()`. + """ + + def __init__(self) -> None: + message = ( + "Attempted to access streaming request content," + " without having called `read()`." + ) + super().__init__(message) + + +@contextlib.contextmanager +def request_context( + request: Request | None = None, +) -> typing.Iterator[None]: + """ + A context manager that can be used to attach the given request context + to any `RequestError` exceptions that are raised within the block. + """ + try: + yield + except RequestError as exc: + if request is not None: + exc.request = request + raise exc diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_main.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_main.py new file mode 100644 index 0000000..72657f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_main.py @@ -0,0 +1,509 @@ +from __future__ import annotations + +import functools +import json +import sys +import typing + +import click +import httpcore +import pygments.lexers +import pygments.util +import rich.console +import rich.markup +import rich.progress +import rich.syntax +import rich.table + +from ._client import Client +from ._exceptions import RequestError +from ._models import Response +from ._status_codes import codes + + +def print_help() -> None: + console = rich.console.Console() + + console.print("[bold]HTTPX :butterfly:", justify="center") + console.print() + console.print("A next generation HTTP client.", justify="center") + console.print() + console.print( + "Usage: [bold]httpx[/bold] [cyan] [OPTIONS][/cyan] ", justify="left" + ) + console.print() + + table = rich.table.Table.grid(padding=1, pad_edge=True) + table.add_column("Parameter", no_wrap=True, justify="left", style="bold") + table.add_column("Description") + table.add_row( + "-m, --method [cyan]METHOD", + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD.\n" + "[Default: GET, or POST if a request body is included]", + ) + table.add_row( + "-p, --params [cyan] ...", + "Query parameters to include in the request URL.", + ) + table.add_row( + "-c, --content [cyan]TEXT", "Byte content to include in the request body." + ) + table.add_row( + "-d, --data [cyan] ...", "Form data to include in the request body." + ) + table.add_row( + "-f, --files [cyan] ...", + "Form files to include in the request body.", + ) + table.add_row("-j, --json [cyan]TEXT", "JSON data to include in the request body.") + table.add_row( + "-h, --headers [cyan] ...", + "Include additional HTTP headers in the request.", + ) + table.add_row( + "--cookies [cyan] ...", "Cookies to include in the request." + ) + table.add_row( + "--auth [cyan]", + "Username and password to include in the request. Specify '-' for the password" + " to use a password prompt. Note that using --verbose/-v will expose" + " the Authorization header, including the password encoding" + " in a trivially reversible format.", + ) + + table.add_row( + "--proxy [cyan]URL", + "Send the request via a proxy. Should be the URL giving the proxy address.", + ) + + table.add_row( + "--timeout [cyan]FLOAT", + "Timeout value to use for network operations, such as establishing the" + " connection, reading some data, etc... [Default: 5.0]", + ) + + table.add_row("--follow-redirects", "Automatically follow redirects.") + table.add_row("--no-verify", "Disable SSL verification.") + table.add_row( + "--http2", "Send the request using HTTP/2, if the remote server supports it." + ) + + table.add_row( + "--download [cyan]FILE", + "Save the response content as a file, rather than displaying it.", + ) + + table.add_row("-v, --verbose", "Verbose output. Show request as well as response.") + table.add_row("--help", "Show this message and exit.") + console.print(table) + + +def get_lexer_for_response(response: Response) -> str: + content_type = response.headers.get("Content-Type") + if content_type is not None: + mime_type, _, _ = content_type.partition(";") + try: + return typing.cast( + str, pygments.lexers.get_lexer_for_mimetype(mime_type.strip()).name + ) + except pygments.util.ClassNotFound: # pragma: no cover + pass + return "" # pragma: no cover + + +def format_request_headers(request: httpcore.Request, http2: bool = False) -> str: + version = "HTTP/2" if http2 else "HTTP/1.1" + headers = [ + (name.lower() if http2 else name, value) for name, value in request.headers + ] + method = request.method.decode("ascii") + target = request.url.target.decode("ascii") + lines = [f"{method} {target} {version}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def format_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> str: + version = http_version.decode("ascii") + reason = ( + codes.get_reason_phrase(status) + if reason_phrase is None + else reason_phrase.decode("ascii") + ) + lines = [f"{version} {status} {reason}"] + [ + f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers + ] + return "\n".join(lines) + + +def print_request_headers(request: httpcore.Request, http2: bool = False) -> None: + console = rich.console.Console() + http_text = format_request_headers(request, http2=http2) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response_headers( + http_version: bytes, + status: int, + reason_phrase: bytes | None, + headers: list[tuple[bytes, bytes]], +) -> None: + console = rich.console.Console() + http_text = format_response_headers(http_version, status, reason_phrase, headers) + syntax = rich.syntax.Syntax(http_text, "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + syntax = rich.syntax.Syntax("", "http", theme="ansi_dark", word_wrap=True) + console.print(syntax) + + +def print_response(response: Response) -> None: + console = rich.console.Console() + lexer_name = get_lexer_for_response(response) + if lexer_name: + if lexer_name.lower() == "json": + try: + data = response.json() + text = json.dumps(data, indent=4) + except ValueError: # pragma: no cover + text = response.text + else: + text = response.text + + syntax = rich.syntax.Syntax(text, lexer_name, theme="ansi_dark", word_wrap=True) + console.print(syntax) + else: + console.print(f"<{len(response.content)} bytes of binary data>") + + +_PCTRTT = typing.Tuple[typing.Tuple[str, str], ...] +_PCTRTTT = typing.Tuple[_PCTRTT, ...] +_PeerCertRetDictType = typing.Dict[str, typing.Union[str, _PCTRTTT, _PCTRTT]] + + +def format_certificate(cert: _PeerCertRetDictType) -> str: # pragma: no cover + lines = [] + for key, value in cert.items(): + if isinstance(value, (list, tuple)): + lines.append(f"* {key}:") + for item in value: + if key in ("subject", "issuer"): + for sub_item in item: + lines.append(f"* {sub_item[0]}: {sub_item[1]!r}") + elif isinstance(item, tuple) and len(item) == 2: + lines.append(f"* {item[0]}: {item[1]!r}") + else: + lines.append(f"* {item!r}") + else: + lines.append(f"* {key}: {value!r}") + return "\n".join(lines) + + +def trace( + name: str, info: typing.Mapping[str, typing.Any], verbose: bool = False +) -> None: + console = rich.console.Console() + if name == "connection.connect_tcp.started" and verbose: + host = info["host"] + console.print(f"* Connecting to {host!r}") + elif name == "connection.connect_tcp.complete" and verbose: + stream = info["return_value"] + server_addr = stream.get_extra_info("server_addr") + console.print(f"* Connected to {server_addr[0]!r} on port {server_addr[1]}") + elif name == "connection.start_tls.complete" and verbose: # pragma: no cover + stream = info["return_value"] + ssl_object = stream.get_extra_info("ssl_object") + version = ssl_object.version() + cipher = ssl_object.cipher() + server_cert = ssl_object.getpeercert() + alpn = ssl_object.selected_alpn_protocol() + console.print(f"* SSL established using {version!r} / {cipher[0]!r}") + console.print(f"* Selected ALPN protocol: {alpn!r}") + if server_cert: + console.print("* Server certificate:") + console.print(format_certificate(server_cert)) + elif name == "http11.send_request_headers.started" and verbose: + request = info["request"] + print_request_headers(request, http2=False) + elif name == "http2.send_request_headers.started" and verbose: # pragma: no cover + request = info["request"] + print_request_headers(request, http2=True) + elif name == "http11.receive_response_headers.complete": + http_version, status, reason_phrase, headers = info["return_value"] + print_response_headers(http_version, status, reason_phrase, headers) + elif name == "http2.receive_response_headers.complete": # pragma: no cover + status, headers = info["return_value"] + http_version = b"HTTP/2" + reason_phrase = None + print_response_headers(http_version, status, reason_phrase, headers) + + +def download_response(response: Response, download: typing.BinaryIO) -> None: + console = rich.console.Console() + console.print() + content_length = response.headers.get("Content-Length") + with rich.progress.Progress( + "[progress.description]{task.description}", + "[progress.percentage]{task.percentage:>3.0f}%", + rich.progress.BarColumn(bar_width=None), + rich.progress.DownloadColumn(), + rich.progress.TransferSpeedColumn(), + ) as progress: + description = f"Downloading [bold]{rich.markup.escape(download.name)}" + download_task = progress.add_task( + description, + total=int(content_length or 0), + start=content_length is not None, + ) + for chunk in response.iter_bytes(): + download.write(chunk) + progress.update(download_task, completed=response.num_bytes_downloaded) + + +def validate_json( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value is None: + return None + + try: + return json.loads(value) + except json.JSONDecodeError: # pragma: no cover + raise click.BadParameter("Not valid JSON") + + +def validate_auth( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> typing.Any: + if value == (None, None): + return None + + username, password = value + if password == "-": # pragma: no cover + password = click.prompt("Password", hide_input=True) + return (username, password) + + +def handle_help( + ctx: click.Context, + param: click.Option | click.Parameter, + value: typing.Any, +) -> None: + if not value or ctx.resilient_parsing: + return + + print_help() + ctx.exit() + + +@click.command(add_help_option=False) +@click.argument("url", type=str) +@click.option( + "--method", + "-m", + "method", + type=str, + help=( + "Request method, such as GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD. " + "[Default: GET, or POST if a request body is included]" + ), +) +@click.option( + "--params", + "-p", + "params", + type=(str, str), + multiple=True, + help="Query parameters to include in the request URL.", +) +@click.option( + "--content", + "-c", + "content", + type=str, + help="Byte content to include in the request body.", +) +@click.option( + "--data", + "-d", + "data", + type=(str, str), + multiple=True, + help="Form data to include in the request body.", +) +@click.option( + "--files", + "-f", + "files", + type=(str, click.File(mode="rb")), + multiple=True, + help="Form files to include in the request body.", +) +@click.option( + "--json", + "-j", + "json", + type=str, + callback=validate_json, + help="JSON data to include in the request body.", +) +@click.option( + "--headers", + "-h", + "headers", + type=(str, str), + multiple=True, + help="Include additional HTTP headers in the request.", +) +@click.option( + "--cookies", + "cookies", + type=(str, str), + multiple=True, + help="Cookies to include in the request.", +) +@click.option( + "--auth", + "auth", + type=(str, str), + default=(None, None), + callback=validate_auth, + help=( + "Username and password to include in the request. " + "Specify '-' for the password to use a password prompt. " + "Note that using --verbose/-v will expose the Authorization header, " + "including the password encoding in a trivially reversible format." + ), +) +@click.option( + "--proxy", + "proxy", + type=str, + default=None, + help="Send the request via a proxy. Should be the URL giving the proxy address.", +) +@click.option( + "--timeout", + "timeout", + type=float, + default=5.0, + help=( + "Timeout value to use for network operations, such as establishing the " + "connection, reading some data, etc... [Default: 5.0]" + ), +) +@click.option( + "--follow-redirects", + "follow_redirects", + is_flag=True, + default=False, + help="Automatically follow redirects.", +) +@click.option( + "--no-verify", + "verify", + is_flag=True, + default=True, + help="Disable SSL verification.", +) +@click.option( + "--http2", + "http2", + type=bool, + is_flag=True, + default=False, + help="Send the request using HTTP/2, if the remote server supports it.", +) +@click.option( + "--download", + type=click.File("wb"), + help="Save the response content as a file, rather than displaying it.", +) +@click.option( + "--verbose", + "-v", + type=bool, + is_flag=True, + default=False, + help="Verbose. Show request as well as response.", +) +@click.option( + "--help", + is_flag=True, + is_eager=True, + expose_value=False, + callback=handle_help, + help="Show this message and exit.", +) +def main( + url: str, + method: str, + params: list[tuple[str, str]], + content: str, + data: list[tuple[str, str]], + files: list[tuple[str, click.File]], + json: str, + headers: list[tuple[str, str]], + cookies: list[tuple[str, str]], + auth: tuple[str, str] | None, + proxy: str, + timeout: float, + follow_redirects: bool, + verify: bool, + http2: bool, + download: typing.BinaryIO | None, + verbose: bool, +) -> None: + """ + An HTTP command line client. + Sends a request and displays the response. + """ + if not method: + method = "POST" if content or data or files or json else "GET" + + try: + with Client( + proxy=proxy, + timeout=timeout, + verify=verify, + http2=http2, + ) as client: + with client.stream( + method, + url, + params=list(params), + content=content, + data=dict(data), + files=files, # type: ignore + json=json, + headers=headers, + cookies=dict(cookies), + auth=auth, + follow_redirects=follow_redirects, + extensions={"trace": functools.partial(trace, verbose=verbose)}, + ) as response: + if download is not None: + download_response(response, download) + else: + response.read() + if response.content: + print_response(response) + + except RequestError as exc: + console = rich.console.Console() + console.print(f"[red]{type(exc).__name__}[/red]: {exc}") + sys.exit(1) + + sys.exit(0 if response.is_success else 1) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_models.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_models.py new file mode 100644 index 0000000..cd76705 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_models.py @@ -0,0 +1,1209 @@ +from __future__ import annotations + +import datetime +import email.message +import json as jsonlib +import typing +import urllib.request +from collections.abc import Mapping +from http.cookiejar import Cookie, CookieJar + +from ._content import ByteStream, UnattachedStream, encode_request, encode_response +from ._decoders import ( + SUPPORTED_DECODERS, + ByteChunker, + ContentDecoder, + IdentityDecoder, + LineDecoder, + MultiDecoder, + TextChunker, + TextDecoder, +) +from ._exceptions import ( + CookieConflict, + HTTPStatusError, + RequestNotRead, + ResponseNotRead, + StreamClosed, + StreamConsumed, + request_context, +) +from ._multipart import get_multipart_boundary_from_content_type +from ._status_codes import codes +from ._types import ( + AsyncByteStream, + CookieTypes, + HeaderTypes, + QueryParamTypes, + RequestContent, + RequestData, + RequestExtensions, + RequestFiles, + ResponseContent, + ResponseExtensions, + SyncByteStream, +) +from ._urls import URL +from ._utils import ( + is_known_encoding, + normalize_header_key, + normalize_header_value, + obfuscate_sensitive_headers, + parse_content_type_charset, + parse_header_links, +) + + +class Headers(typing.MutableMapping[str, str]): + """ + HTTP headers, as a case-insensitive multi-dict. + """ + + def __init__( + self, + headers: HeaderTypes | None = None, + encoding: str | None = None, + ) -> None: + if headers is None: + self._list = [] # type: typing.List[typing.Tuple[bytes, bytes, bytes]] + elif isinstance(headers, Headers): + self._list = list(headers._list) + elif isinstance(headers, Mapping): + self._list = [ + ( + normalize_header_key(k, lower=False, encoding=encoding), + normalize_header_key(k, lower=True, encoding=encoding), + normalize_header_value(v, encoding), + ) + for k, v in headers.items() + ] + else: + self._list = [ + ( + normalize_header_key(k, lower=False, encoding=encoding), + normalize_header_key(k, lower=True, encoding=encoding), + normalize_header_value(v, encoding), + ) + for k, v in headers + ] + + self._encoding = encoding + + @property + def encoding(self) -> str: + """ + Header encoding is mandated as ascii, but we allow fallbacks to utf-8 + or iso-8859-1. + """ + if self._encoding is None: + for encoding in ["ascii", "utf-8"]: + for key, value in self.raw: + try: + key.decode(encoding) + value.decode(encoding) + except UnicodeDecodeError: + break + else: + # The else block runs if 'break' did not occur, meaning + # all values fitted the encoding. + self._encoding = encoding + break + else: + # The ISO-8859-1 encoding covers all 256 code points in a byte, + # so will never raise decode errors. + self._encoding = "iso-8859-1" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + self._encoding = value + + @property + def raw(self) -> list[tuple[bytes, bytes]]: + """ + Returns a list of the raw header items, as byte pairs. + """ + return [(raw_key, value) for raw_key, _, value in self._list] + + def keys(self) -> typing.KeysView[str]: + return {key.decode(self.encoding): None for _, key, value in self._list}.keys() + + def values(self) -> typing.ValuesView[str]: + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return `(key, value)` items of headers. Concatenate headers + into a single comma separated value when a key occurs multiple times. + """ + values_dict: dict[str, str] = {} + for _, key, value in self._list: + str_key = key.decode(self.encoding) + str_value = value.decode(self.encoding) + if str_key in values_dict: + values_dict[str_key] += f", {str_value}" + else: + values_dict[str_key] = str_value + return values_dict.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return a list of `(key, value)` pairs of headers. Allow multiple + occurrences of the same key without concatenating into a single + comma separated value. + """ + return [ + (key.decode(self.encoding), value.decode(self.encoding)) + for _, key, value in self._list + ] + + def get(self, key: str, default: typing.Any = None) -> typing.Any: + """ + Return a header value. If multiple occurrences of the header occur + then concatenate them together with commas. + """ + try: + return self[key] + except KeyError: + return default + + def get_list(self, key: str, split_commas: bool = False) -> list[str]: + """ + Return a list of all header values for a given key. + If `split_commas=True` is passed, then any comma separated header + values are split into multiple return strings. + """ + get_header_key = key.lower().encode(self.encoding) + + values = [ + item_value.decode(self.encoding) + for _, item_key, item_value in self._list + if item_key.lower() == get_header_key + ] + + if not split_commas: + return values + + split_values = [] + for value in values: + split_values.extend([item.strip() for item in value.split(",")]) + return split_values + + def update(self, headers: HeaderTypes | None = None) -> None: # type: ignore + headers = Headers(headers) + for key in headers.keys(): + if key in self: + self.pop(key) + self._list.extend(headers._list) + + def copy(self) -> Headers: + return Headers(self, encoding=self.encoding) + + def __getitem__(self, key: str) -> str: + """ + Return a single header value. + + If there are multiple headers with the same key, then we concatenate + them with commas. See: https://tools.ietf.org/html/rfc7230#section-3.2.2 + """ + normalized_key = key.lower().encode(self.encoding) + + items = [ + header_value.decode(self.encoding) + for _, header_key, header_value in self._list + if header_key == normalized_key + ] + + if items: + return ", ".join(items) + + raise KeyError(key) + + def __setitem__(self, key: str, value: str) -> None: + """ + Set the header `key` to `value`, removing any duplicate entries. + Retains insertion order. + """ + set_key = key.encode(self._encoding or "utf-8") + set_value = value.encode(self._encoding or "utf-8") + lookup_key = set_key.lower() + + found_indexes = [ + idx + for idx, (_, item_key, _) in enumerate(self._list) + if item_key == lookup_key + ] + + for idx in reversed(found_indexes[1:]): + del self._list[idx] + + if found_indexes: + idx = found_indexes[0] + self._list[idx] = (set_key, lookup_key, set_value) + else: + self._list.append((set_key, lookup_key, set_value)) + + def __delitem__(self, key: str) -> None: + """ + Remove the header `key`. + """ + del_key = key.lower().encode(self.encoding) + + pop_indexes = [ + idx + for idx, (_, item_key, _) in enumerate(self._list) + if item_key.lower() == del_key + ] + + if not pop_indexes: + raise KeyError(key) + + for idx in reversed(pop_indexes): + del self._list[idx] + + def __contains__(self, key: typing.Any) -> bool: + header_key = key.lower().encode(self.encoding) + return header_key in [key for _, key, _ in self._list] + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._list) + + def __eq__(self, other: typing.Any) -> bool: + try: + other_headers = Headers(other) + except ValueError: + return False + + self_list = [(key, value) for _, key, value in self._list] + other_list = [(key, value) for _, key, value in other_headers._list] + return sorted(self_list) == sorted(other_list) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + + encoding_str = "" + if self.encoding != "ascii": + encoding_str = f", encoding={self.encoding!r}" + + as_list = list(obfuscate_sensitive_headers(self.multi_items())) + as_dict = dict(as_list) + + no_duplicate_keys = len(as_dict) == len(as_list) + if no_duplicate_keys: + return f"{class_name}({as_dict!r}{encoding_str})" + return f"{class_name}({as_list!r}{encoding_str})" + + +class Request: + def __init__( + self, + method: str | bytes, + url: URL | str, + *, + params: QueryParamTypes | None = None, + headers: HeaderTypes | None = None, + cookies: CookieTypes | None = None, + content: RequestContent | None = None, + data: RequestData | None = None, + files: RequestFiles | None = None, + json: typing.Any | None = None, + stream: SyncByteStream | AsyncByteStream | None = None, + extensions: RequestExtensions | None = None, + ) -> None: + self.method = ( + method.decode("ascii").upper() + if isinstance(method, bytes) + else method.upper() + ) + self.url = URL(url) + if params is not None: + self.url = self.url.copy_merge_params(params=params) + self.headers = Headers(headers) + self.extensions = {} if extensions is None else extensions + + if cookies: + Cookies(cookies).set_cookie_header(self) + + if stream is None: + content_type: str | None = self.headers.get("content-type") + headers, stream = encode_request( + content=content, + data=data, + files=files, + json=json, + boundary=get_multipart_boundary_from_content_type( + content_type=content_type.encode(self.headers.encoding) + if content_type + else None + ), + ) + self._prepare(headers) + self.stream = stream + # Load the request body, except for streaming content. + if isinstance(stream, ByteStream): + self.read() + else: + # There's an important distinction between `Request(content=...)`, + # and `Request(stream=...)`. + # + # Using `content=...` implies automatically populated `Host` and content + # headers, of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include *any* + # auto-populated headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when: + # + # * Preserving the request stream when copying requests, eg for redirects. + # * Creating request instances on the *server-side* of the transport API. + self.stream = stream + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Transfer-Encoding if the Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "Content-Length" in self.headers: + continue + self.headers.setdefault(key, value) + + auto_headers: list[tuple[bytes, bytes]] = [] + + has_host = "Host" in self.headers + has_content_length = ( + "Content-Length" in self.headers or "Transfer-Encoding" in self.headers + ) + + if not has_host and self.url.host: + auto_headers.append((b"Host", self.url.netloc)) + if not has_content_length and self.method in ("POST", "PUT", "PATCH"): + auto_headers.append((b"Content-Length", b"0")) + + self.headers = Headers(auto_headers + self.headers.raw) + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise RequestNotRead() + return self._content + + def read(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.Iterable) + self._content = b"".join(self.stream) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + async def aread(self) -> bytes: + """ + Read and return the request content. + """ + if not hasattr(self, "_content"): + assert isinstance(self.stream, typing.AsyncIterable) + self._content = b"".join([part async for part in self.stream]) + if not isinstance(self.stream, ByteStream): + # If a streaming request has been read entirely into memory, then + # we can replace the stream with a raw bytes implementation, + # to ensure that any non-replayable streams can still be used. + self.stream = ByteStream(self._content) + return self._content + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + url = str(self.url) + return f"<{class_name}({self.method!r}, {url!r})>" + + def __getstate__(self) -> dict[str, typing.Any]: + return { + name: value + for name, value in self.__dict__.items() + if name not in ["extensions", "stream"] + } + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.extensions = {} + self.stream = UnattachedStream() + + +class Response: + def __init__( + self, + status_code: int, + *, + headers: HeaderTypes | None = None, + content: ResponseContent | None = None, + text: str | None = None, + html: str | None = None, + json: typing.Any = None, + stream: SyncByteStream | AsyncByteStream | None = None, + request: Request | None = None, + extensions: ResponseExtensions | None = None, + history: list[Response] | None = None, + default_encoding: str | typing.Callable[[bytes], str] = "utf-8", + ) -> None: + self.status_code = status_code + self.headers = Headers(headers) + + self._request: Request | None = request + + # When follow_redirects=False and a redirect is received, + # the client will set `response.next_request`. + self.next_request: Request | None = None + + self.extensions: ResponseExtensions = {} if extensions is None else extensions + self.history = [] if history is None else list(history) + + self.is_closed = False + self.is_stream_consumed = False + + self.default_encoding = default_encoding + + if stream is None: + headers, stream = encode_response(content, text, html, json) + self._prepare(headers) + self.stream = stream + if isinstance(stream, ByteStream): + # Load the response body, except for streaming content. + self.read() + else: + # There's an important distinction between `Response(content=...)`, + # and `Response(stream=...)`. + # + # Using `content=...` implies automatically populated content headers, + # of either `Content-Length: ...` or `Transfer-Encoding: chunked`. + # + # Using `stream=...` will not automatically include any content headers. + # + # As an end-user you don't really need `stream=...`. It's only + # useful when creating response instances having received a stream + # from the transport API. + self.stream = stream + + self._num_bytes_downloaded = 0 + + def _prepare(self, default_headers: dict[str, str]) -> None: + for key, value in default_headers.items(): + # Ignore Transfer-Encoding if the Content-Length has been set explicitly. + if key.lower() == "transfer-encoding" and "content-length" in self.headers: + continue + self.headers.setdefault(key, value) + + @property + def elapsed(self) -> datetime.timedelta: + """ + Returns the time taken for the complete request/response + cycle to complete. + """ + if not hasattr(self, "_elapsed"): + raise RuntimeError( + "'.elapsed' may only be accessed after the response " + "has been read or closed." + ) + return self._elapsed + + @elapsed.setter + def elapsed(self, elapsed: datetime.timedelta) -> None: + self._elapsed = elapsed + + @property + def request(self) -> Request: + """ + Returns the request instance associated to the current response. + """ + if self._request is None: + raise RuntimeError( + "The request instance has not been set on this response." + ) + return self._request + + @request.setter + def request(self, value: Request) -> None: + self._request = value + + @property + def http_version(self) -> str: + try: + http_version: bytes = self.extensions["http_version"] + except KeyError: + return "HTTP/1.1" + else: + return http_version.decode("ascii", errors="ignore") + + @property + def reason_phrase(self) -> str: + try: + reason_phrase: bytes = self.extensions["reason_phrase"] + except KeyError: + return codes.get_reason_phrase(self.status_code) + else: + return reason_phrase.decode("ascii", errors="ignore") + + @property + def url(self) -> URL: + """ + Returns the URL for which the request was made. + """ + return self.request.url + + @property + def content(self) -> bytes: + if not hasattr(self, "_content"): + raise ResponseNotRead() + return self._content + + @property + def text(self) -> str: + if not hasattr(self, "_text"): + content = self.content + if not content: + self._text = "" + else: + decoder = TextDecoder(encoding=self.encoding or "utf-8") + self._text = "".join([decoder.decode(self.content), decoder.flush()]) + return self._text + + @property + def encoding(self) -> str | None: + """ + Return an encoding to use for decoding the byte content into text. + The priority for determining this is given by... + + * `.encoding = <>` has been set explicitly. + * The encoding as specified by the charset parameter in the Content-Type header. + * The encoding as determined by `default_encoding`, which may either be + a string like "utf-8" indicating the encoding to use, or may be a callable + which enables charset autodetection. + """ + if not hasattr(self, "_encoding"): + encoding = self.charset_encoding + if encoding is None or not is_known_encoding(encoding): + if isinstance(self.default_encoding, str): + encoding = self.default_encoding + elif hasattr(self, "_content"): + encoding = self.default_encoding(self._content) + self._encoding = encoding or "utf-8" + return self._encoding + + @encoding.setter + def encoding(self, value: str) -> None: + """ + Set the encoding to use for decoding the byte content into text. + + If the `text` attribute has been accessed, attempting to set the + encoding will throw a ValueError. + """ + if hasattr(self, "_text"): + raise ValueError( + "Setting encoding after `text` has been accessed is not allowed." + ) + self._encoding = value + + @property + def charset_encoding(self) -> str | None: + """ + Return the encoding, as specified by the Content-Type header. + """ + content_type = self.headers.get("Content-Type") + if content_type is None: + return None + + return parse_content_type_charset(content_type) + + def _get_content_decoder(self) -> ContentDecoder: + """ + Returns a decoder instance which can be used to decode the raw byte + content, depending on the Content-Encoding used in the response. + """ + if not hasattr(self, "_decoder"): + decoders: list[ContentDecoder] = [] + values = self.headers.get_list("content-encoding", split_commas=True) + for value in values: + value = value.strip().lower() + try: + decoder_cls = SUPPORTED_DECODERS[value] + decoders.append(decoder_cls()) + except KeyError: + continue + + if len(decoders) == 1: + self._decoder = decoders[0] + elif len(decoders) > 1: + self._decoder = MultiDecoder(children=decoders) + else: + self._decoder = IdentityDecoder() + + return self._decoder + + @property + def is_informational(self) -> bool: + """ + A property which is `True` for 1xx status codes, `False` otherwise. + """ + return codes.is_informational(self.status_code) + + @property + def is_success(self) -> bool: + """ + A property which is `True` for 2xx status codes, `False` otherwise. + """ + return codes.is_success(self.status_code) + + @property + def is_redirect(self) -> bool: + """ + A property which is `True` for 3xx status codes, `False` otherwise. + + Note that not all responses with a 3xx status code indicate a URL redirect. + + Use `response.has_redirect_location` to determine responses with a properly + formed URL redirection. + """ + return codes.is_redirect(self.status_code) + + @property + def is_client_error(self) -> bool: + """ + A property which is `True` for 4xx status codes, `False` otherwise. + """ + return codes.is_client_error(self.status_code) + + @property + def is_server_error(self) -> bool: + """ + A property which is `True` for 5xx status codes, `False` otherwise. + """ + return codes.is_server_error(self.status_code) + + @property + def is_error(self) -> bool: + """ + A property which is `True` for 4xx and 5xx status codes, `False` otherwise. + """ + return codes.is_error(self.status_code) + + @property + def has_redirect_location(self) -> bool: + """ + Returns True for 3xx responses with a properly formed URL redirection, + `False` otherwise. + """ + return ( + self.status_code + in ( + # 301 (Cacheable redirect. Method may change to GET.) + codes.MOVED_PERMANENTLY, + # 302 (Uncacheable redirect. Method may change to GET.) + codes.FOUND, + # 303 (Client should make a GET or HEAD request.) + codes.SEE_OTHER, + # 307 (Equiv. 302, but retain method) + codes.TEMPORARY_REDIRECT, + # 308 (Equiv. 301, but retain method) + codes.PERMANENT_REDIRECT, + ) + and "Location" in self.headers + ) + + def raise_for_status(self) -> Response: + """ + Raise the `HTTPStatusError` if one occurred. + """ + request = self._request + if request is None: + raise RuntimeError( + "Cannot call `raise_for_status` as the request " + "instance has not been set on this response." + ) + + if self.is_success: + return self + + if self.has_redirect_location: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "Redirect location: '{0.headers[location]}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + else: + message = ( + "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" + ) + + status_class = self.status_code // 100 + error_types = { + 1: "Informational response", + 3: "Redirect response", + 4: "Client error", + 5: "Server error", + } + error_type = error_types.get(status_class, "Invalid status code") + message = message.format(self, error_type=error_type) + raise HTTPStatusError(message, request=request, response=self) + + def json(self, **kwargs: typing.Any) -> typing.Any: + return jsonlib.loads(self.content, **kwargs) + + @property + def cookies(self) -> Cookies: + if not hasattr(self, "_cookies"): + self._cookies = Cookies() + self._cookies.extract_cookies(self) + return self._cookies + + @property + def links(self) -> dict[str | None, dict[str, str]]: + """ + Returns the parsed header links of the response, if any + """ + header = self.headers.get("link") + if header is None: + return {} + + return { + (link.get("rel") or link.get("url")): link + for link in parse_header_links(header) + } + + @property + def num_bytes_downloaded(self) -> int: + return self._num_bytes_downloaded + + def __repr__(self) -> str: + return f"" + + def __getstate__(self) -> dict[str, typing.Any]: + return { + name: value + for name, value in self.__dict__.items() + if name not in ["extensions", "stream", "is_closed", "_decoder"] + } + + def __setstate__(self, state: dict[str, typing.Any]) -> None: + for name, value in state.items(): + setattr(self, name, value) + self.is_closed = True + self.extensions = {} + self.stream = UnattachedStream() + + def read(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + self._content = b"".join(self.iter_bytes()) + return self._content + + def iter_bytes(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, and brotli encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for raw_bytes in self.iter_raw(): + decoded = decoder.decode(raw_bytes) + for chunk in chunker.decode(decoded): + yield chunk + decoded = decoder.flush() + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> typing.Iterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + for byte_content in self.iter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + def iter_lines(self) -> typing.Iterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + for text in self.iter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + def iter_raw(self, chunk_size: int | None = None) -> typing.Iterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call a sync iterator on an async stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + with request_context(request=self._request): + for raw_stream_bytes in self.stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, SyncByteStream): + raise RuntimeError("Attempted to call an sync close on an async stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + self.stream.close() + + async def aread(self) -> bytes: + """ + Read and return the response content. + """ + if not hasattr(self, "_content"): + self._content = b"".join([part async for part in self.aiter_bytes()]) + return self._content + + async def aiter_bytes( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + This allows us to handle gzip, deflate, and brotli encoded responses. + """ + if hasattr(self, "_content"): + chunk_size = len(self._content) if chunk_size is None else chunk_size + for i in range(0, len(self._content), max(chunk_size, 1)): + yield self._content[i : i + chunk_size] + else: + decoder = self._get_content_decoder() + chunker = ByteChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async for raw_bytes in self.aiter_raw(): + decoded = decoder.decode(raw_bytes) + for chunk in chunker.decode(decoded): + yield chunk + decoded = decoder.flush() + for chunk in chunker.decode(decoded): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_text( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[str]: + """ + A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + decoder = TextDecoder(encoding=self.encoding or "utf-8") + chunker = TextChunker(chunk_size=chunk_size) + with request_context(request=self._request): + async for byte_content in self.aiter_bytes(): + text_content = decoder.decode(byte_content) + for chunk in chunker.decode(text_content): + yield chunk + text_content = decoder.flush() + for chunk in chunker.decode(text_content): + yield chunk # pragma: no cover + for chunk in chunker.flush(): + yield chunk + + async def aiter_lines(self) -> typing.AsyncIterator[str]: + decoder = LineDecoder() + with request_context(request=self._request): + async for text in self.aiter_text(): + for line in decoder.decode(text): + yield line + for line in decoder.flush(): + yield line + + async def aiter_raw( + self, chunk_size: int | None = None + ) -> typing.AsyncIterator[bytes]: + """ + A byte-iterator over the raw response content. + """ + if self.is_stream_consumed: + raise StreamConsumed() + if self.is_closed: + raise StreamClosed() + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async iterator on an sync stream.") + + self.is_stream_consumed = True + self._num_bytes_downloaded = 0 + chunker = ByteChunker(chunk_size=chunk_size) + + with request_context(request=self._request): + async for raw_stream_bytes in self.stream: + self._num_bytes_downloaded += len(raw_stream_bytes) + for chunk in chunker.decode(raw_stream_bytes): + yield chunk + + for chunk in chunker.flush(): + yield chunk + + await self.aclose() + + async def aclose(self) -> None: + """ + Close the response and release the connection. + Automatically called if the response body is read to completion. + """ + if not isinstance(self.stream, AsyncByteStream): + raise RuntimeError("Attempted to call an async close on an sync stream.") + + if not self.is_closed: + self.is_closed = True + with request_context(request=self._request): + await self.stream.aclose() + + +class Cookies(typing.MutableMapping[str, str]): + """ + HTTP Cookies, as a mutable mapping. + """ + + def __init__(self, cookies: CookieTypes | None = None) -> None: + if cookies is None or isinstance(cookies, dict): + self.jar = CookieJar() + if isinstance(cookies, dict): + for key, value in cookies.items(): + self.set(key, value) + elif isinstance(cookies, list): + self.jar = CookieJar() + for key, value in cookies: + self.set(key, value) + elif isinstance(cookies, Cookies): + self.jar = CookieJar() + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + else: + self.jar = cookies + + def extract_cookies(self, response: Response) -> None: + """ + Loads any cookies based on the response `Set-Cookie` headers. + """ + urllib_response = self._CookieCompatResponse(response) + urllib_request = self._CookieCompatRequest(response.request) + + self.jar.extract_cookies(urllib_response, urllib_request) # type: ignore + + def set_cookie_header(self, request: Request) -> None: + """ + Sets an appropriate 'Cookie:' HTTP header on the `Request`. + """ + urllib_request = self._CookieCompatRequest(request) + self.jar.add_cookie_header(urllib_request) + + def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None: + """ + Set a cookie value by name. May optionally include domain and path. + """ + kwargs = { + "version": 0, + "name": name, + "value": value, + "port": None, + "port_specified": False, + "domain": domain, + "domain_specified": bool(domain), + "domain_initial_dot": domain.startswith("."), + "path": path, + "path_specified": bool(path), + "secure": False, + "expires": None, + "discard": True, + "comment": None, + "comment_url": None, + "rest": {"HttpOnly": None}, + "rfc2109": False, + } + cookie = Cookie(**kwargs) # type: ignore + self.jar.set_cookie(cookie) + + def get( # type: ignore + self, + name: str, + default: str | None = None, + domain: str | None = None, + path: str | None = None, + ) -> str | None: + """ + Get a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to retrieve. + """ + value = None + for cookie in self.jar: + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if value is not None: + message = f"Multiple cookies exist with name={name}" + raise CookieConflict(message) + value = cookie.value + + if value is None: + return default + return value + + def delete( + self, + name: str, + domain: str | None = None, + path: str | None = None, + ) -> None: + """ + Delete a cookie by name. May optionally include domain and path + in order to specify exactly which cookie to delete. + """ + if domain is not None and path is not None: + return self.jar.clear(domain, path, name) + + remove = [ + cookie + for cookie in self.jar + if cookie.name == name + and (domain is None or cookie.domain == domain) + and (path is None or cookie.path == path) + ] + + for cookie in remove: + self.jar.clear(cookie.domain, cookie.path, cookie.name) + + def clear(self, domain: str | None = None, path: str | None = None) -> None: + """ + Delete all cookies. Optionally include a domain and path in + order to only delete a subset of all the cookies. + """ + args = [] + if domain is not None: + args.append(domain) + if path is not None: + assert domain is not None + args.append(path) + self.jar.clear(*args) + + def update(self, cookies: CookieTypes | None = None) -> None: # type: ignore + cookies = Cookies(cookies) + for cookie in cookies.jar: + self.jar.set_cookie(cookie) + + def __setitem__(self, name: str, value: str) -> None: + return self.set(name, value) + + def __getitem__(self, name: str) -> str: + value = self.get(name) + if value is None: + raise KeyError(name) + return value + + def __delitem__(self, name: str) -> None: + return self.delete(name) + + def __len__(self) -> int: + return len(self.jar) + + def __iter__(self) -> typing.Iterator[str]: + return (cookie.name for cookie in self.jar) + + def __bool__(self) -> bool: + for _ in self.jar: + return True + return False + + def __repr__(self) -> str: + cookies_repr = ", ".join( + [ + f"" + for cookie in self.jar + ] + ) + + return f"" + + class _CookieCompatRequest(urllib.request.Request): + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, request: Request) -> None: + super().__init__( + url=str(request.url), + headers=dict(request.headers), + method=request.method, + ) + self.request = request + + def add_unredirected_header(self, key: str, value: str) -> None: + super().add_unredirected_header(key, value) + self.request.headers[key] = value + + class _CookieCompatResponse: + """ + Wraps a `Request` instance up in a compatibility interface suitable + for use with `CookieJar` operations. + """ + + def __init__(self, response: Response) -> None: + self.response = response + + def info(self) -> email.message.Message: + info = email.message.Message() + for key, value in self.response.headers.multi_items(): + # Note that setting `info[key]` here is an "append" operation, + # not a "replace" operation. + # https://docs.python.org/3/library/email.compat32-message.html#email.message.Message.__setitem__ + info[key] = value + return info diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_multipart.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_multipart.py new file mode 100644 index 0000000..8edb622 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_multipart.py @@ -0,0 +1,269 @@ +from __future__ import annotations + +import io +import os +import typing +from pathlib import Path + +from ._types import ( + AsyncByteStream, + FileContent, + FileTypes, + RequestData, + RequestFiles, + SyncByteStream, +) +from ._utils import ( + format_form_param, + guess_content_type, + peek_filelike_length, + primitive_value_to_str, + to_bytes, +) + + +def get_multipart_boundary_from_content_type( + content_type: bytes | None, +) -> bytes | None: + if not content_type or not content_type.startswith(b"multipart/form-data"): + return None + # parse boundary according to + # https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1 + if b";" in content_type: + for section in content_type.split(b";"): + if section.strip().lower().startswith(b"boundary="): + return section.strip()[len(b"boundary=") :].strip(b'"') + return None + + +class DataField: + """ + A single form field item, within a multipart form field. + """ + + def __init__(self, name: str, value: str | bytes | int | float | None) -> None: + if not isinstance(name, str): + raise TypeError( + f"Invalid type for name. Expected str, got {type(name)}: {name!r}" + ) + if value is not None and not isinstance(value, (str, bytes, int, float)): + raise TypeError( + "Invalid type for value. Expected primitive type," + f" got {type(value)}: {value!r}" + ) + self.name = name + self.value: str | bytes = ( + value if isinstance(value, bytes) else primitive_value_to_str(value) + ) + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + name = format_form_param("name", self.name) + self._headers = b"".join( + [b"Content-Disposition: form-data; ", name, b"\r\n\r\n"] + ) + + return self._headers + + def render_data(self) -> bytes: + if not hasattr(self, "_data"): + self._data = to_bytes(self.value) + + return self._data + + def get_length(self) -> int: + headers = self.render_headers() + data = self.render_data() + return len(headers) + len(data) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield self.render_data() + + +class FileField: + """ + A single file field item, within a multipart form field. + """ + + CHUNK_SIZE = 64 * 1024 + + def __init__(self, name: str, value: FileTypes) -> None: + self.name = name + + fileobj: FileContent + + headers: dict[str, str] = {} + content_type: str | None = None + + # This large tuple based API largely mirror's requests' API + # It would be good to think of better APIs for this that we could + # include in httpx 2.0 since variable length tuples(especially of 4 elements) + # are quite unwieldly + if isinstance(value, tuple): + if len(value) == 2: + # neither the 3rd parameter (content_type) nor the 4th (headers) + # was included + filename, fileobj = value + elif len(value) == 3: + filename, fileobj, content_type = value + else: + # all 4 parameters included + filename, fileobj, content_type, headers = value # type: ignore + else: + filename = Path(str(getattr(value, "name", "upload"))).name + fileobj = value + + if content_type is None: + content_type = guess_content_type(filename) + + has_content_type_header = any("content-type" in key.lower() for key in headers) + if content_type is not None and not has_content_type_header: + # note that unlike requests, we ignore the content_type provided in the 3rd + # tuple element if it is also included in the headers requests does + # the opposite (it overwrites the headerwith the 3rd tuple element) + headers["Content-Type"] = content_type + + if isinstance(fileobj, io.StringIO): + raise TypeError( + "Multipart file uploads require 'io.BytesIO', not 'io.StringIO'." + ) + if isinstance(fileobj, io.TextIOBase): + raise TypeError( + "Multipart file uploads must be opened in binary mode, not text mode." + ) + + self.filename = filename + self.file = fileobj + self.headers = headers + + def get_length(self) -> int | None: + headers = self.render_headers() + + if isinstance(self.file, (str, bytes)): + return len(headers) + len(to_bytes(self.file)) + + file_length = peek_filelike_length(self.file) + + # If we can't determine the filesize without reading it into memory, + # then return `None` here, to indicate an unknown file length. + if file_length is None: + return None + + return len(headers) + file_length + + def render_headers(self) -> bytes: + if not hasattr(self, "_headers"): + parts = [ + b"Content-Disposition: form-data; ", + format_form_param("name", self.name), + ] + if self.filename: + filename = format_form_param("filename", self.filename) + parts.extend([b"; ", filename]) + for header_name, header_value in self.headers.items(): + key, val = f"\r\n{header_name}: ".encode(), header_value.encode() + parts.extend([key, val]) + parts.append(b"\r\n\r\n") + self._headers = b"".join(parts) + + return self._headers + + def render_data(self) -> typing.Iterator[bytes]: + if isinstance(self.file, (str, bytes)): + yield to_bytes(self.file) + return + + if hasattr(self.file, "seek"): + try: + self.file.seek(0) + except io.UnsupportedOperation: + pass + + chunk = self.file.read(self.CHUNK_SIZE) + while chunk: + yield to_bytes(chunk) + chunk = self.file.read(self.CHUNK_SIZE) + + def render(self) -> typing.Iterator[bytes]: + yield self.render_headers() + yield from self.render_data() + + +class MultipartStream(SyncByteStream, AsyncByteStream): + """ + Request content as streaming multipart encoded form data. + """ + + def __init__( + self, + data: RequestData, + files: RequestFiles, + boundary: bytes | None = None, + ) -> None: + if boundary is None: + boundary = os.urandom(16).hex().encode("ascii") + + self.boundary = boundary + self.content_type = "multipart/form-data; boundary=%s" % boundary.decode( + "ascii" + ) + self.fields = list(self._iter_fields(data, files)) + + def _iter_fields( + self, data: RequestData, files: RequestFiles + ) -> typing.Iterator[FileField | DataField]: + for name, value in data.items(): + if isinstance(value, (tuple, list)): + for item in value: + yield DataField(name=name, value=item) + else: + yield DataField(name=name, value=value) + + file_items = files.items() if isinstance(files, typing.Mapping) else files + for name, value in file_items: + yield FileField(name=name, value=value) + + def iter_chunks(self) -> typing.Iterator[bytes]: + for field in self.fields: + yield b"--%s\r\n" % self.boundary + yield from field.render() + yield b"\r\n" + yield b"--%s--\r\n" % self.boundary + + def get_content_length(self) -> int | None: + """ + Return the length of the multipart encoded content, or `None` if + any of the files have a length that cannot be determined upfront. + """ + boundary_length = len(self.boundary) + length = 0 + + for field in self.fields: + field_length = field.get_length() + if field_length is None: + return None + + length += 2 + boundary_length + 2 # b"--{boundary}\r\n" + length += field_length + length += 2 # b"\r\n" + + length += 2 + boundary_length + 4 # b"--{boundary}--\r\n" + return length + + # Content stream interface. + + def get_headers(self) -> dict[str, str]: + content_length = self.get_content_length() + content_type = self.content_type + if content_length is None: + return {"Transfer-Encoding": "chunked", "Content-Type": content_type} + return {"Content-Length": str(content_length), "Content-Type": content_type} + + def __iter__(self) -> typing.Iterator[bytes]: + for chunk in self.iter_chunks(): + yield chunk + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + for chunk in self.iter_chunks(): + yield chunk diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_status_codes.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_status_codes.py new file mode 100644 index 0000000..4cde4e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_status_codes.py @@ -0,0 +1,160 @@ +from __future__ import annotations + +from enum import IntEnum + + +class codes(IntEnum): + """HTTP status codes and reason phrases + + Status codes from the following RFCs are all observed: + + * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616 + * RFC 6585: Additional HTTP Status Codes + * RFC 3229: Delta encoding in HTTP + * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518 + * RFC 5842: Binding Extensions to WebDAV + * RFC 7238: Permanent Redirect + * RFC 2295: Transparent Content Negotiation in HTTP + * RFC 2774: An HTTP Extension Framework + * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2) + * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) + * RFC 7725: An HTTP Status Code to Report Legal Obstacles + * RFC 8297: An HTTP Status Code for Indicating Hints + * RFC 8470: Using Early Data in HTTP + """ + + def __new__(cls, value: int, phrase: str = "") -> codes: + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase # type: ignore[attr-defined] + return obj + + def __str__(self) -> str: + return str(self.value) + + @classmethod + def get_reason_phrase(cls, value: int) -> str: + try: + return codes(value).phrase # type: ignore + except ValueError: + return "" + + @classmethod + def is_informational(cls, value: int) -> bool: + """ + Returns `True` for 1xx status codes, `False` otherwise. + """ + return 100 <= value <= 199 + + @classmethod + def is_success(cls, value: int) -> bool: + """ + Returns `True` for 2xx status codes, `False` otherwise. + """ + return 200 <= value <= 299 + + @classmethod + def is_redirect(cls, value: int) -> bool: + """ + Returns `True` for 3xx status codes, `False` otherwise. + """ + return 300 <= value <= 399 + + @classmethod + def is_client_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx status codes, `False` otherwise. + """ + return 400 <= value <= 499 + + @classmethod + def is_server_error(cls, value: int) -> bool: + """ + Returns `True` for 5xx status codes, `False` otherwise. + """ + return 500 <= value <= 599 + + @classmethod + def is_error(cls, value: int) -> bool: + """ + Returns `True` for 4xx or 5xx status codes, `False` otherwise. + """ + return 400 <= value <= 599 + + # informational + CONTINUE = 100, "Continue" + SWITCHING_PROTOCOLS = 101, "Switching Protocols" + PROCESSING = 102, "Processing" + EARLY_HINTS = 103, "Early Hints" + + # success + OK = 200, "OK" + CREATED = 201, "Created" + ACCEPTED = 202, "Accepted" + NON_AUTHORITATIVE_INFORMATION = 203, "Non-Authoritative Information" + NO_CONTENT = 204, "No Content" + RESET_CONTENT = 205, "Reset Content" + PARTIAL_CONTENT = 206, "Partial Content" + MULTI_STATUS = 207, "Multi-Status" + ALREADY_REPORTED = 208, "Already Reported" + IM_USED = 226, "IM Used" + + # redirection + MULTIPLE_CHOICES = 300, "Multiple Choices" + MOVED_PERMANENTLY = 301, "Moved Permanently" + FOUND = 302, "Found" + SEE_OTHER = 303, "See Other" + NOT_MODIFIED = 304, "Not Modified" + USE_PROXY = 305, "Use Proxy" + TEMPORARY_REDIRECT = 307, "Temporary Redirect" + PERMANENT_REDIRECT = 308, "Permanent Redirect" + + # client error + BAD_REQUEST = 400, "Bad Request" + UNAUTHORIZED = 401, "Unauthorized" + PAYMENT_REQUIRED = 402, "Payment Required" + FORBIDDEN = 403, "Forbidden" + NOT_FOUND = 404, "Not Found" + METHOD_NOT_ALLOWED = 405, "Method Not Allowed" + NOT_ACCEPTABLE = 406, "Not Acceptable" + PROXY_AUTHENTICATION_REQUIRED = 407, "Proxy Authentication Required" + REQUEST_TIMEOUT = 408, "Request Timeout" + CONFLICT = 409, "Conflict" + GONE = 410, "Gone" + LENGTH_REQUIRED = 411, "Length Required" + PRECONDITION_FAILED = 412, "Precondition Failed" + REQUEST_ENTITY_TOO_LARGE = 413, "Request Entity Too Large" + REQUEST_URI_TOO_LONG = 414, "Request-URI Too Long" + UNSUPPORTED_MEDIA_TYPE = 415, "Unsupported Media Type" + REQUESTED_RANGE_NOT_SATISFIABLE = 416, "Requested Range Not Satisfiable" + EXPECTATION_FAILED = 417, "Expectation Failed" + IM_A_TEAPOT = 418, "I'm a teapot" + MISDIRECTED_REQUEST = 421, "Misdirected Request" + UNPROCESSABLE_ENTITY = 422, "Unprocessable Entity" + LOCKED = 423, "Locked" + FAILED_DEPENDENCY = 424, "Failed Dependency" + TOO_EARLY = 425, "Too Early" + UPGRADE_REQUIRED = 426, "Upgrade Required" + PRECONDITION_REQUIRED = 428, "Precondition Required" + TOO_MANY_REQUESTS = 429, "Too Many Requests" + REQUEST_HEADER_FIELDS_TOO_LARGE = 431, "Request Header Fields Too Large" + UNAVAILABLE_FOR_LEGAL_REASONS = 451, "Unavailable For Legal Reasons" + + # server errors + INTERNAL_SERVER_ERROR = 500, "Internal Server Error" + NOT_IMPLEMENTED = 501, "Not Implemented" + BAD_GATEWAY = 502, "Bad Gateway" + SERVICE_UNAVAILABLE = 503, "Service Unavailable" + GATEWAY_TIMEOUT = 504, "Gateway Timeout" + HTTP_VERSION_NOT_SUPPORTED = 505, "HTTP Version Not Supported" + VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates" + INSUFFICIENT_STORAGE = 507, "Insufficient Storage" + LOOP_DETECTED = 508, "Loop Detected" + NOT_EXTENDED = 510, "Not Extended" + NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required" + + +# Include lower-case styles for `requests` compatibility. +for code in codes: + setattr(codes, code._name_.lower(), int(code)) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__init__.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..87a9abf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/asgi.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/asgi.cpython-310.pyc new file mode 100644 index 0000000..01e63c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/asgi.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/base.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000..63cd730 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/base.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/default.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/default.cpython-310.pyc new file mode 100644 index 0000000..7780425 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/default.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/mock.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/mock.cpython-310.pyc new file mode 100644 index 0000000..999a2a6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/mock.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/wsgi.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/wsgi.cpython-310.pyc new file mode 100644 index 0000000..df8e890 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/__pycache__/wsgi.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/asgi.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/asgi.py new file mode 100644 index 0000000..9543a12 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/asgi.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +import typing + +import sniffio + +from .._models import Request, Response +from .._types import AsyncByteStream +from .base import AsyncBaseTransport + +if typing.TYPE_CHECKING: # pragma: no cover + import asyncio + + import trio + + Event = typing.Union[asyncio.Event, trio.Event] + + +_Message = typing.Dict[str, typing.Any] +_Receive = typing.Callable[[], typing.Awaitable[_Message]] +_Send = typing.Callable[ + [typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None] +] +_ASGIApp = typing.Callable[ + [typing.Dict[str, typing.Any], _Receive, _Send], typing.Coroutine[None, None, None] +] + + +def create_event() -> Event: + if sniffio.current_async_library() == "trio": + import trio + + return trio.Event() + else: + import asyncio + + return asyncio.Event() + + +class ASGIResponseStream(AsyncByteStream): + def __init__(self, body: list[bytes]) -> None: + self._body = body + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + yield b"".join(self._body) + + +class ASGITransport(AsyncBaseTransport): + """ + A custom AsyncTransport that handles sending requests directly to an ASGI app. + The simplest way to use this functionality is to use the `app` argument. + + ``` + client = httpx.AsyncClient(app=app) + ``` + + Alternatively, you can setup the transport instance explicitly. + This allows you to include any additional configuration arguments specific + to the ASGITransport class: + + ``` + transport = httpx.ASGITransport( + app=app, + root_path="/submount", + client=("1.2.3.4", 123) + ) + client = httpx.AsyncClient(transport=transport) + ``` + + Arguments: + + * `app` - The ASGI application. + * `raise_app_exceptions` - Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + * `root_path` - The root path on which the ASGI application should be mounted. + * `client` - A two-tuple indicating the client IP and port of incoming requests. + ``` + """ + + def __init__( + self, + app: _ASGIApp, + raise_app_exceptions: bool = True, + root_path: str = "", + client: tuple[str, int] = ("127.0.0.1", 123), + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.root_path = root_path + self.client = client + + async def handle_async_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + # ASGI scope. + scope = { + "type": "http", + "asgi": {"version": "3.0"}, + "http_version": "1.1", + "method": request.method, + "headers": [(k.lower(), v) for (k, v) in request.headers.raw], + "scheme": request.url.scheme, + "path": request.url.path, + "raw_path": request.url.raw_path.split(b"?")[0], + "query_string": request.url.query, + "server": (request.url.host, request.url.port), + "client": self.client, + "root_path": self.root_path, + } + + # Request. + request_body_chunks = request.stream.__aiter__() + request_complete = False + + # Response. + status_code = None + response_headers = None + body_parts = [] + response_started = False + response_complete = create_event() + + # ASGI callables. + + async def receive() -> dict[str, typing.Any]: + nonlocal request_complete + + if request_complete: + await response_complete.wait() + return {"type": "http.disconnect"} + + try: + body = await request_body_chunks.__anext__() + except StopAsyncIteration: + request_complete = True + return {"type": "http.request", "body": b"", "more_body": False} + return {"type": "http.request", "body": body, "more_body": True} + + async def send(message: dict[str, typing.Any]) -> None: + nonlocal status_code, response_headers, response_started + + if message["type"] == "http.response.start": + assert not response_started + + status_code = message["status"] + response_headers = message.get("headers", []) + response_started = True + + elif message["type"] == "http.response.body": + assert not response_complete.is_set() + body = message.get("body", b"") + more_body = message.get("more_body", False) + + if body and request.method != "HEAD": + body_parts.append(body) + + if not more_body: + response_complete.set() + + try: + await self.app(scope, receive, send) + except Exception: # noqa: PIE-786 + if self.raise_app_exceptions: + raise + + response_complete.set() + if status_code is None: + status_code = 500 + if response_headers is None: + response_headers = {} + + assert response_complete.is_set() + assert status_code is not None + assert response_headers is not None + + stream = ASGIResponseStream(body_parts) + + return Response(status_code, headers=response_headers, stream=stream) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/base.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/base.py new file mode 100644 index 0000000..8b6dc3c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/base.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import typing +from types import TracebackType + +from .._models import Request, Response + +T = typing.TypeVar("T", bound="BaseTransport") +A = typing.TypeVar("A", bound="AsyncBaseTransport") + + +class BaseTransport: + def __enter__(self: T) -> T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + self.close() + + def handle_request(self, request: Request) -> Response: + """ + Send a single HTTP request and return a response. + + Developers shouldn't typically ever need to call into this API directly, + since the Client class provides all the higher level user-facing API + niceties. + + In order to properly release any network resources, the response + stream should *either* be consumed immediately, with a call to + `response.stream.read()`, or else the `handle_request` call should + be followed with a try/finally block to ensuring the stream is + always closed. + + Example usage: + + with httpx.HTTPTransport() as transport: + req = httpx.Request( + method=b"GET", + url=(b"https", b"www.example.com", 443, b"/"), + headers=[(b"Host", b"www.example.com")], + ) + resp = transport.handle_request(req) + body = resp.stream.read() + print(resp.status_code, resp.headers, body) + + + Takes a `Request` instance as the only argument. + + Returns a `Response` instance. + """ + raise NotImplementedError( + "The 'handle_request' method must be implemented." + ) # pragma: no cover + + def close(self) -> None: + pass + + +class AsyncBaseTransport: + async def __aenter__(self: A) -> A: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + await self.aclose() + + async def handle_async_request( + self, + request: Request, + ) -> Response: + raise NotImplementedError( + "The 'handle_async_request' method must be implemented." + ) # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/default.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/default.py new file mode 100644 index 0000000..14476a3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/default.py @@ -0,0 +1,385 @@ +""" +Custom transports, with nicely configured defaults. + +The following additional keyword arguments are currently supported by httpcore... + +* uds: str +* local_address: str +* retries: int + +Example usages... + +# Disable HTTP/2 on a single specific domain. +mounts = { + "all://": httpx.HTTPTransport(http2=True), + "all://*example.org": httpx.HTTPTransport() +} + +# Using advanced httpcore configuration, with connection retries. +transport = httpx.HTTPTransport(retries=1) +client = httpx.Client(transport=transport) + +# Using advanced httpcore configuration, with unix domain sockets. +transport = httpx.HTTPTransport(uds="socket.uds") +client = httpx.Client(transport=transport) +""" +from __future__ import annotations + +import contextlib +import typing +from types import TracebackType + +import httpcore + +from .._config import DEFAULT_LIMITS, Limits, Proxy, create_ssl_context +from .._exceptions import ( + ConnectError, + ConnectTimeout, + LocalProtocolError, + NetworkError, + PoolTimeout, + ProtocolError, + ProxyError, + ReadError, + ReadTimeout, + RemoteProtocolError, + TimeoutException, + UnsupportedProtocol, + WriteError, + WriteTimeout, +) +from .._models import Request, Response +from .._types import AsyncByteStream, CertTypes, ProxyTypes, SyncByteStream, VerifyTypes +from .._urls import URL +from .base import AsyncBaseTransport, BaseTransport + +T = typing.TypeVar("T", bound="HTTPTransport") +A = typing.TypeVar("A", bound="AsyncHTTPTransport") + +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + + +@contextlib.contextmanager +def map_httpcore_exceptions() -> typing.Iterator[None]: + try: + yield + except Exception as exc: + mapped_exc = None + + for from_exc, to_exc in HTTPCORE_EXC_MAP.items(): + if not isinstance(exc, from_exc): + continue + # We want to map to the most specific exception we can find. + # Eg if `exc` is an `httpcore.ReadTimeout`, we want to map to + # `httpx.ReadTimeout`, not just `httpx.TimeoutException`. + if mapped_exc is None or issubclass(to_exc, mapped_exc): + mapped_exc = to_exc + + if mapped_exc is None: # pragma: no cover + raise + + message = str(exc) + raise mapped_exc(message) from exc + + +HTTPCORE_EXC_MAP = { + httpcore.TimeoutException: TimeoutException, + httpcore.ConnectTimeout: ConnectTimeout, + httpcore.ReadTimeout: ReadTimeout, + httpcore.WriteTimeout: WriteTimeout, + httpcore.PoolTimeout: PoolTimeout, + httpcore.NetworkError: NetworkError, + httpcore.ConnectError: ConnectError, + httpcore.ReadError: ReadError, + httpcore.WriteError: WriteError, + httpcore.ProxyError: ProxyError, + httpcore.UnsupportedProtocol: UnsupportedProtocol, + httpcore.ProtocolError: ProtocolError, + httpcore.LocalProtocolError: LocalProtocolError, + httpcore.RemoteProtocolError: RemoteProtocolError, +} + + +class ResponseStream(SyncByteStream): + def __init__(self, httpcore_stream: typing.Iterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + def __iter__(self) -> typing.Iterator[bytes]: + with map_httpcore_exceptions(): + for part in self._httpcore_stream: + yield part + + def close(self) -> None: + if hasattr(self._httpcore_stream, "close"): + self._httpcore_stream.close() + + +class HTTPTransport(BaseTransport): + def __init__( + self, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + trust_env: bool = True, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + + if proxy is None: + self._pool = httpcore.ConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore.HTTPProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + ssl_context=ssl_context, + proxy_ssl_context=proxy.ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme == "socks5": + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore.SOCKSProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + "Proxy protocol must be either 'http', 'https', or 'socks5'," + f" but got {proxy.url.scheme!r}." + ) + + def __enter__(self: T) -> T: # Use generics for subclass support. + self._pool.__enter__() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + self._pool.__exit__(exc_type, exc_value, traceback) + + def handle_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, SyncByteStream) + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = self._pool.handle_request(req) + + assert isinstance(resp.stream, typing.Iterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=ResponseStream(resp.stream), + extensions=resp.extensions, + ) + + def close(self) -> None: + self._pool.close() + + +class AsyncResponseStream(AsyncByteStream): + def __init__(self, httpcore_stream: typing.AsyncIterable[bytes]) -> None: + self._httpcore_stream = httpcore_stream + + async def __aiter__(self) -> typing.AsyncIterator[bytes]: + with map_httpcore_exceptions(): + async for part in self._httpcore_stream: + yield part + + async def aclose(self) -> None: + if hasattr(self._httpcore_stream, "aclose"): + await self._httpcore_stream.aclose() + + +class AsyncHTTPTransport(AsyncBaseTransport): + def __init__( + self, + verify: VerifyTypes = True, + cert: CertTypes | None = None, + http1: bool = True, + http2: bool = False, + limits: Limits = DEFAULT_LIMITS, + trust_env: bool = True, + proxy: ProxyTypes | None = None, + uds: str | None = None, + local_address: str | None = None, + retries: int = 0, + socket_options: typing.Iterable[SOCKET_OPTION] | None = None, + ) -> None: + ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) + proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy + + if proxy is None: + self._pool = httpcore.AsyncConnectionPool( + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + uds=uds, + local_address=local_address, + retries=retries, + socket_options=socket_options, + ) + elif proxy.url.scheme in ("http", "https"): + self._pool = httpcore.AsyncHTTPProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + proxy_headers=proxy.headers.raw, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + socket_options=socket_options, + ) + elif proxy.url.scheme == "socks5": + try: + import socksio # noqa + except ImportError: # pragma: no cover + raise ImportError( + "Using SOCKS proxy, but the 'socksio' package is not installed. " + "Make sure to install httpx using `pip install httpx[socks]`." + ) from None + + self._pool = httpcore.AsyncSOCKSProxy( + proxy_url=httpcore.URL( + scheme=proxy.url.raw_scheme, + host=proxy.url.raw_host, + port=proxy.url.port, + target=proxy.url.raw_path, + ), + proxy_auth=proxy.raw_auth, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + http1=http1, + http2=http2, + ) + else: # pragma: no cover + raise ValueError( + "Proxy protocol must be either 'http', 'https', or 'socks5'," + " but got {proxy.url.scheme!r}." + ) + + async def __aenter__(self: A) -> A: # Use generics for subclass support. + await self._pool.__aenter__() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None = None, + exc_value: BaseException | None = None, + traceback: TracebackType | None = None, + ) -> None: + with map_httpcore_exceptions(): + await self._pool.__aexit__(exc_type, exc_value, traceback) + + async def handle_async_request( + self, + request: Request, + ) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + with map_httpcore_exceptions(): + resp = await self._pool.handle_async_request(req) + + assert isinstance(resp.stream, typing.AsyncIterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=AsyncResponseStream(resp.stream), + extensions=resp.extensions, + ) + + async def aclose(self) -> None: + await self._pool.aclose() diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/mock.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/mock.py new file mode 100644 index 0000000..5abea83 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/mock.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +import typing + +from .._models import Request, Response +from .base import AsyncBaseTransport, BaseTransport + +SyncHandler = typing.Callable[[Request], Response] +AsyncHandler = typing.Callable[[Request], typing.Coroutine[None, None, Response]] + + +class MockTransport(AsyncBaseTransport, BaseTransport): + def __init__(self, handler: SyncHandler | AsyncHandler) -> None: + self.handler = handler + + def handle_request( + self, + request: Request, + ) -> Response: + request.read() + response = self.handler(request) + if not isinstance(response, Response): # pragma: no cover + raise TypeError("Cannot use an async handler in a sync Client") + return response + + async def handle_async_request( + self, + request: Request, + ) -> Response: + await request.aread() + response = self.handler(request) + + # Allow handler to *optionally* be an `async` function. + # If it is, then the `response` variable need to be awaited to actually + # return the result. + + if not isinstance(response, Response): + response = await response + + return response diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/wsgi.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/wsgi.py new file mode 100644 index 0000000..cd03a94 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_transports/wsgi.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +import io +import itertools +import sys +import typing + +from .._models import Request, Response +from .._types import SyncByteStream +from .base import BaseTransport + +if typing.TYPE_CHECKING: + from _typeshed import OptExcInfo # pragma: no cover + from _typeshed.wsgi import WSGIApplication # pragma: no cover + +_T = typing.TypeVar("_T") + + +def _skip_leading_empty_chunks(body: typing.Iterable[_T]) -> typing.Iterable[_T]: + body = iter(body) + for chunk in body: + if chunk: + return itertools.chain([chunk], body) + return [] + + +class WSGIByteStream(SyncByteStream): + def __init__(self, result: typing.Iterable[bytes]) -> None: + self._close = getattr(result, "close", None) + self._result = _skip_leading_empty_chunks(result) + + def __iter__(self) -> typing.Iterator[bytes]: + for part in self._result: + yield part + + def close(self) -> None: + if self._close is not None: + self._close() + + +class WSGITransport(BaseTransport): + """ + A custom transport that handles sending requests directly to an WSGI app. + The simplest way to use this functionality is to use the `app` argument. + + ``` + client = httpx.Client(app=app) + ``` + + Alternatively, you can setup the transport instance explicitly. + This allows you to include any additional configuration arguments specific + to the WSGITransport class: + + ``` + transport = httpx.WSGITransport( + app=app, + script_name="/submount", + remote_addr="1.2.3.4" + ) + client = httpx.Client(transport=transport) + ``` + + Arguments: + + * `app` - The WSGI application. + * `raise_app_exceptions` - Boolean indicating if exceptions in the application + should be raised. Default to `True`. Can be set to `False` for use cases + such as testing the content of a client 500 response. + * `script_name` - The root path on which the WSGI application should be mounted. + * `remote_addr` - A string indicating the client IP of incoming requests. + ``` + """ + + def __init__( + self, + app: WSGIApplication, + raise_app_exceptions: bool = True, + script_name: str = "", + remote_addr: str = "127.0.0.1", + wsgi_errors: typing.TextIO | None = None, + ) -> None: + self.app = app + self.raise_app_exceptions = raise_app_exceptions + self.script_name = script_name + self.remote_addr = remote_addr + self.wsgi_errors = wsgi_errors + + def handle_request(self, request: Request) -> Response: + request.read() + wsgi_input = io.BytesIO(request.content) + + port = request.url.port or {"http": 80, "https": 443}[request.url.scheme] + environ = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": request.url.scheme, + "wsgi.input": wsgi_input, + "wsgi.errors": self.wsgi_errors or sys.stderr, + "wsgi.multithread": True, + "wsgi.multiprocess": False, + "wsgi.run_once": False, + "REQUEST_METHOD": request.method, + "SCRIPT_NAME": self.script_name, + "PATH_INFO": request.url.path, + "QUERY_STRING": request.url.query.decode("ascii"), + "SERVER_NAME": request.url.host, + "SERVER_PORT": str(port), + "SERVER_PROTOCOL": "HTTP/1.1", + "REMOTE_ADDR": self.remote_addr, + } + for header_key, header_value in request.headers.raw: + key = header_key.decode("ascii").upper().replace("-", "_") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = "HTTP_" + key + environ[key] = header_value.decode("ascii") + + seen_status = None + seen_response_headers = None + seen_exc_info = None + + def start_response( + status: str, + response_headers: list[tuple[str, str]], + exc_info: OptExcInfo | None = None, + ) -> typing.Callable[[bytes], typing.Any]: + nonlocal seen_status, seen_response_headers, seen_exc_info + seen_status = status + seen_response_headers = response_headers + seen_exc_info = exc_info + return lambda _: None + + result = self.app(environ, start_response) + + stream = WSGIByteStream(result) + + assert seen_status is not None + assert seen_response_headers is not None + if seen_exc_info and seen_exc_info[0] and self.raise_app_exceptions: + raise seen_exc_info[1] + + status_code = int(seen_status.split()[0]) + headers = [ + (key.encode("ascii"), value.encode("ascii")) + for key, value in seen_response_headers + ] + + return Response(status_code, headers=headers, stream=stream) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_types.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_types.py new file mode 100644 index 0000000..649d101 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_types.py @@ -0,0 +1,134 @@ +""" +Type definitions for type checking purposes. +""" + +import ssl +from http.cookiejar import CookieJar +from typing import ( + IO, + TYPE_CHECKING, + Any, + AsyncIterable, + AsyncIterator, + Callable, + Dict, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + NamedTuple, + Optional, + Sequence, + Tuple, + Union, +) + +if TYPE_CHECKING: # pragma: no cover + from ._auth import Auth # noqa: F401 + from ._config import Proxy, Timeout # noqa: F401 + from ._models import Cookies, Headers, Request # noqa: F401 + from ._urls import URL, QueryParams # noqa: F401 + + +PrimitiveData = Optional[Union[str, int, float, bool]] + +RawURL = NamedTuple( + "RawURL", + [ + ("raw_scheme", bytes), + ("raw_host", bytes), + ("port", Optional[int]), + ("raw_path", bytes), + ], +) + +URLTypes = Union["URL", str] + +QueryParamTypes = Union[ + "QueryParams", + Mapping[str, Union[PrimitiveData, Sequence[PrimitiveData]]], + List[Tuple[str, PrimitiveData]], + Tuple[Tuple[str, PrimitiveData], ...], + str, + bytes, +] + +HeaderTypes = Union[ + "Headers", + Mapping[str, str], + Mapping[bytes, bytes], + Sequence[Tuple[str, str]], + Sequence[Tuple[bytes, bytes]], +] + +CookieTypes = Union["Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]] + +CertTypes = Union[ + # certfile + str, + # (certfile, keyfile) + Tuple[str, Optional[str]], + # (certfile, keyfile, password) + Tuple[str, Optional[str], Optional[str]], +] +VerifyTypes = Union[str, bool, ssl.SSLContext] +TimeoutTypes = Union[ + Optional[float], + Tuple[Optional[float], Optional[float], Optional[float], Optional[float]], + "Timeout", +] +ProxyTypes = Union[URLTypes, "Proxy"] +ProxiesTypes = Union[ProxyTypes, Dict[URLTypes, Union[None, ProxyTypes]]] + +AuthTypes = Union[ + Tuple[Union[str, bytes], Union[str, bytes]], + Callable[["Request"], "Request"], + "Auth", +] + +RequestContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] +ResponseContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] +ResponseExtensions = MutableMapping[str, Any] + +RequestData = Mapping[str, Any] + +FileContent = Union[IO[bytes], bytes, str] +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +RequestExtensions = MutableMapping[str, Any] + + +class SyncByteStream: + def __iter__(self) -> Iterator[bytes]: + raise NotImplementedError( + "The '__iter__' method must be implemented." + ) # pragma: no cover + yield b"" # pragma: no cover + + def close(self) -> None: + """ + Subclasses can override this method to release any network resources + after a request/response cycle is complete. + """ + + +class AsyncByteStream: + async def __aiter__(self) -> AsyncIterator[bytes]: + raise NotImplementedError( + "The '__aiter__' method must be implemented." + ) # pragma: no cover + yield b"" # pragma: no cover + + async def aclose(self) -> None: + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_urlparse.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_urlparse.py new file mode 100644 index 0000000..6a4b55b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_urlparse.py @@ -0,0 +1,502 @@ +""" +An implementation of `urlparse` that provides URL validation and normalization +as described by RFC3986. + +We rely on this implementation rather than the one in Python's stdlib, because: + +* It provides more complete URL validation. +* It properly differentiates between an empty querystring and an absent querystring, + to distinguish URLs with a trailing '?'. +* It handles scheme, hostname, port, and path normalization. +* It supports IDNA hostnames, normalizing them to their encoded form. +* The API supports passing individual components, as well as the complete URL string. + +Previously we relied on the excellent `rfc3986` package to handle URL parsing and +validation, but this module provides a simpler alternative, with less indirection +required. +""" +from __future__ import annotations + +import ipaddress +import re +import typing + +import idna + +from ._exceptions import InvalidURL + +MAX_URL_LENGTH = 65536 + +# https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3 +UNRESERVED_CHARACTERS = ( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" +) +SUB_DELIMS = "!$&'()*+,;=" + +PERCENT_ENCODED_REGEX = re.compile("%[A-Fa-f0-9]{2}") + + +# {scheme}: (optional) +# //{authority} (optional) +# {path} +# ?{query} (optional) +# #{fragment} (optional) +URL_REGEX = re.compile( + ( + r"(?:(?P{scheme}):)?" + r"(?://(?P{authority}))?" + r"(?P{path})" + r"(?:\?(?P{query}))?" + r"(?:#(?P{fragment}))?" + ).format( + scheme="([a-zA-Z][a-zA-Z0-9+.-]*)?", + authority="[^/?#]*", + path="[^?#]*", + query="[^#]*", + fragment=".*", + ) +) + +# {userinfo}@ (optional) +# {host} +# :{port} (optional) +AUTHORITY_REGEX = re.compile( + ( + r"(?:(?P{userinfo})@)?" r"(?P{host})" r":?(?P{port})?" + ).format( + userinfo=".*", # Any character sequence. + host="(\\[.*\\]|[^:@]*)", # Either any character sequence excluding ':' or '@', + # or an IPv6 address enclosed within square brackets. + port=".*", # Any character sequence. + ) +) + + +# If we call urlparse with an individual component, then we need to regex +# validate that component individually. +# Note that we're duplicating the same strings as above. Shock! Horror!! +COMPONENT_REGEX = { + "scheme": re.compile("([a-zA-Z][a-zA-Z0-9+.-]*)?"), + "authority": re.compile("[^/?#]*"), + "path": re.compile("[^?#]*"), + "query": re.compile("[^#]*"), + "fragment": re.compile(".*"), + "userinfo": re.compile("[^@]*"), + "host": re.compile("(\\[.*\\]|[^:]*)"), + "port": re.compile(".*"), +} + + +# We use these simple regexs as a first pass before handing off to +# the stdlib 'ipaddress' module for IP address validation. +IPv4_STYLE_HOSTNAME = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$") +IPv6_STYLE_HOSTNAME = re.compile(r"^\[.*\]$") + + +class ParseResult(typing.NamedTuple): + scheme: str + userinfo: str + host: str + port: int | None + path: str + query: str | None + fragment: str | None + + @property + def authority(self) -> str: + return "".join( + [ + f"{self.userinfo}@" if self.userinfo else "", + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + @property + def netloc(self) -> str: + return "".join( + [ + f"[{self.host}]" if ":" in self.host else self.host, + f":{self.port}" if self.port is not None else "", + ] + ) + + def copy_with(self, **kwargs: str | None) -> ParseResult: + if not kwargs: + return self + + defaults = { + "scheme": self.scheme, + "authority": self.authority, + "path": self.path, + "query": self.query, + "fragment": self.fragment, + } + defaults.update(kwargs) + return urlparse("", **defaults) + + def __str__(self) -> str: + authority = self.authority + return "".join( + [ + f"{self.scheme}:" if self.scheme else "", + f"//{authority}" if authority else "", + self.path, + f"?{self.query}" if self.query is not None else "", + f"#{self.fragment}" if self.fragment is not None else "", + ] + ) + + +def urlparse(url: str = "", **kwargs: str | None) -> ParseResult: + # Initial basic checks on allowable URLs. + # --------------------------------------- + + # Hard limit the maximum allowable URL length. + if len(url) > MAX_URL_LENGTH: + raise InvalidURL("URL too long") + + # If a URL includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in url): + raise InvalidURL("Invalid non-printable ASCII character in URL") + + # Some keyword arguments require special handling. + # ------------------------------------------------ + + # Coerce "port" to a string, if it is provided as an integer. + if "port" in kwargs: + port = kwargs["port"] + kwargs["port"] = str(port) if isinstance(port, int) else port + + # Replace "netloc" with "host and "port". + if "netloc" in kwargs: + netloc = kwargs.pop("netloc") or "" + kwargs["host"], _, kwargs["port"] = netloc.partition(":") + + # Replace "username" and/or "password" with "userinfo". + if "username" in kwargs or "password" in kwargs: + username = quote(kwargs.pop("username", "") or "") + password = quote(kwargs.pop("password", "") or "") + kwargs["userinfo"] = f"{username}:{password}" if password else username + + # Replace "raw_path" with "path" and "query". + if "raw_path" in kwargs: + raw_path = kwargs.pop("raw_path") or "" + kwargs["path"], seperator, kwargs["query"] = raw_path.partition("?") + if not seperator: + kwargs["query"] = None + + # Ensure that IPv6 "host" addresses are always escaped with "[...]". + if "host" in kwargs: + host = kwargs.get("host") or "" + if ":" in host and not (host.startswith("[") and host.endswith("]")): + kwargs["host"] = f"[{host}]" + + # If any keyword arguments are provided, ensure they are valid. + # ------------------------------------------------------------- + + for key, value in kwargs.items(): + if value is not None: + if len(value) > MAX_URL_LENGTH: + raise InvalidURL(f"URL component '{key}' too long") + + # If a component includes any ASCII control characters including \t, \r, \n, + # then treat it as invalid. + if any(char.isascii() and not char.isprintable() for char in value): + raise InvalidURL( + f"Invalid non-printable ASCII character in URL component '{key}'" + ) + + # Ensure that keyword arguments match as a valid regex. + if not COMPONENT_REGEX[key].fullmatch(value): + raise InvalidURL(f"Invalid URL component '{key}'") + + # The URL_REGEX will always match, but may have empty components. + url_match = URL_REGEX.match(url) + assert url_match is not None + url_dict = url_match.groupdict() + + # * 'scheme', 'authority', and 'path' may be empty strings. + # * 'query' may be 'None', indicating no trailing "?" portion. + # Any string including the empty string, indicates a trailing "?". + # * 'fragment' may be 'None', indicating no trailing "#" portion. + # Any string including the empty string, indicates a trailing "#". + scheme = kwargs.get("scheme", url_dict["scheme"]) or "" + authority = kwargs.get("authority", url_dict["authority"]) or "" + path = kwargs.get("path", url_dict["path"]) or "" + query = kwargs.get("query", url_dict["query"]) + fragment = kwargs.get("fragment", url_dict["fragment"]) + + # The AUTHORITY_REGEX will always match, but may have empty components. + authority_match = AUTHORITY_REGEX.match(authority) + assert authority_match is not None + authority_dict = authority_match.groupdict() + + # * 'userinfo' and 'host' may be empty strings. + # * 'port' may be 'None'. + userinfo = kwargs.get("userinfo", authority_dict["userinfo"]) or "" + host = kwargs.get("host", authority_dict["host"]) or "" + port = kwargs.get("port", authority_dict["port"]) + + # Normalize and validate each component. + # We end up with a parsed representation of the URL, + # with components that are plain ASCII bytestrings. + parsed_scheme: str = scheme.lower() + parsed_userinfo: str = quote(userinfo, safe=SUB_DELIMS + ":") + parsed_host: str = encode_host(host) + parsed_port: int | None = normalize_port(port, scheme) + + has_scheme = parsed_scheme != "" + has_authority = ( + parsed_userinfo != "" or parsed_host != "" or parsed_port is not None + ) + validate_path(path, has_scheme=has_scheme, has_authority=has_authority) + if has_authority: + path = normalize_path(path) + + # The GEN_DELIMS set is... : / ? # [ ] @ + # These do not need to be percent-quoted unless they serve as delimiters for the + # specific component. + + # For 'path' we need to drop ? and # from the GEN_DELIMS set. + parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@") + # For 'query' we need to drop '#' from the GEN_DELIMS set. + parsed_query: str | None = ( + None if query is None else quote(query, safe=SUB_DELIMS + ":/?[]@") + ) + # For 'fragment' we can include all of the GEN_DELIMS set. + parsed_fragment: str | None = ( + None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@") + ) + + # The parsed ASCII bytestrings are our canonical form. + # All properties of the URL are derived from these. + return ParseResult( + parsed_scheme, + parsed_userinfo, + parsed_host, + parsed_port, + parsed_path, + parsed_query, + parsed_fragment, + ) + + +def encode_host(host: str) -> str: + if not host: + return "" + + elif IPv4_STYLE_HOSTNAME.match(host): + # Validate IPv4 hostnames like #.#.#.# + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + try: + ipaddress.IPv4Address(host) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv4 address: {host!r}") + return host + + elif IPv6_STYLE_HOSTNAME.match(host): + # Validate IPv6 hostnames like [...] + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # "A host identified by an Internet Protocol literal address, version 6 + # [RFC3513] or later, is distinguished by enclosing the IP literal + # within square brackets ("[" and "]"). This is the only place where + # square bracket characters are allowed in the URI syntax." + try: + ipaddress.IPv6Address(host[1:-1]) + except ipaddress.AddressValueError: + raise InvalidURL(f"Invalid IPv6 address: {host!r}") + return host[1:-1] + + elif host.isascii(): + # Regular ASCII hostnames + # + # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2 + # + # reg-name = *( unreserved / pct-encoded / sub-delims ) + return quote(host.lower(), safe=SUB_DELIMS) + + # IDNA hostnames + try: + return idna.encode(host.lower()).decode("ascii") + except idna.IDNAError: + raise InvalidURL(f"Invalid IDNA hostname: {host!r}") + + +def normalize_port(port: str | int | None, scheme: str) -> int | None: + # From https://tools.ietf.org/html/rfc3986#section-3.2.3 + # + # "A scheme may define a default port. For example, the "http" scheme + # defines a default port of "80", corresponding to its reserved TCP + # port number. The type of port designated by the port number (e.g., + # TCP, UDP, SCTP) is defined by the URI scheme. URI producers and + # normalizers should omit the port component and its ":" delimiter if + # port is empty or if its value would be the same as that of the + # scheme's default." + if port is None or port == "": + return None + + try: + port_as_int = int(port) + except ValueError: + raise InvalidURL(f"Invalid port: {port!r}") + + # See https://url.spec.whatwg.org/#url-miscellaneous + default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get( + scheme + ) + if port_as_int == default_port: + return None + return port_as_int + + +def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None: + """ + Path validation rules that depend on if the URL contains + a scheme or authority component. + + See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3 + """ + if has_authority: + # If a URI contains an authority component, then the path component + # must either be empty or begin with a slash ("/") character." + if path and not path.startswith("/"): + raise InvalidURL("For absolute URLs, path must be empty or begin with '/'") + else: + # If a URI does not contain an authority component, then the path cannot begin + # with two slash characters ("//"). + if path.startswith("//"): + raise InvalidURL( + "URLs with no authority component cannot have a path starting with '//'" + ) + # In addition, a URI reference (Section 4.1) may be a relative-path reference, + # in which case the first path segment cannot contain a colon (":") character. + if path.startswith(":") and not has_scheme: + raise InvalidURL( + "URLs with no scheme component cannot have a path starting with ':'" + ) + + +def normalize_path(path: str) -> str: + """ + Drop "." and ".." segments from a URL path. + + For example: + + normalize_path("/path/./to/somewhere/..") == "/path/to" + """ + # https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 + components = path.split("/") + output: list[str] = [] + for component in components: + if component == ".": + pass + elif component == "..": + if output and output != [""]: + output.pop() + else: + output.append(component) + return "/".join(output) + + +def percent_encode(char: str) -> str: + """ + Replace a single character with the percent-encoded representation. + + Characters outside the ASCII range are represented with their a percent-encoded + representation of their UTF-8 byte sequence. + + For example: + + percent_encode(" ") == "%20" + """ + return "".join([f"%{byte:02x}" for byte in char.encode("utf-8")]).upper() + + +def is_safe(string: str, safe: str = "/") -> bool: + """ + Determine if a given string is already quote-safe. + """ + NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe + "%" + + # All characters must already be non-escaping or '%' + for char in string: + if char not in NON_ESCAPED_CHARS: + return False + + return True + + +def percent_encoded(string: str, safe: str = "/") -> str: + """ + Use percent-encoding to quote a string. + """ + if is_safe(string, safe=safe): + return string + + NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe + return "".join( + [char if char in NON_ESCAPED_CHARS else percent_encode(char) for char in string] + ) + + +def quote(string: str, safe: str = "/") -> str: + """ + Use percent-encoding to quote a string, omitting existing '%xx' escape sequences. + + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.1 + + * `string`: The string to be percent-escaped. + * `safe`: A string containing characters that may be treated as safe, and do not + need to be escaped. Unreserved characters are always treated as safe. + See: https://www.rfc-editor.org/rfc/rfc3986#section-2.3 + """ + parts = [] + current_position = 0 + for match in re.finditer(PERCENT_ENCODED_REGEX, string): + start_position, end_position = match.start(), match.end() + matched_text = match.group(0) + # Add any text up to the '%xx' escape sequence. + if start_position != current_position: + leading_text = string[current_position:start_position] + parts.append(percent_encoded(leading_text, safe=safe)) + + # Add the '%xx' escape sequence. + parts.append(matched_text) + current_position = end_position + + # Add any text after the final '%xx' escape sequence. + if current_position != len(string): + trailing_text = string[current_position:] + parts.append(percent_encoded(trailing_text, safe=safe)) + + return "".join(parts) + + +def urlencode(items: list[tuple[str, str]]) -> str: + """ + We can use a much simpler version of the stdlib urlencode here because + we don't need to handle a bunch of different typing cases, such as bytes vs str. + + https://github.com/python/cpython/blob/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb/Lib/urllib/parse.py#L926 + + Note that we use '%20' encoding for spaces. and '%2F for '/'. + This is slightly different than `requests`, but is the behaviour that browsers use. + + See + - https://github.com/encode/httpx/issues/2536 + - https://github.com/encode/httpx/issues/2721 + - https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode + """ + return "&".join( + [ + percent_encoded(k, safe="") + "=" + percent_encoded(v, safe="") + for k, v in items + ] + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_urls.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_urls.py new file mode 100644 index 0000000..43dedd5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_urls.py @@ -0,0 +1,646 @@ +from __future__ import annotations + +import typing +from urllib.parse import parse_qs, unquote + +import idna + +from ._types import QueryParamTypes, RawURL, URLTypes +from ._urlparse import urlencode, urlparse +from ._utils import primitive_value_to_str + + +class URL: + """ + url = httpx.URL("HTTPS://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink") + + assert url.scheme == "https" + assert url.username == "jo@email.com" + assert url.password == "a secret" + assert url.userinfo == b"jo%40email.com:a%20secret" + assert url.host == "müller.de" + assert url.raw_host == b"xn--mller-kva.de" + assert url.port == 1234 + assert url.netloc == b"xn--mller-kva.de:1234" + assert url.path == "/pa th" + assert url.query == b"?search=ab" + assert url.raw_path == b"/pa%20th?search=ab" + assert url.fragment == "anchorlink" + + The components of a URL are broken down like this: + + https://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink + [scheme] [ username ] [password] [ host ][port][ path ] [ query ] [fragment] + [ userinfo ] [ netloc ][ raw_path ] + + Note that: + + * `url.scheme` is normalized to always be lowercased. + + * `url.host` is normalized to always be lowercased. Internationalized domain + names are represented in unicode, without IDNA encoding applied. For instance: + + url = httpx.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + * `url.raw_host` is normalized to always be lowercased, and is IDNA encoded. + + url = httpx.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + * `url.port` is either None or an integer. URLs that include the default port for + "http", "https", "ws", "wss", and "ftp" schemes have their port + normalized to `None`. + + assert httpx.URL("http://example.com") == httpx.URL("http://example.com:80") + assert httpx.URL("http://example.com").port is None + assert httpx.URL("http://example.com:80").port is None + + * `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work + with `url.username` and `url.password` instead, which handle the URL escaping. + + * `url.raw_path` is raw bytes of both the path and query, without URL escaping. + This portion is used as the target when constructing HTTP requests. Usually you'll + want to work with `url.path` instead. + + * `url.query` is raw bytes, without URL escaping. A URL query string portion can + only be properly URL escaped when decoding the parameter names and values + themselves. + """ + + def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None: + if kwargs: + allowed = { + "scheme": str, + "username": str, + "password": str, + "userinfo": bytes, + "host": str, + "port": int, + "netloc": bytes, + "path": str, + "query": bytes, + "raw_path": bytes, + "fragment": str, + "params": object, + } + + # Perform type checking for all supported keyword arguments. + for key, value in kwargs.items(): + if key not in allowed: + message = f"{key!r} is an invalid keyword argument for URL()" + raise TypeError(message) + if value is not None and not isinstance(value, allowed[key]): + expected = allowed[key].__name__ + seen = type(value).__name__ + message = f"Argument {key!r} must be {expected} but got {seen}" + raise TypeError(message) + if isinstance(value, bytes): + kwargs[key] = value.decode("ascii") + + if "params" in kwargs: + # Replace any "params" keyword with the raw "query" instead. + # + # Ensure that empty params use `kwargs["query"] = None` rather + # than `kwargs["query"] = ""`, so that generated URLs do not + # include an empty trailing "?". + params = kwargs.pop("params") + kwargs["query"] = None if not params else str(QueryParams(params)) + + if isinstance(url, str): + self._uri_reference = urlparse(url, **kwargs) + elif isinstance(url, URL): + self._uri_reference = url._uri_reference.copy_with(**kwargs) + else: + raise TypeError( + "Invalid type for url. Expected str or httpx.URL," + f" got {type(url)}: {url!r}" + ) + + @property + def scheme(self) -> str: + """ + The URL scheme, such as "http", "https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme + + @property + def raw_scheme(self) -> bytes: + """ + The raw bytes representation of the URL scheme, such as b"http", b"https". + Always normalised to lowercase. + """ + return self._uri_reference.scheme.encode("ascii") + + @property + def userinfo(self) -> bytes: + """ + The URL userinfo as a raw bytestring. + For example: b"jo%40email.com:a%20secret". + """ + return self._uri_reference.userinfo.encode("ascii") + + @property + def username(self) -> str: + """ + The URL username as a string, with URL decoding applied. + For example: "jo@email.com" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[0]) + + @property + def password(self) -> str: + """ + The URL password as a string, with URL decoding applied. + For example: "a secret" + """ + userinfo = self._uri_reference.userinfo + return unquote(userinfo.partition(":")[2]) + + @property + def host(self) -> str: + """ + The URL host as a string. + Always normalized to lowercase, with IDNA hosts decoded into unicode. + + Examples: + + url = httpx.URL("http://www.EXAMPLE.org") + assert url.host == "www.example.org" + + url = httpx.URL("http://中国.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.host == "中国.icom.museum" + + url = httpx.URL("https://[::ffff:192.168.0.1]") + assert url.host == "::ffff:192.168.0.1" + """ + host: str = self._uri_reference.host + + if host.startswith("xn--"): + host = idna.decode(host) + + return host + + @property + def raw_host(self) -> bytes: + """ + The raw bytes representation of the URL host. + Always normalized to lowercase, and IDNA encoded. + + Examples: + + url = httpx.URL("http://www.EXAMPLE.org") + assert url.raw_host == b"www.example.org" + + url = httpx.URL("http://中国.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx.URL("http://xn--fiqs8s.icom.museum") + assert url.raw_host == b"xn--fiqs8s.icom.museum" + + url = httpx.URL("https://[::ffff:192.168.0.1]") + assert url.raw_host == b"::ffff:192.168.0.1" + """ + return self._uri_reference.host.encode("ascii") + + @property + def port(self) -> int | None: + """ + The URL port as an integer. + + Note that the URL class performs port normalization as per the WHATWG spec. + Default ports for "http", "https", "ws", "wss", and "ftp" schemes are always + treated as `None`. + + For example: + + assert httpx.URL("http://www.example.com") == httpx.URL("http://www.example.com:80") + assert httpx.URL("http://www.example.com:80").port is None + """ + return self._uri_reference.port + + @property + def netloc(self) -> bytes: + """ + Either `` or `:` as bytes. + Always normalized to lowercase, and IDNA encoded. + + This property may be used for generating the value of a request + "Host" header. + """ + return self._uri_reference.netloc.encode("ascii") + + @property + def path(self) -> str: + """ + The URL path as a string. Excluding the query string, and URL decoded. + + For example: + + url = httpx.URL("https://example.com/pa%20th") + assert url.path == "/pa th" + """ + path = self._uri_reference.path or "/" + return unquote(path) + + @property + def query(self) -> bytes: + """ + The URL query string, as raw bytes, excluding the leading b"?". + + This is necessarily a bytewise interface, because we cannot + perform URL decoding of this representation until we've parsed + the keys and values into a QueryParams instance. + + For example: + + url = httpx.URL("https://example.com/?filter=some%20search%20terms") + assert url.query == b"filter=some%20search%20terms" + """ + query = self._uri_reference.query or "" + return query.encode("ascii") + + @property + def params(self) -> QueryParams: + """ + The URL query parameters, neatly parsed and packaged into an immutable + multidict representation. + """ + return QueryParams(self._uri_reference.query) + + @property + def raw_path(self) -> bytes: + """ + The complete URL path and query string as raw bytes. + Used as the target when constructing HTTP requests. + + For example: + + GET /users?search=some%20text HTTP/1.1 + Host: www.example.org + Connection: close + """ + path = self._uri_reference.path or "/" + if self._uri_reference.query is not None: + path += "?" + self._uri_reference.query + return path.encode("ascii") + + @property + def fragment(self) -> str: + """ + The URL fragments, as used in HTML anchors. + As a string, without the leading '#'. + """ + return unquote(self._uri_reference.fragment or "") + + @property + def raw(self) -> RawURL: + """ + Provides the (scheme, host, port, target) for the outgoing request. + + In older versions of `httpx` this was used in the low-level transport API. + We no longer use `RawURL`, and this property will be deprecated + in a future release. + """ + return RawURL( + self.raw_scheme, + self.raw_host, + self.port, + self.raw_path, + ) + + @property + def is_absolute_url(self) -> bool: + """ + Return `True` for absolute URLs such as 'http://example.com/path', + and `False` for relative URLs such as '/path'. + """ + # We don't use `.is_absolute` from `rfc3986` because it treats + # URLs with a fragment portion as not absolute. + # What we actually care about is if the URL provides + # a scheme and hostname to which connections should be made. + return bool(self._uri_reference.scheme and self._uri_reference.host) + + @property + def is_relative_url(self) -> bool: + """ + Return `False` for absolute URLs such as 'http://example.com/path', + and `True` for relative URLs such as '/path'. + """ + return not self.is_absolute_url + + def copy_with(self, **kwargs: typing.Any) -> URL: + """ + Copy this URL, returning a new URL with some components altered. + Accepts the same set of parameters as the components that are made + available via properties on the `URL` class. + + For example: + + url = httpx.URL("https://www.example.com").copy_with( + username="jo@gmail.com", password="a secret" + ) + assert url == "https://jo%40email.com:a%20secret@www.example.com" + """ + return URL(self, **kwargs) + + def copy_set_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.set(key, value)) + + def copy_add_param(self, key: str, value: typing.Any = None) -> URL: + return self.copy_with(params=self.params.add(key, value)) + + def copy_remove_param(self, key: str) -> URL: + return self.copy_with(params=self.params.remove(key)) + + def copy_merge_params(self, params: QueryParamTypes) -> URL: + return self.copy_with(params=self.params.merge(params)) + + def join(self, url: URLTypes) -> URL: + """ + Return an absolute URL, using this URL as the base. + + Eg. + + url = httpx.URL("https://www.example.com/test") + url = url.join("/new/path") + assert url == "https://www.example.com/new/path" + """ + from urllib.parse import urljoin + + return URL(urljoin(str(self), str(URL(url)))) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, (URL, str)) and str(self) == str(URL(other)) + + def __str__(self) -> str: + return str(self._uri_reference) + + def __repr__(self) -> str: + scheme, userinfo, host, port, path, query, fragment = self._uri_reference + + if ":" in userinfo: + # Mask any password component. + userinfo = f'{userinfo.split(":")[0]}:[secure]' + + authority = "".join( + [ + f"{userinfo}@" if userinfo else "", + f"[{host}]" if ":" in host else host, + f":{port}" if port is not None else "", + ] + ) + url = "".join( + [ + f"{self.scheme}:" if scheme else "", + f"//{authority}" if authority else "", + path, + f"?{query}" if query is not None else "", + f"#{fragment}" if fragment is not None else "", + ] + ) + + return f"{self.__class__.__name__}({url!r})" + + +class QueryParams(typing.Mapping[str, str]): + """ + URL query parameters, as a multi-dict. + """ + + def __init__(self, *args: QueryParamTypes | None, **kwargs: typing.Any) -> None: + assert len(args) < 2, "Too many arguments." + assert not (args and kwargs), "Cannot mix named and unnamed arguments." + + value = args[0] if args else kwargs + + if value is None or isinstance(value, (str, bytes)): + value = value.decode("ascii") if isinstance(value, bytes) else value + self._dict = parse_qs(value, keep_blank_values=True) + elif isinstance(value, QueryParams): + self._dict = {k: list(v) for k, v in value._dict.items()} + else: + dict_value: dict[typing.Any, list[typing.Any]] = {} + if isinstance(value, (list, tuple)): + # Convert list inputs like: + # [("a", "123"), ("a", "456"), ("b", "789")] + # To a dict representation, like: + # {"a": ["123", "456"], "b": ["789"]} + for item in value: + dict_value.setdefault(item[0], []).append(item[1]) + else: + # Convert dict inputs like: + # {"a": "123", "b": ["456", "789"]} + # To dict inputs where values are always lists, like: + # {"a": ["123"], "b": ["456", "789"]} + dict_value = { + k: list(v) if isinstance(v, (list, tuple)) else [v] + for k, v in value.items() + } + + # Ensure that keys and values are neatly coerced to strings. + # We coerce values `True` and `False` to JSON-like "true" and "false" + # representations, and coerce `None` values to the empty string. + self._dict = { + str(k): [primitive_value_to_str(item) for item in v] + for k, v in dict_value.items() + } + + def keys(self) -> typing.KeysView[str]: + """ + Return all the keys in the query params. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.keys()) == ["a", "b"] + """ + return self._dict.keys() + + def values(self) -> typing.ValuesView[str]: + """ + Return all the values in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.values()) == ["123", "789"] + """ + return {k: v[0] for k, v in self._dict.items()}.values() + + def items(self) -> typing.ItemsView[str, str]: + """ + Return all items in the query params. If a key occurs more than once + only the first item for that key is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.items()) == [("a", "123"), ("b", "789")] + """ + return {k: v[0] for k, v in self._dict.items()}.items() + + def multi_items(self) -> list[tuple[str, str]]: + """ + Return all items in the query params. Allow duplicate keys to occur. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert list(q.multi_items()) == [("a", "123"), ("a", "456"), ("b", "789")] + """ + multi_items: list[tuple[str, str]] = [] + for k, v in self._dict.items(): + multi_items.extend([(k, i) for i in v]) + return multi_items + + def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any: + """ + Get a value from the query param for a given key. If the key occurs + more than once, then only the first value is returned. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert q.get("a") == "123" + """ + if key in self._dict: + return self._dict[str(key)][0] + return default + + def get_list(self, key: str) -> list[str]: + """ + Get all values from the query param for a given key. + + Usage: + + q = httpx.QueryParams("a=123&a=456&b=789") + assert q.get_list("a") == ["123", "456"] + """ + return list(self._dict.get(str(key), [])) + + def set(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.set("a", "456") + assert q == httpx.QueryParams("a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = [primitive_value_to_str(value)] + return q + + def add(self, key: str, value: typing.Any = None) -> QueryParams: + """ + Return a new QueryParams instance, setting or appending the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.add("a", "456") + assert q == httpx.QueryParams("a=123&a=456") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict[str(key)] = q.get_list(key) + [primitive_value_to_str(value)] + return q + + def remove(self, key: str) -> QueryParams: + """ + Return a new QueryParams instance, removing the value of a key. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.remove("a") + assert q == httpx.QueryParams("") + """ + q = QueryParams() + q._dict = dict(self._dict) + q._dict.pop(str(key), None) + return q + + def merge(self, params: QueryParamTypes | None = None) -> QueryParams: + """ + Return a new QueryParams instance, updated with. + + Usage: + + q = httpx.QueryParams("a=123") + q = q.merge({"b": "456"}) + assert q == httpx.QueryParams("a=123&b=456") + + q = httpx.QueryParams("a=123") + q = q.merge({"a": "456", "b": "789"}) + assert q == httpx.QueryParams("a=456&b=789") + """ + q = QueryParams(params) + q._dict = {**self._dict, **q._dict} + return q + + def __getitem__(self, key: typing.Any) -> str: + return self._dict[key][0] + + def __contains__(self, key: typing.Any) -> bool: + return key in self._dict + + def __iter__(self) -> typing.Iterator[typing.Any]: + return iter(self.keys()) + + def __len__(self) -> int: + return len(self._dict) + + def __bool__(self) -> bool: + return bool(self._dict) + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: typing.Any) -> bool: + if not isinstance(other, self.__class__): + return False + return sorted(self.multi_items()) == sorted(other.multi_items()) + + def __str__(self) -> str: + """ + Note that we use '%20' encoding for spaces, and treat '/' as a safe + character. + + See https://github.com/encode/httpx/issues/2536 and + https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode + """ + return urlencode(self.multi_items()) + + def __repr__(self) -> str: + class_name = self.__class__.__name__ + query_string = str(self) + return f"{class_name}({query_string!r})" + + def update(self, params: QueryParamTypes | None = None) -> None: + raise RuntimeError( + "QueryParams are immutable since 0.18.0. " + "Use `q = q.merge(...)` to create an updated copy." + ) + + def __setitem__(self, key: str, value: str) -> None: + raise RuntimeError( + "QueryParams are immutable since 0.18.0. " + "Use `q = q.set(key, value)` to create an updated copy." + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/_utils.py b/.pythonlibs/lib/python3.10/site-packages/httpx/_utils.py new file mode 100644 index 0000000..a9ece19 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/httpx/_utils.py @@ -0,0 +1,440 @@ +from __future__ import annotations + +import codecs +import email.message +import ipaddress +import mimetypes +import os +import re +import time +import typing +from pathlib import Path +from urllib.request import getproxies + +import sniffio + +from ._types import PrimitiveData + +if typing.TYPE_CHECKING: # pragma: no cover + from ._urls import URL + + +_HTML5_FORM_ENCODING_REPLACEMENTS = {'"': "%22", "\\": "\\\\"} +_HTML5_FORM_ENCODING_REPLACEMENTS.update( + {chr(c): "%{:02X}".format(c) for c in range(0x1F + 1) if c != 0x1B} +) +_HTML5_FORM_ENCODING_RE = re.compile( + r"|".join([re.escape(c) for c in _HTML5_FORM_ENCODING_REPLACEMENTS.keys()]) +) + + +def normalize_header_key( + value: str | bytes, + lower: bool, + encoding: str | None = None, +) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header key. + """ + if isinstance(value, bytes): + bytes_value = value + else: + bytes_value = value.encode(encoding or "ascii") + + return bytes_value.lower() if lower else bytes_value + + +def normalize_header_value(value: str | bytes, encoding: str | None = None) -> bytes: + """ + Coerce str/bytes into a strictly byte-wise HTTP header value. + """ + if isinstance(value, bytes): + return value + return value.encode(encoding or "ascii") + + +def primitive_value_to_str(value: PrimitiveData) -> str: + """ + Coerce a primitive data type into a string value. + + Note that we prefer JSON-style 'true'/'false' for boolean values here. + """ + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +def is_known_encoding(encoding: str) -> bool: + """ + Return `True` if `encoding` is a known codec. + """ + try: + codecs.lookup(encoding) + except LookupError: + return False + return True + + +def format_form_param(name: str, value: str) -> bytes: + """ + Encode a name/value pair within a multipart form. + """ + + def replacer(match: typing.Match[str]) -> str: + return _HTML5_FORM_ENCODING_REPLACEMENTS[match.group(0)] + + value = _HTML5_FORM_ENCODING_RE.sub(replacer, value) + return f'{name}="{value}"'.encode() + + +def get_ca_bundle_from_env() -> str | None: + if "SSL_CERT_FILE" in os.environ: + ssl_file = Path(os.environ["SSL_CERT_FILE"]) + if ssl_file.is_file(): + return str(ssl_file) + if "SSL_CERT_DIR" in os.environ: + ssl_path = Path(os.environ["SSL_CERT_DIR"]) + if ssl_path.is_dir(): + return str(ssl_path) + return None + + +def parse_header_links(value: str) -> list[dict[str, str]]: + """ + Returns a list of parsed link headers, for more info see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link + The generic syntax of those is: + Link: < uri-reference >; param1=value1; param2="value2" + So for instance: + Link; '; type="image/jpeg",;' + would return + [ + {"url": "http:/.../front.jpeg", "type": "image/jpeg"}, + {"url": "http://.../back.jpeg"}, + ] + :param value: HTTP Link entity-header field + :return: list of parsed link headers + """ + links: list[dict[str, str]] = [] + replace_chars = " '\"" + value = value.strip(replace_chars) + if not value: + return links + for val in re.split(", *<", value): + try: + url, params = val.split(";", 1) + except ValueError: + url, params = val, "" + link = {"url": url.strip("<> '\"")} + for param in params.split(";"): + try: + key, value = param.split("=") + except ValueError: + break + link[key.strip(replace_chars)] = value.strip(replace_chars) + links.append(link) + return links + + +def parse_content_type_charset(content_type: str) -> str | None: + # We used to use `cgi.parse_header()` here, but `cgi` became a dead battery. + # See: https://peps.python.org/pep-0594/#cgi + msg = email.message.Message() + msg["content-type"] = content_type + return msg.get_content_charset(failobj=None) + + +SENSITIVE_HEADERS = {"authorization", "proxy-authorization"} + + +def obfuscate_sensitive_headers( + items: typing.Iterable[tuple[typing.AnyStr, typing.AnyStr]], +) -> typing.Iterator[tuple[typing.AnyStr, typing.AnyStr]]: + for k, v in items: + if to_str(k.lower()) in SENSITIVE_HEADERS: + v = to_bytes_or_str("[secure]", match_type_of=v) + yield k, v + + +def port_or_default(url: URL) -> int | None: + if url.port is not None: + return url.port + return {"http": 80, "https": 443}.get(url.scheme) + + +def same_origin(url: URL, other: URL) -> bool: + """ + Return 'True' if the given URLs share the same origin. + """ + return ( + url.scheme == other.scheme + and url.host == other.host + and port_or_default(url) == port_or_default(other) + ) + + +def is_https_redirect(url: URL, location: URL) -> bool: + """ + Return 'True' if 'location' is a HTTPS upgrade of 'url' + """ + if url.host != location.host: + return False + + return ( + url.scheme == "http" + and port_or_default(url) == 80 + and location.scheme == "https" + and port_or_default(location) == 443 + ) + + +def get_environment_proxies() -> dict[str, str | None]: + """Gets proxy information from the environment""" + + # urllib.request.getproxies() falls back on System + # Registry and Config for proxies on Windows and macOS. + # We don't want to propagate non-HTTP proxies into + # our configuration such as 'TRAVIS_APT_PROXY'. + proxy_info = getproxies() + mounts: dict[str, str | None] = {} + + for scheme in ("http", "https", "all"): + if proxy_info.get(scheme): + hostname = proxy_info[scheme] + mounts[f"{scheme}://"] = ( + hostname if "://" in hostname else f"http://{hostname}" + ) + + no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] + for hostname in no_proxy_hosts: + # See https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html for details + # on how names in `NO_PROXY` are handled. + if hostname == "*": + # If NO_PROXY=* is used or if "*" occurs as any one of the comma + # separated hostnames, then we should just bypass any information + # from HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and always ignore + # proxies. + return {} + elif hostname: + # NO_PROXY=.google.com is marked as "all://*.google.com, + # which disables "www.google.com" but not "google.com" + # NO_PROXY=google.com is marked as "all://*google.com, + # which disables "www.google.com" and "google.com". + # (But not "wwwgoogle.com") + # NO_PROXY can include domains, IPv6, IPv4 addresses and "localhost" + # NO_PROXY=example.com,::1,localhost,192.168.0.0/16 + if "://" in hostname: + mounts[hostname] = None + elif is_ipv4_hostname(hostname): + mounts[f"all://{hostname}"] = None + elif is_ipv6_hostname(hostname): + mounts[f"all://[{hostname}]"] = None + elif hostname.lower() == "localhost": + mounts[f"all://{hostname}"] = None + else: + mounts[f"all://*{hostname}"] = None + + return mounts + + +def to_bytes(value: str | bytes, encoding: str = "utf-8") -> bytes: + return value.encode(encoding) if isinstance(value, str) else value + + +def to_str(value: str | bytes, encoding: str = "utf-8") -> str: + return value if isinstance(value, str) else value.decode(encoding) + + +def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing.AnyStr: + return value if isinstance(match_type_of, str) else value.encode() + + +def unquote(value: str) -> str: + return value[1:-1] if value[0] == value[-1] == '"' else value + + +def guess_content_type(filename: str | None) -> str | None: + if filename: + return mimetypes.guess_type(filename)[0] or "application/octet-stream" + return None + + +def peek_filelike_length(stream: typing.Any) -> int | None: + """ + Given a file-like stream object, return its length in number of bytes + without reading it into memory. + """ + try: + # Is it an actual file? + fd = stream.fileno() + # Yup, seems to be an actual file. + length = os.fstat(fd).st_size + except (AttributeError, OSError): + # No... Maybe it's something that supports random access, like `io.BytesIO`? + try: + # Assuming so, go to end of stream to figure out its length, + # then put it back in place. + offset = stream.tell() + length = stream.seek(0, os.SEEK_END) + stream.seek(offset) + except (AttributeError, OSError): + # Not even that? Sorry, we're doomed... + return None + + return length + + +class Timer: + async def _get_time(self) -> float: + library = sniffio.current_async_library() + if library == "trio": + import trio + + return trio.current_time() + else: + import asyncio + + return asyncio.get_event_loop().time() + + def sync_start(self) -> None: + self.started = time.perf_counter() + + async def async_start(self) -> None: + self.started = await self._get_time() + + def sync_elapsed(self) -> float: + now = time.perf_counter() + return now - self.started + + async def async_elapsed(self) -> float: + now = await self._get_time() + return now - self.started + + +class URLPattern: + """ + A utility class currently used for making lookups against proxy keys... + + # Wildcard matching... + >>> pattern = URLPattern("all://") + >>> pattern.matches(httpx.URL("http://example.com")) + True + + # Witch scheme matching... + >>> pattern = URLPattern("https://") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + False + + # With domain matching... + >>> pattern = URLPattern("https://example.com") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + False + >>> pattern.matches(httpx.URL("https://other.com")) + False + + # Wildcard scheme, with domain matching... + >>> pattern = URLPattern("all://example.com") + >>> pattern.matches(httpx.URL("https://example.com")) + True + >>> pattern.matches(httpx.URL("http://example.com")) + True + >>> pattern.matches(httpx.URL("https://other.com")) + False + + # With port matching... + >>> pattern = URLPattern("https://example.com:1234") + >>> pattern.matches(httpx.URL("https://example.com:1234")) + True + >>> pattern.matches(httpx.URL("https://example.com")) + False + """ + + def __init__(self, pattern: str) -> None: + from ._urls import URL + + if pattern and ":" not in pattern: + raise ValueError( + f"Proxy keys should use proper URL forms rather " + f"than plain scheme strings. " + f'Instead of "{pattern}", use "{pattern}://"' + ) + + url = URL(pattern) + self.pattern = pattern + self.scheme = "" if url.scheme == "all" else url.scheme + self.host = "" if url.host == "*" else url.host + self.port = url.port + if not url.host or url.host == "*": + self.host_regex: typing.Pattern[str] | None = None + elif url.host.startswith("*."): + # *.example.com should match "www.example.com", but not "example.com" + domain = re.escape(url.host[2:]) + self.host_regex = re.compile(f"^.+\\.{domain}$") + elif url.host.startswith("*"): + # *example.com should match "www.example.com" and "example.com" + domain = re.escape(url.host[1:]) + self.host_regex = re.compile(f"^(.+\\.)?{domain}$") + else: + # example.com should match "example.com" but not "www.example.com" + domain = re.escape(url.host) + self.host_regex = re.compile(f"^{domain}$") + + def matches(self, other: URL) -> bool: + if self.scheme and self.scheme != other.scheme: + return False + if ( + self.host + and self.host_regex is not None + and not self.host_regex.match(other.host) + ): + return False + if self.port is not None and self.port != other.port: + return False + return True + + @property + def priority(self) -> tuple[int, int, int]: + """ + The priority allows URLPattern instances to be sortable, so that + we can match from most specific to least specific. + """ + # URLs with a port should take priority over URLs without a port. + port_priority = 0 if self.port is not None else 1 + # Longer hostnames should match first. + host_priority = -len(self.host) + # Longer schemes should match first. + scheme_priority = -len(self.scheme) + return (port_priority, host_priority, scheme_priority) + + def __hash__(self) -> int: + return hash(self.pattern) + + def __lt__(self, other: URLPattern) -> bool: + return self.priority < other.priority + + def __eq__(self, other: typing.Any) -> bool: + return isinstance(other, URLPattern) and self.pattern == other.pattern + + +def is_ipv4_hostname(hostname: str) -> bool: + try: + ipaddress.IPv4Address(hostname.split("/")[0]) + except Exception: + return False + return True + + +def is_ipv6_hostname(hostname: str) -> bool: + try: + ipaddress.IPv6Address(hostname.split("/")[0]) + except Exception: + return False + return True diff --git a/.pythonlibs/lib/python3.10/site-packages/httpx/py.typed b/.pythonlibs/lib/python3.10/site-packages/httpx/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/LICENSE.md b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/LICENSE.md new file mode 100644 index 0000000..19b6b45 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2024, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/METADATA new file mode 100644 index 0000000..b28f6ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/METADATA @@ -0,0 +1,243 @@ +Metadata-Version: 2.1 +Name: idna +Version: 3.7 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.5 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for the Internationalized Domain Names in +Applications (IDNA) protocol as specified in `RFC 5891 +`_. This is the latest version of +the protocol and is sometimes referred to as “IDNA 2008”. + +This library also provides support for Unicode Technical +Standard 46, `Unicode IDNA Compatibility Processing +`_. + +This acts as a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports the older superseded IDNA specification (`RFC 3490 +`_). + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to A-labels or U-labels +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +You may use the codec encoding and decoding methods using the +``idna.codec`` module: + +.. code-block:: pycon + + >>> import idna.codec + >>> print('домен.испытание'.encode('idna2008')) + b'xn--d1acufc.xn--80akhbyknj4f' + >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) + домен.испытание + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +As described in `RFC 5895 `_, the +IDNA specification does not normalize input from different potential +ways a user may input a domain name. This functionality, known as +a “mapping”, is considered by the specification to be a local +user-interface issue distinct from IDNA conversion functionality. + +This library provides one such mapping that was developed by the +Unicode Consortium. Known as `Unicode IDNA Compatibility Processing +`_, it provides for both a regular +mapping for typical applications, as well as a transitional mapping to +help migrate from older IDNA 2003 applications. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + +Transitional processing provides conversions to help transition from +the older 2003 standard to the current standard. For example, in the +original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was +converted into two *LATIN SMALL LETTER S* (ss), whereas in the current +IDNA specification this conversion is not performed. + +.. code-block:: pycon + + >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) + 'xn--knigsgsschen-lcb0w' + +Implementers should use transitional processing with caution, only in +rare cases where conversion from legacy labels to current labels must be +performed (i.e. IDNA implementations that pre-date 2008). For typical +applications that just need to convert labels, transitional processing +is unlikely to be beneficial and could produce unexpected incompatible +results. + +``encodings.idna`` Compatibility +++++++++++++++++++++++++++++++++ + +Function calls from the Python built-in ``encodings.idna`` module are +mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. +Simply substitute the ``import`` clause in your code to refer to the new +module name. + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards. These tables are +computed using the command-line script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.5 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Removing support for older versions should be well justified in that the + maintenance burden has become too high. + +* **Python 2**. Python 2 is supported by version 2.x of this library. + While active development of the version 2.x series has ended, notable + issues being corrected may be backported to 2.x. Use "idna<3" in your + requirements file if you need this library for a Python 2 application. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + diff --git a/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/RECORD new file mode 100644 index 0000000..fac5229 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/RECORD @@ -0,0 +1,22 @@ +idna-3.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +idna-3.7.dist-info/LICENSE.md,sha256=pZ8LDvNjWHQQmkRhykT_enDVBpboFHZ7-vch1Mmw2w8,1541 +idna-3.7.dist-info/METADATA,sha256=OixCk-dKLZkPy-MfviOmiPvwJ1O2K_8rqCrFjC_uxy4,9888 +idna-3.7.dist-info/RECORD,, +idna-3.7.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849 +idna/__pycache__/__init__.cpython-310.pyc,, +idna/__pycache__/codec.cpython-310.pyc,, +idna/__pycache__/compat.cpython-310.pyc,, +idna/__pycache__/core.cpython-310.pyc,, +idna/__pycache__/idnadata.cpython-310.pyc,, +idna/__pycache__/intranges.cpython-310.pyc,, +idna/__pycache__/package_data.cpython-310.pyc,, +idna/__pycache__/uts46data.cpython-310.pyc,, +idna/codec.py,sha256=PS6m-XmdST7Wj7J7ulRMakPDt5EBJyYrT3CPtjh-7t4,3426 +idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321 +idna/core.py,sha256=lyhpoe2vulEaB_65xhXmoKgO-xUqFDvcwxu5hpNNO4E,12663 +idna/idnadata.py,sha256=dqRwytzkjIHMBa2R1lYvHDwACenZPt8eGVu1Y8UBE-E,78320 +idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881 +idna/package_data.py,sha256=Tkt0KnIeyIlnHddOaz9WSkkislNgokJAuE-p5GorMqo,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=1KuksWqLuccPXm2uyRVkhfiFLNIhM_H2m4azCcnOqEU,206503 diff --git a/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna-3.7.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__init__.py b/.pythonlibs/lib/python3.10/site-packages/idna/__init__.py new file mode 100644 index 0000000..a40eeaf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/__init__.py @@ -0,0 +1,44 @@ +from .package_data import __version__ +from .core import ( + IDNABidiError, + IDNAError, + InvalidCodepoint, + InvalidCodepointContext, + alabel, + check_bidi, + check_hyphen_ok, + check_initial_combiner, + check_label, + check_nfc, + decode, + encode, + ulabel, + uts46_remap, + valid_contextj, + valid_contexto, + valid_label_length, + valid_string_length, +) +from .intranges import intranges_contain + +__all__ = [ + "IDNABidiError", + "IDNAError", + "InvalidCodepoint", + "InvalidCodepointContext", + "alabel", + "check_bidi", + "check_hyphen_ok", + "check_initial_combiner", + "check_label", + "check_nfc", + "decode", + "encode", + "intranges_contain", + "ulabel", + "uts46_remap", + "valid_contextj", + "valid_contexto", + "valid_label_length", + "valid_string_length", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..da6e738 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc new file mode 100644 index 0000000..eb232cb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/codec.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc new file mode 100644 index 0000000..b51da5e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000..dbc4ca0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/core.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc new file mode 100644 index 0000000..6a38d67 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/idnadata.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc new file mode 100644 index 0000000..67e4515 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/intranges.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc new file mode 100644 index 0000000..fd8ad70 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/package_data.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc new file mode 100644 index 0000000..6b8309b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/idna/__pycache__/uts46data.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/codec.py b/.pythonlibs/lib/python3.10/site-packages/idna/codec.py new file mode 100644 index 0000000..c855a4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/codec.py @@ -0,0 +1,118 @@ +from .core import encode, decode, alabel, ulabel, IDNAError +import codecs +import re +from typing import Any, Tuple, Optional + +_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') + +class Codec(codecs.Codec): + + def encode(self, data: str, errors: str = 'strict') -> Tuple[bytes, int]: + if errors != 'strict': + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) + + if not data: + return b"", 0 + + return encode(data), len(data) + + def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]: + if errors != 'strict': + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) + + if not data: + return '', 0 + + return decode(data), len(data) + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: + if errors != 'strict': + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) + + if not data: + return b'', 0 + + labels = _unicode_dots_re.split(data) + trailing_dot = b'' + if labels: + if not labels[-1]: + trailing_dot = b'.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = b'.' + + result = [] + size = 0 + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result_bytes = b'.'.join(result) + trailing_dot + size += len(trailing_dot) + return result_bytes, size + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: + if errors != 'strict': + raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) + + if not data: + return ('', 0) + + if not isinstance(data, str): + data = str(data, 'ascii') + + labels = _unicode_dots_re.split(data) + trailing_dot = '' + if labels: + if not labels[-1]: + trailing_dot = '.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = '.' + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result_str = '.'.join(result) + trailing_dot + size += len(trailing_dot) + return (result_str, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +def search_function(name: str) -> Optional[codecs.CodecInfo]: + if name != 'idna2008': + return None + return codecs.CodecInfo( + name=name, + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) + +codecs.register(search_function) diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/compat.py b/.pythonlibs/lib/python3.10/site-packages/idna/compat.py new file mode 100644 index 0000000..786e6bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/compat.py @@ -0,0 +1,13 @@ +from .core import * +from .codec import * +from typing import Any, Union + +def ToASCII(label: str) -> bytes: + return encode(label) + +def ToUnicode(label: Union[bytes, bytearray]) -> str: + return decode(label) + +def nameprep(s: Any) -> None: + raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol') + diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/core.py b/.pythonlibs/lib/python3.10/site-packages/idna/core.py new file mode 100644 index 0000000..0dae61a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/core.py @@ -0,0 +1,395 @@ +from . import idnadata +import bisect +import unicodedata +import re +from typing import Union, Optional +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b'xn--' +_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') + +class IDNAError(UnicodeError): + """ Base exception for all IDNA-encoding related problems """ + pass + + +class IDNABidiError(IDNAError): + """ Exception when bidirectional requirements are not satisfied """ + pass + + +class InvalidCodepoint(IDNAError): + """ Exception when a disallowed or unallocated codepoint is used """ + pass + + +class InvalidCodepointContext(IDNAError): + """ Exception when the codepoint is not valid in the context it is used """ + pass + + +def _combining_class(cp: int) -> int: + v = unicodedata.combining(chr(cp)) + if v == 0: + if not unicodedata.name(chr(cp)): + raise ValueError('Unknown character in unicodedata') + return v + +def _is_script(cp: str, script: str) -> bool: + return intranges_contain(ord(cp), idnadata.scripts[script]) + +def _punycode(s: str) -> bytes: + return s.encode('punycode') + +def _unot(s: int) -> str: + return 'U+{:04X}'.format(s) + + +def valid_label_length(label: Union[bytes, str]) -> bool: + if len(label) > 63: + return False + return True + + +def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: + if len(label) > (254 if trailing_dot else 253): + return False + return True + + +def check_bidi(label: str, check_ltr: bool = False) -> bool: + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == '': + # String likely comes from a newer version of Unicode + raise IDNABidiError('Unknown directionality in label {} at position {}'.format(repr(label), idx)) + if direction in ['R', 'AL', 'AN']: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in ['R', 'AL']: + rtl = True + elif direction == 'L': + rtl = False + else: + raise IDNABidiError('First codepoint in label {} must be directionality L, R or AL'.format(repr(label))) + + valid_ending = False + number_type = None # type: Optional[str] + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {} in a right-to-left label'.format(idx)) + # Bidi rule 3 + if direction in ['R', 'AL', 'EN', 'AN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + # Bidi rule 4 + if direction in ['AN', 'EN']: + if not number_type: + number_type = direction + else: + if number_type != direction: + raise IDNABidiError('Can not mix numeral types in a right-to-left label') + else: + # Bidi rule 5 + if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {} in a left-to-right label'.format(idx)) + # Bidi rule 6 + if direction in ['L', 'EN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + + if not valid_ending: + raise IDNABidiError('Label ends with illegal codepoint directionality') + + return True + + +def check_initial_combiner(label: str) -> bool: + if unicodedata.category(label[0])[0] == 'M': + raise IDNAError('Label begins with an illegal combining character') + return True + + +def check_hyphen_ok(label: str) -> bool: + if label[2:4] == '--': + raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') + if label[0] == '-' or label[-1] == '-': + raise IDNAError('Label must not start or end with a hyphen') + return True + + +def check_nfc(label: str) -> None: + if unicodedata.normalize('NFC', label) != label: + raise IDNAError('Label must be in Normalization Form C') + + +def valid_contextj(label: str, pos: int) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x200c: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos-1, -1, -1): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + elif joining_type in [ord('L'), ord('D')]: + ok = True + break + else: + break + + if not ok: + return False + + ok = False + for i in range(pos+1, len(label)): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + elif joining_type in [ord('R'), ord('D')]: + ok = True + break + else: + break + return ok + + if cp_value == 0x200d: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + return False + + else: + + return False + + +def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x00b7: + if 0 < pos < len(label)-1: + if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c: + return True + return False + + elif cp_value == 0x0375: + if pos < len(label)-1 and len(label) > 1: + return _is_script(label[pos + 1], 'Greek') + return False + + elif cp_value == 0x05f3 or cp_value == 0x05f4: + if pos > 0: + return _is_script(label[pos - 1], 'Hebrew') + return False + + elif cp_value == 0x30fb: + for cp in label: + if cp == '\u30fb': + continue + if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): + return True + return False + + elif 0x660 <= cp_value <= 0x669: + for cp in label: + if 0x6f0 <= ord(cp) <= 0x06f9: + return False + return True + + elif 0x6f0 <= cp_value <= 0x6f9: + for cp in label: + if 0x660 <= ord(cp) <= 0x0669: + return False + return True + + return False + + +def check_label(label: Union[str, bytes, bytearray]) -> None: + if isinstance(label, (bytes, bytearray)): + label = label.decode('utf-8') + if len(label) == 0: + raise IDNAError('Empty Label') + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for (pos, cp) in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']): + continue + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): + if not valid_contextj(label, pos): + raise InvalidCodepointContext('Joiner {} not allowed at position {} in {}'.format( + _unot(cp_value), pos+1, repr(label))) + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): + if not valid_contexto(label, pos): + raise InvalidCodepointContext('Codepoint {} not allowed at position {} in {}'.format(_unot(cp_value), pos+1, repr(label))) + else: + raise InvalidCodepoint('Codepoint {} at position {} of {} not allowed'.format(_unot(cp_value), pos+1, repr(label))) + + check_bidi(label) + + +def alabel(label: str) -> bytes: + try: + label_bytes = label.encode('ascii') + ulabel(label_bytes) + if not valid_label_length(label_bytes): + raise IDNAError('Label too long') + return label_bytes + except UnicodeEncodeError: + pass + + check_label(label) + label_bytes = _alabel_prefix + _punycode(label) + + if not valid_label_length(label_bytes): + raise IDNAError('Label too long') + + return label_bytes + + +def ulabel(label: Union[str, bytes, bytearray]) -> str: + if not isinstance(label, (bytes, bytearray)): + try: + label_bytes = label.encode('ascii') + except UnicodeEncodeError: + check_label(label) + return label + else: + label_bytes = label + + label_bytes = label_bytes.lower() + if label_bytes.startswith(_alabel_prefix): + label_bytes = label_bytes[len(_alabel_prefix):] + if not label_bytes: + raise IDNAError('Malformed A-label, no Punycode eligible content found') + if label_bytes.decode('ascii')[-1] == '-': + raise IDNAError('A-label must not end with a hyphen') + else: + check_label(label_bytes) + return label_bytes.decode('ascii') + + try: + label = label_bytes.decode('punycode') + except UnicodeError: + raise IDNAError('Invalid A-label') + check_label(label) + return label + + +def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: + """Re-map the characters in the string according to UTS46 processing.""" + from .uts46data import uts46data + output = '' + + for pos, char in enumerate(domain): + code_point = ord(char) + try: + uts46row = uts46data[code_point if code_point < 256 else + bisect.bisect_left(uts46data, (code_point, 'Z')) - 1] + status = uts46row[1] + replacement = None # type: Optional[str] + if len(uts46row) == 3: + replacement = uts46row[2] + if (status == 'V' or + (status == 'D' and not transitional) or + (status == '3' and not std3_rules and replacement is None)): + output += char + elif replacement is not None and (status == 'M' or + (status == '3' and not std3_rules) or + (status == 'D' and transitional)): + output += replacement + elif status != 'I': + raise IndexError() + except IndexError: + raise InvalidCodepoint( + 'Codepoint {} not allowed at position {} in {}'.format( + _unot(code_point), pos + 1, repr(domain))) + + return unicodedata.normalize('NFC', output) + + +def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False, transitional: bool = False) -> bytes: + if not isinstance(s, str): + try: + s = str(s, 'ascii') + except UnicodeDecodeError: + raise IDNAError('should pass a unicode string to the function rather than a byte string.') + if uts46: + s = uts46_remap(s, std3_rules, transitional) + trailing_dot = False + result = [] + if strict: + labels = s.split('.') + else: + labels = _unicode_dots_re.split(s) + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if labels[-1] == '': + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append(b'') + s = b'.'.join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError('Domain too long') + return s + + +def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False) -> str: + try: + if not isinstance(s, str): + s = str(s, 'ascii') + except UnicodeDecodeError: + raise IDNAError('Invalid ASCII in A-label') + if uts46: + s = uts46_remap(s, std3_rules, False) + trailing_dot = False + result = [] + if not strict: + labels = _unicode_dots_re.split(s) + else: + labels = s.split('.') + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + s = ulabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append('') + return '.'.join(result) diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/idnadata.py b/.pythonlibs/lib/python3.10/site-packages/idna/idnadata.py new file mode 100644 index 0000000..c61dcf9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/idnadata.py @@ -0,0 +1,4245 @@ +# This file is automatically generated by tools/idna-data + +__version__ = '15.1.0' +scripts = { + 'Greek': ( + 0x37000000374, + 0x37500000378, + 0x37a0000037e, + 0x37f00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038b, + 0x38c0000038d, + 0x38e000003a2, + 0x3a3000003e2, + 0x3f000000400, + 0x1d2600001d2b, + 0x1d5d00001d62, + 0x1d6600001d6b, + 0x1dbf00001dc0, + 0x1f0000001f16, + 0x1f1800001f1e, + 0x1f2000001f46, + 0x1f4800001f4e, + 0x1f5000001f58, + 0x1f5900001f5a, + 0x1f5b00001f5c, + 0x1f5d00001f5e, + 0x1f5f00001f7e, + 0x1f8000001fb5, + 0x1fb600001fc5, + 0x1fc600001fd4, + 0x1fd600001fdc, + 0x1fdd00001ff0, + 0x1ff200001ff5, + 0x1ff600001fff, + 0x212600002127, + 0xab650000ab66, + 0x101400001018f, + 0x101a0000101a1, + 0x1d2000001d246, + ), + 'Han': ( + 0x2e8000002e9a, + 0x2e9b00002ef4, + 0x2f0000002fd6, + 0x300500003006, + 0x300700003008, + 0x30210000302a, + 0x30380000303c, + 0x340000004dc0, + 0x4e000000a000, + 0xf9000000fa6e, + 0xfa700000fada, + 0x16fe200016fe4, + 0x16ff000016ff2, + 0x200000002a6e0, + 0x2a7000002b73a, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x2ebf00002ee5e, + 0x2f8000002fa1e, + 0x300000003134b, + 0x31350000323b0, + ), + 'Hebrew': ( + 0x591000005c8, + 0x5d0000005eb, + 0x5ef000005f5, + 0xfb1d0000fb37, + 0xfb380000fb3d, + 0xfb3e0000fb3f, + 0xfb400000fb42, + 0xfb430000fb45, + 0xfb460000fb50, + ), + 'Hiragana': ( + 0x304100003097, + 0x309d000030a0, + 0x1b0010001b120, + 0x1b1320001b133, + 0x1b1500001b153, + 0x1f2000001f201, + ), + 'Katakana': ( + 0x30a1000030fb, + 0x30fd00003100, + 0x31f000003200, + 0x32d0000032ff, + 0x330000003358, + 0xff660000ff70, + 0xff710000ff9e, + 0x1aff00001aff4, + 0x1aff50001affc, + 0x1affd0001afff, + 0x1b0000001b001, + 0x1b1200001b123, + 0x1b1550001b156, + 0x1b1640001b168, + ), +} +joining_types = { + 0xad: 84, + 0x300: 84, + 0x301: 84, + 0x302: 84, + 0x303: 84, + 0x304: 84, + 0x305: 84, + 0x306: 84, + 0x307: 84, + 0x308: 84, + 0x309: 84, + 0x30a: 84, + 0x30b: 84, + 0x30c: 84, + 0x30d: 84, + 0x30e: 84, + 0x30f: 84, + 0x310: 84, + 0x311: 84, + 0x312: 84, + 0x313: 84, + 0x314: 84, + 0x315: 84, + 0x316: 84, + 0x317: 84, + 0x318: 84, + 0x319: 84, + 0x31a: 84, + 0x31b: 84, + 0x31c: 84, + 0x31d: 84, + 0x31e: 84, + 0x31f: 84, + 0x320: 84, + 0x321: 84, + 0x322: 84, + 0x323: 84, + 0x324: 84, + 0x325: 84, + 0x326: 84, + 0x327: 84, + 0x328: 84, + 0x329: 84, + 0x32a: 84, + 0x32b: 84, + 0x32c: 84, + 0x32d: 84, + 0x32e: 84, + 0x32f: 84, + 0x330: 84, + 0x331: 84, + 0x332: 84, + 0x333: 84, + 0x334: 84, + 0x335: 84, + 0x336: 84, + 0x337: 84, + 0x338: 84, + 0x339: 84, + 0x33a: 84, + 0x33b: 84, + 0x33c: 84, + 0x33d: 84, + 0x33e: 84, + 0x33f: 84, + 0x340: 84, + 0x341: 84, + 0x342: 84, + 0x343: 84, + 0x344: 84, + 0x345: 84, + 0x346: 84, + 0x347: 84, + 0x348: 84, + 0x349: 84, + 0x34a: 84, + 0x34b: 84, + 0x34c: 84, + 0x34d: 84, + 0x34e: 84, + 0x34f: 84, + 0x350: 84, + 0x351: 84, + 0x352: 84, + 0x353: 84, + 0x354: 84, + 0x355: 84, + 0x356: 84, + 0x357: 84, + 0x358: 84, + 0x359: 84, + 0x35a: 84, + 0x35b: 84, + 0x35c: 84, + 0x35d: 84, + 0x35e: 84, + 0x35f: 84, + 0x360: 84, + 0x361: 84, + 0x362: 84, + 0x363: 84, + 0x364: 84, + 0x365: 84, + 0x366: 84, + 0x367: 84, + 0x368: 84, + 0x369: 84, + 0x36a: 84, + 0x36b: 84, + 0x36c: 84, + 0x36d: 84, + 0x36e: 84, + 0x36f: 84, + 0x483: 84, + 0x484: 84, + 0x485: 84, + 0x486: 84, + 0x487: 84, + 0x488: 84, + 0x489: 84, + 0x591: 84, + 0x592: 84, + 0x593: 84, + 0x594: 84, + 0x595: 84, + 0x596: 84, + 0x597: 84, + 0x598: 84, + 0x599: 84, + 0x59a: 84, + 0x59b: 84, + 0x59c: 84, + 0x59d: 84, + 0x59e: 84, + 0x59f: 84, + 0x5a0: 84, + 0x5a1: 84, + 0x5a2: 84, + 0x5a3: 84, + 0x5a4: 84, + 0x5a5: 84, + 0x5a6: 84, + 0x5a7: 84, + 0x5a8: 84, + 0x5a9: 84, + 0x5aa: 84, + 0x5ab: 84, + 0x5ac: 84, + 0x5ad: 84, + 0x5ae: 84, + 0x5af: 84, + 0x5b0: 84, + 0x5b1: 84, + 0x5b2: 84, + 0x5b3: 84, + 0x5b4: 84, + 0x5b5: 84, + 0x5b6: 84, + 0x5b7: 84, + 0x5b8: 84, + 0x5b9: 84, + 0x5ba: 84, + 0x5bb: 84, + 0x5bc: 84, + 0x5bd: 84, + 0x5bf: 84, + 0x5c1: 84, + 0x5c2: 84, + 0x5c4: 84, + 0x5c5: 84, + 0x5c7: 84, + 0x610: 84, + 0x611: 84, + 0x612: 84, + 0x613: 84, + 0x614: 84, + 0x615: 84, + 0x616: 84, + 0x617: 84, + 0x618: 84, + 0x619: 84, + 0x61a: 84, + 0x61c: 84, + 0x620: 68, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62a: 68, + 0x62b: 68, + 0x62c: 68, + 0x62d: 68, + 0x62e: 68, + 0x62f: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63a: 68, + 0x63b: 68, + 0x63c: 68, + 0x63d: 68, + 0x63e: 68, + 0x63f: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64a: 68, + 0x64b: 84, + 0x64c: 84, + 0x64d: 84, + 0x64e: 84, + 0x64f: 84, + 0x650: 84, + 0x651: 84, + 0x652: 84, + 0x653: 84, + 0x654: 84, + 0x655: 84, + 0x656: 84, + 0x657: 84, + 0x658: 84, + 0x659: 84, + 0x65a: 84, + 0x65b: 84, + 0x65c: 84, + 0x65d: 84, + 0x65e: 84, + 0x65f: 84, + 0x66e: 68, + 0x66f: 68, + 0x670: 84, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67a: 68, + 0x67b: 68, + 0x67c: 68, + 0x67d: 68, + 0x67e: 68, + 0x67f: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68a: 82, + 0x68b: 82, + 0x68c: 82, + 0x68d: 82, + 0x68e: 82, + 0x68f: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69a: 68, + 0x69b: 68, + 0x69c: 68, + 0x69d: 68, + 0x69e: 68, + 0x69f: 68, + 0x6a0: 68, + 0x6a1: 68, + 0x6a2: 68, + 0x6a3: 68, + 0x6a4: 68, + 0x6a5: 68, + 0x6a6: 68, + 0x6a7: 68, + 0x6a8: 68, + 0x6a9: 68, + 0x6aa: 68, + 0x6ab: 68, + 0x6ac: 68, + 0x6ad: 68, + 0x6ae: 68, + 0x6af: 68, + 0x6b0: 68, + 0x6b1: 68, + 0x6b2: 68, + 0x6b3: 68, + 0x6b4: 68, + 0x6b5: 68, + 0x6b6: 68, + 0x6b7: 68, + 0x6b8: 68, + 0x6b9: 68, + 0x6ba: 68, + 0x6bb: 68, + 0x6bc: 68, + 0x6bd: 68, + 0x6be: 68, + 0x6bf: 68, + 0x6c0: 82, + 0x6c1: 68, + 0x6c2: 68, + 0x6c3: 82, + 0x6c4: 82, + 0x6c5: 82, + 0x6c6: 82, + 0x6c7: 82, + 0x6c8: 82, + 0x6c9: 82, + 0x6ca: 82, + 0x6cb: 82, + 0x6cc: 68, + 0x6cd: 82, + 0x6ce: 68, + 0x6cf: 82, + 0x6d0: 68, + 0x6d1: 68, + 0x6d2: 82, + 0x6d3: 82, + 0x6d5: 82, + 0x6d6: 84, + 0x6d7: 84, + 0x6d8: 84, + 0x6d9: 84, + 0x6da: 84, + 0x6db: 84, + 0x6dc: 84, + 0x6df: 84, + 0x6e0: 84, + 0x6e1: 84, + 0x6e2: 84, + 0x6e3: 84, + 0x6e4: 84, + 0x6e7: 84, + 0x6e8: 84, + 0x6ea: 84, + 0x6eb: 84, + 0x6ec: 84, + 0x6ed: 84, + 0x6ee: 82, + 0x6ef: 82, + 0x6fa: 68, + 0x6fb: 68, + 0x6fc: 68, + 0x6ff: 68, + 0x70f: 84, + 0x710: 82, + 0x711: 84, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71a: 68, + 0x71b: 68, + 0x71c: 68, + 0x71d: 68, + 0x71e: 82, + 0x71f: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72a: 82, + 0x72b: 68, + 0x72c: 82, + 0x72d: 68, + 0x72e: 68, + 0x72f: 82, + 0x730: 84, + 0x731: 84, + 0x732: 84, + 0x733: 84, + 0x734: 84, + 0x735: 84, + 0x736: 84, + 0x737: 84, + 0x738: 84, + 0x739: 84, + 0x73a: 84, + 0x73b: 84, + 0x73c: 84, + 0x73d: 84, + 0x73e: 84, + 0x73f: 84, + 0x740: 84, + 0x741: 84, + 0x742: 84, + 0x743: 84, + 0x744: 84, + 0x745: 84, + 0x746: 84, + 0x747: 84, + 0x748: 84, + 0x749: 84, + 0x74a: 84, + 0x74d: 82, + 0x74e: 68, + 0x74f: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75a: 82, + 0x75b: 82, + 0x75c: 68, + 0x75d: 68, + 0x75e: 68, + 0x75f: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76a: 68, + 0x76b: 82, + 0x76c: 82, + 0x76d: 68, + 0x76e: 68, + 0x76f: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77a: 68, + 0x77b: 68, + 0x77c: 68, + 0x77d: 68, + 0x77e: 68, + 0x77f: 68, + 0x7a6: 84, + 0x7a7: 84, + 0x7a8: 84, + 0x7a9: 84, + 0x7aa: 84, + 0x7ab: 84, + 0x7ac: 84, + 0x7ad: 84, + 0x7ae: 84, + 0x7af: 84, + 0x7b0: 84, + 0x7ca: 68, + 0x7cb: 68, + 0x7cc: 68, + 0x7cd: 68, + 0x7ce: 68, + 0x7cf: 68, + 0x7d0: 68, + 0x7d1: 68, + 0x7d2: 68, + 0x7d3: 68, + 0x7d4: 68, + 0x7d5: 68, + 0x7d6: 68, + 0x7d7: 68, + 0x7d8: 68, + 0x7d9: 68, + 0x7da: 68, + 0x7db: 68, + 0x7dc: 68, + 0x7dd: 68, + 0x7de: 68, + 0x7df: 68, + 0x7e0: 68, + 0x7e1: 68, + 0x7e2: 68, + 0x7e3: 68, + 0x7e4: 68, + 0x7e5: 68, + 0x7e6: 68, + 0x7e7: 68, + 0x7e8: 68, + 0x7e9: 68, + 0x7ea: 68, + 0x7eb: 84, + 0x7ec: 84, + 0x7ed: 84, + 0x7ee: 84, + 0x7ef: 84, + 0x7f0: 84, + 0x7f1: 84, + 0x7f2: 84, + 0x7f3: 84, + 0x7fa: 67, + 0x7fd: 84, + 0x816: 84, + 0x817: 84, + 0x818: 84, + 0x819: 84, + 0x81b: 84, + 0x81c: 84, + 0x81d: 84, + 0x81e: 84, + 0x81f: 84, + 0x820: 84, + 0x821: 84, + 0x822: 84, + 0x823: 84, + 0x825: 84, + 0x826: 84, + 0x827: 84, + 0x829: 84, + 0x82a: 84, + 0x82b: 84, + 0x82c: 84, + 0x82d: 84, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84a: 68, + 0x84b: 68, + 0x84c: 68, + 0x84d: 68, + 0x84e: 68, + 0x84f: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 82, + 0x857: 82, + 0x858: 82, + 0x859: 84, + 0x85a: 84, + 0x85b: 84, + 0x860: 68, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86a: 82, + 0x870: 82, + 0x871: 82, + 0x872: 82, + 0x873: 82, + 0x874: 82, + 0x875: 82, + 0x876: 82, + 0x877: 82, + 0x878: 82, + 0x879: 82, + 0x87a: 82, + 0x87b: 82, + 0x87c: 82, + 0x87d: 82, + 0x87e: 82, + 0x87f: 82, + 0x880: 82, + 0x881: 82, + 0x882: 82, + 0x883: 67, + 0x884: 67, + 0x885: 67, + 0x886: 68, + 0x889: 68, + 0x88a: 68, + 0x88b: 68, + 0x88c: 68, + 0x88d: 68, + 0x88e: 82, + 0x898: 84, + 0x899: 84, + 0x89a: 84, + 0x89b: 84, + 0x89c: 84, + 0x89d: 84, + 0x89e: 84, + 0x89f: 84, + 0x8a0: 68, + 0x8a1: 68, + 0x8a2: 68, + 0x8a3: 68, + 0x8a4: 68, + 0x8a5: 68, + 0x8a6: 68, + 0x8a7: 68, + 0x8a8: 68, + 0x8a9: 68, + 0x8aa: 82, + 0x8ab: 82, + 0x8ac: 82, + 0x8ae: 82, + 0x8af: 68, + 0x8b0: 68, + 0x8b1: 82, + 0x8b2: 82, + 0x8b3: 68, + 0x8b4: 68, + 0x8b5: 68, + 0x8b6: 68, + 0x8b7: 68, + 0x8b8: 68, + 0x8b9: 82, + 0x8ba: 68, + 0x8bb: 68, + 0x8bc: 68, + 0x8bd: 68, + 0x8be: 68, + 0x8bf: 68, + 0x8c0: 68, + 0x8c1: 68, + 0x8c2: 68, + 0x8c3: 68, + 0x8c4: 68, + 0x8c5: 68, + 0x8c6: 68, + 0x8c7: 68, + 0x8c8: 68, + 0x8ca: 84, + 0x8cb: 84, + 0x8cc: 84, + 0x8cd: 84, + 0x8ce: 84, + 0x8cf: 84, + 0x8d0: 84, + 0x8d1: 84, + 0x8d2: 84, + 0x8d3: 84, + 0x8d4: 84, + 0x8d5: 84, + 0x8d6: 84, + 0x8d7: 84, + 0x8d8: 84, + 0x8d9: 84, + 0x8da: 84, + 0x8db: 84, + 0x8dc: 84, + 0x8dd: 84, + 0x8de: 84, + 0x8df: 84, + 0x8e0: 84, + 0x8e1: 84, + 0x8e3: 84, + 0x8e4: 84, + 0x8e5: 84, + 0x8e6: 84, + 0x8e7: 84, + 0x8e8: 84, + 0x8e9: 84, + 0x8ea: 84, + 0x8eb: 84, + 0x8ec: 84, + 0x8ed: 84, + 0x8ee: 84, + 0x8ef: 84, + 0x8f0: 84, + 0x8f1: 84, + 0x8f2: 84, + 0x8f3: 84, + 0x8f4: 84, + 0x8f5: 84, + 0x8f6: 84, + 0x8f7: 84, + 0x8f8: 84, + 0x8f9: 84, + 0x8fa: 84, + 0x8fb: 84, + 0x8fc: 84, + 0x8fd: 84, + 0x8fe: 84, + 0x8ff: 84, + 0x900: 84, + 0x901: 84, + 0x902: 84, + 0x93a: 84, + 0x93c: 84, + 0x941: 84, + 0x942: 84, + 0x943: 84, + 0x944: 84, + 0x945: 84, + 0x946: 84, + 0x947: 84, + 0x948: 84, + 0x94d: 84, + 0x951: 84, + 0x952: 84, + 0x953: 84, + 0x954: 84, + 0x955: 84, + 0x956: 84, + 0x957: 84, + 0x962: 84, + 0x963: 84, + 0x981: 84, + 0x9bc: 84, + 0x9c1: 84, + 0x9c2: 84, + 0x9c3: 84, + 0x9c4: 84, + 0x9cd: 84, + 0x9e2: 84, + 0x9e3: 84, + 0x9fe: 84, + 0xa01: 84, + 0xa02: 84, + 0xa3c: 84, + 0xa41: 84, + 0xa42: 84, + 0xa47: 84, + 0xa48: 84, + 0xa4b: 84, + 0xa4c: 84, + 0xa4d: 84, + 0xa51: 84, + 0xa70: 84, + 0xa71: 84, + 0xa75: 84, + 0xa81: 84, + 0xa82: 84, + 0xabc: 84, + 0xac1: 84, + 0xac2: 84, + 0xac3: 84, + 0xac4: 84, + 0xac5: 84, + 0xac7: 84, + 0xac8: 84, + 0xacd: 84, + 0xae2: 84, + 0xae3: 84, + 0xafa: 84, + 0xafb: 84, + 0xafc: 84, + 0xafd: 84, + 0xafe: 84, + 0xaff: 84, + 0xb01: 84, + 0xb3c: 84, + 0xb3f: 84, + 0xb41: 84, + 0xb42: 84, + 0xb43: 84, + 0xb44: 84, + 0xb4d: 84, + 0xb55: 84, + 0xb56: 84, + 0xb62: 84, + 0xb63: 84, + 0xb82: 84, + 0xbc0: 84, + 0xbcd: 84, + 0xc00: 84, + 0xc04: 84, + 0xc3c: 84, + 0xc3e: 84, + 0xc3f: 84, + 0xc40: 84, + 0xc46: 84, + 0xc47: 84, + 0xc48: 84, + 0xc4a: 84, + 0xc4b: 84, + 0xc4c: 84, + 0xc4d: 84, + 0xc55: 84, + 0xc56: 84, + 0xc62: 84, + 0xc63: 84, + 0xc81: 84, + 0xcbc: 84, + 0xcbf: 84, + 0xcc6: 84, + 0xccc: 84, + 0xccd: 84, + 0xce2: 84, + 0xce3: 84, + 0xd00: 84, + 0xd01: 84, + 0xd3b: 84, + 0xd3c: 84, + 0xd41: 84, + 0xd42: 84, + 0xd43: 84, + 0xd44: 84, + 0xd4d: 84, + 0xd62: 84, + 0xd63: 84, + 0xd81: 84, + 0xdca: 84, + 0xdd2: 84, + 0xdd3: 84, + 0xdd4: 84, + 0xdd6: 84, + 0xe31: 84, + 0xe34: 84, + 0xe35: 84, + 0xe36: 84, + 0xe37: 84, + 0xe38: 84, + 0xe39: 84, + 0xe3a: 84, + 0xe47: 84, + 0xe48: 84, + 0xe49: 84, + 0xe4a: 84, + 0xe4b: 84, + 0xe4c: 84, + 0xe4d: 84, + 0xe4e: 84, + 0xeb1: 84, + 0xeb4: 84, + 0xeb5: 84, + 0xeb6: 84, + 0xeb7: 84, + 0xeb8: 84, + 0xeb9: 84, + 0xeba: 84, + 0xebb: 84, + 0xebc: 84, + 0xec8: 84, + 0xec9: 84, + 0xeca: 84, + 0xecb: 84, + 0xecc: 84, + 0xecd: 84, + 0xece: 84, + 0xf18: 84, + 0xf19: 84, + 0xf35: 84, + 0xf37: 84, + 0xf39: 84, + 0xf71: 84, + 0xf72: 84, + 0xf73: 84, + 0xf74: 84, + 0xf75: 84, + 0xf76: 84, + 0xf77: 84, + 0xf78: 84, + 0xf79: 84, + 0xf7a: 84, + 0xf7b: 84, + 0xf7c: 84, + 0xf7d: 84, + 0xf7e: 84, + 0xf80: 84, + 0xf81: 84, + 0xf82: 84, + 0xf83: 84, + 0xf84: 84, + 0xf86: 84, + 0xf87: 84, + 0xf8d: 84, + 0xf8e: 84, + 0xf8f: 84, + 0xf90: 84, + 0xf91: 84, + 0xf92: 84, + 0xf93: 84, + 0xf94: 84, + 0xf95: 84, + 0xf96: 84, + 0xf97: 84, + 0xf99: 84, + 0xf9a: 84, + 0xf9b: 84, + 0xf9c: 84, + 0xf9d: 84, + 0xf9e: 84, + 0xf9f: 84, + 0xfa0: 84, + 0xfa1: 84, + 0xfa2: 84, + 0xfa3: 84, + 0xfa4: 84, + 0xfa5: 84, + 0xfa6: 84, + 0xfa7: 84, + 0xfa8: 84, + 0xfa9: 84, + 0xfaa: 84, + 0xfab: 84, + 0xfac: 84, + 0xfad: 84, + 0xfae: 84, + 0xfaf: 84, + 0xfb0: 84, + 0xfb1: 84, + 0xfb2: 84, + 0xfb3: 84, + 0xfb4: 84, + 0xfb5: 84, + 0xfb6: 84, + 0xfb7: 84, + 0xfb8: 84, + 0xfb9: 84, + 0xfba: 84, + 0xfbb: 84, + 0xfbc: 84, + 0xfc6: 84, + 0x102d: 84, + 0x102e: 84, + 0x102f: 84, + 0x1030: 84, + 0x1032: 84, + 0x1033: 84, + 0x1034: 84, + 0x1035: 84, + 0x1036: 84, + 0x1037: 84, + 0x1039: 84, + 0x103a: 84, + 0x103d: 84, + 0x103e: 84, + 0x1058: 84, + 0x1059: 84, + 0x105e: 84, + 0x105f: 84, + 0x1060: 84, + 0x1071: 84, + 0x1072: 84, + 0x1073: 84, + 0x1074: 84, + 0x1082: 84, + 0x1085: 84, + 0x1086: 84, + 0x108d: 84, + 0x109d: 84, + 0x135d: 84, + 0x135e: 84, + 0x135f: 84, + 0x1712: 84, + 0x1713: 84, + 0x1714: 84, + 0x1732: 84, + 0x1733: 84, + 0x1752: 84, + 0x1753: 84, + 0x1772: 84, + 0x1773: 84, + 0x17b4: 84, + 0x17b5: 84, + 0x17b7: 84, + 0x17b8: 84, + 0x17b9: 84, + 0x17ba: 84, + 0x17bb: 84, + 0x17bc: 84, + 0x17bd: 84, + 0x17c6: 84, + 0x17c9: 84, + 0x17ca: 84, + 0x17cb: 84, + 0x17cc: 84, + 0x17cd: 84, + 0x17ce: 84, + 0x17cf: 84, + 0x17d0: 84, + 0x17d1: 84, + 0x17d2: 84, + 0x17d3: 84, + 0x17dd: 84, + 0x1807: 68, + 0x180a: 67, + 0x180b: 84, + 0x180c: 84, + 0x180d: 84, + 0x180f: 84, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182a: 68, + 0x182b: 68, + 0x182c: 68, + 0x182d: 68, + 0x182e: 68, + 0x182f: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183a: 68, + 0x183b: 68, + 0x183c: 68, + 0x183d: 68, + 0x183e: 68, + 0x183f: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184a: 68, + 0x184b: 68, + 0x184c: 68, + 0x184d: 68, + 0x184e: 68, + 0x184f: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185a: 68, + 0x185b: 68, + 0x185c: 68, + 0x185d: 68, + 0x185e: 68, + 0x185f: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186a: 68, + 0x186b: 68, + 0x186c: 68, + 0x186d: 68, + 0x186e: 68, + 0x186f: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1878: 68, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188a: 68, + 0x188b: 68, + 0x188c: 68, + 0x188d: 68, + 0x188e: 68, + 0x188f: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189a: 68, + 0x189b: 68, + 0x189c: 68, + 0x189d: 68, + 0x189e: 68, + 0x189f: 68, + 0x18a0: 68, + 0x18a1: 68, + 0x18a2: 68, + 0x18a3: 68, + 0x18a4: 68, + 0x18a5: 68, + 0x18a6: 68, + 0x18a7: 68, + 0x18a8: 68, + 0x18a9: 84, + 0x18aa: 68, + 0x1920: 84, + 0x1921: 84, + 0x1922: 84, + 0x1927: 84, + 0x1928: 84, + 0x1932: 84, + 0x1939: 84, + 0x193a: 84, + 0x193b: 84, + 0x1a17: 84, + 0x1a18: 84, + 0x1a1b: 84, + 0x1a56: 84, + 0x1a58: 84, + 0x1a59: 84, + 0x1a5a: 84, + 0x1a5b: 84, + 0x1a5c: 84, + 0x1a5d: 84, + 0x1a5e: 84, + 0x1a60: 84, + 0x1a62: 84, + 0x1a65: 84, + 0x1a66: 84, + 0x1a67: 84, + 0x1a68: 84, + 0x1a69: 84, + 0x1a6a: 84, + 0x1a6b: 84, + 0x1a6c: 84, + 0x1a73: 84, + 0x1a74: 84, + 0x1a75: 84, + 0x1a76: 84, + 0x1a77: 84, + 0x1a78: 84, + 0x1a79: 84, + 0x1a7a: 84, + 0x1a7b: 84, + 0x1a7c: 84, + 0x1a7f: 84, + 0x1ab0: 84, + 0x1ab1: 84, + 0x1ab2: 84, + 0x1ab3: 84, + 0x1ab4: 84, + 0x1ab5: 84, + 0x1ab6: 84, + 0x1ab7: 84, + 0x1ab8: 84, + 0x1ab9: 84, + 0x1aba: 84, + 0x1abb: 84, + 0x1abc: 84, + 0x1abd: 84, + 0x1abe: 84, + 0x1abf: 84, + 0x1ac0: 84, + 0x1ac1: 84, + 0x1ac2: 84, + 0x1ac3: 84, + 0x1ac4: 84, + 0x1ac5: 84, + 0x1ac6: 84, + 0x1ac7: 84, + 0x1ac8: 84, + 0x1ac9: 84, + 0x1aca: 84, + 0x1acb: 84, + 0x1acc: 84, + 0x1acd: 84, + 0x1ace: 84, + 0x1b00: 84, + 0x1b01: 84, + 0x1b02: 84, + 0x1b03: 84, + 0x1b34: 84, + 0x1b36: 84, + 0x1b37: 84, + 0x1b38: 84, + 0x1b39: 84, + 0x1b3a: 84, + 0x1b3c: 84, + 0x1b42: 84, + 0x1b6b: 84, + 0x1b6c: 84, + 0x1b6d: 84, + 0x1b6e: 84, + 0x1b6f: 84, + 0x1b70: 84, + 0x1b71: 84, + 0x1b72: 84, + 0x1b73: 84, + 0x1b80: 84, + 0x1b81: 84, + 0x1ba2: 84, + 0x1ba3: 84, + 0x1ba4: 84, + 0x1ba5: 84, + 0x1ba8: 84, + 0x1ba9: 84, + 0x1bab: 84, + 0x1bac: 84, + 0x1bad: 84, + 0x1be6: 84, + 0x1be8: 84, + 0x1be9: 84, + 0x1bed: 84, + 0x1bef: 84, + 0x1bf0: 84, + 0x1bf1: 84, + 0x1c2c: 84, + 0x1c2d: 84, + 0x1c2e: 84, + 0x1c2f: 84, + 0x1c30: 84, + 0x1c31: 84, + 0x1c32: 84, + 0x1c33: 84, + 0x1c36: 84, + 0x1c37: 84, + 0x1cd0: 84, + 0x1cd1: 84, + 0x1cd2: 84, + 0x1cd4: 84, + 0x1cd5: 84, + 0x1cd6: 84, + 0x1cd7: 84, + 0x1cd8: 84, + 0x1cd9: 84, + 0x1cda: 84, + 0x1cdb: 84, + 0x1cdc: 84, + 0x1cdd: 84, + 0x1cde: 84, + 0x1cdf: 84, + 0x1ce0: 84, + 0x1ce2: 84, + 0x1ce3: 84, + 0x1ce4: 84, + 0x1ce5: 84, + 0x1ce6: 84, + 0x1ce7: 84, + 0x1ce8: 84, + 0x1ced: 84, + 0x1cf4: 84, + 0x1cf8: 84, + 0x1cf9: 84, + 0x1dc0: 84, + 0x1dc1: 84, + 0x1dc2: 84, + 0x1dc3: 84, + 0x1dc4: 84, + 0x1dc5: 84, + 0x1dc6: 84, + 0x1dc7: 84, + 0x1dc8: 84, + 0x1dc9: 84, + 0x1dca: 84, + 0x1dcb: 84, + 0x1dcc: 84, + 0x1dcd: 84, + 0x1dce: 84, + 0x1dcf: 84, + 0x1dd0: 84, + 0x1dd1: 84, + 0x1dd2: 84, + 0x1dd3: 84, + 0x1dd4: 84, + 0x1dd5: 84, + 0x1dd6: 84, + 0x1dd7: 84, + 0x1dd8: 84, + 0x1dd9: 84, + 0x1dda: 84, + 0x1ddb: 84, + 0x1ddc: 84, + 0x1ddd: 84, + 0x1dde: 84, + 0x1ddf: 84, + 0x1de0: 84, + 0x1de1: 84, + 0x1de2: 84, + 0x1de3: 84, + 0x1de4: 84, + 0x1de5: 84, + 0x1de6: 84, + 0x1de7: 84, + 0x1de8: 84, + 0x1de9: 84, + 0x1dea: 84, + 0x1deb: 84, + 0x1dec: 84, + 0x1ded: 84, + 0x1dee: 84, + 0x1def: 84, + 0x1df0: 84, + 0x1df1: 84, + 0x1df2: 84, + 0x1df3: 84, + 0x1df4: 84, + 0x1df5: 84, + 0x1df6: 84, + 0x1df7: 84, + 0x1df8: 84, + 0x1df9: 84, + 0x1dfa: 84, + 0x1dfb: 84, + 0x1dfc: 84, + 0x1dfd: 84, + 0x1dfe: 84, + 0x1dff: 84, + 0x200b: 84, + 0x200d: 67, + 0x200e: 84, + 0x200f: 84, + 0x202a: 84, + 0x202b: 84, + 0x202c: 84, + 0x202d: 84, + 0x202e: 84, + 0x2060: 84, + 0x2061: 84, + 0x2062: 84, + 0x2063: 84, + 0x2064: 84, + 0x206a: 84, + 0x206b: 84, + 0x206c: 84, + 0x206d: 84, + 0x206e: 84, + 0x206f: 84, + 0x20d0: 84, + 0x20d1: 84, + 0x20d2: 84, + 0x20d3: 84, + 0x20d4: 84, + 0x20d5: 84, + 0x20d6: 84, + 0x20d7: 84, + 0x20d8: 84, + 0x20d9: 84, + 0x20da: 84, + 0x20db: 84, + 0x20dc: 84, + 0x20dd: 84, + 0x20de: 84, + 0x20df: 84, + 0x20e0: 84, + 0x20e1: 84, + 0x20e2: 84, + 0x20e3: 84, + 0x20e4: 84, + 0x20e5: 84, + 0x20e6: 84, + 0x20e7: 84, + 0x20e8: 84, + 0x20e9: 84, + 0x20ea: 84, + 0x20eb: 84, + 0x20ec: 84, + 0x20ed: 84, + 0x20ee: 84, + 0x20ef: 84, + 0x20f0: 84, + 0x2cef: 84, + 0x2cf0: 84, + 0x2cf1: 84, + 0x2d7f: 84, + 0x2de0: 84, + 0x2de1: 84, + 0x2de2: 84, + 0x2de3: 84, + 0x2de4: 84, + 0x2de5: 84, + 0x2de6: 84, + 0x2de7: 84, + 0x2de8: 84, + 0x2de9: 84, + 0x2dea: 84, + 0x2deb: 84, + 0x2dec: 84, + 0x2ded: 84, + 0x2dee: 84, + 0x2def: 84, + 0x2df0: 84, + 0x2df1: 84, + 0x2df2: 84, + 0x2df3: 84, + 0x2df4: 84, + 0x2df5: 84, + 0x2df6: 84, + 0x2df7: 84, + 0x2df8: 84, + 0x2df9: 84, + 0x2dfa: 84, + 0x2dfb: 84, + 0x2dfc: 84, + 0x2dfd: 84, + 0x2dfe: 84, + 0x2dff: 84, + 0x302a: 84, + 0x302b: 84, + 0x302c: 84, + 0x302d: 84, + 0x3099: 84, + 0x309a: 84, + 0xa66f: 84, + 0xa670: 84, + 0xa671: 84, + 0xa672: 84, + 0xa674: 84, + 0xa675: 84, + 0xa676: 84, + 0xa677: 84, + 0xa678: 84, + 0xa679: 84, + 0xa67a: 84, + 0xa67b: 84, + 0xa67c: 84, + 0xa67d: 84, + 0xa69e: 84, + 0xa69f: 84, + 0xa6f0: 84, + 0xa6f1: 84, + 0xa802: 84, + 0xa806: 84, + 0xa80b: 84, + 0xa825: 84, + 0xa826: 84, + 0xa82c: 84, + 0xa840: 68, + 0xa841: 68, + 0xa842: 68, + 0xa843: 68, + 0xa844: 68, + 0xa845: 68, + 0xa846: 68, + 0xa847: 68, + 0xa848: 68, + 0xa849: 68, + 0xa84a: 68, + 0xa84b: 68, + 0xa84c: 68, + 0xa84d: 68, + 0xa84e: 68, + 0xa84f: 68, + 0xa850: 68, + 0xa851: 68, + 0xa852: 68, + 0xa853: 68, + 0xa854: 68, + 0xa855: 68, + 0xa856: 68, + 0xa857: 68, + 0xa858: 68, + 0xa859: 68, + 0xa85a: 68, + 0xa85b: 68, + 0xa85c: 68, + 0xa85d: 68, + 0xa85e: 68, + 0xa85f: 68, + 0xa860: 68, + 0xa861: 68, + 0xa862: 68, + 0xa863: 68, + 0xa864: 68, + 0xa865: 68, + 0xa866: 68, + 0xa867: 68, + 0xa868: 68, + 0xa869: 68, + 0xa86a: 68, + 0xa86b: 68, + 0xa86c: 68, + 0xa86d: 68, + 0xa86e: 68, + 0xa86f: 68, + 0xa870: 68, + 0xa871: 68, + 0xa872: 76, + 0xa8c4: 84, + 0xa8c5: 84, + 0xa8e0: 84, + 0xa8e1: 84, + 0xa8e2: 84, + 0xa8e3: 84, + 0xa8e4: 84, + 0xa8e5: 84, + 0xa8e6: 84, + 0xa8e7: 84, + 0xa8e8: 84, + 0xa8e9: 84, + 0xa8ea: 84, + 0xa8eb: 84, + 0xa8ec: 84, + 0xa8ed: 84, + 0xa8ee: 84, + 0xa8ef: 84, + 0xa8f0: 84, + 0xa8f1: 84, + 0xa8ff: 84, + 0xa926: 84, + 0xa927: 84, + 0xa928: 84, + 0xa929: 84, + 0xa92a: 84, + 0xa92b: 84, + 0xa92c: 84, + 0xa92d: 84, + 0xa947: 84, + 0xa948: 84, + 0xa949: 84, + 0xa94a: 84, + 0xa94b: 84, + 0xa94c: 84, + 0xa94d: 84, + 0xa94e: 84, + 0xa94f: 84, + 0xa950: 84, + 0xa951: 84, + 0xa980: 84, + 0xa981: 84, + 0xa982: 84, + 0xa9b3: 84, + 0xa9b6: 84, + 0xa9b7: 84, + 0xa9b8: 84, + 0xa9b9: 84, + 0xa9bc: 84, + 0xa9bd: 84, + 0xa9e5: 84, + 0xaa29: 84, + 0xaa2a: 84, + 0xaa2b: 84, + 0xaa2c: 84, + 0xaa2d: 84, + 0xaa2e: 84, + 0xaa31: 84, + 0xaa32: 84, + 0xaa35: 84, + 0xaa36: 84, + 0xaa43: 84, + 0xaa4c: 84, + 0xaa7c: 84, + 0xaab0: 84, + 0xaab2: 84, + 0xaab3: 84, + 0xaab4: 84, + 0xaab7: 84, + 0xaab8: 84, + 0xaabe: 84, + 0xaabf: 84, + 0xaac1: 84, + 0xaaec: 84, + 0xaaed: 84, + 0xaaf6: 84, + 0xabe5: 84, + 0xabe8: 84, + 0xabed: 84, + 0xfb1e: 84, + 0xfe00: 84, + 0xfe01: 84, + 0xfe02: 84, + 0xfe03: 84, + 0xfe04: 84, + 0xfe05: 84, + 0xfe06: 84, + 0xfe07: 84, + 0xfe08: 84, + 0xfe09: 84, + 0xfe0a: 84, + 0xfe0b: 84, + 0xfe0c: 84, + 0xfe0d: 84, + 0xfe0e: 84, + 0xfe0f: 84, + 0xfe20: 84, + 0xfe21: 84, + 0xfe22: 84, + 0xfe23: 84, + 0xfe24: 84, + 0xfe25: 84, + 0xfe26: 84, + 0xfe27: 84, + 0xfe28: 84, + 0xfe29: 84, + 0xfe2a: 84, + 0xfe2b: 84, + 0xfe2c: 84, + 0xfe2d: 84, + 0xfe2e: 84, + 0xfe2f: 84, + 0xfeff: 84, + 0xfff9: 84, + 0xfffa: 84, + 0xfffb: 84, + 0x101fd: 84, + 0x102e0: 84, + 0x10376: 84, + 0x10377: 84, + 0x10378: 84, + 0x10379: 84, + 0x1037a: 84, + 0x10a01: 84, + 0x10a02: 84, + 0x10a03: 84, + 0x10a05: 84, + 0x10a06: 84, + 0x10a0c: 84, + 0x10a0d: 84, + 0x10a0e: 84, + 0x10a0f: 84, + 0x10a38: 84, + 0x10a39: 84, + 0x10a3a: 84, + 0x10a3f: 84, + 0x10ac0: 68, + 0x10ac1: 68, + 0x10ac2: 68, + 0x10ac3: 68, + 0x10ac4: 68, + 0x10ac5: 82, + 0x10ac7: 82, + 0x10ac9: 82, + 0x10aca: 82, + 0x10acd: 76, + 0x10ace: 82, + 0x10acf: 82, + 0x10ad0: 82, + 0x10ad1: 82, + 0x10ad2: 82, + 0x10ad3: 68, + 0x10ad4: 68, + 0x10ad5: 68, + 0x10ad6: 68, + 0x10ad7: 76, + 0x10ad8: 68, + 0x10ad9: 68, + 0x10ada: 68, + 0x10adb: 68, + 0x10adc: 68, + 0x10add: 82, + 0x10ade: 68, + 0x10adf: 68, + 0x10ae0: 68, + 0x10ae1: 82, + 0x10ae4: 82, + 0x10ae5: 84, + 0x10ae6: 84, + 0x10aeb: 68, + 0x10aec: 68, + 0x10aed: 68, + 0x10aee: 68, + 0x10aef: 82, + 0x10b80: 68, + 0x10b81: 82, + 0x10b82: 68, + 0x10b83: 82, + 0x10b84: 82, + 0x10b85: 82, + 0x10b86: 68, + 0x10b87: 68, + 0x10b88: 68, + 0x10b89: 82, + 0x10b8a: 68, + 0x10b8b: 68, + 0x10b8c: 82, + 0x10b8d: 68, + 0x10b8e: 82, + 0x10b8f: 82, + 0x10b90: 68, + 0x10b91: 82, + 0x10ba9: 82, + 0x10baa: 82, + 0x10bab: 82, + 0x10bac: 82, + 0x10bad: 68, + 0x10bae: 68, + 0x10d00: 76, + 0x10d01: 68, + 0x10d02: 68, + 0x10d03: 68, + 0x10d04: 68, + 0x10d05: 68, + 0x10d06: 68, + 0x10d07: 68, + 0x10d08: 68, + 0x10d09: 68, + 0x10d0a: 68, + 0x10d0b: 68, + 0x10d0c: 68, + 0x10d0d: 68, + 0x10d0e: 68, + 0x10d0f: 68, + 0x10d10: 68, + 0x10d11: 68, + 0x10d12: 68, + 0x10d13: 68, + 0x10d14: 68, + 0x10d15: 68, + 0x10d16: 68, + 0x10d17: 68, + 0x10d18: 68, + 0x10d19: 68, + 0x10d1a: 68, + 0x10d1b: 68, + 0x10d1c: 68, + 0x10d1d: 68, + 0x10d1e: 68, + 0x10d1f: 68, + 0x10d20: 68, + 0x10d21: 68, + 0x10d22: 82, + 0x10d23: 68, + 0x10d24: 84, + 0x10d25: 84, + 0x10d26: 84, + 0x10d27: 84, + 0x10eab: 84, + 0x10eac: 84, + 0x10efd: 84, + 0x10efe: 84, + 0x10eff: 84, + 0x10f30: 68, + 0x10f31: 68, + 0x10f32: 68, + 0x10f33: 82, + 0x10f34: 68, + 0x10f35: 68, + 0x10f36: 68, + 0x10f37: 68, + 0x10f38: 68, + 0x10f39: 68, + 0x10f3a: 68, + 0x10f3b: 68, + 0x10f3c: 68, + 0x10f3d: 68, + 0x10f3e: 68, + 0x10f3f: 68, + 0x10f40: 68, + 0x10f41: 68, + 0x10f42: 68, + 0x10f43: 68, + 0x10f44: 68, + 0x10f46: 84, + 0x10f47: 84, + 0x10f48: 84, + 0x10f49: 84, + 0x10f4a: 84, + 0x10f4b: 84, + 0x10f4c: 84, + 0x10f4d: 84, + 0x10f4e: 84, + 0x10f4f: 84, + 0x10f50: 84, + 0x10f51: 68, + 0x10f52: 68, + 0x10f53: 68, + 0x10f54: 82, + 0x10f70: 68, + 0x10f71: 68, + 0x10f72: 68, + 0x10f73: 68, + 0x10f74: 82, + 0x10f75: 82, + 0x10f76: 68, + 0x10f77: 68, + 0x10f78: 68, + 0x10f79: 68, + 0x10f7a: 68, + 0x10f7b: 68, + 0x10f7c: 68, + 0x10f7d: 68, + 0x10f7e: 68, + 0x10f7f: 68, + 0x10f80: 68, + 0x10f81: 68, + 0x10f82: 84, + 0x10f83: 84, + 0x10f84: 84, + 0x10f85: 84, + 0x10fb0: 68, + 0x10fb2: 68, + 0x10fb3: 68, + 0x10fb4: 82, + 0x10fb5: 82, + 0x10fb6: 82, + 0x10fb8: 68, + 0x10fb9: 82, + 0x10fba: 82, + 0x10fbb: 68, + 0x10fbc: 68, + 0x10fbd: 82, + 0x10fbe: 68, + 0x10fbf: 68, + 0x10fc1: 68, + 0x10fc2: 82, + 0x10fc3: 82, + 0x10fc4: 68, + 0x10fc9: 82, + 0x10fca: 68, + 0x10fcb: 76, + 0x11001: 84, + 0x11038: 84, + 0x11039: 84, + 0x1103a: 84, + 0x1103b: 84, + 0x1103c: 84, + 0x1103d: 84, + 0x1103e: 84, + 0x1103f: 84, + 0x11040: 84, + 0x11041: 84, + 0x11042: 84, + 0x11043: 84, + 0x11044: 84, + 0x11045: 84, + 0x11046: 84, + 0x11070: 84, + 0x11073: 84, + 0x11074: 84, + 0x1107f: 84, + 0x11080: 84, + 0x11081: 84, + 0x110b3: 84, + 0x110b4: 84, + 0x110b5: 84, + 0x110b6: 84, + 0x110b9: 84, + 0x110ba: 84, + 0x110c2: 84, + 0x11100: 84, + 0x11101: 84, + 0x11102: 84, + 0x11127: 84, + 0x11128: 84, + 0x11129: 84, + 0x1112a: 84, + 0x1112b: 84, + 0x1112d: 84, + 0x1112e: 84, + 0x1112f: 84, + 0x11130: 84, + 0x11131: 84, + 0x11132: 84, + 0x11133: 84, + 0x11134: 84, + 0x11173: 84, + 0x11180: 84, + 0x11181: 84, + 0x111b6: 84, + 0x111b7: 84, + 0x111b8: 84, + 0x111b9: 84, + 0x111ba: 84, + 0x111bb: 84, + 0x111bc: 84, + 0x111bd: 84, + 0x111be: 84, + 0x111c9: 84, + 0x111ca: 84, + 0x111cb: 84, + 0x111cc: 84, + 0x111cf: 84, + 0x1122f: 84, + 0x11230: 84, + 0x11231: 84, + 0x11234: 84, + 0x11236: 84, + 0x11237: 84, + 0x1123e: 84, + 0x11241: 84, + 0x112df: 84, + 0x112e3: 84, + 0x112e4: 84, + 0x112e5: 84, + 0x112e6: 84, + 0x112e7: 84, + 0x112e8: 84, + 0x112e9: 84, + 0x112ea: 84, + 0x11300: 84, + 0x11301: 84, + 0x1133b: 84, + 0x1133c: 84, + 0x11340: 84, + 0x11366: 84, + 0x11367: 84, + 0x11368: 84, + 0x11369: 84, + 0x1136a: 84, + 0x1136b: 84, + 0x1136c: 84, + 0x11370: 84, + 0x11371: 84, + 0x11372: 84, + 0x11373: 84, + 0x11374: 84, + 0x11438: 84, + 0x11439: 84, + 0x1143a: 84, + 0x1143b: 84, + 0x1143c: 84, + 0x1143d: 84, + 0x1143e: 84, + 0x1143f: 84, + 0x11442: 84, + 0x11443: 84, + 0x11444: 84, + 0x11446: 84, + 0x1145e: 84, + 0x114b3: 84, + 0x114b4: 84, + 0x114b5: 84, + 0x114b6: 84, + 0x114b7: 84, + 0x114b8: 84, + 0x114ba: 84, + 0x114bf: 84, + 0x114c0: 84, + 0x114c2: 84, + 0x114c3: 84, + 0x115b2: 84, + 0x115b3: 84, + 0x115b4: 84, + 0x115b5: 84, + 0x115bc: 84, + 0x115bd: 84, + 0x115bf: 84, + 0x115c0: 84, + 0x115dc: 84, + 0x115dd: 84, + 0x11633: 84, + 0x11634: 84, + 0x11635: 84, + 0x11636: 84, + 0x11637: 84, + 0x11638: 84, + 0x11639: 84, + 0x1163a: 84, + 0x1163d: 84, + 0x1163f: 84, + 0x11640: 84, + 0x116ab: 84, + 0x116ad: 84, + 0x116b0: 84, + 0x116b1: 84, + 0x116b2: 84, + 0x116b3: 84, + 0x116b4: 84, + 0x116b5: 84, + 0x116b7: 84, + 0x1171d: 84, + 0x1171e: 84, + 0x1171f: 84, + 0x11722: 84, + 0x11723: 84, + 0x11724: 84, + 0x11725: 84, + 0x11727: 84, + 0x11728: 84, + 0x11729: 84, + 0x1172a: 84, + 0x1172b: 84, + 0x1182f: 84, + 0x11830: 84, + 0x11831: 84, + 0x11832: 84, + 0x11833: 84, + 0x11834: 84, + 0x11835: 84, + 0x11836: 84, + 0x11837: 84, + 0x11839: 84, + 0x1183a: 84, + 0x1193b: 84, + 0x1193c: 84, + 0x1193e: 84, + 0x11943: 84, + 0x119d4: 84, + 0x119d5: 84, + 0x119d6: 84, + 0x119d7: 84, + 0x119da: 84, + 0x119db: 84, + 0x119e0: 84, + 0x11a01: 84, + 0x11a02: 84, + 0x11a03: 84, + 0x11a04: 84, + 0x11a05: 84, + 0x11a06: 84, + 0x11a07: 84, + 0x11a08: 84, + 0x11a09: 84, + 0x11a0a: 84, + 0x11a33: 84, + 0x11a34: 84, + 0x11a35: 84, + 0x11a36: 84, + 0x11a37: 84, + 0x11a38: 84, + 0x11a3b: 84, + 0x11a3c: 84, + 0x11a3d: 84, + 0x11a3e: 84, + 0x11a47: 84, + 0x11a51: 84, + 0x11a52: 84, + 0x11a53: 84, + 0x11a54: 84, + 0x11a55: 84, + 0x11a56: 84, + 0x11a59: 84, + 0x11a5a: 84, + 0x11a5b: 84, + 0x11a8a: 84, + 0x11a8b: 84, + 0x11a8c: 84, + 0x11a8d: 84, + 0x11a8e: 84, + 0x11a8f: 84, + 0x11a90: 84, + 0x11a91: 84, + 0x11a92: 84, + 0x11a93: 84, + 0x11a94: 84, + 0x11a95: 84, + 0x11a96: 84, + 0x11a98: 84, + 0x11a99: 84, + 0x11c30: 84, + 0x11c31: 84, + 0x11c32: 84, + 0x11c33: 84, + 0x11c34: 84, + 0x11c35: 84, + 0x11c36: 84, + 0x11c38: 84, + 0x11c39: 84, + 0x11c3a: 84, + 0x11c3b: 84, + 0x11c3c: 84, + 0x11c3d: 84, + 0x11c3f: 84, + 0x11c92: 84, + 0x11c93: 84, + 0x11c94: 84, + 0x11c95: 84, + 0x11c96: 84, + 0x11c97: 84, + 0x11c98: 84, + 0x11c99: 84, + 0x11c9a: 84, + 0x11c9b: 84, + 0x11c9c: 84, + 0x11c9d: 84, + 0x11c9e: 84, + 0x11c9f: 84, + 0x11ca0: 84, + 0x11ca1: 84, + 0x11ca2: 84, + 0x11ca3: 84, + 0x11ca4: 84, + 0x11ca5: 84, + 0x11ca6: 84, + 0x11ca7: 84, + 0x11caa: 84, + 0x11cab: 84, + 0x11cac: 84, + 0x11cad: 84, + 0x11cae: 84, + 0x11caf: 84, + 0x11cb0: 84, + 0x11cb2: 84, + 0x11cb3: 84, + 0x11cb5: 84, + 0x11cb6: 84, + 0x11d31: 84, + 0x11d32: 84, + 0x11d33: 84, + 0x11d34: 84, + 0x11d35: 84, + 0x11d36: 84, + 0x11d3a: 84, + 0x11d3c: 84, + 0x11d3d: 84, + 0x11d3f: 84, + 0x11d40: 84, + 0x11d41: 84, + 0x11d42: 84, + 0x11d43: 84, + 0x11d44: 84, + 0x11d45: 84, + 0x11d47: 84, + 0x11d90: 84, + 0x11d91: 84, + 0x11d95: 84, + 0x11d97: 84, + 0x11ef3: 84, + 0x11ef4: 84, + 0x11f00: 84, + 0x11f01: 84, + 0x11f36: 84, + 0x11f37: 84, + 0x11f38: 84, + 0x11f39: 84, + 0x11f3a: 84, + 0x11f40: 84, + 0x11f42: 84, + 0x13430: 84, + 0x13431: 84, + 0x13432: 84, + 0x13433: 84, + 0x13434: 84, + 0x13435: 84, + 0x13436: 84, + 0x13437: 84, + 0x13438: 84, + 0x13439: 84, + 0x1343a: 84, + 0x1343b: 84, + 0x1343c: 84, + 0x1343d: 84, + 0x1343e: 84, + 0x1343f: 84, + 0x13440: 84, + 0x13447: 84, + 0x13448: 84, + 0x13449: 84, + 0x1344a: 84, + 0x1344b: 84, + 0x1344c: 84, + 0x1344d: 84, + 0x1344e: 84, + 0x1344f: 84, + 0x13450: 84, + 0x13451: 84, + 0x13452: 84, + 0x13453: 84, + 0x13454: 84, + 0x13455: 84, + 0x16af0: 84, + 0x16af1: 84, + 0x16af2: 84, + 0x16af3: 84, + 0x16af4: 84, + 0x16b30: 84, + 0x16b31: 84, + 0x16b32: 84, + 0x16b33: 84, + 0x16b34: 84, + 0x16b35: 84, + 0x16b36: 84, + 0x16f4f: 84, + 0x16f8f: 84, + 0x16f90: 84, + 0x16f91: 84, + 0x16f92: 84, + 0x16fe4: 84, + 0x1bc9d: 84, + 0x1bc9e: 84, + 0x1bca0: 84, + 0x1bca1: 84, + 0x1bca2: 84, + 0x1bca3: 84, + 0x1cf00: 84, + 0x1cf01: 84, + 0x1cf02: 84, + 0x1cf03: 84, + 0x1cf04: 84, + 0x1cf05: 84, + 0x1cf06: 84, + 0x1cf07: 84, + 0x1cf08: 84, + 0x1cf09: 84, + 0x1cf0a: 84, + 0x1cf0b: 84, + 0x1cf0c: 84, + 0x1cf0d: 84, + 0x1cf0e: 84, + 0x1cf0f: 84, + 0x1cf10: 84, + 0x1cf11: 84, + 0x1cf12: 84, + 0x1cf13: 84, + 0x1cf14: 84, + 0x1cf15: 84, + 0x1cf16: 84, + 0x1cf17: 84, + 0x1cf18: 84, + 0x1cf19: 84, + 0x1cf1a: 84, + 0x1cf1b: 84, + 0x1cf1c: 84, + 0x1cf1d: 84, + 0x1cf1e: 84, + 0x1cf1f: 84, + 0x1cf20: 84, + 0x1cf21: 84, + 0x1cf22: 84, + 0x1cf23: 84, + 0x1cf24: 84, + 0x1cf25: 84, + 0x1cf26: 84, + 0x1cf27: 84, + 0x1cf28: 84, + 0x1cf29: 84, + 0x1cf2a: 84, + 0x1cf2b: 84, + 0x1cf2c: 84, + 0x1cf2d: 84, + 0x1cf30: 84, + 0x1cf31: 84, + 0x1cf32: 84, + 0x1cf33: 84, + 0x1cf34: 84, + 0x1cf35: 84, + 0x1cf36: 84, + 0x1cf37: 84, + 0x1cf38: 84, + 0x1cf39: 84, + 0x1cf3a: 84, + 0x1cf3b: 84, + 0x1cf3c: 84, + 0x1cf3d: 84, + 0x1cf3e: 84, + 0x1cf3f: 84, + 0x1cf40: 84, + 0x1cf41: 84, + 0x1cf42: 84, + 0x1cf43: 84, + 0x1cf44: 84, + 0x1cf45: 84, + 0x1cf46: 84, + 0x1d167: 84, + 0x1d168: 84, + 0x1d169: 84, + 0x1d173: 84, + 0x1d174: 84, + 0x1d175: 84, + 0x1d176: 84, + 0x1d177: 84, + 0x1d178: 84, + 0x1d179: 84, + 0x1d17a: 84, + 0x1d17b: 84, + 0x1d17c: 84, + 0x1d17d: 84, + 0x1d17e: 84, + 0x1d17f: 84, + 0x1d180: 84, + 0x1d181: 84, + 0x1d182: 84, + 0x1d185: 84, + 0x1d186: 84, + 0x1d187: 84, + 0x1d188: 84, + 0x1d189: 84, + 0x1d18a: 84, + 0x1d18b: 84, + 0x1d1aa: 84, + 0x1d1ab: 84, + 0x1d1ac: 84, + 0x1d1ad: 84, + 0x1d242: 84, + 0x1d243: 84, + 0x1d244: 84, + 0x1da00: 84, + 0x1da01: 84, + 0x1da02: 84, + 0x1da03: 84, + 0x1da04: 84, + 0x1da05: 84, + 0x1da06: 84, + 0x1da07: 84, + 0x1da08: 84, + 0x1da09: 84, + 0x1da0a: 84, + 0x1da0b: 84, + 0x1da0c: 84, + 0x1da0d: 84, + 0x1da0e: 84, + 0x1da0f: 84, + 0x1da10: 84, + 0x1da11: 84, + 0x1da12: 84, + 0x1da13: 84, + 0x1da14: 84, + 0x1da15: 84, + 0x1da16: 84, + 0x1da17: 84, + 0x1da18: 84, + 0x1da19: 84, + 0x1da1a: 84, + 0x1da1b: 84, + 0x1da1c: 84, + 0x1da1d: 84, + 0x1da1e: 84, + 0x1da1f: 84, + 0x1da20: 84, + 0x1da21: 84, + 0x1da22: 84, + 0x1da23: 84, + 0x1da24: 84, + 0x1da25: 84, + 0x1da26: 84, + 0x1da27: 84, + 0x1da28: 84, + 0x1da29: 84, + 0x1da2a: 84, + 0x1da2b: 84, + 0x1da2c: 84, + 0x1da2d: 84, + 0x1da2e: 84, + 0x1da2f: 84, + 0x1da30: 84, + 0x1da31: 84, + 0x1da32: 84, + 0x1da33: 84, + 0x1da34: 84, + 0x1da35: 84, + 0x1da36: 84, + 0x1da3b: 84, + 0x1da3c: 84, + 0x1da3d: 84, + 0x1da3e: 84, + 0x1da3f: 84, + 0x1da40: 84, + 0x1da41: 84, + 0x1da42: 84, + 0x1da43: 84, + 0x1da44: 84, + 0x1da45: 84, + 0x1da46: 84, + 0x1da47: 84, + 0x1da48: 84, + 0x1da49: 84, + 0x1da4a: 84, + 0x1da4b: 84, + 0x1da4c: 84, + 0x1da4d: 84, + 0x1da4e: 84, + 0x1da4f: 84, + 0x1da50: 84, + 0x1da51: 84, + 0x1da52: 84, + 0x1da53: 84, + 0x1da54: 84, + 0x1da55: 84, + 0x1da56: 84, + 0x1da57: 84, + 0x1da58: 84, + 0x1da59: 84, + 0x1da5a: 84, + 0x1da5b: 84, + 0x1da5c: 84, + 0x1da5d: 84, + 0x1da5e: 84, + 0x1da5f: 84, + 0x1da60: 84, + 0x1da61: 84, + 0x1da62: 84, + 0x1da63: 84, + 0x1da64: 84, + 0x1da65: 84, + 0x1da66: 84, + 0x1da67: 84, + 0x1da68: 84, + 0x1da69: 84, + 0x1da6a: 84, + 0x1da6b: 84, + 0x1da6c: 84, + 0x1da75: 84, + 0x1da84: 84, + 0x1da9b: 84, + 0x1da9c: 84, + 0x1da9d: 84, + 0x1da9e: 84, + 0x1da9f: 84, + 0x1daa1: 84, + 0x1daa2: 84, + 0x1daa3: 84, + 0x1daa4: 84, + 0x1daa5: 84, + 0x1daa6: 84, + 0x1daa7: 84, + 0x1daa8: 84, + 0x1daa9: 84, + 0x1daaa: 84, + 0x1daab: 84, + 0x1daac: 84, + 0x1daad: 84, + 0x1daae: 84, + 0x1daaf: 84, + 0x1e000: 84, + 0x1e001: 84, + 0x1e002: 84, + 0x1e003: 84, + 0x1e004: 84, + 0x1e005: 84, + 0x1e006: 84, + 0x1e008: 84, + 0x1e009: 84, + 0x1e00a: 84, + 0x1e00b: 84, + 0x1e00c: 84, + 0x1e00d: 84, + 0x1e00e: 84, + 0x1e00f: 84, + 0x1e010: 84, + 0x1e011: 84, + 0x1e012: 84, + 0x1e013: 84, + 0x1e014: 84, + 0x1e015: 84, + 0x1e016: 84, + 0x1e017: 84, + 0x1e018: 84, + 0x1e01b: 84, + 0x1e01c: 84, + 0x1e01d: 84, + 0x1e01e: 84, + 0x1e01f: 84, + 0x1e020: 84, + 0x1e021: 84, + 0x1e023: 84, + 0x1e024: 84, + 0x1e026: 84, + 0x1e027: 84, + 0x1e028: 84, + 0x1e029: 84, + 0x1e02a: 84, + 0x1e08f: 84, + 0x1e130: 84, + 0x1e131: 84, + 0x1e132: 84, + 0x1e133: 84, + 0x1e134: 84, + 0x1e135: 84, + 0x1e136: 84, + 0x1e2ae: 84, + 0x1e2ec: 84, + 0x1e2ed: 84, + 0x1e2ee: 84, + 0x1e2ef: 84, + 0x1e4ec: 84, + 0x1e4ed: 84, + 0x1e4ee: 84, + 0x1e4ef: 84, + 0x1e8d0: 84, + 0x1e8d1: 84, + 0x1e8d2: 84, + 0x1e8d3: 84, + 0x1e8d4: 84, + 0x1e8d5: 84, + 0x1e8d6: 84, + 0x1e900: 68, + 0x1e901: 68, + 0x1e902: 68, + 0x1e903: 68, + 0x1e904: 68, + 0x1e905: 68, + 0x1e906: 68, + 0x1e907: 68, + 0x1e908: 68, + 0x1e909: 68, + 0x1e90a: 68, + 0x1e90b: 68, + 0x1e90c: 68, + 0x1e90d: 68, + 0x1e90e: 68, + 0x1e90f: 68, + 0x1e910: 68, + 0x1e911: 68, + 0x1e912: 68, + 0x1e913: 68, + 0x1e914: 68, + 0x1e915: 68, + 0x1e916: 68, + 0x1e917: 68, + 0x1e918: 68, + 0x1e919: 68, + 0x1e91a: 68, + 0x1e91b: 68, + 0x1e91c: 68, + 0x1e91d: 68, + 0x1e91e: 68, + 0x1e91f: 68, + 0x1e920: 68, + 0x1e921: 68, + 0x1e922: 68, + 0x1e923: 68, + 0x1e924: 68, + 0x1e925: 68, + 0x1e926: 68, + 0x1e927: 68, + 0x1e928: 68, + 0x1e929: 68, + 0x1e92a: 68, + 0x1e92b: 68, + 0x1e92c: 68, + 0x1e92d: 68, + 0x1e92e: 68, + 0x1e92f: 68, + 0x1e930: 68, + 0x1e931: 68, + 0x1e932: 68, + 0x1e933: 68, + 0x1e934: 68, + 0x1e935: 68, + 0x1e936: 68, + 0x1e937: 68, + 0x1e938: 68, + 0x1e939: 68, + 0x1e93a: 68, + 0x1e93b: 68, + 0x1e93c: 68, + 0x1e93d: 68, + 0x1e93e: 68, + 0x1e93f: 68, + 0x1e940: 68, + 0x1e941: 68, + 0x1e942: 68, + 0x1e943: 68, + 0x1e944: 84, + 0x1e945: 84, + 0x1e946: 84, + 0x1e947: 84, + 0x1e948: 84, + 0x1e949: 84, + 0x1e94a: 84, + 0x1e94b: 84, + 0xe0001: 84, + 0xe0020: 84, + 0xe0021: 84, + 0xe0022: 84, + 0xe0023: 84, + 0xe0024: 84, + 0xe0025: 84, + 0xe0026: 84, + 0xe0027: 84, + 0xe0028: 84, + 0xe0029: 84, + 0xe002a: 84, + 0xe002b: 84, + 0xe002c: 84, + 0xe002d: 84, + 0xe002e: 84, + 0xe002f: 84, + 0xe0030: 84, + 0xe0031: 84, + 0xe0032: 84, + 0xe0033: 84, + 0xe0034: 84, + 0xe0035: 84, + 0xe0036: 84, + 0xe0037: 84, + 0xe0038: 84, + 0xe0039: 84, + 0xe003a: 84, + 0xe003b: 84, + 0xe003c: 84, + 0xe003d: 84, + 0xe003e: 84, + 0xe003f: 84, + 0xe0040: 84, + 0xe0041: 84, + 0xe0042: 84, + 0xe0043: 84, + 0xe0044: 84, + 0xe0045: 84, + 0xe0046: 84, + 0xe0047: 84, + 0xe0048: 84, + 0xe0049: 84, + 0xe004a: 84, + 0xe004b: 84, + 0xe004c: 84, + 0xe004d: 84, + 0xe004e: 84, + 0xe004f: 84, + 0xe0050: 84, + 0xe0051: 84, + 0xe0052: 84, + 0xe0053: 84, + 0xe0054: 84, + 0xe0055: 84, + 0xe0056: 84, + 0xe0057: 84, + 0xe0058: 84, + 0xe0059: 84, + 0xe005a: 84, + 0xe005b: 84, + 0xe005c: 84, + 0xe005d: 84, + 0xe005e: 84, + 0xe005f: 84, + 0xe0060: 84, + 0xe0061: 84, + 0xe0062: 84, + 0xe0063: 84, + 0xe0064: 84, + 0xe0065: 84, + 0xe0066: 84, + 0xe0067: 84, + 0xe0068: 84, + 0xe0069: 84, + 0xe006a: 84, + 0xe006b: 84, + 0xe006c: 84, + 0xe006d: 84, + 0xe006e: 84, + 0xe006f: 84, + 0xe0070: 84, + 0xe0071: 84, + 0xe0072: 84, + 0xe0073: 84, + 0xe0074: 84, + 0xe0075: 84, + 0xe0076: 84, + 0xe0077: 84, + 0xe0078: 84, + 0xe0079: 84, + 0xe007a: 84, + 0xe007b: 84, + 0xe007c: 84, + 0xe007d: 84, + 0xe007e: 84, + 0xe007f: 84, + 0xe0100: 84, + 0xe0101: 84, + 0xe0102: 84, + 0xe0103: 84, + 0xe0104: 84, + 0xe0105: 84, + 0xe0106: 84, + 0xe0107: 84, + 0xe0108: 84, + 0xe0109: 84, + 0xe010a: 84, + 0xe010b: 84, + 0xe010c: 84, + 0xe010d: 84, + 0xe010e: 84, + 0xe010f: 84, + 0xe0110: 84, + 0xe0111: 84, + 0xe0112: 84, + 0xe0113: 84, + 0xe0114: 84, + 0xe0115: 84, + 0xe0116: 84, + 0xe0117: 84, + 0xe0118: 84, + 0xe0119: 84, + 0xe011a: 84, + 0xe011b: 84, + 0xe011c: 84, + 0xe011d: 84, + 0xe011e: 84, + 0xe011f: 84, + 0xe0120: 84, + 0xe0121: 84, + 0xe0122: 84, + 0xe0123: 84, + 0xe0124: 84, + 0xe0125: 84, + 0xe0126: 84, + 0xe0127: 84, + 0xe0128: 84, + 0xe0129: 84, + 0xe012a: 84, + 0xe012b: 84, + 0xe012c: 84, + 0xe012d: 84, + 0xe012e: 84, + 0xe012f: 84, + 0xe0130: 84, + 0xe0131: 84, + 0xe0132: 84, + 0xe0133: 84, + 0xe0134: 84, + 0xe0135: 84, + 0xe0136: 84, + 0xe0137: 84, + 0xe0138: 84, + 0xe0139: 84, + 0xe013a: 84, + 0xe013b: 84, + 0xe013c: 84, + 0xe013d: 84, + 0xe013e: 84, + 0xe013f: 84, + 0xe0140: 84, + 0xe0141: 84, + 0xe0142: 84, + 0xe0143: 84, + 0xe0144: 84, + 0xe0145: 84, + 0xe0146: 84, + 0xe0147: 84, + 0xe0148: 84, + 0xe0149: 84, + 0xe014a: 84, + 0xe014b: 84, + 0xe014c: 84, + 0xe014d: 84, + 0xe014e: 84, + 0xe014f: 84, + 0xe0150: 84, + 0xe0151: 84, + 0xe0152: 84, + 0xe0153: 84, + 0xe0154: 84, + 0xe0155: 84, + 0xe0156: 84, + 0xe0157: 84, + 0xe0158: 84, + 0xe0159: 84, + 0xe015a: 84, + 0xe015b: 84, + 0xe015c: 84, + 0xe015d: 84, + 0xe015e: 84, + 0xe015f: 84, + 0xe0160: 84, + 0xe0161: 84, + 0xe0162: 84, + 0xe0163: 84, + 0xe0164: 84, + 0xe0165: 84, + 0xe0166: 84, + 0xe0167: 84, + 0xe0168: 84, + 0xe0169: 84, + 0xe016a: 84, + 0xe016b: 84, + 0xe016c: 84, + 0xe016d: 84, + 0xe016e: 84, + 0xe016f: 84, + 0xe0170: 84, + 0xe0171: 84, + 0xe0172: 84, + 0xe0173: 84, + 0xe0174: 84, + 0xe0175: 84, + 0xe0176: 84, + 0xe0177: 84, + 0xe0178: 84, + 0xe0179: 84, + 0xe017a: 84, + 0xe017b: 84, + 0xe017c: 84, + 0xe017d: 84, + 0xe017e: 84, + 0xe017f: 84, + 0xe0180: 84, + 0xe0181: 84, + 0xe0182: 84, + 0xe0183: 84, + 0xe0184: 84, + 0xe0185: 84, + 0xe0186: 84, + 0xe0187: 84, + 0xe0188: 84, + 0xe0189: 84, + 0xe018a: 84, + 0xe018b: 84, + 0xe018c: 84, + 0xe018d: 84, + 0xe018e: 84, + 0xe018f: 84, + 0xe0190: 84, + 0xe0191: 84, + 0xe0192: 84, + 0xe0193: 84, + 0xe0194: 84, + 0xe0195: 84, + 0xe0196: 84, + 0xe0197: 84, + 0xe0198: 84, + 0xe0199: 84, + 0xe019a: 84, + 0xe019b: 84, + 0xe019c: 84, + 0xe019d: 84, + 0xe019e: 84, + 0xe019f: 84, + 0xe01a0: 84, + 0xe01a1: 84, + 0xe01a2: 84, + 0xe01a3: 84, + 0xe01a4: 84, + 0xe01a5: 84, + 0xe01a6: 84, + 0xe01a7: 84, + 0xe01a8: 84, + 0xe01a9: 84, + 0xe01aa: 84, + 0xe01ab: 84, + 0xe01ac: 84, + 0xe01ad: 84, + 0xe01ae: 84, + 0xe01af: 84, + 0xe01b0: 84, + 0xe01b1: 84, + 0xe01b2: 84, + 0xe01b3: 84, + 0xe01b4: 84, + 0xe01b5: 84, + 0xe01b6: 84, + 0xe01b7: 84, + 0xe01b8: 84, + 0xe01b9: 84, + 0xe01ba: 84, + 0xe01bb: 84, + 0xe01bc: 84, + 0xe01bd: 84, + 0xe01be: 84, + 0xe01bf: 84, + 0xe01c0: 84, + 0xe01c1: 84, + 0xe01c2: 84, + 0xe01c3: 84, + 0xe01c4: 84, + 0xe01c5: 84, + 0xe01c6: 84, + 0xe01c7: 84, + 0xe01c8: 84, + 0xe01c9: 84, + 0xe01ca: 84, + 0xe01cb: 84, + 0xe01cc: 84, + 0xe01cd: 84, + 0xe01ce: 84, + 0xe01cf: 84, + 0xe01d0: 84, + 0xe01d1: 84, + 0xe01d2: 84, + 0xe01d3: 84, + 0xe01d4: 84, + 0xe01d5: 84, + 0xe01d6: 84, + 0xe01d7: 84, + 0xe01d8: 84, + 0xe01d9: 84, + 0xe01da: 84, + 0xe01db: 84, + 0xe01dc: 84, + 0xe01dd: 84, + 0xe01de: 84, + 0xe01df: 84, + 0xe01e0: 84, + 0xe01e1: 84, + 0xe01e2: 84, + 0xe01e3: 84, + 0xe01e4: 84, + 0xe01e5: 84, + 0xe01e6: 84, + 0xe01e7: 84, + 0xe01e8: 84, + 0xe01e9: 84, + 0xe01ea: 84, + 0xe01eb: 84, + 0xe01ec: 84, + 0xe01ed: 84, + 0xe01ee: 84, + 0xe01ef: 84, +} +codepoint_classes = { + 'PVALID': ( + 0x2d0000002e, + 0x300000003a, + 0x610000007b, + 0xdf000000f7, + 0xf800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010a, + 0x10b0000010c, + 0x10d0000010e, + 0x10f00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011a, + 0x11b0000011c, + 0x11d0000011e, + 0x11f00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012a, + 0x12b0000012c, + 0x12d0000012e, + 0x12f00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13a0000013b, + 0x13c0000013d, + 0x13e0000013f, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14b0000014c, + 0x14d0000014e, + 0x14f00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015a, + 0x15b0000015c, + 0x15d0000015e, + 0x15f00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016a, + 0x16b0000016c, + 0x16d0000016e, + 0x16f00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17a0000017b, + 0x17c0000017d, + 0x17e0000017f, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18c0000018e, + 0x19200000193, + 0x19500000196, + 0x1990000019c, + 0x19e0000019f, + 0x1a1000001a2, + 0x1a3000001a4, + 0x1a5000001a6, + 0x1a8000001a9, + 0x1aa000001ac, + 0x1ad000001ae, + 0x1b0000001b1, + 0x1b4000001b5, + 0x1b6000001b7, + 0x1b9000001bc, + 0x1bd000001c4, + 0x1ce000001cf, + 0x1d0000001d1, + 0x1d2000001d3, + 0x1d4000001d5, + 0x1d6000001d7, + 0x1d8000001d9, + 0x1da000001db, + 0x1dc000001de, + 0x1df000001e0, + 0x1e1000001e2, + 0x1e3000001e4, + 0x1e5000001e6, + 0x1e7000001e8, + 0x1e9000001ea, + 0x1eb000001ec, + 0x1ed000001ee, + 0x1ef000001f1, + 0x1f5000001f6, + 0x1f9000001fa, + 0x1fb000001fc, + 0x1fd000001fe, + 0x1ff00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020a, + 0x20b0000020c, + 0x20d0000020e, + 0x20f00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021a, + 0x21b0000021c, + 0x21d0000021e, + 0x21f00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022a, + 0x22b0000022c, + 0x22d0000022e, + 0x22f00000230, + 0x23100000232, + 0x2330000023a, + 0x23c0000023d, + 0x23f00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024a, + 0x24b0000024c, + 0x24d0000024e, + 0x24f000002b0, + 0x2b9000002c2, + 0x2c6000002d2, + 0x2ec000002ed, + 0x2ee000002ef, + 0x30000000340, + 0x34200000343, + 0x3460000034f, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37b0000037e, + 0x39000000391, + 0x3ac000003cf, + 0x3d7000003d8, + 0x3d9000003da, + 0x3db000003dc, + 0x3dd000003de, + 0x3df000003e0, + 0x3e1000003e2, + 0x3e3000003e4, + 0x3e5000003e6, + 0x3e7000003e8, + 0x3e9000003ea, + 0x3eb000003ec, + 0x3ed000003ee, + 0x3ef000003f0, + 0x3f3000003f4, + 0x3f8000003f9, + 0x3fb000003fd, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046a, + 0x46b0000046c, + 0x46d0000046e, + 0x46f00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047a, + 0x47b0000047c, + 0x47d0000047e, + 0x47f00000480, + 0x48100000482, + 0x48300000488, + 0x48b0000048c, + 0x48d0000048e, + 0x48f00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049a, + 0x49b0000049c, + 0x49d0000049e, + 0x49f000004a0, + 0x4a1000004a2, + 0x4a3000004a4, + 0x4a5000004a6, + 0x4a7000004a8, + 0x4a9000004aa, + 0x4ab000004ac, + 0x4ad000004ae, + 0x4af000004b0, + 0x4b1000004b2, + 0x4b3000004b4, + 0x4b5000004b6, + 0x4b7000004b8, + 0x4b9000004ba, + 0x4bb000004bc, + 0x4bd000004be, + 0x4bf000004c0, + 0x4c2000004c3, + 0x4c4000004c5, + 0x4c6000004c7, + 0x4c8000004c9, + 0x4ca000004cb, + 0x4cc000004cd, + 0x4ce000004d0, + 0x4d1000004d2, + 0x4d3000004d4, + 0x4d5000004d6, + 0x4d7000004d8, + 0x4d9000004da, + 0x4db000004dc, + 0x4dd000004de, + 0x4df000004e0, + 0x4e1000004e2, + 0x4e3000004e4, + 0x4e5000004e6, + 0x4e7000004e8, + 0x4e9000004ea, + 0x4eb000004ec, + 0x4ed000004ee, + 0x4ef000004f0, + 0x4f1000004f2, + 0x4f3000004f4, + 0x4f5000004f6, + 0x4f7000004f8, + 0x4f9000004fa, + 0x4fb000004fc, + 0x4fd000004fe, + 0x4ff00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050a, + 0x50b0000050c, + 0x50d0000050e, + 0x50f00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051a, + 0x51b0000051c, + 0x51d0000051e, + 0x51f00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052a, + 0x52b0000052c, + 0x52d0000052e, + 0x52f00000530, + 0x5590000055a, + 0x56000000587, + 0x58800000589, + 0x591000005be, + 0x5bf000005c0, + 0x5c1000005c3, + 0x5c4000005c6, + 0x5c7000005c8, + 0x5d0000005eb, + 0x5ef000005f3, + 0x6100000061b, + 0x62000000640, + 0x64100000660, + 0x66e00000675, + 0x679000006d4, + 0x6d5000006dd, + 0x6df000006e9, + 0x6ea000006f0, + 0x6fa00000700, + 0x7100000074b, + 0x74d000007b2, + 0x7c0000007f6, + 0x7fd000007fe, + 0x8000000082e, + 0x8400000085c, + 0x8600000086b, + 0x87000000888, + 0x8890000088f, + 0x898000008e2, + 0x8e300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098d, + 0x98f00000991, + 0x993000009a9, + 0x9aa000009b1, + 0x9b2000009b3, + 0x9b6000009ba, + 0x9bc000009c5, + 0x9c7000009c9, + 0x9cb000009cf, + 0x9d7000009d8, + 0x9e0000009e4, + 0x9e6000009f2, + 0x9fc000009fd, + 0x9fe000009ff, + 0xa0100000a04, + 0xa0500000a0b, + 0xa0f00000a11, + 0xa1300000a29, + 0xa2a00000a31, + 0xa3200000a33, + 0xa3500000a36, + 0xa3800000a3a, + 0xa3c00000a3d, + 0xa3e00000a43, + 0xa4700000a49, + 0xa4b00000a4e, + 0xa5100000a52, + 0xa5c00000a5d, + 0xa6600000a76, + 0xa8100000a84, + 0xa8500000a8e, + 0xa8f00000a92, + 0xa9300000aa9, + 0xaaa00000ab1, + 0xab200000ab4, + 0xab500000aba, + 0xabc00000ac6, + 0xac700000aca, + 0xacb00000ace, + 0xad000000ad1, + 0xae000000ae4, + 0xae600000af0, + 0xaf900000b00, + 0xb0100000b04, + 0xb0500000b0d, + 0xb0f00000b11, + 0xb1300000b29, + 0xb2a00000b31, + 0xb3200000b34, + 0xb3500000b3a, + 0xb3c00000b45, + 0xb4700000b49, + 0xb4b00000b4e, + 0xb5500000b58, + 0xb5f00000b64, + 0xb6600000b70, + 0xb7100000b72, + 0xb8200000b84, + 0xb8500000b8b, + 0xb8e00000b91, + 0xb9200000b96, + 0xb9900000b9b, + 0xb9c00000b9d, + 0xb9e00000ba0, + 0xba300000ba5, + 0xba800000bab, + 0xbae00000bba, + 0xbbe00000bc3, + 0xbc600000bc9, + 0xbca00000bce, + 0xbd000000bd1, + 0xbd700000bd8, + 0xbe600000bf0, + 0xc0000000c0d, + 0xc0e00000c11, + 0xc1200000c29, + 0xc2a00000c3a, + 0xc3c00000c45, + 0xc4600000c49, + 0xc4a00000c4e, + 0xc5500000c57, + 0xc5800000c5b, + 0xc5d00000c5e, + 0xc6000000c64, + 0xc6600000c70, + 0xc8000000c84, + 0xc8500000c8d, + 0xc8e00000c91, + 0xc9200000ca9, + 0xcaa00000cb4, + 0xcb500000cba, + 0xcbc00000cc5, + 0xcc600000cc9, + 0xcca00000cce, + 0xcd500000cd7, + 0xcdd00000cdf, + 0xce000000ce4, + 0xce600000cf0, + 0xcf100000cf4, + 0xd0000000d0d, + 0xd0e00000d11, + 0xd1200000d45, + 0xd4600000d49, + 0xd4a00000d4f, + 0xd5400000d58, + 0xd5f00000d64, + 0xd6600000d70, + 0xd7a00000d80, + 0xd8100000d84, + 0xd8500000d97, + 0xd9a00000db2, + 0xdb300000dbc, + 0xdbd00000dbe, + 0xdc000000dc7, + 0xdca00000dcb, + 0xdcf00000dd5, + 0xdd600000dd7, + 0xdd800000de0, + 0xde600000df0, + 0xdf200000df4, + 0xe0100000e33, + 0xe3400000e3b, + 0xe4000000e4f, + 0xe5000000e5a, + 0xe8100000e83, + 0xe8400000e85, + 0xe8600000e8b, + 0xe8c00000ea4, + 0xea500000ea6, + 0xea700000eb3, + 0xeb400000ebe, + 0xec000000ec5, + 0xec600000ec7, + 0xec800000ecf, + 0xed000000eda, + 0xede00000ee0, + 0xf0000000f01, + 0xf0b00000f0c, + 0xf1800000f1a, + 0xf2000000f2a, + 0xf3500000f36, + 0xf3700000f38, + 0xf3900000f3a, + 0xf3e00000f43, + 0xf4400000f48, + 0xf4900000f4d, + 0xf4e00000f52, + 0xf5300000f57, + 0xf5800000f5c, + 0xf5d00000f69, + 0xf6a00000f6d, + 0xf7100000f73, + 0xf7400000f75, + 0xf7a00000f81, + 0xf8200000f85, + 0xf8600000f93, + 0xf9400000f98, + 0xf9900000f9d, + 0xf9e00000fa2, + 0xfa300000fa7, + 0xfa800000fac, + 0xfad00000fb9, + 0xfba00000fbd, + 0xfc600000fc7, + 0x10000000104a, + 0x10500000109e, + 0x10d0000010fb, + 0x10fd00001100, + 0x120000001249, + 0x124a0000124e, + 0x125000001257, + 0x125800001259, + 0x125a0000125e, + 0x126000001289, + 0x128a0000128e, + 0x1290000012b1, + 0x12b2000012b6, + 0x12b8000012bf, + 0x12c0000012c1, + 0x12c2000012c6, + 0x12c8000012d7, + 0x12d800001311, + 0x131200001316, + 0x13180000135b, + 0x135d00001360, + 0x138000001390, + 0x13a0000013f6, + 0x14010000166d, + 0x166f00001680, + 0x16810000169b, + 0x16a0000016eb, + 0x16f1000016f9, + 0x170000001716, + 0x171f00001735, + 0x174000001754, + 0x17600000176d, + 0x176e00001771, + 0x177200001774, + 0x1780000017b4, + 0x17b6000017d4, + 0x17d7000017d8, + 0x17dc000017de, + 0x17e0000017ea, + 0x18100000181a, + 0x182000001879, + 0x1880000018ab, + 0x18b0000018f6, + 0x19000000191f, + 0x19200000192c, + 0x19300000193c, + 0x19460000196e, + 0x197000001975, + 0x1980000019ac, + 0x19b0000019ca, + 0x19d0000019da, + 0x1a0000001a1c, + 0x1a2000001a5f, + 0x1a6000001a7d, + 0x1a7f00001a8a, + 0x1a9000001a9a, + 0x1aa700001aa8, + 0x1ab000001abe, + 0x1abf00001acf, + 0x1b0000001b4d, + 0x1b5000001b5a, + 0x1b6b00001b74, + 0x1b8000001bf4, + 0x1c0000001c38, + 0x1c4000001c4a, + 0x1c4d00001c7e, + 0x1cd000001cd3, + 0x1cd400001cfb, + 0x1d0000001d2c, + 0x1d2f00001d30, + 0x1d3b00001d3c, + 0x1d4e00001d4f, + 0x1d6b00001d78, + 0x1d7900001d9b, + 0x1dc000001e00, + 0x1e0100001e02, + 0x1e0300001e04, + 0x1e0500001e06, + 0x1e0700001e08, + 0x1e0900001e0a, + 0x1e0b00001e0c, + 0x1e0d00001e0e, + 0x1e0f00001e10, + 0x1e1100001e12, + 0x1e1300001e14, + 0x1e1500001e16, + 0x1e1700001e18, + 0x1e1900001e1a, + 0x1e1b00001e1c, + 0x1e1d00001e1e, + 0x1e1f00001e20, + 0x1e2100001e22, + 0x1e2300001e24, + 0x1e2500001e26, + 0x1e2700001e28, + 0x1e2900001e2a, + 0x1e2b00001e2c, + 0x1e2d00001e2e, + 0x1e2f00001e30, + 0x1e3100001e32, + 0x1e3300001e34, + 0x1e3500001e36, + 0x1e3700001e38, + 0x1e3900001e3a, + 0x1e3b00001e3c, + 0x1e3d00001e3e, + 0x1e3f00001e40, + 0x1e4100001e42, + 0x1e4300001e44, + 0x1e4500001e46, + 0x1e4700001e48, + 0x1e4900001e4a, + 0x1e4b00001e4c, + 0x1e4d00001e4e, + 0x1e4f00001e50, + 0x1e5100001e52, + 0x1e5300001e54, + 0x1e5500001e56, + 0x1e5700001e58, + 0x1e5900001e5a, + 0x1e5b00001e5c, + 0x1e5d00001e5e, + 0x1e5f00001e60, + 0x1e6100001e62, + 0x1e6300001e64, + 0x1e6500001e66, + 0x1e6700001e68, + 0x1e6900001e6a, + 0x1e6b00001e6c, + 0x1e6d00001e6e, + 0x1e6f00001e70, + 0x1e7100001e72, + 0x1e7300001e74, + 0x1e7500001e76, + 0x1e7700001e78, + 0x1e7900001e7a, + 0x1e7b00001e7c, + 0x1e7d00001e7e, + 0x1e7f00001e80, + 0x1e8100001e82, + 0x1e8300001e84, + 0x1e8500001e86, + 0x1e8700001e88, + 0x1e8900001e8a, + 0x1e8b00001e8c, + 0x1e8d00001e8e, + 0x1e8f00001e90, + 0x1e9100001e92, + 0x1e9300001e94, + 0x1e9500001e9a, + 0x1e9c00001e9e, + 0x1e9f00001ea0, + 0x1ea100001ea2, + 0x1ea300001ea4, + 0x1ea500001ea6, + 0x1ea700001ea8, + 0x1ea900001eaa, + 0x1eab00001eac, + 0x1ead00001eae, + 0x1eaf00001eb0, + 0x1eb100001eb2, + 0x1eb300001eb4, + 0x1eb500001eb6, + 0x1eb700001eb8, + 0x1eb900001eba, + 0x1ebb00001ebc, + 0x1ebd00001ebe, + 0x1ebf00001ec0, + 0x1ec100001ec2, + 0x1ec300001ec4, + 0x1ec500001ec6, + 0x1ec700001ec8, + 0x1ec900001eca, + 0x1ecb00001ecc, + 0x1ecd00001ece, + 0x1ecf00001ed0, + 0x1ed100001ed2, + 0x1ed300001ed4, + 0x1ed500001ed6, + 0x1ed700001ed8, + 0x1ed900001eda, + 0x1edb00001edc, + 0x1edd00001ede, + 0x1edf00001ee0, + 0x1ee100001ee2, + 0x1ee300001ee4, + 0x1ee500001ee6, + 0x1ee700001ee8, + 0x1ee900001eea, + 0x1eeb00001eec, + 0x1eed00001eee, + 0x1eef00001ef0, + 0x1ef100001ef2, + 0x1ef300001ef4, + 0x1ef500001ef6, + 0x1ef700001ef8, + 0x1ef900001efa, + 0x1efb00001efc, + 0x1efd00001efe, + 0x1eff00001f08, + 0x1f1000001f16, + 0x1f2000001f28, + 0x1f3000001f38, + 0x1f4000001f46, + 0x1f5000001f58, + 0x1f6000001f68, + 0x1f7000001f71, + 0x1f7200001f73, + 0x1f7400001f75, + 0x1f7600001f77, + 0x1f7800001f79, + 0x1f7a00001f7b, + 0x1f7c00001f7d, + 0x1fb000001fb2, + 0x1fb600001fb7, + 0x1fc600001fc7, + 0x1fd000001fd3, + 0x1fd600001fd8, + 0x1fe000001fe3, + 0x1fe400001fe8, + 0x1ff600001ff7, + 0x214e0000214f, + 0x218400002185, + 0x2c3000002c60, + 0x2c6100002c62, + 0x2c6500002c67, + 0x2c6800002c69, + 0x2c6a00002c6b, + 0x2c6c00002c6d, + 0x2c7100002c72, + 0x2c7300002c75, + 0x2c7600002c7c, + 0x2c8100002c82, + 0x2c8300002c84, + 0x2c8500002c86, + 0x2c8700002c88, + 0x2c8900002c8a, + 0x2c8b00002c8c, + 0x2c8d00002c8e, + 0x2c8f00002c90, + 0x2c9100002c92, + 0x2c9300002c94, + 0x2c9500002c96, + 0x2c9700002c98, + 0x2c9900002c9a, + 0x2c9b00002c9c, + 0x2c9d00002c9e, + 0x2c9f00002ca0, + 0x2ca100002ca2, + 0x2ca300002ca4, + 0x2ca500002ca6, + 0x2ca700002ca8, + 0x2ca900002caa, + 0x2cab00002cac, + 0x2cad00002cae, + 0x2caf00002cb0, + 0x2cb100002cb2, + 0x2cb300002cb4, + 0x2cb500002cb6, + 0x2cb700002cb8, + 0x2cb900002cba, + 0x2cbb00002cbc, + 0x2cbd00002cbe, + 0x2cbf00002cc0, + 0x2cc100002cc2, + 0x2cc300002cc4, + 0x2cc500002cc6, + 0x2cc700002cc8, + 0x2cc900002cca, + 0x2ccb00002ccc, + 0x2ccd00002cce, + 0x2ccf00002cd0, + 0x2cd100002cd2, + 0x2cd300002cd4, + 0x2cd500002cd6, + 0x2cd700002cd8, + 0x2cd900002cda, + 0x2cdb00002cdc, + 0x2cdd00002cde, + 0x2cdf00002ce0, + 0x2ce100002ce2, + 0x2ce300002ce5, + 0x2cec00002ced, + 0x2cee00002cf2, + 0x2cf300002cf4, + 0x2d0000002d26, + 0x2d2700002d28, + 0x2d2d00002d2e, + 0x2d3000002d68, + 0x2d7f00002d97, + 0x2da000002da7, + 0x2da800002daf, + 0x2db000002db7, + 0x2db800002dbf, + 0x2dc000002dc7, + 0x2dc800002dcf, + 0x2dd000002dd7, + 0x2dd800002ddf, + 0x2de000002e00, + 0x2e2f00002e30, + 0x300500003008, + 0x302a0000302e, + 0x303c0000303d, + 0x304100003097, + 0x30990000309b, + 0x309d0000309f, + 0x30a1000030fb, + 0x30fc000030ff, + 0x310500003130, + 0x31a0000031c0, + 0x31f000003200, + 0x340000004dc0, + 0x4e000000a48d, + 0xa4d00000a4fe, + 0xa5000000a60d, + 0xa6100000a62c, + 0xa6410000a642, + 0xa6430000a644, + 0xa6450000a646, + 0xa6470000a648, + 0xa6490000a64a, + 0xa64b0000a64c, + 0xa64d0000a64e, + 0xa64f0000a650, + 0xa6510000a652, + 0xa6530000a654, + 0xa6550000a656, + 0xa6570000a658, + 0xa6590000a65a, + 0xa65b0000a65c, + 0xa65d0000a65e, + 0xa65f0000a660, + 0xa6610000a662, + 0xa6630000a664, + 0xa6650000a666, + 0xa6670000a668, + 0xa6690000a66a, + 0xa66b0000a66c, + 0xa66d0000a670, + 0xa6740000a67e, + 0xa67f0000a680, + 0xa6810000a682, + 0xa6830000a684, + 0xa6850000a686, + 0xa6870000a688, + 0xa6890000a68a, + 0xa68b0000a68c, + 0xa68d0000a68e, + 0xa68f0000a690, + 0xa6910000a692, + 0xa6930000a694, + 0xa6950000a696, + 0xa6970000a698, + 0xa6990000a69a, + 0xa69b0000a69c, + 0xa69e0000a6e6, + 0xa6f00000a6f2, + 0xa7170000a720, + 0xa7230000a724, + 0xa7250000a726, + 0xa7270000a728, + 0xa7290000a72a, + 0xa72b0000a72c, + 0xa72d0000a72e, + 0xa72f0000a732, + 0xa7330000a734, + 0xa7350000a736, + 0xa7370000a738, + 0xa7390000a73a, + 0xa73b0000a73c, + 0xa73d0000a73e, + 0xa73f0000a740, + 0xa7410000a742, + 0xa7430000a744, + 0xa7450000a746, + 0xa7470000a748, + 0xa7490000a74a, + 0xa74b0000a74c, + 0xa74d0000a74e, + 0xa74f0000a750, + 0xa7510000a752, + 0xa7530000a754, + 0xa7550000a756, + 0xa7570000a758, + 0xa7590000a75a, + 0xa75b0000a75c, + 0xa75d0000a75e, + 0xa75f0000a760, + 0xa7610000a762, + 0xa7630000a764, + 0xa7650000a766, + 0xa7670000a768, + 0xa7690000a76a, + 0xa76b0000a76c, + 0xa76d0000a76e, + 0xa76f0000a770, + 0xa7710000a779, + 0xa77a0000a77b, + 0xa77c0000a77d, + 0xa77f0000a780, + 0xa7810000a782, + 0xa7830000a784, + 0xa7850000a786, + 0xa7870000a789, + 0xa78c0000a78d, + 0xa78e0000a790, + 0xa7910000a792, + 0xa7930000a796, + 0xa7970000a798, + 0xa7990000a79a, + 0xa79b0000a79c, + 0xa79d0000a79e, + 0xa79f0000a7a0, + 0xa7a10000a7a2, + 0xa7a30000a7a4, + 0xa7a50000a7a6, + 0xa7a70000a7a8, + 0xa7a90000a7aa, + 0xa7af0000a7b0, + 0xa7b50000a7b6, + 0xa7b70000a7b8, + 0xa7b90000a7ba, + 0xa7bb0000a7bc, + 0xa7bd0000a7be, + 0xa7bf0000a7c0, + 0xa7c10000a7c2, + 0xa7c30000a7c4, + 0xa7c80000a7c9, + 0xa7ca0000a7cb, + 0xa7d10000a7d2, + 0xa7d30000a7d4, + 0xa7d50000a7d6, + 0xa7d70000a7d8, + 0xa7d90000a7da, + 0xa7f60000a7f8, + 0xa7fa0000a828, + 0xa82c0000a82d, + 0xa8400000a874, + 0xa8800000a8c6, + 0xa8d00000a8da, + 0xa8e00000a8f8, + 0xa8fb0000a8fc, + 0xa8fd0000a92e, + 0xa9300000a954, + 0xa9800000a9c1, + 0xa9cf0000a9da, + 0xa9e00000a9ff, + 0xaa000000aa37, + 0xaa400000aa4e, + 0xaa500000aa5a, + 0xaa600000aa77, + 0xaa7a0000aac3, + 0xaadb0000aade, + 0xaae00000aaf0, + 0xaaf20000aaf7, + 0xab010000ab07, + 0xab090000ab0f, + 0xab110000ab17, + 0xab200000ab27, + 0xab280000ab2f, + 0xab300000ab5b, + 0xab600000ab69, + 0xabc00000abeb, + 0xabec0000abee, + 0xabf00000abfa, + 0xac000000d7a4, + 0xfa0e0000fa10, + 0xfa110000fa12, + 0xfa130000fa15, + 0xfa1f0000fa20, + 0xfa210000fa22, + 0xfa230000fa25, + 0xfa270000fa2a, + 0xfb1e0000fb1f, + 0xfe200000fe30, + 0xfe730000fe74, + 0x100000001000c, + 0x1000d00010027, + 0x100280001003b, + 0x1003c0001003e, + 0x1003f0001004e, + 0x100500001005e, + 0x10080000100fb, + 0x101fd000101fe, + 0x102800001029d, + 0x102a0000102d1, + 0x102e0000102e1, + 0x1030000010320, + 0x1032d00010341, + 0x103420001034a, + 0x103500001037b, + 0x103800001039e, + 0x103a0000103c4, + 0x103c8000103d0, + 0x104280001049e, + 0x104a0000104aa, + 0x104d8000104fc, + 0x1050000010528, + 0x1053000010564, + 0x10597000105a2, + 0x105a3000105b2, + 0x105b3000105ba, + 0x105bb000105bd, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1078000010781, + 0x1080000010806, + 0x1080800010809, + 0x1080a00010836, + 0x1083700010839, + 0x1083c0001083d, + 0x1083f00010856, + 0x1086000010877, + 0x108800001089f, + 0x108e0000108f3, + 0x108f4000108f6, + 0x1090000010916, + 0x109200001093a, + 0x10980000109b8, + 0x109be000109c0, + 0x10a0000010a04, + 0x10a0500010a07, + 0x10a0c00010a14, + 0x10a1500010a18, + 0x10a1900010a36, + 0x10a3800010a3b, + 0x10a3f00010a40, + 0x10a6000010a7d, + 0x10a8000010a9d, + 0x10ac000010ac8, + 0x10ac900010ae7, + 0x10b0000010b36, + 0x10b4000010b56, + 0x10b6000010b73, + 0x10b8000010b92, + 0x10c0000010c49, + 0x10cc000010cf3, + 0x10d0000010d28, + 0x10d3000010d3a, + 0x10e8000010eaa, + 0x10eab00010ead, + 0x10eb000010eb2, + 0x10efd00010f1d, + 0x10f2700010f28, + 0x10f3000010f51, + 0x10f7000010f86, + 0x10fb000010fc5, + 0x10fe000010ff7, + 0x1100000011047, + 0x1106600011076, + 0x1107f000110bb, + 0x110c2000110c3, + 0x110d0000110e9, + 0x110f0000110fa, + 0x1110000011135, + 0x1113600011140, + 0x1114400011148, + 0x1115000011174, + 0x1117600011177, + 0x11180000111c5, + 0x111c9000111cd, + 0x111ce000111db, + 0x111dc000111dd, + 0x1120000011212, + 0x1121300011238, + 0x1123e00011242, + 0x1128000011287, + 0x1128800011289, + 0x1128a0001128e, + 0x1128f0001129e, + 0x1129f000112a9, + 0x112b0000112eb, + 0x112f0000112fa, + 0x1130000011304, + 0x113050001130d, + 0x1130f00011311, + 0x1131300011329, + 0x1132a00011331, + 0x1133200011334, + 0x113350001133a, + 0x1133b00011345, + 0x1134700011349, + 0x1134b0001134e, + 0x1135000011351, + 0x1135700011358, + 0x1135d00011364, + 0x113660001136d, + 0x1137000011375, + 0x114000001144b, + 0x114500001145a, + 0x1145e00011462, + 0x11480000114c6, + 0x114c7000114c8, + 0x114d0000114da, + 0x11580000115b6, + 0x115b8000115c1, + 0x115d8000115de, + 0x1160000011641, + 0x1164400011645, + 0x116500001165a, + 0x11680000116b9, + 0x116c0000116ca, + 0x117000001171b, + 0x1171d0001172c, + 0x117300001173a, + 0x1174000011747, + 0x118000001183b, + 0x118c0000118ea, + 0x118ff00011907, + 0x119090001190a, + 0x1190c00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193b00011944, + 0x119500001195a, + 0x119a0000119a8, + 0x119aa000119d8, + 0x119da000119e2, + 0x119e3000119e5, + 0x11a0000011a3f, + 0x11a4700011a48, + 0x11a5000011a9a, + 0x11a9d00011a9e, + 0x11ab000011af9, + 0x11c0000011c09, + 0x11c0a00011c37, + 0x11c3800011c41, + 0x11c5000011c5a, + 0x11c7200011c90, + 0x11c9200011ca8, + 0x11ca900011cb7, + 0x11d0000011d07, + 0x11d0800011d0a, + 0x11d0b00011d37, + 0x11d3a00011d3b, + 0x11d3c00011d3e, + 0x11d3f00011d48, + 0x11d5000011d5a, + 0x11d6000011d66, + 0x11d6700011d69, + 0x11d6a00011d8f, + 0x11d9000011d92, + 0x11d9300011d99, + 0x11da000011daa, + 0x11ee000011ef7, + 0x11f0000011f11, + 0x11f1200011f3b, + 0x11f3e00011f43, + 0x11f5000011f5a, + 0x11fb000011fb1, + 0x120000001239a, + 0x1248000012544, + 0x12f9000012ff1, + 0x1300000013430, + 0x1344000013456, + 0x1440000014647, + 0x1680000016a39, + 0x16a4000016a5f, + 0x16a6000016a6a, + 0x16a7000016abf, + 0x16ac000016aca, + 0x16ad000016aee, + 0x16af000016af5, + 0x16b0000016b37, + 0x16b4000016b44, + 0x16b5000016b5a, + 0x16b6300016b78, + 0x16b7d00016b90, + 0x16e6000016e80, + 0x16f0000016f4b, + 0x16f4f00016f88, + 0x16f8f00016fa0, + 0x16fe000016fe2, + 0x16fe300016fe5, + 0x16ff000016ff2, + 0x17000000187f8, + 0x1880000018cd6, + 0x18d0000018d09, + 0x1aff00001aff4, + 0x1aff50001affc, + 0x1affd0001afff, + 0x1b0000001b123, + 0x1b1320001b133, + 0x1b1500001b153, + 0x1b1550001b156, + 0x1b1640001b168, + 0x1b1700001b2fc, + 0x1bc000001bc6b, + 0x1bc700001bc7d, + 0x1bc800001bc89, + 0x1bc900001bc9a, + 0x1bc9d0001bc9f, + 0x1cf000001cf2e, + 0x1cf300001cf47, + 0x1da000001da37, + 0x1da3b0001da6d, + 0x1da750001da76, + 0x1da840001da85, + 0x1da9b0001daa0, + 0x1daa10001dab0, + 0x1df000001df1f, + 0x1df250001df2b, + 0x1e0000001e007, + 0x1e0080001e019, + 0x1e01b0001e022, + 0x1e0230001e025, + 0x1e0260001e02b, + 0x1e08f0001e090, + 0x1e1000001e12d, + 0x1e1300001e13e, + 0x1e1400001e14a, + 0x1e14e0001e14f, + 0x1e2900001e2af, + 0x1e2c00001e2fa, + 0x1e4d00001e4fa, + 0x1e7e00001e7e7, + 0x1e7e80001e7ec, + 0x1e7ed0001e7ef, + 0x1e7f00001e7ff, + 0x1e8000001e8c5, + 0x1e8d00001e8d7, + 0x1e9220001e94c, + 0x1e9500001e95a, + 0x200000002a6e0, + 0x2a7000002b73a, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x2ebf00002ee5e, + 0x300000003134b, + 0x31350000323b0, + ), + 'CONTEXTJ': ( + 0x200c0000200e, + ), + 'CONTEXTO': ( + 0xb7000000b8, + 0x37500000376, + 0x5f3000005f5, + 0x6600000066a, + 0x6f0000006fa, + 0x30fb000030fc, + ), +} diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/intranges.py b/.pythonlibs/lib/python3.10/site-packages/idna/intranges.py new file mode 100644 index 0000000..6a43b04 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/intranges.py @@ -0,0 +1,54 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect +from typing import List, Tuple + +def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i+1 < len(sorted_list): + if sorted_list[i] == sorted_list[i+1]-1: + continue + current_range = sorted_list[last_write+1:i+1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + +def _encode_range(start: int, end: int) -> int: + return (start << 32) | end + +def _decode_range(r: int) -> Tuple[int, int]: + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos-1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/package_data.py b/.pythonlibs/lib/python3.10/site-packages/idna/package_data.py new file mode 100644 index 0000000..ed81113 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/package_data.py @@ -0,0 +1,2 @@ +__version__ = '3.7' + diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/py.typed b/.pythonlibs/lib/python3.10/site-packages/idna/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/idna/uts46data.py b/.pythonlibs/lib/python3.10/site-packages/idna/uts46data.py new file mode 100644 index 0000000..6a1eddb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/idna/uts46data.py @@ -0,0 +1,8598 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +from typing import List, Tuple, Union + + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = '15.1.0' +def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x0, '3'), + (0x1, '3'), + (0x2, '3'), + (0x3, '3'), + (0x4, '3'), + (0x5, '3'), + (0x6, '3'), + (0x7, '3'), + (0x8, '3'), + (0x9, '3'), + (0xA, '3'), + (0xB, '3'), + (0xC, '3'), + (0xD, '3'), + (0xE, '3'), + (0xF, '3'), + (0x10, '3'), + (0x11, '3'), + (0x12, '3'), + (0x13, '3'), + (0x14, '3'), + (0x15, '3'), + (0x16, '3'), + (0x17, '3'), + (0x18, '3'), + (0x19, '3'), + (0x1A, '3'), + (0x1B, '3'), + (0x1C, '3'), + (0x1D, '3'), + (0x1E, '3'), + (0x1F, '3'), + (0x20, '3'), + (0x21, '3'), + (0x22, '3'), + (0x23, '3'), + (0x24, '3'), + (0x25, '3'), + (0x26, '3'), + (0x27, '3'), + (0x28, '3'), + (0x29, '3'), + (0x2A, '3'), + (0x2B, '3'), + (0x2C, '3'), + (0x2D, 'V'), + (0x2E, 'V'), + (0x2F, '3'), + (0x30, 'V'), + (0x31, 'V'), + (0x32, 'V'), + (0x33, 'V'), + (0x34, 'V'), + (0x35, 'V'), + (0x36, 'V'), + (0x37, 'V'), + (0x38, 'V'), + (0x39, 'V'), + (0x3A, '3'), + (0x3B, '3'), + (0x3C, '3'), + (0x3D, '3'), + (0x3E, '3'), + (0x3F, '3'), + (0x40, '3'), + (0x41, 'M', 'a'), + (0x42, 'M', 'b'), + (0x43, 'M', 'c'), + (0x44, 'M', 'd'), + (0x45, 'M', 'e'), + (0x46, 'M', 'f'), + (0x47, 'M', 'g'), + (0x48, 'M', 'h'), + (0x49, 'M', 'i'), + (0x4A, 'M', 'j'), + (0x4B, 'M', 'k'), + (0x4C, 'M', 'l'), + (0x4D, 'M', 'm'), + (0x4E, 'M', 'n'), + (0x4F, 'M', 'o'), + (0x50, 'M', 'p'), + (0x51, 'M', 'q'), + (0x52, 'M', 'r'), + (0x53, 'M', 's'), + (0x54, 'M', 't'), + (0x55, 'M', 'u'), + (0x56, 'M', 'v'), + (0x57, 'M', 'w'), + (0x58, 'M', 'x'), + (0x59, 'M', 'y'), + (0x5A, 'M', 'z'), + (0x5B, '3'), + (0x5C, '3'), + (0x5D, '3'), + (0x5E, '3'), + (0x5F, '3'), + (0x60, '3'), + (0x61, 'V'), + (0x62, 'V'), + (0x63, 'V'), + ] + +def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x64, 'V'), + (0x65, 'V'), + (0x66, 'V'), + (0x67, 'V'), + (0x68, 'V'), + (0x69, 'V'), + (0x6A, 'V'), + (0x6B, 'V'), + (0x6C, 'V'), + (0x6D, 'V'), + (0x6E, 'V'), + (0x6F, 'V'), + (0x70, 'V'), + (0x71, 'V'), + (0x72, 'V'), + (0x73, 'V'), + (0x74, 'V'), + (0x75, 'V'), + (0x76, 'V'), + (0x77, 'V'), + (0x78, 'V'), + (0x79, 'V'), + (0x7A, 'V'), + (0x7B, '3'), + (0x7C, '3'), + (0x7D, '3'), + (0x7E, '3'), + (0x7F, '3'), + (0x80, 'X'), + (0x81, 'X'), + (0x82, 'X'), + (0x83, 'X'), + (0x84, 'X'), + (0x85, 'X'), + (0x86, 'X'), + (0x87, 'X'), + (0x88, 'X'), + (0x89, 'X'), + (0x8A, 'X'), + (0x8B, 'X'), + (0x8C, 'X'), + (0x8D, 'X'), + (0x8E, 'X'), + (0x8F, 'X'), + (0x90, 'X'), + (0x91, 'X'), + (0x92, 'X'), + (0x93, 'X'), + (0x94, 'X'), + (0x95, 'X'), + (0x96, 'X'), + (0x97, 'X'), + (0x98, 'X'), + (0x99, 'X'), + (0x9A, 'X'), + (0x9B, 'X'), + (0x9C, 'X'), + (0x9D, 'X'), + (0x9E, 'X'), + (0x9F, 'X'), + (0xA0, '3', ' '), + (0xA1, 'V'), + (0xA2, 'V'), + (0xA3, 'V'), + (0xA4, 'V'), + (0xA5, 'V'), + (0xA6, 'V'), + (0xA7, 'V'), + (0xA8, '3', ' ̈'), + (0xA9, 'V'), + (0xAA, 'M', 'a'), + (0xAB, 'V'), + (0xAC, 'V'), + (0xAD, 'I'), + (0xAE, 'V'), + (0xAF, '3', ' ̄'), + (0xB0, 'V'), + (0xB1, 'V'), + (0xB2, 'M', '2'), + (0xB3, 'M', '3'), + (0xB4, '3', ' ́'), + (0xB5, 'M', 'μ'), + (0xB6, 'V'), + (0xB7, 'V'), + (0xB8, '3', ' ̧'), + (0xB9, 'M', '1'), + (0xBA, 'M', 'o'), + (0xBB, 'V'), + (0xBC, 'M', '1⁄4'), + (0xBD, 'M', '1⁄2'), + (0xBE, 'M', '3⁄4'), + (0xBF, 'V'), + (0xC0, 'M', 'à'), + (0xC1, 'M', 'á'), + (0xC2, 'M', 'â'), + (0xC3, 'M', 'ã'), + (0xC4, 'M', 'ä'), + (0xC5, 'M', 'å'), + (0xC6, 'M', 'æ'), + (0xC7, 'M', 'ç'), + ] + +def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC8, 'M', 'è'), + (0xC9, 'M', 'é'), + (0xCA, 'M', 'ê'), + (0xCB, 'M', 'ë'), + (0xCC, 'M', 'ì'), + (0xCD, 'M', 'í'), + (0xCE, 'M', 'î'), + (0xCF, 'M', 'ï'), + (0xD0, 'M', 'ð'), + (0xD1, 'M', 'ñ'), + (0xD2, 'M', 'ò'), + (0xD3, 'M', 'ó'), + (0xD4, 'M', 'ô'), + (0xD5, 'M', 'õ'), + (0xD6, 'M', 'ö'), + (0xD7, 'V'), + (0xD8, 'M', 'ø'), + (0xD9, 'M', 'ù'), + (0xDA, 'M', 'ú'), + (0xDB, 'M', 'û'), + (0xDC, 'M', 'ü'), + (0xDD, 'M', 'ý'), + (0xDE, 'M', 'þ'), + (0xDF, 'D', 'ss'), + (0xE0, 'V'), + (0xE1, 'V'), + (0xE2, 'V'), + (0xE3, 'V'), + (0xE4, 'V'), + (0xE5, 'V'), + (0xE6, 'V'), + (0xE7, 'V'), + (0xE8, 'V'), + (0xE9, 'V'), + (0xEA, 'V'), + (0xEB, 'V'), + (0xEC, 'V'), + (0xED, 'V'), + (0xEE, 'V'), + (0xEF, 'V'), + (0xF0, 'V'), + (0xF1, 'V'), + (0xF2, 'V'), + (0xF3, 'V'), + (0xF4, 'V'), + (0xF5, 'V'), + (0xF6, 'V'), + (0xF7, 'V'), + (0xF8, 'V'), + (0xF9, 'V'), + (0xFA, 'V'), + (0xFB, 'V'), + (0xFC, 'V'), + (0xFD, 'V'), + (0xFE, 'V'), + (0xFF, 'V'), + (0x100, 'M', 'ā'), + (0x101, 'V'), + (0x102, 'M', 'ă'), + (0x103, 'V'), + (0x104, 'M', 'ą'), + (0x105, 'V'), + (0x106, 'M', 'ć'), + (0x107, 'V'), + (0x108, 'M', 'ĉ'), + (0x109, 'V'), + (0x10A, 'M', 'ċ'), + (0x10B, 'V'), + (0x10C, 'M', 'č'), + (0x10D, 'V'), + (0x10E, 'M', 'ď'), + (0x10F, 'V'), + (0x110, 'M', 'đ'), + (0x111, 'V'), + (0x112, 'M', 'ē'), + (0x113, 'V'), + (0x114, 'M', 'ĕ'), + (0x115, 'V'), + (0x116, 'M', 'ė'), + (0x117, 'V'), + (0x118, 'M', 'ę'), + (0x119, 'V'), + (0x11A, 'M', 'ě'), + (0x11B, 'V'), + (0x11C, 'M', 'ĝ'), + (0x11D, 'V'), + (0x11E, 'M', 'ğ'), + (0x11F, 'V'), + (0x120, 'M', 'ġ'), + (0x121, 'V'), + (0x122, 'M', 'ģ'), + (0x123, 'V'), + (0x124, 'M', 'ĥ'), + (0x125, 'V'), + (0x126, 'M', 'ħ'), + (0x127, 'V'), + (0x128, 'M', 'ĩ'), + (0x129, 'V'), + (0x12A, 'M', 'ī'), + (0x12B, 'V'), + ] + +def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x12C, 'M', 'ĭ'), + (0x12D, 'V'), + (0x12E, 'M', 'į'), + (0x12F, 'V'), + (0x130, 'M', 'i̇'), + (0x131, 'V'), + (0x132, 'M', 'ij'), + (0x134, 'M', 'ĵ'), + (0x135, 'V'), + (0x136, 'M', 'ķ'), + (0x137, 'V'), + (0x139, 'M', 'ĺ'), + (0x13A, 'V'), + (0x13B, 'M', 'ļ'), + (0x13C, 'V'), + (0x13D, 'M', 'ľ'), + (0x13E, 'V'), + (0x13F, 'M', 'l·'), + (0x141, 'M', 'ł'), + (0x142, 'V'), + (0x143, 'M', 'ń'), + (0x144, 'V'), + (0x145, 'M', 'ņ'), + (0x146, 'V'), + (0x147, 'M', 'ň'), + (0x148, 'V'), + (0x149, 'M', 'ʼn'), + (0x14A, 'M', 'ŋ'), + (0x14B, 'V'), + (0x14C, 'M', 'ō'), + (0x14D, 'V'), + (0x14E, 'M', 'ŏ'), + (0x14F, 'V'), + (0x150, 'M', 'ő'), + (0x151, 'V'), + (0x152, 'M', 'œ'), + (0x153, 'V'), + (0x154, 'M', 'ŕ'), + (0x155, 'V'), + (0x156, 'M', 'ŗ'), + (0x157, 'V'), + (0x158, 'M', 'ř'), + (0x159, 'V'), + (0x15A, 'M', 'ś'), + (0x15B, 'V'), + (0x15C, 'M', 'ŝ'), + (0x15D, 'V'), + (0x15E, 'M', 'ş'), + (0x15F, 'V'), + (0x160, 'M', 'š'), + (0x161, 'V'), + (0x162, 'M', 'ţ'), + (0x163, 'V'), + (0x164, 'M', 'ť'), + (0x165, 'V'), + (0x166, 'M', 'ŧ'), + (0x167, 'V'), + (0x168, 'M', 'ũ'), + (0x169, 'V'), + (0x16A, 'M', 'ū'), + (0x16B, 'V'), + (0x16C, 'M', 'ŭ'), + (0x16D, 'V'), + (0x16E, 'M', 'ů'), + (0x16F, 'V'), + (0x170, 'M', 'ű'), + (0x171, 'V'), + (0x172, 'M', 'ų'), + (0x173, 'V'), + (0x174, 'M', 'ŵ'), + (0x175, 'V'), + (0x176, 'M', 'ŷ'), + (0x177, 'V'), + (0x178, 'M', 'ÿ'), + (0x179, 'M', 'ź'), + (0x17A, 'V'), + (0x17B, 'M', 'ż'), + (0x17C, 'V'), + (0x17D, 'M', 'ž'), + (0x17E, 'V'), + (0x17F, 'M', 's'), + (0x180, 'V'), + (0x181, 'M', 'ɓ'), + (0x182, 'M', 'ƃ'), + (0x183, 'V'), + (0x184, 'M', 'ƅ'), + (0x185, 'V'), + (0x186, 'M', 'ɔ'), + (0x187, 'M', 'ƈ'), + (0x188, 'V'), + (0x189, 'M', 'ɖ'), + (0x18A, 'M', 'ɗ'), + (0x18B, 'M', 'ƌ'), + (0x18C, 'V'), + (0x18E, 'M', 'ǝ'), + (0x18F, 'M', 'ə'), + (0x190, 'M', 'ɛ'), + (0x191, 'M', 'ƒ'), + (0x192, 'V'), + (0x193, 'M', 'ɠ'), + ] + +def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x194, 'M', 'ɣ'), + (0x195, 'V'), + (0x196, 'M', 'ɩ'), + (0x197, 'M', 'ɨ'), + (0x198, 'M', 'ƙ'), + (0x199, 'V'), + (0x19C, 'M', 'ɯ'), + (0x19D, 'M', 'ɲ'), + (0x19E, 'V'), + (0x19F, 'M', 'ɵ'), + (0x1A0, 'M', 'ơ'), + (0x1A1, 'V'), + (0x1A2, 'M', 'ƣ'), + (0x1A3, 'V'), + (0x1A4, 'M', 'ƥ'), + (0x1A5, 'V'), + (0x1A6, 'M', 'ʀ'), + (0x1A7, 'M', 'ƨ'), + (0x1A8, 'V'), + (0x1A9, 'M', 'ʃ'), + (0x1AA, 'V'), + (0x1AC, 'M', 'ƭ'), + (0x1AD, 'V'), + (0x1AE, 'M', 'ʈ'), + (0x1AF, 'M', 'ư'), + (0x1B0, 'V'), + (0x1B1, 'M', 'ʊ'), + (0x1B2, 'M', 'ʋ'), + (0x1B3, 'M', 'ƴ'), + (0x1B4, 'V'), + (0x1B5, 'M', 'ƶ'), + (0x1B6, 'V'), + (0x1B7, 'M', 'ʒ'), + (0x1B8, 'M', 'ƹ'), + (0x1B9, 'V'), + (0x1BC, 'M', 'ƽ'), + (0x1BD, 'V'), + (0x1C4, 'M', 'dž'), + (0x1C7, 'M', 'lj'), + (0x1CA, 'M', 'nj'), + (0x1CD, 'M', 'ǎ'), + (0x1CE, 'V'), + (0x1CF, 'M', 'ǐ'), + (0x1D0, 'V'), + (0x1D1, 'M', 'ǒ'), + (0x1D2, 'V'), + (0x1D3, 'M', 'ǔ'), + (0x1D4, 'V'), + (0x1D5, 'M', 'ǖ'), + (0x1D6, 'V'), + (0x1D7, 'M', 'ǘ'), + (0x1D8, 'V'), + (0x1D9, 'M', 'ǚ'), + (0x1DA, 'V'), + (0x1DB, 'M', 'ǜ'), + (0x1DC, 'V'), + (0x1DE, 'M', 'ǟ'), + (0x1DF, 'V'), + (0x1E0, 'M', 'ǡ'), + (0x1E1, 'V'), + (0x1E2, 'M', 'ǣ'), + (0x1E3, 'V'), + (0x1E4, 'M', 'ǥ'), + (0x1E5, 'V'), + (0x1E6, 'M', 'ǧ'), + (0x1E7, 'V'), + (0x1E8, 'M', 'ǩ'), + (0x1E9, 'V'), + (0x1EA, 'M', 'ǫ'), + (0x1EB, 'V'), + (0x1EC, 'M', 'ǭ'), + (0x1ED, 'V'), + (0x1EE, 'M', 'ǯ'), + (0x1EF, 'V'), + (0x1F1, 'M', 'dz'), + (0x1F4, 'M', 'ǵ'), + (0x1F5, 'V'), + (0x1F6, 'M', 'ƕ'), + (0x1F7, 'M', 'ƿ'), + (0x1F8, 'M', 'ǹ'), + (0x1F9, 'V'), + (0x1FA, 'M', 'ǻ'), + (0x1FB, 'V'), + (0x1FC, 'M', 'ǽ'), + (0x1FD, 'V'), + (0x1FE, 'M', 'ǿ'), + (0x1FF, 'V'), + (0x200, 'M', 'ȁ'), + (0x201, 'V'), + (0x202, 'M', 'ȃ'), + (0x203, 'V'), + (0x204, 'M', 'ȅ'), + (0x205, 'V'), + (0x206, 'M', 'ȇ'), + (0x207, 'V'), + (0x208, 'M', 'ȉ'), + (0x209, 'V'), + (0x20A, 'M', 'ȋ'), + (0x20B, 'V'), + (0x20C, 'M', 'ȍ'), + ] + +def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x20D, 'V'), + (0x20E, 'M', 'ȏ'), + (0x20F, 'V'), + (0x210, 'M', 'ȑ'), + (0x211, 'V'), + (0x212, 'M', 'ȓ'), + (0x213, 'V'), + (0x214, 'M', 'ȕ'), + (0x215, 'V'), + (0x216, 'M', 'ȗ'), + (0x217, 'V'), + (0x218, 'M', 'ș'), + (0x219, 'V'), + (0x21A, 'M', 'ț'), + (0x21B, 'V'), + (0x21C, 'M', 'ȝ'), + (0x21D, 'V'), + (0x21E, 'M', 'ȟ'), + (0x21F, 'V'), + (0x220, 'M', 'ƞ'), + (0x221, 'V'), + (0x222, 'M', 'ȣ'), + (0x223, 'V'), + (0x224, 'M', 'ȥ'), + (0x225, 'V'), + (0x226, 'M', 'ȧ'), + (0x227, 'V'), + (0x228, 'M', 'ȩ'), + (0x229, 'V'), + (0x22A, 'M', 'ȫ'), + (0x22B, 'V'), + (0x22C, 'M', 'ȭ'), + (0x22D, 'V'), + (0x22E, 'M', 'ȯ'), + (0x22F, 'V'), + (0x230, 'M', 'ȱ'), + (0x231, 'V'), + (0x232, 'M', 'ȳ'), + (0x233, 'V'), + (0x23A, 'M', 'ⱥ'), + (0x23B, 'M', 'ȼ'), + (0x23C, 'V'), + (0x23D, 'M', 'ƚ'), + (0x23E, 'M', 'ⱦ'), + (0x23F, 'V'), + (0x241, 'M', 'ɂ'), + (0x242, 'V'), + (0x243, 'M', 'ƀ'), + (0x244, 'M', 'ʉ'), + (0x245, 'M', 'ʌ'), + (0x246, 'M', 'ɇ'), + (0x247, 'V'), + (0x248, 'M', 'ɉ'), + (0x249, 'V'), + (0x24A, 'M', 'ɋ'), + (0x24B, 'V'), + (0x24C, 'M', 'ɍ'), + (0x24D, 'V'), + (0x24E, 'M', 'ɏ'), + (0x24F, 'V'), + (0x2B0, 'M', 'h'), + (0x2B1, 'M', 'ɦ'), + (0x2B2, 'M', 'j'), + (0x2B3, 'M', 'r'), + (0x2B4, 'M', 'ɹ'), + (0x2B5, 'M', 'ɻ'), + (0x2B6, 'M', 'ʁ'), + (0x2B7, 'M', 'w'), + (0x2B8, 'M', 'y'), + (0x2B9, 'V'), + (0x2D8, '3', ' ̆'), + (0x2D9, '3', ' ̇'), + (0x2DA, '3', ' ̊'), + (0x2DB, '3', ' ̨'), + (0x2DC, '3', ' ̃'), + (0x2DD, '3', ' ̋'), + (0x2DE, 'V'), + (0x2E0, 'M', 'ɣ'), + (0x2E1, 'M', 'l'), + (0x2E2, 'M', 's'), + (0x2E3, 'M', 'x'), + (0x2E4, 'M', 'ʕ'), + (0x2E5, 'V'), + (0x340, 'M', '̀'), + (0x341, 'M', '́'), + (0x342, 'V'), + (0x343, 'M', '̓'), + (0x344, 'M', '̈́'), + (0x345, 'M', 'ι'), + (0x346, 'V'), + (0x34F, 'I'), + (0x350, 'V'), + (0x370, 'M', 'ͱ'), + (0x371, 'V'), + (0x372, 'M', 'ͳ'), + (0x373, 'V'), + (0x374, 'M', 'ʹ'), + (0x375, 'V'), + (0x376, 'M', 'ͷ'), + (0x377, 'V'), + ] + +def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x378, 'X'), + (0x37A, '3', ' ι'), + (0x37B, 'V'), + (0x37E, '3', ';'), + (0x37F, 'M', 'ϳ'), + (0x380, 'X'), + (0x384, '3', ' ́'), + (0x385, '3', ' ̈́'), + (0x386, 'M', 'ά'), + (0x387, 'M', '·'), + (0x388, 'M', 'έ'), + (0x389, 'M', 'ή'), + (0x38A, 'M', 'ί'), + (0x38B, 'X'), + (0x38C, 'M', 'ό'), + (0x38D, 'X'), + (0x38E, 'M', 'ύ'), + (0x38F, 'M', 'ώ'), + (0x390, 'V'), + (0x391, 'M', 'α'), + (0x392, 'M', 'β'), + (0x393, 'M', 'γ'), + (0x394, 'M', 'δ'), + (0x395, 'M', 'ε'), + (0x396, 'M', 'ζ'), + (0x397, 'M', 'η'), + (0x398, 'M', 'θ'), + (0x399, 'M', 'ι'), + (0x39A, 'M', 'κ'), + (0x39B, 'M', 'λ'), + (0x39C, 'M', 'μ'), + (0x39D, 'M', 'ν'), + (0x39E, 'M', 'ξ'), + (0x39F, 'M', 'ο'), + (0x3A0, 'M', 'π'), + (0x3A1, 'M', 'ρ'), + (0x3A2, 'X'), + (0x3A3, 'M', 'σ'), + (0x3A4, 'M', 'τ'), + (0x3A5, 'M', 'υ'), + (0x3A6, 'M', 'φ'), + (0x3A7, 'M', 'χ'), + (0x3A8, 'M', 'ψ'), + (0x3A9, 'M', 'ω'), + (0x3AA, 'M', 'ϊ'), + (0x3AB, 'M', 'ϋ'), + (0x3AC, 'V'), + (0x3C2, 'D', 'σ'), + (0x3C3, 'V'), + (0x3CF, 'M', 'ϗ'), + (0x3D0, 'M', 'β'), + (0x3D1, 'M', 'θ'), + (0x3D2, 'M', 'υ'), + (0x3D3, 'M', 'ύ'), + (0x3D4, 'M', 'ϋ'), + (0x3D5, 'M', 'φ'), + (0x3D6, 'M', 'π'), + (0x3D7, 'V'), + (0x3D8, 'M', 'ϙ'), + (0x3D9, 'V'), + (0x3DA, 'M', 'ϛ'), + (0x3DB, 'V'), + (0x3DC, 'M', 'ϝ'), + (0x3DD, 'V'), + (0x3DE, 'M', 'ϟ'), + (0x3DF, 'V'), + (0x3E0, 'M', 'ϡ'), + (0x3E1, 'V'), + (0x3E2, 'M', 'ϣ'), + (0x3E3, 'V'), + (0x3E4, 'M', 'ϥ'), + (0x3E5, 'V'), + (0x3E6, 'M', 'ϧ'), + (0x3E7, 'V'), + (0x3E8, 'M', 'ϩ'), + (0x3E9, 'V'), + (0x3EA, 'M', 'ϫ'), + (0x3EB, 'V'), + (0x3EC, 'M', 'ϭ'), + (0x3ED, 'V'), + (0x3EE, 'M', 'ϯ'), + (0x3EF, 'V'), + (0x3F0, 'M', 'κ'), + (0x3F1, 'M', 'ρ'), + (0x3F2, 'M', 'σ'), + (0x3F3, 'V'), + (0x3F4, 'M', 'θ'), + (0x3F5, 'M', 'ε'), + (0x3F6, 'V'), + (0x3F7, 'M', 'ϸ'), + (0x3F8, 'V'), + (0x3F9, 'M', 'σ'), + (0x3FA, 'M', 'ϻ'), + (0x3FB, 'V'), + (0x3FD, 'M', 'ͻ'), + (0x3FE, 'M', 'ͼ'), + (0x3FF, 'M', 'ͽ'), + (0x400, 'M', 'ѐ'), + (0x401, 'M', 'ё'), + (0x402, 'M', 'ђ'), + ] + +def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x403, 'M', 'ѓ'), + (0x404, 'M', 'є'), + (0x405, 'M', 'ѕ'), + (0x406, 'M', 'і'), + (0x407, 'M', 'ї'), + (0x408, 'M', 'ј'), + (0x409, 'M', 'љ'), + (0x40A, 'M', 'њ'), + (0x40B, 'M', 'ћ'), + (0x40C, 'M', 'ќ'), + (0x40D, 'M', 'ѝ'), + (0x40E, 'M', 'ў'), + (0x40F, 'M', 'џ'), + (0x410, 'M', 'а'), + (0x411, 'M', 'б'), + (0x412, 'M', 'в'), + (0x413, 'M', 'г'), + (0x414, 'M', 'д'), + (0x415, 'M', 'е'), + (0x416, 'M', 'ж'), + (0x417, 'M', 'з'), + (0x418, 'M', 'и'), + (0x419, 'M', 'й'), + (0x41A, 'M', 'к'), + (0x41B, 'M', 'л'), + (0x41C, 'M', 'м'), + (0x41D, 'M', 'н'), + (0x41E, 'M', 'о'), + (0x41F, 'M', 'п'), + (0x420, 'M', 'р'), + (0x421, 'M', 'с'), + (0x422, 'M', 'т'), + (0x423, 'M', 'у'), + (0x424, 'M', 'ф'), + (0x425, 'M', 'х'), + (0x426, 'M', 'ц'), + (0x427, 'M', 'ч'), + (0x428, 'M', 'ш'), + (0x429, 'M', 'щ'), + (0x42A, 'M', 'ъ'), + (0x42B, 'M', 'ы'), + (0x42C, 'M', 'ь'), + (0x42D, 'M', 'э'), + (0x42E, 'M', 'ю'), + (0x42F, 'M', 'я'), + (0x430, 'V'), + (0x460, 'M', 'ѡ'), + (0x461, 'V'), + (0x462, 'M', 'ѣ'), + (0x463, 'V'), + (0x464, 'M', 'ѥ'), + (0x465, 'V'), + (0x466, 'M', 'ѧ'), + (0x467, 'V'), + (0x468, 'M', 'ѩ'), + (0x469, 'V'), + (0x46A, 'M', 'ѫ'), + (0x46B, 'V'), + (0x46C, 'M', 'ѭ'), + (0x46D, 'V'), + (0x46E, 'M', 'ѯ'), + (0x46F, 'V'), + (0x470, 'M', 'ѱ'), + (0x471, 'V'), + (0x472, 'M', 'ѳ'), + (0x473, 'V'), + (0x474, 'M', 'ѵ'), + (0x475, 'V'), + (0x476, 'M', 'ѷ'), + (0x477, 'V'), + (0x478, 'M', 'ѹ'), + (0x479, 'V'), + (0x47A, 'M', 'ѻ'), + (0x47B, 'V'), + (0x47C, 'M', 'ѽ'), + (0x47D, 'V'), + (0x47E, 'M', 'ѿ'), + (0x47F, 'V'), + (0x480, 'M', 'ҁ'), + (0x481, 'V'), + (0x48A, 'M', 'ҋ'), + (0x48B, 'V'), + (0x48C, 'M', 'ҍ'), + (0x48D, 'V'), + (0x48E, 'M', 'ҏ'), + (0x48F, 'V'), + (0x490, 'M', 'ґ'), + (0x491, 'V'), + (0x492, 'M', 'ғ'), + (0x493, 'V'), + (0x494, 'M', 'ҕ'), + (0x495, 'V'), + (0x496, 'M', 'җ'), + (0x497, 'V'), + (0x498, 'M', 'ҙ'), + (0x499, 'V'), + (0x49A, 'M', 'қ'), + (0x49B, 'V'), + (0x49C, 'M', 'ҝ'), + (0x49D, 'V'), + ] + +def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x49E, 'M', 'ҟ'), + (0x49F, 'V'), + (0x4A0, 'M', 'ҡ'), + (0x4A1, 'V'), + (0x4A2, 'M', 'ң'), + (0x4A3, 'V'), + (0x4A4, 'M', 'ҥ'), + (0x4A5, 'V'), + (0x4A6, 'M', 'ҧ'), + (0x4A7, 'V'), + (0x4A8, 'M', 'ҩ'), + (0x4A9, 'V'), + (0x4AA, 'M', 'ҫ'), + (0x4AB, 'V'), + (0x4AC, 'M', 'ҭ'), + (0x4AD, 'V'), + (0x4AE, 'M', 'ү'), + (0x4AF, 'V'), + (0x4B0, 'M', 'ұ'), + (0x4B1, 'V'), + (0x4B2, 'M', 'ҳ'), + (0x4B3, 'V'), + (0x4B4, 'M', 'ҵ'), + (0x4B5, 'V'), + (0x4B6, 'M', 'ҷ'), + (0x4B7, 'V'), + (0x4B8, 'M', 'ҹ'), + (0x4B9, 'V'), + (0x4BA, 'M', 'һ'), + (0x4BB, 'V'), + (0x4BC, 'M', 'ҽ'), + (0x4BD, 'V'), + (0x4BE, 'M', 'ҿ'), + (0x4BF, 'V'), + (0x4C0, 'X'), + (0x4C1, 'M', 'ӂ'), + (0x4C2, 'V'), + (0x4C3, 'M', 'ӄ'), + (0x4C4, 'V'), + (0x4C5, 'M', 'ӆ'), + (0x4C6, 'V'), + (0x4C7, 'M', 'ӈ'), + (0x4C8, 'V'), + (0x4C9, 'M', 'ӊ'), + (0x4CA, 'V'), + (0x4CB, 'M', 'ӌ'), + (0x4CC, 'V'), + (0x4CD, 'M', 'ӎ'), + (0x4CE, 'V'), + (0x4D0, 'M', 'ӑ'), + (0x4D1, 'V'), + (0x4D2, 'M', 'ӓ'), + (0x4D3, 'V'), + (0x4D4, 'M', 'ӕ'), + (0x4D5, 'V'), + (0x4D6, 'M', 'ӗ'), + (0x4D7, 'V'), + (0x4D8, 'M', 'ә'), + (0x4D9, 'V'), + (0x4DA, 'M', 'ӛ'), + (0x4DB, 'V'), + (0x4DC, 'M', 'ӝ'), + (0x4DD, 'V'), + (0x4DE, 'M', 'ӟ'), + (0x4DF, 'V'), + (0x4E0, 'M', 'ӡ'), + (0x4E1, 'V'), + (0x4E2, 'M', 'ӣ'), + (0x4E3, 'V'), + (0x4E4, 'M', 'ӥ'), + (0x4E5, 'V'), + (0x4E6, 'M', 'ӧ'), + (0x4E7, 'V'), + (0x4E8, 'M', 'ө'), + (0x4E9, 'V'), + (0x4EA, 'M', 'ӫ'), + (0x4EB, 'V'), + (0x4EC, 'M', 'ӭ'), + (0x4ED, 'V'), + (0x4EE, 'M', 'ӯ'), + (0x4EF, 'V'), + (0x4F0, 'M', 'ӱ'), + (0x4F1, 'V'), + (0x4F2, 'M', 'ӳ'), + (0x4F3, 'V'), + (0x4F4, 'M', 'ӵ'), + (0x4F5, 'V'), + (0x4F6, 'M', 'ӷ'), + (0x4F7, 'V'), + (0x4F8, 'M', 'ӹ'), + (0x4F9, 'V'), + (0x4FA, 'M', 'ӻ'), + (0x4FB, 'V'), + (0x4FC, 'M', 'ӽ'), + (0x4FD, 'V'), + (0x4FE, 'M', 'ӿ'), + (0x4FF, 'V'), + (0x500, 'M', 'ԁ'), + (0x501, 'V'), + (0x502, 'M', 'ԃ'), + ] + +def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x503, 'V'), + (0x504, 'M', 'ԅ'), + (0x505, 'V'), + (0x506, 'M', 'ԇ'), + (0x507, 'V'), + (0x508, 'M', 'ԉ'), + (0x509, 'V'), + (0x50A, 'M', 'ԋ'), + (0x50B, 'V'), + (0x50C, 'M', 'ԍ'), + (0x50D, 'V'), + (0x50E, 'M', 'ԏ'), + (0x50F, 'V'), + (0x510, 'M', 'ԑ'), + (0x511, 'V'), + (0x512, 'M', 'ԓ'), + (0x513, 'V'), + (0x514, 'M', 'ԕ'), + (0x515, 'V'), + (0x516, 'M', 'ԗ'), + (0x517, 'V'), + (0x518, 'M', 'ԙ'), + (0x519, 'V'), + (0x51A, 'M', 'ԛ'), + (0x51B, 'V'), + (0x51C, 'M', 'ԝ'), + (0x51D, 'V'), + (0x51E, 'M', 'ԟ'), + (0x51F, 'V'), + (0x520, 'M', 'ԡ'), + (0x521, 'V'), + (0x522, 'M', 'ԣ'), + (0x523, 'V'), + (0x524, 'M', 'ԥ'), + (0x525, 'V'), + (0x526, 'M', 'ԧ'), + (0x527, 'V'), + (0x528, 'M', 'ԩ'), + (0x529, 'V'), + (0x52A, 'M', 'ԫ'), + (0x52B, 'V'), + (0x52C, 'M', 'ԭ'), + (0x52D, 'V'), + (0x52E, 'M', 'ԯ'), + (0x52F, 'V'), + (0x530, 'X'), + (0x531, 'M', 'ա'), + (0x532, 'M', 'բ'), + (0x533, 'M', 'գ'), + (0x534, 'M', 'դ'), + (0x535, 'M', 'ե'), + (0x536, 'M', 'զ'), + (0x537, 'M', 'է'), + (0x538, 'M', 'ը'), + (0x539, 'M', 'թ'), + (0x53A, 'M', 'ժ'), + (0x53B, 'M', 'ի'), + (0x53C, 'M', 'լ'), + (0x53D, 'M', 'խ'), + (0x53E, 'M', 'ծ'), + (0x53F, 'M', 'կ'), + (0x540, 'M', 'հ'), + (0x541, 'M', 'ձ'), + (0x542, 'M', 'ղ'), + (0x543, 'M', 'ճ'), + (0x544, 'M', 'մ'), + (0x545, 'M', 'յ'), + (0x546, 'M', 'ն'), + (0x547, 'M', 'շ'), + (0x548, 'M', 'ո'), + (0x549, 'M', 'չ'), + (0x54A, 'M', 'պ'), + (0x54B, 'M', 'ջ'), + (0x54C, 'M', 'ռ'), + (0x54D, 'M', 'ս'), + (0x54E, 'M', 'վ'), + (0x54F, 'M', 'տ'), + (0x550, 'M', 'ր'), + (0x551, 'M', 'ց'), + (0x552, 'M', 'ւ'), + (0x553, 'M', 'փ'), + (0x554, 'M', 'ք'), + (0x555, 'M', 'օ'), + (0x556, 'M', 'ֆ'), + (0x557, 'X'), + (0x559, 'V'), + (0x587, 'M', 'եւ'), + (0x588, 'V'), + (0x58B, 'X'), + (0x58D, 'V'), + (0x590, 'X'), + (0x591, 'V'), + (0x5C8, 'X'), + (0x5D0, 'V'), + (0x5EB, 'X'), + (0x5EF, 'V'), + (0x5F5, 'X'), + (0x606, 'V'), + (0x61C, 'X'), + (0x61D, 'V'), + ] + +def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x675, 'M', 'اٴ'), + (0x676, 'M', 'وٴ'), + (0x677, 'M', 'ۇٴ'), + (0x678, 'M', 'يٴ'), + (0x679, 'V'), + (0x6DD, 'X'), + (0x6DE, 'V'), + (0x70E, 'X'), + (0x710, 'V'), + (0x74B, 'X'), + (0x74D, 'V'), + (0x7B2, 'X'), + (0x7C0, 'V'), + (0x7FB, 'X'), + (0x7FD, 'V'), + (0x82E, 'X'), + (0x830, 'V'), + (0x83F, 'X'), + (0x840, 'V'), + (0x85C, 'X'), + (0x85E, 'V'), + (0x85F, 'X'), + (0x860, 'V'), + (0x86B, 'X'), + (0x870, 'V'), + (0x88F, 'X'), + (0x898, 'V'), + (0x8E2, 'X'), + (0x8E3, 'V'), + (0x958, 'M', 'क़'), + (0x959, 'M', 'ख़'), + (0x95A, 'M', 'ग़'), + (0x95B, 'M', 'ज़'), + (0x95C, 'M', 'ड़'), + (0x95D, 'M', 'ढ़'), + (0x95E, 'M', 'फ़'), + (0x95F, 'M', 'य़'), + (0x960, 'V'), + (0x984, 'X'), + (0x985, 'V'), + (0x98D, 'X'), + (0x98F, 'V'), + (0x991, 'X'), + (0x993, 'V'), + (0x9A9, 'X'), + (0x9AA, 'V'), + (0x9B1, 'X'), + (0x9B2, 'V'), + (0x9B3, 'X'), + (0x9B6, 'V'), + (0x9BA, 'X'), + (0x9BC, 'V'), + (0x9C5, 'X'), + (0x9C7, 'V'), + (0x9C9, 'X'), + (0x9CB, 'V'), + (0x9CF, 'X'), + (0x9D7, 'V'), + (0x9D8, 'X'), + (0x9DC, 'M', 'ড়'), + (0x9DD, 'M', 'ঢ়'), + (0x9DE, 'X'), + (0x9DF, 'M', 'য়'), + (0x9E0, 'V'), + (0x9E4, 'X'), + (0x9E6, 'V'), + (0x9FF, 'X'), + (0xA01, 'V'), + (0xA04, 'X'), + (0xA05, 'V'), + (0xA0B, 'X'), + (0xA0F, 'V'), + (0xA11, 'X'), + (0xA13, 'V'), + (0xA29, 'X'), + (0xA2A, 'V'), + (0xA31, 'X'), + (0xA32, 'V'), + (0xA33, 'M', 'ਲ਼'), + (0xA34, 'X'), + (0xA35, 'V'), + (0xA36, 'M', 'ਸ਼'), + (0xA37, 'X'), + (0xA38, 'V'), + (0xA3A, 'X'), + (0xA3C, 'V'), + (0xA3D, 'X'), + (0xA3E, 'V'), + (0xA43, 'X'), + (0xA47, 'V'), + (0xA49, 'X'), + (0xA4B, 'V'), + (0xA4E, 'X'), + (0xA51, 'V'), + (0xA52, 'X'), + (0xA59, 'M', 'ਖ਼'), + (0xA5A, 'M', 'ਗ਼'), + (0xA5B, 'M', 'ਜ਼'), + (0xA5C, 'V'), + (0xA5D, 'X'), + ] + +def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA5E, 'M', 'ਫ਼'), + (0xA5F, 'X'), + (0xA66, 'V'), + (0xA77, 'X'), + (0xA81, 'V'), + (0xA84, 'X'), + (0xA85, 'V'), + (0xA8E, 'X'), + (0xA8F, 'V'), + (0xA92, 'X'), + (0xA93, 'V'), + (0xAA9, 'X'), + (0xAAA, 'V'), + (0xAB1, 'X'), + (0xAB2, 'V'), + (0xAB4, 'X'), + (0xAB5, 'V'), + (0xABA, 'X'), + (0xABC, 'V'), + (0xAC6, 'X'), + (0xAC7, 'V'), + (0xACA, 'X'), + (0xACB, 'V'), + (0xACE, 'X'), + (0xAD0, 'V'), + (0xAD1, 'X'), + (0xAE0, 'V'), + (0xAE4, 'X'), + (0xAE6, 'V'), + (0xAF2, 'X'), + (0xAF9, 'V'), + (0xB00, 'X'), + (0xB01, 'V'), + (0xB04, 'X'), + (0xB05, 'V'), + (0xB0D, 'X'), + (0xB0F, 'V'), + (0xB11, 'X'), + (0xB13, 'V'), + (0xB29, 'X'), + (0xB2A, 'V'), + (0xB31, 'X'), + (0xB32, 'V'), + (0xB34, 'X'), + (0xB35, 'V'), + (0xB3A, 'X'), + (0xB3C, 'V'), + (0xB45, 'X'), + (0xB47, 'V'), + (0xB49, 'X'), + (0xB4B, 'V'), + (0xB4E, 'X'), + (0xB55, 'V'), + (0xB58, 'X'), + (0xB5C, 'M', 'ଡ଼'), + (0xB5D, 'M', 'ଢ଼'), + (0xB5E, 'X'), + (0xB5F, 'V'), + (0xB64, 'X'), + (0xB66, 'V'), + (0xB78, 'X'), + (0xB82, 'V'), + (0xB84, 'X'), + (0xB85, 'V'), + (0xB8B, 'X'), + (0xB8E, 'V'), + (0xB91, 'X'), + (0xB92, 'V'), + (0xB96, 'X'), + (0xB99, 'V'), + (0xB9B, 'X'), + (0xB9C, 'V'), + (0xB9D, 'X'), + (0xB9E, 'V'), + (0xBA0, 'X'), + (0xBA3, 'V'), + (0xBA5, 'X'), + (0xBA8, 'V'), + (0xBAB, 'X'), + (0xBAE, 'V'), + (0xBBA, 'X'), + (0xBBE, 'V'), + (0xBC3, 'X'), + (0xBC6, 'V'), + (0xBC9, 'X'), + (0xBCA, 'V'), + (0xBCE, 'X'), + (0xBD0, 'V'), + (0xBD1, 'X'), + (0xBD7, 'V'), + (0xBD8, 'X'), + (0xBE6, 'V'), + (0xBFB, 'X'), + (0xC00, 'V'), + (0xC0D, 'X'), + (0xC0E, 'V'), + (0xC11, 'X'), + (0xC12, 'V'), + (0xC29, 'X'), + (0xC2A, 'V'), + ] + +def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC3A, 'X'), + (0xC3C, 'V'), + (0xC45, 'X'), + (0xC46, 'V'), + (0xC49, 'X'), + (0xC4A, 'V'), + (0xC4E, 'X'), + (0xC55, 'V'), + (0xC57, 'X'), + (0xC58, 'V'), + (0xC5B, 'X'), + (0xC5D, 'V'), + (0xC5E, 'X'), + (0xC60, 'V'), + (0xC64, 'X'), + (0xC66, 'V'), + (0xC70, 'X'), + (0xC77, 'V'), + (0xC8D, 'X'), + (0xC8E, 'V'), + (0xC91, 'X'), + (0xC92, 'V'), + (0xCA9, 'X'), + (0xCAA, 'V'), + (0xCB4, 'X'), + (0xCB5, 'V'), + (0xCBA, 'X'), + (0xCBC, 'V'), + (0xCC5, 'X'), + (0xCC6, 'V'), + (0xCC9, 'X'), + (0xCCA, 'V'), + (0xCCE, 'X'), + (0xCD5, 'V'), + (0xCD7, 'X'), + (0xCDD, 'V'), + (0xCDF, 'X'), + (0xCE0, 'V'), + (0xCE4, 'X'), + (0xCE6, 'V'), + (0xCF0, 'X'), + (0xCF1, 'V'), + (0xCF4, 'X'), + (0xD00, 'V'), + (0xD0D, 'X'), + (0xD0E, 'V'), + (0xD11, 'X'), + (0xD12, 'V'), + (0xD45, 'X'), + (0xD46, 'V'), + (0xD49, 'X'), + (0xD4A, 'V'), + (0xD50, 'X'), + (0xD54, 'V'), + (0xD64, 'X'), + (0xD66, 'V'), + (0xD80, 'X'), + (0xD81, 'V'), + (0xD84, 'X'), + (0xD85, 'V'), + (0xD97, 'X'), + (0xD9A, 'V'), + (0xDB2, 'X'), + (0xDB3, 'V'), + (0xDBC, 'X'), + (0xDBD, 'V'), + (0xDBE, 'X'), + (0xDC0, 'V'), + (0xDC7, 'X'), + (0xDCA, 'V'), + (0xDCB, 'X'), + (0xDCF, 'V'), + (0xDD5, 'X'), + (0xDD6, 'V'), + (0xDD7, 'X'), + (0xDD8, 'V'), + (0xDE0, 'X'), + (0xDE6, 'V'), + (0xDF0, 'X'), + (0xDF2, 'V'), + (0xDF5, 'X'), + (0xE01, 'V'), + (0xE33, 'M', 'ํา'), + (0xE34, 'V'), + (0xE3B, 'X'), + (0xE3F, 'V'), + (0xE5C, 'X'), + (0xE81, 'V'), + (0xE83, 'X'), + (0xE84, 'V'), + (0xE85, 'X'), + (0xE86, 'V'), + (0xE8B, 'X'), + (0xE8C, 'V'), + (0xEA4, 'X'), + (0xEA5, 'V'), + (0xEA6, 'X'), + (0xEA7, 'V'), + (0xEB3, 'M', 'ໍາ'), + (0xEB4, 'V'), + ] + +def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xEBE, 'X'), + (0xEC0, 'V'), + (0xEC5, 'X'), + (0xEC6, 'V'), + (0xEC7, 'X'), + (0xEC8, 'V'), + (0xECF, 'X'), + (0xED0, 'V'), + (0xEDA, 'X'), + (0xEDC, 'M', 'ຫນ'), + (0xEDD, 'M', 'ຫມ'), + (0xEDE, 'V'), + (0xEE0, 'X'), + (0xF00, 'V'), + (0xF0C, 'M', '་'), + (0xF0D, 'V'), + (0xF43, 'M', 'གྷ'), + (0xF44, 'V'), + (0xF48, 'X'), + (0xF49, 'V'), + (0xF4D, 'M', 'ཌྷ'), + (0xF4E, 'V'), + (0xF52, 'M', 'དྷ'), + (0xF53, 'V'), + (0xF57, 'M', 'བྷ'), + (0xF58, 'V'), + (0xF5C, 'M', 'ཛྷ'), + (0xF5D, 'V'), + (0xF69, 'M', 'ཀྵ'), + (0xF6A, 'V'), + (0xF6D, 'X'), + (0xF71, 'V'), + (0xF73, 'M', 'ཱི'), + (0xF74, 'V'), + (0xF75, 'M', 'ཱུ'), + (0xF76, 'M', 'ྲྀ'), + (0xF77, 'M', 'ྲཱྀ'), + (0xF78, 'M', 'ླྀ'), + (0xF79, 'M', 'ླཱྀ'), + (0xF7A, 'V'), + (0xF81, 'M', 'ཱྀ'), + (0xF82, 'V'), + (0xF93, 'M', 'ྒྷ'), + (0xF94, 'V'), + (0xF98, 'X'), + (0xF99, 'V'), + (0xF9D, 'M', 'ྜྷ'), + (0xF9E, 'V'), + (0xFA2, 'M', 'ྡྷ'), + (0xFA3, 'V'), + (0xFA7, 'M', 'ྦྷ'), + (0xFA8, 'V'), + (0xFAC, 'M', 'ྫྷ'), + (0xFAD, 'V'), + (0xFB9, 'M', 'ྐྵ'), + (0xFBA, 'V'), + (0xFBD, 'X'), + (0xFBE, 'V'), + (0xFCD, 'X'), + (0xFCE, 'V'), + (0xFDB, 'X'), + (0x1000, 'V'), + (0x10A0, 'X'), + (0x10C7, 'M', 'ⴧ'), + (0x10C8, 'X'), + (0x10CD, 'M', 'ⴭ'), + (0x10CE, 'X'), + (0x10D0, 'V'), + (0x10FC, 'M', 'ნ'), + (0x10FD, 'V'), + (0x115F, 'X'), + (0x1161, 'V'), + (0x1249, 'X'), + (0x124A, 'V'), + (0x124E, 'X'), + (0x1250, 'V'), + (0x1257, 'X'), + (0x1258, 'V'), + (0x1259, 'X'), + (0x125A, 'V'), + (0x125E, 'X'), + (0x1260, 'V'), + (0x1289, 'X'), + (0x128A, 'V'), + (0x128E, 'X'), + (0x1290, 'V'), + (0x12B1, 'X'), + (0x12B2, 'V'), + (0x12B6, 'X'), + (0x12B8, 'V'), + (0x12BF, 'X'), + (0x12C0, 'V'), + (0x12C1, 'X'), + (0x12C2, 'V'), + (0x12C6, 'X'), + (0x12C8, 'V'), + (0x12D7, 'X'), + (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), + ] + +def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1316, 'X'), + (0x1318, 'V'), + (0x135B, 'X'), + (0x135D, 'V'), + (0x137D, 'X'), + (0x1380, 'V'), + (0x139A, 'X'), + (0x13A0, 'V'), + (0x13F6, 'X'), + (0x13F8, 'M', 'Ᏸ'), + (0x13F9, 'M', 'Ᏹ'), + (0x13FA, 'M', 'Ᏺ'), + (0x13FB, 'M', 'Ᏻ'), + (0x13FC, 'M', 'Ᏼ'), + (0x13FD, 'M', 'Ᏽ'), + (0x13FE, 'X'), + (0x1400, 'V'), + (0x1680, 'X'), + (0x1681, 'V'), + (0x169D, 'X'), + (0x16A0, 'V'), + (0x16F9, 'X'), + (0x1700, 'V'), + (0x1716, 'X'), + (0x171F, 'V'), + (0x1737, 'X'), + (0x1740, 'V'), + (0x1754, 'X'), + (0x1760, 'V'), + (0x176D, 'X'), + (0x176E, 'V'), + (0x1771, 'X'), + (0x1772, 'V'), + (0x1774, 'X'), + (0x1780, 'V'), + (0x17B4, 'X'), + (0x17B6, 'V'), + (0x17DE, 'X'), + (0x17E0, 'V'), + (0x17EA, 'X'), + (0x17F0, 'V'), + (0x17FA, 'X'), + (0x1800, 'V'), + (0x1806, 'X'), + (0x1807, 'V'), + (0x180B, 'I'), + (0x180E, 'X'), + (0x180F, 'I'), + (0x1810, 'V'), + (0x181A, 'X'), + (0x1820, 'V'), + (0x1879, 'X'), + (0x1880, 'V'), + (0x18AB, 'X'), + (0x18B0, 'V'), + (0x18F6, 'X'), + (0x1900, 'V'), + (0x191F, 'X'), + (0x1920, 'V'), + (0x192C, 'X'), + (0x1930, 'V'), + (0x193C, 'X'), + (0x1940, 'V'), + (0x1941, 'X'), + (0x1944, 'V'), + (0x196E, 'X'), + (0x1970, 'V'), + (0x1975, 'X'), + (0x1980, 'V'), + (0x19AC, 'X'), + (0x19B0, 'V'), + (0x19CA, 'X'), + (0x19D0, 'V'), + (0x19DB, 'X'), + (0x19DE, 'V'), + (0x1A1C, 'X'), + (0x1A1E, 'V'), + (0x1A5F, 'X'), + (0x1A60, 'V'), + (0x1A7D, 'X'), + (0x1A7F, 'V'), + (0x1A8A, 'X'), + (0x1A90, 'V'), + (0x1A9A, 'X'), + (0x1AA0, 'V'), + (0x1AAE, 'X'), + (0x1AB0, 'V'), + (0x1ACF, 'X'), + (0x1B00, 'V'), + (0x1B4D, 'X'), + (0x1B50, 'V'), + (0x1B7F, 'X'), + (0x1B80, 'V'), + (0x1BF4, 'X'), + (0x1BFC, 'V'), + (0x1C38, 'X'), + (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), + (0x1C80, 'M', 'в'), + ] + +def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1C81, 'M', 'д'), + (0x1C82, 'M', 'о'), + (0x1C83, 'M', 'с'), + (0x1C84, 'M', 'т'), + (0x1C86, 'M', 'ъ'), + (0x1C87, 'M', 'ѣ'), + (0x1C88, 'M', 'ꙋ'), + (0x1C89, 'X'), + (0x1C90, 'M', 'ა'), + (0x1C91, 'M', 'ბ'), + (0x1C92, 'M', 'გ'), + (0x1C93, 'M', 'დ'), + (0x1C94, 'M', 'ე'), + (0x1C95, 'M', 'ვ'), + (0x1C96, 'M', 'ზ'), + (0x1C97, 'M', 'თ'), + (0x1C98, 'M', 'ი'), + (0x1C99, 'M', 'კ'), + (0x1C9A, 'M', 'ლ'), + (0x1C9B, 'M', 'მ'), + (0x1C9C, 'M', 'ნ'), + (0x1C9D, 'M', 'ო'), + (0x1C9E, 'M', 'პ'), + (0x1C9F, 'M', 'ჟ'), + (0x1CA0, 'M', 'რ'), + (0x1CA1, 'M', 'ს'), + (0x1CA2, 'M', 'ტ'), + (0x1CA3, 'M', 'უ'), + (0x1CA4, 'M', 'ფ'), + (0x1CA5, 'M', 'ქ'), + (0x1CA6, 'M', 'ღ'), + (0x1CA7, 'M', 'ყ'), + (0x1CA8, 'M', 'შ'), + (0x1CA9, 'M', 'ჩ'), + (0x1CAA, 'M', 'ც'), + (0x1CAB, 'M', 'ძ'), + (0x1CAC, 'M', 'წ'), + (0x1CAD, 'M', 'ჭ'), + (0x1CAE, 'M', 'ხ'), + (0x1CAF, 'M', 'ჯ'), + (0x1CB0, 'M', 'ჰ'), + (0x1CB1, 'M', 'ჱ'), + (0x1CB2, 'M', 'ჲ'), + (0x1CB3, 'M', 'ჳ'), + (0x1CB4, 'M', 'ჴ'), + (0x1CB5, 'M', 'ჵ'), + (0x1CB6, 'M', 'ჶ'), + (0x1CB7, 'M', 'ჷ'), + (0x1CB8, 'M', 'ჸ'), + (0x1CB9, 'M', 'ჹ'), + (0x1CBA, 'M', 'ჺ'), + (0x1CBB, 'X'), + (0x1CBD, 'M', 'ჽ'), + (0x1CBE, 'M', 'ჾ'), + (0x1CBF, 'M', 'ჿ'), + (0x1CC0, 'V'), + (0x1CC8, 'X'), + (0x1CD0, 'V'), + (0x1CFB, 'X'), + (0x1D00, 'V'), + (0x1D2C, 'M', 'a'), + (0x1D2D, 'M', 'æ'), + (0x1D2E, 'M', 'b'), + (0x1D2F, 'V'), + (0x1D30, 'M', 'd'), + (0x1D31, 'M', 'e'), + (0x1D32, 'M', 'ǝ'), + (0x1D33, 'M', 'g'), + (0x1D34, 'M', 'h'), + (0x1D35, 'M', 'i'), + (0x1D36, 'M', 'j'), + (0x1D37, 'M', 'k'), + (0x1D38, 'M', 'l'), + (0x1D39, 'M', 'm'), + (0x1D3A, 'M', 'n'), + (0x1D3B, 'V'), + (0x1D3C, 'M', 'o'), + (0x1D3D, 'M', 'ȣ'), + (0x1D3E, 'M', 'p'), + (0x1D3F, 'M', 'r'), + (0x1D40, 'M', 't'), + (0x1D41, 'M', 'u'), + (0x1D42, 'M', 'w'), + (0x1D43, 'M', 'a'), + (0x1D44, 'M', 'ɐ'), + (0x1D45, 'M', 'ɑ'), + (0x1D46, 'M', 'ᴂ'), + (0x1D47, 'M', 'b'), + (0x1D48, 'M', 'd'), + (0x1D49, 'M', 'e'), + (0x1D4A, 'M', 'ə'), + (0x1D4B, 'M', 'ɛ'), + (0x1D4C, 'M', 'ɜ'), + (0x1D4D, 'M', 'g'), + (0x1D4E, 'V'), + (0x1D4F, 'M', 'k'), + (0x1D50, 'M', 'm'), + (0x1D51, 'M', 'ŋ'), + (0x1D52, 'M', 'o'), + (0x1D53, 'M', 'ɔ'), + ] + +def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D54, 'M', 'ᴖ'), + (0x1D55, 'M', 'ᴗ'), + (0x1D56, 'M', 'p'), + (0x1D57, 'M', 't'), + (0x1D58, 'M', 'u'), + (0x1D59, 'M', 'ᴝ'), + (0x1D5A, 'M', 'ɯ'), + (0x1D5B, 'M', 'v'), + (0x1D5C, 'M', 'ᴥ'), + (0x1D5D, 'M', 'β'), + (0x1D5E, 'M', 'γ'), + (0x1D5F, 'M', 'δ'), + (0x1D60, 'M', 'φ'), + (0x1D61, 'M', 'χ'), + (0x1D62, 'M', 'i'), + (0x1D63, 'M', 'r'), + (0x1D64, 'M', 'u'), + (0x1D65, 'M', 'v'), + (0x1D66, 'M', 'β'), + (0x1D67, 'M', 'γ'), + (0x1D68, 'M', 'ρ'), + (0x1D69, 'M', 'φ'), + (0x1D6A, 'M', 'χ'), + (0x1D6B, 'V'), + (0x1D78, 'M', 'н'), + (0x1D79, 'V'), + (0x1D9B, 'M', 'ɒ'), + (0x1D9C, 'M', 'c'), + (0x1D9D, 'M', 'ɕ'), + (0x1D9E, 'M', 'ð'), + (0x1D9F, 'M', 'ɜ'), + (0x1DA0, 'M', 'f'), + (0x1DA1, 'M', 'ɟ'), + (0x1DA2, 'M', 'ɡ'), + (0x1DA3, 'M', 'ɥ'), + (0x1DA4, 'M', 'ɨ'), + (0x1DA5, 'M', 'ɩ'), + (0x1DA6, 'M', 'ɪ'), + (0x1DA7, 'M', 'ᵻ'), + (0x1DA8, 'M', 'ʝ'), + (0x1DA9, 'M', 'ɭ'), + (0x1DAA, 'M', 'ᶅ'), + (0x1DAB, 'M', 'ʟ'), + (0x1DAC, 'M', 'ɱ'), + (0x1DAD, 'M', 'ɰ'), + (0x1DAE, 'M', 'ɲ'), + (0x1DAF, 'M', 'ɳ'), + (0x1DB0, 'M', 'ɴ'), + (0x1DB1, 'M', 'ɵ'), + (0x1DB2, 'M', 'ɸ'), + (0x1DB3, 'M', 'ʂ'), + (0x1DB4, 'M', 'ʃ'), + (0x1DB5, 'M', 'ƫ'), + (0x1DB6, 'M', 'ʉ'), + (0x1DB7, 'M', 'ʊ'), + (0x1DB8, 'M', 'ᴜ'), + (0x1DB9, 'M', 'ʋ'), + (0x1DBA, 'M', 'ʌ'), + (0x1DBB, 'M', 'z'), + (0x1DBC, 'M', 'ʐ'), + (0x1DBD, 'M', 'ʑ'), + (0x1DBE, 'M', 'ʒ'), + (0x1DBF, 'M', 'θ'), + (0x1DC0, 'V'), + (0x1E00, 'M', 'ḁ'), + (0x1E01, 'V'), + (0x1E02, 'M', 'ḃ'), + (0x1E03, 'V'), + (0x1E04, 'M', 'ḅ'), + (0x1E05, 'V'), + (0x1E06, 'M', 'ḇ'), + (0x1E07, 'V'), + (0x1E08, 'M', 'ḉ'), + (0x1E09, 'V'), + (0x1E0A, 'M', 'ḋ'), + (0x1E0B, 'V'), + (0x1E0C, 'M', 'ḍ'), + (0x1E0D, 'V'), + (0x1E0E, 'M', 'ḏ'), + (0x1E0F, 'V'), + (0x1E10, 'M', 'ḑ'), + (0x1E11, 'V'), + (0x1E12, 'M', 'ḓ'), + (0x1E13, 'V'), + (0x1E14, 'M', 'ḕ'), + (0x1E15, 'V'), + (0x1E16, 'M', 'ḗ'), + (0x1E17, 'V'), + (0x1E18, 'M', 'ḙ'), + (0x1E19, 'V'), + (0x1E1A, 'M', 'ḛ'), + (0x1E1B, 'V'), + (0x1E1C, 'M', 'ḝ'), + (0x1E1D, 'V'), + (0x1E1E, 'M', 'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', 'ḡ'), + (0x1E21, 'V'), + (0x1E22, 'M', 'ḣ'), + (0x1E23, 'V'), + ] + +def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E24, 'M', 'ḥ'), + (0x1E25, 'V'), + (0x1E26, 'M', 'ḧ'), + (0x1E27, 'V'), + (0x1E28, 'M', 'ḩ'), + (0x1E29, 'V'), + (0x1E2A, 'M', 'ḫ'), + (0x1E2B, 'V'), + (0x1E2C, 'M', 'ḭ'), + (0x1E2D, 'V'), + (0x1E2E, 'M', 'ḯ'), + (0x1E2F, 'V'), + (0x1E30, 'M', 'ḱ'), + (0x1E31, 'V'), + (0x1E32, 'M', 'ḳ'), + (0x1E33, 'V'), + (0x1E34, 'M', 'ḵ'), + (0x1E35, 'V'), + (0x1E36, 'M', 'ḷ'), + (0x1E37, 'V'), + (0x1E38, 'M', 'ḹ'), + (0x1E39, 'V'), + (0x1E3A, 'M', 'ḻ'), + (0x1E3B, 'V'), + (0x1E3C, 'M', 'ḽ'), + (0x1E3D, 'V'), + (0x1E3E, 'M', 'ḿ'), + (0x1E3F, 'V'), + (0x1E40, 'M', 'ṁ'), + (0x1E41, 'V'), + (0x1E42, 'M', 'ṃ'), + (0x1E43, 'V'), + (0x1E44, 'M', 'ṅ'), + (0x1E45, 'V'), + (0x1E46, 'M', 'ṇ'), + (0x1E47, 'V'), + (0x1E48, 'M', 'ṉ'), + (0x1E49, 'V'), + (0x1E4A, 'M', 'ṋ'), + (0x1E4B, 'V'), + (0x1E4C, 'M', 'ṍ'), + (0x1E4D, 'V'), + (0x1E4E, 'M', 'ṏ'), + (0x1E4F, 'V'), + (0x1E50, 'M', 'ṑ'), + (0x1E51, 'V'), + (0x1E52, 'M', 'ṓ'), + (0x1E53, 'V'), + (0x1E54, 'M', 'ṕ'), + (0x1E55, 'V'), + (0x1E56, 'M', 'ṗ'), + (0x1E57, 'V'), + (0x1E58, 'M', 'ṙ'), + (0x1E59, 'V'), + (0x1E5A, 'M', 'ṛ'), + (0x1E5B, 'V'), + (0x1E5C, 'M', 'ṝ'), + (0x1E5D, 'V'), + (0x1E5E, 'M', 'ṟ'), + (0x1E5F, 'V'), + (0x1E60, 'M', 'ṡ'), + (0x1E61, 'V'), + (0x1E62, 'M', 'ṣ'), + (0x1E63, 'V'), + (0x1E64, 'M', 'ṥ'), + (0x1E65, 'V'), + (0x1E66, 'M', 'ṧ'), + (0x1E67, 'V'), + (0x1E68, 'M', 'ṩ'), + (0x1E69, 'V'), + (0x1E6A, 'M', 'ṫ'), + (0x1E6B, 'V'), + (0x1E6C, 'M', 'ṭ'), + (0x1E6D, 'V'), + (0x1E6E, 'M', 'ṯ'), + (0x1E6F, 'V'), + (0x1E70, 'M', 'ṱ'), + (0x1E71, 'V'), + (0x1E72, 'M', 'ṳ'), + (0x1E73, 'V'), + (0x1E74, 'M', 'ṵ'), + (0x1E75, 'V'), + (0x1E76, 'M', 'ṷ'), + (0x1E77, 'V'), + (0x1E78, 'M', 'ṹ'), + (0x1E79, 'V'), + (0x1E7A, 'M', 'ṻ'), + (0x1E7B, 'V'), + (0x1E7C, 'M', 'ṽ'), + (0x1E7D, 'V'), + (0x1E7E, 'M', 'ṿ'), + (0x1E7F, 'V'), + (0x1E80, 'M', 'ẁ'), + (0x1E81, 'V'), + (0x1E82, 'M', 'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', 'ẅ'), + (0x1E85, 'V'), + (0x1E86, 'M', 'ẇ'), + (0x1E87, 'V'), + ] + +def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E88, 'M', 'ẉ'), + (0x1E89, 'V'), + (0x1E8A, 'M', 'ẋ'), + (0x1E8B, 'V'), + (0x1E8C, 'M', 'ẍ'), + (0x1E8D, 'V'), + (0x1E8E, 'M', 'ẏ'), + (0x1E8F, 'V'), + (0x1E90, 'M', 'ẑ'), + (0x1E91, 'V'), + (0x1E92, 'M', 'ẓ'), + (0x1E93, 'V'), + (0x1E94, 'M', 'ẕ'), + (0x1E95, 'V'), + (0x1E9A, 'M', 'aʾ'), + (0x1E9B, 'M', 'ṡ'), + (0x1E9C, 'V'), + (0x1E9E, 'M', 'ß'), + (0x1E9F, 'V'), + (0x1EA0, 'M', 'ạ'), + (0x1EA1, 'V'), + (0x1EA2, 'M', 'ả'), + (0x1EA3, 'V'), + (0x1EA4, 'M', 'ấ'), + (0x1EA5, 'V'), + (0x1EA6, 'M', 'ầ'), + (0x1EA7, 'V'), + (0x1EA8, 'M', 'ẩ'), + (0x1EA9, 'V'), + (0x1EAA, 'M', 'ẫ'), + (0x1EAB, 'V'), + (0x1EAC, 'M', 'ậ'), + (0x1EAD, 'V'), + (0x1EAE, 'M', 'ắ'), + (0x1EAF, 'V'), + (0x1EB0, 'M', 'ằ'), + (0x1EB1, 'V'), + (0x1EB2, 'M', 'ẳ'), + (0x1EB3, 'V'), + (0x1EB4, 'M', 'ẵ'), + (0x1EB5, 'V'), + (0x1EB6, 'M', 'ặ'), + (0x1EB7, 'V'), + (0x1EB8, 'M', 'ẹ'), + (0x1EB9, 'V'), + (0x1EBA, 'M', 'ẻ'), + (0x1EBB, 'V'), + (0x1EBC, 'M', 'ẽ'), + (0x1EBD, 'V'), + (0x1EBE, 'M', 'ế'), + (0x1EBF, 'V'), + (0x1EC0, 'M', 'ề'), + (0x1EC1, 'V'), + (0x1EC2, 'M', 'ể'), + (0x1EC3, 'V'), + (0x1EC4, 'M', 'ễ'), + (0x1EC5, 'V'), + (0x1EC6, 'M', 'ệ'), + (0x1EC7, 'V'), + (0x1EC8, 'M', 'ỉ'), + (0x1EC9, 'V'), + (0x1ECA, 'M', 'ị'), + (0x1ECB, 'V'), + (0x1ECC, 'M', 'ọ'), + (0x1ECD, 'V'), + (0x1ECE, 'M', 'ỏ'), + (0x1ECF, 'V'), + (0x1ED0, 'M', 'ố'), + (0x1ED1, 'V'), + (0x1ED2, 'M', 'ồ'), + (0x1ED3, 'V'), + (0x1ED4, 'M', 'ổ'), + (0x1ED5, 'V'), + (0x1ED6, 'M', 'ỗ'), + (0x1ED7, 'V'), + (0x1ED8, 'M', 'ộ'), + (0x1ED9, 'V'), + (0x1EDA, 'M', 'ớ'), + (0x1EDB, 'V'), + (0x1EDC, 'M', 'ờ'), + (0x1EDD, 'V'), + (0x1EDE, 'M', 'ở'), + (0x1EDF, 'V'), + (0x1EE0, 'M', 'ỡ'), + (0x1EE1, 'V'), + (0x1EE2, 'M', 'ợ'), + (0x1EE3, 'V'), + (0x1EE4, 'M', 'ụ'), + (0x1EE5, 'V'), + (0x1EE6, 'M', 'ủ'), + (0x1EE7, 'V'), + (0x1EE8, 'M', 'ứ'), + (0x1EE9, 'V'), + (0x1EEA, 'M', 'ừ'), + (0x1EEB, 'V'), + (0x1EEC, 'M', 'ử'), + (0x1EED, 'V'), + (0x1EEE, 'M', 'ữ'), + (0x1EEF, 'V'), + (0x1EF0, 'M', 'ự'), + ] + +def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EF1, 'V'), + (0x1EF2, 'M', 'ỳ'), + (0x1EF3, 'V'), + (0x1EF4, 'M', 'ỵ'), + (0x1EF5, 'V'), + (0x1EF6, 'M', 'ỷ'), + (0x1EF7, 'V'), + (0x1EF8, 'M', 'ỹ'), + (0x1EF9, 'V'), + (0x1EFA, 'M', 'ỻ'), + (0x1EFB, 'V'), + (0x1EFC, 'M', 'ỽ'), + (0x1EFD, 'V'), + (0x1EFE, 'M', 'ỿ'), + (0x1EFF, 'V'), + (0x1F08, 'M', 'ἀ'), + (0x1F09, 'M', 'ἁ'), + (0x1F0A, 'M', 'ἂ'), + (0x1F0B, 'M', 'ἃ'), + (0x1F0C, 'M', 'ἄ'), + (0x1F0D, 'M', 'ἅ'), + (0x1F0E, 'M', 'ἆ'), + (0x1F0F, 'M', 'ἇ'), + (0x1F10, 'V'), + (0x1F16, 'X'), + (0x1F18, 'M', 'ἐ'), + (0x1F19, 'M', 'ἑ'), + (0x1F1A, 'M', 'ἒ'), + (0x1F1B, 'M', 'ἓ'), + (0x1F1C, 'M', 'ἔ'), + (0x1F1D, 'M', 'ἕ'), + (0x1F1E, 'X'), + (0x1F20, 'V'), + (0x1F28, 'M', 'ἠ'), + (0x1F29, 'M', 'ἡ'), + (0x1F2A, 'M', 'ἢ'), + (0x1F2B, 'M', 'ἣ'), + (0x1F2C, 'M', 'ἤ'), + (0x1F2D, 'M', 'ἥ'), + (0x1F2E, 'M', 'ἦ'), + (0x1F2F, 'M', 'ἧ'), + (0x1F30, 'V'), + (0x1F38, 'M', 'ἰ'), + (0x1F39, 'M', 'ἱ'), + (0x1F3A, 'M', 'ἲ'), + (0x1F3B, 'M', 'ἳ'), + (0x1F3C, 'M', 'ἴ'), + (0x1F3D, 'M', 'ἵ'), + (0x1F3E, 'M', 'ἶ'), + (0x1F3F, 'M', 'ἷ'), + (0x1F40, 'V'), + (0x1F46, 'X'), + (0x1F48, 'M', 'ὀ'), + (0x1F49, 'M', 'ὁ'), + (0x1F4A, 'M', 'ὂ'), + (0x1F4B, 'M', 'ὃ'), + (0x1F4C, 'M', 'ὄ'), + (0x1F4D, 'M', 'ὅ'), + (0x1F4E, 'X'), + (0x1F50, 'V'), + (0x1F58, 'X'), + (0x1F59, 'M', 'ὑ'), + (0x1F5A, 'X'), + (0x1F5B, 'M', 'ὓ'), + (0x1F5C, 'X'), + (0x1F5D, 'M', 'ὕ'), + (0x1F5E, 'X'), + (0x1F5F, 'M', 'ὗ'), + (0x1F60, 'V'), + (0x1F68, 'M', 'ὠ'), + (0x1F69, 'M', 'ὡ'), + (0x1F6A, 'M', 'ὢ'), + (0x1F6B, 'M', 'ὣ'), + (0x1F6C, 'M', 'ὤ'), + (0x1F6D, 'M', 'ὥ'), + (0x1F6E, 'M', 'ὦ'), + (0x1F6F, 'M', 'ὧ'), + (0x1F70, 'V'), + (0x1F71, 'M', 'ά'), + (0x1F72, 'V'), + (0x1F73, 'M', 'έ'), + (0x1F74, 'V'), + (0x1F75, 'M', 'ή'), + (0x1F76, 'V'), + (0x1F77, 'M', 'ί'), + (0x1F78, 'V'), + (0x1F79, 'M', 'ό'), + (0x1F7A, 'V'), + (0x1F7B, 'M', 'ύ'), + (0x1F7C, 'V'), + (0x1F7D, 'M', 'ώ'), + (0x1F7E, 'X'), + (0x1F80, 'M', 'ἀι'), + (0x1F81, 'M', 'ἁι'), + (0x1F82, 'M', 'ἂι'), + (0x1F83, 'M', 'ἃι'), + (0x1F84, 'M', 'ἄι'), + (0x1F85, 'M', 'ἅι'), + (0x1F86, 'M', 'ἆι'), + (0x1F87, 'M', 'ἇι'), + ] + +def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F88, 'M', 'ἀι'), + (0x1F89, 'M', 'ἁι'), + (0x1F8A, 'M', 'ἂι'), + (0x1F8B, 'M', 'ἃι'), + (0x1F8C, 'M', 'ἄι'), + (0x1F8D, 'M', 'ἅι'), + (0x1F8E, 'M', 'ἆι'), + (0x1F8F, 'M', 'ἇι'), + (0x1F90, 'M', 'ἠι'), + (0x1F91, 'M', 'ἡι'), + (0x1F92, 'M', 'ἢι'), + (0x1F93, 'M', 'ἣι'), + (0x1F94, 'M', 'ἤι'), + (0x1F95, 'M', 'ἥι'), + (0x1F96, 'M', 'ἦι'), + (0x1F97, 'M', 'ἧι'), + (0x1F98, 'M', 'ἠι'), + (0x1F99, 'M', 'ἡι'), + (0x1F9A, 'M', 'ἢι'), + (0x1F9B, 'M', 'ἣι'), + (0x1F9C, 'M', 'ἤι'), + (0x1F9D, 'M', 'ἥι'), + (0x1F9E, 'M', 'ἦι'), + (0x1F9F, 'M', 'ἧι'), + (0x1FA0, 'M', 'ὠι'), + (0x1FA1, 'M', 'ὡι'), + (0x1FA2, 'M', 'ὢι'), + (0x1FA3, 'M', 'ὣι'), + (0x1FA4, 'M', 'ὤι'), + (0x1FA5, 'M', 'ὥι'), + (0x1FA6, 'M', 'ὦι'), + (0x1FA7, 'M', 'ὧι'), + (0x1FA8, 'M', 'ὠι'), + (0x1FA9, 'M', 'ὡι'), + (0x1FAA, 'M', 'ὢι'), + (0x1FAB, 'M', 'ὣι'), + (0x1FAC, 'M', 'ὤι'), + (0x1FAD, 'M', 'ὥι'), + (0x1FAE, 'M', 'ὦι'), + (0x1FAF, 'M', 'ὧι'), + (0x1FB0, 'V'), + (0x1FB2, 'M', 'ὰι'), + (0x1FB3, 'M', 'αι'), + (0x1FB4, 'M', 'άι'), + (0x1FB5, 'X'), + (0x1FB6, 'V'), + (0x1FB7, 'M', 'ᾶι'), + (0x1FB8, 'M', 'ᾰ'), + (0x1FB9, 'M', 'ᾱ'), + (0x1FBA, 'M', 'ὰ'), + (0x1FBB, 'M', 'ά'), + (0x1FBC, 'M', 'αι'), + (0x1FBD, '3', ' ̓'), + (0x1FBE, 'M', 'ι'), + (0x1FBF, '3', ' ̓'), + (0x1FC0, '3', ' ͂'), + (0x1FC1, '3', ' ̈͂'), + (0x1FC2, 'M', 'ὴι'), + (0x1FC3, 'M', 'ηι'), + (0x1FC4, 'M', 'ήι'), + (0x1FC5, 'X'), + (0x1FC6, 'V'), + (0x1FC7, 'M', 'ῆι'), + (0x1FC8, 'M', 'ὲ'), + (0x1FC9, 'M', 'έ'), + (0x1FCA, 'M', 'ὴ'), + (0x1FCB, 'M', 'ή'), + (0x1FCC, 'M', 'ηι'), + (0x1FCD, '3', ' ̓̀'), + (0x1FCE, '3', ' ̓́'), + (0x1FCF, '3', ' ̓͂'), + (0x1FD0, 'V'), + (0x1FD3, 'M', 'ΐ'), + (0x1FD4, 'X'), + (0x1FD6, 'V'), + (0x1FD8, 'M', 'ῐ'), + (0x1FD9, 'M', 'ῑ'), + (0x1FDA, 'M', 'ὶ'), + (0x1FDB, 'M', 'ί'), + (0x1FDC, 'X'), + (0x1FDD, '3', ' ̔̀'), + (0x1FDE, '3', ' ̔́'), + (0x1FDF, '3', ' ̔͂'), + (0x1FE0, 'V'), + (0x1FE3, 'M', 'ΰ'), + (0x1FE4, 'V'), + (0x1FE8, 'M', 'ῠ'), + (0x1FE9, 'M', 'ῡ'), + (0x1FEA, 'M', 'ὺ'), + (0x1FEB, 'M', 'ύ'), + (0x1FEC, 'M', 'ῥ'), + (0x1FED, '3', ' ̈̀'), + (0x1FEE, '3', ' ̈́'), + (0x1FEF, '3', '`'), + (0x1FF0, 'X'), + (0x1FF2, 'M', 'ὼι'), + (0x1FF3, 'M', 'ωι'), + (0x1FF4, 'M', 'ώι'), + (0x1FF5, 'X'), + (0x1FF6, 'V'), + ] + +def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FF7, 'M', 'ῶι'), + (0x1FF8, 'M', 'ὸ'), + (0x1FF9, 'M', 'ό'), + (0x1FFA, 'M', 'ὼ'), + (0x1FFB, 'M', 'ώ'), + (0x1FFC, 'M', 'ωι'), + (0x1FFD, '3', ' ́'), + (0x1FFE, '3', ' ̔'), + (0x1FFF, 'X'), + (0x2000, '3', ' '), + (0x200B, 'I'), + (0x200C, 'D', ''), + (0x200E, 'X'), + (0x2010, 'V'), + (0x2011, 'M', '‐'), + (0x2012, 'V'), + (0x2017, '3', ' ̳'), + (0x2018, 'V'), + (0x2024, 'X'), + (0x2027, 'V'), + (0x2028, 'X'), + (0x202F, '3', ' '), + (0x2030, 'V'), + (0x2033, 'M', '′′'), + (0x2034, 'M', '′′′'), + (0x2035, 'V'), + (0x2036, 'M', '‵‵'), + (0x2037, 'M', '‵‵‵'), + (0x2038, 'V'), + (0x203C, '3', '!!'), + (0x203D, 'V'), + (0x203E, '3', ' ̅'), + (0x203F, 'V'), + (0x2047, '3', '??'), + (0x2048, '3', '?!'), + (0x2049, '3', '!?'), + (0x204A, 'V'), + (0x2057, 'M', '′′′′'), + (0x2058, 'V'), + (0x205F, '3', ' '), + (0x2060, 'I'), + (0x2061, 'X'), + (0x2064, 'I'), + (0x2065, 'X'), + (0x2070, 'M', '0'), + (0x2071, 'M', 'i'), + (0x2072, 'X'), + (0x2074, 'M', '4'), + (0x2075, 'M', '5'), + (0x2076, 'M', '6'), + (0x2077, 'M', '7'), + (0x2078, 'M', '8'), + (0x2079, 'M', '9'), + (0x207A, '3', '+'), + (0x207B, 'M', '−'), + (0x207C, '3', '='), + (0x207D, '3', '('), + (0x207E, '3', ')'), + (0x207F, 'M', 'n'), + (0x2080, 'M', '0'), + (0x2081, 'M', '1'), + (0x2082, 'M', '2'), + (0x2083, 'M', '3'), + (0x2084, 'M', '4'), + (0x2085, 'M', '5'), + (0x2086, 'M', '6'), + (0x2087, 'M', '7'), + (0x2088, 'M', '8'), + (0x2089, 'M', '9'), + (0x208A, '3', '+'), + (0x208B, 'M', '−'), + (0x208C, '3', '='), + (0x208D, '3', '('), + (0x208E, '3', ')'), + (0x208F, 'X'), + (0x2090, 'M', 'a'), + (0x2091, 'M', 'e'), + (0x2092, 'M', 'o'), + (0x2093, 'M', 'x'), + (0x2094, 'M', 'ə'), + (0x2095, 'M', 'h'), + (0x2096, 'M', 'k'), + (0x2097, 'M', 'l'), + (0x2098, 'M', 'm'), + (0x2099, 'M', 'n'), + (0x209A, 'M', 'p'), + (0x209B, 'M', 's'), + (0x209C, 'M', 't'), + (0x209D, 'X'), + (0x20A0, 'V'), + (0x20A8, 'M', 'rs'), + (0x20A9, 'V'), + (0x20C1, 'X'), + (0x20D0, 'V'), + (0x20F1, 'X'), + (0x2100, '3', 'a/c'), + (0x2101, '3', 'a/s'), + (0x2102, 'M', 'c'), + (0x2103, 'M', '°c'), + (0x2104, 'V'), + ] + +def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2105, '3', 'c/o'), + (0x2106, '3', 'c/u'), + (0x2107, 'M', 'ɛ'), + (0x2108, 'V'), + (0x2109, 'M', '°f'), + (0x210A, 'M', 'g'), + (0x210B, 'M', 'h'), + (0x210F, 'M', 'ħ'), + (0x2110, 'M', 'i'), + (0x2112, 'M', 'l'), + (0x2114, 'V'), + (0x2115, 'M', 'n'), + (0x2116, 'M', 'no'), + (0x2117, 'V'), + (0x2119, 'M', 'p'), + (0x211A, 'M', 'q'), + (0x211B, 'M', 'r'), + (0x211E, 'V'), + (0x2120, 'M', 'sm'), + (0x2121, 'M', 'tel'), + (0x2122, 'M', 'tm'), + (0x2123, 'V'), + (0x2124, 'M', 'z'), + (0x2125, 'V'), + (0x2126, 'M', 'ω'), + (0x2127, 'V'), + (0x2128, 'M', 'z'), + (0x2129, 'V'), + (0x212A, 'M', 'k'), + (0x212B, 'M', 'å'), + (0x212C, 'M', 'b'), + (0x212D, 'M', 'c'), + (0x212E, 'V'), + (0x212F, 'M', 'e'), + (0x2131, 'M', 'f'), + (0x2132, 'X'), + (0x2133, 'M', 'm'), + (0x2134, 'M', 'o'), + (0x2135, 'M', 'א'), + (0x2136, 'M', 'ב'), + (0x2137, 'M', 'ג'), + (0x2138, 'M', 'ד'), + (0x2139, 'M', 'i'), + (0x213A, 'V'), + (0x213B, 'M', 'fax'), + (0x213C, 'M', 'π'), + (0x213D, 'M', 'γ'), + (0x213F, 'M', 'π'), + (0x2140, 'M', '∑'), + (0x2141, 'V'), + (0x2145, 'M', 'd'), + (0x2147, 'M', 'e'), + (0x2148, 'M', 'i'), + (0x2149, 'M', 'j'), + (0x214A, 'V'), + (0x2150, 'M', '1⁄7'), + (0x2151, 'M', '1⁄9'), + (0x2152, 'M', '1⁄10'), + (0x2153, 'M', '1⁄3'), + (0x2154, 'M', '2⁄3'), + (0x2155, 'M', '1⁄5'), + (0x2156, 'M', '2⁄5'), + (0x2157, 'M', '3⁄5'), + (0x2158, 'M', '4⁄5'), + (0x2159, 'M', '1⁄6'), + (0x215A, 'M', '5⁄6'), + (0x215B, 'M', '1⁄8'), + (0x215C, 'M', '3⁄8'), + (0x215D, 'M', '5⁄8'), + (0x215E, 'M', '7⁄8'), + (0x215F, 'M', '1⁄'), + (0x2160, 'M', 'i'), + (0x2161, 'M', 'ii'), + (0x2162, 'M', 'iii'), + (0x2163, 'M', 'iv'), + (0x2164, 'M', 'v'), + (0x2165, 'M', 'vi'), + (0x2166, 'M', 'vii'), + (0x2167, 'M', 'viii'), + (0x2168, 'M', 'ix'), + (0x2169, 'M', 'x'), + (0x216A, 'M', 'xi'), + (0x216B, 'M', 'xii'), + (0x216C, 'M', 'l'), + (0x216D, 'M', 'c'), + (0x216E, 'M', 'd'), + (0x216F, 'M', 'm'), + (0x2170, 'M', 'i'), + (0x2171, 'M', 'ii'), + (0x2172, 'M', 'iii'), + (0x2173, 'M', 'iv'), + (0x2174, 'M', 'v'), + (0x2175, 'M', 'vi'), + (0x2176, 'M', 'vii'), + (0x2177, 'M', 'viii'), + (0x2178, 'M', 'ix'), + (0x2179, 'M', 'x'), + (0x217A, 'M', 'xi'), + (0x217B, 'M', 'xii'), + (0x217C, 'M', 'l'), + ] + +def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x217D, 'M', 'c'), + (0x217E, 'M', 'd'), + (0x217F, 'M', 'm'), + (0x2180, 'V'), + (0x2183, 'X'), + (0x2184, 'V'), + (0x2189, 'M', '0⁄3'), + (0x218A, 'V'), + (0x218C, 'X'), + (0x2190, 'V'), + (0x222C, 'M', '∫∫'), + (0x222D, 'M', '∫∫∫'), + (0x222E, 'V'), + (0x222F, 'M', '∮∮'), + (0x2230, 'M', '∮∮∮'), + (0x2231, 'V'), + (0x2329, 'M', '〈'), + (0x232A, 'M', '〉'), + (0x232B, 'V'), + (0x2427, 'X'), + (0x2440, 'V'), + (0x244B, 'X'), + (0x2460, 'M', '1'), + (0x2461, 'M', '2'), + (0x2462, 'M', '3'), + (0x2463, 'M', '4'), + (0x2464, 'M', '5'), + (0x2465, 'M', '6'), + (0x2466, 'M', '7'), + (0x2467, 'M', '8'), + (0x2468, 'M', '9'), + (0x2469, 'M', '10'), + (0x246A, 'M', '11'), + (0x246B, 'M', '12'), + (0x246C, 'M', '13'), + (0x246D, 'M', '14'), + (0x246E, 'M', '15'), + (0x246F, 'M', '16'), + (0x2470, 'M', '17'), + (0x2471, 'M', '18'), + (0x2472, 'M', '19'), + (0x2473, 'M', '20'), + (0x2474, '3', '(1)'), + (0x2475, '3', '(2)'), + (0x2476, '3', '(3)'), + (0x2477, '3', '(4)'), + (0x2478, '3', '(5)'), + (0x2479, '3', '(6)'), + (0x247A, '3', '(7)'), + (0x247B, '3', '(8)'), + (0x247C, '3', '(9)'), + (0x247D, '3', '(10)'), + (0x247E, '3', '(11)'), + (0x247F, '3', '(12)'), + (0x2480, '3', '(13)'), + (0x2481, '3', '(14)'), + (0x2482, '3', '(15)'), + (0x2483, '3', '(16)'), + (0x2484, '3', '(17)'), + (0x2485, '3', '(18)'), + (0x2486, '3', '(19)'), + (0x2487, '3', '(20)'), + (0x2488, 'X'), + (0x249C, '3', '(a)'), + (0x249D, '3', '(b)'), + (0x249E, '3', '(c)'), + (0x249F, '3', '(d)'), + (0x24A0, '3', '(e)'), + (0x24A1, '3', '(f)'), + (0x24A2, '3', '(g)'), + (0x24A3, '3', '(h)'), + (0x24A4, '3', '(i)'), + (0x24A5, '3', '(j)'), + (0x24A6, '3', '(k)'), + (0x24A7, '3', '(l)'), + (0x24A8, '3', '(m)'), + (0x24A9, '3', '(n)'), + (0x24AA, '3', '(o)'), + (0x24AB, '3', '(p)'), + (0x24AC, '3', '(q)'), + (0x24AD, '3', '(r)'), + (0x24AE, '3', '(s)'), + (0x24AF, '3', '(t)'), + (0x24B0, '3', '(u)'), + (0x24B1, '3', '(v)'), + (0x24B2, '3', '(w)'), + (0x24B3, '3', '(x)'), + (0x24B4, '3', '(y)'), + (0x24B5, '3', '(z)'), + (0x24B6, 'M', 'a'), + (0x24B7, 'M', 'b'), + (0x24B8, 'M', 'c'), + (0x24B9, 'M', 'd'), + (0x24BA, 'M', 'e'), + (0x24BB, 'M', 'f'), + (0x24BC, 'M', 'g'), + (0x24BD, 'M', 'h'), + (0x24BE, 'M', 'i'), + (0x24BF, 'M', 'j'), + (0x24C0, 'M', 'k'), + ] + +def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x24C1, 'M', 'l'), + (0x24C2, 'M', 'm'), + (0x24C3, 'M', 'n'), + (0x24C4, 'M', 'o'), + (0x24C5, 'M', 'p'), + (0x24C6, 'M', 'q'), + (0x24C7, 'M', 'r'), + (0x24C8, 'M', 's'), + (0x24C9, 'M', 't'), + (0x24CA, 'M', 'u'), + (0x24CB, 'M', 'v'), + (0x24CC, 'M', 'w'), + (0x24CD, 'M', 'x'), + (0x24CE, 'M', 'y'), + (0x24CF, 'M', 'z'), + (0x24D0, 'M', 'a'), + (0x24D1, 'M', 'b'), + (0x24D2, 'M', 'c'), + (0x24D3, 'M', 'd'), + (0x24D4, 'M', 'e'), + (0x24D5, 'M', 'f'), + (0x24D6, 'M', 'g'), + (0x24D7, 'M', 'h'), + (0x24D8, 'M', 'i'), + (0x24D9, 'M', 'j'), + (0x24DA, 'M', 'k'), + (0x24DB, 'M', 'l'), + (0x24DC, 'M', 'm'), + (0x24DD, 'M', 'n'), + (0x24DE, 'M', 'o'), + (0x24DF, 'M', 'p'), + (0x24E0, 'M', 'q'), + (0x24E1, 'M', 'r'), + (0x24E2, 'M', 's'), + (0x24E3, 'M', 't'), + (0x24E4, 'M', 'u'), + (0x24E5, 'M', 'v'), + (0x24E6, 'M', 'w'), + (0x24E7, 'M', 'x'), + (0x24E8, 'M', 'y'), + (0x24E9, 'M', 'z'), + (0x24EA, 'M', '0'), + (0x24EB, 'V'), + (0x2A0C, 'M', '∫∫∫∫'), + (0x2A0D, 'V'), + (0x2A74, '3', '::='), + (0x2A75, '3', '=='), + (0x2A76, '3', '==='), + (0x2A77, 'V'), + (0x2ADC, 'M', '⫝̸'), + (0x2ADD, 'V'), + (0x2B74, 'X'), + (0x2B76, 'V'), + (0x2B96, 'X'), + (0x2B97, 'V'), + (0x2C00, 'M', 'ⰰ'), + (0x2C01, 'M', 'ⰱ'), + (0x2C02, 'M', 'ⰲ'), + (0x2C03, 'M', 'ⰳ'), + (0x2C04, 'M', 'ⰴ'), + (0x2C05, 'M', 'ⰵ'), + (0x2C06, 'M', 'ⰶ'), + (0x2C07, 'M', 'ⰷ'), + (0x2C08, 'M', 'ⰸ'), + (0x2C09, 'M', 'ⰹ'), + (0x2C0A, 'M', 'ⰺ'), + (0x2C0B, 'M', 'ⰻ'), + (0x2C0C, 'M', 'ⰼ'), + (0x2C0D, 'M', 'ⰽ'), + (0x2C0E, 'M', 'ⰾ'), + (0x2C0F, 'M', 'ⰿ'), + (0x2C10, 'M', 'ⱀ'), + (0x2C11, 'M', 'ⱁ'), + (0x2C12, 'M', 'ⱂ'), + (0x2C13, 'M', 'ⱃ'), + (0x2C14, 'M', 'ⱄ'), + (0x2C15, 'M', 'ⱅ'), + (0x2C16, 'M', 'ⱆ'), + (0x2C17, 'M', 'ⱇ'), + (0x2C18, 'M', 'ⱈ'), + (0x2C19, 'M', 'ⱉ'), + (0x2C1A, 'M', 'ⱊ'), + (0x2C1B, 'M', 'ⱋ'), + (0x2C1C, 'M', 'ⱌ'), + (0x2C1D, 'M', 'ⱍ'), + (0x2C1E, 'M', 'ⱎ'), + (0x2C1F, 'M', 'ⱏ'), + (0x2C20, 'M', 'ⱐ'), + (0x2C21, 'M', 'ⱑ'), + (0x2C22, 'M', 'ⱒ'), + (0x2C23, 'M', 'ⱓ'), + (0x2C24, 'M', 'ⱔ'), + (0x2C25, 'M', 'ⱕ'), + (0x2C26, 'M', 'ⱖ'), + (0x2C27, 'M', 'ⱗ'), + (0x2C28, 'M', 'ⱘ'), + (0x2C29, 'M', 'ⱙ'), + (0x2C2A, 'M', 'ⱚ'), + (0x2C2B, 'M', 'ⱛ'), + (0x2C2C, 'M', 'ⱜ'), + ] + +def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2C2D, 'M', 'ⱝ'), + (0x2C2E, 'M', 'ⱞ'), + (0x2C2F, 'M', 'ⱟ'), + (0x2C30, 'V'), + (0x2C60, 'M', 'ⱡ'), + (0x2C61, 'V'), + (0x2C62, 'M', 'ɫ'), + (0x2C63, 'M', 'ᵽ'), + (0x2C64, 'M', 'ɽ'), + (0x2C65, 'V'), + (0x2C67, 'M', 'ⱨ'), + (0x2C68, 'V'), + (0x2C69, 'M', 'ⱪ'), + (0x2C6A, 'V'), + (0x2C6B, 'M', 'ⱬ'), + (0x2C6C, 'V'), + (0x2C6D, 'M', 'ɑ'), + (0x2C6E, 'M', 'ɱ'), + (0x2C6F, 'M', 'ɐ'), + (0x2C70, 'M', 'ɒ'), + (0x2C71, 'V'), + (0x2C72, 'M', 'ⱳ'), + (0x2C73, 'V'), + (0x2C75, 'M', 'ⱶ'), + (0x2C76, 'V'), + (0x2C7C, 'M', 'j'), + (0x2C7D, 'M', 'v'), + (0x2C7E, 'M', 'ȿ'), + (0x2C7F, 'M', 'ɀ'), + (0x2C80, 'M', 'ⲁ'), + (0x2C81, 'V'), + (0x2C82, 'M', 'ⲃ'), + (0x2C83, 'V'), + (0x2C84, 'M', 'ⲅ'), + (0x2C85, 'V'), + (0x2C86, 'M', 'ⲇ'), + (0x2C87, 'V'), + (0x2C88, 'M', 'ⲉ'), + (0x2C89, 'V'), + (0x2C8A, 'M', 'ⲋ'), + (0x2C8B, 'V'), + (0x2C8C, 'M', 'ⲍ'), + (0x2C8D, 'V'), + (0x2C8E, 'M', 'ⲏ'), + (0x2C8F, 'V'), + (0x2C90, 'M', 'ⲑ'), + (0x2C91, 'V'), + (0x2C92, 'M', 'ⲓ'), + (0x2C93, 'V'), + (0x2C94, 'M', 'ⲕ'), + (0x2C95, 'V'), + (0x2C96, 'M', 'ⲗ'), + (0x2C97, 'V'), + (0x2C98, 'M', 'ⲙ'), + (0x2C99, 'V'), + (0x2C9A, 'M', 'ⲛ'), + (0x2C9B, 'V'), + (0x2C9C, 'M', 'ⲝ'), + (0x2C9D, 'V'), + (0x2C9E, 'M', 'ⲟ'), + (0x2C9F, 'V'), + (0x2CA0, 'M', 'ⲡ'), + (0x2CA1, 'V'), + (0x2CA2, 'M', 'ⲣ'), + (0x2CA3, 'V'), + (0x2CA4, 'M', 'ⲥ'), + (0x2CA5, 'V'), + (0x2CA6, 'M', 'ⲧ'), + (0x2CA7, 'V'), + (0x2CA8, 'M', 'ⲩ'), + (0x2CA9, 'V'), + (0x2CAA, 'M', 'ⲫ'), + (0x2CAB, 'V'), + (0x2CAC, 'M', 'ⲭ'), + (0x2CAD, 'V'), + (0x2CAE, 'M', 'ⲯ'), + (0x2CAF, 'V'), + (0x2CB0, 'M', 'ⲱ'), + (0x2CB1, 'V'), + (0x2CB2, 'M', 'ⲳ'), + (0x2CB3, 'V'), + (0x2CB4, 'M', 'ⲵ'), + (0x2CB5, 'V'), + (0x2CB6, 'M', 'ⲷ'), + (0x2CB7, 'V'), + (0x2CB8, 'M', 'ⲹ'), + (0x2CB9, 'V'), + (0x2CBA, 'M', 'ⲻ'), + (0x2CBB, 'V'), + (0x2CBC, 'M', 'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', 'ⲿ'), + (0x2CBF, 'V'), + (0x2CC0, 'M', 'ⳁ'), + (0x2CC1, 'V'), + (0x2CC2, 'M', 'ⳃ'), + (0x2CC3, 'V'), + (0x2CC4, 'M', 'ⳅ'), + (0x2CC5, 'V'), + (0x2CC6, 'M', 'ⳇ'), + ] + +def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2CC7, 'V'), + (0x2CC8, 'M', 'ⳉ'), + (0x2CC9, 'V'), + (0x2CCA, 'M', 'ⳋ'), + (0x2CCB, 'V'), + (0x2CCC, 'M', 'ⳍ'), + (0x2CCD, 'V'), + (0x2CCE, 'M', 'ⳏ'), + (0x2CCF, 'V'), + (0x2CD0, 'M', 'ⳑ'), + (0x2CD1, 'V'), + (0x2CD2, 'M', 'ⳓ'), + (0x2CD3, 'V'), + (0x2CD4, 'M', 'ⳕ'), + (0x2CD5, 'V'), + (0x2CD6, 'M', 'ⳗ'), + (0x2CD7, 'V'), + (0x2CD8, 'M', 'ⳙ'), + (0x2CD9, 'V'), + (0x2CDA, 'M', 'ⳛ'), + (0x2CDB, 'V'), + (0x2CDC, 'M', 'ⳝ'), + (0x2CDD, 'V'), + (0x2CDE, 'M', 'ⳟ'), + (0x2CDF, 'V'), + (0x2CE0, 'M', 'ⳡ'), + (0x2CE1, 'V'), + (0x2CE2, 'M', 'ⳣ'), + (0x2CE3, 'V'), + (0x2CEB, 'M', 'ⳬ'), + (0x2CEC, 'V'), + (0x2CED, 'M', 'ⳮ'), + (0x2CEE, 'V'), + (0x2CF2, 'M', 'ⳳ'), + (0x2CF3, 'V'), + (0x2CF4, 'X'), + (0x2CF9, 'V'), + (0x2D26, 'X'), + (0x2D27, 'V'), + (0x2D28, 'X'), + (0x2D2D, 'V'), + (0x2D2E, 'X'), + (0x2D30, 'V'), + (0x2D68, 'X'), + (0x2D6F, 'M', 'ⵡ'), + (0x2D70, 'V'), + (0x2D71, 'X'), + (0x2D7F, 'V'), + (0x2D97, 'X'), + (0x2DA0, 'V'), + (0x2DA7, 'X'), + (0x2DA8, 'V'), + (0x2DAF, 'X'), + (0x2DB0, 'V'), + (0x2DB7, 'X'), + (0x2DB8, 'V'), + (0x2DBF, 'X'), + (0x2DC0, 'V'), + (0x2DC7, 'X'), + (0x2DC8, 'V'), + (0x2DCF, 'X'), + (0x2DD0, 'V'), + (0x2DD7, 'X'), + (0x2DD8, 'V'), + (0x2DDF, 'X'), + (0x2DE0, 'V'), + (0x2E5E, 'X'), + (0x2E80, 'V'), + (0x2E9A, 'X'), + (0x2E9B, 'V'), + (0x2E9F, 'M', '母'), + (0x2EA0, 'V'), + (0x2EF3, 'M', '龟'), + (0x2EF4, 'X'), + (0x2F00, 'M', '一'), + (0x2F01, 'M', '丨'), + (0x2F02, 'M', '丶'), + (0x2F03, 'M', '丿'), + (0x2F04, 'M', '乙'), + (0x2F05, 'M', '亅'), + (0x2F06, 'M', '二'), + (0x2F07, 'M', '亠'), + (0x2F08, 'M', '人'), + (0x2F09, 'M', '儿'), + (0x2F0A, 'M', '入'), + (0x2F0B, 'M', '八'), + (0x2F0C, 'M', '冂'), + (0x2F0D, 'M', '冖'), + (0x2F0E, 'M', '冫'), + (0x2F0F, 'M', '几'), + (0x2F10, 'M', '凵'), + (0x2F11, 'M', '刀'), + (0x2F12, 'M', '力'), + (0x2F13, 'M', '勹'), + (0x2F14, 'M', '匕'), + (0x2F15, 'M', '匚'), + (0x2F16, 'M', '匸'), + (0x2F17, 'M', '十'), + (0x2F18, 'M', '卜'), + (0x2F19, 'M', '卩'), + ] + +def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F1A, 'M', '厂'), + (0x2F1B, 'M', '厶'), + (0x2F1C, 'M', '又'), + (0x2F1D, 'M', '口'), + (0x2F1E, 'M', '囗'), + (0x2F1F, 'M', '土'), + (0x2F20, 'M', '士'), + (0x2F21, 'M', '夂'), + (0x2F22, 'M', '夊'), + (0x2F23, 'M', '夕'), + (0x2F24, 'M', '大'), + (0x2F25, 'M', '女'), + (0x2F26, 'M', '子'), + (0x2F27, 'M', '宀'), + (0x2F28, 'M', '寸'), + (0x2F29, 'M', '小'), + (0x2F2A, 'M', '尢'), + (0x2F2B, 'M', '尸'), + (0x2F2C, 'M', '屮'), + (0x2F2D, 'M', '山'), + (0x2F2E, 'M', '巛'), + (0x2F2F, 'M', '工'), + (0x2F30, 'M', '己'), + (0x2F31, 'M', '巾'), + (0x2F32, 'M', '干'), + (0x2F33, 'M', '幺'), + (0x2F34, 'M', '广'), + (0x2F35, 'M', '廴'), + (0x2F36, 'M', '廾'), + (0x2F37, 'M', '弋'), + (0x2F38, 'M', '弓'), + (0x2F39, 'M', '彐'), + (0x2F3A, 'M', '彡'), + (0x2F3B, 'M', '彳'), + (0x2F3C, 'M', '心'), + (0x2F3D, 'M', '戈'), + (0x2F3E, 'M', '戶'), + (0x2F3F, 'M', '手'), + (0x2F40, 'M', '支'), + (0x2F41, 'M', '攴'), + (0x2F42, 'M', '文'), + (0x2F43, 'M', '斗'), + (0x2F44, 'M', '斤'), + (0x2F45, 'M', '方'), + (0x2F46, 'M', '无'), + (0x2F47, 'M', '日'), + (0x2F48, 'M', '曰'), + (0x2F49, 'M', '月'), + (0x2F4A, 'M', '木'), + (0x2F4B, 'M', '欠'), + (0x2F4C, 'M', '止'), + (0x2F4D, 'M', '歹'), + (0x2F4E, 'M', '殳'), + (0x2F4F, 'M', '毋'), + (0x2F50, 'M', '比'), + (0x2F51, 'M', '毛'), + (0x2F52, 'M', '氏'), + (0x2F53, 'M', '气'), + (0x2F54, 'M', '水'), + (0x2F55, 'M', '火'), + (0x2F56, 'M', '爪'), + (0x2F57, 'M', '父'), + (0x2F58, 'M', '爻'), + (0x2F59, 'M', '爿'), + (0x2F5A, 'M', '片'), + (0x2F5B, 'M', '牙'), + (0x2F5C, 'M', '牛'), + (0x2F5D, 'M', '犬'), + (0x2F5E, 'M', '玄'), + (0x2F5F, 'M', '玉'), + (0x2F60, 'M', '瓜'), + (0x2F61, 'M', '瓦'), + (0x2F62, 'M', '甘'), + (0x2F63, 'M', '生'), + (0x2F64, 'M', '用'), + (0x2F65, 'M', '田'), + (0x2F66, 'M', '疋'), + (0x2F67, 'M', '疒'), + (0x2F68, 'M', '癶'), + (0x2F69, 'M', '白'), + (0x2F6A, 'M', '皮'), + (0x2F6B, 'M', '皿'), + (0x2F6C, 'M', '目'), + (0x2F6D, 'M', '矛'), + (0x2F6E, 'M', '矢'), + (0x2F6F, 'M', '石'), + (0x2F70, 'M', '示'), + (0x2F71, 'M', '禸'), + (0x2F72, 'M', '禾'), + (0x2F73, 'M', '穴'), + (0x2F74, 'M', '立'), + (0x2F75, 'M', '竹'), + (0x2F76, 'M', '米'), + (0x2F77, 'M', '糸'), + (0x2F78, 'M', '缶'), + (0x2F79, 'M', '网'), + (0x2F7A, 'M', '羊'), + (0x2F7B, 'M', '羽'), + (0x2F7C, 'M', '老'), + (0x2F7D, 'M', '而'), + ] + +def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F7E, 'M', '耒'), + (0x2F7F, 'M', '耳'), + (0x2F80, 'M', '聿'), + (0x2F81, 'M', '肉'), + (0x2F82, 'M', '臣'), + (0x2F83, 'M', '自'), + (0x2F84, 'M', '至'), + (0x2F85, 'M', '臼'), + (0x2F86, 'M', '舌'), + (0x2F87, 'M', '舛'), + (0x2F88, 'M', '舟'), + (0x2F89, 'M', '艮'), + (0x2F8A, 'M', '色'), + (0x2F8B, 'M', '艸'), + (0x2F8C, 'M', '虍'), + (0x2F8D, 'M', '虫'), + (0x2F8E, 'M', '血'), + (0x2F8F, 'M', '行'), + (0x2F90, 'M', '衣'), + (0x2F91, 'M', '襾'), + (0x2F92, 'M', '見'), + (0x2F93, 'M', '角'), + (0x2F94, 'M', '言'), + (0x2F95, 'M', '谷'), + (0x2F96, 'M', '豆'), + (0x2F97, 'M', '豕'), + (0x2F98, 'M', '豸'), + (0x2F99, 'M', '貝'), + (0x2F9A, 'M', '赤'), + (0x2F9B, 'M', '走'), + (0x2F9C, 'M', '足'), + (0x2F9D, 'M', '身'), + (0x2F9E, 'M', '車'), + (0x2F9F, 'M', '辛'), + (0x2FA0, 'M', '辰'), + (0x2FA1, 'M', '辵'), + (0x2FA2, 'M', '邑'), + (0x2FA3, 'M', '酉'), + (0x2FA4, 'M', '釆'), + (0x2FA5, 'M', '里'), + (0x2FA6, 'M', '金'), + (0x2FA7, 'M', '長'), + (0x2FA8, 'M', '門'), + (0x2FA9, 'M', '阜'), + (0x2FAA, 'M', '隶'), + (0x2FAB, 'M', '隹'), + (0x2FAC, 'M', '雨'), + (0x2FAD, 'M', '靑'), + (0x2FAE, 'M', '非'), + (0x2FAF, 'M', '面'), + (0x2FB0, 'M', '革'), + (0x2FB1, 'M', '韋'), + (0x2FB2, 'M', '韭'), + (0x2FB3, 'M', '音'), + (0x2FB4, 'M', '頁'), + (0x2FB5, 'M', '風'), + (0x2FB6, 'M', '飛'), + (0x2FB7, 'M', '食'), + (0x2FB8, 'M', '首'), + (0x2FB9, 'M', '香'), + (0x2FBA, 'M', '馬'), + (0x2FBB, 'M', '骨'), + (0x2FBC, 'M', '高'), + (0x2FBD, 'M', '髟'), + (0x2FBE, 'M', '鬥'), + (0x2FBF, 'M', '鬯'), + (0x2FC0, 'M', '鬲'), + (0x2FC1, 'M', '鬼'), + (0x2FC2, 'M', '魚'), + (0x2FC3, 'M', '鳥'), + (0x2FC4, 'M', '鹵'), + (0x2FC5, 'M', '鹿'), + (0x2FC6, 'M', '麥'), + (0x2FC7, 'M', '麻'), + (0x2FC8, 'M', '黃'), + (0x2FC9, 'M', '黍'), + (0x2FCA, 'M', '黑'), + (0x2FCB, 'M', '黹'), + (0x2FCC, 'M', '黽'), + (0x2FCD, 'M', '鼎'), + (0x2FCE, 'M', '鼓'), + (0x2FCF, 'M', '鼠'), + (0x2FD0, 'M', '鼻'), + (0x2FD1, 'M', '齊'), + (0x2FD2, 'M', '齒'), + (0x2FD3, 'M', '龍'), + (0x2FD4, 'M', '龜'), + (0x2FD5, 'M', '龠'), + (0x2FD6, 'X'), + (0x3000, '3', ' '), + (0x3001, 'V'), + (0x3002, 'M', '.'), + (0x3003, 'V'), + (0x3036, 'M', '〒'), + (0x3037, 'V'), + (0x3038, 'M', '十'), + (0x3039, 'M', '卄'), + (0x303A, 'M', '卅'), + (0x303B, 'V'), + (0x3040, 'X'), + ] + +def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3041, 'V'), + (0x3097, 'X'), + (0x3099, 'V'), + (0x309B, '3', ' ゙'), + (0x309C, '3', ' ゚'), + (0x309D, 'V'), + (0x309F, 'M', 'より'), + (0x30A0, 'V'), + (0x30FF, 'M', 'コト'), + (0x3100, 'X'), + (0x3105, 'V'), + (0x3130, 'X'), + (0x3131, 'M', 'ᄀ'), + (0x3132, 'M', 'ᄁ'), + (0x3133, 'M', 'ᆪ'), + (0x3134, 'M', 'ᄂ'), + (0x3135, 'M', 'ᆬ'), + (0x3136, 'M', 'ᆭ'), + (0x3137, 'M', 'ᄃ'), + (0x3138, 'M', 'ᄄ'), + (0x3139, 'M', 'ᄅ'), + (0x313A, 'M', 'ᆰ'), + (0x313B, 'M', 'ᆱ'), + (0x313C, 'M', 'ᆲ'), + (0x313D, 'M', 'ᆳ'), + (0x313E, 'M', 'ᆴ'), + (0x313F, 'M', 'ᆵ'), + (0x3140, 'M', 'ᄚ'), + (0x3141, 'M', 'ᄆ'), + (0x3142, 'M', 'ᄇ'), + (0x3143, 'M', 'ᄈ'), + (0x3144, 'M', 'ᄡ'), + (0x3145, 'M', 'ᄉ'), + (0x3146, 'M', 'ᄊ'), + (0x3147, 'M', 'ᄋ'), + (0x3148, 'M', 'ᄌ'), + (0x3149, 'M', 'ᄍ'), + (0x314A, 'M', 'ᄎ'), + (0x314B, 'M', 'ᄏ'), + (0x314C, 'M', 'ᄐ'), + (0x314D, 'M', 'ᄑ'), + (0x314E, 'M', 'ᄒ'), + (0x314F, 'M', 'ᅡ'), + (0x3150, 'M', 'ᅢ'), + (0x3151, 'M', 'ᅣ'), + (0x3152, 'M', 'ᅤ'), + (0x3153, 'M', 'ᅥ'), + (0x3154, 'M', 'ᅦ'), + (0x3155, 'M', 'ᅧ'), + (0x3156, 'M', 'ᅨ'), + (0x3157, 'M', 'ᅩ'), + (0x3158, 'M', 'ᅪ'), + (0x3159, 'M', 'ᅫ'), + (0x315A, 'M', 'ᅬ'), + (0x315B, 'M', 'ᅭ'), + (0x315C, 'M', 'ᅮ'), + (0x315D, 'M', 'ᅯ'), + (0x315E, 'M', 'ᅰ'), + (0x315F, 'M', 'ᅱ'), + (0x3160, 'M', 'ᅲ'), + (0x3161, 'M', 'ᅳ'), + (0x3162, 'M', 'ᅴ'), + (0x3163, 'M', 'ᅵ'), + (0x3164, 'X'), + (0x3165, 'M', 'ᄔ'), + (0x3166, 'M', 'ᄕ'), + (0x3167, 'M', 'ᇇ'), + (0x3168, 'M', 'ᇈ'), + (0x3169, 'M', 'ᇌ'), + (0x316A, 'M', 'ᇎ'), + (0x316B, 'M', 'ᇓ'), + (0x316C, 'M', 'ᇗ'), + (0x316D, 'M', 'ᇙ'), + (0x316E, 'M', 'ᄜ'), + (0x316F, 'M', 'ᇝ'), + (0x3170, 'M', 'ᇟ'), + (0x3171, 'M', 'ᄝ'), + (0x3172, 'M', 'ᄞ'), + (0x3173, 'M', 'ᄠ'), + (0x3174, 'M', 'ᄢ'), + (0x3175, 'M', 'ᄣ'), + (0x3176, 'M', 'ᄧ'), + (0x3177, 'M', 'ᄩ'), + (0x3178, 'M', 'ᄫ'), + (0x3179, 'M', 'ᄬ'), + (0x317A, 'M', 'ᄭ'), + (0x317B, 'M', 'ᄮ'), + (0x317C, 'M', 'ᄯ'), + (0x317D, 'M', 'ᄲ'), + (0x317E, 'M', 'ᄶ'), + (0x317F, 'M', 'ᅀ'), + (0x3180, 'M', 'ᅇ'), + (0x3181, 'M', 'ᅌ'), + (0x3182, 'M', 'ᇱ'), + (0x3183, 'M', 'ᇲ'), + (0x3184, 'M', 'ᅗ'), + (0x3185, 'M', 'ᅘ'), + (0x3186, 'M', 'ᅙ'), + (0x3187, 'M', 'ᆄ'), + (0x3188, 'M', 'ᆅ'), + ] + +def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3189, 'M', 'ᆈ'), + (0x318A, 'M', 'ᆑ'), + (0x318B, 'M', 'ᆒ'), + (0x318C, 'M', 'ᆔ'), + (0x318D, 'M', 'ᆞ'), + (0x318E, 'M', 'ᆡ'), + (0x318F, 'X'), + (0x3190, 'V'), + (0x3192, 'M', '一'), + (0x3193, 'M', '二'), + (0x3194, 'M', '三'), + (0x3195, 'M', '四'), + (0x3196, 'M', '上'), + (0x3197, 'M', '中'), + (0x3198, 'M', '下'), + (0x3199, 'M', '甲'), + (0x319A, 'M', '乙'), + (0x319B, 'M', '丙'), + (0x319C, 'M', '丁'), + (0x319D, 'M', '天'), + (0x319E, 'M', '地'), + (0x319F, 'M', '人'), + (0x31A0, 'V'), + (0x31E4, 'X'), + (0x31F0, 'V'), + (0x3200, '3', '(ᄀ)'), + (0x3201, '3', '(ᄂ)'), + (0x3202, '3', '(ᄃ)'), + (0x3203, '3', '(ᄅ)'), + (0x3204, '3', '(ᄆ)'), + (0x3205, '3', '(ᄇ)'), + (0x3206, '3', '(ᄉ)'), + (0x3207, '3', '(ᄋ)'), + (0x3208, '3', '(ᄌ)'), + (0x3209, '3', '(ᄎ)'), + (0x320A, '3', '(ᄏ)'), + (0x320B, '3', '(ᄐ)'), + (0x320C, '3', '(ᄑ)'), + (0x320D, '3', '(ᄒ)'), + (0x320E, '3', '(가)'), + (0x320F, '3', '(나)'), + (0x3210, '3', '(다)'), + (0x3211, '3', '(라)'), + (0x3212, '3', '(마)'), + (0x3213, '3', '(바)'), + (0x3214, '3', '(사)'), + (0x3215, '3', '(아)'), + (0x3216, '3', '(자)'), + (0x3217, '3', '(차)'), + (0x3218, '3', '(카)'), + (0x3219, '3', '(타)'), + (0x321A, '3', '(파)'), + (0x321B, '3', '(하)'), + (0x321C, '3', '(주)'), + (0x321D, '3', '(오전)'), + (0x321E, '3', '(오후)'), + (0x321F, 'X'), + (0x3220, '3', '(一)'), + (0x3221, '3', '(二)'), + (0x3222, '3', '(三)'), + (0x3223, '3', '(四)'), + (0x3224, '3', '(五)'), + (0x3225, '3', '(六)'), + (0x3226, '3', '(七)'), + (0x3227, '3', '(八)'), + (0x3228, '3', '(九)'), + (0x3229, '3', '(十)'), + (0x322A, '3', '(月)'), + (0x322B, '3', '(火)'), + (0x322C, '3', '(水)'), + (0x322D, '3', '(木)'), + (0x322E, '3', '(金)'), + (0x322F, '3', '(土)'), + (0x3230, '3', '(日)'), + (0x3231, '3', '(株)'), + (0x3232, '3', '(有)'), + (0x3233, '3', '(社)'), + (0x3234, '3', '(名)'), + (0x3235, '3', '(特)'), + (0x3236, '3', '(財)'), + (0x3237, '3', '(祝)'), + (0x3238, '3', '(労)'), + (0x3239, '3', '(代)'), + (0x323A, '3', '(呼)'), + (0x323B, '3', '(学)'), + (0x323C, '3', '(監)'), + (0x323D, '3', '(企)'), + (0x323E, '3', '(資)'), + (0x323F, '3', '(協)'), + (0x3240, '3', '(祭)'), + (0x3241, '3', '(休)'), + (0x3242, '3', '(自)'), + (0x3243, '3', '(至)'), + (0x3244, 'M', '問'), + (0x3245, 'M', '幼'), + (0x3246, 'M', '文'), + (0x3247, 'M', '箏'), + (0x3248, 'V'), + (0x3250, 'M', 'pte'), + (0x3251, 'M', '21'), + ] + +def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3252, 'M', '22'), + (0x3253, 'M', '23'), + (0x3254, 'M', '24'), + (0x3255, 'M', '25'), + (0x3256, 'M', '26'), + (0x3257, 'M', '27'), + (0x3258, 'M', '28'), + (0x3259, 'M', '29'), + (0x325A, 'M', '30'), + (0x325B, 'M', '31'), + (0x325C, 'M', '32'), + (0x325D, 'M', '33'), + (0x325E, 'M', '34'), + (0x325F, 'M', '35'), + (0x3260, 'M', 'ᄀ'), + (0x3261, 'M', 'ᄂ'), + (0x3262, 'M', 'ᄃ'), + (0x3263, 'M', 'ᄅ'), + (0x3264, 'M', 'ᄆ'), + (0x3265, 'M', 'ᄇ'), + (0x3266, 'M', 'ᄉ'), + (0x3267, 'M', 'ᄋ'), + (0x3268, 'M', 'ᄌ'), + (0x3269, 'M', 'ᄎ'), + (0x326A, 'M', 'ᄏ'), + (0x326B, 'M', 'ᄐ'), + (0x326C, 'M', 'ᄑ'), + (0x326D, 'M', 'ᄒ'), + (0x326E, 'M', '가'), + (0x326F, 'M', '나'), + (0x3270, 'M', '다'), + (0x3271, 'M', '라'), + (0x3272, 'M', '마'), + (0x3273, 'M', '바'), + (0x3274, 'M', '사'), + (0x3275, 'M', '아'), + (0x3276, 'M', '자'), + (0x3277, 'M', '차'), + (0x3278, 'M', '카'), + (0x3279, 'M', '타'), + (0x327A, 'M', '파'), + (0x327B, 'M', '하'), + (0x327C, 'M', '참고'), + (0x327D, 'M', '주의'), + (0x327E, 'M', '우'), + (0x327F, 'V'), + (0x3280, 'M', '一'), + (0x3281, 'M', '二'), + (0x3282, 'M', '三'), + (0x3283, 'M', '四'), + (0x3284, 'M', '五'), + (0x3285, 'M', '六'), + (0x3286, 'M', '七'), + (0x3287, 'M', '八'), + (0x3288, 'M', '九'), + (0x3289, 'M', '十'), + (0x328A, 'M', '月'), + (0x328B, 'M', '火'), + (0x328C, 'M', '水'), + (0x328D, 'M', '木'), + (0x328E, 'M', '金'), + (0x328F, 'M', '土'), + (0x3290, 'M', '日'), + (0x3291, 'M', '株'), + (0x3292, 'M', '有'), + (0x3293, 'M', '社'), + (0x3294, 'M', '名'), + (0x3295, 'M', '特'), + (0x3296, 'M', '財'), + (0x3297, 'M', '祝'), + (0x3298, 'M', '労'), + (0x3299, 'M', '秘'), + (0x329A, 'M', '男'), + (0x329B, 'M', '女'), + (0x329C, 'M', '適'), + (0x329D, 'M', '優'), + (0x329E, 'M', '印'), + (0x329F, 'M', '注'), + (0x32A0, 'M', '項'), + (0x32A1, 'M', '休'), + (0x32A2, 'M', '写'), + (0x32A3, 'M', '正'), + (0x32A4, 'M', '上'), + (0x32A5, 'M', '中'), + (0x32A6, 'M', '下'), + (0x32A7, 'M', '左'), + (0x32A8, 'M', '右'), + (0x32A9, 'M', '医'), + (0x32AA, 'M', '宗'), + (0x32AB, 'M', '学'), + (0x32AC, 'M', '監'), + (0x32AD, 'M', '企'), + (0x32AE, 'M', '資'), + (0x32AF, 'M', '協'), + (0x32B0, 'M', '夜'), + (0x32B1, 'M', '36'), + (0x32B2, 'M', '37'), + (0x32B3, 'M', '38'), + (0x32B4, 'M', '39'), + (0x32B5, 'M', '40'), + ] + +def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x32B6, 'M', '41'), + (0x32B7, 'M', '42'), + (0x32B8, 'M', '43'), + (0x32B9, 'M', '44'), + (0x32BA, 'M', '45'), + (0x32BB, 'M', '46'), + (0x32BC, 'M', '47'), + (0x32BD, 'M', '48'), + (0x32BE, 'M', '49'), + (0x32BF, 'M', '50'), + (0x32C0, 'M', '1月'), + (0x32C1, 'M', '2月'), + (0x32C2, 'M', '3月'), + (0x32C3, 'M', '4月'), + (0x32C4, 'M', '5月'), + (0x32C5, 'M', '6月'), + (0x32C6, 'M', '7月'), + (0x32C7, 'M', '8月'), + (0x32C8, 'M', '9月'), + (0x32C9, 'M', '10月'), + (0x32CA, 'M', '11月'), + (0x32CB, 'M', '12月'), + (0x32CC, 'M', 'hg'), + (0x32CD, 'M', 'erg'), + (0x32CE, 'M', 'ev'), + (0x32CF, 'M', 'ltd'), + (0x32D0, 'M', 'ア'), + (0x32D1, 'M', 'イ'), + (0x32D2, 'M', 'ウ'), + (0x32D3, 'M', 'エ'), + (0x32D4, 'M', 'オ'), + (0x32D5, 'M', 'カ'), + (0x32D6, 'M', 'キ'), + (0x32D7, 'M', 'ク'), + (0x32D8, 'M', 'ケ'), + (0x32D9, 'M', 'コ'), + (0x32DA, 'M', 'サ'), + (0x32DB, 'M', 'シ'), + (0x32DC, 'M', 'ス'), + (0x32DD, 'M', 'セ'), + (0x32DE, 'M', 'ソ'), + (0x32DF, 'M', 'タ'), + (0x32E0, 'M', 'チ'), + (0x32E1, 'M', 'ツ'), + (0x32E2, 'M', 'テ'), + (0x32E3, 'M', 'ト'), + (0x32E4, 'M', 'ナ'), + (0x32E5, 'M', 'ニ'), + (0x32E6, 'M', 'ヌ'), + (0x32E7, 'M', 'ネ'), + (0x32E8, 'M', 'ノ'), + (0x32E9, 'M', 'ハ'), + (0x32EA, 'M', 'ヒ'), + (0x32EB, 'M', 'フ'), + (0x32EC, 'M', 'ヘ'), + (0x32ED, 'M', 'ホ'), + (0x32EE, 'M', 'マ'), + (0x32EF, 'M', 'ミ'), + (0x32F0, 'M', 'ム'), + (0x32F1, 'M', 'メ'), + (0x32F2, 'M', 'モ'), + (0x32F3, 'M', 'ヤ'), + (0x32F4, 'M', 'ユ'), + (0x32F5, 'M', 'ヨ'), + (0x32F6, 'M', 'ラ'), + (0x32F7, 'M', 'リ'), + (0x32F8, 'M', 'ル'), + (0x32F9, 'M', 'レ'), + (0x32FA, 'M', 'ロ'), + (0x32FB, 'M', 'ワ'), + (0x32FC, 'M', 'ヰ'), + (0x32FD, 'M', 'ヱ'), + (0x32FE, 'M', 'ヲ'), + (0x32FF, 'M', '令和'), + (0x3300, 'M', 'アパート'), + (0x3301, 'M', 'アルファ'), + (0x3302, 'M', 'アンペア'), + (0x3303, 'M', 'アール'), + (0x3304, 'M', 'イニング'), + (0x3305, 'M', 'インチ'), + (0x3306, 'M', 'ウォン'), + (0x3307, 'M', 'エスクード'), + (0x3308, 'M', 'エーカー'), + (0x3309, 'M', 'オンス'), + (0x330A, 'M', 'オーム'), + (0x330B, 'M', 'カイリ'), + (0x330C, 'M', 'カラット'), + (0x330D, 'M', 'カロリー'), + (0x330E, 'M', 'ガロン'), + (0x330F, 'M', 'ガンマ'), + (0x3310, 'M', 'ギガ'), + (0x3311, 'M', 'ギニー'), + (0x3312, 'M', 'キュリー'), + (0x3313, 'M', 'ギルダー'), + (0x3314, 'M', 'キロ'), + (0x3315, 'M', 'キログラム'), + (0x3316, 'M', 'キロメートル'), + (0x3317, 'M', 'キロワット'), + (0x3318, 'M', 'グラム'), + (0x3319, 'M', 'グラムトン'), + ] + +def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x331A, 'M', 'クルゼイロ'), + (0x331B, 'M', 'クローネ'), + (0x331C, 'M', 'ケース'), + (0x331D, 'M', 'コルナ'), + (0x331E, 'M', 'コーポ'), + (0x331F, 'M', 'サイクル'), + (0x3320, 'M', 'サンチーム'), + (0x3321, 'M', 'シリング'), + (0x3322, 'M', 'センチ'), + (0x3323, 'M', 'セント'), + (0x3324, 'M', 'ダース'), + (0x3325, 'M', 'デシ'), + (0x3326, 'M', 'ドル'), + (0x3327, 'M', 'トン'), + (0x3328, 'M', 'ナノ'), + (0x3329, 'M', 'ノット'), + (0x332A, 'M', 'ハイツ'), + (0x332B, 'M', 'パーセント'), + (0x332C, 'M', 'パーツ'), + (0x332D, 'M', 'バーレル'), + (0x332E, 'M', 'ピアストル'), + (0x332F, 'M', 'ピクル'), + (0x3330, 'M', 'ピコ'), + (0x3331, 'M', 'ビル'), + (0x3332, 'M', 'ファラッド'), + (0x3333, 'M', 'フィート'), + (0x3334, 'M', 'ブッシェル'), + (0x3335, 'M', 'フラン'), + (0x3336, 'M', 'ヘクタール'), + (0x3337, 'M', 'ペソ'), + (0x3338, 'M', 'ペニヒ'), + (0x3339, 'M', 'ヘルツ'), + (0x333A, 'M', 'ペンス'), + (0x333B, 'M', 'ページ'), + (0x333C, 'M', 'ベータ'), + (0x333D, 'M', 'ポイント'), + (0x333E, 'M', 'ボルト'), + (0x333F, 'M', 'ホン'), + (0x3340, 'M', 'ポンド'), + (0x3341, 'M', 'ホール'), + (0x3342, 'M', 'ホーン'), + (0x3343, 'M', 'マイクロ'), + (0x3344, 'M', 'マイル'), + (0x3345, 'M', 'マッハ'), + (0x3346, 'M', 'マルク'), + (0x3347, 'M', 'マンション'), + (0x3348, 'M', 'ミクロン'), + (0x3349, 'M', 'ミリ'), + (0x334A, 'M', 'ミリバール'), + (0x334B, 'M', 'メガ'), + (0x334C, 'M', 'メガトン'), + (0x334D, 'M', 'メートル'), + (0x334E, 'M', 'ヤード'), + (0x334F, 'M', 'ヤール'), + (0x3350, 'M', 'ユアン'), + (0x3351, 'M', 'リットル'), + (0x3352, 'M', 'リラ'), + (0x3353, 'M', 'ルピー'), + (0x3354, 'M', 'ルーブル'), + (0x3355, 'M', 'レム'), + (0x3356, 'M', 'レントゲン'), + (0x3357, 'M', 'ワット'), + (0x3358, 'M', '0点'), + (0x3359, 'M', '1点'), + (0x335A, 'M', '2点'), + (0x335B, 'M', '3点'), + (0x335C, 'M', '4点'), + (0x335D, 'M', '5点'), + (0x335E, 'M', '6点'), + (0x335F, 'M', '7点'), + (0x3360, 'M', '8点'), + (0x3361, 'M', '9点'), + (0x3362, 'M', '10点'), + (0x3363, 'M', '11点'), + (0x3364, 'M', '12点'), + (0x3365, 'M', '13点'), + (0x3366, 'M', '14点'), + (0x3367, 'M', '15点'), + (0x3368, 'M', '16点'), + (0x3369, 'M', '17点'), + (0x336A, 'M', '18点'), + (0x336B, 'M', '19点'), + (0x336C, 'M', '20点'), + (0x336D, 'M', '21点'), + (0x336E, 'M', '22点'), + (0x336F, 'M', '23点'), + (0x3370, 'M', '24点'), + (0x3371, 'M', 'hpa'), + (0x3372, 'M', 'da'), + (0x3373, 'M', 'au'), + (0x3374, 'M', 'bar'), + (0x3375, 'M', 'ov'), + (0x3376, 'M', 'pc'), + (0x3377, 'M', 'dm'), + (0x3378, 'M', 'dm2'), + (0x3379, 'M', 'dm3'), + (0x337A, 'M', 'iu'), + (0x337B, 'M', '平成'), + (0x337C, 'M', '昭和'), + (0x337D, 'M', '大正'), + ] + +def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x337E, 'M', '明治'), + (0x337F, 'M', '株式会社'), + (0x3380, 'M', 'pa'), + (0x3381, 'M', 'na'), + (0x3382, 'M', 'μa'), + (0x3383, 'M', 'ma'), + (0x3384, 'M', 'ka'), + (0x3385, 'M', 'kb'), + (0x3386, 'M', 'mb'), + (0x3387, 'M', 'gb'), + (0x3388, 'M', 'cal'), + (0x3389, 'M', 'kcal'), + (0x338A, 'M', 'pf'), + (0x338B, 'M', 'nf'), + (0x338C, 'M', 'μf'), + (0x338D, 'M', 'μg'), + (0x338E, 'M', 'mg'), + (0x338F, 'M', 'kg'), + (0x3390, 'M', 'hz'), + (0x3391, 'M', 'khz'), + (0x3392, 'M', 'mhz'), + (0x3393, 'M', 'ghz'), + (0x3394, 'M', 'thz'), + (0x3395, 'M', 'μl'), + (0x3396, 'M', 'ml'), + (0x3397, 'M', 'dl'), + (0x3398, 'M', 'kl'), + (0x3399, 'M', 'fm'), + (0x339A, 'M', 'nm'), + (0x339B, 'M', 'μm'), + (0x339C, 'M', 'mm'), + (0x339D, 'M', 'cm'), + (0x339E, 'M', 'km'), + (0x339F, 'M', 'mm2'), + (0x33A0, 'M', 'cm2'), + (0x33A1, 'M', 'm2'), + (0x33A2, 'M', 'km2'), + (0x33A3, 'M', 'mm3'), + (0x33A4, 'M', 'cm3'), + (0x33A5, 'M', 'm3'), + (0x33A6, 'M', 'km3'), + (0x33A7, 'M', 'm∕s'), + (0x33A8, 'M', 'm∕s2'), + (0x33A9, 'M', 'pa'), + (0x33AA, 'M', 'kpa'), + (0x33AB, 'M', 'mpa'), + (0x33AC, 'M', 'gpa'), + (0x33AD, 'M', 'rad'), + (0x33AE, 'M', 'rad∕s'), + (0x33AF, 'M', 'rad∕s2'), + (0x33B0, 'M', 'ps'), + (0x33B1, 'M', 'ns'), + (0x33B2, 'M', 'μs'), + (0x33B3, 'M', 'ms'), + (0x33B4, 'M', 'pv'), + (0x33B5, 'M', 'nv'), + (0x33B6, 'M', 'μv'), + (0x33B7, 'M', 'mv'), + (0x33B8, 'M', 'kv'), + (0x33B9, 'M', 'mv'), + (0x33BA, 'M', 'pw'), + (0x33BB, 'M', 'nw'), + (0x33BC, 'M', 'μw'), + (0x33BD, 'M', 'mw'), + (0x33BE, 'M', 'kw'), + (0x33BF, 'M', 'mw'), + (0x33C0, 'M', 'kω'), + (0x33C1, 'M', 'mω'), + (0x33C2, 'X'), + (0x33C3, 'M', 'bq'), + (0x33C4, 'M', 'cc'), + (0x33C5, 'M', 'cd'), + (0x33C6, 'M', 'c∕kg'), + (0x33C7, 'X'), + (0x33C8, 'M', 'db'), + (0x33C9, 'M', 'gy'), + (0x33CA, 'M', 'ha'), + (0x33CB, 'M', 'hp'), + (0x33CC, 'M', 'in'), + (0x33CD, 'M', 'kk'), + (0x33CE, 'M', 'km'), + (0x33CF, 'M', 'kt'), + (0x33D0, 'M', 'lm'), + (0x33D1, 'M', 'ln'), + (0x33D2, 'M', 'log'), + (0x33D3, 'M', 'lx'), + (0x33D4, 'M', 'mb'), + (0x33D5, 'M', 'mil'), + (0x33D6, 'M', 'mol'), + (0x33D7, 'M', 'ph'), + (0x33D8, 'X'), + (0x33D9, 'M', 'ppm'), + (0x33DA, 'M', 'pr'), + (0x33DB, 'M', 'sr'), + (0x33DC, 'M', 'sv'), + (0x33DD, 'M', 'wb'), + (0x33DE, 'M', 'v∕m'), + (0x33DF, 'M', 'a∕m'), + (0x33E0, 'M', '1日'), + (0x33E1, 'M', '2日'), + ] + +def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x33E2, 'M', '3日'), + (0x33E3, 'M', '4日'), + (0x33E4, 'M', '5日'), + (0x33E5, 'M', '6日'), + (0x33E6, 'M', '7日'), + (0x33E7, 'M', '8日'), + (0x33E8, 'M', '9日'), + (0x33E9, 'M', '10日'), + (0x33EA, 'M', '11日'), + (0x33EB, 'M', '12日'), + (0x33EC, 'M', '13日'), + (0x33ED, 'M', '14日'), + (0x33EE, 'M', '15日'), + (0x33EF, 'M', '16日'), + (0x33F0, 'M', '17日'), + (0x33F1, 'M', '18日'), + (0x33F2, 'M', '19日'), + (0x33F3, 'M', '20日'), + (0x33F4, 'M', '21日'), + (0x33F5, 'M', '22日'), + (0x33F6, 'M', '23日'), + (0x33F7, 'M', '24日'), + (0x33F8, 'M', '25日'), + (0x33F9, 'M', '26日'), + (0x33FA, 'M', '27日'), + (0x33FB, 'M', '28日'), + (0x33FC, 'M', '29日'), + (0x33FD, 'M', '30日'), + (0x33FE, 'M', '31日'), + (0x33FF, 'M', 'gal'), + (0x3400, 'V'), + (0xA48D, 'X'), + (0xA490, 'V'), + (0xA4C7, 'X'), + (0xA4D0, 'V'), + (0xA62C, 'X'), + (0xA640, 'M', 'ꙁ'), + (0xA641, 'V'), + (0xA642, 'M', 'ꙃ'), + (0xA643, 'V'), + (0xA644, 'M', 'ꙅ'), + (0xA645, 'V'), + (0xA646, 'M', 'ꙇ'), + (0xA647, 'V'), + (0xA648, 'M', 'ꙉ'), + (0xA649, 'V'), + (0xA64A, 'M', 'ꙋ'), + (0xA64B, 'V'), + (0xA64C, 'M', 'ꙍ'), + (0xA64D, 'V'), + (0xA64E, 'M', 'ꙏ'), + (0xA64F, 'V'), + (0xA650, 'M', 'ꙑ'), + (0xA651, 'V'), + (0xA652, 'M', 'ꙓ'), + (0xA653, 'V'), + (0xA654, 'M', 'ꙕ'), + (0xA655, 'V'), + (0xA656, 'M', 'ꙗ'), + (0xA657, 'V'), + (0xA658, 'M', 'ꙙ'), + (0xA659, 'V'), + (0xA65A, 'M', 'ꙛ'), + (0xA65B, 'V'), + (0xA65C, 'M', 'ꙝ'), + (0xA65D, 'V'), + (0xA65E, 'M', 'ꙟ'), + (0xA65F, 'V'), + (0xA660, 'M', 'ꙡ'), + (0xA661, 'V'), + (0xA662, 'M', 'ꙣ'), + (0xA663, 'V'), + (0xA664, 'M', 'ꙥ'), + (0xA665, 'V'), + (0xA666, 'M', 'ꙧ'), + (0xA667, 'V'), + (0xA668, 'M', 'ꙩ'), + (0xA669, 'V'), + (0xA66A, 'M', 'ꙫ'), + (0xA66B, 'V'), + (0xA66C, 'M', 'ꙭ'), + (0xA66D, 'V'), + (0xA680, 'M', 'ꚁ'), + (0xA681, 'V'), + (0xA682, 'M', 'ꚃ'), + (0xA683, 'V'), + (0xA684, 'M', 'ꚅ'), + (0xA685, 'V'), + (0xA686, 'M', 'ꚇ'), + (0xA687, 'V'), + (0xA688, 'M', 'ꚉ'), + (0xA689, 'V'), + (0xA68A, 'M', 'ꚋ'), + (0xA68B, 'V'), + (0xA68C, 'M', 'ꚍ'), + (0xA68D, 'V'), + (0xA68E, 'M', 'ꚏ'), + (0xA68F, 'V'), + (0xA690, 'M', 'ꚑ'), + (0xA691, 'V'), + ] + +def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA692, 'M', 'ꚓ'), + (0xA693, 'V'), + (0xA694, 'M', 'ꚕ'), + (0xA695, 'V'), + (0xA696, 'M', 'ꚗ'), + (0xA697, 'V'), + (0xA698, 'M', 'ꚙ'), + (0xA699, 'V'), + (0xA69A, 'M', 'ꚛ'), + (0xA69B, 'V'), + (0xA69C, 'M', 'ъ'), + (0xA69D, 'M', 'ь'), + (0xA69E, 'V'), + (0xA6F8, 'X'), + (0xA700, 'V'), + (0xA722, 'M', 'ꜣ'), + (0xA723, 'V'), + (0xA724, 'M', 'ꜥ'), + (0xA725, 'V'), + (0xA726, 'M', 'ꜧ'), + (0xA727, 'V'), + (0xA728, 'M', 'ꜩ'), + (0xA729, 'V'), + (0xA72A, 'M', 'ꜫ'), + (0xA72B, 'V'), + (0xA72C, 'M', 'ꜭ'), + (0xA72D, 'V'), + (0xA72E, 'M', 'ꜯ'), + (0xA72F, 'V'), + (0xA732, 'M', 'ꜳ'), + (0xA733, 'V'), + (0xA734, 'M', 'ꜵ'), + (0xA735, 'V'), + (0xA736, 'M', 'ꜷ'), + (0xA737, 'V'), + (0xA738, 'M', 'ꜹ'), + (0xA739, 'V'), + (0xA73A, 'M', 'ꜻ'), + (0xA73B, 'V'), + (0xA73C, 'M', 'ꜽ'), + (0xA73D, 'V'), + (0xA73E, 'M', 'ꜿ'), + (0xA73F, 'V'), + (0xA740, 'M', 'ꝁ'), + (0xA741, 'V'), + (0xA742, 'M', 'ꝃ'), + (0xA743, 'V'), + (0xA744, 'M', 'ꝅ'), + (0xA745, 'V'), + (0xA746, 'M', 'ꝇ'), + (0xA747, 'V'), + (0xA748, 'M', 'ꝉ'), + (0xA749, 'V'), + (0xA74A, 'M', 'ꝋ'), + (0xA74B, 'V'), + (0xA74C, 'M', 'ꝍ'), + (0xA74D, 'V'), + (0xA74E, 'M', 'ꝏ'), + (0xA74F, 'V'), + (0xA750, 'M', 'ꝑ'), + (0xA751, 'V'), + (0xA752, 'M', 'ꝓ'), + (0xA753, 'V'), + (0xA754, 'M', 'ꝕ'), + (0xA755, 'V'), + (0xA756, 'M', 'ꝗ'), + (0xA757, 'V'), + (0xA758, 'M', 'ꝙ'), + (0xA759, 'V'), + (0xA75A, 'M', 'ꝛ'), + (0xA75B, 'V'), + (0xA75C, 'M', 'ꝝ'), + (0xA75D, 'V'), + (0xA75E, 'M', 'ꝟ'), + (0xA75F, 'V'), + (0xA760, 'M', 'ꝡ'), + (0xA761, 'V'), + (0xA762, 'M', 'ꝣ'), + (0xA763, 'V'), + (0xA764, 'M', 'ꝥ'), + (0xA765, 'V'), + (0xA766, 'M', 'ꝧ'), + (0xA767, 'V'), + (0xA768, 'M', 'ꝩ'), + (0xA769, 'V'), + (0xA76A, 'M', 'ꝫ'), + (0xA76B, 'V'), + (0xA76C, 'M', 'ꝭ'), + (0xA76D, 'V'), + (0xA76E, 'M', 'ꝯ'), + (0xA76F, 'V'), + (0xA770, 'M', 'ꝯ'), + (0xA771, 'V'), + (0xA779, 'M', 'ꝺ'), + (0xA77A, 'V'), + (0xA77B, 'M', 'ꝼ'), + (0xA77C, 'V'), + (0xA77D, 'M', 'ᵹ'), + (0xA77E, 'M', 'ꝿ'), + (0xA77F, 'V'), + ] + +def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA780, 'M', 'ꞁ'), + (0xA781, 'V'), + (0xA782, 'M', 'ꞃ'), + (0xA783, 'V'), + (0xA784, 'M', 'ꞅ'), + (0xA785, 'V'), + (0xA786, 'M', 'ꞇ'), + (0xA787, 'V'), + (0xA78B, 'M', 'ꞌ'), + (0xA78C, 'V'), + (0xA78D, 'M', 'ɥ'), + (0xA78E, 'V'), + (0xA790, 'M', 'ꞑ'), + (0xA791, 'V'), + (0xA792, 'M', 'ꞓ'), + (0xA793, 'V'), + (0xA796, 'M', 'ꞗ'), + (0xA797, 'V'), + (0xA798, 'M', 'ꞙ'), + (0xA799, 'V'), + (0xA79A, 'M', 'ꞛ'), + (0xA79B, 'V'), + (0xA79C, 'M', 'ꞝ'), + (0xA79D, 'V'), + (0xA79E, 'M', 'ꞟ'), + (0xA79F, 'V'), + (0xA7A0, 'M', 'ꞡ'), + (0xA7A1, 'V'), + (0xA7A2, 'M', 'ꞣ'), + (0xA7A3, 'V'), + (0xA7A4, 'M', 'ꞥ'), + (0xA7A5, 'V'), + (0xA7A6, 'M', 'ꞧ'), + (0xA7A7, 'V'), + (0xA7A8, 'M', 'ꞩ'), + (0xA7A9, 'V'), + (0xA7AA, 'M', 'ɦ'), + (0xA7AB, 'M', 'ɜ'), + (0xA7AC, 'M', 'ɡ'), + (0xA7AD, 'M', 'ɬ'), + (0xA7AE, 'M', 'ɪ'), + (0xA7AF, 'V'), + (0xA7B0, 'M', 'ʞ'), + (0xA7B1, 'M', 'ʇ'), + (0xA7B2, 'M', 'ʝ'), + (0xA7B3, 'M', 'ꭓ'), + (0xA7B4, 'M', 'ꞵ'), + (0xA7B5, 'V'), + (0xA7B6, 'M', 'ꞷ'), + (0xA7B7, 'V'), + (0xA7B8, 'M', 'ꞹ'), + (0xA7B9, 'V'), + (0xA7BA, 'M', 'ꞻ'), + (0xA7BB, 'V'), + (0xA7BC, 'M', 'ꞽ'), + (0xA7BD, 'V'), + (0xA7BE, 'M', 'ꞿ'), + (0xA7BF, 'V'), + (0xA7C0, 'M', 'ꟁ'), + (0xA7C1, 'V'), + (0xA7C2, 'M', 'ꟃ'), + (0xA7C3, 'V'), + (0xA7C4, 'M', 'ꞔ'), + (0xA7C5, 'M', 'ʂ'), + (0xA7C6, 'M', 'ᶎ'), + (0xA7C7, 'M', 'ꟈ'), + (0xA7C8, 'V'), + (0xA7C9, 'M', 'ꟊ'), + (0xA7CA, 'V'), + (0xA7CB, 'X'), + (0xA7D0, 'M', 'ꟑ'), + (0xA7D1, 'V'), + (0xA7D2, 'X'), + (0xA7D3, 'V'), + (0xA7D4, 'X'), + (0xA7D5, 'V'), + (0xA7D6, 'M', 'ꟗ'), + (0xA7D7, 'V'), + (0xA7D8, 'M', 'ꟙ'), + (0xA7D9, 'V'), + (0xA7DA, 'X'), + (0xA7F2, 'M', 'c'), + (0xA7F3, 'M', 'f'), + (0xA7F4, 'M', 'q'), + (0xA7F5, 'M', 'ꟶ'), + (0xA7F6, 'V'), + (0xA7F8, 'M', 'ħ'), + (0xA7F9, 'M', 'œ'), + (0xA7FA, 'V'), + (0xA82D, 'X'), + (0xA830, 'V'), + (0xA83A, 'X'), + (0xA840, 'V'), + (0xA878, 'X'), + (0xA880, 'V'), + (0xA8C6, 'X'), + (0xA8CE, 'V'), + (0xA8DA, 'X'), + (0xA8E0, 'V'), + (0xA954, 'X'), + ] + +def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA95F, 'V'), + (0xA97D, 'X'), + (0xA980, 'V'), + (0xA9CE, 'X'), + (0xA9CF, 'V'), + (0xA9DA, 'X'), + (0xA9DE, 'V'), + (0xA9FF, 'X'), + (0xAA00, 'V'), + (0xAA37, 'X'), + (0xAA40, 'V'), + (0xAA4E, 'X'), + (0xAA50, 'V'), + (0xAA5A, 'X'), + (0xAA5C, 'V'), + (0xAAC3, 'X'), + (0xAADB, 'V'), + (0xAAF7, 'X'), + (0xAB01, 'V'), + (0xAB07, 'X'), + (0xAB09, 'V'), + (0xAB0F, 'X'), + (0xAB11, 'V'), + (0xAB17, 'X'), + (0xAB20, 'V'), + (0xAB27, 'X'), + (0xAB28, 'V'), + (0xAB2F, 'X'), + (0xAB30, 'V'), + (0xAB5C, 'M', 'ꜧ'), + (0xAB5D, 'M', 'ꬷ'), + (0xAB5E, 'M', 'ɫ'), + (0xAB5F, 'M', 'ꭒ'), + (0xAB60, 'V'), + (0xAB69, 'M', 'ʍ'), + (0xAB6A, 'V'), + (0xAB6C, 'X'), + (0xAB70, 'M', 'Ꭰ'), + (0xAB71, 'M', 'Ꭱ'), + (0xAB72, 'M', 'Ꭲ'), + (0xAB73, 'M', 'Ꭳ'), + (0xAB74, 'M', 'Ꭴ'), + (0xAB75, 'M', 'Ꭵ'), + (0xAB76, 'M', 'Ꭶ'), + (0xAB77, 'M', 'Ꭷ'), + (0xAB78, 'M', 'Ꭸ'), + (0xAB79, 'M', 'Ꭹ'), + (0xAB7A, 'M', 'Ꭺ'), + (0xAB7B, 'M', 'Ꭻ'), + (0xAB7C, 'M', 'Ꭼ'), + (0xAB7D, 'M', 'Ꭽ'), + (0xAB7E, 'M', 'Ꭾ'), + (0xAB7F, 'M', 'Ꭿ'), + (0xAB80, 'M', 'Ꮀ'), + (0xAB81, 'M', 'Ꮁ'), + (0xAB82, 'M', 'Ꮂ'), + (0xAB83, 'M', 'Ꮃ'), + (0xAB84, 'M', 'Ꮄ'), + (0xAB85, 'M', 'Ꮅ'), + (0xAB86, 'M', 'Ꮆ'), + (0xAB87, 'M', 'Ꮇ'), + (0xAB88, 'M', 'Ꮈ'), + (0xAB89, 'M', 'Ꮉ'), + (0xAB8A, 'M', 'Ꮊ'), + (0xAB8B, 'M', 'Ꮋ'), + (0xAB8C, 'M', 'Ꮌ'), + (0xAB8D, 'M', 'Ꮍ'), + (0xAB8E, 'M', 'Ꮎ'), + (0xAB8F, 'M', 'Ꮏ'), + (0xAB90, 'M', 'Ꮐ'), + (0xAB91, 'M', 'Ꮑ'), + (0xAB92, 'M', 'Ꮒ'), + (0xAB93, 'M', 'Ꮓ'), + (0xAB94, 'M', 'Ꮔ'), + (0xAB95, 'M', 'Ꮕ'), + (0xAB96, 'M', 'Ꮖ'), + (0xAB97, 'M', 'Ꮗ'), + (0xAB98, 'M', 'Ꮘ'), + (0xAB99, 'M', 'Ꮙ'), + (0xAB9A, 'M', 'Ꮚ'), + (0xAB9B, 'M', 'Ꮛ'), + (0xAB9C, 'M', 'Ꮜ'), + (0xAB9D, 'M', 'Ꮝ'), + (0xAB9E, 'M', 'Ꮞ'), + (0xAB9F, 'M', 'Ꮟ'), + (0xABA0, 'M', 'Ꮠ'), + (0xABA1, 'M', 'Ꮡ'), + (0xABA2, 'M', 'Ꮢ'), + (0xABA3, 'M', 'Ꮣ'), + (0xABA4, 'M', 'Ꮤ'), + (0xABA5, 'M', 'Ꮥ'), + (0xABA6, 'M', 'Ꮦ'), + (0xABA7, 'M', 'Ꮧ'), + (0xABA8, 'M', 'Ꮨ'), + (0xABA9, 'M', 'Ꮩ'), + (0xABAA, 'M', 'Ꮪ'), + (0xABAB, 'M', 'Ꮫ'), + (0xABAC, 'M', 'Ꮬ'), + (0xABAD, 'M', 'Ꮭ'), + (0xABAE, 'M', 'Ꮮ'), + ] + +def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xABAF, 'M', 'Ꮯ'), + (0xABB0, 'M', 'Ꮰ'), + (0xABB1, 'M', 'Ꮱ'), + (0xABB2, 'M', 'Ꮲ'), + (0xABB3, 'M', 'Ꮳ'), + (0xABB4, 'M', 'Ꮴ'), + (0xABB5, 'M', 'Ꮵ'), + (0xABB6, 'M', 'Ꮶ'), + (0xABB7, 'M', 'Ꮷ'), + (0xABB8, 'M', 'Ꮸ'), + (0xABB9, 'M', 'Ꮹ'), + (0xABBA, 'M', 'Ꮺ'), + (0xABBB, 'M', 'Ꮻ'), + (0xABBC, 'M', 'Ꮼ'), + (0xABBD, 'M', 'Ꮽ'), + (0xABBE, 'M', 'Ꮾ'), + (0xABBF, 'M', 'Ꮿ'), + (0xABC0, 'V'), + (0xABEE, 'X'), + (0xABF0, 'V'), + (0xABFA, 'X'), + (0xAC00, 'V'), + (0xD7A4, 'X'), + (0xD7B0, 'V'), + (0xD7C7, 'X'), + (0xD7CB, 'V'), + (0xD7FC, 'X'), + (0xF900, 'M', '豈'), + (0xF901, 'M', '更'), + (0xF902, 'M', '車'), + (0xF903, 'M', '賈'), + (0xF904, 'M', '滑'), + (0xF905, 'M', '串'), + (0xF906, 'M', '句'), + (0xF907, 'M', '龜'), + (0xF909, 'M', '契'), + (0xF90A, 'M', '金'), + (0xF90B, 'M', '喇'), + (0xF90C, 'M', '奈'), + (0xF90D, 'M', '懶'), + (0xF90E, 'M', '癩'), + (0xF90F, 'M', '羅'), + (0xF910, 'M', '蘿'), + (0xF911, 'M', '螺'), + (0xF912, 'M', '裸'), + (0xF913, 'M', '邏'), + (0xF914, 'M', '樂'), + (0xF915, 'M', '洛'), + (0xF916, 'M', '烙'), + (0xF917, 'M', '珞'), + (0xF918, 'M', '落'), + (0xF919, 'M', '酪'), + (0xF91A, 'M', '駱'), + (0xF91B, 'M', '亂'), + (0xF91C, 'M', '卵'), + (0xF91D, 'M', '欄'), + (0xF91E, 'M', '爛'), + (0xF91F, 'M', '蘭'), + (0xF920, 'M', '鸞'), + (0xF921, 'M', '嵐'), + (0xF922, 'M', '濫'), + (0xF923, 'M', '藍'), + (0xF924, 'M', '襤'), + (0xF925, 'M', '拉'), + (0xF926, 'M', '臘'), + (0xF927, 'M', '蠟'), + (0xF928, 'M', '廊'), + (0xF929, 'M', '朗'), + (0xF92A, 'M', '浪'), + (0xF92B, 'M', '狼'), + (0xF92C, 'M', '郎'), + (0xF92D, 'M', '來'), + (0xF92E, 'M', '冷'), + (0xF92F, 'M', '勞'), + (0xF930, 'M', '擄'), + (0xF931, 'M', '櫓'), + (0xF932, 'M', '爐'), + (0xF933, 'M', '盧'), + (0xF934, 'M', '老'), + (0xF935, 'M', '蘆'), + (0xF936, 'M', '虜'), + (0xF937, 'M', '路'), + (0xF938, 'M', '露'), + (0xF939, 'M', '魯'), + (0xF93A, 'M', '鷺'), + (0xF93B, 'M', '碌'), + (0xF93C, 'M', '祿'), + (0xF93D, 'M', '綠'), + (0xF93E, 'M', '菉'), + (0xF93F, 'M', '錄'), + (0xF940, 'M', '鹿'), + (0xF941, 'M', '論'), + (0xF942, 'M', '壟'), + (0xF943, 'M', '弄'), + (0xF944, 'M', '籠'), + (0xF945, 'M', '聾'), + (0xF946, 'M', '牢'), + (0xF947, 'M', '磊'), + (0xF948, 'M', '賂'), + (0xF949, 'M', '雷'), + ] + +def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF94A, 'M', '壘'), + (0xF94B, 'M', '屢'), + (0xF94C, 'M', '樓'), + (0xF94D, 'M', '淚'), + (0xF94E, 'M', '漏'), + (0xF94F, 'M', '累'), + (0xF950, 'M', '縷'), + (0xF951, 'M', '陋'), + (0xF952, 'M', '勒'), + (0xF953, 'M', '肋'), + (0xF954, 'M', '凜'), + (0xF955, 'M', '凌'), + (0xF956, 'M', '稜'), + (0xF957, 'M', '綾'), + (0xF958, 'M', '菱'), + (0xF959, 'M', '陵'), + (0xF95A, 'M', '讀'), + (0xF95B, 'M', '拏'), + (0xF95C, 'M', '樂'), + (0xF95D, 'M', '諾'), + (0xF95E, 'M', '丹'), + (0xF95F, 'M', '寧'), + (0xF960, 'M', '怒'), + (0xF961, 'M', '率'), + (0xF962, 'M', '異'), + (0xF963, 'M', '北'), + (0xF964, 'M', '磻'), + (0xF965, 'M', '便'), + (0xF966, 'M', '復'), + (0xF967, 'M', '不'), + (0xF968, 'M', '泌'), + (0xF969, 'M', '數'), + (0xF96A, 'M', '索'), + (0xF96B, 'M', '參'), + (0xF96C, 'M', '塞'), + (0xF96D, 'M', '省'), + (0xF96E, 'M', '葉'), + (0xF96F, 'M', '說'), + (0xF970, 'M', '殺'), + (0xF971, 'M', '辰'), + (0xF972, 'M', '沈'), + (0xF973, 'M', '拾'), + (0xF974, 'M', '若'), + (0xF975, 'M', '掠'), + (0xF976, 'M', '略'), + (0xF977, 'M', '亮'), + (0xF978, 'M', '兩'), + (0xF979, 'M', '凉'), + (0xF97A, 'M', '梁'), + (0xF97B, 'M', '糧'), + (0xF97C, 'M', '良'), + (0xF97D, 'M', '諒'), + (0xF97E, 'M', '量'), + (0xF97F, 'M', '勵'), + (0xF980, 'M', '呂'), + (0xF981, 'M', '女'), + (0xF982, 'M', '廬'), + (0xF983, 'M', '旅'), + (0xF984, 'M', '濾'), + (0xF985, 'M', '礪'), + (0xF986, 'M', '閭'), + (0xF987, 'M', '驪'), + (0xF988, 'M', '麗'), + (0xF989, 'M', '黎'), + (0xF98A, 'M', '力'), + (0xF98B, 'M', '曆'), + (0xF98C, 'M', '歷'), + (0xF98D, 'M', '轢'), + (0xF98E, 'M', '年'), + (0xF98F, 'M', '憐'), + (0xF990, 'M', '戀'), + (0xF991, 'M', '撚'), + (0xF992, 'M', '漣'), + (0xF993, 'M', '煉'), + (0xF994, 'M', '璉'), + (0xF995, 'M', '秊'), + (0xF996, 'M', '練'), + (0xF997, 'M', '聯'), + (0xF998, 'M', '輦'), + (0xF999, 'M', '蓮'), + (0xF99A, 'M', '連'), + (0xF99B, 'M', '鍊'), + (0xF99C, 'M', '列'), + (0xF99D, 'M', '劣'), + (0xF99E, 'M', '咽'), + (0xF99F, 'M', '烈'), + (0xF9A0, 'M', '裂'), + (0xF9A1, 'M', '說'), + (0xF9A2, 'M', '廉'), + (0xF9A3, 'M', '念'), + (0xF9A4, 'M', '捻'), + (0xF9A5, 'M', '殮'), + (0xF9A6, 'M', '簾'), + (0xF9A7, 'M', '獵'), + (0xF9A8, 'M', '令'), + (0xF9A9, 'M', '囹'), + (0xF9AA, 'M', '寧'), + (0xF9AB, 'M', '嶺'), + (0xF9AC, 'M', '怜'), + (0xF9AD, 'M', '玲'), + ] + +def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF9AE, 'M', '瑩'), + (0xF9AF, 'M', '羚'), + (0xF9B0, 'M', '聆'), + (0xF9B1, 'M', '鈴'), + (0xF9B2, 'M', '零'), + (0xF9B3, 'M', '靈'), + (0xF9B4, 'M', '領'), + (0xF9B5, 'M', '例'), + (0xF9B6, 'M', '禮'), + (0xF9B7, 'M', '醴'), + (0xF9B8, 'M', '隸'), + (0xF9B9, 'M', '惡'), + (0xF9BA, 'M', '了'), + (0xF9BB, 'M', '僚'), + (0xF9BC, 'M', '寮'), + (0xF9BD, 'M', '尿'), + (0xF9BE, 'M', '料'), + (0xF9BF, 'M', '樂'), + (0xF9C0, 'M', '燎'), + (0xF9C1, 'M', '療'), + (0xF9C2, 'M', '蓼'), + (0xF9C3, 'M', '遼'), + (0xF9C4, 'M', '龍'), + (0xF9C5, 'M', '暈'), + (0xF9C6, 'M', '阮'), + (0xF9C7, 'M', '劉'), + (0xF9C8, 'M', '杻'), + (0xF9C9, 'M', '柳'), + (0xF9CA, 'M', '流'), + (0xF9CB, 'M', '溜'), + (0xF9CC, 'M', '琉'), + (0xF9CD, 'M', '留'), + (0xF9CE, 'M', '硫'), + (0xF9CF, 'M', '紐'), + (0xF9D0, 'M', '類'), + (0xF9D1, 'M', '六'), + (0xF9D2, 'M', '戮'), + (0xF9D3, 'M', '陸'), + (0xF9D4, 'M', '倫'), + (0xF9D5, 'M', '崙'), + (0xF9D6, 'M', '淪'), + (0xF9D7, 'M', '輪'), + (0xF9D8, 'M', '律'), + (0xF9D9, 'M', '慄'), + (0xF9DA, 'M', '栗'), + (0xF9DB, 'M', '率'), + (0xF9DC, 'M', '隆'), + (0xF9DD, 'M', '利'), + (0xF9DE, 'M', '吏'), + (0xF9DF, 'M', '履'), + (0xF9E0, 'M', '易'), + (0xF9E1, 'M', '李'), + (0xF9E2, 'M', '梨'), + (0xF9E3, 'M', '泥'), + (0xF9E4, 'M', '理'), + (0xF9E5, 'M', '痢'), + (0xF9E6, 'M', '罹'), + (0xF9E7, 'M', '裏'), + (0xF9E8, 'M', '裡'), + (0xF9E9, 'M', '里'), + (0xF9EA, 'M', '離'), + (0xF9EB, 'M', '匿'), + (0xF9EC, 'M', '溺'), + (0xF9ED, 'M', '吝'), + (0xF9EE, 'M', '燐'), + (0xF9EF, 'M', '璘'), + (0xF9F0, 'M', '藺'), + (0xF9F1, 'M', '隣'), + (0xF9F2, 'M', '鱗'), + (0xF9F3, 'M', '麟'), + (0xF9F4, 'M', '林'), + (0xF9F5, 'M', '淋'), + (0xF9F6, 'M', '臨'), + (0xF9F7, 'M', '立'), + (0xF9F8, 'M', '笠'), + (0xF9F9, 'M', '粒'), + (0xF9FA, 'M', '狀'), + (0xF9FB, 'M', '炙'), + (0xF9FC, 'M', '識'), + (0xF9FD, 'M', '什'), + (0xF9FE, 'M', '茶'), + (0xF9FF, 'M', '刺'), + (0xFA00, 'M', '切'), + (0xFA01, 'M', '度'), + (0xFA02, 'M', '拓'), + (0xFA03, 'M', '糖'), + (0xFA04, 'M', '宅'), + (0xFA05, 'M', '洞'), + (0xFA06, 'M', '暴'), + (0xFA07, 'M', '輻'), + (0xFA08, 'M', '行'), + (0xFA09, 'M', '降'), + (0xFA0A, 'M', '見'), + (0xFA0B, 'M', '廓'), + (0xFA0C, 'M', '兀'), + (0xFA0D, 'M', '嗀'), + (0xFA0E, 'V'), + (0xFA10, 'M', '塚'), + (0xFA11, 'V'), + (0xFA12, 'M', '晴'), + ] + +def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA13, 'V'), + (0xFA15, 'M', '凞'), + (0xFA16, 'M', '猪'), + (0xFA17, 'M', '益'), + (0xFA18, 'M', '礼'), + (0xFA19, 'M', '神'), + (0xFA1A, 'M', '祥'), + (0xFA1B, 'M', '福'), + (0xFA1C, 'M', '靖'), + (0xFA1D, 'M', '精'), + (0xFA1E, 'M', '羽'), + (0xFA1F, 'V'), + (0xFA20, 'M', '蘒'), + (0xFA21, 'V'), + (0xFA22, 'M', '諸'), + (0xFA23, 'V'), + (0xFA25, 'M', '逸'), + (0xFA26, 'M', '都'), + (0xFA27, 'V'), + (0xFA2A, 'M', '飯'), + (0xFA2B, 'M', '飼'), + (0xFA2C, 'M', '館'), + (0xFA2D, 'M', '鶴'), + (0xFA2E, 'M', '郞'), + (0xFA2F, 'M', '隷'), + (0xFA30, 'M', '侮'), + (0xFA31, 'M', '僧'), + (0xFA32, 'M', '免'), + (0xFA33, 'M', '勉'), + (0xFA34, 'M', '勤'), + (0xFA35, 'M', '卑'), + (0xFA36, 'M', '喝'), + (0xFA37, 'M', '嘆'), + (0xFA38, 'M', '器'), + (0xFA39, 'M', '塀'), + (0xFA3A, 'M', '墨'), + (0xFA3B, 'M', '層'), + (0xFA3C, 'M', '屮'), + (0xFA3D, 'M', '悔'), + (0xFA3E, 'M', '慨'), + (0xFA3F, 'M', '憎'), + (0xFA40, 'M', '懲'), + (0xFA41, 'M', '敏'), + (0xFA42, 'M', '既'), + (0xFA43, 'M', '暑'), + (0xFA44, 'M', '梅'), + (0xFA45, 'M', '海'), + (0xFA46, 'M', '渚'), + (0xFA47, 'M', '漢'), + (0xFA48, 'M', '煮'), + (0xFA49, 'M', '爫'), + (0xFA4A, 'M', '琢'), + (0xFA4B, 'M', '碑'), + (0xFA4C, 'M', '社'), + (0xFA4D, 'M', '祉'), + (0xFA4E, 'M', '祈'), + (0xFA4F, 'M', '祐'), + (0xFA50, 'M', '祖'), + (0xFA51, 'M', '祝'), + (0xFA52, 'M', '禍'), + (0xFA53, 'M', '禎'), + (0xFA54, 'M', '穀'), + (0xFA55, 'M', '突'), + (0xFA56, 'M', '節'), + (0xFA57, 'M', '練'), + (0xFA58, 'M', '縉'), + (0xFA59, 'M', '繁'), + (0xFA5A, 'M', '署'), + (0xFA5B, 'M', '者'), + (0xFA5C, 'M', '臭'), + (0xFA5D, 'M', '艹'), + (0xFA5F, 'M', '著'), + (0xFA60, 'M', '褐'), + (0xFA61, 'M', '視'), + (0xFA62, 'M', '謁'), + (0xFA63, 'M', '謹'), + (0xFA64, 'M', '賓'), + (0xFA65, 'M', '贈'), + (0xFA66, 'M', '辶'), + (0xFA67, 'M', '逸'), + (0xFA68, 'M', '難'), + (0xFA69, 'M', '響'), + (0xFA6A, 'M', '頻'), + (0xFA6B, 'M', '恵'), + (0xFA6C, 'M', '𤋮'), + (0xFA6D, 'M', '舘'), + (0xFA6E, 'X'), + (0xFA70, 'M', '並'), + (0xFA71, 'M', '况'), + (0xFA72, 'M', '全'), + (0xFA73, 'M', '侀'), + (0xFA74, 'M', '充'), + (0xFA75, 'M', '冀'), + (0xFA76, 'M', '勇'), + (0xFA77, 'M', '勺'), + (0xFA78, 'M', '喝'), + (0xFA79, 'M', '啕'), + (0xFA7A, 'M', '喙'), + (0xFA7B, 'M', '嗢'), + (0xFA7C, 'M', '塚'), + ] + +def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA7D, 'M', '墳'), + (0xFA7E, 'M', '奄'), + (0xFA7F, 'M', '奔'), + (0xFA80, 'M', '婢'), + (0xFA81, 'M', '嬨'), + (0xFA82, 'M', '廒'), + (0xFA83, 'M', '廙'), + (0xFA84, 'M', '彩'), + (0xFA85, 'M', '徭'), + (0xFA86, 'M', '惘'), + (0xFA87, 'M', '慎'), + (0xFA88, 'M', '愈'), + (0xFA89, 'M', '憎'), + (0xFA8A, 'M', '慠'), + (0xFA8B, 'M', '懲'), + (0xFA8C, 'M', '戴'), + (0xFA8D, 'M', '揄'), + (0xFA8E, 'M', '搜'), + (0xFA8F, 'M', '摒'), + (0xFA90, 'M', '敖'), + (0xFA91, 'M', '晴'), + (0xFA92, 'M', '朗'), + (0xFA93, 'M', '望'), + (0xFA94, 'M', '杖'), + (0xFA95, 'M', '歹'), + (0xFA96, 'M', '殺'), + (0xFA97, 'M', '流'), + (0xFA98, 'M', '滛'), + (0xFA99, 'M', '滋'), + (0xFA9A, 'M', '漢'), + (0xFA9B, 'M', '瀞'), + (0xFA9C, 'M', '煮'), + (0xFA9D, 'M', '瞧'), + (0xFA9E, 'M', '爵'), + (0xFA9F, 'M', '犯'), + (0xFAA0, 'M', '猪'), + (0xFAA1, 'M', '瑱'), + (0xFAA2, 'M', '甆'), + (0xFAA3, 'M', '画'), + (0xFAA4, 'M', '瘝'), + (0xFAA5, 'M', '瘟'), + (0xFAA6, 'M', '益'), + (0xFAA7, 'M', '盛'), + (0xFAA8, 'M', '直'), + (0xFAA9, 'M', '睊'), + (0xFAAA, 'M', '着'), + (0xFAAB, 'M', '磌'), + (0xFAAC, 'M', '窱'), + (0xFAAD, 'M', '節'), + (0xFAAE, 'M', '类'), + (0xFAAF, 'M', '絛'), + (0xFAB0, 'M', '練'), + (0xFAB1, 'M', '缾'), + (0xFAB2, 'M', '者'), + (0xFAB3, 'M', '荒'), + (0xFAB4, 'M', '華'), + (0xFAB5, 'M', '蝹'), + (0xFAB6, 'M', '襁'), + (0xFAB7, 'M', '覆'), + (0xFAB8, 'M', '視'), + (0xFAB9, 'M', '調'), + (0xFABA, 'M', '諸'), + (0xFABB, 'M', '請'), + (0xFABC, 'M', '謁'), + (0xFABD, 'M', '諾'), + (0xFABE, 'M', '諭'), + (0xFABF, 'M', '謹'), + (0xFAC0, 'M', '變'), + (0xFAC1, 'M', '贈'), + (0xFAC2, 'M', '輸'), + (0xFAC3, 'M', '遲'), + (0xFAC4, 'M', '醙'), + (0xFAC5, 'M', '鉶'), + (0xFAC6, 'M', '陼'), + (0xFAC7, 'M', '難'), + (0xFAC8, 'M', '靖'), + (0xFAC9, 'M', '韛'), + (0xFACA, 'M', '響'), + (0xFACB, 'M', '頋'), + (0xFACC, 'M', '頻'), + (0xFACD, 'M', '鬒'), + (0xFACE, 'M', '龜'), + (0xFACF, 'M', '𢡊'), + (0xFAD0, 'M', '𢡄'), + (0xFAD1, 'M', '𣏕'), + (0xFAD2, 'M', '㮝'), + (0xFAD3, 'M', '䀘'), + (0xFAD4, 'M', '䀹'), + (0xFAD5, 'M', '𥉉'), + (0xFAD6, 'M', '𥳐'), + (0xFAD7, 'M', '𧻓'), + (0xFAD8, 'M', '齃'), + (0xFAD9, 'M', '龎'), + (0xFADA, 'X'), + (0xFB00, 'M', 'ff'), + (0xFB01, 'M', 'fi'), + (0xFB02, 'M', 'fl'), + (0xFB03, 'M', 'ffi'), + (0xFB04, 'M', 'ffl'), + (0xFB05, 'M', 'st'), + ] + +def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFB07, 'X'), + (0xFB13, 'M', 'մն'), + (0xFB14, 'M', 'մե'), + (0xFB15, 'M', 'մի'), + (0xFB16, 'M', 'վն'), + (0xFB17, 'M', 'մխ'), + (0xFB18, 'X'), + (0xFB1D, 'M', 'יִ'), + (0xFB1E, 'V'), + (0xFB1F, 'M', 'ײַ'), + (0xFB20, 'M', 'ע'), + (0xFB21, 'M', 'א'), + (0xFB22, 'M', 'ד'), + (0xFB23, 'M', 'ה'), + (0xFB24, 'M', 'כ'), + (0xFB25, 'M', 'ל'), + (0xFB26, 'M', 'ם'), + (0xFB27, 'M', 'ר'), + (0xFB28, 'M', 'ת'), + (0xFB29, '3', '+'), + (0xFB2A, 'M', 'שׁ'), + (0xFB2B, 'M', 'שׂ'), + (0xFB2C, 'M', 'שּׁ'), + (0xFB2D, 'M', 'שּׂ'), + (0xFB2E, 'M', 'אַ'), + (0xFB2F, 'M', 'אָ'), + (0xFB30, 'M', 'אּ'), + (0xFB31, 'M', 'בּ'), + (0xFB32, 'M', 'גּ'), + (0xFB33, 'M', 'דּ'), + (0xFB34, 'M', 'הּ'), + (0xFB35, 'M', 'וּ'), + (0xFB36, 'M', 'זּ'), + (0xFB37, 'X'), + (0xFB38, 'M', 'טּ'), + (0xFB39, 'M', 'יּ'), + (0xFB3A, 'M', 'ךּ'), + (0xFB3B, 'M', 'כּ'), + (0xFB3C, 'M', 'לּ'), + (0xFB3D, 'X'), + (0xFB3E, 'M', 'מּ'), + (0xFB3F, 'X'), + (0xFB40, 'M', 'נּ'), + (0xFB41, 'M', 'סּ'), + (0xFB42, 'X'), + (0xFB43, 'M', 'ףּ'), + (0xFB44, 'M', 'פּ'), + (0xFB45, 'X'), + (0xFB46, 'M', 'צּ'), + (0xFB47, 'M', 'קּ'), + (0xFB48, 'M', 'רּ'), + (0xFB49, 'M', 'שּ'), + (0xFB4A, 'M', 'תּ'), + (0xFB4B, 'M', 'וֹ'), + (0xFB4C, 'M', 'בֿ'), + (0xFB4D, 'M', 'כֿ'), + (0xFB4E, 'M', 'פֿ'), + (0xFB4F, 'M', 'אל'), + (0xFB50, 'M', 'ٱ'), + (0xFB52, 'M', 'ٻ'), + (0xFB56, 'M', 'پ'), + (0xFB5A, 'M', 'ڀ'), + (0xFB5E, 'M', 'ٺ'), + (0xFB62, 'M', 'ٿ'), + (0xFB66, 'M', 'ٹ'), + (0xFB6A, 'M', 'ڤ'), + (0xFB6E, 'M', 'ڦ'), + (0xFB72, 'M', 'ڄ'), + (0xFB76, 'M', 'ڃ'), + (0xFB7A, 'M', 'چ'), + (0xFB7E, 'M', 'ڇ'), + (0xFB82, 'M', 'ڍ'), + (0xFB84, 'M', 'ڌ'), + (0xFB86, 'M', 'ڎ'), + (0xFB88, 'M', 'ڈ'), + (0xFB8A, 'M', 'ژ'), + (0xFB8C, 'M', 'ڑ'), + (0xFB8E, 'M', 'ک'), + (0xFB92, 'M', 'گ'), + (0xFB96, 'M', 'ڳ'), + (0xFB9A, 'M', 'ڱ'), + (0xFB9E, 'M', 'ں'), + (0xFBA0, 'M', 'ڻ'), + (0xFBA4, 'M', 'ۀ'), + (0xFBA6, 'M', 'ہ'), + (0xFBAA, 'M', 'ھ'), + (0xFBAE, 'M', 'ے'), + (0xFBB0, 'M', 'ۓ'), + (0xFBB2, 'V'), + (0xFBC3, 'X'), + (0xFBD3, 'M', 'ڭ'), + (0xFBD7, 'M', 'ۇ'), + (0xFBD9, 'M', 'ۆ'), + (0xFBDB, 'M', 'ۈ'), + (0xFBDD, 'M', 'ۇٴ'), + (0xFBDE, 'M', 'ۋ'), + (0xFBE0, 'M', 'ۅ'), + (0xFBE2, 'M', 'ۉ'), + (0xFBE4, 'M', 'ې'), + (0xFBE8, 'M', 'ى'), + ] + +def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFBEA, 'M', 'ئا'), + (0xFBEC, 'M', 'ئە'), + (0xFBEE, 'M', 'ئو'), + (0xFBF0, 'M', 'ئۇ'), + (0xFBF2, 'M', 'ئۆ'), + (0xFBF4, 'M', 'ئۈ'), + (0xFBF6, 'M', 'ئې'), + (0xFBF9, 'M', 'ئى'), + (0xFBFC, 'M', 'ی'), + (0xFC00, 'M', 'ئج'), + (0xFC01, 'M', 'ئح'), + (0xFC02, 'M', 'ئم'), + (0xFC03, 'M', 'ئى'), + (0xFC04, 'M', 'ئي'), + (0xFC05, 'M', 'بج'), + (0xFC06, 'M', 'بح'), + (0xFC07, 'M', 'بخ'), + (0xFC08, 'M', 'بم'), + (0xFC09, 'M', 'بى'), + (0xFC0A, 'M', 'بي'), + (0xFC0B, 'M', 'تج'), + (0xFC0C, 'M', 'تح'), + (0xFC0D, 'M', 'تخ'), + (0xFC0E, 'M', 'تم'), + (0xFC0F, 'M', 'تى'), + (0xFC10, 'M', 'تي'), + (0xFC11, 'M', 'ثج'), + (0xFC12, 'M', 'ثم'), + (0xFC13, 'M', 'ثى'), + (0xFC14, 'M', 'ثي'), + (0xFC15, 'M', 'جح'), + (0xFC16, 'M', 'جم'), + (0xFC17, 'M', 'حج'), + (0xFC18, 'M', 'حم'), + (0xFC19, 'M', 'خج'), + (0xFC1A, 'M', 'خح'), + (0xFC1B, 'M', 'خم'), + (0xFC1C, 'M', 'سج'), + (0xFC1D, 'M', 'سح'), + (0xFC1E, 'M', 'سخ'), + (0xFC1F, 'M', 'سم'), + (0xFC20, 'M', 'صح'), + (0xFC21, 'M', 'صم'), + (0xFC22, 'M', 'ضج'), + (0xFC23, 'M', 'ضح'), + (0xFC24, 'M', 'ضخ'), + (0xFC25, 'M', 'ضم'), + (0xFC26, 'M', 'طح'), + (0xFC27, 'M', 'طم'), + (0xFC28, 'M', 'ظم'), + (0xFC29, 'M', 'عج'), + (0xFC2A, 'M', 'عم'), + (0xFC2B, 'M', 'غج'), + (0xFC2C, 'M', 'غم'), + (0xFC2D, 'M', 'فج'), + (0xFC2E, 'M', 'فح'), + (0xFC2F, 'M', 'فخ'), + (0xFC30, 'M', 'فم'), + (0xFC31, 'M', 'فى'), + (0xFC32, 'M', 'في'), + (0xFC33, 'M', 'قح'), + (0xFC34, 'M', 'قم'), + (0xFC35, 'M', 'قى'), + (0xFC36, 'M', 'قي'), + (0xFC37, 'M', 'كا'), + (0xFC38, 'M', 'كج'), + (0xFC39, 'M', 'كح'), + (0xFC3A, 'M', 'كخ'), + (0xFC3B, 'M', 'كل'), + (0xFC3C, 'M', 'كم'), + (0xFC3D, 'M', 'كى'), + (0xFC3E, 'M', 'كي'), + (0xFC3F, 'M', 'لج'), + (0xFC40, 'M', 'لح'), + (0xFC41, 'M', 'لخ'), + (0xFC42, 'M', 'لم'), + (0xFC43, 'M', 'لى'), + (0xFC44, 'M', 'لي'), + (0xFC45, 'M', 'مج'), + (0xFC46, 'M', 'مح'), + (0xFC47, 'M', 'مخ'), + (0xFC48, 'M', 'مم'), + (0xFC49, 'M', 'مى'), + (0xFC4A, 'M', 'مي'), + (0xFC4B, 'M', 'نج'), + (0xFC4C, 'M', 'نح'), + (0xFC4D, 'M', 'نخ'), + (0xFC4E, 'M', 'نم'), + (0xFC4F, 'M', 'نى'), + (0xFC50, 'M', 'ني'), + (0xFC51, 'M', 'هج'), + (0xFC52, 'M', 'هم'), + (0xFC53, 'M', 'هى'), + (0xFC54, 'M', 'هي'), + (0xFC55, 'M', 'يج'), + (0xFC56, 'M', 'يح'), + (0xFC57, 'M', 'يخ'), + (0xFC58, 'M', 'يم'), + (0xFC59, 'M', 'يى'), + (0xFC5A, 'M', 'يي'), + ] + +def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFC5B, 'M', 'ذٰ'), + (0xFC5C, 'M', 'رٰ'), + (0xFC5D, 'M', 'ىٰ'), + (0xFC5E, '3', ' ٌّ'), + (0xFC5F, '3', ' ٍّ'), + (0xFC60, '3', ' َّ'), + (0xFC61, '3', ' ُّ'), + (0xFC62, '3', ' ِّ'), + (0xFC63, '3', ' ّٰ'), + (0xFC64, 'M', 'ئر'), + (0xFC65, 'M', 'ئز'), + (0xFC66, 'M', 'ئم'), + (0xFC67, 'M', 'ئن'), + (0xFC68, 'M', 'ئى'), + (0xFC69, 'M', 'ئي'), + (0xFC6A, 'M', 'بر'), + (0xFC6B, 'M', 'بز'), + (0xFC6C, 'M', 'بم'), + (0xFC6D, 'M', 'بن'), + (0xFC6E, 'M', 'بى'), + (0xFC6F, 'M', 'بي'), + (0xFC70, 'M', 'تر'), + (0xFC71, 'M', 'تز'), + (0xFC72, 'M', 'تم'), + (0xFC73, 'M', 'تن'), + (0xFC74, 'M', 'تى'), + (0xFC75, 'M', 'تي'), + (0xFC76, 'M', 'ثر'), + (0xFC77, 'M', 'ثز'), + (0xFC78, 'M', 'ثم'), + (0xFC79, 'M', 'ثن'), + (0xFC7A, 'M', 'ثى'), + (0xFC7B, 'M', 'ثي'), + (0xFC7C, 'M', 'فى'), + (0xFC7D, 'M', 'في'), + (0xFC7E, 'M', 'قى'), + (0xFC7F, 'M', 'قي'), + (0xFC80, 'M', 'كا'), + (0xFC81, 'M', 'كل'), + (0xFC82, 'M', 'كم'), + (0xFC83, 'M', 'كى'), + (0xFC84, 'M', 'كي'), + (0xFC85, 'M', 'لم'), + (0xFC86, 'M', 'لى'), + (0xFC87, 'M', 'لي'), + (0xFC88, 'M', 'ما'), + (0xFC89, 'M', 'مم'), + (0xFC8A, 'M', 'نر'), + (0xFC8B, 'M', 'نز'), + (0xFC8C, 'M', 'نم'), + (0xFC8D, 'M', 'نن'), + (0xFC8E, 'M', 'نى'), + (0xFC8F, 'M', 'ني'), + (0xFC90, 'M', 'ىٰ'), + (0xFC91, 'M', 'ير'), + (0xFC92, 'M', 'يز'), + (0xFC93, 'M', 'يم'), + (0xFC94, 'M', 'ين'), + (0xFC95, 'M', 'يى'), + (0xFC96, 'M', 'يي'), + (0xFC97, 'M', 'ئج'), + (0xFC98, 'M', 'ئح'), + (0xFC99, 'M', 'ئخ'), + (0xFC9A, 'M', 'ئم'), + (0xFC9B, 'M', 'ئه'), + (0xFC9C, 'M', 'بج'), + (0xFC9D, 'M', 'بح'), + (0xFC9E, 'M', 'بخ'), + (0xFC9F, 'M', 'بم'), + (0xFCA0, 'M', 'به'), + (0xFCA1, 'M', 'تج'), + (0xFCA2, 'M', 'تح'), + (0xFCA3, 'M', 'تخ'), + (0xFCA4, 'M', 'تم'), + (0xFCA5, 'M', 'ته'), + (0xFCA6, 'M', 'ثم'), + (0xFCA7, 'M', 'جح'), + (0xFCA8, 'M', 'جم'), + (0xFCA9, 'M', 'حج'), + (0xFCAA, 'M', 'حم'), + (0xFCAB, 'M', 'خج'), + (0xFCAC, 'M', 'خم'), + (0xFCAD, 'M', 'سج'), + (0xFCAE, 'M', 'سح'), + (0xFCAF, 'M', 'سخ'), + (0xFCB0, 'M', 'سم'), + (0xFCB1, 'M', 'صح'), + (0xFCB2, 'M', 'صخ'), + (0xFCB3, 'M', 'صم'), + (0xFCB4, 'M', 'ضج'), + (0xFCB5, 'M', 'ضح'), + (0xFCB6, 'M', 'ضخ'), + (0xFCB7, 'M', 'ضم'), + (0xFCB8, 'M', 'طح'), + (0xFCB9, 'M', 'ظم'), + (0xFCBA, 'M', 'عج'), + (0xFCBB, 'M', 'عم'), + (0xFCBC, 'M', 'غج'), + (0xFCBD, 'M', 'غم'), + (0xFCBE, 'M', 'فج'), + ] + +def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFCBF, 'M', 'فح'), + (0xFCC0, 'M', 'فخ'), + (0xFCC1, 'M', 'فم'), + (0xFCC2, 'M', 'قح'), + (0xFCC3, 'M', 'قم'), + (0xFCC4, 'M', 'كج'), + (0xFCC5, 'M', 'كح'), + (0xFCC6, 'M', 'كخ'), + (0xFCC7, 'M', 'كل'), + (0xFCC8, 'M', 'كم'), + (0xFCC9, 'M', 'لج'), + (0xFCCA, 'M', 'لح'), + (0xFCCB, 'M', 'لخ'), + (0xFCCC, 'M', 'لم'), + (0xFCCD, 'M', 'له'), + (0xFCCE, 'M', 'مج'), + (0xFCCF, 'M', 'مح'), + (0xFCD0, 'M', 'مخ'), + (0xFCD1, 'M', 'مم'), + (0xFCD2, 'M', 'نج'), + (0xFCD3, 'M', 'نح'), + (0xFCD4, 'M', 'نخ'), + (0xFCD5, 'M', 'نم'), + (0xFCD6, 'M', 'نه'), + (0xFCD7, 'M', 'هج'), + (0xFCD8, 'M', 'هم'), + (0xFCD9, 'M', 'هٰ'), + (0xFCDA, 'M', 'يج'), + (0xFCDB, 'M', 'يح'), + (0xFCDC, 'M', 'يخ'), + (0xFCDD, 'M', 'يم'), + (0xFCDE, 'M', 'يه'), + (0xFCDF, 'M', 'ئم'), + (0xFCE0, 'M', 'ئه'), + (0xFCE1, 'M', 'بم'), + (0xFCE2, 'M', 'به'), + (0xFCE3, 'M', 'تم'), + (0xFCE4, 'M', 'ته'), + (0xFCE5, 'M', 'ثم'), + (0xFCE6, 'M', 'ثه'), + (0xFCE7, 'M', 'سم'), + (0xFCE8, 'M', 'سه'), + (0xFCE9, 'M', 'شم'), + (0xFCEA, 'M', 'شه'), + (0xFCEB, 'M', 'كل'), + (0xFCEC, 'M', 'كم'), + (0xFCED, 'M', 'لم'), + (0xFCEE, 'M', 'نم'), + (0xFCEF, 'M', 'نه'), + (0xFCF0, 'M', 'يم'), + (0xFCF1, 'M', 'يه'), + (0xFCF2, 'M', 'ـَّ'), + (0xFCF3, 'M', 'ـُّ'), + (0xFCF4, 'M', 'ـِّ'), + (0xFCF5, 'M', 'طى'), + (0xFCF6, 'M', 'طي'), + (0xFCF7, 'M', 'عى'), + (0xFCF8, 'M', 'عي'), + (0xFCF9, 'M', 'غى'), + (0xFCFA, 'M', 'غي'), + (0xFCFB, 'M', 'سى'), + (0xFCFC, 'M', 'سي'), + (0xFCFD, 'M', 'شى'), + (0xFCFE, 'M', 'شي'), + (0xFCFF, 'M', 'حى'), + (0xFD00, 'M', 'حي'), + (0xFD01, 'M', 'جى'), + (0xFD02, 'M', 'جي'), + (0xFD03, 'M', 'خى'), + (0xFD04, 'M', 'خي'), + (0xFD05, 'M', 'صى'), + (0xFD06, 'M', 'صي'), + (0xFD07, 'M', 'ضى'), + (0xFD08, 'M', 'ضي'), + (0xFD09, 'M', 'شج'), + (0xFD0A, 'M', 'شح'), + (0xFD0B, 'M', 'شخ'), + (0xFD0C, 'M', 'شم'), + (0xFD0D, 'M', 'شر'), + (0xFD0E, 'M', 'سر'), + (0xFD0F, 'M', 'صر'), + (0xFD10, 'M', 'ضر'), + (0xFD11, 'M', 'طى'), + (0xFD12, 'M', 'طي'), + (0xFD13, 'M', 'عى'), + (0xFD14, 'M', 'عي'), + (0xFD15, 'M', 'غى'), + (0xFD16, 'M', 'غي'), + (0xFD17, 'M', 'سى'), + (0xFD18, 'M', 'سي'), + (0xFD19, 'M', 'شى'), + (0xFD1A, 'M', 'شي'), + (0xFD1B, 'M', 'حى'), + (0xFD1C, 'M', 'حي'), + (0xFD1D, 'M', 'جى'), + (0xFD1E, 'M', 'جي'), + (0xFD1F, 'M', 'خى'), + (0xFD20, 'M', 'خي'), + (0xFD21, 'M', 'صى'), + (0xFD22, 'M', 'صي'), + ] + +def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFD23, 'M', 'ضى'), + (0xFD24, 'M', 'ضي'), + (0xFD25, 'M', 'شج'), + (0xFD26, 'M', 'شح'), + (0xFD27, 'M', 'شخ'), + (0xFD28, 'M', 'شم'), + (0xFD29, 'M', 'شر'), + (0xFD2A, 'M', 'سر'), + (0xFD2B, 'M', 'صر'), + (0xFD2C, 'M', 'ضر'), + (0xFD2D, 'M', 'شج'), + (0xFD2E, 'M', 'شح'), + (0xFD2F, 'M', 'شخ'), + (0xFD30, 'M', 'شم'), + (0xFD31, 'M', 'سه'), + (0xFD32, 'M', 'شه'), + (0xFD33, 'M', 'طم'), + (0xFD34, 'M', 'سج'), + (0xFD35, 'M', 'سح'), + (0xFD36, 'M', 'سخ'), + (0xFD37, 'M', 'شج'), + (0xFD38, 'M', 'شح'), + (0xFD39, 'M', 'شخ'), + (0xFD3A, 'M', 'طم'), + (0xFD3B, 'M', 'ظم'), + (0xFD3C, 'M', 'اً'), + (0xFD3E, 'V'), + (0xFD50, 'M', 'تجم'), + (0xFD51, 'M', 'تحج'), + (0xFD53, 'M', 'تحم'), + (0xFD54, 'M', 'تخم'), + (0xFD55, 'M', 'تمج'), + (0xFD56, 'M', 'تمح'), + (0xFD57, 'M', 'تمخ'), + (0xFD58, 'M', 'جمح'), + (0xFD5A, 'M', 'حمي'), + (0xFD5B, 'M', 'حمى'), + (0xFD5C, 'M', 'سحج'), + (0xFD5D, 'M', 'سجح'), + (0xFD5E, 'M', 'سجى'), + (0xFD5F, 'M', 'سمح'), + (0xFD61, 'M', 'سمج'), + (0xFD62, 'M', 'سمم'), + (0xFD64, 'M', 'صحح'), + (0xFD66, 'M', 'صمم'), + (0xFD67, 'M', 'شحم'), + (0xFD69, 'M', 'شجي'), + (0xFD6A, 'M', 'شمخ'), + (0xFD6C, 'M', 'شمم'), + (0xFD6E, 'M', 'ضحى'), + (0xFD6F, 'M', 'ضخم'), + (0xFD71, 'M', 'طمح'), + (0xFD73, 'M', 'طمم'), + (0xFD74, 'M', 'طمي'), + (0xFD75, 'M', 'عجم'), + (0xFD76, 'M', 'عمم'), + (0xFD78, 'M', 'عمى'), + (0xFD79, 'M', 'غمم'), + (0xFD7A, 'M', 'غمي'), + (0xFD7B, 'M', 'غمى'), + (0xFD7C, 'M', 'فخم'), + (0xFD7E, 'M', 'قمح'), + (0xFD7F, 'M', 'قمم'), + (0xFD80, 'M', 'لحم'), + (0xFD81, 'M', 'لحي'), + (0xFD82, 'M', 'لحى'), + (0xFD83, 'M', 'لجج'), + (0xFD85, 'M', 'لخم'), + (0xFD87, 'M', 'لمح'), + (0xFD89, 'M', 'محج'), + (0xFD8A, 'M', 'محم'), + (0xFD8B, 'M', 'محي'), + (0xFD8C, 'M', 'مجح'), + (0xFD8D, 'M', 'مجم'), + (0xFD8E, 'M', 'مخج'), + (0xFD8F, 'M', 'مخم'), + (0xFD90, 'X'), + (0xFD92, 'M', 'مجخ'), + (0xFD93, 'M', 'همج'), + (0xFD94, 'M', 'همم'), + (0xFD95, 'M', 'نحم'), + (0xFD96, 'M', 'نحى'), + (0xFD97, 'M', 'نجم'), + (0xFD99, 'M', 'نجى'), + (0xFD9A, 'M', 'نمي'), + (0xFD9B, 'M', 'نمى'), + (0xFD9C, 'M', 'يمم'), + (0xFD9E, 'M', 'بخي'), + (0xFD9F, 'M', 'تجي'), + (0xFDA0, 'M', 'تجى'), + (0xFDA1, 'M', 'تخي'), + (0xFDA2, 'M', 'تخى'), + (0xFDA3, 'M', 'تمي'), + (0xFDA4, 'M', 'تمى'), + (0xFDA5, 'M', 'جمي'), + (0xFDA6, 'M', 'جحى'), + (0xFDA7, 'M', 'جمى'), + (0xFDA8, 'M', 'سخى'), + (0xFDA9, 'M', 'صحي'), + (0xFDAA, 'M', 'شحي'), + ] + +def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFDAB, 'M', 'ضحي'), + (0xFDAC, 'M', 'لجي'), + (0xFDAD, 'M', 'لمي'), + (0xFDAE, 'M', 'يحي'), + (0xFDAF, 'M', 'يجي'), + (0xFDB0, 'M', 'يمي'), + (0xFDB1, 'M', 'ممي'), + (0xFDB2, 'M', 'قمي'), + (0xFDB3, 'M', 'نحي'), + (0xFDB4, 'M', 'قمح'), + (0xFDB5, 'M', 'لحم'), + (0xFDB6, 'M', 'عمي'), + (0xFDB7, 'M', 'كمي'), + (0xFDB8, 'M', 'نجح'), + (0xFDB9, 'M', 'مخي'), + (0xFDBA, 'M', 'لجم'), + (0xFDBB, 'M', 'كمم'), + (0xFDBC, 'M', 'لجم'), + (0xFDBD, 'M', 'نجح'), + (0xFDBE, 'M', 'جحي'), + (0xFDBF, 'M', 'حجي'), + (0xFDC0, 'M', 'مجي'), + (0xFDC1, 'M', 'فمي'), + (0xFDC2, 'M', 'بحي'), + (0xFDC3, 'M', 'كمم'), + (0xFDC4, 'M', 'عجم'), + (0xFDC5, 'M', 'صمم'), + (0xFDC6, 'M', 'سخي'), + (0xFDC7, 'M', 'نجي'), + (0xFDC8, 'X'), + (0xFDCF, 'V'), + (0xFDD0, 'X'), + (0xFDF0, 'M', 'صلے'), + (0xFDF1, 'M', 'قلے'), + (0xFDF2, 'M', 'الله'), + (0xFDF3, 'M', 'اكبر'), + (0xFDF4, 'M', 'محمد'), + (0xFDF5, 'M', 'صلعم'), + (0xFDF6, 'M', 'رسول'), + (0xFDF7, 'M', 'عليه'), + (0xFDF8, 'M', 'وسلم'), + (0xFDF9, 'M', 'صلى'), + (0xFDFA, '3', 'صلى الله عليه وسلم'), + (0xFDFB, '3', 'جل جلاله'), + (0xFDFC, 'M', 'ریال'), + (0xFDFD, 'V'), + (0xFE00, 'I'), + (0xFE10, '3', ','), + (0xFE11, 'M', '、'), + (0xFE12, 'X'), + (0xFE13, '3', ':'), + (0xFE14, '3', ';'), + (0xFE15, '3', '!'), + (0xFE16, '3', '?'), + (0xFE17, 'M', '〖'), + (0xFE18, 'M', '〗'), + (0xFE19, 'X'), + (0xFE20, 'V'), + (0xFE30, 'X'), + (0xFE31, 'M', '—'), + (0xFE32, 'M', '–'), + (0xFE33, '3', '_'), + (0xFE35, '3', '('), + (0xFE36, '3', ')'), + (0xFE37, '3', '{'), + (0xFE38, '3', '}'), + (0xFE39, 'M', '〔'), + (0xFE3A, 'M', '〕'), + (0xFE3B, 'M', '【'), + (0xFE3C, 'M', '】'), + (0xFE3D, 'M', '《'), + (0xFE3E, 'M', '》'), + (0xFE3F, 'M', '〈'), + (0xFE40, 'M', '〉'), + (0xFE41, 'M', '「'), + (0xFE42, 'M', '」'), + (0xFE43, 'M', '『'), + (0xFE44, 'M', '』'), + (0xFE45, 'V'), + (0xFE47, '3', '['), + (0xFE48, '3', ']'), + (0xFE49, '3', ' ̅'), + (0xFE4D, '3', '_'), + (0xFE50, '3', ','), + (0xFE51, 'M', '、'), + (0xFE52, 'X'), + (0xFE54, '3', ';'), + (0xFE55, '3', ':'), + (0xFE56, '3', '?'), + (0xFE57, '3', '!'), + (0xFE58, 'M', '—'), + (0xFE59, '3', '('), + (0xFE5A, '3', ')'), + (0xFE5B, '3', '{'), + (0xFE5C, '3', '}'), + (0xFE5D, 'M', '〔'), + (0xFE5E, 'M', '〕'), + (0xFE5F, '3', '#'), + (0xFE60, '3', '&'), + (0xFE61, '3', '*'), + ] + +def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFE62, '3', '+'), + (0xFE63, 'M', '-'), + (0xFE64, '3', '<'), + (0xFE65, '3', '>'), + (0xFE66, '3', '='), + (0xFE67, 'X'), + (0xFE68, '3', '\\'), + (0xFE69, '3', '$'), + (0xFE6A, '3', '%'), + (0xFE6B, '3', '@'), + (0xFE6C, 'X'), + (0xFE70, '3', ' ً'), + (0xFE71, 'M', 'ـً'), + (0xFE72, '3', ' ٌ'), + (0xFE73, 'V'), + (0xFE74, '3', ' ٍ'), + (0xFE75, 'X'), + (0xFE76, '3', ' َ'), + (0xFE77, 'M', 'ـَ'), + (0xFE78, '3', ' ُ'), + (0xFE79, 'M', 'ـُ'), + (0xFE7A, '3', ' ِ'), + (0xFE7B, 'M', 'ـِ'), + (0xFE7C, '3', ' ّ'), + (0xFE7D, 'M', 'ـّ'), + (0xFE7E, '3', ' ْ'), + (0xFE7F, 'M', 'ـْ'), + (0xFE80, 'M', 'ء'), + (0xFE81, 'M', 'آ'), + (0xFE83, 'M', 'أ'), + (0xFE85, 'M', 'ؤ'), + (0xFE87, 'M', 'إ'), + (0xFE89, 'M', 'ئ'), + (0xFE8D, 'M', 'ا'), + (0xFE8F, 'M', 'ب'), + (0xFE93, 'M', 'ة'), + (0xFE95, 'M', 'ت'), + (0xFE99, 'M', 'ث'), + (0xFE9D, 'M', 'ج'), + (0xFEA1, 'M', 'ح'), + (0xFEA5, 'M', 'خ'), + (0xFEA9, 'M', 'د'), + (0xFEAB, 'M', 'ذ'), + (0xFEAD, 'M', 'ر'), + (0xFEAF, 'M', 'ز'), + (0xFEB1, 'M', 'س'), + (0xFEB5, 'M', 'ش'), + (0xFEB9, 'M', 'ص'), + (0xFEBD, 'M', 'ض'), + (0xFEC1, 'M', 'ط'), + (0xFEC5, 'M', 'ظ'), + (0xFEC9, 'M', 'ع'), + (0xFECD, 'M', 'غ'), + (0xFED1, 'M', 'ف'), + (0xFED5, 'M', 'ق'), + (0xFED9, 'M', 'ك'), + (0xFEDD, 'M', 'ل'), + (0xFEE1, 'M', 'م'), + (0xFEE5, 'M', 'ن'), + (0xFEE9, 'M', 'ه'), + (0xFEED, 'M', 'و'), + (0xFEEF, 'M', 'ى'), + (0xFEF1, 'M', 'ي'), + (0xFEF5, 'M', 'لآ'), + (0xFEF7, 'M', 'لأ'), + (0xFEF9, 'M', 'لإ'), + (0xFEFB, 'M', 'لا'), + (0xFEFD, 'X'), + (0xFEFF, 'I'), + (0xFF00, 'X'), + (0xFF01, '3', '!'), + (0xFF02, '3', '"'), + (0xFF03, '3', '#'), + (0xFF04, '3', '$'), + (0xFF05, '3', '%'), + (0xFF06, '3', '&'), + (0xFF07, '3', '\''), + (0xFF08, '3', '('), + (0xFF09, '3', ')'), + (0xFF0A, '3', '*'), + (0xFF0B, '3', '+'), + (0xFF0C, '3', ','), + (0xFF0D, 'M', '-'), + (0xFF0E, 'M', '.'), + (0xFF0F, '3', '/'), + (0xFF10, 'M', '0'), + (0xFF11, 'M', '1'), + (0xFF12, 'M', '2'), + (0xFF13, 'M', '3'), + (0xFF14, 'M', '4'), + (0xFF15, 'M', '5'), + (0xFF16, 'M', '6'), + (0xFF17, 'M', '7'), + (0xFF18, 'M', '8'), + (0xFF19, 'M', '9'), + (0xFF1A, '3', ':'), + (0xFF1B, '3', ';'), + (0xFF1C, '3', '<'), + (0xFF1D, '3', '='), + (0xFF1E, '3', '>'), + ] + +def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF1F, '3', '?'), + (0xFF20, '3', '@'), + (0xFF21, 'M', 'a'), + (0xFF22, 'M', 'b'), + (0xFF23, 'M', 'c'), + (0xFF24, 'M', 'd'), + (0xFF25, 'M', 'e'), + (0xFF26, 'M', 'f'), + (0xFF27, 'M', 'g'), + (0xFF28, 'M', 'h'), + (0xFF29, 'M', 'i'), + (0xFF2A, 'M', 'j'), + (0xFF2B, 'M', 'k'), + (0xFF2C, 'M', 'l'), + (0xFF2D, 'M', 'm'), + (0xFF2E, 'M', 'n'), + (0xFF2F, 'M', 'o'), + (0xFF30, 'M', 'p'), + (0xFF31, 'M', 'q'), + (0xFF32, 'M', 'r'), + (0xFF33, 'M', 's'), + (0xFF34, 'M', 't'), + (0xFF35, 'M', 'u'), + (0xFF36, 'M', 'v'), + (0xFF37, 'M', 'w'), + (0xFF38, 'M', 'x'), + (0xFF39, 'M', 'y'), + (0xFF3A, 'M', 'z'), + (0xFF3B, '3', '['), + (0xFF3C, '3', '\\'), + (0xFF3D, '3', ']'), + (0xFF3E, '3', '^'), + (0xFF3F, '3', '_'), + (0xFF40, '3', '`'), + (0xFF41, 'M', 'a'), + (0xFF42, 'M', 'b'), + (0xFF43, 'M', 'c'), + (0xFF44, 'M', 'd'), + (0xFF45, 'M', 'e'), + (0xFF46, 'M', 'f'), + (0xFF47, 'M', 'g'), + (0xFF48, 'M', 'h'), + (0xFF49, 'M', 'i'), + (0xFF4A, 'M', 'j'), + (0xFF4B, 'M', 'k'), + (0xFF4C, 'M', 'l'), + (0xFF4D, 'M', 'm'), + (0xFF4E, 'M', 'n'), + (0xFF4F, 'M', 'o'), + (0xFF50, 'M', 'p'), + (0xFF51, 'M', 'q'), + (0xFF52, 'M', 'r'), + (0xFF53, 'M', 's'), + (0xFF54, 'M', 't'), + (0xFF55, 'M', 'u'), + (0xFF56, 'M', 'v'), + (0xFF57, 'M', 'w'), + (0xFF58, 'M', 'x'), + (0xFF59, 'M', 'y'), + (0xFF5A, 'M', 'z'), + (0xFF5B, '3', '{'), + (0xFF5C, '3', '|'), + (0xFF5D, '3', '}'), + (0xFF5E, '3', '~'), + (0xFF5F, 'M', '⦅'), + (0xFF60, 'M', '⦆'), + (0xFF61, 'M', '.'), + (0xFF62, 'M', '「'), + (0xFF63, 'M', '」'), + (0xFF64, 'M', '、'), + (0xFF65, 'M', '・'), + (0xFF66, 'M', 'ヲ'), + (0xFF67, 'M', 'ァ'), + (0xFF68, 'M', 'ィ'), + (0xFF69, 'M', 'ゥ'), + (0xFF6A, 'M', 'ェ'), + (0xFF6B, 'M', 'ォ'), + (0xFF6C, 'M', 'ャ'), + (0xFF6D, 'M', 'ュ'), + (0xFF6E, 'M', 'ョ'), + (0xFF6F, 'M', 'ッ'), + (0xFF70, 'M', 'ー'), + (0xFF71, 'M', 'ア'), + (0xFF72, 'M', 'イ'), + (0xFF73, 'M', 'ウ'), + (0xFF74, 'M', 'エ'), + (0xFF75, 'M', 'オ'), + (0xFF76, 'M', 'カ'), + (0xFF77, 'M', 'キ'), + (0xFF78, 'M', 'ク'), + (0xFF79, 'M', 'ケ'), + (0xFF7A, 'M', 'コ'), + (0xFF7B, 'M', 'サ'), + (0xFF7C, 'M', 'シ'), + (0xFF7D, 'M', 'ス'), + (0xFF7E, 'M', 'セ'), + (0xFF7F, 'M', 'ソ'), + (0xFF80, 'M', 'タ'), + (0xFF81, 'M', 'チ'), + (0xFF82, 'M', 'ツ'), + ] + +def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF83, 'M', 'テ'), + (0xFF84, 'M', 'ト'), + (0xFF85, 'M', 'ナ'), + (0xFF86, 'M', 'ニ'), + (0xFF87, 'M', 'ヌ'), + (0xFF88, 'M', 'ネ'), + (0xFF89, 'M', 'ノ'), + (0xFF8A, 'M', 'ハ'), + (0xFF8B, 'M', 'ヒ'), + (0xFF8C, 'M', 'フ'), + (0xFF8D, 'M', 'ヘ'), + (0xFF8E, 'M', 'ホ'), + (0xFF8F, 'M', 'マ'), + (0xFF90, 'M', 'ミ'), + (0xFF91, 'M', 'ム'), + (0xFF92, 'M', 'メ'), + (0xFF93, 'M', 'モ'), + (0xFF94, 'M', 'ヤ'), + (0xFF95, 'M', 'ユ'), + (0xFF96, 'M', 'ヨ'), + (0xFF97, 'M', 'ラ'), + (0xFF98, 'M', 'リ'), + (0xFF99, 'M', 'ル'), + (0xFF9A, 'M', 'レ'), + (0xFF9B, 'M', 'ロ'), + (0xFF9C, 'M', 'ワ'), + (0xFF9D, 'M', 'ン'), + (0xFF9E, 'M', '゙'), + (0xFF9F, 'M', '゚'), + (0xFFA0, 'X'), + (0xFFA1, 'M', 'ᄀ'), + (0xFFA2, 'M', 'ᄁ'), + (0xFFA3, 'M', 'ᆪ'), + (0xFFA4, 'M', 'ᄂ'), + (0xFFA5, 'M', 'ᆬ'), + (0xFFA6, 'M', 'ᆭ'), + (0xFFA7, 'M', 'ᄃ'), + (0xFFA8, 'M', 'ᄄ'), + (0xFFA9, 'M', 'ᄅ'), + (0xFFAA, 'M', 'ᆰ'), + (0xFFAB, 'M', 'ᆱ'), + (0xFFAC, 'M', 'ᆲ'), + (0xFFAD, 'M', 'ᆳ'), + (0xFFAE, 'M', 'ᆴ'), + (0xFFAF, 'M', 'ᆵ'), + (0xFFB0, 'M', 'ᄚ'), + (0xFFB1, 'M', 'ᄆ'), + (0xFFB2, 'M', 'ᄇ'), + (0xFFB3, 'M', 'ᄈ'), + (0xFFB4, 'M', 'ᄡ'), + (0xFFB5, 'M', 'ᄉ'), + (0xFFB6, 'M', 'ᄊ'), + (0xFFB7, 'M', 'ᄋ'), + (0xFFB8, 'M', 'ᄌ'), + (0xFFB9, 'M', 'ᄍ'), + (0xFFBA, 'M', 'ᄎ'), + (0xFFBB, 'M', 'ᄏ'), + (0xFFBC, 'M', 'ᄐ'), + (0xFFBD, 'M', 'ᄑ'), + (0xFFBE, 'M', 'ᄒ'), + (0xFFBF, 'X'), + (0xFFC2, 'M', 'ᅡ'), + (0xFFC3, 'M', 'ᅢ'), + (0xFFC4, 'M', 'ᅣ'), + (0xFFC5, 'M', 'ᅤ'), + (0xFFC6, 'M', 'ᅥ'), + (0xFFC7, 'M', 'ᅦ'), + (0xFFC8, 'X'), + (0xFFCA, 'M', 'ᅧ'), + (0xFFCB, 'M', 'ᅨ'), + (0xFFCC, 'M', 'ᅩ'), + (0xFFCD, 'M', 'ᅪ'), + (0xFFCE, 'M', 'ᅫ'), + (0xFFCF, 'M', 'ᅬ'), + (0xFFD0, 'X'), + (0xFFD2, 'M', 'ᅭ'), + (0xFFD3, 'M', 'ᅮ'), + (0xFFD4, 'M', 'ᅯ'), + (0xFFD5, 'M', 'ᅰ'), + (0xFFD6, 'M', 'ᅱ'), + (0xFFD7, 'M', 'ᅲ'), + (0xFFD8, 'X'), + (0xFFDA, 'M', 'ᅳ'), + (0xFFDB, 'M', 'ᅴ'), + (0xFFDC, 'M', 'ᅵ'), + (0xFFDD, 'X'), + (0xFFE0, 'M', '¢'), + (0xFFE1, 'M', '£'), + (0xFFE2, 'M', '¬'), + (0xFFE3, '3', ' ̄'), + (0xFFE4, 'M', '¦'), + (0xFFE5, 'M', '¥'), + (0xFFE6, 'M', '₩'), + (0xFFE7, 'X'), + (0xFFE8, 'M', '│'), + (0xFFE9, 'M', '←'), + (0xFFEA, 'M', '↑'), + (0xFFEB, 'M', '→'), + (0xFFEC, 'M', '↓'), + (0xFFED, 'M', '■'), + ] + +def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFFEE, 'M', '○'), + (0xFFEF, 'X'), + (0x10000, 'V'), + (0x1000C, 'X'), + (0x1000D, 'V'), + (0x10027, 'X'), + (0x10028, 'V'), + (0x1003B, 'X'), + (0x1003C, 'V'), + (0x1003E, 'X'), + (0x1003F, 'V'), + (0x1004E, 'X'), + (0x10050, 'V'), + (0x1005E, 'X'), + (0x10080, 'V'), + (0x100FB, 'X'), + (0x10100, 'V'), + (0x10103, 'X'), + (0x10107, 'V'), + (0x10134, 'X'), + (0x10137, 'V'), + (0x1018F, 'X'), + (0x10190, 'V'), + (0x1019D, 'X'), + (0x101A0, 'V'), + (0x101A1, 'X'), + (0x101D0, 'V'), + (0x101FE, 'X'), + (0x10280, 'V'), + (0x1029D, 'X'), + (0x102A0, 'V'), + (0x102D1, 'X'), + (0x102E0, 'V'), + (0x102FC, 'X'), + (0x10300, 'V'), + (0x10324, 'X'), + (0x1032D, 'V'), + (0x1034B, 'X'), + (0x10350, 'V'), + (0x1037B, 'X'), + (0x10380, 'V'), + (0x1039E, 'X'), + (0x1039F, 'V'), + (0x103C4, 'X'), + (0x103C8, 'V'), + (0x103D6, 'X'), + (0x10400, 'M', '𐐨'), + (0x10401, 'M', '𐐩'), + (0x10402, 'M', '𐐪'), + (0x10403, 'M', '𐐫'), + (0x10404, 'M', '𐐬'), + (0x10405, 'M', '𐐭'), + (0x10406, 'M', '𐐮'), + (0x10407, 'M', '𐐯'), + (0x10408, 'M', '𐐰'), + (0x10409, 'M', '𐐱'), + (0x1040A, 'M', '𐐲'), + (0x1040B, 'M', '𐐳'), + (0x1040C, 'M', '𐐴'), + (0x1040D, 'M', '𐐵'), + (0x1040E, 'M', '𐐶'), + (0x1040F, 'M', '𐐷'), + (0x10410, 'M', '𐐸'), + (0x10411, 'M', '𐐹'), + (0x10412, 'M', '𐐺'), + (0x10413, 'M', '𐐻'), + (0x10414, 'M', '𐐼'), + (0x10415, 'M', '𐐽'), + (0x10416, 'M', '𐐾'), + (0x10417, 'M', '𐐿'), + (0x10418, 'M', '𐑀'), + (0x10419, 'M', '𐑁'), + (0x1041A, 'M', '𐑂'), + (0x1041B, 'M', '𐑃'), + (0x1041C, 'M', '𐑄'), + (0x1041D, 'M', '𐑅'), + (0x1041E, 'M', '𐑆'), + (0x1041F, 'M', '𐑇'), + (0x10420, 'M', '𐑈'), + (0x10421, 'M', '𐑉'), + (0x10422, 'M', '𐑊'), + (0x10423, 'M', '𐑋'), + (0x10424, 'M', '𐑌'), + (0x10425, 'M', '𐑍'), + (0x10426, 'M', '𐑎'), + (0x10427, 'M', '𐑏'), + (0x10428, 'V'), + (0x1049E, 'X'), + (0x104A0, 'V'), + (0x104AA, 'X'), + (0x104B0, 'M', '𐓘'), + (0x104B1, 'M', '𐓙'), + (0x104B2, 'M', '𐓚'), + (0x104B3, 'M', '𐓛'), + (0x104B4, 'M', '𐓜'), + (0x104B5, 'M', '𐓝'), + (0x104B6, 'M', '𐓞'), + (0x104B7, 'M', '𐓟'), + (0x104B8, 'M', '𐓠'), + (0x104B9, 'M', '𐓡'), + ] + +def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x104BA, 'M', '𐓢'), + (0x104BB, 'M', '𐓣'), + (0x104BC, 'M', '𐓤'), + (0x104BD, 'M', '𐓥'), + (0x104BE, 'M', '𐓦'), + (0x104BF, 'M', '𐓧'), + (0x104C0, 'M', '𐓨'), + (0x104C1, 'M', '𐓩'), + (0x104C2, 'M', '𐓪'), + (0x104C3, 'M', '𐓫'), + (0x104C4, 'M', '𐓬'), + (0x104C5, 'M', '𐓭'), + (0x104C6, 'M', '𐓮'), + (0x104C7, 'M', '𐓯'), + (0x104C8, 'M', '𐓰'), + (0x104C9, 'M', '𐓱'), + (0x104CA, 'M', '𐓲'), + (0x104CB, 'M', '𐓳'), + (0x104CC, 'M', '𐓴'), + (0x104CD, 'M', '𐓵'), + (0x104CE, 'M', '𐓶'), + (0x104CF, 'M', '𐓷'), + (0x104D0, 'M', '𐓸'), + (0x104D1, 'M', '𐓹'), + (0x104D2, 'M', '𐓺'), + (0x104D3, 'M', '𐓻'), + (0x104D4, 'X'), + (0x104D8, 'V'), + (0x104FC, 'X'), + (0x10500, 'V'), + (0x10528, 'X'), + (0x10530, 'V'), + (0x10564, 'X'), + (0x1056F, 'V'), + (0x10570, 'M', '𐖗'), + (0x10571, 'M', '𐖘'), + (0x10572, 'M', '𐖙'), + (0x10573, 'M', '𐖚'), + (0x10574, 'M', '𐖛'), + (0x10575, 'M', '𐖜'), + (0x10576, 'M', '𐖝'), + (0x10577, 'M', '𐖞'), + (0x10578, 'M', '𐖟'), + (0x10579, 'M', '𐖠'), + (0x1057A, 'M', '𐖡'), + (0x1057B, 'X'), + (0x1057C, 'M', '𐖣'), + (0x1057D, 'M', '𐖤'), + (0x1057E, 'M', '𐖥'), + (0x1057F, 'M', '𐖦'), + (0x10580, 'M', '𐖧'), + (0x10581, 'M', '𐖨'), + (0x10582, 'M', '𐖩'), + (0x10583, 'M', '𐖪'), + (0x10584, 'M', '𐖫'), + (0x10585, 'M', '𐖬'), + (0x10586, 'M', '𐖭'), + (0x10587, 'M', '𐖮'), + (0x10588, 'M', '𐖯'), + (0x10589, 'M', '𐖰'), + (0x1058A, 'M', '𐖱'), + (0x1058B, 'X'), + (0x1058C, 'M', '𐖳'), + (0x1058D, 'M', '𐖴'), + (0x1058E, 'M', '𐖵'), + (0x1058F, 'M', '𐖶'), + (0x10590, 'M', '𐖷'), + (0x10591, 'M', '𐖸'), + (0x10592, 'M', '𐖹'), + (0x10593, 'X'), + (0x10594, 'M', '𐖻'), + (0x10595, 'M', '𐖼'), + (0x10596, 'X'), + (0x10597, 'V'), + (0x105A2, 'X'), + (0x105A3, 'V'), + (0x105B2, 'X'), + (0x105B3, 'V'), + (0x105BA, 'X'), + (0x105BB, 'V'), + (0x105BD, 'X'), + (0x10600, 'V'), + (0x10737, 'X'), + (0x10740, 'V'), + (0x10756, 'X'), + (0x10760, 'V'), + (0x10768, 'X'), + (0x10780, 'V'), + (0x10781, 'M', 'ː'), + (0x10782, 'M', 'ˑ'), + (0x10783, 'M', 'æ'), + (0x10784, 'M', 'ʙ'), + (0x10785, 'M', 'ɓ'), + (0x10786, 'X'), + (0x10787, 'M', 'ʣ'), + (0x10788, 'M', 'ꭦ'), + (0x10789, 'M', 'ʥ'), + (0x1078A, 'M', 'ʤ'), + (0x1078B, 'M', 'ɖ'), + (0x1078C, 'M', 'ɗ'), + ] + +def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1078D, 'M', 'ᶑ'), + (0x1078E, 'M', 'ɘ'), + (0x1078F, 'M', 'ɞ'), + (0x10790, 'M', 'ʩ'), + (0x10791, 'M', 'ɤ'), + (0x10792, 'M', 'ɢ'), + (0x10793, 'M', 'ɠ'), + (0x10794, 'M', 'ʛ'), + (0x10795, 'M', 'ħ'), + (0x10796, 'M', 'ʜ'), + (0x10797, 'M', 'ɧ'), + (0x10798, 'M', 'ʄ'), + (0x10799, 'M', 'ʪ'), + (0x1079A, 'M', 'ʫ'), + (0x1079B, 'M', 'ɬ'), + (0x1079C, 'M', '𝼄'), + (0x1079D, 'M', 'ꞎ'), + (0x1079E, 'M', 'ɮ'), + (0x1079F, 'M', '𝼅'), + (0x107A0, 'M', 'ʎ'), + (0x107A1, 'M', '𝼆'), + (0x107A2, 'M', 'ø'), + (0x107A3, 'M', 'ɶ'), + (0x107A4, 'M', 'ɷ'), + (0x107A5, 'M', 'q'), + (0x107A6, 'M', 'ɺ'), + (0x107A7, 'M', '𝼈'), + (0x107A8, 'M', 'ɽ'), + (0x107A9, 'M', 'ɾ'), + (0x107AA, 'M', 'ʀ'), + (0x107AB, 'M', 'ʨ'), + (0x107AC, 'M', 'ʦ'), + (0x107AD, 'M', 'ꭧ'), + (0x107AE, 'M', 'ʧ'), + (0x107AF, 'M', 'ʈ'), + (0x107B0, 'M', 'ⱱ'), + (0x107B1, 'X'), + (0x107B2, 'M', 'ʏ'), + (0x107B3, 'M', 'ʡ'), + (0x107B4, 'M', 'ʢ'), + (0x107B5, 'M', 'ʘ'), + (0x107B6, 'M', 'ǀ'), + (0x107B7, 'M', 'ǁ'), + (0x107B8, 'M', 'ǂ'), + (0x107B9, 'M', '𝼊'), + (0x107BA, 'M', '𝼞'), + (0x107BB, 'X'), + (0x10800, 'V'), + (0x10806, 'X'), + (0x10808, 'V'), + (0x10809, 'X'), + (0x1080A, 'V'), + (0x10836, 'X'), + (0x10837, 'V'), + (0x10839, 'X'), + (0x1083C, 'V'), + (0x1083D, 'X'), + (0x1083F, 'V'), + (0x10856, 'X'), + (0x10857, 'V'), + (0x1089F, 'X'), + (0x108A7, 'V'), + (0x108B0, 'X'), + (0x108E0, 'V'), + (0x108F3, 'X'), + (0x108F4, 'V'), + (0x108F6, 'X'), + (0x108FB, 'V'), + (0x1091C, 'X'), + (0x1091F, 'V'), + (0x1093A, 'X'), + (0x1093F, 'V'), + (0x10940, 'X'), + (0x10980, 'V'), + (0x109B8, 'X'), + (0x109BC, 'V'), + (0x109D0, 'X'), + (0x109D2, 'V'), + (0x10A04, 'X'), + (0x10A05, 'V'), + (0x10A07, 'X'), + (0x10A0C, 'V'), + (0x10A14, 'X'), + (0x10A15, 'V'), + (0x10A18, 'X'), + (0x10A19, 'V'), + (0x10A36, 'X'), + (0x10A38, 'V'), + (0x10A3B, 'X'), + (0x10A3F, 'V'), + (0x10A49, 'X'), + (0x10A50, 'V'), + (0x10A59, 'X'), + (0x10A60, 'V'), + (0x10AA0, 'X'), + (0x10AC0, 'V'), + (0x10AE7, 'X'), + (0x10AEB, 'V'), + (0x10AF7, 'X'), + (0x10B00, 'V'), + ] + +def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10B36, 'X'), + (0x10B39, 'V'), + (0x10B56, 'X'), + (0x10B58, 'V'), + (0x10B73, 'X'), + (0x10B78, 'V'), + (0x10B92, 'X'), + (0x10B99, 'V'), + (0x10B9D, 'X'), + (0x10BA9, 'V'), + (0x10BB0, 'X'), + (0x10C00, 'V'), + (0x10C49, 'X'), + (0x10C80, 'M', '𐳀'), + (0x10C81, 'M', '𐳁'), + (0x10C82, 'M', '𐳂'), + (0x10C83, 'M', '𐳃'), + (0x10C84, 'M', '𐳄'), + (0x10C85, 'M', '𐳅'), + (0x10C86, 'M', '𐳆'), + (0x10C87, 'M', '𐳇'), + (0x10C88, 'M', '𐳈'), + (0x10C89, 'M', '𐳉'), + (0x10C8A, 'M', '𐳊'), + (0x10C8B, 'M', '𐳋'), + (0x10C8C, 'M', '𐳌'), + (0x10C8D, 'M', '𐳍'), + (0x10C8E, 'M', '𐳎'), + (0x10C8F, 'M', '𐳏'), + (0x10C90, 'M', '𐳐'), + (0x10C91, 'M', '𐳑'), + (0x10C92, 'M', '𐳒'), + (0x10C93, 'M', '𐳓'), + (0x10C94, 'M', '𐳔'), + (0x10C95, 'M', '𐳕'), + (0x10C96, 'M', '𐳖'), + (0x10C97, 'M', '𐳗'), + (0x10C98, 'M', '𐳘'), + (0x10C99, 'M', '𐳙'), + (0x10C9A, 'M', '𐳚'), + (0x10C9B, 'M', '𐳛'), + (0x10C9C, 'M', '𐳜'), + (0x10C9D, 'M', '𐳝'), + (0x10C9E, 'M', '𐳞'), + (0x10C9F, 'M', '𐳟'), + (0x10CA0, 'M', '𐳠'), + (0x10CA1, 'M', '𐳡'), + (0x10CA2, 'M', '𐳢'), + (0x10CA3, 'M', '𐳣'), + (0x10CA4, 'M', '𐳤'), + (0x10CA5, 'M', '𐳥'), + (0x10CA6, 'M', '𐳦'), + (0x10CA7, 'M', '𐳧'), + (0x10CA8, 'M', '𐳨'), + (0x10CA9, 'M', '𐳩'), + (0x10CAA, 'M', '𐳪'), + (0x10CAB, 'M', '𐳫'), + (0x10CAC, 'M', '𐳬'), + (0x10CAD, 'M', '𐳭'), + (0x10CAE, 'M', '𐳮'), + (0x10CAF, 'M', '𐳯'), + (0x10CB0, 'M', '𐳰'), + (0x10CB1, 'M', '𐳱'), + (0x10CB2, 'M', '𐳲'), + (0x10CB3, 'X'), + (0x10CC0, 'V'), + (0x10CF3, 'X'), + (0x10CFA, 'V'), + (0x10D28, 'X'), + (0x10D30, 'V'), + (0x10D3A, 'X'), + (0x10E60, 'V'), + (0x10E7F, 'X'), + (0x10E80, 'V'), + (0x10EAA, 'X'), + (0x10EAB, 'V'), + (0x10EAE, 'X'), + (0x10EB0, 'V'), + (0x10EB2, 'X'), + (0x10EFD, 'V'), + (0x10F28, 'X'), + (0x10F30, 'V'), + (0x10F5A, 'X'), + (0x10F70, 'V'), + (0x10F8A, 'X'), + (0x10FB0, 'V'), + (0x10FCC, 'X'), + (0x10FE0, 'V'), + (0x10FF7, 'X'), + (0x11000, 'V'), + (0x1104E, 'X'), + (0x11052, 'V'), + (0x11076, 'X'), + (0x1107F, 'V'), + (0x110BD, 'X'), + (0x110BE, 'V'), + (0x110C3, 'X'), + (0x110D0, 'V'), + (0x110E9, 'X'), + (0x110F0, 'V'), + ] + +def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x110FA, 'X'), + (0x11100, 'V'), + (0x11135, 'X'), + (0x11136, 'V'), + (0x11148, 'X'), + (0x11150, 'V'), + (0x11177, 'X'), + (0x11180, 'V'), + (0x111E0, 'X'), + (0x111E1, 'V'), + (0x111F5, 'X'), + (0x11200, 'V'), + (0x11212, 'X'), + (0x11213, 'V'), + (0x11242, 'X'), + (0x11280, 'V'), + (0x11287, 'X'), + (0x11288, 'V'), + (0x11289, 'X'), + (0x1128A, 'V'), + (0x1128E, 'X'), + (0x1128F, 'V'), + (0x1129E, 'X'), + (0x1129F, 'V'), + (0x112AA, 'X'), + (0x112B0, 'V'), + (0x112EB, 'X'), + (0x112F0, 'V'), + (0x112FA, 'X'), + (0x11300, 'V'), + (0x11304, 'X'), + (0x11305, 'V'), + (0x1130D, 'X'), + (0x1130F, 'V'), + (0x11311, 'X'), + (0x11313, 'V'), + (0x11329, 'X'), + (0x1132A, 'V'), + (0x11331, 'X'), + (0x11332, 'V'), + (0x11334, 'X'), + (0x11335, 'V'), + (0x1133A, 'X'), + (0x1133B, 'V'), + (0x11345, 'X'), + (0x11347, 'V'), + (0x11349, 'X'), + (0x1134B, 'V'), + (0x1134E, 'X'), + (0x11350, 'V'), + (0x11351, 'X'), + (0x11357, 'V'), + (0x11358, 'X'), + (0x1135D, 'V'), + (0x11364, 'X'), + (0x11366, 'V'), + (0x1136D, 'X'), + (0x11370, 'V'), + (0x11375, 'X'), + (0x11400, 'V'), + (0x1145C, 'X'), + (0x1145D, 'V'), + (0x11462, 'X'), + (0x11480, 'V'), + (0x114C8, 'X'), + (0x114D0, 'V'), + (0x114DA, 'X'), + (0x11580, 'V'), + (0x115B6, 'X'), + (0x115B8, 'V'), + (0x115DE, 'X'), + (0x11600, 'V'), + (0x11645, 'X'), + (0x11650, 'V'), + (0x1165A, 'X'), + (0x11660, 'V'), + (0x1166D, 'X'), + (0x11680, 'V'), + (0x116BA, 'X'), + (0x116C0, 'V'), + (0x116CA, 'X'), + (0x11700, 'V'), + (0x1171B, 'X'), + (0x1171D, 'V'), + (0x1172C, 'X'), + (0x11730, 'V'), + (0x11747, 'X'), + (0x11800, 'V'), + (0x1183C, 'X'), + (0x118A0, 'M', '𑣀'), + (0x118A1, 'M', '𑣁'), + (0x118A2, 'M', '𑣂'), + (0x118A3, 'M', '𑣃'), + (0x118A4, 'M', '𑣄'), + (0x118A5, 'M', '𑣅'), + (0x118A6, 'M', '𑣆'), + (0x118A7, 'M', '𑣇'), + (0x118A8, 'M', '𑣈'), + (0x118A9, 'M', '𑣉'), + (0x118AA, 'M', '𑣊'), + ] + +def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x118AB, 'M', '𑣋'), + (0x118AC, 'M', '𑣌'), + (0x118AD, 'M', '𑣍'), + (0x118AE, 'M', '𑣎'), + (0x118AF, 'M', '𑣏'), + (0x118B0, 'M', '𑣐'), + (0x118B1, 'M', '𑣑'), + (0x118B2, 'M', '𑣒'), + (0x118B3, 'M', '𑣓'), + (0x118B4, 'M', '𑣔'), + (0x118B5, 'M', '𑣕'), + (0x118B6, 'M', '𑣖'), + (0x118B7, 'M', '𑣗'), + (0x118B8, 'M', '𑣘'), + (0x118B9, 'M', '𑣙'), + (0x118BA, 'M', '𑣚'), + (0x118BB, 'M', '𑣛'), + (0x118BC, 'M', '𑣜'), + (0x118BD, 'M', '𑣝'), + (0x118BE, 'M', '𑣞'), + (0x118BF, 'M', '𑣟'), + (0x118C0, 'V'), + (0x118F3, 'X'), + (0x118FF, 'V'), + (0x11907, 'X'), + (0x11909, 'V'), + (0x1190A, 'X'), + (0x1190C, 'V'), + (0x11914, 'X'), + (0x11915, 'V'), + (0x11917, 'X'), + (0x11918, 'V'), + (0x11936, 'X'), + (0x11937, 'V'), + (0x11939, 'X'), + (0x1193B, 'V'), + (0x11947, 'X'), + (0x11950, 'V'), + (0x1195A, 'X'), + (0x119A0, 'V'), + (0x119A8, 'X'), + (0x119AA, 'V'), + (0x119D8, 'X'), + (0x119DA, 'V'), + (0x119E5, 'X'), + (0x11A00, 'V'), + (0x11A48, 'X'), + (0x11A50, 'V'), + (0x11AA3, 'X'), + (0x11AB0, 'V'), + (0x11AF9, 'X'), + (0x11B00, 'V'), + (0x11B0A, 'X'), + (0x11C00, 'V'), + (0x11C09, 'X'), + (0x11C0A, 'V'), + (0x11C37, 'X'), + (0x11C38, 'V'), + (0x11C46, 'X'), + (0x11C50, 'V'), + (0x11C6D, 'X'), + (0x11C70, 'V'), + (0x11C90, 'X'), + (0x11C92, 'V'), + (0x11CA8, 'X'), + (0x11CA9, 'V'), + (0x11CB7, 'X'), + (0x11D00, 'V'), + (0x11D07, 'X'), + (0x11D08, 'V'), + (0x11D0A, 'X'), + (0x11D0B, 'V'), + (0x11D37, 'X'), + (0x11D3A, 'V'), + (0x11D3B, 'X'), + (0x11D3C, 'V'), + (0x11D3E, 'X'), + (0x11D3F, 'V'), + (0x11D48, 'X'), + (0x11D50, 'V'), + (0x11D5A, 'X'), + (0x11D60, 'V'), + (0x11D66, 'X'), + (0x11D67, 'V'), + (0x11D69, 'X'), + (0x11D6A, 'V'), + (0x11D8F, 'X'), + (0x11D90, 'V'), + (0x11D92, 'X'), + (0x11D93, 'V'), + (0x11D99, 'X'), + (0x11DA0, 'V'), + (0x11DAA, 'X'), + (0x11EE0, 'V'), + (0x11EF9, 'X'), + (0x11F00, 'V'), + (0x11F11, 'X'), + (0x11F12, 'V'), + (0x11F3B, 'X'), + (0x11F3E, 'V'), + ] + +def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x11F5A, 'X'), + (0x11FB0, 'V'), + (0x11FB1, 'X'), + (0x11FC0, 'V'), + (0x11FF2, 'X'), + (0x11FFF, 'V'), + (0x1239A, 'X'), + (0x12400, 'V'), + (0x1246F, 'X'), + (0x12470, 'V'), + (0x12475, 'X'), + (0x12480, 'V'), + (0x12544, 'X'), + (0x12F90, 'V'), + (0x12FF3, 'X'), + (0x13000, 'V'), + (0x13430, 'X'), + (0x13440, 'V'), + (0x13456, 'X'), + (0x14400, 'V'), + (0x14647, 'X'), + (0x16800, 'V'), + (0x16A39, 'X'), + (0x16A40, 'V'), + (0x16A5F, 'X'), + (0x16A60, 'V'), + (0x16A6A, 'X'), + (0x16A6E, 'V'), + (0x16ABF, 'X'), + (0x16AC0, 'V'), + (0x16ACA, 'X'), + (0x16AD0, 'V'), + (0x16AEE, 'X'), + (0x16AF0, 'V'), + (0x16AF6, 'X'), + (0x16B00, 'V'), + (0x16B46, 'X'), + (0x16B50, 'V'), + (0x16B5A, 'X'), + (0x16B5B, 'V'), + (0x16B62, 'X'), + (0x16B63, 'V'), + (0x16B78, 'X'), + (0x16B7D, 'V'), + (0x16B90, 'X'), + (0x16E40, 'M', '𖹠'), + (0x16E41, 'M', '𖹡'), + (0x16E42, 'M', '𖹢'), + (0x16E43, 'M', '𖹣'), + (0x16E44, 'M', '𖹤'), + (0x16E45, 'M', '𖹥'), + (0x16E46, 'M', '𖹦'), + (0x16E47, 'M', '𖹧'), + (0x16E48, 'M', '𖹨'), + (0x16E49, 'M', '𖹩'), + (0x16E4A, 'M', '𖹪'), + (0x16E4B, 'M', '𖹫'), + (0x16E4C, 'M', '𖹬'), + (0x16E4D, 'M', '𖹭'), + (0x16E4E, 'M', '𖹮'), + (0x16E4F, 'M', '𖹯'), + (0x16E50, 'M', '𖹰'), + (0x16E51, 'M', '𖹱'), + (0x16E52, 'M', '𖹲'), + (0x16E53, 'M', '𖹳'), + (0x16E54, 'M', '𖹴'), + (0x16E55, 'M', '𖹵'), + (0x16E56, 'M', '𖹶'), + (0x16E57, 'M', '𖹷'), + (0x16E58, 'M', '𖹸'), + (0x16E59, 'M', '𖹹'), + (0x16E5A, 'M', '𖹺'), + (0x16E5B, 'M', '𖹻'), + (0x16E5C, 'M', '𖹼'), + (0x16E5D, 'M', '𖹽'), + (0x16E5E, 'M', '𖹾'), + (0x16E5F, 'M', '𖹿'), + (0x16E60, 'V'), + (0x16E9B, 'X'), + (0x16F00, 'V'), + (0x16F4B, 'X'), + (0x16F4F, 'V'), + (0x16F88, 'X'), + (0x16F8F, 'V'), + (0x16FA0, 'X'), + (0x16FE0, 'V'), + (0x16FE5, 'X'), + (0x16FF0, 'V'), + (0x16FF2, 'X'), + (0x17000, 'V'), + (0x187F8, 'X'), + (0x18800, 'V'), + (0x18CD6, 'X'), + (0x18D00, 'V'), + (0x18D09, 'X'), + (0x1AFF0, 'V'), + (0x1AFF4, 'X'), + (0x1AFF5, 'V'), + (0x1AFFC, 'X'), + (0x1AFFD, 'V'), + ] + +def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1AFFF, 'X'), + (0x1B000, 'V'), + (0x1B123, 'X'), + (0x1B132, 'V'), + (0x1B133, 'X'), + (0x1B150, 'V'), + (0x1B153, 'X'), + (0x1B155, 'V'), + (0x1B156, 'X'), + (0x1B164, 'V'), + (0x1B168, 'X'), + (0x1B170, 'V'), + (0x1B2FC, 'X'), + (0x1BC00, 'V'), + (0x1BC6B, 'X'), + (0x1BC70, 'V'), + (0x1BC7D, 'X'), + (0x1BC80, 'V'), + (0x1BC89, 'X'), + (0x1BC90, 'V'), + (0x1BC9A, 'X'), + (0x1BC9C, 'V'), + (0x1BCA0, 'I'), + (0x1BCA4, 'X'), + (0x1CF00, 'V'), + (0x1CF2E, 'X'), + (0x1CF30, 'V'), + (0x1CF47, 'X'), + (0x1CF50, 'V'), + (0x1CFC4, 'X'), + (0x1D000, 'V'), + (0x1D0F6, 'X'), + (0x1D100, 'V'), + (0x1D127, 'X'), + (0x1D129, 'V'), + (0x1D15E, 'M', '𝅗𝅥'), + (0x1D15F, 'M', '𝅘𝅥'), + (0x1D160, 'M', '𝅘𝅥𝅮'), + (0x1D161, 'M', '𝅘𝅥𝅯'), + (0x1D162, 'M', '𝅘𝅥𝅰'), + (0x1D163, 'M', '𝅘𝅥𝅱'), + (0x1D164, 'M', '𝅘𝅥𝅲'), + (0x1D165, 'V'), + (0x1D173, 'X'), + (0x1D17B, 'V'), + (0x1D1BB, 'M', '𝆹𝅥'), + (0x1D1BC, 'M', '𝆺𝅥'), + (0x1D1BD, 'M', '𝆹𝅥𝅮'), + (0x1D1BE, 'M', '𝆺𝅥𝅮'), + (0x1D1BF, 'M', '𝆹𝅥𝅯'), + (0x1D1C0, 'M', '𝆺𝅥𝅯'), + (0x1D1C1, 'V'), + (0x1D1EB, 'X'), + (0x1D200, 'V'), + (0x1D246, 'X'), + (0x1D2C0, 'V'), + (0x1D2D4, 'X'), + (0x1D2E0, 'V'), + (0x1D2F4, 'X'), + (0x1D300, 'V'), + (0x1D357, 'X'), + (0x1D360, 'V'), + (0x1D379, 'X'), + (0x1D400, 'M', 'a'), + (0x1D401, 'M', 'b'), + (0x1D402, 'M', 'c'), + (0x1D403, 'M', 'd'), + (0x1D404, 'M', 'e'), + (0x1D405, 'M', 'f'), + (0x1D406, 'M', 'g'), + (0x1D407, 'M', 'h'), + (0x1D408, 'M', 'i'), + (0x1D409, 'M', 'j'), + (0x1D40A, 'M', 'k'), + (0x1D40B, 'M', 'l'), + (0x1D40C, 'M', 'm'), + (0x1D40D, 'M', 'n'), + (0x1D40E, 'M', 'o'), + (0x1D40F, 'M', 'p'), + (0x1D410, 'M', 'q'), + (0x1D411, 'M', 'r'), + (0x1D412, 'M', 's'), + (0x1D413, 'M', 't'), + (0x1D414, 'M', 'u'), + (0x1D415, 'M', 'v'), + (0x1D416, 'M', 'w'), + (0x1D417, 'M', 'x'), + (0x1D418, 'M', 'y'), + (0x1D419, 'M', 'z'), + (0x1D41A, 'M', 'a'), + (0x1D41B, 'M', 'b'), + (0x1D41C, 'M', 'c'), + (0x1D41D, 'M', 'd'), + (0x1D41E, 'M', 'e'), + (0x1D41F, 'M', 'f'), + (0x1D420, 'M', 'g'), + (0x1D421, 'M', 'h'), + (0x1D422, 'M', 'i'), + (0x1D423, 'M', 'j'), + (0x1D424, 'M', 'k'), + ] + +def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D425, 'M', 'l'), + (0x1D426, 'M', 'm'), + (0x1D427, 'M', 'n'), + (0x1D428, 'M', 'o'), + (0x1D429, 'M', 'p'), + (0x1D42A, 'M', 'q'), + (0x1D42B, 'M', 'r'), + (0x1D42C, 'M', 's'), + (0x1D42D, 'M', 't'), + (0x1D42E, 'M', 'u'), + (0x1D42F, 'M', 'v'), + (0x1D430, 'M', 'w'), + (0x1D431, 'M', 'x'), + (0x1D432, 'M', 'y'), + (0x1D433, 'M', 'z'), + (0x1D434, 'M', 'a'), + (0x1D435, 'M', 'b'), + (0x1D436, 'M', 'c'), + (0x1D437, 'M', 'd'), + (0x1D438, 'M', 'e'), + (0x1D439, 'M', 'f'), + (0x1D43A, 'M', 'g'), + (0x1D43B, 'M', 'h'), + (0x1D43C, 'M', 'i'), + (0x1D43D, 'M', 'j'), + (0x1D43E, 'M', 'k'), + (0x1D43F, 'M', 'l'), + (0x1D440, 'M', 'm'), + (0x1D441, 'M', 'n'), + (0x1D442, 'M', 'o'), + (0x1D443, 'M', 'p'), + (0x1D444, 'M', 'q'), + (0x1D445, 'M', 'r'), + (0x1D446, 'M', 's'), + (0x1D447, 'M', 't'), + (0x1D448, 'M', 'u'), + (0x1D449, 'M', 'v'), + (0x1D44A, 'M', 'w'), + (0x1D44B, 'M', 'x'), + (0x1D44C, 'M', 'y'), + (0x1D44D, 'M', 'z'), + (0x1D44E, 'M', 'a'), + (0x1D44F, 'M', 'b'), + (0x1D450, 'M', 'c'), + (0x1D451, 'M', 'd'), + (0x1D452, 'M', 'e'), + (0x1D453, 'M', 'f'), + (0x1D454, 'M', 'g'), + (0x1D455, 'X'), + (0x1D456, 'M', 'i'), + (0x1D457, 'M', 'j'), + (0x1D458, 'M', 'k'), + (0x1D459, 'M', 'l'), + (0x1D45A, 'M', 'm'), + (0x1D45B, 'M', 'n'), + (0x1D45C, 'M', 'o'), + (0x1D45D, 'M', 'p'), + (0x1D45E, 'M', 'q'), + (0x1D45F, 'M', 'r'), + (0x1D460, 'M', 's'), + (0x1D461, 'M', 't'), + (0x1D462, 'M', 'u'), + (0x1D463, 'M', 'v'), + (0x1D464, 'M', 'w'), + (0x1D465, 'M', 'x'), + (0x1D466, 'M', 'y'), + (0x1D467, 'M', 'z'), + (0x1D468, 'M', 'a'), + (0x1D469, 'M', 'b'), + (0x1D46A, 'M', 'c'), + (0x1D46B, 'M', 'd'), + (0x1D46C, 'M', 'e'), + (0x1D46D, 'M', 'f'), + (0x1D46E, 'M', 'g'), + (0x1D46F, 'M', 'h'), + (0x1D470, 'M', 'i'), + (0x1D471, 'M', 'j'), + (0x1D472, 'M', 'k'), + (0x1D473, 'M', 'l'), + (0x1D474, 'M', 'm'), + (0x1D475, 'M', 'n'), + (0x1D476, 'M', 'o'), + (0x1D477, 'M', 'p'), + (0x1D478, 'M', 'q'), + (0x1D479, 'M', 'r'), + (0x1D47A, 'M', 's'), + (0x1D47B, 'M', 't'), + (0x1D47C, 'M', 'u'), + (0x1D47D, 'M', 'v'), + (0x1D47E, 'M', 'w'), + (0x1D47F, 'M', 'x'), + (0x1D480, 'M', 'y'), + (0x1D481, 'M', 'z'), + (0x1D482, 'M', 'a'), + (0x1D483, 'M', 'b'), + (0x1D484, 'M', 'c'), + (0x1D485, 'M', 'd'), + (0x1D486, 'M', 'e'), + (0x1D487, 'M', 'f'), + (0x1D488, 'M', 'g'), + ] + +def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D489, 'M', 'h'), + (0x1D48A, 'M', 'i'), + (0x1D48B, 'M', 'j'), + (0x1D48C, 'M', 'k'), + (0x1D48D, 'M', 'l'), + (0x1D48E, 'M', 'm'), + (0x1D48F, 'M', 'n'), + (0x1D490, 'M', 'o'), + (0x1D491, 'M', 'p'), + (0x1D492, 'M', 'q'), + (0x1D493, 'M', 'r'), + (0x1D494, 'M', 's'), + (0x1D495, 'M', 't'), + (0x1D496, 'M', 'u'), + (0x1D497, 'M', 'v'), + (0x1D498, 'M', 'w'), + (0x1D499, 'M', 'x'), + (0x1D49A, 'M', 'y'), + (0x1D49B, 'M', 'z'), + (0x1D49C, 'M', 'a'), + (0x1D49D, 'X'), + (0x1D49E, 'M', 'c'), + (0x1D49F, 'M', 'd'), + (0x1D4A0, 'X'), + (0x1D4A2, 'M', 'g'), + (0x1D4A3, 'X'), + (0x1D4A5, 'M', 'j'), + (0x1D4A6, 'M', 'k'), + (0x1D4A7, 'X'), + (0x1D4A9, 'M', 'n'), + (0x1D4AA, 'M', 'o'), + (0x1D4AB, 'M', 'p'), + (0x1D4AC, 'M', 'q'), + (0x1D4AD, 'X'), + (0x1D4AE, 'M', 's'), + (0x1D4AF, 'M', 't'), + (0x1D4B0, 'M', 'u'), + (0x1D4B1, 'M', 'v'), + (0x1D4B2, 'M', 'w'), + (0x1D4B3, 'M', 'x'), + (0x1D4B4, 'M', 'y'), + (0x1D4B5, 'M', 'z'), + (0x1D4B6, 'M', 'a'), + (0x1D4B7, 'M', 'b'), + (0x1D4B8, 'M', 'c'), + (0x1D4B9, 'M', 'd'), + (0x1D4BA, 'X'), + (0x1D4BB, 'M', 'f'), + (0x1D4BC, 'X'), + (0x1D4BD, 'M', 'h'), + (0x1D4BE, 'M', 'i'), + (0x1D4BF, 'M', 'j'), + (0x1D4C0, 'M', 'k'), + (0x1D4C1, 'M', 'l'), + (0x1D4C2, 'M', 'm'), + (0x1D4C3, 'M', 'n'), + (0x1D4C4, 'X'), + (0x1D4C5, 'M', 'p'), + (0x1D4C6, 'M', 'q'), + (0x1D4C7, 'M', 'r'), + (0x1D4C8, 'M', 's'), + (0x1D4C9, 'M', 't'), + (0x1D4CA, 'M', 'u'), + (0x1D4CB, 'M', 'v'), + (0x1D4CC, 'M', 'w'), + (0x1D4CD, 'M', 'x'), + (0x1D4CE, 'M', 'y'), + (0x1D4CF, 'M', 'z'), + (0x1D4D0, 'M', 'a'), + (0x1D4D1, 'M', 'b'), + (0x1D4D2, 'M', 'c'), + (0x1D4D3, 'M', 'd'), + (0x1D4D4, 'M', 'e'), + (0x1D4D5, 'M', 'f'), + (0x1D4D6, 'M', 'g'), + (0x1D4D7, 'M', 'h'), + (0x1D4D8, 'M', 'i'), + (0x1D4D9, 'M', 'j'), + (0x1D4DA, 'M', 'k'), + (0x1D4DB, 'M', 'l'), + (0x1D4DC, 'M', 'm'), + (0x1D4DD, 'M', 'n'), + (0x1D4DE, 'M', 'o'), + (0x1D4DF, 'M', 'p'), + (0x1D4E0, 'M', 'q'), + (0x1D4E1, 'M', 'r'), + (0x1D4E2, 'M', 's'), + (0x1D4E3, 'M', 't'), + (0x1D4E4, 'M', 'u'), + (0x1D4E5, 'M', 'v'), + (0x1D4E6, 'M', 'w'), + (0x1D4E7, 'M', 'x'), + (0x1D4E8, 'M', 'y'), + (0x1D4E9, 'M', 'z'), + (0x1D4EA, 'M', 'a'), + (0x1D4EB, 'M', 'b'), + (0x1D4EC, 'M', 'c'), + (0x1D4ED, 'M', 'd'), + (0x1D4EE, 'M', 'e'), + (0x1D4EF, 'M', 'f'), + ] + +def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D4F0, 'M', 'g'), + (0x1D4F1, 'M', 'h'), + (0x1D4F2, 'M', 'i'), + (0x1D4F3, 'M', 'j'), + (0x1D4F4, 'M', 'k'), + (0x1D4F5, 'M', 'l'), + (0x1D4F6, 'M', 'm'), + (0x1D4F7, 'M', 'n'), + (0x1D4F8, 'M', 'o'), + (0x1D4F9, 'M', 'p'), + (0x1D4FA, 'M', 'q'), + (0x1D4FB, 'M', 'r'), + (0x1D4FC, 'M', 's'), + (0x1D4FD, 'M', 't'), + (0x1D4FE, 'M', 'u'), + (0x1D4FF, 'M', 'v'), + (0x1D500, 'M', 'w'), + (0x1D501, 'M', 'x'), + (0x1D502, 'M', 'y'), + (0x1D503, 'M', 'z'), + (0x1D504, 'M', 'a'), + (0x1D505, 'M', 'b'), + (0x1D506, 'X'), + (0x1D507, 'M', 'd'), + (0x1D508, 'M', 'e'), + (0x1D509, 'M', 'f'), + (0x1D50A, 'M', 'g'), + (0x1D50B, 'X'), + (0x1D50D, 'M', 'j'), + (0x1D50E, 'M', 'k'), + (0x1D50F, 'M', 'l'), + (0x1D510, 'M', 'm'), + (0x1D511, 'M', 'n'), + (0x1D512, 'M', 'o'), + (0x1D513, 'M', 'p'), + (0x1D514, 'M', 'q'), + (0x1D515, 'X'), + (0x1D516, 'M', 's'), + (0x1D517, 'M', 't'), + (0x1D518, 'M', 'u'), + (0x1D519, 'M', 'v'), + (0x1D51A, 'M', 'w'), + (0x1D51B, 'M', 'x'), + (0x1D51C, 'M', 'y'), + (0x1D51D, 'X'), + (0x1D51E, 'M', 'a'), + (0x1D51F, 'M', 'b'), + (0x1D520, 'M', 'c'), + (0x1D521, 'M', 'd'), + (0x1D522, 'M', 'e'), + (0x1D523, 'M', 'f'), + (0x1D524, 'M', 'g'), + (0x1D525, 'M', 'h'), + (0x1D526, 'M', 'i'), + (0x1D527, 'M', 'j'), + (0x1D528, 'M', 'k'), + (0x1D529, 'M', 'l'), + (0x1D52A, 'M', 'm'), + (0x1D52B, 'M', 'n'), + (0x1D52C, 'M', 'o'), + (0x1D52D, 'M', 'p'), + (0x1D52E, 'M', 'q'), + (0x1D52F, 'M', 'r'), + (0x1D530, 'M', 's'), + (0x1D531, 'M', 't'), + (0x1D532, 'M', 'u'), + (0x1D533, 'M', 'v'), + (0x1D534, 'M', 'w'), + (0x1D535, 'M', 'x'), + (0x1D536, 'M', 'y'), + (0x1D537, 'M', 'z'), + (0x1D538, 'M', 'a'), + (0x1D539, 'M', 'b'), + (0x1D53A, 'X'), + (0x1D53B, 'M', 'd'), + (0x1D53C, 'M', 'e'), + (0x1D53D, 'M', 'f'), + (0x1D53E, 'M', 'g'), + (0x1D53F, 'X'), + (0x1D540, 'M', 'i'), + (0x1D541, 'M', 'j'), + (0x1D542, 'M', 'k'), + (0x1D543, 'M', 'l'), + (0x1D544, 'M', 'm'), + (0x1D545, 'X'), + (0x1D546, 'M', 'o'), + (0x1D547, 'X'), + (0x1D54A, 'M', 's'), + (0x1D54B, 'M', 't'), + (0x1D54C, 'M', 'u'), + (0x1D54D, 'M', 'v'), + (0x1D54E, 'M', 'w'), + (0x1D54F, 'M', 'x'), + (0x1D550, 'M', 'y'), + (0x1D551, 'X'), + (0x1D552, 'M', 'a'), + (0x1D553, 'M', 'b'), + (0x1D554, 'M', 'c'), + (0x1D555, 'M', 'd'), + (0x1D556, 'M', 'e'), + ] + +def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D557, 'M', 'f'), + (0x1D558, 'M', 'g'), + (0x1D559, 'M', 'h'), + (0x1D55A, 'M', 'i'), + (0x1D55B, 'M', 'j'), + (0x1D55C, 'M', 'k'), + (0x1D55D, 'M', 'l'), + (0x1D55E, 'M', 'm'), + (0x1D55F, 'M', 'n'), + (0x1D560, 'M', 'o'), + (0x1D561, 'M', 'p'), + (0x1D562, 'M', 'q'), + (0x1D563, 'M', 'r'), + (0x1D564, 'M', 's'), + (0x1D565, 'M', 't'), + (0x1D566, 'M', 'u'), + (0x1D567, 'M', 'v'), + (0x1D568, 'M', 'w'), + (0x1D569, 'M', 'x'), + (0x1D56A, 'M', 'y'), + (0x1D56B, 'M', 'z'), + (0x1D56C, 'M', 'a'), + (0x1D56D, 'M', 'b'), + (0x1D56E, 'M', 'c'), + (0x1D56F, 'M', 'd'), + (0x1D570, 'M', 'e'), + (0x1D571, 'M', 'f'), + (0x1D572, 'M', 'g'), + (0x1D573, 'M', 'h'), + (0x1D574, 'M', 'i'), + (0x1D575, 'M', 'j'), + (0x1D576, 'M', 'k'), + (0x1D577, 'M', 'l'), + (0x1D578, 'M', 'm'), + (0x1D579, 'M', 'n'), + (0x1D57A, 'M', 'o'), + (0x1D57B, 'M', 'p'), + (0x1D57C, 'M', 'q'), + (0x1D57D, 'M', 'r'), + (0x1D57E, 'M', 's'), + (0x1D57F, 'M', 't'), + (0x1D580, 'M', 'u'), + (0x1D581, 'M', 'v'), + (0x1D582, 'M', 'w'), + (0x1D583, 'M', 'x'), + (0x1D584, 'M', 'y'), + (0x1D585, 'M', 'z'), + (0x1D586, 'M', 'a'), + (0x1D587, 'M', 'b'), + (0x1D588, 'M', 'c'), + (0x1D589, 'M', 'd'), + (0x1D58A, 'M', 'e'), + (0x1D58B, 'M', 'f'), + (0x1D58C, 'M', 'g'), + (0x1D58D, 'M', 'h'), + (0x1D58E, 'M', 'i'), + (0x1D58F, 'M', 'j'), + (0x1D590, 'M', 'k'), + (0x1D591, 'M', 'l'), + (0x1D592, 'M', 'm'), + (0x1D593, 'M', 'n'), + (0x1D594, 'M', 'o'), + (0x1D595, 'M', 'p'), + (0x1D596, 'M', 'q'), + (0x1D597, 'M', 'r'), + (0x1D598, 'M', 's'), + (0x1D599, 'M', 't'), + (0x1D59A, 'M', 'u'), + (0x1D59B, 'M', 'v'), + (0x1D59C, 'M', 'w'), + (0x1D59D, 'M', 'x'), + (0x1D59E, 'M', 'y'), + (0x1D59F, 'M', 'z'), + (0x1D5A0, 'M', 'a'), + (0x1D5A1, 'M', 'b'), + (0x1D5A2, 'M', 'c'), + (0x1D5A3, 'M', 'd'), + (0x1D5A4, 'M', 'e'), + (0x1D5A5, 'M', 'f'), + (0x1D5A6, 'M', 'g'), + (0x1D5A7, 'M', 'h'), + (0x1D5A8, 'M', 'i'), + (0x1D5A9, 'M', 'j'), + (0x1D5AA, 'M', 'k'), + (0x1D5AB, 'M', 'l'), + (0x1D5AC, 'M', 'm'), + (0x1D5AD, 'M', 'n'), + (0x1D5AE, 'M', 'o'), + (0x1D5AF, 'M', 'p'), + (0x1D5B0, 'M', 'q'), + (0x1D5B1, 'M', 'r'), + (0x1D5B2, 'M', 's'), + (0x1D5B3, 'M', 't'), + (0x1D5B4, 'M', 'u'), + (0x1D5B5, 'M', 'v'), + (0x1D5B6, 'M', 'w'), + (0x1D5B7, 'M', 'x'), + (0x1D5B8, 'M', 'y'), + (0x1D5B9, 'M', 'z'), + (0x1D5BA, 'M', 'a'), + ] + +def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D5BB, 'M', 'b'), + (0x1D5BC, 'M', 'c'), + (0x1D5BD, 'M', 'd'), + (0x1D5BE, 'M', 'e'), + (0x1D5BF, 'M', 'f'), + (0x1D5C0, 'M', 'g'), + (0x1D5C1, 'M', 'h'), + (0x1D5C2, 'M', 'i'), + (0x1D5C3, 'M', 'j'), + (0x1D5C4, 'M', 'k'), + (0x1D5C5, 'M', 'l'), + (0x1D5C6, 'M', 'm'), + (0x1D5C7, 'M', 'n'), + (0x1D5C8, 'M', 'o'), + (0x1D5C9, 'M', 'p'), + (0x1D5CA, 'M', 'q'), + (0x1D5CB, 'M', 'r'), + (0x1D5CC, 'M', 's'), + (0x1D5CD, 'M', 't'), + (0x1D5CE, 'M', 'u'), + (0x1D5CF, 'M', 'v'), + (0x1D5D0, 'M', 'w'), + (0x1D5D1, 'M', 'x'), + (0x1D5D2, 'M', 'y'), + (0x1D5D3, 'M', 'z'), + (0x1D5D4, 'M', 'a'), + (0x1D5D5, 'M', 'b'), + (0x1D5D6, 'M', 'c'), + (0x1D5D7, 'M', 'd'), + (0x1D5D8, 'M', 'e'), + (0x1D5D9, 'M', 'f'), + (0x1D5DA, 'M', 'g'), + (0x1D5DB, 'M', 'h'), + (0x1D5DC, 'M', 'i'), + (0x1D5DD, 'M', 'j'), + (0x1D5DE, 'M', 'k'), + (0x1D5DF, 'M', 'l'), + (0x1D5E0, 'M', 'm'), + (0x1D5E1, 'M', 'n'), + (0x1D5E2, 'M', 'o'), + (0x1D5E3, 'M', 'p'), + (0x1D5E4, 'M', 'q'), + (0x1D5E5, 'M', 'r'), + (0x1D5E6, 'M', 's'), + (0x1D5E7, 'M', 't'), + (0x1D5E8, 'M', 'u'), + (0x1D5E9, 'M', 'v'), + (0x1D5EA, 'M', 'w'), + (0x1D5EB, 'M', 'x'), + (0x1D5EC, 'M', 'y'), + (0x1D5ED, 'M', 'z'), + (0x1D5EE, 'M', 'a'), + (0x1D5EF, 'M', 'b'), + (0x1D5F0, 'M', 'c'), + (0x1D5F1, 'M', 'd'), + (0x1D5F2, 'M', 'e'), + (0x1D5F3, 'M', 'f'), + (0x1D5F4, 'M', 'g'), + (0x1D5F5, 'M', 'h'), + (0x1D5F6, 'M', 'i'), + (0x1D5F7, 'M', 'j'), + (0x1D5F8, 'M', 'k'), + (0x1D5F9, 'M', 'l'), + (0x1D5FA, 'M', 'm'), + (0x1D5FB, 'M', 'n'), + (0x1D5FC, 'M', 'o'), + (0x1D5FD, 'M', 'p'), + (0x1D5FE, 'M', 'q'), + (0x1D5FF, 'M', 'r'), + (0x1D600, 'M', 's'), + (0x1D601, 'M', 't'), + (0x1D602, 'M', 'u'), + (0x1D603, 'M', 'v'), + (0x1D604, 'M', 'w'), + (0x1D605, 'M', 'x'), + (0x1D606, 'M', 'y'), + (0x1D607, 'M', 'z'), + (0x1D608, 'M', 'a'), + (0x1D609, 'M', 'b'), + (0x1D60A, 'M', 'c'), + (0x1D60B, 'M', 'd'), + (0x1D60C, 'M', 'e'), + (0x1D60D, 'M', 'f'), + (0x1D60E, 'M', 'g'), + (0x1D60F, 'M', 'h'), + (0x1D610, 'M', 'i'), + (0x1D611, 'M', 'j'), + (0x1D612, 'M', 'k'), + (0x1D613, 'M', 'l'), + (0x1D614, 'M', 'm'), + (0x1D615, 'M', 'n'), + (0x1D616, 'M', 'o'), + (0x1D617, 'M', 'p'), + (0x1D618, 'M', 'q'), + (0x1D619, 'M', 'r'), + (0x1D61A, 'M', 's'), + (0x1D61B, 'M', 't'), + (0x1D61C, 'M', 'u'), + (0x1D61D, 'M', 'v'), + (0x1D61E, 'M', 'w'), + ] + +def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D61F, 'M', 'x'), + (0x1D620, 'M', 'y'), + (0x1D621, 'M', 'z'), + (0x1D622, 'M', 'a'), + (0x1D623, 'M', 'b'), + (0x1D624, 'M', 'c'), + (0x1D625, 'M', 'd'), + (0x1D626, 'M', 'e'), + (0x1D627, 'M', 'f'), + (0x1D628, 'M', 'g'), + (0x1D629, 'M', 'h'), + (0x1D62A, 'M', 'i'), + (0x1D62B, 'M', 'j'), + (0x1D62C, 'M', 'k'), + (0x1D62D, 'M', 'l'), + (0x1D62E, 'M', 'm'), + (0x1D62F, 'M', 'n'), + (0x1D630, 'M', 'o'), + (0x1D631, 'M', 'p'), + (0x1D632, 'M', 'q'), + (0x1D633, 'M', 'r'), + (0x1D634, 'M', 's'), + (0x1D635, 'M', 't'), + (0x1D636, 'M', 'u'), + (0x1D637, 'M', 'v'), + (0x1D638, 'M', 'w'), + (0x1D639, 'M', 'x'), + (0x1D63A, 'M', 'y'), + (0x1D63B, 'M', 'z'), + (0x1D63C, 'M', 'a'), + (0x1D63D, 'M', 'b'), + (0x1D63E, 'M', 'c'), + (0x1D63F, 'M', 'd'), + (0x1D640, 'M', 'e'), + (0x1D641, 'M', 'f'), + (0x1D642, 'M', 'g'), + (0x1D643, 'M', 'h'), + (0x1D644, 'M', 'i'), + (0x1D645, 'M', 'j'), + (0x1D646, 'M', 'k'), + (0x1D647, 'M', 'l'), + (0x1D648, 'M', 'm'), + (0x1D649, 'M', 'n'), + (0x1D64A, 'M', 'o'), + (0x1D64B, 'M', 'p'), + (0x1D64C, 'M', 'q'), + (0x1D64D, 'M', 'r'), + (0x1D64E, 'M', 's'), + (0x1D64F, 'M', 't'), + (0x1D650, 'M', 'u'), + (0x1D651, 'M', 'v'), + (0x1D652, 'M', 'w'), + (0x1D653, 'M', 'x'), + (0x1D654, 'M', 'y'), + (0x1D655, 'M', 'z'), + (0x1D656, 'M', 'a'), + (0x1D657, 'M', 'b'), + (0x1D658, 'M', 'c'), + (0x1D659, 'M', 'd'), + (0x1D65A, 'M', 'e'), + (0x1D65B, 'M', 'f'), + (0x1D65C, 'M', 'g'), + (0x1D65D, 'M', 'h'), + (0x1D65E, 'M', 'i'), + (0x1D65F, 'M', 'j'), + (0x1D660, 'M', 'k'), + (0x1D661, 'M', 'l'), + (0x1D662, 'M', 'm'), + (0x1D663, 'M', 'n'), + (0x1D664, 'M', 'o'), + (0x1D665, 'M', 'p'), + (0x1D666, 'M', 'q'), + (0x1D667, 'M', 'r'), + (0x1D668, 'M', 's'), + (0x1D669, 'M', 't'), + (0x1D66A, 'M', 'u'), + (0x1D66B, 'M', 'v'), + (0x1D66C, 'M', 'w'), + (0x1D66D, 'M', 'x'), + (0x1D66E, 'M', 'y'), + (0x1D66F, 'M', 'z'), + (0x1D670, 'M', 'a'), + (0x1D671, 'M', 'b'), + (0x1D672, 'M', 'c'), + (0x1D673, 'M', 'd'), + (0x1D674, 'M', 'e'), + (0x1D675, 'M', 'f'), + (0x1D676, 'M', 'g'), + (0x1D677, 'M', 'h'), + (0x1D678, 'M', 'i'), + (0x1D679, 'M', 'j'), + (0x1D67A, 'M', 'k'), + (0x1D67B, 'M', 'l'), + (0x1D67C, 'M', 'm'), + (0x1D67D, 'M', 'n'), + (0x1D67E, 'M', 'o'), + (0x1D67F, 'M', 'p'), + (0x1D680, 'M', 'q'), + (0x1D681, 'M', 'r'), + (0x1D682, 'M', 's'), + ] + +def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D683, 'M', 't'), + (0x1D684, 'M', 'u'), + (0x1D685, 'M', 'v'), + (0x1D686, 'M', 'w'), + (0x1D687, 'M', 'x'), + (0x1D688, 'M', 'y'), + (0x1D689, 'M', 'z'), + (0x1D68A, 'M', 'a'), + (0x1D68B, 'M', 'b'), + (0x1D68C, 'M', 'c'), + (0x1D68D, 'M', 'd'), + (0x1D68E, 'M', 'e'), + (0x1D68F, 'M', 'f'), + (0x1D690, 'M', 'g'), + (0x1D691, 'M', 'h'), + (0x1D692, 'M', 'i'), + (0x1D693, 'M', 'j'), + (0x1D694, 'M', 'k'), + (0x1D695, 'M', 'l'), + (0x1D696, 'M', 'm'), + (0x1D697, 'M', 'n'), + (0x1D698, 'M', 'o'), + (0x1D699, 'M', 'p'), + (0x1D69A, 'M', 'q'), + (0x1D69B, 'M', 'r'), + (0x1D69C, 'M', 's'), + (0x1D69D, 'M', 't'), + (0x1D69E, 'M', 'u'), + (0x1D69F, 'M', 'v'), + (0x1D6A0, 'M', 'w'), + (0x1D6A1, 'M', 'x'), + (0x1D6A2, 'M', 'y'), + (0x1D6A3, 'M', 'z'), + (0x1D6A4, 'M', 'ı'), + (0x1D6A5, 'M', 'ȷ'), + (0x1D6A6, 'X'), + (0x1D6A8, 'M', 'α'), + (0x1D6A9, 'M', 'β'), + (0x1D6AA, 'M', 'γ'), + (0x1D6AB, 'M', 'δ'), + (0x1D6AC, 'M', 'ε'), + (0x1D6AD, 'M', 'ζ'), + (0x1D6AE, 'M', 'η'), + (0x1D6AF, 'M', 'θ'), + (0x1D6B0, 'M', 'ι'), + (0x1D6B1, 'M', 'κ'), + (0x1D6B2, 'M', 'λ'), + (0x1D6B3, 'M', 'μ'), + (0x1D6B4, 'M', 'ν'), + (0x1D6B5, 'M', 'ξ'), + (0x1D6B6, 'M', 'ο'), + (0x1D6B7, 'M', 'π'), + (0x1D6B8, 'M', 'ρ'), + (0x1D6B9, 'M', 'θ'), + (0x1D6BA, 'M', 'σ'), + (0x1D6BB, 'M', 'τ'), + (0x1D6BC, 'M', 'υ'), + (0x1D6BD, 'M', 'φ'), + (0x1D6BE, 'M', 'χ'), + (0x1D6BF, 'M', 'ψ'), + (0x1D6C0, 'M', 'ω'), + (0x1D6C1, 'M', '∇'), + (0x1D6C2, 'M', 'α'), + (0x1D6C3, 'M', 'β'), + (0x1D6C4, 'M', 'γ'), + (0x1D6C5, 'M', 'δ'), + (0x1D6C6, 'M', 'ε'), + (0x1D6C7, 'M', 'ζ'), + (0x1D6C8, 'M', 'η'), + (0x1D6C9, 'M', 'θ'), + (0x1D6CA, 'M', 'ι'), + (0x1D6CB, 'M', 'κ'), + (0x1D6CC, 'M', 'λ'), + (0x1D6CD, 'M', 'μ'), + (0x1D6CE, 'M', 'ν'), + (0x1D6CF, 'M', 'ξ'), + (0x1D6D0, 'M', 'ο'), + (0x1D6D1, 'M', 'π'), + (0x1D6D2, 'M', 'ρ'), + (0x1D6D3, 'M', 'σ'), + (0x1D6D5, 'M', 'τ'), + (0x1D6D6, 'M', 'υ'), + (0x1D6D7, 'M', 'φ'), + (0x1D6D8, 'M', 'χ'), + (0x1D6D9, 'M', 'ψ'), + (0x1D6DA, 'M', 'ω'), + (0x1D6DB, 'M', '∂'), + (0x1D6DC, 'M', 'ε'), + (0x1D6DD, 'M', 'θ'), + (0x1D6DE, 'M', 'κ'), + (0x1D6DF, 'M', 'φ'), + (0x1D6E0, 'M', 'ρ'), + (0x1D6E1, 'M', 'π'), + (0x1D6E2, 'M', 'α'), + (0x1D6E3, 'M', 'β'), + (0x1D6E4, 'M', 'γ'), + (0x1D6E5, 'M', 'δ'), + (0x1D6E6, 'M', 'ε'), + (0x1D6E7, 'M', 'ζ'), + (0x1D6E8, 'M', 'η'), + ] + +def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D6E9, 'M', 'θ'), + (0x1D6EA, 'M', 'ι'), + (0x1D6EB, 'M', 'κ'), + (0x1D6EC, 'M', 'λ'), + (0x1D6ED, 'M', 'μ'), + (0x1D6EE, 'M', 'ν'), + (0x1D6EF, 'M', 'ξ'), + (0x1D6F0, 'M', 'ο'), + (0x1D6F1, 'M', 'π'), + (0x1D6F2, 'M', 'ρ'), + (0x1D6F3, 'M', 'θ'), + (0x1D6F4, 'M', 'σ'), + (0x1D6F5, 'M', 'τ'), + (0x1D6F6, 'M', 'υ'), + (0x1D6F7, 'M', 'φ'), + (0x1D6F8, 'M', 'χ'), + (0x1D6F9, 'M', 'ψ'), + (0x1D6FA, 'M', 'ω'), + (0x1D6FB, 'M', '∇'), + (0x1D6FC, 'M', 'α'), + (0x1D6FD, 'M', 'β'), + (0x1D6FE, 'M', 'γ'), + (0x1D6FF, 'M', 'δ'), + (0x1D700, 'M', 'ε'), + (0x1D701, 'M', 'ζ'), + (0x1D702, 'M', 'η'), + (0x1D703, 'M', 'θ'), + (0x1D704, 'M', 'ι'), + (0x1D705, 'M', 'κ'), + (0x1D706, 'M', 'λ'), + (0x1D707, 'M', 'μ'), + (0x1D708, 'M', 'ν'), + (0x1D709, 'M', 'ξ'), + (0x1D70A, 'M', 'ο'), + (0x1D70B, 'M', 'π'), + (0x1D70C, 'M', 'ρ'), + (0x1D70D, 'M', 'σ'), + (0x1D70F, 'M', 'τ'), + (0x1D710, 'M', 'υ'), + (0x1D711, 'M', 'φ'), + (0x1D712, 'M', 'χ'), + (0x1D713, 'M', 'ψ'), + (0x1D714, 'M', 'ω'), + (0x1D715, 'M', '∂'), + (0x1D716, 'M', 'ε'), + (0x1D717, 'M', 'θ'), + (0x1D718, 'M', 'κ'), + (0x1D719, 'M', 'φ'), + (0x1D71A, 'M', 'ρ'), + (0x1D71B, 'M', 'π'), + (0x1D71C, 'M', 'α'), + (0x1D71D, 'M', 'β'), + (0x1D71E, 'M', 'γ'), + (0x1D71F, 'M', 'δ'), + (0x1D720, 'M', 'ε'), + (0x1D721, 'M', 'ζ'), + (0x1D722, 'M', 'η'), + (0x1D723, 'M', 'θ'), + (0x1D724, 'M', 'ι'), + (0x1D725, 'M', 'κ'), + (0x1D726, 'M', 'λ'), + (0x1D727, 'M', 'μ'), + (0x1D728, 'M', 'ν'), + (0x1D729, 'M', 'ξ'), + (0x1D72A, 'M', 'ο'), + (0x1D72B, 'M', 'π'), + (0x1D72C, 'M', 'ρ'), + (0x1D72D, 'M', 'θ'), + (0x1D72E, 'M', 'σ'), + (0x1D72F, 'M', 'τ'), + (0x1D730, 'M', 'υ'), + (0x1D731, 'M', 'φ'), + (0x1D732, 'M', 'χ'), + (0x1D733, 'M', 'ψ'), + (0x1D734, 'M', 'ω'), + (0x1D735, 'M', '∇'), + (0x1D736, 'M', 'α'), + (0x1D737, 'M', 'β'), + (0x1D738, 'M', 'γ'), + (0x1D739, 'M', 'δ'), + (0x1D73A, 'M', 'ε'), + (0x1D73B, 'M', 'ζ'), + (0x1D73C, 'M', 'η'), + (0x1D73D, 'M', 'θ'), + (0x1D73E, 'M', 'ι'), + (0x1D73F, 'M', 'κ'), + (0x1D740, 'M', 'λ'), + (0x1D741, 'M', 'μ'), + (0x1D742, 'M', 'ν'), + (0x1D743, 'M', 'ξ'), + (0x1D744, 'M', 'ο'), + (0x1D745, 'M', 'π'), + (0x1D746, 'M', 'ρ'), + (0x1D747, 'M', 'σ'), + (0x1D749, 'M', 'τ'), + (0x1D74A, 'M', 'υ'), + (0x1D74B, 'M', 'φ'), + (0x1D74C, 'M', 'χ'), + (0x1D74D, 'M', 'ψ'), + (0x1D74E, 'M', 'ω'), + ] + +def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D74F, 'M', '∂'), + (0x1D750, 'M', 'ε'), + (0x1D751, 'M', 'θ'), + (0x1D752, 'M', 'κ'), + (0x1D753, 'M', 'φ'), + (0x1D754, 'M', 'ρ'), + (0x1D755, 'M', 'π'), + (0x1D756, 'M', 'α'), + (0x1D757, 'M', 'β'), + (0x1D758, 'M', 'γ'), + (0x1D759, 'M', 'δ'), + (0x1D75A, 'M', 'ε'), + (0x1D75B, 'M', 'ζ'), + (0x1D75C, 'M', 'η'), + (0x1D75D, 'M', 'θ'), + (0x1D75E, 'M', 'ι'), + (0x1D75F, 'M', 'κ'), + (0x1D760, 'M', 'λ'), + (0x1D761, 'M', 'μ'), + (0x1D762, 'M', 'ν'), + (0x1D763, 'M', 'ξ'), + (0x1D764, 'M', 'ο'), + (0x1D765, 'M', 'π'), + (0x1D766, 'M', 'ρ'), + (0x1D767, 'M', 'θ'), + (0x1D768, 'M', 'σ'), + (0x1D769, 'M', 'τ'), + (0x1D76A, 'M', 'υ'), + (0x1D76B, 'M', 'φ'), + (0x1D76C, 'M', 'χ'), + (0x1D76D, 'M', 'ψ'), + (0x1D76E, 'M', 'ω'), + (0x1D76F, 'M', '∇'), + (0x1D770, 'M', 'α'), + (0x1D771, 'M', 'β'), + (0x1D772, 'M', 'γ'), + (0x1D773, 'M', 'δ'), + (0x1D774, 'M', 'ε'), + (0x1D775, 'M', 'ζ'), + (0x1D776, 'M', 'η'), + (0x1D777, 'M', 'θ'), + (0x1D778, 'M', 'ι'), + (0x1D779, 'M', 'κ'), + (0x1D77A, 'M', 'λ'), + (0x1D77B, 'M', 'μ'), + (0x1D77C, 'M', 'ν'), + (0x1D77D, 'M', 'ξ'), + (0x1D77E, 'M', 'ο'), + (0x1D77F, 'M', 'π'), + (0x1D780, 'M', 'ρ'), + (0x1D781, 'M', 'σ'), + (0x1D783, 'M', 'τ'), + (0x1D784, 'M', 'υ'), + (0x1D785, 'M', 'φ'), + (0x1D786, 'M', 'χ'), + (0x1D787, 'M', 'ψ'), + (0x1D788, 'M', 'ω'), + (0x1D789, 'M', '∂'), + (0x1D78A, 'M', 'ε'), + (0x1D78B, 'M', 'θ'), + (0x1D78C, 'M', 'κ'), + (0x1D78D, 'M', 'φ'), + (0x1D78E, 'M', 'ρ'), + (0x1D78F, 'M', 'π'), + (0x1D790, 'M', 'α'), + (0x1D791, 'M', 'β'), + (0x1D792, 'M', 'γ'), + (0x1D793, 'M', 'δ'), + (0x1D794, 'M', 'ε'), + (0x1D795, 'M', 'ζ'), + (0x1D796, 'M', 'η'), + (0x1D797, 'M', 'θ'), + (0x1D798, 'M', 'ι'), + (0x1D799, 'M', 'κ'), + (0x1D79A, 'M', 'λ'), + (0x1D79B, 'M', 'μ'), + (0x1D79C, 'M', 'ν'), + (0x1D79D, 'M', 'ξ'), + (0x1D79E, 'M', 'ο'), + (0x1D79F, 'M', 'π'), + (0x1D7A0, 'M', 'ρ'), + (0x1D7A1, 'M', 'θ'), + (0x1D7A2, 'M', 'σ'), + (0x1D7A3, 'M', 'τ'), + (0x1D7A4, 'M', 'υ'), + (0x1D7A5, 'M', 'φ'), + (0x1D7A6, 'M', 'χ'), + (0x1D7A7, 'M', 'ψ'), + (0x1D7A8, 'M', 'ω'), + (0x1D7A9, 'M', '∇'), + (0x1D7AA, 'M', 'α'), + (0x1D7AB, 'M', 'β'), + (0x1D7AC, 'M', 'γ'), + (0x1D7AD, 'M', 'δ'), + (0x1D7AE, 'M', 'ε'), + (0x1D7AF, 'M', 'ζ'), + (0x1D7B0, 'M', 'η'), + (0x1D7B1, 'M', 'θ'), + (0x1D7B2, 'M', 'ι'), + (0x1D7B3, 'M', 'κ'), + ] + +def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D7B4, 'M', 'λ'), + (0x1D7B5, 'M', 'μ'), + (0x1D7B6, 'M', 'ν'), + (0x1D7B7, 'M', 'ξ'), + (0x1D7B8, 'M', 'ο'), + (0x1D7B9, 'M', 'π'), + (0x1D7BA, 'M', 'ρ'), + (0x1D7BB, 'M', 'σ'), + (0x1D7BD, 'M', 'τ'), + (0x1D7BE, 'M', 'υ'), + (0x1D7BF, 'M', 'φ'), + (0x1D7C0, 'M', 'χ'), + (0x1D7C1, 'M', 'ψ'), + (0x1D7C2, 'M', 'ω'), + (0x1D7C3, 'M', '∂'), + (0x1D7C4, 'M', 'ε'), + (0x1D7C5, 'M', 'θ'), + (0x1D7C6, 'M', 'κ'), + (0x1D7C7, 'M', 'φ'), + (0x1D7C8, 'M', 'ρ'), + (0x1D7C9, 'M', 'π'), + (0x1D7CA, 'M', 'ϝ'), + (0x1D7CC, 'X'), + (0x1D7CE, 'M', '0'), + (0x1D7CF, 'M', '1'), + (0x1D7D0, 'M', '2'), + (0x1D7D1, 'M', '3'), + (0x1D7D2, 'M', '4'), + (0x1D7D3, 'M', '5'), + (0x1D7D4, 'M', '6'), + (0x1D7D5, 'M', '7'), + (0x1D7D6, 'M', '8'), + (0x1D7D7, 'M', '9'), + (0x1D7D8, 'M', '0'), + (0x1D7D9, 'M', '1'), + (0x1D7DA, 'M', '2'), + (0x1D7DB, 'M', '3'), + (0x1D7DC, 'M', '4'), + (0x1D7DD, 'M', '5'), + (0x1D7DE, 'M', '6'), + (0x1D7DF, 'M', '7'), + (0x1D7E0, 'M', '8'), + (0x1D7E1, 'M', '9'), + (0x1D7E2, 'M', '0'), + (0x1D7E3, 'M', '1'), + (0x1D7E4, 'M', '2'), + (0x1D7E5, 'M', '3'), + (0x1D7E6, 'M', '4'), + (0x1D7E7, 'M', '5'), + (0x1D7E8, 'M', '6'), + (0x1D7E9, 'M', '7'), + (0x1D7EA, 'M', '8'), + (0x1D7EB, 'M', '9'), + (0x1D7EC, 'M', '0'), + (0x1D7ED, 'M', '1'), + (0x1D7EE, 'M', '2'), + (0x1D7EF, 'M', '3'), + (0x1D7F0, 'M', '4'), + (0x1D7F1, 'M', '5'), + (0x1D7F2, 'M', '6'), + (0x1D7F3, 'M', '7'), + (0x1D7F4, 'M', '8'), + (0x1D7F5, 'M', '9'), + (0x1D7F6, 'M', '0'), + (0x1D7F7, 'M', '1'), + (0x1D7F8, 'M', '2'), + (0x1D7F9, 'M', '3'), + (0x1D7FA, 'M', '4'), + (0x1D7FB, 'M', '5'), + (0x1D7FC, 'M', '6'), + (0x1D7FD, 'M', '7'), + (0x1D7FE, 'M', '8'), + (0x1D7FF, 'M', '9'), + (0x1D800, 'V'), + (0x1DA8C, 'X'), + (0x1DA9B, 'V'), + (0x1DAA0, 'X'), + (0x1DAA1, 'V'), + (0x1DAB0, 'X'), + (0x1DF00, 'V'), + (0x1DF1F, 'X'), + (0x1DF25, 'V'), + (0x1DF2B, 'X'), + (0x1E000, 'V'), + (0x1E007, 'X'), + (0x1E008, 'V'), + (0x1E019, 'X'), + (0x1E01B, 'V'), + (0x1E022, 'X'), + (0x1E023, 'V'), + (0x1E025, 'X'), + (0x1E026, 'V'), + (0x1E02B, 'X'), + (0x1E030, 'M', 'а'), + (0x1E031, 'M', 'б'), + (0x1E032, 'M', 'в'), + (0x1E033, 'M', 'г'), + (0x1E034, 'M', 'д'), + (0x1E035, 'M', 'е'), + (0x1E036, 'M', 'ж'), + ] + +def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E037, 'M', 'з'), + (0x1E038, 'M', 'и'), + (0x1E039, 'M', 'к'), + (0x1E03A, 'M', 'л'), + (0x1E03B, 'M', 'м'), + (0x1E03C, 'M', 'о'), + (0x1E03D, 'M', 'п'), + (0x1E03E, 'M', 'р'), + (0x1E03F, 'M', 'с'), + (0x1E040, 'M', 'т'), + (0x1E041, 'M', 'у'), + (0x1E042, 'M', 'ф'), + (0x1E043, 'M', 'х'), + (0x1E044, 'M', 'ц'), + (0x1E045, 'M', 'ч'), + (0x1E046, 'M', 'ш'), + (0x1E047, 'M', 'ы'), + (0x1E048, 'M', 'э'), + (0x1E049, 'M', 'ю'), + (0x1E04A, 'M', 'ꚉ'), + (0x1E04B, 'M', 'ә'), + (0x1E04C, 'M', 'і'), + (0x1E04D, 'M', 'ј'), + (0x1E04E, 'M', 'ө'), + (0x1E04F, 'M', 'ү'), + (0x1E050, 'M', 'ӏ'), + (0x1E051, 'M', 'а'), + (0x1E052, 'M', 'б'), + (0x1E053, 'M', 'в'), + (0x1E054, 'M', 'г'), + (0x1E055, 'M', 'д'), + (0x1E056, 'M', 'е'), + (0x1E057, 'M', 'ж'), + (0x1E058, 'M', 'з'), + (0x1E059, 'M', 'и'), + (0x1E05A, 'M', 'к'), + (0x1E05B, 'M', 'л'), + (0x1E05C, 'M', 'о'), + (0x1E05D, 'M', 'п'), + (0x1E05E, 'M', 'с'), + (0x1E05F, 'M', 'у'), + (0x1E060, 'M', 'ф'), + (0x1E061, 'M', 'х'), + (0x1E062, 'M', 'ц'), + (0x1E063, 'M', 'ч'), + (0x1E064, 'M', 'ш'), + (0x1E065, 'M', 'ъ'), + (0x1E066, 'M', 'ы'), + (0x1E067, 'M', 'ґ'), + (0x1E068, 'M', 'і'), + (0x1E069, 'M', 'ѕ'), + (0x1E06A, 'M', 'џ'), + (0x1E06B, 'M', 'ҫ'), + (0x1E06C, 'M', 'ꙑ'), + (0x1E06D, 'M', 'ұ'), + (0x1E06E, 'X'), + (0x1E08F, 'V'), + (0x1E090, 'X'), + (0x1E100, 'V'), + (0x1E12D, 'X'), + (0x1E130, 'V'), + (0x1E13E, 'X'), + (0x1E140, 'V'), + (0x1E14A, 'X'), + (0x1E14E, 'V'), + (0x1E150, 'X'), + (0x1E290, 'V'), + (0x1E2AF, 'X'), + (0x1E2C0, 'V'), + (0x1E2FA, 'X'), + (0x1E2FF, 'V'), + (0x1E300, 'X'), + (0x1E4D0, 'V'), + (0x1E4FA, 'X'), + (0x1E7E0, 'V'), + (0x1E7E7, 'X'), + (0x1E7E8, 'V'), + (0x1E7EC, 'X'), + (0x1E7ED, 'V'), + (0x1E7EF, 'X'), + (0x1E7F0, 'V'), + (0x1E7FF, 'X'), + (0x1E800, 'V'), + (0x1E8C5, 'X'), + (0x1E8C7, 'V'), + (0x1E8D7, 'X'), + (0x1E900, 'M', '𞤢'), + (0x1E901, 'M', '𞤣'), + (0x1E902, 'M', '𞤤'), + (0x1E903, 'M', '𞤥'), + (0x1E904, 'M', '𞤦'), + (0x1E905, 'M', '𞤧'), + (0x1E906, 'M', '𞤨'), + (0x1E907, 'M', '𞤩'), + (0x1E908, 'M', '𞤪'), + (0x1E909, 'M', '𞤫'), + (0x1E90A, 'M', '𞤬'), + (0x1E90B, 'M', '𞤭'), + (0x1E90C, 'M', '𞤮'), + (0x1E90D, 'M', '𞤯'), + ] + +def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E90E, 'M', '𞤰'), + (0x1E90F, 'M', '𞤱'), + (0x1E910, 'M', '𞤲'), + (0x1E911, 'M', '𞤳'), + (0x1E912, 'M', '𞤴'), + (0x1E913, 'M', '𞤵'), + (0x1E914, 'M', '𞤶'), + (0x1E915, 'M', '𞤷'), + (0x1E916, 'M', '𞤸'), + (0x1E917, 'M', '𞤹'), + (0x1E918, 'M', '𞤺'), + (0x1E919, 'M', '𞤻'), + (0x1E91A, 'M', '𞤼'), + (0x1E91B, 'M', '𞤽'), + (0x1E91C, 'M', '𞤾'), + (0x1E91D, 'M', '𞤿'), + (0x1E91E, 'M', '𞥀'), + (0x1E91F, 'M', '𞥁'), + (0x1E920, 'M', '𞥂'), + (0x1E921, 'M', '𞥃'), + (0x1E922, 'V'), + (0x1E94C, 'X'), + (0x1E950, 'V'), + (0x1E95A, 'X'), + (0x1E95E, 'V'), + (0x1E960, 'X'), + (0x1EC71, 'V'), + (0x1ECB5, 'X'), + (0x1ED01, 'V'), + (0x1ED3E, 'X'), + (0x1EE00, 'M', 'ا'), + (0x1EE01, 'M', 'ب'), + (0x1EE02, 'M', 'ج'), + (0x1EE03, 'M', 'د'), + (0x1EE04, 'X'), + (0x1EE05, 'M', 'و'), + (0x1EE06, 'M', 'ز'), + (0x1EE07, 'M', 'ح'), + (0x1EE08, 'M', 'ط'), + (0x1EE09, 'M', 'ي'), + (0x1EE0A, 'M', 'ك'), + (0x1EE0B, 'M', 'ل'), + (0x1EE0C, 'M', 'م'), + (0x1EE0D, 'M', 'ن'), + (0x1EE0E, 'M', 'س'), + (0x1EE0F, 'M', 'ع'), + (0x1EE10, 'M', 'ف'), + (0x1EE11, 'M', 'ص'), + (0x1EE12, 'M', 'ق'), + (0x1EE13, 'M', 'ر'), + (0x1EE14, 'M', 'ش'), + (0x1EE15, 'M', 'ت'), + (0x1EE16, 'M', 'ث'), + (0x1EE17, 'M', 'خ'), + (0x1EE18, 'M', 'ذ'), + (0x1EE19, 'M', 'ض'), + (0x1EE1A, 'M', 'ظ'), + (0x1EE1B, 'M', 'غ'), + (0x1EE1C, 'M', 'ٮ'), + (0x1EE1D, 'M', 'ں'), + (0x1EE1E, 'M', 'ڡ'), + (0x1EE1F, 'M', 'ٯ'), + (0x1EE20, 'X'), + (0x1EE21, 'M', 'ب'), + (0x1EE22, 'M', 'ج'), + (0x1EE23, 'X'), + (0x1EE24, 'M', 'ه'), + (0x1EE25, 'X'), + (0x1EE27, 'M', 'ح'), + (0x1EE28, 'X'), + (0x1EE29, 'M', 'ي'), + (0x1EE2A, 'M', 'ك'), + (0x1EE2B, 'M', 'ل'), + (0x1EE2C, 'M', 'م'), + (0x1EE2D, 'M', 'ن'), + (0x1EE2E, 'M', 'س'), + (0x1EE2F, 'M', 'ع'), + (0x1EE30, 'M', 'ف'), + (0x1EE31, 'M', 'ص'), + (0x1EE32, 'M', 'ق'), + (0x1EE33, 'X'), + (0x1EE34, 'M', 'ش'), + (0x1EE35, 'M', 'ت'), + (0x1EE36, 'M', 'ث'), + (0x1EE37, 'M', 'خ'), + (0x1EE38, 'X'), + (0x1EE39, 'M', 'ض'), + (0x1EE3A, 'X'), + (0x1EE3B, 'M', 'غ'), + (0x1EE3C, 'X'), + (0x1EE42, 'M', 'ج'), + (0x1EE43, 'X'), + (0x1EE47, 'M', 'ح'), + (0x1EE48, 'X'), + (0x1EE49, 'M', 'ي'), + (0x1EE4A, 'X'), + (0x1EE4B, 'M', 'ل'), + (0x1EE4C, 'X'), + (0x1EE4D, 'M', 'ن'), + (0x1EE4E, 'M', 'س'), + ] + +def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EE4F, 'M', 'ع'), + (0x1EE50, 'X'), + (0x1EE51, 'M', 'ص'), + (0x1EE52, 'M', 'ق'), + (0x1EE53, 'X'), + (0x1EE54, 'M', 'ش'), + (0x1EE55, 'X'), + (0x1EE57, 'M', 'خ'), + (0x1EE58, 'X'), + (0x1EE59, 'M', 'ض'), + (0x1EE5A, 'X'), + (0x1EE5B, 'M', 'غ'), + (0x1EE5C, 'X'), + (0x1EE5D, 'M', 'ں'), + (0x1EE5E, 'X'), + (0x1EE5F, 'M', 'ٯ'), + (0x1EE60, 'X'), + (0x1EE61, 'M', 'ب'), + (0x1EE62, 'M', 'ج'), + (0x1EE63, 'X'), + (0x1EE64, 'M', 'ه'), + (0x1EE65, 'X'), + (0x1EE67, 'M', 'ح'), + (0x1EE68, 'M', 'ط'), + (0x1EE69, 'M', 'ي'), + (0x1EE6A, 'M', 'ك'), + (0x1EE6B, 'X'), + (0x1EE6C, 'M', 'م'), + (0x1EE6D, 'M', 'ن'), + (0x1EE6E, 'M', 'س'), + (0x1EE6F, 'M', 'ع'), + (0x1EE70, 'M', 'ف'), + (0x1EE71, 'M', 'ص'), + (0x1EE72, 'M', 'ق'), + (0x1EE73, 'X'), + (0x1EE74, 'M', 'ش'), + (0x1EE75, 'M', 'ت'), + (0x1EE76, 'M', 'ث'), + (0x1EE77, 'M', 'خ'), + (0x1EE78, 'X'), + (0x1EE79, 'M', 'ض'), + (0x1EE7A, 'M', 'ظ'), + (0x1EE7B, 'M', 'غ'), + (0x1EE7C, 'M', 'ٮ'), + (0x1EE7D, 'X'), + (0x1EE7E, 'M', 'ڡ'), + (0x1EE7F, 'X'), + (0x1EE80, 'M', 'ا'), + (0x1EE81, 'M', 'ب'), + (0x1EE82, 'M', 'ج'), + (0x1EE83, 'M', 'د'), + (0x1EE84, 'M', 'ه'), + (0x1EE85, 'M', 'و'), + (0x1EE86, 'M', 'ز'), + (0x1EE87, 'M', 'ح'), + (0x1EE88, 'M', 'ط'), + (0x1EE89, 'M', 'ي'), + (0x1EE8A, 'X'), + (0x1EE8B, 'M', 'ل'), + (0x1EE8C, 'M', 'م'), + (0x1EE8D, 'M', 'ن'), + (0x1EE8E, 'M', 'س'), + (0x1EE8F, 'M', 'ع'), + (0x1EE90, 'M', 'ف'), + (0x1EE91, 'M', 'ص'), + (0x1EE92, 'M', 'ق'), + (0x1EE93, 'M', 'ر'), + (0x1EE94, 'M', 'ش'), + (0x1EE95, 'M', 'ت'), + (0x1EE96, 'M', 'ث'), + (0x1EE97, 'M', 'خ'), + (0x1EE98, 'M', 'ذ'), + (0x1EE99, 'M', 'ض'), + (0x1EE9A, 'M', 'ظ'), + (0x1EE9B, 'M', 'غ'), + (0x1EE9C, 'X'), + (0x1EEA1, 'M', 'ب'), + (0x1EEA2, 'M', 'ج'), + (0x1EEA3, 'M', 'د'), + (0x1EEA4, 'X'), + (0x1EEA5, 'M', 'و'), + (0x1EEA6, 'M', 'ز'), + (0x1EEA7, 'M', 'ح'), + (0x1EEA8, 'M', 'ط'), + (0x1EEA9, 'M', 'ي'), + (0x1EEAA, 'X'), + (0x1EEAB, 'M', 'ل'), + (0x1EEAC, 'M', 'م'), + (0x1EEAD, 'M', 'ن'), + (0x1EEAE, 'M', 'س'), + (0x1EEAF, 'M', 'ع'), + (0x1EEB0, 'M', 'ف'), + (0x1EEB1, 'M', 'ص'), + (0x1EEB2, 'M', 'ق'), + (0x1EEB3, 'M', 'ر'), + (0x1EEB4, 'M', 'ش'), + (0x1EEB5, 'M', 'ت'), + (0x1EEB6, 'M', 'ث'), + (0x1EEB7, 'M', 'خ'), + (0x1EEB8, 'M', 'ذ'), + ] + +def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EEB9, 'M', 'ض'), + (0x1EEBA, 'M', 'ظ'), + (0x1EEBB, 'M', 'غ'), + (0x1EEBC, 'X'), + (0x1EEF0, 'V'), + (0x1EEF2, 'X'), + (0x1F000, 'V'), + (0x1F02C, 'X'), + (0x1F030, 'V'), + (0x1F094, 'X'), + (0x1F0A0, 'V'), + (0x1F0AF, 'X'), + (0x1F0B1, 'V'), + (0x1F0C0, 'X'), + (0x1F0C1, 'V'), + (0x1F0D0, 'X'), + (0x1F0D1, 'V'), + (0x1F0F6, 'X'), + (0x1F101, '3', '0,'), + (0x1F102, '3', '1,'), + (0x1F103, '3', '2,'), + (0x1F104, '3', '3,'), + (0x1F105, '3', '4,'), + (0x1F106, '3', '5,'), + (0x1F107, '3', '6,'), + (0x1F108, '3', '7,'), + (0x1F109, '3', '8,'), + (0x1F10A, '3', '9,'), + (0x1F10B, 'V'), + (0x1F110, '3', '(a)'), + (0x1F111, '3', '(b)'), + (0x1F112, '3', '(c)'), + (0x1F113, '3', '(d)'), + (0x1F114, '3', '(e)'), + (0x1F115, '3', '(f)'), + (0x1F116, '3', '(g)'), + (0x1F117, '3', '(h)'), + (0x1F118, '3', '(i)'), + (0x1F119, '3', '(j)'), + (0x1F11A, '3', '(k)'), + (0x1F11B, '3', '(l)'), + (0x1F11C, '3', '(m)'), + (0x1F11D, '3', '(n)'), + (0x1F11E, '3', '(o)'), + (0x1F11F, '3', '(p)'), + (0x1F120, '3', '(q)'), + (0x1F121, '3', '(r)'), + (0x1F122, '3', '(s)'), + (0x1F123, '3', '(t)'), + (0x1F124, '3', '(u)'), + (0x1F125, '3', '(v)'), + (0x1F126, '3', '(w)'), + (0x1F127, '3', '(x)'), + (0x1F128, '3', '(y)'), + (0x1F129, '3', '(z)'), + (0x1F12A, 'M', '〔s〕'), + (0x1F12B, 'M', 'c'), + (0x1F12C, 'M', 'r'), + (0x1F12D, 'M', 'cd'), + (0x1F12E, 'M', 'wz'), + (0x1F12F, 'V'), + (0x1F130, 'M', 'a'), + (0x1F131, 'M', 'b'), + (0x1F132, 'M', 'c'), + (0x1F133, 'M', 'd'), + (0x1F134, 'M', 'e'), + (0x1F135, 'M', 'f'), + (0x1F136, 'M', 'g'), + (0x1F137, 'M', 'h'), + (0x1F138, 'M', 'i'), + (0x1F139, 'M', 'j'), + (0x1F13A, 'M', 'k'), + (0x1F13B, 'M', 'l'), + (0x1F13C, 'M', 'm'), + (0x1F13D, 'M', 'n'), + (0x1F13E, 'M', 'o'), + (0x1F13F, 'M', 'p'), + (0x1F140, 'M', 'q'), + (0x1F141, 'M', 'r'), + (0x1F142, 'M', 's'), + (0x1F143, 'M', 't'), + (0x1F144, 'M', 'u'), + (0x1F145, 'M', 'v'), + (0x1F146, 'M', 'w'), + (0x1F147, 'M', 'x'), + (0x1F148, 'M', 'y'), + (0x1F149, 'M', 'z'), + (0x1F14A, 'M', 'hv'), + (0x1F14B, 'M', 'mv'), + (0x1F14C, 'M', 'sd'), + (0x1F14D, 'M', 'ss'), + (0x1F14E, 'M', 'ppv'), + (0x1F14F, 'M', 'wc'), + (0x1F150, 'V'), + (0x1F16A, 'M', 'mc'), + (0x1F16B, 'M', 'md'), + (0x1F16C, 'M', 'mr'), + (0x1F16D, 'V'), + (0x1F190, 'M', 'dj'), + (0x1F191, 'V'), + ] + +def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F1AE, 'X'), + (0x1F1E6, 'V'), + (0x1F200, 'M', 'ほか'), + (0x1F201, 'M', 'ココ'), + (0x1F202, 'M', 'サ'), + (0x1F203, 'X'), + (0x1F210, 'M', '手'), + (0x1F211, 'M', '字'), + (0x1F212, 'M', '双'), + (0x1F213, 'M', 'デ'), + (0x1F214, 'M', '二'), + (0x1F215, 'M', '多'), + (0x1F216, 'M', '解'), + (0x1F217, 'M', '天'), + (0x1F218, 'M', '交'), + (0x1F219, 'M', '映'), + (0x1F21A, 'M', '無'), + (0x1F21B, 'M', '料'), + (0x1F21C, 'M', '前'), + (0x1F21D, 'M', '後'), + (0x1F21E, 'M', '再'), + (0x1F21F, 'M', '新'), + (0x1F220, 'M', '初'), + (0x1F221, 'M', '終'), + (0x1F222, 'M', '生'), + (0x1F223, 'M', '販'), + (0x1F224, 'M', '声'), + (0x1F225, 'M', '吹'), + (0x1F226, 'M', '演'), + (0x1F227, 'M', '投'), + (0x1F228, 'M', '捕'), + (0x1F229, 'M', '一'), + (0x1F22A, 'M', '三'), + (0x1F22B, 'M', '遊'), + (0x1F22C, 'M', '左'), + (0x1F22D, 'M', '中'), + (0x1F22E, 'M', '右'), + (0x1F22F, 'M', '指'), + (0x1F230, 'M', '走'), + (0x1F231, 'M', '打'), + (0x1F232, 'M', '禁'), + (0x1F233, 'M', '空'), + (0x1F234, 'M', '合'), + (0x1F235, 'M', '満'), + (0x1F236, 'M', '有'), + (0x1F237, 'M', '月'), + (0x1F238, 'M', '申'), + (0x1F239, 'M', '割'), + (0x1F23A, 'M', '営'), + (0x1F23B, 'M', '配'), + (0x1F23C, 'X'), + (0x1F240, 'M', '〔本〕'), + (0x1F241, 'M', '〔三〕'), + (0x1F242, 'M', '〔二〕'), + (0x1F243, 'M', '〔安〕'), + (0x1F244, 'M', '〔点〕'), + (0x1F245, 'M', '〔打〕'), + (0x1F246, 'M', '〔盗〕'), + (0x1F247, 'M', '〔勝〕'), + (0x1F248, 'M', '〔敗〕'), + (0x1F249, 'X'), + (0x1F250, 'M', '得'), + (0x1F251, 'M', '可'), + (0x1F252, 'X'), + (0x1F260, 'V'), + (0x1F266, 'X'), + (0x1F300, 'V'), + (0x1F6D8, 'X'), + (0x1F6DC, 'V'), + (0x1F6ED, 'X'), + (0x1F6F0, 'V'), + (0x1F6FD, 'X'), + (0x1F700, 'V'), + (0x1F777, 'X'), + (0x1F77B, 'V'), + (0x1F7DA, 'X'), + (0x1F7E0, 'V'), + (0x1F7EC, 'X'), + (0x1F7F0, 'V'), + (0x1F7F1, 'X'), + (0x1F800, 'V'), + (0x1F80C, 'X'), + (0x1F810, 'V'), + (0x1F848, 'X'), + (0x1F850, 'V'), + (0x1F85A, 'X'), + (0x1F860, 'V'), + (0x1F888, 'X'), + (0x1F890, 'V'), + (0x1F8AE, 'X'), + (0x1F8B0, 'V'), + (0x1F8B2, 'X'), + (0x1F900, 'V'), + (0x1FA54, 'X'), + (0x1FA60, 'V'), + (0x1FA6E, 'X'), + (0x1FA70, 'V'), + (0x1FA7D, 'X'), + (0x1FA80, 'V'), + (0x1FA89, 'X'), + ] + +def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FA90, 'V'), + (0x1FABE, 'X'), + (0x1FABF, 'V'), + (0x1FAC6, 'X'), + (0x1FACE, 'V'), + (0x1FADC, 'X'), + (0x1FAE0, 'V'), + (0x1FAE9, 'X'), + (0x1FAF0, 'V'), + (0x1FAF9, 'X'), + (0x1FB00, 'V'), + (0x1FB93, 'X'), + (0x1FB94, 'V'), + (0x1FBCB, 'X'), + (0x1FBF0, 'M', '0'), + (0x1FBF1, 'M', '1'), + (0x1FBF2, 'M', '2'), + (0x1FBF3, 'M', '3'), + (0x1FBF4, 'M', '4'), + (0x1FBF5, 'M', '5'), + (0x1FBF6, 'M', '6'), + (0x1FBF7, 'M', '7'), + (0x1FBF8, 'M', '8'), + (0x1FBF9, 'M', '9'), + (0x1FBFA, 'X'), + (0x20000, 'V'), + (0x2A6E0, 'X'), + (0x2A700, 'V'), + (0x2B73A, 'X'), + (0x2B740, 'V'), + (0x2B81E, 'X'), + (0x2B820, 'V'), + (0x2CEA2, 'X'), + (0x2CEB0, 'V'), + (0x2EBE1, 'X'), + (0x2EBF0, 'V'), + (0x2EE5E, 'X'), + (0x2F800, 'M', '丽'), + (0x2F801, 'M', '丸'), + (0x2F802, 'M', '乁'), + (0x2F803, 'M', '𠄢'), + (0x2F804, 'M', '你'), + (0x2F805, 'M', '侮'), + (0x2F806, 'M', '侻'), + (0x2F807, 'M', '倂'), + (0x2F808, 'M', '偺'), + (0x2F809, 'M', '備'), + (0x2F80A, 'M', '僧'), + (0x2F80B, 'M', '像'), + (0x2F80C, 'M', '㒞'), + (0x2F80D, 'M', '𠘺'), + (0x2F80E, 'M', '免'), + (0x2F80F, 'M', '兔'), + (0x2F810, 'M', '兤'), + (0x2F811, 'M', '具'), + (0x2F812, 'M', '𠔜'), + (0x2F813, 'M', '㒹'), + (0x2F814, 'M', '內'), + (0x2F815, 'M', '再'), + (0x2F816, 'M', '𠕋'), + (0x2F817, 'M', '冗'), + (0x2F818, 'M', '冤'), + (0x2F819, 'M', '仌'), + (0x2F81A, 'M', '冬'), + (0x2F81B, 'M', '况'), + (0x2F81C, 'M', '𩇟'), + (0x2F81D, 'M', '凵'), + (0x2F81E, 'M', '刃'), + (0x2F81F, 'M', '㓟'), + (0x2F820, 'M', '刻'), + (0x2F821, 'M', '剆'), + (0x2F822, 'M', '割'), + (0x2F823, 'M', '剷'), + (0x2F824, 'M', '㔕'), + (0x2F825, 'M', '勇'), + (0x2F826, 'M', '勉'), + (0x2F827, 'M', '勤'), + (0x2F828, 'M', '勺'), + (0x2F829, 'M', '包'), + (0x2F82A, 'M', '匆'), + (0x2F82B, 'M', '北'), + (0x2F82C, 'M', '卉'), + (0x2F82D, 'M', '卑'), + (0x2F82E, 'M', '博'), + (0x2F82F, 'M', '即'), + (0x2F830, 'M', '卽'), + (0x2F831, 'M', '卿'), + (0x2F834, 'M', '𠨬'), + (0x2F835, 'M', '灰'), + (0x2F836, 'M', '及'), + (0x2F837, 'M', '叟'), + (0x2F838, 'M', '𠭣'), + (0x2F839, 'M', '叫'), + (0x2F83A, 'M', '叱'), + (0x2F83B, 'M', '吆'), + (0x2F83C, 'M', '咞'), + (0x2F83D, 'M', '吸'), + (0x2F83E, 'M', '呈'), + (0x2F83F, 'M', '周'), + (0x2F840, 'M', '咢'), + ] + +def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F841, 'M', '哶'), + (0x2F842, 'M', '唐'), + (0x2F843, 'M', '啓'), + (0x2F844, 'M', '啣'), + (0x2F845, 'M', '善'), + (0x2F847, 'M', '喙'), + (0x2F848, 'M', '喫'), + (0x2F849, 'M', '喳'), + (0x2F84A, 'M', '嗂'), + (0x2F84B, 'M', '圖'), + (0x2F84C, 'M', '嘆'), + (0x2F84D, 'M', '圗'), + (0x2F84E, 'M', '噑'), + (0x2F84F, 'M', '噴'), + (0x2F850, 'M', '切'), + (0x2F851, 'M', '壮'), + (0x2F852, 'M', '城'), + (0x2F853, 'M', '埴'), + (0x2F854, 'M', '堍'), + (0x2F855, 'M', '型'), + (0x2F856, 'M', '堲'), + (0x2F857, 'M', '報'), + (0x2F858, 'M', '墬'), + (0x2F859, 'M', '𡓤'), + (0x2F85A, 'M', '売'), + (0x2F85B, 'M', '壷'), + (0x2F85C, 'M', '夆'), + (0x2F85D, 'M', '多'), + (0x2F85E, 'M', '夢'), + (0x2F85F, 'M', '奢'), + (0x2F860, 'M', '𡚨'), + (0x2F861, 'M', '𡛪'), + (0x2F862, 'M', '姬'), + (0x2F863, 'M', '娛'), + (0x2F864, 'M', '娧'), + (0x2F865, 'M', '姘'), + (0x2F866, 'M', '婦'), + (0x2F867, 'M', '㛮'), + (0x2F868, 'X'), + (0x2F869, 'M', '嬈'), + (0x2F86A, 'M', '嬾'), + (0x2F86C, 'M', '𡧈'), + (0x2F86D, 'M', '寃'), + (0x2F86E, 'M', '寘'), + (0x2F86F, 'M', '寧'), + (0x2F870, 'M', '寳'), + (0x2F871, 'M', '𡬘'), + (0x2F872, 'M', '寿'), + (0x2F873, 'M', '将'), + (0x2F874, 'X'), + (0x2F875, 'M', '尢'), + (0x2F876, 'M', '㞁'), + (0x2F877, 'M', '屠'), + (0x2F878, 'M', '屮'), + (0x2F879, 'M', '峀'), + (0x2F87A, 'M', '岍'), + (0x2F87B, 'M', '𡷤'), + (0x2F87C, 'M', '嵃'), + (0x2F87D, 'M', '𡷦'), + (0x2F87E, 'M', '嵮'), + (0x2F87F, 'M', '嵫'), + (0x2F880, 'M', '嵼'), + (0x2F881, 'M', '巡'), + (0x2F882, 'M', '巢'), + (0x2F883, 'M', '㠯'), + (0x2F884, 'M', '巽'), + (0x2F885, 'M', '帨'), + (0x2F886, 'M', '帽'), + (0x2F887, 'M', '幩'), + (0x2F888, 'M', '㡢'), + (0x2F889, 'M', '𢆃'), + (0x2F88A, 'M', '㡼'), + (0x2F88B, 'M', '庰'), + (0x2F88C, 'M', '庳'), + (0x2F88D, 'M', '庶'), + (0x2F88E, 'M', '廊'), + (0x2F88F, 'M', '𪎒'), + (0x2F890, 'M', '廾'), + (0x2F891, 'M', '𢌱'), + (0x2F893, 'M', '舁'), + (0x2F894, 'M', '弢'), + (0x2F896, 'M', '㣇'), + (0x2F897, 'M', '𣊸'), + (0x2F898, 'M', '𦇚'), + (0x2F899, 'M', '形'), + (0x2F89A, 'M', '彫'), + (0x2F89B, 'M', '㣣'), + (0x2F89C, 'M', '徚'), + (0x2F89D, 'M', '忍'), + (0x2F89E, 'M', '志'), + (0x2F89F, 'M', '忹'), + (0x2F8A0, 'M', '悁'), + (0x2F8A1, 'M', '㤺'), + (0x2F8A2, 'M', '㤜'), + (0x2F8A3, 'M', '悔'), + (0x2F8A4, 'M', '𢛔'), + (0x2F8A5, 'M', '惇'), + (0x2F8A6, 'M', '慈'), + (0x2F8A7, 'M', '慌'), + (0x2F8A8, 'M', '慎'), + ] + +def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F8A9, 'M', '慌'), + (0x2F8AA, 'M', '慺'), + (0x2F8AB, 'M', '憎'), + (0x2F8AC, 'M', '憲'), + (0x2F8AD, 'M', '憤'), + (0x2F8AE, 'M', '憯'), + (0x2F8AF, 'M', '懞'), + (0x2F8B0, 'M', '懲'), + (0x2F8B1, 'M', '懶'), + (0x2F8B2, 'M', '成'), + (0x2F8B3, 'M', '戛'), + (0x2F8B4, 'M', '扝'), + (0x2F8B5, 'M', '抱'), + (0x2F8B6, 'M', '拔'), + (0x2F8B7, 'M', '捐'), + (0x2F8B8, 'M', '𢬌'), + (0x2F8B9, 'M', '挽'), + (0x2F8BA, 'M', '拼'), + (0x2F8BB, 'M', '捨'), + (0x2F8BC, 'M', '掃'), + (0x2F8BD, 'M', '揤'), + (0x2F8BE, 'M', '𢯱'), + (0x2F8BF, 'M', '搢'), + (0x2F8C0, 'M', '揅'), + (0x2F8C1, 'M', '掩'), + (0x2F8C2, 'M', '㨮'), + (0x2F8C3, 'M', '摩'), + (0x2F8C4, 'M', '摾'), + (0x2F8C5, 'M', '撝'), + (0x2F8C6, 'M', '摷'), + (0x2F8C7, 'M', '㩬'), + (0x2F8C8, 'M', '敏'), + (0x2F8C9, 'M', '敬'), + (0x2F8CA, 'M', '𣀊'), + (0x2F8CB, 'M', '旣'), + (0x2F8CC, 'M', '書'), + (0x2F8CD, 'M', '晉'), + (0x2F8CE, 'M', '㬙'), + (0x2F8CF, 'M', '暑'), + (0x2F8D0, 'M', '㬈'), + (0x2F8D1, 'M', '㫤'), + (0x2F8D2, 'M', '冒'), + (0x2F8D3, 'M', '冕'), + (0x2F8D4, 'M', '最'), + (0x2F8D5, 'M', '暜'), + (0x2F8D6, 'M', '肭'), + (0x2F8D7, 'M', '䏙'), + (0x2F8D8, 'M', '朗'), + (0x2F8D9, 'M', '望'), + (0x2F8DA, 'M', '朡'), + (0x2F8DB, 'M', '杞'), + (0x2F8DC, 'M', '杓'), + (0x2F8DD, 'M', '𣏃'), + (0x2F8DE, 'M', '㭉'), + (0x2F8DF, 'M', '柺'), + (0x2F8E0, 'M', '枅'), + (0x2F8E1, 'M', '桒'), + (0x2F8E2, 'M', '梅'), + (0x2F8E3, 'M', '𣑭'), + (0x2F8E4, 'M', '梎'), + (0x2F8E5, 'M', '栟'), + (0x2F8E6, 'M', '椔'), + (0x2F8E7, 'M', '㮝'), + (0x2F8E8, 'M', '楂'), + (0x2F8E9, 'M', '榣'), + (0x2F8EA, 'M', '槪'), + (0x2F8EB, 'M', '檨'), + (0x2F8EC, 'M', '𣚣'), + (0x2F8ED, 'M', '櫛'), + (0x2F8EE, 'M', '㰘'), + (0x2F8EF, 'M', '次'), + (0x2F8F0, 'M', '𣢧'), + (0x2F8F1, 'M', '歔'), + (0x2F8F2, 'M', '㱎'), + (0x2F8F3, 'M', '歲'), + (0x2F8F4, 'M', '殟'), + (0x2F8F5, 'M', '殺'), + (0x2F8F6, 'M', '殻'), + (0x2F8F7, 'M', '𣪍'), + (0x2F8F8, 'M', '𡴋'), + (0x2F8F9, 'M', '𣫺'), + (0x2F8FA, 'M', '汎'), + (0x2F8FB, 'M', '𣲼'), + (0x2F8FC, 'M', '沿'), + (0x2F8FD, 'M', '泍'), + (0x2F8FE, 'M', '汧'), + (0x2F8FF, 'M', '洖'), + (0x2F900, 'M', '派'), + (0x2F901, 'M', '海'), + (0x2F902, 'M', '流'), + (0x2F903, 'M', '浩'), + (0x2F904, 'M', '浸'), + (0x2F905, 'M', '涅'), + (0x2F906, 'M', '𣴞'), + (0x2F907, 'M', '洴'), + (0x2F908, 'M', '港'), + (0x2F909, 'M', '湮'), + (0x2F90A, 'M', '㴳'), + (0x2F90B, 'M', '滋'), + (0x2F90C, 'M', '滇'), + ] + +def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F90D, 'M', '𣻑'), + (0x2F90E, 'M', '淹'), + (0x2F90F, 'M', '潮'), + (0x2F910, 'M', '𣽞'), + (0x2F911, 'M', '𣾎'), + (0x2F912, 'M', '濆'), + (0x2F913, 'M', '瀹'), + (0x2F914, 'M', '瀞'), + (0x2F915, 'M', '瀛'), + (0x2F916, 'M', '㶖'), + (0x2F917, 'M', '灊'), + (0x2F918, 'M', '災'), + (0x2F919, 'M', '灷'), + (0x2F91A, 'M', '炭'), + (0x2F91B, 'M', '𠔥'), + (0x2F91C, 'M', '煅'), + (0x2F91D, 'M', '𤉣'), + (0x2F91E, 'M', '熜'), + (0x2F91F, 'X'), + (0x2F920, 'M', '爨'), + (0x2F921, 'M', '爵'), + (0x2F922, 'M', '牐'), + (0x2F923, 'M', '𤘈'), + (0x2F924, 'M', '犀'), + (0x2F925, 'M', '犕'), + (0x2F926, 'M', '𤜵'), + (0x2F927, 'M', '𤠔'), + (0x2F928, 'M', '獺'), + (0x2F929, 'M', '王'), + (0x2F92A, 'M', '㺬'), + (0x2F92B, 'M', '玥'), + (0x2F92C, 'M', '㺸'), + (0x2F92E, 'M', '瑇'), + (0x2F92F, 'M', '瑜'), + (0x2F930, 'M', '瑱'), + (0x2F931, 'M', '璅'), + (0x2F932, 'M', '瓊'), + (0x2F933, 'M', '㼛'), + (0x2F934, 'M', '甤'), + (0x2F935, 'M', '𤰶'), + (0x2F936, 'M', '甾'), + (0x2F937, 'M', '𤲒'), + (0x2F938, 'M', '異'), + (0x2F939, 'M', '𢆟'), + (0x2F93A, 'M', '瘐'), + (0x2F93B, 'M', '𤾡'), + (0x2F93C, 'M', '𤾸'), + (0x2F93D, 'M', '𥁄'), + (0x2F93E, 'M', '㿼'), + (0x2F93F, 'M', '䀈'), + (0x2F940, 'M', '直'), + (0x2F941, 'M', '𥃳'), + (0x2F942, 'M', '𥃲'), + (0x2F943, 'M', '𥄙'), + (0x2F944, 'M', '𥄳'), + (0x2F945, 'M', '眞'), + (0x2F946, 'M', '真'), + (0x2F948, 'M', '睊'), + (0x2F949, 'M', '䀹'), + (0x2F94A, 'M', '瞋'), + (0x2F94B, 'M', '䁆'), + (0x2F94C, 'M', '䂖'), + (0x2F94D, 'M', '𥐝'), + (0x2F94E, 'M', '硎'), + (0x2F94F, 'M', '碌'), + (0x2F950, 'M', '磌'), + (0x2F951, 'M', '䃣'), + (0x2F952, 'M', '𥘦'), + (0x2F953, 'M', '祖'), + (0x2F954, 'M', '𥚚'), + (0x2F955, 'M', '𥛅'), + (0x2F956, 'M', '福'), + (0x2F957, 'M', '秫'), + (0x2F958, 'M', '䄯'), + (0x2F959, 'M', '穀'), + (0x2F95A, 'M', '穊'), + (0x2F95B, 'M', '穏'), + (0x2F95C, 'M', '𥥼'), + (0x2F95D, 'M', '𥪧'), + (0x2F95F, 'X'), + (0x2F960, 'M', '䈂'), + (0x2F961, 'M', '𥮫'), + (0x2F962, 'M', '篆'), + (0x2F963, 'M', '築'), + (0x2F964, 'M', '䈧'), + (0x2F965, 'M', '𥲀'), + (0x2F966, 'M', '糒'), + (0x2F967, 'M', '䊠'), + (0x2F968, 'M', '糨'), + (0x2F969, 'M', '糣'), + (0x2F96A, 'M', '紀'), + (0x2F96B, 'M', '𥾆'), + (0x2F96C, 'M', '絣'), + (0x2F96D, 'M', '䌁'), + (0x2F96E, 'M', '緇'), + (0x2F96F, 'M', '縂'), + (0x2F970, 'M', '繅'), + (0x2F971, 'M', '䌴'), + (0x2F972, 'M', '𦈨'), + (0x2F973, 'M', '𦉇'), + ] + +def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F974, 'M', '䍙'), + (0x2F975, 'M', '𦋙'), + (0x2F976, 'M', '罺'), + (0x2F977, 'M', '𦌾'), + (0x2F978, 'M', '羕'), + (0x2F979, 'M', '翺'), + (0x2F97A, 'M', '者'), + (0x2F97B, 'M', '𦓚'), + (0x2F97C, 'M', '𦔣'), + (0x2F97D, 'M', '聠'), + (0x2F97E, 'M', '𦖨'), + (0x2F97F, 'M', '聰'), + (0x2F980, 'M', '𣍟'), + (0x2F981, 'M', '䏕'), + (0x2F982, 'M', '育'), + (0x2F983, 'M', '脃'), + (0x2F984, 'M', '䐋'), + (0x2F985, 'M', '脾'), + (0x2F986, 'M', '媵'), + (0x2F987, 'M', '𦞧'), + (0x2F988, 'M', '𦞵'), + (0x2F989, 'M', '𣎓'), + (0x2F98A, 'M', '𣎜'), + (0x2F98B, 'M', '舁'), + (0x2F98C, 'M', '舄'), + (0x2F98D, 'M', '辞'), + (0x2F98E, 'M', '䑫'), + (0x2F98F, 'M', '芑'), + (0x2F990, 'M', '芋'), + (0x2F991, 'M', '芝'), + (0x2F992, 'M', '劳'), + (0x2F993, 'M', '花'), + (0x2F994, 'M', '芳'), + (0x2F995, 'M', '芽'), + (0x2F996, 'M', '苦'), + (0x2F997, 'M', '𦬼'), + (0x2F998, 'M', '若'), + (0x2F999, 'M', '茝'), + (0x2F99A, 'M', '荣'), + (0x2F99B, 'M', '莭'), + (0x2F99C, 'M', '茣'), + (0x2F99D, 'M', '莽'), + (0x2F99E, 'M', '菧'), + (0x2F99F, 'M', '著'), + (0x2F9A0, 'M', '荓'), + (0x2F9A1, 'M', '菊'), + (0x2F9A2, 'M', '菌'), + (0x2F9A3, 'M', '菜'), + (0x2F9A4, 'M', '𦰶'), + (0x2F9A5, 'M', '𦵫'), + (0x2F9A6, 'M', '𦳕'), + (0x2F9A7, 'M', '䔫'), + (0x2F9A8, 'M', '蓱'), + (0x2F9A9, 'M', '蓳'), + (0x2F9AA, 'M', '蔖'), + (0x2F9AB, 'M', '𧏊'), + (0x2F9AC, 'M', '蕤'), + (0x2F9AD, 'M', '𦼬'), + (0x2F9AE, 'M', '䕝'), + (0x2F9AF, 'M', '䕡'), + (0x2F9B0, 'M', '𦾱'), + (0x2F9B1, 'M', '𧃒'), + (0x2F9B2, 'M', '䕫'), + (0x2F9B3, 'M', '虐'), + (0x2F9B4, 'M', '虜'), + (0x2F9B5, 'M', '虧'), + (0x2F9B6, 'M', '虩'), + (0x2F9B7, 'M', '蚩'), + (0x2F9B8, 'M', '蚈'), + (0x2F9B9, 'M', '蜎'), + (0x2F9BA, 'M', '蛢'), + (0x2F9BB, 'M', '蝹'), + (0x2F9BC, 'M', '蜨'), + (0x2F9BD, 'M', '蝫'), + (0x2F9BE, 'M', '螆'), + (0x2F9BF, 'X'), + (0x2F9C0, 'M', '蟡'), + (0x2F9C1, 'M', '蠁'), + (0x2F9C2, 'M', '䗹'), + (0x2F9C3, 'M', '衠'), + (0x2F9C4, 'M', '衣'), + (0x2F9C5, 'M', '𧙧'), + (0x2F9C6, 'M', '裗'), + (0x2F9C7, 'M', '裞'), + (0x2F9C8, 'M', '䘵'), + (0x2F9C9, 'M', '裺'), + (0x2F9CA, 'M', '㒻'), + (0x2F9CB, 'M', '𧢮'), + (0x2F9CC, 'M', '𧥦'), + (0x2F9CD, 'M', '䚾'), + (0x2F9CE, 'M', '䛇'), + (0x2F9CF, 'M', '誠'), + (0x2F9D0, 'M', '諭'), + (0x2F9D1, 'M', '變'), + (0x2F9D2, 'M', '豕'), + (0x2F9D3, 'M', '𧲨'), + (0x2F9D4, 'M', '貫'), + (0x2F9D5, 'M', '賁'), + (0x2F9D6, 'M', '贛'), + (0x2F9D7, 'M', '起'), + ] + +def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F9D8, 'M', '𧼯'), + (0x2F9D9, 'M', '𠠄'), + (0x2F9DA, 'M', '跋'), + (0x2F9DB, 'M', '趼'), + (0x2F9DC, 'M', '跰'), + (0x2F9DD, 'M', '𠣞'), + (0x2F9DE, 'M', '軔'), + (0x2F9DF, 'M', '輸'), + (0x2F9E0, 'M', '𨗒'), + (0x2F9E1, 'M', '𨗭'), + (0x2F9E2, 'M', '邔'), + (0x2F9E3, 'M', '郱'), + (0x2F9E4, 'M', '鄑'), + (0x2F9E5, 'M', '𨜮'), + (0x2F9E6, 'M', '鄛'), + (0x2F9E7, 'M', '鈸'), + (0x2F9E8, 'M', '鋗'), + (0x2F9E9, 'M', '鋘'), + (0x2F9EA, 'M', '鉼'), + (0x2F9EB, 'M', '鏹'), + (0x2F9EC, 'M', '鐕'), + (0x2F9ED, 'M', '𨯺'), + (0x2F9EE, 'M', '開'), + (0x2F9EF, 'M', '䦕'), + (0x2F9F0, 'M', '閷'), + (0x2F9F1, 'M', '𨵷'), + (0x2F9F2, 'M', '䧦'), + (0x2F9F3, 'M', '雃'), + (0x2F9F4, 'M', '嶲'), + (0x2F9F5, 'M', '霣'), + (0x2F9F6, 'M', '𩅅'), + (0x2F9F7, 'M', '𩈚'), + (0x2F9F8, 'M', '䩮'), + (0x2F9F9, 'M', '䩶'), + (0x2F9FA, 'M', '韠'), + (0x2F9FB, 'M', '𩐊'), + (0x2F9FC, 'M', '䪲'), + (0x2F9FD, 'M', '𩒖'), + (0x2F9FE, 'M', '頋'), + (0x2FA00, 'M', '頩'), + (0x2FA01, 'M', '𩖶'), + (0x2FA02, 'M', '飢'), + (0x2FA03, 'M', '䬳'), + (0x2FA04, 'M', '餩'), + (0x2FA05, 'M', '馧'), + (0x2FA06, 'M', '駂'), + (0x2FA07, 'M', '駾'), + (0x2FA08, 'M', '䯎'), + (0x2FA09, 'M', '𩬰'), + (0x2FA0A, 'M', '鬒'), + (0x2FA0B, 'M', '鱀'), + (0x2FA0C, 'M', '鳽'), + (0x2FA0D, 'M', '䳎'), + (0x2FA0E, 'M', '䳭'), + (0x2FA0F, 'M', '鵧'), + (0x2FA10, 'M', '𪃎'), + (0x2FA11, 'M', '䳸'), + (0x2FA12, 'M', '𪄅'), + (0x2FA13, 'M', '𪈎'), + (0x2FA14, 'M', '𪊑'), + (0x2FA15, 'M', '麻'), + (0x2FA16, 'M', '䵖'), + (0x2FA17, 'M', '黹'), + (0x2FA18, 'M', '黾'), + (0x2FA19, 'M', '鼅'), + (0x2FA1A, 'M', '鼏'), + (0x2FA1B, 'M', '鼖'), + (0x2FA1C, 'M', '鼻'), + (0x2FA1D, 'M', '𪘀'), + (0x2FA1E, 'X'), + (0x30000, 'V'), + (0x3134B, 'X'), + (0x31350, 'V'), + (0x323B0, 'X'), + (0xE0100, 'I'), + (0xE01F0, 'X'), + ] + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() + + _seg_78() + + _seg_79() + + _seg_80() + + _seg_81() +) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..910537b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/LICENSE.txt @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2008-2021, The joblib developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/METADATA new file mode 100644 index 0000000..0ca5d79 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/METADATA @@ -0,0 +1,165 @@ +Metadata-Version: 2.1 +Name: joblib +Version: 1.4.0 +Summary: Lightweight pipelining with Python functions +Author-email: Gael Varoquaux +License: BSD 3-Clause +Project-URL: Homepage, https://joblib.readthedocs.io +Project-URL: Source, https://github.com/joblib/joblib +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: Intended Audience :: Education +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Utilities +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt + +|PyPi| |Azure| |ReadTheDocs| |Codecov| + +.. |PyPi| image:: https://badge.fury.io/py/joblib.svg + :target: https://badge.fury.io/py/joblib + :alt: Joblib version + +.. |Azure| image:: https://dev.azure.com/joblib/joblib/_apis/build/status/joblib.joblib?branchName=master + :target: https://dev.azure.com/joblib/joblib/_build?definitionId=3&_a=summary&branchFilter=40 + :alt: Azure CI status + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/joblib/badge/?version=latest + :target: https://joblib.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. |Codecov| image:: https://codecov.io/gh/joblib/joblib/branch/master/graph/badge.svg + :target: https://codecov.io/gh/joblib/joblib + :alt: Codecov coverage + + +The homepage of joblib with user documentation is located on: + +https://joblib.readthedocs.io + +Getting the latest code +======================= + +To get the latest code using git, simply type:: + + git clone https://github.com/joblib/joblib.git + +If you don't have git installed, you can download a zip +of the latest code: https://github.com/joblib/joblib/archive/refs/heads/master.zip + +Installing +========== + +You can use `pip` to install joblib:: + + pip install joblib + +from any directory or:: + + python setup.py install + +from the source directory. + +Dependencies +============ + +- Joblib has no mandatory dependencies besides Python (supported versions are + 3.8+). +- Joblib has an optional dependency on Numpy (at least version 1.6.1) for array + manipulation. +- Joblib includes its own vendored copy of + `loky `_ for process management. +- Joblib can efficiently dump and load numpy arrays but does not require numpy + to be installed. +- Joblib has an optional dependency on + `python-lz4 `_ as a faster alternative to + zlib and gzip for compressed serialization. +- Joblib has an optional dependency on psutil to mitigate memory leaks in + parallel worker processes. +- Some examples require external dependencies such as pandas. See the + instructions in the `Building the docs`_ section for details. + +Workflow to contribute +====================== + +To contribute to joblib, first create an account on `github +`_. Once this is done, fork the `joblib repository +`_ to have your own repository, +clone it using 'git clone' on the computers where you want to work. Make +your changes in your clone, push them to your github account, test them +on several computers, and when you are happy with them, send a pull +request to the main repository. + +Running the test suite +====================== + +To run the test suite, you need the pytest (version >= 3) and coverage modules. +Run the test suite using:: + + pytest joblib + +from the root of the project. + +Building the docs +================= + +To build the docs you need to have sphinx (>=1.4) and some dependencies +installed:: + + pip install -U -r .readthedocs-requirements.txt + +The docs can then be built with the following command:: + + make doc + +The html docs are located in the ``doc/_build/html`` directory. + + +Making a source tarball +======================= + +To create a source tarball, eg for packaging or distributing, run the +following command:: + + python setup.py sdist + +The tarball will be created in the `dist` directory. This command will +compile the docs, and the resulting tarball can be installed with +no extra dependencies than the Python standard library. You will need +setuptool and sphinx. + +Making a release and uploading it to PyPI +========================================= + +This command is only run by project manager, to make a release, and +upload in to PyPI:: + + python setup.py sdist bdist_wheel + twine upload dist/* + + +Note that the documentation should automatically get updated at each git +push. If that is not the case, try building th doc locally and resolve +any doc build error (in particular when running the examples). + +Updating the changelog +====================== + +Changes are listed in the CHANGES.rst file. They must be manually updated +but, the following git command may be used to generate the lines:: + + git log --abbrev-commit --date=short --no-merges --sparse + diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/RECORD new file mode 100644 index 0000000..854e9ed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/RECORD @@ -0,0 +1,217 @@ +joblib-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +joblib-1.4.0.dist-info/LICENSE.txt,sha256=QmEpEcGHLF5LQ_auDo7llGfNNQMyJBz3LOkGQCZPrmo,1527 +joblib-1.4.0.dist-info/METADATA,sha256=RvoJhBm8jAYOnpWDNhwlybOVwN5nFst_OYytoS6_xRQ,5370 +joblib-1.4.0.dist-info/RECORD,, +joblib-1.4.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +joblib-1.4.0.dist-info/top_level.txt,sha256=P0LsoZ45gBL7ckL4lqQt7tdbrHD4xlVYhffmhHeeT_U,7 +joblib/__init__.py,sha256=ytsTJjbnDLtKYZ1il4QVYU8ReyTdKgKqd3zO_2FTv7Y,5132 +joblib/__pycache__/__init__.cpython-310.pyc,, +joblib/__pycache__/_cloudpickle_wrapper.cpython-310.pyc,, +joblib/__pycache__/_dask.cpython-310.pyc,, +joblib/__pycache__/_memmapping_reducer.cpython-310.pyc,, +joblib/__pycache__/_multiprocessing_helpers.cpython-310.pyc,, +joblib/__pycache__/_parallel_backends.cpython-310.pyc,, +joblib/__pycache__/_store_backends.cpython-310.pyc,, +joblib/__pycache__/_utils.cpython-310.pyc,, +joblib/__pycache__/backports.cpython-310.pyc,, +joblib/__pycache__/compressor.cpython-310.pyc,, +joblib/__pycache__/disk.cpython-310.pyc,, +joblib/__pycache__/executor.cpython-310.pyc,, +joblib/__pycache__/func_inspect.cpython-310.pyc,, +joblib/__pycache__/hashing.cpython-310.pyc,, +joblib/__pycache__/logger.cpython-310.pyc,, +joblib/__pycache__/memory.cpython-310.pyc,, +joblib/__pycache__/numpy_pickle.cpython-310.pyc,, +joblib/__pycache__/numpy_pickle_compat.cpython-310.pyc,, +joblib/__pycache__/numpy_pickle_utils.cpython-310.pyc,, +joblib/__pycache__/parallel.cpython-310.pyc,, +joblib/__pycache__/pool.cpython-310.pyc,, +joblib/__pycache__/testing.cpython-310.pyc,, +joblib/_cloudpickle_wrapper.py,sha256=-PBMUqgZCfd5EMaKn5FCQhWKiAJfbq4i2o6Z6KZ1ynE,417 +joblib/_dask.py,sha256=IhFjN6oMQVIcLC7bLHnA7KgO7nnEp7p6P4JH12934J4,13313 +joblib/_memmapping_reducer.py,sha256=4xclMWTgI5l-C6OFyt6FvamH-ToJu3DmopAXwt7a9F4,28092 +joblib/_multiprocessing_helpers.py,sha256=t7wIXfrLfzqFXjOeOYs4JP45tptxmYm5_yE8ylIRbR8,1925 +joblib/_parallel_backends.py,sha256=82qX4mJ5eHz4i0e5gbdHgvzST3P6LJ59hpfee0IyZCQ,25489 +joblib/_store_backends.py,sha256=PaoeHo4Mr7idPI3MvX766OqBQ_y_WkG_oZbE4MjbdKw,16683 +joblib/_utils.py,sha256=BBUs4ZHpDRxpgTsdrZSyBb39MqacM6JauI3dTRmnHm4,2076 +joblib/backports.py,sha256=aGh0cjs_xMhO2ym7RprpDjCBudiPtDhgD1vf5YMbMaI,5361 +joblib/compressor.py,sha256=WwRzsQhdQp0lbQvvEbAcaevCOCM2_Qyv_HJ4PVHtobQ,19768 +joblib/disk.py,sha256=PxUC63dBG2O1GriL1SLskHUjz7XzR-y6rqKEJHEY0jA,4389 +joblib/executor.py,sha256=PiaOwENhecRVnOdvHZLwGsr-RMKfk_F_Noy8y7nntJY,5136 +joblib/externals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/externals/__pycache__/__init__.cpython-310.pyc,, +joblib/externals/cloudpickle/__init__.py,sha256=vb2JCOn1EpLUdVyPe1ESyhLymcvh-Rk3ISHJ-52aDLw,308 +joblib/externals/cloudpickle/__pycache__/__init__.cpython-310.pyc,, +joblib/externals/cloudpickle/__pycache__/cloudpickle.cpython-310.pyc,, +joblib/externals/cloudpickle/__pycache__/cloudpickle_fast.cpython-310.pyc,, +joblib/externals/cloudpickle/cloudpickle.py,sha256=APCGMuIfVpWcelGsLlo2zRmwKRloaoiznQEOAoEWH9Y,55283 +joblib/externals/cloudpickle/cloudpickle_fast.py,sha256=1GqUD4nLKsv0vv9ty2La3eVLyeWNrPFlhUCN-aNI-30,322 +joblib/externals/loky/__init__.py,sha256=T20hgxG9YPps-AhsElBMzIJD73q0h3x0Hys49alTvq8,1104 +joblib/externals/loky/__pycache__/__init__.cpython-310.pyc,, +joblib/externals/loky/__pycache__/_base.cpython-310.pyc,, +joblib/externals/loky/__pycache__/cloudpickle_wrapper.cpython-310.pyc,, +joblib/externals/loky/__pycache__/initializers.cpython-310.pyc,, +joblib/externals/loky/__pycache__/process_executor.cpython-310.pyc,, +joblib/externals/loky/__pycache__/reusable_executor.cpython-310.pyc,, +joblib/externals/loky/_base.py,sha256=LsQnEoKWKGhdeqGhMc68Aqwz4MrTnEs20KAYbFiUHzo,1057 +joblib/externals/loky/backend/__init__.py,sha256=Ix9KThV1CYk7-M5OQnJ_A_JrrrWJ-Jowa-HMMeGbp18,312 +joblib/externals/loky/backend/__pycache__/__init__.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/_posix_reduction.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/_win_reduction.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/context.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/fork_exec.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/popen_loky_posix.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/popen_loky_win32.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/process.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/queues.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/reduction.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/resource_tracker.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/spawn.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/synchronize.cpython-310.pyc,, +joblib/externals/loky/backend/__pycache__/utils.cpython-310.pyc,, +joblib/externals/loky/backend/_posix_reduction.py,sha256=xgCSrIaLI0k_MI0XNOBSp5e1ox1WN9idgrWbkWpMUr4,1776 +joblib/externals/loky/backend/_win_reduction.py,sha256=WmNB0NXtyJ_o_WzfPUEGh5dPhXIeI6FkEnFNXUxO2ws,683 +joblib/externals/loky/backend/context.py,sha256=GGBUGp4QDx1qvBuWDvyOSjNWYA79shxgAagsrcxA50E,13654 +joblib/externals/loky/backend/fork_exec.py,sha256=c3I22U_ewtT1T5Xn65SUXHrftspvllrGezGiv5KSRQY,1186 +joblib/externals/loky/backend/popen_loky_posix.py,sha256=ZFFs6H7Xp3CCQDE4oqmd_flWday6EdtlQ34Hkpa2PFQ,5580 +joblib/externals/loky/backend/popen_loky_win32.py,sha256=bYkhRA0w8qUcYFwoezeGwcnlCocEdheWXc6SZ-_rVxo,5325 +joblib/externals/loky/backend/process.py,sha256=4-Y94EoIrg4btsjTNxUBHAHhR96Nrugn_7_PGL6aU50,2018 +joblib/externals/loky/backend/queues.py,sha256=eETFvbPHwKfdoYyOgNQCyKq_Zlm-lzH3fwwpUIh-_4U,7322 +joblib/externals/loky/backend/reduction.py,sha256=CRu922R8xOxog2Snhop7y6fN1fPX9_h110brrICwZUE,7063 +joblib/externals/loky/backend/resource_tracker.py,sha256=421689XAmmxmNXktfkhNHNERIY3LbAcmWAsmRHPNUjg,14498 +joblib/externals/loky/backend/spawn.py,sha256=PVOHs8ou7IPcISb7t_Pp86FnUtPUw_KUAdxmDHOrAaU,8962 +joblib/externals/loky/backend/synchronize.py,sha256=nlDwBoLZB93m_l55qfZM_Ql-4L84PSYimoQqt5TzpDk,11768 +joblib/externals/loky/backend/utils.py,sha256=RVsxqyET4TJdbjc9uUHJmfhlQ2v4Uq-fiT_5b5rfC0s,5757 +joblib/externals/loky/cloudpickle_wrapper.py,sha256=9VfrXIfHiJcoXDqwgaHWbRsWipVA23oRJaxnXzSu7GM,3608 +joblib/externals/loky/initializers.py,sha256=dtKtRsJUmVwiJu0yZ-Ih0m8PvW_MxmouG7mShEcsStc,2567 +joblib/externals/loky/process_executor.py,sha256=eP5eVZFUsYPDhN91cxRHl23uEfqNWDeDQjrdNDB4c3o,51050 +joblib/externals/loky/reusable_executor.py,sha256=XVxLGMhCDij8CU6BDHbYKPxn8lG-rnqXaIfxO2nXGXg,10305 +joblib/func_inspect.py,sha256=Cwei03uwnZKJ9mbGmdCSjVGgq2_1lJNJSkeUn1GTvSo,14204 +joblib/hashing.py,sha256=4X9OOofkfDQr3N8NZKTsMOKWr8IiIV0kjBwqCUJCej4,10535 +joblib/logger.py,sha256=meT-hFPTZukfBRkeyCVCxhssPSr668_R5Nn25S-iXtc,5463 +joblib/memory.py,sha256=SDxiok3TiQOqy7Cd6A6yxrtWCZADWhMIiiWsBe97QRA,45990 +joblib/numpy_pickle.py,sha256=5_L7EbPg94T3iSODEdkikJNj_z2qvpZYaGei9HlMBWU,26886 +joblib/numpy_pickle_compat.py,sha256=U7zVNNF03an_7AgHOxhTxcOSbLJTWcGsnV_xbT3vRdY,8547 +joblib/numpy_pickle_utils.py,sha256=KccSp_MhMFsV41Mw9pSijchtGOCympDaHWH0VpIUeSs,8723 +joblib/parallel.py,sha256=foh2ez7ep8oIS5QInXU6qRSZ4xWWY_AeZjzDVDIOppc,84579 +joblib/pool.py,sha256=7jhFg7_qIuBbJ2URbKvyZtET9AxRKaYrYiBSes1VFxo,14411 +joblib/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/test/__pycache__/__init__.cpython-310.pyc,, +joblib/test/__pycache__/common.cpython-310.pyc,, +joblib/test/__pycache__/test_backports.cpython-310.pyc,, +joblib/test/__pycache__/test_cloudpickle_wrapper.cpython-310.pyc,, +joblib/test/__pycache__/test_config.cpython-310.pyc,, +joblib/test/__pycache__/test_dask.cpython-310.pyc,, +joblib/test/__pycache__/test_disk.cpython-310.pyc,, +joblib/test/__pycache__/test_func_inspect.cpython-310.pyc,, +joblib/test/__pycache__/test_func_inspect_special_encoding.cpython-310.pyc,, +joblib/test/__pycache__/test_hashing.cpython-310.pyc,, +joblib/test/__pycache__/test_init.cpython-310.pyc,, +joblib/test/__pycache__/test_logger.cpython-310.pyc,, +joblib/test/__pycache__/test_memmapping.cpython-310.pyc,, +joblib/test/__pycache__/test_memory.cpython-310.pyc,, +joblib/test/__pycache__/test_memory_async.cpython-310.pyc,, +joblib/test/__pycache__/test_missing_multiprocessing.cpython-310.pyc,, +joblib/test/__pycache__/test_module.cpython-310.pyc,, +joblib/test/__pycache__/test_numpy_pickle.cpython-310.pyc,, +joblib/test/__pycache__/test_numpy_pickle_compat.cpython-310.pyc,, +joblib/test/__pycache__/test_numpy_pickle_utils.cpython-310.pyc,, +joblib/test/__pycache__/test_parallel.cpython-310.pyc,, +joblib/test/__pycache__/test_store_backends.cpython-310.pyc,, +joblib/test/__pycache__/test_testing.cpython-310.pyc,, +joblib/test/__pycache__/test_utils.cpython-310.pyc,, +joblib/test/__pycache__/testutils.cpython-310.pyc,, +joblib/test/common.py,sha256=seNB39WwHwu0qfMo6qeV7y0m6wxCcJ1B-Q1MWLvn4Vk,2336 +joblib/test/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/test/data/__pycache__/__init__.cpython-310.pyc,, +joblib/test/data/__pycache__/create_numpy_pickle.cpython-310.pyc,, +joblib/test/data/create_numpy_pickle.py,sha256=BEaSv8dm3a48mvxl_Kf5ASwkkbGhwRplzNotTP4RCC0,3460 +joblib/test/data/joblib_0.10.0_compressed_pickle_py27_np16.gz,sha256=QYRH6Q2DSGVorjCSqWCxjTWCMOJKyew4Nl2qmfQVvQ8,769 +joblib/test/data/joblib_0.10.0_compressed_pickle_py27_np17.gz,sha256=ofTozM_KlPJa50TR8FCwc09mMmO6OO0GQhgUBLNIsXs,757 +joblib/test/data/joblib_0.10.0_compressed_pickle_py33_np18.gz,sha256=2eIVeA-XjOaT5IEQ6tI2UuHG3hwhiRciMmkBmPcIh4g,792 +joblib/test/data/joblib_0.10.0_compressed_pickle_py34_np19.gz,sha256=Gr2z_1tVWDH1H3_wCVHmakknf8KqeHKT8Yz4d1vmUCM,794 +joblib/test/data/joblib_0.10.0_compressed_pickle_py35_np19.gz,sha256=pWw_xuDbOkECqu1KGf1OFU7s2VbzC2v5F5iXhE7TwB4,790 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl,sha256=icRQjj374B-AHk5znxre0T9oWUHokoHIBQ8MqKo8l-U,986 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.bz2,sha256=oYQVIyMiUxyRgWSuBBSOvCWKzToA-kUpcoQWdV4UoV4,997 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.gzip,sha256=Jpv3iGcDgKTv-O4nZsUreIbUK7qnt2cugZ-VMgNeEDQ,798 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.lzma,sha256=c0wu0x8pPv4BcStj7pE61rZpf68FLG_pNzQZ4e82zH8,660 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.xz,sha256=77FG1FDG0GHQav-1bxc4Tn9ky6ubUW_MbE0_iGmz5wc,712 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl,sha256=4GTC7s_cWNVShERn2nvVbspZYJgyK_0man4TEqvdVzU,1068 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.bz2,sha256=6G1vbs_iYmz2kYJ6w4qB1k7D67UnxUMus0S4SWeBtFo,1000 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.gzip,sha256=tlRUWeJS1BXmcwtLNSNK9L0hDHekFl07CqWxTShinmY,831 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.lzma,sha256=CorPwnfv3rR5hjNtJI01-sEBMOnkSxNlRVaWTszMopA,694 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.xz,sha256=Dppj3MffOKsKETeptEtDaxPOv6MA6xnbpK5LzlDQ-oE,752 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl,sha256=HL5Fb1uR9aPLjjhoOPJ2wwM1Qyo1FCZoYYd2HVw0Fos,1068 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.bz2,sha256=Pyr2fqZnwfUxXdyrBr-kRwBYY8HA_Yi7fgSguKy5pUs,1021 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.gzip,sha256=os8NJjQI9FhnlZM-Ay9dX_Uo35gZnoJCgQSIVvcBPfE,831 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.lzma,sha256=Q_0y43qU7_GqAabJ8y3PWVhOisurnCAq3GzuCu04V58,697 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.xz,sha256=BNfmiQfpeLVpdfkwlJK4hJ5Cpgl0vreVyekyc5d_PNM,752 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl,sha256=l7nvLolhBDIdPFznOz3lBHiMOPBPCMi1bXop1tFSCpY,1068 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.bz2,sha256=pqGpuIS-ZU4uP8mkglHs8MaSDiVcPy7l3XHYJSppRgY,1005 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.gzip,sha256=YRFXE6LEb6qK72yPqnXdqQVY8Ts8xKUS9PWQKhLxWvk,833 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.lzma,sha256=Bf7gCUeTuTjCkbcIdyZYz69irblX4SAVQEzxCnMQhNU,701 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.xz,sha256=As8w2LGWwwNmKy3QNdKljK63Yq46gjRf_RJ0lh5_WqA,752 +joblib/test/data/joblib_0.11.0_compressed_pickle_py36_np111.gz,sha256=1WrnXDqDoNEPYOZX1Q5Wr2463b8vVV6fw4Wm5S4bMt4,800 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl,sha256=XmsOFxeC1f1aYdGETclG6yfF9rLoB11DayOAhDMULrw,1068 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.bz2,sha256=vI2yWb50LKL_NgZyd_XkoD5teIg93uI42mWnx9ee-AQ,991 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.gzip,sha256=1WrnXDqDoNEPYOZX1Q5Wr2463b8vVV6fw4Wm5S4bMt4,800 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.lzma,sha256=IWA0JlZG2ur53HgTUDl1m7q79dcVq6b0VOq33gKoJU0,715 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.xz,sha256=3Xh_NbMZdBjYx7ynfJ3Fyke28izSRSSzzNB0z5D4k9Y,752 +joblib/test/data/joblib_0.8.4_compressed_pickle_py27_np17.gz,sha256=Sp-ZT7i6pj5on2gbptszu7RarzJpOmHJ67UKOmCPQMg,659 +joblib/test/data/joblib_0.9.2_compressed_pickle_py27_np16.gz,sha256=NLtDrvo2XIH0KvUUAvhOqMeoXEjGW0IuTk_osu5XiDw,658 +joblib/test/data/joblib_0.9.2_compressed_pickle_py27_np17.gz,sha256=NLtDrvo2XIH0KvUUAvhOqMeoXEjGW0IuTk_osu5XiDw,658 +joblib/test/data/joblib_0.9.2_compressed_pickle_py34_np19.gz,sha256=nzO9iiGkG3KbBdrF3usOho8higkrDj_lmICUzxZyF_Y,673 +joblib/test/data/joblib_0.9.2_compressed_pickle_py35_np19.gz,sha256=nzO9iiGkG3KbBdrF3usOho8higkrDj_lmICUzxZyF_Y,673 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl,sha256=naijdk2xIeKdIa3mfJw0JlmOdtiN6uRM1yOJg6-M73M,670 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_03.npy,sha256=oMRa4qKJhBy-uiRDt-uqOzHAqencxzKUrKVynaAJJAU,236 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl,sha256=LynX8dLOygfxDfFywOgm7wgWOhSxLG7z-oDsU6X83Dw,670 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_03.npy,sha256=oMRa4qKJhBy-uiRDt-uqOzHAqencxzKUrKVynaAJJAU,236 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl,sha256=w9TLxpDTzp5TI6cU6lRvMsAasXEChcQgGE9s30sm_CU,691 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl,sha256=ilOBAOaulLFvKrD32S1NfnpiK-LfzA9rC3O2I7xROuI,691 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl,sha256=WfDVIqKcMzzh1gSAshIfzBoIpdLdZQuG79yYf5kfpOo,691 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz,sha256=8jYfWJsx0oY2J-3LlmEigK5cClnJSW2J2rfeSTZw-Ts,802 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_01.npy.z,sha256=YT9VvT3sEl2uWlOyvH2CkyE9Sok4od9O3kWtgeuUUqE,43 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_02.npy.z,sha256=txA5RDI0PRuiU_UNKY8pGp-zQgQQ9vaVvMi60hOPaVs,43 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_03.npy.z,sha256=d3AwICvU2MpSNjh2aPIsdJeGZLlDjANAF1Soa6uM0Po,37 +joblib/test/test_backports.py,sha256=Y9bhGa6H-K_FgLkDyXaSHzpaWk148Rjn8R9IKCKdy-k,1175 +joblib/test/test_cloudpickle_wrapper.py,sha256=gc05MGe1LJfhCNTv02VdDXg8Z6FeJJ3rFTff1_WmMwc,751 +joblib/test/test_config.py,sha256=EvQKH7n4qXAcPd-BTnfu4duR6b5ag65xjaEjPMDM50w,5284 +joblib/test/test_dask.py,sha256=IIHdXXpH5NenurB6jem9rYisa_rP-usPyoPVaWzBEN0,18418 +joblib/test/test_disk.py,sha256=wJd1o9nLzqEjLqxxkgB9S7-UcKjHPQ8qK5l0czcNp0o,2205 +joblib/test/test_func_inspect.py,sha256=fyR9q1nGHwch5hD1hedut2YeN19hHDyUuDc8mXQuGeY,9488 +joblib/test/test_func_inspect_special_encoding.py,sha256=5xILDjSO-xtjQAMLvMeVD-L7IG4ZURb2gvBiShaDE78,145 +joblib/test/test_hashing.py,sha256=w8_WVgXL1TFueCSriX97GrCAS3mcmu5Ly062pOAOoRI,16054 +joblib/test/test_init.py,sha256=bgNF-9CIJl1MFNA75LBWOaiNtvduVfuvglz_u9Tt8Uc,422 +joblib/test/test_logger.py,sha256=CyA3E8Y74AHZfqJxetNrYfwXSOlLc1Pq1hqt7aJ6PwA,984 +joblib/test/test_memmapping.py,sha256=6kXT4ZMCtPykWhDf66QCcQvsdC9PaJumiCY0jHPwfio,43298 +joblib/test/test_memory.py,sha256=1dWfSPgMgdp2WdnY6lMILRdCbQ4v915DDOC_Q8d3WDE,49419 +joblib/test/test_memory_async.py,sha256=g-3Y09LqMccWR8Kw_yA0-yaduL-5zDBgaGUlgleOGDo,4807 +joblib/test/test_missing_multiprocessing.py,sha256=oeneMgi6iUVIbkfazGvXmOp6fqa7ok9uhj902Qjs_nk,1123 +joblib/test/test_module.py,sha256=HTHQSgnwa-1blkvhL_oVBO5yYdw8IrQTIJCK_QCsMtM,1936 +joblib/test/test_numpy_pickle.py,sha256=6rRMvj-9dgBVucPLY7J0qBIIcY2XOSQGh1owqOiGIrg,42485 +joblib/test/test_numpy_pickle_compat.py,sha256=o4mB8M6mVRX8Hxw-fxOmWPcbSAyswTdEHk64hVv_ZCk,609 +joblib/test/test_numpy_pickle_utils.py,sha256=-lQnhOAIGyd5oyWT2LGQPc3QO_Oxb3VhM_H3QjKm5e0,383 +joblib/test/test_parallel.py,sha256=fyvzDhsGADyjLoTy9Jycdketu1YZ1WyYtQCBIQ_YEso,73523 +joblib/test/test_store_backends.py,sha256=LCCUS9DN7feJniwnOD7x0pMKAQ-5rgCBBctwi5FhjWM,3121 +joblib/test/test_testing.py,sha256=1O5ZTJkdBjVkM-Ss4mComsEbJgrwROloVu8Mqv7Jv-w,2570 +joblib/test/test_utils.py,sha256=L6aBHiZrF6TUFbPGmQXGyNMnoAy5pEhOJoH5CAG_6CU,584 +joblib/test/testutils.py,sha256=6a7zVJm1kg6M-t4CH9tz8A6rMdC6ZY9sU6wBB8C7Zzo,251 +joblib/testing.py,sha256=6i2ctsokVXohLBhc1dXIZc2TiQDRiB-glGKFbfZDIqE,3093 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/WHEEL new file mode 100644 index 0000000..57e3d84 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..ca4af27 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib-1.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +joblib diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/__init__.py new file mode 100644 index 0000000..962a434 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/__init__.py @@ -0,0 +1,148 @@ +"""Joblib is a set of tools to provide **lightweight pipelining in +Python**. In particular: + +1. transparent disk-caching of functions and lazy re-evaluation + (memoize pattern) + +2. easy simple parallel computing + +Joblib is optimized to be **fast** and **robust** on large +data in particular and has specific optimizations for `numpy` arrays. It is +**BSD-licensed**. + + + ==================== =============================================== + **Documentation:** https://joblib.readthedocs.io + + **Download:** https://pypi.python.org/pypi/joblib#downloads + + **Source code:** https://github.com/joblib/joblib + + **Report issues:** https://github.com/joblib/joblib/issues + ==================== =============================================== + + +Vision +-------- + +The vision is to provide tools to easily achieve better performance and +reproducibility when working with long running jobs. + + * **Avoid computing the same thing twice**: code is often rerun again and + again, for instance when prototyping computational-heavy jobs (as in + scientific development), but hand-crafted solutions to alleviate this + issue are error-prone and often lead to unreproducible results. + + * **Persist to disk transparently**: efficiently persisting + arbitrary objects containing large data is hard. Using + joblib's caching mechanism avoids hand-written persistence and + implicitly links the file on disk to the execution context of + the original Python object. As a result, joblib's persistence is + good for resuming an application status or computational job, eg + after a crash. + +Joblib addresses these problems while **leaving your code and your flow +control as unmodified as possible** (no framework, no new paradigms). + +Main features +------------------ + +1) **Transparent and fast disk-caching of output value:** a memoize or + make-like functionality for Python functions that works well for + arbitrary Python objects, including very large numpy arrays. Separate + persistence and flow-execution logic from domain logic or algorithmic + code by writing the operations as a set of steps with well-defined + inputs and outputs: Python functions. Joblib can save their + computation to disk and rerun it only if necessary:: + + >>> from joblib import Memory + >>> cachedir = 'your_cache_dir_goes_here' + >>> mem = Memory(cachedir) + >>> import numpy as np + >>> a = np.vander(np.arange(3)).astype(float) + >>> square = mem.cache(np.square) + >>> b = square(a) # doctest: +ELLIPSIS + ______________________________________________________________________... + [Memory] Calling square... + square(array([[0., 0., 1.], + [1., 1., 1.], + [4., 2., 1.]])) + _________________________________________________...square - ...s, 0.0min + + >>> c = square(a) + >>> # The above call did not trigger an evaluation + +2) **Embarrassingly parallel helper:** to make it easy to write readable + parallel code and debug it quickly:: + + >>> from joblib import Parallel, delayed + >>> from math import sqrt + >>> Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10)) + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + + +3) **Fast compressed Persistence**: a replacement for pickle to work + efficiently on Python objects containing large data ( + *joblib.dump* & *joblib.load* ). + +.. + >>> import shutil ; shutil.rmtree(cachedir) + +""" + +# PEP0440 compatible formatted version, see: +# https://www.python.org/dev/peps/pep-0440/ +# +# Generic release markers: +# X.Y +# X.Y.Z # For bugfix releases +# +# Admissible pre-release markers: +# X.YaN # Alpha release +# X.YbN # Beta release +# X.YrcN # Release Candidate +# X.Y # Final release +# +# Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer. +# 'X.Y.dev0' is the canonical version of 'X.Y.dev' +# +__version__ = '1.4.0' + + +import os + +from .memory import Memory +from .memory import MemorizedResult +from .memory import register_store_backend +from .memory import expires_after + +from .logger import PrintTime +from .logger import Logger + +from .hashing import hash + +from .numpy_pickle import dump +from .numpy_pickle import load + +from .compressor import register_compressor + +from .parallel import Parallel +from .parallel import delayed +from .parallel import cpu_count +from .parallel import register_parallel_backend +from .parallel import parallel_backend +from .parallel import parallel_config +from .parallel import effective_n_jobs +from ._cloudpickle_wrapper import wrap_non_picklable_objects + + +__all__ = ['Memory', 'MemorizedResult', 'PrintTime', 'Logger', 'hash', 'dump', + 'load', 'Parallel', 'delayed', 'cpu_count', 'effective_n_jobs', + 'register_parallel_backend', 'parallel_backend', 'expires_after', + 'register_store_backend', 'register_compressor', + 'wrap_non_picklable_objects', 'parallel_config'] + + +# Workaround issue discovered in intel-openmp 2019.5: +# https://github.com/ContinuumIO/anaconda-issues/issues/11294 +os.environ.setdefault("KMP_INIT_AT_FORK", "FALSE") diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..dc53533 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_cloudpickle_wrapper.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_cloudpickle_wrapper.cpython-310.pyc new file mode 100644 index 0000000..58b2e6b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_cloudpickle_wrapper.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_dask.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_dask.cpython-310.pyc new file mode 100644 index 0000000..8894bf4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_dask.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_memmapping_reducer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_memmapping_reducer.cpython-310.pyc new file mode 100644 index 0000000..84f30e5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_memmapping_reducer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_multiprocessing_helpers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_multiprocessing_helpers.cpython-310.pyc new file mode 100644 index 0000000..793c733 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_multiprocessing_helpers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_parallel_backends.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_parallel_backends.cpython-310.pyc new file mode 100644 index 0000000..c627f8e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_parallel_backends.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_store_backends.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_store_backends.cpython-310.pyc new file mode 100644 index 0000000..8d878ff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_store_backends.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000..4f64f45 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/backports.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/backports.cpython-310.pyc new file mode 100644 index 0000000..4ac760a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/backports.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/compressor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/compressor.cpython-310.pyc new file mode 100644 index 0000000..1c5364c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/compressor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/disk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/disk.cpython-310.pyc new file mode 100644 index 0000000..a49792a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/disk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/executor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/executor.cpython-310.pyc new file mode 100644 index 0000000..117dd39 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/executor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/func_inspect.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/func_inspect.cpython-310.pyc new file mode 100644 index 0000000..15b7d57 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/func_inspect.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/hashing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/hashing.cpython-310.pyc new file mode 100644 index 0000000..b7aab04 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/hashing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/logger.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/logger.cpython-310.pyc new file mode 100644 index 0000000..480137b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/logger.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/memory.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/memory.cpython-310.pyc new file mode 100644 index 0000000..9b5e35d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/memory.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle.cpython-310.pyc new file mode 100644 index 0000000..965946f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_compat.cpython-310.pyc new file mode 100644 index 0000000..50618f5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_utils.cpython-310.pyc new file mode 100644 index 0000000..24c2205 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/numpy_pickle_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/parallel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/parallel.cpython-310.pyc new file mode 100644 index 0000000..b0a6d24 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/parallel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/pool.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/pool.cpython-310.pyc new file mode 100644 index 0000000..232a174 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/pool.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/testing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/testing.cpython-310.pyc new file mode 100644 index 0000000..49df9f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/__pycache__/testing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_cloudpickle_wrapper.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_cloudpickle_wrapper.py new file mode 100644 index 0000000..daf899d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_cloudpickle_wrapper.py @@ -0,0 +1,19 @@ +""" +Small shim of loky's cloudpickle_wrapper to avoid failure when +multiprocessing is not available. +""" + + +from ._multiprocessing_helpers import mp + + +def _my_wrap_non_picklable_objects(obj, keep_wrapper=True): + return obj + + +if mp is not None: + from .externals.loky import wrap_non_picklable_objects +else: + wrap_non_picklable_objects = _my_wrap_non_picklable_objects + +__all__ = ["wrap_non_picklable_objects"] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_dask.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_dask.py new file mode 100644 index 0000000..4288ed0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_dask.py @@ -0,0 +1,379 @@ +from __future__ import print_function, division, absolute_import + +import asyncio +import concurrent.futures +import contextlib + +import time +from uuid import uuid4 +import weakref + +from .parallel import parallel_config +from .parallel import AutoBatchingMixin, ParallelBackendBase + +from ._utils import ( + _TracebackCapturingWrapper, + _retrieve_traceback_capturing_wrapped_call +) + +try: + import dask + import distributed +except ImportError: + dask = None + distributed = None + +if dask is not None and distributed is not None: + from dask.utils import funcname + from dask.sizeof import sizeof + from dask.distributed import ( + Client, + as_completed, + get_client, + secede, + rejoin, + ) + from distributed.utils import thread_state + + try: + # asyncio.TimeoutError, Python3-only error thrown by recent versions of + # distributed + from distributed.utils import TimeoutError as _TimeoutError + except ImportError: + from tornado.gen import TimeoutError as _TimeoutError + + +def is_weakrefable(obj): + try: + weakref.ref(obj) + return True + except TypeError: + return False + + +class _WeakKeyDictionary: + """A variant of weakref.WeakKeyDictionary for unhashable objects. + + This datastructure is used to store futures for broadcasted data objects + such as large numpy arrays or pandas dataframes that are not hashable and + therefore cannot be used as keys of traditional python dicts. + + Furthermore using a dict with id(array) as key is not safe because the + Python is likely to reuse id of recently collected arrays. + """ + + def __init__(self): + self._data = {} + + def __getitem__(self, obj): + ref, val = self._data[id(obj)] + if ref() is not obj: + # In case of a race condition with on_destroy. + raise KeyError(obj) + return val + + def __setitem__(self, obj, value): + key = id(obj) + try: + ref, _ = self._data[key] + if ref() is not obj: + # In case of race condition with on_destroy. + raise KeyError(obj) + except KeyError: + # Insert the new entry in the mapping along with a weakref + # callback to automatically delete the entry from the mapping + # as soon as the object used as key is garbage collected. + def on_destroy(_): + del self._data[key] + ref = weakref.ref(obj, on_destroy) + self._data[key] = ref, value + + def __len__(self): + return len(self._data) + + def clear(self): + self._data.clear() + + +def _funcname(x): + try: + if isinstance(x, list): + x = x[0][0] + except Exception: + pass + return funcname(x) + + +def _make_tasks_summary(tasks): + """Summarize of list of (func, args, kwargs) function calls""" + unique_funcs = {func for func, args, kwargs in tasks} + + if len(unique_funcs) == 1: + mixed = False + else: + mixed = True + return len(tasks), mixed, _funcname(tasks) + + +class Batch: + """dask-compatible wrapper that executes a batch of tasks""" + def __init__(self, tasks): + # collect some metadata from the tasks to ease Batch calls + # introspection when debugging + self._num_tasks, self._mixed, self._funcname = _make_tasks_summary( + tasks + ) + + def __call__(self, tasks=None): + results = [] + with parallel_config(backend='dask'): + for func, args, kwargs in tasks: + results.append(func(*args, **kwargs)) + return results + + def __repr__(self): + descr = f"batch_of_{self._funcname}_{self._num_tasks}_calls" + if self._mixed: + descr = "mixed_" + descr + return descr + + +def _joblib_probe_task(): + # Noop used by the joblib connector to probe when workers are ready. + pass + + +class DaskDistributedBackend(AutoBatchingMixin, ParallelBackendBase): + MIN_IDEAL_BATCH_DURATION = 0.2 + MAX_IDEAL_BATCH_DURATION = 1.0 + supports_retrieve_callback = True + default_n_jobs = -1 + + def __init__(self, scheduler_host=None, scatter=None, + client=None, loop=None, wait_for_workers_timeout=10, + **submit_kwargs): + super().__init__() + + if distributed is None: + msg = ("You are trying to use 'dask' as a joblib parallel backend " + "but dask is not installed. Please install dask " + "to fix this error.") + raise ValueError(msg) + + if client is None: + if scheduler_host: + client = Client(scheduler_host, loop=loop, + set_as_default=False) + else: + try: + client = get_client() + except ValueError as e: + msg = ("To use Joblib with Dask first create a Dask Client" + "\n\n" + " from dask.distributed import Client\n" + " client = Client()\n" + "or\n" + " client = Client('scheduler-address:8786')") + raise ValueError(msg) from e + + self.client = client + + if scatter is not None and not isinstance(scatter, (list, tuple)): + raise TypeError("scatter must be a list/tuple, got " + "`%s`" % type(scatter).__name__) + + if scatter is not None and len(scatter) > 0: + # Keep a reference to the scattered data to keep the ids the same + self._scatter = list(scatter) + scattered = self.client.scatter(scatter, broadcast=True) + self.data_futures = {id(x): f for x, f in zip(scatter, scattered)} + else: + self._scatter = [] + self.data_futures = {} + self.wait_for_workers_timeout = wait_for_workers_timeout + self.submit_kwargs = submit_kwargs + self.waiting_futures = as_completed( + [], + loop=client.loop, + with_results=True, + raise_errors=False + ) + self._results = {} + self._callbacks = {} + + async def _collect(self): + while self._continue: + async for future, result in self.waiting_futures: + cf_future = self._results.pop(future) + callback = self._callbacks.pop(future) + if future.status == "error": + typ, exc, tb = result + cf_future.set_exception(exc) + else: + cf_future.set_result(result) + callback(result) + await asyncio.sleep(0.01) + + def __reduce__(self): + return (DaskDistributedBackend, ()) + + def get_nested_backend(self): + return DaskDistributedBackend(client=self.client), -1 + + def configure(self, n_jobs=1, parallel=None, **backend_args): + self.parallel = parallel + return self.effective_n_jobs(n_jobs) + + def start_call(self): + self._continue = True + self.client.loop.add_callback(self._collect) + self.call_data_futures = _WeakKeyDictionary() + + def stop_call(self): + # The explicit call to clear is required to break a cycling reference + # to the futures. + self._continue = False + # wait for the future collection routine (self._backend._collect) to + # finish in order to limit asyncio warnings due to aborting _collect + # during a following backend termination call + time.sleep(0.01) + self.call_data_futures.clear() + + def effective_n_jobs(self, n_jobs): + effective_n_jobs = sum(self.client.ncores().values()) + if effective_n_jobs != 0 or not self.wait_for_workers_timeout: + return effective_n_jobs + + # If there is no worker, schedule a probe task to wait for the workers + # to come up and be available. If the dask cluster is in adaptive mode + # task might cause the cluster to provision some workers. + try: + self.client.submit(_joblib_probe_task).result( + timeout=self.wait_for_workers_timeout + ) + except _TimeoutError as e: + error_msg = ( + "DaskDistributedBackend has no worker after {} seconds. " + "Make sure that workers are started and can properly connect " + "to the scheduler and increase the joblib/dask connection " + "timeout with:\n\n" + "parallel_config(backend='dask', wait_for_workers_timeout={})" + ).format(self.wait_for_workers_timeout, + max(10, 2 * self.wait_for_workers_timeout)) + raise TimeoutError(error_msg) from e + return sum(self.client.ncores().values()) + + async def _to_func_args(self, func): + itemgetters = dict() + + # Futures that are dynamically generated during a single call to + # Parallel.__call__. + call_data_futures = getattr(self, 'call_data_futures', None) + + async def maybe_to_futures(args): + out = [] + for arg in args: + arg_id = id(arg) + if arg_id in itemgetters: + out.append(itemgetters[arg_id]) + continue + + f = self.data_futures.get(arg_id, None) + if f is None and call_data_futures is not None: + try: + f = await call_data_futures[arg] + except KeyError: + pass + if f is None: + if is_weakrefable(arg) and sizeof(arg) > 1e3: + # Automatically scatter large objects to some of + # the workers to avoid duplicated data transfers. + # Rely on automated inter-worker data stealing if + # more workers need to reuse this data + # concurrently. + # set hash=False - nested scatter calls (i.e + # calling client.scatter inside a dask worker) + # using hash=True often raise CancelledError, + # see dask/distributed#3703 + _coro = self.client.scatter( + arg, + asynchronous=True, + hash=False + ) + # Centralize the scattering of identical arguments + # between concurrent apply_async callbacks by + # exposing the running coroutine in + # call_data_futures before it completes. + t = asyncio.Task(_coro) + call_data_futures[arg] = t + + f = await t + + if f is not None: + out.append(f) + else: + out.append(arg) + return out + + tasks = [] + for f, args, kwargs in func.items: + args = list(await maybe_to_futures(args)) + kwargs = dict(zip(kwargs.keys(), + await maybe_to_futures(kwargs.values()))) + tasks.append((f, args, kwargs)) + + return (Batch(tasks), tasks) + + def apply_async(self, func, callback=None): + + cf_future = concurrent.futures.Future() + cf_future.get = cf_future.result # achieve AsyncResult API + + async def f(func, callback): + batch, tasks = await self._to_func_args(func) + key = f'{repr(batch)}-{uuid4().hex}' + + dask_future = self.client.submit( + _TracebackCapturingWrapper(batch), + tasks=tasks, + key=key, + **self.submit_kwargs + ) + self.waiting_futures.add(dask_future) + self._callbacks[dask_future] = callback + self._results[dask_future] = cf_future + + self.client.loop.add_callback(f, func, callback) + + return cf_future + + def retrieve_result_callback(self, out): + return _retrieve_traceback_capturing_wrapped_call(out) + + def abort_everything(self, ensure_ready=True): + """ Tell the client to cancel any task submitted via this instance + + joblib.Parallel will never access those results + """ + with self.waiting_futures.lock: + self.waiting_futures.futures.clear() + while not self.waiting_futures.queue.empty(): + self.waiting_futures.queue.get() + + @contextlib.contextmanager + def retrieval_context(self): + """Override ParallelBackendBase.retrieval_context to avoid deadlocks. + + This removes thread from the worker's thread pool (using 'secede'). + Seceding avoids deadlock in nested parallelism settings. + """ + # See 'joblib.Parallel.__call__' and 'joblib.Parallel.retrieve' for how + # this is used. + if hasattr(thread_state, 'execution_state'): + # we are in a worker. Secede to avoid deadlock. + secede() + + yield + + if hasattr(thread_state, 'execution_state'): + rejoin() diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_memmapping_reducer.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_memmapping_reducer.py new file mode 100644 index 0000000..13f5c4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_memmapping_reducer.py @@ -0,0 +1,657 @@ +""" +Reducer using memory mapping for numpy arrays +""" +# Author: Thomas Moreau +# Copyright: 2017, Thomas Moreau +# License: BSD 3 clause + +from mmap import mmap +import errno +import os +import stat +import threading +import atexit +import tempfile +import time +import warnings +import weakref +from uuid import uuid4 +from multiprocessing import util + +from pickle import whichmodule, loads, dumps, HIGHEST_PROTOCOL, PicklingError + +try: + WindowsError +except NameError: + WindowsError = type(None) + +try: + import numpy as np + from numpy.lib.stride_tricks import as_strided +except ImportError: + np = None + +from .numpy_pickle import dump, load, load_temporary_memmap +from .backports import make_memmap +from .disk import delete_folder +from .externals.loky.backend import resource_tracker + +# Some system have a ramdisk mounted by default, we can use it instead of /tmp +# as the default folder to dump big arrays to share with subprocesses. +SYSTEM_SHARED_MEM_FS = '/dev/shm' + +# Minimal number of bytes available on SYSTEM_SHARED_MEM_FS to consider using +# it as the default folder to dump big arrays to share with subprocesses. +SYSTEM_SHARED_MEM_FS_MIN_SIZE = int(2e9) + +# Folder and file permissions to chmod temporary files generated by the +# memmapping pool. Only the owner of the Python process can access the +# temporary files and folder. +FOLDER_PERMISSIONS = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR +FILE_PERMISSIONS = stat.S_IRUSR | stat.S_IWUSR + +# Set used in joblib workers, referencing the filenames of temporary memmaps +# created by joblib to speed up data communication. In child processes, we add +# a finalizer to these memmaps that sends a maybe_unlink call to the +# resource_tracker, in order to free main memory as fast as possible. +JOBLIB_MMAPS = set() + + +def _log_and_unlink(filename): + from .externals.loky.backend.resource_tracker import _resource_tracker + util.debug( + "[FINALIZER CALL] object mapping to {} about to be deleted," + " decrementing the refcount of the file (pid: {})".format( + os.path.basename(filename), os.getpid())) + _resource_tracker.maybe_unlink(filename, "file") + + +def add_maybe_unlink_finalizer(memmap): + util.debug( + "[FINALIZER ADD] adding finalizer to {} (id {}, filename {}, pid {})" + "".format(type(memmap), id(memmap), os.path.basename(memmap.filename), + os.getpid())) + weakref.finalize(memmap, _log_and_unlink, memmap.filename) + + +def unlink_file(filename): + """Wrapper around os.unlink with a retry mechanism. + + The retry mechanism has been implemented primarily to overcome a race + condition happening during the finalizer of a np.memmap: when a process + holding the last reference to a mmap-backed np.memmap/np.array is about to + delete this array (and close the reference), it sends a maybe_unlink + request to the resource_tracker. This request can be processed faster than + it takes for the last reference of the memmap to be closed, yielding (on + Windows) a PermissionError in the resource_tracker loop. + """ + NUM_RETRIES = 10 + for retry_no in range(1, NUM_RETRIES + 1): + try: + os.unlink(filename) + break + except PermissionError: + util.debug( + '[ResourceTracker] tried to unlink {}, got ' + 'PermissionError'.format(filename) + ) + if retry_no == NUM_RETRIES: + raise + else: + time.sleep(.2) + except FileNotFoundError: + # In case of a race condition when deleting the temporary folder, + # avoid noisy FileNotFoundError exception in the resource tracker. + pass + + +resource_tracker._CLEANUP_FUNCS['file'] = unlink_file + + +class _WeakArrayKeyMap: + """A variant of weakref.WeakKeyDictionary for unhashable numpy arrays. + + This datastructure will be used with numpy arrays as obj keys, therefore we + do not use the __get__ / __set__ methods to avoid any conflict with the + numpy fancy indexing syntax. + """ + + def __init__(self): + self._data = {} + + def get(self, obj): + ref, val = self._data[id(obj)] + if ref() is not obj: + # In case of race condition with on_destroy: could never be + # triggered by the joblib tests with CPython. + raise KeyError(obj) + return val + + def set(self, obj, value): + key = id(obj) + try: + ref, _ = self._data[key] + if ref() is not obj: + # In case of race condition with on_destroy: could never be + # triggered by the joblib tests with CPython. + raise KeyError(obj) + except KeyError: + # Insert the new entry in the mapping along with a weakref + # callback to automatically delete the entry from the mapping + # as soon as the object used as key is garbage collected. + def on_destroy(_): + del self._data[key] + ref = weakref.ref(obj, on_destroy) + self._data[key] = ref, value + + def __getstate__(self): + raise PicklingError("_WeakArrayKeyMap is not pickleable") + + +############################################################################### +# Support for efficient transient pickling of numpy data structures + + +def _get_backing_memmap(a): + """Recursively look up the original np.memmap instance base if any.""" + b = getattr(a, 'base', None) + if b is None: + # TODO: check scipy sparse datastructure if scipy is installed + # a nor its descendants do not have a memmap base + return None + + elif isinstance(b, mmap): + # a is already a real memmap instance. + return a + + else: + # Recursive exploration of the base ancestry + return _get_backing_memmap(b) + + +def _get_temp_dir(pool_folder_name, temp_folder=None): + """Get the full path to a subfolder inside the temporary folder. + + Parameters + ---------- + pool_folder_name : str + Sub-folder name used for the serialization of a pool instance. + + temp_folder: str, optional + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the JOBLIB_TEMP_FOLDER environment + variable, + - /dev/shm if the folder exists and is writable: this is a + RAMdisk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with TMP, TMPDIR or TEMP environment + variables, typically /tmp under Unix operating systems. + + Returns + ------- + pool_folder : str + full path to the temporary folder + use_shared_mem : bool + whether the temporary folder is written to the system shared memory + folder or some other temporary folder. + """ + use_shared_mem = False + if temp_folder is None: + temp_folder = os.environ.get('JOBLIB_TEMP_FOLDER', None) + if temp_folder is None: + if os.path.exists(SYSTEM_SHARED_MEM_FS) and hasattr(os, 'statvfs'): + try: + shm_stats = os.statvfs(SYSTEM_SHARED_MEM_FS) + available_nbytes = shm_stats.f_bsize * shm_stats.f_bavail + if available_nbytes > SYSTEM_SHARED_MEM_FS_MIN_SIZE: + # Try to see if we have write access to the shared mem + # folder only if it is reasonably large (that is 2GB or + # more). + temp_folder = SYSTEM_SHARED_MEM_FS + pool_folder = os.path.join(temp_folder, pool_folder_name) + if not os.path.exists(pool_folder): + os.makedirs(pool_folder) + use_shared_mem = True + except (IOError, OSError): + # Missing rights in the /dev/shm partition, fallback to regular + # temp folder. + temp_folder = None + if temp_folder is None: + # Fallback to the default tmp folder, typically /tmp + temp_folder = tempfile.gettempdir() + temp_folder = os.path.abspath(os.path.expanduser(temp_folder)) + pool_folder = os.path.join(temp_folder, pool_folder_name) + return pool_folder, use_shared_mem + + +def has_shareable_memory(a): + """Return True if a is backed by some mmap buffer directly or not.""" + return _get_backing_memmap(a) is not None + + +def _strided_from_memmap(filename, dtype, mode, offset, order, shape, strides, + total_buffer_len, unlink_on_gc_collect): + """Reconstruct an array view on a memory mapped file.""" + if mode == 'w+': + # Do not zero the original data when unpickling + mode = 'r+' + + if strides is None: + # Simple, contiguous memmap + return make_memmap( + filename, dtype=dtype, shape=shape, mode=mode, offset=offset, + order=order, unlink_on_gc_collect=unlink_on_gc_collect + ) + else: + # For non-contiguous data, memmap the total enclosing buffer and then + # extract the non-contiguous view with the stride-tricks API + base = make_memmap( + filename, dtype=dtype, shape=total_buffer_len, offset=offset, + mode=mode, order=order, unlink_on_gc_collect=unlink_on_gc_collect + ) + return as_strided(base, shape=shape, strides=strides) + + +def _reduce_memmap_backed(a, m): + """Pickling reduction for memmap backed arrays. + + a is expected to be an instance of np.ndarray (or np.memmap) + m is expected to be an instance of np.memmap on the top of the ``base`` + attribute ancestry of a. ``m.base`` should be the real python mmap object. + """ + # offset that comes from the striding differences between a and m + util.debug('[MEMMAP REDUCE] reducing a memmap-backed array ' + '(shape, {}, pid: {})'.format(a.shape, os.getpid())) + try: + from numpy.lib.array_utils import byte_bounds + except (ModuleNotFoundError, ImportError): + # Backward-compat for numpy < 2.0 + from numpy import byte_bounds + a_start, a_end = byte_bounds(a) + m_start = byte_bounds(m)[0] + offset = a_start - m_start + + # offset from the backing memmap + offset += m.offset + + if m.flags['F_CONTIGUOUS']: + order = 'F' + else: + # The backing memmap buffer is necessarily contiguous hence C if not + # Fortran + order = 'C' + + if a.flags['F_CONTIGUOUS'] or a.flags['C_CONTIGUOUS']: + # If the array is a contiguous view, no need to pass the strides + strides = None + total_buffer_len = None + else: + # Compute the total number of items to map from which the strided + # view will be extracted. + strides = a.strides + total_buffer_len = (a_end - a_start) // a.itemsize + + return (_strided_from_memmap, + (m.filename, a.dtype, m.mode, offset, order, a.shape, strides, + total_buffer_len, False)) + + +def reduce_array_memmap_backward(a): + """reduce a np.array or a np.memmap from a child process""" + m = _get_backing_memmap(a) + if isinstance(m, np.memmap) and m.filename not in JOBLIB_MMAPS: + # if a is backed by a memmaped file, reconstruct a using the + # memmaped file. + return _reduce_memmap_backed(a, m) + else: + # a is either a regular (not memmap-backed) numpy array, or an array + # backed by a shared temporary file created by joblib. In the latter + # case, in order to limit the lifespan of these temporary files, we + # serialize the memmap as a regular numpy array, and decref the + # file backing the memmap (done implicitly in a previously registered + # finalizer, see ``unlink_on_gc_collect`` for more details) + return ( + loads, (dumps(np.asarray(a), protocol=HIGHEST_PROTOCOL), ) + ) + + +class ArrayMemmapForwardReducer(object): + """Reducer callable to dump large arrays to memmap files. + + Parameters + ---------- + max_nbytes: int + Threshold to trigger memmapping of large arrays to files created + a folder. + temp_folder_resolver: callable + An callable in charge of resolving a temporary folder name where files + for backing memmapped arrays are created. + mmap_mode: 'r', 'r+' or 'c' + Mode for the created memmap datastructure. See the documentation of + numpy.memmap for more details. Note: 'w+' is coerced to 'r+' + automatically to avoid zeroing the data on unpickling. + verbose: int, optional, 0 by default + If verbose > 0, memmap creations are logged. + If verbose > 1, both memmap creations, reuse and array pickling are + logged. + prewarm: bool, optional, False by default. + Force a read on newly memmapped array to make sure that OS pre-cache it + memory. This can be useful to avoid concurrent disk access when the + same data array is passed to different worker processes. + """ + + def __init__(self, max_nbytes, temp_folder_resolver, mmap_mode, + unlink_on_gc_collect, verbose=0, prewarm=True): + self._max_nbytes = max_nbytes + self._temp_folder_resolver = temp_folder_resolver + self._mmap_mode = mmap_mode + self.verbose = int(verbose) + if prewarm == "auto": + self._prewarm = not self._temp_folder.startswith( + SYSTEM_SHARED_MEM_FS + ) + else: + self._prewarm = prewarm + self._prewarm = prewarm + self._memmaped_arrays = _WeakArrayKeyMap() + self._temporary_memmaped_filenames = set() + self._unlink_on_gc_collect = unlink_on_gc_collect + + @property + def _temp_folder(self): + return self._temp_folder_resolver() + + def __reduce__(self): + # The ArrayMemmapForwardReducer is passed to the children processes: it + # needs to be pickled but the _WeakArrayKeyMap need to be skipped as + # it's only guaranteed to be consistent with the parent process memory + # garbage collection. + # Although this reducer is pickled, it is not needed in its destination + # process (child processes), as we only use this reducer to send + # memmaps from the parent process to the children processes. For this + # reason, we can afford skipping the resolver, (which would otherwise + # be unpicklable), and pass it as None instead. + args = (self._max_nbytes, None, self._mmap_mode, + self._unlink_on_gc_collect) + kwargs = { + 'verbose': self.verbose, + 'prewarm': self._prewarm, + } + return ArrayMemmapForwardReducer, args, kwargs + + def __call__(self, a): + m = _get_backing_memmap(a) + if m is not None and isinstance(m, np.memmap): + # a is already backed by a memmap file, let's reuse it directly + return _reduce_memmap_backed(a, m) + + if (not a.dtype.hasobject and self._max_nbytes is not None and + a.nbytes > self._max_nbytes): + # check that the folder exists (lazily create the pool temp folder + # if required) + try: + os.makedirs(self._temp_folder) + os.chmod(self._temp_folder, FOLDER_PERMISSIONS) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + + try: + basename = self._memmaped_arrays.get(a) + except KeyError: + # Generate a new unique random filename. The process and thread + # ids are only useful for debugging purpose and to make it + # easier to cleanup orphaned files in case of hard process + # kill (e.g. by "kill -9" or segfault). + basename = "{}-{}-{}.pkl".format( + os.getpid(), id(threading.current_thread()), uuid4().hex) + self._memmaped_arrays.set(a, basename) + filename = os.path.join(self._temp_folder, basename) + + # In case the same array with the same content is passed several + # times to the pool subprocess children, serialize it only once + + is_new_memmap = filename not in self._temporary_memmaped_filenames + + # add the memmap to the list of temporary memmaps created by joblib + self._temporary_memmaped_filenames.add(filename) + + if self._unlink_on_gc_collect: + # Bump reference count of the memmap by 1 to account for + # shared usage of the memmap by a child process. The + # corresponding decref call will be executed upon calling + # resource_tracker.maybe_unlink, registered as a finalizer in + # the child. + # the incref/decref calls here are only possible when the child + # and the parent share the same resource_tracker. It is not the + # case for the multiprocessing backend, but it does not matter + # because unlinking a memmap from a child process is only + # useful to control the memory usage of long-lasting child + # processes, while the multiprocessing-based pools terminate + # their workers at the end of a map() call. + resource_tracker.register(filename, "file") + + if is_new_memmap: + # Incref each temporary memmap created by joblib one extra + # time. This means that these memmaps will only be deleted + # once an extra maybe_unlink() is called, which is done once + # all the jobs have completed (or been canceled) in the + # Parallel._terminate_backend() method. + resource_tracker.register(filename, "file") + + if not os.path.exists(filename): + util.debug( + "[ARRAY DUMP] Pickling new array (shape={}, dtype={}) " + "creating a new memmap at {}".format( + a.shape, a.dtype, filename)) + for dumped_filename in dump(a, filename): + os.chmod(dumped_filename, FILE_PERMISSIONS) + + if self._prewarm: + # Warm up the data by accessing it. This operation ensures + # that the disk access required to create the memmapping + # file are performed in the reducing process and avoids + # concurrent memmap creation in multiple children + # processes. + load(filename, mmap_mode=self._mmap_mode).max() + + else: + util.debug( + "[ARRAY DUMP] Pickling known array (shape={}, dtype={}) " + "reusing memmap file: {}".format( + a.shape, a.dtype, os.path.basename(filename))) + + # The worker process will use joblib.load to memmap the data + return ( + (load_temporary_memmap, (filename, self._mmap_mode, + self._unlink_on_gc_collect)) + ) + else: + # do not convert a into memmap, let pickler do its usual copy with + # the default system pickler + util.debug( + '[ARRAY DUMP] Pickling array (NO MEMMAPPING) (shape={}, ' + ' dtype={}).'.format(a.shape, a.dtype)) + return (loads, (dumps(a, protocol=HIGHEST_PROTOCOL),)) + + +def get_memmapping_reducers( + forward_reducers=None, backward_reducers=None, + temp_folder_resolver=None, max_nbytes=1e6, mmap_mode='r', verbose=0, + prewarm=False, unlink_on_gc_collect=True, **kwargs): + """Construct a pair of memmapping reducer linked to a tmpdir. + + This function manage the creation and the clean up of the temporary folders + underlying the memory maps and should be use to get the reducers necessary + to construct joblib pool or executor. + """ + if forward_reducers is None: + forward_reducers = dict() + if backward_reducers is None: + backward_reducers = dict() + + if np is not None: + # Register smart numpy.ndarray reducers that detects memmap backed + # arrays and that is also able to dump to memmap large in-memory + # arrays over the max_nbytes threshold + forward_reduce_ndarray = ArrayMemmapForwardReducer( + max_nbytes, temp_folder_resolver, mmap_mode, unlink_on_gc_collect, + verbose, prewarm=prewarm) + forward_reducers[np.ndarray] = forward_reduce_ndarray + forward_reducers[np.memmap] = forward_reduce_ndarray + + # Communication from child process to the parent process always + # pickles in-memory numpy.ndarray without dumping them as memmap + # to avoid confusing the caller and make it tricky to collect the + # temporary folder + backward_reducers[np.ndarray] = reduce_array_memmap_backward + backward_reducers[np.memmap] = reduce_array_memmap_backward + + return forward_reducers, backward_reducers + + +class TemporaryResourcesManager(object): + """Stateful object able to manage temporary folder and pickles + + It exposes: + - a per-context folder name resolving API that memmap-based reducers will + rely on to know where to pickle the temporary memmaps + - a temporary file/folder management API that internally uses the + resource_tracker. + """ + + def __init__(self, temp_folder_root=None, context_id=None): + self._current_temp_folder = None + self._temp_folder_root = temp_folder_root + self._use_shared_mem = None + self._cached_temp_folders = dict() + self._id = uuid4().hex + self._finalizers = {} + if context_id is None: + # It would be safer to not assign a default context id (less silent + # bugs), but doing this while maintaining backward compatibility + # with the previous, context-unaware version get_memmaping_executor + # exposes too many low-level details. + context_id = uuid4().hex + self.set_current_context(context_id) + + def set_current_context(self, context_id): + self._current_context_id = context_id + self.register_new_context(context_id) + + def register_new_context(self, context_id): + # Prepare a sub-folder name specific to a context (usually a unique id + # generated by each instance of the Parallel class). Do not create in + # advance to spare FS write access if no array is to be dumped). + if context_id in self._cached_temp_folders: + return + else: + # During its lifecycle, one Parallel object can have several + # executors associated to it (for instance, if a loky worker raises + # an exception, joblib shutdowns the executor and instantly + # recreates a new one before raising the error - see + # ``ensure_ready``. Because we don't want two executors tied to + # the same Parallel object (and thus the same context id) to + # register/use/delete the same folder, we also add an id specific + # to the current Manager (and thus specific to its associated + # executor) to the folder name. + new_folder_name = ( + "joblib_memmapping_folder_{}_{}_{}".format( + os.getpid(), self._id, context_id) + ) + new_folder_path, _ = _get_temp_dir( + new_folder_name, self._temp_folder_root + ) + self.register_folder_finalizer(new_folder_path, context_id) + self._cached_temp_folders[context_id] = new_folder_path + + def resolve_temp_folder_name(self): + """Return a folder name specific to the currently activated context""" + return self._cached_temp_folders[self._current_context_id] + + # resource management API + + def register_folder_finalizer(self, pool_subfolder, context_id): + # Register the garbage collector at program exit in case caller forgets + # to call terminate explicitly: note we do not pass any reference to + # ensure that this callback won't prevent garbage collection of + # parallel instance and related file handler resources such as POSIX + # semaphores and pipes + pool_module_name = whichmodule(delete_folder, 'delete_folder') + resource_tracker.register(pool_subfolder, "folder") + + def _cleanup(): + # In some cases the Python runtime seems to set delete_folder to + # None just before exiting when accessing the delete_folder + # function from the closure namespace. So instead we reimport + # the delete_folder function explicitly. + # https://github.com/joblib/joblib/issues/328 + # We cannot just use from 'joblib.pool import delete_folder' + # because joblib should only use relative imports to allow + # easy vendoring. + delete_folder = __import__( + pool_module_name, fromlist=['delete_folder'] + ).delete_folder + try: + delete_folder(pool_subfolder, allow_non_empty=True) + resource_tracker.unregister(pool_subfolder, "folder") + except OSError: + warnings.warn("Failed to delete temporary folder: {}" + .format(pool_subfolder)) + + self._finalizers[context_id] = atexit.register(_cleanup) + + def _clean_temporary_resources(self, context_id=None, force=False, + allow_non_empty=False): + """Clean temporary resources created by a process-based pool""" + if context_id is None: + # Iterates over a copy of the cache keys to avoid Error due to + # iterating over a changing size dictionary. + for context_id in list(self._cached_temp_folders): + self._clean_temporary_resources( + context_id, force=force, allow_non_empty=allow_non_empty + ) + else: + temp_folder = self._cached_temp_folders.get(context_id) + if temp_folder and os.path.exists(temp_folder): + for filename in os.listdir(temp_folder): + if force: + # Some workers have failed and the ref counted might + # be off. The workers should have shut down by this + # time so forcefully clean up the files. + resource_tracker.unregister( + os.path.join(temp_folder, filename), "file" + ) + else: + resource_tracker.maybe_unlink( + os.path.join(temp_folder, filename), "file" + ) + + # When forcing clean-up, try to delete the folder even if some + # files are still in it. Otherwise, try to delete the folder + allow_non_empty |= force + + # Clean up the folder if possible, either if it is empty or + # if none of the files in it are in used and allow_non_empty. + try: + delete_folder( + temp_folder, allow_non_empty=allow_non_empty + ) + # Forget the folder once it has been deleted + self._cached_temp_folders.pop(context_id, None) + resource_tracker.unregister(temp_folder, "folder") + + # Also cancel the finalizers that gets triggered at gc. + finalizer = self._finalizers.pop(context_id, None) + if finalizer is not None: + atexit.unregister(finalizer) + + except OSError: + # Temporary folder cannot be deleted right now. + # This folder will be cleaned up by an atexit + # finalizer registered by the memmapping_reducer. + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_multiprocessing_helpers.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_multiprocessing_helpers.py new file mode 100644 index 0000000..bde4bc1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_multiprocessing_helpers.py @@ -0,0 +1,53 @@ +"""Helper module to factorize the conditional multiprocessing import logic + +We use a distinct module to simplify import statements and avoid introducing +circular dependencies (for instance for the assert_spawning name). +""" +import os +import warnings + + +# Obtain possible configuration from the environment, assuming 1 (on) +# by default, upon 0 set to None. Should instructively fail if some non +# 0/1 value is set. +mp = int(os.environ.get('JOBLIB_MULTIPROCESSING', 1)) or None +if mp: + try: + import multiprocessing as mp + import _multiprocessing # noqa + except ImportError: + mp = None + +# 2nd stage: validate that locking is available on the system and +# issue a warning if not +if mp is not None: + try: + # try to create a named semaphore using SemLock to make sure they are + # available on this platform. We use the low level object + # _multiprocessing.SemLock to avoid spawning a resource tracker on + # Unix system or changing the default backend. + import tempfile + from _multiprocessing import SemLock + + _rand = tempfile._RandomNameSequence() + for i in range(100): + try: + name = '/joblib-{}-{}' .format( + os.getpid(), next(_rand)) + _sem = SemLock(0, 0, 1, name=name, unlink=True) + del _sem # cleanup + break + except FileExistsError as e: # pragma: no cover + if i >= 99: + raise FileExistsError( + 'cannot find name for semaphore') from e + except (FileExistsError, AttributeError, ImportError, OSError) as e: + mp = None + warnings.warn('%s. joblib will operate in serial mode' % (e,)) + + +# 3rd stage: backward compat for the assert_spawning helper +if mp is not None: + from multiprocessing.context import assert_spawning +else: + assert_spawning = None diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_parallel_backends.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_parallel_backends.py new file mode 100644 index 0000000..8201c96 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_parallel_backends.py @@ -0,0 +1,649 @@ +""" +Backends for embarrassingly parallel code. +""" + +import gc +import os +import warnings +import threading +import contextlib +from abc import ABCMeta, abstractmethod + +from ._utils import ( + _TracebackCapturingWrapper, + _retrieve_traceback_capturing_wrapped_call +) + +from ._multiprocessing_helpers import mp + +if mp is not None: + from .pool import MemmappingPool + from multiprocessing.pool import ThreadPool + from .executor import get_memmapping_executor + + # Import loky only if multiprocessing is present + from .externals.loky import process_executor, cpu_count + from .externals.loky.process_executor import ShutdownExecutorError + + +class ParallelBackendBase(metaclass=ABCMeta): + """Helper abc which defines all methods a ParallelBackend must implement""" + + supports_inner_max_num_threads = False + supports_retrieve_callback = False + default_n_jobs = 1 + + @property + def supports_return_generator(self): + return self.supports_retrieve_callback + + @property + def supports_timeout(self): + return self.supports_retrieve_callback + + nesting_level = None + + def __init__(self, nesting_level=None, inner_max_num_threads=None, + **kwargs): + super().__init__(**kwargs) + self.nesting_level = nesting_level + self.inner_max_num_threads = inner_max_num_threads + + MAX_NUM_THREADS_VARS = [ + 'OMP_NUM_THREADS', 'OPENBLAS_NUM_THREADS', 'MKL_NUM_THREADS', + 'BLIS_NUM_THREADS', 'VECLIB_MAXIMUM_THREADS', 'NUMBA_NUM_THREADS', + 'NUMEXPR_NUM_THREADS', + ] + + TBB_ENABLE_IPC_VAR = "ENABLE_IPC" + + @abstractmethod + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs that can actually run in parallel + + n_jobs is the number of workers requested by the callers. Passing + n_jobs=-1 means requesting all available workers for instance matching + the number of CPU cores on the worker host(s). + + This method should return a guesstimate of the number of workers that + can actually perform work concurrently. The primary use case is to make + it possible for the caller to know in how many chunks to slice the + work. + + In general working on larger data chunks is more efficient (less + scheduling overhead and better use of CPU cache prefetching heuristics) + as long as all the workers have enough work to do. + """ + + @abstractmethod + def apply_async(self, func, callback=None): + """Schedule a func to be run""" + + def retrieve_result_callback(self, out): + """Called within the callback function passed in apply_async. + + The argument of this function is the argument given to a callback in + the considered backend. It is supposed to return the outcome of a task + if it succeeded or raise the exception if it failed. + """ + + def configure(self, n_jobs=1, parallel=None, prefer=None, require=None, + **backend_args): + """Reconfigure the backend and return the number of workers. + + This makes it possible to reuse an existing backend instance for + successive independent calls to Parallel with different parameters. + """ + self.parallel = parallel + return self.effective_n_jobs(n_jobs) + + def start_call(self): + """Call-back method called at the beginning of a Parallel call""" + + def stop_call(self): + """Call-back method called at the end of a Parallel call""" + + def terminate(self): + """Shutdown the workers and free the shared memory.""" + + def compute_batch_size(self): + """Determine the optimal batch size""" + return 1 + + def batch_completed(self, batch_size, duration): + """Callback indicate how long it took to run a batch""" + + def get_exceptions(self): + """List of exception types to be captured.""" + return [] + + def abort_everything(self, ensure_ready=True): + """Abort any running tasks + + This is called when an exception has been raised when executing a task + and all the remaining tasks will be ignored and can therefore be + aborted to spare computation resources. + + If ensure_ready is True, the backend should be left in an operating + state as future tasks might be re-submitted via that same backend + instance. + + If ensure_ready is False, the implementer of this method can decide + to leave the backend in a closed / terminated state as no new task + are expected to be submitted to this backend. + + Setting ensure_ready to False is an optimization that can be leveraged + when aborting tasks via killing processes from a local process pool + managed by the backend it-self: if we expect no new tasks, there is no + point in re-creating new workers. + """ + # Does nothing by default: to be overridden in subclasses when + # canceling tasks is possible. + pass + + def get_nested_backend(self): + """Backend instance to be used by nested Parallel calls. + + By default a thread-based backend is used for the first level of + nesting. Beyond, switch to sequential backend to avoid spawning too + many threads on the host. + """ + nesting_level = getattr(self, 'nesting_level', 0) + 1 + if nesting_level > 1: + return SequentialBackend(nesting_level=nesting_level), None + else: + return ThreadingBackend(nesting_level=nesting_level), None + + @contextlib.contextmanager + def retrieval_context(self): + """Context manager to manage an execution context. + + Calls to Parallel.retrieve will be made inside this context. + + By default, this does nothing. It may be useful for subclasses to + handle nested parallelism. In particular, it may be required to avoid + deadlocks if a backend manages a fixed number of workers, when those + workers may be asked to do nested Parallel calls. Without + 'retrieval_context' this could lead to deadlock, as all the workers + managed by the backend may be "busy" waiting for the nested parallel + calls to finish, but the backend has no free workers to execute those + tasks. + """ + yield + + def _prepare_worker_env(self, n_jobs): + """Return environment variables limiting threadpools in external libs. + + This function return a dict containing environment variables to pass + when creating a pool of process. These environment variables limit the + number of threads to `n_threads` for OpenMP, MKL, Accelerated and + OpenBLAS libraries in the child processes. + """ + explicit_n_threads = self.inner_max_num_threads + default_n_threads = max(cpu_count() // n_jobs, 1) + + # Set the inner environment variables to self.inner_max_num_threads if + # it is given. Else, default to cpu_count // n_jobs unless the variable + # is already present in the parent process environment. + env = {} + for var in self.MAX_NUM_THREADS_VARS: + if explicit_n_threads is None: + var_value = os.environ.get(var, default_n_threads) + else: + var_value = explicit_n_threads + + env[var] = str(var_value) + + if self.TBB_ENABLE_IPC_VAR not in os.environ: + # To avoid over-subscription when using TBB, let the TBB schedulers + # use Inter Process Communication to coordinate: + env[self.TBB_ENABLE_IPC_VAR] = "1" + return env + + @staticmethod + def in_main_thread(): + return isinstance(threading.current_thread(), threading._MainThread) + + +class SequentialBackend(ParallelBackendBase): + """A ParallelBackend which will execute all batches sequentially. + + Does not use/create any threading objects, and hence has minimal + overhead. Used when n_jobs == 1. + """ + + uses_threads = True + supports_timeout = False + supports_retrieve_callback = False + supports_sharedmem = True + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError('n_jobs == 0 in Parallel has no meaning') + return 1 + + def apply_async(self, func, callback=None): + """Schedule a func to be run""" + raise RuntimeError("Should never be called for SequentialBackend.") + + def retrieve_result_callback(self, out): + raise RuntimeError("Should never be called for SequentialBackend.") + + def get_nested_backend(self): + # import is not top level to avoid cyclic import errors. + from .parallel import get_active_backend + + # SequentialBackend should neither change the nesting level, the + # default backend or the number of jobs. Just return the current one. + return get_active_backend() + + +class PoolManagerMixin(object): + """A helper class for managing pool of workers.""" + + _pool = None + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError('n_jobs == 0 in Parallel has no meaning') + elif mp is None or n_jobs is None: + # multiprocessing is not available or disabled, fallback + # to sequential mode + return 1 + elif n_jobs < 0: + n_jobs = max(cpu_count() + 1 + n_jobs, 1) + return n_jobs + + def terminate(self): + """Shutdown the process or thread pool""" + if self._pool is not None: + self._pool.close() + self._pool.terminate() # terminate does a join() + self._pool = None + + def _get_pool(self): + """Used by apply_async to make it possible to implement lazy init""" + return self._pool + + def apply_async(self, func, callback=None): + """Schedule a func to be run""" + # Here, we need a wrapper to avoid crashes on KeyboardInterruptErrors. + # We also call the callback on error, to make sure the pool does not + # wait on crashed jobs. + return self._get_pool().apply_async( + _TracebackCapturingWrapper(func), (), + callback=callback, error_callback=callback + ) + + def retrieve_result_callback(self, out): + """Mimic concurrent.futures results, raising an error if needed.""" + return _retrieve_traceback_capturing_wrapped_call(out) + + def abort_everything(self, ensure_ready=True): + """Shutdown the pool and restart a new one with the same parameters""" + self.terminate() + if ensure_ready: + self.configure(n_jobs=self.parallel.n_jobs, parallel=self.parallel, + **self.parallel._backend_args) + + +class AutoBatchingMixin(object): + """A helper class for automagically batching jobs.""" + + # In seconds, should be big enough to hide multiprocessing dispatching + # overhead. + # This settings was found by running benchmarks/bench_auto_batching.py + # with various parameters on various platforms. + MIN_IDEAL_BATCH_DURATION = .2 + + # Should not be too high to avoid stragglers: long jobs running alone + # on a single worker while other workers have no work to process any more. + MAX_IDEAL_BATCH_DURATION = 2 + + # Batching counters default values + _DEFAULT_EFFECTIVE_BATCH_SIZE = 1 + _DEFAULT_SMOOTHED_BATCH_DURATION = 0.0 + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self._effective_batch_size = self._DEFAULT_EFFECTIVE_BATCH_SIZE + self._smoothed_batch_duration = self._DEFAULT_SMOOTHED_BATCH_DURATION + + def compute_batch_size(self): + """Determine the optimal batch size""" + old_batch_size = self._effective_batch_size + batch_duration = self._smoothed_batch_duration + if (batch_duration > 0 and + batch_duration < self.MIN_IDEAL_BATCH_DURATION): + # The current batch size is too small: the duration of the + # processing of a batch of task is not large enough to hide + # the scheduling overhead. + ideal_batch_size = int(old_batch_size * + self.MIN_IDEAL_BATCH_DURATION / + batch_duration) + # Multiply by two to limit oscilations between min and max. + ideal_batch_size *= 2 + + # dont increase the batch size too fast to limit huge batch sizes + # potentially leading to starving worker + batch_size = min(2 * old_batch_size, ideal_batch_size) + + batch_size = max(batch_size, 1) + + self._effective_batch_size = batch_size + if self.parallel.verbose >= 10: + self.parallel._print( + f"Batch computation too fast ({batch_duration}s.) " + f"Setting batch_size={batch_size}." + ) + elif (batch_duration > self.MAX_IDEAL_BATCH_DURATION and + old_batch_size >= 2): + # The current batch size is too big. If we schedule overly long + # running batches some CPUs might wait with nothing left to do + # while a couple of CPUs a left processing a few long running + # batches. Better reduce the batch size a bit to limit the + # likelihood of scheduling such stragglers. + + # decrease the batch size quickly to limit potential starving + ideal_batch_size = int( + old_batch_size * self.MIN_IDEAL_BATCH_DURATION / batch_duration + ) + # Multiply by two to limit oscilations between min and max. + batch_size = max(2 * ideal_batch_size, 1) + self._effective_batch_size = batch_size + if self.parallel.verbose >= 10: + self.parallel._print( + f"Batch computation too slow ({batch_duration}s.) " + f"Setting batch_size={batch_size}." + ) + else: + # No batch size adjustment + batch_size = old_batch_size + + if batch_size != old_batch_size: + # Reset estimation of the smoothed mean batch duration: this + # estimate is updated in the multiprocessing apply_async + # CallBack as long as the batch_size is constant. Therefore + # we need to reset the estimate whenever we re-tune the batch + # size. + self._smoothed_batch_duration = \ + self._DEFAULT_SMOOTHED_BATCH_DURATION + + return batch_size + + def batch_completed(self, batch_size, duration): + """Callback indicate how long it took to run a batch""" + if batch_size == self._effective_batch_size: + # Update the smoothed streaming estimate of the duration of a batch + # from dispatch to completion + old_duration = self._smoothed_batch_duration + if old_duration == self._DEFAULT_SMOOTHED_BATCH_DURATION: + # First record of duration for this batch size after the last + # reset. + new_duration = duration + else: + # Update the exponentially weighted average of the duration of + # batch for the current effective size. + new_duration = 0.8 * old_duration + 0.2 * duration + self._smoothed_batch_duration = new_duration + + def reset_batch_stats(self): + """Reset batch statistics to default values. + + This avoids interferences with future jobs. + """ + self._effective_batch_size = self._DEFAULT_EFFECTIVE_BATCH_SIZE + self._smoothed_batch_duration = self._DEFAULT_SMOOTHED_BATCH_DURATION + + +class ThreadingBackend(PoolManagerMixin, ParallelBackendBase): + """A ParallelBackend which will use a thread pool to execute batches in. + + This is a low-overhead backend but it suffers from the Python Global + Interpreter Lock if the called function relies a lot on Python objects. + Mostly useful when the execution bottleneck is a compiled extension that + explicitly releases the GIL (for instance a Cython loop wrapped in a "with + nogil" block or an expensive call to a library such as NumPy). + + The actual thread pool is lazily initialized: the actual thread pool + construction is delayed to the first call to apply_async. + + ThreadingBackend is used as the default backend for nested calls. + """ + + supports_retrieve_callback = True + uses_threads = True + supports_sharedmem = True + + def configure(self, n_jobs=1, parallel=None, **backend_args): + """Build a process or thread pool and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + # Avoid unnecessary overhead and use sequential backend instead. + raise FallbackToBackend( + SequentialBackend(nesting_level=self.nesting_level)) + self.parallel = parallel + self._n_jobs = n_jobs + return n_jobs + + def _get_pool(self): + """Lazily initialize the thread pool + + The actual pool of worker threads is only initialized at the first + call to apply_async. + """ + if self._pool is None: + self._pool = ThreadPool(self._n_jobs) + return self._pool + + +class MultiprocessingBackend(PoolManagerMixin, AutoBatchingMixin, + ParallelBackendBase): + """A ParallelBackend which will use a multiprocessing.Pool. + + Will introduce some communication and memory overhead when exchanging + input and output data with the with the worker Python processes. + However, does not suffer from the Python Global Interpreter Lock. + """ + + supports_retrieve_callback = True + supports_return_generator = False + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel. + + This also checks if we are attempting to create a nested parallel + loop. + """ + if mp is None: + return 1 + + if mp.current_process().daemon: + # Daemonic processes cannot have children + if n_jobs != 1: + if inside_dask_worker(): + msg = ( + "Inside a Dask worker with daemon=True, " + "setting n_jobs=1.\nPossible work-arounds:\n" + "- dask.config.set(" + "{'distributed.worker.daemon': False})" + "- set the environment variable " + "DASK_DISTRIBUTED__WORKER__DAEMON=False\n" + "before creating your Dask cluster." + ) + else: + msg = ( + 'Multiprocessing-backed parallel loops ' + 'cannot be nested, setting n_jobs=1' + ) + warnings.warn(msg, stacklevel=3) + return 1 + + if process_executor._CURRENT_DEPTH > 0: + # Mixing loky and multiprocessing in nested loop is not supported + if n_jobs != 1: + warnings.warn( + 'Multiprocessing-backed parallel loops cannot be nested,' + ' below loky, setting n_jobs=1', + stacklevel=3) + return 1 + + elif not (self.in_main_thread() or self.nesting_level == 0): + # Prevent posix fork inside in non-main posix threads + if n_jobs != 1: + warnings.warn( + 'Multiprocessing-backed parallel loops cannot be nested' + ' below threads, setting n_jobs=1', + stacklevel=3) + return 1 + + return super(MultiprocessingBackend, self).effective_n_jobs(n_jobs) + + def configure(self, n_jobs=1, parallel=None, prefer=None, require=None, + **memmappingpool_args): + """Build a process or thread pool and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + raise FallbackToBackend( + SequentialBackend(nesting_level=self.nesting_level)) + + # Make sure to free as much memory as possible before forking + gc.collect() + self._pool = MemmappingPool(n_jobs, **memmappingpool_args) + self.parallel = parallel + return n_jobs + + def terminate(self): + """Shutdown the process or thread pool""" + super(MultiprocessingBackend, self).terminate() + self.reset_batch_stats() + + +class LokyBackend(AutoBatchingMixin, ParallelBackendBase): + """Managing pool of workers with loky instead of multiprocessing.""" + + supports_retrieve_callback = True + supports_inner_max_num_threads = True + + def configure(self, n_jobs=1, parallel=None, prefer=None, require=None, + idle_worker_timeout=300, **memmappingexecutor_args): + """Build a process executor and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + raise FallbackToBackend( + SequentialBackend(nesting_level=self.nesting_level)) + + self._workers = get_memmapping_executor( + n_jobs, timeout=idle_worker_timeout, + env=self._prepare_worker_env(n_jobs=n_jobs), + context_id=parallel._id, **memmappingexecutor_args) + self.parallel = parallel + return n_jobs + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError('n_jobs == 0 in Parallel has no meaning') + elif mp is None or n_jobs is None: + # multiprocessing is not available or disabled, fallback + # to sequential mode + return 1 + elif mp.current_process().daemon: + # Daemonic processes cannot have children + if n_jobs != 1: + if inside_dask_worker(): + msg = ( + "Inside a Dask worker with daemon=True, " + "setting n_jobs=1.\nPossible work-arounds:\n" + "- dask.config.set(" + "{'distributed.worker.daemon': False})\n" + "- set the environment variable " + "DASK_DISTRIBUTED__WORKER__DAEMON=False\n" + "before creating your Dask cluster." + ) + else: + msg = ( + 'Loky-backed parallel loops cannot be called in a' + ' multiprocessing, setting n_jobs=1' + ) + warnings.warn(msg, stacklevel=3) + + return 1 + elif not (self.in_main_thread() or self.nesting_level == 0): + # Prevent posix fork inside in non-main posix threads + if n_jobs != 1: + warnings.warn( + 'Loky-backed parallel loops cannot be nested below ' + 'threads, setting n_jobs=1', + stacklevel=3) + return 1 + elif n_jobs < 0: + n_jobs = max(cpu_count() + 1 + n_jobs, 1) + return n_jobs + + def apply_async(self, func, callback=None): + """Schedule a func to be run""" + future = self._workers.submit(func) + if callback is not None: + future.add_done_callback(callback) + return future + + def retrieve_result_callback(self, out): + try: + return out.result() + except ShutdownExecutorError: + raise RuntimeError( + "The executor underlying Parallel has been shutdown. " + "This is likely due to the garbage collection of a previous " + "generator from a call to Parallel with return_as='generator'." + " Make sure the generator is not garbage collected when " + "submitting a new job or that it is first properly exhausted." + ) + + def terminate(self): + if self._workers is not None: + # Don't terminate the workers as we want to reuse them in later + # calls, but cleanup the temporary resources that the Parallel call + # created. This 'hack' requires a private, low-level operation. + self._workers._temp_folder_manager._clean_temporary_resources( + context_id=self.parallel._id, force=False + ) + self._workers = None + + self.reset_batch_stats() + + def abort_everything(self, ensure_ready=True): + """Shutdown the workers and restart a new one with the same parameters + """ + self._workers.terminate(kill_workers=True) + self._workers = None + + if ensure_ready: + self.configure(n_jobs=self.parallel.n_jobs, parallel=self.parallel) + + +class FallbackToBackend(Exception): + """Raised when configuration should fallback to another backend""" + + def __init__(self, backend): + self.backend = backend + + +def inside_dask_worker(): + """Check whether the current function is executed inside a Dask worker. + """ + # This function can not be in joblib._dask because there would be a + # circular import: + # _dask imports _parallel_backend that imports _dask ... + try: + from distributed import get_worker + except ImportError: + return False + + try: + get_worker() + return True + except ValueError: + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_store_backends.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_store_backends.py new file mode 100644 index 0000000..68e207c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_store_backends.py @@ -0,0 +1,474 @@ +"""Storage providers backends for Memory caching.""" + +from pickle import PicklingError +import re +import os +import os.path +import datetime +import json +import shutil +import time +import warnings +import collections +import operator +import threading +from abc import ABCMeta, abstractmethod + +from .backports import concurrency_safe_rename +from .disk import mkdirp, memstr_to_bytes, rm_subdirs +from .logger import format_time +from . import numpy_pickle + +CacheItemInfo = collections.namedtuple('CacheItemInfo', + 'path size last_access') + + +class CacheWarning(Warning): + """Warning to capture dump failures except for PicklingError.""" + pass + + +def concurrency_safe_write(object_to_write, filename, write_func): + """Writes an object into a unique file in a concurrency-safe way.""" + thread_id = id(threading.current_thread()) + temporary_filename = '{}.thread-{}-pid-{}'.format( + filename, thread_id, os.getpid()) + write_func(object_to_write, temporary_filename) + + return temporary_filename + + +class StoreBackendBase(metaclass=ABCMeta): + """Helper Abstract Base Class which defines all methods that + a StorageBackend must implement.""" + + location = None + + @abstractmethod + def _open_item(self, f, mode): + """Opens an item on the store and return a file-like object. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + f: a file-like object + The file-like object where an item is stored and retrieved + mode: string, optional + the mode in which the file-like object is opened allowed valued are + 'rb', 'wb' + + Returns + ------- + a file-like object + """ + + @abstractmethod + def _item_exists(self, location): + """Checks if an item location exists in the store. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + location: string + The location of an item. On a filesystem, this corresponds to the + absolute path, including the filename, of a file. + + Returns + ------- + True if the item exists, False otherwise + """ + + @abstractmethod + def _move_item(self, src, dst): + """Moves an item from src to dst in the store. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + src: string + The source location of an item + dst: string + The destination location of an item + """ + + @abstractmethod + def create_location(self, location): + """Creates a location on the store. + + Parameters + ---------- + location: string + The location in the store. On a filesystem, this corresponds to a + directory. + """ + + @abstractmethod + def clear_location(self, location): + """Clears a location on the store. + + Parameters + ---------- + location: string + The location in the store. On a filesystem, this corresponds to a + directory or a filename absolute path + """ + + @abstractmethod + def get_items(self): + """Returns the whole list of items available in the store. + + Returns + ------- + The list of items identified by their ids (e.g filename in a + filesystem). + """ + + @abstractmethod + def configure(self, location, verbose=0, backend_options=dict()): + """Configures the store. + + Parameters + ---------- + location: string + The base location used by the store. On a filesystem, this + corresponds to a directory. + verbose: int + The level of verbosity of the store + backend_options: dict + Contains a dictionary of named parameters used to configure the + store backend. + """ + + +class StoreBackendMixin(object): + """Class providing all logic for managing the store in a generic way. + + The StoreBackend subclass has to implement 3 methods: create_location, + clear_location and configure. The StoreBackend also has to provide + a private _open_item, _item_exists and _move_item methods. The _open_item + method has to have the same signature as the builtin open and return a + file-like object. + """ + + def load_item(self, call_id, verbose=1, timestamp=None, metadata=None): + """Load an item from the store given its id as a list of str.""" + full_path = os.path.join(self.location, *call_id) + + if verbose > 1: + ts_string = ('{: <16}'.format(format_time(time.time() - timestamp)) + if timestamp is not None else '') + signature = os.path.basename(call_id[0]) + if metadata is not None and 'input_args' in metadata: + kwargs = ', '.join('{}={}'.format(*item) + for item in metadata['input_args'].items()) + signature += '({})'.format(kwargs) + msg = '[Memory]{}: Loading {}'.format(ts_string, signature) + if verbose < 10: + print('{0}...'.format(msg)) + else: + print('{0} from {1}'.format(msg, full_path)) + + mmap_mode = (None if not hasattr(self, 'mmap_mode') + else self.mmap_mode) + + filename = os.path.join(full_path, 'output.pkl') + if not self._item_exists(filename): + raise KeyError("Non-existing item (may have been " + "cleared).\nFile %s does not exist" % filename) + + # file-like object cannot be used when mmap_mode is set + if mmap_mode is None: + with self._open_item(filename, "rb") as f: + item = numpy_pickle.load(f) + else: + item = numpy_pickle.load(filename, mmap_mode=mmap_mode) + return item + + def dump_item(self, call_id, item, verbose=1): + """Dump an item in the store at the id given as a list of str.""" + try: + item_path = os.path.join(self.location, *call_id) + if not self._item_exists(item_path): + self.create_location(item_path) + filename = os.path.join(item_path, 'output.pkl') + if verbose > 10: + print('Persisting in %s' % item_path) + + def write_func(to_write, dest_filename): + with self._open_item(dest_filename, "wb") as f: + try: + numpy_pickle.dump(to_write, f, compress=self.compress) + except PicklingError as e: + # TODO(1.5) turn into error + warnings.warn( + "Unable to cache to disk: failed to pickle " + "output. In version 1.5 this will raise an " + f"exception. Exception: {e}.", + FutureWarning + ) + + self._concurrency_safe_write(item, filename, write_func) + except Exception as e: # noqa: E722 + warnings.warn( + "Unable to cache to disk. Possibly a race condition in the " + f"creation of the directory. Exception: {e}.", + CacheWarning + ) + + def clear_item(self, call_id): + """Clear the item at the id, given as a list of str.""" + item_path = os.path.join(self.location, *call_id) + if self._item_exists(item_path): + self.clear_location(item_path) + + def contains_item(self, call_id): + """Check if there is an item at the id, given as a list of str.""" + item_path = os.path.join(self.location, *call_id) + filename = os.path.join(item_path, 'output.pkl') + + return self._item_exists(filename) + + def get_item_info(self, call_id): + """Return information about item.""" + return {'location': os.path.join(self.location, *call_id)} + + def get_metadata(self, call_id): + """Return actual metadata of an item.""" + try: + item_path = os.path.join(self.location, *call_id) + filename = os.path.join(item_path, 'metadata.json') + with self._open_item(filename, 'rb') as f: + return json.loads(f.read().decode('utf-8')) + except: # noqa: E722 + return {} + + def store_metadata(self, call_id, metadata): + """Store metadata of a computation.""" + try: + item_path = os.path.join(self.location, *call_id) + self.create_location(item_path) + filename = os.path.join(item_path, 'metadata.json') + + def write_func(to_write, dest_filename): + with self._open_item(dest_filename, "wb") as f: + f.write(json.dumps(to_write).encode('utf-8')) + + self._concurrency_safe_write(metadata, filename, write_func) + except: # noqa: E722 + pass + + def contains_path(self, call_id): + """Check cached function is available in store.""" + func_path = os.path.join(self.location, *call_id) + return self.object_exists(func_path) + + def clear_path(self, call_id): + """Clear all items with a common path in the store.""" + func_path = os.path.join(self.location, *call_id) + if self._item_exists(func_path): + self.clear_location(func_path) + + def store_cached_func_code(self, call_id, func_code=None): + """Store the code of the cached function.""" + func_path = os.path.join(self.location, *call_id) + if not self._item_exists(func_path): + self.create_location(func_path) + + if func_code is not None: + filename = os.path.join(func_path, "func_code.py") + with self._open_item(filename, 'wb') as f: + f.write(func_code.encode('utf-8')) + + def get_cached_func_code(self, call_id): + """Store the code of the cached function.""" + filename = os.path.join(self.location, *call_id, 'func_code.py') + try: + with self._open_item(filename, 'rb') as f: + return f.read().decode('utf-8') + except: # noqa: E722 + raise + + def get_cached_func_info(self, call_id): + """Return information related to the cached function if it exists.""" + return {'location': os.path.join(self.location, *call_id)} + + def clear(self): + """Clear the whole store content.""" + self.clear_location(self.location) + + def enforce_store_limits( + self, bytes_limit, items_limit=None, age_limit=None + ): + """ + Remove the store's oldest files to enforce item, byte, and age limits. + """ + items_to_delete = self._get_items_to_delete( + bytes_limit, items_limit, age_limit + ) + + for item in items_to_delete: + if self.verbose > 10: + print('Deleting item {0}'.format(item)) + try: + self.clear_location(item.path) + except OSError: + # Even with ignore_errors=True shutil.rmtree can raise OSError + # with: + # [Errno 116] Stale file handle if another process has deleted + # the folder already. + pass + + def _get_items_to_delete( + self, bytes_limit, items_limit=None, age_limit=None + ): + """ + Get items to delete to keep the store under size, file, & age limits. + """ + if isinstance(bytes_limit, str): + bytes_limit = memstr_to_bytes(bytes_limit) + + items = self.get_items() + if not items: + return [] + + size = sum(item.size for item in items) + + if bytes_limit is not None: + to_delete_size = size - bytes_limit + else: + to_delete_size = 0 + + if items_limit is not None: + to_delete_items = len(items) - items_limit + else: + to_delete_items = 0 + + if age_limit is not None: + older_item = min(item.last_access for item in items) + deadline = datetime.datetime.now() - age_limit + else: + deadline = None + + if ( + to_delete_size <= 0 and to_delete_items <= 0 + and (deadline is None or older_item > deadline) + ): + return [] + + # We want to delete first the cache items that were accessed a + # long time ago + items.sort(key=operator.attrgetter('last_access')) + + items_to_delete = [] + size_so_far = 0 + items_so_far = 0 + + for item in items: + if ( + (size_so_far >= to_delete_size) + and items_so_far >= to_delete_items + and (deadline is None or deadline < item.last_access) + ): + break + + items_to_delete.append(item) + size_so_far += item.size + items_so_far += 1 + + return items_to_delete + + def _concurrency_safe_write(self, to_write, filename, write_func): + """Writes an object into a file in a concurrency-safe way.""" + temporary_filename = concurrency_safe_write(to_write, + filename, write_func) + self._move_item(temporary_filename, filename) + + def __repr__(self): + """Printable representation of the store location.""" + return '{class_name}(location="{location}")'.format( + class_name=self.__class__.__name__, location=self.location) + + +class FileSystemStoreBackend(StoreBackendBase, StoreBackendMixin): + """A StoreBackend used with local or network file systems.""" + + _open_item = staticmethod(open) + _item_exists = staticmethod(os.path.exists) + _move_item = staticmethod(concurrency_safe_rename) + + def clear_location(self, location): + """Delete location on store.""" + if (location == self.location): + rm_subdirs(location) + else: + shutil.rmtree(location, ignore_errors=True) + + def create_location(self, location): + """Create object location on store""" + mkdirp(location) + + def get_items(self): + """Returns the whole list of items available in the store.""" + items = [] + + for dirpath, _, filenames in os.walk(self.location): + is_cache_hash_dir = re.match('[a-f0-9]{32}', + os.path.basename(dirpath)) + + if is_cache_hash_dir: + output_filename = os.path.join(dirpath, 'output.pkl') + try: + last_access = os.path.getatime(output_filename) + except OSError: + try: + last_access = os.path.getatime(dirpath) + except OSError: + # The directory has already been deleted + continue + + last_access = datetime.datetime.fromtimestamp(last_access) + try: + full_filenames = [os.path.join(dirpath, fn) + for fn in filenames] + dirsize = sum(os.path.getsize(fn) + for fn in full_filenames) + except OSError: + # Either output_filename or one of the files in + # dirpath does not exist any more. We assume this + # directory is being cleaned by another process already + continue + + items.append(CacheItemInfo(dirpath, dirsize, + last_access)) + + return items + + def configure(self, location, verbose=1, backend_options=None): + """Configure the store backend. + + For this backend, valid store options are 'compress' and 'mmap_mode' + """ + if backend_options is None: + backend_options = {} + + # setup location directory + self.location = location + if not os.path.exists(self.location): + mkdirp(self.location) + + # item can be stored compressed for faster I/O + self.compress = backend_options.get('compress', False) + + # FileSystemStoreBackend can be used with mmap_mode options under + # certain conditions. + mmap_mode = backend_options.get('mmap_mode') + if self.compress and mmap_mode is not None: + warnings.warn('Compressed items cannot be memmapped in a ' + 'filesystem store. Option will be ignored.', + stacklevel=2) + + self.mmap_mode = mmap_mode + self.verbose = verbose diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/_utils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/_utils.py new file mode 100644 index 0000000..0b7cc64 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/_utils.py @@ -0,0 +1,83 @@ +# Adapted from https://stackoverflow.com/a/9558001/2536294 + +import ast +from dataclasses import dataclass +import operator as op + + +from ._multiprocessing_helpers import mp + +if mp is not None: + from .externals.loky.process_executor import _ExceptionWithTraceback + + +# supported operators +operators = { + ast.Add: op.add, + ast.Sub: op.sub, + ast.Mult: op.mul, + ast.Div: op.truediv, + ast.FloorDiv: op.floordiv, + ast.Mod: op.mod, + ast.Pow: op.pow, + ast.USub: op.neg, +} + + +def eval_expr(expr): + """ + >>> eval_expr('2*6') + 12 + >>> eval_expr('2**6') + 64 + >>> eval_expr('1 + 2*3**(4) / (6 + -7)') + -161.0 + """ + try: + return eval_(ast.parse(expr, mode="eval").body) + except (TypeError, SyntaxError, KeyError) as e: + raise ValueError( + f"{expr!r} is not a valid or supported arithmetic expression." + ) from e + + +def eval_(node): + if isinstance(node, ast.Constant): # + return node.value + elif isinstance(node, ast.BinOp): # + return operators[type(node.op)](eval_(node.left), eval_(node.right)) + elif isinstance(node, ast.UnaryOp): # e.g., -1 + return operators[type(node.op)](eval_(node.operand)) + else: + raise TypeError(node) + + +@dataclass(frozen=True) +class _Sentinel: + """A sentinel to mark a parameter as not explicitly set""" + default_value: object + + def __repr__(self): + return f"default({self.default_value!r})" + + +class _TracebackCapturingWrapper: + """Protect function call and return error with traceback.""" + + def __init__(self, func): + self.func = func + + def __call__(self, **kwargs): + try: + return self.func(**kwargs) + except BaseException as e: + return _ExceptionWithTraceback(e) + + +def _retrieve_traceback_capturing_wrapped_call(out): + if isinstance(out, _ExceptionWithTraceback): + rebuild, args = out.__reduce__() + out = rebuild(*args) + if isinstance(out, BaseException): + raise out + return out diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/backports.py b/.pythonlibs/lib/python3.10/site-packages/joblib/backports.py new file mode 100644 index 0000000..3a14f10 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/backports.py @@ -0,0 +1,177 @@ +""" +Backports of fixes for joblib dependencies +""" +import os +import re +import time + +from os.path import basename +from multiprocessing import util + + +class Version: + """Backport from deprecated distutils + + We maintain this backport to avoid introducing a new dependency on + `packaging`. + + We might rexplore this choice in the future if all major Python projects + introduce a dependency on packaging anyway. + """ + + def __init__(self, vstring=None): + if vstring: + self.parse(vstring) + + def __repr__(self): + return "%s ('%s')" % (self.__class__.__name__, str(self)) + + def __eq__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c == 0 + + def __lt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c >= 0 + + +class LooseVersion(Version): + """Backport from deprecated distutils + + We maintain this backport to avoid introducing a new dependency on + `packaging`. + + We might rexplore this choice in the future if all major Python projects + introduce a dependency on packaging anyway. + """ + + component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + + def __init__(self, vstring=None): + if vstring: + self.parse(vstring) + + def parse(self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) + if x and x != '.'] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + def __str__(self): + return self.vstring + + def __repr__(self): + return "LooseVersion ('%s')" % str(self) + + def _cmp(self, other): + if isinstance(other, str): + other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + + +try: + import numpy as np + + def make_memmap(filename, dtype='uint8', mode='r+', offset=0, + shape=None, order='C', unlink_on_gc_collect=False): + """Custom memmap constructor compatible with numpy.memmap. + + This function: + - is a backport the numpy memmap offset fix (See + https://github.com/numpy/numpy/pull/8443 for more details. + The numpy fix is available starting numpy 1.13) + - adds ``unlink_on_gc_collect``, which specifies explicitly whether + the process re-constructing the memmap owns a reference to the + underlying file. If set to True, it adds a finalizer to the + newly-created memmap that sends a maybe_unlink request for the + memmaped file to resource_tracker. + """ + util.debug( + "[MEMMAP READ] creating a memmap (shape {}, filename {}, " + "pid {})".format(shape, basename(filename), os.getpid()) + ) + + mm = np.memmap(filename, dtype=dtype, mode=mode, offset=offset, + shape=shape, order=order) + if LooseVersion(np.__version__) < '1.13': + mm.offset = offset + if unlink_on_gc_collect: + from ._memmapping_reducer import add_maybe_unlink_finalizer + add_maybe_unlink_finalizer(mm) + return mm +except ImportError: + def make_memmap(filename, dtype='uint8', mode='r+', offset=0, + shape=None, order='C', unlink_on_gc_collect=False): + raise NotImplementedError( + "'joblib.backports.make_memmap' should not be used " + 'if numpy is not installed.') + + +if os.name == 'nt': + # https://github.com/joblib/joblib/issues/540 + access_denied_errors = (5, 13) + from os import replace + + def concurrency_safe_rename(src, dst): + """Renames ``src`` into ``dst`` overwriting ``dst`` if it exists. + + On Windows os.replace can yield permission errors if executed by two + different processes. + """ + max_sleep_time = 1 + total_sleep_time = 0 + sleep_time = 0.001 + while total_sleep_time < max_sleep_time: + try: + replace(src, dst) + break + except Exception as exc: + if getattr(exc, 'winerror', None) in access_denied_errors: + time.sleep(sleep_time) + total_sleep_time += sleep_time + sleep_time *= 2 + else: + raise + else: + raise +else: + from os import replace as concurrency_safe_rename # noqa diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/compressor.py b/.pythonlibs/lib/python3.10/site-packages/joblib/compressor.py new file mode 100644 index 0000000..0d9e261 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/compressor.py @@ -0,0 +1,570 @@ +"""Classes and functions for managing compressors.""" + +import io +import zlib +from joblib.backports import LooseVersion + +try: + from threading import RLock +except ImportError: + from dummy_threading import RLock + +try: + import bz2 +except ImportError: + bz2 = None + +try: + import lz4 + from lz4.frame import LZ4FrameFile +except ImportError: + lz4 = None + +try: + import lzma +except ImportError: + lzma = None + + +LZ4_NOT_INSTALLED_ERROR = ('LZ4 is not installed. Install it with pip: ' + 'https://python-lz4.readthedocs.io/') + +# Registered compressors +_COMPRESSORS = {} + +# Magic numbers of supported compression file formats. +_ZFILE_PREFIX = b'ZF' # used with pickle files created before 0.9.3. +_ZLIB_PREFIX = b'\x78' +_GZIP_PREFIX = b'\x1f\x8b' +_BZ2_PREFIX = b'BZ' +_XZ_PREFIX = b'\xfd\x37\x7a\x58\x5a' +_LZMA_PREFIX = b'\x5d\x00' +_LZ4_PREFIX = b'\x04\x22\x4D\x18' + + +def register_compressor(compressor_name, compressor, + force=False): + """Register a new compressor. + + Parameters + ---------- + compressor_name: str. + The name of the compressor. + compressor: CompressorWrapper + An instance of a 'CompressorWrapper'. + """ + global _COMPRESSORS + if not isinstance(compressor_name, str): + raise ValueError("Compressor name should be a string, " + "'{}' given.".format(compressor_name)) + + if not isinstance(compressor, CompressorWrapper): + raise ValueError("Compressor should implement the CompressorWrapper " + "interface, '{}' given.".format(compressor)) + + if (compressor.fileobj_factory is not None and + (not hasattr(compressor.fileobj_factory, 'read') or + not hasattr(compressor.fileobj_factory, 'write') or + not hasattr(compressor.fileobj_factory, 'seek') or + not hasattr(compressor.fileobj_factory, 'tell'))): + raise ValueError("Compressor 'fileobj_factory' attribute should " + "implement the file object interface, '{}' given." + .format(compressor.fileobj_factory)) + + if compressor_name in _COMPRESSORS and not force: + raise ValueError("Compressor '{}' already registered." + .format(compressor_name)) + + _COMPRESSORS[compressor_name] = compressor + + +class CompressorWrapper(): + """A wrapper around a compressor file object. + + Attributes + ---------- + obj: a file-like object + The object must implement the buffer interface and will be used + internally to compress/decompress the data. + prefix: bytestring + A bytestring corresponding to the magic number that identifies the + file format associated to the compressor. + extension: str + The file extension used to automatically select this compressor during + a dump to a file. + """ + + def __init__(self, obj, prefix=b'', extension=''): + self.fileobj_factory = obj + self.prefix = prefix + self.extension = extension + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + if compresslevel is None: + return self.fileobj_factory(fileobj, 'wb') + else: + return self.fileobj_factory(fileobj, 'wb', + compresslevel=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + return self.fileobj_factory(fileobj, 'rb') + + +class BZ2CompressorWrapper(CompressorWrapper): + + prefix = _BZ2_PREFIX + extension = '.bz2' + + def __init__(self): + if bz2 is not None: + self.fileobj_factory = bz2.BZ2File + else: + self.fileobj_factory = None + + def _check_versions(self): + if bz2 is None: + raise ValueError('bz2 module is not compiled on your python ' + 'standard library.') + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + self._check_versions() + if compresslevel is None: + return self.fileobj_factory(fileobj, 'wb') + else: + return self.fileobj_factory(fileobj, 'wb', + compresslevel=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + self._check_versions() + fileobj = self.fileobj_factory(fileobj, 'rb') + return fileobj + + +class LZMACompressorWrapper(CompressorWrapper): + + prefix = _LZMA_PREFIX + extension = '.lzma' + _lzma_format_name = 'FORMAT_ALONE' + + def __init__(self): + if lzma is not None: + self.fileobj_factory = lzma.LZMAFile + self._lzma_format = getattr(lzma, self._lzma_format_name) + else: + self.fileobj_factory = None + + def _check_versions(self): + if lzma is None: + raise ValueError('lzma module is not compiled on your python ' + 'standard library.') + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + if compresslevel is None: + return self.fileobj_factory(fileobj, 'wb', + format=self._lzma_format) + else: + return self.fileobj_factory(fileobj, 'wb', + format=self._lzma_format, + preset=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + return lzma.LZMAFile(fileobj, 'rb') + + +class XZCompressorWrapper(LZMACompressorWrapper): + + prefix = _XZ_PREFIX + extension = '.xz' + _lzma_format_name = 'FORMAT_XZ' + + +class LZ4CompressorWrapper(CompressorWrapper): + + prefix = _LZ4_PREFIX + extension = '.lz4' + + def __init__(self): + if lz4 is not None: + self.fileobj_factory = LZ4FrameFile + else: + self.fileobj_factory = None + + def _check_versions(self): + if lz4 is None: + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + lz4_version = lz4.__version__ + if lz4_version.startswith("v"): + lz4_version = lz4_version[1:] + if LooseVersion(lz4_version) < LooseVersion('0.19'): + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + self._check_versions() + if compresslevel is None: + return self.fileobj_factory(fileobj, 'wb') + else: + return self.fileobj_factory(fileobj, 'wb', + compression_level=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + self._check_versions() + return self.fileobj_factory(fileobj, 'rb') + + +############################################################################### +# base file compression/decompression object definition +_MODE_CLOSED = 0 +_MODE_READ = 1 +_MODE_READ_EOF = 2 +_MODE_WRITE = 3 +_BUFFER_SIZE = 8192 + + +class BinaryZlibFile(io.BufferedIOBase): + """A file object providing transparent zlib (de)compression. + + TODO python2_drop: is it still needed since we dropped Python 2 support A + BinaryZlibFile can act as a wrapper for an existing file object, or refer + directly to a named file on disk. + + Note that BinaryZlibFile provides only a *binary* file interface: data read + is returned as bytes, and data to be written should be given as bytes. + + This object is an adaptation of the BZ2File object and is compatible with + versions of python >= 2.7. + + If filename is a str or bytes object, it gives the name + of the file to be opened. Otherwise, it should be a file object, + which will be used to read or write the compressed data. + + mode can be 'rb' for reading (default) or 'wb' for (over)writing + + If mode is 'wb', compresslevel can be a number between 1 + and 9 specifying the level of compression: 1 produces the least + compression, and 9 produces the most compression. 3 is the default. + """ + + wbits = zlib.MAX_WBITS + + def __init__(self, filename, mode="rb", compresslevel=3): + # This lock must be recursive, so that BufferedIOBase's + # readline(), readlines() and writelines() don't deadlock. + self._lock = RLock() + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + self._pos = 0 + self._size = -1 + self.compresslevel = compresslevel + + if not isinstance(compresslevel, int) or not (1 <= compresslevel <= 9): + raise ValueError("'compresslevel' must be an integer " + "between 1 and 9. You provided 'compresslevel={}'" + .format(compresslevel)) + + if mode == "rb": + self._mode = _MODE_READ + self._decompressor = zlib.decompressobj(self.wbits) + self._buffer = b"" + self._buffer_offset = 0 + elif mode == "wb": + self._mode = _MODE_WRITE + self._compressor = zlib.compressobj(self.compresslevel, + zlib.DEFLATED, self.wbits, + zlib.DEF_MEM_LEVEL, 0) + else: + raise ValueError("Invalid mode: %r" % (mode,)) + + if isinstance(filename, str): + self._fp = io.open(filename, mode) + self._closefp = True + elif hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename + else: + raise TypeError("filename must be a str or bytes object, " + "or a file") + + def close(self): + """Flush and close the file. + + May be called more than once without error. Once the file is + closed, any other operation on it will raise a ValueError. + """ + with self._lock: + if self._mode == _MODE_CLOSED: + return + try: + if self._mode in (_MODE_READ, _MODE_READ_EOF): + self._decompressor = None + elif self._mode == _MODE_WRITE: + self._fp.write(self._compressor.flush()) + self._compressor = None + finally: + try: + if self._closefp: + self._fp.close() + finally: + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + self._buffer = b"" + self._buffer_offset = 0 + + @property + def closed(self): + """True if this file is closed.""" + return self._mode == _MODE_CLOSED + + def fileno(self): + """Return the file descriptor for the underlying file.""" + self._check_not_closed() + return self._fp.fileno() + + def seekable(self): + """Return whether the file supports seeking.""" + return self.readable() and self._fp.seekable() + + def readable(self): + """Return whether the file was opened for reading.""" + self._check_not_closed() + return self._mode in (_MODE_READ, _MODE_READ_EOF) + + def writable(self): + """Return whether the file was opened for writing.""" + self._check_not_closed() + return self._mode == _MODE_WRITE + + # Mode-checking helper functions. + + def _check_not_closed(self): + if self.closed: + fname = getattr(self._fp, 'name', None) + msg = "I/O operation on closed file" + if fname is not None: + msg += " {}".format(fname) + msg += "." + raise ValueError(msg) + + def _check_can_read(self): + if self._mode not in (_MODE_READ, _MODE_READ_EOF): + self._check_not_closed() + raise io.UnsupportedOperation("File not open for reading") + + def _check_can_write(self): + if self._mode != _MODE_WRITE: + self._check_not_closed() + raise io.UnsupportedOperation("File not open for writing") + + def _check_can_seek(self): + if self._mode not in (_MODE_READ, _MODE_READ_EOF): + self._check_not_closed() + raise io.UnsupportedOperation("Seeking is only supported " + "on files open for reading") + if not self._fp.seekable(): + raise io.UnsupportedOperation("The underlying file object " + "does not support seeking") + + # Fill the readahead buffer if it is empty. Returns False on EOF. + def _fill_buffer(self): + if self._mode == _MODE_READ_EOF: + return False + # Depending on the input data, our call to the decompressor may not + # return any data. In this case, try again after reading another block. + while self._buffer_offset == len(self._buffer): + try: + rawblock = (self._decompressor.unused_data or + self._fp.read(_BUFFER_SIZE)) + if not rawblock: + raise EOFError + except EOFError: + # End-of-stream marker and end of file. We're good. + self._mode = _MODE_READ_EOF + self._size = self._pos + return False + else: + self._buffer = self._decompressor.decompress(rawblock) + self._buffer_offset = 0 + return True + + # Read data until EOF. + # If return_data is false, consume the data without returning it. + def _read_all(self, return_data=True): + # The loop assumes that _buffer_offset is 0. Ensure that this is true. + self._buffer = self._buffer[self._buffer_offset:] + self._buffer_offset = 0 + + blocks = [] + while self._fill_buffer(): + if return_data: + blocks.append(self._buffer) + self._pos += len(self._buffer) + self._buffer = b"" + if return_data: + return b"".join(blocks) + + # Read a block of up to n bytes. + # If return_data is false, consume the data without returning it. + def _read_block(self, n_bytes, return_data=True): + # If we have enough data buffered, return immediately. + end = self._buffer_offset + n_bytes + if end <= len(self._buffer): + data = self._buffer[self._buffer_offset: end] + self._buffer_offset = end + self._pos += len(data) + return data if return_data else None + + # The loop assumes that _buffer_offset is 0. Ensure that this is true. + self._buffer = self._buffer[self._buffer_offset:] + self._buffer_offset = 0 + + blocks = [] + while n_bytes > 0 and self._fill_buffer(): + if n_bytes < len(self._buffer): + data = self._buffer[:n_bytes] + self._buffer_offset = n_bytes + else: + data = self._buffer + self._buffer = b"" + if return_data: + blocks.append(data) + self._pos += len(data) + n_bytes -= len(data) + if return_data: + return b"".join(blocks) + + def read(self, size=-1): + """Read up to size uncompressed bytes from the file. + + If size is negative or omitted, read until EOF is reached. + Returns b'' if the file is already at EOF. + """ + with self._lock: + self._check_can_read() + if size == 0: + return b"" + elif size < 0: + return self._read_all() + else: + return self._read_block(size) + + def readinto(self, b): + """Read up to len(b) bytes into b. + + Returns the number of bytes read (0 for EOF). + """ + with self._lock: + return io.BufferedIOBase.readinto(self, b) + + def write(self, data): + """Write a byte string to the file. + + Returns the number of uncompressed bytes written, which is + always len(data). Note that due to buffering, the file on disk + may not reflect the data written until close() is called. + """ + with self._lock: + self._check_can_write() + # Convert data type if called by io.BufferedWriter. + if isinstance(data, memoryview): + data = data.tobytes() + + compressed = self._compressor.compress(data) + self._fp.write(compressed) + self._pos += len(data) + return len(data) + + # Rewind the file to the beginning of the data stream. + def _rewind(self): + self._fp.seek(0, 0) + self._mode = _MODE_READ + self._pos = 0 + self._decompressor = zlib.decompressobj(self.wbits) + self._buffer = b"" + self._buffer_offset = 0 + + def seek(self, offset, whence=0): + """Change the file position. + + The new position is specified by offset, relative to the + position indicated by whence. Values for whence are: + + 0: start of stream (default); offset must not be negative + 1: current stream position + 2: end of stream; offset must not be positive + + Returns the new file position. + + Note that seeking is emulated, so depending on the parameters, + this operation may be extremely slow. + """ + with self._lock: + self._check_can_seek() + + # Recalculate offset as an absolute file position. + if whence == 0: + pass + elif whence == 1: + offset = self._pos + offset + elif whence == 2: + # Seeking relative to EOF - we need to know the file's size. + if self._size < 0: + self._read_all(return_data=False) + offset = self._size + offset + else: + raise ValueError("Invalid value for whence: %s" % (whence,)) + + # Make it so that offset is the number of bytes to skip forward. + if offset < self._pos: + self._rewind() + else: + offset -= self._pos + + # Read and discard data until we reach the desired position. + self._read_block(offset, return_data=False) + + return self._pos + + def tell(self): + """Return the current file position.""" + with self._lock: + self._check_not_closed() + return self._pos + + +class ZlibCompressorWrapper(CompressorWrapper): + + def __init__(self): + CompressorWrapper.__init__(self, obj=BinaryZlibFile, + prefix=_ZLIB_PREFIX, extension='.z') + + +class BinaryGzipFile(BinaryZlibFile): + """A file object providing transparent gzip (de)compression. + + If filename is a str or bytes object, it gives the name + of the file to be opened. Otherwise, it should be a file object, + which will be used to read or write the compressed data. + + mode can be 'rb' for reading (default) or 'wb' for (over)writing + + If mode is 'wb', compresslevel can be a number between 1 + and 9 specifying the level of compression: 1 produces the least + compression, and 9 produces the most compression. 3 is the default. + """ + + wbits = 31 # zlib compressor/decompressor wbits value for gzip format. + + +class GzipCompressorWrapper(CompressorWrapper): + + def __init__(self): + CompressorWrapper.__init__(self, obj=BinaryGzipFile, + prefix=_GZIP_PREFIX, extension='.gz') diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/disk.py b/.pythonlibs/lib/python3.10/site-packages/joblib/disk.py new file mode 100644 index 0000000..32fbb89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/disk.py @@ -0,0 +1,136 @@ +""" +Disk management utilities. +""" + +# Authors: Gael Varoquaux +# Lars Buitinck +# Copyright (c) 2010 Gael Varoquaux +# License: BSD Style, 3 clauses. + + +import os +import sys +import time +import errno +import shutil + +from multiprocessing import util + + +try: + WindowsError +except NameError: + WindowsError = OSError + + +def disk_used(path): + """ Return the disk usage in a directory.""" + size = 0 + for file in os.listdir(path) + ['.']: + stat = os.stat(os.path.join(path, file)) + if hasattr(stat, 'st_blocks'): + size += stat.st_blocks * 512 + else: + # on some platform st_blocks is not available (e.g., Windows) + # approximate by rounding to next multiple of 512 + size += (stat.st_size // 512 + 1) * 512 + # We need to convert to int to avoid having longs on some systems (we + # don't want longs to avoid problems we SQLite) + return int(size / 1024.) + + +def memstr_to_bytes(text): + """ Convert a memory text to its value in bytes. + """ + kilo = 1024 + units = dict(K=kilo, M=kilo ** 2, G=kilo ** 3) + try: + size = int(units[text[-1]] * float(text[:-1])) + except (KeyError, ValueError) as e: + raise ValueError( + "Invalid literal for size give: %s (type %s) should be " + "alike '10G', '500M', '50K'." % (text, type(text))) from e + return size + + +def mkdirp(d): + """Ensure directory d exists (like mkdir -p on Unix) + No guarantee that the directory is writable. + """ + try: + os.makedirs(d) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +# if a rmtree operation fails in rm_subdirs, wait for this much time (in secs), +# then retry up to RM_SUBDIRS_N_RETRY times. If it still fails, raise the +# exception. this mechanism ensures that the sub-process gc have the time to +# collect and close the memmaps before we fail. +RM_SUBDIRS_RETRY_TIME = 0.1 +RM_SUBDIRS_N_RETRY = 10 + + +def rm_subdirs(path, onerror=None): + """Remove all subdirectories in this path. + + The directory indicated by `path` is left in place, and its subdirectories + are erased. + + If onerror is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If onerror is None, + an exception is raised. + """ + + # NOTE this code is adapted from the one in shutil.rmtree, and is + # just as fast + + names = [] + try: + names = os.listdir(path) + except os.error: + if onerror is not None: + onerror(os.listdir, path, sys.exc_info()) + else: + raise + + for name in names: + fullname = os.path.join(path, name) + delete_folder(fullname, onerror=onerror) + + +def delete_folder(folder_path, onerror=None, allow_non_empty=True): + """Utility function to cleanup a temporary folder if it still exists.""" + if os.path.isdir(folder_path): + if onerror is not None: + shutil.rmtree(folder_path, False, onerror) + else: + # allow the rmtree to fail once, wait and re-try. + # if the error is raised again, fail + err_count = 0 + while True: + files = os.listdir(folder_path) + try: + if len(files) == 0 or allow_non_empty: + shutil.rmtree( + folder_path, ignore_errors=False, onerror=None + ) + util.debug( + "Successfully deleted {}".format(folder_path)) + break + else: + raise OSError( + "Expected empty folder {} but got {} " + "files.".format(folder_path, len(files)) + ) + except (OSError, WindowsError): + err_count += 1 + if err_count > RM_SUBDIRS_N_RETRY: + # the folder cannot be deleted right now. It maybe + # because some temporary files have not been deleted + # yet. + raise + time.sleep(RM_SUBDIRS_RETRY_TIME) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/executor.py b/.pythonlibs/lib/python3.10/site-packages/joblib/executor.py new file mode 100644 index 0000000..6837a7d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/executor.py @@ -0,0 +1,117 @@ +"""Utility function to construct a loky.ReusableExecutor with custom pickler. + +This module provides efficient ways of working with data stored in +shared memory with numpy.memmap arrays without inducing any memory +copy between the parent and child processes. +""" +# Author: Thomas Moreau +# Copyright: 2017, Thomas Moreau +# License: BSD 3 clause + +from ._memmapping_reducer import get_memmapping_reducers +from ._memmapping_reducer import TemporaryResourcesManager +from .externals.loky.reusable_executor import _ReusablePoolExecutor + + +_executor_args = None + + +def get_memmapping_executor(n_jobs, **kwargs): + return MemmappingExecutor.get_memmapping_executor(n_jobs, **kwargs) + + +class MemmappingExecutor(_ReusablePoolExecutor): + + @classmethod + def get_memmapping_executor(cls, n_jobs, timeout=300, initializer=None, + initargs=(), env=None, temp_folder=None, + context_id=None, **backend_args): + """Factory for ReusableExecutor with automatic memmapping for large + numpy arrays. + """ + global _executor_args + # Check if we can reuse the executor here instead of deferring the test + # to loky as the reducers are objects that changes at each call. + executor_args = backend_args.copy() + executor_args.update(env if env else {}) + executor_args.update(dict( + timeout=timeout, initializer=initializer, initargs=initargs)) + reuse = _executor_args is None or _executor_args == executor_args + _executor_args = executor_args + + manager = TemporaryResourcesManager(temp_folder) + + # reducers access the temporary folder in which to store temporary + # pickles through a call to manager.resolve_temp_folder_name. resolving + # the folder name dynamically is useful to use different folders across + # calls of a same reusable executor + job_reducers, result_reducers = get_memmapping_reducers( + unlink_on_gc_collect=True, + temp_folder_resolver=manager.resolve_temp_folder_name, + **backend_args) + _executor, executor_is_reused = super().get_reusable_executor( + n_jobs, job_reducers=job_reducers, result_reducers=result_reducers, + reuse=reuse, timeout=timeout, initializer=initializer, + initargs=initargs, env=env + ) + + if not executor_is_reused: + # Only set a _temp_folder_manager for new executors. Reused + # executors already have a _temporary_folder_manager that must not + # be re-assigned like that because it is referenced in various + # places in the reducing machinery of the executor. + _executor._temp_folder_manager = manager + + if context_id is not None: + # Only register the specified context once we know which manager + # the current executor is using, in order to not register an atexit + # finalizer twice for the same folder. + _executor._temp_folder_manager.register_new_context(context_id) + + return _executor + + def terminate(self, kill_workers=False): + + self.shutdown(kill_workers=kill_workers) + + # When workers are killed in a brutal manner, they cannot execute the + # finalizer of their shared memmaps. The refcount of those memmaps may + # be off by an unknown number, so instead of decref'ing them, we force + # delete the whole temporary folder, and unregister them. There is no + # risk of PermissionError at folder deletion because at this + # point, all child processes are dead, so all references to temporary + # memmaps are closed. Otherwise, just try to delete as much as possible + # with allow_non_empty=True but if we can't, it will be clean up later + # on by the resource_tracker. + with self._submit_resize_lock: + self._temp_folder_manager._clean_temporary_resources( + force=kill_workers, allow_non_empty=True + ) + + @property + def _temp_folder(self): + # Legacy property in tests. could be removed if we refactored the + # memmapping tests. SHOULD ONLY BE USED IN TESTS! + # We cache this property because it is called late in the tests - at + # this point, all context have been unregistered, and + # resolve_temp_folder_name raises an error. + if getattr(self, '_cached_temp_folder', None) is not None: + return self._cached_temp_folder + else: + self._cached_temp_folder = self._temp_folder_manager.resolve_temp_folder_name() # noqa + return self._cached_temp_folder + + +class _TestingMemmappingExecutor(MemmappingExecutor): + """Wrapper around ReusableExecutor to ease memmapping testing with Pool + and Executor. This is only for testing purposes. + + """ + def apply_async(self, func, args): + """Schedule a func to be run""" + future = self.submit(func, *args) + future.get = future.result + return future + + def map(self, f, *args): + return list(super().map(f, *args)) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d48d0f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__init__.py new file mode 100644 index 0000000..58a8d08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__init__.py @@ -0,0 +1,18 @@ +from . import cloudpickle +from .cloudpickle import * # noqa + +__doc__ = cloudpickle.__doc__ + +__version__ = "3.0.0" + +__all__ = [ # noqa + "__version__", + "Pickler", + "CloudPickler", + "dumps", + "loads", + "dump", + "load", + "register_pickle_by_value", + "unregister_pickle_by_value", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..797ebe0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle.cpython-310.pyc new file mode 100644 index 0000000..3d2c002 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle_fast.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle_fast.cpython-310.pyc new file mode 100644 index 0000000..416fc1a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/__pycache__/cloudpickle_fast.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle.py new file mode 100644 index 0000000..eb43a96 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle.py @@ -0,0 +1,1487 @@ +"""Pickler class to extend the standard pickle.Pickler functionality + +The main objective is to make it natural to perform distributed computing on +clusters (such as PySpark, Dask, Ray...) with interactively defined code +(functions, classes, ...) written in notebooks or console. + +In particular this pickler adds the following features: +- serialize interactively-defined or locally-defined functions, classes, + enums, typevars, lambdas and nested functions to compiled byte code; +- deal with some other non-serializable objects in an ad-hoc manner where + applicable. + +This pickler is therefore meant to be used for the communication between short +lived Python processes running the same version of Python and libraries. In +particular, it is not meant to be used for long term storage of Python objects. + +It does not include an unpickler, as standard Python unpickling suffices. + +This module was extracted from the `cloud` package, developed by `PiCloud, Inc. +`_. + +Copyright (c) 2012-now, CloudPickle developers and contributors. +Copyright (c) 2012, Regents of the University of California. +Copyright (c) 2009 `PiCloud, Inc. `_. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the University of California, Berkeley nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +import _collections_abc +from collections import ChainMap, OrderedDict +import abc +import builtins +import copyreg +import dataclasses +import dis +from enum import Enum +import io +import itertools +import logging +import opcode +import pickle +from pickle import _getattribute +import platform +import struct +import sys +import threading +import types +import typing +import uuid +import warnings +import weakref + +# The following import is required to be imported in the cloudpickle +# namespace to be able to load pickle files generated with older versions of +# cloudpickle. See: tests/test_backward_compat.py +from types import CellType # noqa: F401 + + +# cloudpickle is meant for inter process communication: we expect all +# communicating processes to run the same Python version hence we favor +# communication speed over compatibility: +DEFAULT_PROTOCOL = pickle.HIGHEST_PROTOCOL + +# Names of modules whose resources should be treated as dynamic. +_PICKLE_BY_VALUE_MODULES = set() + +# Track the provenance of reconstructed dynamic classes to make it possible to +# reconstruct instances from the matching singleton class definition when +# appropriate and preserve the usual "isinstance" semantics of Python objects. +_DYNAMIC_CLASS_TRACKER_BY_CLASS = weakref.WeakKeyDictionary() +_DYNAMIC_CLASS_TRACKER_BY_ID = weakref.WeakValueDictionary() +_DYNAMIC_CLASS_TRACKER_LOCK = threading.Lock() + +PYPY = platform.python_implementation() == "PyPy" + +builtin_code_type = None +if PYPY: + # builtin-code objects only exist in pypy + builtin_code_type = type(float.__new__.__code__) + +_extract_code_globals_cache = weakref.WeakKeyDictionary() + + +def _get_or_create_tracker_id(class_def): + with _DYNAMIC_CLASS_TRACKER_LOCK: + class_tracker_id = _DYNAMIC_CLASS_TRACKER_BY_CLASS.get(class_def) + if class_tracker_id is None: + class_tracker_id = uuid.uuid4().hex + _DYNAMIC_CLASS_TRACKER_BY_CLASS[class_def] = class_tracker_id + _DYNAMIC_CLASS_TRACKER_BY_ID[class_tracker_id] = class_def + return class_tracker_id + + +def _lookup_class_or_track(class_tracker_id, class_def): + if class_tracker_id is not None: + with _DYNAMIC_CLASS_TRACKER_LOCK: + class_def = _DYNAMIC_CLASS_TRACKER_BY_ID.setdefault( + class_tracker_id, class_def + ) + _DYNAMIC_CLASS_TRACKER_BY_CLASS[class_def] = class_tracker_id + return class_def + + +def register_pickle_by_value(module): + """Register a module to make it functions and classes picklable by value. + + By default, functions and classes that are attributes of an importable + module are to be pickled by reference, that is relying on re-importing + the attribute from the module at load time. + + If `register_pickle_by_value(module)` is called, all its functions and + classes are subsequently to be pickled by value, meaning that they can + be loaded in Python processes where the module is not importable. + + This is especially useful when developing a module in a distributed + execution environment: restarting the client Python process with the new + source code is enough: there is no need to re-install the new version + of the module on all the worker nodes nor to restart the workers. + + Note: this feature is considered experimental. See the cloudpickle + README.md file for more details and limitations. + """ + if not isinstance(module, types.ModuleType): + raise ValueError(f"Input should be a module object, got {str(module)} instead") + # In the future, cloudpickle may need a way to access any module registered + # for pickling by value in order to introspect relative imports inside + # functions pickled by value. (see + # https://github.com/cloudpipe/cloudpickle/pull/417#issuecomment-873684633). + # This access can be ensured by checking that module is present in + # sys.modules at registering time and assuming that it will still be in + # there when accessed during pickling. Another alternative would be to + # store a weakref to the module. Even though cloudpickle does not implement + # this introspection yet, in order to avoid a possible breaking change + # later, we still enforce the presence of module inside sys.modules. + if module.__name__ not in sys.modules: + raise ValueError( + f"{module} was not imported correctly, have you used an " + "`import` statement to access it?" + ) + _PICKLE_BY_VALUE_MODULES.add(module.__name__) + + +def unregister_pickle_by_value(module): + """Unregister that the input module should be pickled by value.""" + if not isinstance(module, types.ModuleType): + raise ValueError(f"Input should be a module object, got {str(module)} instead") + if module.__name__ not in _PICKLE_BY_VALUE_MODULES: + raise ValueError(f"{module} is not registered for pickle by value") + else: + _PICKLE_BY_VALUE_MODULES.remove(module.__name__) + + +def list_registry_pickle_by_value(): + return _PICKLE_BY_VALUE_MODULES.copy() + + +def _is_registered_pickle_by_value(module): + module_name = module.__name__ + if module_name in _PICKLE_BY_VALUE_MODULES: + return True + while True: + parent_name = module_name.rsplit(".", 1)[0] + if parent_name == module_name: + break + if parent_name in _PICKLE_BY_VALUE_MODULES: + return True + module_name = parent_name + return False + + +def _whichmodule(obj, name): + """Find the module an object belongs to. + + This function differs from ``pickle.whichmodule`` in two ways: + - it does not mangle the cases where obj's module is __main__ and obj was + not found in any module. + - Errors arising during module introspection are ignored, as those errors + are considered unwanted side effects. + """ + module_name = getattr(obj, "__module__", None) + + if module_name is not None: + return module_name + # Protect the iteration by using a copy of sys.modules against dynamic + # modules that trigger imports of other modules upon calls to getattr or + # other threads importing at the same time. + for module_name, module in sys.modules.copy().items(): + # Some modules such as coverage can inject non-module objects inside + # sys.modules + if ( + module_name == "__main__" + or module is None + or not isinstance(module, types.ModuleType) + ): + continue + try: + if _getattribute(module, name)[0] is obj: + return module_name + except Exception: + pass + return None + + +def _should_pickle_by_reference(obj, name=None): + """Test whether an function or a class should be pickled by reference + + Pickling by reference means by that the object (typically a function or a + class) is an attribute of a module that is assumed to be importable in the + target Python environment. Loading will therefore rely on importing the + module and then calling `getattr` on it to access the function or class. + + Pickling by reference is the only option to pickle functions and classes + in the standard library. In cloudpickle the alternative option is to + pickle by value (for instance for interactively or locally defined + functions and classes or for attributes of modules that have been + explicitly registered to be pickled by value. + """ + if isinstance(obj, types.FunctionType) or issubclass(type(obj), type): + module_and_name = _lookup_module_and_qualname(obj, name=name) + if module_and_name is None: + return False + module, name = module_and_name + return not _is_registered_pickle_by_value(module) + + elif isinstance(obj, types.ModuleType): + # We assume that sys.modules is primarily used as a cache mechanism for + # the Python import machinery. Checking if a module has been added in + # is sys.modules therefore a cheap and simple heuristic to tell us + # whether we can assume that a given module could be imported by name + # in another Python process. + if _is_registered_pickle_by_value(obj): + return False + return obj.__name__ in sys.modules + else: + raise TypeError( + "cannot check importability of {} instances".format(type(obj).__name__) + ) + + +def _lookup_module_and_qualname(obj, name=None): + if name is None: + name = getattr(obj, "__qualname__", None) + if name is None: # pragma: no cover + # This used to be needed for Python 2.7 support but is probably not + # needed anymore. However we keep the __name__ introspection in case + # users of cloudpickle rely on this old behavior for unknown reasons. + name = getattr(obj, "__name__", None) + + module_name = _whichmodule(obj, name) + + if module_name is None: + # In this case, obj.__module__ is None AND obj was not found in any + # imported module. obj is thus treated as dynamic. + return None + + if module_name == "__main__": + return None + + # Note: if module_name is in sys.modules, the corresponding module is + # assumed importable at unpickling time. See #357 + module = sys.modules.get(module_name, None) + if module is None: + # The main reason why obj's module would not be imported is that this + # module has been dynamically created, using for example + # types.ModuleType. The other possibility is that module was removed + # from sys.modules after obj was created/imported. But this case is not + # supported, as the standard pickle does not support it either. + return None + + try: + obj2, parent = _getattribute(module, name) + except AttributeError: + # obj was not found inside the module it points to + return None + if obj2 is not obj: + return None + return module, name + + +def _extract_code_globals(co): + """Find all globals names read or written to by codeblock co.""" + out_names = _extract_code_globals_cache.get(co) + if out_names is None: + # We use a dict with None values instead of a set to get a + # deterministic order and avoid introducing non-deterministic pickle + # bytes as a results. + out_names = {name: None for name in _walk_global_ops(co)} + + # Declaring a function inside another one using the "def ..." syntax + # generates a constant code object corresponding to the one of the + # nested function's As the nested function may itself need global + # variables, we need to introspect its code, extract its globals, (look + # for code object in it's co_consts attribute..) and add the result to + # code_globals + if co.co_consts: + for const in co.co_consts: + if isinstance(const, types.CodeType): + out_names.update(_extract_code_globals(const)) + + _extract_code_globals_cache[co] = out_names + + return out_names + + +def _find_imported_submodules(code, top_level_dependencies): + """Find currently imported submodules used by a function. + + Submodules used by a function need to be detected and referenced for the + function to work correctly at depickling time. Because submodules can be + referenced as attribute of their parent package (``package.submodule``), we + need a special introspection technique that does not rely on GLOBAL-related + opcodes to find references of them in a code object. + + Example: + ``` + import concurrent.futures + import cloudpickle + def func(): + x = concurrent.futures.ThreadPoolExecutor + if __name__ == '__main__': + cloudpickle.dumps(func) + ``` + The globals extracted by cloudpickle in the function's state include the + concurrent package, but not its submodule (here, concurrent.futures), which + is the module used by func. Find_imported_submodules will detect the usage + of concurrent.futures. Saving this module alongside with func will ensure + that calling func once depickled does not fail due to concurrent.futures + not being imported + """ + + subimports = [] + # check if any known dependency is an imported package + for x in top_level_dependencies: + if ( + isinstance(x, types.ModuleType) + and hasattr(x, "__package__") + and x.__package__ + ): + # check if the package has any currently loaded sub-imports + prefix = x.__name__ + "." + # A concurrent thread could mutate sys.modules, + # make sure we iterate over a copy to avoid exceptions + for name in list(sys.modules): + # Older versions of pytest will add a "None" module to + # sys.modules. + if name is not None and name.startswith(prefix): + # check whether the function can address the sub-module + tokens = set(name[len(prefix) :].split(".")) + if not tokens - set(code.co_names): + subimports.append(sys.modules[name]) + return subimports + + +# relevant opcodes +STORE_GLOBAL = opcode.opmap["STORE_GLOBAL"] +DELETE_GLOBAL = opcode.opmap["DELETE_GLOBAL"] +LOAD_GLOBAL = opcode.opmap["LOAD_GLOBAL"] +GLOBAL_OPS = (STORE_GLOBAL, DELETE_GLOBAL, LOAD_GLOBAL) +HAVE_ARGUMENT = dis.HAVE_ARGUMENT +EXTENDED_ARG = dis.EXTENDED_ARG + + +_BUILTIN_TYPE_NAMES = {} +for k, v in types.__dict__.items(): + if type(v) is type: + _BUILTIN_TYPE_NAMES[v] = k + + +def _builtin_type(name): + if name == "ClassType": # pragma: no cover + # Backward compat to load pickle files generated with cloudpickle + # < 1.3 even if loading pickle files from older versions is not + # officially supported. + return type + return getattr(types, name) + + +def _walk_global_ops(code): + """Yield referenced name for global-referencing instructions in code.""" + for instr in dis.get_instructions(code): + op = instr.opcode + if op in GLOBAL_OPS: + yield instr.argval + + +def _extract_class_dict(cls): + """Retrieve a copy of the dict of a class without the inherited method.""" + clsdict = dict(cls.__dict__) # copy dict proxy to a dict + if len(cls.__bases__) == 1: + inherited_dict = cls.__bases__[0].__dict__ + else: + inherited_dict = {} + for base in reversed(cls.__bases__): + inherited_dict.update(base.__dict__) + to_remove = [] + for name, value in clsdict.items(): + try: + base_value = inherited_dict[name] + if value is base_value: + to_remove.append(name) + except KeyError: + pass + for name in to_remove: + clsdict.pop(name) + return clsdict + + +def is_tornado_coroutine(func): + """Return whether `func` is a Tornado coroutine function. + + Running coroutines are not supported. + """ + warnings.warn( + "is_tornado_coroutine is deprecated in cloudpickle 3.0 and will be " + "removed in cloudpickle 4.0. Use tornado.gen.is_coroutine_function " + "directly instead.", + category=DeprecationWarning, + ) + if "tornado.gen" not in sys.modules: + return False + gen = sys.modules["tornado.gen"] + if not hasattr(gen, "is_coroutine_function"): + # Tornado version is too old + return False + return gen.is_coroutine_function(func) + + +def subimport(name): + # We cannot do simply: `return __import__(name)`: Indeed, if ``name`` is + # the name of a submodule, __import__ will return the top-level root module + # of this submodule. For instance, __import__('os.path') returns the `os` + # module. + __import__(name) + return sys.modules[name] + + +def dynamic_subimport(name, vars): + mod = types.ModuleType(name) + mod.__dict__.update(vars) + mod.__dict__["__builtins__"] = builtins.__dict__ + return mod + + +def _get_cell_contents(cell): + try: + return cell.cell_contents + except ValueError: + # Handle empty cells explicitly with a sentinel value. + return _empty_cell_value + + +def instance(cls): + """Create a new instance of a class. + + Parameters + ---------- + cls : type + The class to create an instance of. + + Returns + ------- + instance : cls + A new instance of ``cls``. + """ + return cls() + + +@instance +class _empty_cell_value: + """Sentinel for empty closures.""" + + @classmethod + def __reduce__(cls): + return cls.__name__ + + +def _make_function(code, globals, name, argdefs, closure): + # Setting __builtins__ in globals is needed for nogil CPython. + globals["__builtins__"] = __builtins__ + return types.FunctionType(code, globals, name, argdefs, closure) + + +def _make_empty_cell(): + if False: + # trick the compiler into creating an empty cell in our lambda + cell = None + raise AssertionError("this route should not be executed") + + return (lambda: cell).__closure__[0] + + +def _make_cell(value=_empty_cell_value): + cell = _make_empty_cell() + if value is not _empty_cell_value: + cell.cell_contents = value + return cell + + +def _make_skeleton_class( + type_constructor, name, bases, type_kwargs, class_tracker_id, extra +): + """Build dynamic class with an empty __dict__ to be filled once memoized + + If class_tracker_id is not None, try to lookup an existing class definition + matching that id. If none is found, track a newly reconstructed class + definition under that id so that other instances stemming from the same + class id will also reuse this class definition. + + The "extra" variable is meant to be a dict (or None) that can be used for + forward compatibility shall the need arise. + """ + skeleton_class = types.new_class( + name, bases, {"metaclass": type_constructor}, lambda ns: ns.update(type_kwargs) + ) + return _lookup_class_or_track(class_tracker_id, skeleton_class) + + +def _make_skeleton_enum( + bases, name, qualname, members, module, class_tracker_id, extra +): + """Build dynamic enum with an empty __dict__ to be filled once memoized + + The creation of the enum class is inspired by the code of + EnumMeta._create_. + + If class_tracker_id is not None, try to lookup an existing enum definition + matching that id. If none is found, track a newly reconstructed enum + definition under that id so that other instances stemming from the same + class id will also reuse this enum definition. + + The "extra" variable is meant to be a dict (or None) that can be used for + forward compatibility shall the need arise. + """ + # enums always inherit from their base Enum class at the last position in + # the list of base classes: + enum_base = bases[-1] + metacls = enum_base.__class__ + classdict = metacls.__prepare__(name, bases) + + for member_name, member_value in members.items(): + classdict[member_name] = member_value + enum_class = metacls.__new__(metacls, name, bases, classdict) + enum_class.__module__ = module + enum_class.__qualname__ = qualname + + return _lookup_class_or_track(class_tracker_id, enum_class) + + +def _make_typevar(name, bound, constraints, covariant, contravariant, class_tracker_id): + tv = typing.TypeVar( + name, + *constraints, + bound=bound, + covariant=covariant, + contravariant=contravariant, + ) + return _lookup_class_or_track(class_tracker_id, tv) + + +def _decompose_typevar(obj): + return ( + obj.__name__, + obj.__bound__, + obj.__constraints__, + obj.__covariant__, + obj.__contravariant__, + _get_or_create_tracker_id(obj), + ) + + +def _typevar_reduce(obj): + # TypeVar instances require the module information hence why we + # are not using the _should_pickle_by_reference directly + module_and_name = _lookup_module_and_qualname(obj, name=obj.__name__) + + if module_and_name is None: + return (_make_typevar, _decompose_typevar(obj)) + elif _is_registered_pickle_by_value(module_and_name[0]): + return (_make_typevar, _decompose_typevar(obj)) + + return (getattr, module_and_name) + + +def _get_bases(typ): + if "__orig_bases__" in getattr(typ, "__dict__", {}): + # For generic types (see PEP 560) + # Note that simply checking `hasattr(typ, '__orig_bases__')` is not + # correct. Subclasses of a fully-parameterized generic class does not + # have `__orig_bases__` defined, but `hasattr(typ, '__orig_bases__')` + # will return True because it's defined in the base class. + bases_attr = "__orig_bases__" + else: + # For regular class objects + bases_attr = "__bases__" + return getattr(typ, bases_attr) + + +def _make_dict_keys(obj, is_ordered=False): + if is_ordered: + return OrderedDict.fromkeys(obj).keys() + else: + return dict.fromkeys(obj).keys() + + +def _make_dict_values(obj, is_ordered=False): + if is_ordered: + return OrderedDict((i, _) for i, _ in enumerate(obj)).values() + else: + return {i: _ for i, _ in enumerate(obj)}.values() + + +def _make_dict_items(obj, is_ordered=False): + if is_ordered: + return OrderedDict(obj).items() + else: + return obj.items() + + +# COLLECTION OF OBJECTS __getnewargs__-LIKE METHODS +# ------------------------------------------------- + + +def _class_getnewargs(obj): + type_kwargs = {} + if "__module__" in obj.__dict__: + type_kwargs["__module__"] = obj.__module__ + + __dict__ = obj.__dict__.get("__dict__", None) + if isinstance(__dict__, property): + type_kwargs["__dict__"] = __dict__ + + return ( + type(obj), + obj.__name__, + _get_bases(obj), + type_kwargs, + _get_or_create_tracker_id(obj), + None, + ) + + +def _enum_getnewargs(obj): + members = {e.name: e.value for e in obj} + return ( + obj.__bases__, + obj.__name__, + obj.__qualname__, + members, + obj.__module__, + _get_or_create_tracker_id(obj), + None, + ) + + +# COLLECTION OF OBJECTS RECONSTRUCTORS +# ------------------------------------ +def _file_reconstructor(retval): + return retval + + +# COLLECTION OF OBJECTS STATE GETTERS +# ----------------------------------- + + +def _function_getstate(func): + # - Put func's dynamic attributes (stored in func.__dict__) in state. These + # attributes will be restored at unpickling time using + # f.__dict__.update(state) + # - Put func's members into slotstate. Such attributes will be restored at + # unpickling time by iterating over slotstate and calling setattr(func, + # slotname, slotvalue) + slotstate = { + "__name__": func.__name__, + "__qualname__": func.__qualname__, + "__annotations__": func.__annotations__, + "__kwdefaults__": func.__kwdefaults__, + "__defaults__": func.__defaults__, + "__module__": func.__module__, + "__doc__": func.__doc__, + "__closure__": func.__closure__, + } + + f_globals_ref = _extract_code_globals(func.__code__) + f_globals = {k: func.__globals__[k] for k in f_globals_ref if k in func.__globals__} + + if func.__closure__ is not None: + closure_values = list(map(_get_cell_contents, func.__closure__)) + else: + closure_values = () + + # Extract currently-imported submodules used by func. Storing these modules + # in a smoke _cloudpickle_subimports attribute of the object's state will + # trigger the side effect of importing these modules at unpickling time + # (which is necessary for func to work correctly once depickled) + slotstate["_cloudpickle_submodules"] = _find_imported_submodules( + func.__code__, itertools.chain(f_globals.values(), closure_values) + ) + slotstate["__globals__"] = f_globals + + state = func.__dict__ + return state, slotstate + + +def _class_getstate(obj): + clsdict = _extract_class_dict(obj) + clsdict.pop("__weakref__", None) + + if issubclass(type(obj), abc.ABCMeta): + # If obj is an instance of an ABCMeta subclass, don't pickle the + # cache/negative caches populated during isinstance/issubclass + # checks, but pickle the list of registered subclasses of obj. + clsdict.pop("_abc_cache", None) + clsdict.pop("_abc_negative_cache", None) + clsdict.pop("_abc_negative_cache_version", None) + registry = clsdict.pop("_abc_registry", None) + if registry is None: + # The abc caches and registered subclasses of a + # class are bundled into the single _abc_impl attribute + clsdict.pop("_abc_impl", None) + (registry, _, _, _) = abc._get_dump(obj) + + clsdict["_abc_impl"] = [subclass_weakref() for subclass_weakref in registry] + else: + # In the above if clause, registry is a set of weakrefs -- in + # this case, registry is a WeakSet + clsdict["_abc_impl"] = [type_ for type_ in registry] + + if "__slots__" in clsdict: + # pickle string length optimization: member descriptors of obj are + # created automatically from obj's __slots__ attribute, no need to + # save them in obj's state + if isinstance(obj.__slots__, str): + clsdict.pop(obj.__slots__) + else: + for k in obj.__slots__: + clsdict.pop(k, None) + + clsdict.pop("__dict__", None) # unpicklable property object + + return (clsdict, {}) + + +def _enum_getstate(obj): + clsdict, slotstate = _class_getstate(obj) + + members = {e.name: e.value for e in obj} + # Cleanup the clsdict that will be passed to _make_skeleton_enum: + # Those attributes are already handled by the metaclass. + for attrname in [ + "_generate_next_value_", + "_member_names_", + "_member_map_", + "_member_type_", + "_value2member_map_", + ]: + clsdict.pop(attrname, None) + for member in members: + clsdict.pop(member) + # Special handling of Enum subclasses + return clsdict, slotstate + + +# COLLECTIONS OF OBJECTS REDUCERS +# ------------------------------- +# A reducer is a function taking a single argument (obj), and that returns a +# tuple with all the necessary data to re-construct obj. Apart from a few +# exceptions (list, dict, bytes, int, etc.), a reducer is necessary to +# correctly pickle an object. +# While many built-in objects (Exceptions objects, instances of the "object" +# class, etc), are shipped with their own built-in reducer (invoked using +# obj.__reduce__), some do not. The following methods were created to "fill +# these holes". + + +def _code_reduce(obj): + """code object reducer.""" + # If you are not sure about the order of arguments, take a look at help + # of the specific type from types, for example: + # >>> from types import CodeType + # >>> help(CodeType) + if hasattr(obj, "co_exceptiontable"): + # Python 3.11 and later: there are some new attributes + # related to the enhanced exceptions. + args = ( + obj.co_argcount, + obj.co_posonlyargcount, + obj.co_kwonlyargcount, + obj.co_nlocals, + obj.co_stacksize, + obj.co_flags, + obj.co_code, + obj.co_consts, + obj.co_names, + obj.co_varnames, + obj.co_filename, + obj.co_name, + obj.co_qualname, + obj.co_firstlineno, + obj.co_linetable, + obj.co_exceptiontable, + obj.co_freevars, + obj.co_cellvars, + ) + elif hasattr(obj, "co_linetable"): + # Python 3.10 and later: obj.co_lnotab is deprecated and constructor + # expects obj.co_linetable instead. + args = ( + obj.co_argcount, + obj.co_posonlyargcount, + obj.co_kwonlyargcount, + obj.co_nlocals, + obj.co_stacksize, + obj.co_flags, + obj.co_code, + obj.co_consts, + obj.co_names, + obj.co_varnames, + obj.co_filename, + obj.co_name, + obj.co_firstlineno, + obj.co_linetable, + obj.co_freevars, + obj.co_cellvars, + ) + elif hasattr(obj, "co_nmeta"): # pragma: no cover + # "nogil" Python: modified attributes from 3.9 + args = ( + obj.co_argcount, + obj.co_posonlyargcount, + obj.co_kwonlyargcount, + obj.co_nlocals, + obj.co_framesize, + obj.co_ndefaultargs, + obj.co_nmeta, + obj.co_flags, + obj.co_code, + obj.co_consts, + obj.co_varnames, + obj.co_filename, + obj.co_name, + obj.co_firstlineno, + obj.co_lnotab, + obj.co_exc_handlers, + obj.co_jump_table, + obj.co_freevars, + obj.co_cellvars, + obj.co_free2reg, + obj.co_cell2reg, + ) + else: + # Backward compat for 3.8 and 3.9 + args = ( + obj.co_argcount, + obj.co_posonlyargcount, + obj.co_kwonlyargcount, + obj.co_nlocals, + obj.co_stacksize, + obj.co_flags, + obj.co_code, + obj.co_consts, + obj.co_names, + obj.co_varnames, + obj.co_filename, + obj.co_name, + obj.co_firstlineno, + obj.co_lnotab, + obj.co_freevars, + obj.co_cellvars, + ) + return types.CodeType, args + + +def _cell_reduce(obj): + """Cell (containing values of a function's free variables) reducer.""" + try: + obj.cell_contents + except ValueError: # cell is empty + return _make_empty_cell, () + else: + return _make_cell, (obj.cell_contents,) + + +def _classmethod_reduce(obj): + orig_func = obj.__func__ + return type(obj), (orig_func,) + + +def _file_reduce(obj): + """Save a file.""" + import io + + if not hasattr(obj, "name") or not hasattr(obj, "mode"): + raise pickle.PicklingError( + "Cannot pickle files that do not map to an actual file" + ) + if obj is sys.stdout: + return getattr, (sys, "stdout") + if obj is sys.stderr: + return getattr, (sys, "stderr") + if obj is sys.stdin: + raise pickle.PicklingError("Cannot pickle standard input") + if obj.closed: + raise pickle.PicklingError("Cannot pickle closed files") + if hasattr(obj, "isatty") and obj.isatty(): + raise pickle.PicklingError("Cannot pickle files that map to tty objects") + if "r" not in obj.mode and "+" not in obj.mode: + raise pickle.PicklingError( + "Cannot pickle files that are not opened for reading: %s" % obj.mode + ) + + name = obj.name + + retval = io.StringIO() + + try: + # Read the whole file + curloc = obj.tell() + obj.seek(0) + contents = obj.read() + obj.seek(curloc) + except OSError as e: + raise pickle.PicklingError( + "Cannot pickle file %s as it cannot be read" % name + ) from e + retval.write(contents) + retval.seek(curloc) + + retval.name = name + return _file_reconstructor, (retval,) + + +def _getset_descriptor_reduce(obj): + return getattr, (obj.__objclass__, obj.__name__) + + +def _mappingproxy_reduce(obj): + return types.MappingProxyType, (dict(obj),) + + +def _memoryview_reduce(obj): + return bytes, (obj.tobytes(),) + + +def _module_reduce(obj): + if _should_pickle_by_reference(obj): + return subimport, (obj.__name__,) + else: + # Some external libraries can populate the "__builtins__" entry of a + # module's `__dict__` with unpicklable objects (see #316). For that + # reason, we do not attempt to pickle the "__builtins__" entry, and + # restore a default value for it at unpickling time. + state = obj.__dict__.copy() + state.pop("__builtins__", None) + return dynamic_subimport, (obj.__name__, state) + + +def _method_reduce(obj): + return (types.MethodType, (obj.__func__, obj.__self__)) + + +def _logger_reduce(obj): + return logging.getLogger, (obj.name,) + + +def _root_logger_reduce(obj): + return logging.getLogger, () + + +def _property_reduce(obj): + return property, (obj.fget, obj.fset, obj.fdel, obj.__doc__) + + +def _weakset_reduce(obj): + return weakref.WeakSet, (list(obj),) + + +def _dynamic_class_reduce(obj): + """Save a class that can't be referenced as a module attribute. + + This method is used to serialize classes that are defined inside + functions, or that otherwise can't be serialized as attribute lookups + from importable modules. + """ + if Enum is not None and issubclass(obj, Enum): + return ( + _make_skeleton_enum, + _enum_getnewargs(obj), + _enum_getstate(obj), + None, + None, + _class_setstate, + ) + else: + return ( + _make_skeleton_class, + _class_getnewargs(obj), + _class_getstate(obj), + None, + None, + _class_setstate, + ) + + +def _class_reduce(obj): + """Select the reducer depending on the dynamic nature of the class obj.""" + if obj is type(None): # noqa + return type, (None,) + elif obj is type(Ellipsis): + return type, (Ellipsis,) + elif obj is type(NotImplemented): + return type, (NotImplemented,) + elif obj in _BUILTIN_TYPE_NAMES: + return _builtin_type, (_BUILTIN_TYPE_NAMES[obj],) + elif not _should_pickle_by_reference(obj): + return _dynamic_class_reduce(obj) + return NotImplemented + + +def _dict_keys_reduce(obj): + # Safer not to ship the full dict as sending the rest might + # be unintended and could potentially cause leaking of + # sensitive information + return _make_dict_keys, (list(obj),) + + +def _dict_values_reduce(obj): + # Safer not to ship the full dict as sending the rest might + # be unintended and could potentially cause leaking of + # sensitive information + return _make_dict_values, (list(obj),) + + +def _dict_items_reduce(obj): + return _make_dict_items, (dict(obj),) + + +def _odict_keys_reduce(obj): + # Safer not to ship the full dict as sending the rest might + # be unintended and could potentially cause leaking of + # sensitive information + return _make_dict_keys, (list(obj), True) + + +def _odict_values_reduce(obj): + # Safer not to ship the full dict as sending the rest might + # be unintended and could potentially cause leaking of + # sensitive information + return _make_dict_values, (list(obj), True) + + +def _odict_items_reduce(obj): + return _make_dict_items, (dict(obj), True) + + +def _dataclass_field_base_reduce(obj): + return _get_dataclass_field_type_sentinel, (obj.name,) + + +# COLLECTIONS OF OBJECTS STATE SETTERS +# ------------------------------------ +# state setters are called at unpickling time, once the object is created and +# it has to be updated to how it was at unpickling time. + + +def _function_setstate(obj, state): + """Update the state of a dynamic function. + + As __closure__ and __globals__ are readonly attributes of a function, we + cannot rely on the native setstate routine of pickle.load_build, that calls + setattr on items of the slotstate. Instead, we have to modify them inplace. + """ + state, slotstate = state + obj.__dict__.update(state) + + obj_globals = slotstate.pop("__globals__") + obj_closure = slotstate.pop("__closure__") + # _cloudpickle_subimports is a set of submodules that must be loaded for + # the pickled function to work correctly at unpickling time. Now that these + # submodules are depickled (hence imported), they can be removed from the + # object's state (the object state only served as a reference holder to + # these submodules) + slotstate.pop("_cloudpickle_submodules") + + obj.__globals__.update(obj_globals) + obj.__globals__["__builtins__"] = __builtins__ + + if obj_closure is not None: + for i, cell in enumerate(obj_closure): + try: + value = cell.cell_contents + except ValueError: # cell is empty + continue + obj.__closure__[i].cell_contents = value + + for k, v in slotstate.items(): + setattr(obj, k, v) + + +def _class_setstate(obj, state): + state, slotstate = state + registry = None + for attrname, attr in state.items(): + if attrname == "_abc_impl": + registry = attr + else: + setattr(obj, attrname, attr) + if registry is not None: + for subclass in registry: + obj.register(subclass) + + return obj + + +# COLLECTION OF DATACLASS UTILITIES +# --------------------------------- +# There are some internal sentinel values whose identity must be preserved when +# unpickling dataclass fields. Each sentinel value has a unique name that we can +# use to retrieve its identity at unpickling time. + + +_DATACLASSE_FIELD_TYPE_SENTINELS = { + dataclasses._FIELD.name: dataclasses._FIELD, + dataclasses._FIELD_CLASSVAR.name: dataclasses._FIELD_CLASSVAR, + dataclasses._FIELD_INITVAR.name: dataclasses._FIELD_INITVAR, +} + + +def _get_dataclass_field_type_sentinel(name): + return _DATACLASSE_FIELD_TYPE_SENTINELS[name] + + +class Pickler(pickle.Pickler): + # set of reducers defined and used by cloudpickle (private) + _dispatch_table = {} + _dispatch_table[classmethod] = _classmethod_reduce + _dispatch_table[io.TextIOWrapper] = _file_reduce + _dispatch_table[logging.Logger] = _logger_reduce + _dispatch_table[logging.RootLogger] = _root_logger_reduce + _dispatch_table[memoryview] = _memoryview_reduce + _dispatch_table[property] = _property_reduce + _dispatch_table[staticmethod] = _classmethod_reduce + _dispatch_table[CellType] = _cell_reduce + _dispatch_table[types.CodeType] = _code_reduce + _dispatch_table[types.GetSetDescriptorType] = _getset_descriptor_reduce + _dispatch_table[types.ModuleType] = _module_reduce + _dispatch_table[types.MethodType] = _method_reduce + _dispatch_table[types.MappingProxyType] = _mappingproxy_reduce + _dispatch_table[weakref.WeakSet] = _weakset_reduce + _dispatch_table[typing.TypeVar] = _typevar_reduce + _dispatch_table[_collections_abc.dict_keys] = _dict_keys_reduce + _dispatch_table[_collections_abc.dict_values] = _dict_values_reduce + _dispatch_table[_collections_abc.dict_items] = _dict_items_reduce + _dispatch_table[type(OrderedDict().keys())] = _odict_keys_reduce + _dispatch_table[type(OrderedDict().values())] = _odict_values_reduce + _dispatch_table[type(OrderedDict().items())] = _odict_items_reduce + _dispatch_table[abc.abstractmethod] = _classmethod_reduce + _dispatch_table[abc.abstractclassmethod] = _classmethod_reduce + _dispatch_table[abc.abstractstaticmethod] = _classmethod_reduce + _dispatch_table[abc.abstractproperty] = _property_reduce + _dispatch_table[dataclasses._FIELD_BASE] = _dataclass_field_base_reduce + + dispatch_table = ChainMap(_dispatch_table, copyreg.dispatch_table) + + # function reducers are defined as instance methods of cloudpickle.Pickler + # objects, as they rely on a cloudpickle.Pickler attribute (globals_ref) + def _dynamic_function_reduce(self, func): + """Reduce a function that is not pickleable via attribute lookup.""" + newargs = self._function_getnewargs(func) + state = _function_getstate(func) + return (_make_function, newargs, state, None, None, _function_setstate) + + def _function_reduce(self, obj): + """Reducer for function objects. + + If obj is a top-level attribute of a file-backed module, this reducer + returns NotImplemented, making the cloudpickle.Pickler fall back to + traditional pickle.Pickler routines to save obj. Otherwise, it reduces + obj using a custom cloudpickle reducer designed specifically to handle + dynamic functions. + """ + if _should_pickle_by_reference(obj): + return NotImplemented + else: + return self._dynamic_function_reduce(obj) + + def _function_getnewargs(self, func): + code = func.__code__ + + # base_globals represents the future global namespace of func at + # unpickling time. Looking it up and storing it in + # cloudpickle.Pickler.globals_ref allow functions sharing the same + # globals at pickling time to also share them once unpickled, at one + # condition: since globals_ref is an attribute of a cloudpickle.Pickler + # instance, and that a new cloudpickle.Pickler is created each time + # cloudpickle.dump or cloudpickle.dumps is called, functions also need + # to be saved within the same invocation of + # cloudpickle.dump/cloudpickle.dumps (for example: + # cloudpickle.dumps([f1, f2])). There is no such limitation when using + # cloudpickle.Pickler.dump, as long as the multiple invocations are + # bound to the same cloudpickle.Pickler instance. + base_globals = self.globals_ref.setdefault(id(func.__globals__), {}) + + if base_globals == {}: + # Add module attributes used to resolve relative imports + # instructions inside func. + for k in ["__package__", "__name__", "__path__", "__file__"]: + if k in func.__globals__: + base_globals[k] = func.__globals__[k] + + # Do not bind the free variables before the function is created to + # avoid infinite recursion. + if func.__closure__ is None: + closure = None + else: + closure = tuple(_make_empty_cell() for _ in range(len(code.co_freevars))) + + return code, base_globals, None, None, closure + + def dump(self, obj): + try: + return super().dump(obj) + except RuntimeError as e: + if len(e.args) > 0 and "recursion" in e.args[0]: + msg = "Could not pickle object as excessively deep recursion required." + raise pickle.PicklingError(msg) from e + else: + raise + + def __init__(self, file, protocol=None, buffer_callback=None): + if protocol is None: + protocol = DEFAULT_PROTOCOL + super().__init__(file, protocol=protocol, buffer_callback=buffer_callback) + # map functions __globals__ attribute ids, to ensure that functions + # sharing the same global namespace at pickling time also share + # their global namespace at unpickling time. + self.globals_ref = {} + self.proto = int(protocol) + + if not PYPY: + # pickle.Pickler is the C implementation of the CPython pickler and + # therefore we rely on reduce_override method to customize the pickler + # behavior. + + # `cloudpickle.Pickler.dispatch` is only left for backward + # compatibility - note that when using protocol 5, + # `cloudpickle.Pickler.dispatch` is not an extension of + # `pickle._Pickler.dispatch` dictionary, because `cloudpickle.Pickler` + # subclasses the C-implemented `pickle.Pickler`, which does not expose + # a `dispatch` attribute. Earlier versions of `cloudpickle.Pickler` + # used `cloudpickle.Pickler.dispatch` as a class-level attribute + # storing all reducers implemented by cloudpickle, but the attribute + # name was not a great choice given because it would collide with a + # similarly named attribute in the pure-Python `pickle._Pickler` + # implementation in the standard library. + dispatch = dispatch_table + + # Implementation of the reducer_override callback, in order to + # efficiently serialize dynamic functions and classes by subclassing + # the C-implemented `pickle.Pickler`. + # TODO: decorrelate reducer_override (which is tied to CPython's + # implementation - would it make sense to backport it to pypy? - and + # pickle's protocol 5 which is implementation agnostic. Currently, the + # availability of both notions coincide on CPython's pickle, but it may + # not be the case anymore when pypy implements protocol 5. + + def reducer_override(self, obj): + """Type-agnostic reducing callback for function and classes. + + For performance reasons, subclasses of the C `pickle.Pickler` class + cannot register custom reducers for functions and classes in the + dispatch_table attribute. Reducers for such types must instead + implemented via the special `reducer_override` method. + + Note that this method will be called for any object except a few + builtin-types (int, lists, dicts etc.), which differs from reducers + in the Pickler's dispatch_table, each of them being invoked for + objects of a specific type only. + + This property comes in handy for classes: although most classes are + instances of the ``type`` metaclass, some of them can be instances + of other custom metaclasses (such as enum.EnumMeta for example). In + particular, the metaclass will likely not be known in advance, and + thus cannot be special-cased using an entry in the dispatch_table. + reducer_override, among other things, allows us to register a + reducer that will be called for any class, independently of its + type. + + Notes: + + * reducer_override has the priority over dispatch_table-registered + reducers. + * reducer_override can be used to fix other limitations of + cloudpickle for other types that suffered from type-specific + reducers, such as Exceptions. See + https://github.com/cloudpipe/cloudpickle/issues/248 + """ + t = type(obj) + try: + is_anyclass = issubclass(t, type) + except TypeError: # t is not a class (old Boost; see SF #502085) + is_anyclass = False + + if is_anyclass: + return _class_reduce(obj) + elif isinstance(obj, types.FunctionType): + return self._function_reduce(obj) + else: + # fallback to save_global, including the Pickler's + # dispatch_table + return NotImplemented + + else: + # When reducer_override is not available, hack the pure-Python + # Pickler's types.FunctionType and type savers. Note: the type saver + # must override Pickler.save_global, because pickle.py contains a + # hard-coded call to save_global when pickling meta-classes. + dispatch = pickle.Pickler.dispatch.copy() + + def _save_reduce_pickle5( + self, + func, + args, + state=None, + listitems=None, + dictitems=None, + state_setter=None, + obj=None, + ): + save = self.save + write = self.write + self.save_reduce( + func, + args, + state=None, + listitems=listitems, + dictitems=dictitems, + obj=obj, + ) + # backport of the Python 3.8 state_setter pickle operations + save(state_setter) + save(obj) # simple BINGET opcode as obj is already memoized. + save(state) + write(pickle.TUPLE2) + # Trigger a state_setter(obj, state) function call. + write(pickle.REDUCE) + # The purpose of state_setter is to carry-out an + # inplace modification of obj. We do not care about what the + # method might return, so its output is eventually removed from + # the stack. + write(pickle.POP) + + def save_global(self, obj, name=None, pack=struct.pack): + """Main dispatch method. + + The name of this method is somewhat misleading: all types get + dispatched here. + """ + if obj is type(None): # noqa + return self.save_reduce(type, (None,), obj=obj) + elif obj is type(Ellipsis): + return self.save_reduce(type, (Ellipsis,), obj=obj) + elif obj is type(NotImplemented): + return self.save_reduce(type, (NotImplemented,), obj=obj) + elif obj in _BUILTIN_TYPE_NAMES: + return self.save_reduce( + _builtin_type, (_BUILTIN_TYPE_NAMES[obj],), obj=obj + ) + + if name is not None: + super().save_global(obj, name=name) + elif not _should_pickle_by_reference(obj, name=name): + self._save_reduce_pickle5(*_dynamic_class_reduce(obj), obj=obj) + else: + super().save_global(obj, name=name) + + dispatch[type] = save_global + + def save_function(self, obj, name=None): + """Registered with the dispatch to handle all function types. + + Determines what kind of function obj is (e.g. lambda, defined at + interactive prompt, etc) and handles the pickling appropriately. + """ + if _should_pickle_by_reference(obj, name=name): + return super().save_global(obj, name=name) + elif PYPY and isinstance(obj.__code__, builtin_code_type): + return self.save_pypy_builtin_func(obj) + else: + return self._save_reduce_pickle5( + *self._dynamic_function_reduce(obj), obj=obj + ) + + def save_pypy_builtin_func(self, obj): + """Save pypy equivalent of builtin functions. + + PyPy does not have the concept of builtin-functions. Instead, + builtin-functions are simple function instances, but with a + builtin-code attribute. + Most of the time, builtin functions should be pickled by attribute. + But PyPy has flaky support for __qualname__, so some builtin + functions such as float.__new__ will be classified as dynamic. For + this reason only, we created this special routine. Because + builtin-functions are not expected to have closure or globals, + there is no additional hack (compared the one already implemented + in pickle) to protect ourselves from reference cycles. A simple + (reconstructor, newargs, obj.__dict__) tuple is save_reduced. Note + also that PyPy improved their support for __qualname__ in v3.6, so + this routing should be removed when cloudpickle supports only PyPy + 3.6 and later. + """ + rv = ( + types.FunctionType, + (obj.__code__, {}, obj.__name__, obj.__defaults__, obj.__closure__), + obj.__dict__, + ) + self.save_reduce(*rv, obj=obj) + + dispatch[types.FunctionType] = save_function + + +# Shorthands similar to pickle.dump/pickle.dumps + + +def dump(obj, file, protocol=None, buffer_callback=None): + """Serialize obj as bytes streamed into file + + protocol defaults to cloudpickle.DEFAULT_PROTOCOL which is an alias to + pickle.HIGHEST_PROTOCOL. This setting favors maximum communication + speed between processes running the same Python version. + + Set protocol=pickle.DEFAULT_PROTOCOL instead if you need to ensure + compatibility with older versions of Python (although this is not always + guaranteed to work because cloudpickle relies on some internal + implementation details that can change from one Python version to the + next). + """ + Pickler(file, protocol=protocol, buffer_callback=buffer_callback).dump(obj) + + +def dumps(obj, protocol=None, buffer_callback=None): + """Serialize obj as a string of bytes allocated in memory + + protocol defaults to cloudpickle.DEFAULT_PROTOCOL which is an alias to + pickle.HIGHEST_PROTOCOL. This setting favors maximum communication + speed between processes running the same Python version. + + Set protocol=pickle.DEFAULT_PROTOCOL instead if you need to ensure + compatibility with older versions of Python (although this is not always + guaranteed to work because cloudpickle relies on some internal + implementation details that can change from one Python version to the + next). + """ + with io.BytesIO() as file: + cp = Pickler(file, protocol=protocol, buffer_callback=buffer_callback) + cp.dump(obj) + return file.getvalue() + + +# Include pickles unloading functions in this namespace for convenience. +load, loads = pickle.load, pickle.loads + +# Backward compat alias. +CloudPickler = Pickler diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle_fast.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle_fast.py new file mode 100644 index 0000000..52d6732 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/cloudpickle/cloudpickle_fast.py @@ -0,0 +1,13 @@ +"""Compatibility module. + +It can be necessary to load files generated by previous versions of cloudpickle +that rely on symbols being defined under the `cloudpickle.cloudpickle_fast` +namespace. + +See: tests/test_backward_compat.py +""" +from . import cloudpickle + + +def __getattr__(name): + return getattr(cloudpickle, name) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__init__.py new file mode 100644 index 0000000..5886d2a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__init__.py @@ -0,0 +1,44 @@ +r"""The :mod:`loky` module manages a pool of worker that can be re-used across time. +It provides a robust and dynamic implementation os the +:class:`ProcessPoolExecutor` and a function :func:`get_reusable_executor` which +hide the pool management under the hood. +""" +from concurrent.futures import ( + ALL_COMPLETED, + FIRST_COMPLETED, + FIRST_EXCEPTION, + CancelledError, + Executor, + TimeoutError, + as_completed, + wait, +) + +from ._base import Future +from .backend.context import cpu_count +from .backend.reduction import set_loky_pickler +from .reusable_executor import get_reusable_executor +from .cloudpickle_wrapper import wrap_non_picklable_objects +from .process_executor import BrokenProcessPool, ProcessPoolExecutor + + +__all__ = [ + "get_reusable_executor", + "cpu_count", + "wait", + "as_completed", + "Future", + "Executor", + "ProcessPoolExecutor", + "BrokenProcessPool", + "CancelledError", + "TimeoutError", + "FIRST_COMPLETED", + "FIRST_EXCEPTION", + "ALL_COMPLETED", + "wrap_non_picklable_objects", + "set_loky_pickler", +] + + +__version__ = "3.4.1" diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..21f36c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/_base.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/_base.cpython-310.pyc new file mode 100644 index 0000000..f3df8e2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/_base.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/cloudpickle_wrapper.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/cloudpickle_wrapper.cpython-310.pyc new file mode 100644 index 0000000..b2a1a66 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/cloudpickle_wrapper.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/initializers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/initializers.cpython-310.pyc new file mode 100644 index 0000000..fe240e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/initializers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/process_executor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/process_executor.cpython-310.pyc new file mode 100644 index 0000000..2f8686e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/process_executor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/reusable_executor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/reusable_executor.cpython-310.pyc new file mode 100644 index 0000000..e0f2cb1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/__pycache__/reusable_executor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/_base.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/_base.py new file mode 100644 index 0000000..da0abc1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/_base.py @@ -0,0 +1,28 @@ +############################################################################### +# Modification of concurrent.futures.Future +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from concurrent/futures/_base.py (17/02/2017) +# * Do not use yield from +# * Use old super syntax +# +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +from concurrent.futures import Future as _BaseFuture +from concurrent.futures._base import LOGGER + + +# To make loky._base.Future instances awaitable by concurrent.futures.wait, +# derive our custom Future class from _BaseFuture. _invoke_callback is the only +# modification made to this class in loky. +# TODO investigate why using `concurrent.futures.Future` directly does not +# always work in our test suite. +class Future(_BaseFuture): + def _invoke_callbacks(self): + for callback in self._done_callbacks: + try: + callback(self) + except BaseException: + LOGGER.exception(f"exception calling callback for {self!r}") diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__init__.py new file mode 100644 index 0000000..d339aa6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__init__.py @@ -0,0 +1,14 @@ +import os +from multiprocessing import synchronize + +from .context import get_context + + +def _make_name(): + return f"/loky-{os.getpid()}-{next(synchronize.SemLock._rand)}" + + +# monkey patch the name creation for multiprocessing +synchronize.SemLock._make_name = staticmethod(_make_name) + +__all__ = ["get_context"] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..13a8c15 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_posix_reduction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_posix_reduction.cpython-310.pyc new file mode 100644 index 0000000..60ca4c5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_posix_reduction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_win_reduction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_win_reduction.cpython-310.pyc new file mode 100644 index 0000000..d08a10f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/_win_reduction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/context.cpython-310.pyc new file mode 100644 index 0000000..a1472a6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/fork_exec.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/fork_exec.cpython-310.pyc new file mode 100644 index 0000000..c7a6091 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/fork_exec.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_posix.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_posix.cpython-310.pyc new file mode 100644 index 0000000..3f8010d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_posix.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_win32.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_win32.cpython-310.pyc new file mode 100644 index 0000000..dbf2b2c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/popen_loky_win32.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/process.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/process.cpython-310.pyc new file mode 100644 index 0000000..ba37f8d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/process.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/queues.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/queues.cpython-310.pyc new file mode 100644 index 0000000..965e610 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/queues.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/reduction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/reduction.cpython-310.pyc new file mode 100644 index 0000000..9152f12 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/reduction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/resource_tracker.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/resource_tracker.cpython-310.pyc new file mode 100644 index 0000000..6313fbf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/resource_tracker.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/spawn.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/spawn.cpython-310.pyc new file mode 100644 index 0000000..8a9c9b5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/spawn.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/synchronize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/synchronize.cpython-310.pyc new file mode 100644 index 0000000..bbaa934 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/synchronize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..58275a3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_posix_reduction.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_posix_reduction.py new file mode 100644 index 0000000..4b800ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_posix_reduction.py @@ -0,0 +1,67 @@ +############################################################################### +# Extra reducers for Unix based system and connections objects +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from multiprocessing/reduction.py (17/02/2017) +# * Add adapted reduction for LokyProcesses and socket/Connection +# +import os +import socket +import _socket +from multiprocessing.connection import Connection +from multiprocessing.context import get_spawning_popen + +from .reduction import register + +HAVE_SEND_HANDLE = ( + hasattr(socket, "CMSG_LEN") + and hasattr(socket, "SCM_RIGHTS") + and hasattr(socket.socket, "sendmsg") +) + + +def _mk_inheritable(fd): + os.set_inheritable(fd, True) + return fd + + +def DupFd(fd): + """Return a wrapper for an fd.""" + popen_obj = get_spawning_popen() + if popen_obj is not None: + return popen_obj.DupFd(popen_obj.duplicate_for_child(fd)) + elif HAVE_SEND_HANDLE: + from multiprocessing import resource_sharer + + return resource_sharer.DupFd(fd) + else: + raise TypeError( + "Cannot pickle connection object. This object can only be " + "passed when spawning a new process" + ) + + +def _reduce_socket(s): + df = DupFd(s.fileno()) + return _rebuild_socket, (df, s.family, s.type, s.proto) + + +def _rebuild_socket(df, family, type, proto): + fd = df.detach() + return socket.fromfd(fd, family, type, proto) + + +def rebuild_connection(df, readable, writable): + fd = df.detach() + return Connection(fd, readable, writable) + + +def reduce_connection(conn): + df = DupFd(conn.fileno()) + return rebuild_connection, (df, conn.readable, conn.writable) + + +register(socket.socket, _reduce_socket) +register(_socket.socket, _reduce_socket) +register(Connection, reduce_connection) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_win_reduction.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_win_reduction.py new file mode 100644 index 0000000..506d0ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/_win_reduction.py @@ -0,0 +1,18 @@ +############################################################################### +# Extra reducers for Windows system and connections objects +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from multiprocessing/reduction.py (17/02/2017) +# * Add adapted reduction for LokyProcesses and socket/PipeConnection +# +import socket +from multiprocessing import connection +from multiprocessing.reduction import _reduce_socket + +from .reduction import register + +# register reduction for win32 communication objects +register(socket.socket, _reduce_socket) +register(connection.Connection, connection.reduce_connection) +register(connection.PipeConnection, connection.reduce_pipe_connection) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/context.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/context.py new file mode 100644 index 0000000..d0f5903 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/context.py @@ -0,0 +1,378 @@ +############################################################################### +# Basic context management with LokyContext +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from multiprocessing/context.py +# * Create a context ensuring loky uses only objects that are compatible +# * Add LokyContext to the list of context of multiprocessing so loky can be +# used with multiprocessing.set_start_method +# * Implement a CFS-aware amd physical-core aware cpu_count function. +# +import os +import sys +import math +import subprocess +import traceback +import warnings +import multiprocessing as mp +from multiprocessing import get_context as mp_get_context +from multiprocessing.context import BaseContext + + +from .process import LokyProcess, LokyInitMainProcess + +# Apparently, on older Python versions, loky cannot work 61 workers on Windows +# but instead 60: ¯\_(ツ)_/¯ +if sys.version_info >= (3, 8): + from concurrent.futures.process import _MAX_WINDOWS_WORKERS + + if sys.version_info < (3, 10): + _MAX_WINDOWS_WORKERS = _MAX_WINDOWS_WORKERS - 1 +else: + # compat for versions before 3.8 which do not define this. + _MAX_WINDOWS_WORKERS = 60 + +START_METHODS = ["loky", "loky_init_main", "spawn"] +if sys.platform != "win32": + START_METHODS += ["fork", "forkserver"] + +_DEFAULT_START_METHOD = None + +# Cache for the number of physical cores to avoid repeating subprocess calls. +# It should not change during the lifetime of the program. +physical_cores_cache = None + + +def get_context(method=None): + # Try to overload the default context + method = method or _DEFAULT_START_METHOD or "loky" + if method == "fork": + # If 'fork' is explicitly requested, warn user about potential issues. + warnings.warn( + "`fork` start method should not be used with " + "`loky` as it does not respect POSIX. Try using " + "`spawn` or `loky` instead.", + UserWarning, + ) + try: + return mp_get_context(method) + except ValueError: + raise ValueError( + f"Unknown context '{method}'. Value should be in " + f"{START_METHODS}." + ) + + +def set_start_method(method, force=False): + global _DEFAULT_START_METHOD + if _DEFAULT_START_METHOD is not None and not force: + raise RuntimeError("context has already been set") + assert method is None or method in START_METHODS, ( + f"'{method}' is not a valid start_method. It should be in " + f"{START_METHODS}" + ) + + _DEFAULT_START_METHOD = method + + +def get_start_method(): + return _DEFAULT_START_METHOD + + +def cpu_count(only_physical_cores=False): + """Return the number of CPUs the current process can use. + + The returned number of CPUs accounts for: + * the number of CPUs in the system, as given by + ``multiprocessing.cpu_count``; + * the CPU affinity settings of the current process + (available on some Unix systems); + * Cgroup CPU bandwidth limit (available on Linux only, typically + set by docker and similar container orchestration systems); + * the value of the LOKY_MAX_CPU_COUNT environment variable if defined. + and is given as the minimum of these constraints. + + If ``only_physical_cores`` is True, return the number of physical cores + instead of the number of logical cores (hyperthreading / SMT). Note that + this option is not enforced if the number of usable cores is controlled in + any other way such as: process affinity, Cgroup restricted CPU bandwidth + or the LOKY_MAX_CPU_COUNT environment variable. If the number of physical + cores is not found, return the number of logical cores. + + Note that on Windows, the returned number of CPUs cannot exceed 61 (or 60 for + Python < 3.10), see: + https://bugs.python.org/issue26903. + + It is also always larger or equal to 1. + """ + # Note: os.cpu_count() is allowed to return None in its docstring + os_cpu_count = os.cpu_count() or 1 + if sys.platform == "win32": + # On Windows, attempting to use more than 61 CPUs would result in a + # OS-level error. See https://bugs.python.org/issue26903. According to + # https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups + # it might be possible to go beyond with a lot of extra work but this + # does not look easy. + os_cpu_count = min(os_cpu_count, _MAX_WINDOWS_WORKERS) + + cpu_count_user = _cpu_count_user(os_cpu_count) + aggregate_cpu_count = max(min(os_cpu_count, cpu_count_user), 1) + + if not only_physical_cores: + return aggregate_cpu_count + + if cpu_count_user < os_cpu_count: + # Respect user setting + return max(cpu_count_user, 1) + + cpu_count_physical, exception = _count_physical_cores() + if cpu_count_physical != "not found": + return cpu_count_physical + + # Fallback to default behavior + if exception is not None: + # warns only the first time + warnings.warn( + "Could not find the number of physical cores for the " + f"following reason:\n{exception}\n" + "Returning the number of logical cores instead. You can " + "silence this warning by setting LOKY_MAX_CPU_COUNT to " + "the number of cores you want to use." + ) + traceback.print_tb(exception.__traceback__) + + return aggregate_cpu_count + + +def _cpu_count_cgroup(os_cpu_count): + # Cgroup CPU bandwidth limit available in Linux since 2.6 kernel + cpu_max_fname = "/sys/fs/cgroup/cpu.max" + cfs_quota_fname = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" + cfs_period_fname = "/sys/fs/cgroup/cpu/cpu.cfs_period_us" + if os.path.exists(cpu_max_fname): + # cgroup v2 + # https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html + with open(cpu_max_fname) as fh: + cpu_quota_us, cpu_period_us = fh.read().strip().split() + elif os.path.exists(cfs_quota_fname) and os.path.exists(cfs_period_fname): + # cgroup v1 + # https://www.kernel.org/doc/html/latest/scheduler/sched-bwc.html#management + with open(cfs_quota_fname) as fh: + cpu_quota_us = fh.read().strip() + with open(cfs_period_fname) as fh: + cpu_period_us = fh.read().strip() + else: + # No Cgroup CPU bandwidth limit (e.g. non-Linux platform) + cpu_quota_us = "max" + cpu_period_us = 100_000 # unused, for consistency with default values + + if cpu_quota_us == "max": + # No active Cgroup quota on a Cgroup-capable platform + return os_cpu_count + else: + cpu_quota_us = int(cpu_quota_us) + cpu_period_us = int(cpu_period_us) + if cpu_quota_us > 0 and cpu_period_us > 0: + return math.ceil(cpu_quota_us / cpu_period_us) + else: # pragma: no cover + # Setting a negative cpu_quota_us value is a valid way to disable + # cgroup CPU bandwith limits + return os_cpu_count + + +def _cpu_count_affinity(os_cpu_count): + # Number of available CPUs given affinity settings + if hasattr(os, "sched_getaffinity"): + try: + return len(os.sched_getaffinity(0)) + except NotImplementedError: + pass + + # On PyPy and possibly other platforms, os.sched_getaffinity does not exist + # or raises NotImplementedError, let's try with the psutil if installed. + try: + import psutil + + p = psutil.Process() + if hasattr(p, "cpu_affinity"): + return len(p.cpu_affinity()) + + except ImportError: # pragma: no cover + if ( + sys.platform == "linux" + and os.environ.get("LOKY_MAX_CPU_COUNT") is None + ): + # PyPy does not implement os.sched_getaffinity on Linux which + # can cause severe oversubscription problems. Better warn the + # user in this particularly pathological case which can wreck + # havoc, typically on CI workers. + warnings.warn( + "Failed to inspect CPU affinity constraints on this system. " + "Please install psutil or explictly set LOKY_MAX_CPU_COUNT." + ) + + # This can happen for platforms that do not implement any kind of CPU + # infinity such as macOS-based platforms. + return os_cpu_count + + +def _cpu_count_user(os_cpu_count): + """Number of user defined available CPUs""" + cpu_count_affinity = _cpu_count_affinity(os_cpu_count) + + cpu_count_cgroup = _cpu_count_cgroup(os_cpu_count) + + # User defined soft-limit passed as a loky specific environment variable. + cpu_count_loky = int(os.environ.get("LOKY_MAX_CPU_COUNT", os_cpu_count)) + + return min(cpu_count_affinity, cpu_count_cgroup, cpu_count_loky) + + +def _count_physical_cores(): + """Return a tuple (number of physical cores, exception) + + If the number of physical cores is found, exception is set to None. + If it has not been found, return ("not found", exception). + + The number of physical cores is cached to avoid repeating subprocess calls. + """ + exception = None + + # First check if the value is cached + global physical_cores_cache + if physical_cores_cache is not None: + return physical_cores_cache, exception + + # Not cached yet, find it + try: + if sys.platform == "linux": + cpu_info = subprocess.run( + "lscpu --parse=core".split(), capture_output=True, text=True + ) + cpu_info = cpu_info.stdout.splitlines() + cpu_info = {line for line in cpu_info if not line.startswith("#")} + cpu_count_physical = len(cpu_info) + elif sys.platform == "win32": + cpu_info = subprocess.run( + "wmic CPU Get NumberOfCores /Format:csv".split(), + capture_output=True, + text=True, + ) + cpu_info = cpu_info.stdout.splitlines() + cpu_info = [ + l.split(",")[1] + for l in cpu_info + if (l and l != "Node,NumberOfCores") + ] + cpu_count_physical = sum(map(int, cpu_info)) + elif sys.platform == "darwin": + cpu_info = subprocess.run( + "sysctl -n hw.physicalcpu".split(), + capture_output=True, + text=True, + ) + cpu_info = cpu_info.stdout + cpu_count_physical = int(cpu_info) + else: + raise NotImplementedError(f"unsupported platform: {sys.platform}") + + # if cpu_count_physical < 1, we did not find a valid value + if cpu_count_physical < 1: + raise ValueError(f"found {cpu_count_physical} physical cores < 1") + + except Exception as e: + exception = e + cpu_count_physical = "not found" + + # Put the result in cache + physical_cores_cache = cpu_count_physical + + return cpu_count_physical, exception + + +class LokyContext(BaseContext): + """Context relying on the LokyProcess.""" + + _name = "loky" + Process = LokyProcess + cpu_count = staticmethod(cpu_count) + + def Queue(self, maxsize=0, reducers=None): + """Returns a queue object""" + from .queues import Queue + + return Queue(maxsize, reducers=reducers, ctx=self.get_context()) + + def SimpleQueue(self, reducers=None): + """Returns a queue object""" + from .queues import SimpleQueue + + return SimpleQueue(reducers=reducers, ctx=self.get_context()) + + if sys.platform != "win32": + """For Unix platform, use our custom implementation of synchronize + ensuring that we use the loky.backend.resource_tracker to clean-up + the semaphores in case of a worker crash. + """ + + def Semaphore(self, value=1): + """Returns a semaphore object""" + from .synchronize import Semaphore + + return Semaphore(value=value) + + def BoundedSemaphore(self, value): + """Returns a bounded semaphore object""" + from .synchronize import BoundedSemaphore + + return BoundedSemaphore(value) + + def Lock(self): + """Returns a lock object""" + from .synchronize import Lock + + return Lock() + + def RLock(self): + """Returns a recurrent lock object""" + from .synchronize import RLock + + return RLock() + + def Condition(self, lock=None): + """Returns a condition object""" + from .synchronize import Condition + + return Condition(lock) + + def Event(self): + """Returns an event object""" + from .synchronize import Event + + return Event() + + +class LokyInitMainContext(LokyContext): + """Extra context with LokyProcess, which does load the main module + + This context is used for compatibility in the case ``cloudpickle`` is not + present on the running system. This permits to load functions defined in + the ``main`` module, using proper safeguards. The declaration of the + ``executor`` should be protected by ``if __name__ == "__main__":`` and the + functions and variable used from main should be out of this block. + + This mimics the default behavior of multiprocessing under Windows and the + behavior of the ``spawn`` start method on a posix system. + For more details, see the end of the following section of python doc + https://docs.python.org/3/library/multiprocessing.html#multiprocessing-programming + """ + + _name = "loky_init_main" + Process = LokyInitMainProcess + + +# Register loky context so it works with multiprocessing.get_context +ctx_loky = LokyContext() +mp.context._concrete_contexts["loky"] = ctx_loky +mp.context._concrete_contexts["loky_init_main"] = LokyInitMainContext() diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/fork_exec.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/fork_exec.py new file mode 100644 index 0000000..2353c42 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/fork_exec.py @@ -0,0 +1,43 @@ +############################################################################### +# Launch a subprocess using forkexec and make sure only the needed fd are +# shared in the two process. +# +# author: Thomas Moreau and Olivier Grisel +# +import os +import sys + + +def close_fds(keep_fds): # pragma: no cover + """Close all the file descriptors except those in keep_fds.""" + + # Make sure to keep stdout and stderr open for logging purpose + keep_fds = {*keep_fds, 1, 2} + + # We try to retrieve all the open fds + try: + open_fds = {int(fd) for fd in os.listdir("/proc/self/fd")} + except FileNotFoundError: + import resource + + max_nfds = resource.getrlimit(resource.RLIMIT_NOFILE)[0] + open_fds = {*range(max_nfds)} + + for i in open_fds - keep_fds: + try: + os.close(i) + except OSError: + pass + + +def fork_exec(cmd, keep_fds, env=None): + # copy the environment variables to set in the child process + env = env or {} + child_env = {**os.environ, **env} + + pid = os.fork() + if pid == 0: # pragma: no cover + close_fds(keep_fds) + os.execve(sys.executable, cmd, child_env) + else: + return pid diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_posix.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_posix.py new file mode 100644 index 0000000..74395be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_posix.py @@ -0,0 +1,193 @@ +############################################################################### +# Popen for LokyProcess. +# +# author: Thomas Moreau and Olivier Grisel +# +import os +import sys +import signal +import pickle +from io import BytesIO +from multiprocessing import util, process +from multiprocessing.connection import wait +from multiprocessing.context import set_spawning_popen + +from . import reduction, resource_tracker, spawn + + +__all__ = ["Popen"] + + +# +# Wrapper for an fd used while launching a process +# + + +class _DupFd: + def __init__(self, fd): + self.fd = reduction._mk_inheritable(fd) + + def detach(self): + return self.fd + + +# +# Start child process using subprocess.Popen +# + + +class Popen: + method = "loky" + DupFd = _DupFd + + def __init__(self, process_obj): + sys.stdout.flush() + sys.stderr.flush() + self.returncode = None + self._fds = [] + self._launch(process_obj) + + def duplicate_for_child(self, fd): + self._fds.append(fd) + return reduction._mk_inheritable(fd) + + def poll(self, flag=os.WNOHANG): + if self.returncode is None: + while True: + try: + pid, sts = os.waitpid(self.pid, flag) + except OSError: + # Child process not yet created. See #1731717 + # e.errno == errno.ECHILD == 10 + return None + else: + break + if pid == self.pid: + if os.WIFSIGNALED(sts): + self.returncode = -os.WTERMSIG(sts) + else: + assert os.WIFEXITED(sts) + self.returncode = os.WEXITSTATUS(sts) + return self.returncode + + def wait(self, timeout=None): + if self.returncode is None: + if timeout is not None: + if not wait([self.sentinel], timeout): + return None + # This shouldn't block if wait() returned successfully. + return self.poll(os.WNOHANG if timeout == 0.0 else 0) + return self.returncode + + def terminate(self): + if self.returncode is None: + try: + os.kill(self.pid, signal.SIGTERM) + except ProcessLookupError: + pass + except OSError: + if self.wait(timeout=0.1) is None: + raise + + def _launch(self, process_obj): + + tracker_fd = resource_tracker._resource_tracker.getfd() + + fp = BytesIO() + set_spawning_popen(self) + try: + prep_data = spawn.get_preparation_data( + process_obj._name, + getattr(process_obj, "init_main_module", True), + ) + reduction.dump(prep_data, fp) + reduction.dump(process_obj, fp) + + finally: + set_spawning_popen(None) + + try: + parent_r, child_w = os.pipe() + child_r, parent_w = os.pipe() + # for fd in self._fds: + # _mk_inheritable(fd) + + cmd_python = [sys.executable] + cmd_python += ["-m", self.__module__] + cmd_python += ["--process-name", str(process_obj.name)] + cmd_python += ["--pipe", str(reduction._mk_inheritable(child_r))] + reduction._mk_inheritable(child_w) + reduction._mk_inheritable(tracker_fd) + self._fds += [child_r, child_w, tracker_fd] + if sys.version_info >= (3, 8) and os.name == "posix": + mp_tracker_fd = prep_data["mp_tracker_args"]["fd"] + self.duplicate_for_child(mp_tracker_fd) + + from .fork_exec import fork_exec + + pid = fork_exec(cmd_python, self._fds, env=process_obj.env) + util.debug( + f"launched python with pid {pid} and cmd:\n{cmd_python}" + ) + self.sentinel = parent_r + + method = "getbuffer" + if not hasattr(fp, method): + method = "getvalue" + with os.fdopen(parent_w, "wb") as f: + f.write(getattr(fp, method)()) + self.pid = pid + finally: + if parent_r is not None: + util.Finalize(self, os.close, (parent_r,)) + for fd in (child_r, child_w): + if fd is not None: + os.close(fd) + + @staticmethod + def thread_is_spawning(): + return True + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser("Command line parser") + parser.add_argument( + "--pipe", type=int, required=True, help="File handle for the pipe" + ) + parser.add_argument( + "--process-name", + type=str, + default=None, + help="Identifier for debugging purpose", + ) + + args = parser.parse_args() + + info = {} + exitcode = 1 + try: + with os.fdopen(args.pipe, "rb") as from_parent: + process.current_process()._inheriting = True + try: + prep_data = pickle.load(from_parent) + spawn.prepare(prep_data) + process_obj = pickle.load(from_parent) + finally: + del process.current_process()._inheriting + + exitcode = process_obj._bootstrap() + except Exception: + print("\n\n" + "-" * 80) + print(f"{args.process_name} failed with traceback: ") + print("-" * 80) + import traceback + + print(traceback.format_exc()) + print("\n" + "-" * 80) + finally: + if from_parent is not None: + from_parent.close() + + sys.exit(exitcode) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_win32.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_win32.py new file mode 100644 index 0000000..4f85f65 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/popen_loky_win32.py @@ -0,0 +1,173 @@ +import os +import sys +import msvcrt +import _winapi +from pickle import load +from multiprocessing import process, util +from multiprocessing.context import set_spawning_popen +from multiprocessing.popen_spawn_win32 import Popen as _Popen + +from . import reduction, spawn + + +__all__ = ["Popen"] + +# +# +# + + +def _path_eq(p1, p2): + return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2) + + +WINENV = hasattr(sys, "_base_executable") and not _path_eq( + sys.executable, sys._base_executable +) + + +def _close_handles(*handles): + for handle in handles: + _winapi.CloseHandle(handle) + + +# +# We define a Popen class similar to the one from subprocess, but +# whose constructor takes a process object as its argument. +# + + +class Popen(_Popen): + """ + Start a subprocess to run the code of a process object. + + We differ from cpython implementation with the way we handle environment + variables, in order to be able to modify then in the child processes before + importing any library, in order to control the number of threads in C-level + threadpools. + + We also use the loky preparation data, in particular to handle main_module + inits and the loky resource tracker. + """ + + method = "loky" + + def __init__(self, process_obj): + prep_data = spawn.get_preparation_data( + process_obj._name, getattr(process_obj, "init_main_module", True) + ) + + # read end of pipe will be duplicated by the child process + # -- see spawn_main() in spawn.py. + # + # bpo-33929: Previously, the read end of pipe was "stolen" by the child + # process, but it leaked a handle if the child process had been + # terminated before it could steal the handle from the parent process. + rhandle, whandle = _winapi.CreatePipe(None, 0) + wfd = msvcrt.open_osfhandle(whandle, 0) + cmd = get_command_line(parent_pid=os.getpid(), pipe_handle=rhandle) + + python_exe = spawn.get_executable() + + # copy the environment variables to set in the child process + child_env = {**os.environ, **process_obj.env} + + # bpo-35797: When running in a venv, we bypass the redirect + # executor and launch our base Python. + if WINENV and _path_eq(python_exe, sys.executable): + cmd[0] = python_exe = sys._base_executable + child_env["__PYVENV_LAUNCHER__"] = sys.executable + + cmd = " ".join(f'"{x}"' for x in cmd) + + with open(wfd, "wb") as to_child: + # start process + try: + hp, ht, pid, _ = _winapi.CreateProcess( + python_exe, + cmd, + None, + None, + False, + 0, + child_env, + None, + None, + ) + _winapi.CloseHandle(ht) + except BaseException: + _winapi.CloseHandle(rhandle) + raise + + # set attributes of self + self.pid = pid + self.returncode = None + self._handle = hp + self.sentinel = int(hp) + self.finalizer = util.Finalize( + self, _close_handles, (self.sentinel, int(rhandle)) + ) + + # send information to child + set_spawning_popen(self) + try: + reduction.dump(prep_data, to_child) + reduction.dump(process_obj, to_child) + finally: + set_spawning_popen(None) + + +def get_command_line(pipe_handle, parent_pid, **kwds): + """Returns prefix of command line used for spawning a child process.""" + if getattr(sys, "frozen", False): + return [sys.executable, "--multiprocessing-fork", pipe_handle] + else: + prog = ( + "from joblib.externals.loky.backend.popen_loky_win32 import main; " + f"main(pipe_handle={pipe_handle}, parent_pid={parent_pid})" + ) + opts = util._args_from_interpreter_flags() + return [ + spawn.get_executable(), + *opts, + "-c", + prog, + "--multiprocessing-fork", + ] + + +def is_forking(argv): + """Return whether commandline indicates we are forking.""" + if len(argv) >= 2 and argv[1] == "--multiprocessing-fork": + return True + else: + return False + + +def main(pipe_handle, parent_pid=None): + """Run code specified by data received over pipe.""" + assert is_forking(sys.argv), "Not forking" + + if parent_pid is not None: + source_process = _winapi.OpenProcess( + _winapi.SYNCHRONIZE | _winapi.PROCESS_DUP_HANDLE, False, parent_pid + ) + else: + source_process = None + new_handle = reduction.duplicate( + pipe_handle, source_process=source_process + ) + fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY) + parent_sentinel = source_process + + with os.fdopen(fd, "rb", closefd=True) as from_parent: + process.current_process()._inheriting = True + try: + preparation_data = load(from_parent) + spawn.prepare(preparation_data, parent_sentinel) + self = load(from_parent) + finally: + del process.current_process()._inheriting + + exitcode = self._bootstrap(parent_sentinel) + sys.exit(exitcode) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/process.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/process.py new file mode 100644 index 0000000..3562550 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/process.py @@ -0,0 +1,85 @@ +############################################################################### +# LokyProcess implementation +# +# authors: Thomas Moreau and Olivier Grisel +# +# based on multiprocessing/process.py (17/02/2017) +# +import sys +from multiprocessing.context import assert_spawning +from multiprocessing.process import BaseProcess + + +class LokyProcess(BaseProcess): + _start_method = "loky" + + def __init__( + self, + group=None, + target=None, + name=None, + args=(), + kwargs={}, + daemon=None, + init_main_module=False, + env=None, + ): + super().__init__( + group=group, + target=target, + name=name, + args=args, + kwargs=kwargs, + daemon=daemon, + ) + self.env = {} if env is None else env + self.authkey = self.authkey + self.init_main_module = init_main_module + + @staticmethod + def _Popen(process_obj): + if sys.platform == "win32": + from .popen_loky_win32 import Popen + else: + from .popen_loky_posix import Popen + return Popen(process_obj) + + +class LokyInitMainProcess(LokyProcess): + _start_method = "loky_init_main" + + def __init__( + self, + group=None, + target=None, + name=None, + args=(), + kwargs={}, + daemon=None, + ): + super().__init__( + group=group, + target=target, + name=name, + args=args, + kwargs=kwargs, + daemon=daemon, + init_main_module=True, + ) + + +# +# We subclass bytes to avoid accidental transmission of auth keys over network +# + + +class AuthenticationKey(bytes): + def __reduce__(self): + try: + assert_spawning(self) + except RuntimeError: + raise TypeError( + "Pickling an AuthenticationKey object is " + "disallowed for security reasons" + ) + return AuthenticationKey, (bytes(self),) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/queues.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/queues.py new file mode 100644 index 0000000..5afd99b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/queues.py @@ -0,0 +1,236 @@ +############################################################################### +# Queue and SimpleQueue implementation for loky +# +# authors: Thomas Moreau, Olivier Grisel +# +# based on multiprocessing/queues.py (16/02/2017) +# * Add some custom reducers for the Queues/SimpleQueue to tweak the +# pickling process. (overload Queue._feed/SimpleQueue.put) +# +import os +import sys +import errno +import weakref +import threading +from multiprocessing import util +from multiprocessing.queues import ( + Full, + Queue as mp_Queue, + SimpleQueue as mp_SimpleQueue, + _sentinel, +) +from multiprocessing.context import assert_spawning + +from .reduction import dumps + + +__all__ = ["Queue", "SimpleQueue", "Full"] + + +class Queue(mp_Queue): + def __init__(self, maxsize=0, reducers=None, ctx=None): + super().__init__(maxsize=maxsize, ctx=ctx) + self._reducers = reducers + + # Use custom queue set/get state to be able to reduce the custom reducers + def __getstate__(self): + assert_spawning(self) + return ( + self._ignore_epipe, + self._maxsize, + self._reader, + self._writer, + self._reducers, + self._rlock, + self._wlock, + self._sem, + self._opid, + ) + + def __setstate__(self, state): + ( + self._ignore_epipe, + self._maxsize, + self._reader, + self._writer, + self._reducers, + self._rlock, + self._wlock, + self._sem, + self._opid, + ) = state + if sys.version_info >= (3, 9): + self._reset() + else: + self._after_fork() + + # Overload _start_thread to correctly call our custom _feed + def _start_thread(self): + util.debug("Queue._start_thread()") + + # Start thread which transfers data from buffer to pipe + self._buffer.clear() + self._thread = threading.Thread( + target=Queue._feed, + args=( + self._buffer, + self._notempty, + self._send_bytes, + self._wlock, + self._writer.close, + self._reducers, + self._ignore_epipe, + self._on_queue_feeder_error, + self._sem, + ), + name="QueueFeederThread", + ) + self._thread.daemon = True + + util.debug("doing self._thread.start()") + self._thread.start() + util.debug("... done self._thread.start()") + + # On process exit we will wait for data to be flushed to pipe. + # + # However, if this process created the queue then all + # processes which use the queue will be descendants of this + # process. Therefore waiting for the queue to be flushed + # is pointless once all the child processes have been joined. + created_by_this_process = self._opid == os.getpid() + if not self._joincancelled and not created_by_this_process: + self._jointhread = util.Finalize( + self._thread, + Queue._finalize_join, + [weakref.ref(self._thread)], + exitpriority=-5, + ) + + # Send sentinel to the thread queue object when garbage collected + self._close = util.Finalize( + self, + Queue._finalize_close, + [self._buffer, self._notempty], + exitpriority=10, + ) + + # Overload the _feed methods to use our custom pickling strategy. + @staticmethod + def _feed( + buffer, + notempty, + send_bytes, + writelock, + close, + reducers, + ignore_epipe, + onerror, + queue_sem, + ): + util.debug("starting thread to feed data to pipe") + nacquire = notempty.acquire + nrelease = notempty.release + nwait = notempty.wait + bpopleft = buffer.popleft + sentinel = _sentinel + if sys.platform != "win32": + wacquire = writelock.acquire + wrelease = writelock.release + else: + wacquire = None + + while True: + try: + nacquire() + try: + if not buffer: + nwait() + finally: + nrelease() + try: + while True: + obj = bpopleft() + if obj is sentinel: + util.debug("feeder thread got sentinel -- exiting") + close() + return + + # serialize the data before acquiring the lock + obj_ = dumps(obj, reducers=reducers) + if wacquire is None: + send_bytes(obj_) + else: + wacquire() + try: + send_bytes(obj_) + finally: + wrelease() + # Remove references early to avoid leaking memory + del obj, obj_ + except IndexError: + pass + except BaseException as e: + if ignore_epipe and getattr(e, "errno", 0) == errno.EPIPE: + return + # Since this runs in a daemon thread the resources it uses + # may be become unusable while the process is cleaning up. + # We ignore errors which happen after the process has + # started to cleanup. + if util.is_exiting(): + util.info(f"error in queue thread: {e}") + return + else: + queue_sem.release() + onerror(e, obj) + + def _on_queue_feeder_error(self, e, obj): + """ + Private API hook called when feeding data in the background thread + raises an exception. For overriding by concurrent.futures. + """ + import traceback + + traceback.print_exc() + + +class SimpleQueue(mp_SimpleQueue): + def __init__(self, reducers=None, ctx=None): + super().__init__(ctx=ctx) + + # Add possiblity to use custom reducers + self._reducers = reducers + + def close(self): + self._reader.close() + self._writer.close() + + # Use custom queue set/get state to be able to reduce the custom reducers + def __getstate__(self): + assert_spawning(self) + return ( + self._reader, + self._writer, + self._reducers, + self._rlock, + self._wlock, + ) + + def __setstate__(self, state): + ( + self._reader, + self._writer, + self._reducers, + self._rlock, + self._wlock, + ) = state + + # Overload put to use our customizable reducer + def put(self, obj): + # serialize the data before acquiring the lock + obj = dumps(obj, reducers=self._reducers) + if self._wlock is None: + # writes to a message oriented win32 pipe are atomic + self._writer.send_bytes(obj) + else: + with self._wlock: + self._writer.send_bytes(obj) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/reduction.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/reduction.py new file mode 100644 index 0000000..bed32ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/reduction.py @@ -0,0 +1,224 @@ +############################################################################### +# Customizable Pickler with some basic reducers +# +# author: Thomas Moreau +# +# adapted from multiprocessing/reduction.py (17/02/2017) +# * Replace the ForkingPickler with a similar _LokyPickler, +# * Add CustomizableLokyPickler to allow customizing pickling process +# on the fly. +# +import copyreg +import io +import functools +import types +import sys +import os + +from multiprocessing import util +from pickle import loads, HIGHEST_PROTOCOL + +############################################################################### +# Enable custom pickling in Loky. + +_dispatch_table = {} + + +def register(type_, reduce_function): + _dispatch_table[type_] = reduce_function + + +############################################################################### +# Registers extra pickling routines to improve picklization for loky + + +# make methods picklable +def _reduce_method(m): + if m.__self__ is None: + return getattr, (m.__class__, m.__func__.__name__) + else: + return getattr, (m.__self__, m.__func__.__name__) + + +class _C: + def f(self): + pass + + @classmethod + def h(cls): + pass + + +register(type(_C().f), _reduce_method) +register(type(_C.h), _reduce_method) + + +if not hasattr(sys, "pypy_version_info"): + # PyPy uses functions instead of method_descriptors and wrapper_descriptors + def _reduce_method_descriptor(m): + return getattr, (m.__objclass__, m.__name__) + + register(type(list.append), _reduce_method_descriptor) + register(type(int.__add__), _reduce_method_descriptor) + + +# Make partial func pickable +def _reduce_partial(p): + return _rebuild_partial, (p.func, p.args, p.keywords or {}) + + +def _rebuild_partial(func, args, keywords): + return functools.partial(func, *args, **keywords) + + +register(functools.partial, _reduce_partial) + +if sys.platform != "win32": + from ._posix_reduction import _mk_inheritable # noqa: F401 +else: + from . import _win_reduction # noqa: F401 + +# global variable to change the pickler behavior +try: + from joblib.externals import cloudpickle # noqa: F401 + + DEFAULT_ENV = "cloudpickle" +except ImportError: + # If cloudpickle is not present, fallback to pickle + DEFAULT_ENV = "pickle" + +ENV_LOKY_PICKLER = os.environ.get("LOKY_PICKLER", DEFAULT_ENV) +_LokyPickler = None +_loky_pickler_name = None + + +def set_loky_pickler(loky_pickler=None): + global _LokyPickler, _loky_pickler_name + + if loky_pickler is None: + loky_pickler = ENV_LOKY_PICKLER + + loky_pickler_cls = None + + # The default loky_pickler is cloudpickle + if loky_pickler in ["", None]: + loky_pickler = "cloudpickle" + + if loky_pickler == _loky_pickler_name: + return + + if loky_pickler == "cloudpickle": + from joblib.externals.cloudpickle import CloudPickler as loky_pickler_cls + else: + try: + from importlib import import_module + + module_pickle = import_module(loky_pickler) + loky_pickler_cls = module_pickle.Pickler + except (ImportError, AttributeError) as e: + extra_info = ( + "\nThis error occurred while setting loky_pickler to" + f" '{loky_pickler}', as required by the env variable " + "LOKY_PICKLER or the function set_loky_pickler." + ) + e.args = (e.args[0] + extra_info,) + e.args[1:] + e.msg = e.args[0] + raise e + + util.debug( + f"Using '{loky_pickler if loky_pickler else 'cloudpickle'}' for " + "serialization." + ) + + class CustomizablePickler(loky_pickler_cls): + _loky_pickler_cls = loky_pickler_cls + + def _set_dispatch_table(self, dispatch_table): + for ancestor_class in self._loky_pickler_cls.mro(): + dt_attribute = getattr(ancestor_class, "dispatch_table", None) + if isinstance(dt_attribute, types.MemberDescriptorType): + # Ancestor class (typically _pickle.Pickler) has a + # member_descriptor for its "dispatch_table" attribute. Use + # it to set the dispatch_table as a member instead of a + # dynamic attribute in the __dict__ of the instance, + # otherwise it will not be taken into account by the C + # implementation of the dump method if a subclass defines a + # class-level dispatch_table attribute as was done in + # cloudpickle 1.6.0: + # https://github.com/joblib/loky/pull/260 + dt_attribute.__set__(self, dispatch_table) + break + + # On top of member descriptor set, also use setattr such that code + # that directly access self.dispatch_table gets a consistent view + # of the same table. + self.dispatch_table = dispatch_table + + def __init__(self, writer, reducers=None, protocol=HIGHEST_PROTOCOL): + loky_pickler_cls.__init__(self, writer, protocol=protocol) + if reducers is None: + reducers = {} + + if hasattr(self, "dispatch_table"): + # Force a copy that we will update without mutating the + # any class level defined dispatch_table. + loky_dt = dict(self.dispatch_table) + else: + # Use standard reducers as bases + loky_dt = copyreg.dispatch_table.copy() + + # Register loky specific reducers + loky_dt.update(_dispatch_table) + + # Set the new dispatch table, taking care of the fact that we + # need to use the member_descriptor when we inherit from a + # subclass of the C implementation of the Pickler base class + # with an class level dispatch_table attribute. + self._set_dispatch_table(loky_dt) + + # Register the reducers + for type, reduce_func in reducers.items(): + self.register(type, reduce_func) + + def register(self, type, reduce_func): + """Attach a reducer function to a given type in the dispatch table.""" + self.dispatch_table[type] = reduce_func + + _LokyPickler = CustomizablePickler + _loky_pickler_name = loky_pickler + + +def get_loky_pickler_name(): + global _loky_pickler_name + return _loky_pickler_name + + +def get_loky_pickler(): + global _LokyPickler + return _LokyPickler + + +# Set it to its default value +set_loky_pickler() + + +def dump(obj, file, reducers=None, protocol=None): + """Replacement for pickle.dump() using _LokyPickler.""" + global _LokyPickler + _LokyPickler(file, reducers=reducers, protocol=protocol).dump(obj) + + +def dumps(obj, reducers=None, protocol=None): + global _LokyPickler + + buf = io.BytesIO() + dump(obj, buf, reducers=reducers, protocol=protocol) + return buf.getbuffer() + + +__all__ = ["dump", "dumps", "loads", "register", "set_loky_pickler"] + +if sys.platform == "win32": + from multiprocessing.reduction import duplicate + + __all__ += ["duplicate"] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/resource_tracker.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/resource_tracker.py new file mode 100644 index 0000000..25204a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/resource_tracker.py @@ -0,0 +1,378 @@ +############################################################################### +# Server process to keep track of unlinked resources, like folders and +# semaphores and clean them. +# +# author: Thomas Moreau +# +# adapted from multiprocessing/semaphore_tracker.py (17/02/2017) +# * include custom spawnv_passfds to start the process +# * add some VERBOSE logging +# +# TODO: multiprocessing.resource_tracker was contributed to Python 3.8 so +# once loky drops support for Python 3.7 it might be possible to stop +# maintaining this loky-specific fork. As a consequence, it might also be +# possible to stop maintaining the loky.backend.synchronize fork of +# multiprocessing.synchronize. + +# +# On Unix we run a server process which keeps track of unlinked +# resources. The server ignores SIGINT and SIGTERM and reads from a +# pipe. The resource_tracker implements a reference counting scheme: each time +# a Python process anticipates the shared usage of a resource by another +# process, it signals the resource_tracker of this shared usage, and in return, +# the resource_tracker increments the resource's reference count by 1. +# Similarly, when access to a resource is closed by a Python process, the +# process notifies the resource_tracker by asking it to decrement the +# resource's reference count by 1. When the reference count drops to 0, the +# resource_tracker attempts to clean up the underlying resource. + +# Finally, every other process connected to the resource tracker has a copy of +# the writable end of the pipe used to communicate with it, so the resource +# tracker gets EOF when all other processes have exited. Then the +# resource_tracker process unlinks any remaining leaked resources (with +# reference count above 0) + +# For semaphores, this is important because the system only supports a limited +# number of named semaphores, and they will not be automatically removed till +# the next reboot. Without this resource tracker process, "killall python" +# would probably leave unlinked semaphores. + +# Note that this behavior differs from CPython's resource_tracker, which only +# implements list of shared resources, and not a proper refcounting scheme. +# Also, CPython's resource tracker will only attempt to cleanup those shared +# resources once all procsses connected to the resouce tracker have exited. + + +import os +import shutil +import sys +import signal +import warnings +import threading +from _multiprocessing import sem_unlink +from multiprocessing import util + +from . import spawn + +if sys.platform == "win32": + import _winapi + import msvcrt + from multiprocessing.reduction import duplicate + + +__all__ = ["ensure_running", "register", "unregister"] + +_HAVE_SIGMASK = hasattr(signal, "pthread_sigmask") +_IGNORED_SIGNALS = (signal.SIGINT, signal.SIGTERM) + +_CLEANUP_FUNCS = {"folder": shutil.rmtree, "file": os.unlink} + +if os.name == "posix": + _CLEANUP_FUNCS["semlock"] = sem_unlink + + +VERBOSE = False + + +class ResourceTracker: + def __init__(self): + self._lock = threading.Lock() + self._fd = None + self._pid = None + + def getfd(self): + self.ensure_running() + return self._fd + + def ensure_running(self): + """Make sure that resource tracker process is running. + + This can be run from any process. Usually a child process will use + the resource created by its parent.""" + with self._lock: + if self._fd is not None: + # resource tracker was launched before, is it still running? + if self._check_alive(): + # => still alive + return + # => dead, launch it again + os.close(self._fd) + if os.name == "posix": + try: + # At this point, the resource_tracker process has been + # killed or crashed. Let's remove the process entry + # from the process table to avoid zombie processes. + os.waitpid(self._pid, 0) + except OSError: + # The process was terminated or is a child from an + # ancestor of the current process. + pass + self._fd = None + self._pid = None + + warnings.warn( + "resource_tracker: process died unexpectedly, " + "relaunching. Some folders/sempahores might " + "leak." + ) + + fds_to_pass = [] + try: + fds_to_pass.append(sys.stderr.fileno()) + except Exception: + pass + + r, w = os.pipe() + if sys.platform == "win32": + _r = duplicate(msvcrt.get_osfhandle(r), inheritable=True) + os.close(r) + r = _r + + cmd = f"from {main.__module__} import main; main({r}, {VERBOSE})" + try: + fds_to_pass.append(r) + # process will out live us, so no need to wait on pid + exe = spawn.get_executable() + args = [exe, *util._args_from_interpreter_flags(), "-c", cmd] + util.debug(f"launching resource tracker: {args}") + # bpo-33613: Register a signal mask that will block the + # signals. This signal mask will be inherited by the child + # that is going to be spawned and will protect the child from a + # race condition that can make the child die before it + # registers signal handlers for SIGINT and SIGTERM. The mask is + # unregistered after spawning the child. + try: + if _HAVE_SIGMASK: + signal.pthread_sigmask( + signal.SIG_BLOCK, _IGNORED_SIGNALS + ) + pid = spawnv_passfds(exe, args, fds_to_pass) + finally: + if _HAVE_SIGMASK: + signal.pthread_sigmask( + signal.SIG_UNBLOCK, _IGNORED_SIGNALS + ) + except BaseException: + os.close(w) + raise + else: + self._fd = w + self._pid = pid + finally: + if sys.platform == "win32": + _winapi.CloseHandle(r) + else: + os.close(r) + + def _check_alive(self): + """Check for the existence of the resource tracker process.""" + try: + self._send("PROBE", "", "") + except BrokenPipeError: + return False + else: + return True + + def register(self, name, rtype): + """Register a named resource, and increment its refcount.""" + self.ensure_running() + self._send("REGISTER", name, rtype) + + def unregister(self, name, rtype): + """Unregister a named resource with resource tracker.""" + self.ensure_running() + self._send("UNREGISTER", name, rtype) + + def maybe_unlink(self, name, rtype): + """Decrement the refcount of a resource, and delete it if it hits 0""" + self.ensure_running() + self._send("MAYBE_UNLINK", name, rtype) + + def _send(self, cmd, name, rtype): + if len(name) > 512: + # posix guarantees that writes to a pipe of less than PIPE_BUF + # bytes are atomic, and that PIPE_BUF >= 512 + raise ValueError("name too long") + msg = f"{cmd}:{name}:{rtype}\n".encode("ascii") + nbytes = os.write(self._fd, msg) + assert nbytes == len(msg) + + +_resource_tracker = ResourceTracker() +ensure_running = _resource_tracker.ensure_running +register = _resource_tracker.register +maybe_unlink = _resource_tracker.maybe_unlink +unregister = _resource_tracker.unregister +getfd = _resource_tracker.getfd + + +def main(fd, verbose=0): + """Run resource tracker.""" + # protect the process from ^C and "killall python" etc + if verbose: + util.log_to_stderr(level=util.DEBUG) + + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + + if _HAVE_SIGMASK: + signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS) + + for f in (sys.stdin, sys.stdout): + try: + f.close() + except Exception: + pass + + if verbose: + util.debug("Main resource tracker is running") + + registry = {rtype: {} for rtype in _CLEANUP_FUNCS.keys()} + try: + # keep track of registered/unregistered resources + if sys.platform == "win32": + fd = msvcrt.open_osfhandle(fd, os.O_RDONLY) + with open(fd, "rb") as f: + while True: + line = f.readline() + if line == b"": # EOF + break + try: + splitted = line.strip().decode("ascii").split(":") + # name can potentially contain separator symbols (for + # instance folders on Windows) + cmd, name, rtype = ( + splitted[0], + ":".join(splitted[1:-1]), + splitted[-1], + ) + + if cmd == "PROBE": + continue + + if rtype not in _CLEANUP_FUNCS: + raise ValueError( + f"Cannot register {name} for automatic cleanup: " + f"unknown resource type ({rtype}). Resource type " + "should be one of the following: " + f"{list(_CLEANUP_FUNCS.keys())}" + ) + + if cmd == "REGISTER": + if name not in registry[rtype]: + registry[rtype][name] = 1 + else: + registry[rtype][name] += 1 + + if verbose: + util.debug( + "[ResourceTracker] incremented refcount of " + f"{rtype} {name} " + f"(current {registry[rtype][name]})" + ) + elif cmd == "UNREGISTER": + del registry[rtype][name] + if verbose: + util.debug( + f"[ResourceTracker] unregister {name} {rtype}: " + f"registry({len(registry)})" + ) + elif cmd == "MAYBE_UNLINK": + registry[rtype][name] -= 1 + if verbose: + util.debug( + "[ResourceTracker] decremented refcount of " + f"{rtype} {name} " + f"(current {registry[rtype][name]})" + ) + + if registry[rtype][name] == 0: + del registry[rtype][name] + try: + if verbose: + util.debug( + f"[ResourceTracker] unlink {name}" + ) + _CLEANUP_FUNCS[rtype](name) + except Exception as e: + warnings.warn( + f"resource_tracker: {name}: {e!r}" + ) + + else: + raise RuntimeError(f"unrecognized command {cmd!r}") + except BaseException: + try: + sys.excepthook(*sys.exc_info()) + except BaseException: + pass + finally: + # all processes have terminated; cleanup any remaining resources + def _unlink_resources(rtype_registry, rtype): + if rtype_registry: + try: + warnings.warn( + "resource_tracker: There appear to be " + f"{len(rtype_registry)} leaked {rtype} objects to " + "clean up at shutdown" + ) + except Exception: + pass + for name in rtype_registry: + # For some reason the process which created and registered this + # resource has failed to unregister it. Presumably it has + # died. We therefore clean it up. + try: + _CLEANUP_FUNCS[rtype](name) + if verbose: + util.debug(f"[ResourceTracker] unlink {name}") + except Exception as e: + warnings.warn(f"resource_tracker: {name}: {e!r}") + + for rtype, rtype_registry in registry.items(): + if rtype == "folder": + continue + else: + _unlink_resources(rtype_registry, rtype) + + # The default cleanup routine for folders deletes everything inside + # those folders recursively, which can include other resources tracked + # by the resource tracker). To limit the risk of the resource tracker + # attempting to delete twice a resource (once as part of a tracked + # folder, and once as a resource), we delete the folders after all + # other resource types. + if "folder" in registry: + _unlink_resources(registry["folder"], "folder") + + if verbose: + util.debug("resource tracker shut down") + + +# +# Start a program with only specified fds kept open +# + + +def spawnv_passfds(path, args, passfds): + passfds = sorted(passfds) + if sys.platform != "win32": + errpipe_read, errpipe_write = os.pipe() + try: + from .reduction import _mk_inheritable + from .fork_exec import fork_exec + + _pass = [_mk_inheritable(fd) for fd in passfds] + return fork_exec(args, _pass) + finally: + os.close(errpipe_read) + os.close(errpipe_write) + else: + cmd = " ".join(f'"{x}"' for x in args) + try: + _, ht, pid, _ = _winapi.CreateProcess( + path, cmd, None, None, True, 0, None, None, None + ) + _winapi.CloseHandle(ht) + except BaseException: + pass + return pid diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/spawn.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/spawn.py new file mode 100644 index 0000000..d011c39 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/spawn.py @@ -0,0 +1,250 @@ +############################################################################### +# Prepares and processes the data to setup the new process environment +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from multiprocessing/spawn.py (17/02/2017) +# * Improve logging data +# +import os +import sys +import runpy +import textwrap +import types +from multiprocessing import process, util + + +if sys.platform != "win32": + WINEXE = False + WINSERVICE = False +else: + import msvcrt + from multiprocessing.reduction import duplicate + + WINEXE = sys.platform == "win32" and getattr(sys, "frozen", False) + WINSERVICE = sys.executable.lower().endswith("pythonservice.exe") + +if WINSERVICE: + _python_exe = os.path.join(sys.exec_prefix, "python.exe") +else: + _python_exe = sys.executable + + +def get_executable(): + return _python_exe + + +def _check_not_importing_main(): + if getattr(process.current_process(), "_inheriting", False): + raise RuntimeError( + textwrap.dedent( + """\ + An attempt has been made to start a new process before the + current process has finished its bootstrapping phase. + + This probably means that you are not using fork to start your + child processes and you have forgotten to use the proper idiom + in the main module: + + if __name__ == '__main__': + freeze_support() + ... + + The "freeze_support()" line can be omitted if the program + is not going to be frozen to produce an executable.""" + ) + ) + + +def get_preparation_data(name, init_main_module=True): + """Return info about parent needed by child to unpickle process object.""" + _check_not_importing_main() + d = dict( + log_to_stderr=util._log_to_stderr, + authkey=bytes(process.current_process().authkey), + name=name, + sys_argv=sys.argv, + orig_dir=process.ORIGINAL_DIR, + dir=os.getcwd(), + ) + + # Send sys_path and make sure the current directory will not be changed + d["sys_path"] = [p if p != "" else process.ORIGINAL_DIR for p in sys.path] + + # Make sure to pass the information if the multiprocessing logger is active + if util._logger is not None: + d["log_level"] = util._logger.getEffectiveLevel() + if util._logger.handlers: + h = util._logger.handlers[0] + d["log_fmt"] = h.formatter._fmt + + # Tell the child how to communicate with the resource_tracker + from .resource_tracker import _resource_tracker + + _resource_tracker.ensure_running() + d["tracker_args"] = {"pid": _resource_tracker._pid} + if sys.platform == "win32": + d["tracker_args"]["fh"] = msvcrt.get_osfhandle(_resource_tracker._fd) + else: + d["tracker_args"]["fd"] = _resource_tracker._fd + + if sys.version_info >= (3, 8) and os.name == "posix": + # joblib/loky#242: allow loky processes to retrieve the resource + # tracker of their parent in case the child processes depickles + # shared_memory objects, that are still tracked by multiprocessing's + # resource_tracker by default. + # XXX: this is a workaround that may be error prone: in the future, it + # would be better to have loky subclass multiprocessing's shared_memory + # to force registration of shared_memory segments via loky's + # resource_tracker. + from multiprocessing.resource_tracker import ( + _resource_tracker as mp_resource_tracker, + ) + + # multiprocessing's resource_tracker must be running before loky + # process is created (othewise the child won't be able to use it if it + # is created later on) + mp_resource_tracker.ensure_running() + d["mp_tracker_args"] = { + "fd": mp_resource_tracker._fd, + "pid": mp_resource_tracker._pid, + } + + # Figure out whether to initialise main in the subprocess as a module + # or through direct execution (or to leave it alone entirely) + if init_main_module: + main_module = sys.modules["__main__"] + try: + main_mod_name = getattr(main_module.__spec__, "name", None) + except BaseException: + main_mod_name = None + if main_mod_name is not None: + d["init_main_from_name"] = main_mod_name + elif sys.platform != "win32" or (not WINEXE and not WINSERVICE): + main_path = getattr(main_module, "__file__", None) + if main_path is not None: + if ( + not os.path.isabs(main_path) + and process.ORIGINAL_DIR is not None + ): + main_path = os.path.join(process.ORIGINAL_DIR, main_path) + d["init_main_from_path"] = os.path.normpath(main_path) + + return d + + +# +# Prepare current process +# +old_main_modules = [] + + +def prepare(data, parent_sentinel=None): + """Try to get current process ready to unpickle process object.""" + if "name" in data: + process.current_process().name = data["name"] + + if "authkey" in data: + process.current_process().authkey = data["authkey"] + + if "log_to_stderr" in data and data["log_to_stderr"]: + util.log_to_stderr() + + if "log_level" in data: + util.get_logger().setLevel(data["log_level"]) + + if "log_fmt" in data: + import logging + + util.get_logger().handlers[0].setFormatter( + logging.Formatter(data["log_fmt"]) + ) + + if "sys_path" in data: + sys.path = data["sys_path"] + + if "sys_argv" in data: + sys.argv = data["sys_argv"] + + if "dir" in data: + os.chdir(data["dir"]) + + if "orig_dir" in data: + process.ORIGINAL_DIR = data["orig_dir"] + + if "mp_tracker_args" in data: + from multiprocessing.resource_tracker import ( + _resource_tracker as mp_resource_tracker, + ) + + mp_resource_tracker._fd = data["mp_tracker_args"]["fd"] + mp_resource_tracker._pid = data["mp_tracker_args"]["pid"] + if "tracker_args" in data: + from .resource_tracker import _resource_tracker + + _resource_tracker._pid = data["tracker_args"]["pid"] + if sys.platform == "win32": + handle = data["tracker_args"]["fh"] + handle = duplicate(handle, source_process=parent_sentinel) + _resource_tracker._fd = msvcrt.open_osfhandle(handle, os.O_RDONLY) + else: + _resource_tracker._fd = data["tracker_args"]["fd"] + + if "init_main_from_name" in data: + _fixup_main_from_name(data["init_main_from_name"]) + elif "init_main_from_path" in data: + _fixup_main_from_path(data["init_main_from_path"]) + + +# Multiprocessing module helpers to fix up the main module in +# spawned subprocesses +def _fixup_main_from_name(mod_name): + # __main__.py files for packages, directories, zip archives, etc, run + # their "main only" code unconditionally, so we don't even try to + # populate anything in __main__, nor do we make any changes to + # __main__ attributes + current_main = sys.modules["__main__"] + if mod_name == "__main__" or mod_name.endswith(".__main__"): + return + + # If this process was forked, __main__ may already be populated + if getattr(current_main.__spec__, "name", None) == mod_name: + return + + # Otherwise, __main__ may contain some non-main code where we need to + # support unpickling it properly. We rerun it as __mp_main__ and make + # the normal __main__ an alias to that + old_main_modules.append(current_main) + main_module = types.ModuleType("__mp_main__") + main_content = runpy.run_module( + mod_name, run_name="__mp_main__", alter_sys=True + ) + main_module.__dict__.update(main_content) + sys.modules["__main__"] = sys.modules["__mp_main__"] = main_module + + +def _fixup_main_from_path(main_path): + # If this process was forked, __main__ may already be populated + current_main = sys.modules["__main__"] + + # Unfortunately, the main ipython launch script historically had no + # "if __name__ == '__main__'" guard, so we work around that + # by treating it like a __main__.py file + # See https://github.com/ipython/ipython/issues/4698 + main_name = os.path.splitext(os.path.basename(main_path))[0] + if main_name == "ipython": + return + + # Otherwise, if __file__ already has the setting we expect, + # there's nothing more to do + if getattr(current_main, "__file__", None) == main_path: + return + + # If the parent process has sent a path through rather than a module + # name we assume it is an executable script that may contain + # non-main code that needs to be executed + old_main_modules.append(current_main) + main_module = types.ModuleType("__mp_main__") + main_content = runpy.run_path(main_path, run_name="__mp_main__") + main_module.__dict__.update(main_content) + sys.modules["__main__"] = sys.modules["__mp_main__"] = main_module diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/synchronize.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/synchronize.py new file mode 100644 index 0000000..18db3e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/synchronize.py @@ -0,0 +1,409 @@ +############################################################################### +# Synchronization primitives based on our SemLock implementation +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from multiprocessing/synchronize.py (17/02/2017) +# * Remove ctx argument for compatibility reason +# * Registers a cleanup function with the loky resource_tracker to remove the +# semaphore when the process dies instead. +# +# TODO: investigate which Python version is required to be able to use +# multiprocessing.resource_tracker and therefore multiprocessing.synchronize +# instead of a loky-specific fork. + +import os +import sys +import tempfile +import threading +import _multiprocessing +from time import time as _time +from multiprocessing import process, util +from multiprocessing.context import assert_spawning + +from . import resource_tracker + +__all__ = [ + "Lock", + "RLock", + "Semaphore", + "BoundedSemaphore", + "Condition", + "Event", +] +# Try to import the mp.synchronize module cleanly, if it fails +# raise ImportError for platforms lacking a working sem_open implementation. +# See issue 3770 +try: + from _multiprocessing import SemLock as _SemLock + from _multiprocessing import sem_unlink +except ImportError: + raise ImportError( + "This platform lacks a functioning sem_open" + " implementation, therefore, the required" + " synchronization primitives needed will not" + " function, see issue 3770." + ) + +# +# Constants +# + +RECURSIVE_MUTEX, SEMAPHORE = range(2) +SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX + + +# +# Base class for semaphores and mutexes; wraps `_multiprocessing.SemLock` +# + + +class SemLock: + + _rand = tempfile._RandomNameSequence() + + def __init__(self, kind, value, maxvalue, name=None): + # unlink_now is only used on win32 or when we are using fork. + unlink_now = False + if name is None: + # Try to find an unused name for the SemLock instance. + for _ in range(100): + try: + self._semlock = _SemLock( + kind, value, maxvalue, SemLock._make_name(), unlink_now + ) + except FileExistsError: # pragma: no cover + pass + else: + break + else: # pragma: no cover + raise FileExistsError("cannot find name for semaphore") + else: + self._semlock = _SemLock(kind, value, maxvalue, name, unlink_now) + self.name = name + util.debug( + f"created semlock with handle {self._semlock.handle} and name " + f'"{self.name}"' + ) + + self._make_methods() + + def _after_fork(obj): + obj._semlock._after_fork() + + util.register_after_fork(self, _after_fork) + + # When the object is garbage collected or the + # process shuts down we unlink the semaphore name + resource_tracker.register(self._semlock.name, "semlock") + util.Finalize( + self, SemLock._cleanup, (self._semlock.name,), exitpriority=0 + ) + + @staticmethod + def _cleanup(name): + try: + sem_unlink(name) + except FileNotFoundError: + # Already unlinked, possibly by user code: ignore and make sure to + # unregister the semaphore from the resource tracker. + pass + finally: + resource_tracker.unregister(name, "semlock") + + def _make_methods(self): + self.acquire = self._semlock.acquire + self.release = self._semlock.release + + def __enter__(self): + return self._semlock.acquire() + + def __exit__(self, *args): + return self._semlock.release() + + def __getstate__(self): + assert_spawning(self) + sl = self._semlock + h = sl.handle + return (h, sl.kind, sl.maxvalue, sl.name) + + def __setstate__(self, state): + self._semlock = _SemLock._rebuild(*state) + util.debug( + f'recreated blocker with handle {state[0]!r} and name "{state[3]}"' + ) + self._make_methods() + + @staticmethod + def _make_name(): + # OSX does not support long names for semaphores + return f"/loky-{os.getpid()}-{next(SemLock._rand)}" + + +# +# Semaphore +# + + +class Semaphore(SemLock): + def __init__(self, value=1): + SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX) + + def get_value(self): + if sys.platform == "darwin": + raise NotImplementedError("OSX does not implement sem_getvalue") + return self._semlock._get_value() + + def __repr__(self): + try: + value = self._semlock._get_value() + except Exception: + value = "unknown" + return f"<{self.__class__.__name__}(value={value})>" + + +# +# Bounded semaphore +# + + +class BoundedSemaphore(Semaphore): + def __init__(self, value=1): + SemLock.__init__(self, SEMAPHORE, value, value) + + def __repr__(self): + try: + value = self._semlock._get_value() + except Exception: + value = "unknown" + return ( + f"<{self.__class__.__name__}(value={value}, " + f"maxvalue={self._semlock.maxvalue})>" + ) + + +# +# Non-recursive lock +# + + +class Lock(SemLock): + def __init__(self): + super().__init__(SEMAPHORE, 1, 1) + + def __repr__(self): + try: + if self._semlock._is_mine(): + name = process.current_process().name + if threading.current_thread().name != "MainThread": + name = f"{name}|{threading.current_thread().name}" + elif self._semlock._get_value() == 1: + name = "None" + elif self._semlock._count() > 0: + name = "SomeOtherThread" + else: + name = "SomeOtherProcess" + except Exception: + name = "unknown" + return f"<{self.__class__.__name__}(owner={name})>" + + +# +# Recursive lock +# + + +class RLock(SemLock): + def __init__(self): + super().__init__(RECURSIVE_MUTEX, 1, 1) + + def __repr__(self): + try: + if self._semlock._is_mine(): + name = process.current_process().name + if threading.current_thread().name != "MainThread": + name = f"{name}|{threading.current_thread().name}" + count = self._semlock._count() + elif self._semlock._get_value() == 1: + name, count = "None", 0 + elif self._semlock._count() > 0: + name, count = "SomeOtherThread", "nonzero" + else: + name, count = "SomeOtherProcess", "nonzero" + except Exception: + name, count = "unknown", "unknown" + return f"<{self.__class__.__name__}({name}, {count})>" + + +# +# Condition variable +# + + +class Condition: + def __init__(self, lock=None): + self._lock = lock or RLock() + self._sleeping_count = Semaphore(0) + self._woken_count = Semaphore(0) + self._wait_semaphore = Semaphore(0) + self._make_methods() + + def __getstate__(self): + assert_spawning(self) + return ( + self._lock, + self._sleeping_count, + self._woken_count, + self._wait_semaphore, + ) + + def __setstate__(self, state): + ( + self._lock, + self._sleeping_count, + self._woken_count, + self._wait_semaphore, + ) = state + self._make_methods() + + def __enter__(self): + return self._lock.__enter__() + + def __exit__(self, *args): + return self._lock.__exit__(*args) + + def _make_methods(self): + self.acquire = self._lock.acquire + self.release = self._lock.release + + def __repr__(self): + try: + num_waiters = ( + self._sleeping_count._semlock._get_value() + - self._woken_count._semlock._get_value() + ) + except Exception: + num_waiters = "unknown" + return f"<{self.__class__.__name__}({self._lock}, {num_waiters})>" + + def wait(self, timeout=None): + assert ( + self._lock._semlock._is_mine() + ), "must acquire() condition before using wait()" + + # indicate that this thread is going to sleep + self._sleeping_count.release() + + # release lock + count = self._lock._semlock._count() + for _ in range(count): + self._lock.release() + + try: + # wait for notification or timeout + return self._wait_semaphore.acquire(True, timeout) + finally: + # indicate that this thread has woken + self._woken_count.release() + + # reacquire lock + for _ in range(count): + self._lock.acquire() + + def notify(self): + assert self._lock._semlock._is_mine(), "lock is not owned" + assert not self._wait_semaphore.acquire(False) + + # to take account of timeouts since last notify() we subtract + # woken_count from sleeping_count and rezero woken_count + while self._woken_count.acquire(False): + res = self._sleeping_count.acquire(False) + assert res + + if self._sleeping_count.acquire(False): # try grabbing a sleeper + self._wait_semaphore.release() # wake up one sleeper + self._woken_count.acquire() # wait for the sleeper to wake + + # rezero _wait_semaphore in case a timeout just happened + self._wait_semaphore.acquire(False) + + def notify_all(self): + assert self._lock._semlock._is_mine(), "lock is not owned" + assert not self._wait_semaphore.acquire(False) + + # to take account of timeouts since last notify*() we subtract + # woken_count from sleeping_count and rezero woken_count + while self._woken_count.acquire(False): + res = self._sleeping_count.acquire(False) + assert res + + sleepers = 0 + while self._sleeping_count.acquire(False): + self._wait_semaphore.release() # wake up one sleeper + sleepers += 1 + + if sleepers: + for _ in range(sleepers): + self._woken_count.acquire() # wait for a sleeper to wake + + # rezero wait_semaphore in case some timeouts just happened + while self._wait_semaphore.acquire(False): + pass + + def wait_for(self, predicate, timeout=None): + result = predicate() + if result: + return result + if timeout is not None: + endtime = _time() + timeout + else: + endtime = None + waittime = None + while not result: + if endtime is not None: + waittime = endtime - _time() + if waittime <= 0: + break + self.wait(waittime) + result = predicate() + return result + + +# +# Event +# + + +class Event: + def __init__(self): + self._cond = Condition(Lock()) + self._flag = Semaphore(0) + + def is_set(self): + with self._cond: + if self._flag.acquire(False): + self._flag.release() + return True + return False + + def set(self): + with self._cond: + self._flag.acquire(False) + self._flag.release() + self._cond.notify_all() + + def clear(self): + with self._cond: + self._flag.acquire(False) + + def wait(self, timeout=None): + with self._cond: + if self._flag.acquire(False): + self._flag.release() + else: + self._cond.wait(timeout) + + if self._flag.acquire(False): + self._flag.release() + return True + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/utils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/utils.py new file mode 100644 index 0000000..aa089f7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/backend/utils.py @@ -0,0 +1,181 @@ +import os +import sys +import time +import errno +import signal +import warnings +import subprocess +import traceback + +try: + import psutil +except ImportError: + psutil = None + + +def kill_process_tree(process, use_psutil=True): + """Terminate process and its descendants with SIGKILL""" + if use_psutil and psutil is not None: + _kill_process_tree_with_psutil(process) + else: + _kill_process_tree_without_psutil(process) + + +def recursive_terminate(process, use_psutil=True): + warnings.warn( + "recursive_terminate is deprecated in loky 3.2, use kill_process_tree" + "instead", + DeprecationWarning, + ) + kill_process_tree(process, use_psutil=use_psutil) + + +def _kill_process_tree_with_psutil(process): + try: + descendants = psutil.Process(process.pid).children(recursive=True) + except psutil.NoSuchProcess: + return + + # Kill the descendants in reverse order to avoid killing the parents before + # the descendant in cases where there are more processes nested. + for descendant in descendants[::-1]: + try: + descendant.kill() + except psutil.NoSuchProcess: + pass + + try: + psutil.Process(process.pid).kill() + except psutil.NoSuchProcess: + pass + process.join() + + +def _kill_process_tree_without_psutil(process): + """Terminate a process and its descendants.""" + try: + if sys.platform == "win32": + _windows_taskkill_process_tree(process.pid) + else: + _posix_recursive_kill(process.pid) + except Exception: # pragma: no cover + details = traceback.format_exc() + warnings.warn( + "Failed to kill subprocesses on this platform. Please install" + "psutil: https://github.com/giampaolo/psutil\n" + f"Details:\n{details}" + ) + # In case we cannot introspect or kill the descendants, we fall back to + # only killing the main process. + # + # Note: on Windows, process.kill() is an alias for process.terminate() + # which in turns calls the Win32 API function TerminateProcess(). + process.kill() + process.join() + + +def _windows_taskkill_process_tree(pid): + # On windows, the taskkill function with option `/T` terminate a given + # process pid and its children. + try: + subprocess.check_output( + ["taskkill", "/F", "/T", "/PID", str(pid)], stderr=None + ) + except subprocess.CalledProcessError as e: + # In Windows, taskkill returns 128, 255 for no process found. + if e.returncode not in [128, 255]: + # Let's raise to let the caller log the error details in a + # warning and only kill the root process. + raise # pragma: no cover + + +def _kill(pid): + # Not all systems (e.g. Windows) have a SIGKILL, but the C specification + # mandates a SIGTERM signal. While Windows is handled specifically above, + # let's try to be safe for other hypothetic platforms that only have + # SIGTERM without SIGKILL. + kill_signal = getattr(signal, "SIGKILL", signal.SIGTERM) + try: + os.kill(pid, kill_signal) + except OSError as e: + # if OSError is raised with [Errno 3] no such process, the process + # is already terminated, else, raise the error and let the top + # level function raise a warning and retry to kill the process. + if e.errno != errno.ESRCH: + raise # pragma: no cover + + +def _posix_recursive_kill(pid): + """Recursively kill the descendants of a process before killing it.""" + try: + children_pids = subprocess.check_output( + ["pgrep", "-P", str(pid)], stderr=None, text=True + ) + except subprocess.CalledProcessError as e: + # `ps` returns 1 when no child process has been found + if e.returncode == 1: + children_pids = "" + else: + raise # pragma: no cover + + # Decode the result, split the cpid and remove the trailing line + for cpid in children_pids.splitlines(): + cpid = int(cpid) + _posix_recursive_kill(cpid) + + _kill(pid) + + +def get_exitcodes_terminated_worker(processes): + """Return a formatted string with the exitcodes of terminated workers. + + If necessary, wait (up to .25s) for the system to correctly set the + exitcode of one terminated worker. + """ + patience = 5 + + # Catch the exitcode of the terminated workers. There should at least be + # one. If not, wait a bit for the system to correctly set the exitcode of + # the terminated worker. + exitcodes = [ + p.exitcode for p in list(processes.values()) if p.exitcode is not None + ] + while not exitcodes and patience > 0: + patience -= 1 + exitcodes = [ + p.exitcode + for p in list(processes.values()) + if p.exitcode is not None + ] + time.sleep(0.05) + + return _format_exitcodes(exitcodes) + + +def _format_exitcodes(exitcodes): + """Format a list of exit code with names of the signals if possible""" + str_exitcodes = [ + f"{_get_exitcode_name(e)}({e})" for e in exitcodes if e is not None + ] + return "{" + ", ".join(str_exitcodes) + "}" + + +def _get_exitcode_name(exitcode): + if sys.platform == "win32": + # The exitcode are unreliable on windows (see bpo-31863). + # For this case, return UNKNOWN + return "UNKNOWN" + + if exitcode < 0: + try: + import signal + + return signal.Signals(-exitcode).name + except ValueError: + return "UNKNOWN" + elif exitcode != 255: + # The exitcode are unreliable on forkserver were 255 is always returned + # (see bpo-30589). For this case, return UNKNOWN + return "EXIT" + + return "UNKNOWN" diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/cloudpickle_wrapper.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/cloudpickle_wrapper.py new file mode 100644 index 0000000..099debc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/cloudpickle_wrapper.py @@ -0,0 +1,102 @@ +import inspect +from functools import partial +from joblib.externals.cloudpickle import dumps, loads + + +WRAP_CACHE = {} + + +class CloudpickledObjectWrapper: + def __init__(self, obj, keep_wrapper=False): + self._obj = obj + self._keep_wrapper = keep_wrapper + + def __reduce__(self): + _pickled_object = dumps(self._obj) + if not self._keep_wrapper: + return loads, (_pickled_object,) + + return _reconstruct_wrapper, (_pickled_object, self._keep_wrapper) + + def __getattr__(self, attr): + # Ensure that the wrapped object can be used seemlessly as the + # previous object. + if attr not in ["_obj", "_keep_wrapper"]: + return getattr(self._obj, attr) + return getattr(self, attr) + + +# Make sure the wrapped object conserves the callable property +class CallableObjectWrapper(CloudpickledObjectWrapper): + def __call__(self, *args, **kwargs): + return self._obj(*args, **kwargs) + + +def _wrap_non_picklable_objects(obj, keep_wrapper): + if callable(obj): + return CallableObjectWrapper(obj, keep_wrapper=keep_wrapper) + return CloudpickledObjectWrapper(obj, keep_wrapper=keep_wrapper) + + +def _reconstruct_wrapper(_pickled_object, keep_wrapper): + obj = loads(_pickled_object) + return _wrap_non_picklable_objects(obj, keep_wrapper) + + +def _wrap_objects_when_needed(obj): + # Function to introspect an object and decide if it should be wrapped or + # not. + need_wrap = "__main__" in getattr(obj, "__module__", "") + if isinstance(obj, partial): + return partial( + _wrap_objects_when_needed(obj.func), + *[_wrap_objects_when_needed(a) for a in obj.args], + **{ + k: _wrap_objects_when_needed(v) + for k, v in obj.keywords.items() + } + ) + if callable(obj): + # Need wrap if the object is a function defined in a local scope of + # another function. + func_code = getattr(obj, "__code__", "") + need_wrap |= getattr(func_code, "co_flags", 0) & inspect.CO_NESTED + + # Need wrap if the obj is a lambda expression + func_name = getattr(obj, "__name__", "") + need_wrap |= "" in func_name + + if not need_wrap: + return obj + + wrapped_obj = WRAP_CACHE.get(obj) + if wrapped_obj is None: + wrapped_obj = _wrap_non_picklable_objects(obj, keep_wrapper=False) + WRAP_CACHE[obj] = wrapped_obj + return wrapped_obj + + +def wrap_non_picklable_objects(obj, keep_wrapper=True): + """Wrapper for non-picklable object to use cloudpickle to serialize them. + + Note that this wrapper tends to slow down the serialization process as it + is done with cloudpickle which is typically slower compared to pickle. The + proper way to solve serialization issues is to avoid defining functions and + objects in the main scripts and to implement __reduce__ functions for + complex classes. + """ + # If obj is a class, create a CloudpickledClassWrapper which instantiates + # the object internally and wrap it directly in a CloudpickledObjectWrapper + if inspect.isclass(obj): + + class CloudpickledClassWrapper(CloudpickledObjectWrapper): + def __init__(self, *args, **kwargs): + self._obj = obj(*args, **kwargs) + self._keep_wrapper = keep_wrapper + + CloudpickledClassWrapper.__name__ = obj.__name__ + return CloudpickledClassWrapper + + # If obj is an instance of a class, just wrap it in a regular + # CloudpickledObjectWrapper + return _wrap_non_picklable_objects(obj, keep_wrapper=keep_wrapper) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/initializers.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/initializers.py new file mode 100644 index 0000000..aea0e56 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/initializers.py @@ -0,0 +1,80 @@ +import warnings + + +def _viztracer_init(init_kwargs): + """Initialize viztracer's profiler in worker processes""" + from viztracer import VizTracer + + tracer = VizTracer(**init_kwargs) + tracer.register_exit() + tracer.start() + + +def _make_viztracer_initializer_and_initargs(): + try: + import viztracer + + tracer = viztracer.get_tracer() + if tracer is not None and getattr(tracer, "enable", False): + # Profiler is active: introspect its configuration to + # initialize the workers with the same configuration. + return _viztracer_init, (tracer.init_kwargs,) + except ImportError: + # viztracer is not installed: nothing to do + pass + except Exception as e: + # In case viztracer's API evolve, we do not want to crash loky but + # we want to know about it to be able to update loky. + warnings.warn(f"Unable to introspect viztracer state: {e}") + return None, () + + +class _ChainedInitializer: + """Compound worker initializer + + This is meant to be used in conjunction with _chain_initializers to + produce the necessary chained_args list to be passed to __call__. + """ + + def __init__(self, initializers): + self._initializers = initializers + + def __call__(self, *chained_args): + for initializer, args in zip(self._initializers, chained_args): + initializer(*args) + + +def _chain_initializers(initializer_and_args): + """Convenience helper to combine a sequence of initializers. + + If some initializers are None, they are filtered out. + """ + filtered_initializers = [] + filtered_initargs = [] + for initializer, initargs in initializer_and_args: + if initializer is not None: + filtered_initializers.append(initializer) + filtered_initargs.append(initargs) + + if not filtered_initializers: + return None, () + elif len(filtered_initializers) == 1: + return filtered_initializers[0], filtered_initargs[0] + else: + return _ChainedInitializer(filtered_initializers), filtered_initargs + + +def _prepare_initializer(initializer, initargs): + if initializer is not None and not callable(initializer): + raise TypeError( + f"initializer must be a callable, got: {initializer!r}" + ) + + # Introspect runtime to determine if we need to propagate the viztracer + # profiler information to the workers: + return _chain_initializers( + [ + (initializer, initargs), + _make_viztracer_initializer_and_initargs(), + ] + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py new file mode 100644 index 0000000..1e08cc2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py @@ -0,0 +1,1314 @@ +############################################################################### +# Re-implementation of the ProcessPoolExecutor more robust to faults +# +# author: Thomas Moreau and Olivier Grisel +# +# adapted from concurrent/futures/process_pool_executor.py (17/02/2017) +# * Add an extra management thread to detect executor_manager_thread failures, +# * Improve the shutdown process to avoid deadlocks, +# * Add timeout for workers, +# * More robust pickling process. +# +# Copyright 2009 Brian Quinlan. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Implements ProcessPoolExecutor. + +The follow diagram and text describe the data-flow through the system: + +|======================= In-process =====================|== Out-of-process ==| + ++----------+ +----------+ +--------+ +-----------+ +---------+ +| | => | Work Ids | | | | Call Q | | Process | +| | +----------+ | | +-----------+ | Pool | +| | | ... | | | | ... | +---------+ +| | | 6 | => | | => | 5, call() | => | | +| | | 7 | | | | ... | | | +| Process | | ... | | Local | +-----------+ | Process | +| Pool | +----------+ | Worker | | #1..n | +| Executor | | Thread | | | +| | +----------- + | | +-----------+ | | +| | <=> | Work Items | <=> | | <= | Result Q | <= | | +| | +------------+ | | +-----------+ | | +| | | 6: call() | | | | ... | | | +| | | future | +--------+ | 4, result | | | +| | | ... | | 3, except | | | ++----------+ +------------+ +-----------+ +---------+ + +Executor.submit() called: +- creates a uniquely numbered _WorkItem and adds it to the "Work Items" dict +- adds the id of the _WorkItem to the "Work Ids" queue + +Local worker thread: +- reads work ids from the "Work Ids" queue and looks up the corresponding + WorkItem from the "Work Items" dict: if the work item has been cancelled then + it is simply removed from the dict, otherwise it is repackaged as a + _CallItem and put in the "Call Q". New _CallItems are put in the "Call Q" + until "Call Q" is full. NOTE: the size of the "Call Q" is kept small because + calls placed in the "Call Q" can no longer be cancelled with Future.cancel(). +- reads _ResultItems from "Result Q", updates the future stored in the + "Work Items" dict and deletes the dict entry + +Process #1..n: +- reads _CallItems from "Call Q", executes the calls, and puts the resulting + _ResultItems in "Result Q" +""" + + +__author__ = "Thomas Moreau (thomas.moreau.2010@gmail.com)" + + +import os +import gc +import sys +import queue +import struct +import weakref +import warnings +import itertools +import traceback +import threading +from time import time, sleep +import multiprocessing as mp +from functools import partial +from pickle import PicklingError +from concurrent.futures import Executor +from concurrent.futures._base import LOGGER +from concurrent.futures.process import BrokenProcessPool as _BPPException +from multiprocessing.connection import wait + +from ._base import Future +from .backend import get_context +from .backend.context import cpu_count, _MAX_WINDOWS_WORKERS +from .backend.queues import Queue, SimpleQueue +from .backend.reduction import set_loky_pickler, get_loky_pickler_name +from .backend.utils import kill_process_tree, get_exitcodes_terminated_worker +from .initializers import _prepare_initializer + + +# Mechanism to prevent infinite process spawning. When a worker of a +# ProcessPoolExecutor nested in MAX_DEPTH Executor tries to create a new +# Executor, a LokyRecursionError is raised +MAX_DEPTH = int(os.environ.get("LOKY_MAX_DEPTH", 10)) +_CURRENT_DEPTH = 0 + +# Minimum time interval between two consecutive memory leak protection checks. +_MEMORY_LEAK_CHECK_DELAY = 1.0 + +# Number of bytes of memory usage allowed over the reference process size. +_MAX_MEMORY_LEAK_SIZE = int(3e8) + + +try: + from psutil import Process + + _USE_PSUTIL = True + + def _get_memory_usage(pid, force_gc=False): + if force_gc: + gc.collect() + + mem_size = Process(pid).memory_info().rss + mp.util.debug(f"psutil return memory size: {mem_size}") + return mem_size + +except ImportError: + _USE_PSUTIL = False + + +class _ThreadWakeup: + def __init__(self): + self._closed = False + self._reader, self._writer = mp.Pipe(duplex=False) + + def close(self): + if not self._closed: + self._closed = True + self._writer.close() + self._reader.close() + + def wakeup(self): + if not self._closed: + self._writer.send_bytes(b"") + + def clear(self): + if not self._closed: + while self._reader.poll(): + self._reader.recv_bytes() + + +class _ExecutorFlags: + """necessary references to maintain executor states without preventing gc + + It permits to keep the information needed by executor_manager_thread + and crash_detection_thread to maintain the pool without preventing the + garbage collection of unreferenced executors. + """ + + def __init__(self, shutdown_lock): + + self.shutdown = False + self.broken = None + self.kill_workers = False + self.shutdown_lock = shutdown_lock + + def flag_as_shutting_down(self, kill_workers=None): + with self.shutdown_lock: + self.shutdown = True + if kill_workers is not None: + self.kill_workers = kill_workers + + def flag_as_broken(self, broken): + with self.shutdown_lock: + self.shutdown = True + self.broken = broken + + +# Prior to 3.9, executor_manager_thread is created as daemon thread. This means +# that it is not joined automatically when the interpreter is shutting down. +# To work around this problem, an exit handler is installed to tell the +# thread to exit when the interpreter is shutting down and then waits until +# it finishes. The thread needs to be daemonized because the atexit hooks are +# called after all non daemonized threads are joined. +# +# Starting 3.9, there exists a specific atexit hook to be called before joining +# the threads so the executor_manager_thread does not need to be daemonized +# anymore. +# +# The atexit hooks are registered when starting the first ProcessPoolExecutor +# to avoid import having an effect on the interpreter. + +_global_shutdown = False +_global_shutdown_lock = threading.Lock() +_threads_wakeups = weakref.WeakKeyDictionary() + + +def _python_exit(): + global _global_shutdown + _global_shutdown = True + + # Materialize the list of items to avoid error due to iterating over + # changing size dictionary. + items = list(_threads_wakeups.items()) + if len(items) > 0: + mp.util.debug( + "Interpreter shutting down. Waking up {len(items)}" + f"executor_manager_thread:\n{items}" + ) + + # Wake up the executor_manager_thread's so they can detect the interpreter + # is shutting down and exit. + for _, (shutdown_lock, thread_wakeup) in items: + with shutdown_lock: + thread_wakeup.wakeup() + + # Collect the executor_manager_thread's to make sure we exit cleanly. + for thread, _ in items: + # This locks is to prevent situations where an executor is gc'ed in one + # thread while the atexit finalizer is running in another thread. This + # can happen when joblib is used in pypy for instance. + with _global_shutdown_lock: + thread.join() + + +# With the fork context, _thread_wakeups is propagated to children. +# Clear it after fork to avoid some situation that can cause some +# freeze when joining the workers. +mp.util.register_after_fork(_threads_wakeups, lambda obj: obj.clear()) + + +# Module variable to register the at_exit call +process_pool_executor_at_exit = None + +# Controls how many more calls than processes will be queued in the call queue. +# A smaller number will mean that processes spend more time idle waiting for +# work while a larger number will make Future.cancel() succeed less frequently +# (Futures in the call queue cannot be cancelled). +EXTRA_QUEUED_CALLS = 1 + + +class _RemoteTraceback(Exception): + """Embed stringification of remote traceback in local traceback""" + + def __init__(self, tb=None): + self.tb = f'\n"""\n{tb}"""' + + def __str__(self): + return self.tb + + +# Do not inherit from BaseException to mirror +# concurrent.futures.process._ExceptionWithTraceback +class _ExceptionWithTraceback: + def __init__(self, exc): + tb = getattr(exc, "__traceback__", None) + if tb is None: + _, _, tb = sys.exc_info() + tb = traceback.format_exception(type(exc), exc, tb) + tb = "".join(tb) + self.exc = exc + self.tb = tb + + def __reduce__(self): + return _rebuild_exc, (self.exc, self.tb) + + +def _rebuild_exc(exc, tb): + exc.__cause__ = _RemoteTraceback(tb) + return exc + + +class _WorkItem: + + __slots__ = ["future", "fn", "args", "kwargs"] + + def __init__(self, future, fn, args, kwargs): + self.future = future + self.fn = fn + self.args = args + self.kwargs = kwargs + + +class _ResultItem: + def __init__(self, work_id, exception=None, result=None): + self.work_id = work_id + self.exception = exception + self.result = result + + +class _CallItem: + def __init__(self, work_id, fn, args, kwargs): + self.work_id = work_id + self.fn = fn + self.args = args + self.kwargs = kwargs + + # Store the current loky_pickler so it is correctly set in the worker + self.loky_pickler = get_loky_pickler_name() + + def __call__(self): + set_loky_pickler(self.loky_pickler) + return self.fn(*self.args, **self.kwargs) + + def __repr__(self): + return ( + f"CallItem({self.work_id}, {self.fn}, {self.args}, {self.kwargs})" + ) + + +class _SafeQueue(Queue): + """Safe Queue set exception to the future object linked to a job""" + + def __init__( + self, + max_size=0, + ctx=None, + pending_work_items=None, + running_work_items=None, + thread_wakeup=None, + reducers=None, + ): + self.thread_wakeup = thread_wakeup + self.pending_work_items = pending_work_items + self.running_work_items = running_work_items + super().__init__(max_size, reducers=reducers, ctx=ctx) + + def _on_queue_feeder_error(self, e, obj): + if isinstance(obj, _CallItem): + # format traceback only works on python3 + if isinstance(e, struct.error): + raised_error = RuntimeError( + "The task could not be sent to the workers as it is too " + "large for `send_bytes`." + ) + else: + raised_error = PicklingError( + "Could not pickle the task to send it to the workers." + ) + tb = traceback.format_exception( + type(e), e, getattr(e, "__traceback__", None) + ) + raised_error.__cause__ = _RemoteTraceback("".join(tb)) + work_item = self.pending_work_items.pop(obj.work_id, None) + self.running_work_items.remove(obj.work_id) + # work_item can be None if another process terminated. In this + # case, the executor_manager_thread fails all work_items with + # BrokenProcessPool + if work_item is not None: + work_item.future.set_exception(raised_error) + del work_item + self.thread_wakeup.wakeup() + else: + super()._on_queue_feeder_error(e, obj) + + +def _get_chunks(chunksize, *iterables): + """Iterates over zip()ed iterables in chunks.""" + it = zip(*iterables) + while True: + chunk = tuple(itertools.islice(it, chunksize)) + if not chunk: + return + yield chunk + + +def _process_chunk(fn, chunk): + """Processes a chunk of an iterable passed to map. + + Runs the function passed to map() on a chunk of the + iterable passed to map. + + This function is run in a separate process. + + """ + return [fn(*args) for args in chunk] + + +def _sendback_result(result_queue, work_id, result=None, exception=None): + """Safely send back the given result or exception""" + try: + result_queue.put( + _ResultItem(work_id, result=result, exception=exception) + ) + except BaseException as e: + exc = _ExceptionWithTraceback(e) + result_queue.put(_ResultItem(work_id, exception=exc)) + + +def _process_worker( + call_queue, + result_queue, + initializer, + initargs, + processes_management_lock, + timeout, + worker_exit_lock, + current_depth, +): + """Evaluates calls from call_queue and places the results in result_queue. + + This worker is run in a separate process. + + Args: + call_queue: A ctx.Queue of _CallItems that will be read and + evaluated by the worker. + result_queue: A ctx.Queue of _ResultItems that will written + to by the worker. + initializer: A callable initializer, or None + initargs: A tuple of args for the initializer + processes_management_lock: A ctx.Lock avoiding worker timeout while + some workers are being spawned. + timeout: maximum time to wait for a new item in the call_queue. If that + time is expired, the worker will shutdown. + worker_exit_lock: Lock to avoid flagging the executor as broken on + workers timeout. + current_depth: Nested parallelism level, to avoid infinite spawning. + """ + if initializer is not None: + try: + initializer(*initargs) + except BaseException: + LOGGER.critical("Exception in initializer:", exc_info=True) + # The parent will notice that the process stopped and + # mark the pool broken + return + + # set the global _CURRENT_DEPTH mechanism to limit recursive call + global _CURRENT_DEPTH + _CURRENT_DEPTH = current_depth + _process_reference_size = None + _last_memory_leak_check = None + pid = os.getpid() + + mp.util.debug(f"Worker started with timeout={timeout}") + while True: + try: + call_item = call_queue.get(block=True, timeout=timeout) + if call_item is None: + mp.util.info("Shutting down worker on sentinel") + except queue.Empty: + mp.util.info(f"Shutting down worker after timeout {timeout:0.3f}s") + if processes_management_lock.acquire(block=False): + processes_management_lock.release() + call_item = None + else: + mp.util.info("Could not acquire processes_management_lock") + continue + except BaseException: + previous_tb = traceback.format_exc() + try: + result_queue.put(_RemoteTraceback(previous_tb)) + except BaseException: + # If we cannot format correctly the exception, at least print + # the traceback. + print(previous_tb) + mp.util.debug("Exiting with code 1") + sys.exit(1) + if call_item is None: + # Notify queue management thread about worker shutdown + result_queue.put(pid) + is_clean = worker_exit_lock.acquire(True, timeout=30) + + # Early notify any loky executor running in this worker process + # (nested parallelism) that this process is about to shutdown to + # avoid a deadlock waiting undifinitely for the worker to finish. + _python_exit() + + if is_clean: + mp.util.debug("Exited cleanly") + else: + mp.util.info("Main process did not release worker_exit") + return + try: + r = call_item() + except BaseException as e: + exc = _ExceptionWithTraceback(e) + result_queue.put(_ResultItem(call_item.work_id, exception=exc)) + else: + _sendback_result(result_queue, call_item.work_id, result=r) + del r + + # Free the resource as soon as possible, to avoid holding onto + # open files or shared memory that is not needed anymore + del call_item + + if _USE_PSUTIL: + if _process_reference_size is None: + # Make reference measurement after the first call + _process_reference_size = _get_memory_usage(pid, force_gc=True) + _last_memory_leak_check = time() + continue + if time() - _last_memory_leak_check > _MEMORY_LEAK_CHECK_DELAY: + mem_usage = _get_memory_usage(pid) + _last_memory_leak_check = time() + if mem_usage - _process_reference_size < _MAX_MEMORY_LEAK_SIZE: + # Memory usage stays within bounds: everything is fine. + continue + + # Check again memory usage; this time take the measurement + # after a forced garbage collection to break any reference + # cycles. + mem_usage = _get_memory_usage(pid, force_gc=True) + _last_memory_leak_check = time() + if mem_usage - _process_reference_size < _MAX_MEMORY_LEAK_SIZE: + # The GC managed to free the memory: everything is fine. + continue + + # The process is leaking memory: let the master process + # know that we need to start a new worker. + mp.util.info("Memory leak detected: shutting down worker") + result_queue.put(pid) + with worker_exit_lock: + mp.util.debug("Exit due to memory leak") + return + else: + # if psutil is not installed, trigger gc.collect events + # regularly to limit potential memory leaks due to reference cycles + if _last_memory_leak_check is None or ( + time() - _last_memory_leak_check > _MEMORY_LEAK_CHECK_DELAY + ): + gc.collect() + _last_memory_leak_check = time() + + +class _ExecutorManagerThread(threading.Thread): + """Manages the communication between this process and the worker processes. + + The manager is run in a local thread. + + Args: + executor: A reference to the ProcessPoolExecutor that owns + this thread. A weakref will be own by the manager as well as + references to internal objects used to introspect the state of + the executor. + """ + + def __init__(self, executor): + # Store references to necessary internals of the executor. + + # A _ThreadWakeup to allow waking up the executor_manager_thread from + # the main Thread and avoid deadlocks caused by permanently + # locked queues. + self.thread_wakeup = executor._executor_manager_thread_wakeup + self.shutdown_lock = executor._shutdown_lock + + # A weakref.ref to the ProcessPoolExecutor that owns this thread. Used + # to determine if the ProcessPoolExecutor has been garbage collected + # and that the manager can exit. + # When the executor gets garbage collected, the weakref callback + # will wake up the queue management thread so that it can terminate + # if there is no pending work item. + def weakref_cb( + _, + thread_wakeup=self.thread_wakeup, + shutdown_lock=self.shutdown_lock, + ): + if mp is not None: + # At this point, the multiprocessing module can already be + # garbage collected. We only log debug info when still + # possible. + mp.util.debug( + "Executor collected: triggering callback for" + " QueueManager wakeup" + ) + with shutdown_lock: + thread_wakeup.wakeup() + + self.executor_reference = weakref.ref(executor, weakref_cb) + + # The flags of the executor + self.executor_flags = executor._flags + + # A list of the ctx.Process instances used as workers. + self.processes = executor._processes + + # A ctx.Queue that will be filled with _CallItems derived from + # _WorkItems for processing by the process workers. + self.call_queue = executor._call_queue + + # A ctx.SimpleQueue of _ResultItems generated by the process workers. + self.result_queue = executor._result_queue + + # A queue.Queue of work ids e.g. Queue([5, 6, ...]). + self.work_ids_queue = executor._work_ids + + # A dict mapping work ids to _WorkItems e.g. + # {5: <_WorkItem...>, 6: <_WorkItem...>, ...} + self.pending_work_items = executor._pending_work_items + + # A list of the work_ids that are currently running + self.running_work_items = executor._running_work_items + + # A lock to avoid concurrent shutdown of workers on timeout and spawn + # of new processes or shut down + self.processes_management_lock = executor._processes_management_lock + + super().__init__(name="ExecutorManagerThread") + if sys.version_info < (3, 9): + self.daemon = True + + def run(self): + # Main loop for the executor manager thread. + + while True: + self.add_call_item_to_queue() + + result_item, is_broken, bpe = self.wait_result_broken_or_wakeup() + + if is_broken: + self.terminate_broken(bpe) + return + if result_item is not None: + self.process_result_item(result_item) + # Delete reference to result_item to avoid keeping references + # while waiting on new results. + del result_item + + if self.is_shutting_down(): + self.flag_executor_shutting_down() + + # Since no new work items can be added, it is safe to shutdown + # this thread if there are no pending work items. + if not self.pending_work_items: + self.join_executor_internals() + return + + def add_call_item_to_queue(self): + # Fills call_queue with _WorkItems from pending_work_items. + # This function never blocks. + while True: + if self.call_queue.full(): + return + try: + work_id = self.work_ids_queue.get(block=False) + except queue.Empty: + return + else: + work_item = self.pending_work_items[work_id] + + if work_item.future.set_running_or_notify_cancel(): + self.running_work_items += [work_id] + self.call_queue.put( + _CallItem( + work_id, + work_item.fn, + work_item.args, + work_item.kwargs, + ), + block=True, + ) + else: + del self.pending_work_items[work_id] + continue + + def wait_result_broken_or_wakeup(self): + # Wait for a result to be ready in the result_queue while checking + # that all worker processes are still running, or for a wake up + # signal send. The wake up signals come either from new tasks being + # submitted, from the executor being shutdown/gc-ed, or from the + # shutdown of the python interpreter. + result_reader = self.result_queue._reader + wakeup_reader = self.thread_wakeup._reader + readers = [result_reader, wakeup_reader] + worker_sentinels = [p.sentinel for p in list(self.processes.values())] + ready = wait(readers + worker_sentinels) + + bpe = None + is_broken = True + result_item = None + if result_reader in ready: + try: + result_item = result_reader.recv() + if isinstance(result_item, _RemoteTraceback): + bpe = BrokenProcessPool( + "A task has failed to un-serialize. Please ensure that" + " the arguments of the function are all picklable." + ) + bpe.__cause__ = result_item + else: + is_broken = False + except BaseException as e: + bpe = BrokenProcessPool( + "A result has failed to un-serialize. Please ensure that " + "the objects returned by the function are always " + "picklable." + ) + tb = traceback.format_exception( + type(e), e, getattr(e, "__traceback__", None) + ) + bpe.__cause__ = _RemoteTraceback("".join(tb)) + + elif wakeup_reader in ready: + # This is simply a wake-up event that might either trigger putting + # more tasks in the queue or trigger the clean up of resources. + is_broken = False + else: + # A worker has terminated and we don't know why, set the state of + # the executor as broken + exit_codes = "" + if sys.platform != "win32": + # In Windows, introspecting terminated workers exitcodes seems + # unstable, therefore they are not appended in the exception + # message. + exit_codes = ( + "\nThe exit codes of the workers are " + f"{get_exitcodes_terminated_worker(self.processes)}" + ) + mp.util.debug( + "A worker unexpectedly terminated. Workers that " + "might have caused the breakage: " + + str( + { + p.name: p.exitcode + for p in list(self.processes.values()) + if p is not None and p.sentinel in ready + } + ) + ) + bpe = TerminatedWorkerError( + "A worker process managed by the executor was unexpectedly " + "terminated. This could be caused by a segmentation fault " + "while calling the function or by an excessive memory usage " + "causing the Operating System to kill the worker.\n" + f"{exit_codes}" + ) + + self.thread_wakeup.clear() + + return result_item, is_broken, bpe + + def process_result_item(self, result_item): + # Process the received a result_item. This can be either the PID of a + # worker that exited gracefully or a _ResultItem + + if isinstance(result_item, int): + # Clean shutdown of a worker using its PID, either on request + # by the executor.shutdown method or by the timeout of the worker + # itself: we should not mark the executor as broken. + with self.processes_management_lock: + p = self.processes.pop(result_item, None) + + # p can be None if the executor is concurrently shutting down. + if p is not None: + p._worker_exit_lock.release() + mp.util.debug( + f"joining {p.name} when processing {p.pid} as result_item" + ) + p.join() + del p + + # Make sure the executor have the right number of worker, even if a + # worker timeout while some jobs were submitted. If some work is + # pending or there is less processes than running items, we need to + # start a new Process and raise a warning. + n_pending = len(self.pending_work_items) + n_running = len(self.running_work_items) + if n_pending - n_running > 0 or n_running > len(self.processes): + executor = self.executor_reference() + if ( + executor is not None + and len(self.processes) < executor._max_workers + ): + warnings.warn( + "A worker stopped while some jobs were given to the " + "executor. This can be caused by a too short worker " + "timeout or by a memory leak.", + UserWarning, + ) + with executor._processes_management_lock: + executor._adjust_process_count() + executor = None + else: + # Received a _ResultItem so mark the future as completed. + work_item = self.pending_work_items.pop(result_item.work_id, None) + # work_item can be None if another process terminated (see above) + if work_item is not None: + if result_item.exception: + work_item.future.set_exception(result_item.exception) + else: + work_item.future.set_result(result_item.result) + self.running_work_items.remove(result_item.work_id) + + def is_shutting_down(self): + # Check whether we should start shutting down the executor. + executor = self.executor_reference() + # No more work items can be added if: + # - The interpreter is shutting down OR + # - The executor that owns this thread is not broken AND + # * The executor that owns this worker has been collected OR + # * The executor that owns this worker has been shutdown. + # If the executor is broken, it should be detected in the next loop. + return _global_shutdown or ( + (executor is None or self.executor_flags.shutdown) + and not self.executor_flags.broken + ) + + def terminate_broken(self, bpe): + # Terminate the executor because it is in a broken state. The bpe + # argument can be used to display more information on the error that + # lead the executor into becoming broken. + + # Mark the process pool broken so that submits fail right now. + self.executor_flags.flag_as_broken(bpe) + + # Mark pending tasks as failed. + for work_item in self.pending_work_items.values(): + work_item.future.set_exception(bpe) + # Delete references to object. See issue16284 + del work_item + self.pending_work_items.clear() + + # Terminate remaining workers forcibly: the queues or their + # locks may be in a dirty state and block forever. + self.kill_workers(reason="broken executor") + + # clean up resources + self.join_executor_internals() + + def flag_executor_shutting_down(self): + # Flag the executor as shutting down and cancel remaining tasks if + # requested as early as possible if it is not gc-ed yet. + self.executor_flags.flag_as_shutting_down() + + # Cancel pending work items if requested. + if self.executor_flags.kill_workers: + while self.pending_work_items: + _, work_item = self.pending_work_items.popitem() + work_item.future.set_exception( + ShutdownExecutorError( + "The Executor was shutdown with `kill_workers=True` " + "before this job could complete." + ) + ) + del work_item + + # Kill the remaining worker forcibly to no waste time joining them + self.kill_workers(reason="executor shutting down") + + def kill_workers(self, reason=""): + # Terminate the remaining workers using SIGKILL. This function also + # terminates descendant workers of the children in case there is some + # nested parallelism. + while self.processes: + _, p = self.processes.popitem() + mp.util.debug(f"terminate process {p.name}, reason: {reason}") + try: + kill_process_tree(p) + except ProcessLookupError: # pragma: no cover + pass + + def shutdown_workers(self): + # shutdown all workers in self.processes + + # Create a list to avoid RuntimeError due to concurrent modification of + # processes. nb_children_alive is thus an upper bound. Also release the + # processes' _worker_exit_lock to accelerate the shutdown procedure, as + # there is no need for hand-shake here. + with self.processes_management_lock: + n_children_to_stop = 0 + for p in list(self.processes.values()): + mp.util.debug(f"releasing worker exit lock on {p.name}") + p._worker_exit_lock.release() + n_children_to_stop += 1 + + mp.util.debug(f"found {n_children_to_stop} processes to stop") + + # Send the right number of sentinels, to make sure all children are + # properly terminated. Do it with a mechanism that avoid hanging on + # Full queue when all workers have already been shutdown. + n_sentinels_sent = 0 + cooldown_time = 0.001 + while ( + n_sentinels_sent < n_children_to_stop + and self.get_n_children_alive() > 0 + ): + for _ in range(n_children_to_stop - n_sentinels_sent): + try: + self.call_queue.put_nowait(None) + n_sentinels_sent += 1 + except queue.Full as e: + if cooldown_time > 5.0: + mp.util.info( + "failed to send all sentinels and exit with error." + f"\ncall_queue size={self.call_queue._maxsize}; " + f" full is {self.call_queue.full()}; " + ) + raise e + mp.util.info( + "full call_queue prevented to send all sentinels at " + "once, waiting..." + ) + sleep(cooldown_time) + cooldown_time *= 1.2 + break + + mp.util.debug(f"sent {n_sentinels_sent} sentinels to the call queue") + + def join_executor_internals(self): + self.shutdown_workers() + + # Release the queue's resources as soon as possible. Flag the feeder + # thread for clean exit to avoid having the crash detection thread flag + # the Executor as broken during the shutdown. This is safe as either: + # * We don't need to communicate with the workers anymore + # * There is nothing left in the Queue buffer except None sentinels + mp.util.debug("closing call_queue") + self.call_queue.close() + self.call_queue.join_thread() + + # Closing result_queue + mp.util.debug("closing result_queue") + self.result_queue.close() + + mp.util.debug("closing thread_wakeup") + with self.shutdown_lock: + self.thread_wakeup.close() + + # If .join() is not called on the created processes then + # some ctx.Queue methods may deadlock on macOS. + with self.processes_management_lock: + mp.util.debug(f"joining {len(self.processes)} processes") + n_joined_processes = 0 + while True: + try: + pid, p = self.processes.popitem() + mp.util.debug(f"joining process {p.name} with pid {pid}") + p.join() + n_joined_processes += 1 + except KeyError: + break + + mp.util.debug( + "executor management thread clean shutdown of " + f"{n_joined_processes} workers" + ) + + def get_n_children_alive(self): + # This is an upper bound on the number of children alive. + with self.processes_management_lock: + return sum(p.is_alive() for p in list(self.processes.values())) + + +_system_limits_checked = False +_system_limited = None + + +def _check_system_limits(): + global _system_limits_checked, _system_limited + if _system_limits_checked and _system_limited: + raise NotImplementedError(_system_limited) + _system_limits_checked = True + try: + nsems_max = os.sysconf("SC_SEM_NSEMS_MAX") + except (AttributeError, ValueError): + # sysconf not available or setting not available + return + if nsems_max == -1: + # undetermined limit, assume that limit is determined + # by available memory only + return + if nsems_max >= 256: + # minimum number of semaphores available + # according to POSIX + return + _system_limited = ( + f"system provides too few semaphores ({nsems_max} available, " + "256 necessary)" + ) + raise NotImplementedError(_system_limited) + + +def _chain_from_iterable_of_lists(iterable): + """ + Specialized implementation of itertools.chain.from_iterable. + Each item in *iterable* should be a list. This function is + careful not to keep references to yielded objects. + """ + for element in iterable: + element.reverse() + while element: + yield element.pop() + + +def _check_max_depth(context): + # Limit the maxmal recursion level + global _CURRENT_DEPTH + if context.get_start_method() == "fork" and _CURRENT_DEPTH > 0: + raise LokyRecursionError( + "Could not spawn extra nested processes at depth superior to " + "MAX_DEPTH=1. It is not possible to increase this limit when " + "using the 'fork' start method." + ) + + if 0 < MAX_DEPTH and _CURRENT_DEPTH + 1 > MAX_DEPTH: + raise LokyRecursionError( + "Could not spawn extra nested processes at depth superior to " + f"MAX_DEPTH={MAX_DEPTH}. If this is intendend, you can change " + "this limit with the LOKY_MAX_DEPTH environment variable." + ) + + +class LokyRecursionError(RuntimeError): + """A process tries to spawn too many levels of nested processes.""" + + +class BrokenProcessPool(_BPPException): + """ + Raised when the executor is broken while a future was in the running state. + The cause can an error raised when unpickling the task in the worker + process or when unpickling the result value in the parent process. It can + also be caused by a worker process being terminated unexpectedly. + """ + + +class TerminatedWorkerError(BrokenProcessPool): + """ + Raised when a process in a ProcessPoolExecutor terminated abruptly + while a future was in the running state. + """ + + +# Alias for backward compat (for code written for loky 1.1.4 and earlier). Do +# not use in new code. +BrokenExecutor = BrokenProcessPool + + +class ShutdownExecutorError(RuntimeError): + + """ + Raised when a ProcessPoolExecutor is shutdown while a future was in the + running or pending state. + """ + + +class ProcessPoolExecutor(Executor): + + _at_exit = None + + def __init__( + self, + max_workers=None, + job_reducers=None, + result_reducers=None, + timeout=None, + context=None, + initializer=None, + initargs=(), + env=None, + ): + """Initializes a new ProcessPoolExecutor instance. + + Args: + max_workers: int, optional (default: cpu_count()) + The maximum number of processes that can be used to execute the + given calls. If None or not given then as many worker processes + will be created as the number of CPUs the current process + can use. + job_reducers, result_reducers: dict(type: reducer_func) + Custom reducer for pickling the jobs and the results from the + Executor. If only `job_reducers` is provided, `result_reducer` + will use the same reducers + timeout: int, optional (default: None) + Idle workers exit after timeout seconds. If a new job is + submitted after the timeout, the executor will start enough + new Python processes to make sure the pool of workers is full. + context: A multiprocessing context to launch the workers. This + object should provide SimpleQueue, Queue and Process. + initializer: An callable used to initialize worker processes. + initargs: A tuple of arguments to pass to the initializer. + env: A dict of environment variable to overwrite in the child + process. The environment variables are set before any module is + loaded. Note that this only works with the loky context. + """ + _check_system_limits() + + if max_workers is None: + self._max_workers = cpu_count() + else: + if max_workers <= 0: + raise ValueError("max_workers must be greater than 0") + self._max_workers = max_workers + + if ( + sys.platform == "win32" + and self._max_workers > _MAX_WINDOWS_WORKERS + ): + warnings.warn( + f"On Windows, max_workers cannot exceed {_MAX_WINDOWS_WORKERS} " + "due to limitations of the operating system." + ) + self._max_workers = _MAX_WINDOWS_WORKERS + + if context is None: + context = get_context() + self._context = context + self._env = env + + self._initializer, self._initargs = _prepare_initializer( + initializer, initargs + ) + _check_max_depth(self._context) + + if result_reducers is None: + result_reducers = job_reducers + + # Timeout + self._timeout = timeout + + # Management thread + self._executor_manager_thread = None + + # Map of pids to processes + self._processes = {} + + # Internal variables of the ProcessPoolExecutor + self._processes = {} + self._queue_count = 0 + self._pending_work_items = {} + self._running_work_items = [] + self._work_ids = queue.Queue() + self._processes_management_lock = self._context.Lock() + self._executor_manager_thread = None + self._shutdown_lock = threading.Lock() + + # _ThreadWakeup is a communication channel used to interrupt the wait + # of the main loop of executor_manager_thread from another thread (e.g. + # when calling executor.submit or executor.shutdown). We do not use the + # _result_queue to send wakeup signals to the executor_manager_thread + # as it could result in a deadlock if a worker process dies with the + # _result_queue write lock still acquired. + # + # _shutdown_lock must be locked to access _ThreadWakeup.wakeup. + self._executor_manager_thread_wakeup = _ThreadWakeup() + + # Flag to hold the state of the Executor. This permits to introspect + # the Executor state even once it has been garbage collected. + self._flags = _ExecutorFlags(self._shutdown_lock) + + # Finally setup the queues for interprocess communication + self._setup_queues(job_reducers, result_reducers) + + mp.util.debug("ProcessPoolExecutor is setup") + + def _setup_queues(self, job_reducers, result_reducers, queue_size=None): + # Make the call queue slightly larger than the number of processes to + # prevent the worker processes from idling. But don't make it too big + # because futures in the call queue cannot be cancelled. + if queue_size is None: + queue_size = 2 * self._max_workers + EXTRA_QUEUED_CALLS + self._call_queue = _SafeQueue( + max_size=queue_size, + pending_work_items=self._pending_work_items, + running_work_items=self._running_work_items, + thread_wakeup=self._executor_manager_thread_wakeup, + reducers=job_reducers, + ctx=self._context, + ) + # Killed worker processes can produce spurious "broken pipe" + # tracebacks in the queue's own worker thread. But we detect killed + # processes anyway, so silence the tracebacks. + self._call_queue._ignore_epipe = True + + self._result_queue = SimpleQueue( + reducers=result_reducers, ctx=self._context + ) + + def _start_executor_manager_thread(self): + if self._executor_manager_thread is None: + mp.util.debug("_start_executor_manager_thread called") + + # Start the processes so that their sentinels are known. + self._executor_manager_thread = _ExecutorManagerThread(self) + self._executor_manager_thread.start() + + # register this executor in a mechanism that ensures it will wakeup + # when the interpreter is exiting. + _threads_wakeups[self._executor_manager_thread] = ( + self._shutdown_lock, + self._executor_manager_thread_wakeup, + ) + + global process_pool_executor_at_exit + if process_pool_executor_at_exit is None: + # Ensure that the _python_exit function will be called before + # the multiprocessing.Queue._close finalizers which have an + # exitpriority of 10. + + if sys.version_info < (3, 9): + process_pool_executor_at_exit = mp.util.Finalize( + None, _python_exit, exitpriority=20 + ) + else: + process_pool_executor_at_exit = threading._register_atexit( + _python_exit + ) + + def _adjust_process_count(self): + while len(self._processes) < self._max_workers: + worker_exit_lock = self._context.BoundedSemaphore(1) + args = ( + self._call_queue, + self._result_queue, + self._initializer, + self._initargs, + self._processes_management_lock, + self._timeout, + worker_exit_lock, + _CURRENT_DEPTH + 1, + ) + worker_exit_lock.acquire() + try: + # Try to spawn the process with some environment variable to + # overwrite but it only works with the loky context for now. + p = self._context.Process( + target=_process_worker, args=args, env=self._env + ) + except TypeError: + p = self._context.Process(target=_process_worker, args=args) + p._worker_exit_lock = worker_exit_lock + p.start() + self._processes[p.pid] = p + mp.util.debug( + f"Adjusted process count to {self._max_workers}: " + f"{[(p.name, pid) for pid, p in self._processes.items()]}" + ) + + def _ensure_executor_running(self): + """ensures all workers and management thread are running""" + with self._processes_management_lock: + if len(self._processes) != self._max_workers: + self._adjust_process_count() + self._start_executor_manager_thread() + + def submit(self, fn, *args, **kwargs): + with self._flags.shutdown_lock: + if self._flags.broken is not None: + raise self._flags.broken + if self._flags.shutdown: + raise ShutdownExecutorError( + "cannot schedule new futures after shutdown" + ) + + # Cannot submit a new calls once the interpreter is shutting down. + # This check avoids spawning new processes at exit. + if _global_shutdown: + raise RuntimeError( + "cannot schedule new futures after " "interpreter shutdown" + ) + + f = Future() + w = _WorkItem(f, fn, args, kwargs) + + self._pending_work_items[self._queue_count] = w + self._work_ids.put(self._queue_count) + self._queue_count += 1 + # Wake up queue management thread + self._executor_manager_thread_wakeup.wakeup() + + self._ensure_executor_running() + return f + + submit.__doc__ = Executor.submit.__doc__ + + def map(self, fn, *iterables, **kwargs): + """Returns an iterator equivalent to map(fn, iter). + + Args: + fn: A callable that will take as many arguments as there are + passed iterables. + timeout: The maximum number of seconds to wait. If None, then there + is no limit on the wait time. + chunksize: If greater than one, the iterables will be chopped into + chunks of size chunksize and submitted to the process pool. + If set to one, the items in the list will be sent one at a + time. + + Returns: + An iterator equivalent to: map(func, *iterables) but the calls may + be evaluated out-of-order. + + Raises: + TimeoutError: If the entire result iterator could not be generated + before the given timeout. + Exception: If fn(*args) raises for any values. + """ + timeout = kwargs.get("timeout", None) + chunksize = kwargs.get("chunksize", 1) + if chunksize < 1: + raise ValueError("chunksize must be >= 1.") + + results = super().map( + partial(_process_chunk, fn), + _get_chunks(chunksize, *iterables), + timeout=timeout, + ) + return _chain_from_iterable_of_lists(results) + + def shutdown(self, wait=True, kill_workers=False): + mp.util.debug(f"shutting down executor {self}") + + self._flags.flag_as_shutting_down(kill_workers) + executor_manager_thread = self._executor_manager_thread + executor_manager_thread_wakeup = self._executor_manager_thread_wakeup + + if executor_manager_thread_wakeup is not None: + # Wake up queue management thread + with self._shutdown_lock: + self._executor_manager_thread_wakeup.wakeup() + + if executor_manager_thread is not None and wait: + # This locks avoids concurrent join if the interpreter + # is shutting down. + with _global_shutdown_lock: + executor_manager_thread.join() + _threads_wakeups.pop(executor_manager_thread, None) + + # To reduce the risk of opening too many files, remove references to + # objects that use file descriptors. + self._executor_manager_thread = None + self._executor_manager_thread_wakeup = None + self._call_queue = None + self._result_queue = None + self._processes_management_lock = None + + shutdown.__doc__ = Executor.shutdown.__doc__ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/reusable_executor.py b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/reusable_executor.py new file mode 100644 index 0000000..ad016fd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/externals/loky/reusable_executor.py @@ -0,0 +1,285 @@ +############################################################################### +# Reusable ProcessPoolExecutor +# +# author: Thomas Moreau and Olivier Grisel +# +import time +import warnings +import threading +import multiprocessing as mp + +from .process_executor import ProcessPoolExecutor, EXTRA_QUEUED_CALLS +from .backend.context import cpu_count +from .backend import get_context + +__all__ = ["get_reusable_executor"] + +# Singleton executor and id management +_executor_lock = threading.RLock() +_next_executor_id = 0 +_executor = None +_executor_kwargs = None + + +def _get_next_executor_id(): + """Ensure that each successive executor instance has a unique, monotonic id. + + The purpose of this monotonic id is to help debug and test automated + instance creation. + """ + global _next_executor_id + with _executor_lock: + executor_id = _next_executor_id + _next_executor_id += 1 + return executor_id + + +def get_reusable_executor( + max_workers=None, + context=None, + timeout=10, + kill_workers=False, + reuse="auto", + job_reducers=None, + result_reducers=None, + initializer=None, + initargs=(), + env=None, +): + """Return the current ReusableExectutor instance. + + Start a new instance if it has not been started already or if the previous + instance was left in a broken state. + + If the previous instance does not have the requested number of workers, the + executor is dynamically resized to adjust the number of workers prior to + returning. + + Reusing a singleton instance spares the overhead of starting new worker + processes and importing common python packages each time. + + ``max_workers`` controls the maximum number of tasks that can be running in + parallel in worker processes. By default this is set to the number of + CPUs on the host. + + Setting ``timeout`` (in seconds) makes idle workers automatically shutdown + so as to release system resources. New workers are respawn upon submission + of new tasks so that ``max_workers`` are available to accept the newly + submitted tasks. Setting ``timeout`` to around 100 times the time required + to spawn new processes and import packages in them (on the order of 100ms) + ensures that the overhead of spawning workers is negligible. + + Setting ``kill_workers=True`` makes it possible to forcibly interrupt + previously spawned jobs to get a new instance of the reusable executor + with new constructor argument values. + + The ``job_reducers`` and ``result_reducers`` are used to customize the + pickling of tasks and results send to the executor. + + When provided, the ``initializer`` is run first in newly spawned + processes with argument ``initargs``. + + The environment variable in the child process are a copy of the values in + the main process. One can provide a dict ``{ENV: VAL}`` where ``ENV`` and + ``VAL`` are string literals to overwrite the environment variable ``ENV`` + in the child processes to value ``VAL``. The environment variables are set + in the children before any module is loaded. This only works with the + ``loky`` context. + """ + _executor, _ = _ReusablePoolExecutor.get_reusable_executor( + max_workers=max_workers, + context=context, + timeout=timeout, + kill_workers=kill_workers, + reuse=reuse, + job_reducers=job_reducers, + result_reducers=result_reducers, + initializer=initializer, + initargs=initargs, + env=env, + ) + return _executor + + +class _ReusablePoolExecutor(ProcessPoolExecutor): + def __init__( + self, + submit_resize_lock, + max_workers=None, + context=None, + timeout=None, + executor_id=0, + job_reducers=None, + result_reducers=None, + initializer=None, + initargs=(), + env=None, + ): + super().__init__( + max_workers=max_workers, + context=context, + timeout=timeout, + job_reducers=job_reducers, + result_reducers=result_reducers, + initializer=initializer, + initargs=initargs, + env=env, + ) + self.executor_id = executor_id + self._submit_resize_lock = submit_resize_lock + + @classmethod + def get_reusable_executor( + cls, + max_workers=None, + context=None, + timeout=10, + kill_workers=False, + reuse="auto", + job_reducers=None, + result_reducers=None, + initializer=None, + initargs=(), + env=None, + ): + with _executor_lock: + global _executor, _executor_kwargs + executor = _executor + + if max_workers is None: + if reuse is True and executor is not None: + max_workers = executor._max_workers + else: + max_workers = cpu_count() + elif max_workers <= 0: + raise ValueError( + f"max_workers must be greater than 0, got {max_workers}." + ) + + if isinstance(context, str): + context = get_context(context) + if context is not None and context.get_start_method() == "fork": + raise ValueError( + "Cannot use reusable executor with the 'fork' context" + ) + + kwargs = dict( + context=context, + timeout=timeout, + job_reducers=job_reducers, + result_reducers=result_reducers, + initializer=initializer, + initargs=initargs, + env=env, + ) + if executor is None: + is_reused = False + mp.util.debug( + f"Create a executor with max_workers={max_workers}." + ) + executor_id = _get_next_executor_id() + _executor_kwargs = kwargs + _executor = executor = cls( + _executor_lock, + max_workers=max_workers, + executor_id=executor_id, + **kwargs, + ) + else: + if reuse == "auto": + reuse = kwargs == _executor_kwargs + if ( + executor._flags.broken + or executor._flags.shutdown + or not reuse + ): + if executor._flags.broken: + reason = "broken" + elif executor._flags.shutdown: + reason = "shutdown" + else: + reason = "arguments have changed" + mp.util.debug( + "Creating a new executor with max_workers=" + f"{max_workers} as the previous instance cannot be " + f"reused ({reason})." + ) + executor.shutdown(wait=True, kill_workers=kill_workers) + _executor = executor = _executor_kwargs = None + # Recursive call to build a new instance + return cls.get_reusable_executor( + max_workers=max_workers, **kwargs + ) + else: + mp.util.debug( + "Reusing existing executor with " + f"max_workers={executor._max_workers}." + ) + is_reused = True + executor._resize(max_workers) + + return executor, is_reused + + def submit(self, fn, *args, **kwargs): + with self._submit_resize_lock: + return super().submit(fn, *args, **kwargs) + + def _resize(self, max_workers): + with self._submit_resize_lock: + if max_workers is None: + raise ValueError("Trying to resize with max_workers=None") + elif max_workers == self._max_workers: + return + + if self._executor_manager_thread is None: + # If the executor_manager_thread has not been started + # then no processes have been spawned and we can just + # update _max_workers and return + self._max_workers = max_workers + return + + self._wait_job_completion() + + # Some process might have returned due to timeout so check how many + # children are still alive. Use the _process_management_lock to + # ensure that no process are spawned or timeout during the resize. + with self._processes_management_lock: + processes = list(self._processes.values()) + nb_children_alive = sum(p.is_alive() for p in processes) + self._max_workers = max_workers + for _ in range(max_workers, nb_children_alive): + self._call_queue.put(None) + while ( + len(self._processes) > max_workers and not self._flags.broken + ): + time.sleep(1e-3) + + self._adjust_process_count() + processes = list(self._processes.values()) + while not all(p.is_alive() for p in processes): + time.sleep(1e-3) + + def _wait_job_completion(self): + """Wait for the cache to be empty before resizing the pool.""" + # Issue a warning to the user about the bad effect of this usage. + if self._pending_work_items: + warnings.warn( + "Trying to resize an executor with running jobs: " + "waiting for jobs completion before resizing.", + UserWarning, + ) + mp.util.debug( + f"Executor {self.executor_id} waiting for jobs completion " + "before resizing" + ) + # Wait for the completion of the jobs + while self._pending_work_items: + time.sleep(1e-3) + + def _setup_queues(self, job_reducers, result_reducers): + # As this executor can be resized, use a large queue size to avoid + # underestimating capacity and introducing overhead + queue_size = 2 * cpu_count() + EXTRA_QUEUED_CALLS + super()._setup_queues( + job_reducers, result_reducers, queue_size=queue_size + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/func_inspect.py b/.pythonlibs/lib/python3.10/site-packages/joblib/func_inspect.py new file mode 100644 index 0000000..3f80946 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/func_inspect.py @@ -0,0 +1,369 @@ +""" +My own variation on function-specific inspect-like features. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import inspect +import warnings +import re +import os +import collections + +from itertools import islice +from tokenize import open as open_py_source + +from .logger import pformat + +full_argspec_fields = ('args varargs varkw defaults kwonlyargs ' + 'kwonlydefaults annotations') +full_argspec_type = collections.namedtuple('FullArgSpec', full_argspec_fields) + + +def get_func_code(func): + """ Attempts to retrieve a reliable function code hash. + + The reason we don't use inspect.getsource is that it caches the + source, whereas we want this to be modified on the fly when the + function is modified. + + Returns + ------- + func_code: string + The function code + source_file: string + The path to the file in which the function is defined. + first_line: int + The first line of the code in the source file. + + Notes + ------ + This function does a bit more magic than inspect, and is thus + more robust. + """ + source_file = None + try: + code = func.__code__ + source_file = code.co_filename + if not os.path.exists(source_file): + # Use inspect for lambda functions and functions defined in an + # interactive shell, or in doctests + source_code = ''.join(inspect.getsourcelines(func)[0]) + line_no = 1 + if source_file.startswith('', source_file).groups() + line_no = int(line_no) + source_file = '' % source_file + return source_code, source_file, line_no + # Try to retrieve the source code. + with open_py_source(source_file) as source_file_obj: + first_line = code.co_firstlineno + # All the lines after the function definition: + source_lines = list(islice(source_file_obj, first_line - 1, None)) + return ''.join(inspect.getblock(source_lines)), source_file, first_line + except: # noqa: E722 + # If the source code fails, we use the hash. This is fragile and + # might change from one session to another. + if hasattr(func, '__code__'): + # Python 3.X + return str(func.__code__.__hash__()), source_file, -1 + else: + # Weird objects like numpy ufunc don't have __code__ + # This is fragile, as quite often the id of the object is + # in the repr, so it might not persist across sessions, + # however it will work for ufuncs. + return repr(func), source_file, -1 + + +def _clean_win_chars(string): + """Windows cannot encode some characters in filename.""" + import urllib + if hasattr(urllib, 'quote'): + quote = urllib.quote + else: + # In Python 3, quote is elsewhere + import urllib.parse + quote = urllib.parse.quote + for char in ('<', '>', '!', ':', '\\'): + string = string.replace(char, quote(char)) + return string + + +def get_func_name(func, resolv_alias=True, win_characters=True): + """ Return the function import path (as a list of module names), and + a name for the function. + + Parameters + ---------- + func: callable + The func to inspect + resolv_alias: boolean, optional + If true, possible local aliases are indicated. + win_characters: boolean, optional + If true, substitute special characters using urllib.quote + This is useful in Windows, as it cannot encode some filenames + """ + if hasattr(func, '__module__'): + module = func.__module__ + else: + try: + module = inspect.getmodule(func) + except TypeError: + if hasattr(func, '__class__'): + module = func.__class__.__module__ + else: + module = 'unknown' + if module is None: + # Happens in doctests, eg + module = '' + if module == '__main__': + try: + filename = os.path.abspath(inspect.getsourcefile(func)) + except: # noqa: E722 + filename = None + if filename is not None: + # mangling of full path to filename + parts = filename.split(os.sep) + if parts[-1].startswith(', where: + # - N is the cell number where the function was defined + # - XYZ is a hash representing the function's code (and name). + # It will be consistent across sessions and kernel restarts, + # and will change if the function's code/name changes + # We remove N so that cache is properly hit if the cell where + # the func is defined is re-exectuted. + # The XYZ hash should avoid collisions between functions with + # the same name, both within the same notebook but also across + # notebooks + splitted = parts[-1].split('-') + parts[-1] = '-'.join(splitted[:2] + splitted[3:]) + elif len(parts) > 2 and parts[-2].startswith('ipykernel_'): + # In a notebook session (ipykernel). Filename seems to be 'xyz' + # of above. parts[-2] has the structure ipykernel_XXXXXX where + # XXXXXX is a six-digit number identifying the current run (?). + # If we split it off, the function again has the same + # identifier across runs. + parts[-2] = 'ipykernel' + filename = '-'.join(parts) + if filename.endswith('.py'): + filename = filename[:-3] + module = module + '-' + filename + module = module.split('.') + if hasattr(func, 'func_name'): + name = func.func_name + elif hasattr(func, '__name__'): + name = func.__name__ + else: + name = 'unknown' + # Hack to detect functions not defined at the module-level + if resolv_alias: + # TODO: Maybe add a warning here? + if hasattr(func, 'func_globals') and name in func.func_globals: + if not func.func_globals[name] is func: + name = '%s-alias' % name + if hasattr(func, '__qualname__') and func.__qualname__ != name: + # Extend the module name in case of nested functions to avoid + # (module, name) collisions + module.extend(func.__qualname__.split(".")[:-1]) + if inspect.ismethod(func): + # We need to add the name of the class + if hasattr(func, 'im_class'): + klass = func.im_class + module.append(klass.__name__) + if os.name == 'nt' and win_characters: + # Windows can't encode certain characters in filenames + name = _clean_win_chars(name) + module = [_clean_win_chars(s) for s in module] + return module, name + + +def _signature_str(function_name, arg_sig): + """Helper function to output a function signature""" + return '{}{}'.format(function_name, arg_sig) + + +def _function_called_str(function_name, args, kwargs): + """Helper function to output a function call""" + template_str = '{0}({1}, {2})' + + args_str = repr(args)[1:-1] + kwargs_str = ', '.join('%s=%s' % (k, v) + for k, v in kwargs.items()) + return template_str.format(function_name, args_str, + kwargs_str) + + +def filter_args(func, ignore_lst, args=(), kwargs=dict()): + """ Filters the given args and kwargs using a list of arguments to + ignore, and a function specification. + + Parameters + ---------- + func: callable + Function giving the argument specification + ignore_lst: list of strings + List of arguments to ignore (either a name of an argument + in the function spec, or '*', or '**') + *args: list + Positional arguments passed to the function. + **kwargs: dict + Keyword arguments passed to the function + + Returns + ------- + filtered_args: list + List of filtered positional and keyword arguments. + """ + args = list(args) + if isinstance(ignore_lst, str): + # Catch a common mistake + raise ValueError( + 'ignore_lst must be a list of parameters to ignore ' + '%s (type %s) was given' % (ignore_lst, type(ignore_lst))) + # Special case for functools.partial objects + if (not inspect.ismethod(func) and not inspect.isfunction(func)): + if ignore_lst: + warnings.warn('Cannot inspect object %s, ignore list will ' + 'not work.' % func, stacklevel=2) + return {'*': args, '**': kwargs} + arg_sig = inspect.signature(func) + arg_names = [] + arg_defaults = [] + arg_kwonlyargs = [] + arg_varargs = None + arg_varkw = None + for param in arg_sig.parameters.values(): + if param.kind is param.POSITIONAL_OR_KEYWORD: + arg_names.append(param.name) + elif param.kind is param.KEYWORD_ONLY: + arg_names.append(param.name) + arg_kwonlyargs.append(param.name) + elif param.kind is param.VAR_POSITIONAL: + arg_varargs = param.name + elif param.kind is param.VAR_KEYWORD: + arg_varkw = param.name + if param.default is not param.empty: + arg_defaults.append(param.default) + if inspect.ismethod(func): + # First argument is 'self', it has been removed by Python + # we need to add it back: + args = [func.__self__, ] + args + # func is an instance method, inspect.signature(func) does not + # include self, we need to fetch it from the class method, i.e + # func.__func__ + class_method_sig = inspect.signature(func.__func__) + self_name = next(iter(class_method_sig.parameters)) + arg_names = [self_name] + arg_names + # XXX: Maybe I need an inspect.isbuiltin to detect C-level methods, such + # as on ndarrays. + + _, name = get_func_name(func, resolv_alias=False) + arg_dict = dict() + arg_position = -1 + for arg_position, arg_name in enumerate(arg_names): + if arg_position < len(args): + # Positional argument or keyword argument given as positional + if arg_name not in arg_kwonlyargs: + arg_dict[arg_name] = args[arg_position] + else: + raise ValueError( + "Keyword-only parameter '%s' was passed as " + 'positional parameter for %s:\n' + ' %s was called.' + % (arg_name, + _signature_str(name, arg_sig), + _function_called_str(name, args, kwargs)) + ) + + else: + position = arg_position - len(arg_names) + if arg_name in kwargs: + arg_dict[arg_name] = kwargs[arg_name] + else: + try: + arg_dict[arg_name] = arg_defaults[position] + except (IndexError, KeyError) as e: + # Missing argument + raise ValueError( + 'Wrong number of arguments for %s:\n' + ' %s was called.' + % (_signature_str(name, arg_sig), + _function_called_str(name, args, kwargs)) + ) from e + + varkwargs = dict() + for arg_name, arg_value in sorted(kwargs.items()): + if arg_name in arg_dict: + arg_dict[arg_name] = arg_value + elif arg_varkw is not None: + varkwargs[arg_name] = arg_value + else: + raise TypeError("Ignore list for %s() contains an unexpected " + "keyword argument '%s'" % (name, arg_name)) + + if arg_varkw is not None: + arg_dict['**'] = varkwargs + if arg_varargs is not None: + varargs = args[arg_position + 1:] + arg_dict['*'] = varargs + + # Now remove the arguments to be ignored + for item in ignore_lst: + if item in arg_dict: + arg_dict.pop(item) + else: + raise ValueError("Ignore list: argument '%s' is not defined for " + "function %s" + % (item, + _signature_str(name, arg_sig)) + ) + # XXX: Return a sorted list of pairs? + return arg_dict + + +def _format_arg(arg): + formatted_arg = pformat(arg, indent=2) + if len(formatted_arg) > 1500: + formatted_arg = '%s...' % formatted_arg[:700] + return formatted_arg + + +def format_signature(func, *args, **kwargs): + # XXX: Should this use inspect.formatargvalues/formatargspec? + module, name = get_func_name(func) + module = [m for m in module if m] + if module: + module.append(name) + module_path = '.'.join(module) + else: + module_path = name + arg_str = list() + previous_length = 0 + for arg in args: + formatted_arg = _format_arg(arg) + if previous_length > 80: + formatted_arg = '\n%s' % formatted_arg + previous_length = len(formatted_arg) + arg_str.append(formatted_arg) + arg_str.extend(['%s=%s' % (v, _format_arg(i)) for v, i in kwargs.items()]) + arg_str = ', '.join(arg_str) + + signature = '%s(%s)' % (name, arg_str) + return module_path, signature + + +def format_call(func, args, kwargs, object_name="Memory"): + """ Returns a nicely formatted statement displaying the function + call with the given arguments. + """ + path, signature = format_signature(func, *args, **kwargs) + msg = '%s\n[%s] Calling %s...\n%s' % (80 * '_', object_name, + path, signature) + return msg + # XXX: Not using logging framework + # self.debug(msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/hashing.py b/.pythonlibs/lib/python3.10/site-packages/joblib/hashing.py new file mode 100644 index 0000000..6c081f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/hashing.py @@ -0,0 +1,265 @@ +""" +Fast cryptographic hash of Python objects, with a special case for fast +hashing of numpy arrays. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import pickle +import hashlib +import sys +import types +import struct +import io +import decimal + + +Pickler = pickle._Pickler + + +class _ConsistentSet(object): + """ Class used to ensure the hash of Sets is preserved + whatever the order of its items. + """ + def __init__(self, set_sequence): + # Forces order of elements in set to ensure consistent hash. + try: + # Trying first to order the set assuming the type of elements is + # consistent and orderable. + # This fails on python 3 when elements are unorderable + # but we keep it in a try as it's faster. + self._sequence = sorted(set_sequence) + except (TypeError, decimal.InvalidOperation): + # If elements are unorderable, sorting them using their hash. + # This is slower but works in any case. + self._sequence = sorted((hash(e) for e in set_sequence)) + + +class _MyHash(object): + """ Class used to hash objects that won't normally pickle """ + + def __init__(self, *args): + self.args = args + + +class Hasher(Pickler): + """ A subclass of pickler, to do cryptographic hashing, rather than + pickling. + """ + + def __init__(self, hash_name='md5'): + self.stream = io.BytesIO() + # By default we want a pickle protocol that only changes with + # the major python version and not the minor one + protocol = 3 + Pickler.__init__(self, self.stream, protocol=protocol) + # Initialise the hash obj + self._hash = hashlib.new(hash_name) + + def hash(self, obj, return_digest=True): + try: + self.dump(obj) + except pickle.PicklingError as e: + e.args += ('PicklingError while hashing %r: %r' % (obj, e),) + raise + dumps = self.stream.getvalue() + self._hash.update(dumps) + if return_digest: + return self._hash.hexdigest() + + def save(self, obj): + if isinstance(obj, (types.MethodType, type({}.pop))): + # the Pickler cannot pickle instance methods; here we decompose + # them into components that make them uniquely identifiable + if hasattr(obj, '__func__'): + func_name = obj.__func__.__name__ + else: + func_name = obj.__name__ + inst = obj.__self__ + if type(inst) is type(pickle): + obj = _MyHash(func_name, inst.__name__) + elif inst is None: + # type(None) or type(module) do not pickle + obj = _MyHash(func_name, inst) + else: + cls = obj.__self__.__class__ + obj = _MyHash(func_name, inst, cls) + Pickler.save(self, obj) + + def memoize(self, obj): + # We want hashing to be sensitive to value instead of reference. + # For example we want ['aa', 'aa'] and ['aa', 'aaZ'[:2]] + # to hash to the same value and that's why we disable memoization + # for strings + if isinstance(obj, (bytes, str)): + return + Pickler.memoize(self, obj) + + # The dispatch table of the pickler is not accessible in Python + # 3, as these lines are only bugware for IPython, we skip them. + def save_global(self, obj, name=None, pack=struct.pack): + # We have to override this method in order to deal with objects + # defined interactively in IPython that are not injected in + # __main__ + kwargs = dict(name=name, pack=pack) + del kwargs['pack'] + try: + Pickler.save_global(self, obj, **kwargs) + except pickle.PicklingError: + Pickler.save_global(self, obj, **kwargs) + module = getattr(obj, "__module__", None) + if module == '__main__': + my_name = name + if my_name is None: + my_name = obj.__name__ + mod = sys.modules[module] + if not hasattr(mod, my_name): + # IPython doesn't inject the variables define + # interactively in __main__ + setattr(mod, my_name, obj) + + dispatch = Pickler.dispatch.copy() + # builtin + dispatch[type(len)] = save_global + # type + dispatch[type(object)] = save_global + # classobj + dispatch[type(Pickler)] = save_global + # function + dispatch[type(pickle.dump)] = save_global + + def _batch_setitems(self, items): + # forces order of keys in dict to ensure consistent hash. + try: + # Trying first to compare dict assuming the type of keys is + # consistent and orderable. + # This fails on python 3 when keys are unorderable + # but we keep it in a try as it's faster. + Pickler._batch_setitems(self, iter(sorted(items))) + except TypeError: + # If keys are unorderable, sorting them using their hash. This is + # slower but works in any case. + Pickler._batch_setitems(self, iter(sorted((hash(k), v) + for k, v in items))) + + def save_set(self, set_items): + # forces order of items in Set to ensure consistent hash + Pickler.save(self, _ConsistentSet(set_items)) + + dispatch[type(set())] = save_set + + +class NumpyHasher(Hasher): + """ Special case the hasher for when numpy is loaded. + """ + + def __init__(self, hash_name='md5', coerce_mmap=False): + """ + Parameters + ---------- + hash_name: string + The hash algorithm to be used + coerce_mmap: boolean + Make no difference between np.memmap and np.ndarray + objects. + """ + self.coerce_mmap = coerce_mmap + Hasher.__init__(self, hash_name=hash_name) + # delayed import of numpy, to avoid tight coupling + import numpy as np + self.np = np + if hasattr(np, 'getbuffer'): + self._getbuffer = np.getbuffer + else: + self._getbuffer = memoryview + + def save(self, obj): + """ Subclass the save method, to hash ndarray subclass, rather + than pickling them. Off course, this is a total abuse of + the Pickler class. + """ + if isinstance(obj, self.np.ndarray) and not obj.dtype.hasobject: + # Compute a hash of the object + # The update function of the hash requires a c_contiguous buffer. + if obj.shape == (): + # 0d arrays need to be flattened because viewing them as bytes + # raises a ValueError exception. + obj_c_contiguous = obj.flatten() + elif obj.flags.c_contiguous: + obj_c_contiguous = obj + elif obj.flags.f_contiguous: + obj_c_contiguous = obj.T + else: + # Cater for non-single-segment arrays: this creates a + # copy, and thus alleviates this issue. + # XXX: There might be a more efficient way of doing this + obj_c_contiguous = obj.flatten() + + # memoryview is not supported for some dtypes, e.g. datetime64, see + # https://github.com/numpy/numpy/issues/4983. The + # workaround is to view the array as bytes before + # taking the memoryview. + self._hash.update( + self._getbuffer(obj_c_contiguous.view(self.np.uint8))) + + # We store the class, to be able to distinguish between + # Objects with the same binary content, but different + # classes. + if self.coerce_mmap and isinstance(obj, self.np.memmap): + # We don't make the difference between memmap and + # normal ndarrays, to be able to reload previously + # computed results with memmap. + klass = self.np.ndarray + else: + klass = obj.__class__ + # We also return the dtype and the shape, to distinguish + # different views on the same data with different dtypes. + + # The object will be pickled by the pickler hashed at the end. + obj = (klass, ('HASHED', obj.dtype, obj.shape, obj.strides)) + elif isinstance(obj, self.np.dtype): + # numpy.dtype consistent hashing is tricky to get right. This comes + # from the fact that atomic np.dtype objects are interned: + # ``np.dtype('f4') is np.dtype('f4')``. The situation is + # complicated by the fact that this interning does not resist a + # simple pickle.load/dump roundtrip: + # ``pickle.loads(pickle.dumps(np.dtype('f4'))) is not + # np.dtype('f4') Because pickle relies on memoization during + # pickling, it is easy to + # produce different hashes for seemingly identical objects, such as + # ``[np.dtype('f4'), np.dtype('f4')]`` + # and ``[np.dtype('f4'), pickle.loads(pickle.dumps('f4'))]``. + # To prevent memoization from interfering with hashing, we isolate + # the serialization (and thus the pickle memoization) of each dtype + # using each time a different ``pickle.dumps`` call unrelated to + # the current Hasher instance. + self._hash.update("_HASHED_DTYPE".encode('utf-8')) + self._hash.update(pickle.dumps(obj)) + return + Hasher.save(self, obj) + + +def hash(obj, hash_name='md5', coerce_mmap=False): + """ Quick calculation of a hash to identify uniquely Python objects + containing numpy arrays. + + Parameters + ---------- + hash_name: 'md5' or 'sha1' + Hashing algorithm used. sha1 is supposedly safer, but md5 is + faster. + coerce_mmap: boolean + Make no difference between np.memmap and np.ndarray + """ + valid_hash_names = ('md5', 'sha1') + if hash_name not in valid_hash_names: + raise ValueError("Valid options for 'hash_name' are {}. " + "Got hash_name={!r} instead." + .format(valid_hash_names, hash_name)) + if 'numpy' in sys.modules: + hasher = NumpyHasher(hash_name=hash_name, coerce_mmap=coerce_mmap) + else: + hasher = Hasher(hash_name=hash_name) + return hasher.hash(obj) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/logger.py b/.pythonlibs/lib/python3.10/site-packages/joblib/logger.py new file mode 100644 index 0000000..cf9d258 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/logger.py @@ -0,0 +1,162 @@ +""" +Helpers for logging. + +This module needs much love to become useful. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2008 Gael Varoquaux +# License: BSD Style, 3 clauses. + +from __future__ import print_function + +import time +import sys +import os +import shutil +import logging +import pprint + +from .disk import mkdirp + + +def _squeeze_time(t): + """Remove .1s to the time under Windows: this is the time it take to + stat files. This is needed to make results similar to timings under + Unix, for tests + """ + if sys.platform.startswith('win'): + return max(0, t - .1) + else: + return t + + +def format_time(t): + t = _squeeze_time(t) + return "%.1fs, %.1fmin" % (t, t / 60.) + + +def short_format_time(t): + t = _squeeze_time(t) + if t > 60: + return "%4.1fmin" % (t / 60.) + else: + return " %5.1fs" % (t) + + +def pformat(obj, indent=0, depth=3): + if 'numpy' in sys.modules: + import numpy as np + print_options = np.get_printoptions() + np.set_printoptions(precision=6, threshold=64, edgeitems=1) + else: + print_options = None + out = pprint.pformat(obj, depth=depth, indent=indent) + if print_options: + np.set_printoptions(**print_options) + return out + + +############################################################################### +# class `Logger` +############################################################################### +class Logger(object): + """ Base class for logging messages. + """ + + def __init__(self, depth=3, name=None): + """ + Parameters + ---------- + depth: int, optional + The depth of objects printed. + name: str, optional + The namespace to log to. If None, defaults to joblib. + """ + self.depth = depth + self._name = name if name else 'joblib' + + def warn(self, msg): + logging.getLogger(self._name).warning("[%s]: %s" % (self, msg)) + + def info(self, msg): + logging.info("[%s]: %s" % (self, msg)) + + def debug(self, msg): + # XXX: This conflicts with the debug flag used in children class + logging.getLogger(self._name).debug("[%s]: %s" % (self, msg)) + + def format(self, obj, indent=0): + """Return the formatted representation of the object.""" + return pformat(obj, indent=indent, depth=self.depth) + + +############################################################################### +# class `PrintTime` +############################################################################### +class PrintTime(object): + """ Print and log messages while keeping track of time. + """ + + def __init__(self, logfile=None, logdir=None): + if logfile is not None and logdir is not None: + raise ValueError('Cannot specify both logfile and logdir') + # XXX: Need argument docstring + self.last_time = time.time() + self.start_time = self.last_time + if logdir is not None: + logfile = os.path.join(logdir, 'joblib.log') + self.logfile = logfile + if logfile is not None: + mkdirp(os.path.dirname(logfile)) + if os.path.exists(logfile): + # Rotate the logs + for i in range(1, 9): + try: + shutil.move(logfile + '.%i' % i, + logfile + '.%i' % (i + 1)) + except: # noqa: E722 + "No reason failing here" + # Use a copy rather than a move, so that a process + # monitoring this file does not get lost. + try: + shutil.copy(logfile, logfile + '.1') + except: # noqa: E722 + "No reason failing here" + try: + with open(logfile, 'w') as logfile: + logfile.write('\nLogging joblib python script\n') + logfile.write('\n---%s---\n' % time.ctime(self.last_time)) + except: # noqa: E722 + """ Multiprocessing writing to files can create race + conditions. Rather fail silently than crash the + computation. + """ + # XXX: We actually need a debug flag to disable this + # silent failure. + + def __call__(self, msg='', total=False): + """ Print the time elapsed between the last call and the current + call, with an optional message. + """ + if not total: + time_lapse = time.time() - self.last_time + full_msg = "%s: %s" % (msg, format_time(time_lapse)) + else: + # FIXME: Too much logic duplicated + time_lapse = time.time() - self.start_time + full_msg = "%s: %.2fs, %.1f min" % (msg, time_lapse, + time_lapse / 60) + print(full_msg, file=sys.stderr) + if self.logfile is not None: + try: + with open(self.logfile, 'a') as f: + print(full_msg, file=f) + except: # noqa: E722 + """ Multiprocessing writing to files can create race + conditions. Rather fail silently than crash the + calculation. + """ + # XXX: We actually need a debug flag to disable this + # silent failure. + self.last_time = time.time() diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/memory.py b/.pythonlibs/lib/python3.10/site-packages/joblib/memory.py new file mode 100644 index 0000000..6f87f80 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/memory.py @@ -0,0 +1,1160 @@ +""" +A context object for caching a function's return value each time it +is called with the same input arguments. + +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + + +import asyncio +import datetime +import functools +import inspect +import logging +import os +import pathlib +import pydoc +import re +import textwrap +import time +import tokenize +import traceback +import warnings +import weakref + +from . import hashing +from ._store_backends import CacheWarning # noqa +from ._store_backends import FileSystemStoreBackend, StoreBackendBase +from .func_inspect import (filter_args, format_call, format_signature, + get_func_code, get_func_name) +from .logger import Logger, format_time, pformat + +FIRST_LINE_TEXT = "# first line:" + +# TODO: The following object should have a data store object as a sub +# object, and the interface to persist and query should be separated in +# the data store. +# +# This would enable creating 'Memory' objects with a different logic for +# pickling that would simply span a MemorizedFunc with the same +# store (or do we want to copy it to avoid cross-talks?), for instance to +# implement HDF5 pickling. + +# TODO: Same remark for the logger, and probably use the Python logging +# mechanism. + + +def extract_first_line(func_code): + """ Extract the first line information from the function code + text if available. + """ + if func_code.startswith(FIRST_LINE_TEXT): + func_code = func_code.split('\n') + first_line = int(func_code[0][len(FIRST_LINE_TEXT):]) + func_code = '\n'.join(func_code[1:]) + else: + first_line = -1 + return func_code, first_line + + +class JobLibCollisionWarning(UserWarning): + """ Warn that there might be a collision between names of functions. + """ + + +_STORE_BACKENDS = {'local': FileSystemStoreBackend} + + +def register_store_backend(backend_name, backend): + """Extend available store backends. + + The Memory, MemorizeResult and MemorizeFunc objects are designed to be + agnostic to the type of store used behind. By default, the local file + system is used but this function gives the possibility to extend joblib's + memory pattern with other types of storage such as cloud storage (S3, GCS, + OpenStack, HadoopFS, etc) or blob DBs. + + Parameters + ---------- + backend_name: str + The name identifying the store backend being registered. For example, + 'local' is used with FileSystemStoreBackend. + backend: StoreBackendBase subclass + The name of a class that implements the StoreBackendBase interface. + + """ + if not isinstance(backend_name, str): + raise ValueError("Store backend name should be a string, " + "'{0}' given.".format(backend_name)) + if backend is None or not issubclass(backend, StoreBackendBase): + raise ValueError("Store backend should inherit " + "StoreBackendBase, " + "'{0}' given.".format(backend)) + + _STORE_BACKENDS[backend_name] = backend + + +def _store_backend_factory(backend, location, verbose=0, backend_options=None): + """Return the correct store object for the given location.""" + if backend_options is None: + backend_options = {} + + if isinstance(location, pathlib.Path): + location = str(location) + + if isinstance(location, StoreBackendBase): + return location + elif isinstance(location, str): + obj = None + location = os.path.expanduser(location) + # The location is not a local file system, we look in the + # registered backends if there's one matching the given backend + # name. + for backend_key, backend_obj in _STORE_BACKENDS.items(): + if backend == backend_key: + obj = backend_obj() + + # By default, we assume the FileSystemStoreBackend can be used if no + # matching backend could be found. + if obj is None: + raise TypeError('Unknown location {0} or backend {1}'.format( + location, backend)) + + # The store backend is configured with the extra named parameters, + # some of them are specific to the underlying store backend. + obj.configure(location, verbose=verbose, + backend_options=backend_options) + return obj + elif location is not None: + warnings.warn( + "Instantiating a backend using a {} as a location is not " + "supported by joblib. Returning None instead.".format( + location.__class__.__name__), UserWarning) + + return None + + +def _build_func_identifier(func): + """Build a roughly unique identifier for the cached function.""" + modules, funcname = get_func_name(func) + # We reuse historical fs-like way of building a function identifier + return os.path.join(*modules, funcname) + + +# An in-memory store to avoid looking at the disk-based function +# source code to check if a function definition has changed +_FUNCTION_HASHES = weakref.WeakKeyDictionary() + + +############################################################################### +# class `MemorizedResult` +############################################################################### +class MemorizedResult(Logger): + """Object representing a cached value. + + Attributes + ---------- + location: str + The location of joblib cache. Depends on the store backend used. + + func: function or str + function whose output is cached. The string case is intended only for + instantiation based on the output of repr() on another instance. + (namely eval(repr(memorized_instance)) works). + + argument_hash: str + hash of the function arguments. + + backend: str + Type of store backend for reading/writing cache files. + Default is 'local'. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'} + The memmapping mode used when loading from cache numpy arrays. See + numpy.load for the meaning of the different values. + + verbose: int + verbosity level (0 means no message). + + timestamp, metadata: string + for internal use only. + """ + def __init__(self, location, call_id, backend='local', mmap_mode=None, + verbose=0, timestamp=None, metadata=None): + Logger.__init__(self) + self._call_id = call_id + self.store_backend = _store_backend_factory(backend, location, + verbose=verbose) + self.mmap_mode = mmap_mode + + if metadata is not None: + self.metadata = metadata + else: + self.metadata = self.store_backend.get_metadata(self._call_id) + + self.duration = self.metadata.get('duration', None) + self.verbose = verbose + self.timestamp = timestamp + + @property + def func(self): + return self.func_id + + @property + def func_id(self): + return self._call_id[0] + + @property + def args_id(self): + return self._call_id[1] + + @property + def argument_hash(self): + warnings.warn( + "The 'argument_hash' attribute has been deprecated in version " + "0.12 and will be removed in version 0.14.\n" + "Use `args_id` attribute instead.", + DeprecationWarning, stacklevel=2) + return self.args_id + + def get(self): + """Read value from cache and return it.""" + try: + return self.store_backend.load_item( + self._call_id, + timestamp=self.timestamp, + metadata=self.metadata, + verbose=self.verbose + ) + except ValueError as exc: + new_exc = KeyError( + "Error while trying to load a MemorizedResult's value. " + "It seems that this folder is corrupted : {}".format( + os.path.join(self.store_backend.location, *self._call_id))) + raise new_exc from exc + + def clear(self): + """Clear value from cache""" + self.store_backend.clear_item(self._call_id) + + def __repr__(self): + return '{}(location="{}", func="{}", args_id="{}")'.format( + self.__class__.__name__, self.store_backend.location, + *self._call_id + ) + + def __getstate__(self): + state = self.__dict__.copy() + state['timestamp'] = None + return state + + +class NotMemorizedResult(object): + """Class representing an arbitrary value. + + This class is a replacement for MemorizedResult when there is no cache. + """ + __slots__ = ('value', 'valid') + + def __init__(self, value): + self.value = value + self.valid = True + + def get(self): + if self.valid: + return self.value + else: + raise KeyError("No value stored.") + + def clear(self): + self.valid = False + self.value = None + + def __repr__(self): + if self.valid: + return ('{class_name}({value})' + .format(class_name=self.__class__.__name__, + value=pformat(self.value))) + else: + return self.__class__.__name__ + ' with no value' + + # __getstate__ and __setstate__ are required because of __slots__ + def __getstate__(self): + return {"valid": self.valid, "value": self.value} + + def __setstate__(self, state): + self.valid = state["valid"] + self.value = state["value"] + + +############################################################################### +# class `NotMemorizedFunc` +############################################################################### +class NotMemorizedFunc(object): + """No-op object decorating a function. + + This class replaces MemorizedFunc when there is no cache. It provides an + identical API but does not write anything on disk. + + Attributes + ---------- + func: callable + Original undecorated function. + """ + # Should be a light as possible (for speed) + def __init__(self, func): + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def call_and_shelve(self, *args, **kwargs): + return NotMemorizedResult(self.func(*args, **kwargs)) + + def __repr__(self): + return '{0}(func={1})'.format(self.__class__.__name__, self.func) + + def clear(self, warn=True): + # Argument "warn" is for compatibility with MemorizedFunc.clear + pass + + def call(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def check_call_in_cache(self, *args, **kwargs): + return False + + +############################################################################### +# class `AsyncNotMemorizedFunc` +############################################################################### +class AsyncNotMemorizedFunc(NotMemorizedFunc): + async def call_and_shelve(self, *args, **kwargs): + return NotMemorizedResult(await self.func(*args, **kwargs)) + + +############################################################################### +# class `MemorizedFunc` +############################################################################### +class MemorizedFunc(Logger): + """Callable object decorating a function for caching its return value + each time it is called. + + Methods are provided to inspect the cache or clean it. + + Attributes + ---------- + func: callable + The original, undecorated, function. + + location: string + The location of joblib cache. Depends on the store backend used. + + backend: str + Type of store backend for reading/writing cache files. + Default is 'local', in which case the location is the path to a + disk storage. + + ignore: list or None + List of variable names to ignore when choosing whether to + recompute. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'} + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the different + values. + + compress: boolean, or integer + Whether to zip the stored data on disk. If an integer is + given, it should be between 1 and 9, and sets the amount + of compression. Note that compressed arrays cannot be + read by memmapping. + + verbose: int, optional + The verbosity flag, controls messages that are issued as + the function is evaluated. + + cache_validation_callback: callable, optional + Callable to check if a result in cache is valid or is to be recomputed. + When the function is called with arguments for which a cache exists, + the callback is called with the cache entry's metadata as its sole + argument. If it returns True, the cached result is returned, else the + cache for these arguments is cleared and the result is recomputed. + """ + # ------------------------------------------------------------------------ + # Public interface + # ------------------------------------------------------------------------ + + def __init__(self, func, location, backend='local', ignore=None, + mmap_mode=None, compress=False, verbose=1, timestamp=None, + cache_validation_callback=None): + Logger.__init__(self) + self.mmap_mode = mmap_mode + self.compress = compress + self.func = func + self.cache_validation_callback = cache_validation_callback + self.func_id = _build_func_identifier(func) + self.ignore = ignore if ignore is not None else [] + self._verbose = verbose + + # retrieve store object from backend type and location. + self.store_backend = _store_backend_factory(backend, location, + verbose=verbose, + backend_options=dict( + compress=compress, + mmap_mode=mmap_mode), + ) + if self.store_backend is not None: + # Create func directory on demand. + self.store_backend.store_cached_func_code([self.func_id]) + + self.timestamp = timestamp if timestamp is not None else time.time() + try: + functools.update_wrapper(self, func) + except Exception: + pass # Objects like ufunc don't like that + if inspect.isfunction(func): + doc = pydoc.TextDoc().document(func) + # Remove blank line + doc = doc.replace('\n', '\n\n', 1) + # Strip backspace-overprints for compatibility with autodoc + doc = re.sub('\x08.', '', doc) + else: + # Pydoc does a poor job on other objects + doc = func.__doc__ + self.__doc__ = 'Memoized version of %s' % doc + + self._func_code_info = None + self._func_code_id = None + + def _is_in_cache_and_valid(self, call_id): + """Check if the function call is cached and valid for given arguments. + + - Compare the function code with the one from the cached function, + asserting if it has changed. + - Check if the function call is present in the cache. + - Call `cache_validation_callback` for user define cache validation. + + Returns True if the function call is in cache and can be used, and + returns False otherwise. + """ + # Check if the code of the function has changed + if not self._check_previous_func_code(stacklevel=4): + return False + + # Check if this specific call is in the cache + if not self.store_backend.contains_item(call_id): + return False + + # Call the user defined cache validation callback + metadata = self.store_backend.get_metadata(call_id) + if (self.cache_validation_callback is not None and + not self.cache_validation_callback(metadata)): + self.store_backend.clear_item(call_id) + return False + + return True + + def _cached_call(self, args, kwargs, shelving): + """Call wrapped function and cache result, or read cache if available. + + This function returns the wrapped function output or a reference to + the cached result. + + Arguments: + ---------- + + args, kwargs: list and dict + input arguments for wrapped function + + shelving: bool + True when called via the call_and_shelve function. + + + Returns + ------- + Output of the wrapped function if shelving is false, or a + MemorizedResult reference to the value if shelving is true. + """ + args_id = self._get_args_id(*args, **kwargs) + call_id = (self.func_id, args_id) + _, func_name = get_func_name(self.func) + func_info = self.store_backend.get_cached_func_info([self.func_id]) + location = func_info['location'] + + if self._verbose >= 20: + logging.basicConfig(level=logging.INFO) + _, signature = format_signature(self.func, *args, **kwargs) + self.info( + textwrap.dedent( + f""" + Querying {func_name} with signature + {signature}. + + (argument hash {args_id}) + + The store location is {location}. + """ + ) + ) + + # Compare the function code with the previous to see if the + # function code has changed and check if the results are present in + # the cache. + if self._is_in_cache_and_valid(call_id): + if shelving: + return self._get_memorized_result(call_id) + + try: + start_time = time.time() + output = self._load_item(call_id) + if self._verbose > 4: + self._print_duration(time.time() - start_time, + context='cache loaded ') + return output + except Exception: + # XXX: Should use an exception logger + _, signature = format_signature(self.func, *args, **kwargs) + self.warn('Exception while loading results for ' + '{}\n {}'.format(signature, traceback.format_exc())) + + if self._verbose > 10: + self.warn( + f"Computing func {func_name}, argument hash {args_id} " + f"in location {location}" + ) + + return self._call(call_id, args, kwargs, shelving) + + @property + def func_code_info(self): + # 3-tuple property containing: the function source code, source file, + # and first line of the code inside the source file + if hasattr(self.func, '__code__'): + if self._func_code_id is None: + self._func_code_id = id(self.func.__code__) + elif id(self.func.__code__) != self._func_code_id: + # Be robust to dynamic reassignments of self.func.__code__ + self._func_code_info = None + + if self._func_code_info is None: + # Cache the source code of self.func . Provided that get_func_code + # (which should be called once on self) gets called in the process + # in which self.func was defined, this caching mechanism prevents + # undesired cache clearing when the cached function is called in + # an environment where the introspection utilities get_func_code + # relies on do not work (typically, in joblib child processes). + # See #1035 for more info + # TODO (pierreglaser): do the same with get_func_name? + self._func_code_info = get_func_code(self.func) + return self._func_code_info + + def call_and_shelve(self, *args, **kwargs): + """Call wrapped function, cache result and return a reference. + + This method returns a reference to the cached result instead of the + result itself. The reference object is small and pickeable, allowing + to send or store it easily. Call .get() on reference object to get + result. + + Returns + ------- + cached_result: MemorizedResult or NotMemorizedResult + reference to the value returned by the wrapped function. The + class "NotMemorizedResult" is used when there is no cache + activated (e.g. location=None in Memory). + """ + return self._cached_call(args, kwargs, shelving=True) + + def __call__(self, *args, **kwargs): + return self._cached_call(args, kwargs, shelving=False) + + def __getstate__(self): + # Make sure self.func's source is introspected prior to being pickled - + # code introspection utilities typically do not work inside child + # processes + _ = self.func_code_info + + # We don't store the timestamp when pickling, to avoid the hash + # depending from it. + state = self.__dict__.copy() + state['timestamp'] = None + + # Invalidate the code id as id(obj) will be different in the child + state['_func_code_id'] = None + + return state + + def check_call_in_cache(self, *args, **kwargs): + """Check if function call is in the memory cache. + + Does not call the function or do any work besides func inspection + and arg hashing. + + Returns + ------- + is_call_in_cache: bool + Whether or not the result of the function has been cached + for the input arguments that have been passed. + """ + call_id = (self.func_id, self._get_args_id(*args, **kwargs)) + return self.store_backend.contains_item(call_id) + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_args_id(self, *args, **kwargs): + """Return the input parameter hash of a result.""" + return hashing.hash(filter_args(self.func, self.ignore, args, kwargs), + coerce_mmap=self.mmap_mode is not None) + + def _hash_func(self): + """Hash a function to key the online cache""" + func_code_h = hash(getattr(self.func, '__code__', None)) + return id(self.func), hash(self.func), func_code_h + + def _write_func_code(self, func_code, first_line): + """ Write the function code and the filename to a file. + """ + # We store the first line because the filename and the function + # name is not always enough to identify a function: people + # sometimes have several functions named the same way in a + # file. This is bad practice, but joblib should be robust to bad + # practice. + func_code = u'%s %i\n%s' % (FIRST_LINE_TEXT, first_line, func_code) + self.store_backend.store_cached_func_code([self.func_id], func_code) + + # Also store in the in-memory store of function hashes + is_named_callable = (hasattr(self.func, '__name__') and + self.func.__name__ != '') + if is_named_callable: + # Don't do this for lambda functions or strange callable + # objects, as it ends up being too fragile + func_hash = self._hash_func() + try: + _FUNCTION_HASHES[self.func] = func_hash + except TypeError: + # Some callable are not hashable + pass + + def _check_previous_func_code(self, stacklevel=2): + """ + stacklevel is the depth a which this function is called, to + issue useful warnings to the user. + """ + # First check if our function is in the in-memory store. + # Using the in-memory store not only makes things faster, but it + # also renders us robust to variations of the files when the + # in-memory version of the code does not vary + try: + if self.func in _FUNCTION_HASHES: + # We use as an identifier the id of the function and its + # hash. This is more likely to falsely change than have hash + # collisions, thus we are on the safe side. + func_hash = self._hash_func() + if func_hash == _FUNCTION_HASHES[self.func]: + return True + except TypeError: + # Some callables are not hashable + pass + + # Here, we go through some effort to be robust to dynamically + # changing code and collision. We cannot inspect.getsource + # because it is not reliable when using IPython's magic "%run". + func_code, source_file, first_line = self.func_code_info + try: + old_func_code, old_first_line = extract_first_line( + self.store_backend.get_cached_func_code([self.func_id])) + except (IOError, OSError): # some backend can also raise OSError + self._write_func_code(func_code, first_line) + return False + if old_func_code == func_code: + return True + + # We have differing code, is this because we are referring to + # different functions, or because the function we are referring to has + # changed? + + _, func_name = get_func_name(self.func, resolv_alias=False, + win_characters=False) + if old_first_line == first_line == -1 or func_name == '': + if not first_line == -1: + func_description = ("{0} ({1}:{2})" + .format(func_name, source_file, + first_line)) + else: + func_description = func_name + warnings.warn(JobLibCollisionWarning( + "Cannot detect name collisions for function '{0}'" + .format(func_description)), stacklevel=stacklevel) + + # Fetch the code at the old location and compare it. If it is the + # same than the code store, we have a collision: the code in the + # file has not changed, but the name we have is pointing to a new + # code block. + if not old_first_line == first_line and source_file is not None: + if os.path.exists(source_file): + _, func_name = get_func_name(self.func, resolv_alias=False) + num_lines = len(func_code.split('\n')) + with tokenize.open(source_file) as f: + on_disk_func_code = f.readlines()[ + old_first_line - 1:old_first_line - 1 + num_lines - 1] + on_disk_func_code = ''.join(on_disk_func_code) + possible_collision = (on_disk_func_code.rstrip() == + old_func_code.rstrip()) + else: + possible_collision = source_file.startswith(' 10: + _, func_name = get_func_name(self.func, resolv_alias=False) + self.warn("Function {0} (identified by {1}) has changed" + ".".format(func_name, self.func_id)) + self.clear(warn=True) + return False + + def clear(self, warn=True): + """Empty the function's cache.""" + func_id = self.func_id + if self._verbose > 0 and warn: + self.warn("Clearing function cache identified by %s" % func_id) + self.store_backend.clear_path([func_id, ]) + + func_code, _, first_line = self.func_code_info + self._write_func_code(func_code, first_line) + + def call(self, *args, **kwargs): + """Force the execution of the function with the given arguments. + + The output values will be persisted, i.e., the cache will be updated + with any new values. + + Parameters + ---------- + *args: arguments + The arguments. + **kwargs: keyword arguments + Keyword arguments. + + Returns + ------- + output : object + The output of the function call. + """ + call_id = (self.func_id, self._get_args_id(*args, **kwargs)) + return self._call(call_id, args, kwargs) + + def _call(self, call_id, args, kwargs, shelving=False): + self._before_call(args, kwargs) + start_time = time.time() + output = self.func(*args, **kwargs) + return self._after_call(call_id, args, kwargs, shelving, + output, start_time) + + def _before_call(self, args, kwargs): + if self._verbose > 0: + print(format_call(self.func, args, kwargs)) + + def _after_call(self, call_id, args, kwargs, shelving, output, start_time): + self.store_backend.dump_item(call_id, output, verbose=self._verbose) + duration = time.time() - start_time + if self._verbose > 0: + self._print_duration(duration) + metadata = self._persist_input(duration, call_id, args, kwargs) + if shelving: + return self._get_memorized_result(call_id, metadata) + + if self.mmap_mode is not None: + # Memmap the output at the first call to be consistent with + # later calls + output = self._load_item(call_id, metadata) + return output + + def _persist_input(self, duration, call_id, args, kwargs, + this_duration_limit=0.5): + """ Save a small summary of the call using json format in the + output directory. + + output_dir: string + directory where to write metadata. + + duration: float + time taken by hashing input arguments, calling the wrapped + function and persisting its output. + + args, kwargs: list and dict + input arguments for wrapped function + + this_duration_limit: float + Max execution time for this function before issuing a warning. + """ + start_time = time.time() + argument_dict = filter_args(self.func, self.ignore, + args, kwargs) + + input_repr = dict((k, repr(v)) for k, v in argument_dict.items()) + # This can fail due to race-conditions with multiple + # concurrent joblibs removing the file or the directory + metadata = { + "duration": duration, "input_args": input_repr, "time": start_time, + } + + self.store_backend.store_metadata(call_id, metadata) + + this_duration = time.time() - start_time + if this_duration > this_duration_limit: + # This persistence should be fast. It will not be if repr() takes + # time and its output is large, because json.dump will have to + # write a large file. This should not be an issue with numpy arrays + # for which repr() always output a short representation, but can + # be with complex dictionaries. Fixing the problem should be a + # matter of replacing repr() above by something smarter. + warnings.warn("Persisting input arguments took %.2fs to run." + "If this happens often in your code, it can cause " + "performance problems " + "(results will be correct in all cases). " + "The reason for this is probably some large input " + "arguments for a wrapped function." + % this_duration, stacklevel=5) + return metadata + + def _get_memorized_result(self, call_id, metadata=None): + return MemorizedResult(self.store_backend, call_id, + metadata=metadata, timestamp=self.timestamp, + verbose=self._verbose - 1) + + def _load_item(self, call_id, metadata=None): + return self.store_backend.load_item(call_id, metadata=metadata, + timestamp=self.timestamp, + verbose=self._verbose) + + def _print_duration(self, duration, context=''): + _, name = get_func_name(self.func) + msg = f"{name} {context}- {format_time(duration)}" + print(max(0, (80 - len(msg))) * '_' + msg) + + # ------------------------------------------------------------------------ + # Private `object` interface + # ------------------------------------------------------------------------ + + def __repr__(self): + return '{class_name}(func={func}, location={location})'.format( + class_name=self.__class__.__name__, + func=self.func, + location=self.store_backend.location,) + + +############################################################################### +# class `AsyncMemorizedFunc` +############################################################################### +class AsyncMemorizedFunc(MemorizedFunc): + async def __call__(self, *args, **kwargs): + out = super().__call__(*args, **kwargs) + return await out if asyncio.iscoroutine(out) else out + + async def call_and_shelve(self, *args, **kwargs): + out = super().call_and_shelve(*args, **kwargs) + return await out if asyncio.iscoroutine(out) else out + + async def call(self, *args, **kwargs): + out = super().call(*args, **kwargs) + return await out if asyncio.iscoroutine(out) else out + + async def _call(self, call_id, args, kwargs, shelving=False): + self._before_call(args, kwargs) + start_time = time.time() + output = await self.func(*args, **kwargs) + return self._after_call(call_id, args, kwargs, shelving, + output, start_time) + + +############################################################################### +# class `Memory` +############################################################################### +class Memory(Logger): + """ A context object for caching a function's return value each time it + is called with the same input arguments. + + All values are cached on the filesystem, in a deep directory + structure. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + location: str, pathlib.Path or None + The path of the base directory to use as a data store + or None. If None is given, no caching is done and + the Memory object is completely transparent. This option + replaces cachedir since version 0.12. + + backend: str, optional + Type of store backend for reading/writing cache files. + Default: 'local'. + The 'local' backend is using regular filesystem operations to + manipulate data (open, mv, etc) in the backend. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the + arguments. + + compress: boolean, or integer, optional + Whether to zip the stored data on disk. If an integer is + given, it should be between 1 and 9, and sets the amount + of compression. Note that compressed arrays cannot be + read by memmapping. + + verbose: int, optional + Verbosity flag, controls the debug messages that are issued + as functions are evaluated. + + bytes_limit: int | str, optional + Limit in bytes of the size of the cache. By default, the size of + the cache is unlimited. When reducing the size of the cache, + ``joblib`` keeps the most recently accessed items first. If a + str is passed, it is converted to a number of bytes using units + { K | M | G} for kilo, mega, giga. + + **Note:** You need to call :meth:`joblib.Memory.reduce_size` to + actually reduce the cache size to be less than ``bytes_limit``. + + **Note:** This argument has been deprecated. One should give the + value of ``bytes_limit`` directly in + :meth:`joblib.Memory.reduce_size`. + + backend_options: dict, optional + Contains a dictionary of named parameters used to configure + the store backend. + """ + # ------------------------------------------------------------------------ + # Public interface + # ------------------------------------------------------------------------ + + def __init__(self, location=None, backend='local', + mmap_mode=None, compress=False, verbose=1, bytes_limit=None, + backend_options=None): + Logger.__init__(self) + self._verbose = verbose + self.mmap_mode = mmap_mode + self.timestamp = time.time() + if bytes_limit is not None: + warnings.warn( + "bytes_limit argument has been deprecated. It will be removed " + "in version 1.5. Please pass its value directly to " + "Memory.reduce_size.", + category=DeprecationWarning + ) + self.bytes_limit = bytes_limit + self.backend = backend + self.compress = compress + if backend_options is None: + backend_options = {} + self.backend_options = backend_options + + if compress and mmap_mode is not None: + warnings.warn('Compressed results cannot be memmapped', + stacklevel=2) + + self.location = location + if isinstance(location, str): + location = os.path.join(location, 'joblib') + + self.store_backend = _store_backend_factory( + backend, location, verbose=self._verbose, + backend_options=dict(compress=compress, mmap_mode=mmap_mode, + **backend_options)) + + def cache(self, func=None, ignore=None, verbose=None, mmap_mode=False, + cache_validation_callback=None): + """ Decorates the given function func to only compute its return + value for input arguments not cached on disk. + + Parameters + ---------- + func: callable, optional + The function to be decorated + ignore: list of strings + A list of arguments name to ignore in the hashing + verbose: integer, optional + The verbosity mode of the function. By default that + of the memory object is used. + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the + arguments. By default that of the memory object is used. + cache_validation_callback: callable, optional + Callable to validate whether or not the cache is valid. When + the cached function is called with arguments for which a cache + exists, this callable is called with the metadata of the cached + result as its sole argument. If it returns True, then the + cached result is returned, else the cache for these arguments + is cleared and recomputed. + + Returns + ------- + decorated_func: MemorizedFunc object + The returned object is a MemorizedFunc object, that is + callable (behaves like a function), but offers extra + methods for cache lookup and management. See the + documentation for :class:`joblib.memory.MemorizedFunc`. + """ + if (cache_validation_callback is not None and + not callable(cache_validation_callback)): + raise ValueError( + "cache_validation_callback needs to be callable. " + f"Got {cache_validation_callback}." + ) + if func is None: + # Partial application, to be able to specify extra keyword + # arguments in decorators + return functools.partial( + self.cache, ignore=ignore, + mmap_mode=mmap_mode, + verbose=verbose, + cache_validation_callback=cache_validation_callback + ) + if self.store_backend is None: + cls = (AsyncNotMemorizedFunc + if asyncio.iscoroutinefunction(func) + else NotMemorizedFunc) + return cls(func) + if verbose is None: + verbose = self._verbose + if mmap_mode is False: + mmap_mode = self.mmap_mode + if isinstance(func, MemorizedFunc): + func = func.func + cls = (AsyncMemorizedFunc + if asyncio.iscoroutinefunction(func) + else MemorizedFunc) + return cls( + func, location=self.store_backend, backend=self.backend, + ignore=ignore, mmap_mode=mmap_mode, compress=self.compress, + verbose=verbose, timestamp=self.timestamp, + cache_validation_callback=cache_validation_callback + ) + + def clear(self, warn=True): + """ Erase the complete cache directory. + """ + if warn: + self.warn('Flushing completely the cache') + if self.store_backend is not None: + self.store_backend.clear() + + # As the cache is completely clear, make sure the _FUNCTION_HASHES + # cache is also reset. Else, for a function that is present in this + # table, results cached after this clear will be have cache miss + # as the function code is not re-written. + _FUNCTION_HASHES.clear() + + def reduce_size(self, bytes_limit=None, items_limit=None, age_limit=None): + """Remove cache elements to make the cache fit its limits. + + The limitation can impose that the cache size fits in ``bytes_limit``, + that the number of cache items is no more than ``items_limit``, and + that all files in cache are not older than ``age_limit``. + + Parameters + ---------- + bytes_limit: int | str, optional + Limit in bytes of the size of the cache. By default, the size of + the cache is unlimited. When reducing the size of the cache, + ``joblib`` keeps the most recently accessed items first. If a + str is passed, it is converted to a number of bytes using units + { K | M | G} for kilo, mega, giga. + + items_limit: int, optional + Number of items to limit the cache to. By default, the number of + items in the cache is unlimited. When reducing the size of the + cache, ``joblib`` keeps the most recently accessed items first. + + age_limit: datetime.timedelta, optional + Maximum age of items to limit the cache to. When reducing the size + of the cache, any items last accessed more than the given length of + time ago are deleted. + """ + if bytes_limit is None: + bytes_limit = self.bytes_limit + + if self.store_backend is None: + # No cached results, this function does nothing. + return + + if bytes_limit is None and items_limit is None and age_limit is None: + # No limitation to impose, returning + return + + # Defers the actual limits enforcing to the store backend. + self.store_backend.enforce_store_limits( + bytes_limit, items_limit, age_limit + ) + + def eval(self, func, *args, **kwargs): + """ Eval function func with arguments `*args` and `**kwargs`, + in the context of the memory. + + This method works similarly to the builtin `apply`, except + that the function is called only if the cache is not + up to date. + + """ + if self.store_backend is None: + return func(*args, **kwargs) + return self.cache(func)(*args, **kwargs) + + # ------------------------------------------------------------------------ + # Private `object` interface + # ------------------------------------------------------------------------ + + def __repr__(self): + return '{class_name}(location={location})'.format( + class_name=self.__class__.__name__, + location=(None if self.store_backend is None + else self.store_backend.location)) + + def __getstate__(self): + """ We don't store the timestamp when pickling, to avoid the hash + depending from it. + """ + state = self.__dict__.copy() + state['timestamp'] = None + return state + + +############################################################################### +# cache_validation_callback helpers +############################################################################### + +def expires_after(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, + hours=0, weeks=0): + """Helper cache_validation_callback to force recompute after a duration. + + Parameters + ---------- + days, seconds, microseconds, milliseconds, minutes, hours, weeks: numbers + argument passed to a timedelta. + """ + delta = datetime.timedelta( + days=days, seconds=seconds, microseconds=microseconds, + milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks + ) + + def cache_validation_callback(metadata): + computation_age = time.time() - metadata['time'] + return computation_age < delta.total_seconds() + + return cache_validation_callback diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle.py b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle.py new file mode 100644 index 0000000..bf83bb0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle.py @@ -0,0 +1,659 @@ +"""Utilities for fast persistence of big data, with optional compression.""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import pickle +import os +import warnings +import io +from pathlib import Path + +from .compressor import lz4, LZ4_NOT_INSTALLED_ERROR +from .compressor import _COMPRESSORS, register_compressor, BinaryZlibFile +from .compressor import (ZlibCompressorWrapper, GzipCompressorWrapper, + BZ2CompressorWrapper, LZMACompressorWrapper, + XZCompressorWrapper, LZ4CompressorWrapper) +from .numpy_pickle_utils import Unpickler, Pickler +from .numpy_pickle_utils import _read_fileobject, _write_fileobject +from .numpy_pickle_utils import _read_bytes, BUFFER_SIZE +from .numpy_pickle_utils import _ensure_native_byte_order +from .numpy_pickle_compat import load_compatibility +from .numpy_pickle_compat import NDArrayWrapper +# For compatibility with old versions of joblib, we need ZNDArrayWrapper +# to be visible in the current namespace. +# Explicitly skipping next line from flake8 as it triggers an F401 warning +# which we don't care. +from .numpy_pickle_compat import ZNDArrayWrapper # noqa +from .backports import make_memmap + +# Register supported compressors +register_compressor('zlib', ZlibCompressorWrapper()) +register_compressor('gzip', GzipCompressorWrapper()) +register_compressor('bz2', BZ2CompressorWrapper()) +register_compressor('lzma', LZMACompressorWrapper()) +register_compressor('xz', XZCompressorWrapper()) +register_compressor('lz4', LZ4CompressorWrapper()) + + +############################################################################### +# Utility objects for persistence. + +# For convenience, 16 bytes are used to be sure to cover all the possible +# dtypes' alignments. For reference, see: +# https://numpy.org/devdocs/dev/alignment.html +NUMPY_ARRAY_ALIGNMENT_BYTES = 16 + + +class NumpyArrayWrapper(object): + """An object to be persisted instead of numpy arrays. + + This object is used to hack into the pickle machinery and read numpy + array data from our custom persistence format. + More precisely, this object is used for: + * carrying the information of the persisted array: subclass, shape, order, + dtype. Those ndarray metadata are used to correctly reconstruct the array + with low level numpy functions. + * determining if memmap is allowed on the array. + * reading the array bytes from a file. + * reading the array using memorymap from a file. + * writing the array bytes to a file. + + Attributes + ---------- + subclass: numpy.ndarray subclass + Determine the subclass of the wrapped array. + shape: numpy.ndarray shape + Determine the shape of the wrapped array. + order: {'C', 'F'} + Determine the order of wrapped array data. 'C' is for C order, 'F' is + for fortran order. + dtype: numpy.ndarray dtype + Determine the data type of the wrapped array. + allow_mmap: bool + Determine if memory mapping is allowed on the wrapped array. + Default: False. + """ + + def __init__(self, subclass, shape, order, dtype, allow_mmap=False, + numpy_array_alignment_bytes=NUMPY_ARRAY_ALIGNMENT_BYTES): + """Constructor. Store the useful information for later.""" + self.subclass = subclass + self.shape = shape + self.order = order + self.dtype = dtype + self.allow_mmap = allow_mmap + # We make numpy_array_alignment_bytes an instance attribute to allow us + # to change our mind about the default alignment and still load the old + # pickles (with the previous alignment) correctly + self.numpy_array_alignment_bytes = numpy_array_alignment_bytes + + def safe_get_numpy_array_alignment_bytes(self): + # NumpyArrayWrapper instances loaded from joblib <= 1.1 pickles don't + # have an numpy_array_alignment_bytes attribute + return getattr(self, 'numpy_array_alignment_bytes', None) + + def write_array(self, array, pickler): + """Write array bytes to pickler file handle. + + This function is an adaptation of the numpy write_array function + available in version 1.10.1 in numpy/lib/format.py. + """ + # Set buffer size to 16 MiB to hide the Python loop overhead. + buffersize = max(16 * 1024 ** 2 // array.itemsize, 1) + if array.dtype.hasobject: + # We contain Python objects so we cannot write out the data + # directly. Instead, we will pickle it out with version 2 of the + # pickle protocol. + pickle.dump(array, pickler.file_handle, protocol=2) + else: + numpy_array_alignment_bytes = \ + self.safe_get_numpy_array_alignment_bytes() + if numpy_array_alignment_bytes is not None: + current_pos = pickler.file_handle.tell() + pos_after_padding_byte = current_pos + 1 + padding_length = numpy_array_alignment_bytes - ( + pos_after_padding_byte % numpy_array_alignment_bytes) + # A single byte is written that contains the padding length in + # bytes + padding_length_byte = int.to_bytes( + padding_length, length=1, byteorder='little') + pickler.file_handle.write(padding_length_byte) + + if padding_length != 0: + padding = b'\xff' * padding_length + pickler.file_handle.write(padding) + + for chunk in pickler.np.nditer(array, + flags=['external_loop', + 'buffered', + 'zerosize_ok'], + buffersize=buffersize, + order=self.order): + pickler.file_handle.write(chunk.tobytes('C')) + + def read_array(self, unpickler): + """Read array from unpickler file handle. + + This function is an adaptation of the numpy read_array function + available in version 1.10.1 in numpy/lib/format.py. + """ + if len(self.shape) == 0: + count = 1 + else: + # joblib issue #859: we cast the elements of self.shape to int64 to + # prevent a potential overflow when computing their product. + shape_int64 = [unpickler.np.int64(x) for x in self.shape] + count = unpickler.np.multiply.reduce(shape_int64) + # Now read the actual data. + if self.dtype.hasobject: + # The array contained Python objects. We need to unpickle the data. + array = pickle.load(unpickler.file_handle) + else: + numpy_array_alignment_bytes = \ + self.safe_get_numpy_array_alignment_bytes() + if numpy_array_alignment_bytes is not None: + padding_byte = unpickler.file_handle.read(1) + padding_length = int.from_bytes( + padding_byte, byteorder='little') + if padding_length != 0: + unpickler.file_handle.read(padding_length) + + # This is not a real file. We have to read it the + # memory-intensive way. + # crc32 module fails on reads greater than 2 ** 32 bytes, + # breaking large reads from gzip streams. Chunk reads to + # BUFFER_SIZE bytes to avoid issue and reduce memory overhead + # of the read. In non-chunked case count < max_read_count, so + # only one read is performed. + max_read_count = BUFFER_SIZE // min(BUFFER_SIZE, + self.dtype.itemsize) + + array = unpickler.np.empty(count, dtype=self.dtype) + for i in range(0, count, max_read_count): + read_count = min(max_read_count, count - i) + read_size = int(read_count * self.dtype.itemsize) + data = _read_bytes(unpickler.file_handle, + read_size, "array data") + array[i:i + read_count] = \ + unpickler.np.frombuffer(data, dtype=self.dtype, + count=read_count) + del data + + if self.order == 'F': + array.shape = self.shape[::-1] + array = array.transpose() + else: + array.shape = self.shape + + # Detect byte order mismatch and swap as needed. + return _ensure_native_byte_order(array) + + def read_mmap(self, unpickler): + """Read an array using numpy memmap.""" + current_pos = unpickler.file_handle.tell() + offset = current_pos + numpy_array_alignment_bytes = \ + self.safe_get_numpy_array_alignment_bytes() + + if numpy_array_alignment_bytes is not None: + padding_byte = unpickler.file_handle.read(1) + padding_length = int.from_bytes(padding_byte, byteorder='little') + # + 1 is for the padding byte + offset += padding_length + 1 + + if unpickler.mmap_mode == 'w+': + unpickler.mmap_mode = 'r+' + + marray = make_memmap(unpickler.filename, + dtype=self.dtype, + shape=self.shape, + order=self.order, + mode=unpickler.mmap_mode, + offset=offset) + # update the offset so that it corresponds to the end of the read array + unpickler.file_handle.seek(offset + marray.nbytes) + + if (numpy_array_alignment_bytes is None and + current_pos % NUMPY_ARRAY_ALIGNMENT_BYTES != 0): + message = ( + f'The memmapped array {marray} loaded from the file ' + f'{unpickler.file_handle.name} is not byte aligned. ' + 'This may cause segmentation faults if this memmapped array ' + 'is used in some libraries like BLAS or PyTorch. ' + 'To get rid of this warning, regenerate your pickle file ' + 'with joblib >= 1.2.0. ' + 'See https://github.com/joblib/joblib/issues/563 ' + 'for more details' + ) + warnings.warn(message) + + return _ensure_native_byte_order(marray) + + def read(self, unpickler): + """Read the array corresponding to this wrapper. + + Use the unpickler to get all information to correctly read the array. + + Parameters + ---------- + unpickler: NumpyUnpickler + + Returns + ------- + array: numpy.ndarray + + """ + # When requested, only use memmap mode if allowed. + if unpickler.mmap_mode is not None and self.allow_mmap: + array = self.read_mmap(unpickler) + else: + array = self.read_array(unpickler) + + # Manage array subclass case + if (hasattr(array, '__array_prepare__') and + self.subclass not in (unpickler.np.ndarray, + unpickler.np.memmap)): + # We need to reconstruct another subclass + new_array = unpickler.np.core.multiarray._reconstruct( + self.subclass, (0,), 'b') + return new_array.__array_prepare__(array) + else: + return array + +############################################################################### +# Pickler classes + + +class NumpyPickler(Pickler): + """A pickler to persist big data efficiently. + + The main features of this object are: + * persistence of numpy arrays in a single file. + * optional compression with a special care on avoiding memory copies. + + Attributes + ---------- + fp: file + File object handle used for serializing the input object. + protocol: int, optional + Pickle protocol used. Default is pickle.DEFAULT_PROTOCOL. + """ + + dispatch = Pickler.dispatch.copy() + + def __init__(self, fp, protocol=None): + self.file_handle = fp + self.buffered = isinstance(self.file_handle, BinaryZlibFile) + + # By default we want a pickle protocol that only changes with + # the major python version and not the minor one + if protocol is None: + protocol = pickle.DEFAULT_PROTOCOL + + Pickler.__init__(self, self.file_handle, protocol=protocol) + # delayed import of numpy, to avoid tight coupling + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def _create_array_wrapper(self, array): + """Create and returns a numpy array wrapper from a numpy array.""" + order = 'F' if (array.flags.f_contiguous and + not array.flags.c_contiguous) else 'C' + allow_mmap = not self.buffered and not array.dtype.hasobject + + kwargs = {} + try: + self.file_handle.tell() + except io.UnsupportedOperation: + kwargs = {'numpy_array_alignment_bytes': None} + + wrapper = NumpyArrayWrapper(type(array), + array.shape, order, array.dtype, + allow_mmap=allow_mmap, + **kwargs) + + return wrapper + + def save(self, obj): + """Subclass the Pickler `save` method. + + This is a total abuse of the Pickler class in order to use the numpy + persistence function `save` instead of the default pickle + implementation. The numpy array is replaced by a custom wrapper in the + pickle persistence stack and the serialized array is written right + after in the file. Warning: the file produced does not follow the + pickle format. As such it can not be read with `pickle.load`. + """ + if self.np is not None and type(obj) in (self.np.ndarray, + self.np.matrix, + self.np.memmap): + if type(obj) is self.np.memmap: + # Pickling doesn't work with memmapped arrays + obj = self.np.asanyarray(obj) + + # The array wrapper is pickled instead of the real array. + wrapper = self._create_array_wrapper(obj) + Pickler.save(self, wrapper) + + # A framer was introduced with pickle protocol 4 and we want to + # ensure the wrapper object is written before the numpy array + # buffer in the pickle file. + # See https://www.python.org/dev/peps/pep-3154/#framing to get + # more information on the framer behavior. + if self.proto >= 4: + self.framer.commit_frame(force=True) + + # And then array bytes are written right after the wrapper. + wrapper.write_array(obj, self) + return + + return Pickler.save(self, obj) + + +class NumpyUnpickler(Unpickler): + """A subclass of the Unpickler to unpickle our numpy pickles. + + Attributes + ---------- + mmap_mode: str + The memorymap mode to use for reading numpy arrays. + file_handle: file_like + File object to unpickle from. + filename: str + Name of the file to unpickle from. It should correspond to file_handle. + This parameter is required when using mmap_mode. + np: module + Reference to numpy module if numpy is installed else None. + + """ + + dispatch = Unpickler.dispatch.copy() + + def __init__(self, filename, file_handle, mmap_mode=None): + # The next line is for backward compatibility with pickle generated + # with joblib versions less than 0.10. + self._dirname = os.path.dirname(filename) + + self.mmap_mode = mmap_mode + self.file_handle = file_handle + # filename is required for numpy mmap mode. + self.filename = filename + self.compat_mode = False + Unpickler.__init__(self, self.file_handle) + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def load_build(self): + """Called to set the state of a newly created object. + + We capture it to replace our place-holder objects, NDArrayWrapper or + NumpyArrayWrapper, by the array we are interested in. We + replace them directly in the stack of pickler. + NDArrayWrapper is used for backward compatibility with joblib <= 0.9. + """ + Unpickler.load_build(self) + + # For backward compatibility, we support NDArrayWrapper objects. + if isinstance(self.stack[-1], (NDArrayWrapper, NumpyArrayWrapper)): + if self.np is None: + raise ImportError("Trying to unpickle an ndarray, " + "but numpy didn't import correctly") + array_wrapper = self.stack.pop() + # If any NDArrayWrapper is found, we switch to compatibility mode, + # this will be used to raise a DeprecationWarning to the user at + # the end of the unpickling. + if isinstance(array_wrapper, NDArrayWrapper): + self.compat_mode = True + self.stack.append(array_wrapper.read(self)) + + # Be careful to register our new method. + dispatch[pickle.BUILD[0]] = load_build + + +############################################################################### +# Utility functions + +def dump(value, filename, compress=0, protocol=None, cache_size=None): + """Persist an arbitrary Python object into one file. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + value: any Python object + The object to store to disk. + filename: str, pathlib.Path, or file object. + The file object or path of the file in which it is to be stored. + The compression method corresponding to one of the supported filename + extensions ('.z', '.gz', '.bz2', '.xz' or '.lzma') will be used + automatically. + compress: int from 0 to 9 or bool or 2-tuple, optional + Optional compression level for the data. 0 or False is no compression. + Higher value means more compression, but also slower read and + write times. Using a value of 3 is often a good compromise. + See the notes for more details. + If compress is True, the compression level used is 3. + If compress is a 2-tuple, the first element must correspond to a string + between supported compressors (e.g 'zlib', 'gzip', 'bz2', 'lzma' + 'xz'), the second element must be an integer from 0 to 9, corresponding + to the compression level. + protocol: int, optional + Pickle protocol, see pickle.dump documentation for more details. + cache_size: positive int, optional + This option is deprecated in 0.10 and has no effect. + + Returns + ------- + filenames: list of strings + The list of file names in which the data is stored. If + compress is false, each array is stored in a different file. + + See Also + -------- + joblib.load : corresponding loader + + Notes + ----- + Memmapping on load cannot be used for compressed files. Thus + using compression can significantly slow down loading. In + addition, compressed files take up extra memory during + dump and load. + + """ + + if Path is not None and isinstance(filename, Path): + filename = str(filename) + + is_filename = isinstance(filename, str) + is_fileobj = hasattr(filename, "write") + + compress_method = 'zlib' # zlib is the default compression method. + if compress is True: + # By default, if compress is enabled, we want the default compress + # level of the compressor. + compress_level = None + elif isinstance(compress, tuple): + # a 2-tuple was set in compress + if len(compress) != 2: + raise ValueError( + 'Compress argument tuple should contain exactly 2 elements: ' + '(compress method, compress level), you passed {}' + .format(compress)) + compress_method, compress_level = compress + elif isinstance(compress, str): + compress_method = compress + compress_level = None # Use default compress level + compress = (compress_method, compress_level) + else: + compress_level = compress + + if compress_method == 'lz4' and lz4 is None: + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + + if (compress_level is not None and + compress_level is not False and + compress_level not in range(10)): + # Raising an error if a non valid compress level is given. + raise ValueError( + 'Non valid compress level given: "{}". Possible values are ' + '{}.'.format(compress_level, list(range(10)))) + + if compress_method not in _COMPRESSORS: + # Raising an error if an unsupported compression method is given. + raise ValueError( + 'Non valid compression method given: "{}". Possible values are ' + '{}.'.format(compress_method, _COMPRESSORS)) + + if not is_filename and not is_fileobj: + # People keep inverting arguments, and the resulting error is + # incomprehensible + raise ValueError( + 'Second argument should be a filename or a file-like object, ' + '%s (type %s) was given.' + % (filename, type(filename)) + ) + + if is_filename and not isinstance(compress, tuple): + # In case no explicit compression was requested using both compression + # method and level in a tuple and the filename has an explicit + # extension, we select the corresponding compressor. + + # unset the variable to be sure no compression level is set afterwards. + compress_method = None + for name, compressor in _COMPRESSORS.items(): + if filename.endswith(compressor.extension): + compress_method = name + + if compress_method in _COMPRESSORS and compress_level == 0: + # we choose the default compress_level in case it was not given + # as an argument (using compress). + compress_level = None + + if cache_size is not None: + # Cache size is deprecated starting from version 0.10 + warnings.warn("Please do not set 'cache_size' in joblib.dump, " + "this parameter has no effect and will be removed. " + "You used 'cache_size={}'".format(cache_size), + DeprecationWarning, stacklevel=2) + + if compress_level != 0: + with _write_fileobject(filename, compress=(compress_method, + compress_level)) as f: + NumpyPickler(f, protocol=protocol).dump(value) + elif is_filename: + with open(filename, 'wb') as f: + NumpyPickler(f, protocol=protocol).dump(value) + else: + NumpyPickler(filename, protocol=protocol).dump(value) + + # If the target container is a file object, nothing is returned. + if is_fileobj: + return + + # For compatibility, the list of created filenames (e.g with one element + # after 0.10.0) is returned by default. + return [filename] + + +def _unpickle(fobj, filename="", mmap_mode=None): + """Internal unpickling function.""" + # We are careful to open the file handle early and keep it open to + # avoid race-conditions on renames. + # That said, if data is stored in companion files, which can be + # the case with the old persistence format, moving the directory + # will create a race when joblib tries to access the companion + # files. + unpickler = NumpyUnpickler(filename, fobj, mmap_mode=mmap_mode) + obj = None + try: + obj = unpickler.load() + if unpickler.compat_mode: + warnings.warn("The file '%s' has been generated with a " + "joblib version less than 0.10. " + "Please regenerate this pickle file." + % filename, + DeprecationWarning, stacklevel=3) + except UnicodeDecodeError as exc: + # More user-friendly error message + new_exc = ValueError( + 'You may be trying to read with ' + 'python 3 a joblib pickle generated with python 2. ' + 'This feature is not supported by joblib.') + new_exc.__cause__ = exc + raise new_exc + return obj + + +def load_temporary_memmap(filename, mmap_mode, unlink_on_gc_collect): + from ._memmapping_reducer import JOBLIB_MMAPS, add_maybe_unlink_finalizer + obj = load(filename, mmap_mode) + JOBLIB_MMAPS.add(obj.filename) + if unlink_on_gc_collect: + add_maybe_unlink_finalizer(obj) + return obj + + +def load(filename, mmap_mode=None): + """Reconstruct a Python object from a file persisted with joblib.dump. + + Read more in the :ref:`User Guide `. + + WARNING: joblib.load relies on the pickle module and can therefore + execute arbitrary Python code. It should therefore never be used + to load files from untrusted sources. + + Parameters + ---------- + filename: str, pathlib.Path, or file object. + The file object or path of the file from which to load the object + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + If not None, the arrays are memory-mapped from the disk. This + mode has no effect for compressed files. Note that in this + case the reconstructed object might no longer match exactly + the originally pickled object. + + Returns + ------- + result: any Python object + The object stored in the file. + + See Also + -------- + joblib.dump : function to save an object + + Notes + ----- + + This function can load numpy array files saved separately during the + dump. If the mmap_mode argument is given, it is passed to np.load and + arrays are loaded as memmaps. As a consequence, the reconstructed + object might not match the original pickled object. Note that if the + file was saved with compression, the arrays cannot be memmapped. + """ + if Path is not None and isinstance(filename, Path): + filename = str(filename) + + if hasattr(filename, "read"): + fobj = filename + filename = getattr(fobj, 'name', '') + with _read_fileobject(fobj, filename, mmap_mode) as fobj: + obj = _unpickle(fobj) + else: + with open(filename, 'rb') as f: + with _read_fileobject(f, filename, mmap_mode) as fobj: + if isinstance(fobj, str): + # if the returned file object is a string, this means we + # try to load a pickle file generated with an version of + # Joblib so we load it with joblib compatibility function. + return load_compatibility(fobj) + + obj = _unpickle(fobj, filename, mmap_mode) + return obj diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_compat.py b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_compat.py new file mode 100644 index 0000000..3261284 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_compat.py @@ -0,0 +1,244 @@ +"""Numpy pickle compatibility functions.""" + +import pickle +import os +import zlib +import inspect + +from io import BytesIO + +from .numpy_pickle_utils import _ZFILE_PREFIX +from .numpy_pickle_utils import Unpickler +from .numpy_pickle_utils import _ensure_native_byte_order + + +def hex_str(an_int): + """Convert an int to an hexadecimal string.""" + return '{:#x}'.format(an_int) + + +def asbytes(s): + if isinstance(s, bytes): + return s + return s.encode('latin1') + + +_MAX_LEN = len(hex_str(2 ** 64)) +_CHUNK_SIZE = 64 * 1024 + + +def read_zfile(file_handle): + """Read the z-file and return the content as a string. + + Z-files are raw data compressed with zlib used internally by joblib + for persistence. Backward compatibility is not guaranteed. Do not + use for external purposes. + """ + file_handle.seek(0) + header_length = len(_ZFILE_PREFIX) + _MAX_LEN + length = file_handle.read(header_length) + length = length[len(_ZFILE_PREFIX):] + length = int(length, 16) + + # With python2 and joblib version <= 0.8.4 compressed pickle header is one + # character wider so we need to ignore an additional space if present. + # Note: the first byte of the zlib data is guaranteed not to be a + # space according to + # https://tools.ietf.org/html/rfc6713#section-2.1 + next_byte = file_handle.read(1) + if next_byte != b' ': + # The zlib compressed data has started and we need to go back + # one byte + file_handle.seek(header_length) + + # We use the known length of the data to tell Zlib the size of the + # buffer to allocate. + data = zlib.decompress(file_handle.read(), 15, length) + assert len(data) == length, ( + "Incorrect data length while decompressing %s." + "The file could be corrupted." % file_handle) + return data + + +def write_zfile(file_handle, data, compress=1): + """Write the data in the given file as a Z-file. + + Z-files are raw data compressed with zlib used internally by joblib + for persistence. Backward compatibility is not guaranteed. Do not + use for external purposes. + """ + file_handle.write(_ZFILE_PREFIX) + length = hex_str(len(data)) + # Store the length of the data + file_handle.write(asbytes(length.ljust(_MAX_LEN))) + file_handle.write(zlib.compress(asbytes(data), compress)) + +############################################################################### +# Utility objects for persistence. + + +class NDArrayWrapper(object): + """An object to be persisted instead of numpy arrays. + + The only thing this object does, is to carry the filename in which + the array has been persisted, and the array subclass. + """ + + def __init__(self, filename, subclass, allow_mmap=True): + """Constructor. Store the useful information for later.""" + self.filename = filename + self.subclass = subclass + self.allow_mmap = allow_mmap + + def read(self, unpickler): + """Reconstruct the array.""" + filename = os.path.join(unpickler._dirname, self.filename) + # Load the array from the disk + # use getattr instead of self.allow_mmap to ensure backward compat + # with NDArrayWrapper instances pickled with joblib < 0.9.0 + allow_mmap = getattr(self, 'allow_mmap', True) + kwargs = {} + if allow_mmap: + kwargs['mmap_mode'] = unpickler.mmap_mode + if "allow_pickle" in inspect.signature(unpickler.np.load).parameters: + # Required in numpy 1.16.3 and later to aknowledge the security + # risk. + kwargs["allow_pickle"] = True + array = unpickler.np.load(filename, **kwargs) + + # Detect byte order mismatch and swap as needed. + array = _ensure_native_byte_order(array) + + # Reconstruct subclasses. This does not work with old + # versions of numpy + if (hasattr(array, '__array_prepare__') and + self.subclass not in (unpickler.np.ndarray, + unpickler.np.memmap)): + # We need to reconstruct another subclass + new_array = unpickler.np.core.multiarray._reconstruct( + self.subclass, (0,), 'b') + return new_array.__array_prepare__(array) + else: + return array + + +class ZNDArrayWrapper(NDArrayWrapper): + """An object to be persisted instead of numpy arrays. + + This object store the Zfile filename in which + the data array has been persisted, and the meta information to + retrieve it. + The reason that we store the raw buffer data of the array and + the meta information, rather than array representation routine + (tobytes) is that it enables us to use completely the strided + model to avoid memory copies (a and a.T store as fast). In + addition saving the heavy information separately can avoid + creating large temporary buffers when unpickling data with + large arrays. + """ + + def __init__(self, filename, init_args, state): + """Constructor. Store the useful information for later.""" + self.filename = filename + self.state = state + self.init_args = init_args + + def read(self, unpickler): + """Reconstruct the array from the meta-information and the z-file.""" + # Here we a simply reproducing the unpickling mechanism for numpy + # arrays + filename = os.path.join(unpickler._dirname, self.filename) + array = unpickler.np.core.multiarray._reconstruct(*self.init_args) + with open(filename, 'rb') as f: + data = read_zfile(f) + state = self.state + (data,) + array.__setstate__(state) + return array + + +class ZipNumpyUnpickler(Unpickler): + """A subclass of the Unpickler to unpickle our numpy pickles.""" + + dispatch = Unpickler.dispatch.copy() + + def __init__(self, filename, file_handle, mmap_mode=None): + """Constructor.""" + self._filename = os.path.basename(filename) + self._dirname = os.path.dirname(filename) + self.mmap_mode = mmap_mode + self.file_handle = self._open_pickle(file_handle) + Unpickler.__init__(self, self.file_handle) + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def _open_pickle(self, file_handle): + return BytesIO(read_zfile(file_handle)) + + def load_build(self): + """Set the state of a newly created object. + + We capture it to replace our place-holder objects, + NDArrayWrapper, by the array we are interested in. We + replace them directly in the stack of pickler. + """ + Unpickler.load_build(self) + if isinstance(self.stack[-1], NDArrayWrapper): + if self.np is None: + raise ImportError("Trying to unpickle an ndarray, " + "but numpy didn't import correctly") + nd_array_wrapper = self.stack.pop() + array = nd_array_wrapper.read(self) + self.stack.append(array) + + dispatch[pickle.BUILD[0]] = load_build + + +def load_compatibility(filename): + """Reconstruct a Python object from a file persisted with joblib.dump. + + This function ensures the compatibility with joblib old persistence format + (<= 0.9.3). + + Parameters + ---------- + filename: string + The name of the file from which to load the object + + Returns + ------- + result: any Python object + The object stored in the file. + + See Also + -------- + joblib.dump : function to save an object + + Notes + ----- + + This function can load numpy array files saved separately during the + dump. + """ + with open(filename, 'rb') as file_handle: + # We are careful to open the file handle early and keep it open to + # avoid race-conditions on renames. That said, if data is stored in + # companion files, moving the directory will create a race when + # joblib tries to access the companion files. + unpickler = ZipNumpyUnpickler(filename, file_handle=file_handle) + try: + obj = unpickler.load() + except UnicodeDecodeError as exc: + # More user-friendly error message + new_exc = ValueError( + 'You may be trying to read with ' + 'python 3 a joblib pickle generated with python 2. ' + 'This feature is not supported by joblib.') + new_exc.__cause__ = exc + raise new_exc + finally: + if hasattr(unpickler, 'file_handle'): + unpickler.file_handle.close() + return obj diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_utils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_utils.py new file mode 100644 index 0000000..23cfb34 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/numpy_pickle_utils.py @@ -0,0 +1,253 @@ +"""Utilities for fast persistence of big data, with optional compression.""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import pickle +import io +import sys +import warnings +import contextlib + +from .compressor import _ZFILE_PREFIX +from .compressor import _COMPRESSORS + +try: + import numpy as np +except ImportError: + np = None + +Unpickler = pickle._Unpickler +Pickler = pickle._Pickler +xrange = range + + +try: + # The python standard library can be built without bz2 so we make bz2 + # usage optional. + # see https://github.com/scikit-learn/scikit-learn/issues/7526 for more + # details. + import bz2 +except ImportError: + bz2 = None + +# Buffer size used in io.BufferedReader and io.BufferedWriter +_IO_BUFFER_SIZE = 1024 ** 2 + + +def _is_raw_file(fileobj): + """Check if fileobj is a raw file object, e.g created with open.""" + fileobj = getattr(fileobj, 'raw', fileobj) + return isinstance(fileobj, io.FileIO) + + +def _get_prefixes_max_len(): + # Compute the max prefix len of registered compressors. + prefixes = [len(compressor.prefix) for compressor in _COMPRESSORS.values()] + prefixes += [len(_ZFILE_PREFIX)] + return max(prefixes) + + +def _is_numpy_array_byte_order_mismatch(array): + """Check if numpy array is having byte order mismatch""" + return ((sys.byteorder == 'big' and + (array.dtype.byteorder == '<' or + (array.dtype.byteorder == '|' and array.dtype.fields and + all(e[0].byteorder == '<' + for e in array.dtype.fields.values())))) or + (sys.byteorder == 'little' and + (array.dtype.byteorder == '>' or + (array.dtype.byteorder == '|' and array.dtype.fields and + all(e[0].byteorder == '>' + for e in array.dtype.fields.values()))))) + + +def _ensure_native_byte_order(array): + """Use the byte order of the host while preserving values + + Does nothing if array already uses the system byte order. + """ + if _is_numpy_array_byte_order_mismatch(array): + array = array.byteswap().view(array.dtype.newbyteorder('=')) + return array + + +############################################################################### +# Cache file utilities +def _detect_compressor(fileobj): + """Return the compressor matching fileobj. + + Parameters + ---------- + fileobj: file object + + Returns + ------- + str in {'zlib', 'gzip', 'bz2', 'lzma', 'xz', 'compat', 'not-compressed'} + """ + # Read the magic number in the first bytes of the file. + max_prefix_len = _get_prefixes_max_len() + if hasattr(fileobj, 'peek'): + # Peek allows to read those bytes without moving the cursor in the + # file whic. + first_bytes = fileobj.peek(max_prefix_len) + else: + # Fallback to seek if the fileobject is not peekable. + first_bytes = fileobj.read(max_prefix_len) + fileobj.seek(0) + + if first_bytes.startswith(_ZFILE_PREFIX): + return "compat" + else: + for name, compressor in _COMPRESSORS.items(): + if first_bytes.startswith(compressor.prefix): + return name + + return "not-compressed" + + +def _buffered_read_file(fobj): + """Return a buffered version of a read file object.""" + return io.BufferedReader(fobj, buffer_size=_IO_BUFFER_SIZE) + + +def _buffered_write_file(fobj): + """Return a buffered version of a write file object.""" + return io.BufferedWriter(fobj, buffer_size=_IO_BUFFER_SIZE) + + +@contextlib.contextmanager +def _read_fileobject(fileobj, filename, mmap_mode=None): + """Utility function opening the right fileobject from a filename. + + The magic number is used to choose between the type of file object to open: + * regular file object (default) + * zlib file object + * gzip file object + * bz2 file object + * lzma file object (for xz and lzma compressor) + + Parameters + ---------- + fileobj: file object + compressor: str in {'zlib', 'gzip', 'bz2', 'lzma', 'xz', 'compat', + 'not-compressed'} + filename: str + filename path corresponding to the fileobj parameter. + mmap_mode: str + memory map mode that should be used to open the pickle file. This + parameter is useful to verify that the user is not trying to one with + compression. Default: None. + + Returns + ------- + a file like object + + """ + # Detect if the fileobj contains compressed data. + compressor = _detect_compressor(fileobj) + + if compressor == 'compat': + # Compatibility with old pickle mode: simply return the input + # filename "as-is" and let the compatibility function be called by the + # caller. + warnings.warn("The file '%s' has been generated with a joblib " + "version less than 0.10. " + "Please regenerate this pickle file." % filename, + DeprecationWarning, stacklevel=2) + yield filename + else: + if compressor in _COMPRESSORS: + # based on the compressor detected in the file, we open the + # correct decompressor file object, wrapped in a buffer. + compressor_wrapper = _COMPRESSORS[compressor] + inst = compressor_wrapper.decompressor_file(fileobj) + fileobj = _buffered_read_file(inst) + + # Checking if incompatible load parameters with the type of file: + # mmap_mode cannot be used with compressed file or in memory buffers + # such as io.BytesIO. + if mmap_mode is not None: + if isinstance(fileobj, io.BytesIO): + warnings.warn('In memory persistence is not compatible with ' + 'mmap_mode "%(mmap_mode)s" flag passed. ' + 'mmap_mode option will be ignored.' + % locals(), stacklevel=2) + elif compressor != 'not-compressed': + warnings.warn('mmap_mode "%(mmap_mode)s" is not compatible ' + 'with compressed file %(filename)s. ' + '"%(mmap_mode)s" flag will be ignored.' + % locals(), stacklevel=2) + elif not _is_raw_file(fileobj): + warnings.warn('"%(fileobj)r" is not a raw file, mmap_mode ' + '"%(mmap_mode)s" flag will be ignored.' + % locals(), stacklevel=2) + + yield fileobj + + +def _write_fileobject(filename, compress=("zlib", 3)): + """Return the right compressor file object in write mode.""" + compressmethod = compress[0] + compresslevel = compress[1] + + if compressmethod in _COMPRESSORS.keys(): + file_instance = _COMPRESSORS[compressmethod].compressor_file( + filename, compresslevel=compresslevel) + return _buffered_write_file(file_instance) + else: + file_instance = _COMPRESSORS['zlib'].compressor_file( + filename, compresslevel=compresslevel) + return _buffered_write_file(file_instance) + + +# Utility functions/variables from numpy required for writing arrays. +# We need at least the functions introduced in version 1.9 of numpy. Here, +# we use the ones from numpy 1.10.2. +BUFFER_SIZE = 2 ** 18 # size of buffer for reading npz files in bytes + + +def _read_bytes(fp, size, error_template="ran out of data"): + """Read from file-like object until size bytes are read. + + TODO python2_drop: is it still needed? The docstring mentions python 2.6 + and it looks like this can be at least simplified ... + + Raises ValueError if not EOF is encountered before size bytes are read. + Non-blocking objects only supported if they derive from io objects. + + Required as e.g. ZipExtFile in python 2.6 can return less data than + requested. + + This function was taken from numpy/lib/format.py in version 1.10.2. + + Parameters + ---------- + fp: file-like object + size: int + error_template: str + + Returns + ------- + a bytes object + The data read in bytes. + + """ + data = bytes() + while True: + # io files (default in python3) return None or raise on + # would-block, python2 file will truncate, probably nothing can be + # done about that. note that regular files can't be non-blocking + try: + r = fp.read(size - len(data)) + data += r + if len(r) == 0 or len(data) == size: + break + except io.BlockingIOError: + pass + if len(data) != size: + msg = "EOF: reading %s, expected %d bytes got %d" + raise ValueError(msg % (error_template, size, len(data))) + else: + return data diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/parallel.py b/.pythonlibs/lib/python3.10/site-packages/joblib/parallel.py new file mode 100644 index 0000000..e42385d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/parallel.py @@ -0,0 +1,2010 @@ +""" +Helpers for embarrassingly parallel code. +""" +# Author: Gael Varoquaux < gael dot varoquaux at normalesup dot org > +# Copyright: 2010, Gael Varoquaux +# License: BSD 3 clause + +from __future__ import division + +import os +import sys +from math import sqrt +import functools +import collections +import time +import threading +import itertools +from uuid import uuid4 +from numbers import Integral +import warnings +import queue +import weakref +from contextlib import nullcontext + +from multiprocessing import TimeoutError + +from ._multiprocessing_helpers import mp + +from .logger import Logger, short_format_time +from .disk import memstr_to_bytes +from ._parallel_backends import (FallbackToBackend, MultiprocessingBackend, + ThreadingBackend, SequentialBackend, + LokyBackend) +from ._utils import eval_expr, _Sentinel + +# Make sure that those two classes are part of the public joblib.parallel API +# so that 3rd party backend implementers can import them from here. +from ._parallel_backends import AutoBatchingMixin # noqa +from ._parallel_backends import ParallelBackendBase # noqa + + +IS_PYPY = hasattr(sys, "pypy_version_info") + + +BACKENDS = { + 'threading': ThreadingBackend, + 'sequential': SequentialBackend, +} +# name of the backend used by default by Parallel outside of any context +# managed by ``parallel_config`` or ``parallel_backend``. + +# threading is the only backend that is always everywhere +DEFAULT_BACKEND = 'threading' + +MAYBE_AVAILABLE_BACKENDS = {'multiprocessing', 'loky'} + +# if multiprocessing is available, so is loky, we set it as the default +# backend +if mp is not None: + BACKENDS['multiprocessing'] = MultiprocessingBackend + from .externals import loky + BACKENDS['loky'] = LokyBackend + DEFAULT_BACKEND = 'loky' + + +DEFAULT_THREAD_BACKEND = 'threading' + + +# Thread local value that can be overridden by the ``parallel_config`` context +# manager +_backend = threading.local() + + +def _register_dask(): + """Register Dask Backend if called with parallel_config(backend="dask")""" + try: + from ._dask import DaskDistributedBackend + register_parallel_backend('dask', DaskDistributedBackend) + except ImportError as e: + msg = ("To use the dask.distributed backend you must install both " + "the `dask` and distributed modules.\n\n" + "See https://dask.pydata.org/en/latest/install.html for more " + "information.") + raise ImportError(msg) from e + + +EXTERNAL_BACKENDS = { + 'dask': _register_dask, +} + + +# Sentinels for the default values of the Parallel constructor and +# the parallel_config and parallel_backend context managers +default_parallel_config = { + "backend": _Sentinel(default_value=None), + "n_jobs": _Sentinel(default_value=None), + "verbose": _Sentinel(default_value=0), + "temp_folder": _Sentinel(default_value=None), + "max_nbytes": _Sentinel(default_value="1M"), + "mmap_mode": _Sentinel(default_value="r"), + "prefer": _Sentinel(default_value=None), + "require": _Sentinel(default_value=None), +} + + +VALID_BACKEND_HINTS = ('processes', 'threads', None) +VALID_BACKEND_CONSTRAINTS = ('sharedmem', None) + + +def _get_config_param(param, context_config, key): + """Return the value of a parallel config parameter + + Explicitly setting it in Parallel has priority over setting in a + parallel_(config/backend) context manager. + """ + if param is not default_parallel_config[key]: + # param is explicitely set, return it + return param + + if context_config[key] is not default_parallel_config[key]: + # there's a context manager and the key is set, return it + return context_config[key] + + # Otherwise, we are in the default_parallel_config, + # return the default value + return param.default_value + + +def get_active_backend( + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + verbose=default_parallel_config["verbose"], +): + """Return the active default backend""" + backend, config = _get_active_backend(prefer, require, verbose) + n_jobs = _get_config_param( + default_parallel_config['n_jobs'], config, "n_jobs" + ) + return backend, n_jobs + + +def _get_active_backend( + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + verbose=default_parallel_config["verbose"], +): + """Return the active default backend""" + + backend_config = getattr(_backend, "config", default_parallel_config) + + backend = _get_config_param( + default_parallel_config['backend'], backend_config, "backend" + ) + prefer = _get_config_param(prefer, backend_config, "prefer") + require = _get_config_param(require, backend_config, "require") + verbose = _get_config_param(verbose, backend_config, "verbose") + + if prefer not in VALID_BACKEND_HINTS: + raise ValueError( + f"prefer={prefer} is not a valid backend hint, " + f"expected one of {VALID_BACKEND_HINTS}" + ) + if require not in VALID_BACKEND_CONSTRAINTS: + raise ValueError( + f"require={require} is not a valid backend constraint, " + f"expected one of {VALID_BACKEND_CONSTRAINTS}" + ) + if prefer == 'processes' and require == 'sharedmem': + raise ValueError( + "prefer == 'processes' and require == 'sharedmem'" + " are inconsistent settings" + ) + + explicit_backend = True + if backend is None: + + # We are either outside of the scope of any parallel_(config/backend) + # context manager or the context manager did not set a backend. + # create the default backend instance now. + backend = BACKENDS[DEFAULT_BACKEND](nesting_level=0) + explicit_backend = False + + # Try to use the backend set by the user with the context manager. + + nesting_level = backend.nesting_level + uses_threads = getattr(backend, 'uses_threads', False) + supports_sharedmem = getattr(backend, 'supports_sharedmem', False) + # Force to use thread-based backend if the provided backend does not + # match the shared memory constraint or if the backend is not explicitely + # given and threads are prefered. + force_threads = (require == 'sharedmem' and not supports_sharedmem) + force_threads |= ( + not explicit_backend and prefer == 'threads' and not uses_threads + ) + if force_threads: + # This backend does not match the shared memory constraint: + # fallback to the default thead-based backend. + sharedmem_backend = BACKENDS[DEFAULT_THREAD_BACKEND]( + nesting_level=nesting_level + ) + # Warn the user if we forced the backend to thread-based, while the + # user explicitely specified a non-thread-based backend. + if verbose >= 10 and explicit_backend: + print( + f"Using {sharedmem_backend.__class__.__name__} as " + f"joblib backend instead of {backend.__class__.__name__} " + "as the latter does not provide shared memory semantics." + ) + # Force to n_jobs=1 by default + thread_config = backend_config.copy() + thread_config['n_jobs'] = 1 + return sharedmem_backend, thread_config + + return backend, backend_config + + +class parallel_config: + """Set the default backend or configuration for :class:`~joblib.Parallel`. + + This is an alternative to directly passing keyword arguments to the + :class:`~joblib.Parallel` class constructor. It is particularly useful when + calling into library code that uses joblib internally but does not expose + the various parallel configuration arguments in its own API. + + Parameters + ---------- + backend: str or ParallelBackendBase instance, default=None + If ``backend`` is a string it must match a previously registered + implementation using the :func:`~register_parallel_backend` function. + + By default the following backends are available: + + - 'loky': single-host, process-based parallelism (used by default), + - 'threading': single-host, thread-based parallelism, + - 'multiprocessing': legacy single-host, process-based parallelism. + + 'loky' is recommended to run functions that manipulate Python objects. + 'threading' is a low-overhead alternative that is most efficient for + functions that release the Global Interpreter Lock: e.g. I/O-bound + code or CPU-bound code in a few calls to native code that explicitly + releases the GIL. Note that on some rare systems (such as pyodide), + multiprocessing and loky may not be available, in which case joblib + defaults to threading. + + In addition, if the ``dask`` and ``distributed`` Python packages are + installed, it is possible to use the 'dask' backend for better + scheduling of nested parallel calls without over-subscription and + potentially distribute parallel calls over a networked cluster of + several hosts. + + It is also possible to use the distributed 'ray' backend for + distributing the workload to a cluster of nodes. See more details + in the Examples section below. + + Alternatively the backend can be passed directly as an instance. + + n_jobs: int, default=None + The maximum number of concurrently running jobs, such as the number + of Python worker processes when ``backend="loky"`` or the size of the + thread-pool when ``backend="threading"``. + This argument is converted to an integer, rounded below for float. + If -1 is given, `joblib` tries to use all CPUs. The number of CPUs + ``n_cpus`` is obtained with :func:`~cpu_count`. + For n_jobs below -1, (n_cpus + 1 + n_jobs) are used. For instance, + using ``n_jobs=-2`` will result in all CPUs but one being used. + This argument can also go above ``n_cpus``, which will cause + oversubscription. In some cases, slight oversubscription can be + beneficial, e.g., for tasks with large I/O operations. + If 1 is given, no parallel computing code is used at all, and the + behavior amounts to a simple python `for` loop. This mode is not + compatible with `timeout`. + None is a marker for 'unset' that will be interpreted as n_jobs=1 + unless the call is performed under a :func:`~parallel_config` + context manager that sets another value for ``n_jobs``. + If n_jobs = 0 then a ValueError is raised. + + verbose: int, default=0 + The verbosity level: if non zero, progress messages are + printed. Above 50, the output is sent to stdout. + The frequency of the messages increases with the verbosity level. + If it more than 10, all iterations are reported. + + temp_folder: str or None, default=None + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the ``JOBLIB_TEMP_FOLDER`` environment + variable, + - ``/dev/shm`` if the folder exists and is writable: this is a + RAM disk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with ``TMP``, ``TMPDIR`` or ``TEMP`` environment + variables, typically ``/tmp`` under Unix operating systems. + + max_nbytes int, str, or None, optional, default='1M' + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. Can be an int + in Bytes, or a human-readable string, e.g., '1M' for 1 megabyte. + Use None to disable memmapping of large arrays. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, default='r' + Memmapping mode for numpy arrays passed to workers. None will + disable memmapping, other modes defined in the numpy.memmap doc: + https://numpy.org/doc/stable/reference/generated/numpy.memmap.html + Also, see 'max_nbytes' parameter documentation for more details. + + prefer: str in {'processes', 'threads'} or None, default=None + Soft hint to choose the default backend. + The default process-based backend is 'loky' and the default + thread-based backend is 'threading'. Ignored if the ``backend`` + parameter is specified. + + require: 'sharedmem' or None, default=None + Hard constraint to select the backend. If set to 'sharedmem', + the selected backend will be single-host and thread-based. + + inner_max_num_threads: int, default=None + If not None, overwrites the limit set on the number of threads + usable in some third-party library threadpools like OpenBLAS, + MKL or OpenMP. This is only used with the ``loky`` backend. + + backend_params: dict + Additional parameters to pass to the backend constructor when + backend is a string. + + Notes + ----- + Joblib tries to limit the oversubscription by limiting the number of + threads usable in some third-party library threadpools like OpenBLAS, MKL + or OpenMP. The default limit in each worker is set to + ``max(cpu_count() // effective_n_jobs, 1)`` but this limit can be + overwritten with the ``inner_max_num_threads`` argument which will be used + to set this limit in the child processes. + + .. versionadded:: 1.3 + + Examples + -------- + >>> from operator import neg + >>> with parallel_config(backend='threading'): + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + ... + [-1, -2, -3, -4, -5] + + To use the 'ray' joblib backend add the following lines: + + >>> from ray.util.joblib import register_ray # doctest: +SKIP + >>> register_ray() # doctest: +SKIP + >>> with parallel_config(backend="ray"): # doctest: +SKIP + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + [-1, -2, -3, -4, -5] + + """ + def __init__( + self, + backend=default_parallel_config["backend"], + *, + n_jobs=default_parallel_config["n_jobs"], + verbose=default_parallel_config["verbose"], + temp_folder=default_parallel_config["temp_folder"], + max_nbytes=default_parallel_config["max_nbytes"], + mmap_mode=default_parallel_config["mmap_mode"], + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + inner_max_num_threads=None, + **backend_params + ): + # Save the parallel info and set the active parallel config + self.old_parallel_config = getattr( + _backend, "config", default_parallel_config + ) + + backend = self._check_backend( + backend, inner_max_num_threads, **backend_params + ) + + new_config = { + "n_jobs": n_jobs, + "verbose": verbose, + "temp_folder": temp_folder, + "max_nbytes": max_nbytes, + "mmap_mode": mmap_mode, + "prefer": prefer, + "require": require, + "backend": backend + } + self.parallel_config = self.old_parallel_config.copy() + self.parallel_config.update({ + k: v for k, v in new_config.items() + if not isinstance(v, _Sentinel) + }) + + setattr(_backend, "config", self.parallel_config) + + def _check_backend(self, backend, inner_max_num_threads, **backend_params): + if backend is default_parallel_config['backend']: + if inner_max_num_threads is not None or len(backend_params) > 0: + raise ValueError( + "inner_max_num_threads and other constructor " + "parameters backend_params are only supported " + "when backend is not None." + ) + return backend + + if isinstance(backend, str): + # Handle non-registered or missing backends + if backend not in BACKENDS: + if backend in EXTERNAL_BACKENDS: + register = EXTERNAL_BACKENDS[backend] + register() + elif backend in MAYBE_AVAILABLE_BACKENDS: + warnings.warn( + f"joblib backend '{backend}' is not available on " + f"your system, falling back to {DEFAULT_BACKEND}.", + UserWarning, + stacklevel=2 + ) + BACKENDS[backend] = BACKENDS[DEFAULT_BACKEND] + else: + raise ValueError( + f"Invalid backend: {backend}, expected one of " + f"{sorted(BACKENDS.keys())}" + ) + + backend = BACKENDS[backend](**backend_params) + + if inner_max_num_threads is not None: + msg = ( + f"{backend.__class__.__name__} does not accept setting the " + "inner_max_num_threads argument." + ) + assert backend.supports_inner_max_num_threads, msg + backend.inner_max_num_threads = inner_max_num_threads + + # If the nesting_level of the backend is not set previously, use the + # nesting level from the previous active_backend to set it + if backend.nesting_level is None: + parent_backend = self.old_parallel_config['backend'] + if parent_backend is default_parallel_config['backend']: + nesting_level = 0 + else: + nesting_level = parent_backend.nesting_level + backend.nesting_level = nesting_level + + return backend + + def __enter__(self): + return self.parallel_config + + def __exit__(self, type, value, traceback): + self.unregister() + + def unregister(self): + setattr(_backend, "config", self.old_parallel_config) + + +class parallel_backend(parallel_config): + """Change the default backend used by Parallel inside a with block. + + .. warning:: + It is advised to use the :class:`~joblib.parallel_config` context + manager instead, which allows more fine-grained control over the + backend configuration. + + If ``backend`` is a string it must match a previously registered + implementation using the :func:`~register_parallel_backend` function. + + By default the following backends are available: + + - 'loky': single-host, process-based parallelism (used by default), + - 'threading': single-host, thread-based parallelism, + - 'multiprocessing': legacy single-host, process-based parallelism. + + 'loky' is recommended to run functions that manipulate Python objects. + 'threading' is a low-overhead alternative that is most efficient for + functions that release the Global Interpreter Lock: e.g. I/O-bound code or + CPU-bound code in a few calls to native code that explicitly releases the + GIL. Note that on some rare systems (such as Pyodide), + multiprocessing and loky may not be available, in which case joblib + defaults to threading. + + You can also use the `Dask `_ joblib + backend to distribute work across machines. This works well with + scikit-learn estimators with the ``n_jobs`` parameter, for example:: + + >>> import joblib # doctest: +SKIP + >>> from sklearn.model_selection import GridSearchCV # doctest: +SKIP + >>> from dask.distributed import Client, LocalCluster # doctest: +SKIP + + >>> # create a local Dask cluster + >>> cluster = LocalCluster() # doctest: +SKIP + >>> client = Client(cluster) # doctest: +SKIP + >>> grid_search = GridSearchCV(estimator, param_grid, n_jobs=-1) + ... # doctest: +SKIP + >>> with joblib.parallel_backend("dask", scatter=[X, y]): # doctest: +SKIP + ... grid_search.fit(X, y) + + It is also possible to use the distributed 'ray' backend for distributing + the workload to a cluster of nodes. To use the 'ray' joblib backend add + the following lines:: + + >>> from ray.util.joblib import register_ray # doctest: +SKIP + >>> register_ray() # doctest: +SKIP + >>> with parallel_backend("ray"): # doctest: +SKIP + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + [-1, -2, -3, -4, -5] + + Alternatively the backend can be passed directly as an instance. + + By default all available workers will be used (``n_jobs=-1``) unless the + caller passes an explicit value for the ``n_jobs`` parameter. + + This is an alternative to passing a ``backend='backend_name'`` argument to + the :class:`~Parallel` class constructor. It is particularly useful when + calling into library code that uses joblib internally but does not expose + the backend argument in its own API. + + >>> from operator import neg + >>> with parallel_backend('threading'): + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + ... + [-1, -2, -3, -4, -5] + + Joblib also tries to limit the oversubscription by limiting the number of + threads usable in some third-party library threadpools like OpenBLAS, MKL + or OpenMP. The default limit in each worker is set to + ``max(cpu_count() // effective_n_jobs, 1)`` but this limit can be + overwritten with the ``inner_max_num_threads`` argument which will be used + to set this limit in the child processes. + + .. versionadded:: 0.10 + + See Also + -------- + joblib.parallel_config: context manager to change the backend + configuration. + """ + def __init__(self, backend, n_jobs=-1, inner_max_num_threads=None, + **backend_params): + + super().__init__( + backend=backend, + n_jobs=n_jobs, + inner_max_num_threads=inner_max_num_threads, + **backend_params + ) + + if self.old_parallel_config is None: + self.old_backend_and_jobs = None + else: + self.old_backend_and_jobs = ( + self.old_parallel_config["backend"], + self.old_parallel_config["n_jobs"], + ) + self.new_backend_and_jobs = ( + self.parallel_config["backend"], + self.parallel_config["n_jobs"], + ) + + def __enter__(self): + return self.new_backend_and_jobs + + +# Under Linux or OS X the default start method of multiprocessing +# can cause third party libraries to crash. Under Python 3.4+ it is possible +# to set an environment variable to switch the default start method from +# 'fork' to 'forkserver' or 'spawn' to avoid this issue albeit at the cost +# of causing semantic changes and some additional pool instantiation overhead. +DEFAULT_MP_CONTEXT = None +if hasattr(mp, 'get_context'): + method = os.environ.get('JOBLIB_START_METHOD', '').strip() or None + if method is not None: + DEFAULT_MP_CONTEXT = mp.get_context(method=method) + + +class BatchedCalls(object): + """Wrap a sequence of (func, args, kwargs) tuples as a single callable""" + + def __init__(self, iterator_slice, backend_and_jobs, reducer_callback=None, + pickle_cache=None): + self.items = list(iterator_slice) + self._size = len(self.items) + self._reducer_callback = reducer_callback + if isinstance(backend_and_jobs, tuple): + self._backend, self._n_jobs = backend_and_jobs + else: + # this is for backward compatibility purposes. Before 0.12.6, + # nested backends were returned without n_jobs indications. + self._backend, self._n_jobs = backend_and_jobs, None + self._pickle_cache = pickle_cache if pickle_cache is not None else {} + + def __call__(self): + # Set the default nested backend to self._backend but do not set the + # change the default number of processes to -1 + with parallel_config(backend=self._backend, n_jobs=self._n_jobs): + return [func(*args, **kwargs) + for func, args, kwargs in self.items] + + def __reduce__(self): + if self._reducer_callback is not None: + self._reducer_callback() + # no need to pickle the callback. + return ( + BatchedCalls, + (self.items, (self._backend, self._n_jobs), None, + self._pickle_cache) + ) + + def __len__(self): + return self._size + + +# Possible exit status for a task +TASK_DONE = "Done" +TASK_ERROR = "Error" +TASK_PENDING = "Pending" + + +############################################################################### +# CPU count that works also when multiprocessing has been disabled via +# the JOBLIB_MULTIPROCESSING environment variable +def cpu_count(only_physical_cores=False): + """Return the number of CPUs. + + This delegates to loky.cpu_count that takes into account additional + constraints such as Linux CFS scheduler quotas (typically set by container + runtimes such as docker) and CPU affinity (for instance using the taskset + command on Linux). + + If only_physical_cores is True, do not take hyperthreading / SMT logical + cores into account. + """ + if mp is None: + return 1 + + return loky.cpu_count(only_physical_cores=only_physical_cores) + + +############################################################################### +# For verbosity + +def _verbosity_filter(index, verbose): + """ Returns False for indices increasingly apart, the distance + depending on the value of verbose. + + We use a lag increasing as the square of index + """ + if not verbose: + return True + elif verbose > 10: + return False + if index == 0: + return False + verbose = .5 * (11 - verbose) ** 2 + scale = sqrt(index / verbose) + next_scale = sqrt((index + 1) / verbose) + return (int(next_scale) == int(scale)) + + +############################################################################### +def delayed(function): + """Decorator used to capture the arguments of a function.""" + + def delayed_function(*args, **kwargs): + return function, args, kwargs + try: + delayed_function = functools.wraps(function)(delayed_function) + except AttributeError: + " functools.wraps fails on some callable objects " + return delayed_function + + +############################################################################### +class BatchCompletionCallBack(object): + """Callback to keep track of completed results and schedule the next tasks. + + This callable is executed by the parent process whenever a worker process + has completed a batch of tasks. + + It is used for progress reporting, to update estimate of the batch + processing duration and to schedule the next batch of tasks to be + processed. + + It is assumed that this callback will always be triggered by the backend + right after the end of a task, in case of success as well as in case of + failure. + """ + + ########################################################################## + # METHODS CALLED BY THE MAIN THREAD # + ########################################################################## + def __init__(self, dispatch_timestamp, batch_size, parallel): + self.dispatch_timestamp = dispatch_timestamp + self.batch_size = batch_size + self.parallel = parallel + self.parallel_call_id = parallel._call_id + + # Internals to keep track of the status and outcome of the task. + + # Used to hold a reference to the future-like object returned by the + # backend after launching this task + # This will be set later when calling `register_job`, as it is only + # created once the task has been submitted. + self.job = None + + if not parallel._backend.supports_retrieve_callback: + # The status is only used for asynchronous result retrieval in the + # callback. + self.status = None + else: + # The initial status for the job is TASK_PENDING. + # Once it is done, it will be either TASK_DONE, or TASK_ERROR. + self.status = TASK_PENDING + + def register_job(self, job): + """Register the object returned by `apply_async`.""" + self.job = job + + def get_result(self, timeout): + """Returns the raw result of the task that was submitted. + + If the task raised an exception rather than returning, this same + exception will be raised instead. + + If the backend supports the retrieval callback, it is assumed that this + method is only called after the result has been registered. It is + ensured by checking that `self.status(timeout)` does not return + TASK_PENDING. In this case, `get_result` directly returns the + registered result (or raise the registered exception). + + For other backends, there are no such assumptions, but `get_result` + still needs to synchronously retrieve the result before it can + return it or raise. It will block at most `self.timeout` seconds + waiting for retrieval to complete, after that it raises a TimeoutError. + """ + + backend = self.parallel._backend + + if backend.supports_retrieve_callback: + # We assume that the result has already been retrieved by the + # callback thread, and is stored internally. It's just waiting to + # be returned. + return self._return_or_raise() + + # For other backends, the main thread needs to run the retrieval step. + try: + if backend.supports_timeout: + result = self.job.get(timeout=timeout) + else: + result = self.job.get() + outcome = dict(result=result, status=TASK_DONE) + except BaseException as e: + outcome = dict(result=e, status=TASK_ERROR) + self._register_outcome(outcome) + + return self._return_or_raise() + + def _return_or_raise(self): + try: + if self.status == TASK_ERROR: + raise self._result + return self._result + finally: + del self._result + + def get_status(self, timeout): + """Get the status of the task. + + This function also checks if the timeout has been reached and register + the TimeoutError outcome when it is the case. + """ + if timeout is None or self.status != TASK_PENDING: + return self.status + + # The computation are running and the status is pending. + # Check that we did not wait for this jobs more than `timeout`. + now = time.time() + if not hasattr(self, "_completion_timeout_counter"): + self._completion_timeout_counter = now + + if (now - self._completion_timeout_counter) > timeout: + outcome = dict(result=TimeoutError(), status=TASK_ERROR) + self._register_outcome(outcome) + + return self.status + + ########################################################################## + # METHODS CALLED BY CALLBACK THREADS # + ########################################################################## + def __call__(self, out): + """Function called by the callback thread after a job is completed.""" + + # If the backend doesn't support callback retrievals, the next batch of + # tasks is dispatched regardless. The result will be retrieved by the + # main thread when calling `get_result`. + if not self.parallel._backend.supports_retrieve_callback: + self._dispatch_new() + return + + # If the backend supports retrieving the result in the callback, it + # registers the task outcome (TASK_ERROR or TASK_DONE), and schedules + # the next batch if needed. + with self.parallel._lock: + # Edge case where while the task was processing, the `parallel` + # instance has been reset and a new call has been issued, but the + # worker managed to complete the task and trigger this callback + # call just before being aborted by the reset. + if self.parallel._call_id != self.parallel_call_id: + return + + # When aborting, stop as fast as possible and do not retrieve the + # result as it won't be returned by the Parallel call. + if self.parallel._aborting: + return + + # Retrieves the result of the task in the main process and dispatch + # a new batch if needed. + job_succeeded = self._retrieve_result(out) + + if not self.parallel.return_ordered: + # Append the job to the queue in the order of completion + # instead of submission. + self.parallel._jobs.append(self) + + if job_succeeded: + self._dispatch_new() + + def _dispatch_new(self): + """Schedule the next batch of tasks to be processed.""" + + # This steps ensure that auto-batching works as expected. + this_batch_duration = time.time() - self.dispatch_timestamp + self.parallel._backend.batch_completed(self.batch_size, + this_batch_duration) + + # Schedule the next batch of tasks. + with self.parallel._lock: + self.parallel.n_completed_tasks += self.batch_size + self.parallel.print_progress() + if self.parallel._original_iterator is not None: + self.parallel.dispatch_next() + + def _retrieve_result(self, out): + """Fetch and register the outcome of a task. + + Return True if the task succeeded, False otherwise. + This function is only called by backends that support retrieving + the task result in the callback thread. + """ + try: + result = self.parallel._backend.retrieve_result_callback(out) + outcome = dict(status=TASK_DONE, result=result) + except BaseException as e: + # Avoid keeping references to parallel in the error. + e.__traceback__ = None + outcome = dict(result=e, status=TASK_ERROR) + + self._register_outcome(outcome) + return outcome['status'] != TASK_ERROR + + ########################################################################## + # This method can be called either in the main thread # + # or in the callback thread. # + ########################################################################## + def _register_outcome(self, outcome): + """Register the outcome of a task. + + This method can be called only once, future calls will be ignored. + """ + # Covers the edge case where the main thread tries to register a + # `TimeoutError` while the callback thread tries to register a result + # at the same time. + with self.parallel._lock: + if self.status not in (TASK_PENDING, None): + return + self.status = outcome["status"] + + self._result = outcome["result"] + + # Once the result and the status are extracted, the last reference to + # the job can be deleted. + self.job = None + + # As soon as an error as been spotted, early stopping flags are sent to + # the `parallel` instance. + if self.status == TASK_ERROR: + self.parallel._exception = True + self.parallel._aborting = True + + +############################################################################### +def register_parallel_backend(name, factory, make_default=False): + """Register a new Parallel backend factory. + + The new backend can then be selected by passing its name as the backend + argument to the :class:`~Parallel` class. Moreover, the default backend can + be overwritten globally by setting make_default=True. + + The factory can be any callable that takes no argument and return an + instance of ``ParallelBackendBase``. + + Warning: this function is experimental and subject to change in a future + version of joblib. + + .. versionadded:: 0.10 + """ + BACKENDS[name] = factory + if make_default: + global DEFAULT_BACKEND + DEFAULT_BACKEND = name + + +def effective_n_jobs(n_jobs=-1): + """Determine the number of jobs that can actually run in parallel + + n_jobs is the number of workers requested by the callers. Passing n_jobs=-1 + means requesting all available workers for instance matching the number of + CPU cores on the worker host(s). + + This method should return a guesstimate of the number of workers that can + actually perform work concurrently with the currently enabled default + backend. The primary use case is to make it possible for the caller to know + in how many chunks to slice the work. + + In general working on larger data chunks is more efficient (less scheduling + overhead and better use of CPU cache prefetching heuristics) as long as all + the workers have enough work to do. + + Warning: this function is experimental and subject to change in a future + version of joblib. + + .. versionadded:: 0.10 + """ + if n_jobs == 1: + return 1 + + backend, backend_n_jobs = get_active_backend() + if n_jobs is None: + n_jobs = backend_n_jobs + return backend.effective_n_jobs(n_jobs=n_jobs) + + +############################################################################### +class Parallel(Logger): + ''' Helper class for readable parallel mapping. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + n_jobs: int, default=None + The maximum number of concurrently running jobs, such as the number + of Python worker processes when ``backend="loky"`` or the size of + the thread-pool when ``backend="threading"``. + This argument is converted to an integer, rounded below for float. + If -1 is given, `joblib` tries to use all CPUs. The number of CPUs + ``n_cpus`` is obtained with :func:`~cpu_count`. + For n_jobs below -1, (n_cpus + 1 + n_jobs) are used. For instance, + using ``n_jobs=-2`` will result in all CPUs but one being used. + This argument can also go above ``n_cpus``, which will cause + oversubscription. In some cases, slight oversubscription can be + beneficial, e.g., for tasks with large I/O operations. + If 1 is given, no parallel computing code is used at all, and the + behavior amounts to a simple python `for` loop. This mode is not + compatible with ``timeout``. + None is a marker for 'unset' that will be interpreted as n_jobs=1 + unless the call is performed under a :func:`~parallel_config` + context manager that sets another value for ``n_jobs``. + If n_jobs = 0 then a ValueError is raised. + backend: str, ParallelBackendBase instance or None, default='loky' + Specify the parallelization backend implementation. + Supported backends are: + + - "loky" used by default, can induce some + communication and memory overhead when exchanging input and + output data with the worker Python processes. On some rare + systems (such as Pyiodide), the loky backend may not be + available. + - "multiprocessing" previous process-based backend based on + `multiprocessing.Pool`. Less robust than `loky`. + - "threading" is a very low-overhead backend but it suffers + from the Python Global Interpreter Lock if the called function + relies a lot on Python objects. "threading" is mostly useful + when the execution bottleneck is a compiled extension that + explicitly releases the GIL (for instance a Cython loop wrapped + in a "with nogil" block or an expensive call to a library such + as NumPy). + - finally, you can register backends by calling + :func:`~register_parallel_backend`. This will allow you to + implement a backend of your liking. + + It is not recommended to hard-code the backend name in a call to + :class:`~Parallel` in a library. Instead it is recommended to set + soft hints (prefer) or hard constraints (require) so as to make it + possible for library users to change the backend from the outside + using the :func:`~parallel_config` context manager. + return_as: str in {'list', 'generator', 'generator_unordered'}, default='list' + If 'list', calls to this instance will return a list, only when + all results have been processed and retrieved. + If 'generator', it will return a generator that yields the results + as soon as they are available, in the order the tasks have been + submitted with. + If 'generator_unordered', the generator will immediately yield + available results independently of the submission order. The output + order is not deterministic in this case because it depends on the + concurrency of the workers. + prefer: str in {'processes', 'threads'} or None, default=None + Soft hint to choose the default backend if no specific backend + was selected with the :func:`~parallel_config` context manager. + The default process-based backend is 'loky' and the default + thread-based backend is 'threading'. Ignored if the ``backend`` + parameter is specified. + require: 'sharedmem' or None, default=None + Hard constraint to select the backend. If set to 'sharedmem', + the selected backend will be single-host and thread-based even + if the user asked for a non-thread based backend with + :func:`~joblib.parallel_config`. + verbose: int, default=0 + The verbosity level: if non zero, progress messages are + printed. Above 50, the output is sent to stdout. + The frequency of the messages increases with the verbosity level. + If it more than 10, all iterations are reported. + timeout: float or None, default=None + Timeout limit for each task to complete. If any task takes longer + a TimeOutError will be raised. Only applied when n_jobs != 1 + pre_dispatch: {'all', integer, or expression, as in '3*n_jobs'}, default='2*n_jobs' + The number of batches (of tasks) to be pre-dispatched. + Default is '2*n_jobs'. When batch_size="auto" this is reasonable + default and the workers should never starve. Note that only basic + arithmetics are allowed here and no modules can be used in this + expression. + batch_size: int or 'auto', default='auto' + The number of atomic tasks to dispatch at once to each + worker. When individual evaluations are very fast, dispatching + calls to workers can be slower than sequential computation because + of the overhead. Batching fast computations together can mitigate + this. + The ``'auto'`` strategy keeps track of the time it takes for a + batch to complete, and dynamically adjusts the batch size to keep + the time on the order of half a second, using a heuristic. The + initial batch size is 1. + ``batch_size="auto"`` with ``backend="threading"`` will dispatch + batches of a single task at a time as the threading backend has + very little overhead and using larger batch size has not proved to + bring any gain in that case. + temp_folder: str or None, default=None + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the JOBLIB_TEMP_FOLDER environment + variable, + - /dev/shm if the folder exists and is writable: this is a + RAM disk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with TMP, TMPDIR or TEMP environment + variables, typically /tmp under Unix operating systems. + + Only active when ``backend="loky"`` or ``"multiprocessing"``. + max_nbytes int, str, or None, optional, default='1M' + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. Can be an int + in Bytes, or a human-readable string, e.g., '1M' for 1 megabyte. + Use None to disable memmapping of large arrays. + Only active when ``backend="loky"`` or ``"multiprocessing"``. + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, default='r' + Memmapping mode for numpy arrays passed to workers. None will + disable memmapping, other modes defined in the numpy.memmap doc: + https://numpy.org/doc/stable/reference/generated/numpy.memmap.html + Also, see 'max_nbytes' parameter documentation for more details. + + Notes + ----- + + This object uses workers to compute in parallel the application of a + function to many different arguments. The main functionality it brings + in addition to using the raw multiprocessing or concurrent.futures API + are (see examples for details): + + * More readable code, in particular since it avoids + constructing list of arguments. + + * Easier debugging: + - informative tracebacks even when the error happens on + the client side + - using 'n_jobs=1' enables to turn off parallel computing + for debugging without changing the codepath + - early capture of pickling errors + + * An optional progress meter. + + * Interruption of multiprocesses jobs with 'Ctrl-C' + + * Flexible pickling control for the communication to and from + the worker processes. + + * Ability to use shared memory efficiently with worker + processes for large numpy-based datastructures. + + Note that the intended usage is to run one call at a time. Multiple + calls to the same Parallel object will result in a ``RuntimeError`` + + Examples + -------- + + A simple example: + + >>> from math import sqrt + >>> from joblib import Parallel, delayed + >>> Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10)) + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + + Reshaping the output when the function has several return + values: + + >>> from math import modf + >>> from joblib import Parallel, delayed + >>> r = Parallel(n_jobs=1)(delayed(modf)(i/2.) for i in range(10)) + >>> res, i = zip(*r) + >>> res + (0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5) + >>> i + (0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0) + + The progress meter: the higher the value of `verbose`, the more + messages: + + >>> from time import sleep + >>> from joblib import Parallel, delayed + >>> r = Parallel(n_jobs=2, verbose=10)( + ... delayed(sleep)(.2) for _ in range(10)) #doctest: +SKIP + [Parallel(n_jobs=2)]: Done 1 tasks | elapsed: 0.6s + [Parallel(n_jobs=2)]: Done 4 tasks | elapsed: 0.8s + [Parallel(n_jobs=2)]: Done 10 out of 10 | elapsed: 1.4s finished + + Traceback example, note how the line of the error is indicated + as well as the values of the parameter passed to the function that + triggered the exception, even though the traceback happens in the + child process: + + >>> from heapq import nlargest + >>> from joblib import Parallel, delayed + >>> Parallel(n_jobs=2)( + ... delayed(nlargest)(2, n) for n in (range(4), 'abcde', 3)) + ... # doctest: +SKIP + ----------------------------------------------------------------------- + Sub-process traceback: + ----------------------------------------------------------------------- + TypeError Mon Nov 12 11:37:46 2012 + PID: 12934 Python 2.7.3: /usr/bin/python + ........................................................................ + /usr/lib/python2.7/heapq.pyc in nlargest(n=2, iterable=3, key=None) + 419 if n >= size: + 420 return sorted(iterable, key=key, reverse=True)[:n] + 421 + 422 # When key is none, use simpler decoration + 423 if key is None: + --> 424 it = izip(iterable, count(0,-1)) # decorate + 425 result = _nlargest(n, it) + 426 return map(itemgetter(0), result) # undecorate + 427 + 428 # General case, slowest method + TypeError: izip argument #1 must support iteration + _______________________________________________________________________ + + + Using pre_dispatch in a producer/consumer situation, where the + data is generated on the fly. Note how the producer is first + called 3 times before the parallel loop is initiated, and then + called to generate new data on the fly: + + >>> from math import sqrt + >>> from joblib import Parallel, delayed + >>> def producer(): + ... for i in range(6): + ... print('Produced %s' % i) + ... yield i + >>> out = Parallel(n_jobs=2, verbose=100, pre_dispatch='1.5*n_jobs')( + ... delayed(sqrt)(i) for i in producer()) #doctest: +SKIP + Produced 0 + Produced 1 + Produced 2 + [Parallel(n_jobs=2)]: Done 1 jobs | elapsed: 0.0s + Produced 3 + [Parallel(n_jobs=2)]: Done 2 jobs | elapsed: 0.0s + Produced 4 + [Parallel(n_jobs=2)]: Done 3 jobs | elapsed: 0.0s + Produced 5 + [Parallel(n_jobs=2)]: Done 4 jobs | elapsed: 0.0s + [Parallel(n_jobs=2)]: Done 6 out of 6 | elapsed: 0.0s remaining: 0.0s + [Parallel(n_jobs=2)]: Done 6 out of 6 | elapsed: 0.0s finished + + ''' # noqa: E501 + def __init__( + self, + n_jobs=default_parallel_config["n_jobs"], + backend=default_parallel_config['backend'], + return_as="list", + verbose=default_parallel_config["verbose"], + timeout=None, + pre_dispatch='2 * n_jobs', + batch_size='auto', + temp_folder=default_parallel_config["temp_folder"], + max_nbytes=default_parallel_config["max_nbytes"], + mmap_mode=default_parallel_config["mmap_mode"], + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + ): + # Initiate parent Logger class state + super().__init__() + + # Interpret n_jobs=None as 'unset' + if n_jobs is None: + n_jobs = default_parallel_config["n_jobs"] + + active_backend, context_config = _get_active_backend( + prefer=prefer, require=require, verbose=verbose + ) + + nesting_level = active_backend.nesting_level + + self.verbose = _get_config_param(verbose, context_config, "verbose") + self.timeout = timeout + self.pre_dispatch = pre_dispatch + + if return_as not in {"list", "generator", "generator_unordered"}: + raise ValueError( + 'Expected `return_as` parameter to be a string equal to "list"' + f',"generator" or "generator_unordered", but got {return_as} ' + "instead." + ) + self.return_as = return_as + self.return_generator = return_as != "list" + self.return_ordered = return_as != "generator_unordered" + + # Check if we are under a parallel_config or parallel_backend + # context manager and use the config from the context manager + # for arguments that are not explicitly set. + self._backend_args = { + k: _get_config_param(param, context_config, k) for param, k in [ + (max_nbytes, "max_nbytes"), + (temp_folder, "temp_folder"), + (mmap_mode, "mmap_mode"), + (prefer, "prefer"), + (require, "require"), + (verbose, "verbose"), + ] + } + + if isinstance(self._backend_args["max_nbytes"], str): + self._backend_args["max_nbytes"] = memstr_to_bytes( + self._backend_args["max_nbytes"] + ) + self._backend_args["verbose"] = max( + 0, self._backend_args["verbose"] - 50 + ) + + if DEFAULT_MP_CONTEXT is not None: + self._backend_args['context'] = DEFAULT_MP_CONTEXT + elif hasattr(mp, "get_context"): + self._backend_args['context'] = mp.get_context() + + if backend is default_parallel_config['backend'] or backend is None: + backend = active_backend + + elif isinstance(backend, ParallelBackendBase): + # Use provided backend as is, with the current nesting_level if it + # is not set yet. + if backend.nesting_level is None: + backend.nesting_level = nesting_level + + elif hasattr(backend, 'Pool') and hasattr(backend, 'Lock'): + # Make it possible to pass a custom multiprocessing context as + # backend to change the start method to forkserver or spawn or + # preload modules on the forkserver helper process. + self._backend_args['context'] = backend + backend = MultiprocessingBackend(nesting_level=nesting_level) + + elif backend not in BACKENDS and backend in MAYBE_AVAILABLE_BACKENDS: + warnings.warn( + f"joblib backend '{backend}' is not available on " + f"your system, falling back to {DEFAULT_BACKEND}.", + UserWarning, + stacklevel=2) + BACKENDS[backend] = BACKENDS[DEFAULT_BACKEND] + backend = BACKENDS[DEFAULT_BACKEND](nesting_level=nesting_level) + + else: + try: + backend_factory = BACKENDS[backend] + except KeyError as e: + raise ValueError("Invalid backend: %s, expected one of %r" + % (backend, sorted(BACKENDS.keys()))) from e + backend = backend_factory(nesting_level=nesting_level) + + n_jobs = _get_config_param(n_jobs, context_config, "n_jobs") + if n_jobs is None: + # No specific context override and no specific value request: + # default to the default of the backend. + n_jobs = backend.default_n_jobs + try: + n_jobs = int(n_jobs) + except ValueError: + raise ValueError("n_jobs could not be converted to int") + self.n_jobs = n_jobs + + if (require == 'sharedmem' and + not getattr(backend, 'supports_sharedmem', False)): + raise ValueError("Backend %s does not support shared memory" + % backend) + + if (batch_size == 'auto' or isinstance(batch_size, Integral) and + batch_size > 0): + self.batch_size = batch_size + else: + raise ValueError( + "batch_size must be 'auto' or a positive integer, got: %r" + % batch_size) + + if not isinstance(backend, SequentialBackend): + if self.return_generator and not backend.supports_return_generator: + raise ValueError( + "Backend {} does not support " + "return_as={}".format(backend, return_as) + ) + # This lock is used to coordinate the main thread of this process + # with the async callback thread of our the pool. + self._lock = threading.RLock() + self._jobs = collections.deque() + self._pending_outputs = list() + self._ready_batches = queue.Queue() + self._reducer_callback = None + + # Internal variables + self._backend = backend + self._running = False + self._managed_backend = False + self._id = uuid4().hex + self._call_ref = None + + def __enter__(self): + self._managed_backend = True + self._calling = False + self._initialize_backend() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._managed_backend = False + if self.return_generator and self._calling: + self._abort() + self._terminate_and_reset() + + def _initialize_backend(self): + """Build a process or thread pool and return the number of workers""" + try: + n_jobs = self._backend.configure(n_jobs=self.n_jobs, parallel=self, + **self._backend_args) + if self.timeout is not None and not self._backend.supports_timeout: + warnings.warn( + 'The backend class {!r} does not support timeout. ' + "You have set 'timeout={}' in Parallel but " + "the 'timeout' parameter will not be used.".format( + self._backend.__class__.__name__, + self.timeout)) + + except FallbackToBackend as e: + # Recursively initialize the backend in case of requested fallback. + self._backend = e.backend + n_jobs = self._initialize_backend() + + return n_jobs + + def _effective_n_jobs(self): + if self._backend: + return self._backend.effective_n_jobs(self.n_jobs) + return 1 + + def _terminate_and_reset(self): + if hasattr(self._backend, 'stop_call') and self._calling: + self._backend.stop_call() + self._calling = False + if not self._managed_backend: + self._backend.terminate() + + def _dispatch(self, batch): + """Queue the batch for computing, with or without multiprocessing + + WARNING: this method is not thread-safe: it should be only called + indirectly via dispatch_one_batch. + + """ + # If job.get() catches an exception, it closes the queue: + if self._aborting: + return + + batch_size = len(batch) + + self.n_dispatched_tasks += batch_size + self.n_dispatched_batches += 1 + + dispatch_timestamp = time.time() + + batch_tracker = BatchCompletionCallBack( + dispatch_timestamp, batch_size, self + ) + + if self.return_ordered: + self._jobs.append(batch_tracker) + + # If return_ordered is False, the batch_tracker is not stored in the + # jobs queue at the time of submission. Instead, it will be appended to + # the queue by itself as soon as the callback is triggered to be able + # to return the results in the order of completion. + + job = self._backend.apply_async(batch, callback=batch_tracker) + batch_tracker.register_job(job) + + def dispatch_next(self): + """Dispatch more data for parallel processing + + This method is meant to be called concurrently by the multiprocessing + callback. We rely on the thread-safety of dispatch_one_batch to protect + against concurrent consumption of the unprotected iterator. + + """ + if not self.dispatch_one_batch(self._original_iterator): + self._iterating = False + self._original_iterator = None + + def dispatch_one_batch(self, iterator): + """Prefetch the tasks for the next batch and dispatch them. + + The effective size of the batch is computed here. + If there are no more jobs to dispatch, return False, else return True. + + The iterator consumption and dispatching is protected by the same + lock so calling this function should be thread safe. + + """ + + if self._aborting: + return False + + batch_size = self._get_batch_size() + + with self._lock: + # to ensure an even distribution of the workload between workers, + # we look ahead in the original iterators more than batch_size + # tasks - However, we keep consuming only one batch at each + # dispatch_one_batch call. The extra tasks are stored in a local + # queue, _ready_batches, that is looked-up prior to re-consuming + # tasks from the origal iterator. + try: + tasks = self._ready_batches.get(block=False) + except queue.Empty: + # slice the iterator n_jobs * batchsize items at a time. If the + # slice returns less than that, then the current batchsize puts + # too much weight on a subset of workers, while other may end + # up starving. So in this case, re-scale the batch size + # accordingly to distribute evenly the last items between all + # workers. + n_jobs = self._cached_effective_n_jobs + big_batch_size = batch_size * n_jobs + + try: + islice = list(itertools.islice(iterator, big_batch_size)) + except Exception as e: + # Handle the fact that the generator of task raised an + # exception. As this part of the code can be executed in + # a thread internal to the backend, register a task with + # an error that will be raised in the user's thread. + if isinstance(e.__context__, queue.Empty): + # Supress the cause of the exception if it is + # queue.Empty to avoid cluttered traceback. Only do it + # if the __context__ is really empty to avoid messing + # with causes of the original error. + e.__cause__ = None + batch_tracker = BatchCompletionCallBack( + 0, batch_size, self + ) + self._jobs.append(batch_tracker) + batch_tracker._register_outcome(dict( + result=e, status=TASK_ERROR + )) + return True + + if len(islice) == 0: + return False + elif (iterator is self._original_iterator and + len(islice) < big_batch_size): + # We reached the end of the original iterator (unless + # iterator is the ``pre_dispatch``-long initial slice of + # the original iterator) -- decrease the batch size to + # account for potential variance in the batches running + # time. + final_batch_size = max(1, len(islice) // (10 * n_jobs)) + else: + final_batch_size = max(1, len(islice) // n_jobs) + + # enqueue n_jobs batches in a local queue + for i in range(0, len(islice), final_batch_size): + tasks = BatchedCalls(islice[i:i + final_batch_size], + self._backend.get_nested_backend(), + self._reducer_callback, + self._pickle_cache) + self._ready_batches.put(tasks) + + # finally, get one task. + tasks = self._ready_batches.get(block=False) + if len(tasks) == 0: + # No more tasks available in the iterator: tell caller to stop. + return False + else: + self._dispatch(tasks) + return True + + def _get_batch_size(self): + """Returns the effective batch size for dispatch""" + if self.batch_size == 'auto': + return self._backend.compute_batch_size() + else: + # Fixed batch size strategy + return self.batch_size + + def _print(self, msg): + """Display the message on stout or stderr depending on verbosity""" + # XXX: Not using the logger framework: need to + # learn to use logger better. + if not self.verbose: + return + if self.verbose < 50: + writer = sys.stderr.write + else: + writer = sys.stdout.write + writer(f"[{self}]: {msg}\n") + + def _is_completed(self): + """Check if all tasks have been completed""" + return self.n_completed_tasks == self.n_dispatched_tasks and not ( + self._iterating or self._aborting + ) + + def print_progress(self): + """Display the process of the parallel execution only a fraction + of time, controlled by self.verbose. + """ + + if not self.verbose: + return + + elapsed_time = time.time() - self._start_time + + if self._is_completed(): + # Make sure that we get a last message telling us we are done + self._print( + f"Done {self.n_completed_tasks:3d} out of " + f"{self.n_completed_tasks:3d} | elapsed: " + f"{short_format_time(elapsed_time)} finished" + ) + return + + # Original job iterator becomes None once it has been fully + # consumed: at this point we know the total number of jobs and we are + # able to display an estimation of the remaining time based on already + # completed jobs. Otherwise, we simply display the number of completed + # tasks. + elif self._original_iterator is not None: + if _verbosity_filter(self.n_dispatched_batches, self.verbose): + return + self._print( + f"Done {self.n_completed_tasks:3d} tasks | elapsed: " + f"{short_format_time(elapsed_time)}" + ) + else: + index = self.n_completed_tasks + # We are finished dispatching + total_tasks = self.n_dispatched_tasks + # We always display the first loop + if not index == 0: + # Display depending on the number of remaining items + # A message as soon as we finish dispatching, cursor is 0 + cursor = (total_tasks - index + 1 - + self._pre_dispatch_amount) + frequency = (total_tasks // self.verbose) + 1 + is_last_item = (index + 1 == total_tasks) + if (is_last_item or cursor % frequency): + return + remaining_time = (elapsed_time / index) * \ + (self.n_dispatched_tasks - index * 1.0) + # only display status if remaining time is greater or equal to 0 + self._print( + f"Done {index:3d} out of {total_tasks:3d} | elapsed: " + f"{short_format_time(elapsed_time)} remaining: " + f"{short_format_time(remaining_time)}" + ) + + def _abort(self): + # Stop dispatching new jobs in the async callback thread + self._aborting = True + + # If the backend allows it, cancel or kill remaining running + # tasks without waiting for the results as we will raise + # the exception we got back to the caller instead of returning + # any result. + backend = self._backend + if (not self._aborted and hasattr(backend, 'abort_everything')): + # If the backend is managed externally we need to make sure + # to leave it in a working state to allow for future jobs + # scheduling. + ensure_ready = self._managed_backend + backend.abort_everything(ensure_ready=ensure_ready) + self._aborted = True + + def _start(self, iterator, pre_dispatch): + # Only set self._iterating to True if at least a batch + # was dispatched. In particular this covers the edge + # case of Parallel used with an exhausted iterator. If + # self._original_iterator is None, then this means either + # that pre_dispatch == "all", n_jobs == 1 or that the first batch + # was very quick and its callback already dispatched all the + # remaining jobs. + self._iterating = False + if self.dispatch_one_batch(iterator): + self._iterating = self._original_iterator is not None + + while self.dispatch_one_batch(iterator): + pass + + if pre_dispatch == "all": + # The iterable was consumed all at once by the above for loop. + # No need to wait for async callbacks to trigger to + # consumption. + self._iterating = False + + def _get_outputs(self, iterator, pre_dispatch): + """Iterator returning the tasks' output as soon as they are ready.""" + dispatch_thread_id = threading.get_ident() + detach_generator_exit = False + try: + self._start(iterator, pre_dispatch) + # first yield returns None, for internal use only. This ensures + # that we enter the try/except block and start dispatching the + # tasks. + yield + + with self._backend.retrieval_context(): + yield from self._retrieve() + + except GeneratorExit: + # The generator has been garbage collected before being fully + # consumed. This aborts the remaining tasks if possible and warn + # the user if necessary. + self._exception = True + + # In some interpreters such as PyPy, GeneratorExit can be raised in + # a different thread than the one used to start the dispatch of the + # parallel tasks. This can lead to hang when a thread attempts to + # join itself. As workaround, we detach the execution of the + # aborting code to a dedicated thread. We then need to make sure + # the rest of the function does not call `_terminate_and_reset` + # in finally. + if dispatch_thread_id != threading.get_ident(): + if not IS_PYPY: + warnings.warn( + "A generator produced by joblib.Parallel has been " + "gc'ed in an unexpected thread. This behavior should " + "not cause major -issues but to make sure, please " + "report this warning and your use case at " + "https://github.com/joblib/joblib/issues so it can " + "be investigated." + ) + + detach_generator_exit = True + _parallel = self + + class _GeneratorExitThread(threading.Thread): + def run(self): + _parallel._abort() + if _parallel.return_generator: + _parallel._warn_exit_early() + _parallel._terminate_and_reset() + + _GeneratorExitThread( + name="GeneratorExitThread" + ).start() + return + + # Otherwise, we are in the thread that started the dispatch: we can + # safely abort the execution and warn the user. + self._abort() + if self.return_generator: + self._warn_exit_early() + + raise + + # Note: we catch any BaseException instead of just Exception instances + # to also include KeyboardInterrupt + except BaseException: + self._exception = True + self._abort() + raise + finally: + # Store the unconsumed tasks and terminate the workers if necessary + _remaining_outputs = ([] if self._exception else self._jobs) + self._jobs = collections.deque() + self._running = False + if not detach_generator_exit: + self._terminate_and_reset() + + while len(_remaining_outputs) > 0: + batched_results = _remaining_outputs.popleft() + batched_results = batched_results.get_result(self.timeout) + for result in batched_results: + yield result + + def _wait_retrieval(self): + """Return True if we need to continue retriving some tasks.""" + + # If the input load is still being iterated over, it means that tasks + # are still on the dispatch wait list and their results will need to + # be retrieved later on. + if self._iterating: + return True + + # If some of the dispatched tasks are still being processed by the + # workers, wait for the compute to finish before starting retrieval + if self.n_completed_tasks < self.n_dispatched_tasks: + return True + + # For backends that does not support retrieving asynchronously the + # result to the main process, all results must be carefully retrieved + # in the _retrieve loop in the main thread while the backend is alive. + # For other backends, the actual retrieval is done asynchronously in + # the callback thread, and we can terminate the backend before the + # `self._jobs` result list has been emptied. The remaining results + # will be collected in the `finally` step of the generator. + if not self._backend.supports_retrieve_callback: + if len(self._jobs) > 0: + return True + + return False + + def _retrieve(self): + while self._wait_retrieval(): + + # If the callback thread of a worker has signaled that its task + # triggered an exception, or if the retrieval loop has raised an + # exception (e.g. `GeneratorExit`), exit the loop and surface the + # worker traceback. + if self._aborting: + self._raise_error_fast() + break + + # If the next job is not ready for retrieval yet, we just wait for + # async callbacks to progress. + if ((len(self._jobs) == 0) or + (self._jobs[0].get_status( + timeout=self.timeout) == TASK_PENDING)): + time.sleep(0.01) + continue + + # We need to be careful: the job list can be filling up as + # we empty it and Python list are not thread-safe by + # default hence the use of the lock + with self._lock: + batched_results = self._jobs.popleft() + + # Flatten the batched results to output one output at a time + batched_results = batched_results.get_result(self.timeout) + for result in batched_results: + self._nb_consumed += 1 + yield result + + def _raise_error_fast(self): + """If we are aborting, raise if a job caused an error.""" + + # Find the first job whose status is TASK_ERROR if it exists. + with self._lock: + error_job = next((job for job in self._jobs + if job.status == TASK_ERROR), None) + + # If this error job exists, immediatly raise the error by + # calling get_result. This job might not exists if abort has been + # called directly or if the generator is gc'ed. + if error_job is not None: + error_job.get_result(self.timeout) + + def _warn_exit_early(self): + """Warn the user if the generator is gc'ed before being consumned.""" + ready_outputs = self.n_completed_tasks - self._nb_consumed + is_completed = self._is_completed() + msg = "" + if ready_outputs: + msg += ( + f"{ready_outputs} tasks have been successfully executed " + " but not used." + ) + if not is_completed: + msg += " Additionally, " + + if not is_completed: + msg += ( + f"{self.n_dispatched_tasks - self.n_completed_tasks} tasks " + "which were still being processed by the workers have been " + "cancelled." + ) + + if msg: + msg += ( + " You could benefit from adjusting the input task " + "iterator to limit unnecessary computation time." + ) + + warnings.warn(msg) + + def _get_sequential_output(self, iterable): + """Separate loop for sequential output. + + This simplifies the traceback in case of errors and reduces the + overhead of calling sequential tasks with `joblib`. + """ + try: + self._iterating = True + self._original_iterator = iterable + batch_size = self._get_batch_size() + + if batch_size != 1: + it = iter(iterable) + iterable_batched = iter( + lambda: tuple(itertools.islice(it, batch_size)), () + ) + iterable = ( + task for batch in iterable_batched for task in batch + ) + + # first yield returns None, for internal use only. This ensures + # that we enter the try/except block and setup the generator. + yield None + + # Sequentially call the tasks and yield the results. + for func, args, kwargs in iterable: + self.n_dispatched_batches += 1 + self.n_dispatched_tasks += 1 + res = func(*args, **kwargs) + self.n_completed_tasks += 1 + self.print_progress() + yield res + self._nb_consumed += 1 + except BaseException: + self._exception = True + self._aborting = True + self._aborted = True + raise + finally: + self.print_progress() + self._running = False + self._iterating = False + self._original_iterator = None + + def _reset_run_tracking(self): + """Reset the counters and flags used to track the execution.""" + + # Makes sur the parallel instance was not previously running in a + # thread-safe way. + with getattr(self, '_lock', nullcontext()): + if self._running: + msg = 'This Parallel instance is already running !' + if self.return_generator is True: + msg += ( + " Before submitting new tasks, you must wait for the " + "completion of all the previous tasks, or clean all " + "references to the output generator." + ) + raise RuntimeError(msg) + self._running = True + + # Counter to keep track of the task dispatched and completed. + self.n_dispatched_batches = 0 + self.n_dispatched_tasks = 0 + self.n_completed_tasks = 0 + + # Following count is incremented by one each time the user iterates + # on the output generator, it is used to prepare an informative + # warning message in case the generator is deleted before all the + # dispatched tasks have been consumed. + self._nb_consumed = 0 + + # Following flags are used to synchronize the threads in case one of + # the tasks error-out to ensure that all workers abort fast and that + # the backend terminates properly. + + # Set to True as soon as a worker signals that a task errors-out + self._exception = False + # Set to True in case of early termination following an incident + self._aborting = False + # Set to True after abortion is complete + self._aborted = False + + def __call__(self, iterable): + """Main function to dispatch parallel tasks.""" + + self._reset_run_tracking() + self._start_time = time.time() + + if not self._managed_backend: + n_jobs = self._initialize_backend() + else: + n_jobs = self._effective_n_jobs() + + if n_jobs == 1: + # If n_jobs==1, run the computation sequentially and return + # immediatly to avoid overheads. + output = self._get_sequential_output(iterable) + next(output) + return output if self.return_generator else list(output) + + # Let's create an ID that uniquely identifies the current call. If the + # call is interrupted early and that the same instance is immediately + # re-used, this id will be used to prevent workers that were + # concurrently finalizing a task from the previous call to run the + # callback. + with self._lock: + self._call_id = uuid4().hex + + # self._effective_n_jobs should be called in the Parallel.__call__ + # thread only -- store its value in an attribute for further queries. + self._cached_effective_n_jobs = n_jobs + + if isinstance(self._backend, LokyBackend): + # For the loky backend, we add a callback executed when reducing + # BatchCalls, that makes the loky executor use a temporary folder + # specific to this Parallel object when pickling temporary memmaps. + # This callback is necessary to ensure that several Parallel + # objects using the same resuable executor don't use the same + # temporary resources. + + def _batched_calls_reducer_callback(): + # Relevant implementation detail: the following lines, called + # when reducing BatchedCalls, are called in a thread-safe + # situation, meaning that the context of the temporary folder + # manager will not be changed in between the callback execution + # and the end of the BatchedCalls pickling. The reason is that + # pickling (the only place where set_current_context is used) + # is done from a single thread (the queue_feeder_thread). + self._backend._workers._temp_folder_manager.set_current_context( # noqa + self._id + ) + self._reducer_callback = _batched_calls_reducer_callback + + # self._effective_n_jobs should be called in the Parallel.__call__ + # thread only -- store its value in an attribute for further queries. + self._cached_effective_n_jobs = n_jobs + + backend_name = self._backend.__class__.__name__ + if n_jobs == 0: + raise RuntimeError("%s has no active worker." % backend_name) + + self._print( + f"Using backend {backend_name} with {n_jobs} concurrent workers." + ) + if hasattr(self._backend, 'start_call'): + self._backend.start_call() + + # Following flag prevents double calls to `backend.stop_call`. + self._calling = True + + iterator = iter(iterable) + pre_dispatch = self.pre_dispatch + + if pre_dispatch == 'all': + # prevent further dispatch via multiprocessing callback thread + self._original_iterator = None + self._pre_dispatch_amount = 0 + else: + self._original_iterator = iterator + if hasattr(pre_dispatch, 'endswith'): + pre_dispatch = eval_expr( + pre_dispatch.replace("n_jobs", str(n_jobs)) + ) + self._pre_dispatch_amount = pre_dispatch = int(pre_dispatch) + + # The main thread will consume the first pre_dispatch items and + # the remaining items will later be lazily dispatched by async + # callbacks upon task completions. + + # TODO: this iterator should be batch_size * n_jobs + iterator = itertools.islice(iterator, self._pre_dispatch_amount) + + # Use a caching dict for callables that are pickled with cloudpickle to + # improve performances. This cache is used only in the case of + # functions that are defined in the __main__ module, functions that + # are defined locally (inside another function) and lambda expressions. + self._pickle_cache = dict() + + output = self._get_outputs(iterator, pre_dispatch) + self._call_ref = weakref.ref(output) + + # The first item from the output is blank, but it makes the interpreter + # progress until it enters the Try/Except block of the generator and + # reach the first `yield` statement. This starts the aynchronous + # dispatch of the tasks to the workers. + next(output) + + return output if self.return_generator else list(output) + + def __repr__(self): + return '%s(n_jobs=%s)' % (self.__class__.__name__, self.n_jobs) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/pool.py b/.pythonlibs/lib/python3.10/site-packages/joblib/pool.py new file mode 100644 index 0000000..72a6baa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/pool.py @@ -0,0 +1,354 @@ +"""Custom implementation of multiprocessing.Pool with custom pickler. + +This module provides efficient ways of working with data stored in +shared memory with numpy.memmap arrays without inducing any memory +copy between the parent and child processes. + +This module should not be imported if multiprocessing is not +available as it implements subclasses of multiprocessing Pool +that uses a custom alternative to SimpleQueue. + +""" +# Author: Olivier Grisel +# Copyright: 2012, Olivier Grisel +# License: BSD 3 clause + +import copyreg +import sys +import warnings +from time import sleep + +try: + WindowsError +except NameError: + WindowsError = type(None) + +from pickle import Pickler + +from pickle import HIGHEST_PROTOCOL +from io import BytesIO + +from ._memmapping_reducer import get_memmapping_reducers +from ._memmapping_reducer import TemporaryResourcesManager +from ._multiprocessing_helpers import mp, assert_spawning + +# We need the class definition to derive from it, not the multiprocessing.Pool +# factory function +from multiprocessing.pool import Pool + +try: + import numpy as np +except ImportError: + np = None + + +############################################################################### +# Enable custom pickling in Pool queues + +class CustomizablePickler(Pickler): + """Pickler that accepts custom reducers. + + TODO python2_drop : can this be simplified ? + + HIGHEST_PROTOCOL is selected by default as this pickler is used + to pickle ephemeral datastructures for interprocess communication + hence no backward compatibility is required. + + `reducers` is expected to be a dictionary with key/values + being `(type, callable)` pairs where `callable` is a function that + give an instance of `type` will return a tuple `(constructor, + tuple_of_objects)` to rebuild an instance out of the pickled + `tuple_of_objects` as would return a `__reduce__` method. See the + standard library documentation on pickling for more details. + + """ + + # We override the pure Python pickler as its the only way to be able to + # customize the dispatch table without side effects in Python 2.7 + # to 3.2. For Python 3.3+ leverage the new dispatch_table + # feature from https://bugs.python.org/issue14166 that makes it possible + # to use the C implementation of the Pickler which is faster. + + def __init__(self, writer, reducers=None, protocol=HIGHEST_PROTOCOL): + Pickler.__init__(self, writer, protocol=protocol) + if reducers is None: + reducers = {} + if hasattr(Pickler, 'dispatch'): + # Make the dispatch registry an instance level attribute instead of + # a reference to the class dictionary under Python 2 + self.dispatch = Pickler.dispatch.copy() + else: + # Under Python 3 initialize the dispatch table with a copy of the + # default registry + self.dispatch_table = copyreg.dispatch_table.copy() + for type, reduce_func in reducers.items(): + self.register(type, reduce_func) + + def register(self, type, reduce_func): + """Attach a reducer function to a given type in the dispatch table.""" + if hasattr(Pickler, 'dispatch'): + # Python 2 pickler dispatching is not explicitly customizable. + # Let us use a closure to workaround this limitation. + def dispatcher(self, obj): + reduced = reduce_func(obj) + self.save_reduce(obj=obj, *reduced) + self.dispatch[type] = dispatcher + else: + self.dispatch_table[type] = reduce_func + + +class CustomizablePicklingQueue(object): + """Locked Pipe implementation that uses a customizable pickler. + + This class is an alternative to the multiprocessing implementation + of SimpleQueue in order to make it possible to pass custom + pickling reducers, for instance to avoid memory copy when passing + memory mapped datastructures. + + `reducers` is expected to be a dict with key / values being + `(type, callable)` pairs where `callable` is a function that, given an + instance of `type`, will return a tuple `(constructor, tuple_of_objects)` + to rebuild an instance out of the pickled `tuple_of_objects` as would + return a `__reduce__` method. + + See the standard library documentation on pickling for more details. + """ + + def __init__(self, context, reducers=None): + self._reducers = reducers + self._reader, self._writer = context.Pipe(duplex=False) + self._rlock = context.Lock() + if sys.platform == 'win32': + self._wlock = None + else: + self._wlock = context.Lock() + self._make_methods() + + def __getstate__(self): + assert_spawning(self) + return (self._reader, self._writer, self._rlock, self._wlock, + self._reducers) + + def __setstate__(self, state): + (self._reader, self._writer, self._rlock, self._wlock, + self._reducers) = state + self._make_methods() + + def empty(self): + return not self._reader.poll() + + def _make_methods(self): + self._recv = recv = self._reader.recv + racquire, rrelease = self._rlock.acquire, self._rlock.release + + def get(): + racquire() + try: + return recv() + finally: + rrelease() + + self.get = get + + if self._reducers: + def send(obj): + buffer = BytesIO() + CustomizablePickler(buffer, self._reducers).dump(obj) + self._writer.send_bytes(buffer.getvalue()) + self._send = send + else: + self._send = send = self._writer.send + if self._wlock is None: + # writes to a message oriented win32 pipe are atomic + self.put = send + else: + wlock_acquire, wlock_release = ( + self._wlock.acquire, self._wlock.release) + + def put(obj): + wlock_acquire() + try: + return send(obj) + finally: + wlock_release() + + self.put = put + + +class PicklingPool(Pool): + """Pool implementation with customizable pickling reducers. + + This is useful to control how data is shipped between processes + and makes it possible to use shared memory without useless + copies induces by the default pickling methods of the original + objects passed as arguments to dispatch. + + `forward_reducers` and `backward_reducers` are expected to be + dictionaries with key/values being `(type, callable)` pairs where + `callable` is a function that, given an instance of `type`, will return a + tuple `(constructor, tuple_of_objects)` to rebuild an instance out of the + pickled `tuple_of_objects` as would return a `__reduce__` method. + See the standard library documentation about pickling for more details. + + """ + + def __init__(self, processes=None, forward_reducers=None, + backward_reducers=None, **kwargs): + if forward_reducers is None: + forward_reducers = dict() + if backward_reducers is None: + backward_reducers = dict() + self._forward_reducers = forward_reducers + self._backward_reducers = backward_reducers + poolargs = dict(processes=processes) + poolargs.update(kwargs) + super(PicklingPool, self).__init__(**poolargs) + + def _setup_queues(self): + context = getattr(self, '_ctx', mp) + self._inqueue = CustomizablePicklingQueue(context, + self._forward_reducers) + self._outqueue = CustomizablePicklingQueue(context, + self._backward_reducers) + self._quick_put = self._inqueue._send + self._quick_get = self._outqueue._recv + + +class MemmappingPool(PicklingPool): + """Process pool that shares large arrays to avoid memory copy. + + This drop-in replacement for `multiprocessing.pool.Pool` makes + it possible to work efficiently with shared memory in a numpy + context. + + Existing instances of numpy.memmap are preserved: the child + suprocesses will have access to the same shared memory in the + original mode except for the 'w+' mode that is automatically + transformed as 'r+' to avoid zeroing the original data upon + instantiation. + + Furthermore large arrays from the parent process are automatically + dumped to a temporary folder on the filesystem such as child + processes to access their content via memmapping (file system + backed shared memory). + + Note: it is important to call the terminate method to collect + the temporary folder used by the pool. + + Parameters + ---------- + processes: int, optional + Number of worker processes running concurrently in the pool. + initializer: callable, optional + Callable executed on worker process creation. + initargs: tuple, optional + Arguments passed to the initializer callable. + temp_folder: (str, callable) optional + If str: + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + - a folder pointed by the JOBLIB_TEMP_FOLDER environment variable, + - /dev/shm if the folder exists and is writable: this is a RAMdisk + filesystem available by default on modern Linux distributions, + - the default system temporary folder that can be overridden + with TMP, TMPDIR or TEMP environment variables, typically /tmp + under Unix operating systems. + if callable: + An callable in charge of dynamically resolving a temporary folder + for memmapping large arrays. + max_nbytes int or None, optional, 1e6 by default + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. + Use None to disable memmapping of large arrays. + mmap_mode: {'r+', 'r', 'w+', 'c'} + Memmapping mode for numpy arrays passed to workers. + See 'max_nbytes' parameter documentation for more details. + forward_reducers: dictionary, optional + Reducers used to pickle objects passed from master to worker + processes: see below. + backward_reducers: dictionary, optional + Reducers used to pickle return values from workers back to the + master process. + verbose: int, optional + Make it possible to monitor how the communication of numpy arrays + with the subprocess is handled (pickling or memmapping) + prewarm: bool or str, optional, "auto" by default. + If True, force a read on newly memmapped array to make sure that OS + pre-cache it in memory. This can be useful to avoid concurrent disk + access when the same data array is passed to different worker + processes. If "auto" (by default), prewarm is set to True, unless the + Linux shared memory partition /dev/shm is available and used as temp + folder. + + `forward_reducers` and `backward_reducers` are expected to be + dictionaries with key/values being `(type, callable)` pairs where + `callable` is a function that give an instance of `type` will return + a tuple `(constructor, tuple_of_objects)` to rebuild an instance out + of the pickled `tuple_of_objects` as would return a `__reduce__` + method. See the standard library documentation on pickling for more + details. + + """ + + def __init__(self, processes=None, temp_folder=None, max_nbytes=1e6, + mmap_mode='r', forward_reducers=None, backward_reducers=None, + verbose=0, context_id=None, prewarm=False, **kwargs): + + if context_id is not None: + warnings.warn('context_id is deprecated and ignored in joblib' + ' 0.9.4 and will be removed in 0.11', + DeprecationWarning) + + manager = TemporaryResourcesManager(temp_folder) + self._temp_folder_manager = manager + + # The usage of a temp_folder_resolver over a simple temp_folder is + # superfluous for multiprocessing pools, as they don't get reused, see + # get_memmapping_executor for more details. We still use it for code + # simplicity. + forward_reducers, backward_reducers = \ + get_memmapping_reducers( + temp_folder_resolver=manager.resolve_temp_folder_name, + max_nbytes=max_nbytes, mmap_mode=mmap_mode, + forward_reducers=forward_reducers, + backward_reducers=backward_reducers, verbose=verbose, + unlink_on_gc_collect=False, prewarm=prewarm) + + poolargs = dict( + processes=processes, + forward_reducers=forward_reducers, + backward_reducers=backward_reducers) + poolargs.update(kwargs) + super(MemmappingPool, self).__init__(**poolargs) + + def terminate(self): + n_retries = 10 + for i in range(n_retries): + try: + super(MemmappingPool, self).terminate() + break + except OSError as e: + if isinstance(e, WindowsError): + # Workaround occasional "[Error 5] Access is denied" issue + # when trying to terminate a process under windows. + sleep(0.1) + if i + 1 == n_retries: + warnings.warn("Failed to terminate worker processes in" + " multiprocessing pool: %r" % e) + + # Clean up the temporary resources as the workers should now be off. + self._temp_folder_manager._clean_temporary_resources() + + @property + def _temp_folder(self): + # Legacy property in tests. could be removed if we refactored the + # memmapping tests. SHOULD ONLY BE USED IN TESTS! + # We cache this property because it is called late in the tests - at + # this point, all context have been unregistered, and + # resolve_temp_folder_name raises an error. + if getattr(self, '_cached_temp_folder', None) is not None: + return self._cached_temp_folder + else: + self._cached_temp_folder = self._temp_folder_manager.resolve_temp_folder_name() # noqa + return self._cached_temp_folder diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2d79ee2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/common.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/common.cpython-310.pyc new file mode 100644 index 0000000..6054293 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/common.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_backports.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_backports.cpython-310.pyc new file mode 100644 index 0000000..b8c31f7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_backports.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_cloudpickle_wrapper.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_cloudpickle_wrapper.cpython-310.pyc new file mode 100644 index 0000000..9deb32e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_cloudpickle_wrapper.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_config.cpython-310.pyc new file mode 100644 index 0000000..091803a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_dask.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_dask.cpython-310.pyc new file mode 100644 index 0000000..010280b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_dask.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_disk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_disk.cpython-310.pyc new file mode 100644 index 0000000..66350f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_disk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect.cpython-310.pyc new file mode 100644 index 0000000..0e54b10 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect_special_encoding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect_special_encoding.cpython-310.pyc new file mode 100644 index 0000000..c293c66 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_func_inspect_special_encoding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_hashing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_hashing.cpython-310.pyc new file mode 100644 index 0000000..5291809 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_hashing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_init.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_init.cpython-310.pyc new file mode 100644 index 0000000..abe3730 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_init.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_logger.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_logger.cpython-310.pyc new file mode 100644 index 0000000..f0a530d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_logger.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memmapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memmapping.cpython-310.pyc new file mode 100644 index 0000000..d86a2e0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memmapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory.cpython-310.pyc new file mode 100644 index 0000000..1b0a263 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory_async.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory_async.cpython-310.pyc new file mode 100644 index 0000000..2b6677b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_memory_async.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_missing_multiprocessing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_missing_multiprocessing.cpython-310.pyc new file mode 100644 index 0000000..6ca4bfc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_missing_multiprocessing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_module.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_module.cpython-310.pyc new file mode 100644 index 0000000..877b113 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_module.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle.cpython-310.pyc new file mode 100644 index 0000000..92f399f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_compat.cpython-310.pyc new file mode 100644 index 0000000..616380f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_utils.cpython-310.pyc new file mode 100644 index 0000000..0f455e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_numpy_pickle_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_parallel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_parallel.cpython-310.pyc new file mode 100644 index 0000000..7a2f114 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_parallel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_store_backends.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_store_backends.cpython-310.pyc new file mode 100644 index 0000000..ddb8af2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_store_backends.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_testing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_testing.cpython-310.pyc new file mode 100644 index 0000000..3876379 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_testing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_utils.cpython-310.pyc new file mode 100644 index 0000000..4d328e7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/test_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/testutils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/testutils.cpython-310.pyc new file mode 100644 index 0000000..dfbf291 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/__pycache__/testutils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/common.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/common.py new file mode 100644 index 0000000..b0ca0c6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/common.py @@ -0,0 +1,84 @@ +""" +Small utilities for testing. +""" +import os +import gc +import sys + +from joblib._multiprocessing_helpers import mp +from joblib.testing import SkipTest, skipif + +try: + import lz4 +except ImportError: + lz4 = None + +IS_PYPY = hasattr(sys, "pypy_version_info") + +# A decorator to run tests only when numpy is available +try: + import numpy as np + + def with_numpy(func): + """A decorator to skip tests requiring numpy.""" + return func + +except ImportError: + def with_numpy(func): + """A decorator to skip tests requiring numpy.""" + def my_func(): + raise SkipTest('Test requires numpy') + return my_func + np = None + +# TODO: Turn this back on after refactoring yield based tests in test_hashing +# with_numpy = skipif(not np, reason='Test requires numpy.') + +# we use memory_profiler library for memory consumption checks +try: + from memory_profiler import memory_usage + + def with_memory_profiler(func): + """A decorator to skip tests requiring memory_profiler.""" + return func + + def memory_used(func, *args, **kwargs): + """Compute memory usage when executing func.""" + gc.collect() + mem_use = memory_usage((func, args, kwargs), interval=.001) + return max(mem_use) - min(mem_use) + +except ImportError: + def with_memory_profiler(func): + """A decorator to skip tests requiring memory_profiler.""" + def dummy_func(): + raise SkipTest('Test requires memory_profiler.') + return dummy_func + + memory_usage = memory_used = None + + +def force_gc_pypy(): + # The gc in pypy can be delayed. Force it to test the behavior when it + # will eventually be collected. + if IS_PYPY: + # Run gc.collect() twice to make sure the weakref is collected, as + # mentionned in the pypy doc: + # https://doc.pypy.org/en/latest/config/objspace.usemodules._weakref.html + import gc + gc.collect() + gc.collect() + + +with_multiprocessing = skipif( + mp is None, reason='Needs multiprocessing to run.') + + +with_dev_shm = skipif( + not os.path.exists('/dev/shm'), + reason='This test requires a large /dev/shm shared memory fs.') + +with_lz4 = skipif(lz4 is None, reason='Needs lz4 compression to run') + +without_lz4 = skipif( + lz4 is not None, reason='Needs lz4 not being installed to run') diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__init__.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..72929e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/create_numpy_pickle.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/create_numpy_pickle.cpython-310.pyc new file mode 100644 index 0000000..55ab542 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/__pycache__/create_numpy_pickle.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/create_numpy_pickle.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/create_numpy_pickle.py new file mode 100644 index 0000000..ba903d6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/data/create_numpy_pickle.py @@ -0,0 +1,95 @@ +""" +This script is used to generate test data for joblib/test/test_numpy_pickle.py +""" + +import sys +import re + +# pytest needs to be able to import this module even when numpy is +# not installed +try: + import numpy as np +except ImportError: + np = None + +import joblib + + +def get_joblib_version(joblib_version=joblib.__version__): + """Normalize joblib version by removing suffix. + + >>> get_joblib_version('0.8.4') + '0.8.4' + >>> get_joblib_version('0.8.4b1') + '0.8.4' + >>> get_joblib_version('0.9.dev0') + '0.9' + """ + matches = [re.match(r'(\d+).*', each) + for each in joblib_version.split('.')] + return '.'.join([m.group(1) for m in matches if m is not None]) + + +def write_test_pickle(to_pickle, args): + kwargs = {} + compress = args.compress + method = args.method + joblib_version = get_joblib_version() + py_version = '{0[0]}{0[1]}'.format(sys.version_info) + numpy_version = ''.join(np.__version__.split('.')[:2]) + + # The game here is to generate the right filename according to the options. + body = '_compressed' if (compress and method == 'zlib') else '' + if compress: + if method == 'zlib': + kwargs['compress'] = True + extension = '.gz' + else: + kwargs['compress'] = (method, 3) + extension = '.pkl.{}'.format(method) + if args.cache_size: + kwargs['cache_size'] = 0 + body += '_cache_size' + else: + extension = '.pkl' + + pickle_filename = 'joblib_{}{}_pickle_py{}_np{}{}'.format( + joblib_version, body, py_version, numpy_version, extension) + + try: + joblib.dump(to_pickle, pickle_filename, **kwargs) + except Exception as e: + # With old python version (=< 3.3.), we can arrive there when + # dumping compressed pickle with LzmaFile. + print("Error: cannot generate file '{}' with arguments '{}'. " + "Error was: {}".format(pickle_filename, kwargs, e)) + else: + print("File '{}' generated successfully.".format(pickle_filename)) + + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser(description="Joblib pickle data " + "generator.") + parser.add_argument('--cache_size', action="store_true", + help="Force creation of companion numpy " + "files for pickled arrays.") + parser.add_argument('--compress', action="store_true", + help="Generate compress pickles.") + parser.add_argument('--method', type=str, default='zlib', + choices=['zlib', 'gzip', 'bz2', 'xz', 'lzma', 'lz4'], + help="Set compression method.") + # We need to be specific about dtypes in particular endianness + # because the pickles can be generated on one architecture and + # the tests run on another one. See + # https://github.com/joblib/joblib/issues/279. + to_pickle = [np.arange(5, dtype=np.dtype(' 0 + + +@with_numpy +@with_multiprocessing +def test_parallel_config_params_explicit_set(tmpdir): + with parallel_config(n_jobs=3, max_nbytes=1, temp_folder=tmpdir): + with Parallel(n_jobs=2, prefer="processes", max_nbytes='1M') as p: + assert isinstance(p._backend, LokyBackend) + assert p.n_jobs == 2 + + # Checks that memmapping is disabled + with raises(TypeError, match="Expected np.memmap instance"): + p(delayed(check_memmap)(a) for a in [np.random.random(10)] * 2) + + +@parametrize("param", ["prefer", "require"]) +def test_parallel_config_bad_params(param): + # Check that an error is raised when setting a wrong backend + # hint or constraint + with raises(ValueError, match=f"{param}=wrong is not a valid"): + with parallel_config(**{param: "wrong"}): + Parallel() + + +def test_parallel_config_constructor_params(): + # Check that an error is raised when backend is None + # but backend constructor params are given + with raises(ValueError, match="only supported when backend is not None"): + with parallel_config(inner_max_num_threads=1): + pass + + with raises(ValueError, match="only supported when backend is not None"): + with parallel_config(backend_param=1): + pass + + +def test_parallel_config_nested(): + # Check that nested configuration retrieves the info from the + # parent config and do not reset them. + + with parallel_config(n_jobs=2): + p = Parallel() + assert isinstance(p._backend, BACKENDS[DEFAULT_BACKEND]) + assert p.n_jobs == 2 + + with parallel_config(backend='threading'): + with parallel_config(n_jobs=2): + p = Parallel() + assert isinstance(p._backend, ThreadingBackend) + assert p.n_jobs == 2 + + with parallel_config(verbose=100): + with parallel_config(n_jobs=2): + p = Parallel() + assert p.verbose == 100 + assert p.n_jobs == 2 + + +@with_numpy +@with_multiprocessing +@parametrize('backend', ['multiprocessing', 'threading', + MultiprocessingBackend(), ThreadingBackend()]) +@parametrize("context", [parallel_config, parallel_backend]) +def test_threadpool_limitation_in_child_context_error(context, backend): + + with raises(AssertionError, match=r"does not acc.*inner_max_num_threads"): + context(backend, inner_max_num_threads=1) + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_parallel_n_jobs_none(context): + # Check that n_jobs=None is interpreted as "unset" in Parallel + # non regression test for #1473 + with context(backend="threading", n_jobs=2): + with Parallel(n_jobs=None) as p: + assert p.n_jobs == 2 + + with context(backend="threading"): + default_n_jobs = Parallel().n_jobs + with Parallel(n_jobs=None) as p: + assert p.n_jobs == default_n_jobs + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_parallel_config_n_jobs_none(context): + # Check that n_jobs=None is interpreted as "explicitly set" in + # parallel_(config/backend) + # non regression test for #1473 + with context(backend="threading", n_jobs=2): + with context(backend="threading", n_jobs=None): + # n_jobs=None resets n_jobs to backend's default + with Parallel() as p: + assert p.n_jobs == 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_dask.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_dask.py new file mode 100644 index 0000000..aebe655 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_dask.py @@ -0,0 +1,499 @@ +from __future__ import print_function, division, absolute_import +import os +import warnings + +import pytest +from random import random +from uuid import uuid4 +from time import sleep + +from .. import Parallel, delayed, parallel_config +from ..parallel import ThreadingBackend, AutoBatchingMixin +from .._dask import DaskDistributedBackend + +distributed = pytest.importorskip('distributed') +dask = pytest.importorskip('dask') + +# These imports need to be after the pytest.importorskip hence the noqa: E402 +from distributed import Client, LocalCluster, get_client # noqa: E402 +from distributed.metrics import time # noqa: E402 +# Note: pytest requires to manually import all fixtures used in the test +# and their dependencies. +from distributed.utils_test import cluster, inc, cleanup # noqa: E402, F401 + + +def noop(*args, **kwargs): + pass + + +def slow_raise_value_error(condition, duration=0.05): + sleep(duration) + if condition: + raise ValueError("condition evaluated to True") + + +def count_events(event_name, client): + worker_events = client.run(lambda dask_worker: dask_worker.log) + event_counts = {} + for w, events in worker_events.items(): + event_counts[w] = len([event for event in list(events) + if event[1] == event_name]) + return event_counts + + +def test_simple(loop): + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask'): + seq = Parallel()(delayed(inc)(i) for i in range(10)) + assert seq == [inc(i) for i in range(10)] + + with pytest.raises(ValueError): + Parallel()(delayed(slow_raise_value_error)(i == 3) + for i in range(10)) + + seq = Parallel()(delayed(inc)(i) for i in range(10)) + assert seq == [inc(i) for i in range(10)] + + +def test_dask_backend_uses_autobatching(loop): + assert (DaskDistributedBackend.compute_batch_size + is AutoBatchingMixin.compute_batch_size) + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask'): + with Parallel() as parallel: + # The backend should be initialized with a default + # batch size of 1: + backend = parallel._backend + assert isinstance(backend, DaskDistributedBackend) + assert backend.parallel is parallel + assert backend._effective_batch_size == 1 + + # Launch many short tasks that should trigger + # auto-batching: + parallel( + delayed(lambda: None)() + for _ in range(int(1e4)) + ) + assert backend._effective_batch_size > 10 + + +def random2(): + return random() + + +def test_dont_assume_function_purity(loop): + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask'): + x, y = Parallel()(delayed(random2)() for i in range(2)) + assert x != y + + +@pytest.mark.parametrize("mixed", [True, False]) +def test_dask_funcname(loop, mixed): + from joblib._dask import Batch + if not mixed: + tasks = [delayed(inc)(i) for i in range(4)] + batch_repr = 'batch_of_inc_4_calls' + else: + tasks = [ + delayed(abs)(i) if i % 2 else delayed(inc)(i) for i in range(4) + ] + batch_repr = 'mixed_batch_of_inc_4_calls' + + assert repr(Batch(tasks)) == batch_repr + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: + with parallel_config(backend='dask'): + _ = Parallel(batch_size=2, pre_dispatch='all')(tasks) + + def f(dask_scheduler): + return list(dask_scheduler.transition_log) + batch_repr = batch_repr.replace('4', '2') + log = client.run_on_scheduler(f) + assert all('batch_of_inc' in tup[0] for tup in log) + + +def test_no_undesired_distributed_cache_hit(): + # Dask has a pickle cache for callables that are called many times. Because + # the dask backends used to wrap both the functions and the arguments + # under instances of the Batch callable class this caching mechanism could + # lead to bugs as described in: https://github.com/joblib/joblib/pull/1055 + # The joblib-dask backend has been refactored to avoid bundling the + # arguments as an attribute of the Batch instance to avoid this problem. + # This test serves as non-regression problem. + + # Use a large number of input arguments to give the AutoBatchingMixin + # enough tasks to kick-in. + lists = [[] for _ in range(100)] + np = pytest.importorskip('numpy') + X = np.arange(int(1e6)) + + def isolated_operation(list_, data=None): + if data is not None: + np.testing.assert_array_equal(data, X) + list_.append(uuid4().hex) + return list_ + + cluster = LocalCluster(n_workers=1, threads_per_worker=2) + client = Client(cluster) + try: + with parallel_config(backend='dask'): + # dispatches joblib.parallel.BatchedCalls + res = Parallel()( + delayed(isolated_operation)(list_) for list_ in lists + ) + + # The original arguments should not have been mutated as the mutation + # happens in the dask worker process. + assert lists == [[] for _ in range(100)] + + # Here we did not pass any large numpy array as argument to + # isolated_operation so no scattering event should happen under the + # hood. + counts = count_events('receive-from-scatter', client) + assert sum(counts.values()) == 0 + assert all([len(r) == 1 for r in res]) + + with parallel_config(backend='dask'): + # Append a large array which will be scattered by dask, and + # dispatch joblib._dask.Batch + res = Parallel()( + delayed(isolated_operation)(list_, data=X) for list_ in lists + ) + + # This time, auto-scattering should have kicked it. + counts = count_events('receive-from-scatter', client) + assert sum(counts.values()) > 0 + assert all([len(r) == 1 for r in res]) + finally: + client.close(timeout=30) + cluster.close(timeout=30) + + +class CountSerialized(object): + def __init__(self, x): + self.x = x + self.count = 0 + + def __add__(self, other): + return self.x + getattr(other, 'x', other) + + __radd__ = __add__ + + def __reduce__(self): + self.count += 1 + return (CountSerialized, (self.x,)) + + +def add5(a, b, c, d=0, e=0): + return a + b + c + d + e + + +def test_manual_scatter(loop): + x = CountSerialized(1) + y = CountSerialized(2) + z = CountSerialized(3) + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask', scatter=[x, y]): + f = delayed(add5) + tasks = [f(x, y, z, d=4, e=5), + f(x, z, y, d=5, e=4), + f(y, x, z, d=x, e=5), + f(z, z, x, d=z, e=y)] + expected = [func(*args, **kwargs) + for func, args, kwargs in tasks] + results = Parallel()(tasks) + + # Scatter must take a list/tuple + with pytest.raises(TypeError): + with parallel_config(backend='dask', loop=loop, scatter=1): + pass + + assert results == expected + + # Scattered variables only serialized once + assert x.count == 1 + assert y.count == 1 + # Depending on the version of distributed, the unscattered z variable + # is either pickled 4 or 6 times, possibly because of the memoization + # of objects that appear several times in the arguments of a delayed + # task. + assert z.count in (4, 6) + + +# When the same IOLoop is used for multiple clients in a row, use +# loop_in_thread instead of loop to prevent the Client from closing it. See +# dask/distributed #4112 +def test_auto_scatter(loop_in_thread): + np = pytest.importorskip('numpy') + data1 = np.ones(int(1e4), dtype=np.uint8) + data2 = np.ones(int(1e4), dtype=np.uint8) + data_to_process = ([data1] * 3) + ([data2] * 3) + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop_in_thread) as client: + with parallel_config(backend='dask'): + # Passing the same data as arg and kwarg triggers a single + # scatter operation whose result is reused. + Parallel()(delayed(noop)(data, data, i, opt=data) + for i, data in enumerate(data_to_process)) + # By default large array are automatically scattered with + # broadcast=1 which means that one worker must directly receive + # the data from the scatter operation once. + counts = count_events('receive-from-scatter', client) + assert counts[a['address']] + counts[b['address']] == 2 + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop_in_thread) as client: + with parallel_config(backend='dask'): + Parallel()(delayed(noop)(data1[:3], i) for i in range(5)) + # Small arrays are passed within the task definition without going + # through a scatter operation. + counts = count_events('receive-from-scatter', client) + assert counts[a['address']] == 0 + assert counts[b['address']] == 0 + + +@pytest.mark.parametrize("retry_no", list(range(2))) +def test_nested_scatter(loop, retry_no): + + np = pytest.importorskip('numpy') + + NUM_INNER_TASKS = 10 + NUM_OUTER_TASKS = 10 + + def my_sum(x, i, j): + return np.sum(x) + + def outer_function_joblib(array, i): + client = get_client() # noqa + with parallel_config(backend="dask"): + results = Parallel()( + delayed(my_sum)(array[j:], i, j) for j in range( + NUM_INNER_TASKS) + ) + return sum(results) + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as _: + with parallel_config(backend="dask"): + my_array = np.ones(10000) + _ = Parallel()( + delayed(outer_function_joblib)( + my_array[i:], i) for i in range(NUM_OUTER_TASKS) + ) + + +def test_nested_backend_context_manager(loop_in_thread): + def get_nested_pids(): + pids = set(Parallel(n_jobs=2)(delayed(os.getpid)() for _ in range(2))) + pids |= set(Parallel(n_jobs=2)(delayed(os.getpid)() for _ in range(2))) + return pids + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop_in_thread) as client: + with parallel_config(backend='dask'): + pid_groups = Parallel(n_jobs=2)( + delayed(get_nested_pids)() + for _ in range(10) + ) + for pid_group in pid_groups: + assert len(set(pid_group)) <= 2 + + # No deadlocks + with Client(s['address'], loop=loop_in_thread) as client: # noqa: F841 + with parallel_config(backend='dask'): + pid_groups = Parallel(n_jobs=2)( + delayed(get_nested_pids)() + for _ in range(10) + ) + for pid_group in pid_groups: + assert len(set(pid_group)) <= 2 + + +def test_nested_backend_context_manager_implicit_n_jobs(loop): + # Check that Parallel with no explicit n_jobs value automatically selects + # all the dask workers, including in nested calls. + + def _backend_type(p): + return p._backend.__class__.__name__ + + def get_nested_implicit_n_jobs(): + with Parallel() as p: + return _backend_type(p), p.n_jobs + + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask'): + with Parallel() as p: + assert _backend_type(p) == "DaskDistributedBackend" + assert p.n_jobs == -1 + all_nested_n_jobs = p( + delayed(get_nested_implicit_n_jobs)() + for _ in range(2) + ) + for backend_type, nested_n_jobs in all_nested_n_jobs: + assert backend_type == "DaskDistributedBackend" + assert nested_n_jobs == -1 + + +def test_errors(loop): + with pytest.raises(ValueError) as info: + with parallel_config(backend='dask'): + pass + + assert "create a dask client" in str(info.value).lower() + + +def test_correct_nested_backend(loop): + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + # No requirement, should be us + with parallel_config(backend='dask'): + result = Parallel(n_jobs=2)( + delayed(outer)(nested_require=None) for _ in range(1)) + assert isinstance(result[0][0][0], DaskDistributedBackend) + + # Require threads, should be threading + with parallel_config(backend='dask'): + result = Parallel(n_jobs=2)( + delayed(outer)(nested_require='sharedmem') + for _ in range(1)) + assert isinstance(result[0][0][0], ThreadingBackend) + + +def outer(nested_require): + return Parallel(n_jobs=2, prefer='threads')( + delayed(middle)(nested_require) for _ in range(1) + ) + + +def middle(require): + return Parallel(n_jobs=2, require=require)( + delayed(inner)() for _ in range(1) + ) + + +def inner(): + return Parallel()._backend + + +def test_secede_with_no_processes(loop): + # https://github.com/dask/distributed/issues/1775 + with Client(loop=loop, processes=False, set_as_default=True): + with parallel_config(backend='dask'): + Parallel(n_jobs=4)(delayed(id)(i) for i in range(2)) + + +def _worker_address(_): + from distributed import get_worker + return get_worker().address + + +def test_dask_backend_keywords(loop): + with cluster() as (s, [a, b]): + with Client(s['address'], loop=loop) as client: # noqa: F841 + with parallel_config(backend='dask', workers=a['address']): + seq = Parallel()( + delayed(_worker_address)(i) for i in range(10)) + assert seq == [a['address']] * 10 + + with parallel_config(backend='dask', workers=b['address']): + seq = Parallel()( + delayed(_worker_address)(i) for i in range(10)) + assert seq == [b['address']] * 10 + + +def test_scheduler_tasks_cleanup(loop): + with Client(processes=False, loop=loop) as client: + with parallel_config(backend='dask'): + Parallel()(delayed(inc)(i) for i in range(10)) + + start = time() + while client.cluster.scheduler.tasks: + sleep(0.01) + assert time() < start + 5 + + assert not client.futures + + +@pytest.mark.parametrize("cluster_strategy", ["adaptive", "late_scaling"]) +@pytest.mark.skipif( + distributed.__version__ <= '2.1.1' and distributed.__version__ >= '1.28.0', + reason="distributed bug - https://github.com/dask/distributed/pull/2841") +def test_wait_for_workers(cluster_strategy): + cluster = LocalCluster(n_workers=0, processes=False, threads_per_worker=2) + client = Client(cluster) + if cluster_strategy == "adaptive": + cluster.adapt(minimum=0, maximum=2) + elif cluster_strategy == "late_scaling": + # Tell the cluster to start workers but this is a non-blocking call + # and new workers might take time to connect. In this case the Parallel + # call should wait for at least one worker to come up before starting + # to schedule work. + cluster.scale(2) + try: + with parallel_config(backend='dask'): + # The following should wait a bit for at least one worker to + # become available. + Parallel()(delayed(inc)(i) for i in range(10)) + finally: + client.close() + cluster.close() + + +def test_wait_for_workers_timeout(): + # Start a cluster with 0 worker: + cluster = LocalCluster(n_workers=0, processes=False, threads_per_worker=2) + client = Client(cluster) + try: + with parallel_config(backend='dask', wait_for_workers_timeout=0.1): + # Short timeout: DaskDistributedBackend + msg = "DaskDistributedBackend has no worker after 0.1 seconds." + with pytest.raises(TimeoutError, match=msg): + Parallel()(delayed(inc)(i) for i in range(10)) + + with parallel_config(backend='dask', wait_for_workers_timeout=0): + # No timeout: fallback to generic joblib failure: + msg = "DaskDistributedBackend has no active worker" + with pytest.raises(RuntimeError, match=msg): + Parallel()(delayed(inc)(i) for i in range(10)) + finally: + client.close() + cluster.close() + + +@pytest.mark.parametrize("backend", ["loky", "multiprocessing"]) +def test_joblib_warning_inside_dask_daemonic_worker(backend): + cluster = LocalCluster(n_workers=2) + client = Client(cluster) + try: + + def func_using_joblib_parallel(): + # Somehow trying to check the warning type here (e.g. with + # pytest.warns(UserWarning)) make the test hang. Work-around: + # return the warning record to the client and the warning check is + # done client-side. + with warnings.catch_warnings(record=True) as record: + Parallel(n_jobs=2, backend=backend)( + delayed(inc)(i) for i in range(10)) + + return record + + fut = client.submit(func_using_joblib_parallel) + record = fut.result() + + assert len(record) == 1 + warning = record[0].message + assert isinstance(warning, UserWarning) + assert "distributed.worker.daemon" in str(warning) + finally: + client.close(timeout=30) + cluster.close(timeout=30) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_disk.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_disk.py new file mode 100644 index 0000000..b825a8b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_disk.py @@ -0,0 +1,71 @@ +""" +Unit tests for the disk utilities. +""" + +# Authors: Gael Varoquaux +# Lars Buitinck +# Copyright (c) 2010 Gael Varoquaux +# License: BSD Style, 3 clauses. + +from __future__ import with_statement +import array +import os + +from joblib.disk import disk_used, memstr_to_bytes, mkdirp, rm_subdirs +from joblib.testing import parametrize, raises + +############################################################################### + + +def test_disk_used(tmpdir): + cachedir = tmpdir.strpath + # Not write a file that is 1M big in this directory, and check the + # size. The reason we use such a big file is that it makes us robust + # to errors due to block allocation. + a = array.array('i') + sizeof_i = a.itemsize + target_size = 1024 + n = int(target_size * 1024 / sizeof_i) + a = array.array('i', n * (1,)) + with open(os.path.join(cachedir, 'test'), 'wb') as output: + a.tofile(output) + assert disk_used(cachedir) >= target_size + assert disk_used(cachedir) < target_size + 12 + + +@parametrize('text,value', + [('80G', 80 * 1024 ** 3), + ('1.4M', int(1.4 * 1024 ** 2)), + ('120M', 120 * 1024 ** 2), + ('53K', 53 * 1024)]) +def test_memstr_to_bytes(text, value): + assert memstr_to_bytes(text) == value + + +@parametrize('text,exception,regex', + [('fooG', ValueError, r'Invalid literal for size.*fooG.*'), + ('1.4N', ValueError, r'Invalid literal for size.*1.4N.*')]) +def test_memstr_to_bytes_exception(text, exception, regex): + with raises(exception) as excinfo: + memstr_to_bytes(text) + assert excinfo.match(regex) + + +def test_mkdirp(tmpdir): + mkdirp(os.path.join(tmpdir.strpath, 'ham')) + mkdirp(os.path.join(tmpdir.strpath, 'ham')) + mkdirp(os.path.join(tmpdir.strpath, 'spam', 'spam')) + + # Not all OSErrors are ignored + with raises(OSError): + mkdirp('') + + +def test_rm_subdirs(tmpdir): + sub_path = os.path.join(tmpdir.strpath, "am", "stram") + full_path = os.path.join(sub_path, "gram") + mkdirp(os.path.join(full_path)) + + rm_subdirs(sub_path) + assert os.path.exists(sub_path) + assert not os.path.exists(full_path) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect.py new file mode 100644 index 0000000..dba237d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect.py @@ -0,0 +1,310 @@ +""" +Test the func_inspect module. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import functools + +from joblib.func_inspect import filter_args, get_func_name, get_func_code +from joblib.func_inspect import _clean_win_chars, format_signature +from joblib.memory import Memory +from joblib.test.common import with_numpy +from joblib.testing import fixture, parametrize, raises + + +############################################################################### +# Module-level functions and fixture, for tests +def f(x, y=0): + pass + + +def g(x): + pass + + +def h(x, y=0, *args, **kwargs): + pass + + +def i(x=1): + pass + + +def j(x, y, **kwargs): + pass + + +def k(*args, **kwargs): + pass + + +def m1(x, *, y): + pass + + +def m2(x, *, y, z=3): + pass + + +@fixture(scope='module') +def cached_func(tmpdir_factory): + # Create a Memory object to test decorated functions. + # We should be careful not to call the decorated functions, so that + # cache directories are not created in the temp dir. + cachedir = tmpdir_factory.mktemp("joblib_test_func_inspect") + mem = Memory(cachedir.strpath) + + @mem.cache + def cached_func_inner(x): + return x + + return cached_func_inner + + +class Klass(object): + + def f(self, x): + return x + + +############################################################################### +# Tests + +@parametrize('func,args,filtered_args', + [(f, [[], (1, )], {'x': 1, 'y': 0}), + (f, [['x'], (1, )], {'y': 0}), + (f, [['y'], (0, )], {'x': 0}), + (f, [['y'], (0, ), {'y': 1}], {'x': 0}), + (f, [['x', 'y'], (0, )], {}), + (f, [[], (0,), {'y': 1}], {'x': 0, 'y': 1}), + (f, [['y'], (), {'x': 2, 'y': 1}], {'x': 2}), + (g, [[], (), {'x': 1}], {'x': 1}), + (i, [[], (2, )], {'x': 2})]) +def test_filter_args(func, args, filtered_args): + assert filter_args(func, *args) == filtered_args + + +def test_filter_args_method(): + obj = Klass() + assert filter_args(obj.f, [], (1, )) == {'x': 1, 'self': obj} + + +@parametrize('func,args,filtered_args', + [(h, [[], (1, )], + {'x': 1, 'y': 0, '*': [], '**': {}}), + (h, [[], (1, 2, 3, 4)], + {'x': 1, 'y': 2, '*': [3, 4], '**': {}}), + (h, [[], (1, 25), {'ee': 2}], + {'x': 1, 'y': 25, '*': [], '**': {'ee': 2}}), + (h, [['*'], (1, 2, 25), {'ee': 2}], + {'x': 1, 'y': 2, '**': {'ee': 2}})]) +def test_filter_varargs(func, args, filtered_args): + assert filter_args(func, *args) == filtered_args + + +test_filter_kwargs_extra_params = [ + (m1, [[], (1,), {'y': 2}], {'x': 1, 'y': 2}), + (m2, [[], (1,), {'y': 2}], {'x': 1, 'y': 2, 'z': 3}) +] + + +@parametrize('func,args,filtered_args', + [(k, [[], (1, 2), {'ee': 2}], + {'*': [1, 2], '**': {'ee': 2}}), + (k, [[], (3, 4)], + {'*': [3, 4], '**': {}})] + + test_filter_kwargs_extra_params) +def test_filter_kwargs(func, args, filtered_args): + assert filter_args(func, *args) == filtered_args + + +def test_filter_args_2(): + assert (filter_args(j, [], (1, 2), {'ee': 2}) == + {'x': 1, 'y': 2, '**': {'ee': 2}}) + + ff = functools.partial(f, 1) + # filter_args has to special-case partial + assert filter_args(ff, [], (1, )) == {'*': [1], '**': {}} + assert filter_args(ff, ['y'], (1, )) == {'*': [1], '**': {}} + + +@parametrize('func,funcname', [(f, 'f'), (g, 'g'), + (cached_func, 'cached_func')]) +def test_func_name(func, funcname): + # Check that we are not confused by decoration + # here testcase 'cached_func' is the function itself + assert get_func_name(func)[1] == funcname + + +def test_func_name_on_inner_func(cached_func): + # Check that we are not confused by decoration + # here testcase 'cached_func' is the 'cached_func_inner' function + # returned by 'cached_func' fixture + assert get_func_name(cached_func)[1] == 'cached_func_inner' + + +def test_func_name_collision_on_inner_func(): + # Check that two functions defining and caching an inner function + # with the same do not cause (module, name) collision + def f(): + def inner_func(): + return # pragma: no cover + return get_func_name(inner_func) + + def g(): + def inner_func(): + return # pragma: no cover + return get_func_name(inner_func) + + module, name = f() + other_module, other_name = g() + + assert name == other_name + assert module != other_module + + +def test_func_inspect_errors(): + # Check that func_inspect is robust and will work on weird objects + assert get_func_name('a'.lower)[-1] == 'lower' + assert get_func_code('a'.lower)[1:] == (None, -1) + ff = lambda x: x # noqa: E731 + assert get_func_name(ff, win_characters=False)[-1] == '' + assert get_func_code(ff)[1] == __file__.replace('.pyc', '.py') + # Simulate a function defined in __main__ + ff.__module__ = '__main__' + assert get_func_name(ff, win_characters=False)[-1] == '' + assert get_func_code(ff)[1] == __file__.replace('.pyc', '.py') + + +def func_with_kwonly_args(a, b, *, kw1='kw1', kw2='kw2'): + pass + + +def func_with_signature(a: int, b: int) -> None: + pass + + +def test_filter_args_edge_cases(): + assert ( + filter_args(func_with_kwonly_args, [], (1, 2), + {'kw1': 3, 'kw2': 4}) == + {'a': 1, 'b': 2, 'kw1': 3, 'kw2': 4}) + + # filter_args doesn't care about keyword-only arguments so you + # can pass 'kw1' into *args without any problem + with raises(ValueError) as excinfo: + filter_args(func_with_kwonly_args, [], (1, 2, 3), {'kw2': 2}) + excinfo.match("Keyword-only parameter 'kw1' was passed as positional " + "parameter") + + assert ( + filter_args(func_with_kwonly_args, ['b', 'kw2'], (1, 2), + {'kw1': 3, 'kw2': 4}) == + {'a': 1, 'kw1': 3}) + + assert (filter_args(func_with_signature, ['b'], (1, 2)) == {'a': 1}) + + +def test_bound_methods(): + """ Make sure that calling the same method on two different instances + of the same class does resolv to different signatures. + """ + a = Klass() + b = Klass() + assert filter_args(a.f, [], (1, )) != filter_args(b.f, [], (1, )) + + +@parametrize('exception,regex,func,args', + [(ValueError, 'ignore_lst must be a list of parameters to ignore', + f, ['bar', (None, )]), + (ValueError, r'Ignore list: argument \'(.*)\' is not defined', + g, [['bar'], (None, )]), + (ValueError, 'Wrong number of arguments', + h, [[]])]) +def test_filter_args_error_msg(exception, regex, func, args): + """ Make sure that filter_args returns decent error messages, for the + sake of the user. + """ + with raises(exception) as excinfo: + filter_args(func, *args) + excinfo.match(regex) + + +def test_filter_args_no_kwargs_mutation(): + """None-regression test against 0.12.0 changes. + + https://github.com/joblib/joblib/pull/75 + + Make sure filter args doesn't mutate the kwargs dict that gets passed in. + """ + kwargs = {'x': 0} + filter_args(g, [], [], kwargs) + assert kwargs == {'x': 0} + + +def test_clean_win_chars(): + string = r'C:\foo\bar\main.py' + mangled_string = _clean_win_chars(string) + for char in ('\\', ':', '<', '>', '!'): + assert char not in mangled_string + + +@parametrize('func,args,kwargs,sgn_expected', + [(g, [list(range(5))], {}, 'g([0, 1, 2, 3, 4])'), + (k, [1, 2, (3, 4)], {'y': True}, 'k(1, 2, (3, 4), y=True)')]) +def test_format_signature(func, args, kwargs, sgn_expected): + # Test signature formatting. + path, sgn_result = format_signature(func, *args, **kwargs) + assert sgn_result == sgn_expected + + +def test_format_signature_long_arguments(): + shortening_threshold = 1500 + # shortening gets it down to 700 characters but there is the name + # of the function in the signature and a few additional things + # like dots for the ellipsis + shortening_target = 700 + 10 + + arg = 'a' * shortening_threshold + _, signature = format_signature(h, arg) + assert len(signature) < shortening_target + + nb_args = 5 + args = [arg for _ in range(nb_args)] + _, signature = format_signature(h, *args) + assert len(signature) < shortening_target * nb_args + + kwargs = {str(i): arg for i, arg in enumerate(args)} + _, signature = format_signature(h, **kwargs) + assert len(signature) < shortening_target * nb_args + + _, signature = format_signature(h, *args, **kwargs) + assert len(signature) < shortening_target * 2 * nb_args + + +@with_numpy +def test_format_signature_numpy(): + """ Test the format signature formatting with numpy. + """ + + +def test_special_source_encoding(): + from joblib.test.test_func_inspect_special_encoding import big5_f + func_code, source_file, first_line = get_func_code(big5_f) + assert first_line == 5 + assert "def big5_f():" in func_code + assert "test_func_inspect_special_encoding" in source_file + + +def _get_code(): + from joblib.test.test_func_inspect_special_encoding import big5_f + return get_func_code(big5_f)[0] + + +def test_func_code_consistency(): + from joblib.parallel import Parallel, delayed + codes = Parallel(n_jobs=2)(delayed(_get_code)() for _ in range(5)) + assert len(set(codes)) == 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect_special_encoding.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect_special_encoding.py new file mode 100644 index 0000000..6c41a59 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_func_inspect_special_encoding.py @@ -0,0 +1,9 @@ +# -*- coding: big5 -*- + + +# Some Traditional Chinese characters: @Ǥr +def big5_f(): + """Ωժ + """ + # + return 0 diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_hashing.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_hashing.py new file mode 100644 index 0000000..85593d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_hashing.py @@ -0,0 +1,495 @@ +""" +Test the hashing module. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import time +import hashlib +import sys +import gc +import io +import collections +import itertools +import pickle +import random +from concurrent.futures import ProcessPoolExecutor +from decimal import Decimal + +from joblib.hashing import hash +from joblib.func_inspect import filter_args +from joblib.memory import Memory +from joblib.testing import raises, skipif, fixture, parametrize +from joblib.test.common import np, with_numpy + + +def unicode(s): + return s + + +############################################################################### +# Helper functions for the tests +def time_func(func, *args): + """ Time function func on *args. + """ + times = list() + for _ in range(3): + t1 = time.time() + func(*args) + times.append(time.time() - t1) + return min(times) + + +def relative_time(func1, func2, *args): + """ Return the relative time between func1 and func2 applied on + *args. + """ + time_func1 = time_func(func1, *args) + time_func2 = time_func(func2, *args) + relative_diff = 0.5 * (abs(time_func1 - time_func2) + / (time_func1 + time_func2)) + return relative_diff + + +class Klass(object): + + def f(self, x): + return x + + +class KlassWithCachedMethod(object): + + def __init__(self, cachedir): + mem = Memory(location=cachedir) + self.f = mem.cache(self.f) + + def f(self, x): + return x + + +############################################################################### +# Tests + +input_list = [1, 2, 1., 2., 1 + 1j, 2. + 1j, + 'a', 'b', + (1,), (1, 1,), [1, ], [1, 1, ], + {1: 1}, {1: 2}, {2: 1}, + None, + gc.collect, + [1, ].append, + # Next 2 sets have unorderable elements in python 3. + set(('a', 1)), + set(('a', 1, ('a', 1))), + # Next 2 dicts have unorderable type of keys in python 3. + {'a': 1, 1: 2}, + {'a': 1, 1: 2, 'd': {'a': 1}}] + + +@parametrize('obj1', input_list) +@parametrize('obj2', input_list) +def test_trivial_hash(obj1, obj2): + """Smoke test hash on various types.""" + # Check that 2 objects have the same hash only if they are the same. + are_hashes_equal = hash(obj1) == hash(obj2) + are_objs_identical = obj1 is obj2 + assert are_hashes_equal == are_objs_identical + + +def test_hash_methods(): + # Check that hashing instance methods works + a = io.StringIO(unicode('a')) + assert hash(a.flush) == hash(a.flush) + a1 = collections.deque(range(10)) + a2 = collections.deque(range(9)) + assert hash(a1.extend) != hash(a2.extend) + + +@fixture(scope='function') +@with_numpy +def three_np_arrays(): + rnd = np.random.RandomState(0) + arr1 = rnd.random_sample((10, 10)) + arr2 = arr1.copy() + arr3 = arr2.copy() + arr3[0] += 1 + return arr1, arr2, arr3 + + +def test_hash_numpy_arrays(three_np_arrays): + arr1, arr2, arr3 = three_np_arrays + + for obj1, obj2 in itertools.product(three_np_arrays, repeat=2): + are_hashes_equal = hash(obj1) == hash(obj2) + are_arrays_equal = np.all(obj1 == obj2) + assert are_hashes_equal == are_arrays_equal + + assert hash(arr1) != hash(arr1.T) + + +def test_hash_numpy_dict_of_arrays(three_np_arrays): + arr1, arr2, arr3 = three_np_arrays + + d1 = {1: arr1, 2: arr2} + d2 = {1: arr2, 2: arr1} + d3 = {1: arr2, 2: arr3} + + assert hash(d1) == hash(d2) + assert hash(d1) != hash(d3) + + +@with_numpy +@parametrize('dtype', ['datetime64[s]', 'timedelta64[D]']) +def test_numpy_datetime_array(dtype): + # memoryview is not supported for some dtypes e.g. datetime64 + # see https://github.com/joblib/joblib/issues/188 for more details + a_hash = hash(np.arange(10)) + array = np.arange(0, 10, dtype=dtype) + assert hash(array) != a_hash + + +@with_numpy +def test_hash_numpy_noncontiguous(): + a = np.asarray(np.arange(6000).reshape((1000, 2, 3)), + order='F')[:, :1, :] + b = np.ascontiguousarray(a) + assert hash(a) != hash(b) + + c = np.asfortranarray(a) + assert hash(a) != hash(c) + + +@with_numpy +@parametrize('coerce_mmap', [True, False]) +def test_hash_memmap(tmpdir, coerce_mmap): + """Check that memmap and arrays hash identically if coerce_mmap is True.""" + filename = tmpdir.join('memmap_temp').strpath + try: + m = np.memmap(filename, shape=(10, 10), mode='w+') + a = np.asarray(m) + are_hashes_equal = (hash(a, coerce_mmap=coerce_mmap) == + hash(m, coerce_mmap=coerce_mmap)) + assert are_hashes_equal == coerce_mmap + finally: + if 'm' in locals(): + del m + # Force a garbage-collection cycle, to be certain that the + # object is delete, and we don't run in a problem under + # Windows with a file handle still open. + gc.collect() + + +@with_numpy +@skipif(sys.platform == 'win32', reason='This test is not stable under windows' + ' for some reason') +def test_hash_numpy_performance(): + """ Check the performance of hashing numpy arrays: + + In [22]: a = np.random.random(1000000) + + In [23]: %timeit hashlib.md5(a).hexdigest() + 100 loops, best of 3: 20.7 ms per loop + + In [24]: %timeit hashlib.md5(pickle.dumps(a, protocol=2)).hexdigest() + 1 loops, best of 3: 73.1 ms per loop + + In [25]: %timeit hashlib.md5(cPickle.dumps(a, protocol=2)).hexdigest() + 10 loops, best of 3: 53.9 ms per loop + + In [26]: %timeit hash(a) + 100 loops, best of 3: 20.8 ms per loop + """ + rnd = np.random.RandomState(0) + a = rnd.random_sample(1000000) + + def md5_hash(x): + return hashlib.md5(memoryview(x)).hexdigest() + + relative_diff = relative_time(md5_hash, hash, a) + assert relative_diff < 0.3 + + # Check that hashing an tuple of 3 arrays takes approximately + # 3 times as much as hashing one array + time_hashlib = 3 * time_func(md5_hash, a) + time_hash = time_func(hash, (a, a, a)) + relative_diff = 0.5 * (abs(time_hash - time_hashlib) + / (time_hash + time_hashlib)) + assert relative_diff < 0.3 + + +def test_bound_methods_hash(): + """ Make sure that calling the same method on two different instances + of the same class does resolve to the same hashes. + """ + a = Klass() + b = Klass() + assert (hash(filter_args(a.f, [], (1, ))) == + hash(filter_args(b.f, [], (1, )))) + + +def test_bound_cached_methods_hash(tmpdir): + """ Make sure that calling the same _cached_ method on two different + instances of the same class does resolve to the same hashes. + """ + a = KlassWithCachedMethod(tmpdir.strpath) + b = KlassWithCachedMethod(tmpdir.strpath) + assert (hash(filter_args(a.f.func, [], (1, ))) == + hash(filter_args(b.f.func, [], (1, )))) + + +@with_numpy +def test_hash_object_dtype(): + """ Make sure that ndarrays with dtype `object' hash correctly.""" + + a = np.array([np.arange(i) for i in range(6)], dtype=object) + b = np.array([np.arange(i) for i in range(6)], dtype=object) + + assert hash(a) == hash(b) + + +@with_numpy +def test_numpy_scalar(): + # Numpy scalars are built from compiled functions, and lead to + # strange pickling paths explored, that can give hash collisions + a = np.float64(2.0) + b = np.float64(3.0) + assert hash(a) != hash(b) + + +def test_dict_hash(tmpdir): + # Check that dictionaries hash consistently, even though the ordering + # of the keys is not guaranteed + k = KlassWithCachedMethod(tmpdir.strpath) + + d = {'#s12069__c_maps.nii.gz': [33], + '#s12158__c_maps.nii.gz': [33], + '#s12258__c_maps.nii.gz': [33], + '#s12277__c_maps.nii.gz': [33], + '#s12300__c_maps.nii.gz': [33], + '#s12401__c_maps.nii.gz': [33], + '#s12430__c_maps.nii.gz': [33], + '#s13817__c_maps.nii.gz': [33], + '#s13903__c_maps.nii.gz': [33], + '#s13916__c_maps.nii.gz': [33], + '#s13981__c_maps.nii.gz': [33], + '#s13982__c_maps.nii.gz': [33], + '#s13983__c_maps.nii.gz': [33]} + + a = k.f(d) + b = k.f(a) + + assert hash(a) == hash(b) + + +def test_set_hash(tmpdir): + # Check that sets hash consistently, even though their ordering + # is not guaranteed + k = KlassWithCachedMethod(tmpdir.strpath) + + s = set(['#s12069__c_maps.nii.gz', + '#s12158__c_maps.nii.gz', + '#s12258__c_maps.nii.gz', + '#s12277__c_maps.nii.gz', + '#s12300__c_maps.nii.gz', + '#s12401__c_maps.nii.gz', + '#s12430__c_maps.nii.gz', + '#s13817__c_maps.nii.gz', + '#s13903__c_maps.nii.gz', + '#s13916__c_maps.nii.gz', + '#s13981__c_maps.nii.gz', + '#s13982__c_maps.nii.gz', + '#s13983__c_maps.nii.gz']) + + a = k.f(s) + b = k.f(a) + + assert hash(a) == hash(b) + + +def test_set_decimal_hash(): + # Check that sets containing decimals hash consistently, even though + # ordering is not guaranteed + assert (hash(set([Decimal(0), Decimal('NaN')])) == + hash(set([Decimal('NaN'), Decimal(0)]))) + + +def test_string(): + # Test that we obtain the same hash for object owning several strings, + # whatever the past of these strings (which are immutable in Python) + string = 'foo' + a = {string: 'bar'} + b = {string: 'bar'} + c = pickle.loads(pickle.dumps(b)) + assert hash([a, b]) == hash([a, c]) + + +@with_numpy +def test_numpy_dtype_pickling(): + # numpy dtype hashing is tricky to get right: see #231, #239, #251 #1080, + # #1082, and explanatory comments inside + # ``joblib.hashing.NumpyHasher.save``. + + # In this test, we make sure that the pickling of numpy dtypes is robust to + # object identity and object copy. + + dt1 = np.dtype('f4') + dt2 = np.dtype('f4') + + # simple dtypes objects are interned + assert dt1 is dt2 + assert hash(dt1) == hash(dt2) + + dt1_roundtripped = pickle.loads(pickle.dumps(dt1)) + assert dt1 is not dt1_roundtripped + assert hash(dt1) == hash(dt1_roundtripped) + + assert hash([dt1, dt1]) == hash([dt1_roundtripped, dt1_roundtripped]) + assert hash([dt1, dt1]) == hash([dt1, dt1_roundtripped]) + + complex_dt1 = np.dtype( + [('name', np.str_, 16), ('grades', np.float64, (2,))] + ) + complex_dt2 = np.dtype( + [('name', np.str_, 16), ('grades', np.float64, (2,))] + ) + + # complex dtypes objects are not interned + assert hash(complex_dt1) == hash(complex_dt2) + + complex_dt1_roundtripped = pickle.loads(pickle.dumps(complex_dt1)) + assert complex_dt1_roundtripped is not complex_dt1 + assert hash(complex_dt1) == hash(complex_dt1_roundtripped) + + assert hash([complex_dt1, complex_dt1]) == hash( + [complex_dt1_roundtripped, complex_dt1_roundtripped] + ) + assert hash([complex_dt1, complex_dt1]) == hash( + [complex_dt1_roundtripped, complex_dt1] + ) + + +@parametrize('to_hash,expected', + [('This is a string to hash', + '71b3f47df22cb19431d85d92d0b230b2'), + (u"C'est l\xe9t\xe9", + '2d8d189e9b2b0b2e384d93c868c0e576'), + ((123456, 54321, -98765), + 'e205227dd82250871fa25aa0ec690aa3'), + ([random.Random(42).random() for _ in range(5)], + 'a11ffad81f9682a7d901e6edc3d16c84'), + ({'abcde': 123, 'sadfas': [-9999, 2, 3]}, + 'aeda150553d4bb5c69f0e69d51b0e2ef')]) +def test_hashes_stay_the_same(to_hash, expected): + # We want to make sure that hashes don't change with joblib + # version. For end users, that would mean that they have to + # regenerate their cache from scratch, which potentially means + # lengthy recomputations. + # Expected results have been generated with joblib 0.9.2 + assert hash(to_hash) == expected + + +@with_numpy +def test_hashes_are_different_between_c_and_fortran_contiguous_arrays(): + # We want to be sure that the c-contiguous and f-contiguous versions of the + # same array produce 2 different hashes. + rng = np.random.RandomState(0) + arr_c = rng.random_sample((10, 10)) + arr_f = np.asfortranarray(arr_c) + assert hash(arr_c) != hash(arr_f) + + +@with_numpy +def test_0d_array(): + hash(np.array(0)) + + +@with_numpy +def test_0d_and_1d_array_hashing_is_different(): + assert hash(np.array(0)) != hash(np.array([0])) + + +@with_numpy +def test_hashes_stay_the_same_with_numpy_objects(): + # Note: joblib used to test numpy objects hashing by comparing the produced + # hash of an object with some hard-coded target value to guarantee that + # hashing remains the same across joblib versions. However, since numpy + # 1.20 and joblib 1.0, joblib relies on potentially unstable implementation + # details of numpy to hash np.dtype objects, which makes the stability of + # hash values across different environments hard to guarantee and to test. + # As a result, hashing stability across joblib versions becomes best-effort + # only, and we only test the consistency within a single environment by + # making sure: + # - the hash of two copies of the same objects is the same + # - hashing some object in two different python processes produces the same + # value. This should be viewed as a proxy for testing hash consistency + # through time between Python sessions (provided no change in the + # environment was done between sessions). + + def create_objects_to_hash(): + rng = np.random.RandomState(42) + # Being explicit about dtypes in order to avoid + # architecture-related differences. Also using 'f4' rather than + # 'f8' for float arrays because 'f8' arrays generated by + # rng.random.randn don't seem to be bit-identical on 32bit and + # 64bit machines. + to_hash_list = [ + rng.randint(-1000, high=1000, size=50).astype(' +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. +import re + +from joblib.logger import PrintTime + + +def test_print_time(tmpdir, capsys): + # A simple smoke test for PrintTime. + logfile = tmpdir.join('test.log').strpath + print_time = PrintTime(logfile=logfile) + print_time('Foo') + # Create a second time, to smoke test log rotation. + print_time = PrintTime(logfile=logfile) + print_time('Foo') + # And a third time + print_time = PrintTime(logfile=logfile) + print_time('Foo') + + out_printed_text, err_printed_text = capsys.readouterr() + # Use regexps to be robust to time variations + match = r"Foo: 0\..s, 0\..min\nFoo: 0\..s, 0..min\nFoo: " + \ + r".\..s, 0..min\n" + if not re.match(match, err_printed_text): + raise AssertionError('Excepted %s, got %s' % + (match, err_printed_text)) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memmapping.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memmapping.py new file mode 100644 index 0000000..42a297a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memmapping.py @@ -0,0 +1,1191 @@ +import os +import mmap +import sys +import platform +import gc +import pickle +import itertools +from time import sleep +import subprocess +import threading +import faulthandler + +import pytest + +from joblib.test.common import with_numpy, np +from joblib.test.common import with_multiprocessing +from joblib.test.common import with_dev_shm +from joblib.testing import raises, parametrize, skipif +from joblib.backports import make_memmap +from joblib.parallel import Parallel, delayed + +from joblib.pool import MemmappingPool +from joblib.executor import _TestingMemmappingExecutor as TestExecutor +from joblib._memmapping_reducer import has_shareable_memory +from joblib._memmapping_reducer import ArrayMemmapForwardReducer +from joblib._memmapping_reducer import _strided_from_memmap +from joblib._memmapping_reducer import _get_temp_dir +from joblib._memmapping_reducer import _WeakArrayKeyMap +from joblib._memmapping_reducer import _get_backing_memmap +import joblib._memmapping_reducer as jmr + + +def setup_module(): + faulthandler.dump_traceback_later(timeout=300, exit=True) + + +def teardown_module(): + faulthandler.cancel_dump_traceback_later() + + +def check_memmap_and_send_back(array): + assert _get_backing_memmap(array) is not None + return array + + +def check_array(args): + """Dummy helper function to be executed in subprocesses + + Check that the provided array has the expected values in the provided + range. + + """ + data, position, expected = args + np.testing.assert_array_equal(data[position], expected) + + +def inplace_double(args): + """Dummy helper function to be executed in subprocesses + + + Check that the input array has the right values in the provided range + and perform an inplace modification to double the values in the range by + two. + + """ + data, position, expected = args + assert data[position] == expected + data[position] *= 2 + np.testing.assert_array_equal(data[position], 2 * expected) + + +@with_numpy +@with_multiprocessing +def test_memmap_based_array_reducing(tmpdir): + """Check that it is possible to reduce a memmap backed array""" + assert_array_equal = np.testing.assert_array_equal + filename = tmpdir.join('test.mmap').strpath + + # Create a file larger than what will be used by a + buffer = np.memmap(filename, dtype=np.float64, shape=500, mode='w+') + + # Fill the original buffer with negative markers to detect over of + # underflow in case of test failures + buffer[:] = - 1.0 * np.arange(buffer.shape[0], dtype=buffer.dtype) + buffer.flush() + + # Memmap a 2D fortran array on a offsetted subsection of the previous + # buffer + a = np.memmap(filename, dtype=np.float64, shape=(3, 5, 4), + mode='r+', order='F', offset=4) + a[:] = np.arange(60).reshape(a.shape) + + # Build various views that share the buffer with the original memmap + + # b is an memmap sliced view on an memmap instance + b = a[1:-1, 2:-1, 2:4] + + # c and d are array views + c = np.asarray(b) + d = c.T + + # Array reducer with auto dumping disabled + reducer = ArrayMemmapForwardReducer(None, tmpdir.strpath, 'c', True) + + def reconstruct_array_or_memmap(x): + cons, args = reducer(x) + return cons(*args) + + # Reconstruct original memmap + a_reconstructed = reconstruct_array_or_memmap(a) + assert has_shareable_memory(a_reconstructed) + assert isinstance(a_reconstructed, np.memmap) + assert_array_equal(a_reconstructed, a) + + # Reconstruct strided memmap view + b_reconstructed = reconstruct_array_or_memmap(b) + assert has_shareable_memory(b_reconstructed) + assert_array_equal(b_reconstructed, b) + + # Reconstruct arrays views on memmap base + c_reconstructed = reconstruct_array_or_memmap(c) + assert not isinstance(c_reconstructed, np.memmap) + assert has_shareable_memory(c_reconstructed) + assert_array_equal(c_reconstructed, c) + + d_reconstructed = reconstruct_array_or_memmap(d) + assert not isinstance(d_reconstructed, np.memmap) + assert has_shareable_memory(d_reconstructed) + assert_array_equal(d_reconstructed, d) + + # Test graceful degradation on fake memmap instances with in-memory + # buffers + a3 = a * 3 + assert not has_shareable_memory(a3) + a3_reconstructed = reconstruct_array_or_memmap(a3) + assert not has_shareable_memory(a3_reconstructed) + assert not isinstance(a3_reconstructed, np.memmap) + assert_array_equal(a3_reconstructed, a * 3) + + # Test graceful degradation on arrays derived from fake memmap instances + b3 = np.asarray(a3) + assert not has_shareable_memory(b3) + + b3_reconstructed = reconstruct_array_or_memmap(b3) + assert isinstance(b3_reconstructed, np.ndarray) + assert not has_shareable_memory(b3_reconstructed) + assert_array_equal(b3_reconstructed, b3) + + +@with_multiprocessing +@skipif((sys.platform != "win32") or (), + reason="PermissionError only easily triggerable on Windows") +def test_resource_tracker_retries_when_permissionerror(tmpdir): + # Test resource_tracker retry mechanism when unlinking memmaps. See more + # thorough information in the ``unlink_file`` documentation of joblib. + filename = tmpdir.join('test.mmap').strpath + cmd = """if 1: + import os + import numpy as np + import time + from joblib.externals.loky.backend import resource_tracker + resource_tracker.VERBOSE = 1 + + # Start the resource tracker + resource_tracker.ensure_running() + time.sleep(1) + + # Create a file containing numpy data + memmap = np.memmap(r"{filename}", dtype=np.float64, shape=10, mode='w+') + memmap[:] = np.arange(10).astype(np.int8).data + memmap.flush() + assert os.path.exists(r"{filename}") + del memmap + + # Create a np.memmap backed by this file + memmap = np.memmap(r"{filename}", dtype=np.float64, shape=10, mode='w+') + resource_tracker.register(r"{filename}", "file") + + # Ask the resource_tracker to delete the file backing the np.memmap , this + # should raise PermissionError that the resource_tracker will log. + resource_tracker.maybe_unlink(r"{filename}", "file") + + # Wait for the resource_tracker to process the maybe_unlink before cleaning + # up the memmap + time.sleep(2) + """.format(filename=filename) + p = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + p.wait() + out, err = p.communicate() + assert p.returncode == 0 + assert out == b'' + msg = 'tried to unlink {}, got PermissionError'.format(filename) + assert msg in err.decode() + + +@with_numpy +@with_multiprocessing +def test_high_dimension_memmap_array_reducing(tmpdir): + assert_array_equal = np.testing.assert_array_equal + + filename = tmpdir.join('test.mmap').strpath + + # Create a high dimensional memmap + a = np.memmap(filename, dtype=np.float64, shape=(100, 15, 15, 3), + mode='w+') + a[:] = np.arange(100 * 15 * 15 * 3).reshape(a.shape) + + # Create some slices/indices at various dimensions + b = a[0:10] + c = a[:, 5:10] + d = a[:, :, :, 0] + e = a[1:3:4] + + # Array reducer with auto dumping disabled + reducer = ArrayMemmapForwardReducer(None, tmpdir.strpath, 'c', True) + + def reconstruct_array_or_memmap(x): + cons, args = reducer(x) + return cons(*args) + + a_reconstructed = reconstruct_array_or_memmap(a) + assert has_shareable_memory(a_reconstructed) + assert isinstance(a_reconstructed, np.memmap) + assert_array_equal(a_reconstructed, a) + + b_reconstructed = reconstruct_array_or_memmap(b) + assert has_shareable_memory(b_reconstructed) + assert_array_equal(b_reconstructed, b) + + c_reconstructed = reconstruct_array_or_memmap(c) + assert has_shareable_memory(c_reconstructed) + assert_array_equal(c_reconstructed, c) + + d_reconstructed = reconstruct_array_or_memmap(d) + assert has_shareable_memory(d_reconstructed) + assert_array_equal(d_reconstructed, d) + + e_reconstructed = reconstruct_array_or_memmap(e) + assert has_shareable_memory(e_reconstructed) + assert_array_equal(e_reconstructed, e) + + +@with_numpy +def test__strided_from_memmap(tmpdir): + fname = tmpdir.join('test.mmap').strpath + size = 5 * mmap.ALLOCATIONGRANULARITY + offset = mmap.ALLOCATIONGRANULARITY + 1 + # This line creates the mmap file that is reused later + memmap_obj = np.memmap(fname, mode='w+', shape=size + offset) + # filename, dtype, mode, offset, order, shape, strides, total_buffer_len + memmap_obj = _strided_from_memmap(fname, dtype='uint8', mode='r', + offset=offset, order='C', shape=size, + strides=None, total_buffer_len=None, + unlink_on_gc_collect=False) + assert isinstance(memmap_obj, np.memmap) + assert memmap_obj.offset == offset + memmap_backed_obj = _strided_from_memmap( + fname, dtype='uint8', mode='r', offset=offset, order='C', + shape=(size // 2,), strides=(2,), total_buffer_len=size, + unlink_on_gc_collect=False + ) + assert _get_backing_memmap(memmap_backed_obj).offset == offset + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_pool_with_memmap(factory, tmpdir): + """Check that subprocess can access and update shared memory memmap""" + assert_array_equal = np.testing.assert_array_equal + + # Fork the subprocess before allocating the objects to be passed + pool_temp_folder = tmpdir.mkdir('pool').strpath + p = factory(10, max_nbytes=2, temp_folder=pool_temp_folder) + try: + filename = tmpdir.join('test.mmap').strpath + a = np.memmap(filename, dtype=np.float32, shape=(3, 5), mode='w+') + a.fill(1.0) + + p.map(inplace_double, [(a, (i, j), 1.0) + for i in range(a.shape[0]) + for j in range(a.shape[1])]) + + assert_array_equal(a, 2 * np.ones(a.shape)) + + # Open a copy-on-write view on the previous data + b = np.memmap(filename, dtype=np.float32, shape=(5, 3), mode='c') + + p.map(inplace_double, [(b, (i, j), 2.0) + for i in range(b.shape[0]) + for j in range(b.shape[1])]) + + # Passing memmap instances to the pool should not trigger the creation + # of new files on the FS + assert os.listdir(pool_temp_folder) == [] + + # the original data is untouched + assert_array_equal(a, 2 * np.ones(a.shape)) + assert_array_equal(b, 2 * np.ones(b.shape)) + + # readonly maps can be read but not updated + c = np.memmap(filename, dtype=np.float32, shape=(10,), mode='r', + offset=5 * 4) + + with raises(AssertionError): + p.map(check_array, [(c, i, 3.0) for i in range(c.shape[0])]) + + # depending on the version of numpy one can either get a RuntimeError + # or a ValueError + with raises((RuntimeError, ValueError)): + p.map(inplace_double, [(c, i, 2.0) for i in range(c.shape[0])]) + finally: + # Clean all filehandlers held by the pool + p.terminate() + del p + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_pool_with_memmap_array_view(factory, tmpdir): + """Check that subprocess can access and update shared memory array""" + assert_array_equal = np.testing.assert_array_equal + + # Fork the subprocess before allocating the objects to be passed + pool_temp_folder = tmpdir.mkdir('pool').strpath + p = factory(10, max_nbytes=2, temp_folder=pool_temp_folder) + try: + + filename = tmpdir.join('test.mmap').strpath + a = np.memmap(filename, dtype=np.float32, shape=(3, 5), mode='w+') + a.fill(1.0) + + # Create an ndarray view on the memmap instance + a_view = np.asarray(a) + assert not isinstance(a_view, np.memmap) + assert has_shareable_memory(a_view) + + p.map(inplace_double, [(a_view, (i, j), 1.0) + for i in range(a.shape[0]) + for j in range(a.shape[1])]) + + # Both a and the a_view have been updated + assert_array_equal(a, 2 * np.ones(a.shape)) + assert_array_equal(a_view, 2 * np.ones(a.shape)) + + # Passing memmap array view to the pool should not trigger the + # creation of new files on the FS + assert os.listdir(pool_temp_folder) == [] + + finally: + p.terminate() + del p + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_permission_error_windows_reference_cycle(backend): + # Non regression test for: + # https://github.com/joblib/joblib/issues/806 + # + # The issue happens when trying to delete a memory mapped file that has + # not yet been closed by one of the worker processes. + cmd = """if 1: + import numpy as np + from joblib import Parallel, delayed + + + data = np.random.rand(int(2e6)).reshape((int(1e6), 2)) + + # Build a complex cyclic reference that is likely to delay garbage + # collection of the memmapped array in the worker processes. + first_list = current_list = [data] + for i in range(10): + current_list = [current_list] + first_list.append(current_list) + + if __name__ == "__main__": + results = Parallel(n_jobs=2, backend="{b}")( + delayed(len)(current_list) for i in range(10)) + assert results == [1] * 10 + """.format(b=backend) + p = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + p.wait() + out, err = p.communicate() + assert p.returncode == 0, out.decode() + "\n\n" + err.decode() + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_permission_error_windows_memmap_sent_to_parent(backend): + # Second non-regression test for: + # https://github.com/joblib/joblib/issues/806 + # previously, child process would not convert temporary memmaps to numpy + # arrays when sending the data back to the parent process. This would lead + # to permission errors on windows when deleting joblib's temporary folder, + # as the memmaped files handles would still opened in the parent process. + cmd = '''if 1: + import os + import time + + import numpy as np + + from joblib import Parallel, delayed + from testutils import return_slice_of_data + + data = np.ones(int(2e6)) + + if __name__ == '__main__': + # warm-up call to launch the workers and start the resource_tracker + _ = Parallel(n_jobs=2, verbose=5, backend='{b}')( + delayed(id)(i) for i in range(20)) + + time.sleep(0.5) + + slice_of_data = Parallel(n_jobs=2, verbose=5, backend='{b}')( + delayed(return_slice_of_data)(data, 0, 20) for _ in range(10)) + '''.format(b=backend) + + for _ in range(3): + env = os.environ.copy() + env['PYTHONPATH'] = os.path.dirname(__file__) + p = subprocess.Popen([sys.executable, '-c', cmd], + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, env=env) + p.wait() + out, err = p.communicate() + assert p.returncode == 0, err + assert out == b'' + if sys.version_info[:3] not in [(3, 8, 0), (3, 8, 1)]: + # In early versions of Python 3.8, a reference leak + # https://github.com/cloudpipe/cloudpickle/issues/327, holds + # references to pickled objects, generating race condition during + # cleanup finalizers of joblib and noisy resource_tracker outputs. + assert b'resource_tracker' not in err + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_parallel_isolated_temp_folders(backend): + # Test that consecutive Parallel call use isolated subfolders, even + # for the loky backend that reuses its executor instance across calls. + array = np.arange(int(1e2)) + [filename_1] = Parallel(n_jobs=2, backend=backend, max_nbytes=10)( + delayed(getattr)(array, 'filename') for _ in range(1) + ) + [filename_2] = Parallel(n_jobs=2, backend=backend, max_nbytes=10)( + delayed(getattr)(array, 'filename') for _ in range(1) + ) + assert os.path.dirname(filename_2) != os.path.dirname(filename_1) + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_managed_backend_reuse_temp_folder(backend): + # Test that calls to a managed parallel object reuse the same memmaps. + array = np.arange(int(1e2)) + with Parallel(n_jobs=2, backend=backend, max_nbytes=10) as p: + [filename_1] = p( + delayed(getattr)(array, 'filename') for _ in range(1) + ) + [filename_2] = p( + delayed(getattr)(array, 'filename') for _ in range(1) + ) + assert os.path.dirname(filename_2) == os.path.dirname(filename_1) + + +@with_numpy +@with_multiprocessing +def test_memmapping_temp_folder_thread_safety(): + # Concurrent calls to Parallel with the loky backend will use the same + # executor, and thus the same reducers. Make sure that those reducers use + # different temporary folders depending on which Parallel objects called + # them, which is necessary to limit potential race conditions during the + # garbage collection of temporary memmaps. + array = np.arange(int(1e2)) + + temp_dirs_thread_1 = set() + temp_dirs_thread_2 = set() + + def concurrent_get_filename(array, temp_dirs): + with Parallel(backend='loky', n_jobs=2, max_nbytes=10) as p: + for i in range(10): + [filename] = p( + delayed(getattr)(array, 'filename') for _ in range(1) + ) + temp_dirs.add(os.path.dirname(filename)) + + t1 = threading.Thread( + target=concurrent_get_filename, args=(array, temp_dirs_thread_1) + ) + t2 = threading.Thread( + target=concurrent_get_filename, args=(array, temp_dirs_thread_2) + ) + + t1.start() + t2.start() + + t1.join() + t2.join() + + assert len(temp_dirs_thread_1) == 1 + assert len(temp_dirs_thread_2) == 1 + + assert temp_dirs_thread_1 != temp_dirs_thread_2 + + +@with_numpy +@with_multiprocessing +def test_multithreaded_parallel_termination_resource_tracker_silent(): + # test that concurrent termination attempts of a same executor does not + # emit any spurious error from the resource_tracker. We test various + # situations making 0, 1 or both parallel call sending a task that will + # make the worker (and thus the whole Parallel call) error out. + cmd = '''if 1: + import os + import numpy as np + from joblib import Parallel, delayed + from joblib.externals.loky.backend import resource_tracker + from concurrent.futures import ThreadPoolExecutor, wait + + resource_tracker.VERBOSE = 0 + + array = np.arange(int(1e2)) + + temp_dirs_thread_1 = set() + temp_dirs_thread_2 = set() + + + def raise_error(array): + raise ValueError + + + def parallel_get_filename(array, temp_dirs): + with Parallel(backend="loky", n_jobs=2, max_nbytes=10) as p: + for i in range(10): + [filename] = p( + delayed(getattr)(array, "filename") for _ in range(1) + ) + temp_dirs.add(os.path.dirname(filename)) + + + def parallel_raise(array, temp_dirs): + with Parallel(backend="loky", n_jobs=2, max_nbytes=10) as p: + for i in range(10): + [filename] = p( + delayed(raise_error)(array) for _ in range(1) + ) + temp_dirs.add(os.path.dirname(filename)) + + + executor = ThreadPoolExecutor(max_workers=2) + + # both function calls will use the same loky executor, but with a + # different Parallel object. + future_1 = executor.submit({f1}, array, temp_dirs_thread_1) + future_2 = executor.submit({f2}, array, temp_dirs_thread_2) + + # Wait for both threads to terminate their backend + wait([future_1, future_2]) + + future_1.result() + future_2.result() + ''' + functions_and_returncodes = [ + ("parallel_get_filename", "parallel_get_filename", 0), + ("parallel_get_filename", "parallel_raise", 1), + ("parallel_raise", "parallel_raise", 1) + ] + + for f1, f2, returncode in functions_and_returncodes: + p = subprocess.Popen([sys.executable, '-c', cmd.format(f1=f1, f2=f2)], + stderr=subprocess.PIPE, stdout=subprocess.PIPE) + p.wait() + out, err = p.communicate() + assert p.returncode == returncode, out.decode() + assert b"resource_tracker" not in err, err.decode() + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_many_parallel_calls_on_same_object(backend): + # After #966 got merged, consecutive Parallel objects were sharing temp + # folder, which would lead to race conditions happening during the + # temporary resources management with the resource_tracker. This is a + # non-regression test that makes sure that consecutive Parallel operations + # on the same object do not error out. + cmd = '''if 1: + import os + import time + + import numpy as np + + from joblib import Parallel, delayed + from testutils import return_slice_of_data + + data = np.ones(100) + + if __name__ == '__main__': + for i in range(5): + slice_of_data = Parallel( + n_jobs=2, max_nbytes=1, backend='{b}')( + delayed(return_slice_of_data)(data, 0, 20) + for _ in range(10) + ) + '''.format(b=backend) + env = os.environ.copy() + env['PYTHONPATH'] = os.path.dirname(__file__) + p = subprocess.Popen( + [sys.executable, '-c', cmd], + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + env=env, + ) + p.wait() + out, err = p.communicate() + assert p.returncode == 0, err + assert out == b'' + if sys.version_info[:3] not in [(3, 8, 0), (3, 8, 1)]: + # In early versions of Python 3.8, a reference leak + # https://github.com/cloudpipe/cloudpickle/issues/327, holds + # references to pickled objects, generating race condition during + # cleanup finalizers of joblib and noisy resource_tracker outputs. + assert b'resource_tracker' not in err + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_memmap_returned_as_regular_array(backend): + data = np.ones(int(1e3)) + # Check that child processes send temporary memmaps back as numpy arrays. + [result] = Parallel(n_jobs=2, backend=backend, max_nbytes=100)( + delayed(check_memmap_and_send_back)(data) for _ in range(1)) + assert _get_backing_memmap(result) is None + + +@with_numpy +@with_multiprocessing +@parametrize("backend", ["multiprocessing", "loky"]) +def test_resource_tracker_silent_when_reference_cycles(backend): + # There is a variety of reasons that can make joblib with loky backend + # output noisy warnings when a reference cycle is preventing a memmap from + # being garbage collected. Especially, joblib's main process finalizer + # deletes the temporary folder if it was not done before, which can + # interact badly with the resource_tracker. We don't risk leaking any + # resources, but this will likely make joblib output a lot of low-level + # confusing messages. + # + # This test makes sure that the resource_tracker is silent when a reference + # has been collected concurrently on non-Windows platforms. + # + # Note that the script in ``cmd`` is the exact same script as in + # test_permission_error_windows_reference_cycle. + if backend == "loky" and sys.platform.startswith('win'): + # XXX: on Windows, reference cycles can delay timely garbage collection + # and make it impossible to properly delete the temporary folder in the + # main process because of permission errors. + pytest.xfail( + "The temporary folder cannot be deleted on Windows in the " + "presence of a reference cycle" + ) + + cmd = """if 1: + import numpy as np + from joblib import Parallel, delayed + + + data = np.random.rand(int(2e6)).reshape((int(1e6), 2)) + + # Build a complex cyclic reference that is likely to delay garbage + # collection of the memmapped array in the worker processes. + first_list = current_list = [data] + for i in range(10): + current_list = [current_list] + first_list.append(current_list) + + if __name__ == "__main__": + results = Parallel(n_jobs=2, backend="{b}")( + delayed(len)(current_list) for i in range(10)) + assert results == [1] * 10 + """.format(b=backend) + p = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE, + stdout=subprocess.PIPE) + p.wait() + out, err = p.communicate() + out = out.decode() + err = err.decode() + assert p.returncode == 0, out + "\n\n" + err + assert "resource_tracker" not in err, err + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_memmapping_pool_for_large_arrays(factory, tmpdir): + """Check that large arrays are not copied in memory""" + + # Check that the tempfolder is empty + assert os.listdir(tmpdir.strpath) == [] + + # Build an array reducers that automatically dump large array content + # to filesystem backed memmap instances to avoid memory explosion + p = factory(3, max_nbytes=40, temp_folder=tmpdir.strpath, verbose=2) + try: + # The temporary folder for the pool is not provisioned in advance + assert os.listdir(tmpdir.strpath) == [] + assert not os.path.exists(p._temp_folder) + + small = np.ones(5, dtype=np.float32) + assert small.nbytes == 20 + p.map(check_array, [(small, i, 1.0) for i in range(small.shape[0])]) + + # Memory has been copied, the pool filesystem folder is unused + assert os.listdir(tmpdir.strpath) == [] + + # Try with a file larger than the memmap threshold of 40 bytes + large = np.ones(100, dtype=np.float64) + assert large.nbytes == 800 + p.map(check_array, [(large, i, 1.0) for i in range(large.shape[0])]) + + # The data has been dumped in a temp folder for subprocess to share it + # without per-child memory copies + assert os.path.isdir(p._temp_folder) + dumped_filenames = os.listdir(p._temp_folder) + assert len(dumped_filenames) == 1 + + # Check that memory mapping is not triggered for arrays with + # dtype='object' + objects = np.array(['abc'] * 100, dtype='object') + results = p.map(has_shareable_memory, [objects]) + assert not results[0] + + finally: + # check FS garbage upon pool termination + p.terminate() + for i in range(10): + sleep(.1) + if not os.path.exists(p._temp_folder): + break + else: # pragma: no cover + raise AssertionError( + 'temporary folder {} was not deleted'.format(p._temp_folder) + ) + del p + + +@with_numpy +@with_multiprocessing +@parametrize( + "backend", + [ + pytest.param( + "multiprocessing", + marks=pytest.mark.xfail( + reason='https://github.com/joblib/joblib/issues/1086' + ), + ), + "loky", + ] +) +def test_child_raises_parent_exits_cleanly(backend): + # When a task executed by a child process raises an error, the parent + # process's backend is notified, and calls abort_everything. + # In loky, abort_everything itself calls shutdown(kill_workers=True) which + # sends SIGKILL to the worker, preventing it from running the finalizers + # supposed to signal the resource_tracker when the worker is done using + # objects relying on a shared resource (e.g np.memmaps). Because this + # behavior is prone to : + # - cause a resource leak + # - make the resource tracker emit noisy resource warnings + # we explicitly test that, when the said situation occurs: + # - no resources are actually leaked + # - the temporary resources are deleted as soon as possible (typically, at + # the end of the failing Parallel call) + # - the resource_tracker does not emit any warnings. + cmd = """if 1: + import os + from pathlib import Path + from time import sleep + + import numpy as np + from joblib import Parallel, delayed + from testutils import print_filename_and_raise + + data = np.random.rand(1000) + + def get_temp_folder(parallel_obj, backend): + if "{b}" == "loky": + return Path(parallel_obj._backend._workers._temp_folder) + else: + return Path(parallel_obj._backend._pool._temp_folder) + + + if __name__ == "__main__": + try: + with Parallel(n_jobs=2, backend="{b}", max_nbytes=100) as p: + temp_folder = get_temp_folder(p, "{b}") + p(delayed(print_filename_and_raise)(data) + for i in range(1)) + except ValueError as e: + # the temporary folder should be deleted by the end of this + # call but apparently on some file systems, this takes + # some time to be visible. + # + # We attempt to write into the temporary folder to test for + # its existence and we wait for a maximum of 10 seconds. + for i in range(100): + try: + with open(temp_folder / "some_file.txt", "w") as f: + f.write("some content") + except FileNotFoundError: + # temp_folder has been deleted, all is fine + break + + # ... else, wait a bit and try again + sleep(.1) + else: + raise AssertionError( + str(temp_folder) + " was not deleted" + ) from e + """.format(b=backend) + env = os.environ.copy() + env['PYTHONPATH'] = os.path.dirname(__file__) + p = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE, + stdout=subprocess.PIPE, env=env) + p.wait() + out, err = p.communicate() + out, err = out.decode(), err.decode() + filename = out.split('\n')[0] + assert p.returncode == 0, err or out + assert err == '' # no resource_tracker warnings. + assert not os.path.exists(filename) + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_memmapping_pool_for_large_arrays_disabled(factory, tmpdir): + """Check that large arrays memmapping can be disabled""" + # Set max_nbytes to None to disable the auto memmapping feature + p = factory(3, max_nbytes=None, temp_folder=tmpdir.strpath) + try: + + # Check that the tempfolder is empty + assert os.listdir(tmpdir.strpath) == [] + + # Try with a file largish than the memmap threshold of 40 bytes + large = np.ones(100, dtype=np.float64) + assert large.nbytes == 800 + p.map(check_array, [(large, i, 1.0) for i in range(large.shape[0])]) + + # Check that the tempfolder is still empty + assert os.listdir(tmpdir.strpath) == [] + + finally: + # Cleanup open file descriptors + p.terminate() + del p + + +@with_numpy +@with_multiprocessing +@with_dev_shm +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_memmapping_on_large_enough_dev_shm(factory): + """Check that memmapping uses /dev/shm when possible""" + orig_size = jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE + try: + # Make joblib believe that it can use /dev/shm even when running on a + # CI container where the size of the /dev/shm is not very large (that + # is at least 32 MB instead of 2 GB by default). + jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE = int(32e6) + p = factory(3, max_nbytes=10) + try: + # Check that the pool has correctly detected the presence of the + # shared memory filesystem. + pool_temp_folder = p._temp_folder + folder_prefix = '/dev/shm/joblib_memmapping_folder_' + assert pool_temp_folder.startswith(folder_prefix) + assert os.path.exists(pool_temp_folder) + + # Try with a file larger than the memmap threshold of 10 bytes + a = np.ones(100, dtype=np.float64) + assert a.nbytes == 800 + p.map(id, [a] * 10) + # a should have been memmapped to the pool temp folder: the joblib + # pickling procedure generate one .pkl file: + assert len(os.listdir(pool_temp_folder)) == 1 + + # create a new array with content that is different from 'a' so + # that it is mapped to a different file in the temporary folder of + # the pool. + b = np.ones(100, dtype=np.float64) * 2 + assert b.nbytes == 800 + p.map(id, [b] * 10) + # A copy of both a and b are now stored in the shared memory folder + assert len(os.listdir(pool_temp_folder)) == 2 + finally: + # Cleanup open file descriptors + p.terminate() + del p + + for i in range(100): + # The temp folder is cleaned up upon pool termination + if not os.path.exists(pool_temp_folder): + break + sleep(.1) + else: # pragma: no cover + raise AssertionError('temporary folder of pool was not deleted') + finally: + jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE = orig_size + + +@with_numpy +@with_multiprocessing +@with_dev_shm +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_memmapping_on_too_small_dev_shm(factory): + orig_size = jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE + try: + # Make joblib believe that it cannot use /dev/shm unless there is + # 42 exabytes of available shared memory in /dev/shm + jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE = int(42e18) + + p = factory(3, max_nbytes=10) + try: + # Check that the pool has correctly detected the presence of the + # shared memory filesystem. + pool_temp_folder = p._temp_folder + assert not pool_temp_folder.startswith('/dev/shm') + finally: + # Cleanup open file descriptors + p.terminate() + del p + + # The temp folder is cleaned up upon pool termination + assert not os.path.exists(pool_temp_folder) + finally: + jmr.SYSTEM_SHARED_MEM_FS_MIN_SIZE = orig_size + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_memmapping_pool_for_large_arrays_in_return(factory, tmpdir): + """Check that large arrays are not copied in memory in return""" + assert_array_equal = np.testing.assert_array_equal + + # Build an array reducers that automatically dump large array content + # but check that the returned datastructure are regular arrays to avoid + # passing a memmap array pointing to a pool controlled temp folder that + # might be confusing to the user + + # The MemmappingPool user can always return numpy.memmap object explicitly + # to avoid memory copy + p = factory(3, max_nbytes=10, temp_folder=tmpdir.strpath) + try: + res = p.apply_async(np.ones, args=(1000,)) + large = res.get() + assert not has_shareable_memory(large) + assert_array_equal(large, np.ones(1000)) + finally: + p.terminate() + del p + + +def _worker_multiply(a, n_times): + """Multiplication function to be executed by subprocess""" + assert has_shareable_memory(a) + return a * n_times + + +@with_numpy +@with_multiprocessing +@parametrize("factory", [MemmappingPool, TestExecutor.get_memmapping_executor], + ids=["multiprocessing", "loky"]) +def test_workaround_against_bad_memmap_with_copied_buffers(factory, tmpdir): + """Check that memmaps with a bad buffer are returned as regular arrays + + Unary operations and ufuncs on memmap instances return a new memmap + instance with an in-memory buffer (probably a numpy bug). + """ + assert_array_equal = np.testing.assert_array_equal + + p = factory(3, max_nbytes=10, temp_folder=tmpdir.strpath) + try: + # Send a complex, large-ish view on a array that will be converted to + # a memmap in the worker process + a = np.asarray(np.arange(6000).reshape((1000, 2, 3)), + order='F')[:, :1, :] + + # Call a non-inplace multiply operation on the worker and memmap and + # send it back to the parent. + b = p.apply_async(_worker_multiply, args=(a, 3)).get() + assert not has_shareable_memory(b) + assert_array_equal(b, 3 * a) + finally: + p.terminate() + del p + + +def identity(arg): + return arg + + +@with_numpy +@with_multiprocessing +@parametrize( + "factory,retry_no", + list(itertools.product( + [MemmappingPool, TestExecutor.get_memmapping_executor], range(3))), + ids=['{}, {}'.format(x, y) for x, y in itertools.product( + ["multiprocessing", "loky"], map(str, range(3)))]) +def test_pool_memmap_with_big_offset(factory, retry_no, tmpdir): + # Test that numpy memmap offset is set correctly if greater than + # mmap.ALLOCATIONGRANULARITY, see + # https://github.com/joblib/joblib/issues/451 and + # https://github.com/numpy/numpy/pull/8443 for more details. + fname = tmpdir.join('test.mmap').strpath + size = 5 * mmap.ALLOCATIONGRANULARITY + offset = mmap.ALLOCATIONGRANULARITY + 1 + obj = make_memmap(fname, mode='w+', shape=size, dtype='uint8', + offset=offset) + + p = factory(2, temp_folder=tmpdir.strpath) + result = p.apply_async(identity, args=(obj,)).get() + assert isinstance(result, np.memmap) + assert result.offset == offset + np.testing.assert_array_equal(obj, result) + p.terminate() + + +def test_pool_get_temp_dir(tmpdir): + pool_folder_name = 'test.tmpdir' + pool_folder, shared_mem = _get_temp_dir(pool_folder_name, tmpdir.strpath) + assert shared_mem is False + assert pool_folder == tmpdir.join('test.tmpdir').strpath + + pool_folder, shared_mem = _get_temp_dir(pool_folder_name, temp_folder=None) + if sys.platform.startswith('win'): + assert shared_mem is False + assert pool_folder.endswith(pool_folder_name) + + +def test_pool_get_temp_dir_no_statvfs(tmpdir, monkeypatch): + """Check that _get_temp_dir works when os.statvfs is not defined + + Regression test for #902 + """ + pool_folder_name = 'test.tmpdir' + import joblib._memmapping_reducer + if hasattr(joblib._memmapping_reducer.os, 'statvfs'): + # We are on Unix, since Windows doesn't have this function + monkeypatch.delattr(joblib._memmapping_reducer.os, 'statvfs') + + pool_folder, shared_mem = _get_temp_dir(pool_folder_name, temp_folder=None) + if sys.platform.startswith('win'): + assert shared_mem is False + assert pool_folder.endswith(pool_folder_name) + + +@with_numpy +@skipif(sys.platform == 'win32', reason='This test fails with a ' + 'PermissionError on Windows') +@parametrize("mmap_mode", ["r+", "w+"]) +def test_numpy_arrays_use_different_memory(mmap_mode): + def func(arr, value): + arr[:] = value + return arr + + arrays = [np.zeros((10, 10), dtype='float64') for i in range(10)] + + results = Parallel(mmap_mode=mmap_mode, max_nbytes=0, n_jobs=2)( + delayed(func)(arr, i) for i, arr in enumerate(arrays)) + + for i, arr in enumerate(results): + np.testing.assert_array_equal(arr, i) + + +@with_numpy +def test_weak_array_key_map(): + + def assert_empty_after_gc_collect(container, retries=100): + for i in range(retries): + if len(container) == 0: + return + gc.collect() + sleep(.1) + assert len(container) == 0 + + a = np.ones(42) + m = _WeakArrayKeyMap() + m.set(a, 'a') + assert m.get(a) == 'a' + + b = a + assert m.get(b) == 'a' + m.set(b, 'b') + assert m.get(a) == 'b' + + del a + gc.collect() + assert len(m._data) == 1 + assert m.get(b) == 'b' + + del b + assert_empty_after_gc_collect(m._data) + + c = np.ones(42) + m.set(c, 'c') + assert len(m._data) == 1 + assert m.get(c) == 'c' + + with raises(KeyError): + m.get(np.ones(42)) + + del c + assert_empty_after_gc_collect(m._data) + + # Check that creating and dropping numpy arrays with potentially the same + # object id will not cause the map to get confused. + def get_set_get_collect(m, i): + a = np.ones(42) + with raises(KeyError): + m.get(a) + m.set(a, i) + assert m.get(a) == i + return id(a) + + unique_ids = set([get_set_get_collect(m, i) for i in range(1000)]) + if platform.python_implementation() == 'CPython': + # On CPython (at least) the same id is often reused many times for the + # temporary arrays created under the local scope of the + # get_set_get_collect function without causing any spurious lookups / + # insertions in the map. Apparently on Python nogil, the id is not + # reused as often. + max_len_unique_ids = 400 if getattr(sys.flags, 'nogil', False) else 100 + assert len(unique_ids) < max_len_unique_ids + + +def test_weak_array_key_map_no_pickling(): + m = _WeakArrayKeyMap() + with raises(pickle.PicklingError): + pickle.dumps(m) + + +@with_numpy +@with_multiprocessing +def test_direct_mmap(tmpdir): + testfile = str(tmpdir.join('arr.dat')) + a = np.arange(10, dtype='uint8') + a.tofile(testfile) + + def _read_array(): + with open(testfile) as fd: + mm = mmap.mmap(fd.fileno(), 0, access=mmap.ACCESS_READ, offset=0) + return np.ndarray((10,), dtype=np.uint8, buffer=mm, offset=0) + + def func(x): + return x**2 + + arr = _read_array() + + # this is expected to work and gives the reference + ref = Parallel(n_jobs=2)(delayed(func)(x) for x in [a]) + + # now test that it work with the mmap array + results = Parallel(n_jobs=2)(delayed(func)(x) for x in [arr]) + np.testing.assert_array_equal(results, ref) + + # also test with a mmap array read in the subprocess + def worker(): + return _read_array() + + results = Parallel(n_jobs=2)(delayed(worker)() for _ in range(1)) + np.testing.assert_array_equal(results[0], arr) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory.py new file mode 100644 index 0000000..120987b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory.py @@ -0,0 +1,1493 @@ +""" +Test the memory module. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import functools +import gc +import logging +import shutil +import os +import os.path +import pathlib +import pickle +import sys +import time +import datetime +import textwrap + +import pytest + +from joblib.memory import Memory +from joblib.memory import expires_after +from joblib.memory import MemorizedFunc, NotMemorizedFunc +from joblib.memory import MemorizedResult, NotMemorizedResult +from joblib.memory import _FUNCTION_HASHES +from joblib.memory import register_store_backend, _STORE_BACKENDS +from joblib.memory import _build_func_identifier, _store_backend_factory +from joblib.memory import JobLibCollisionWarning +from joblib.parallel import Parallel, delayed +from joblib._store_backends import StoreBackendBase, FileSystemStoreBackend +from joblib.test.common import with_numpy, np +from joblib.test.common import with_multiprocessing +from joblib.testing import parametrize, raises, warns +from joblib.hashing import hash + + +############################################################################### +# Module-level variables for the tests +def f(x, y=1): + """ A module-level function for testing purposes. + """ + return x ** 2 + y + + +############################################################################### +# Helper function for the tests +def check_identity_lazy(func, accumulator, location): + """ Given a function and an accumulator (a list that grows every + time the function is called), check that the function can be + decorated by memory to be a lazy identity. + """ + # Call each function with several arguments, and check that it is + # evaluated only once per argument. + memory = Memory(location=location, verbose=0) + func = memory.cache(func) + for i in range(3): + for _ in range(2): + assert func(i) == i + assert len(accumulator) == i + 1 + + +def corrupt_single_cache_item(memory): + single_cache_item, = memory.store_backend.get_items() + output_filename = os.path.join(single_cache_item.path, 'output.pkl') + with open(output_filename, 'w') as f: + f.write('garbage') + + +def monkeypatch_cached_func_warn(func, monkeypatch_fixture): + # Need monkeypatch because pytest does not + # capture stdlib logging output (see + # https://github.com/pytest-dev/pytest/issues/2079) + + recorded = [] + + def append_to_record(item): + recorded.append(item) + monkeypatch_fixture.setattr(func, 'warn', append_to_record) + return recorded + + +############################################################################### +# Tests +def test_memory_integration(tmpdir): + """ Simple test of memory lazy evaluation. + """ + accumulator = list() + + # Rmk: this function has the same name than a module-level function, + # thus it serves as a test to see that both are identified + # as different. + def f(arg): + accumulator.append(1) + return arg + + check_identity_lazy(f, accumulator, tmpdir.strpath) + + # Now test clearing + for compress in (False, True): + for mmap_mode in ('r', None): + memory = Memory(location=tmpdir.strpath, verbose=10, + mmap_mode=mmap_mode, compress=compress) + # First clear the cache directory, to check that our code can + # handle that + # NOTE: this line would raise an exception, as the database file is + # still open; we ignore the error since we want to test what + # happens if the directory disappears + shutil.rmtree(tmpdir.strpath, ignore_errors=True) + g = memory.cache(f) + g(1) + g.clear(warn=False) + current_accumulator = len(accumulator) + out = g(1) + + assert len(accumulator) == current_accumulator + 1 + # Also, check that Memory.eval works similarly + assert memory.eval(f, 1) == out + assert len(accumulator) == current_accumulator + 1 + + # Now do a smoke test with a function defined in __main__, as the name + # mangling rules are more complex + f.__module__ = '__main__' + memory = Memory(location=tmpdir.strpath, verbose=0) + memory.cache(f)(1) + + +@parametrize("call_before_reducing", [True, False]) +def test_parallel_call_cached_function_defined_in_jupyter( + tmpdir, call_before_reducing +): + # Calling an interactively defined memory.cache()'d function inside a + # Parallel call used to clear the existing cache related to the said + # function (https://github.com/joblib/joblib/issues/1035) + + # This tests checks that this is no longer the case. + + # TODO: test that the cache related to the function cache persists across + # ipython sessions (provided that no code change were made to the + # function's source)? + + # The first part of the test makes the necessary low-level calls to emulate + # the definition of a function in an jupyter notebook cell. Joblib has + # some custom code to treat functions defined specifically in jupyter + # notebooks/ipython session -- we want to test this code, which requires + # the emulation to be rigorous. + for session_no in [0, 1]: + ipython_cell_source = ''' + def f(x): + return x + ''' + + ipython_cell_id = ''.format(session_no) + + exec( + compile( + textwrap.dedent(ipython_cell_source), + filename=ipython_cell_id, + mode='exec' + ) + ) + # f is now accessible in the locals mapping - but for some unknown + # reason, f = locals()['f'] throws a KeyError at runtime, we need to + # bind locals()['f'] to a different name in the local namespace + aliased_f = locals()['f'] + aliased_f.__module__ = "__main__" + + # Preliminary sanity checks, and tests checking that joblib properly + # identified f as an interactive function defined in a jupyter notebook + assert aliased_f(1) == 1 + assert aliased_f.__code__.co_filename == ipython_cell_id + + memory = Memory(location=tmpdir.strpath, verbose=0) + cached_f = memory.cache(aliased_f) + + assert len(os.listdir(tmpdir / 'joblib')) == 1 + f_cache_relative_directory = os.listdir(tmpdir / 'joblib')[0] + assert 'ipython-input' in f_cache_relative_directory + + f_cache_directory = tmpdir / 'joblib' / f_cache_relative_directory + + if session_no == 0: + # The cache should be empty as cached_f has not been called yet. + assert os.listdir(f_cache_directory) == ['f'] + assert os.listdir(f_cache_directory / 'f') == [] + + if call_before_reducing: + cached_f(3) + # Two files were just created, func_code.py, and a folder + # containing the information (inputs hash/ouptput) of + # cached_f(3) + assert len(os.listdir(f_cache_directory / 'f')) == 2 + + # Now, testing #1035: when calling a cached function, joblib + # used to dynamically inspect the underlying function to + # extract its source code (to verify it matches the source code + # of the function as last inspected by joblib) -- however, + # source code introspection fails for dynamic functions sent to + # child processes - which would eventually make joblib clear + # the cache associated to f + res = Parallel(n_jobs=2)(delayed(cached_f)(i) for i in [1, 2]) + else: + # Submit the function to the joblib child processes, although + # the function has never been called in the parent yet. This + # triggers a specific code branch inside + # MemorizedFunc.__reduce__. + res = Parallel(n_jobs=2)(delayed(cached_f)(i) for i in [1, 2]) + assert len(os.listdir(f_cache_directory / 'f')) == 3 + + cached_f(3) + + # Making sure f's cache does not get cleared after the parallel + # calls, and contains ALL cached functions calls (f(1), f(2), f(3)) + # and 'func_code.py' + assert len(os.listdir(f_cache_directory / 'f')) == 4 + else: + # For the second session, there should be an already existing cache + assert len(os.listdir(f_cache_directory / 'f')) == 4 + + cached_f(3) + + # The previous cache should not be invalidated after calling the + # function in a new session + assert len(os.listdir(f_cache_directory / 'f')) == 4 + + +def test_no_memory(): + """ Test memory with location=None: no memoize """ + accumulator = list() + + def ff(arg): + accumulator.append(1) + return arg + + memory = Memory(location=None, verbose=0) + gg = memory.cache(ff) + for _ in range(4): + current_accumulator = len(accumulator) + gg(1) + assert len(accumulator) == current_accumulator + 1 + + +def test_memory_kwarg(tmpdir): + " Test memory with a function with keyword arguments." + accumulator = list() + + def g(arg1=None, arg2=1): + accumulator.append(1) + return arg1 + + check_identity_lazy(g, accumulator, tmpdir.strpath) + + memory = Memory(location=tmpdir.strpath, verbose=0) + g = memory.cache(g) + # Smoke test with an explicit keyword argument: + assert g(arg1=30, arg2=2) == 30 + + +def test_memory_lambda(tmpdir): + " Test memory with a function with a lambda." + accumulator = list() + + def helper(x): + """ A helper function to define l as a lambda. + """ + accumulator.append(1) + return x + + check_identity_lazy(lambda x: helper(x), accumulator, tmpdir.strpath) + + +def test_memory_name_collision(tmpdir): + " Check that name collisions with functions will raise warnings" + memory = Memory(location=tmpdir.strpath, verbose=0) + + @memory.cache + def name_collision(x): + """ A first function called name_collision + """ + return x + + a = name_collision + + @memory.cache + def name_collision(x): + """ A second function called name_collision + """ + return x + + b = name_collision + + with warns(JobLibCollisionWarning) as warninfo: + a(1) + b(1) + + assert len(warninfo) == 1 + assert "collision" in str(warninfo[0].message) + + +def test_memory_warning_lambda_collisions(tmpdir): + # Check that multiple use of lambda will raise collisions + memory = Memory(location=tmpdir.strpath, verbose=0) + a = memory.cache(lambda x: x) + b = memory.cache(lambda x: x + 1) + + with warns(JobLibCollisionWarning) as warninfo: + assert a(0) == 0 + assert b(1) == 2 + assert a(1) == 1 + + # In recent Python versions, we can retrieve the code of lambdas, + # thus nothing is raised + assert len(warninfo) == 4 + + +def test_memory_warning_collision_detection(tmpdir): + # Check that collisions impossible to detect will raise appropriate + # warnings. + memory = Memory(location=tmpdir.strpath, verbose=0) + a1 = eval('lambda x: x') + a1 = memory.cache(a1) + b1 = eval('lambda x: x+1') + b1 = memory.cache(b1) + + with warns(JobLibCollisionWarning) as warninfo: + a1(1) + b1(1) + a1(0) + + assert len(warninfo) == 2 + assert "cannot detect" in str(warninfo[0].message).lower() + + +def test_memory_partial(tmpdir): + " Test memory with functools.partial." + accumulator = list() + + def func(x, y): + """ A helper function to define l as a lambda. + """ + accumulator.append(1) + return y + + import functools + function = functools.partial(func, 1) + + check_identity_lazy(function, accumulator, tmpdir.strpath) + + +def test_memory_eval(tmpdir): + " Smoke test memory with a function with a function defined in an eval." + memory = Memory(location=tmpdir.strpath, verbose=0) + + m = eval('lambda x: x') + mm = memory.cache(m) + + assert mm(1) == 1 + + +def count_and_append(x=[]): + """ A function with a side effect in its arguments. + + Return the length of its argument and append one element. + """ + len_x = len(x) + x.append(None) + return len_x + + +def test_argument_change(tmpdir): + """ Check that if a function has a side effect in its arguments, it + should use the hash of changing arguments. + """ + memory = Memory(location=tmpdir.strpath, verbose=0) + func = memory.cache(count_and_append) + # call the function for the first time, is should cache it with + # argument x=[] + assert func() == 0 + # the second time the argument is x=[None], which is not cached + # yet, so the functions should be called a second time + assert func() == 1 + + +@with_numpy +@parametrize('mmap_mode', [None, 'r']) +def test_memory_numpy(tmpdir, mmap_mode): + " Test memory with a function with numpy arrays." + accumulator = list() + + def n(arg=None): + accumulator.append(1) + return arg + + memory = Memory(location=tmpdir.strpath, mmap_mode=mmap_mode, + verbose=0) + cached_n = memory.cache(n) + + rnd = np.random.RandomState(0) + for i in range(3): + a = rnd.random_sample((10, 10)) + for _ in range(3): + assert np.all(cached_n(a) == a) + assert len(accumulator) == i + 1 + + +@with_numpy +def test_memory_numpy_check_mmap_mode(tmpdir, monkeypatch): + """Check that mmap_mode is respected even at the first call""" + + memory = Memory(location=tmpdir.strpath, mmap_mode='r', verbose=0) + + @memory.cache() + def twice(a): + return a * 2 + + a = np.ones(3) + + b = twice(a) + c = twice(a) + + assert isinstance(c, np.memmap) + assert c.mode == 'r' + + assert isinstance(b, np.memmap) + assert b.mode == 'r' + + # Corrupts the file, Deleting b and c mmaps + # is necessary to be able edit the file + del b + del c + gc.collect() + corrupt_single_cache_item(memory) + + # Make sure that corrupting the file causes recomputation and that + # a warning is issued. + recorded_warnings = monkeypatch_cached_func_warn(twice, monkeypatch) + d = twice(a) + assert len(recorded_warnings) == 1 + exception_msg = 'Exception while loading results' + assert exception_msg in recorded_warnings[0] + # Asserts that the recomputation returns a mmap + assert isinstance(d, np.memmap) + assert d.mode == 'r' + + +def test_memory_exception(tmpdir): + """ Smoketest the exception handling of Memory. + """ + memory = Memory(location=tmpdir.strpath, verbose=0) + + class MyException(Exception): + pass + + @memory.cache + def h(exc=0): + if exc: + raise MyException + + # Call once, to initialise the cache + h() + + for _ in range(3): + # Call 3 times, to be sure that the Exception is always raised + with raises(MyException): + h(1) + + +def test_memory_ignore(tmpdir): + " Test the ignore feature of memory " + memory = Memory(location=tmpdir.strpath, verbose=0) + accumulator = list() + + @memory.cache(ignore=['y']) + def z(x, y=1): + accumulator.append(1) + + assert z.ignore == ['y'] + + z(0, y=1) + assert len(accumulator) == 1 + z(0, y=1) + assert len(accumulator) == 1 + z(0, y=2) + assert len(accumulator) == 1 + + +def test_memory_ignore_decorated(tmpdir): + " Test the ignore feature of memory on a decorated function " + memory = Memory(location=tmpdir.strpath, verbose=0) + accumulator = list() + + def decorate(f): + @functools.wraps(f) + def wrapped(*args, **kwargs): + return f(*args, **kwargs) + return wrapped + + @memory.cache(ignore=['y']) + @decorate + def z(x, y=1): + accumulator.append(1) + + assert z.ignore == ['y'] + + z(0, y=1) + assert len(accumulator) == 1 + z(0, y=1) + assert len(accumulator) == 1 + z(0, y=2) + assert len(accumulator) == 1 + + +def test_memory_args_as_kwargs(tmpdir): + """Non-regression test against 0.12.0 changes. + + https://github.com/joblib/joblib/pull/751 + """ + memory = Memory(location=tmpdir.strpath, verbose=0) + + @memory.cache + def plus_one(a): + return a + 1 + + # It's possible to call a positional arg as a kwarg. + assert plus_one(1) == 2 + assert plus_one(a=1) == 2 + + # However, a positional argument that joblib hadn't seen + # before would cause a failure if it was passed as a kwarg. + assert plus_one(a=2) == 3 + + +@parametrize('ignore, verbose, mmap_mode', [(['x'], 100, 'r'), + ([], 10, None)]) +def test_partial_decoration(tmpdir, ignore, verbose, mmap_mode): + "Check cache may be called with kwargs before decorating" + memory = Memory(location=tmpdir.strpath, verbose=0) + + @memory.cache(ignore=ignore, verbose=verbose, mmap_mode=mmap_mode) + def z(x): + pass + + assert z.ignore == ignore + assert z._verbose == verbose + assert z.mmap_mode == mmap_mode + + +def test_func_dir(tmpdir): + # Test the creation of the memory cache directory for the function. + memory = Memory(location=tmpdir.strpath, verbose=0) + path = __name__.split('.') + path.append('f') + path = tmpdir.join('joblib', *path).strpath + + g = memory.cache(f) + # Test that the function directory is created on demand + func_id = _build_func_identifier(f) + location = os.path.join(g.store_backend.location, func_id) + assert location == path + assert os.path.exists(path) + assert memory.location == os.path.dirname(g.store_backend.location) + + # Test that the code is stored. + # For the following test to be robust to previous execution, we clear + # the in-memory store + _FUNCTION_HASHES.clear() + assert not g._check_previous_func_code() + assert os.path.exists(os.path.join(path, 'func_code.py')) + assert g._check_previous_func_code() + + # Test the robustness to failure of loading previous results. + args_id = g._get_args_id(1) + output_dir = os.path.join(g.store_backend.location, g.func_id, args_id) + a = g(1) + assert os.path.exists(output_dir) + os.remove(os.path.join(output_dir, 'output.pkl')) + assert a == g(1) + + +def test_persistence(tmpdir): + # Test the memorized functions can be pickled and restored. + memory = Memory(location=tmpdir.strpath, verbose=0) + g = memory.cache(f) + output = g(1) + + h = pickle.loads(pickle.dumps(g)) + + args_id = h._get_args_id(1) + output_dir = os.path.join(h.store_backend.location, h.func_id, args_id) + assert os.path.exists(output_dir) + assert output == h.store_backend.load_item([h.func_id, args_id]) + memory2 = pickle.loads(pickle.dumps(memory)) + assert memory.store_backend.location == memory2.store_backend.location + + # Smoke test that pickling a memory with location=None works + memory = Memory(location=None, verbose=0) + pickle.loads(pickle.dumps(memory)) + g = memory.cache(f) + gp = pickle.loads(pickle.dumps(g)) + gp(1) + + +def test_check_call_in_cache(tmpdir): + for func in (MemorizedFunc(f, tmpdir.strpath), + Memory(location=tmpdir.strpath, verbose=0).cache(f)): + result = func.check_call_in_cache(2) + assert not result + assert isinstance(result, bool) + assert func(2) == 5 + result = func.check_call_in_cache(2) + assert result + assert isinstance(result, bool) + func.clear() + + +def test_call_and_shelve(tmpdir): + # Test MemorizedFunc outputting a reference to cache. + + for func, Result in zip((MemorizedFunc(f, tmpdir.strpath), + NotMemorizedFunc(f), + Memory(location=tmpdir.strpath, + verbose=0).cache(f), + Memory(location=None).cache(f), + ), + (MemorizedResult, NotMemorizedResult, + MemorizedResult, NotMemorizedResult)): + assert func(2) == 5 + result = func.call_and_shelve(2) + assert isinstance(result, Result) + assert result.get() == 5 + + result.clear() + with raises(KeyError): + result.get() + result.clear() # Do nothing if there is no cache. + + +def test_call_and_shelve_argument_hash(tmpdir): + # Verify that a warning is raised when accessing arguments_hash + # attribute from MemorizedResult + func = Memory(location=tmpdir.strpath, verbose=0).cache(f) + result = func.call_and_shelve(2) + assert isinstance(result, MemorizedResult) + with warns(DeprecationWarning) as w: + assert result.argument_hash == result.args_id + assert len(w) == 1 + assert "The 'argument_hash' attribute has been deprecated" \ + in str(w[-1].message) + + +def test_call_and_shelve_lazily_load_stored_result(tmpdir): + """Check call_and_shelve only load stored data if needed.""" + test_access_time_file = tmpdir.join('test_access') + test_access_time_file.write('test_access') + test_access_time = os.stat(test_access_time_file.strpath).st_atime + # check file system access time stats resolution is lower than test wait + # timings. + time.sleep(0.5) + assert test_access_time_file.read() == 'test_access' + + if test_access_time == os.stat(test_access_time_file.strpath).st_atime: + # Skip this test when access time cannot be retrieved with enough + # precision from the file system (e.g. NTFS on windows). + pytest.skip("filesystem does not support fine-grained access time " + "attribute") + + memory = Memory(location=tmpdir.strpath, verbose=0) + func = memory.cache(f) + args_id = func._get_args_id(2) + result_path = os.path.join(memory.store_backend.location, + func.func_id, args_id, 'output.pkl') + assert func(2) == 5 + first_access_time = os.stat(result_path).st_atime + time.sleep(1) + + # Should not access the stored data + result = func.call_and_shelve(2) + assert isinstance(result, MemorizedResult) + assert os.stat(result_path).st_atime == first_access_time + time.sleep(1) + + # Read the stored data => last access time is greater than first_access + assert result.get() == 5 + assert os.stat(result_path).st_atime > first_access_time + + +def test_memorized_pickling(tmpdir): + for func in (MemorizedFunc(f, tmpdir.strpath), NotMemorizedFunc(f)): + filename = tmpdir.join('pickling_test.dat').strpath + result = func.call_and_shelve(2) + with open(filename, 'wb') as fp: + pickle.dump(result, fp) + with open(filename, 'rb') as fp: + result2 = pickle.load(fp) + assert result2.get() == result.get() + os.remove(filename) + + +def test_memorized_repr(tmpdir): + func = MemorizedFunc(f, tmpdir.strpath) + result = func.call_and_shelve(2) + + func2 = MemorizedFunc(f, tmpdir.strpath) + result2 = func2.call_and_shelve(2) + assert result.get() == result2.get() + assert repr(func) == repr(func2) + + # Smoke test with NotMemorizedFunc + func = NotMemorizedFunc(f) + repr(func) + repr(func.call_and_shelve(2)) + + # Smoke test for message output (increase code coverage) + func = MemorizedFunc(f, tmpdir.strpath, verbose=11, timestamp=time.time()) + result = func.call_and_shelve(11) + result.get() + + func = MemorizedFunc(f, tmpdir.strpath, verbose=11) + result = func.call_and_shelve(11) + result.get() + + func = MemorizedFunc(f, tmpdir.strpath, verbose=5, timestamp=time.time()) + result = func.call_and_shelve(11) + result.get() + + func = MemorizedFunc(f, tmpdir.strpath, verbose=5) + result = func.call_and_shelve(11) + result.get() + + +def test_memory_file_modification(capsys, tmpdir, monkeypatch): + # Test that modifying a Python file after loading it does not lead to + # Recomputation + dir_name = tmpdir.mkdir('tmp_import').strpath + filename = os.path.join(dir_name, 'tmp_joblib_.py') + content = 'def f(x):\n print(x)\n return x\n' + with open(filename, 'w') as module_file: + module_file.write(content) + + # Load the module: + monkeypatch.syspath_prepend(dir_name) + import tmp_joblib_ as tmp + + memory = Memory(location=tmpdir.strpath, verbose=0) + f = memory.cache(tmp.f) + # First call f a few times + f(1) + f(2) + f(1) + + # Now modify the module where f is stored without modifying f + with open(filename, 'w') as module_file: + module_file.write('\n\n' + content) + + # And call f a couple more times + f(1) + f(1) + + # Flush the .pyc files + shutil.rmtree(dir_name) + os.mkdir(dir_name) + # Now modify the module where f is stored, modifying f + content = 'def f(x):\n print("x=%s" % x)\n return x\n' + with open(filename, 'w') as module_file: + module_file.write(content) + + # And call f more times prior to reloading: the cache should not be + # invalidated at this point as the active function definition has not + # changed in memory yet. + f(1) + f(1) + + # Now reload + sys.stdout.write('Reloading\n') + sys.modules.pop('tmp_joblib_') + import tmp_joblib_ as tmp + f = memory.cache(tmp.f) + + # And call f more times + f(1) + f(1) + + out, err = capsys.readouterr() + assert out == '1\n2\nReloading\nx=1\n' + + +def _function_to_cache(a, b): + # Just a place holder function to be mutated by tests + pass + + +def _sum(a, b): + return a + b + + +def _product(a, b): + return a * b + + +def test_memory_in_memory_function_code_change(tmpdir): + _function_to_cache.__code__ = _sum.__code__ + + memory = Memory(location=tmpdir.strpath, verbose=0) + f = memory.cache(_function_to_cache) + + assert f(1, 2) == 3 + assert f(1, 2) == 3 + + with warns(JobLibCollisionWarning): + # Check that inline function modification triggers a cache invalidation + _function_to_cache.__code__ = _product.__code__ + assert f(1, 2) == 2 + assert f(1, 2) == 2 + + +def test_clear_memory_with_none_location(): + memory = Memory(location=None) + memory.clear() + + +def func_with_kwonly_args(a, b, *, kw1='kw1', kw2='kw2'): + return a, b, kw1, kw2 + + +def func_with_signature(a: int, b: float) -> float: + return a + b + + +def test_memory_func_with_kwonly_args(tmpdir): + memory = Memory(location=tmpdir.strpath, verbose=0) + func_cached = memory.cache(func_with_kwonly_args) + + assert func_cached(1, 2, kw1=3) == (1, 2, 3, 'kw2') + + # Making sure that providing a keyword-only argument by + # position raises an exception + with raises(ValueError) as excinfo: + func_cached(1, 2, 3, kw2=4) + excinfo.match("Keyword-only parameter 'kw1' was passed as positional " + "parameter") + + # Keyword-only parameter passed by position with cached call + # should still raise ValueError + func_cached(1, 2, kw1=3, kw2=4) + + with raises(ValueError) as excinfo: + func_cached(1, 2, 3, kw2=4) + excinfo.match("Keyword-only parameter 'kw1' was passed as positional " + "parameter") + + # Test 'ignore' parameter + func_cached = memory.cache(func_with_kwonly_args, ignore=['kw2']) + assert func_cached(1, 2, kw1=3, kw2=4) == (1, 2, 3, 4) + assert func_cached(1, 2, kw1=3, kw2='ignored') == (1, 2, 3, 4) + + +def test_memory_func_with_signature(tmpdir): + memory = Memory(location=tmpdir.strpath, verbose=0) + func_cached = memory.cache(func_with_signature) + + assert func_cached(1, 2.) == 3. + + +def _setup_toy_cache(tmpdir, num_inputs=10): + memory = Memory(location=tmpdir.strpath, verbose=0) + + @memory.cache() + def get_1000_bytes(arg): + return 'a' * 1000 + + inputs = list(range(num_inputs)) + for arg in inputs: + get_1000_bytes(arg) + + func_id = _build_func_identifier(get_1000_bytes) + hash_dirnames = [get_1000_bytes._get_args_id(arg) + for arg in inputs] + + full_hashdirs = [os.path.join(get_1000_bytes.store_backend.location, + func_id, dirname) + for dirname in hash_dirnames] + return memory, full_hashdirs, get_1000_bytes + + +def test__get_items(tmpdir): + memory, expected_hash_dirs, _ = _setup_toy_cache(tmpdir) + items = memory.store_backend.get_items() + hash_dirs = [ci.path for ci in items] + assert set(hash_dirs) == set(expected_hash_dirs) + + def get_files_size(directory): + full_paths = [os.path.join(directory, fn) + for fn in os.listdir(directory)] + return sum(os.path.getsize(fp) for fp in full_paths) + + expected_hash_cache_sizes = [get_files_size(hash_dir) + for hash_dir in hash_dirs] + hash_cache_sizes = [ci.size for ci in items] + assert hash_cache_sizes == expected_hash_cache_sizes + + output_filenames = [os.path.join(hash_dir, 'output.pkl') + for hash_dir in hash_dirs] + + expected_last_accesses = [ + datetime.datetime.fromtimestamp(os.path.getatime(fn)) + for fn in output_filenames] + last_accesses = [ci.last_access for ci in items] + assert last_accesses == expected_last_accesses + + +def test__get_items_to_delete(tmpdir): + # test empty cache + memory, _, _ = _setup_toy_cache(tmpdir, num_inputs=0) + items_to_delete = memory.store_backend._get_items_to_delete('1K') + assert items_to_delete == [] + + memory, expected_hash_cachedirs, _ = _setup_toy_cache(tmpdir) + items = memory.store_backend.get_items() + # bytes_limit set to keep only one cache item (each hash cache + # folder is about 1000 bytes + metadata) + items_to_delete = memory.store_backend._get_items_to_delete('2K') + nb_hashes = len(expected_hash_cachedirs) + assert set.issubset(set(items_to_delete), set(items)) + assert len(items_to_delete) == nb_hashes - 1 + + # Sanity check bytes_limit=2048 is the same as bytes_limit='2K' + items_to_delete_2048b = memory.store_backend._get_items_to_delete(2048) + assert sorted(items_to_delete) == sorted(items_to_delete_2048b) + + # bytes_limit greater than the size of the cache + items_to_delete_empty = memory.store_backend._get_items_to_delete('1M') + assert items_to_delete_empty == [] + + # All the cache items need to be deleted + bytes_limit_too_small = 500 + items_to_delete_500b = memory.store_backend._get_items_to_delete( + bytes_limit_too_small + ) + assert set(items_to_delete_500b), set(items) + + # Test LRU property: surviving cache items should all have a more + # recent last_access that the ones that have been deleted + items_to_delete_6000b = memory.store_backend._get_items_to_delete(6000) + surviving_items = set(items).difference(items_to_delete_6000b) + + assert (max(ci.last_access for ci in items_to_delete_6000b) <= + min(ci.last_access for ci in surviving_items)) + + +def test_memory_reduce_size_bytes_limit(tmpdir): + memory, _, _ = _setup_toy_cache(tmpdir) + ref_cache_items = memory.store_backend.get_items() + + # By default memory.bytes_limit is None and reduce_size is a noop + memory.reduce_size() + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # No cache items deleted if bytes_limit greater than the size of + # the cache + memory.reduce_size(bytes_limit='1M') + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # bytes_limit is set so that only two cache items are kept + memory.reduce_size(bytes_limit='3K') + cache_items = memory.store_backend.get_items() + assert set.issubset(set(cache_items), set(ref_cache_items)) + assert len(cache_items) == 2 + + # bytes_limit set so that no cache item is kept + bytes_limit_too_small = 500 + memory.reduce_size(bytes_limit=bytes_limit_too_small) + cache_items = memory.store_backend.get_items() + assert cache_items == [] + + +def test_memory_reduce_size_items_limit(tmpdir): + memory, _, _ = _setup_toy_cache(tmpdir) + ref_cache_items = memory.store_backend.get_items() + + # By default reduce_size is a noop + memory.reduce_size() + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # No cache items deleted if items_limit greater than the size of + # the cache + memory.reduce_size(items_limit=10) + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # items_limit is set so that only two cache items are kept + memory.reduce_size(items_limit=2) + cache_items = memory.store_backend.get_items() + assert set.issubset(set(cache_items), set(ref_cache_items)) + assert len(cache_items) == 2 + + # item_limit set so that no cache item is kept + memory.reduce_size(items_limit=0) + cache_items = memory.store_backend.get_items() + assert cache_items == [] + + +def test_memory_reduce_size_age_limit(tmpdir): + import time + import datetime + memory, _, put_cache = _setup_toy_cache(tmpdir) + ref_cache_items = memory.store_backend.get_items() + + # By default reduce_size is a noop + memory.reduce_size() + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # No cache items deleted if age_limit big. + memory.reduce_size(age_limit=datetime.timedelta(days=1)) + cache_items = memory.store_backend.get_items() + assert sorted(ref_cache_items) == sorted(cache_items) + + # age_limit is set so that only two cache items are kept + time.sleep(1) + put_cache(-1) + put_cache(-2) + memory.reduce_size(age_limit=datetime.timedelta(seconds=1)) + cache_items = memory.store_backend.get_items() + assert not set.issubset(set(cache_items), set(ref_cache_items)) + assert len(cache_items) == 2 + + # age_limit set so that no cache item is kept + memory.reduce_size(age_limit=datetime.timedelta(seconds=0)) + cache_items = memory.store_backend.get_items() + assert cache_items == [] + + +def test_memory_clear(tmpdir): + memory, _, g = _setup_toy_cache(tmpdir) + memory.clear() + + assert os.listdir(memory.store_backend.location) == [] + + # Check that the cache for functions hash is also reset. + assert not g._check_previous_func_code(stacklevel=4) + + +def fast_func_with_complex_output(): + complex_obj = ['a' * 1000] * 1000 + return complex_obj + + +def fast_func_with_conditional_complex_output(complex_output=True): + complex_obj = {str(i): i for i in range(int(1e5))} + return complex_obj if complex_output else 'simple output' + + +@with_multiprocessing +def test_cached_function_race_condition_when_persisting_output(tmpdir, capfd): + # Test race condition where multiple processes are writing into + # the same output.pkl. See + # https://github.com/joblib/joblib/issues/490 for more details. + memory = Memory(location=tmpdir.strpath) + func_cached = memory.cache(fast_func_with_complex_output) + + Parallel(n_jobs=2)(delayed(func_cached)() for i in range(3)) + + stdout, stderr = capfd.readouterr() + + # Checking both stdout and stderr (ongoing PR #434 may change + # logging destination) to make sure there is no exception while + # loading the results + exception_msg = 'Exception while loading results' + assert exception_msg not in stdout + assert exception_msg not in stderr + + +@with_multiprocessing +def test_cached_function_race_condition_when_persisting_output_2(tmpdir, + capfd): + # Test race condition in first attempt at solving + # https://github.com/joblib/joblib/issues/490. The race condition + # was due to the delay between seeing the cache directory created + # (interpreted as the result being cached) and the output.pkl being + # pickled. + memory = Memory(location=tmpdir.strpath) + func_cached = memory.cache(fast_func_with_conditional_complex_output) + + Parallel(n_jobs=2)(delayed(func_cached)(True if i % 2 == 0 else False) + for i in range(3)) + + stdout, stderr = capfd.readouterr() + + # Checking both stdout and stderr (ongoing PR #434 may change + # logging destination) to make sure there is no exception while + # loading the results + exception_msg = 'Exception while loading results' + assert exception_msg not in stdout + assert exception_msg not in stderr + + +def test_memory_recomputes_after_an_error_while_loading_results( + tmpdir, monkeypatch): + memory = Memory(location=tmpdir.strpath) + + def func(arg): + # This makes sure that the timestamp returned by two calls of + # func are different. This is needed on Windows where + # time.time resolution may not be accurate enough + time.sleep(0.01) + return arg, time.time() + + cached_func = memory.cache(func) + input_arg = 'arg' + arg, timestamp = cached_func(input_arg) + + # Make sure the function is correctly cached + assert arg == input_arg + + # Corrupting output.pkl to make sure that an error happens when + # loading the cached result + corrupt_single_cache_item(memory) + + # Make sure that corrupting the file causes recomputation and that + # a warning is issued. + recorded_warnings = monkeypatch_cached_func_warn(cached_func, monkeypatch) + recomputed_arg, recomputed_timestamp = cached_func(arg) + assert len(recorded_warnings) == 1 + exception_msg = 'Exception while loading results' + assert exception_msg in recorded_warnings[0] + assert recomputed_arg == arg + assert recomputed_timestamp > timestamp + + # Corrupting output.pkl to make sure that an error happens when + # loading the cached result + corrupt_single_cache_item(memory) + reference = cached_func.call_and_shelve(arg) + try: + reference.get() + raise AssertionError( + "It normally not possible to load a corrupted" + " MemorizedResult" + ) + except KeyError as e: + message = "is corrupted" + assert message in str(e.args) + + +class IncompleteStoreBackend(StoreBackendBase): + """This backend cannot be instantiated and should raise a TypeError.""" + pass + + +class DummyStoreBackend(StoreBackendBase): + """A dummy store backend that does nothing.""" + + def _open_item(self, *args, **kwargs): + """Open an item on store.""" + "Does nothing" + + def _item_exists(self, location): + """Check if an item location exists.""" + "Does nothing" + + def _move_item(self, src, dst): + """Move an item from src to dst in store.""" + "Does nothing" + + def create_location(self, location): + """Create location on store.""" + "Does nothing" + + def exists(self, obj): + """Check if an object exists in the store""" + return False + + def clear_location(self, obj): + """Clear object on store""" + "Does nothing" + + def get_items(self): + """Returns the whole list of items available in cache.""" + return [] + + def configure(self, location, *args, **kwargs): + """Configure the store""" + "Does nothing" + + +@parametrize("invalid_prefix", [None, dict(), list()]) +def test_register_invalid_store_backends_key(invalid_prefix): + # verify the right exceptions are raised when passing a wrong backend key. + with raises(ValueError) as excinfo: + register_store_backend(invalid_prefix, None) + excinfo.match(r'Store backend name should be a string*') + + +def test_register_invalid_store_backends_object(): + # verify the right exceptions are raised when passing a wrong backend + # object. + with raises(ValueError) as excinfo: + register_store_backend("fs", None) + excinfo.match(r'Store backend should inherit StoreBackendBase*') + + +def test_memory_default_store_backend(): + # test an unknown backend falls back into a FileSystemStoreBackend + with raises(TypeError) as excinfo: + Memory(location='/tmp/joblib', backend='unknown') + excinfo.match(r"Unknown location*") + + +def test_warning_on_unknown_location_type(): + class NonSupportedLocationClass: + pass + unsupported_location = NonSupportedLocationClass() + + with warns(UserWarning) as warninfo: + _store_backend_factory("local", location=unsupported_location) + + expected_mesage = ("Instantiating a backend using a " + "NonSupportedLocationClass as a location is not " + "supported by joblib") + assert expected_mesage in str(warninfo[0].message) + + +def test_instanciate_incomplete_store_backend(): + # Verify that registering an external incomplete store backend raises an + # exception when one tries to instantiate it. + backend_name = "isb" + register_store_backend(backend_name, IncompleteStoreBackend) + assert (backend_name, IncompleteStoreBackend) in _STORE_BACKENDS.items() + with raises(TypeError) as excinfo: + _store_backend_factory(backend_name, "fake_location") + excinfo.match(r"Can't instantiate abstract class IncompleteStoreBackend " + "(without an implementation for|with) abstract methods*") + + +def test_dummy_store_backend(): + # Verify that registering an external store backend works. + + backend_name = "dsb" + register_store_backend(backend_name, DummyStoreBackend) + assert (backend_name, DummyStoreBackend) in _STORE_BACKENDS.items() + + backend_obj = _store_backend_factory(backend_name, "dummy_location") + assert isinstance(backend_obj, DummyStoreBackend) + + +def test_instanciate_store_backend_with_pathlib_path(): + # Instantiate a FileSystemStoreBackend using a pathlib.Path object + path = pathlib.Path("some_folder") + backend_obj = _store_backend_factory("local", path) + assert backend_obj.location == "some_folder" + + +def test_filesystem_store_backend_repr(tmpdir): + # Verify string representation of a filesystem store backend. + + repr_pattern = 'FileSystemStoreBackend(location="{location}")' + backend = FileSystemStoreBackend() + assert backend.location is None + + repr(backend) # Should not raise an exception + + assert str(backend) == repr_pattern.format(location=None) + + # backend location is passed explicitly via the configure method (called + # by the internal _store_backend_factory function) + backend.configure(tmpdir.strpath) + + assert str(backend) == repr_pattern.format(location=tmpdir.strpath) + + repr(backend) # Should not raise an exception + + +def test_memory_objects_repr(tmpdir): + # Verify printable reprs of MemorizedResult, MemorizedFunc and Memory. + + def my_func(a, b): + return a + b + + memory = Memory(location=tmpdir.strpath, verbose=0) + memorized_func = memory.cache(my_func) + + memorized_func_repr = 'MemorizedFunc(func={func}, location={location})' + + assert str(memorized_func) == memorized_func_repr.format( + func=my_func, + location=memory.store_backend.location) + + memorized_result = memorized_func.call_and_shelve(42, 42) + + memorized_result_repr = ('MemorizedResult(location="{location}", ' + 'func="{func}", args_id="{args_id}")') + + assert str(memorized_result) == memorized_result_repr.format( + location=memory.store_backend.location, + func=memorized_result.func_id, + args_id=memorized_result.args_id) + + assert str(memory) == 'Memory(location={location})'.format( + location=memory.store_backend.location) + + +def test_memorized_result_pickle(tmpdir): + # Verify a MemoryResult object can be pickled/depickled. Non regression + # test introduced following issue + # https://github.com/joblib/joblib/issues/747 + + memory = Memory(location=tmpdir.strpath) + + @memory.cache + def g(x): + return x**2 + + memorized_result = g.call_and_shelve(4) + memorized_result_pickle = pickle.dumps(memorized_result) + memorized_result_loads = pickle.loads(memorized_result_pickle) + + assert memorized_result.store_backend.location == \ + memorized_result_loads.store_backend.location + assert memorized_result.func == memorized_result_loads.func + assert memorized_result.args_id == memorized_result_loads.args_id + assert str(memorized_result) == str(memorized_result_loads) + + +def compare(left, right, ignored_attrs=None): + if ignored_attrs is None: + ignored_attrs = [] + + left_vars = vars(left) + right_vars = vars(right) + assert set(left_vars.keys()) == set(right_vars.keys()) + for attr in left_vars.keys(): + if attr in ignored_attrs: + continue + assert left_vars[attr] == right_vars[attr] + + +@pytest.mark.parametrize('memory_kwargs', + [{'compress': 3, 'verbose': 2}, + {'mmap_mode': 'r', 'verbose': 5, + 'backend_options': {'parameter': 'unused'}}]) +def test_memory_pickle_dump_load(tmpdir, memory_kwargs): + memory = Memory(location=tmpdir.strpath, **memory_kwargs) + + memory_reloaded = pickle.loads(pickle.dumps(memory)) + + # Compare Memory instance before and after pickle roundtrip + compare(memory.store_backend, memory_reloaded.store_backend) + compare(memory, memory_reloaded, + ignored_attrs=set(['store_backend', 'timestamp', '_func_code_id'])) + assert hash(memory) == hash(memory_reloaded) + + func_cached = memory.cache(f) + + func_cached_reloaded = pickle.loads(pickle.dumps(func_cached)) + + # Compare MemorizedFunc instance before/after pickle roundtrip + compare(func_cached.store_backend, func_cached_reloaded.store_backend) + compare(func_cached, func_cached_reloaded, + ignored_attrs=set(['store_backend', 'timestamp', '_func_code_id'])) + assert hash(func_cached) == hash(func_cached_reloaded) + + # Compare MemorizedResult instance before/after pickle roundtrip + memorized_result = func_cached.call_and_shelve(1) + memorized_result_reloaded = pickle.loads(pickle.dumps(memorized_result)) + + compare(memorized_result.store_backend, + memorized_result_reloaded.store_backend) + compare(memorized_result, memorized_result_reloaded, + ignored_attrs=set(['store_backend', 'timestamp', '_func_code_id'])) + assert hash(memorized_result) == hash(memorized_result_reloaded) + + +def test_info_log(tmpdir, caplog): + caplog.set_level(logging.INFO) + x = 3 + + memory = Memory(location=tmpdir.strpath, verbose=20) + + @memory.cache + def f(x): + return x ** 2 + + _ = f(x) + assert "Querying" in caplog.text + caplog.clear() + + memory = Memory(location=tmpdir.strpath, verbose=0) + + @memory.cache + def f(x): + return x ** 2 + + _ = f(x) + assert "Querying" not in caplog.text + caplog.clear() + + +def test_deprecated_bytes_limit(tmpdir): + from joblib import __version__ + if __version__ >= "1.5": + raise DeprecationWarning( + "Bytes limit is deprecated and should be removed by 1.4" + ) + with pytest.warns(DeprecationWarning, match="bytes_limit"): + _ = Memory(location=tmpdir.strpath, bytes_limit='1K') + + +class TestCacheValidationCallback: + "Tests on parameter `cache_validation_callback`" + + @pytest.fixture() + def memory(self, tmp_path): + mem = Memory(location=tmp_path) + yield mem + mem.clear() + + def foo(self, x, d, delay=None): + d["run"] = True + if delay is not None: + time.sleep(delay) + return x * 2 + + def test_invalid_cache_validation_callback(self, memory): + "Test invalid values for `cache_validation_callback" + match = "cache_validation_callback needs to be callable. Got True." + with pytest.raises(ValueError, match=match): + memory.cache(cache_validation_callback=True) + + @pytest.mark.parametrize("consider_cache_valid", [True, False]) + def test_constant_cache_validation_callback( + self, memory, consider_cache_valid + ): + "Test expiry of old results" + f = memory.cache( + self.foo, cache_validation_callback=lambda _: consider_cache_valid, + ignore=["d"] + ) + + d1, d2 = {"run": False}, {"run": False} + assert f(2, d1) == 4 + assert f(2, d2) == 4 + + assert d1["run"] + assert d2["run"] != consider_cache_valid + + def test_memory_only_cache_long_run(self, memory): + "Test cache validity based on run duration." + + def cache_validation_callback(metadata): + duration = metadata['duration'] + if duration > 0.1: + return True + + f = memory.cache( + self.foo, cache_validation_callback=cache_validation_callback, + ignore=["d"] + ) + + # Short run are not cached + d1, d2 = {"run": False}, {"run": False} + assert f(2, d1, delay=0) == 4 + assert f(2, d2, delay=0) == 4 + assert d1["run"] + assert d2["run"] + + # Longer run are cached + d1, d2 = {"run": False}, {"run": False} + assert f(2, d1, delay=0.2) == 4 + assert f(2, d2, delay=0.2) == 4 + assert d1["run"] + assert not d2["run"] + + def test_memory_expires_after(self, memory): + "Test expiry of old cached results" + + f = memory.cache( + self.foo, cache_validation_callback=expires_after(seconds=.3), + ignore=["d"] + ) + + d1, d2, d3 = {"run": False}, {"run": False}, {"run": False} + assert f(2, d1) == 4 + assert f(2, d2) == 4 + time.sleep(.5) + assert f(2, d3) == 4 + + assert d1["run"] + assert not d2["run"] + assert d3["run"] diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory_async.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory_async.py new file mode 100644 index 0000000..ecad0c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_memory_async.py @@ -0,0 +1,149 @@ +import asyncio +import gc +import shutil + +import pytest + +from joblib.memory import (AsyncMemorizedFunc, AsyncNotMemorizedFunc, + MemorizedResult, Memory, NotMemorizedResult) +from joblib.test.common import np, with_numpy +from joblib.testing import raises + +from .test_memory import (corrupt_single_cache_item, + monkeypatch_cached_func_warn) + + +async def check_identity_lazy_async(func, accumulator, location): + """ Similar to check_identity_lazy_async for coroutine functions""" + memory = Memory(location=location, verbose=0) + func = memory.cache(func) + for i in range(3): + for _ in range(2): + value = await func(i) + assert value == i + assert len(accumulator) == i + 1 + + +@pytest.mark.asyncio +async def test_memory_integration_async(tmpdir): + accumulator = list() + + async def f(n): + await asyncio.sleep(0.1) + accumulator.append(1) + return n + + await check_identity_lazy_async(f, accumulator, tmpdir.strpath) + + # Now test clearing + for compress in (False, True): + for mmap_mode in ('r', None): + memory = Memory(location=tmpdir.strpath, verbose=10, + mmap_mode=mmap_mode, compress=compress) + # First clear the cache directory, to check that our code can + # handle that + # NOTE: this line would raise an exception, as the database + # file is still open; we ignore the error since we want to + # test what happens if the directory disappears + shutil.rmtree(tmpdir.strpath, ignore_errors=True) + g = memory.cache(f) + await g(1) + g.clear(warn=False) + current_accumulator = len(accumulator) + out = await g(1) + + assert len(accumulator) == current_accumulator + 1 + # Also, check that Memory.eval works similarly + evaled = await memory.eval(f, 1) + assert evaled == out + assert len(accumulator) == current_accumulator + 1 + + # Now do a smoke test with a function defined in __main__, as the name + # mangling rules are more complex + f.__module__ = '__main__' + memory = Memory(location=tmpdir.strpath, verbose=0) + await memory.cache(f)(1) + + +@pytest.mark.asyncio +async def test_no_memory_async(): + accumulator = list() + + async def ff(x): + await asyncio.sleep(0.1) + accumulator.append(1) + return x + + memory = Memory(location=None, verbose=0) + gg = memory.cache(ff) + for _ in range(4): + current_accumulator = len(accumulator) + await gg(1) + assert len(accumulator) == current_accumulator + 1 + + +@with_numpy +@pytest.mark.asyncio +async def test_memory_numpy_check_mmap_mode_async(tmpdir, monkeypatch): + """Check that mmap_mode is respected even at the first call""" + + memory = Memory(location=tmpdir.strpath, mmap_mode='r', verbose=0) + + @memory.cache() + async def twice(a): + return a * 2 + + a = np.ones(3) + b = await twice(a) + c = await twice(a) + + assert isinstance(c, np.memmap) + assert c.mode == 'r' + + assert isinstance(b, np.memmap) + assert b.mode == 'r' + + # Corrupts the file, Deleting b and c mmaps + # is necessary to be able edit the file + del b + del c + gc.collect() + corrupt_single_cache_item(memory) + + # Make sure that corrupting the file causes recomputation and that + # a warning is issued. + recorded_warnings = monkeypatch_cached_func_warn(twice, monkeypatch) + d = await twice(a) + assert len(recorded_warnings) == 1 + exception_msg = 'Exception while loading results' + assert exception_msg in recorded_warnings[0] + # Asserts that the recomputation returns a mmap + assert isinstance(d, np.memmap) + assert d.mode == 'r' + + +@pytest.mark.asyncio +async def test_call_and_shelve_async(tmpdir): + async def f(x, y=1): + await asyncio.sleep(0.1) + return x ** 2 + y + + # Test MemorizedFunc outputting a reference to cache. + for func, Result in zip((AsyncMemorizedFunc(f, tmpdir.strpath), + AsyncNotMemorizedFunc(f), + Memory(location=tmpdir.strpath, + verbose=0).cache(f), + Memory(location=None).cache(f), + ), + (MemorizedResult, NotMemorizedResult, + MemorizedResult, NotMemorizedResult, + )): + for _ in range(2): + result = await func.call_and_shelve(2) + assert isinstance(result, Result) + assert result.get() == 5 + + result.clear() + with raises(KeyError): + result.get() + result.clear() # Do nothing if there is no cache. diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_missing_multiprocessing.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_missing_multiprocessing.py new file mode 100644 index 0000000..251925c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_missing_multiprocessing.py @@ -0,0 +1,32 @@ +""" +Pyodide and other single-threaded Python builds will be missing the +_multiprocessing module. Test that joblib still works in this environment. +""" + +import os +import subprocess +import sys + + +def test_missing_multiprocessing(tmp_path): + """ + Test that import joblib works even if _multiprocessing is missing. + + pytest has already imported everything from joblib. The most reasonable way + to test importing joblib with modified environment is to invoke a separate + Python process. This also ensures that we don't break other tests by + importing a bad `_multiprocessing` module. + """ + (tmp_path / "_multiprocessing.py").write_text( + 'raise ImportError("No _multiprocessing module!")' + ) + env = dict(os.environ) + # For subprocess, use current sys.path with our custom version of + # multiprocessing inserted. + env["PYTHONPATH"] = ":".join([str(tmp_path)] + sys.path) + subprocess.check_call( + [sys.executable, "-c", + "import joblib, math; " + "joblib.Parallel(n_jobs=1)(" + "joblib.delayed(math.sqrt)(i**2) for i in range(10))" + ], env=env) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_module.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_module.py new file mode 100644 index 0000000..a2257a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_module.py @@ -0,0 +1,53 @@ +import sys +import joblib +from joblib.testing import check_subprocess_call +from joblib.test.common import with_multiprocessing + + +def test_version(): + assert hasattr(joblib, '__version__'), ( + "There are no __version__ argument on the joblib module") + + +@with_multiprocessing +def test_no_start_method_side_effect_on_import(): + # check that importing joblib does not implicitly set the global + # start_method for multiprocessing. + code = """if True: + import joblib + import multiprocessing as mp + # The following line would raise RuntimeError if the + # start_method is already set. + mp.set_start_method("loky") + """ + check_subprocess_call([sys.executable, '-c', code]) + + +@with_multiprocessing +def test_no_semaphore_tracker_on_import(): + # check that importing joblib does not implicitly spawn a resource tracker + # or a semaphore tracker + code = """if True: + import joblib + from multiprocessing import semaphore_tracker + # The following line would raise RuntimeError if the + # start_method is already set. + msg = "multiprocessing.semaphore_tracker has been spawned on import" + assert semaphore_tracker._semaphore_tracker._fd is None, msg""" + if sys.version_info >= (3, 8): + # semaphore_tracker was renamed in Python 3.8: + code = code.replace("semaphore_tracker", "resource_tracker") + check_subprocess_call([sys.executable, '-c', code]) + + +@with_multiprocessing +def test_no_resource_tracker_on_import(): + code = """if True: + import joblib + from joblib.externals.loky.backend import resource_tracker + # The following line would raise RuntimeError if the + # start_method is already set. + msg = "loky.resource_tracker has been spawned on import" + assert resource_tracker._resource_tracker._fd is None, msg + """ + check_subprocess_call([sys.executable, '-c', code]) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle.py new file mode 100644 index 0000000..9fee585 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle.py @@ -0,0 +1,1159 @@ +"""Test the numpy pickler as a replacement of the standard pickler.""" + +import copy +import os +import random +import re +import io +import sys +import warnings +import gzip +import zlib +import bz2 +import pickle +import socket +from contextlib import closing +import mmap +from pathlib import Path + +try: + import lzma +except ImportError: + lzma = None + +import pytest + +from joblib.test.common import np, with_numpy, with_lz4, without_lz4 +from joblib.test.common import with_memory_profiler, memory_used +from joblib.testing import parametrize, raises, warns + +# numpy_pickle is not a drop-in replacement of pickle, as it takes +# filenames instead of open files as arguments. +from joblib import numpy_pickle, register_compressor +from joblib.test import data + +from joblib.numpy_pickle_utils import _IO_BUFFER_SIZE +from joblib.numpy_pickle_utils import _detect_compressor +from joblib.numpy_pickle_utils import _is_numpy_array_byte_order_mismatch +from joblib.numpy_pickle_utils import _ensure_native_byte_order +from joblib.compressor import (_COMPRESSORS, _LZ4_PREFIX, CompressorWrapper, + LZ4_NOT_INSTALLED_ERROR, BinaryZlibFile) + + +############################################################################### +# Define a list of standard types. +# Borrowed from dill, initial author: Micheal McKerns: +# http://dev.danse.us/trac/pathos/browser/dill/dill_test2.py + +typelist = [] + +# testing types +_none = None +typelist.append(_none) +_type = type +typelist.append(_type) +_bool = bool(1) +typelist.append(_bool) +_int = int(1) +typelist.append(_int) +_float = float(1) +typelist.append(_float) +_complex = complex(1) +typelist.append(_complex) +_string = str(1) +typelist.append(_string) +_tuple = () +typelist.append(_tuple) +_list = [] +typelist.append(_list) +_dict = {} +typelist.append(_dict) +_builtin = len +typelist.append(_builtin) + + +def _function(x): + yield x + + +class _class: + def _method(self): + pass + + +class _newclass(object): + def _method(self): + pass + + +typelist.append(_function) +typelist.append(_class) +typelist.append(_newclass) # +_instance = _class() +typelist.append(_instance) +_object = _newclass() +typelist.append(_object) # + + +############################################################################### +# Tests + +@parametrize('compress', [0, 1]) +@parametrize('member', typelist) +def test_standard_types(tmpdir, compress, member): + # Test pickling and saving with standard types. + filename = tmpdir.join('test.pkl').strpath + numpy_pickle.dump(member, filename, compress=compress) + _member = numpy_pickle.load(filename) + # We compare the pickled instance to the reloaded one only if it + # can be compared to a copied one + if member == copy.deepcopy(member): + assert member == _member + + +def test_value_error(): + # Test inverting the input arguments to dump + with raises(ValueError): + numpy_pickle.dump('foo', dict()) + + +@parametrize('wrong_compress', [-1, 10, dict()]) +def test_compress_level_error(wrong_compress): + # Verify that passing an invalid compress argument raises an error. + exception_msg = ('Non valid compress level given: ' + '"{0}"'.format(wrong_compress)) + with raises(ValueError) as excinfo: + numpy_pickle.dump('dummy', 'foo', compress=wrong_compress) + excinfo.match(exception_msg) + + +@with_numpy +@parametrize('compress', [False, True, 0, 3, 'zlib']) +def test_numpy_persistence(tmpdir, compress): + filename = tmpdir.join('test.pkl').strpath + rnd = np.random.RandomState(0) + a = rnd.random_sample((10, 2)) + # We use 'a.T' to have a non C-contiguous array. + for index, obj in enumerate(((a,), (a.T,), (a, a), [a, a, a])): + filenames = numpy_pickle.dump(obj, filename, compress=compress) + + # All is cached in one file + assert len(filenames) == 1 + # Check that only one file was created + assert filenames[0] == filename + # Check that this file does exist + assert os.path.exists(filenames[0]) + + # Unpickle the object + obj_ = numpy_pickle.load(filename) + # Check that the items are indeed arrays + for item in obj_: + assert isinstance(item, np.ndarray) + # And finally, check that all the values are equal. + np.testing.assert_array_equal(np.array(obj), np.array(obj_)) + + # Now test with an array subclass + obj = np.memmap(filename + 'mmap', mode='w+', shape=4, dtype=np.float64) + filenames = numpy_pickle.dump(obj, filename, compress=compress) + # All is cached in one file + assert len(filenames) == 1 + + obj_ = numpy_pickle.load(filename) + if (type(obj) is not np.memmap and + hasattr(obj, '__array_prepare__')): + # We don't reconstruct memmaps + assert isinstance(obj_, type(obj)) + + np.testing.assert_array_equal(obj_, obj) + + # Test with an object containing multiple numpy arrays + obj = ComplexTestObject() + filenames = numpy_pickle.dump(obj, filename, compress=compress) + # All is cached in one file + assert len(filenames) == 1 + + obj_loaded = numpy_pickle.load(filename) + assert isinstance(obj_loaded, type(obj)) + np.testing.assert_array_equal(obj_loaded.array_float, obj.array_float) + np.testing.assert_array_equal(obj_loaded.array_int, obj.array_int) + np.testing.assert_array_equal(obj_loaded.array_obj, obj.array_obj) + + +@with_numpy +def test_numpy_persistence_bufferred_array_compression(tmpdir): + big_array = np.ones((_IO_BUFFER_SIZE + 100), dtype=np.uint8) + filename = tmpdir.join('test.pkl').strpath + numpy_pickle.dump(big_array, filename, compress=True) + arr_reloaded = numpy_pickle.load(filename) + + np.testing.assert_array_equal(big_array, arr_reloaded) + + +@with_numpy +def test_memmap_persistence(tmpdir): + rnd = np.random.RandomState(0) + a = rnd.random_sample(10) + filename = tmpdir.join('test1.pkl').strpath + numpy_pickle.dump(a, filename) + b = numpy_pickle.load(filename, mmap_mode='r') + + assert isinstance(b, np.memmap) + + # Test with an object containing multiple numpy arrays + filename = tmpdir.join('test2.pkl').strpath + obj = ComplexTestObject() + numpy_pickle.dump(obj, filename) + obj_loaded = numpy_pickle.load(filename, mmap_mode='r') + assert isinstance(obj_loaded, type(obj)) + assert isinstance(obj_loaded.array_float, np.memmap) + assert not obj_loaded.array_float.flags.writeable + assert isinstance(obj_loaded.array_int, np.memmap) + assert not obj_loaded.array_int.flags.writeable + # Memory map not allowed for numpy object arrays + assert not isinstance(obj_loaded.array_obj, np.memmap) + np.testing.assert_array_equal(obj_loaded.array_float, + obj.array_float) + np.testing.assert_array_equal(obj_loaded.array_int, + obj.array_int) + np.testing.assert_array_equal(obj_loaded.array_obj, + obj.array_obj) + + # Test we can write in memmapped arrays + obj_loaded = numpy_pickle.load(filename, mmap_mode='r+') + assert obj_loaded.array_float.flags.writeable + obj_loaded.array_float[0:10] = 10.0 + assert obj_loaded.array_int.flags.writeable + obj_loaded.array_int[0:10] = 10 + + obj_reloaded = numpy_pickle.load(filename, mmap_mode='r') + np.testing.assert_array_equal(obj_reloaded.array_float, + obj_loaded.array_float) + np.testing.assert_array_equal(obj_reloaded.array_int, + obj_loaded.array_int) + + # Test w+ mode is caught and the mode has switched to r+ + numpy_pickle.load(filename, mmap_mode='w+') + assert obj_loaded.array_int.flags.writeable + assert obj_loaded.array_int.mode == 'r+' + assert obj_loaded.array_float.flags.writeable + assert obj_loaded.array_float.mode == 'r+' + + +@with_numpy +def test_memmap_persistence_mixed_dtypes(tmpdir): + # loading datastructures that have sub-arrays with dtype=object + # should not prevent memmapping on fixed size dtype sub-arrays. + rnd = np.random.RandomState(0) + a = rnd.random_sample(10) + b = np.array([1, 'b'], dtype=object) + construct = (a, b) + filename = tmpdir.join('test.pkl').strpath + numpy_pickle.dump(construct, filename) + a_clone, b_clone = numpy_pickle.load(filename, mmap_mode='r') + + # the floating point array has been memory mapped + assert isinstance(a_clone, np.memmap) + + # the object-dtype array has been loaded in memory + assert not isinstance(b_clone, np.memmap) + + +@with_numpy +def test_masked_array_persistence(tmpdir): + # The special-case picker fails, because saving masked_array + # not implemented, but it just delegates to the standard pickler. + rnd = np.random.RandomState(0) + a = rnd.random_sample(10) + a = np.ma.masked_greater(a, 0.5) + filename = tmpdir.join('test.pkl').strpath + numpy_pickle.dump(a, filename) + b = numpy_pickle.load(filename, mmap_mode='r') + assert isinstance(b, np.ma.masked_array) + + +@with_numpy +def test_compress_mmap_mode_warning(tmpdir): + # Test the warning in case of compress + mmap_mode + rnd = np.random.RandomState(0) + a = rnd.random_sample(10) + this_filename = tmpdir.join('test.pkl').strpath + numpy_pickle.dump(a, this_filename, compress=1) + with warns(UserWarning) as warninfo: + numpy_pickle.load(this_filename, mmap_mode='r+') + debug_msg = "\n".join([str(w) for w in warninfo]) + warninfo = [w.message for w in warninfo] + assert len(warninfo) == 1, debug_msg + assert ( + str(warninfo[0]) == + 'mmap_mode "r+" is not compatible with compressed ' + f'file {this_filename}. "r+" flag will be ignored.' + ) + + +@with_numpy +@parametrize('cache_size', [None, 0, 10]) +def test_cache_size_warning(tmpdir, cache_size): + # Check deprecation warning raised when cache size is not None + filename = tmpdir.join('test.pkl').strpath + rnd = np.random.RandomState(0) + a = rnd.random_sample((10, 2)) + + warnings.simplefilter("always") + with warnings.catch_warnings(record=True) as warninfo: + numpy_pickle.dump(a, filename, cache_size=cache_size) + expected_nb_warnings = 1 if cache_size is not None else 0 + assert len(warninfo) == expected_nb_warnings + for w in warninfo: + assert w.category == DeprecationWarning + assert (str(w.message) == + "Please do not set 'cache_size' in joblib.dump, this " + "parameter has no effect and will be removed. You " + "used 'cache_size={0}'".format(cache_size)) + + +@with_numpy +@with_memory_profiler +@parametrize('compress', [True, False]) +def test_memory_usage(tmpdir, compress): + # Verify memory stays within expected bounds. + filename = tmpdir.join('test.pkl').strpath + small_array = np.ones((10, 10)) + big_array = np.ones(shape=100 * int(1e6), dtype=np.uint8) + + for obj in (small_array, big_array): + size = obj.nbytes / 1e6 + obj_filename = filename + str(np.random.randint(0, 1000)) + mem_used = memory_used(numpy_pickle.dump, + obj, obj_filename, compress=compress) + + # The memory used to dump the object shouldn't exceed the buffer + # size used to write array chunks (16MB). + write_buf_size = _IO_BUFFER_SIZE + 16 * 1024 ** 2 / 1e6 + assert mem_used <= write_buf_size + + mem_used = memory_used(numpy_pickle.load, obj_filename) + # memory used should be less than array size + buffer size used to + # read the array chunk by chunk. + read_buf_size = 32 + _IO_BUFFER_SIZE # MiB + assert mem_used < size + read_buf_size + + +@with_numpy +def test_compressed_pickle_dump_and_load(tmpdir): + expected_list = [np.arange(5, dtype=np.dtype('i8')), + np.arange(5, dtype=np.dtype('f8')), + np.array([1, 'abc', {'a': 1, 'b': 2}], dtype='O'), + np.arange(256, dtype=np.uint8).tobytes(), + u"C'est l'\xe9t\xe9 !"] + + fname = tmpdir.join('temp.pkl.gz').strpath + + dumped_filenames = numpy_pickle.dump(expected_list, fname, compress=1) + assert len(dumped_filenames) == 1 + result_list = numpy_pickle.load(fname) + for result, expected in zip(result_list, expected_list): + if isinstance(expected, np.ndarray): + expected = _ensure_native_byte_order(expected) + assert result.dtype == expected.dtype + np.testing.assert_equal(result, expected) + else: + assert result == expected + + +def _check_pickle(filename, expected_list, mmap_mode=None): + """Helper function to test joblib pickle content. + + Note: currently only pickles containing an iterable are supported + by this function. + """ + version_match = re.match(r'.+py(\d)(\d).+', filename) + py_version_used_for_writing = int(version_match.group(1)) + + py_version_to_default_pickle_protocol = {2: 2, 3: 3} + pickle_reading_protocol = py_version_to_default_pickle_protocol.get(3, 4) + pickle_writing_protocol = py_version_to_default_pickle_protocol.get( + py_version_used_for_writing, 4) + if pickle_reading_protocol >= pickle_writing_protocol: + try: + with warnings.catch_warnings(record=True) as warninfo: + warnings.simplefilter('always') + warnings.filterwarnings( + 'ignore', module='numpy', + message='The compiler package is deprecated') + result_list = numpy_pickle.load(filename, mmap_mode=mmap_mode) + filename_base = os.path.basename(filename) + expected_nb_deprecation_warnings = 1 if ( + "_0.9" in filename_base or "_0.8.4" in filename_base) else 0 + + expected_nb_user_warnings = 3 if ( + re.search("_0.1.+.pkl$", filename_base) and + mmap_mode is not None) else 0 + expected_nb_warnings = \ + expected_nb_deprecation_warnings + expected_nb_user_warnings + assert len(warninfo) == expected_nb_warnings + + deprecation_warnings = [ + w for w in warninfo if issubclass( + w.category, DeprecationWarning)] + user_warnings = [ + w for w in warninfo if issubclass( + w.category, UserWarning)] + for w in deprecation_warnings: + assert (str(w.message) == + "The file '{0}' has been generated with a joblib " + "version less than 0.10. Please regenerate this " + "pickle file.".format(filename)) + + for w in user_warnings: + escaped_filename = re.escape(filename) + assert re.search( + f"memmapped.+{escaped_filename}.+segmentation fault", + str(w.message)) + + for result, expected in zip(result_list, expected_list): + if isinstance(expected, np.ndarray): + expected = _ensure_native_byte_order(expected) + assert result.dtype == expected.dtype + np.testing.assert_equal(result, expected) + else: + assert result == expected + except Exception as exc: + # When trying to read with python 3 a pickle generated + # with python 2 we expect a user-friendly error + if py_version_used_for_writing == 2: + assert isinstance(exc, ValueError) + message = ('You may be trying to read with ' + 'python 3 a joblib pickle generated with python 2.') + assert message in str(exc) + elif filename.endswith('.lz4') and with_lz4.args[0]: + assert isinstance(exc, ValueError) + assert LZ4_NOT_INSTALLED_ERROR in str(exc) + else: + raise + else: + # Pickle protocol used for writing is too high. We expect a + # "unsupported pickle protocol" error message + try: + numpy_pickle.load(filename) + raise AssertionError('Numpy pickle loading should ' + 'have raised a ValueError exception') + except ValueError as e: + message = 'unsupported pickle protocol: {0}'.format( + pickle_writing_protocol) + assert message in str(e.args) + + +@with_numpy +def test_joblib_pickle_across_python_versions(): + # We need to be specific about dtypes in particular endianness + # because the pickles can be generated on one architecture and + # the tests run on another one. See + # https://github.com/joblib/joblib/issues/279. + expected_list = [np.arange(5, dtype=np.dtype('i8'), ('', '>f8')]), + np.arange(3, dtype=np.dtype('>i8')), + np.arange(3, dtype=np.dtype('>f8'))] + + # Verify the byteorder mismatch is correctly detected. + for array in be_arrays: + if sys.byteorder == 'big': + assert not _is_numpy_array_byte_order_mismatch(array) + else: + assert _is_numpy_array_byte_order_mismatch(array) + converted = _ensure_native_byte_order(array) + if converted.dtype.fields: + for f in converted.dtype.fields.values(): + f[0].byteorder == '=' + else: + assert converted.dtype.byteorder == "=" + + # List of numpy arrays with little endian byteorder. + le_arrays = [np.array([(1, 2.0), (3, 4.0)], + dtype=[('', ' size + np.testing.assert_array_equal(obj, memmaps) + + +def test_register_compressor(tmpdir): + # Check that registering compressor file works. + compressor_name = 'test-name' + compressor_prefix = 'test-prefix' + + class BinaryCompressorTestFile(io.BufferedIOBase): + pass + + class BinaryCompressorTestWrapper(CompressorWrapper): + + def __init__(self): + CompressorWrapper.__init__(self, obj=BinaryCompressorTestFile, + prefix=compressor_prefix) + + register_compressor(compressor_name, BinaryCompressorTestWrapper()) + + assert (_COMPRESSORS[compressor_name].fileobj_factory == + BinaryCompressorTestFile) + assert _COMPRESSORS[compressor_name].prefix == compressor_prefix + + # Remove this dummy compressor file from extra compressors because other + # tests might fail because of this + _COMPRESSORS.pop(compressor_name) + + +@parametrize('invalid_name', [1, (), {}]) +def test_register_compressor_invalid_name(invalid_name): + # Test that registering an invalid compressor name is not allowed. + with raises(ValueError) as excinfo: + register_compressor(invalid_name, None) + excinfo.match("Compressor name should be a string") + + +def test_register_compressor_invalid_fileobj(): + # Test that registering an invalid file object is not allowed. + + class InvalidFileObject(): + pass + + class InvalidFileObjectWrapper(CompressorWrapper): + def __init__(self): + CompressorWrapper.__init__(self, obj=InvalidFileObject, + prefix=b'prefix') + + with raises(ValueError) as excinfo: + register_compressor('invalid', InvalidFileObjectWrapper()) + + excinfo.match("Compressor 'fileobj_factory' attribute should implement " + "the file object interface") + + +class AnotherZlibCompressorWrapper(CompressorWrapper): + + def __init__(self): + CompressorWrapper.__init__(self, obj=BinaryZlibFile, prefix=b'prefix') + + +class StandardLibGzipCompressorWrapper(CompressorWrapper): + + def __init__(self): + CompressorWrapper.__init__(self, obj=gzip.GzipFile, prefix=b'prefix') + + +def test_register_compressor_already_registered(): + # Test registration of existing compressor files. + compressor_name = 'test-name' + + # register a test compressor + register_compressor(compressor_name, AnotherZlibCompressorWrapper()) + + with raises(ValueError) as excinfo: + register_compressor(compressor_name, + StandardLibGzipCompressorWrapper()) + excinfo.match("Compressor '{}' already registered." + .format(compressor_name)) + + register_compressor(compressor_name, StandardLibGzipCompressorWrapper(), + force=True) + + assert compressor_name in _COMPRESSORS + assert _COMPRESSORS[compressor_name].fileobj_factory == gzip.GzipFile + + # Remove this dummy compressor file from extra compressors because other + # tests might fail because of this + _COMPRESSORS.pop(compressor_name) + + +@with_lz4 +def test_lz4_compression(tmpdir): + # Check that lz4 can be used when dependency is available. + import lz4.frame + compressor = 'lz4' + assert compressor in _COMPRESSORS + assert _COMPRESSORS[compressor].fileobj_factory == lz4.frame.LZ4FrameFile + + fname = tmpdir.join('test.pkl').strpath + data = 'test data' + numpy_pickle.dump(data, fname, compress=compressor) + + with open(fname, 'rb') as f: + assert f.read(len(_LZ4_PREFIX)) == _LZ4_PREFIX + assert numpy_pickle.load(fname) == data + + # Test that LZ4 is applied based on file extension + numpy_pickle.dump(data, fname + '.lz4') + with open(fname, 'rb') as f: + assert f.read(len(_LZ4_PREFIX)) == _LZ4_PREFIX + assert numpy_pickle.load(fname) == data + + +@without_lz4 +def test_lz4_compression_without_lz4(tmpdir): + # Check that lz4 cannot be used when dependency is not available. + fname = tmpdir.join('test.nolz4').strpath + data = 'test data' + msg = LZ4_NOT_INSTALLED_ERROR + with raises(ValueError) as excinfo: + numpy_pickle.dump(data, fname, compress='lz4') + excinfo.match(msg) + + with raises(ValueError) as excinfo: + numpy_pickle.dump(data, fname + '.lz4') + excinfo.match(msg) + + +protocols = [pickle.DEFAULT_PROTOCOL] +if pickle.HIGHEST_PROTOCOL != pickle.DEFAULT_PROTOCOL: + protocols.append(pickle.HIGHEST_PROTOCOL) + + +@with_numpy +@parametrize('protocol', protocols) +def test_memmap_alignment_padding(tmpdir, protocol): + # Test that memmaped arrays returned by numpy.load are correctly aligned + fname = tmpdir.join('test.mmap').strpath + + a = np.random.randn(2) + numpy_pickle.dump(a, fname, protocol=protocol) + memmap = numpy_pickle.load(fname, mmap_mode='r') + assert isinstance(memmap, np.memmap) + np.testing.assert_array_equal(a, memmap) + assert ( + memmap.ctypes.data % numpy_pickle.NUMPY_ARRAY_ALIGNMENT_BYTES == 0) + assert memmap.flags.aligned + + array_list = [ + np.random.randn(2), np.random.randn(2), + np.random.randn(2), np.random.randn(2) + ] + + # On Windows OSError 22 if reusing the same path for memmap ... + fname = tmpdir.join('test1.mmap').strpath + numpy_pickle.dump(array_list, fname, protocol=protocol) + l_reloaded = numpy_pickle.load(fname, mmap_mode='r') + + for idx, memmap in enumerate(l_reloaded): + assert isinstance(memmap, np.memmap) + np.testing.assert_array_equal(array_list[idx], memmap) + assert ( + memmap.ctypes.data % numpy_pickle.NUMPY_ARRAY_ALIGNMENT_BYTES == 0) + assert memmap.flags.aligned + + array_dict = { + 'a0': np.arange(2, dtype=np.uint8), + 'a1': np.arange(3, dtype=np.uint8), + 'a2': np.arange(5, dtype=np.uint8), + 'a3': np.arange(7, dtype=np.uint8), + 'a4': np.arange(11, dtype=np.uint8), + 'a5': np.arange(13, dtype=np.uint8), + 'a6': np.arange(17, dtype=np.uint8), + 'a7': np.arange(19, dtype=np.uint8), + 'a8': np.arange(23, dtype=np.uint8), + } + + # On Windows OSError 22 if reusing the same path for memmap ... + fname = tmpdir.join('test2.mmap').strpath + numpy_pickle.dump(array_dict, fname, protocol=protocol) + d_reloaded = numpy_pickle.load(fname, mmap_mode='r') + + for key, memmap in d_reloaded.items(): + assert isinstance(memmap, np.memmap) + np.testing.assert_array_equal(array_dict[key], memmap) + assert ( + memmap.ctypes.data % numpy_pickle.NUMPY_ARRAY_ALIGNMENT_BYTES == 0) + assert memmap.flags.aligned diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_compat.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_compat.py new file mode 100644 index 0000000..9e95393 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_compat.py @@ -0,0 +1,16 @@ +"""Test the old numpy pickler, compatibility version.""" + +# numpy_pickle is not a drop-in replacement of pickle, as it takes +# filenames instead of open files as arguments. +from joblib import numpy_pickle_compat + + +def test_z_file(tmpdir): + # Test saving and loading data with Zfiles. + filename = tmpdir.join('test.pkl').strpath + data = numpy_pickle_compat.asbytes('Foo, \n Bar, baz, \n\nfoobar') + with open(filename, 'wb') as f: + numpy_pickle_compat.write_zfile(f, data) + with open(filename, 'rb') as f: + data_read = numpy_pickle_compat.read_zfile(f) + assert data == data_read diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_utils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_utils.py new file mode 100644 index 0000000..5c414a2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_numpy_pickle_utils.py @@ -0,0 +1,9 @@ +from joblib.compressor import BinaryZlibFile +from joblib.testing import parametrize + + +@parametrize('filename', ['test', u'test']) # testing str and unicode names +def test_binary_zlib_file(tmpdir, filename): + """Testing creation of files depending on the type of the filenames.""" + binary_file = BinaryZlibFile(tmpdir.join(filename).strpath, mode='wb') + binary_file.close() diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_parallel.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_parallel.py new file mode 100644 index 0000000..21f06fa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_parallel.py @@ -0,0 +1,2034 @@ +""" +Test the parallel module. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2010-2011 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import os +import sys +import time +import mmap +import weakref +import warnings +import threading +from traceback import format_exception +from math import sqrt +from time import sleep +from pickle import PicklingError +from contextlib import nullcontext +from multiprocessing import TimeoutError +import pytest + +import joblib +from joblib import parallel +from joblib import dump, load + +from joblib._multiprocessing_helpers import mp + +from joblib.test.common import np, with_numpy +from joblib.test.common import with_multiprocessing +from joblib.test.common import IS_PYPY, force_gc_pypy +from joblib.testing import (parametrize, raises, check_subprocess_call, + skipif, warns) + +if mp is not None: + # Loky is not available if multiprocessing is not + from joblib.externals.loky import get_reusable_executor + +from queue import Queue + +try: + import posix +except ImportError: + posix = None + +try: + from ._openmp_test_helper.parallel_sum import parallel_sum +except ImportError: + parallel_sum = None + +try: + import distributed +except ImportError: + distributed = None + +from joblib._parallel_backends import SequentialBackend +from joblib._parallel_backends import ThreadingBackend +from joblib._parallel_backends import MultiprocessingBackend +from joblib._parallel_backends import ParallelBackendBase +from joblib._parallel_backends import LokyBackend + +from joblib.parallel import Parallel, delayed +from joblib.parallel import parallel_config +from joblib.parallel import parallel_backend +from joblib.parallel import register_parallel_backend +from joblib.parallel import effective_n_jobs, cpu_count + +from joblib.parallel import mp, BACKENDS, DEFAULT_BACKEND + + +RETURN_GENERATOR_BACKENDS = BACKENDS.copy() +RETURN_GENERATOR_BACKENDS.pop("multiprocessing", None) + +ALL_VALID_BACKENDS = [None] + sorted(BACKENDS.keys()) +# Add instances of backend classes deriving from ParallelBackendBase +ALL_VALID_BACKENDS += [BACKENDS[backend_str]() for backend_str in BACKENDS] +if mp is None: + PROCESS_BACKENDS = [] +else: + PROCESS_BACKENDS = ['multiprocessing', 'loky'] +PARALLEL_BACKENDS = PROCESS_BACKENDS + ['threading'] + +if hasattr(mp, 'get_context'): + # Custom multiprocessing context in Python 3.4+ + ALL_VALID_BACKENDS.append(mp.get_context('spawn')) + +DefaultBackend = BACKENDS[DEFAULT_BACKEND] + + +def get_workers(backend): + return getattr(backend, '_pool', getattr(backend, '_workers', None)) + + +def division(x, y): + return x / y + + +def square(x): + return x ** 2 + + +class MyExceptionWithFinickyInit(Exception): + """An exception class with non trivial __init__ + """ + def __init__(self, a, b, c, d): + pass + + +def exception_raiser(x, custom_exception=False): + if x == 7: + raise (MyExceptionWithFinickyInit('a', 'b', 'c', 'd') + if custom_exception else ValueError) + return x + + +def interrupt_raiser(x): + time.sleep(.05) + raise KeyboardInterrupt + + +def f(x, y=0, z=0): + """ A module-level function so that it can be spawn with + multiprocessing. + """ + return x ** 2 + y + z + + +def _active_backend_type(): + return type(parallel.get_active_backend()[0]) + + +def parallel_func(inner_n_jobs, backend): + return Parallel(n_jobs=inner_n_jobs, backend=backend)( + delayed(square)(i) for i in range(3)) + + +############################################################################### +def test_cpu_count(): + assert cpu_count() > 0 + + +def test_effective_n_jobs(): + assert effective_n_jobs() > 0 + + +@parametrize("context", [parallel_config, parallel_backend]) +@pytest.mark.parametrize( + "backend_n_jobs, expected_n_jobs", + [(3, 3), (-1, effective_n_jobs(n_jobs=-1)), (None, 1)], + ids=["positive-int", "negative-int", "None"] +) +@with_multiprocessing +def test_effective_n_jobs_None(context, backend_n_jobs, expected_n_jobs): + # check the number of effective jobs when `n_jobs=None` + # non-regression test for https://github.com/joblib/joblib/issues/984 + with context("threading", n_jobs=backend_n_jobs): + # when using a backend, the default of number jobs will be the one set + # in the backend + assert effective_n_jobs(n_jobs=None) == expected_n_jobs + # without any backend, None will default to a single job + assert effective_n_jobs(n_jobs=None) == 1 + + +############################################################################### +# Test parallel + +@parametrize('backend', ALL_VALID_BACKENDS) +@parametrize('n_jobs', [1, 2, -1, -2]) +@parametrize('verbose', [2, 11, 100]) +def test_simple_parallel(backend, n_jobs, verbose): + assert ([square(x) for x in range(5)] == + Parallel(n_jobs=n_jobs, backend=backend, + verbose=verbose)( + delayed(square)(x) for x in range(5))) + + +@parametrize('backend', ALL_VALID_BACKENDS) +def test_main_thread_renamed_no_warning(backend, monkeypatch): + # Check that no default backend relies on the name of the main thread: + # https://github.com/joblib/joblib/issues/180#issuecomment-253266247 + # Some programs use a different name for the main thread. This is the case + # for uWSGI apps for instance. + monkeypatch.setattr(target=threading.current_thread(), name='name', + value='some_new_name_for_the_main_thread') + + with warnings.catch_warnings(record=True) as warninfo: + results = Parallel(n_jobs=2, backend=backend)( + delayed(square)(x) for x in range(3)) + assert results == [0, 1, 4] + + # Due to the default parameters of LokyBackend, there is a chance that + # warninfo catches Warnings from worker timeouts. We remove it if it exists + warninfo = [w for w in warninfo if "worker timeout" not in str(w.message)] + + # The multiprocessing backend will raise a warning when detecting that is + # started from the non-main thread. Let's check that there is no false + # positive because of the name change. + assert len(warninfo) == 0 + + +def _assert_warning_nested(backend, inner_n_jobs, expected): + with warnings.catch_warnings(record=True) as warninfo: + warnings.simplefilter("always") + parallel_func(backend=backend, inner_n_jobs=inner_n_jobs) + + warninfo = [w.message for w in warninfo] + if expected: + if warninfo: + warnings_are_correct = all( + 'backed parallel loops cannot' in each.args[0] + for each in warninfo + ) + # With Python nogil, when the outer backend is threading, we might + # see more that one warning + warnings_have_the_right_length = ( + len(warninfo) >= 1 if getattr(sys.flags, 'nogil', False) + else len(warninfo) == 1) + return warnings_are_correct and warnings_have_the_right_length + + return False + else: + assert not warninfo + return True + + +@with_multiprocessing +@parametrize('parent_backend,child_backend,expected', [ + ('loky', 'multiprocessing', True), + ('loky', 'loky', False), + ('multiprocessing', 'multiprocessing', True), + ('multiprocessing', 'loky', True), + ('threading', 'multiprocessing', True), + ('threading', 'loky', True), +]) +def test_nested_parallel_warnings(parent_backend, child_backend, expected): + + # no warnings if inner_n_jobs=1 + Parallel(n_jobs=2, backend=parent_backend)( + delayed(_assert_warning_nested)( + backend=child_backend, inner_n_jobs=1, + expected=False) + for _ in range(5)) + + # warnings if inner_n_jobs != 1 and expected + res = Parallel(n_jobs=2, backend=parent_backend)( + delayed(_assert_warning_nested)( + backend=child_backend, inner_n_jobs=2, + expected=expected) + for _ in range(5)) + + # warning handling is not thread safe. One thread might see multiple + # warning or no warning at all. + if parent_backend == "threading": + if IS_PYPY and not any(res): + # Related to joblib#1426, should be removed once it is solved. + pytest.xfail(reason="This test often fails in PyPy.") + assert any(res) + else: + assert all(res) + + +@with_multiprocessing +@parametrize('backend', ['loky', 'multiprocessing', 'threading']) +def test_background_thread_parallelism(backend): + is_run_parallel = [False] + + def background_thread(is_run_parallel): + with warnings.catch_warnings(record=True) as warninfo: + Parallel(n_jobs=2)( + delayed(sleep)(.1) for _ in range(4)) + print(len(warninfo)) + is_run_parallel[0] = len(warninfo) == 0 + + t = threading.Thread(target=background_thread, args=(is_run_parallel,)) + t.start() + t.join() + assert is_run_parallel[0] + + +def nested_loop(backend): + Parallel(n_jobs=2, backend=backend)( + delayed(square)(.01) for _ in range(2)) + + +@parametrize('child_backend', BACKENDS) +@parametrize('parent_backend', BACKENDS) +def test_nested_loop(parent_backend, child_backend): + Parallel(n_jobs=2, backend=parent_backend)( + delayed(nested_loop)(child_backend) for _ in range(2)) + + +def raise_exception(backend): + raise ValueError + + +@with_multiprocessing +def test_nested_loop_with_exception_with_loky(): + with raises(ValueError): + with Parallel(n_jobs=2, backend="loky") as parallel: + parallel([delayed(nested_loop)("loky"), + delayed(raise_exception)("loky")]) + + +def test_mutate_input_with_threads(): + """Input is mutable when using the threading backend""" + q = Queue(maxsize=5) + Parallel(n_jobs=2, backend="threading")( + delayed(q.put)(1) for _ in range(5)) + assert q.full() + + +@parametrize('n_jobs', [1, 2, 3]) +def test_parallel_kwargs(n_jobs): + """Check the keyword argument processing of pmap.""" + lst = range(10) + assert ([f(x, y=1) for x in lst] == + Parallel(n_jobs=n_jobs)(delayed(f)(x, y=1) for x in lst)) + + +@parametrize('backend', PARALLEL_BACKENDS) +def test_parallel_as_context_manager(backend): + lst = range(10) + expected = [f(x, y=1) for x in lst] + + with Parallel(n_jobs=4, backend=backend) as p: + # Internally a pool instance has been eagerly created and is managed + # via the context manager protocol + managed_backend = p._backend + + # We make call with the managed parallel object several times inside + # the managed block: + assert expected == p(delayed(f)(x, y=1) for x in lst) + assert expected == p(delayed(f)(x, y=1) for x in lst) + + # Those calls have all used the same pool instance: + if mp is not None: + assert get_workers(managed_backend) is get_workers(p._backend) + + # As soon as we exit the context manager block, the pool is terminated and + # no longer referenced from the parallel object: + if mp is not None: + assert get_workers(p._backend) is None + + # It's still possible to use the parallel instance in non-managed mode: + assert expected == p(delayed(f)(x, y=1) for x in lst) + if mp is not None: + assert get_workers(p._backend) is None + + +@with_multiprocessing +def test_parallel_pickling(): + """ Check that pmap captures the errors when it is passed an object + that cannot be pickled. + """ + class UnpicklableObject(object): + def __reduce__(self): + raise RuntimeError('123') + + with raises(PicklingError, match=r"the task to send"): + Parallel(n_jobs=2, backend='loky')(delayed(id)( + UnpicklableObject()) for _ in range(10)) + + +@parametrize('backend', PARALLEL_BACKENDS) +def test_parallel_timeout_success(backend): + # Check that timeout isn't thrown when function is fast enough + assert len(Parallel(n_jobs=2, backend=backend, timeout=30)( + delayed(sleep)(0.001) for x in range(10))) == 10 + + +@with_multiprocessing +@parametrize('backend', PARALLEL_BACKENDS) +def test_parallel_timeout_fail(backend): + # Check that timeout properly fails when function is too slow + with raises(TimeoutError): + Parallel(n_jobs=2, backend=backend, timeout=0.01)( + delayed(sleep)(10) for x in range(10)) + + +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_error_capture(backend): + # Check that error are captured, and that correct exceptions + # are raised. + if mp is not None: + with raises(ZeroDivisionError): + Parallel(n_jobs=2, backend=backend)( + [delayed(division)(x, y) + for x, y in zip((0, 1), (1, 0))]) + + with raises(KeyboardInterrupt): + Parallel(n_jobs=2, backend=backend)( + [delayed(interrupt_raiser)(x) for x in (1, 0)]) + + # Try again with the context manager API + with Parallel(n_jobs=2, backend=backend) as parallel: + assert get_workers(parallel._backend) is not None + original_workers = get_workers(parallel._backend) + + with raises(ZeroDivisionError): + parallel([delayed(division)(x, y) + for x, y in zip((0, 1), (1, 0))]) + + # The managed pool should still be available and be in a working + # state despite the previously raised (and caught) exception + assert get_workers(parallel._backend) is not None + + # The pool should have been interrupted and restarted: + assert get_workers(parallel._backend) is not original_workers + + assert ([f(x, y=1) for x in range(10)] == + parallel(delayed(f)(x, y=1) for x in range(10))) + + original_workers = get_workers(parallel._backend) + with raises(KeyboardInterrupt): + parallel([delayed(interrupt_raiser)(x) for x in (1, 0)]) + + # The pool should still be available despite the exception + assert get_workers(parallel._backend) is not None + + # The pool should have been interrupted and restarted: + assert get_workers(parallel._backend) is not original_workers + + assert ([f(x, y=1) for x in range(10)] == + parallel(delayed(f)(x, y=1) for x in range(10))), ( + parallel._iterating, parallel.n_completed_tasks, + parallel.n_dispatched_tasks, parallel._aborting + ) + + # Check that the inner pool has been terminated when exiting the + # context manager + assert get_workers(parallel._backend) is None + else: + with raises(KeyboardInterrupt): + Parallel(n_jobs=2)( + [delayed(interrupt_raiser)(x) for x in (1, 0)]) + + # wrapped exceptions should inherit from the class of the original + # exception to make it easy to catch them + with raises(ZeroDivisionError): + Parallel(n_jobs=2)( + [delayed(division)(x, y) for x, y in zip((0, 1), (1, 0))]) + + with raises(MyExceptionWithFinickyInit): + Parallel(n_jobs=2, verbose=0)( + (delayed(exception_raiser)(i, custom_exception=True) + for i in range(30))) + + +@with_multiprocessing +@parametrize('backend', BACKENDS) +def test_error_in_task_iterator(backend): + + def my_generator(raise_at=0): + for i in range(20): + if i == raise_at: + raise ValueError("Iterator Raising Error") + yield i + + with Parallel(n_jobs=2, backend=backend) as p: + # The error is raised in the pre-dispatch phase + with raises(ValueError, match="Iterator Raising Error"): + p(delayed(square)(i) for i in my_generator(raise_at=0)) + + # The error is raised when dispatching a new task after the + # pre-dispatch (likely to happen in a different thread) + with raises(ValueError, match="Iterator Raising Error"): + p(delayed(square)(i) for i in my_generator(raise_at=5)) + + # Same, but raises long after the pre-dispatch phase + with raises(ValueError, match="Iterator Raising Error"): + p(delayed(square)(i) for i in my_generator(raise_at=19)) + + +def consumer(queue, item): + queue.append('Consumed %s' % item) + + +@parametrize('backend', BACKENDS) +@parametrize('batch_size, expected_queue', + [(1, ['Produced 0', 'Consumed 0', + 'Produced 1', 'Consumed 1', + 'Produced 2', 'Consumed 2', + 'Produced 3', 'Consumed 3', + 'Produced 4', 'Consumed 4', + 'Produced 5', 'Consumed 5']), + (4, [ # First Batch + 'Produced 0', 'Produced 1', 'Produced 2', 'Produced 3', + 'Consumed 0', 'Consumed 1', 'Consumed 2', 'Consumed 3', + # Second batch + 'Produced 4', 'Produced 5', 'Consumed 4', 'Consumed 5'])]) +def test_dispatch_one_job(backend, batch_size, expected_queue): + """ Test that with only one job, Parallel does act as a iterator. + """ + queue = list() + + def producer(): + for i in range(6): + queue.append('Produced %i' % i) + yield i + + Parallel(n_jobs=1, batch_size=batch_size, backend=backend)( + delayed(consumer)(queue, x) for x in producer()) + assert queue == expected_queue + assert len(queue) == 12 + + +@with_multiprocessing +@parametrize('backend', PARALLEL_BACKENDS) +def test_dispatch_multiprocessing(backend): + """ Check that using pre_dispatch Parallel does indeed dispatch items + lazily. + """ + manager = mp.Manager() + queue = manager.list() + + def producer(): + for i in range(6): + queue.append('Produced %i' % i) + yield i + + Parallel(n_jobs=2, batch_size=1, pre_dispatch=3, backend=backend)( + delayed(consumer)(queue, 'any') for _ in producer()) + + queue_contents = list(queue) + assert queue_contents[0] == 'Produced 0' + + # Only 3 tasks are pre-dispatched out of 6. The 4th task is dispatched only + # after any of the first 3 jobs have completed. + first_consumption_index = queue_contents[:4].index('Consumed any') + assert first_consumption_index > -1 + + produced_3_index = queue_contents.index('Produced 3') # 4th task produced + assert produced_3_index > first_consumption_index + + assert len(queue) == 12 + + +def test_batching_auto_threading(): + # batching='auto' with the threading backend leaves the effective batch + # size to 1 (no batching) as it has been found to never be beneficial with + # this low-overhead backend. + + with Parallel(n_jobs=2, batch_size='auto', backend='threading') as p: + p(delayed(id)(i) for i in range(5000)) # many very fast tasks + assert p._backend.compute_batch_size() == 1 + + +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_batching_auto_subprocesses(backend): + with Parallel(n_jobs=2, batch_size='auto', backend=backend) as p: + p(delayed(id)(i) for i in range(5000)) # many very fast tasks + + # It should be strictly larger than 1 but as we don't want heisen + # failures on clogged CI worker environment be safe and only check that + # it's a strictly positive number. + assert p._backend.compute_batch_size() > 0 + + +def test_exception_dispatch(): + """Make sure that exception raised during dispatch are indeed captured""" + with raises(ValueError): + Parallel(n_jobs=2, pre_dispatch=16, verbose=0)( + delayed(exception_raiser)(i) for i in range(30)) + + +def nested_function_inner(i): + Parallel(n_jobs=2)( + delayed(exception_raiser)(j) for j in range(30)) + + +def nested_function_outer(i): + Parallel(n_jobs=2)( + delayed(nested_function_inner)(j) for j in range(30)) + + +@with_multiprocessing +@parametrize('backend', PARALLEL_BACKENDS) +@pytest.mark.xfail(reason="https://github.com/joblib/loky/pull/255") +def test_nested_exception_dispatch(backend): + """Ensure errors for nested joblib cases gets propagated + + We rely on the Python 3 built-in __cause__ system that already + report this kind of information to the user. + """ + with raises(ValueError) as excinfo: + Parallel(n_jobs=2, backend=backend)( + delayed(nested_function_outer)(i) for i in range(30)) + + # Check that important information such as function names are visible + # in the final error message reported to the user + report_lines = format_exception(excinfo.type, excinfo.value, excinfo.tb) + report = "".join(report_lines) + assert 'nested_function_outer' in report + assert 'nested_function_inner' in report + assert 'exception_raiser' in report + + assert type(excinfo.value) is ValueError + + +class FakeParallelBackend(SequentialBackend): + """Pretends to run concurrently while running sequentially.""" + + def configure(self, n_jobs=1, parallel=None, **backend_args): + self.n_jobs = self.effective_n_jobs(n_jobs) + self.parallel = parallel + return n_jobs + + def effective_n_jobs(self, n_jobs=1): + if n_jobs < 0: + n_jobs = max(mp.cpu_count() + 1 + n_jobs, 1) + return n_jobs + + +def test_invalid_backend(): + with raises(ValueError, match="Invalid backend:"): + Parallel(backend='unit-testing') + + with raises(ValueError, match="Invalid backend:"): + with parallel_config(backend='unit-testing'): + pass + + with raises(ValueError, match="Invalid backend:"): + with parallel_config(backend='unit-testing'): + pass + + +@parametrize('backend', ALL_VALID_BACKENDS) +def test_invalid_njobs(backend): + with raises(ValueError) as excinfo: + Parallel(n_jobs=0, backend=backend)._initialize_backend() + assert "n_jobs == 0 in Parallel has no meaning" in str(excinfo.value) + + with raises(ValueError) as excinfo: + Parallel(n_jobs=0.5, backend=backend)._initialize_backend() + assert "n_jobs == 0 in Parallel has no meaning" in str(excinfo.value) + + with raises(ValueError) as excinfo: + Parallel(n_jobs="2.3", backend=backend)._initialize_backend() + assert "n_jobs could not be converted to int" in str(excinfo.value) + + with raises(ValueError) as excinfo: + Parallel(n_jobs="invalid_str", backend=backend)._initialize_backend() + assert "n_jobs could not be converted to int" in str(excinfo.value) + + +@with_multiprocessing +@parametrize('backend', PARALLEL_BACKENDS) +@parametrize('n_jobs', ['2', 2.3, 2]) +def test_njobs_converted_to_int(backend, n_jobs): + p = Parallel(n_jobs=n_jobs, backend=backend) + assert p._effective_n_jobs() == 2 + + res = p(delayed(square)(i) for i in range(10)) + assert all(r == square(i) for i, r in enumerate(res)) + + +def test_register_parallel_backend(): + try: + register_parallel_backend("test_backend", FakeParallelBackend) + assert "test_backend" in BACKENDS + assert BACKENDS["test_backend"] == FakeParallelBackend + finally: + del BACKENDS["test_backend"] + + +def test_overwrite_default_backend(): + assert _active_backend_type() == DefaultBackend + try: + register_parallel_backend("threading", BACKENDS["threading"], + make_default=True) + assert _active_backend_type() == ThreadingBackend + finally: + # Restore the global default manually + parallel.DEFAULT_BACKEND = DEFAULT_BACKEND + assert _active_backend_type() == DefaultBackend + + +@skipif(mp is not None, reason="Only without multiprocessing") +def test_backend_no_multiprocessing(): + with warns(UserWarning, + match="joblib backend '.*' is not available on.*"): + Parallel(backend='loky')(delayed(square)(i) for i in range(3)) + + # The below should now work without problems + with parallel_config(backend='loky'): + Parallel()(delayed(square)(i) for i in range(3)) + + +def check_backend_context_manager(context, backend_name): + with context(backend_name, n_jobs=3): + active_backend, active_n_jobs = parallel.get_active_backend() + assert active_n_jobs == 3 + assert effective_n_jobs(3) == 3 + p = Parallel() + assert p.n_jobs == 3 + if backend_name == 'multiprocessing': + assert type(active_backend) is MultiprocessingBackend + assert type(p._backend) is MultiprocessingBackend + elif backend_name == 'loky': + assert type(active_backend) is LokyBackend + assert type(p._backend) is LokyBackend + elif backend_name == 'threading': + assert type(active_backend) is ThreadingBackend + assert type(p._backend) is ThreadingBackend + elif backend_name.startswith('test_'): + assert type(active_backend) is FakeParallelBackend + assert type(p._backend) is FakeParallelBackend + + +all_backends_for_context_manager = PARALLEL_BACKENDS[:] +all_backends_for_context_manager.extend( + ['test_backend_%d' % i for i in range(3)] +) + + +@with_multiprocessing +@parametrize('backend', all_backends_for_context_manager) +@parametrize('context', [parallel_backend, parallel_config]) +def test_backend_context_manager(monkeypatch, backend, context): + if backend not in BACKENDS: + monkeypatch.setitem(BACKENDS, backend, FakeParallelBackend) + + assert _active_backend_type() == DefaultBackend + # check that this possible to switch parallel backends sequentially + check_backend_context_manager(context, backend) + + # The default backend is restored + assert _active_backend_type() == DefaultBackend + + # Check that context manager switching is thread safe: + Parallel(n_jobs=2, backend='threading')( + delayed(check_backend_context_manager)(context, b) + for b in all_backends_for_context_manager if not b) + + # The default backend is again restored + assert _active_backend_type() == DefaultBackend + + +class ParameterizedParallelBackend(SequentialBackend): + """Pretends to run conncurrently while running sequentially.""" + + def __init__(self, param=None): + if param is None: + raise ValueError('param should not be None') + self.param = param + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_parameterized_backend_context_manager(monkeypatch, context): + monkeypatch.setitem(BACKENDS, 'param_backend', + ParameterizedParallelBackend) + assert _active_backend_type() == DefaultBackend + + with context('param_backend', param=42, n_jobs=3): + active_backend, active_n_jobs = parallel.get_active_backend() + assert type(active_backend) is ParameterizedParallelBackend + assert active_backend.param == 42 + assert active_n_jobs == 3 + p = Parallel() + assert p.n_jobs == 3 + assert p._backend is active_backend + results = p(delayed(sqrt)(i) for i in range(5)) + assert results == [sqrt(i) for i in range(5)] + + # The default backend is again restored + assert _active_backend_type() == DefaultBackend + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_directly_parameterized_backend_context_manager(context): + assert _active_backend_type() == DefaultBackend + + # Check that it's possible to pass a backend instance directly, + # without registration + with context(ParameterizedParallelBackend(param=43), n_jobs=5): + active_backend, active_n_jobs = parallel.get_active_backend() + assert type(active_backend) is ParameterizedParallelBackend + assert active_backend.param == 43 + assert active_n_jobs == 5 + p = Parallel() + assert p.n_jobs == 5 + assert p._backend is active_backend + results = p(delayed(sqrt)(i) for i in range(5)) + assert results == [sqrt(i) for i in range(5)] + + # The default backend is again restored + assert _active_backend_type() == DefaultBackend + + +def sleep_and_return_pid(): + sleep(.1) + return os.getpid() + + +def get_nested_pids(): + assert _active_backend_type() == ThreadingBackend + # Assert that the nested backend does not change the default number of + # jobs used in Parallel + assert Parallel()._effective_n_jobs() == 1 + + # Assert that the tasks are running only on one process + return Parallel(n_jobs=2)(delayed(sleep_and_return_pid)() + for _ in range(2)) + + +class MyBackend(joblib._parallel_backends.LokyBackend): + """Backend to test backward compatibility with older backends""" + def get_nested_backend(self, ): + # Older backends only return a backend, without n_jobs indications. + return super(MyBackend, self).get_nested_backend()[0] + + +register_parallel_backend('back_compat_backend', MyBackend) + + +@with_multiprocessing +@parametrize('backend', ['threading', 'loky', 'multiprocessing', + 'back_compat_backend']) +@parametrize("context", [parallel_config, parallel_backend]) +def test_nested_backend_context_manager(context, backend): + # Check that by default, nested parallel calls will always use the + # ThreadingBackend + + with context(backend): + pid_groups = Parallel(n_jobs=2)( + delayed(get_nested_pids)() + for _ in range(10) + ) + for pid_group in pid_groups: + assert len(set(pid_group)) == 1 + + +@with_multiprocessing +@parametrize('n_jobs', [2, -1, None]) +@parametrize('backend', PARALLEL_BACKENDS) +@parametrize("context", [parallel_config, parallel_backend]) +def test_nested_backend_in_sequential(backend, n_jobs, context): + # Check that by default, nested parallel calls will always use the + # ThreadingBackend + + def check_nested_backend(expected_backend_type, expected_n_job): + # Assert that the sequential backend at top level, does not change the + # backend for nested calls. + assert _active_backend_type() == BACKENDS[expected_backend_type] + + # Assert that the nested backend in SequentialBackend does not change + # the default number of jobs used in Parallel + expected_n_job = effective_n_jobs(expected_n_job) + assert Parallel()._effective_n_jobs() == expected_n_job + + Parallel(n_jobs=1)( + delayed(check_nested_backend)(DEFAULT_BACKEND, 1) + for _ in range(10) + ) + + with context(backend, n_jobs=n_jobs): + Parallel(n_jobs=1)( + delayed(check_nested_backend)(backend, n_jobs) + for _ in range(10) + ) + + +def check_nesting_level(context, inner_backend, expected_level): + with context(inner_backend) as ctx: + if context is parallel_config: + backend = ctx["backend"] + if context is parallel_backend: + backend = ctx[0] + assert backend.nesting_level == expected_level + + +@with_multiprocessing +@parametrize('outer_backend', PARALLEL_BACKENDS) +@parametrize('inner_backend', PARALLEL_BACKENDS) +@parametrize("context", [parallel_config, parallel_backend]) +def test_backend_nesting_level(context, outer_backend, inner_backend): + # Check that the nesting level for the backend is correctly set + check_nesting_level(context, outer_backend, 0) + + Parallel(n_jobs=2, backend=outer_backend)( + delayed(check_nesting_level)(context, inner_backend, 1) + for _ in range(10) + ) + + with context(inner_backend, n_jobs=2): + Parallel()(delayed(check_nesting_level)(context, inner_backend, 1) + for _ in range(10)) + + +@with_multiprocessing +@parametrize("context", [parallel_config, parallel_backend]) +@parametrize('with_retrieve_callback', [True, False]) +def test_retrieval_context(context, with_retrieve_callback): + import contextlib + + class MyBackend(ThreadingBackend): + i = 0 + supports_retrieve_callback = with_retrieve_callback + + @contextlib.contextmanager + def retrieval_context(self): + self.i += 1 + yield + + register_parallel_backend("retrieval", MyBackend) + + def nested_call(n): + return Parallel(n_jobs=2)(delayed(id)(i) for i in range(n)) + + with context("retrieval") as ctx: + Parallel(n_jobs=2)( + delayed(nested_call)(i) + for i in range(5) + ) + if context is parallel_config: + assert ctx["backend"].i == 1 + if context is parallel_backend: + assert ctx[0].i == 1 + + +############################################################################### +# Test helpers + +@parametrize('batch_size', [0, -1, 1.42]) +def test_invalid_batch_size(batch_size): + with raises(ValueError): + Parallel(batch_size=batch_size) + + +@parametrize('n_tasks, n_jobs, pre_dispatch, batch_size', + [(2, 2, 'all', 'auto'), + (2, 2, 'n_jobs', 'auto'), + (10, 2, 'n_jobs', 'auto'), + (517, 2, 'n_jobs', 'auto'), + (10, 2, 'n_jobs', 'auto'), + (10, 4, 'n_jobs', 'auto'), + (200, 12, 'n_jobs', 'auto'), + (25, 12, '2 * n_jobs', 1), + (250, 12, 'all', 1), + (250, 12, '2 * n_jobs', 7), + (200, 12, '2 * n_jobs', 'auto')]) +def test_dispatch_race_condition(n_tasks, n_jobs, pre_dispatch, batch_size): + # Check that using (async-)dispatch does not yield a race condition on the + # iterable generator that is not thread-safe natively. + # This is a non-regression test for the "Pool seems closed" class of error + params = {'n_jobs': n_jobs, 'pre_dispatch': pre_dispatch, + 'batch_size': batch_size} + expected = [square(i) for i in range(n_tasks)] + results = Parallel(**params)(delayed(square)(i) for i in range(n_tasks)) + assert results == expected + + +@with_multiprocessing +def test_default_mp_context(): + mp_start_method = mp.get_start_method() + p = Parallel(n_jobs=2, backend='multiprocessing') + context = p._backend_args.get('context') + start_method = context.get_start_method() + assert start_method == mp_start_method + + +@with_numpy +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_no_blas_crash_or_freeze_with_subprocesses(backend): + if backend == 'multiprocessing': + # Use the spawn backend that is both robust and available on all + # platforms + backend = mp.get_context('spawn') + + # Check that on recent Python version, the 'spawn' start method can make + # it possible to use multiprocessing in conjunction of any BLAS + # implementation that happens to be used by numpy with causing a freeze or + # a crash + rng = np.random.RandomState(42) + + # call BLAS DGEMM to force the initialization of the internal thread-pool + # in the main process + a = rng.randn(1000, 1000) + np.dot(a, a.T) + + # check that the internal BLAS thread-pool is not in an inconsistent state + # in the worker processes managed by multiprocessing + Parallel(n_jobs=2, backend=backend)( + delayed(np.dot)(a, a.T) for i in range(2)) + + +UNPICKLABLE_CALLABLE_SCRIPT_TEMPLATE_NO_MAIN = """\ +from joblib import Parallel, delayed + +def square(x): + return x ** 2 + +backend = "{}" +if backend == "spawn": + from multiprocessing import get_context + backend = get_context(backend) + +print(Parallel(n_jobs=2, backend=backend)( + delayed(square)(i) for i in range(5))) +""" + + +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_parallel_with_interactively_defined_functions(backend): + # When using the "-c" flag, interactive functions defined in __main__ + # should work with any backend. + if backend == "multiprocessing" and mp.get_start_method() != "fork": + pytest.skip("Require fork start method to use interactively defined " + "functions with multiprocessing.") + code = UNPICKLABLE_CALLABLE_SCRIPT_TEMPLATE_NO_MAIN.format(backend) + check_subprocess_call( + [sys.executable, '-c', code], timeout=10, + stdout_regex=r'\[0, 1, 4, 9, 16\]') + + +UNPICKLABLE_CALLABLE_SCRIPT_TEMPLATE_MAIN = """\ +import sys +# Make sure that joblib is importable in the subprocess launching this +# script. This is needed in case we run the tests from the joblib root +# folder without having installed joblib +sys.path.insert(0, {joblib_root_folder!r}) + +from joblib import Parallel, delayed + +def run(f, x): + return f(x) + +{define_func} + +if __name__ == "__main__": + backend = "{backend}" + if backend == "spawn": + from multiprocessing import get_context + backend = get_context(backend) + + callable_position = "{callable_position}" + if callable_position == "delayed": + print(Parallel(n_jobs=2, backend=backend)( + delayed(square)(i) for i in range(5))) + elif callable_position == "args": + print(Parallel(n_jobs=2, backend=backend)( + delayed(run)(square, i) for i in range(5))) + else: + print(Parallel(n_jobs=2, backend=backend)( + delayed(run)(f=square, x=i) for i in range(5))) +""" + +SQUARE_MAIN = """\ +def square(x): + return x ** 2 +""" +SQUARE_LOCAL = """\ +def gen_square(): + def square(x): + return x ** 2 + return square +square = gen_square() +""" +SQUARE_LAMBDA = """\ +square = lambda x: x ** 2 +""" + + +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS + ([] if mp is None else ['spawn'])) +@parametrize('define_func', [SQUARE_MAIN, SQUARE_LOCAL, SQUARE_LAMBDA]) +@parametrize('callable_position', ['delayed', 'args', 'kwargs']) +def test_parallel_with_unpicklable_functions_in_args( + backend, define_func, callable_position, tmpdir): + if backend in ['multiprocessing', 'spawn'] and ( + define_func != SQUARE_MAIN or sys.platform == "win32"): + pytest.skip("Not picklable with pickle") + code = UNPICKLABLE_CALLABLE_SCRIPT_TEMPLATE_MAIN.format( + define_func=define_func, backend=backend, + callable_position=callable_position, + joblib_root_folder=os.path.dirname(os.path.dirname(joblib.__file__))) + code_file = tmpdir.join("unpicklable_func_script.py") + code_file.write(code) + check_subprocess_call( + [sys.executable, code_file.strpath], timeout=10, + stdout_regex=r'\[0, 1, 4, 9, 16\]') + + +INTERACTIVE_DEFINED_FUNCTION_AND_CLASS_SCRIPT_CONTENT = """\ +import sys +import faulthandler +# Make sure that joblib is importable in the subprocess launching this +# script. This is needed in case we run the tests from the joblib root +# folder without having installed joblib +sys.path.insert(0, {joblib_root_folder!r}) + +from joblib import Parallel, delayed +from functools import partial + +class MyClass: + '''Class defined in the __main__ namespace''' + def __init__(self, value): + self.value = value + + +def square(x, ignored=None, ignored2=None): + '''Function defined in the __main__ namespace''' + return x.value ** 2 + + +square2 = partial(square, ignored2='something') + +# Here, we do not need the `if __name__ == "__main__":` safeguard when +# using the default `loky` backend (even on Windows). + +# To make debugging easier +faulthandler.dump_traceback_later(30, exit=True) + +# The following baroque function call is meant to check that joblib +# introspection rightfully uses cloudpickle instead of the (faster) pickle +# module of the standard library when necessary. In particular cloudpickle is +# necessary for functions and instances of classes interactively defined in the +# __main__ module. + +print(Parallel(backend="loky", n_jobs=2)( + delayed(square2)(MyClass(i), ignored=[dict(a=MyClass(1))]) + for i in range(5) +)) +""".format(joblib_root_folder=os.path.dirname( + os.path.dirname(joblib.__file__))) + + +@with_multiprocessing +def test_parallel_with_interactively_defined_functions_loky(tmpdir): + # loky accepts interactive functions defined in __main__ and does not + # require if __name__ == '__main__' even when the __main__ module is + # defined by the result of the execution of a filesystem script. + script = tmpdir.join('joblib_interactively_defined_function.py') + script.write(INTERACTIVE_DEFINED_FUNCTION_AND_CLASS_SCRIPT_CONTENT) + check_subprocess_call( + [sys.executable, script.strpath], + stdout_regex=r'\[0, 1, 4, 9, 16\]', + timeout=None, # rely on faulthandler to kill the process + ) + + +INTERACTIVELY_DEFINED_SUBCLASS_WITH_METHOD_SCRIPT_CONTENT = """\ +import sys +# Make sure that joblib is importable in the subprocess launching this +# script. This is needed in case we run the tests from the joblib root +# folder without having installed joblib +sys.path.insert(0, {joblib_root_folder!r}) + +from joblib import Parallel, delayed, hash +import multiprocessing as mp +mp.util.log_to_stderr(5) + +class MyList(list): + '''MyList is interactively defined by MyList.append is a built-in''' + def __hash__(self): + # XXX: workaround limitation in cloudpickle + return hash(self).__hash__() + +l = MyList() + +print(Parallel(backend="loky", n_jobs=2)( + delayed(l.append)(i) for i in range(3) +)) +""".format(joblib_root_folder=os.path.dirname( + os.path.dirname(joblib.__file__))) + + +@with_multiprocessing +def test_parallel_with_interactively_defined_bound_method_loky(tmpdir): + script = tmpdir.join('joblib_interactive_bound_method_script.py') + script.write(INTERACTIVELY_DEFINED_SUBCLASS_WITH_METHOD_SCRIPT_CONTENT) + check_subprocess_call([sys.executable, script.strpath], + stdout_regex=r'\[None, None, None\]', + stderr_regex=r'LokyProcess', + timeout=15) + + +def test_parallel_with_exhausted_iterator(): + exhausted_iterator = iter([]) + assert Parallel(n_jobs=2)(exhausted_iterator) == [] + + +def _cleanup_worker(): + """Helper function to force gc in each worker.""" + force_gc_pypy() + time.sleep(.1) + + +def check_memmap(a): + if not isinstance(a, np.memmap): + raise TypeError('Expected np.memmap instance, got %r', + type(a)) + return a.copy() # return a regular array instead of a memmap + + +@with_numpy +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_auto_memmap_on_arrays_from_generator(backend): + # Non-regression test for a problem with a bad interaction between the + # GC collecting arrays recently created during iteration inside the + # parallel dispatch loop and the auto-memmap feature of Parallel. + # See: https://github.com/joblib/joblib/pull/294 + def generate_arrays(n): + for i in range(n): + yield np.ones(10, dtype=np.float32) * i + # Use max_nbytes=1 to force the use of memory-mapping even for small + # arrays + results = Parallel(n_jobs=2, max_nbytes=1, backend=backend)( + delayed(check_memmap)(a) for a in generate_arrays(100)) + for result, expected in zip(results, generate_arrays(len(results))): + np.testing.assert_array_equal(expected, result) + + # Second call to force loky to adapt the executor by growing the number + # of worker processes. This is a non-regression test for: + # https://github.com/joblib/joblib/issues/629. + results = Parallel(n_jobs=4, max_nbytes=1, backend=backend)( + delayed(check_memmap)(a) for a in generate_arrays(100)) + for result, expected in zip(results, generate_arrays(len(results))): + np.testing.assert_array_equal(expected, result) + + +def identity(arg): + return arg + + +@with_numpy +@with_multiprocessing +def test_memmap_with_big_offset(tmpdir): + fname = tmpdir.join('test.mmap').strpath + size = mmap.ALLOCATIONGRANULARITY + obj = [np.zeros(size, dtype='uint8'), np.ones(size, dtype='uint8')] + dump(obj, fname) + memmap = load(fname, mmap_mode='r') + result, = Parallel(n_jobs=2)(delayed(identity)(memmap) for _ in [0]) + assert isinstance(memmap[1], np.memmap) + assert memmap[1].offset > size + np.testing.assert_array_equal(obj, result) + + +def test_warning_about_timeout_not_supported_by_backend(): + with warnings.catch_warnings(record=True) as warninfo: + Parallel(n_jobs=1, timeout=1)(delayed(square)(i) for i in range(50)) + assert len(warninfo) == 1 + w = warninfo[0] + assert isinstance(w.message, UserWarning) + assert str(w.message) == ( + "The backend class 'SequentialBackend' does not support timeout. " + "You have set 'timeout=1' in Parallel but the 'timeout' parameter " + "will not be used.") + + +def set_list_value(input_list, index, value): + input_list[index] = value + return value + + +@pytest.mark.parametrize('n_jobs', [1, 2, 4]) +def test_parallel_return_order_with_return_as_generator_parameter(n_jobs): + # This test inserts values in a list in some expected order + # in sequential computing, and then checks that this order has been + # respected by Parallel output generator. + input_list = [0] * 5 + result = Parallel(n_jobs=n_jobs, return_as="generator", + backend='threading')( + delayed(set_list_value)(input_list, i, i) for i in range(5)) + + # Ensure that all the tasks are completed before checking the result + result = list(result) + + assert all(v == r for v, r in zip(input_list, result)) + + +def _sqrt_with_delay(e, delay): + if delay: + sleep(30) + return sqrt(e) + + +def _test_parallel_unordered_generator_returns_fastest_first(backend, n_jobs): + # This test submits 10 tasks, but the second task is super slow. This test + # checks that the 9 other tasks return before the slow task is done, when + # `return_as` parameter is set to `'generator_unordered'` + result = Parallel(n_jobs=n_jobs, return_as="generator_unordered", + backend=backend)( + delayed(_sqrt_with_delay)(i**2, (i == 1)) for i in range(10)) + + quickly_returned = sorted(next(result) for _ in range(9)) + + expected_quickly_returned = [0] + list(range(2, 10)) + + assert all( + v == r for v, r in zip(expected_quickly_returned, quickly_returned) + ) + + del result + force_gc_pypy() + + +@pytest.mark.parametrize('n_jobs', [2, 4]) +# NB: for this test to work, the backend must be allowed to process tasks +# concurrently, so at least two jobs with a non-sequential backend are +# mandatory. +@with_multiprocessing +@parametrize('backend', set(RETURN_GENERATOR_BACKENDS) - {"sequential"}) +def test_parallel_unordered_generator_returns_fastest_first(backend, n_jobs): + _test_parallel_unordered_generator_returns_fastest_first(backend, n_jobs) + + +@pytest.mark.parametrize('n_jobs', [2, -1]) +@parametrize("context", [parallel_config, parallel_backend]) +@skipif(distributed is None, reason='This test requires dask') +def test_parallel_unordered_generator_returns_fastest_first_with_dask( + n_jobs, context +): + with distributed.Client( + n_workers=2, threads_per_worker=2 + ), context("dask"): + _test_parallel_unordered_generator_returns_fastest_first(None, n_jobs) + + +@parametrize('backend', ALL_VALID_BACKENDS) +@parametrize('n_jobs', [1, 2, -2, -1]) +def test_abort_backend(n_jobs, backend): + delays = ["a"] + [10] * 100 + with raises(TypeError): + t_start = time.time() + Parallel(n_jobs=n_jobs, backend=backend)( + delayed(time.sleep)(i) for i in delays) + dt = time.time() - t_start + assert dt < 20 + + +def get_large_object(arg): + result = np.ones(int(5 * 1e5), dtype=bool) + result[0] = False + return result + + +def _test_deadlock_with_generator(backend, return_as, n_jobs): + # Non-regression test for a race condition in the backends when the pickler + # is delayed by a large object. + with Parallel(n_jobs=n_jobs, backend=backend, + return_as=return_as) as parallel: + result = parallel(delayed(get_large_object)(i) for i in range(10)) + next(result) + next(result) + del result + # The gc in pypy can be delayed. Force it to make sure this test does + # not cause timeout on the CI. + force_gc_pypy() + + +@with_numpy +@parametrize('backend', RETURN_GENERATOR_BACKENDS) +@parametrize('return_as', ["generator", "generator_unordered"]) +@parametrize('n_jobs', [1, 2, -2, -1]) +def test_deadlock_with_generator(backend, return_as, n_jobs): + _test_deadlock_with_generator(backend, return_as, n_jobs) + + +@with_numpy +@pytest.mark.parametrize('n_jobs', [2, -1]) +@parametrize('return_as', ["generator", "generator_unordered"]) +@parametrize("context", [parallel_config, parallel_backend]) +@skipif(distributed is None, reason='This test requires dask') +def test_deadlock_with_generator_and_dask(context, return_as, n_jobs): + with distributed.Client( + n_workers=2, threads_per_worker=2 + ), context("dask"): + _test_deadlock_with_generator(None, return_as, n_jobs) + + +@parametrize('backend', RETURN_GENERATOR_BACKENDS) +@parametrize('return_as', ["generator", "generator_unordered"]) +@parametrize('n_jobs', [1, 2, -2, -1]) +def test_multiple_generator_call(backend, return_as, n_jobs): + # Non-regression test that ensures the dispatch of the tasks starts + # immediately when Parallel.__call__ is called. This test relies on the + # assumption that only one generator can be submitted at a time. + with raises(RuntimeError, + match="This Parallel instance is already running"): + parallel = Parallel(n_jobs, backend=backend, return_as=return_as) + g = parallel(delayed(sleep)(1) for _ in range(10)) # noqa: F841 + t_start = time.time() + gen2 = parallel(delayed(id)(i) for i in range(100)) # noqa: F841 + + # Make sure that the error is raised quickly + assert time.time() - t_start < 2, ( + "The error should be raised immediatly when submitting a new task " + "but it took more than 2s." + ) + + del g + # The gc in pypy can be delayed. Force it to make sure this test does not + # cause timeout on the CI. + force_gc_pypy() + + +@parametrize('backend', RETURN_GENERATOR_BACKENDS) +@parametrize('return_as', ["generator", "generator_unordered"]) +@parametrize('n_jobs', [1, 2, -2, -1]) +def test_multiple_generator_call_managed(backend, return_as, n_jobs): + # Non-regression test that ensures the dispatch of the tasks starts + # immediately when Parallel.__call__ is called. This test relies on the + # assumption that only one generator can be submitted at a time. + with Parallel(n_jobs, backend=backend, + return_as=return_as) as parallel: + g = parallel(delayed(sleep)(10) for _ in range(10)) # noqa: F841 + t_start = time.time() + with raises(RuntimeError, + match="This Parallel instance is already running"): + g2 = parallel(delayed(id)(i) for i in range(100)) # noqa: F841 + + # Make sure that the error is raised quickly + assert time.time() - t_start < 2, ( + "The error should be raised immediatly when submitting a new task " + "but it took more than 2s." + ) + + # The gc in pypy can be delayed. Force it to make sure this test does not + # cause timeout on the CI. + del g + force_gc_pypy() + + +@parametrize('backend', RETURN_GENERATOR_BACKENDS) +@parametrize('return_as_1', ["generator", "generator_unordered"]) +@parametrize('return_as_2', ["generator", "generator_unordered"]) +@parametrize('n_jobs', [1, 2, -2, -1]) +def test_multiple_generator_call_separated( + backend, return_as_1, return_as_2, n_jobs +): + # Check that for separated Parallel, both tasks are correctly returned. + g = Parallel(n_jobs, backend=backend, return_as=return_as_1)( + delayed(sqrt)(i ** 2) for i in range(10) + ) + g2 = Parallel(n_jobs, backend=backend, return_as=return_as_2)( + delayed(sqrt)(i ** 2) for i in range(10, 20) + ) + + if return_as_1 == "generator_unordered": + g = sorted(g) + + if return_as_2 == "generator_unordered": + g2 = sorted(g2) + + assert all(res == i for res, i in zip(g, range(10))) + assert all(res == i for res, i in zip(g2, range(10, 20))) + + +@parametrize('backend, error', [ + ('loky', True), + ('threading', False), + ('sequential', False), +]) +@parametrize('return_as_1', ["generator", "generator_unordered"]) +@parametrize('return_as_2', ["generator", "generator_unordered"]) +def test_multiple_generator_call_separated_gc( + backend, return_as_1, return_as_2, error +): + + if (backend == 'loky') and (mp is None): + pytest.skip("Requires multiprocessing") + + # Check that in loky, only one call can be run at a time with + # a single executor. + parallel = Parallel(2, backend=backend, return_as=return_as_1) + g = parallel(delayed(sleep)(10) for i in range(10)) + g_wr = weakref.finalize(g, lambda: print("Generator collected")) + ctx = ( + raises(RuntimeError, match="The executor underlying Parallel") + if error else nullcontext() + ) + with ctx: + # For loky, this call will raise an error as the gc of the previous + # generator will shutdown the shared executor. + # For the other backends, as the worker pools are not shared between + # the two calls, this should proceed correctly. + t_start = time.time() + g = Parallel(2, backend=backend, return_as=return_as_2)( + delayed(sqrt)(i ** 2) for i in range(10, 20) + ) + + # The gc in pypy can be delayed. Force it to test the behavior when it + # will eventually be collected. + force_gc_pypy() + + if return_as_2 == "generator_unordered": + g = sorted(g) + + assert all(res == i for res, i in zip(g, range(10, 20))) + + assert time.time() - t_start < 5 + + # Make sure that the computation are stopped for the gc'ed generator + retry = 0 + while g_wr.alive and retry < 3: + retry += 1 + time.sleep(.5) + assert time.time() - t_start < 5 + + if parallel._effective_n_jobs() != 1: + # check that the first parallel object is aborting (the final _aborted + # state might be delayed). + assert parallel._aborting + + +@with_numpy +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_memmapping_leaks(backend, tmpdir): + # Non-regression test for memmapping backends. Ensure that the data + # does not stay too long in memory + tmpdir = tmpdir.strpath + + # Use max_nbytes=1 to force the use of memory-mapping even for small + # arrays + with Parallel(n_jobs=2, max_nbytes=1, backend=backend, + temp_folder=tmpdir) as p: + p(delayed(check_memmap)(a) for a in [np.random.random(10)] * 2) + + # The memmap folder should not be clean in the context scope + assert len(os.listdir(tmpdir)) > 0 + + # Cleaning of the memmap folder is triggered by the garbage + # collection. With pypy the garbage collection has been observed to be + # delayed, sometimes up until the shutdown of the interpreter. This + # cleanup job executed in the worker ensures that it's triggered + # immediately. + p(delayed(_cleanup_worker)() for _ in range(2)) + + # Make sure that the shared memory is cleaned at the end when we exit + # the context + for _ in range(100): + if not os.listdir(tmpdir): + break + sleep(.1) + else: + raise AssertionError('temporary directory of Parallel was not removed') + + # Make sure that the shared memory is cleaned at the end of a call + p = Parallel(n_jobs=2, max_nbytes=1, backend=backend) + p(delayed(check_memmap)(a) for a in [np.random.random(10)] * 2) + p(delayed(_cleanup_worker)() for _ in range(2)) + + for _ in range(100): + if not os.listdir(tmpdir): + break + sleep(.1) + else: + raise AssertionError('temporary directory of Parallel was not removed') + + +@parametrize('backend', + ([None, 'threading'] if mp is None + else [None, 'loky', 'threading']) + ) +def test_lambda_expression(backend): + # cloudpickle is used to pickle delayed callables + results = Parallel(n_jobs=2, backend=backend)( + delayed(lambda x: x ** 2)(i) for i in range(10)) + assert results == [i ** 2 for i in range(10)] + + +@with_multiprocessing +@parametrize('backend', PROCESS_BACKENDS) +def test_backend_batch_statistics_reset(backend): + """Test that a parallel backend correctly resets its batch statistics.""" + n_jobs = 2 + n_inputs = 500 + task_time = 2. / n_inputs + + p = Parallel(verbose=10, n_jobs=n_jobs, backend=backend) + p(delayed(time.sleep)(task_time) for i in range(n_inputs)) + assert (p._backend._effective_batch_size == + p._backend._DEFAULT_EFFECTIVE_BATCH_SIZE) + assert (p._backend._smoothed_batch_duration == + p._backend._DEFAULT_SMOOTHED_BATCH_DURATION) + + p(delayed(time.sleep)(task_time) for i in range(n_inputs)) + assert (p._backend._effective_batch_size == + p._backend._DEFAULT_EFFECTIVE_BATCH_SIZE) + assert (p._backend._smoothed_batch_duration == + p._backend._DEFAULT_SMOOTHED_BATCH_DURATION) + + +@with_multiprocessing +@parametrize("context", [parallel_config, parallel_backend]) +def test_backend_hinting_and_constraints(context): + for n_jobs in [1, 2, -1]: + assert type(Parallel(n_jobs=n_jobs)._backend) == DefaultBackend + + p = Parallel(n_jobs=n_jobs, prefer='threads') + assert type(p._backend) is ThreadingBackend + + p = Parallel(n_jobs=n_jobs, prefer='processes') + assert type(p._backend) is DefaultBackend + + p = Parallel(n_jobs=n_jobs, require='sharedmem') + assert type(p._backend) is ThreadingBackend + + # Explicit backend selection can override backend hinting although it + # is useless to pass a hint when selecting a backend. + p = Parallel(n_jobs=2, backend='loky', prefer='threads') + assert type(p._backend) is LokyBackend + + with context('loky', n_jobs=2): + # Explicit backend selection by the user with the context manager + # should be respected when combined with backend hints only. + p = Parallel(prefer='threads') + assert type(p._backend) is LokyBackend + assert p.n_jobs == 2 + + with context('loky', n_jobs=2): + # Locally hard-coded n_jobs value is respected. + p = Parallel(n_jobs=3, prefer='threads') + assert type(p._backend) is LokyBackend + assert p.n_jobs == 3 + + with context('loky', n_jobs=2): + # Explicit backend selection by the user with the context manager + # should be ignored when the Parallel call has hard constraints. + # In this case, the default backend that supports shared mem is + # used an the default number of processes is used. + p = Parallel(require='sharedmem') + assert type(p._backend) is ThreadingBackend + assert p.n_jobs == 1 + + with context('loky', n_jobs=2): + p = Parallel(n_jobs=3, require='sharedmem') + assert type(p._backend) is ThreadingBackend + assert p.n_jobs == 3 + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_backend_hinting_and_constraints_with_custom_backends( + capsys, context +): + # Custom backends can declare that they use threads and have shared memory + # semantics: + class MyCustomThreadingBackend(ParallelBackendBase): + supports_sharedmem = True + use_threads = True + + def apply_async(self): + pass + + def effective_n_jobs(self, n_jobs): + return n_jobs + + with context(MyCustomThreadingBackend()): + p = Parallel(n_jobs=2, prefer='processes') # ignored + assert type(p._backend) is MyCustomThreadingBackend + + p = Parallel(n_jobs=2, require='sharedmem') + assert type(p._backend) is MyCustomThreadingBackend + + class MyCustomProcessingBackend(ParallelBackendBase): + supports_sharedmem = False + use_threads = False + + def apply_async(self): + pass + + def effective_n_jobs(self, n_jobs): + return n_jobs + + with context(MyCustomProcessingBackend()): + p = Parallel(n_jobs=2, prefer='processes') + assert type(p._backend) is MyCustomProcessingBackend + + out, err = capsys.readouterr() + assert out == "" + assert err == "" + + p = Parallel(n_jobs=2, require='sharedmem', verbose=10) + assert type(p._backend) is ThreadingBackend + + out, err = capsys.readouterr() + expected = ("Using ThreadingBackend as joblib backend " + "instead of MyCustomProcessingBackend as the latter " + "does not provide shared memory semantics.") + assert out.strip() == expected + assert err == "" + + with raises(ValueError): + Parallel(backend=MyCustomProcessingBackend(), require='sharedmem') + + +def test_invalid_backend_hinting_and_constraints(): + with raises(ValueError): + Parallel(prefer='invalid') + + with raises(ValueError): + Parallel(require='invalid') + + with raises(ValueError): + # It is inconsistent to prefer process-based parallelism while + # requiring shared memory semantics. + Parallel(prefer='processes', require='sharedmem') + + if mp is not None: + # It is inconsistent to ask explicitly for a process-based + # parallelism while requiring shared memory semantics. + with raises(ValueError): + Parallel(backend='loky', require='sharedmem') + with raises(ValueError): + Parallel(backend='multiprocessing', require='sharedmem') + + +def _recursive_backend_info(limit=3, **kwargs): + """Perform nested parallel calls and introspect the backend on the way""" + + with Parallel(n_jobs=2) as p: + this_level = [(type(p._backend).__name__, p._backend.nesting_level)] + if limit == 0: + return this_level + results = p(delayed(_recursive_backend_info)(limit=limit - 1, **kwargs) + for i in range(1)) + return this_level + results[0] + + +@with_multiprocessing +@parametrize('backend', ['loky', 'threading']) +@parametrize("context", [parallel_config, parallel_backend]) +def test_nested_parallelism_limit(context, backend): + with context(backend, n_jobs=2): + backend_types_and_levels = _recursive_backend_info() + + if cpu_count() == 1: + second_level_backend_type = 'SequentialBackend' + max_level = 1 + else: + second_level_backend_type = 'ThreadingBackend' + max_level = 2 + + top_level_backend_type = backend.title() + 'Backend' + expected_types_and_levels = [ + (top_level_backend_type, 0), + (second_level_backend_type, 1), + ('SequentialBackend', max_level), + ('SequentialBackend', max_level) + ] + assert backend_types_and_levels == expected_types_and_levels + + +@with_numpy +@parametrize("context", [parallel_config, parallel_backend]) +@skipif(distributed is None, reason='This test requires dask') +def test_nested_parallelism_with_dask(context): + with distributed.Client(n_workers=2, threads_per_worker=2): + # 10 MB of data as argument to trigger implicit scattering + data = np.ones(int(1e7), dtype=np.uint8) + for i in range(2): + with context('dask'): + backend_types_and_levels = _recursive_backend_info(data=data) + assert len(backend_types_and_levels) == 4 + assert all(name == 'DaskDistributedBackend' + for name, _ in backend_types_and_levels) + + # No argument + with context('dask'): + backend_types_and_levels = _recursive_backend_info() + assert len(backend_types_and_levels) == 4 + assert all(name == 'DaskDistributedBackend' + for name, _ in backend_types_and_levels) + + +def _recursive_parallel(nesting_limit=None): + """A horrible function that does recursive parallel calls""" + return Parallel()(delayed(_recursive_parallel)() for i in range(2)) + + +@pytest.mark.no_cover +@parametrize("context", [parallel_config, parallel_backend]) +@parametrize( + 'backend', (['threading'] if mp is None else ['loky', 'threading']) +) +def test_thread_bomb_mitigation(context, backend): + # Test that recursive parallelism raises a recursion rather than + # saturating the operating system resources by creating a unbounded number + # of threads. + with context(backend, n_jobs=2): + with raises(BaseException) as excinfo: + _recursive_parallel() + exc = excinfo.value + if backend == "loky": + # Local import because loky may not be importable for lack of + # multiprocessing + from joblib.externals.loky.process_executor import TerminatedWorkerError # noqa + if isinstance(exc, (TerminatedWorkerError, PicklingError)): + # The recursion exception can itself cause an error when + # pickling it to be send back to the parent process. In this + # case the worker crashes but the original traceback is still + # printed on stderr. This could be improved but does not seem + # simple to do and this is not critical for users (as long + # as there is no process or thread bomb happening). + pytest.xfail("Loky worker crash when serializing RecursionError") + + assert isinstance(exc, RecursionError) + + +def _run_parallel_sum(): + env_vars = {} + for var in ['OMP_NUM_THREADS', 'OPENBLAS_NUM_THREADS', 'MKL_NUM_THREADS', + 'VECLIB_MAXIMUM_THREADS', 'NUMEXPR_NUM_THREADS', + 'NUMBA_NUM_THREADS', 'ENABLE_IPC']: + env_vars[var] = os.environ.get(var) + return env_vars, parallel_sum(100) + + +@parametrize("backend", ([None, 'loky'] if mp is not None else [None])) +@skipif(parallel_sum is None, reason="Need OpenMP helper compiled") +def test_parallel_thread_limit(backend): + results = Parallel(n_jobs=2, backend=backend)( + delayed(_run_parallel_sum)() for _ in range(2) + ) + expected_num_threads = max(cpu_count() // 2, 1) + for worker_env_vars, omp_num_threads in results: + assert omp_num_threads == expected_num_threads + for name, value in worker_env_vars.items(): + if name.endswith("_THREADS"): + assert value == str(expected_num_threads) + else: + assert name == "ENABLE_IPC" + assert value == "1" + + +@parametrize("context", [parallel_config, parallel_backend]) +@skipif(distributed is not None, reason='This test requires dask') +def test_dask_backend_when_dask_not_installed(context): + with raises(ValueError, match='Please install dask'): + context('dask') + + +@parametrize("context", [parallel_config, parallel_backend]) +def test_zero_worker_backend(context): + # joblib.Parallel should reject with an explicit error message parallel + # backends that have no worker. + class ZeroWorkerBackend(ThreadingBackend): + def configure(self, *args, **kwargs): + return 0 + + def apply_async(self, func, callback=None): # pragma: no cover + raise TimeoutError("No worker available") + + def effective_n_jobs(self, n_jobs): # pragma: no cover + return 0 + + expected_msg = "ZeroWorkerBackend has no active worker" + with context(ZeroWorkerBackend()): + with pytest.raises(RuntimeError, match=expected_msg): + Parallel(n_jobs=2)(delayed(id)(i) for i in range(2)) + + +def test_globals_update_at_each_parallel_call(): + # This is a non-regression test related to joblib issues #836 and #833. + # Cloudpickle versions between 0.5.4 and 0.7 introduced a bug where global + # variables changes in a parent process between two calls to + # joblib.Parallel would not be propagated into the workers. + global MY_GLOBAL_VARIABLE + MY_GLOBAL_VARIABLE = "original value" + + def check_globals(): + global MY_GLOBAL_VARIABLE + return MY_GLOBAL_VARIABLE + + assert check_globals() == "original value" + + workers_global_variable = Parallel(n_jobs=2)( + delayed(check_globals)() for i in range(2)) + assert set(workers_global_variable) == {"original value"} + + # Change the value of MY_GLOBAL_VARIABLE, and make sure this change gets + # propagated into the workers environment + MY_GLOBAL_VARIABLE = "changed value" + assert check_globals() == "changed value" + + workers_global_variable = Parallel(n_jobs=2)( + delayed(check_globals)() for i in range(2)) + assert set(workers_global_variable) == {"changed value"} + + +############################################################################## +# Test environment variable in child env, in particular for limiting +# the maximal number of threads in C-library threadpools. +# + +def _check_numpy_threadpool_limits(): + import numpy as np + # Let's call BLAS on a Matrix Matrix multiplication with dimensions large + # enough to ensure that the threadpool managed by the underlying BLAS + # implementation is actually used so as to force its initialization. + a = np.random.randn(100, 100) + np.dot(a, a) + from threadpoolctl import threadpool_info + return threadpool_info() + + +def _parent_max_num_threads_for(child_module, parent_info): + for parent_module in parent_info: + if parent_module['filepath'] == child_module['filepath']: + return parent_module['num_threads'] + raise ValueError("An unexpected module was loaded in child:\n{}" + .format(child_module)) + + +def check_child_num_threads(workers_info, parent_info, num_threads): + # Check that the number of threads reported in workers_info is consistent + # with the expectation. We need to be careful to handle the cases where + # the requested number of threads is below max_num_thread for the library. + for child_threadpool_info in workers_info: + for child_module in child_threadpool_info: + parent_max_num_threads = _parent_max_num_threads_for( + child_module, parent_info) + expected = {min(num_threads, parent_max_num_threads), num_threads} + assert child_module['num_threads'] in expected + + +@with_numpy +@with_multiprocessing +@parametrize('n_jobs', [2, 4, -2, -1]) +def test_threadpool_limitation_in_child_loky(n_jobs): + # Check that the protection against oversubscription in workers is working + # using threadpoolctl functionalities. + + # Skip this test if numpy is not linked to a BLAS library + parent_info = _check_numpy_threadpool_limits() + if len(parent_info) == 0: + pytest.skip(reason="Need a version of numpy linked to BLAS") + + workers_threadpool_infos = Parallel(backend="loky", n_jobs=n_jobs)( + delayed(_check_numpy_threadpool_limits)() for i in range(2)) + + n_jobs = effective_n_jobs(n_jobs) + expected_child_num_threads = max(cpu_count() // n_jobs, 1) + + check_child_num_threads(workers_threadpool_infos, parent_info, + expected_child_num_threads) + + +@with_numpy +@with_multiprocessing +@parametrize('inner_max_num_threads', [1, 2, 4, None]) +@parametrize('n_jobs', [2, -1]) +@parametrize("context", [parallel_config, parallel_backend]) +def test_threadpool_limitation_in_child_context( + context, n_jobs, inner_max_num_threads +): + # Check that the protection against oversubscription in workers is working + # using threadpoolctl functionalities. + + # Skip this test if numpy is not linked to a BLAS library + parent_info = _check_numpy_threadpool_limits() + if len(parent_info) == 0: + pytest.skip(reason="Need a version of numpy linked to BLAS") + + with context('loky', inner_max_num_threads=inner_max_num_threads): + workers_threadpool_infos = Parallel(n_jobs=n_jobs)( + delayed(_check_numpy_threadpool_limits)() for i in range(2)) + + n_jobs = effective_n_jobs(n_jobs) + if inner_max_num_threads is None: + expected_child_num_threads = max(cpu_count() // n_jobs, 1) + else: + expected_child_num_threads = inner_max_num_threads + + check_child_num_threads(workers_threadpool_infos, parent_info, + expected_child_num_threads) + + +@with_multiprocessing +@parametrize('n_jobs', [2, -1]) +@parametrize('var_name', ["OPENBLAS_NUM_THREADS", + "MKL_NUM_THREADS", + "OMP_NUM_THREADS"]) +@parametrize("context", [parallel_config, parallel_backend]) +def test_threadpool_limitation_in_child_override(context, n_jobs, var_name): + # Check that environment variables set by the user on the main process + # always have the priority. + + # Clean up the existing executor because we change the environment of the + # parent at runtime and it is not detected in loky intentionally. + get_reusable_executor(reuse=True).shutdown() + + def _get_env(var_name): + return os.environ.get(var_name) + + original_var_value = os.environ.get(var_name) + try: + os.environ[var_name] = "4" + # Skip this test if numpy is not linked to a BLAS library + results = Parallel(n_jobs=n_jobs)( + delayed(_get_env)(var_name) for i in range(2)) + assert results == ["4", "4"] + + with context('loky', inner_max_num_threads=1): + results = Parallel(n_jobs=n_jobs)( + delayed(_get_env)(var_name) for i in range(2)) + assert results == ["1", "1"] + + finally: + if original_var_value is None: + del os.environ[var_name] + else: + os.environ[var_name] = original_var_value + + +@with_multiprocessing +@parametrize('n_jobs', [2, 4, -1]) +def test_loky_reuse_workers(n_jobs): + # Non-regression test for issue #967 where the workers are not reused when + # calling multiple Parallel loops. + + def parallel_call(n_jobs): + x = range(10) + Parallel(n_jobs=n_jobs)(delayed(sum)(x) for i in range(10)) + + # Run a parallel loop and get the workers used for computations + parallel_call(n_jobs) + first_executor = get_reusable_executor(reuse=True) + + # Ensure that the workers are reused for the next calls, as the executor is + # not restarted. + for _ in range(10): + parallel_call(n_jobs) + executor = get_reusable_executor(reuse=True) + assert executor == first_executor diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_store_backends.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_store_backends.py new file mode 100644 index 0000000..e5db167 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_store_backends.py @@ -0,0 +1,94 @@ + +try: + # Python 2.7: use the C pickle to speed up + # test_concurrency_safe_write which pickles big python objects + import cPickle as cpickle +except ImportError: + import pickle as cpickle +import functools +from pickle import PicklingError +import time + +import pytest + +from joblib.testing import parametrize, timeout +from joblib.test.common import with_multiprocessing +from joblib.backports import concurrency_safe_rename +from joblib import Parallel, delayed +from joblib._store_backends import ( + concurrency_safe_write, + FileSystemStoreBackend, + CacheWarning, +) + + +def write_func(output, filename): + with open(filename, 'wb') as f: + cpickle.dump(output, f) + + +def load_func(expected, filename): + for i in range(10): + try: + with open(filename, 'rb') as f: + reloaded = cpickle.load(f) + break + except (OSError, IOError): + # On Windows you can have WindowsError ([Error 5] Access + # is denied or [Error 13] Permission denied) when reading the file, + # probably because a writer process has a lock on the file + time.sleep(0.1) + else: + raise + assert expected == reloaded + + +def concurrency_safe_write_rename(to_write, filename, write_func): + temporary_filename = concurrency_safe_write(to_write, + filename, write_func) + concurrency_safe_rename(temporary_filename, filename) + + +@timeout(0) # No timeout as this test can be long +@with_multiprocessing +@parametrize('backend', ['multiprocessing', 'loky', 'threading']) +def test_concurrency_safe_write(tmpdir, backend): + # Add one item to cache + filename = tmpdir.join('test.pkl').strpath + + obj = {str(i): i for i in range(int(1e5))} + funcs = [functools.partial(concurrency_safe_write_rename, + write_func=write_func) + if i % 3 != 2 else load_func for i in range(12)] + Parallel(n_jobs=2, backend=backend)( + delayed(func)(obj, filename) for func in funcs) + + +def test_warning_on_dump_failure(tmpdir): + # Check that a warning is raised when the dump fails for any reason but + # a PicklingError. + class UnpicklableObject(object): + def __reduce__(self): + raise RuntimeError("some exception") + + backend = FileSystemStoreBackend() + backend.location = tmpdir.join('test_warning_on_pickling_error').strpath + backend.compress = None + + with pytest.warns(CacheWarning, match="some exception"): + backend.dump_item("testpath", UnpicklableObject()) + + +def test_warning_on_pickling_error(tmpdir): + # This is separate from test_warning_on_dump_failure because in the + # future we will turn this into an exception. + class UnpicklableObject(object): + def __reduce__(self): + raise PicklingError("not picklable") + + backend = FileSystemStoreBackend() + backend.location = tmpdir.join('test_warning_on_pickling_error').strpath + backend.compress = None + + with pytest.warns(FutureWarning, match="not picklable"): + backend.dump_item("testpath", UnpicklableObject()) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_testing.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_testing.py new file mode 100644 index 0000000..e8095aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_testing.py @@ -0,0 +1,75 @@ +import sys +import re + +from joblib.testing import raises, check_subprocess_call + + +def test_check_subprocess_call(): + code = '\n'.join(['result = 1 + 2 * 3', + 'print(result)', + 'my_list = [1, 2, 3]', + 'print(my_list)']) + + check_subprocess_call([sys.executable, '-c', code]) + + # Now checking stdout with a regex + check_subprocess_call([sys.executable, '-c', code], + # Regex needed for platform-specific line endings + stdout_regex=r'7\s{1,2}\[1, 2, 3\]') + + +def test_check_subprocess_call_non_matching_regex(): + code = '42' + non_matching_pattern = '_no_way_this_matches_anything_' + + with raises(ValueError) as excinfo: + check_subprocess_call([sys.executable, '-c', code], + stdout_regex=non_matching_pattern) + excinfo.match('Unexpected stdout.+{}'.format(non_matching_pattern)) + + +def test_check_subprocess_call_wrong_command(): + wrong_command = '_a_command_that_does_not_exist_' + with raises(OSError): + check_subprocess_call([wrong_command]) + + +def test_check_subprocess_call_non_zero_return_code(): + code_with_non_zero_exit = '\n'.join([ + 'import sys', + 'print("writing on stdout")', + 'sys.stderr.write("writing on stderr")', + 'sys.exit(123)']) + + pattern = re.compile('Non-zero return code: 123.+' + 'Stdout:\nwriting on stdout.+' + 'Stderr:\nwriting on stderr', re.DOTALL) + + with raises(ValueError) as excinfo: + check_subprocess_call([sys.executable, '-c', code_with_non_zero_exit]) + excinfo.match(pattern) + + +def test_check_subprocess_call_timeout(): + code_timing_out = '\n'.join([ + 'import time', + 'import sys', + 'print("before sleep on stdout")', + 'sys.stdout.flush()', + 'sys.stderr.write("before sleep on stderr")', + 'sys.stderr.flush()', + # We need to sleep for at least 2 * timeout seconds in case the SIGKILL + # is triggered. + 'time.sleep(10)', + 'print("process should have be killed before")', + 'sys.stdout.flush()']) + + pattern = re.compile('Non-zero return code:.+' + 'Stdout:\nbefore sleep on stdout\\s+' + 'Stderr:\nbefore sleep on stderr', + re.DOTALL) + + with raises(ValueError) as excinfo: + check_subprocess_call([sys.executable, '-c', code_timing_out], + timeout=1) + excinfo.match(pattern) diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_utils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_utils.py new file mode 100644 index 0000000..4999a21 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/test_utils.py @@ -0,0 +1,27 @@ +import pytest + +from joblib._utils import eval_expr + + +@pytest.mark.parametrize( + "expr", + ["exec('import os')", "print(1)", "import os", "1+1; import os", "1^1"], +) +def test_eval_expr_invalid(expr): + with pytest.raises( + ValueError, match="is not a valid or supported arithmetic" + ): + eval_expr(expr) + + +@pytest.mark.parametrize( + "expr, result", + [ + ("2*6", 12), + ("2**6", 64), + ("1 + 2*3**(4) / (6 + -7)", -161.0), + ("(20 // 3) % 5", 1), + ], +) +def test_eval_expr_valid(expr, result): + assert eval_expr(expr) == result diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/test/testutils.py b/.pythonlibs/lib/python3.10/site-packages/joblib/test/testutils.py new file mode 100644 index 0000000..20ec8c1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/test/testutils.py @@ -0,0 +1,8 @@ +def return_slice_of_data(arr, start_idx, end_idx): + return arr[start_idx:end_idx] + + +def print_filename_and_raise(arr): + from joblib._memmapping_reducer import _get_backing_memmap + print(_get_backing_memmap(arr).filename) + raise ValueError diff --git a/.pythonlibs/lib/python3.10/site-packages/joblib/testing.py b/.pythonlibs/lib/python3.10/site-packages/joblib/testing.py new file mode 100644 index 0000000..caab7d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/joblib/testing.py @@ -0,0 +1,99 @@ +""" +Helper for testing. +""" + +import sys +import warnings +import os.path +import re +import subprocess +import threading + +import pytest +import _pytest + + +raises = pytest.raises +warns = pytest.warns +SkipTest = _pytest.runner.Skipped +skipif = pytest.mark.skipif +fixture = pytest.fixture +parametrize = pytest.mark.parametrize +timeout = pytest.mark.timeout +xfail = pytest.mark.xfail +param = pytest.param + + +def warnings_to_stdout(): + """ Redirect all warnings to stdout. + """ + showwarning_orig = warnings.showwarning + + def showwarning(msg, cat, fname, lno, file=None, line=0): + showwarning_orig(msg, cat, os.path.basename(fname), line, sys.stdout) + + warnings.showwarning = showwarning + # warnings.simplefilter('always') + + +def check_subprocess_call(cmd, timeout=5, stdout_regex=None, + stderr_regex=None): + """Runs a command in a subprocess with timeout in seconds. + + A SIGTERM is sent after `timeout` and if it does not terminate, a + SIGKILL is sent after `2 * timeout`. + + Also checks returncode is zero, stdout if stdout_regex is set, and + stderr if stderr_regex is set. + """ + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + def terminate_process(): # pragma: no cover + """ + Attempt to terminate a leftover process spawned during test execution: + ideally this should not be needed but can help avoid clogging the CI + workers in case of deadlocks. + """ + warnings.warn(f"Timeout running {cmd}") + proc.terminate() + + def kill_process(): # pragma: no cover + """ + Kill a leftover process spawned during test execution: ideally this + should not be needed but can help avoid clogging the CI workers in + case of deadlocks. + """ + warnings.warn(f"Timeout running {cmd}") + proc.kill() + + try: + if timeout is not None: + terminate_timer = threading.Timer(timeout, terminate_process) + terminate_timer.start() + kill_timer = threading.Timer(2 * timeout, kill_process) + kill_timer.start() + stdout, stderr = proc.communicate() + stdout, stderr = stdout.decode(), stderr.decode() + if proc.returncode != 0: + message = ( + 'Non-zero return code: {}.\nStdout:\n{}\n' + 'Stderr:\n{}').format( + proc.returncode, stdout, stderr) + raise ValueError(message) + + if (stdout_regex is not None and + not re.search(stdout_regex, stdout)): + raise ValueError( + "Unexpected stdout: {!r} does not match:\n{!r}".format( + stdout_regex, stdout)) + if (stderr_regex is not None and + not re.search(stderr_regex, stderr)): + raise ValueError( + "Unexpected stderr: {!r} does not match:\n{!r}".format( + stderr_regex, stderr)) + + finally: + if timeout is not None: + terminate_timer.cancel() + kill_timer.cancel() diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__init__.py b/.pythonlibs/lib/python3.10/site-packages/jwt/__init__.py new file mode 100644 index 0000000..68d09c1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/__init__.py @@ -0,0 +1,74 @@ +from .api_jwk import PyJWK, PyJWKSet +from .api_jws import ( + PyJWS, + get_algorithm_by_name, + get_unverified_header, + register_algorithm, + unregister_algorithm, +) +from .api_jwt import PyJWT, decode, encode +from .exceptions import ( + DecodeError, + ExpiredSignatureError, + ImmatureSignatureError, + InvalidAlgorithmError, + InvalidAudienceError, + InvalidIssuedAtError, + InvalidIssuerError, + InvalidKeyError, + InvalidSignatureError, + InvalidTokenError, + MissingRequiredClaimError, + PyJWKClientConnectionError, + PyJWKClientError, + PyJWKError, + PyJWKSetError, + PyJWTError, +) +from .jwks_client import PyJWKClient + +__version__ = "2.8.0" + +__title__ = "PyJWT" +__description__ = "JSON Web Token implementation in Python" +__url__ = "https://pyjwt.readthedocs.io" +__uri__ = __url__ +__doc__ = f"{__description__} <{__uri__}>" + +__author__ = "José Padilla" +__email__ = "hello@jpadilla.com" + +__license__ = "MIT" +__copyright__ = "Copyright 2015-2022 José Padilla" + + +__all__ = [ + "PyJWS", + "PyJWT", + "PyJWKClient", + "PyJWK", + "PyJWKSet", + "decode", + "encode", + "get_unverified_header", + "register_algorithm", + "unregister_algorithm", + "get_algorithm_by_name", + # Exceptions + "DecodeError", + "ExpiredSignatureError", + "ImmatureSignatureError", + "InvalidAlgorithmError", + "InvalidAudienceError", + "InvalidIssuedAtError", + "InvalidIssuerError", + "InvalidKeyError", + "InvalidSignatureError", + "InvalidTokenError", + "MissingRequiredClaimError", + "PyJWKClientConnectionError", + "PyJWKClientError", + "PyJWKError", + "PyJWKSetError", + "PyJWTError", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0b6dc4d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/algorithms.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/algorithms.cpython-310.pyc new file mode 100644 index 0000000..24202c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/algorithms.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwk.cpython-310.pyc new file mode 100644 index 0000000..4b61b8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jws.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jws.cpython-310.pyc new file mode 100644 index 0000000..7568331 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jws.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwt.cpython-310.pyc new file mode 100644 index 0000000..09102fb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/api_jwt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..e2097a7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/help.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/help.cpython-310.pyc new file mode 100644 index 0000000..aa1c8a9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/help.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwk_set_cache.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwk_set_cache.cpython-310.pyc new file mode 100644 index 0000000..27c24b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwk_set_cache.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwks_client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwks_client.cpython-310.pyc new file mode 100644 index 0000000..4d19131 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/jwks_client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000..44c8904 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..c1450e5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/warnings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/warnings.cpython-310.pyc new file mode 100644 index 0000000..56ffce3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/jwt/__pycache__/warnings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/algorithms.py b/.pythonlibs/lib/python3.10/site-packages/jwt/algorithms.py new file mode 100644 index 0000000..ed18715 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/algorithms.py @@ -0,0 +1,862 @@ +from __future__ import annotations + +import hashlib +import hmac +import json +import sys +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, ClassVar, NoReturn, Union, cast, overload + +from .exceptions import InvalidKeyError +from .types import HashlibHash, JWKDict +from .utils import ( + base64url_decode, + base64url_encode, + der_to_raw_signature, + force_bytes, + from_base64url_uint, + is_pem_format, + is_ssh_key, + raw_to_der_signature, + to_base64url_uint, +) + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + + +try: + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives.asymmetric.ec import ( + ECDSA, + SECP256K1, + SECP256R1, + SECP384R1, + SECP521R1, + EllipticCurve, + EllipticCurvePrivateKey, + EllipticCurvePrivateNumbers, + EllipticCurvePublicKey, + EllipticCurvePublicNumbers, + ) + from cryptography.hazmat.primitives.asymmetric.ed448 import ( + Ed448PrivateKey, + Ed448PublicKey, + ) + from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, + Ed25519PublicKey, + ) + from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, + RSAPrivateNumbers, + RSAPublicKey, + RSAPublicNumbers, + rsa_crt_dmp1, + rsa_crt_dmq1, + rsa_crt_iqmp, + rsa_recover_prime_factors, + ) + from cryptography.hazmat.primitives.serialization import ( + Encoding, + NoEncryption, + PrivateFormat, + PublicFormat, + load_pem_private_key, + load_pem_public_key, + load_ssh_public_key, + ) + + has_crypto = True +except ModuleNotFoundError: + has_crypto = False + + +if TYPE_CHECKING: + # Type aliases for convenience in algorithms method signatures + AllowedRSAKeys = RSAPrivateKey | RSAPublicKey + AllowedECKeys = EllipticCurvePrivateKey | EllipticCurvePublicKey + AllowedOKPKeys = ( + Ed25519PrivateKey | Ed25519PublicKey | Ed448PrivateKey | Ed448PublicKey + ) + AllowedKeys = AllowedRSAKeys | AllowedECKeys | AllowedOKPKeys + AllowedPrivateKeys = ( + RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey | Ed448PrivateKey + ) + AllowedPublicKeys = ( + RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey | Ed448PublicKey + ) + + +requires_cryptography = { + "RS256", + "RS384", + "RS512", + "ES256", + "ES256K", + "ES384", + "ES521", + "ES512", + "PS256", + "PS384", + "PS512", + "EdDSA", +} + + +def get_default_algorithms() -> dict[str, Algorithm]: + """ + Returns the algorithms that are implemented by the library. + """ + default_algorithms = { + "none": NoneAlgorithm(), + "HS256": HMACAlgorithm(HMACAlgorithm.SHA256), + "HS384": HMACAlgorithm(HMACAlgorithm.SHA384), + "HS512": HMACAlgorithm(HMACAlgorithm.SHA512), + } + + if has_crypto: + default_algorithms.update( + { + "RS256": RSAAlgorithm(RSAAlgorithm.SHA256), + "RS384": RSAAlgorithm(RSAAlgorithm.SHA384), + "RS512": RSAAlgorithm(RSAAlgorithm.SHA512), + "ES256": ECAlgorithm(ECAlgorithm.SHA256), + "ES256K": ECAlgorithm(ECAlgorithm.SHA256), + "ES384": ECAlgorithm(ECAlgorithm.SHA384), + "ES521": ECAlgorithm(ECAlgorithm.SHA512), + "ES512": ECAlgorithm( + ECAlgorithm.SHA512 + ), # Backward compat for #219 fix + "PS256": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256), + "PS384": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384), + "PS512": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512), + "EdDSA": OKPAlgorithm(), + } + ) + + return default_algorithms + + +class Algorithm(ABC): + """ + The interface for an algorithm used to sign and verify tokens. + """ + + def compute_hash_digest(self, bytestr: bytes) -> bytes: + """ + Compute a hash digest using the specified algorithm's hash algorithm. + + If there is no hash algorithm, raises a NotImplementedError. + """ + # lookup self.hash_alg if defined in a way that mypy can understand + hash_alg = getattr(self, "hash_alg", None) + if hash_alg is None: + raise NotImplementedError + + if ( + has_crypto + and isinstance(hash_alg, type) + and issubclass(hash_alg, hashes.HashAlgorithm) + ): + digest = hashes.Hash(hash_alg(), backend=default_backend()) + digest.update(bytestr) + return bytes(digest.finalize()) + else: + return bytes(hash_alg(bytestr).digest()) + + @abstractmethod + def prepare_key(self, key: Any) -> Any: + """ + Performs necessary validation and conversions on the key and returns + the key value in the proper format for sign() and verify(). + """ + + @abstractmethod + def sign(self, msg: bytes, key: Any) -> bytes: + """ + Returns a digital signature for the specified message + using the specified key value. + """ + + @abstractmethod + def verify(self, msg: bytes, key: Any, sig: bytes) -> bool: + """ + Verifies that the specified digital signature is valid + for the specified message and key values. + """ + + @overload + @staticmethod + @abstractmethod + def to_jwk(key_obj, as_dict: Literal[True]) -> JWKDict: + ... # pragma: no cover + + @overload + @staticmethod + @abstractmethod + def to_jwk(key_obj, as_dict: Literal[False] = False) -> str: + ... # pragma: no cover + + @staticmethod + @abstractmethod + def to_jwk(key_obj, as_dict: bool = False) -> Union[JWKDict, str]: + """ + Serializes a given key into a JWK + """ + + @staticmethod + @abstractmethod + def from_jwk(jwk: str | JWKDict) -> Any: + """ + Deserializes a given key from JWK back into a key object + """ + + +class NoneAlgorithm(Algorithm): + """ + Placeholder for use when no signing or verification + operations are required. + """ + + def prepare_key(self, key: str | None) -> None: + if key == "": + key = None + + if key is not None: + raise InvalidKeyError('When alg = "none", key value must be None.') + + return key + + def sign(self, msg: bytes, key: None) -> bytes: + return b"" + + def verify(self, msg: bytes, key: None, sig: bytes) -> bool: + return False + + @staticmethod + def to_jwk(key_obj: Any, as_dict: bool = False) -> NoReturn: + raise NotImplementedError() + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> NoReturn: + raise NotImplementedError() + + +class HMACAlgorithm(Algorithm): + """ + Performs signing and verification operations using HMAC + and the specified hash function. + """ + + SHA256: ClassVar[HashlibHash] = hashlib.sha256 + SHA384: ClassVar[HashlibHash] = hashlib.sha384 + SHA512: ClassVar[HashlibHash] = hashlib.sha512 + + def __init__(self, hash_alg: HashlibHash) -> None: + self.hash_alg = hash_alg + + def prepare_key(self, key: str | bytes) -> bytes: + key_bytes = force_bytes(key) + + if is_pem_format(key_bytes) or is_ssh_key(key_bytes): + raise InvalidKeyError( + "The specified key is an asymmetric key or x509 certificate and" + " should not be used as an HMAC secret." + ) + + return key_bytes + + @overload + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: Literal[True]) -> JWKDict: + ... # pragma: no cover + + @overload + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: Literal[False] = False) -> str: + ... # pragma: no cover + + @staticmethod + def to_jwk(key_obj: str | bytes, as_dict: bool = False) -> Union[JWKDict, str]: + jwk = { + "k": base64url_encode(force_bytes(key_obj)).decode(), + "kty": "oct", + } + + if as_dict: + return jwk + else: + return json.dumps(jwk) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> bytes: + try: + if isinstance(jwk, str): + obj: JWKDict = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") + + if obj.get("kty") != "oct": + raise InvalidKeyError("Not an HMAC key") + + return base64url_decode(obj["k"]) + + def sign(self, msg: bytes, key: bytes) -> bytes: + return hmac.new(key, msg, self.hash_alg).digest() + + def verify(self, msg: bytes, key: bytes, sig: bytes) -> bool: + return hmac.compare_digest(sig, self.sign(msg, key)) + + +if has_crypto: + + class RSAAlgorithm(Algorithm): + """ + Performs signing and verification operations using + RSASSA-PKCS-v1_5 and the specified hash function. + """ + + SHA256: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA256 + SHA384: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA384 + SHA512: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA512 + + def __init__(self, hash_alg: type[hashes.HashAlgorithm]) -> None: + self.hash_alg = hash_alg + + def prepare_key(self, key: AllowedRSAKeys | str | bytes) -> AllowedRSAKeys: + if isinstance(key, (RSAPrivateKey, RSAPublicKey)): + return key + + if not isinstance(key, (bytes, str)): + raise TypeError("Expecting a PEM-formatted key.") + + key_bytes = force_bytes(key) + + try: + if key_bytes.startswith(b"ssh-rsa"): + return cast(RSAPublicKey, load_ssh_public_key(key_bytes)) + else: + return cast( + RSAPrivateKey, load_pem_private_key(key_bytes, password=None) + ) + except ValueError: + return cast(RSAPublicKey, load_pem_public_key(key_bytes)) + + @overload + @staticmethod + def to_jwk(key_obj: AllowedRSAKeys, as_dict: Literal[True]) -> JWKDict: + ... # pragma: no cover + + @overload + @staticmethod + def to_jwk(key_obj: AllowedRSAKeys, as_dict: Literal[False] = False) -> str: + ... # pragma: no cover + + @staticmethod + def to_jwk( + key_obj: AllowedRSAKeys, as_dict: bool = False + ) -> Union[JWKDict, str]: + obj: dict[str, Any] | None = None + + if hasattr(key_obj, "private_numbers"): + # Private key + numbers = key_obj.private_numbers() + + obj = { + "kty": "RSA", + "key_ops": ["sign"], + "n": to_base64url_uint(numbers.public_numbers.n).decode(), + "e": to_base64url_uint(numbers.public_numbers.e).decode(), + "d": to_base64url_uint(numbers.d).decode(), + "p": to_base64url_uint(numbers.p).decode(), + "q": to_base64url_uint(numbers.q).decode(), + "dp": to_base64url_uint(numbers.dmp1).decode(), + "dq": to_base64url_uint(numbers.dmq1).decode(), + "qi": to_base64url_uint(numbers.iqmp).decode(), + } + + elif hasattr(key_obj, "verify"): + # Public key + numbers = key_obj.public_numbers() + + obj = { + "kty": "RSA", + "key_ops": ["verify"], + "n": to_base64url_uint(numbers.n).decode(), + "e": to_base64url_uint(numbers.e).decode(), + } + else: + raise InvalidKeyError("Not a public or private key") + + if as_dict: + return obj + else: + return json.dumps(obj) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedRSAKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") + + if obj.get("kty") != "RSA": + raise InvalidKeyError("Not an RSA key") + + if "d" in obj and "e" in obj and "n" in obj: + # Private key + if "oth" in obj: + raise InvalidKeyError( + "Unsupported RSA private key: > 2 primes not supported" + ) + + other_props = ["p", "q", "dp", "dq", "qi"] + props_found = [prop in obj for prop in other_props] + any_props_found = any(props_found) + + if any_props_found and not all(props_found): + raise InvalidKeyError( + "RSA key must include all parameters if any are present besides d" + ) + + public_numbers = RSAPublicNumbers( + from_base64url_uint(obj["e"]), + from_base64url_uint(obj["n"]), + ) + + if any_props_found: + numbers = RSAPrivateNumbers( + d=from_base64url_uint(obj["d"]), + p=from_base64url_uint(obj["p"]), + q=from_base64url_uint(obj["q"]), + dmp1=from_base64url_uint(obj["dp"]), + dmq1=from_base64url_uint(obj["dq"]), + iqmp=from_base64url_uint(obj["qi"]), + public_numbers=public_numbers, + ) + else: + d = from_base64url_uint(obj["d"]) + p, q = rsa_recover_prime_factors( + public_numbers.n, d, public_numbers.e + ) + + numbers = RSAPrivateNumbers( + d=d, + p=p, + q=q, + dmp1=rsa_crt_dmp1(d, p), + dmq1=rsa_crt_dmq1(d, q), + iqmp=rsa_crt_iqmp(p, q), + public_numbers=public_numbers, + ) + + return numbers.private_key() + elif "n" in obj and "e" in obj: + # Public key + return RSAPublicNumbers( + from_base64url_uint(obj["e"]), + from_base64url_uint(obj["n"]), + ).public_key() + else: + raise InvalidKeyError("Not a public or private key") + + def sign(self, msg: bytes, key: RSAPrivateKey) -> bytes: + return key.sign(msg, padding.PKCS1v15(), self.hash_alg()) + + def verify(self, msg: bytes, key: RSAPublicKey, sig: bytes) -> bool: + try: + key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg()) + return True + except InvalidSignature: + return False + + class ECAlgorithm(Algorithm): + """ + Performs signing and verification operations using + ECDSA and the specified hash function + """ + + SHA256: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA256 + SHA384: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA384 + SHA512: ClassVar[type[hashes.HashAlgorithm]] = hashes.SHA512 + + def __init__(self, hash_alg: type[hashes.HashAlgorithm]) -> None: + self.hash_alg = hash_alg + + def prepare_key(self, key: AllowedECKeys | str | bytes) -> AllowedECKeys: + if isinstance(key, (EllipticCurvePrivateKey, EllipticCurvePublicKey)): + return key + + if not isinstance(key, (bytes, str)): + raise TypeError("Expecting a PEM-formatted key.") + + key_bytes = force_bytes(key) + + # Attempt to load key. We don't know if it's + # a Signing Key or a Verifying Key, so we try + # the Verifying Key first. + try: + if key_bytes.startswith(b"ecdsa-sha2-"): + crypto_key = load_ssh_public_key(key_bytes) + else: + crypto_key = load_pem_public_key(key_bytes) # type: ignore[assignment] + except ValueError: + crypto_key = load_pem_private_key(key_bytes, password=None) # type: ignore[assignment] + + # Explicit check the key to prevent confusing errors from cryptography + if not isinstance( + crypto_key, (EllipticCurvePrivateKey, EllipticCurvePublicKey) + ): + raise InvalidKeyError( + "Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for ECDSA algorithms" + ) + + return crypto_key + + def sign(self, msg: bytes, key: EllipticCurvePrivateKey) -> bytes: + der_sig = key.sign(msg, ECDSA(self.hash_alg())) + + return der_to_raw_signature(der_sig, key.curve) + + def verify(self, msg: bytes, key: "AllowedECKeys", sig: bytes) -> bool: + try: + der_sig = raw_to_der_signature(sig, key.curve) + except ValueError: + return False + + try: + public_key = ( + key.public_key() + if isinstance(key, EllipticCurvePrivateKey) + else key + ) + public_key.verify(der_sig, msg, ECDSA(self.hash_alg())) + return True + except InvalidSignature: + return False + + @overload + @staticmethod + def to_jwk(key_obj: AllowedECKeys, as_dict: Literal[True]) -> JWKDict: + ... # pragma: no cover + + @overload + @staticmethod + def to_jwk(key_obj: AllowedECKeys, as_dict: Literal[False] = False) -> str: + ... # pragma: no cover + + @staticmethod + def to_jwk( + key_obj: AllowedECKeys, as_dict: bool = False + ) -> Union[JWKDict, str]: + if isinstance(key_obj, EllipticCurvePrivateKey): + public_numbers = key_obj.public_key().public_numbers() + elif isinstance(key_obj, EllipticCurvePublicKey): + public_numbers = key_obj.public_numbers() + else: + raise InvalidKeyError("Not a public or private key") + + if isinstance(key_obj.curve, SECP256R1): + crv = "P-256" + elif isinstance(key_obj.curve, SECP384R1): + crv = "P-384" + elif isinstance(key_obj.curve, SECP521R1): + crv = "P-521" + elif isinstance(key_obj.curve, SECP256K1): + crv = "secp256k1" + else: + raise InvalidKeyError(f"Invalid curve: {key_obj.curve}") + + obj: dict[str, Any] = { + "kty": "EC", + "crv": crv, + "x": to_base64url_uint(public_numbers.x).decode(), + "y": to_base64url_uint(public_numbers.y).decode(), + } + + if isinstance(key_obj, EllipticCurvePrivateKey): + obj["d"] = to_base64url_uint( + key_obj.private_numbers().private_value + ).decode() + + if as_dict: + return obj + else: + return json.dumps(obj) + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedECKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") + + if obj.get("kty") != "EC": + raise InvalidKeyError("Not an Elliptic curve key") + + if "x" not in obj or "y" not in obj: + raise InvalidKeyError("Not an Elliptic curve key") + + x = base64url_decode(obj.get("x")) + y = base64url_decode(obj.get("y")) + + curve = obj.get("crv") + curve_obj: EllipticCurve + + if curve == "P-256": + if len(x) == len(y) == 32: + curve_obj = SECP256R1() + else: + raise InvalidKeyError("Coords should be 32 bytes for curve P-256") + elif curve == "P-384": + if len(x) == len(y) == 48: + curve_obj = SECP384R1() + else: + raise InvalidKeyError("Coords should be 48 bytes for curve P-384") + elif curve == "P-521": + if len(x) == len(y) == 66: + curve_obj = SECP521R1() + else: + raise InvalidKeyError("Coords should be 66 bytes for curve P-521") + elif curve == "secp256k1": + if len(x) == len(y) == 32: + curve_obj = SECP256K1() + else: + raise InvalidKeyError( + "Coords should be 32 bytes for curve secp256k1" + ) + else: + raise InvalidKeyError(f"Invalid curve: {curve}") + + public_numbers = EllipticCurvePublicNumbers( + x=int.from_bytes(x, byteorder="big"), + y=int.from_bytes(y, byteorder="big"), + curve=curve_obj, + ) + + if "d" not in obj: + return public_numbers.public_key() + + d = base64url_decode(obj.get("d")) + if len(d) != len(x): + raise InvalidKeyError( + "D should be {} bytes for curve {}", len(x), curve + ) + + return EllipticCurvePrivateNumbers( + int.from_bytes(d, byteorder="big"), public_numbers + ).private_key() + + class RSAPSSAlgorithm(RSAAlgorithm): + """ + Performs a signature using RSASSA-PSS with MGF1 + """ + + def sign(self, msg: bytes, key: RSAPrivateKey) -> bytes: + return key.sign( + msg, + padding.PSS( + mgf=padding.MGF1(self.hash_alg()), + salt_length=self.hash_alg().digest_size, + ), + self.hash_alg(), + ) + + def verify(self, msg: bytes, key: RSAPublicKey, sig: bytes) -> bool: + try: + key.verify( + sig, + msg, + padding.PSS( + mgf=padding.MGF1(self.hash_alg()), + salt_length=self.hash_alg().digest_size, + ), + self.hash_alg(), + ) + return True + except InvalidSignature: + return False + + class OKPAlgorithm(Algorithm): + """ + Performs signing and verification operations using EdDSA + + This class requires ``cryptography>=2.6`` to be installed. + """ + + def __init__(self, **kwargs: Any) -> None: + pass + + def prepare_key(self, key: AllowedOKPKeys | str | bytes) -> AllowedOKPKeys: + if isinstance(key, (bytes, str)): + key_str = key.decode("utf-8") if isinstance(key, bytes) else key + key_bytes = key.encode("utf-8") if isinstance(key, str) else key + + if "-----BEGIN PUBLIC" in key_str: + key = load_pem_public_key(key_bytes) # type: ignore[assignment] + elif "-----BEGIN PRIVATE" in key_str: + key = load_pem_private_key(key_bytes, password=None) # type: ignore[assignment] + elif key_str[0:4] == "ssh-": + key = load_ssh_public_key(key_bytes) # type: ignore[assignment] + + # Explicit check the key to prevent confusing errors from cryptography + if not isinstance( + key, + (Ed25519PrivateKey, Ed25519PublicKey, Ed448PrivateKey, Ed448PublicKey), + ): + raise InvalidKeyError( + "Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for EdDSA algorithms" + ) + + return key + + def sign( + self, msg: str | bytes, key: Ed25519PrivateKey | Ed448PrivateKey + ) -> bytes: + """ + Sign a message ``msg`` using the EdDSA private key ``key`` + :param str|bytes msg: Message to sign + :param Ed25519PrivateKey}Ed448PrivateKey key: A :class:`.Ed25519PrivateKey` + or :class:`.Ed448PrivateKey` isinstance + :return bytes signature: The signature, as bytes + """ + msg_bytes = msg.encode("utf-8") if isinstance(msg, str) else msg + return key.sign(msg_bytes) + + def verify( + self, msg: str | bytes, key: AllowedOKPKeys, sig: str | bytes + ) -> bool: + """ + Verify a given ``msg`` against a signature ``sig`` using the EdDSA key ``key`` + + :param str|bytes sig: EdDSA signature to check ``msg`` against + :param str|bytes msg: Message to sign + :param Ed25519PrivateKey|Ed25519PublicKey|Ed448PrivateKey|Ed448PublicKey key: + A private or public EdDSA key instance + :return bool verified: True if signature is valid, False if not. + """ + try: + msg_bytes = msg.encode("utf-8") if isinstance(msg, str) else msg + sig_bytes = sig.encode("utf-8") if isinstance(sig, str) else sig + + public_key = ( + key.public_key() + if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)) + else key + ) + public_key.verify(sig_bytes, msg_bytes) + return True # If no exception was raised, the signature is valid. + except InvalidSignature: + return False + + @overload + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: Literal[True]) -> JWKDict: + ... # pragma: no cover + + @overload + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: Literal[False] = False) -> str: + ... # pragma: no cover + + @staticmethod + def to_jwk(key: AllowedOKPKeys, as_dict: bool = False) -> Union[JWKDict, str]: + if isinstance(key, (Ed25519PublicKey, Ed448PublicKey)): + x = key.public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + crv = "Ed25519" if isinstance(key, Ed25519PublicKey) else "Ed448" + + obj = { + "x": base64url_encode(force_bytes(x)).decode(), + "kty": "OKP", + "crv": crv, + } + + if as_dict: + return obj + else: + return json.dumps(obj) + + if isinstance(key, (Ed25519PrivateKey, Ed448PrivateKey)): + d = key.private_bytes( + encoding=Encoding.Raw, + format=PrivateFormat.Raw, + encryption_algorithm=NoEncryption(), + ) + + x = key.public_key().public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw, + ) + + crv = "Ed25519" if isinstance(key, Ed25519PrivateKey) else "Ed448" + obj = { + "x": base64url_encode(force_bytes(x)).decode(), + "d": base64url_encode(force_bytes(d)).decode(), + "kty": "OKP", + "crv": crv, + } + + if as_dict: + return obj + else: + return json.dumps(obj) + + raise InvalidKeyError("Not a public or private key") + + @staticmethod + def from_jwk(jwk: str | JWKDict) -> AllowedOKPKeys: + try: + if isinstance(jwk, str): + obj = json.loads(jwk) + elif isinstance(jwk, dict): + obj = jwk + else: + raise ValueError + except ValueError: + raise InvalidKeyError("Key is not valid JSON") + + if obj.get("kty") != "OKP": + raise InvalidKeyError("Not an Octet Key Pair") + + curve = obj.get("crv") + if curve != "Ed25519" and curve != "Ed448": + raise InvalidKeyError(f"Invalid curve: {curve}") + + if "x" not in obj: + raise InvalidKeyError('OKP should have "x" parameter') + x = base64url_decode(obj.get("x")) + + try: + if "d" not in obj: + if curve == "Ed25519": + return Ed25519PublicKey.from_public_bytes(x) + return Ed448PublicKey.from_public_bytes(x) + d = base64url_decode(obj.get("d")) + if curve == "Ed25519": + return Ed25519PrivateKey.from_private_bytes(d) + return Ed448PrivateKey.from_private_bytes(d) + except ValueError as err: + raise InvalidKeyError("Invalid key parameter") from err diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwk.py b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwk.py new file mode 100644 index 0000000..456c7f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwk.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +import json +import time +from typing import Any + +from .algorithms import get_default_algorithms, has_crypto, requires_cryptography +from .exceptions import InvalidKeyError, PyJWKError, PyJWKSetError, PyJWTError +from .types import JWKDict + + +class PyJWK: + def __init__(self, jwk_data: JWKDict, algorithm: str | None = None) -> None: + self._algorithms = get_default_algorithms() + self._jwk_data = jwk_data + + kty = self._jwk_data.get("kty", None) + if not kty: + raise InvalidKeyError(f"kty is not found: {self._jwk_data}") + + if not algorithm and isinstance(self._jwk_data, dict): + algorithm = self._jwk_data.get("alg", None) + + if not algorithm: + # Determine alg with kty (and crv). + crv = self._jwk_data.get("crv", None) + if kty == "EC": + if crv == "P-256" or not crv: + algorithm = "ES256" + elif crv == "P-384": + algorithm = "ES384" + elif crv == "P-521": + algorithm = "ES512" + elif crv == "secp256k1": + algorithm = "ES256K" + else: + raise InvalidKeyError(f"Unsupported crv: {crv}") + elif kty == "RSA": + algorithm = "RS256" + elif kty == "oct": + algorithm = "HS256" + elif kty == "OKP": + if not crv: + raise InvalidKeyError(f"crv is not found: {self._jwk_data}") + if crv == "Ed25519": + algorithm = "EdDSA" + else: + raise InvalidKeyError(f"Unsupported crv: {crv}") + else: + raise InvalidKeyError(f"Unsupported kty: {kty}") + + if not has_crypto and algorithm in requires_cryptography: + raise PyJWKError(f"{algorithm} requires 'cryptography' to be installed.") + + self.Algorithm = self._algorithms.get(algorithm) + + if not self.Algorithm: + raise PyJWKError(f"Unable to find an algorithm for key: {self._jwk_data}") + + self.key = self.Algorithm.from_jwk(self._jwk_data) + + @staticmethod + def from_dict(obj: JWKDict, algorithm: str | None = None) -> "PyJWK": + return PyJWK(obj, algorithm) + + @staticmethod + def from_json(data: str, algorithm: None = None) -> "PyJWK": + obj = json.loads(data) + return PyJWK.from_dict(obj, algorithm) + + @property + def key_type(self) -> str | None: + return self._jwk_data.get("kty", None) + + @property + def key_id(self) -> str | None: + return self._jwk_data.get("kid", None) + + @property + def public_key_use(self) -> str | None: + return self._jwk_data.get("use", None) + + +class PyJWKSet: + def __init__(self, keys: list[JWKDict]) -> None: + self.keys = [] + + if not keys: + raise PyJWKSetError("The JWK Set did not contain any keys") + + if not isinstance(keys, list): + raise PyJWKSetError("Invalid JWK Set value") + + for key in keys: + try: + self.keys.append(PyJWK(key)) + except PyJWTError: + # skip unusable keys + continue + + if len(self.keys) == 0: + raise PyJWKSetError( + "The JWK Set did not contain any usable keys. Perhaps 'cryptography' is not installed?" + ) + + @staticmethod + def from_dict(obj: dict[str, Any]) -> "PyJWKSet": + keys = obj.get("keys", []) + return PyJWKSet(keys) + + @staticmethod + def from_json(data: str) -> "PyJWKSet": + obj = json.loads(data) + return PyJWKSet.from_dict(obj) + + def __getitem__(self, kid: str) -> "PyJWK": + for key in self.keys: + if key.key_id == kid: + return key + raise KeyError(f"keyset has no key for kid: {kid}") + + +class PyJWTSetWithTimestamp: + def __init__(self, jwk_set: PyJWKSet): + self.jwk_set = jwk_set + self.timestamp = time.monotonic() + + def get_jwk_set(self) -> PyJWKSet: + return self.jwk_set + + def get_timestamp(self) -> float: + return self.timestamp diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/api_jws.py b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jws.py new file mode 100644 index 0000000..fa6708c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jws.py @@ -0,0 +1,328 @@ +from __future__ import annotations + +import binascii +import json +import warnings +from typing import TYPE_CHECKING, Any + +from .algorithms import ( + Algorithm, + get_default_algorithms, + has_crypto, + requires_cryptography, +) +from .exceptions import ( + DecodeError, + InvalidAlgorithmError, + InvalidSignatureError, + InvalidTokenError, +) +from .utils import base64url_decode, base64url_encode +from .warnings import RemovedInPyjwt3Warning + +if TYPE_CHECKING: + from .algorithms import AllowedPrivateKeys, AllowedPublicKeys + + +class PyJWS: + header_typ = "JWT" + + def __init__( + self, + algorithms: list[str] | None = None, + options: dict[str, Any] | None = None, + ) -> None: + self._algorithms = get_default_algorithms() + self._valid_algs = ( + set(algorithms) if algorithms is not None else set(self._algorithms) + ) + + # Remove algorithms that aren't on the whitelist + for key in list(self._algorithms.keys()): + if key not in self._valid_algs: + del self._algorithms[key] + + if options is None: + options = {} + self.options = {**self._get_default_options(), **options} + + @staticmethod + def _get_default_options() -> dict[str, bool]: + return {"verify_signature": True} + + def register_algorithm(self, alg_id: str, alg_obj: Algorithm) -> None: + """ + Registers a new Algorithm for use when creating and verifying tokens. + """ + if alg_id in self._algorithms: + raise ValueError("Algorithm already has a handler.") + + if not isinstance(alg_obj, Algorithm): + raise TypeError("Object is not of type `Algorithm`") + + self._algorithms[alg_id] = alg_obj + self._valid_algs.add(alg_id) + + def unregister_algorithm(self, alg_id: str) -> None: + """ + Unregisters an Algorithm for use when creating and verifying tokens + Throws KeyError if algorithm is not registered. + """ + if alg_id not in self._algorithms: + raise KeyError( + "The specified algorithm could not be removed" + " because it is not registered." + ) + + del self._algorithms[alg_id] + self._valid_algs.remove(alg_id) + + def get_algorithms(self) -> list[str]: + """ + Returns a list of supported values for the 'alg' parameter. + """ + return list(self._valid_algs) + + def get_algorithm_by_name(self, alg_name: str) -> Algorithm: + """ + For a given string name, return the matching Algorithm object. + + Example usage: + + >>> jws_obj.get_algorithm_by_name("RS256") + """ + try: + return self._algorithms[alg_name] + except KeyError as e: + if not has_crypto and alg_name in requires_cryptography: + raise NotImplementedError( + f"Algorithm '{alg_name}' could not be found. Do you have cryptography installed?" + ) from e + raise NotImplementedError("Algorithm not supported") from e + + def encode( + self, + payload: bytes, + key: AllowedPrivateKeys | str | bytes, + algorithm: str | None = "HS256", + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + is_payload_detached: bool = False, + sort_headers: bool = True, + ) -> str: + segments = [] + + # declare a new var to narrow the type for type checkers + algorithm_: str = algorithm if algorithm is not None else "none" + + # Prefer headers values if present to function parameters. + if headers: + headers_alg = headers.get("alg") + if headers_alg: + algorithm_ = headers["alg"] + + headers_b64 = headers.get("b64") + if headers_b64 is False: + is_payload_detached = True + + # Header + header: dict[str, Any] = {"typ": self.header_typ, "alg": algorithm_} + + if headers: + self._validate_headers(headers) + header.update(headers) + + if not header["typ"]: + del header["typ"] + + if is_payload_detached: + header["b64"] = False + elif "b64" in header: + # True is the standard value for b64, so no need for it + del header["b64"] + + json_header = json.dumps( + header, separators=(",", ":"), cls=json_encoder, sort_keys=sort_headers + ).encode() + + segments.append(base64url_encode(json_header)) + + if is_payload_detached: + msg_payload = payload + else: + msg_payload = base64url_encode(payload) + segments.append(msg_payload) + + # Segments + signing_input = b".".join(segments) + + alg_obj = self.get_algorithm_by_name(algorithm_) + key = alg_obj.prepare_key(key) + signature = alg_obj.sign(signing_input, key) + + segments.append(base64url_encode(signature)) + + # Don't put the payload content inside the encoded token when detached + if is_payload_detached: + segments[1] = b"" + encoded_string = b".".join(segments) + + return encoded_string.decode("utf-8") + + def decode_complete( + self, + jwt: str | bytes, + key: AllowedPublicKeys | str | bytes = "", + algorithms: list[str] | None = None, + options: dict[str, Any] | None = None, + detached_payload: bytes | None = None, + **kwargs, + ) -> dict[str, Any]: + if kwargs: + warnings.warn( + "passing additional kwargs to decode_complete() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + ) + if options is None: + options = {} + merged_options = {**self.options, **options} + verify_signature = merged_options["verify_signature"] + + if verify_signature and not algorithms: + raise DecodeError( + 'It is required that you pass in a value for the "algorithms" argument when calling decode().' + ) + + payload, signing_input, header, signature = self._load(jwt) + + if header.get("b64", True) is False: + if detached_payload is None: + raise DecodeError( + 'It is required that you pass in a value for the "detached_payload" argument to decode a message having the b64 header set to false.' + ) + payload = detached_payload + signing_input = b".".join([signing_input.rsplit(b".", 1)[0], payload]) + + if verify_signature: + self._verify_signature(signing_input, header, signature, key, algorithms) + + return { + "payload": payload, + "header": header, + "signature": signature, + } + + def decode( + self, + jwt: str | bytes, + key: AllowedPublicKeys | str | bytes = "", + algorithms: list[str] | None = None, + options: dict[str, Any] | None = None, + detached_payload: bytes | None = None, + **kwargs, + ) -> Any: + if kwargs: + warnings.warn( + "passing additional kwargs to decode() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + ) + decoded = self.decode_complete( + jwt, key, algorithms, options, detached_payload=detached_payload + ) + return decoded["payload"] + + def get_unverified_header(self, jwt: str | bytes) -> dict[str, Any]: + """Returns back the JWT header parameters as a dict() + + Note: The signature is not verified so the header parameters + should not be fully trusted until signature verification is complete + """ + headers = self._load(jwt)[2] + self._validate_headers(headers) + + return headers + + def _load(self, jwt: str | bytes) -> tuple[bytes, bytes, dict[str, Any], bytes]: + if isinstance(jwt, str): + jwt = jwt.encode("utf-8") + + if not isinstance(jwt, bytes): + raise DecodeError(f"Invalid token type. Token must be a {bytes}") + + try: + signing_input, crypto_segment = jwt.rsplit(b".", 1) + header_segment, payload_segment = signing_input.split(b".", 1) + except ValueError as err: + raise DecodeError("Not enough segments") from err + + try: + header_data = base64url_decode(header_segment) + except (TypeError, binascii.Error) as err: + raise DecodeError("Invalid header padding") from err + + try: + header = json.loads(header_data) + except ValueError as e: + raise DecodeError(f"Invalid header string: {e}") from e + + if not isinstance(header, dict): + raise DecodeError("Invalid header string: must be a json object") + + try: + payload = base64url_decode(payload_segment) + except (TypeError, binascii.Error) as err: + raise DecodeError("Invalid payload padding") from err + + try: + signature = base64url_decode(crypto_segment) + except (TypeError, binascii.Error) as err: + raise DecodeError("Invalid crypto padding") from err + + return (payload, signing_input, header, signature) + + def _verify_signature( + self, + signing_input: bytes, + header: dict[str, Any], + signature: bytes, + key: AllowedPublicKeys | str | bytes = "", + algorithms: list[str] | None = None, + ) -> None: + try: + alg = header["alg"] + except KeyError: + raise InvalidAlgorithmError("Algorithm not specified") + + if not alg or (algorithms is not None and alg not in algorithms): + raise InvalidAlgorithmError("The specified alg value is not allowed") + + try: + alg_obj = self.get_algorithm_by_name(alg) + except NotImplementedError as e: + raise InvalidAlgorithmError("Algorithm not supported") from e + prepared_key = alg_obj.prepare_key(key) + + if not alg_obj.verify(signing_input, prepared_key, signature): + raise InvalidSignatureError("Signature verification failed") + + def _validate_headers(self, headers: dict[str, Any]) -> None: + if "kid" in headers: + self._validate_kid(headers["kid"]) + + def _validate_kid(self, kid: Any) -> None: + if not isinstance(kid, str): + raise InvalidTokenError("Key ID header parameter must be a string") + + +_jws_global_obj = PyJWS() +encode = _jws_global_obj.encode +decode_complete = _jws_global_obj.decode_complete +decode = _jws_global_obj.decode +register_algorithm = _jws_global_obj.register_algorithm +unregister_algorithm = _jws_global_obj.unregister_algorithm +get_algorithm_by_name = _jws_global_obj.get_algorithm_by_name +get_unverified_header = _jws_global_obj.get_unverified_header diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwt.py b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwt.py new file mode 100644 index 0000000..48d739a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/api_jwt.py @@ -0,0 +1,372 @@ +from __future__ import annotations + +import json +import warnings +from calendar import timegm +from collections.abc import Iterable +from datetime import datetime, timedelta, timezone +from typing import TYPE_CHECKING, Any + +from . import api_jws +from .exceptions import ( + DecodeError, + ExpiredSignatureError, + ImmatureSignatureError, + InvalidAudienceError, + InvalidIssuedAtError, + InvalidIssuerError, + MissingRequiredClaimError, +) +from .warnings import RemovedInPyjwt3Warning + +if TYPE_CHECKING: + from .algorithms import AllowedPrivateKeys, AllowedPublicKeys + + +class PyJWT: + def __init__(self, options: dict[str, Any] | None = None) -> None: + if options is None: + options = {} + self.options: dict[str, Any] = {**self._get_default_options(), **options} + + @staticmethod + def _get_default_options() -> dict[str, bool | list[str]]: + return { + "verify_signature": True, + "verify_exp": True, + "verify_nbf": True, + "verify_iat": True, + "verify_aud": True, + "verify_iss": True, + "require": [], + } + + def encode( + self, + payload: dict[str, Any], + key: AllowedPrivateKeys | str | bytes, + algorithm: str | None = "HS256", + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + sort_headers: bool = True, + ) -> str: + # Check that we get a dict + if not isinstance(payload, dict): + raise TypeError( + "Expecting a dict object, as JWT only supports " + "JSON objects as payloads." + ) + + # Payload + payload = payload.copy() + for time_claim in ["exp", "iat", "nbf"]: + # Convert datetime to a intDate value in known time-format claims + if isinstance(payload.get(time_claim), datetime): + payload[time_claim] = timegm(payload[time_claim].utctimetuple()) + + json_payload = self._encode_payload( + payload, + headers=headers, + json_encoder=json_encoder, + ) + + return api_jws.encode( + json_payload, + key, + algorithm, + headers, + json_encoder, + sort_headers=sort_headers, + ) + + def _encode_payload( + self, + payload: dict[str, Any], + headers: dict[str, Any] | None = None, + json_encoder: type[json.JSONEncoder] | None = None, + ) -> bytes: + """ + Encode a given payload to the bytes to be signed. + + This method is intended to be overridden by subclasses that need to + encode the payload in a different way, e.g. compress the payload. + """ + return json.dumps( + payload, + separators=(",", ":"), + cls=json_encoder, + ).encode("utf-8") + + def decode_complete( + self, + jwt: str | bytes, + key: AllowedPublicKeys | str | bytes = "", + algorithms: list[str] | None = None, + options: dict[str, Any] | None = None, + # deprecated arg, remove in pyjwt3 + verify: bool | None = None, + # could be used as passthrough to api_jws, consider removal in pyjwt3 + detached_payload: bytes | None = None, + # passthrough arguments to _validate_claims + # consider putting in options + audience: str | Iterable[str] | None = None, + issuer: str | None = None, + leeway: float | timedelta = 0, + # kwargs + **kwargs: Any, + ) -> dict[str, Any]: + if kwargs: + warnings.warn( + "passing additional kwargs to decode_complete() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + ) + options = dict(options or {}) # shallow-copy or initialize an empty dict + options.setdefault("verify_signature", True) + + # If the user has set the legacy `verify` argument, and it doesn't match + # what the relevant `options` entry for the argument is, inform the user + # that they're likely making a mistake. + if verify is not None and verify != options["verify_signature"]: + warnings.warn( + "The `verify` argument to `decode` does nothing in PyJWT 2.0 and newer. " + "The equivalent is setting `verify_signature` to False in the `options` dictionary. " + "This invocation has a mismatch between the kwarg and the option entry.", + category=DeprecationWarning, + ) + + if not options["verify_signature"]: + options.setdefault("verify_exp", False) + options.setdefault("verify_nbf", False) + options.setdefault("verify_iat", False) + options.setdefault("verify_aud", False) + options.setdefault("verify_iss", False) + + if options["verify_signature"] and not algorithms: + raise DecodeError( + 'It is required that you pass in a value for the "algorithms" argument when calling decode().' + ) + + decoded = api_jws.decode_complete( + jwt, + key=key, + algorithms=algorithms, + options=options, + detached_payload=detached_payload, + ) + + payload = self._decode_payload(decoded) + + merged_options = {**self.options, **options} + self._validate_claims( + payload, merged_options, audience=audience, issuer=issuer, leeway=leeway + ) + + decoded["payload"] = payload + return decoded + + def _decode_payload(self, decoded: dict[str, Any]) -> Any: + """ + Decode the payload from a JWS dictionary (payload, signature, header). + + This method is intended to be overridden by subclasses that need to + decode the payload in a different way, e.g. decompress compressed + payloads. + """ + try: + payload = json.loads(decoded["payload"]) + except ValueError as e: + raise DecodeError(f"Invalid payload string: {e}") + if not isinstance(payload, dict): + raise DecodeError("Invalid payload string: must be a json object") + return payload + + def decode( + self, + jwt: str | bytes, + key: AllowedPublicKeys | str | bytes = "", + algorithms: list[str] | None = None, + options: dict[str, Any] | None = None, + # deprecated arg, remove in pyjwt3 + verify: bool | None = None, + # could be used as passthrough to api_jws, consider removal in pyjwt3 + detached_payload: bytes | None = None, + # passthrough arguments to _validate_claims + # consider putting in options + audience: str | Iterable[str] | None = None, + issuer: str | None = None, + leeway: float | timedelta = 0, + # kwargs + **kwargs: Any, + ) -> Any: + if kwargs: + warnings.warn( + "passing additional kwargs to decode() is deprecated " + "and will be removed in pyjwt version 3. " + f"Unsupported kwargs: {tuple(kwargs.keys())}", + RemovedInPyjwt3Warning, + ) + decoded = self.decode_complete( + jwt, + key, + algorithms, + options, + verify=verify, + detached_payload=detached_payload, + audience=audience, + issuer=issuer, + leeway=leeway, + ) + return decoded["payload"] + + def _validate_claims( + self, + payload: dict[str, Any], + options: dict[str, Any], + audience=None, + issuer=None, + leeway: float | timedelta = 0, + ) -> None: + if isinstance(leeway, timedelta): + leeway = leeway.total_seconds() + + if audience is not None and not isinstance(audience, (str, Iterable)): + raise TypeError("audience must be a string, iterable or None") + + self._validate_required_claims(payload, options) + + now = datetime.now(tz=timezone.utc).timestamp() + + if "iat" in payload and options["verify_iat"]: + self._validate_iat(payload, now, leeway) + + if "nbf" in payload and options["verify_nbf"]: + self._validate_nbf(payload, now, leeway) + + if "exp" in payload and options["verify_exp"]: + self._validate_exp(payload, now, leeway) + + if options["verify_iss"]: + self._validate_iss(payload, issuer) + + if options["verify_aud"]: + self._validate_aud( + payload, audience, strict=options.get("strict_aud", False) + ) + + def _validate_required_claims( + self, + payload: dict[str, Any], + options: dict[str, Any], + ) -> None: + for claim in options["require"]: + if payload.get(claim) is None: + raise MissingRequiredClaimError(claim) + + def _validate_iat( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + iat = int(payload["iat"]) + except ValueError: + raise InvalidIssuedAtError("Issued At claim (iat) must be an integer.") + if iat > (now + leeway): + raise ImmatureSignatureError("The token is not yet valid (iat)") + + def _validate_nbf( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + nbf = int(payload["nbf"]) + except ValueError: + raise DecodeError("Not Before claim (nbf) must be an integer.") + + if nbf > (now + leeway): + raise ImmatureSignatureError("The token is not yet valid (nbf)") + + def _validate_exp( + self, + payload: dict[str, Any], + now: float, + leeway: float, + ) -> None: + try: + exp = int(payload["exp"]) + except ValueError: + raise DecodeError("Expiration Time claim (exp) must be an" " integer.") + + if exp <= (now - leeway): + raise ExpiredSignatureError("Signature has expired") + + def _validate_aud( + self, + payload: dict[str, Any], + audience: str | Iterable[str] | None, + *, + strict: bool = False, + ) -> None: + if audience is None: + if "aud" not in payload or not payload["aud"]: + return + # Application did not specify an audience, but + # the token has the 'aud' claim + raise InvalidAudienceError("Invalid audience") + + if "aud" not in payload or not payload["aud"]: + # Application specified an audience, but it could not be + # verified since the token does not contain a claim. + raise MissingRequiredClaimError("aud") + + audience_claims = payload["aud"] + + # In strict mode, we forbid list matching: the supplied audience + # must be a string, and it must exactly match the audience claim. + if strict: + # Only a single audience is allowed in strict mode. + if not isinstance(audience, str): + raise InvalidAudienceError("Invalid audience (strict)") + + # Only a single audience claim is allowed in strict mode. + if not isinstance(audience_claims, str): + raise InvalidAudienceError("Invalid claim format in token (strict)") + + if audience != audience_claims: + raise InvalidAudienceError("Audience doesn't match (strict)") + + return + + if isinstance(audience_claims, str): + audience_claims = [audience_claims] + if not isinstance(audience_claims, list): + raise InvalidAudienceError("Invalid claim format in token") + if any(not isinstance(c, str) for c in audience_claims): + raise InvalidAudienceError("Invalid claim format in token") + + if isinstance(audience, str): + audience = [audience] + + if all(aud not in audience_claims for aud in audience): + raise InvalidAudienceError("Audience doesn't match") + + def _validate_iss(self, payload: dict[str, Any], issuer: Any) -> None: + if issuer is None: + return + + if "iss" not in payload: + raise MissingRequiredClaimError("iss") + + if payload["iss"] != issuer: + raise InvalidIssuerError("Invalid issuer") + + +_jwt_global_obj = PyJWT() +encode = _jwt_global_obj.encode +decode_complete = _jwt_global_obj.decode_complete +decode = _jwt_global_obj.decode diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/jwt/exceptions.py new file mode 100644 index 0000000..8ac6ecf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/exceptions.py @@ -0,0 +1,70 @@ +class PyJWTError(Exception): + """ + Base class for all exceptions + """ + + pass + + +class InvalidTokenError(PyJWTError): + pass + + +class DecodeError(InvalidTokenError): + pass + + +class InvalidSignatureError(DecodeError): + pass + + +class ExpiredSignatureError(InvalidTokenError): + pass + + +class InvalidAudienceError(InvalidTokenError): + pass + + +class InvalidIssuerError(InvalidTokenError): + pass + + +class InvalidIssuedAtError(InvalidTokenError): + pass + + +class ImmatureSignatureError(InvalidTokenError): + pass + + +class InvalidKeyError(PyJWTError): + pass + + +class InvalidAlgorithmError(InvalidTokenError): + pass + + +class MissingRequiredClaimError(InvalidTokenError): + def __init__(self, claim: str) -> None: + self.claim = claim + + def __str__(self) -> str: + return f'Token is missing the "{self.claim}" claim' + + +class PyJWKError(PyJWTError): + pass + + +class PyJWKSetError(PyJWTError): + pass + + +class PyJWKClientError(PyJWTError): + pass + + +class PyJWKClientConnectionError(PyJWKClientError): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/help.py b/.pythonlibs/lib/python3.10/site-packages/jwt/help.py new file mode 100644 index 0000000..80b0ca5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/help.py @@ -0,0 +1,64 @@ +import json +import platform +import sys +from typing import Dict + +from . import __version__ as pyjwt_version + +try: + import cryptography + + cryptography_version = cryptography.__version__ +except ModuleNotFoundError: + cryptography_version = "" + + +def info() -> Dict[str, Dict[str, str]]: + """ + Generate information for a bug report. + Based on the requests package help utility module. + """ + try: + platform_info = { + "system": platform.system(), + "release": platform.release(), + } + except OSError: + platform_info = {"system": "Unknown", "release": "Unknown"} + + implementation = platform.python_implementation() + + if implementation == "CPython": + implementation_version = platform.python_version() + elif implementation == "PyPy": + pypy_version_info = sys.pypy_version_info # type: ignore[attr-defined] + implementation_version = ( + f"{pypy_version_info.major}." + f"{pypy_version_info.minor}." + f"{pypy_version_info.micro}" + ) + if pypy_version_info.releaselevel != "final": + implementation_version = "".join( + [implementation_version, pypy_version_info.releaselevel] + ) + else: + implementation_version = "Unknown" + + return { + "platform": platform_info, + "implementation": { + "name": implementation, + "version": implementation_version, + }, + "cryptography": {"version": cryptography_version}, + "pyjwt": {"version": pyjwt_version}, + } + + +def main() -> None: + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/jwk_set_cache.py b/.pythonlibs/lib/python3.10/site-packages/jwt/jwk_set_cache.py new file mode 100644 index 0000000..2432563 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/jwk_set_cache.py @@ -0,0 +1,31 @@ +import time +from typing import Optional + +from .api_jwk import PyJWKSet, PyJWTSetWithTimestamp + + +class JWKSetCache: + def __init__(self, lifespan: int) -> None: + self.jwk_set_with_timestamp: Optional[PyJWTSetWithTimestamp] = None + self.lifespan = lifespan + + def put(self, jwk_set: PyJWKSet) -> None: + if jwk_set is not None: + self.jwk_set_with_timestamp = PyJWTSetWithTimestamp(jwk_set) + else: + # clear cache + self.jwk_set_with_timestamp = None + + def get(self) -> Optional[PyJWKSet]: + if self.jwk_set_with_timestamp is None or self.is_expired(): + return None + + return self.jwk_set_with_timestamp.get_jwk_set() + + def is_expired(self) -> bool: + return ( + self.jwk_set_with_timestamp is not None + and self.lifespan > -1 + and time.monotonic() + > self.jwk_set_with_timestamp.get_timestamp() + self.lifespan + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/jwks_client.py b/.pythonlibs/lib/python3.10/site-packages/jwt/jwks_client.py new file mode 100644 index 0000000..f19b10a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/jwks_client.py @@ -0,0 +1,124 @@ +import json +import urllib.request +from functools import lru_cache +from ssl import SSLContext +from typing import Any, Dict, List, Optional +from urllib.error import URLError + +from .api_jwk import PyJWK, PyJWKSet +from .api_jwt import decode_complete as decode_token +from .exceptions import PyJWKClientConnectionError, PyJWKClientError +from .jwk_set_cache import JWKSetCache + + +class PyJWKClient: + def __init__( + self, + uri: str, + cache_keys: bool = False, + max_cached_keys: int = 16, + cache_jwk_set: bool = True, + lifespan: int = 300, + headers: Optional[Dict[str, Any]] = None, + timeout: int = 30, + ssl_context: Optional[SSLContext] = None, + ): + if headers is None: + headers = {} + self.uri = uri + self.jwk_set_cache: Optional[JWKSetCache] = None + self.headers = headers + self.timeout = timeout + self.ssl_context = ssl_context + + if cache_jwk_set: + # Init jwt set cache with default or given lifespan. + # Default lifespan is 300 seconds (5 minutes). + if lifespan <= 0: + raise PyJWKClientError( + f'Lifespan must be greater than 0, the input is "{lifespan}"' + ) + self.jwk_set_cache = JWKSetCache(lifespan) + else: + self.jwk_set_cache = None + + if cache_keys: + # Cache signing keys + # Ignore mypy (https://github.com/python/mypy/issues/2427) + self.get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key) # type: ignore + + def fetch_data(self) -> Any: + jwk_set: Any = None + try: + r = urllib.request.Request(url=self.uri, headers=self.headers) + with urllib.request.urlopen( + r, timeout=self.timeout, context=self.ssl_context + ) as response: + jwk_set = json.load(response) + except (URLError, TimeoutError) as e: + raise PyJWKClientConnectionError( + f'Fail to fetch data from the url, err: "{e}"' + ) + else: + return jwk_set + finally: + if self.jwk_set_cache is not None: + self.jwk_set_cache.put(jwk_set) + + def get_jwk_set(self, refresh: bool = False) -> PyJWKSet: + data = None + if self.jwk_set_cache is not None and not refresh: + data = self.jwk_set_cache.get() + + if data is None: + data = self.fetch_data() + + if not isinstance(data, dict): + raise PyJWKClientError("The JWKS endpoint did not return a JSON object") + + return PyJWKSet.from_dict(data) + + def get_signing_keys(self, refresh: bool = False) -> List[PyJWK]: + jwk_set = self.get_jwk_set(refresh) + signing_keys = [ + jwk_set_key + for jwk_set_key in jwk_set.keys + if jwk_set_key.public_key_use in ["sig", None] and jwk_set_key.key_id + ] + + if not signing_keys: + raise PyJWKClientError("The JWKS endpoint did not contain any signing keys") + + return signing_keys + + def get_signing_key(self, kid: str) -> PyJWK: + signing_keys = self.get_signing_keys() + signing_key = self.match_kid(signing_keys, kid) + + if not signing_key: + # If no matching signing key from the jwk set, refresh the jwk set and try again. + signing_keys = self.get_signing_keys(refresh=True) + signing_key = self.match_kid(signing_keys, kid) + + if not signing_key: + raise PyJWKClientError( + f'Unable to find a signing key that matches: "{kid}"' + ) + + return signing_key + + def get_signing_key_from_jwt(self, token: str) -> PyJWK: + unverified = decode_token(token, options={"verify_signature": False}) + header = unverified["header"] + return self.get_signing_key(header.get("kid")) + + @staticmethod + def match_kid(signing_keys: List[PyJWK], kid: str) -> Optional[PyJWK]: + signing_key = None + + for key in signing_keys: + if key.key_id == kid: + signing_key = key + break + + return signing_key diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/py.typed b/.pythonlibs/lib/python3.10/site-packages/jwt/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/types.py b/.pythonlibs/lib/python3.10/site-packages/jwt/types.py new file mode 100644 index 0000000..7d99352 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/types.py @@ -0,0 +1,5 @@ +from typing import Any, Callable, Dict + +JWKDict = Dict[str, Any] + +HashlibHash = Callable[..., Any] diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/utils.py b/.pythonlibs/lib/python3.10/site-packages/jwt/utils.py new file mode 100644 index 0000000..81c5ee4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/utils.py @@ -0,0 +1,156 @@ +import base64 +import binascii +import re +from typing import Union + +try: + from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve + from cryptography.hazmat.primitives.asymmetric.utils import ( + decode_dss_signature, + encode_dss_signature, + ) +except ModuleNotFoundError: + pass + + +def force_bytes(value: Union[bytes, str]) -> bytes: + if isinstance(value, str): + return value.encode("utf-8") + elif isinstance(value, bytes): + return value + else: + raise TypeError("Expected a string value") + + +def base64url_decode(input: Union[bytes, str]) -> bytes: + input_bytes = force_bytes(input) + + rem = len(input_bytes) % 4 + + if rem > 0: + input_bytes += b"=" * (4 - rem) + + return base64.urlsafe_b64decode(input_bytes) + + +def base64url_encode(input: bytes) -> bytes: + return base64.urlsafe_b64encode(input).replace(b"=", b"") + + +def to_base64url_uint(val: int) -> bytes: + if val < 0: + raise ValueError("Must be a positive integer") + + int_bytes = bytes_from_int(val) + + if len(int_bytes) == 0: + int_bytes = b"\x00" + + return base64url_encode(int_bytes) + + +def from_base64url_uint(val: Union[bytes, str]) -> int: + data = base64url_decode(force_bytes(val)) + return int.from_bytes(data, byteorder="big") + + +def number_to_bytes(num: int, num_bytes: int) -> bytes: + padded_hex = "%0*x" % (2 * num_bytes, num) + return binascii.a2b_hex(padded_hex.encode("ascii")) + + +def bytes_to_number(string: bytes) -> int: + return int(binascii.b2a_hex(string), 16) + + +def bytes_from_int(val: int) -> bytes: + remaining = val + byte_length = 0 + + while remaining != 0: + remaining >>= 8 + byte_length += 1 + + return val.to_bytes(byte_length, "big", signed=False) + + +def der_to_raw_signature(der_sig: bytes, curve: "EllipticCurve") -> bytes: + num_bits = curve.key_size + num_bytes = (num_bits + 7) // 8 + + r, s = decode_dss_signature(der_sig) + + return number_to_bytes(r, num_bytes) + number_to_bytes(s, num_bytes) + + +def raw_to_der_signature(raw_sig: bytes, curve: "EllipticCurve") -> bytes: + num_bits = curve.key_size + num_bytes = (num_bits + 7) // 8 + + if len(raw_sig) != 2 * num_bytes: + raise ValueError("Invalid signature") + + r = bytes_to_number(raw_sig[:num_bytes]) + s = bytes_to_number(raw_sig[num_bytes:]) + + return bytes(encode_dss_signature(r, s)) + + +# Based on https://github.com/hynek/pem/blob/7ad94db26b0bc21d10953f5dbad3acfdfacf57aa/src/pem/_core.py#L224-L252 +_PEMS = { + b"CERTIFICATE", + b"TRUSTED CERTIFICATE", + b"PRIVATE KEY", + b"PUBLIC KEY", + b"ENCRYPTED PRIVATE KEY", + b"OPENSSH PRIVATE KEY", + b"DSA PRIVATE KEY", + b"RSA PRIVATE KEY", + b"RSA PUBLIC KEY", + b"EC PRIVATE KEY", + b"DH PARAMETERS", + b"NEW CERTIFICATE REQUEST", + b"CERTIFICATE REQUEST", + b"SSH2 PUBLIC KEY", + b"SSH2 ENCRYPTED PRIVATE KEY", + b"X509 CRL", +} + +_PEM_RE = re.compile( + b"----[- ]BEGIN (" + + b"|".join(_PEMS) + + b""")[- ]----\r? +.+?\r? +----[- ]END \\1[- ]----\r?\n?""", + re.DOTALL, +) + + +def is_pem_format(key: bytes) -> bool: + return bool(_PEM_RE.search(key)) + + +# Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46 +_CERT_SUFFIX = b"-cert-v01@openssh.com" +_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") +_SSH_KEY_FORMATS = [ + b"ssh-ed25519", + b"ssh-rsa", + b"ssh-dss", + b"ecdsa-sha2-nistp256", + b"ecdsa-sha2-nistp384", + b"ecdsa-sha2-nistp521", +] + + +def is_ssh_key(key: bytes) -> bool: + if any(string_value in key for string_value in _SSH_KEY_FORMATS): + return True + + ssh_pubkey_match = _SSH_PUBKEY_RC.match(key) + if ssh_pubkey_match: + key_type = ssh_pubkey_match.group(1) + if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]: + return True + + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/jwt/warnings.py b/.pythonlibs/lib/python3.10/site-packages/jwt/warnings.py new file mode 100644 index 0000000..8762a8c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/jwt/warnings.py @@ -0,0 +1,2 @@ +class RemovedInPyjwt3Warning(DeprecationWarning): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/LICENSE new file mode 100644 index 0000000..8727172 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/LICENSE @@ -0,0 +1,13 @@ + Copyright 2016 Andrew Svetlov and aio-libs contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/METADATA new file mode 100644 index 0000000..9d9b4a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/METADATA @@ -0,0 +1,132 @@ +Metadata-Version: 2.1 +Name: multidict +Version: 6.0.5 +Summary: multidict implementation +Home-page: https://github.com/aio-libs/multidict +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +License: Apache 2 +Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby +Project-URL: CI: GitHub, https://github.com/aio-libs/multidict/actions +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/multidict +Project-URL: Docs: RTD, https://multidict.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/multidict/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/multidict +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE + +========= +multidict +========= + +.. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg + :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI + :alt: GitHub status for master branch + +.. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/multidict + :alt: Coverage metrics + +.. image:: https://img.shields.io/pypi/v/multidict.svg + :target: https://pypi.org/project/multidict + :alt: PyPI + +.. image:: https://readthedocs.org/projects/multidict/badge/?version=latest + :target: http://multidict.aio-libs.org/en/latest/?badge=latest + :alt: Documentation + +.. image:: https://img.shields.io/pypi/pyversions/multidict.svg + :target: https://pypi.org/project/multidict + :alt: Python versions + +.. image:: https://badges.gitter.im/Join%20Chat.svg + :target: https://gitter.im/aio-libs/Lobby + :alt: Chat on Gitter + +Multidict is dict-like collection of *key-value pairs* where key +might occur more than once in the container. + +Introduction +------------ + +*HTTP Headers* and *URL query string* require specific data structure: +*multidict*. It behaves mostly like a regular ``dict`` but it may have +several *values* for the same *key* and *preserves insertion ordering*. + +The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries). + +``multidict`` has four multidict classes: +``MultiDict``, ``MultiDictProxy``, ``CIMultiDict`` +and ``CIMultiDictProxy``. + +Immutable proxies (``MultiDictProxy`` and +``CIMultiDictProxy``) provide a dynamic view for the +proxied multidict, the view reflects underlying collection changes. They +implement the ``collections.abc.Mapping`` interface. + +Regular mutable (``MultiDict`` and ``CIMultiDict``) classes +implement ``collections.abc.MutableMapping`` and allows them to change +their own content. + + +*Case insensitive* (``CIMultiDict`` and +``CIMultiDictProxy``) assume the *keys* are case +insensitive, e.g.:: + + >>> dct = CIMultiDict(key='val') + >>> 'Key' in dct + True + >>> dct['Key'] + 'val' + +*Keys* should be ``str`` or ``istr`` instances. + +The library has optional C Extensions for speed. + + +License +------- + +Apache 2 + +Library Installation +-------------------- + +.. code-block:: bash + + $ pip install multidict + +The library is Python 3 only! + +PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install +``multidict`` on another operating system (or *Alpine Linux* inside a Docker) the +tarball will be used to compile the library from source. It requires a C compiler and +Python headers to be installed. + +To skip the compilation, please use the `MULTIDICT_NO_EXTENSIONS` environment variable, +e.g.: + +.. code-block:: bash + + $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict + +Please note, the pure Python (uncompiled) version is about 20-50 times slower depending on +the usage scenario!!! + + + +Changelog +--------- +See `RTD page `_. diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/RECORD new file mode 100644 index 0000000..3e1553f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/RECORD @@ -0,0 +1,19 @@ +multidict-6.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +multidict-6.0.5.dist-info/LICENSE,sha256=k9Ealo4vDzY3PECBH_bSDhc_WMPKtYhM1mF7v9eVSSo,611 +multidict-6.0.5.dist-info/METADATA,sha256=fGbYCQYEMcDtxEz2H6GLf1np9JtMhNTaLVzgAhsQYzU,4214 +multidict-6.0.5.dist-info/RECORD,, +multidict-6.0.5.dist-info/WHEEL,sha256=1FEjxEYgybphwh9S0FO9IcZ0B-NIeM2ko8OzhFZeOeQ,152 +multidict-6.0.5.dist-info/top_level.txt,sha256=-euDElkk5_qkmfIJ7WiqCab02ZlSFZWynejKg59qZQQ,10 +multidict/__init__.py,sha256=psbRrP64CD22Wjoc_OoqG9QlkRGcaZfOFCoPmoUiMig,928 +multidict/__init__.pyi,sha256=SbgC2ew1NvNXWlRKs9o0KhW4moozgMqgQ0OA4Re5JQQ,4840 +multidict/__pycache__/__init__.cpython-310.pyc,, +multidict/__pycache__/_abc.cpython-310.pyc,, +multidict/__pycache__/_compat.cpython-310.pyc,, +multidict/__pycache__/_multidict_base.cpython-310.pyc,, +multidict/__pycache__/_multidict_py.cpython-310.pyc,, +multidict/_abc.py,sha256=Zvnrn4SBkrv4QTD7-ZzqNcoxw0f8KStLMPzGvBuGT2w,1190 +multidict/_compat.py,sha256=tjUGdP9ooiH6c2KJrvUbPRwcvjWerKlKU6InIviwh7w,316 +multidict/_multidict.cpython-310-x86_64-linux-gnu.so,sha256=BmNKiShRM0HlwDp_fjuenfzDjBlo6C0BEfpi55TRp-k,394656 +multidict/_multidict_base.py,sha256=XugkE78fXBmtzDdg2Yi9TrEhDexmL-6qJbFIG0viLMg,3791 +multidict/_multidict_py.py,sha256=57h4sYrRIu7EjMX4YpHVIZVrV9-q1KCW3F6rao10D3U,15050 +multidict/py.typed,sha256=e9bmbH3UFxsabQrnNFPG9qxIXztwbcM6IKDYnvZwprY,15 diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/WHEEL new file mode 100644 index 0000000..1d81251 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/top_level.txt new file mode 100644 index 0000000..afcecdf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict-6.0.5.dist-info/top_level.txt @@ -0,0 +1 @@ +multidict diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.py b/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.py new file mode 100644 index 0000000..23142ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.py @@ -0,0 +1,48 @@ +"""Multidict implementation. + +HTTP Headers and URL query string require specific data structure: +multidict. It behaves mostly like a dict but it can have +several values for the same key. +""" + +from ._abc import MultiMapping, MutableMultiMapping +from ._compat import USE_EXTENSIONS + +__all__ = ( + "MultiMapping", + "MutableMultiMapping", + "MultiDictProxy", + "CIMultiDictProxy", + "MultiDict", + "CIMultiDict", + "upstr", + "istr", + "getversion", +) + +__version__ = "6.0.5" + + +try: + if not USE_EXTENSIONS: + raise ImportError + from ._multidict import ( + CIMultiDict, + CIMultiDictProxy, + MultiDict, + MultiDictProxy, + getversion, + istr, + ) +except ImportError: # pragma: no cover + from ._multidict_py import ( + CIMultiDict, + CIMultiDictProxy, + MultiDict, + MultiDictProxy, + getversion, + istr, + ) + + +upstr = istr diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.pyi new file mode 100644 index 0000000..0940340 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/__init__.pyi @@ -0,0 +1,152 @@ +import abc +from typing import ( + Generic, + Iterable, + Iterator, + Mapping, + MutableMapping, + TypeVar, + overload, +) + +class istr(str): ... + +upstr = istr + +_S = str | istr + +_T = TypeVar("_T") + +_T_co = TypeVar("_T_co", covariant=True) + +_D = TypeVar("_D") + +class MultiMapping(Mapping[_S, _T_co]): + @overload + @abc.abstractmethod + def getall(self, key: _S) -> list[_T_co]: ... + @overload + @abc.abstractmethod + def getall(self, key: _S, default: _D) -> list[_T_co] | _D: ... + @overload + @abc.abstractmethod + def getone(self, key: _S) -> _T_co: ... + @overload + @abc.abstractmethod + def getone(self, key: _S, default: _D) -> _T_co | _D: ... + +_Arg = ( + Mapping[str, _T] + | Mapping[istr, _T] + | dict[str, _T] + | dict[istr, _T] + | MultiMapping[_T] + | Iterable[tuple[str, _T]] + | Iterable[tuple[istr, _T]] +) + +class MutableMultiMapping(MultiMapping[_T], MutableMapping[_S, _T], Generic[_T]): + @abc.abstractmethod + def add(self, key: _S, value: _T) -> None: ... + @abc.abstractmethod + def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ... + @overload + @abc.abstractmethod + def popone(self, key: _S) -> _T: ... + @overload + @abc.abstractmethod + def popone(self, key: _S, default: _D) -> _T | _D: ... + @overload + @abc.abstractmethod + def popall(self, key: _S) -> list[_T]: ... + @overload + @abc.abstractmethod + def popall(self, key: _S, default: _D) -> list[_T] | _D: ... + +class MultiDict(MutableMultiMapping[_T], Generic[_T]): + def __init__(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ... + def copy(self) -> MultiDict[_T]: ... + def __getitem__(self, k: _S) -> _T: ... + def __setitem__(self, k: _S, v: _T) -> None: ... + def __delitem__(self, v: _S) -> None: ... + def __iter__(self) -> Iterator[_S]: ... + def __len__(self) -> int: ... + @overload + def getall(self, key: _S) -> list[_T]: ... + @overload + def getall(self, key: _S, default: _D) -> list[_T] | _D: ... + @overload + def getone(self, key: _S) -> _T: ... + @overload + def getone(self, key: _S, default: _D) -> _T | _D: ... + def add(self, key: _S, value: _T) -> None: ... + def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ... + @overload + def popone(self, key: _S) -> _T: ... + @overload + def popone(self, key: _S, default: _D) -> _T | _D: ... + @overload + def popall(self, key: _S) -> list[_T]: ... + @overload + def popall(self, key: _S, default: _D) -> list[_T] | _D: ... + +class CIMultiDict(MutableMultiMapping[_T], Generic[_T]): + def __init__(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ... + def copy(self) -> CIMultiDict[_T]: ... + def __getitem__(self, k: _S) -> _T: ... + def __setitem__(self, k: _S, v: _T) -> None: ... + def __delitem__(self, v: _S) -> None: ... + def __iter__(self) -> Iterator[_S]: ... + def __len__(self) -> int: ... + @overload + def getall(self, key: _S) -> list[_T]: ... + @overload + def getall(self, key: _S, default: _D) -> list[_T] | _D: ... + @overload + def getone(self, key: _S) -> _T: ... + @overload + def getone(self, key: _S, default: _D) -> _T | _D: ... + def add(self, key: _S, value: _T) -> None: ... + def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ... + @overload + def popone(self, key: _S) -> _T: ... + @overload + def popone(self, key: _S, default: _D) -> _T | _D: ... + @overload + def popall(self, key: _S) -> list[_T]: ... + @overload + def popall(self, key: _S, default: _D) -> list[_T] | _D: ... + +class MultiDictProxy(MultiMapping[_T], Generic[_T]): + def __init__(self, arg: MultiMapping[_T] | MutableMultiMapping[_T]) -> None: ... + def copy(self) -> MultiDict[_T]: ... + def __getitem__(self, k: _S) -> _T: ... + def __iter__(self) -> Iterator[_S]: ... + def __len__(self) -> int: ... + @overload + def getall(self, key: _S) -> list[_T]: ... + @overload + def getall(self, key: _S, default: _D) -> list[_T] | _D: ... + @overload + def getone(self, key: _S) -> _T: ... + @overload + def getone(self, key: _S, default: _D) -> _T | _D: ... + +class CIMultiDictProxy(MultiMapping[_T], Generic[_T]): + def __init__(self, arg: MultiMapping[_T] | MutableMultiMapping[_T]) -> None: ... + def __getitem__(self, k: _S) -> _T: ... + def __iter__(self) -> Iterator[_S]: ... + def __len__(self) -> int: ... + @overload + def getall(self, key: _S) -> list[_T]: ... + @overload + def getall(self, key: _S, default: _D) -> list[_T] | _D: ... + @overload + def getone(self, key: _S) -> _T: ... + @overload + def getone(self, key: _S, default: _D) -> _T | _D: ... + def copy(self) -> CIMultiDict[_T]: ... + +def getversion( + md: MultiDict[_T] | CIMultiDict[_T] | MultiDictProxy[_T] | CIMultiDictProxy[_T], +) -> int: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e9a594d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_abc.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_abc.cpython-310.pyc new file mode 100644 index 0000000..0b075af Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_abc.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000..92f3f7e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_base.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_base.cpython-310.pyc new file mode 100644 index 0000000..5fbf07a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_base.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_py.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_py.cpython-310.pyc new file mode 100644 index 0000000..667d1ea Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/__pycache__/_multidict_py.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/_abc.py b/.pythonlibs/lib/python3.10/site-packages/multidict/_abc.py new file mode 100644 index 0000000..0603cdd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/_abc.py @@ -0,0 +1,48 @@ +import abc +import sys +import types +from collections.abc import Mapping, MutableMapping + + +class _TypingMeta(abc.ABCMeta): + # A fake metaclass to satisfy typing deps in runtime + # basically MultiMapping[str] and other generic-like type instantiations + # are emulated. + # Note: real type hints are provided by __init__.pyi stub file + if sys.version_info >= (3, 9): + + def __getitem__(self, key): + return types.GenericAlias(self, key) + + else: + + def __getitem__(self, key): + return self + + +class MultiMapping(Mapping, metaclass=_TypingMeta): + @abc.abstractmethod + def getall(self, key, default=None): + raise KeyError + + @abc.abstractmethod + def getone(self, key, default=None): + raise KeyError + + +class MutableMultiMapping(MultiMapping, MutableMapping): + @abc.abstractmethod + def add(self, key, value): + raise NotImplementedError + + @abc.abstractmethod + def extend(self, *args, **kwargs): + raise NotImplementedError + + @abc.abstractmethod + def popone(self, key, default=None): + raise KeyError + + @abc.abstractmethod + def popall(self, key, default=None): + raise KeyError diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/_compat.py b/.pythonlibs/lib/python3.10/site-packages/multidict/_compat.py new file mode 100644 index 0000000..d1ff392 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/_compat.py @@ -0,0 +1,14 @@ +import os +import platform + +NO_EXTENSIONS = bool(os.environ.get("MULTIDICT_NO_EXTENSIONS")) + +PYPY = platform.python_implementation() == "PyPy" + +USE_EXTENSIONS = not NO_EXTENSIONS and not PYPY + +if USE_EXTENSIONS: + try: + from . import _multidict # noqa + except ImportError: + USE_EXTENSIONS = False diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..040bb20 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_base.py b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_base.py new file mode 100644 index 0000000..3944665 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_base.py @@ -0,0 +1,144 @@ +from collections.abc import ItemsView, Iterable, KeysView, Set, ValuesView + + +def _abc_itemsview_register(view_cls): + ItemsView.register(view_cls) + + +def _abc_keysview_register(view_cls): + KeysView.register(view_cls) + + +def _abc_valuesview_register(view_cls): + ValuesView.register(view_cls) + + +def _viewbaseset_richcmp(view, other, op): + if op == 0: # < + if not isinstance(other, Set): + return NotImplemented + return len(view) < len(other) and view <= other + elif op == 1: # <= + if not isinstance(other, Set): + return NotImplemented + if len(view) > len(other): + return False + for elem in view: + if elem not in other: + return False + return True + elif op == 2: # == + if not isinstance(other, Set): + return NotImplemented + return len(view) == len(other) and view <= other + elif op == 3: # != + return not view == other + elif op == 4: # > + if not isinstance(other, Set): + return NotImplemented + return len(view) > len(other) and view >= other + elif op == 5: # >= + if not isinstance(other, Set): + return NotImplemented + if len(view) < len(other): + return False + for elem in other: + if elem not in view: + return False + return True + + +def _viewbaseset_and(view, other): + if not isinstance(other, Iterable): + return NotImplemented + if isinstance(view, Set): + view = set(iter(view)) + if isinstance(other, Set): + other = set(iter(other)) + if not isinstance(other, Set): + other = set(iter(other)) + return view & other + + +def _viewbaseset_or(view, other): + if not isinstance(other, Iterable): + return NotImplemented + if isinstance(view, Set): + view = set(iter(view)) + if isinstance(other, Set): + other = set(iter(other)) + if not isinstance(other, Set): + other = set(iter(other)) + return view | other + + +def _viewbaseset_sub(view, other): + if not isinstance(other, Iterable): + return NotImplemented + if isinstance(view, Set): + view = set(iter(view)) + if isinstance(other, Set): + other = set(iter(other)) + if not isinstance(other, Set): + other = set(iter(other)) + return view - other + + +def _viewbaseset_xor(view, other): + if not isinstance(other, Iterable): + return NotImplemented + if isinstance(view, Set): + view = set(iter(view)) + if isinstance(other, Set): + other = set(iter(other)) + if not isinstance(other, Set): + other = set(iter(other)) + return view ^ other + + +def _itemsview_isdisjoint(view, other): + "Return True if two sets have a null intersection." + for v in other: + if v in view: + return False + return True + + +def _itemsview_repr(view): + lst = [] + for k, v in view: + lst.append("{!r}: {!r}".format(k, v)) + body = ", ".join(lst) + return "{}({})".format(view.__class__.__name__, body) + + +def _keysview_isdisjoint(view, other): + "Return True if two sets have a null intersection." + for k in other: + if k in view: + return False + return True + + +def _keysview_repr(view): + lst = [] + for k in view: + lst.append("{!r}".format(k)) + body = ", ".join(lst) + return "{}({})".format(view.__class__.__name__, body) + + +def _valuesview_repr(view): + lst = [] + for v in view: + lst.append("{!r}".format(v)) + body = ", ".join(lst) + return "{}({})".format(view.__class__.__name__, body) + + +def _mdrepr(md): + lst = [] + for k, v in md.items(): + lst.append("'{}': {!r}".format(k, v)) + body = ", ".join(lst) + return "<{}({})>".format(md.__class__.__name__, body) diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_py.py b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_py.py new file mode 100644 index 0000000..79c45aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/_multidict_py.py @@ -0,0 +1,527 @@ +import sys +import types +from array import array +from collections import abc + +from ._abc import MultiMapping, MutableMultiMapping + +_marker = object() + +if sys.version_info >= (3, 9): + GenericAlias = types.GenericAlias +else: + + def GenericAlias(cls): + return cls + + +class istr(str): + + """Case insensitive str.""" + + __is_istr__ = True + + +upstr = istr # for relaxing backward compatibility problems + + +def getversion(md): + if not isinstance(md, _Base): + raise TypeError("Parameter should be multidict or proxy") + return md._impl._version + + +_version = array("Q", [0]) + + +class _Impl: + __slots__ = ("_items", "_version") + + def __init__(self): + self._items = [] + self.incr_version() + + def incr_version(self): + global _version + v = _version + v[0] += 1 + self._version = v[0] + + if sys.implementation.name != "pypy": + + def __sizeof__(self): + return object.__sizeof__(self) + sys.getsizeof(self._items) + + +class _Base: + def _title(self, key): + return key + + def getall(self, key, default=_marker): + """Return a list of all values matching the key.""" + identity = self._title(key) + res = [v for i, k, v in self._impl._items if i == identity] + if res: + return res + if not res and default is not _marker: + return default + raise KeyError("Key not found: %r" % key) + + def getone(self, key, default=_marker): + """Get first value matching the key. + + Raises KeyError if the key is not found and no default is provided. + """ + identity = self._title(key) + for i, k, v in self._impl._items: + if i == identity: + return v + if default is not _marker: + return default + raise KeyError("Key not found: %r" % key) + + # Mapping interface # + + def __getitem__(self, key): + return self.getone(key) + + def get(self, key, default=None): + """Get first value matching the key. + + If the key is not found, returns the default (or None if no default is provided) + """ + return self.getone(key, default) + + def __iter__(self): + return iter(self.keys()) + + def __len__(self): + return len(self._impl._items) + + def keys(self): + """Return a new view of the dictionary's keys.""" + return _KeysView(self._impl) + + def items(self): + """Return a new view of the dictionary's items *(key, value) pairs).""" + return _ItemsView(self._impl) + + def values(self): + """Return a new view of the dictionary's values.""" + return _ValuesView(self._impl) + + def __eq__(self, other): + if not isinstance(other, abc.Mapping): + return NotImplemented + if isinstance(other, _Base): + lft = self._impl._items + rht = other._impl._items + if len(lft) != len(rht): + return False + for (i1, k2, v1), (i2, k2, v2) in zip(lft, rht): + if i1 != i2 or v1 != v2: + return False + return True + if len(self._impl._items) != len(other): + return False + for k, v in self.items(): + nv = other.get(k, _marker) + if v != nv: + return False + return True + + def __contains__(self, key): + identity = self._title(key) + for i, k, v in self._impl._items: + if i == identity: + return True + return False + + def __repr__(self): + body = ", ".join("'{}': {!r}".format(k, v) for k, v in self.items()) + return "<{}({})>".format(self.__class__.__name__, body) + + __class_getitem__ = classmethod(GenericAlias) + + +class MultiDictProxy(_Base, MultiMapping): + """Read-only proxy for MultiDict instance.""" + + def __init__(self, arg): + if not isinstance(arg, (MultiDict, MultiDictProxy)): + raise TypeError( + "ctor requires MultiDict or MultiDictProxy instance" + ", not {}".format(type(arg)) + ) + + self._impl = arg._impl + + def __reduce__(self): + raise TypeError("can't pickle {} objects".format(self.__class__.__name__)) + + def copy(self): + """Return a copy of itself.""" + return MultiDict(self.items()) + + +class CIMultiDictProxy(MultiDictProxy): + """Read-only proxy for CIMultiDict instance.""" + + def __init__(self, arg): + if not isinstance(arg, (CIMultiDict, CIMultiDictProxy)): + raise TypeError( + "ctor requires CIMultiDict or CIMultiDictProxy instance" + ", not {}".format(type(arg)) + ) + + self._impl = arg._impl + + def _title(self, key): + return key.title() + + def copy(self): + """Return a copy of itself.""" + return CIMultiDict(self.items()) + + +class MultiDict(_Base, MutableMultiMapping): + """Dictionary with the support for duplicate keys.""" + + def __init__(self, *args, **kwargs): + self._impl = _Impl() + + self._extend(args, kwargs, self.__class__.__name__, self._extend_items) + + if sys.implementation.name != "pypy": + + def __sizeof__(self): + return object.__sizeof__(self) + sys.getsizeof(self._impl) + + def __reduce__(self): + return (self.__class__, (list(self.items()),)) + + def _title(self, key): + return key + + def _key(self, key): + if isinstance(key, str): + return key + else: + raise TypeError( + "MultiDict keys should be either str " "or subclasses of str" + ) + + def add(self, key, value): + identity = self._title(key) + self._impl._items.append((identity, self._key(key), value)) + self._impl.incr_version() + + def copy(self): + """Return a copy of itself.""" + cls = self.__class__ + return cls(self.items()) + + __copy__ = copy + + def extend(self, *args, **kwargs): + """Extend current MultiDict with more values. + + This method must be used instead of update. + """ + self._extend(args, kwargs, "extend", self._extend_items) + + def _extend(self, args, kwargs, name, method): + if len(args) > 1: + raise TypeError( + "{} takes at most 1 positional argument" + " ({} given)".format(name, len(args)) + ) + if args: + arg = args[0] + if isinstance(args[0], (MultiDict, MultiDictProxy)) and not kwargs: + items = arg._impl._items + else: + if hasattr(arg, "items"): + arg = arg.items() + if kwargs: + arg = list(arg) + arg.extend(list(kwargs.items())) + items = [] + for item in arg: + if not len(item) == 2: + raise TypeError( + "{} takes either dict or list of (key, value) " + "tuples".format(name) + ) + items.append((self._title(item[0]), self._key(item[0]), item[1])) + + method(items) + else: + method( + [ + (self._title(key), self._key(key), value) + for key, value in kwargs.items() + ] + ) + + def _extend_items(self, items): + for identity, key, value in items: + self.add(key, value) + + def clear(self): + """Remove all items from MultiDict.""" + self._impl._items.clear() + self._impl.incr_version() + + # Mapping interface # + + def __setitem__(self, key, value): + self._replace(key, value) + + def __delitem__(self, key): + identity = self._title(key) + items = self._impl._items + found = False + for i in range(len(items) - 1, -1, -1): + if items[i][0] == identity: + del items[i] + found = True + if not found: + raise KeyError(key) + else: + self._impl.incr_version() + + def setdefault(self, key, default=None): + """Return value for key, set value to default if key is not present.""" + identity = self._title(key) + for i, k, v in self._impl._items: + if i == identity: + return v + self.add(key, default) + return default + + def popone(self, key, default=_marker): + """Remove specified key and return the corresponding value. + + If key is not found, d is returned if given, otherwise + KeyError is raised. + + """ + identity = self._title(key) + for i in range(len(self._impl._items)): + if self._impl._items[i][0] == identity: + value = self._impl._items[i][2] + del self._impl._items[i] + self._impl.incr_version() + return value + if default is _marker: + raise KeyError(key) + else: + return default + + pop = popone # type: ignore + + def popall(self, key, default=_marker): + """Remove all occurrences of key and return the list of corresponding + values. + + If key is not found, default is returned if given, otherwise + KeyError is raised. + + """ + found = False + identity = self._title(key) + ret = [] + for i in range(len(self._impl._items) - 1, -1, -1): + item = self._impl._items[i] + if item[0] == identity: + ret.append(item[2]) + del self._impl._items[i] + self._impl.incr_version() + found = True + if not found: + if default is _marker: + raise KeyError(key) + else: + return default + else: + ret.reverse() + return ret + + def popitem(self): + """Remove and return an arbitrary (key, value) pair.""" + if self._impl._items: + i = self._impl._items.pop(0) + self._impl.incr_version() + return i[1], i[2] + else: + raise KeyError("empty multidict") + + def update(self, *args, **kwargs): + """Update the dictionary from *other*, overwriting existing keys.""" + self._extend(args, kwargs, "update", self._update_items) + + def _update_items(self, items): + if not items: + return + used_keys = {} + for identity, key, value in items: + start = used_keys.get(identity, 0) + for i in range(start, len(self._impl._items)): + item = self._impl._items[i] + if item[0] == identity: + used_keys[identity] = i + 1 + self._impl._items[i] = (identity, key, value) + break + else: + self._impl._items.append((identity, key, value)) + used_keys[identity] = len(self._impl._items) + + # drop tails + i = 0 + while i < len(self._impl._items): + item = self._impl._items[i] + identity = item[0] + pos = used_keys.get(identity) + if pos is None: + i += 1 + continue + if i >= pos: + del self._impl._items[i] + else: + i += 1 + + self._impl.incr_version() + + def _replace(self, key, value): + key = self._key(key) + identity = self._title(key) + items = self._impl._items + + for i in range(len(items)): + item = items[i] + if item[0] == identity: + items[i] = (identity, key, value) + # i points to last found item + rgt = i + self._impl.incr_version() + break + else: + self._impl._items.append((identity, key, value)) + self._impl.incr_version() + return + + # remove all tail items + i = rgt + 1 + while i < len(items): + item = items[i] + if item[0] == identity: + del items[i] + else: + i += 1 + + +class CIMultiDict(MultiDict): + """Dictionary with the support for duplicate case-insensitive keys.""" + + def _title(self, key): + return key.title() + + +class _Iter: + __slots__ = ("_size", "_iter") + + def __init__(self, size, iterator): + self._size = size + self._iter = iterator + + def __iter__(self): + return self + + def __next__(self): + return next(self._iter) + + def __length_hint__(self): + return self._size + + +class _ViewBase: + def __init__(self, impl): + self._impl = impl + + def __len__(self): + return len(self._impl._items) + + +class _ItemsView(_ViewBase, abc.ItemsView): + def __contains__(self, item): + assert isinstance(item, tuple) or isinstance(item, list) + assert len(item) == 2 + for i, k, v in self._impl._items: + if item[0] == k and item[1] == v: + return True + return False + + def __iter__(self): + return _Iter(len(self), self._iter(self._impl._version)) + + def _iter(self, version): + for i, k, v in self._impl._items: + if version != self._impl._version: + raise RuntimeError("Dictionary changed during iteration") + yield k, v + + def __repr__(self): + lst = [] + for item in self._impl._items: + lst.append("{!r}: {!r}".format(item[1], item[2])) + body = ", ".join(lst) + return "{}({})".format(self.__class__.__name__, body) + + +class _ValuesView(_ViewBase, abc.ValuesView): + def __contains__(self, value): + for item in self._impl._items: + if item[2] == value: + return True + return False + + def __iter__(self): + return _Iter(len(self), self._iter(self._impl._version)) + + def _iter(self, version): + for item in self._impl._items: + if version != self._impl._version: + raise RuntimeError("Dictionary changed during iteration") + yield item[2] + + def __repr__(self): + lst = [] + for item in self._impl._items: + lst.append("{!r}".format(item[2])) + body = ", ".join(lst) + return "{}({})".format(self.__class__.__name__, body) + + +class _KeysView(_ViewBase, abc.KeysView): + def __contains__(self, key): + for item in self._impl._items: + if item[1] == key: + return True + return False + + def __iter__(self): + return _Iter(len(self), self._iter(self._impl._version)) + + def _iter(self, version): + for item in self._impl._items: + if version != self._impl._version: + raise RuntimeError("Dictionary changed during iteration") + yield item[1] + + def __repr__(self): + lst = [] + for item in self._impl._items: + lst.append("{!r}".format(item[1])) + body = ", ".join(lst) + return "{}({})".format(self.__class__.__name__, body) diff --git a/.pythonlibs/lib/python3.10/site-packages/multidict/py.typed b/.pythonlibs/lib/python3.10/site-packages/multidict/py.typed new file mode 100644 index 0000000..dfe8cc0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/multidict/py.typed @@ -0,0 +1 @@ +PEP-561 marker. \ No newline at end of file diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/AUTHORS.md b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/AUTHORS.md new file mode 100644 index 0000000..d73953d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/AUTHORS.md @@ -0,0 +1,313 @@ +# Natural Language Toolkit (NLTK) Authors + +## Original Authors + +- Steven Bird +- Edward Loper +- Ewan Klein + +## Contributors + +- Tom Aarsen +- Rami Al-Rfou' +- Mark Amery +- Greg Aumann +- Ivan Barria +- Ingolf Becker +- Yonatan Becker +- Paul Bedaride +- Steven Bethard +- Robert Berwick +- Dan Blanchard +- Nathan Bodenstab +- Alexander Böhm +- Francis Bond +- Paul Bone +- Jordan Boyd-Graber +- Daniel Blanchard +- Phil Blunsom +- Lars Buitinck +- Cristian Capdevila +- Steve Cassidy +- Chen-Fu Chiang +- Dmitry Chichkov +- Jinyoung Choi +- Andrew Clausen +- Lucas Champollion +- Graham Christensen +- Trevor Cohn +- David Coles +- Tom Conroy +- Claude Coulombe +- Lucas Cooper +- Robin Cooper +- Chris Crowner +- James Curran +- Arthur Darcet +- Dariel Dato-on +- Selina Dennis +- Leon Derczynski +- Alexis Dimitriadis +- Nikhil Dinesh +- Liang Dong +- David Doukhan +- Rebecca Dridan +- Pablo Duboue +- Long Duong +- Christian Federmann +- Campion Fellin +- Michelle Fullwood +- Dan Garrette +- Maciej Gawinecki +- Jean Mark Gawron +- Sumukh Ghodke +- Yoav Goldberg +- Michael Wayne Goodman +- Dougal Graham +- Brent Gray +- Simon Greenhill +- Clark Grubb +- Eduardo Pereira Habkost +- Masato Hagiwara +- Lauri Hallila +- Michael Hansen +- Yurie Hara +- Will Hardy +- Tyler Hartley +- Peter Hawkins +- Saimadhav Heblikar +- Fredrik Hedman +- Helder +- Michael Heilman +- Ofer Helman +- Christopher Hench +- Bruce Hill +- Amy Holland +- Kristy Hollingshead +- Marcus Huderle +- Baden Hughes +- Nancy Ide +- Rebecca Ingram +- Edward Ivanovic +- Thomas Jakobsen +- Nick Johnson +- Eric Kafe +- Piotr Kasprzyk +- Angelos Katharopoulos +- Sudharshan Kaushik +- Chris Koenig +- Mikhail Korobov +- Denis Krusko +- Ilia Kurenkov +- Stefano Lattarini +- Pierre-François Laquerre +- Stefano Lattarini +- Haejoong Lee +- Jackson Lee +- Max Leonov +- Chris Liechti +- Hyuckin David Lim +- Tom Lippincott +- Peter Ljunglöf +- Alex Louden +- Joseph Lynch +- Nitin Madnani +- Felipe Madrigal +- Bjørn Mæland +- Dean Malmgren +- Christopher Maloof +- Rob Malouf +- Iker Manterola +- Carl de Marcken +- Mitch Marcus +- Torsten Marek +- Robert Marshall +- Marius Mather +- Duncan McGreggor +- David McClosky +- Xinfan Meng +- Dmitrijs Milajevs +- Margaret Mitchell +- Tomonori Nagano +- Jason Narad +- Shari A’aidil Nasruddin +- Lance Nathan +- Morten Neergaard +- David Nemeskey +- Eric Nichols +- Joel Nothman +- Alireza Nourian +- Alexander Oleynikov +- Pierpaolo Pantone +- Ted Pedersen +- Jacob Perkins +- Alberto Planas +- Ondrej Platek +- Alessandro Presta +- Qi Liu +- Martin Thorsen Ranang +- Michael Recachinas +- Brandon Rhodes +- Joshua Ritterman +- Will Roberts +- Stuart Robinson +- Carlos Rodriguez +- Lorenzo Rubio +- Alex Rudnick +- Jussi Salmela +- Geoffrey Sampson +- Kepa Sarasola +- Kevin Scannell +- Nathan Schneider +- Rico Sennrich +- Thomas Skardal +- Eric Smith +- Lynn Soe +- Rob Speer +- Peter Spiller +- Richard Sproat +- Ceri Stagg +- Peter Stahl +- Oliver Steele +- Thomas Stieglmaier +- Jan Strunk +- Liling Tan +- Claire Taylor +- Louis Tiao +- Steven Tomcavage +- Tiago Tresoldi +- Marcus Uneson +- Yu Usami +- Petro Verkhogliad +- Peter Wang +- Zhe Wang +- Charlotte Wilson +- Chuck Wooters +- Steven Xu +- Beracah Yankama +- Lei Ye (叶磊) +- Patrick Ye +- Geraldine Sim Wei Ying +- Jason Yoder +- Thomas Zieglier +- 0ssifrage +- ducki13 +- kiwipi +- lade +- isnowfy +- onesandzeros +- pquentin +- wvanlint +- Álvaro Justen +- bjut-hz +- Sergio Oller +- Will Monroe +- Elijah Rippeth +- Emil Manukyan +- Casper Lehmann-Strøm +- Andrew Giel +- Tanin Na Nakorn +- Linghao Zhang +- Colin Carroll +- Heguang Miao +- Hannah Aizenman (story645) +- George Berry +- Adam Nelson +- J Richard Snape +- Alex Constantin +- Tsolak Ghukasyan +- Prasasto Adi +- Safwan Kamarrudin +- Arthur Tilley +- Vilhjalmur Thorsteinsson +- Jaehoon Hwang +- Chintan Shah +- sbagan +- Zicheng Xu +- Albert Au Yeung +- Shenjian Zhao +- Deng Wang +- Ali Abdullah +- Stoytcho Stoytchev +- Lakhdar Benzahia +- Kheireddine Abainia +- Yibin Lin +- Artiem Krinitsyn +- Björn Mattsson +- Oleg Chislov +- Pavan Gururaj Joshi +- Ethan Hill +- Vivek Lakshmanan +- Somnath Rakshit +- Anlan Du +- Pulkit Maloo +- Brandon M. Burroughs +- John Stewart +- Iaroslav Tymchenko +- Aleš Tamchyna +- Tim Gianitsos +- Philippe Partarrieu +- Andrew Owen Martin +- Adrian Ellis +- Nat Quayle Nelson +- Yanpeng Zhao +- Matan Rak +- Nick Ulle +- Uday Krishna +- Osman Zubair +- Viresh Gupta +- Ondřej Cífka +- Iris X. Zhou +- Devashish Lal +- Gerhard Kremer +- Nicolas Darr +- Hervé Nicol +- Alexandre H. T. Dias +- Daksh Shah +- Jacob Weightman +- Bonifacio de Oliveira +- Armins Bagrats Stepanjans +- Vassilis Palassopoulos +- Ram Rachum +- Or Sharir +- Denali Molitor +- Jacob Moorman +- Cory Nezin +- Matt Chaput +- Danny Sepler +- Akshita Bhagia +- Pratap Yadav +- Hiroki Teranishi +- Ruben Cartuyvels +- Dalton Pearson +- Robby Horvath +- Gavish Poddar +- Saibo Geng +- Ahmet Yildirim +- Yuta Nakamura +- Adam Hawley +- Panagiotis Simakis +- Richard Wang +- Alexandre Perez-Lebel +- Fernando Carranza +- Martin Kondratzky +- Heungson Lee +- M.K. Pawelkiewicz +- Steven Thomas Smith +- Jan Lennartz + +## Others whose work we've taken and included in NLTK, but who didn't directly contribute it: + +### Contributors to the Porter Stemmer + +- Martin Porter +- Vivake Gupta +- Barry Wilkins +- Hiranmay Ghosh +- Chris Emerson + +### Authors of snowball arabic stemmer algorithm + +- Assem Chelli +- Abdelkrim Aries +- Lakhdar Benzahia diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/METADATA new file mode 100644 index 0000000..cad1d0d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/METADATA @@ -0,0 +1,67 @@ +Metadata-Version: 2.1 +Name: nltk +Version: 3.8.1 +Summary: Natural Language Toolkit +Home-page: https://www.nltk.org/ +Author: NLTK Team +Author-email: nltk.team@gmail.com +Maintainer: NLTK Team +Maintainer-email: nltk.team@gmail.com +License: Apache License, Version 2.0 +Project-URL: Documentation, https://www.nltk.org/ +Project-URL: Source Code, https://github.com/nltk/nltk +Project-URL: Issue Tracker, https://github.com/nltk/nltk/issues +Keywords: NLP,CL,natural language processing,computational linguistics,parsing,tagging,tokenizing,syntax,linguistics,language,natural language,text analytics +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces +Classifier: Topic :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Text Processing :: General +Classifier: Topic :: Text Processing :: Indexing +Classifier: Topic :: Text Processing :: Linguistic +Requires-Python: >=3.7 +Requires-Dist: click +Requires-Dist: joblib +Requires-Dist: regex (>=2021.8.3) +Requires-Dist: tqdm +Provides-Extra: all +Requires-Dist: scikit-learn ; extra == 'all' +Requires-Dist: python-crfsuite ; extra == 'all' +Requires-Dist: requests ; extra == 'all' +Requires-Dist: numpy ; extra == 'all' +Requires-Dist: pyparsing ; extra == 'all' +Requires-Dist: twython ; extra == 'all' +Requires-Dist: scipy ; extra == 'all' +Requires-Dist: matplotlib ; extra == 'all' +Provides-Extra: corenlp +Requires-Dist: requests ; extra == 'corenlp' +Provides-Extra: machine_learning +Requires-Dist: numpy ; extra == 'machine_learning' +Requires-Dist: python-crfsuite ; extra == 'machine_learning' +Requires-Dist: scikit-learn ; extra == 'machine_learning' +Requires-Dist: scipy ; extra == 'machine_learning' +Provides-Extra: plot +Requires-Dist: matplotlib ; extra == 'plot' +Provides-Extra: tgrep +Requires-Dist: pyparsing ; extra == 'tgrep' +Provides-Extra: twitter +Requires-Dist: twython ; extra == 'twitter' + +The Natural Language Toolkit (NLTK) is a Python package for +natural language processing. NLTK requires Python 3.7, 3.8, 3.9, 3.10 or 3.11. + diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/README.md b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/README.md new file mode 100644 index 0000000..821f454 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/README.md @@ -0,0 +1,50 @@ +# Natural Language Toolkit (NLTK) +[![PyPI](https://img.shields.io/pypi/v/nltk.svg)](https://pypi.python.org/pypi/nltk) +![CI](https://github.com/nltk/nltk/actions/workflows/ci.yaml/badge.svg?branch=develop) + +NLTK -- the Natural Language Toolkit -- is a suite of open source Python +modules, data sets, and tutorials supporting research and development in Natural +Language Processing. NLTK requires Python version 3.7, 3.8, 3.9, 3.10 or 3.11. + +For documentation, please visit [nltk.org](https://www.nltk.org/). + + +## Contributing + +Do you want to contribute to NLTK development? Great! +Please read [CONTRIBUTING.md](CONTRIBUTING.md) for more details. + +See also [how to contribute to NLTK](https://www.nltk.org/contribute.html). + + +## Donate + +Have you found the toolkit helpful? Please support NLTK development by donating +to the project via PayPal, using the link on the NLTK homepage. + + +## Citing + +If you publish work that uses NLTK, please cite the NLTK book, as follows: + + Bird, Steven, Edward Loper and Ewan Klein (2009). + Natural Language Processing with Python. O'Reilly Media Inc. + + +## Copyright + +Copyright (C) 2001-2023 NLTK Project + +For license information, see [LICENSE.txt](LICENSE.txt). + +[AUTHORS.md](AUTHORS.md) contains a list of everyone who has contributed to NLTK. + + +### Redistributing + +- NLTK source code is distributed under the Apache 2.0 License. +- NLTK documentation is distributed under the Creative Commons + Attribution-Noncommercial-No Derivative Works 3.0 United States license. +- NLTK corpora are provided under the terms given in the README file for each + corpus; all are redistributable and available for non-commercial use. +- NLTK may be freely redistributed, subject to the provisions of these licenses. diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/RECORD new file mode 100644 index 0000000..dd7d94a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/RECORD @@ -0,0 +1,783 @@ +../../../bin/nltk,sha256=-Kt8XYE7POPvOdFavRLk-rB8I1AyOjxjAqNIaPPIe20,209 +nltk-3.8.1.dist-info/AUTHORS.md,sha256=lwegiKq14iCouEfpgu85VSAWadP2X1MkLhUsgYBfPOI,7628 +nltk-3.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +nltk-3.8.1.dist-info/LICENSE.txt,sha256=Pd-b5cKP4n2tFDpdx27qJSIq0d1ok0oEcGTlbtL6QMU,11560 +nltk-3.8.1.dist-info/METADATA,sha256=CUHc77qyEPWGmH6DiO6622SIsRU2fGENF_LCKUzGEOI,2847 +nltk-3.8.1.dist-info/README.md,sha256=_oLlVxk8v-ARv0t4wAyrPKZ8KmLA2y1tlhJ4C3QjRk0,1789 +nltk-3.8.1.dist-info/RECORD,, +nltk-3.8.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nltk-3.8.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +nltk-3.8.1.dist-info/entry_points.txt,sha256=SK6SzMicwUtBiwUOmv5P1ZVs0h-xqey6PnRpsUGGx5c,37 +nltk-3.8.1.dist-info/top_level.txt,sha256=YoQ-mwqckmTv1Qktmlk5Ylb6lDG77jg5qwoEB7c-pXo,5 +nltk/VERSION,sha256=932VxvO5Mh-hWNyZVXsqpdneLGKgY0kKcA4_XVSSvyQ,7 +nltk/__init__.py,sha256=RSji7RAoc5vyHzOA6pB6LPpCUxB8o68igd6CrYElOGA,6432 +nltk/__pycache__/__init__.cpython-310.pyc,, +nltk/__pycache__/book.cpython-310.pyc,, +nltk/__pycache__/cli.cpython-310.pyc,, +nltk/__pycache__/collections.cpython-310.pyc,, +nltk/__pycache__/collocations.cpython-310.pyc,, +nltk/__pycache__/compat.cpython-310.pyc,, +nltk/__pycache__/data.cpython-310.pyc,, +nltk/__pycache__/decorators.cpython-310.pyc,, +nltk/__pycache__/downloader.cpython-310.pyc,, +nltk/__pycache__/featstruct.cpython-310.pyc,, +nltk/__pycache__/grammar.cpython-310.pyc,, +nltk/__pycache__/help.cpython-310.pyc,, +nltk/__pycache__/internals.cpython-310.pyc,, +nltk/__pycache__/jsontags.cpython-310.pyc,, +nltk/__pycache__/langnames.cpython-310.pyc,, +nltk/__pycache__/lazyimport.cpython-310.pyc,, +nltk/__pycache__/probability.cpython-310.pyc,, +nltk/__pycache__/text.cpython-310.pyc,, +nltk/__pycache__/tgrep.cpython-310.pyc,, +nltk/__pycache__/toolbox.cpython-310.pyc,, +nltk/__pycache__/treeprettyprinter.cpython-310.pyc,, +nltk/__pycache__/treetransforms.cpython-310.pyc,, +nltk/__pycache__/util.cpython-310.pyc,, +nltk/__pycache__/wsd.cpython-310.pyc,, +nltk/app/__init__.py,sha256=xGZbbDC3xv67XnHHusxZmCPMNqj07BM9W6ZSlkWn9eQ,1578 +nltk/app/__pycache__/__init__.cpython-310.pyc,, +nltk/app/__pycache__/chartparser_app.cpython-310.pyc,, +nltk/app/__pycache__/chunkparser_app.cpython-310.pyc,, +nltk/app/__pycache__/collocations_app.cpython-310.pyc,, +nltk/app/__pycache__/concordance_app.cpython-310.pyc,, +nltk/app/__pycache__/nemo_app.cpython-310.pyc,, +nltk/app/__pycache__/rdparser_app.cpython-310.pyc,, +nltk/app/__pycache__/srparser_app.cpython-310.pyc,, +nltk/app/__pycache__/wordfreq_app.cpython-310.pyc,, +nltk/app/__pycache__/wordnet_app.cpython-310.pyc,, +nltk/app/chartparser_app.py,sha256=8FX3-eJQmB-8LT9k-lQJe_y5dSO3Ly4AD2K_wIs9FuE,88195 +nltk/app/chunkparser_app.py,sha256=tbEPtYtccyTcbSCUhFhhbkjCVi_rtel4EKPSogeJOT8,58322 +nltk/app/collocations_app.py,sha256=gJBWxNmkUjXQWkruqcOSE8l51Md2fcM3RmZlrJHJZK4,14664 +nltk/app/concordance_app.py,sha256=HjLN9ybKbjbKqkhJNUakivwIPojvDxqvwnqb7u07BYE,24882 +nltk/app/nemo_app.py,sha256=6ZBJXlJWKWoYnsrEy3Yy6IeFxcy3FNaGWK6QnMYEy4E,12305 +nltk/app/rdparser_app.py,sha256=j4tMGNLnwrwkVw3MyMr3-56TXAwAIIEo-v0yWyjDKEQ,37781 +nltk/app/srparser_app.py,sha256=UQwxqEPDfSYJTw22SSdp5EUX0QwlbztyH4EYHtBerw0,34401 +nltk/app/wordfreq_app.py,sha256=0mzSrNosW3Wh_J_5FdJV8Bq-F_a2x5HRV3iQAI03fnQ,957 +nltk/app/wordnet_app.py,sha256=Ut5VU3hzM4inRayi4c5uyNsbAz7YcGW7_q8BBmrJpPs,35574 +nltk/book.py,sha256=enAPUeJxxAXY0C60vlmPHCVhUxVY2K2gx3wWPH6tU6k,3912 +nltk/ccg/__init__.py,sha256=Gz2z13lWdN_wdcvn78rxJGvU23EKIN_sNm5twz_2nWw,915 +nltk/ccg/__pycache__/__init__.cpython-310.pyc,, +nltk/ccg/__pycache__/api.cpython-310.pyc,, +nltk/ccg/__pycache__/chart.cpython-310.pyc,, +nltk/ccg/__pycache__/combinator.cpython-310.pyc,, +nltk/ccg/__pycache__/lexicon.cpython-310.pyc,, +nltk/ccg/__pycache__/logic.cpython-310.pyc,, +nltk/ccg/api.py,sha256=3xzrsFkp0XM_SihDIEODQEMgFh-KYBaCQs4WNStLTgU,10360 +nltk/ccg/chart.py,sha256=2lyYNM8PY6AhucRmNetqgylPfKz3Pzn4faAKtkvYuFA,14147 +nltk/ccg/combinator.py,sha256=1C5Tqwhp-diD7rHtUfpPbVt4v1a7oPBY0bkHPm52OD4,10633 +nltk/ccg/lexicon.py,sha256=9rC11EzdzOVMybBt6TeYdVw4xj73Ufy7NS1cTqtq5sU,9863 +nltk/ccg/logic.py,sha256=MEukXOQu6dX-i-irRH3Nko5D2ElpDGjVosdgHPZs8wg,1871 +nltk/chat/__init__.py,sha256=4aSic0g0Zwhlxm7PC_t-0JZjChyKcPXTS0hoWTyTvLw,1556 +nltk/chat/__pycache__/__init__.cpython-310.pyc,, +nltk/chat/__pycache__/eliza.cpython-310.pyc,, +nltk/chat/__pycache__/iesha.cpython-310.pyc,, +nltk/chat/__pycache__/rude.cpython-310.pyc,, +nltk/chat/__pycache__/suntsu.cpython-310.pyc,, +nltk/chat/__pycache__/util.cpython-310.pyc,, +nltk/chat/__pycache__/zen.cpython-310.pyc,, +nltk/chat/eliza.py,sha256=27GYLQfKpMzsBvPixXQnZHqZSmaI_8H3mAvuPZAUVNw,9626 +nltk/chat/iesha.py,sha256=WassBbqcT2LbxZHda7vwcIBeIOfzefZ19cxUGay-NNM,4407 +nltk/chat/rude.py,sha256=JMoqOg2_r30pNRwknXWG8qIi_0mm__AnI7tTM1orj2I,3289 +nltk/chat/suntsu.py,sha256=dlYCRQ3INyOXbfL0qwyLaq1E-fqIVS8weRk2gOC8tq0,7185 +nltk/chat/util.py,sha256=dbgxikuBJGP6YhDPFw_ZYTSsBqpqEV5HU1ipWfj21Bw,4014 +nltk/chat/zen.py,sha256=KtZcUzKXlwyfL_tQpa9rtuNB12PAscwaWt2pbvk6GcM,11679 +nltk/chunk/__init__.py,sha256=hIssYRWZj_6YmHQOhJe3DRlvqbehf-Y7e6kSy8Sicp0,7597 +nltk/chunk/__pycache__/__init__.cpython-310.pyc,, +nltk/chunk/__pycache__/api.cpython-310.pyc,, +nltk/chunk/__pycache__/named_entity.cpython-310.pyc,, +nltk/chunk/__pycache__/regexp.cpython-310.pyc,, +nltk/chunk/__pycache__/util.cpython-310.pyc,, +nltk/chunk/api.py,sha256=-gEfVh1nv3CO-YXV3kTSMNDS4_sbuKnM3xVuTq2oc60,1946 +nltk/chunk/named_entity.py,sha256=v__H3Rply3PvrzKRUM2ktkLQcMYJc_14qHFbiKqqaMo,11140 +nltk/chunk/regexp.py,sha256=KXfm9-KJNqSRSJFfV5192yErXVuLH2jmOeCJbROkPRU,55980 +nltk/chunk/util.py,sha256=Ll5PB0ozF7rwNJtsdM6YiA1zktVLO7MOaQtJDR2Qx4g,21311 +nltk/classify/__init__.py,sha256=2s2RPR2IPix1aXumcnpzKSYJ8BzaC-VsKcpVHHZPT0E,4596 +nltk/classify/__pycache__/__init__.cpython-310.pyc,, +nltk/classify/__pycache__/api.cpython-310.pyc,, +nltk/classify/__pycache__/decisiontree.cpython-310.pyc,, +nltk/classify/__pycache__/maxent.cpython-310.pyc,, +nltk/classify/__pycache__/megam.cpython-310.pyc,, +nltk/classify/__pycache__/naivebayes.cpython-310.pyc,, +nltk/classify/__pycache__/positivenaivebayes.cpython-310.pyc,, +nltk/classify/__pycache__/rte_classify.cpython-310.pyc,, +nltk/classify/__pycache__/scikitlearn.cpython-310.pyc,, +nltk/classify/__pycache__/senna.cpython-310.pyc,, +nltk/classify/__pycache__/svm.cpython-310.pyc,, +nltk/classify/__pycache__/tadm.cpython-310.pyc,, +nltk/classify/__pycache__/textcat.cpython-310.pyc,, +nltk/classify/__pycache__/util.cpython-310.pyc,, +nltk/classify/__pycache__/weka.cpython-310.pyc,, +nltk/classify/api.py,sha256=PN1b_jw2InZWMNuzMaPSs2PP-f9_7IZfokohkKd0Xro,6625 +nltk/classify/decisiontree.py,sha256=HL-V9gcFYX2uYaonc3glQq_CAEqyCxKTb1FnKxkpx8U,13083 +nltk/classify/maxent.py,sha256=pJZFnshxF4jfYlY-8zgf3N8P5jVczqxTOCAI6HrVTqA,60921 +nltk/classify/megam.py,sha256=4d2NlMAyrXca2TB_phpff41-qY8YZdqx6LrYLL5s0jI,6396 +nltk/classify/naivebayes.py,sha256=fahYSKoSAMisUKOjXxkIDsRRU7swLyFMSAloL8mToNU,10713 +nltk/classify/positivenaivebayes.py,sha256=WckMp6Olu6x6Ku__NCRVPO2n6WY_AI2yr1y46cy-IgU,7412 +nltk/classify/rte_classify.py,sha256=d7BhvcXp-j1ovcbFs0jz2I22nZtn1pBvfT79kpc1xnY,6301 +nltk/classify/scikitlearn.py,sha256=_D3TQC-jxEn-eq3Y7Ydc1OczkhIAbednxDpcFpbj99U,5548 +nltk/classify/senna.py,sha256=WGne67HygHBl85t4DKqTjWgjILTVOaXoDrQgV7odLm8,6931 +nltk/classify/svm.py,sha256=Izn33z8jQhQ70hJdbli-HUc_dly9O2sxMso0v1MZ5dY,525 +nltk/classify/tadm.py,sha256=jGR9ga8n1rQUCoRg49kkSyZkJ7thteHc0TAApdtVaVU,3555 +nltk/classify/textcat.py,sha256=BeHyxtRXdqAGJtnipjORVrCIjWTHVa0OJm1-WOoMybI,6035 +nltk/classify/util.py,sha256=1Puz0ks5SrYXYQ8eJbcJpqRtGdwS1Hji0TMQk70ZCy4,12461 +nltk/classify/weka.py,sha256=em2Rij5vMKo5LFZbHWBsPZlDkBb-QOLMMEGIPjSgBoI,12938 +nltk/cli.py,sha256=ZdakKKRjRmDn_b3e4TL1UNqaTF4VsLSMgQ4juVWstEM,1897 +nltk/cluster/__init__.py,sha256=1mPkvd-mjaRXe0Aha9qx5Gn_Dr39BRovq-qT74bUi54,4361 +nltk/cluster/__pycache__/__init__.cpython-310.pyc,, +nltk/cluster/__pycache__/api.cpython-310.pyc,, +nltk/cluster/__pycache__/em.cpython-310.pyc,, +nltk/cluster/__pycache__/gaac.cpython-310.pyc,, +nltk/cluster/__pycache__/kmeans.cpython-310.pyc,, +nltk/cluster/__pycache__/util.cpython-310.pyc,, +nltk/cluster/api.py,sha256=sranVby2NHrTr3vmefGkZgREzzOjlE94MLCUbO63rlU,2162 +nltk/cluster/em.py,sha256=uxd6qQ0T1PSE5e_3q41yk66OlkPMX0ws4L0J7ciq1YM,8419 +nltk/cluster/gaac.py,sha256=c_2ewAkcLdWLhW0WjUedESoV7I1UPGlNeY_BNhOTQqY,5921 +nltk/cluster/kmeans.py,sha256=1Ik_3_pIjCfpgDag0LmP4hi2SQiRIwy8sQqtXFSuHYA,8592 +nltk/cluster/util.py,sha256=TVZtWob_8SoZOwG6NtsBPk4fOD40ZVjxnHj9oPa6eC8,10039 +nltk/collections.py,sha256=DiSo-vicLp7UQLpDiDTljFwdDLdQVUi7UVAdgublO8A,23673 +nltk/collocations.py,sha256=NWC5upNNulRI_FYmCHX0rNeZCkCQxqVXMlLnOC_bwa8,14964 +nltk/compat.py,sha256=7f0Eg2_MbidKae8brT_oCuqDSHcfmOskS88Y6-lycmw,1307 +nltk/corpus/__init__.py,sha256=qGkuNZ2GIP4qDOR6mMVtJHnC7oMV3kRxiNr8aGFHhfs,17359 +nltk/corpus/__pycache__/__init__.cpython-310.pyc,, +nltk/corpus/__pycache__/europarl_raw.cpython-310.pyc,, +nltk/corpus/__pycache__/util.cpython-310.pyc,, +nltk/corpus/europarl_raw.py,sha256=aXMKViytBRbry4J0FrO0P20JTOV2bgjuJQ5hOxFkJ-0,1896 +nltk/corpus/reader/__init__.py,sha256=urxkSILuhBlGI9qvsIlhQap6nFKSfkKrYi-rb4LCV5U,6677 +nltk/corpus/reader/__pycache__/__init__.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/aligned.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/api.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/bcp47.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/bnc.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/bracket_parse.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/categorized_sents.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/chasen.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/childes.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/chunked.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/cmudict.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/comparative_sents.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/conll.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/crubadan.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/dependency.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/framenet.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/ieer.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/indian.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/ipipan.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/knbc.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/lin.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/markdown.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/mte.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/nkjp.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/nombank.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/nps_chat.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/opinion_lexicon.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/panlex_lite.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/panlex_swadesh.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/pl196x.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/plaintext.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/ppattach.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/propbank.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/pros_cons.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/reviews.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/rte.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/semcor.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/senseval.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/sentiwordnet.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/sinica_treebank.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/string_category.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/switchboard.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/tagged.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/timit.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/toolbox.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/twitter.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/udhr.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/util.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/verbnet.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/wordlist.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/wordnet.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/xmldocs.cpython-310.pyc,, +nltk/corpus/reader/__pycache__/ycoe.cpython-310.pyc,, +nltk/corpus/reader/aligned.py,sha256=OJUpm8HyzqR0e8hPHd5CrAmH1bfQfXV2hZ4KCak0Zzw,5005 +nltk/corpus/reader/api.py,sha256=Dhu491gmTJnWSilm6lWdQBN59RM1aIT410mrbiDwD1k,19671 +nltk/corpus/reader/bcp47.py,sha256=I27Lj4hMW2IRM62RyXeK9OX-kSeAvEEkoXVWN1HwEiY,8534 +nltk/corpus/reader/bnc.py,sha256=AhFjLzhCpgijSGvhhHiB5AT5vmAPRP6LO-bHEXOEXas,9716 +nltk/corpus/reader/bracket_parse.py,sha256=1EfRuNmCU9DESWjwalyiDZhQuOwb3IYDjL-aAXlId_U,9619 +nltk/corpus/reader/categorized_sents.py,sha256=QLpHrDk3JlbgitkpbfndSXGxfssAyAA5MsnBesFSKis,6221 +nltk/corpus/reader/chasen.py,sha256=iVW9xtRtGcr3EpEf_BjbusRcPqRqPzKoFlOJfxdooTM,4699 +nltk/corpus/reader/childes.py,sha256=keelogBlaIvHWv1A0Q_tHbzmKyMthB2_YNWXQQmG53g,26105 +nltk/corpus/reader/chunked.py,sha256=8foVT90OCSIzv7B99rHsVGSiAX62e4HFc8QyGDqc_4Y,9366 +nltk/corpus/reader/cmudict.py,sha256=CCDpjWDzwPMOXKprKxmNtvX_-gUSFahFO-vUdTJbNhU,3366 +nltk/corpus/reader/comparative_sents.py,sha256=9YwKIk0xf_EhB6qBepCMOENtxoyvOqwuV_aIjW_WRk4,12069 +nltk/corpus/reader/conll.py,sha256=l-pgBQfCiZP90EJo1hnG3TLM9wM5imFvqlt4FfXlDII,22301 +nltk/corpus/reader/crubadan.py,sha256=FLorWHpsugP1Z3bgkukr51baIw_FvAdNqRKIcPeURL4,3627 +nltk/corpus/reader/dependency.py,sha256=nAdjT0H-HV9JW6iUMNe4cW8RR5rdA8IB8ZQ5IQY9aIc,3890 +nltk/corpus/reader/framenet.py,sha256=n59uoGpOSFIapeYnkbPmCamuslARpoa9q8773DkxB5Y,134791 +nltk/corpus/reader/ieer.py,sha256=x6CnLllJTOQYpwGKxjsbtGjej-rxVCEKb4HUyUbmCAw,3802 +nltk/corpus/reader/indian.py,sha256=GQMsNmZwoWvSS4bdeBT5guNUyU8-LErUoMfuT2ukJb0,3014 +nltk/corpus/reader/ipipan.py,sha256=1yJ5cl7AwX9SFmJs07HsuW-eZHp_YkTL-n1nRhadzgw,13092 +nltk/corpus/reader/knbc.py,sha256=rKg0SDmeQCUaSc3SOr97_IfM5FGJjjvTCdFDn1Vnvn8,5787 +nltk/corpus/reader/lin.py,sha256=1gx48Odd8WJ9PpoF6_f5UjmqJ5j22Rsu8GMraepu7Mg,6654 +nltk/corpus/reader/markdown.py,sha256=Y9AeB3F1gbUuEK7HNjR6iwHDAYeneI5XH1rMkw_ubWQ,12028 +nltk/corpus/reader/mte.py,sha256=8p5iQIJOuxu9MOWa1xH1Iijj9J9KmcRUWh2vmcWSOqQ,14385 +nltk/corpus/reader/nkjp.py,sha256=9npzIMG-tOJjb1NpiwDvH1uXXQ5eBEq8xp_KeQ5jD10,16332 +nltk/corpus/reader/nombank.py,sha256=v-smV3YSQfl54cF-NM0St9dodOKawPL6JGsMv5tvI4g,16247 +nltk/corpus/reader/nps_chat.py,sha256=tjp98O7FNsyoxXenMT5OW0XaJN1RciICJfeekN6AWa8,2940 +nltk/corpus/reader/opinion_lexicon.py,sha256=Xnb9RpQJBxZWdnN-DN41Cw1S4HR35FmomKO-ISy476k,4230 +nltk/corpus/reader/panlex_lite.py,sha256=QJg00vmQTl-iTot524qmEah5LBiacNA9GmpNp9hhZYE,5440 +nltk/corpus/reader/panlex_swadesh.py,sha256=JxpPNLY-WqHZvXsSG7eKFc9eBUHsKO0qOCEUm4iupUw,3287 +nltk/corpus/reader/pl196x.py,sha256=e7yXJOjyKh4uTxn78rvSdSF-K8wSDghYutvrf1IMPjk,12320 +nltk/corpus/reader/plaintext.py,sha256=o1ekALImW_Uyv4pjXn3y2P7uAevWqAAU-X1FrAinqiA,8456 +nltk/corpus/reader/ppattach.py,sha256=vGCcRHcwSMWaHYI1K6KliD9kTKtvabGosqGvPWZjIVs,2903 +nltk/corpus/reader/propbank.py,sha256=1Bv1psLPUwLsbfVUuTP9RanC4MoJp1JHboxgRqGJxrs,17776 +nltk/corpus/reader/pros_cons.py,sha256=DzQ0xyw7fiyZucbrrOqUooPsDv-SCARTMwTxcVzzwlo,4896 +nltk/corpus/reader/reviews.py,sha256=nDNnRfW6XqdJEYh1nKbIOOHFxpHTPD27nxgQanmL8go,12321 +nltk/corpus/reader/rte.py,sha256=ihayGJCEZ9Mw4idk337owoXGlC1Ikn4yM_eUZyHlqgY,4785 +nltk/corpus/reader/semcor.py,sha256=UxLpm2sbR5_6kPQD-9QSwzYkciA71OGRZ4PGudlln1w,11694 +nltk/corpus/reader/senseval.py,sha256=ZNyHHh5gpa2dGQHThMVUoxekDP0DRkrQhKAs8wmY0vI,7539 +nltk/corpus/reader/sentiwordnet.py,sha256=yQeTvAT2T1XFg45dgYfFyIJBhXvkxvoEB7H7w-tEosk,4626 +nltk/corpus/reader/sinica_treebank.py,sha256=64GeJSNCg3eXUFXDasGIql60OC6Pq1Fwc9OtB8Dsais,2541 +nltk/corpus/reader/string_category.py,sha256=-oPUy5R2qb4xiyFsCKGAlUwc3CCsWYCvQsmc2F7NNt8,1919 +nltk/corpus/reader/switchboard.py,sha256=yRjPwtDRx-9rZLsBiX_cAcrMl90zaJ2kanD7RB5hT2A,4547 +nltk/corpus/reader/tagged.py,sha256=P-gUFkUTazKCJzOlqvwA8aAPWYB6Pw08pkieRduIaJU,12140 +nltk/corpus/reader/timit.py,sha256=FpbCiufjEoKPZNgGjvUO1-dAhTq36qeCauaFIzqzRO8,18473 +nltk/corpus/reader/toolbox.py,sha256=yVxaqjDOVCjHRNz4Zs_8Zu8fkbi88YEb_JxwCYtHwVE,2121 +nltk/corpus/reader/twitter.py,sha256=UdnwB2Hh6quI1KhFAAlriybVrYBoUyNqxzUBCdFLics,4608 +nltk/corpus/reader/udhr.py,sha256=tjqXc1JQiTURHnsTU8RIzwxdQIZasbtdMSPz4msnHVo,2592 +nltk/corpus/reader/util.py,sha256=fZy5GyMxJ-urpTqO19Sj-oRQqgL-EI65_UDscr2nizg,32225 +nltk/corpus/reader/verbnet.py,sha256=NBIwOd2JrnHUamt29yzQjHS5VyzqQsOoCI9ZwOfXZIU,25404 +nltk/corpus/reader/wordlist.py,sha256=8dlcXRIjDuJ6U_dvbw6OF_VOgbC4EVeXI8uE2tCxjbM,5812 +nltk/corpus/reader/wordnet.py,sha256=nSIDdVHF_FDx6eOlsU5pUgV5z6YjPbhvQoWyEfb8_Yo,93352 +nltk/corpus/reader/xmldocs.py,sha256=_fVqoEIAaYKT772IEcYmuk_7OaqUdsRLKZpbaL4up88,16285 +nltk/corpus/reader/ycoe.py,sha256=9VbkO_JnFG2joiWfjsfYZ53vsSPl8lWfK00faIAaLN4,10504 +nltk/corpus/util.py,sha256=Q9xYJ97UUOy8vuuDA-uidzpE1oEU_-k6M6L0CcxsZ90,5867 +nltk/data.py,sha256=rr3iRF4UJi7bh3Ss1Gp0bn8qml3YmTOV8kyWeYOavO8,52814 +nltk/decorators.py,sha256=U1-DvExxy0Uv96M0St_rR8IAh8Em3eK6uS4AXIf_Ti4,8526 +nltk/downloader.py,sha256=SWUlq_6w6PDWKs4UCXmY3HyvEfDII1Mp7bgjChv-KEM,95506 +nltk/draw/__init__.py,sha256=vtk9kECEd_9ZZ0pqST6z5Sb-no-VDpEohi7UHD_YQcE,810 +nltk/draw/__pycache__/__init__.cpython-310.pyc,, +nltk/draw/__pycache__/cfg.cpython-310.pyc,, +nltk/draw/__pycache__/dispersion.cpython-310.pyc,, +nltk/draw/__pycache__/table.cpython-310.pyc,, +nltk/draw/__pycache__/tree.cpython-310.pyc,, +nltk/draw/__pycache__/util.cpython-310.pyc,, +nltk/draw/cfg.py,sha256=Y-89bIKWPdiCAn1GkA0eOP08L6eeQHc4gVQKMzBj2sk,30794 +nltk/draw/dispersion.py,sha256=MaCehYu6cTuRhMTzDW7E_cwGjXkP7auGCMsD31WjLcE,1854 +nltk/draw/table.py,sha256=Gz7IZ6JDxsfLUc5zLui_g1IyTfhPCEJU-u8K71S_qrc,46257 +nltk/draw/tree.py,sha256=N8qbNssr6A8OLp4zLE2FJ-jQzWYWFkeASvUeGzc2wKY,39275 +nltk/draw/util.py,sha256=8n8YJrrTWSD-MUEy96bX-oaaRBufEtg74bXPXWzWbJ0,90944 +nltk/featstruct.py,sha256=BVbotcvgnlNTKMDC1bL16-i3PCw5zXgP7X20tt-yPF0,106108 +nltk/grammar.py,sha256=uTC2ScpQIVxWt38QfWceYczkTjTVzPplmD63RfLLKkY,59174 +nltk/help.py,sha256=Sj2M3-tktpBZxwHxx1btdthJZ4hhZx-XUXlYuGv2Kp8,1709 +nltk/inference/__init__.py,sha256=nw4pQFHOGUv4x7u21GrJBOUS2hc7JibvvgbVnqXuksA,814 +nltk/inference/__pycache__/__init__.cpython-310.pyc,, +nltk/inference/__pycache__/api.cpython-310.pyc,, +nltk/inference/__pycache__/discourse.cpython-310.pyc,, +nltk/inference/__pycache__/mace.cpython-310.pyc,, +nltk/inference/__pycache__/nonmonotonic.cpython-310.pyc,, +nltk/inference/__pycache__/prover9.cpython-310.pyc,, +nltk/inference/__pycache__/resolution.cpython-310.pyc,, +nltk/inference/__pycache__/tableau.cpython-310.pyc,, +nltk/inference/api.py,sha256=GdomkZQT97b7Z_HER__KuhCDehRbT1jxD7MoV-1COnY,19560 +nltk/inference/discourse.py,sha256=XojyiwkuvpTjBOZCcQ0q5CBpIMOw5fXw3eNzjZPJoqw,22691 +nltk/inference/mace.py,sha256=EKRZuwCrX320jzWrq3NOFnfIugapuYdgiH31dVf2ZvE,12243 +nltk/inference/nonmonotonic.py,sha256=4_uEiG5h11Cv9_Z7dFIGAZ8M_QpZ-gWNVew11TGbeSU,19174 +nltk/inference/prover9.py,sha256=LHyuOmISLo5c64e5VOXiZsK-bG7z-0cb3xY01kWE068,16266 +nltk/inference/resolution.py,sha256=wx0YCAC5GgICEOg0VN_x67W88cGHu854aQD3pIJcfq4,26761 +nltk/inference/tableau.py,sha256=IpDZT3FbM02R2TnfjmAOlwXivnP19DXoT9Fu6xW4Cv0,26320 +nltk/internals.py,sha256=eOGggyeDGf6gyU76p2SU6_vFQP6h5Frg8idDyS6naW8,39416 +nltk/jsontags.py,sha256=IWrXxAjSzlgaxqclkKcAnxNzANbG1Zjf_j2jjPnxUy4,1948 +nltk/langnames.py,sha256=dDNlEkDmsxWUKxU5CGpYLBHpWlcD1ZrUiO3XTMEPBFU,17957 +nltk/lazyimport.py,sha256=qBI5PNKz5qYLrxow19KwLrSE821TyRhVKCafAGML-1E,4719 +nltk/lm/__init__.py,sha256=Gsg0OaefWlZVbCDGYqh1cEluhXpWS7vN8Bgpfm3wa-w,8051 +nltk/lm/__pycache__/__init__.cpython-310.pyc,, +nltk/lm/__pycache__/api.cpython-310.pyc,, +nltk/lm/__pycache__/counter.cpython-310.pyc,, +nltk/lm/__pycache__/models.cpython-310.pyc,, +nltk/lm/__pycache__/preprocessing.cpython-310.pyc,, +nltk/lm/__pycache__/smoothing.cpython-310.pyc,, +nltk/lm/__pycache__/util.cpython-310.pyc,, +nltk/lm/__pycache__/vocabulary.cpython-310.pyc,, +nltk/lm/api.py,sha256=u65V1dwqoBjCdxNSbLLUpY0zjzY0-WBGK7HHvV0Dct4,8495 +nltk/lm/counter.py,sha256=AOqwTQFxaWNMnJZp5E6i5bPBq54Lc7dklp76J5Ty_rY,5250 +nltk/lm/models.py,sha256=ricaFU593KT1n8ri5b-3JTxwpO__XXkZCYgPadPmrLA,4903 +nltk/lm/preprocessing.py,sha256=yeW6yCp2e0zGFpcQ_puPZ0VBsjcespq2MLPPdUojY3A,1714 +nltk/lm/smoothing.py,sha256=GqBAZAgZbQgMFTfu8LJFuWobda4AC8A8va2C3hbkI28,4745 +nltk/lm/util.py,sha256=X7x-__sk-f_Z8ttRmLP1ASLIQlVLOVo1ziID3F9qDZQ,474 +nltk/lm/vocabulary.py,sha256=rMng32oqXSg1XXOFpRi0TQtjaF_fQNx3b9MGKGakPnQ,7099 +nltk/metrics/__init__.py,sha256=gu6faSWxN5vW86Lk7fvzb_NeD4H-5BcUvTqmo5lSNLg,1243 +nltk/metrics/__pycache__/__init__.cpython-310.pyc,, +nltk/metrics/__pycache__/agreement.cpython-310.pyc,, +nltk/metrics/__pycache__/aline.cpython-310.pyc,, +nltk/metrics/__pycache__/association.cpython-310.pyc,, +nltk/metrics/__pycache__/confusionmatrix.cpython-310.pyc,, +nltk/metrics/__pycache__/distance.cpython-310.pyc,, +nltk/metrics/__pycache__/paice.cpython-310.pyc,, +nltk/metrics/__pycache__/scores.cpython-310.pyc,, +nltk/metrics/__pycache__/segmentation.cpython-310.pyc,, +nltk/metrics/__pycache__/spearman.cpython-310.pyc,, +nltk/metrics/agreement.py,sha256=NveWTfhBZDQWxqwEWbDMJKpeRElk7vtdN0bh9nMnKWI,16421 +nltk/metrics/aline.py,sha256=yWktce0eQ_r60KKUioLZSkorZT8U2zPbxLA-wqLzzDA,32845 +nltk/metrics/association.py,sha256=1hNurRIKGop2rDttTKddxtZkMrJ_0qBczugF9Mm4S4o,16569 +nltk/metrics/confusionmatrix.py,sha256=szVY58vzNWQRr4objnsiouDcwd-kqAT5_b59wvwx4q8,13035 +nltk/metrics/distance.py,sha256=oQ-o9tMyv5kTOJsRe2EKHLlArLiZxlAMJY73tKFnAcw,17661 +nltk/metrics/paice.py,sha256=yge6FA2y8_AGYSmCWfzapmKBFF6kkJTe-puWp-KcTBk,14739 +nltk/metrics/scores.py,sha256=nv0f5lsR_nKY_A2pJrVMtH4Mzef_CBpDWpkBoHnb-L0,7922 +nltk/metrics/segmentation.py,sha256=WdPD6aH31Z55RfEJ7OM0FQoxmGLr_q-q6vEJ0CqxwDY,7221 +nltk/metrics/spearman.py,sha256=irorJE5fYsGGxLLvKGvvL6je-hM4aonDBVhWS_ZiOS0,2197 +nltk/misc/__init__.py,sha256=pgYpCMn6fRf90Zwn52OjzHDe5MsPgl7u9cbTKeEH8pk,406 +nltk/misc/__pycache__/__init__.cpython-310.pyc,, +nltk/misc/__pycache__/babelfish.cpython-310.pyc,, +nltk/misc/__pycache__/chomsky.cpython-310.pyc,, +nltk/misc/__pycache__/minimalset.cpython-310.pyc,, +nltk/misc/__pycache__/sort.cpython-310.pyc,, +nltk/misc/__pycache__/wordfinder.cpython-310.pyc,, +nltk/misc/babelfish.py,sha256=9UkSa6l_j1BHxaT9CU1Viv_51RB0k9AA--3ytmQpsAk,361 +nltk/misc/chomsky.py,sha256=UatgZu7Zj3W5DQzuxHZW6Zyxv5kH9l_W2u_ZnOCqXcc,5319 +nltk/misc/minimalset.py,sha256=z7-UaqT7F-2ba_qybwY46bgaH4l5zQcyBkg5K_joFQs,2979 +nltk/misc/sort.py,sha256=aeqONHRGSDcVksvB6a5npkwdf5sAy6A2vRP9wYp0Y1w,4547 +nltk/misc/wordfinder.py,sha256=2HR5Fj4hv7Q2IDFlJb5Nl644uNWtSkWEUe-hBKYt6Zg,4352 +nltk/parse/__init__.py,sha256=w2H8yrs8-Wov_wtGTHoMyRyGt7Q4iLyQC6Z05LhuEhc,3797 +nltk/parse/__pycache__/__init__.cpython-310.pyc,, +nltk/parse/__pycache__/api.cpython-310.pyc,, +nltk/parse/__pycache__/bllip.cpython-310.pyc,, +nltk/parse/__pycache__/chart.cpython-310.pyc,, +nltk/parse/__pycache__/corenlp.cpython-310.pyc,, +nltk/parse/__pycache__/dependencygraph.cpython-310.pyc,, +nltk/parse/__pycache__/earleychart.cpython-310.pyc,, +nltk/parse/__pycache__/evaluate.cpython-310.pyc,, +nltk/parse/__pycache__/featurechart.cpython-310.pyc,, +nltk/parse/__pycache__/generate.cpython-310.pyc,, +nltk/parse/__pycache__/malt.cpython-310.pyc,, +nltk/parse/__pycache__/nonprojectivedependencyparser.cpython-310.pyc,, +nltk/parse/__pycache__/pchart.cpython-310.pyc,, +nltk/parse/__pycache__/projectivedependencyparser.cpython-310.pyc,, +nltk/parse/__pycache__/recursivedescent.cpython-310.pyc,, +nltk/parse/__pycache__/shiftreduce.cpython-310.pyc,, +nltk/parse/__pycache__/stanford.cpython-310.pyc,, +nltk/parse/__pycache__/transitionparser.cpython-310.pyc,, +nltk/parse/__pycache__/util.cpython-310.pyc,, +nltk/parse/__pycache__/viterbi.cpython-310.pyc,, +nltk/parse/api.py,sha256=R3xG2NEEAq5TvzQpuTKGE_xX_JLoVwp0lTGCQsL2WV8,2354 +nltk/parse/bllip.py,sha256=LipdGFlJrKinwKVIgiEKosvdaDrFQGQq-7rZUOkkPNw,10976 +nltk/parse/chart.py,sha256=6b3HoLPbT0OlKkETtezIODvqpBKIlkAPoY7r4gV91oM,63760 +nltk/parse/corenlp.py,sha256=t9ePlsr3zWxf6CKEbczk71mCVe_y5j9U2sd8el8REms,27745 +nltk/parse/dependencygraph.py,sha256=J9DcO7sszs6Pp_TqSRL-pJLwBATw_F98_xaZ9m0-kbM,32468 +nltk/parse/earleychart.py,sha256=OVBGyzHbv6M76LyFlHv1Gz-0PvZ0xdRdIYgSb_Qet84,18274 +nltk/parse/evaluate.py,sha256=P3qGZ7WG90mOrP4MEQXzDbIOB8WWhDBG3PIDIYfozx0,4468 +nltk/parse/featurechart.py,sha256=iK0O0uC-ZAgg6Bcj9ACeQkAvBTrWo-hteu-b-cPdPhU,22532 +nltk/parse/generate.py,sha256=kDlYgWj1f_YpbzVJq7OL6O5H0zU03hrrjxLuKLzJgvE,2381 +nltk/parse/malt.py,sha256=_XMFcW3SOH0XCrzfv-o4-U8RTkcwDbFSHpfbWOzMtuM,16571 +nltk/parse/nonprojectivedependencyparser.py,sha256=ncMpvaMMc8TtRzaPSub8Qt0IUfaXQV42YSbUMQDEnvg,29446 +nltk/parse/pchart.py,sha256=KGMYKf5x2psS9XZfU6wbUnIF5jwtoeAtsHzJz6vyZ-E,20480 +nltk/parse/projectivedependencyparser.py,sha256=WIWDRYAaUqr8u9UQP5AwSLXbciyoaClSN4TavumkfkY,28243 +nltk/parse/recursivedescent.py,sha256=uaVune-fIIWa_wT3CTY4O8p9MhTP4VgW1UoNauPsBZQ,26032 +nltk/parse/shiftreduce.py,sha256=Yl2JYRdUOqJIjQXOEWv4fUfpfGu5kLq94HaYPTV5QAk,17071 +nltk/parse/stanford.py,sha256=xyPRx710ddMCVYIXje2gTwvZrvYgxOM1CMhM7ziDuVQ,19312 +nltk/parse/transitionparser.py,sha256=9EdYOIGZPq77l3fgI4dnH63146YpC5PBfaZKaBPkhlU,32272 +nltk/parse/util.py,sha256=6mu9ZVO2hRduKhZGr40bAwRPvhKyTO_Srx2MT8pAy6E,8667 +nltk/parse/viterbi.py,sha256=czOjotH__XU_A_Mpf5-xYlYi8bXYOTbIff1mrPZBNYQ,18351 +nltk/probability.py,sha256=ikWJyp0Equm4RyGxXOJFBBgNfr2jo2fK0Ck7xwmSks0,92907 +nltk/sem/__init__.py,sha256=3-QdBYTgLd1iRHzo6e2f3OT0CDBWZAVmzhnlh7Yvu24,2443 +nltk/sem/__pycache__/__init__.cpython-310.pyc,, +nltk/sem/__pycache__/boxer.cpython-310.pyc,, +nltk/sem/__pycache__/chat80.cpython-310.pyc,, +nltk/sem/__pycache__/cooper_storage.cpython-310.pyc,, +nltk/sem/__pycache__/drt.cpython-310.pyc,, +nltk/sem/__pycache__/drt_glue_demo.cpython-310.pyc,, +nltk/sem/__pycache__/evaluate.cpython-310.pyc,, +nltk/sem/__pycache__/glue.cpython-310.pyc,, +nltk/sem/__pycache__/hole.cpython-310.pyc,, +nltk/sem/__pycache__/lfg.cpython-310.pyc,, +nltk/sem/__pycache__/linearlogic.cpython-310.pyc,, +nltk/sem/__pycache__/logic.cpython-310.pyc,, +nltk/sem/__pycache__/relextract.cpython-310.pyc,, +nltk/sem/__pycache__/skolemize.cpython-310.pyc,, +nltk/sem/__pycache__/util.cpython-310.pyc,, +nltk/sem/boxer.py,sha256=rACkFGXGKpK0pMxGXxtpkaKF50-aysHw_9GQjf9xtww,55287 +nltk/sem/chat80.py,sha256=TAiZ4BROBzIibz3jVkcgWL_BlmkC-3gMziEySS8JWMw,26519 +nltk/sem/cooper_storage.py,sha256=ghg6kYG6a54I4sDiwgzX70Jcm6HlNCPDm5gZi2ZqPG4,4210 +nltk/sem/drt.py,sha256=7YDYqc9XuWBleZ0aL41LW_wqpBbpvCMgqMnTOiRXX9s,53149 +nltk/sem/drt_glue_demo.py,sha256=xe-SoNG6JVIYtB7YoMO75vEil0KSVGI-gxcfzuLgQdc,19171 +nltk/sem/evaluate.py,sha256=07NnlgTmfjJy93UtFfFB6mdZRiW2hrm9ZTfO4RLfjcM,26282 +nltk/sem/glue.py,sha256=1nBjlbsqna4gXnNG8iMQn5wb15QPxejyDn-vbTPmXrc,30254 +nltk/sem/hole.py,sha256=D9Cnc89WvG9WCDlDNjYF3cig4UmHJgFIwtevuNX1CBs,14216 +nltk/sem/lfg.py,sha256=SR-OYvj8HBtIx-EBvnfkOFstSj8eKXzAiVyQeFIsmZI,7716 +nltk/sem/linearlogic.py,sha256=Wg_jzVVQDy1nCvYkuJB-g9tdwBYRmsenQboD7eRbEU8,17234 +nltk/sem/logic.py,sha256=F0giVBpzZi3HYLQBlsiU0tLOOVEEkswlOYkRsitiVnU,70239 +nltk/sem/relextract.py,sha256=UPv_7dKm-GpdYGL_J8CsJHAz0ALxGv_4EZ_rFk7_y7o,15809 +nltk/sem/skolemize.py,sha256=CtOfU12APkeIkp-Z_jMlAOMkXIEO9le8TQUvlZ5rBns,5870 +nltk/sem/util.py,sha256=GB8SOc7dtyywa4WrlVHr1mHwRntX9ppD7N1em-Bh1Yo,9062 +nltk/sentiment/__init__.py,sha256=vre9oZROX6xHjdKjM8inuxYfiVLf341HZ_sPKyoA2Jo,382 +nltk/sentiment/__pycache__/__init__.cpython-310.pyc,, +nltk/sentiment/__pycache__/sentiment_analyzer.cpython-310.pyc,, +nltk/sentiment/__pycache__/util.cpython-310.pyc,, +nltk/sentiment/__pycache__/vader.cpython-310.pyc,, +nltk/sentiment/sentiment_analyzer.py,sha256=vB09EsrK-16IqHDD7TdsHZYGrkrUcY1rPzNtHpTrL1w,10432 +nltk/sentiment/util.py,sha256=memSkOlHeTfJpeZYtgYJ9LsiMOcoVw4wvwHBOsFwneY,31275 +nltk/sentiment/vader.py,sha256=efJHlAUaRr6bV63ZVDIMAOaolVqct6lBKo9tMVCOpkg,21764 +nltk/stem/__init__.py,sha256=BUVavJGvw_wgnL3PkklsVg9PNMbz3rCW1U6XTufamC8,1296 +nltk/stem/__pycache__/__init__.cpython-310.pyc,, +nltk/stem/__pycache__/api.cpython-310.pyc,, +nltk/stem/__pycache__/arlstem.cpython-310.pyc,, +nltk/stem/__pycache__/arlstem2.cpython-310.pyc,, +nltk/stem/__pycache__/cistem.cpython-310.pyc,, +nltk/stem/__pycache__/isri.cpython-310.pyc,, +nltk/stem/__pycache__/lancaster.cpython-310.pyc,, +nltk/stem/__pycache__/porter.cpython-310.pyc,, +nltk/stem/__pycache__/regexp.cpython-310.pyc,, +nltk/stem/__pycache__/rslp.cpython-310.pyc,, +nltk/stem/__pycache__/snowball.cpython-310.pyc,, +nltk/stem/__pycache__/util.cpython-310.pyc,, +nltk/stem/__pycache__/wordnet.cpython-310.pyc,, +nltk/stem/api.py,sha256=eCDlGturJXqs0AG-q0AHN6EiqU7ytGs43ly-eEiob6s,741 +nltk/stem/arlstem.py,sha256=3LW-2dSdsNNAGlQluBiOmHR6V7xOaRdIT2OdlZgcJVk,13006 +nltk/stem/arlstem2.py,sha256=2SrhWIANG2Gd9Rxbvj-UIWc7-zr5-zIJGB8HDyszqW0,16535 +nltk/stem/cistem.py,sha256=59OmWHS_EWFO-I-ZN1DVFnnz1rdcUxKCBwM4z8DRQoo,7259 +nltk/stem/isri.py,sha256=2tOa82AbRnXPympONMJOVc6-CsWdyWlMgcllFwqrJ-I,14990 +nltk/stem/lancaster.py,sha256=QrvXzQGULfwIMsW1Z8By8Pz8Exb1MTTydc5ZBV4dJao,12587 +nltk/stem/porter.py,sha256=c5jYrt7IHEpV-neeZp6zHOjLXYh83x0tksE3VP7YkRg,28372 +nltk/stem/regexp.py,sha256=5lb_FGFd7SixF8hyAZpNNLCCeNDfRhnp750We9dbZJA,1578 +nltk/stem/rslp.py,sha256=cMyro3T1eslSdD-sE3Vq5Nnp4yJNrlB6gTQz08tlOxU,5511 +nltk/stem/snowball.py,sha256=6somwXR8EoIN9TNmK251dPRGRZ75oC4i6CfPV8iqwk8,183890 +nltk/stem/util.py,sha256=ktwGClVb3h-AndTu60wS6rfTAdruI41M1zbccWt7wm0,644 +nltk/stem/wordnet.py,sha256=AojhkFURMhpF8vmS7uTkfeJNL-EYrvGb42v-yrTSD8w,1655 +nltk/tag/__init__.py,sha256=v7hPbsW3lrb6AFSIZ3uhZ33iwCKVx7RvBHrNkNro1NY,7298 +nltk/tag/__pycache__/__init__.cpython-310.pyc,, +nltk/tag/__pycache__/api.cpython-310.pyc,, +nltk/tag/__pycache__/brill.cpython-310.pyc,, +nltk/tag/__pycache__/brill_trainer.cpython-310.pyc,, +nltk/tag/__pycache__/crf.cpython-310.pyc,, +nltk/tag/__pycache__/hmm.cpython-310.pyc,, +nltk/tag/__pycache__/hunpos.cpython-310.pyc,, +nltk/tag/__pycache__/mapping.cpython-310.pyc,, +nltk/tag/__pycache__/perceptron.cpython-310.pyc,, +nltk/tag/__pycache__/senna.cpython-310.pyc,, +nltk/tag/__pycache__/sequential.cpython-310.pyc,, +nltk/tag/__pycache__/stanford.cpython-310.pyc,, +nltk/tag/__pycache__/tnt.cpython-310.pyc,, +nltk/tag/__pycache__/util.cpython-310.pyc,, +nltk/tag/api.py,sha256=hxGeLViDHBmcXYnWtYA8N3r7meYyOzW8nxafEaSXH0c,14810 +nltk/tag/brill.py,sha256=PA8cP2tXwbxYtSO33vrV8RwGJNm4ZU_T5iCkqKf0W4g,16829 +nltk/tag/brill_trainer.py,sha256=ba0A2b255xtF3N30iiLWy8oazQycJ3IP-gBY6SMuX2w,27900 +nltk/tag/crf.py,sha256=eaU05hpUOPRO9TRVt6VXffvCB9ZsahkDu2gQi1x8FFQ,7960 +nltk/tag/hmm.py,sha256=mkD06CBebJyBZ-lqXVsyVwCL1blqA0ucijINjynZiJQ,50349 +nltk/tag/hunpos.py,sha256=th_TEehZi3QIlaFLJhI40tBpqn1Rn3QYzfv_dT1n1w8,5195 +nltk/tag/mapping.py,sha256=TfcRmPsp-cM7FFx03ElLmf0ZYvW48N35SIl_y7M7QHY,4024 +nltk/tag/perceptron.py,sha256=ge04T_6s-qIk2ElRLA43euMSzbaxQur2KxosuLfq_Tg,13425 +nltk/tag/senna.py,sha256=_Y-mrYv1Y4SH8720pMKiXYvPFnuNJMeDeG6PUu1TyHk,5903 +nltk/tag/sequential.py,sha256=hWSEZAYlZa8uKd5-o7NeAIsEaHOj2lZVaVX5F5ymdoI,28621 +nltk/tag/stanford.py,sha256=_HuQnKPvcHn01gUtrvhaYI_GpOG2tldthDXoV3daCFA,8427 +nltk/tag/tnt.py,sha256=gNsbvbZYFnhPLnbCKCAOdwJIsK7OyNxAje2SfRclyz8,18432 +nltk/tag/util.py,sha256=FEApJmJ5lpb1mWbfhRvtKce9sR93WPiRJfUyAvBoc78,2353 +nltk/tbl/__init__.py,sha256=7w88VhcTvvCRY03cUftsmoLkf5YanRyM3PXU-Ik2t2c,790 +nltk/tbl/__pycache__/__init__.cpython-310.pyc,, +nltk/tbl/__pycache__/api.cpython-310.pyc,, +nltk/tbl/__pycache__/demo.cpython-310.pyc,, +nltk/tbl/__pycache__/erroranalysis.cpython-310.pyc,, +nltk/tbl/__pycache__/feature.cpython-310.pyc,, +nltk/tbl/__pycache__/rule.cpython-310.pyc,, +nltk/tbl/__pycache__/template.cpython-310.pyc,, +nltk/tbl/api.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nltk/tbl/demo.py,sha256=VpqlJQdaXU5wrpJF2aSLhTlF62fOQl5sCYiBJrPZcZU,15338 +nltk/tbl/erroranalysis.py,sha256=pav23zp0nOiGydqQ4wSJbXAVJmixxCwgodSthCL-onU,1454 +nltk/tbl/feature.py,sha256=CrcNBJ_BGqnEXzFshpz_MGe6mTB1V0rMWBGWyn5Kukw,9690 +nltk/tbl/rule.py,sha256=k-0gRVsZOigPWkqn_NuZvOeQ5bXx6YTYuSRA-oUSXPo,11515 +nltk/tbl/template.py,sha256=LeTafIw_oehScGgwFgl9fklSIOY5tJvuJNFgzaRsJHU,12892 +nltk/test/__init__.py,sha256=79tUwF8keWTdPOWa-gpVx0VkJr6DABwmq9j589IFABU,487 +nltk/test/__pycache__/__init__.cpython-310.pyc,, +nltk/test/__pycache__/all.cpython-310.pyc,, +nltk/test/__pycache__/childes_fixt.cpython-310.pyc,, +nltk/test/__pycache__/classify_fixt.cpython-310.pyc,, +nltk/test/__pycache__/conftest.cpython-310.pyc,, +nltk/test/__pycache__/gensim_fixt.cpython-310.pyc,, +nltk/test/__pycache__/gluesemantics_malt_fixt.cpython-310.pyc,, +nltk/test/__pycache__/portuguese_en_fixt.cpython-310.pyc,, +nltk/test/__pycache__/probability_fixt.cpython-310.pyc,, +nltk/test/__pycache__/setup_fixt.cpython-310.pyc,, +nltk/test/all.py,sha256=Ojl5ZxWh7LV7XsdOyR8kWEOxMk7E4m7QnCUrQoACzQk,819 +nltk/test/bleu.doctest,sha256=2LmQDiyg-BueBx8LbLJanXCs7UZI9zsLe5nurrXBVBs,862 +nltk/test/bnc.doctest,sha256=_8MYpY69ibBaUmkNE3lu187WDOVV_AF4GgTvxSmRuLQ,2051 +nltk/test/ccg.doctest,sha256=YHiQsfRdaAuOXrkBfD2WTSd9yWbAWyJ8K2KDtkAWyhk,19786 +nltk/test/ccg_semantics.doctest,sha256=vPdQojJQwAxOXJ12cUQXKnHTnU5IOr1z-FszyX1MAAQ,31066 +nltk/test/chat80.doctest,sha256=-bY1zKFhVZmtKf82ndwBt3aiNHy7SeSduXjLKggyy2o,8735 +nltk/test/childes.doctest,sha256=FcKzuX_RXXHZNhCyU_S2B7xTN46R54mxjYs17bejhN0,9363 +nltk/test/childes_fixt.py,sha256=A04g6D3QytqCatGOYAg6fCXqXT8XyjIPbRSkdG1ys8o,372 +nltk/test/chunk.doctest,sha256=hDVcWtgJPEZJZLceKbm_885sgpaSR6DFHQMesFzyYeU,11511 +nltk/test/classify.doctest,sha256=e1MEsCmHQcLkky6XomMc4je0mhImN8YghEy-A13Ieh8,7699 +nltk/test/classify_fixt.py,sha256=zUj2OUNhyi2sJNmVdShbQnuhLUieJ7h_AwKmvj1Gnzw,119 +nltk/test/collections.doctest,sha256=ygTv1l_HbXdVHDw91rjUxJy6KFnSvbvzpkXLvTfnVUU,622 +nltk/test/collocations.doctest,sha256=ezSwbUboIG91fbmKX7mvFo3c9C_BlbgM85tRrctZAKU,12506 +nltk/test/concordance.doctest,sha256=rPESoW6-ugpgj2LgBYgChcjOWv75x0eu3Y4A2MuZSsQ,3544 +nltk/test/conftest.py,sha256=apMfe_V5EHXZHCXCMxVqMt3gpHCUwwxR-MxESis9--Q,804 +nltk/test/corpus.doctest,sha256=K3vInYJLjonuHTysZCrHvZ1ytgseREpqZMcz9VsolHA,99206 +nltk/test/crubadan.doctest,sha256=qn63dlNT7XmxUA1m2JmcwuX_i12_cWCAfJDVxSp9Kgs,2060 +nltk/test/data.doctest,sha256=ggmXuZT1Hy7Bd5_bppQPhz64lO8E-dDHXtuh2hQ5HYM,14266 +nltk/test/dependency.doctest,sha256=pXyaAjDE232xA0Q2EEvxkMNu8COSk0l52ELMi07C-qI,7669 +nltk/test/discourse.doctest,sha256=AxJetWi0DetMfptecrI9lXac7mN4YVrfCVPr3VXDBkQ,17923 +nltk/test/drt.doctest,sha256=wTRxo8ISpwcsF9RpwlHn0PLfpaL5ZCZxN2oEpqlGHYg,20076 +nltk/test/featgram.doctest,sha256=APe6OaMrt1UwuKzLM-7NRXIQF0mPnpZj4vR1yTSnz0o,28870 +nltk/test/featstruct.doctest,sha256=hjCDZs3NN2DJdyAH9BygfXUuWAP723xQFk3YMSg7p_Q,38894 +nltk/test/framenet.doctest,sha256=KcZIkt-8_HJ2gDl7BvWG-vfOI3NI7Be2VT9sc9hcAXY,10797 +nltk/test/generate.doctest,sha256=7yYAJ1EO1qAHy9OYVcoEehPs4hoEhvTvs0VH5H08oBg,2050 +nltk/test/gensim.doctest,sha256=kmwbF1XcgZvyfrfdotjNw-VlkzgGFVxwVObQ0IPcAXI,5200 +nltk/test/gensim_fixt.py,sha256=2p-4RObBWBU-YzbGPXAj03aPhimK-l_RLNdS_RuKHh4,77 +nltk/test/gluesemantics.doctest,sha256=QVR_Hki10s1CRghGvxJDNDA2u1J89uj1O_nKt0RF8eo,12705 +nltk/test/gluesemantics_malt.doctest,sha256=4S5usX2BTPAPpK7t1SIfwHGaajpVXAuk5CuAUvSagZU,2667 +nltk/test/gluesemantics_malt_fixt.py,sha256=H7YUsT_M5LteTBF9utPjqUP8pybUaW3w2fQdGUPiT3c,232 +nltk/test/grammar.doctest,sha256=z2ZzpqBjN64pPB7J3WbZcI_QsH5a1TkgdNKN5GsHMLE,1949 +nltk/test/grammartestsuites.doctest,sha256=4eF9lME7iQqaAUMvp3IX0s815yi9MSUgbDrT_hJov2c,3309 +nltk/test/index.doctest,sha256=MuVP-xRyC9Zu_bY7PU8BZekRztycmrD28ngq6q6RonI,2701 +nltk/test/inference.doctest,sha256=8dABBDz575EQIw1qMwVdDCEQClRmaRxxMARqC1CHYBw,18365 +nltk/test/internals.doctest,sha256=dzXAofPbEfck_BZYO1ZHp5NpqQd_Ofz3nGKjP_KlzHE,4283 +nltk/test/japanese.doctest,sha256=79O_D43s3rVCS0Am06pG2hCiNUkT2A7AT1Up_BT82_g,1093 +nltk/test/lm.doctest,sha256=mTo97a-_5wZ1t_G3Hso-LuhluTTtHZrsE5b89dhQXfY,3951 +nltk/test/logic.doctest,sha256=h8numvKfRWQucoEA9HPJ02pxviSb8SdCa02BUvWPL5o,35183 +nltk/test/meteor.doctest,sha256=dcxi8dfWOG6fm-7_VlHkxnaW8d3vEw6TBir0iAsf2Qo,1523 +nltk/test/metrics.doctest,sha256=h9P-WsPkMHmFQDQw98TRwfRBAVhkuLqE3MOSl3hZaJY,11283 +nltk/test/misc.doctest,sha256=upseLcrsXziqxxF0pCmA1Nyx9ovGAuUlpG7w4PK8a1k,3464 +nltk/test/nonmonotonic.doctest,sha256=mG0_JgtIhZh9pD-jqiGAHD7nuQHGClxWhKvffiBPHNM,10370 +nltk/test/paice.doctest,sha256=9KOoUsd6O-ACSMbt3Ras4zS-FE55R4jZ6xh1JKopb3c,1273 +nltk/test/parse.doctest,sha256=fKDMs2TD3qhUgNdwW30CTYcEOl1olf3TRCHsf_i1heY,34936 +nltk/test/portuguese_en.doctest,sha256=voMsH9rhsaI2ETsXYI8T8-ZXKYv6l1-ghv4TMaEDt7c,23121 +nltk/test/portuguese_en_fixt.py,sha256=-66oHXFBbvDKHkwkcOLnpwcn29iUpwUEWrJ-LqSk5FM,130 +nltk/test/probability.doctest,sha256=4BuJPzdy6l8hlRXIJOEMxfzlLl1oaru2ezTV6olNx0U,9244 +nltk/test/probability_fixt.py,sha256=avszs9PHMTVYHHcOXA19-EsTYDahH4VPPPMqv1QkGpE,188 +nltk/test/propbank.doctest,sha256=jDD0XEFjm2-hDA8g_Y7Us3feNLeOgsw8lvDn-tApO0g,6694 +nltk/test/relextract.doctest,sha256=Klf15poDywGJTJlpmpTBnLF5ur-_0tLZC-S93Sox95A,9520 +nltk/test/resolution.doctest,sha256=XJw6Bs4CBYSEEWreyqWTaoTo1ADGwGwRptEsZV8qUH8,8010 +nltk/test/semantics.doctest,sha256=XhmM0qSpAcmqYZu6dV7Veugf3FpuyMxaHEh1t2UEqwI,25190 +nltk/test/sentiment.doctest,sha256=dwARYfcbIn6oaPX7kRAo_ZjjJ_YDowxh3zAgr-16Mak,12229 +nltk/test/sentiwordnet.doctest,sha256=7wIk6gIiYtONvkpNfAUK_xk-jXNVVzIzPlQJ4h2UTrk,1051 +nltk/test/setup_fixt.py,sha256=IQUyYM-mNaVbfsGFvfOdJc0ymAJ-0u5OAZd2cqRgF0s,912 +nltk/test/simple.doctest,sha256=ZF_0SZ5vp7pMfFp6iKf3ZvKkRYLHlxBamC0aaQItSog,2407 +nltk/test/stem.doctest,sha256=XJu6ADeinzu41KgldR5pVuiLdzkmCsoJIXLaSQocTPs,2552 +nltk/test/tag.doctest,sha256=Dl3QKGZi-1uJQwQKenGThEIfOLKKR7k3wghJKAf4GC4,34100 +nltk/test/tokenize.doctest,sha256=9fZOgyZnwOBNiPYS0xRFSPcr8asz18Tc29Et8_nzHs4,20353 +nltk/test/toolbox.doctest,sha256=NfbQ7Q_WFajCTtjxcLSzYkkvr8VVm8SGtqDerv5KBJ4,10323 +nltk/test/translate.doctest,sha256=if9_vzqjIWk0wnok6QSaLO-dry5lt3DLWCTj99VWmf0,8396 +nltk/test/tree.doctest,sha256=_MYclk55SLXJ4zGRu-bbL-oPOThpWc2G4c0cuLkyXXo,47273 +nltk/test/treeprettyprinter.doctest,sha256=yuAL_WWYVV5jCAe0TmzQ9j4N2CSZD9r13_g992DUvkM,9376 +nltk/test/treetransforms.doctest,sha256=UDSeLha6tfB-PN4_eJGQeOMifVhTIY89tho_2cuXDyc,5006 +nltk/test/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nltk/test/unit/__pycache__/__init__.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_aline.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_bllip.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_brill.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_cfd_mutation.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_cfg2chomsky.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_chunk.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_classify.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_collocations.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_concordance.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_corenlp.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_corpora.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_corpus_views.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_data.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_disagreement.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_distance.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_downloader.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_freqdist.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_hmm.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_json2csv_corpus.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_json_serialization.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_metrics.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_naivebayes.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_nombank.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_pl196x.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_pos_tag.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_ribes.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_rte_classify.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_seekable_unicode_stream_reader.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_senna.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_stem.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_tag.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_tgrep.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_tokenize.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_twitter_auth.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_util.cpython-310.pyc,, +nltk/test/unit/__pycache__/test_wordnet.cpython-310.pyc,, +nltk/test/unit/lm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nltk/test/unit/lm/__pycache__/__init__.cpython-310.pyc,, +nltk/test/unit/lm/__pycache__/test_counter.cpython-310.pyc,, +nltk/test/unit/lm/__pycache__/test_models.cpython-310.pyc,, +nltk/test/unit/lm/__pycache__/test_preprocessing.cpython-310.pyc,, +nltk/test/unit/lm/__pycache__/test_vocabulary.cpython-310.pyc,, +nltk/test/unit/lm/test_counter.py,sha256=t1Dgd6b6JFrtkggAzoRXSGfZ1gwNKiD4aM_cBPoFsEM,3891 +nltk/test/unit/lm/test_models.py,sha256=3yVFNUsVC2_VUJexqclnSjInpQe14MuPVq3qsYlr2lc,20160 +nltk/test/unit/lm/test_preprocessing.py,sha256=05lMhOdkglGipiMuJ0gTJwaGweYed0qVGade6VY1uXw,999 +nltk/test/unit/lm/test_vocabulary.py,sha256=1Pd5rfniE0C_Pq5v2k3IKGdug6ZuA4ycbNZKfRpCfuI,5917 +nltk/test/unit/test_aline.py,sha256=u9y3D19sJGY_VE9nSOOON5GMYyTIRGVXMyqveeMypyU,1130 +nltk/test/unit/test_bllip.py,sha256=rm3KrL9F6F49WfXDV42GpuPlvbvsXibTbUXU0pWa-pw,1115 +nltk/test/unit/test_brill.py,sha256=ZTDCN3y2mTZ-dLeL0GwvO3yM6LlcTmIQ1LaNMBtXZyE,1024 +nltk/test/unit/test_cfd_mutation.py,sha256=YutOmFGBizpUzZ61l1cDxIegJah2ntuwKL88C49-WBA,1373 +nltk/test/unit/test_cfg2chomsky.py,sha256=g2wKKjmogJpAaRiLxA0_xw4KLwZa4zvjA-gBMt9z1l0,1726 +nltk/test/unit/test_chunk.py,sha256=xo-ItBtJdBsRIt-rX1rYYkV_ufABgPK4e6HlogRTvWg,2219 +nltk/test/unit/test_classify.py,sha256=4Bv5-rDyrjDGdmRHY_qh4Rq_5VdoghzlJEPB_PCIzQo,1337 +nltk/test/unit/test_collocations.py,sha256=vaiBeImr5dCDOhFPAN8Q4CAUVojJTwNHpodolyIymCU,3690 +nltk/test/unit/test_concordance.py,sha256=91x9LT-875n7OOi3it5O1qr1xKdOA1ufZY3KLH10Iaw,4108 +nltk/test/unit/test_corenlp.py,sha256=doMoc3Drnl3fDFxTaZKVPKq-75RTXOGSOqkI9K_74FQ,58632 +nltk/test/unit/test_corpora.py,sha256=46IA2v_oxDRFQQGa6iGTHiTHAPsDZjIovDSG2432Ubc,9923 +nltk/test/unit/test_corpus_views.py,sha256=mIxoCvqWSfInEQkISPwfZvTG6dTxYh7Bx0kCGC6VsoA,1600 +nltk/test/unit/test_data.py,sha256=y1fXWnIylRrff9fBJBUYZ6xw3T6uwMg_6View-jKcas,390 +nltk/test/unit/test_disagreement.py,sha256=e2JIXrNqCg1YTSh6P2lnGs9YN8KmkWcFD-zcZPsNkjk,4461 +nltk/test/unit/test_distance.py,sha256=DIMhkfn2y6WvsiJRyw1y_T5b_4OHI6wG01eEAt8Cd9Q,5839 +nltk/test/unit/test_downloader.py,sha256=QvpnRVehOfLZVJ-iUH8m5mEHG8w4deKxRhF7IOnjAZM,741 +nltk/test/unit/test_freqdist.py,sha256=I6qkc8zleTMeivGWB0NntBVQDx_tVxthWRwcOB-T0i4,210 +nltk/test/unit/test_hmm.py,sha256=bX7fSFd7k89JCr9VNFr1ZAng4m2KkfuTL_M2TNvA1nU,2285 +nltk/test/unit/test_json2csv_corpus.py,sha256=-BUZfzFHAof4umKGAL-9WKGUBiPFyKzLVZOCmzW-a-g,5888 +nltk/test/unit/test_json_serialization.py,sha256=CfpHkTvY0lF8rMQXQsv_0nSVhDfhxVkqDwTLq26pv5Q,3634 +nltk/test/unit/test_metrics.py,sha256=iK6bLxVi1fVll-2eCmgzE-ubWnQlFeQjP079qdiRP-A,1949 +nltk/test/unit/test_naivebayes.py,sha256=a_tjsQsyvPIsO3mrtmN6knaC9BFwPE7PDNHBSNdhYMc,764 +nltk/test/unit/test_nombank.py,sha256=gIgs6vlEI2NheAh8c6wlJdk6apHmAMmaDZkP8laIvKY,760 +nltk/test/unit/test_pl196x.py,sha256=C41qhbllNBqtVJ9tCFM8mReQqzsdbM7uoMo9hFVHKLg,410 +nltk/test/unit/test_pos_tag.py,sha256=5HkW7hpjZd2270RVSFXECLxXg8jCY2iBViDoDA8O2Qs,2782 +nltk/test/unit/test_ribes.py,sha256=DItkydO5d543kRFYiAebnqudiF2HETHrMAntG3H75jA,5204 +nltk/test/unit/test_rte_classify.py,sha256=oNGw78oedct_VpwelsMVFb7v3bRFepnQWWbHgKp3GBQ,2765 +nltk/test/unit/test_seekable_unicode_stream_reader.py,sha256=XBxkic2HcfqxfTY0XxBBBRNEo5FQrYYQzkg1vywwUA0,2265 +nltk/test/unit/test_senna.py,sha256=fuLdpQO7kG-12rWpGprIOiH9fwxhv1yseNxKtpcUmss,3712 +nltk/test/unit/test_stem.py,sha256=kjtoZlKkgtCZYX8kxyVQIPf5f6QSPzUCLkCJLDvDWFA,6347 +nltk/test/unit/test_tag.py,sha256=h7YztNxvYcx2177MkQrPqPgYR2gL1sdl9YB3ZMKuciw,535 +nltk/test/unit/test_tgrep.py,sha256=elx0roGwZJEOJy7-j7cqwAXvzvldlzYkxo8lHDoKf8E,31708 +nltk/test/unit/test_tokenize.py,sha256=9uQx21Vs5Iv5mBmNyiHqTaOmIecSlD1n9jUY9dF1mBM,30921 +nltk/test/unit/test_twitter_auth.py,sha256=bms9DQ07DwEr53IqMr49qGL9ria_1rEf3aA7xt8oR-A,2509 +nltk/test/unit/test_util.py,sha256=UMUTzBJRSSAdFwp7tZkG7gygQ9gHcrk2IEiuq6XvTRA,1888 +nltk/test/unit/test_wordnet.py,sha256=tZCn_lZVJ8POuehMbAIcgV000vCMwXFJUbdhuPEOSmw,9260 +nltk/test/unit/translate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nltk/test/unit/translate/__pycache__/__init__.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_bleu.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_gdfa.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm1.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm2.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm3.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm4.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm5.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_ibm_model.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_meteor.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_nist.cpython-310.pyc,, +nltk/test/unit/translate/__pycache__/test_stack_decoder.cpython-310.pyc,, +nltk/test/unit/translate/test_bleu.py,sha256=YpYHqzzebFqF8M-x2AjUYeI3YNmRSseJmAf4xoT13XY,15874 +nltk/test/unit/translate/test_gdfa.py,sha256=E4r6o0S2r5rzK7NPFTyDA2a7IAfYzmIhCU4WXB0Wvdo,4770 +nltk/test/unit/translate/test_ibm1.py,sha256=7dGtPK_T9qXGTmB8skOeJ_mNJ2G8VaoYOlqMkhP0fBs,2669 +nltk/test/unit/translate/test_ibm2.py,sha256=fAjggyyMHRzPmpuFdov7dWfwsNM7hK9Z8p_qalCn_lY,3377 +nltk/test/unit/translate/test_ibm3.py,sha256=9PUQNN75ITw_TgzWayxQrMZRNS6pdyNThSw-sTdscP4,4189 +nltk/test/unit/translate/test_ibm4.py,sha256=r1gfJCmXlP0UZhZdrdzc0CcJLZNCn0zrn_BIMH0dVDk,5209 +nltk/test/unit/translate/test_ibm5.py,sha256=20agaTpArhfMcx-Ady0BUXyxayBU_ipPiDTvb8s_1oo,6761 +nltk/test/unit/translate/test_ibm_model.py,sha256=qTMFR4acSkEP5-kta-9B6RymoswEIitV3ljn86riNCo,9676 +nltk/test/unit/translate/test_meteor.py,sha256=sldeMjDkStoMnxBnx1MKDRNBGmcs4Hdu9VmMSzpl1Jo,750 +nltk/test/unit/translate/test_nist.py,sha256=HFfcs5Gq_goyYm-NSqdb_Eet6kClibKNvcr3gdasMmk,1645 +nltk/test/unit/translate/test_stack_decoder.py,sha256=37pm9cVUc-I0789Yt-yUZME9wG6Xrcdzqs0a3lB_8mg,10000 +nltk/test/util.doctest,sha256=BYtTUbvvvKXlM57NfVs-QGe2YqSN3M_Ad40fJNI0go0,1058 +nltk/test/wordnet.doctest,sha256=5VI2tl-FxJ-NCwpMbv5AMvy8vvEoqaLW1hUNu2mzV9A,30528 +nltk/test/wordnet_lch.doctest,sha256=5a80en1DUmUKj9RepGbrpJmPsgvYSFCezlYqh_da9ME,2361 +nltk/test/wsd.doctest,sha256=wa0eAdE0glaOrzkY7VnxIb297tmA46TsuNqdeyHUNR4,3014 +nltk/text.py,sha256=TVmS9X9weLrodN2y_VlHqSYaJi6LOh9U6lpLtyAJU0o,28909 +nltk/tgrep.py,sha256=g3BNjLGCcIrKUnwXRoMd32f-l-8JqF6mUrJfxFKLOUI,37911 +nltk/tokenize/__init__.py,sha256=0qmUpIe6PExgEzV9lXnzeiSfGkHVLoIxVrcZIgBF3FA,5243 +nltk/tokenize/__pycache__/__init__.cpython-310.pyc,, +nltk/tokenize/__pycache__/api.cpython-310.pyc,, +nltk/tokenize/__pycache__/casual.cpython-310.pyc,, +nltk/tokenize/__pycache__/destructive.cpython-310.pyc,, +nltk/tokenize/__pycache__/legality_principle.cpython-310.pyc,, +nltk/tokenize/__pycache__/mwe.cpython-310.pyc,, +nltk/tokenize/__pycache__/nist.cpython-310.pyc,, +nltk/tokenize/__pycache__/punkt.cpython-310.pyc,, +nltk/tokenize/__pycache__/regexp.cpython-310.pyc,, +nltk/tokenize/__pycache__/repp.cpython-310.pyc,, +nltk/tokenize/__pycache__/sexpr.cpython-310.pyc,, +nltk/tokenize/__pycache__/simple.cpython-310.pyc,, +nltk/tokenize/__pycache__/sonority_sequencing.cpython-310.pyc,, +nltk/tokenize/__pycache__/stanford.cpython-310.pyc,, +nltk/tokenize/__pycache__/stanford_segmenter.cpython-310.pyc,, +nltk/tokenize/__pycache__/texttiling.cpython-310.pyc,, +nltk/tokenize/__pycache__/toktok.cpython-310.pyc,, +nltk/tokenize/__pycache__/treebank.cpython-310.pyc,, +nltk/tokenize/__pycache__/util.cpython-310.pyc,, +nltk/tokenize/api.py,sha256=gSsubjy4wvCeoLz2LIToXV7fEbH-ZYuU1ZQKiZAUATc,2357 +nltk/tokenize/casual.py,sha256=38kW21jjDImAPLoCsvoCIHV91U6SqKYKroIzts82f3o,16101 +nltk/tokenize/destructive.py,sha256=JkyvgJ4vbKQ7PKeG5_Ss3LWvYVhCLzrnuNC9mmXO66U,9447 +nltk/tokenize/legality_principle.py,sha256=AIbUBCKtuAvehwOkC0Aa4lGmc8vJoRrNN5hm7wCpfyg,6236 +nltk/tokenize/mwe.py,sha256=lKHLQ-4lwHuhDeg3OJvTnq1xPk2H-Sp78VYL1WRovQ0,4181 +nltk/tokenize/nist.py,sha256=-EXf8gKQOFQmuymmIsslr1RInp4lS20rgXlECRNWbdA,7720 +nltk/tokenize/punkt.py,sha256=KVKagPpsPNYKzGc6a9sPkBMUlHjw5LwZxWr_r4z6hpw,68804 +nltk/tokenize/regexp.py,sha256=4oFnXCBYYwHbl9PLVIRdLIsMoFDKvhUvXUTB0N_Yu10,8331 +nltk/tokenize/repp.py,sha256=0c9syu4dcvFjlFt-bwIIlM2uunhtnAc0rcR_QqfjYJ4,8245 +nltk/tokenize/sexpr.py,sha256=NZazV0MD6VJH30gHaf4Ul4NlJMZ5r5S9ZR8xOROnezw,5302 +nltk/tokenize/simple.py,sha256=Ie7Fhs95ubIHTzb-1Kfnfm0xjRNNsR9Jplu9Ei8v728,5379 +nltk/tokenize/sonority_sequencing.py,sha256=XBiVVAp9f2kCX_bg80r0QI835uF0jtTMUuCUimsqWPQ,7739 +nltk/tokenize/stanford.py,sha256=_p90fpkx6hmIwBtZYsKsXRa0i0OthUZ2kJqgXAkHLRY,3875 +nltk/tokenize/stanford_segmenter.py,sha256=E3Y1HS4Y298DpKFvotlpWf2YMcO7zCs7JrajhugJf1Q,9857 +nltk/tokenize/texttiling.py,sha256=QYDETnGqm4RdimXBZNInhALjzt0Wca8M3mH8aaX1lFU,16943 +nltk/tokenize/toktok.py,sha256=R1eW8VozEtxuwDYjNVn2eWSGSTDvtTKtASMhDvdrdIk,7679 +nltk/tokenize/treebank.py,sha256=ahB5jsQrFeXS-H1Y2Cm5dkkJE91Dva5nWI9Io-Uco2M,16669 +nltk/tokenize/util.py,sha256=iw9hPFtD_oZOVetjcCjh0P6s98u_fjbMDgpxGxn4RGY,10339 +nltk/toolbox.py,sha256=bMXsrkHbgGrP-Ktg2MBLDr-6MJupIlkTRtBhLUwI7tY,18337 +nltk/translate/__init__.py,sha256=a9SOGnQ057m96m5YY8JOP5OIWi353ManWall-EIlxCo,1331 +nltk/translate/__pycache__/__init__.cpython-310.pyc,, +nltk/translate/__pycache__/api.cpython-310.pyc,, +nltk/translate/__pycache__/bleu_score.cpython-310.pyc,, +nltk/translate/__pycache__/chrf_score.cpython-310.pyc,, +nltk/translate/__pycache__/gale_church.cpython-310.pyc,, +nltk/translate/__pycache__/gdfa.cpython-310.pyc,, +nltk/translate/__pycache__/gleu_score.cpython-310.pyc,, +nltk/translate/__pycache__/ibm1.cpython-310.pyc,, +nltk/translate/__pycache__/ibm2.cpython-310.pyc,, +nltk/translate/__pycache__/ibm3.cpython-310.pyc,, +nltk/translate/__pycache__/ibm4.cpython-310.pyc,, +nltk/translate/__pycache__/ibm5.cpython-310.pyc,, +nltk/translate/__pycache__/ibm_model.cpython-310.pyc,, +nltk/translate/__pycache__/meteor_score.cpython-310.pyc,, +nltk/translate/__pycache__/metrics.cpython-310.pyc,, +nltk/translate/__pycache__/nist_score.cpython-310.pyc,, +nltk/translate/__pycache__/phrase_based.cpython-310.pyc,, +nltk/translate/__pycache__/ribes_score.cpython-310.pyc,, +nltk/translate/__pycache__/stack_decoder.cpython-310.pyc,, +nltk/translate/api.py,sha256=SM3sIpzqhMYSFxnOHo1G30y9hkxKrQtE2Ugi1Qln03o,11109 +nltk/translate/bleu_score.py,sha256=YpFNn80ydIWg8iKHgAJ7jgFFkPrkuEMku6m1rC9X_kA,30415 +nltk/translate/chrf_score.py,sha256=C7mEHCk0Jn7QdmxctQImEja-HovmvpPOCfos_UhUH80,8978 +nltk/translate/gale_church.py,sha256=fy4jIbJpZmiJyjVEc_2s1ng6BORp63vG9_HbEToFM6E,8732 +nltk/translate/gdfa.py,sha256=dMWFOM72FZh8d3iuJhyhQZ8KxMfbusSzR0Nr74lyfKQ,6246 +nltk/translate/gleu_score.py,sha256=symY8I6w4SxQDKUN9EyJL6_1Da-4bVT_y-Kd2nnFe38,8831 +nltk/translate/ibm1.py,sha256=MnR2l9vpkyxCAyqGd6ajMUkVxsp5Q6m8Hz--lLzWVMs,9522 +nltk/translate/ibm2.py,sha256=kaGLTJfIrsy3KYj1-72vFYMNtDD8ptJst73mS4NH2nk,12561 +nltk/translate/ibm3.py,sha256=E9aNT2lKkV0I9wwGCvhZPWXk7FUl5VwqV2qPeDUbTck,14154 +nltk/translate/ibm4.py,sha256=1kUdywKq3klNtmId3AHrdYhk-3ptzYlaRw9CH7Jydzk,20765 +nltk/translate/ibm5.py,sha256=H6V--iB46jm9btUyvRSaJ09L2fPCda8xNhbNW9AbtKQ,27957 +nltk/translate/ibm_model.py,sha256=AXy2cgctd8CBV77lih-Yvw2G32xyCO8YMLIGpTugXHU,20504 +nltk/translate/meteor_score.py,sha256=-ZKuCuXxmexx3U-GkpUBvbxNyFEyKZUL16ZIkNnOVYY,17301 +nltk/translate/metrics.py,sha256=qyM4DXkdyRY6OYiHR0M9anTGl129lsHyKIzutfJ-Low,1513 +nltk/translate/nist_score.py,sha256=5n8KyFK_99PcGPGE_l-wkKPbbj9Uy68iE4MZiWEKaxY,8148 +nltk/translate/phrase_based.py,sha256=KBfNqROhEiut1N0C-nFDCivllejA4OUeZ6BLyuMNYTA,7860 +nltk/translate/ribes_score.py,sha256=OGPOh-byCf0R7i2aQYWR-Vh21RJtl9nxQKHEH_YuBto,14027 +nltk/translate/stack_decoder.py,sha256=MuRPezJG4gq2TQsArF0Cm3xn2X6E8FAKSg9BD4Qx4cI,20516 +nltk/tree/__init__.py,sha256=mruLTDldjRtc3viZeVxuUp_QaWW49aRrCSIlFUgln9I,1466 +nltk/tree/__pycache__/__init__.cpython-310.pyc,, +nltk/tree/__pycache__/immutable.cpython-310.pyc,, +nltk/tree/__pycache__/parented.cpython-310.pyc,, +nltk/tree/__pycache__/parsing.cpython-310.pyc,, +nltk/tree/__pycache__/prettyprinter.cpython-310.pyc,, +nltk/tree/__pycache__/probabilistic.cpython-310.pyc,, +nltk/tree/__pycache__/transforms.cpython-310.pyc,, +nltk/tree/__pycache__/tree.cpython-310.pyc,, +nltk/tree/immutable.py,sha256=NOmT_xXNUrk3ct0m7ZMNhqj_kL-91lRdP_ZwTc6bZEc,4178 +nltk/tree/parented.py,sha256=0VrrC0i7eBQuj0Q3H4bGrhWXXzlqvIX-7efnZmXtNTI,23192 +nltk/tree/parsing.py,sha256=IpYHYTBD_VZkp4m0NbC8qmhj9ZiQVI_8x4iJmoe2Hp4,2083 +nltk/tree/prettyprinter.py,sha256=1nybXp3GGP8AF_FxG_v6xrXiQxyvTAt7yseLLZ0QLdI,25586 +nltk/tree/probabilistic.py,sha256=mOnOfXKwE_OeIs4llAStM0vtm4DOqDbWgMOrR42Ni6U,2492 +nltk/tree/transforms.py,sha256=PmprKuO_0pYsSxQPdhnNnJsRmZNOtDYkImwZMoOVfvY,13689 +nltk/tree/tree.py,sha256=euTPBiCu9qVe2_-amu74Ew4SdqxrfIC0itRZkh_r05Q,36500 +nltk/treeprettyprinter.py,sha256=TJU6UHvemo2C_9a0LdTYS6n4ES4QjVf4sQcXq8HapQo,975 +nltk/treetransforms.py,sha256=B_0-bNh4gkTu2dY8E5mt8XQdN97IKFL7LrtOMeASRps,5288 +nltk/twitter/__init__.py,sha256=npOhJzWN63BFf7aPDfUAia76r8uVgVy3ll6ohLKS8fU,819 +nltk/twitter/__pycache__/__init__.cpython-310.pyc,, +nltk/twitter/__pycache__/api.cpython-310.pyc,, +nltk/twitter/__pycache__/common.cpython-310.pyc,, +nltk/twitter/__pycache__/twitter_demo.cpython-310.pyc,, +nltk/twitter/__pycache__/twitterclient.cpython-310.pyc,, +nltk/twitter/__pycache__/util.cpython-310.pyc,, +nltk/twitter/api.py,sha256=yAHSw0JeVRAKJOHvWGqml3U3t7GYaQwhPU3Kf1sRYqw,4692 +nltk/twitter/common.py,sha256=RJbcw3Wvr6YTnQ4YqfWQDN494vKATE_REx-5-9YL5Qg,10120 +nltk/twitter/twitter_demo.py,sha256=dCplglFGLm9I5NgSt3UHVM-NVES2zWWjDnVPRFUONIQ,8309 +nltk/twitter/twitterclient.py,sha256=Vliyz7To9z2IJmUUg6AWcwDS-gNIIF2Yi5fuPfJN0zE,19927 +nltk/twitter/util.py,sha256=Ij7TX3ypt1uYcfo6nSwCbygUpK6_59OhNN3l1ZPefS0,4546 +nltk/util.py,sha256=VijbYZYpM5wjWEsgI0uu-GnR-zJMulfBoDgeJI9QpGw,42026 +nltk/wsd.py,sha256=5Ie3V_RtWQGRsIeL0rMnGGA_9KEGD_9l9GkbkFyYQoA,1789 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/REQUESTED b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/entry_points.txt new file mode 100644 index 0000000..65a3a33 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/entry_points.txt @@ -0,0 +1,3 @@ + +[console_scripts] +nltk=nltk.cli:cli diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/top_level.txt new file mode 100644 index 0000000..8469296 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk-3.8.1.dist-info/top_level.txt @@ -0,0 +1 @@ +nltk diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/VERSION b/.pythonlibs/lib/python3.10/site-packages/nltk/VERSION new file mode 100644 index 0000000..f280719 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/VERSION @@ -0,0 +1 @@ +3.8.1 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/__init__.py new file mode 100644 index 0000000..062d3d4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/__init__.py @@ -0,0 +1,209 @@ +# Natural Language Toolkit (NLTK) +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +The Natural Language Toolkit (NLTK) is an open source Python library +for Natural Language Processing. A free online book is available. +(If you use the library for academic research, please cite the book.) + +Steven Bird, Ewan Klein, and Edward Loper (2009). +Natural Language Processing with Python. O'Reilly Media Inc. +https://www.nltk.org/book/ + +isort:skip_file +""" + +import os + +# ////////////////////////////////////////////////////// +# Metadata +# ////////////////////////////////////////////////////// + +# Version. For each new release, the version number should be updated +# in the file VERSION. +try: + # If a VERSION file exists, use it! + version_file = os.path.join(os.path.dirname(__file__), "VERSION") + with open(version_file) as infile: + __version__ = infile.read().strip() +except NameError: + __version__ = "unknown (running code interactively?)" +except OSError as ex: + __version__ = "unknown (%s)" % ex + +if __doc__ is not None: # fix for the ``python -OO`` + __doc__ += "\n@version: " + __version__ + + +# Copyright notice +__copyright__ = """\ +Copyright (C) 2001-2023 NLTK Project. + +Distributed and Licensed under the Apache License, Version 2.0, +which is included by reference. +""" + +__license__ = "Apache License, Version 2.0" +# Description of the toolkit, keywords, and the project's primary URL. +__longdescr__ = """\ +The Natural Language Toolkit (NLTK) is a Python package for +natural language processing. NLTK requires Python 3.7, 3.8, 3.9, 3.10 or 3.11.""" +__keywords__ = [ + "NLP", + "CL", + "natural language processing", + "computational linguistics", + "parsing", + "tagging", + "tokenizing", + "syntax", + "linguistics", + "language", + "natural language", + "text analytics", +] +__url__ = "https://www.nltk.org/" + +# Maintainer, contributors, etc. +__maintainer__ = "NLTK Team" +__maintainer_email__ = "nltk.team@gmail.com" +__author__ = __maintainer__ +__author_email__ = __maintainer_email__ + +# "Trove" classifiers for Python Package Index. +__classifiers__ = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "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", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Human Machine Interfaces", + "Topic :: Scientific/Engineering :: Information Analysis", + "Topic :: Text Processing", + "Topic :: Text Processing :: Filters", + "Topic :: Text Processing :: General", + "Topic :: Text Processing :: Indexing", + "Topic :: Text Processing :: Linguistic", +] + +from nltk.internals import config_java + +# support numpy from pypy +try: + import numpypy +except ImportError: + pass + +# Override missing methods on environments where it cannot be used like GAE. +import subprocess + +if not hasattr(subprocess, "PIPE"): + + def _fake_PIPE(*args, **kwargs): + raise NotImplementedError("subprocess.PIPE is not supported.") + + subprocess.PIPE = _fake_PIPE +if not hasattr(subprocess, "Popen"): + + def _fake_Popen(*args, **kwargs): + raise NotImplementedError("subprocess.Popen is not supported.") + + subprocess.Popen = _fake_Popen + +########################################################### +# TOP-LEVEL MODULES +########################################################### + +# Import top-level functionality into top-level namespace + +from nltk.collocations import * +from nltk.decorators import decorator, memoize +from nltk.featstruct import * +from nltk.grammar import * +from nltk.probability import * +from nltk.text import * +from nltk.util import * +from nltk.jsontags import * + +########################################################### +# PACKAGES +########################################################### + +from nltk.chunk import * +from nltk.classify import * +from nltk.inference import * +from nltk.metrics import * +from nltk.parse import * +from nltk.tag import * +from nltk.tokenize import * +from nltk.translate import * +from nltk.tree import * +from nltk.sem import * +from nltk.stem import * + +# Packages which can be lazily imported +# (a) we don't import * +# (b) they're slow to import or have run-time dependencies +# that can safely fail at run time + +from nltk import lazyimport + +app = lazyimport.LazyModule("app", locals(), globals()) +chat = lazyimport.LazyModule("chat", locals(), globals()) +corpus = lazyimport.LazyModule("corpus", locals(), globals()) +draw = lazyimport.LazyModule("draw", locals(), globals()) +toolbox = lazyimport.LazyModule("toolbox", locals(), globals()) + +# Optional loading + +try: + import numpy +except ImportError: + pass +else: + from nltk import cluster + +from nltk.downloader import download, download_shell + +try: + import tkinter +except ImportError: + pass +else: + try: + from nltk.downloader import download_gui + except RuntimeError as e: + import warnings + + warnings.warn( + "Corpus downloader GUI not loaded " + "(RuntimeError during import: %s)" % str(e) + ) + +# explicitly import all top-level modules (ensuring +# they override the same names inadvertently imported +# from a subpackage) + +from nltk import ccg, chunk, classify, collocations +from nltk import data, featstruct, grammar, help, inference, metrics +from nltk import misc, parse, probability, sem, stem, wsd +from nltk import tag, tbl, text, tokenize, translate, tree, util + + +# FIXME: override any accidentally imported demo, see https://github.com/nltk/nltk/issues/2116 +def demo(): + print("To run the demo code for a module, type nltk.module.demo()") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..142f461 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/book.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/book.cpython-310.pyc new file mode 100644 index 0000000..9a464e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/book.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/cli.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/cli.cpython-310.pyc new file mode 100644 index 0000000..99b1767 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/cli.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collections.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collections.cpython-310.pyc new file mode 100644 index 0000000..351e977 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collections.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collocations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collocations.cpython-310.pyc new file mode 100644 index 0000000..65a7be6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/collocations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/compat.cpython-310.pyc new file mode 100644 index 0000000..9d9796e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/data.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/data.cpython-310.pyc new file mode 100644 index 0000000..acbfef9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/data.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/decorators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/decorators.cpython-310.pyc new file mode 100644 index 0000000..16b6e1e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/decorators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/downloader.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/downloader.cpython-310.pyc new file mode 100644 index 0000000..0c9f783 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/downloader.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/featstruct.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/featstruct.cpython-310.pyc new file mode 100644 index 0000000..bd0cecd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/featstruct.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/grammar.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/grammar.cpython-310.pyc new file mode 100644 index 0000000..9ece769 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/grammar.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/help.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/help.cpython-310.pyc new file mode 100644 index 0000000..228b02c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/help.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/internals.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/internals.cpython-310.pyc new file mode 100644 index 0000000..3ededda Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/internals.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/jsontags.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/jsontags.cpython-310.pyc new file mode 100644 index 0000000..df1531b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/jsontags.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/langnames.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/langnames.cpython-310.pyc new file mode 100644 index 0000000..c1205f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/langnames.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/lazyimport.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/lazyimport.cpython-310.pyc new file mode 100644 index 0000000..aa09868 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/lazyimport.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/probability.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/probability.cpython-310.pyc new file mode 100644 index 0000000..bdac58e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/probability.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/text.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/text.cpython-310.pyc new file mode 100644 index 0000000..6d42304 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/text.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/tgrep.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/tgrep.cpython-310.pyc new file mode 100644 index 0000000..4665ddb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/tgrep.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/toolbox.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/toolbox.cpython-310.pyc new file mode 100644 index 0000000..f031919 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/toolbox.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treeprettyprinter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treeprettyprinter.cpython-310.pyc new file mode 100644 index 0000000..13948cd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treeprettyprinter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treetransforms.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treetransforms.cpython-310.pyc new file mode 100644 index 0000000..4ee4f04 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/treetransforms.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..458b753 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/wsd.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/wsd.cpython-310.pyc new file mode 100644 index 0000000..d5295c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/__pycache__/wsd.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__init__.py new file mode 100644 index 0000000..bf41ae9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__init__.py @@ -0,0 +1,47 @@ +# Natural Language Toolkit: Applications package +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Interactive NLTK Applications: + +chartparser: Chart Parser +chunkparser: Regular-Expression Chunk Parser +collocations: Find collocations in text +concordance: Part-of-speech concordancer +nemo: Finding (and Replacing) Nemo regular expression tool +rdparser: Recursive Descent Parser +srparser: Shift-Reduce Parser +wordnet: WordNet Browser +""" + + +# Import Tkinter-based modules if Tkinter is installed +try: + import tkinter +except ImportError: + import warnings + + warnings.warn("nltk.app package not loaded (please install Tkinter library).") +else: + from nltk.app.chartparser_app import app as chartparser + from nltk.app.chunkparser_app import app as chunkparser + from nltk.app.collocations_app import app as collocations + from nltk.app.concordance_app import app as concordance + from nltk.app.nemo_app import app as nemo + from nltk.app.rdparser_app import app as rdparser + from nltk.app.srparser_app import app as srparser + from nltk.app.wordnet_app import app as wordnet + + try: + from matplotlib import pylab + except ImportError: + import warnings + + warnings.warn("nltk.app.wordfreq not loaded (requires the matplotlib library).") + else: + from nltk.app.wordfreq_app import app as wordfreq diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..28ac72a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chartparser_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chartparser_app.cpython-310.pyc new file mode 100644 index 0000000..f4ff65c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chartparser_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chunkparser_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chunkparser_app.cpython-310.pyc new file mode 100644 index 0000000..1adaa05 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/chunkparser_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/collocations_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/collocations_app.cpython-310.pyc new file mode 100644 index 0000000..11ebd95 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/collocations_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/concordance_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/concordance_app.cpython-310.pyc new file mode 100644 index 0000000..67646e0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/concordance_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/nemo_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/nemo_app.cpython-310.pyc new file mode 100644 index 0000000..4ff8af8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/nemo_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/rdparser_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/rdparser_app.cpython-310.pyc new file mode 100644 index 0000000..5729da9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/rdparser_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/srparser_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/srparser_app.cpython-310.pyc new file mode 100644 index 0000000..d0abc79 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/srparser_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordfreq_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordfreq_app.cpython-310.pyc new file mode 100644 index 0000000..39b61fa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordfreq_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordnet_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordnet_app.cpython-310.pyc new file mode 100644 index 0000000..24b6545 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/app/__pycache__/wordnet_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/chartparser_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/chartparser_app.py new file mode 100644 index 0000000..f1bfbde --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/chartparser_app.py @@ -0,0 +1,2569 @@ +# Natural Language Toolkit: Chart Parser Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Jean Mark Gawron +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +A graphical tool for exploring chart parsing. + +Chart parsing is a flexible parsing algorithm that uses a data +structure called a "chart" to record hypotheses about syntactic +constituents. Each hypothesis is represented by a single "edge" on +the chart. A set of "chart rules" determine when new edges can be +added to the chart. This set of rules controls the overall behavior +of the parser (e.g. whether it parses top-down or bottom-up). + +The chart parsing tool demonstrates the process of parsing a single +sentence, with a given grammar and lexicon. Its display is divided +into three sections: the bottom section displays the chart; the middle +section displays the sentence; and the top section displays the +partial syntax tree corresponding to the selected edge. Buttons along +the bottom of the window are used to control the execution of the +algorithm. + +The chart parsing tool allows for flexible control of the parsing +algorithm. At each step of the algorithm, you can select which rule +or strategy you wish to apply. This allows you to experiment with +mixing different strategies (e.g. top-down and bottom-up). You can +exercise fine-grained control over the algorithm by selecting which +edge you wish to apply a rule to. +""" + +# At some point, we should rewrite this tool to use the new canvas +# widget system. + + +import os.path +import pickle +from tkinter import ( + Button, + Canvas, + Checkbutton, + Frame, + IntVar, + Label, + Menu, + Scrollbar, + Tk, + Toplevel, +) +from tkinter.filedialog import askopenfilename, asksaveasfilename +from tkinter.font import Font +from tkinter.messagebox import showerror, showinfo + +from nltk.draw import CFGEditor, TreeSegmentWidget, tree_to_treesegment +from nltk.draw.util import ( + CanvasFrame, + ColorizedList, + EntryDialog, + MutableOptionMenu, + ShowText, + SymbolWidget, +) +from nltk.grammar import CFG, Nonterminal +from nltk.parse.chart import ( + BottomUpPredictCombineRule, + BottomUpPredictRule, + Chart, + LeafEdge, + LeafInitRule, + SingleEdgeFundamentalRule, + SteppingChartParser, + TopDownInitRule, + TopDownPredictRule, + TreeEdge, +) +from nltk.tree import Tree +from nltk.util import in_idle + +# Known bug: ChartView doesn't handle edges generated by epsilon +# productions (e.g., [Production: PP -> ]) very well. + +####################################################################### +# Edge List +####################################################################### + + +class EdgeList(ColorizedList): + ARROW = SymbolWidget.SYMBOLS["rightarrow"] + + def _init_colortags(self, textwidget, options): + textwidget.tag_config("terminal", foreground="#006000") + textwidget.tag_config("arrow", font="symbol", underline="0") + textwidget.tag_config("dot", foreground="#000000") + textwidget.tag_config( + "nonterminal", foreground="blue", font=("helvetica", -12, "bold") + ) + + def _item_repr(self, item): + contents = [] + contents.append(("%s\t" % item.lhs(), "nonterminal")) + contents.append((self.ARROW, "arrow")) + for i, elt in enumerate(item.rhs()): + if i == item.dot(): + contents.append((" *", "dot")) + if isinstance(elt, Nonterminal): + contents.append((" %s" % elt.symbol(), "nonterminal")) + else: + contents.append((" %r" % elt, "terminal")) + if item.is_complete(): + contents.append((" *", "dot")) + return contents + + +####################################################################### +# Chart Matrix View +####################################################################### + + +class ChartMatrixView: + """ + A view of a chart that displays the contents of the corresponding matrix. + """ + + def __init__( + self, parent, chart, toplevel=True, title="Chart Matrix", show_numedges=False + ): + self._chart = chart + self._cells = [] + self._marks = [] + + self._selected_cell = None + + if toplevel: + self._root = Toplevel(parent) + self._root.title(title) + self._root.bind("", self.destroy) + self._init_quit(self._root) + else: + self._root = Frame(parent) + + self._init_matrix(self._root) + self._init_list(self._root) + if show_numedges: + self._init_numedges(self._root) + else: + self._numedges_label = None + + self._callbacks = {} + + self._num_edges = 0 + + self.draw() + + def _init_quit(self, root): + quit = Button(root, text="Quit", command=self.destroy) + quit.pack(side="bottom", expand=0, fill="none") + + def _init_matrix(self, root): + cframe = Frame(root, border=2, relief="sunken") + cframe.pack(expand=0, fill="none", padx=1, pady=3, side="top") + self._canvas = Canvas(cframe, width=200, height=200, background="white") + self._canvas.pack(expand=0, fill="none") + + def _init_numedges(self, root): + self._numedges_label = Label(root, text="0 edges") + self._numedges_label.pack(expand=0, fill="none", side="top") + + def _init_list(self, root): + self._list = EdgeList(root, [], width=20, height=5) + self._list.pack(side="top", expand=1, fill="both", pady=3) + + def cb(edge, self=self): + self._fire_callbacks("select", edge) + + self._list.add_callback("select", cb) + self._list.focus() + + def destroy(self, *e): + if self._root is None: + return + try: + self._root.destroy() + except: + pass + self._root = None + + def set_chart(self, chart): + if chart is not self._chart: + self._chart = chart + self._num_edges = 0 + self.draw() + + def update(self): + if self._root is None: + return + + # Count the edges in each cell + N = len(self._cells) + cell_edges = [[0 for i in range(N)] for j in range(N)] + for edge in self._chart: + cell_edges[edge.start()][edge.end()] += 1 + + # Color the cells correspondingly. + for i in range(N): + for j in range(i, N): + if cell_edges[i][j] == 0: + color = "gray20" + else: + color = "#00{:02x}{:02x}".format( + min(255, 50 + 128 * cell_edges[i][j] / 10), + max(0, 128 - 128 * cell_edges[i][j] / 10), + ) + cell_tag = self._cells[i][j] + self._canvas.itemconfig(cell_tag, fill=color) + if (i, j) == self._selected_cell: + self._canvas.itemconfig(cell_tag, outline="#00ffff", width=3) + self._canvas.tag_raise(cell_tag) + else: + self._canvas.itemconfig(cell_tag, outline="black", width=1) + + # Update the edge list. + edges = list(self._chart.select(span=self._selected_cell)) + self._list.set(edges) + + # Update our edge count. + self._num_edges = self._chart.num_edges() + if self._numedges_label is not None: + self._numedges_label["text"] = "%d edges" % self._num_edges + + def activate(self): + self._canvas.itemconfig("inactivebox", state="hidden") + self.update() + + def inactivate(self): + self._canvas.itemconfig("inactivebox", state="normal") + self.update() + + def add_callback(self, event, func): + self._callbacks.setdefault(event, {})[func] = 1 + + def remove_callback(self, event, func=None): + if func is None: + del self._callbacks[event] + else: + try: + del self._callbacks[event][func] + except: + pass + + def _fire_callbacks(self, event, *args): + if event not in self._callbacks: + return + for cb_func in list(self._callbacks[event].keys()): + cb_func(*args) + + def select_cell(self, i, j): + if self._root is None: + return + + # If the cell is already selected (and the chart contents + # haven't changed), then do nothing. + if (i, j) == self._selected_cell and self._chart.num_edges() == self._num_edges: + return + + self._selected_cell = (i, j) + self.update() + + # Fire the callback. + self._fire_callbacks("select_cell", i, j) + + def deselect_cell(self): + if self._root is None: + return + self._selected_cell = None + self._list.set([]) + self.update() + + def _click_cell(self, i, j): + if self._selected_cell == (i, j): + self.deselect_cell() + else: + self.select_cell(i, j) + + def view_edge(self, edge): + self.select_cell(*edge.span()) + self._list.view(edge) + + def mark_edge(self, edge): + if self._root is None: + return + self.select_cell(*edge.span()) + self._list.mark(edge) + + def unmark_edge(self, edge=None): + if self._root is None: + return + self._list.unmark(edge) + + def markonly_edge(self, edge): + if self._root is None: + return + self.select_cell(*edge.span()) + self._list.markonly(edge) + + def draw(self): + if self._root is None: + return + LEFT_MARGIN = BOT_MARGIN = 15 + TOP_MARGIN = 5 + c = self._canvas + c.delete("all") + N = self._chart.num_leaves() + 1 + dx = (int(c["width"]) - LEFT_MARGIN) / N + dy = (int(c["height"]) - TOP_MARGIN - BOT_MARGIN) / N + + c.delete("all") + + # Labels and dotted lines + for i in range(N): + c.create_text( + LEFT_MARGIN - 2, i * dy + dy / 2 + TOP_MARGIN, text=repr(i), anchor="e" + ) + c.create_text( + i * dx + dx / 2 + LEFT_MARGIN, + N * dy + TOP_MARGIN + 1, + text=repr(i), + anchor="n", + ) + c.create_line( + LEFT_MARGIN, + dy * (i + 1) + TOP_MARGIN, + dx * N + LEFT_MARGIN, + dy * (i + 1) + TOP_MARGIN, + dash=".", + ) + c.create_line( + dx * i + LEFT_MARGIN, + TOP_MARGIN, + dx * i + LEFT_MARGIN, + dy * N + TOP_MARGIN, + dash=".", + ) + + # A box around the whole thing + c.create_rectangle( + LEFT_MARGIN, TOP_MARGIN, LEFT_MARGIN + dx * N, dy * N + TOP_MARGIN, width=2 + ) + + # Cells + self._cells = [[None for i in range(N)] for j in range(N)] + for i in range(N): + for j in range(i, N): + t = c.create_rectangle( + j * dx + LEFT_MARGIN, + i * dy + TOP_MARGIN, + (j + 1) * dx + LEFT_MARGIN, + (i + 1) * dy + TOP_MARGIN, + fill="gray20", + ) + self._cells[i][j] = t + + def cb(event, self=self, i=i, j=j): + self._click_cell(i, j) + + c.tag_bind(t, "", cb) + + # Inactive box + xmax, ymax = int(c["width"]), int(c["height"]) + t = c.create_rectangle( + -100, + -100, + xmax + 100, + ymax + 100, + fill="gray50", + state="hidden", + tag="inactivebox", + ) + c.tag_lower(t) + + # Update the cells. + self.update() + + def pack(self, *args, **kwargs): + self._root.pack(*args, **kwargs) + + +####################################################################### +# Chart Results View +####################################################################### + + +class ChartResultsView: + def __init__(self, parent, chart, grammar, toplevel=True): + self._chart = chart + self._grammar = grammar + self._trees = [] + self._y = 10 + self._treewidgets = [] + self._selection = None + self._selectbox = None + + if toplevel: + self._root = Toplevel(parent) + self._root.title("Chart Parser Application: Results") + self._root.bind("", self.destroy) + else: + self._root = Frame(parent) + + # Buttons + if toplevel: + buttons = Frame(self._root) + buttons.pack(side="bottom", expand=0, fill="x") + Button(buttons, text="Quit", command=self.destroy).pack(side="right") + Button(buttons, text="Print All", command=self.print_all).pack(side="left") + Button(buttons, text="Print Selection", command=self.print_selection).pack( + side="left" + ) + + # Canvas frame. + self._cframe = CanvasFrame(self._root, closeenough=20) + self._cframe.pack(side="top", expand=1, fill="both") + + # Initial update + self.update() + + def update(self, edge=None): + if self._root is None: + return + # If the edge isn't a parse edge, do nothing. + if edge is not None: + if edge.lhs() != self._grammar.start(): + return + if edge.span() != (0, self._chart.num_leaves()): + return + + for parse in self._chart.parses(self._grammar.start()): + if parse not in self._trees: + self._add(parse) + + def _add(self, parse): + # Add it to self._trees. + self._trees.append(parse) + + # Create a widget for it. + c = self._cframe.canvas() + treewidget = tree_to_treesegment(c, parse) + + # Add it to the canvas frame. + self._treewidgets.append(treewidget) + self._cframe.add_widget(treewidget, 10, self._y) + + # Register callbacks. + treewidget.bind_click(self._click) + + # Update y. + self._y = treewidget.bbox()[3] + 10 + + def _click(self, widget): + c = self._cframe.canvas() + if self._selection is not None: + c.delete(self._selectbox) + self._selection = widget + (x1, y1, x2, y2) = widget.bbox() + self._selectbox = c.create_rectangle(x1, y1, x2, y2, width=2, outline="#088") + + def _color(self, treewidget, color): + treewidget.label()["color"] = color + for child in treewidget.subtrees(): + if isinstance(child, TreeSegmentWidget): + self._color(child, color) + else: + child["color"] = color + + def print_all(self, *e): + if self._root is None: + return + self._cframe.print_to_file() + + def print_selection(self, *e): + if self._root is None: + return + if self._selection is None: + showerror("Print Error", "No tree selected") + else: + c = self._cframe.canvas() + for widget in self._treewidgets: + if widget is not self._selection: + self._cframe.destroy_widget(widget) + c.delete(self._selectbox) + (x1, y1, x2, y2) = self._selection.bbox() + self._selection.move(10 - x1, 10 - y1) + c["scrollregion"] = f"0 0 {x2 - x1 + 20} {y2 - y1 + 20}" + self._cframe.print_to_file() + + # Restore our state. + self._treewidgets = [self._selection] + self.clear() + self.update() + + def clear(self): + if self._root is None: + return + for treewidget in self._treewidgets: + self._cframe.destroy_widget(treewidget) + self._trees = [] + self._treewidgets = [] + if self._selection is not None: + self._cframe.canvas().delete(self._selectbox) + self._selection = None + self._y = 10 + + def set_chart(self, chart): + self.clear() + self._chart = chart + self.update() + + def set_grammar(self, grammar): + self.clear() + self._grammar = grammar + self.update() + + def destroy(self, *e): + if self._root is None: + return + try: + self._root.destroy() + except: + pass + self._root = None + + def pack(self, *args, **kwargs): + self._root.pack(*args, **kwargs) + + +####################################################################### +# Chart Comparer +####################################################################### + + +class ChartComparer: + """ + + :ivar _root: The root window + + :ivar _charts: A dictionary mapping names to charts. When + charts are loaded, they are added to this dictionary. + + :ivar _left_chart: The left ``Chart``. + :ivar _left_name: The name ``_left_chart`` (derived from filename) + :ivar _left_matrix: The ``ChartMatrixView`` for ``_left_chart`` + :ivar _left_selector: The drop-down ``MutableOptionsMenu`` used + to select ``_left_chart``. + + :ivar _right_chart: The right ``Chart``. + :ivar _right_name: The name ``_right_chart`` (derived from filename) + :ivar _right_matrix: The ``ChartMatrixView`` for ``_right_chart`` + :ivar _right_selector: The drop-down ``MutableOptionsMenu`` used + to select ``_right_chart``. + + :ivar _out_chart: The out ``Chart``. + :ivar _out_name: The name ``_out_chart`` (derived from filename) + :ivar _out_matrix: The ``ChartMatrixView`` for ``_out_chart`` + :ivar _out_label: The label for ``_out_chart``. + + :ivar _op_label: A Label containing the most recent operation. + """ + + _OPSYMBOL = { + "-": "-", + "and": SymbolWidget.SYMBOLS["intersection"], + "or": SymbolWidget.SYMBOLS["union"], + } + + def __init__(self, *chart_filenames): + # This chart is displayed when we don't have a value (eg + # before any chart is loaded). + faketok = [""] * 8 + self._emptychart = Chart(faketok) + + # The left & right charts start out empty. + self._left_name = "None" + self._right_name = "None" + self._left_chart = self._emptychart + self._right_chart = self._emptychart + + # The charts that have been loaded. + self._charts = {"None": self._emptychart} + + # The output chart. + self._out_chart = self._emptychart + + # The most recent operation + self._operator = None + + # Set up the root window. + self._root = Tk() + self._root.title("Chart Comparison") + self._root.bind("", self.destroy) + self._root.bind("", self.destroy) + + # Initialize all widgets, etc. + self._init_menubar(self._root) + self._init_chartviews(self._root) + self._init_divider(self._root) + self._init_buttons(self._root) + self._init_bindings(self._root) + + # Load any specified charts. + for filename in chart_filenames: + self.load_chart(filename) + + def destroy(self, *e): + if self._root is None: + return + try: + self._root.destroy() + except: + pass + self._root = None + + def mainloop(self, *args, **kwargs): + return + self._root.mainloop(*args, **kwargs) + + # //////////////////////////////////////////////////////////// + # Initialization + # //////////////////////////////////////////////////////////// + + def _init_menubar(self, root): + menubar = Menu(root) + + # File menu + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Load Chart", + accelerator="Ctrl-o", + underline=0, + command=self.load_chart_dialog, + ) + filemenu.add_command( + label="Save Output", + accelerator="Ctrl-s", + underline=0, + command=self.save_chart_dialog, + ) + filemenu.add_separator() + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + # Compare menu + opmenu = Menu(menubar, tearoff=0) + opmenu.add_command( + label="Intersection", command=self._intersection, accelerator="+" + ) + opmenu.add_command(label="Union", command=self._union, accelerator="*") + opmenu.add_command( + label="Difference", command=self._difference, accelerator="-" + ) + opmenu.add_separator() + opmenu.add_command(label="Swap Charts", command=self._swapcharts) + menubar.add_cascade(label="Compare", underline=0, menu=opmenu) + + # Add the menu + self._root.config(menu=menubar) + + def _init_divider(self, root): + divider = Frame(root, border=2, relief="sunken") + divider.pack(side="top", fill="x", ipady=2) + + def _init_chartviews(self, root): + opfont = ("symbol", -36) # Font for operator. + eqfont = ("helvetica", -36) # Font for equals sign. + + frame = Frame(root, background="#c0c0c0") + frame.pack(side="top", expand=1, fill="both") + + # The left matrix. + cv1_frame = Frame(frame, border=3, relief="groove") + cv1_frame.pack(side="left", padx=8, pady=7, expand=1, fill="both") + self._left_selector = MutableOptionMenu( + cv1_frame, list(self._charts.keys()), command=self._select_left + ) + self._left_selector.pack(side="top", pady=5, fill="x") + self._left_matrix = ChartMatrixView( + cv1_frame, self._emptychart, toplevel=False, show_numedges=True + ) + self._left_matrix.pack(side="bottom", padx=5, pady=5, expand=1, fill="both") + self._left_matrix.add_callback("select", self.select_edge) + self._left_matrix.add_callback("select_cell", self.select_cell) + self._left_matrix.inactivate() + + # The operator. + self._op_label = Label( + frame, text=" ", width=3, background="#c0c0c0", font=opfont + ) + self._op_label.pack(side="left", padx=5, pady=5) + + # The right matrix. + cv2_frame = Frame(frame, border=3, relief="groove") + cv2_frame.pack(side="left", padx=8, pady=7, expand=1, fill="both") + self._right_selector = MutableOptionMenu( + cv2_frame, list(self._charts.keys()), command=self._select_right + ) + self._right_selector.pack(side="top", pady=5, fill="x") + self._right_matrix = ChartMatrixView( + cv2_frame, self._emptychart, toplevel=False, show_numedges=True + ) + self._right_matrix.pack(side="bottom", padx=5, pady=5, expand=1, fill="both") + self._right_matrix.add_callback("select", self.select_edge) + self._right_matrix.add_callback("select_cell", self.select_cell) + self._right_matrix.inactivate() + + # The equals sign + Label(frame, text="=", width=3, background="#c0c0c0", font=eqfont).pack( + side="left", padx=5, pady=5 + ) + + # The output matrix. + out_frame = Frame(frame, border=3, relief="groove") + out_frame.pack(side="left", padx=8, pady=7, expand=1, fill="both") + self._out_label = Label(out_frame, text="Output") + self._out_label.pack(side="top", pady=9) + self._out_matrix = ChartMatrixView( + out_frame, self._emptychart, toplevel=False, show_numedges=True + ) + self._out_matrix.pack(side="bottom", padx=5, pady=5, expand=1, fill="both") + self._out_matrix.add_callback("select", self.select_edge) + self._out_matrix.add_callback("select_cell", self.select_cell) + self._out_matrix.inactivate() + + def _init_buttons(self, root): + buttons = Frame(root) + buttons.pack(side="bottom", pady=5, fill="x", expand=0) + Button(buttons, text="Intersection", command=self._intersection).pack( + side="left" + ) + Button(buttons, text="Union", command=self._union).pack(side="left") + Button(buttons, text="Difference", command=self._difference).pack(side="left") + Frame(buttons, width=20).pack(side="left") + Button(buttons, text="Swap Charts", command=self._swapcharts).pack(side="left") + + Button(buttons, text="Detach Output", command=self._detach_out).pack( + side="right" + ) + + def _init_bindings(self, root): + # root.bind('', self.save_chart) + root.bind("", self.load_chart_dialog) + # root.bind('', self.reset) + + # //////////////////////////////////////////////////////////// + # Input Handling + # //////////////////////////////////////////////////////////// + + def _select_left(self, name): + self._left_name = name + self._left_chart = self._charts[name] + self._left_matrix.set_chart(self._left_chart) + if name == "None": + self._left_matrix.inactivate() + self._apply_op() + + def _select_right(self, name): + self._right_name = name + self._right_chart = self._charts[name] + self._right_matrix.set_chart(self._right_chart) + if name == "None": + self._right_matrix.inactivate() + self._apply_op() + + def _apply_op(self): + if self._operator == "-": + self._difference() + elif self._operator == "or": + self._union() + elif self._operator == "and": + self._intersection() + + # //////////////////////////////////////////////////////////// + # File + # //////////////////////////////////////////////////////////// + CHART_FILE_TYPES = [("Pickle file", ".pickle"), ("All files", "*")] + + def save_chart_dialog(self, *args): + filename = asksaveasfilename( + filetypes=self.CHART_FILE_TYPES, defaultextension=".pickle" + ) + if not filename: + return + try: + with open(filename, "wb") as outfile: + pickle.dump(self._out_chart, outfile) + except Exception as e: + showerror("Error Saving Chart", f"Unable to open file: {filename!r}\n{e}") + + def load_chart_dialog(self, *args): + filename = askopenfilename( + filetypes=self.CHART_FILE_TYPES, defaultextension=".pickle" + ) + if not filename: + return + try: + self.load_chart(filename) + except Exception as e: + showerror("Error Loading Chart", f"Unable to open file: {filename!r}\n{e}") + + def load_chart(self, filename): + with open(filename, "rb") as infile: + chart = pickle.load(infile) + name = os.path.basename(filename) + if name.endswith(".pickle"): + name = name[:-7] + if name.endswith(".chart"): + name = name[:-6] + self._charts[name] = chart + self._left_selector.add(name) + self._right_selector.add(name) + + # If either left_matrix or right_matrix is empty, then + # display the new chart. + if self._left_chart is self._emptychart: + self._left_selector.set(name) + elif self._right_chart is self._emptychart: + self._right_selector.set(name) + + def _update_chartviews(self): + self._left_matrix.update() + self._right_matrix.update() + self._out_matrix.update() + + # //////////////////////////////////////////////////////////// + # Selection + # //////////////////////////////////////////////////////////// + + def select_edge(self, edge): + if edge in self._left_chart: + self._left_matrix.markonly_edge(edge) + else: + self._left_matrix.unmark_edge() + if edge in self._right_chart: + self._right_matrix.markonly_edge(edge) + else: + self._right_matrix.unmark_edge() + if edge in self._out_chart: + self._out_matrix.markonly_edge(edge) + else: + self._out_matrix.unmark_edge() + + def select_cell(self, i, j): + self._left_matrix.select_cell(i, j) + self._right_matrix.select_cell(i, j) + self._out_matrix.select_cell(i, j) + + # //////////////////////////////////////////////////////////// + # Operations + # //////////////////////////////////////////////////////////// + + def _difference(self): + if not self._checkcompat(): + return + + out_chart = Chart(self._left_chart.tokens()) + for edge in self._left_chart: + if edge not in self._right_chart: + out_chart.insert(edge, []) + + self._update("-", out_chart) + + def _intersection(self): + if not self._checkcompat(): + return + + out_chart = Chart(self._left_chart.tokens()) + for edge in self._left_chart: + if edge in self._right_chart: + out_chart.insert(edge, []) + + self._update("and", out_chart) + + def _union(self): + if not self._checkcompat(): + return + + out_chart = Chart(self._left_chart.tokens()) + for edge in self._left_chart: + out_chart.insert(edge, []) + for edge in self._right_chart: + out_chart.insert(edge, []) + + self._update("or", out_chart) + + def _swapcharts(self): + left, right = self._left_name, self._right_name + self._left_selector.set(right) + self._right_selector.set(left) + + def _checkcompat(self): + if ( + self._left_chart.tokens() != self._right_chart.tokens() + or self._left_chart.property_names() != self._right_chart.property_names() + or self._left_chart == self._emptychart + or self._right_chart == self._emptychart + ): + # Clear & inactivate the output chart. + self._out_chart = self._emptychart + self._out_matrix.set_chart(self._out_chart) + self._out_matrix.inactivate() + self._out_label["text"] = "Output" + # Issue some other warning? + return False + else: + return True + + def _update(self, operator, out_chart): + self._operator = operator + self._op_label["text"] = self._OPSYMBOL[operator] + self._out_chart = out_chart + self._out_matrix.set_chart(out_chart) + self._out_label["text"] = "{} {} {}".format( + self._left_name, + self._operator, + self._right_name, + ) + + def _clear_out_chart(self): + self._out_chart = self._emptychart + self._out_matrix.set_chart(self._out_chart) + self._op_label["text"] = " " + self._out_matrix.inactivate() + + def _detach_out(self): + ChartMatrixView(self._root, self._out_chart, title=self._out_label["text"]) + + +####################################################################### +# Chart View +####################################################################### + + +class ChartView: + """ + A component for viewing charts. This is used by ``ChartParserApp`` to + allow students to interactively experiment with various chart + parsing techniques. It is also used by ``Chart.draw()``. + + :ivar _chart: The chart that we are giving a view of. This chart + may be modified; after it is modified, you should call + ``update``. + :ivar _sentence: The list of tokens that the chart spans. + + :ivar _root: The root window. + :ivar _chart_canvas: The canvas we're using to display the chart + itself. + :ivar _tree_canvas: The canvas we're using to display the tree + that each edge spans. May be None, if we're not displaying + trees. + :ivar _sentence_canvas: The canvas we're using to display the sentence + text. May be None, if we're not displaying the sentence text. + :ivar _edgetags: A dictionary mapping from edges to the tags of + the canvas elements (lines, etc) used to display that edge. + The values of this dictionary have the form + ``(linetag, rhstag1, dottag, rhstag2, lhstag)``. + :ivar _treetags: A list of all the tags that make up the tree; + used to erase the tree (without erasing the loclines). + :ivar _chart_height: The height of the chart canvas. + :ivar _sentence_height: The height of the sentence canvas. + :ivar _tree_height: The height of the tree + + :ivar _text_height: The height of a text string (in the normal + font). + + :ivar _edgelevels: A list of edges at each level of the chart (the + top level is the 0th element). This list is used to remember + where edges should be drawn; and to make sure that no edges + are overlapping on the chart view. + + :ivar _unitsize: Pixel size of one unit (from the location). This + is determined by the span of the chart's location, and the + width of the chart display canvas. + + :ivar _fontsize: The current font size + + :ivar _marks: A dictionary from edges to marks. Marks are + strings, specifying colors (e.g. 'green'). + """ + + _LEAF_SPACING = 10 + _MARGIN = 10 + _TREE_LEVEL_SIZE = 12 + _CHART_LEVEL_SIZE = 40 + + def __init__(self, chart, root=None, **kw): + """ + Construct a new ``Chart`` display. + """ + # Process keyword args. + draw_tree = kw.get("draw_tree", 0) + draw_sentence = kw.get("draw_sentence", 1) + self._fontsize = kw.get("fontsize", -12) + + # The chart! + self._chart = chart + + # Callback functions + self._callbacks = {} + + # Keep track of drawn edges + self._edgelevels = [] + self._edgetags = {} + + # Keep track of which edges are marked. + self._marks = {} + + # These are used to keep track of the set of tree tokens + # currently displayed in the tree canvas. + self._treetoks = [] + self._treetoks_edge = None + self._treetoks_index = 0 + + # Keep track of the tags used to draw the tree + self._tree_tags = [] + + # Put multiple edges on each level? + self._compact = 0 + + # If they didn't provide a main window, then set one up. + if root is None: + top = Tk() + top.title("Chart View") + + def destroy1(e, top=top): + top.destroy() + + def destroy2(top=top): + top.destroy() + + top.bind("q", destroy1) + b = Button(top, text="Done", command=destroy2) + b.pack(side="bottom") + self._root = top + else: + self._root = root + + # Create some fonts. + self._init_fonts(root) + + # Create the chart canvas. + (self._chart_sb, self._chart_canvas) = self._sb_canvas(self._root) + self._chart_canvas["height"] = 300 + self._chart_canvas["closeenough"] = 15 + + # Create the sentence canvas. + if draw_sentence: + cframe = Frame(self._root, relief="sunk", border=2) + cframe.pack(fill="both", side="bottom") + self._sentence_canvas = Canvas(cframe, height=50) + self._sentence_canvas["background"] = "#e0e0e0" + self._sentence_canvas.pack(fill="both") + # self._sentence_canvas['height'] = self._sentence_height + else: + self._sentence_canvas = None + + # Create the tree canvas. + if draw_tree: + (sb, canvas) = self._sb_canvas(self._root, "n", "x") + (self._tree_sb, self._tree_canvas) = (sb, canvas) + self._tree_canvas["height"] = 200 + else: + self._tree_canvas = None + + # Do some analysis to figure out how big the window should be + self._analyze() + self.draw() + self._resize() + self._grow() + + # Set up the configure callback, which will be called whenever + # the window is resized. + self._chart_canvas.bind("", self._configure) + + def _init_fonts(self, root): + self._boldfont = Font(family="helvetica", weight="bold", size=self._fontsize) + self._font = Font(family="helvetica", size=self._fontsize) + # See: + self._sysfont = Font(font=Button()["font"]) + root.option_add("*Font", self._sysfont) + + def _sb_canvas(self, root, expand="y", fill="both", side="bottom"): + """ + Helper for __init__: construct a canvas with a scrollbar. + """ + cframe = Frame(root, relief="sunk", border=2) + cframe.pack(fill=fill, expand=expand, side=side) + canvas = Canvas(cframe, background="#e0e0e0") + + # Give the canvas a scrollbar. + sb = Scrollbar(cframe, orient="vertical") + sb.pack(side="right", fill="y") + canvas.pack(side="left", fill=fill, expand="yes") + + # Connect the scrollbars to the canvas. + sb["command"] = canvas.yview + canvas["yscrollcommand"] = sb.set + + return (sb, canvas) + + def scroll_up(self, *e): + self._chart_canvas.yview("scroll", -1, "units") + + def scroll_down(self, *e): + self._chart_canvas.yview("scroll", 1, "units") + + def page_up(self, *e): + self._chart_canvas.yview("scroll", -1, "pages") + + def page_down(self, *e): + self._chart_canvas.yview("scroll", 1, "pages") + + def _grow(self): + """ + Grow the window, if necessary + """ + # Grow, if need-be + N = self._chart.num_leaves() + width = max( + int(self._chart_canvas["width"]), N * self._unitsize + ChartView._MARGIN * 2 + ) + + # It won't resize without the second (height) line, but I + # don't understand why not. + self._chart_canvas.configure(width=width) + self._chart_canvas.configure(height=self._chart_canvas["height"]) + + self._unitsize = (width - 2 * ChartView._MARGIN) / N + + # Reset the height for the sentence window. + if self._sentence_canvas is not None: + self._sentence_canvas["height"] = self._sentence_height + + def set_font_size(self, size): + self._font.configure(size=-abs(size)) + self._boldfont.configure(size=-abs(size)) + self._sysfont.configure(size=-abs(size)) + self._analyze() + self._grow() + self.draw() + + def get_font_size(self): + return abs(self._fontsize) + + def _configure(self, e): + """ + The configure callback. This is called whenever the window is + resized. It is also called when the window is first mapped. + It figures out the unit size, and redraws the contents of each + canvas. + """ + N = self._chart.num_leaves() + self._unitsize = (e.width - 2 * ChartView._MARGIN) / N + self.draw() + + def update(self, chart=None): + """ + Draw any edges that have not been drawn. This is typically + called when a after modifies the canvas that a CanvasView is + displaying. ``update`` will cause any edges that have been + added to the chart to be drawn. + + If update is given a ``chart`` argument, then it will replace + the current chart with the given chart. + """ + if chart is not None: + self._chart = chart + self._edgelevels = [] + self._marks = {} + self._analyze() + self._grow() + self.draw() + self.erase_tree() + self._resize() + else: + for edge in self._chart: + if edge not in self._edgetags: + self._add_edge(edge) + self._resize() + + def _edge_conflict(self, edge, lvl): + """ + Return True if the given edge overlaps with any edge on the given + level. This is used by _add_edge to figure out what level a + new edge should be added to. + """ + (s1, e1) = edge.span() + for otheredge in self._edgelevels[lvl]: + (s2, e2) = otheredge.span() + if (s1 <= s2 < e1) or (s2 <= s1 < e2) or (s1 == s2 == e1 == e2): + return True + return False + + def _analyze_edge(self, edge): + """ + Given a new edge, recalculate: + + - _text_height + - _unitsize (if the edge text is too big for the current + _unitsize, then increase _unitsize) + """ + c = self._chart_canvas + + if isinstance(edge, TreeEdge): + lhs = edge.lhs() + rhselts = [] + for elt in edge.rhs(): + if isinstance(elt, Nonterminal): + rhselts.append(str(elt.symbol())) + else: + rhselts.append(repr(elt)) + rhs = " ".join(rhselts) + else: + lhs = edge.lhs() + rhs = "" + + for s in (lhs, rhs): + tag = c.create_text( + 0, 0, text=s, font=self._boldfont, anchor="nw", justify="left" + ) + bbox = c.bbox(tag) + c.delete(tag) + width = bbox[2] # + ChartView._LEAF_SPACING + edgelen = max(edge.length(), 1) + self._unitsize = max(self._unitsize, width / edgelen) + self._text_height = max(self._text_height, bbox[3] - bbox[1]) + + def _add_edge(self, edge, minlvl=0): + """ + Add a single edge to the ChartView: + + - Call analyze_edge to recalculate display parameters + - Find an available level + - Call _draw_edge + """ + # Do NOT show leaf edges in the chart. + if isinstance(edge, LeafEdge): + return + + if edge in self._edgetags: + return + self._analyze_edge(edge) + self._grow() + + if not self._compact: + self._edgelevels.append([edge]) + lvl = len(self._edgelevels) - 1 + self._draw_edge(edge, lvl) + self._resize() + return + + # Figure out what level to draw the edge on. + lvl = 0 + while True: + # If this level doesn't exist yet, create it. + while lvl >= len(self._edgelevels): + self._edgelevels.append([]) + self._resize() + + # Check if we can fit the edge in this level. + if lvl >= minlvl and not self._edge_conflict(edge, lvl): + # Go ahead and draw it. + self._edgelevels[lvl].append(edge) + break + + # Try the next level. + lvl += 1 + + self._draw_edge(edge, lvl) + + def view_edge(self, edge): + level = None + for i in range(len(self._edgelevels)): + if edge in self._edgelevels[i]: + level = i + break + if level is None: + return + # Try to view the new edge.. + y = (level + 1) * self._chart_level_size + dy = self._text_height + 10 + self._chart_canvas.yview("moveto", 1.0) + if self._chart_height != 0: + self._chart_canvas.yview("moveto", (y - dy) / self._chart_height) + + def _draw_edge(self, edge, lvl): + """ + Draw a single edge on the ChartView. + """ + c = self._chart_canvas + + # Draw the arrow. + x1 = edge.start() * self._unitsize + ChartView._MARGIN + x2 = edge.end() * self._unitsize + ChartView._MARGIN + if x2 == x1: + x2 += max(4, self._unitsize / 5) + y = (lvl + 1) * self._chart_level_size + linetag = c.create_line(x1, y, x2, y, arrow="last", width=3) + + # Draw a label for the edge. + if isinstance(edge, TreeEdge): + rhs = [] + for elt in edge.rhs(): + if isinstance(elt, Nonterminal): + rhs.append(str(elt.symbol())) + else: + rhs.append(repr(elt)) + pos = edge.dot() + else: + rhs = [] + pos = 0 + + rhs1 = " ".join(rhs[:pos]) + rhs2 = " ".join(rhs[pos:]) + rhstag1 = c.create_text(x1 + 3, y, text=rhs1, font=self._font, anchor="nw") + dotx = c.bbox(rhstag1)[2] + 6 + doty = (c.bbox(rhstag1)[1] + c.bbox(rhstag1)[3]) / 2 + dottag = c.create_oval(dotx - 2, doty - 2, dotx + 2, doty + 2) + rhstag2 = c.create_text(dotx + 6, y, text=rhs2, font=self._font, anchor="nw") + lhstag = c.create_text( + (x1 + x2) / 2, y, text=str(edge.lhs()), anchor="s", font=self._boldfont + ) + + # Keep track of the edge's tags. + self._edgetags[edge] = (linetag, rhstag1, dottag, rhstag2, lhstag) + + # Register a callback for clicking on the edge. + def cb(event, self=self, edge=edge): + self._fire_callbacks("select", edge) + + c.tag_bind(rhstag1, "", cb) + c.tag_bind(rhstag2, "", cb) + c.tag_bind(linetag, "", cb) + c.tag_bind(dottag, "", cb) + c.tag_bind(lhstag, "", cb) + + self._color_edge(edge) + + def _color_edge(self, edge, linecolor=None, textcolor=None): + """ + Color in an edge with the given colors. + If no colors are specified, use intelligent defaults + (dependent on selection, etc.) + """ + if edge not in self._edgetags: + return + c = self._chart_canvas + + if linecolor is not None and textcolor is not None: + if edge in self._marks: + linecolor = self._marks[edge] + tags = self._edgetags[edge] + c.itemconfig(tags[0], fill=linecolor) + c.itemconfig(tags[1], fill=textcolor) + c.itemconfig(tags[2], fill=textcolor, outline=textcolor) + c.itemconfig(tags[3], fill=textcolor) + c.itemconfig(tags[4], fill=textcolor) + return + else: + N = self._chart.num_leaves() + if edge in self._marks: + self._color_edge(self._marks[edge]) + if edge.is_complete() and edge.span() == (0, N): + self._color_edge(edge, "#084", "#042") + elif isinstance(edge, LeafEdge): + self._color_edge(edge, "#48c", "#246") + else: + self._color_edge(edge, "#00f", "#008") + + def mark_edge(self, edge, mark="#0df"): + """ + Mark an edge + """ + self._marks[edge] = mark + self._color_edge(edge) + + def unmark_edge(self, edge=None): + """ + Unmark an edge (or all edges) + """ + if edge is None: + old_marked_edges = list(self._marks.keys()) + self._marks = {} + for edge in old_marked_edges: + self._color_edge(edge) + else: + del self._marks[edge] + self._color_edge(edge) + + def markonly_edge(self, edge, mark="#0df"): + self.unmark_edge() + self.mark_edge(edge, mark) + + def _analyze(self): + """ + Analyze the sentence string, to figure out how big a unit needs + to be, How big the tree should be, etc. + """ + # Figure out the text height and the unit size. + unitsize = 70 # min unitsize + text_height = 0 + c = self._chart_canvas + + # Check against all tokens + for leaf in self._chart.leaves(): + tag = c.create_text( + 0, 0, text=repr(leaf), font=self._font, anchor="nw", justify="left" + ) + bbox = c.bbox(tag) + c.delete(tag) + width = bbox[2] + ChartView._LEAF_SPACING + unitsize = max(width, unitsize) + text_height = max(text_height, bbox[3] - bbox[1]) + + self._unitsize = unitsize + self._text_height = text_height + self._sentence_height = self._text_height + 2 * ChartView._MARGIN + + # Check against edges. + for edge in self._chart.edges(): + self._analyze_edge(edge) + + # Size of chart levels + self._chart_level_size = self._text_height * 2 + + # Default tree size.. + self._tree_height = 3 * (ChartView._TREE_LEVEL_SIZE + self._text_height) + + # Resize the scrollregions. + self._resize() + + def _resize(self): + """ + Update the scroll-regions for each canvas. This ensures that + everything is within a scroll-region, so the user can use the + scrollbars to view the entire display. This does *not* + resize the window. + """ + c = self._chart_canvas + + # Reset the chart scroll region + width = self._chart.num_leaves() * self._unitsize + ChartView._MARGIN * 2 + + levels = len(self._edgelevels) + self._chart_height = (levels + 2) * self._chart_level_size + c["scrollregion"] = (0, 0, width, self._chart_height) + + # Reset the tree scroll region + if self._tree_canvas: + self._tree_canvas["scrollregion"] = (0, 0, width, self._tree_height) + + def _draw_loclines(self): + """ + Draw location lines. These are vertical gridlines used to + show where each location unit is. + """ + BOTTOM = 50000 + c1 = self._tree_canvas + c2 = self._sentence_canvas + c3 = self._chart_canvas + margin = ChartView._MARGIN + self._loclines = [] + for i in range(0, self._chart.num_leaves() + 1): + x = i * self._unitsize + margin + + if c1: + t1 = c1.create_line(x, 0, x, BOTTOM) + c1.tag_lower(t1) + if c2: + t2 = c2.create_line(x, 0, x, self._sentence_height) + c2.tag_lower(t2) + t3 = c3.create_line(x, 0, x, BOTTOM) + c3.tag_lower(t3) + t4 = c3.create_text(x + 2, 0, text=repr(i), anchor="nw", font=self._font) + c3.tag_lower(t4) + # if i % 4 == 0: + # if c1: c1.itemconfig(t1, width=2, fill='gray60') + # if c2: c2.itemconfig(t2, width=2, fill='gray60') + # c3.itemconfig(t3, width=2, fill='gray60') + if i % 2 == 0: + if c1: + c1.itemconfig(t1, fill="gray60") + if c2: + c2.itemconfig(t2, fill="gray60") + c3.itemconfig(t3, fill="gray60") + else: + if c1: + c1.itemconfig(t1, fill="gray80") + if c2: + c2.itemconfig(t2, fill="gray80") + c3.itemconfig(t3, fill="gray80") + + def _draw_sentence(self): + """Draw the sentence string.""" + if self._chart.num_leaves() == 0: + return + c = self._sentence_canvas + margin = ChartView._MARGIN + y = ChartView._MARGIN + + for i, leaf in enumerate(self._chart.leaves()): + x1 = i * self._unitsize + margin + x2 = x1 + self._unitsize + x = (x1 + x2) / 2 + tag = c.create_text( + x, y, text=repr(leaf), font=self._font, anchor="n", justify="left" + ) + bbox = c.bbox(tag) + rt = c.create_rectangle( + x1 + 2, + bbox[1] - (ChartView._LEAF_SPACING / 2), + x2 - 2, + bbox[3] + (ChartView._LEAF_SPACING / 2), + fill="#f0f0f0", + outline="#f0f0f0", + ) + c.tag_lower(rt) + + def erase_tree(self): + for tag in self._tree_tags: + self._tree_canvas.delete(tag) + self._treetoks = [] + self._treetoks_edge = None + self._treetoks_index = 0 + + def draw_tree(self, edge=None): + if edge is None and self._treetoks_edge is None: + return + if edge is None: + edge = self._treetoks_edge + + # If it's a new edge, then get a new list of treetoks. + if self._treetoks_edge != edge: + self._treetoks = [t for t in self._chart.trees(edge) if isinstance(t, Tree)] + self._treetoks_edge = edge + self._treetoks_index = 0 + + # Make sure there's something to draw. + if len(self._treetoks) == 0: + return + + # Erase the old tree. + for tag in self._tree_tags: + self._tree_canvas.delete(tag) + + # Draw the new tree. + tree = self._treetoks[self._treetoks_index] + self._draw_treetok(tree, edge.start()) + + # Show how many trees are available for the edge. + self._draw_treecycle() + + # Update the scroll region. + w = self._chart.num_leaves() * self._unitsize + 2 * ChartView._MARGIN + h = tree.height() * (ChartView._TREE_LEVEL_SIZE + self._text_height) + self._tree_canvas["scrollregion"] = (0, 0, w, h) + + def cycle_tree(self): + self._treetoks_index = (self._treetoks_index + 1) % len(self._treetoks) + self.draw_tree(self._treetoks_edge) + + def _draw_treecycle(self): + if len(self._treetoks) <= 1: + return + + # Draw the label. + label = "%d Trees" % len(self._treetoks) + c = self._tree_canvas + margin = ChartView._MARGIN + right = self._chart.num_leaves() * self._unitsize + margin - 2 + tag = c.create_text(right, 2, anchor="ne", text=label, font=self._boldfont) + self._tree_tags.append(tag) + _, _, _, y = c.bbox(tag) + + # Draw the triangles. + for i in range(len(self._treetoks)): + x = right - 20 * (len(self._treetoks) - i - 1) + if i == self._treetoks_index: + fill = "#084" + else: + fill = "#fff" + tag = c.create_polygon( + x, y + 10, x - 5, y, x - 10, y + 10, fill=fill, outline="black" + ) + self._tree_tags.append(tag) + + # Set up a callback: show the tree if they click on its + # triangle. + def cb(event, self=self, i=i): + self._treetoks_index = i + self.draw_tree() + + c.tag_bind(tag, "", cb) + + def _draw_treetok(self, treetok, index, depth=0): + """ + :param index: The index of the first leaf in the tree. + :return: The index of the first leaf after the tree. + """ + c = self._tree_canvas + margin = ChartView._MARGIN + + # Draw the children + child_xs = [] + for child in treetok: + if isinstance(child, Tree): + child_x, index = self._draw_treetok(child, index, depth + 1) + child_xs.append(child_x) + else: + child_xs.append((2 * index + 1) * self._unitsize / 2 + margin) + index += 1 + + # If we have children, then get the node's x by averaging their + # node x's. Otherwise, make room for ourselves. + if child_xs: + nodex = sum(child_xs) / len(child_xs) + else: + # [XX] breaks for null productions. + nodex = (2 * index + 1) * self._unitsize / 2 + margin + index += 1 + + # Draw the node + nodey = depth * (ChartView._TREE_LEVEL_SIZE + self._text_height) + tag = c.create_text( + nodex, + nodey, + anchor="n", + justify="center", + text=str(treetok.label()), + fill="#042", + font=self._boldfont, + ) + self._tree_tags.append(tag) + + # Draw lines to the children. + childy = nodey + ChartView._TREE_LEVEL_SIZE + self._text_height + for childx, child in zip(child_xs, treetok): + if isinstance(child, Tree) and child: + # A "real" tree token: + tag = c.create_line( + nodex, + nodey + self._text_height, + childx, + childy, + width=2, + fill="#084", + ) + self._tree_tags.append(tag) + if isinstance(child, Tree) and not child: + # An unexpanded tree token: + tag = c.create_line( + nodex, + nodey + self._text_height, + childx, + childy, + width=2, + fill="#048", + dash="2 3", + ) + self._tree_tags.append(tag) + if not isinstance(child, Tree): + # A leaf: + tag = c.create_line( + nodex, + nodey + self._text_height, + childx, + 10000, + width=2, + fill="#084", + ) + self._tree_tags.append(tag) + + return nodex, index + + def draw(self): + """ + Draw everything (from scratch). + """ + if self._tree_canvas: + self._tree_canvas.delete("all") + self.draw_tree() + + if self._sentence_canvas: + self._sentence_canvas.delete("all") + self._draw_sentence() + + self._chart_canvas.delete("all") + self._edgetags = {} + + # Redraw any edges we erased. + for lvl in range(len(self._edgelevels)): + for edge in self._edgelevels[lvl]: + self._draw_edge(edge, lvl) + + for edge in self._chart: + self._add_edge(edge) + + self._draw_loclines() + + def add_callback(self, event, func): + self._callbacks.setdefault(event, {})[func] = 1 + + def remove_callback(self, event, func=None): + if func is None: + del self._callbacks[event] + else: + try: + del self._callbacks[event][func] + except: + pass + + def _fire_callbacks(self, event, *args): + if event not in self._callbacks: + return + for cb_func in list(self._callbacks[event].keys()): + cb_func(*args) + + +####################################################################### +# Edge Rules +####################################################################### +# These version of the chart rules only apply to a specific edge. +# This lets the user select an edge, and then apply a rule. + + +class EdgeRule: + """ + To create an edge rule, make an empty base class that uses + EdgeRule as the first base class, and the basic rule as the + second base class. (Order matters!) + """ + + def __init__(self, edge): + super = self.__class__.__bases__[1] + self._edge = edge + self.NUM_EDGES = super.NUM_EDGES - 1 + + def apply(self, chart, grammar, *edges): + super = self.__class__.__bases__[1] + edges += (self._edge,) + yield from super.apply(self, chart, grammar, *edges) + + def __str__(self): + super = self.__class__.__bases__[1] + return super.__str__(self) + + +class TopDownPredictEdgeRule(EdgeRule, TopDownPredictRule): + pass + + +class BottomUpEdgeRule(EdgeRule, BottomUpPredictRule): + pass + + +class BottomUpLeftCornerEdgeRule(EdgeRule, BottomUpPredictCombineRule): + pass + + +class FundamentalEdgeRule(EdgeRule, SingleEdgeFundamentalRule): + pass + + +####################################################################### +# Chart Parser Application +####################################################################### + + +class ChartParserApp: + def __init__(self, grammar, tokens, title="Chart Parser Application"): + # Initialize the parser + self._init_parser(grammar, tokens) + + self._root = None + try: + # Create the root window. + self._root = Tk() + self._root.title(title) + self._root.bind("", self.destroy) + + # Set up some frames. + frame3 = Frame(self._root) + frame2 = Frame(self._root) + frame1 = Frame(self._root) + frame3.pack(side="bottom", fill="none") + frame2.pack(side="bottom", fill="x") + frame1.pack(side="bottom", fill="both", expand=1) + + self._init_fonts(self._root) + self._init_animation() + self._init_chartview(frame1) + self._init_rulelabel(frame2) + self._init_buttons(frame3) + self._init_menubar() + + self._matrix = None + self._results = None + + # Set up keyboard bindings. + self._init_bindings() + + except: + print("Error creating Tree View") + self.destroy() + raise + + def destroy(self, *args): + if self._root is None: + return + self._root.destroy() + self._root = None + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self._root.mainloop(*args, **kwargs) + + # //////////////////////////////////////////////////////////// + # Initialization Helpers + # //////////////////////////////////////////////////////////// + + def _init_parser(self, grammar, tokens): + self._grammar = grammar + self._tokens = tokens + self._reset_parser() + + def _reset_parser(self): + self._cp = SteppingChartParser(self._grammar) + self._cp.initialize(self._tokens) + self._chart = self._cp.chart() + + # Insert LeafEdges before the parsing starts. + for _new_edge in LeafInitRule().apply(self._chart, self._grammar): + pass + + # The step iterator -- use this to generate new edges + self._cpstep = self._cp.step() + + # The currently selected edge + self._selection = None + + def _init_fonts(self, root): + # See: + self._sysfont = Font(font=Button()["font"]) + root.option_add("*Font", self._sysfont) + + # TWhat's our font size (default=same as sysfont) + self._size = IntVar(root) + self._size.set(self._sysfont.cget("size")) + + self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) + self._font = Font(family="helvetica", size=self._size.get()) + + def _init_animation(self): + # Are we stepping? (default=yes) + self._step = IntVar(self._root) + self._step.set(1) + + # What's our animation speed (default=fast) + self._animate = IntVar(self._root) + self._animate.set(3) # Default speed = fast + + # Are we currently animating? + self._animating = 0 + + def _init_chartview(self, parent): + self._cv = ChartView(self._chart, parent, draw_tree=1, draw_sentence=1) + self._cv.add_callback("select", self._click_cv_edge) + + def _init_rulelabel(self, parent): + ruletxt = "Last edge generated by:" + + self._rulelabel1 = Label(parent, text=ruletxt, font=self._boldfont) + self._rulelabel2 = Label( + parent, width=40, relief="groove", anchor="w", font=self._boldfont + ) + self._rulelabel1.pack(side="left") + self._rulelabel2.pack(side="left") + step = Checkbutton(parent, variable=self._step, text="Step") + step.pack(side="right") + + def _init_buttons(self, parent): + frame1 = Frame(parent) + frame2 = Frame(parent) + frame1.pack(side="bottom", fill="x") + frame2.pack(side="top", fill="none") + + Button( + frame1, + text="Reset\nParser", + background="#90c0d0", + foreground="black", + command=self.reset, + ).pack(side="right") + # Button(frame1, text='Pause', + # background='#90c0d0', foreground='black', + # command=self.pause).pack(side='left') + + Button( + frame1, + text="Top Down\nStrategy", + background="#90c0d0", + foreground="black", + command=self.top_down_strategy, + ).pack(side="left") + Button( + frame1, + text="Bottom Up\nStrategy", + background="#90c0d0", + foreground="black", + command=self.bottom_up_strategy, + ).pack(side="left") + Button( + frame1, + text="Bottom Up\nLeft-Corner Strategy", + background="#90c0d0", + foreground="black", + command=self.bottom_up_leftcorner_strategy, + ).pack(side="left") + + Button( + frame2, + text="Top Down Init\nRule", + background="#90f090", + foreground="black", + command=self.top_down_init, + ).pack(side="left") + Button( + frame2, + text="Top Down Predict\nRule", + background="#90f090", + foreground="black", + command=self.top_down_predict, + ).pack(side="left") + Frame(frame2, width=20).pack(side="left") + + Button( + frame2, + text="Bottom Up Predict\nRule", + background="#90f090", + foreground="black", + command=self.bottom_up, + ).pack(side="left") + Frame(frame2, width=20).pack(side="left") + + Button( + frame2, + text="Bottom Up Left-Corner\nPredict Rule", + background="#90f090", + foreground="black", + command=self.bottom_up_leftcorner, + ).pack(side="left") + Frame(frame2, width=20).pack(side="left") + + Button( + frame2, + text="Fundamental\nRule", + background="#90f090", + foreground="black", + command=self.fundamental, + ).pack(side="left") + + def _init_bindings(self): + self._root.bind("", self._cv.scroll_up) + self._root.bind("", self._cv.scroll_down) + self._root.bind("", self._cv.page_up) + self._root.bind("", self._cv.page_down) + self._root.bind("", self.destroy) + self._root.bind("", self.destroy) + self._root.bind("", self.help) + + self._root.bind("", self.save_chart) + self._root.bind("", self.load_chart) + self._root.bind("", self.reset) + + self._root.bind("t", self.top_down_strategy) + self._root.bind("b", self.bottom_up_strategy) + self._root.bind("c", self.bottom_up_leftcorner_strategy) + self._root.bind("", self._stop_animation) + + self._root.bind("", self.edit_grammar) + self._root.bind("", self.edit_sentence) + + # Animation speed control + self._root.bind("-", lambda e, a=self._animate: a.set(1)) + self._root.bind("=", lambda e, a=self._animate: a.set(2)) + self._root.bind("+", lambda e, a=self._animate: a.set(3)) + + # Step control + self._root.bind("s", lambda e, s=self._step: s.set(not s.get())) + + def _init_menubar(self): + menubar = Menu(self._root) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Save Chart", + underline=0, + command=self.save_chart, + accelerator="Ctrl-s", + ) + filemenu.add_command( + label="Load Chart", + underline=0, + command=self.load_chart, + accelerator="Ctrl-o", + ) + filemenu.add_command( + label="Reset Chart", underline=0, command=self.reset, accelerator="Ctrl-r" + ) + filemenu.add_separator() + filemenu.add_command(label="Save Grammar", command=self.save_grammar) + filemenu.add_command(label="Load Grammar", command=self.load_grammar) + filemenu.add_separator() + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + editmenu = Menu(menubar, tearoff=0) + editmenu.add_command( + label="Edit Grammar", + underline=5, + command=self.edit_grammar, + accelerator="Ctrl-g", + ) + editmenu.add_command( + label="Edit Text", + underline=5, + command=self.edit_sentence, + accelerator="Ctrl-t", + ) + menubar.add_cascade(label="Edit", underline=0, menu=editmenu) + + viewmenu = Menu(menubar, tearoff=0) + viewmenu.add_command( + label="Chart Matrix", underline=6, command=self.view_matrix + ) + viewmenu.add_command(label="Results", underline=0, command=self.view_results) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + rulemenu = Menu(menubar, tearoff=0) + rulemenu.add_command( + label="Top Down Strategy", + underline=0, + command=self.top_down_strategy, + accelerator="t", + ) + rulemenu.add_command( + label="Bottom Up Strategy", + underline=0, + command=self.bottom_up_strategy, + accelerator="b", + ) + rulemenu.add_command( + label="Bottom Up Left-Corner Strategy", + underline=0, + command=self.bottom_up_leftcorner_strategy, + accelerator="c", + ) + rulemenu.add_separator() + rulemenu.add_command(label="Bottom Up Rule", command=self.bottom_up) + rulemenu.add_command( + label="Bottom Up Left-Corner Rule", command=self.bottom_up_leftcorner + ) + rulemenu.add_command(label="Top Down Init Rule", command=self.top_down_init) + rulemenu.add_command( + label="Top Down Predict Rule", command=self.top_down_predict + ) + rulemenu.add_command(label="Fundamental Rule", command=self.fundamental) + menubar.add_cascade(label="Apply", underline=0, menu=rulemenu) + + animatemenu = Menu(menubar, tearoff=0) + animatemenu.add_checkbutton( + label="Step", underline=0, variable=self._step, accelerator="s" + ) + animatemenu.add_separator() + animatemenu.add_radiobutton( + label="No Animation", underline=0, variable=self._animate, value=0 + ) + animatemenu.add_radiobutton( + label="Slow Animation", + underline=0, + variable=self._animate, + value=1, + accelerator="-", + ) + animatemenu.add_radiobutton( + label="Normal Animation", + underline=0, + variable=self._animate, + value=2, + accelerator="=", + ) + animatemenu.add_radiobutton( + label="Fast Animation", + underline=0, + variable=self._animate, + value=3, + accelerator="+", + ) + menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) + + zoommenu = Menu(menubar, tearoff=0) + zoommenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=12, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=14, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=18, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=24, + command=self.resize, + ) + menubar.add_cascade(label="Zoom", underline=0, menu=zoommenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + helpmenu.add_command( + label="Instructions", underline=0, command=self.help, accelerator="F1" + ) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + + self._root.config(menu=menubar) + + # //////////////////////////////////////////////////////////// + # Selection Handling + # //////////////////////////////////////////////////////////// + + def _click_cv_edge(self, edge): + if edge != self._selection: + # Clicking on a new edge selects it. + self._select_edge(edge) + else: + # Repeated clicks on one edge cycle its trees. + self._cv.cycle_tree() + # [XX] this can get confused if animation is running + # faster than the callbacks... + + def _select_matrix_edge(self, edge): + self._select_edge(edge) + self._cv.view_edge(edge) + + def _select_edge(self, edge): + self._selection = edge + # Update the chart view. + self._cv.markonly_edge(edge, "#f00") + self._cv.draw_tree(edge) + # Update the matrix view. + if self._matrix: + self._matrix.markonly_edge(edge) + if self._matrix: + self._matrix.view_edge(edge) + + def _deselect_edge(self): + self._selection = None + # Update the chart view. + self._cv.unmark_edge() + self._cv.erase_tree() + # Update the matrix view + if self._matrix: + self._matrix.unmark_edge() + + def _show_new_edge(self, edge): + self._display_rule(self._cp.current_chartrule()) + # Update the chart view. + self._cv.update() + self._cv.draw_tree(edge) + self._cv.markonly_edge(edge, "#0df") + self._cv.view_edge(edge) + # Update the matrix view. + if self._matrix: + self._matrix.update() + if self._matrix: + self._matrix.markonly_edge(edge) + if self._matrix: + self._matrix.view_edge(edge) + # Update the results view. + if self._results: + self._results.update(edge) + + # //////////////////////////////////////////////////////////// + # Help/usage + # //////////////////////////////////////////////////////////// + + def help(self, *e): + self._animating = 0 + # The default font's not very legible; try using 'fixed' instead. + try: + ShowText( + self._root, + "Help: Chart Parser Application", + (__doc__ or "").strip(), + width=75, + font="fixed", + ) + except: + ShowText( + self._root, + "Help: Chart Parser Application", + (__doc__ or "").strip(), + width=75, + ) + + def about(self, *e): + ABOUT = "NLTK Chart Parser Application\n" + "Written by Edward Loper" + showinfo("About: Chart Parser Application", ABOUT) + + # //////////////////////////////////////////////////////////// + # File Menu + # //////////////////////////////////////////////////////////// + + CHART_FILE_TYPES = [("Pickle file", ".pickle"), ("All files", "*")] + GRAMMAR_FILE_TYPES = [ + ("Plaintext grammar file", ".cfg"), + ("Pickle file", ".pickle"), + ("All files", "*"), + ] + + def load_chart(self, *args): + "Load a chart from a pickle file" + filename = askopenfilename( + filetypes=self.CHART_FILE_TYPES, defaultextension=".pickle" + ) + if not filename: + return + try: + with open(filename, "rb") as infile: + chart = pickle.load(infile) + self._chart = chart + self._cv.update(chart) + if self._matrix: + self._matrix.set_chart(chart) + if self._matrix: + self._matrix.deselect_cell() + if self._results: + self._results.set_chart(chart) + self._cp.set_chart(chart) + except Exception as e: + raise + showerror("Error Loading Chart", "Unable to open file: %r" % filename) + + def save_chart(self, *args): + "Save a chart to a pickle file" + filename = asksaveasfilename( + filetypes=self.CHART_FILE_TYPES, defaultextension=".pickle" + ) + if not filename: + return + try: + with open(filename, "wb") as outfile: + pickle.dump(self._chart, outfile) + except Exception as e: + raise + showerror("Error Saving Chart", "Unable to open file: %r" % filename) + + def load_grammar(self, *args): + "Load a grammar from a pickle file" + filename = askopenfilename( + filetypes=self.GRAMMAR_FILE_TYPES, defaultextension=".cfg" + ) + if not filename: + return + try: + if filename.endswith(".pickle"): + with open(filename, "rb") as infile: + grammar = pickle.load(infile) + else: + with open(filename) as infile: + grammar = CFG.fromstring(infile.read()) + self.set_grammar(grammar) + except Exception as e: + showerror("Error Loading Grammar", "Unable to open file: %r" % filename) + + def save_grammar(self, *args): + filename = asksaveasfilename( + filetypes=self.GRAMMAR_FILE_TYPES, defaultextension=".cfg" + ) + if not filename: + return + try: + if filename.endswith(".pickle"): + with open(filename, "wb") as outfile: + pickle.dump((self._chart, self._tokens), outfile) + else: + with open(filename, "w") as outfile: + prods = self._grammar.productions() + start = [p for p in prods if p.lhs() == self._grammar.start()] + rest = [p for p in prods if p.lhs() != self._grammar.start()] + for prod in start: + outfile.write("%s\n" % prod) + for prod in rest: + outfile.write("%s\n" % prod) + except Exception as e: + showerror("Error Saving Grammar", "Unable to open file: %r" % filename) + + def reset(self, *args): + self._animating = 0 + self._reset_parser() + self._cv.update(self._chart) + if self._matrix: + self._matrix.set_chart(self._chart) + if self._matrix: + self._matrix.deselect_cell() + if self._results: + self._results.set_chart(self._chart) + + # //////////////////////////////////////////////////////////// + # Edit + # //////////////////////////////////////////////////////////// + + def edit_grammar(self, *e): + CFGEditor(self._root, self._grammar, self.set_grammar) + + def set_grammar(self, grammar): + self._grammar = grammar + self._cp.set_grammar(grammar) + if self._results: + self._results.set_grammar(grammar) + + def edit_sentence(self, *e): + sentence = " ".join(self._tokens) + title = "Edit Text" + instr = "Enter a new sentence to parse." + EntryDialog(self._root, sentence, instr, self.set_sentence, title) + + def set_sentence(self, sentence): + self._tokens = list(sentence.split()) + self.reset() + + # //////////////////////////////////////////////////////////// + # View Menu + # //////////////////////////////////////////////////////////// + + def view_matrix(self, *e): + if self._matrix is not None: + self._matrix.destroy() + self._matrix = ChartMatrixView(self._root, self._chart) + self._matrix.add_callback("select", self._select_matrix_edge) + + def view_results(self, *e): + if self._results is not None: + self._results.destroy() + self._results = ChartResultsView(self._root, self._chart, self._grammar) + + # //////////////////////////////////////////////////////////// + # Zoom Menu + # //////////////////////////////////////////////////////////// + + def resize(self): + self._animating = 0 + self.set_font_size(self._size.get()) + + def set_font_size(self, size): + self._cv.set_font_size(size) + self._font.configure(size=-abs(size)) + self._boldfont.configure(size=-abs(size)) + self._sysfont.configure(size=-abs(size)) + + def get_font_size(self): + return abs(self._size.get()) + + # //////////////////////////////////////////////////////////// + # Parsing + # //////////////////////////////////////////////////////////// + + def apply_strategy(self, strategy, edge_strategy=None): + # If we're animating, then stop. + if self._animating: + self._animating = 0 + return + + # Clear the rule display & mark. + self._display_rule(None) + # self._cv.unmark_edge() + + if self._step.get(): + selection = self._selection + if (selection is not None) and (edge_strategy is not None): + # Apply the given strategy to the selected edge. + self._cp.set_strategy([edge_strategy(selection)]) + newedge = self._apply_strategy() + + # If it failed, then clear the selection. + if newedge is None: + self._cv.unmark_edge() + self._selection = None + else: + self._cp.set_strategy(strategy) + self._apply_strategy() + + else: + self._cp.set_strategy(strategy) + if self._animate.get(): + self._animating = 1 + self._animate_strategy() + else: + for edge in self._cpstep: + if edge is None: + break + self._cv.update() + if self._matrix: + self._matrix.update() + if self._results: + self._results.update() + + def _stop_animation(self, *e): + self._animating = 0 + + def _animate_strategy(self, speed=1): + if self._animating == 0: + return + if self._apply_strategy() is not None: + if self._animate.get() == 0 or self._step.get() == 1: + return + if self._animate.get() == 1: + self._root.after(3000, self._animate_strategy) + elif self._animate.get() == 2: + self._root.after(1000, self._animate_strategy) + else: + self._root.after(20, self._animate_strategy) + + def _apply_strategy(self): + new_edge = next(self._cpstep) + + if new_edge is not None: + self._show_new_edge(new_edge) + return new_edge + + def _display_rule(self, rule): + if rule is None: + self._rulelabel2["text"] = "" + else: + name = str(rule) + self._rulelabel2["text"] = name + size = self._cv.get_font_size() + + # //////////////////////////////////////////////////////////// + # Parsing Strategies + # //////////////////////////////////////////////////////////// + + # Basic rules: + _TD_INIT = [TopDownInitRule()] + _TD_PREDICT = [TopDownPredictRule()] + _BU_RULE = [BottomUpPredictRule()] + _BU_LC_RULE = [BottomUpPredictCombineRule()] + _FUNDAMENTAL = [SingleEdgeFundamentalRule()] + + # Complete strategies: + _TD_STRATEGY = _TD_INIT + _TD_PREDICT + _FUNDAMENTAL + _BU_STRATEGY = _BU_RULE + _FUNDAMENTAL + _BU_LC_STRATEGY = _BU_LC_RULE + _FUNDAMENTAL + + # Button callback functions: + def top_down_init(self, *e): + self.apply_strategy(self._TD_INIT, None) + + def top_down_predict(self, *e): + self.apply_strategy(self._TD_PREDICT, TopDownPredictEdgeRule) + + def bottom_up(self, *e): + self.apply_strategy(self._BU_RULE, BottomUpEdgeRule) + + def bottom_up_leftcorner(self, *e): + self.apply_strategy(self._BU_LC_RULE, BottomUpLeftCornerEdgeRule) + + def fundamental(self, *e): + self.apply_strategy(self._FUNDAMENTAL, FundamentalEdgeRule) + + def bottom_up_strategy(self, *e): + self.apply_strategy(self._BU_STRATEGY, BottomUpEdgeRule) + + def bottom_up_leftcorner_strategy(self, *e): + self.apply_strategy(self._BU_LC_STRATEGY, BottomUpLeftCornerEdgeRule) + + def top_down_strategy(self, *e): + self.apply_strategy(self._TD_STRATEGY, TopDownPredictEdgeRule) + + +def app(): + grammar = CFG.fromstring( + """ + # Grammatical productions. + S -> NP VP + VP -> VP PP | V NP | V + NP -> Det N | NP PP + PP -> P NP + # Lexical productions. + NP -> 'John' | 'I' + Det -> 'the' | 'my' | 'a' + N -> 'dog' | 'cookie' | 'table' | 'cake' | 'fork' + V -> 'ate' | 'saw' + P -> 'on' | 'under' | 'with' + """ + ) + + sent = "John ate the cake on the table with a fork" + sent = "John ate the cake on the table" + tokens = list(sent.split()) + + print("grammar= (") + for rule in grammar.productions(): + print((" ", repr(rule) + ",")) + print(")") + print("tokens = %r" % tokens) + print('Calling "ChartParserApp(grammar, tokens)"...') + ChartParserApp(grammar, tokens).mainloop() + + +if __name__ == "__main__": + app() + + # Chart comparer: + # charts = ['/tmp/earley.pickle', + # '/tmp/topdown.pickle', + # '/tmp/bottomup.pickle'] + # ChartComparer(*charts).mainloop() + + # import profile + # profile.run('demo2()', '/tmp/profile.out') + # import pstats + # p = pstats.Stats('/tmp/profile.out') + # p.strip_dirs().sort_stats('time', 'cum').print_stats(60) + # p.strip_dirs().sort_stats('cum', 'time').print_stats(60) + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/chunkparser_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/chunkparser_app.py new file mode 100644 index 0000000..aab19e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/chunkparser_app.py @@ -0,0 +1,1500 @@ +# Natural Language Toolkit: Regexp Chunk Parser Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A graphical tool for exploring the regular expression based chunk +parser ``nltk.chunk.RegexpChunkParser``. +""" + +# Todo: Add a way to select the development set from the menubar. This +# might just need to be a selection box (conll vs treebank etc) plus +# configuration parameters to select what's being chunked (eg VP vs NP) +# and what part of the data is being used as the development set. + +import random +import re +import textwrap +import time +from tkinter import ( + Button, + Canvas, + Checkbutton, + Frame, + IntVar, + Label, + Menu, + Scrollbar, + Text, + Tk, +) +from tkinter.filedialog import askopenfilename, asksaveasfilename +from tkinter.font import Font + +from nltk.chunk import ChunkScore, RegexpChunkParser +from nltk.chunk.regexp import RegexpChunkRule +from nltk.corpus import conll2000, treebank_chunk +from nltk.draw.util import ShowText +from nltk.tree import Tree +from nltk.util import in_idle + + +class RegexpChunkApp: + """ + A graphical tool for exploring the regular expression based chunk + parser ``nltk.chunk.RegexpChunkParser``. + + See ``HELP`` for instructional text. + """ + + ##///////////////////////////////////////////////////////////////// + ## Help Text + ##///////////////////////////////////////////////////////////////// + + #: A dictionary mapping from part of speech tags to descriptions, + #: which is used in the help text. (This should probably live with + #: the conll and/or treebank corpus instead.) + TAGSET = { + "CC": "Coordinating conjunction", + "PRP$": "Possessive pronoun", + "CD": "Cardinal number", + "RB": "Adverb", + "DT": "Determiner", + "RBR": "Adverb, comparative", + "EX": "Existential there", + "RBS": "Adverb, superlative", + "FW": "Foreign word", + "RP": "Particle", + "JJ": "Adjective", + "TO": "to", + "JJR": "Adjective, comparative", + "UH": "Interjection", + "JJS": "Adjective, superlative", + "VB": "Verb, base form", + "LS": "List item marker", + "VBD": "Verb, past tense", + "MD": "Modal", + "NNS": "Noun, plural", + "NN": "Noun, singular or masps", + "VBN": "Verb, past participle", + "VBZ": "Verb,3rd ps. sing. present", + "NNP": "Proper noun, singular", + "NNPS": "Proper noun plural", + "WDT": "wh-determiner", + "PDT": "Predeterminer", + "WP": "wh-pronoun", + "POS": "Possessive ending", + "WP$": "Possessive wh-pronoun", + "PRP": "Personal pronoun", + "WRB": "wh-adverb", + "(": "open parenthesis", + ")": "close parenthesis", + "``": "open quote", + ",": "comma", + "''": "close quote", + ".": "period", + "#": "pound sign (currency marker)", + "$": "dollar sign (currency marker)", + "IN": "Preposition/subord. conjunction", + "SYM": "Symbol (mathematical or scientific)", + "VBG": "Verb, gerund/present participle", + "VBP": "Verb, non-3rd ps. sing. present", + ":": "colon", + } + + #: Contents for the help box. This is a list of tuples, one for + #: each help page, where each tuple has four elements: + #: - A title (displayed as a tab) + #: - A string description of tabstops (see Tkinter.Text for details) + #: - The text contents for the help page. You can use expressions + #: like ... to colorize the text; see ``HELP_AUTOTAG`` + #: for a list of tags you can use for colorizing. + HELP = [ + ( + "Help", + "20", + "Welcome to the regular expression chunk-parser grammar editor. " + "You can use this editor to develop and test chunk parser grammars " + "based on NLTK's RegexpChunkParser class.\n\n" + # Help box. + "Use this box ('Help') to learn more about the editor; click on the " + "tabs for help on specific topics:" + "\n" + "Rules: grammar rule types\n" + "Regexps: regular expression syntax\n" + "Tags: part of speech tags\n\n" + # Grammar. + "Use the upper-left box ('Grammar') to edit your grammar. " + "Each line of your grammar specifies a single 'rule', " + "which performs an action such as creating a chunk or merging " + "two chunks.\n\n" + # Dev set. + "The lower-left box ('Development Set') runs your grammar on the " + "development set, and displays the results. " + "Your grammar's chunks are highlighted, and " + "the correct (gold standard) chunks are " + "underlined. If they " + "match, they are displayed in green; otherwise, " + "they are displayed in red. The box displays a single " + "sentence from the development set at a time; use the scrollbar or " + "the next/previous buttons view additional sentences.\n\n" + # Performance + "The lower-right box ('Evaluation') tracks the performance of " + "your grammar on the development set. The 'precision' axis " + "indicates how many of your grammar's chunks are correct; and " + "the 'recall' axis indicates how many of the gold standard " + "chunks your system generated. Typically, you should try to " + "design a grammar that scores high on both metrics. The " + "exact precision and recall of the current grammar, as well " + "as their harmonic mean (the 'f-score'), are displayed in " + "the status bar at the bottom of the window.", + ), + ( + "Rules", + "10", + "

    {...regexp...}

    " + "\nChunk rule: creates new chunks from words matching " + "regexp.\n\n" + "

    }...regexp...{

    " + "\nStrip rule: removes words matching regexp from existing " + "chunks.\n\n" + "

    ...regexp1...}{...regexp2...

    " + "\nSplit rule: splits chunks that match regexp1 followed by " + "regexp2 in two.\n\n" + "

    ...regexp...{}...regexp...

    " + "\nMerge rule: joins consecutive chunks that match regexp1 " + "and regexp2\n", + ), + ( + "Regexps", + "10 60", + # "Regular Expression Syntax Summary:\n\n" + "

    Pattern\t\tMatches...

    \n" + "" + "\t<T>\ta word with tag T " + "(where T may be a regexp).\n" + "\tx?\tan optional x\n" + "\tx+\ta sequence of 1 or more x's\n" + "\tx*\ta sequence of 0 or more x's\n" + "\tx|y\tx or y\n" + "\t.\tmatches any character\n" + "\t(x)\tTreats x as a group\n" + "\t# x...\tTreats x... " + "(to the end of the line) as a comment\n" + "\t\\C\tmatches character C " + "(useful when C is a special character " + "like + or #)\n" + "" + "\n

    Examples:

    \n" + "" + "\t\n" + '\t\tMatches "cow/NN"\n' + '\t\tMatches "green/NN"\n' + "\t\n" + '\t\tMatches "eating/VBG"\n' + '\t\tMatches "ate/VBD"\n' + "\t
    \n" + '\t\tMatches "on/IN the/DT car/NN"\n' + "\t?\n" + '\t\tMatches "ran/VBD"\n' + '\t\tMatches "slowly/RB ate/VBD"\n' + r"\t<\#> # This is a comment...\n" + '\t\tMatches "#/# 100/CD"\n' + "", + ), + ( + "Tags", + "10 60", + "

    Part of Speech Tags:

    \n" + + "" + + "<>" + + "\n", # this gets auto-substituted w/ self.TAGSET + ), + ] + + HELP_AUTOTAG = [ + ("red", dict(foreground="#a00")), + ("green", dict(foreground="#080")), + ("highlight", dict(background="#ddd")), + ("underline", dict(underline=True)), + ("h1", dict(underline=True)), + ("indent", dict(lmargin1=20, lmargin2=20)), + ("hangindent", dict(lmargin1=0, lmargin2=60)), + ("var", dict(foreground="#88f")), + ("regexp", dict(foreground="#ba7")), + ("match", dict(foreground="#6a6")), + ] + + ##///////////////////////////////////////////////////////////////// + ## Config Parameters + ##///////////////////////////////////////////////////////////////// + + _EVAL_DELAY = 1 + """If the user has not pressed any key for this amount of time (in + seconds), and the current grammar has not been evaluated, then + the eval demon will evaluate it.""" + + _EVAL_CHUNK = 15 + """The number of sentences that should be evaluated by the eval + demon each time it runs.""" + _EVAL_FREQ = 0.2 + """The frequency (in seconds) at which the eval demon is run""" + _EVAL_DEMON_MIN = 0.02 + """The minimum amount of time that the eval demon should take each time + it runs -- if it takes less than this time, _EVAL_CHUNK will be + modified upwards.""" + _EVAL_DEMON_MAX = 0.04 + """The maximum amount of time that the eval demon should take each time + it runs -- if it takes more than this time, _EVAL_CHUNK will be + modified downwards.""" + + _GRAMMARBOX_PARAMS = dict( + width=40, + height=12, + background="#efe", + highlightbackground="#efe", + highlightthickness=1, + relief="groove", + border=2, + wrap="word", + ) + _HELPBOX_PARAMS = dict( + width=15, + height=15, + background="#efe", + highlightbackground="#efe", + foreground="#555", + highlightthickness=1, + relief="groove", + border=2, + wrap="word", + ) + _DEVSETBOX_PARAMS = dict( + width=70, + height=10, + background="#eef", + highlightbackground="#eef", + highlightthickness=1, + relief="groove", + border=2, + wrap="word", + tabs=(30,), + ) + _STATUS_PARAMS = dict(background="#9bb", relief="groove", border=2) + _FONT_PARAMS = dict(family="helvetica", size=-20) + _FRAME_PARAMS = dict(background="#777", padx=2, pady=2, border=3) + _EVALBOX_PARAMS = dict( + background="#eef", + highlightbackground="#eef", + highlightthickness=1, + relief="groove", + border=2, + width=300, + height=280, + ) + _BUTTON_PARAMS = dict( + background="#777", activebackground="#777", highlightbackground="#777" + ) + _HELPTAB_BG_COLOR = "#aba" + _HELPTAB_FG_COLOR = "#efe" + + _HELPTAB_FG_PARAMS = dict(background="#efe") + _HELPTAB_BG_PARAMS = dict(background="#aba") + _HELPTAB_SPACER = 6 + + def normalize_grammar(self, grammar): + # Strip comments + grammar = re.sub(r"((\\.|[^#])*)(#.*)?", r"\1", grammar) + # Normalize whitespace + grammar = re.sub(" +", " ", grammar) + grammar = re.sub(r"\n\s+", r"\n", grammar) + grammar = grammar.strip() + # [xx] Hack: automatically backslash $! + grammar = re.sub(r"([^\\])\$", r"\1\\$", grammar) + return grammar + + def __init__( + self, + devset_name="conll2000", + devset=None, + grammar="", + chunk_label="NP", + tagset=None, + ): + """ + :param devset_name: The name of the development set; used for + display & for save files. If either the name 'treebank' + or the name 'conll2000' is used, and devset is None, then + devset will be set automatically. + :param devset: A list of chunked sentences + :param grammar: The initial grammar to display. + :param tagset: Dictionary from tags to string descriptions, used + for the help page. Defaults to ``self.TAGSET``. + """ + self._chunk_label = chunk_label + + if tagset is None: + tagset = self.TAGSET + self.tagset = tagset + + # Named development sets: + if devset is None: + if devset_name == "conll2000": + devset = conll2000.chunked_sents("train.txt") # [:100] + elif devset == "treebank": + devset = treebank_chunk.chunked_sents() # [:100] + else: + raise ValueError("Unknown development set %s" % devset_name) + + self.chunker = None + """The chunker built from the grammar string""" + + self.grammar = grammar + """The unparsed grammar string""" + + self.normalized_grammar = None + """A normalized version of ``self.grammar``.""" + + self.grammar_changed = 0 + """The last time() that the grammar was changed.""" + + self.devset = devset + """The development set -- a list of chunked sentences.""" + + self.devset_name = devset_name + """The name of the development set (for save files).""" + + self.devset_index = -1 + """The index into the development set of the first instance + that's currently being viewed.""" + + self._last_keypress = 0 + """The time() when a key was most recently pressed""" + + self._history = [] + """A list of (grammar, precision, recall, fscore) tuples for + grammars that the user has already tried.""" + + self._history_index = 0 + """When the user is scrolling through previous grammars, this + is used to keep track of which grammar they're looking at.""" + + self._eval_grammar = None + """The grammar that is being currently evaluated by the eval + demon.""" + + self._eval_normalized_grammar = None + """A normalized copy of ``_eval_grammar``.""" + + self._eval_index = 0 + """The index of the next sentence in the development set that + should be looked at by the eval demon.""" + + self._eval_score = ChunkScore(chunk_label=chunk_label) + """The ``ChunkScore`` object that's used to keep track of the score + of the current grammar on the development set.""" + + # Set up the main window. + top = self.top = Tk() + top.geometry("+50+50") + top.title("Regexp Chunk Parser App") + top.bind("", self.destroy) + + # Variable that restricts how much of the devset we look at. + self._devset_size = IntVar(top) + self._devset_size.set(100) + + # Set up all the tkinter widgets + self._init_fonts(top) + self._init_widgets(top) + self._init_bindings(top) + self._init_menubar(top) + self.grammarbox.focus() + + # If a grammar was given, then display it. + if grammar: + self.grammarbox.insert("end", grammar + "\n") + self.grammarbox.mark_set("insert", "1.0") + + # Display the first item in the development set + self.show_devset(0) + self.update() + + def _init_bindings(self, top): + top.bind("", self._devset_next) + top.bind("", self._devset_prev) + top.bind("", self.toggle_show_trace) + top.bind("", self.update) + top.bind("", lambda e: self.save_grammar()) + top.bind("", lambda e: self.load_grammar()) + self.grammarbox.bind("", self.toggle_show_trace) + self.grammarbox.bind("", self._devset_next) + self.grammarbox.bind("", self._devset_prev) + + # Redraw the eval graph when the window size changes + self.evalbox.bind("", self._eval_plot) + + def _init_fonts(self, top): + # TWhat's our font size (default=same as sysfont) + self._size = IntVar(top) + self._size.set(20) + self._font = Font(family="helvetica", size=-self._size.get()) + self._smallfont = Font( + family="helvetica", size=-(int(self._size.get() * 14 // 20)) + ) + + def _init_menubar(self, parent): + menubar = Menu(parent) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command(label="Reset Application", underline=0, command=self.reset) + filemenu.add_command( + label="Save Current Grammar", + underline=0, + accelerator="Ctrl-s", + command=self.save_grammar, + ) + filemenu.add_command( + label="Load Grammar", + underline=0, + accelerator="Ctrl-o", + command=self.load_grammar, + ) + + filemenu.add_command( + label="Save Grammar History", underline=13, command=self.save_history + ) + + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + viewmenu = Menu(menubar, tearoff=0) + viewmenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=16, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=20, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=24, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=34, + command=self.resize, + ) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + devsetmenu = Menu(menubar, tearoff=0) + devsetmenu.add_radiobutton( + label="50 sentences", + variable=self._devset_size, + value=50, + command=self.set_devset_size, + ) + devsetmenu.add_radiobutton( + label="100 sentences", + variable=self._devset_size, + value=100, + command=self.set_devset_size, + ) + devsetmenu.add_radiobutton( + label="200 sentences", + variable=self._devset_size, + value=200, + command=self.set_devset_size, + ) + devsetmenu.add_radiobutton( + label="500 sentences", + variable=self._devset_size, + value=500, + command=self.set_devset_size, + ) + menubar.add_cascade(label="Development-Set", underline=0, menu=devsetmenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + + parent.config(menu=menubar) + + def toggle_show_trace(self, *e): + if self._showing_trace: + self.show_devset() + else: + self.show_trace() + return "break" + + _SCALE_N = 5 # center on the last 5 examples. + _DRAW_LINES = False + + def _eval_plot(self, *e, **config): + width = config.get("width", self.evalbox.winfo_width()) + height = config.get("height", self.evalbox.winfo_height()) + + # Clear the canvas + self.evalbox.delete("all") + + # Draw the precision & recall labels. + tag = self.evalbox.create_text( + 10, height // 2 - 10, justify="left", anchor="w", text="Precision" + ) + left, right = self.evalbox.bbox(tag)[2] + 5, width - 10 + tag = self.evalbox.create_text( + left + (width - left) // 2, + height - 10, + anchor="s", + text="Recall", + justify="center", + ) + top, bot = 10, self.evalbox.bbox(tag)[1] - 10 + + # Draw masks for clipping the plot. + bg = self._EVALBOX_PARAMS["background"] + self.evalbox.lower( + self.evalbox.create_rectangle(0, 0, left - 1, 5000, fill=bg, outline=bg) + ) + self.evalbox.lower( + self.evalbox.create_rectangle(0, bot + 1, 5000, 5000, fill=bg, outline=bg) + ) + + # Calculate the plot's scale. + if self._autoscale.get() and len(self._history) > 1: + max_precision = max_recall = 0 + min_precision = min_recall = 1 + for i in range(1, min(len(self._history), self._SCALE_N + 1)): + grammar, precision, recall, fmeasure = self._history[-i] + min_precision = min(precision, min_precision) + min_recall = min(recall, min_recall) + max_precision = max(precision, max_precision) + max_recall = max(recall, max_recall) + # if max_precision-min_precision > max_recall-min_recall: + # min_recall -= (max_precision-min_precision)/2 + # max_recall += (max_precision-min_precision)/2 + # else: + # min_precision -= (max_recall-min_recall)/2 + # max_precision += (max_recall-min_recall)/2 + # if min_recall < 0: + # max_recall -= min_recall + # min_recall = 0 + # if min_precision < 0: + # max_precision -= min_precision + # min_precision = 0 + min_precision = max(min_precision - 0.01, 0) + min_recall = max(min_recall - 0.01, 0) + max_precision = min(max_precision + 0.01, 1) + max_recall = min(max_recall + 0.01, 1) + else: + min_precision = min_recall = 0 + max_precision = max_recall = 1 + + # Draw the axis lines & grid lines + for i in range(11): + x = left + (right - left) * ( + (i / 10.0 - min_recall) / (max_recall - min_recall) + ) + y = bot - (bot - top) * ( + (i / 10.0 - min_precision) / (max_precision - min_precision) + ) + if left < x < right: + self.evalbox.create_line(x, top, x, bot, fill="#888") + if top < y < bot: + self.evalbox.create_line(left, y, right, y, fill="#888") + self.evalbox.create_line(left, top, left, bot) + self.evalbox.create_line(left, bot, right, bot) + + # Display the plot's scale + self.evalbox.create_text( + left - 3, + bot, + justify="right", + anchor="se", + text="%d%%" % (100 * min_precision), + ) + self.evalbox.create_text( + left - 3, + top, + justify="right", + anchor="ne", + text="%d%%" % (100 * max_precision), + ) + self.evalbox.create_text( + left, + bot + 3, + justify="center", + anchor="nw", + text="%d%%" % (100 * min_recall), + ) + self.evalbox.create_text( + right, + bot + 3, + justify="center", + anchor="ne", + text="%d%%" % (100 * max_recall), + ) + + # Display the scores. + prev_x = prev_y = None + for i, (_, precision, recall, fscore) in enumerate(self._history): + x = left + (right - left) * ( + (recall - min_recall) / (max_recall - min_recall) + ) + y = bot - (bot - top) * ( + (precision - min_precision) / (max_precision - min_precision) + ) + if i == self._history_index: + self.evalbox.create_oval( + x - 2, y - 2, x + 2, y + 2, fill="#0f0", outline="#000" + ) + self.status["text"] = ( + "Precision: %.2f%%\t" % (precision * 100) + + "Recall: %.2f%%\t" % (recall * 100) + + "F-score: %.2f%%" % (fscore * 100) + ) + else: + self.evalbox.lower( + self.evalbox.create_oval( + x - 2, y - 2, x + 2, y + 2, fill="#afa", outline="#8c8" + ) + ) + if prev_x is not None and self._eval_lines.get(): + self.evalbox.lower( + self.evalbox.create_line(prev_x, prev_y, x, y, fill="#8c8") + ) + prev_x, prev_y = x, y + + _eval_demon_running = False + + def _eval_demon(self): + if self.top is None: + return + if self.chunker is None: + self._eval_demon_running = False + return + + # Note our starting time. + t0 = time.time() + + # If are still typing, then wait for them to finish. + if ( + time.time() - self._last_keypress < self._EVAL_DELAY + and self.normalized_grammar != self._eval_normalized_grammar + ): + self._eval_demon_running = True + return self.top.after(int(self._EVAL_FREQ * 1000), self._eval_demon) + + # If the grammar changed, restart the evaluation. + if self.normalized_grammar != self._eval_normalized_grammar: + # Check if we've seen this grammar already. If so, then + # just use the old evaluation values. + for (g, p, r, f) in self._history: + if self.normalized_grammar == self.normalize_grammar(g): + self._history.append((g, p, r, f)) + self._history_index = len(self._history) - 1 + self._eval_plot() + self._eval_demon_running = False + self._eval_normalized_grammar = None + return + self._eval_index = 0 + self._eval_score = ChunkScore(chunk_label=self._chunk_label) + self._eval_grammar = self.grammar + self._eval_normalized_grammar = self.normalized_grammar + + # If the grammar is empty, the don't bother evaluating it, or + # recording it in history -- the score will just be 0. + if self.normalized_grammar.strip() == "": + # self._eval_index = self._devset_size.get() + self._eval_demon_running = False + return + + # Score the next set of examples + for gold in self.devset[ + self._eval_index : min( + self._eval_index + self._EVAL_CHUNK, self._devset_size.get() + ) + ]: + guess = self._chunkparse(gold.leaves()) + self._eval_score.score(gold, guess) + + # update our index in the devset. + self._eval_index += self._EVAL_CHUNK + + # Check if we're done + if self._eval_index >= self._devset_size.get(): + self._history.append( + ( + self._eval_grammar, + self._eval_score.precision(), + self._eval_score.recall(), + self._eval_score.f_measure(), + ) + ) + self._history_index = len(self._history) - 1 + self._eval_plot() + self._eval_demon_running = False + self._eval_normalized_grammar = None + else: + progress = 100 * self._eval_index / self._devset_size.get() + self.status["text"] = "Evaluating on Development Set (%d%%)" % progress + self._eval_demon_running = True + self._adaptively_modify_eval_chunk(time.time() - t0) + self.top.after(int(self._EVAL_FREQ * 1000), self._eval_demon) + + def _adaptively_modify_eval_chunk(self, t): + """ + Modify _EVAL_CHUNK to try to keep the amount of time that the + eval demon takes between _EVAL_DEMON_MIN and _EVAL_DEMON_MAX. + + :param t: The amount of time that the eval demon took. + """ + if t > self._EVAL_DEMON_MAX and self._EVAL_CHUNK > 5: + self._EVAL_CHUNK = min( + self._EVAL_CHUNK - 1, + max( + int(self._EVAL_CHUNK * (self._EVAL_DEMON_MAX / t)), + self._EVAL_CHUNK - 10, + ), + ) + elif t < self._EVAL_DEMON_MIN: + self._EVAL_CHUNK = max( + self._EVAL_CHUNK + 1, + min( + int(self._EVAL_CHUNK * (self._EVAL_DEMON_MIN / t)), + self._EVAL_CHUNK + 10, + ), + ) + + def _init_widgets(self, top): + frame0 = Frame(top, **self._FRAME_PARAMS) + frame0.grid_columnconfigure(0, weight=4) + frame0.grid_columnconfigure(3, weight=2) + frame0.grid_rowconfigure(1, weight=1) + frame0.grid_rowconfigure(5, weight=1) + + # The grammar + self.grammarbox = Text(frame0, font=self._font, **self._GRAMMARBOX_PARAMS) + self.grammarlabel = Label( + frame0, + font=self._font, + text="Grammar:", + highlightcolor="black", + background=self._GRAMMARBOX_PARAMS["background"], + ) + self.grammarlabel.grid(column=0, row=0, sticky="SW") + self.grammarbox.grid(column=0, row=1, sticky="NEWS") + + # Scroll bar for grammar + grammar_scrollbar = Scrollbar(frame0, command=self.grammarbox.yview) + grammar_scrollbar.grid(column=1, row=1, sticky="NWS") + self.grammarbox.config(yscrollcommand=grammar_scrollbar.set) + + # grammar buttons + bg = self._FRAME_PARAMS["background"] + frame3 = Frame(frame0, background=bg) + frame3.grid(column=0, row=2, sticky="EW") + Button( + frame3, + text="Prev Grammar", + command=self._history_prev, + **self._BUTTON_PARAMS, + ).pack(side="left") + Button( + frame3, + text="Next Grammar", + command=self._history_next, + **self._BUTTON_PARAMS, + ).pack(side="left") + + # Help box + self.helpbox = Text(frame0, font=self._smallfont, **self._HELPBOX_PARAMS) + self.helpbox.grid(column=3, row=1, sticky="NEWS") + self.helptabs = {} + bg = self._FRAME_PARAMS["background"] + helptab_frame = Frame(frame0, background=bg) + helptab_frame.grid(column=3, row=0, sticky="SW") + for i, (tab, tabstops, text) in enumerate(self.HELP): + label = Label(helptab_frame, text=tab, font=self._smallfont) + label.grid(column=i * 2, row=0, sticky="S") + # help_frame.grid_columnconfigure(i, weight=1) + # label.pack(side='left') + label.bind("", lambda e, tab=tab: self.show_help(tab)) + self.helptabs[tab] = label + Frame( + helptab_frame, height=1, width=self._HELPTAB_SPACER, background=bg + ).grid(column=i * 2 + 1, row=0) + self.helptabs[self.HELP[0][0]].configure(font=self._font) + self.helpbox.tag_config("elide", elide=True) + for (tag, params) in self.HELP_AUTOTAG: + self.helpbox.tag_config("tag-%s" % tag, **params) + self.show_help(self.HELP[0][0]) + + # Scroll bar for helpbox + help_scrollbar = Scrollbar(frame0, command=self.helpbox.yview) + self.helpbox.config(yscrollcommand=help_scrollbar.set) + help_scrollbar.grid(column=4, row=1, sticky="NWS") + + # The dev set + frame4 = Frame(frame0, background=self._FRAME_PARAMS["background"]) + self.devsetbox = Text(frame4, font=self._font, **self._DEVSETBOX_PARAMS) + self.devsetbox.pack(expand=True, fill="both") + self.devsetlabel = Label( + frame0, + font=self._font, + text="Development Set:", + justify="right", + background=self._DEVSETBOX_PARAMS["background"], + ) + self.devsetlabel.grid(column=0, row=4, sticky="SW") + frame4.grid(column=0, row=5, sticky="NEWS") + + # dev set scrollbars + self.devset_scroll = Scrollbar(frame0, command=self._devset_scroll) + self.devset_scroll.grid(column=1, row=5, sticky="NWS") + self.devset_xscroll = Scrollbar( + frame4, command=self.devsetbox.xview, orient="horiz" + ) + self.devsetbox["xscrollcommand"] = self.devset_xscroll.set + self.devset_xscroll.pack(side="bottom", fill="x") + + # dev set buttons + bg = self._FRAME_PARAMS["background"] + frame1 = Frame(frame0, background=bg) + frame1.grid(column=0, row=7, sticky="EW") + Button( + frame1, + text="Prev Example (Ctrl-p)", + command=self._devset_prev, + **self._BUTTON_PARAMS, + ).pack(side="left") + Button( + frame1, + text="Next Example (Ctrl-n)", + command=self._devset_next, + **self._BUTTON_PARAMS, + ).pack(side="left") + self.devset_button = Button( + frame1, + text="Show example", + command=self.show_devset, + state="disabled", + **self._BUTTON_PARAMS, + ) + self.devset_button.pack(side="right") + self.trace_button = Button( + frame1, text="Show trace", command=self.show_trace, **self._BUTTON_PARAMS + ) + self.trace_button.pack(side="right") + + # evaluation box + self.evalbox = Canvas(frame0, **self._EVALBOX_PARAMS) + label = Label( + frame0, + font=self._font, + text="Evaluation:", + justify="right", + background=self._EVALBOX_PARAMS["background"], + ) + label.grid(column=3, row=4, sticky="SW") + self.evalbox.grid(column=3, row=5, sticky="NEWS", columnspan=2) + + # evaluation box buttons + bg = self._FRAME_PARAMS["background"] + frame2 = Frame(frame0, background=bg) + frame2.grid(column=3, row=7, sticky="EW") + self._autoscale = IntVar(self.top) + self._autoscale.set(False) + Checkbutton( + frame2, + variable=self._autoscale, + command=self._eval_plot, + text="Zoom", + **self._BUTTON_PARAMS, + ).pack(side="left") + self._eval_lines = IntVar(self.top) + self._eval_lines.set(False) + Checkbutton( + frame2, + variable=self._eval_lines, + command=self._eval_plot, + text="Lines", + **self._BUTTON_PARAMS, + ).pack(side="left") + Button(frame2, text="History", **self._BUTTON_PARAMS).pack(side="right") + + # The status label + self.status = Label(frame0, font=self._font, **self._STATUS_PARAMS) + self.status.grid(column=0, row=9, sticky="NEW", padx=3, pady=2, columnspan=5) + + # Help box & devset box can't be edited. + self.helpbox["state"] = "disabled" + self.devsetbox["state"] = "disabled" + + # Spacers + bg = self._FRAME_PARAMS["background"] + Frame(frame0, height=10, width=0, background=bg).grid(column=0, row=3) + Frame(frame0, height=0, width=10, background=bg).grid(column=2, row=0) + Frame(frame0, height=6, width=0, background=bg).grid(column=0, row=8) + + # pack the frame. + frame0.pack(fill="both", expand=True) + + # Set up colors for the devset box + self.devsetbox.tag_config("true-pos", background="#afa", underline="True") + self.devsetbox.tag_config("false-neg", underline="True", foreground="#800") + self.devsetbox.tag_config("false-pos", background="#faa") + self.devsetbox.tag_config("trace", foreground="#666", wrap="none") + self.devsetbox.tag_config("wrapindent", lmargin2=30, wrap="none") + self.devsetbox.tag_config("error", foreground="#800") + + # And for the grammarbox + self.grammarbox.tag_config("error", background="#fec") + self.grammarbox.tag_config("comment", foreground="#840") + self.grammarbox.tag_config("angle", foreground="#00f") + self.grammarbox.tag_config("brace", foreground="#0a0") + self.grammarbox.tag_config("hangindent", lmargin1=0, lmargin2=40) + + _showing_trace = False + + def show_trace(self, *e): + self._showing_trace = True + self.trace_button["state"] = "disabled" + self.devset_button["state"] = "normal" + + self.devsetbox["state"] = "normal" + # self.devsetbox['wrap'] = 'none' + self.devsetbox.delete("1.0", "end") + self.devsetlabel["text"] = "Development Set (%d/%d)" % ( + (self.devset_index + 1, self._devset_size.get()) + ) + + if self.chunker is None: + self.devsetbox.insert("1.0", "Trace: waiting for a valid grammar.") + self.devsetbox.tag_add("error", "1.0", "end") + return # can't do anything more + + gold_tree = self.devset[self.devset_index] + rules = self.chunker.rules() + + # Calculate the tag sequence + tagseq = "\t" + charnum = [1] + for wordnum, (word, pos) in enumerate(gold_tree.leaves()): + tagseq += "%s " % pos + charnum.append(len(tagseq)) + self.charnum = { + (i, j): charnum[j] + for i in range(len(rules) + 1) + for j in range(len(charnum)) + } + self.linenum = {i: i * 2 + 2 for i in range(len(rules) + 1)} + + for i in range(len(rules) + 1): + if i == 0: + self.devsetbox.insert("end", "Start:\n") + self.devsetbox.tag_add("trace", "end -2c linestart", "end -2c") + else: + self.devsetbox.insert("end", "Apply %s:\n" % rules[i - 1]) + self.devsetbox.tag_add("trace", "end -2c linestart", "end -2c") + # Display the tag sequence. + self.devsetbox.insert("end", tagseq + "\n") + self.devsetbox.tag_add("wrapindent", "end -2c linestart", "end -2c") + # Run a partial parser, and extract gold & test chunks + chunker = RegexpChunkParser(rules[:i]) + test_tree = self._chunkparse(gold_tree.leaves()) + gold_chunks = self._chunks(gold_tree) + test_chunks = self._chunks(test_tree) + # Compare them. + for chunk in gold_chunks.intersection(test_chunks): + self._color_chunk(i, chunk, "true-pos") + for chunk in gold_chunks - test_chunks: + self._color_chunk(i, chunk, "false-neg") + for chunk in test_chunks - gold_chunks: + self._color_chunk(i, chunk, "false-pos") + self.devsetbox.insert("end", "Finished.\n") + self.devsetbox.tag_add("trace", "end -2c linestart", "end -2c") + + # This is a hack, because the x-scrollbar isn't updating its + # position right -- I'm not sure what the underlying cause is + # though. (This is on OS X w/ python 2.5) + self.top.after(100, self.devset_xscroll.set, 0, 0.3) + + def show_help(self, tab): + self.helpbox["state"] = "normal" + self.helpbox.delete("1.0", "end") + for (name, tabstops, text) in self.HELP: + if name == tab: + text = text.replace( + "<>", + "\n".join( + "\t%s\t%s" % item + for item in sorted( + list(self.tagset.items()), + key=lambda t_w: re.match(r"\w+", t_w[0]) + and (0, t_w[0]) + or (1, t_w[0]), + ) + ), + ) + + self.helptabs[name].config(**self._HELPTAB_FG_PARAMS) + self.helpbox.config(tabs=tabstops) + self.helpbox.insert("1.0", text + "\n" * 20) + C = "1.0 + %d chars" + for (tag, params) in self.HELP_AUTOTAG: + pattern = f"(?s)(<{tag}>)(.*?)()" + for m in re.finditer(pattern, text): + self.helpbox.tag_add("elide", C % m.start(1), C % m.end(1)) + self.helpbox.tag_add( + "tag-%s" % tag, C % m.start(2), C % m.end(2) + ) + self.helpbox.tag_add("elide", C % m.start(3), C % m.end(3)) + else: + self.helptabs[name].config(**self._HELPTAB_BG_PARAMS) + self.helpbox["state"] = "disabled" + + def _history_prev(self, *e): + self._view_history(self._history_index - 1) + return "break" + + def _history_next(self, *e): + self._view_history(self._history_index + 1) + return "break" + + def _view_history(self, index): + # Bounds & sanity checking: + index = max(0, min(len(self._history) - 1, index)) + if not self._history: + return + # Already viewing the requested history item? + if index == self._history_index: + return + # Show the requested grammar. It will get added to _history + # only if they edit it (causing self.update() to get run.) + self.grammarbox["state"] = "normal" + self.grammarbox.delete("1.0", "end") + self.grammarbox.insert("end", self._history[index][0]) + self.grammarbox.mark_set("insert", "1.0") + self._history_index = index + self._syntax_highlight_grammar(self._history[index][0]) + # Record the normalized grammar & regenerate the chunker. + self.normalized_grammar = self.normalize_grammar(self._history[index][0]) + if self.normalized_grammar: + rules = [ + RegexpChunkRule.fromstring(line) + for line in self.normalized_grammar.split("\n") + ] + else: + rules = [] + self.chunker = RegexpChunkParser(rules) + # Show the score. + self._eval_plot() + # Update the devset box + self._highlight_devset() + if self._showing_trace: + self.show_trace() + # Update the grammar label + if self._history_index < len(self._history) - 1: + self.grammarlabel["text"] = "Grammar {}/{}:".format( + self._history_index + 1, + len(self._history), + ) + else: + self.grammarlabel["text"] = "Grammar:" + + def _devset_next(self, *e): + self._devset_scroll("scroll", 1, "page") + return "break" + + def _devset_prev(self, *e): + self._devset_scroll("scroll", -1, "page") + return "break" + + def destroy(self, *e): + if self.top is None: + return + self.top.destroy() + self.top = None + + def _devset_scroll(self, command, *args): + N = 1 # size of a page -- one sentence. + showing_trace = self._showing_trace + if command == "scroll" and args[1].startswith("unit"): + self.show_devset(self.devset_index + int(args[0])) + elif command == "scroll" and args[1].startswith("page"): + self.show_devset(self.devset_index + N * int(args[0])) + elif command == "moveto": + self.show_devset(int(float(args[0]) * self._devset_size.get())) + else: + assert 0, f"bad scroll command {command} {args}" + if showing_trace: + self.show_trace() + + def show_devset(self, index=None): + if index is None: + index = self.devset_index + + # Bounds checking + index = min(max(0, index), self._devset_size.get() - 1) + + if index == self.devset_index and not self._showing_trace: + return + self.devset_index = index + + self._showing_trace = False + self.trace_button["state"] = "normal" + self.devset_button["state"] = "disabled" + + # Clear the text box. + self.devsetbox["state"] = "normal" + self.devsetbox["wrap"] = "word" + self.devsetbox.delete("1.0", "end") + self.devsetlabel["text"] = "Development Set (%d/%d)" % ( + (self.devset_index + 1, self._devset_size.get()) + ) + + # Add the sentences + sample = self.devset[self.devset_index : self.devset_index + 1] + self.charnum = {} + self.linenum = {0: 1} + for sentnum, sent in enumerate(sample): + linestr = "" + for wordnum, (word, pos) in enumerate(sent.leaves()): + self.charnum[sentnum, wordnum] = len(linestr) + linestr += f"{word}/{pos} " + self.charnum[sentnum, wordnum + 1] = len(linestr) + self.devsetbox.insert("end", linestr[:-1] + "\n\n") + + # Highlight chunks in the dev set + if self.chunker is not None: + self._highlight_devset() + self.devsetbox["state"] = "disabled" + + # Update the scrollbar + first = self.devset_index / self._devset_size.get() + last = (self.devset_index + 2) / self._devset_size.get() + self.devset_scroll.set(first, last) + + def _chunks(self, tree): + chunks = set() + wordnum = 0 + for child in tree: + if isinstance(child, Tree): + if child.label() == self._chunk_label: + chunks.add((wordnum, wordnum + len(child))) + wordnum += len(child) + else: + wordnum += 1 + return chunks + + def _syntax_highlight_grammar(self, grammar): + if self.top is None: + return + self.grammarbox.tag_remove("comment", "1.0", "end") + self.grammarbox.tag_remove("angle", "1.0", "end") + self.grammarbox.tag_remove("brace", "1.0", "end") + self.grammarbox.tag_add("hangindent", "1.0", "end") + for lineno, line in enumerate(grammar.split("\n")): + if not line.strip(): + continue + m = re.match(r"(\\.|[^#])*(#.*)?", line) + comment_start = None + if m.group(2): + comment_start = m.start(2) + s = "%d.%d" % (lineno + 1, m.start(2)) + e = "%d.%d" % (lineno + 1, m.end(2)) + self.grammarbox.tag_add("comment", s, e) + for m in re.finditer("[<>{}]", line): + if comment_start is not None and m.start() >= comment_start: + break + s = "%d.%d" % (lineno + 1, m.start()) + e = "%d.%d" % (lineno + 1, m.end()) + if m.group() in "<>": + self.grammarbox.tag_add("angle", s, e) + else: + self.grammarbox.tag_add("brace", s, e) + + def _grammarcheck(self, grammar): + if self.top is None: + return + self.grammarbox.tag_remove("error", "1.0", "end") + self._grammarcheck_errs = [] + for lineno, line in enumerate(grammar.split("\n")): + line = re.sub(r"((\\.|[^#])*)(#.*)?", r"\1", line) + line = line.strip() + if line: + try: + RegexpChunkRule.fromstring(line) + except ValueError as e: + self.grammarbox.tag_add( + "error", "%s.0" % (lineno + 1), "%s.0 lineend" % (lineno + 1) + ) + self.status["text"] = "" + + def update(self, *event): + # Record when update was called (for grammarcheck) + if event: + self._last_keypress = time.time() + + # Read the grammar from the Text box. + self.grammar = grammar = self.grammarbox.get("1.0", "end") + + # If the grammar hasn't changed, do nothing: + normalized_grammar = self.normalize_grammar(grammar) + if normalized_grammar == self.normalized_grammar: + return + else: + self.normalized_grammar = normalized_grammar + + # If the grammar has changed, and we're looking at history, + # then stop looking at history. + if self._history_index < len(self._history) - 1: + self.grammarlabel["text"] = "Grammar:" + + self._syntax_highlight_grammar(grammar) + + # The grammar has changed; try parsing it. If it doesn't + # parse, do nothing. (flag error location?) + try: + # Note: the normalized grammar has no blank lines. + if normalized_grammar: + rules = [ + RegexpChunkRule.fromstring(line) + for line in normalized_grammar.split("\n") + ] + else: + rules = [] + except ValueError as e: + # Use the un-normalized grammar for error highlighting. + self._grammarcheck(grammar) + self.chunker = None + return + + self.chunker = RegexpChunkParser(rules) + self.grammarbox.tag_remove("error", "1.0", "end") + self.grammar_changed = time.time() + # Display the results + if self._showing_trace: + self.show_trace() + else: + self._highlight_devset() + # Start the eval demon + if not self._eval_demon_running: + self._eval_demon() + + def _highlight_devset(self, sample=None): + if sample is None: + sample = self.devset[self.devset_index : self.devset_index + 1] + + self.devsetbox.tag_remove("true-pos", "1.0", "end") + self.devsetbox.tag_remove("false-neg", "1.0", "end") + self.devsetbox.tag_remove("false-pos", "1.0", "end") + + # Run the grammar on the test cases. + for sentnum, gold_tree in enumerate(sample): + # Run the chunk parser + test_tree = self._chunkparse(gold_tree.leaves()) + # Extract gold & test chunks + gold_chunks = self._chunks(gold_tree) + test_chunks = self._chunks(test_tree) + # Compare them. + for chunk in gold_chunks.intersection(test_chunks): + self._color_chunk(sentnum, chunk, "true-pos") + for chunk in gold_chunks - test_chunks: + self._color_chunk(sentnum, chunk, "false-neg") + for chunk in test_chunks - gold_chunks: + self._color_chunk(sentnum, chunk, "false-pos") + + def _chunkparse(self, words): + try: + return self.chunker.parse(words) + except (ValueError, IndexError) as e: + # There's an error somewhere in the grammar, but we're not sure + # exactly where, so just mark the whole grammar as bad. + # E.g., this is caused by: "({})" + self.grammarbox.tag_add("error", "1.0", "end") + # Treat it as tagging nothing: + return words + + def _color_chunk(self, sentnum, chunk, tag): + start, end = chunk + self.devsetbox.tag_add( + tag, + f"{self.linenum[sentnum]}.{self.charnum[sentnum, start]}", + f"{self.linenum[sentnum]}.{self.charnum[sentnum, end] - 1}", + ) + + def reset(self): + # Clear various variables + self.chunker = None + self.grammar = None + self.normalized_grammar = None + self.grammar_changed = 0 + self._history = [] + self._history_index = 0 + # Update the on-screen display. + self.grammarbox.delete("1.0", "end") + self.show_devset(0) + self.update() + # self._eval_plot() + + SAVE_GRAMMAR_TEMPLATE = ( + "# Regexp Chunk Parsing Grammar\n" + "# Saved %(date)s\n" + "#\n" + "# Development set: %(devset)s\n" + "# Precision: %(precision)s\n" + "# Recall: %(recall)s\n" + "# F-score: %(fscore)s\n\n" + "%(grammar)s\n" + ) + + def save_grammar(self, filename=None): + if not filename: + ftypes = [("Chunk Gramamr", ".chunk"), ("All files", "*")] + filename = asksaveasfilename(filetypes=ftypes, defaultextension=".chunk") + if not filename: + return + if self._history and self.normalized_grammar == self.normalize_grammar( + self._history[-1][0] + ): + precision, recall, fscore = ( + "%.2f%%" % (100 * v) for v in self._history[-1][1:] + ) + elif self.chunker is None: + precision = recall = fscore = "Grammar not well formed" + else: + precision = recall = fscore = "Not finished evaluation yet" + + with open(filename, "w") as outfile: + outfile.write( + self.SAVE_GRAMMAR_TEMPLATE + % dict( + date=time.ctime(), + devset=self.devset_name, + precision=precision, + recall=recall, + fscore=fscore, + grammar=self.grammar.strip(), + ) + ) + + def load_grammar(self, filename=None): + if not filename: + ftypes = [("Chunk Gramamr", ".chunk"), ("All files", "*")] + filename = askopenfilename(filetypes=ftypes, defaultextension=".chunk") + if not filename: + return + self.grammarbox.delete("1.0", "end") + self.update() + with open(filename) as infile: + grammar = infile.read() + grammar = re.sub( + r"^\# Regexp Chunk Parsing Grammar[\s\S]*" "F-score:.*\n", "", grammar + ).lstrip() + self.grammarbox.insert("1.0", grammar) + self.update() + + def save_history(self, filename=None): + if not filename: + ftypes = [("Chunk Gramamr History", ".txt"), ("All files", "*")] + filename = asksaveasfilename(filetypes=ftypes, defaultextension=".txt") + if not filename: + return + + with open(filename, "w") as outfile: + outfile.write("# Regexp Chunk Parsing Grammar History\n") + outfile.write("# Saved %s\n" % time.ctime()) + outfile.write("# Development set: %s\n" % self.devset_name) + for i, (g, p, r, f) in enumerate(self._history): + hdr = ( + "Grammar %d/%d (precision=%.2f%%, recall=%.2f%%, " + "fscore=%.2f%%)" + % (i + 1, len(self._history), p * 100, r * 100, f * 100) + ) + outfile.write("\n%s\n" % hdr) + outfile.write("".join(" %s\n" % line for line in g.strip().split())) + + if not ( + self._history + and self.normalized_grammar + == self.normalize_grammar(self._history[-1][0]) + ): + if self.chunker is None: + outfile.write("\nCurrent Grammar (not well-formed)\n") + else: + outfile.write("\nCurrent Grammar (not evaluated)\n") + outfile.write( + "".join(" %s\n" % line for line in self.grammar.strip().split()) + ) + + def about(self, *e): + ABOUT = "NLTK RegExp Chunk Parser Application\n" + "Written by Edward Loper" + TITLE = "About: Regular Expression Chunk Parser Application" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE).show() + except: + ShowText(self.top, TITLE, ABOUT) + + def set_devset_size(self, size=None): + if size is not None: + self._devset_size.set(size) + self._devset_size.set(min(len(self.devset), self._devset_size.get())) + self.show_devset(1) + self.show_devset(0) + # what about history? Evaluated at diff dev set sizes! + + def resize(self, size=None): + if size is not None: + self._size.set(size) + size = self._size.get() + self._font.configure(size=-(abs(size))) + self._smallfont.configure(size=min(-10, -(abs(size)) * 14 // 20)) + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self.top.mainloop(*args, **kwargs) + + +def app(): + RegexpChunkApp().mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/collocations_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/collocations_app.py new file mode 100644 index 0000000..2d7da0a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/collocations_app.py @@ -0,0 +1,438 @@ +# Natural Language Toolkit: Collocations Application +# Much of the GUI code is imported from concordance.py; We intend to merge these tools together +# Copyright (C) 2001-2023 NLTK Project +# Author: Sumukh Ghodke +# URL: +# For license information, see LICENSE.TXT +# + + +import queue as q +import threading +from tkinter import ( + END, + LEFT, + SUNKEN, + Button, + Frame, + IntVar, + Label, + Menu, + OptionMenu, + Scrollbar, + StringVar, + Text, + Tk, +) +from tkinter.font import Font + +from nltk.corpus import ( + alpino, + brown, + cess_cat, + cess_esp, + floresta, + indian, + mac_morpho, + machado, + nps_chat, + sinica_treebank, + treebank, +) +from nltk.probability import FreqDist +from nltk.util import in_idle + +CORPUS_LOADED_EVENT = "<>" +ERROR_LOADING_CORPUS_EVENT = "<>" +POLL_INTERVAL = 100 + +_DEFAULT = "English: Brown Corpus (Humor)" +_CORPORA = { + "Catalan: CESS-CAT Corpus": lambda: cess_cat.words(), + "English: Brown Corpus": lambda: brown.words(), + "English: Brown Corpus (Press)": lambda: brown.words( + categories=["news", "editorial", "reviews"] + ), + "English: Brown Corpus (Religion)": lambda: brown.words(categories="religion"), + "English: Brown Corpus (Learned)": lambda: brown.words(categories="learned"), + "English: Brown Corpus (Science Fiction)": lambda: brown.words( + categories="science_fiction" + ), + "English: Brown Corpus (Romance)": lambda: brown.words(categories="romance"), + "English: Brown Corpus (Humor)": lambda: brown.words(categories="humor"), + "English: NPS Chat Corpus": lambda: nps_chat.words(), + "English: Wall Street Journal Corpus": lambda: treebank.words(), + "Chinese: Sinica Corpus": lambda: sinica_treebank.words(), + "Dutch: Alpino Corpus": lambda: alpino.words(), + "Hindi: Indian Languages Corpus": lambda: indian.words(files="hindi.pos"), + "Portuguese: Floresta Corpus (Portugal)": lambda: floresta.words(), + "Portuguese: MAC-MORPHO Corpus (Brazil)": lambda: mac_morpho.words(), + "Portuguese: Machado Corpus (Brazil)": lambda: machado.words(), + "Spanish: CESS-ESP Corpus": lambda: cess_esp.words(), +} + + +class CollocationsView: + _BACKGROUND_COLOUR = "#FFF" # white + + def __init__(self): + self.queue = q.Queue() + self.model = CollocationsModel(self.queue) + self.top = Tk() + self._init_top(self.top) + self._init_menubar() + self._init_widgets(self.top) + self.load_corpus(self.model.DEFAULT_CORPUS) + self.after = self.top.after(POLL_INTERVAL, self._poll) + + def _init_top(self, top): + top.geometry("550x650+50+50") + top.title("NLTK Collocations List") + top.bind("", self.destroy) + top.protocol("WM_DELETE_WINDOW", self.destroy) + top.minsize(550, 650) + + def _init_widgets(self, parent): + self.main_frame = Frame( + parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1) + ) + self._init_corpus_select(self.main_frame) + self._init_results_box(self.main_frame) + self._init_paging(self.main_frame) + self._init_status(self.main_frame) + self.main_frame.pack(fill="both", expand=True) + + def _init_corpus_select(self, parent): + innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) + self.var = StringVar(innerframe) + self.var.set(self.model.DEFAULT_CORPUS) + Label( + innerframe, + justify=LEFT, + text=" Corpus: ", + background=self._BACKGROUND_COLOUR, + padx=2, + pady=1, + border=0, + ).pack(side="left") + + other_corpora = list(self.model.CORPORA.keys()).remove( + self.model.DEFAULT_CORPUS + ) + om = OptionMenu( + innerframe, + self.var, + self.model.DEFAULT_CORPUS, + command=self.corpus_selected, + *self.model.non_default_corpora() + ) + om["borderwidth"] = 0 + om["highlightthickness"] = 1 + om.pack(side="left") + innerframe.pack(side="top", fill="x", anchor="n") + + def _init_status(self, parent): + self.status = Label( + parent, + justify=LEFT, + relief=SUNKEN, + background=self._BACKGROUND_COLOUR, + border=0, + padx=1, + pady=0, + ) + self.status.pack(side="top", anchor="sw") + + def _init_menubar(self): + self._result_size = IntVar(self.top) + menubar = Menu(self.top) + + filemenu = Menu(menubar, tearoff=0, borderwidth=0) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + editmenu = Menu(menubar, tearoff=0) + rescntmenu = Menu(editmenu, tearoff=0) + rescntmenu.add_radiobutton( + label="20", + variable=self._result_size, + underline=0, + value=20, + command=self.set_result_size, + ) + rescntmenu.add_radiobutton( + label="50", + variable=self._result_size, + underline=0, + value=50, + command=self.set_result_size, + ) + rescntmenu.add_radiobutton( + label="100", + variable=self._result_size, + underline=0, + value=100, + command=self.set_result_size, + ) + rescntmenu.invoke(1) + editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) + + menubar.add_cascade(label="Edit", underline=0, menu=editmenu) + self.top.config(menu=menubar) + + def set_result_size(self, **kwargs): + self.model.result_count = self._result_size.get() + + def _init_results_box(self, parent): + innerframe = Frame(parent) + i1 = Frame(innerframe) + i2 = Frame(innerframe) + vscrollbar = Scrollbar(i1, borderwidth=1) + hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") + self.results_box = Text( + i1, + font=Font(family="courier", size="16"), + state="disabled", + borderwidth=1, + yscrollcommand=vscrollbar.set, + xscrollcommand=hscrollbar.set, + wrap="none", + width="40", + height="20", + exportselection=1, + ) + self.results_box.pack(side="left", fill="both", expand=True) + vscrollbar.pack(side="left", fill="y", anchor="e") + vscrollbar.config(command=self.results_box.yview) + hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") + hscrollbar.config(command=self.results_box.xview) + # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! + Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack( + side="left", anchor="e" + ) + i1.pack(side="top", fill="both", expand=True, anchor="n") + i2.pack(side="bottom", fill="x", anchor="s") + innerframe.pack(side="top", fill="both", expand=True) + + def _init_paging(self, parent): + innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) + self.prev = prev = Button( + innerframe, + text="Previous", + command=self.previous, + width="10", + borderwidth=1, + highlightthickness=1, + state="disabled", + ) + prev.pack(side="left", anchor="center") + self.next = next = Button( + innerframe, + text="Next", + command=self.__next__, + width="10", + borderwidth=1, + highlightthickness=1, + state="disabled", + ) + next.pack(side="right", anchor="center") + innerframe.pack(side="top", fill="y") + self.reset_current_page() + + def reset_current_page(self): + self.current_page = -1 + + def _poll(self): + try: + event = self.queue.get(block=False) + except q.Empty: + pass + else: + if event == CORPUS_LOADED_EVENT: + self.handle_corpus_loaded(event) + elif event == ERROR_LOADING_CORPUS_EVENT: + self.handle_error_loading_corpus(event) + self.after = self.top.after(POLL_INTERVAL, self._poll) + + def handle_error_loading_corpus(self, event): + self.status["text"] = "Error in loading " + self.var.get() + self.unfreeze_editable() + self.clear_results_box() + self.freeze_editable() + self.reset_current_page() + + def handle_corpus_loaded(self, event): + self.status["text"] = self.var.get() + " is loaded" + self.unfreeze_editable() + self.clear_results_box() + self.reset_current_page() + # self.next() + collocations = self.model.next(self.current_page + 1) + self.write_results(collocations) + self.current_page += 1 + + def corpus_selected(self, *args): + new_selection = self.var.get() + self.load_corpus(new_selection) + + def previous(self): + self.freeze_editable() + collocations = self.model.prev(self.current_page - 1) + self.current_page = self.current_page - 1 + self.clear_results_box() + self.write_results(collocations) + self.unfreeze_editable() + + def __next__(self): + self.freeze_editable() + collocations = self.model.next(self.current_page + 1) + self.clear_results_box() + self.write_results(collocations) + self.current_page += 1 + self.unfreeze_editable() + + def load_corpus(self, selection): + if self.model.selected_corpus != selection: + self.status["text"] = "Loading " + selection + "..." + self.freeze_editable() + self.model.load_corpus(selection) + + def freeze_editable(self): + self.prev["state"] = "disabled" + self.next["state"] = "disabled" + + def clear_results_box(self): + self.results_box["state"] = "normal" + self.results_box.delete("1.0", END) + self.results_box["state"] = "disabled" + + def fire_event(self, event): + # Firing an event so that rendering of widgets happen in the mainloop thread + self.top.event_generate(event, when="tail") + + def destroy(self, *e): + if self.top is None: + return + self.top.after_cancel(self.after) + self.top.destroy() + self.top = None + + def mainloop(self, *args, **kwargs): + if in_idle(): + return + self.top.mainloop(*args, **kwargs) + + def unfreeze_editable(self): + self.set_paging_button_states() + + def set_paging_button_states(self): + if self.current_page == -1 or self.current_page == 0: + self.prev["state"] = "disabled" + else: + self.prev["state"] = "normal" + if self.model.is_last_page(self.current_page): + self.next["state"] = "disabled" + else: + self.next["state"] = "normal" + + def write_results(self, results): + self.results_box["state"] = "normal" + row = 1 + for each in results: + self.results_box.insert(str(row) + ".0", each[0] + " " + each[1] + "\n") + row += 1 + self.results_box["state"] = "disabled" + + +class CollocationsModel: + def __init__(self, queue): + self.result_count = None + self.selected_corpus = None + self.collocations = None + self.CORPORA = _CORPORA + self.DEFAULT_CORPUS = _DEFAULT + self.queue = queue + self.reset_results() + + def reset_results(self): + self.result_pages = [] + self.results_returned = 0 + + def load_corpus(self, name): + self.selected_corpus = name + self.collocations = None + runner_thread = self.LoadCorpus(name, self) + runner_thread.start() + self.reset_results() + + def non_default_corpora(self): + copy = [] + copy.extend(list(self.CORPORA.keys())) + copy.remove(self.DEFAULT_CORPUS) + copy.sort() + return copy + + def is_last_page(self, number): + if number < len(self.result_pages): + return False + return self.results_returned + ( + number - len(self.result_pages) + ) * self.result_count >= len(self.collocations) + + def next(self, page): + if (len(self.result_pages) - 1) < page: + for i in range(page - (len(self.result_pages) - 1)): + self.result_pages.append( + self.collocations[ + self.results_returned : self.results_returned + + self.result_count + ] + ) + self.results_returned += self.result_count + return self.result_pages[page] + + def prev(self, page): + if page == -1: + return [] + return self.result_pages[page] + + class LoadCorpus(threading.Thread): + def __init__(self, name, model): + threading.Thread.__init__(self) + self.model, self.name = model, name + + def run(self): + try: + words = self.model.CORPORA[self.name]() + from operator import itemgetter + + text = [w for w in words if len(w) > 2] + fd = FreqDist(tuple(text[i : i + 2]) for i in range(len(text) - 1)) + vocab = FreqDist(text) + scored = [ + ((w1, w2), fd[(w1, w2)] ** 3 / (vocab[w1] * vocab[w2])) + for w1, w2 in fd + ] + scored.sort(key=itemgetter(1), reverse=True) + self.model.collocations = list(map(itemgetter(0), scored)) + self.model.queue.put(CORPUS_LOADED_EVENT) + except Exception as e: + print(e) + self.model.queue.put(ERROR_LOADING_CORPUS_EVENT) + + +# def collocations(): +# colloc_strings = [w1 + ' ' + w2 for w1, w2 in self._collocations[:num]] + + +def app(): + c = CollocationsView() + c.mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/concordance_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/concordance_app.py new file mode 100644 index 0000000..da8ce7a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/concordance_app.py @@ -0,0 +1,709 @@ +# Natural Language Toolkit: Concordance Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Sumukh Ghodke +# URL: +# For license information, see LICENSE.TXT + +import queue as q +import re +import threading +from tkinter import ( + END, + LEFT, + SUNKEN, + Button, + Entry, + Frame, + IntVar, + Label, + Menu, + OptionMenu, + Scrollbar, + StringVar, + Text, + Tk, +) +from tkinter.font import Font + +from nltk.corpus import ( + alpino, + brown, + cess_cat, + cess_esp, + floresta, + indian, + mac_morpho, + nps_chat, + sinica_treebank, + treebank, +) +from nltk.draw.util import ShowText +from nltk.util import in_idle + +WORD_OR_TAG = "[^/ ]+" +BOUNDARY = r"\b" + +CORPUS_LOADED_EVENT = "<>" +SEARCH_TERMINATED_EVENT = "<>" +SEARCH_ERROR_EVENT = "<>" +ERROR_LOADING_CORPUS_EVENT = "<>" + +POLL_INTERVAL = 50 + +# NB All corpora must be specified in a lambda expression so as not to be +# loaded when the module is imported. + +_DEFAULT = "English: Brown Corpus (Humor, simplified)" +_CORPORA = { + "Catalan: CESS-CAT Corpus (simplified)": lambda: cess_cat.tagged_sents( + tagset="universal" + ), + "English: Brown Corpus": lambda: brown.tagged_sents(), + "English: Brown Corpus (simplified)": lambda: brown.tagged_sents( + tagset="universal" + ), + "English: Brown Corpus (Press, simplified)": lambda: brown.tagged_sents( + categories=["news", "editorial", "reviews"], tagset="universal" + ), + "English: Brown Corpus (Religion, simplified)": lambda: brown.tagged_sents( + categories="religion", tagset="universal" + ), + "English: Brown Corpus (Learned, simplified)": lambda: brown.tagged_sents( + categories="learned", tagset="universal" + ), + "English: Brown Corpus (Science Fiction, simplified)": lambda: brown.tagged_sents( + categories="science_fiction", tagset="universal" + ), + "English: Brown Corpus (Romance, simplified)": lambda: brown.tagged_sents( + categories="romance", tagset="universal" + ), + "English: Brown Corpus (Humor, simplified)": lambda: brown.tagged_sents( + categories="humor", tagset="universal" + ), + "English: NPS Chat Corpus": lambda: nps_chat.tagged_posts(), + "English: NPS Chat Corpus (simplified)": lambda: nps_chat.tagged_posts( + tagset="universal" + ), + "English: Wall Street Journal Corpus": lambda: treebank.tagged_sents(), + "English: Wall Street Journal Corpus (simplified)": lambda: treebank.tagged_sents( + tagset="universal" + ), + "Chinese: Sinica Corpus": lambda: sinica_treebank.tagged_sents(), + "Chinese: Sinica Corpus (simplified)": lambda: sinica_treebank.tagged_sents( + tagset="universal" + ), + "Dutch: Alpino Corpus": lambda: alpino.tagged_sents(), + "Dutch: Alpino Corpus (simplified)": lambda: alpino.tagged_sents( + tagset="universal" + ), + "Hindi: Indian Languages Corpus": lambda: indian.tagged_sents(files="hindi.pos"), + "Hindi: Indian Languages Corpus (simplified)": lambda: indian.tagged_sents( + files="hindi.pos", tagset="universal" + ), + "Portuguese: Floresta Corpus (Portugal)": lambda: floresta.tagged_sents(), + "Portuguese: Floresta Corpus (Portugal, simplified)": lambda: floresta.tagged_sents( + tagset="universal" + ), + "Portuguese: MAC-MORPHO Corpus (Brazil)": lambda: mac_morpho.tagged_sents(), + "Portuguese: MAC-MORPHO Corpus (Brazil, simplified)": lambda: mac_morpho.tagged_sents( + tagset="universal" + ), + "Spanish: CESS-ESP Corpus (simplified)": lambda: cess_esp.tagged_sents( + tagset="universal" + ), +} + + +class ConcordanceSearchView: + _BACKGROUND_COLOUR = "#FFF" # white + + # Colour of highlighted results + _HIGHLIGHT_WORD_COLOUR = "#F00" # red + _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG" + + _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0" # dark grey + _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG" + + # Percentage of text left of the scrollbar position + _FRACTION_LEFT_TEXT = 0.30 + + def __init__(self): + self.queue = q.Queue() + self.model = ConcordanceSearchModel(self.queue) + self.top = Tk() + self._init_top(self.top) + self._init_menubar() + self._init_widgets(self.top) + self.load_corpus(self.model.DEFAULT_CORPUS) + self.after = self.top.after(POLL_INTERVAL, self._poll) + + def _init_top(self, top): + top.geometry("950x680+50+50") + top.title("NLTK Concordance Search") + top.bind("", self.destroy) + top.protocol("WM_DELETE_WINDOW", self.destroy) + top.minsize(950, 680) + + def _init_widgets(self, parent): + self.main_frame = Frame( + parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1) + ) + self._init_corpus_select(self.main_frame) + self._init_query_box(self.main_frame) + self._init_results_box(self.main_frame) + self._init_paging(self.main_frame) + self._init_status(self.main_frame) + self.main_frame.pack(fill="both", expand=True) + + def _init_menubar(self): + self._result_size = IntVar(self.top) + self._cntx_bf_len = IntVar(self.top) + self._cntx_af_len = IntVar(self.top) + menubar = Menu(self.top) + + filemenu = Menu(menubar, tearoff=0, borderwidth=0) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-q" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + editmenu = Menu(menubar, tearoff=0) + rescntmenu = Menu(editmenu, tearoff=0) + rescntmenu.add_radiobutton( + label="20", + variable=self._result_size, + underline=0, + value=20, + command=self.set_result_size, + ) + rescntmenu.add_radiobutton( + label="50", + variable=self._result_size, + underline=0, + value=50, + command=self.set_result_size, + ) + rescntmenu.add_radiobutton( + label="100", + variable=self._result_size, + underline=0, + value=100, + command=self.set_result_size, + ) + rescntmenu.invoke(1) + editmenu.add_cascade(label="Result Count", underline=0, menu=rescntmenu) + + cntxmenu = Menu(editmenu, tearoff=0) + cntxbfmenu = Menu(cntxmenu, tearoff=0) + cntxbfmenu.add_radiobutton( + label="60 characters", + variable=self._cntx_bf_len, + underline=0, + value=60, + command=self.set_cntx_bf_len, + ) + cntxbfmenu.add_radiobutton( + label="80 characters", + variable=self._cntx_bf_len, + underline=0, + value=80, + command=self.set_cntx_bf_len, + ) + cntxbfmenu.add_radiobutton( + label="100 characters", + variable=self._cntx_bf_len, + underline=0, + value=100, + command=self.set_cntx_bf_len, + ) + cntxbfmenu.invoke(1) + cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu) + + cntxafmenu = Menu(cntxmenu, tearoff=0) + cntxafmenu.add_radiobutton( + label="70 characters", + variable=self._cntx_af_len, + underline=0, + value=70, + command=self.set_cntx_af_len, + ) + cntxafmenu.add_radiobutton( + label="90 characters", + variable=self._cntx_af_len, + underline=0, + value=90, + command=self.set_cntx_af_len, + ) + cntxafmenu.add_radiobutton( + label="110 characters", + variable=self._cntx_af_len, + underline=0, + value=110, + command=self.set_cntx_af_len, + ) + cntxafmenu.invoke(1) + cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu) + + editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu) + + menubar.add_cascade(label="Edit", underline=0, menu=editmenu) + + self.top.config(menu=menubar) + + def set_result_size(self, **kwargs): + self.model.result_count = self._result_size.get() + + def set_cntx_af_len(self, **kwargs): + self._char_after = self._cntx_af_len.get() + + def set_cntx_bf_len(self, **kwargs): + self._char_before = self._cntx_bf_len.get() + + def _init_corpus_select(self, parent): + innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) + self.var = StringVar(innerframe) + self.var.set(self.model.DEFAULT_CORPUS) + Label( + innerframe, + justify=LEFT, + text=" Corpus: ", + background=self._BACKGROUND_COLOUR, + padx=2, + pady=1, + border=0, + ).pack(side="left") + + other_corpora = list(self.model.CORPORA.keys()).remove( + self.model.DEFAULT_CORPUS + ) + om = OptionMenu( + innerframe, + self.var, + self.model.DEFAULT_CORPUS, + command=self.corpus_selected, + *self.model.non_default_corpora() + ) + om["borderwidth"] = 0 + om["highlightthickness"] = 1 + om.pack(side="left") + innerframe.pack(side="top", fill="x", anchor="n") + + def _init_status(self, parent): + self.status = Label( + parent, + justify=LEFT, + relief=SUNKEN, + background=self._BACKGROUND_COLOUR, + border=0, + padx=1, + pady=0, + ) + self.status.pack(side="top", anchor="sw") + + def _init_query_box(self, parent): + innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) + another = Frame(innerframe, background=self._BACKGROUND_COLOUR) + self.query_box = Entry(another, width=60) + self.query_box.pack(side="left", fill="x", pady=25, anchor="center") + self.search_button = Button( + another, + text="Search", + command=self.search, + borderwidth=1, + highlightthickness=1, + ) + self.search_button.pack(side="left", fill="x", pady=25, anchor="center") + self.query_box.bind("", self.search_enter_keypress_handler) + another.pack() + innerframe.pack(side="top", fill="x", anchor="n") + + def search_enter_keypress_handler(self, *event): + self.search() + + def _init_results_box(self, parent): + innerframe = Frame(parent) + i1 = Frame(innerframe) + i2 = Frame(innerframe) + vscrollbar = Scrollbar(i1, borderwidth=1) + hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz") + self.results_box = Text( + i1, + font=Font(family="courier", size="16"), + state="disabled", + borderwidth=1, + yscrollcommand=vscrollbar.set, + xscrollcommand=hscrollbar.set, + wrap="none", + width="40", + height="20", + exportselection=1, + ) + self.results_box.pack(side="left", fill="both", expand=True) + self.results_box.tag_config( + self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR + ) + self.results_box.tag_config( + self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR + ) + vscrollbar.pack(side="left", fill="y", anchor="e") + vscrollbar.config(command=self.results_box.yview) + hscrollbar.pack(side="left", fill="x", expand=True, anchor="w") + hscrollbar.config(command=self.results_box.xview) + # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!! + Label(i2, text=" ", background=self._BACKGROUND_COLOUR).pack( + side="left", anchor="e" + ) + i1.pack(side="top", fill="both", expand=True, anchor="n") + i2.pack(side="bottom", fill="x", anchor="s") + innerframe.pack(side="top", fill="both", expand=True) + + def _init_paging(self, parent): + innerframe = Frame(parent, background=self._BACKGROUND_COLOUR) + self.prev = prev = Button( + innerframe, + text="Previous", + command=self.previous, + width="10", + borderwidth=1, + highlightthickness=1, + state="disabled", + ) + prev.pack(side="left", anchor="center") + self.next = next = Button( + innerframe, + text="Next", + command=self.__next__, + width="10", + borderwidth=1, + highlightthickness=1, + state="disabled", + ) + next.pack(side="right", anchor="center") + innerframe.pack(side="top", fill="y") + self.current_page = 0 + + def previous(self): + self.clear_results_box() + self.freeze_editable() + self.model.prev(self.current_page - 1) + + def __next__(self): + self.clear_results_box() + self.freeze_editable() + self.model.next(self.current_page + 1) + + def about(self, *e): + ABOUT = "NLTK Concordance Search Demo\n" + TITLE = "About: NLTK Concordance Search Demo" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE, parent=self.main_frame).show() + except: + ShowText(self.top, TITLE, ABOUT) + + def _bind_event_handlers(self): + self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded) + self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated) + self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error) + self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus) + + def _poll(self): + try: + event = self.queue.get(block=False) + except q.Empty: + pass + else: + if event == CORPUS_LOADED_EVENT: + self.handle_corpus_loaded(event) + elif event == SEARCH_TERMINATED_EVENT: + self.handle_search_terminated(event) + elif event == SEARCH_ERROR_EVENT: + self.handle_search_error(event) + elif event == ERROR_LOADING_CORPUS_EVENT: + self.handle_error_loading_corpus(event) + self.after = self.top.after(POLL_INTERVAL, self._poll) + + def handle_error_loading_corpus(self, event): + self.status["text"] = "Error in loading " + self.var.get() + self.unfreeze_editable() + self.clear_all() + self.freeze_editable() + + def handle_corpus_loaded(self, event): + self.status["text"] = self.var.get() + " is loaded" + self.unfreeze_editable() + self.clear_all() + self.query_box.focus_set() + + def handle_search_terminated(self, event): + # todo: refactor the model such that it is less state sensitive + results = self.model.get_results() + self.write_results(results) + self.status["text"] = "" + if len(results) == 0: + self.status["text"] = "No results found for " + self.model.query + else: + self.current_page = self.model.last_requested_page + self.unfreeze_editable() + self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT) + + def handle_search_error(self, event): + self.status["text"] = "Error in query " + self.model.query + self.unfreeze_editable() + + def corpus_selected(self, *args): + new_selection = self.var.get() + self.load_corpus(new_selection) + + def load_corpus(self, selection): + if self.model.selected_corpus != selection: + self.status["text"] = "Loading " + selection + "..." + self.freeze_editable() + self.model.load_corpus(selection) + + def search(self): + self.current_page = 0 + self.clear_results_box() + self.model.reset_results() + query = self.query_box.get() + if len(query.strip()) == 0: + return + self.status["text"] = "Searching for " + query + self.freeze_editable() + self.model.search(query, self.current_page + 1) + + def write_results(self, results): + self.results_box["state"] = "normal" + row = 1 + for each in results: + sent, pos1, pos2 = each[0].strip(), each[1], each[2] + if len(sent) != 0: + if pos1 < self._char_before: + sent, pos1, pos2 = self.pad(sent, pos1, pos2) + sentence = sent[pos1 - self._char_before : pos1 + self._char_after] + if not row == len(results): + sentence += "\n" + self.results_box.insert(str(row) + ".0", sentence) + word_markers, label_markers = self.words_and_labels(sent, pos1, pos2) + for marker in word_markers: + self.results_box.tag_add( + self._HIGHLIGHT_WORD_TAG, + str(row) + "." + str(marker[0]), + str(row) + "." + str(marker[1]), + ) + for marker in label_markers: + self.results_box.tag_add( + self._HIGHLIGHT_LABEL_TAG, + str(row) + "." + str(marker[0]), + str(row) + "." + str(marker[1]), + ) + row += 1 + self.results_box["state"] = "disabled" + + def words_and_labels(self, sentence, pos1, pos2): + search_exp = sentence[pos1:pos2] + words, labels = [], [] + labeled_words = search_exp.split(" ") + index = 0 + for each in labeled_words: + if each == "": + index += 1 + else: + word, label = each.split("/") + words.append( + (self._char_before + index, self._char_before + index + len(word)) + ) + index += len(word) + 1 + labels.append( + (self._char_before + index, self._char_before + index + len(label)) + ) + index += len(label) + index += 1 + return words, labels + + def pad(self, sent, hstart, hend): + if hstart >= self._char_before: + return sent, hstart, hend + d = self._char_before - hstart + sent = "".join([" "] * d) + sent + return sent, hstart + d, hend + d + + def destroy(self, *e): + if self.top is None: + return + self.top.after_cancel(self.after) + self.top.destroy() + self.top = None + + def clear_all(self): + self.query_box.delete(0, END) + self.model.reset_query() + self.clear_results_box() + + def clear_results_box(self): + self.results_box["state"] = "normal" + self.results_box.delete("1.0", END) + self.results_box["state"] = "disabled" + + def freeze_editable(self): + self.query_box["state"] = "disabled" + self.search_button["state"] = "disabled" + self.prev["state"] = "disabled" + self.next["state"] = "disabled" + + def unfreeze_editable(self): + self.query_box["state"] = "normal" + self.search_button["state"] = "normal" + self.set_paging_button_states() + + def set_paging_button_states(self): + if self.current_page == 0 or self.current_page == 1: + self.prev["state"] = "disabled" + else: + self.prev["state"] = "normal" + if self.model.has_more_pages(self.current_page): + self.next["state"] = "normal" + else: + self.next["state"] = "disabled" + + def fire_event(self, event): + # Firing an event so that rendering of widgets happen in the mainloop thread + self.top.event_generate(event, when="tail") + + def mainloop(self, *args, **kwargs): + if in_idle(): + return + self.top.mainloop(*args, **kwargs) + + +class ConcordanceSearchModel: + def __init__(self, queue): + self.queue = queue + self.CORPORA = _CORPORA + self.DEFAULT_CORPUS = _DEFAULT + self.selected_corpus = None + self.reset_query() + self.reset_results() + self.result_count = None + self.last_sent_searched = 0 + + def non_default_corpora(self): + copy = [] + copy.extend(list(self.CORPORA.keys())) + copy.remove(self.DEFAULT_CORPUS) + copy.sort() + return copy + + def load_corpus(self, name): + self.selected_corpus = name + self.tagged_sents = [] + runner_thread = self.LoadCorpus(name, self) + runner_thread.start() + + def search(self, query, page): + self.query = query + self.last_requested_page = page + self.SearchCorpus(self, page, self.result_count).start() + + def next(self, page): + self.last_requested_page = page + if len(self.results) < page: + self.search(self.query, page) + else: + self.queue.put(SEARCH_TERMINATED_EVENT) + + def prev(self, page): + self.last_requested_page = page + self.queue.put(SEARCH_TERMINATED_EVENT) + + def reset_results(self): + self.last_sent_searched = 0 + self.results = [] + self.last_page = None + + def reset_query(self): + self.query = None + + def set_results(self, page, resultset): + self.results.insert(page - 1, resultset) + + def get_results(self): + return self.results[self.last_requested_page - 1] + + def has_more_pages(self, page): + if self.results == [] or self.results[0] == []: + return False + if self.last_page is None: + return True + return page < self.last_page + + class LoadCorpus(threading.Thread): + def __init__(self, name, model): + threading.Thread.__init__(self) + self.model, self.name = model, name + + def run(self): + try: + ts = self.model.CORPORA[self.name]() + self.model.tagged_sents = [ + " ".join(w + "/" + t for (w, t) in sent) for sent in ts + ] + self.model.queue.put(CORPUS_LOADED_EVENT) + except Exception as e: + print(e) + self.model.queue.put(ERROR_LOADING_CORPUS_EVENT) + + class SearchCorpus(threading.Thread): + def __init__(self, model, page, count): + self.model, self.count, self.page = model, count, page + threading.Thread.__init__(self) + + def run(self): + q = self.processed_query() + sent_pos, i, sent_count = [], 0, 0 + for sent in self.model.tagged_sents[self.model.last_sent_searched :]: + try: + m = re.search(q, sent) + except re.error: + self.model.reset_results() + self.model.queue.put(SEARCH_ERROR_EVENT) + return + if m: + sent_pos.append((sent, m.start(), m.end())) + i += 1 + if i > self.count: + self.model.last_sent_searched += sent_count - 1 + break + sent_count += 1 + if self.count >= len(sent_pos): + self.model.last_sent_searched += sent_count - 1 + self.model.last_page = self.page + self.model.set_results(self.page, sent_pos) + else: + self.model.set_results(self.page, sent_pos[:-1]) + self.model.queue.put(SEARCH_TERMINATED_EVENT) + + def processed_query(self): + new = [] + for term in self.model.query.split(): + term = re.sub(r"\.", r"[^/ ]", term) + if re.match("[A-Z]+$", term): + new.append(BOUNDARY + WORD_OR_TAG + "/" + term + BOUNDARY) + elif "/" in term: + new.append(BOUNDARY + term + BOUNDARY) + else: + new.append(BOUNDARY + term + "/" + WORD_OR_TAG + BOUNDARY) + return " ".join(new) + + +def app(): + d = ConcordanceSearchView() + d.mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/nemo_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/nemo_app.py new file mode 100644 index 0000000..b830cc8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/nemo_app.py @@ -0,0 +1,163 @@ +# Finding (and Replacing) Nemo, Version 1.1, Aristide Grange 2006/06/06 +# https://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496783 + +""" +Finding (and Replacing) Nemo + +Instant Regular Expressions +Created by Aristide Grange +""" +import itertools +import re +from tkinter import SEL_FIRST, SEL_LAST, Frame, Label, PhotoImage, Scrollbar, Text, Tk + +windowTitle = "Finding (and Replacing) Nemo" +initialFind = r"n(.*?)e(.*?)m(.*?)o" +initialRepl = r"M\1A\2K\3I" +initialText = """\ +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +""" +images = { + "FIND": "R0lGODlhMAAiAPcAMf/////37//35//n1v97Off///f/9/f37/fexvfOvfeEQvd7QvdrQvdrKfdaKfdSMfdSIe/v9+/v7+/v5+/n3u/e1u/Wxu/Gre+1lO+tnO+thO+Ua+97Y+97Oe97Me9rOe9rMe9jOe9jMe9jIe9aMefe5+fe3ufezuece+eEWudzQudaIedSIedKMedKIedCKedCId7e1t7Wzt7Oxt7Gvd69vd69rd61pd6ljN6UjN6Ue96EY95zY95rUt5rQt5jMd5SId5KIdbn59be3tbGztbGvda1rdaEa9Z7a9Z7WtZzQtZzOdZzMdZjMdZaQtZSOdZSMdZKMdZCKdZCGNY5Ic7W1s7Oxs7Gtc69xs69tc69rc6tpc6llM6clM6cjM6Ue86EY85zWs5rSs5SKc5KKc5KGMa1tcatrcalvcalnMaUpcZ7c8ZzMcZrUsZrOcZrMcZaQsZSOcZSMcZKMcZCKcZCGMYxIcYxGL3Gxr21tb21rb2lpb2crb2cjL2UnL2UlL2UhL2Ec717Wr17Ur1zWr1rMb1jUr1KMb1KIb1CIb0xGLWlrbWlpbWcnLWEe7V7c7VzY7VzUrVSKbVKMbVCMbVCIbU5KbUxIbUxEK2lta2lpa2clK2UjK2MnK2MlK2Ea617e61za61rY61rMa1jSq1aUq1aSq1SQq1KKa0xEKWlnKWcnKWUnKWUhKWMjKWEa6Vza6VrWqVjMaVaUqVaKaVSMaVCMaU5KaUxIaUxGJyclJyMe5yElJyEhJx7e5x7c5xrOZxaQpxSOZxKQpw5IZSMhJSEjJR7c5Rre5RrY5RrUpRSQpRSKZRCOZRCKZQxKZQxIYyEhIx7hIxza4xzY4xrc4xjUoxaa4xaUoxSSoxKQoxCMYw5GIR7c4Rzc4Rre4RjY4RjWoRaa4RSWoRSUoRSMYRKQoRCOYQ5KYQxIXtra3taY3taSntKOXtCMXtCKXNCMXM5MXMxIWtSUmtKSmtKQmtCOWs5MWs5KWs5IWNCKWMxIVIxKUIQCDkhGAAAACH+AS4ALAAAAAAwACIAAAj/AAEIHEiwoMGDCBMqXMiwoUOHMqxIeEiRoZVp7cpZ29WrF4WKIAd208dGAQEVbiTVChUjZMU9+pYQmPmBZpxgvVw+nDdKwQICNVcIXQEkTgKdDdUJ+/nggVAXK1xI3TEA6UIr2uJ8iBqka1cXXTlkqGoVYRZ7iLyqBSs0iiEtZQVKiDGxBI1u3NR6lUpGDKg8MSgEQCphU7Z22vhg0dILXRCpYLuSCcYJT4wqXASBQaBzU7klHxC127OHD7ZDJFpERqRt0x5OnwQpmZmCLEhrbgg4WIHO1RY+nbQ9WRGEDJlmnXwJ+9FBgXMCIzYMVijBBgYMFxIMqJBMSc0Ht7qh/+Gjpte2rnYsYeNlasWIBgQ6yCewIoPCCp/cyP/wgUGbXVu0QcADZNBDnh98gHMLGXYQUw02w61QU3wdbNWDbQVVIIhMMwFF1DaZiPLBAy7E04kafrjSizaK3LFNNc0AAYRQDsAHHQlJ2IDQJ2zE1+EKDjiAijShkECCC8Qgw4cr7ZgyzC2WaHPNLWWoNeNWPiRAw0QFWQFMhz8C+QQ20yAiVSrY+MGOJCsccsst2GCzoHFxxEGGC+8hgs0MB2kyCpgzrUDCbs1Es41UdtATHFFkWELMOtsoQsYcgvRRQw5RSDgGOjZMR1AvPQIq6KCo9AKOJWDd48owQlHR4DXEKP9iyRrK+DNNBTu4RwIPFeTAGUG7hAomkA84gEg1m6ADljy9PBKGGJY4ig0xlsTBRSn98FOFDUC8pwQOPkgHbCGAzhTkA850s0c7j6Hjix9+gBIrMXLeAccWXUCyiRBcBEECdEJ98KtAqtBCYQc/OvDENnl4gYpUxISCIjjzylkGGV9okYUVNogRhAOBuuAEhjG08wOgDYzAgA5bCjIoCe5uwUk80RKTTSppPREGGGCIISOQ9AXBg6cC6WIywvCpoMHAocRBwhP4bHLFLujYkV42xNxBRhAyGrc113EgYtRBerDDDHMoDCyQEL5sE083EkgwQyBhxGFHMM206DUixGxmE0wssbQjCQ4JCaFKFwgQTVAVVhQUwAVPIFJKrHfYYRwi6OCDzzuIJIFhXAD0EccPsYRiSyqKSDpFcWSMIcZRoBMkQyA2BGZDIKSYcggih8TRRg4VxM5QABVYYLxgwiev/PLMCxQQADs=", + "find": "R0lGODlhMAAiAPQAMf////f39+/v7+fn597e3tbW1s7OzsbGxr29vbW1ta2traWlpZycnJSUlIyMjISEhHt7e3Nzc2tra2NjY1paWlJSUkpKSkJCQjk5OSkpKRgYGAAAAAAAAAAAAAAAAAAAACH+AS4ALAAAAAAwACIAAAX/ICCOZGmeaKquY2AGLiuvMCAUBuHWc48Kh0iFInEYCb4kSQCxPBiMxkMigRQEgJiSFVBYHNGG0RiZOHjblWAiiY4fkDhEYoBp06dAWfyAQyKAgAwDaHgnB0RwgYASgQ0IhDuGJDAIFhMRVFSLEX8QCJJ4AQM5AgQHTZqqjBAOCQQEkWkCDRMUFQsICQ4Vm5maEwwHOAsPDTpKMAsUDlO4CssTcb+2DAp8YGCyNFoCEsZwFQ3QDRTTVBRS0g1QbgsCd5QAAwgIBwYFAwStzQ8UEdCKVchky0yVBw7YuXkAKt4IAg74vXHVagqFBRgXSCAyYWAVCH0SNhDTitCJfSL5/4RbAPKPhQYYjVCYYAvCP0BxEDaD8CheAAHNwqh8MMGPSwgLeJWhwHSjqkYI+xg4MMCEgQjtRvZ7UAYCpghMF7CxONOWJkYR+rCpY4JlVpVxKDwYWEactKW9mhYRtqCTgwgWEMArERSK1j5q//6T8KXonFsShpiJkAECgQYVjykooCVA0JGHEWNiYCHThTFeb3UkoiCCBgwGEKQ1kuAJlhFwhA71h5SukwUM5qqeCSGBgicEWkfNiWSERtBad4JNIBaQBaQah1ToyGZBAnsIuIJs1qnqiAIVjIE2gnAB1T5x0icgzXT79ipgMOOEH6HBbREBMJCeGEY08IoLAkzB1YYFwjxwSUGSNULQJnNUwRYlCcyEkALIxECAP9cNMMABYpRhy3ZsSLDaR70oUAiABGCkAxowCGCAAfDYIQACXoElGRsdXWDBdg2Y90IWktDYGYAB9PWHP0PMdFZaF07SQgAFNDAMAQg0QA1UC8xoZQl22JGFPgWkOUCOL1pZQyhjxinnnCWEAAA7", + "REPL": "R0lGODlhMAAjAPcAMf/////3//+lOf+UKf+MEPf///f39/f35/fv7/ecQvecOfecKfeUIfeUGPeUEPeUCPeMAO/37+/v9+/v3u/n3u/n1u+9jO+9c++1hO+ta++tY++tWu+tUu+tSu+lUu+lQu+lMe+UMe+UKe+UGO+UEO+UAO+MCOfv5+fvxufn7+fn5+fnzue9lOe9c+e1jOe1e+e1c+e1a+etWuetUuelQuecOeeUUueUCN7e597e3t7e1t7ezt7evd7Wzt7Oxt7Ovd7Otd7Opd7OnN7Gtd7Gpd69lN61hN6ta96lStbextberdbW3tbWztbWxtbOvdbOrda1hNalUtaECM7W1s7Ozs7Oxs7Otc7Gxs7Gvc69tc69rc69pc61jM6lc8bWlMbOvcbGxsbGpca9tca9pca1nMaMAL3OhL3Gtb21vb21tb2tpb2tnL2tlLW9tbW9pbW9e7W1pbWtjLWcKa21nK2tra2tnK2tlK2lpa2llK2ljK2le6WlnKWljKWUe6WUc6WUY5y1QpyclJycjJychJyUc5yMY5StY5SUe5SMhJSMe5SMc5SMWpSEa5SESoyUe4yMhIyEY4SlKYScWoSMe4SEe4SEa4R7c4R7Y3uMY3uEe3t7e3t7c3tza3tzY3trKXtjIXOcAHOUMXOEY3Nzc3NzWnNrSmulCGuUMWuMGGtzWmtrY2taMWtaGGOUOWOMAGNzUmNjWmNjSmNaUmNaQmNaOWNaIWNSCFqcAFpjUlpSMVpSIVpSEFpKKVKMAFJSUlJSSlJSMVJKMVJKGFJKAFI5CEqUAEqEAEpzQkpKIUpCQkpCGEpCAEo5EEoxAEJjOUJCOUJCAEI5IUIxADl7ADlaITlCOTkxMTkxKTkxEDkhADFzADFrGDE5OTExADEpEClrCCkxKSkpKSkpISkpACkhCCkhACkYACFzACFrACEhCCEYGBhjEBhjABghABgYCBgYABgQEBgQABAQABAIAAhjAAhSAAhKAAgIEAgICABaAABCAAAhAAAQAAAIAAAAAAAAACH+AS4ALAAAAAAwACMAAAj/AAEIHEiwoMGDCBMqXMiwocOHAA4cgEixIIIJO3JMmAjADIqKFU/8MHIkg5EgYXx4iaTkI0iHE6wE2TCggYILQayEAgXIy8uGCKz8sDCAQAMRG3iEcXULlJkJPwli3OFjh9UdYYLE6NBhA04UXHoVA2XoTZgfPKBWlOBDphAWOdfMcfMDLloeO3hIMjbWVCQ5Fn6E2UFxgpsgFjYIEBADrZU6luqEEfqjTqpt54z1uuWqTIcgWAk7PECGzIUQDRosDmxlUrVJkwQJkqVuX71v06YZcyUlROAdbnLAJKPFyAYFAhoMwFlnEh0rWkpz8raPHm7dqKKc/KFFkBUrVn1M/ziBcEIeLUEQI8/AYk0i9Be4sqjsrN66c9/OnbobhpR3HkIUoZ0WVnBE0AGLFKKFD0HAFUQe77HQgQI1hRBDEHMcY0899bBzihZuCPILJD8EccEGGzwAQhFaUHHQH82sUkgeNHISDBk8WCCCcsqFUEQWmOyzjz3sUGNNOO5Y48YOEgowAAQhnBScQV00k82V47jzjy9CXZBcjziFoco//4CDiSOyhPMPLkJZkEBqJmRQxA9uZGEQD8Ncmc044/zzDF2IZQBCCDYE8QMZz/iiCSx0neHGI7BIhhhNn+1gxRpokEcQAp7seWU7/PwTyxqG/iCEEVzQmUombnDRxRExzP9nBR2PCKLFD3UJwcMPa/SRqUGNWJmNOVn+M44ukMRB4KGcWDNLVhuUMEIJAlzwA3DJBHMJIXm4sQYhqyxCRQQGLSIsn1qac2UzysQSyzX/hLMGD0F0IMCODYAQBA9W/PKPOcRiw0wzwxTiokF9dLMnuv/Mo+fCZF7jBr0xbDDCACWEYKgb1vzjDp/jZNOMLX0IZxAKq2TZTjtaOjwOsXyG+s8sZJTIQsUdIGHoJPf8w487QI/TDSt5mGwQFZxc406o8HiDJchk/ltLHpSlJwSvz5DpTjvmuGNOM57koelBOaAhiCaaPBLL0wwbm003peRBnBZqJMJL1ECz/HXYYx/NdAIOOVCxQyLorswymU93o0wuwfAiTDNR/xz0MLXU0XdCE+UwSTRZAq2lsSATu+4wkGvt+TjNzPLrQyegAUku2Hij5cd8LhxyM8QIg4w18HgcdC6BTBFSDmfQqsovttveDcG7lFLHI75cE841sARCxeWsnxC4G9HADPK6ywzDCRqBo0EHHWhMgT1IJzziNci1N7PMKnSYfML96/90AiJKey/0KtbLX1QK0rrNnQ541xugQ7SHhkXBghN0SKACWRc4KlAhBwKcIOYymJCAAAA7", + "repl": "R0lGODlhMAAjAPQAMf////f39+/v7+fn597e3tbW1s7OzsbGxr29vbW1ta2traWlpZycnJSUlIyMjISEhHt7e3Nzc2tra2NjY1paWlJSUkpKSkJCQjk5OTExMSkpKSEhIRgYGBAQEAgICAAAACH+AS4ALAAAAAAwACMAAAX/ICCOZGmeaKqubOu+gCDANBkIQ1EMQhAghFptYEAkEgjEwXBo7ISvweGgWCwUysPjwTgEoCafTySYIhYMxgLBjEQgCULvCw0QdAZdoVhUIJUFChISEAxYeQM1N1OMTAp+UwZ5eA4TEhFbDWYFdC4ECVMJjwl5BwsQa0umEhUVlhESDgqlBp0rAn5nVpBMDxeZDRQbHBgWFBSWDgtLBnFjKwRYCI9VqQsPs0YKEcMXFq0UEalFDWx4BAO2IwPjppAKDkrTWKYUGd7fEJJFEZpM00cOzCgh4EE8SaoWxKNixQooBRMyZMBwAYIRBhUgLDGS4MoBJeoANMhAgQsaCRZm/5lqaCUJhA4cNHjDoKEDBlJUHqkBlYBTiQUZNGjYMMxDhY3VWk6R4MEDBoMUak5AqoYBqANIBo4wcGGDUKIeLlzVZmWJggsVIkwAZaQSA3kdZzlKkIiEAAlDvW5oOkEBs488JTw44oeUIwdvVTFTUK7uiAAPgubt8GFDhQepqETAQCFU1UMGzlqAgFhUsAcCS0AO6lUDhw8xNRSbENGDhgWSHjWUe6ACbKITizmopZoBa6KvOwj9uuHDhwxyj3xekgDDhw5EvWKo0IB4iQLCOCC/njc7ZQ8UeGvza+ABZZgcxJNc4FO1gc0cOsCUrHevc8tdIMTIAhc4F198G2Qwwd8CBIQUAwEINABBBJUwR9R5wElgVRLwWODBBx4cGB8GEzDQIAo33CGJA8gh+JoH/clUgQU0YvDhdfmJdwEFC6Sjgg8yEPAABsPkh2F22cl2AQbn6QdTghTQ5eAJAQyQAAQV0MSBB9gRVZ4GE1mw5JZOAmiAVi1UWcAZDrDyZXYTeaOhA/bIVuIBPtKQ4h7ViYekUPdcEAEbzTzCRp5CADmAAwj+ORGPBcgwAAHo9ABGCYtm0ChwFHShlRiXhmHlkAcCiOeUodqQw5W0oXLAiamy4MOkjOyAaqxUymApDCEAADs=", +} +colors = ["#FF7B39", "#80F121"] +emphColors = ["#DAFC33", "#F42548"] +fieldParams = { + "height": 3, + "width": 70, + "font": ("monaco", 14), + "highlightthickness": 0, + "borderwidth": 0, + "background": "white", +} +textParams = { + "bg": "#F7E0D4", + "fg": "#2321F1", + "highlightthickness": 0, + "width": 1, + "height": 10, + "font": ("verdana", 16), + "wrap": "word", +} + + +class Zone: + def __init__(self, image, initialField, initialText): + frm = Frame(root) + frm.config(background="white") + self.image = PhotoImage(format="gif", data=images[image.upper()]) + self.imageDimmed = PhotoImage(format="gif", data=images[image]) + self.img = Label(frm) + self.img.config(borderwidth=0) + self.img.pack(side="left") + self.fld = Text(frm, **fieldParams) + self.initScrollText(frm, self.fld, initialField) + frm = Frame(root) + self.txt = Text(frm, **textParams) + self.initScrollText(frm, self.txt, initialText) + for i in range(2): + self.txt.tag_config(colors[i], background=colors[i]) + self.txt.tag_config("emph" + colors[i], foreground=emphColors[i]) + + def initScrollText(self, frm, txt, contents): + scl = Scrollbar(frm) + scl.config(command=txt.yview) + scl.pack(side="right", fill="y") + txt.pack(side="left", expand=True, fill="x") + txt.config(yscrollcommand=scl.set) + txt.insert("1.0", contents) + frm.pack(fill="x") + Frame(height=2, bd=1, relief="ridge").pack(fill="x") + + def refresh(self): + self.colorCycle = itertools.cycle(colors) + try: + self.substitute() + self.img.config(image=self.image) + except re.error: + self.img.config(image=self.imageDimmed) + + +class FindZone(Zone): + def addTags(self, m): + color = next(self.colorCycle) + self.txt.tag_add(color, "1.0+%sc" % m.start(), "1.0+%sc" % m.end()) + try: + self.txt.tag_add( + "emph" + color, "1.0+%sc" % m.start("emph"), "1.0+%sc" % m.end("emph") + ) + except: + pass + + def substitute(self, *args): + for color in colors: + self.txt.tag_remove(color, "1.0", "end") + self.txt.tag_remove("emph" + color, "1.0", "end") + self.rex = re.compile("") # default value in case of malformed regexp + self.rex = re.compile(self.fld.get("1.0", "end")[:-1], re.MULTILINE) + try: + re.compile("(?P%s)" % self.fld.get(SEL_FIRST, SEL_LAST)) + self.rexSel = re.compile( + "%s(?P%s)%s" + % ( + self.fld.get("1.0", SEL_FIRST), + self.fld.get(SEL_FIRST, SEL_LAST), + self.fld.get(SEL_LAST, "end")[:-1], + ), + re.MULTILINE, + ) + except: + self.rexSel = self.rex + self.rexSel.sub(self.addTags, self.txt.get("1.0", "end")) + + +class ReplaceZone(Zone): + def addTags(self, m): + s = sz.rex.sub(self.repl, m.group()) + self.txt.delete( + "1.0+%sc" % (m.start() + self.diff), "1.0+%sc" % (m.end() + self.diff) + ) + self.txt.insert("1.0+%sc" % (m.start() + self.diff), s, next(self.colorCycle)) + self.diff += len(s) - (m.end() - m.start()) + + def substitute(self): + self.txt.delete("1.0", "end") + self.txt.insert("1.0", sz.txt.get("1.0", "end")[:-1]) + self.diff = 0 + self.repl = rex0.sub(r"\\g<\1>", self.fld.get("1.0", "end")[:-1]) + sz.rex.sub(self.addTags, sz.txt.get("1.0", "end")[:-1]) + + +def launchRefresh(_): + sz.fld.after_idle(sz.refresh) + rz.fld.after_idle(rz.refresh) + + +def app(): + global root, sz, rz, rex0 + root = Tk() + root.resizable(height=False, width=True) + root.title(windowTitle) + root.minsize(width=250, height=0) + sz = FindZone("find", initialFind, initialText) + sz.fld.bind("", launchRefresh) + sz.fld.bind("", launchRefresh) + sz.fld.bind("", launchRefresh) + sz.rexSel = re.compile("") + rz = ReplaceZone("repl", initialRepl, "") + rex0 = re.compile(r"(?", launchRefresh) + launchRefresh(None) + root.mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/rdparser_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/rdparser_app.py new file mode 100644 index 0000000..6cd880a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/rdparser_app.py @@ -0,0 +1,1052 @@ +# Natural Language Toolkit: Recursive Descent Parser Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A graphical tool for exploring the recursive descent parser. + +The recursive descent parser maintains a tree, which records the +structure of the portion of the text that has been parsed. It uses +CFG productions to expand the fringe of the tree, and matches its +leaves against the text. Initially, the tree contains the start +symbol ("S"). It is shown in the main canvas, to the right of the +list of available expansions. + +The parser builds up a tree structure for the text using three +operations: + + - "expand" uses a CFG production to add children to a node on the + fringe of the tree. + - "match" compares a leaf in the tree to a text token. + - "backtrack" returns the tree to its state before the most recent + expand or match operation. + +The parser maintains a list of tree locations called a "frontier" to +remember which nodes have not yet been expanded and which leaves have +not yet been matched against the text. The leftmost frontier node is +shown in green, and the other frontier nodes are shown in blue. The +parser always performs expand and match operations on the leftmost +element of the frontier. + +You can control the parser's operation by using the "expand," "match," +and "backtrack" buttons; or you can use the "step" button to let the +parser automatically decide which operation to apply. The parser uses +the following rules to decide which operation to apply: + + - If the leftmost frontier element is a token, try matching it. + - If the leftmost frontier element is a node, try expanding it with + the first untried expansion. + - Otherwise, backtrack. + +The "expand" button applies the untried expansion whose CFG production +is listed earliest in the grammar. To manually choose which expansion +to apply, click on a CFG production from the list of available +expansions, on the left side of the main window. + +The "autostep" button will let the parser continue applying +applications to the tree until it reaches a complete parse. You can +cancel an autostep in progress at any time by clicking on the +"autostep" button again. + +Keyboard Shortcuts:: + [Space]\t Perform the next expand, match, or backtrack operation + [a]\t Step through operations until the next complete parse + [e]\t Perform an expand operation + [m]\t Perform a match operation + [b]\t Perform a backtrack operation + [Delete]\t Reset the parser + [g]\t Show/hide available expansions list + [h]\t Help + [Ctrl-p]\t Print + [q]\t Quit +""" + +from tkinter import Button, Frame, IntVar, Label, Listbox, Menu, Scrollbar, Tk +from tkinter.font import Font + +from nltk.draw import CFGEditor, TreeSegmentWidget, tree_to_treesegment +from nltk.draw.util import CanvasFrame, EntryDialog, ShowText, TextWidget +from nltk.parse import SteppingRecursiveDescentParser +from nltk.tree import Tree +from nltk.util import in_idle + + +class RecursiveDescentApp: + """ + A graphical tool for exploring the recursive descent parser. The tool + displays the parser's tree and the remaining text, and allows the + user to control the parser's operation. In particular, the user + can expand subtrees on the frontier, match tokens on the frontier + against the text, and backtrack. A "step" button simply steps + through the parsing process, performing the operations that + ``RecursiveDescentParser`` would use. + """ + + def __init__(self, grammar, sent, trace=0): + self._sent = sent + self._parser = SteppingRecursiveDescentParser(grammar, trace) + + # Set up the main window. + self._top = Tk() + self._top.title("Recursive Descent Parser Application") + + # Set up key bindings. + self._init_bindings() + + # Initialize the fonts. + self._init_fonts(self._top) + + # Animations. animating_lock is a lock to prevent the demo + # from performing new operations while it's animating. + self._animation_frames = IntVar(self._top) + self._animation_frames.set(5) + self._animating_lock = 0 + self._autostep = 0 + + # The user can hide the grammar. + self._show_grammar = IntVar(self._top) + self._show_grammar.set(1) + + # Create the basic frames. + self._init_menubar(self._top) + self._init_buttons(self._top) + self._init_feedback(self._top) + self._init_grammar(self._top) + self._init_canvas(self._top) + + # Initialize the parser. + self._parser.initialize(self._sent) + + # Resize callback + self._canvas.bind("", self._configure) + + ######################################### + ## Initialization Helpers + ######################################### + + def _init_fonts(self, root): + # See: + self._sysfont = Font(font=Button()["font"]) + root.option_add("*Font", self._sysfont) + + # TWhat's our font size (default=same as sysfont) + self._size = IntVar(root) + self._size.set(self._sysfont.cget("size")) + + self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) + self._font = Font(family="helvetica", size=self._size.get()) + if self._size.get() < 0: + big = self._size.get() - 2 + else: + big = self._size.get() + 2 + self._bigfont = Font(family="helvetica", weight="bold", size=big) + + def _init_grammar(self, parent): + # Grammar view. + self._prodframe = listframe = Frame(parent) + self._prodframe.pack(fill="both", side="left", padx=2) + self._prodlist_label = Label( + self._prodframe, font=self._boldfont, text="Available Expansions" + ) + self._prodlist_label.pack() + self._prodlist = Listbox( + self._prodframe, + selectmode="single", + relief="groove", + background="white", + foreground="#909090", + font=self._font, + selectforeground="#004040", + selectbackground="#c0f0c0", + ) + + self._prodlist.pack(side="right", fill="both", expand=1) + + self._productions = list(self._parser.grammar().productions()) + for production in self._productions: + self._prodlist.insert("end", (" %s" % production)) + self._prodlist.config(height=min(len(self._productions), 25)) + + # Add a scrollbar if there are more than 25 productions. + if len(self._productions) > 25: + listscroll = Scrollbar(self._prodframe, orient="vertical") + self._prodlist.config(yscrollcommand=listscroll.set) + listscroll.config(command=self._prodlist.yview) + listscroll.pack(side="left", fill="y") + + # If they select a production, apply it. + self._prodlist.bind("<>", self._prodlist_select) + + def _init_bindings(self): + # Key bindings are a good thing. + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("e", self.expand) + # self._top.bind('', self.expand) + # self._top.bind('', self.expand) + self._top.bind("m", self.match) + self._top.bind("", self.match) + self._top.bind("", self.match) + self._top.bind("b", self.backtrack) + self._top.bind("", self.backtrack) + self._top.bind("", self.backtrack) + self._top.bind("", self.backtrack) + self._top.bind("", self.backtrack) + self._top.bind("a", self.autostep) + # self._top.bind('', self.autostep) + self._top.bind("", self.autostep) + self._top.bind("", self.cancel_autostep) + self._top.bind("", self.step) + self._top.bind("", self.reset) + self._top.bind("", self.postscript) + # self._top.bind('', self.help) + # self._top.bind('', self.help) + self._top.bind("", self.help) + self._top.bind("", self.help) + # self._top.bind('', self.toggle_grammar) + # self._top.bind('', self.toggle_grammar) + # self._top.bind('', self.toggle_grammar) + self._top.bind("", self.edit_grammar) + self._top.bind("", self.edit_sentence) + + def _init_buttons(self, parent): + # Set up the frames. + self._buttonframe = buttonframe = Frame(parent) + buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) + Button( + buttonframe, + text="Step", + background="#90c0d0", + foreground="black", + command=self.step, + ).pack(side="left") + Button( + buttonframe, + text="Autostep", + background="#90c0d0", + foreground="black", + command=self.autostep, + ).pack(side="left") + Button( + buttonframe, + text="Expand", + underline=0, + background="#90f090", + foreground="black", + command=self.expand, + ).pack(side="left") + Button( + buttonframe, + text="Match", + underline=0, + background="#90f090", + foreground="black", + command=self.match, + ).pack(side="left") + Button( + buttonframe, + text="Backtrack", + underline=0, + background="#f0a0a0", + foreground="black", + command=self.backtrack, + ).pack(side="left") + # Replace autostep... + + # self._autostep_button = Button(buttonframe, text='Autostep', + # underline=0, command=self.autostep) + # self._autostep_button.pack(side='left') + + def _configure(self, event): + self._autostep = 0 + (x1, y1, x2, y2) = self._cframe.scrollregion() + y2 = event.height - 6 + self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) + self._redraw() + + def _init_feedback(self, parent): + self._feedbackframe = feedbackframe = Frame(parent) + feedbackframe.pack(fill="x", side="bottom", padx=3, pady=3) + self._lastoper_label = Label( + feedbackframe, text="Last Operation:", font=self._font + ) + self._lastoper_label.pack(side="left") + lastoperframe = Frame(feedbackframe, relief="sunken", border=1) + lastoperframe.pack(fill="x", side="right", expand=1, padx=5) + self._lastoper1 = Label( + lastoperframe, foreground="#007070", background="#f0f0f0", font=self._font + ) + self._lastoper2 = Label( + lastoperframe, + anchor="w", + width=30, + foreground="#004040", + background="#f0f0f0", + font=self._font, + ) + self._lastoper1.pack(side="left") + self._lastoper2.pack(side="left", fill="x", expand=1) + + def _init_canvas(self, parent): + self._cframe = CanvasFrame( + parent, + background="white", + # width=525, height=250, + closeenough=10, + border=2, + relief="sunken", + ) + self._cframe.pack(expand=1, fill="both", side="top", pady=2) + canvas = self._canvas = self._cframe.canvas() + + # Initially, there's no tree or text + self._tree = None + self._textwidgets = [] + self._textline = None + + def _init_menubar(self, parent): + menubar = Menu(parent) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Reset Parser", underline=0, command=self.reset, accelerator="Del" + ) + filemenu.add_command( + label="Print to Postscript", + underline=0, + command=self.postscript, + accelerator="Ctrl-p", + ) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + editmenu = Menu(menubar, tearoff=0) + editmenu.add_command( + label="Edit Grammar", + underline=5, + command=self.edit_grammar, + accelerator="Ctrl-g", + ) + editmenu.add_command( + label="Edit Text", + underline=5, + command=self.edit_sentence, + accelerator="Ctrl-t", + ) + menubar.add_cascade(label="Edit", underline=0, menu=editmenu) + + rulemenu = Menu(menubar, tearoff=0) + rulemenu.add_command( + label="Step", underline=1, command=self.step, accelerator="Space" + ) + rulemenu.add_separator() + rulemenu.add_command( + label="Match", underline=0, command=self.match, accelerator="Ctrl-m" + ) + rulemenu.add_command( + label="Expand", underline=0, command=self.expand, accelerator="Ctrl-e" + ) + rulemenu.add_separator() + rulemenu.add_command( + label="Backtrack", underline=0, command=self.backtrack, accelerator="Ctrl-b" + ) + menubar.add_cascade(label="Apply", underline=0, menu=rulemenu) + + viewmenu = Menu(menubar, tearoff=0) + viewmenu.add_checkbutton( + label="Show Grammar", + underline=0, + variable=self._show_grammar, + command=self._toggle_grammar, + ) + viewmenu.add_separator() + viewmenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=12, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=14, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=18, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=24, + command=self.resize, + ) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + animatemenu = Menu(menubar, tearoff=0) + animatemenu.add_radiobutton( + label="No Animation", underline=0, variable=self._animation_frames, value=0 + ) + animatemenu.add_radiobutton( + label="Slow Animation", + underline=0, + variable=self._animation_frames, + value=10, + accelerator="-", + ) + animatemenu.add_radiobutton( + label="Normal Animation", + underline=0, + variable=self._animation_frames, + value=5, + accelerator="=", + ) + animatemenu.add_radiobutton( + label="Fast Animation", + underline=0, + variable=self._animation_frames, + value=2, + accelerator="+", + ) + menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + helpmenu.add_command( + label="Instructions", underline=0, command=self.help, accelerator="F1" + ) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + + parent.config(menu=menubar) + + ######################################### + ## Helper + ######################################### + + def _get(self, widget, treeloc): + for i in treeloc: + widget = widget.subtrees()[i] + if isinstance(widget, TreeSegmentWidget): + widget = widget.label() + return widget + + ######################################### + ## Main draw procedure + ######################################### + + def _redraw(self): + canvas = self._canvas + + # Delete the old tree, widgets, etc. + if self._tree is not None: + self._cframe.destroy_widget(self._tree) + for twidget in self._textwidgets: + self._cframe.destroy_widget(twidget) + if self._textline is not None: + self._canvas.delete(self._textline) + + # Draw the tree. + helv = ("helvetica", -self._size.get()) + bold = ("helvetica", -self._size.get(), "bold") + attribs = { + "tree_color": "#000000", + "tree_width": 2, + "node_font": bold, + "leaf_font": helv, + } + tree = self._parser.tree() + self._tree = tree_to_treesegment(canvas, tree, **attribs) + self._cframe.add_widget(self._tree, 30, 5) + + # Draw the text. + helv = ("helvetica", -self._size.get()) + bottom = y = self._cframe.scrollregion()[3] + self._textwidgets = [ + TextWidget(canvas, word, font=self._font) for word in self._sent + ] + for twidget in self._textwidgets: + self._cframe.add_widget(twidget, 0, 0) + twidget.move(0, bottom - twidget.bbox()[3] - 5) + y = min(y, twidget.bbox()[1]) + + # Draw a line over the text, to separate it from the tree. + self._textline = canvas.create_line(-5000, y - 5, 5000, y - 5, dash=".") + + # Highlight appropriate nodes. + self._highlight_nodes() + self._highlight_prodlist() + + # Make sure the text lines up. + self._position_text() + + def _redraw_quick(self): + # This should be more-or-less sufficient after an animation. + self._highlight_nodes() + self._highlight_prodlist() + self._position_text() + + def _highlight_nodes(self): + # Highlight the list of nodes to be checked. + bold = ("helvetica", -self._size.get(), "bold") + for treeloc in self._parser.frontier()[:1]: + self._get(self._tree, treeloc)["color"] = "#20a050" + self._get(self._tree, treeloc)["font"] = bold + for treeloc in self._parser.frontier()[1:]: + self._get(self._tree, treeloc)["color"] = "#008080" + + def _highlight_prodlist(self): + # Highlight the productions that can be expanded. + # Boy, too bad tkinter doesn't implement Listbox.itemconfig; + # that would be pretty useful here. + self._prodlist.delete(0, "end") + expandable = self._parser.expandable_productions() + untried = self._parser.untried_expandable_productions() + productions = self._productions + for index in range(len(productions)): + if productions[index] in expandable: + if productions[index] in untried: + self._prodlist.insert(index, " %s" % productions[index]) + else: + self._prodlist.insert(index, " %s (TRIED)" % productions[index]) + self._prodlist.selection_set(index) + else: + self._prodlist.insert(index, " %s" % productions[index]) + + def _position_text(self): + # Line up the text widgets that are matched against the tree + numwords = len(self._sent) + num_matched = numwords - len(self._parser.remaining_text()) + leaves = self._tree_leaves()[:num_matched] + xmax = self._tree.bbox()[0] + for i in range(0, len(leaves)): + widget = self._textwidgets[i] + leaf = leaves[i] + widget["color"] = "#006040" + leaf["color"] = "#006040" + widget.move(leaf.bbox()[0] - widget.bbox()[0], 0) + xmax = widget.bbox()[2] + 10 + + # Line up the text widgets that are not matched against the tree. + for i in range(len(leaves), numwords): + widget = self._textwidgets[i] + widget["color"] = "#a0a0a0" + widget.move(xmax - widget.bbox()[0], 0) + xmax = widget.bbox()[2] + 10 + + # If we have a complete parse, make everything green :) + if self._parser.currently_complete(): + for twidget in self._textwidgets: + twidget["color"] = "#00a000" + + # Move the matched leaves down to the text. + for i in range(0, len(leaves)): + widget = self._textwidgets[i] + leaf = leaves[i] + dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0 + dy = max(dy, leaf.parent().label().bbox()[3] - leaf.bbox()[3] + 10) + leaf.move(0, dy) + + def _tree_leaves(self, tree=None): + if tree is None: + tree = self._tree + if isinstance(tree, TreeSegmentWidget): + leaves = [] + for child in tree.subtrees(): + leaves += self._tree_leaves(child) + return leaves + else: + return [tree] + + ######################################### + ## Button Callbacks + ######################################### + + def destroy(self, *e): + self._autostep = 0 + if self._top is None: + return + self._top.destroy() + self._top = None + + def reset(self, *e): + self._autostep = 0 + self._parser.initialize(self._sent) + self._lastoper1["text"] = "Reset Application" + self._lastoper2["text"] = "" + self._redraw() + + def autostep(self, *e): + if self._animation_frames.get() == 0: + self._animation_frames.set(2) + if self._autostep: + self._autostep = 0 + else: + self._autostep = 1 + self._step() + + def cancel_autostep(self, *e): + # self._autostep_button['text'] = 'Autostep' + self._autostep = 0 + + # Make sure to stop auto-stepping if we get any user input. + def step(self, *e): + self._autostep = 0 + self._step() + + def match(self, *e): + self._autostep = 0 + self._match() + + def expand(self, *e): + self._autostep = 0 + self._expand() + + def backtrack(self, *e): + self._autostep = 0 + self._backtrack() + + def _step(self): + if self._animating_lock: + return + + # Try expanding, matching, and backtracking (in that order) + if self._expand(): + pass + elif self._parser.untried_match() and self._match(): + pass + elif self._backtrack(): + pass + else: + self._lastoper1["text"] = "Finished" + self._lastoper2["text"] = "" + self._autostep = 0 + + # Check if we just completed a parse. + if self._parser.currently_complete(): + self._autostep = 0 + self._lastoper2["text"] += " [COMPLETE PARSE]" + + def _expand(self, *e): + if self._animating_lock: + return + old_frontier = self._parser.frontier() + rv = self._parser.expand() + if rv is not None: + self._lastoper1["text"] = "Expand:" + self._lastoper2["text"] = rv + self._prodlist.selection_clear(0, "end") + index = self._productions.index(rv) + self._prodlist.selection_set(index) + self._animate_expand(old_frontier[0]) + return True + else: + self._lastoper1["text"] = "Expand:" + self._lastoper2["text"] = "(all expansions tried)" + return False + + def _match(self, *e): + if self._animating_lock: + return + old_frontier = self._parser.frontier() + rv = self._parser.match() + if rv is not None: + self._lastoper1["text"] = "Match:" + self._lastoper2["text"] = rv + self._animate_match(old_frontier[0]) + return True + else: + self._lastoper1["text"] = "Match:" + self._lastoper2["text"] = "(failed)" + return False + + def _backtrack(self, *e): + if self._animating_lock: + return + if self._parser.backtrack(): + elt = self._parser.tree() + for i in self._parser.frontier()[0]: + elt = elt[i] + self._lastoper1["text"] = "Backtrack" + self._lastoper2["text"] = "" + if isinstance(elt, Tree): + self._animate_backtrack(self._parser.frontier()[0]) + else: + self._animate_match_backtrack(self._parser.frontier()[0]) + return True + else: + self._autostep = 0 + self._lastoper1["text"] = "Finished" + self._lastoper2["text"] = "" + return False + + def about(self, *e): + ABOUT = ( + "NLTK Recursive Descent Parser Application\n" + "Written by Edward Loper" + ) + TITLE = "About: Recursive Descent Parser Application" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE).show() + except: + ShowText(self._top, TITLE, ABOUT) + + def help(self, *e): + self._autostep = 0 + # The default font's not very legible; try using 'fixed' instead. + try: + ShowText( + self._top, + "Help: Recursive Descent Parser Application", + (__doc__ or "").strip(), + width=75, + font="fixed", + ) + except: + ShowText( + self._top, + "Help: Recursive Descent Parser Application", + (__doc__ or "").strip(), + width=75, + ) + + def postscript(self, *e): + self._autostep = 0 + self._cframe.print_to_file() + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self._top.mainloop(*args, **kwargs) + + def resize(self, size=None): + if size is not None: + self._size.set(size) + size = self._size.get() + self._font.configure(size=-(abs(size))) + self._boldfont.configure(size=-(abs(size))) + self._sysfont.configure(size=-(abs(size))) + self._bigfont.configure(size=-(abs(size + 2))) + self._redraw() + + ######################################### + ## Expand Production Selection + ######################################### + + def _toggle_grammar(self, *e): + if self._show_grammar.get(): + self._prodframe.pack( + fill="both", side="left", padx=2, after=self._feedbackframe + ) + self._lastoper1["text"] = "Show Grammar" + else: + self._prodframe.pack_forget() + self._lastoper1["text"] = "Hide Grammar" + self._lastoper2["text"] = "" + + # def toggle_grammar(self, *e): + # self._show_grammar = not self._show_grammar + # if self._show_grammar: + # self._prodframe.pack(fill='both', expand='y', side='left', + # after=self._feedbackframe) + # self._lastoper1['text'] = 'Show Grammar' + # else: + # self._prodframe.pack_forget() + # self._lastoper1['text'] = 'Hide Grammar' + # self._lastoper2['text'] = '' + + def _prodlist_select(self, event): + selection = self._prodlist.curselection() + if len(selection) != 1: + return + index = int(selection[0]) + old_frontier = self._parser.frontier() + production = self._parser.expand(self._productions[index]) + + if production: + self._lastoper1["text"] = "Expand:" + self._lastoper2["text"] = production + self._prodlist.selection_clear(0, "end") + self._prodlist.selection_set(index) + self._animate_expand(old_frontier[0]) + else: + # Reset the production selections. + self._prodlist.selection_clear(0, "end") + for prod in self._parser.expandable_productions(): + index = self._productions.index(prod) + self._prodlist.selection_set(index) + + ######################################### + ## Animation + ######################################### + + def _animate_expand(self, treeloc): + oldwidget = self._get(self._tree, treeloc) + oldtree = oldwidget.parent() + top = not isinstance(oldtree.parent(), TreeSegmentWidget) + + tree = self._parser.tree() + for i in treeloc: + tree = tree[i] + + widget = tree_to_treesegment( + self._canvas, + tree, + node_font=self._boldfont, + leaf_color="white", + tree_width=2, + tree_color="white", + node_color="white", + leaf_font=self._font, + ) + widget.label()["color"] = "#20a050" + + (oldx, oldy) = oldtree.label().bbox()[:2] + (newx, newy) = widget.label().bbox()[:2] + widget.move(oldx - newx, oldy - newy) + + if top: + self._cframe.add_widget(widget, 0, 5) + widget.move(30 - widget.label().bbox()[0], 0) + self._tree = widget + else: + oldtree.parent().replace_child(oldtree, widget) + + # Move the children over so they don't overlap. + # Line the children up in a strange way. + if widget.subtrees(): + dx = ( + oldx + + widget.label().width() / 2 + - widget.subtrees()[0].bbox()[0] / 2 + - widget.subtrees()[0].bbox()[2] / 2 + ) + for subtree in widget.subtrees(): + subtree.move(dx, 0) + + self._makeroom(widget) + + if top: + self._cframe.destroy_widget(oldtree) + else: + oldtree.destroy() + + colors = [ + "gray%d" % (10 * int(10 * x / self._animation_frames.get())) + for x in range(self._animation_frames.get(), 0, -1) + ] + + # Move the text string down, if necessary. + dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1] + if dy > 0: + for twidget in self._textwidgets: + twidget.move(0, dy) + self._canvas.move(self._textline, 0, dy) + + self._animate_expand_frame(widget, colors) + + def _makeroom(self, treeseg): + """ + Make sure that no sibling tree bbox's overlap. + """ + parent = treeseg.parent() + if not isinstance(parent, TreeSegmentWidget): + return + + index = parent.subtrees().index(treeseg) + + # Handle siblings to the right + rsiblings = parent.subtrees()[index + 1 :] + if rsiblings: + dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10 + for sibling in rsiblings: + sibling.move(dx, 0) + + # Handle siblings to the left + if index > 0: + lsibling = parent.subtrees()[index - 1] + dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10) + treeseg.move(dx, 0) + + # Keep working up the tree. + self._makeroom(parent) + + def _animate_expand_frame(self, widget, colors): + if len(colors) > 0: + self._animating_lock = 1 + widget["color"] = colors[0] + for subtree in widget.subtrees(): + if isinstance(subtree, TreeSegmentWidget): + subtree.label()["color"] = colors[0] + else: + subtree["color"] = colors[0] + self._top.after(50, self._animate_expand_frame, widget, colors[1:]) + else: + widget["color"] = "black" + for subtree in widget.subtrees(): + if isinstance(subtree, TreeSegmentWidget): + subtree.label()["color"] = "black" + else: + subtree["color"] = "black" + self._redraw_quick() + widget.label()["color"] = "black" + self._animating_lock = 0 + if self._autostep: + self._step() + + def _animate_backtrack(self, treeloc): + # Flash red first, if we're animating. + if self._animation_frames.get() == 0: + colors = [] + else: + colors = ["#a00000", "#000000", "#a00000"] + colors += [ + "gray%d" % (10 * int(10 * x / (self._animation_frames.get()))) + for x in range(1, self._animation_frames.get() + 1) + ] + + widgets = [self._get(self._tree, treeloc).parent()] + for subtree in widgets[0].subtrees(): + if isinstance(subtree, TreeSegmentWidget): + widgets.append(subtree.label()) + else: + widgets.append(subtree) + + self._animate_backtrack_frame(widgets, colors) + + def _animate_backtrack_frame(self, widgets, colors): + if len(colors) > 0: + self._animating_lock = 1 + for widget in widgets: + widget["color"] = colors[0] + self._top.after(50, self._animate_backtrack_frame, widgets, colors[1:]) + else: + for widget in widgets[0].subtrees(): + widgets[0].remove_child(widget) + widget.destroy() + self._redraw_quick() + self._animating_lock = 0 + if self._autostep: + self._step() + + def _animate_match_backtrack(self, treeloc): + widget = self._get(self._tree, treeloc) + node = widget.parent().label() + dy = (node.bbox()[3] - widget.bbox()[1] + 14) / max( + 1, self._animation_frames.get() + ) + self._animate_match_backtrack_frame(self._animation_frames.get(), widget, dy) + + def _animate_match(self, treeloc): + widget = self._get(self._tree, treeloc) + + dy = (self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) / max( + 1, self._animation_frames.get() + ) + self._animate_match_frame(self._animation_frames.get(), widget, dy) + + def _animate_match_frame(self, frame, widget, dy): + if frame > 0: + self._animating_lock = 1 + widget.move(0, dy) + self._top.after(10, self._animate_match_frame, frame - 1, widget, dy) + else: + widget["color"] = "#006040" + self._redraw_quick() + self._animating_lock = 0 + if self._autostep: + self._step() + + def _animate_match_backtrack_frame(self, frame, widget, dy): + if frame > 0: + self._animating_lock = 1 + widget.move(0, dy) + self._top.after( + 10, self._animate_match_backtrack_frame, frame - 1, widget, dy + ) + else: + widget.parent().remove_child(widget) + widget.destroy() + self._animating_lock = 0 + if self._autostep: + self._step() + + def edit_grammar(self, *e): + CFGEditor(self._top, self._parser.grammar(), self.set_grammar) + + def set_grammar(self, grammar): + self._parser.set_grammar(grammar) + self._productions = list(grammar.productions()) + self._prodlist.delete(0, "end") + for production in self._productions: + self._prodlist.insert("end", (" %s" % production)) + + def edit_sentence(self, *e): + sentence = " ".join(self._sent) + title = "Edit Text" + instr = "Enter a new sentence to parse." + EntryDialog(self._top, sentence, instr, self.set_sentence, title) + + def set_sentence(self, sentence): + self._sent = sentence.split() # [XX] use tagged? + self.reset() + + +def app(): + """ + Create a recursive descent parser demo, using a simple grammar and + text. + """ + from nltk.grammar import CFG + + grammar = CFG.fromstring( + """ + # Grammatical productions. + S -> NP VP + NP -> Det N PP | Det N + VP -> V NP PP | V NP | V + PP -> P NP + # Lexical productions. + NP -> 'I' + Det -> 'the' | 'a' + N -> 'man' | 'park' | 'dog' | 'telescope' + V -> 'ate' | 'saw' + P -> 'in' | 'under' | 'with' + """ + ) + + sent = "the dog saw a man in the park".split() + + RecursiveDescentApp(grammar, sent).mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/srparser_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/srparser_app.py new file mode 100644 index 0000000..58eeab3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/srparser_app.py @@ -0,0 +1,937 @@ +# Natural Language Toolkit: Shift-Reduce Parser Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A graphical tool for exploring the shift-reduce parser. + +The shift-reduce parser maintains a stack, which records the structure +of the portion of the text that has been parsed. The stack is +initially empty. Its contents are shown on the left side of the main +canvas. + +On the right side of the main canvas is the remaining text. This is +the portion of the text which has not yet been considered by the +parser. + +The parser builds up a tree structure for the text using two +operations: + + - "shift" moves the first token from the remaining text to the top + of the stack. In the demo, the top of the stack is its right-hand + side. + - "reduce" uses a grammar production to combine the rightmost stack + elements into a single tree token. + +You can control the parser's operation by using the "shift" and +"reduce" buttons; or you can use the "step" button to let the parser +automatically decide which operation to apply. The parser uses the +following rules to decide which operation to apply: + + - Only shift if no reductions are available. + - If multiple reductions are available, then apply the reduction + whose CFG production is listed earliest in the grammar. + +The "reduce" button applies the reduction whose CFG production is +listed earliest in the grammar. There are two ways to manually choose +which reduction to apply: + + - Click on a CFG production from the list of available reductions, + on the left side of the main window. The reduction based on that + production will be applied to the top of the stack. + - Click on one of the stack elements. A popup window will appear, + containing all available reductions. Select one, and it will be + applied to the top of the stack. + +Note that reductions can only be applied to the top of the stack. + +Keyboard Shortcuts:: + [Space]\t Perform the next shift or reduce operation + [s]\t Perform a shift operation + [r]\t Perform a reduction operation + [Ctrl-z]\t Undo most recent operation + [Delete]\t Reset the parser + [g]\t Show/hide available production list + [Ctrl-a]\t Toggle animations + [h]\t Help + [Ctrl-p]\t Print + [q]\t Quit + +""" + +from tkinter import Button, Frame, IntVar, Label, Listbox, Menu, Scrollbar, Tk +from tkinter.font import Font + +from nltk.draw import CFGEditor, TreeSegmentWidget, tree_to_treesegment +from nltk.draw.util import CanvasFrame, EntryDialog, ShowText, TextWidget +from nltk.parse import SteppingShiftReduceParser +from nltk.tree import Tree +from nltk.util import in_idle + +""" +Possible future improvements: + - button/window to change and/or select text. Just pop up a window + with an entry, and let them modify the text; and then retokenize + it? Maybe give a warning if it contains tokens whose types are + not in the grammar. + - button/window to change and/or select grammar. Select from + several alternative grammars? Or actually change the grammar? If + the later, then I'd want to define nltk.draw.cfg, which would be + responsible for that. +""" + + +class ShiftReduceApp: + """ + A graphical tool for exploring the shift-reduce parser. The tool + displays the parser's stack and the remaining text, and allows the + user to control the parser's operation. In particular, the user + can shift tokens onto the stack, and can perform reductions on the + top elements of the stack. A "step" button simply steps through + the parsing process, performing the operations that + ``nltk.parse.ShiftReduceParser`` would use. + """ + + def __init__(self, grammar, sent, trace=0): + self._sent = sent + self._parser = SteppingShiftReduceParser(grammar, trace) + + # Set up the main window. + self._top = Tk() + self._top.title("Shift Reduce Parser Application") + + # Animations. animating_lock is a lock to prevent the demo + # from performing new operations while it's animating. + self._animating_lock = 0 + self._animate = IntVar(self._top) + self._animate.set(10) # = medium + + # The user can hide the grammar. + self._show_grammar = IntVar(self._top) + self._show_grammar.set(1) + + # Initialize fonts. + self._init_fonts(self._top) + + # Set up key bindings. + self._init_bindings() + + # Create the basic frames. + self._init_menubar(self._top) + self._init_buttons(self._top) + self._init_feedback(self._top) + self._init_grammar(self._top) + self._init_canvas(self._top) + + # A popup menu for reducing. + self._reduce_menu = Menu(self._canvas, tearoff=0) + + # Reset the demo, and set the feedback frame to empty. + self.reset() + self._lastoper1["text"] = "" + + ######################################### + ## Initialization Helpers + ######################################### + + def _init_fonts(self, root): + # See: + self._sysfont = Font(font=Button()["font"]) + root.option_add("*Font", self._sysfont) + + # TWhat's our font size (default=same as sysfont) + self._size = IntVar(root) + self._size.set(self._sysfont.cget("size")) + + self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) + self._font = Font(family="helvetica", size=self._size.get()) + + def _init_grammar(self, parent): + # Grammar view. + self._prodframe = listframe = Frame(parent) + self._prodframe.pack(fill="both", side="left", padx=2) + self._prodlist_label = Label( + self._prodframe, font=self._boldfont, text="Available Reductions" + ) + self._prodlist_label.pack() + self._prodlist = Listbox( + self._prodframe, + selectmode="single", + relief="groove", + background="white", + foreground="#909090", + font=self._font, + selectforeground="#004040", + selectbackground="#c0f0c0", + ) + + self._prodlist.pack(side="right", fill="both", expand=1) + + self._productions = list(self._parser.grammar().productions()) + for production in self._productions: + self._prodlist.insert("end", (" %s" % production)) + self._prodlist.config(height=min(len(self._productions), 25)) + + # Add a scrollbar if there are more than 25 productions. + if 1: # len(self._productions) > 25: + listscroll = Scrollbar(self._prodframe, orient="vertical") + self._prodlist.config(yscrollcommand=listscroll.set) + listscroll.config(command=self._prodlist.yview) + listscroll.pack(side="left", fill="y") + + # If they select a production, apply it. + self._prodlist.bind("<>", self._prodlist_select) + + # When they hover over a production, highlight it. + self._hover = -1 + self._prodlist.bind("", self._highlight_hover) + self._prodlist.bind("", self._clear_hover) + + def _init_bindings(self): + # Quit + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + + # Ops (step, shift, reduce, undo) + self._top.bind("", self.step) + self._top.bind("", self.shift) + self._top.bind("", self.shift) + self._top.bind("", self.shift) + self._top.bind("", self.reduce) + self._top.bind("", self.reduce) + self._top.bind("", self.reduce) + self._top.bind("", self.reset) + self._top.bind("", self.undo) + self._top.bind("", self.undo) + self._top.bind("", self.undo) + self._top.bind("", self.undo) + self._top.bind("", self.undo) + + # Misc + self._top.bind("", self.postscript) + self._top.bind("", self.help) + self._top.bind("", self.help) + self._top.bind("", self.edit_grammar) + self._top.bind("", self.edit_sentence) + + # Animation speed control + self._top.bind("-", lambda e, a=self._animate: a.set(20)) + self._top.bind("=", lambda e, a=self._animate: a.set(10)) + self._top.bind("+", lambda e, a=self._animate: a.set(4)) + + def _init_buttons(self, parent): + # Set up the frames. + self._buttonframe = buttonframe = Frame(parent) + buttonframe.pack(fill="none", side="bottom") + Button( + buttonframe, + text="Step", + background="#90c0d0", + foreground="black", + command=self.step, + ).pack(side="left") + Button( + buttonframe, + text="Shift", + underline=0, + background="#90f090", + foreground="black", + command=self.shift, + ).pack(side="left") + Button( + buttonframe, + text="Reduce", + underline=0, + background="#90f090", + foreground="black", + command=self.reduce, + ).pack(side="left") + Button( + buttonframe, + text="Undo", + underline=0, + background="#f0a0a0", + foreground="black", + command=self.undo, + ).pack(side="left") + + def _init_menubar(self, parent): + menubar = Menu(parent) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Reset Parser", underline=0, command=self.reset, accelerator="Del" + ) + filemenu.add_command( + label="Print to Postscript", + underline=0, + command=self.postscript, + accelerator="Ctrl-p", + ) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + editmenu = Menu(menubar, tearoff=0) + editmenu.add_command( + label="Edit Grammar", + underline=5, + command=self.edit_grammar, + accelerator="Ctrl-g", + ) + editmenu.add_command( + label="Edit Text", + underline=5, + command=self.edit_sentence, + accelerator="Ctrl-t", + ) + menubar.add_cascade(label="Edit", underline=0, menu=editmenu) + + rulemenu = Menu(menubar, tearoff=0) + rulemenu.add_command( + label="Step", underline=1, command=self.step, accelerator="Space" + ) + rulemenu.add_separator() + rulemenu.add_command( + label="Shift", underline=0, command=self.shift, accelerator="Ctrl-s" + ) + rulemenu.add_command( + label="Reduce", underline=0, command=self.reduce, accelerator="Ctrl-r" + ) + rulemenu.add_separator() + rulemenu.add_command( + label="Undo", underline=0, command=self.undo, accelerator="Ctrl-u" + ) + menubar.add_cascade(label="Apply", underline=0, menu=rulemenu) + + viewmenu = Menu(menubar, tearoff=0) + viewmenu.add_checkbutton( + label="Show Grammar", + underline=0, + variable=self._show_grammar, + command=self._toggle_grammar, + ) + viewmenu.add_separator() + viewmenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=12, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=14, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=18, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=24, + command=self.resize, + ) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + animatemenu = Menu(menubar, tearoff=0) + animatemenu.add_radiobutton( + label="No Animation", underline=0, variable=self._animate, value=0 + ) + animatemenu.add_radiobutton( + label="Slow Animation", + underline=0, + variable=self._animate, + value=20, + accelerator="-", + ) + animatemenu.add_radiobutton( + label="Normal Animation", + underline=0, + variable=self._animate, + value=10, + accelerator="=", + ) + animatemenu.add_radiobutton( + label="Fast Animation", + underline=0, + variable=self._animate, + value=4, + accelerator="+", + ) + menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + helpmenu.add_command( + label="Instructions", underline=0, command=self.help, accelerator="F1" + ) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + + parent.config(menu=menubar) + + def _init_feedback(self, parent): + self._feedbackframe = feedbackframe = Frame(parent) + feedbackframe.pack(fill="x", side="bottom", padx=3, pady=3) + self._lastoper_label = Label( + feedbackframe, text="Last Operation:", font=self._font + ) + self._lastoper_label.pack(side="left") + lastoperframe = Frame(feedbackframe, relief="sunken", border=1) + lastoperframe.pack(fill="x", side="right", expand=1, padx=5) + self._lastoper1 = Label( + lastoperframe, foreground="#007070", background="#f0f0f0", font=self._font + ) + self._lastoper2 = Label( + lastoperframe, + anchor="w", + width=30, + foreground="#004040", + background="#f0f0f0", + font=self._font, + ) + self._lastoper1.pack(side="left") + self._lastoper2.pack(side="left", fill="x", expand=1) + + def _init_canvas(self, parent): + self._cframe = CanvasFrame( + parent, + background="white", + width=525, + closeenough=10, + border=2, + relief="sunken", + ) + self._cframe.pack(expand=1, fill="both", side="top", pady=2) + canvas = self._canvas = self._cframe.canvas() + + self._stackwidgets = [] + self._rtextwidgets = [] + self._titlebar = canvas.create_rectangle( + 0, 0, 0, 0, fill="#c0f0f0", outline="black" + ) + self._exprline = canvas.create_line(0, 0, 0, 0, dash=".") + self._stacktop = canvas.create_line(0, 0, 0, 0, fill="#408080") + size = self._size.get() + 4 + self._stacklabel = TextWidget( + canvas, "Stack", color="#004040", font=self._boldfont + ) + self._rtextlabel = TextWidget( + canvas, "Remaining Text", color="#004040", font=self._boldfont + ) + self._cframe.add_widget(self._stacklabel) + self._cframe.add_widget(self._rtextlabel) + + ######################################### + ## Main draw procedure + ######################################### + + def _redraw(self): + scrollregion = self._canvas["scrollregion"].split() + (cx1, cy1, cx2, cy2) = (int(c) for c in scrollregion) + + # Delete the old stack & rtext widgets. + for stackwidget in self._stackwidgets: + self._cframe.destroy_widget(stackwidget) + self._stackwidgets = [] + for rtextwidget in self._rtextwidgets: + self._cframe.destroy_widget(rtextwidget) + self._rtextwidgets = [] + + # Position the titlebar & exprline + (x1, y1, x2, y2) = self._stacklabel.bbox() + y = y2 - y1 + 10 + self._canvas.coords(self._titlebar, -5000, 0, 5000, y - 4) + self._canvas.coords(self._exprline, 0, y * 2 - 10, 5000, y * 2 - 10) + + # Position the titlebar labels.. + (x1, y1, x2, y2) = self._stacklabel.bbox() + self._stacklabel.move(5 - x1, 3 - y1) + (x1, y1, x2, y2) = self._rtextlabel.bbox() + self._rtextlabel.move(cx2 - x2 - 5, 3 - y1) + + # Draw the stack. + stackx = 5 + for tok in self._parser.stack(): + if isinstance(tok, Tree): + attribs = { + "tree_color": "#4080a0", + "tree_width": 2, + "node_font": self._boldfont, + "node_color": "#006060", + "leaf_color": "#006060", + "leaf_font": self._font, + } + widget = tree_to_treesegment(self._canvas, tok, **attribs) + widget.label()["color"] = "#000000" + else: + widget = TextWidget(self._canvas, tok, color="#000000", font=self._font) + widget.bind_click(self._popup_reduce) + self._stackwidgets.append(widget) + self._cframe.add_widget(widget, stackx, y) + stackx = widget.bbox()[2] + 10 + + # Draw the remaining text. + rtextwidth = 0 + for tok in self._parser.remaining_text(): + widget = TextWidget(self._canvas, tok, color="#000000", font=self._font) + self._rtextwidgets.append(widget) + self._cframe.add_widget(widget, rtextwidth, y) + rtextwidth = widget.bbox()[2] + 4 + + # Allow enough room to shift the next token (for animations) + if len(self._rtextwidgets) > 0: + stackx += self._rtextwidgets[0].width() + + # Move the remaining text to the correct location (keep it + # right-justified, when possible); and move the remaining text + # label, if necessary. + stackx = max(stackx, self._stacklabel.width() + 25) + rlabelwidth = self._rtextlabel.width() + 10 + if stackx >= cx2 - max(rtextwidth, rlabelwidth): + cx2 = stackx + max(rtextwidth, rlabelwidth) + for rtextwidget in self._rtextwidgets: + rtextwidget.move(4 + cx2 - rtextwidth, 0) + self._rtextlabel.move(cx2 - self._rtextlabel.bbox()[2] - 5, 0) + + midx = (stackx + cx2 - max(rtextwidth, rlabelwidth)) / 2 + self._canvas.coords(self._stacktop, midx, 0, midx, 5000) + (x1, y1, x2, y2) = self._stacklabel.bbox() + + # Set up binding to allow them to shift a token by dragging it. + if len(self._rtextwidgets) > 0: + + def drag_shift(widget, midx=midx, self=self): + if widget.bbox()[0] < midx: + self.shift() + else: + self._redraw() + + self._rtextwidgets[0].bind_drag(drag_shift) + self._rtextwidgets[0].bind_click(self.shift) + + # Draw the stack top. + self._highlight_productions() + + def _draw_stack_top(self, widget): + # hack.. + midx = widget.bbox()[2] + 50 + self._canvas.coords(self._stacktop, midx, 0, midx, 5000) + + def _highlight_productions(self): + # Highlight the productions that can be reduced. + self._prodlist.selection_clear(0, "end") + for prod in self._parser.reducible_productions(): + index = self._productions.index(prod) + self._prodlist.selection_set(index) + + ######################################### + ## Button Callbacks + ######################################### + + def destroy(self, *e): + if self._top is None: + return + self._top.destroy() + self._top = None + + def reset(self, *e): + self._parser.initialize(self._sent) + self._lastoper1["text"] = "Reset App" + self._lastoper2["text"] = "" + self._redraw() + + def step(self, *e): + if self.reduce(): + return True + elif self.shift(): + return True + else: + if list(self._parser.parses()): + self._lastoper1["text"] = "Finished:" + self._lastoper2["text"] = "Success" + else: + self._lastoper1["text"] = "Finished:" + self._lastoper2["text"] = "Failure" + + def shift(self, *e): + if self._animating_lock: + return + if self._parser.shift(): + tok = self._parser.stack()[-1] + self._lastoper1["text"] = "Shift:" + self._lastoper2["text"] = "%r" % tok + if self._animate.get(): + self._animate_shift() + else: + self._redraw() + return True + return False + + def reduce(self, *e): + if self._animating_lock: + return + production = self._parser.reduce() + if production: + self._lastoper1["text"] = "Reduce:" + self._lastoper2["text"] = "%s" % production + if self._animate.get(): + self._animate_reduce() + else: + self._redraw() + return production + + def undo(self, *e): + if self._animating_lock: + return + if self._parser.undo(): + self._redraw() + + def postscript(self, *e): + self._cframe.print_to_file() + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self._top.mainloop(*args, **kwargs) + + ######################################### + ## Menubar callbacks + ######################################### + + def resize(self, size=None): + if size is not None: + self._size.set(size) + size = self._size.get() + self._font.configure(size=-(abs(size))) + self._boldfont.configure(size=-(abs(size))) + self._sysfont.configure(size=-(abs(size))) + + # self._stacklabel['font'] = ('helvetica', -size-4, 'bold') + # self._rtextlabel['font'] = ('helvetica', -size-4, 'bold') + # self._lastoper_label['font'] = ('helvetica', -size) + # self._lastoper1['font'] = ('helvetica', -size) + # self._lastoper2['font'] = ('helvetica', -size) + # self._prodlist['font'] = ('helvetica', -size) + # self._prodlist_label['font'] = ('helvetica', -size-2, 'bold') + self._redraw() + + def help(self, *e): + # The default font's not very legible; try using 'fixed' instead. + try: + ShowText( + self._top, + "Help: Shift-Reduce Parser Application", + (__doc__ or "").strip(), + width=75, + font="fixed", + ) + except: + ShowText( + self._top, + "Help: Shift-Reduce Parser Application", + (__doc__ or "").strip(), + width=75, + ) + + def about(self, *e): + ABOUT = "NLTK Shift-Reduce Parser Application\n" + "Written by Edward Loper" + TITLE = "About: Shift-Reduce Parser Application" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE).show() + except: + ShowText(self._top, TITLE, ABOUT) + + def edit_grammar(self, *e): + CFGEditor(self._top, self._parser.grammar(), self.set_grammar) + + def set_grammar(self, grammar): + self._parser.set_grammar(grammar) + self._productions = list(grammar.productions()) + self._prodlist.delete(0, "end") + for production in self._productions: + self._prodlist.insert("end", (" %s" % production)) + + def edit_sentence(self, *e): + sentence = " ".join(self._sent) + title = "Edit Text" + instr = "Enter a new sentence to parse." + EntryDialog(self._top, sentence, instr, self.set_sentence, title) + + def set_sentence(self, sent): + self._sent = sent.split() # [XX] use tagged? + self.reset() + + ######################################### + ## Reduce Production Selection + ######################################### + + def _toggle_grammar(self, *e): + if self._show_grammar.get(): + self._prodframe.pack( + fill="both", side="left", padx=2, after=self._feedbackframe + ) + self._lastoper1["text"] = "Show Grammar" + else: + self._prodframe.pack_forget() + self._lastoper1["text"] = "Hide Grammar" + self._lastoper2["text"] = "" + + def _prodlist_select(self, event): + selection = self._prodlist.curselection() + if len(selection) != 1: + return + index = int(selection[0]) + production = self._parser.reduce(self._productions[index]) + if production: + self._lastoper1["text"] = "Reduce:" + self._lastoper2["text"] = "%s" % production + if self._animate.get(): + self._animate_reduce() + else: + self._redraw() + else: + # Reset the production selections. + self._prodlist.selection_clear(0, "end") + for prod in self._parser.reducible_productions(): + index = self._productions.index(prod) + self._prodlist.selection_set(index) + + def _popup_reduce(self, widget): + # Remove old commands. + productions = self._parser.reducible_productions() + if len(productions) == 0: + return + + self._reduce_menu.delete(0, "end") + for production in productions: + self._reduce_menu.add_command(label=str(production), command=self.reduce) + self._reduce_menu.post( + self._canvas.winfo_pointerx(), self._canvas.winfo_pointery() + ) + + ######################################### + ## Animations + ######################################### + + def _animate_shift(self): + # What widget are we shifting? + widget = self._rtextwidgets[0] + + # Where are we shifting from & to? + right = widget.bbox()[0] + if len(self._stackwidgets) == 0: + left = 5 + else: + left = self._stackwidgets[-1].bbox()[2] + 10 + + # Start animating. + dt = self._animate.get() + dx = (left - right) * 1.0 / dt + self._animate_shift_frame(dt, widget, dx) + + def _animate_shift_frame(self, frame, widget, dx): + if frame > 0: + self._animating_lock = 1 + widget.move(dx, 0) + self._top.after(10, self._animate_shift_frame, frame - 1, widget, dx) + else: + # but: stacktop?? + + # Shift the widget to the stack. + del self._rtextwidgets[0] + self._stackwidgets.append(widget) + self._animating_lock = 0 + + # Display the available productions. + self._draw_stack_top(widget) + self._highlight_productions() + + def _animate_reduce(self): + # What widgets are we shifting? + numwidgets = len(self._parser.stack()[-1]) # number of children + widgets = self._stackwidgets[-numwidgets:] + + # How far are we moving? + if isinstance(widgets[0], TreeSegmentWidget): + ydist = 15 + widgets[0].label().height() + else: + ydist = 15 + widgets[0].height() + + # Start animating. + dt = self._animate.get() + dy = ydist * 2.0 / dt + self._animate_reduce_frame(dt / 2, widgets, dy) + + def _animate_reduce_frame(self, frame, widgets, dy): + if frame > 0: + self._animating_lock = 1 + for widget in widgets: + widget.move(0, dy) + self._top.after(10, self._animate_reduce_frame, frame - 1, widgets, dy) + else: + del self._stackwidgets[-len(widgets) :] + for widget in widgets: + self._cframe.remove_widget(widget) + tok = self._parser.stack()[-1] + if not isinstance(tok, Tree): + raise ValueError() + label = TextWidget( + self._canvas, str(tok.label()), color="#006060", font=self._boldfont + ) + widget = TreeSegmentWidget(self._canvas, label, widgets, width=2) + (x1, y1, x2, y2) = self._stacklabel.bbox() + y = y2 - y1 + 10 + if not self._stackwidgets: + x = 5 + else: + x = self._stackwidgets[-1].bbox()[2] + 10 + self._cframe.add_widget(widget, x, y) + self._stackwidgets.append(widget) + + # Display the available productions. + self._draw_stack_top(widget) + self._highlight_productions() + + # # Delete the old widgets.. + # del self._stackwidgets[-len(widgets):] + # for widget in widgets: + # self._cframe.destroy_widget(widget) + # + # # Make a new one. + # tok = self._parser.stack()[-1] + # if isinstance(tok, Tree): + # attribs = {'tree_color': '#4080a0', 'tree_width': 2, + # 'node_font': bold, 'node_color': '#006060', + # 'leaf_color': '#006060', 'leaf_font':self._font} + # widget = tree_to_treesegment(self._canvas, tok.type(), + # **attribs) + # widget.node()['color'] = '#000000' + # else: + # widget = TextWidget(self._canvas, tok.type(), + # color='#000000', font=self._font) + # widget.bind_click(self._popup_reduce) + # (x1, y1, x2, y2) = self._stacklabel.bbox() + # y = y2-y1+10 + # if not self._stackwidgets: x = 5 + # else: x = self._stackwidgets[-1].bbox()[2] + 10 + # self._cframe.add_widget(widget, x, y) + # self._stackwidgets.append(widget) + + # self._redraw() + self._animating_lock = 0 + + ######################################### + ## Hovering. + ######################################### + + def _highlight_hover(self, event): + # What production are we hovering over? + index = self._prodlist.nearest(event.y) + if self._hover == index: + return + + # Clear any previous hover highlighting. + self._clear_hover() + + # If the production corresponds to an available reduction, + # highlight the stack. + selection = [int(s) for s in self._prodlist.curselection()] + if index in selection: + rhslen = len(self._productions[index].rhs()) + for stackwidget in self._stackwidgets[-rhslen:]: + if isinstance(stackwidget, TreeSegmentWidget): + stackwidget.label()["color"] = "#00a000" + else: + stackwidget["color"] = "#00a000" + + # Remember what production we're hovering over. + self._hover = index + + def _clear_hover(self, *event): + # Clear any previous hover highlighting. + if self._hover == -1: + return + self._hover = -1 + for stackwidget in self._stackwidgets: + if isinstance(stackwidget, TreeSegmentWidget): + stackwidget.label()["color"] = "black" + else: + stackwidget["color"] = "black" + + +def app(): + """ + Create a shift reduce parser app, using a simple grammar and + text. + """ + + from nltk.grammar import CFG, Nonterminal, Production + + nonterminals = "S VP NP PP P N Name V Det" + (S, VP, NP, PP, P, N, Name, V, Det) = (Nonterminal(s) for s in nonterminals.split()) + + productions = ( + # Syntactic Productions + Production(S, [NP, VP]), + Production(NP, [Det, N]), + Production(NP, [NP, PP]), + Production(VP, [VP, PP]), + Production(VP, [V, NP, PP]), + Production(VP, [V, NP]), + Production(PP, [P, NP]), + # Lexical Productions + Production(NP, ["I"]), + Production(Det, ["the"]), + Production(Det, ["a"]), + Production(N, ["man"]), + Production(V, ["saw"]), + Production(P, ["in"]), + Production(P, ["with"]), + Production(N, ["park"]), + Production(N, ["dog"]), + Production(N, ["statue"]), + Production(Det, ["my"]), + ) + + grammar = CFG(S, productions) + + # tokenize the sentence + sent = "my dog saw a man in the park with a statue".split() + + ShiftReduceApp(grammar, sent).mainloop() + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordfreq_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordfreq_app.py new file mode 100644 index 0000000..607186e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordfreq_app.py @@ -0,0 +1,36 @@ +# Natural Language Toolkit: Wordfreq Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Sumukh Ghodke +# URL: +# For license information, see LICENSE.TXT + +from matplotlib import pylab + +from nltk.corpus import gutenberg +from nltk.text import Text + + +def plot_word_freq_dist(text): + fd = text.vocab() + + samples = [item for item, _ in fd.most_common(50)] + values = [fd[sample] for sample in samples] + values = [sum(values[: i + 1]) * 100.0 / fd.N() for i in range(len(values))] + pylab.title(text.name) + pylab.xlabel("Samples") + pylab.ylabel("Cumulative Percentage") + pylab.plot(values) + pylab.xticks(range(len(samples)), [str(s) for s in samples], rotation=90) + pylab.show() + + +def app(): + t1 = Text(gutenberg.words("melville-moby_dick.txt")) + plot_word_freq_dist(t1) + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordnet_app.py b/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordnet_app.py new file mode 100644 index 0000000..196be8d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/app/wordnet_app.py @@ -0,0 +1,1005 @@ +# Natural Language Toolkit: WordNet Browser Application +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Jussi Salmela +# Paul Bone +# URL: +# For license information, see LICENSE.TXT + +""" +A WordNet Browser application which launches the default browser +(if it is not already running) and opens a new tab with a connection +to http://localhost:port/ . It also starts an HTTP server on the +specified port and begins serving browser requests. The default +port is 8000. (For command-line help, run "python wordnet -h") +This application requires that the user's web browser supports +Javascript. + +BrowServer is a server for browsing the NLTK Wordnet database It first +launches a browser client to be used for browsing and then starts +serving the requests of that and maybe other clients + +Usage:: + + browserver.py -h + browserver.py [-s] [-p ] + +Options:: + + -h or --help + Display this help message. + + -l or --log-file + Logs messages to the given file, If this option is not specified + messages are silently dropped. + + -p or --port + Run the web server on this TCP port, defaults to 8000. + + -s or --server-mode + Do not start a web browser, and do not allow a user to + shutdown the server through the web interface. +""" +# TODO: throughout this package variable names and docstrings need +# modifying to be compliant with NLTK's coding standards. Tests also +# need to be develop to ensure this continues to work in the face of +# changes to other NLTK packages. + +import base64 +import copy +import getopt +import io +import os +import pickle +import sys +import threading +import time +import webbrowser +from collections import defaultdict +from http.server import BaseHTTPRequestHandler, HTTPServer + +# Allow this program to run inside the NLTK source tree. +from sys import argv +from urllib.parse import unquote_plus + +from nltk.corpus import wordnet as wn +from nltk.corpus.reader.wordnet import Lemma, Synset + +firstClient = True + +# True if we're not also running a web browser. The value f server_mode +# gets set by demo(). +server_mode = None + +# If set this is a file object for writing log messages. +logfile = None + + +class MyServerHandler(BaseHTTPRequestHandler): + def do_HEAD(self): + self.send_head() + + def do_GET(self): + global firstClient + sp = self.path[1:] + if unquote_plus(sp) == "SHUTDOWN THE SERVER": + if server_mode: + page = "Server must be killed with SIGTERM." + type = "text/plain" + else: + print("Server shutting down!") + os._exit(0) + + elif sp == "": # First request. + type = "text/html" + if not server_mode and firstClient: + firstClient = False + page = get_static_index_page(True) + else: + page = get_static_index_page(False) + word = "green" + + elif sp.endswith(".html"): # Trying to fetch a HTML file TODO: + type = "text/html" + usp = unquote_plus(sp) + if usp == "NLTK Wordnet Browser Database Info.html": + word = "* Database Info *" + if os.path.isfile(usp): + with open(usp) as infile: + page = infile.read() + else: + page = ( + (html_header % word) + "

    The database info file:" + "

    " + + usp + + "" + + "

    was not found. Run this:" + + "

    python dbinfo_html.py" + + "

    to produce it." + + html_trailer + ) + else: + # Handle files here. + word = sp + try: + page = get_static_page_by_path(usp) + except FileNotFoundError: + page = "Internal error: Path for static page '%s' is unknown" % usp + # Set type to plain to prevent XSS by printing the path as HTML + type = "text/plain" + elif sp.startswith("search"): + # This doesn't seem to work with MWEs. + type = "text/html" + parts = (sp.split("?")[1]).split("&") + word = [ + p.split("=")[1].replace("+", " ") + for p in parts + if p.startswith("nextWord") + ][0] + page, word = page_from_word(word) + elif sp.startswith("lookup_"): + # TODO add a variation of this that takes a non ecoded word or MWE. + type = "text/html" + sp = sp[len("lookup_") :] + page, word = page_from_href(sp) + elif sp == "start_page": + # if this is the first request we should display help + # information, and possibly set a default word. + type = "text/html" + page, word = page_from_word("wordnet") + else: + type = "text/plain" + page = "Could not parse request: '%s'" % sp + + # Send result. + self.send_head(type) + self.wfile.write(page.encode("utf8")) + + def send_head(self, type=None): + self.send_response(200) + self.send_header("Content-type", type) + self.end_headers() + + def log_message(self, format, *args): + global logfile + + if logfile: + logfile.write( + "%s - - [%s] %s\n" + % (self.address_string(), self.log_date_time_string(), format % args) + ) + + +def get_unique_counter_from_url(sp): + """ + Extract the unique counter from the URL if it has one. Otherwise return + null. + """ + pos = sp.rfind("%23") + if pos != -1: + return int(sp[(pos + 3) :]) + else: + return None + + +def wnb(port=8000, runBrowser=True, logfilename=None): + """ + Run NLTK Wordnet Browser Server. + + :param port: The port number for the server to listen on, defaults to + 8000 + :type port: int + + :param runBrowser: True to start a web browser and point it at the web + server. + :type runBrowser: bool + """ + # The webbrowser module is unpredictable, typically it blocks if it uses + # a console web browser, and doesn't block if it uses a GUI webbrowser, + # so we need to force it to have a clear correct behaviour. + # + # Normally the server should run for as long as the user wants. they + # should idealy be able to control this from the UI by closing the + # window or tab. Second best would be clicking a button to say + # 'Shutdown' that first shutsdown the server and closes the window or + # tab, or exits the text-mode browser. Both of these are unfreasable. + # + # The next best alternative is to start the server, have it close when + # it receives SIGTERM (default), and run the browser as well. The user + # may have to shutdown both programs. + # + # Since webbrowser may block, and the webserver will block, we must run + # them in separate threads. + # + global server_mode, logfile + server_mode = not runBrowser + + # Setup logging. + if logfilename: + try: + logfile = open(logfilename, "a", 1) # 1 means 'line buffering' + except OSError as e: + sys.stderr.write("Couldn't open %s for writing: %s", logfilename, e) + sys.exit(1) + else: + logfile = None + + # Compute URL and start web browser + url = "http://localhost:" + str(port) + + server_ready = None + browser_thread = None + + if runBrowser: + server_ready = threading.Event() + browser_thread = startBrowser(url, server_ready) + + # Start the server. + server = HTTPServer(("", port), MyServerHandler) + if logfile: + logfile.write("NLTK Wordnet browser server running serving: %s\n" % url) + if runBrowser: + server_ready.set() + + try: + server.serve_forever() + except KeyboardInterrupt: + pass + + if runBrowser: + browser_thread.join() + + if logfile: + logfile.close() + + +def startBrowser(url, server_ready): + def run(): + server_ready.wait() + time.sleep(1) # Wait a little bit more, there's still the chance of + # a race condition. + webbrowser.open(url, new=2, autoraise=1) + + t = threading.Thread(target=run) + t.start() + return t + + +##################################################################### +# Utilities +##################################################################### + + +""" +WordNet Browser Utilities. + +This provides a backend to both wxbrowse and browserver.py. +""" + +################################################################################ +# +# Main logic for wordnet browser. +# + +# This is wrapped inside a function since wn is only available if the +# WordNet corpus is installed. +def _pos_tuples(): + return [ + (wn.NOUN, "N", "noun"), + (wn.VERB, "V", "verb"), + (wn.ADJ, "J", "adj"), + (wn.ADV, "R", "adv"), + ] + + +def _pos_match(pos_tuple): + """ + This function returns the complete pos tuple for the partial pos + tuple given to it. It attempts to match it against the first + non-null component of the given pos tuple. + """ + if pos_tuple[0] == "s": + pos_tuple = ("a", pos_tuple[1], pos_tuple[2]) + for n, x in enumerate(pos_tuple): + if x is not None: + break + for pt in _pos_tuples(): + if pt[n] == pos_tuple[n]: + return pt + return None + + +HYPONYM = 0 +HYPERNYM = 1 +CLASS_REGIONAL = 2 +PART_HOLONYM = 3 +PART_MERONYM = 4 +ATTRIBUTE = 5 +SUBSTANCE_HOLONYM = 6 +SUBSTANCE_MERONYM = 7 +MEMBER_HOLONYM = 8 +MEMBER_MERONYM = 9 +VERB_GROUP = 10 +INSTANCE_HYPONYM = 12 +INSTANCE_HYPERNYM = 13 +CAUSE = 14 +ALSO_SEE = 15 +SIMILAR = 16 +ENTAILMENT = 17 +ANTONYM = 18 +FRAMES = 19 +PERTAINYM = 20 + +CLASS_CATEGORY = 21 +CLASS_USAGE = 22 +CLASS_REGIONAL = 23 +CLASS_USAGE = 24 +CLASS_CATEGORY = 11 + +DERIVATIONALLY_RELATED_FORM = 25 + +INDIRECT_HYPERNYMS = 26 + + +def lemma_property(word, synset, func): + def flattern(l): + if l == []: + return [] + else: + return l[0] + flattern(l[1:]) + + return flattern([func(l) for l in synset.lemmas() if l.name == word]) + + +def rebuild_tree(orig_tree): + node = orig_tree[0] + children = orig_tree[1:] + return (node, [rebuild_tree(t) for t in children]) + + +def get_relations_data(word, synset): + """ + Get synset relations data for a synset. Note that this doesn't + yet support things such as full hyponym vs direct hyponym. + """ + if synset.pos() == wn.NOUN: + return ( + (HYPONYM, "Hyponyms", synset.hyponyms()), + (INSTANCE_HYPONYM, "Instance hyponyms", synset.instance_hyponyms()), + (HYPERNYM, "Direct hypernyms", synset.hypernyms()), + ( + INDIRECT_HYPERNYMS, + "Indirect hypernyms", + rebuild_tree(synset.tree(lambda x: x.hypernyms()))[1], + ), + # hypernyms', 'Sister terms', + (INSTANCE_HYPERNYM, "Instance hypernyms", synset.instance_hypernyms()), + # (CLASS_REGIONAL, ['domain term region'], ), + (PART_HOLONYM, "Part holonyms", synset.part_holonyms()), + (PART_MERONYM, "Part meronyms", synset.part_meronyms()), + (SUBSTANCE_HOLONYM, "Substance holonyms", synset.substance_holonyms()), + (SUBSTANCE_MERONYM, "Substance meronyms", synset.substance_meronyms()), + (MEMBER_HOLONYM, "Member holonyms", synset.member_holonyms()), + (MEMBER_MERONYM, "Member meronyms", synset.member_meronyms()), + (ATTRIBUTE, "Attributes", synset.attributes()), + (ANTONYM, "Antonyms", lemma_property(word, synset, lambda l: l.antonyms())), + ( + DERIVATIONALLY_RELATED_FORM, + "Derivationally related form", + lemma_property( + word, synset, lambda l: l.derivationally_related_forms() + ), + ), + ) + elif synset.pos() == wn.VERB: + return ( + (ANTONYM, "Antonym", lemma_property(word, synset, lambda l: l.antonyms())), + (HYPONYM, "Hyponym", synset.hyponyms()), + (HYPERNYM, "Direct hypernyms", synset.hypernyms()), + ( + INDIRECT_HYPERNYMS, + "Indirect hypernyms", + rebuild_tree(synset.tree(lambda x: x.hypernyms()))[1], + ), + (ENTAILMENT, "Entailments", synset.entailments()), + (CAUSE, "Causes", synset.causes()), + (ALSO_SEE, "Also see", synset.also_sees()), + (VERB_GROUP, "Verb Groups", synset.verb_groups()), + ( + DERIVATIONALLY_RELATED_FORM, + "Derivationally related form", + lemma_property( + word, synset, lambda l: l.derivationally_related_forms() + ), + ), + ) + elif synset.pos() == wn.ADJ or synset.pos == wn.ADJ_SAT: + return ( + (ANTONYM, "Antonym", lemma_property(word, synset, lambda l: l.antonyms())), + (SIMILAR, "Similar to", synset.similar_tos()), + # Participle of verb - not supported by corpus + ( + PERTAINYM, + "Pertainyms", + lemma_property(word, synset, lambda l: l.pertainyms()), + ), + (ATTRIBUTE, "Attributes", synset.attributes()), + (ALSO_SEE, "Also see", synset.also_sees()), + ) + elif synset.pos() == wn.ADV: + # This is weird. adverbs such as 'quick' and 'fast' don't seem + # to have antonyms returned by the corpus.a + return ( + (ANTONYM, "Antonym", lemma_property(word, synset, lambda l: l.antonyms())), + ) + # Derived from adjective - not supported by corpus + else: + raise TypeError("Unhandles synset POS type: " + str(synset.pos())) + + +html_header = """ + + + + + +NLTK Wordnet Browser display of: %s + +""" +html_trailer = """ + + +""" + +explanation = """ +

    Search Help

    +
    • The display below the line is an example of the output the browser +shows you when you enter a search word. The search word was green.
    • +
    • The search result shows for different parts of speech the synsets +i.e. different meanings for the word.
    • +
    • All underlined texts are hypertext links. There are two types of links: +word links and others. Clicking a word link carries out a search for the word +in the Wordnet database.
    • +
    • Clicking a link of the other type opens a display section of data attached +to that link. Clicking that link a second time closes the section again.
    • +
    • Clicking S: opens a section showing the relations for that synset. +
    • +
    • Clicking on a relation name opens a section that displays the associated +synsets.
    • +
    • Type a search word in the Word field and start the search by the +Enter/Return key or click the Search button.
    • +
    +
    +""" + +# HTML oriented functions + + +def _bold(txt): + return "%s" % txt + + +def _center(txt): + return "
    %s
    " % txt + + +def _hlev(n, txt): + return "%s" % (n, txt, n) + + +def _italic(txt): + return "%s" % txt + + +def _li(txt): + return "
  • %s
  • " % txt + + +def pg(word, body): + """ + Return a HTML page of NLTK Browser format constructed from the + word and body + + :param word: The word that the body corresponds to + :type word: str + :param body: The HTML body corresponding to the word + :type body: str + :return: a HTML page for the word-body combination + :rtype: str + """ + return (html_header % word) + body + html_trailer + + +def _ul(txt): + return "
      " + txt + "
    " + + +def _abbc(txt): + """ + abbc = asterisks, breaks, bold, center + """ + return _center(_bold("
    " * 10 + "*" * 10 + " " + txt + " " + "*" * 10)) + + +full_hyponym_cont_text = _ul(_li(_italic("(has full hyponym continuation)"))) + "\n" + + +def _get_synset(synset_key): + """ + The synset key is the unique name of the synset, this can be + retrieved via synset.name() + """ + return wn.synset(synset_key) + + +def _collect_one_synset(word, synset, synset_relations): + """ + Returns the HTML string for one synset or word + + :param word: the current word + :type word: str + :param synset: a synset + :type synset: synset + :param synset_relations: information about which synset relations + to display. + :type synset_relations: dict(synset_key, set(relation_id)) + :return: The HTML string built for this synset + :rtype: str + """ + if isinstance(synset, tuple): # It's a word + raise NotImplementedError("word not supported by _collect_one_synset") + + typ = "S" + pos_tuple = _pos_match((synset.pos(), None, None)) + assert pos_tuple is not None, "pos_tuple is null: synset.pos(): %s" % synset.pos() + descr = pos_tuple[2] + ref = copy.deepcopy(Reference(word, synset_relations)) + ref.toggle_synset(synset) + synset_label = typ + ";" + if synset.name() in synset_relations: + synset_label = _bold(synset_label) + s = f"
  • {make_lookup_link(ref, synset_label)} ({descr}) " + + def format_lemma(w): + w = w.replace("_", " ") + if w.lower() == word: + return _bold(w) + else: + ref = Reference(w) + return make_lookup_link(ref, w) + + s += ", ".join(format_lemma(l.name()) for l in synset.lemmas()) + + gl = " ({}) {} ".format( + synset.definition(), + "; ".join('"%s"' % e for e in synset.examples()), + ) + return s + gl + _synset_relations(word, synset, synset_relations) + "
  • \n" + + +def _collect_all_synsets(word, pos, synset_relations=dict()): + """ + Return a HTML unordered list of synsets for the given word and + part of speech. + """ + return "
      %s\n
    \n" % "".join( + _collect_one_synset(word, synset, synset_relations) + for synset in wn.synsets(word, pos) + ) + + +def _synset_relations(word, synset, synset_relations): + """ + Builds the HTML string for the relations of a synset + + :param word: The current word + :type word: str + :param synset: The synset for which we're building the relations. + :type synset: Synset + :param synset_relations: synset keys and relation types for which to display relations. + :type synset_relations: dict(synset_key, set(relation_type)) + :return: The HTML for a synset's relations + :rtype: str + """ + + if not synset.name() in synset_relations: + return "" + ref = Reference(word, synset_relations) + + def relation_html(r): + if isinstance(r, Synset): + return make_lookup_link(Reference(r.lemma_names()[0]), r.lemma_names()[0]) + elif isinstance(r, Lemma): + return relation_html(r.synset()) + elif isinstance(r, tuple): + # It's probably a tuple containing a Synset and a list of + # similar tuples. This forms a tree of synsets. + return "{}\n
      {}
    \n".format( + relation_html(r[0]), + "".join("
  • %s
  • \n" % relation_html(sr) for sr in r[1]), + ) + else: + raise TypeError( + "r must be a synset, lemma or list, it was: type(r) = %s, r = %s" + % (type(r), r) + ) + + def make_synset_html(db_name, disp_name, rels): + synset_html = "%s\n" % make_lookup_link( + copy.deepcopy(ref).toggle_synset_relation(synset, db_name), + disp_name, + ) + + if db_name in ref.synset_relations[synset.name()]: + synset_html += "
      %s
    \n" % "".join( + "
  • %s
  • \n" % relation_html(r) for r in rels + ) + + return synset_html + + html = ( + "
      " + + "\n".join( + "
    • %s
    • " % make_synset_html(*rel_data) + for rel_data in get_relations_data(word, synset) + if rel_data[2] != [] + ) + + "
    " + ) + + return html + + +class RestrictedUnpickler(pickle.Unpickler): + """ + Unpickler that prevents any class or function from being used during loading. + """ + + def find_class(self, module, name): + # Forbid every function + raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden") + + +class Reference: + """ + A reference to a page that may be generated by page_word + """ + + def __init__(self, word, synset_relations=dict()): + """ + Build a reference to a new page. + + word is the word or words (separated by commas) for which to + search for synsets of + + synset_relations is a dictionary of synset keys to sets of + synset relation identifaiers to unfold a list of synset + relations for. + """ + self.word = word + self.synset_relations = synset_relations + + def encode(self): + """ + Encode this reference into a string to be used in a URL. + """ + # This uses a tuple rather than an object since the python + # pickle representation is much smaller and there is no need + # to represent the complete object. + string = pickle.dumps((self.word, self.synset_relations), -1) + return base64.urlsafe_b64encode(string).decode() + + @staticmethod + def decode(string): + """ + Decode a reference encoded with Reference.encode + """ + string = base64.urlsafe_b64decode(string.encode()) + word, synset_relations = RestrictedUnpickler(io.BytesIO(string)).load() + return Reference(word, synset_relations) + + def toggle_synset_relation(self, synset, relation): + """ + Toggle the display of the relations for the given synset and + relation type. + + This function will throw a KeyError if the synset is currently + not being displayed. + """ + if relation in self.synset_relations[synset.name()]: + self.synset_relations[synset.name()].remove(relation) + else: + self.synset_relations[synset.name()].add(relation) + + return self + + def toggle_synset(self, synset): + """ + Toggle displaying of the relation types for the given synset + """ + if synset.name() in self.synset_relations: + del self.synset_relations[synset.name()] + else: + self.synset_relations[synset.name()] = set() + + return self + + +def make_lookup_link(ref, label): + return f'{label}' + + +def page_from_word(word): + """ + Return a HTML page for the given word. + + :type word: str + :param word: The currently active word + :return: A tuple (page,word), where page is the new current HTML page + to be sent to the browser and + word is the new current word + :rtype: A tuple (str,str) + """ + return page_from_reference(Reference(word)) + + +def page_from_href(href): + """ + Returns a tuple of the HTML page built and the new current word + + :param href: The hypertext reference to be solved + :type href: str + :return: A tuple (page,word), where page is the new current HTML page + to be sent to the browser and + word is the new current word + :rtype: A tuple (str,str) + """ + return page_from_reference(Reference.decode(href)) + + +def page_from_reference(href): + """ + Returns a tuple of the HTML page built and the new current word + + :param href: The hypertext reference to be solved + :type href: str + :return: A tuple (page,word), where page is the new current HTML page + to be sent to the browser and + word is the new current word + :rtype: A tuple (str,str) + """ + word = href.word + pos_forms = defaultdict(list) + words = word.split(",") + words = [w for w in [w.strip().lower().replace(" ", "_") for w in words] if w != ""] + if len(words) == 0: + # No words were found. + return "", "Please specify a word to search for." + + # This looks up multiple words at once. This is probably not + # necessary and may lead to problems. + for w in words: + for pos in [wn.NOUN, wn.VERB, wn.ADJ, wn.ADV]: + form = wn.morphy(w, pos) + if form and form not in pos_forms[pos]: + pos_forms[pos].append(form) + body = "" + for pos, pos_str, name in _pos_tuples(): + if pos in pos_forms: + body += _hlev(3, name) + "\n" + for w in pos_forms[pos]: + # Not all words of exc files are in the database, skip + # to the next word if a KeyError is raised. + try: + body += _collect_all_synsets(w, pos, href.synset_relations) + except KeyError: + pass + if not body: + body = "The word or words '%s' were not found in the dictionary." % word + return body, word + + +##################################################################### +# Static pages +##################################################################### + + +def get_static_page_by_path(path): + """ + Return a static HTML page from the path given. + """ + if path == "index_2.html": + return get_static_index_page(False) + elif path == "index.html": + return get_static_index_page(True) + elif path == "NLTK Wordnet Browser Database Info.html": + return "Display of Wordnet Database Statistics is not supported" + elif path == "upper_2.html": + return get_static_upper_page(False) + elif path == "upper.html": + return get_static_upper_page(True) + elif path == "web_help.html": + return get_static_web_help_page() + elif path == "wx_help.html": + return get_static_wx_help_page() + raise FileNotFoundError() + + +def get_static_web_help_page(): + """ + Return the static web help page. + """ + return """ + + + + + + NLTK Wordnet Browser display of: * Help * + + +

    NLTK Wordnet Browser Help

    +

    The NLTK Wordnet Browser is a tool to use in browsing the Wordnet database. It tries to behave like the Wordnet project's web browser but the difference is that the NLTK Wordnet Browser uses a local Wordnet database. +

    You are using the Javascript client part of the NLTK Wordnet BrowseServer. We assume your browser is in tab sheets enabled mode.

    +

    For background information on Wordnet, see the Wordnet project home page: https://wordnet.princeton.edu/. For more information on the NLTK project, see the project home: +https://www.nltk.org/. To get an idea of what the Wordnet version used by this browser includes choose Show Database Info from the View submenu.

    +

    Word search

    +

    The word to be searched is typed into the New Word field and the search started with Enter or by clicking the Search button. There is no uppercase/lowercase distinction: the search word is transformed to lowercase before the search.

    +

    In addition, the word does not have to be in base form. The browser tries to find the possible base form(s) by making certain morphological substitutions. Typing fLIeS as an obscure example gives one this. Click the previous link to see what this kind of search looks like and then come back to this page by using the Alt+LeftArrow key combination.

    +

    The result of a search is a display of one or more +synsets for every part of speech in which a form of the +search word was found to occur. A synset is a set of words +having the same sense or meaning. Each word in a synset that is +underlined is a hyperlink which can be clicked to trigger an +automatic search for that word.

    +

    Every synset has a hyperlink S: at the start of its +display line. Clicking that symbol shows you the name of every +relation that this synset is part of. Every relation name is a hyperlink that opens up a display for that relation. Clicking it another time closes the display again. Clicking another relation name on a line that has an opened relation closes the open relation and opens the clicked relation.

    +

    It is also possible to give two or more words or collocations to be searched at the same time separating them with a comma like this cheer up,clear up, for example. Click the previous link to see what this kind of search looks like and then come back to this page by using the Alt+LeftArrow key combination. As you could see the search result includes the synsets found in the same order than the forms were given in the search field.

    +

    +There are also word level (lexical) relations recorded in the Wordnet database. Opening this kind of relation displays lines with a hyperlink W: at their beginning. Clicking this link shows more info on the word in question.

    +

    The Buttons

    +

    The Search and Help buttons need no more explanation.

    +

    The Show Database Info button shows a collection of Wordnet database statistics.

    +

    The Shutdown the Server button is shown for the first client of the BrowServer program i.e. for the client that is automatically launched when the BrowServer is started but not for the succeeding clients in order to protect the server from accidental shutdowns. +

    + +""" + + +def get_static_welcome_message(): + """ + Get the static welcome page. + """ + return """ +

    Search Help

    +
    • The display below the line is an example of the output the browser +shows you when you enter a search word. The search word was green.
    • +
    • The search result shows for different parts of speech the synsets +i.e. different meanings for the word.
    • +
    • All underlined texts are hypertext links. There are two types of links: +word links and others. Clicking a word link carries out a search for the word +in the Wordnet database.
    • +
    • Clicking a link of the other type opens a display section of data attached +to that link. Clicking that link a second time closes the section again.
    • +
    • Clicking S: opens a section showing the relations for that synset.
    • +
    • Clicking on a relation name opens a section that displays the associated +synsets.
    • +
    • Type a search word in the Next Word field and start the search by the +Enter/Return key or click the Search button.
    • +
    +""" + + +def get_static_index_page(with_shutdown): + """ + Get the static index page. + """ + template = """ + + + + + NLTK Wordnet Browser + + + + + + + +""" + if with_shutdown: + upper_link = "upper.html" + else: + upper_link = "upper_2.html" + + return template % upper_link + + +def get_static_upper_page(with_shutdown): + """ + Return the upper frame page, + + If with_shutdown is True then a 'shutdown' button is also provided + to shutdown the server. + """ + template = """ + + + + + + Untitled Document + + + + Current Word:  + Next Word:  + + + Help + %s + + + +""" + if with_shutdown: + shutdown_link = 'Shutdown' + else: + shutdown_link = "" + + return template % shutdown_link + + +def usage(): + """ + Display the command line help message. + """ + print(__doc__) + + +def app(): + # Parse and interpret options. + (opts, _) = getopt.getopt( + argv[1:], "l:p:sh", ["logfile=", "port=", "server-mode", "help"] + ) + port = 8000 + server_mode = False + help_mode = False + logfilename = None + for (opt, value) in opts: + if (opt == "-l") or (opt == "--logfile"): + logfilename = str(value) + elif (opt == "-p") or (opt == "--port"): + port = int(value) + elif (opt == "-s") or (opt == "--server-mode"): + server_mode = True + elif (opt == "-h") or (opt == "--help"): + help_mode = True + + if help_mode: + usage() + else: + wnb(port, not server_mode, logfilename) + + +if __name__ == "__main__": + app() + +__all__ = ["app"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/book.py b/.pythonlibs/lib/python3.10/site-packages/nltk/book.py new file mode 100644 index 0000000..dad9f5b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/book.py @@ -0,0 +1,213 @@ +# Natural Language Toolkit: Some texts for exploration in chapter 1 of the book +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# +# URL: +# For license information, see LICENSE.TXT + +from nltk.corpus import ( + genesis, + gutenberg, + inaugural, + nps_chat, + treebank, + webtext, + wordnet, +) +from nltk.probability import FreqDist +from nltk.text import Text +from nltk.util import bigrams + +print("*** Introductory Examples for the NLTK Book ***") +print("Loading text1, ..., text9 and sent1, ..., sent9") +print("Type the name of the text or sentence to view it.") +print("Type: 'texts()' or 'sents()' to list the materials.") + +text1 = Text(gutenberg.words("melville-moby_dick.txt")) +print("text1:", text1.name) + +text2 = Text(gutenberg.words("austen-sense.txt")) +print("text2:", text2.name) + +text3 = Text(genesis.words("english-kjv.txt"), name="The Book of Genesis") +print("text3:", text3.name) + +text4 = Text(inaugural.words(), name="Inaugural Address Corpus") +print("text4:", text4.name) + +text5 = Text(nps_chat.words(), name="Chat Corpus") +print("text5:", text5.name) + +text6 = Text(webtext.words("grail.txt"), name="Monty Python and the Holy Grail") +print("text6:", text6.name) + +text7 = Text(treebank.words(), name="Wall Street Journal") +print("text7:", text7.name) + +text8 = Text(webtext.words("singles.txt"), name="Personals Corpus") +print("text8:", text8.name) + +text9 = Text(gutenberg.words("chesterton-thursday.txt")) +print("text9:", text9.name) + + +def texts(): + print("text1:", text1.name) + print("text2:", text2.name) + print("text3:", text3.name) + print("text4:", text4.name) + print("text5:", text5.name) + print("text6:", text6.name) + print("text7:", text7.name) + print("text8:", text8.name) + print("text9:", text9.name) + + +sent1 = ["Call", "me", "Ishmael", "."] +sent2 = [ + "The", + "family", + "of", + "Dashwood", + "had", + "long", + "been", + "settled", + "in", + "Sussex", + ".", +] +sent3 = [ + "In", + "the", + "beginning", + "God", + "created", + "the", + "heaven", + "and", + "the", + "earth", + ".", +] +sent4 = [ + "Fellow", + "-", + "Citizens", + "of", + "the", + "Senate", + "and", + "of", + "the", + "House", + "of", + "Representatives", + ":", +] +sent5 = [ + "I", + "have", + "a", + "problem", + "with", + "people", + "PMing", + "me", + "to", + "lol", + "JOIN", +] +sent6 = [ + "SCENE", + "1", + ":", + "[", + "wind", + "]", + "[", + "clop", + "clop", + "clop", + "]", + "KING", + "ARTHUR", + ":", + "Whoa", + "there", + "!", +] +sent7 = [ + "Pierre", + "Vinken", + ",", + "61", + "years", + "old", + ",", + "will", + "join", + "the", + "board", + "as", + "a", + "nonexecutive", + "director", + "Nov.", + "29", + ".", +] +sent8 = [ + "25", + "SEXY", + "MALE", + ",", + "seeks", + "attrac", + "older", + "single", + "lady", + ",", + "for", + "discreet", + "encounters", + ".", +] +sent9 = [ + "THE", + "suburb", + "of", + "Saffron", + "Park", + "lay", + "on", + "the", + "sunset", + "side", + "of", + "London", + ",", + "as", + "red", + "and", + "ragged", + "as", + "a", + "cloud", + "of", + "sunset", + ".", +] + + +def sents(): + print("sent1:", " ".join(sent1)) + print("sent2:", " ".join(sent2)) + print("sent3:", " ".join(sent3)) + print("sent4:", " ".join(sent4)) + print("sent5:", " ".join(sent5)) + print("sent6:", " ".join(sent6)) + print("sent7:", " ".join(sent7)) + print("sent8:", " ".join(sent8)) + print("sent9:", " ".join(sent9)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__init__.py new file mode 100644 index 0000000..11877a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__init__.py @@ -0,0 +1,34 @@ +# Natural Language Toolkit: Combinatory Categorial Grammar +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Graeme Gange +# URL: +# For license information, see LICENSE.TXT + +""" +Combinatory Categorial Grammar. + +For more information see nltk/doc/contrib/ccg/ccg.pdf +""" + +from nltk.ccg.chart import CCGChart, CCGChartParser, CCGEdge, CCGLeafEdge +from nltk.ccg.combinator import ( + BackwardApplication, + BackwardBx, + BackwardCombinator, + BackwardComposition, + BackwardSx, + BackwardT, + DirectedBinaryCombinator, + ForwardApplication, + ForwardCombinator, + ForwardComposition, + ForwardSubstitution, + ForwardT, + UndirectedBinaryCombinator, + UndirectedComposition, + UndirectedFunctionApplication, + UndirectedSubstitution, + UndirectedTypeRaise, +) +from nltk.ccg.lexicon import CCGLexicon diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5a33bf0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..25baed2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/chart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/chart.cpython-310.pyc new file mode 100644 index 0000000..dcc2302 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/chart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/combinator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/combinator.cpython-310.pyc new file mode 100644 index 0000000..4c38eb3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/combinator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/lexicon.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/lexicon.cpython-310.pyc new file mode 100644 index 0000000..4455a40 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/lexicon.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/logic.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/logic.cpython-310.pyc new file mode 100644 index 0000000..403488e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/__pycache__/logic.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/api.py new file mode 100644 index 0000000..b00475f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/api.py @@ -0,0 +1,358 @@ +# Natural Language Toolkit: CCG Categories +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Graeme Gange +# URL: +# For license information, see LICENSE.TXT + +from abc import ABCMeta, abstractmethod +from functools import total_ordering + +from nltk.internals import raise_unorderable_types + + +@total_ordering +class AbstractCCGCategory(metaclass=ABCMeta): + """ + Interface for categories in combinatory grammars. + """ + + @abstractmethod + def is_primitive(self): + """ + Returns true if the category is primitive. + """ + + @abstractmethod + def is_function(self): + """ + Returns true if the category is a function application. + """ + + @abstractmethod + def is_var(self): + """ + Returns true if the category is a variable. + """ + + @abstractmethod + def substitute(self, substitutions): + """ + Takes a set of (var, category) substitutions, and replaces every + occurrence of the variable with the corresponding category. + """ + + @abstractmethod + def can_unify(self, other): + """ + Determines whether two categories can be unified. + - Returns None if they cannot be unified + - Returns a list of necessary substitutions if they can. + """ + + # Utility functions: comparison, strings and hashing. + @abstractmethod + def __str__(self): + pass + + def __eq__(self, other): + return ( + self.__class__ is other.__class__ + and self._comparison_key == other._comparison_key + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, AbstractCCGCategory): + raise_unorderable_types("<", self, other) + if self.__class__ is other.__class__: + return self._comparison_key < other._comparison_key + else: + return self.__class__.__name__ < other.__class__.__name__ + + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self._comparison_key) + return self._hash + + +class CCGVar(AbstractCCGCategory): + """ + Class representing a variable CCG category. + Used for conjunctions (and possibly type-raising, if implemented as a + unary rule). + """ + + _maxID = 0 + + def __init__(self, prim_only=False): + """Initialize a variable (selects a new identifier) + + :param prim_only: a boolean that determines whether the variable is + restricted to primitives + :type prim_only: bool + """ + self._id = self.new_id() + self._prim_only = prim_only + self._comparison_key = self._id + + @classmethod + def new_id(cls): + """ + A class method allowing generation of unique variable identifiers. + """ + cls._maxID = cls._maxID + 1 + return cls._maxID - 1 + + @classmethod + def reset_id(cls): + cls._maxID = 0 + + def is_primitive(self): + return False + + def is_function(self): + return False + + def is_var(self): + return True + + def substitute(self, substitutions): + """If there is a substitution corresponding to this variable, + return the substituted category. + """ + for (var, cat) in substitutions: + if var == self: + return cat + return self + + def can_unify(self, other): + """If the variable can be replaced with other + a substitution is returned. + """ + if other.is_primitive() or not self._prim_only: + return [(self, other)] + return None + + def id(self): + return self._id + + def __str__(self): + return "_var" + str(self._id) + + +@total_ordering +class Direction: + """ + Class representing the direction of a function application. + Also contains maintains information as to which combinators + may be used with the category. + """ + + def __init__(self, dir, restrictions): + self._dir = dir + self._restrs = restrictions + self._comparison_key = (dir, tuple(restrictions)) + + # Testing the application direction + def is_forward(self): + return self._dir == "/" + + def is_backward(self): + return self._dir == "\\" + + def dir(self): + return self._dir + + def restrs(self): + """A list of restrictions on the combinators. + '.' denotes that permuting operations are disallowed + ',' denotes that function composition is disallowed + '_' denotes that the direction has variable restrictions. + (This is redundant in the current implementation of type-raising) + """ + return self._restrs + + def is_variable(self): + return self._restrs == "_" + + # Unification and substitution of variable directions. + # Used only if type-raising is implemented as a unary rule, as it + # must inherit restrictions from the argument category. + def can_unify(self, other): + if other.is_variable(): + return [("_", self.restrs())] + elif self.is_variable(): + return [("_", other.restrs())] + else: + if self.restrs() == other.restrs(): + return [] + return None + + def substitute(self, subs): + if not self.is_variable(): + return self + + for (var, restrs) in subs: + if var == "_": + return Direction(self._dir, restrs) + return self + + # Testing permitted combinators + def can_compose(self): + return "," not in self._restrs + + def can_cross(self): + return "." not in self._restrs + + def __eq__(self, other): + return ( + self.__class__ is other.__class__ + and self._comparison_key == other._comparison_key + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, Direction): + raise_unorderable_types("<", self, other) + if self.__class__ is other.__class__: + return self._comparison_key < other._comparison_key + else: + return self.__class__.__name__ < other.__class__.__name__ + + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self._comparison_key) + return self._hash + + def __str__(self): + r_str = "" + for r in self._restrs: + r_str = r_str + "%s" % r + return f"{self._dir}{r_str}" + + # The negation operator reverses the direction of the application + def __neg__(self): + if self._dir == "/": + return Direction("\\", self._restrs) + else: + return Direction("/", self._restrs) + + +class PrimitiveCategory(AbstractCCGCategory): + """ + Class representing primitive categories. + Takes a string representation of the category, and a + list of strings specifying the morphological subcategories. + """ + + def __init__(self, categ, restrictions=[]): + self._categ = categ + self._restrs = restrictions + self._comparison_key = (categ, tuple(restrictions)) + + def is_primitive(self): + return True + + def is_function(self): + return False + + def is_var(self): + return False + + def restrs(self): + return self._restrs + + def categ(self): + return self._categ + + # Substitution does nothing to a primitive category + def substitute(self, subs): + return self + + # A primitive can be unified with a class of the same + # base category, given that the other category shares all + # of its subclasses, or with a variable. + def can_unify(self, other): + if not other.is_primitive(): + return None + if other.is_var(): + return [(other, self)] + if other.categ() == self.categ(): + for restr in self._restrs: + if restr not in other.restrs(): + return None + return [] + return None + + def __str__(self): + if self._restrs == []: + return "%s" % self._categ + restrictions = "[%s]" % ",".join(repr(r) for r in self._restrs) + return f"{self._categ}{restrictions}" + + +class FunctionalCategory(AbstractCCGCategory): + """ + Class that represents a function application category. + Consists of argument and result categories, together with + an application direction. + """ + + def __init__(self, res, arg, dir): + self._res = res + self._arg = arg + self._dir = dir + self._comparison_key = (arg, dir, res) + + def is_primitive(self): + return False + + def is_function(self): + return True + + def is_var(self): + return False + + # Substitution returns the category consisting of the + # substitution applied to each of its constituents. + def substitute(self, subs): + sub_res = self._res.substitute(subs) + sub_dir = self._dir.substitute(subs) + sub_arg = self._arg.substitute(subs) + return FunctionalCategory(sub_res, sub_arg, self._dir) + + # A function can unify with another function, so long as its + # constituents can unify, or with an unrestricted variable. + def can_unify(self, other): + if other.is_var(): + return [(other, self)] + if other.is_function(): + sa = self._res.can_unify(other.res()) + sd = self._dir.can_unify(other.dir()) + if sa is not None and sd is not None: + sb = self._arg.substitute(sa).can_unify(other.arg().substitute(sa)) + if sb is not None: + return sa + sb + return None + + # Constituent accessors + def arg(self): + return self._arg + + def res(self): + return self._res + + def dir(self): + return self._dir + + def __str__(self): + return f"({self._res}{self._dir}{self._arg})" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/chart.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/chart.py new file mode 100644 index 0000000..4d4fcb9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/chart.py @@ -0,0 +1,480 @@ +# Natural Language Toolkit: Combinatory Categorial Grammar +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Graeme Gange +# URL: +# For license information, see LICENSE.TXT + +""" +The lexicon is constructed by calling +``lexicon.fromstring()``. + +In order to construct a parser, you also need a rule set. +The standard English rules are provided in chart as +``chart.DefaultRuleSet``. + +The parser can then be constructed by calling, for example: +``parser = chart.CCGChartParser(, )`` + +Parsing is then performed by running +``parser.parse(.split())``. + +While this returns a list of trees, the default representation +of the produced trees is not very enlightening, particularly +given that it uses the same tree class as the CFG parsers. +It is probably better to call: +``chart.printCCGDerivation()`` +which should print a nice representation of the derivation. + +This entire process is shown far more clearly in the demonstration: +python chart.py +""" + +import itertools + +from nltk.ccg.combinator import * +from nltk.ccg.combinator import ( + BackwardApplication, + BackwardBx, + BackwardComposition, + BackwardSx, + BackwardT, + ForwardApplication, + ForwardComposition, + ForwardSubstitution, + ForwardT, +) +from nltk.ccg.lexicon import Token, fromstring +from nltk.ccg.logic import * +from nltk.parse import ParserI +from nltk.parse.chart import AbstractChartRule, Chart, EdgeI +from nltk.sem.logic import * +from nltk.tree import Tree + + +# Based on the EdgeI class from NLTK. +# A number of the properties of the EdgeI interface don't +# transfer well to CCGs, however. +class CCGEdge(EdgeI): + def __init__(self, span, categ, rule): + self._span = span + self._categ = categ + self._rule = rule + self._comparison_key = (span, categ, rule) + + # Accessors + def lhs(self): + return self._categ + + def span(self): + return self._span + + def start(self): + return self._span[0] + + def end(self): + return self._span[1] + + def length(self): + return self._span[1] - self.span[0] + + def rhs(self): + return () + + def dot(self): + return 0 + + def is_complete(self): + return True + + def is_incomplete(self): + return False + + def nextsym(self): + return None + + def categ(self): + return self._categ + + def rule(self): + return self._rule + + +class CCGLeafEdge(EdgeI): + """ + Class representing leaf edges in a CCG derivation. + """ + + def __init__(self, pos, token, leaf): + self._pos = pos + self._token = token + self._leaf = leaf + self._comparison_key = (pos, token.categ(), leaf) + + # Accessors + def lhs(self): + return self._token.categ() + + def span(self): + return (self._pos, self._pos + 1) + + def start(self): + return self._pos + + def end(self): + return self._pos + 1 + + def length(self): + return 1 + + def rhs(self): + return self._leaf + + def dot(self): + return 0 + + def is_complete(self): + return True + + def is_incomplete(self): + return False + + def nextsym(self): + return None + + def token(self): + return self._token + + def categ(self): + return self._token.categ() + + def leaf(self): + return self._leaf + + +class BinaryCombinatorRule(AbstractChartRule): + """ + Class implementing application of a binary combinator to a chart. + Takes the directed combinator to apply. + """ + + NUMEDGES = 2 + + def __init__(self, combinator): + self._combinator = combinator + + # Apply a combinator + def apply(self, chart, grammar, left_edge, right_edge): + # The left & right edges must be touching. + if not (left_edge.end() == right_edge.start()): + return + + # Check if the two edges are permitted to combine. + # If so, generate the corresponding edge. + if self._combinator.can_combine(left_edge.categ(), right_edge.categ()): + for res in self._combinator.combine(left_edge.categ(), right_edge.categ()): + new_edge = CCGEdge( + span=(left_edge.start(), right_edge.end()), + categ=res, + rule=self._combinator, + ) + if chart.insert(new_edge, (left_edge, right_edge)): + yield new_edge + + # The representation of the combinator (for printing derivations) + def __str__(self): + return "%s" % self._combinator + + +# Type-raising must be handled slightly differently to the other rules, as the +# resulting rules only span a single edge, rather than both edges. + + +class ForwardTypeRaiseRule(AbstractChartRule): + """ + Class for applying forward type raising + """ + + NUMEDGES = 2 + + def __init__(self): + self._combinator = ForwardT + + def apply(self, chart, grammar, left_edge, right_edge): + if not (left_edge.end() == right_edge.start()): + return + + for res in self._combinator.combine(left_edge.categ(), right_edge.categ()): + new_edge = CCGEdge(span=left_edge.span(), categ=res, rule=self._combinator) + if chart.insert(new_edge, (left_edge,)): + yield new_edge + + def __str__(self): + return "%s" % self._combinator + + +class BackwardTypeRaiseRule(AbstractChartRule): + """ + Class for applying backward type raising. + """ + + NUMEDGES = 2 + + def __init__(self): + self._combinator = BackwardT + + def apply(self, chart, grammar, left_edge, right_edge): + if not (left_edge.end() == right_edge.start()): + return + + for res in self._combinator.combine(left_edge.categ(), right_edge.categ()): + new_edge = CCGEdge(span=right_edge.span(), categ=res, rule=self._combinator) + if chart.insert(new_edge, (right_edge,)): + yield new_edge + + def __str__(self): + return "%s" % self._combinator + + +# Common sets of combinators used for English derivations. +ApplicationRuleSet = [ + BinaryCombinatorRule(ForwardApplication), + BinaryCombinatorRule(BackwardApplication), +] +CompositionRuleSet = [ + BinaryCombinatorRule(ForwardComposition), + BinaryCombinatorRule(BackwardComposition), + BinaryCombinatorRule(BackwardBx), +] +SubstitutionRuleSet = [ + BinaryCombinatorRule(ForwardSubstitution), + BinaryCombinatorRule(BackwardSx), +] +TypeRaiseRuleSet = [ForwardTypeRaiseRule(), BackwardTypeRaiseRule()] + +# The standard English rule set. +DefaultRuleSet = ( + ApplicationRuleSet + CompositionRuleSet + SubstitutionRuleSet + TypeRaiseRuleSet +) + + +class CCGChartParser(ParserI): + """ + Chart parser for CCGs. + Based largely on the ChartParser class from NLTK. + """ + + def __init__(self, lexicon, rules, trace=0): + self._lexicon = lexicon + self._rules = rules + self._trace = trace + + def lexicon(self): + return self._lexicon + + # Implements the CYK algorithm + def parse(self, tokens): + tokens = list(tokens) + chart = CCGChart(list(tokens)) + lex = self._lexicon + + # Initialize leaf edges. + for index in range(chart.num_leaves()): + for token in lex.categories(chart.leaf(index)): + new_edge = CCGLeafEdge(index, token, chart.leaf(index)) + chart.insert(new_edge, ()) + + # Select a span for the new edges + for span in range(2, chart.num_leaves() + 1): + for start in range(0, chart.num_leaves() - span + 1): + # Try all possible pairs of edges that could generate + # an edge for that span + for part in range(1, span): + lstart = start + mid = start + part + rend = start + span + + for left in chart.select(span=(lstart, mid)): + for right in chart.select(span=(mid, rend)): + # Generate all possible combinations of the two edges + for rule in self._rules: + edges_added_by_rule = 0 + for newedge in rule.apply(chart, lex, left, right): + edges_added_by_rule += 1 + + # Output the resulting parses + return chart.parses(lex.start()) + + +class CCGChart(Chart): + def __init__(self, tokens): + Chart.__init__(self, tokens) + + # Constructs the trees for a given parse. Unfortnunately, the parse trees need to be + # constructed slightly differently to those in the default Chart class, so it has to + # be reimplemented + def _trees(self, edge, complete, memo, tree_class): + assert complete, "CCGChart cannot build incomplete trees" + + if edge in memo: + return memo[edge] + + if isinstance(edge, CCGLeafEdge): + word = tree_class(edge.token(), [self._tokens[edge.start()]]) + leaf = tree_class((edge.token(), "Leaf"), [word]) + memo[edge] = [leaf] + return [leaf] + + memo[edge] = [] + trees = [] + + for cpl in self.child_pointer_lists(edge): + child_choices = [self._trees(cp, complete, memo, tree_class) for cp in cpl] + for children in itertools.product(*child_choices): + lhs = ( + Token( + self._tokens[edge.start() : edge.end()], + edge.lhs(), + compute_semantics(children, edge), + ), + str(edge.rule()), + ) + trees.append(tree_class(lhs, children)) + + memo[edge] = trees + return trees + + +def compute_semantics(children, edge): + if children[0].label()[0].semantics() is None: + return None + + if len(children) == 2: + if isinstance(edge.rule(), BackwardCombinator): + children = [children[1], children[0]] + + combinator = edge.rule()._combinator + function = children[0].label()[0].semantics() + argument = children[1].label()[0].semantics() + + if isinstance(combinator, UndirectedFunctionApplication): + return compute_function_semantics(function, argument) + elif isinstance(combinator, UndirectedComposition): + return compute_composition_semantics(function, argument) + elif isinstance(combinator, UndirectedSubstitution): + return compute_substitution_semantics(function, argument) + else: + raise AssertionError("Unsupported combinator '" + combinator + "'") + else: + return compute_type_raised_semantics(children[0].label()[0].semantics()) + + +# -------- +# Displaying derivations +# -------- +def printCCGDerivation(tree): + # Get the leaves and initial categories + leafcats = tree.pos() + leafstr = "" + catstr = "" + + # Construct a string with both the leaf word and corresponding + # category aligned. + for (leaf, cat) in leafcats: + str_cat = "%s" % cat + nextlen = 2 + max(len(leaf), len(str_cat)) + lcatlen = (nextlen - len(str_cat)) // 2 + rcatlen = lcatlen + (nextlen - len(str_cat)) % 2 + catstr += " " * lcatlen + str_cat + " " * rcatlen + lleaflen = (nextlen - len(leaf)) // 2 + rleaflen = lleaflen + (nextlen - len(leaf)) % 2 + leafstr += " " * lleaflen + leaf + " " * rleaflen + print(leafstr.rstrip()) + print(catstr.rstrip()) + + # Display the derivation steps + printCCGTree(0, tree) + + +# Prints the sequence of derivation steps. +def printCCGTree(lwidth, tree): + rwidth = lwidth + + # Is a leaf (word). + # Increment the span by the space occupied by the leaf. + if not isinstance(tree, Tree): + return 2 + lwidth + len(tree) + + # Find the width of the current derivation step + for child in tree: + rwidth = max(rwidth, printCCGTree(rwidth, child)) + + # Is a leaf node. + # Don't print anything, but account for the space occupied. + if not isinstance(tree.label(), tuple): + return max( + rwidth, 2 + lwidth + len("%s" % tree.label()), 2 + lwidth + len(tree[0]) + ) + + (token, op) = tree.label() + + if op == "Leaf": + return rwidth + + # Pad to the left with spaces, followed by a sequence of '-' + # and the derivation rule. + print(lwidth * " " + (rwidth - lwidth) * "-" + "%s" % op) + # Print the resulting category on a new line. + str_res = "%s" % (token.categ()) + if token.semantics() is not None: + str_res += " {" + str(token.semantics()) + "}" + respadlen = (rwidth - lwidth - len(str_res)) // 2 + lwidth + print(respadlen * " " + str_res) + return rwidth + + +### Demonstration code + +# Construct the lexicon +lex = fromstring( + """ + :- S, NP, N, VP # Primitive categories, S is the target primitive + + Det :: NP/N # Family of words + Pro :: NP + TV :: VP/NP + Modal :: (S\\NP)/VP # Backslashes need to be escaped + + I => Pro # Word -> Category mapping + you => Pro + + the => Det + + # Variables have the special keyword 'var' + # '.' prevents permutation + # ',' prevents composition + and => var\\.,var/.,var + + which => (N\\N)/(S/NP) + + will => Modal # Categories can be either explicit, or families. + might => Modal + + cook => TV + eat => TV + + mushrooms => N + parsnips => N + bacon => N + """ +) + + +def demo(): + parser = CCGChartParser(lex, DefaultRuleSet) + for parse in parser.parse("I might cook and eat the bacon".split()): + printCCGDerivation(parse) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/combinator.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/combinator.py new file mode 100644 index 0000000..d5a6eda --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/combinator.py @@ -0,0 +1,339 @@ +# Natural Language Toolkit: Combinatory Categorial Grammar +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Graeme Gange +# URL: +# For license information, see LICENSE.TXT +""" +CCG Combinators +""" + +from abc import ABCMeta, abstractmethod + +from nltk.ccg.api import FunctionalCategory + + +class UndirectedBinaryCombinator(metaclass=ABCMeta): + """ + Abstract class for representing a binary combinator. + Merely defines functions for checking if the function and argument + are able to be combined, and what the resulting category is. + + Note that as no assumptions are made as to direction, the unrestricted + combinators can perform all backward, forward and crossed variations + of the combinators; these restrictions must be added in the rule + class. + """ + + @abstractmethod + def can_combine(self, function, argument): + pass + + @abstractmethod + def combine(self, function, argument): + pass + + +class DirectedBinaryCombinator(metaclass=ABCMeta): + """ + Wrapper for the undirected binary combinator. + It takes left and right categories, and decides which is to be + the function, and which the argument. + It then decides whether or not they can be combined. + """ + + @abstractmethod + def can_combine(self, left, right): + pass + + @abstractmethod + def combine(self, left, right): + pass + + +class ForwardCombinator(DirectedBinaryCombinator): + """ + Class representing combinators where the primary functor is on the left. + + Takes an undirected combinator, and a predicate which adds constraints + restricting the cases in which it may apply. + """ + + def __init__(self, combinator, predicate, suffix=""): + self._combinator = combinator + self._predicate = predicate + self._suffix = suffix + + def can_combine(self, left, right): + return self._combinator.can_combine(left, right) and self._predicate( + left, right + ) + + def combine(self, left, right): + yield from self._combinator.combine(left, right) + + def __str__(self): + return f">{self._combinator}{self._suffix}" + + +class BackwardCombinator(DirectedBinaryCombinator): + """ + The backward equivalent of the ForwardCombinator class. + """ + + def __init__(self, combinator, predicate, suffix=""): + self._combinator = combinator + self._predicate = predicate + self._suffix = suffix + + def can_combine(self, left, right): + return self._combinator.can_combine(right, left) and self._predicate( + left, right + ) + + def combine(self, left, right): + yield from self._combinator.combine(right, left) + + def __str__(self): + return f"<{self._combinator}{self._suffix}" + + +class UndirectedFunctionApplication(UndirectedBinaryCombinator): + """ + Class representing function application. + Implements rules of the form: + X/Y Y -> X (>) + And the corresponding backwards application rule + """ + + def can_combine(self, function, argument): + if not function.is_function(): + return False + + return not function.arg().can_unify(argument) is None + + def combine(self, function, argument): + if not function.is_function(): + return + + subs = function.arg().can_unify(argument) + if subs is None: + return + + yield function.res().substitute(subs) + + def __str__(self): + return "" + + +# Predicates for function application. + +# Ensures the left functor takes an argument on the right +def forwardOnly(left, right): + return left.dir().is_forward() + + +# Ensures the right functor takes an argument on the left +def backwardOnly(left, right): + return right.dir().is_backward() + + +# Application combinator instances +ForwardApplication = ForwardCombinator(UndirectedFunctionApplication(), forwardOnly) +BackwardApplication = BackwardCombinator(UndirectedFunctionApplication(), backwardOnly) + + +class UndirectedComposition(UndirectedBinaryCombinator): + """ + Functional composition (harmonic) combinator. + Implements rules of the form + X/Y Y/Z -> X/Z (B>) + And the corresponding backwards and crossed variations. + """ + + def can_combine(self, function, argument): + # Can only combine two functions, and both functions must + # allow composition. + if not (function.is_function() and argument.is_function()): + return False + if function.dir().can_compose() and argument.dir().can_compose(): + return not function.arg().can_unify(argument.res()) is None + return False + + def combine(self, function, argument): + if not (function.is_function() and argument.is_function()): + return + if function.dir().can_compose() and argument.dir().can_compose(): + subs = function.arg().can_unify(argument.res()) + if subs is not None: + yield FunctionalCategory( + function.res().substitute(subs), + argument.arg().substitute(subs), + argument.dir(), + ) + + def __str__(self): + return "B" + + +# Predicates for restricting application of straight composition. +def bothForward(left, right): + return left.dir().is_forward() and right.dir().is_forward() + + +def bothBackward(left, right): + return left.dir().is_backward() and right.dir().is_backward() + + +# Predicates for crossed composition +def crossedDirs(left, right): + return left.dir().is_forward() and right.dir().is_backward() + + +def backwardBxConstraint(left, right): + # The functors must be crossed inwards + if not crossedDirs(left, right): + return False + # Permuting combinators must be allowed + if not left.dir().can_cross() and right.dir().can_cross(): + return False + # The resulting argument category is restricted to be primitive + return left.arg().is_primitive() + + +# Straight composition combinators +ForwardComposition = ForwardCombinator(UndirectedComposition(), forwardOnly) +BackwardComposition = BackwardCombinator(UndirectedComposition(), backwardOnly) + +# Backward crossed composition +BackwardBx = BackwardCombinator( + UndirectedComposition(), backwardBxConstraint, suffix="x" +) + + +class UndirectedSubstitution(UndirectedBinaryCombinator): + r""" + Substitution (permutation) combinator. + Implements rules of the form + Y/Z (X\Y)/Z -> X/Z ( N\N +def innermostFunction(categ): + while categ.res().is_function(): + categ = categ.res() + return categ + + +class UndirectedTypeRaise(UndirectedBinaryCombinator): + """ + Undirected combinator for type raising. + """ + + def can_combine(self, function, arg): + # The argument must be a function. + # The restriction that arg.res() must be a function + # merely reduces redundant type-raising; if arg.res() is + # primitive, we have: + # X Y\X =>((>) Y + # which is equivalent to + # X Y\X =>(<) Y + if not (arg.is_function() and arg.res().is_function()): + return False + + arg = innermostFunction(arg) + + # left, arg_categ are undefined! + subs = left.can_unify(arg_categ.arg()) + if subs is not None: + return True + return False + + def combine(self, function, arg): + if not ( + function.is_primitive() and arg.is_function() and arg.res().is_function() + ): + return + + # Type-raising matches only the innermost application. + arg = innermostFunction(arg) + + subs = function.can_unify(arg.arg()) + if subs is not None: + xcat = arg.res().substitute(subs) + yield FunctionalCategory( + xcat, FunctionalCategory(xcat, function, arg.dir()), -(arg.dir()) + ) + + def __str__(self): + return "T" + + +# Predicates for type-raising +# The direction of the innermost category must be towards +# the primary functor. +# The restriction that the variable must be primitive is not +# common to all versions of CCGs; some authors have other restrictions. +def forwardTConstraint(left, right): + arg = innermostFunction(right) + return arg.dir().is_backward() and arg.res().is_primitive() + + +def backwardTConstraint(left, right): + arg = innermostFunction(left) + return arg.dir().is_forward() and arg.res().is_primitive() + + +# Instances of type-raising combinators +ForwardT = ForwardCombinator(UndirectedTypeRaise(), forwardTConstraint) +BackwardT = BackwardCombinator(UndirectedTypeRaise(), backwardTConstraint) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/lexicon.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/lexicon.py new file mode 100644 index 0000000..f15c5d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/lexicon.py @@ -0,0 +1,338 @@ +# Natural Language Toolkit: Combinatory Categorial Grammar +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Graeme Gange +# URL: +# For license information, see LICENSE.TXT +""" +CCG Lexicons +""" + +import re +from collections import defaultdict + +from nltk.ccg.api import CCGVar, Direction, FunctionalCategory, PrimitiveCategory +from nltk.internals import deprecated +from nltk.sem.logic import Expression + +# ------------ +# Regular expressions used for parsing components of the lexicon +# ------------ + +# Parses a primitive category and subscripts +PRIM_RE = re.compile(r"""([A-Za-z]+)(\[[A-Za-z,]+\])?""") + +# Separates the next primitive category from the remainder of the +# string +NEXTPRIM_RE = re.compile(r"""([A-Za-z]+(?:\[[A-Za-z,]+\])?)(.*)""") + +# Separates the next application operator from the remainder +APP_RE = re.compile(r"""([\\/])([.,]?)([.,]?)(.*)""") + +# Parses the definition of the right-hand side (rhs) of either a word or a family +LEX_RE = re.compile(r"""([\S_]+)\s*(::|[-=]+>)\s*(.+)""", re.UNICODE) + +# Parses the right hand side that contains category and maybe semantic predicate +RHS_RE = re.compile(r"""([^{}]*[^ {}])\s*(\{[^}]+\})?""", re.UNICODE) + +# Parses the semantic predicate +SEMANTICS_RE = re.compile(r"""\{([^}]+)\}""", re.UNICODE) + +# Strips comments from a line +COMMENTS_RE = re.compile("""([^#]*)(?:#.*)?""") + + +class Token: + """ + Class representing a token. + + token => category {semantics} + e.g. eat => S\\var[pl]/var {\\x y.eat(x,y)} + + * `token` (string) + * `categ` (string) + * `semantics` (Expression) + """ + + def __init__(self, token, categ, semantics=None): + self._token = token + self._categ = categ + self._semantics = semantics + + def categ(self): + return self._categ + + def semantics(self): + return self._semantics + + def __str__(self): + semantics_str = "" + if self._semantics is not None: + semantics_str = " {" + str(self._semantics) + "}" + return "" + str(self._categ) + semantics_str + + def __cmp__(self, other): + if not isinstance(other, Token): + return -1 + return cmp((self._categ, self._semantics), other.categ(), other.semantics()) + + +class CCGLexicon: + """ + Class representing a lexicon for CCG grammars. + + * `primitives`: The list of primitive categories for the lexicon + * `families`: Families of categories + * `entries`: A mapping of words to possible categories + """ + + def __init__(self, start, primitives, families, entries): + self._start = PrimitiveCategory(start) + self._primitives = primitives + self._families = families + self._entries = entries + + def categories(self, word): + """ + Returns all the possible categories for a word + """ + return self._entries[word] + + def start(self): + """ + Return the target category for the parser + """ + return self._start + + def __str__(self): + """ + String representation of the lexicon. Used for debugging. + """ + string = "" + first = True + for ident in sorted(self._entries): + if not first: + string = string + "\n" + string = string + ident + " => " + + first = True + for cat in self._entries[ident]: + if not first: + string = string + " | " + else: + first = False + string = string + "%s" % cat + return string + + +# ----------- +# Parsing lexicons +# ----------- + + +def matchBrackets(string): + """ + Separate the contents matching the first set of brackets from the rest of + the input. + """ + rest = string[1:] + inside = "(" + + while rest != "" and not rest.startswith(")"): + if rest.startswith("("): + (part, rest) = matchBrackets(rest) + inside = inside + part + else: + inside = inside + rest[0] + rest = rest[1:] + if rest.startswith(")"): + return (inside + ")", rest[1:]) + raise AssertionError("Unmatched bracket in string '" + string + "'") + + +def nextCategory(string): + """ + Separate the string for the next portion of the category from the rest + of the string + """ + if string.startswith("("): + return matchBrackets(string) + return NEXTPRIM_RE.match(string).groups() + + +def parseApplication(app): + """ + Parse an application operator + """ + return Direction(app[0], app[1:]) + + +def parseSubscripts(subscr): + """ + Parse the subscripts for a primitive category + """ + if subscr: + return subscr[1:-1].split(",") + return [] + + +def parsePrimitiveCategory(chunks, primitives, families, var): + """ + Parse a primitive category + + If the primitive is the special category 'var', replace it with the + correct `CCGVar`. + """ + if chunks[0] == "var": + if chunks[1] is None: + if var is None: + var = CCGVar() + return (var, var) + + catstr = chunks[0] + if catstr in families: + (cat, cvar) = families[catstr] + if var is None: + var = cvar + else: + cat = cat.substitute([(cvar, var)]) + return (cat, var) + + if catstr in primitives: + subscrs = parseSubscripts(chunks[1]) + return (PrimitiveCategory(catstr, subscrs), var) + raise AssertionError( + "String '" + catstr + "' is neither a family nor primitive category." + ) + + +def augParseCategory(line, primitives, families, var=None): + """ + Parse a string representing a category, and returns a tuple with + (possibly) the CCG variable for the category + """ + (cat_string, rest) = nextCategory(line) + + if cat_string.startswith("("): + (res, var) = augParseCategory(cat_string[1:-1], primitives, families, var) + + else: + (res, var) = parsePrimitiveCategory( + PRIM_RE.match(cat_string).groups(), primitives, families, var + ) + + while rest != "": + app = APP_RE.match(rest).groups() + direction = parseApplication(app[0:3]) + rest = app[3] + + (cat_string, rest) = nextCategory(rest) + if cat_string.startswith("("): + (arg, var) = augParseCategory(cat_string[1:-1], primitives, families, var) + else: + (arg, var) = parsePrimitiveCategory( + PRIM_RE.match(cat_string).groups(), primitives, families, var + ) + res = FunctionalCategory(res, arg, direction) + + return (res, var) + + +def fromstring(lex_str, include_semantics=False): + """ + Convert string representation into a lexicon for CCGs. + """ + CCGVar.reset_id() + primitives = [] + families = {} + entries = defaultdict(list) + for line in lex_str.splitlines(): + # Strip comments and leading/trailing whitespace. + line = COMMENTS_RE.match(line).groups()[0].strip() + if line == "": + continue + + if line.startswith(":-"): + # A line of primitive categories. + # The first one is the target category + # ie, :- S, N, NP, VP + primitives = primitives + [ + prim.strip() for prim in line[2:].strip().split(",") + ] + else: + # Either a family definition, or a word definition + (ident, sep, rhs) = LEX_RE.match(line).groups() + (catstr, semantics_str) = RHS_RE.match(rhs).groups() + (cat, var) = augParseCategory(catstr, primitives, families) + + if sep == "::": + # Family definition + # ie, Det :: NP/N + families[ident] = (cat, var) + else: + semantics = None + if include_semantics is True: + if semantics_str is None: + raise AssertionError( + line + + " must contain semantics because include_semantics is set to True" + ) + else: + semantics = Expression.fromstring( + SEMANTICS_RE.match(semantics_str).groups()[0] + ) + # Word definition + # ie, which => (N\N)/(S/NP) + entries[ident].append(Token(ident, cat, semantics)) + return CCGLexicon(primitives[0], primitives, families, entries) + + +@deprecated("Use fromstring() instead.") +def parseLexicon(lex_str): + return fromstring(lex_str) + + +openccg_tinytiny = fromstring( + """ + # Rather minimal lexicon based on the openccg `tinytiny' grammar. + # Only incorporates a subset of the morphological subcategories, however. + :- S,NP,N # Primitive categories + Det :: NP/N # Determiners + Pro :: NP + IntransVsg :: S\\NP[sg] # Tensed intransitive verbs (singular) + IntransVpl :: S\\NP[pl] # Plural + TransVsg :: S\\NP[sg]/NP # Tensed transitive verbs (singular) + TransVpl :: S\\NP[pl]/NP # Plural + + the => NP[sg]/N[sg] + the => NP[pl]/N[pl] + + I => Pro + me => Pro + we => Pro + us => Pro + + book => N[sg] + books => N[pl] + + peach => N[sg] + peaches => N[pl] + + policeman => N[sg] + policemen => N[pl] + + boy => N[sg] + boys => N[pl] + + sleep => IntransVsg + sleep => IntransVpl + + eat => IntransVpl + eat => TransVpl + eats => IntransVsg + eats => TransVsg + + see => TransVpl + sees => TransVsg + """ +) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/logic.py b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/logic.py new file mode 100644 index 0000000..f702df2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/ccg/logic.py @@ -0,0 +1,60 @@ +# Natural Language Toolkit: Combinatory Categorial Grammar +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tanin Na Nakorn (@tanin) +# URL: +# For license information, see LICENSE.TXT +""" +Helper functions for CCG semantics computation +""" + +from nltk.sem.logic import * + + +def compute_type_raised_semantics(semantics): + core = semantics + parent = None + while isinstance(core, LambdaExpression): + parent = core + core = core.term + + var = Variable("F") + while var in core.free(): + var = unique_variable(pattern=var) + core = ApplicationExpression(FunctionVariableExpression(var), core) + + if parent is not None: + parent.term = core + else: + semantics = core + + return LambdaExpression(var, semantics) + + +def compute_function_semantics(function, argument): + return ApplicationExpression(function, argument).simplify() + + +def compute_composition_semantics(function, argument): + assert isinstance(argument, LambdaExpression), ( + "`" + str(argument) + "` must be a lambda expression" + ) + return LambdaExpression( + argument.variable, ApplicationExpression(function, argument.term).simplify() + ) + + +def compute_substitution_semantics(function, argument): + assert isinstance(function, LambdaExpression) and isinstance( + function.term, LambdaExpression + ), ("`" + str(function) + "` must be a lambda expression with 2 arguments") + assert isinstance(argument, LambdaExpression), ( + "`" + str(argument) + "` must be a lambda expression" + ) + + new_argument = ApplicationExpression( + argument, VariableExpression(function.variable) + ).simplify() + new_term = ApplicationExpression(function.term, new_argument).simplify() + + return LambdaExpression(function.variable, new_term) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__init__.py new file mode 100644 index 0000000..97cad81 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__init__.py @@ -0,0 +1,48 @@ +# Natural Language Toolkit: Chatbots +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +# Based on an Eliza implementation by Joe Strout , +# Jeff Epler and Jez Higgins . + +""" +A class for simple chatbots. These perform simple pattern matching on sentences +typed by users, and respond with automatically generated sentences. + +These chatbots may not work using the windows command line or the +windows IDLE GUI. +""" + +from nltk.chat.eliza import eliza_chat +from nltk.chat.iesha import iesha_chat +from nltk.chat.rude import rude_chat +from nltk.chat.suntsu import suntsu_chat +from nltk.chat.util import Chat +from nltk.chat.zen import zen_chat + +bots = [ + (eliza_chat, "Eliza (psycho-babble)"), + (iesha_chat, "Iesha (teen anime junky)"), + (rude_chat, "Rude (abusive bot)"), + (suntsu_chat, "Suntsu (Chinese sayings)"), + (zen_chat, "Zen (gems of wisdom)"), +] + + +def chatbots(): + print("Which chatbot would you like to talk to?") + botcount = len(bots) + for i in range(botcount): + print(" %d: %s" % (i + 1, bots[i][1])) + while True: + choice = input(f"\nEnter a number in the range 1-{botcount}: ").strip() + if choice.isdigit() and (int(choice) - 1) in range(botcount): + break + else: + print(" Error: bad chatbot number") + + chatbot = bots[int(choice) - 1][0] + chatbot() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a8837dd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/eliza.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/eliza.cpython-310.pyc new file mode 100644 index 0000000..bf4a506 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/eliza.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/iesha.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/iesha.cpython-310.pyc new file mode 100644 index 0000000..a32930e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/iesha.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/rude.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/rude.cpython-310.pyc new file mode 100644 index 0000000..dec9986 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/rude.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/suntsu.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/suntsu.cpython-310.pyc new file mode 100644 index 0000000..1bd2ee9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/suntsu.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..d22c60b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/zen.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/zen.cpython-310.pyc new file mode 100644 index 0000000..a049703 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/__pycache__/zen.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/eliza.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/eliza.py new file mode 100644 index 0000000..65ecc64 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/eliza.py @@ -0,0 +1,337 @@ +# Natural Language Toolkit: Eliza +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +# Based on an Eliza implementation by Joe Strout , +# Jeff Epler and Jez Higgins . + +# a translation table used to convert things you say into things the +# computer says back, e.g. "I am" --> "you are" + +from nltk.chat.util import Chat, reflections + +# a table of response pairs, where each pair consists of a +# regular expression, and a list of possible responses, +# with group-macros labelled as %1, %2. + +pairs = ( + ( + r"I need (.*)", + ( + "Why do you need %1?", + "Would it really help you to get %1?", + "Are you sure you need %1?", + ), + ), + ( + r"Why don\'t you (.*)", + ( + "Do you really think I don't %1?", + "Perhaps eventually I will %1.", + "Do you really want me to %1?", + ), + ), + ( + r"Why can\'t I (.*)", + ( + "Do you think you should be able to %1?", + "If you could %1, what would you do?", + "I don't know -- why can't you %1?", + "Have you really tried?", + ), + ), + ( + r"I can\'t (.*)", + ( + "How do you know you can't %1?", + "Perhaps you could %1 if you tried.", + "What would it take for you to %1?", + ), + ), + ( + r"I am (.*)", + ( + "Did you come to me because you are %1?", + "How long have you been %1?", + "How do you feel about being %1?", + ), + ), + ( + r"I\'m (.*)", + ( + "How does being %1 make you feel?", + "Do you enjoy being %1?", + "Why do you tell me you're %1?", + "Why do you think you're %1?", + ), + ), + ( + r"Are you (.*)", + ( + "Why does it matter whether I am %1?", + "Would you prefer it if I were not %1?", + "Perhaps you believe I am %1.", + "I may be %1 -- what do you think?", + ), + ), + ( + r"What (.*)", + ( + "Why do you ask?", + "How would an answer to that help you?", + "What do you think?", + ), + ), + ( + r"How (.*)", + ( + "How do you suppose?", + "Perhaps you can answer your own question.", + "What is it you're really asking?", + ), + ), + ( + r"Because (.*)", + ( + "Is that the real reason?", + "What other reasons come to mind?", + "Does that reason apply to anything else?", + "If %1, what else must be true?", + ), + ), + ( + r"(.*) sorry (.*)", + ( + "There are many times when no apology is needed.", + "What feelings do you have when you apologize?", + ), + ), + ( + r"Hello(.*)", + ( + "Hello... I'm glad you could drop by today.", + "Hi there... how are you today?", + "Hello, how are you feeling today?", + ), + ), + ( + r"I think (.*)", + ("Do you doubt %1?", "Do you really think so?", "But you're not sure %1?"), + ), + ( + r"(.*) friend (.*)", + ( + "Tell me more about your friends.", + "When you think of a friend, what comes to mind?", + "Why don't you tell me about a childhood friend?", + ), + ), + (r"Yes", ("You seem quite sure.", "OK, but can you elaborate a bit?")), + ( + r"(.*) computer(.*)", + ( + "Are you really talking about me?", + "Does it seem strange to talk to a computer?", + "How do computers make you feel?", + "Do you feel threatened by computers?", + ), + ), + ( + r"Is it (.*)", + ( + "Do you think it is %1?", + "Perhaps it's %1 -- what do you think?", + "If it were %1, what would you do?", + "It could well be that %1.", + ), + ), + ( + r"It is (.*)", + ( + "You seem very certain.", + "If I told you that it probably isn't %1, what would you feel?", + ), + ), + ( + r"Can you (.*)", + ( + "What makes you think I can't %1?", + "If I could %1, then what?", + "Why do you ask if I can %1?", + ), + ), + ( + r"Can I (.*)", + ( + "Perhaps you don't want to %1.", + "Do you want to be able to %1?", + "If you could %1, would you?", + ), + ), + ( + r"You are (.*)", + ( + "Why do you think I am %1?", + "Does it please you to think that I'm %1?", + "Perhaps you would like me to be %1.", + "Perhaps you're really talking about yourself?", + ), + ), + ( + r"You\'re (.*)", + ( + "Why do you say I am %1?", + "Why do you think I am %1?", + "Are we talking about you, or me?", + ), + ), + ( + r"I don\'t (.*)", + ("Don't you really %1?", "Why don't you %1?", "Do you want to %1?"), + ), + ( + r"I feel (.*)", + ( + "Good, tell me more about these feelings.", + "Do you often feel %1?", + "When do you usually feel %1?", + "When you feel %1, what do you do?", + ), + ), + ( + r"I have (.*)", + ( + "Why do you tell me that you've %1?", + "Have you really %1?", + "Now that you have %1, what will you do next?", + ), + ), + ( + r"I would (.*)", + ( + "Could you explain why you would %1?", + "Why would you %1?", + "Who else knows that you would %1?", + ), + ), + ( + r"Is there (.*)", + ( + "Do you think there is %1?", + "It's likely that there is %1.", + "Would you like there to be %1?", + ), + ), + ( + r"My (.*)", + ( + "I see, your %1.", + "Why do you say that your %1?", + "When your %1, how do you feel?", + ), + ), + ( + r"You (.*)", + ( + "We should be discussing you, not me.", + "Why do you say that about me?", + "Why do you care whether I %1?", + ), + ), + (r"Why (.*)", ("Why don't you tell me the reason why %1?", "Why do you think %1?")), + ( + r"I want (.*)", + ( + "What would it mean to you if you got %1?", + "Why do you want %1?", + "What would you do if you got %1?", + "If you got %1, then what would you do?", + ), + ), + ( + r"(.*) mother(.*)", + ( + "Tell me more about your mother.", + "What was your relationship with your mother like?", + "How do you feel about your mother?", + "How does this relate to your feelings today?", + "Good family relations are important.", + ), + ), + ( + r"(.*) father(.*)", + ( + "Tell me more about your father.", + "How did your father make you feel?", + "How do you feel about your father?", + "Does your relationship with your father relate to your feelings today?", + "Do you have trouble showing affection with your family?", + ), + ), + ( + r"(.*) child(.*)", + ( + "Did you have close friends as a child?", + "What is your favorite childhood memory?", + "Do you remember any dreams or nightmares from childhood?", + "Did the other children sometimes tease you?", + "How do you think your childhood experiences relate to your feelings today?", + ), + ), + ( + r"(.*)\?", + ( + "Why do you ask that?", + "Please consider whether you can answer your own question.", + "Perhaps the answer lies within yourself?", + "Why don't you tell me?", + ), + ), + ( + r"quit", + ( + "Thank you for talking with me.", + "Good-bye.", + "Thank you, that will be $150. Have a good day!", + ), + ), + ( + r"(.*)", + ( + "Please tell me more.", + "Let's change focus a bit... Tell me about your family.", + "Can you elaborate on that?", + "Why do you say that %1?", + "I see.", + "Very interesting.", + "%1.", + "I see. And what does that tell you?", + "How does that make you feel?", + "How do you feel when you say that?", + ), + ), +) + +eliza_chatbot = Chat(pairs, reflections) + + +def eliza_chat(): + print("Therapist\n---------") + print("Talk to the program by typing in plain English, using normal upper-") + print('and lower-case letters and punctuation. Enter "quit" when done.') + print("=" * 72) + print("Hello. How are you feeling today?") + + eliza_chatbot.converse() + + +def demo(): + eliza_chat() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/iesha.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/iesha.py new file mode 100644 index 0000000..81a4b53 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/iesha.py @@ -0,0 +1,160 @@ +# Natural Language Toolkit: Teen Chatbot +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Selina Dennis +# URL: +# For license information, see LICENSE.TXT + +""" +This chatbot is a tongue-in-cheek take on the average teen +anime junky that frequents YahooMessenger or MSNM. +All spelling mistakes and flawed grammar are intentional. +""" + +from nltk.chat.util import Chat + +reflections = { + "am": "r", + "was": "were", + "i": "u", + "i'd": "u'd", + "i've": "u'v", + "ive": "u'v", + "i'll": "u'll", + "my": "ur", + "are": "am", + "you're": "im", + "you've": "ive", + "you'll": "i'll", + "your": "my", + "yours": "mine", + "you": "me", + "u": "me", + "ur": "my", + "urs": "mine", + "me": "u", +} + +# Note: %1/2/etc are used without spaces prior as the chat bot seems +# to add a superfluous space when matching. + +pairs = ( + ( + r"I\'m (.*)", + ( + "ur%1?? that's so cool! kekekekeke ^_^ tell me more!", + "ur%1? neat!! kekeke >_<", + ), + ), + ( + r"(.*) don\'t you (.*)", + ( + r"u think I can%2??! really?? kekeke \<_\<", + "what do u mean%2??!", + "i could if i wanted, don't you think!! kekeke", + ), + ), + (r"ye[as] [iI] (.*)", ("u%1? cool!! how?", "how come u%1??", "u%1? so do i!!")), + ( + r"do (you|u) (.*)\??", + ("do i%2? only on tuesdays! kekeke *_*", "i dunno! do u%2??"), + ), + ( + r"(.*)\?", + ( + "man u ask lots of questions!", + "booooring! how old r u??", + "boooooring!! ur not very fun", + ), + ), + ( + r"(cos|because) (.*)", + ("hee! i don't believe u! >_<", "nuh-uh! >_<", "ooooh i agree!"), + ), + ( + r"why can\'t [iI] (.*)", + ( + "i dunno! y u askin me for!", + "try harder, silly! hee! ^_^", + "i dunno! but when i can't%1 i jump up and down!", + ), + ), + ( + r"I can\'t (.*)", + ( + "u can't what??! >_<", + "that's ok! i can't%1 either! kekekekeke ^_^", + "try harder, silly! hee! ^&^", + ), + ), + ( + r"(.*) (like|love|watch) anime", + ( + "omg i love anime!! do u like sailor moon??! ^&^", + "anime yay! anime rocks sooooo much!", + "oooh anime! i love anime more than anything!", + "anime is the bestest evar! evangelion is the best!", + "hee anime is the best! do you have ur fav??", + ), + ), + ( + r"I (like|love|watch|play) (.*)", + ("yay! %2 rocks!", "yay! %2 is neat!", "cool! do u like other stuff?? ^_^"), + ), + ( + r"anime sucks|(.*) (hate|detest) anime", + ( + "ur a liar! i'm not gonna talk to u nemore if u h8 anime *;*", + "no way! anime is the best ever!", + "nuh-uh, anime is the best!", + ), + ), + ( + r"(are|r) (you|u) (.*)", + ("am i%1??! how come u ask that!", "maybe! y shud i tell u?? kekeke >_>"), + ), + ( + r"what (.*)", + ("hee u think im gonna tell u? .v.", "booooooooring! ask me somethin else!"), + ), + (r"how (.*)", ("not tellin!! kekekekekeke ^_^",)), + (r"(hi|hello|hey) (.*)", ("hi!!! how r u!!",)), + ( + r"quit", + ( + "mom says i have to go eat dinner now :,( bye!!", + "awww u have to go?? see u next time!!", + "how to see u again soon! ^_^", + ), + ), + ( + r"(.*)", + ( + "ur funny! kekeke", + "boooooring! talk about something else! tell me wat u like!", + "do u like anime??", + "do u watch anime? i like sailor moon! ^_^", + "i wish i was a kitty!! kekekeke ^_^", + ), + ), +) + +iesha_chatbot = Chat(pairs, reflections) + + +def iesha_chat(): + print("Iesha the TeenBoT\n---------") + print("Talk to the program by typing in plain English, using normal upper-") + print('and lower-case letters and punctuation. Enter "quit" when done.') + print("=" * 72) + print("hi!! i'm iesha! who r u??!") + + iesha_chatbot.converse() + + +def demo(): + iesha_chat() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/rude.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/rude.py new file mode 100644 index 0000000..42d483d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/rude.py @@ -0,0 +1,125 @@ +# Natural Language Toolkit: Rude Chatbot +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Peter Spiller +# URL: +# For license information, see LICENSE.TXT + +from nltk.chat.util import Chat, reflections + +pairs = ( + ( + r"We (.*)", + ( + "What do you mean, 'we'?", + "Don't include me in that!", + "I wouldn't be so sure about that.", + ), + ), + ( + r"You should (.*)", + ("Don't tell me what to do, buddy.", "Really? I should, should I?"), + ), + ( + r"You\'re(.*)", + ( + "More like YOU'RE %1!", + "Hah! Look who's talking.", + "Come over here and tell me I'm %1.", + ), + ), + ( + r"You are(.*)", + ( + "More like YOU'RE %1!", + "Hah! Look who's talking.", + "Come over here and tell me I'm %1.", + ), + ), + ( + r"I can\'t(.*)", + ( + "You do sound like the type who can't %1.", + "Hear that splashing sound? That's my heart bleeding for you.", + "Tell somebody who might actually care.", + ), + ), + ( + r"I think (.*)", + ( + "I wouldn't think too hard if I were you.", + "You actually think? I'd never have guessed...", + ), + ), + ( + r"I (.*)", + ( + "I'm getting a bit tired of hearing about you.", + "How about we talk about me instead?", + "Me, me, me... Frankly, I don't care.", + ), + ), + ( + r"How (.*)", + ( + "How do you think?", + "Take a wild guess.", + "I'm not even going to dignify that with an answer.", + ), + ), + (r"What (.*)", ("Do I look like an encyclopedia?", "Figure it out yourself.")), + ( + r"Why (.*)", + ( + "Why not?", + "That's so obvious I thought even you'd have already figured it out.", + ), + ), + ( + r"(.*)shut up(.*)", + ( + "Make me.", + "Getting angry at a feeble NLP assignment? Somebody's losing it.", + "Say that again, I dare you.", + ), + ), + ( + r"Shut up(.*)", + ( + "Make me.", + "Getting angry at a feeble NLP assignment? Somebody's losing it.", + "Say that again, I dare you.", + ), + ), + ( + r"Hello(.*)", + ("Oh good, somebody else to talk to. Joy.", "'Hello'? How original..."), + ), + ( + r"(.*)", + ( + "I'm getting bored here. Become more interesting.", + "Either become more thrilling or get lost, buddy.", + "Change the subject before I die of fatal boredom.", + ), + ), +) + +rude_chatbot = Chat(pairs, reflections) + + +def rude_chat(): + print("Talk to the program by typing in plain English, using normal upper-") + print('and lower-case letters and punctuation. Enter "quit" when done.') + print("=" * 72) + print("I suppose I should say hello.") + + rude_chatbot.converse() + + +def demo(): + rude_chat() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/suntsu.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/suntsu.py new file mode 100644 index 0000000..3473482 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/suntsu.py @@ -0,0 +1,140 @@ +# Natural Language Toolkit: Sun Tsu-Bot +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Sam Huston 2007 +# URL: +# For license information, see LICENSE.TXT + +""" +Tsu bot responds to all queries with a Sun Tsu sayings + +Quoted from Sun Tsu's The Art of War +Translated by LIONEL GILES, M.A. 1910 +Hosted by the Gutenberg Project +https://www.gutenberg.org/ +""" + +from nltk.chat.util import Chat, reflections + +pairs = ( + (r"quit", ("Good-bye.", "Plan well", "May victory be your future")), + ( + r"[^\?]*\?", + ( + "Please consider whether you can answer your own question.", + "Ask me no questions!", + ), + ), + ( + r"[0-9]+(.*)", + ( + "It is the rule in war, if our forces are ten to the enemy's one, to surround him; if five to one, to attack him; if twice as numerous, to divide our army into two.", + "There are five essentials for victory", + ), + ), + ( + r"[A-Ca-c](.*)", + ( + "The art of war is of vital importance to the State.", + "All warfare is based on deception.", + "If your opponent is secure at all points, be prepared for him. If he is in superior strength, evade him.", + "If the campaign is protracted, the resources of the State will not be equal to the strain.", + "Attack him where he is unprepared, appear where you are not expected.", + "There is no instance of a country having benefited from prolonged warfare.", + ), + ), + ( + r"[D-Fd-f](.*)", + ( + "The skillful soldier does not raise a second levy, neither are his supply-wagons loaded more than twice.", + "Bring war material with you from home, but forage on the enemy.", + "In war, then, let your great object be victory, not lengthy campaigns.", + "To fight and conquer in all your battles is not supreme excellence; supreme excellence consists in breaking the enemy's resistance without fighting.", + ), + ), + ( + r"[G-Ig-i](.*)", + ( + "Heaven signifies night and day, cold and heat, times and seasons.", + "It is the rule in war, if our forces are ten to the enemy's one, to surround him; if five to one, to attack him; if twice as numerous, to divide our army into two.", + "The good fighters of old first put themselves beyond the possibility of defeat, and then waited for an opportunity of defeating the enemy.", + "One may know how to conquer without being able to do it.", + ), + ), + ( + r"[J-Lj-l](.*)", + ( + "There are three ways in which a ruler can bring misfortune upon his army.", + "By commanding the army to advance or to retreat, being ignorant of the fact that it cannot obey. This is called hobbling the army.", + "By attempting to govern an army in the same way as he administers a kingdom, being ignorant of the conditions which obtain in an army. This causes restlessness in the soldier's minds.", + "By employing the officers of his army without discrimination, through ignorance of the military principle of adaptation to circumstances. This shakes the confidence of the soldiers.", + "There are five essentials for victory", + "He will win who knows when to fight and when not to fight.", + "He will win who knows how to handle both superior and inferior forces.", + "He will win whose army is animated by the same spirit throughout all its ranks.", + "He will win who, prepared himself, waits to take the enemy unprepared.", + "He will win who has military capacity and is not interfered with by the sovereign.", + ), + ), + ( + r"[M-Om-o](.*)", + ( + "If you know the enemy and know yourself, you need not fear the result of a hundred battles.", + "If you know yourself but not the enemy, for every victory gained you will also suffer a defeat.", + "If you know neither the enemy nor yourself, you will succumb in every battle.", + "The control of a large force is the same principle as the control of a few men: it is merely a question of dividing up their numbers.", + ), + ), + ( + r"[P-Rp-r](.*)", + ( + "Security against defeat implies defensive tactics; ability to defeat the enemy means taking the offensive.", + "Standing on the defensive indicates insufficient strength; attacking, a superabundance of strength.", + "He wins his battles by making no mistakes. Making no mistakes is what establishes the certainty of victory, for it means conquering an enemy that is already defeated.", + "A victorious army opposed to a routed one, is as a pound's weight placed in the scale against a single grain.", + "The onrush of a conquering force is like the bursting of pent-up waters into a chasm a thousand fathoms deep.", + ), + ), + ( + r"[S-Us-u](.*)", + ( + "What the ancients called a clever fighter is one who not only wins, but excels in winning with ease.", + "Hence his victories bring him neither reputation for wisdom nor credit for courage.", + "Hence the skillful fighter puts himself into a position which makes defeat impossible, and does not miss the moment for defeating the enemy.", + "In war the victorious strategist only seeks battle after the victory has been won, whereas he who is destined to defeat first fights and afterwards looks for victory.", + "There are not more than five musical notes, yet the combinations of these five give rise to more melodies than can ever be heard.", + "Appear at points which the enemy must hasten to defend; march swiftly to places where you are not expected.", + ), + ), + ( + r"[V-Zv-z](.*)", + ( + "It is a matter of life and death, a road either to safety or to ruin.", + "Hold out baits to entice the enemy. Feign disorder, and crush him.", + "All men can see the tactics whereby I conquer, but what none can see is the strategy out of which victory is evolved.", + "Do not repeat the tactics which have gained you one victory, but let your methods be regulated by the infinite variety of circumstances.", + "So in war, the way is to avoid what is strong and to strike at what is weak.", + "Just as water retains no constant shape, so in warfare there are no constant conditions.", + ), + ), + (r"(.*)", ("Your statement insults me.", "")), +) + +suntsu_chatbot = Chat(pairs, reflections) + + +def suntsu_chat(): + print("Talk to the program by typing in plain English, using normal upper-") + print('and lower-case letters and punctuation. Enter "quit" when done.') + print("=" * 72) + print("You seek enlightenment?") + + suntsu_chatbot.converse() + + +def demo(): + suntsu_chat() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/util.py new file mode 100644 index 0000000..9c98ed1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/util.py @@ -0,0 +1,124 @@ +# Natural Language Toolkit: Chatbot Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +# Based on an Eliza implementation by Joe Strout , +# Jeff Epler and Jez Higgins . + +import random +import re + +reflections = { + "i am": "you are", + "i was": "you were", + "i": "you", + "i'm": "you are", + "i'd": "you would", + "i've": "you have", + "i'll": "you will", + "my": "your", + "you are": "I am", + "you were": "I was", + "you've": "I have", + "you'll": "I will", + "your": "my", + "yours": "mine", + "you": "me", + "me": "you", +} + + +class Chat: + def __init__(self, pairs, reflections={}): + """ + Initialize the chatbot. Pairs is a list of patterns and responses. Each + pattern is a regular expression matching the user's statement or question, + e.g. r'I like (.*)'. For each such pattern a list of possible responses + is given, e.g. ['Why do you like %1', 'Did you ever dislike %1']. Material + which is matched by parenthesized sections of the patterns (e.g. .*) is mapped to + the numbered positions in the responses, e.g. %1. + + :type pairs: list of tuple + :param pairs: The patterns and responses + :type reflections: dict + :param reflections: A mapping between first and second person expressions + :rtype: None + """ + + self._pairs = [(re.compile(x, re.IGNORECASE), y) for (x, y) in pairs] + self._reflections = reflections + self._regex = self._compile_reflections() + + def _compile_reflections(self): + sorted_refl = sorted(self._reflections, key=len, reverse=True) + return re.compile( + r"\b({})\b".format("|".join(map(re.escape, sorted_refl))), re.IGNORECASE + ) + + def _substitute(self, str): + """ + Substitute words in the string, according to the specified reflections, + e.g. "I'm" -> "you are" + + :type str: str + :param str: The string to be mapped + :rtype: str + """ + + return self._regex.sub( + lambda mo: self._reflections[mo.string[mo.start() : mo.end()]], str.lower() + ) + + def _wildcards(self, response, match): + pos = response.find("%") + while pos >= 0: + num = int(response[pos + 1 : pos + 2]) + response = ( + response[:pos] + + self._substitute(match.group(num)) + + response[pos + 2 :] + ) + pos = response.find("%") + return response + + def respond(self, str): + """ + Generate a response to the user input. + + :type str: str + :param str: The string to be mapped + :rtype: str + """ + + # check each pattern + for (pattern, response) in self._pairs: + match = pattern.match(str) + + # did the pattern match? + if match: + resp = random.choice(response) # pick a random response + resp = self._wildcards(resp, match) # process wildcards + + # fix munged punctuation at the end + if resp[-2:] == "?.": + resp = resp[:-2] + "." + if resp[-2:] == "??": + resp = resp[:-2] + "?" + return resp + + # Hold a conversation with a chatbot + def converse(self, quit="quit"): + user_input = "" + while user_input != quit: + user_input = quit + try: + user_input = input(">") + except EOFError: + print(user_input) + if user_input: + while user_input[-1] in "!.": + user_input = user_input[:-1] + print(self.respond(user_input)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chat/zen.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/zen.py new file mode 100644 index 0000000..c2bb15c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chat/zen.py @@ -0,0 +1,329 @@ +# Natural Language Toolkit: Zen Chatbot +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Amy Holland +# URL: +# For license information, see LICENSE.TXT + +""" +Zen Chatbot talks in gems of Zen wisdom. + +This is a sample conversation with Zen Chatbot: +ZC: Welcome, my child. +me: Good afternoon. +ZC: Ask the question you have come to ask. +me: How can I achieve enlightenment? +ZC: How do you suppose? +me: Through meditation. +ZC: Form is emptiness, and emptiness form. +me: How can I empty my mind of worldly troubles? +ZC: Will an answer to that really help in your search for enlightenment? +me: Yes. +ZC: It is better to be right than to be certain. +me: I seek truth and wisdom. +ZC: The search for truth is a long journey. +me: Are you sure? +ZC: Maybe sure, maybe not sure. + + +The chatbot structure is based on that of chat.eliza. Thus, it uses +a translation table to convert from question to response +i.e. "I am" --> "you are" + +Of course, since Zen Chatbot does not understand the meaning of any words, +responses are very limited. Zen Chatbot will usually answer very vaguely, or +respond to a question by asking a different question, in much the same way +as Eliza. +""" + +from nltk.chat.util import Chat, reflections + +# responses are matched top to bottom, so non-specific matches occur later +# for each match, a list of possible responses is provided +responses = ( + # Zen Chatbot opens with the line "Welcome, my child." The usual + # response will be a greeting problem: 'good' matches "good morning", + # "good day" etc, but also "good grief!" and other sentences starting + # with the word 'good' that may not be a greeting + ( + r"(hello(.*))|(good [a-zA-Z]+)", + ( + "The path to enlightenment is often difficult to see.", + "Greetings. I sense your mind is troubled. Tell me of your troubles.", + "Ask the question you have come to ask.", + "Hello. Do you seek englightenment?", + ), + ), + # "I need" and "I want" can be followed by a thing (eg 'help') + # or an action (eg 'to see you') + # + # This is a problem with this style of response - + # person: "I need you" + # chatbot: "me can be achieved by hard work and dedication of the mind" + # i.e. 'you' is not really a thing that can be mapped this way, so this + # interpretation only makes sense for some inputs + # + ( + r"i need (.*)", + ( + "%1 can be achieved by hard work and dedication of the mind.", + "%1 is not a need, but a desire of the mind. Clear your mind of such concerns.", + "Focus your mind on%1, and you will find what you need.", + ), + ), + ( + r"i want (.*)", + ( + "Desires of the heart will distract you from the path to enlightenment.", + "Will%1 help you attain enlightenment?", + "Is%1 a desire of the mind, or of the heart?", + ), + ), + # why questions are separated into three types: + # "why..I" e.g. "why am I here?" "Why do I like cake?" + # "why..you" e.g. "why are you here?" "Why won't you tell me?" + # "why..." e.g. "Why is the sky blue?" + # problems: + # person: "Why can't you tell me?" + # chatbot: "Are you sure I tell you?" + # - this style works for positives (e.g. "why do you like cake?") + # but does not work for negatives (e.g. "why don't you like cake?") + (r"why (.*) i (.*)\?", ("You%1%2?", "Perhaps you only think you%1%2")), + (r"why (.*) you(.*)\?", ("Why%1 you%2?", "%2 I%1", "Are you sure I%2?")), + (r"why (.*)\?", ("I cannot tell you why%1.", "Why do you think %1?")), + # e.g. "are you listening?", "are you a duck" + ( + r"are you (.*)\?", + ("Maybe%1, maybe not%1.", "Whether I am%1 or not is God's business."), + ), + # e.g. "am I a duck?", "am I going to die?" + ( + r"am i (.*)\?", + ("Perhaps%1, perhaps not%1.", "Whether you are%1 or not is not for me to say."), + ), + # what questions, e.g. "what time is it?" + # problems: + # person: "What do you want?" + # chatbot: "Seek truth, not what do me want." + (r"what (.*)\?", ("Seek truth, not what%1.", "What%1 should not concern you.")), + # how questions, e.g. "how do you do?" + ( + r"how (.*)\?", + ( + "How do you suppose?", + "Will an answer to that really help in your search for enlightenment?", + "Ask yourself not how, but why.", + ), + ), + # can questions, e.g. "can you run?", "can you come over here please?" + ( + r"can you (.*)\?", + ( + "I probably can, but I may not.", + "Maybe I can%1, and maybe I cannot.", + "I can do all, and I can do nothing.", + ), + ), + # can questions, e.g. "can I have some cake?", "can I know truth?" + ( + r"can i (.*)\?", + ( + "You can%1 if you believe you can%1, and have a pure spirit.", + "Seek truth and you will know if you can%1.", + ), + ), + # e.g. "It is raining" - implies the speaker is certain of a fact + ( + r"it is (.*)", + ( + "How can you be certain that%1, when you do not even know yourself?", + "Whether it is%1 or not does not change the way the world is.", + ), + ), + # e.g. "is there a doctor in the house?" + ( + r"is there (.*)\?", + ("There is%1 if you believe there is.", "It is possible that there is%1."), + ), + # e.g. "is it possible?", "is this true?" + (r"is(.*)\?", ("%1 is not relevant.", "Does this matter?")), + # non-specific question + ( + r"(.*)\?", + ( + "Do you think %1?", + "You seek the truth. Does the truth seek you?", + "If you intentionally pursue the answers to your questions, the answers become hard to see.", + "The answer to your question cannot be told. It must be experienced.", + ), + ), + # expression of hate of form "I hate you" or "Kelly hates cheese" + ( + r"(.*) (hate[s]?)|(dislike[s]?)|(don\'t like)(.*)", + ( + "Perhaps it is not about hating %2, but about hate from within.", + "Weeds only grow when we dislike them", + "Hate is a very strong emotion.", + ), + ), + # statement containing the word 'truth' + ( + r"(.*) truth(.*)", + ( + "Seek truth, and truth will seek you.", + "Remember, it is not the spoon which bends - only yourself.", + "The search for truth is a long journey.", + ), + ), + # desire to do an action + # e.g. "I want to go shopping" + ( + r"i want to (.*)", + ("You may %1 if your heart truly desires to.", "You may have to %1."), + ), + # desire for an object + # e.g. "I want a pony" + ( + r"i want (.*)", + ( + "Does your heart truly desire %1?", + "Is this a desire of the heart, or of the mind?", + ), + ), + # e.g. "I can't wait" or "I can't do this" + ( + r"i can\'t (.*)", + ( + "What we can and can't do is a limitation of the mind.", + "There are limitations of the body, and limitations of the mind.", + "Have you tried to%1 with a clear mind?", + ), + ), + # "I think.." indicates uncertainty. e.g. "I think so." + # problem: exceptions... + # e.g. "I think, therefore I am" + ( + r"i think (.*)", + ( + "Uncertainty in an uncertain world.", + "Indeed, how can we be certain of anything in such uncertain times.", + "Are you not, in fact, certain that%1?", + ), + ), + # "I feel...emotions/sick/light-headed..." + ( + r"i feel (.*)", + ( + "Your body and your emotions are both symptoms of your mind." + "What do you believe is the root of such feelings?", + "Feeling%1 can be a sign of your state-of-mind.", + ), + ), + # exclaimation mark indicating emotion + # e.g. "Wow!" or "No!" + ( + r"(.*)!", + ( + "I sense that you are feeling emotional today.", + "You need to calm your emotions.", + ), + ), + # because [statement] + # e.g. "because I said so" + ( + r"because (.*)", + ( + "Does knowning the reasons behind things help you to understand" + " the things themselves?", + "If%1, what else must be true?", + ), + ), + # yes or no - raise an issue of certainty/correctness + ( + r"(yes)|(no)", + ( + "Is there certainty in an uncertain world?", + "It is better to be right than to be certain.", + ), + ), + # sentence containing word 'love' + ( + r"(.*)love(.*)", + ( + "Think of the trees: they let the birds perch and fly with no intention to call them when they come, and no longing for their return when they fly away. Let your heart be like the trees.", + "Free love!", + ), + ), + # sentence containing word 'understand' - r + ( + r"(.*)understand(.*)", + ( + "If you understand, things are just as they are;" + " if you do not understand, things are just as they are.", + "Imagination is more important than knowledge.", + ), + ), + # 'I', 'me', 'my' - person is talking about themself. + # this breaks down when words contain these - eg 'Thyme', 'Irish' + ( + r"(.*)(me )|( me)|(my)|(mine)|(i)(.*)", + ( + "'I', 'me', 'my'... these are selfish expressions.", + "Have you ever considered that you might be a selfish person?", + "Try to consider others, not just yourself.", + "Think not just of yourself, but of others.", + ), + ), + # 'you' starting a sentence + # e.g. "you stink!" + ( + r"you (.*)", + ("My path is not of concern to you.", "I am but one, and you but one more."), + ), + # say goodbye with some extra Zen wisdom. + ( + r"exit", + ( + "Farewell. The obstacle is the path.", + "Farewell. Life is a journey, not a destination.", + "Good bye. We are cups, constantly and quietly being filled." + "\nThe trick is knowning how to tip ourselves over and let the beautiful stuff out.", + ), + ), + # fall through case - + # when stumped, respond with generic zen wisdom + # + ( + r"(.*)", + ( + "When you're enlightened, every word is wisdom.", + "Random talk is useless.", + "The reverse side also has a reverse side.", + "Form is emptiness, and emptiness is form.", + "I pour out a cup of water. Is the cup empty?", + ), + ), +) + +zen_chatbot = Chat(responses, reflections) + + +def zen_chat(): + print("*" * 75) + print("Zen Chatbot!".center(75)) + print("*" * 75) + print('"Look beyond mere words and letters - look into your mind"'.center(75)) + print("* Talk your way to truth with Zen Chatbot.") + print("* Type 'quit' when you have had enough.") + print("*" * 75) + print("Welcome, my child.") + + zen_chatbot.converse() + + +def demo(): + zen_chat() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__init__.py new file mode 100644 index 0000000..0162f7a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__init__.py @@ -0,0 +1,197 @@ +# Natural Language Toolkit: Chunkers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +# + +""" +Classes and interfaces for identifying non-overlapping linguistic +groups (such as base noun phrases) in unrestricted text. This task is +called "chunk parsing" or "chunking", and the identified groups are +called "chunks". The chunked text is represented using a shallow +tree called a "chunk structure." A chunk structure is a tree +containing tokens and chunks, where each chunk is a subtree containing +only tokens. For example, the chunk structure for base noun phrase +chunks in the sentence "I saw the big dog on the hill" is:: + + (SENTENCE: + (NP: ) + + (NP: ) + + (NP: )) + +To convert a chunk structure back to a list of tokens, simply use the +chunk structure's ``leaves()`` method. + +This module defines ``ChunkParserI``, a standard interface for +chunking texts; and ``RegexpChunkParser``, a regular-expression based +implementation of that interface. It also defines ``ChunkScore``, a +utility class for scoring chunk parsers. + +RegexpChunkParser +================= + +``RegexpChunkParser`` is an implementation of the chunk parser interface +that uses regular-expressions over tags to chunk a text. Its +``parse()`` method first constructs a ``ChunkString``, which encodes a +particular chunking of the input text. Initially, nothing is +chunked. ``parse.RegexpChunkParser`` then applies a sequence of +``RegexpChunkRule`` rules to the ``ChunkString``, each of which modifies +the chunking that it encodes. Finally, the ``ChunkString`` is +transformed back into a chunk structure, which is returned. + +``RegexpChunkParser`` can only be used to chunk a single kind of phrase. +For example, you can use an ``RegexpChunkParser`` to chunk the noun +phrases in a text, or the verb phrases in a text; but you can not +use it to simultaneously chunk both noun phrases and verb phrases in +the same text. (This is a limitation of ``RegexpChunkParser``, not of +chunk parsers in general.) + +RegexpChunkRules +---------------- + +A ``RegexpChunkRule`` is a transformational rule that updates the +chunking of a text by modifying its ``ChunkString``. Each +``RegexpChunkRule`` defines the ``apply()`` method, which modifies +the chunking encoded by a ``ChunkString``. The +``RegexpChunkRule`` class itself can be used to implement any +transformational rule based on regular expressions. There are +also a number of subclasses, which can be used to implement +simpler types of rules: + + - ``ChunkRule`` chunks anything that matches a given regular + expression. + - ``StripRule`` strips anything that matches a given regular + expression. + - ``UnChunkRule`` will un-chunk any chunk that matches a given + regular expression. + - ``MergeRule`` can be used to merge two contiguous chunks. + - ``SplitRule`` can be used to split a single chunk into two + smaller chunks. + - ``ExpandLeftRule`` will expand a chunk to incorporate new + unchunked material on the left. + - ``ExpandRightRule`` will expand a chunk to incorporate new + unchunked material on the right. + +Tag Patterns +~~~~~~~~~~~~ + +A ``RegexpChunkRule`` uses a modified version of regular +expression patterns, called "tag patterns". Tag patterns are +used to match sequences of tags. Examples of tag patterns are:: + + r'(
    ||)+' + r'+' + r'' + +The differences between regular expression patterns and tag +patterns are: + + - In tag patterns, ``'<'`` and ``'>'`` act as parentheses; so + ``'+'`` matches one or more repetitions of ``''``, not + ``''``. + - Whitespace in tag patterns is ignored. So + ``'
    | '`` is equivalent to ``'
    |'`` + - In tag patterns, ``'.'`` is equivalent to ``'[^{}<>]'``; so + ``''`` matches any single tag starting with ``'NN'``. + +The function ``tag_pattern2re_pattern`` can be used to transform +a tag pattern to an equivalent regular expression pattern. + +Efficiency +---------- + +Preliminary tests indicate that ``RegexpChunkParser`` can chunk at a +rate of about 300 tokens/second, with a moderately complex rule set. + +There may be problems if ``RegexpChunkParser`` is used with more than +5,000 tokens at a time. In particular, evaluation of some regular +expressions may cause the Python regular expression engine to +exceed its maximum recursion depth. We have attempted to minimize +these problems, but it is impossible to avoid them completely. We +therefore recommend that you apply the chunk parser to a single +sentence at a time. + +Emacs Tip +--------- + +If you evaluate the following elisp expression in emacs, it will +colorize a ``ChunkString`` when you use an interactive python shell +with emacs or xemacs ("C-c !"):: + + (let () + (defconst comint-mode-font-lock-keywords + '(("<[^>]+>" 0 'font-lock-reference-face) + ("[{}]" 0 'font-lock-function-name-face))) + (add-hook 'comint-mode-hook (lambda () (turn-on-font-lock)))) + +You can evaluate this code by copying it to a temporary buffer, +placing the cursor after the last close parenthesis, and typing +"``C-x C-e``". You should evaluate it before running the interactive +session. The change will last until you close emacs. + +Unresolved Issues +----------------- + +If we use the ``re`` module for regular expressions, Python's +regular expression engine generates "maximum recursion depth +exceeded" errors when processing very large texts, even for +regular expressions that should not require any recursion. We +therefore use the ``pre`` module instead. But note that ``pre`` +does not include Unicode support, so this module will not work +with unicode strings. Note also that ``pre`` regular expressions +are not quite as advanced as ``re`` ones (e.g., no leftward +zero-length assertions). + +:type CHUNK_TAG_PATTERN: regexp +:var CHUNK_TAG_PATTERN: A regular expression to test whether a tag + pattern is valid. +""" + +from nltk.chunk.api import ChunkParserI +from nltk.chunk.regexp import RegexpChunkParser, RegexpParser +from nltk.chunk.util import ( + ChunkScore, + accuracy, + conllstr2tree, + conlltags2tree, + ieerstr2tree, + tagstr2tree, + tree2conllstr, + tree2conlltags, +) +from nltk.data import load + +# Standard treebank POS tagger +_BINARY_NE_CHUNKER = "chunkers/maxent_ne_chunker/english_ace_binary.pickle" +_MULTICLASS_NE_CHUNKER = "chunkers/maxent_ne_chunker/english_ace_multiclass.pickle" + + +def ne_chunk(tagged_tokens, binary=False): + """ + Use NLTK's currently recommended named entity chunker to + chunk the given list of tagged tokens. + """ + if binary: + chunker_pickle = _BINARY_NE_CHUNKER + else: + chunker_pickle = _MULTICLASS_NE_CHUNKER + chunker = load(chunker_pickle) + return chunker.parse(tagged_tokens) + + +def ne_chunk_sents(tagged_sentences, binary=False): + """ + Use NLTK's currently recommended named entity chunker to chunk the + given list of tagged sentences, each consisting of a list of tagged tokens. + """ + if binary: + chunker_pickle = _BINARY_NE_CHUNKER + else: + chunker_pickle = _MULTICLASS_NE_CHUNKER + chunker = load(chunker_pickle) + return chunker.parse_sents(tagged_sentences) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f4d9e54 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..c51086c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/named_entity.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/named_entity.cpython-310.pyc new file mode 100644 index 0000000..59ff932 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/named_entity.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/regexp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/regexp.cpython-310.pyc new file mode 100644 index 0000000..3611d6a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/regexp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..c436cde Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/api.py new file mode 100644 index 0000000..1b85609 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/api.py @@ -0,0 +1,56 @@ +# Natural Language Toolkit: Chunk parsing API +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT + +##////////////////////////////////////////////////////// +## Chunk Parser Interface +##////////////////////////////////////////////////////// + +from nltk.chunk.util import ChunkScore +from nltk.internals import deprecated +from nltk.parse import ParserI + + +class ChunkParserI(ParserI): + """ + A processing interface for identifying non-overlapping groups in + unrestricted text. Typically, chunk parsers are used to find base + syntactic constituents, such as base noun phrases. Unlike + ``ParserI``, ``ChunkParserI`` guarantees that the ``parse()`` method + will always generate a parse. + """ + + def parse(self, tokens): + """ + Return the best chunk structure for the given tokens + and return a tree. + + :param tokens: The list of (word, tag) tokens to be chunked. + :type tokens: list(tuple) + :rtype: Tree + """ + raise NotImplementedError() + + @deprecated("Use accuracy(gold) instead.") + def evaluate(self, gold): + return self.accuracy(gold) + + def accuracy(self, gold): + """ + Score the accuracy of the chunker against the gold standard. + Remove the chunking the gold standard text, rechunk it using + the chunker, and return a ``ChunkScore`` object + reflecting the performance of this chunk parser. + + :type gold: list(Tree) + :param gold: The list of chunked sentences to score the chunker on. + :rtype: ChunkScore + """ + chunkscore = ChunkScore() + for correct in gold: + chunkscore.score(correct, self.parse(correct.leaves())) + return chunkscore diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/named_entity.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/named_entity.py new file mode 100644 index 0000000..da0f52d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/named_entity.py @@ -0,0 +1,352 @@ +# Natural Language Toolkit: Chunk parsing API +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Named entity chunker +""" + +import os +import pickle +import re +from xml.etree import ElementTree as ET + +from nltk.tag import ClassifierBasedTagger, pos_tag + +try: + from nltk.classify import MaxentClassifier +except ImportError: + pass + +from nltk.chunk.api import ChunkParserI +from nltk.chunk.util import ChunkScore +from nltk.data import find +from nltk.tokenize import word_tokenize +from nltk.tree import Tree + + +class NEChunkParserTagger(ClassifierBasedTagger): + """ + The IOB tagger used by the chunk parser. + """ + + def __init__(self, train): + ClassifierBasedTagger.__init__( + self, train=train, classifier_builder=self._classifier_builder + ) + + def _classifier_builder(self, train): + return MaxentClassifier.train( + train, algorithm="megam", gaussian_prior_sigma=1, trace=2 + ) + + def _english_wordlist(self): + try: + wl = self._en_wordlist + except AttributeError: + from nltk.corpus import words + + self._en_wordlist = set(words.words("en-basic")) + wl = self._en_wordlist + return wl + + def _feature_detector(self, tokens, index, history): + word = tokens[index][0] + pos = simplify_pos(tokens[index][1]) + if index == 0: + prevword = prevprevword = None + prevpos = prevprevpos = None + prevshape = prevtag = prevprevtag = None + elif index == 1: + prevword = tokens[index - 1][0].lower() + prevprevword = None + prevpos = simplify_pos(tokens[index - 1][1]) + prevprevpos = None + prevtag = history[index - 1][0] + prevshape = prevprevtag = None + else: + prevword = tokens[index - 1][0].lower() + prevprevword = tokens[index - 2][0].lower() + prevpos = simplify_pos(tokens[index - 1][1]) + prevprevpos = simplify_pos(tokens[index - 2][1]) + prevtag = history[index - 1] + prevprevtag = history[index - 2] + prevshape = shape(prevword) + if index == len(tokens) - 1: + nextword = nextnextword = None + nextpos = nextnextpos = None + elif index == len(tokens) - 2: + nextword = tokens[index + 1][0].lower() + nextpos = tokens[index + 1][1].lower() + nextnextword = None + nextnextpos = None + else: + nextword = tokens[index + 1][0].lower() + nextpos = tokens[index + 1][1].lower() + nextnextword = tokens[index + 2][0].lower() + nextnextpos = tokens[index + 2][1].lower() + + # 89.6 + features = { + "bias": True, + "shape": shape(word), + "wordlen": len(word), + "prefix3": word[:3].lower(), + "suffix3": word[-3:].lower(), + "pos": pos, + "word": word, + "en-wordlist": (word in self._english_wordlist()), + "prevtag": prevtag, + "prevpos": prevpos, + "nextpos": nextpos, + "prevword": prevword, + "nextword": nextword, + "word+nextpos": f"{word.lower()}+{nextpos}", + "pos+prevtag": f"{pos}+{prevtag}", + "shape+prevtag": f"{prevshape}+{prevtag}", + } + + return features + + +class NEChunkParser(ChunkParserI): + """ + Expected input: list of pos-tagged words + """ + + def __init__(self, train): + self._train(train) + + def parse(self, tokens): + """ + Each token should be a pos-tagged word + """ + tagged = self._tagger.tag(tokens) + tree = self._tagged_to_parse(tagged) + return tree + + def _train(self, corpus): + # Convert to tagged sequence + corpus = [self._parse_to_tagged(s) for s in corpus] + + self._tagger = NEChunkParserTagger(train=corpus) + + def _tagged_to_parse(self, tagged_tokens): + """ + Convert a list of tagged tokens to a chunk-parse tree. + """ + sent = Tree("S", []) + + for (tok, tag) in tagged_tokens: + if tag == "O": + sent.append(tok) + elif tag.startswith("B-"): + sent.append(Tree(tag[2:], [tok])) + elif tag.startswith("I-"): + if sent and isinstance(sent[-1], Tree) and sent[-1].label() == tag[2:]: + sent[-1].append(tok) + else: + sent.append(Tree(tag[2:], [tok])) + return sent + + @staticmethod + def _parse_to_tagged(sent): + """ + Convert a chunk-parse tree to a list of tagged tokens. + """ + toks = [] + for child in sent: + if isinstance(child, Tree): + if len(child) == 0: + print("Warning -- empty chunk in sentence") + continue + toks.append((child[0], f"B-{child.label()}")) + for tok in child[1:]: + toks.append((tok, f"I-{child.label()}")) + else: + toks.append((child, "O")) + return toks + + +def shape(word): + if re.match(r"[0-9]+(\.[0-9]*)?|[0-9]*\.[0-9]+$", word, re.UNICODE): + return "number" + elif re.match(r"\W+$", word, re.UNICODE): + return "punct" + elif re.match(r"\w+$", word, re.UNICODE): + if word.istitle(): + return "upcase" + elif word.islower(): + return "downcase" + else: + return "mixedcase" + else: + return "other" + + +def simplify_pos(s): + if s.startswith("V"): + return "V" + else: + return s.split("-")[0] + + +def postag_tree(tree): + # Part-of-speech tagging. + words = tree.leaves() + tag_iter = (pos for (word, pos) in pos_tag(words)) + newtree = Tree("S", []) + for child in tree: + if isinstance(child, Tree): + newtree.append(Tree(child.label(), [])) + for subchild in child: + newtree[-1].append((subchild, next(tag_iter))) + else: + newtree.append((child, next(tag_iter))) + return newtree + + +def load_ace_data(roots, fmt="binary", skip_bnews=True): + for root in roots: + for root, dirs, files in os.walk(root): + if root.endswith("bnews") and skip_bnews: + continue + for f in files: + if f.endswith(".sgm"): + yield from load_ace_file(os.path.join(root, f), fmt) + + +def load_ace_file(textfile, fmt): + print(f" - {os.path.split(textfile)[1]}") + annfile = textfile + ".tmx.rdc.xml" + + # Read the xml file, and get a list of entities + entities = [] + with open(annfile) as infile: + xml = ET.parse(infile).getroot() + for entity in xml.findall("document/entity"): + typ = entity.find("entity_type").text + for mention in entity.findall("entity_mention"): + if mention.get("TYPE") != "NAME": + continue # only NEs + s = int(mention.find("head/charseq/start").text) + e = int(mention.find("head/charseq/end").text) + 1 + entities.append((s, e, typ)) + + # Read the text file, and mark the entities. + with open(textfile) as infile: + text = infile.read() + + # Strip XML tags, since they don't count towards the indices + text = re.sub("<(?!/?TEXT)[^>]+>", "", text) + + # Blank out anything before/after + def subfunc(m): + return " " * (m.end() - m.start() - 6) + + text = re.sub(r"[\s\S]*", subfunc, text) + text = re.sub(r"[\s\S]*", "", text) + + # Simplify quotes + text = re.sub("``", ' "', text) + text = re.sub("''", '" ', text) + + entity_types = {typ for (s, e, typ) in entities} + + # Binary distinction (NE or not NE) + if fmt == "binary": + i = 0 + toks = Tree("S", []) + for (s, e, typ) in sorted(entities): + if s < i: + s = i # Overlapping! Deal with this better? + if e <= s: + continue + toks.extend(word_tokenize(text[i:s])) + toks.append(Tree("NE", text[s:e].split())) + i = e + toks.extend(word_tokenize(text[i:])) + yield toks + + # Multiclass distinction (NE type) + elif fmt == "multiclass": + i = 0 + toks = Tree("S", []) + for (s, e, typ) in sorted(entities): + if s < i: + s = i # Overlapping! Deal with this better? + if e <= s: + continue + toks.extend(word_tokenize(text[i:s])) + toks.append(Tree(typ, text[s:e].split())) + i = e + toks.extend(word_tokenize(text[i:])) + yield toks + + else: + raise ValueError("bad fmt value") + + +# This probably belongs in a more general-purpose location (as does +# the parse_to_tagged function). +def cmp_chunks(correct, guessed): + correct = NEChunkParser._parse_to_tagged(correct) + guessed = NEChunkParser._parse_to_tagged(guessed) + ellipsis = False + for (w, ct), (w, gt) in zip(correct, guessed): + if ct == gt == "O": + if not ellipsis: + print(f" {ct:15} {gt:15} {w}") + print(" {:15} {:15} {2}".format("...", "...", "...")) + ellipsis = True + else: + ellipsis = False + print(f" {ct:15} {gt:15} {w}") + + +def build_model(fmt="binary"): + print("Loading training data...") + train_paths = [ + find("corpora/ace_data/ace.dev"), + find("corpora/ace_data/ace.heldout"), + find("corpora/ace_data/bbn.dev"), + find("corpora/ace_data/muc.dev"), + ] + train_trees = load_ace_data(train_paths, fmt) + train_data = [postag_tree(t) for t in train_trees] + print("Training...") + cp = NEChunkParser(train_data) + del train_data + + print("Loading eval data...") + eval_paths = [find("corpora/ace_data/ace.eval")] + eval_trees = load_ace_data(eval_paths, fmt) + eval_data = [postag_tree(t) for t in eval_trees] + + print("Evaluating...") + chunkscore = ChunkScore() + for i, correct in enumerate(eval_data): + guess = cp.parse(correct.leaves()) + chunkscore.score(correct, guess) + if i < 3: + cmp_chunks(correct, guess) + print(chunkscore) + + outfilename = f"/tmp/ne_chunker_{fmt}.pickle" + print(f"Saving chunker to {outfilename}...") + + with open(outfilename, "wb") as outfile: + pickle.dump(cp, outfile, -1) + + return cp + + +if __name__ == "__main__": + # Make sure that the pickled object has the right class name: + from nltk.chunk.named_entity import build_model + + build_model("binary") + build_model("multiclass") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/regexp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/regexp.py new file mode 100644 index 0000000..09f2ed6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/regexp.py @@ -0,0 +1,1475 @@ +# Natural Language Toolkit: Regular Expression Chunkers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT + +import re + +import regex + +from nltk.chunk.api import ChunkParserI +from nltk.tree import Tree + +# ////////////////////////////////////////////////////// +# ChunkString +# ////////////////////////////////////////////////////// + + +class ChunkString: + """ + A string-based encoding of a particular chunking of a text. + Internally, the ``ChunkString`` class uses a single string to + encode the chunking of the input text. This string contains a + sequence of angle-bracket delimited tags, with chunking indicated + by braces. An example of this encoding is:: + + {
    }{
    }<.>{
    }<.> + + ``ChunkString`` are created from tagged texts (i.e., lists of + ``tokens`` whose type is ``TaggedType``). Initially, nothing is + chunked. + + The chunking of a ``ChunkString`` can be modified with the ``xform()`` + method, which uses a regular expression to transform the string + representation. These transformations should only add and remove + braces; they should *not* modify the sequence of angle-bracket + delimited tags. + + :type _str: str + :ivar _str: The internal string representation of the text's + encoding. This string representation contains a sequence of + angle-bracket delimited tags, with chunking indicated by + braces. An example of this encoding is:: + + {
    }{
    }<.>{
    }<.> + + :type _pieces: list(tagged tokens and chunks) + :ivar _pieces: The tagged tokens and chunks encoded by this ``ChunkString``. + :ivar _debug: The debug level. See the constructor docs. + + :cvar IN_CHUNK_PATTERN: A zero-width regexp pattern string that + will only match positions that are in chunks. + :cvar IN_STRIP_PATTERN: A zero-width regexp pattern string that + will only match positions that are in strips. + """ + + CHUNK_TAG_CHAR = r"[^\{\}<>]" + CHUNK_TAG = r"(<%s+?>)" % CHUNK_TAG_CHAR + + IN_CHUNK_PATTERN = r"(?=[^\{]*\})" + IN_STRIP_PATTERN = r"(?=[^\}]*(\{|$))" + + # These are used by _verify + _CHUNK = r"(\{%s+?\})+?" % CHUNK_TAG + _STRIP = r"(%s+?)+?" % CHUNK_TAG + _VALID = re.compile(r"^(\{?%s\}?)*?$" % CHUNK_TAG) + _BRACKETS = re.compile(r"[^\{\}]+") + _BALANCED_BRACKETS = re.compile(r"(\{\})*$") + + def __init__(self, chunk_struct, debug_level=1): + """ + Construct a new ``ChunkString`` that encodes the chunking of + the text ``tagged_tokens``. + + :type chunk_struct: Tree + :param chunk_struct: The chunk structure to be further chunked. + :type debug_level: int + :param debug_level: The level of debugging which should be + applied to transformations on the ``ChunkString``. The + valid levels are: + + - 0: no checks + - 1: full check on to_chunkstruct + - 2: full check on to_chunkstruct and cursory check after + each transformation. + - 3: full check on to_chunkstruct and full check after + each transformation. + + We recommend you use at least level 1. You should + probably use level 3 if you use any non-standard + subclasses of ``RegexpChunkRule``. + """ + self._root_label = chunk_struct.label() + self._pieces = chunk_struct[:] + tags = [self._tag(tok) for tok in self._pieces] + self._str = "<" + "><".join(tags) + ">" + self._debug = debug_level + + def _tag(self, tok): + if isinstance(tok, tuple): + return tok[1] + elif isinstance(tok, Tree): + return tok.label() + else: + raise ValueError("chunk structures must contain tagged " "tokens or trees") + + def _verify(self, s, verify_tags): + """ + Check to make sure that ``s`` still corresponds to some chunked + version of ``_pieces``. + + :type verify_tags: bool + :param verify_tags: Whether the individual tags should be + checked. If this is false, ``_verify`` will check to make + sure that ``_str`` encodes a chunked version of *some* + list of tokens. If this is true, then ``_verify`` will + check to make sure that the tags in ``_str`` match those in + ``_pieces``. + + :raise ValueError: if the internal string representation of + this ``ChunkString`` is invalid or not consistent with _pieces. + """ + # Check overall form + if not ChunkString._VALID.match(s): + raise ValueError( + "Transformation generated invalid " "chunkstring:\n %s" % s + ) + + # Check that parens are balanced. If the string is long, we + # have to do this in pieces, to avoid a maximum recursion + # depth limit for regular expressions. + brackets = ChunkString._BRACKETS.sub("", s) + for i in range(1 + len(brackets) // 5000): + substr = brackets[i * 5000 : i * 5000 + 5000] + if not ChunkString._BALANCED_BRACKETS.match(substr): + raise ValueError( + "Transformation generated invalid " "chunkstring:\n %s" % s + ) + + if verify_tags <= 0: + return + + tags1 = (re.split(r"[\{\}<>]+", s))[1:-1] + tags2 = [self._tag(piece) for piece in self._pieces] + if tags1 != tags2: + raise ValueError( + "Transformation generated invalid " "chunkstring: tag changed" + ) + + def to_chunkstruct(self, chunk_label="CHUNK"): + """ + Return the chunk structure encoded by this ``ChunkString``. + + :rtype: Tree + :raise ValueError: If a transformation has generated an + invalid chunkstring. + """ + if self._debug > 0: + self._verify(self._str, 1) + + # Use this alternating list to create the chunkstruct. + pieces = [] + index = 0 + piece_in_chunk = 0 + for piece in re.split("[{}]", self._str): + + # Find the list of tokens contained in this piece. + length = piece.count("<") + subsequence = self._pieces[index : index + length] + + # Add this list of tokens to our pieces. + if piece_in_chunk: + pieces.append(Tree(chunk_label, subsequence)) + else: + pieces += subsequence + + # Update index, piece_in_chunk + index += length + piece_in_chunk = not piece_in_chunk + + return Tree(self._root_label, pieces) + + def xform(self, regexp, repl): + """ + Apply the given transformation to the string encoding of this + ``ChunkString``. In particular, find all occurrences that match + ``regexp``, and replace them using ``repl`` (as done by + ``re.sub``). + + This transformation should only add and remove braces; it + should *not* modify the sequence of angle-bracket delimited + tags. Furthermore, this transformation may not result in + improper bracketing. Note, in particular, that bracketing may + not be nested. + + :type regexp: str or regexp + :param regexp: A regular expression matching the substring + that should be replaced. This will typically include a + named group, which can be used by ``repl``. + :type repl: str + :param repl: An expression specifying what should replace the + matched substring. Typically, this will include a named + replacement group, specified by ``regexp``. + :rtype: None + :raise ValueError: If this transformation generated an + invalid chunkstring. + """ + # Do the actual substitution + s = re.sub(regexp, repl, self._str) + + # The substitution might have generated "empty chunks" + # (substrings of the form "{}"). Remove them, so they don't + # interfere with other transformations. + s = re.sub(r"\{\}", "", s) + + # Make sure that the transformation was legal. + if self._debug > 1: + self._verify(s, self._debug - 2) + + # Commit the transformation. + self._str = s + + def __repr__(self): + """ + Return a string representation of this ``ChunkString``. + It has the form:: + + }{
    }'> + + :rtype: str + """ + return "" % repr(self._str) + + def __str__(self): + """ + Return a formatted representation of this ``ChunkString``. + This representation will include extra spaces to ensure that + tags will line up with the representation of other + ``ChunkStrings`` for the same text, regardless of the chunking. + + :rtype: str + """ + # Add spaces to make everything line up. + str = re.sub(r">(?!\})", r"> ", self._str) + str = re.sub(r"([^\{])<", r"\1 <", str) + if str[0] == "<": + str = " " + str + return str + + +# ////////////////////////////////////////////////////// +# Chunking Rules +# ////////////////////////////////////////////////////// + + +class RegexpChunkRule: + """ + A rule specifying how to modify the chunking in a ``ChunkString``, + using a transformational regular expression. The + ``RegexpChunkRule`` class itself can be used to implement any + transformational rule based on regular expressions. There are + also a number of subclasses, which can be used to implement + simpler types of rules, based on matching regular expressions. + + Each ``RegexpChunkRule`` has a regular expression and a + replacement expression. When a ``RegexpChunkRule`` is "applied" + to a ``ChunkString``, it searches the ``ChunkString`` for any + substring that matches the regular expression, and replaces it + using the replacement expression. This search/replace operation + has the same semantics as ``re.sub``. + + Each ``RegexpChunkRule`` also has a description string, which + gives a short (typically less than 75 characters) description of + the purpose of the rule. + + This transformation defined by this ``RegexpChunkRule`` should + only add and remove braces; it should *not* modify the sequence + of angle-bracket delimited tags. Furthermore, this transformation + may not result in nested or mismatched bracketing. + """ + + def __init__(self, regexp, repl, descr): + """ + Construct a new RegexpChunkRule. + + :type regexp: regexp or str + :param regexp: The regular expression for this ``RegexpChunkRule``. + When this rule is applied to a ``ChunkString``, any + substring that matches ``regexp`` will be replaced using + the replacement string ``repl``. Note that this must be a + normal regular expression, not a tag pattern. + :type repl: str + :param repl: The replacement expression for this ``RegexpChunkRule``. + When this rule is applied to a ``ChunkString``, any substring + that matches ``regexp`` will be replaced using ``repl``. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + if isinstance(regexp, str): + regexp = re.compile(regexp) + self._repl = repl + self._descr = descr + self._regexp = regexp + + def apply(self, chunkstr): + # Keep docstring generic so we can inherit it. + """ + Apply this rule to the given ``ChunkString``. See the + class reference documentation for a description of what it + means to apply a rule. + + :type chunkstr: ChunkString + :param chunkstr: The chunkstring to which this rule is applied. + :rtype: None + :raise ValueError: If this transformation generated an + invalid chunkstring. + """ + chunkstr.xform(self._regexp, self._repl) + + def descr(self): + """ + Return a short description of the purpose and/or effect of + this rule. + + :rtype: str + """ + return self._descr + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + }'->''> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return ( + "" + + repr(self._repl) + + ">" + ) + + @staticmethod + def fromstring(s): + """ + Create a RegexpChunkRule from a string description. + Currently, the following formats are supported:: + + {regexp} # chunk rule + }regexp{ # strip rule + regexp}{regexp # split rule + regexp{}regexp # merge rule + + Where ``regexp`` is a regular expression for the rule. Any + text following the comment marker (``#``) will be used as + the rule's description: + + >>> from nltk.chunk.regexp import RegexpChunkRule + >>> RegexpChunkRule.fromstring('{
    ?+}') + ?+'> + """ + # Split off the comment (but don't split on '\#') + m = re.match(r"(?P(\\.|[^#])*)(?P#.*)?", s) + rule = m.group("rule").strip() + comment = (m.group("comment") or "")[1:].strip() + + # Pattern bodies: chunk, strip, split, merge + try: + if not rule: + raise ValueError("Empty chunk pattern") + if rule[0] == "{" and rule[-1] == "}": + return ChunkRule(rule[1:-1], comment) + elif rule[0] == "}" and rule[-1] == "{": + return StripRule(rule[1:-1], comment) + elif "}{" in rule: + left, right = rule.split("}{") + return SplitRule(left, right, comment) + elif "{}" in rule: + left, right = rule.split("{}") + return MergeRule(left, right, comment) + elif re.match("[^{}]*{[^{}]*}[^{}]*", rule): + left, chunk, right = re.split("[{}]", rule) + return ChunkRuleWithContext(left, chunk, right, comment) + else: + raise ValueError("Illegal chunk pattern: %s" % rule) + except (ValueError, re.error) as e: + raise ValueError("Illegal chunk pattern: %s" % rule) from e + + +class ChunkRule(RegexpChunkRule): + """ + A rule specifying how to add chunks to a ``ChunkString``, using a + matching tag pattern. When applied to a ``ChunkString``, it will + find any substring that matches this tag pattern and that is not + already part of a chunk, and create a new chunk containing that + substring. + """ + + def __init__(self, tag_pattern, descr): + """ + Construct a new ``ChunkRule``. + + :type tag_pattern: str + :param tag_pattern: This rule's tag pattern. When + applied to a ``ChunkString``, this rule will + chunk any substring that matches this tag pattern and that + is not already part of a chunk. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + self._pattern = tag_pattern + regexp = re.compile( + "(?P%s)%s" + % (tag_pattern2re_pattern(tag_pattern), ChunkString.IN_STRIP_PATTERN) + ) + RegexpChunkRule.__init__(self, regexp, r"{\g}", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + '> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return "" + + +class StripRule(RegexpChunkRule): + """ + A rule specifying how to remove strips to a ``ChunkString``, + using a matching tag pattern. When applied to a + ``ChunkString``, it will find any substring that matches this + tag pattern and that is contained in a chunk, and remove it + from that chunk, thus creating two new chunks. + """ + + def __init__(self, tag_pattern, descr): + """ + Construct a new ``StripRule``. + + :type tag_pattern: str + :param tag_pattern: This rule's tag pattern. When + applied to a ``ChunkString``, this rule will + find any substring that matches this tag pattern and that + is contained in a chunk, and remove it from that chunk, + thus creating two new chunks. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + self._pattern = tag_pattern + regexp = re.compile( + "(?P%s)%s" + % (tag_pattern2re_pattern(tag_pattern), ChunkString.IN_CHUNK_PATTERN) + ) + RegexpChunkRule.__init__(self, regexp, r"}\g{", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + '> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return "" + + +class UnChunkRule(RegexpChunkRule): + """ + A rule specifying how to remove chunks to a ``ChunkString``, + using a matching tag pattern. When applied to a + ``ChunkString``, it will find any complete chunk that matches this + tag pattern, and un-chunk it. + """ + + def __init__(self, tag_pattern, descr): + """ + Construct a new ``UnChunkRule``. + + :type tag_pattern: str + :param tag_pattern: This rule's tag pattern. When + applied to a ``ChunkString``, this rule will + find any complete chunk that matches this tag pattern, + and un-chunk it. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + self._pattern = tag_pattern + regexp = re.compile(r"\{(?P%s)\}" % tag_pattern2re_pattern(tag_pattern)) + RegexpChunkRule.__init__(self, regexp, r"\g", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + '> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return "" + + +class MergeRule(RegexpChunkRule): + """ + A rule specifying how to merge chunks in a ``ChunkString``, using + two matching tag patterns: a left pattern, and a right pattern. + When applied to a ``ChunkString``, it will find any chunk whose end + matches left pattern, and immediately followed by a chunk whose + beginning matches right pattern. It will then merge those two + chunks into a single chunk. + """ + + def __init__(self, left_tag_pattern, right_tag_pattern, descr): + """ + Construct a new ``MergeRule``. + + :type right_tag_pattern: str + :param right_tag_pattern: This rule's right tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose end matches + ``left_tag_pattern``, and immediately followed by a chunk + whose beginning matches this pattern. It will + then merge those two chunks into a single chunk. + :type left_tag_pattern: str + :param left_tag_pattern: This rule's left tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose end matches + this pattern, and immediately followed by a chunk + whose beginning matches ``right_tag_pattern``. It will + then merge those two chunks into a single chunk. + + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + # Ensure that the individual patterns are coherent. E.g., if + # left='(' and right=')', then this will raise an exception: + re.compile(tag_pattern2re_pattern(left_tag_pattern)) + re.compile(tag_pattern2re_pattern(right_tag_pattern)) + + self._left_tag_pattern = left_tag_pattern + self._right_tag_pattern = right_tag_pattern + regexp = re.compile( + "(?P%s)}{(?=%s)" + % ( + tag_pattern2re_pattern(left_tag_pattern), + tag_pattern2re_pattern(right_tag_pattern), + ) + ) + RegexpChunkRule.__init__(self, regexp, r"\g", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + ', ''> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return ( + "" + ) + + +class SplitRule(RegexpChunkRule): + """ + A rule specifying how to split chunks in a ``ChunkString``, using + two matching tag patterns: a left pattern, and a right pattern. + When applied to a ``ChunkString``, it will find any chunk that + matches the left pattern followed by the right pattern. It will + then split the chunk into two new chunks, at the point between the + two pattern matches. + """ + + def __init__(self, left_tag_pattern, right_tag_pattern, descr): + """ + Construct a new ``SplitRule``. + + :type right_tag_pattern: str + :param right_tag_pattern: This rule's right tag + pattern. When applied to a ``ChunkString``, this rule will + find any chunk containing a substring that matches + ``left_tag_pattern`` followed by this pattern. It will + then split the chunk into two new chunks at the point + between these two matching patterns. + :type left_tag_pattern: str + :param left_tag_pattern: This rule's left tag + pattern. When applied to a ``ChunkString``, this rule will + find any chunk containing a substring that matches this + pattern followed by ``right_tag_pattern``. It will then + split the chunk into two new chunks at the point between + these two matching patterns. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + # Ensure that the individual patterns are coherent. E.g., if + # left='(' and right=')', then this will raise an exception: + re.compile(tag_pattern2re_pattern(left_tag_pattern)) + re.compile(tag_pattern2re_pattern(right_tag_pattern)) + + self._left_tag_pattern = left_tag_pattern + self._right_tag_pattern = right_tag_pattern + regexp = re.compile( + "(?P%s)(?=%s)" + % ( + tag_pattern2re_pattern(left_tag_pattern), + tag_pattern2re_pattern(right_tag_pattern), + ) + ) + RegexpChunkRule.__init__(self, regexp, r"\g}{", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + ', '
    '> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return ( + "" + ) + + +class ExpandLeftRule(RegexpChunkRule): + """ + A rule specifying how to expand chunks in a ``ChunkString`` to the left, + using two matching tag patterns: a left pattern, and a right pattern. + When applied to a ``ChunkString``, it will find any chunk whose beginning + matches right pattern, and immediately preceded by a strip whose + end matches left pattern. It will then expand the chunk to incorporate + the new material on the left. + """ + + def __init__(self, left_tag_pattern, right_tag_pattern, descr): + """ + Construct a new ``ExpandRightRule``. + + :type right_tag_pattern: str + :param right_tag_pattern: This rule's right tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose beginning matches + ``right_tag_pattern``, and immediately preceded by a strip + whose end matches this pattern. It will + then merge those two chunks into a single chunk. + :type left_tag_pattern: str + :param left_tag_pattern: This rule's left tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose beginning matches + this pattern, and immediately preceded by a strip + whose end matches ``left_tag_pattern``. It will + then expand the chunk to incorporate the new material on the left. + + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + # Ensure that the individual patterns are coherent. E.g., if + # left='(' and right=')', then this will raise an exception: + re.compile(tag_pattern2re_pattern(left_tag_pattern)) + re.compile(tag_pattern2re_pattern(right_tag_pattern)) + + self._left_tag_pattern = left_tag_pattern + self._right_tag_pattern = right_tag_pattern + regexp = re.compile( + r"(?P%s)\{(?P%s)" + % ( + tag_pattern2re_pattern(left_tag_pattern), + tag_pattern2re_pattern(right_tag_pattern), + ) + ) + RegexpChunkRule.__init__(self, regexp, r"{\g\g", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + ', ''> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return ( + "" + ) + + +class ExpandRightRule(RegexpChunkRule): + """ + A rule specifying how to expand chunks in a ``ChunkString`` to the + right, using two matching tag patterns: a left pattern, and a + right pattern. When applied to a ``ChunkString``, it will find any + chunk whose end matches left pattern, and immediately followed by + a strip whose beginning matches right pattern. It will then + expand the chunk to incorporate the new material on the right. + """ + + def __init__(self, left_tag_pattern, right_tag_pattern, descr): + """ + Construct a new ``ExpandRightRule``. + + :type right_tag_pattern: str + :param right_tag_pattern: This rule's right tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose end matches + ``left_tag_pattern``, and immediately followed by a strip + whose beginning matches this pattern. It will + then merge those two chunks into a single chunk. + :type left_tag_pattern: str + :param left_tag_pattern: This rule's left tag + pattern. When applied to a ``ChunkString``, this + rule will find any chunk whose end matches + this pattern, and immediately followed by a strip + whose beginning matches ``right_tag_pattern``. It will + then expand the chunk to incorporate the new material on the right. + + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + # Ensure that the individual patterns are coherent. E.g., if + # left='(' and right=')', then this will raise an exception: + re.compile(tag_pattern2re_pattern(left_tag_pattern)) + re.compile(tag_pattern2re_pattern(right_tag_pattern)) + + self._left_tag_pattern = left_tag_pattern + self._right_tag_pattern = right_tag_pattern + regexp = re.compile( + r"(?P%s)\}(?P%s)" + % ( + tag_pattern2re_pattern(left_tag_pattern), + tag_pattern2re_pattern(right_tag_pattern), + ) + ) + RegexpChunkRule.__init__(self, regexp, r"\g\g}", descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + ', ''> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return ( + "" + ) + + +class ChunkRuleWithContext(RegexpChunkRule): + """ + A rule specifying how to add chunks to a ``ChunkString``, using + three matching tag patterns: one for the left context, one for the + chunk, and one for the right context. When applied to a + ``ChunkString``, it will find any substring that matches the chunk + tag pattern, is surrounded by substrings that match the two + context patterns, and is not already part of a chunk; and create a + new chunk containing the substring that matched the chunk tag + pattern. + + Caveat: Both the left and right context are consumed when this + rule matches; therefore, if you need to find overlapping matches, + you will need to apply your rule more than once. + """ + + def __init__( + self, + left_context_tag_pattern, + chunk_tag_pattern, + right_context_tag_pattern, + descr, + ): + """ + Construct a new ``ChunkRuleWithContext``. + + :type left_context_tag_pattern: str + :param left_context_tag_pattern: A tag pattern that must match + the left context of ``chunk_tag_pattern`` for this rule to + apply. + :type chunk_tag_pattern: str + :param chunk_tag_pattern: A tag pattern that must match for this + rule to apply. If the rule does apply, then this pattern + also identifies the substring that will be made into a chunk. + :type right_context_tag_pattern: str + :param right_context_tag_pattern: A tag pattern that must match + the right context of ``chunk_tag_pattern`` for this rule to + apply. + :type descr: str + :param descr: A short description of the purpose and/or effect + of this rule. + """ + # Ensure that the individual patterns are coherent. E.g., if + # left='(' and right=')', then this will raise an exception: + re.compile(tag_pattern2re_pattern(left_context_tag_pattern)) + re.compile(tag_pattern2re_pattern(chunk_tag_pattern)) + re.compile(tag_pattern2re_pattern(right_context_tag_pattern)) + + self._left_context_tag_pattern = left_context_tag_pattern + self._chunk_tag_pattern = chunk_tag_pattern + self._right_context_tag_pattern = right_context_tag_pattern + regexp = re.compile( + "(?P%s)(?P%s)(?P%s)%s" + % ( + tag_pattern2re_pattern(left_context_tag_pattern), + tag_pattern2re_pattern(chunk_tag_pattern), + tag_pattern2re_pattern(right_context_tag_pattern), + ChunkString.IN_STRIP_PATTERN, + ) + ) + replacement = r"\g{\g}\g" + RegexpChunkRule.__init__(self, regexp, replacement, descr) + + def __repr__(self): + """ + Return a string representation of this rule. It has the form:: + + ', '', '
    '> + + Note that this representation does not include the + description string; that string can be accessed + separately with the ``descr()`` method. + + :rtype: str + """ + return "".format( + self._left_context_tag_pattern, + self._chunk_tag_pattern, + self._right_context_tag_pattern, + ) + + +# ////////////////////////////////////////////////////// +# Tag Pattern Format Conversion +# ////////////////////////////////////////////////////// + +# this should probably be made more strict than it is -- e.g., it +# currently accepts 'foo'. +CHUNK_TAG_PATTERN = re.compile( + r"^(({}|<{}>)*)$".format(r"([^\{\}<>]|\{\d+,?\}|\{\d*,\d+\})+", r"[^\{\}<>]+") +) + + +def tag_pattern2re_pattern(tag_pattern): + """ + Convert a tag pattern to a regular expression pattern. A "tag + pattern" is a modified version of a regular expression, designed + for matching sequences of tags. The differences between regular + expression patterns and tag patterns are: + + - In tag patterns, ``'<'`` and ``'>'`` act as parentheses; so + ``'+'`` matches one or more repetitions of ``''``, not + ``''``. + - Whitespace in tag patterns is ignored. So + ``'
    | '`` is equivalent to ``'
    |'`` + - In tag patterns, ``'.'`` is equivalent to ``'[^{}<>]'``; so + ``''`` matches any single tag starting with ``'NN'``. + + In particular, ``tag_pattern2re_pattern`` performs the following + transformations on the given pattern: + + - Replace '.' with '[^<>{}]' + - Remove any whitespace + - Add extra parens around '<' and '>', to make '<' and '>' act + like parentheses. E.g., so that in '+', the '+' has scope + over the entire ''; and so that in '', the '|' has + scope over 'NN' and 'IN', but not '<' or '>'. + - Check to make sure the resulting pattern is valid. + + :type tag_pattern: str + :param tag_pattern: The tag pattern to convert to a regular + expression pattern. + :raise ValueError: If ``tag_pattern`` is not a valid tag pattern. + In particular, ``tag_pattern`` should not include braces; and it + should not contain nested or mismatched angle-brackets. + :rtype: str + :return: A regular expression pattern corresponding to + ``tag_pattern``. + """ + # Clean up the regular expression + tag_pattern = re.sub(r"\s", "", tag_pattern) + tag_pattern = re.sub(r"<", "(<(", tag_pattern) + tag_pattern = re.sub(r">", ")>)", tag_pattern) + + # Check the regular expression + if not CHUNK_TAG_PATTERN.match(tag_pattern): + raise ValueError("Bad tag pattern: %r" % tag_pattern) + + # Replace "." with CHUNK_TAG_CHAR. + # We have to do this after, since it adds {}[]<>s, which would + # confuse CHUNK_TAG_PATTERN. + # PRE doesn't have lookback assertions, so reverse twice, and do + # the pattern backwards (with lookahead assertions). This can be + # made much cleaner once we can switch back to SRE. + def reverse_str(str): + lst = list(str) + lst.reverse() + return "".join(lst) + + tc_rev = reverse_str(ChunkString.CHUNK_TAG_CHAR) + reversed = reverse_str(tag_pattern) + reversed = re.sub(r"\.(?!\\(\\\\)*($|[^\\]))", tc_rev, reversed) + tag_pattern = reverse_str(reversed) + + return tag_pattern + + +# ////////////////////////////////////////////////////// +# RegexpChunkParser +# ////////////////////////////////////////////////////// + + +class RegexpChunkParser(ChunkParserI): + """ + A regular expression based chunk parser. ``RegexpChunkParser`` uses a + sequence of "rules" to find chunks of a single type within a + text. The chunking of the text is encoded using a ``ChunkString``, + and each rule acts by modifying the chunking in the + ``ChunkString``. The rules are all implemented using regular + expression matching and substitution. + + The ``RegexpChunkRule`` class and its subclasses (``ChunkRule``, + ``StripRule``, ``UnChunkRule``, ``MergeRule``, and ``SplitRule``) + define the rules that are used by ``RegexpChunkParser``. Each rule + defines an ``apply()`` method, which modifies the chunking encoded + by a given ``ChunkString``. + + :type _rules: list(RegexpChunkRule) + :ivar _rules: The list of rules that should be applied to a text. + :type _trace: int + :ivar _trace: The default level of tracing. + + """ + + def __init__(self, rules, chunk_label="NP", root_label="S", trace=0): + """ + Construct a new ``RegexpChunkParser``. + + :type rules: list(RegexpChunkRule) + :param rules: The sequence of rules that should be used to + generate the chunking for a tagged text. + :type chunk_label: str + :param chunk_label: The node value that should be used for + chunk subtrees. This is typically a short string + describing the type of information contained by the chunk, + such as ``"NP"`` for base noun phrases. + :type root_label: str + :param root_label: The node value that should be used for the + top node of the chunk structure. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + ``1`` will generate normal tracing output; and ``2`` or + higher will generate verbose tracing output. + """ + self._rules = rules + self._trace = trace + self._chunk_label = chunk_label + self._root_label = root_label + + def _trace_apply(self, chunkstr, verbose): + """ + Apply each rule of this ``RegexpChunkParser`` to ``chunkstr``, in + turn. Generate trace output between each rule. If ``verbose`` + is true, then generate verbose output. + + :type chunkstr: ChunkString + :param chunkstr: The chunk string to which each rule should be + applied. + :type verbose: bool + :param verbose: Whether output should be verbose. + :rtype: None + """ + print("# Input:") + print(chunkstr) + for rule in self._rules: + rule.apply(chunkstr) + if verbose: + print("#", rule.descr() + " (" + repr(rule) + "):") + else: + print("#", rule.descr() + ":") + print(chunkstr) + + def _notrace_apply(self, chunkstr): + """ + Apply each rule of this ``RegexpChunkParser`` to ``chunkstr``, in + turn. + + :param chunkstr: The chunk string to which each rule should be + applied. + :type chunkstr: ChunkString + :rtype: None + """ + + for rule in self._rules: + rule.apply(chunkstr) + + def parse(self, chunk_struct, trace=None): + """ + :type chunk_struct: Tree + :param chunk_struct: the chunk structure to be (further) chunked + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + ``1`` will generate normal tracing output; and ``2`` or + higher will generate verbose tracing output. This value + overrides the trace level value that was given to the + constructor. + :rtype: Tree + :return: a chunk structure that encodes the chunks in a given + tagged sentence. A chunk is a non-overlapping linguistic + group, such as a noun phrase. The set of chunks + identified in the chunk structure depends on the rules + used to define this ``RegexpChunkParser``. + """ + if len(chunk_struct) == 0: + print("Warning: parsing empty text") + return Tree(self._root_label, []) + + try: + chunk_struct.label() + except AttributeError: + chunk_struct = Tree(self._root_label, chunk_struct) + + # Use the default trace value? + if trace is None: + trace = self._trace + + chunkstr = ChunkString(chunk_struct) + + # Apply the sequence of rules to the chunkstring. + if trace: + verbose = trace > 1 + self._trace_apply(chunkstr, verbose) + else: + self._notrace_apply(chunkstr) + + # Use the chunkstring to create a chunk structure. + return chunkstr.to_chunkstruct(self._chunk_label) + + def rules(self): + """ + :return: the sequence of rules used by ``RegexpChunkParser``. + :rtype: list(RegexpChunkRule) + """ + return self._rules + + def __repr__(self): + """ + :return: a concise string representation of this + ``RegexpChunkParser``. + :rtype: str + """ + return "" % len(self._rules) + + def __str__(self): + """ + :return: a verbose string representation of this ``RegexpChunkParser``. + :rtype: str + """ + s = "RegexpChunkParser with %d rules:\n" % len(self._rules) + margin = 0 + for rule in self._rules: + margin = max(margin, len(rule.descr())) + if margin < 35: + format = " %" + repr(-(margin + 3)) + "s%s\n" + else: + format = " %s\n %s\n" + for rule in self._rules: + s += format % (rule.descr(), repr(rule)) + return s[:-1] + + +# ////////////////////////////////////////////////////// +# Chunk Grammar +# ////////////////////////////////////////////////////// + + +class RegexpParser(ChunkParserI): + r""" + A grammar based chunk parser. ``chunk.RegexpParser`` uses a set of + regular expression patterns to specify the behavior of the parser. + The chunking of the text is encoded using a ``ChunkString``, and + each rule acts by modifying the chunking in the ``ChunkString``. + The rules are all implemented using regular expression matching + and substitution. + + A grammar contains one or more clauses in the following form:: + + NP: + {} # chunk determiners and adjectives + }<[\.VI].*>+{ # strip any tag beginning with V, I, or . + <.*>}{
    # split a chunk at a determiner + {} # merge chunk ending with det/adj + # with one starting with a noun + + The patterns of a clause are executed in order. An earlier + pattern may introduce a chunk boundary that prevents a later + pattern from executing. Sometimes an individual pattern will + match on multiple, overlapping extents of the input. As with + regular expression substitution more generally, the chunker will + identify the first match possible, then continue looking for matches + after this one has ended. + + The clauses of a grammar are also executed in order. A cascaded + chunk parser is one having more than one clause. The maximum depth + of a parse tree created by this chunk parser is the same as the + number of clauses in the grammar. + + When tracing is turned on, the comment portion of a line is displayed + each time the corresponding pattern is applied. + + :type _start: str + :ivar _start: The start symbol of the grammar (the root node of + resulting trees) + :type _stages: int + :ivar _stages: The list of parsing stages corresponding to the grammar + + """ + + def __init__(self, grammar, root_label="S", loop=1, trace=0): + """ + Create a new chunk parser, from the given start state + and set of chunk patterns. + + :param grammar: The grammar, or a list of RegexpChunkParser objects + :type grammar: str or list(RegexpChunkParser) + :param root_label: The top node of the tree being created + :type root_label: str or Nonterminal + :param loop: The number of times to run through the patterns + :type loop: int + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + ``1`` will generate normal tracing output; and ``2`` or + higher will generate verbose tracing output. + """ + self._trace = trace + self._stages = [] + self._grammar = grammar + self._loop = loop + + if isinstance(grammar, str): + self._read_grammar(grammar, root_label, trace) + else: + # Make sur the grammar looks like it has the right type: + type_err = ( + "Expected string or list of RegexpChunkParsers " "for the grammar." + ) + try: + grammar = list(grammar) + except BaseException as e: + raise TypeError(type_err) from e + for elt in grammar: + if not isinstance(elt, RegexpChunkParser): + raise TypeError(type_err) + self._stages = grammar + + def _read_grammar(self, grammar, root_label, trace): + """ + Helper function for __init__: read the grammar if it is a + string. + """ + rules = [] + lhs = None + pattern = regex.compile("(?P(\\.|[^:])*)(:(?P.*))") + for line in grammar.split("\n"): + line = line.strip() + + # New stage begins if there's an unescaped ':' + m = pattern.match(line) + if m: + # Record the stage that we just completed. + self._add_stage(rules, lhs, root_label, trace) + # Start a new stage. + lhs = m.group("nonterminal").strip() + rules = [] + line = m.group("rule").strip() + + # Skip blank & comment-only lines + if line == "" or line.startswith("#"): + continue + + # Add the rule + rules.append(RegexpChunkRule.fromstring(line)) + + # Record the final stage + self._add_stage(rules, lhs, root_label, trace) + + def _add_stage(self, rules, lhs, root_label, trace): + """ + Helper function for __init__: add a new stage to the parser. + """ + if rules != []: + if not lhs: + raise ValueError("Expected stage marker (eg NP:)") + parser = RegexpChunkParser( + rules, chunk_label=lhs, root_label=root_label, trace=trace + ) + self._stages.append(parser) + + def parse(self, chunk_struct, trace=None): + """ + Apply the chunk parser to this input. + + :type chunk_struct: Tree + :param chunk_struct: the chunk structure to be (further) chunked + (this tree is modified, and is also returned) + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + ``1`` will generate normal tracing output; and ``2`` or + higher will generate verbose tracing output. This value + overrides the trace level value that was given to the + constructor. + :return: the chunked output. + :rtype: Tree + """ + if trace is None: + trace = self._trace + for i in range(self._loop): + for parser in self._stages: + chunk_struct = parser.parse(chunk_struct, trace=trace) + return chunk_struct + + def __repr__(self): + """ + :return: a concise string representation of this ``chunk.RegexpParser``. + :rtype: str + """ + return "" % len(self._stages) + + def __str__(self): + """ + :return: a verbose string representation of this + ``RegexpParser``. + :rtype: str + """ + s = "chunk.RegexpParser with %d stages:\n" % len(self._stages) + margin = 0 + for parser in self._stages: + s += "%s\n" % parser + return s[:-1] + + +# ////////////////////////////////////////////////////// +# Demonstration code +# ////////////////////////////////////////////////////// + + +def demo_eval(chunkparser, text): + """ + Demonstration code for evaluating a chunk parser, using a + ``ChunkScore``. This function assumes that ``text`` contains one + sentence per line, and that each sentence has the form expected by + ``tree.chunk``. It runs the given chunk parser on each sentence in + the text, and scores the result. It prints the final score + (precision, recall, and f-measure); and reports the set of chunks + that were missed and the set of chunks that were incorrect. (At + most 10 missing chunks and 10 incorrect chunks are reported). + + :param chunkparser: The chunkparser to be tested + :type chunkparser: ChunkParserI + :param text: The chunked tagged text that should be used for + evaluation. + :type text: str + """ + from nltk import chunk + from nltk.tree import Tree + + # Evaluate our chunk parser. + chunkscore = chunk.ChunkScore() + + for sentence in text.split("\n"): + print(sentence) + sentence = sentence.strip() + if not sentence: + continue + gold = chunk.tagstr2tree(sentence) + tokens = gold.leaves() + test = chunkparser.parse(Tree("S", tokens), trace=1) + chunkscore.score(gold, test) + print() + + print("/" + ("=" * 75) + "\\") + print("Scoring", chunkparser) + print("-" * 77) + print("Precision: %5.1f%%" % (chunkscore.precision() * 100), " " * 4, end=" ") + print("Recall: %5.1f%%" % (chunkscore.recall() * 100), " " * 6, end=" ") + print("F-Measure: %5.1f%%" % (chunkscore.f_measure() * 100)) + + # Missed chunks. + if chunkscore.missed(): + print("Missed:") + missed = chunkscore.missed() + for chunk in missed[:10]: + print(" ", " ".join(map(str, chunk))) + if len(chunkscore.missed()) > 10: + print(" ...") + + # Incorrect chunks. + if chunkscore.incorrect(): + print("Incorrect:") + incorrect = chunkscore.incorrect() + for chunk in incorrect[:10]: + print(" ", " ".join(map(str, chunk))) + if len(chunkscore.incorrect()) > 10: + print(" ...") + + print("\\" + ("=" * 75) + "/") + print() + + +def demo(): + """ + A demonstration for the ``RegexpChunkParser`` class. A single text is + parsed with four different chunk parsers, using a variety of rules + and strategies. + """ + + from nltk import Tree, chunk + + text = """\ + [ the/DT little/JJ cat/NN ] sat/VBD on/IN [ the/DT mat/NN ] ./. + [ John/NNP ] saw/VBD [the/DT cats/NNS] [the/DT dog/NN] chased/VBD ./. + [ John/NNP ] thinks/VBZ [ Mary/NN ] saw/VBD [ the/DT cat/NN ] sit/VB on/IN [ the/DT mat/NN ]./. + """ + + print("*" * 75) + print("Evaluation text:") + print(text) + print("*" * 75) + print() + + grammar = r""" + NP: # NP stage + {
    ?*} # chunk determiners, adjectives and nouns + {+} # chunk proper nouns + """ + cp = chunk.RegexpParser(grammar) + demo_eval(cp, text) + + grammar = r""" + NP: + {<.*>} # start by chunking each tag + }<[\.VI].*>+{ # unchunk any verbs, prepositions or periods + {} # merge det/adj with nouns + """ + cp = chunk.RegexpParser(grammar) + demo_eval(cp, text) + + grammar = r""" + NP: {
    ?*} # chunk determiners, adjectives and nouns + VP: {?} # VP = verb words + """ + cp = chunk.RegexpParser(grammar) + demo_eval(cp, text) + + grammar = r""" + NP: {<.*>*} # start by chunking everything + }<[\.VI].*>+{ # strip any verbs, prepositions or periods + <.*>}{
    # separate on determiners + PP: {} # PP = preposition + noun phrase + VP: {*} # VP = verb words + NPs and PPs + """ + cp = chunk.RegexpParser(grammar) + demo_eval(cp, text) + + # Evaluation + + from nltk.corpus import conll2000 + + print() + print("Demonstration of empty grammar:") + + cp = chunk.RegexpParser("") + print(chunk.accuracy(cp, conll2000.chunked_sents("test.txt", chunk_types=("NP",)))) + + print() + print("Demonstration of accuracy evaluation using CoNLL tags:") + + grammar = r""" + NP: + {<.*>} # start by chunking each tag + }<[\.VI].*>+{ # unchunk any verbs, prepositions or periods + {} # merge det/adj with nouns + """ + cp = chunk.RegexpParser(grammar) + print(chunk.accuracy(cp, conll2000.chunked_sents("test.txt")[:5])) + + print() + print("Demonstration of tagged token input") + + grammar = r""" + NP: {<.*>*} # start by chunking everything + }<[\.VI].*>+{ # strip any verbs, prepositions or periods + <.*>}{
    # separate on determiners + PP: {} # PP = preposition + noun phrase + VP: {*} # VP = verb words + NPs and PPs + """ + cp = chunk.RegexpParser(grammar) + print( + cp.parse( + [ + ("the", "DT"), + ("little", "JJ"), + ("cat", "NN"), + ("sat", "VBD"), + ("on", "IN"), + ("the", "DT"), + ("mat", "NN"), + (".", "."), + ] + ) + ) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/util.py new file mode 100644 index 0000000..d8c8fdb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/chunk/util.py @@ -0,0 +1,643 @@ +# Natural Language Toolkit: Chunk format conversions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT + +import re + +from nltk.metrics import accuracy as _accuracy +from nltk.tag.mapping import map_tag +from nltk.tag.util import str2tuple +from nltk.tree import Tree + +##////////////////////////////////////////////////////// +## EVALUATION +##////////////////////////////////////////////////////// + + +def accuracy(chunker, gold): + """ + Score the accuracy of the chunker against the gold standard. + Strip the chunk information from the gold standard and rechunk it using + the chunker, then compute the accuracy score. + + :type chunker: ChunkParserI + :param chunker: The chunker being evaluated. + :type gold: tree + :param gold: The chunk structures to score the chunker on. + :rtype: float + """ + + gold_tags = [] + test_tags = [] + for gold_tree in gold: + test_tree = chunker.parse(gold_tree.flatten()) + gold_tags += tree2conlltags(gold_tree) + test_tags += tree2conlltags(test_tree) + + # print 'GOLD:', gold_tags[:50] + # print 'TEST:', test_tags[:50] + return _accuracy(gold_tags, test_tags) + + +# Patched for increased performance by Yoav Goldberg , 2006-01-13 +# -- statistics are evaluated only on demand, instead of at every sentence evaluation +# +# SB: use nltk.metrics for precision/recall scoring? +# +class ChunkScore: + """ + A utility class for scoring chunk parsers. ``ChunkScore`` can + evaluate a chunk parser's output, based on a number of statistics + (precision, recall, f-measure, misssed chunks, incorrect chunks). + It can also combine the scores from the parsing of multiple texts; + this makes it significantly easier to evaluate a chunk parser that + operates one sentence at a time. + + Texts are evaluated with the ``score`` method. The results of + evaluation can be accessed via a number of accessor methods, such + as ``precision`` and ``f_measure``. A typical use of the + ``ChunkScore`` class is:: + + >>> chunkscore = ChunkScore() # doctest: +SKIP + >>> for correct in correct_sentences: # doctest: +SKIP + ... guess = chunkparser.parse(correct.leaves()) # doctest: +SKIP + ... chunkscore.score(correct, guess) # doctest: +SKIP + >>> print('F Measure:', chunkscore.f_measure()) # doctest: +SKIP + F Measure: 0.823 + + :ivar kwargs: Keyword arguments: + + - max_tp_examples: The maximum number actual examples of true + positives to record. This affects the ``correct`` member + function: ``correct`` will not return more than this number + of true positive examples. This does *not* affect any of + the numerical metrics (precision, recall, or f-measure) + + - max_fp_examples: The maximum number actual examples of false + positives to record. This affects the ``incorrect`` member + function and the ``guessed`` member function: ``incorrect`` + will not return more than this number of examples, and + ``guessed`` will not return more than this number of true + positive examples. This does *not* affect any of the + numerical metrics (precision, recall, or f-measure) + + - max_fn_examples: The maximum number actual examples of false + negatives to record. This affects the ``missed`` member + function and the ``correct`` member function: ``missed`` + will not return more than this number of examples, and + ``correct`` will not return more than this number of true + negative examples. This does *not* affect any of the + numerical metrics (precision, recall, or f-measure) + + - chunk_label: A regular expression indicating which chunks + should be compared. Defaults to ``'.*'`` (i.e., all chunks). + + :type _tp: list(Token) + :ivar _tp: List of true positives + :type _fp: list(Token) + :ivar _fp: List of false positives + :type _fn: list(Token) + :ivar _fn: List of false negatives + + :type _tp_num: int + :ivar _tp_num: Number of true positives + :type _fp_num: int + :ivar _fp_num: Number of false positives + :type _fn_num: int + :ivar _fn_num: Number of false negatives. + """ + + def __init__(self, **kwargs): + self._correct = set() + self._guessed = set() + self._tp = set() + self._fp = set() + self._fn = set() + self._max_tp = kwargs.get("max_tp_examples", 100) + self._max_fp = kwargs.get("max_fp_examples", 100) + self._max_fn = kwargs.get("max_fn_examples", 100) + self._chunk_label = kwargs.get("chunk_label", ".*") + self._tp_num = 0 + self._fp_num = 0 + self._fn_num = 0 + self._count = 0 + self._tags_correct = 0.0 + self._tags_total = 0.0 + + self._measuresNeedUpdate = False + + def _updateMeasures(self): + if self._measuresNeedUpdate: + self._tp = self._guessed & self._correct + self._fn = self._correct - self._guessed + self._fp = self._guessed - self._correct + self._tp_num = len(self._tp) + self._fp_num = len(self._fp) + self._fn_num = len(self._fn) + self._measuresNeedUpdate = False + + def score(self, correct, guessed): + """ + Given a correctly chunked sentence, score another chunked + version of the same sentence. + + :type correct: chunk structure + :param correct: The known-correct ("gold standard") chunked + sentence. + :type guessed: chunk structure + :param guessed: The chunked sentence to be scored. + """ + self._correct |= _chunksets(correct, self._count, self._chunk_label) + self._guessed |= _chunksets(guessed, self._count, self._chunk_label) + self._count += 1 + self._measuresNeedUpdate = True + # Keep track of per-tag accuracy (if possible) + try: + correct_tags = tree2conlltags(correct) + guessed_tags = tree2conlltags(guessed) + except ValueError: + # This exception case is for nested chunk structures, + # where tree2conlltags will fail with a ValueError: "Tree + # is too deeply nested to be printed in CoNLL format." + correct_tags = guessed_tags = () + self._tags_total += len(correct_tags) + self._tags_correct += sum( + 1 for (t, g) in zip(guessed_tags, correct_tags) if t == g + ) + + def accuracy(self): + """ + Return the overall tag-based accuracy for all text that have + been scored by this ``ChunkScore``, using the IOB (conll2000) + tag encoding. + + :rtype: float + """ + if self._tags_total == 0: + return 1 + return self._tags_correct / self._tags_total + + def precision(self): + """ + Return the overall precision for all texts that have been + scored by this ``ChunkScore``. + + :rtype: float + """ + self._updateMeasures() + div = self._tp_num + self._fp_num + if div == 0: + return 0 + else: + return self._tp_num / div + + def recall(self): + """ + Return the overall recall for all texts that have been + scored by this ``ChunkScore``. + + :rtype: float + """ + self._updateMeasures() + div = self._tp_num + self._fn_num + if div == 0: + return 0 + else: + return self._tp_num / div + + def f_measure(self, alpha=0.5): + """ + Return the overall F measure for all texts that have been + scored by this ``ChunkScore``. + + :param alpha: the relative weighting of precision and recall. + Larger alpha biases the score towards the precision value, + while smaller alpha biases the score towards the recall + value. ``alpha`` should have a value in the range [0,1]. + :type alpha: float + :rtype: float + """ + self._updateMeasures() + p = self.precision() + r = self.recall() + if p == 0 or r == 0: # what if alpha is 0 or 1? + return 0 + return 1 / (alpha / p + (1 - alpha) / r) + + def missed(self): + """ + Return the chunks which were included in the + correct chunk structures, but not in the guessed chunk + structures, listed in input order. + + :rtype: list of chunks + """ + self._updateMeasures() + chunks = list(self._fn) + return [c[1] for c in chunks] # discard position information + + def incorrect(self): + """ + Return the chunks which were included in the guessed chunk structures, + but not in the correct chunk structures, listed in input order. + + :rtype: list of chunks + """ + self._updateMeasures() + chunks = list(self._fp) + return [c[1] for c in chunks] # discard position information + + def correct(self): + """ + Return the chunks which were included in the correct + chunk structures, listed in input order. + + :rtype: list of chunks + """ + chunks = list(self._correct) + return [c[1] for c in chunks] # discard position information + + def guessed(self): + """ + Return the chunks which were included in the guessed + chunk structures, listed in input order. + + :rtype: list of chunks + """ + chunks = list(self._guessed) + return [c[1] for c in chunks] # discard position information + + def __len__(self): + self._updateMeasures() + return self._tp_num + self._fn_num + + def __repr__(self): + """ + Return a concise representation of this ``ChunkScoring``. + + :rtype: str + """ + return "" + + def __str__(self): + """ + Return a verbose representation of this ``ChunkScoring``. + This representation includes the precision, recall, and + f-measure scores. For other information about the score, + use the accessor methods (e.g., ``missed()`` and ``incorrect()``). + + :rtype: str + """ + return ( + "ChunkParse score:\n" + + (f" IOB Accuracy: {self.accuracy() * 100:5.1f}%%\n") + + (f" Precision: {self.precision() * 100:5.1f}%%\n") + + (f" Recall: {self.recall() * 100:5.1f}%%\n") + + (f" F-Measure: {self.f_measure() * 100:5.1f}%%") + ) + + +# extract chunks, and assign unique id, the absolute position of +# the first word of the chunk +def _chunksets(t, count, chunk_label): + pos = 0 + chunks = [] + for child in t: + if isinstance(child, Tree): + if re.match(chunk_label, child.label()): + chunks.append(((count, pos), child.freeze())) + pos += len(child.leaves()) + else: + pos += 1 + return set(chunks) + + +def tagstr2tree( + s, chunk_label="NP", root_label="S", sep="/", source_tagset=None, target_tagset=None +): + """ + Divide a string of bracketted tagged text into + chunks and unchunked tokens, and produce a Tree. + Chunks are marked by square brackets (``[...]``). Words are + delimited by whitespace, and each word should have the form + ``text/tag``. Words that do not contain a slash are + assigned a ``tag`` of None. + + :param s: The string to be converted + :type s: str + :param chunk_label: The label to use for chunk nodes + :type chunk_label: str + :param root_label: The label to use for the root of the tree + :type root_label: str + :rtype: Tree + """ + + WORD_OR_BRACKET = re.compile(r"\[|\]|[^\[\]\s]+") + + stack = [Tree(root_label, [])] + for match in WORD_OR_BRACKET.finditer(s): + text = match.group() + if text[0] == "[": + if len(stack) != 1: + raise ValueError(f"Unexpected [ at char {match.start():d}") + chunk = Tree(chunk_label, []) + stack[-1].append(chunk) + stack.append(chunk) + elif text[0] == "]": + if len(stack) != 2: + raise ValueError(f"Unexpected ] at char {match.start():d}") + stack.pop() + else: + if sep is None: + stack[-1].append(text) + else: + word, tag = str2tuple(text, sep) + if source_tagset and target_tagset: + tag = map_tag(source_tagset, target_tagset, tag) + stack[-1].append((word, tag)) + + if len(stack) != 1: + raise ValueError(f"Expected ] at char {len(s):d}") + return stack[0] + + +### CONLL + +_LINE_RE = re.compile(r"(\S+)\s+(\S+)\s+([IOB])-?(\S+)?") + + +def conllstr2tree(s, chunk_types=("NP", "PP", "VP"), root_label="S"): + """ + Return a chunk structure for a single sentence + encoded in the given CONLL 2000 style string. + This function converts a CoNLL IOB string into a tree. + It uses the specified chunk types + (defaults to NP, PP and VP), and creates a tree rooted at a node + labeled S (by default). + + :param s: The CoNLL string to be converted. + :type s: str + :param chunk_types: The chunk types to be converted. + :type chunk_types: tuple + :param root_label: The node label to use for the root. + :type root_label: str + :rtype: Tree + """ + + stack = [Tree(root_label, [])] + + for lineno, line in enumerate(s.split("\n")): + if not line.strip(): + continue + + # Decode the line. + match = _LINE_RE.match(line) + if match is None: + raise ValueError(f"Error on line {lineno:d}") + (word, tag, state, chunk_type) = match.groups() + + # If it's a chunk type we don't care about, treat it as O. + if chunk_types is not None and chunk_type not in chunk_types: + state = "O" + + # For "Begin"/"Outside", finish any completed chunks - + # also do so for "Inside" which don't match the previous token. + mismatch_I = state == "I" and chunk_type != stack[-1].label() + if state in "BO" or mismatch_I: + if len(stack) == 2: + stack.pop() + + # For "Begin", start a new chunk. + if state == "B" or mismatch_I: + chunk = Tree(chunk_type, []) + stack[-1].append(chunk) + stack.append(chunk) + + # Add the new word token. + stack[-1].append((word, tag)) + + return stack[0] + + +def tree2conlltags(t): + """ + Return a list of 3-tuples containing ``(word, tag, IOB-tag)``. + Convert a tree to the CoNLL IOB tag format. + + :param t: The tree to be converted. + :type t: Tree + :rtype: list(tuple) + """ + + tags = [] + for child in t: + try: + category = child.label() + prefix = "B-" + for contents in child: + if isinstance(contents, Tree): + raise ValueError( + "Tree is too deeply nested to be printed in CoNLL format" + ) + tags.append((contents[0], contents[1], prefix + category)) + prefix = "I-" + except AttributeError: + tags.append((child[0], child[1], "O")) + return tags + + +def conlltags2tree( + sentence, chunk_types=("NP", "PP", "VP"), root_label="S", strict=False +): + """ + Convert the CoNLL IOB format to a tree. + """ + tree = Tree(root_label, []) + for (word, postag, chunktag) in sentence: + if chunktag is None: + if strict: + raise ValueError("Bad conll tag sequence") + else: + # Treat as O + tree.append((word, postag)) + elif chunktag.startswith("B-"): + tree.append(Tree(chunktag[2:], [(word, postag)])) + elif chunktag.startswith("I-"): + if ( + len(tree) == 0 + or not isinstance(tree[-1], Tree) + or tree[-1].label() != chunktag[2:] + ): + if strict: + raise ValueError("Bad conll tag sequence") + else: + # Treat as B-* + tree.append(Tree(chunktag[2:], [(word, postag)])) + else: + tree[-1].append((word, postag)) + elif chunktag == "O": + tree.append((word, postag)) + else: + raise ValueError(f"Bad conll tag {chunktag!r}") + return tree + + +def tree2conllstr(t): + """ + Return a multiline string where each line contains a word, tag and IOB tag. + Convert a tree to the CoNLL IOB string format + + :param t: The tree to be converted. + :type t: Tree + :rtype: str + """ + lines = [" ".join(token) for token in tree2conlltags(t)] + return "\n".join(lines) + + +### IEER + +_IEER_DOC_RE = re.compile( + r"\s*" + r"(\s*(?P.+?)\s*\s*)?" + r"(\s*(?P.+?)\s*\s*)?" + r"(\s*(?P.+?)\s*\s*)?" + r"\s*" + r"(\s*(?P.+?)\s*\s*)?" + r"(?P.*?)\s*" + r"\s*\s*", + re.DOTALL, +) + +_IEER_TYPE_RE = re.compile(r']*?type="(?P\w+)"') + + +def _ieer_read_text(s, root_label): + stack = [Tree(root_label, [])] + # s will be None if there is no headline in the text + # return the empty list in place of a Tree + if s is None: + return [] + for piece_m in re.finditer(r"<[^>]+>|[^\s<]+", s): + piece = piece_m.group() + try: + if piece.startswith(".... + m = _IEER_DOC_RE.match(s) + if m: + return { + "text": _ieer_read_text(m.group("text"), root_label), + "docno": m.group("docno"), + "doctype": m.group("doctype"), + "date_time": m.group("date_time"), + #'headline': m.group('headline') + # we want to capture NEs in the headline too! + "headline": _ieer_read_text(m.group("headline"), root_label), + } + else: + return _ieer_read_text(s, root_label) + + +def demo(): + + s = "[ Pierre/NNP Vinken/NNP ] ,/, [ 61/CD years/NNS ] old/JJ ,/, will/MD join/VB [ the/DT board/NN ] ./." + import nltk + + t = nltk.chunk.tagstr2tree(s, chunk_label="NP") + t.pprint() + print() + + s = """ +These DT B-NP +research NN I-NP +protocols NNS I-NP +offer VBP B-VP +to TO B-PP +the DT B-NP +patient NN I-NP +not RB O +only RB O +the DT B-NP +very RB I-NP +best JJS I-NP +therapy NN I-NP +which WDT B-NP +we PRP B-NP +have VBP B-VP +established VBN I-VP +today NN B-NP +but CC B-NP +also RB I-NP +the DT B-NP +hope NN I-NP +of IN B-PP +something NN B-NP +still RB B-ADJP +better JJR I-ADJP +. . O +""" + + conll_tree = conllstr2tree(s, chunk_types=("NP", "PP")) + conll_tree.pprint() + + # Demonstrate CoNLL output + print("CoNLL output:") + print(nltk.chunk.tree2conllstr(conll_tree)) + print() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__init__.py new file mode 100644 index 0000000..55f7116 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__init__.py @@ -0,0 +1,101 @@ +# Natural Language Toolkit: Classifiers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Classes and interfaces for labeling tokens with category labels (or +"class labels"). Typically, labels are represented with strings +(such as ``'health'`` or ``'sports'``). Classifiers can be used to +perform a wide range of classification tasks. For example, +classifiers can be used... + +- to classify documents by topic +- to classify ambiguous words by which word sense is intended +- to classify acoustic signals by which phoneme they represent +- to classify sentences by their author + +Features +======== +In order to decide which category label is appropriate for a given +token, classifiers examine one or more 'features' of the token. These +"features" are typically chosen by hand, and indicate which aspects +of the token are relevant to the classification decision. For +example, a document classifier might use a separate feature for each +word, recording how often that word occurred in the document. + +Featuresets +=========== +The features describing a token are encoded using a "featureset", +which is a dictionary that maps from "feature names" to "feature +values". Feature names are unique strings that indicate what aspect +of the token is encoded by the feature. Examples include +``'prevword'``, for a feature whose value is the previous word; and +``'contains-word(library)'`` for a feature that is true when a document +contains the word ``'library'``. Feature values are typically +booleans, numbers, or strings, depending on which feature they +describe. + +Featuresets are typically constructed using a "feature detector" +(also known as a "feature extractor"). A feature detector is a +function that takes a token (and sometimes information about its +context) as its input, and returns a featureset describing that token. +For example, the following feature detector converts a document +(stored as a list of words) to a featureset describing the set of +words included in the document: + + >>> # Define a feature detector function. + >>> def document_features(document): + ... return dict([('contains-word(%s)' % w, True) for w in document]) + +Feature detectors are typically applied to each token before it is fed +to the classifier: + + >>> # Classify each Gutenberg document. + >>> from nltk.corpus import gutenberg + >>> for fileid in gutenberg.fileids(): # doctest: +SKIP + ... doc = gutenberg.words(fileid) # doctest: +SKIP + ... print(fileid, classifier.classify(document_features(doc))) # doctest: +SKIP + +The parameters that a feature detector expects will vary, depending on +the task and the needs of the feature detector. For example, a +feature detector for word sense disambiguation (WSD) might take as its +input a sentence, and the index of a word that should be classified, +and return a featureset for that word. The following feature detector +for WSD includes features describing the left and right contexts of +the target word: + + >>> def wsd_features(sentence, index): + ... featureset = {} + ... for i in range(max(0, index-3), index): + ... featureset['left-context(%s)' % sentence[i]] = True + ... for i in range(index, max(index+3, len(sentence))): + ... featureset['right-context(%s)' % sentence[i]] = True + ... return featureset + +Training Classifiers +==================== +Most classifiers are built by training them on a list of hand-labeled +examples, known as the "training set". Training sets are represented +as lists of ``(featuredict, label)`` tuples. +""" + +from nltk.classify.api import ClassifierI, MultiClassifierI +from nltk.classify.decisiontree import DecisionTreeClassifier +from nltk.classify.maxent import ( + BinaryMaxentFeatureEncoding, + ConditionalExponentialClassifier, + MaxentClassifier, + TypedMaxentFeatureEncoding, +) +from nltk.classify.megam import call_megam, config_megam +from nltk.classify.naivebayes import NaiveBayesClassifier +from nltk.classify.positivenaivebayes import PositiveNaiveBayesClassifier +from nltk.classify.rte_classify import RTEFeatureExtractor, rte_classifier, rte_features +from nltk.classify.scikitlearn import SklearnClassifier +from nltk.classify.senna import Senna +from nltk.classify.textcat import TextCat +from nltk.classify.util import accuracy, apply_features, log_likelihood +from nltk.classify.weka import WekaClassifier, config_weka diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c611073 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..0c6a83e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/decisiontree.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/decisiontree.cpython-310.pyc new file mode 100644 index 0000000..d1d3b0a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/decisiontree.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/maxent.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/maxent.cpython-310.pyc new file mode 100644 index 0000000..b2bb7cb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/maxent.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/megam.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/megam.cpython-310.pyc new file mode 100644 index 0000000..8cac6a1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/megam.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/naivebayes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/naivebayes.cpython-310.pyc new file mode 100644 index 0000000..e34e4fc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/naivebayes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/positivenaivebayes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/positivenaivebayes.cpython-310.pyc new file mode 100644 index 0000000..12c8e37 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/positivenaivebayes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/rte_classify.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/rte_classify.cpython-310.pyc new file mode 100644 index 0000000..9c986b3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/rte_classify.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/scikitlearn.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/scikitlearn.cpython-310.pyc new file mode 100644 index 0000000..35ff7ae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/scikitlearn.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/senna.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/senna.cpython-310.pyc new file mode 100644 index 0000000..4e1b646 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/senna.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/svm.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/svm.cpython-310.pyc new file mode 100644 index 0000000..ecd0221 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/svm.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/tadm.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/tadm.cpython-310.pyc new file mode 100644 index 0000000..300d54a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/tadm.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/textcat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/textcat.cpython-310.pyc new file mode 100644 index 0000000..1326144 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/textcat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..5652635 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/weka.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/weka.cpython-310.pyc new file mode 100644 index 0000000..3315b0c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/__pycache__/weka.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/api.py new file mode 100644 index 0000000..88e627a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/api.py @@ -0,0 +1,195 @@ +# Natural Language Toolkit: Classifier Interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT + +""" +Interfaces for labeling tokens with category labels (or "class labels"). + +``ClassifierI`` is a standard interface for "single-category +classification", in which the set of categories is known, the number +of categories is finite, and each text belongs to exactly one +category. + +``MultiClassifierI`` is a standard interface for "multi-category +classification", which is like single-category classification except +that each text belongs to zero or more categories. +""" +from nltk.internals import overridden + +##////////////////////////////////////////////////////// +# { Classification Interfaces +##////////////////////////////////////////////////////// + + +class ClassifierI: + """ + A processing interface for labeling tokens with a single category + label (or "class"). Labels are typically strs or + ints, but can be any immutable type. The set of labels + that the classifier chooses from must be fixed and finite. + + Subclasses must define: + - ``labels()`` + - either ``classify()`` or ``classify_many()`` (or both) + + Subclasses may define: + - either ``prob_classify()`` or ``prob_classify_many()`` (or both) + """ + + def labels(self): + """ + :return: the list of category labels used by this classifier. + :rtype: list of (immutable) + """ + raise NotImplementedError() + + def classify(self, featureset): + """ + :return: the most appropriate label for the given featureset. + :rtype: label + """ + if overridden(self.classify_many): + return self.classify_many([featureset])[0] + else: + raise NotImplementedError() + + def prob_classify(self, featureset): + """ + :return: a probability distribution over labels for the given + featureset. + :rtype: ProbDistI + """ + if overridden(self.prob_classify_many): + return self.prob_classify_many([featureset])[0] + else: + raise NotImplementedError() + + def classify_many(self, featuresets): + """ + Apply ``self.classify()`` to each element of ``featuresets``. I.e.: + + return [self.classify(fs) for fs in featuresets] + + :rtype: list(label) + """ + return [self.classify(fs) for fs in featuresets] + + def prob_classify_many(self, featuresets): + """ + Apply ``self.prob_classify()`` to each element of ``featuresets``. I.e.: + + return [self.prob_classify(fs) for fs in featuresets] + + :rtype: list(ProbDistI) + """ + return [self.prob_classify(fs) for fs in featuresets] + + +class MultiClassifierI: + """ + A processing interface for labeling tokens with zero or more + category labels (or "labels"). Labels are typically strs + or ints, but can be any immutable type. The set of labels + that the multi-classifier chooses from must be fixed and finite. + + Subclasses must define: + - ``labels()`` + - either ``classify()`` or ``classify_many()`` (or both) + + Subclasses may define: + - either ``prob_classify()`` or ``prob_classify_many()`` (or both) + """ + + def labels(self): + """ + :return: the list of category labels used by this classifier. + :rtype: list of (immutable) + """ + raise NotImplementedError() + + def classify(self, featureset): + """ + :return: the most appropriate set of labels for the given featureset. + :rtype: set(label) + """ + if overridden(self.classify_many): + return self.classify_many([featureset])[0] + else: + raise NotImplementedError() + + def prob_classify(self, featureset): + """ + :return: a probability distribution over sets of labels for the + given featureset. + :rtype: ProbDistI + """ + if overridden(self.prob_classify_many): + return self.prob_classify_many([featureset])[0] + else: + raise NotImplementedError() + + def classify_many(self, featuresets): + """ + Apply ``self.classify()`` to each element of ``featuresets``. I.e.: + + return [self.classify(fs) for fs in featuresets] + + :rtype: list(set(label)) + """ + return [self.classify(fs) for fs in featuresets] + + def prob_classify_many(self, featuresets): + """ + Apply ``self.prob_classify()`` to each element of ``featuresets``. I.e.: + + return [self.prob_classify(fs) for fs in featuresets] + + :rtype: list(ProbDistI) + """ + return [self.prob_classify(fs) for fs in featuresets] + + +# # [XX] IN PROGRESS: +# class SequenceClassifierI: +# """ +# A processing interface for labeling sequences of tokens with a +# single category label (or "class"). Labels are typically +# strs or ints, but can be any immutable type. The set +# of labels that the classifier chooses from must be fixed and +# finite. +# """ +# def labels(self): +# """ +# :return: the list of category labels used by this classifier. +# :rtype: list of (immutable) +# """ +# raise NotImplementedError() + +# def prob_classify(self, featureset): +# """ +# Return a probability distribution over labels for the given +# featureset. + +# If ``featureset`` is a list of featuresets, then return a +# corresponding list containing the probability distribution +# over labels for each of the given featuresets, where the +# *i*\ th element of this list is the most appropriate label for +# the *i*\ th element of ``featuresets``. +# """ +# raise NotImplementedError() + +# def classify(self, featureset): +# """ +# Return the most appropriate label for the given featureset. + +# If ``featureset`` is a list of featuresets, then return a +# corresponding list containing the most appropriate label for +# each of the given featuresets, where the *i*\ th element of +# this list is the most appropriate label for the *i*\ th element +# of ``featuresets``. +# """ +# raise NotImplementedError() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/decisiontree.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/decisiontree.py new file mode 100644 index 0000000..9c9cc3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/decisiontree.py @@ -0,0 +1,349 @@ +# Natural Language Toolkit: Decision Tree Classifiers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A classifier model that decides which label to assign to a token on +the basis of a tree structure, where branches correspond to conditions +on feature values, and leaves correspond to label assignments. +""" + +from collections import defaultdict + +from nltk.classify.api import ClassifierI +from nltk.probability import FreqDist, MLEProbDist, entropy + + +class DecisionTreeClassifier(ClassifierI): + def __init__(self, label, feature_name=None, decisions=None, default=None): + """ + :param label: The most likely label for tokens that reach + this node in the decision tree. If this decision tree + has no children, then this label will be assigned to + any token that reaches this decision tree. + :param feature_name: The name of the feature that this + decision tree selects for. + :param decisions: A dictionary mapping from feature values + for the feature identified by ``feature_name`` to + child decision trees. + :param default: The child that will be used if the value of + feature ``feature_name`` does not match any of the keys in + ``decisions``. This is used when constructing binary + decision trees. + """ + self._label = label + self._fname = feature_name + self._decisions = decisions + self._default = default + + def labels(self): + labels = [self._label] + if self._decisions is not None: + for dt in self._decisions.values(): + labels.extend(dt.labels()) + if self._default is not None: + labels.extend(self._default.labels()) + return list(set(labels)) + + def classify(self, featureset): + # Decision leaf: + if self._fname is None: + return self._label + + # Decision tree: + fval = featureset.get(self._fname) + if fval in self._decisions: + return self._decisions[fval].classify(featureset) + elif self._default is not None: + return self._default.classify(featureset) + else: + return self._label + + def error(self, labeled_featuresets): + errors = 0 + for featureset, label in labeled_featuresets: + if self.classify(featureset) != label: + errors += 1 + return errors / len(labeled_featuresets) + + def pretty_format(self, width=70, prefix="", depth=4): + """ + Return a string containing a pretty-printed version of this + decision tree. Each line in this string corresponds to a + single decision tree node or leaf, and indentation is used to + display the structure of the decision tree. + """ + # [xx] display default!! + if self._fname is None: + n = width - len(prefix) - 15 + return "{}{} {}\n".format(prefix, "." * n, self._label) + s = "" + for i, (fval, result) in enumerate( + sorted( + self._decisions.items(), + key=lambda item: (item[0] in [None, False, True], str(item[0]).lower()), + ) + ): + hdr = f"{prefix}{self._fname}={fval}? " + n = width - 15 - len(hdr) + s += "{}{} {}\n".format(hdr, "." * (n), result._label) + if result._fname is not None and depth > 1: + s += result.pretty_format(width, prefix + " ", depth - 1) + if self._default is not None: + n = width - len(prefix) - 21 + s += "{}else: {} {}\n".format(prefix, "." * n, self._default._label) + if self._default._fname is not None and depth > 1: + s += self._default.pretty_format(width, prefix + " ", depth - 1) + return s + + def pseudocode(self, prefix="", depth=4): + """ + Return a string representation of this decision tree that + expresses the decisions it makes as a nested set of pseudocode + if statements. + """ + if self._fname is None: + return f"{prefix}return {self._label!r}\n" + s = "" + for (fval, result) in sorted( + self._decisions.items(), + key=lambda item: (item[0] in [None, False, True], str(item[0]).lower()), + ): + s += f"{prefix}if {self._fname} == {fval!r}: " + if result._fname is not None and depth > 1: + s += "\n" + result.pseudocode(prefix + " ", depth - 1) + else: + s += f"return {result._label!r}\n" + if self._default is not None: + if len(self._decisions) == 1: + s += "{}if {} != {!r}: ".format( + prefix, self._fname, list(self._decisions.keys())[0] + ) + else: + s += f"{prefix}else: " + if self._default._fname is not None and depth > 1: + s += "\n" + self._default.pseudocode(prefix + " ", depth - 1) + else: + s += f"return {self._default._label!r}\n" + return s + + def __str__(self): + return self.pretty_format() + + @staticmethod + def train( + labeled_featuresets, + entropy_cutoff=0.05, + depth_cutoff=100, + support_cutoff=10, + binary=False, + feature_values=None, + verbose=False, + ): + """ + :param binary: If true, then treat all feature/value pairs as + individual binary features, rather than using a single n-way + branch for each feature. + """ + # Collect a list of all feature names. + feature_names = set() + for featureset, label in labeled_featuresets: + for fname in featureset: + feature_names.add(fname) + + # Collect a list of the values each feature can take. + if feature_values is None and binary: + feature_values = defaultdict(set) + for featureset, label in labeled_featuresets: + for fname, fval in featureset.items(): + feature_values[fname].add(fval) + + # Start with a stump. + if not binary: + tree = DecisionTreeClassifier.best_stump( + feature_names, labeled_featuresets, verbose + ) + else: + tree = DecisionTreeClassifier.best_binary_stump( + feature_names, labeled_featuresets, feature_values, verbose + ) + + # Refine the stump. + tree.refine( + labeled_featuresets, + entropy_cutoff, + depth_cutoff - 1, + support_cutoff, + binary, + feature_values, + verbose, + ) + + # Return it + return tree + + @staticmethod + def leaf(labeled_featuresets): + label = FreqDist(label for (featureset, label) in labeled_featuresets).max() + return DecisionTreeClassifier(label) + + @staticmethod + def stump(feature_name, labeled_featuresets): + label = FreqDist(label for (featureset, label) in labeled_featuresets).max() + + # Find the best label for each value. + freqs = defaultdict(FreqDist) # freq(label|value) + for featureset, label in labeled_featuresets: + feature_value = featureset.get(feature_name) + freqs[feature_value][label] += 1 + + decisions = {val: DecisionTreeClassifier(freqs[val].max()) for val in freqs} + return DecisionTreeClassifier(label, feature_name, decisions) + + def refine( + self, + labeled_featuresets, + entropy_cutoff, + depth_cutoff, + support_cutoff, + binary=False, + feature_values=None, + verbose=False, + ): + if len(labeled_featuresets) <= support_cutoff: + return + if self._fname is None: + return + if depth_cutoff <= 0: + return + for fval in self._decisions: + fval_featuresets = [ + (featureset, label) + for (featureset, label) in labeled_featuresets + if featureset.get(self._fname) == fval + ] + + label_freqs = FreqDist(label for (featureset, label) in fval_featuresets) + if entropy(MLEProbDist(label_freqs)) > entropy_cutoff: + self._decisions[fval] = DecisionTreeClassifier.train( + fval_featuresets, + entropy_cutoff, + depth_cutoff, + support_cutoff, + binary, + feature_values, + verbose, + ) + if self._default is not None: + default_featuresets = [ + (featureset, label) + for (featureset, label) in labeled_featuresets + if featureset.get(self._fname) not in self._decisions + ] + label_freqs = FreqDist(label for (featureset, label) in default_featuresets) + if entropy(MLEProbDist(label_freqs)) > entropy_cutoff: + self._default = DecisionTreeClassifier.train( + default_featuresets, + entropy_cutoff, + depth_cutoff, + support_cutoff, + binary, + feature_values, + verbose, + ) + + @staticmethod + def best_stump(feature_names, labeled_featuresets, verbose=False): + best_stump = DecisionTreeClassifier.leaf(labeled_featuresets) + best_error = best_stump.error(labeled_featuresets) + for fname in feature_names: + stump = DecisionTreeClassifier.stump(fname, labeled_featuresets) + stump_error = stump.error(labeled_featuresets) + if stump_error < best_error: + best_error = stump_error + best_stump = stump + if verbose: + print( + "best stump for {:6d} toks uses {:20} err={:6.4f}".format( + len(labeled_featuresets), best_stump._fname, best_error + ) + ) + return best_stump + + @staticmethod + def binary_stump(feature_name, feature_value, labeled_featuresets): + label = FreqDist(label for (featureset, label) in labeled_featuresets).max() + + # Find the best label for each value. + pos_fdist = FreqDist() + neg_fdist = FreqDist() + for featureset, label in labeled_featuresets: + if featureset.get(feature_name) == feature_value: + pos_fdist[label] += 1 + else: + neg_fdist[label] += 1 + + decisions = {} + default = label + # But hopefully we have observations! + if pos_fdist.N() > 0: + decisions = {feature_value: DecisionTreeClassifier(pos_fdist.max())} + if neg_fdist.N() > 0: + default = DecisionTreeClassifier(neg_fdist.max()) + + return DecisionTreeClassifier(label, feature_name, decisions, default) + + @staticmethod + def best_binary_stump( + feature_names, labeled_featuresets, feature_values, verbose=False + ): + best_stump = DecisionTreeClassifier.leaf(labeled_featuresets) + best_error = best_stump.error(labeled_featuresets) + for fname in feature_names: + for fval in feature_values[fname]: + stump = DecisionTreeClassifier.binary_stump( + fname, fval, labeled_featuresets + ) + stump_error = stump.error(labeled_featuresets) + if stump_error < best_error: + best_error = stump_error + best_stump = stump + if verbose: + if best_stump._decisions: + descr = "{}={}".format( + best_stump._fname, list(best_stump._decisions.keys())[0] + ) + else: + descr = "(default)" + print( + "best stump for {:6d} toks uses {:20} err={:6.4f}".format( + len(labeled_featuresets), descr, best_error + ) + ) + return best_stump + + +##////////////////////////////////////////////////////// +## Demo +##////////////////////////////////////////////////////// + + +def f(x): + return DecisionTreeClassifier.train(x, binary=True, verbose=True) + + +def demo(): + from nltk.classify.util import binary_names_demo_features, names_demo + + classifier = names_demo( + f, binary_names_demo_features # DecisionTreeClassifier.train, + ) + print(classifier.pretty_format(depth=7)) + print(classifier.pseudocode(depth=7)) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/maxent.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/maxent.py new file mode 100644 index 0000000..26e14ab --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/maxent.py @@ -0,0 +1,1569 @@ +# Natural Language Toolkit: Maximum Entropy Classifiers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Dmitry Chichkov (TypedMaxentFeatureEncoding) +# URL: +# For license information, see LICENSE.TXT + +""" +A classifier model based on maximum entropy modeling framework. This +framework considers all of the probability distributions that are +empirically consistent with the training data; and chooses the +distribution with the highest entropy. A probability distribution is +"empirically consistent" with a set of training data if its estimated +frequency with which a class and a feature vector value co-occur is +equal to the actual frequency in the data. + +Terminology: 'feature' +====================== +The term *feature* is usually used to refer to some property of an +unlabeled token. For example, when performing word sense +disambiguation, we might define a ``'prevword'`` feature whose value is +the word preceding the target word. However, in the context of +maxent modeling, the term *feature* is typically used to refer to a +property of a "labeled" token. In order to prevent confusion, we +will introduce two distinct terms to disambiguate these two different +concepts: + + - An "input-feature" is a property of an unlabeled token. + - A "joint-feature" is a property of a labeled token. + +In the rest of the ``nltk.classify`` module, the term "features" is +used to refer to what we will call "input-features" in this module. + +In literature that describes and discusses maximum entropy models, +input-features are typically called "contexts", and joint-features +are simply referred to as "features". + +Converting Input-Features to Joint-Features +------------------------------------------- +In maximum entropy models, joint-features are required to have numeric +values. Typically, each input-feature ``input_feat`` is mapped to a +set of joint-features of the form: + +| joint_feat(token, label) = { 1 if input_feat(token) == feat_val +| { and label == some_label +| { +| { 0 otherwise + +For all values of ``feat_val`` and ``some_label``. This mapping is +performed by classes that implement the ``MaxentFeatureEncodingI`` +interface. +""" +try: + import numpy +except ImportError: + pass + +import os +import tempfile +from collections import defaultdict + +from nltk.classify.api import ClassifierI +from nltk.classify.megam import call_megam, parse_megam_weights, write_megam_file +from nltk.classify.tadm import call_tadm, parse_tadm_weights, write_tadm_file +from nltk.classify.util import CutoffChecker, accuracy, log_likelihood +from nltk.data import gzip_open_unicode +from nltk.probability import DictionaryProbDist +from nltk.util import OrderedDict + +__docformat__ = "epytext en" + +###################################################################### +# { Classifier Model +###################################################################### + + +class MaxentClassifier(ClassifierI): + """ + A maximum entropy classifier (also known as a "conditional + exponential classifier"). This classifier is parameterized by a + set of "weights", which are used to combine the joint-features + that are generated from a featureset by an "encoding". In + particular, the encoding maps each ``(featureset, label)`` pair to + a vector. The probability of each label is then computed using + the following equation:: + + dotprod(weights, encode(fs,label)) + prob(fs|label) = --------------------------------------------------- + sum(dotprod(weights, encode(fs,l)) for l in labels) + + Where ``dotprod`` is the dot product:: + + dotprod(a,b) = sum(x*y for (x,y) in zip(a,b)) + """ + + def __init__(self, encoding, weights, logarithmic=True): + """ + Construct a new maxent classifier model. Typically, new + classifier models are created using the ``train()`` method. + + :type encoding: MaxentFeatureEncodingI + :param encoding: An encoding that is used to convert the + featuresets that are given to the ``classify`` method into + joint-feature vectors, which are used by the maxent + classifier model. + + :type weights: list of float + :param weights: The feature weight vector for this classifier. + + :type logarithmic: bool + :param logarithmic: If false, then use non-logarithmic weights. + """ + self._encoding = encoding + self._weights = weights + self._logarithmic = logarithmic + # self._logarithmic = False + assert encoding.length() == len(weights) + + def labels(self): + return self._encoding.labels() + + def set_weights(self, new_weights): + """ + Set the feature weight vector for this classifier. + :param new_weights: The new feature weight vector. + :type new_weights: list of float + """ + self._weights = new_weights + assert self._encoding.length() == len(new_weights) + + def weights(self): + """ + :return: The feature weight vector for this classifier. + :rtype: list of float + """ + return self._weights + + def classify(self, featureset): + return self.prob_classify(featureset).max() + + def prob_classify(self, featureset): + prob_dict = {} + for label in self._encoding.labels(): + feature_vector = self._encoding.encode(featureset, label) + + if self._logarithmic: + total = 0.0 + for (f_id, f_val) in feature_vector: + total += self._weights[f_id] * f_val + prob_dict[label] = total + + else: + prod = 1.0 + for (f_id, f_val) in feature_vector: + prod *= self._weights[f_id] ** f_val + prob_dict[label] = prod + + # Normalize the dictionary to give a probability distribution + return DictionaryProbDist(prob_dict, log=self._logarithmic, normalize=True) + + def explain(self, featureset, columns=4): + """ + Print a table showing the effect of each of the features in + the given feature set, and how they combine to determine the + probabilities of each label for that featureset. + """ + descr_width = 50 + TEMPLATE = " %-" + str(descr_width - 2) + "s%s%8.3f" + + pdist = self.prob_classify(featureset) + labels = sorted(pdist.samples(), key=pdist.prob, reverse=True) + labels = labels[:columns] + print( + " Feature".ljust(descr_width) + + "".join("%8s" % (("%s" % l)[:7]) for l in labels) + ) + print(" " + "-" * (descr_width - 2 + 8 * len(labels))) + sums = defaultdict(int) + for i, label in enumerate(labels): + feature_vector = self._encoding.encode(featureset, label) + feature_vector.sort( + key=lambda fid__: abs(self._weights[fid__[0]]), reverse=True + ) + for (f_id, f_val) in feature_vector: + if self._logarithmic: + score = self._weights[f_id] * f_val + else: + score = self._weights[f_id] ** f_val + descr = self._encoding.describe(f_id) + descr = descr.split(" and label is ")[0] # hack + descr += " (%s)" % f_val # hack + if len(descr) > 47: + descr = descr[:44] + "..." + print(TEMPLATE % (descr, i * 8 * " ", score)) + sums[label] += score + print(" " + "-" * (descr_width - 1 + 8 * len(labels))) + print( + " TOTAL:".ljust(descr_width) + "".join("%8.3f" % sums[l] for l in labels) + ) + print( + " PROBS:".ljust(descr_width) + + "".join("%8.3f" % pdist.prob(l) for l in labels) + ) + + def most_informative_features(self, n=10): + """ + Generates the ranked list of informative features from most to least. + """ + if hasattr(self, "_most_informative_features"): + return self._most_informative_features[:n] + else: + self._most_informative_features = sorted( + list(range(len(self._weights))), + key=lambda fid: abs(self._weights[fid]), + reverse=True, + ) + return self._most_informative_features[:n] + + def show_most_informative_features(self, n=10, show="all"): + """ + :param show: all, neg, or pos (for negative-only or positive-only) + :type show: str + :param n: The no. of top features + :type n: int + """ + # Use None the full list of ranked features. + fids = self.most_informative_features(None) + if show == "pos": + fids = [fid for fid in fids if self._weights[fid] > 0] + elif show == "neg": + fids = [fid for fid in fids if self._weights[fid] < 0] + for fid in fids[:n]: + print(f"{self._weights[fid]:8.3f} {self._encoding.describe(fid)}") + + def __repr__(self): + return "" % ( + len(self._encoding.labels()), + self._encoding.length(), + ) + + #: A list of the algorithm names that are accepted for the + #: ``train()`` method's ``algorithm`` parameter. + ALGORITHMS = ["GIS", "IIS", "MEGAM", "TADM"] + + @classmethod + def train( + cls, + train_toks, + algorithm=None, + trace=3, + encoding=None, + labels=None, + gaussian_prior_sigma=0, + **cutoffs, + ): + """ + Train a new maxent classifier based on the given corpus of + training samples. This classifier will have its weights + chosen to maximize entropy while remaining empirically + consistent with the training corpus. + + :rtype: MaxentClassifier + :return: The new maxent classifier + + :type train_toks: list + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a featureset, + and the second of which is a classification label. + + :type algorithm: str + :param algorithm: A case-insensitive string, specifying which + algorithm should be used to train the classifier. The + following algorithms are currently available. + + - Iterative Scaling Methods: Generalized Iterative Scaling (``'GIS'``), + Improved Iterative Scaling (``'IIS'``) + - External Libraries (requiring megam): + LM-BFGS algorithm, with training performed by Megam (``'megam'``) + + The default algorithm is ``'IIS'``. + + :type trace: int + :param trace: The level of diagnostic tracing output to produce. + Higher values produce more verbose output. + :type encoding: MaxentFeatureEncodingI + :param encoding: A feature encoding, used to convert featuresets + into feature vectors. If none is specified, then a + ``BinaryMaxentFeatureEncoding`` will be built based on the + features that are attested in the training corpus. + :type labels: list(str) + :param labels: The set of possible labels. If none is given, then + the set of all labels attested in the training data will be + used instead. + :param gaussian_prior_sigma: The sigma value for a gaussian + prior on model weights. Currently, this is supported by + ``megam``. For other algorithms, its value is ignored. + :param cutoffs: Arguments specifying various conditions under + which the training should be halted. (Some of the cutoff + conditions are not supported by some algorithms.) + + - ``max_iter=v``: Terminate after ``v`` iterations. + - ``min_ll=v``: Terminate after the negative average + log-likelihood drops under ``v``. + - ``min_lldelta=v``: Terminate if a single iteration improves + log likelihood by less than ``v``. + """ + if algorithm is None: + algorithm = "iis" + for key in cutoffs: + if key not in ( + "max_iter", + "min_ll", + "min_lldelta", + "max_acc", + "min_accdelta", + "count_cutoff", + "norm", + "explicit", + "bernoulli", + ): + raise TypeError("Unexpected keyword arg %r" % key) + algorithm = algorithm.lower() + if algorithm == "iis": + return train_maxent_classifier_with_iis( + train_toks, trace, encoding, labels, **cutoffs + ) + elif algorithm == "gis": + return train_maxent_classifier_with_gis( + train_toks, trace, encoding, labels, **cutoffs + ) + elif algorithm == "megam": + return train_maxent_classifier_with_megam( + train_toks, trace, encoding, labels, gaussian_prior_sigma, **cutoffs + ) + elif algorithm == "tadm": + kwargs = cutoffs + kwargs["trace"] = trace + kwargs["encoding"] = encoding + kwargs["labels"] = labels + kwargs["gaussian_prior_sigma"] = gaussian_prior_sigma + return TadmMaxentClassifier.train(train_toks, **kwargs) + else: + raise ValueError("Unknown algorithm %s" % algorithm) + + +#: Alias for MaxentClassifier. +ConditionalExponentialClassifier = MaxentClassifier + + +###################################################################### +# { Feature Encodings +###################################################################### + + +class MaxentFeatureEncodingI: + """ + A mapping that converts a set of input-feature values to a vector + of joint-feature values, given a label. This conversion is + necessary to translate featuresets into a format that can be used + by maximum entropy models. + + The set of joint-features used by a given encoding is fixed, and + each index in the generated joint-feature vectors corresponds to a + single joint-feature. The length of the generated joint-feature + vectors is therefore constant (for a given encoding). + + Because the joint-feature vectors generated by + ``MaxentFeatureEncodingI`` are typically very sparse, they are + represented as a list of ``(index, value)`` tuples, specifying the + value of each non-zero joint-feature. + + Feature encodings are generally created using the ``train()`` + method, which generates an appropriate encoding based on the + input-feature values and labels that are present in a given + corpus. + """ + + def encode(self, featureset, label): + """ + Given a (featureset, label) pair, return the corresponding + vector of joint-feature values. This vector is represented as + a list of ``(index, value)`` tuples, specifying the value of + each non-zero joint-feature. + + :type featureset: dict + :rtype: list(tuple(int, int)) + """ + raise NotImplementedError() + + def length(self): + """ + :return: The size of the fixed-length joint-feature vectors + that are generated by this encoding. + :rtype: int + """ + raise NotImplementedError() + + def labels(self): + """ + :return: A list of the \"known labels\" -- i.e., all labels + ``l`` such that ``self.encode(fs,l)`` can be a nonzero + joint-feature vector for some value of ``fs``. + :rtype: list + """ + raise NotImplementedError() + + def describe(self, fid): + """ + :return: A string describing the value of the joint-feature + whose index in the generated feature vectors is ``fid``. + :rtype: str + """ + raise NotImplementedError() + + def train(cls, train_toks): + """ + Construct and return new feature encoding, based on a given + training corpus ``train_toks``. + + :type train_toks: list(tuple(dict, str)) + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a feature dictionary, + and the second of which is a classification label. + """ + raise NotImplementedError() + + +class FunctionBackedMaxentFeatureEncoding(MaxentFeatureEncodingI): + """ + A feature encoding that calls a user-supplied function to map a + given featureset/label pair to a sparse joint-feature vector. + """ + + def __init__(self, func, length, labels): + """ + Construct a new feature encoding based on the given function. + + :type func: (callable) + :param func: A function that takes two arguments, a featureset + and a label, and returns the sparse joint feature vector + that encodes them:: + + func(featureset, label) -> feature_vector + + This sparse joint feature vector (``feature_vector``) is a + list of ``(index,value)`` tuples. + + :type length: int + :param length: The size of the fixed-length joint-feature + vectors that are generated by this encoding. + + :type labels: list + :param labels: A list of the \"known labels\" for this + encoding -- i.e., all labels ``l`` such that + ``self.encode(fs,l)`` can be a nonzero joint-feature vector + for some value of ``fs``. + """ + self._length = length + self._func = func + self._labels = labels + + def encode(self, featureset, label): + return self._func(featureset, label) + + def length(self): + return self._length + + def labels(self): + return self._labels + + def describe(self, fid): + return "no description available" + + +class BinaryMaxentFeatureEncoding(MaxentFeatureEncodingI): + """ + A feature encoding that generates vectors containing a binary + joint-features of the form: + + | joint_feat(fs, l) = { 1 if (fs[fname] == fval) and (l == label) + | { + | { 0 otherwise + + Where ``fname`` is the name of an input-feature, ``fval`` is a value + for that input-feature, and ``label`` is a label. + + Typically, these features are constructed based on a training + corpus, using the ``train()`` method. This method will create one + feature for each combination of ``fname``, ``fval``, and ``label`` + that occurs at least once in the training corpus. + + The ``unseen_features`` parameter can be used to add "unseen-value + features", which are used whenever an input feature has a value + that was not encountered in the training corpus. These features + have the form: + + | joint_feat(fs, l) = { 1 if is_unseen(fname, fs[fname]) + | { and l == label + | { + | { 0 otherwise + + Where ``is_unseen(fname, fval)`` is true if the encoding does not + contain any joint features that are true when ``fs[fname]==fval``. + + The ``alwayson_features`` parameter can be used to add "always-on + features", which have the form:: + + | joint_feat(fs, l) = { 1 if (l == label) + | { + | { 0 otherwise + + These always-on features allow the maxent model to directly model + the prior probabilities of each label. + """ + + def __init__(self, labels, mapping, unseen_features=False, alwayson_features=False): + """ + :param labels: A list of the \"known labels\" for this encoding. + + :param mapping: A dictionary mapping from ``(fname,fval,label)`` + tuples to corresponding joint-feature indexes. These + indexes must be the set of integers from 0...len(mapping). + If ``mapping[fname,fval,label]=id``, then + ``self.encode(..., fname:fval, ..., label)[id]`` is 1; + otherwise, it is 0. + + :param unseen_features: If true, then include unseen value + features in the generated joint-feature vectors. + + :param alwayson_features: If true, then include always-on + features in the generated joint-feature vectors. + """ + if set(mapping.values()) != set(range(len(mapping))): + raise ValueError( + "Mapping values must be exactly the " + "set of integers from 0...len(mapping)" + ) + + self._labels = list(labels) + """A list of attested labels.""" + + self._mapping = mapping + """dict mapping from (fname,fval,label) -> fid""" + + self._length = len(mapping) + """The length of generated joint feature vectors.""" + + self._alwayson = None + """dict mapping from label -> fid""" + + self._unseen = None + """dict mapping from fname -> fid""" + + if alwayson_features: + self._alwayson = { + label: i + self._length for (i, label) in enumerate(labels) + } + self._length += len(self._alwayson) + + if unseen_features: + fnames = {fname for (fname, fval, label) in mapping} + self._unseen = {fname: i + self._length for (i, fname) in enumerate(fnames)} + self._length += len(fnames) + + def encode(self, featureset, label): + # Inherit docs. + encoding = [] + + # Convert input-features to joint-features: + for fname, fval in featureset.items(): + # Known feature name & value: + if (fname, fval, label) in self._mapping: + encoding.append((self._mapping[fname, fval, label], 1)) + + # Otherwise, we might want to fire an "unseen-value feature". + elif self._unseen: + # Have we seen this fname/fval combination with any label? + for label2 in self._labels: + if (fname, fval, label2) in self._mapping: + break # we've seen this fname/fval combo + # We haven't -- fire the unseen-value feature + else: + if fname in self._unseen: + encoding.append((self._unseen[fname], 1)) + + # Add always-on features: + if self._alwayson and label in self._alwayson: + encoding.append((self._alwayson[label], 1)) + + return encoding + + def describe(self, f_id): + # Inherit docs. + if not isinstance(f_id, int): + raise TypeError("describe() expected an int") + try: + self._inv_mapping + except AttributeError: + self._inv_mapping = [-1] * len(self._mapping) + for (info, i) in self._mapping.items(): + self._inv_mapping[i] = info + + if f_id < len(self._mapping): + (fname, fval, label) = self._inv_mapping[f_id] + return f"{fname}=={fval!r} and label is {label!r}" + elif self._alwayson and f_id in self._alwayson.values(): + for (label, f_id2) in self._alwayson.items(): + if f_id == f_id2: + return "label is %r" % label + elif self._unseen and f_id in self._unseen.values(): + for (fname, f_id2) in self._unseen.items(): + if f_id == f_id2: + return "%s is unseen" % fname + else: + raise ValueError("Bad feature id") + + def labels(self): + # Inherit docs. + return self._labels + + def length(self): + # Inherit docs. + return self._length + + @classmethod + def train(cls, train_toks, count_cutoff=0, labels=None, **options): + """ + Construct and return new feature encoding, based on a given + training corpus ``train_toks``. See the class description + ``BinaryMaxentFeatureEncoding`` for a description of the + joint-features that will be included in this encoding. + + :type train_toks: list(tuple(dict, str)) + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a feature dictionary, + and the second of which is a classification label. + + :type count_cutoff: int + :param count_cutoff: A cutoff value that is used to discard + rare joint-features. If a joint-feature's value is 1 + fewer than ``count_cutoff`` times in the training corpus, + then that joint-feature is not included in the generated + encoding. + + :type labels: list + :param labels: A list of labels that should be used by the + classifier. If not specified, then the set of labels + attested in ``train_toks`` will be used. + + :param options: Extra parameters for the constructor, such as + ``unseen_features`` and ``alwayson_features``. + """ + mapping = {} # maps (fname, fval, label) -> fid + seen_labels = set() # The set of labels we've encountered + count = defaultdict(int) # maps (fname, fval) -> count + + for (tok, label) in train_toks: + if labels and label not in labels: + raise ValueError("Unexpected label %s" % label) + seen_labels.add(label) + + # Record each of the features. + for (fname, fval) in tok.items(): + + # If a count cutoff is given, then only add a joint + # feature once the corresponding (fname, fval, label) + # tuple exceeds that cutoff. + count[fname, fval] += 1 + if count[fname, fval] >= count_cutoff: + if (fname, fval, label) not in mapping: + mapping[fname, fval, label] = len(mapping) + + if labels is None: + labels = seen_labels + return cls(labels, mapping, **options) + + +class GISEncoding(BinaryMaxentFeatureEncoding): + """ + A binary feature encoding which adds one new joint-feature to the + joint-features defined by ``BinaryMaxentFeatureEncoding``: a + correction feature, whose value is chosen to ensure that the + sparse vector always sums to a constant non-negative number. This + new feature is used to ensure two preconditions for the GIS + training algorithm: + + - At least one feature vector index must be nonzero for every + token. + - The feature vector must sum to a constant non-negative number + for every token. + """ + + def __init__( + self, labels, mapping, unseen_features=False, alwayson_features=False, C=None + ): + """ + :param C: The correction constant. The value of the correction + feature is based on this value. In particular, its value is + ``C - sum([v for (f,v) in encoding])``. + :seealso: ``BinaryMaxentFeatureEncoding.__init__`` + """ + BinaryMaxentFeatureEncoding.__init__( + self, labels, mapping, unseen_features, alwayson_features + ) + if C is None: + C = len({fname for (fname, fval, label) in mapping}) + 1 + self._C = C + + @property + def C(self): + """The non-negative constant that all encoded feature vectors + will sum to.""" + return self._C + + def encode(self, featureset, label): + # Get the basic encoding. + encoding = BinaryMaxentFeatureEncoding.encode(self, featureset, label) + base_length = BinaryMaxentFeatureEncoding.length(self) + + # Add a correction feature. + total = sum(v for (f, v) in encoding) + if total >= self._C: + raise ValueError("Correction feature is not high enough!") + encoding.append((base_length, self._C - total)) + + # Return the result + return encoding + + def length(self): + return BinaryMaxentFeatureEncoding.length(self) + 1 + + def describe(self, f_id): + if f_id == BinaryMaxentFeatureEncoding.length(self): + return "Correction feature (%s)" % self._C + else: + return BinaryMaxentFeatureEncoding.describe(self, f_id) + + +class TadmEventMaxentFeatureEncoding(BinaryMaxentFeatureEncoding): + def __init__(self, labels, mapping, unseen_features=False, alwayson_features=False): + self._mapping = OrderedDict(mapping) + self._label_mapping = OrderedDict() + BinaryMaxentFeatureEncoding.__init__( + self, labels, self._mapping, unseen_features, alwayson_features + ) + + def encode(self, featureset, label): + encoding = [] + for feature, value in featureset.items(): + if (feature, label) not in self._mapping: + self._mapping[(feature, label)] = len(self._mapping) + if value not in self._label_mapping: + if not isinstance(value, int): + self._label_mapping[value] = len(self._label_mapping) + else: + self._label_mapping[value] = value + encoding.append( + (self._mapping[(feature, label)], self._label_mapping[value]) + ) + return encoding + + def labels(self): + return self._labels + + def describe(self, fid): + for (feature, label) in self._mapping: + if self._mapping[(feature, label)] == fid: + return (feature, label) + + def length(self): + return len(self._mapping) + + @classmethod + def train(cls, train_toks, count_cutoff=0, labels=None, **options): + mapping = OrderedDict() + if not labels: + labels = [] + + # This gets read twice, so compute the values in case it's lazy. + train_toks = list(train_toks) + + for (featureset, label) in train_toks: + if label not in labels: + labels.append(label) + + for (featureset, label) in train_toks: + for label in labels: + for feature in featureset: + if (feature, label) not in mapping: + mapping[(feature, label)] = len(mapping) + + return cls(labels, mapping, **options) + + +class TypedMaxentFeatureEncoding(MaxentFeatureEncodingI): + """ + A feature encoding that generates vectors containing integer, + float and binary joint-features of the form: + + Binary (for string and boolean features): + + | joint_feat(fs, l) = { 1 if (fs[fname] == fval) and (l == label) + | { + | { 0 otherwise + + Value (for integer and float features): + + | joint_feat(fs, l) = { fval if (fs[fname] == type(fval)) + | { and (l == label) + | { + | { not encoded otherwise + + Where ``fname`` is the name of an input-feature, ``fval`` is a value + for that input-feature, and ``label`` is a label. + + Typically, these features are constructed based on a training + corpus, using the ``train()`` method. + + For string and boolean features [type(fval) not in (int, float)] + this method will create one feature for each combination of + ``fname``, ``fval``, and ``label`` that occurs at least once in the + training corpus. + + For integer and float features [type(fval) in (int, float)] this + method will create one feature for each combination of ``fname`` + and ``label`` that occurs at least once in the training corpus. + + For binary features the ``unseen_features`` parameter can be used + to add "unseen-value features", which are used whenever an input + feature has a value that was not encountered in the training + corpus. These features have the form: + + | joint_feat(fs, l) = { 1 if is_unseen(fname, fs[fname]) + | { and l == label + | { + | { 0 otherwise + + Where ``is_unseen(fname, fval)`` is true if the encoding does not + contain any joint features that are true when ``fs[fname]==fval``. + + The ``alwayson_features`` parameter can be used to add "always-on + features", which have the form: + + | joint_feat(fs, l) = { 1 if (l == label) + | { + | { 0 otherwise + + These always-on features allow the maxent model to directly model + the prior probabilities of each label. + """ + + def __init__(self, labels, mapping, unseen_features=False, alwayson_features=False): + """ + :param labels: A list of the \"known labels\" for this encoding. + + :param mapping: A dictionary mapping from ``(fname,fval,label)`` + tuples to corresponding joint-feature indexes. These + indexes must be the set of integers from 0...len(mapping). + If ``mapping[fname,fval,label]=id``, then + ``self.encode({..., fname:fval, ...``, label)[id]} is 1; + otherwise, it is 0. + + :param unseen_features: If true, then include unseen value + features in the generated joint-feature vectors. + + :param alwayson_features: If true, then include always-on + features in the generated joint-feature vectors. + """ + if set(mapping.values()) != set(range(len(mapping))): + raise ValueError( + "Mapping values must be exactly the " + "set of integers from 0...len(mapping)" + ) + + self._labels = list(labels) + """A list of attested labels.""" + + self._mapping = mapping + """dict mapping from (fname,fval,label) -> fid""" + + self._length = len(mapping) + """The length of generated joint feature vectors.""" + + self._alwayson = None + """dict mapping from label -> fid""" + + self._unseen = None + """dict mapping from fname -> fid""" + + if alwayson_features: + self._alwayson = { + label: i + self._length for (i, label) in enumerate(labels) + } + self._length += len(self._alwayson) + + if unseen_features: + fnames = {fname for (fname, fval, label) in mapping} + self._unseen = {fname: i + self._length for (i, fname) in enumerate(fnames)} + self._length += len(fnames) + + def encode(self, featureset, label): + # Inherit docs. + encoding = [] + + # Convert input-features to joint-features: + for fname, fval in featureset.items(): + if isinstance(fval, (int, float)): + # Known feature name & value: + if (fname, type(fval), label) in self._mapping: + encoding.append((self._mapping[fname, type(fval), label], fval)) + else: + # Known feature name & value: + if (fname, fval, label) in self._mapping: + encoding.append((self._mapping[fname, fval, label], 1)) + + # Otherwise, we might want to fire an "unseen-value feature". + elif self._unseen: + # Have we seen this fname/fval combination with any label? + for label2 in self._labels: + if (fname, fval, label2) in self._mapping: + break # we've seen this fname/fval combo + # We haven't -- fire the unseen-value feature + else: + if fname in self._unseen: + encoding.append((self._unseen[fname], 1)) + + # Add always-on features: + if self._alwayson and label in self._alwayson: + encoding.append((self._alwayson[label], 1)) + + return encoding + + def describe(self, f_id): + # Inherit docs. + if not isinstance(f_id, int): + raise TypeError("describe() expected an int") + try: + self._inv_mapping + except AttributeError: + self._inv_mapping = [-1] * len(self._mapping) + for (info, i) in self._mapping.items(): + self._inv_mapping[i] = info + + if f_id < len(self._mapping): + (fname, fval, label) = self._inv_mapping[f_id] + return f"{fname}=={fval!r} and label is {label!r}" + elif self._alwayson and f_id in self._alwayson.values(): + for (label, f_id2) in self._alwayson.items(): + if f_id == f_id2: + return "label is %r" % label + elif self._unseen and f_id in self._unseen.values(): + for (fname, f_id2) in self._unseen.items(): + if f_id == f_id2: + return "%s is unseen" % fname + else: + raise ValueError("Bad feature id") + + def labels(self): + # Inherit docs. + return self._labels + + def length(self): + # Inherit docs. + return self._length + + @classmethod + def train(cls, train_toks, count_cutoff=0, labels=None, **options): + """ + Construct and return new feature encoding, based on a given + training corpus ``train_toks``. See the class description + ``TypedMaxentFeatureEncoding`` for a description of the + joint-features that will be included in this encoding. + + Note: recognized feature values types are (int, float), over + types are interpreted as regular binary features. + + :type train_toks: list(tuple(dict, str)) + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a feature dictionary, + and the second of which is a classification label. + + :type count_cutoff: int + :param count_cutoff: A cutoff value that is used to discard + rare joint-features. If a joint-feature's value is 1 + fewer than ``count_cutoff`` times in the training corpus, + then that joint-feature is not included in the generated + encoding. + + :type labels: list + :param labels: A list of labels that should be used by the + classifier. If not specified, then the set of labels + attested in ``train_toks`` will be used. + + :param options: Extra parameters for the constructor, such as + ``unseen_features`` and ``alwayson_features``. + """ + mapping = {} # maps (fname, fval, label) -> fid + seen_labels = set() # The set of labels we've encountered + count = defaultdict(int) # maps (fname, fval) -> count + + for (tok, label) in train_toks: + if labels and label not in labels: + raise ValueError("Unexpected label %s" % label) + seen_labels.add(label) + + # Record each of the features. + for (fname, fval) in tok.items(): + if type(fval) in (int, float): + fval = type(fval) + # If a count cutoff is given, then only add a joint + # feature once the corresponding (fname, fval, label) + # tuple exceeds that cutoff. + count[fname, fval] += 1 + if count[fname, fval] >= count_cutoff: + if (fname, fval, label) not in mapping: + mapping[fname, fval, label] = len(mapping) + + if labels is None: + labels = seen_labels + return cls(labels, mapping, **options) + + +###################################################################### +# { Classifier Trainer: Generalized Iterative Scaling +###################################################################### + + +def train_maxent_classifier_with_gis( + train_toks, trace=3, encoding=None, labels=None, **cutoffs +): + """ + Train a new ``ConditionalExponentialClassifier``, using the given + training samples, using the Generalized Iterative Scaling + algorithm. This ``ConditionalExponentialClassifier`` will encode + the model that maximizes entropy from all the models that are + empirically consistent with ``train_toks``. + + :see: ``train_maxent_classifier()`` for parameter descriptions. + """ + cutoffs.setdefault("max_iter", 100) + cutoffchecker = CutoffChecker(cutoffs) + + # Construct an encoding from the training data. + if encoding is None: + encoding = GISEncoding.train(train_toks, labels=labels) + + if not hasattr(encoding, "C"): + raise TypeError( + "The GIS algorithm requires an encoding that " + "defines C (e.g., GISEncoding)." + ) + + # Cinv is the inverse of the sum of each joint feature vector. + # This controls the learning rate: higher Cinv (or lower C) gives + # faster learning. + Cinv = 1.0 / encoding.C + + # Count how many times each feature occurs in the training data. + empirical_fcount = calculate_empirical_fcount(train_toks, encoding) + + # Check for any features that are not attested in train_toks. + unattested = set(numpy.nonzero(empirical_fcount == 0)[0]) + + # Build the classifier. Start with weight=0 for each attested + # feature, and weight=-infinity for each unattested feature. + weights = numpy.zeros(len(empirical_fcount), "d") + for fid in unattested: + weights[fid] = numpy.NINF + classifier = ConditionalExponentialClassifier(encoding, weights) + + # Take the log of the empirical fcount. + log_empirical_fcount = numpy.log2(empirical_fcount) + del empirical_fcount + + if trace > 0: + print(" ==> Training (%d iterations)" % cutoffs["max_iter"]) + if trace > 2: + print() + print(" Iteration Log Likelihood Accuracy") + print(" ---------------------------------------") + + # Train the classifier. + try: + while True: + if trace > 2: + ll = cutoffchecker.ll or log_likelihood(classifier, train_toks) + acc = cutoffchecker.acc or accuracy(classifier, train_toks) + iternum = cutoffchecker.iter + print(" %9d %14.5f %9.3f" % (iternum, ll, acc)) + + # Use the model to estimate the number of times each + # feature should occur in the training data. + estimated_fcount = calculate_estimated_fcount( + classifier, train_toks, encoding + ) + + # Take the log of estimated fcount (avoid taking log(0).) + for fid in unattested: + estimated_fcount[fid] += 1 + log_estimated_fcount = numpy.log2(estimated_fcount) + del estimated_fcount + + # Update the classifier weights + weights = classifier.weights() + weights += (log_empirical_fcount - log_estimated_fcount) * Cinv + classifier.set_weights(weights) + + # Check the log-likelihood & accuracy cutoffs. + if cutoffchecker.check(classifier, train_toks): + break + + except KeyboardInterrupt: + print(" Training stopped: keyboard interrupt") + except: + raise + + if trace > 2: + ll = log_likelihood(classifier, train_toks) + acc = accuracy(classifier, train_toks) + print(f" Final {ll:14.5f} {acc:9.3f}") + + # Return the classifier. + return classifier + + +def calculate_empirical_fcount(train_toks, encoding): + fcount = numpy.zeros(encoding.length(), "d") + + for tok, label in train_toks: + for (index, val) in encoding.encode(tok, label): + fcount[index] += val + + return fcount + + +def calculate_estimated_fcount(classifier, train_toks, encoding): + fcount = numpy.zeros(encoding.length(), "d") + + for tok, label in train_toks: + pdist = classifier.prob_classify(tok) + for label in pdist.samples(): + prob = pdist.prob(label) + for (fid, fval) in encoding.encode(tok, label): + fcount[fid] += prob * fval + + return fcount + + +###################################################################### +# { Classifier Trainer: Improved Iterative Scaling +###################################################################### + + +def train_maxent_classifier_with_iis( + train_toks, trace=3, encoding=None, labels=None, **cutoffs +): + """ + Train a new ``ConditionalExponentialClassifier``, using the given + training samples, using the Improved Iterative Scaling algorithm. + This ``ConditionalExponentialClassifier`` will encode the model + that maximizes entropy from all the models that are empirically + consistent with ``train_toks``. + + :see: ``train_maxent_classifier()`` for parameter descriptions. + """ + cutoffs.setdefault("max_iter", 100) + cutoffchecker = CutoffChecker(cutoffs) + + # Construct an encoding from the training data. + if encoding is None: + encoding = BinaryMaxentFeatureEncoding.train(train_toks, labels=labels) + + # Count how many times each feature occurs in the training data. + empirical_ffreq = calculate_empirical_fcount(train_toks, encoding) / len(train_toks) + + # Find the nf map, and related variables nfarray and nfident. + # nf is the sum of the features for a given labeled text. + # nfmap compresses this sparse set of values to a dense list. + # nfarray performs the reverse operation. nfident is + # nfarray multiplied by an identity matrix. + nfmap = calculate_nfmap(train_toks, encoding) + nfarray = numpy.array(sorted(nfmap, key=nfmap.__getitem__), "d") + nftranspose = numpy.reshape(nfarray, (len(nfarray), 1)) + + # Check for any features that are not attested in train_toks. + unattested = set(numpy.nonzero(empirical_ffreq == 0)[0]) + + # Build the classifier. Start with weight=0 for each attested + # feature, and weight=-infinity for each unattested feature. + weights = numpy.zeros(len(empirical_ffreq), "d") + for fid in unattested: + weights[fid] = numpy.NINF + classifier = ConditionalExponentialClassifier(encoding, weights) + + if trace > 0: + print(" ==> Training (%d iterations)" % cutoffs["max_iter"]) + if trace > 2: + print() + print(" Iteration Log Likelihood Accuracy") + print(" ---------------------------------------") + + # Train the classifier. + try: + while True: + if trace > 2: + ll = cutoffchecker.ll or log_likelihood(classifier, train_toks) + acc = cutoffchecker.acc or accuracy(classifier, train_toks) + iternum = cutoffchecker.iter + print(" %9d %14.5f %9.3f" % (iternum, ll, acc)) + + # Calculate the deltas for this iteration, using Newton's method. + deltas = calculate_deltas( + train_toks, + classifier, + unattested, + empirical_ffreq, + nfmap, + nfarray, + nftranspose, + encoding, + ) + + # Use the deltas to update our weights. + weights = classifier.weights() + weights += deltas + classifier.set_weights(weights) + + # Check the log-likelihood & accuracy cutoffs. + if cutoffchecker.check(classifier, train_toks): + break + + except KeyboardInterrupt: + print(" Training stopped: keyboard interrupt") + except: + raise + + if trace > 2: + ll = log_likelihood(classifier, train_toks) + acc = accuracy(classifier, train_toks) + print(f" Final {ll:14.5f} {acc:9.3f}") + + # Return the classifier. + return classifier + + +def calculate_nfmap(train_toks, encoding): + """ + Construct a map that can be used to compress ``nf`` (which is + typically sparse). + + *nf(feature_vector)* is the sum of the feature values for + *feature_vector*. + + This represents the number of features that are active for a + given labeled text. This method finds all values of *nf(t)* + that are attested for at least one token in the given list of + training tokens; and constructs a dictionary mapping these + attested values to a continuous range *0...N*. For example, + if the only values of *nf()* that were attested were 3, 5, and + 7, then ``_nfmap`` might return the dictionary ``{3:0, 5:1, 7:2}``. + + :return: A map that can be used to compress ``nf`` to a dense + vector. + :rtype: dict(int -> int) + """ + # Map from nf to indices. This allows us to use smaller arrays. + nfset = set() + for tok, _ in train_toks: + for label in encoding.labels(): + nfset.add(sum(val for (id, val) in encoding.encode(tok, label))) + return {nf: i for (i, nf) in enumerate(nfset)} + + +def calculate_deltas( + train_toks, + classifier, + unattested, + ffreq_empirical, + nfmap, + nfarray, + nftranspose, + encoding, +): + r""" + Calculate the update values for the classifier weights for + this iteration of IIS. These update weights are the value of + ``delta`` that solves the equation:: + + ffreq_empirical[i] + = + SUM[fs,l] (classifier.prob_classify(fs).prob(l) * + feature_vector(fs,l)[i] * + exp(delta[i] * nf(feature_vector(fs,l)))) + + Where: + - *(fs,l)* is a (featureset, label) tuple from ``train_toks`` + - *feature_vector(fs,l)* = ``encoding.encode(fs,l)`` + - *nf(vector)* = ``sum([val for (id,val) in vector])`` + + This method uses Newton's method to solve this equation for + *delta[i]*. In particular, it starts with a guess of + ``delta[i]`` = 1; and iteratively updates ``delta`` with: + + | delta[i] -= (ffreq_empirical[i] - sum1[i])/(-sum2[i]) + + until convergence, where *sum1* and *sum2* are defined as: + + | sum1[i](delta) = SUM[fs,l] f[i](fs,l,delta) + | sum2[i](delta) = SUM[fs,l] (f[i](fs,l,delta).nf(feature_vector(fs,l))) + | f[i](fs,l,delta) = (classifier.prob_classify(fs).prob(l) . + | feature_vector(fs,l)[i] . + | exp(delta[i] . nf(feature_vector(fs,l)))) + + Note that *sum1* and *sum2* depend on ``delta``; so they need + to be re-computed each iteration. + + The variables ``nfmap``, ``nfarray``, and ``nftranspose`` are + used to generate a dense encoding for *nf(ltext)*. This + allows ``_deltas`` to calculate *sum1* and *sum2* using + matrices, which yields a significant performance improvement. + + :param train_toks: The set of training tokens. + :type train_toks: list(tuple(dict, str)) + :param classifier: The current classifier. + :type classifier: ClassifierI + :param ffreq_empirical: An array containing the empirical + frequency for each feature. The *i*\ th element of this + array is the empirical frequency for feature *i*. + :type ffreq_empirical: sequence of float + :param unattested: An array that is 1 for features that are + not attested in the training data; and 0 for features that + are attested. In other words, ``unattested[i]==0`` iff + ``ffreq_empirical[i]==0``. + :type unattested: sequence of int + :param nfmap: A map that can be used to compress ``nf`` to a dense + vector. + :type nfmap: dict(int -> int) + :param nfarray: An array that can be used to uncompress ``nf`` + from a dense vector. + :type nfarray: array(float) + :param nftranspose: The transpose of ``nfarray`` + :type nftranspose: array(float) + """ + # These parameters control when we decide that we've + # converged. It probably should be possible to set these + # manually, via keyword arguments to train. + NEWTON_CONVERGE = 1e-12 + MAX_NEWTON = 300 + + deltas = numpy.ones(encoding.length(), "d") + + # Precompute the A matrix: + # A[nf][id] = sum ( p(fs) * p(label|fs) * f(fs,label) ) + # over all label,fs s.t. num_features[label,fs]=nf + A = numpy.zeros((len(nfmap), encoding.length()), "d") + + for tok, label in train_toks: + dist = classifier.prob_classify(tok) + + for label in encoding.labels(): + # Generate the feature vector + feature_vector = encoding.encode(tok, label) + # Find the number of active features + nf = sum(val for (id, val) in feature_vector) + # Update the A matrix + for (id, val) in feature_vector: + A[nfmap[nf], id] += dist.prob(label) * val + A /= len(train_toks) + + # Iteratively solve for delta. Use the following variables: + # - nf_delta[x][y] = nfarray[x] * delta[y] + # - exp_nf_delta[x][y] = exp(nf[x] * delta[y]) + # - nf_exp_nf_delta[x][y] = nf[x] * exp(nf[x] * delta[y]) + # - sum1[i][nf] = sum p(fs)p(label|fs)f[i](label,fs) + # exp(delta[i]nf) + # - sum2[i][nf] = sum p(fs)p(label|fs)f[i](label,fs) + # nf exp(delta[i]nf) + for rangenum in range(MAX_NEWTON): + nf_delta = numpy.outer(nfarray, deltas) + exp_nf_delta = 2**nf_delta + nf_exp_nf_delta = nftranspose * exp_nf_delta + sum1 = numpy.sum(exp_nf_delta * A, axis=0) + sum2 = numpy.sum(nf_exp_nf_delta * A, axis=0) + + # Avoid division by zero. + for fid in unattested: + sum2[fid] += 1 + + # Update the deltas. + deltas -= (ffreq_empirical - sum1) / -sum2 + + # We can stop once we converge. + n_error = numpy.sum(abs(ffreq_empirical - sum1)) / numpy.sum(abs(deltas)) + if n_error < NEWTON_CONVERGE: + return deltas + + return deltas + + +###################################################################### +# { Classifier Trainer: megam +###################################################################### + +# [xx] possible extension: add support for using implicit file format; +# this would need to put requirements on what encoding is used. But +# we may need this for other maxent classifier trainers that require +# implicit formats anyway. +def train_maxent_classifier_with_megam( + train_toks, trace=3, encoding=None, labels=None, gaussian_prior_sigma=0, **kwargs +): + """ + Train a new ``ConditionalExponentialClassifier``, using the given + training samples, using the external ``megam`` library. This + ``ConditionalExponentialClassifier`` will encode the model that + maximizes entropy from all the models that are empirically + consistent with ``train_toks``. + + :see: ``train_maxent_classifier()`` for parameter descriptions. + :see: ``nltk.classify.megam`` + """ + + explicit = True + bernoulli = True + if "explicit" in kwargs: + explicit = kwargs["explicit"] + if "bernoulli" in kwargs: + bernoulli = kwargs["bernoulli"] + + # Construct an encoding from the training data. + if encoding is None: + # Count cutoff can also be controlled by megam with the -minfc + # option. Not sure where the best place for it is. + count_cutoff = kwargs.get("count_cutoff", 0) + encoding = BinaryMaxentFeatureEncoding.train( + train_toks, count_cutoff, labels=labels, alwayson_features=True + ) + elif labels is not None: + raise ValueError("Specify encoding or labels, not both") + + # Write a training file for megam. + try: + fd, trainfile_name = tempfile.mkstemp(prefix="nltk-") + with open(trainfile_name, "w") as trainfile: + write_megam_file( + train_toks, encoding, trainfile, explicit=explicit, bernoulli=bernoulli + ) + os.close(fd) + except (OSError, ValueError) as e: + raise ValueError("Error while creating megam training file: %s" % e) from e + + # Run megam on the training file. + options = [] + options += ["-nobias", "-repeat", "10"] + if explicit: + options += ["-explicit"] + if not bernoulli: + options += ["-fvals"] + if gaussian_prior_sigma: + # Lambda is just the precision of the Gaussian prior, i.e. it's the + # inverse variance, so the parameter conversion is 1.0/sigma**2. + # See https://users.umiacs.umd.edu/~hal/docs/daume04cg-bfgs.pdf + inv_variance = 1.0 / gaussian_prior_sigma**2 + else: + inv_variance = 0 + options += ["-lambda", "%.2f" % inv_variance, "-tune"] + if trace < 3: + options += ["-quiet"] + if "max_iter" in kwargs: + options += ["-maxi", "%s" % kwargs["max_iter"]] + if "ll_delta" in kwargs: + # [xx] this is actually a perplexity delta, not a log + # likelihood delta + options += ["-dpp", "%s" % abs(kwargs["ll_delta"])] + if hasattr(encoding, "cost"): + options += ["-multilabel"] # each possible la + options += ["multiclass", trainfile_name] + stdout = call_megam(options) + # print('./megam_i686.opt ', ' '.join(options)) + # Delete the training file + try: + os.remove(trainfile_name) + except OSError as e: + print(f"Warning: unable to delete {trainfile_name}: {e}") + + # Parse the generated weight vector. + weights = parse_megam_weights(stdout, encoding.length(), explicit) + + # Convert from base-e to base-2 weights. + weights *= numpy.log2(numpy.e) + + # Build the classifier + return MaxentClassifier(encoding, weights) + + +###################################################################### +# { Classifier Trainer: tadm +###################################################################### + + +class TadmMaxentClassifier(MaxentClassifier): + @classmethod + def train(cls, train_toks, **kwargs): + algorithm = kwargs.get("algorithm", "tao_lmvm") + trace = kwargs.get("trace", 3) + encoding = kwargs.get("encoding", None) + labels = kwargs.get("labels", None) + sigma = kwargs.get("gaussian_prior_sigma", 0) + count_cutoff = kwargs.get("count_cutoff", 0) + max_iter = kwargs.get("max_iter") + ll_delta = kwargs.get("min_lldelta") + + # Construct an encoding from the training data. + if not encoding: + encoding = TadmEventMaxentFeatureEncoding.train( + train_toks, count_cutoff, labels=labels + ) + + trainfile_fd, trainfile_name = tempfile.mkstemp( + prefix="nltk-tadm-events-", suffix=".gz" + ) + weightfile_fd, weightfile_name = tempfile.mkstemp(prefix="nltk-tadm-weights-") + + trainfile = gzip_open_unicode(trainfile_name, "w") + write_tadm_file(train_toks, encoding, trainfile) + trainfile.close() + + options = [] + options.extend(["-monitor"]) + options.extend(["-method", algorithm]) + if sigma: + options.extend(["-l2", "%.6f" % sigma**2]) + if max_iter: + options.extend(["-max_it", "%d" % max_iter]) + if ll_delta: + options.extend(["-fatol", "%.6f" % abs(ll_delta)]) + options.extend(["-events_in", trainfile_name]) + options.extend(["-params_out", weightfile_name]) + if trace < 3: + options.extend(["2>&1"]) + else: + options.extend(["-summary"]) + + call_tadm(options) + + with open(weightfile_name) as weightfile: + weights = parse_tadm_weights(weightfile) + + os.remove(trainfile_name) + os.remove(weightfile_name) + + # Convert from base-e to base-2 weights. + weights *= numpy.log2(numpy.e) + + # Build the classifier + return cls(encoding, weights) + + +###################################################################### +# { Demo +###################################################################### +def demo(): + from nltk.classify.util import names_demo + + classifier = names_demo(MaxentClassifier.train) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/megam.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/megam.py new file mode 100644 index 0000000..d02af12 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/megam.py @@ -0,0 +1,184 @@ +# Natural Language Toolkit: Interface to Megam Classifier +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A set of functions used to interface with the external megam_ maxent +optimization package. Before megam can be used, you should tell NLTK where it +can find the megam binary, using the ``config_megam()`` function. Typical +usage: + + >>> from nltk.classify import megam + >>> megam.config_megam() # pass path to megam if not found in PATH # doctest: +SKIP + [Found megam: ...] + +Use with MaxentClassifier. Example below, see MaxentClassifier documentation +for details. + + nltk.classify.MaxentClassifier.train(corpus, 'megam') + +.. _megam: https://www.umiacs.umd.edu/~hal/megam/index.html +""" +import subprocess + +from nltk.internals import find_binary + +try: + import numpy +except ImportError: + numpy = None + +###################################################################### +# { Configuration +###################################################################### + +_megam_bin = None + + +def config_megam(bin=None): + """ + Configure NLTK's interface to the ``megam`` maxent optimization + package. + + :param bin: The full path to the ``megam`` binary. If not specified, + then nltk will search the system for a ``megam`` binary; and if + one is not found, it will raise a ``LookupError`` exception. + :type bin: str + """ + global _megam_bin + _megam_bin = find_binary( + "megam", + bin, + env_vars=["MEGAM"], + binary_names=["megam.opt", "megam", "megam_686", "megam_i686.opt"], + url="https://www.umiacs.umd.edu/~hal/megam/index.html", + ) + + +###################################################################### +# { Megam Interface Functions +###################################################################### + + +def write_megam_file(train_toks, encoding, stream, bernoulli=True, explicit=True): + """ + Generate an input file for ``megam`` based on the given corpus of + classified tokens. + + :type train_toks: list(tuple(dict, str)) + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a feature dictionary, + and the second of which is a classification label. + + :type encoding: MaxentFeatureEncodingI + :param encoding: A feature encoding, used to convert featuresets + into feature vectors. May optionally implement a cost() method + in order to assign different costs to different class predictions. + + :type stream: stream + :param stream: The stream to which the megam input file should be + written. + + :param bernoulli: If true, then use the 'bernoulli' format. I.e., + all joint features have binary values, and are listed iff they + are true. Otherwise, list feature values explicitly. If + ``bernoulli=False``, then you must call ``megam`` with the + ``-fvals`` option. + + :param explicit: If true, then use the 'explicit' format. I.e., + list the features that would fire for any of the possible + labels, for each token. If ``explicit=True``, then you must + call ``megam`` with the ``-explicit`` option. + """ + # Look up the set of labels. + labels = encoding.labels() + labelnum = {label: i for (i, label) in enumerate(labels)} + + # Write the file, which contains one line per instance. + for featureset, label in train_toks: + # First, the instance number (or, in the weighted multiclass case, the cost of each label). + if hasattr(encoding, "cost"): + stream.write( + ":".join(str(encoding.cost(featureset, label, l)) for l in labels) + ) + else: + stream.write("%d" % labelnum[label]) + + # For implicit file formats, just list the features that fire + # for this instance's actual label. + if not explicit: + _write_megam_features(encoding.encode(featureset, label), stream, bernoulli) + + # For explicit formats, list the features that would fire for + # any of the possible labels. + else: + for l in labels: + stream.write(" #") + _write_megam_features(encoding.encode(featureset, l), stream, bernoulli) + + # End of the instance. + stream.write("\n") + + +def parse_megam_weights(s, features_count, explicit=True): + """ + Given the stdout output generated by ``megam`` when training a + model, return a ``numpy`` array containing the corresponding weight + vector. This function does not currently handle bias features. + """ + if numpy is None: + raise ValueError("This function requires that numpy be installed") + assert explicit, "non-explicit not supported yet" + lines = s.strip().split("\n") + weights = numpy.zeros(features_count, "d") + for line in lines: + if line.strip(): + fid, weight = line.split() + weights[int(fid)] = float(weight) + return weights + + +def _write_megam_features(vector, stream, bernoulli): + if not vector: + raise ValueError( + "MEGAM classifier requires the use of an " "always-on feature." + ) + for (fid, fval) in vector: + if bernoulli: + if fval == 1: + stream.write(" %s" % fid) + elif fval != 0: + raise ValueError( + "If bernoulli=True, then all" "features must be binary." + ) + else: + stream.write(f" {fid} {fval}") + + +def call_megam(args): + """ + Call the ``megam`` binary with the given arguments. + """ + if isinstance(args, str): + raise TypeError("args should be a list of strings") + if _megam_bin is None: + config_megam() + + # Call megam via a subprocess + cmd = [_megam_bin] + args + p = subprocess.Popen(cmd, stdout=subprocess.PIPE) + (stdout, stderr) = p.communicate() + + # Check the return code. + if p.returncode != 0: + print() + print(stderr) + raise OSError("megam command failed!") + + if isinstance(stdout, str): + return stdout + else: + return stdout.decode("utf-8") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/naivebayes.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/naivebayes.py new file mode 100644 index 0000000..0cda42f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/naivebayes.py @@ -0,0 +1,260 @@ +# Natural Language Toolkit: Naive Bayes Classifiers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A classifier based on the Naive Bayes algorithm. In order to find the +probability for a label, this algorithm first uses the Bayes rule to +express P(label|features) in terms of P(label) and P(features|label): + +| P(label) * P(features|label) +| P(label|features) = ------------------------------ +| P(features) + +The algorithm then makes the 'naive' assumption that all features are +independent, given the label: + +| P(label) * P(f1|label) * ... * P(fn|label) +| P(label|features) = -------------------------------------------- +| P(features) + +Rather than computing P(features) explicitly, the algorithm just +calculates the numerator for each label, and normalizes them so they +sum to one: + +| P(label) * P(f1|label) * ... * P(fn|label) +| P(label|features) = -------------------------------------------- +| SUM[l]( P(l) * P(f1|l) * ... * P(fn|l) ) +""" + +from collections import defaultdict + +from nltk.classify.api import ClassifierI +from nltk.probability import DictionaryProbDist, ELEProbDist, FreqDist, sum_logs + +##////////////////////////////////////////////////////// +## Naive Bayes Classifier +##////////////////////////////////////////////////////// + + +class NaiveBayesClassifier(ClassifierI): + """ + A Naive Bayes classifier. Naive Bayes classifiers are + paramaterized by two probability distributions: + + - P(label) gives the probability that an input will receive each + label, given no information about the input's features. + + - P(fname=fval|label) gives the probability that a given feature + (fname) will receive a given value (fval), given that the + label (label). + + If the classifier encounters an input with a feature that has + never been seen with any label, then rather than assigning a + probability of 0 to all labels, it will ignore that feature. + + The feature value 'None' is reserved for unseen feature values; + you generally should not use 'None' as a feature value for one of + your own features. + """ + + def __init__(self, label_probdist, feature_probdist): + """ + :param label_probdist: P(label), the probability distribution + over labels. It is expressed as a ``ProbDistI`` whose + samples are labels. I.e., P(label) = + ``label_probdist.prob(label)``. + + :param feature_probdist: P(fname=fval|label), the probability + distribution for feature values, given labels. It is + expressed as a dictionary whose keys are ``(label, fname)`` + pairs and whose values are ``ProbDistI`` objects over feature + values. I.e., P(fname=fval|label) = + ``feature_probdist[label,fname].prob(fval)``. If a given + ``(label,fname)`` is not a key in ``feature_probdist``, then + it is assumed that the corresponding P(fname=fval|label) + is 0 for all values of ``fval``. + """ + self._label_probdist = label_probdist + self._feature_probdist = feature_probdist + self._labels = list(label_probdist.samples()) + + def labels(self): + return self._labels + + def classify(self, featureset): + return self.prob_classify(featureset).max() + + def prob_classify(self, featureset): + # Discard any feature names that we've never seen before. + # Otherwise, we'll just assign a probability of 0 to + # everything. + featureset = featureset.copy() + for fname in list(featureset.keys()): + for label in self._labels: + if (label, fname) in self._feature_probdist: + break + else: + # print('Ignoring unseen feature %s' % fname) + del featureset[fname] + + # Find the log probability of each label, given the features. + # Start with the log probability of the label itself. + logprob = {} + for label in self._labels: + logprob[label] = self._label_probdist.logprob(label) + + # Then add in the log probability of features given labels. + for label in self._labels: + for (fname, fval) in featureset.items(): + if (label, fname) in self._feature_probdist: + feature_probs = self._feature_probdist[label, fname] + logprob[label] += feature_probs.logprob(fval) + else: + # nb: This case will never come up if the + # classifier was created by + # NaiveBayesClassifier.train(). + logprob[label] += sum_logs([]) # = -INF. + + return DictionaryProbDist(logprob, normalize=True, log=True) + + def show_most_informative_features(self, n=10): + # Determine the most relevant features, and display them. + cpdist = self._feature_probdist + print("Most Informative Features") + + for (fname, fval) in self.most_informative_features(n): + + def labelprob(l): + return cpdist[l, fname].prob(fval) + + labels = sorted( + (l for l in self._labels if fval in cpdist[l, fname].samples()), + key=lambda element: (-labelprob(element), element), + reverse=True, + ) + if len(labels) == 1: + continue + l0 = labels[0] + l1 = labels[-1] + if cpdist[l0, fname].prob(fval) == 0: + ratio = "INF" + else: + ratio = "%8.1f" % ( + cpdist[l1, fname].prob(fval) / cpdist[l0, fname].prob(fval) + ) + print( + "%24s = %-14r %6s : %-6s = %s : 1.0" + % (fname, fval, ("%s" % l1)[:6], ("%s" % l0)[:6], ratio) + ) + + def most_informative_features(self, n=100): + """ + Return a list of the 'most informative' features used by this + classifier. For the purpose of this function, the + informativeness of a feature ``(fname,fval)`` is equal to the + highest value of P(fname=fval|label), for any label, divided by + the lowest value of P(fname=fval|label), for any label: + + | max[ P(fname=fval|label1) / P(fname=fval|label2) ] + """ + if hasattr(self, "_most_informative_features"): + return self._most_informative_features[:n] + else: + # The set of (fname, fval) pairs used by this classifier. + features = set() + # The max & min probability associated w/ each (fname, fval) + # pair. Maps (fname,fval) -> float. + maxprob = defaultdict(lambda: 0.0) + minprob = defaultdict(lambda: 1.0) + + for (label, fname), probdist in self._feature_probdist.items(): + for fval in probdist.samples(): + feature = (fname, fval) + features.add(feature) + p = probdist.prob(fval) + maxprob[feature] = max(p, maxprob[feature]) + minprob[feature] = min(p, minprob[feature]) + if minprob[feature] == 0: + features.discard(feature) + + # Convert features to a list, & sort it by how informative + # features are. + self._most_informative_features = sorted( + features, + key=lambda feature_: ( + minprob[feature_] / maxprob[feature_], + feature_[0], + feature_[1] in [None, False, True], + str(feature_[1]).lower(), + ), + ) + return self._most_informative_features[:n] + + @classmethod + def train(cls, labeled_featuresets, estimator=ELEProbDist): + """ + :param labeled_featuresets: A list of classified featuresets, + i.e., a list of tuples ``(featureset, label)``. + """ + label_freqdist = FreqDist() + feature_freqdist = defaultdict(FreqDist) + feature_values = defaultdict(set) + fnames = set() + + # Count up how many times each feature value occurred, given + # the label and featurename. + for featureset, label in labeled_featuresets: + label_freqdist[label] += 1 + for fname, fval in featureset.items(): + # Increment freq(fval|label, fname) + feature_freqdist[label, fname][fval] += 1 + # Record that fname can take the value fval. + feature_values[fname].add(fval) + # Keep a list of all feature names. + fnames.add(fname) + + # If a feature didn't have a value given for an instance, then + # we assume that it gets the implicit value 'None.' This loop + # counts up the number of 'missing' feature values for each + # (label,fname) pair, and increments the count of the fval + # 'None' by that amount. + for label in label_freqdist: + num_samples = label_freqdist[label] + for fname in fnames: + count = feature_freqdist[label, fname].N() + # Only add a None key when necessary, i.e. if there are + # any samples with feature 'fname' missing. + if num_samples - count > 0: + feature_freqdist[label, fname][None] += num_samples - count + feature_values[fname].add(None) + + # Create the P(label) distribution + label_probdist = estimator(label_freqdist) + + # Create the P(fval|label, fname) distribution + feature_probdist = {} + for ((label, fname), freqdist) in feature_freqdist.items(): + probdist = estimator(freqdist, bins=len(feature_values[fname])) + feature_probdist[label, fname] = probdist + + return cls(label_probdist, feature_probdist) + + +##////////////////////////////////////////////////////// +## Demo +##////////////////////////////////////////////////////// + + +def demo(): + from nltk.classify.util import names_demo + + classifier = names_demo(NaiveBayesClassifier.train) + classifier.show_most_informative_features() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/positivenaivebayes.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/positivenaivebayes.py new file mode 100644 index 0000000..cc303cc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/positivenaivebayes.py @@ -0,0 +1,180 @@ +# Natural Language Toolkit: Positive Naive Bayes Classifier +# +# Copyright (C) 2012 NLTK Project +# Author: Alessandro Presta +# URL: +# For license information, see LICENSE.TXT + +""" +A variant of the Naive Bayes Classifier that performs binary classification with +partially-labeled training sets. In other words, assume we want to build a classifier +that assigns each example to one of two complementary classes (e.g., male names and +female names). +If we have a training set with labeled examples for both classes, we can use a +standard Naive Bayes Classifier. However, consider the case when we only have labeled +examples for one of the classes, and other, unlabeled, examples. +Then, assuming a prior distribution on the two labels, we can use the unlabeled set +to estimate the frequencies of the various features. + +Let the two possible labels be 1 and 0, and let's say we only have examples labeled 1 +and unlabeled examples. We are also given an estimate of P(1). + +We compute P(feature|1) exactly as in the standard case. + +To compute P(feature|0), we first estimate P(feature) from the unlabeled set (we are +assuming that the unlabeled examples are drawn according to the given prior distribution) +and then express the conditional probability as: + +| P(feature) - P(feature|1) * P(1) +| P(feature|0) = ---------------------------------- +| P(0) + +Example: + + >>> from nltk.classify import PositiveNaiveBayesClassifier + +Some sentences about sports: + + >>> sports_sentences = [ 'The team dominated the game', + ... 'They lost the ball', + ... 'The game was intense', + ... 'The goalkeeper catched the ball', + ... 'The other team controlled the ball' ] + +Mixed topics, including sports: + + >>> various_sentences = [ 'The President did not comment', + ... 'I lost the keys', + ... 'The team won the game', + ... 'Sara has two kids', + ... 'The ball went off the court', + ... 'They had the ball for the whole game', + ... 'The show is over' ] + +The features of a sentence are simply the words it contains: + + >>> def features(sentence): + ... words = sentence.lower().split() + ... return dict(('contains(%s)' % w, True) for w in words) + +We use the sports sentences as positive examples, the mixed ones ad unlabeled examples: + + >>> positive_featuresets = map(features, sports_sentences) + >>> unlabeled_featuresets = map(features, various_sentences) + >>> classifier = PositiveNaiveBayesClassifier.train(positive_featuresets, + ... unlabeled_featuresets) + +Is the following sentence about sports? + + >>> classifier.classify(features('The cat is on the table')) + False + +What about this one? + + >>> classifier.classify(features('My team lost the game')) + True +""" + +from collections import defaultdict + +from nltk.classify.naivebayes import NaiveBayesClassifier +from nltk.probability import DictionaryProbDist, ELEProbDist, FreqDist + +##////////////////////////////////////////////////////// +## Positive Naive Bayes Classifier +##////////////////////////////////////////////////////// + + +class PositiveNaiveBayesClassifier(NaiveBayesClassifier): + @staticmethod + def train( + positive_featuresets, + unlabeled_featuresets, + positive_prob_prior=0.5, + estimator=ELEProbDist, + ): + """ + :param positive_featuresets: An iterable of featuresets that are known as positive + examples (i.e., their label is ``True``). + + :param unlabeled_featuresets: An iterable of featuresets whose label is unknown. + + :param positive_prob_prior: A prior estimate of the probability of the label + ``True`` (default 0.5). + """ + positive_feature_freqdist = defaultdict(FreqDist) + unlabeled_feature_freqdist = defaultdict(FreqDist) + feature_values = defaultdict(set) + fnames = set() + + # Count up how many times each feature value occurred in positive examples. + num_positive_examples = 0 + for featureset in positive_featuresets: + for fname, fval in featureset.items(): + positive_feature_freqdist[fname][fval] += 1 + feature_values[fname].add(fval) + fnames.add(fname) + num_positive_examples += 1 + + # Count up how many times each feature value occurred in unlabeled examples. + num_unlabeled_examples = 0 + for featureset in unlabeled_featuresets: + for fname, fval in featureset.items(): + unlabeled_feature_freqdist[fname][fval] += 1 + feature_values[fname].add(fval) + fnames.add(fname) + num_unlabeled_examples += 1 + + # If a feature didn't have a value given for an instance, then we assume that + # it gets the implicit value 'None'. + for fname in fnames: + count = positive_feature_freqdist[fname].N() + positive_feature_freqdist[fname][None] += num_positive_examples - count + feature_values[fname].add(None) + + for fname in fnames: + count = unlabeled_feature_freqdist[fname].N() + unlabeled_feature_freqdist[fname][None] += num_unlabeled_examples - count + feature_values[fname].add(None) + + negative_prob_prior = 1.0 - positive_prob_prior + + # Create the P(label) distribution. + label_probdist = DictionaryProbDist( + {True: positive_prob_prior, False: negative_prob_prior} + ) + + # Create the P(fval|label, fname) distribution. + feature_probdist = {} + for fname, freqdist in positive_feature_freqdist.items(): + probdist = estimator(freqdist, bins=len(feature_values[fname])) + feature_probdist[True, fname] = probdist + + for fname, freqdist in unlabeled_feature_freqdist.items(): + global_probdist = estimator(freqdist, bins=len(feature_values[fname])) + negative_feature_probs = {} + for fval in feature_values[fname]: + prob = ( + global_probdist.prob(fval) + - positive_prob_prior * feature_probdist[True, fname].prob(fval) + ) / negative_prob_prior + # TODO: We need to add some kind of smoothing here, instead of + # setting negative probabilities to zero and normalizing. + negative_feature_probs[fval] = max(prob, 0.0) + feature_probdist[False, fname] = DictionaryProbDist( + negative_feature_probs, normalize=True + ) + + return PositiveNaiveBayesClassifier(label_probdist, feature_probdist) + + +##////////////////////////////////////////////////////// +## Demo +##////////////////////////////////////////////////////// + + +def demo(): + from nltk.classify.util import partial_names_demo + + classifier = partial_names_demo(PositiveNaiveBayesClassifier.train) + classifier.show_most_informative_features() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/rte_classify.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/rte_classify.py new file mode 100644 index 0000000..38bea71 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/rte_classify.py @@ -0,0 +1,183 @@ +# Natural Language Toolkit: RTE Classifier +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +Simple classifier for RTE corpus. + +It calculates the overlap in words and named entities between text and +hypothesis, and also whether there are words / named entities in the +hypothesis which fail to occur in the text, since this is an indicator that +the hypothesis is more informative than (i.e not entailed by) the text. + +TO DO: better Named Entity classification +TO DO: add lemmatization +""" + +from nltk.classify.maxent import MaxentClassifier +from nltk.classify.util import accuracy +from nltk.tokenize import RegexpTokenizer + + +class RTEFeatureExtractor: + """ + This builds a bag of words for both the text and the hypothesis after + throwing away some stopwords, then calculates overlap and difference. + """ + + def __init__(self, rtepair, stop=True, use_lemmatize=False): + """ + :param rtepair: a ``RTEPair`` from which features should be extracted + :param stop: if ``True``, stopwords are thrown away. + :type stop: bool + """ + self.stop = stop + self.stopwords = { + "a", + "the", + "it", + "they", + "of", + "in", + "to", + "is", + "have", + "are", + "were", + "and", + "very", + ".", + ",", + } + + self.negwords = {"no", "not", "never", "failed", "rejected", "denied"} + # Try to tokenize so that abbreviations, monetary amounts, email + # addresses, URLs are single tokens. + tokenizer = RegexpTokenizer(r"[\w.@:/]+|\w+|\$[\d.]+") + + # Get the set of word types for text and hypothesis + self.text_tokens = tokenizer.tokenize(rtepair.text) + self.hyp_tokens = tokenizer.tokenize(rtepair.hyp) + self.text_words = set(self.text_tokens) + self.hyp_words = set(self.hyp_tokens) + + if use_lemmatize: + self.text_words = {self._lemmatize(token) for token in self.text_tokens} + self.hyp_words = {self._lemmatize(token) for token in self.hyp_tokens} + + if self.stop: + self.text_words = self.text_words - self.stopwords + self.hyp_words = self.hyp_words - self.stopwords + + self._overlap = self.hyp_words & self.text_words + self._hyp_extra = self.hyp_words - self.text_words + self._txt_extra = self.text_words - self.hyp_words + + def overlap(self, toktype, debug=False): + """ + Compute the overlap between text and hypothesis. + + :param toktype: distinguish Named Entities from ordinary words + :type toktype: 'ne' or 'word' + """ + ne_overlap = {token for token in self._overlap if self._ne(token)} + if toktype == "ne": + if debug: + print("ne overlap", ne_overlap) + return ne_overlap + elif toktype == "word": + if debug: + print("word overlap", self._overlap - ne_overlap) + return self._overlap - ne_overlap + else: + raise ValueError("Type not recognized:'%s'" % toktype) + + def hyp_extra(self, toktype, debug=True): + """ + Compute the extraneous material in the hypothesis. + + :param toktype: distinguish Named Entities from ordinary words + :type toktype: 'ne' or 'word' + """ + ne_extra = {token for token in self._hyp_extra if self._ne(token)} + if toktype == "ne": + return ne_extra + elif toktype == "word": + return self._hyp_extra - ne_extra + else: + raise ValueError("Type not recognized: '%s'" % toktype) + + @staticmethod + def _ne(token): + """ + This just assumes that words in all caps or titles are + named entities. + + :type token: str + """ + if token.istitle() or token.isupper(): + return True + return False + + @staticmethod + def _lemmatize(word): + """ + Use morphy from WordNet to find the base form of verbs. + """ + from nltk.corpus import wordnet as wn + + lemma = wn.morphy(word, pos=wn.VERB) + if lemma is not None: + return lemma + return word + + +def rte_features(rtepair): + extractor = RTEFeatureExtractor(rtepair) + features = {} + features["alwayson"] = True + features["word_overlap"] = len(extractor.overlap("word")) + features["word_hyp_extra"] = len(extractor.hyp_extra("word")) + features["ne_overlap"] = len(extractor.overlap("ne")) + features["ne_hyp_extra"] = len(extractor.hyp_extra("ne")) + features["neg_txt"] = len(extractor.negwords & extractor.text_words) + features["neg_hyp"] = len(extractor.negwords & extractor.hyp_words) + return features + + +def rte_featurize(rte_pairs): + return [(rte_features(pair), pair.value) for pair in rte_pairs] + + +def rte_classifier(algorithm, sample_N=None): + from nltk.corpus import rte as rte_corpus + + train_set = rte_corpus.pairs(["rte1_dev.xml", "rte2_dev.xml", "rte3_dev.xml"]) + test_set = rte_corpus.pairs(["rte1_test.xml", "rte2_test.xml", "rte3_test.xml"]) + + if sample_N is not None: + train_set = train_set[:sample_N] + test_set = test_set[:sample_N] + + featurized_train_set = rte_featurize(train_set) + featurized_test_set = rte_featurize(test_set) + + # Train the classifier + print("Training classifier...") + if algorithm in ["megam"]: # MEGAM based algorithms. + clf = MaxentClassifier.train(featurized_train_set, algorithm) + elif algorithm in ["GIS", "IIS"]: # Use default GIS/IIS MaxEnt algorithm + clf = MaxentClassifier.train(featurized_train_set, algorithm) + else: + err_msg = str( + "RTEClassifier only supports these algorithms:\n " + "'megam', 'GIS', 'IIS'.\n" + ) + raise Exception(err_msg) + print("Testing classifier...") + acc = accuracy(clf, featurized_test_set) + print("Accuracy: %6.4f" % acc) + return clf diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/scikitlearn.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/scikitlearn.py new file mode 100644 index 0000000..95afc70 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/scikitlearn.py @@ -0,0 +1,143 @@ +# Natural Language Toolkit: Interface to scikit-learn classifiers +# +# Author: Lars Buitinck +# URL: +# For license information, see LICENSE.TXT +""" +scikit-learn (https://scikit-learn.org) is a machine learning library for +Python. It supports many classification algorithms, including SVMs, +Naive Bayes, logistic regression (MaxEnt) and decision trees. + +This package implements a wrapper around scikit-learn classifiers. To use this +wrapper, construct a scikit-learn estimator object, then use that to construct +a SklearnClassifier. E.g., to wrap a linear SVM with default settings: + +>>> from sklearn.svm import LinearSVC +>>> from nltk.classify.scikitlearn import SklearnClassifier +>>> classif = SklearnClassifier(LinearSVC()) + +A scikit-learn classifier may include preprocessing steps when it's wrapped +in a Pipeline object. The following constructs and wraps a Naive Bayes text +classifier with tf-idf weighting and chi-square feature selection to get the +best 1000 features: + +>>> from sklearn.feature_extraction.text import TfidfTransformer +>>> from sklearn.feature_selection import SelectKBest, chi2 +>>> from sklearn.naive_bayes import MultinomialNB +>>> from sklearn.pipeline import Pipeline +>>> pipeline = Pipeline([('tfidf', TfidfTransformer()), +... ('chi2', SelectKBest(chi2, k=1000)), +... ('nb', MultinomialNB())]) +>>> classif = SklearnClassifier(pipeline) +""" + +from nltk.classify.api import ClassifierI +from nltk.probability import DictionaryProbDist + +try: + from sklearn.feature_extraction import DictVectorizer + from sklearn.preprocessing import LabelEncoder +except ImportError: + pass + +__all__ = ["SklearnClassifier"] + + +class SklearnClassifier(ClassifierI): + """Wrapper for scikit-learn classifiers.""" + + def __init__(self, estimator, dtype=float, sparse=True): + """ + :param estimator: scikit-learn classifier object. + + :param dtype: data type used when building feature array. + scikit-learn estimators work exclusively on numeric data. The + default value should be fine for almost all situations. + + :param sparse: Whether to use sparse matrices internally. + The estimator must support these; not all scikit-learn classifiers + do (see their respective documentation and look for "sparse + matrix"). The default value is True, since most NLP problems + involve sparse feature sets. Setting this to False may take a + great amount of memory. + :type sparse: boolean. + """ + self._clf = estimator + self._encoder = LabelEncoder() + self._vectorizer = DictVectorizer(dtype=dtype, sparse=sparse) + + def __repr__(self): + return "" % self._clf + + def classify_many(self, featuresets): + """Classify a batch of samples. + + :param featuresets: An iterable over featuresets, each a dict mapping + strings to either numbers, booleans or strings. + :return: The predicted class label for each input sample. + :rtype: list + """ + X = self._vectorizer.transform(featuresets) + classes = self._encoder.classes_ + return [classes[i] for i in self._clf.predict(X)] + + def prob_classify_many(self, featuresets): + """Compute per-class probabilities for a batch of samples. + + :param featuresets: An iterable over featuresets, each a dict mapping + strings to either numbers, booleans or strings. + :rtype: list of ``ProbDistI`` + """ + X = self._vectorizer.transform(featuresets) + y_proba_list = self._clf.predict_proba(X) + return [self._make_probdist(y_proba) for y_proba in y_proba_list] + + def labels(self): + """The class labels used by this classifier. + + :rtype: list + """ + return list(self._encoder.classes_) + + def train(self, labeled_featuresets): + """ + Train (fit) the scikit-learn estimator. + + :param labeled_featuresets: A list of ``(featureset, label)`` + where each ``featureset`` is a dict mapping strings to either + numbers, booleans or strings. + """ + + X, y = list(zip(*labeled_featuresets)) + X = self._vectorizer.fit_transform(X) + y = self._encoder.fit_transform(y) + self._clf.fit(X, y) + + return self + + def _make_probdist(self, y_proba): + classes = self._encoder.classes_ + return DictionaryProbDist({classes[i]: p for i, p in enumerate(y_proba)}) + + +if __name__ == "__main__": + from sklearn.linear_model import LogisticRegression + from sklearn.naive_bayes import BernoulliNB + + from nltk.classify.util import names_demo, names_demo_features + + # Bernoulli Naive Bayes is designed for binary classification. We set the + # binarize option to False since we know we're passing boolean features. + print("scikit-learn Naive Bayes:") + names_demo( + SklearnClassifier(BernoulliNB(binarize=False)).train, + features=names_demo_features, + ) + + # The C parameter on logistic regression (MaxEnt) controls regularization. + # The higher it's set, the less regularized the classifier is. + print("\n\nscikit-learn logistic regression:") + names_demo( + SklearnClassifier(LogisticRegression(C=1000)).train, + features=names_demo_features, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/senna.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/senna.py new file mode 100644 index 0000000..ff985b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/senna.py @@ -0,0 +1,176 @@ +# Natural Language Toolkit: Senna Interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Rami Al-Rfou' +# URL: +# For license information, see LICENSE.TXT + +""" +A general interface to the SENNA pipeline that supports any of the +operations specified in SUPPORTED_OPERATIONS. + +Applying multiple operations at once has the speed advantage. For example, +Senna will automatically determine POS tags if you are extracting named +entities. Applying both of the operations will cost only the time of +extracting the named entities. + +The SENNA pipeline has a fixed maximum size of the sentences that it can read. +By default it is 1024 token/sentence. If you have larger sentences, changing +the MAX_SENTENCE_SIZE value in SENNA_main.c should be considered and your +system specific binary should be rebuilt. Otherwise this could introduce +misalignment errors. + +The input is: + +- path to the directory that contains SENNA executables. If the path is incorrect, + Senna will automatically search for executable file specified in SENNA environment variable +- List of the operations needed to be performed. +- (optionally) the encoding of the input data (default:utf-8) + +Note: Unit tests for this module can be found in test/unit/test_senna.py + +>>> from nltk.classify import Senna +>>> pipeline = Senna('/usr/share/senna-v3.0', ['pos', 'chk', 'ner']) # doctest: +SKIP +>>> sent = 'Dusseldorf is an international business center'.split() +>>> [(token['word'], token['chk'], token['ner'], token['pos']) for token in pipeline.tag(sent)] # doctest: +SKIP +[('Dusseldorf', 'B-NP', 'B-LOC', 'NNP'), ('is', 'B-VP', 'O', 'VBZ'), ('an', 'B-NP', 'O', 'DT'), +('international', 'I-NP', 'O', 'JJ'), ('business', 'I-NP', 'O', 'NN'), ('center', 'I-NP', 'O', 'NN')] +""" + +from os import environ, path, sep +from platform import architecture, system +from subprocess import PIPE, Popen + +from nltk.tag.api import TaggerI + + +class Senna(TaggerI): + + SUPPORTED_OPERATIONS = ["pos", "chk", "ner"] + + def __init__(self, senna_path, operations, encoding="utf-8"): + self._encoding = encoding + self._path = path.normpath(senna_path) + sep + + # Verifies the existence of the executable on the self._path first + # senna_binary_file_1 = self.executable(self._path) + exe_file_1 = self.executable(self._path) + if not path.isfile(exe_file_1): + # Check for the system environment + if "SENNA" in environ: + # self._path = path.join(environ['SENNA'],'') + self._path = path.normpath(environ["SENNA"]) + sep + exe_file_2 = self.executable(self._path) + if not path.isfile(exe_file_2): + raise LookupError( + "Senna executable expected at %s or %s but not found" + % (exe_file_1, exe_file_2) + ) + + self.operations = operations + + def executable(self, base_path): + """ + The function that determines the system specific binary that should be + used in the pipeline. In case, the system is not known the default senna binary will + be used. + """ + os_name = system() + if os_name == "Linux": + bits = architecture()[0] + if bits == "64bit": + return path.join(base_path, "senna-linux64") + return path.join(base_path, "senna-linux32") + if os_name == "Windows": + return path.join(base_path, "senna-win32.exe") + if os_name == "Darwin": + return path.join(base_path, "senna-osx") + return path.join(base_path, "senna") + + def _map(self): + """ + A method that calculates the order of the columns that SENNA pipeline + will output the tags into. This depends on the operations being ordered. + """ + _map = {} + i = 1 + for operation in Senna.SUPPORTED_OPERATIONS: + if operation in self.operations: + _map[operation] = i + i += 1 + return _map + + def tag(self, tokens): + """ + Applies the specified operation(s) on a list of tokens. + """ + return self.tag_sents([tokens])[0] + + def tag_sents(self, sentences): + """ + Applies the tag method over a list of sentences. This method will return a + list of dictionaries. Every dictionary will contain a word with its + calculated annotations/tags. + """ + encoding = self._encoding + + if not path.isfile(self.executable(self._path)): + raise LookupError( + "Senna executable expected at %s but not found" + % self.executable(self._path) + ) + + # Build the senna command to run the tagger + _senna_cmd = [ + self.executable(self._path), + "-path", + self._path, + "-usrtokens", + "-iobtags", + ] + _senna_cmd.extend(["-" + op for op in self.operations]) + + # Serialize the actual sentences to a temporary string + _input = "\n".join(" ".join(x) for x in sentences) + "\n" + if isinstance(_input, str) and encoding: + _input = _input.encode(encoding) + + # Run the tagger and get the output + p = Popen(_senna_cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + (stdout, stderr) = p.communicate(input=_input) + senna_output = stdout + + # Check the return code. + if p.returncode != 0: + raise RuntimeError("Senna command failed! Details: %s" % stderr) + + if encoding: + senna_output = stdout.decode(encoding) + + # Output the tagged sentences + map_ = self._map() + tagged_sentences = [[]] + sentence_index = 0 + token_index = 0 + for tagged_word in senna_output.strip().split("\n"): + if not tagged_word: + tagged_sentences.append([]) + sentence_index += 1 + token_index = 0 + continue + tags = tagged_word.split("\t") + result = {} + for tag in map_: + result[tag] = tags[map_[tag]].strip() + try: + result["word"] = sentences[sentence_index][token_index] + except IndexError as e: + raise IndexError( + "Misalignment error occurred at sentence number %d. Possible reason" + " is that the sentence size exceeded the maximum size. Check the " + "documentation of Senna class for more information." + % sentence_index + ) from e + tagged_sentences[-1].append(result) + token_index += 1 + return tagged_sentences diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/svm.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/svm.py new file mode 100644 index 0000000..6411f86 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/svm.py @@ -0,0 +1,17 @@ +# Natural Language Toolkit: SVM-based classifier +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Leon Derczynski +# +# URL: +# For license information, see LICENSE.TXT +""" +nltk.classify.svm was deprecated. For classification based +on support vector machines SVMs use nltk.classify.scikitlearn +(or `scikit-learn `_ directly). +""" + + +class SvmClassifier: + def __init__(self, *args, **kwargs): + raise NotImplementedError(__doc__) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/tadm.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/tadm.py new file mode 100644 index 0000000..39fc34f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/tadm.py @@ -0,0 +1,122 @@ +# Natural Language Toolkit: Interface to TADM Classifier +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Joseph Frazee +# URL: +# For license information, see LICENSE.TXT + +import subprocess +import sys + +from nltk.internals import find_binary + +try: + import numpy +except ImportError: + pass + +_tadm_bin = None + + +def config_tadm(bin=None): + global _tadm_bin + _tadm_bin = find_binary( + "tadm", bin, env_vars=["TADM"], binary_names=["tadm"], url="http://tadm.sf.net" + ) + + +def write_tadm_file(train_toks, encoding, stream): + """ + Generate an input file for ``tadm`` based on the given corpus of + classified tokens. + + :type train_toks: list(tuple(dict, str)) + :param train_toks: Training data, represented as a list of + pairs, the first member of which is a feature dictionary, + and the second of which is a classification label. + :type encoding: TadmEventMaxentFeatureEncoding + :param encoding: A feature encoding, used to convert featuresets + into feature vectors. + :type stream: stream + :param stream: The stream to which the ``tadm`` input file should be + written. + """ + # See the following for a file format description: + # + # https://sf.net/forum/forum.php?thread_id=1391502&forum_id=473054 + # https://sf.net/forum/forum.php?thread_id=1675097&forum_id=473054 + labels = encoding.labels() + for featureset, label in train_toks: + length_line = "%d\n" % len(labels) + stream.write(length_line) + for known_label in labels: + v = encoding.encode(featureset, known_label) + line = "%d %d %s\n" % ( + int(label == known_label), + len(v), + " ".join("%d %d" % u for u in v), + ) + stream.write(line) + + +def parse_tadm_weights(paramfile): + """ + Given the stdout output generated by ``tadm`` when training a + model, return a ``numpy`` array containing the corresponding weight + vector. + """ + weights = [] + for line in paramfile: + weights.append(float(line.strip())) + return numpy.array(weights, "d") + + +def call_tadm(args): + """ + Call the ``tadm`` binary with the given arguments. + """ + if isinstance(args, str): + raise TypeError("args should be a list of strings") + if _tadm_bin is None: + config_tadm() + + # Call tadm via a subprocess + cmd = [_tadm_bin] + args + p = subprocess.Popen(cmd, stdout=sys.stdout) + (stdout, stderr) = p.communicate() + + # Check the return code. + if p.returncode != 0: + print() + print(stderr) + raise OSError("tadm command failed!") + + +def names_demo(): + from nltk.classify.maxent import TadmMaxentClassifier + from nltk.classify.util import names_demo + + classifier = names_demo(TadmMaxentClassifier.train) + + +def encoding_demo(): + import sys + + from nltk.classify.maxent import TadmEventMaxentFeatureEncoding + + tokens = [ + ({"f0": 1, "f1": 1, "f3": 1}, "A"), + ({"f0": 1, "f2": 1, "f4": 1}, "B"), + ({"f0": 2, "f2": 1, "f3": 1, "f4": 1}, "A"), + ] + encoding = TadmEventMaxentFeatureEncoding.train(tokens) + write_tadm_file(tokens, encoding, sys.stdout) + print() + for i in range(encoding.length()): + print("%s --> %d" % (encoding.describe(i), i)) + print() + + +if __name__ == "__main__": + encoding_demo() + names_demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/textcat.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/textcat.py new file mode 100644 index 0000000..4b7f2ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/textcat.py @@ -0,0 +1,197 @@ +# Natural Language Toolkit: Language ID module using TextCat algorithm +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Avital Pekker +# +# URL: +# For license information, see LICENSE.TXT + +""" +A module for language identification using the TextCat algorithm. +An implementation of the text categorization algorithm +presented in Cavnar, W. B. and J. M. Trenkle, +"N-Gram-Based Text Categorization". + +The algorithm takes advantage of Zipf's law and uses +n-gram frequencies to profile languages and text-yet to +be identified-then compares using a distance measure. + +Language n-grams are provided by the "An Crubadan" +project. A corpus reader was created separately to read +those files. + +For details regarding the algorithm, see: +https://www.let.rug.nl/~vannoord/TextCat/textcat.pdf + +For details about An Crubadan, see: +https://borel.slu.edu/crubadan/index.html +""" + +from sys import maxsize + +from nltk.util import trigrams + +# Note: this is NOT "re" you're likely used to. The regex module +# is an alternative to the standard re module that supports +# Unicode codepoint properties with the \p{} syntax. +# You may have to "pip install regx" +try: + import regex as re +except ImportError: + re = None +###################################################################### +## Language identification using TextCat +###################################################################### + + +class TextCat: + + _corpus = None + fingerprints = {} + _START_CHAR = "<" + _END_CHAR = ">" + + last_distances = {} + + def __init__(self): + if not re: + raise OSError( + "classify.textcat requires the regex module that " + "supports unicode. Try '$ pip install regex' and " + "see https://pypi.python.org/pypi/regex for " + "further details." + ) + + from nltk.corpus import crubadan + + self._corpus = crubadan + # Load all language ngrams into cache + for lang in self._corpus.langs(): + self._corpus.lang_freq(lang) + + def remove_punctuation(self, text): + """Get rid of punctuation except apostrophes""" + return re.sub(r"[^\P{P}\']+", "", text) + + def profile(self, text): + """Create FreqDist of trigrams within text""" + from nltk import FreqDist, word_tokenize + + clean_text = self.remove_punctuation(text) + tokens = word_tokenize(clean_text) + + fingerprint = FreqDist() + for t in tokens: + token_trigram_tuples = trigrams(self._START_CHAR + t + self._END_CHAR) + token_trigrams = ["".join(tri) for tri in token_trigram_tuples] + + for cur_trigram in token_trigrams: + if cur_trigram in fingerprint: + fingerprint[cur_trigram] += 1 + else: + fingerprint[cur_trigram] = 1 + + return fingerprint + + def calc_dist(self, lang, trigram, text_profile): + """Calculate the "out-of-place" measure between the + text and language profile for a single trigram""" + + lang_fd = self._corpus.lang_freq(lang) + dist = 0 + + if trigram in lang_fd: + idx_lang_profile = list(lang_fd.keys()).index(trigram) + idx_text = list(text_profile.keys()).index(trigram) + + # print(idx_lang_profile, ", ", idx_text) + dist = abs(idx_lang_profile - idx_text) + else: + # Arbitrary but should be larger than + # any possible trigram file length + # in terms of total lines + dist = maxsize + + return dist + + def lang_dists(self, text): + """Calculate the "out-of-place" measure between + the text and all languages""" + + distances = {} + profile = self.profile(text) + # For all the languages + for lang in self._corpus._all_lang_freq.keys(): + # Calculate distance metric for every trigram in + # input text to be identified + lang_dist = 0 + for trigram in profile: + lang_dist += self.calc_dist(lang, trigram, profile) + + distances[lang] = lang_dist + + return distances + + def guess_language(self, text): + """Find the language with the min distance + to the text and return its ISO 639-3 code""" + self.last_distances = self.lang_dists(text) + + return min(self.last_distances, key=self.last_distances.get) + #################################################') + + +def demo(): + from nltk.corpus import udhr + + langs = [ + "Kurdish-UTF8", + "Abkhaz-UTF8", + "Farsi_Persian-UTF8", + "Hindi-UTF8", + "Hawaiian-UTF8", + "Russian-UTF8", + "Vietnamese-UTF8", + "Serbian_Srpski-UTF8", + "Esperanto-UTF8", + ] + + friendly = { + "kmr": "Northern Kurdish", + "abk": "Abkhazian", + "pes": "Iranian Persian", + "hin": "Hindi", + "haw": "Hawaiian", + "rus": "Russian", + "vie": "Vietnamese", + "srp": "Serbian", + "epo": "Esperanto", + } + + tc = TextCat() + + for cur_lang in langs: + # Get raw data from UDHR corpus + raw_sentences = udhr.sents(cur_lang) + rows = len(raw_sentences) - 1 + cols = list(map(len, raw_sentences)) + + sample = "" + + # Generate a sample text of the language + for i in range(0, rows): + cur_sent = "" + for j in range(0, cols[i]): + cur_sent += " " + raw_sentences[i][j] + + sample += cur_sent + + # Try to detect what it is + print("Language snippet: " + sample[0:140] + "...") + guess = tc.guess_language(sample) + print(f"Language detection: {guess} ({friendly[guess]})") + print("#" * 140) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/util.py new file mode 100644 index 0000000..4d8c628 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/util.py @@ -0,0 +1,346 @@ +# Natural Language Toolkit: Classifier Utility Functions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT + +""" +Utility functions and classes for classifiers. +""" + +import math + +# from nltk.util import Deprecated +import nltk.classify.util # for accuracy & log_likelihood +from nltk.util import LazyMap + +###################################################################### +# { Helper Functions +###################################################################### + +# alternative name possibility: 'map_featurefunc()'? +# alternative name possibility: 'detect_features()'? +# alternative name possibility: 'map_featuredetect()'? +# or.. just have users use LazyMap directly? +def apply_features(feature_func, toks, labeled=None): + """ + Use the ``LazyMap`` class to construct a lazy list-like + object that is analogous to ``map(feature_func, toks)``. In + particular, if ``labeled=False``, then the returned list-like + object's values are equal to:: + + [feature_func(tok) for tok in toks] + + If ``labeled=True``, then the returned list-like object's values + are equal to:: + + [(feature_func(tok), label) for (tok, label) in toks] + + The primary purpose of this function is to avoid the memory + overhead involved in storing all the featuresets for every token + in a corpus. Instead, these featuresets are constructed lazily, + as-needed. The reduction in memory overhead can be especially + significant when the underlying list of tokens is itself lazy (as + is the case with many corpus readers). + + :param feature_func: The function that will be applied to each + token. It should return a featureset -- i.e., a dict + mapping feature names to feature values. + :param toks: The list of tokens to which ``feature_func`` should be + applied. If ``labeled=True``, then the list elements will be + passed directly to ``feature_func()``. If ``labeled=False``, + then the list elements should be tuples ``(tok,label)``, and + ``tok`` will be passed to ``feature_func()``. + :param labeled: If true, then ``toks`` contains labeled tokens -- + i.e., tuples of the form ``(tok, label)``. (Default: + auto-detect based on types.) + """ + if labeled is None: + labeled = toks and isinstance(toks[0], (tuple, list)) + if labeled: + + def lazy_func(labeled_token): + return (feature_func(labeled_token[0]), labeled_token[1]) + + return LazyMap(lazy_func, toks) + else: + return LazyMap(feature_func, toks) + + +def attested_labels(tokens): + """ + :return: A list of all labels that are attested in the given list + of tokens. + :rtype: list of (immutable) + :param tokens: The list of classified tokens from which to extract + labels. A classified token has the form ``(token, label)``. + :type tokens: list + """ + return tuple({label for (tok, label) in tokens}) + + +def log_likelihood(classifier, gold): + results = classifier.prob_classify_many([fs for (fs, l) in gold]) + ll = [pdist.prob(l) for ((fs, l), pdist) in zip(gold, results)] + return math.log(sum(ll) / len(ll)) + + +def accuracy(classifier, gold): + results = classifier.classify_many([fs for (fs, l) in gold]) + correct = [l == r for ((fs, l), r) in zip(gold, results)] + if correct: + return sum(correct) / len(correct) + else: + return 0 + + +class CutoffChecker: + """ + A helper class that implements cutoff checks based on number of + iterations and log likelihood. + + Accuracy cutoffs are also implemented, but they're almost never + a good idea to use. + """ + + def __init__(self, cutoffs): + self.cutoffs = cutoffs.copy() + if "min_ll" in cutoffs: + cutoffs["min_ll"] = -abs(cutoffs["min_ll"]) + if "min_lldelta" in cutoffs: + cutoffs["min_lldelta"] = abs(cutoffs["min_lldelta"]) + self.ll = None + self.acc = None + self.iter = 1 + + def check(self, classifier, train_toks): + cutoffs = self.cutoffs + self.iter += 1 + if "max_iter" in cutoffs and self.iter >= cutoffs["max_iter"]: + return True # iteration cutoff. + + new_ll = nltk.classify.util.log_likelihood(classifier, train_toks) + if math.isnan(new_ll): + return True + + if "min_ll" in cutoffs or "min_lldelta" in cutoffs: + if "min_ll" in cutoffs and new_ll >= cutoffs["min_ll"]: + return True # log likelihood cutoff + if ( + "min_lldelta" in cutoffs + and self.ll + and ((new_ll - self.ll) <= abs(cutoffs["min_lldelta"])) + ): + return True # log likelihood delta cutoff + self.ll = new_ll + + if "max_acc" in cutoffs or "min_accdelta" in cutoffs: + new_acc = nltk.classify.util.log_likelihood(classifier, train_toks) + if "max_acc" in cutoffs and new_acc >= cutoffs["max_acc"]: + return True # log likelihood cutoff + if ( + "min_accdelta" in cutoffs + and self.acc + and ((new_acc - self.acc) <= abs(cutoffs["min_accdelta"])) + ): + return True # log likelihood delta cutoff + self.acc = new_acc + + return False # no cutoff reached. + + +###################################################################### +# { Demos +###################################################################### + + +def names_demo_features(name): + features = {} + features["alwayson"] = True + features["startswith"] = name[0].lower() + features["endswith"] = name[-1].lower() + for letter in "abcdefghijklmnopqrstuvwxyz": + features["count(%s)" % letter] = name.lower().count(letter) + features["has(%s)" % letter] = letter in name.lower() + return features + + +def binary_names_demo_features(name): + features = {} + features["alwayson"] = True + features["startswith(vowel)"] = name[0].lower() in "aeiouy" + features["endswith(vowel)"] = name[-1].lower() in "aeiouy" + for letter in "abcdefghijklmnopqrstuvwxyz": + features["count(%s)" % letter] = name.lower().count(letter) + features["has(%s)" % letter] = letter in name.lower() + features["startswith(%s)" % letter] = letter == name[0].lower() + features["endswith(%s)" % letter] = letter == name[-1].lower() + return features + + +def names_demo(trainer, features=names_demo_features): + import random + + from nltk.corpus import names + + # Construct a list of classified names, using the names corpus. + namelist = [(name, "male") for name in names.words("male.txt")] + [ + (name, "female") for name in names.words("female.txt") + ] + + # Randomly split the names into a test & train set. + random.seed(123456) + random.shuffle(namelist) + train = namelist[:5000] + test = namelist[5000:5500] + + # Train up a classifier. + print("Training classifier...") + classifier = trainer([(features(n), g) for (n, g) in train]) + + # Run the classifier on the test data. + print("Testing classifier...") + acc = accuracy(classifier, [(features(n), g) for (n, g) in test]) + print("Accuracy: %6.4f" % acc) + + # For classifiers that can find probabilities, show the log + # likelihood and some sample probability distributions. + try: + test_featuresets = [features(n) for (n, g) in test] + pdists = classifier.prob_classify_many(test_featuresets) + ll = [pdist.logprob(gold) for ((name, gold), pdist) in zip(test, pdists)] + print("Avg. log likelihood: %6.4f" % (sum(ll) / len(test))) + print() + print("Unseen Names P(Male) P(Female)\n" + "-" * 40) + for ((name, gender), pdist) in list(zip(test, pdists))[:5]: + if gender == "male": + fmt = " %-15s *%6.4f %6.4f" + else: + fmt = " %-15s %6.4f *%6.4f" + print(fmt % (name, pdist.prob("male"), pdist.prob("female"))) + except NotImplementedError: + pass + + # Return the classifier + return classifier + + +def partial_names_demo(trainer, features=names_demo_features): + import random + + from nltk.corpus import names + + male_names = names.words("male.txt") + female_names = names.words("female.txt") + + random.seed(654321) + random.shuffle(male_names) + random.shuffle(female_names) + + # Create a list of male names to be used as positive-labeled examples for training + positive = map(features, male_names[:2000]) + + # Create a list of male and female names to be used as unlabeled examples + unlabeled = map(features, male_names[2000:2500] + female_names[:500]) + + # Create a test set with correctly-labeled male and female names + test = [(name, True) for name in male_names[2500:2750]] + [ + (name, False) for name in female_names[500:750] + ] + + random.shuffle(test) + + # Train up a classifier. + print("Training classifier...") + classifier = trainer(positive, unlabeled) + + # Run the classifier on the test data. + print("Testing classifier...") + acc = accuracy(classifier, [(features(n), m) for (n, m) in test]) + print("Accuracy: %6.4f" % acc) + + # For classifiers that can find probabilities, show the log + # likelihood and some sample probability distributions. + try: + test_featuresets = [features(n) for (n, m) in test] + pdists = classifier.prob_classify_many(test_featuresets) + ll = [pdist.logprob(gold) for ((name, gold), pdist) in zip(test, pdists)] + print("Avg. log likelihood: %6.4f" % (sum(ll) / len(test))) + print() + print("Unseen Names P(Male) P(Female)\n" + "-" * 40) + for ((name, is_male), pdist) in zip(test, pdists)[:5]: + if is_male == True: + fmt = " %-15s *%6.4f %6.4f" + else: + fmt = " %-15s %6.4f *%6.4f" + print(fmt % (name, pdist.prob(True), pdist.prob(False))) + except NotImplementedError: + pass + + # Return the classifier + return classifier + + +_inst_cache = {} + + +def wsd_demo(trainer, word, features, n=1000): + import random + + from nltk.corpus import senseval + + # Get the instances. + print("Reading data...") + global _inst_cache + if word not in _inst_cache: + _inst_cache[word] = [(i, i.senses[0]) for i in senseval.instances(word)] + instances = _inst_cache[word][:] + if n > len(instances): + n = len(instances) + senses = list({l for (i, l) in instances}) + print(" Senses: " + " ".join(senses)) + + # Randomly split the names into a test & train set. + print("Splitting into test & train...") + random.seed(123456) + random.shuffle(instances) + train = instances[: int(0.8 * n)] + test = instances[int(0.8 * n) : n] + + # Train up a classifier. + print("Training classifier...") + classifier = trainer([(features(i), l) for (i, l) in train]) + + # Run the classifier on the test data. + print("Testing classifier...") + acc = accuracy(classifier, [(features(i), l) for (i, l) in test]) + print("Accuracy: %6.4f" % acc) + + # For classifiers that can find probabilities, show the log + # likelihood and some sample probability distributions. + try: + test_featuresets = [features(i) for (i, n) in test] + pdists = classifier.prob_classify_many(test_featuresets) + ll = [pdist.logprob(gold) for ((name, gold), pdist) in zip(test, pdists)] + print("Avg. log likelihood: %6.4f" % (sum(ll) / len(test))) + except NotImplementedError: + pass + + # Return the classifier + return classifier + + +def check_megam_config(): + """ + Checks whether the MEGAM binary is configured. + """ + try: + _megam_bin + except NameError as e: + err_msg = str( + "Please configure your megam binary first, e.g.\n" + ">>> nltk.config_megam('/usr/bin/local/megam')" + ) + raise NameError(err_msg) from e diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/classify/weka.py b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/weka.py new file mode 100644 index 0000000..6b348ff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/classify/weka.py @@ -0,0 +1,377 @@ +# Natural Language Toolkit: Interface to Weka Classsifiers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Classifiers that make use of the external 'Weka' package. +""" + +import os +import re +import subprocess +import tempfile +import time +import zipfile +from sys import stdin + +from nltk.classify.api import ClassifierI +from nltk.internals import config_java, java +from nltk.probability import DictionaryProbDist + +_weka_classpath = None +_weka_search = [ + ".", + "/usr/share/weka", + "/usr/local/share/weka", + "/usr/lib/weka", + "/usr/local/lib/weka", +] + + +def config_weka(classpath=None): + global _weka_classpath + + # Make sure java's configured first. + config_java() + + if classpath is not None: + _weka_classpath = classpath + + if _weka_classpath is None: + searchpath = _weka_search + if "WEKAHOME" in os.environ: + searchpath.insert(0, os.environ["WEKAHOME"]) + + for path in searchpath: + if os.path.exists(os.path.join(path, "weka.jar")): + _weka_classpath = os.path.join(path, "weka.jar") + version = _check_weka_version(_weka_classpath) + if version: + print(f"[Found Weka: {_weka_classpath} (version {version})]") + else: + print("[Found Weka: %s]" % _weka_classpath) + _check_weka_version(_weka_classpath) + + if _weka_classpath is None: + raise LookupError( + "Unable to find weka.jar! Use config_weka() " + "or set the WEKAHOME environment variable. " + "For more information about Weka, please see " + "https://www.cs.waikato.ac.nz/ml/weka/" + ) + + +def _check_weka_version(jar): + try: + zf = zipfile.ZipFile(jar) + except (SystemExit, KeyboardInterrupt): + raise + except: + return None + try: + try: + return zf.read("weka/core/version.txt") + except KeyError: + return None + finally: + zf.close() + + +class WekaClassifier(ClassifierI): + def __init__(self, formatter, model_filename): + self._formatter = formatter + self._model = model_filename + + def prob_classify_many(self, featuresets): + return self._classify_many(featuresets, ["-p", "0", "-distribution"]) + + def classify_many(self, featuresets): + return self._classify_many(featuresets, ["-p", "0"]) + + def _classify_many(self, featuresets, options): + # Make sure we can find java & weka. + config_weka() + + temp_dir = tempfile.mkdtemp() + try: + # Write the test data file. + test_filename = os.path.join(temp_dir, "test.arff") + self._formatter.write(test_filename, featuresets) + + # Call weka to classify the data. + cmd = [ + "weka.classifiers.bayes.NaiveBayes", + "-l", + self._model, + "-T", + test_filename, + ] + options + (stdout, stderr) = java( + cmd, + classpath=_weka_classpath, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # Check if something went wrong: + if stderr and not stdout: + if "Illegal options: -distribution" in stderr: + raise ValueError( + "The installed version of weka does " + "not support probability distribution " + "output." + ) + else: + raise ValueError("Weka failed to generate output:\n%s" % stderr) + + # Parse weka's output. + return self.parse_weka_output(stdout.decode(stdin.encoding).split("\n")) + + finally: + for f in os.listdir(temp_dir): + os.remove(os.path.join(temp_dir, f)) + os.rmdir(temp_dir) + + def parse_weka_distribution(self, s): + probs = [float(v) for v in re.split("[*,]+", s) if v.strip()] + probs = dict(zip(self._formatter.labels(), probs)) + return DictionaryProbDist(probs) + + def parse_weka_output(self, lines): + # Strip unwanted text from stdout + for i, line in enumerate(lines): + if line.strip().startswith("inst#"): + lines = lines[i:] + break + + if lines[0].split() == ["inst#", "actual", "predicted", "error", "prediction"]: + return [line.split()[2].split(":")[1] for line in lines[1:] if line.strip()] + elif lines[0].split() == [ + "inst#", + "actual", + "predicted", + "error", + "distribution", + ]: + return [ + self.parse_weka_distribution(line.split()[-1]) + for line in lines[1:] + if line.strip() + ] + + # is this safe:? + elif re.match(r"^0 \w+ [01]\.[0-9]* \?\s*$", lines[0]): + return [line.split()[1] for line in lines if line.strip()] + + else: + for line in lines[:10]: + print(line) + raise ValueError( + "Unhandled output format -- your version " + "of weka may not be supported.\n" + " Header: %s" % lines[0] + ) + + # [xx] full list of classifiers (some may be abstract?): + # ADTree, AODE, BayesNet, ComplementNaiveBayes, ConjunctiveRule, + # DecisionStump, DecisionTable, HyperPipes, IB1, IBk, Id3, J48, + # JRip, KStar, LBR, LeastMedSq, LinearRegression, LMT, Logistic, + # LogisticBase, M5Base, MultilayerPerceptron, + # MultipleClassifiersCombiner, NaiveBayes, NaiveBayesMultinomial, + # NaiveBayesSimple, NBTree, NNge, OneR, PaceRegression, PART, + # PreConstructedLinearModel, Prism, RandomForest, + # RandomizableClassifier, RandomTree, RBFNetwork, REPTree, Ridor, + # RuleNode, SimpleLinearRegression, SimpleLogistic, + # SingleClassifierEnhancer, SMO, SMOreg, UserClassifier, VFI, + # VotedPerceptron, Winnow, ZeroR + + _CLASSIFIER_CLASS = { + "naivebayes": "weka.classifiers.bayes.NaiveBayes", + "C4.5": "weka.classifiers.trees.J48", + "log_regression": "weka.classifiers.functions.Logistic", + "svm": "weka.classifiers.functions.SMO", + "kstar": "weka.classifiers.lazy.KStar", + "ripper": "weka.classifiers.rules.JRip", + } + + @classmethod + def train( + cls, + model_filename, + featuresets, + classifier="naivebayes", + options=[], + quiet=True, + ): + # Make sure we can find java & weka. + config_weka() + + # Build an ARFF formatter. + formatter = ARFF_Formatter.from_train(featuresets) + + temp_dir = tempfile.mkdtemp() + try: + # Write the training data file. + train_filename = os.path.join(temp_dir, "train.arff") + formatter.write(train_filename, featuresets) + + if classifier in cls._CLASSIFIER_CLASS: + javaclass = cls._CLASSIFIER_CLASS[classifier] + elif classifier in cls._CLASSIFIER_CLASS.values(): + javaclass = classifier + else: + raise ValueError("Unknown classifier %s" % classifier) + + # Train the weka model. + cmd = [javaclass, "-d", model_filename, "-t", train_filename] + cmd += list(options) + if quiet: + stdout = subprocess.PIPE + else: + stdout = None + java(cmd, classpath=_weka_classpath, stdout=stdout) + + # Return the new classifier. + return WekaClassifier(formatter, model_filename) + + finally: + for f in os.listdir(temp_dir): + os.remove(os.path.join(temp_dir, f)) + os.rmdir(temp_dir) + + +class ARFF_Formatter: + """ + Converts featuresets and labeled featuresets to ARFF-formatted + strings, appropriate for input into Weka. + + Features and classes can be specified manually in the constructor, or may + be determined from data using ``from_train``. + """ + + def __init__(self, labels, features): + """ + :param labels: A list of all class labels that can be generated. + :param features: A list of feature specifications, where + each feature specification is a tuple (fname, ftype); + and ftype is an ARFF type string such as NUMERIC or + STRING. + """ + self._labels = labels + self._features = features + + def format(self, tokens): + """Returns a string representation of ARFF output for the given data.""" + return self.header_section() + self.data_section(tokens) + + def labels(self): + """Returns the list of classes.""" + return list(self._labels) + + def write(self, outfile, tokens): + """Writes ARFF data to a file for the given data.""" + if not hasattr(outfile, "write"): + outfile = open(outfile, "w") + outfile.write(self.format(tokens)) + outfile.close() + + @staticmethod + def from_train(tokens): + """ + Constructs an ARFF_Formatter instance with class labels and feature + types determined from the given data. Handles boolean, numeric and + string (note: not nominal) types. + """ + # Find the set of all attested labels. + labels = {label for (tok, label) in tokens} + + # Determine the types of all features. + features = {} + for tok, label in tokens: + for (fname, fval) in tok.items(): + if issubclass(type(fval), bool): + ftype = "{True, False}" + elif issubclass(type(fval), (int, float, bool)): + ftype = "NUMERIC" + elif issubclass(type(fval), str): + ftype = "STRING" + elif fval is None: + continue # can't tell the type. + else: + raise ValueError("Unsupported value type %r" % ftype) + + if features.get(fname, ftype) != ftype: + raise ValueError("Inconsistent type for %s" % fname) + features[fname] = ftype + features = sorted(features.items()) + + return ARFF_Formatter(labels, features) + + def header_section(self): + """Returns an ARFF header as a string.""" + # Header comment. + s = ( + "% Weka ARFF file\n" + + "% Generated automatically by NLTK\n" + + "%% %s\n\n" % time.ctime() + ) + + # Relation name + s += "@RELATION rel\n\n" + + # Input attribute specifications + for fname, ftype in self._features: + s += "@ATTRIBUTE %-30r %s\n" % (fname, ftype) + + # Label attribute specification + s += "@ATTRIBUTE %-30r {%s}\n" % ("-label-", ",".join(self._labels)) + + return s + + def data_section(self, tokens, labeled=None): + """ + Returns the ARFF data section for the given data. + + :param tokens: a list of featuresets (dicts) or labelled featuresets + which are tuples (featureset, label). + :param labeled: Indicates whether the given tokens are labeled + or not. If None, then the tokens will be assumed to be + labeled if the first token's value is a tuple or list. + """ + # Check if the tokens are labeled or unlabeled. If unlabeled, + # then use 'None' + if labeled is None: + labeled = tokens and isinstance(tokens[0], (tuple, list)) + if not labeled: + tokens = [(tok, None) for tok in tokens] + + # Data section + s = "\n@DATA\n" + for (tok, label) in tokens: + for fname, ftype in self._features: + s += "%s," % self._fmt_arff_val(tok.get(fname)) + s += "%s\n" % self._fmt_arff_val(label) + + return s + + def _fmt_arff_val(self, fval): + if fval is None: + return "?" + elif isinstance(fval, (bool, int)): + return "%s" % fval + elif isinstance(fval, float): + return "%r" % fval + else: + return "%r" % fval + + +if __name__ == "__main__": + from nltk.classify.util import binary_names_demo_features, names_demo + + def make_classifier(featuresets): + return WekaClassifier.train("/tmp/name.model", featuresets, "C4.5") + + classifier = names_demo(make_classifier, binary_names_demo_features) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cli.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cli.py new file mode 100644 index 0000000..e06e326 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cli.py @@ -0,0 +1,55 @@ +# Natural Language Toolkit: NLTK Command-Line Interface +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + + +import click +from tqdm import tqdm + +from nltk import word_tokenize +from nltk.util import parallelize_preprocess + +CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) + + +@click.group(context_settings=CONTEXT_SETTINGS) +@click.version_option() +def cli(): + pass + + +@cli.command("tokenize") +@click.option( + "--language", + "-l", + default="en", + help="The language for the Punkt sentence tokenization.", +) +@click.option( + "--preserve-line", + "-l", + default=True, + is_flag=True, + help="An option to keep the preserve the sentence and not sentence tokenize it.", +) +@click.option("--processes", "-j", default=1, help="No. of processes.") +@click.option("--encoding", "-e", default="utf8", help="Specify encoding of file.") +@click.option( + "--delimiter", "-d", default=" ", help="Specify delimiter to join the tokens." +) +def tokenize_file(language, preserve_line, processes, encoding, delimiter): + """This command tokenizes text stream using nltk.word_tokenize""" + with click.get_text_stream("stdin", encoding=encoding) as fin: + with click.get_text_stream("stdout", encoding=encoding) as fout: + # If it's single process, joblib parallelization is slower, + # so just process line by line normally. + if processes == 1: + for line in tqdm(fin.readlines()): + print(delimiter.join(word_tokenize(line)), end="\n", file=fout) + else: + for outline in parallelize_preprocess( + word_tokenize, fin.readlines(), processes, progress_bar=True + ): + print(delimiter.join(outline), end="\n", file=fout) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__init__.py new file mode 100644 index 0000000..4278333 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__init__.py @@ -0,0 +1,92 @@ +# Natural Language Toolkit: Clusterers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# URL: +# For license information, see LICENSE.TXT + +""" +This module contains a number of basic clustering algorithms. Clustering +describes the task of discovering groups of similar items with a large +collection. It is also describe as unsupervised machine learning, as the data +from which it learns is unannotated with class information, as is the case for +supervised learning. Annotated data is difficult and expensive to obtain in +the quantities required for the majority of supervised learning algorithms. +This problem, the knowledge acquisition bottleneck, is common to most natural +language processing tasks, thus fueling the need for quality unsupervised +approaches. + +This module contains a k-means clusterer, E-M clusterer and a group average +agglomerative clusterer (GAAC). All these clusterers involve finding good +cluster groupings for a set of vectors in multi-dimensional space. + +The K-means clusterer starts with k arbitrary chosen means then allocates each +vector to the cluster with the closest mean. It then recalculates the means of +each cluster as the centroid of the vectors in the cluster. This process +repeats until the cluster memberships stabilise. This is a hill-climbing +algorithm which may converge to a local maximum. Hence the clustering is +often repeated with random initial means and the most commonly occurring +output means are chosen. + +The GAAC clusterer starts with each of the *N* vectors as singleton clusters. +It then iteratively merges pairs of clusters which have the closest centroids. +This continues until there is only one cluster. The order of merges gives rise +to a dendrogram - a tree with the earlier merges lower than later merges. The +membership of a given number of clusters *c*, *1 <= c <= N*, can be found by +cutting the dendrogram at depth *c*. + +The Gaussian EM clusterer models the vectors as being produced by a mixture +of k Gaussian sources. The parameters of these sources (prior probability, +mean and covariance matrix) are then found to maximise the likelihood of the +given data. This is done with the expectation maximisation algorithm. It +starts with k arbitrarily chosen means, priors and covariance matrices. It +then calculates the membership probabilities for each vector in each of the +clusters - this is the 'E' step. The cluster parameters are then updated in +the 'M' step using the maximum likelihood estimate from the cluster membership +probabilities. This process continues until the likelihood of the data does +not significantly increase. + +They all extend the ClusterI interface which defines common operations +available with each clusterer. These operations include: + +- cluster: clusters a sequence of vectors +- classify: assign a vector to a cluster +- classification_probdist: give the probability distribution over cluster memberships + +The current existing classifiers also extend cluster.VectorSpace, an +abstract class which allows for singular value decomposition (SVD) and vector +normalisation. SVD is used to reduce the dimensionality of the vector space in +such a manner as to preserve as much of the variation as possible, by +reparameterising the axes in order of variability and discarding all bar the +first d dimensions. Normalisation ensures that vectors fall in the unit +hypersphere. + +Usage example (see also demo()):: + + from nltk import cluster + from nltk.cluster import euclidean_distance + from numpy import array + + vectors = [array(f) for f in [[3, 3], [1, 2], [4, 2], [4, 0]]] + + # initialise the clusterer (will also assign the vectors to clusters) + clusterer = cluster.KMeansClusterer(2, euclidean_distance) + clusterer.cluster(vectors, True) + + # classify a new vector + print(clusterer.classify(array([3, 3]))) + +Note that the vectors must use numpy array-like +objects. nltk_contrib.unimelb.tacohn.SparseArrays may be used for +efficiency when required. +""" + +from nltk.cluster.em import EMClusterer +from nltk.cluster.gaac import GAAClusterer +from nltk.cluster.kmeans import KMeansClusterer +from nltk.cluster.util import ( + Dendrogram, + VectorSpaceClusterer, + cosine_distance, + euclidean_distance, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0dfd5e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..d1a3fbe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/em.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/em.cpython-310.pyc new file mode 100644 index 0000000..2669116 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/em.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/gaac.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/gaac.cpython-310.pyc new file mode 100644 index 0000000..2a9e6a8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/gaac.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/kmeans.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/kmeans.cpython-310.pyc new file mode 100644 index 0000000..cf535e8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/kmeans.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..97bd631 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/api.py new file mode 100644 index 0000000..3d0d020 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/api.py @@ -0,0 +1,74 @@ +# Natural Language Toolkit: Clusterer Interfaces +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Porting: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from abc import ABCMeta, abstractmethod + +from nltk.probability import DictionaryProbDist + + +class ClusterI(metaclass=ABCMeta): + """ + Interface covering basic clustering functionality. + """ + + @abstractmethod + def cluster(self, vectors, assign_clusters=False): + """ + Assigns the vectors to clusters, learning the clustering parameters + from the data. Returns a cluster identifier for each vector. + """ + + @abstractmethod + def classify(self, token): + """ + Classifies the token into a cluster, setting the token's CLUSTER + parameter to that cluster identifier. + """ + + def likelihood(self, vector, label): + """ + Returns the likelihood (a float) of the token having the + corresponding cluster. + """ + if self.classify(vector) == label: + return 1.0 + else: + return 0.0 + + def classification_probdist(self, vector): + """ + Classifies the token into a cluster, returning + a probability distribution over the cluster identifiers. + """ + likelihoods = {} + sum = 0.0 + for cluster in self.cluster_names(): + likelihoods[cluster] = self.likelihood(vector, cluster) + sum += likelihoods[cluster] + for cluster in self.cluster_names(): + likelihoods[cluster] /= sum + return DictionaryProbDist(likelihoods) + + @abstractmethod + def num_clusters(self): + """ + Returns the number of clusters. + """ + + def cluster_names(self): + """ + Returns the names of the clusters. + :rtype: list + """ + return list(range(self.num_clusters())) + + def cluster_name(self, index): + """ + Returns the names of the cluster at index. + """ + return index diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/em.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/em.py new file mode 100644 index 0000000..a99b136 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/em.py @@ -0,0 +1,219 @@ +# Natural Language Toolkit: Expectation Maximization Clusterer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# URL: +# For license information, see LICENSE.TXT + +try: + import numpy +except ImportError: + pass + +from nltk.cluster.util import VectorSpaceClusterer + + +class EMClusterer(VectorSpaceClusterer): + """ + The Gaussian EM clusterer models the vectors as being produced by + a mixture of k Gaussian sources. The parameters of these sources + (prior probability, mean and covariance matrix) are then found to + maximise the likelihood of the given data. This is done with the + expectation maximisation algorithm. It starts with k arbitrarily + chosen means, priors and covariance matrices. It then calculates + the membership probabilities for each vector in each of the + clusters; this is the 'E' step. The cluster parameters are then + updated in the 'M' step using the maximum likelihood estimate from + the cluster membership probabilities. This process continues until + the likelihood of the data does not significantly increase. + """ + + def __init__( + self, + initial_means, + priors=None, + covariance_matrices=None, + conv_threshold=1e-6, + bias=0.1, + normalise=False, + svd_dimensions=None, + ): + """ + Creates an EM clusterer with the given starting parameters, + convergence threshold and vector mangling parameters. + + :param initial_means: the means of the gaussian cluster centers + :type initial_means: [seq of] numpy array or seq of SparseArray + :param priors: the prior probability for each cluster + :type priors: numpy array or seq of float + :param covariance_matrices: the covariance matrix for each cluster + :type covariance_matrices: [seq of] numpy array + :param conv_threshold: maximum change in likelihood before deemed + convergent + :type conv_threshold: int or float + :param bias: variance bias used to ensure non-singular covariance + matrices + :type bias: float + :param normalise: should vectors be normalised to length 1 + :type normalise: boolean + :param svd_dimensions: number of dimensions to use in reducing vector + dimensionsionality with SVD + :type svd_dimensions: int + """ + VectorSpaceClusterer.__init__(self, normalise, svd_dimensions) + self._means = numpy.array(initial_means, numpy.float64) + self._num_clusters = len(initial_means) + self._conv_threshold = conv_threshold + self._covariance_matrices = covariance_matrices + self._priors = priors + self._bias = bias + + def num_clusters(self): + return self._num_clusters + + def cluster_vectorspace(self, vectors, trace=False): + assert len(vectors) > 0 + + # set the parameters to initial values + dimensions = len(vectors[0]) + means = self._means + priors = self._priors + if not priors: + priors = self._priors = ( + numpy.ones(self._num_clusters, numpy.float64) / self._num_clusters + ) + covariances = self._covariance_matrices + if not covariances: + covariances = self._covariance_matrices = [ + numpy.identity(dimensions, numpy.float64) + for i in range(self._num_clusters) + ] + + # do the E and M steps until the likelihood plateaus + lastl = self._loglikelihood(vectors, priors, means, covariances) + converged = False + + while not converged: + if trace: + print("iteration; loglikelihood", lastl) + # E-step, calculate hidden variables, h[i,j] + h = numpy.zeros((len(vectors), self._num_clusters), numpy.float64) + for i in range(len(vectors)): + for j in range(self._num_clusters): + h[i, j] = priors[j] * self._gaussian( + means[j], covariances[j], vectors[i] + ) + h[i, :] /= sum(h[i, :]) + + # M-step, update parameters - cvm, p, mean + for j in range(self._num_clusters): + covariance_before = covariances[j] + new_covariance = numpy.zeros((dimensions, dimensions), numpy.float64) + new_mean = numpy.zeros(dimensions, numpy.float64) + sum_hj = 0.0 + for i in range(len(vectors)): + delta = vectors[i] - means[j] + new_covariance += h[i, j] * numpy.multiply.outer(delta, delta) + sum_hj += h[i, j] + new_mean += h[i, j] * vectors[i] + covariances[j] = new_covariance / sum_hj + means[j] = new_mean / sum_hj + priors[j] = sum_hj / len(vectors) + + # bias term to stop covariance matrix being singular + covariances[j] += self._bias * numpy.identity(dimensions, numpy.float64) + + # calculate likelihood - FIXME: may be broken + l = self._loglikelihood(vectors, priors, means, covariances) + + # check for convergence + if abs(lastl - l) < self._conv_threshold: + converged = True + lastl = l + + def classify_vectorspace(self, vector): + best = None + for j in range(self._num_clusters): + p = self._priors[j] * self._gaussian( + self._means[j], self._covariance_matrices[j], vector + ) + if not best or p > best[0]: + best = (p, j) + return best[1] + + def likelihood_vectorspace(self, vector, cluster): + cid = self.cluster_names().index(cluster) + return self._priors[cluster] * self._gaussian( + self._means[cluster], self._covariance_matrices[cluster], vector + ) + + def _gaussian(self, mean, cvm, x): + m = len(mean) + assert cvm.shape == (m, m), "bad sized covariance matrix, %s" % str(cvm.shape) + try: + det = numpy.linalg.det(cvm) + inv = numpy.linalg.inv(cvm) + a = det**-0.5 * (2 * numpy.pi) ** (-m / 2.0) + dx = x - mean + print(dx, inv) + b = -0.5 * numpy.dot(numpy.dot(dx, inv), dx) + return a * numpy.exp(b) + except OverflowError: + # happens when the exponent is negative infinity - i.e. b = 0 + # i.e. the inverse of cvm is huge (cvm is almost zero) + return 0 + + def _loglikelihood(self, vectors, priors, means, covariances): + llh = 0.0 + for vector in vectors: + p = 0 + for j in range(len(priors)): + p += priors[j] * self._gaussian(means[j], covariances[j], vector) + llh += numpy.log(p) + return llh + + def __repr__(self): + return "" % list(self._means) + + +def demo(): + """ + Non-interactive demonstration of the clusterers with simple 2-D data. + """ + + from nltk import cluster + + # example from figure 14.10, page 519, Manning and Schutze + + vectors = [numpy.array(f) for f in [[0.5, 0.5], [1.5, 0.5], [1, 3]]] + means = [[4, 2], [4, 2.01]] + + clusterer = cluster.EMClusterer(means, bias=0.1) + clusters = clusterer.cluster(vectors, True, trace=True) + + print("Clustered:", vectors) + print("As: ", clusters) + print() + + for c in range(2): + print("Cluster:", c) + print("Prior: ", clusterer._priors[c]) + print("Mean: ", clusterer._means[c]) + print("Covar: ", clusterer._covariance_matrices[c]) + print() + + # classify a new vector + vector = numpy.array([2, 2]) + print("classify(%s):" % vector, end=" ") + print(clusterer.classify(vector)) + + # show the classification probabilities + vector = numpy.array([2, 2]) + print("classification_probdist(%s):" % vector) + pdist = clusterer.classification_probdist(vector) + for sample in pdist.samples(): + print(f"{sample} => {pdist.prob(sample) * 100:.0f}%") + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/gaac.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/gaac.py new file mode 100644 index 0000000..f13161b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/gaac.py @@ -0,0 +1,170 @@ +# Natural Language Toolkit: Group Average Agglomerative Clusterer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# URL: +# For license information, see LICENSE.TXT + +try: + import numpy +except ImportError: + pass + +from nltk.cluster.util import Dendrogram, VectorSpaceClusterer, cosine_distance + + +class GAAClusterer(VectorSpaceClusterer): + """ + The Group Average Agglomerative starts with each of the N vectors as singleton + clusters. It then iteratively merges pairs of clusters which have the + closest centroids. This continues until there is only one cluster. The + order of merges gives rise to a dendrogram: a tree with the earlier merges + lower than later merges. The membership of a given number of clusters c, 1 + <= c <= N, can be found by cutting the dendrogram at depth c. + + This clusterer uses the cosine similarity metric only, which allows for + efficient speed-up in the clustering process. + """ + + def __init__(self, num_clusters=1, normalise=True, svd_dimensions=None): + VectorSpaceClusterer.__init__(self, normalise, svd_dimensions) + self._num_clusters = num_clusters + self._dendrogram = None + self._groups_values = None + + def cluster(self, vectors, assign_clusters=False, trace=False): + # stores the merge order + self._dendrogram = Dendrogram( + [numpy.array(vector, numpy.float64) for vector in vectors] + ) + return VectorSpaceClusterer.cluster(self, vectors, assign_clusters, trace) + + def cluster_vectorspace(self, vectors, trace=False): + # variables describing the initial situation + N = len(vectors) + cluster_len = [1] * N + cluster_count = N + index_map = numpy.arange(N) + + # construct the similarity matrix + dims = (N, N) + dist = numpy.ones(dims, dtype=float) * numpy.inf + for i in range(N): + for j in range(i + 1, N): + dist[i, j] = cosine_distance(vectors[i], vectors[j]) + + while cluster_count > max(self._num_clusters, 1): + i, j = numpy.unravel_index(dist.argmin(), dims) + if trace: + print("merging %d and %d" % (i, j)) + + # update similarities for merging i and j + self._merge_similarities(dist, cluster_len, i, j) + + # remove j + dist[:, j] = numpy.inf + dist[j, :] = numpy.inf + + # merge the clusters + cluster_len[i] = cluster_len[i] + cluster_len[j] + self._dendrogram.merge(index_map[i], index_map[j]) + cluster_count -= 1 + + # update the index map to reflect the indexes if we + # had removed j + index_map[j + 1 :] -= 1 + index_map[j] = N + + self.update_clusters(self._num_clusters) + + def _merge_similarities(self, dist, cluster_len, i, j): + # the new cluster i merged from i and j adopts the average of + # i and j's similarity to each other cluster, weighted by the + # number of points in the clusters i and j + i_weight = cluster_len[i] + j_weight = cluster_len[j] + weight_sum = i_weight + j_weight + + # update for x 0 + if self._should_normalise: + centroid = self._normalise(cluster[0]) + else: + centroid = numpy.array(cluster[0]) + for vector in cluster[1:]: + if self._should_normalise: + centroid += self._normalise(vector) + else: + centroid += vector + centroid /= len(cluster) + self._centroids.append(centroid) + self._num_clusters = len(self._centroids) + + def classify_vectorspace(self, vector): + best = None + for i in range(self._num_clusters): + centroid = self._centroids[i] + dist = cosine_distance(vector, centroid) + if not best or dist < best[0]: + best = (dist, i) + return best[1] + + def dendrogram(self): + """ + :return: The dendrogram representing the current clustering + :rtype: Dendrogram + """ + return self._dendrogram + + def num_clusters(self): + return self._num_clusters + + def __repr__(self): + return "" % self._num_clusters + + +def demo(): + """ + Non-interactive demonstration of the clusterers with simple 2-D data. + """ + + from nltk.cluster import GAAClusterer + + # use a set of tokens with 2D indices + vectors = [numpy.array(f) for f in [[3, 3], [1, 2], [4, 2], [4, 0], [2, 3], [3, 1]]] + + # test the GAAC clusterer with 4 clusters + clusterer = GAAClusterer(4) + clusters = clusterer.cluster(vectors, True) + + print("Clusterer:", clusterer) + print("Clustered:", vectors) + print("As:", clusters) + print() + + # show the dendrogram + clusterer.dendrogram().show() + + # classify a new vector + vector = numpy.array([3, 3]) + print("classify(%s):" % vector, end=" ") + print(clusterer.classify(vector)) + print() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/kmeans.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/kmeans.py new file mode 100644 index 0000000..4246d3c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/kmeans.py @@ -0,0 +1,231 @@ +# Natural Language Toolkit: K-Means Clusterer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# URL: +# For license information, see LICENSE.TXT + +import copy +import random +import sys + +try: + import numpy +except ImportError: + pass + + +from nltk.cluster.util import VectorSpaceClusterer + + +class KMeansClusterer(VectorSpaceClusterer): + """ + The K-means clusterer starts with k arbitrary chosen means then allocates + each vector to the cluster with the closest mean. It then recalculates the + means of each cluster as the centroid of the vectors in the cluster. This + process repeats until the cluster memberships stabilise. This is a + hill-climbing algorithm which may converge to a local maximum. Hence the + clustering is often repeated with random initial means and the most + commonly occurring output means are chosen. + """ + + def __init__( + self, + num_means, + distance, + repeats=1, + conv_test=1e-6, + initial_means=None, + normalise=False, + svd_dimensions=None, + rng=None, + avoid_empty_clusters=False, + ): + + """ + :param num_means: the number of means to use (may use fewer) + :type num_means: int + :param distance: measure of distance between two vectors + :type distance: function taking two vectors and returning a float + :param repeats: number of randomised clustering trials to use + :type repeats: int + :param conv_test: maximum variation in mean differences before + deemed convergent + :type conv_test: number + :param initial_means: set of k initial means + :type initial_means: sequence of vectors + :param normalise: should vectors be normalised to length 1 + :type normalise: boolean + :param svd_dimensions: number of dimensions to use in reducing vector + dimensionsionality with SVD + :type svd_dimensions: int + :param rng: random number generator (or None) + :type rng: Random + :param avoid_empty_clusters: include current centroid in computation + of next one; avoids undefined behavior + when clusters become empty + :type avoid_empty_clusters: boolean + """ + VectorSpaceClusterer.__init__(self, normalise, svd_dimensions) + self._num_means = num_means + self._distance = distance + self._max_difference = conv_test + assert not initial_means or len(initial_means) == num_means + self._means = initial_means + assert repeats >= 1 + assert not (initial_means and repeats > 1) + self._repeats = repeats + self._rng = rng if rng else random.Random() + self._avoid_empty_clusters = avoid_empty_clusters + + def cluster_vectorspace(self, vectors, trace=False): + if self._means and self._repeats > 1: + print("Warning: means will be discarded for subsequent trials") + + meanss = [] + for trial in range(self._repeats): + if trace: + print("k-means trial", trial) + if not self._means or trial > 1: + self._means = self._rng.sample(list(vectors), self._num_means) + self._cluster_vectorspace(vectors, trace) + meanss.append(self._means) + + if len(meanss) > 1: + # sort the means first (so that different cluster numbering won't + # effect the distance comparison) + for means in meanss: + means.sort(key=sum) + + # find the set of means that's minimally different from the others + min_difference = min_means = None + for i in range(len(meanss)): + d = 0 + for j in range(len(meanss)): + if i != j: + d += self._sum_distances(meanss[i], meanss[j]) + if min_difference is None or d < min_difference: + min_difference, min_means = d, meanss[i] + + # use the best means + self._means = min_means + + def _cluster_vectorspace(self, vectors, trace=False): + if self._num_means < len(vectors): + # perform k-means clustering + converged = False + while not converged: + # assign the tokens to clusters based on minimum distance to + # the cluster means + clusters = [[] for m in range(self._num_means)] + for vector in vectors: + index = self.classify_vectorspace(vector) + clusters[index].append(vector) + + if trace: + print("iteration") + # for i in range(self._num_means): + # print ' mean', i, 'allocated', len(clusters[i]), 'vectors' + + # recalculate cluster means by computing the centroid of each cluster + new_means = list(map(self._centroid, clusters, self._means)) + + # measure the degree of change from the previous step for convergence + difference = self._sum_distances(self._means, new_means) + if difference < self._max_difference: + converged = True + + # remember the new means + self._means = new_means + + def classify_vectorspace(self, vector): + # finds the closest cluster centroid + # returns that cluster's index + best_distance = best_index = None + for index in range(len(self._means)): + mean = self._means[index] + dist = self._distance(vector, mean) + if best_distance is None or dist < best_distance: + best_index, best_distance = index, dist + return best_index + + def num_clusters(self): + if self._means: + return len(self._means) + else: + return self._num_means + + def means(self): + """ + The means used for clustering. + """ + return self._means + + def _sum_distances(self, vectors1, vectors2): + difference = 0.0 + for u, v in zip(vectors1, vectors2): + difference += self._distance(u, v) + return difference + + def _centroid(self, cluster, mean): + if self._avoid_empty_clusters: + centroid = copy.copy(mean) + for vector in cluster: + centroid += vector + return centroid / (1 + len(cluster)) + else: + if not len(cluster): + sys.stderr.write("Error: no centroid defined for empty cluster.\n") + sys.stderr.write( + "Try setting argument 'avoid_empty_clusters' to True\n" + ) + assert False + centroid = copy.copy(cluster[0]) + for vector in cluster[1:]: + centroid += vector + return centroid / len(cluster) + + def __repr__(self): + return "" % (self._means, self._repeats) + + +################################################################################# + + +def demo(): + # example from figure 14.9, page 517, Manning and Schutze + + from nltk.cluster import KMeansClusterer, euclidean_distance + + vectors = [numpy.array(f) for f in [[2, 1], [1, 3], [4, 7], [6, 7]]] + means = [[4, 3], [5, 5]] + + clusterer = KMeansClusterer(2, euclidean_distance, initial_means=means) + clusters = clusterer.cluster(vectors, True, trace=True) + + print("Clustered:", vectors) + print("As:", clusters) + print("Means:", clusterer.means()) + print() + + vectors = [numpy.array(f) for f in [[3, 3], [1, 2], [4, 2], [4, 0], [2, 3], [3, 1]]] + + # test k-means using the euclidean distance metric, 2 means and repeat + # clustering 10 times with random seeds + + clusterer = KMeansClusterer(2, euclidean_distance, repeats=10) + clusters = clusterer.cluster(vectors, True) + print("Clustered:", vectors) + print("As:", clusters) + print("Means:", clusterer.means()) + print() + + # classify a new vector + vector = numpy.array([3, 3]) + print("classify(%s):" % vector, end=" ") + print(clusterer.classify(vector)) + print() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/util.py new file mode 100644 index 0000000..bec4f67 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/cluster/util.py @@ -0,0 +1,300 @@ +# Natural Language Toolkit: Clusterer Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Contributor: J Richard Snape +# URL: +# For license information, see LICENSE.TXT +import copy +from abc import abstractmethod +from math import sqrt +from sys import stdout + +try: + import numpy +except ImportError: + pass + +from nltk.cluster.api import ClusterI + + +class VectorSpaceClusterer(ClusterI): + """ + Abstract clusterer which takes tokens and maps them into a vector space. + Optionally performs singular value decomposition to reduce the + dimensionality. + """ + + def __init__(self, normalise=False, svd_dimensions=None): + """ + :param normalise: should vectors be normalised to length 1 + :type normalise: boolean + :param svd_dimensions: number of dimensions to use in reducing vector + dimensionsionality with SVD + :type svd_dimensions: int + """ + self._Tt = None + self._should_normalise = normalise + self._svd_dimensions = svd_dimensions + + def cluster(self, vectors, assign_clusters=False, trace=False): + assert len(vectors) > 0 + + # normalise the vectors + if self._should_normalise: + vectors = list(map(self._normalise, vectors)) + + # use SVD to reduce the dimensionality + if self._svd_dimensions and self._svd_dimensions < len(vectors[0]): + [u, d, vt] = numpy.linalg.svd(numpy.transpose(numpy.array(vectors))) + S = d[: self._svd_dimensions] * numpy.identity( + self._svd_dimensions, numpy.float64 + ) + T = u[:, : self._svd_dimensions] + Dt = vt[: self._svd_dimensions, :] + vectors = numpy.transpose(numpy.dot(S, Dt)) + self._Tt = numpy.transpose(T) + + # call abstract method to cluster the vectors + self.cluster_vectorspace(vectors, trace) + + # assign the vectors to clusters + if assign_clusters: + return [self.classify(vector) for vector in vectors] + + @abstractmethod + def cluster_vectorspace(self, vectors, trace): + """ + Finds the clusters using the given set of vectors. + """ + + def classify(self, vector): + if self._should_normalise: + vector = self._normalise(vector) + if self._Tt is not None: + vector = numpy.dot(self._Tt, vector) + cluster = self.classify_vectorspace(vector) + return self.cluster_name(cluster) + + @abstractmethod + def classify_vectorspace(self, vector): + """ + Returns the index of the appropriate cluster for the vector. + """ + + def likelihood(self, vector, label): + if self._should_normalise: + vector = self._normalise(vector) + if self._Tt is not None: + vector = numpy.dot(self._Tt, vector) + return self.likelihood_vectorspace(vector, label) + + def likelihood_vectorspace(self, vector, cluster): + """ + Returns the likelihood of the vector belonging to the cluster. + """ + predicted = self.classify_vectorspace(vector) + return 1.0 if cluster == predicted else 0.0 + + def vector(self, vector): + """ + Returns the vector after normalisation and dimensionality reduction + """ + if self._should_normalise: + vector = self._normalise(vector) + if self._Tt is not None: + vector = numpy.dot(self._Tt, vector) + return vector + + def _normalise(self, vector): + """ + Normalises the vector to unit length. + """ + return vector / sqrt(numpy.dot(vector, vector)) + + +def euclidean_distance(u, v): + """ + Returns the euclidean distance between vectors u and v. This is equivalent + to the length of the vector (u - v). + """ + diff = u - v + return sqrt(numpy.dot(diff, diff)) + + +def cosine_distance(u, v): + """ + Returns 1 minus the cosine of the angle between vectors v and u. This is + equal to ``1 - (u.v / |u||v|)``. + """ + return 1 - (numpy.dot(u, v) / (sqrt(numpy.dot(u, u)) * sqrt(numpy.dot(v, v)))) + + +class _DendrogramNode: + """Tree node of a dendrogram.""" + + def __init__(self, value, *children): + self._value = value + self._children = children + + def leaves(self, values=True): + if self._children: + leaves = [] + for child in self._children: + leaves.extend(child.leaves(values)) + return leaves + elif values: + return [self._value] + else: + return [self] + + def groups(self, n): + queue = [(self._value, self)] + + while len(queue) < n: + priority, node = queue.pop() + if not node._children: + queue.push((priority, node)) + break + for child in node._children: + if child._children: + queue.append((child._value, child)) + else: + queue.append((0, child)) + # makes the earliest merges at the start, latest at the end + queue.sort() + + groups = [] + for priority, node in queue: + groups.append(node.leaves()) + return groups + + def __lt__(self, comparator): + return cosine_distance(self._value, comparator._value) < 0 + + +class Dendrogram: + """ + Represents a dendrogram, a tree with a specified branching order. This + must be initialised with the leaf items, then iteratively call merge for + each branch. This class constructs a tree representing the order of calls + to the merge function. + """ + + def __init__(self, items=[]): + """ + :param items: the items at the leaves of the dendrogram + :type items: sequence of (any) + """ + self._items = [_DendrogramNode(item) for item in items] + self._original_items = copy.copy(self._items) + self._merge = 1 + + def merge(self, *indices): + """ + Merges nodes at given indices in the dendrogram. The nodes will be + combined which then replaces the first node specified. All other nodes + involved in the merge will be removed. + + :param indices: indices of the items to merge (at least two) + :type indices: seq of int + """ + assert len(indices) >= 2 + node = _DendrogramNode(self._merge, *(self._items[i] for i in indices)) + self._merge += 1 + self._items[indices[0]] = node + for i in indices[1:]: + del self._items[i] + + def groups(self, n): + """ + Finds the n-groups of items (leaves) reachable from a cut at depth n. + :param n: number of groups + :type n: int + """ + if len(self._items) > 1: + root = _DendrogramNode(self._merge, *self._items) + else: + root = self._items[0] + return root.groups(n) + + def show(self, leaf_labels=[]): + """ + Print the dendrogram in ASCII art to standard out. + + :param leaf_labels: an optional list of strings to use for labeling the + leaves + :type leaf_labels: list + """ + + # ASCII rendering characters + JOIN, HLINK, VLINK = "+", "-", "|" + + # find the root (or create one) + if len(self._items) > 1: + root = _DendrogramNode(self._merge, *self._items) + else: + root = self._items[0] + leaves = self._original_items + + if leaf_labels: + last_row = leaf_labels + else: + last_row = ["%s" % leaf._value for leaf in leaves] + + # find the bottom row and the best cell width + width = max(map(len, last_row)) + 1 + lhalf = width // 2 + rhalf = int(width - lhalf - 1) + + # display functions + def format(centre, left=" ", right=" "): + return f"{lhalf * left}{centre}{right * rhalf}" + + def display(str): + stdout.write(str) + + # for each merge, top down + queue = [(root._value, root)] + verticals = [format(" ") for leaf in leaves] + while queue: + priority, node = queue.pop() + child_left_leaf = list(map(lambda c: c.leaves(False)[0], node._children)) + indices = list(map(leaves.index, child_left_leaf)) + if child_left_leaf: + min_idx = min(indices) + max_idx = max(indices) + for i in range(len(leaves)): + if leaves[i] in child_left_leaf: + if i == min_idx: + display(format(JOIN, " ", HLINK)) + elif i == max_idx: + display(format(JOIN, HLINK, " ")) + else: + display(format(JOIN, HLINK, HLINK)) + verticals[i] = format(VLINK) + elif min_idx <= i <= max_idx: + display(format(HLINK, HLINK, HLINK)) + else: + display(verticals[i]) + display("\n") + for child in node._children: + if child._children: + queue.append((child._value, child)) + queue.sort() + + for vertical in verticals: + display(vertical) + display("\n") + + # finally, display the last line + display("".join(item.center(width) for item in last_row)) + display("\n") + + def __repr__(self): + if len(self._items) > 1: + root = _DendrogramNode(self._merge, *self._items) + else: + root = self._items[0] + leaves = root.leaves(False) + return "" % len(leaves) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/collections.py b/.pythonlibs/lib/python3.10/site-packages/nltk/collections.py new file mode 100644 index 0000000..a8a64a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/collections.py @@ -0,0 +1,661 @@ +# Natural Language Toolkit: Collections +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +import bisect + +# this unused import is for python 2.7 +from collections import Counter, defaultdict, deque +from functools import total_ordering +from itertools import chain, islice + +from nltk.internals import raise_unorderable_types, slice_bounds + +########################################################################## +# Ordered Dictionary +########################################################################## + + +class OrderedDict(dict): + def __init__(self, data=None, **kwargs): + self._keys = self.keys(data, kwargs.get("keys")) + self._default_factory = kwargs.get("default_factory") + if data is None: + dict.__init__(self) + else: + dict.__init__(self, data) + + def __delitem__(self, key): + dict.__delitem__(self, key) + self._keys.remove(key) + + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + return self.__missing__(key) + + def __iter__(self): + return (key for key in self.keys()) + + def __missing__(self, key): + if not self._default_factory and key not in self._keys: + raise KeyError() + return self._default_factory() + + def __setitem__(self, key, item): + dict.__setitem__(self, key, item) + if key not in self._keys: + self._keys.append(key) + + def clear(self): + dict.clear(self) + self._keys.clear() + + def copy(self): + d = dict.copy(self) + d._keys = self._keys + return d + + def items(self): + # returns iterator under python 3 and list under python 2 + return zip(self.keys(), self.values()) + + def keys(self, data=None, keys=None): + if data: + if keys: + assert isinstance(keys, list) + assert len(data) == len(keys) + return keys + else: + assert ( + isinstance(data, dict) + or isinstance(data, OrderedDict) + or isinstance(data, list) + ) + if isinstance(data, dict) or isinstance(data, OrderedDict): + return data.keys() + elif isinstance(data, list): + return [key for (key, value) in data] + elif "_keys" in self.__dict__: + return self._keys + else: + return [] + + def popitem(self): + if not self._keys: + raise KeyError() + + key = self._keys.pop() + value = self[key] + del self[key] + return (key, value) + + def setdefault(self, key, failobj=None): + dict.setdefault(self, key, failobj) + if key not in self._keys: + self._keys.append(key) + + def update(self, data): + dict.update(self, data) + for key in self.keys(data): + if key not in self._keys: + self._keys.append(key) + + def values(self): + # returns iterator under python 3 + return map(self.get, self._keys) + + +###################################################################### +# Lazy Sequences +###################################################################### + + +@total_ordering +class AbstractLazySequence: + """ + An abstract base class for read-only sequences whose values are + computed as needed. Lazy sequences act like tuples -- they can be + indexed, sliced, and iterated over; but they may not be modified. + + The most common application of lazy sequences in NLTK is for + corpus view objects, which provide access to the contents of a + corpus without loading the entire corpus into memory, by loading + pieces of the corpus from disk as needed. + + The result of modifying a mutable element of a lazy sequence is + undefined. In particular, the modifications made to the element + may or may not persist, depending on whether and when the lazy + sequence caches that element's value or reconstructs it from + scratch. + + Subclasses are required to define two methods: ``__len__()`` + and ``iterate_from()``. + """ + + def __len__(self): + """ + Return the number of tokens in the corpus file underlying this + corpus view. + """ + raise NotImplementedError("should be implemented by subclass") + + def iterate_from(self, start): + """ + Return an iterator that generates the tokens in the corpus + file underlying this corpus view, starting at the token number + ``start``. If ``start>=len(self)``, then this iterator will + generate no tokens. + """ + raise NotImplementedError("should be implemented by subclass") + + def __getitem__(self, i): + """ + Return the *i* th token in the corpus file underlying this + corpus view. Negative indices and spans are both supported. + """ + if isinstance(i, slice): + start, stop = slice_bounds(self, i) + return LazySubsequence(self, start, stop) + else: + # Handle negative indices + if i < 0: + i += len(self) + if i < 0: + raise IndexError("index out of range") + # Use iterate_from to extract it. + try: + return next(self.iterate_from(i)) + except StopIteration as e: + raise IndexError("index out of range") from e + + def __iter__(self): + """Return an iterator that generates the tokens in the corpus + file underlying this corpus view.""" + return self.iterate_from(0) + + def count(self, value): + """Return the number of times this list contains ``value``.""" + return sum(1 for elt in self if elt == value) + + def index(self, value, start=None, stop=None): + """Return the index of the first occurrence of ``value`` in this + list that is greater than or equal to ``start`` and less than + ``stop``. Negative start and stop values are treated like negative + slice bounds -- i.e., they count from the end of the list.""" + start, stop = slice_bounds(self, slice(start, stop)) + for i, elt in enumerate(islice(self, start, stop)): + if elt == value: + return i + start + raise ValueError("index(x): x not in list") + + def __contains__(self, value): + """Return true if this list contains ``value``.""" + return bool(self.count(value)) + + def __add__(self, other): + """Return a list concatenating self with other.""" + return LazyConcatenation([self, other]) + + def __radd__(self, other): + """Return a list concatenating other with self.""" + return LazyConcatenation([other, self]) + + def __mul__(self, count): + """Return a list concatenating self with itself ``count`` times.""" + return LazyConcatenation([self] * count) + + def __rmul__(self, count): + """Return a list concatenating self with itself ``count`` times.""" + return LazyConcatenation([self] * count) + + _MAX_REPR_SIZE = 60 + + def __repr__(self): + """ + Return a string representation for this corpus view that is + similar to a list's representation; but if it would be more + than 60 characters long, it is truncated. + """ + pieces = [] + length = 5 + for elt in self: + pieces.append(repr(elt)) + length += len(pieces[-1]) + 2 + if length > self._MAX_REPR_SIZE and len(pieces) > 2: + return "[%s, ...]" % ", ".join(pieces[:-1]) + return "[%s]" % ", ".join(pieces) + + def __eq__(self, other): + return type(self) == type(other) and list(self) == list(other) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if type(other) != type(self): + raise_unorderable_types("<", self, other) + return list(self) < list(other) + + def __hash__(self): + """ + :raise ValueError: Corpus view objects are unhashable. + """ + raise ValueError("%s objects are unhashable" % self.__class__.__name__) + + +class LazySubsequence(AbstractLazySequence): + """ + A subsequence produced by slicing a lazy sequence. This slice + keeps a reference to its source sequence, and generates its values + by looking them up in the source sequence. + """ + + MIN_SIZE = 100 + """ + The minimum size for which lazy slices should be created. If + ``LazySubsequence()`` is called with a subsequence that is + shorter than ``MIN_SIZE``, then a tuple will be returned instead. + """ + + def __new__(cls, source, start, stop): + """ + Construct a new slice from a given underlying sequence. The + ``start`` and ``stop`` indices should be absolute indices -- + i.e., they should not be negative (for indexing from the back + of a list) or greater than the length of ``source``. + """ + # If the slice is small enough, just use a tuple. + if stop - start < cls.MIN_SIZE: + return list(islice(source.iterate_from(start), stop - start)) + else: + return object.__new__(cls) + + def __init__(self, source, start, stop): + self._source = source + self._start = start + self._stop = stop + + def __len__(self): + return self._stop - self._start + + def iterate_from(self, start): + return islice( + self._source.iterate_from(start + self._start), max(0, len(self) - start) + ) + + +class LazyConcatenation(AbstractLazySequence): + """ + A lazy sequence formed by concatenating a list of lists. This + underlying list of lists may itself be lazy. ``LazyConcatenation`` + maintains an index that it uses to keep track of the relationship + between offsets in the concatenated lists and offsets in the + sublists. + """ + + def __init__(self, list_of_lists): + self._list = list_of_lists + self._offsets = [0] + + def __len__(self): + if len(self._offsets) <= len(self._list): + for _ in self.iterate_from(self._offsets[-1]): + pass + return self._offsets[-1] + + def iterate_from(self, start_index): + if start_index < self._offsets[-1]: + sublist_index = bisect.bisect_right(self._offsets, start_index) - 1 + else: + sublist_index = len(self._offsets) - 1 + + index = self._offsets[sublist_index] + + # Construct an iterator over the sublists. + if isinstance(self._list, AbstractLazySequence): + sublist_iter = self._list.iterate_from(sublist_index) + else: + sublist_iter = islice(self._list, sublist_index, None) + + for sublist in sublist_iter: + if sublist_index == (len(self._offsets) - 1): + assert ( + index + len(sublist) >= self._offsets[-1] + ), "offsets not monotonic increasing!" + self._offsets.append(index + len(sublist)) + else: + assert self._offsets[sublist_index + 1] == index + len( + sublist + ), "inconsistent list value (num elts)" + + yield from sublist[max(0, start_index - index) :] + + index += len(sublist) + sublist_index += 1 + + +class LazyMap(AbstractLazySequence): + """ + A lazy sequence whose elements are formed by applying a given + function to each element in one or more underlying lists. The + function is applied lazily -- i.e., when you read a value from the + list, ``LazyMap`` will calculate that value by applying its + function to the underlying lists' value(s). ``LazyMap`` is + essentially a lazy version of the Python primitive function + ``map``. In particular, the following two expressions are + equivalent: + + >>> from nltk.collections import LazyMap + >>> function = str + >>> sequence = [1,2,3] + >>> map(function, sequence) # doctest: +SKIP + ['1', '2', '3'] + >>> list(LazyMap(function, sequence)) + ['1', '2', '3'] + + Like the Python ``map`` primitive, if the source lists do not have + equal size, then the value None will be supplied for the + 'missing' elements. + + Lazy maps can be useful for conserving memory, in cases where + individual values take up a lot of space. This is especially true + if the underlying list's values are constructed lazily, as is the + case with many corpus readers. + + A typical example of a use case for this class is performing + feature detection on the tokens in a corpus. Since featuresets + are encoded as dictionaries, which can take up a lot of memory, + using a ``LazyMap`` can significantly reduce memory usage when + training and running classifiers. + """ + + def __init__(self, function, *lists, **config): + """ + :param function: The function that should be applied to + elements of ``lists``. It should take as many arguments + as there are ``lists``. + :param lists: The underlying lists. + :param cache_size: Determines the size of the cache used + by this lazy map. (default=5) + """ + if not lists: + raise TypeError("LazyMap requires at least two args") + + self._lists = lists + self._func = function + self._cache_size = config.get("cache_size", 5) + self._cache = {} if self._cache_size > 0 else None + + # If you just take bool() of sum() here _all_lazy will be true just + # in case n >= 1 list is an AbstractLazySequence. Presumably this + # isn't what's intended. + self._all_lazy = sum( + isinstance(lst, AbstractLazySequence) for lst in lists + ) == len(lists) + + def iterate_from(self, index): + # Special case: one lazy sublist + if len(self._lists) == 1 and self._all_lazy: + for value in self._lists[0].iterate_from(index): + yield self._func(value) + return + + # Special case: one non-lazy sublist + elif len(self._lists) == 1: + while True: + try: + yield self._func(self._lists[0][index]) + except IndexError: + return + index += 1 + + # Special case: n lazy sublists + elif self._all_lazy: + iterators = [lst.iterate_from(index) for lst in self._lists] + while True: + elements = [] + for iterator in iterators: + try: + elements.append(next(iterator)) + except: # FIXME: What is this except really catching? StopIteration? + elements.append(None) + if elements == [None] * len(self._lists): + return + yield self._func(*elements) + index += 1 + + # general case + else: + while True: + try: + elements = [lst[index] for lst in self._lists] + except IndexError: + elements = [None] * len(self._lists) + for i, lst in enumerate(self._lists): + try: + elements[i] = lst[index] + except IndexError: + pass + if elements == [None] * len(self._lists): + return + yield self._func(*elements) + index += 1 + + def __getitem__(self, index): + if isinstance(index, slice): + sliced_lists = [lst[index] for lst in self._lists] + return LazyMap(self._func, *sliced_lists) + else: + # Handle negative indices + if index < 0: + index += len(self) + if index < 0: + raise IndexError("index out of range") + # Check the cache + if self._cache is not None and index in self._cache: + return self._cache[index] + # Calculate the value + try: + val = next(self.iterate_from(index)) + except StopIteration as e: + raise IndexError("index out of range") from e + # Update the cache + if self._cache is not None: + if len(self._cache) > self._cache_size: + self._cache.popitem() # discard random entry + self._cache[index] = val + # Return the value + return val + + def __len__(self): + return max(len(lst) for lst in self._lists) + + +class LazyZip(LazyMap): + """ + A lazy sequence whose elements are tuples, each containing the i-th + element from each of the argument sequences. The returned list is + truncated in length to the length of the shortest argument sequence. The + tuples are constructed lazily -- i.e., when you read a value from the + list, ``LazyZip`` will calculate that value by forming a tuple from + the i-th element of each of the argument sequences. + + ``LazyZip`` is essentially a lazy version of the Python primitive function + ``zip``. In particular, an evaluated LazyZip is equivalent to a zip: + + >>> from nltk.collections import LazyZip + >>> sequence1, sequence2 = [1, 2, 3], ['a', 'b', 'c'] + >>> zip(sequence1, sequence2) # doctest: +SKIP + [(1, 'a'), (2, 'b'), (3, 'c')] + >>> list(LazyZip(sequence1, sequence2)) + [(1, 'a'), (2, 'b'), (3, 'c')] + >>> sequences = [sequence1, sequence2, [6,7,8,9]] + >>> list(zip(*sequences)) == list(LazyZip(*sequences)) + True + + Lazy zips can be useful for conserving memory in cases where the argument + sequences are particularly long. + + A typical example of a use case for this class is combining long sequences + of gold standard and predicted values in a classification or tagging task + in order to calculate accuracy. By constructing tuples lazily and + avoiding the creation of an additional long sequence, memory usage can be + significantly reduced. + """ + + def __init__(self, *lists): + """ + :param lists: the underlying lists + :type lists: list(list) + """ + LazyMap.__init__(self, lambda *elts: elts, *lists) + + def iterate_from(self, index): + iterator = LazyMap.iterate_from(self, index) + while index < len(self): + yield next(iterator) + index += 1 + return + + def __len__(self): + return min(len(lst) for lst in self._lists) + + +class LazyEnumerate(LazyZip): + """ + A lazy sequence whose elements are tuples, each containing a count (from + zero) and a value yielded by underlying sequence. ``LazyEnumerate`` is + useful for obtaining an indexed list. The tuples are constructed lazily + -- i.e., when you read a value from the list, ``LazyEnumerate`` will + calculate that value by forming a tuple from the count of the i-th + element and the i-th element of the underlying sequence. + + ``LazyEnumerate`` is essentially a lazy version of the Python primitive + function ``enumerate``. In particular, the following two expressions are + equivalent: + + >>> from nltk.collections import LazyEnumerate + >>> sequence = ['first', 'second', 'third'] + >>> list(enumerate(sequence)) + [(0, 'first'), (1, 'second'), (2, 'third')] + >>> list(LazyEnumerate(sequence)) + [(0, 'first'), (1, 'second'), (2, 'third')] + + Lazy enumerations can be useful for conserving memory in cases where the + argument sequences are particularly long. + + A typical example of a use case for this class is obtaining an indexed + list for a long sequence of values. By constructing tuples lazily and + avoiding the creation of an additional long sequence, memory usage can be + significantly reduced. + """ + + def __init__(self, lst): + """ + :param lst: the underlying list + :type lst: list + """ + LazyZip.__init__(self, range(len(lst)), lst) + + +class LazyIteratorList(AbstractLazySequence): + """ + Wraps an iterator, loading its elements on demand + and making them subscriptable. + __repr__ displays only the first few elements. + """ + + def __init__(self, it, known_len=None): + self._it = it + self._len = known_len + self._cache = [] + + def __len__(self): + if self._len: + return self._len + for _ in self.iterate_from(len(self._cache)): + pass + self._len = len(self._cache) + return self._len + + def iterate_from(self, start): + """Create a new iterator over this list starting at the given offset.""" + while len(self._cache) < start: + v = next(self._it) + self._cache.append(v) + i = start + while i < len(self._cache): + yield self._cache[i] + i += 1 + try: + while True: + v = next(self._it) + self._cache.append(v) + yield v + except StopIteration: + pass + + def __add__(self, other): + """Return a list concatenating self with other.""" + return type(self)(chain(self, other)) + + def __radd__(self, other): + """Return a list concatenating other with self.""" + return type(self)(chain(other, self)) + + +###################################################################### +# Trie Implementation +###################################################################### +class Trie(dict): + """A Trie implementation for strings""" + + LEAF = True + + def __init__(self, strings=None): + """Builds a Trie object, which is built around a ``dict`` + + If ``strings`` is provided, it will add the ``strings``, which + consist of a ``list`` of ``strings``, to the Trie. + Otherwise, it'll construct an empty Trie. + + :param strings: List of strings to insert into the trie + (Default is ``None``) + :type strings: list(str) + + """ + super().__init__() + if strings: + for string in strings: + self.insert(string) + + def insert(self, string): + """Inserts ``string`` into the Trie + + :param string: String to insert into the trie + :type string: str + + :Example: + + >>> from nltk.collections import Trie + >>> trie = Trie(["abc", "def"]) + >>> expected = {'a': {'b': {'c': {True: None}}}, \ + 'd': {'e': {'f': {True: None}}}} + >>> trie == expected + True + + """ + if len(string): + self[string[0]].insert(string[1:]) + else: + # mark the string is complete + self[Trie.LEAF] = None + + def __missing__(self, key): + self[key] = Trie() + return self[key] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/collocations.py b/.pythonlibs/lib/python3.10/site-packages/nltk/collocations.py new file mode 100644 index 0000000..7531f88 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/collocations.py @@ -0,0 +1,412 @@ +# Natural Language Toolkit: Collocations and Association Measures +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Joel Nothman +# URL: +# For license information, see LICENSE.TXT +# +""" +Tools to identify collocations --- words that often appear consecutively +--- within corpora. They may also be used to find other associations between +word occurrences. +See Manning and Schutze ch. 5 at https://nlp.stanford.edu/fsnlp/promo/colloc.pdf +and the Text::NSP Perl package at http://ngram.sourceforge.net + +Finding collocations requires first calculating the frequencies of words and +their appearance in the context of other words. Often the collection of words +will then requiring filtering to only retain useful content terms. Each ngram +of words may then be scored according to some association measure, in order +to determine the relative likelihood of each ngram being a collocation. + +The ``BigramCollocationFinder`` and ``TrigramCollocationFinder`` classes provide +these functionalities, dependent on being provided a function which scores a +ngram given appropriate frequency counts. A number of standard association +measures are provided in bigram_measures and trigram_measures. +""" + +# Possible TODOs: +# - consider the distinction between f(x,_) and f(x) and whether our +# approximation is good enough for fragmented data, and mention it +# - add a n-gram collocation finder with measures which only utilise n-gram +# and unigram counts (raw_freq, pmi, student_t) + +import itertools as _itertools + +# these two unused imports are referenced in collocations.doctest +from nltk.metrics import ( + BigramAssocMeasures, + ContingencyMeasures, + QuadgramAssocMeasures, + TrigramAssocMeasures, +) +from nltk.metrics.spearman import ranks_from_scores, spearman_correlation +from nltk.probability import FreqDist +from nltk.util import ngrams + + +class AbstractCollocationFinder: + """ + An abstract base class for collocation finders whose purpose is to + collect collocation candidate frequencies, filter and rank them. + + As a minimum, collocation finders require the frequencies of each + word in a corpus, and the joint frequency of word tuples. This data + should be provided through nltk.probability.FreqDist objects or an + identical interface. + """ + + def __init__(self, word_fd, ngram_fd): + self.word_fd = word_fd + self.N = word_fd.N() + self.ngram_fd = ngram_fd + + @classmethod + def _build_new_documents( + cls, documents, window_size, pad_left=False, pad_right=False, pad_symbol=None + ): + """ + Pad the document with the place holder according to the window_size + """ + padding = (pad_symbol,) * (window_size - 1) + if pad_right: + return _itertools.chain.from_iterable( + _itertools.chain(doc, padding) for doc in documents + ) + if pad_left: + return _itertools.chain.from_iterable( + _itertools.chain(padding, doc) for doc in documents + ) + + @classmethod + def from_documents(cls, documents): + """Constructs a collocation finder given a collection of documents, + each of which is a list (or iterable) of tokens. + """ + # return cls.from_words(_itertools.chain(*documents)) + return cls.from_words( + cls._build_new_documents(documents, cls.default_ws, pad_right=True) + ) + + @staticmethod + def _ngram_freqdist(words, n): + return FreqDist(tuple(words[i : i + n]) for i in range(len(words) - 1)) + + def _apply_filter(self, fn=lambda ngram, freq: False): + """Generic filter removes ngrams from the frequency distribution + if the function returns True when passed an ngram tuple. + """ + tmp_ngram = FreqDist() + for ngram, freq in self.ngram_fd.items(): + if not fn(ngram, freq): + tmp_ngram[ngram] = freq + self.ngram_fd = tmp_ngram + + def apply_freq_filter(self, min_freq): + """Removes candidate ngrams which have frequency less than min_freq.""" + self._apply_filter(lambda ng, freq: freq < min_freq) + + def apply_ngram_filter(self, fn): + """Removes candidate ngrams (w1, w2, ...) where fn(w1, w2, ...) + evaluates to True. + """ + self._apply_filter(lambda ng, f: fn(*ng)) + + def apply_word_filter(self, fn): + """Removes candidate ngrams (w1, w2, ...) where any of (fn(w1), fn(w2), + ...) evaluates to True. + """ + self._apply_filter(lambda ng, f: any(fn(w) for w in ng)) + + def _score_ngrams(self, score_fn): + """Generates of (ngram, score) pairs as determined by the scoring + function provided. + """ + for tup in self.ngram_fd: + score = self.score_ngram(score_fn, *tup) + if score is not None: + yield tup, score + + def score_ngrams(self, score_fn): + """Returns a sequence of (ngram, score) pairs ordered from highest to + lowest score, as determined by the scoring function provided. + """ + return sorted(self._score_ngrams(score_fn), key=lambda t: (-t[1], t[0])) + + def nbest(self, score_fn, n): + """Returns the top n ngrams when scored by the given function.""" + return [p for p, s in self.score_ngrams(score_fn)[:n]] + + def above_score(self, score_fn, min_score): + """Returns a sequence of ngrams, ordered by decreasing score, whose + scores each exceed the given minimum score. + """ + for ngram, score in self.score_ngrams(score_fn): + if score > min_score: + yield ngram + else: + break + + +class BigramCollocationFinder(AbstractCollocationFinder): + """A tool for the finding and ranking of bigram collocations or other + association measures. It is often useful to use from_words() rather than + constructing an instance directly. + """ + + default_ws = 2 + + def __init__(self, word_fd, bigram_fd, window_size=2): + """Construct a BigramCollocationFinder, given FreqDists for + appearances of words and (possibly non-contiguous) bigrams. + """ + AbstractCollocationFinder.__init__(self, word_fd, bigram_fd) + self.window_size = window_size + + @classmethod + def from_words(cls, words, window_size=2): + """Construct a BigramCollocationFinder for all bigrams in the given + sequence. When window_size > 2, count non-contiguous bigrams, in the + style of Church and Hanks's (1990) association ratio. + """ + wfd = FreqDist() + bfd = FreqDist() + + if window_size < 2: + raise ValueError("Specify window_size at least 2") + + for window in ngrams(words, window_size, pad_right=True): + w1 = window[0] + if w1 is None: + continue + wfd[w1] += 1 + for w2 in window[1:]: + if w2 is not None: + bfd[(w1, w2)] += 1 + return cls(wfd, bfd, window_size=window_size) + + def score_ngram(self, score_fn, w1, w2): + """Returns the score for a given bigram using the given scoring + function. Following Church and Hanks (1990), counts are scaled by + a factor of 1/(window_size - 1). + """ + n_all = self.N + n_ii = self.ngram_fd[(w1, w2)] / (self.window_size - 1.0) + if not n_ii: + return + n_ix = self.word_fd[w1] + n_xi = self.word_fd[w2] + return score_fn(n_ii, (n_ix, n_xi), n_all) + + +class TrigramCollocationFinder(AbstractCollocationFinder): + """A tool for the finding and ranking of trigram collocations or other + association measures. It is often useful to use from_words() rather than + constructing an instance directly. + """ + + default_ws = 3 + + def __init__(self, word_fd, bigram_fd, wildcard_fd, trigram_fd): + """Construct a TrigramCollocationFinder, given FreqDists for + appearances of words, bigrams, two words with any word between them, + and trigrams. + """ + AbstractCollocationFinder.__init__(self, word_fd, trigram_fd) + self.wildcard_fd = wildcard_fd + self.bigram_fd = bigram_fd + + @classmethod + def from_words(cls, words, window_size=3): + """Construct a TrigramCollocationFinder for all trigrams in the given + sequence. + """ + if window_size < 3: + raise ValueError("Specify window_size at least 3") + + wfd = FreqDist() + wildfd = FreqDist() + bfd = FreqDist() + tfd = FreqDist() + for window in ngrams(words, window_size, pad_right=True): + w1 = window[0] + if w1 is None: + continue + for w2, w3 in _itertools.combinations(window[1:], 2): + wfd[w1] += 1 + if w2 is None: + continue + bfd[(w1, w2)] += 1 + if w3 is None: + continue + wildfd[(w1, w3)] += 1 + tfd[(w1, w2, w3)] += 1 + return cls(wfd, bfd, wildfd, tfd) + + def bigram_finder(self): + """Constructs a bigram collocation finder with the bigram and unigram + data from this finder. Note that this does not include any filtering + applied to this finder. + """ + return BigramCollocationFinder(self.word_fd, self.bigram_fd) + + def score_ngram(self, score_fn, w1, w2, w3): + """Returns the score for a given trigram using the given scoring + function. + """ + n_all = self.N + n_iii = self.ngram_fd[(w1, w2, w3)] + if not n_iii: + return + n_iix = self.bigram_fd[(w1, w2)] + n_ixi = self.wildcard_fd[(w1, w3)] + n_xii = self.bigram_fd[(w2, w3)] + n_ixx = self.word_fd[w1] + n_xix = self.word_fd[w2] + n_xxi = self.word_fd[w3] + return score_fn(n_iii, (n_iix, n_ixi, n_xii), (n_ixx, n_xix, n_xxi), n_all) + + +class QuadgramCollocationFinder(AbstractCollocationFinder): + """A tool for the finding and ranking of quadgram collocations or other association measures. + It is often useful to use from_words() rather than constructing an instance directly. + """ + + default_ws = 4 + + def __init__(self, word_fd, quadgram_fd, ii, iii, ixi, ixxi, iixi, ixii): + """Construct a QuadgramCollocationFinder, given FreqDists for appearances of words, + bigrams, trigrams, two words with one word and two words between them, three words + with a word between them in both variations. + """ + AbstractCollocationFinder.__init__(self, word_fd, quadgram_fd) + self.iii = iii + self.ii = ii + self.ixi = ixi + self.ixxi = ixxi + self.iixi = iixi + self.ixii = ixii + + @classmethod + def from_words(cls, words, window_size=4): + if window_size < 4: + raise ValueError("Specify window_size at least 4") + ixxx = FreqDist() + iiii = FreqDist() + ii = FreqDist() + iii = FreqDist() + ixi = FreqDist() + ixxi = FreqDist() + iixi = FreqDist() + ixii = FreqDist() + + for window in ngrams(words, window_size, pad_right=True): + w1 = window[0] + if w1 is None: + continue + for w2, w3, w4 in _itertools.combinations(window[1:], 3): + ixxx[w1] += 1 + if w2 is None: + continue + ii[(w1, w2)] += 1 + if w3 is None: + continue + iii[(w1, w2, w3)] += 1 + ixi[(w1, w3)] += 1 + if w4 is None: + continue + iiii[(w1, w2, w3, w4)] += 1 + ixxi[(w1, w4)] += 1 + ixii[(w1, w3, w4)] += 1 + iixi[(w1, w2, w4)] += 1 + + return cls(ixxx, iiii, ii, iii, ixi, ixxi, iixi, ixii) + + def score_ngram(self, score_fn, w1, w2, w3, w4): + n_all = self.N + n_iiii = self.ngram_fd[(w1, w2, w3, w4)] + if not n_iiii: + return + n_iiix = self.iii[(w1, w2, w3)] + n_xiii = self.iii[(w2, w3, w4)] + n_iixi = self.iixi[(w1, w2, w4)] + n_ixii = self.ixii[(w1, w3, w4)] + + n_iixx = self.ii[(w1, w2)] + n_xxii = self.ii[(w3, w4)] + n_xiix = self.ii[(w2, w3)] + n_ixix = self.ixi[(w1, w3)] + n_ixxi = self.ixxi[(w1, w4)] + n_xixi = self.ixi[(w2, w4)] + + n_ixxx = self.word_fd[w1] + n_xixx = self.word_fd[w2] + n_xxix = self.word_fd[w3] + n_xxxi = self.word_fd[w4] + return score_fn( + n_iiii, + (n_iiix, n_iixi, n_ixii, n_xiii), + (n_iixx, n_ixix, n_ixxi, n_xixi, n_xxii, n_xiix), + (n_ixxx, n_xixx, n_xxix, n_xxxi), + n_all, + ) + + +def demo(scorer=None, compare_scorer=None): + """Finds bigram collocations in the files of the WebText corpus.""" + from nltk.metrics import ( + BigramAssocMeasures, + ranks_from_scores, + spearman_correlation, + ) + + if scorer is None: + scorer = BigramAssocMeasures.likelihood_ratio + if compare_scorer is None: + compare_scorer = BigramAssocMeasures.raw_freq + + from nltk.corpus import stopwords, webtext + + ignored_words = stopwords.words("english") + word_filter = lambda w: len(w) < 3 or w.lower() in ignored_words + + for file in webtext.fileids(): + words = [word.lower() for word in webtext.words(file)] + + cf = BigramCollocationFinder.from_words(words) + cf.apply_freq_filter(3) + cf.apply_word_filter(word_filter) + + corr = spearman_correlation( + ranks_from_scores(cf.score_ngrams(scorer)), + ranks_from_scores(cf.score_ngrams(compare_scorer)), + ) + print(file) + print("\t", [" ".join(tup) for tup in cf.nbest(scorer, 15)]) + print(f"\t Correlation to {compare_scorer.__name__}: {corr:0.4f}") + + +# Slows down loading too much +# bigram_measures = BigramAssocMeasures() +# trigram_measures = TrigramAssocMeasures() + +if __name__ == "__main__": + import sys + + from nltk.metrics import BigramAssocMeasures + + try: + scorer = eval("BigramAssocMeasures." + sys.argv[1]) + except IndexError: + scorer = None + try: + compare_scorer = eval("BigramAssocMeasures." + sys.argv[2]) + except IndexError: + compare_scorer = None + + demo(scorer, compare_scorer) + +__all__ = [ + "BigramCollocationFinder", + "TrigramCollocationFinder", + "QuadgramCollocationFinder", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/compat.py b/.pythonlibs/lib/python3.10/site-packages/nltk/compat.py new file mode 100644 index 0000000..61ab94c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/compat.py @@ -0,0 +1,43 @@ +# Natural Language Toolkit: Compatibility +# +# Copyright (C) 2001-2023 NLTK Project +# +# URL: +# For license information, see LICENSE.TXT + +import os +from functools import wraps + +# ======= Compatibility for datasets that care about Python versions ======== + +# The following datasets have a /PY3 subdirectory containing +# a full copy of the data which has been re-encoded or repickled. +DATA_UPDATES = [ + ("chunkers", "maxent_ne_chunker"), + ("help", "tagsets"), + ("taggers", "maxent_treebank_pos_tagger"), + ("tokenizers", "punkt"), +] + +_PY3_DATA_UPDATES = [os.path.join(*path_list) for path_list in DATA_UPDATES] + + +def add_py3_data(path): + for item in _PY3_DATA_UPDATES: + if item in str(path) and "/PY3" not in str(path): + pos = path.index(item) + len(item) + if path[pos : pos + 4] == ".zip": + pos += 4 + path = path[:pos] + "/PY3" + path[pos:] + break + return path + + +# for use in adding /PY3 to the second (filename) argument +# of the file pointers in data.py +def py3_data(init_func): + def _decorator(*args, **kwargs): + args = (args[0], add_py3_data(args[1])) + args[2:] + return init_func(*args, **kwargs) + + return wraps(init_func)(_decorator) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__init__.py new file mode 100644 index 0000000..115a9ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__init__.py @@ -0,0 +1,529 @@ +# Natural Language Toolkit: Corpus Readers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +# TODO this docstring isn't up-to-date! +""" +NLTK corpus readers. The modules in this package provide functions +that can be used to read corpus files in a variety of formats. These +functions can be used to read both the corpus files that are +distributed in the NLTK corpus package, and corpus files that are part +of external corpora. + +Available Corpora +================= + +Please see https://www.nltk.org/nltk_data/ for a complete list. +Install corpora using nltk.download(). + +Corpus Reader Functions +======================= +Each corpus module defines one or more "corpus reader functions", +which can be used to read documents from that corpus. These functions +take an argument, ``item``, which is used to indicate which document +should be read from the corpus: + +- If ``item`` is one of the unique identifiers listed in the corpus + module's ``items`` variable, then the corresponding document will + be loaded from the NLTK corpus package. +- If ``item`` is a filename, then that file will be read. + +Additionally, corpus reader functions can be given lists of item +names; in which case, they will return a concatenation of the +corresponding documents. + +Corpus reader functions are named based on the type of information +they return. Some common examples, and their return types, are: + +- words(): list of str +- sents(): list of (list of str) +- paras(): list of (list of (list of str)) +- tagged_words(): list of (str,str) tuple +- tagged_sents(): list of (list of (str,str)) +- tagged_paras(): list of (list of (list of (str,str))) +- chunked_sents(): list of (Tree w/ (str,str) leaves) +- parsed_sents(): list of (Tree with str leaves) +- parsed_paras(): list of (list of (Tree with str leaves)) +- xml(): A single xml ElementTree +- raw(): unprocessed corpus contents + +For example, to read a list of the words in the Brown Corpus, use +``nltk.corpus.brown.words()``: + + >>> from nltk.corpus import brown + >>> print(", ".join(brown.words())) # doctest: +ELLIPSIS + The, Fulton, County, Grand, Jury, said, ... + +""" + +import re + +from nltk.corpus.reader import * +from nltk.corpus.util import LazyCorpusLoader +from nltk.tokenize import RegexpTokenizer + +abc: PlaintextCorpusReader = LazyCorpusLoader( + "abc", + PlaintextCorpusReader, + r"(?!\.).*\.txt", + encoding=[("science", "latin_1"), ("rural", "utf8")], +) +alpino: AlpinoCorpusReader = LazyCorpusLoader( + "alpino", AlpinoCorpusReader, tagset="alpino" +) +bcp47: BCP47CorpusReader = LazyCorpusLoader( + "bcp47", BCP47CorpusReader, r"(cldr|iana)/*" +) +brown: CategorizedTaggedCorpusReader = LazyCorpusLoader( + "brown", + CategorizedTaggedCorpusReader, + r"c[a-z]\d\d", + cat_file="cats.txt", + tagset="brown", + encoding="ascii", +) +cess_cat: BracketParseCorpusReader = LazyCorpusLoader( + "cess_cat", + BracketParseCorpusReader, + r"(?!\.).*\.tbf", + tagset="unknown", + encoding="ISO-8859-15", +) +cess_esp: BracketParseCorpusReader = LazyCorpusLoader( + "cess_esp", + BracketParseCorpusReader, + r"(?!\.).*\.tbf", + tagset="unknown", + encoding="ISO-8859-15", +) +cmudict: CMUDictCorpusReader = LazyCorpusLoader( + "cmudict", CMUDictCorpusReader, ["cmudict"] +) +comtrans: AlignedCorpusReader = LazyCorpusLoader( + "comtrans", AlignedCorpusReader, r"(?!\.).*\.txt" +) +comparative_sentences: ComparativeSentencesCorpusReader = LazyCorpusLoader( + "comparative_sentences", + ComparativeSentencesCorpusReader, + r"labeledSentences\.txt", + encoding="latin-1", +) +conll2000: ConllChunkCorpusReader = LazyCorpusLoader( + "conll2000", + ConllChunkCorpusReader, + ["train.txt", "test.txt"], + ("NP", "VP", "PP"), + tagset="wsj", + encoding="ascii", +) +conll2002: ConllChunkCorpusReader = LazyCorpusLoader( + "conll2002", + ConllChunkCorpusReader, + r".*\.(test|train).*", + ("LOC", "PER", "ORG", "MISC"), + encoding="utf-8", +) +conll2007: DependencyCorpusReader = LazyCorpusLoader( + "conll2007", + DependencyCorpusReader, + r".*\.(test|train).*", + encoding=[("eus", "ISO-8859-2"), ("esp", "utf8")], +) +crubadan: CrubadanCorpusReader = LazyCorpusLoader( + "crubadan", CrubadanCorpusReader, r".*\.txt" +) +dependency_treebank: DependencyCorpusReader = LazyCorpusLoader( + "dependency_treebank", DependencyCorpusReader, r".*\.dp", encoding="ascii" +) +extended_omw: CorpusReader = LazyCorpusLoader( + "extended_omw", CorpusReader, r".*/wn-[a-z\-]*\.tab", encoding="utf8" +) +floresta: BracketParseCorpusReader = LazyCorpusLoader( + "floresta", + BracketParseCorpusReader, + r"(?!\.).*\.ptb", + "#", + tagset="unknown", + encoding="ISO-8859-15", +) +framenet15: FramenetCorpusReader = LazyCorpusLoader( + "framenet_v15", + FramenetCorpusReader, + [ + "frRelation.xml", + "frameIndex.xml", + "fulltextIndex.xml", + "luIndex.xml", + "semTypes.xml", + ], +) +framenet: FramenetCorpusReader = LazyCorpusLoader( + "framenet_v17", + FramenetCorpusReader, + [ + "frRelation.xml", + "frameIndex.xml", + "fulltextIndex.xml", + "luIndex.xml", + "semTypes.xml", + ], +) +gazetteers: WordListCorpusReader = LazyCorpusLoader( + "gazetteers", WordListCorpusReader, r"(?!LICENSE|\.).*\.txt", encoding="ISO-8859-2" +) +genesis: PlaintextCorpusReader = LazyCorpusLoader( + "genesis", + PlaintextCorpusReader, + r"(?!\.).*\.txt", + encoding=[ + ("finnish|french|german", "latin_1"), + ("swedish", "cp865"), + (".*", "utf_8"), + ], +) +gutenberg: PlaintextCorpusReader = LazyCorpusLoader( + "gutenberg", PlaintextCorpusReader, r"(?!\.).*\.txt", encoding="latin1" +) +ieer: IEERCorpusReader = LazyCorpusLoader("ieer", IEERCorpusReader, r"(?!README|\.).*") +inaugural: PlaintextCorpusReader = LazyCorpusLoader( + "inaugural", PlaintextCorpusReader, r"(?!\.).*\.txt", encoding="latin1" +) +# [XX] This should probably just use TaggedCorpusReader: +indian: IndianCorpusReader = LazyCorpusLoader( + "indian", IndianCorpusReader, r"(?!\.).*\.pos", tagset="unknown", encoding="utf8" +) + +jeita: ChasenCorpusReader = LazyCorpusLoader( + "jeita", ChasenCorpusReader, r".*\.chasen", encoding="utf-8" +) +knbc: KNBCorpusReader = LazyCorpusLoader( + "knbc/corpus1", KNBCorpusReader, r".*/KN.*", encoding="euc-jp" +) +lin_thesaurus: LinThesaurusCorpusReader = LazyCorpusLoader( + "lin_thesaurus", LinThesaurusCorpusReader, r".*\.lsp" +) +mac_morpho: MacMorphoCorpusReader = LazyCorpusLoader( + "mac_morpho", + MacMorphoCorpusReader, + r"(?!\.).*\.txt", + tagset="unknown", + encoding="latin-1", +) +machado: PortugueseCategorizedPlaintextCorpusReader = LazyCorpusLoader( + "machado", + PortugueseCategorizedPlaintextCorpusReader, + r"(?!\.).*\.txt", + cat_pattern=r"([a-z]*)/.*", + encoding="latin-1", +) +masc_tagged: CategorizedTaggedCorpusReader = LazyCorpusLoader( + "masc_tagged", + CategorizedTaggedCorpusReader, + r"(spoken|written)/.*\.txt", + cat_file="categories.txt", + tagset="wsj", + encoding="utf-8", + sep="_", +) +movie_reviews: CategorizedPlaintextCorpusReader = LazyCorpusLoader( + "movie_reviews", + CategorizedPlaintextCorpusReader, + r"(?!\.).*\.txt", + cat_pattern=r"(neg|pos)/.*", + encoding="ascii", +) +multext_east: MTECorpusReader = LazyCorpusLoader( + "mte_teip5", MTECorpusReader, r"(oana).*\.xml", encoding="utf-8" +) +names: WordListCorpusReader = LazyCorpusLoader( + "names", WordListCorpusReader, r"(?!\.).*\.txt", encoding="ascii" +) +nps_chat: NPSChatCorpusReader = LazyCorpusLoader( + "nps_chat", NPSChatCorpusReader, r"(?!README|\.).*\.xml", tagset="wsj" +) +opinion_lexicon: OpinionLexiconCorpusReader = LazyCorpusLoader( + "opinion_lexicon", + OpinionLexiconCorpusReader, + r"(\w+)\-words\.txt", + encoding="ISO-8859-2", +) +ppattach: PPAttachmentCorpusReader = LazyCorpusLoader( + "ppattach", PPAttachmentCorpusReader, ["training", "test", "devset"] +) +product_reviews_1: ReviewsCorpusReader = LazyCorpusLoader( + "product_reviews_1", ReviewsCorpusReader, r"^(?!Readme).*\.txt", encoding="utf8" +) +product_reviews_2: ReviewsCorpusReader = LazyCorpusLoader( + "product_reviews_2", ReviewsCorpusReader, r"^(?!Readme).*\.txt", encoding="utf8" +) +pros_cons: ProsConsCorpusReader = LazyCorpusLoader( + "pros_cons", + ProsConsCorpusReader, + r"Integrated(Cons|Pros)\.txt", + cat_pattern=r"Integrated(Cons|Pros)\.txt", + encoding="ISO-8859-2", +) +ptb: CategorizedBracketParseCorpusReader = ( + LazyCorpusLoader( # Penn Treebank v3: WSJ and Brown portions + "ptb", + CategorizedBracketParseCorpusReader, + r"(WSJ/\d\d/WSJ_\d\d|BROWN/C[A-Z]/C[A-Z])\d\d.MRG", + cat_file="allcats.txt", + tagset="wsj", + ) +) +qc: StringCategoryCorpusReader = LazyCorpusLoader( + "qc", StringCategoryCorpusReader, ["train.txt", "test.txt"], encoding="ISO-8859-2" +) +reuters: CategorizedPlaintextCorpusReader = LazyCorpusLoader( + "reuters", + CategorizedPlaintextCorpusReader, + "(training|test).*", + cat_file="cats.txt", + encoding="ISO-8859-2", +) +rte: RTECorpusReader = LazyCorpusLoader("rte", RTECorpusReader, r"(?!\.).*\.xml") +senseval: SensevalCorpusReader = LazyCorpusLoader( + "senseval", SensevalCorpusReader, r"(?!\.).*\.pos" +) +sentence_polarity: CategorizedSentencesCorpusReader = LazyCorpusLoader( + "sentence_polarity", + CategorizedSentencesCorpusReader, + r"rt-polarity\.(neg|pos)", + cat_pattern=r"rt-polarity\.(neg|pos)", + encoding="utf-8", +) +sentiwordnet: SentiWordNetCorpusReader = LazyCorpusLoader( + "sentiwordnet", SentiWordNetCorpusReader, "SentiWordNet_3.0.0.txt", encoding="utf-8" +) +shakespeare: XMLCorpusReader = LazyCorpusLoader( + "shakespeare", XMLCorpusReader, r"(?!\.).*\.xml" +) +sinica_treebank: SinicaTreebankCorpusReader = LazyCorpusLoader( + "sinica_treebank", + SinicaTreebankCorpusReader, + ["parsed"], + tagset="unknown", + encoding="utf-8", +) +state_union: PlaintextCorpusReader = LazyCorpusLoader( + "state_union", PlaintextCorpusReader, r"(?!\.).*\.txt", encoding="ISO-8859-2" +) +stopwords: WordListCorpusReader = LazyCorpusLoader( + "stopwords", WordListCorpusReader, r"(?!README|\.).*", encoding="utf8" +) +subjectivity: CategorizedSentencesCorpusReader = LazyCorpusLoader( + "subjectivity", + CategorizedSentencesCorpusReader, + r"(quote.tok.gt9|plot.tok.gt9)\.5000", + cat_map={"quote.tok.gt9.5000": ["subj"], "plot.tok.gt9.5000": ["obj"]}, + encoding="latin-1", +) +swadesh: SwadeshCorpusReader = LazyCorpusLoader( + "swadesh", SwadeshCorpusReader, r"(?!README|\.).*", encoding="utf8" +) +swadesh110: PanlexSwadeshCorpusReader = LazyCorpusLoader( + "panlex_swadesh", PanlexSwadeshCorpusReader, r"swadesh110/.*\.txt", encoding="utf8" +) +swadesh207: PanlexSwadeshCorpusReader = LazyCorpusLoader( + "panlex_swadesh", PanlexSwadeshCorpusReader, r"swadesh207/.*\.txt", encoding="utf8" +) +switchboard: SwitchboardCorpusReader = LazyCorpusLoader( + "switchboard", SwitchboardCorpusReader, tagset="wsj" +) +timit: TimitCorpusReader = LazyCorpusLoader("timit", TimitCorpusReader) +timit_tagged: TimitTaggedCorpusReader = LazyCorpusLoader( + "timit", TimitTaggedCorpusReader, r".+\.tags", tagset="wsj", encoding="ascii" +) +toolbox: ToolboxCorpusReader = LazyCorpusLoader( + "toolbox", ToolboxCorpusReader, r"(?!.*(README|\.)).*\.(dic|txt)" +) +treebank: BracketParseCorpusReader = LazyCorpusLoader( + "treebank/combined", + BracketParseCorpusReader, + r"wsj_.*\.mrg", + tagset="wsj", + encoding="ascii", +) +treebank_chunk: ChunkedCorpusReader = LazyCorpusLoader( + "treebank/tagged", + ChunkedCorpusReader, + r"wsj_.*\.pos", + sent_tokenizer=RegexpTokenizer(r"(?<=/\.)\s*(?![^\[]*\])", gaps=True), + para_block_reader=tagged_treebank_para_block_reader, + tagset="wsj", + encoding="ascii", +) +treebank_raw: PlaintextCorpusReader = LazyCorpusLoader( + "treebank/raw", PlaintextCorpusReader, r"wsj_.*", encoding="ISO-8859-2" +) +twitter_samples: TwitterCorpusReader = LazyCorpusLoader( + "twitter_samples", TwitterCorpusReader, r".*\.json" +) +udhr: UdhrCorpusReader = LazyCorpusLoader("udhr", UdhrCorpusReader) +udhr2: PlaintextCorpusReader = LazyCorpusLoader( + "udhr2", PlaintextCorpusReader, r".*\.txt", encoding="utf8" +) +universal_treebanks: ConllCorpusReader = LazyCorpusLoader( + "universal_treebanks_v20", + ConllCorpusReader, + r".*\.conll", + columntypes=( + "ignore", + "words", + "ignore", + "ignore", + "pos", + "ignore", + "ignore", + "ignore", + "ignore", + "ignore", + ), +) +verbnet: VerbnetCorpusReader = LazyCorpusLoader( + "verbnet", VerbnetCorpusReader, r"(?!\.).*\.xml" +) +webtext: PlaintextCorpusReader = LazyCorpusLoader( + "webtext", PlaintextCorpusReader, r"(?!README|\.).*\.txt", encoding="ISO-8859-2" +) +wordnet: WordNetCorpusReader = LazyCorpusLoader( + "wordnet", + WordNetCorpusReader, + LazyCorpusLoader("omw-1.4", CorpusReader, r".*/wn-data-.*\.tab", encoding="utf8"), +) +wordnet31: WordNetCorpusReader = LazyCorpusLoader( + "wordnet31", + WordNetCorpusReader, + LazyCorpusLoader("omw-1.4", CorpusReader, r".*/wn-data-.*\.tab", encoding="utf8"), +) +wordnet2021: WordNetCorpusReader = LazyCorpusLoader( + "wordnet2021", + WordNetCorpusReader, + LazyCorpusLoader("omw-1.4", CorpusReader, r".*/wn-data-.*\.tab", encoding="utf8"), +) +wordnet_ic: WordNetICCorpusReader = LazyCorpusLoader( + "wordnet_ic", WordNetICCorpusReader, r".*\.dat" +) +words: WordListCorpusReader = LazyCorpusLoader( + "words", WordListCorpusReader, r"(?!README|\.).*", encoding="ascii" +) + +# defined after treebank +propbank: PropbankCorpusReader = LazyCorpusLoader( + "propbank", + PropbankCorpusReader, + "prop.txt", + r"frames/.*\.xml", + "verbs.txt", + lambda filename: re.sub(r"^wsj/\d\d/", "", filename), + treebank, +) # Must be defined *after* treebank corpus. +nombank: NombankCorpusReader = LazyCorpusLoader( + "nombank.1.0", + NombankCorpusReader, + "nombank.1.0", + r"frames/.*\.xml", + "nombank.1.0.words", + lambda filename: re.sub(r"^wsj/\d\d/", "", filename), + treebank, +) # Must be defined *after* treebank corpus. +propbank_ptb: PropbankCorpusReader = LazyCorpusLoader( + "propbank", + PropbankCorpusReader, + "prop.txt", + r"frames/.*\.xml", + "verbs.txt", + lambda filename: filename.upper(), + ptb, +) # Must be defined *after* ptb corpus. +nombank_ptb: NombankCorpusReader = LazyCorpusLoader( + "nombank.1.0", + NombankCorpusReader, + "nombank.1.0", + r"frames/.*\.xml", + "nombank.1.0.words", + lambda filename: filename.upper(), + ptb, +) # Must be defined *after* ptb corpus. +semcor: SemcorCorpusReader = LazyCorpusLoader( + "semcor", SemcorCorpusReader, r"brown./tagfiles/br-.*\.xml", wordnet +) # Must be defined *after* wordnet corpus. + +nonbreaking_prefixes: NonbreakingPrefixesCorpusReader = LazyCorpusLoader( + "nonbreaking_prefixes", + NonbreakingPrefixesCorpusReader, + r"(?!README|\.).*", + encoding="utf8", +) +perluniprops: UnicharsCorpusReader = LazyCorpusLoader( + "perluniprops", + UnicharsCorpusReader, + r"(?!README|\.).*", + nltk_data_subdir="misc", + encoding="utf8", +) + +# mwa_ppdb = LazyCorpusLoader( +# 'mwa_ppdb', MWAPPDBCorpusReader, r'(?!README|\.).*', nltk_data_subdir='misc', encoding='utf8') + +# See https://github.com/nltk/nltk/issues/1579 +# and https://github.com/nltk/nltk/issues/1716 +# +# pl196x = LazyCorpusLoader( +# 'pl196x', Pl196xCorpusReader, r'[a-z]-.*\.xml', +# cat_file='cats.txt', textid_file='textids.txt', encoding='utf8') +# +# ipipan = LazyCorpusLoader( +# 'ipipan', IPIPANCorpusReader, r'(?!\.).*morph\.xml') +# +# nkjp = LazyCorpusLoader( +# 'nkjp', NKJPCorpusReader, r'', encoding='utf8') +# +# panlex_lite = LazyCorpusLoader( +# 'panlex_lite', PanLexLiteCorpusReader) +# +# ycoe = LazyCorpusLoader( +# 'ycoe', YCOECorpusReader) +# +# corpus not available with NLTK; these lines caused help(nltk.corpus) to break +# hebrew_treebank = LazyCorpusLoader( +# 'hebrew_treebank', BracketParseCorpusReader, r'.*\.txt') + +# FIXME: override any imported demo from various corpora, see https://github.com/nltk/nltk/issues/2116 +def demo(): + # This is out-of-date: + abc.demo() + brown.demo() + # chat80.demo() + cmudict.demo() + conll2000.demo() + conll2002.demo() + genesis.demo() + gutenberg.demo() + ieer.demo() + inaugural.demo() + indian.demo() + names.demo() + ppattach.demo() + senseval.demo() + shakespeare.demo() + sinica_treebank.demo() + state_union.demo() + stopwords.demo() + timit.demo() + toolbox.demo() + treebank.demo() + udhr.demo() + webtext.demo() + words.demo() + + +# ycoe.demo() + +if __name__ == "__main__": + # demo() + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..34bc04d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/europarl_raw.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/europarl_raw.cpython-310.pyc new file mode 100644 index 0000000..25b428b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/europarl_raw.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..9518da6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/europarl_raw.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/europarl_raw.py new file mode 100644 index 0000000..7173f9d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/europarl_raw.py @@ -0,0 +1,56 @@ +# Natural Language Toolkit: Europarl Corpus Readers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Nitin Madnani +# URL: +# For license information, see LICENSE.TXT + +import re + +from nltk.corpus.reader import * +from nltk.corpus.util import LazyCorpusLoader + +# Create a new corpus reader instance for each European language +danish: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/danish", EuroparlCorpusReader, r"ep-.*\.da", encoding="utf-8" +) + +dutch: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/dutch", EuroparlCorpusReader, r"ep-.*\.nl", encoding="utf-8" +) + +english: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/english", EuroparlCorpusReader, r"ep-.*\.en", encoding="utf-8" +) + +finnish: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/finnish", EuroparlCorpusReader, r"ep-.*\.fi", encoding="utf-8" +) + +french: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/french", EuroparlCorpusReader, r"ep-.*\.fr", encoding="utf-8" +) + +german: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/german", EuroparlCorpusReader, r"ep-.*\.de", encoding="utf-8" +) + +greek: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/greek", EuroparlCorpusReader, r"ep-.*\.el", encoding="utf-8" +) + +italian: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/italian", EuroparlCorpusReader, r"ep-.*\.it", encoding="utf-8" +) + +portuguese: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/portuguese", EuroparlCorpusReader, r"ep-.*\.pt", encoding="utf-8" +) + +spanish: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/spanish", EuroparlCorpusReader, r"ep-.*\.es", encoding="utf-8" +) + +swedish: EuroparlCorpusReader = LazyCorpusLoader( + "europarl_raw/swedish", EuroparlCorpusReader, r"ep-.*\.sv", encoding="utf-8" +) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__init__.py new file mode 100644 index 0000000..081b7fa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__init__.py @@ -0,0 +1,186 @@ +# Natural Language Toolkit: Corpus Readers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK corpus readers. The modules in this package provide functions +that can be used to read corpus fileids in a variety of formats. These +functions can be used to read both the corpus fileids that are +distributed in the NLTK corpus package, and corpus fileids that are part +of external corpora. + +Corpus Reader Functions +======================= +Each corpus module defines one or more "corpus reader functions", +which can be used to read documents from that corpus. These functions +take an argument, ``item``, which is used to indicate which document +should be read from the corpus: + +- If ``item`` is one of the unique identifiers listed in the corpus + module's ``items`` variable, then the corresponding document will + be loaded from the NLTK corpus package. +- If ``item`` is a fileid, then that file will be read. + +Additionally, corpus reader functions can be given lists of item +names; in which case, they will return a concatenation of the +corresponding documents. + +Corpus reader functions are named based on the type of information +they return. Some common examples, and their return types, are: + +- words(): list of str +- sents(): list of (list of str) +- paras(): list of (list of (list of str)) +- tagged_words(): list of (str,str) tuple +- tagged_sents(): list of (list of (str,str)) +- tagged_paras(): list of (list of (list of (str,str))) +- chunked_sents(): list of (Tree w/ (str,str) leaves) +- parsed_sents(): list of (Tree with str leaves) +- parsed_paras(): list of (list of (Tree with str leaves)) +- xml(): A single xml ElementTree +- raw(): unprocessed corpus contents + +For example, to read a list of the words in the Brown Corpus, use +``nltk.corpus.brown.words()``: + + >>> from nltk.corpus import brown + >>> print(", ".join(brown.words()[:6])) # only first 6 words + The, Fulton, County, Grand, Jury, said + +isort:skip_file +""" + +from nltk.corpus.reader.plaintext import * +from nltk.corpus.reader.util import * +from nltk.corpus.reader.api import * +from nltk.corpus.reader.tagged import * +from nltk.corpus.reader.cmudict import * +from nltk.corpus.reader.conll import * +from nltk.corpus.reader.chunked import * +from nltk.corpus.reader.wordlist import * +from nltk.corpus.reader.xmldocs import * +from nltk.corpus.reader.ppattach import * +from nltk.corpus.reader.senseval import * +from nltk.corpus.reader.ieer import * +from nltk.corpus.reader.sinica_treebank import * +from nltk.corpus.reader.bracket_parse import * +from nltk.corpus.reader.indian import * +from nltk.corpus.reader.toolbox import * +from nltk.corpus.reader.timit import * +from nltk.corpus.reader.ycoe import * +from nltk.corpus.reader.rte import * +from nltk.corpus.reader.string_category import * +from nltk.corpus.reader.propbank import * +from nltk.corpus.reader.verbnet import * +from nltk.corpus.reader.bnc import * +from nltk.corpus.reader.nps_chat import * +from nltk.corpus.reader.wordnet import * +from nltk.corpus.reader.switchboard import * +from nltk.corpus.reader.dependency import * +from nltk.corpus.reader.nombank import * +from nltk.corpus.reader.ipipan import * +from nltk.corpus.reader.pl196x import * +from nltk.corpus.reader.knbc import * +from nltk.corpus.reader.chasen import * +from nltk.corpus.reader.childes import * +from nltk.corpus.reader.aligned import * +from nltk.corpus.reader.lin import * +from nltk.corpus.reader.semcor import * +from nltk.corpus.reader.framenet import * +from nltk.corpus.reader.udhr import * +from nltk.corpus.reader.bnc import * +from nltk.corpus.reader.sentiwordnet import * +from nltk.corpus.reader.twitter import * +from nltk.corpus.reader.nkjp import * +from nltk.corpus.reader.crubadan import * +from nltk.corpus.reader.mte import * +from nltk.corpus.reader.reviews import * +from nltk.corpus.reader.opinion_lexicon import * +from nltk.corpus.reader.pros_cons import * +from nltk.corpus.reader.categorized_sents import * +from nltk.corpus.reader.comparative_sents import * +from nltk.corpus.reader.panlex_lite import * +from nltk.corpus.reader.panlex_swadesh import * +from nltk.corpus.reader.bcp47 import * + +# Make sure that nltk.corpus.reader.bracket_parse gives the module, not +# the function bracket_parse() defined in nltk.tree: +from nltk.corpus.reader import bracket_parse + +__all__ = [ + "CorpusReader", + "CategorizedCorpusReader", + "PlaintextCorpusReader", + "find_corpus_fileids", + "TaggedCorpusReader", + "CMUDictCorpusReader", + "ConllChunkCorpusReader", + "WordListCorpusReader", + "PPAttachmentCorpusReader", + "SensevalCorpusReader", + "IEERCorpusReader", + "ChunkedCorpusReader", + "SinicaTreebankCorpusReader", + "BracketParseCorpusReader", + "IndianCorpusReader", + "ToolboxCorpusReader", + "TimitCorpusReader", + "YCOECorpusReader", + "MacMorphoCorpusReader", + "SyntaxCorpusReader", + "AlpinoCorpusReader", + "RTECorpusReader", + "StringCategoryCorpusReader", + "EuroparlCorpusReader", + "CategorizedBracketParseCorpusReader", + "CategorizedTaggedCorpusReader", + "CategorizedPlaintextCorpusReader", + "PortugueseCategorizedPlaintextCorpusReader", + "tagged_treebank_para_block_reader", + "PropbankCorpusReader", + "VerbnetCorpusReader", + "BNCCorpusReader", + "ConllCorpusReader", + "XMLCorpusReader", + "NPSChatCorpusReader", + "SwadeshCorpusReader", + "WordNetCorpusReader", + "WordNetICCorpusReader", + "SwitchboardCorpusReader", + "DependencyCorpusReader", + "NombankCorpusReader", + "IPIPANCorpusReader", + "Pl196xCorpusReader", + "TEICorpusView", + "KNBCorpusReader", + "ChasenCorpusReader", + "CHILDESCorpusReader", + "AlignedCorpusReader", + "TimitTaggedCorpusReader", + "LinThesaurusCorpusReader", + "SemcorCorpusReader", + "FramenetCorpusReader", + "UdhrCorpusReader", + "BNCCorpusReader", + "SentiWordNetCorpusReader", + "SentiSynset", + "TwitterCorpusReader", + "NKJPCorpusReader", + "CrubadanCorpusReader", + "MTECorpusReader", + "ReviewsCorpusReader", + "OpinionLexiconCorpusReader", + "ProsConsCorpusReader", + "CategorizedSentencesCorpusReader", + "ComparativeSentencesCorpusReader", + "PanLexLiteCorpusReader", + "NonbreakingPrefixesCorpusReader", + "UnicharsCorpusReader", + "MWAPPDBCorpusReader", + "PanlexSwadeshCorpusReader", + "BCP47CorpusReader", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2909d25 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/aligned.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/aligned.cpython-310.pyc new file mode 100644 index 0000000..62ec253 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/aligned.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..0da1f03 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bcp47.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bcp47.cpython-310.pyc new file mode 100644 index 0000000..b598bb4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bcp47.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bnc.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bnc.cpython-310.pyc new file mode 100644 index 0000000..0cd5cd6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bnc.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bracket_parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bracket_parse.cpython-310.pyc new file mode 100644 index 0000000..828a065 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/bracket_parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/categorized_sents.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/categorized_sents.cpython-310.pyc new file mode 100644 index 0000000..1be0414 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/categorized_sents.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chasen.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chasen.cpython-310.pyc new file mode 100644 index 0000000..61c03a1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chasen.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/childes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/childes.cpython-310.pyc new file mode 100644 index 0000000..c86fbf5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/childes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chunked.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chunked.cpython-310.pyc new file mode 100644 index 0000000..83f8b5a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/chunked.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/cmudict.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/cmudict.cpython-310.pyc new file mode 100644 index 0000000..2b5ba94 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/cmudict.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/comparative_sents.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/comparative_sents.cpython-310.pyc new file mode 100644 index 0000000..a449c54 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/comparative_sents.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/conll.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/conll.cpython-310.pyc new file mode 100644 index 0000000..36962d7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/conll.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/crubadan.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/crubadan.cpython-310.pyc new file mode 100644 index 0000000..62bbb9d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/crubadan.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/dependency.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/dependency.cpython-310.pyc new file mode 100644 index 0000000..0336d84 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/dependency.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/framenet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/framenet.cpython-310.pyc new file mode 100644 index 0000000..a9a3463 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/framenet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ieer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ieer.cpython-310.pyc new file mode 100644 index 0000000..0d7322c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ieer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/indian.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/indian.cpython-310.pyc new file mode 100644 index 0000000..28409cf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/indian.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ipipan.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ipipan.cpython-310.pyc new file mode 100644 index 0000000..6f6d8dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ipipan.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/knbc.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/knbc.cpython-310.pyc new file mode 100644 index 0000000..9daafa1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/knbc.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/lin.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/lin.cpython-310.pyc new file mode 100644 index 0000000..8b96931 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/lin.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/markdown.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/markdown.cpython-310.pyc new file mode 100644 index 0000000..6b5706b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/markdown.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/mte.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/mte.cpython-310.pyc new file mode 100644 index 0000000..bb39f00 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/mte.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nkjp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nkjp.cpython-310.pyc new file mode 100644 index 0000000..bbee052 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nkjp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nombank.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nombank.cpython-310.pyc new file mode 100644 index 0000000..12407e4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nombank.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nps_chat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nps_chat.cpython-310.pyc new file mode 100644 index 0000000..3711fb3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/nps_chat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/opinion_lexicon.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/opinion_lexicon.cpython-310.pyc new file mode 100644 index 0000000..c72424e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/opinion_lexicon.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_lite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_lite.cpython-310.pyc new file mode 100644 index 0000000..15e5681 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_lite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_swadesh.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_swadesh.cpython-310.pyc new file mode 100644 index 0000000..01813f7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/panlex_swadesh.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pl196x.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pl196x.cpython-310.pyc new file mode 100644 index 0000000..e1f16b3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pl196x.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/plaintext.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/plaintext.cpython-310.pyc new file mode 100644 index 0000000..3626703 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/plaintext.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ppattach.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ppattach.cpython-310.pyc new file mode 100644 index 0000000..ac86a41 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ppattach.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/propbank.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/propbank.cpython-310.pyc new file mode 100644 index 0000000..43817f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/propbank.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pros_cons.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pros_cons.cpython-310.pyc new file mode 100644 index 0000000..091023e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/pros_cons.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/reviews.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/reviews.cpython-310.pyc new file mode 100644 index 0000000..694f602 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/reviews.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/rte.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/rte.cpython-310.pyc new file mode 100644 index 0000000..7303ff9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/rte.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/semcor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/semcor.cpython-310.pyc new file mode 100644 index 0000000..cd24430 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/semcor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/senseval.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/senseval.cpython-310.pyc new file mode 100644 index 0000000..58cf756 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/senseval.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sentiwordnet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sentiwordnet.cpython-310.pyc new file mode 100644 index 0000000..20f27c2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sentiwordnet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sinica_treebank.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sinica_treebank.cpython-310.pyc new file mode 100644 index 0000000..d5651c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/sinica_treebank.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/string_category.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/string_category.cpython-310.pyc new file mode 100644 index 0000000..d66cf54 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/string_category.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/switchboard.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/switchboard.cpython-310.pyc new file mode 100644 index 0000000..be1742a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/switchboard.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/tagged.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/tagged.cpython-310.pyc new file mode 100644 index 0000000..5db0052 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/tagged.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/timit.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/timit.cpython-310.pyc new file mode 100644 index 0000000..98f6247 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/timit.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/toolbox.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/toolbox.cpython-310.pyc new file mode 100644 index 0000000..ec79d12 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/toolbox.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/twitter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/twitter.cpython-310.pyc new file mode 100644 index 0000000..53357d7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/twitter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/udhr.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/udhr.cpython-310.pyc new file mode 100644 index 0000000..1a613e9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/udhr.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..a033e7b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/verbnet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/verbnet.cpython-310.pyc new file mode 100644 index 0000000..38a629a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/verbnet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordlist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordlist.cpython-310.pyc new file mode 100644 index 0000000..6811ae3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordlist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordnet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordnet.cpython-310.pyc new file mode 100644 index 0000000..d00d88b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/wordnet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/xmldocs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/xmldocs.cpython-310.pyc new file mode 100644 index 0000000..fbaa09e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/xmldocs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ycoe.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ycoe.cpython-310.pyc new file mode 100644 index 0000000..93be559 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/__pycache__/ycoe.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/aligned.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/aligned.py new file mode 100644 index 0000000..1355282 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/aligned.py @@ -0,0 +1,154 @@ +# Natural Language Toolkit: Aligned Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# Author: Steven Bird +# For license information, see LICENSE.TXT + +from nltk.corpus.reader.api import CorpusReader +from nltk.corpus.reader.util import ( + StreamBackedCorpusView, + concat, + read_alignedsent_block, +) +from nltk.tokenize import RegexpTokenizer, WhitespaceTokenizer +from nltk.translate import AlignedSent, Alignment + + +class AlignedCorpusReader(CorpusReader): + """ + Reader for corpora of word-aligned sentences. Tokens are assumed + to be separated by whitespace. Sentences begin on separate lines. + """ + + def __init__( + self, + root, + fileids, + sep="/", + word_tokenizer=WhitespaceTokenizer(), + sent_tokenizer=RegexpTokenizer("\n", gaps=True), + alignedsent_block_reader=read_alignedsent_block, + encoding="latin1", + ): + """ + Construct a new Aligned Corpus reader for a set of documents + located at the given root directory. Example usage: + + >>> root = '/...path to corpus.../' + >>> reader = AlignedCorpusReader(root, '.*', '.txt') # doctest: +SKIP + + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + """ + CorpusReader.__init__(self, root, fileids, encoding) + self._sep = sep + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._alignedsent_block_reader = alignedsent_block_reader + + def words(self, fileids=None): + """ + :return: the given file(s) as a list of words + and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + AlignedSentCorpusView( + fileid, + enc, + False, + False, + self._word_tokenizer, + self._sent_tokenizer, + self._alignedsent_block_reader, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + """ + :return: the given file(s) as a list of + sentences or utterances, each encoded as a list of word + strings. + :rtype: list(list(str)) + """ + return concat( + [ + AlignedSentCorpusView( + fileid, + enc, + False, + True, + self._word_tokenizer, + self._sent_tokenizer, + self._alignedsent_block_reader, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def aligned_sents(self, fileids=None): + """ + :return: the given file(s) as a list of AlignedSent objects. + :rtype: list(AlignedSent) + """ + return concat( + [ + AlignedSentCorpusView( + fileid, + enc, + True, + True, + self._word_tokenizer, + self._sent_tokenizer, + self._alignedsent_block_reader, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + +class AlignedSentCorpusView(StreamBackedCorpusView): + """ + A specialized corpus view for aligned sentences. + ``AlignedSentCorpusView`` objects are typically created by + ``AlignedCorpusReader`` (not directly by nltk users). + """ + + def __init__( + self, + corpus_file, + encoding, + aligned, + group_by_sent, + word_tokenizer, + sent_tokenizer, + alignedsent_block_reader, + ): + self._aligned = aligned + self._group_by_sent = group_by_sent + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._alignedsent_block_reader = alignedsent_block_reader + StreamBackedCorpusView.__init__(self, corpus_file, encoding=encoding) + + def read_block(self, stream): + block = [ + self._word_tokenizer.tokenize(sent_str) + for alignedsent_str in self._alignedsent_block_reader(stream) + for sent_str in self._sent_tokenizer.tokenize(alignedsent_str) + ] + if self._aligned: + block[2] = Alignment.fromstring( + " ".join(block[2]) + ) # kludge; we shouldn't have tokenized the alignment string + block = [AlignedSent(*block)] + elif self._group_by_sent: + block = [block[0]] + else: + block = block[0] + + return block diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/api.py new file mode 100644 index 0000000..9eed73d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/api.py @@ -0,0 +1,516 @@ +# Natural Language Toolkit: API for Corpus Readers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +API for corpus readers. +""" + +import os +import re +from collections import defaultdict +from itertools import chain + +from nltk.corpus.reader.util import * +from nltk.data import FileSystemPathPointer, PathPointer, ZipFilePathPointer + + +class CorpusReader: + """ + A base class for "corpus reader" classes, each of which can be + used to read a specific corpus format. Each individual corpus + reader instance is used to read a specific corpus, consisting of + one or more files under a common root directory. Each file is + identified by its ``file identifier``, which is the relative path + to the file from the root directory. + + A separate subclass is defined for each corpus format. These + subclasses define one or more methods that provide 'views' on the + corpus contents, such as ``words()`` (for a list of words) and + ``parsed_sents()`` (for a list of parsed sentences). Called with + no arguments, these methods will return the contents of the entire + corpus. For most corpora, these methods define one or more + selection arguments, such as ``fileids`` or ``categories``, which can + be used to select which portion of the corpus should be returned. + """ + + def __init__(self, root, fileids, encoding="utf8", tagset=None): + """ + :type root: PathPointer or str + :param root: A path pointer identifying the root directory for + this corpus. If a string is specified, then it will be + converted to a ``PathPointer`` automatically. + :param fileids: A list of the files that make up this corpus. + This list can either be specified explicitly, as a list of + strings; or implicitly, as a regular expression over file + paths. The absolute path for each file will be constructed + by joining the reader's root to each file name. + :param encoding: The default unicode encoding for the files + that make up the corpus. The value of ``encoding`` can be any + of the following: + + - A string: ``encoding`` is the encoding name for all files. + - A dictionary: ``encoding[file_id]`` is the encoding + name for the file whose identifier is ``file_id``. If + ``file_id`` is not in ``encoding``, then the file + contents will be processed using non-unicode byte strings. + - A list: ``encoding`` should be a list of ``(regexp, encoding)`` + tuples. The encoding for a file whose identifier is ``file_id`` + will be the ``encoding`` value for the first tuple whose + ``regexp`` matches the ``file_id``. If no tuple's ``regexp`` + matches the ``file_id``, the file contents will be processed + using non-unicode byte strings. + - None: the file contents of all files will be + processed using non-unicode byte strings. + :param tagset: The name of the tagset used by this corpus, to be used + for normalizing or converting the POS tags returned by the + ``tagged_...()`` methods. + """ + # Convert the root to a path pointer, if necessary. + if isinstance(root, str) and not isinstance(root, PathPointer): + m = re.match(r"(.*\.zip)/?(.*)$|", root) + zipfile, zipentry = m.groups() + if zipfile: + root = ZipFilePathPointer(zipfile, zipentry) + else: + root = FileSystemPathPointer(root) + elif not isinstance(root, PathPointer): + raise TypeError("CorpusReader: expected a string or a PathPointer") + + # If `fileids` is a regexp, then expand it. + if isinstance(fileids, str): + fileids = find_corpus_fileids(root, fileids) + + self._fileids = fileids + """A list of the relative paths for the fileids that make up + this corpus.""" + + self._root = root + """The root directory for this corpus.""" + + self._readme = "README" + self._license = "LICENSE" + self._citation = "citation.bib" + + # If encoding was specified as a list of regexps, then convert + # it to a dictionary. + if isinstance(encoding, list): + encoding_dict = {} + for fileid in self._fileids: + for x in encoding: + (regexp, enc) = x + if re.match(regexp, fileid): + encoding_dict[fileid] = enc + break + encoding = encoding_dict + + self._encoding = encoding + """The default unicode encoding for the fileids that make up + this corpus. If ``encoding`` is None, then the file + contents are processed using byte strings.""" + self._tagset = tagset + + def __repr__(self): + if isinstance(self._root, ZipFilePathPointer): + path = f"{self._root.zipfile.filename}/{self._root.entry}" + else: + path = "%s" % self._root.path + return f"<{self.__class__.__name__} in {path!r}>" + + def ensure_loaded(self): + """ + Load this corpus (if it has not already been loaded). This is + used by LazyCorpusLoader as a simple method that can be used to + make sure a corpus is loaded -- e.g., in case a user wants to + do help(some_corpus). + """ + pass # no need to actually do anything. + + def readme(self): + """ + Return the contents of the corpus README file, if it exists. + """ + with self.open(self._readme) as f: + return f.read() + + def license(self): + """ + Return the contents of the corpus LICENSE file, if it exists. + """ + with self.open(self._license) as f: + return f.read() + + def citation(self): + """ + Return the contents of the corpus citation.bib file, if it exists. + """ + with self.open(self._citation) as f: + return f.read() + + def fileids(self): + """ + Return a list of file identifiers for the fileids that make up + this corpus. + """ + return self._fileids + + def abspath(self, fileid): + """ + Return the absolute path for the given file. + + :type fileid: str + :param fileid: The file identifier for the file whose path + should be returned. + :rtype: PathPointer + """ + return self._root.join(fileid) + + def abspaths(self, fileids=None, include_encoding=False, include_fileid=False): + """ + Return a list of the absolute paths for all fileids in this corpus; + or for the given list of fileids, if specified. + + :type fileids: None or str or list + :param fileids: Specifies the set of fileids for which paths should + be returned. Can be None, for all fileids; a list of + file identifiers, for a specified set of fileids; or a single + file identifier, for a single file. Note that the return + value is always a list of paths, even if ``fileids`` is a + single file identifier. + + :param include_encoding: If true, then return a list of + ``(path_pointer, encoding)`` tuples. + + :rtype: list(PathPointer) + """ + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + paths = [self._root.join(f) for f in fileids] + + if include_encoding and include_fileid: + return list(zip(paths, [self.encoding(f) for f in fileids], fileids)) + elif include_fileid: + return list(zip(paths, fileids)) + elif include_encoding: + return list(zip(paths, [self.encoding(f) for f in fileids])) + else: + return paths + + def raw(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a single string. + :rtype: str + """ + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + contents = [] + for f in fileids: + with self.open(f) as fp: + contents.append(fp.read()) + return concat(contents) + + def open(self, file): + """ + Return an open stream that can be used to read the given file. + If the file's encoding is not None, then the stream will + automatically decode the file's contents into unicode. + + :param file: The file identifier of the file to read. + """ + encoding = self.encoding(file) + stream = self._root.join(file).open(encoding) + return stream + + def encoding(self, file): + """ + Return the unicode encoding for the given corpus file, if known. + If the encoding is unknown, or if the given file should be + processed using byte strings (str), then return None. + """ + if isinstance(self._encoding, dict): + return self._encoding.get(file) + else: + return self._encoding + + def _get_root(self): + return self._root + + root = property( + _get_root, + doc=""" + The directory where this corpus is stored. + + :type: PathPointer""", + ) + + +###################################################################### +# { Corpora containing categorized items +###################################################################### + + +class CategorizedCorpusReader: + """ + A mixin class used to aid in the implementation of corpus readers + for categorized corpora. This class defines the method + ``categories()``, which returns a list of the categories for the + corpus or for a specified set of fileids; and overrides ``fileids()`` + to take a ``categories`` argument, restricting the set of fileids to + be returned. + + Subclasses are expected to: + + - Call ``__init__()`` to set up the mapping. + + - Override all view methods to accept a ``categories`` parameter, + which can be used *instead* of the ``fileids`` parameter, to + select which fileids should be included in the returned view. + """ + + def __init__(self, kwargs): + """ + Initialize this mapping based on keyword arguments, as + follows: + + - cat_pattern: A regular expression pattern used to find the + category for each file identifier. The pattern will be + applied to each file identifier, and the first matching + group will be used as the category label for that file. + + - cat_map: A dictionary, mapping from file identifiers to + category labels. + + - cat_file: The name of a file that contains the mapping + from file identifiers to categories. The argument + ``cat_delimiter`` can be used to specify a delimiter. + + The corresponding argument will be deleted from ``kwargs``. If + more than one argument is specified, an exception will be + raised. + """ + self._f2c = None #: file-to-category mapping + self._c2f = None #: category-to-file mapping + + self._pattern = None #: regexp specifying the mapping + self._map = None #: dict specifying the mapping + self._file = None #: fileid of file containing the mapping + self._delimiter = None #: delimiter for ``self._file`` + + if "cat_pattern" in kwargs: + self._pattern = kwargs["cat_pattern"] + del kwargs["cat_pattern"] + elif "cat_map" in kwargs: + self._map = kwargs["cat_map"] + del kwargs["cat_map"] + elif "cat_file" in kwargs: + self._file = kwargs["cat_file"] + del kwargs["cat_file"] + if "cat_delimiter" in kwargs: + self._delimiter = kwargs["cat_delimiter"] + del kwargs["cat_delimiter"] + else: + raise ValueError( + "Expected keyword argument cat_pattern or " "cat_map or cat_file." + ) + + if "cat_pattern" in kwargs or "cat_map" in kwargs or "cat_file" in kwargs: + raise ValueError( + "Specify exactly one of: cat_pattern, " "cat_map, cat_file." + ) + + def _init(self): + self._f2c = defaultdict(set) + self._c2f = defaultdict(set) + + if self._pattern is not None: + for file_id in self._fileids: + category = re.match(self._pattern, file_id).group(1) + self._add(file_id, category) + + elif self._map is not None: + for (file_id, categories) in self._map.items(): + for category in categories: + self._add(file_id, category) + + elif self._file is not None: + with self.open(self._file) as f: + for line in f.readlines(): + line = line.strip() + file_id, categories = line.split(self._delimiter, 1) + if file_id not in self.fileids(): + raise ValueError( + "In category mapping file %s: %s " + "not found" % (self._file, file_id) + ) + for category in categories.split(self._delimiter): + self._add(file_id, category) + + def _add(self, file_id, category): + self._f2c[file_id].add(category) + self._c2f[category].add(file_id) + + def categories(self, fileids=None): + """ + Return a list of the categories that are defined for this corpus, + or for the file(s) if it is given. + """ + if self._f2c is None: + self._init() + if fileids is None: + return sorted(self._c2f) + if isinstance(fileids, str): + fileids = [fileids] + return sorted(set.union(*(self._f2c[d] for d in fileids))) + + def fileids(self, categories=None): + """ + Return a list of file identifiers for the files that make up + this corpus, or that make up the given category(s) if specified. + """ + if categories is None: + return super().fileids() + elif isinstance(categories, str): + if self._f2c is None: + self._init() + if categories in self._c2f: + return sorted(self._c2f[categories]) + else: + raise ValueError("Category %s not found" % categories) + else: + if self._f2c is None: + self._init() + return sorted(set.union(*(self._c2f[c] for c in categories))) + + def _resolve(self, fileids, categories): + if fileids is not None and categories is not None: + raise ValueError("Specify fileids or categories, not both") + if categories is not None: + return self.fileids(categories) + else: + return fileids + + def raw(self, fileids=None, categories=None): + return super().raw(self._resolve(fileids, categories)) + + def words(self, fileids=None, categories=None): + return super().words(self._resolve(fileids, categories)) + + def sents(self, fileids=None, categories=None): + return super().sents(self._resolve(fileids, categories)) + + def paras(self, fileids=None, categories=None): + return super().paras(self._resolve(fileids, categories)) + + +###################################################################### +# { Treebank readers +###################################################################### + +# [xx] is it worth it to factor this out? +class SyntaxCorpusReader(CorpusReader): + """ + An abstract base class for reading corpora consisting of + syntactically parsed text. Subclasses should define: + + - ``__init__``, which specifies the location of the corpus + and a method for detecting the sentence blocks in corpus files. + - ``_read_block``, which reads a block from the input stream. + - ``_word``, which takes a block and returns a list of list of words. + - ``_tag``, which takes a block and returns a list of list of tagged + words. + - ``_parse``, which takes a block and returns a list of parsed + sentences. + """ + + def _parse(self, s): + raise NotImplementedError() + + def _word(self, s): + raise NotImplementedError() + + def _tag(self, s): + raise NotImplementedError() + + def _read_block(self, stream): + raise NotImplementedError() + + def parsed_sents(self, fileids=None): + reader = self._read_parsed_sent_block + return concat( + [ + StreamBackedCorpusView(fileid, reader, encoding=enc) + for fileid, enc in self.abspaths(fileids, True) + ] + ) + + def tagged_sents(self, fileids=None, tagset=None): + def reader(stream): + return self._read_tagged_sent_block(stream, tagset) + + return concat( + [ + StreamBackedCorpusView(fileid, reader, encoding=enc) + for fileid, enc in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + reader = self._read_sent_block + return concat( + [ + StreamBackedCorpusView(fileid, reader, encoding=enc) + for fileid, enc in self.abspaths(fileids, True) + ] + ) + + def tagged_words(self, fileids=None, tagset=None): + def reader(stream): + return self._read_tagged_word_block(stream, tagset) + + return concat( + [ + StreamBackedCorpusView(fileid, reader, encoding=enc) + for fileid, enc in self.abspaths(fileids, True) + ] + ) + + def words(self, fileids=None): + return concat( + [ + StreamBackedCorpusView(fileid, self._read_word_block, encoding=enc) + for fileid, enc in self.abspaths(fileids, True) + ] + ) + + # ------------------------------------------------------------ + # { Block Readers + + def _read_word_block(self, stream): + return list(chain.from_iterable(self._read_sent_block(stream))) + + def _read_tagged_word_block(self, stream, tagset=None): + return list(chain.from_iterable(self._read_tagged_sent_block(stream, tagset))) + + def _read_sent_block(self, stream): + return list(filter(None, [self._word(t) for t in self._read_block(stream)])) + + def _read_tagged_sent_block(self, stream, tagset=None): + return list( + filter(None, [self._tag(t, tagset) for t in self._read_block(stream)]) + ) + + def _read_parsed_sent_block(self, stream): + return list(filter(None, [self._parse(t) for t in self._read_block(stream)])) + + # } End of Block Readers + # ------------------------------------------------------------ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bcp47.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bcp47.py new file mode 100644 index 0000000..7040f3b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bcp47.py @@ -0,0 +1,218 @@ +# Natural Language Toolkit: BCP-47 language tags +# +# Copyright (C) 2022-2023 NLTK Project +# Author: Eric Kafe +# URL: +# For license information, see LICENSE.TXT + +import re +from warnings import warn +from xml.etree import ElementTree as et + +from nltk.corpus.reader import CorpusReader + + +class BCP47CorpusReader(CorpusReader): + """ + Parse BCP-47 composite language tags + + Supports all the main subtags, and the 'u-sd' extension: + + >>> from nltk.corpus import bcp47 + >>> bcp47.name('oc-gascon-u-sd-fr64') + 'Occitan (post 1500): Gascon: Pyrénées-Atlantiques' + + Can load a conversion table to Wikidata Q-codes: + >>> bcp47.load_wiki_q() + >>> bcp47.wiki_q['en-GI-spanglis'] + 'Q79388' + + """ + + def __init__(self, root, fileids): + """Read the BCP-47 database""" + super().__init__(root, fileids) + self.langcode = {} + with self.open("iana/language-subtag-registry.txt") as fp: + self.db = self.data_dict(fp.read().split("%%\n")) + with self.open("cldr/common-subdivisions-en.xml") as fp: + self.subdiv = self.subdiv_dict( + et.parse(fp).iterfind("localeDisplayNames/subdivisions/subdivision") + ) + self.morphology() + + def load_wiki_q(self): + """Load conversion table to Wikidata Q-codes (only if needed)""" + with self.open("cldr/tools-cldr-rdf-external-entityToCode.tsv") as fp: + self.wiki_q = self.wiki_dict(fp.read().strip().split("\n")[1:]) + + def wiki_dict(self, lines): + """Convert Wikidata list of Q-codes to a BCP-47 dictionary""" + return { + pair[1]: pair[0].split("/")[-1] + for pair in [line.strip().split("\t") for line in lines] + } + + def subdiv_dict(self, subdivs): + """Convert the CLDR subdivisions list to a dictionary""" + return {sub.attrib["type"]: sub.text for sub in subdivs} + + def morphology(self): + self.casing = { + "language": str.lower, + "extlang": str.lower, + "script": str.title, + "region": str.upper, + "variant": str.lower, + } + dig = "[0-9]" + low = "[a-z]" + up = "[A-Z]" + alnum = "[a-zA-Z0-9]" + self.format = { + "language": re.compile(f"{low*3}?"), + "extlang": re.compile(f"{low*3}"), + "script": re.compile(f"{up}{low*3}"), + "region": re.compile(f"({up*2})|({dig*3})"), + "variant": re.compile(f"{alnum*4}{(alnum+'?')*4}"), + "singleton": re.compile(f"{low}"), + } + + def data_dict(self, records): + """Convert the BCP-47 language subtag registry to a dictionary""" + self.version = records[0].replace("File-Date:", "").strip() + dic = {} + dic["deprecated"] = {} + for label in [ + "language", + "extlang", + "script", + "region", + "variant", + "redundant", + "grandfathered", + ]: + dic["deprecated"][label] = {} + for record in records[1:]: + fields = [field.split(": ") for field in record.strip().split("\n")] + typ = fields[0][1] + tag = fields[1][1] + if typ not in dic: + dic[typ] = {} + subfields = {} + for field in fields[2:]: + if len(field) == 2: + [key, val] = field + if key not in subfields: + subfields[key] = [val] + else: # multiple value + subfields[key].append(val) + else: # multiline field + subfields[key][-1] += " " + field[0].strip() + if ( + "Deprecated" not in record + and typ == "language" + and key == "Description" + ): + self.langcode[subfields[key][-1]] = tag + for key in subfields: + if len(subfields[key]) == 1: # single value + subfields[key] = subfields[key][0] + if "Deprecated" in record: + dic["deprecated"][typ][tag] = subfields + else: + dic[typ][tag] = subfields + return dic + + def val2str(self, val): + """Return only first value""" + if type(val) == list: + # val = "/".join(val) # Concatenate all values + val = val[0] + return val + + def lang2str(self, lg_record): + """Concatenate subtag values""" + name = f"{lg_record['language']}" + for label in ["extlang", "script", "region", "variant", "extension"]: + if label in lg_record: + name += f": {lg_record[label]}" + return name + + def parse_tag(self, tag): + """Convert a BCP-47 tag to a dictionary of labelled subtags""" + subtags = tag.split("-") + lang = {} + labels = ["language", "extlang", "script", "region", "variant", "variant"] + while subtags and labels: + subtag = subtags.pop(0) + found = False + while labels: + label = labels.pop(0) + subtag = self.casing[label](subtag) + if self.format[label].fullmatch(subtag): + if subtag in self.db[label]: + found = True + valstr = self.val2str(self.db[label][subtag]["Description"]) + if label == "variant" and label in lang: + lang[label] += ": " + valstr + else: + lang[label] = valstr + break + elif subtag in self.db["deprecated"][label]: + found = True + note = f"The {subtag!r} {label} code is deprecated" + if "Preferred-Value" in self.db["deprecated"][label][subtag]: + prefer = self.db["deprecated"][label][subtag][ + "Preferred-Value" + ] + note += f"', prefer '{self.val2str(prefer)}'" + lang[label] = self.val2str( + self.db["deprecated"][label][subtag]["Description"] + ) + warn(note) + break + if not found: + if subtag == "u" and subtags[0] == "sd": # CLDR regional subdivisions + sd = subtags[1] + if sd in self.subdiv: + ext = self.subdiv[sd] + else: + ext = f"" + else: # other extension subtags are not supported yet + ext = f"{subtag}{''.join(['-'+ext for ext in subtags])}".lower() + if not self.format["singleton"].fullmatch(subtag): + ext = f"" + warn(ext) + lang["extension"] = ext + subtags = [] + return lang + + def name(self, tag): + """ + Convert a BCP-47 tag to a colon-separated string of subtag names + + >>> from nltk.corpus import bcp47 + >>> bcp47.name('ca-Latn-ES-valencia') + 'Catalan: Latin: Spain: Valencian' + + """ + for label in ["redundant", "grandfathered"]: + val = None + if tag in self.db[label]: + val = f"{self.db[label][tag]['Description']}" + note = f"The {tag!r} code is {label}" + elif tag in self.db["deprecated"][label]: + val = f"{self.db['deprecated'][label][tag]['Description']}" + note = f"The {tag!r} code is {label} and deprecated" + if "Preferred-Value" in self.db["deprecated"][label][tag]: + prefer = self.db["deprecated"][label][tag]["Preferred-Value"] + note += f", prefer {self.val2str(prefer)!r}" + if val: + warn(note) + return val + try: + return self.lang2str(self.parse_tag(tag)) + except: + warn(f"Tag {tag!r} was not recognized") + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bnc.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bnc.py new file mode 100644 index 0000000..be8d217 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bnc.py @@ -0,0 +1,265 @@ +# Natural Language Toolkit: Plaintext Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +"""Corpus reader for the XML version of the British National Corpus.""" + +from nltk.corpus.reader.util import concat +from nltk.corpus.reader.xmldocs import ElementTree, XMLCorpusReader, XMLCorpusView + + +class BNCCorpusReader(XMLCorpusReader): + r"""Corpus reader for the XML version of the British National Corpus. + + For access to the complete XML data structure, use the ``xml()`` + method. For access to simple word lists and tagged word lists, use + ``words()``, ``sents()``, ``tagged_words()``, and ``tagged_sents()``. + + You can obtain the full version of the BNC corpus at + https://www.ota.ox.ac.uk/desc/2554 + + If you extracted the archive to a directory called `BNC`, then you can + instantiate the reader as:: + + BNCCorpusReader(root='BNC/Texts/', fileids=r'[A-K]/\w*/\w*\.xml') + + """ + + def __init__(self, root, fileids, lazy=True): + XMLCorpusReader.__init__(self, root, fileids) + self._lazy = lazy + + def words(self, fileids=None, strip_space=True, stem=False): + """ + :return: the given file(s) as a list of words + and punctuation symbols. + :rtype: list(str) + + :param strip_space: If true, then strip trailing spaces from + word tokens. Otherwise, leave the spaces on the tokens. + :param stem: If true, then use word stems instead of word strings. + """ + return self._views(fileids, False, None, strip_space, stem) + + def tagged_words(self, fileids=None, c5=False, strip_space=True, stem=False): + """ + :return: the given file(s) as a list of tagged + words and punctuation symbols, encoded as tuples + ``(word,tag)``. + :rtype: list(tuple(str,str)) + + :param c5: If true, then the tags used will be the more detailed + c5 tags. Otherwise, the simplified tags will be used. + :param strip_space: If true, then strip trailing spaces from + word tokens. Otherwise, leave the spaces on the tokens. + :param stem: If true, then use word stems instead of word strings. + """ + tag = "c5" if c5 else "pos" + return self._views(fileids, False, tag, strip_space, stem) + + def sents(self, fileids=None, strip_space=True, stem=False): + """ + :return: the given file(s) as a list of + sentences or utterances, each encoded as a list of word + strings. + :rtype: list(list(str)) + + :param strip_space: If true, then strip trailing spaces from + word tokens. Otherwise, leave the spaces on the tokens. + :param stem: If true, then use word stems instead of word strings. + """ + return self._views(fileids, True, None, strip_space, stem) + + def tagged_sents(self, fileids=None, c5=False, strip_space=True, stem=False): + """ + :return: the given file(s) as a list of + sentences, each encoded as a list of ``(word,tag)`` tuples. + :rtype: list(list(tuple(str,str))) + + :param c5: If true, then the tags used will be the more detailed + c5 tags. Otherwise, the simplified tags will be used. + :param strip_space: If true, then strip trailing spaces from + word tokens. Otherwise, leave the spaces on the tokens. + :param stem: If true, then use word stems instead of word strings. + """ + tag = "c5" if c5 else "pos" + return self._views( + fileids, sent=True, tag=tag, strip_space=strip_space, stem=stem + ) + + def _views(self, fileids=None, sent=False, tag=False, strip_space=True, stem=False): + """A helper function that instantiates BNCWordViews or the list of words/sentences.""" + f = BNCWordView if self._lazy else self._words + return concat( + [ + f(fileid, sent, tag, strip_space, stem) + for fileid in self.abspaths(fileids) + ] + ) + + def _words(self, fileid, bracket_sent, tag, strip_space, stem): + """ + Helper used to implement the view methods -- returns a list of + words or a list of sentences, optionally tagged. + + :param fileid: The name of the underlying file. + :param bracket_sent: If true, include sentence bracketing. + :param tag: The name of the tagset to use, or None for no tags. + :param strip_space: If true, strip spaces from word tokens. + :param stem: If true, then substitute stems for words. + """ + result = [] + + xmldoc = ElementTree.parse(fileid).getroot() + for xmlsent in xmldoc.findall(".//s"): + sent = [] + for xmlword in _all_xmlwords_in(xmlsent): + word = xmlword.text + if not word: + word = "" # fixes issue 337? + if strip_space or stem: + word = word.strip() + if stem: + word = xmlword.get("hw", word) + if tag == "c5": + word = (word, xmlword.get("c5")) + elif tag == "pos": + word = (word, xmlword.get("pos", xmlword.get("c5"))) + sent.append(word) + if bracket_sent: + result.append(BNCSentence(xmlsent.attrib["n"], sent)) + else: + result.extend(sent) + + assert None not in result + return result + + +def _all_xmlwords_in(elt, result=None): + if result is None: + result = [] + for child in elt: + if child.tag in ("c", "w"): + result.append(child) + else: + _all_xmlwords_in(child, result) + return result + + +class BNCSentence(list): + """ + A list of words, augmented by an attribute ``num`` used to record + the sentence identifier (the ``n`` attribute from the XML). + """ + + def __init__(self, num, items): + self.num = num + list.__init__(self, items) + + +class BNCWordView(XMLCorpusView): + """ + A stream backed corpus view specialized for use with the BNC corpus. + """ + + tags_to_ignore = { + "pb", + "gap", + "vocal", + "event", + "unclear", + "shift", + "pause", + "align", + } + """These tags are ignored. For their description refer to the + technical documentation, for example, + http://www.natcorp.ox.ac.uk/docs/URG/ref-vocal.html + + """ + + def __init__(self, fileid, sent, tag, strip_space, stem): + """ + :param fileid: The name of the underlying file. + :param sent: If true, include sentence bracketing. + :param tag: The name of the tagset to use, or None for no tags. + :param strip_space: If true, strip spaces from word tokens. + :param stem: If true, then substitute stems for words. + """ + if sent: + tagspec = ".*/s" + else: + tagspec = ".*/s/(.*/)?(c|w)" + self._sent = sent + self._tag = tag + self._strip_space = strip_space + self._stem = stem + + self.title = None #: Title of the document. + self.author = None #: Author of the document. + self.editor = None #: Editor + self.resps = None #: Statement of responsibility + + XMLCorpusView.__init__(self, fileid, tagspec) + + # Read in a tasty header. + self._open() + self.read_block(self._stream, ".*/teiHeader$", self.handle_header) + self.close() + + # Reset tag context. + self._tag_context = {0: ()} + + def handle_header(self, elt, context): + # Set up some metadata! + titles = elt.findall("titleStmt/title") + if titles: + self.title = "\n".join(title.text.strip() for title in titles) + + authors = elt.findall("titleStmt/author") + if authors: + self.author = "\n".join(author.text.strip() for author in authors) + + editors = elt.findall("titleStmt/editor") + if editors: + self.editor = "\n".join(editor.text.strip() for editor in editors) + + resps = elt.findall("titleStmt/respStmt") + if resps: + self.resps = "\n\n".join( + "\n".join(resp_elt.text.strip() for resp_elt in resp) for resp in resps + ) + + def handle_elt(self, elt, context): + if self._sent: + return self.handle_sent(elt) + else: + return self.handle_word(elt) + + def handle_word(self, elt): + word = elt.text + if not word: + word = "" # fixes issue 337? + if self._strip_space or self._stem: + word = word.strip() + if self._stem: + word = elt.get("hw", word) + if self._tag == "c5": + word = (word, elt.get("c5")) + elif self._tag == "pos": + word = (word, elt.get("pos", elt.get("c5"))) + return word + + def handle_sent(self, elt): + sent = [] + for child in elt: + if child.tag in ("mw", "hi", "corr", "trunc"): + sent += [self.handle_word(w) for w in child] + elif child.tag in ("w", "c"): + sent.append(self.handle_word(child)) + elif child.tag not in self.tags_to_ignore: + raise ValueError("Unexpected element %s" % child.tag) + return BNCSentence(elt.attrib["n"], sent) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bracket_parse.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bracket_parse.py new file mode 100644 index 0000000..7a35afc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/bracket_parse.py @@ -0,0 +1,237 @@ +# Natural Language Toolkit: Penn Treebank Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +""" +Corpus reader for corpora that consist of parenthesis-delineated parse trees. +""" + +import sys + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tag import map_tag +from nltk.tree import Tree + +# we use [^\s()]+ instead of \S+? to avoid matching () +SORTTAGWRD = re.compile(r"\((\d+) ([^\s()]+) ([^\s()]+)\)") +TAGWORD = re.compile(r"\(([^\s()]+) ([^\s()]+)\)") +WORD = re.compile(r"\([^\s()]+ ([^\s()]+)\)") +EMPTY_BRACKETS = re.compile(r"\s*\(\s*\(") + + +class BracketParseCorpusReader(SyntaxCorpusReader): + """ + Reader for corpora that consist of parenthesis-delineated parse trees, + like those found in the "combined" section of the Penn Treebank, + e.g. "(S (NP (DT the) (JJ little) (NN dog)) (VP (VBD barked)))". + + """ + + def __init__( + self, + root, + fileids, + comment_char=None, + detect_blocks="unindented_paren", + encoding="utf8", + tagset=None, + ): + """ + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + :param comment_char: The character which can appear at the start of + a line to indicate that the rest of the line is a comment. + :param detect_blocks: The method that is used to find blocks + in the corpus; can be 'unindented_paren' (every unindented + parenthesis starts a new parse) or 'sexpr' (brackets are + matched). + :param tagset: The name of the tagset used by this corpus, to be used + for normalizing or converting the POS tags returned by the + ``tagged_...()`` methods. + """ + SyntaxCorpusReader.__init__(self, root, fileids, encoding) + self._comment_char = comment_char + self._detect_blocks = detect_blocks + self._tagset = tagset + + def _read_block(self, stream): + if self._detect_blocks == "sexpr": + return read_sexpr_block(stream, comment_char=self._comment_char) + elif self._detect_blocks == "blankline": + return read_blankline_block(stream) + elif self._detect_blocks == "unindented_paren": + # Tokens start with unindented left parens. + toks = read_regexp_block(stream, start_re=r"^\(") + # Strip any comments out of the tokens. + if self._comment_char: + toks = [ + re.sub("(?m)^%s.*" % re.escape(self._comment_char), "", tok) + for tok in toks + ] + return toks + else: + assert 0, "bad block type" + + def _normalize(self, t): + # Replace leaves of the form (!), (,), with (! !), (, ,) + t = re.sub(r"\((.)\)", r"(\1 \1)", t) + # Replace leaves of the form (tag word root) with (tag word) + t = re.sub(r"\(([^\s()]+) ([^\s()]+) [^\s()]+\)", r"(\1 \2)", t) + return t + + def _parse(self, t): + try: + tree = Tree.fromstring(self._normalize(t)) + # If there's an empty node at the top, strip it off + if tree.label() == "" and len(tree) == 1: + return tree[0] + else: + return tree + + except ValueError as e: + sys.stderr.write("Bad tree detected; trying to recover...\n") + # Try to recover, if we can: + if e.args == ("mismatched parens",): + for n in range(1, 5): + try: + v = Tree(self._normalize(t + ")" * n)) + sys.stderr.write( + " Recovered by adding %d close " "paren(s)\n" % n + ) + return v + except ValueError: + pass + # Try something else: + sys.stderr.write(" Recovered by returning a flat parse.\n") + # sys.stderr.write(' '.join(t.split())+'\n') + return Tree("S", self._tag(t)) + + def _tag(self, t, tagset=None): + tagged_sent = [(w, p) for (p, w) in TAGWORD.findall(self._normalize(t))] + if tagset and tagset != self._tagset: + tagged_sent = [ + (w, map_tag(self._tagset, tagset, p)) for (w, p) in tagged_sent + ] + return tagged_sent + + def _word(self, t): + return WORD.findall(self._normalize(t)) + + +class CategorizedBracketParseCorpusReader( + CategorizedCorpusReader, BracketParseCorpusReader +): + """ + A reader for parsed corpora whose documents are + divided into categories based on their file identifiers. + @author: Nathan Schneider + """ + + def __init__(self, *args, **kwargs): + """ + Initialize the corpus reader. Categorization arguments + (C{cat_pattern}, C{cat_map}, and C{cat_file}) are passed to + the L{CategorizedCorpusReader constructor + }. The remaining arguments + are passed to the L{BracketParseCorpusReader constructor + }. + """ + CategorizedCorpusReader.__init__(self, kwargs) + BracketParseCorpusReader.__init__(self, *args, **kwargs) + + def tagged_words(self, fileids=None, categories=None, tagset=None): + return super().tagged_words(self._resolve(fileids, categories), tagset) + + def tagged_sents(self, fileids=None, categories=None, tagset=None): + return super().tagged_sents(self._resolve(fileids, categories), tagset) + + def tagged_paras(self, fileids=None, categories=None, tagset=None): + return super().tagged_paras(self._resolve(fileids, categories), tagset) + + def parsed_words(self, fileids=None, categories=None): + return super().parsed_words(self._resolve(fileids, categories)) + + def parsed_sents(self, fileids=None, categories=None): + return super().parsed_sents(self._resolve(fileids, categories)) + + def parsed_paras(self, fileids=None, categories=None): + return super().parsed_paras(self._resolve(fileids, categories)) + + +class AlpinoCorpusReader(BracketParseCorpusReader): + """ + Reader for the Alpino Dutch Treebank. + This corpus has a lexical breakdown structure embedded, as read by `_parse` + Unfortunately this puts punctuation and some other words out of the sentence + order in the xml element tree. This is no good for `tag_` and `word_` + `_tag` and `_word` will be overridden to use a non-default new parameter 'ordered' + to the overridden _normalize function. The _parse function can then remain + untouched. + """ + + def __init__(self, root, encoding="ISO-8859-1", tagset=None): + BracketParseCorpusReader.__init__( + self, + root, + r"alpino\.xml", + detect_blocks="blankline", + encoding=encoding, + tagset=tagset, + ) + + def _normalize(self, t, ordered=False): + """Normalize the xml sentence element in t. + The sentence elements , although embedded in a few overall + xml elements, are separated by blank lines. That's how the reader can + deliver them one at a time. + Each sentence has a few category subnodes that are of no use to us. + The remaining word nodes may or may not appear in the proper order. + Each word node has attributes, among which: + - begin : the position of the word in the sentence + - pos : Part of Speech: the Tag + - word : the actual word + The return value is a string with all xml elementes replaced by + clauses: either a cat clause with nested clauses, or a word clause. + The order of the bracket clauses closely follows the xml. + If ordered == True, the word clauses include an order sequence number. + If ordered == False, the word clauses only have pos and word parts. + """ + if t[:10] != "', r"(\1", t) + if ordered: + t = re.sub( + r' ', + r"(\1 \2 \3)", + t, + ) + else: + t = re.sub(r' ', r"(\1 \2)", t) + t = re.sub(r" ", r")", t) + t = re.sub(r".*", r"", t) + t = re.sub(r"", r"", t) + return t + + def _tag(self, t, tagset=None): + tagged_sent = [ + (int(o), w, p) + for (o, p, w) in SORTTAGWRD.findall(self._normalize(t, ordered=True)) + ] + tagged_sent.sort() + if tagset and tagset != self._tagset: + tagged_sent = [ + (w, map_tag(self._tagset, tagset, p)) for (o, w, p) in tagged_sent + ] + else: + tagged_sent = [(w, p) for (o, w, p) in tagged_sent] + return tagged_sent + + def _word(self, t): + """Return a correctly ordered list if words""" + tagged_sent = self._tag(t) + return [w for (w, p) in tagged_sent] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/categorized_sents.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/categorized_sents.py new file mode 100644 index 0000000..371e8cd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/categorized_sents.py @@ -0,0 +1,168 @@ +# Natural Language Toolkit: Categorized Sentences Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader structured for corpora that contain one instance on each row. +This CorpusReader is specifically used for the Subjectivity Dataset and the +Sentence Polarity Dataset. + +- Subjectivity Dataset information - + +Authors: Bo Pang and Lillian Lee. +Url: https://www.cs.cornell.edu/people/pabo/movie-review-data + +Distributed with permission. + +Related papers: + +- Bo Pang and Lillian Lee. "A Sentimental Education: Sentiment Analysis Using + Subjectivity Summarization Based on Minimum Cuts". Proceedings of the ACL, + 2004. + +- Sentence Polarity Dataset information - + +Authors: Bo Pang and Lillian Lee. +Url: https://www.cs.cornell.edu/people/pabo/movie-review-data + +Related papers: + +- Bo Pang and Lillian Lee. "Seeing stars: Exploiting class relationships for + sentiment categorization with respect to rating scales". Proceedings of the + ACL, 2005. +""" + +from nltk.corpus.reader.api import * +from nltk.tokenize import * + + +class CategorizedSentencesCorpusReader(CategorizedCorpusReader, CorpusReader): + """ + A reader for corpora in which each row represents a single instance, mainly + a sentence. Istances are divided into categories based on their file identifiers + (see CategorizedCorpusReader). + Since many corpora allow rows that contain more than one sentence, it is + possible to specify a sentence tokenizer to retrieve all sentences instead + than all rows. + + Examples using the Subjectivity Dataset: + + >>> from nltk.corpus import subjectivity + >>> subjectivity.sents()[23] # doctest: +NORMALIZE_WHITESPACE + ['television', 'made', 'him', 'famous', ',', 'but', 'his', 'biggest', 'hits', + 'happened', 'off', 'screen', '.'] + >>> subjectivity.categories() + ['obj', 'subj'] + >>> subjectivity.words(categories='subj') + ['smart', 'and', 'alert', ',', 'thirteen', ...] + + Examples using the Sentence Polarity Dataset: + + >>> from nltk.corpus import sentence_polarity + >>> sentence_polarity.sents() # doctest: +NORMALIZE_WHITESPACE + [['simplistic', ',', 'silly', 'and', 'tedious', '.'], ["it's", 'so', 'laddish', + 'and', 'juvenile', ',', 'only', 'teenage', 'boys', 'could', 'possibly', 'find', + 'it', 'funny', '.'], ...] + >>> sentence_polarity.categories() + ['neg', 'pos'] + """ + + CorpusView = StreamBackedCorpusView + + def __init__( + self, + root, + fileids, + word_tokenizer=WhitespaceTokenizer(), + sent_tokenizer=None, + encoding="utf8", + **kwargs + ): + """ + :param root: The root directory for the corpus. + :param fileids: a list or regexp specifying the fileids in the corpus. + :param word_tokenizer: a tokenizer for breaking sentences or paragraphs + into words. Default: `WhitespaceTokenizer` + :param sent_tokenizer: a tokenizer for breaking paragraphs into sentences. + :param encoding: the encoding that should be used to read the corpus. + :param kwargs: additional parameters passed to CategorizedCorpusReader. + """ + + CorpusReader.__init__(self, root, fileids, encoding) + CategorizedCorpusReader.__init__(self, kwargs) + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + + def sents(self, fileids=None, categories=None): + """ + Return all sentences in the corpus or in the specified file(s). + + :param fileids: a list or regexp specifying the ids of the files whose + sentences have to be returned. + :param categories: a list specifying the categories whose sentences have + to be returned. + :return: the given file(s) as a list of sentences. + Each sentence is tokenized using the specified word_tokenizer. + :rtype: list(list(str)) + """ + fileids = self._resolve(fileids, categories) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_sent_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def words(self, fileids=None, categories=None): + """ + Return all words and punctuation symbols in the corpus or in the specified + file(s). + + :param fileids: a list or regexp specifying the ids of the files whose + words have to be returned. + :param categories: a list specifying the categories whose words have to + be returned. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + fileids = self._resolve(fileids, categories) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def _read_sent_block(self, stream): + sents = [] + for i in range(20): # Read 20 lines at a time. + line = stream.readline() + if not line: + continue + if self._sent_tokenizer: + sents.extend( + [ + self._word_tokenizer.tokenize(sent) + for sent in self._sent_tokenizer.tokenize(line) + ] + ) + else: + sents.append(self._word_tokenizer.tokenize(line)) + return sents + + def _read_word_block(self, stream): + words = [] + for sent in self._read_sent_block(stream): + words.extend(sent) + return words diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chasen.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chasen.py new file mode 100644 index 0000000..705e3b4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chasen.py @@ -0,0 +1,158 @@ +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Masato Hagiwara +# URL: +# For license information, see LICENSE.TXT + +import sys + +from nltk.corpus.reader import util +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * + + +class ChasenCorpusReader(CorpusReader): + def __init__(self, root, fileids, encoding="utf8", sent_splitter=None): + self._sent_splitter = sent_splitter + CorpusReader.__init__(self, root, fileids, encoding) + + def words(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, False, False, False, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_words(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, True, False, False, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, False, True, False, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_sents(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, True, True, False, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def paras(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, False, True, True, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_paras(self, fileids=None): + return concat( + [ + ChasenCorpusView(fileid, enc, True, True, True, self._sent_splitter) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + +class ChasenCorpusView(StreamBackedCorpusView): + """ + A specialized corpus view for ChasenReader. Similar to ``TaggedCorpusView``, + but this'll use fixed sets of word and sentence tokenizer. + """ + + def __init__( + self, + corpus_file, + encoding, + tagged, + group_by_sent, + group_by_para, + sent_splitter=None, + ): + self._tagged = tagged + self._group_by_sent = group_by_sent + self._group_by_para = group_by_para + self._sent_splitter = sent_splitter + StreamBackedCorpusView.__init__(self, corpus_file, encoding=encoding) + + def read_block(self, stream): + """Reads one paragraph at a time.""" + block = [] + for para_str in read_regexp_block(stream, r".", r"^EOS\n"): + + para = [] + + sent = [] + for line in para_str.splitlines(): + + _eos = line.strip() == "EOS" + _cells = line.split("\t") + w = (_cells[0], "\t".join(_cells[1:])) + if not _eos: + sent.append(w) + + if _eos or (self._sent_splitter and self._sent_splitter(w)): + if not self._tagged: + sent = [w for (w, t) in sent] + if self._group_by_sent: + para.append(sent) + else: + para.extend(sent) + sent = [] + + if len(sent) > 0: + if not self._tagged: + sent = [w for (w, t) in sent] + + if self._group_by_sent: + para.append(sent) + else: + para.extend(sent) + + if self._group_by_para: + block.append(para) + else: + block.extend(para) + + return block + + +def demo(): + + import nltk + from nltk.corpus.util import LazyCorpusLoader + + jeita = LazyCorpusLoader("jeita", ChasenCorpusReader, r".*chasen", encoding="utf-8") + print("/".join(jeita.words()[22100:22140])) + + print( + "\nEOS\n".join( + "\n".join("{}/{}".format(w[0], w[1].split("\t")[2]) for w in sent) + for sent in jeita.tagged_sents()[2170:2173] + ) + ) + + +def test(): + + from nltk.corpus.util import LazyCorpusLoader + + jeita = LazyCorpusLoader("jeita", ChasenCorpusReader, r".*chasen", encoding="utf-8") + + assert isinstance(jeita.tagged_words()[0][1], str) + + +if __name__ == "__main__": + demo() + test() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/childes.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/childes.py new file mode 100644 index 0000000..59feccd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/childes.py @@ -0,0 +1,630 @@ +# CHILDES XML Corpus Reader + +# Copyright (C) 2001-2023 NLTK Project +# Author: Tomonori Nagano +# Alexis Dimitriadis +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for the XML version of the CHILDES corpus. +""" + +__docformat__ = "epytext en" + +import re +from collections import defaultdict + +from nltk.corpus.reader.util import concat +from nltk.corpus.reader.xmldocs import ElementTree, XMLCorpusReader +from nltk.util import LazyConcatenation, LazyMap, flatten + +# to resolve the namespace issue +NS = "http://www.talkbank.org/ns/talkbank" + + +class CHILDESCorpusReader(XMLCorpusReader): + """ + Corpus reader for the XML version of the CHILDES corpus. + The CHILDES corpus is available at ``https://childes.talkbank.org/``. The XML + version of CHILDES is located at ``https://childes.talkbank.org/data-xml/``. + Copy the needed parts of the CHILDES XML corpus into the NLTK data directory + (``nltk_data/corpora/CHILDES/``). + + For access to the file text use the usual nltk functions, + ``words()``, ``sents()``, ``tagged_words()`` and ``tagged_sents()``. + """ + + def __init__(self, root, fileids, lazy=True): + XMLCorpusReader.__init__(self, root, fileids) + self._lazy = lazy + + def words( + self, + fileids=None, + speaker="ALL", + stem=False, + relation=False, + strip_space=True, + replace=False, + ): + """ + :return: the given file(s) as a list of words + :rtype: list(str) + + :param speaker: If specified, select specific speaker(s) defined + in the corpus. Default is 'ALL' (all participants). Common choices + are 'CHI' (the child), 'MOT' (mother), ['CHI','MOT'] (exclude + researchers) + :param stem: If true, then use word stems instead of word strings. + :param relation: If true, then return tuples of (stem, index, + dependent_index) + :param strip_space: If true, then strip trailing spaces from word + tokens. Otherwise, leave the spaces on the tokens. + :param replace: If true, then use the replaced (intended) word instead + of the original word (e.g., 'wat' will be replaced with 'watch') + """ + sent = None + pos = False + if not self._lazy: + return [ + self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + for fileid in self.abspaths(fileids) + ] + + get_words = lambda fileid: self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + return LazyConcatenation(LazyMap(get_words, self.abspaths(fileids))) + + def tagged_words( + self, + fileids=None, + speaker="ALL", + stem=False, + relation=False, + strip_space=True, + replace=False, + ): + """ + :return: the given file(s) as a list of tagged + words and punctuation symbols, encoded as tuples + ``(word,tag)``. + :rtype: list(tuple(str,str)) + + :param speaker: If specified, select specific speaker(s) defined + in the corpus. Default is 'ALL' (all participants). Common choices + are 'CHI' (the child), 'MOT' (mother), ['CHI','MOT'] (exclude + researchers) + :param stem: If true, then use word stems instead of word strings. + :param relation: If true, then return tuples of (stem, index, + dependent_index) + :param strip_space: If true, then strip trailing spaces from word + tokens. Otherwise, leave the spaces on the tokens. + :param replace: If true, then use the replaced (intended) word instead + of the original word (e.g., 'wat' will be replaced with 'watch') + """ + sent = None + pos = True + if not self._lazy: + return [ + self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + for fileid in self.abspaths(fileids) + ] + + get_words = lambda fileid: self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + return LazyConcatenation(LazyMap(get_words, self.abspaths(fileids))) + + def sents( + self, + fileids=None, + speaker="ALL", + stem=False, + relation=None, + strip_space=True, + replace=False, + ): + """ + :return: the given file(s) as a list of sentences or utterances, each + encoded as a list of word strings. + :rtype: list(list(str)) + + :param speaker: If specified, select specific speaker(s) defined + in the corpus. Default is 'ALL' (all participants). Common choices + are 'CHI' (the child), 'MOT' (mother), ['CHI','MOT'] (exclude + researchers) + :param stem: If true, then use word stems instead of word strings. + :param relation: If true, then return tuples of ``(str,pos,relation_list)``. + If there is manually-annotated relation info, it will return + tuples of ``(str,pos,test_relation_list,str,pos,gold_relation_list)`` + :param strip_space: If true, then strip trailing spaces from word + tokens. Otherwise, leave the spaces on the tokens. + :param replace: If true, then use the replaced (intended) word instead + of the original word (e.g., 'wat' will be replaced with 'watch') + """ + sent = True + pos = False + if not self._lazy: + return [ + self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + for fileid in self.abspaths(fileids) + ] + + get_words = lambda fileid: self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + return LazyConcatenation(LazyMap(get_words, self.abspaths(fileids))) + + def tagged_sents( + self, + fileids=None, + speaker="ALL", + stem=False, + relation=None, + strip_space=True, + replace=False, + ): + """ + :return: the given file(s) as a list of + sentences, each encoded as a list of ``(word,tag)`` tuples. + :rtype: list(list(tuple(str,str))) + + :param speaker: If specified, select specific speaker(s) defined + in the corpus. Default is 'ALL' (all participants). Common choices + are 'CHI' (the child), 'MOT' (mother), ['CHI','MOT'] (exclude + researchers) + :param stem: If true, then use word stems instead of word strings. + :param relation: If true, then return tuples of ``(str,pos,relation_list)``. + If there is manually-annotated relation info, it will return + tuples of ``(str,pos,test_relation_list,str,pos,gold_relation_list)`` + :param strip_space: If true, then strip trailing spaces from word + tokens. Otherwise, leave the spaces on the tokens. + :param replace: If true, then use the replaced (intended) word instead + of the original word (e.g., 'wat' will be replaced with 'watch') + """ + sent = True + pos = True + if not self._lazy: + return [ + self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + for fileid in self.abspaths(fileids) + ] + + get_words = lambda fileid: self._get_words( + fileid, speaker, sent, stem, relation, pos, strip_space, replace + ) + return LazyConcatenation(LazyMap(get_words, self.abspaths(fileids))) + + def corpus(self, fileids=None): + """ + :return: the given file(s) as a dict of ``(corpus_property_key, value)`` + :rtype: list(dict) + """ + if not self._lazy: + return [self._get_corpus(fileid) for fileid in self.abspaths(fileids)] + return LazyMap(self._get_corpus, self.abspaths(fileids)) + + def _get_corpus(self, fileid): + results = dict() + xmldoc = ElementTree.parse(fileid).getroot() + for key, value in xmldoc.items(): + results[key] = value + return results + + def participants(self, fileids=None): + """ + :return: the given file(s) as a dict of + ``(participant_property_key, value)`` + :rtype: list(dict) + """ + if not self._lazy: + return [self._get_participants(fileid) for fileid in self.abspaths(fileids)] + return LazyMap(self._get_participants, self.abspaths(fileids)) + + def _get_participants(self, fileid): + # multidimensional dicts + def dictOfDicts(): + return defaultdict(dictOfDicts) + + xmldoc = ElementTree.parse(fileid).getroot() + # getting participants' data + pat = dictOfDicts() + for participant in xmldoc.findall( + f".//{{{NS}}}Participants/{{{NS}}}participant" + ): + for (key, value) in participant.items(): + pat[participant.get("id")][key] = value + return pat + + def age(self, fileids=None, speaker="CHI", month=False): + """ + :return: the given file(s) as string or int + :rtype: list or int + + :param month: If true, return months instead of year-month-date + """ + if not self._lazy: + return [ + self._get_age(fileid, speaker, month) + for fileid in self.abspaths(fileids) + ] + get_age = lambda fileid: self._get_age(fileid, speaker, month) + return LazyMap(get_age, self.abspaths(fileids)) + + def _get_age(self, fileid, speaker, month): + xmldoc = ElementTree.parse(fileid).getroot() + for pat in xmldoc.findall(f".//{{{NS}}}Participants/{{{NS}}}participant"): + try: + if pat.get("id") == speaker: + age = pat.get("age") + if month: + age = self.convert_age(age) + return age + # some files don't have age data + except (TypeError, AttributeError) as e: + return None + + def convert_age(self, age_year): + "Caclculate age in months from a string in CHILDES format" + m = re.match(r"P(\d+)Y(\d+)M?(\d?\d?)D?", age_year) + age_month = int(m.group(1)) * 12 + int(m.group(2)) + try: + if int(m.group(3)) > 15: + age_month += 1 + # some corpora don't have age information? + except ValueError as e: + pass + return age_month + + def MLU(self, fileids=None, speaker="CHI"): + """ + :return: the given file(s) as a floating number + :rtype: list(float) + """ + if not self._lazy: + return [ + self._getMLU(fileid, speaker=speaker) + for fileid in self.abspaths(fileids) + ] + get_MLU = lambda fileid: self._getMLU(fileid, speaker=speaker) + return LazyMap(get_MLU, self.abspaths(fileids)) + + def _getMLU(self, fileid, speaker): + sents = self._get_words( + fileid, + speaker=speaker, + sent=True, + stem=True, + relation=False, + pos=True, + strip_space=True, + replace=True, + ) + results = [] + lastSent = [] + numFillers = 0 + sentDiscount = 0 + for sent in sents: + posList = [pos for (word, pos) in sent] + # if any part of the sentence is intelligible + if any(pos == "unk" for pos in posList): + continue + # if the sentence is null + elif sent == []: + continue + # if the sentence is the same as the last sent + elif sent == lastSent: + continue + else: + results.append([word for (word, pos) in sent]) + # count number of fillers + if len({"co", None}.intersection(posList)) > 0: + numFillers += posList.count("co") + numFillers += posList.count(None) + sentDiscount += 1 + lastSent = sent + try: + thisWordList = flatten(results) + # count number of morphemes + # (e.g., 'read' = 1 morpheme but 'read-PAST' is 2 morphemes) + numWords = ( + len(flatten([word.split("-") for word in thisWordList])) - numFillers + ) + numSents = len(results) - sentDiscount + mlu = numWords / numSents + except ZeroDivisionError: + mlu = 0 + # return {'mlu':mlu,'wordNum':numWords,'sentNum':numSents} + return mlu + + def _get_words( + self, fileid, speaker, sent, stem, relation, pos, strip_space, replace + ): + if ( + isinstance(speaker, str) and speaker != "ALL" + ): # ensure we have a list of speakers + speaker = [speaker] + xmldoc = ElementTree.parse(fileid).getroot() + # processing each xml doc + results = [] + for xmlsent in xmldoc.findall(".//{%s}u" % NS): + sents = [] + # select speakers + if speaker == "ALL" or xmlsent.get("who") in speaker: + for xmlword in xmlsent.findall(".//{%s}w" % NS): + infl = None + suffixStem = None + suffixTag = None + # getting replaced words + if replace and xmlsent.find(f".//{{{NS}}}w/{{{NS}}}replacement"): + xmlword = xmlsent.find( + f".//{{{NS}}}w/{{{NS}}}replacement/{{{NS}}}w" + ) + elif replace and xmlsent.find(f".//{{{NS}}}w/{{{NS}}}wk"): + xmlword = xmlsent.find(f".//{{{NS}}}w/{{{NS}}}wk") + # get text + if xmlword.text: + word = xmlword.text + else: + word = "" + # strip tailing space + if strip_space: + word = word.strip() + # stem + if relation or stem: + try: + xmlstem = xmlword.find(".//{%s}stem" % NS) + word = xmlstem.text + except AttributeError as e: + pass + # if there is an inflection + try: + xmlinfl = xmlword.find( + f".//{{{NS}}}mor/{{{NS}}}mw/{{{NS}}}mk" + ) + word += "-" + xmlinfl.text + except: + pass + # if there is a suffix + try: + xmlsuffix = xmlword.find( + ".//{%s}mor/{%s}mor-post/{%s}mw/{%s}stem" + % (NS, NS, NS, NS) + ) + suffixStem = xmlsuffix.text + except AttributeError: + suffixStem = "" + if suffixStem: + word += "~" + suffixStem + # pos + if relation or pos: + try: + xmlpos = xmlword.findall(".//{%s}c" % NS) + xmlpos2 = xmlword.findall(".//{%s}s" % NS) + if xmlpos2 != []: + tag = xmlpos[0].text + ":" + xmlpos2[0].text + else: + tag = xmlpos[0].text + except (AttributeError, IndexError) as e: + tag = "" + try: + xmlsuffixpos = xmlword.findall( + ".//{%s}mor/{%s}mor-post/{%s}mw/{%s}pos/{%s}c" + % (NS, NS, NS, NS, NS) + ) + xmlsuffixpos2 = xmlword.findall( + ".//{%s}mor/{%s}mor-post/{%s}mw/{%s}pos/{%s}s" + % (NS, NS, NS, NS, NS) + ) + if xmlsuffixpos2: + suffixTag = ( + xmlsuffixpos[0].text + ":" + xmlsuffixpos2[0].text + ) + else: + suffixTag = xmlsuffixpos[0].text + except: + pass + if suffixTag: + tag += "~" + suffixTag + word = (word, tag) + # relational + # the gold standard is stored in + # + if relation == True: + for xmlstem_rel in xmlword.findall( + f".//{{{NS}}}mor/{{{NS}}}gra" + ): + if not xmlstem_rel.get("type") == "grt": + word = ( + word[0], + word[1], + xmlstem_rel.get("index") + + "|" + + xmlstem_rel.get("head") + + "|" + + xmlstem_rel.get("relation"), + ) + else: + word = ( + word[0], + word[1], + word[2], + word[0], + word[1], + xmlstem_rel.get("index") + + "|" + + xmlstem_rel.get("head") + + "|" + + xmlstem_rel.get("relation"), + ) + try: + for xmlpost_rel in xmlword.findall( + f".//{{{NS}}}mor/{{{NS}}}mor-post/{{{NS}}}gra" + ): + if not xmlpost_rel.get("type") == "grt": + suffixStem = ( + suffixStem[0], + suffixStem[1], + xmlpost_rel.get("index") + + "|" + + xmlpost_rel.get("head") + + "|" + + xmlpost_rel.get("relation"), + ) + else: + suffixStem = ( + suffixStem[0], + suffixStem[1], + suffixStem[2], + suffixStem[0], + suffixStem[1], + xmlpost_rel.get("index") + + "|" + + xmlpost_rel.get("head") + + "|" + + xmlpost_rel.get("relation"), + ) + except: + pass + sents.append(word) + if sent or relation: + results.append(sents) + else: + results.extend(sents) + return LazyMap(lambda x: x, results) + + # Ready-to-use browser opener + + """ + The base URL for viewing files on the childes website. This + shouldn't need to be changed, unless CHILDES changes the configuration + of their server or unless the user sets up their own corpus webserver. + """ + childes_url_base = r"https://childes.talkbank.org/browser/index.php?url=" + + def webview_file(self, fileid, urlbase=None): + """Map a corpus file to its web version on the CHILDES website, + and open it in a web browser. + + The complete URL to be used is: + childes.childes_url_base + urlbase + fileid.replace('.xml', '.cha') + + If no urlbase is passed, we try to calculate it. This + requires that the childes corpus was set up to mirror the + folder hierarchy under childes.psy.cmu.edu/data-xml/, e.g.: + nltk_data/corpora/childes/Eng-USA/Cornell/??? or + nltk_data/corpora/childes/Romance/Spanish/Aguirre/??? + + The function first looks (as a special case) if "Eng-USA" is + on the path consisting of +fileid; then if + "childes", possibly followed by "data-xml", appears. If neither + one is found, we use the unmodified fileid and hope for the best. + If this is not right, specify urlbase explicitly, e.g., if the + corpus root points to the Cornell folder, urlbase='Eng-USA/Cornell'. + """ + + import webbrowser + + if urlbase: + path = urlbase + "/" + fileid + else: + full = self.root + "/" + fileid + full = re.sub(r"\\", "/", full) + if "/childes/" in full.lower(): + # Discard /data-xml/ if present + path = re.findall(r"(?i)/childes(?:/data-xml)?/(.*)\.xml", full)[0] + elif "eng-usa" in full.lower(): + path = "Eng-USA/" + re.findall(r"/(?i)Eng-USA/(.*)\.xml", full)[0] + else: + path = fileid + + # Strip ".xml" and add ".cha", as necessary: + if path.endswith(".xml"): + path = path[:-4] + + if not path.endswith(".cha"): + path = path + ".cha" + + url = self.childes_url_base + path + + webbrowser.open_new_tab(url) + print("Opening in browser:", url) + # Pausing is a good idea, but it's up to the user... + # raw_input("Hit Return to continue") + + +def demo(corpus_root=None): + """ + The CHILDES corpus should be manually downloaded and saved + to ``[NLTK_Data_Dir]/corpora/childes/`` + """ + if not corpus_root: + from nltk.data import find + + corpus_root = find("corpora/childes/data-xml/Eng-USA/") + + try: + childes = CHILDESCorpusReader(corpus_root, ".*.xml") + # describe all corpus + for file in childes.fileids()[:5]: + corpus = "" + corpus_id = "" + for (key, value) in childes.corpus(file)[0].items(): + if key == "Corpus": + corpus = value + if key == "Id": + corpus_id = value + print("Reading", corpus, corpus_id, " .....") + print("words:", childes.words(file)[:7], "...") + print( + "words with replaced words:", + childes.words(file, replace=True)[:7], + " ...", + ) + print("words with pos tags:", childes.tagged_words(file)[:7], " ...") + print("words (only MOT):", childes.words(file, speaker="MOT")[:7], "...") + print("words (only CHI):", childes.words(file, speaker="CHI")[:7], "...") + print("stemmed words:", childes.words(file, stem=True)[:7], " ...") + print( + "words with relations and pos-tag:", + childes.words(file, relation=True)[:5], + " ...", + ) + print("sentence:", childes.sents(file)[:2], " ...") + for (participant, values) in childes.participants(file)[0].items(): + for (key, value) in values.items(): + print("\tparticipant", participant, key, ":", value) + print("num of sent:", len(childes.sents(file))) + print("num of morphemes:", len(childes.words(file, stem=True))) + print("age:", childes.age(file)) + print("age in month:", childes.age(file, month=True)) + print("MLU:", childes.MLU(file)) + print() + + except LookupError as e: + print( + """The CHILDES corpus, or the parts you need, should be manually + downloaded from https://childes.talkbank.org/data-xml/ and saved at + [NLTK_Data_Dir]/corpora/childes/ + Alternately, you can call the demo with the path to a portion of the CHILDES corpus, e.g.: + demo('/path/to/childes/data-xml/Eng-USA/") + """ + ) + # corpus_root_http = urllib2.urlopen('https://childes.talkbank.org/data-xml/Eng-USA/Bates.zip') + # corpus_root_http_bates = zipfile.ZipFile(cStringIO.StringIO(corpus_root_http.read())) + ##this fails + # childes = CHILDESCorpusReader(corpus_root_http_bates,corpus_root_http_bates.namelist()) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chunked.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chunked.py new file mode 100644 index 0000000..5a7b5b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/chunked.py @@ -0,0 +1,273 @@ +# Natural Language Toolkit: Chunked Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +A reader for corpora that contain chunked (and optionally tagged) +documents. +""" + +import codecs +import os.path + +import nltk +from nltk.chunk import tagstr2tree +from nltk.corpus.reader.api import * +from nltk.corpus.reader.bracket_parse import BracketParseCorpusReader +from nltk.corpus.reader.util import * +from nltk.tokenize import * +from nltk.tree import Tree + + +class ChunkedCorpusReader(CorpusReader): + """ + Reader for chunked (and optionally tagged) corpora. Paragraphs + are split using a block reader. They are then tokenized into + sentences using a sentence tokenizer. Finally, these sentences + are parsed into chunk trees using a string-to-chunktree conversion + function. Each of these steps can be performed using a default + function or a custom function. By default, paragraphs are split + on blank lines; sentences are listed one per line; and sentences + are parsed into chunk trees using ``nltk.chunk.tagstr2tree``. + """ + + def __init__( + self, + root, + fileids, + extension="", + str2chunktree=tagstr2tree, + sent_tokenizer=RegexpTokenizer("\n", gaps=True), + para_block_reader=read_blankline_block, + encoding="utf8", + tagset=None, + ): + """ + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + """ + CorpusReader.__init__(self, root, fileids, encoding) + self._cv_args = (str2chunktree, sent_tokenizer, para_block_reader, tagset) + """Arguments for corpus views generated by this corpus: a tuple + (str2chunktree, sent_tokenizer, para_block_tokenizer)""" + + def words(self, fileids=None): + """ + :return: the given file(s) as a list of words + and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + ChunkedCorpusView(f, enc, 0, 0, 0, 0, *self._cv_args) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + """ + :return: the given file(s) as a list of + sentences or utterances, each encoded as a list of word + strings. + :rtype: list(list(str)) + """ + return concat( + [ + ChunkedCorpusView(f, enc, 0, 1, 0, 0, *self._cv_args) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def paras(self, fileids=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as lists of word strings. + :rtype: list(list(list(str))) + """ + return concat( + [ + ChunkedCorpusView(f, enc, 0, 1, 1, 0, *self._cv_args) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_words(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of tagged + words and punctuation symbols, encoded as tuples + ``(word,tag)``. + :rtype: list(tuple(str,str)) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 0, 0, 0, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_sents(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + sentences, each encoded as a list of ``(word,tag)`` tuples. + + :rtype: list(list(tuple(str,str))) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 1, 0, 0, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_paras(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as lists of ``(word,tag)`` tuples. + :rtype: list(list(list(tuple(str,str)))) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 1, 1, 0, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def chunked_words(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of tagged + words and chunks. Words are encoded as ``(word, tag)`` + tuples (if the corpus has tags) or word strings (if the + corpus has no tags). Chunks are encoded as depth-one + trees over ``(word,tag)`` tuples or word strings. + :rtype: list(tuple(str,str) and Tree) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 0, 0, 1, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def chunked_sents(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + sentences, each encoded as a shallow Tree. The leaves + of these trees are encoded as ``(word, tag)`` tuples (if + the corpus has tags) or word strings (if the corpus has no + tags). + :rtype: list(Tree) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 1, 0, 1, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def chunked_paras(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as a shallow Tree. The leaves of these + trees are encoded as ``(word, tag)`` tuples (if the corpus + has tags) or word strings (if the corpus has no tags). + :rtype: list(list(Tree)) + """ + return concat( + [ + ChunkedCorpusView( + f, enc, 1, 1, 1, 1, *self._cv_args, target_tagset=tagset + ) + for (f, enc) in self.abspaths(fileids, True) + ] + ) + + def _read_block(self, stream): + return [tagstr2tree(t) for t in read_blankline_block(stream)] + + +class ChunkedCorpusView(StreamBackedCorpusView): + def __init__( + self, + fileid, + encoding, + tagged, + group_by_sent, + group_by_para, + chunked, + str2chunktree, + sent_tokenizer, + para_block_reader, + source_tagset=None, + target_tagset=None, + ): + StreamBackedCorpusView.__init__(self, fileid, encoding=encoding) + self._tagged = tagged + self._group_by_sent = group_by_sent + self._group_by_para = group_by_para + self._chunked = chunked + self._str2chunktree = str2chunktree + self._sent_tokenizer = sent_tokenizer + self._para_block_reader = para_block_reader + self._source_tagset = source_tagset + self._target_tagset = target_tagset + + def read_block(self, stream): + block = [] + for para_str in self._para_block_reader(stream): + para = [] + for sent_str in self._sent_tokenizer.tokenize(para_str): + sent = self._str2chunktree( + sent_str, + source_tagset=self._source_tagset, + target_tagset=self._target_tagset, + ) + + # If requested, throw away the tags. + if not self._tagged: + sent = self._untag(sent) + + # If requested, throw away the chunks. + if not self._chunked: + sent = sent.leaves() + + # Add the sentence to `para`. + if self._group_by_sent: + para.append(sent) + else: + para.extend(sent) + + # Add the paragraph to `block`. + if self._group_by_para: + block.append(para) + else: + block.extend(para) + + # Return the block + return block + + def _untag(self, tree): + for i, child in enumerate(tree): + if isinstance(child, Tree): + self._untag(child) + elif isinstance(child, tuple): + tree[i] = child[0] + else: + raise ValueError("expected child to be Tree or tuple") + return tree diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/cmudict.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/cmudict.py new file mode 100644 index 0000000..06f1bcf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/cmudict.py @@ -0,0 +1,88 @@ +# Natural Language Toolkit: Carnegie Mellon Pronouncing Dictionary Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +The Carnegie Mellon Pronouncing Dictionary [cmudict.0.6] +ftp://ftp.cs.cmu.edu/project/speech/dict/ +Copyright 1998 Carnegie Mellon University + +File Format: Each line consists of an uppercased word, a counter +(for alternative pronunciations), and a transcription. Vowels are +marked for stress (1=primary, 2=secondary, 0=no stress). E.g.: +NATURAL 1 N AE1 CH ER0 AH0 L + +The dictionary contains 127069 entries. Of these, 119400 words are assigned +a unique pronunciation, 6830 words have two pronunciations, and 839 words have +three or more pronunciations. Many of these are fast-speech variants. + +Phonemes: There are 39 phonemes, as shown below: + +Phoneme Example Translation Phoneme Example Translation +------- ------- ----------- ------- ------- ----------- +AA odd AA D AE at AE T +AH hut HH AH T AO ought AO T +AW cow K AW AY hide HH AY D +B be B IY CH cheese CH IY Z +D dee D IY DH thee DH IY +EH Ed EH D ER hurt HH ER T +EY ate EY T F fee F IY +G green G R IY N HH he HH IY +IH it IH T IY eat IY T +JH gee JH IY K key K IY +L lee L IY M me M IY +N knee N IY NG ping P IH NG +OW oat OW T OY toy T OY +P pee P IY R read R IY D +S sea S IY SH she SH IY +T tea T IY TH theta TH EY T AH +UH hood HH UH D UW two T UW +V vee V IY W we W IY +Y yield Y IY L D Z zee Z IY +ZH seizure S IY ZH ER +""" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.util import Index + + +class CMUDictCorpusReader(CorpusReader): + def entries(self): + """ + :return: the cmudict lexicon as a list of entries + containing (word, transcriptions) tuples. + """ + return concat( + [ + StreamBackedCorpusView(fileid, read_cmudict_block, encoding=enc) + for fileid, enc in self.abspaths(None, True) + ] + ) + + def words(self): + """ + :return: a list of all words defined in the cmudict lexicon. + """ + return [word.lower() for (word, _) in self.entries()] + + def dict(self): + """ + :return: the cmudict lexicon as a dictionary, whose keys are + lowercase words and whose values are lists of pronunciations. + """ + return dict(Index(self.entries())) + + +def read_cmudict_block(stream): + entries = [] + while len(entries) < 100: # Read 100 at a time. + line = stream.readline() + if line == "": + return entries # end of file. + pieces = line.split() + entries.append((pieces[0].lower(), pieces[2:])) + return entries diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/comparative_sents.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/comparative_sents.py new file mode 100644 index 0000000..d6a341b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/comparative_sents.py @@ -0,0 +1,309 @@ +# Natural Language Toolkit: Comparative Sentence Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader for the Comparative Sentence Dataset. + +- Comparative Sentence Dataset information - + +Annotated by: Nitin Jindal and Bing Liu, 2006. + Department of Computer Sicence + University of Illinois at Chicago + +Contact: Nitin Jindal, njindal@cs.uic.edu + Bing Liu, liub@cs.uic.edu (https://www.cs.uic.edu/~liub) + +Distributed with permission. + +Related papers: + +- Nitin Jindal and Bing Liu. "Identifying Comparative Sentences in Text Documents". + Proceedings of the ACM SIGIR International Conference on Information Retrieval + (SIGIR-06), 2006. + +- Nitin Jindal and Bing Liu. "Mining Comprative Sentences and Relations". + Proceedings of Twenty First National Conference on Artificial Intelligence + (AAAI-2006), 2006. + +- Murthy Ganapathibhotla and Bing Liu. "Mining Opinions in Comparative Sentences". + Proceedings of the 22nd International Conference on Computational Linguistics + (Coling-2008), Manchester, 18-22 August, 2008. +""" +import re + +from nltk.corpus.reader.api import * +from nltk.tokenize import * + +# Regular expressions for dataset components +STARS = re.compile(r"^\*+$") +COMPARISON = re.compile(r"") +CLOSE_COMPARISON = re.compile(r"") +GRAD_COMPARISON = re.compile(r"") +NON_GRAD_COMPARISON = re.compile(r"") +ENTITIES_FEATS = re.compile(r"(\d)_((?:[\.\w\s/-](?!\d_))+)") +KEYWORD = re.compile(r"\(([^\(]*)\)$") + + +class Comparison: + """ + A Comparison represents a comparative sentence and its constituents. + """ + + def __init__( + self, + text=None, + comp_type=None, + entity_1=None, + entity_2=None, + feature=None, + keyword=None, + ): + """ + :param text: a string (optionally tokenized) containing a comparison. + :param comp_type: an integer defining the type of comparison expressed. + Values can be: 1 (Non-equal gradable), 2 (Equative), 3 (Superlative), + 4 (Non-gradable). + :param entity_1: the first entity considered in the comparison relation. + :param entity_2: the second entity considered in the comparison relation. + :param feature: the feature considered in the comparison relation. + :param keyword: the word or phrase which is used for that comparative relation. + """ + self.text = text + self.comp_type = comp_type + self.entity_1 = entity_1 + self.entity_2 = entity_2 + self.feature = feature + self.keyword = keyword + + def __repr__(self): + return ( + 'Comparison(text="{}", comp_type={}, entity_1="{}", entity_2="{}", ' + 'feature="{}", keyword="{}")' + ).format( + self.text, + self.comp_type, + self.entity_1, + self.entity_2, + self.feature, + self.keyword, + ) + + +class ComparativeSentencesCorpusReader(CorpusReader): + """ + Reader for the Comparative Sentence Dataset by Jindal and Liu (2006). + + >>> from nltk.corpus import comparative_sentences + >>> comparison = comparative_sentences.comparisons()[0] + >>> comparison.text # doctest: +NORMALIZE_WHITESPACE + ['its', 'fast-forward', 'and', 'rewind', 'work', 'much', 'more', 'smoothly', + 'and', 'consistently', 'than', 'those', 'of', 'other', 'models', 'i', "'ve", + 'had', '.'] + >>> comparison.entity_2 + 'models' + >>> (comparison.feature, comparison.keyword) + ('rewind', 'more') + >>> len(comparative_sentences.comparisons()) + 853 + """ + + CorpusView = StreamBackedCorpusView + + def __init__( + self, + root, + fileids, + word_tokenizer=WhitespaceTokenizer(), + sent_tokenizer=None, + encoding="utf8", + ): + """ + :param root: The root directory for this corpus. + :param fileids: a list or regexp specifying the fileids in this corpus. + :param word_tokenizer: tokenizer for breaking sentences or paragraphs + into words. Default: `WhitespaceTokenizer` + :param sent_tokenizer: tokenizer for breaking paragraphs into sentences. + :param encoding: the encoding that should be used to read the corpus. + """ + + CorpusReader.__init__(self, root, fileids, encoding) + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._readme = "README.txt" + + def comparisons(self, fileids=None): + """ + Return all comparisons in the corpus. + + :param fileids: a list or regexp specifying the ids of the files whose + comparisons have to be returned. + :return: the given file(s) as a list of Comparison objects. + :rtype: list(Comparison) + """ + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_comparison_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def keywords(self, fileids=None): + """ + Return a set of all keywords used in the corpus. + + :param fileids: a list or regexp specifying the ids of the files whose + keywords have to be returned. + :return: the set of keywords and comparative phrases used in the corpus. + :rtype: set(str) + """ + all_keywords = concat( + [ + self.CorpusView(path, self._read_keyword_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + keywords_set = {keyword.lower() for keyword in all_keywords if keyword} + return keywords_set + + def keywords_readme(self): + """ + Return the list of words and constituents considered as clues of a + comparison (from listOfkeywords.txt). + """ + keywords = [] + with self.open("listOfkeywords.txt") as fp: + raw_text = fp.read() + for line in raw_text.split("\n"): + if not line or line.startswith("//"): + continue + keywords.append(line.strip()) + return keywords + + def sents(self, fileids=None): + """ + Return all sentences in the corpus. + + :param fileids: a list or regexp specifying the ids of the files whose + sentences have to be returned. + :return: all sentences of the corpus as lists of tokens (or as plain + strings, if no word tokenizer is specified). + :rtype: list(list(str)) or list(str) + """ + return concat( + [ + self.CorpusView(path, self._read_sent_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def words(self, fileids=None): + """ + Return all words and punctuation symbols in the corpus. + + :param fileids: a list or regexp specifying the ids of the files whose + words have to be returned. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def _read_comparison_block(self, stream): + while True: + line = stream.readline() + if not line: + return [] # end of file. + comparison_tags = re.findall(COMPARISON, line) + if comparison_tags: + grad_comparisons = re.findall(GRAD_COMPARISON, line) + non_grad_comparisons = re.findall(NON_GRAD_COMPARISON, line) + # Advance to the next line (it contains the comparative sentence) + comparison_text = stream.readline().strip() + if self._word_tokenizer: + comparison_text = self._word_tokenizer.tokenize(comparison_text) + # Skip the next line (it contains closing comparison tags) + stream.readline() + # If gradable comparisons are found, create Comparison instances + # and populate their fields + comparison_bundle = [] + if grad_comparisons: + # Each comparison tag has its own relations on a separate line + for comp in grad_comparisons: + comp_type = int(re.match(r"", comp).group(1)) + comparison = Comparison( + text=comparison_text, comp_type=comp_type + ) + line = stream.readline() + entities_feats = ENTITIES_FEATS.findall(line) + if entities_feats: + for (code, entity_feat) in entities_feats: + if code == "1": + comparison.entity_1 = entity_feat.strip() + elif code == "2": + comparison.entity_2 = entity_feat.strip() + elif code == "3": + comparison.feature = entity_feat.strip() + keyword = KEYWORD.findall(line) + if keyword: + comparison.keyword = keyword[0] + comparison_bundle.append(comparison) + # If non-gradable comparisons are found, create a simple Comparison + # instance for each one + if non_grad_comparisons: + for comp in non_grad_comparisons: + # comp_type in this case should always be 4. + comp_type = int(re.match(r"", comp).group(1)) + comparison = Comparison( + text=comparison_text, comp_type=comp_type + ) + comparison_bundle.append(comparison) + # Flatten the list of comparisons before returning them + # return concat([comparison_bundle]) + return comparison_bundle + + def _read_keyword_block(self, stream): + keywords = [] + for comparison in self._read_comparison_block(stream): + keywords.append(comparison.keyword) + return keywords + + def _read_sent_block(self, stream): + while True: + line = stream.readline() + if re.match(STARS, line): + while True: + line = stream.readline() + if re.match(STARS, line): + break + continue + if ( + not re.findall(COMPARISON, line) + and not ENTITIES_FEATS.findall(line) + and not re.findall(CLOSE_COMPARISON, line) + ): + if self._sent_tokenizer: + return [ + self._word_tokenizer.tokenize(sent) + for sent in self._sent_tokenizer.tokenize(line) + ] + else: + return [self._word_tokenizer.tokenize(line)] + + def _read_word_block(self, stream): + words = [] + for sent in self._read_sent_block(stream): + words.extend(sent) + return words diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/conll.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/conll.py new file mode 100644 index 0000000..1cd1e40 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/conll.py @@ -0,0 +1,579 @@ +# Natural Language Toolkit: CONLL Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Read CoNLL-style chunk fileids. +""" + +import textwrap + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tag import map_tag +from nltk.tree import Tree +from nltk.util import LazyConcatenation, LazyMap + + +class ConllCorpusReader(CorpusReader): + """ + A corpus reader for CoNLL-style files. These files consist of a + series of sentences, separated by blank lines. Each sentence is + encoded using a table (or "grid") of values, where each line + corresponds to a single word, and each column corresponds to an + annotation type. The set of columns used by CoNLL-style files can + vary from corpus to corpus; the ``ConllCorpusReader`` constructor + therefore takes an argument, ``columntypes``, which is used to + specify the columns that are used by a given corpus. By default + columns are split by consecutive whitespaces, with the + ``separator`` argument you can set a string to split by (e.g. + ``\'\t\'``). + + + @todo: Add support for reading from corpora where different + parallel files contain different columns. + @todo: Possibly add caching of the grid corpus view? This would + allow the same grid view to be used by different data access + methods (eg words() and parsed_sents() could both share the + same grid corpus view object). + @todo: Better support for -DOCSTART-. Currently, we just ignore + it, but it could be used to define methods that retrieve a + document at a time (eg parsed_documents()). + """ + + # ///////////////////////////////////////////////////////////////// + # Column Types + # ///////////////////////////////////////////////////////////////// + + WORDS = "words" #: column type for words + POS = "pos" #: column type for part-of-speech tags + TREE = "tree" #: column type for parse trees + CHUNK = "chunk" #: column type for chunk structures + NE = "ne" #: column type for named entities + SRL = "srl" #: column type for semantic role labels + IGNORE = "ignore" #: column type for column that should be ignored + + #: A list of all column types supported by the conll corpus reader. + COLUMN_TYPES = (WORDS, POS, TREE, CHUNK, NE, SRL, IGNORE) + + # ///////////////////////////////////////////////////////////////// + # Constructor + # ///////////////////////////////////////////////////////////////// + + def __init__( + self, + root, + fileids, + columntypes, + chunk_types=None, + root_label="S", + pos_in_tree=False, + srl_includes_roleset=True, + encoding="utf8", + tree_class=Tree, + tagset=None, + separator=None, + ): + for columntype in columntypes: + if columntype not in self.COLUMN_TYPES: + raise ValueError("Bad column type %r" % columntype) + if isinstance(chunk_types, str): + chunk_types = [chunk_types] + self._chunk_types = chunk_types + self._colmap = {c: i for (i, c) in enumerate(columntypes)} + self._pos_in_tree = pos_in_tree + self._root_label = root_label # for chunks + self._srl_includes_roleset = srl_includes_roleset + self._tree_class = tree_class + CorpusReader.__init__(self, root, fileids, encoding) + self._tagset = tagset + self.sep = separator + + # ///////////////////////////////////////////////////////////////// + # Data Access Methods + # ///////////////////////////////////////////////////////////////// + + def words(self, fileids=None): + self._require(self.WORDS) + return LazyConcatenation(LazyMap(self._get_words, self._grids(fileids))) + + def sents(self, fileids=None): + self._require(self.WORDS) + return LazyMap(self._get_words, self._grids(fileids)) + + def tagged_words(self, fileids=None, tagset=None): + self._require(self.WORDS, self.POS) + + def get_tagged_words(grid): + return self._get_tagged_words(grid, tagset) + + return LazyConcatenation(LazyMap(get_tagged_words, self._grids(fileids))) + + def tagged_sents(self, fileids=None, tagset=None): + self._require(self.WORDS, self.POS) + + def get_tagged_words(grid): + return self._get_tagged_words(grid, tagset) + + return LazyMap(get_tagged_words, self._grids(fileids)) + + def chunked_words(self, fileids=None, chunk_types=None, tagset=None): + self._require(self.WORDS, self.POS, self.CHUNK) + if chunk_types is None: + chunk_types = self._chunk_types + + def get_chunked_words(grid): # capture chunk_types as local var + return self._get_chunked_words(grid, chunk_types, tagset) + + return LazyConcatenation(LazyMap(get_chunked_words, self._grids(fileids))) + + def chunked_sents(self, fileids=None, chunk_types=None, tagset=None): + self._require(self.WORDS, self.POS, self.CHUNK) + if chunk_types is None: + chunk_types = self._chunk_types + + def get_chunked_words(grid): # capture chunk_types as local var + return self._get_chunked_words(grid, chunk_types, tagset) + + return LazyMap(get_chunked_words, self._grids(fileids)) + + def parsed_sents(self, fileids=None, pos_in_tree=None, tagset=None): + self._require(self.WORDS, self.POS, self.TREE) + if pos_in_tree is None: + pos_in_tree = self._pos_in_tree + + def get_parsed_sent(grid): # capture pos_in_tree as local var + return self._get_parsed_sent(grid, pos_in_tree, tagset) + + return LazyMap(get_parsed_sent, self._grids(fileids)) + + def srl_spans(self, fileids=None): + self._require(self.SRL) + return LazyMap(self._get_srl_spans, self._grids(fileids)) + + def srl_instances(self, fileids=None, pos_in_tree=None, flatten=True): + self._require(self.WORDS, self.POS, self.TREE, self.SRL) + if pos_in_tree is None: + pos_in_tree = self._pos_in_tree + + def get_srl_instances(grid): # capture pos_in_tree as local var + return self._get_srl_instances(grid, pos_in_tree) + + result = LazyMap(get_srl_instances, self._grids(fileids)) + if flatten: + result = LazyConcatenation(result) + return result + + def iob_words(self, fileids=None, tagset=None): + """ + :return: a list of word/tag/IOB tuples + :rtype: list(tuple) + :param fileids: the list of fileids that make up this corpus + :type fileids: None or str or list + """ + self._require(self.WORDS, self.POS, self.CHUNK) + + def get_iob_words(grid): + return self._get_iob_words(grid, tagset) + + return LazyConcatenation(LazyMap(get_iob_words, self._grids(fileids))) + + def iob_sents(self, fileids=None, tagset=None): + """ + :return: a list of lists of word/tag/IOB tuples + :rtype: list(list) + :param fileids: the list of fileids that make up this corpus + :type fileids: None or str or list + """ + self._require(self.WORDS, self.POS, self.CHUNK) + + def get_iob_words(grid): + return self._get_iob_words(grid, tagset) + + return LazyMap(get_iob_words, self._grids(fileids)) + + # ///////////////////////////////////////////////////////////////// + # Grid Reading + # ///////////////////////////////////////////////////////////////// + + def _grids(self, fileids=None): + # n.b.: we could cache the object returned here (keyed on + # fileids), which would let us reuse the same corpus view for + # different things (eg srl and parse trees). + return concat( + [ + StreamBackedCorpusView(fileid, self._read_grid_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def _read_grid_block(self, stream): + grids = [] + for block in read_blankline_block(stream): + block = block.strip() + if not block: + continue + + grid = [line.split(self.sep) for line in block.split("\n")] + + # If there's a docstart row, then discard. ([xx] eventually it + # would be good to actually use it) + if grid[0][self._colmap.get("words", 0)] == "-DOCSTART-": + del grid[0] + + # Check that the grid is consistent. + for row in grid: + if len(row) != len(grid[0]): + raise ValueError("Inconsistent number of columns:\n%s" % block) + grids.append(grid) + return grids + + # ///////////////////////////////////////////////////////////////// + # Transforms + # ///////////////////////////////////////////////////////////////// + # given a grid, transform it into some representation (e.g., + # a list of words or a parse tree). + + def _get_words(self, grid): + return self._get_column(grid, self._colmap["words"]) + + def _get_tagged_words(self, grid, tagset=None): + pos_tags = self._get_column(grid, self._colmap["pos"]) + if tagset and tagset != self._tagset: + pos_tags = [map_tag(self._tagset, tagset, t) for t in pos_tags] + return list(zip(self._get_column(grid, self._colmap["words"]), pos_tags)) + + def _get_iob_words(self, grid, tagset=None): + pos_tags = self._get_column(grid, self._colmap["pos"]) + if tagset and tagset != self._tagset: + pos_tags = [map_tag(self._tagset, tagset, t) for t in pos_tags] + return list( + zip( + self._get_column(grid, self._colmap["words"]), + pos_tags, + self._get_column(grid, self._colmap["chunk"]), + ) + ) + + def _get_chunked_words(self, grid, chunk_types, tagset=None): + # n.b.: this method is very similar to conllstr2tree. + words = self._get_column(grid, self._colmap["words"]) + pos_tags = self._get_column(grid, self._colmap["pos"]) + if tagset and tagset != self._tagset: + pos_tags = [map_tag(self._tagset, tagset, t) for t in pos_tags] + chunk_tags = self._get_column(grid, self._colmap["chunk"]) + + stack = [Tree(self._root_label, [])] + + for (word, pos_tag, chunk_tag) in zip(words, pos_tags, chunk_tags): + if chunk_tag == "O": + state, chunk_type = "O", "" + else: + (state, chunk_type) = chunk_tag.split("-") + # If it's a chunk we don't care about, treat it as O. + if chunk_types is not None and chunk_type not in chunk_types: + state = "O" + # Treat a mismatching I like a B. + if state == "I" and chunk_type != stack[-1].label(): + state = "B" + # For B or I: close any open chunks + if state in "BO" and len(stack) == 2: + stack.pop() + # For B: start a new chunk. + if state == "B": + new_chunk = Tree(chunk_type, []) + stack[-1].append(new_chunk) + stack.append(new_chunk) + # Add the word token. + stack[-1].append((word, pos_tag)) + + return stack[0] + + def _get_parsed_sent(self, grid, pos_in_tree, tagset=None): + words = self._get_column(grid, self._colmap["words"]) + pos_tags = self._get_column(grid, self._colmap["pos"]) + if tagset and tagset != self._tagset: + pos_tags = [map_tag(self._tagset, tagset, t) for t in pos_tags] + parse_tags = self._get_column(grid, self._colmap["tree"]) + + treestr = "" + for (word, pos_tag, parse_tag) in zip(words, pos_tags, parse_tags): + if word == "(": + word = "-LRB-" + if word == ")": + word = "-RRB-" + if pos_tag == "(": + pos_tag = "-LRB-" + if pos_tag == ")": + pos_tag = "-RRB-" + (left, right) = parse_tag.split("*") + right = right.count(")") * ")" # only keep ')'. + treestr += f"{left} ({pos_tag} {word}) {right}" + try: + tree = self._tree_class.fromstring(treestr) + except (ValueError, IndexError): + tree = self._tree_class.fromstring(f"({self._root_label} {treestr})") + + if not pos_in_tree: + for subtree in tree.subtrees(): + for i, child in enumerate(subtree): + if ( + isinstance(child, Tree) + and len(child) == 1 + and isinstance(child[0], str) + ): + subtree[i] = (child[0], child.label()) + + return tree + + def _get_srl_spans(self, grid): + """ + list of list of (start, end), tag) tuples + """ + if self._srl_includes_roleset: + predicates = self._get_column(grid, self._colmap["srl"] + 1) + start_col = self._colmap["srl"] + 2 + else: + predicates = self._get_column(grid, self._colmap["srl"]) + start_col = self._colmap["srl"] + 1 + + # Count how many predicates there are. This tells us how many + # columns to expect for SRL data. + num_preds = len([p for p in predicates if p != "-"]) + + spanlists = [] + for i in range(num_preds): + col = self._get_column(grid, start_col + i) + spanlist = [] + stack = [] + for wordnum, srl_tag in enumerate(col): + (left, right) = srl_tag.split("*") + for tag in left.split("("): + if tag: + stack.append((tag, wordnum)) + for i in range(right.count(")")): + (tag, start) = stack.pop() + spanlist.append(((start, wordnum + 1), tag)) + spanlists.append(spanlist) + + return spanlists + + def _get_srl_instances(self, grid, pos_in_tree): + tree = self._get_parsed_sent(grid, pos_in_tree) + spanlists = self._get_srl_spans(grid) + if self._srl_includes_roleset: + predicates = self._get_column(grid, self._colmap["srl"] + 1) + rolesets = self._get_column(grid, self._colmap["srl"]) + else: + predicates = self._get_column(grid, self._colmap["srl"]) + rolesets = [None] * len(predicates) + + instances = ConllSRLInstanceList(tree) + for wordnum, predicate in enumerate(predicates): + if predicate == "-": + continue + # Decide which spanlist to use. Don't assume that they're + # sorted in the same order as the predicates (even though + # they usually are). + for spanlist in spanlists: + for (start, end), tag in spanlist: + if wordnum in range(start, end) and tag in ("V", "C-V"): + break + else: + continue + break + else: + raise ValueError("No srl column found for %r" % predicate) + instances.append( + ConllSRLInstance(tree, wordnum, predicate, rolesets[wordnum], spanlist) + ) + + return instances + + # ///////////////////////////////////////////////////////////////// + # Helper Methods + # ///////////////////////////////////////////////////////////////// + + def _require(self, *columntypes): + for columntype in columntypes: + if columntype not in self._colmap: + raise ValueError( + "This corpus does not contain a %s " "column." % columntype + ) + + @staticmethod + def _get_column(grid, column_index): + return [grid[i][column_index] for i in range(len(grid))] + + +class ConllSRLInstance: + """ + An SRL instance from a CoNLL corpus, which identifies and + providing labels for the arguments of a single verb. + """ + + # [xx] add inst.core_arguments, inst.argm_arguments? + + def __init__(self, tree, verb_head, verb_stem, roleset, tagged_spans): + self.verb = [] + """A list of the word indices of the words that compose the + verb whose arguments are identified by this instance. + This will contain multiple word indices when multi-word + verbs are used (e.g. 'turn on').""" + + self.verb_head = verb_head + """The word index of the head word of the verb whose arguments + are identified by this instance. E.g., for a sentence that + uses the verb 'turn on,' ``verb_head`` will be the word index + of the word 'turn'.""" + + self.verb_stem = verb_stem + + self.roleset = roleset + + self.arguments = [] + """A list of ``(argspan, argid)`` tuples, specifying the location + and type for each of the arguments identified by this + instance. ``argspan`` is a tuple ``start, end``, indicating + that the argument consists of the ``words[start:end]``.""" + + self.tagged_spans = tagged_spans + """A list of ``(span, id)`` tuples, specifying the location and + type for each of the arguments, as well as the verb pieces, + that make up this instance.""" + + self.tree = tree + """The parse tree for the sentence containing this instance.""" + + self.words = tree.leaves() + """A list of the words in the sentence containing this + instance.""" + + # Fill in the self.verb and self.arguments values. + for (start, end), tag in tagged_spans: + if tag in ("V", "C-V"): + self.verb += list(range(start, end)) + else: + self.arguments.append(((start, end), tag)) + + def __repr__(self): + # Originally, its: + ##plural = 's' if len(self.arguments) != 1 else '' + plural = "s" if len(self.arguments) != 1 else "" + return "" % ( + (self.verb_stem, len(self.arguments), plural) + ) + + def pprint(self): + verbstr = " ".join(self.words[i][0] for i in self.verb) + hdr = f"SRL for {verbstr!r} (stem={self.verb_stem!r}):\n" + s = "" + for i, word in enumerate(self.words): + if isinstance(word, tuple): + word = word[0] + for (start, end), argid in self.arguments: + if i == start: + s += "[%s " % argid + if i == end: + s += "] " + if i in self.verb: + word = "<<%s>>" % word + s += word + " " + return hdr + textwrap.fill( + s.replace(" ]", "]"), initial_indent=" ", subsequent_indent=" " + ) + + +class ConllSRLInstanceList(list): + """ + Set of instances for a single sentence + """ + + def __init__(self, tree, instances=()): + self.tree = tree + list.__init__(self, instances) + + def __str__(self): + return self.pprint() + + def pprint(self, include_tree=False): + # Sanity check: trees should be the same + for inst in self: + if inst.tree != self.tree: + raise ValueError("Tree mismatch!") + + # If desired, add trees: + if include_tree: + words = self.tree.leaves() + pos = [None] * len(words) + synt = ["*"] * len(words) + self._tree2conll(self.tree, 0, words, pos, synt) + + s = "" + for i in range(len(words)): + # optional tree columns + if include_tree: + s += "%-20s " % words[i] + s += "%-8s " % pos[i] + s += "%15s*%-8s " % tuple(synt[i].split("*")) + + # verb head column + for inst in self: + if i == inst.verb_head: + s += "%-20s " % inst.verb_stem + break + else: + s += "%-20s " % "-" + # Remaining columns: self + for inst in self: + argstr = "*" + for (start, end), argid in inst.tagged_spans: + if i == start: + argstr = f"({argid}{argstr}" + if i == (end - 1): + argstr += ")" + s += "%-12s " % argstr + s += "\n" + return s + + def _tree2conll(self, tree, wordnum, words, pos, synt): + assert isinstance(tree, Tree) + if len(tree) == 1 and isinstance(tree[0], str): + pos[wordnum] = tree.label() + assert words[wordnum] == tree[0] + return wordnum + 1 + elif len(tree) == 1 and isinstance(tree[0], tuple): + assert len(tree[0]) == 2 + pos[wordnum], pos[wordnum] = tree[0] + return wordnum + 1 + else: + synt[wordnum] = f"({tree.label()}{synt[wordnum]}" + for child in tree: + wordnum = self._tree2conll(child, wordnum, words, pos, synt) + synt[wordnum - 1] += ")" + return wordnum + + +class ConllChunkCorpusReader(ConllCorpusReader): + """ + A ConllCorpusReader whose data file contains three columns: words, + pos, and chunk. + """ + + def __init__( + self, root, fileids, chunk_types, encoding="utf8", tagset=None, separator=None + ): + ConllCorpusReader.__init__( + self, + root, + fileids, + ("words", "pos", "chunk"), + chunk_types=chunk_types, + encoding=encoding, + tagset=tagset, + separator=separator, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/crubadan.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/crubadan.py new file mode 100644 index 0000000..64638ad --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/crubadan.py @@ -0,0 +1,106 @@ +# Natural Language Toolkit: An Crubadan N-grams Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Avital Pekker +# +# URL: +# For license information, see LICENSE.TXT + +""" +An NLTK interface for the n-gram statistics gathered from +the corpora for each language using An Crubadan. + +There are multiple potential applications for the data but +this reader was created with the goal of using it in the +context of language identification. + +For details about An Crubadan, this data, and its potential uses, see: +http://borel.slu.edu/crubadan/index.html +""" + +import re +from os import path + +from nltk.corpus.reader import CorpusReader +from nltk.data import ZipFilePathPointer +from nltk.probability import FreqDist + + +class CrubadanCorpusReader(CorpusReader): + """ + A corpus reader used to access language An Crubadan n-gram files. + """ + + _LANG_MAPPER_FILE = "table.txt" + _all_lang_freq = {} + + def __init__(self, root, fileids, encoding="utf8", tagset=None): + super().__init__(root, fileids, encoding="utf8") + self._lang_mapping_data = [] + self._load_lang_mapping_data() + + def lang_freq(self, lang): + """Return n-gram FreqDist for a specific language + given ISO 639-3 language code""" + + if lang not in self._all_lang_freq: + self._all_lang_freq[lang] = self._load_lang_ngrams(lang) + + return self._all_lang_freq[lang] + + def langs(self): + """Return a list of supported languages as ISO 639-3 codes""" + return [row[1] for row in self._lang_mapping_data] + + def iso_to_crubadan(self, lang): + """Return internal Crubadan code based on ISO 639-3 code""" + for i in self._lang_mapping_data: + if i[1].lower() == lang.lower(): + return i[0] + + def crubadan_to_iso(self, lang): + """Return ISO 639-3 code given internal Crubadan code""" + for i in self._lang_mapping_data: + if i[0].lower() == lang.lower(): + return i[1] + + def _load_lang_mapping_data(self): + """Load language mappings between codes and description from table.txt""" + if isinstance(self.root, ZipFilePathPointer): + raise RuntimeError( + "Please install the 'crubadan' corpus first, use nltk.download()" + ) + + mapper_file = path.join(self.root, self._LANG_MAPPER_FILE) + if self._LANG_MAPPER_FILE not in self.fileids(): + raise RuntimeError("Could not find language mapper file: " + mapper_file) + + with open(mapper_file, encoding="utf-8") as raw: + strip_raw = raw.read().strip() + + self._lang_mapping_data = [row.split("\t") for row in strip_raw.split("\n")] + + def _load_lang_ngrams(self, lang): + """Load single n-gram language file given the ISO 639-3 language code + and return its FreqDist""" + + if lang not in self.langs(): + raise RuntimeError("Unsupported language.") + + crubadan_code = self.iso_to_crubadan(lang) + ngram_file = path.join(self.root, crubadan_code + "-3grams.txt") + + if not path.isfile(ngram_file): + raise RuntimeError("No N-gram file found for requested language.") + + counts = FreqDist() + with open(ngram_file, encoding="utf-8") as f: + for line in f: + data = line.split(" ") + + ngram = data[1].strip("\n") + freq = int(data[0]) + + counts[ngram] = freq + + return counts diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/dependency.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/dependency.py new file mode 100644 index 0000000..378fffc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/dependency.py @@ -0,0 +1,115 @@ +# Natural Language Toolkit: Dependency Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Kepa Sarasola +# Iker Manterola +# +# URL: +# For license information, see LICENSE.TXT + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.parse import DependencyGraph +from nltk.tokenize import * + + +class DependencyCorpusReader(SyntaxCorpusReader): + def __init__( + self, + root, + fileids, + encoding="utf8", + word_tokenizer=TabTokenizer(), + sent_tokenizer=RegexpTokenizer("\n", gaps=True), + para_block_reader=read_blankline_block, + ): + SyntaxCorpusReader.__init__(self, root, fileids, encoding) + + ######################################################### + + def words(self, fileids=None): + return concat( + [ + DependencyCorpusView(fileid, False, False, False, encoding=enc) + for fileid, enc in self.abspaths(fileids, include_encoding=True) + ] + ) + + def tagged_words(self, fileids=None): + return concat( + [ + DependencyCorpusView(fileid, True, False, False, encoding=enc) + for fileid, enc in self.abspaths(fileids, include_encoding=True) + ] + ) + + def sents(self, fileids=None): + return concat( + [ + DependencyCorpusView(fileid, False, True, False, encoding=enc) + for fileid, enc in self.abspaths(fileids, include_encoding=True) + ] + ) + + def tagged_sents(self, fileids=None): + return concat( + [ + DependencyCorpusView(fileid, True, True, False, encoding=enc) + for fileid, enc in self.abspaths(fileids, include_encoding=True) + ] + ) + + def parsed_sents(self, fileids=None): + sents = concat( + [ + DependencyCorpusView(fileid, False, True, True, encoding=enc) + for fileid, enc in self.abspaths(fileids, include_encoding=True) + ] + ) + return [DependencyGraph(sent) for sent in sents] + + +class DependencyCorpusView(StreamBackedCorpusView): + _DOCSTART = "-DOCSTART- -DOCSTART- O\n" # dokumentu hasiera definitzen da + + def __init__( + self, + corpus_file, + tagged, + group_by_sent, + dependencies, + chunk_types=None, + encoding="utf8", + ): + self._tagged = tagged + self._dependencies = dependencies + self._group_by_sent = group_by_sent + self._chunk_types = chunk_types + StreamBackedCorpusView.__init__(self, corpus_file, encoding=encoding) + + def read_block(self, stream): + # Read the next sentence. + sent = read_blankline_block(stream)[0].strip() + # Strip off the docstart marker, if present. + if sent.startswith(self._DOCSTART): + sent = sent[len(self._DOCSTART) :].lstrip() + + # extract word and tag from any of the formats + if not self._dependencies: + lines = [line.split("\t") for line in sent.split("\n")] + if len(lines[0]) == 3 or len(lines[0]) == 4: + sent = [(line[0], line[1]) for line in lines] + elif len(lines[0]) == 10: + sent = [(line[1], line[4]) for line in lines] + else: + raise ValueError("Unexpected number of fields in dependency tree file") + + # discard tags if they weren't requested + if not self._tagged: + sent = [word for (word, tag) in sent] + + # Return the result. + if self._group_by_sent: + return [sent] + else: + return list(sent) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/framenet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/framenet.py new file mode 100644 index 0000000..16b3e93 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/framenet.py @@ -0,0 +1,3442 @@ +# Natural Language Toolkit: Framenet Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Chuck Wooters , +# Nathan Schneider +# URL: +# For license information, see LICENSE.TXT + + +""" +Corpus reader for the FrameNet 1.7 lexicon and corpus. +""" + +import itertools +import os +import re +import sys +import textwrap +import types +from collections import OrderedDict, defaultdict +from itertools import zip_longest +from operator import itemgetter +from pprint import pprint + +from nltk.corpus.reader import XMLCorpusReader, XMLCorpusView +from nltk.util import LazyConcatenation, LazyIteratorList, LazyMap + +__docformat__ = "epytext en" + + +def mimic_wrap(lines, wrap_at=65, **kwargs): + """ + Wrap the first of 'lines' with textwrap and the remaining lines at exactly the same + positions as the first. + """ + l0 = textwrap.fill(lines[0], wrap_at, drop_whitespace=False).split("\n") + yield l0 + + def _(line): + il0 = 0 + while line and il0 < len(l0) - 1: + yield line[: len(l0[il0])] + line = line[len(l0[il0]) :] + il0 += 1 + if line: # Remaining stuff on this line past the end of the mimicked line. + # So just textwrap this line. + yield from textwrap.fill(line, wrap_at, drop_whitespace=False).split("\n") + + for l in lines[1:]: + yield list(_(l)) + + +def _pretty_longstring(defstr, prefix="", wrap_at=65): + + """ + Helper function for pretty-printing a long string. + + :param defstr: The string to be printed. + :type defstr: str + :return: A nicely formatted string representation of the long string. + :rtype: str + """ + + outstr = "" + for line in textwrap.fill(defstr, wrap_at).split("\n"): + outstr += prefix + line + "\n" + return outstr + + +def _pretty_any(obj): + + """ + Helper function for pretty-printing any AttrDict object. + + :param obj: The obj to be printed. + :type obj: AttrDict + :return: A nicely formatted string representation of the AttrDict object. + :rtype: str + """ + + outstr = "" + for k in obj: + if isinstance(obj[k], str) and len(obj[k]) > 65: + outstr += f"[{k}]\n" + outstr += "{}".format(_pretty_longstring(obj[k], prefix=" ")) + outstr += "\n" + else: + outstr += f"[{k}] {obj[k]}\n" + + return outstr + + +def _pretty_semtype(st): + + """ + Helper function for pretty-printing a semantic type. + + :param st: The semantic type to be printed. + :type st: AttrDict + :return: A nicely formatted string representation of the semantic type. + :rtype: str + """ + + semkeys = st.keys() + if len(semkeys) == 1: + return "" + + outstr = "" + outstr += "semantic type ({0.ID}): {0.name}\n".format(st) + if "abbrev" in semkeys: + outstr += f"[abbrev] {st.abbrev}\n" + if "definition" in semkeys: + outstr += "[definition]\n" + outstr += _pretty_longstring(st.definition, " ") + outstr += f"[rootType] {st.rootType.name}({st.rootType.ID})\n" + if st.superType is None: + outstr += "[superType] \n" + else: + outstr += f"[superType] {st.superType.name}({st.superType.ID})\n" + outstr += f"[subTypes] {len(st.subTypes)} subtypes\n" + outstr += ( + " " + + ", ".join(f"{x.name}({x.ID})" for x in st.subTypes) + + "\n" * (len(st.subTypes) > 0) + ) + return outstr + + +def _pretty_frame_relation_type(freltyp): + + """ + Helper function for pretty-printing a frame relation type. + + :param freltyp: The frame relation type to be printed. + :type freltyp: AttrDict + :return: A nicely formatted string representation of the frame relation type. + :rtype: str + """ + outstr = " {0.subFrameName}>".format( + freltyp + ) + return outstr + + +def _pretty_frame_relation(frel): + + """ + Helper function for pretty-printing a frame relation. + + :param frel: The frame relation to be printed. + :type frel: AttrDict + :return: A nicely formatted string representation of the frame relation. + :rtype: str + """ + outstr = "<{0.type.superFrameName}={0.superFrameName} -- {0.type.name} -> {0.type.subFrameName}={0.subFrameName}>".format( + frel + ) + return outstr + + +def _pretty_fe_relation(ferel): + + """ + Helper function for pretty-printing an FE relation. + + :param ferel: The FE relation to be printed. + :type ferel: AttrDict + :return: A nicely formatted string representation of the FE relation. + :rtype: str + """ + outstr = "<{0.type.superFrameName}={0.frameRelation.superFrameName}.{0.superFEName} -- {0.type.name} -> {0.type.subFrameName}={0.frameRelation.subFrameName}.{0.subFEName}>".format( + ferel + ) + return outstr + + +def _pretty_lu(lu): + + """ + Helper function for pretty-printing a lexical unit. + + :param lu: The lu to be printed. + :type lu: AttrDict + :return: A nicely formatted string representation of the lexical unit. + :rtype: str + """ + + lukeys = lu.keys() + outstr = "" + outstr += "lexical unit ({0.ID}): {0.name}\n\n".format(lu) + if "definition" in lukeys: + outstr += "[definition]\n" + outstr += _pretty_longstring(lu.definition, " ") + if "frame" in lukeys: + outstr += f"\n[frame] {lu.frame.name}({lu.frame.ID})\n" + if "incorporatedFE" in lukeys: + outstr += f"\n[incorporatedFE] {lu.incorporatedFE}\n" + if "POS" in lukeys: + outstr += f"\n[POS] {lu.POS}\n" + if "status" in lukeys: + outstr += f"\n[status] {lu.status}\n" + if "totalAnnotated" in lukeys: + outstr += f"\n[totalAnnotated] {lu.totalAnnotated} annotated examples\n" + if "lexemes" in lukeys: + outstr += "\n[lexemes] {}\n".format( + " ".join(f"{lex.name}/{lex.POS}" for lex in lu.lexemes) + ) + if "semTypes" in lukeys: + outstr += f"\n[semTypes] {len(lu.semTypes)} semantic types\n" + outstr += ( + " " * (len(lu.semTypes) > 0) + + ", ".join(f"{x.name}({x.ID})" for x in lu.semTypes) + + "\n" * (len(lu.semTypes) > 0) + ) + if "URL" in lukeys: + outstr += f"\n[URL] {lu.URL}\n" + if "subCorpus" in lukeys: + subc = [x.name for x in lu.subCorpus] + outstr += f"\n[subCorpus] {len(lu.subCorpus)} subcorpora\n" + for line in textwrap.fill(", ".join(sorted(subc)), 60).split("\n"): + outstr += f" {line}\n" + if "exemplars" in lukeys: + outstr += "\n[exemplars] {} sentences across all subcorpora\n".format( + len(lu.exemplars) + ) + + return outstr + + +def _pretty_exemplars(exemplars, lu): + """ + Helper function for pretty-printing a list of exemplar sentences for a lexical unit. + + :param sent: The list of exemplar sentences to be printed. + :type sent: list(AttrDict) + :return: An index of the text of the exemplar sentences. + :rtype: str + """ + + outstr = "" + outstr += "exemplar sentences for {0.name} in {0.frame.name}:\n\n".format(lu) + for i, sent in enumerate(exemplars): + outstr += f"[{i}] {sent.text}\n" + outstr += "\n" + return outstr + + +def _pretty_fulltext_sentences(sents): + """ + Helper function for pretty-printing a list of annotated sentences for a full-text document. + + :param sent: The list of sentences to be printed. + :type sent: list(AttrDict) + :return: An index of the text of the sentences. + :rtype: str + """ + + outstr = "" + outstr += "full-text document ({0.ID}) {0.name}:\n\n".format(sents) + outstr += "[corpid] {0.corpid}\n[corpname] {0.corpname}\n[description] {0.description}\n[URL] {0.URL}\n\n".format( + sents + ) + outstr += f"[sentence]\n" + for i, sent in enumerate(sents.sentence): + outstr += f"[{i}] {sent.text}\n" + outstr += "\n" + return outstr + + +def _pretty_fulltext_sentence(sent): + """ + Helper function for pretty-printing an annotated sentence from a full-text document. + + :param sent: The sentence to be printed. + :type sent: list(AttrDict) + :return: The text of the sentence with annotation set indices on frame targets. + :rtype: str + """ + + outstr = "" + outstr += "full-text sentence ({0.ID}) in {1}:\n\n".format( + sent, sent.doc.get("name", sent.doc.description) + ) + outstr += f"\n[POS] {len(sent.POS)} tags\n" + outstr += f"\n[POS_tagset] {sent.POS_tagset}\n\n" + outstr += "[text] + [annotationSet]\n\n" + outstr += sent._ascii() # -> _annotation_ascii() + outstr += "\n" + return outstr + + +def _pretty_pos(aset): + """ + Helper function for pretty-printing a sentence with its POS tags. + + :param aset: The POS annotation set of the sentence to be printed. + :type sent: list(AttrDict) + :return: The text of the sentence and its POS tags. + :rtype: str + """ + + outstr = "" + outstr += "POS annotation set ({0.ID}) {0.POS_tagset} in sentence {0.sent.ID}:\n\n".format( + aset + ) + + # list the target spans and their associated aset index + overt = sorted(aset.POS) + + sent = aset.sent + s0 = sent.text + s1 = "" + s2 = "" + i = 0 + adjust = 0 + for j, k, lbl in overt: + assert j >= i, ("Overlapping targets?", (j, k, lbl)) + s1 += " " * (j - i) + "-" * (k - j) + if len(lbl) > (k - j): + # add space in the sentence to make room for the annotation index + amt = len(lbl) - (k - j) + s0 = ( + s0[: k + adjust] + "~" * amt + s0[k + adjust :] + ) # '~' to prevent line wrapping + s1 = s1[: k + adjust] + " " * amt + s1[k + adjust :] + adjust += amt + s2 += " " * (j - i) + lbl.ljust(k - j) + i = k + + long_lines = [s0, s1, s2] + + outstr += "\n\n".join( + map("\n".join, zip_longest(*mimic_wrap(long_lines), fillvalue=" ")) + ).replace("~", " ") + outstr += "\n" + return outstr + + +def _pretty_annotation(sent, aset_level=False): + """ + Helper function for pretty-printing an exemplar sentence for a lexical unit. + + :param sent: An annotation set or exemplar sentence to be printed. + :param aset_level: If True, 'sent' is actually an annotation set within a sentence. + :type sent: AttrDict + :return: A nicely formatted string representation of the exemplar sentence + with its target, frame, and FE annotations. + :rtype: str + """ + + sentkeys = sent.keys() + outstr = "annotation set" if aset_level else "exemplar sentence" + outstr += f" ({sent.ID}):\n" + if aset_level: # TODO: any UNANN exemplars? + outstr += f"\n[status] {sent.status}\n" + for k in ("corpID", "docID", "paragNo", "sentNo", "aPos"): + if k in sentkeys: + outstr += f"[{k}] {sent[k]}\n" + outstr += ( + "\n[LU] ({0.ID}) {0.name} in {0.frame.name}\n".format(sent.LU) + if sent.LU + else "\n[LU] Not found!" + ) + outstr += "\n[frame] ({0.ID}) {0.name}\n".format( + sent.frame + ) # redundant with above, but .frame is convenient + if not aset_level: + outstr += "\n[annotationSet] {} annotation sets\n".format( + len(sent.annotationSet) + ) + outstr += f"\n[POS] {len(sent.POS)} tags\n" + outstr += f"\n[POS_tagset] {sent.POS_tagset}\n" + outstr += "\n[GF] {} relation{}\n".format( + len(sent.GF), "s" if len(sent.GF) != 1 else "" + ) + outstr += "\n[PT] {} phrase{}\n".format( + len(sent.PT), "s" if len(sent.PT) != 1 else "" + ) + """ + Special Layers + -------------- + + The 'NER' layer contains, for some of the data, named entity labels. + + The 'WSL' (word status layer) contains, for some of the data, + spans which should not in principle be considered targets (NT). + + The 'Other' layer records relative clause constructions (Rel=relativizer, Ant=antecedent), + pleonastic 'it' (Null), and existential 'there' (Exist). + On occasion they are duplicated by accident (e.g., annotationSet 1467275 in lu6700.xml). + + The 'Sent' layer appears to contain labels that the annotator has flagged the + sentence with for their convenience: values include + 'sense1', 'sense2', 'sense3', etc.; + 'Blend', 'Canonical', 'Idiom', 'Metaphor', 'Special-Sent', + 'keepS', 'deleteS', 'reexamine' + (sometimes they are duplicated for no apparent reason). + + The POS-specific layers may contain the following kinds of spans: + Asp (aspectual particle), Non-Asp (non-aspectual particle), + Cop (copula), Supp (support), Ctrlr (controller), + Gov (governor), X. Gov and X always cooccur. + + >>> from nltk.corpus import framenet as fn + >>> def f(luRE, lyr, ignore=set()): + ... for i,ex in enumerate(fn.exemplars(luRE)): + ... if lyr in ex and ex[lyr] and set(zip(*ex[lyr])[2]) - ignore: + ... print(i,ex[lyr]) + + - Verb: Asp, Non-Asp + - Noun: Cop, Supp, Ctrlr, Gov, X + - Adj: Cop, Supp, Ctrlr, Gov, X + - Prep: Cop, Supp, Ctrlr + - Adv: Ctrlr + - Scon: (none) + - Art: (none) + """ + for lyr in ("NER", "WSL", "Other", "Sent"): + if lyr in sent and sent[lyr]: + outstr += "\n[{}] {} entr{}\n".format( + lyr, len(sent[lyr]), "ies" if len(sent[lyr]) != 1 else "y" + ) + outstr += "\n[text] + [Target] + [FE]" + # POS-specific layers: syntactically important words that are neither the target + # nor the FEs. Include these along with the first FE layer but with '^' underlining. + for lyr in ("Verb", "Noun", "Adj", "Adv", "Prep", "Scon", "Art"): + if lyr in sent and sent[lyr]: + outstr += f" + [{lyr}]" + if "FE2" in sentkeys: + outstr += " + [FE2]" + if "FE3" in sentkeys: + outstr += " + [FE3]" + outstr += "\n\n" + outstr += sent._ascii() # -> _annotation_ascii() + outstr += "\n" + + return outstr + + +def _annotation_ascii(sent): + """ + Given a sentence or FE annotation set, construct the width-limited string showing + an ASCII visualization of the sentence's annotations, calling either + _annotation_ascii_frames() or _annotation_ascii_FEs() as appropriate. + This will be attached as a method to appropriate AttrDict instances + and called in the full pretty-printing of the instance. + """ + if sent._type == "fulltext_sentence" or ( + "annotationSet" in sent and len(sent.annotationSet) > 2 + ): + # a full-text sentence OR sentence with multiple targets. + # (multiple targets = >2 annotation sets, because the first annotation set is POS.) + return _annotation_ascii_frames(sent) + else: # an FE annotation set, or an LU sentence with 1 target + return _annotation_ascii_FEs(sent) + + +def _annotation_ascii_frames(sent): + """ + ASCII string rendering of the sentence along with its targets and frame names. + Called for all full-text sentences, as well as the few LU sentences with multiple + targets (e.g., fn.lu(6412).exemplars[82] has two want.v targets). + Line-wrapped to limit the display width. + """ + # list the target spans and their associated aset index + overt = [] + for a, aset in enumerate(sent.annotationSet[1:]): + for j, k in aset.Target: + indexS = f"[{a + 1}]" + if aset.status == "UNANN" or aset.LU.status == "Problem": + indexS += " " + if aset.status == "UNANN": + indexS += "!" # warning indicator that there is a frame annotation but no FE annotation + if aset.LU.status == "Problem": + indexS += "?" # warning indicator that there is a missing LU definition (because the LU has Problem status) + overt.append((j, k, aset.LU.frame.name, indexS)) + overt = sorted(overt) + + duplicates = set() + for o, (j, k, fname, asetIndex) in enumerate(overt): + if o > 0 and j <= overt[o - 1][1]: + # multiple annotation sets on the same target + # (e.g. due to a coordination construction or multiple annotators) + if ( + overt[o - 1][:2] == (j, k) and overt[o - 1][2] == fname + ): # same target, same frame + # splice indices together + combinedIndex = ( + overt[o - 1][3] + asetIndex + ) # e.g., '[1][2]', '[1]! [2]' + combinedIndex = combinedIndex.replace(" !", "! ").replace(" ?", "? ") + overt[o - 1] = overt[o - 1][:3] + (combinedIndex,) + duplicates.add(o) + else: # different frames, same or overlapping targets + s = sent.text + for j, k, fname, asetIndex in overt: + s += "\n" + asetIndex + " " + sent.text[j:k] + " :: " + fname + s += "\n(Unable to display sentence with targets marked inline due to overlap)" + return s + for o in reversed(sorted(duplicates)): + del overt[o] + + s0 = sent.text + s1 = "" + s11 = "" + s2 = "" + i = 0 + adjust = 0 + fAbbrevs = OrderedDict() + for j, k, fname, asetIndex in overt: + if not j >= i: + assert j >= i, ( + "Overlapping targets?" + + ( + " UNANN" + if any(aset.status == "UNANN" for aset in sent.annotationSet[1:]) + else "" + ), + (j, k, asetIndex), + ) + s1 += " " * (j - i) + "*" * (k - j) + short = fname[: k - j] + if (k - j) < len(fname): + r = 0 + while short in fAbbrevs: + if fAbbrevs[short] == fname: + break + r += 1 + short = fname[: k - j - 1] + str(r) + else: # short not in fAbbrevs + fAbbrevs[short] = fname + s11 += " " * (j - i) + short.ljust(k - j) + if len(asetIndex) > (k - j): + # add space in the sentence to make room for the annotation index + amt = len(asetIndex) - (k - j) + s0 = ( + s0[: k + adjust] + "~" * amt + s0[k + adjust :] + ) # '~' to prevent line wrapping + s1 = s1[: k + adjust] + " " * amt + s1[k + adjust :] + s11 = s11[: k + adjust] + " " * amt + s11[k + adjust :] + adjust += amt + s2 += " " * (j - i) + asetIndex.ljust(k - j) + i = k + + long_lines = [s0, s1, s11, s2] + + outstr = "\n\n".join( + map("\n".join, zip_longest(*mimic_wrap(long_lines), fillvalue=" ")) + ).replace("~", " ") + outstr += "\n" + if fAbbrevs: + outstr += " (" + ", ".join("=".join(pair) for pair in fAbbrevs.items()) + ")" + assert len(fAbbrevs) == len(dict(fAbbrevs)), "Abbreviation clash" + + return outstr + + +def _annotation_ascii_FE_layer(overt, ni, feAbbrevs): + """Helper for _annotation_ascii_FEs().""" + s1 = "" + s2 = "" + i = 0 + for j, k, fename in overt: + s1 += " " * (j - i) + ("^" if fename.islower() else "-") * (k - j) + short = fename[: k - j] + if len(fename) > len(short): + r = 0 + while short in feAbbrevs: + if feAbbrevs[short] == fename: + break + r += 1 + short = fename[: k - j - 1] + str(r) + else: # short not in feAbbrevs + feAbbrevs[short] = fename + s2 += " " * (j - i) + short.ljust(k - j) + i = k + + sNI = "" + if ni: + sNI += " [" + ", ".join(":".join(x) for x in sorted(ni.items())) + "]" + return [s1, s2, sNI] + + +def _annotation_ascii_FEs(sent): + """ + ASCII string rendering of the sentence along with a single target and its FEs. + Secondary and tertiary FE layers are included if present. + 'sent' can be an FE annotation set or an LU sentence with a single target. + Line-wrapped to limit the display width. + """ + feAbbrevs = OrderedDict() + posspec = [] # POS-specific layer spans (e.g., Supp[ort], Cop[ula]) + posspec_separate = False + for lyr in ("Verb", "Noun", "Adj", "Adv", "Prep", "Scon", "Art"): + if lyr in sent and sent[lyr]: + for a, b, lbl in sent[lyr]: + if ( + lbl == "X" + ): # skip this, which covers an entire phrase typically containing the target and all its FEs + # (but do display the Gov) + continue + if any(1 for x, y, felbl in sent.FE[0] if x <= a < y or a <= x < b): + # overlap between one of the POS-specific layers and first FE layer + posspec_separate = ( + True # show POS-specific layers on a separate line + ) + posspec.append( + (a, b, lbl.lower().replace("-", "")) + ) # lowercase Cop=>cop, Non-Asp=>nonasp, etc. to distinguish from FE names + if posspec_separate: + POSSPEC = _annotation_ascii_FE_layer(posspec, {}, feAbbrevs) + FE1 = _annotation_ascii_FE_layer( + sorted(sent.FE[0] + (posspec if not posspec_separate else [])), + sent.FE[1], + feAbbrevs, + ) + FE2 = FE3 = None + if "FE2" in sent: + FE2 = _annotation_ascii_FE_layer(sent.FE2[0], sent.FE2[1], feAbbrevs) + if "FE3" in sent: + FE3 = _annotation_ascii_FE_layer(sent.FE3[0], sent.FE3[1], feAbbrevs) + + for i, j in sent.Target: + FE1span, FE1name, FE1exp = FE1 + if len(FE1span) < j: + FE1span += " " * (j - len(FE1span)) + if len(FE1name) < j: + FE1name += " " * (j - len(FE1name)) + FE1[1] = FE1name + FE1[0] = ( + FE1span[:i] + FE1span[i:j].replace(" ", "*").replace("-", "=") + FE1span[j:] + ) + long_lines = [sent.text] + if posspec_separate: + long_lines.extend(POSSPEC[:2]) + long_lines.extend([FE1[0], FE1[1] + FE1[2]]) # lines with no length limit + if FE2: + long_lines.extend([FE2[0], FE2[1] + FE2[2]]) + if FE3: + long_lines.extend([FE3[0], FE3[1] + FE3[2]]) + long_lines.append("") + outstr = "\n".join( + map("\n".join, zip_longest(*mimic_wrap(long_lines), fillvalue=" ")) + ) + if feAbbrevs: + outstr += "(" + ", ".join("=".join(pair) for pair in feAbbrevs.items()) + ")" + assert len(feAbbrevs) == len(dict(feAbbrevs)), "Abbreviation clash" + outstr += "\n" + + return outstr + + +def _pretty_fe(fe): + + """ + Helper function for pretty-printing a frame element. + + :param fe: The frame element to be printed. + :type fe: AttrDict + :return: A nicely formatted string representation of the frame element. + :rtype: str + """ + fekeys = fe.keys() + outstr = "" + outstr += "frame element ({0.ID}): {0.name}\n of {1.name}({1.ID})\n".format( + fe, fe.frame + ) + if "definition" in fekeys: + outstr += "[definition]\n" + outstr += _pretty_longstring(fe.definition, " ") + if "abbrev" in fekeys: + outstr += f"[abbrev] {fe.abbrev}\n" + if "coreType" in fekeys: + outstr += f"[coreType] {fe.coreType}\n" + if "requiresFE" in fekeys: + outstr += "[requiresFE] " + if fe.requiresFE is None: + outstr += "\n" + else: + outstr += f"{fe.requiresFE.name}({fe.requiresFE.ID})\n" + if "excludesFE" in fekeys: + outstr += "[excludesFE] " + if fe.excludesFE is None: + outstr += "\n" + else: + outstr += f"{fe.excludesFE.name}({fe.excludesFE.ID})\n" + if "semType" in fekeys: + outstr += "[semType] " + if fe.semType is None: + outstr += "\n" + else: + outstr += "\n " + f"{fe.semType.name}({fe.semType.ID})" + "\n" + + return outstr + + +def _pretty_frame(frame): + + """ + Helper function for pretty-printing a frame. + + :param frame: The frame to be printed. + :type frame: AttrDict + :return: A nicely formatted string representation of the frame. + :rtype: str + """ + + outstr = "" + outstr += "frame ({0.ID}): {0.name}\n\n".format(frame) + outstr += f"[URL] {frame.URL}\n\n" + outstr += "[definition]\n" + outstr += _pretty_longstring(frame.definition, " ") + "\n" + + outstr += f"[semTypes] {len(frame.semTypes)} semantic types\n" + outstr += ( + " " * (len(frame.semTypes) > 0) + + ", ".join(f"{x.name}({x.ID})" for x in frame.semTypes) + + "\n" * (len(frame.semTypes) > 0) + ) + + outstr += "\n[frameRelations] {} frame relations\n".format( + len(frame.frameRelations) + ) + outstr += " " + "\n ".join(repr(frel) for frel in frame.frameRelations) + "\n" + + outstr += f"\n[lexUnit] {len(frame.lexUnit)} lexical units\n" + lustrs = [] + for luName, lu in sorted(frame.lexUnit.items()): + tmpstr = f"{luName} ({lu.ID})" + lustrs.append(tmpstr) + outstr += "{}\n".format(_pretty_longstring(", ".join(lustrs), prefix=" ")) + + outstr += f"\n[FE] {len(frame.FE)} frame elements\n" + fes = {} + for feName, fe in sorted(frame.FE.items()): + try: + fes[fe.coreType].append(f"{feName} ({fe.ID})") + except KeyError: + fes[fe.coreType] = [] + fes[fe.coreType].append(f"{feName} ({fe.ID})") + for ct in sorted( + fes.keys(), + key=lambda ct2: [ + "Core", + "Core-Unexpressed", + "Peripheral", + "Extra-Thematic", + ].index(ct2), + ): + outstr += "{:>16}: {}\n".format(ct, ", ".join(sorted(fes[ct]))) + + outstr += "\n[FEcoreSets] {} frame element core sets\n".format( + len(frame.FEcoreSets) + ) + outstr += ( + " " + + "\n ".join( + ", ".join([x.name for x in coreSet]) for coreSet in frame.FEcoreSets + ) + + "\n" + ) + + return outstr + + +class FramenetError(Exception): + + """An exception class for framenet-related errors.""" + + +class AttrDict(dict): + + """A class that wraps a dict and allows accessing the keys of the + dict as if they were attributes. Taken from here: + https://stackoverflow.com/a/14620633/8879 + + >>> foo = {'a':1, 'b':2, 'c':3} + >>> bar = AttrDict(foo) + >>> pprint(dict(bar)) + {'a': 1, 'b': 2, 'c': 3} + >>> bar.b + 2 + >>> bar.d = 4 + >>> pprint(dict(bar)) + {'a': 1, 'b': 2, 'c': 3, 'd': 4} + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # self.__dict__ = self + + def __setattr__(self, name, value): + self[name] = value + + def __getattr__(self, name): + if name == "_short_repr": + return self._short_repr + return self[name] + + def __getitem__(self, name): + v = super().__getitem__(name) + if isinstance(v, Future): + return v._data() + return v + + def _short_repr(self): + if "_type" in self: + if self["_type"].endswith("relation"): + return self.__repr__() + try: + return "<{} ID={} name={}>".format( + self["_type"], self["ID"], self["name"] + ) + except KeyError: + try: # no ID--e.g., for _type=lusubcorpus + return "<{} name={}>".format(self["_type"], self["name"]) + except KeyError: # no name--e.g., for _type=lusentence + return "<{} ID={}>".format(self["_type"], self["ID"]) + else: + return self.__repr__() + + def _str(self): + outstr = "" + + if "_type" not in self: + outstr = _pretty_any(self) + elif self["_type"] == "frame": + outstr = _pretty_frame(self) + elif self["_type"] == "fe": + outstr = _pretty_fe(self) + elif self["_type"] == "lu": + outstr = _pretty_lu(self) + elif self["_type"] == "luexemplars": # list of ALL exemplars for LU + outstr = _pretty_exemplars(self, self[0].LU) + elif ( + self["_type"] == "fulltext_annotation" + ): # list of all sentences for full-text doc + outstr = _pretty_fulltext_sentences(self) + elif self["_type"] == "lusentence": + outstr = _pretty_annotation(self) + elif self["_type"] == "fulltext_sentence": + outstr = _pretty_fulltext_sentence(self) + elif self["_type"] in ("luannotationset", "fulltext_annotationset"): + outstr = _pretty_annotation(self, aset_level=True) + elif self["_type"] == "posannotationset": + outstr = _pretty_pos(self) + elif self["_type"] == "semtype": + outstr = _pretty_semtype(self) + elif self["_type"] == "framerelationtype": + outstr = _pretty_frame_relation_type(self) + elif self["_type"] == "framerelation": + outstr = _pretty_frame_relation(self) + elif self["_type"] == "ferelation": + outstr = _pretty_fe_relation(self) + else: + outstr = _pretty_any(self) + + # ensure result is unicode string prior to applying the + # decorator (because non-ASCII characters + # could in principle occur in the data and would trigger an encoding error when + # passed as arguments to str.format()). + # assert isinstance(outstr, unicode) # not in Python 3.2 + return outstr + + def __str__(self): + return self._str() + + def __repr__(self): + return self.__str__() + + +class SpecialList(list): + """ + A list subclass which adds a '_type' attribute for special printing + (similar to an AttrDict, though this is NOT an AttrDict subclass). + """ + + def __init__(self, typ, *args, **kwargs): + super().__init__(*args, **kwargs) + self._type = typ + + def _str(self): + outstr = "" + + assert self._type + if len(self) == 0: + outstr = "[]" + elif self._type == "luexemplars": # list of ALL exemplars for LU + outstr = _pretty_exemplars(self, self[0].LU) + else: + assert False, self._type + return outstr + + def __str__(self): + return self._str() + + def __repr__(self): + return self.__str__() + + +class Future: + """ + Wraps and acts as a proxy for a value to be loaded lazily (on demand). + Adapted from https://gist.github.com/sergey-miryanov/2935416 + """ + + def __init__(self, loader, *args, **kwargs): + """ + :param loader: when called with no arguments, returns the value to be stored + :type loader: callable + """ + super().__init__(*args, **kwargs) + self._loader = loader + self._d = None + + def _data(self): + if callable(self._loader): + self._d = self._loader() + self._loader = None # the data is now cached + return self._d + + def __nonzero__(self): + return bool(self._data()) + + def __len__(self): + return len(self._data()) + + def __setitem__(self, key, value): + return self._data().__setitem__(key, value) + + def __getitem__(self, key): + return self._data().__getitem__(key) + + def __getattr__(self, key): + return self._data().__getattr__(key) + + def __str__(self): + return self._data().__str__() + + def __repr__(self): + return self._data().__repr__() + + +class PrettyDict(AttrDict): + """ + Displays an abbreviated repr of values where possible. + Inherits from AttrDict, so a callable value will + be lazily converted to an actual value. + """ + + def __init__(self, *args, **kwargs): + _BREAK_LINES = kwargs.pop("breakLines", False) + super().__init__(*args, **kwargs) + dict.__setattr__(self, "_BREAK_LINES", _BREAK_LINES) + + def __repr__(self): + parts = [] + for k, v in sorted(self.items()): + kv = repr(k) + ": " + try: + kv += v._short_repr() + except AttributeError: + kv += repr(v) + parts.append(kv) + return "{" + (",\n " if self._BREAK_LINES else ", ").join(parts) + "}" + + +class PrettyList(list): + """ + Displays an abbreviated repr of only the first several elements, not the whole list. + """ + + # from nltk.util + def __init__(self, *args, **kwargs): + self._MAX_REPR_SIZE = kwargs.pop("maxReprSize", 60) + self._BREAK_LINES = kwargs.pop("breakLines", False) + super().__init__(*args, **kwargs) + + def __repr__(self): + """ + Return a string representation for this corpus view that is + similar to a list's representation; but if it would be more + than 60 characters long, it is truncated. + """ + pieces = [] + length = 5 + + for elt in self: + pieces.append( + elt._short_repr() + ) # key difference from inherited version: call to _short_repr() + length += len(pieces[-1]) + 2 + if self._MAX_REPR_SIZE and length > self._MAX_REPR_SIZE and len(pieces) > 2: + return "[%s, ...]" % str(",\n " if self._BREAK_LINES else ", ").join( + pieces[:-1] + ) + return "[%s]" % str(",\n " if self._BREAK_LINES else ", ").join(pieces) + + +class PrettyLazyMap(LazyMap): + """ + Displays an abbreviated repr of only the first several elements, not the whole list. + """ + + # from nltk.util + _MAX_REPR_SIZE = 60 + + def __repr__(self): + """ + Return a string representation for this corpus view that is + similar to a list's representation; but if it would be more + than 60 characters long, it is truncated. + """ + pieces = [] + length = 5 + for elt in self: + pieces.append( + elt._short_repr() + ) # key difference from inherited version: call to _short_repr() + length += len(pieces[-1]) + 2 + if length > self._MAX_REPR_SIZE and len(pieces) > 2: + return "[%s, ...]" % ", ".join(pieces[:-1]) + return "[%s]" % ", ".join(pieces) + + +class PrettyLazyIteratorList(LazyIteratorList): + """ + Displays an abbreviated repr of only the first several elements, not the whole list. + """ + + # from nltk.util + _MAX_REPR_SIZE = 60 + + def __repr__(self): + """ + Return a string representation for this corpus view that is + similar to a list's representation; but if it would be more + than 60 characters long, it is truncated. + """ + pieces = [] + length = 5 + for elt in self: + pieces.append( + elt._short_repr() + ) # key difference from inherited version: call to _short_repr() + length += len(pieces[-1]) + 2 + if length > self._MAX_REPR_SIZE and len(pieces) > 2: + return "[%s, ...]" % ", ".join(pieces[:-1]) + return "[%s]" % ", ".join(pieces) + + +class PrettyLazyConcatenation(LazyConcatenation): + """ + Displays an abbreviated repr of only the first several elements, not the whole list. + """ + + # from nltk.util + _MAX_REPR_SIZE = 60 + + def __repr__(self): + """ + Return a string representation for this corpus view that is + similar to a list's representation; but if it would be more + than 60 characters long, it is truncated. + """ + pieces = [] + length = 5 + for elt in self: + pieces.append( + elt._short_repr() + ) # key difference from inherited version: call to _short_repr() + length += len(pieces[-1]) + 2 + if length > self._MAX_REPR_SIZE and len(pieces) > 2: + return "[%s, ...]" % ", ".join(pieces[:-1]) + return "[%s]" % ", ".join(pieces) + + def __add__(self, other): + """Return a list concatenating self with other.""" + return PrettyLazyIteratorList(itertools.chain(self, other)) + + def __radd__(self, other): + """Return a list concatenating other with self.""" + return PrettyLazyIteratorList(itertools.chain(other, self)) + + +class FramenetCorpusReader(XMLCorpusReader): + """A corpus reader for the Framenet Corpus. + + >>> from nltk.corpus import framenet as fn + >>> fn.lu(3238).frame.lexUnit['glint.v'] is fn.lu(3238) + True + >>> fn.frame_by_name('Replacing') is fn.lus('replace.v')[0].frame + True + >>> fn.lus('prejudice.n')[0].frame.frameRelations == fn.frame_relations('Partiality') + True + """ + + _bad_statuses = ["Problem"] + """ + When loading LUs for a frame, those whose status is in this list will be ignored. + Due to caching, if user code modifies this, it should do so before loading any data. + 'Problem' should always be listed for FrameNet 1.5, as these LUs are not included + in the XML index. + """ + + _warnings = False + + def warnings(self, v): + """Enable or disable warnings of data integrity issues as they are encountered. + If v is truthy, warnings will be enabled. + + (This is a function rather than just an attribute/property to ensure that if + enabling warnings is the first action taken, the corpus reader is instantiated first.) + """ + self._warnings = v + + def __init__(self, root, fileids): + XMLCorpusReader.__init__(self, root, fileids) + + # framenet corpus sub dirs + # sub dir containing the xml files for frames + self._frame_dir = "frame" + # sub dir containing the xml files for lexical units + self._lu_dir = "lu" + # sub dir containing the xml files for fulltext annotation files + self._fulltext_dir = "fulltext" + + # location of latest development version of FrameNet + self._fnweb_url = "https://framenet2.icsi.berkeley.edu/fnReports/data" + + # Indexes used for faster look-ups + self._frame_idx = None + self._cached_frames = {} # name -> ID + self._lu_idx = None + self._fulltext_idx = None + self._semtypes = None + self._freltyp_idx = None # frame relation types (Inheritance, Using, etc.) + self._frel_idx = None # frame-to-frame relation instances + self._ferel_idx = None # FE-to-FE relation instances + self._frel_f_idx = None # frame-to-frame relations associated with each frame + + self._readme = "README.txt" + + def help(self, attrname=None): + """Display help information summarizing the main methods.""" + + if attrname is not None: + return help(self.__getattribute__(attrname)) + + # No need to mention frame_by_name() or frame_by_id(), + # as it's easier to just call frame(). + # Also not mentioning lu_basic(). + + msg = """ +Citation: Nathan Schneider and Chuck Wooters (2017), +"The NLTK FrameNet API: Designing for Discoverability with a Rich Linguistic Resource". +Proceedings of EMNLP: System Demonstrations. https://arxiv.org/abs/1703.07438 + +Use the following methods to access data in FrameNet. +Provide a method name to `help()` for more information. + +FRAMES +====== + +frame() to look up a frame by its exact name or ID +frames() to get frames matching a name pattern +frames_by_lemma() to get frames containing an LU matching a name pattern +frame_ids_and_names() to get a mapping from frame IDs to names + +FRAME ELEMENTS +============== + +fes() to get frame elements (a.k.a. roles) matching a name pattern, optionally constrained + by a frame name pattern + +LEXICAL UNITS +============= + +lu() to look up an LU by its ID +lus() to get lexical units matching a name pattern, optionally constrained by frame +lu_ids_and_names() to get a mapping from LU IDs to names + +RELATIONS +========= + +frame_relation_types() to get the different kinds of frame-to-frame relations + (Inheritance, Subframe, Using, etc.). +frame_relations() to get the relation instances, optionally constrained by + frame(s) or relation type +fe_relations() to get the frame element pairs belonging to a frame-to-frame relation + +SEMANTIC TYPES +============== + +semtypes() to get the different kinds of semantic types that can be applied to + FEs, LUs, and entire frames +semtype() to look up a particular semtype by name, ID, or abbreviation +semtype_inherits() to check whether two semantic types have a subtype-supertype + relationship in the semtype hierarchy +propagate_semtypes() to apply inference rules that distribute semtypes over relations + between FEs + +ANNOTATIONS +=========== + +annotations() to get annotation sets, in which a token in a sentence is annotated + with a lexical unit in a frame, along with its frame elements and their syntactic properties; + can be constrained by LU name pattern and limited to lexicographic exemplars or full-text. + Sentences of full-text annotation can have multiple annotation sets. +sents() to get annotated sentences illustrating one or more lexical units +exemplars() to get sentences of lexicographic annotation, most of which have + just 1 annotation set; can be constrained by LU name pattern, frame, and overt FE(s) +doc() to look up a document of full-text annotation by its ID +docs() to get documents of full-text annotation that match a name pattern +docs_metadata() to get metadata about all full-text documents without loading them +ft_sents() to iterate over sentences of full-text annotation + +UTILITIES +========= + +buildindexes() loads metadata about all frames, LUs, etc. into memory to avoid + delay when one is accessed for the first time. It does not load annotations. +readme() gives the text of the FrameNet README file +warnings(True) to display corpus consistency warnings when loading data + """ + print(msg) + + def _buildframeindex(self): + # The total number of Frames in Framenet is fairly small (~1200) so + # this index should not be very large + if not self._frel_idx: + self._buildrelationindex() # always load frame relations before frames, + # otherwise weird ordering effects might result in incomplete information + self._frame_idx = {} + with XMLCorpusView( + self.abspath("frameIndex.xml"), "frameIndex/frame", self._handle_elt + ) as view: + for f in view: + self._frame_idx[f["ID"]] = f + + def _buildcorpusindex(self): + # The total number of fulltext annotated documents in Framenet + # is fairly small (~90) so this index should not be very large + self._fulltext_idx = {} + with XMLCorpusView( + self.abspath("fulltextIndex.xml"), + "fulltextIndex/corpus", + self._handle_fulltextindex_elt, + ) as view: + for doclist in view: + for doc in doclist: + self._fulltext_idx[doc.ID] = doc + + def _buildluindex(self): + # The number of LUs in Framenet is about 13,000 so this index + # should not be very large + self._lu_idx = {} + with XMLCorpusView( + self.abspath("luIndex.xml"), "luIndex/lu", self._handle_elt + ) as view: + for lu in view: + self._lu_idx[ + lu["ID"] + ] = lu # populate with LU index entries. if any of these + # are looked up they will be replaced by full LU objects. + + def _buildrelationindex(self): + # print('building relation index...', file=sys.stderr) + self._freltyp_idx = {} + self._frel_idx = {} + self._frel_f_idx = defaultdict(set) + self._ferel_idx = {} + + with XMLCorpusView( + self.abspath("frRelation.xml"), + "frameRelations/frameRelationType", + self._handle_framerelationtype_elt, + ) as view: + for freltyp in view: + self._freltyp_idx[freltyp.ID] = freltyp + for frel in freltyp.frameRelations: + supF = frel.superFrame = frel[freltyp.superFrameName] = Future( + (lambda fID: lambda: self.frame_by_id(fID))(frel.supID) + ) + subF = frel.subFrame = frel[freltyp.subFrameName] = Future( + (lambda fID: lambda: self.frame_by_id(fID))(frel.subID) + ) + self._frel_idx[frel.ID] = frel + self._frel_f_idx[frel.supID].add(frel.ID) + self._frel_f_idx[frel.subID].add(frel.ID) + for ferel in frel.feRelations: + ferel.superFrame = supF + ferel.subFrame = subF + ferel.superFE = Future( + (lambda fer: lambda: fer.superFrame.FE[fer.superFEName])( + ferel + ) + ) + ferel.subFE = Future( + (lambda fer: lambda: fer.subFrame.FE[fer.subFEName])(ferel) + ) + self._ferel_idx[ferel.ID] = ferel + # print('...done building relation index', file=sys.stderr) + + def _warn(self, *message, **kwargs): + if self._warnings: + kwargs.setdefault("file", sys.stderr) + print(*message, **kwargs) + + def buildindexes(self): + """ + Build the internal indexes to make look-ups faster. + """ + # Frames + self._buildframeindex() + # LUs + self._buildluindex() + # Fulltext annotation corpora index + self._buildcorpusindex() + # frame and FE relations + self._buildrelationindex() + + def doc(self, fn_docid): + """ + Returns the annotated document whose id number is + ``fn_docid``. This id number can be obtained by calling the + Documents() function. + + The dict that is returned from this function will contain the + following keys: + + - '_type' : 'fulltextannotation' + - 'sentence' : a list of sentences in the document + - Each item in the list is a dict containing the following keys: + - 'ID' : the ID number of the sentence + - '_type' : 'sentence' + - 'text' : the text of the sentence + - 'paragNo' : the paragraph number + - 'sentNo' : the sentence number + - 'docID' : the document ID number + - 'corpID' : the corpus ID number + - 'aPos' : the annotation position + - 'annotationSet' : a list of annotation layers for the sentence + - Each item in the list is a dict containing the following keys: + - 'ID' : the ID number of the annotation set + - '_type' : 'annotationset' + - 'status' : either 'MANUAL' or 'UNANN' + - 'luName' : (only if status is 'MANUAL') + - 'luID' : (only if status is 'MANUAL') + - 'frameID' : (only if status is 'MANUAL') + - 'frameName': (only if status is 'MANUAL') + - 'layer' : a list of labels for the layer + - Each item in the layer is a dict containing the following keys: + - '_type': 'layer' + - 'rank' + - 'name' + - 'label' : a list of labels in the layer + - Each item is a dict containing the following keys: + - 'start' + - 'end' + - 'name' + - 'feID' (optional) + + :param fn_docid: The Framenet id number of the document + :type fn_docid: int + :return: Information about the annotated document + :rtype: dict + """ + try: + xmlfname = self._fulltext_idx[fn_docid].filename + except TypeError: # happens when self._fulltext_idx == None + # build the index + self._buildcorpusindex() + xmlfname = self._fulltext_idx[fn_docid].filename + except KeyError as e: # probably means that fn_docid was not in the index + raise FramenetError(f"Unknown document id: {fn_docid}") from e + + # construct the path name for the xml file containing the document info + locpath = os.path.join(f"{self._root}", self._fulltext_dir, xmlfname) + + # Grab the top-level xml element containing the fulltext annotation + with XMLCorpusView(locpath, "fullTextAnnotation") as view: + elt = view[0] + info = self._handle_fulltextannotation_elt(elt) + # add metadata + for k, v in self._fulltext_idx[fn_docid].items(): + info[k] = v + return info + + def frame_by_id(self, fn_fid, ignorekeys=[]): + """ + Get the details for the specified Frame using the frame's id + number. + + Usage examples: + + >>> from nltk.corpus import framenet as fn + >>> f = fn.frame_by_id(256) + >>> f.ID + 256 + >>> f.name + 'Medical_specialties' + >>> f.definition # doctest: +NORMALIZE_WHITESPACE + "This frame includes words that name medical specialties and is closely related to the + Medical_professionals frame. The FE Type characterizing a sub-are in a Specialty may also be + expressed. 'Ralph practices paediatric oncology.'" + + :param fn_fid: The Framenet id number of the frame + :type fn_fid: int + :param ignorekeys: The keys to ignore. These keys will not be + included in the output. (optional) + :type ignorekeys: list(str) + :return: Information about a frame + :rtype: dict + + Also see the ``frame()`` function for details about what is + contained in the dict that is returned. + """ + + # get the name of the frame with this id number + try: + fentry = self._frame_idx[fn_fid] + if "_type" in fentry: + return fentry # full frame object is cached + name = fentry["name"] + except TypeError: + self._buildframeindex() + name = self._frame_idx[fn_fid]["name"] + except KeyError as e: + raise FramenetError(f"Unknown frame id: {fn_fid}") from e + + return self.frame_by_name(name, ignorekeys, check_cache=False) + + def frame_by_name(self, fn_fname, ignorekeys=[], check_cache=True): + """ + Get the details for the specified Frame using the frame's name. + + Usage examples: + + >>> from nltk.corpus import framenet as fn + >>> f = fn.frame_by_name('Medical_specialties') + >>> f.ID + 256 + >>> f.name + 'Medical_specialties' + >>> f.definition # doctest: +NORMALIZE_WHITESPACE + "This frame includes words that name medical specialties and is closely related to the + Medical_professionals frame. The FE Type characterizing a sub-are in a Specialty may also be + expressed. 'Ralph practices paediatric oncology.'" + + :param fn_fname: The name of the frame + :type fn_fname: str + :param ignorekeys: The keys to ignore. These keys will not be + included in the output. (optional) + :type ignorekeys: list(str) + :return: Information about a frame + :rtype: dict + + Also see the ``frame()`` function for details about what is + contained in the dict that is returned. + """ + + if check_cache and fn_fname in self._cached_frames: + return self._frame_idx[self._cached_frames[fn_fname]] + elif not self._frame_idx: + self._buildframeindex() + + # construct the path name for the xml file containing the Frame info + locpath = os.path.join(f"{self._root}", self._frame_dir, fn_fname + ".xml") + # print(locpath, file=sys.stderr) + # Grab the xml for the frame + try: + with XMLCorpusView(locpath, "frame") as view: + elt = view[0] + except OSError as e: + raise FramenetError(f"Unknown frame: {fn_fname}") from e + + fentry = self._handle_frame_elt(elt, ignorekeys) + assert fentry + + fentry.URL = self._fnweb_url + "/" + self._frame_dir + "/" + fn_fname + ".xml" + + # INFERENCE RULE: propagate lexical semtypes from the frame to all its LUs + for st in fentry.semTypes: + if st.rootType.name == "Lexical_type": + for lu in fentry.lexUnit.values(): + if not any( + x is st for x in lu.semTypes + ): # identity containment check + lu.semTypes.append(st) + + self._frame_idx[fentry.ID] = fentry + self._cached_frames[fentry.name] = fentry.ID + """ + # now set up callables to resolve the LU pointers lazily. + # (could also do this here--caching avoids infinite recursion.) + for luName,luinfo in fentry.lexUnit.items(): + fentry.lexUnit[luName] = (lambda luID: Future(lambda: self.lu(luID)))(luinfo.ID) + """ + return fentry + + def frame(self, fn_fid_or_fname, ignorekeys=[]): + """ + Get the details for the specified Frame using the frame's name + or id number. + + Usage examples: + + >>> from nltk.corpus import framenet as fn + >>> f = fn.frame(256) + >>> f.name + 'Medical_specialties' + >>> f = fn.frame('Medical_specialties') + >>> f.ID + 256 + >>> # ensure non-ASCII character in definition doesn't trigger an encoding error: + >>> fn.frame('Imposing_obligation') # doctest: +ELLIPSIS + frame (1494): Imposing_obligation... + + + The dict that is returned from this function will contain the + following information about the Frame: + + - 'name' : the name of the Frame (e.g. 'Birth', 'Apply_heat', etc.) + - 'definition' : textual definition of the Frame + - 'ID' : the internal ID number of the Frame + - 'semTypes' : a list of semantic types for this frame + - Each item in the list is a dict containing the following keys: + - 'name' : can be used with the semtype() function + - 'ID' : can be used with the semtype() function + + - 'lexUnit' : a dict containing all of the LUs for this frame. + The keys in this dict are the names of the LUs and + the value for each key is itself a dict containing + info about the LU (see the lu() function for more info.) + + - 'FE' : a dict containing the Frame Elements that are part of this frame + The keys in this dict are the names of the FEs (e.g. 'Body_system') + and the values are dicts containing the following keys + + - 'definition' : The definition of the FE + - 'name' : The name of the FE e.g. 'Body_system' + - 'ID' : The id number + - '_type' : 'fe' + - 'abbrev' : Abbreviation e.g. 'bod' + - 'coreType' : one of "Core", "Peripheral", or "Extra-Thematic" + - 'semType' : if not None, a dict with the following two keys: + - 'name' : name of the semantic type. can be used with + the semtype() function + - 'ID' : id number of the semantic type. can be used with + the semtype() function + - 'requiresFE' : if not None, a dict with the following two keys: + - 'name' : the name of another FE in this frame + - 'ID' : the id of the other FE in this frame + - 'excludesFE' : if not None, a dict with the following two keys: + - 'name' : the name of another FE in this frame + - 'ID' : the id of the other FE in this frame + + - 'frameRelation' : a list of objects describing frame relations + - 'FEcoreSets' : a list of Frame Element core sets for this frame + - Each item in the list is a list of FE objects + + :param fn_fid_or_fname: The Framenet name or id number of the frame + :type fn_fid_or_fname: int or str + :param ignorekeys: The keys to ignore. These keys will not be + included in the output. (optional) + :type ignorekeys: list(str) + :return: Information about a frame + :rtype: dict + """ + + # get the frame info by name or id number + if isinstance(fn_fid_or_fname, str): + f = self.frame_by_name(fn_fid_or_fname, ignorekeys) + else: + f = self.frame_by_id(fn_fid_or_fname, ignorekeys) + + return f + + def frames_by_lemma(self, pat): + """ + Returns a list of all frames that contain LUs in which the + ``name`` attribute of the LU matches the given regular expression + ``pat``. Note that LU names are composed of "lemma.POS", where + the "lemma" part can be made up of either a single lexeme + (e.g. 'run') or multiple lexemes (e.g. 'a little'). + + Note: if you are going to be doing a lot of this type of + searching, you'd want to build an index that maps from lemmas to + frames because each time frames_by_lemma() is called, it has to + search through ALL of the frame XML files in the db. + + >>> from nltk.corpus import framenet as fn + >>> from nltk.corpus.reader.framenet import PrettyList + >>> PrettyList(sorted(fn.frames_by_lemma(r'(?i)a little'), key=itemgetter('ID'))) # doctest: +ELLIPSIS + [, ] + + :return: A list of frame objects. + :rtype: list(AttrDict) + """ + return PrettyList( + f + for f in self.frames() + if any(re.search(pat, luName) for luName in f.lexUnit) + ) + + def lu_basic(self, fn_luid): + """ + Returns basic information about the LU whose id is + ``fn_luid``. This is basically just a wrapper around the + ``lu()`` function with "subCorpus" info excluded. + + >>> from nltk.corpus import framenet as fn + >>> lu = PrettyDict(fn.lu_basic(256), breakLines=True) + >>> # ellipses account for differences between FN 1.5 and 1.7 + >>> lu # doctest: +ELLIPSIS + {'ID': 256, + 'POS': 'V', + 'URL': 'https://framenet2.icsi.berkeley.edu/fnReports/data/lu/lu256.xml', + '_type': 'lu', + 'cBy': ..., + 'cDate': '02/08/2001 01:27:50 PST Thu', + 'definition': 'COD: be aware of beforehand; predict.', + 'definitionMarkup': 'COD: be aware of beforehand; predict.', + 'frame': , + 'lemmaID': 15082, + 'lexemes': [{'POS': 'V', 'breakBefore': 'false', 'headword': 'false', 'name': 'foresee', 'order': 1}], + 'name': 'foresee.v', + 'semTypes': [], + 'sentenceCount': {'annotated': ..., 'total': ...}, + 'status': 'FN1_Sent'} + + :param fn_luid: The id number of the desired LU + :type fn_luid: int + :return: Basic information about the lexical unit + :rtype: dict + """ + return self.lu(fn_luid, ignorekeys=["subCorpus", "exemplars"]) + + def lu(self, fn_luid, ignorekeys=[], luName=None, frameID=None, frameName=None): + """ + Access a lexical unit by its ID. luName, frameID, and frameName are used + only in the event that the LU does not have a file in the database + (which is the case for LUs with "Problem" status); in this case, + a placeholder LU is created which just contains its name, ID, and frame. + + + Usage examples: + + >>> from nltk.corpus import framenet as fn + >>> fn.lu(256).name + 'foresee.v' + >>> fn.lu(256).definition + 'COD: be aware of beforehand; predict.' + >>> fn.lu(256).frame.name + 'Expectation' + >>> list(map(PrettyDict, fn.lu(256).lexemes)) + [{'POS': 'V', 'breakBefore': 'false', 'headword': 'false', 'name': 'foresee', 'order': 1}] + + >>> fn.lu(227).exemplars[23] # doctest: +NORMALIZE_WHITESPACE + exemplar sentence (352962): + [sentNo] 0 + [aPos] 59699508 + + [LU] (227) guess.v in Coming_to_believe + + [frame] (23) Coming_to_believe + + [annotationSet] 2 annotation sets + + [POS] 18 tags + + [POS_tagset] BNC + + [GF] 3 relations + + [PT] 3 phrases + + [Other] 1 entry + + [text] + [Target] + [FE] + + When he was inside the house , Culley noticed the characteristic + ------------------ + Content + + he would n't have guessed at . + -- ******* -- + Co C1 [Evidence:INI] + (Co=Cognizer, C1=Content) + + + + The dict that is returned from this function will contain most of the + following information about the LU. Note that some LUs do not contain + all of these pieces of information - particularly 'totalAnnotated' and + 'incorporatedFE' may be missing in some LUs: + + - 'name' : the name of the LU (e.g. 'merger.n') + - 'definition' : textual definition of the LU + - 'ID' : the internal ID number of the LU + - '_type' : 'lu' + - 'status' : e.g. 'Created' + - 'frame' : Frame that this LU belongs to + - 'POS' : the part of speech of this LU (e.g. 'N') + - 'totalAnnotated' : total number of examples annotated with this LU + - 'incorporatedFE' : FE that incorporates this LU (e.g. 'Ailment') + - 'sentenceCount' : a dict with the following two keys: + - 'annotated': number of sentences annotated with this LU + - 'total' : total number of sentences with this LU + + - 'lexemes' : a list of dicts describing the lemma of this LU. + Each dict in the list contains these keys: + + - 'POS' : part of speech e.g. 'N' + - 'name' : either single-lexeme e.g. 'merger' or + multi-lexeme e.g. 'a little' + - 'order': the order of the lexeme in the lemma (starting from 1) + - 'headword': a boolean ('true' or 'false') + - 'breakBefore': Can this lexeme be separated from the previous lexeme? + Consider: "take over.v" as in:: + + Germany took over the Netherlands in 2 days. + Germany took the Netherlands over in 2 days. + + In this case, 'breakBefore' would be "true" for the lexeme + "over". Contrast this with "take after.v" as in:: + + Mary takes after her grandmother. + *Mary takes her grandmother after. + + In this case, 'breakBefore' would be "false" for the lexeme "after" + + - 'lemmaID' : Can be used to connect lemmas in different LUs + - 'semTypes' : a list of semantic type objects for this LU + - 'subCorpus' : a list of subcorpora + - Each item in the list is a dict containing the following keys: + - 'name' : + - 'sentence' : a list of sentences in the subcorpus + - each item in the list is a dict with the following keys: + - 'ID': + - 'sentNo': + - 'text': the text of the sentence + - 'aPos': + - 'annotationSet': a list of annotation sets + - each item in the list is a dict with the following keys: + - 'ID': + - 'status': + - 'layer': a list of layers + - each layer is a dict containing the following keys: + - 'name': layer name (e.g. 'BNC') + - 'rank': + - 'label': a list of labels for the layer + - each label is a dict containing the following keys: + - 'start': start pos of label in sentence 'text' (0-based) + - 'end': end pos of label in sentence 'text' (0-based) + - 'name': name of label (e.g. 'NN1') + + Under the hood, this implementation looks up the lexical unit information + in the *frame* definition file. That file does not contain + corpus annotations, so the LU files will be accessed on demand if those are + needed. In principle, valence patterns could be loaded here too, + though these are not currently supported. + + :param fn_luid: The id number of the lexical unit + :type fn_luid: int + :param ignorekeys: The keys to ignore. These keys will not be + included in the output. (optional) + :type ignorekeys: list(str) + :return: All information about the lexical unit + :rtype: dict + """ + # look for this LU in cache + if not self._lu_idx: + self._buildluindex() + OOV = object() + luinfo = self._lu_idx.get(fn_luid, OOV) + if luinfo is OOV: + # LU not in the index. We create a placeholder by falling back to + # luName, frameID, and frameName. However, this will not be listed + # among the LUs for its frame. + self._warn( + "LU ID not found: {} ({}) in {} ({})".format( + luName, fn_luid, frameName, frameID + ) + ) + luinfo = AttrDict( + { + "_type": "lu", + "ID": fn_luid, + "name": luName, + "frameID": frameID, + "status": "Problem", + } + ) + f = self.frame_by_id(luinfo.frameID) + assert f.name == frameName, (f.name, frameName) + luinfo["frame"] = f + self._lu_idx[fn_luid] = luinfo + elif "_type" not in luinfo: + # we only have an index entry for the LU. loading the frame will replace this. + f = self.frame_by_id(luinfo.frameID) + luinfo = self._lu_idx[fn_luid] + if ignorekeys: + return AttrDict({k: v for k, v in luinfo.items() if k not in ignorekeys}) + + return luinfo + + def _lu_file(self, lu, ignorekeys=[]): + """ + Augment the LU information that was loaded from the frame file + with additional information from the LU file. + """ + fn_luid = lu.ID + + fname = f"lu{fn_luid}.xml" + locpath = os.path.join(f"{self._root}", self._lu_dir, fname) + # print(locpath, file=sys.stderr) + if not self._lu_idx: + self._buildluindex() + + try: + with XMLCorpusView(locpath, "lexUnit") as view: + elt = view[0] + except OSError as e: + raise FramenetError(f"Unknown LU id: {fn_luid}") from e + + lu2 = self._handle_lexunit_elt(elt, ignorekeys) + lu.URL = self._fnweb_url + "/" + self._lu_dir + "/" + fname + lu.subCorpus = lu2.subCorpus + lu.exemplars = SpecialList( + "luexemplars", [sent for subc in lu.subCorpus for sent in subc.sentence] + ) + for sent in lu.exemplars: + sent["LU"] = lu + sent["frame"] = lu.frame + for aset in sent.annotationSet: + aset["LU"] = lu + aset["frame"] = lu.frame + + return lu + + def _loadsemtypes(self): + """Create the semantic types index.""" + self._semtypes = AttrDict() + with XMLCorpusView( + self.abspath("semTypes.xml"), + "semTypes/semType", + self._handle_semtype_elt, + ) as view: + for st in view: + n = st["name"] + a = st["abbrev"] + i = st["ID"] + # Both name and abbrev should be able to retrieve the + # ID. The ID will retrieve the semantic type dict itself. + self._semtypes[n] = i + self._semtypes[a] = i + self._semtypes[i] = st + # now that all individual semtype XML is loaded, we can link them together + roots = [] + for st in self.semtypes(): + if st.superType: + st.superType = self.semtype(st.superType.supID) + st.superType.subTypes.append(st) + else: + if st not in roots: + roots.append(st) + st.rootType = st + queue = list(roots) + assert queue + while queue: + st = queue.pop(0) + for child in st.subTypes: + child.rootType = st.rootType + queue.append(child) + # self.propagate_semtypes() # apply inferencing over FE relations + + def propagate_semtypes(self): + """ + Apply inference rules to distribute semtypes over relations between FEs. + For FrameNet 1.5, this results in 1011 semtypes being propagated. + (Not done by default because it requires loading all frame files, + which takes several seconds. If this needed to be fast, it could be rewritten + to traverse the neighboring relations on demand for each FE semtype.) + + >>> from nltk.corpus import framenet as fn + >>> x = sum(1 for f in fn.frames() for fe in f.FE.values() if fe.semType) + >>> fn.propagate_semtypes() + >>> y = sum(1 for f in fn.frames() for fe in f.FE.values() if fe.semType) + >>> y-x > 1000 + True + """ + if not self._semtypes: + self._loadsemtypes() + if not self._ferel_idx: + self._buildrelationindex() + changed = True + i = 0 + nPropagations = 0 + while changed: + # make a pass and see if anything needs to be propagated + i += 1 + changed = False + for ferel in self.fe_relations(): + superST = ferel.superFE.semType + subST = ferel.subFE.semType + try: + if superST and superST is not subST: + # propagate downward + assert subST is None or self.semtype_inherits(subST, superST), ( + superST.name, + ferel, + subST.name, + ) + if subST is None: + ferel.subFE.semType = subST = superST + changed = True + nPropagations += 1 + if ( + ferel.type.name in ["Perspective_on", "Subframe", "Precedes"] + and subST + and subST is not superST + ): + # propagate upward + assert superST is None, (superST.name, ferel, subST.name) + ferel.superFE.semType = superST = subST + changed = True + nPropagations += 1 + except AssertionError as ex: + # bug in the data! ignore + # print(ex, file=sys.stderr) + continue + # print(i, nPropagations, file=sys.stderr) + + def semtype(self, key): + """ + >>> from nltk.corpus import framenet as fn + >>> fn.semtype(233).name + 'Temperature' + >>> fn.semtype(233).abbrev + 'Temp' + >>> fn.semtype('Temperature').ID + 233 + + :param key: The name, abbreviation, or id number of the semantic type + :type key: string or int + :return: Information about a semantic type + :rtype: dict + """ + if isinstance(key, int): + stid = key + else: + try: + stid = self._semtypes[key] + except TypeError: + self._loadsemtypes() + stid = self._semtypes[key] + + try: + st = self._semtypes[stid] + except TypeError: + self._loadsemtypes() + st = self._semtypes[stid] + + return st + + def semtype_inherits(self, st, superST): + if not isinstance(st, dict): + st = self.semtype(st) + if not isinstance(superST, dict): + superST = self.semtype(superST) + par = st.superType + while par: + if par is superST: + return True + par = par.superType + return False + + def frames(self, name=None): + """ + Obtain details for a specific frame. + + >>> from nltk.corpus import framenet as fn + >>> len(fn.frames()) in (1019, 1221) # FN 1.5 and 1.7, resp. + True + >>> x = PrettyList(fn.frames(r'(?i)crim'), maxReprSize=0, breakLines=True) + >>> x.sort(key=itemgetter('ID')) + >>> x + [, + , + , + ] + + A brief intro to Frames (excerpted from "FrameNet II: Extended + Theory and Practice" by Ruppenhofer et. al., 2010): + + A Frame is a script-like conceptual structure that describes a + particular type of situation, object, or event along with the + participants and props that are needed for that Frame. For + example, the "Apply_heat" frame describes a common situation + involving a Cook, some Food, and a Heating_Instrument, and is + evoked by words such as bake, blanch, boil, broil, brown, + simmer, steam, etc. + + We call the roles of a Frame "frame elements" (FEs) and the + frame-evoking words are called "lexical units" (LUs). + + FrameNet includes relations between Frames. Several types of + relations are defined, of which the most important are: + + - Inheritance: An IS-A relation. The child frame is a subtype + of the parent frame, and each FE in the parent is bound to + a corresponding FE in the child. An example is the + "Revenge" frame which inherits from the + "Rewards_and_punishments" frame. + + - Using: The child frame presupposes the parent frame as + background, e.g the "Speed" frame "uses" (or presupposes) + the "Motion" frame; however, not all parent FEs need to be + bound to child FEs. + + - Subframe: The child frame is a subevent of a complex event + represented by the parent, e.g. the "Criminal_process" frame + has subframes of "Arrest", "Arraignment", "Trial", and + "Sentencing". + + - Perspective_on: The child frame provides a particular + perspective on an un-perspectivized parent frame. A pair of + examples consists of the "Hiring" and "Get_a_job" frames, + which perspectivize the "Employment_start" frame from the + Employer's and the Employee's point of view, respectively. + + :param name: A regular expression pattern used to match against + Frame names. If 'name' is None, then a list of all + Framenet Frames will be returned. + :type name: str + :return: A list of matching Frames (or all Frames). + :rtype: list(AttrDict) + """ + try: + fIDs = list(self._frame_idx.keys()) + except AttributeError: + self._buildframeindex() + fIDs = list(self._frame_idx.keys()) + + if name is not None: + return PrettyList( + self.frame(fID) for fID, finfo in self.frame_ids_and_names(name).items() + ) + else: + return PrettyLazyMap(self.frame, fIDs) + + def frame_ids_and_names(self, name=None): + """ + Uses the frame index, which is much faster than looking up each frame definition + if only the names and IDs are needed. + """ + if not self._frame_idx: + self._buildframeindex() + return { + fID: finfo.name + for fID, finfo in self._frame_idx.items() + if name is None or re.search(name, finfo.name) is not None + } + + def fes(self, name=None, frame=None): + """ + Lists frame element objects. If 'name' is provided, this is treated as + a case-insensitive regular expression to filter by frame name. + (Case-insensitivity is because casing of frame element names is not always + consistent across frames.) Specify 'frame' to filter by a frame name pattern, + ID, or object. + + >>> from nltk.corpus import framenet as fn + >>> fn.fes('Noise_maker') + [] + >>> sorted([(fe.frame.name,fe.name) for fe in fn.fes('sound')]) # doctest: +NORMALIZE_WHITESPACE + [('Cause_to_make_noise', 'Sound_maker'), ('Make_noise', 'Sound'), + ('Make_noise', 'Sound_source'), ('Sound_movement', 'Location_of_sound_source'), + ('Sound_movement', 'Sound'), ('Sound_movement', 'Sound_source'), + ('Sounds', 'Component_sound'), ('Sounds', 'Location_of_sound_source'), + ('Sounds', 'Sound_source'), ('Vocalizations', 'Location_of_sound_source'), + ('Vocalizations', 'Sound_source')] + >>> sorted([(fe.frame.name,fe.name) for fe in fn.fes('sound',r'(?i)make_noise')]) # doctest: +NORMALIZE_WHITESPACE + [('Cause_to_make_noise', 'Sound_maker'), + ('Make_noise', 'Sound'), + ('Make_noise', 'Sound_source')] + >>> sorted(set(fe.name for fe in fn.fes('^sound'))) + ['Sound', 'Sound_maker', 'Sound_source'] + >>> len(fn.fes('^sound$')) + 2 + + :param name: A regular expression pattern used to match against + frame element names. If 'name' is None, then a list of all + frame elements will be returned. + :type name: str + :return: A list of matching frame elements + :rtype: list(AttrDict) + """ + # what frames are we searching in? + if frame is not None: + if isinstance(frame, int): + frames = [self.frame(frame)] + elif isinstance(frame, str): + frames = self.frames(frame) + else: + frames = [frame] + else: + frames = self.frames() + + return PrettyList( + fe + for f in frames + for fename, fe in f.FE.items() + if name is None or re.search(name, fename, re.I) + ) + + def lus(self, name=None, frame=None): + """ + Obtain details for lexical units. + Optionally restrict by lexical unit name pattern, and/or to a certain frame + or frames whose name matches a pattern. + + >>> from nltk.corpus import framenet as fn + >>> len(fn.lus()) in (11829, 13572) # FN 1.5 and 1.7, resp. + True + >>> PrettyList(sorted(fn.lus(r'(?i)a little'), key=itemgetter('ID')), maxReprSize=0, breakLines=True) + [, + , + ] + >>> PrettyList(sorted(fn.lus(r'interest', r'(?i)stimulus'), key=itemgetter('ID'))) + [, ] + + A brief intro to Lexical Units (excerpted from "FrameNet II: + Extended Theory and Practice" by Ruppenhofer et. al., 2010): + + A lexical unit (LU) is a pairing of a word with a meaning. For + example, the "Apply_heat" Frame describes a common situation + involving a Cook, some Food, and a Heating Instrument, and is + _evoked_ by words such as bake, blanch, boil, broil, brown, + simmer, steam, etc. These frame-evoking words are the LUs in the + Apply_heat frame. Each sense of a polysemous word is a different + LU. + + We have used the word "word" in talking about LUs. The reality + is actually rather complex. When we say that the word "bake" is + polysemous, we mean that the lemma "bake.v" (which has the + word-forms "bake", "bakes", "baked", and "baking") is linked to + three different frames: + + - Apply_heat: "Michelle baked the potatoes for 45 minutes." + + - Cooking_creation: "Michelle baked her mother a cake for her birthday." + + - Absorb_heat: "The potatoes have to bake for more than 30 minutes." + + These constitute three different LUs, with different + definitions. + + Multiword expressions such as "given name" and hyphenated words + like "shut-eye" can also be LUs. Idiomatic phrases such as + "middle of nowhere" and "give the slip (to)" are also defined as + LUs in the appropriate frames ("Isolated_places" and "Evading", + respectively), and their internal structure is not analyzed. + + Framenet provides multiple annotated examples of each sense of a + word (i.e. each LU). Moreover, the set of examples + (approximately 20 per LU) illustrates all of the combinatorial + possibilities of the lexical unit. + + Each LU is linked to a Frame, and hence to the other words which + evoke that Frame. This makes the FrameNet database similar to a + thesaurus, grouping together semantically similar words. + + In the simplest case, frame-evoking words are verbs such as + "fried" in: + + "Matilde fried the catfish in a heavy iron skillet." + + Sometimes event nouns may evoke a Frame. For example, + "reduction" evokes "Cause_change_of_scalar_position" in: + + "...the reduction of debt levels to $665 million from $2.6 billion." + + Adjectives may also evoke a Frame. For example, "asleep" may + evoke the "Sleep" frame as in: + + "They were asleep for hours." + + Many common nouns, such as artifacts like "hat" or "tower", + typically serve as dependents rather than clearly evoking their + own frames. + + :param name: A regular expression pattern used to search the LU + names. Note that LU names take the form of a dotted + string (e.g. "run.v" or "a little.adv") in which a + lemma precedes the "." and a POS follows the + dot. The lemma may be composed of a single lexeme + (e.g. "run") or of multiple lexemes (e.g. "a + little"). If 'name' is not given, then all LUs will + be returned. + + The valid POSes are: + + v - verb + n - noun + a - adjective + adv - adverb + prep - preposition + num - numbers + intj - interjection + art - article + c - conjunction + scon - subordinating conjunction + + :type name: str + :type frame: str or int or frame + :return: A list of selected (or all) lexical units + :rtype: list of LU objects (dicts). See the lu() function for info + about the specifics of LU objects. + + """ + if not self._lu_idx: + self._buildluindex() + + if name is not None: # match LUs, then restrict by frame + result = PrettyList( + self.lu(luID) for luID, luName in self.lu_ids_and_names(name).items() + ) + if frame is not None: + if isinstance(frame, int): + frameIDs = {frame} + elif isinstance(frame, str): + frameIDs = {f.ID for f in self.frames(frame)} + else: + frameIDs = {frame.ID} + result = PrettyList(lu for lu in result if lu.frame.ID in frameIDs) + elif frame is not None: # all LUs in matching frames + if isinstance(frame, int): + frames = [self.frame(frame)] + elif isinstance(frame, str): + frames = self.frames(frame) + else: + frames = [frame] + result = PrettyLazyIteratorList( + iter(LazyConcatenation(list(f.lexUnit.values()) for f in frames)) + ) + else: # all LUs + luIDs = [ + luID + for luID, lu in self._lu_idx.items() + if lu.status not in self._bad_statuses + ] + result = PrettyLazyMap(self.lu, luIDs) + return result + + def lu_ids_and_names(self, name=None): + """ + Uses the LU index, which is much faster than looking up each LU definition + if only the names and IDs are needed. + """ + if not self._lu_idx: + self._buildluindex() + return { + luID: luinfo.name + for luID, luinfo in self._lu_idx.items() + if luinfo.status not in self._bad_statuses + and (name is None or re.search(name, luinfo.name) is not None) + } + + def docs_metadata(self, name=None): + """ + Return an index of the annotated documents in Framenet. + + Details for a specific annotated document can be obtained using this + class's doc() function and pass it the value of the 'ID' field. + + >>> from nltk.corpus import framenet as fn + >>> len(fn.docs()) in (78, 107) # FN 1.5 and 1.7, resp. + True + >>> set([x.corpname for x in fn.docs_metadata()])>=set(['ANC', 'KBEval', \ + 'LUCorpus-v0.3', 'Miscellaneous', 'NTI', 'PropBank']) + True + + :param name: A regular expression pattern used to search the + file name of each annotated document. The document's + file name contains the name of the corpus that the + document is from, followed by two underscores "__" + followed by the document name. So, for example, the + file name "LUCorpus-v0.3__20000410_nyt-NEW.xml" is + from the corpus named "LUCorpus-v0.3" and the + document name is "20000410_nyt-NEW.xml". + :type name: str + :return: A list of selected (or all) annotated documents + :rtype: list of dicts, where each dict object contains the following + keys: + + - 'name' + - 'ID' + - 'corpid' + - 'corpname' + - 'description' + - 'filename' + """ + try: + ftlist = PrettyList(self._fulltext_idx.values()) + except AttributeError: + self._buildcorpusindex() + ftlist = PrettyList(self._fulltext_idx.values()) + + if name is None: + return ftlist + else: + return PrettyList( + x for x in ftlist if re.search(name, x["filename"]) is not None + ) + + def docs(self, name=None): + """ + Return a list of the annotated full-text documents in FrameNet, + optionally filtered by a regex to be matched against the document name. + """ + return PrettyLazyMap((lambda x: self.doc(x.ID)), self.docs_metadata(name)) + + def sents(self, exemplars=True, full_text=True): + """ + Annotated sentences matching the specified criteria. + """ + if exemplars: + if full_text: + return self.exemplars() + self.ft_sents() + else: + return self.exemplars() + elif full_text: + return self.ft_sents() + + def annotations(self, luNamePattern=None, exemplars=True, full_text=True): + """ + Frame annotation sets matching the specified criteria. + """ + + if exemplars: + epart = PrettyLazyIteratorList( + sent.frameAnnotation for sent in self.exemplars(luNamePattern) + ) + else: + epart = [] + + if full_text: + if luNamePattern is not None: + matchedLUIDs = set(self.lu_ids_and_names(luNamePattern).keys()) + ftpart = PrettyLazyIteratorList( + aset + for sent in self.ft_sents() + for aset in sent.annotationSet[1:] + if luNamePattern is None or aset.get("luID", "CXN_ASET") in matchedLUIDs + ) + else: + ftpart = [] + + if exemplars: + if full_text: + return epart + ftpart + else: + return epart + elif full_text: + return ftpart + + def exemplars(self, luNamePattern=None, frame=None, fe=None, fe2=None): + """ + Lexicographic exemplar sentences, optionally filtered by LU name and/or 1-2 FEs that + are realized overtly. 'frame' may be a name pattern, frame ID, or frame instance. + 'fe' may be a name pattern or FE instance; if specified, 'fe2' may also + be specified to retrieve sentences with both overt FEs (in either order). + """ + if fe is None and fe2 is not None: + raise FramenetError("exemplars(..., fe=None, fe2=) is not allowed") + elif fe is not None and fe2 is not None: + if not isinstance(fe2, str): + if isinstance(fe, str): + # fe2 is specific to a particular frame. swap fe and fe2 so fe is always used to determine the frame. + fe, fe2 = fe2, fe + elif fe.frame is not fe2.frame: # ensure frames match + raise FramenetError( + "exemplars() call with inconsistent `fe` and `fe2` specification (frames must match)" + ) + if frame is None and fe is not None and not isinstance(fe, str): + frame = fe.frame + + # narrow down to frames matching criteria + + lusByFrame = defaultdict( + list + ) # frame name -> matching LUs, if luNamePattern is specified + if frame is not None or luNamePattern is not None: + if frame is None or isinstance(frame, str): + if luNamePattern is not None: + frames = set() + for lu in self.lus(luNamePattern, frame=frame): + frames.add(lu.frame.ID) + lusByFrame[lu.frame.name].append(lu) + frames = LazyMap(self.frame, list(frames)) + else: + frames = self.frames(frame) + else: + if isinstance(frame, int): + frames = [self.frame(frame)] + else: # frame object + frames = [frame] + + if luNamePattern is not None: + lusByFrame = {frame.name: self.lus(luNamePattern, frame=frame)} + + if fe is not None: # narrow to frames that define this FE + if isinstance(fe, str): + frames = PrettyLazyIteratorList( + f + for f in frames + if fe in f.FE + or any(re.search(fe, ffe, re.I) for ffe in f.FE.keys()) + ) + else: + if fe.frame not in frames: + raise FramenetError( + "exemplars() call with inconsistent `frame` and `fe` specification" + ) + frames = [fe.frame] + + if fe2 is not None: # narrow to frames that ALSO define this FE + if isinstance(fe2, str): + frames = PrettyLazyIteratorList( + f + for f in frames + if fe2 in f.FE + or any(re.search(fe2, ffe, re.I) for ffe in f.FE.keys()) + ) + # else we already narrowed it to a single frame + else: # frame, luNamePattern are None. fe, fe2 are None or strings + if fe is not None: + frames = {ffe.frame.ID for ffe in self.fes(fe)} + if fe2 is not None: + frames2 = {ffe.frame.ID for ffe in self.fes(fe2)} + frames = frames & frames2 + frames = LazyMap(self.frame, list(frames)) + else: + frames = self.frames() + + # we've narrowed down 'frames' + # now get exemplars for relevant LUs in those frames + + def _matching_exs(): + for f in frames: + fes = fes2 = None # FEs of interest + if fe is not None: + fes = ( + {ffe for ffe in f.FE.keys() if re.search(fe, ffe, re.I)} + if isinstance(fe, str) + else {fe.name} + ) + if fe2 is not None: + fes2 = ( + {ffe for ffe in f.FE.keys() if re.search(fe2, ffe, re.I)} + if isinstance(fe2, str) + else {fe2.name} + ) + + for lu in ( + lusByFrame[f.name] + if luNamePattern is not None + else f.lexUnit.values() + ): + for ex in lu.exemplars: + if (fes is None or self._exemplar_of_fes(ex, fes)) and ( + fes2 is None or self._exemplar_of_fes(ex, fes2) + ): + yield ex + + return PrettyLazyIteratorList(_matching_exs()) + + def _exemplar_of_fes(self, ex, fes=None): + """ + Given an exemplar sentence and a set of FE names, return the subset of FE names + that are realized overtly in the sentence on the FE, FE2, or FE3 layer. + + If 'fes' is None, returns all overt FE names. + """ + overtNames = set(list(zip(*ex.FE[0]))[2]) if ex.FE[0] else set() + if "FE2" in ex: + overtNames |= set(list(zip(*ex.FE2[0]))[2]) if ex.FE2[0] else set() + if "FE3" in ex: + overtNames |= set(list(zip(*ex.FE3[0]))[2]) if ex.FE3[0] else set() + return overtNames & fes if fes is not None else overtNames + + def ft_sents(self, docNamePattern=None): + """ + Full-text annotation sentences, optionally filtered by document name. + """ + return PrettyLazyIteratorList( + sent for d in self.docs(docNamePattern) for sent in d.sentence + ) + + def frame_relation_types(self): + """ + Obtain a list of frame relation types. + + >>> from nltk.corpus import framenet as fn + >>> frts = sorted(fn.frame_relation_types(), key=itemgetter('ID')) + >>> isinstance(frts, list) + True + >>> len(frts) in (9, 10) # FN 1.5 and 1.7, resp. + True + >>> PrettyDict(frts[0], breakLines=True) + {'ID': 1, + '_type': 'framerelationtype', + 'frameRelations': [ Child=Change_of_consistency>, Child=Rotting>, ...], + 'name': 'Inheritance', + 'subFrameName': 'Child', + 'superFrameName': 'Parent'} + + :return: A list of all of the frame relation types in framenet + :rtype: list(dict) + """ + if not self._freltyp_idx: + self._buildrelationindex() + return self._freltyp_idx.values() + + def frame_relations(self, frame=None, frame2=None, type=None): + """ + :param frame: (optional) frame object, name, or ID; only relations involving + this frame will be returned + :param frame2: (optional; 'frame' must be a different frame) only show relations + between the two specified frames, in either direction + :param type: (optional) frame relation type (name or object); show only relations + of this type + :type frame: int or str or AttrDict + :return: A list of all of the frame relations in framenet + :rtype: list(dict) + + >>> from nltk.corpus import framenet as fn + >>> frels = fn.frame_relations() + >>> isinstance(frels, list) + True + >>> len(frels) in (1676, 2070) # FN 1.5 and 1.7, resp. + True + >>> PrettyList(fn.frame_relations('Cooking_creation'), maxReprSize=0, breakLines=True) + [ Child=Cooking_creation>, + Child=Cooking_creation>, + ReferringEntry=Cooking_creation>] + >>> PrettyList(fn.frame_relations(274), breakLines=True) + [ Child=Dodging>, + Child=Evading>, ...] + >>> PrettyList(fn.frame_relations(fn.frame('Cooking_creation')), breakLines=True) + [ Child=Cooking_creation>, + Child=Cooking_creation>, ...] + >>> PrettyList(fn.frame_relations('Cooking_creation', type='Inheritance')) + [ Child=Cooking_creation>] + >>> PrettyList(fn.frame_relations('Cooking_creation', 'Apply_heat'), breakLines=True) # doctest: +NORMALIZE_WHITESPACE + [ Child=Cooking_creation>, + ReferringEntry=Cooking_creation>] + """ + relation_type = type + + if not self._frel_idx: + self._buildrelationindex() + + rels = None + + if relation_type is not None: + if not isinstance(relation_type, dict): + type = [rt for rt in self.frame_relation_types() if rt.name == type][0] + assert isinstance(type, dict) + + # lookup by 'frame' + if frame is not None: + if isinstance(frame, dict) and "frameRelations" in frame: + rels = PrettyList(frame.frameRelations) + else: + if not isinstance(frame, int): + if isinstance(frame, dict): + frame = frame.ID + else: + frame = self.frame_by_name(frame).ID + rels = [self._frel_idx[frelID] for frelID in self._frel_f_idx[frame]] + + # filter by 'type' + if type is not None: + rels = [rel for rel in rels if rel.type is type] + elif type is not None: + # lookup by 'type' + rels = type.frameRelations + else: + rels = self._frel_idx.values() + + # filter by 'frame2' + if frame2 is not None: + if frame is None: + raise FramenetError( + "frame_relations(frame=None, frame2=) is not allowed" + ) + if not isinstance(frame2, int): + if isinstance(frame2, dict): + frame2 = frame2.ID + else: + frame2 = self.frame_by_name(frame2).ID + if frame == frame2: + raise FramenetError( + "The two frame arguments to frame_relations() must be different frames" + ) + rels = [ + rel + for rel in rels + if rel.superFrame.ID == frame2 or rel.subFrame.ID == frame2 + ] + + return PrettyList( + sorted( + rels, + key=lambda frel: (frel.type.ID, frel.superFrameName, frel.subFrameName), + ) + ) + + def fe_relations(self): + """ + Obtain a list of frame element relations. + + >>> from nltk.corpus import framenet as fn + >>> ferels = fn.fe_relations() + >>> isinstance(ferels, list) + True + >>> len(ferels) in (10020, 12393) # FN 1.5 and 1.7, resp. + True + >>> PrettyDict(ferels[0], breakLines=True) # doctest: +NORMALIZE_WHITESPACE + {'ID': 14642, + '_type': 'ferelation', + 'frameRelation': Child=Lively_place>, + 'subFE': , + 'subFEName': 'Degree', + 'subFrame': , + 'subID': 11370, + 'supID': 2271, + 'superFE': , + 'superFEName': 'Degree', + 'superFrame': , + 'type': } + + :return: A list of all of the frame element relations in framenet + :rtype: list(dict) + """ + if not self._ferel_idx: + self._buildrelationindex() + return PrettyList( + sorted( + self._ferel_idx.values(), + key=lambda ferel: ( + ferel.type.ID, + ferel.frameRelation.superFrameName, + ferel.superFEName, + ferel.frameRelation.subFrameName, + ferel.subFEName, + ), + ) + ) + + def semtypes(self): + """ + Obtain a list of semantic types. + + >>> from nltk.corpus import framenet as fn + >>> stypes = fn.semtypes() + >>> len(stypes) in (73, 109) # FN 1.5 and 1.7, resp. + True + >>> sorted(stypes[0].keys()) + ['ID', '_type', 'abbrev', 'definition', 'definitionMarkup', 'name', 'rootType', 'subTypes', 'superType'] + + :return: A list of all of the semantic types in framenet + :rtype: list(dict) + """ + if not self._semtypes: + self._loadsemtypes() + return PrettyList( + self._semtypes[i] for i in self._semtypes if isinstance(i, int) + ) + + def _load_xml_attributes(self, d, elt): + """ + Extracts a subset of the attributes from the given element and + returns them in a dictionary. + + :param d: A dictionary in which to store the attributes. + :type d: dict + :param elt: An ElementTree Element + :type elt: Element + :return: Returns the input dict ``d`` possibly including attributes from ``elt`` + :rtype: dict + """ + + d = type(d)(d) + + try: + attr_dict = elt.attrib + except AttributeError: + return d + + if attr_dict is None: + return d + + # Ignore these attributes when loading attributes from an xml node + ignore_attrs = [ #'cBy', 'cDate', 'mDate', # <-- annotation metadata that could be of interest + "xsi", + "schemaLocation", + "xmlns", + "bgColor", + "fgColor", + ] + + for attr in attr_dict: + + if any(attr.endswith(x) for x in ignore_attrs): + continue + + val = attr_dict[attr] + if val.isdigit(): + d[attr] = int(val) + else: + d[attr] = val + + return d + + def _strip_tags(self, data): + """ + Gets rid of all tags and newline characters from the given input + + :return: A cleaned-up version of the input string + :rtype: str + """ + + try: + r""" + # Look for boundary issues in markup. (Sometimes FEs are pluralized in definitions.) + m = re.search(r'\w[<][^/]|[<][/][^>]+[>](s\w|[a-rt-z0-9])', data) + if m: + print('Markup boundary:', data[max(0,m.start(0)-10):m.end(0)+10].replace('\n',' '), file=sys.stderr) + """ + + data = data.replace("", "") + data = data.replace("", "") + data = re.sub('', "", data) + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "'") + data = data.replace("", "'") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("", "") + + # Get rid of and tags + data = data.replace("", "") + data = data.replace("", "") + + data = data.replace("\n", " ") + except AttributeError: + pass + + return data + + def _handle_elt(self, elt, tagspec=None): + """Extracts and returns the attributes of the given element""" + return self._load_xml_attributes(AttrDict(), elt) + + def _handle_fulltextindex_elt(self, elt, tagspec=None): + """ + Extracts corpus/document info from the fulltextIndex.xml file. + + Note that this function "flattens" the information contained + in each of the "corpus" elements, so that each "document" + element will contain attributes for the corpus and + corpusid. Also, each of the "document" items will contain a + new attribute called "filename" that is the base file name of + the xml file for the document in the "fulltext" subdir of the + Framenet corpus. + """ + ftinfo = self._load_xml_attributes(AttrDict(), elt) + corpname = ftinfo.name + corpid = ftinfo.ID + retlist = [] + for sub in elt: + if sub.tag.endswith("document"): + doc = self._load_xml_attributes(AttrDict(), sub) + if "name" in doc: + docname = doc.name + else: + docname = doc.description + doc.filename = f"{corpname}__{docname}.xml" + doc.URL = ( + self._fnweb_url + "/" + self._fulltext_dir + "/" + doc.filename + ) + doc.corpname = corpname + doc.corpid = corpid + retlist.append(doc) + + return retlist + + def _handle_frame_elt(self, elt, ignorekeys=[]): + """Load the info for a Frame from a frame xml file""" + frinfo = self._load_xml_attributes(AttrDict(), elt) + + frinfo["_type"] = "frame" + frinfo["definition"] = "" + frinfo["definitionMarkup"] = "" + frinfo["FE"] = PrettyDict() + frinfo["FEcoreSets"] = [] + frinfo["lexUnit"] = PrettyDict() + frinfo["semTypes"] = [] + for k in ignorekeys: + if k in frinfo: + del frinfo[k] + + for sub in elt: + if sub.tag.endswith("definition") and "definition" not in ignorekeys: + frinfo["definitionMarkup"] = sub.text + frinfo["definition"] = self._strip_tags(sub.text) + elif sub.tag.endswith("FE") and "FE" not in ignorekeys: + feinfo = self._handle_fe_elt(sub) + frinfo["FE"][feinfo.name] = feinfo + feinfo["frame"] = frinfo # backpointer + elif sub.tag.endswith("FEcoreSet") and "FEcoreSet" not in ignorekeys: + coreset = self._handle_fecoreset_elt(sub) + # assumes all FEs have been loaded before coresets + frinfo["FEcoreSets"].append( + PrettyList(frinfo["FE"][fe.name] for fe in coreset) + ) + elif sub.tag.endswith("lexUnit") and "lexUnit" not in ignorekeys: + luentry = self._handle_framelexunit_elt(sub) + if luentry["status"] in self._bad_statuses: + # problematic LU entry; ignore it + continue + luentry["frame"] = frinfo + luentry["URL"] = ( + self._fnweb_url + + "/" + + self._lu_dir + + "/" + + "lu{}.xml".format(luentry["ID"]) + ) + luentry["subCorpus"] = Future( + (lambda lu: lambda: self._lu_file(lu).subCorpus)(luentry) + ) + luentry["exemplars"] = Future( + (lambda lu: lambda: self._lu_file(lu).exemplars)(luentry) + ) + frinfo["lexUnit"][luentry.name] = luentry + if not self._lu_idx: + self._buildluindex() + self._lu_idx[luentry.ID] = luentry + elif sub.tag.endswith("semType") and "semTypes" not in ignorekeys: + semtypeinfo = self._load_xml_attributes(AttrDict(), sub) + frinfo["semTypes"].append(self.semtype(semtypeinfo.ID)) + + frinfo["frameRelations"] = self.frame_relations(frame=frinfo) + + # resolve 'requires' and 'excludes' links between FEs of this frame + for fe in frinfo.FE.values(): + if fe.requiresFE: + name, ID = fe.requiresFE.name, fe.requiresFE.ID + fe.requiresFE = frinfo.FE[name] + assert fe.requiresFE.ID == ID + if fe.excludesFE: + name, ID = fe.excludesFE.name, fe.excludesFE.ID + fe.excludesFE = frinfo.FE[name] + assert fe.excludesFE.ID == ID + + return frinfo + + def _handle_fecoreset_elt(self, elt): + """Load fe coreset info from xml.""" + info = self._load_xml_attributes(AttrDict(), elt) + tmp = [] + for sub in elt: + tmp.append(self._load_xml_attributes(AttrDict(), sub)) + + return tmp + + def _handle_framerelationtype_elt(self, elt, *args): + """Load frame-relation element and its child fe-relation elements from frRelation.xml.""" + info = self._load_xml_attributes(AttrDict(), elt) + info["_type"] = "framerelationtype" + info["frameRelations"] = PrettyList() + + for sub in elt: + if sub.tag.endswith("frameRelation"): + frel = self._handle_framerelation_elt(sub) + frel["type"] = info # backpointer + for ferel in frel.feRelations: + ferel["type"] = info + info["frameRelations"].append(frel) + + return info + + def _handle_framerelation_elt(self, elt): + """Load frame-relation element and its child fe-relation elements from frRelation.xml.""" + info = self._load_xml_attributes(AttrDict(), elt) + assert info["superFrameName"] != info["subFrameName"], (elt, info) + info["_type"] = "framerelation" + info["feRelations"] = PrettyList() + + for sub in elt: + if sub.tag.endswith("FERelation"): + ferel = self._handle_elt(sub) + ferel["_type"] = "ferelation" + ferel["frameRelation"] = info # backpointer + info["feRelations"].append(ferel) + + return info + + def _handle_fulltextannotation_elt(self, elt): + """Load full annotation info for a document from its xml + file. The main element (fullTextAnnotation) contains a 'header' + element (which we ignore here) and a bunch of 'sentence' + elements.""" + info = AttrDict() + info["_type"] = "fulltext_annotation" + info["sentence"] = [] + + for sub in elt: + if sub.tag.endswith("header"): + continue # not used + elif sub.tag.endswith("sentence"): + s = self._handle_fulltext_sentence_elt(sub) + s.doc = info + info["sentence"].append(s) + + return info + + def _handle_fulltext_sentence_elt(self, elt): + """Load information from the given 'sentence' element. Each + 'sentence' element contains a "text" and "annotationSet" sub + elements.""" + info = self._load_xml_attributes(AttrDict(), elt) + info["_type"] = "fulltext_sentence" + info["annotationSet"] = [] + info["targets"] = [] + target_spans = set() + info["_ascii"] = types.MethodType( + _annotation_ascii, info + ) # attach a method for this instance + info["text"] = "" + + for sub in elt: + if sub.tag.endswith("text"): + info["text"] = self._strip_tags(sub.text) + elif sub.tag.endswith("annotationSet"): + a = self._handle_fulltextannotationset_elt( + sub, is_pos=(len(info["annotationSet"]) == 0) + ) + if "cxnID" in a: # ignoring construction annotations for now + continue + a.sent = info + a.text = info.text + info["annotationSet"].append(a) + if "Target" in a: + for tspan in a.Target: + if tspan in target_spans: + self._warn( + 'Duplicate target span "{}"'.format( + info.text[slice(*tspan)] + ), + tspan, + "in sentence", + info["ID"], + info.text, + ) + # this can happen in cases like "chemical and biological weapons" + # being annotated as "chemical weapons" and "biological weapons" + else: + target_spans.add(tspan) + info["targets"].append((a.Target, a.luName, a.frameName)) + + assert info["annotationSet"][0].status == "UNANN" + info["POS"] = info["annotationSet"][0].POS + info["POS_tagset"] = info["annotationSet"][0].POS_tagset + return info + + def _handle_fulltextannotationset_elt(self, elt, is_pos=False): + """Load information from the given 'annotationSet' element. Each + 'annotationSet' contains several "layer" elements.""" + + info = self._handle_luannotationset_elt(elt, is_pos=is_pos) + if not is_pos: + info["_type"] = "fulltext_annotationset" + if "cxnID" not in info: # ignoring construction annotations for now + info["LU"] = self.lu( + info.luID, + luName=info.luName, + frameID=info.frameID, + frameName=info.frameName, + ) + info["frame"] = info.LU.frame + return info + + def _handle_fulltextlayer_elt(self, elt): + """Load information from the given 'layer' element. Each + 'layer' contains several "label" elements.""" + info = self._load_xml_attributes(AttrDict(), elt) + info["_type"] = "layer" + info["label"] = [] + + for sub in elt: + if sub.tag.endswith("label"): + l = self._load_xml_attributes(AttrDict(), sub) + info["label"].append(l) + + return info + + def _handle_framelexunit_elt(self, elt): + """Load the lexical unit info from an xml element in a frame's xml file.""" + luinfo = AttrDict() + luinfo["_type"] = "lu" + luinfo = self._load_xml_attributes(luinfo, elt) + luinfo["definition"] = "" + luinfo["definitionMarkup"] = "" + luinfo["sentenceCount"] = PrettyDict() + luinfo["lexemes"] = PrettyList() # multiword LUs have multiple lexemes + luinfo["semTypes"] = PrettyList() # an LU can have multiple semtypes + + for sub in elt: + if sub.tag.endswith("definition"): + luinfo["definitionMarkup"] = sub.text + luinfo["definition"] = self._strip_tags(sub.text) + elif sub.tag.endswith("sentenceCount"): + luinfo["sentenceCount"] = self._load_xml_attributes(PrettyDict(), sub) + elif sub.tag.endswith("lexeme"): + lexemeinfo = self._load_xml_attributes(PrettyDict(), sub) + if not isinstance(lexemeinfo.name, str): + # some lexeme names are ints by default: e.g., + # thousand.num has lexeme with name="1000" + lexemeinfo.name = str(lexemeinfo.name) + luinfo["lexemes"].append(lexemeinfo) + elif sub.tag.endswith("semType"): + semtypeinfo = self._load_xml_attributes(PrettyDict(), sub) + luinfo["semTypes"].append(self.semtype(semtypeinfo.ID)) + + # sort lexemes by 'order' attribute + # otherwise, e.g., 'write down.v' may have lexemes in wrong order + luinfo["lexemes"].sort(key=lambda x: x.order) + + return luinfo + + def _handle_lexunit_elt(self, elt, ignorekeys): + """ + Load full info for a lexical unit from its xml file. + This should only be called when accessing corpus annotations + (which are not included in frame files). + """ + luinfo = self._load_xml_attributes(AttrDict(), elt) + luinfo["_type"] = "lu" + luinfo["definition"] = "" + luinfo["definitionMarkup"] = "" + luinfo["subCorpus"] = PrettyList() + luinfo["lexemes"] = PrettyList() # multiword LUs have multiple lexemes + luinfo["semTypes"] = PrettyList() # an LU can have multiple semtypes + for k in ignorekeys: + if k in luinfo: + del luinfo[k] + + for sub in elt: + if sub.tag.endswith("header"): + continue # not used + elif sub.tag.endswith("valences"): + continue # not used + elif sub.tag.endswith("definition") and "definition" not in ignorekeys: + luinfo["definitionMarkup"] = sub.text + luinfo["definition"] = self._strip_tags(sub.text) + elif sub.tag.endswith("subCorpus") and "subCorpus" not in ignorekeys: + sc = self._handle_lusubcorpus_elt(sub) + if sc is not None: + luinfo["subCorpus"].append(sc) + elif sub.tag.endswith("lexeme") and "lexeme" not in ignorekeys: + luinfo["lexemes"].append(self._load_xml_attributes(PrettyDict(), sub)) + elif sub.tag.endswith("semType") and "semType" not in ignorekeys: + semtypeinfo = self._load_xml_attributes(AttrDict(), sub) + luinfo["semTypes"].append(self.semtype(semtypeinfo.ID)) + + return luinfo + + def _handle_lusubcorpus_elt(self, elt): + """Load a subcorpus of a lexical unit from the given xml.""" + sc = AttrDict() + try: + sc["name"] = elt.get("name") + except AttributeError: + return None + sc["_type"] = "lusubcorpus" + sc["sentence"] = [] + + for sub in elt: + if sub.tag.endswith("sentence"): + s = self._handle_lusentence_elt(sub) + if s is not None: + sc["sentence"].append(s) + + return sc + + def _handle_lusentence_elt(self, elt): + """Load a sentence from a subcorpus of an LU from xml.""" + info = self._load_xml_attributes(AttrDict(), elt) + info["_type"] = "lusentence" + info["annotationSet"] = [] + info["_ascii"] = types.MethodType( + _annotation_ascii, info + ) # attach a method for this instance + for sub in elt: + if sub.tag.endswith("text"): + info["text"] = self._strip_tags(sub.text) + elif sub.tag.endswith("annotationSet"): + annset = self._handle_luannotationset_elt( + sub, is_pos=(len(info["annotationSet"]) == 0) + ) + if annset is not None: + assert annset.status == "UNANN" or "FE" in annset, annset + if annset.status != "UNANN": + info["frameAnnotation"] = annset + # copy layer info up to current level + for k in ( + "Target", + "FE", + "FE2", + "FE3", + "GF", + "PT", + "POS", + "POS_tagset", + "Other", + "Sent", + "Verb", + "Noun", + "Adj", + "Adv", + "Prep", + "Scon", + "Art", + ): + if k in annset: + info[k] = annset[k] + info["annotationSet"].append(annset) + annset["sent"] = info + annset["text"] = info.text + return info + + def _handle_luannotationset_elt(self, elt, is_pos=False): + """Load an annotation set from a sentence in an subcorpus of an LU""" + info = self._load_xml_attributes(AttrDict(), elt) + info["_type"] = "posannotationset" if is_pos else "luannotationset" + info["layer"] = [] + info["_ascii"] = types.MethodType( + _annotation_ascii, info + ) # attach a method for this instance + + if "cxnID" in info: # ignoring construction annotations for now. + return info + + for sub in elt: + if sub.tag.endswith("layer"): + l = self._handle_lulayer_elt(sub) + if l is not None: + overt = [] + ni = {} # null instantiations + + info["layer"].append(l) + for lbl in l.label: + if "start" in lbl: + thespan = (lbl.start, lbl.end + 1, lbl.name) + if l.name not in ( + "Sent", + "Other", + ): # 'Sent' and 'Other' layers sometimes contain accidental duplicate spans + assert thespan not in overt, (info.ID, l.name, thespan) + overt.append(thespan) + else: # null instantiation + if lbl.name in ni: + self._warn( + "FE with multiple NI entries:", + lbl.name, + ni[lbl.name], + lbl.itype, + ) + else: + ni[lbl.name] = lbl.itype + overt = sorted(overt) + + if l.name == "Target": + if not overt: + self._warn( + "Skipping empty Target layer in annotation set ID={}".format( + info.ID + ) + ) + continue + assert all(lblname == "Target" for i, j, lblname in overt) + if "Target" in info: + self._warn( + "Annotation set {} has multiple Target layers".format( + info.ID + ) + ) + else: + info["Target"] = [(i, j) for (i, j, _) in overt] + elif l.name == "FE": + if l.rank == 1: + assert "FE" not in info + info["FE"] = (overt, ni) + # assert False,info + else: + # sometimes there are 3 FE layers! e.g. Change_position_on_a_scale.fall.v + assert 2 <= l.rank <= 3, l.rank + k = "FE" + str(l.rank) + assert k not in info + info[k] = (overt, ni) + elif l.name in ("GF", "PT"): + assert l.rank == 1 + info[l.name] = overt + elif l.name in ("BNC", "PENN"): + assert l.rank == 1 + info["POS"] = overt + info["POS_tagset"] = l.name + else: + if is_pos: + if l.name not in ("NER", "WSL"): + self._warn( + "Unexpected layer in sentence annotationset:", + l.name, + ) + else: + if l.name not in ( + "Sent", + "Verb", + "Noun", + "Adj", + "Adv", + "Prep", + "Scon", + "Art", + "Other", + ): + self._warn( + "Unexpected layer in frame annotationset:", l.name + ) + info[l.name] = overt + if not is_pos and "cxnID" not in info: + if "Target" not in info: + self._warn(f"Missing target in annotation set ID={info.ID}") + assert "FE" in info + if "FE3" in info: + assert "FE2" in info + + return info + + def _handle_lulayer_elt(self, elt): + """Load a layer from an annotation set""" + layer = self._load_xml_attributes(AttrDict(), elt) + layer["_type"] = "lulayer" + layer["label"] = [] + + for sub in elt: + if sub.tag.endswith("label"): + l = self._load_xml_attributes(AttrDict(), sub) + if l is not None: + layer["label"].append(l) + return layer + + def _handle_fe_elt(self, elt): + feinfo = self._load_xml_attributes(AttrDict(), elt) + feinfo["_type"] = "fe" + feinfo["definition"] = "" + feinfo["definitionMarkup"] = "" + feinfo["semType"] = None + feinfo["requiresFE"] = None + feinfo["excludesFE"] = None + for sub in elt: + if sub.tag.endswith("definition"): + feinfo["definitionMarkup"] = sub.text + feinfo["definition"] = self._strip_tags(sub.text) + elif sub.tag.endswith("semType"): + stinfo = self._load_xml_attributes(AttrDict(), sub) + feinfo["semType"] = self.semtype(stinfo.ID) + elif sub.tag.endswith("requiresFE"): + feinfo["requiresFE"] = self._load_xml_attributes(AttrDict(), sub) + elif sub.tag.endswith("excludesFE"): + feinfo["excludesFE"] = self._load_xml_attributes(AttrDict(), sub) + + return feinfo + + def _handle_semtype_elt(self, elt, tagspec=None): + semt = self._load_xml_attributes(AttrDict(), elt) + semt["_type"] = "semtype" + semt["superType"] = None + semt["subTypes"] = PrettyList() + for sub in elt: + if sub.text is not None: + semt["definitionMarkup"] = sub.text + semt["definition"] = self._strip_tags(sub.text) + else: + supertypeinfo = self._load_xml_attributes(AttrDict(), sub) + semt["superType"] = supertypeinfo + # the supertype may not have been loaded yet + + return semt + + +# +# Demo +# +def demo(): + from nltk.corpus import framenet as fn + + # + # It is not necessary to explicitly build the indexes by calling + # buildindexes(). We do this here just for demo purposes. If the + # indexes are not built explicitly, they will be built as needed. + # + print("Building the indexes...") + fn.buildindexes() + + # + # Get some statistics about the corpus + # + print("Number of Frames:", len(fn.frames())) + print("Number of Lexical Units:", len(fn.lus())) + print("Number of annotated documents:", len(fn.docs())) + print() + + # + # Frames + # + print( + 'getting frames whose name matches the (case insensitive) regex: "(?i)medical"' + ) + medframes = fn.frames(r"(?i)medical") + print(f'Found {len(medframes)} Frames whose name matches "(?i)medical":') + print([(f.name, f.ID) for f in medframes]) + + # + # store the first frame in the list of frames + # + tmp_id = medframes[0].ID + m_frame = fn.frame(tmp_id) # reads all info for the frame + + # + # get the frame relations + # + print( + '\nNumber of frame relations for the "{}" ({}) frame:'.format( + m_frame.name, m_frame.ID + ), + len(m_frame.frameRelations), + ) + for fr in m_frame.frameRelations: + print(" ", fr) + + # + # get the names of the Frame Elements + # + print( + f'\nNumber of Frame Elements in the "{m_frame.name}" frame:', + len(m_frame.FE), + ) + print(" ", [x for x in m_frame.FE]) + + # + # get the names of the "Core" Frame Elements + # + print(f'\nThe "core" Frame Elements in the "{m_frame.name}" frame:') + print(" ", [x.name for x in m_frame.FE.values() if x.coreType == "Core"]) + + # + # get all of the Lexical Units that are incorporated in the + # 'Ailment' FE of the 'Medical_conditions' frame (id=239) + # + print('\nAll Lexical Units that are incorporated in the "Ailment" FE:') + m_frame = fn.frame(239) + ailment_lus = [ + x + for x in m_frame.lexUnit.values() + if "incorporatedFE" in x and x.incorporatedFE == "Ailment" + ] + print(" ", [x.name for x in ailment_lus]) + + # + # get all of the Lexical Units for the frame + # + print( + f'\nNumber of Lexical Units in the "{m_frame.name}" frame:', + len(m_frame.lexUnit), + ) + print(" ", [x.name for x in m_frame.lexUnit.values()][:5], "...") + + # + # get basic info on the second LU in the frame + # + tmp_id = m_frame.lexUnit["ailment.n"].ID # grab the id of the specified LU + luinfo = fn.lu_basic(tmp_id) # get basic info on the LU + print(f"\nInformation on the LU: {luinfo.name}") + pprint(luinfo) + + # + # Get a list of all of the corpora used for fulltext annotation + # + print("\nNames of all of the corpora used for fulltext annotation:") + allcorpora = {x.corpname for x in fn.docs_metadata()} + pprint(list(allcorpora)) + + # + # Get the names of the annotated documents in the first corpus + # + firstcorp = list(allcorpora)[0] + firstcorp_docs = fn.docs(firstcorp) + print(f'\nNames of the annotated documents in the "{firstcorp}" corpus:') + pprint([x.filename for x in firstcorp_docs]) + + # + # Search for frames containing LUs whose name attribute matches a + # regexp pattern. + # + # Note: if you were going to be doing a lot of this type of + # searching, you'd want to build an index that maps from + # lemmas to frames because each time frames_by_lemma() is + # called, it has to search through ALL of the frame XML files + # in the db. + print( + '\nSearching for all Frames that have a lemma that matches the regexp: "^run.v$":' + ) + pprint(fn.frames_by_lemma(r"^run.v$")) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ieer.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ieer.py new file mode 100644 index 0000000..ecbe2fb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ieer.py @@ -0,0 +1,116 @@ +# Natural Language Toolkit: IEER Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for the Information Extraction and Entity Recognition Corpus. + +NIST 1999 Information Extraction: Entity Recognition Evaluation +https://www.itl.nist.gov/iad/894.01/tests/ie-er/er_99/er_99.htm + +This corpus contains the NEWSWIRE development test data for the +NIST 1999 IE-ER Evaluation. The files were taken from the +subdirectory: ``/ie_er_99/english/devtest/newswire/*.ref.nwt`` +and filenames were shortened. + +The corpus contains the following files: APW_19980314, APW_19980424, +APW_19980429, NYT_19980315, NYT_19980403, and NYT_19980407. +""" + +import nltk +from nltk.corpus.reader.api import * + +#: A dictionary whose keys are the names of documents in this corpus; +#: and whose values are descriptions of those documents' contents. +titles = { + "APW_19980314": "Associated Press Weekly, 14 March 1998", + "APW_19980424": "Associated Press Weekly, 24 April 1998", + "APW_19980429": "Associated Press Weekly, 29 April 1998", + "NYT_19980315": "New York Times, 15 March 1998", + "NYT_19980403": "New York Times, 3 April 1998", + "NYT_19980407": "New York Times, 7 April 1998", +} + +#: A list of all documents in this corpus. +documents = sorted(titles) + + +class IEERDocument: + def __init__(self, text, docno=None, doctype=None, date_time=None, headline=""): + self.text = text + self.docno = docno + self.doctype = doctype + self.date_time = date_time + self.headline = headline + + def __repr__(self): + if self.headline: + headline = " ".join(self.headline.leaves()) + else: + headline = ( + " ".join([w for w in self.text.leaves() if w[:1] != "<"][:12]) + "..." + ) + if self.docno is not None: + return f"" + else: + return "" % headline + + +class IEERCorpusReader(CorpusReader): + """ """ + + def docs(self, fileids=None): + return concat( + [ + StreamBackedCorpusView(fileid, self._read_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def parsed_docs(self, fileids=None): + return concat( + [ + StreamBackedCorpusView(fileid, self._read_parsed_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def _read_parsed_block(self, stream): + # TODO: figure out while empty documents are being returned + return [ + self._parse(doc) + for doc in self._read_block(stream) + if self._parse(doc).docno is not None + ] + + def _parse(self, doc): + val = nltk.chunk.ieerstr2tree(doc, root_label="DOCUMENT") + if isinstance(val, dict): + return IEERDocument(**val) + else: + return IEERDocument(val) + + def _read_block(self, stream): + out = [] + # Skip any preamble. + while True: + line = stream.readline() + if not line: + break + if line.strip() == "": + break + out.append(line) + # Read the document + while True: + line = stream.readline() + if not line: + break + out.append(line) + if line.strip() == "": + break + # Return the document + return ["\n".join(out)] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/indian.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/indian.py new file mode 100644 index 0000000..7ee2247 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/indian.py @@ -0,0 +1,93 @@ +# Natural Language Toolkit: Indian Language POS-Tagged Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Indian Language POS-Tagged Corpus +Collected by A Kumaran, Microsoft Research, India +Distributed with permission + +Contents: + - Bangla: IIT Kharagpur + - Hindi: Microsoft Research India + - Marathi: IIT Bombay + - Telugu: IIIT Hyderabad +""" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tag import map_tag, str2tuple + + +class IndianCorpusReader(CorpusReader): + """ + List of words, one per line. Blank lines are ignored. + """ + + def words(self, fileids=None): + return concat( + [ + IndianCorpusView(fileid, enc, False, False) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_words(self, fileids=None, tagset=None): + if tagset and tagset != self._tagset: + tag_mapping_function = lambda t: map_tag(self._tagset, tagset, t) + else: + tag_mapping_function = None + return concat( + [ + IndianCorpusView(fileid, enc, True, False, tag_mapping_function) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + return concat( + [ + IndianCorpusView(fileid, enc, False, True) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_sents(self, fileids=None, tagset=None): + if tagset and tagset != self._tagset: + tag_mapping_function = lambda t: map_tag(self._tagset, tagset, t) + else: + tag_mapping_function = None + return concat( + [ + IndianCorpusView(fileid, enc, True, True, tag_mapping_function) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + +class IndianCorpusView(StreamBackedCorpusView): + def __init__( + self, corpus_file, encoding, tagged, group_by_sent, tag_mapping_function=None + ): + self._tagged = tagged + self._group_by_sent = group_by_sent + self._tag_mapping_function = tag_mapping_function + StreamBackedCorpusView.__init__(self, corpus_file, encoding=encoding) + + def read_block(self, stream): + line = stream.readline() + if line.startswith("<"): + return [] + sent = [str2tuple(word, sep="_") for word in line.split()] + if self._tag_mapping_function: + sent = [(w, self._tag_mapping_function(t)) for (w, t) in sent] + if not self._tagged: + sent = [w for (w, t) in sent] + if self._group_by_sent: + return [sent] + else: + return sent diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ipipan.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ipipan.py new file mode 100644 index 0000000..b574561 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ipipan.py @@ -0,0 +1,356 @@ +# Natural Language Toolkit: IPI PAN Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Konrad Goluchowski +# URL: +# For license information, see LICENSE.TXT + +import functools + +from nltk.corpus.reader.api import CorpusReader +from nltk.corpus.reader.util import StreamBackedCorpusView, concat + + +def _parse_args(fun): + @functools.wraps(fun) + def decorator(self, fileids=None, **kwargs): + kwargs.pop("tags", None) + if not fileids: + fileids = self.fileids() + return fun(self, fileids, **kwargs) + + return decorator + + +class IPIPANCorpusReader(CorpusReader): + """ + Corpus reader designed to work with corpus created by IPI PAN. + See http://korpus.pl/en/ for more details about IPI PAN corpus. + + The corpus includes information about text domain, channel and categories. + You can access possible values using ``domains()``, ``channels()`` and + ``categories()``. You can use also this metadata to filter files, e.g.: + ``fileids(channel='prasa')``, ``fileids(categories='publicystyczny')``. + + The reader supports methods: words, sents, paras and their tagged versions. + You can get part of speech instead of full tag by giving "simplify_tags=True" + parameter, e.g.: ``tagged_sents(simplify_tags=True)``. + + Also you can get all tags disambiguated tags specifying parameter + "one_tag=False", e.g.: ``tagged_paras(one_tag=False)``. + + You can get all tags that were assigned by a morphological analyzer specifying + parameter "disamb_only=False", e.g. ``tagged_words(disamb_only=False)``. + + The IPIPAN Corpus contains tags indicating if there is a space between two + tokens. To add special "no space" markers, you should specify parameter + "append_no_space=True", e.g. ``tagged_words(append_no_space=True)``. + As a result in place where there should be no space between two tokens new + pair ('', 'no-space') will be inserted (for tagged data) and just '' for + methods without tags. + + The corpus reader can also try to append spaces between words. To enable this + option, specify parameter "append_space=True", e.g. ``words(append_space=True)``. + As a result either ' ' or (' ', 'space') will be inserted between tokens. + + By default, xml entities like " and & are replaced by corresponding + characters. You can turn off this feature, specifying parameter + "replace_xmlentities=False", e.g. ``words(replace_xmlentities=False)``. + """ + + def __init__(self, root, fileids): + CorpusReader.__init__(self, root, fileids, None, None) + + def channels(self, fileids=None): + if not fileids: + fileids = self.fileids() + return self._parse_header(fileids, "channel") + + def domains(self, fileids=None): + if not fileids: + fileids = self.fileids() + return self._parse_header(fileids, "domain") + + def categories(self, fileids=None): + if not fileids: + fileids = self.fileids() + return [ + self._map_category(cat) for cat in self._parse_header(fileids, "keyTerm") + ] + + def fileids(self, channels=None, domains=None, categories=None): + if channels is not None and domains is not None and categories is not None: + raise ValueError( + "You can specify only one of channels, domains " + "and categories parameter at once" + ) + if channels is None and domains is None and categories is None: + return CorpusReader.fileids(self) + if isinstance(channels, str): + channels = [channels] + if isinstance(domains, str): + domains = [domains] + if isinstance(categories, str): + categories = [categories] + if channels: + return self._list_morph_files_by("channel", channels) + elif domains: + return self._list_morph_files_by("domain", domains) + else: + return self._list_morph_files_by( + "keyTerm", categories, map=self._map_category + ) + + @_parse_args + def sents(self, fileids=None, **kwargs): + return concat( + [ + self._view( + fileid, mode=IPIPANCorpusView.SENTS_MODE, tags=False, **kwargs + ) + for fileid in self._list_morph_files(fileids) + ] + ) + + @_parse_args + def paras(self, fileids=None, **kwargs): + return concat( + [ + self._view( + fileid, mode=IPIPANCorpusView.PARAS_MODE, tags=False, **kwargs + ) + for fileid in self._list_morph_files(fileids) + ] + ) + + @_parse_args + def words(self, fileids=None, **kwargs): + return concat( + [ + self._view(fileid, tags=False, **kwargs) + for fileid in self._list_morph_files(fileids) + ] + ) + + @_parse_args + def tagged_sents(self, fileids=None, **kwargs): + return concat( + [ + self._view(fileid, mode=IPIPANCorpusView.SENTS_MODE, **kwargs) + for fileid in self._list_morph_files(fileids) + ] + ) + + @_parse_args + def tagged_paras(self, fileids=None, **kwargs): + return concat( + [ + self._view(fileid, mode=IPIPANCorpusView.PARAS_MODE, **kwargs) + for fileid in self._list_morph_files(fileids) + ] + ) + + @_parse_args + def tagged_words(self, fileids=None, **kwargs): + return concat( + [self._view(fileid, **kwargs) for fileid in self._list_morph_files(fileids)] + ) + + def _list_morph_files(self, fileids): + return [f for f in self.abspaths(fileids)] + + def _list_header_files(self, fileids): + return [ + f.replace("morph.xml", "header.xml") + for f in self._list_morph_files(fileids) + ] + + def _parse_header(self, fileids, tag): + values = set() + for f in self._list_header_files(fileids): + values_list = self._get_tag(f, tag) + for v in values_list: + values.add(v) + return list(values) + + def _list_morph_files_by(self, tag, values, map=None): + fileids = self.fileids() + ret_fileids = set() + for f in fileids: + fp = self.abspath(f).replace("morph.xml", "header.xml") + values_list = self._get_tag(fp, tag) + for value in values_list: + if map is not None: + value = map(value) + if value in values: + ret_fileids.add(f) + return list(ret_fileids) + + def _get_tag(self, f, tag): + tags = [] + with open(f) as infile: + header = infile.read() + tag_end = 0 + while True: + tag_pos = header.find("<" + tag, tag_end) + if tag_pos < 0: + return tags + tag_end = header.find("", tag_pos) + tags.append(header[tag_pos + len(tag) + 2 : tag_end]) + + def _map_category(self, cat): + pos = cat.find(">") + if pos == -1: + return cat + else: + return cat[pos + 1 :] + + def _view(self, filename, **kwargs): + tags = kwargs.pop("tags", True) + mode = kwargs.pop("mode", 0) + simplify_tags = kwargs.pop("simplify_tags", False) + one_tag = kwargs.pop("one_tag", True) + disamb_only = kwargs.pop("disamb_only", True) + append_no_space = kwargs.pop("append_no_space", False) + append_space = kwargs.pop("append_space", False) + replace_xmlentities = kwargs.pop("replace_xmlentities", True) + + if len(kwargs) > 0: + raise ValueError("Unexpected arguments: %s" % kwargs.keys()) + if not one_tag and not disamb_only: + raise ValueError( + "You cannot specify both one_tag=False and " "disamb_only=False" + ) + if not tags and (simplify_tags or not one_tag or not disamb_only): + raise ValueError( + "You cannot specify simplify_tags, one_tag or " + "disamb_only with functions other than tagged_*" + ) + + return IPIPANCorpusView( + filename, + tags=tags, + mode=mode, + simplify_tags=simplify_tags, + one_tag=one_tag, + disamb_only=disamb_only, + append_no_space=append_no_space, + append_space=append_space, + replace_xmlentities=replace_xmlentities, + ) + + +class IPIPANCorpusView(StreamBackedCorpusView): + + WORDS_MODE = 0 + SENTS_MODE = 1 + PARAS_MODE = 2 + + def __init__(self, filename, startpos=0, **kwargs): + StreamBackedCorpusView.__init__(self, filename, None, startpos, None) + self.in_sentence = False + self.position = 0 + + self.show_tags = kwargs.pop("tags", True) + self.disamb_only = kwargs.pop("disamb_only", True) + self.mode = kwargs.pop("mode", IPIPANCorpusView.WORDS_MODE) + self.simplify_tags = kwargs.pop("simplify_tags", False) + self.one_tag = kwargs.pop("one_tag", True) + self.append_no_space = kwargs.pop("append_no_space", False) + self.append_space = kwargs.pop("append_space", False) + self.replace_xmlentities = kwargs.pop("replace_xmlentities", True) + + def read_block(self, stream): + sentence = [] + sentences = [] + space = False + no_space = False + + tags = set() + + lines = self._read_data(stream) + + while True: + + # we may have only part of last line + if len(lines) <= 1: + self._seek(stream) + lines = self._read_data(stream) + + if lines == [""]: + assert not sentences + return [] + + line = lines.pop() + self.position += len(line) + 1 + + if line.startswith('"): + if self.append_space: + no_space = True + if self.append_no_space: + if self.show_tags: + sentence.append(("", "no-space")) + else: + sentence.append("") + elif line.startswith(" +# URL: +# For license information, see LICENSE.TXT + +# For more information, see http://lilyx.net/pages/nltkjapanesecorpus.html + +import re + +from nltk.corpus.reader.api import CorpusReader, SyntaxCorpusReader +from nltk.corpus.reader.util import ( + FileSystemPathPointer, + find_corpus_fileids, + read_blankline_block, +) +from nltk.parse import DependencyGraph + +# default function to convert morphlist to str for tree representation +_morphs2str_default = lambda morphs: "/".join(m[0] for m in morphs if m[0] != "EOS") + + +class KNBCorpusReader(SyntaxCorpusReader): + """ + This class implements: + - ``__init__``, which specifies the location of the corpus + and a method for detecting the sentence blocks in corpus files. + - ``_read_block``, which reads a block from the input stream. + - ``_word``, which takes a block and returns a list of list of words. + - ``_tag``, which takes a block and returns a list of list of tagged + words. + - ``_parse``, which takes a block and returns a list of parsed + sentences. + + The structure of tagged words: + tagged_word = (word(str), tags(tuple)) + tags = (surface, reading, lemma, pos1, posid1, pos2, posid2, pos3, posid3, others ...) + + Usage example + + >>> from nltk.corpus.util import LazyCorpusLoader + >>> knbc = LazyCorpusLoader( + ... 'knbc/corpus1', + ... KNBCorpusReader, + ... r'.*/KN.*', + ... encoding='euc-jp', + ... ) + + >>> len(knbc.sents()[0]) + 9 + + """ + + def __init__(self, root, fileids, encoding="utf8", morphs2str=_morphs2str_default): + """ + Initialize KNBCorpusReader + morphs2str is a function to convert morphlist to str for tree representation + for _parse() + """ + SyntaxCorpusReader.__init__(self, root, fileids, encoding) + self.morphs2str = morphs2str + + def _read_block(self, stream): + # blocks are split by blankline (or EOF) - default + return read_blankline_block(stream) + + def _word(self, t): + res = [] + for line in t.splitlines(): + # ignore the Bunsets headers + if not re.match(r"EOS|\*|\#|\+", line): + cells = line.strip().split(" ") + res.append(cells[0]) + + return res + + # ignores tagset argument + def _tag(self, t, tagset=None): + res = [] + for line in t.splitlines(): + # ignore the Bunsets headers + if not re.match(r"EOS|\*|\#|\+", line): + cells = line.strip().split(" ") + # convert cells to morph tuples + res.append((cells[0], " ".join(cells[1:]))) + + return res + + def _parse(self, t): + dg = DependencyGraph() + i = 0 + for line in t.splitlines(): + if line[0] in "*+": + # start of bunsetsu or tag + + cells = line.strip().split(" ", 3) + m = re.match(r"([\-0-9]*)([ADIP])", cells[1]) + + assert m is not None + + node = dg.nodes[i] + node.update({"address": i, "rel": m.group(2), "word": []}) + + dep_parent = int(m.group(1)) + + if dep_parent == -1: + dg.root = node + else: + dg.nodes[dep_parent]["deps"].append(i) + + i += 1 + elif line[0] != "#": + # normal morph + cells = line.strip().split(" ") + # convert cells to morph tuples + morph = cells[0], " ".join(cells[1:]) + dg.nodes[i - 1]["word"].append(morph) + + if self.morphs2str: + for node in dg.nodes.values(): + node["word"] = self.morphs2str(node["word"]) + + return dg.tree() + + +###################################################################### +# Demo +###################################################################### + + +def demo(): + + import nltk + from nltk.corpus.util import LazyCorpusLoader + + root = nltk.data.find("corpora/knbc/corpus1") + fileids = [ + f + for f in find_corpus_fileids(FileSystemPathPointer(root), ".*") + if re.search(r"\d\-\d\-[\d]+\-[\d]+", f) + ] + + def _knbc_fileids_sort(x): + cells = x.split("-") + return (cells[0], int(cells[1]), int(cells[2]), int(cells[3])) + + knbc = LazyCorpusLoader( + "knbc/corpus1", + KNBCorpusReader, + sorted(fileids, key=_knbc_fileids_sort), + encoding="euc-jp", + ) + + print(knbc.fileids()[:10]) + print("".join(knbc.words()[:100])) + + print("\n\n".join(str(tree) for tree in knbc.parsed_sents()[:2])) + + knbc.morphs2str = lambda morphs: "/".join( + "{}({})".format(m[0], m[1].split(" ")[2]) for m in morphs if m[0] != "EOS" + ).encode("utf-8") + + print("\n\n".join("%s" % tree for tree in knbc.parsed_sents()[:2])) + + print( + "\n".join( + " ".join("{}/{}".format(w[0], w[1].split(" ")[2]) for w in sent) + for sent in knbc.tagged_sents()[0:2] + ) + ) + + +def test(): + + from nltk.corpus.util import LazyCorpusLoader + + knbc = LazyCorpusLoader( + "knbc/corpus1", KNBCorpusReader, r".*/KN.*", encoding="euc-jp" + ) + assert isinstance(knbc.words()[0], str) + assert isinstance(knbc.sents()[0][0], str) + assert isinstance(knbc.tagged_words()[0], tuple) + assert isinstance(knbc.tagged_sents()[0][0], tuple) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/lin.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/lin.py new file mode 100644 index 0000000..dbbaf2c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/lin.py @@ -0,0 +1,183 @@ +# Natural Language Toolkit: Lin's Thesaurus +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Dan Blanchard +# URL: +# For license information, see LICENSE.txt + +import re +from collections import defaultdict +from functools import reduce + +from nltk.corpus.reader import CorpusReader + + +class LinThesaurusCorpusReader(CorpusReader): + """Wrapper for the LISP-formatted thesauruses distributed by Dekang Lin.""" + + # Compiled regular expression for extracting the key from the first line of each + # thesaurus entry + _key_re = re.compile(r'\("?([^"]+)"? \(desc [0-9.]+\).+') + + @staticmethod + def __defaultdict_factory(): + """Factory for creating defaultdict of defaultdict(dict)s""" + return defaultdict(dict) + + def __init__(self, root, badscore=0.0): + """ + Initialize the thesaurus. + + :param root: root directory containing thesaurus LISP files + :type root: C{string} + :param badscore: the score to give to words which do not appear in each other's sets of synonyms + :type badscore: C{float} + """ + + super().__init__(root, r"sim[A-Z]\.lsp") + self._thesaurus = defaultdict(LinThesaurusCorpusReader.__defaultdict_factory) + self._badscore = badscore + for path, encoding, fileid in self.abspaths( + include_encoding=True, include_fileid=True + ): + with open(path) as lin_file: + first = True + for line in lin_file: + line = line.strip() + # Start of entry + if first: + key = LinThesaurusCorpusReader._key_re.sub(r"\1", line) + first = False + # End of entry + elif line == "))": + first = True + # Lines with pairs of ngrams and scores + else: + split_line = line.split("\t") + if len(split_line) == 2: + ngram, score = split_line + self._thesaurus[fileid][key][ngram.strip('"')] = float( + score + ) + + def similarity(self, ngram1, ngram2, fileid=None): + """ + Returns the similarity score for two ngrams. + + :param ngram1: first ngram to compare + :type ngram1: C{string} + :param ngram2: second ngram to compare + :type ngram2: C{string} + :param fileid: thesaurus fileid to search in. If None, search all fileids. + :type fileid: C{string} + :return: If fileid is specified, just the score for the two ngrams; otherwise, + list of tuples of fileids and scores. + """ + # Entries don't contain themselves, so make sure similarity between item and itself is 1.0 + if ngram1 == ngram2: + if fileid: + return 1.0 + else: + return [(fid, 1.0) for fid in self._fileids] + else: + if fileid: + return ( + self._thesaurus[fileid][ngram1][ngram2] + if ngram2 in self._thesaurus[fileid][ngram1] + else self._badscore + ) + else: + return [ + ( + fid, + ( + self._thesaurus[fid][ngram1][ngram2] + if ngram2 in self._thesaurus[fid][ngram1] + else self._badscore + ), + ) + for fid in self._fileids + ] + + def scored_synonyms(self, ngram, fileid=None): + """ + Returns a list of scored synonyms (tuples of synonyms and scores) for the current ngram + + :param ngram: ngram to lookup + :type ngram: C{string} + :param fileid: thesaurus fileid to search in. If None, search all fileids. + :type fileid: C{string} + :return: If fileid is specified, list of tuples of scores and synonyms; otherwise, + list of tuples of fileids and lists, where inner lists consist of tuples of + scores and synonyms. + """ + if fileid: + return self._thesaurus[fileid][ngram].items() + else: + return [ + (fileid, self._thesaurus[fileid][ngram].items()) + for fileid in self._fileids + ] + + def synonyms(self, ngram, fileid=None): + """ + Returns a list of synonyms for the current ngram. + + :param ngram: ngram to lookup + :type ngram: C{string} + :param fileid: thesaurus fileid to search in. If None, search all fileids. + :type fileid: C{string} + :return: If fileid is specified, list of synonyms; otherwise, list of tuples of fileids and + lists, where inner lists contain synonyms. + """ + if fileid: + return self._thesaurus[fileid][ngram].keys() + else: + return [ + (fileid, self._thesaurus[fileid][ngram].keys()) + for fileid in self._fileids + ] + + def __contains__(self, ngram): + """ + Determines whether or not the given ngram is in the thesaurus. + + :param ngram: ngram to lookup + :type ngram: C{string} + :return: whether the given ngram is in the thesaurus. + """ + return reduce( + lambda accum, fileid: accum or (ngram in self._thesaurus[fileid]), + self._fileids, + False, + ) + + +###################################################################### +# Demo +###################################################################### + + +def demo(): + from nltk.corpus import lin_thesaurus as thes + + word1 = "business" + word2 = "enterprise" + print("Getting synonyms for " + word1) + print(thes.synonyms(word1)) + + print("Getting scored synonyms for " + word1) + print(thes.scored_synonyms(word1)) + + print("Getting synonyms from simN.lsp (noun subsection) for " + word1) + print(thes.synonyms(word1, fileid="simN.lsp")) + + print("Getting synonyms from simN.lsp (noun subsection) for " + word1) + print(thes.synonyms(word1, fileid="simN.lsp")) + + print(f"Similarity score for {word1} and {word2}:") + print(thes.similarity(word1, word2)) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/markdown.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/markdown.py new file mode 100644 index 0000000..7ecca3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/markdown.py @@ -0,0 +1,342 @@ +from collections import namedtuple +from functools import partial, wraps + +from nltk.corpus.reader.api import CategorizedCorpusReader +from nltk.corpus.reader.plaintext import PlaintextCorpusReader +from nltk.corpus.reader.util import concat, read_blankline_block +from nltk.tokenize import blankline_tokenize, sent_tokenize, word_tokenize + + +def comma_separated_string_args(func): + """ + A decorator that allows a function to be called with + a single string of comma-separated values which become + individual function arguments. + """ + + @wraps(func) + def wrapper(*args, **kwargs): + _args = list() + for arg in args: + if isinstance(arg, str): + _args.append({part.strip() for part in arg.split(",")}) + elif isinstance(arg, list): + _args.append(set(arg)) + else: + _args.append(arg) + for name, value in kwargs.items(): + if isinstance(value, str): + kwargs[name] = {part.strip() for part in value.split(",")} + return func(*_args, **kwargs) + + return wrapper + + +def read_parse_blankline_block(stream, parser): + block = read_blankline_block(stream) + if block: + return [parser.render(block[0])] + return block + + +class MarkdownBlock: + def __init__(self, content): + self.content = content + self.truncate_at = 16 + + def __repr__(self): + return f"{self.__class__.__name__}(content={repr(str(self))})" + + def __str__(self): + return ( + f"{self.content[:self.truncate_at]}" + f"{'...' if len(self.content) > self.truncate_at else ''}" + ) + + @property + def raw(self): + return self.content + + @property + def words(self): + return word_tokenize(self.content) + + @property + def sents(self): + return [word_tokenize(sent) for sent in sent_tokenize(self.content)] + + @property + def paras(self): + return [ + [word_tokenize(sent) for sent in sent_tokenize(para)] + for para in blankline_tokenize(self.content) + ] + + +class CodeBlock(MarkdownBlock): + def __init__(self, language, *args): + self.language = language + super().__init__(*args) + + @property + def sents(self): + return [word_tokenize(line) for line in self.content.splitlines()] + + @property + def lines(self): + return self.content.splitlines() + + @property + def paras(self): + return [ + [word_tokenize(line) for line in para.splitlines()] + for para in blankline_tokenize(self.content) + ] + + +class MarkdownSection(MarkdownBlock): + def __init__(self, heading, level, *args): + self.heading = heading + self.level = level + super().__init__(*args) + + +Image = namedtuple("Image", "label, src, title") +Link = namedtuple("Link", "label, href, title") +List = namedtuple("List", "is_ordered, items") + + +class MarkdownCorpusReader(PlaintextCorpusReader): + def __init__(self, *args, parser=None, **kwargs): + from markdown_it import MarkdownIt + from mdit_plain.renderer import RendererPlain + from mdit_py_plugins.front_matter import front_matter_plugin + + self.parser = parser + if self.parser is None: + self.parser = MarkdownIt("commonmark", renderer_cls=RendererPlain) + self.parser.use(front_matter_plugin) + + kwargs.setdefault( + "para_block_reader", partial(read_parse_blankline_block, parser=self.parser) + ) + super().__init__(*args, **kwargs) + + # This override takes care of removing markup. + def _read_word_block(self, stream): + words = list() + for para in self._para_block_reader(stream): + words.extend(self._word_tokenizer.tokenize(para)) + return words + + +class CategorizedMarkdownCorpusReader(CategorizedCorpusReader, MarkdownCorpusReader): + """ + A reader for markdown corpora whose documents are divided into + categories based on their file identifiers. + + Based on nltk.corpus.reader.plaintext.CategorizedPlaintextCorpusReader: + https://www.nltk.org/_modules/nltk/corpus/reader/api.html#CategorizedCorpusReader + """ + + def __init__(self, *args, cat_field="tags", **kwargs): + """ + Initialize the corpus reader. Categorization arguments + (``cat_pattern``, ``cat_map``, and ``cat_file``) are passed to + the ``CategorizedCorpusReader`` constructor. The remaining arguments + are passed to the ``MarkdownCorpusReader`` constructor. + """ + cat_args = ["cat_pattern", "cat_map", "cat_file"] + if not any(arg in kwargs for arg in cat_args): + # Initialize with a blank map now, + # and try to build categories from document metadata later. + kwargs["cat_map"] = dict() + CategorizedCorpusReader.__init__(self, kwargs) + MarkdownCorpusReader.__init__(self, *args, **kwargs) + + # Map file IDs to categories if self._map exists but is still empty: + if self._map is not None and not self._map: + for file_id in self._fileids: + metadata = self.metadata(file_id) + if metadata: + self._map[file_id] = metadata[0].get(cat_field, []) + + ### Begin CategorizedCorpusReader Overrides + @comma_separated_string_args + def categories(self, fileids=None): + return super().categories(fileids) + + @comma_separated_string_args + def fileids(self, categories=None): + if categories is None: + return self._fileids + return super().fileids(categories) + + ### End CategorizedCorpusReader Overrides + + ### Begin MarkdownCorpusReader Overrides + @comma_separated_string_args + def raw(self, fileids=None, categories=None): + return super().raw(self._resolve(fileids, categories)) + + @comma_separated_string_args + def words(self, fileids=None, categories=None): + return super().words(self._resolve(fileids, categories)) + + @comma_separated_string_args + def sents(self, fileids=None, categories=None): + return super().sents(self._resolve(fileids, categories)) + + @comma_separated_string_args + def paras(self, fileids=None, categories=None): + return super().paras(self._resolve(fileids, categories)) + + ### End MarkdownCorpusReader Overrides + + def concatenated_view(self, reader, fileids, categories): + return concat( + [ + self.CorpusView(path, reader, encoding=enc) + for (path, enc) in self.abspaths( + self._resolve(fileids, categories), include_encoding=True + ) + ] + ) + + def metadata_reader(self, stream): + from yaml import safe_load + + return [ + safe_load(t.content) + for t in self.parser.parse(stream.read()) + if t.type == "front_matter" + ] + + @comma_separated_string_args + def metadata(self, fileids=None, categories=None): + return self.concatenated_view(self.metadata_reader, fileids, categories) + + def blockquote_reader(self, stream): + tokens = self.parser.parse(stream.read()) + opening_tokens = filter( + lambda t: t.level == 0 and t.type == "blockquote_open", tokens + ) + closing_tokens = filter( + lambda t: t.level == 0 and t.type == "blockquote_close", tokens + ) + blockquotes = list() + for o, c in zip(opening_tokens, closing_tokens): + opening_index = tokens.index(o) + closing_index = tokens.index(c, opening_index) + blockquotes.append(tokens[opening_index : closing_index + 1]) + return [ + MarkdownBlock( + self.parser.renderer.render(block, self.parser.options, env=None) + ) + for block in blockquotes + ] + + @comma_separated_string_args + def blockquotes(self, fileids=None, categories=None): + return self.concatenated_view(self.blockquote_reader, fileids, categories) + + def code_block_reader(self, stream): + return [ + CodeBlock( + t.info, + t.content, + ) + for t in self.parser.parse(stream.read()) + if t.level == 0 and t.type in ("fence", "code_block") + ] + + @comma_separated_string_args + def code_blocks(self, fileids=None, categories=None): + return self.concatenated_view(self.code_block_reader, fileids, categories) + + def image_reader(self, stream): + return [ + Image( + child_token.content, + child_token.attrGet("src"), + child_token.attrGet("title"), + ) + for inline_token in filter( + lambda t: t.type == "inline", self.parser.parse(stream.read()) + ) + for child_token in inline_token.children + if child_token.type == "image" + ] + + @comma_separated_string_args + def images(self, fileids=None, categories=None): + return self.concatenated_view(self.image_reader, fileids, categories) + + def link_reader(self, stream): + return [ + Link( + inline_token.children[i + 1].content, + child_token.attrGet("href"), + child_token.attrGet("title"), + ) + for inline_token in filter( + lambda t: t.type == "inline", self.parser.parse(stream.read()) + ) + for i, child_token in enumerate(inline_token.children) + if child_token.type == "link_open" + ] + + @comma_separated_string_args + def links(self, fileids=None, categories=None): + return self.concatenated_view(self.link_reader, fileids, categories) + + def list_reader(self, stream): + tokens = self.parser.parse(stream.read()) + opening_types = ("bullet_list_open", "ordered_list_open") + opening_tokens = filter( + lambda t: t.level == 0 and t.type in opening_types, tokens + ) + closing_types = ("bullet_list_close", "ordered_list_close") + closing_tokens = filter( + lambda t: t.level == 0 and t.type in closing_types, tokens + ) + list_blocks = list() + for o, c in zip(opening_tokens, closing_tokens): + opening_index = tokens.index(o) + closing_index = tokens.index(c, opening_index) + list_blocks.append(tokens[opening_index : closing_index + 1]) + return [ + List( + tokens[0].type == "ordered_list_open", + [t.content for t in tokens if t.content], + ) + for tokens in list_blocks + ] + + @comma_separated_string_args + def lists(self, fileids=None, categories=None): + return self.concatenated_view(self.list_reader, fileids, categories) + + def section_reader(self, stream): + section_blocks, block = list(), list() + in_heading = False + for t in self.parser.parse(stream.read()): + if t.level == 0 and t.type == "heading_open": + if block: + section_blocks.append(block) + block = list() + in_heading = True + if in_heading: + block.append(t) + return [ + MarkdownSection( + block[1].content, + block[0].markup.count("#"), + self.parser.renderer.render(block, self.parser.options, env=None), + ) + for block in section_blocks + ] + + @comma_separated_string_args + def sections(self, fileids=None, categories=None): + return self.concatenated_view(self.section_reader, fileids, categories) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/mte.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/mte.py new file mode 100644 index 0000000..b0ac56e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/mte.py @@ -0,0 +1,397 @@ +""" +A reader for corpora whose documents are in MTE format. +""" +import os +import re +from functools import reduce + +from nltk.corpus.reader import TaggedCorpusReader, concat +from nltk.corpus.reader.xmldocs import XMLCorpusView + + +def xpath(root, path, ns): + return root.findall(path, ns) + + +class MTECorpusView(XMLCorpusView): + """ + Class for lazy viewing the MTE Corpus. + """ + + def __init__(self, fileid, tagspec, elt_handler=None): + XMLCorpusView.__init__(self, fileid, tagspec, elt_handler) + + def read_block(self, stream, tagspec=None, elt_handler=None): + return list( + filter( + lambda x: x is not None, + XMLCorpusView.read_block(self, stream, tagspec, elt_handler), + ) + ) + + +class MTEFileReader: + """ + Class for loading the content of the multext-east corpus. It + parses the xml files and does some tag-filtering depending on the + given method parameters. + """ + + ns = { + "tei": "https://www.tei-c.org/ns/1.0", + "xml": "https://www.w3.org/XML/1998/namespace", + } + tag_ns = "{https://www.tei-c.org/ns/1.0}" + xml_ns = "{https://www.w3.org/XML/1998/namespace}" + word_path = "TEI/text/body/div/div/p/s/(w|c)" + sent_path = "TEI/text/body/div/div/p/s" + para_path = "TEI/text/body/div/div/p" + + def __init__(self, file_path): + self.__file_path = file_path + + @classmethod + def _word_elt(cls, elt, context): + return elt.text + + @classmethod + def _sent_elt(cls, elt, context): + return [cls._word_elt(w, None) for w in xpath(elt, "*", cls.ns)] + + @classmethod + def _para_elt(cls, elt, context): + return [cls._sent_elt(s, None) for s in xpath(elt, "*", cls.ns)] + + @classmethod + def _tagged_word_elt(cls, elt, context): + if "ana" not in elt.attrib: + return (elt.text, "") + + if cls.__tags == "" and cls.__tagset == "msd": + return (elt.text, elt.attrib["ana"]) + elif cls.__tags == "" and cls.__tagset == "universal": + return (elt.text, MTETagConverter.msd_to_universal(elt.attrib["ana"])) + else: + tags = re.compile("^" + re.sub("-", ".", cls.__tags) + ".*$") + if tags.match(elt.attrib["ana"]): + if cls.__tagset == "msd": + return (elt.text, elt.attrib["ana"]) + else: + return ( + elt.text, + MTETagConverter.msd_to_universal(elt.attrib["ana"]), + ) + else: + return None + + @classmethod + def _tagged_sent_elt(cls, elt, context): + return list( + filter( + lambda x: x is not None, + [cls._tagged_word_elt(w, None) for w in xpath(elt, "*", cls.ns)], + ) + ) + + @classmethod + def _tagged_para_elt(cls, elt, context): + return list( + filter( + lambda x: x is not None, + [cls._tagged_sent_elt(s, None) for s in xpath(elt, "*", cls.ns)], + ) + ) + + @classmethod + def _lemma_word_elt(cls, elt, context): + if "lemma" not in elt.attrib: + return (elt.text, "") + else: + return (elt.text, elt.attrib["lemma"]) + + @classmethod + def _lemma_sent_elt(cls, elt, context): + return [cls._lemma_word_elt(w, None) for w in xpath(elt, "*", cls.ns)] + + @classmethod + def _lemma_para_elt(cls, elt, context): + return [cls._lemma_sent_elt(s, None) for s in xpath(elt, "*", cls.ns)] + + def words(self): + return MTECorpusView( + self.__file_path, MTEFileReader.word_path, MTEFileReader._word_elt + ) + + def sents(self): + return MTECorpusView( + self.__file_path, MTEFileReader.sent_path, MTEFileReader._sent_elt + ) + + def paras(self): + return MTECorpusView( + self.__file_path, MTEFileReader.para_path, MTEFileReader._para_elt + ) + + def lemma_words(self): + return MTECorpusView( + self.__file_path, MTEFileReader.word_path, MTEFileReader._lemma_word_elt + ) + + def tagged_words(self, tagset, tags): + MTEFileReader.__tagset = tagset + MTEFileReader.__tags = tags + return MTECorpusView( + self.__file_path, MTEFileReader.word_path, MTEFileReader._tagged_word_elt + ) + + def lemma_sents(self): + return MTECorpusView( + self.__file_path, MTEFileReader.sent_path, MTEFileReader._lemma_sent_elt + ) + + def tagged_sents(self, tagset, tags): + MTEFileReader.__tagset = tagset + MTEFileReader.__tags = tags + return MTECorpusView( + self.__file_path, MTEFileReader.sent_path, MTEFileReader._tagged_sent_elt + ) + + def lemma_paras(self): + return MTECorpusView( + self.__file_path, MTEFileReader.para_path, MTEFileReader._lemma_para_elt + ) + + def tagged_paras(self, tagset, tags): + MTEFileReader.__tagset = tagset + MTEFileReader.__tags = tags + return MTECorpusView( + self.__file_path, MTEFileReader.para_path, MTEFileReader._tagged_para_elt + ) + + +class MTETagConverter: + """ + Class for converting msd tags to universal tags, more conversion + options are currently not implemented. + """ + + mapping_msd_universal = { + "A": "ADJ", + "S": "ADP", + "R": "ADV", + "C": "CONJ", + "D": "DET", + "N": "NOUN", + "M": "NUM", + "Q": "PRT", + "P": "PRON", + "V": "VERB", + ".": ".", + "-": "X", + } + + @staticmethod + def msd_to_universal(tag): + """ + This function converts the annotation from the Multex-East to the universal tagset + as described in Chapter 5 of the NLTK-Book + + Unknown Tags will be mapped to X. Punctuation marks are not supported in MSD tags, so + """ + indicator = tag[0] if not tag[0] == "#" else tag[1] + + if not indicator in MTETagConverter.mapping_msd_universal: + indicator = "-" + + return MTETagConverter.mapping_msd_universal[indicator] + + +class MTECorpusReader(TaggedCorpusReader): + """ + Reader for corpora following the TEI-p5 xml scheme, such as MULTEXT-East. + MULTEXT-East contains part-of-speech-tagged words with a quite precise tagging + scheme. These tags can be converted to the Universal tagset + """ + + def __init__(self, root=None, fileids=None, encoding="utf8"): + """ + Construct a new MTECorpusreader for a set of documents + located at the given root directory. Example usage: + + >>> root = '/...path to corpus.../' + >>> reader = MTECorpusReader(root, 'oana-*.xml', 'utf8') # doctest: +SKIP + + :param root: The root directory for this corpus. (default points to location in multext config file) + :param fileids: A list or regexp specifying the fileids in this corpus. (default is oana-en.xml) + :param encoding: The encoding of the given files (default is utf8) + """ + TaggedCorpusReader.__init__(self, root, fileids, encoding) + self._readme = "00README.txt" + + def __fileids(self, fileids): + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + # filter wrong userinput + fileids = filter(lambda x: x in self._fileids, fileids) + # filter multext-east sourcefiles that are not compatible to the teip5 specification + fileids = filter(lambda x: x not in ["oana-bg.xml", "oana-mk.xml"], fileids) + if not fileids: + print("No valid multext-east file specified") + return fileids + + def words(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).words() + for f in self.__fileids(fileids) + ] + ) + + def sents(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of sentences or utterances, + each encoded as a list of word strings + :rtype: list(list(str)) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).sents() + for f in self.__fileids(fileids) + ] + ) + + def paras(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of paragraphs, each encoded as a list + of sentences, which are in turn encoded as lists of word string + :rtype: list(list(list(str))) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).paras() + for f in self.__fileids(fileids) + ] + ) + + def lemma_words(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of words, the corresponding lemmas + and punctuation symbols, encoded as tuples (word, lemma) + :rtype: list(tuple(str,str)) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).lemma_words() + for f in self.__fileids(fileids) + ] + ) + + def tagged_words(self, fileids=None, tagset="msd", tags=""): + """ + :param fileids: A list specifying the fileids that should be used. + :param tagset: The tagset that should be used in the returned object, + either "universal" or "msd", "msd" is the default + :param tags: An MSD Tag that is used to filter all parts of the used corpus + that are not more precise or at least equal to the given tag + :return: the given file(s) as a list of tagged words and punctuation symbols + encoded as tuples (word, tag) + :rtype: list(tuple(str, str)) + """ + if tagset == "universal" or tagset == "msd": + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).tagged_words( + tagset, tags + ) + for f in self.__fileids(fileids) + ] + ) + else: + print("Unknown tagset specified.") + + def lemma_sents(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of sentences or utterances, each + encoded as a list of tuples of the word and the corresponding + lemma (word, lemma) + :rtype: list(list(tuple(str, str))) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).lemma_sents() + for f in self.__fileids(fileids) + ] + ) + + def tagged_sents(self, fileids=None, tagset="msd", tags=""): + """ + :param fileids: A list specifying the fileids that should be used. + :param tagset: The tagset that should be used in the returned object, + either "universal" or "msd", "msd" is the default + :param tags: An MSD Tag that is used to filter all parts of the used corpus + that are not more precise or at least equal to the given tag + :return: the given file(s) as a list of sentences or utterances, each + each encoded as a list of (word,tag) tuples + :rtype: list(list(tuple(str, str))) + """ + if tagset == "universal" or tagset == "msd": + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).tagged_sents( + tagset, tags + ) + for f in self.__fileids(fileids) + ] + ) + else: + print("Unknown tagset specified.") + + def lemma_paras(self, fileids=None): + """ + :param fileids: A list specifying the fileids that should be used. + :return: the given file(s) as a list of paragraphs, each encoded as a + list of sentences, which are in turn encoded as a list of + tuples of the word and the corresponding lemma (word, lemma) + :rtype: list(List(List(tuple(str, str)))) + """ + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).lemma_paras() + for f in self.__fileids(fileids) + ] + ) + + def tagged_paras(self, fileids=None, tagset="msd", tags=""): + """ + :param fileids: A list specifying the fileids that should be used. + :param tagset: The tagset that should be used in the returned object, + either "universal" or "msd", "msd" is the default + :param tags: An MSD Tag that is used to filter all parts of the used corpus + that are not more precise or at least equal to the given tag + :return: the given file(s) as a list of paragraphs, each encoded as a + list of sentences, which are in turn encoded as a list + of (word,tag) tuples + :rtype: list(list(list(tuple(str, str)))) + """ + if tagset == "universal" or tagset == "msd": + return concat( + [ + MTEFileReader(os.path.join(self._root, f)).tagged_paras( + tagset, tags + ) + for f in self.__fileids(fileids) + ] + ) + else: + print("Unknown tagset specified.") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nkjp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nkjp.py new file mode 100644 index 0000000..c4865c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nkjp.py @@ -0,0 +1,487 @@ +# Natural Language Toolkit: NKJP Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Gabriela Kaczka +# URL: +# For license information, see LICENSE.TXT + +import functools +import os +import re +import tempfile + +from nltk.corpus.reader.util import concat +from nltk.corpus.reader.xmldocs import XMLCorpusReader, XMLCorpusView + + +def _parse_args(fun): + """ + Wraps function arguments: + if fileids not specified then function set NKJPCorpusReader paths. + """ + + @functools.wraps(fun) + def decorator(self, fileids=None, **kwargs): + if not fileids: + fileids = self._paths + return fun(self, fileids, **kwargs) + + return decorator + + +class NKJPCorpusReader(XMLCorpusReader): + WORDS_MODE = 0 + SENTS_MODE = 1 + HEADER_MODE = 2 + RAW_MODE = 3 + + def __init__(self, root, fileids=".*"): + """ + Corpus reader designed to work with National Corpus of Polish. + See http://nkjp.pl/ for more details about NKJP. + use example: + import nltk + import nkjp + from nkjp import NKJPCorpusReader + x = NKJPCorpusReader(root='/home/USER/nltk_data/corpora/nkjp/', fileids='') # obtain the whole corpus + x.header() + x.raw() + x.words() + x.tagged_words(tags=['subst', 'comp']) #Link to find more tags: nkjp.pl/poliqarp/help/ense2.html + x.sents() + x = NKJPCorpusReader(root='/home/USER/nltk_data/corpora/nkjp/', fileids='Wilk*') # obtain particular file(s) + x.header(fileids=['WilkDom', '/home/USER/nltk_data/corpora/nkjp/WilkWilczy']) + x.tagged_words(fileids=['WilkDom', '/home/USER/nltk_data/corpora/nkjp/WilkWilczy'], tags=['subst', 'comp']) + """ + if isinstance(fileids, str): + XMLCorpusReader.__init__(self, root, fileids + ".*/header.xml") + else: + XMLCorpusReader.__init__( + self, root, [fileid + "/header.xml" for fileid in fileids] + ) + self._paths = self.get_paths() + + def get_paths(self): + return [ + os.path.join(str(self._root), f.split("header.xml")[0]) + for f in self._fileids + ] + + def fileids(self): + """ + Returns a list of file identifiers for the fileids that make up + this corpus. + """ + return [f.split("header.xml")[0] for f in self._fileids] + + def _view(self, filename, tags=None, **kwargs): + """ + Returns a view specialised for use with particular corpus file. + """ + mode = kwargs.pop("mode", NKJPCorpusReader.WORDS_MODE) + if mode is NKJPCorpusReader.WORDS_MODE: + return NKJPCorpus_Morph_View(filename, tags=tags) + elif mode is NKJPCorpusReader.SENTS_MODE: + return NKJPCorpus_Segmentation_View(filename, tags=tags) + elif mode is NKJPCorpusReader.HEADER_MODE: + return NKJPCorpus_Header_View(filename, tags=tags) + elif mode is NKJPCorpusReader.RAW_MODE: + return NKJPCorpus_Text_View( + filename, tags=tags, mode=NKJPCorpus_Text_View.RAW_MODE + ) + + else: + raise NameError("No such mode!") + + def add_root(self, fileid): + """ + Add root if necessary to specified fileid. + """ + if self.root in fileid: + return fileid + return self.root + fileid + + @_parse_args + def header(self, fileids=None, **kwargs): + """ + Returns header(s) of specified fileids. + """ + return concat( + [ + self._view( + self.add_root(fileid), mode=NKJPCorpusReader.HEADER_MODE, **kwargs + ).handle_query() + for fileid in fileids + ] + ) + + @_parse_args + def sents(self, fileids=None, **kwargs): + """ + Returns sentences in specified fileids. + """ + return concat( + [ + self._view( + self.add_root(fileid), mode=NKJPCorpusReader.SENTS_MODE, **kwargs + ).handle_query() + for fileid in fileids + ] + ) + + @_parse_args + def words(self, fileids=None, **kwargs): + """ + Returns words in specified fileids. + """ + + return concat( + [ + self._view( + self.add_root(fileid), mode=NKJPCorpusReader.WORDS_MODE, **kwargs + ).handle_query() + for fileid in fileids + ] + ) + + @_parse_args + def tagged_words(self, fileids=None, **kwargs): + """ + Call with specified tags as a list, e.g. tags=['subst', 'comp']. + Returns tagged words in specified fileids. + """ + tags = kwargs.pop("tags", []) + return concat( + [ + self._view( + self.add_root(fileid), + mode=NKJPCorpusReader.WORDS_MODE, + tags=tags, + **kwargs + ).handle_query() + for fileid in fileids + ] + ) + + @_parse_args + def raw(self, fileids=None, **kwargs): + """ + Returns words in specified fileids. + """ + return concat( + [ + self._view( + self.add_root(fileid), mode=NKJPCorpusReader.RAW_MODE, **kwargs + ).handle_query() + for fileid in fileids + ] + ) + + +class NKJPCorpus_Header_View(XMLCorpusView): + def __init__(self, filename, **kwargs): + """ + HEADER_MODE + A stream backed corpus view specialized for use with + header.xml files in NKJP corpus. + """ + self.tagspec = ".*/sourceDesc$" + XMLCorpusView.__init__(self, filename + "header.xml", self.tagspec) + + def handle_query(self): + self._open() + header = [] + while True: + segm = XMLCorpusView.read_block(self, self._stream) + if len(segm) == 0: + break + header.extend(segm) + self.close() + return header + + def handle_elt(self, elt, context): + titles = elt.findall("bibl/title") + title = [] + if titles: + title = "\n".join(title.text.strip() for title in titles) + + authors = elt.findall("bibl/author") + author = [] + if authors: + author = "\n".join(author.text.strip() for author in authors) + + dates = elt.findall("bibl/date") + date = [] + if dates: + date = "\n".join(date.text.strip() for date in dates) + + publishers = elt.findall("bibl/publisher") + publisher = [] + if publishers: + publisher = "\n".join(publisher.text.strip() for publisher in publishers) + + idnos = elt.findall("bibl/idno") + idno = [] + if idnos: + idno = "\n".join(idno.text.strip() for idno in idnos) + + notes = elt.findall("bibl/note") + note = [] + if notes: + note = "\n".join(note.text.strip() for note in notes) + + return { + "title": title, + "author": author, + "date": date, + "publisher": publisher, + "idno": idno, + "note": note, + } + + +class XML_Tool: + """ + Helper class creating xml file to one without references to nkjp: namespace. + That's needed because the XMLCorpusView assumes that one can find short substrings + of XML that are valid XML, which is not true if a namespace is declared at top level + """ + + def __init__(self, root, filename): + self.read_file = os.path.join(root, filename) + self.write_file = tempfile.NamedTemporaryFile(delete=False) + + def build_preprocessed_file(self): + try: + fr = open(self.read_file) + fw = self.write_file + line = " " + while len(line): + line = fr.readline() + x = re.split(r"nkjp:[^ ]* ", line) # in all files + ret = " ".join(x) + x = re.split("", ret) # in ann_segmentation.xml + ret = " ".join(x) + x = re.split("", ret) # in ann_segmentation.xml + ret = " ".join(x) + x = re.split("", ret) # in ann_segmentation.xml + ret = " ".join(x) + x = re.split("", ret) # in ann_segmentation.xml + ret = " ".join(x) + fw.write(ret) + fr.close() + fw.close() + return self.write_file.name + except Exception as e: + self.remove_preprocessed_file() + raise Exception from e + + def remove_preprocessed_file(self): + os.remove(self.write_file.name) + + +class NKJPCorpus_Segmentation_View(XMLCorpusView): + """ + A stream backed corpus view specialized for use with + ann_segmentation.xml files in NKJP corpus. + """ + + def __init__(self, filename, **kwargs): + self.tagspec = ".*p/.*s" + # intersperse NKJPCorpus_Text_View + self.text_view = NKJPCorpus_Text_View( + filename, mode=NKJPCorpus_Text_View.SENTS_MODE + ) + self.text_view.handle_query() + # xml preprocessing + self.xml_tool = XML_Tool(filename, "ann_segmentation.xml") + # base class init + XMLCorpusView.__init__( + self, self.xml_tool.build_preprocessed_file(), self.tagspec + ) + + def get_segm_id(self, example_word): + return example_word.split("(")[1].split(",")[0] + + def get_sent_beg(self, beg_word): + # returns index of beginning letter in sentence + return int(beg_word.split(",")[1]) + + def get_sent_end(self, end_word): + # returns index of end letter in sentence + splitted = end_word.split(")")[0].split(",") + return int(splitted[1]) + int(splitted[2]) + + def get_sentences(self, sent_segm): + # returns one sentence + id = self.get_segm_id(sent_segm[0]) + segm = self.text_view.segm_dict[id] # text segment + beg = self.get_sent_beg(sent_segm[0]) + end = self.get_sent_end(sent_segm[len(sent_segm) - 1]) + return segm[beg:end] + + def remove_choice(self, segm): + ret = [] + prev_txt_end = -1 + prev_txt_nr = -1 + for word in segm: + txt_nr = self.get_segm_id(word) + # get increasing sequence of ids: in case of choice get first possibility + if self.get_sent_beg(word) > prev_txt_end - 1 or prev_txt_nr != txt_nr: + ret.append(word) + prev_txt_end = self.get_sent_end(word) + prev_txt_nr = txt_nr + + return ret + + def handle_query(self): + try: + self._open() + sentences = [] + while True: + sent_segm = XMLCorpusView.read_block(self, self._stream) + if len(sent_segm) == 0: + break + for segm in sent_segm: + segm = self.remove_choice(segm) + sentences.append(self.get_sentences(segm)) + self.close() + self.xml_tool.remove_preprocessed_file() + return sentences + except Exception as e: + self.xml_tool.remove_preprocessed_file() + raise Exception from e + + def handle_elt(self, elt, context): + ret = [] + for seg in elt: + ret.append(seg.get("corresp")) + return ret + + +class NKJPCorpus_Text_View(XMLCorpusView): + """ + A stream backed corpus view specialized for use with + text.xml files in NKJP corpus. + """ + + SENTS_MODE = 0 + RAW_MODE = 1 + + def __init__(self, filename, **kwargs): + self.mode = kwargs.pop("mode", 0) + self.tagspec = ".*/div/ab" + self.segm_dict = dict() + # xml preprocessing + self.xml_tool = XML_Tool(filename, "text.xml") + # base class init + XMLCorpusView.__init__( + self, self.xml_tool.build_preprocessed_file(), self.tagspec + ) + + def handle_query(self): + try: + self._open() + x = self.read_block(self._stream) + self.close() + self.xml_tool.remove_preprocessed_file() + return x + except Exception as e: + self.xml_tool.remove_preprocessed_file() + raise Exception from e + + def read_block(self, stream, tagspec=None, elt_handler=None): + """ + Returns text as a list of sentences. + """ + txt = [] + while True: + segm = XMLCorpusView.read_block(self, stream) + if len(segm) == 0: + break + for part in segm: + txt.append(part) + + return [" ".join([segm for segm in txt])] + + def get_segm_id(self, elt): + for attr in elt.attrib: + if attr.endswith("id"): + return elt.get(attr) + + def handle_elt(self, elt, context): + # fill dictionary to use later in sents mode + if self.mode is NKJPCorpus_Text_View.SENTS_MODE: + self.segm_dict[self.get_segm_id(elt)] = elt.text + return elt.text + + +class NKJPCorpus_Morph_View(XMLCorpusView): + """ + A stream backed corpus view specialized for use with + ann_morphosyntax.xml files in NKJP corpus. + """ + + def __init__(self, filename, **kwargs): + self.tags = kwargs.pop("tags", None) + self.tagspec = ".*/seg/fs" + self.xml_tool = XML_Tool(filename, "ann_morphosyntax.xml") + XMLCorpusView.__init__( + self, self.xml_tool.build_preprocessed_file(), self.tagspec + ) + + def handle_query(self): + try: + self._open() + words = [] + while True: + segm = XMLCorpusView.read_block(self, self._stream) + if len(segm) == 0: + break + for part in segm: + if part is not None: + words.append(part) + self.close() + self.xml_tool.remove_preprocessed_file() + return words + except Exception as e: + self.xml_tool.remove_preprocessed_file() + raise Exception from e + + def handle_elt(self, elt, context): + word = "" + flag = False + is_not_interp = True + # if tags not specified, then always return word + if self.tags is None: + flag = True + + for child in elt: + + # get word + if "name" in child.keys() and child.attrib["name"] == "orth": + for symbol in child: + if symbol.tag == "string": + word = symbol.text + elif "name" in child.keys() and child.attrib["name"] == "interps": + for symbol in child: + if "type" in symbol.keys() and symbol.attrib["type"] == "lex": + for symbol2 in symbol: + if ( + "name" in symbol2.keys() + and symbol2.attrib["name"] == "ctag" + ): + for symbol3 in symbol2: + if ( + "value" in symbol3.keys() + and self.tags is not None + and symbol3.attrib["value"] in self.tags + ): + flag = True + elif ( + "value" in symbol3.keys() + and symbol3.attrib["value"] == "interp" + ): + is_not_interp = False + if flag and is_not_interp: + return word diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nombank.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nombank.py new file mode 100644 index 0000000..ab700ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nombank.py @@ -0,0 +1,466 @@ +# Natural Language Toolkit: NomBank Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Paul Bedaride +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +from functools import total_ordering +from xml.etree import ElementTree + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.internals import raise_unorderable_types +from nltk.tree import Tree + + +class NombankCorpusReader(CorpusReader): + """ + Corpus reader for the nombank corpus, which augments the Penn + Treebank with information about the predicate argument structure + of every noun instance. The corpus consists of two parts: the + predicate-argument annotations themselves, and a set of "frameset + files" which define the argument labels used by the annotations, + on a per-noun basis. Each "frameset file" contains one or more + predicates, such as ``'turn'`` or ``'turn_on'``, each of which is + divided into coarse-grained word senses called "rolesets". For + each "roleset", the frameset file provides descriptions of the + argument roles, along with examples. + """ + + def __init__( + self, + root, + nomfile, + framefiles="", + nounsfile=None, + parse_fileid_xform=None, + parse_corpus=None, + encoding="utf8", + ): + """ + :param root: The root directory for this corpus. + :param nomfile: The name of the file containing the predicate- + argument annotations (relative to ``root``). + :param framefiles: A list or regexp specifying the frameset + fileids for this corpus. + :param parse_fileid_xform: A transform that should be applied + to the fileids in this corpus. This should be a function + of one argument (a fileid) that returns a string (the new + fileid). + :param parse_corpus: The corpus containing the parse trees + corresponding to this corpus. These parse trees are + necessary to resolve the tree pointers used by nombank. + """ + + # If framefiles is specified as a regexp, expand it. + if isinstance(framefiles, str): + self._fileids = find_corpus_fileids(root, framefiles) + self._fileids = list(framefiles) + # Initialize the corpus reader. + CorpusReader.__init__(self, root, framefiles, encoding) + + # Record our nom file & nouns file. + self._nomfile = nomfile + self._nounsfile = nounsfile + self._parse_fileid_xform = parse_fileid_xform + self._parse_corpus = parse_corpus + + def instances(self, baseform=None): + """ + :return: a corpus view that acts as a list of + ``NombankInstance`` objects, one for each noun in the corpus. + """ + kwargs = {} + if baseform is not None: + kwargs["instance_filter"] = lambda inst: inst.baseform == baseform + return StreamBackedCorpusView( + self.abspath(self._nomfile), + lambda stream: self._read_instance_block(stream, **kwargs), + encoding=self.encoding(self._nomfile), + ) + + def lines(self): + """ + :return: a corpus view that acts as a list of strings, one for + each line in the predicate-argument annotation file. + """ + return StreamBackedCorpusView( + self.abspath(self._nomfile), + read_line_block, + encoding=self.encoding(self._nomfile), + ) + + def roleset(self, roleset_id): + """ + :return: the xml description for the given roleset. + """ + baseform = roleset_id.split(".")[0] + baseform = baseform.replace("perc-sign", "%") + baseform = baseform.replace("oneslashonezero", "1/10").replace( + "1/10", "1-slash-10" + ) + framefile = "frames/%s.xml" % baseform + if framefile not in self.fileids(): + raise ValueError("Frameset file for %s not found" % roleset_id) + + # n.b.: The encoding for XML fileids is specified by the file + # itself; so we ignore self._encoding here. + with self.abspath(framefile).open() as fp: + etree = ElementTree.parse(fp).getroot() + for roleset in etree.findall("predicate/roleset"): + if roleset.attrib["id"] == roleset_id: + return roleset + raise ValueError(f"Roleset {roleset_id} not found in {framefile}") + + def rolesets(self, baseform=None): + """ + :return: list of xml descriptions for rolesets. + """ + if baseform is not None: + framefile = "frames/%s.xml" % baseform + if framefile not in self.fileids(): + raise ValueError("Frameset file for %s not found" % baseform) + framefiles = [framefile] + else: + framefiles = self.fileids() + + rsets = [] + for framefile in framefiles: + # n.b.: The encoding for XML fileids is specified by the file + # itself; so we ignore self._encoding here. + with self.abspath(framefile).open() as fp: + etree = ElementTree.parse(fp).getroot() + rsets.append(etree.findall("predicate/roleset")) + return LazyConcatenation(rsets) + + def nouns(self): + """ + :return: a corpus view that acts as a list of all noun lemmas + in this corpus (from the nombank.1.0.words file). + """ + return StreamBackedCorpusView( + self.abspath(self._nounsfile), + read_line_block, + encoding=self.encoding(self._nounsfile), + ) + + def _read_instance_block(self, stream, instance_filter=lambda inst: True): + block = [] + + # Read 100 at a time. + for i in range(100): + line = stream.readline().strip() + if line: + inst = NombankInstance.parse( + line, self._parse_fileid_xform, self._parse_corpus + ) + if instance_filter(inst): + block.append(inst) + + return block + + +###################################################################### +# { Nombank Instance & related datatypes +###################################################################### + + +class NombankInstance: + def __init__( + self, + fileid, + sentnum, + wordnum, + baseform, + sensenumber, + predicate, + predid, + arguments, + parse_corpus=None, + ): + + self.fileid = fileid + """The name of the file containing the parse tree for this + instance's sentence.""" + + self.sentnum = sentnum + """The sentence number of this sentence within ``fileid``. + Indexing starts from zero.""" + + self.wordnum = wordnum + """The word number of this instance's predicate within its + containing sentence. Word numbers are indexed starting from + zero, and include traces and other empty parse elements.""" + + self.baseform = baseform + """The baseform of the predicate.""" + + self.sensenumber = sensenumber + """The sense number of the predicate.""" + + self.predicate = predicate + """A ``NombankTreePointer`` indicating the position of this + instance's predicate within its containing sentence.""" + + self.predid = predid + """Identifier of the predicate.""" + + self.arguments = tuple(arguments) + """A list of tuples (argloc, argid), specifying the location + and identifier for each of the predicate's argument in the + containing sentence. Argument identifiers are strings such as + ``'ARG0'`` or ``'ARGM-TMP'``. This list does *not* contain + the predicate.""" + + self.parse_corpus = parse_corpus + """A corpus reader for the parse trees corresponding to the + instances in this nombank corpus.""" + + @property + def roleset(self): + """The name of the roleset used by this instance's predicate. + Use ``nombank.roleset() `` to + look up information about the roleset.""" + r = self.baseform.replace("%", "perc-sign") + r = r.replace("1/10", "1-slash-10").replace("1-slash-10", "oneslashonezero") + return f"{r}.{self.sensenumber}" + + def __repr__(self): + return "".format( + self.fileid, + self.sentnum, + self.wordnum, + ) + + def __str__(self): + s = "{} {} {} {} {}".format( + self.fileid, + self.sentnum, + self.wordnum, + self.baseform, + self.sensenumber, + ) + items = self.arguments + ((self.predicate, "rel"),) + for (argloc, argid) in sorted(items): + s += f" {argloc}-{argid}" + return s + + def _get_tree(self): + if self.parse_corpus is None: + return None + if self.fileid not in self.parse_corpus.fileids(): + return None + return self.parse_corpus.parsed_sents(self.fileid)[self.sentnum] + + tree = property( + _get_tree, + doc=""" + The parse tree corresponding to this instance, or None if + the corresponding tree is not available.""", + ) + + @staticmethod + def parse(s, parse_fileid_xform=None, parse_corpus=None): + pieces = s.split() + if len(pieces) < 6: + raise ValueError("Badly formatted nombank line: %r" % s) + + # Divide the line into its basic pieces. + (fileid, sentnum, wordnum, baseform, sensenumber) = pieces[:5] + + args = pieces[5:] + rel = [args.pop(i) for i, p in enumerate(args) if "-rel" in p] + if len(rel) != 1: + raise ValueError("Badly formatted nombank line: %r" % s) + + # Apply the fileid selector, if any. + if parse_fileid_xform is not None: + fileid = parse_fileid_xform(fileid) + + # Convert sentence & word numbers to ints. + sentnum = int(sentnum) + wordnum = int(wordnum) + + # Parse the predicate location. + + predloc, predid = rel[0].split("-", 1) + predicate = NombankTreePointer.parse(predloc) + + # Parse the arguments. + arguments = [] + for arg in args: + argloc, argid = arg.split("-", 1) + arguments.append((NombankTreePointer.parse(argloc), argid)) + + # Put it all together. + return NombankInstance( + fileid, + sentnum, + wordnum, + baseform, + sensenumber, + predicate, + predid, + arguments, + parse_corpus, + ) + + +class NombankPointer: + """ + A pointer used by nombank to identify one or more constituents in + a parse tree. ``NombankPointer`` is an abstract base class with + three concrete subclasses: + + - ``NombankTreePointer`` is used to point to single constituents. + - ``NombankSplitTreePointer`` is used to point to 'split' + constituents, which consist of a sequence of two or more + ``NombankTreePointer`` pointers. + - ``NombankChainTreePointer`` is used to point to entire trace + chains in a tree. It consists of a sequence of pieces, which + can be ``NombankTreePointer`` or ``NombankSplitTreePointer`` pointers. + """ + + def __init__(self): + if self.__class__ == NombankPointer: + raise NotImplementedError() + + +class NombankChainTreePointer(NombankPointer): + def __init__(self, pieces): + self.pieces = pieces + """A list of the pieces that make up this chain. Elements may + be either ``NombankSplitTreePointer`` or + ``NombankTreePointer`` pointers.""" + + def __str__(self): + return "*".join("%s" % p for p in self.pieces) + + def __repr__(self): + return "" % self + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return Tree("*CHAIN*", [p.select(tree) for p in self.pieces]) + + +class NombankSplitTreePointer(NombankPointer): + def __init__(self, pieces): + self.pieces = pieces + """A list of the pieces that make up this chain. Elements are + all ``NombankTreePointer`` pointers.""" + + def __str__(self): + return ",".join("%s" % p for p in self.pieces) + + def __repr__(self): + return "" % self + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return Tree("*SPLIT*", [p.select(tree) for p in self.pieces]) + + +@total_ordering +class NombankTreePointer(NombankPointer): + """ + wordnum:height*wordnum:height*... + wordnum:height, + + """ + + def __init__(self, wordnum, height): + self.wordnum = wordnum + self.height = height + + @staticmethod + def parse(s): + # Deal with chains (xx*yy*zz) + pieces = s.split("*") + if len(pieces) > 1: + return NombankChainTreePointer( + [NombankTreePointer.parse(elt) for elt in pieces] + ) + + # Deal with split args (xx,yy,zz) + pieces = s.split(",") + if len(pieces) > 1: + return NombankSplitTreePointer( + [NombankTreePointer.parse(elt) for elt in pieces] + ) + + # Deal with normal pointers. + pieces = s.split(":") + if len(pieces) != 2: + raise ValueError("bad nombank pointer %r" % s) + return NombankTreePointer(int(pieces[0]), int(pieces[1])) + + def __str__(self): + return f"{self.wordnum}:{self.height}" + + def __repr__(self): + return "NombankTreePointer(%d, %d)" % (self.wordnum, self.height) + + def __eq__(self, other): + while isinstance(other, (NombankChainTreePointer, NombankSplitTreePointer)): + other = other.pieces[0] + + if not isinstance(other, NombankTreePointer): + return self is other + + return self.wordnum == other.wordnum and self.height == other.height + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + while isinstance(other, (NombankChainTreePointer, NombankSplitTreePointer)): + other = other.pieces[0] + + if not isinstance(other, NombankTreePointer): + return id(self) < id(other) + + return (self.wordnum, -self.height) < (other.wordnum, -other.height) + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return tree[self.treepos(tree)] + + def treepos(self, tree): + """ + Convert this pointer to a standard 'tree position' pointer, + given that it points to the given tree. + """ + if tree is None: + raise ValueError("Parse tree not available") + stack = [tree] + treepos = [] + + wordnum = 0 + while True: + # tree node: + if isinstance(stack[-1], Tree): + # Select the next child. + if len(treepos) < len(stack): + treepos.append(0) + else: + treepos[-1] += 1 + # Update the stack. + if treepos[-1] < len(stack[-1]): + stack.append(stack[-1][treepos[-1]]) + else: + # End of node's child list: pop up a level. + stack.pop() + treepos.pop() + # word node: + else: + if wordnum == self.wordnum: + return tuple(treepos[: len(treepos) - self.height - 1]) + else: + wordnum += 1 + stack.pop() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nps_chat.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nps_chat.py new file mode 100644 index 0000000..8efce88 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/nps_chat.py @@ -0,0 +1,90 @@ +# Natural Language Toolkit: NPS Chat Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +import re +import textwrap + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.corpus.reader.xmldocs import * +from nltk.internals import ElementWrapper +from nltk.tag import map_tag +from nltk.util import LazyConcatenation + + +class NPSChatCorpusReader(XMLCorpusReader): + def __init__(self, root, fileids, wrap_etree=False, tagset=None): + XMLCorpusReader.__init__(self, root, fileids, wrap_etree) + self._tagset = tagset + + def xml_posts(self, fileids=None): + if self._wrap_etree: + return concat( + [ + XMLCorpusView(fileid, "Session/Posts/Post", self._wrap_elt) + for fileid in self.abspaths(fileids) + ] + ) + else: + return concat( + [ + XMLCorpusView(fileid, "Session/Posts/Post") + for fileid in self.abspaths(fileids) + ] + ) + + def posts(self, fileids=None): + return concat( + [ + XMLCorpusView( + fileid, "Session/Posts/Post/terminals", self._elt_to_words + ) + for fileid in self.abspaths(fileids) + ] + ) + + def tagged_posts(self, fileids=None, tagset=None): + def reader(elt, handler): + return self._elt_to_tagged_words(elt, handler, tagset) + + return concat( + [ + XMLCorpusView(fileid, "Session/Posts/Post/terminals", reader) + for fileid in self.abspaths(fileids) + ] + ) + + def words(self, fileids=None): + return LazyConcatenation(self.posts(fileids)) + + def tagged_words(self, fileids=None, tagset=None): + return LazyConcatenation(self.tagged_posts(fileids, tagset)) + + def _wrap_elt(self, elt, handler): + return ElementWrapper(elt) + + def _elt_to_words(self, elt, handler): + return [self._simplify_username(t.attrib["word"]) for t in elt.findall("t")] + + def _elt_to_tagged_words(self, elt, handler, tagset=None): + tagged_post = [ + (self._simplify_username(t.attrib["word"]), t.attrib["pos"]) + for t in elt.findall("t") + ] + if tagset and tagset != self._tagset: + tagged_post = [ + (w, map_tag(self._tagset, tagset, t)) for (w, t) in tagged_post + ] + return tagged_post + + @staticmethod + def _simplify_username(word): + if "User" in word: + word = "U" + word.split("User", 1)[1] + elif isinstance(word, bytes): + word = word.decode("ascii") + return word diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/opinion_lexicon.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/opinion_lexicon.py new file mode 100644 index 0000000..a1abb4f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/opinion_lexicon.py @@ -0,0 +1,125 @@ +# Natural Language Toolkit: Opinion Lexicon Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader for the Opinion Lexicon. + +Opinion Lexicon information +=========================== + +Authors: Minqing Hu and Bing Liu, 2004. + Department of Computer Science + University of Illinois at Chicago + +Contact: Bing Liu, liub@cs.uic.edu + https://www.cs.uic.edu/~liub + +Distributed with permission. + +Related papers: + +- Minqing Hu and Bing Liu. "Mining and summarizing customer reviews". + Proceedings of the ACM SIGKDD International Conference on Knowledge Discovery + & Data Mining (KDD-04), Aug 22-25, 2004, Seattle, Washington, USA. + +- Bing Liu, Minqing Hu and Junsheng Cheng. "Opinion Observer: Analyzing and + Comparing Opinions on the Web". Proceedings of the 14th International World + Wide Web conference (WWW-2005), May 10-14, 2005, Chiba, Japan. +""" + +from nltk.corpus.reader import WordListCorpusReader +from nltk.corpus.reader.api import * + + +class IgnoreReadmeCorpusView(StreamBackedCorpusView): + """ + This CorpusView is used to skip the initial readme block of the corpus. + """ + + def __init__(self, *args, **kwargs): + StreamBackedCorpusView.__init__(self, *args, **kwargs) + # open self._stream + self._open() + # skip the readme block + read_blankline_block(self._stream) + # Set the initial position to the current stream position + self._filepos = [self._stream.tell()] + + +class OpinionLexiconCorpusReader(WordListCorpusReader): + """ + Reader for Liu and Hu opinion lexicon. Blank lines and readme are ignored. + + >>> from nltk.corpus import opinion_lexicon + >>> opinion_lexicon.words() + ['2-faced', '2-faces', 'abnormal', 'abolish', ...] + + The OpinionLexiconCorpusReader provides shortcuts to retrieve positive/negative + words: + + >>> opinion_lexicon.negative() + ['2-faced', '2-faces', 'abnormal', 'abolish', ...] + + Note that words from `words()` method are sorted by file id, not alphabetically: + + >>> opinion_lexicon.words()[0:10] # doctest: +NORMALIZE_WHITESPACE + ['2-faced', '2-faces', 'abnormal', 'abolish', 'abominable', 'abominably', + 'abominate', 'abomination', 'abort', 'aborted'] + >>> sorted(opinion_lexicon.words())[0:10] # doctest: +NORMALIZE_WHITESPACE + ['2-faced', '2-faces', 'a+', 'abnormal', 'abolish', 'abominable', 'abominably', + 'abominate', 'abomination', 'abort'] + """ + + CorpusView = IgnoreReadmeCorpusView + + def words(self, fileids=None): + """ + Return all words in the opinion lexicon. Note that these words are not + sorted in alphabetical order. + + :param fileids: a list or regexp specifying the ids of the files whose + words have to be returned. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def positive(self): + """ + Return all positive words in alphabetical order. + + :return: a list of positive words. + :rtype: list(str) + """ + return self.words("positive-words.txt") + + def negative(self): + """ + Return all negative words in alphabetical order. + + :return: a list of negative words. + :rtype: list(str) + """ + return self.words("negative-words.txt") + + def _read_word_block(self, stream): + words = [] + for i in range(20): # Read 20 lines at a time. + line = stream.readline() + if not line: + continue + words.append(line.strip()) + return words diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_lite.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_lite.py new file mode 100644 index 0000000..05264ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_lite.py @@ -0,0 +1,174 @@ +# Natural Language Toolkit: PanLex Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: David Kamholz +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader for PanLex Lite, a stripped down version of PanLex distributed +as an SQLite database. See the README.txt in the panlex_lite corpus directory +for more information on PanLex Lite. +""" + +import os +import sqlite3 + +from nltk.corpus.reader.api import CorpusReader + + +class PanLexLiteCorpusReader(CorpusReader): + MEANING_Q = """ + SELECT dnx2.mn, dnx2.uq, dnx2.ap, dnx2.ui, ex2.tt, ex2.lv + FROM dnx + JOIN ex ON (ex.ex = dnx.ex) + JOIN dnx dnx2 ON (dnx2.mn = dnx.mn) + JOIN ex ex2 ON (ex2.ex = dnx2.ex) + WHERE dnx.ex != dnx2.ex AND ex.tt = ? AND ex.lv = ? + ORDER BY dnx2.uq DESC + """ + + TRANSLATION_Q = """ + SELECT s.tt, sum(s.uq) AS trq FROM ( + SELECT ex2.tt, max(dnx.uq) AS uq + FROM dnx + JOIN ex ON (ex.ex = dnx.ex) + JOIN dnx dnx2 ON (dnx2.mn = dnx.mn) + JOIN ex ex2 ON (ex2.ex = dnx2.ex) + WHERE dnx.ex != dnx2.ex AND ex.lv = ? AND ex.tt = ? AND ex2.lv = ? + GROUP BY ex2.tt, dnx.ui + ) s + GROUP BY s.tt + ORDER BY trq DESC, s.tt + """ + + def __init__(self, root): + self._c = sqlite3.connect(os.path.join(root, "db.sqlite")).cursor() + + self._uid_lv = {} + self._lv_uid = {} + + for row in self._c.execute("SELECT uid, lv FROM lv"): + self._uid_lv[row[0]] = row[1] + self._lv_uid[row[1]] = row[0] + + def language_varieties(self, lc=None): + """ + Return a list of PanLex language varieties. + + :param lc: ISO 639 alpha-3 code. If specified, filters returned varieties + by this code. If unspecified, all varieties are returned. + :return: the specified language varieties as a list of tuples. The first + element is the language variety's seven-character uniform identifier, + and the second element is its default name. + :rtype: list(tuple) + """ + + if lc is None: + return self._c.execute("SELECT uid, tt FROM lv ORDER BY uid").fetchall() + else: + return self._c.execute( + "SELECT uid, tt FROM lv WHERE lc = ? ORDER BY uid", (lc,) + ).fetchall() + + def meanings(self, expr_uid, expr_tt): + """ + Return a list of meanings for an expression. + + :param expr_uid: the expression's language variety, as a seven-character + uniform identifier. + :param expr_tt: the expression's text. + :return: a list of Meaning objects. + :rtype: list(Meaning) + """ + + expr_lv = self._uid_lv[expr_uid] + + mn_info = {} + + for i in self._c.execute(self.MEANING_Q, (expr_tt, expr_lv)): + mn = i[0] + uid = self._lv_uid[i[5]] + + if not mn in mn_info: + mn_info[mn] = { + "uq": i[1], + "ap": i[2], + "ui": i[3], + "ex": {expr_uid: [expr_tt]}, + } + + if not uid in mn_info[mn]["ex"]: + mn_info[mn]["ex"][uid] = [] + + mn_info[mn]["ex"][uid].append(i[4]) + + return [Meaning(mn, mn_info[mn]) for mn in mn_info] + + def translations(self, from_uid, from_tt, to_uid): + """ + Return a list of translations for an expression into a single language + variety. + + :param from_uid: the source expression's language variety, as a + seven-character uniform identifier. + :param from_tt: the source expression's text. + :param to_uid: the target language variety, as a seven-character + uniform identifier. + :return: a list of translation tuples. The first element is the expression + text and the second element is the translation quality. + :rtype: list(tuple) + """ + + from_lv = self._uid_lv[from_uid] + to_lv = self._uid_lv[to_uid] + + return self._c.execute(self.TRANSLATION_Q, (from_lv, from_tt, to_lv)).fetchall() + + +class Meaning(dict): + """ + Represents a single PanLex meaning. A meaning is a translation set derived + from a single source. + """ + + def __init__(self, mn, attr): + super().__init__(**attr) + self["mn"] = mn + + def id(self): + """ + :return: the meaning's id. + :rtype: int + """ + return self["mn"] + + def quality(self): + """ + :return: the meaning's source's quality (0=worst, 9=best). + :rtype: int + """ + return self["uq"] + + def source(self): + """ + :return: the meaning's source id. + :rtype: int + """ + return self["ap"] + + def source_group(self): + """ + :return: the meaning's source group id. + :rtype: int + """ + return self["ui"] + + def expressions(self): + """ + :return: the meaning's expressions as a dictionary whose keys are language + variety uniform identifiers and whose values are lists of expression + texts. + :rtype: dict + """ + return self["ex"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_swadesh.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_swadesh.py new file mode 100644 index 0000000..4e6fee9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/panlex_swadesh.py @@ -0,0 +1,95 @@ +# Natural Language Toolkit: Word List Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + + +import re +from collections import defaultdict, namedtuple + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.corpus.reader.wordlist import WordListCorpusReader +from nltk.tokenize import line_tokenize + +PanlexLanguage = namedtuple( + "PanlexLanguage", + [ + "panlex_uid", # (1) PanLex UID + "iso639", # (2) ISO 639 language code + "iso639_type", # (3) ISO 639 language type, see README + "script", # (4) normal scripts of expressions + "name", # (5) PanLex default name + "langvar_uid", # (6) UID of the language variety in which the default name is an expression + ], +) + + +class PanlexSwadeshCorpusReader(WordListCorpusReader): + """ + This is a class to read the PanLex Swadesh list from + + David Kamholz, Jonathan Pool, and Susan M. Colowick (2014). + PanLex: Building a Resource for Panlingual Lexical Translation. + In LREC. http://www.lrec-conf.org/proceedings/lrec2014/pdf/1029_Paper.pdf + + License: CC0 1.0 Universal + https://creativecommons.org/publicdomain/zero/1.0/legalcode + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Find the swadesh size using the fileids' path. + self.swadesh_size = re.match(r"swadesh([0-9].*)\/", self.fileids()[0]).group(1) + self._languages = {lang.panlex_uid: lang for lang in self.get_languages()} + self._macro_langauges = self.get_macrolanguages() + + def license(self): + return "CC0 1.0 Universal" + + def language_codes(self): + return self._languages.keys() + + def get_languages(self): + for line in self.raw(f"langs{self.swadesh_size}.txt").split("\n"): + if not line.strip(): # Skip empty lines. + continue + yield PanlexLanguage(*line.strip().split("\t")) + + def get_macrolanguages(self): + macro_langauges = defaultdict(list) + for lang in self._languages.values(): + macro_langauges[lang.iso639].append(lang.panlex_uid) + return macro_langauges + + def words_by_lang(self, lang_code): + """ + :return: a list of list(str) + """ + fileid = f"swadesh{self.swadesh_size}/{lang_code}.txt" + return [concept.split("\t") for concept in self.words(fileid)] + + def words_by_iso639(self, iso63_code): + """ + :return: a list of list(str) + """ + fileids = [ + f"swadesh{self.swadesh_size}/{lang_code}.txt" + for lang_code in self._macro_langauges[iso63_code] + ] + return [ + concept.split("\t") for fileid in fileids for concept in self.words(fileid) + ] + + def entries(self, fileids=None): + """ + :return: a tuple of words for the specified fileids. + """ + if not fileids: + fileids = self.fileids() + + wordlists = [self.words(f) for f in fileids] + return list(zip(*wordlists)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pl196x.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pl196x.py new file mode 100644 index 0000000..bdb88a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pl196x.py @@ -0,0 +1,375 @@ +# Natural Language Toolkit: +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Piotr Kasprzyk +# URL: +# For license information, see LICENSE.TXT + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.xmldocs import XMLCorpusReader + +PARA = re.compile(r"]*){0,1}>(.*?)

    ") +SENT = re.compile(r"]*){0,1}>(.*?)
    ") + +TAGGEDWORD = re.compile(r"<([wc](?: [^>]*){0,1}>)(.*?)") +WORD = re.compile(r"<[wc](?: [^>]*){0,1}>(.*?)") + +TYPE = re.compile(r'type="(.*?)"') +ANA = re.compile(r'ana="(.*?)"') + +TEXTID = re.compile(r'text id="(.*?)"') + + +class TEICorpusView(StreamBackedCorpusView): + def __init__( + self, + corpus_file, + tagged, + group_by_sent, + group_by_para, + tagset=None, + head_len=0, + textids=None, + ): + + self._tagged = tagged + self._textids = textids + + self._group_by_sent = group_by_sent + self._group_by_para = group_by_para + # WARNING -- skip header + StreamBackedCorpusView.__init__(self, corpus_file, startpos=head_len) + + _pagesize = 4096 + + def read_block(self, stream): + block = stream.readlines(self._pagesize) + block = concat(block) + while (block.count(" block.count("")) or block.count( + "") + len("") + block = block[:beg] + block[beg + end :] + + output = [] + for para_str in PARA.findall(block): + para = [] + for sent_str in SENT.findall(para_str): + if not self._tagged: + sent = WORD.findall(sent_str) + else: + sent = list(map(self._parse_tag, TAGGEDWORD.findall(sent_str))) + if self._group_by_sent: + para.append(sent) + else: + para.extend(sent) + if self._group_by_para: + output.append(para) + else: + output.extend(para) + return output + + def _parse_tag(self, tag_word_tuple): + (tag, word) = tag_word_tuple + if tag.startswith("w"): + tag = ANA.search(tag).group(1) + else: # tag.startswith('c') + tag = TYPE.search(tag).group(1) + return word, tag + + +class Pl196xCorpusReader(CategorizedCorpusReader, XMLCorpusReader): + head_len = 2770 + + def __init__(self, *args, **kwargs): + if "textid_file" in kwargs: + self._textids = kwargs["textid_file"] + else: + self._textids = None + + XMLCorpusReader.__init__(self, *args) + CategorizedCorpusReader.__init__(self, kwargs) + + self._init_textids() + + def _init_textids(self): + self._f2t = defaultdict(list) + self._t2f = defaultdict(list) + if self._textids is not None: + with open(self._textids) as fp: + for line in fp: + line = line.strip() + file_id, text_ids = line.split(" ", 1) + if file_id not in self.fileids(): + raise ValueError( + "In text_id mapping file %s: %s not found" + % (self._textids, file_id) + ) + for text_id in text_ids.split(self._delimiter): + self._add_textids(file_id, text_id) + + def _add_textids(self, file_id, text_id): + self._f2t[file_id].append(text_id) + self._t2f[text_id].append(file_id) + + def _resolve(self, fileids, categories, textids=None): + tmp = None + if ( + len( + list( + filter( + lambda accessor: accessor is None, + (fileids, categories, textids), + ) + ) + ) + != 1 + ): + + raise ValueError( + "Specify exactly one of: fileids, " "categories or textids" + ) + + if fileids is not None: + return fileids, None + + if categories is not None: + return self.fileids(categories), None + + if textids is not None: + if isinstance(textids, str): + textids = [textids] + files = sum((self._t2f[t] for t in textids), []) + tdict = dict() + for f in files: + tdict[f] = set(self._f2t[f]) & set(textids) + return files, tdict + + def decode_tag(self, tag): + # to be implemented + return tag + + def textids(self, fileids=None, categories=None): + """ + In the pl196x corpus each category is stored in single + file and thus both methods provide identical functionality. In order + to accommodate finer granularity, a non-standard textids() method was + implemented. All the main functions can be supplied with a list + of required chunks---giving much more control to the user. + """ + fileids, _ = self._resolve(fileids, categories) + if fileids is None: + return sorted(self._t2f) + + if isinstance(fileids, str): + fileids = [fileids] + return sorted(sum((self._f2t[d] for d in fileids), [])) + + def words(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + False, + False, + False, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + False, + False, + False, + head_len=self.head_len, + ) + for fileid in fileids + ] + ) + + def sents(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + False, + True, + False, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), False, True, False, head_len=self.head_len + ) + for fileid in fileids + ] + ) + + def paras(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + False, + True, + True, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), False, True, True, head_len=self.head_len + ) + for fileid in fileids + ] + ) + + def tagged_words(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + True, + False, + False, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), True, False, False, head_len=self.head_len + ) + for fileid in fileids + ] + ) + + def tagged_sents(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + True, + True, + False, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), True, True, False, head_len=self.head_len + ) + for fileid in fileids + ] + ) + + def tagged_paras(self, fileids=None, categories=None, textids=None): + fileids, textids = self._resolve(fileids, categories, textids) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + + if textids: + return concat( + [ + TEICorpusView( + self.abspath(fileid), + True, + True, + True, + head_len=self.head_len, + textids=textids[fileid], + ) + for fileid in fileids + ] + ) + else: + return concat( + [ + TEICorpusView( + self.abspath(fileid), True, True, True, head_len=self.head_len + ) + for fileid in fileids + ] + ) + + def xml(self, fileids=None, categories=None): + fileids, _ = self._resolve(fileids, categories) + if len(fileids) == 1: + return XMLCorpusReader.xml(self, fileids[0]) + else: + raise TypeError("Expected a single file") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/plaintext.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/plaintext.py new file mode 100644 index 0000000..0b08b79 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/plaintext.py @@ -0,0 +1,227 @@ +# Natural Language Toolkit: Plaintext Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# Nitin Madnani +# URL: +# For license information, see LICENSE.TXT + +""" +A reader for corpora that consist of plaintext documents. +""" + +import nltk.data +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tokenize import * + + +class PlaintextCorpusReader(CorpusReader): + """ + Reader for corpora that consist of plaintext documents. Paragraphs + are assumed to be split using blank lines. Sentences and words can + be tokenized using the default tokenizers, or by custom tokenizers + specified as parameters to the constructor. + + This corpus reader can be customized (e.g., to skip preface + sections of specific document formats) by creating a subclass and + overriding the ``CorpusView`` class variable. + """ + + CorpusView = StreamBackedCorpusView + """The corpus view class used by this reader. Subclasses of + ``PlaintextCorpusReader`` may specify alternative corpus view + classes (e.g., to skip the preface sections of documents.)""" + + def __init__( + self, + root, + fileids, + word_tokenizer=WordPunctTokenizer(), + sent_tokenizer=nltk.data.LazyLoader("tokenizers/punkt/english.pickle"), + para_block_reader=read_blankline_block, + encoding="utf8", + ): + r""" + Construct a new plaintext corpus reader for a set of documents + located at the given root directory. Example usage: + + >>> root = '/usr/local/share/nltk_data/corpora/webtext/' + >>> reader = PlaintextCorpusReader(root, '.*\.txt') # doctest: +SKIP + + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + :param word_tokenizer: Tokenizer for breaking sentences or + paragraphs into words. + :param sent_tokenizer: Tokenizer for breaking paragraphs + into words. + :param para_block_reader: The block reader used to divide the + corpus into paragraph blocks. + """ + CorpusReader.__init__(self, root, fileids, encoding) + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._para_block_reader = para_block_reader + + def words(self, fileids=None): + """ + :return: the given file(s) as a list of words + and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def sents(self, fileids=None): + """ + :return: the given file(s) as a list of + sentences or utterances, each encoded as a list of word + strings. + :rtype: list(list(str)) + """ + if self._sent_tokenizer is None: + raise ValueError("No sentence tokenizer for this corpus") + + return concat( + [ + self.CorpusView(path, self._read_sent_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def paras(self, fileids=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as lists of word strings. + :rtype: list(list(list(str))) + """ + if self._sent_tokenizer is None: + raise ValueError("No sentence tokenizer for this corpus") + + return concat( + [ + self.CorpusView(path, self._read_para_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def _read_word_block(self, stream): + words = [] + for i in range(20): # Read 20 lines at a time. + words.extend(self._word_tokenizer.tokenize(stream.readline())) + return words + + def _read_sent_block(self, stream): + sents = [] + for para in self._para_block_reader(stream): + sents.extend( + [ + self._word_tokenizer.tokenize(sent) + for sent in self._sent_tokenizer.tokenize(para) + ] + ) + return sents + + def _read_para_block(self, stream): + paras = [] + for para in self._para_block_reader(stream): + paras.append( + [ + self._word_tokenizer.tokenize(sent) + for sent in self._sent_tokenizer.tokenize(para) + ] + ) + return paras + + +class CategorizedPlaintextCorpusReader(CategorizedCorpusReader, PlaintextCorpusReader): + """ + A reader for plaintext corpora whose documents are divided into + categories based on their file identifiers. + """ + + def __init__(self, *args, **kwargs): + """ + Initialize the corpus reader. Categorization arguments + (``cat_pattern``, ``cat_map``, and ``cat_file``) are passed to + the ``CategorizedCorpusReader`` constructor. The remaining arguments + are passed to the ``PlaintextCorpusReader`` constructor. + """ + CategorizedCorpusReader.__init__(self, kwargs) + PlaintextCorpusReader.__init__(self, *args, **kwargs) + + +# FIXME: Is there a better way? How to not hardcode this? +# Possibly, add a language kwargs to CategorizedPlaintextCorpusReader to +# override the `sent_tokenizer`. +class PortugueseCategorizedPlaintextCorpusReader(CategorizedPlaintextCorpusReader): + def __init__(self, *args, **kwargs): + CategorizedCorpusReader.__init__(self, kwargs) + kwargs["sent_tokenizer"] = nltk.data.LazyLoader( + "tokenizers/punkt/portuguese.pickle" + ) + PlaintextCorpusReader.__init__(self, *args, **kwargs) + + +class EuroparlCorpusReader(PlaintextCorpusReader): + + """ + Reader for Europarl corpora that consist of plaintext documents. + Documents are divided into chapters instead of paragraphs as + for regular plaintext documents. Chapters are separated using blank + lines. Everything is inherited from ``PlaintextCorpusReader`` except + that: + + - Since the corpus is pre-processed and pre-tokenized, the + word tokenizer should just split the line at whitespaces. + - For the same reason, the sentence tokenizer should just + split the paragraph at line breaks. + - There is a new 'chapters()' method that returns chapters instead + instead of paragraphs. + - The 'paras()' method inherited from PlaintextCorpusReader is + made non-functional to remove any confusion between chapters + and paragraphs for Europarl. + """ + + def _read_word_block(self, stream): + words = [] + for i in range(20): # Read 20 lines at a time. + words.extend(stream.readline().split()) + return words + + def _read_sent_block(self, stream): + sents = [] + for para in self._para_block_reader(stream): + sents.extend([sent.split() for sent in para.splitlines()]) + return sents + + def _read_para_block(self, stream): + paras = [] + for para in self._para_block_reader(stream): + paras.append([sent.split() for sent in para.splitlines()]) + return paras + + def chapters(self, fileids=None): + """ + :return: the given file(s) as a list of + chapters, each encoded as a list of sentences, which are + in turn encoded as lists of word strings. + :rtype: list(list(list(str))) + """ + return concat( + [ + self.CorpusView(fileid, self._read_para_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def paras(self, fileids=None): + raise NotImplementedError( + "The Europarl corpus reader does not support paragraphs. Please use chapters() instead." + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ppattach.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ppattach.py new file mode 100644 index 0000000..e7296b0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ppattach.py @@ -0,0 +1,95 @@ +# Natural Language Toolkit: PP Attachment Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Read lines from the Prepositional Phrase Attachment Corpus. + +The PP Attachment Corpus contains several files having the format: + +sentence_id verb noun1 preposition noun2 attachment + +For example: + +42960 gives authority to administration V +46742 gives inventors of microchip N + +The PP attachment is to the verb phrase (V) or noun phrase (N), i.e.: + +(VP gives (NP authority) (PP to administration)) +(VP gives (NP inventors (PP of microchip))) + +The corpus contains the following files: + +training: training set +devset: development test set, used for algorithm development. +test: test set, used to report results +bitstrings: word classes derived from Mutual Information Clustering for the Wall Street Journal. + +Ratnaparkhi, Adwait (1994). A Maximum Entropy Model for Prepositional +Phrase Attachment. Proceedings of the ARPA Human Language Technology +Conference. [http://www.cis.upenn.edu/~adwait/papers/hlt94.ps] + +The PP Attachment Corpus is distributed with NLTK with the permission +of the author. +""" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * + + +class PPAttachment: + def __init__(self, sent, verb, noun1, prep, noun2, attachment): + self.sent = sent + self.verb = verb + self.noun1 = noun1 + self.prep = prep + self.noun2 = noun2 + self.attachment = attachment + + def __repr__(self): + return ( + "PPAttachment(sent=%r, verb=%r, noun1=%r, prep=%r, " + "noun2=%r, attachment=%r)" + % (self.sent, self.verb, self.noun1, self.prep, self.noun2, self.attachment) + ) + + +class PPAttachmentCorpusReader(CorpusReader): + """ + sentence_id verb noun1 preposition noun2 attachment + """ + + def attachments(self, fileids): + return concat( + [ + StreamBackedCorpusView(fileid, self._read_obj_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tuples(self, fileids): + return concat( + [ + StreamBackedCorpusView(fileid, self._read_tuple_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def _read_tuple_block(self, stream): + line = stream.readline() + if line: + return [tuple(line.split())] + else: + return [] + + def _read_obj_block(self, stream): + line = stream.readline() + if line: + return [PPAttachment(*line.split())] + else: + return [] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/propbank.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/propbank.py new file mode 100644 index 0000000..936a4c3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/propbank.py @@ -0,0 +1,520 @@ +# Natural Language Toolkit: PropBank Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +import re +from functools import total_ordering +from xml.etree import ElementTree + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.internals import raise_unorderable_types +from nltk.tree import Tree + + +class PropbankCorpusReader(CorpusReader): + """ + Corpus reader for the propbank corpus, which augments the Penn + Treebank with information about the predicate argument structure + of every verb instance. The corpus consists of two parts: the + predicate-argument annotations themselves, and a set of "frameset + files" which define the argument labels used by the annotations, + on a per-verb basis. Each "frameset file" contains one or more + predicates, such as ``'turn'`` or ``'turn_on'``, each of which is + divided into coarse-grained word senses called "rolesets". For + each "roleset", the frameset file provides descriptions of the + argument roles, along with examples. + """ + + def __init__( + self, + root, + propfile, + framefiles="", + verbsfile=None, + parse_fileid_xform=None, + parse_corpus=None, + encoding="utf8", + ): + """ + :param root: The root directory for this corpus. + :param propfile: The name of the file containing the predicate- + argument annotations (relative to ``root``). + :param framefiles: A list or regexp specifying the frameset + fileids for this corpus. + :param parse_fileid_xform: A transform that should be applied + to the fileids in this corpus. This should be a function + of one argument (a fileid) that returns a string (the new + fileid). + :param parse_corpus: The corpus containing the parse trees + corresponding to this corpus. These parse trees are + necessary to resolve the tree pointers used by propbank. + """ + # If framefiles is specified as a regexp, expand it. + if isinstance(framefiles, str): + framefiles = find_corpus_fileids(root, framefiles) + framefiles = list(framefiles) + # Initialize the corpus reader. + CorpusReader.__init__(self, root, [propfile, verbsfile] + framefiles, encoding) + + # Record our frame fileids & prop file. + self._propfile = propfile + self._framefiles = framefiles + self._verbsfile = verbsfile + self._parse_fileid_xform = parse_fileid_xform + self._parse_corpus = parse_corpus + + def instances(self, baseform=None): + """ + :return: a corpus view that acts as a list of + ``PropBankInstance`` objects, one for each noun in the corpus. + """ + kwargs = {} + if baseform is not None: + kwargs["instance_filter"] = lambda inst: inst.baseform == baseform + return StreamBackedCorpusView( + self.abspath(self._propfile), + lambda stream: self._read_instance_block(stream, **kwargs), + encoding=self.encoding(self._propfile), + ) + + def lines(self): + """ + :return: a corpus view that acts as a list of strings, one for + each line in the predicate-argument annotation file. + """ + return StreamBackedCorpusView( + self.abspath(self._propfile), + read_line_block, + encoding=self.encoding(self._propfile), + ) + + def roleset(self, roleset_id): + """ + :return: the xml description for the given roleset. + """ + baseform = roleset_id.split(".")[0] + framefile = "frames/%s.xml" % baseform + if framefile not in self._framefiles: + raise ValueError("Frameset file for %s not found" % roleset_id) + + # n.b.: The encoding for XML fileids is specified by the file + # itself; so we ignore self._encoding here. + with self.abspath(framefile).open() as fp: + etree = ElementTree.parse(fp).getroot() + for roleset in etree.findall("predicate/roleset"): + if roleset.attrib["id"] == roleset_id: + return roleset + raise ValueError(f"Roleset {roleset_id} not found in {framefile}") + + def rolesets(self, baseform=None): + """ + :return: list of xml descriptions for rolesets. + """ + if baseform is not None: + framefile = "frames/%s.xml" % baseform + if framefile not in self._framefiles: + raise ValueError("Frameset file for %s not found" % baseform) + framefiles = [framefile] + else: + framefiles = self._framefiles + + rsets = [] + for framefile in framefiles: + # n.b.: The encoding for XML fileids is specified by the file + # itself; so we ignore self._encoding here. + with self.abspath(framefile).open() as fp: + etree = ElementTree.parse(fp).getroot() + rsets.append(etree.findall("predicate/roleset")) + return LazyConcatenation(rsets) + + def verbs(self): + """ + :return: a corpus view that acts as a list of all verb lemmas + in this corpus (from the verbs.txt file). + """ + return StreamBackedCorpusView( + self.abspath(self._verbsfile), + read_line_block, + encoding=self.encoding(self._verbsfile), + ) + + def _read_instance_block(self, stream, instance_filter=lambda inst: True): + block = [] + + # Read 100 at a time. + for i in range(100): + line = stream.readline().strip() + if line: + inst = PropbankInstance.parse( + line, self._parse_fileid_xform, self._parse_corpus + ) + if instance_filter(inst): + block.append(inst) + + return block + + +###################################################################### +# { Propbank Instance & related datatypes +###################################################################### + + +class PropbankInstance: + def __init__( + self, + fileid, + sentnum, + wordnum, + tagger, + roleset, + inflection, + predicate, + arguments, + parse_corpus=None, + ): + + self.fileid = fileid + """The name of the file containing the parse tree for this + instance's sentence.""" + + self.sentnum = sentnum + """The sentence number of this sentence within ``fileid``. + Indexing starts from zero.""" + + self.wordnum = wordnum + """The word number of this instance's predicate within its + containing sentence. Word numbers are indexed starting from + zero, and include traces and other empty parse elements.""" + + self.tagger = tagger + """An identifier for the tagger who tagged this instance; or + ``'gold'`` if this is an adjuticated instance.""" + + self.roleset = roleset + """The name of the roleset used by this instance's predicate. + Use ``propbank.roleset() `` to + look up information about the roleset.""" + + self.inflection = inflection + """A ``PropbankInflection`` object describing the inflection of + this instance's predicate.""" + + self.predicate = predicate + """A ``PropbankTreePointer`` indicating the position of this + instance's predicate within its containing sentence.""" + + self.arguments = tuple(arguments) + """A list of tuples (argloc, argid), specifying the location + and identifier for each of the predicate's argument in the + containing sentence. Argument identifiers are strings such as + ``'ARG0'`` or ``'ARGM-TMP'``. This list does *not* contain + the predicate.""" + + self.parse_corpus = parse_corpus + """A corpus reader for the parse trees corresponding to the + instances in this propbank corpus.""" + + @property + def baseform(self): + """The baseform of the predicate.""" + return self.roleset.split(".")[0] + + @property + def sensenumber(self): + """The sense number of the predicate.""" + return self.roleset.split(".")[1] + + @property + def predid(self): + """Identifier of the predicate.""" + return "rel" + + def __repr__(self): + return "".format( + self.fileid, + self.sentnum, + self.wordnum, + ) + + def __str__(self): + s = "{} {} {} {} {} {}".format( + self.fileid, + self.sentnum, + self.wordnum, + self.tagger, + self.roleset, + self.inflection, + ) + items = self.arguments + ((self.predicate, "rel"),) + for (argloc, argid) in sorted(items): + s += f" {argloc}-{argid}" + return s + + def _get_tree(self): + if self.parse_corpus is None: + return None + if self.fileid not in self.parse_corpus.fileids(): + return None + return self.parse_corpus.parsed_sents(self.fileid)[self.sentnum] + + tree = property( + _get_tree, + doc=""" + The parse tree corresponding to this instance, or None if + the corresponding tree is not available.""", + ) + + @staticmethod + def parse(s, parse_fileid_xform=None, parse_corpus=None): + pieces = s.split() + if len(pieces) < 7: + raise ValueError("Badly formatted propbank line: %r" % s) + + # Divide the line into its basic pieces. + (fileid, sentnum, wordnum, tagger, roleset, inflection) = pieces[:6] + rel = [p for p in pieces[6:] if p.endswith("-rel")] + args = [p for p in pieces[6:] if not p.endswith("-rel")] + if len(rel) != 1: + raise ValueError("Badly formatted propbank line: %r" % s) + + # Apply the fileid selector, if any. + if parse_fileid_xform is not None: + fileid = parse_fileid_xform(fileid) + + # Convert sentence & word numbers to ints. + sentnum = int(sentnum) + wordnum = int(wordnum) + + # Parse the inflection + inflection = PropbankInflection.parse(inflection) + + # Parse the predicate location. + predicate = PropbankTreePointer.parse(rel[0][:-4]) + + # Parse the arguments. + arguments = [] + for arg in args: + argloc, argid = arg.split("-", 1) + arguments.append((PropbankTreePointer.parse(argloc), argid)) + + # Put it all together. + return PropbankInstance( + fileid, + sentnum, + wordnum, + tagger, + roleset, + inflection, + predicate, + arguments, + parse_corpus, + ) + + +class PropbankPointer: + """ + A pointer used by propbank to identify one or more constituents in + a parse tree. ``PropbankPointer`` is an abstract base class with + three concrete subclasses: + + - ``PropbankTreePointer`` is used to point to single constituents. + - ``PropbankSplitTreePointer`` is used to point to 'split' + constituents, which consist of a sequence of two or more + ``PropbankTreePointer`` pointers. + - ``PropbankChainTreePointer`` is used to point to entire trace + chains in a tree. It consists of a sequence of pieces, which + can be ``PropbankTreePointer`` or ``PropbankSplitTreePointer`` pointers. + """ + + def __init__(self): + if self.__class__ == PropbankPointer: + raise NotImplementedError() + + +class PropbankChainTreePointer(PropbankPointer): + def __init__(self, pieces): + self.pieces = pieces + """A list of the pieces that make up this chain. Elements may + be either ``PropbankSplitTreePointer`` or + ``PropbankTreePointer`` pointers.""" + + def __str__(self): + return "*".join("%s" % p for p in self.pieces) + + def __repr__(self): + return "" % self + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return Tree("*CHAIN*", [p.select(tree) for p in self.pieces]) + + +class PropbankSplitTreePointer(PropbankPointer): + def __init__(self, pieces): + self.pieces = pieces + """A list of the pieces that make up this chain. Elements are + all ``PropbankTreePointer`` pointers.""" + + def __str__(self): + return ",".join("%s" % p for p in self.pieces) + + def __repr__(self): + return "" % self + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return Tree("*SPLIT*", [p.select(tree) for p in self.pieces]) + + +@total_ordering +class PropbankTreePointer(PropbankPointer): + """ + wordnum:height*wordnum:height*... + wordnum:height, + + """ + + def __init__(self, wordnum, height): + self.wordnum = wordnum + self.height = height + + @staticmethod + def parse(s): + # Deal with chains (xx*yy*zz) + pieces = s.split("*") + if len(pieces) > 1: + return PropbankChainTreePointer( + [PropbankTreePointer.parse(elt) for elt in pieces] + ) + + # Deal with split args (xx,yy,zz) + pieces = s.split(",") + if len(pieces) > 1: + return PropbankSplitTreePointer( + [PropbankTreePointer.parse(elt) for elt in pieces] + ) + + # Deal with normal pointers. + pieces = s.split(":") + if len(pieces) != 2: + raise ValueError("bad propbank pointer %r" % s) + return PropbankTreePointer(int(pieces[0]), int(pieces[1])) + + def __str__(self): + return f"{self.wordnum}:{self.height}" + + def __repr__(self): + return "PropbankTreePointer(%d, %d)" % (self.wordnum, self.height) + + def __eq__(self, other): + while isinstance(other, (PropbankChainTreePointer, PropbankSplitTreePointer)): + other = other.pieces[0] + + if not isinstance(other, PropbankTreePointer): + return self is other + + return self.wordnum == other.wordnum and self.height == other.height + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + while isinstance(other, (PropbankChainTreePointer, PropbankSplitTreePointer)): + other = other.pieces[0] + + if not isinstance(other, PropbankTreePointer): + return id(self) < id(other) + + return (self.wordnum, -self.height) < (other.wordnum, -other.height) + + def select(self, tree): + if tree is None: + raise ValueError("Parse tree not available") + return tree[self.treepos(tree)] + + def treepos(self, tree): + """ + Convert this pointer to a standard 'tree position' pointer, + given that it points to the given tree. + """ + if tree is None: + raise ValueError("Parse tree not available") + stack = [tree] + treepos = [] + + wordnum = 0 + while True: + # tree node: + if isinstance(stack[-1], Tree): + # Select the next child. + if len(treepos) < len(stack): + treepos.append(0) + else: + treepos[-1] += 1 + # Update the stack. + if treepos[-1] < len(stack[-1]): + stack.append(stack[-1][treepos[-1]]) + else: + # End of node's child list: pop up a level. + stack.pop() + treepos.pop() + # word node: + else: + if wordnum == self.wordnum: + return tuple(treepos[: len(treepos) - self.height - 1]) + else: + wordnum += 1 + stack.pop() + + +class PropbankInflection: + # { Inflection Form + INFINITIVE = "i" + GERUND = "g" + PARTICIPLE = "p" + FINITE = "v" + # { Inflection Tense + FUTURE = "f" + PAST = "p" + PRESENT = "n" + # { Inflection Aspect + PERFECT = "p" + PROGRESSIVE = "o" + PERFECT_AND_PROGRESSIVE = "b" + # { Inflection Person + THIRD_PERSON = "3" + # { Inflection Voice + ACTIVE = "a" + PASSIVE = "p" + # { Inflection + NONE = "-" + # } + + def __init__(self, form="-", tense="-", aspect="-", person="-", voice="-"): + self.form = form + self.tense = tense + self.aspect = aspect + self.person = person + self.voice = voice + + def __str__(self): + return self.form + self.tense + self.aspect + self.person + self.voice + + def __repr__(self): + return "" % self + + _VALIDATE = re.compile(r"[igpv\-][fpn\-][pob\-][3\-][ap\-]$") + + @staticmethod + def parse(s): + if not isinstance(s, str): + raise TypeError("expected a string") + if len(s) != 5 or not PropbankInflection._VALIDATE.match(s): + raise ValueError("Bad propbank inflection string %r" % s) + return PropbankInflection(*s) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pros_cons.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pros_cons.py new file mode 100644 index 0000000..e4189da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/pros_cons.py @@ -0,0 +1,133 @@ +# Natural Language Toolkit: Pros and Cons Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader for the Pros and Cons dataset. + +- Pros and Cons dataset information - + +Contact: Bing Liu, liub@cs.uic.edu + https://www.cs.uic.edu/~liub + +Distributed with permission. + +Related papers: + +- Murthy Ganapathibhotla and Bing Liu. "Mining Opinions in Comparative Sentences". + Proceedings of the 22nd International Conference on Computational Linguistics + (Coling-2008), Manchester, 18-22 August, 2008. + +- Bing Liu, Minqing Hu and Junsheng Cheng. "Opinion Observer: Analyzing and Comparing + Opinions on the Web". Proceedings of the 14th international World Wide Web + conference (WWW-2005), May 10-14, 2005, in Chiba, Japan. +""" +import re + +from nltk.corpus.reader.api import * +from nltk.tokenize import * + + +class ProsConsCorpusReader(CategorizedCorpusReader, CorpusReader): + """ + Reader for the Pros and Cons sentence dataset. + + >>> from nltk.corpus import pros_cons + >>> pros_cons.sents(categories='Cons') # doctest: +NORMALIZE_WHITESPACE + [['East', 'batteries', '!', 'On', '-', 'off', 'switch', 'too', 'easy', + 'to', 'maneuver', '.'], ['Eats', '...', 'no', ',', 'GULPS', 'batteries'], + ...] + >>> pros_cons.words('IntegratedPros.txt') + ['Easy', 'to', 'use', ',', 'economical', '!', ...] + """ + + CorpusView = StreamBackedCorpusView + + def __init__( + self, + root, + fileids, + word_tokenizer=WordPunctTokenizer(), + encoding="utf8", + **kwargs + ): + """ + :param root: The root directory for the corpus. + :param fileids: a list or regexp specifying the fileids in the corpus. + :param word_tokenizer: a tokenizer for breaking sentences or paragraphs + into words. Default: `WhitespaceTokenizer` + :param encoding: the encoding that should be used to read the corpus. + :param kwargs: additional parameters passed to CategorizedCorpusReader. + """ + + CorpusReader.__init__(self, root, fileids, encoding) + CategorizedCorpusReader.__init__(self, kwargs) + self._word_tokenizer = word_tokenizer + + def sents(self, fileids=None, categories=None): + """ + Return all sentences in the corpus or in the specified files/categories. + + :param fileids: a list or regexp specifying the ids of the files whose + sentences have to be returned. + :param categories: a list specifying the categories whose sentences + have to be returned. + :return: the given file(s) as a list of sentences. Each sentence is + tokenized using the specified word_tokenizer. + :rtype: list(list(str)) + """ + fileids = self._resolve(fileids, categories) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_sent_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def words(self, fileids=None, categories=None): + """ + Return all words and punctuation symbols in the corpus or in the specified + files/categories. + + :param fileids: a list or regexp specifying the ids of the files whose + words have to be returned. + :param categories: a list specifying the categories whose words have + to be returned. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + fileids = self._resolve(fileids, categories) + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def _read_sent_block(self, stream): + sents = [] + for i in range(20): # Read 20 lines at a time. + line = stream.readline() + if not line: + continue + sent = re.match(r"^(?!\n)\s*<(Pros|Cons)>(.*)", line) + if sent: + sents.append(self._word_tokenizer.tokenize(sent.group(2).strip())) + return sents + + def _read_word_block(self, stream): + words = [] + for sent in self._read_sent_block(stream): + words.extend(sent) + return words diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/reviews.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/reviews.py new file mode 100644 index 0000000..4ef929f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/reviews.py @@ -0,0 +1,331 @@ +# Natural Language Toolkit: Product Reviews Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +CorpusReader for reviews corpora (syntax based on Customer Review Corpus). + +Customer Review Corpus information +================================== + +Annotated by: Minqing Hu and Bing Liu, 2004. + Department of Computer Science + University of Illinois at Chicago + +Contact: Bing Liu, liub@cs.uic.edu + https://www.cs.uic.edu/~liub + +Distributed with permission. + +The "product_reviews_1" and "product_reviews_2" datasets respectively contain +annotated customer reviews of 5 and 9 products from amazon.com. + +Related papers: + +- Minqing Hu and Bing Liu. "Mining and summarizing customer reviews". + Proceedings of the ACM SIGKDD International Conference on Knowledge + Discovery & Data Mining (KDD-04), 2004. + +- Minqing Hu and Bing Liu. "Mining Opinion Features in Customer Reviews". + Proceedings of Nineteeth National Conference on Artificial Intelligence + (AAAI-2004), 2004. + +- Xiaowen Ding, Bing Liu and Philip S. Yu. "A Holistic Lexicon-Based Appraoch to + Opinion Mining." Proceedings of First ACM International Conference on Web + Search and Data Mining (WSDM-2008), Feb 11-12, 2008, Stanford University, + Stanford, California, USA. + +Symbols used in the annotated reviews: + + :[t]: the title of the review: Each [t] tag starts a review. + :xxxx[+|-n]: xxxx is a product feature. + :[+n]: Positive opinion, n is the opinion strength: 3 strongest, and 1 weakest. + Note that the strength is quite subjective. + You may want ignore it, but only considering + and - + :[-n]: Negative opinion + :##: start of each sentence. Each line is a sentence. + :[u]: feature not appeared in the sentence. + :[p]: feature not appeared in the sentence. Pronoun resolution is needed. + :[s]: suggestion or recommendation. + :[cc]: comparison with a competing product from a different brand. + :[cs]: comparison with a competing product from the same brand. + +Note: Some of the files (e.g. "ipod.txt", "Canon PowerShot SD500.txt") do not + provide separation between different reviews. This is due to the fact that + the dataset was specifically designed for aspect/feature-based sentiment + analysis, for which sentence-level annotation is sufficient. For document- + level classification and analysis, this peculiarity should be taken into + consideration. +""" + +import re + +from nltk.corpus.reader.api import * +from nltk.tokenize import * + +TITLE = re.compile(r"^\[t\](.*)$") # [t] Title +FEATURES = re.compile( + r"((?:(?:\w+\s)+)?\w+)\[((?:\+|\-)\d)\]" +) # find 'feature' in feature[+3] +NOTES = re.compile(r"\[(?!t)(p|u|s|cc|cs)\]") # find 'p' in camera[+2][p] +SENT = re.compile(r"##(.*)$") # find tokenized sentence + + +class Review: + """ + A Review is the main block of a ReviewsCorpusReader. + """ + + def __init__(self, title=None, review_lines=None): + """ + :param title: the title of the review. + :param review_lines: the list of the ReviewLines that belong to the Review. + """ + self.title = title + if review_lines is None: + self.review_lines = [] + else: + self.review_lines = review_lines + + def add_line(self, review_line): + """ + Add a line (ReviewLine) to the review. + + :param review_line: a ReviewLine instance that belongs to the Review. + """ + assert isinstance(review_line, ReviewLine) + self.review_lines.append(review_line) + + def features(self): + """ + Return a list of features in the review. Each feature is a tuple made of + the specific item feature and the opinion strength about that feature. + + :return: all features of the review as a list of tuples (feat, score). + :rtype: list(tuple) + """ + features = [] + for review_line in self.review_lines: + features.extend(review_line.features) + return features + + def sents(self): + """ + Return all tokenized sentences in the review. + + :return: all sentences of the review as lists of tokens. + :rtype: list(list(str)) + """ + return [review_line.sent for review_line in self.review_lines] + + def __repr__(self): + return 'Review(title="{}", review_lines={})'.format( + self.title, self.review_lines + ) + + +class ReviewLine: + """ + A ReviewLine represents a sentence of the review, together with (optional) + annotations of its features and notes about the reviewed item. + """ + + def __init__(self, sent, features=None, notes=None): + self.sent = sent + if features is None: + self.features = [] + else: + self.features = features + + if notes is None: + self.notes = [] + else: + self.notes = notes + + def __repr__(self): + return "ReviewLine(features={}, notes={}, sent={})".format( + self.features, self.notes, self.sent + ) + + +class ReviewsCorpusReader(CorpusReader): + """ + Reader for the Customer Review Data dataset by Hu, Liu (2004). + Note: we are not applying any sentence tokenization at the moment, just word + tokenization. + + >>> from nltk.corpus import product_reviews_1 + >>> camera_reviews = product_reviews_1.reviews('Canon_G3.txt') + >>> review = camera_reviews[0] + >>> review.sents()[0] # doctest: +NORMALIZE_WHITESPACE + ['i', 'recently', 'purchased', 'the', 'canon', 'powershot', 'g3', 'and', 'am', + 'extremely', 'satisfied', 'with', 'the', 'purchase', '.'] + >>> review.features() # doctest: +NORMALIZE_WHITESPACE + [('canon powershot g3', '+3'), ('use', '+2'), ('picture', '+2'), + ('picture quality', '+1'), ('picture quality', '+1'), ('camera', '+2'), + ('use', '+2'), ('feature', '+1'), ('picture quality', '+3'), ('use', '+1'), + ('option', '+1')] + + We can also reach the same information directly from the stream: + + >>> product_reviews_1.features('Canon_G3.txt') + [('canon powershot g3', '+3'), ('use', '+2'), ...] + + We can compute stats for specific product features: + + >>> n_reviews = len([(feat,score) for (feat,score) in product_reviews_1.features('Canon_G3.txt') if feat=='picture']) + >>> tot = sum([int(score) for (feat,score) in product_reviews_1.features('Canon_G3.txt') if feat=='picture']) + >>> mean = tot / n_reviews + >>> print(n_reviews, tot, mean) + 15 24 1.6 + """ + + CorpusView = StreamBackedCorpusView + + def __init__( + self, root, fileids, word_tokenizer=WordPunctTokenizer(), encoding="utf8" + ): + """ + :param root: The root directory for the corpus. + :param fileids: a list or regexp specifying the fileids in the corpus. + :param word_tokenizer: a tokenizer for breaking sentences or paragraphs + into words. Default: `WordPunctTokenizer` + :param encoding: the encoding that should be used to read the corpus. + """ + + CorpusReader.__init__(self, root, fileids, encoding) + self._word_tokenizer = word_tokenizer + self._readme = "README.txt" + + def features(self, fileids=None): + """ + Return a list of features. Each feature is a tuple made of the specific + item feature and the opinion strength about that feature. + + :param fileids: a list or regexp specifying the ids of the files whose + features have to be returned. + :return: all features for the item(s) in the given file(s). + :rtype: list(tuple) + """ + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + self.CorpusView(fileid, self._read_features, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def reviews(self, fileids=None): + """ + Return all the reviews as a list of Review objects. If `fileids` is + specified, return all the reviews from each of the specified files. + + :param fileids: a list or regexp specifying the ids of the files whose + reviews have to be returned. + :return: the given file(s) as a list of reviews. + """ + if fileids is None: + fileids = self._fileids + return concat( + [ + self.CorpusView(fileid, self._read_review_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + """ + Return all sentences in the corpus or in the specified files. + + :param fileids: a list or regexp specifying the ids of the files whose + sentences have to be returned. + :return: the given file(s) as a list of sentences, each encoded as a + list of word strings. + :rtype: list(list(str)) + """ + return concat( + [ + self.CorpusView(path, self._read_sent_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def words(self, fileids=None): + """ + Return all words and punctuation symbols in the corpus or in the specified + files. + + :param fileids: a list or regexp specifying the ids of the files whose + words have to be returned. + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + self.CorpusView(path, self._read_word_block, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def _read_features(self, stream): + features = [] + for i in range(20): + line = stream.readline() + if not line: + return features + features.extend(re.findall(FEATURES, line)) + return features + + def _read_review_block(self, stream): + while True: + line = stream.readline() + if not line: + return [] # end of file. + title_match = re.match(TITLE, line) + if title_match: + review = Review( + title=title_match.group(1).strip() + ) # We create a new review + break + + # Scan until we find another line matching the regexp, or EOF. + while True: + oldpos = stream.tell() + line = stream.readline() + # End of file: + if not line: + return [review] + # Start of a new review: backup to just before it starts, and + # return the review we've already collected. + if re.match(TITLE, line): + stream.seek(oldpos) + return [review] + # Anything else is part of the review line. + feats = re.findall(FEATURES, line) + notes = re.findall(NOTES, line) + sent = re.findall(SENT, line) + if sent: + sent = self._word_tokenizer.tokenize(sent[0]) + review_line = ReviewLine(sent=sent, features=feats, notes=notes) + review.add_line(review_line) + + def _read_sent_block(self, stream): + sents = [] + for review in self._read_review_block(stream): + sents.extend([sent for sent in review.sents()]) + return sents + + def _read_word_block(self, stream): + words = [] + for i in range(20): # Read 20 lines at a time. + line = stream.readline() + sent = re.findall(SENT, line) + if sent: + words.extend(self._word_tokenizer.tokenize(sent[0])) + return words diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/rte.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/rte.py new file mode 100644 index 0000000..2edd391 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/rte.py @@ -0,0 +1,146 @@ +# Natural Language Toolkit: RTE Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for the Recognizing Textual Entailment (RTE) Challenge Corpora. + +The files were taken from the RTE1, RTE2 and RTE3 datasets and the files +were regularized. + +Filenames are of the form rte*_dev.xml and rte*_test.xml. The latter are the +gold standard annotated files. + +Each entailment corpus is a list of 'text'/'hypothesis' pairs. The following +example is taken from RTE3:: + + + + The sale was made to pay Yukos' US$ 27.5 billion tax bill, + Yuganskneftegaz was originally sold for US$ 9.4 billion to a little known + company Baikalfinansgroup which was later bought by the Russian + state-owned oil company Rosneft . + + Baikalfinansgroup was sold to Rosneft. + + +In order to provide globally unique IDs for each pair, a new attribute +``challenge`` has been added to the root element ``entailment-corpus`` of each +file, taking values 1, 2 or 3. The GID is formatted 'm-n', where 'm' is the +challenge number and 'n' is the pair ID. +""" +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.corpus.reader.xmldocs import * + + +def norm(value_string): + """ + Normalize the string value in an RTE pair's ``value`` or ``entailment`` + attribute as an integer (1, 0). + + :param value_string: the label used to classify a text/hypothesis pair + :type value_string: str + :rtype: int + """ + + valdict = {"TRUE": 1, "FALSE": 0, "YES": 1, "NO": 0} + return valdict[value_string.upper()] + + +class RTEPair: + """ + Container for RTE text-hypothesis pairs. + + The entailment relation is signalled by the ``value`` attribute in RTE1, and by + ``entailment`` in RTE2 and RTE3. These both get mapped on to the ``entailment`` + attribute of this class. + """ + + def __init__( + self, + pair, + challenge=None, + id=None, + text=None, + hyp=None, + value=None, + task=None, + length=None, + ): + """ + :param challenge: version of the RTE challenge (i.e., RTE1, RTE2 or RTE3) + :param id: identifier for the pair + :param text: the text component of the pair + :param hyp: the hypothesis component of the pair + :param value: classification label for the pair + :param task: attribute for the particular NLP task that the data was drawn from + :param length: attribute for the length of the text of the pair + """ + self.challenge = challenge + self.id = pair.attrib["id"] + self.gid = f"{self.challenge}-{self.id}" + self.text = pair[0].text + self.hyp = pair[1].text + + if "value" in pair.attrib: + self.value = norm(pair.attrib["value"]) + elif "entailment" in pair.attrib: + self.value = norm(pair.attrib["entailment"]) + else: + self.value = value + if "task" in pair.attrib: + self.task = pair.attrib["task"] + else: + self.task = task + if "length" in pair.attrib: + self.length = pair.attrib["length"] + else: + self.length = length + + def __repr__(self): + if self.challenge: + return f"" + else: + return "" % self.id + + +class RTECorpusReader(XMLCorpusReader): + """ + Corpus reader for corpora in RTE challenges. + + This is just a wrapper around the XMLCorpusReader. See module docstring above for the expected + structure of input documents. + """ + + def _read_etree(self, doc): + """ + Map the XML input into an RTEPair. + + This uses the ``getiterator()`` method from the ElementTree package to + find all the ```` elements. + + :param doc: a parsed XML document + :rtype: list(RTEPair) + """ + try: + challenge = doc.attrib["challenge"] + except KeyError: + challenge = None + pairiter = doc.iter("pair") + return [RTEPair(pair, challenge=challenge) for pair in pairiter] + + def pairs(self, fileids): + """ + Build a list of RTEPairs from a RTE corpus. + + :param fileids: a list of RTE corpus fileids + :type: list + :rtype: list(RTEPair) + """ + if isinstance(fileids, str): + fileids = [fileids] + return concat([self._read_etree(self.xml(fileid)) for fileid in fileids]) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/semcor.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/semcor.py new file mode 100644 index 0000000..3e9521a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/semcor.py @@ -0,0 +1,296 @@ +# Natural Language Toolkit: SemCor Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Nathan Schneider +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for the SemCor Corpus. +""" + +__docformat__ = "epytext en" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.xmldocs import XMLCorpusReader, XMLCorpusView +from nltk.tree import Tree + + +class SemcorCorpusReader(XMLCorpusReader): + """ + Corpus reader for the SemCor Corpus. + For access to the complete XML data structure, use the ``xml()`` + method. For access to simple word lists and tagged word lists, use + ``words()``, ``sents()``, ``tagged_words()``, and ``tagged_sents()``. + """ + + def __init__(self, root, fileids, wordnet, lazy=True): + XMLCorpusReader.__init__(self, root, fileids) + self._lazy = lazy + self._wordnet = wordnet + + def words(self, fileids=None): + """ + :return: the given file(s) as a list of words and punctuation symbols. + :rtype: list(str) + """ + return self._items(fileids, "word", False, False, False) + + def chunks(self, fileids=None): + """ + :return: the given file(s) as a list of chunks, + each of which is a list of words and punctuation symbols + that form a unit. + :rtype: list(list(str)) + """ + return self._items(fileids, "chunk", False, False, False) + + def tagged_chunks(self, fileids=None, tag=("pos" or "sem" or "both")): + """ + :return: the given file(s) as a list of tagged chunks, represented + in tree form. + :rtype: list(Tree) + + :param tag: `'pos'` (part of speech), `'sem'` (semantic), or `'both'` + to indicate the kind of tags to include. Semantic tags consist of + WordNet lemma IDs, plus an `'NE'` node if the chunk is a named entity + without a specific entry in WordNet. (Named entities of type 'other' + have no lemma. Other chunks not in WordNet have no semantic tag. + Punctuation tokens have `None` for their part of speech tag.) + """ + return self._items(fileids, "chunk", False, tag != "sem", tag != "pos") + + def sents(self, fileids=None): + """ + :return: the given file(s) as a list of sentences, each encoded + as a list of word strings. + :rtype: list(list(str)) + """ + return self._items(fileids, "word", True, False, False) + + def chunk_sents(self, fileids=None): + """ + :return: the given file(s) as a list of sentences, each encoded + as a list of chunks. + :rtype: list(list(list(str))) + """ + return self._items(fileids, "chunk", True, False, False) + + def tagged_sents(self, fileids=None, tag=("pos" or "sem" or "both")): + """ + :return: the given file(s) as a list of sentences. Each sentence + is represented as a list of tagged chunks (in tree form). + :rtype: list(list(Tree)) + + :param tag: `'pos'` (part of speech), `'sem'` (semantic), or `'both'` + to indicate the kind of tags to include. Semantic tags consist of + WordNet lemma IDs, plus an `'NE'` node if the chunk is a named entity + without a specific entry in WordNet. (Named entities of type 'other' + have no lemma. Other chunks not in WordNet have no semantic tag. + Punctuation tokens have `None` for their part of speech tag.) + """ + return self._items(fileids, "chunk", True, tag != "sem", tag != "pos") + + def _items(self, fileids, unit, bracket_sent, pos_tag, sem_tag): + if unit == "word" and not bracket_sent: + # the result of the SemcorWordView may be a multiword unit, so the + # LazyConcatenation will make sure the sentence is flattened + _ = lambda *args: LazyConcatenation( + (SemcorWordView if self._lazy else self._words)(*args) + ) + else: + _ = SemcorWordView if self._lazy else self._words + return concat( + [ + _(fileid, unit, bracket_sent, pos_tag, sem_tag, self._wordnet) + for fileid in self.abspaths(fileids) + ] + ) + + def _words(self, fileid, unit, bracket_sent, pos_tag, sem_tag): + """ + Helper used to implement the view methods -- returns a list of + tokens, (segmented) words, chunks, or sentences. The tokens + and chunks may optionally be tagged (with POS and sense + information). + + :param fileid: The name of the underlying file. + :param unit: One of `'token'`, `'word'`, or `'chunk'`. + :param bracket_sent: If true, include sentence bracketing. + :param pos_tag: Whether to include part-of-speech tags. + :param sem_tag: Whether to include semantic tags, namely WordNet lemma + and OOV named entity status. + """ + assert unit in ("token", "word", "chunk") + result = [] + + xmldoc = ElementTree.parse(fileid).getroot() + for xmlsent in xmldoc.findall(".//s"): + sent = [] + for xmlword in _all_xmlwords_in(xmlsent): + itm = SemcorCorpusReader._word( + xmlword, unit, pos_tag, sem_tag, self._wordnet + ) + if unit == "word": + sent.extend(itm) + else: + sent.append(itm) + + if bracket_sent: + result.append(SemcorSentence(xmlsent.attrib["snum"], sent)) + else: + result.extend(sent) + + assert None not in result + return result + + @staticmethod + def _word(xmlword, unit, pos_tag, sem_tag, wordnet): + tkn = xmlword.text + if not tkn: + tkn = "" # fixes issue 337? + + lemma = xmlword.get("lemma", tkn) # lemma or NE class + lexsn = xmlword.get("lexsn") # lex_sense (locator for the lemma's sense) + if lexsn is not None: + sense_key = lemma + "%" + lexsn + wnpos = ("n", "v", "a", "r", "s")[ + int(lexsn.split(":")[0]) - 1 + ] # see http://wordnet.princeton.edu/man/senseidx.5WN.html + else: + sense_key = wnpos = None + redef = xmlword.get( + "rdf", tkn + ) # redefinition--this indicates the lookup string + # does not exactly match the enclosed string, e.g. due to typographical adjustments + # or discontinuity of a multiword expression. If a redefinition has occurred, + # the "rdf" attribute holds its inflected form and "lemma" holds its lemma. + # For NEs, "rdf", "lemma", and "pn" all hold the same value (the NE class). + sensenum = xmlword.get("wnsn") # WordNet sense number + isOOVEntity = "pn" in xmlword.keys() # a "personal name" (NE) not in WordNet + pos = xmlword.get( + "pos" + ) # part of speech for the whole chunk (None for punctuation) + + if unit == "token": + if not pos_tag and not sem_tag: + itm = tkn + else: + itm = ( + (tkn,) + + ((pos,) if pos_tag else ()) + + ((lemma, wnpos, sensenum, isOOVEntity) if sem_tag else ()) + ) + return itm + else: + ww = tkn.split("_") # TODO: case where punctuation intervenes in MWE + if unit == "word": + return ww + else: + if sensenum is not None: + try: + sense = wordnet.lemma_from_key(sense_key) # Lemma object + except Exception: + # cannot retrieve the wordnet.Lemma object. possible reasons: + # (a) the wordnet corpus is not downloaded; + # (b) a nonexistent sense is annotated: e.g., such.s.00 triggers: + # nltk.corpus.reader.wordnet.WordNetError: No synset found for key u'such%5:00:01:specified:00' + # solution: just use the lemma name as a string + try: + sense = "%s.%s.%02d" % ( + lemma, + wnpos, + int(sensenum), + ) # e.g.: reach.v.02 + except ValueError: + sense = ( + lemma + "." + wnpos + "." + sensenum + ) # e.g. the sense number may be "2;1" + + bottom = [Tree(pos, ww)] if pos_tag else ww + + if sem_tag and isOOVEntity: + if sensenum is not None: + return Tree(sense, [Tree("NE", bottom)]) + else: # 'other' NE + return Tree("NE", bottom) + elif sem_tag and sensenum is not None: + return Tree(sense, bottom) + elif pos_tag: + return bottom[0] + else: + return bottom # chunk as a list + + +def _all_xmlwords_in(elt, result=None): + if result is None: + result = [] + for child in elt: + if child.tag in ("wf", "punc"): + result.append(child) + else: + _all_xmlwords_in(child, result) + return result + + +class SemcorSentence(list): + """ + A list of words, augmented by an attribute ``num`` used to record + the sentence identifier (the ``n`` attribute from the XML). + """ + + def __init__(self, num, items): + self.num = num + list.__init__(self, items) + + +class SemcorWordView(XMLCorpusView): + """ + A stream backed corpus view specialized for use with the BNC corpus. + """ + + def __init__(self, fileid, unit, bracket_sent, pos_tag, sem_tag, wordnet): + """ + :param fileid: The name of the underlying file. + :param unit: One of `'token'`, `'word'`, or `'chunk'`. + :param bracket_sent: If true, include sentence bracketing. + :param pos_tag: Whether to include part-of-speech tags. + :param sem_tag: Whether to include semantic tags, namely WordNet lemma + and OOV named entity status. + """ + if bracket_sent: + tagspec = ".*/s" + else: + tagspec = ".*/s/(punc|wf)" + + self._unit = unit + self._sent = bracket_sent + self._pos_tag = pos_tag + self._sem_tag = sem_tag + self._wordnet = wordnet + + XMLCorpusView.__init__(self, fileid, tagspec) + + def handle_elt(self, elt, context): + if self._sent: + return self.handle_sent(elt) + else: + return self.handle_word(elt) + + def handle_word(self, elt): + return SemcorCorpusReader._word( + elt, self._unit, self._pos_tag, self._sem_tag, self._wordnet + ) + + def handle_sent(self, elt): + sent = [] + for child in elt: + if child.tag in ("wf", "punc"): + itm = self.handle_word(child) + if self._unit == "word": + sent.extend(itm) + else: + sent.append(itm) + else: + raise ValueError("Unexpected element %s" % child.tag) + return SemcorSentence(elt.attrib["snum"], sent) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/senseval.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/senseval.py new file mode 100644 index 0000000..cecbf31 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/senseval.py @@ -0,0 +1,196 @@ +# Natural Language Toolkit: Senseval 2 Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Steven Bird (modifications) +# URL: +# For license information, see LICENSE.TXT + +""" +Read from the Senseval 2 Corpus. + +SENSEVAL [http://www.senseval.org/] +Evaluation exercises for Word Sense Disambiguation. +Organized by ACL-SIGLEX [https://www.siglex.org/] + +Prepared by Ted Pedersen , University of Minnesota, +https://www.d.umn.edu/~tpederse/data.html +Distributed with permission. + +The NLTK version of the Senseval 2 files uses well-formed XML. +Each instance of the ambiguous words "hard", "interest", "line", and "serve" +is tagged with a sense identifier, and supplied with context. +""" + +import re +from xml.etree import ElementTree + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tokenize import * + + +class SensevalInstance: + def __init__(self, word, position, context, senses): + self.word = word + self.senses = tuple(senses) + self.position = position + self.context = context + + def __repr__(self): + return "SensevalInstance(word=%r, position=%r, " "context=%r, senses=%r)" % ( + self.word, + self.position, + self.context, + self.senses, + ) + + +class SensevalCorpusReader(CorpusReader): + def instances(self, fileids=None): + return concat( + [ + SensevalCorpusView(fileid, enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def _entry(self, tree): + elts = [] + for lexelt in tree.findall("lexelt"): + for inst in lexelt.findall("instance"): + sense = inst[0].attrib["senseid"] + context = [(w.text, w.attrib["pos"]) for w in inst[1]] + elts.append((sense, context)) + return elts + + +class SensevalCorpusView(StreamBackedCorpusView): + def __init__(self, fileid, encoding): + StreamBackedCorpusView.__init__(self, fileid, encoding=encoding) + + self._word_tokenizer = WhitespaceTokenizer() + self._lexelt_starts = [0] # list of streampos + self._lexelts = [None] # list of lexelt names + + def read_block(self, stream): + # Decide which lexical element we're in. + lexelt_num = bisect.bisect_right(self._lexelt_starts, stream.tell()) - 1 + lexelt = self._lexelts[lexelt_num] + + instance_lines = [] + in_instance = False + while True: + line = stream.readline() + if line == "": + assert instance_lines == [] + return [] + + # Start of a lexical element? + if line.lstrip().startswith(" has no 'item=...' + lexelt = m.group(1)[1:-1] + if lexelt_num < len(self._lexelts): + assert lexelt == self._lexelts[lexelt_num] + else: + self._lexelts.append(lexelt) + self._lexelt_starts.append(stream.tell()) + + # Start of an instance? + if line.lstrip().startswith("" + elif cword.tag == "wf": + context.append((cword.text, cword.attrib["pos"])) + elif cword.tag == "s": + pass # Sentence boundary marker. + + else: + print("ACK", cword.tag) + assert False, "expected CDATA or or " + if cword.tail: + context += self._word_tokenizer.tokenize(cword.tail) + else: + assert False, "unexpected tag %s" % child.tag + return SensevalInstance(lexelt, position, context, senses) + + +def _fixXML(text): + """ + Fix the various issues with Senseval pseudo-XML. + """ + # <~> or <^> => ~ or ^ + text = re.sub(r"<([~\^])>", r"\1", text) + # fix lone & + text = re.sub(r"(\s+)\&(\s+)", r"\1&\2", text) + # fix """ + text = re.sub(r'"""', "'\"'", text) + # fix => + text = re.sub(r'(<[^<]*snum=)([^">]+)>', r'\1"\2"/>', text) + # fix foreign word tag + text = re.sub(r"<\&frasl>\s*]*>", "FRASL", text) + # remove <&I .> + text = re.sub(r"<\&I[^>]*>", "", text) + # fix <{word}> + text = re.sub(r"<{([^}]+)}>", r"\1", text) + # remove <@>,

    ,

    + text = re.sub(r"<(@|/?p)>", r"", text) + # remove <&M .> and <&T .> and <&Ms .> + text = re.sub(r"<&\w+ \.>", r"", text) + # remove lines + text = re.sub(r"]*>", r"", text) + # remove <[hi]> and <[/p]> etc + text = re.sub(r"<\[\/?[^>]+\]*>", r"", text) + # take the thing out of the brackets: <…> + text = re.sub(r"<(\&\w+;)>", r"\1", text) + # and remove the & for those patterns that aren't regular XML + text = re.sub(r"&(?!amp|gt|lt|apos|quot)", r"", text) + # fix 'abc ' style tags - now abc + text = re.sub( + r'[ \t]*([^<>\s]+?)[ \t]*', r' \1', text + ) + text = re.sub(r'\s*"\s*', " \"", text) + return text diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sentiwordnet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sentiwordnet.py new file mode 100644 index 0000000..e61844b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sentiwordnet.py @@ -0,0 +1,136 @@ +# Natural Language Toolkit: SentiWordNet +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Christopher Potts +# URL: +# For license information, see LICENSE.TXT + +""" +An NLTK interface for SentiWordNet + +SentiWordNet is a lexical resource for opinion mining. +SentiWordNet assigns to each synset of WordNet three +sentiment scores: positivity, negativity, and objectivity. + +For details about SentiWordNet see: +http://sentiwordnet.isti.cnr.it/ + + >>> from nltk.corpus import sentiwordnet as swn + >>> print(swn.senti_synset('breakdown.n.03')) + + >>> list(swn.senti_synsets('slow')) + [SentiSynset('decelerate.v.01'), SentiSynset('slow.v.02'),\ + SentiSynset('slow.v.03'), SentiSynset('slow.a.01'),\ + SentiSynset('slow.a.02'), SentiSynset('dense.s.04'),\ + SentiSynset('slow.a.04'), SentiSynset('boring.s.01'),\ + SentiSynset('dull.s.08'), SentiSynset('slowly.r.01'),\ + SentiSynset('behind.r.03')] + >>> happy = swn.senti_synsets('happy', 'a') + >>> happy0 = list(happy)[0] + >>> happy0.pos_score() + 0.875 + >>> happy0.neg_score() + 0.0 + >>> happy0.obj_score() + 0.125 +""" + +import re + +from nltk.corpus.reader import CorpusReader + + +class SentiWordNetCorpusReader(CorpusReader): + def __init__(self, root, fileids, encoding="utf-8"): + """ + Construct a new SentiWordNet Corpus Reader, using data from + the specified file. + """ + super().__init__(root, fileids, encoding=encoding) + if len(self._fileids) != 1: + raise ValueError("Exactly one file must be specified") + self._db = {} + self._parse_src_file() + + def _parse_src_file(self): + lines = self.open(self._fileids[0]).read().splitlines() + lines = filter((lambda x: not re.search(r"^\s*#", x)), lines) + for i, line in enumerate(lines): + fields = [field.strip() for field in re.split(r"\t+", line)] + try: + pos, offset, pos_score, neg_score, synset_terms, gloss = fields + except BaseException as e: + raise ValueError(f"Line {i} formatted incorrectly: {line}\n") from e + if pos and offset: + offset = int(offset) + self._db[(pos, offset)] = (float(pos_score), float(neg_score)) + + def senti_synset(self, *vals): + from nltk.corpus import wordnet as wn + + if tuple(vals) in self._db: + pos_score, neg_score = self._db[tuple(vals)] + pos, offset = vals + if pos == "s": + pos = "a" + synset = wn.synset_from_pos_and_offset(pos, offset) + return SentiSynset(pos_score, neg_score, synset) + else: + synset = wn.synset(vals[0]) + pos = synset.pos() + if pos == "s": + pos = "a" + offset = synset.offset() + if (pos, offset) in self._db: + pos_score, neg_score = self._db[(pos, offset)] + return SentiSynset(pos_score, neg_score, synset) + else: + return None + + def senti_synsets(self, string, pos=None): + from nltk.corpus import wordnet as wn + + sentis = [] + synset_list = wn.synsets(string, pos) + for synset in synset_list: + sentis.append(self.senti_synset(synset.name())) + sentis = filter(lambda x: x, sentis) + return sentis + + def all_senti_synsets(self): + from nltk.corpus import wordnet as wn + + for key, fields in self._db.items(): + pos, offset = key + pos_score, neg_score = fields + synset = wn.synset_from_pos_and_offset(pos, offset) + yield SentiSynset(pos_score, neg_score, synset) + + +class SentiSynset: + def __init__(self, pos_score, neg_score, synset): + self._pos_score = pos_score + self._neg_score = neg_score + self._obj_score = 1.0 - (self._pos_score + self._neg_score) + self.synset = synset + + def pos_score(self): + return self._pos_score + + def neg_score(self): + return self._neg_score + + def obj_score(self): + return self._obj_score + + def __str__(self): + """Prints just the Pos/Neg scores for now.""" + s = "<" + s += self.synset.name() + ": " + s += "PosScore=%s " % self._pos_score + s += "NegScore=%s" % self._neg_score + s += ">" + return s + + def __repr__(self): + return "Senti" + repr(self.synset) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sinica_treebank.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sinica_treebank.py new file mode 100644 index 0000000..926e1ab --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/sinica_treebank.py @@ -0,0 +1,75 @@ +# Natural Language Toolkit: Sinica Treebank Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Sinica Treebank Corpus Sample + +http://rocling.iis.sinica.edu.tw/CKIP/engversion/treebank.htm + +10,000 parsed sentences, drawn from the Academia Sinica Balanced +Corpus of Modern Chinese. Parse tree notation is based on +Information-based Case Grammar. Tagset documentation is available +at https://www.sinica.edu.tw/SinicaCorpus/modern_e_wordtype.html + +Language and Knowledge Processing Group, Institute of Information +Science, Academia Sinica + +The data is distributed with the Natural Language Toolkit under the terms of +the Creative Commons Attribution-NonCommercial-ShareAlike License +[https://creativecommons.org/licenses/by-nc-sa/2.5/]. + +References: + +Feng-Yi Chen, Pi-Fang Tsai, Keh-Jiann Chen, and Chu-Ren Huang (1999) +The Construction of Sinica Treebank. Computational Linguistics and +Chinese Language Processing, 4, pp 87-104. + +Huang Chu-Ren, Keh-Jiann Chen, Feng-Yi Chen, Keh-Jiann Chen, Zhao-Ming +Gao, and Kuang-Yu Chen. 2000. Sinica Treebank: Design Criteria, +Annotation Guidelines, and On-line Interface. Proceedings of 2nd +Chinese Language Processing Workshop, Association for Computational +Linguistics. + +Chen Keh-Jiann and Yu-Ming Hsieh (2004) Chinese Treebanks and Grammar +Extraction, Proceedings of IJCNLP-04, pp560-565. +""" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tag import map_tag +from nltk.tree import sinica_parse + +IDENTIFIER = re.compile(r"^#\S+\s") +APPENDIX = re.compile(r"(?<=\))#.*$") +TAGWORD = re.compile(r":([^:()|]+):([^:()|]+)") +WORD = re.compile(r":[^:()|]+:([^:()|]+)") + + +class SinicaTreebankCorpusReader(SyntaxCorpusReader): + """ + Reader for the sinica treebank. + """ + + def _read_block(self, stream): + sent = stream.readline() + sent = IDENTIFIER.sub("", sent) + sent = APPENDIX.sub("", sent) + return [sent] + + def _parse(self, sent): + return sinica_parse(sent) + + def _tag(self, sent, tagset=None): + tagged_sent = [(w, t) for (t, w) in TAGWORD.findall(sent)] + if tagset and tagset != self._tagset: + tagged_sent = [ + (w, map_tag(self._tagset, tagset, t)) for (w, t) in tagged_sent + ] + return tagged_sent + + def _word(self, sent): + return WORD.findall(sent) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/string_category.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/string_category.py new file mode 100644 index 0000000..35cbc0c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/string_category.py @@ -0,0 +1,56 @@ +# Natural Language Toolkit: String Category Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Read tuples from a corpus consisting of categorized strings. +For example, from the question classification corpus: + +NUM:dist How far is it from Denver to Aspen ? +LOC:city What county is Modesto , California in ? +HUM:desc Who was Galileo ? +DESC:def What is an atom ? +NUM:date When did Hawaii become a state ? +""" + +from nltk.corpus.reader.api import * + +# based on PPAttachmentCorpusReader +from nltk.corpus.reader.util import * + + +# [xx] Should the order of the tuple be reversed -- in most other places +# in nltk, we use the form (data, tag) -- e.g., tagged words and +# labeled texts for classifiers. +class StringCategoryCorpusReader(CorpusReader): + def __init__(self, root, fileids, delimiter=" ", encoding="utf8"): + """ + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + :param delimiter: Field delimiter + """ + CorpusReader.__init__(self, root, fileids, encoding) + self._delimiter = delimiter + + def tuples(self, fileids=None): + if fileids is None: + fileids = self._fileids + elif isinstance(fileids, str): + fileids = [fileids] + return concat( + [ + StreamBackedCorpusView(fileid, self._read_tuple_block, encoding=enc) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def _read_tuple_block(self, stream): + line = stream.readline().strip() + if line: + return [tuple(line.split(self._delimiter, 1))] + else: + return [] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/switchboard.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/switchboard.py new file mode 100644 index 0000000..23d6dcf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/switchboard.py @@ -0,0 +1,125 @@ +# Natural Language Toolkit: Switchboard Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT +import re + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tag import map_tag, str2tuple + + +class SwitchboardTurn(list): + """ + A specialized list object used to encode switchboard utterances. + The elements of the list are the words in the utterance; and two + attributes, ``speaker`` and ``id``, are provided to retrieve the + spearker identifier and utterance id. Note that utterance ids + are only unique within a given discourse. + """ + + def __init__(self, words, speaker, id): + list.__init__(self, words) + self.speaker = speaker + self.id = int(id) + + def __repr__(self): + if len(self) == 0: + text = "" + elif isinstance(self[0], tuple): + text = " ".join("%s/%s" % w for w in self) + else: + text = " ".join(self) + return f"<{self.speaker}.{self.id}: {text!r}>" + + +class SwitchboardCorpusReader(CorpusReader): + _FILES = ["tagged"] + # Use the "tagged" file even for non-tagged data methods, since + # it's tokenized. + + def __init__(self, root, tagset=None): + CorpusReader.__init__(self, root, self._FILES) + self._tagset = tagset + + def words(self): + return StreamBackedCorpusView(self.abspath("tagged"), self._words_block_reader) + + def tagged_words(self, tagset=None): + def tagged_words_block_reader(stream): + return self._tagged_words_block_reader(stream, tagset) + + return StreamBackedCorpusView(self.abspath("tagged"), tagged_words_block_reader) + + def turns(self): + return StreamBackedCorpusView(self.abspath("tagged"), self._turns_block_reader) + + def tagged_turns(self, tagset=None): + def tagged_turns_block_reader(stream): + return self._tagged_turns_block_reader(stream, tagset) + + return StreamBackedCorpusView(self.abspath("tagged"), tagged_turns_block_reader) + + def discourses(self): + return StreamBackedCorpusView( + self.abspath("tagged"), self._discourses_block_reader + ) + + def tagged_discourses(self, tagset=False): + def tagged_discourses_block_reader(stream): + return self._tagged_discourses_block_reader(stream, tagset) + + return StreamBackedCorpusView( + self.abspath("tagged"), tagged_discourses_block_reader + ) + + def _discourses_block_reader(self, stream): + # returns at most 1 discourse. (The other methods depend on this.) + return [ + [ + self._parse_utterance(u, include_tag=False) + for b in read_blankline_block(stream) + for u in b.split("\n") + if u.strip() + ] + ] + + def _tagged_discourses_block_reader(self, stream, tagset=None): + # returns at most 1 discourse. (The other methods depend on this.) + return [ + [ + self._parse_utterance(u, include_tag=True, tagset=tagset) + for b in read_blankline_block(stream) + for u in b.split("\n") + if u.strip() + ] + ] + + def _turns_block_reader(self, stream): + return self._discourses_block_reader(stream)[0] + + def _tagged_turns_block_reader(self, stream, tagset=None): + return self._tagged_discourses_block_reader(stream, tagset)[0] + + def _words_block_reader(self, stream): + return sum(self._discourses_block_reader(stream)[0], []) + + def _tagged_words_block_reader(self, stream, tagset=None): + return sum(self._tagged_discourses_block_reader(stream, tagset)[0], []) + + _UTTERANCE_RE = re.compile(r"(\w+)\.(\d+)\:\s*(.*)") + _SEP = "/" + + def _parse_utterance(self, utterance, include_tag, tagset=None): + m = self._UTTERANCE_RE.match(utterance) + if m is None: + raise ValueError("Bad utterance %r" % utterance) + speaker, id, text = m.groups() + words = [str2tuple(s, self._SEP) for s in text.split()] + if not include_tag: + words = [w for (w, t) in words] + elif tagset and tagset != self._tagset: + words = [(w, map_tag(self._tagset, tagset, t)) for (w, t) in words] + return SwitchboardTurn(words, speaker, id) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/tagged.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/tagged.py new file mode 100644 index 0000000..d03851a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/tagged.py @@ -0,0 +1,354 @@ +# Natural Language Toolkit: Tagged Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Jacob Perkins +# URL: +# For license information, see LICENSE.TXT + +""" +A reader for corpora whose documents contain part-of-speech-tagged words. +""" + +import os + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.timit import read_timit_block +from nltk.corpus.reader.util import * +from nltk.tag import map_tag, str2tuple +from nltk.tokenize import * + + +class TaggedCorpusReader(CorpusReader): + """ + Reader for simple part-of-speech tagged corpora. Paragraphs are + assumed to be split using blank lines. Sentences and words can be + tokenized using the default tokenizers, or by custom tokenizers + specified as parameters to the constructor. Words are parsed + using ``nltk.tag.str2tuple``. By default, ``'/'`` is used as the + separator. I.e., words should have the form:: + + word1/tag1 word2/tag2 word3/tag3 ... + + But custom separators may be specified as parameters to the + constructor. Part of speech tags are case-normalized to upper + case. + """ + + def __init__( + self, + root, + fileids, + sep="/", + word_tokenizer=WhitespaceTokenizer(), + sent_tokenizer=RegexpTokenizer("\n", gaps=True), + para_block_reader=read_blankline_block, + encoding="utf8", + tagset=None, + ): + """ + Construct a new Tagged Corpus reader for a set of documents + located at the given root directory. Example usage: + + >>> root = '/...path to corpus.../' + >>> reader = TaggedCorpusReader(root, '.*', '.txt') # doctest: +SKIP + + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + """ + CorpusReader.__init__(self, root, fileids, encoding) + self._sep = sep + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._para_block_reader = para_block_reader + self._tagset = tagset + + def words(self, fileids=None): + """ + :return: the given file(s) as a list of words + and punctuation symbols. + :rtype: list(str) + """ + return concat( + [ + TaggedCorpusView( + fileid, + enc, + False, + False, + False, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + None, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def sents(self, fileids=None): + """ + :return: the given file(s) as a list of + sentences or utterances, each encoded as a list of word + strings. + :rtype: list(list(str)) + """ + return concat( + [ + TaggedCorpusView( + fileid, + enc, + False, + True, + False, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + None, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def paras(self, fileids=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as lists of word strings. + :rtype: list(list(list(str))) + """ + return concat( + [ + TaggedCorpusView( + fileid, + enc, + False, + True, + True, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + None, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_words(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of tagged + words and punctuation symbols, encoded as tuples + ``(word,tag)``. + :rtype: list(tuple(str,str)) + """ + if tagset and tagset != self._tagset: + tag_mapping_function = lambda t: map_tag(self._tagset, tagset, t) + else: + tag_mapping_function = None + return concat( + [ + TaggedCorpusView( + fileid, + enc, + True, + False, + False, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + tag_mapping_function, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_sents(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + sentences, each encoded as a list of ``(word,tag)`` tuples. + + :rtype: list(list(tuple(str,str))) + """ + if tagset and tagset != self._tagset: + tag_mapping_function = lambda t: map_tag(self._tagset, tagset, t) + else: + tag_mapping_function = None + return concat( + [ + TaggedCorpusView( + fileid, + enc, + True, + True, + False, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + tag_mapping_function, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + def tagged_paras(self, fileids=None, tagset=None): + """ + :return: the given file(s) as a list of + paragraphs, each encoded as a list of sentences, which are + in turn encoded as lists of ``(word,tag)`` tuples. + :rtype: list(list(list(tuple(str,str)))) + """ + if tagset and tagset != self._tagset: + tag_mapping_function = lambda t: map_tag(self._tagset, tagset, t) + else: + tag_mapping_function = None + return concat( + [ + TaggedCorpusView( + fileid, + enc, + True, + True, + True, + self._sep, + self._word_tokenizer, + self._sent_tokenizer, + self._para_block_reader, + tag_mapping_function, + ) + for (fileid, enc) in self.abspaths(fileids, True) + ] + ) + + +class CategorizedTaggedCorpusReader(CategorizedCorpusReader, TaggedCorpusReader): + """ + A reader for part-of-speech tagged corpora whose documents are + divided into categories based on their file identifiers. + """ + + def __init__(self, *args, **kwargs): + """ + Initialize the corpus reader. Categorization arguments + (``cat_pattern``, ``cat_map``, and ``cat_file``) are passed to + the ``CategorizedCorpusReader`` constructor. The remaining arguments + are passed to the ``TaggedCorpusReader``. + """ + CategorizedCorpusReader.__init__(self, kwargs) + TaggedCorpusReader.__init__(self, *args, **kwargs) + + def tagged_words(self, fileids=None, categories=None, tagset=None): + return super().tagged_words(self._resolve(fileids, categories), tagset) + + def tagged_sents(self, fileids=None, categories=None, tagset=None): + return super().tagged_sents(self._resolve(fileids, categories), tagset) + + def tagged_paras(self, fileids=None, categories=None, tagset=None): + return super().tagged_paras(self._resolve(fileids, categories), tagset) + + +class TaggedCorpusView(StreamBackedCorpusView): + """ + A specialized corpus view for tagged documents. It can be + customized via flags to divide the tagged corpus documents up by + sentence or paragraph, and to include or omit part of speech tags. + ``TaggedCorpusView`` objects are typically created by + ``TaggedCorpusReader`` (not directly by nltk users). + """ + + def __init__( + self, + corpus_file, + encoding, + tagged, + group_by_sent, + group_by_para, + sep, + word_tokenizer, + sent_tokenizer, + para_block_reader, + tag_mapping_function=None, + ): + self._tagged = tagged + self._group_by_sent = group_by_sent + self._group_by_para = group_by_para + self._sep = sep + self._word_tokenizer = word_tokenizer + self._sent_tokenizer = sent_tokenizer + self._para_block_reader = para_block_reader + self._tag_mapping_function = tag_mapping_function + StreamBackedCorpusView.__init__(self, corpus_file, encoding=encoding) + + def read_block(self, stream): + """Reads one paragraph at a time.""" + block = [] + for para_str in self._para_block_reader(stream): + para = [] + for sent_str in self._sent_tokenizer.tokenize(para_str): + sent = [ + str2tuple(s, self._sep) + for s in self._word_tokenizer.tokenize(sent_str) + ] + if self._tag_mapping_function: + sent = [(w, self._tag_mapping_function(t)) for (w, t) in sent] + if not self._tagged: + sent = [w for (w, t) in sent] + if self._group_by_sent: + para.append(sent) + else: + para.extend(sent) + if self._group_by_para: + block.append(para) + else: + block.extend(para) + return block + + +# needs to implement simplified tags +class MacMorphoCorpusReader(TaggedCorpusReader): + """ + A corpus reader for the MAC_MORPHO corpus. Each line contains a + single tagged word, using '_' as a separator. Sentence boundaries + are based on the end-sentence tag ('_.'). Paragraph information + is not included in the corpus, so each paragraph returned by + ``self.paras()`` and ``self.tagged_paras()`` contains a single + sentence. + """ + + def __init__(self, root, fileids, encoding="utf8", tagset=None): + TaggedCorpusReader.__init__( + self, + root, + fileids, + sep="_", + word_tokenizer=LineTokenizer(), + sent_tokenizer=RegexpTokenizer(".*\n"), + para_block_reader=self._read_block, + encoding=encoding, + tagset=tagset, + ) + + def _read_block(self, stream): + return read_regexp_block(stream, r".*", r".*_\.") + + +class TimitTaggedCorpusReader(TaggedCorpusReader): + """ + A corpus reader for tagged sentences that are included in the TIMIT corpus. + """ + + def __init__(self, *args, **kwargs): + TaggedCorpusReader.__init__( + self, para_block_reader=read_timit_block, *args, **kwargs + ) + + def paras(self): + raise NotImplementedError("use sents() instead") + + def tagged_paras(self): + raise NotImplementedError("use tagged_sents() instead") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/timit.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/timit.py new file mode 100644 index 0000000..cf258ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/timit.py @@ -0,0 +1,510 @@ +# Natural Language Toolkit: TIMIT Corpus Reader +# +# Copyright (C) 2001-2007 NLTK Project +# Author: Haejoong Lee +# Steven Bird +# Jacob Perkins +# URL: +# For license information, see LICENSE.TXT + +# [xx] this docstring is out-of-date: +""" +Read tokens, phonemes and audio data from the NLTK TIMIT Corpus. + +This corpus contains selected portion of the TIMIT corpus. + + - 16 speakers from 8 dialect regions + - 1 male and 1 female from each dialect region + - total 130 sentences (10 sentences per speaker. Note that some + sentences are shared among other speakers, especially sa1 and sa2 + are spoken by all speakers.) + - total 160 recording of sentences (10 recordings per speaker) + - audio format: NIST Sphere, single channel, 16kHz sampling, + 16 bit sample, PCM encoding + + +Module contents +=============== + +The timit corpus reader provides 4 functions and 4 data items. + + - utterances + + List of utterances in the corpus. There are total 160 utterances, + each of which corresponds to a unique utterance of a speaker. + Here's an example of an utterance identifier in the list:: + + dr1-fvmh0/sx206 + - _---- _--- + | | | | | + | | | | | + | | | | `--- sentence number + | | | `----- sentence type (a:all, i:shared, x:exclusive) + | | `--------- speaker ID + | `------------ sex (m:male, f:female) + `-------------- dialect region (1..8) + + - speakers + + List of speaker IDs. An example of speaker ID:: + + dr1-fvmh0 + + Note that if you split an item ID with colon and take the first element of + the result, you will get a speaker ID. + + >>> itemid = 'dr1-fvmh0/sx206' + >>> spkrid , sentid = itemid.split('/') + >>> spkrid + 'dr1-fvmh0' + + The second element of the result is a sentence ID. + + - dictionary() + + Phonetic dictionary of words contained in this corpus. This is a Python + dictionary from words to phoneme lists. + + - spkrinfo() + + Speaker information table. It's a Python dictionary from speaker IDs to + records of 10 fields. Speaker IDs the same as the ones in timie.speakers. + Each record is a dictionary from field names to values, and the fields are + as follows:: + + id speaker ID as defined in the original TIMIT speaker info table + sex speaker gender (M:male, F:female) + dr speaker dialect region (1:new england, 2:northern, + 3:north midland, 4:south midland, 5:southern, 6:new york city, + 7:western, 8:army brat (moved around)) + use corpus type (TRN:training, TST:test) + in this sample corpus only TRN is available + recdate recording date + birthdate speaker birth date + ht speaker height + race speaker race (WHT:white, BLK:black, AMR:american indian, + SPN:spanish-american, ORN:oriental,???:unknown) + edu speaker education level (HS:high school, AS:associate degree, + BS:bachelor's degree (BS or BA), MS:master's degree (MS or MA), + PHD:doctorate degree (PhD,JD,MD), ??:unknown) + comments comments by the recorder + +The 4 functions are as follows. + + - tokenized(sentences=items, offset=False) + + Given a list of items, returns an iterator of a list of word lists, + each of which corresponds to an item (sentence). If offset is set to True, + each element of the word list is a tuple of word(string), start offset and + end offset, where offset is represented as a number of 16kHz samples. + + - phonetic(sentences=items, offset=False) + + Given a list of items, returns an iterator of a list of phoneme lists, + each of which corresponds to an item (sentence). If offset is set to True, + each element of the phoneme list is a tuple of word(string), start offset + and end offset, where offset is represented as a number of 16kHz samples. + + - audiodata(item, start=0, end=None) + + Given an item, returns a chunk of audio samples formatted into a string. + When the function is called, if start and end are omitted, the entire + samples of the recording will be returned. If only end is omitted, + samples from the start offset to the end of the recording will be returned. + + - play(data) + + Play the given audio samples. The audio samples can be obtained from the + timit.audiodata function. + +""" +import sys +import time + +from nltk.corpus.reader.api import * +from nltk.internals import import_from_stdlib +from nltk.tree import Tree + + +class TimitCorpusReader(CorpusReader): + """ + Reader for the TIMIT corpus (or any other corpus with the same + file layout and use of file formats). The corpus root directory + should contain the following files: + + - timitdic.txt: dictionary of standard transcriptions + - spkrinfo.txt: table of speaker information + + In addition, the root directory should contain one subdirectory + for each speaker, containing three files for each utterance: + + - .txt: text content of utterances + - .wrd: tokenized text content of utterances + - .phn: phonetic transcription of utterances + - .wav: utterance sound file + """ + + _FILE_RE = r"(\w+-\w+/\w+\.(phn|txt|wav|wrd))|" + r"timitdic\.txt|spkrinfo\.txt" + """A regexp matching fileids that are used by this corpus reader.""" + _UTTERANCE_RE = r"\w+-\w+/\w+\.txt" + + def __init__(self, root, encoding="utf8"): + """ + Construct a new TIMIT corpus reader in the given directory. + :param root: The root directory for this corpus. + """ + # Ensure that wave files don't get treated as unicode data: + if isinstance(encoding, str): + encoding = [(r".*\.wav", None), (".*", encoding)] + + CorpusReader.__init__( + self, root, find_corpus_fileids(root, self._FILE_RE), encoding=encoding + ) + + self._utterances = [ + name[:-4] for name in find_corpus_fileids(root, self._UTTERANCE_RE) + ] + """A list of the utterance identifiers for all utterances in + this corpus.""" + + self._speakerinfo = None + self._root = root + self.speakers = sorted({u.split("/")[0] for u in self._utterances}) + + def fileids(self, filetype=None): + """ + Return a list of file identifiers for the files that make up + this corpus. + + :param filetype: If specified, then ``filetype`` indicates that + only the files that have the given type should be + returned. Accepted values are: ``txt``, ``wrd``, ``phn``, + ``wav``, or ``metadata``, + """ + if filetype is None: + return CorpusReader.fileids(self) + elif filetype in ("txt", "wrd", "phn", "wav"): + return [f"{u}.{filetype}" for u in self._utterances] + elif filetype == "metadata": + return ["timitdic.txt", "spkrinfo.txt"] + else: + raise ValueError("Bad value for filetype: %r" % filetype) + + def utteranceids( + self, dialect=None, sex=None, spkrid=None, sent_type=None, sentid=None + ): + """ + :return: A list of the utterance identifiers for all + utterances in this corpus, or for the given speaker, dialect + region, gender, sentence type, or sentence number, if + specified. + """ + if isinstance(dialect, str): + dialect = [dialect] + if isinstance(sex, str): + sex = [sex] + if isinstance(spkrid, str): + spkrid = [spkrid] + if isinstance(sent_type, str): + sent_type = [sent_type] + if isinstance(sentid, str): + sentid = [sentid] + + utterances = self._utterances[:] + if dialect is not None: + utterances = [u for u in utterances if u[2] in dialect] + if sex is not None: + utterances = [u for u in utterances if u[4] in sex] + if spkrid is not None: + utterances = [u for u in utterances if u[:9] in spkrid] + if sent_type is not None: + utterances = [u for u in utterances if u[11] in sent_type] + if sentid is not None: + utterances = [u for u in utterances if u[10:] in spkrid] + return utterances + + def transcription_dict(self): + """ + :return: A dictionary giving the 'standard' transcription for + each word. + """ + _transcriptions = {} + with self.open("timitdic.txt") as fp: + for line in fp: + if not line.strip() or line[0] == ";": + continue + m = re.match(r"\s*(\S+)\s+/(.*)/\s*$", line) + if not m: + raise ValueError("Bad line: %r" % line) + _transcriptions[m.group(1)] = m.group(2).split() + return _transcriptions + + def spkrid(self, utterance): + return utterance.split("/")[0] + + def sentid(self, utterance): + return utterance.split("/")[1] + + def utterance(self, spkrid, sentid): + return f"{spkrid}/{sentid}" + + def spkrutteranceids(self, speaker): + """ + :return: A list of all utterances associated with a given + speaker. + """ + return [ + utterance + for utterance in self._utterances + if utterance.startswith(speaker + "/") + ] + + def spkrinfo(self, speaker): + """ + :return: A dictionary mapping .. something. + """ + if speaker in self._utterances: + speaker = self.spkrid(speaker) + + if self._speakerinfo is None: + self._speakerinfo = {} + with self.open("spkrinfo.txt") as fp: + for line in fp: + if not line.strip() or line[0] == ";": + continue + rec = line.strip().split(None, 9) + key = f"dr{rec[2]}-{rec[1].lower()}{rec[0].lower()}" + self._speakerinfo[key] = SpeakerInfo(*rec) + + return self._speakerinfo[speaker] + + def phones(self, utterances=None): + results = [] + for fileid in self._utterance_fileids(utterances, ".phn"): + with self.open(fileid) as fp: + for line in fp: + if line.strip(): + results.append(line.split()[-1]) + return results + + def phone_times(self, utterances=None): + """ + offset is represented as a number of 16kHz samples! + """ + results = [] + for fileid in self._utterance_fileids(utterances, ".phn"): + with self.open(fileid) as fp: + for line in fp: + if line.strip(): + results.append( + ( + line.split()[2], + int(line.split()[0]), + int(line.split()[1]), + ) + ) + return results + + def words(self, utterances=None): + results = [] + for fileid in self._utterance_fileids(utterances, ".wrd"): + with self.open(fileid) as fp: + for line in fp: + if line.strip(): + results.append(line.split()[-1]) + return results + + def word_times(self, utterances=None): + results = [] + for fileid in self._utterance_fileids(utterances, ".wrd"): + with self.open(fileid) as fp: + for line in fp: + if line.strip(): + results.append( + ( + line.split()[2], + int(line.split()[0]), + int(line.split()[1]), + ) + ) + return results + + def sents(self, utterances=None): + results = [] + for fileid in self._utterance_fileids(utterances, ".wrd"): + with self.open(fileid) as fp: + results.append([line.split()[-1] for line in fp if line.strip()]) + return results + + def sent_times(self, utterances=None): + # TODO: Check this + return [ + ( + line.split(None, 2)[-1].strip(), + int(line.split()[0]), + int(line.split()[1]), + ) + for fileid in self._utterance_fileids(utterances, ".txt") + for line in self.open(fileid) + if line.strip() + ] + + def phone_trees(self, utterances=None): + if utterances is None: + utterances = self._utterances + if isinstance(utterances, str): + utterances = [utterances] + + trees = [] + for utterance in utterances: + word_times = self.word_times(utterance) + phone_times = self.phone_times(utterance) + sent_times = self.sent_times(utterance) + + while sent_times: + (sent, sent_start, sent_end) = sent_times.pop(0) + trees.append(Tree("S", [])) + while ( + word_times and phone_times and phone_times[0][2] <= word_times[0][1] + ): + trees[-1].append(phone_times.pop(0)[0]) + while word_times and word_times[0][2] <= sent_end: + (word, word_start, word_end) = word_times.pop(0) + trees[-1].append(Tree(word, [])) + while phone_times and phone_times[0][2] <= word_end: + trees[-1][-1].append(phone_times.pop(0)[0]) + while phone_times and phone_times[0][2] <= sent_end: + trees[-1].append(phone_times.pop(0)[0]) + return trees + + # [xx] NOTE: This is currently broken -- we're assuming that the + # fileids are WAV fileids (aka RIFF), but they're actually NIST SPHERE + # fileids. + def wav(self, utterance, start=0, end=None): + # nltk.chunk conflicts with the stdlib module 'chunk' + wave = import_from_stdlib("wave") + + w = wave.open(self.open(utterance + ".wav"), "rb") + + if end is None: + end = w.getnframes() + + # Skip past frames before start, then read the frames we want + w.readframes(start) + frames = w.readframes(end - start) + + # Open a new temporary file -- the wave module requires + # an actual file, and won't work w/ stringio. :( + tf = tempfile.TemporaryFile() + out = wave.open(tf, "w") + + # Write the parameters & data to the new file. + out.setparams(w.getparams()) + out.writeframes(frames) + out.close() + + # Read the data back from the file, and return it. The + # file will automatically be deleted when we return. + tf.seek(0) + return tf.read() + + def audiodata(self, utterance, start=0, end=None): + assert end is None or end > start + headersize = 44 + with self.open(utterance + ".wav") as fp: + if end is None: + data = fp.read() + else: + data = fp.read(headersize + end * 2) + return data[headersize + start * 2 :] + + def _utterance_fileids(self, utterances, extension): + if utterances is None: + utterances = self._utterances + if isinstance(utterances, str): + utterances = [utterances] + return [f"{u}{extension}" for u in utterances] + + def play(self, utterance, start=0, end=None): + """ + Play the given audio sample. + + :param utterance: The utterance id of the sample to play + """ + # Method 1: os audio dev. + try: + import ossaudiodev + + try: + dsp = ossaudiodev.open("w") + dsp.setfmt(ossaudiodev.AFMT_S16_LE) + dsp.channels(1) + dsp.speed(16000) + dsp.write(self.audiodata(utterance, start, end)) + dsp.close() + except OSError as e: + print( + ( + "can't acquire the audio device; please " + "activate your audio device." + ), + file=sys.stderr, + ) + print("system error message:", str(e), file=sys.stderr) + return + except ImportError: + pass + + # Method 2: pygame + try: + # FIXME: this won't work under python 3 + import pygame.mixer + import StringIO + + pygame.mixer.init(16000) + f = StringIO.StringIO(self.wav(utterance, start, end)) + pygame.mixer.Sound(f).play() + while pygame.mixer.get_busy(): + time.sleep(0.01) + return + except ImportError: + pass + + # Method 3: complain. :) + print( + ("you must install pygame or ossaudiodev " "for audio playback."), + file=sys.stderr, + ) + + +class SpeakerInfo: + def __init__( + self, id, sex, dr, use, recdate, birthdate, ht, race, edu, comments=None + ): + self.id = id + self.sex = sex + self.dr = dr + self.use = use + self.recdate = recdate + self.birthdate = birthdate + self.ht = ht + self.race = race + self.edu = edu + self.comments = comments + + def __repr__(self): + attribs = "id sex dr use recdate birthdate ht race edu comments" + args = [f"{attr}={getattr(self, attr)!r}" for attr in attribs.split()] + return "SpeakerInfo(%s)" % (", ".join(args)) + + +def read_timit_block(stream): + """ + Block reader for timit tagged sentences, which are preceded by a sentence + number that will be ignored. + """ + line = stream.readline() + if not line: + return [] + n, sent = line.split(" ", 1) + return [sent] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/toolbox.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/toolbox.py new file mode 100644 index 0000000..4d07b45 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/toolbox.py @@ -0,0 +1,76 @@ +# Natural Language Toolkit: Toolbox Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Greg Aumann +# Stuart Robinson +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Module for reading, writing and manipulating +Toolbox databases and settings fileids. +""" + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.toolbox import ToolboxData + + +class ToolboxCorpusReader(CorpusReader): + def xml(self, fileids, key=None): + return concat( + [ + ToolboxData(path, enc).parse(key=key) + for (path, enc) in self.abspaths(fileids, True) + ] + ) + + def fields( + self, + fileids, + strip=True, + unwrap=True, + encoding="utf8", + errors="strict", + unicode_fields=None, + ): + return concat( + [ + list( + ToolboxData(fileid, enc).fields( + strip, unwrap, encoding, errors, unicode_fields + ) + ) + for (fileid, enc) in self.abspaths(fileids, include_encoding=True) + ] + ) + + # should probably be done lazily: + def entries(self, fileids, **kwargs): + if "key" in kwargs: + key = kwargs["key"] + del kwargs["key"] + else: + key = "lx" # the default key in MDF + entries = [] + for marker, contents in self.fields(fileids, **kwargs): + if marker == key: + entries.append((contents, [])) + else: + try: + entries[-1][-1].append((marker, contents)) + except IndexError: + pass + return entries + + def words(self, fileids, key="lx"): + return [contents for marker, contents in self.fields(fileids) if marker == key] + + +def demo(): + pass + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/twitter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/twitter.py new file mode 100644 index 0000000..6eeea81 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/twitter.py @@ -0,0 +1,136 @@ +# Natural Language Toolkit: Twitter Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +A reader for corpora that consist of Tweets. It is assumed that the Tweets +have been serialised into line-delimited JSON. +""" + +import json +import os + +from nltk.corpus.reader.api import CorpusReader +from nltk.corpus.reader.util import StreamBackedCorpusView, ZipFilePathPointer, concat +from nltk.tokenize import TweetTokenizer + + +class TwitterCorpusReader(CorpusReader): + r""" + Reader for corpora that consist of Tweets represented as a list of line-delimited JSON. + + Individual Tweets can be tokenized using the default tokenizer, or by a + custom tokenizer specified as a parameter to the constructor. + + Construct a new Tweet corpus reader for a set of documents + located at the given root directory. + + If you made your own tweet collection in a directory called + `twitter-files`, then you can initialise the reader as:: + + from nltk.corpus import TwitterCorpusReader + reader = TwitterCorpusReader(root='/path/to/twitter-files', '.*\.json') + + However, the recommended approach is to set the relevant directory as the + value of the environmental variable `TWITTER`, and then invoke the reader + as follows:: + + root = os.environ['TWITTER'] + reader = TwitterCorpusReader(root, '.*\.json') + + If you want to work directly with the raw Tweets, the `json` library can + be used:: + + import json + for tweet in reader.docs(): + print(json.dumps(tweet, indent=1, sort_keys=True)) + + """ + + CorpusView = StreamBackedCorpusView + """ + The corpus view class used by this reader. + """ + + def __init__( + self, root, fileids=None, word_tokenizer=TweetTokenizer(), encoding="utf8" + ): + """ + :param root: The root directory for this corpus. + :param fileids: A list or regexp specifying the fileids in this corpus. + :param word_tokenizer: Tokenizer for breaking the text of Tweets into + smaller units, including but not limited to words. + """ + CorpusReader.__init__(self, root, fileids, encoding) + + for path in self.abspaths(self._fileids): + if isinstance(path, ZipFilePathPointer): + pass + elif os.path.getsize(path) == 0: + raise ValueError(f"File {path} is empty") + """Check that all user-created corpus files are non-empty.""" + + self._word_tokenizer = word_tokenizer + + def docs(self, fileids=None): + """ + Returns the full Tweet objects, as specified by `Twitter + documentation on Tweets + `_ + + :return: the given file(s) as a list of dictionaries deserialised + from JSON. + :rtype: list(dict) + """ + return concat( + [ + self.CorpusView(path, self._read_tweets, encoding=enc) + for (path, enc, fileid) in self.abspaths(fileids, True, True) + ] + ) + + def strings(self, fileids=None): + """ + Returns only the text content of Tweets in the file(s) + + :return: the given file(s) as a list of Tweets. + :rtype: list(str) + """ + fulltweets = self.docs(fileids) + tweets = [] + for jsono in fulltweets: + try: + text = jsono["text"] + if isinstance(text, bytes): + text = text.decode(self.encoding) + tweets.append(text) + except KeyError: + pass + return tweets + + def tokenized(self, fileids=None): + """ + :return: the given file(s) as a list of the text content of Tweets as + as a list of words, screenanames, hashtags, URLs and punctuation symbols. + + :rtype: list(list(str)) + """ + tweets = self.strings(fileids) + tokenizer = self._word_tokenizer + return [tokenizer.tokenize(t) for t in tweets] + + def _read_tweets(self, stream): + """ + Assumes that each line in ``stream`` is a JSON-serialised object. + """ + tweets = [] + for i in range(10): + line = stream.readline() + if not line: + return tweets + tweet = json.loads(line) + tweets.append(tweet) + return tweets diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/udhr.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/udhr.py new file mode 100644 index 0000000..2bbd102 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/udhr.py @@ -0,0 +1,75 @@ +""" +UDHR corpus reader. It mostly deals with encodings. +""" + +from nltk.corpus.reader.plaintext import PlaintextCorpusReader +from nltk.corpus.reader.util import find_corpus_fileids + + +class UdhrCorpusReader(PlaintextCorpusReader): + + ENCODINGS = [ + (".*-Latin1$", "latin-1"), + (".*-Hebrew$", "hebrew"), + (".*-Arabic$", "cp1256"), + ("Czech_Cesky-UTF8", "cp1250"), # yeah + ("Polish-Latin2", "cp1250"), + ("Polish_Polski-Latin2", "cp1250"), + (".*-Cyrillic$", "cyrillic"), + (".*-SJIS$", "SJIS"), + (".*-GB2312$", "GB2312"), + (".*-Latin2$", "ISO-8859-2"), + (".*-Greek$", "greek"), + (".*-UTF8$", "utf-8"), + ("Hungarian_Magyar-Unicode", "utf-16-le"), + ("Amahuaca", "latin1"), + ("Turkish_Turkce-Turkish", "latin5"), + ("Lithuanian_Lietuviskai-Baltic", "latin4"), + ("Japanese_Nihongo-EUC", "EUC-JP"), + ("Japanese_Nihongo-JIS", "iso2022_jp"), + ("Chinese_Mandarin-HZ", "hz"), + (r"Abkhaz\-Cyrillic\+Abkh", "cp1251"), + ] + + SKIP = { + # The following files are not fully decodable because they + # were truncated at wrong bytes: + "Burmese_Myanmar-UTF8", + "Japanese_Nihongo-JIS", + "Chinese_Mandarin-HZ", + "Chinese_Mandarin-UTF8", + "Gujarati-UTF8", + "Hungarian_Magyar-Unicode", + "Lao-UTF8", + "Magahi-UTF8", + "Marathi-UTF8", + "Tamil-UTF8", + # Unfortunately, encodings required for reading + # the following files are not supported by Python: + "Vietnamese-VPS", + "Vietnamese-VIQR", + "Vietnamese-TCVN", + "Magahi-Agra", + "Bhojpuri-Agra", + "Esperanto-T61", # latin3 raises an exception + # The following files are encoded for specific fonts: + "Burmese_Myanmar-WinResearcher", + "Armenian-DallakHelv", + "Tigrinya_Tigrigna-VG2Main", + "Amharic-Afenegus6..60375", # ? + "Navaho_Dine-Navajo-Navaho-font", + # What are these? + "Azeri_Azerbaijani_Cyrillic-Az.Times.Cyr.Normal0117", + "Azeri_Azerbaijani_Latin-Az.Times.Lat0117", + # The following files are unintended: + "Czech-Latin2-err", + "Russian_Russky-UTF8~", + } + + def __init__(self, root="udhr"): + fileids = find_corpus_fileids(root, r"(?!README|\.).*") + super().__init__( + root, + [fileid for fileid in fileids if fileid not in self.SKIP], + encoding=self.ENCODINGS, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/util.py new file mode 100644 index 0000000..9c8b16a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/util.py @@ -0,0 +1,867 @@ +# Natural Language Toolkit: Corpus Reader Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +import bisect +import os +import pickle +import re +import tempfile +from functools import reduce +from xml.etree import ElementTree + +from nltk.data import ( + FileSystemPathPointer, + PathPointer, + SeekableUnicodeStreamReader, + ZipFilePathPointer, +) +from nltk.internals import slice_bounds +from nltk.tokenize import wordpunct_tokenize +from nltk.util import AbstractLazySequence, LazyConcatenation, LazySubsequence + +###################################################################### +# { Corpus View +###################################################################### + + +class StreamBackedCorpusView(AbstractLazySequence): + """ + A 'view' of a corpus file, which acts like a sequence of tokens: + it can be accessed by index, iterated over, etc. However, the + tokens are only constructed as-needed -- the entire corpus is + never stored in memory at once. + + The constructor to ``StreamBackedCorpusView`` takes two arguments: + a corpus fileid (specified as a string or as a ``PathPointer``); + and a block reader. A "block reader" is a function that reads + zero or more tokens from a stream, and returns them as a list. A + very simple example of a block reader is: + + >>> def simple_block_reader(stream): + ... return stream.readline().split() + + This simple block reader reads a single line at a time, and + returns a single token (consisting of a string) for each + whitespace-separated substring on the line. + + When deciding how to define the block reader for a given + corpus, careful consideration should be given to the size of + blocks handled by the block reader. Smaller block sizes will + increase the memory requirements of the corpus view's internal + data structures (by 2 integers per block). On the other hand, + larger block sizes may decrease performance for random access to + the corpus. (But note that larger block sizes will *not* + decrease performance for iteration.) + + Internally, ``CorpusView`` maintains a partial mapping from token + index to file position, with one entry per block. When a token + with a given index *i* is requested, the ``CorpusView`` constructs + it as follows: + + 1. First, it searches the toknum/filepos mapping for the token + index closest to (but less than or equal to) *i*. + + 2. Then, starting at the file position corresponding to that + index, it reads one block at a time using the block reader + until it reaches the requested token. + + The toknum/filepos mapping is created lazily: it is initially + empty, but every time a new block is read, the block's + initial token is added to the mapping. (Thus, the toknum/filepos + map has one entry per block.) + + In order to increase efficiency for random access patterns that + have high degrees of locality, the corpus view may cache one or + more blocks. + + :note: Each ``CorpusView`` object internally maintains an open file + object for its underlying corpus file. This file should be + automatically closed when the ``CorpusView`` is garbage collected, + but if you wish to close it manually, use the ``close()`` + method. If you access a ``CorpusView``'s items after it has been + closed, the file object will be automatically re-opened. + + :warning: If the contents of the file are modified during the + lifetime of the ``CorpusView``, then the ``CorpusView``'s behavior + is undefined. + + :warning: If a unicode encoding is specified when constructing a + ``CorpusView``, then the block reader may only call + ``stream.seek()`` with offsets that have been returned by + ``stream.tell()``; in particular, calling ``stream.seek()`` with + relative offsets, or with offsets based on string lengths, may + lead to incorrect behavior. + + :ivar _block_reader: The function used to read + a single block from the underlying file stream. + :ivar _toknum: A list containing the token index of each block + that has been processed. In particular, ``_toknum[i]`` is the + token index of the first token in block ``i``. Together + with ``_filepos``, this forms a partial mapping between token + indices and file positions. + :ivar _filepos: A list containing the file position of each block + that has been processed. In particular, ``_toknum[i]`` is the + file position of the first character in block ``i``. Together + with ``_toknum``, this forms a partial mapping between token + indices and file positions. + :ivar _stream: The stream used to access the underlying corpus file. + :ivar _len: The total number of tokens in the corpus, if known; + or None, if the number of tokens is not yet known. + :ivar _eofpos: The character position of the last character in the + file. This is calculated when the corpus view is initialized, + and is used to decide when the end of file has been reached. + :ivar _cache: A cache of the most recently read block. It + is encoded as a tuple (start_toknum, end_toknum, tokens), where + start_toknum is the token index of the first token in the block; + end_toknum is the token index of the first token not in the + block; and tokens is a list of the tokens in the block. + """ + + def __init__(self, fileid, block_reader=None, startpos=0, encoding="utf8"): + """ + Create a new corpus view, based on the file ``fileid``, and + read with ``block_reader``. See the class documentation + for more information. + + :param fileid: The path to the file that is read by this + corpus view. ``fileid`` can either be a string or a + ``PathPointer``. + + :param startpos: The file position at which the view will + start reading. This can be used to skip over preface + sections. + + :param encoding: The unicode encoding that should be used to + read the file's contents. If no encoding is specified, + then the file's contents will be read as a non-unicode + string (i.e., a str). + """ + if block_reader: + self.read_block = block_reader + # Initialize our toknum/filepos mapping. + self._toknum = [0] + self._filepos = [startpos] + self._encoding = encoding + # We don't know our length (number of tokens) yet. + self._len = None + + self._fileid = fileid + self._stream = None + + self._current_toknum = None + """This variable is set to the index of the next token that + will be read, immediately before ``self.read_block()`` is + called. This is provided for the benefit of the block + reader, which under rare circumstances may need to know + the current token number.""" + + self._current_blocknum = None + """This variable is set to the index of the next block that + will be read, immediately before ``self.read_block()`` is + called. This is provided for the benefit of the block + reader, which under rare circumstances may need to know + the current block number.""" + + # Find the length of the file. + try: + if isinstance(self._fileid, PathPointer): + self._eofpos = self._fileid.file_size() + else: + self._eofpos = os.stat(self._fileid).st_size + except Exception as exc: + raise ValueError(f"Unable to open or access {fileid!r} -- {exc}") from exc + + # Maintain a cache of the most recently read block, to + # increase efficiency of random access. + self._cache = (-1, -1, None) + + fileid = property( + lambda self: self._fileid, + doc=""" + The fileid of the file that is accessed by this view. + + :type: str or PathPointer""", + ) + + def read_block(self, stream): + """ + Read a block from the input stream. + + :return: a block of tokens from the input stream + :rtype: list(any) + :param stream: an input stream + :type stream: stream + """ + raise NotImplementedError("Abstract Method") + + def _open(self): + """ + Open the file stream associated with this corpus view. This + will be called performed if any value is read from the view + while its file stream is closed. + """ + if isinstance(self._fileid, PathPointer): + self._stream = self._fileid.open(self._encoding) + elif self._encoding: + self._stream = SeekableUnicodeStreamReader( + open(self._fileid, "rb"), self._encoding + ) + else: + self._stream = open(self._fileid, "rb") + + def close(self): + """ + Close the file stream associated with this corpus view. This + can be useful if you are worried about running out of file + handles (although the stream should automatically be closed + upon garbage collection of the corpus view). If the corpus + view is accessed after it is closed, it will be automatically + re-opened. + """ + if self._stream is not None: + self._stream.close() + self._stream = None + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def __len__(self): + if self._len is None: + # iterate_from() sets self._len when it reaches the end + # of the file: + for tok in self.iterate_from(self._toknum[-1]): + pass + return self._len + + def __getitem__(self, i): + if isinstance(i, slice): + start, stop = slice_bounds(self, i) + # Check if it's in the cache. + offset = self._cache[0] + if offset <= start and stop <= self._cache[1]: + return self._cache[2][start - offset : stop - offset] + # Construct & return the result. + return LazySubsequence(self, start, stop) + else: + # Handle negative indices + if i < 0: + i += len(self) + if i < 0: + raise IndexError("index out of range") + # Check if it's in the cache. + offset = self._cache[0] + if offset <= i < self._cache[1]: + return self._cache[2][i - offset] + # Use iterate_from to extract it. + try: + return next(self.iterate_from(i)) + except StopIteration as e: + raise IndexError("index out of range") from e + + # If we wanted to be thread-safe, then this method would need to + # do some locking. + def iterate_from(self, start_tok): + # Start by feeding from the cache, if possible. + if self._cache[0] <= start_tok < self._cache[1]: + for tok in self._cache[2][start_tok - self._cache[0] :]: + yield tok + start_tok += 1 + + # Decide where in the file we should start. If `start` is in + # our mapping, then we can jump straight to the correct block; + # otherwise, start at the last block we've processed. + if start_tok < self._toknum[-1]: + block_index = bisect.bisect_right(self._toknum, start_tok) - 1 + toknum = self._toknum[block_index] + filepos = self._filepos[block_index] + else: + block_index = len(self._toknum) - 1 + toknum = self._toknum[-1] + filepos = self._filepos[-1] + + # Open the stream, if it's not open already. + if self._stream is None: + self._open() + + # If the file is empty, the while loop will never run. + # This *seems* to be all the state we need to set: + if self._eofpos == 0: + self._len = 0 + + # Each iteration through this loop, we read a single block + # from the stream. + while filepos < self._eofpos: + # Read the next block. + self._stream.seek(filepos) + self._current_toknum = toknum + self._current_blocknum = block_index + tokens = self.read_block(self._stream) + assert isinstance(tokens, (tuple, list, AbstractLazySequence)), ( + "block reader %s() should return list or tuple." + % self.read_block.__name__ + ) + num_toks = len(tokens) + new_filepos = self._stream.tell() + assert ( + new_filepos > filepos + ), "block reader %s() should consume at least 1 byte (filepos=%d)" % ( + self.read_block.__name__, + filepos, + ) + + # Update our cache. + self._cache = (toknum, toknum + num_toks, list(tokens)) + + # Update our mapping. + assert toknum <= self._toknum[-1] + if num_toks > 0: + block_index += 1 + if toknum == self._toknum[-1]: + assert new_filepos > self._filepos[-1] # monotonic! + self._filepos.append(new_filepos) + self._toknum.append(toknum + num_toks) + else: + # Check for consistency: + assert ( + new_filepos == self._filepos[block_index] + ), "inconsistent block reader (num chars read)" + assert ( + toknum + num_toks == self._toknum[block_index] + ), "inconsistent block reader (num tokens returned)" + + # If we reached the end of the file, then update self._len + if new_filepos == self._eofpos: + self._len = toknum + num_toks + # Generate the tokens in this block (but skip any tokens + # before start_tok). Note that between yields, our state + # may be modified. + for tok in tokens[max(0, start_tok - toknum) :]: + yield tok + # If we're at the end of the file, then we're done. + assert new_filepos <= self._eofpos + if new_filepos == self._eofpos: + break + # Update our indices + toknum += num_toks + filepos = new_filepos + + # If we reach this point, then we should know our length. + assert self._len is not None + # Enforce closing of stream once we reached end of file + # We should have reached EOF once we're out of the while loop. + self.close() + + # Use concat for these, so we can use a ConcatenatedCorpusView + # when possible. + def __add__(self, other): + return concat([self, other]) + + def __radd__(self, other): + return concat([other, self]) + + def __mul__(self, count): + return concat([self] * count) + + def __rmul__(self, count): + return concat([self] * count) + + +class ConcatenatedCorpusView(AbstractLazySequence): + """ + A 'view' of a corpus file that joins together one or more + ``StreamBackedCorpusViews``. At most + one file handle is left open at any time. + """ + + def __init__(self, corpus_views): + self._pieces = corpus_views + """A list of the corpus subviews that make up this + concatenation.""" + + self._offsets = [0] + """A list of offsets, indicating the index at which each + subview begins. In particular:: + offsets[i] = sum([len(p) for p in pieces[:i]])""" + + self._open_piece = None + """The most recently accessed corpus subview (or None). + Before a new subview is accessed, this subview will be closed.""" + + def __len__(self): + if len(self._offsets) <= len(self._pieces): + # Iterate to the end of the corpus. + for tok in self.iterate_from(self._offsets[-1]): + pass + + return self._offsets[-1] + + def close(self): + for piece in self._pieces: + piece.close() + + def iterate_from(self, start_tok): + piecenum = bisect.bisect_right(self._offsets, start_tok) - 1 + + while piecenum < len(self._pieces): + offset = self._offsets[piecenum] + piece = self._pieces[piecenum] + + # If we've got another piece open, close it first. + if self._open_piece is not piece: + if self._open_piece is not None: + self._open_piece.close() + self._open_piece = piece + + # Get everything we can from this piece. + yield from piece.iterate_from(max(0, start_tok - offset)) + + # Update the offset table. + if piecenum + 1 == len(self._offsets): + self._offsets.append(self._offsets[-1] + len(piece)) + + # Move on to the next piece. + piecenum += 1 + + +def concat(docs): + """ + Concatenate together the contents of multiple documents from a + single corpus, using an appropriate concatenation function. This + utility function is used by corpus readers when the user requests + more than one document at a time. + """ + if len(docs) == 1: + return docs[0] + if len(docs) == 0: + raise ValueError("concat() expects at least one object!") + + types = {d.__class__ for d in docs} + + # If they're all strings, use string concatenation. + if all(isinstance(doc, str) for doc in docs): + return "".join(docs) + + # If they're all corpus views, then use ConcatenatedCorpusView. + for typ in types: + if not issubclass(typ, (StreamBackedCorpusView, ConcatenatedCorpusView)): + break + else: + return ConcatenatedCorpusView(docs) + + # If they're all lazy sequences, use a lazy concatenation + for typ in types: + if not issubclass(typ, AbstractLazySequence): + break + else: + return LazyConcatenation(docs) + + # Otherwise, see what we can do: + if len(types) == 1: + typ = list(types)[0] + + if issubclass(typ, list): + return reduce((lambda a, b: a + b), docs, []) + + if issubclass(typ, tuple): + return reduce((lambda a, b: a + b), docs, ()) + + if ElementTree.iselement(typ): + xmltree = ElementTree.Element("documents") + for doc in docs: + xmltree.append(doc) + return xmltree + + # No method found! + raise ValueError("Don't know how to concatenate types: %r" % types) + + +###################################################################### +# { Corpus View for Pickled Sequences +###################################################################### + + +class PickleCorpusView(StreamBackedCorpusView): + """ + A stream backed corpus view for corpus files that consist of + sequences of serialized Python objects (serialized using + ``pickle.dump``). One use case for this class is to store the + result of running feature detection on a corpus to disk. This can + be useful when performing feature detection is expensive (so we + don't want to repeat it); but the corpus is too large to store in + memory. The following example illustrates this technique: + + >>> from nltk.corpus.reader.util import PickleCorpusView + >>> from nltk.util import LazyMap + >>> feature_corpus = LazyMap(detect_features, corpus) # doctest: +SKIP + >>> PickleCorpusView.write(feature_corpus, some_fileid) # doctest: +SKIP + >>> pcv = PickleCorpusView(some_fileid) # doctest: +SKIP + """ + + BLOCK_SIZE = 100 + PROTOCOL = -1 + + def __init__(self, fileid, delete_on_gc=False): + """ + Create a new corpus view that reads the pickle corpus + ``fileid``. + + :param delete_on_gc: If true, then ``fileid`` will be deleted + whenever this object gets garbage-collected. + """ + self._delete_on_gc = delete_on_gc + StreamBackedCorpusView.__init__(self, fileid) + + def read_block(self, stream): + result = [] + for i in range(self.BLOCK_SIZE): + try: + result.append(pickle.load(stream)) + except EOFError: + break + return result + + def __del__(self): + """ + If ``delete_on_gc`` was set to true when this + ``PickleCorpusView`` was created, then delete the corpus view's + fileid. (This method is called whenever a + ``PickledCorpusView`` is garbage-collected. + """ + if getattr(self, "_delete_on_gc"): + if os.path.exists(self._fileid): + try: + os.remove(self._fileid) + except OSError: + pass + self.__dict__.clear() # make the garbage collector's job easier + + @classmethod + def write(cls, sequence, output_file): + if isinstance(output_file, str): + output_file = open(output_file, "wb") + for item in sequence: + pickle.dump(item, output_file, cls.PROTOCOL) + + @classmethod + def cache_to_tempfile(cls, sequence, delete_on_gc=True): + """ + Write the given sequence to a temporary file as a pickle + corpus; and then return a ``PickleCorpusView`` view for that + temporary corpus file. + + :param delete_on_gc: If true, then the temporary file will be + deleted whenever this object gets garbage-collected. + """ + try: + fd, output_file_name = tempfile.mkstemp(".pcv", "nltk-") + output_file = os.fdopen(fd, "wb") + cls.write(sequence, output_file) + output_file.close() + return PickleCorpusView(output_file_name, delete_on_gc) + except OSError as e: + raise ValueError("Error while creating temp file: %s" % e) from e + + +###################################################################### +# { Block Readers +###################################################################### + + +def read_whitespace_block(stream): + toks = [] + for i in range(20): # Read 20 lines at a time. + toks.extend(stream.readline().split()) + return toks + + +def read_wordpunct_block(stream): + toks = [] + for i in range(20): # Read 20 lines at a time. + toks.extend(wordpunct_tokenize(stream.readline())) + return toks + + +def read_line_block(stream): + toks = [] + for i in range(20): + line = stream.readline() + if not line: + return toks + toks.append(line.rstrip("\n")) + return toks + + +def read_blankline_block(stream): + s = "" + while True: + line = stream.readline() + # End of file: + if not line: + if s: + return [s] + else: + return [] + # Blank line: + elif line and not line.strip(): + if s: + return [s] + # Other line: + else: + s += line + + +def read_alignedsent_block(stream): + s = "" + while True: + line = stream.readline() + if line[0] == "=" or line[0] == "\n" or line[:2] == "\r\n": + continue + # End of file: + if not line: + if s: + return [s] + else: + return [] + # Other line: + else: + s += line + if re.match(r"^\d+-\d+", line) is not None: + return [s] + + +def read_regexp_block(stream, start_re, end_re=None): + """ + Read a sequence of tokens from a stream, where tokens begin with + lines that match ``start_re``. If ``end_re`` is specified, then + tokens end with lines that match ``end_re``; otherwise, tokens end + whenever the next line matching ``start_re`` or EOF is found. + """ + # Scan until we find a line matching the start regexp. + while True: + line = stream.readline() + if not line: + return [] # end of file. + if re.match(start_re, line): + break + + # Scan until we find another line matching the regexp, or EOF. + lines = [line] + while True: + oldpos = stream.tell() + line = stream.readline() + # End of file: + if not line: + return ["".join(lines)] + # End of token: + if end_re is not None and re.match(end_re, line): + return ["".join(lines)] + # Start of new token: backup to just before it starts, and + # return the token we've already collected. + if end_re is None and re.match(start_re, line): + stream.seek(oldpos) + return ["".join(lines)] + # Anything else is part of the token. + lines.append(line) + + +def read_sexpr_block(stream, block_size=16384, comment_char=None): + """ + Read a sequence of s-expressions from the stream, and leave the + stream's file position at the end the last complete s-expression + read. This function will always return at least one s-expression, + unless there are no more s-expressions in the file. + + If the file ends in in the middle of an s-expression, then that + incomplete s-expression is returned when the end of the file is + reached. + + :param block_size: The default block size for reading. If an + s-expression is longer than one block, then more than one + block will be read. + :param comment_char: A character that marks comments. Any lines + that begin with this character will be stripped out. + (If spaces or tabs precede the comment character, then the + line will not be stripped.) + """ + start = stream.tell() + block = stream.read(block_size) + encoding = getattr(stream, "encoding", None) + assert encoding is not None or isinstance(block, str) + if encoding not in (None, "utf-8"): + import warnings + + warnings.warn( + "Parsing may fail, depending on the properties " + "of the %s encoding!" % encoding + ) + # (e.g., the utf-16 encoding does not work because it insists + # on adding BOMs to the beginning of encoded strings.) + + if comment_char: + COMMENT = re.compile("(?m)^%s.*$" % re.escape(comment_char)) + while True: + try: + # If we're stripping comments, then make sure our block ends + # on a line boundary; and then replace any comments with + # space characters. (We can't just strip them out -- that + # would make our offset wrong.) + if comment_char: + block += stream.readline() + block = re.sub(COMMENT, _sub_space, block) + # Read the block. + tokens, offset = _parse_sexpr_block(block) + # Skip whitespace + offset = re.compile(r"\s*").search(block, offset).end() + + # Move to the end position. + if encoding is None: + stream.seek(start + offset) + else: + stream.seek(start + len(block[:offset].encode(encoding))) + + # Return the list of tokens we processed + return tokens + except ValueError as e: + if e.args[0] == "Block too small": + next_block = stream.read(block_size) + if next_block: + block += next_block + continue + else: + # The file ended mid-sexpr -- return what we got. + return [block.strip()] + else: + raise + + +def _sub_space(m): + """Helper function: given a regexp match, return a string of + spaces that's the same length as the matched string.""" + return " " * (m.end() - m.start()) + + +def _parse_sexpr_block(block): + tokens = [] + start = end = 0 + + while end < len(block): + m = re.compile(r"\S").search(block, end) + if not m: + return tokens, end + + start = m.start() + + # Case 1: sexpr is not parenthesized. + if m.group() != "(": + m2 = re.compile(r"[\s(]").search(block, start) + if m2: + end = m2.start() + else: + if tokens: + return tokens, end + raise ValueError("Block too small") + + # Case 2: parenthesized sexpr. + else: + nesting = 0 + for m in re.compile(r"[()]").finditer(block, start): + if m.group() == "(": + nesting += 1 + else: + nesting -= 1 + if nesting == 0: + end = m.end() + break + else: + if tokens: + return tokens, end + raise ValueError("Block too small") + + tokens.append(block[start:end]) + + return tokens, end + + +###################################################################### +# { Finding Corpus Items +###################################################################### + + +def find_corpus_fileids(root, regexp): + if not isinstance(root, PathPointer): + raise TypeError("find_corpus_fileids: expected a PathPointer") + regexp += "$" + + # Find fileids in a zipfile: scan the zipfile's namelist. Filter + # out entries that end in '/' -- they're directories. + if isinstance(root, ZipFilePathPointer): + fileids = [ + name[len(root.entry) :] + for name in root.zipfile.namelist() + if not name.endswith("/") + ] + items = [name for name in fileids if re.match(regexp, name)] + return sorted(items) + + # Find fileids in a directory: use os.walk to search all (proper + # or symlinked) subdirectories, and match paths against the regexp. + elif isinstance(root, FileSystemPathPointer): + items = [] + for dirname, subdirs, fileids in os.walk(root.path): + prefix = "".join("%s/" % p for p in _path_from(root.path, dirname)) + items += [ + prefix + fileid + for fileid in fileids + if re.match(regexp, prefix + fileid) + ] + # Don't visit svn directories: + if ".svn" in subdirs: + subdirs.remove(".svn") + return sorted(items) + + else: + raise AssertionError("Don't know how to handle %r" % root) + + +def _path_from(parent, child): + if os.path.split(parent)[1] == "": + parent = os.path.split(parent)[0] + path = [] + while parent != child: + child, dirname = os.path.split(child) + path.insert(0, dirname) + assert os.path.split(child)[0] != child + return path + + +###################################################################### +# { Paragraph structure in Treebank files +###################################################################### + + +def tagged_treebank_para_block_reader(stream): + # Read the next paragraph. + para = "" + while True: + line = stream.readline() + # End of paragraph: + if re.match(r"======+\s*$", line): + if para.strip(): + return [para] + # End of file: + elif line == "": + if para.strip(): + return [para] + else: + return [] + # Content line: + else: + para += line diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/verbnet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/verbnet.py new file mode 100644 index 0000000..b73ac23 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/verbnet.py @@ -0,0 +1,629 @@ +# Natural Language Toolkit: Verbnet Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +An NLTK interface to the VerbNet verb lexicon + +For details about VerbNet see: +https://verbs.colorado.edu/~mpalmer/projects/verbnet.html +""" + +import re +import textwrap +from collections import defaultdict + +from nltk.corpus.reader.xmldocs import XMLCorpusReader + + +class VerbnetCorpusReader(XMLCorpusReader): + """ + An NLTK interface to the VerbNet verb lexicon. + + From the VerbNet site: "VerbNet (VN) (Kipper-Schuler 2006) is the largest + on-line verb lexicon currently available for English. It is a hierarchical + domain-independent, broad-coverage verb lexicon with mappings to other + lexical resources such as WordNet (Miller, 1990; Fellbaum, 1998), XTAG + (XTAG Research Group, 2001), and FrameNet (Baker et al., 1998)." + + For details about VerbNet see: + https://verbs.colorado.edu/~mpalmer/projects/verbnet.html + """ + + # No unicode encoding param, since the data files are all XML. + def __init__(self, root, fileids, wrap_etree=False): + XMLCorpusReader.__init__(self, root, fileids, wrap_etree) + + self._lemma_to_class = defaultdict(list) + """A dictionary mapping from verb lemma strings to lists of + VerbNet class identifiers.""" + + self._wordnet_to_class = defaultdict(list) + """A dictionary mapping from wordnet identifier strings to + lists of VerbNet class identifiers.""" + + self._class_to_fileid = {} + """A dictionary mapping from class identifiers to + corresponding file identifiers. The keys of this dictionary + provide a complete list of all classes and subclasses.""" + + self._shortid_to_longid = {} + + # Initialize the dictionaries. Use the quick (regexp-based) + # method instead of the slow (xml-based) method, because it + # runs 2-30 times faster. + self._quick_index() + + _LONGID_RE = re.compile(r"([^\-\.]*)-([\d+.\-]+)$") + """Regular expression that matches (and decomposes) longids""" + + _SHORTID_RE = re.compile(r"[\d+.\-]+$") + """Regular expression that matches shortids""" + + _INDEX_RE = re.compile( + r']+>|' r'' + ) + """Regular expression used by ``_index()`` to quickly scan the corpus + for basic information.""" + + def lemmas(self, vnclass=None): + """ + Return a list of all verb lemmas that appear in any class, or + in the ``classid`` if specified. + """ + if vnclass is None: + return sorted(self._lemma_to_class.keys()) + else: + # [xx] should this include subclass members? + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + return [member.get("name") for member in vnclass.findall("MEMBERS/MEMBER")] + + def wordnetids(self, vnclass=None): + """ + Return a list of all wordnet identifiers that appear in any + class, or in ``classid`` if specified. + """ + if vnclass is None: + return sorted(self._wordnet_to_class.keys()) + else: + # [xx] should this include subclass members? + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + return sum( + ( + member.get("wn", "").split() + for member in vnclass.findall("MEMBERS/MEMBER") + ), + [], + ) + + def classids(self, lemma=None, wordnetid=None, fileid=None, classid=None): + """ + Return a list of the VerbNet class identifiers. If a file + identifier is specified, then return only the VerbNet class + identifiers for classes (and subclasses) defined by that file. + If a lemma is specified, then return only VerbNet class + identifiers for classes that contain that lemma as a member. + If a wordnetid is specified, then return only identifiers for + classes that contain that wordnetid as a member. If a classid + is specified, then return only identifiers for subclasses of + the specified VerbNet class. + If nothing is specified, return all classids within VerbNet + """ + if fileid is not None: + return [c for (c, f) in self._class_to_fileid.items() if f == fileid] + elif lemma is not None: + return self._lemma_to_class[lemma] + elif wordnetid is not None: + return self._wordnet_to_class[wordnetid] + elif classid is not None: + xmltree = self.vnclass(classid) + return [ + subclass.get("ID") + for subclass in xmltree.findall("SUBCLASSES/VNSUBCLASS") + ] + else: + return sorted(self._class_to_fileid.keys()) + + def vnclass(self, fileid_or_classid): + """Returns VerbNet class ElementTree + + Return an ElementTree containing the xml for the specified + VerbNet class. + + :param fileid_or_classid: An identifier specifying which class + should be returned. Can be a file identifier (such as + ``'put-9.1.xml'``), or a VerbNet class identifier (such as + ``'put-9.1'``) or a short VerbNet class identifier (such as + ``'9.1'``). + """ + # File identifier: just return the xml. + if fileid_or_classid in self._fileids: + return self.xml(fileid_or_classid) + + # Class identifier: get the xml, and find the right elt. + classid = self.longid(fileid_or_classid) + if classid in self._class_to_fileid: + fileid = self._class_to_fileid[self.longid(classid)] + tree = self.xml(fileid) + if classid == tree.get("ID"): + return tree + else: + for subclass in tree.findall(".//VNSUBCLASS"): + if classid == subclass.get("ID"): + return subclass + else: + assert False # we saw it during _index()! + + else: + raise ValueError(f"Unknown identifier {fileid_or_classid}") + + def fileids(self, vnclass_ids=None): + """ + Return a list of fileids that make up this corpus. If + ``vnclass_ids`` is specified, then return the fileids that make + up the specified VerbNet class(es). + """ + if vnclass_ids is None: + return self._fileids + elif isinstance(vnclass_ids, str): + return [self._class_to_fileid[self.longid(vnclass_ids)]] + else: + return [ + self._class_to_fileid[self.longid(vnclass_id)] + for vnclass_id in vnclass_ids + ] + + def frames(self, vnclass): + """Given a VerbNet class, this method returns VerbNet frames + + The members returned are: + 1) Example + 2) Description + 3) Syntax + 4) Semantics + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + :return: frames - a list of frame dictionaries + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + frames = [] + vnframes = vnclass.findall("FRAMES/FRAME") + for vnframe in vnframes: + frames.append( + { + "example": self._get_example_within_frame(vnframe), + "description": self._get_description_within_frame(vnframe), + "syntax": self._get_syntactic_list_within_frame(vnframe), + "semantics": self._get_semantics_within_frame(vnframe), + } + ) + return frames + + def subclasses(self, vnclass): + """Returns subclass ids, if any exist + + Given a VerbNet class, this method returns subclass ids (if they exist) + in a list of strings. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + :return: list of subclasses + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + subclasses = [ + subclass.get("ID") for subclass in vnclass.findall("SUBCLASSES/VNSUBCLASS") + ] + return subclasses + + def themroles(self, vnclass): + """Returns thematic roles participating in a VerbNet class + + Members returned as part of roles are- + 1) Type + 2) Modifiers + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + :return: themroles: A list of thematic roles in the VerbNet class + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + themroles = [] + for trole in vnclass.findall("THEMROLES/THEMROLE"): + themroles.append( + { + "type": trole.get("type"), + "modifiers": [ + {"value": restr.get("Value"), "type": restr.get("type")} + for restr in trole.findall("SELRESTRS/SELRESTR") + ], + } + ) + return themroles + + ###################################################################### + # { Index Initialization + ###################################################################### + + def _index(self): + """ + Initialize the indexes ``_lemma_to_class``, + ``_wordnet_to_class``, and ``_class_to_fileid`` by scanning + through the corpus fileids. This is fast if ElementTree + uses the C implementation (<0.1 secs), but quite slow (>10 secs) + if only the python implementation is available. + """ + for fileid in self._fileids: + self._index_helper(self.xml(fileid), fileid) + + def _index_helper(self, xmltree, fileid): + """Helper for ``_index()``""" + vnclass = xmltree.get("ID") + self._class_to_fileid[vnclass] = fileid + self._shortid_to_longid[self.shortid(vnclass)] = vnclass + for member in xmltree.findall("MEMBERS/MEMBER"): + self._lemma_to_class[member.get("name")].append(vnclass) + for wn in member.get("wn", "").split(): + self._wordnet_to_class[wn].append(vnclass) + for subclass in xmltree.findall("SUBCLASSES/VNSUBCLASS"): + self._index_helper(subclass, fileid) + + def _quick_index(self): + """ + Initialize the indexes ``_lemma_to_class``, + ``_wordnet_to_class``, and ``_class_to_fileid`` by scanning + through the corpus fileids. This doesn't do proper xml parsing, + but is good enough to find everything in the standard VerbNet + corpus -- and it runs about 30 times faster than xml parsing + (with the python ElementTree; only 2-3 times faster + if ElementTree uses the C implementation). + """ + # nb: if we got rid of wordnet_to_class, this would run 2-3 + # times faster. + for fileid in self._fileids: + vnclass = fileid[:-4] # strip the '.xml' + self._class_to_fileid[vnclass] = fileid + self._shortid_to_longid[self.shortid(vnclass)] = vnclass + with self.open(fileid) as fp: + for m in self._INDEX_RE.finditer(fp.read()): + groups = m.groups() + if groups[0] is not None: + self._lemma_to_class[groups[0]].append(vnclass) + for wn in groups[1].split(): + self._wordnet_to_class[wn].append(vnclass) + elif groups[2] is not None: + self._class_to_fileid[groups[2]] = fileid + vnclass = groups[2] # for elts. + self._shortid_to_longid[self.shortid(vnclass)] = vnclass + else: + assert False, "unexpected match condition" + + ###################################################################### + # { Identifier conversion + ###################################################################### + + def longid(self, shortid): + """Returns longid of a VerbNet class + + Given a short VerbNet class identifier (eg '37.10'), map it + to a long id (eg 'confess-37.10'). If ``shortid`` is already a + long id, then return it as-is""" + if self._LONGID_RE.match(shortid): + return shortid # it's already a longid. + elif not self._SHORTID_RE.match(shortid): + raise ValueError("vnclass identifier %r not found" % shortid) + try: + return self._shortid_to_longid[shortid] + except KeyError as e: + raise ValueError("vnclass identifier %r not found" % shortid) from e + + def shortid(self, longid): + """Returns shortid of a VerbNet class + + Given a long VerbNet class identifier (eg 'confess-37.10'), + map it to a short id (eg '37.10'). If ``longid`` is already a + short id, then return it as-is.""" + if self._SHORTID_RE.match(longid): + return longid # it's already a shortid. + m = self._LONGID_RE.match(longid) + if m: + return m.group(2) + else: + raise ValueError("vnclass identifier %r not found" % longid) + + ###################################################################### + # { Frame access utility functions + ###################################################################### + + def _get_semantics_within_frame(self, vnframe): + """Returns semantics within a single frame + + A utility function to retrieve semantics within a frame in VerbNet + Members of the semantics dictionary: + 1) Predicate value + 2) Arguments + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + :return: semantics: semantics dictionary + """ + semantics_within_single_frame = [] + for pred in vnframe.findall("SEMANTICS/PRED"): + arguments = [ + {"type": arg.get("type"), "value": arg.get("value")} + for arg in pred.findall("ARGS/ARG") + ] + semantics_within_single_frame.append( + { + "predicate_value": pred.get("value"), + "arguments": arguments, + "negated": pred.get("bool") == "!", + } + ) + return semantics_within_single_frame + + def _get_example_within_frame(self, vnframe): + """Returns example within a frame + + A utility function to retrieve an example within a frame in VerbNet. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + :return: example_text: The example sentence for this particular frame + """ + example_element = vnframe.find("EXAMPLES/EXAMPLE") + if example_element is not None: + example_text = example_element.text + else: + example_text = "" + return example_text + + def _get_description_within_frame(self, vnframe): + """Returns member description within frame + + A utility function to retrieve a description of participating members + within a frame in VerbNet. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + :return: description: a description dictionary with members - primary and secondary + """ + description_element = vnframe.find("DESCRIPTION") + return { + "primary": description_element.attrib["primary"], + "secondary": description_element.get("secondary", ""), + } + + def _get_syntactic_list_within_frame(self, vnframe): + """Returns semantics within a frame + + A utility function to retrieve semantics within a frame in VerbNet. + Members of the syntactic dictionary: + 1) POS Tag + 2) Modifiers + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + :return: syntax_within_single_frame + """ + syntax_within_single_frame = [] + for elt in vnframe.find("SYNTAX"): + pos_tag = elt.tag + modifiers = dict() + modifiers["value"] = elt.get("value") if "value" in elt.attrib else "" + modifiers["selrestrs"] = [ + {"value": restr.get("Value"), "type": restr.get("type")} + for restr in elt.findall("SELRESTRS/SELRESTR") + ] + modifiers["synrestrs"] = [ + {"value": restr.get("Value"), "type": restr.get("type")} + for restr in elt.findall("SYNRESTRS/SYNRESTR") + ] + syntax_within_single_frame.append( + {"pos_tag": pos_tag, "modifiers": modifiers} + ) + return syntax_within_single_frame + + ###################################################################### + # { Pretty Printing + ###################################################################### + + def pprint(self, vnclass): + """Returns pretty printed version of a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet class. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + s = vnclass.get("ID") + "\n" + s += self.pprint_subclasses(vnclass, indent=" ") + "\n" + s += self.pprint_members(vnclass, indent=" ") + "\n" + s += " Thematic roles:\n" + s += self.pprint_themroles(vnclass, indent=" ") + "\n" + s += " Frames:\n" + s += self.pprint_frames(vnclass, indent=" ") + return s + + def pprint_subclasses(self, vnclass, indent=""): + """Returns pretty printed version of subclasses of VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet class's subclasses. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + subclasses = self.subclasses(vnclass) + if not subclasses: + subclasses = ["(none)"] + s = "Subclasses: " + " ".join(subclasses) + return textwrap.fill( + s, 70, initial_indent=indent, subsequent_indent=indent + " " + ) + + def pprint_members(self, vnclass, indent=""): + """Returns pretty printed version of members in a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet class's member verbs. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + members = self.lemmas(vnclass) + if not members: + members = ["(none)"] + s = "Members: " + " ".join(members) + return textwrap.fill( + s, 70, initial_indent=indent, subsequent_indent=indent + " " + ) + + def pprint_themroles(self, vnclass, indent=""): + """Returns pretty printed version of thematic roles in a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet class's thematic roles. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + + pieces = [] + for themrole in self.themroles(vnclass): + piece = indent + "* " + themrole.get("type") + modifiers = [ + modifier["value"] + modifier["type"] + for modifier in themrole["modifiers"] + ] + if modifiers: + piece += "[{}]".format(" ".join(modifiers)) + pieces.append(piece) + return "\n".join(pieces) + + def pprint_frames(self, vnclass, indent=""): + """Returns pretty version of all frames in a VerbNet class + + Return a string containing a pretty-printed representation of + the list of frames within the VerbNet class. + + :param vnclass: A VerbNet class identifier; or an ElementTree + containing the xml contents of a VerbNet class. + """ + if isinstance(vnclass, str): + vnclass = self.vnclass(vnclass) + pieces = [] + for vnframe in self.frames(vnclass): + pieces.append(self._pprint_single_frame(vnframe, indent)) + return "\n".join(pieces) + + def _pprint_single_frame(self, vnframe, indent=""): + """Returns pretty printed version of a single frame in a VerbNet class + + Returns a string containing a pretty-printed representation of + the given frame. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + """ + frame_string = self._pprint_description_within_frame(vnframe, indent) + "\n" + frame_string += self._pprint_example_within_frame(vnframe, indent + " ") + "\n" + frame_string += ( + self._pprint_syntax_within_frame(vnframe, indent + " Syntax: ") + "\n" + ) + frame_string += indent + " Semantics:\n" + frame_string += self._pprint_semantics_within_frame(vnframe, indent + " ") + return frame_string + + def _pprint_example_within_frame(self, vnframe, indent=""): + """Returns pretty printed version of example within frame in a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet frame example. + + :param vnframe: An ElementTree containing the xml contents of + a Verbnet frame. + """ + if vnframe["example"]: + return indent + " Example: " + vnframe["example"] + + def _pprint_description_within_frame(self, vnframe, indent=""): + """Returns pretty printed version of a VerbNet frame description + + Return a string containing a pretty-printed representation of + the given VerbNet frame description. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + """ + description = indent + vnframe["description"]["primary"] + if vnframe["description"]["secondary"]: + description += " ({})".format(vnframe["description"]["secondary"]) + return description + + def _pprint_syntax_within_frame(self, vnframe, indent=""): + """Returns pretty printed version of syntax within a frame in a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet frame syntax. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + """ + pieces = [] + for element in vnframe["syntax"]: + piece = element["pos_tag"] + modifier_list = [] + if "value" in element["modifiers"] and element["modifiers"]["value"]: + modifier_list.append(element["modifiers"]["value"]) + modifier_list += [ + "{}{}".format(restr["value"], restr["type"]) + for restr in ( + element["modifiers"]["selrestrs"] + + element["modifiers"]["synrestrs"] + ) + ] + if modifier_list: + piece += "[{}]".format(" ".join(modifier_list)) + pieces.append(piece) + + return indent + " ".join(pieces) + + def _pprint_semantics_within_frame(self, vnframe, indent=""): + """Returns a pretty printed version of semantics within frame in a VerbNet class + + Return a string containing a pretty-printed representation of + the given VerbNet frame semantics. + + :param vnframe: An ElementTree containing the xml contents of + a VerbNet frame. + """ + pieces = [] + for predicate in vnframe["semantics"]: + arguments = [argument["value"] for argument in predicate["arguments"]] + pieces.append( + f"{'¬' if predicate['negated'] else ''}{predicate['predicate_value']}({', '.join(arguments)})" + ) + return "\n".join(f"{indent}* {piece}" for piece in pieces) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordlist.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordlist.py new file mode 100644 index 0000000..50458c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordlist.py @@ -0,0 +1,166 @@ +# Natural Language Toolkit: Word List Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +from nltk.corpus.reader.api import * +from nltk.corpus.reader.util import * +from nltk.tokenize import line_tokenize + + +class WordListCorpusReader(CorpusReader): + """ + List of words, one per line. Blank lines are ignored. + """ + + def words(self, fileids=None, ignore_lines_startswith="\n"): + return [ + line + for line in line_tokenize(self.raw(fileids)) + if not line.startswith(ignore_lines_startswith) + ] + + +class SwadeshCorpusReader(WordListCorpusReader): + def entries(self, fileids=None): + """ + :return: a tuple of words for the specified fileids. + """ + if not fileids: + fileids = self.fileids() + + wordlists = [self.words(f) for f in fileids] + return list(zip(*wordlists)) + + +class NonbreakingPrefixesCorpusReader(WordListCorpusReader): + """ + This is a class to read the nonbreaking prefixes textfiles from the + Moses Machine Translation toolkit. These lists are used in the Python port + of the Moses' word tokenizer. + """ + + available_langs = { + "catalan": "ca", + "czech": "cs", + "german": "de", + "greek": "el", + "english": "en", + "spanish": "es", + "finnish": "fi", + "french": "fr", + "hungarian": "hu", + "icelandic": "is", + "italian": "it", + "latvian": "lv", + "dutch": "nl", + "polish": "pl", + "portuguese": "pt", + "romanian": "ro", + "russian": "ru", + "slovak": "sk", + "slovenian": "sl", + "swedish": "sv", + "tamil": "ta", + } + # Also, add the lang IDs as the keys. + available_langs.update({v: v for v in available_langs.values()}) + + def words(self, lang=None, fileids=None, ignore_lines_startswith="#"): + """ + This module returns a list of nonbreaking prefixes for the specified + language(s). + + >>> from nltk.corpus import nonbreaking_prefixes as nbp + >>> nbp.words('en')[:10] == [u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J'] + True + >>> nbp.words('ta')[:5] == [u'\u0b85', u'\u0b86', u'\u0b87', u'\u0b88', u'\u0b89'] + True + + :return: a list words for the specified language(s). + """ + # If *lang* in list of languages available, allocate apt fileid. + # Otherwise, the function returns non-breaking prefixes for + # all languages when fileids==None. + if lang in self.available_langs: + lang = self.available_langs[lang] + fileids = ["nonbreaking_prefix." + lang] + return [ + line + for line in line_tokenize(self.raw(fileids)) + if not line.startswith(ignore_lines_startswith) + ] + + +class UnicharsCorpusReader(WordListCorpusReader): + """ + This class is used to read lists of characters from the Perl Unicode + Properties (see https://perldoc.perl.org/perluniprops.html). + The files in the perluniprop.zip are extracted using the Unicode::Tussle + module from https://search.cpan.org/~bdfoy/Unicode-Tussle-1.11/lib/Unicode/Tussle.pm + """ + + # These are categories similar to the Perl Unicode Properties + available_categories = [ + "Close_Punctuation", + "Currency_Symbol", + "IsAlnum", + "IsAlpha", + "IsLower", + "IsN", + "IsSc", + "IsSo", + "IsUpper", + "Line_Separator", + "Number", + "Open_Punctuation", + "Punctuation", + "Separator", + "Symbol", + ] + + def chars(self, category=None, fileids=None): + """ + This module returns a list of characters from the Perl Unicode Properties. + They are very useful when porting Perl tokenizers to Python. + + >>> from nltk.corpus import perluniprops as pup + >>> pup.chars('Open_Punctuation')[:5] == [u'(', u'[', u'{', u'\u0f3a', u'\u0f3c'] + True + >>> pup.chars('Currency_Symbol')[:5] == [u'$', u'\xa2', u'\xa3', u'\xa4', u'\xa5'] + True + >>> pup.available_categories + ['Close_Punctuation', 'Currency_Symbol', 'IsAlnum', 'IsAlpha', 'IsLower', 'IsN', 'IsSc', 'IsSo', 'IsUpper', 'Line_Separator', 'Number', 'Open_Punctuation', 'Punctuation', 'Separator', 'Symbol'] + + :return: a list of characters given the specific unicode character category + """ + if category in self.available_categories: + fileids = [category + ".txt"] + return list(self.raw(fileids).strip()) + + +class MWAPPDBCorpusReader(WordListCorpusReader): + """ + This class is used to read the list of word pairs from the subset of lexical + pairs of The Paraphrase Database (PPDB) XXXL used in the Monolingual Word + Alignment (MWA) algorithm described in Sultan et al. (2014a, 2014b, 2015): + + - http://acl2014.org/acl2014/Q14/pdf/Q14-1017 + - https://www.aclweb.org/anthology/S14-2039 + - https://www.aclweb.org/anthology/S15-2027 + + The original source of the full PPDB corpus can be found on + https://www.cis.upenn.edu/~ccb/ppdb/ + + :return: a list of tuples of similar lexical terms. + """ + + mwa_ppdb_xxxl_file = "ppdb-1.0-xxxl-lexical.extended.synonyms.uniquepairs" + + def entries(self, fileids=mwa_ppdb_xxxl_file): + """ + :return: a tuple of synonym word pairs. + """ + return [tuple(line.split("\t")) for line in line_tokenize(self.raw(fileids))] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordnet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordnet.py new file mode 100644 index 0000000..e5b3b5d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/wordnet.py @@ -0,0 +1,2489 @@ +# Natural Language Toolkit: WordNet +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bethard +# Steven Bird +# Edward Loper +# Nitin Madnani +# Nasruddin A’aidil Shari +# Sim Wei Ying Geraldine +# Soe Lynn +# Francis Bond +# Eric Kafe + +# URL: +# For license information, see LICENSE.TXT + +""" +An NLTK interface for WordNet + +WordNet is a lexical database of English. +Using synsets, helps find conceptual relationships between words +such as hypernyms, hyponyms, synonyms, antonyms etc. + +For details about WordNet see: +https://wordnet.princeton.edu/ + +This module also allows you to find lemmas in languages +other than English from the Open Multilingual Wordnet +https://omwn.org/ + +""" + +import math +import os +import re +import warnings +from collections import defaultdict, deque +from functools import total_ordering +from itertools import chain, islice +from operator import itemgetter + +from nltk.corpus.reader import CorpusReader +from nltk.internals import deprecated +from nltk.probability import FreqDist +from nltk.util import binary_search_file as _binary_search_file + +###################################################################### +# Table of Contents +###################################################################### +# - Constants +# - Data Classes +# - WordNetError +# - Lemma +# - Synset +# - WordNet Corpus Reader +# - WordNet Information Content Corpus Reader +# - Similarity Metrics +# - Demo + +###################################################################### +# Constants +###################################################################### + +#: Positive infinity (for similarity functions) +_INF = 1e300 + +# { Part-of-speech constants +ADJ, ADJ_SAT, ADV, NOUN, VERB = "a", "s", "r", "n", "v" +# } + +POS_LIST = [NOUN, VERB, ADJ, ADV] + +# A table of strings that are used to express verb frames. +VERB_FRAME_STRINGS = ( + None, + "Something %s", + "Somebody %s", + "It is %sing", + "Something is %sing PP", + "Something %s something Adjective/Noun", + "Something %s Adjective/Noun", + "Somebody %s Adjective", + "Somebody %s something", + "Somebody %s somebody", + "Something %s somebody", + "Something %s something", + "Something %s to somebody", + "Somebody %s on something", + "Somebody %s somebody something", + "Somebody %s something to somebody", + "Somebody %s something from somebody", + "Somebody %s somebody with something", + "Somebody %s somebody of something", + "Somebody %s something on somebody", + "Somebody %s somebody PP", + "Somebody %s something PP", + "Somebody %s PP", + "Somebody's (body part) %s", + "Somebody %s somebody to INFINITIVE", + "Somebody %s somebody INFINITIVE", + "Somebody %s that CLAUSE", + "Somebody %s to somebody", + "Somebody %s to INFINITIVE", + "Somebody %s whether INFINITIVE", + "Somebody %s somebody into V-ing something", + "Somebody %s something with something", + "Somebody %s INFINITIVE", + "Somebody %s VERB-ing", + "It %s that CLAUSE", + "Something %s INFINITIVE", + # OEWN additions: + "Somebody %s at something", + "Somebody %s for something", + "Somebody %s on somebody", + "Somebody %s out of somebody", +) + +SENSENUM_RE = re.compile(r"\.[\d]+\.") + + +###################################################################### +# Data Classes +###################################################################### + + +class WordNetError(Exception): + """An exception class for wordnet-related errors.""" + + +@total_ordering +class _WordNetObject: + """A common base class for lemmas and synsets.""" + + def hypernyms(self): + return self._related("@") + + def _hypernyms(self): + return self._related("@") + + def instance_hypernyms(self): + return self._related("@i") + + def _instance_hypernyms(self): + return self._related("@i") + + def hyponyms(self): + return self._related("~") + + def instance_hyponyms(self): + return self._related("~i") + + def member_holonyms(self): + return self._related("#m") + + def substance_holonyms(self): + return self._related("#s") + + def part_holonyms(self): + return self._related("#p") + + def member_meronyms(self): + return self._related("%m") + + def substance_meronyms(self): + return self._related("%s") + + def part_meronyms(self): + return self._related("%p") + + def topic_domains(self): + return self._related(";c") + + def in_topic_domains(self): + return self._related("-c") + + def region_domains(self): + return self._related(";r") + + def in_region_domains(self): + return self._related("-r") + + def usage_domains(self): + return self._related(";u") + + def in_usage_domains(self): + return self._related("-u") + + def attributes(self): + return self._related("=") + + def entailments(self): + return self._related("*") + + def causes(self): + return self._related(">") + + def also_sees(self): + return self._related("^") + + def verb_groups(self): + return self._related("$") + + def similar_tos(self): + return self._related("&") + + def __hash__(self): + return hash(self._name) + + def __eq__(self, other): + return self._name == other._name + + def __ne__(self, other): + return self._name != other._name + + def __lt__(self, other): + return self._name < other._name + + +class Lemma(_WordNetObject): + """ + The lexical entry for a single morphological form of a + sense-disambiguated word. + + Create a Lemma from a "..." string where: + is the morphological stem identifying the synset + is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB + is the sense number, counting from 0. + is the morphological form of interest + + Note that and can be different, e.g. the Synset + 'salt.n.03' has the Lemmas 'salt.n.03.salt', 'salt.n.03.saltiness' and + 'salt.n.03.salinity'. + + Lemma attributes, accessible via methods with the same name: + + - name: The canonical name of this lemma. + - synset: The synset that this lemma belongs to. + - syntactic_marker: For adjectives, the WordNet string identifying the + syntactic position relative modified noun. See: + https://wordnet.princeton.edu/documentation/wninput5wn + For all other parts of speech, this attribute is None. + - count: The frequency of this lemma in wordnet. + + Lemma methods: + + Lemmas have the following methods for retrieving related Lemmas. They + correspond to the names for the pointer symbols defined here: + https://wordnet.princeton.edu/documentation/wninput5wn + These methods all return lists of Lemmas: + + - antonyms + - hypernyms, instance_hypernyms + - hyponyms, instance_hyponyms + - member_holonyms, substance_holonyms, part_holonyms + - member_meronyms, substance_meronyms, part_meronyms + - topic_domains, region_domains, usage_domains + - attributes + - derivationally_related_forms + - entailments + - causes + - also_sees + - verb_groups + - similar_tos + - pertainyms + """ + + __slots__ = [ + "_wordnet_corpus_reader", + "_name", + "_syntactic_marker", + "_synset", + "_frame_strings", + "_frame_ids", + "_lexname_index", + "_lex_id", + "_lang", + "_key", + ] + + def __init__( + self, + wordnet_corpus_reader, + synset, + name, + lexname_index, + lex_id, + syntactic_marker, + ): + self._wordnet_corpus_reader = wordnet_corpus_reader + self._name = name + self._syntactic_marker = syntactic_marker + self._synset = synset + self._frame_strings = [] + self._frame_ids = [] + self._lexname_index = lexname_index + self._lex_id = lex_id + self._lang = "eng" + + self._key = None # gets set later. + + def name(self): + return self._name + + def syntactic_marker(self): + return self._syntactic_marker + + def synset(self): + return self._synset + + def frame_strings(self): + return self._frame_strings + + def frame_ids(self): + return self._frame_ids + + def lang(self): + return self._lang + + def key(self): + return self._key + + def __repr__(self): + tup = type(self).__name__, self._synset._name, self._name + return "%s('%s.%s')" % tup + + def _related(self, relation_symbol): + get_synset = self._wordnet_corpus_reader.synset_from_pos_and_offset + if (self._name, relation_symbol) not in self._synset._lemma_pointers: + return [] + return [ + get_synset(pos, offset)._lemmas[lemma_index] + for pos, offset, lemma_index in self._synset._lemma_pointers[ + self._name, relation_symbol + ] + ] + + def count(self): + """Return the frequency count for this Lemma""" + return self._wordnet_corpus_reader.lemma_count(self) + + def antonyms(self): + return self._related("!") + + def derivationally_related_forms(self): + return self._related("+") + + def pertainyms(self): + return self._related("\\") + + +class Synset(_WordNetObject): + """Create a Synset from a ".." string where: + is the word's morphological stem + is one of the module attributes ADJ, ADJ_SAT, ADV, NOUN or VERB + is the sense number, counting from 0. + + Synset attributes, accessible via methods with the same name: + + - name: The canonical name of this synset, formed using the first lemma + of this synset. Note that this may be different from the name + passed to the constructor if that string used a different lemma to + identify the synset. + - pos: The synset's part of speech, matching one of the module level + attributes ADJ, ADJ_SAT, ADV, NOUN or VERB. + - lemmas: A list of the Lemma objects for this synset. + - definition: The definition for this synset. + - examples: A list of example strings for this synset. + - offset: The offset in the WordNet dict file of this synset. + - lexname: The name of the lexicographer file containing this synset. + + Synset methods: + + Synsets have the following methods for retrieving related Synsets. + They correspond to the names for the pointer symbols defined here: + https://wordnet.princeton.edu/documentation/wninput5wn + These methods all return lists of Synsets. + + - hypernyms, instance_hypernyms + - hyponyms, instance_hyponyms + - member_holonyms, substance_holonyms, part_holonyms + - member_meronyms, substance_meronyms, part_meronyms + - attributes + - entailments + - causes + - also_sees + - verb_groups + - similar_tos + + Additionally, Synsets support the following methods specific to the + hypernym relation: + + - root_hypernyms + - common_hypernyms + - lowest_common_hypernyms + + Note that Synsets do not support the following relations because + these are defined by WordNet as lexical relations: + + - antonyms + - derivationally_related_forms + - pertainyms + """ + + __slots__ = [ + "_pos", + "_offset", + "_name", + "_frame_ids", + "_lemmas", + "_lemma_names", + "_definition", + "_examples", + "_lexname", + "_pointers", + "_lemma_pointers", + "_max_depth", + "_min_depth", + ] + + def __init__(self, wordnet_corpus_reader): + self._wordnet_corpus_reader = wordnet_corpus_reader + # All of these attributes get initialized by + # WordNetCorpusReader._synset_from_pos_and_line() + + self._pos = None + self._offset = None + self._name = None + self._frame_ids = [] + self._lemmas = [] + self._lemma_names = [] + self._definition = None + self._examples = [] + self._lexname = None # lexicographer name + self._all_hypernyms = None + + self._pointers = defaultdict(set) + self._lemma_pointers = defaultdict(list) + + def pos(self): + return self._pos + + def offset(self): + return self._offset + + def name(self): + return self._name + + def frame_ids(self): + return self._frame_ids + + def _doc(self, doc_type, default, lang="eng"): + """Helper method for Synset.definition and Synset.examples""" + corpus = self._wordnet_corpus_reader + if lang not in corpus.langs(): + return None + elif lang == "eng": + return default + else: + corpus._load_lang_data(lang) + of = corpus.ss2of(self) + i = corpus.lg_attrs.index(doc_type) + if of in corpus._lang_data[lang][i]: + return corpus._lang_data[lang][i][of] + else: + return None + + def definition(self, lang="eng"): + """Return definition in specified language""" + return self._doc("def", self._definition, lang=lang) + + def examples(self, lang="eng"): + """Return examples in specified language""" + return self._doc("exe", self._examples, lang=lang) + + def lexname(self): + return self._lexname + + def _needs_root(self): + if self._pos == NOUN and self._wordnet_corpus_reader.get_version() != "1.6": + return False + else: + return True + + def lemma_names(self, lang="eng"): + """Return all the lemma_names associated with the synset""" + if lang == "eng": + return self._lemma_names + else: + reader = self._wordnet_corpus_reader + reader._load_lang_data(lang) + i = reader.ss2of(self) + if i in reader._lang_data[lang][0]: + return reader._lang_data[lang][0][i] + else: + return [] + + def lemmas(self, lang="eng"): + """Return all the lemma objects associated with the synset""" + if lang == "eng": + return self._lemmas + elif self._name: + self._wordnet_corpus_reader._load_lang_data(lang) + lemmark = [] + lemmy = self.lemma_names(lang) + for lem in lemmy: + temp = Lemma( + self._wordnet_corpus_reader, + self, + lem, + self._wordnet_corpus_reader._lexnames.index(self.lexname()), + 0, + None, + ) + temp._lang = lang + lemmark.append(temp) + return lemmark + + def root_hypernyms(self): + """Get the topmost hypernyms of this synset in WordNet.""" + + result = [] + seen = set() + todo = [self] + while todo: + next_synset = todo.pop() + if next_synset not in seen: + seen.add(next_synset) + next_hypernyms = ( + next_synset.hypernyms() + next_synset.instance_hypernyms() + ) + if not next_hypernyms: + result.append(next_synset) + else: + todo.extend(next_hypernyms) + return result + + # Simpler implementation which makes incorrect assumption that + # hypernym hierarchy is acyclic: + # + # if not self.hypernyms(): + # return [self] + # else: + # return list(set(root for h in self.hypernyms() + # for root in h.root_hypernyms())) + def max_depth(self): + """ + :return: The length of the longest hypernym path from this + synset to the root. + """ + + if "_max_depth" not in self.__dict__: + hypernyms = self.hypernyms() + self.instance_hypernyms() + if not hypernyms: + self._max_depth = 0 + else: + self._max_depth = 1 + max(h.max_depth() for h in hypernyms) + return self._max_depth + + def min_depth(self): + """ + :return: The length of the shortest hypernym path from this + synset to the root. + """ + + if "_min_depth" not in self.__dict__: + hypernyms = self.hypernyms() + self.instance_hypernyms() + if not hypernyms: + self._min_depth = 0 + else: + self._min_depth = 1 + min(h.min_depth() for h in hypernyms) + return self._min_depth + + def closure(self, rel, depth=-1): + """ + Return the transitive closure of source under the rel + relationship, breadth-first, discarding cycles: + + >>> from nltk.corpus import wordnet as wn + >>> computer = wn.synset('computer.n.01') + >>> topic = lambda s:s.topic_domains() + >>> print(list(computer.closure(topic))) + [Synset('computer_science.n.01')] + + UserWarning: Discarded redundant search for Synset('computer.n.01') at depth 2 + + + Include redundant paths (but only once), avoiding duplicate searches + (from 'animal.n.01' to 'entity.n.01'): + + >>> dog = wn.synset('dog.n.01') + >>> hyp = lambda s:s.hypernyms() + >>> print(list(dog.closure(hyp))) + [Synset('canine.n.02'), Synset('domestic_animal.n.01'), Synset('carnivore.n.01'),\ + Synset('animal.n.01'), Synset('placental.n.01'), Synset('organism.n.01'),\ + Synset('mammal.n.01'), Synset('living_thing.n.01'), Synset('vertebrate.n.01'),\ + Synset('whole.n.02'), Synset('chordate.n.01'), Synset('object.n.01'),\ + Synset('physical_entity.n.01'), Synset('entity.n.01')] + + UserWarning: Discarded redundant search for Synset('animal.n.01') at depth 7 + """ + + from nltk.util import acyclic_breadth_first + + for synset in acyclic_breadth_first(self, rel, depth): + if synset != self: + yield synset + + from nltk.util import acyclic_depth_first as acyclic_tree + from nltk.util import unweighted_minimum_spanning_tree as mst + + # Also add this shortcut? + # from nltk.util import unweighted_minimum_spanning_digraph as umsd + + def tree(self, rel, depth=-1, cut_mark=None): + """ + Return the full relation tree, including self, + discarding cycles: + + >>> from nltk.corpus import wordnet as wn + >>> from pprint import pprint + >>> computer = wn.synset('computer.n.01') + >>> topic = lambda s:s.topic_domains() + >>> pprint(computer.tree(topic)) + [Synset('computer.n.01'), [Synset('computer_science.n.01')]] + + UserWarning: Discarded redundant search for Synset('computer.n.01') at depth -3 + + + But keep duplicate branches (from 'animal.n.01' to 'entity.n.01'): + + >>> dog = wn.synset('dog.n.01') + >>> hyp = lambda s:s.hypernyms() + >>> pprint(dog.tree(hyp)) + [Synset('dog.n.01'), + [Synset('canine.n.02'), + [Synset('carnivore.n.01'), + [Synset('placental.n.01'), + [Synset('mammal.n.01'), + [Synset('vertebrate.n.01'), + [Synset('chordate.n.01'), + [Synset('animal.n.01'), + [Synset('organism.n.01'), + [Synset('living_thing.n.01'), + [Synset('whole.n.02'), + [Synset('object.n.01'), + [Synset('physical_entity.n.01'), + [Synset('entity.n.01')]]]]]]]]]]]]], + [Synset('domestic_animal.n.01'), + [Synset('animal.n.01'), + [Synset('organism.n.01'), + [Synset('living_thing.n.01'), + [Synset('whole.n.02'), + [Synset('object.n.01'), + [Synset('physical_entity.n.01'), [Synset('entity.n.01')]]]]]]]]] + """ + + from nltk.util import acyclic_branches_depth_first + + return acyclic_branches_depth_first(self, rel, depth, cut_mark) + + def hypernym_paths(self): + """ + Get the path(s) from this synset to the root, where each path is a + list of the synset nodes traversed on the way to the root. + + :return: A list of lists, where each list gives the node sequence + connecting the initial ``Synset`` node and a root node. + """ + paths = [] + + hypernyms = self.hypernyms() + self.instance_hypernyms() + if len(hypernyms) == 0: + paths = [[self]] + + for hypernym in hypernyms: + for ancestor_list in hypernym.hypernym_paths(): + ancestor_list.append(self) + paths.append(ancestor_list) + return paths + + def common_hypernyms(self, other): + """ + Find all synsets that are hypernyms of this synset and the + other synset. + + :type other: Synset + :param other: other input synset. + :return: The synsets that are hypernyms of both synsets. + """ + if not self._all_hypernyms: + self._all_hypernyms = { + self_synset + for self_synsets in self._iter_hypernym_lists() + for self_synset in self_synsets + } + if not other._all_hypernyms: + other._all_hypernyms = { + other_synset + for other_synsets in other._iter_hypernym_lists() + for other_synset in other_synsets + } + return list(self._all_hypernyms.intersection(other._all_hypernyms)) + + def lowest_common_hypernyms(self, other, simulate_root=False, use_min_depth=False): + """ + Get a list of lowest synset(s) that both synsets have as a hypernym. + When `use_min_depth == False` this means that the synset which appears + as a hypernym of both `self` and `other` with the lowest maximum depth + is returned or if there are multiple such synsets at the same depth + they are all returned + + However, if `use_min_depth == True` then the synset(s) which has/have + the lowest minimum depth and appear(s) in both paths is/are returned. + + By setting the use_min_depth flag to True, the behavior of NLTK2 can be + preserved. This was changed in NLTK3 to give more accurate results in a + small set of cases, generally with synsets concerning people. (eg: + 'chef.n.01', 'fireman.n.01', etc.) + + This method is an implementation of Ted Pedersen's "Lowest Common + Subsumer" method from the Perl Wordnet module. It can return either + "self" or "other" if they are a hypernym of the other. + + :type other: Synset + :param other: other input synset + :type simulate_root: bool + :param simulate_root: The various verb taxonomies do not + share a single root which disallows this metric from working for + synsets that are not connected. This flag (False by default) + creates a fake root that connects all the taxonomies. Set it + to True to enable this behavior. For the noun taxonomy, + there is usually a default root except for WordNet version 1.6. + If you are using wordnet 1.6, a fake root will need to be added + for nouns as well. + :type use_min_depth: bool + :param use_min_depth: This setting mimics older (v2) behavior of NLTK + wordnet If True, will use the min_depth function to calculate the + lowest common hypernyms. This is known to give strange results for + some synset pairs (eg: 'chef.n.01', 'fireman.n.01') but is retained + for backwards compatibility + :return: The synsets that are the lowest common hypernyms of both + synsets + """ + synsets = self.common_hypernyms(other) + if simulate_root: + fake_synset = Synset(None) + fake_synset._name = "*ROOT*" + fake_synset.hypernyms = lambda: [] + fake_synset.instance_hypernyms = lambda: [] + synsets.append(fake_synset) + + try: + if use_min_depth: + max_depth = max(s.min_depth() for s in synsets) + unsorted_lch = [s for s in synsets if s.min_depth() == max_depth] + else: + max_depth = max(s.max_depth() for s in synsets) + unsorted_lch = [s for s in synsets if s.max_depth() == max_depth] + return sorted(unsorted_lch) + except ValueError: + return [] + + def hypernym_distances(self, distance=0, simulate_root=False): + """ + Get the path(s) from this synset to the root, counting the distance + of each node from the initial node on the way. A set of + (synset, distance) tuples is returned. + + :type distance: int + :param distance: the distance (number of edges) from this hypernym to + the original hypernym ``Synset`` on which this method was called. + :return: A set of ``(Synset, int)`` tuples where each ``Synset`` is + a hypernym of the first ``Synset``. + """ + distances = {(self, distance)} + for hypernym in self._hypernyms() + self._instance_hypernyms(): + distances |= hypernym.hypernym_distances(distance + 1, simulate_root=False) + if simulate_root: + fake_synset = Synset(None) + fake_synset._name = "*ROOT*" + fake_synset_distance = max(distances, key=itemgetter(1))[1] + distances.add((fake_synset, fake_synset_distance + 1)) + return distances + + def _shortest_hypernym_paths(self, simulate_root): + if self._name == "*ROOT*": + return {self: 0} + + queue = deque([(self, 0)]) + path = {} + + while queue: + s, depth = queue.popleft() + if s in path: + continue + path[s] = depth + + depth += 1 + queue.extend((hyp, depth) for hyp in s._hypernyms()) + queue.extend((hyp, depth) for hyp in s._instance_hypernyms()) + + if simulate_root: + fake_synset = Synset(None) + fake_synset._name = "*ROOT*" + path[fake_synset] = max(path.values()) + 1 + + return path + + def shortest_path_distance(self, other, simulate_root=False): + """ + Returns the distance of the shortest path linking the two synsets (if + one exists). For each synset, all the ancestor nodes and their + distances are recorded and compared. The ancestor node common to both + synsets that can be reached with the minimum number of traversals is + used. If no ancestor nodes are common, None is returned. If a node is + compared with itself 0 is returned. + + :type other: Synset + :param other: The Synset to which the shortest path will be found. + :return: The number of edges in the shortest path connecting the two + nodes, or None if no path exists. + """ + + if self == other: + return 0 + + dist_dict1 = self._shortest_hypernym_paths(simulate_root) + dist_dict2 = other._shortest_hypernym_paths(simulate_root) + + # For each ancestor synset common to both subject synsets, find the + # connecting path length. Return the shortest of these. + + inf = float("inf") + path_distance = inf + for synset, d1 in dist_dict1.items(): + d2 = dist_dict2.get(synset, inf) + path_distance = min(path_distance, d1 + d2) + + return None if math.isinf(path_distance) else path_distance + + # interface to similarity methods + def path_similarity(self, other, verbose=False, simulate_root=True): + """ + Path Distance Similarity: + Return a score denoting how similar two word senses are, based on the + shortest path that connects the senses in the is-a (hypernym/hypnoym) + taxonomy. The score is in the range 0 to 1, except in those cases where + a path cannot be found (will only be true for verbs as there are many + distinct verb taxonomies), in which case None is returned. A score of + 1 represents identity i.e. comparing a sense with itself will return 1. + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type simulate_root: bool + :param simulate_root: The various verb taxonomies do not + share a single root which disallows this metric from working for + synsets that are not connected. This flag (True by default) + creates a fake root that connects all the taxonomies. Set it + to false to disable this behavior. For the noun taxonomy, + there is usually a default root except for WordNet version 1.6. + If you are using wordnet 1.6, a fake root will be added for nouns + as well. + :return: A score denoting the similarity of the two ``Synset`` objects, + normally between 0 and 1. None is returned if no connecting path + could be found. 1 is returned if a ``Synset`` is compared with + itself. + """ + + distance = self.shortest_path_distance( + other, + simulate_root=simulate_root and (self._needs_root() or other._needs_root()), + ) + if distance is None or distance < 0: + return None + return 1.0 / (distance + 1) + + def lch_similarity(self, other, verbose=False, simulate_root=True): + """ + Leacock Chodorow Similarity: + Return a score denoting how similar two word senses are, based on the + shortest path that connects the senses (as above) and the maximum depth + of the taxonomy in which the senses occur. The relationship is given as + -log(p/2d) where p is the shortest path length and d is the taxonomy + depth. + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type simulate_root: bool + :param simulate_root: The various verb taxonomies do not + share a single root which disallows this metric from working for + synsets that are not connected. This flag (True by default) + creates a fake root that connects all the taxonomies. Set it + to false to disable this behavior. For the noun taxonomy, + there is usually a default root except for WordNet version 1.6. + If you are using wordnet 1.6, a fake root will be added for nouns + as well. + :return: A score denoting the similarity of the two ``Synset`` objects, + normally greater than 0. None is returned if no connecting path + could be found. If a ``Synset`` is compared with itself, the + maximum score is returned, which varies depending on the taxonomy + depth. + """ + + if self._pos != other._pos: + raise WordNetError( + "Computing the lch similarity requires " + "%s and %s to have the same part of speech." % (self, other) + ) + + need_root = self._needs_root() + + if self._pos not in self._wordnet_corpus_reader._max_depth: + self._wordnet_corpus_reader._compute_max_depth(self._pos, need_root) + + depth = self._wordnet_corpus_reader._max_depth[self._pos] + + distance = self.shortest_path_distance( + other, simulate_root=simulate_root and need_root + ) + + if distance is None or distance < 0 or depth == 0: + return None + return -math.log((distance + 1) / (2.0 * depth)) + + def wup_similarity(self, other, verbose=False, simulate_root=True): + """ + Wu-Palmer Similarity: + Return a score denoting how similar two word senses are, based on the + depth of the two senses in the taxonomy and that of their Least Common + Subsumer (most specific ancestor node). Previously, the scores computed + by this implementation did _not_ always agree with those given by + Pedersen's Perl implementation of WordNet Similarity. However, with + the addition of the simulate_root flag (see below), the score for + verbs now almost always agree but not always for nouns. + + The LCS does not necessarily feature in the shortest path connecting + the two senses, as it is by definition the common ancestor deepest in + the taxonomy, not closest to the two senses. Typically, however, it + will so feature. Where multiple candidates for the LCS exist, that + whose shortest path to the root node is the longest will be selected. + Where the LCS has multiple paths to the root, the longer path is used + for the purposes of the calculation. + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type simulate_root: bool + :param simulate_root: The various verb taxonomies do not + share a single root which disallows this metric from working for + synsets that are not connected. This flag (True by default) + creates a fake root that connects all the taxonomies. Set it + to false to disable this behavior. For the noun taxonomy, + there is usually a default root except for WordNet version 1.6. + If you are using wordnet 1.6, a fake root will be added for nouns + as well. + :return: A float score denoting the similarity of the two ``Synset`` + objects, normally greater than zero. If no connecting path between + the two senses can be found, None is returned. + + """ + need_root = self._needs_root() or other._needs_root() + + # Note that to preserve behavior from NLTK2 we set use_min_depth=True + # It is possible that more accurate results could be obtained by + # removing this setting and it should be tested later on + subsumers = self.lowest_common_hypernyms( + other, simulate_root=simulate_root and need_root, use_min_depth=True + ) + + # If no LCS was found return None + if len(subsumers) == 0: + return None + + subsumer = self if self in subsumers else subsumers[0] + + # Get the longest path from the LCS to the root, + # including a correction: + # - add one because the calculations include both the start and end + # nodes + depth = subsumer.max_depth() + 1 + + # Note: No need for an additional add-one correction for non-nouns + # to account for an imaginary root node because that is now + # automatically handled by simulate_root + # if subsumer._pos != NOUN: + # depth += 1 + + # Get the shortest path from the LCS to each of the synsets it is + # subsuming. Add this to the LCS path length to get the path + # length from each synset to the root. + len1 = self.shortest_path_distance( + subsumer, simulate_root=simulate_root and need_root + ) + len2 = other.shortest_path_distance( + subsumer, simulate_root=simulate_root and need_root + ) + if len1 is None or len2 is None: + return None + len1 += depth + len2 += depth + return (2.0 * depth) / (len1 + len2) + + def res_similarity(self, other, ic, verbose=False): + """ + Resnik Similarity: + Return a score denoting how similar two word senses are, based on the + Information Content (IC) of the Least Common Subsumer (most specific + ancestor node). + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type ic: dict + :param ic: an information content object (as returned by + ``nltk.corpus.wordnet_ic.ic()``). + :return: A float score denoting the similarity of the two ``Synset`` + objects. Synsets whose LCS is the root node of the taxonomy will + have a score of 0 (e.g. N['dog'][0] and N['table'][0]). + """ + + ic1, ic2, lcs_ic = _lcs_ic(self, other, ic) + return lcs_ic + + def jcn_similarity(self, other, ic, verbose=False): + """ + Jiang-Conrath Similarity: + Return a score denoting how similar two word senses are, based on the + Information Content (IC) of the Least Common Subsumer (most specific + ancestor node) and that of the two input Synsets. The relationship is + given by the equation 1 / (IC(s1) + IC(s2) - 2 * IC(lcs)). + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type ic: dict + :param ic: an information content object (as returned by + ``nltk.corpus.wordnet_ic.ic()``). + :return: A float score denoting the similarity of the two ``Synset`` + objects. + """ + + if self == other: + return _INF + + ic1, ic2, lcs_ic = _lcs_ic(self, other, ic) + + # If either of the input synsets are the root synset, or have a + # frequency of 0 (sparse data problem), return 0. + if ic1 == 0 or ic2 == 0: + return 0 + + ic_difference = ic1 + ic2 - 2 * lcs_ic + + if ic_difference == 0: + return _INF + + return 1 / ic_difference + + def lin_similarity(self, other, ic, verbose=False): + """ + Lin Similarity: + Return a score denoting how similar two word senses are, based on the + Information Content (IC) of the Least Common Subsumer (most specific + ancestor node) and that of the two input Synsets. The relationship is + given by the equation 2 * IC(lcs) / (IC(s1) + IC(s2)). + + :type other: Synset + :param other: The ``Synset`` that this ``Synset`` is being compared to. + :type ic: dict + :param ic: an information content object (as returned by + ``nltk.corpus.wordnet_ic.ic()``). + :return: A float score denoting the similarity of the two ``Synset`` + objects, in the range 0 to 1. + """ + + ic1, ic2, lcs_ic = _lcs_ic(self, other, ic) + return (2.0 * lcs_ic) / (ic1 + ic2) + + def _iter_hypernym_lists(self): + """ + :return: An iterator over ``Synset`` objects that are either proper + hypernyms or instance of hypernyms of the synset. + """ + todo = [self] + seen = set() + while todo: + for synset in todo: + seen.add(synset) + yield todo + todo = [ + hypernym + for synset in todo + for hypernym in (synset.hypernyms() + synset.instance_hypernyms()) + if hypernym not in seen + ] + + def __repr__(self): + return f"{type(self).__name__}('{self._name}')" + + def _related(self, relation_symbol, sort=True): + get_synset = self._wordnet_corpus_reader.synset_from_pos_and_offset + if relation_symbol not in self._pointers: + return [] + pointer_tuples = self._pointers[relation_symbol] + r = [get_synset(pos, offset) for pos, offset in pointer_tuples] + if sort: + r.sort() + return r + + +###################################################################### +# WordNet Corpus Reader +###################################################################### + + +class WordNetCorpusReader(CorpusReader): + """ + A corpus reader used to access wordnet or its variants. + """ + + _ENCODING = "utf8" + + # { Part-of-speech constants + ADJ, ADJ_SAT, ADV, NOUN, VERB = "a", "s", "r", "n", "v" + # } + + # { Filename constants + _FILEMAP = {ADJ: "adj", ADV: "adv", NOUN: "noun", VERB: "verb"} + # } + + # { Part of speech constants + _pos_numbers = {NOUN: 1, VERB: 2, ADJ: 3, ADV: 4, ADJ_SAT: 5} + _pos_names = dict(tup[::-1] for tup in _pos_numbers.items()) + # } + + #: A list of file identifiers for all the fileids used by this + #: corpus reader. + _FILES = ( + "cntlist.rev", + "lexnames", + "index.sense", + "index.adj", + "index.adv", + "index.noun", + "index.verb", + "data.adj", + "data.adv", + "data.noun", + "data.verb", + "adj.exc", + "adv.exc", + "noun.exc", + "verb.exc", + ) + + def __init__(self, root, omw_reader): + """ + Construct a new wordnet corpus reader, with the given root + directory. + """ + + super().__init__(root, self._FILES, encoding=self._ENCODING) + + # A index that provides the file offset + # Map from lemma -> pos -> synset_index -> offset + self._lemma_pos_offset_map = defaultdict(dict) + + # A cache so we don't have to reconstruct synsets + # Map from pos -> offset -> synset + self._synset_offset_cache = defaultdict(dict) + + # A lookup for the maximum depth of each part of speech. Useful for + # the lch similarity metric. + self._max_depth = defaultdict(dict) + + # Corpus reader containing omw data. + self._omw_reader = omw_reader + + # Corpus reader containing extended_omw data. + self._exomw_reader = None + + self.provenances = defaultdict(str) + self.provenances["eng"] = "" + + if self._omw_reader is None: + warnings.warn( + "The multilingual functions are not available with this Wordnet version" + ) + + self.omw_langs = set() + + # A cache to store the wordnet data of multiple languages + self._lang_data = defaultdict(list) + + self._data_file_map = {} + self._exception_map = {} + self._lexnames = [] + self._key_count_file = None + self._key_synset_file = None + + # Load the lexnames + with self.open("lexnames") as fp: + for i, line in enumerate(fp): + index, lexname, _ = line.split() + assert int(index) == i + self._lexnames.append(lexname) + + # Load the indices for lemmas and synset offsets + self._load_lemma_pos_offset_map() + + # load the exception file data into memory + self._load_exception_map() + + self.nomap = [] + self.splits = {} + + # map from WordNet 3.0 for OMW data + self.map30 = self.map_wn30() + + # Language data attributes + self.lg_attrs = ["lemma", "none", "def", "exe"] + + def index_sense(self, version=None): + """Read sense key to synset id mapping from index.sense file in corpus directory""" + fn = "index.sense" + if version: + from nltk.corpus import CorpusReader, LazyCorpusLoader + + ixreader = LazyCorpusLoader(version, CorpusReader, r".*/" + fn) + else: + ixreader = self + with ixreader.open(fn) as fp: + sensekey_map = {} + for line in fp: + fields = line.strip().split() + sensekey = fields[0] + pos = self._pos_names[int(sensekey.split("%")[1].split(":")[0])] + sensekey_map[sensekey] = f"{fields[1]}-{pos}" + return sensekey_map + + def map_to_many(self): + sensekey_map1 = self.index_sense("wordnet") + sensekey_map2 = self.index_sense() + synset_to_many = {} + for synsetid in set(sensekey_map1.values()): + synset_to_many[synsetid] = [] + for sensekey in set(sensekey_map1.keys()).intersection( + set(sensekey_map2.keys()) + ): + source = sensekey_map1[sensekey] + target = sensekey_map2[sensekey] + synset_to_many[source].append(target) + return synset_to_many + + def map_to_one(self): + synset_to_many = self.map_to_many() + synset_to_one = {} + for source in synset_to_many: + candidates_bag = synset_to_many[source] + if candidates_bag: + candidates_set = set(candidates_bag) + if len(candidates_set) == 1: + target = candidates_bag[0] + else: + counts = [] + for candidate in candidates_set: + counts.append((candidates_bag.count(candidate), candidate)) + self.splits[source] = counts + target = max(counts)[1] + synset_to_one[source] = target + if source[-1] == "s": + # Add a mapping from "a" to target for applications like omw, + # where only Lithuanian and Slovak use the "s" ss_type. + synset_to_one[f"{source[:-1]}a"] = target + else: + self.nomap.append(source) + return synset_to_one + + def map_wn30(self): + """Mapping from Wordnet 3.0 to currently loaded Wordnet version""" + if self.get_version() == "3.0": + return None + else: + return self.map_to_one() + + # Open Multilingual WordNet functions, contributed by + # Nasruddin A’aidil Shari, Sim Wei Ying Geraldine, and Soe Lynn + + def of2ss(self, of): + """take an id and return the synsets""" + return self.synset_from_pos_and_offset(of[-1], int(of[:8])) + + def ss2of(self, ss): + """return the ID of the synset""" + if ss: + return f"{ss.offset():08d}-{ss.pos()}" + + def _load_lang_data(self, lang): + """load the wordnet data of the requested language from the file to + the cache, _lang_data""" + + if lang in self._lang_data: + return + + if self._omw_reader and not self.omw_langs: + self.add_omw() + + if lang not in self.langs(): + raise WordNetError("Language is not supported.") + + if self._exomw_reader and lang not in self.omw_langs: + reader = self._exomw_reader + else: + reader = self._omw_reader + + prov = self.provenances[lang] + if prov in ["cldr", "wikt"]: + prov2 = prov + else: + prov2 = "data" + + with reader.open(f"{prov}/wn-{prov2}-{lang.split('_')[0]}.tab") as fp: + self.custom_lemmas(fp, lang) + self.disable_custom_lemmas(lang) + + def add_provs(self, reader): + """Add languages from Multilingual Wordnet to the provenance dictionary""" + fileids = reader.fileids() + for fileid in fileids: + prov, langfile = os.path.split(fileid) + file_name, file_extension = os.path.splitext(langfile) + if file_extension == ".tab": + lang = file_name.split("-")[-1] + if lang in self.provenances or prov in ["cldr", "wikt"]: + # We already have another resource for this lang, + # so we need to further specify the lang id: + lang = f"{lang}_{prov}" + self.provenances[lang] = prov + + def add_omw(self): + self.add_provs(self._omw_reader) + self.omw_langs = set(self.provenances.keys()) + + def add_exomw(self): + """ + Add languages from Extended OMW + + >>> import nltk + >>> from nltk.corpus import wordnet as wn + >>> wn.add_exomw() + >>> print(wn.synset('intrinsically.r.01').lemmas(lang="eng_wikt")) + [Lemma('intrinsically.r.01.per_se'), Lemma('intrinsically.r.01.as_such')] + """ + from nltk.corpus import extended_omw + + self.add_omw() + self._exomw_reader = extended_omw + self.add_provs(self._exomw_reader) + + def langs(self): + """return a list of languages supported by Multilingual Wordnet""" + return list(self.provenances.keys()) + + def _load_lemma_pos_offset_map(self): + for suffix in self._FILEMAP.values(): + + # parse each line of the file (ignoring comment lines) + with self.open("index.%s" % suffix) as fp: + for i, line in enumerate(fp): + if line.startswith(" "): + continue + + _iter = iter(line.split()) + + def _next_token(): + return next(_iter) + + try: + + # get the lemma and part-of-speech + lemma = _next_token() + pos = _next_token() + + # get the number of synsets for this lemma + n_synsets = int(_next_token()) + assert n_synsets > 0 + + # get and ignore the pointer symbols for all synsets of + # this lemma + n_pointers = int(_next_token()) + [_next_token() for _ in range(n_pointers)] + + # same as number of synsets + n_senses = int(_next_token()) + assert n_synsets == n_senses + + # get and ignore number of senses ranked according to + # frequency + _next_token() + + # get synset offsets + synset_offsets = [int(_next_token()) for _ in range(n_synsets)] + + # raise more informative error with file name and line number + except (AssertionError, ValueError) as e: + tup = ("index.%s" % suffix), (i + 1), e + raise WordNetError("file %s, line %i: %s" % tup) from e + + # map lemmas and parts of speech to synsets + self._lemma_pos_offset_map[lemma][pos] = synset_offsets + if pos == ADJ: + self._lemma_pos_offset_map[lemma][ADJ_SAT] = synset_offsets + + def _load_exception_map(self): + # load the exception file data into memory + for pos, suffix in self._FILEMAP.items(): + self._exception_map[pos] = {} + with self.open("%s.exc" % suffix) as fp: + for line in fp: + terms = line.split() + self._exception_map[pos][terms[0]] = terms[1:] + self._exception_map[ADJ_SAT] = self._exception_map[ADJ] + + def _compute_max_depth(self, pos, simulate_root): + """ + Compute the max depth for the given part of speech. This is + used by the lch similarity metric. + """ + depth = 0 + for ii in self.all_synsets(pos): + try: + depth = max(depth, ii.max_depth()) + except RuntimeError: + print(ii) + if simulate_root: + depth += 1 + self._max_depth[pos] = depth + + def get_version(self): + fh = self._data_file(ADJ) + fh.seek(0) + for line in fh: + match = re.search(r"Word[nN]et (\d+|\d+\.\d+) Copyright", line) + if match is not None: + version = match.group(1) + fh.seek(0) + return version + + ############################################################# + # Loading Lemmas + ############################################################# + + def lemma(self, name, lang="eng"): + """Return lemma object that matches the name""" + # cannot simply split on first '.', + # e.g.: '.45_caliber.a.01..45_caliber' + separator = SENSENUM_RE.search(name).end() + + synset_name, lemma_name = name[: separator - 1], name[separator:] + + synset = self.synset(synset_name) + for lemma in synset.lemmas(lang): + if lemma._name == lemma_name: + return lemma + raise WordNetError(f"No lemma {lemma_name!r} in {synset_name!r}") + + def lemma_from_key(self, key): + # Keys are case sensitive and always lower-case + key = key.lower() + + lemma_name, lex_sense = key.split("%") + pos_number, lexname_index, lex_id, _, _ = lex_sense.split(":") + pos = self._pos_names[int(pos_number)] + + # open the key -> synset file if necessary + if self._key_synset_file is None: + self._key_synset_file = self.open("index.sense") + + # Find the synset for the lemma. + synset_line = _binary_search_file(self._key_synset_file, key) + if not synset_line: + raise WordNetError("No synset found for key %r" % key) + offset = int(synset_line.split()[1]) + synset = self.synset_from_pos_and_offset(pos, offset) + # return the corresponding lemma + for lemma in synset._lemmas: + if lemma._key == key: + return lemma + raise WordNetError("No lemma found for for key %r" % key) + + ############################################################# + # Loading Synsets + ############################################################# + def synset(self, name): + # split name into lemma, part of speech and synset number + lemma, pos, synset_index_str = name.lower().rsplit(".", 2) + synset_index = int(synset_index_str) - 1 + + # get the offset for this synset + try: + offset = self._lemma_pos_offset_map[lemma][pos][synset_index] + except KeyError as e: + raise WordNetError(f"No lemma {lemma!r} with part of speech {pos!r}") from e + except IndexError as e: + n_senses = len(self._lemma_pos_offset_map[lemma][pos]) + raise WordNetError( + f"Lemma {lemma!r} with part of speech {pos!r} only " + f"has {n_senses} {'sense' if n_senses == 1 else 'senses'}" + ) from e + + # load synset information from the appropriate file + synset = self.synset_from_pos_and_offset(pos, offset) + + # some basic sanity checks on loaded attributes + if pos == "s" and synset._pos == "a": + message = ( + "Adjective satellite requested but only plain " + "adjective found for lemma %r" + ) + raise WordNetError(message % lemma) + assert synset._pos == pos or (pos == "a" and synset._pos == "s") + + # Return the synset object. + return synset + + def _data_file(self, pos): + """ + Return an open file pointer for the data file for the given + part of speech. + """ + if pos == ADJ_SAT: + pos = ADJ + if self._data_file_map.get(pos) is None: + fileid = "data.%s" % self._FILEMAP[pos] + self._data_file_map[pos] = self.open(fileid) + return self._data_file_map[pos] + + def synset_from_pos_and_offset(self, pos, offset): + """ + - pos: The synset's part of speech, matching one of the module level + attributes ADJ, ADJ_SAT, ADV, NOUN or VERB ('a', 's', 'r', 'n', or 'v'). + - offset: The byte offset of this synset in the WordNet dict file + for this pos. + + >>> from nltk.corpus import wordnet as wn + >>> print(wn.synset_from_pos_and_offset('n', 1740)) + Synset('entity.n.01') + """ + # Check to see if the synset is in the cache + if offset in self._synset_offset_cache[pos]: + return self._synset_offset_cache[pos][offset] + + data_file = self._data_file(pos) + data_file.seek(offset) + data_file_line = data_file.readline() + # If valid, the offset equals the 8-digit 0-padded integer found at the start of the line: + line_offset = data_file_line[:8] + if ( + line_offset.isalnum() + and line_offset == f"{'0'*(8-len(str(offset)))}{str(offset)}" + ): + synset = self._synset_from_pos_and_line(pos, data_file_line) + assert synset._offset == offset + self._synset_offset_cache[pos][offset] = synset + else: + synset = None + warnings.warn(f"No WordNet synset found for pos={pos} at offset={offset}.") + data_file.seek(0) + return synset + + @deprecated("Use public method synset_from_pos_and_offset() instead") + def _synset_from_pos_and_offset(self, *args, **kwargs): + """ + Hack to help people like the readers of + https://stackoverflow.com/a/27145655/1709587 + who were using this function before it was officially a public method + """ + return self.synset_from_pos_and_offset(*args, **kwargs) + + def _synset_from_pos_and_line(self, pos, data_file_line): + # Construct a new (empty) synset. + synset = Synset(self) + + # parse the entry for this synset + try: + + # parse out the definitions and examples from the gloss + columns_str, gloss = data_file_line.strip().split("|") + definition = re.sub(r"[\"].*?[\"]", "", gloss).strip() + examples = re.findall(r'"([^"]*)"', gloss) + for example in examples: + synset._examples.append(example) + + synset._definition = definition.strip("; ") + + # split the other info into fields + _iter = iter(columns_str.split()) + + def _next_token(): + return next(_iter) + + # get the offset + synset._offset = int(_next_token()) + + # determine the lexicographer file name + lexname_index = int(_next_token()) + synset._lexname = self._lexnames[lexname_index] + + # get the part of speech + synset._pos = _next_token() + + # create Lemma objects for each lemma + n_lemmas = int(_next_token(), 16) + for _ in range(n_lemmas): + # get the lemma name + lemma_name = _next_token() + # get the lex_id (used for sense_keys) + lex_id = int(_next_token(), 16) + # If the lemma has a syntactic marker, extract it. + m = re.match(r"(.*?)(\(.*\))?$", lemma_name) + lemma_name, syn_mark = m.groups() + # create the lemma object + lemma = Lemma(self, synset, lemma_name, lexname_index, lex_id, syn_mark) + synset._lemmas.append(lemma) + synset._lemma_names.append(lemma._name) + + # collect the pointer tuples + n_pointers = int(_next_token()) + for _ in range(n_pointers): + symbol = _next_token() + offset = int(_next_token()) + pos = _next_token() + lemma_ids_str = _next_token() + if lemma_ids_str == "0000": + synset._pointers[symbol].add((pos, offset)) + else: + source_index = int(lemma_ids_str[:2], 16) - 1 + target_index = int(lemma_ids_str[2:], 16) - 1 + source_lemma_name = synset._lemmas[source_index]._name + lemma_pointers = synset._lemma_pointers + tups = lemma_pointers[source_lemma_name, symbol] + tups.append((pos, offset, target_index)) + + # read the verb frames + try: + frame_count = int(_next_token()) + except StopIteration: + pass + else: + for _ in range(frame_count): + # read the plus sign + plus = _next_token() + assert plus == "+" + # read the frame and lemma number + frame_number = int(_next_token()) + frame_string_fmt = VERB_FRAME_STRINGS[frame_number] + lemma_number = int(_next_token(), 16) + # lemma number of 00 means all words in the synset + if lemma_number == 0: + synset._frame_ids.append(frame_number) + for lemma in synset._lemmas: + lemma._frame_ids.append(frame_number) + lemma._frame_strings.append(frame_string_fmt % lemma._name) + # only a specific word in the synset + else: + lemma = synset._lemmas[lemma_number - 1] + lemma._frame_ids.append(frame_number) + lemma._frame_strings.append(frame_string_fmt % lemma._name) + + # raise a more informative error with line text + except ValueError as e: + raise WordNetError(f"line {data_file_line!r}: {e}") from e + + # set sense keys for Lemma objects - note that this has to be + # done afterwards so that the relations are available + for lemma in synset._lemmas: + if synset._pos == ADJ_SAT: + head_lemma = synset.similar_tos()[0]._lemmas[0] + head_name = head_lemma._name + head_id = "%02d" % head_lemma._lex_id + else: + head_name = head_id = "" + tup = ( + lemma._name, + WordNetCorpusReader._pos_numbers[synset._pos], + lemma._lexname_index, + lemma._lex_id, + head_name, + head_id, + ) + lemma._key = ("%s%%%d:%02d:%02d:%s:%s" % tup).lower() + + # the canonical name is based on the first lemma + lemma_name = synset._lemmas[0]._name.lower() + offsets = self._lemma_pos_offset_map[lemma_name][synset._pos] + sense_index = offsets.index(synset._offset) + tup = lemma_name, synset._pos, sense_index + 1 + synset._name = "%s.%s.%02i" % tup + + return synset + + def synset_from_sense_key(self, sense_key): + """ + Retrieves synset based on a given sense_key. Sense keys can be + obtained from lemma.key() + + From https://wordnet.princeton.edu/documentation/senseidx5wn: + A sense_key is represented as:: + + lemma % lex_sense (e.g. 'dog%1:18:01::') + + where lex_sense is encoded as:: + + ss_type:lex_filenum:lex_id:head_word:head_id + + :lemma: ASCII text of word/collocation, in lower case + :ss_type: synset type for the sense (1 digit int) + The synset type is encoded as follows:: + + 1 NOUN + 2 VERB + 3 ADJECTIVE + 4 ADVERB + 5 ADJECTIVE SATELLITE + :lex_filenum: name of lexicographer file containing the synset for the sense (2 digit int) + :lex_id: when paired with lemma, uniquely identifies a sense in the lexicographer file (2 digit int) + :head_word: lemma of the first word in satellite's head synset + Only used if sense is in an adjective satellite synset + :head_id: uniquely identifies sense in a lexicographer file when paired with head_word + Only used if head_word is present (2 digit int) + + >>> import nltk + >>> from nltk.corpus import wordnet as wn + >>> print(wn.synset_from_sense_key("drive%1:04:03::")) + Synset('drive.n.06') + + >>> print(wn.synset_from_sense_key("driving%1:04:03::")) + Synset('drive.n.06') + """ + return self.lemma_from_key(sense_key).synset() + + ############################################################# + # Retrieve synsets and lemmas. + ############################################################# + + def synsets(self, lemma, pos=None, lang="eng", check_exceptions=True): + """Load all synsets with a given lemma and part of speech tag. + If no pos is specified, all synsets for all parts of speech + will be loaded. + If lang is specified, all the synsets associated with the lemma name + of that language will be returned. + """ + lemma = lemma.lower() + + if lang == "eng": + get_synset = self.synset_from_pos_and_offset + index = self._lemma_pos_offset_map + if pos is None: + pos = POS_LIST + return [ + get_synset(p, offset) + for p in pos + for form in self._morphy(lemma, p, check_exceptions) + for offset in index[form].get(p, []) + ] + + else: + self._load_lang_data(lang) + synset_list = [] + if lemma in self._lang_data[lang][1]: + for l in self._lang_data[lang][1][lemma]: + if pos is not None and l[-1] != pos: + continue + synset_list.append(self.of2ss(l)) + return synset_list + + def lemmas(self, lemma, pos=None, lang="eng"): + """Return all Lemma objects with a name matching the specified lemma + name and part of speech tag. Matches any part of speech tag if none is + specified.""" + + lemma = lemma.lower() + if lang == "eng": + return [ + lemma_obj + for synset in self.synsets(lemma, pos) + for lemma_obj in synset.lemmas() + if lemma_obj.name().lower() == lemma + ] + + else: + self._load_lang_data(lang) + lemmas = [] + syn = self.synsets(lemma, lang=lang) + for s in syn: + if pos is not None and s.pos() != pos: + continue + for lemma_obj in s.lemmas(lang=lang): + if lemma_obj.name().lower() == lemma: + lemmas.append(lemma_obj) + return lemmas + + def all_lemma_names(self, pos=None, lang="eng"): + """Return all lemma names for all synsets for the given + part of speech tag and language or languages. If pos is + not specified, all synsets for all parts of speech will + be used.""" + + if lang == "eng": + if pos is None: + return iter(self._lemma_pos_offset_map) + else: + return ( + lemma + for lemma in self._lemma_pos_offset_map + if pos in self._lemma_pos_offset_map[lemma] + ) + else: + self._load_lang_data(lang) + lemma = [] + for i in self._lang_data[lang][0]: + if pos is not None and i[-1] != pos: + continue + lemma.extend(self._lang_data[lang][0][i]) + + lemma = iter(set(lemma)) + return lemma + + def all_omw_synsets(self, pos=None, lang=None): + if lang not in self.langs(): + return None + self._load_lang_data(lang) + for of in self._lang_data[lang][0]: + if not pos or of[-1] == pos: + ss = self.of2ss(of) + if ss: + yield ss + + # else: + # A few OMW offsets don't exist in Wordnet 3.0. + # warnings.warn(f"Language {lang}: no synset found for {of}") + + def all_synsets(self, pos=None, lang="eng"): + """Iterate over all synsets with a given part of speech tag. + If no pos is specified, all synsets for all parts of speech + will be loaded. + """ + if lang == "eng": + return self.all_eng_synsets(pos=pos) + else: + return self.all_omw_synsets(pos=pos, lang=lang) + + def all_eng_synsets(self, pos=None): + if pos is None: + pos_tags = self._FILEMAP.keys() + else: + pos_tags = [pos] + + cache = self._synset_offset_cache + from_pos_and_line = self._synset_from_pos_and_line + + # generate all synsets for each part of speech + for pos_tag in pos_tags: + # Open the file for reading. Note that we can not re-use + # the file pointers from self._data_file_map here, because + # we're defining an iterator, and those file pointers might + # be moved while we're not looking. + if pos_tag == ADJ_SAT: + pos_file = ADJ + else: + pos_file = pos_tag + fileid = "data.%s" % self._FILEMAP[pos_file] + data_file = self.open(fileid) + + try: + # generate synsets for each line in the POS file + offset = data_file.tell() + line = data_file.readline() + while line: + if not line[0].isspace(): + if offset in cache[pos_tag]: + # See if the synset is cached + synset = cache[pos_tag][offset] + else: + # Otherwise, parse the line + synset = from_pos_and_line(pos_tag, line) + cache[pos_tag][offset] = synset + + # adjective satellites are in the same file as + # adjectives so only yield the synset if it's actually + # a satellite + if pos_tag == ADJ_SAT and synset._pos == ADJ_SAT: + yield synset + # for all other POS tags, yield all synsets (this means + # that adjectives also include adjective satellites) + elif pos_tag != ADJ_SAT: + yield synset + offset = data_file.tell() + line = data_file.readline() + + # close the extra file handle we opened + except: + data_file.close() + raise + else: + data_file.close() + + def words(self, lang="eng"): + """return lemmas of the given language as list of words""" + return self.all_lemma_names(lang=lang) + + def synonyms(self, word, lang="eng"): + """return nested list with the synonyms of the different senses of word in the given language""" + return [ + sorted(list(set(ss.lemma_names(lang=lang)) - {word})) + for ss in self.synsets(word, lang=lang) + ] + + def doc(self, file="README", lang="eng"): + """Return the contents of readme, license or citation file + use lang=lang to get the file for an individual language""" + if lang == "eng": + reader = self + else: + reader = self._omw_reader + if lang in self.langs(): + file = f"{os.path.join(self.provenances[lang],file)}" + try: + with reader.open(file) as fp: + return fp.read() + except: + if lang in self._lang_data: + return f"Cannot determine {file} for {lang}" + else: + return f"Language {lang} is not supported." + + def license(self, lang="eng"): + """Return the contents of LICENSE (for omw) + use lang=lang to get the license for an individual language""" + return self.doc(file="LICENSE", lang=lang) + + def readme(self, lang="eng"): + """Return the contents of README (for omw) + use lang=lang to get the readme for an individual language""" + return self.doc(file="README", lang=lang) + + def citation(self, lang="eng"): + """Return the contents of citation.bib file (for omw) + use lang=lang to get the citation for an individual language""" + return self.doc(file="citation.bib", lang=lang) + + ############################################################# + # Misc + ############################################################# + def lemma_count(self, lemma): + """Return the frequency count for this Lemma""" + # Currently, count is only work for English + if lemma._lang != "eng": + return 0 + # open the count file if we haven't already + if self._key_count_file is None: + self._key_count_file = self.open("cntlist.rev") + # find the key in the counts file and return the count + line = _binary_search_file(self._key_count_file, lemma._key) + if line: + return int(line.rsplit(" ", 1)[-1]) + else: + return 0 + + def path_similarity(self, synset1, synset2, verbose=False, simulate_root=True): + return synset1.path_similarity(synset2, verbose, simulate_root) + + path_similarity.__doc__ = Synset.path_similarity.__doc__ + + def lch_similarity(self, synset1, synset2, verbose=False, simulate_root=True): + return synset1.lch_similarity(synset2, verbose, simulate_root) + + lch_similarity.__doc__ = Synset.lch_similarity.__doc__ + + def wup_similarity(self, synset1, synset2, verbose=False, simulate_root=True): + return synset1.wup_similarity(synset2, verbose, simulate_root) + + wup_similarity.__doc__ = Synset.wup_similarity.__doc__ + + def res_similarity(self, synset1, synset2, ic, verbose=False): + return synset1.res_similarity(synset2, ic, verbose) + + res_similarity.__doc__ = Synset.res_similarity.__doc__ + + def jcn_similarity(self, synset1, synset2, ic, verbose=False): + return synset1.jcn_similarity(synset2, ic, verbose) + + jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__ + + def lin_similarity(self, synset1, synset2, ic, verbose=False): + return synset1.lin_similarity(synset2, ic, verbose) + + lin_similarity.__doc__ = Synset.lin_similarity.__doc__ + + ############################################################# + # Morphy + ############################################################# + # Morphy, adapted from Oliver Steele's pywordnet + def morphy(self, form, pos=None, check_exceptions=True): + """ + Find a possible base form for the given form, with the given + part of speech, by checking WordNet's list of exceptional + forms, and by recursively stripping affixes for this part of + speech until a form in WordNet is found. + + >>> from nltk.corpus import wordnet as wn + >>> print(wn.morphy('dogs')) + dog + >>> print(wn.morphy('churches')) + church + >>> print(wn.morphy('aardwolves')) + aardwolf + >>> print(wn.morphy('abaci')) + abacus + >>> wn.morphy('hardrock', wn.ADV) + >>> print(wn.morphy('book', wn.NOUN)) + book + >>> wn.morphy('book', wn.ADJ) + """ + + if pos is None: + morphy = self._morphy + analyses = chain(a for p in POS_LIST for a in morphy(form, p)) + else: + analyses = self._morphy(form, pos, check_exceptions) + + # get the first one we find + first = list(islice(analyses, 1)) + if len(first) == 1: + return first[0] + else: + return None + + MORPHOLOGICAL_SUBSTITUTIONS = { + NOUN: [ + ("s", ""), + ("ses", "s"), + ("ves", "f"), + ("xes", "x"), + ("zes", "z"), + ("ches", "ch"), + ("shes", "sh"), + ("men", "man"), + ("ies", "y"), + ], + VERB: [ + ("s", ""), + ("ies", "y"), + ("es", "e"), + ("es", ""), + ("ed", "e"), + ("ed", ""), + ("ing", "e"), + ("ing", ""), + ], + ADJ: [("er", ""), ("est", ""), ("er", "e"), ("est", "e")], + ADV: [], + } + + MORPHOLOGICAL_SUBSTITUTIONS[ADJ_SAT] = MORPHOLOGICAL_SUBSTITUTIONS[ADJ] + + def _morphy(self, form, pos, check_exceptions=True): + # from jordanbg: + # Given an original string x + # 1. Apply rules once to the input to get y1, y2, y3, etc. + # 2. Return all that are in the database + # 3. If there are no matches, keep applying rules until you either + # find a match or you can't go any further + + exceptions = self._exception_map[pos] + substitutions = self.MORPHOLOGICAL_SUBSTITUTIONS[pos] + + def apply_rules(forms): + return [ + form[: -len(old)] + new + for form in forms + for old, new in substitutions + if form.endswith(old) + ] + + def filter_forms(forms): + result = [] + seen = set() + for form in forms: + if form in self._lemma_pos_offset_map: + if pos in self._lemma_pos_offset_map[form]: + if form not in seen: + result.append(form) + seen.add(form) + return result + + # 0. Check the exception lists + if check_exceptions: + if form in exceptions: + return filter_forms([form] + exceptions[form]) + + # 1. Apply rules once to the input to get y1, y2, y3, etc. + forms = apply_rules([form]) + + # 2. Return all that are in the database (and check the original too) + results = filter_forms([form] + forms) + if results: + return results + + # 3. If there are no matches, keep applying rules until we find a match + while forms: + forms = apply_rules(forms) + results = filter_forms(forms) + if results: + return results + + # Return an empty list if we can't find anything + return [] + + ############################################################# + # Create information content from corpus + ############################################################# + def ic(self, corpus, weight_senses_equally=False, smoothing=1.0): + """ + Creates an information content lookup dictionary from a corpus. + + :type corpus: CorpusReader + :param corpus: The corpus from which we create an information + content dictionary. + :type weight_senses_equally: bool + :param weight_senses_equally: If this is True, gives all + possible senses equal weight rather than dividing by the + number of possible senses. (If a word has 3 synses, each + sense gets 0.3333 per appearance when this is False, 1.0 when + it is true.) + :param smoothing: How much do we smooth synset counts (default is 1.0) + :type smoothing: float + :return: An information content dictionary + """ + counts = FreqDist() + for ww in corpus.words(): + counts[ww] += 1 + + ic = {} + for pp in POS_LIST: + ic[pp] = defaultdict(float) + + # Initialize the counts with the smoothing value + if smoothing > 0.0: + for pp in POS_LIST: + ic[pp][0] = smoothing + for ss in self.all_synsets(): + pos = ss._pos + if pos == ADJ_SAT: + pos = ADJ + ic[pos][ss._offset] = smoothing + + for ww in counts: + possible_synsets = self.synsets(ww) + if len(possible_synsets) == 0: + continue + + # Distribute weight among possible synsets + weight = float(counts[ww]) + if not weight_senses_equally: + weight /= float(len(possible_synsets)) + + for ss in possible_synsets: + pos = ss._pos + if pos == ADJ_SAT: + pos = ADJ + for level in ss._iter_hypernym_lists(): + for hh in level: + ic[pos][hh._offset] += weight + # Add the weight to the root + ic[pos][0] += weight + return ic + + def custom_lemmas(self, tab_file, lang): + """ + Reads a custom tab file containing mappings of lemmas in the given + language to Princeton WordNet 3.0 synset offsets, allowing NLTK's + WordNet functions to then be used with that language. + + See the "Tab files" section at https://omwn.org/omw1.html for + documentation on the Multilingual WordNet tab file format. + + :param tab_file: Tab file as a file or file-like object + :type: lang str + :param: lang ISO 639-3 code of the language of the tab file + """ + lg = lang.split("_")[0] + if len(lg) != 3: + raise ValueError("lang should be a (3 character) ISO 639-3 code") + self._lang_data[lang] = [ + defaultdict(list), + defaultdict(list), + defaultdict(list), + defaultdict(list), + ] + for line in tab_file.readlines(): + if isinstance(line, bytes): + # Support byte-stream files (e.g. as returned by Python 2's + # open() function) as well as text-stream ones + line = line.decode("utf-8") + if not line.startswith("#"): + triple = line.strip().split("\t") + if len(triple) < 3: + continue + offset_pos, label = triple[:2] + val = triple[-1] + if self.map30: + if offset_pos in self.map30: + # Map offset_pos to current Wordnet version: + offset_pos = self.map30[offset_pos] + else: + # Some OMW offsets were never in Wordnet: + if ( + offset_pos not in self.nomap + and offset_pos.replace("a", "s") not in self.nomap + ): + warnings.warn( + f"{lang}: invalid offset {offset_pos} in '{line}'" + ) + continue + elif offset_pos[-1] == "a": + wnss = self.of2ss(offset_pos) + if wnss and wnss.pos() == "s": # Wordnet pos is "s" + # Label OMW adjective satellites back to their Wordnet pos ("s") + offset_pos = self.ss2of(wnss) + pair = label.split(":") + attr = pair[-1] + if len(pair) == 1 or pair[0] == lg: + if attr == "lemma": + val = val.strip().replace(" ", "_") + self._lang_data[lang][1][val.lower()].append(offset_pos) + if attr in self.lg_attrs: + self._lang_data[lang][self.lg_attrs.index(attr)][ + offset_pos + ].append(val) + + def disable_custom_lemmas(self, lang): + """prevent synsets from being mistakenly added""" + for n in range(len(self.lg_attrs)): + self._lang_data[lang][n].default_factory = None + + ###################################################################### + # Visualize WordNet relation graphs using Graphviz + ###################################################################### + + def digraph( + self, + inputs, + rel=lambda s: s.hypernyms(), + pos=None, + maxdepth=-1, + shapes=None, + attr=None, + verbose=False, + ): + """ + Produce a graphical representation from 'inputs' (a list of + start nodes, which can be a mix of Synsets, Lemmas and/or words), + and a synset relation, for drawing with the 'dot' graph visualisation + program from the Graphviz package. + + Return a string in the DOT graph file language, which can then be + converted to an image by nltk.parse.dependencygraph.dot2img(dot_string). + + Optional Parameters: + :rel: Wordnet synset relation + :pos: for words, restricts Part of Speech to 'n', 'v', 'a' or 'r' + :maxdepth: limit the longest path + :shapes: dictionary of strings that trigger a specified shape + :attr: dictionary with global graph attributes + :verbose: warn about cycles + + >>> from nltk.corpus import wordnet as wn + >>> print(wn.digraph([wn.synset('dog.n.01')])) + digraph G { + "Synset('animal.n.01')" -> "Synset('organism.n.01')"; + "Synset('canine.n.02')" -> "Synset('carnivore.n.01')"; + "Synset('carnivore.n.01')" -> "Synset('placental.n.01')"; + "Synset('chordate.n.01')" -> "Synset('animal.n.01')"; + "Synset('dog.n.01')" -> "Synset('canine.n.02')"; + "Synset('dog.n.01')" -> "Synset('domestic_animal.n.01')"; + "Synset('domestic_animal.n.01')" -> "Synset('animal.n.01')"; + "Synset('living_thing.n.01')" -> "Synset('whole.n.02')"; + "Synset('mammal.n.01')" -> "Synset('vertebrate.n.01')"; + "Synset('object.n.01')" -> "Synset('physical_entity.n.01')"; + "Synset('organism.n.01')" -> "Synset('living_thing.n.01')"; + "Synset('physical_entity.n.01')" -> "Synset('entity.n.01')"; + "Synset('placental.n.01')" -> "Synset('mammal.n.01')"; + "Synset('vertebrate.n.01')" -> "Synset('chordate.n.01')"; + "Synset('whole.n.02')" -> "Synset('object.n.01')"; + } + + """ + from nltk.util import edge_closure, edges2dot + + synsets = set() + edges = set() + if not shapes: + shapes = dict() + if not attr: + attr = dict() + + def add_lemma(lem): + ss = lem.synset() + synsets.add(ss) + edges.add((lem, ss)) + + for node in inputs: + typ = type(node) + if typ == Synset: + synsets.add(node) + elif typ == Lemma: + add_lemma(node) + elif typ == str: + for lemma in self.lemmas(node, pos): + add_lemma(lemma) + + for ss in synsets: + edges = edges.union(edge_closure(ss, rel, maxdepth, verbose)) + dot_string = edges2dot(sorted(list(edges)), shapes=shapes, attr=attr) + return dot_string + + +###################################################################### +# WordNet Information Content Corpus Reader +###################################################################### + + +class WordNetICCorpusReader(CorpusReader): + """ + A corpus reader for the WordNet information content corpus. + """ + + def __init__(self, root, fileids): + CorpusReader.__init__(self, root, fileids, encoding="utf8") + + # this load function would be more efficient if the data was pickled + # Note that we can't use NLTK's frequency distributions because + # synsets are overlapping (each instance of a synset also counts + # as an instance of its hypernyms) + def ic(self, icfile): + """ + Load an information content file from the wordnet_ic corpus + and return a dictionary. This dictionary has just two keys, + NOUN and VERB, whose values are dictionaries that map from + synsets to information content values. + + :type icfile: str + :param icfile: The name of the wordnet_ic file (e.g. "ic-brown.dat") + :return: An information content dictionary + """ + ic = {} + ic[NOUN] = defaultdict(float) + ic[VERB] = defaultdict(float) + with self.open(icfile) as fp: + for num, line in enumerate(fp): + if num == 0: # skip the header + continue + fields = line.split() + offset = int(fields[0][:-1]) + value = float(fields[1]) + pos = _get_pos(fields[0]) + if len(fields) == 3 and fields[2] == "ROOT": + # Store root count. + ic[pos][0] += value + if value != 0: + ic[pos][offset] = value + return ic + + +###################################################################### +# Similarity metrics +###################################################################### + +# TODO: Add in the option to manually add a new root node; this will be +# useful for verb similarity as there exist multiple verb taxonomies. + +# More information about the metrics is available at +# http://marimba.d.umn.edu/similarity/measures.html + + +def path_similarity(synset1, synset2, verbose=False, simulate_root=True): + return synset1.path_similarity( + synset2, verbose=verbose, simulate_root=simulate_root + ) + + +def lch_similarity(synset1, synset2, verbose=False, simulate_root=True): + return synset1.lch_similarity(synset2, verbose=verbose, simulate_root=simulate_root) + + +def wup_similarity(synset1, synset2, verbose=False, simulate_root=True): + return synset1.wup_similarity(synset2, verbose=verbose, simulate_root=simulate_root) + + +def res_similarity(synset1, synset2, ic, verbose=False): + return synset1.res_similarity(synset2, ic, verbose=verbose) + + +def jcn_similarity(synset1, synset2, ic, verbose=False): + return synset1.jcn_similarity(synset2, ic, verbose=verbose) + + +def lin_similarity(synset1, synset2, ic, verbose=False): + return synset1.lin_similarity(synset2, ic, verbose=verbose) + + +path_similarity.__doc__ = Synset.path_similarity.__doc__ +lch_similarity.__doc__ = Synset.lch_similarity.__doc__ +wup_similarity.__doc__ = Synset.wup_similarity.__doc__ +res_similarity.__doc__ = Synset.res_similarity.__doc__ +jcn_similarity.__doc__ = Synset.jcn_similarity.__doc__ +lin_similarity.__doc__ = Synset.lin_similarity.__doc__ + + +def _lcs_ic(synset1, synset2, ic, verbose=False): + """ + Get the information content of the least common subsumer that has + the highest information content value. If two nodes have no + explicit common subsumer, assume that they share an artificial + root node that is the hypernym of all explicit roots. + + :type synset1: Synset + :param synset1: First input synset. + :type synset2: Synset + :param synset2: Second input synset. Must be the same part of + speech as the first synset. + :type ic: dict + :param ic: an information content object (as returned by ``load_ic()``). + :return: The information content of the two synsets and their most + informative subsumer + """ + if synset1._pos != synset2._pos: + raise WordNetError( + "Computing the least common subsumer requires " + "%s and %s to have the same part of speech." % (synset1, synset2) + ) + + ic1 = information_content(synset1, ic) + ic2 = information_content(synset2, ic) + subsumers = synset1.common_hypernyms(synset2) + if len(subsumers) == 0: + subsumer_ic = 0 + else: + subsumer_ic = max(information_content(s, ic) for s in subsumers) + + if verbose: + print("> LCS Subsumer by content:", subsumer_ic) + + return ic1, ic2, subsumer_ic + + +# Utility functions + + +def information_content(synset, ic): + pos = synset._pos + if pos == ADJ_SAT: + pos = ADJ + try: + icpos = ic[pos] + except KeyError as e: + msg = "Information content file has no entries for part-of-speech: %s" + raise WordNetError(msg % pos) from e + + counts = icpos[synset._offset] + if counts == 0: + return _INF + else: + return -math.log(counts / icpos[0]) + + +# get the part of speech (NOUN or VERB) from the information content record +# (each identifier has a 'n' or 'v' suffix) + + +def _get_pos(field): + if field[-1] == "n": + return NOUN + elif field[-1] == "v": + return VERB + else: + msg = ( + "Unidentified part of speech in WordNet Information Content file " + "for field %s" % field + ) + raise ValueError(msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/xmldocs.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/xmldocs.py new file mode 100644 index 0000000..05f2363 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/xmldocs.py @@ -0,0 +1,397 @@ +# Natural Language Toolkit: XML Corpus Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for corpora whose documents are xml files. + +(note -- not named 'xml' to avoid conflicting w/ standard xml package) +""" + +import codecs +from xml.etree import ElementTree + +from nltk.corpus.reader.api import CorpusReader +from nltk.corpus.reader.util import * +from nltk.data import SeekableUnicodeStreamReader +from nltk.internals import ElementWrapper +from nltk.tokenize import WordPunctTokenizer + + +class XMLCorpusReader(CorpusReader): + """ + Corpus reader for corpora whose documents are xml files. + + Note that the ``XMLCorpusReader`` constructor does not take an + ``encoding`` argument, because the unicode encoding is specified by + the XML files themselves. See the XML specs for more info. + """ + + def __init__(self, root, fileids, wrap_etree=False): + self._wrap_etree = wrap_etree + CorpusReader.__init__(self, root, fileids) + + def xml(self, fileid=None): + # Make sure we have exactly one file -- no concatenating XML. + if fileid is None and len(self._fileids) == 1: + fileid = self._fileids[0] + if not isinstance(fileid, str): + raise TypeError("Expected a single file identifier string") + # Read the XML in using ElementTree. + with self.abspath(fileid).open() as fp: + elt = ElementTree.parse(fp).getroot() + # If requested, wrap it. + if self._wrap_etree: + elt = ElementWrapper(elt) + # Return the ElementTree element. + return elt + + def words(self, fileid=None): + """ + Returns all of the words and punctuation symbols in the specified file + that were in text nodes -- ie, tags are ignored. Like the xml() method, + fileid can only specify one file. + + :return: the given file's text nodes as a list of words and punctuation symbols + :rtype: list(str) + """ + + elt = self.xml(fileid) + encoding = self.encoding(fileid) + word_tokenizer = WordPunctTokenizer() + try: + iterator = elt.getiterator() + except: + iterator = elt.iter() + out = [] + + for node in iterator: + text = node.text + if text is not None: + if isinstance(text, bytes): + text = text.decode(encoding) + toks = word_tokenizer.tokenize(text) + out.extend(toks) + return out + + +class XMLCorpusView(StreamBackedCorpusView): + """ + A corpus view that selects out specified elements from an XML + file, and provides a flat list-like interface for accessing them. + (Note: ``XMLCorpusView`` is not used by ``XMLCorpusReader`` itself, + but may be used by subclasses of ``XMLCorpusReader``.) + + Every XML corpus view has a "tag specification", indicating what + XML elements should be included in the view; and each (non-nested) + element that matches this specification corresponds to one item in + the view. Tag specifications are regular expressions over tag + paths, where a tag path is a list of element tag names, separated + by '/', indicating the ancestry of the element. Some examples: + + - ``'foo'``: A top-level element whose tag is ``foo``. + - ``'foo/bar'``: An element whose tag is ``bar`` and whose parent + is a top-level element whose tag is ``foo``. + - ``'.*/foo'``: An element whose tag is ``foo``, appearing anywhere + in the xml tree. + - ``'.*/(foo|bar)'``: An wlement whose tag is ``foo`` or ``bar``, + appearing anywhere in the xml tree. + + The view items are generated from the selected XML elements via + the method ``handle_elt()``. By default, this method returns the + element as-is (i.e., as an ElementTree object); but it can be + overridden, either via subclassing or via the ``elt_handler`` + constructor parameter. + """ + + #: If true, then display debugging output to stdout when reading + #: blocks. + _DEBUG = False + + #: The number of characters read at a time by this corpus reader. + _BLOCK_SIZE = 1024 + + def __init__(self, fileid, tagspec, elt_handler=None): + """ + Create a new corpus view based on a specified XML file. + + Note that the ``XMLCorpusView`` constructor does not take an + ``encoding`` argument, because the unicode encoding is + specified by the XML files themselves. + + :type tagspec: str + :param tagspec: A tag specification, indicating what XML + elements should be included in the view. Each non-nested + element that matches this specification corresponds to one + item in the view. + + :param elt_handler: A function used to transform each element + to a value for the view. If no handler is specified, then + ``self.handle_elt()`` is called, which returns the element + as an ElementTree object. The signature of elt_handler is:: + + elt_handler(elt, tagspec) -> value + """ + if elt_handler: + self.handle_elt = elt_handler + + self._tagspec = re.compile(tagspec + r"\Z") + """The tag specification for this corpus view.""" + + self._tag_context = {0: ()} + """A dictionary mapping from file positions (as returned by + ``stream.seek()`` to XML contexts. An XML context is a + tuple of XML tag names, indicating which tags have not yet + been closed.""" + + encoding = self._detect_encoding(fileid) + StreamBackedCorpusView.__init__(self, fileid, encoding=encoding) + + def _detect_encoding(self, fileid): + if isinstance(fileid, PathPointer): + try: + infile = fileid.open() + s = infile.readline() + finally: + infile.close() + else: + with open(fileid, "rb") as infile: + s = infile.readline() + if s.startswith(codecs.BOM_UTF16_BE): + return "utf-16-be" + if s.startswith(codecs.BOM_UTF16_LE): + return "utf-16-le" + if s.startswith(codecs.BOM_UTF32_BE): + return "utf-32-be" + if s.startswith(codecs.BOM_UTF32_LE): + return "utf-32-le" + if s.startswith(codecs.BOM_UTF8): + return "utf-8" + m = re.match(rb'\s*<\?xml\b.*\bencoding="([^"]+)"', s) + if m: + return m.group(1).decode() + m = re.match(rb"\s*<\?xml\b.*\bencoding='([^']+)'", s) + if m: + return m.group(1).decode() + # No encoding found -- what should the default be? + return "utf-8" + + def handle_elt(self, elt, context): + """ + Convert an element into an appropriate value for inclusion in + the view. Unless overridden by a subclass or by the + ``elt_handler`` constructor argument, this method simply + returns ``elt``. + + :return: The view value corresponding to ``elt``. + + :type elt: ElementTree + :param elt: The element that should be converted. + + :type context: str + :param context: A string composed of element tags separated by + forward slashes, indicating the XML context of the given + element. For example, the string ``'foo/bar/baz'`` + indicates that the element is a ``baz`` element whose + parent is a ``bar`` element and whose grandparent is a + top-level ``foo`` element. + """ + return elt + + #: A regular expression that matches XML fragments that do not + #: contain any un-closed tags. + _VALID_XML_RE = re.compile( + r""" + [^<]* + ( + (() | # comment + () | # doctype decl + (<[^!>][^>]*>)) # tag or PI + [^<]*)* + \Z""", + re.DOTALL | re.VERBOSE, + ) + + #: A regular expression used to extract the tag name from a start tag, + #: end tag, or empty-elt tag string. + _XML_TAG_NAME = re.compile(r"<\s*(?:/\s*)?([^\s>]+)") + + #: A regular expression used to find all start-tags, end-tags, and + #: empty-elt tags in an XML file. This regexp is more lenient than + #: the XML spec -- e.g., it allows spaces in some places where the + #: spec does not. + _XML_PIECE = re.compile( + r""" + # Include these so we can skip them: + (?P )| + (?P )| + (?P <\?.*?\?> )| + (?P ]*(\[[^\]]*])?\s*>)| + # These are the ones we actually care about: + (?P <\s*[^>/\?!\s][^>]*/\s*> )| + (?P <\s*[^>/\?!\s][^>]*> )| + (?P <\s*/[^>/\?!\s][^>]*> )""", + re.DOTALL | re.VERBOSE, + ) + + def _read_xml_fragment(self, stream): + """ + Read a string from the given stream that does not contain any + un-closed tags. In particular, this function first reads a + block from the stream of size ``self._BLOCK_SIZE``. It then + checks if that block contains an un-closed tag. If it does, + then this function either backtracks to the last '<', or reads + another block. + """ + fragment = "" + + if isinstance(stream, SeekableUnicodeStreamReader): + startpos = stream.tell() + while True: + # Read a block and add it to the fragment. + xml_block = stream.read(self._BLOCK_SIZE) + fragment += xml_block + + # Do we have a well-formed xml fragment? + if self._VALID_XML_RE.match(fragment): + return fragment + + # Do we have a fragment that will never be well-formed? + if re.search("[<>]", fragment).group(0) == ">": + pos = stream.tell() - ( + len(fragment) - re.search("[<>]", fragment).end() + ) + raise ValueError('Unexpected ">" near char %s' % pos) + + # End of file? + if not xml_block: + raise ValueError("Unexpected end of file: tag not closed") + + # If not, then we must be in the middle of a <..tag..>. + # If appropriate, backtrack to the most recent '<' + # character. + last_open_bracket = fragment.rfind("<") + if last_open_bracket > 0: + if self._VALID_XML_RE.match(fragment[:last_open_bracket]): + if isinstance(stream, SeekableUnicodeStreamReader): + stream.seek(startpos) + stream.char_seek_forward(last_open_bracket) + else: + stream.seek(-(len(fragment) - last_open_bracket), 1) + return fragment[:last_open_bracket] + + # Otherwise, read another block. (i.e., return to the + # top of the loop.) + + def read_block(self, stream, tagspec=None, elt_handler=None): + """ + Read from ``stream`` until we find at least one element that + matches ``tagspec``, and return the result of applying + ``elt_handler`` to each element found. + """ + if tagspec is None: + tagspec = self._tagspec + if elt_handler is None: + elt_handler = self.handle_elt + + # Use a stack of strings to keep track of our context: + context = list(self._tag_context.get(stream.tell())) + assert context is not None # check this -- could it ever happen? + + elts = [] + + elt_start = None # where does the elt start + elt_depth = None # what context depth + elt_text = "" + + while elts == [] or elt_start is not None: + if isinstance(stream, SeekableUnicodeStreamReader): + startpos = stream.tell() + xml_fragment = self._read_xml_fragment(stream) + + # End of file. + if not xml_fragment: + if elt_start is None: + break + else: + raise ValueError("Unexpected end of file") + + # Process each in the xml fragment. + for piece in self._XML_PIECE.finditer(xml_fragment): + if self._DEBUG: + print("{:>25} {}".format("/".join(context)[-20:], piece.group())) + + if piece.group("START_TAG"): + name = self._XML_TAG_NAME.match(piece.group()).group(1) + # Keep context up-to-date. + context.append(name) + # Is this one of the elts we're looking for? + if elt_start is None: + if re.match(tagspec, "/".join(context)): + elt_start = piece.start() + elt_depth = len(context) + + elif piece.group("END_TAG"): + name = self._XML_TAG_NAME.match(piece.group()).group(1) + # sanity checks: + if not context: + raise ValueError("Unmatched tag " % name) + if name != context[-1]: + raise ValueError(f"Unmatched tag <{context[-1]}>...") + # Is this the end of an element? + if elt_start is not None and elt_depth == len(context): + elt_text += xml_fragment[elt_start : piece.end()] + elts.append((elt_text, "/".join(context))) + elt_start = elt_depth = None + elt_text = "" + # Keep context up-to-date + context.pop() + + elif piece.group("EMPTY_ELT_TAG"): + name = self._XML_TAG_NAME.match(piece.group()).group(1) + if elt_start is None: + if re.match(tagspec, "/".join(context) + "/" + name): + elts.append((piece.group(), "/".join(context) + "/" + name)) + + if elt_start is not None: + # If we haven't found any elements yet, then keep + # looping until we do. + if elts == []: + elt_text += xml_fragment[elt_start:] + elt_start = 0 + + # If we've found at least one element, then try + # backtracking to the start of the element that we're + # inside of. + else: + # take back the last start-tag, and return what + # we've gotten so far (elts is non-empty). + if self._DEBUG: + print(" " * 36 + "(backtrack)") + if isinstance(stream, SeekableUnicodeStreamReader): + stream.seek(startpos) + stream.char_seek_forward(elt_start) + else: + stream.seek(-(len(xml_fragment) - elt_start), 1) + context = context[: elt_depth - 1] + elt_start = elt_depth = None + elt_text = "" + + # Update the _tag_context dict. + pos = stream.tell() + if pos in self._tag_context: + assert tuple(context) == self._tag_context[pos] + else: + self._tag_context[pos] = tuple(context) + + return [ + elt_handler( + ElementTree.fromstring(elt.encode("ascii", "xmlcharrefreplace")), + context, + ) + for (elt, context) in elts + ] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ycoe.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ycoe.py new file mode 100644 index 0000000..db39538 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/reader/ycoe.py @@ -0,0 +1,256 @@ +# Natural Language Toolkit: York-Toronto-Helsinki Parsed Corpus of Old English Prose (YCOE) +# +# Copyright (C) 2001-2015 NLTK Project +# Author: Selina Dennis +# URL: +# For license information, see LICENSE.TXT + +""" +Corpus reader for the York-Toronto-Helsinki Parsed Corpus of Old +English Prose (YCOE), a 1.5 million word syntactically-annotated +corpus of Old English prose texts. The corpus is distributed by the +Oxford Text Archive: http://www.ota.ahds.ac.uk/ It is not included +with NLTK. + +The YCOE corpus is divided into 100 files, each representing +an Old English prose text. Tags used within each text complies +to the YCOE standard: https://www-users.york.ac.uk/~lang22/YCOE/YcoeHome.htm +""" + +import os +import re + +from nltk.corpus.reader.api import * +from nltk.corpus.reader.bracket_parse import BracketParseCorpusReader +from nltk.corpus.reader.tagged import TaggedCorpusReader +from nltk.corpus.reader.util import * +from nltk.tokenize import RegexpTokenizer + + +class YCOECorpusReader(CorpusReader): + """ + Corpus reader for the York-Toronto-Helsinki Parsed Corpus of Old + English Prose (YCOE), a 1.5 million word syntactically-annotated + corpus of Old English prose texts. + """ + + def __init__(self, root, encoding="utf8"): + CorpusReader.__init__(self, root, [], encoding) + + self._psd_reader = YCOEParseCorpusReader( + self.root.join("psd"), ".*", ".psd", encoding=encoding + ) + self._pos_reader = YCOETaggedCorpusReader(self.root.join("pos"), ".*", ".pos") + + # Make sure we have a consistent set of items: + documents = {f[:-4] for f in self._psd_reader.fileids()} + if {f[:-4] for f in self._pos_reader.fileids()} != documents: + raise ValueError('Items in "psd" and "pos" ' "subdirectories do not match.") + + fileids = sorted( + ["%s.psd" % doc for doc in documents] + + ["%s.pos" % doc for doc in documents] + ) + CorpusReader.__init__(self, root, fileids, encoding) + self._documents = sorted(documents) + + def documents(self, fileids=None): + """ + Return a list of document identifiers for all documents in + this corpus, or for the documents with the given file(s) if + specified. + """ + if fileids is None: + return self._documents + if isinstance(fileids, str): + fileids = [fileids] + for f in fileids: + if f not in self._fileids: + raise KeyError("File id %s not found" % fileids) + # Strip off the '.pos' and '.psd' extensions. + return sorted({f[:-4] for f in fileids}) + + def fileids(self, documents=None): + """ + Return a list of file identifiers for the files that make up + this corpus, or that store the given document(s) if specified. + """ + if documents is None: + return self._fileids + elif isinstance(documents, str): + documents = [documents] + return sorted( + set( + ["%s.pos" % doc for doc in documents] + + ["%s.psd" % doc for doc in documents] + ) + ) + + def _getfileids(self, documents, subcorpus): + """ + Helper that selects the appropriate fileids for a given set of + documents from a given subcorpus (pos or psd). + """ + if documents is None: + documents = self._documents + else: + if isinstance(documents, str): + documents = [documents] + for document in documents: + if document not in self._documents: + if document[-4:] in (".pos", ".psd"): + raise ValueError( + "Expected a document identifier, not a file " + "identifier. (Use corpus.documents() to get " + "a list of document identifiers." + ) + else: + raise ValueError("Document identifier %s not found" % document) + return [f"{d}.{subcorpus}" for d in documents] + + # Delegate to one of our two sub-readers: + def words(self, documents=None): + return self._pos_reader.words(self._getfileids(documents, "pos")) + + def sents(self, documents=None): + return self._pos_reader.sents(self._getfileids(documents, "pos")) + + def paras(self, documents=None): + return self._pos_reader.paras(self._getfileids(documents, "pos")) + + def tagged_words(self, documents=None): + return self._pos_reader.tagged_words(self._getfileids(documents, "pos")) + + def tagged_sents(self, documents=None): + return self._pos_reader.tagged_sents(self._getfileids(documents, "pos")) + + def tagged_paras(self, documents=None): + return self._pos_reader.tagged_paras(self._getfileids(documents, "pos")) + + def parsed_sents(self, documents=None): + return self._psd_reader.parsed_sents(self._getfileids(documents, "psd")) + + +class YCOEParseCorpusReader(BracketParseCorpusReader): + """Specialized version of the standard bracket parse corpus reader + that strips out (CODE ...) and (ID ...) nodes.""" + + def _parse(self, t): + t = re.sub(r"(?u)\((CODE|ID)[^\)]*\)", "", t) + if re.match(r"\s*\(\s*\)\s*$", t): + return None + return BracketParseCorpusReader._parse(self, t) + + +class YCOETaggedCorpusReader(TaggedCorpusReader): + def __init__(self, root, items, encoding="utf8"): + gaps_re = r"(?u)(?<=/\.)\s+|\s*\S*_CODE\s*|\s*\S*_ID\s*" + sent_tokenizer = RegexpTokenizer(gaps_re, gaps=True) + TaggedCorpusReader.__init__( + self, root, items, sep="_", sent_tokenizer=sent_tokenizer + ) + + +#: A list of all documents and their titles in ycoe. +documents = { + "coadrian.o34": "Adrian and Ritheus", + "coaelhom.o3": "Ælfric, Supplemental Homilies", + "coaelive.o3": "Ælfric's Lives of Saints", + "coalcuin": "Alcuin De virtutibus et vitiis", + "coalex.o23": "Alexander's Letter to Aristotle", + "coapollo.o3": "Apollonius of Tyre", + "coaugust": "Augustine", + "cobede.o2": "Bede's History of the English Church", + "cobenrul.o3": "Benedictine Rule", + "coblick.o23": "Blickling Homilies", + "coboeth.o2": "Boethius' Consolation of Philosophy", + "cobyrhtf.o3": "Byrhtferth's Manual", + "cocanedgD": "Canons of Edgar (D)", + "cocanedgX": "Canons of Edgar (X)", + "cocathom1.o3": "Ælfric's Catholic Homilies I", + "cocathom2.o3": "Ælfric's Catholic Homilies II", + "cochad.o24": "Saint Chad", + "cochdrul": "Chrodegang of Metz, Rule", + "cochristoph": "Saint Christopher", + "cochronA.o23": "Anglo-Saxon Chronicle A", + "cochronC": "Anglo-Saxon Chronicle C", + "cochronD": "Anglo-Saxon Chronicle D", + "cochronE.o34": "Anglo-Saxon Chronicle E", + "cocura.o2": "Cura Pastoralis", + "cocuraC": "Cura Pastoralis (Cotton)", + "codicts.o34": "Dicts of Cato", + "codocu1.o1": "Documents 1 (O1)", + "codocu2.o12": "Documents 2 (O1/O2)", + "codocu2.o2": "Documents 2 (O2)", + "codocu3.o23": "Documents 3 (O2/O3)", + "codocu3.o3": "Documents 3 (O3)", + "codocu4.o24": "Documents 4 (O2/O4)", + "coeluc1": "Honorius of Autun, Elucidarium 1", + "coeluc2": "Honorius of Autun, Elucidarium 1", + "coepigen.o3": "Ælfric's Epilogue to Genesis", + "coeuphr": "Saint Euphrosyne", + "coeust": "Saint Eustace and his companions", + "coexodusP": "Exodus (P)", + "cogenesiC": "Genesis (C)", + "cogregdC.o24": "Gregory's Dialogues (C)", + "cogregdH.o23": "Gregory's Dialogues (H)", + "coherbar": "Pseudo-Apuleius, Herbarium", + "coinspolD.o34": "Wulfstan's Institute of Polity (D)", + "coinspolX": "Wulfstan's Institute of Polity (X)", + "cojames": "Saint James", + "colacnu.o23": "Lacnunga", + "colaece.o2": "Leechdoms", + "colaw1cn.o3": "Laws, Cnut I", + "colaw2cn.o3": "Laws, Cnut II", + "colaw5atr.o3": "Laws, Æthelred V", + "colaw6atr.o3": "Laws, Æthelred VI", + "colawaf.o2": "Laws, Alfred", + "colawafint.o2": "Alfred's Introduction to Laws", + "colawger.o34": "Laws, Gerefa", + "colawine.ox2": "Laws, Ine", + "colawnorthu.o3": "Northumbra Preosta Lagu", + "colawwllad.o4": "Laws, William I, Lad", + "coleofri.o4": "Leofric", + "colsigef.o3": "Ælfric's Letter to Sigefyrth", + "colsigewB": "Ælfric's Letter to Sigeweard (B)", + "colsigewZ.o34": "Ælfric's Letter to Sigeweard (Z)", + "colwgeat": "Ælfric's Letter to Wulfgeat", + "colwsigeT": "Ælfric's Letter to Wulfsige (T)", + "colwsigeXa.o34": "Ælfric's Letter to Wulfsige (Xa)", + "colwstan1.o3": "Ælfric's Letter to Wulfstan I", + "colwstan2.o3": "Ælfric's Letter to Wulfstan II", + "comargaC.o34": "Saint Margaret (C)", + "comargaT": "Saint Margaret (T)", + "comart1": "Martyrology, I", + "comart2": "Martyrology, II", + "comart3.o23": "Martyrology, III", + "comarvel.o23": "Marvels of the East", + "comary": "Mary of Egypt", + "coneot": "Saint Neot", + "conicodA": "Gospel of Nicodemus (A)", + "conicodC": "Gospel of Nicodemus (C)", + "conicodD": "Gospel of Nicodemus (D)", + "conicodE": "Gospel of Nicodemus (E)", + "coorosiu.o2": "Orosius", + "cootest.o3": "Heptateuch", + "coprefcath1.o3": "Ælfric's Preface to Catholic Homilies I", + "coprefcath2.o3": "Ælfric's Preface to Catholic Homilies II", + "coprefcura.o2": "Preface to the Cura Pastoralis", + "coprefgen.o3": "Ælfric's Preface to Genesis", + "copreflives.o3": "Ælfric's Preface to Lives of Saints", + "coprefsolilo": "Preface to Augustine's Soliloquies", + "coquadru.o23": "Pseudo-Apuleius, Medicina de quadrupedibus", + "corood": "History of the Holy Rood-Tree", + "cosevensl": "Seven Sleepers", + "cosolilo": "St. Augustine's Soliloquies", + "cosolsat1.o4": "Solomon and Saturn I", + "cosolsat2": "Solomon and Saturn II", + "cotempo.o3": "Ælfric's De Temporibus Anni", + "coverhom": "Vercelli Homilies", + "coverhomE": "Vercelli Homilies (E)", + "coverhomL": "Vercelli Homilies (L)", + "covinceB": "Saint Vincent (Bodley 343)", + "covinsal": "Vindicta Salvatoris", + "cowsgosp.o3": "West-Saxon Gospels", + "cowulf.o34": "Wulfstan's Homilies", +} diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/util.py new file mode 100644 index 0000000..9c5ef54 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/corpus/util.py @@ -0,0 +1,154 @@ +# Natural Language Toolkit: Corpus Reader Utility Functions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +###################################################################### +# { Lazy Corpus Loader +###################################################################### + +import gc +import re + +import nltk + +TRY_ZIPFILE_FIRST = False + + +class LazyCorpusLoader: + """ + To see the API documentation for this lazily loaded corpus, first + run corpus.ensure_loaded(), and then run help(this_corpus). + + LazyCorpusLoader is a proxy object which is used to stand in for a + corpus object before the corpus is loaded. This allows NLTK to + create an object for each corpus, but defer the costs associated + with loading those corpora until the first time that they're + actually accessed. + + The first time this object is accessed in any way, it will load + the corresponding corpus, and transform itself into that corpus + (by modifying its own ``__class__`` and ``__dict__`` attributes). + + If the corpus can not be found, then accessing this object will + raise an exception, displaying installation instructions for the + NLTK data package. Once they've properly installed the data + package (or modified ``nltk.data.path`` to point to its location), + they can then use the corpus object without restarting python. + + :param name: The name of the corpus + :type name: str + :param reader_cls: The specific CorpusReader class, e.g. PlaintextCorpusReader, WordListCorpusReader + :type reader: nltk.corpus.reader.api.CorpusReader + :param nltk_data_subdir: The subdirectory where the corpus is stored. + :type nltk_data_subdir: str + :param `*args`: Any other non-keywords arguments that `reader_cls` might need. + :param `**kwargs`: Any other keywords arguments that `reader_cls` might need. + """ + + def __init__(self, name, reader_cls, *args, **kwargs): + from nltk.corpus.reader.api import CorpusReader + + assert issubclass(reader_cls, CorpusReader) + self.__name = self.__name__ = name + self.__reader_cls = reader_cls + # If nltk_data_subdir is set explicitly + if "nltk_data_subdir" in kwargs: + # Use the specified subdirectory path + self.subdir = kwargs["nltk_data_subdir"] + # Pops the `nltk_data_subdir` argument, we don't need it anymore. + kwargs.pop("nltk_data_subdir", None) + else: # Otherwise use 'nltk_data/corpora' + self.subdir = "corpora" + self.__args = args + self.__kwargs = kwargs + + def __load(self): + # Find the corpus root directory. + zip_name = re.sub(r"(([^/]+)(/.*)?)", r"\2.zip/\1/", self.__name) + if TRY_ZIPFILE_FIRST: + try: + root = nltk.data.find(f"{self.subdir}/{zip_name}") + except LookupError as e: + try: + root = nltk.data.find(f"{self.subdir}/{self.__name}") + except LookupError: + raise e + else: + try: + root = nltk.data.find(f"{self.subdir}/{self.__name}") + except LookupError as e: + try: + root = nltk.data.find(f"{self.subdir}/{zip_name}") + except LookupError: + raise e + + # Load the corpus. + corpus = self.__reader_cls(root, *self.__args, **self.__kwargs) + + # This is where the magic happens! Transform ourselves into + # the corpus by modifying our own __dict__ and __class__ to + # match that of the corpus. + + args, kwargs = self.__args, self.__kwargs + name, reader_cls = self.__name, self.__reader_cls + + self.__dict__ = corpus.__dict__ + self.__class__ = corpus.__class__ + + # _unload support: assign __dict__ and __class__ back, then do GC. + # after reassigning __dict__ there shouldn't be any references to + # corpus data so the memory should be deallocated after gc.collect() + def _unload(self): + lazy_reader = LazyCorpusLoader(name, reader_cls, *args, **kwargs) + self.__dict__ = lazy_reader.__dict__ + self.__class__ = lazy_reader.__class__ + gc.collect() + + self._unload = _make_bound_method(_unload, self) + + def __getattr__(self, attr): + + # Fix for inspect.isclass under Python 2.6 + # (see https://bugs.python.org/issue1225107). + # Without this fix tests may take extra 1.5GB RAM + # because all corpora gets loaded during test collection. + if attr == "__bases__": + raise AttributeError("LazyCorpusLoader object has no attribute '__bases__'") + + self.__load() + # This looks circular, but its not, since __load() changes our + # __class__ to something new: + return getattr(self, attr) + + def __repr__(self): + return "<{} in {!r} (not loaded yet)>".format( + self.__reader_cls.__name__, + ".../corpora/" + self.__name, + ) + + def _unload(self): + # If an exception occurs during corpus loading then + # '_unload' method may be unattached, so __getattr__ can be called; + # we shouldn't trigger corpus loading again in this case. + pass + + +def _make_bound_method(func, self): + """ + Magic for creating bound methods (used for _unload). + """ + + class Foo: + def meth(self): + pass + + f = Foo() + bound_method = type(f.meth) + + try: + return bound_method(func, self, self.__class__) + except TypeError: # python3 + return bound_method(func, self) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/data.py b/.pythonlibs/lib/python3.10/site-packages/nltk/data.py new file mode 100644 index 0000000..67b9338 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/data.py @@ -0,0 +1,1441 @@ +# Natural Language Toolkit: Utility functions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Functions to find and load NLTK resource files, such as corpora, +grammars, and saved processing objects. Resource files are identified +using URLs, such as ``nltk:corpora/abc/rural.txt`` or +``https://raw.githubusercontent.com/nltk/nltk/develop/nltk/test/toy.cfg``. +The following URL protocols are supported: + + - ``file:path``: Specifies the file whose path is *path*. + Both relative and absolute paths may be used. + + - ``https://host/path``: Specifies the file stored on the web + server *host* at path *path*. + + - ``nltk:path``: Specifies the file stored in the NLTK data + package at *path*. NLTK will search for these files in the + directories specified by ``nltk.data.path``. + +If no protocol is specified, then the default protocol ``nltk:`` will +be used. + +This module provides to functions that can be used to access a +resource file, given its URL: ``load()`` loads a given resource, and +adds it to a resource cache; and ``retrieve()`` copies a given resource +to a local file. +""" + +import codecs +import functools +import os +import pickle +import re +import sys +import textwrap +import zipfile +from abc import ABCMeta, abstractmethod +from gzip import WRITE as GZ_WRITE +from gzip import GzipFile +from io import BytesIO, TextIOWrapper +from urllib.request import url2pathname, urlopen + +try: + from zlib import Z_SYNC_FLUSH as FLUSH +except ImportError: + from zlib import Z_FINISH as FLUSH + +from nltk import grammar, sem +from nltk.compat import add_py3_data, py3_data +from nltk.internals import deprecated + +textwrap_indent = functools.partial(textwrap.indent, prefix=" ") + +###################################################################### +# Search Path +###################################################################### + +path = [] +"""A list of directories where the NLTK data package might reside. + These directories will be checked in order when looking for a + resource in the data package. Note that this allows users to + substitute in their own versions of resources, if they have them + (e.g., in their home directory under ~/nltk_data).""" + +# User-specified locations: +_paths_from_env = os.environ.get("NLTK_DATA", "").split(os.pathsep) +path += [d for d in _paths_from_env if d] +if "APPENGINE_RUNTIME" not in os.environ and os.path.expanduser("~/") != "~/": + path.append(os.path.expanduser("~/nltk_data")) + +if sys.platform.startswith("win"): + # Common locations on Windows: + path += [ + os.path.join(sys.prefix, "nltk_data"), + os.path.join(sys.prefix, "share", "nltk_data"), + os.path.join(sys.prefix, "lib", "nltk_data"), + os.path.join(os.environ.get("APPDATA", "C:\\"), "nltk_data"), + r"C:\nltk_data", + r"D:\nltk_data", + r"E:\nltk_data", + ] +else: + # Common locations on UNIX & OS X: + path += [ + os.path.join(sys.prefix, "nltk_data"), + os.path.join(sys.prefix, "share", "nltk_data"), + os.path.join(sys.prefix, "lib", "nltk_data"), + "/usr/share/nltk_data", + "/usr/local/share/nltk_data", + "/usr/lib/nltk_data", + "/usr/local/lib/nltk_data", + ] + + +###################################################################### +# Util Functions +###################################################################### + + +def gzip_open_unicode( + filename, + mode="rb", + compresslevel=9, + encoding="utf-8", + fileobj=None, + errors=None, + newline=None, +): + if fileobj is None: + fileobj = GzipFile(filename, mode, compresslevel, fileobj) + return TextIOWrapper(fileobj, encoding, errors, newline) + + +def split_resource_url(resource_url): + """ + Splits a resource url into ":". + + >>> windows = sys.platform.startswith('win') + >>> split_resource_url('nltk:home/nltk') + ('nltk', 'home/nltk') + >>> split_resource_url('nltk:/home/nltk') + ('nltk', '/home/nltk') + >>> split_resource_url('file:/home/nltk') + ('file', '/home/nltk') + >>> split_resource_url('file:///home/nltk') + ('file', '/home/nltk') + >>> split_resource_url('file:///C:/home/nltk') + ('file', '/C:/home/nltk') + """ + protocol, path_ = resource_url.split(":", 1) + if protocol == "nltk": + pass + elif protocol == "file": + if path_.startswith("/"): + path_ = "/" + path_.lstrip("/") + else: + path_ = re.sub(r"^/{0,2}", "", path_) + return protocol, path_ + + +def normalize_resource_url(resource_url): + r""" + Normalizes a resource url + + >>> windows = sys.platform.startswith('win') + >>> os.path.normpath(split_resource_url(normalize_resource_url('file:grammar.fcfg'))[1]) == \ + ... ('\\' if windows else '') + os.path.abspath(os.path.join(os.curdir, 'grammar.fcfg')) + True + >>> not windows or normalize_resource_url('file:C:/dir/file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('file:C:\\dir\\file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('file:C:\\dir/file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('file://C:/dir/file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('file:////C:/dir/file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('nltk:C:/dir/file') == 'file:///C:/dir/file' + True + >>> not windows or normalize_resource_url('nltk:C:\\dir\\file') == 'file:///C:/dir/file' + True + >>> windows or normalize_resource_url('file:/dir/file/toy.cfg') == 'file:///dir/file/toy.cfg' + True + >>> normalize_resource_url('nltk:home/nltk') + 'nltk:home/nltk' + >>> windows or normalize_resource_url('nltk:/home/nltk') == 'file:///home/nltk' + True + >>> normalize_resource_url('https://example.com/dir/file') + 'https://example.com/dir/file' + >>> normalize_resource_url('dir/file') + 'nltk:dir/file' + """ + try: + protocol, name = split_resource_url(resource_url) + except ValueError: + # the resource url has no protocol, use the nltk protocol by default + protocol = "nltk" + name = resource_url + # use file protocol if the path is an absolute path + if protocol == "nltk" and os.path.isabs(name): + protocol = "file://" + name = normalize_resource_name(name, False, None) + elif protocol == "file": + protocol = "file://" + # name is absolute + name = normalize_resource_name(name, False, None) + elif protocol == "nltk": + protocol = "nltk:" + name = normalize_resource_name(name, True) + else: + # handled by urllib + protocol += "://" + return "".join([protocol, name]) + + +def normalize_resource_name(resource_name, allow_relative=True, relative_path=None): + """ + :type resource_name: str or unicode + :param resource_name: The name of the resource to search for. + Resource names are posix-style relative path names, such as + ``corpora/brown``. Directory names will automatically + be converted to a platform-appropriate path separator. + Directory trailing slashes are preserved + + >>> windows = sys.platform.startswith('win') + >>> normalize_resource_name('.', True) + './' + >>> normalize_resource_name('./', True) + './' + >>> windows or normalize_resource_name('dir/file', False, '/') == '/dir/file' + True + >>> not windows or normalize_resource_name('C:/file', False, '/') == '/C:/file' + True + >>> windows or normalize_resource_name('/dir/file', False, '/') == '/dir/file' + True + >>> windows or normalize_resource_name('../dir/file', False, '/') == '/dir/file' + True + >>> not windows or normalize_resource_name('/dir/file', True, '/') == 'dir/file' + True + >>> windows or normalize_resource_name('/dir/file', True, '/') == '/dir/file' + True + """ + is_dir = bool(re.search(r"[\\/.]$", resource_name)) or resource_name.endswith( + os.path.sep + ) + if sys.platform.startswith("win"): + resource_name = resource_name.lstrip("/") + else: + resource_name = re.sub(r"^/+", "/", resource_name) + if allow_relative: + resource_name = os.path.normpath(resource_name) + else: + if relative_path is None: + relative_path = os.curdir + resource_name = os.path.abspath(os.path.join(relative_path, resource_name)) + resource_name = resource_name.replace("\\", "/").replace(os.path.sep, "/") + if sys.platform.startswith("win") and os.path.isabs(resource_name): + resource_name = "/" + resource_name + if is_dir and not resource_name.endswith("/"): + resource_name += "/" + return resource_name + + +###################################################################### +# Path Pointers +###################################################################### + + +class PathPointer(metaclass=ABCMeta): + """ + An abstract base class for 'path pointers,' used by NLTK's data + package to identify specific paths. Two subclasses exist: + ``FileSystemPathPointer`` identifies a file that can be accessed + directly via a given absolute path. ``ZipFilePathPointer`` + identifies a file contained within a zipfile, that can be accessed + by reading that zipfile. + """ + + @abstractmethod + def open(self, encoding=None): + """ + Return a seekable read-only stream that can be used to read + the contents of the file identified by this path pointer. + + :raise IOError: If the path specified by this pointer does + not contain a readable file. + """ + + @abstractmethod + def file_size(self): + """ + Return the size of the file pointed to by this path pointer, + in bytes. + + :raise IOError: If the path specified by this pointer does + not contain a readable file. + """ + + @abstractmethod + def join(self, fileid): + """ + Return a new path pointer formed by starting at the path + identified by this pointer, and then following the relative + path given by ``fileid``. The path components of ``fileid`` + should be separated by forward slashes, regardless of + the underlying file system's path separator character. + """ + + +class FileSystemPathPointer(PathPointer, str): + """ + A path pointer that identifies a file which can be accessed + directly via a given absolute path. + """ + + @py3_data + def __init__(self, _path): + """ + Create a new path pointer for the given absolute path. + + :raise IOError: If the given path does not exist. + """ + + _path = os.path.abspath(_path) + if not os.path.exists(_path): + raise OSError("No such file or directory: %r" % _path) + self._path = _path + + # There's no need to call str.__init__(), since it's a no-op; + # str does all of its setup work in __new__. + + @property + def path(self): + """The absolute path identified by this path pointer.""" + return self._path + + def open(self, encoding=None): + stream = open(self._path, "rb") + if encoding is not None: + stream = SeekableUnicodeStreamReader(stream, encoding) + return stream + + def file_size(self): + return os.stat(self._path).st_size + + def join(self, fileid): + _path = os.path.join(self._path, fileid) + return FileSystemPathPointer(_path) + + def __repr__(self): + return "FileSystemPathPointer(%r)" % self._path + + def __str__(self): + return self._path + + +@deprecated("Use gzip.GzipFile instead as it also uses a buffer.") +class BufferedGzipFile(GzipFile): + """A ``GzipFile`` subclass for compatibility with older nltk releases. + + Use ``GzipFile`` directly as it also buffers in all supported + Python versions. + """ + + @py3_data + def __init__( + self, filename=None, mode=None, compresslevel=9, fileobj=None, **kwargs + ): + """Return a buffered gzip file object.""" + GzipFile.__init__(self, filename, mode, compresslevel, fileobj) + + def write(self, data): + # This is identical to GzipFile.write but does not return + # the bytes written to retain compatibility. + super().write(data) + + +class GzipFileSystemPathPointer(FileSystemPathPointer): + """ + A subclass of ``FileSystemPathPointer`` that identifies a gzip-compressed + file located at a given absolute path. ``GzipFileSystemPathPointer`` is + appropriate for loading large gzip-compressed pickle objects efficiently. + """ + + def open(self, encoding=None): + stream = GzipFile(self._path, "rb") + if encoding: + stream = SeekableUnicodeStreamReader(stream, encoding) + return stream + + +class ZipFilePathPointer(PathPointer): + """ + A path pointer that identifies a file contained within a zipfile, + which can be accessed by reading that zipfile. + """ + + @py3_data + def __init__(self, zipfile, entry=""): + """ + Create a new path pointer pointing at the specified entry + in the given zipfile. + + :raise IOError: If the given zipfile does not exist, or if it + does not contain the specified entry. + """ + if isinstance(zipfile, str): + zipfile = OpenOnDemandZipFile(os.path.abspath(zipfile)) + + # Check that the entry exists: + if entry: + + # Normalize the entry string, it should be relative: + entry = normalize_resource_name(entry, True, "/").lstrip("/") + + try: + zipfile.getinfo(entry) + except Exception as e: + # Sometimes directories aren't explicitly listed in + # the zip file. So if `entry` is a directory name, + # then check if the zipfile contains any files that + # are under the given directory. + if entry.endswith("/") and [ + n for n in zipfile.namelist() if n.startswith(entry) + ]: + pass # zipfile contains a file in that directory. + else: + # Otherwise, complain. + raise OSError( + f"Zipfile {zipfile.filename!r} does not contain {entry!r}" + ) from e + self._zipfile = zipfile + self._entry = entry + + @property + def zipfile(self): + """ + The zipfile.ZipFile object used to access the zip file + containing the entry identified by this path pointer. + """ + return self._zipfile + + @property + def entry(self): + """ + The name of the file within zipfile that this path + pointer points to. + """ + return self._entry + + def open(self, encoding=None): + data = self._zipfile.read(self._entry) + stream = BytesIO(data) + if self._entry.endswith(".gz"): + stream = GzipFile(self._entry, fileobj=stream) + elif encoding is not None: + stream = SeekableUnicodeStreamReader(stream, encoding) + return stream + + def file_size(self): + return self._zipfile.getinfo(self._entry).file_size + + def join(self, fileid): + entry = f"{self._entry}/{fileid}" + return ZipFilePathPointer(self._zipfile, entry) + + def __repr__(self): + return f"ZipFilePathPointer({self._zipfile.filename!r}, {self._entry!r})" + + def __str__(self): + return os.path.normpath(os.path.join(self._zipfile.filename, self._entry)) + + +###################################################################### +# Access Functions +###################################################################### + +# Don't use a weak dictionary, because in the common case this +# causes a lot more reloading that necessary. +_resource_cache = {} +"""A dictionary used to cache resources so that they won't + need to be loaded more than once.""" + + +def find(resource_name, paths=None): + """ + Find the given resource by searching through the directories and + zip files in paths, where a None or empty string specifies an absolute path. + Returns a corresponding path name. If the given resource is not + found, raise a ``LookupError``, whose message gives a pointer to + the installation instructions for the NLTK downloader. + + Zip File Handling: + + - If ``resource_name`` contains a component with a ``.zip`` + extension, then it is assumed to be a zipfile; and the + remaining path components are used to look inside the zipfile. + + - If any element of ``nltk.data.path`` has a ``.zip`` extension, + then it is assumed to be a zipfile. + + - If a given resource name that does not contain any zipfile + component is not found initially, then ``find()`` will make a + second attempt to find that resource, by replacing each + component *p* in the path with *p.zip/p*. For example, this + allows ``find()`` to map the resource name + ``corpora/chat80/cities.pl`` to a zip file path pointer to + ``corpora/chat80.zip/chat80/cities.pl``. + + - When using ``find()`` to locate a directory contained in a + zipfile, the resource name must end with the forward slash + character. Otherwise, ``find()`` will not locate the + directory. + + :type resource_name: str or unicode + :param resource_name: The name of the resource to search for. + Resource names are posix-style relative path names, such as + ``corpora/brown``. Directory names will be + automatically converted to a platform-appropriate path separator. + :rtype: str + """ + resource_name = normalize_resource_name(resource_name, True) + + # Resolve default paths at runtime in-case the user overrides + # nltk.data.path + if paths is None: + paths = path + + # Check if the resource name includes a zipfile name + m = re.match(r"(.*\.zip)/?(.*)$|", resource_name) + zipfile, zipentry = m.groups() + + # Check each item in our path + for path_ in paths: + # Is the path item a zipfile? + if path_ and (os.path.isfile(path_) and path_.endswith(".zip")): + try: + return ZipFilePathPointer(path_, resource_name) + except OSError: + # resource not in zipfile + continue + + # Is the path item a directory or is resource_name an absolute path? + elif not path_ or os.path.isdir(path_): + if zipfile is None: + p = os.path.join(path_, url2pathname(resource_name)) + if os.path.exists(p): + if p.endswith(".gz"): + return GzipFileSystemPathPointer(p) + else: + return FileSystemPathPointer(p) + else: + p = os.path.join(path_, url2pathname(zipfile)) + if os.path.exists(p): + try: + return ZipFilePathPointer(p, zipentry) + except OSError: + # resource not in zipfile + continue + + # Fallback: if the path doesn't include a zip file, then try + # again, assuming that one of the path components is inside a + # zipfile of the same name. + if zipfile is None: + pieces = resource_name.split("/") + for i in range(len(pieces)): + modified_name = "/".join(pieces[:i] + [pieces[i] + ".zip"] + pieces[i:]) + try: + return find(modified_name, paths) + except LookupError: + pass + + # Identify the package (i.e. the .zip file) to download. + resource_zipname = resource_name.split("/")[1] + if resource_zipname.endswith(".zip"): + resource_zipname = resource_zipname.rpartition(".")[0] + # Display a friendly error message if the resource wasn't found: + msg = str( + "Resource \33[93m{resource}\033[0m not found.\n" + "Please use the NLTK Downloader to obtain the resource:\n\n" + "\33[31m" # To display red text in terminal. + ">>> import nltk\n" + ">>> nltk.download('{resource}')\n" + "\033[0m" + ).format(resource=resource_zipname) + msg = textwrap_indent(msg) + + msg += "\n For more information see: https://www.nltk.org/data.html\n" + + msg += "\n Attempted to load \33[93m{resource_name}\033[0m\n".format( + resource_name=resource_name + ) + + msg += "\n Searched in:" + "".join("\n - %r" % d for d in paths) + sep = "*" * 70 + resource_not_found = f"\n{sep}\n{msg}\n{sep}\n" + raise LookupError(resource_not_found) + + +def retrieve(resource_url, filename=None, verbose=True): + """ + Copy the given resource to a local file. If no filename is + specified, then use the URL's filename. If there is already a + file named ``filename``, then raise a ``ValueError``. + + :type resource_url: str + :param resource_url: A URL specifying where the resource should be + loaded from. The default protocol is "nltk:", which searches + for the file in the the NLTK data package. + """ + resource_url = normalize_resource_url(resource_url) + if filename is None: + if resource_url.startswith("file:"): + filename = os.path.split(resource_url)[-1] + else: + filename = re.sub(r"(^\w+:)?.*/", "", resource_url) + if os.path.exists(filename): + filename = os.path.abspath(filename) + raise ValueError("File %r already exists!" % filename) + + if verbose: + print(f"Retrieving {resource_url!r}, saving to {filename!r}") + + # Open the input & output streams. + infile = _open(resource_url) + + # Copy infile -> outfile, using 64k blocks. + with open(filename, "wb") as outfile: + while True: + s = infile.read(1024 * 64) # 64k blocks. + outfile.write(s) + if not s: + break + + infile.close() + + +#: A dictionary describing the formats that are supported by NLTK's +#: load() method. Keys are format names, and values are format +#: descriptions. +FORMATS = { + "pickle": "A serialized python object, stored using the pickle module.", + "json": "A serialized python object, stored using the json module.", + "yaml": "A serialized python object, stored using the yaml module.", + "cfg": "A context free grammar.", + "pcfg": "A probabilistic CFG.", + "fcfg": "A feature CFG.", + "fol": "A list of first order logic expressions, parsed with " + "nltk.sem.logic.Expression.fromstring.", + "logic": "A list of first order logic expressions, parsed with " + "nltk.sem.logic.LogicParser. Requires an additional logic_parser " + "parameter", + "val": "A semantic valuation, parsed by nltk.sem.Valuation.fromstring.", + "raw": "The raw (byte string) contents of a file.", + "text": "The raw (unicode string) contents of a file. ", +} + +#: A dictionary mapping from file extensions to format names, used +#: by load() when format="auto" to decide the format for a +#: given resource url. +AUTO_FORMATS = { + "pickle": "pickle", + "json": "json", + "yaml": "yaml", + "cfg": "cfg", + "pcfg": "pcfg", + "fcfg": "fcfg", + "fol": "fol", + "logic": "logic", + "val": "val", + "txt": "text", + "text": "text", +} + + +def load( + resource_url, + format="auto", + cache=True, + verbose=False, + logic_parser=None, + fstruct_reader=None, + encoding=None, +): + """ + Load a given resource from the NLTK data package. The following + resource formats are currently supported: + + - ``pickle`` + - ``json`` + - ``yaml`` + - ``cfg`` (context free grammars) + - ``pcfg`` (probabilistic CFGs) + - ``fcfg`` (feature-based CFGs) + - ``fol`` (formulas of First Order Logic) + - ``logic`` (Logical formulas to be parsed by the given logic_parser) + - ``val`` (valuation of First Order Logic model) + - ``text`` (the file contents as a unicode string) + - ``raw`` (the raw file contents as a byte string) + + If no format is specified, ``load()`` will attempt to determine a + format based on the resource name's file extension. If that + fails, ``load()`` will raise a ``ValueError`` exception. + + For all text formats (everything except ``pickle``, ``json``, ``yaml`` and ``raw``), + it tries to decode the raw contents using UTF-8, and if that doesn't + work, it tries with ISO-8859-1 (Latin-1), unless the ``encoding`` + is specified. + + :type resource_url: str + :param resource_url: A URL specifying where the resource should be + loaded from. The default protocol is "nltk:", which searches + for the file in the the NLTK data package. + :type cache: bool + :param cache: If true, add this resource to a cache. If load() + finds a resource in its cache, then it will return it from the + cache rather than loading it. + :type verbose: bool + :param verbose: If true, print a message when loading a resource. + Messages are not displayed when a resource is retrieved from + the cache. + :type logic_parser: LogicParser + :param logic_parser: The parser that will be used to parse logical + expressions. + :type fstruct_reader: FeatStructReader + :param fstruct_reader: The parser that will be used to parse the + feature structure of an fcfg. + :type encoding: str + :param encoding: the encoding of the input; only used for text formats. + """ + resource_url = normalize_resource_url(resource_url) + resource_url = add_py3_data(resource_url) + + # Determine the format of the resource. + if format == "auto": + resource_url_parts = resource_url.split(".") + ext = resource_url_parts[-1] + if ext == "gz": + ext = resource_url_parts[-2] + format = AUTO_FORMATS.get(ext) + if format is None: + raise ValueError( + "Could not determine format for %s based " + 'on its file\nextension; use the "format" ' + "argument to specify the format explicitly." % resource_url + ) + + if format not in FORMATS: + raise ValueError(f"Unknown format type: {format}!") + + # If we've cached the resource, then just return it. + if cache: + resource_val = _resource_cache.get((resource_url, format)) + if resource_val is not None: + if verbose: + print(f"<>") + return resource_val + + # Let the user know what's going on. + if verbose: + print(f"<>") + + # Load the resource. + opened_resource = _open(resource_url) + + if format == "raw": + resource_val = opened_resource.read() + elif format == "pickle": + resource_val = pickle.load(opened_resource) + elif format == "json": + import json + + from nltk.jsontags import json_tags + + resource_val = json.load(opened_resource) + tag = None + if len(resource_val) != 1: + tag = next(resource_val.keys()) + if tag not in json_tags: + raise ValueError("Unknown json tag.") + elif format == "yaml": + import yaml + + resource_val = yaml.safe_load(opened_resource) + else: + # The resource is a text format. + binary_data = opened_resource.read() + if encoding is not None: + string_data = binary_data.decode(encoding) + else: + try: + string_data = binary_data.decode("utf-8") + except UnicodeDecodeError: + string_data = binary_data.decode("latin-1") + if format == "text": + resource_val = string_data + elif format == "cfg": + resource_val = grammar.CFG.fromstring(string_data, encoding=encoding) + elif format == "pcfg": + resource_val = grammar.PCFG.fromstring(string_data, encoding=encoding) + elif format == "fcfg": + resource_val = grammar.FeatureGrammar.fromstring( + string_data, + logic_parser=logic_parser, + fstruct_reader=fstruct_reader, + encoding=encoding, + ) + elif format == "fol": + resource_val = sem.read_logic( + string_data, + logic_parser=sem.logic.LogicParser(), + encoding=encoding, + ) + elif format == "logic": + resource_val = sem.read_logic( + string_data, logic_parser=logic_parser, encoding=encoding + ) + elif format == "val": + resource_val = sem.read_valuation(string_data, encoding=encoding) + else: + raise AssertionError( + "Internal NLTK error: Format %s isn't " + "handled by nltk.data.load()" % (format,) + ) + + opened_resource.close() + + # If requested, add it to the cache. + if cache: + try: + _resource_cache[(resource_url, format)] = resource_val + # TODO: add this line + # print('<>' % (resource_url,)) + except TypeError: + # We can't create weak references to some object types, like + # strings and tuples. For now, just don't cache them. + pass + + return resource_val + + +def show_cfg(resource_url, escape="##"): + """ + Write out a grammar file, ignoring escaped and empty lines. + + :type resource_url: str + :param resource_url: A URL specifying where the resource should be + loaded from. The default protocol is "nltk:", which searches + for the file in the the NLTK data package. + :type escape: str + :param escape: Prepended string that signals lines to be ignored + """ + resource_url = normalize_resource_url(resource_url) + resource_val = load(resource_url, format="text", cache=False) + lines = resource_val.splitlines() + for l in lines: + if l.startswith(escape): + continue + if re.match("^$", l): + continue + print(l) + + +def clear_cache(): + """ + Remove all objects from the resource cache. + :see: load() + """ + _resource_cache.clear() + + +def _open(resource_url): + """ + Helper function that returns an open file object for a resource, + given its resource URL. If the given resource URL uses the "nltk:" + protocol, or uses no protocol, then use ``nltk.data.find`` to find + its path, and open it with the given mode; if the resource URL + uses the 'file' protocol, then open the file with the given mode; + otherwise, delegate to ``urllib2.urlopen``. + + :type resource_url: str + :param resource_url: A URL specifying where the resource should be + loaded from. The default protocol is "nltk:", which searches + for the file in the the NLTK data package. + """ + resource_url = normalize_resource_url(resource_url) + protocol, path_ = split_resource_url(resource_url) + + if protocol is None or protocol.lower() == "nltk": + return find(path_, path + [""]).open() + elif protocol.lower() == "file": + # urllib might not use mode='rb', so handle this one ourselves: + return find(path_, [""]).open() + else: + return urlopen(resource_url) + + +###################################################################### +# Lazy Resource Loader +###################################################################### + + +class LazyLoader: + @py3_data + def __init__(self, _path): + self._path = _path + + def __load(self): + resource = load(self._path) + # This is where the magic happens! Transform ourselves into + # the object by modifying our own __dict__ and __class__ to + # match that of `resource`. + self.__dict__ = resource.__dict__ + self.__class__ = resource.__class__ + + def __getattr__(self, attr): + self.__load() + # This looks circular, but its not, since __load() changes our + # __class__ to something new: + return getattr(self, attr) + + def __repr__(self): + self.__load() + # This looks circular, but its not, since __load() changes our + # __class__ to something new: + return repr(self) + + +###################################################################### +# Open-On-Demand ZipFile +###################################################################### + + +class OpenOnDemandZipFile(zipfile.ZipFile): + """ + A subclass of ``zipfile.ZipFile`` that closes its file pointer + whenever it is not using it; and re-opens it when it needs to read + data from the zipfile. This is useful for reducing the number of + open file handles when many zip files are being accessed at once. + ``OpenOnDemandZipFile`` must be constructed from a filename, not a + file-like object (to allow re-opening). ``OpenOnDemandZipFile`` is + read-only (i.e. ``write()`` and ``writestr()`` are disabled. + """ + + @py3_data + def __init__(self, filename): + if not isinstance(filename, str): + raise TypeError("ReopenableZipFile filename must be a string") + zipfile.ZipFile.__init__(self, filename) + assert self.filename == filename + self.close() + # After closing a ZipFile object, the _fileRefCnt needs to be cleared + # for Python2and3 compatible code. + self._fileRefCnt = 0 + + def read(self, name): + assert self.fp is None + self.fp = open(self.filename, "rb") + value = zipfile.ZipFile.read(self, name) + # Ensure that _fileRefCnt needs to be set for Python2and3 compatible code. + # Since we only opened one file here, we add 1. + self._fileRefCnt += 1 + self.close() + return value + + def write(self, *args, **kwargs): + """:raise NotImplementedError: OpenOnDemandZipfile is read-only""" + raise NotImplementedError("OpenOnDemandZipfile is read-only") + + def writestr(self, *args, **kwargs): + """:raise NotImplementedError: OpenOnDemandZipfile is read-only""" + raise NotImplementedError("OpenOnDemandZipfile is read-only") + + def __repr__(self): + return repr("OpenOnDemandZipFile(%r)" % self.filename) + + +###################################################################### +# Seekable Unicode Stream Reader +###################################################################### + + +class SeekableUnicodeStreamReader: + """ + A stream reader that automatically encodes the source byte stream + into unicode (like ``codecs.StreamReader``); but still supports the + ``seek()`` and ``tell()`` operations correctly. This is in contrast + to ``codecs.StreamReader``, which provide *broken* ``seek()`` and + ``tell()`` methods. + + This class was motivated by ``StreamBackedCorpusView``, which + makes extensive use of ``seek()`` and ``tell()``, and needs to be + able to handle unicode-encoded files. + + Note: this class requires stateless decoders. To my knowledge, + this shouldn't cause a problem with any of python's builtin + unicode encodings. + """ + + DEBUG = True # : If true, then perform extra sanity checks. + + @py3_data + def __init__(self, stream, encoding, errors="strict"): + # Rewind the stream to its beginning. + stream.seek(0) + + self.stream = stream + """The underlying stream.""" + + self.encoding = encoding + """The name of the encoding that should be used to encode the + underlying stream.""" + + self.errors = errors + """The error mode that should be used when decoding data from + the underlying stream. Can be 'strict', 'ignore', or + 'replace'.""" + + self.decode = codecs.getdecoder(encoding) + """The function that is used to decode byte strings into + unicode strings.""" + + self.bytebuffer = b"" + """A buffer to use bytes that have been read but have not yet + been decoded. This is only used when the final bytes from + a read do not form a complete encoding for a character.""" + + self.linebuffer = None + """A buffer used by ``readline()`` to hold characters that have + been read, but have not yet been returned by ``read()`` or + ``readline()``. This buffer consists of a list of unicode + strings, where each string corresponds to a single line. + The final element of the list may or may not be a complete + line. Note that the existence of a linebuffer makes the + ``tell()`` operation more complex, because it must backtrack + to the beginning of the buffer to determine the correct + file position in the underlying byte stream.""" + + self._rewind_checkpoint = 0 + """The file position at which the most recent read on the + underlying stream began. This is used, together with + ``_rewind_numchars``, to backtrack to the beginning of + ``linebuffer`` (which is required by ``tell()``).""" + + self._rewind_numchars = None + """The number of characters that have been returned since the + read that started at ``_rewind_checkpoint``. This is used, + together with ``_rewind_checkpoint``, to backtrack to the + beginning of ``linebuffer`` (which is required by ``tell()``).""" + + self._bom = self._check_bom() + """The length of the byte order marker at the beginning of + the stream (or None for no byte order marker).""" + + # ///////////////////////////////////////////////////////////////// + # Read methods + # ///////////////////////////////////////////////////////////////// + + def read(self, size=None): + """ + Read up to ``size`` bytes, decode them using this reader's + encoding, and return the resulting unicode string. + + :param size: The maximum number of bytes to read. If not + specified, then read as many bytes as possible. + :type size: int + :rtype: unicode + """ + chars = self._read(size) + + # If linebuffer is not empty, then include it in the result + if self.linebuffer: + chars = "".join(self.linebuffer) + chars + self.linebuffer = None + self._rewind_numchars = None + + return chars + + def discard_line(self): + if self.linebuffer and len(self.linebuffer) > 1: + line = self.linebuffer.pop(0) + self._rewind_numchars += len(line) + else: + self.stream.readline() + + def readline(self, size=None): + """ + Read a line of text, decode it using this reader's encoding, + and return the resulting unicode string. + + :param size: The maximum number of bytes to read. If no + newline is encountered before ``size`` bytes have been read, + then the returned value may not be a complete line of text. + :type size: int + """ + # If we have a non-empty linebuffer, then return the first + # line from it. (Note that the last element of linebuffer may + # not be a complete line; so let _read() deal with it.) + if self.linebuffer and len(self.linebuffer) > 1: + line = self.linebuffer.pop(0) + self._rewind_numchars += len(line) + return line + + readsize = size or 72 + chars = "" + + # If there's a remaining incomplete line in the buffer, add it. + if self.linebuffer: + chars += self.linebuffer.pop() + self.linebuffer = None + + while True: + startpos = self.stream.tell() - len(self.bytebuffer) + new_chars = self._read(readsize) + + # If we're at a '\r', then read one extra character, since + # it might be a '\n', to get the proper line ending. + if new_chars and new_chars.endswith("\r"): + new_chars += self._read(1) + + chars += new_chars + lines = chars.splitlines(True) + if len(lines) > 1: + line = lines[0] + self.linebuffer = lines[1:] + self._rewind_numchars = len(new_chars) - (len(chars) - len(line)) + self._rewind_checkpoint = startpos + break + elif len(lines) == 1: + line0withend = lines[0] + line0withoutend = lines[0].splitlines(False)[0] + if line0withend != line0withoutend: # complete line + line = line0withend + break + + if not new_chars or size is not None: + line = chars + break + + # Read successively larger blocks of text. + if readsize < 8000: + readsize *= 2 + + return line + + def readlines(self, sizehint=None, keepends=True): + """ + Read this file's contents, decode them using this reader's + encoding, and return it as a list of unicode lines. + + :rtype: list(unicode) + :param sizehint: Ignored. + :param keepends: If false, then strip newlines. + """ + return self.read().splitlines(keepends) + + def next(self): + """Return the next decoded line from the underlying stream.""" + line = self.readline() + if line: + return line + else: + raise StopIteration + + def __next__(self): + return self.next() + + def __iter__(self): + """Return self""" + return self + + def __del__(self): + # let garbage collector deal with still opened streams + if not self.closed: + self.close() + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def xreadlines(self): + """Return self""" + return self + + # ///////////////////////////////////////////////////////////////// + # Pass-through methods & properties + # ///////////////////////////////////////////////////////////////// + + @property + def closed(self): + """True if the underlying stream is closed.""" + return self.stream.closed + + @property + def name(self): + """The name of the underlying stream.""" + return self.stream.name + + @property + def mode(self): + """The mode of the underlying stream.""" + return self.stream.mode + + def close(self): + """ + Close the underlying stream. + """ + self.stream.close() + + # ///////////////////////////////////////////////////////////////// + # Seek and tell + # ///////////////////////////////////////////////////////////////// + + def seek(self, offset, whence=0): + """ + Move the stream to a new file position. If the reader is + maintaining any buffers, then they will be cleared. + + :param offset: A byte count offset. + :param whence: If 0, then the offset is from the start of the file + (offset should be positive), if 1, then the offset is from the + current position (offset may be positive or negative); and if 2, + then the offset is from the end of the file (offset should + typically be negative). + """ + if whence == 1: + raise ValueError( + "Relative seek is not supported for " + "SeekableUnicodeStreamReader -- consider " + "using char_seek_forward() instead." + ) + self.stream.seek(offset, whence) + self.linebuffer = None + self.bytebuffer = b"" + self._rewind_numchars = None + self._rewind_checkpoint = self.stream.tell() + + def char_seek_forward(self, offset): + """ + Move the read pointer forward by ``offset`` characters. + """ + if offset < 0: + raise ValueError("Negative offsets are not supported") + # Clear all buffers. + self.seek(self.tell()) + # Perform the seek operation. + self._char_seek_forward(offset) + + def _char_seek_forward(self, offset, est_bytes=None): + """ + Move the file position forward by ``offset`` characters, + ignoring all buffers. + + :param est_bytes: A hint, giving an estimate of the number of + bytes that will be needed to move forward by ``offset`` chars. + Defaults to ``offset``. + """ + if est_bytes is None: + est_bytes = offset + bytes = b"" + + while True: + # Read in a block of bytes. + newbytes = self.stream.read(est_bytes - len(bytes)) + bytes += newbytes + + # Decode the bytes to characters. + chars, bytes_decoded = self._incr_decode(bytes) + + # If we got the right number of characters, then seek + # backwards over any truncated characters, and return. + if len(chars) == offset: + self.stream.seek(-len(bytes) + bytes_decoded, 1) + return + + # If we went too far, then we can back-up until we get it + # right, using the bytes we've already read. + if len(chars) > offset: + while len(chars) > offset: + # Assume at least one byte/char. + est_bytes += offset - len(chars) + chars, bytes_decoded = self._incr_decode(bytes[:est_bytes]) + self.stream.seek(-len(bytes) + bytes_decoded, 1) + return + + # Otherwise, we haven't read enough bytes yet; loop again. + est_bytes += offset - len(chars) + + def tell(self): + """ + Return the current file position on the underlying byte + stream. If this reader is maintaining any buffers, then the + returned file position will be the position of the beginning + of those buffers. + """ + # If nothing's buffered, then just return our current filepos: + if self.linebuffer is None: + return self.stream.tell() - len(self.bytebuffer) + + # Otherwise, we'll need to backtrack the filepos until we + # reach the beginning of the buffer. + + # Store our original file position, so we can return here. + orig_filepos = self.stream.tell() + + # Calculate an estimate of where we think the newline is. + bytes_read = (orig_filepos - len(self.bytebuffer)) - self._rewind_checkpoint + buf_size = sum(len(line) for line in self.linebuffer) + est_bytes = int( + bytes_read * self._rewind_numchars / (self._rewind_numchars + buf_size) + ) + + self.stream.seek(self._rewind_checkpoint) + self._char_seek_forward(self._rewind_numchars, est_bytes) + filepos = self.stream.tell() + + # Sanity check + if self.DEBUG: + self.stream.seek(filepos) + check1 = self._incr_decode(self.stream.read(50))[0] + check2 = "".join(self.linebuffer) + assert check1.startswith(check2) or check2.startswith(check1) + + # Return to our original filepos (so we don't have to throw + # out our buffer.) + self.stream.seek(orig_filepos) + + # Return the calculated filepos + return filepos + + # ///////////////////////////////////////////////////////////////// + # Helper methods + # ///////////////////////////////////////////////////////////////// + + def _read(self, size=None): + """ + Read up to ``size`` bytes from the underlying stream, decode + them using this reader's encoding, and return the resulting + unicode string. ``linebuffer`` is not included in the result. + """ + if size == 0: + return "" + + # Skip past the byte order marker, if present. + if self._bom and self.stream.tell() == 0: + self.stream.read(self._bom) + + # Read the requested number of bytes. + if size is None: + new_bytes = self.stream.read() + else: + new_bytes = self.stream.read(size) + bytes = self.bytebuffer + new_bytes + + # Decode the bytes into unicode characters + chars, bytes_decoded = self._incr_decode(bytes) + + # If we got bytes but couldn't decode any, then read further. + if (size is not None) and (not chars) and (len(new_bytes) > 0): + while not chars: + new_bytes = self.stream.read(1) + if not new_bytes: + break # end of file. + bytes += new_bytes + chars, bytes_decoded = self._incr_decode(bytes) + + # Record any bytes we didn't consume. + self.bytebuffer = bytes[bytes_decoded:] + + # Return the result + return chars + + def _incr_decode(self, bytes): + """ + Decode the given byte string into a unicode string, using this + reader's encoding. If an exception is encountered that + appears to be caused by a truncation error, then just decode + the byte string without the bytes that cause the trunctaion + error. + + Return a tuple ``(chars, num_consumed)``, where ``chars`` is + the decoded unicode string, and ``num_consumed`` is the + number of bytes that were consumed. + """ + while True: + try: + return self.decode(bytes, "strict") + except UnicodeDecodeError as exc: + # If the exception occurs at the end of the string, + # then assume that it's a truncation error. + if exc.end == len(bytes): + return self.decode(bytes[: exc.start], self.errors) + + # Otherwise, if we're being strict, then raise it. + elif self.errors == "strict": + raise + + # If we're not strict, then re-process it with our + # errors setting. This *may* raise an exception. + else: + return self.decode(bytes, self.errors) + + _BOM_TABLE = { + "utf8": [(codecs.BOM_UTF8, None)], + "utf16": [(codecs.BOM_UTF16_LE, "utf16-le"), (codecs.BOM_UTF16_BE, "utf16-be")], + "utf16le": [(codecs.BOM_UTF16_LE, None)], + "utf16be": [(codecs.BOM_UTF16_BE, None)], + "utf32": [(codecs.BOM_UTF32_LE, "utf32-le"), (codecs.BOM_UTF32_BE, "utf32-be")], + "utf32le": [(codecs.BOM_UTF32_LE, None)], + "utf32be": [(codecs.BOM_UTF32_BE, None)], + } + + def _check_bom(self): + # Normalize our encoding name + enc = re.sub("[ -]", "", self.encoding.lower()) + + # Look up our encoding in the BOM table. + bom_info = self._BOM_TABLE.get(enc) + + if bom_info: + # Read a prefix, to check against the BOM(s) + bytes = self.stream.read(16) + self.stream.seek(0) + + # Check for each possible BOM. + for (bom, new_encoding) in bom_info: + if bytes.startswith(bom): + if new_encoding: + self.encoding = new_encoding + return len(bom) + + return None + + +__all__ = [ + "path", + "PathPointer", + "FileSystemPathPointer", + "BufferedGzipFile", + "GzipFileSystemPathPointer", + "GzipFileSystemPathPointer", + "find", + "retrieve", + "FORMATS", + "AUTO_FORMATS", + "load", + "show_cfg", + "clear_cache", + "LazyLoader", + "OpenOnDemandZipFile", + "GzipFileSystemPathPointer", + "SeekableUnicodeStreamReader", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/decorators.py b/.pythonlibs/lib/python3.10/site-packages/nltk/decorators.py new file mode 100644 index 0000000..d64fe0e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/decorators.py @@ -0,0 +1,251 @@ +""" +Decorator module by Michele Simionato +Copyright Michele Simionato, distributed under the terms of the BSD License (see below). +http://www.phyast.pitt.edu/~micheles/python/documentation.html + +Included in NLTK for its support of a nice memoization decorator. +""" + +__docformat__ = "restructuredtext en" + +## The basic trick is to generate the source code for the decorated function +## with the right signature and to evaluate it. +## Uncomment the statement 'print >> sys.stderr, func_src' in _decorator +## to understand what is going on. + +__all__ = ["decorator", "new_wrapper", "getinfo"] + +import sys + +# Hack to keep NLTK's "tokenize" module from colliding with the "tokenize" in +# the Python standard library. +OLD_SYS_PATH = sys.path[:] +sys.path = [p for p in sys.path if p and "nltk" not in str(p)] +import inspect + +sys.path = OLD_SYS_PATH + + +def __legacysignature(signature): + """ + For retrocompatibility reasons, we don't use a standard Signature. + Instead, we use the string generated by this method. + Basically, from a Signature we create a string and remove the default values. + """ + listsignature = str(signature)[1:-1].split(",") + for counter, param in enumerate(listsignature): + if param.count("=") > 0: + listsignature[counter] = param[0 : param.index("=")].strip() + else: + listsignature[counter] = param.strip() + return ", ".join(listsignature) + + +def getinfo(func): + """ + Returns an info dictionary containing: + - name (the name of the function : str) + - argnames (the names of the arguments : list) + - defaults (the values of the default arguments : tuple) + - signature (the signature : str) + - fullsignature (the full signature : Signature) + - doc (the docstring : str) + - module (the module name : str) + - dict (the function __dict__ : str) + + >>> def f(self, x=1, y=2, *args, **kw): pass + + >>> info = getinfo(f) + + >>> info["name"] + 'f' + >>> info["argnames"] + ['self', 'x', 'y', 'args', 'kw'] + + >>> info["defaults"] + (1, 2) + + >>> info["signature"] + 'self, x, y, *args, **kw' + + >>> info["fullsignature"] + + """ + assert inspect.ismethod(func) or inspect.isfunction(func) + argspec = inspect.getfullargspec(func) + regargs, varargs, varkwargs = argspec[:3] + argnames = list(regargs) + if varargs: + argnames.append(varargs) + if varkwargs: + argnames.append(varkwargs) + fullsignature = inspect.signature(func) + # Convert Signature to str + signature = __legacysignature(fullsignature) + + # pypy compatibility + if hasattr(func, "__closure__"): + _closure = func.__closure__ + _globals = func.__globals__ + else: + _closure = func.func_closure + _globals = func.func_globals + + return dict( + name=func.__name__, + argnames=argnames, + signature=signature, + fullsignature=fullsignature, + defaults=func.__defaults__, + doc=func.__doc__, + module=func.__module__, + dict=func.__dict__, + globals=_globals, + closure=_closure, + ) + + +def update_wrapper(wrapper, model, infodict=None): + "akin to functools.update_wrapper" + infodict = infodict or getinfo(model) + wrapper.__name__ = infodict["name"] + wrapper.__doc__ = infodict["doc"] + wrapper.__module__ = infodict["module"] + wrapper.__dict__.update(infodict["dict"]) + wrapper.__defaults__ = infodict["defaults"] + wrapper.undecorated = model + return wrapper + + +def new_wrapper(wrapper, model): + """ + An improvement over functools.update_wrapper. The wrapper is a generic + callable object. It works by generating a copy of the wrapper with the + right signature and by updating the copy, not the original. + Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', + 'dict', 'defaults'. + """ + if isinstance(model, dict): + infodict = model + else: # assume model is a function + infodict = getinfo(model) + assert ( + not "_wrapper_" in infodict["argnames"] + ), '"_wrapper_" is a reserved argument name!' + src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict + funcopy = eval(src, dict(_wrapper_=wrapper)) + return update_wrapper(funcopy, model, infodict) + + +# helper used in decorator_factory +def __call__(self, func): + return new_wrapper(lambda *a, **k: self.call(func, *a, **k), func) + + +def decorator_factory(cls): + """ + Take a class with a ``.caller`` method and return a callable decorator + object. It works by adding a suitable __call__ method to the class; + it raises a TypeError if the class already has a nontrivial __call__ + method. + """ + attrs = set(dir(cls)) + if "__call__" in attrs: + raise TypeError( + "You cannot decorate a class with a nontrivial " "__call__ method" + ) + if "call" not in attrs: + raise TypeError("You cannot decorate a class without a " ".call method") + cls.__call__ = __call__ + return cls + + +def decorator(caller): + """ + General purpose decorator factory: takes a caller function as + input and returns a decorator with the same attributes. + A caller function is any function like this:: + + def caller(func, *args, **kw): + # do something + return func(*args, **kw) + + Here is an example of usage: + + >>> @decorator + ... def chatty(f, *args, **kw): + ... print("Calling %r" % f.__name__) + ... return f(*args, **kw) + + >>> chatty.__name__ + 'chatty' + + >>> @chatty + ... def f(): pass + ... + >>> f() + Calling 'f' + + decorator can also take in input a class with a .caller method; in this + case it converts the class into a factory of callable decorator objects. + See the documentation for an example. + """ + if inspect.isclass(caller): + return decorator_factory(caller) + + def _decorator(func): # the real meat is here + infodict = getinfo(func) + argnames = infodict["argnames"] + assert not ( + "_call_" in argnames or "_func_" in argnames + ), "You cannot use _call_ or _func_ as argument names!" + src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict + # import sys; print >> sys.stderr, src # for debugging purposes + dec_func = eval(src, dict(_func_=func, _call_=caller)) + return update_wrapper(dec_func, func, infodict) + + return update_wrapper(_decorator, caller) + + +def getattr_(obj, name, default_thunk): + "Similar to .setdefault in dictionaries." + try: + return getattr(obj, name) + except AttributeError: + default = default_thunk() + setattr(obj, name, default) + return default + + +@decorator +def memoize(func, *args): + dic = getattr_(func, "memoize_dic", dict) + # memoize_dic is created at the first call + if args in dic: + return dic[args] + result = func(*args) + dic[args] = result + return result + + +########################## LEGALESE ############################### + +## Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## Redistributions in bytecode form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. + +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +## DAMAGE. diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/downloader.py b/.pythonlibs/lib/python3.10/site-packages/nltk/downloader.py new file mode 100644 index 0000000..e5542be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/downloader.py @@ -0,0 +1,2559 @@ +# Natural Language Toolkit: Corpus & Model Downloader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +The NLTK corpus and module downloader. This module defines several +interfaces which can be used to download corpora, models, and other +data packages that can be used with NLTK. + +Downloading Packages +==================== +If called with no arguments, ``download()`` will display an interactive +interface which can be used to download and install new packages. +If Tkinter is available, then a graphical interface will be shown, +otherwise a simple text interface will be provided. + +Individual packages can be downloaded by calling the ``download()`` +function with a single argument, giving the package identifier for the +package that should be downloaded: + + >>> download('treebank') # doctest: +SKIP + [nltk_data] Downloading package 'treebank'... + [nltk_data] Unzipping corpora/treebank.zip. + +NLTK also provides a number of \"package collections\", consisting of +a group of related packages. To download all packages in a +colleciton, simply call ``download()`` with the collection's +identifier: + + >>> download('all-corpora') # doctest: +SKIP + [nltk_data] Downloading package 'abc'... + [nltk_data] Unzipping corpora/abc.zip. + [nltk_data] Downloading package 'alpino'... + [nltk_data] Unzipping corpora/alpino.zip. + ... + [nltk_data] Downloading package 'words'... + [nltk_data] Unzipping corpora/words.zip. + +Download Directory +================== +By default, packages are installed in either a system-wide directory +(if Python has sufficient access to write to it); or in the current +user's home directory. However, the ``download_dir`` argument may be +used to specify a different installation target, if desired. + +See ``Downloader.default_download_dir()`` for more a detailed +description of how the default download directory is chosen. + +NLTK Download Server +==================== +Before downloading any packages, the corpus and module downloader +contacts the NLTK download server, to retrieve an index file +describing the available packages. By default, this index file is +loaded from ``https://raw.githubusercontent.com/nltk/nltk_data/gh-pages/index.xml``. +If necessary, it is possible to create a new ``Downloader`` object, +specifying a different URL for the package index file. + +Usage:: + + python nltk/downloader.py [-d DATADIR] [-q] [-f] [-k] PACKAGE_IDS + +or:: + + python -m nltk.downloader [-d DATADIR] [-q] [-f] [-k] PACKAGE_IDS +""" +# ---------------------------------------------------------------------- + +""" + + 0 1 2 3 +[label][----][label][----] +[column ][column ] + +Notes +===== +Handling data files.. Some questions: + +* Should the data files be kept zipped or unzipped? I say zipped. + +* Should the data files be kept in svn at all? Advantages: history; + automatic version numbers; 'svn up' could be used rather than the + downloader to update the corpora. Disadvantages: they're big, + which makes working from svn a bit of a pain. And we're planning + to potentially make them much bigger. I don't think we want + people to have to download 400MB corpora just to use nltk from svn. + +* Compromise: keep the data files in trunk/data rather than in + trunk/nltk. That way you can check them out in svn if you want + to; but you don't need to, and you can use the downloader instead. + +* Also: keep models in mind. When we change the code, we'd + potentially like the models to get updated. This could require a + little thought. + +* So.. let's assume we have a trunk/data directory, containing a bunch + of packages. The packages should be kept as zip files, because we + really shouldn't be editing them much (well -- we may edit models + more, but they tend to be binary-ish files anyway, where diffs + aren't that helpful). So we'll have trunk/data, with a bunch of + files like abc.zip and treebank.zip and propbank.zip. For each + package we could also have eg treebank.xml and propbank.xml, + describing the contents of the package (name, copyright, license, + etc). Collections would also have .xml files. Finally, we would + pull all these together to form a single index.xml file. Some + directory structure wouldn't hurt. So how about:: + + /trunk/data/ ....................... root of data svn + index.xml ........................ main index file + src/ ............................. python scripts + packages/ ........................ dir for packages + corpora/ ....................... zip & xml files for corpora + grammars/ ...................... zip & xml files for grammars + taggers/ ....................... zip & xml files for taggers + tokenizers/ .................... zip & xml files for tokenizers + etc. + collections/ ..................... xml files for collections + + Where the root (/trunk/data) would contain a makefile; and src/ + would contain a script to update the info.xml file. It could also + contain scripts to rebuild some of the various model files. The + script that builds index.xml should probably check that each zip + file expands entirely into a single subdir, whose name matches the + package's uid. + +Changes I need to make: + - in index: change "size" to "filesize" or "compressed-size" + - in index: add "unzipped-size" + - when checking status: check both compressed & uncompressed size. + uncompressed size is important to make sure we detect a problem + if something got partially unzipped. define new status values + to differentiate stale vs corrupt vs corruptly-uncompressed?? + (we shouldn't need to re-download the file if the zip file is ok + but it didn't get uncompressed fully.) + - add other fields to the index: author, license, copyright, contact, + etc. + +the current grammars/ package would become a single new package (eg +toy-grammars or book-grammars). + +xml file should have: + - authorship info + - license info + - copyright info + - contact info + - info about what type of data/annotation it contains? + - recommended corpus reader? + +collections can contain other collections. they can also contain +multiple package types (corpora & models). Have a single 'basics' +package that includes everything we talk about in the book? + +n.b.: there will have to be a fallback to the punkt tokenizer, in case +they didn't download that model. + +default: unzip or not? + +""" +import functools +import itertools +import os +import shutil +import subprocess +import sys +import textwrap +import threading +import time +import warnings +import zipfile +from hashlib import md5 +from xml.etree import ElementTree + +try: + TKINTER = True + from tkinter import Button, Canvas, Entry, Frame, IntVar, Label, Menu, TclError, Tk + from tkinter.messagebox import showerror + + from nltk.draw.table import Table + from nltk.draw.util import ShowText +except ImportError: + TKINTER = False + TclError = ValueError + +from urllib.error import HTTPError, URLError +from urllib.request import urlopen + +import nltk + +# urllib2 = nltk.internals.import_from_stdlib('urllib2') + + +###################################################################### +# Directory entry objects (from the data server's index file) +###################################################################### + + +class Package: + """ + A directory entry for a downloadable package. These entries are + extracted from the XML index file that is downloaded by + ``Downloader``. Each package consists of a single file; but if + that file is a zip file, then it can be automatically decompressed + when the package is installed. + """ + + def __init__( + self, + id, + url, + name=None, + subdir="", + size=None, + unzipped_size=None, + checksum=None, + svn_revision=None, + copyright="Unknown", + contact="Unknown", + license="Unknown", + author="Unknown", + unzip=True, + **kw, + ): + self.id = id + """A unique identifier for this package.""" + + self.name = name or id + """A string name for this package.""" + + self.subdir = subdir + """The subdirectory where this package should be installed. + E.g., ``'corpora'`` or ``'taggers'``.""" + + self.url = url + """A URL that can be used to download this package's file.""" + + self.size = int(size) + """The filesize (in bytes) of the package file.""" + + self.unzipped_size = int(unzipped_size) + """The total filesize of the files contained in the package's + zipfile.""" + + self.checksum = checksum + """The MD-5 checksum of the package file.""" + + self.svn_revision = svn_revision + """A subversion revision number for this package.""" + + self.copyright = copyright + """Copyright holder for this package.""" + + self.contact = contact + """Name & email of the person who should be contacted with + questions about this package.""" + + self.license = license + """License information for this package.""" + + self.author = author + """Author of this package.""" + + ext = os.path.splitext(url.split("/")[-1])[1] + self.filename = os.path.join(subdir, id + ext) + """The filename that should be used for this package's file. It + is formed by joining ``self.subdir`` with ``self.id``, and + using the same extension as ``url``.""" + + self.unzip = bool(int(unzip)) # '0' or '1' + """A flag indicating whether this corpus should be unzipped by + default.""" + + # Include any other attributes provided by the XML file. + self.__dict__.update(kw) + + @staticmethod + def fromxml(xml): + if isinstance(xml, str): + xml = ElementTree.parse(xml) + for key in xml.attrib: + xml.attrib[key] = str(xml.attrib[key]) + return Package(**xml.attrib) + + def __lt__(self, other): + return self.id < other.id + + def __repr__(self): + return "" % self.id + + +class Collection: + """ + A directory entry for a collection of downloadable packages. + These entries are extracted from the XML index file that is + downloaded by ``Downloader``. + """ + + def __init__(self, id, children, name=None, **kw): + self.id = id + """A unique identifier for this collection.""" + + self.name = name or id + """A string name for this collection.""" + + self.children = children + """A list of the ``Collections`` or ``Packages`` directly + contained by this collection.""" + + self.packages = None + """A list of ``Packages`` contained by this collection or any + collections it recursively contains.""" + + # Include any other attributes provided by the XML file. + self.__dict__.update(kw) + + @staticmethod + def fromxml(xml): + if isinstance(xml, str): + xml = ElementTree.parse(xml) + for key in xml.attrib: + xml.attrib[key] = str(xml.attrib[key]) + children = [child.get("ref") for child in xml.findall("item")] + return Collection(children=children, **xml.attrib) + + def __lt__(self, other): + return self.id < other.id + + def __repr__(self): + return "" % self.id + + +###################################################################### +# Message Passing Objects +###################################################################### + + +class DownloaderMessage: + """A status message object, used by ``incr_download`` to + communicate its progress.""" + + +class StartCollectionMessage(DownloaderMessage): + """Data server has started working on a collection of packages.""" + + def __init__(self, collection): + self.collection = collection + + +class FinishCollectionMessage(DownloaderMessage): + """Data server has finished working on a collection of packages.""" + + def __init__(self, collection): + self.collection = collection + + +class StartPackageMessage(DownloaderMessage): + """Data server has started working on a package.""" + + def __init__(self, package): + self.package = package + + +class FinishPackageMessage(DownloaderMessage): + """Data server has finished working on a package.""" + + def __init__(self, package): + self.package = package + + +class StartDownloadMessage(DownloaderMessage): + """Data server has started downloading a package.""" + + def __init__(self, package): + self.package = package + + +class FinishDownloadMessage(DownloaderMessage): + """Data server has finished downloading a package.""" + + def __init__(self, package): + self.package = package + + +class StartUnzipMessage(DownloaderMessage): + """Data server has started unzipping a package.""" + + def __init__(self, package): + self.package = package + + +class FinishUnzipMessage(DownloaderMessage): + """Data server has finished unzipping a package.""" + + def __init__(self, package): + self.package = package + + +class UpToDateMessage(DownloaderMessage): + """The package download file is already up-to-date""" + + def __init__(self, package): + self.package = package + + +class StaleMessage(DownloaderMessage): + """The package download file is out-of-date or corrupt""" + + def __init__(self, package): + self.package = package + + +class ErrorMessage(DownloaderMessage): + """Data server encountered an error""" + + def __init__(self, package, message): + self.package = package + if isinstance(message, Exception): + self.message = str(message) + else: + self.message = message + + +class ProgressMessage(DownloaderMessage): + """Indicates how much progress the data server has made""" + + def __init__(self, progress): + self.progress = progress + + +class SelectDownloadDirMessage(DownloaderMessage): + """Indicates what download directory the data server is using""" + + def __init__(self, download_dir): + self.download_dir = download_dir + + +###################################################################### +# NLTK Data Server +###################################################################### + + +class Downloader: + """ + A class used to access the NLTK data server, which can be used to + download corpora and other data packages. + """ + + # ///////////////////////////////////////////////////////////////// + # Configuration + # ///////////////////////////////////////////////////////////////// + + INDEX_TIMEOUT = 60 * 60 # 1 hour + """The amount of time after which the cached copy of the data + server index will be considered 'stale,' and will be + re-downloaded.""" + + DEFAULT_URL = "https://raw.githubusercontent.com/nltk/nltk_data/gh-pages/index.xml" + """The default URL for the NLTK data server's index. An + alternative URL can be specified when creating a new + ``Downloader`` object.""" + + # ///////////////////////////////////////////////////////////////// + # Status Constants + # ///////////////////////////////////////////////////////////////// + + INSTALLED = "installed" + """A status string indicating that a package or collection is + installed and up-to-date.""" + NOT_INSTALLED = "not installed" + """A status string indicating that a package or collection is + not installed.""" + STALE = "out of date" + """A status string indicating that a package or collection is + corrupt or out-of-date.""" + PARTIAL = "partial" + """A status string indicating that a collection is partially + installed (i.e., only some of its packages are installed.)""" + + # ///////////////////////////////////////////////////////////////// + # Constructor + # ///////////////////////////////////////////////////////////////// + + def __init__(self, server_index_url=None, download_dir=None): + self._url = server_index_url or self.DEFAULT_URL + """The URL for the data server's index file.""" + + self._collections = {} + """Dictionary from collection identifier to ``Collection``""" + + self._packages = {} + """Dictionary from package identifier to ``Package``""" + + self._download_dir = download_dir + """The default directory to which packages will be downloaded.""" + + self._index = None + """The XML index file downloaded from the data server""" + + self._index_timestamp = None + """Time at which ``self._index`` was downloaded. If it is more + than ``INDEX_TIMEOUT`` seconds old, it will be re-downloaded.""" + + self._status_cache = {} + """Dictionary from package/collection identifier to status + string (``INSTALLED``, ``NOT_INSTALLED``, ``STALE``, or + ``PARTIAL``). Cache is used for packages only, not + collections.""" + + self._errors = None + """Flag for telling if all packages got successfully downloaded or not.""" + + # decide where we're going to save things to. + if self._download_dir is None: + self._download_dir = self.default_download_dir() + + # ///////////////////////////////////////////////////////////////// + # Information + # ///////////////////////////////////////////////////////////////// + + def list( + self, + download_dir=None, + show_packages=True, + show_collections=True, + header=True, + more_prompt=False, + skip_installed=False, + ): + lines = 0 # for more_prompt + if download_dir is None: + download_dir = self._download_dir + print("Using default data directory (%s)" % download_dir) + if header: + print("=" * (26 + len(self._url))) + print(" Data server index for <%s>" % self._url) + print("=" * (26 + len(self._url))) + lines += 3 # for more_prompt + stale = partial = False + + categories = [] + if show_packages: + categories.append("packages") + if show_collections: + categories.append("collections") + for category in categories: + print("%s:" % category.capitalize()) + lines += 1 # for more_prompt + for info in sorted(getattr(self, category)(), key=str): + status = self.status(info, download_dir) + if status == self.INSTALLED and skip_installed: + continue + if status == self.STALE: + stale = True + if status == self.PARTIAL: + partial = True + prefix = { + self.INSTALLED: "*", + self.STALE: "-", + self.PARTIAL: "P", + self.NOT_INSTALLED: " ", + }[status] + name = textwrap.fill( + "-" * 27 + (info.name or info.id), 75, subsequent_indent=27 * " " + )[27:] + print(" [{}] {} {}".format(prefix, info.id.ljust(20, "."), name)) + lines += len(name.split("\n")) # for more_prompt + if more_prompt and lines > 20: + user_input = input("Hit Enter to continue: ") + if user_input.lower() in ("x", "q"): + return + lines = 0 + print() + msg = "([*] marks installed packages" + if stale: + msg += "; [-] marks out-of-date or corrupt packages" + if partial: + msg += "; [P] marks partially installed collections" + print(textwrap.fill(msg + ")", subsequent_indent=" ", width=76)) + + def packages(self): + self._update_index() + return self._packages.values() + + def corpora(self): + self._update_index() + return [pkg for (id, pkg) in self._packages.items() if pkg.subdir == "corpora"] + + def models(self): + self._update_index() + return [pkg for (id, pkg) in self._packages.items() if pkg.subdir != "corpora"] + + def collections(self): + self._update_index() + return self._collections.values() + + # ///////////////////////////////////////////////////////////////// + # Downloading + # ///////////////////////////////////////////////////////////////// + + def _info_or_id(self, info_or_id): + if isinstance(info_or_id, str): + return self.info(info_or_id) + else: + return info_or_id + + # [xx] When during downloading is it 'safe' to abort? Only unsafe + # time is *during* an unzip -- we don't want to leave a + # partially-unzipped corpus in place because we wouldn't notice + # it. But if we had the exact total size of the unzipped corpus, + # then that would be fine. Then we could abort anytime we want! + # So this is really what we should do. That way the threaded + # downloader in the gui can just kill the download thread anytime + # it wants. + + def incr_download(self, info_or_id, download_dir=None, force=False): + # If they didn't specify a download_dir, then use the default one. + if download_dir is None: + download_dir = self._download_dir + yield SelectDownloadDirMessage(download_dir) + + # If they gave us a list of ids, then download each one. + if isinstance(info_or_id, (list, tuple)): + yield from self._download_list(info_or_id, download_dir, force) + return + + # Look up the requested collection or package. + try: + info = self._info_or_id(info_or_id) + except (OSError, ValueError) as e: + yield ErrorMessage(None, f"Error loading {info_or_id}: {e}") + return + + # Handle collections. + if isinstance(info, Collection): + yield StartCollectionMessage(info) + yield from self.incr_download(info.children, download_dir, force) + yield FinishCollectionMessage(info) + + # Handle Packages (delegate to a helper function). + else: + yield from self._download_package(info, download_dir, force) + + def _num_packages(self, item): + if isinstance(item, Package): + return 1 + else: + return len(item.packages) + + def _download_list(self, items, download_dir, force): + # Look up the requested items. + for i in range(len(items)): + try: + items[i] = self._info_or_id(items[i]) + except (OSError, ValueError) as e: + yield ErrorMessage(items[i], e) + return + + # Download each item, re-scaling their progress. + num_packages = sum(self._num_packages(item) for item in items) + progress = 0 + for i, item in enumerate(items): + if isinstance(item, Package): + delta = 1.0 / num_packages + else: + delta = len(item.packages) / num_packages + for msg in self.incr_download(item, download_dir, force): + if isinstance(msg, ProgressMessage): + yield ProgressMessage(progress + msg.progress * delta) + else: + yield msg + + progress += 100 * delta + + def _download_package(self, info, download_dir, force): + yield StartPackageMessage(info) + yield ProgressMessage(0) + + # Do we already have the current version? + status = self.status(info, download_dir) + if not force and status == self.INSTALLED: + yield UpToDateMessage(info) + yield ProgressMessage(100) + yield FinishPackageMessage(info) + return + + # Remove the package from our status cache + self._status_cache.pop(info.id, None) + + # Check for (and remove) any old/stale version. + filepath = os.path.join(download_dir, info.filename) + if os.path.exists(filepath): + if status == self.STALE: + yield StaleMessage(info) + os.remove(filepath) + + # Ensure the download_dir exists + if not os.path.exists(download_dir): + os.makedirs(download_dir) + if not os.path.exists(os.path.join(download_dir, info.subdir)): + os.makedirs(os.path.join(download_dir, info.subdir)) + + # Download the file. This will raise an IOError if the url + # is not found. + yield StartDownloadMessage(info) + yield ProgressMessage(5) + try: + infile = urlopen(info.url) + with open(filepath, "wb") as outfile: + num_blocks = max(1, info.size / (1024 * 16)) + for block in itertools.count(): + s = infile.read(1024 * 16) # 16k blocks. + outfile.write(s) + if not s: + break + if block % 2 == 0: # how often? + yield ProgressMessage(min(80, 5 + 75 * (block / num_blocks))) + infile.close() + except OSError as e: + yield ErrorMessage( + info, + "Error downloading %r from <%s>:" "\n %s" % (info.id, info.url, e), + ) + return + yield FinishDownloadMessage(info) + yield ProgressMessage(80) + + # If it's a zipfile, uncompress it. + if info.filename.endswith(".zip"): + zipdir = os.path.join(download_dir, info.subdir) + # Unzip if we're unzipping by default; *or* if it's already + # been unzipped (presumably a previous version). + if info.unzip or os.path.exists(os.path.join(zipdir, info.id)): + yield StartUnzipMessage(info) + for msg in _unzip_iter(filepath, zipdir, verbose=False): + # Somewhat of a hack, but we need a proper package reference + msg.package = info + yield msg + yield FinishUnzipMessage(info) + + yield FinishPackageMessage(info) + + def download( + self, + info_or_id=None, + download_dir=None, + quiet=False, + force=False, + prefix="[nltk_data] ", + halt_on_error=True, + raise_on_error=False, + print_error_to=sys.stderr, + ): + + print_to = functools.partial(print, file=print_error_to) + # If no info or id is given, then use the interactive shell. + if info_or_id is None: + # [xx] hmm -- changing self._download_dir here seems like + # the wrong thing to do. Maybe the _interactive_download + # function should make a new copy of self to use? + if download_dir is not None: + self._download_dir = download_dir + self._interactive_download() + return True + + else: + # Define a helper function for displaying output: + def show(s, prefix2=""): + print_to( + textwrap.fill( + s, + initial_indent=prefix + prefix2, + subsequent_indent=prefix + prefix2 + " " * 4, + ) + ) + + for msg in self.incr_download(info_or_id, download_dir, force): + # Error messages + if isinstance(msg, ErrorMessage): + show(msg.message) + if raise_on_error: + raise ValueError(msg.message) + if halt_on_error: + return False + self._errors = True + if not quiet: + print_to("Error installing package. Retry? [n/y/e]") + choice = input().strip() + if choice in ["y", "Y"]: + if not self.download( + msg.package.id, + download_dir, + quiet, + force, + prefix, + halt_on_error, + raise_on_error, + ): + return False + elif choice in ["e", "E"]: + return False + + # All other messages + if not quiet: + # Collection downloading messages: + if isinstance(msg, StartCollectionMessage): + show("Downloading collection %r" % msg.collection.id) + prefix += " | " + print_to(prefix) + elif isinstance(msg, FinishCollectionMessage): + print_to(prefix) + prefix = prefix[:-4] + if self._errors: + show( + "Downloaded collection %r with errors" + % msg.collection.id + ) + else: + show("Done downloading collection %s" % msg.collection.id) + + # Package downloading messages: + elif isinstance(msg, StartPackageMessage): + show( + "Downloading package %s to %s..." + % (msg.package.id, download_dir) + ) + elif isinstance(msg, UpToDateMessage): + show("Package %s is already up-to-date!" % msg.package.id, " ") + # elif isinstance(msg, StaleMessage): + # show('Package %s is out-of-date or corrupt' % + # msg.package.id, ' ') + elif isinstance(msg, StartUnzipMessage): + show("Unzipping %s." % msg.package.filename, " ") + + # Data directory message: + elif isinstance(msg, SelectDownloadDirMessage): + download_dir = msg.download_dir + return True + + def is_stale(self, info_or_id, download_dir=None): + return self.status(info_or_id, download_dir) == self.STALE + + def is_installed(self, info_or_id, download_dir=None): + return self.status(info_or_id, download_dir) == self.INSTALLED + + def clear_status_cache(self, id=None): + if id is None: + self._status_cache.clear() + else: + self._status_cache.pop(id, None) + + def status(self, info_or_id, download_dir=None): + """ + Return a constant describing the status of the given package + or collection. Status can be one of ``INSTALLED``, + ``NOT_INSTALLED``, ``STALE``, or ``PARTIAL``. + """ + if download_dir is None: + download_dir = self._download_dir + info = self._info_or_id(info_or_id) + + # Handle collections: + if isinstance(info, Collection): + pkg_status = [self.status(pkg.id) for pkg in info.packages] + if self.STALE in pkg_status: + return self.STALE + elif self.PARTIAL in pkg_status: + return self.PARTIAL + elif self.INSTALLED in pkg_status and self.NOT_INSTALLED in pkg_status: + return self.PARTIAL + elif self.NOT_INSTALLED in pkg_status: + return self.NOT_INSTALLED + else: + return self.INSTALLED + + # Handle packages: + else: + filepath = os.path.join(download_dir, info.filename) + if download_dir != self._download_dir: + return self._pkg_status(info, filepath) + else: + if info.id not in self._status_cache: + self._status_cache[info.id] = self._pkg_status(info, filepath) + return self._status_cache[info.id] + + def _pkg_status(self, info, filepath): + if not os.path.exists(filepath): + return self.NOT_INSTALLED + + # Check if the file has the correct size. + try: + filestat = os.stat(filepath) + except OSError: + return self.NOT_INSTALLED + if filestat.st_size != int(info.size): + return self.STALE + + # Check if the file's checksum matches + if md5_hexdigest(filepath) != info.checksum: + return self.STALE + + # If it's a zipfile, and it's been at least partially + # unzipped, then check if it's been fully unzipped. + if filepath.endswith(".zip"): + unzipdir = filepath[:-4] + if not os.path.exists(unzipdir): + return self.INSTALLED # but not unzipped -- ok! + if not os.path.isdir(unzipdir): + return self.STALE + + unzipped_size = sum( + os.stat(os.path.join(d, f)).st_size + for d, _, files in os.walk(unzipdir) + for f in files + ) + if unzipped_size != info.unzipped_size: + return self.STALE + + # Otherwise, everything looks good. + return self.INSTALLED + + def update(self, quiet=False, prefix="[nltk_data] "): + """ + Re-download any packages whose status is STALE. + """ + self.clear_status_cache() + for pkg in self.packages(): + if self.status(pkg) == self.STALE: + self.download(pkg, quiet=quiet, prefix=prefix) + + # ///////////////////////////////////////////////////////////////// + # Index + # ///////////////////////////////////////////////////////////////// + + def _update_index(self, url=None): + """A helper function that ensures that self._index is + up-to-date. If the index is older than self.INDEX_TIMEOUT, + then download it again.""" + # Check if the index is already up-to-date. If so, do nothing. + if not ( + self._index is None + or url is not None + or time.time() - self._index_timestamp > self.INDEX_TIMEOUT + ): + return + + # If a URL was specified, then update our URL. + self._url = url or self._url + + # Download the index file. + self._index = nltk.internals.ElementWrapper( + ElementTree.parse(urlopen(self._url)).getroot() + ) + self._index_timestamp = time.time() + + # Build a dictionary of packages. + packages = [Package.fromxml(p) for p in self._index.findall("packages/package")] + self._packages = {p.id: p for p in packages} + + # Build a dictionary of collections. + collections = [ + Collection.fromxml(c) for c in self._index.findall("collections/collection") + ] + self._collections = {c.id: c for c in collections} + + # Replace identifiers with actual children in collection.children. + for collection in self._collections.values(): + for i, child_id in enumerate(collection.children): + if child_id in self._packages: + collection.children[i] = self._packages[child_id] + elif child_id in self._collections: + collection.children[i] = self._collections[child_id] + else: + print( + "removing collection member with no package: {}".format( + child_id + ) + ) + del collection.children[i] + + # Fill in collection.packages for each collection. + for collection in self._collections.values(): + packages = {} + queue = [collection] + for child in queue: + if isinstance(child, Collection): + queue.extend(child.children) + elif isinstance(child, Package): + packages[child.id] = child + else: + pass + collection.packages = packages.values() + + # Flush the status cache + self._status_cache.clear() + + def index(self): + """ + Return the XML index describing the packages available from + the data server. If necessary, this index will be downloaded + from the data server. + """ + self._update_index() + return self._index + + def info(self, id): + """Return the ``Package`` or ``Collection`` record for the + given item.""" + self._update_index() + if id in self._packages: + return self._packages[id] + if id in self._collections: + return self._collections[id] + raise ValueError("Package %r not found in index" % id) + + def xmlinfo(self, id): + """Return the XML info record for the given item""" + self._update_index() + for package in self._index.findall("packages/package"): + if package.get("id") == id: + return package + for collection in self._index.findall("collections/collection"): + if collection.get("id") == id: + return collection + raise ValueError("Package %r not found in index" % id) + + # ///////////////////////////////////////////////////////////////// + # URL & Data Directory + # ///////////////////////////////////////////////////////////////// + + def _get_url(self): + """The URL for the data server's index file.""" + return self._url + + def _set_url(self, url): + """ + Set a new URL for the data server. If we're unable to contact + the given url, then the original url is kept. + """ + original_url = self._url + try: + self._update_index(url) + except: + self._url = original_url + raise + + url = property(_get_url, _set_url) + + def default_download_dir(self): + """ + Return the directory to which packages will be downloaded by + default. This value can be overridden using the constructor, + or on a case-by-case basis using the ``download_dir`` argument when + calling ``download()``. + + On Windows, the default download directory is + ``PYTHONHOME/lib/nltk``, where *PYTHONHOME* is the + directory containing Python, e.g. ``C:\\Python25``. + + On all other platforms, the default directory is the first of + the following which exists or which can be created with write + permission: ``/usr/share/nltk_data``, ``/usr/local/share/nltk_data``, + ``/usr/lib/nltk_data``, ``/usr/local/lib/nltk_data``, ``~/nltk_data``. + """ + # Check if we are on GAE where we cannot write into filesystem. + if "APPENGINE_RUNTIME" in os.environ: + return + + # Check if we have sufficient permissions to install in a + # variety of system-wide locations. + for nltkdir in nltk.data.path: + if os.path.exists(nltkdir) and nltk.internals.is_writable(nltkdir): + return nltkdir + + # On Windows, use %APPDATA% + if sys.platform == "win32" and "APPDATA" in os.environ: + homedir = os.environ["APPDATA"] + + # Otherwise, install in the user's home directory. + else: + homedir = os.path.expanduser("~/") + if homedir == "~/": + raise ValueError("Could not find a default download directory") + + # append "nltk_data" to the home directory + return os.path.join(homedir, "nltk_data") + + def _get_download_dir(self): + """ + The default directory to which packages will be downloaded. + This defaults to the value returned by ``default_download_dir()``. + To override this default on a case-by-case basis, use the + ``download_dir`` argument when calling ``download()``. + """ + return self._download_dir + + def _set_download_dir(self, download_dir): + self._download_dir = download_dir + # Clear the status cache. + self._status_cache.clear() + + download_dir = property(_get_download_dir, _set_download_dir) + + # ///////////////////////////////////////////////////////////////// + # Interactive Shell + # ///////////////////////////////////////////////////////////////// + + def _interactive_download(self): + # Try the GUI first; if that doesn't work, try the simple + # interactive shell. + if TKINTER: + try: + DownloaderGUI(self).mainloop() + except TclError: + DownloaderShell(self).run() + else: + DownloaderShell(self).run() + + +class DownloaderShell: + def __init__(self, dataserver): + self._ds = dataserver + + def _simple_interactive_menu(self, *options): + print("-" * 75) + spc = (68 - sum(len(o) for o in options)) // (len(options) - 1) * " " + print(" " + spc.join(options)) + print("-" * 75) + + def run(self): + print("NLTK Downloader") + while True: + self._simple_interactive_menu( + "d) Download", + "l) List", + " u) Update", + "c) Config", + "h) Help", + "q) Quit", + ) + user_input = input("Downloader> ").strip() + if not user_input: + print() + continue + command = user_input.lower().split()[0] + args = user_input.split()[1:] + try: + if command == "l": + print() + self._ds.list(self._ds.download_dir, header=False, more_prompt=True) + elif command == "h": + self._simple_interactive_help() + elif command == "c": + self._simple_interactive_config() + elif command in ("q", "x"): + return + elif command == "d": + self._simple_interactive_download(args) + elif command == "u": + self._simple_interactive_update() + else: + print("Command %r unrecognized" % user_input) + except HTTPError as e: + print("Error reading from server: %s" % e) + except URLError as e: + print("Error connecting to server: %s" % e.reason) + # try checking if user_input is a package name, & + # downloading it? + print() + + def _simple_interactive_download(self, args): + if args: + for arg in args: + try: + self._ds.download(arg, prefix=" ") + except (OSError, ValueError) as e: + print(e) + else: + while True: + print() + print("Download which package (l=list; x=cancel)?") + user_input = input(" Identifier> ") + if user_input.lower() == "l": + self._ds.list( + self._ds.download_dir, + header=False, + more_prompt=True, + skip_installed=True, + ) + continue + elif user_input.lower() in ("x", "q", ""): + return + elif user_input: + for id in user_input.split(): + try: + self._ds.download(id, prefix=" ") + except (OSError, ValueError) as e: + print(e) + break + + def _simple_interactive_update(self): + while True: + stale_packages = [] + stale = partial = False + for info in sorted(getattr(self._ds, "packages")(), key=str): + if self._ds.status(info) == self._ds.STALE: + stale_packages.append((info.id, info.name)) + + print() + if stale_packages: + print("Will update following packages (o=ok; x=cancel)") + for pid, pname in stale_packages: + name = textwrap.fill( + "-" * 27 + (pname), 75, subsequent_indent=27 * " " + )[27:] + print(" [ ] {} {}".format(pid.ljust(20, "."), name)) + print() + + user_input = input(" Identifier> ") + if user_input.lower() == "o": + for pid, pname in stale_packages: + try: + self._ds.download(pid, prefix=" ") + except (OSError, ValueError) as e: + print(e) + break + elif user_input.lower() in ("x", "q", ""): + return + else: + print("Nothing to update.") + return + + def _simple_interactive_help(self): + print() + print("Commands:") + print( + " d) Download a package or collection u) Update out of date packages" + ) + print(" l) List packages & collections h) Help") + print(" c) View & Modify Configuration q) Quit") + + def _show_config(self): + print() + print("Data Server:") + print(" - URL: <%s>" % self._ds.url) + print(" - %d Package Collections Available" % len(self._ds.collections())) + print(" - %d Individual Packages Available" % len(self._ds.packages())) + print() + print("Local Machine:") + print(" - Data directory: %s" % self._ds.download_dir) + + def _simple_interactive_config(self): + self._show_config() + while True: + print() + self._simple_interactive_menu( + "s) Show Config", "u) Set Server URL", "d) Set Data Dir", "m) Main Menu" + ) + user_input = input("Config> ").strip().lower() + if user_input == "s": + self._show_config() + elif user_input == "d": + new_dl_dir = input(" New Directory> ").strip() + if new_dl_dir in ("", "x", "q", "X", "Q"): + print(" Cancelled!") + elif os.path.isdir(new_dl_dir): + self._ds.download_dir = new_dl_dir + else: + print("Directory %r not found! Create it first." % new_dl_dir) + elif user_input == "u": + new_url = input(" New URL> ").strip() + if new_url in ("", "x", "q", "X", "Q"): + print(" Cancelled!") + else: + if not new_url.startswith(("http://", "https://")): + new_url = "http://" + new_url + try: + self._ds.url = new_url + except Exception as e: + print(f"Error reading <{new_url!r}>:\n {e}") + elif user_input == "m": + break + + +class DownloaderGUI: + """ + Graphical interface for downloading packages from the NLTK data + server. + """ + + # ///////////////////////////////////////////////////////////////// + # Column Configuration + # ///////////////////////////////////////////////////////////////// + + COLUMNS = [ + "", + "Identifier", + "Name", + "Size", + "Status", + "Unzipped Size", + "Copyright", + "Contact", + "License", + "Author", + "Subdir", + "Checksum", + ] + """A list of the names of columns. This controls the order in + which the columns will appear. If this is edited, then + ``_package_to_columns()`` may need to be edited to match.""" + + COLUMN_WEIGHTS = {"": 0, "Name": 5, "Size": 0, "Status": 0} + """A dictionary specifying how columns should be resized when the + table is resized. Columns with weight 0 will not be resized at + all; and columns with high weight will be resized more. + Default weight (for columns not explicitly listed) is 1.""" + + COLUMN_WIDTHS = { + "": 1, + "Identifier": 20, + "Name": 45, + "Size": 10, + "Unzipped Size": 10, + "Status": 12, + } + """A dictionary specifying how wide each column should be, in + characters. The default width (for columns not explicitly + listed) is specified by ``DEFAULT_COLUMN_WIDTH``.""" + + DEFAULT_COLUMN_WIDTH = 30 + """The default width for columns that are not explicitly listed + in ``COLUMN_WIDTHS``.""" + + INITIAL_COLUMNS = ["", "Identifier", "Name", "Size", "Status"] + """The set of columns that should be displayed by default.""" + + # Perform a few import-time sanity checks to make sure that the + # column configuration variables are defined consistently: + for c in COLUMN_WEIGHTS: + assert c in COLUMNS + for c in COLUMN_WIDTHS: + assert c in COLUMNS + for c in INITIAL_COLUMNS: + assert c in COLUMNS + + # ///////////////////////////////////////////////////////////////// + # Color Configuration + # ///////////////////////////////////////////////////////////////// + + _BACKDROP_COLOR = ("#000", "#ccc") + + _ROW_COLOR = { + Downloader.INSTALLED: ("#afa", "#080"), + Downloader.PARTIAL: ("#ffa", "#880"), + Downloader.STALE: ("#faa", "#800"), + Downloader.NOT_INSTALLED: ("#fff", "#888"), + } + + _MARK_COLOR = ("#000", "#ccc") + + # _FRONT_TAB_COLOR = ('#ccf', '#008') + # _BACK_TAB_COLOR = ('#88a', '#448') + _FRONT_TAB_COLOR = ("#fff", "#45c") + _BACK_TAB_COLOR = ("#aaa", "#67a") + + _PROGRESS_COLOR = ("#f00", "#aaa") + + _TAB_FONT = "helvetica -16 bold" + + # ///////////////////////////////////////////////////////////////// + # Constructor + # ///////////////////////////////////////////////////////////////// + + def __init__(self, dataserver, use_threads=True): + self._ds = dataserver + self._use_threads = use_threads + + # For the threaded downloader: + self._download_lock = threading.Lock() + self._download_msg_queue = [] + self._download_abort_queue = [] + self._downloading = False + + # For tkinter after callbacks: + self._afterid = {} + + # A message log. + self._log_messages = [] + self._log_indent = 0 + self._log("NLTK Downloader Started!") + + # Create the main window. + top = self.top = Tk() + top.geometry("+50+50") + top.title("NLTK Downloader") + top.configure(background=self._BACKDROP_COLOR[1]) + + # Set up some bindings now, in case anything goes wrong. + top.bind("", self.destroy) + top.bind("", self.destroy) + self._destroyed = False + + self._column_vars = {} + + # Initialize the GUI. + self._init_widgets() + self._init_menu() + try: + self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + + self._show_info() + self._select_columns() + self._table.select(0) + + # Make sure we get notified when we're destroyed, so we can + # cancel any download in progress. + self._table.bind("", self._destroy) + + def _log(self, msg): + self._log_messages.append( + "{} {}{}".format(time.ctime(), " | " * self._log_indent, msg) + ) + + # ///////////////////////////////////////////////////////////////// + # Internals + # ///////////////////////////////////////////////////////////////// + + def _init_widgets(self): + # Create the top-level frame structures + f1 = Frame(self.top, relief="raised", border=2, padx=8, pady=0) + f1.pack(sid="top", expand=True, fill="both") + f1.grid_rowconfigure(2, weight=1) + f1.grid_columnconfigure(0, weight=1) + Frame(f1, height=8).grid(column=0, row=0) # spacer + tabframe = Frame(f1) + tabframe.grid(column=0, row=1, sticky="news") + tableframe = Frame(f1) + tableframe.grid(column=0, row=2, sticky="news") + buttonframe = Frame(f1) + buttonframe.grid(column=0, row=3, sticky="news") + Frame(f1, height=8).grid(column=0, row=4) # spacer + infoframe = Frame(f1) + infoframe.grid(column=0, row=5, sticky="news") + Frame(f1, height=8).grid(column=0, row=6) # spacer + progressframe = Frame( + self.top, padx=3, pady=3, background=self._BACKDROP_COLOR[1] + ) + progressframe.pack(side="bottom", fill="x") + self.top["border"] = 0 + self.top["highlightthickness"] = 0 + + # Create the tabs + self._tab_names = ["Collections", "Corpora", "Models", "All Packages"] + self._tabs = {} + for i, tab in enumerate(self._tab_names): + label = Label(tabframe, text=tab, font=self._TAB_FONT) + label.pack(side="left", padx=((i + 1) % 2) * 10) + label.bind("", self._select_tab) + self._tabs[tab.lower()] = label + + # Create the table. + column_weights = [self.COLUMN_WEIGHTS.get(column, 1) for column in self.COLUMNS] + self._table = Table( + tableframe, + self.COLUMNS, + column_weights=column_weights, + highlightthickness=0, + listbox_height=16, + reprfunc=self._table_reprfunc, + ) + self._table.columnconfig(0, foreground=self._MARK_COLOR[0]) # marked + for i, column in enumerate(self.COLUMNS): + width = self.COLUMN_WIDTHS.get(column, self.DEFAULT_COLUMN_WIDTH) + self._table.columnconfig(i, width=width) + self._table.pack(expand=True, fill="both") + self._table.focus() + self._table.bind_to_listboxes("", self._download) + self._table.bind("", self._table_mark) + self._table.bind("", self._download) + self._table.bind("", self._prev_tab) + self._table.bind("", self._next_tab) + self._table.bind("", self._mark_all) + + # Create entry boxes for URL & download_dir + infoframe.grid_columnconfigure(1, weight=1) + + info = [ + ("url", "Server Index:", self._set_url), + ("download_dir", "Download Directory:", self._set_download_dir), + ] + self._info = {} + for (i, (key, label, callback)) in enumerate(info): + Label(infoframe, text=label).grid(column=0, row=i, sticky="e") + entry = Entry( + infoframe, + font="courier", + relief="groove", + disabledforeground="#007aff", + foreground="#007aff", + ) + self._info[key] = (entry, callback) + entry.bind("", self._info_save) + entry.bind("", lambda e, key=key: self._info_edit(key)) + entry.grid(column=1, row=i, sticky="ew") + + # If the user edits url or download_dir, and then clicks outside + # the entry box, then save their results. + self.top.bind("", self._info_save) + + # Create Download & Refresh buttons. + self._download_button = Button( + buttonframe, text="Download", command=self._download, width=8 + ) + self._download_button.pack(side="left") + self._refresh_button = Button( + buttonframe, text="Refresh", command=self._refresh, width=8 + ) + self._refresh_button.pack(side="right") + + # Create Progress bar + self._progresslabel = Label( + progressframe, + text="", + foreground=self._BACKDROP_COLOR[0], + background=self._BACKDROP_COLOR[1], + ) + self._progressbar = Canvas( + progressframe, + width=200, + height=16, + background=self._PROGRESS_COLOR[1], + relief="sunken", + border=1, + ) + self._init_progressbar() + self._progressbar.pack(side="right") + self._progresslabel.pack(side="left") + + def _init_menu(self): + menubar = Menu(self.top) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Download", underline=0, command=self._download, accelerator="Return" + ) + filemenu.add_separator() + filemenu.add_command( + label="Change Server Index", + underline=7, + command=lambda: self._info_edit("url"), + ) + filemenu.add_command( + label="Change Download Directory", + underline=0, + command=lambda: self._info_edit("download_dir"), + ) + filemenu.add_separator() + filemenu.add_command(label="Show Log", underline=5, command=self._show_log) + filemenu.add_separator() + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + # Create a menu to control which columns of the table are + # shown. n.b.: we never hide the first two columns (mark and + # identifier). + viewmenu = Menu(menubar, tearoff=0) + for column in self._table.column_names[2:]: + var = IntVar(self.top) + assert column not in self._column_vars + self._column_vars[column] = var + if column in self.INITIAL_COLUMNS: + var.set(1) + viewmenu.add_checkbutton( + label=column, underline=0, variable=var, command=self._select_columns + ) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + # Create a sort menu + # [xx] this should be selectbuttons; and it should include + # reversed sorts as options. + sortmenu = Menu(menubar, tearoff=0) + for column in self._table.column_names[1:]: + sortmenu.add_command( + label="Sort by %s" % column, + command=(lambda c=column: self._table.sort_by(c, "ascending")), + ) + sortmenu.add_separator() + # sortmenu.add_command(label='Descending Sort:') + for column in self._table.column_names[1:]: + sortmenu.add_command( + label="Reverse sort by %s" % column, + command=(lambda c=column: self._table.sort_by(c, "descending")), + ) + menubar.add_cascade(label="Sort", underline=0, menu=sortmenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + helpmenu.add_command( + label="Instructions", underline=0, command=self.help, accelerator="F1" + ) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + self.top.bind("", self.help) + + self.top.config(menu=menubar) + + def _select_columns(self): + for (column, var) in self._column_vars.items(): + if var.get(): + self._table.show_column(column) + else: + self._table.hide_column(column) + + def _refresh(self): + self._ds.clear_status_cache() + try: + self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + self._table.select(0) + + def _info_edit(self, info_key): + self._info_save() # just in case. + (entry, callback) = self._info[info_key] + entry["state"] = "normal" + entry["relief"] = "sunken" + entry.focus() + + def _info_save(self, e=None): + focus = self._table + for entry, callback in self._info.values(): + if entry["state"] == "disabled": + continue + if e is not None and e.widget is entry and e.keysym != "Return": + focus = entry + else: + entry["state"] = "disabled" + entry["relief"] = "groove" + callback(entry.get()) + focus.focus() + + def _table_reprfunc(self, row, col, val): + if self._table.column_names[col].endswith("Size"): + if isinstance(val, str): + return " %s" % val + elif val < 1024**2: + return " %.1f KB" % (val / 1024.0**1) + elif val < 1024**3: + return " %.1f MB" % (val / 1024.0**2) + else: + return " %.1f GB" % (val / 1024.0**3) + + if col in (0, ""): + return str(val) + else: + return " %s" % val + + def _set_url(self, url): + if url == self._ds.url: + return + try: + self._ds.url = url + self._fill_table() + except OSError as e: + showerror("Error Setting Server Index", str(e)) + self._show_info() + + def _set_download_dir(self, download_dir): + if self._ds.download_dir == download_dir: + return + # check if the dir exists, and if not, ask if we should create it? + + # Clear our status cache, & re-check what's installed + self._ds.download_dir = download_dir + try: + self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + self._show_info() + + def _show_info(self): + print("showing info", self._ds.url) + for entry, cb in self._info.values(): + entry["state"] = "normal" + entry.delete(0, "end") + self._info["url"][0].insert(0, self._ds.url) + self._info["download_dir"][0].insert(0, self._ds.download_dir) + for entry, cb in self._info.values(): + entry["state"] = "disabled" + + def _prev_tab(self, *e): + for i, tab in enumerate(self._tab_names): + if tab.lower() == self._tab and i > 0: + self._tab = self._tab_names[i - 1].lower() + try: + return self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + + def _next_tab(self, *e): + for i, tab in enumerate(self._tab_names): + if tab.lower() == self._tab and i < (len(self._tabs) - 1): + self._tab = self._tab_names[i + 1].lower() + try: + return self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + + def _select_tab(self, event): + self._tab = event.widget["text"].lower() + try: + self._fill_table() + except HTTPError as e: + showerror("Error reading from server", e) + except URLError as e: + showerror("Error connecting to server", e.reason) + + _tab = "collections" + # _tab = 'corpora' + _rows = None + + def _fill_table(self): + selected_row = self._table.selected_row() + self._table.clear() + if self._tab == "all packages": + items = self._ds.packages() + elif self._tab == "corpora": + items = self._ds.corpora() + elif self._tab == "models": + items = self._ds.models() + elif self._tab == "collections": + items = self._ds.collections() + else: + assert 0, "bad tab value %r" % self._tab + rows = [self._package_to_columns(item) for item in items] + self._table.extend(rows) + + # Highlight the active tab. + for tab, label in self._tabs.items(): + if tab == self._tab: + label.configure( + foreground=self._FRONT_TAB_COLOR[0], + background=self._FRONT_TAB_COLOR[1], + ) + else: + label.configure( + foreground=self._BACK_TAB_COLOR[0], + background=self._BACK_TAB_COLOR[1], + ) + + self._table.sort_by("Identifier", order="ascending") + self._color_table() + self._table.select(selected_row) + + # This is a hack, because the scrollbar isn't updating its + # position right -- I'm not sure what the underlying cause is + # though. (This is on OS X w/ python 2.5) The length of + # delay that's necessary seems to depend on how fast the + # comptuer is. :-/ + self.top.after(150, self._table._scrollbar.set, *self._table._mlb.yview()) + self.top.after(300, self._table._scrollbar.set, *self._table._mlb.yview()) + + def _update_table_status(self): + for row_num in range(len(self._table)): + status = self._ds.status(self._table[row_num, "Identifier"]) + self._table[row_num, "Status"] = status + self._color_table() + + def _download(self, *e): + # If we're using threads, then delegate to the threaded + # downloader instead. + if self._use_threads: + return self._download_threaded(*e) + + marked = [ + self._table[row, "Identifier"] + for row in range(len(self._table)) + if self._table[row, 0] != "" + ] + selection = self._table.selected_row() + if not marked and selection is not None: + marked = [self._table[selection, "Identifier"]] + + download_iter = self._ds.incr_download(marked, self._ds.download_dir) + self._log_indent = 0 + self._download_cb(download_iter, marked) + + _DL_DELAY = 10 + + def _download_cb(self, download_iter, ids): + try: + msg = next(download_iter) + except StopIteration: + # self._fill_table(sort=False) + self._update_table_status() + afterid = self.top.after(10, self._show_progress, 0) + self._afterid["_download_cb"] = afterid + return + + def show(s): + self._progresslabel["text"] = s + self._log(s) + + if isinstance(msg, ProgressMessage): + self._show_progress(msg.progress) + elif isinstance(msg, ErrorMessage): + show(msg.message) + if msg.package is not None: + self._select(msg.package.id) + self._show_progress(None) + return # halt progress. + elif isinstance(msg, StartCollectionMessage): + show("Downloading collection %s" % msg.collection.id) + self._log_indent += 1 + elif isinstance(msg, StartPackageMessage): + show("Downloading package %s" % msg.package.id) + elif isinstance(msg, UpToDateMessage): + show("Package %s is up-to-date!" % msg.package.id) + # elif isinstance(msg, StaleMessage): + # show('Package %s is out-of-date or corrupt' % msg.package.id) + elif isinstance(msg, FinishDownloadMessage): + show("Finished downloading %r." % msg.package.id) + elif isinstance(msg, StartUnzipMessage): + show("Unzipping %s" % msg.package.filename) + elif isinstance(msg, FinishCollectionMessage): + self._log_indent -= 1 + show("Finished downloading collection %r." % msg.collection.id) + self._clear_mark(msg.collection.id) + elif isinstance(msg, FinishPackageMessage): + self._clear_mark(msg.package.id) + afterid = self.top.after(self._DL_DELAY, self._download_cb, download_iter, ids) + self._afterid["_download_cb"] = afterid + + def _select(self, id): + for row in range(len(self._table)): + if self._table[row, "Identifier"] == id: + self._table.select(row) + return + + def _color_table(self): + # Color rows according to status. + for row in range(len(self._table)): + bg, sbg = self._ROW_COLOR[self._table[row, "Status"]] + fg, sfg = ("black", "white") + self._table.rowconfig( + row, + foreground=fg, + selectforeground=sfg, + background=bg, + selectbackground=sbg, + ) + # Color the marked column + self._table.itemconfigure( + row, 0, foreground=self._MARK_COLOR[0], background=self._MARK_COLOR[1] + ) + + def _clear_mark(self, id): + for row in range(len(self._table)): + if self._table[row, "Identifier"] == id: + self._table[row, 0] = "" + + def _mark_all(self, *e): + for row in range(len(self._table)): + self._table[row, 0] = "X" + + def _table_mark(self, *e): + selection = self._table.selected_row() + if selection >= 0: + if self._table[selection][0] != "": + self._table[selection, 0] = "" + else: + self._table[selection, 0] = "X" + self._table.select(delta=1) + + def _show_log(self): + text = "\n".join(self._log_messages) + ShowText(self.top, "NLTK Downloader Log", text) + + def _package_to_columns(self, pkg): + """ + Given a package, return a list of values describing that + package, one for each column in ``self.COLUMNS``. + """ + row = [] + for column_index, column_name in enumerate(self.COLUMNS): + if column_index == 0: # Mark: + row.append("") + elif column_name == "Identifier": + row.append(pkg.id) + elif column_name == "Status": + row.append(self._ds.status(pkg)) + else: + attr = column_name.lower().replace(" ", "_") + row.append(getattr(pkg, attr, "n/a")) + return row + + # ///////////////////////////////////////////////////////////////// + # External Interface + # ///////////////////////////////////////////////////////////////// + + def destroy(self, *e): + if self._destroyed: + return + self.top.destroy() + self._destroyed = True + + def _destroy(self, *e): + if self.top is not None: + for afterid in self._afterid.values(): + self.top.after_cancel(afterid) + + # Abort any download in progress. + if self._downloading and self._use_threads: + self._abort_download() + + # Make sure the garbage collector destroys these now; + # otherwise, they may get destroyed when we're not in the main + # thread, which would make Tkinter unhappy. + self._column_vars.clear() + + def mainloop(self, *args, **kwargs): + self.top.mainloop(*args, **kwargs) + + # ///////////////////////////////////////////////////////////////// + # HELP + # ///////////////////////////////////////////////////////////////// + + HELP = textwrap.dedent( + """\ + This tool can be used to download a variety of corpora and models + that can be used with NLTK. Each corpus or model is distributed + in a single zip file, known as a \"package file.\" You can + download packages individually, or you can download pre-defined + collections of packages. + + When you download a package, it will be saved to the \"download + directory.\" A default download directory is chosen when you run + + the downloader; but you may also select a different download + directory. On Windows, the default download directory is + + + \"package.\" + + The NLTK downloader can be used to download a variety of corpora, + models, and other data packages. + + Keyboard shortcuts:: + [return]\t Download + [up]\t Select previous package + [down]\t Select next package + [left]\t Select previous tab + [right]\t Select next tab + """ + ) + + def help(self, *e): + # The default font's not very legible; try using 'fixed' instead. + try: + ShowText( + self.top, + "Help: NLTK Downloader", + self.HELP.strip(), + width=75, + font="fixed", + ) + except: + ShowText(self.top, "Help: NLTK Downloader", self.HELP.strip(), width=75) + + def about(self, *e): + ABOUT = "NLTK Downloader\n" + "Written by Edward Loper" + TITLE = "About: NLTK Downloader" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE).show() + except ImportError: + ShowText(self.top, TITLE, ABOUT) + + # ///////////////////////////////////////////////////////////////// + # Progress Bar + # ///////////////////////////////////////////////////////////////// + + _gradient_width = 5 + + def _init_progressbar(self): + c = self._progressbar + width, height = int(c["width"]), int(c["height"]) + for i in range(0, (int(c["width"]) * 2) // self._gradient_width): + c.create_line( + i * self._gradient_width + 20, + -20, + i * self._gradient_width - height - 20, + height + 20, + width=self._gradient_width, + fill="#%02x0000" % (80 + abs(i % 6 - 3) * 12), + ) + c.addtag_all("gradient") + c.itemconfig("gradient", state="hidden") + + # This is used to display progress + c.addtag_withtag( + "redbox", c.create_rectangle(0, 0, 0, 0, fill=self._PROGRESS_COLOR[0]) + ) + + def _show_progress(self, percent): + c = self._progressbar + if percent is None: + c.coords("redbox", 0, 0, 0, 0) + c.itemconfig("gradient", state="hidden") + else: + width, height = int(c["width"]), int(c["height"]) + x = percent * int(width) // 100 + 1 + c.coords("redbox", 0, 0, x, height + 1) + + def _progress_alive(self): + c = self._progressbar + if not self._downloading: + c.itemconfig("gradient", state="hidden") + else: + c.itemconfig("gradient", state="normal") + x1, y1, x2, y2 = c.bbox("gradient") + if x1 <= -100: + c.move("gradient", (self._gradient_width * 6) - 4, 0) + else: + c.move("gradient", -4, 0) + afterid = self.top.after(200, self._progress_alive) + self._afterid["_progress_alive"] = afterid + + # ///////////////////////////////////////////////////////////////// + # Threaded downloader + # ///////////////////////////////////////////////////////////////// + + def _download_threaded(self, *e): + # If the user tries to start a new download while we're already + # downloading something, then abort the current download instead. + if self._downloading: + self._abort_download() + return + + # Change the 'download' button to an 'abort' button. + self._download_button["text"] = "Cancel" + + marked = [ + self._table[row, "Identifier"] + for row in range(len(self._table)) + if self._table[row, 0] != "" + ] + selection = self._table.selected_row() + if not marked and selection is not None: + marked = [self._table[selection, "Identifier"]] + + # Create a new data server object for the download operation, + # just in case the user modifies our data server during the + # download (e.g., clicking 'refresh' or editing the index url). + ds = Downloader(self._ds.url, self._ds.download_dir) + + # Start downloading in a separate thread. + assert self._download_msg_queue == [] + assert self._download_abort_queue == [] + self._DownloadThread( + ds, + marked, + self._download_lock, + self._download_msg_queue, + self._download_abort_queue, + ).start() + + # Monitor the download message queue & display its progress. + self._log_indent = 0 + self._downloading = True + self._monitor_message_queue() + + # Display an indication that we're still alive and well by + # cycling the progress bar. + self._progress_alive() + + def _abort_download(self): + if self._downloading: + self._download_lock.acquire() + self._download_abort_queue.append("abort") + self._download_lock.release() + + class _DownloadThread(threading.Thread): + def __init__(self, data_server, items, lock, message_queue, abort): + self.data_server = data_server + self.items = items + self.lock = lock + self.message_queue = message_queue + self.abort = abort + threading.Thread.__init__(self) + + def run(self): + for msg in self.data_server.incr_download(self.items): + self.lock.acquire() + self.message_queue.append(msg) + # Check if we've been told to kill ourselves: + if self.abort: + self.message_queue.append("aborted") + self.lock.release() + return + self.lock.release() + self.lock.acquire() + self.message_queue.append("finished") + self.lock.release() + + _MONITOR_QUEUE_DELAY = 100 + + def _monitor_message_queue(self): + def show(s): + self._progresslabel["text"] = s + self._log(s) + + # Try to acquire the lock; if it's busy, then just try again later. + if not self._download_lock.acquire(): + return + for msg in self._download_msg_queue: + + # Done downloading? + if msg == "finished" or msg == "aborted": + # self._fill_table(sort=False) + self._update_table_status() + self._downloading = False + self._download_button["text"] = "Download" + del self._download_msg_queue[:] + del self._download_abort_queue[:] + self._download_lock.release() + if msg == "aborted": + show("Download aborted!") + self._show_progress(None) + else: + afterid = self.top.after(100, self._show_progress, None) + self._afterid["_monitor_message_queue"] = afterid + return + + # All other messages + elif isinstance(msg, ProgressMessage): + self._show_progress(msg.progress) + elif isinstance(msg, ErrorMessage): + show(msg.message) + if msg.package is not None: + self._select(msg.package.id) + self._show_progress(None) + self._downloading = False + return # halt progress. + elif isinstance(msg, StartCollectionMessage): + show("Downloading collection %r" % msg.collection.id) + self._log_indent += 1 + elif isinstance(msg, StartPackageMessage): + self._ds.clear_status_cache(msg.package.id) + show("Downloading package %r" % msg.package.id) + elif isinstance(msg, UpToDateMessage): + show("Package %s is up-to-date!" % msg.package.id) + # elif isinstance(msg, StaleMessage): + # show('Package %s is out-of-date or corrupt; updating it' % + # msg.package.id) + elif isinstance(msg, FinishDownloadMessage): + show("Finished downloading %r." % msg.package.id) + elif isinstance(msg, StartUnzipMessage): + show("Unzipping %s" % msg.package.filename) + elif isinstance(msg, FinishUnzipMessage): + show("Finished installing %s" % msg.package.id) + elif isinstance(msg, FinishCollectionMessage): + self._log_indent -= 1 + show("Finished downloading collection %r." % msg.collection.id) + self._clear_mark(msg.collection.id) + elif isinstance(msg, FinishPackageMessage): + self._update_table_status() + self._clear_mark(msg.package.id) + + # Let the user know when we're aborting a download (but + # waiting for a good point to abort it, so we don't end up + # with a partially unzipped package or anything like that). + if self._download_abort_queue: + self._progresslabel["text"] = "Aborting download..." + + # Clear the message queue and then release the lock + del self._download_msg_queue[:] + self._download_lock.release() + + # Check the queue again after MONITOR_QUEUE_DELAY msec. + afterid = self.top.after(self._MONITOR_QUEUE_DELAY, self._monitor_message_queue) + self._afterid["_monitor_message_queue"] = afterid + + +###################################################################### +# Helper Functions +###################################################################### +# [xx] It may make sense to move these to nltk.internals. + + +def md5_hexdigest(file): + """ + Calculate and return the MD5 checksum for a given file. + ``file`` may either be a filename or an open stream. + """ + if isinstance(file, str): + with open(file, "rb") as infile: + return _md5_hexdigest(infile) + return _md5_hexdigest(file) + + +def _md5_hexdigest(fp): + md5_digest = md5() + while True: + block = fp.read(1024 * 16) # 16k blocks + if not block: + break + md5_digest.update(block) + return md5_digest.hexdigest() + + +# change this to periodically yield progress messages? +# [xx] get rid of topdir parameter -- we should be checking +# this when we build the index, anyway. +def unzip(filename, root, verbose=True): + """ + Extract the contents of the zip file ``filename`` into the + directory ``root``. + """ + for message in _unzip_iter(filename, root, verbose): + if isinstance(message, ErrorMessage): + raise Exception(message) + + +def _unzip_iter(filename, root, verbose=True): + if verbose: + sys.stdout.write("Unzipping %s" % os.path.split(filename)[1]) + sys.stdout.flush() + + try: + zf = zipfile.ZipFile(filename) + except zipfile.error as e: + yield ErrorMessage(filename, "Error with downloaded zip file") + return + except Exception as e: + yield ErrorMessage(filename, e) + return + + zf.extractall(root) + + if verbose: + print() + + +###################################################################### +# Index Builder +###################################################################### +# This may move to a different file sometime. + + +def build_index(root, base_url): + """ + Create a new data.xml index file, by combining the xml description + files for various packages and collections. ``root`` should be the + path to a directory containing the package xml and zip files; and + the collection xml files. The ``root`` directory is expected to + have the following subdirectories:: + + root/ + packages/ .................. subdirectory for packages + corpora/ ................. zip & xml files for corpora + grammars/ ................ zip & xml files for grammars + taggers/ ................. zip & xml files for taggers + tokenizers/ .............. zip & xml files for tokenizers + etc. + collections/ ............... xml files for collections + + For each package, there should be two files: ``package.zip`` + (where *package* is the package name) + which contains the package itself as a compressed zip file; and + ``package.xml``, which is an xml description of the package. The + zipfile ``package.zip`` should expand to a single subdirectory + named ``package/``. The base filename ``package`` must match + the identifier given in the package's xml file. + + For each collection, there should be a single file ``collection.zip`` + describing the collection, where *collection* is the name of the collection. + + All identifiers (for both packages and collections) must be unique. + """ + # Find all packages. + packages = [] + for pkg_xml, zf, subdir in _find_packages(os.path.join(root, "packages")): + zipstat = os.stat(zf.filename) + url = f"{base_url}/{subdir}/{os.path.split(zf.filename)[1]}" + unzipped_size = sum(zf_info.file_size for zf_info in zf.infolist()) + + # Fill in several fields of the package xml with calculated values. + pkg_xml.set("unzipped_size", "%s" % unzipped_size) + pkg_xml.set("size", "%s" % zipstat.st_size) + pkg_xml.set("checksum", "%s" % md5_hexdigest(zf.filename)) + pkg_xml.set("subdir", subdir) + # pkg_xml.set('svn_revision', _svn_revision(zf.filename)) + if not pkg_xml.get("url"): + pkg_xml.set("url", url) + + # Record the package. + packages.append(pkg_xml) + + # Find all collections + collections = list(_find_collections(os.path.join(root, "collections"))) + + # Check that all UIDs are unique + uids = set() + for item in packages + collections: + if item.get("id") in uids: + raise ValueError("Duplicate UID: %s" % item.get("id")) + uids.add(item.get("id")) + + # Put it all together + top_elt = ElementTree.Element("nltk_data") + top_elt.append(ElementTree.Element("packages")) + top_elt[0].extend(sorted(packages, key=lambda package: package.get("id"))) + top_elt.append(ElementTree.Element("collections")) + top_elt[1].extend(sorted(collections, key=lambda collection: collection.get("id"))) + + _indent_xml(top_elt) + return top_elt + + +def _indent_xml(xml, prefix=""): + """ + Helper for ``build_index()``: Given an XML ``ElementTree``, modify it + (and its descendents) ``text`` and ``tail`` attributes to generate + an indented tree, where each nested element is indented by 2 + spaces with respect to its parent. + """ + if len(xml) > 0: + xml.text = (xml.text or "").strip() + "\n" + prefix + " " + for child in xml: + _indent_xml(child, prefix + " ") + for child in xml[:-1]: + child.tail = (child.tail or "").strip() + "\n" + prefix + " " + xml[-1].tail = (xml[-1].tail or "").strip() + "\n" + prefix + + +def _check_package(pkg_xml, zipfilename, zf): + """ + Helper for ``build_index()``: Perform some checks to make sure that + the given package is consistent. + """ + # The filename must patch the id given in the XML file. + uid = os.path.splitext(os.path.split(zipfilename)[1])[0] + if pkg_xml.get("id") != uid: + raise ValueError( + "package identifier mismatch ({} vs {})".format(pkg_xml.get("id"), uid) + ) + + # Zip file must expand to a subdir whose name matches uid. + if sum((name != uid and not name.startswith(uid + "/")) for name in zf.namelist()): + raise ValueError( + "Zipfile %s.zip does not expand to a single " + "subdirectory %s/" % (uid, uid) + ) + + +# update for git? +def _svn_revision(filename): + """ + Helper for ``build_index()``: Calculate the subversion revision + number for a given file (by using ``subprocess`` to run ``svn``). + """ + p = subprocess.Popen( + ["svn", "status", "-v", filename], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + (stdout, stderr) = p.communicate() + if p.returncode != 0 or stderr or not stdout: + raise ValueError( + "Error determining svn_revision for %s: %s" + % (os.path.split(filename)[1], textwrap.fill(stderr)) + ) + return stdout.split()[2] + + +def _find_collections(root): + """ + Helper for ``build_index()``: Yield a list of ElementTree.Element + objects, each holding the xml for a single package collection. + """ + for dirname, _subdirs, files in os.walk(root): + for filename in files: + if filename.endswith(".xml"): + xmlfile = os.path.join(dirname, filename) + yield ElementTree.parse(xmlfile).getroot() + + +def _find_packages(root): + """ + Helper for ``build_index()``: Yield a list of tuples + ``(pkg_xml, zf, subdir)``, where: + - ``pkg_xml`` is an ``ElementTree.Element`` holding the xml for a + package + - ``zf`` is a ``zipfile.ZipFile`` for the package's contents. + - ``subdir`` is the subdirectory (relative to ``root``) where + the package was found (e.g. 'corpora' or 'grammars'). + """ + from nltk.corpus.reader.util import _path_from + + # Find all packages. + packages = [] + for dirname, subdirs, files in os.walk(root): + relpath = "/".join(_path_from(root, dirname)) + for filename in files: + if filename.endswith(".xml"): + xmlfilename = os.path.join(dirname, filename) + zipfilename = xmlfilename[:-4] + ".zip" + try: + zf = zipfile.ZipFile(zipfilename) + except Exception as e: + raise ValueError(f"Error reading file {zipfilename!r}!\n{e}") from e + try: + pkg_xml = ElementTree.parse(xmlfilename).getroot() + except Exception as e: + raise ValueError(f"Error reading file {xmlfilename!r}!\n{e}") from e + + # Check that the UID matches the filename + uid = os.path.split(xmlfilename[:-4])[1] + if pkg_xml.get("id") != uid: + raise ValueError( + "package identifier mismatch (%s " + "vs %s)" % (pkg_xml.get("id"), uid) + ) + + # Check that the zipfile expands to a subdir whose + # name matches the uid. + if sum( + (name != uid and not name.startswith(uid + "/")) + for name in zf.namelist() + ): + raise ValueError( + "Zipfile %s.zip does not expand to a " + "single subdirectory %s/" % (uid, uid) + ) + + yield pkg_xml, zf, relpath + + elif filename.endswith(".zip"): + # Warn user in case a .xml does not exist for a .zip + resourcename = os.path.splitext(filename)[0] + xmlfilename = os.path.join(dirname, resourcename + ".xml") + if not os.path.exists(xmlfilename): + warnings.warn( + f"{filename} exists, but {resourcename + '.xml'} cannot be found! " + f"This could mean that {resourcename} can not be downloaded.", + stacklevel=2, + ) + + # Don't recurse into svn subdirectories: + try: + subdirs.remove(".svn") + except ValueError: + pass + + +###################################################################### +# Main: +###################################################################### + +# There should be a command-line interface + +# Aliases +_downloader = Downloader() +download = _downloader.download + + +def download_shell(): + DownloaderShell(_downloader).run() + + +def download_gui(): + DownloaderGUI(_downloader).mainloop() + + +def update(): + _downloader.update() + + +if __name__ == "__main__": + from optparse import OptionParser + + parser = OptionParser() + parser.add_option( + "-d", + "--dir", + dest="dir", + help="download package to directory DIR", + metavar="DIR", + ) + parser.add_option( + "-q", + "--quiet", + dest="quiet", + action="store_true", + default=False, + help="work quietly", + ) + parser.add_option( + "-f", + "--force", + dest="force", + action="store_true", + default=False, + help="download even if already installed", + ) + parser.add_option( + "-e", + "--exit-on-error", + dest="halt_on_error", + action="store_true", + default=False, + help="exit if an error occurs", + ) + parser.add_option( + "-u", + "--url", + dest="server_index_url", + default=os.environ.get("NLTK_DOWNLOAD_URL"), + help="download server index url", + ) + + (options, args) = parser.parse_args() + + downloader = Downloader(server_index_url=options.server_index_url) + + if args: + for pkg_id in args: + rv = downloader.download( + info_or_id=pkg_id, + download_dir=options.dir, + quiet=options.quiet, + force=options.force, + halt_on_error=options.halt_on_error, + ) + if rv == False and options.halt_on_error: + break + else: + downloader.download( + download_dir=options.dir, + quiet=options.quiet, + force=options.force, + halt_on_error=options.halt_on_error, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__init__.py new file mode 100644 index 0000000..c48564d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__init__.py @@ -0,0 +1,27 @@ +# Natural Language Toolkit: graphical representations package +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +# Import Tkinter-based modules if Tkinter is installed +try: + import tkinter +except ImportError: + import warnings + + warnings.warn("nltk.draw package not loaded (please install Tkinter library).") +else: + from nltk.draw.cfg import ProductionList, CFGEditor, CFGDemo + from nltk.draw.tree import ( + TreeSegmentWidget, + tree_to_treesegment, + TreeWidget, + TreeView, + draw_trees, + ) + from nltk.draw.table import Table + +from nltk.draw.dispersion import dispersion_plot diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..66981bb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/cfg.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/cfg.cpython-310.pyc new file mode 100644 index 0000000..2c96bf9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/cfg.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/dispersion.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/dispersion.cpython-310.pyc new file mode 100644 index 0000000..e3eff9b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/dispersion.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/table.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/table.cpython-310.pyc new file mode 100644 index 0000000..d39548d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/table.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/tree.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/tree.cpython-310.pyc new file mode 100644 index 0000000..c5facb7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/tree.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..719d281 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/cfg.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/cfg.py new file mode 100644 index 0000000..568437f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/cfg.py @@ -0,0 +1,859 @@ +# Natural Language Toolkit: CFG visualization +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Visualization tools for CFGs. +""" + +# Idea for a nice demo: +# - 3 panes: grammar, treelet, working area +# - grammar is a list of productions +# - when you select a production, the treelet that it licenses appears +# in the treelet area +# - the working area has the text on the bottom, and S at top. When +# you select a production, it shows (ghosted) the locations where +# that production's treelet could be attached to either the text +# or the tree rooted at S. +# - the user can drag the treelet onto one of those (or click on them?) +# - the user can delete pieces of the tree from the working area +# (right click?) +# - connecting top to bottom? drag one NP onto another? +# +# +-------------------------------------------------------------+ +# | S -> NP VP | S | +# |[NP -> Det N ]| / \ | +# | ... | NP VP | +# | N -> 'dog' | | +# | N -> 'cat' | | +# | ... | | +# +--------------+ | +# | NP | Det N | +# | / \ | | | | +# | Det N | the cat saw the dog | +# | | | +# +--------------+----------------------------------------------+ +# +# Operations: +# - connect a new treelet -- drag or click shadow +# - delete a treelet -- right click +# - if only connected to top, delete everything below +# - if only connected to bottom, delete everything above +# - connect top & bottom -- drag a leaf to a root or a root to a leaf +# - disconnect top & bottom -- right click +# - if connected to top & bottom, then disconnect + +import re +from tkinter import ( + Button, + Canvas, + Entry, + Frame, + IntVar, + Label, + Scrollbar, + Text, + Tk, + Toplevel, +) + +from nltk.draw.tree import TreeSegmentWidget, tree_to_treesegment +from nltk.draw.util import ( + CanvasFrame, + ColorizedList, + ShowText, + SymbolWidget, + TextWidget, +) +from nltk.grammar import CFG, Nonterminal, _read_cfg_production, nonterminals +from nltk.tree import Tree + +###################################################################### +# Production List +###################################################################### + + +class ProductionList(ColorizedList): + ARROW = SymbolWidget.SYMBOLS["rightarrow"] + + def _init_colortags(self, textwidget, options): + textwidget.tag_config("terminal", foreground="#006000") + textwidget.tag_config("arrow", font="symbol", underline="0") + textwidget.tag_config( + "nonterminal", foreground="blue", font=("helvetica", -12, "bold") + ) + + def _item_repr(self, item): + contents = [] + contents.append(("%s\t" % item.lhs(), "nonterminal")) + contents.append((self.ARROW, "arrow")) + for elt in item.rhs(): + if isinstance(elt, Nonterminal): + contents.append((" %s" % elt.symbol(), "nonterminal")) + else: + contents.append((" %r" % elt, "terminal")) + return contents + + +###################################################################### +# CFG Editor +###################################################################### + +_CFGEditor_HELP = """ + +The CFG Editor can be used to create or modify context free grammars. +A context free grammar consists of a start symbol and a list of +productions. The start symbol is specified by the text entry field in +the upper right hand corner of the editor; and the list of productions +are specified in the main text editing box. + +Every non-blank line specifies a single production. Each production +has the form "LHS -> RHS," where LHS is a single nonterminal, and RHS +is a list of nonterminals and terminals. + +Nonterminals must be a single word, such as S or NP or NP_subj. +Currently, nonterminals must consists of alphanumeric characters and +underscores (_). Nonterminals are colored blue. If you place the +mouse over any nonterminal, then all occurrences of that nonterminal +will be highlighted. + +Terminals must be surrounded by single quotes (') or double +quotes(\"). For example, "dog" and "New York" are terminals. +Currently, the string within the quotes must consist of alphanumeric +characters, underscores, and spaces. + +To enter a new production, go to a blank line, and type a nonterminal, +followed by an arrow (->), followed by a sequence of terminals and +nonterminals. Note that "->" (dash + greater-than) is automatically +converted to an arrow symbol. When you move your cursor to a +different line, your production will automatically be colorized. If +there are any errors, they will be highlighted in red. + +Note that the order of the productions is significant for some +algorithms. To re-order the productions, use cut and paste to move +them. + +Use the buttons at the bottom of the window when you are done editing +the CFG: + - Ok: apply the new CFG, and exit the editor. + - Apply: apply the new CFG, and do not exit the editor. + - Reset: revert to the original CFG, and do not exit the editor. + - Cancel: revert to the original CFG, and exit the editor. + +""" + + +class CFGEditor: + """ + A dialog window for creating and editing context free grammars. + ``CFGEditor`` imposes the following restrictions: + + - All nonterminals must be strings consisting of word + characters. + - All terminals must be strings consisting of word characters + and space characters. + """ + + # Regular expressions used by _analyze_line. Precompile them, so + # we can process the text faster. + ARROW = SymbolWidget.SYMBOLS["rightarrow"] + _LHS_RE = re.compile(r"(^\s*\w+\s*)(->|(" + ARROW + "))") + _ARROW_RE = re.compile(r"\s*(->|(" + ARROW + r"))\s*") + _PRODUCTION_RE = re.compile( + r"(^\s*\w+\s*)" + + "(->|(" # LHS + + ARROW + + r"))\s*" + + r"((\w+|'[\w ]*'|\"[\w ]*\"|\|)\s*)*$" # arrow + ) # RHS + _TOKEN_RE = re.compile("\\w+|->|'[\\w ]+'|\"[\\w ]+\"|(" + ARROW + ")") + _BOLD = ("helvetica", -12, "bold") + + def __init__(self, parent, cfg=None, set_cfg_callback=None): + self._parent = parent + if cfg is not None: + self._cfg = cfg + else: + self._cfg = CFG(Nonterminal("S"), []) + self._set_cfg_callback = set_cfg_callback + + self._highlight_matching_nonterminals = 1 + + # Create the top-level window. + self._top = Toplevel(parent) + self._init_bindings() + + self._init_startframe() + self._startframe.pack(side="top", fill="x", expand=0) + self._init_prodframe() + self._prodframe.pack(side="top", fill="both", expand=1) + self._init_buttons() + self._buttonframe.pack(side="bottom", fill="x", expand=0) + + self._textwidget.focus() + + def _init_startframe(self): + frame = self._startframe = Frame(self._top) + self._start = Entry(frame) + self._start.pack(side="right") + Label(frame, text="Start Symbol:").pack(side="right") + Label(frame, text="Productions:").pack(side="left") + self._start.insert(0, self._cfg.start().symbol()) + + def _init_buttons(self): + frame = self._buttonframe = Frame(self._top) + Button(frame, text="Ok", command=self._ok, underline=0, takefocus=0).pack( + side="left" + ) + Button(frame, text="Apply", command=self._apply, underline=0, takefocus=0).pack( + side="left" + ) + Button(frame, text="Reset", command=self._reset, underline=0, takefocus=0).pack( + side="left" + ) + Button( + frame, text="Cancel", command=self._cancel, underline=0, takefocus=0 + ).pack(side="left") + Button(frame, text="Help", command=self._help, underline=0, takefocus=0).pack( + side="right" + ) + + def _init_bindings(self): + self._top.title("CFG Editor") + self._top.bind("", self._cancel) + self._top.bind("", self._cancel) + self._top.bind("", self._cancel) + # self._top.bind('', self._cancel) + self._top.bind("", self._cancel) + self._top.bind("", self._cancel) + # self._top.bind('', self._cancel) + self._top.bind("", self._cancel) + + self._top.bind("", self._ok) + self._top.bind("", self._ok) + self._top.bind("", self._apply) + self._top.bind("", self._apply) + self._top.bind("", self._reset) + self._top.bind("", self._reset) + self._top.bind("", self._help) + self._top.bind("", self._help) + self._top.bind("", self._help) + + def _init_prodframe(self): + self._prodframe = Frame(self._top) + + # Create the basic Text widget & scrollbar. + self._textwidget = Text( + self._prodframe, background="#e0e0e0", exportselection=1 + ) + self._textscroll = Scrollbar(self._prodframe, takefocus=0, orient="vertical") + self._textwidget.config(yscrollcommand=self._textscroll.set) + self._textscroll.config(command=self._textwidget.yview) + self._textscroll.pack(side="right", fill="y") + self._textwidget.pack(expand=1, fill="both", side="left") + + # Initialize the colorization tags. Each nonterminal gets its + # own tag, so they aren't listed here. + self._textwidget.tag_config("terminal", foreground="#006000") + self._textwidget.tag_config("arrow", font="symbol") + self._textwidget.tag_config("error", background="red") + + # Keep track of what line they're on. We use that to remember + # to re-analyze a line whenever they leave it. + self._linenum = 0 + + # Expand "->" to an arrow. + self._top.bind(">", self._replace_arrows) + + # Re-colorize lines when appropriate. + self._top.bind("<>", self._analyze) + self._top.bind("", self._check_analyze) + self._top.bind("", self._check_analyze) + + # Tab cycles focus. (why doesn't this work??) + def cycle(e, textwidget=self._textwidget): + textwidget.tk_focusNext().focus() + + self._textwidget.bind("", cycle) + + prod_tuples = [(p.lhs(), [p.rhs()]) for p in self._cfg.productions()] + for i in range(len(prod_tuples) - 1, 0, -1): + if prod_tuples[i][0] == prod_tuples[i - 1][0]: + if () in prod_tuples[i][1]: + continue + if () in prod_tuples[i - 1][1]: + continue + print(prod_tuples[i - 1][1]) + print(prod_tuples[i][1]) + prod_tuples[i - 1][1].extend(prod_tuples[i][1]) + del prod_tuples[i] + + for lhs, rhss in prod_tuples: + print(lhs, rhss) + s = "%s ->" % lhs + for rhs in rhss: + for elt in rhs: + if isinstance(elt, Nonterminal): + s += " %s" % elt + else: + s += " %r" % elt + s += " |" + s = s[:-2] + "\n" + self._textwidget.insert("end", s) + + self._analyze() + + # # Add the producitons to the text widget, and colorize them. + # prod_by_lhs = {} + # for prod in self._cfg.productions(): + # if len(prod.rhs()) > 0: + # prod_by_lhs.setdefault(prod.lhs(),[]).append(prod) + # for (lhs, prods) in prod_by_lhs.items(): + # self._textwidget.insert('end', '%s ->' % lhs) + # self._textwidget.insert('end', self._rhs(prods[0])) + # for prod in prods[1:]: + # print '\t|'+self._rhs(prod), + # self._textwidget.insert('end', '\t|'+self._rhs(prod)) + # print + # self._textwidget.insert('end', '\n') + # for prod in self._cfg.productions(): + # if len(prod.rhs()) == 0: + # self._textwidget.insert('end', '%s' % prod) + # self._analyze() + + # def _rhs(self, prod): + # s = '' + # for elt in prod.rhs(): + # if isinstance(elt, Nonterminal): s += ' %s' % elt.symbol() + # else: s += ' %r' % elt + # return s + + def _clear_tags(self, linenum): + """ + Remove all tags (except ``arrow`` and ``sel``) from the given + line of the text widget used for editing the productions. + """ + start = "%d.0" % linenum + end = "%d.end" % linenum + for tag in self._textwidget.tag_names(): + if tag not in ("arrow", "sel"): + self._textwidget.tag_remove(tag, start, end) + + def _check_analyze(self, *e): + """ + Check if we've moved to a new line. If we have, then remove + all colorization from the line we moved to, and re-colorize + the line that we moved from. + """ + linenum = int(self._textwidget.index("insert").split(".")[0]) + if linenum != self._linenum: + self._clear_tags(linenum) + self._analyze_line(self._linenum) + self._linenum = linenum + + def _replace_arrows(self, *e): + """ + Replace any ``'->'`` text strings with arrows (char \\256, in + symbol font). This searches the whole buffer, but is fast + enough to be done anytime they press '>'. + """ + arrow = "1.0" + while True: + arrow = self._textwidget.search("->", arrow, "end+1char") + if arrow == "": + break + self._textwidget.delete(arrow, arrow + "+2char") + self._textwidget.insert(arrow, self.ARROW, "arrow") + self._textwidget.insert(arrow, "\t") + + arrow = "1.0" + while True: + arrow = self._textwidget.search(self.ARROW, arrow + "+1char", "end+1char") + if arrow == "": + break + self._textwidget.tag_add("arrow", arrow, arrow + "+1char") + + def _analyze_token(self, match, linenum): + """ + Given a line number and a regexp match for a token on that + line, colorize the token. Note that the regexp match gives us + the token's text, start index (on the line), and end index (on + the line). + """ + # What type of token is it? + if match.group()[0] in "'\"": + tag = "terminal" + elif match.group() in ("->", self.ARROW): + tag = "arrow" + else: + # If it's a nonterminal, then set up new bindings, so we + # can highlight all instances of that nonterminal when we + # put the mouse over it. + tag = "nonterminal_" + match.group() + if tag not in self._textwidget.tag_names(): + self._init_nonterminal_tag(tag) + + start = "%d.%d" % (linenum, match.start()) + end = "%d.%d" % (linenum, match.end()) + self._textwidget.tag_add(tag, start, end) + + def _init_nonterminal_tag(self, tag, foreground="blue"): + self._textwidget.tag_config(tag, foreground=foreground, font=CFGEditor._BOLD) + if not self._highlight_matching_nonterminals: + return + + def enter(e, textwidget=self._textwidget, tag=tag): + textwidget.tag_config(tag, background="#80ff80") + + def leave(e, textwidget=self._textwidget, tag=tag): + textwidget.tag_config(tag, background="") + + self._textwidget.tag_bind(tag, "", enter) + self._textwidget.tag_bind(tag, "", leave) + + def _analyze_line(self, linenum): + """ + Colorize a given line. + """ + # Get rid of any tags that were previously on the line. + self._clear_tags(linenum) + + # Get the line line's text string. + line = self._textwidget.get(repr(linenum) + ".0", repr(linenum) + ".end") + + # If it's a valid production, then colorize each token. + if CFGEditor._PRODUCTION_RE.match(line): + # It's valid; Use _TOKEN_RE to tokenize the production, + # and call analyze_token on each token. + def analyze_token(match, self=self, linenum=linenum): + self._analyze_token(match, linenum) + return "" + + CFGEditor._TOKEN_RE.sub(analyze_token, line) + elif line.strip() != "": + # It's invalid; show the user where the error is. + self._mark_error(linenum, line) + + def _mark_error(self, linenum, line): + """ + Mark the location of an error in a line. + """ + arrowmatch = CFGEditor._ARROW_RE.search(line) + if not arrowmatch: + # If there's no arrow at all, highlight the whole line. + start = "%d.0" % linenum + end = "%d.end" % linenum + elif not CFGEditor._LHS_RE.match(line): + # Otherwise, if the LHS is bad, highlight it. + start = "%d.0" % linenum + end = "%d.%d" % (linenum, arrowmatch.start()) + else: + # Otherwise, highlight the RHS. + start = "%d.%d" % (linenum, arrowmatch.end()) + end = "%d.end" % linenum + + # If we're highlighting 0 chars, highlight the whole line. + if self._textwidget.compare(start, "==", end): + start = "%d.0" % linenum + end = "%d.end" % linenum + self._textwidget.tag_add("error", start, end) + + def _analyze(self, *e): + """ + Replace ``->`` with arrows, and colorize the entire buffer. + """ + self._replace_arrows() + numlines = int(self._textwidget.index("end").split(".")[0]) + for linenum in range(1, numlines + 1): # line numbers start at 1. + self._analyze_line(linenum) + + def _parse_productions(self): + """ + Parse the current contents of the textwidget buffer, to create + a list of productions. + """ + productions = [] + + # Get the text, normalize it, and split it into lines. + text = self._textwidget.get("1.0", "end") + text = re.sub(self.ARROW, "->", text) + text = re.sub("\t", " ", text) + lines = text.split("\n") + + # Convert each line to a CFG production + for line in lines: + line = line.strip() + if line == "": + continue + productions += _read_cfg_production(line) + # if line.strip() == '': continue + # if not CFGEditor._PRODUCTION_RE.match(line): + # raise ValueError('Bad production string %r' % line) + # + # (lhs_str, rhs_str) = line.split('->') + # lhs = Nonterminal(lhs_str.strip()) + # rhs = [] + # def parse_token(match, rhs=rhs): + # token = match.group() + # if token[0] in "'\"": rhs.append(token[1:-1]) + # else: rhs.append(Nonterminal(token)) + # return '' + # CFGEditor._TOKEN_RE.sub(parse_token, rhs_str) + # + # productions.append(Production(lhs, *rhs)) + + return productions + + def _destroy(self, *e): + if self._top is None: + return + self._top.destroy() + self._top = None + + def _ok(self, *e): + self._apply() + self._destroy() + + def _apply(self, *e): + productions = self._parse_productions() + start = Nonterminal(self._start.get()) + cfg = CFG(start, productions) + if self._set_cfg_callback is not None: + self._set_cfg_callback(cfg) + + def _reset(self, *e): + self._textwidget.delete("1.0", "end") + for production in self._cfg.productions(): + self._textwidget.insert("end", "%s\n" % production) + self._analyze() + if self._set_cfg_callback is not None: + self._set_cfg_callback(self._cfg) + + def _cancel(self, *e): + try: + self._reset() + except: + pass + self._destroy() + + def _help(self, *e): + # The default font's not very legible; try using 'fixed' instead. + try: + ShowText( + self._parent, + "Help: Chart Parser Demo", + (_CFGEditor_HELP).strip(), + width=75, + font="fixed", + ) + except: + ShowText( + self._parent, + "Help: Chart Parser Demo", + (_CFGEditor_HELP).strip(), + width=75, + ) + + +###################################################################### +# New Demo (built tree based on cfg) +###################################################################### + + +class CFGDemo: + def __init__(self, grammar, text): + self._grammar = grammar + self._text = text + + # Set up the main window. + self._top = Tk() + self._top.title("Context Free Grammar Demo") + + # Base font size + self._size = IntVar(self._top) + self._size.set(12) # = medium + + # Set up the key bindings + self._init_bindings(self._top) + + # Create the basic frames + frame1 = Frame(self._top) + frame1.pack(side="left", fill="y", expand=0) + self._init_menubar(self._top) + self._init_buttons(self._top) + self._init_grammar(frame1) + self._init_treelet(frame1) + self._init_workspace(self._top) + + # ////////////////////////////////////////////////// + # Initialization + # ////////////////////////////////////////////////// + + def _init_bindings(self, top): + top.bind("", self.destroy) + + def _init_menubar(self, parent): + pass + + def _init_buttons(self, parent): + pass + + def _init_grammar(self, parent): + self._prodlist = ProductionList(parent, self._grammar, width=20) + self._prodlist.pack(side="top", fill="both", expand=1) + self._prodlist.focus() + self._prodlist.add_callback("select", self._selectprod_cb) + self._prodlist.add_callback("move", self._selectprod_cb) + + def _init_treelet(self, parent): + self._treelet_canvas = Canvas(parent, background="white") + self._treelet_canvas.pack(side="bottom", fill="x") + self._treelet = None + + def _init_workspace(self, parent): + self._workspace = CanvasFrame(parent, background="white") + self._workspace.pack(side="right", fill="both", expand=1) + self._tree = None + self.reset_workspace() + + # ////////////////////////////////////////////////// + # Workspace + # ////////////////////////////////////////////////// + + def reset_workspace(self): + c = self._workspace.canvas() + fontsize = int(self._size.get()) + node_font = ("helvetica", -(fontsize + 4), "bold") + leaf_font = ("helvetica", -(fontsize + 2)) + + # Remove the old tree + if self._tree is not None: + self._workspace.remove_widget(self._tree) + + # The root of the tree. + start = self._grammar.start().symbol() + rootnode = TextWidget(c, start, font=node_font, draggable=1) + + # The leaves of the tree. + leaves = [] + for word in self._text: + leaves.append(TextWidget(c, word, font=leaf_font, draggable=1)) + + # Put it all together into one tree + self._tree = TreeSegmentWidget(c, rootnode, leaves, color="white") + + # Add it to the workspace. + self._workspace.add_widget(self._tree) + + # Move the leaves to the bottom of the workspace. + for leaf in leaves: + leaf.move(0, 100) + + # self._nodes = {start:1} + # self._leaves = dict([(l,1) for l in leaves]) + + def workspace_markprod(self, production): + pass + + def _markproduction(self, prod, tree=None): + if tree is None: + tree = self._tree + for i in range(len(tree.subtrees()) - len(prod.rhs())): + if tree["color", i] == "white": + self._markproduction # FIXME: Is this necessary at all? + + for j, node in enumerate(prod.rhs()): + widget = tree.subtrees()[i + j] + if ( + isinstance(node, Nonterminal) + and isinstance(widget, TreeSegmentWidget) + and node.symbol == widget.label().text() + ): + pass # matching nonterminal + elif ( + isinstance(node, str) + and isinstance(widget, TextWidget) + and node == widget.text() + ): + pass # matching nonterminal + else: + break + else: + # Everything matched! + print("MATCH AT", i) + + # ////////////////////////////////////////////////// + # Grammar + # ////////////////////////////////////////////////// + + def _selectprod_cb(self, production): + canvas = self._treelet_canvas + + self._prodlist.highlight(production) + if self._treelet is not None: + self._treelet.destroy() + + # Convert the production to a tree. + rhs = production.rhs() + for (i, elt) in enumerate(rhs): + if isinstance(elt, Nonterminal): + elt = Tree(elt) + tree = Tree(production.lhs().symbol(), *rhs) + + # Draw the tree in the treelet area. + fontsize = int(self._size.get()) + node_font = ("helvetica", -(fontsize + 4), "bold") + leaf_font = ("helvetica", -(fontsize + 2)) + self._treelet = tree_to_treesegment( + canvas, tree, node_font=node_font, leaf_font=leaf_font + ) + self._treelet["draggable"] = 1 + + # Center the treelet. + (x1, y1, x2, y2) = self._treelet.bbox() + w, h = int(canvas["width"]), int(canvas["height"]) + self._treelet.move((w - x1 - x2) / 2, (h - y1 - y2) / 2) + + # Mark the places where we can add it to the workspace. + self._markproduction(production) + + def destroy(self, *args): + self._top.destroy() + + def mainloop(self, *args, **kwargs): + self._top.mainloop(*args, **kwargs) + + +def demo2(): + from nltk import CFG, Nonterminal, Production + + nonterminals = "S VP NP PP P N Name V Det" + (S, VP, NP, PP, P, N, Name, V, Det) = (Nonterminal(s) for s in nonterminals.split()) + productions = ( + # Syntactic Productions + Production(S, [NP, VP]), + Production(NP, [Det, N]), + Production(NP, [NP, PP]), + Production(VP, [VP, PP]), + Production(VP, [V, NP, PP]), + Production(VP, [V, NP]), + Production(PP, [P, NP]), + Production(PP, []), + Production(PP, ["up", "over", NP]), + # Lexical Productions + Production(NP, ["I"]), + Production(Det, ["the"]), + Production(Det, ["a"]), + Production(N, ["man"]), + Production(V, ["saw"]), + Production(P, ["in"]), + Production(P, ["with"]), + Production(N, ["park"]), + Production(N, ["dog"]), + Production(N, ["statue"]), + Production(Det, ["my"]), + ) + grammar = CFG(S, productions) + + text = "I saw a man in the park".split() + d = CFGDemo(grammar, text) + d.mainloop() + + +###################################################################### +# Old Demo +###################################################################### + + +def demo(): + from nltk import CFG, Nonterminal + + nonterminals = "S VP NP PP P N Name V Det" + (S, VP, NP, PP, P, N, Name, V, Det) = (Nonterminal(s) for s in nonterminals.split()) + + grammar = CFG.fromstring( + """ + S -> NP VP + PP -> P NP + NP -> Det N + NP -> NP PP + VP -> V NP + VP -> VP PP + Det -> 'a' + Det -> 'the' + Det -> 'my' + NP -> 'I' + N -> 'dog' + N -> 'man' + N -> 'park' + N -> 'statue' + V -> 'saw' + P -> 'in' + P -> 'up' + P -> 'over' + P -> 'with' + """ + ) + + def cb(grammar): + print(grammar) + + top = Tk() + editor = CFGEditor(top, grammar, cb) + Label(top, text="\nTesting CFG Editor\n").pack() + Button(top, text="Quit", command=top.destroy).pack() + top.mainloop() + + +def demo3(): + from nltk import Production + + (S, VP, NP, PP, P, N, Name, V, Det) = nonterminals( + "S, VP, NP, PP, P, N, Name, V, Det" + ) + + productions = ( + # Syntactic Productions + Production(S, [NP, VP]), + Production(NP, [Det, N]), + Production(NP, [NP, PP]), + Production(VP, [VP, PP]), + Production(VP, [V, NP, PP]), + Production(VP, [V, NP]), + Production(PP, [P, NP]), + Production(PP, []), + Production(PP, ["up", "over", NP]), + # Lexical Productions + Production(NP, ["I"]), + Production(Det, ["the"]), + Production(Det, ["a"]), + Production(N, ["man"]), + Production(V, ["saw"]), + Production(P, ["in"]), + Production(P, ["with"]), + Production(N, ["park"]), + Production(N, ["dog"]), + Production(N, ["statue"]), + Production(Det, ["my"]), + ) + + t = Tk() + + def destroy(e, t=t): + t.destroy() + + t.bind("q", destroy) + p = ProductionList(t, productions) + p.pack(expand=1, fill="both") + p.add_callback("select", p.markonly) + p.add_callback("move", p.markonly) + p.focus() + p.mark(productions[2]) + p.mark(productions[8]) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/dispersion.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/dispersion.py new file mode 100644 index 0000000..d87357a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/dispersion.py @@ -0,0 +1,63 @@ +# Natural Language Toolkit: Dispersion Plots +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +A utility for displaying lexical dispersion. +""" + + +def dispersion_plot(text, words, ignore_case=False, title="Lexical Dispersion Plot"): + """ + Generate a lexical dispersion plot. + + :param text: The source text + :type text: list(str) or iter(str) + :param words: The target words + :type words: list of str + :param ignore_case: flag to set if case should be ignored when searching text + :type ignore_case: bool + :return: a matplotlib Axes object that may still be modified before plotting + :rtype: Axes + """ + + try: + import matplotlib.pyplot as plt + except ImportError as e: + raise ImportError( + "The plot function requires matplotlib to be installed. " + "See https://matplotlib.org/" + ) from e + + word2y = { + word.casefold() if ignore_case else word: y + for y, word in enumerate(reversed(words)) + } + xs, ys = [], [] + for x, token in enumerate(text): + token = token.casefold() if ignore_case else token + y = word2y.get(token) + if y is not None: + xs.append(x) + ys.append(y) + + _, ax = plt.subplots() + ax.plot(xs, ys, "|") + ax.set_yticks(list(range(len(words))), words, color="C0") + ax.set_ylim(-1, len(words)) + ax.set_title(title) + ax.set_xlabel("Word Offset") + return ax + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + + from nltk.corpus import gutenberg + + words = ["Elinor", "Marianne", "Edward", "Willoughby"] + dispersion_plot(gutenberg.words("austen-sense.txt"), words) + plt.show() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/table.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/table.py new file mode 100644 index 0000000..1319b1c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/table.py @@ -0,0 +1,1177 @@ +# Natural Language Toolkit: Table widget +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Tkinter widgets for displaying multi-column listboxes and tables. +""" + +import operator +from tkinter import Frame, Label, Listbox, Scrollbar, Tk + +###################################################################### +# Multi-Column Listbox +###################################################################### + + +class MultiListbox(Frame): + """ + A multi-column listbox, where the current selection applies to an + entire row. Based on the MultiListbox Tkinter widget + recipe from the Python Cookbook (https://code.activestate.com/recipes/52266/) + + For the most part, ``MultiListbox`` methods delegate to its + contained listboxes. For any methods that do not have docstrings, + see ``Tkinter.Listbox`` for a description of what that method does. + """ + + # ///////////////////////////////////////////////////////////////// + # Configuration + # ///////////////////////////////////////////////////////////////// + + #: Default configuration values for the frame. + FRAME_CONFIG = dict(background="#888", takefocus=True, highlightthickness=1) + + #: Default configurations for the column labels. + LABEL_CONFIG = dict( + borderwidth=1, + relief="raised", + font="helvetica -16 bold", + background="#444", + foreground="white", + ) + + #: Default configuration for the column listboxes. + LISTBOX_CONFIG = dict( + borderwidth=1, + selectborderwidth=0, + highlightthickness=0, + exportselection=False, + selectbackground="#888", + activestyle="none", + takefocus=False, + ) + + # ///////////////////////////////////////////////////////////////// + # Constructor + # ///////////////////////////////////////////////////////////////// + + def __init__(self, master, columns, column_weights=None, cnf={}, **kw): + """ + Construct a new multi-column listbox widget. + + :param master: The widget that should contain the new + multi-column listbox. + + :param columns: Specifies what columns should be included in + the new multi-column listbox. If ``columns`` is an integer, + then it is the number of columns to include. If it is + a list, then its length indicates the number of columns + to include; and each element of the list will be used as + a label for the corresponding column. + + :param cnf, kw: Configuration parameters for this widget. + Use ``label_*`` to configure all labels; and ``listbox_*`` + to configure all listboxes. E.g.: + >>> root = Tk() # doctest: +SKIP + >>> MultiListbox(root, ["Subject", "Sender", "Date"], label_foreground='red').pack() # doctest: +SKIP + """ + # If columns was specified as an int, convert it to a list. + if isinstance(columns, int): + columns = list(range(columns)) + include_labels = False + else: + include_labels = True + + if len(columns) == 0: + raise ValueError("Expected at least one column") + + # Instance variables + self._column_names = tuple(columns) + self._listboxes = [] + self._labels = [] + + # Pick a default value for column_weights, if none was specified. + if column_weights is None: + column_weights = [1] * len(columns) + elif len(column_weights) != len(columns): + raise ValueError("Expected one column_weight for each column") + self._column_weights = column_weights + + # Configure our widgets. + Frame.__init__(self, master, **self.FRAME_CONFIG) + self.grid_rowconfigure(1, weight=1) + for i, label in enumerate(self._column_names): + self.grid_columnconfigure(i, weight=column_weights[i]) + + # Create a label for the column + if include_labels: + l = Label(self, text=label, **self.LABEL_CONFIG) + self._labels.append(l) + l.grid(column=i, row=0, sticky="news", padx=0, pady=0) + l.column_index = i + + # Create a listbox for the column + lb = Listbox(self, **self.LISTBOX_CONFIG) + self._listboxes.append(lb) + lb.grid(column=i, row=1, sticky="news", padx=0, pady=0) + lb.column_index = i + + # Clicking or dragging selects: + lb.bind("", self._select) + lb.bind("", self._select) + # Scroll wheel scrolls: + lb.bind("", lambda e: self._scroll(-1)) + lb.bind("", lambda e: self._scroll(+1)) + lb.bind("", lambda e: self._scroll(e.delta)) + # Button 2 can be used to scan: + lb.bind("", lambda e: self.scan_mark(e.x, e.y)) + lb.bind("", lambda e: self.scan_dragto(e.x, e.y)) + # Dragging outside the window has no effect (disable + # the default listbox behavior, which scrolls): + lb.bind("", lambda e: "break") + # Columns can be resized by dragging them: + lb.bind("", self._resize_column) + + # Columns can be resized by dragging them. (This binding is + # used if they click on the grid between columns:) + self.bind("", self._resize_column) + + # Set up key bindings for the widget: + self.bind("", lambda e: self.select(delta=-1)) + self.bind("", lambda e: self.select(delta=1)) + self.bind("", lambda e: self.select(delta=-self._pagesize())) + self.bind("", lambda e: self.select(delta=self._pagesize())) + + # Configuration customizations + self.configure(cnf, **kw) + + # ///////////////////////////////////////////////////////////////// + # Column Resizing + # ///////////////////////////////////////////////////////////////// + + def _resize_column(self, event): + """ + Callback used to resize a column of the table. Return ``True`` + if the column is actually getting resized (if the user clicked + on the far left or far right 5 pixels of a label); and + ``False`` otherwies. + """ + # If we're already waiting for a button release, then ignore + # the new button press. + if event.widget.bind(""): + return False + + # Decide which column (if any) to resize. + self._resize_column_index = None + if event.widget is self: + for i, lb in enumerate(self._listboxes): + if abs(event.x - (lb.winfo_x() + lb.winfo_width())) < 10: + self._resize_column_index = i + elif event.x > (event.widget.winfo_width() - 5): + self._resize_column_index = event.widget.column_index + elif event.x < 5 and event.widget.column_index != 0: + self._resize_column_index = event.widget.column_index - 1 + + # Bind callbacks that are used to resize it. + if self._resize_column_index is not None: + event.widget.bind("", self._resize_column_motion_cb) + event.widget.bind( + "" % event.num, self._resize_column_buttonrelease_cb + ) + return True + else: + return False + + def _resize_column_motion_cb(self, event): + lb = self._listboxes[self._resize_column_index] + charwidth = lb.winfo_width() / lb["width"] + + x1 = event.x + event.widget.winfo_x() + x2 = lb.winfo_x() + lb.winfo_width() + + lb["width"] = max(3, lb["width"] + (x1 - x2) // charwidth) + + def _resize_column_buttonrelease_cb(self, event): + event.widget.unbind("" % event.num) + event.widget.unbind("") + + # ///////////////////////////////////////////////////////////////// + # Properties + # ///////////////////////////////////////////////////////////////// + + @property + def column_names(self): + """ + A tuple containing the names of the columns used by this + multi-column listbox. + """ + return self._column_names + + @property + def column_labels(self): + """ + A tuple containing the ``Tkinter.Label`` widgets used to + display the label of each column. If this multi-column + listbox was created without labels, then this will be an empty + tuple. These widgets will all be augmented with a + ``column_index`` attribute, which can be used to determine + which column they correspond to. This can be convenient, + e.g., when defining callbacks for bound events. + """ + return tuple(self._labels) + + @property + def listboxes(self): + """ + A tuple containing the ``Tkinter.Listbox`` widgets used to + display individual columns. These widgets will all be + augmented with a ``column_index`` attribute, which can be used + to determine which column they correspond to. This can be + convenient, e.g., when defining callbacks for bound events. + """ + return tuple(self._listboxes) + + # ///////////////////////////////////////////////////////////////// + # Mouse & Keyboard Callback Functions + # ///////////////////////////////////////////////////////////////// + + def _select(self, e): + i = e.widget.nearest(e.y) + self.selection_clear(0, "end") + self.selection_set(i) + self.activate(i) + self.focus() + + def _scroll(self, delta): + for lb in self._listboxes: + lb.yview_scroll(delta, "unit") + return "break" + + def _pagesize(self): + """:return: The number of rows that makes up one page""" + return int(self.index("@0,1000000")) - int(self.index("@0,0")) + + # ///////////////////////////////////////////////////////////////// + # Row selection + # ///////////////////////////////////////////////////////////////// + + def select(self, index=None, delta=None, see=True): + """ + Set the selected row. If ``index`` is specified, then select + row ``index``. Otherwise, if ``delta`` is specified, then move + the current selection by ``delta`` (negative numbers for up, + positive numbers for down). This will not move the selection + past the top or the bottom of the list. + + :param see: If true, then call ``self.see()`` with the newly + selected index, to ensure that it is visible. + """ + if (index is not None) and (delta is not None): + raise ValueError("specify index or delta, but not both") + + # If delta was given, then calculate index. + if delta is not None: + if len(self.curselection()) == 0: + index = -1 + delta + else: + index = int(self.curselection()[0]) + delta + + # Clear all selected rows. + self.selection_clear(0, "end") + + # Select the specified index + if index is not None: + index = min(max(index, 0), self.size() - 1) + # self.activate(index) + self.selection_set(index) + if see: + self.see(index) + + # ///////////////////////////////////////////////////////////////// + # Configuration + # ///////////////////////////////////////////////////////////////// + + def configure(self, cnf={}, **kw): + """ + Configure this widget. Use ``label_*`` to configure all + labels; and ``listbox_*`` to configure all listboxes. E.g.: + + >>> master = Tk() # doctest: +SKIP + >>> mlb = MultiListbox(master, 5) # doctest: +SKIP + >>> mlb.configure(label_foreground='red') # doctest: +SKIP + >>> mlb.configure(listbox_foreground='red') # doctest: +SKIP + """ + cnf = dict(list(cnf.items()) + list(kw.items())) + for (key, val) in list(cnf.items()): + if key.startswith("label_") or key.startswith("label-"): + for label in self._labels: + label.configure({key[6:]: val}) + elif key.startswith("listbox_") or key.startswith("listbox-"): + for listbox in self._listboxes: + listbox.configure({key[8:]: val}) + else: + Frame.configure(self, {key: val}) + + def __setitem__(self, key, val): + """ + Configure this widget. This is equivalent to + ``self.configure({key,val``)}. See ``configure()``. + """ + self.configure({key: val}) + + def rowconfigure(self, row_index, cnf={}, **kw): + """ + Configure all table cells in the given row. Valid keyword + arguments are: ``background``, ``bg``, ``foreground``, ``fg``, + ``selectbackground``, ``selectforeground``. + """ + for lb in self._listboxes: + lb.itemconfigure(row_index, cnf, **kw) + + def columnconfigure(self, col_index, cnf={}, **kw): + """ + Configure all table cells in the given column. Valid keyword + arguments are: ``background``, ``bg``, ``foreground``, ``fg``, + ``selectbackground``, ``selectforeground``. + """ + lb = self._listboxes[col_index] + + cnf = dict(list(cnf.items()) + list(kw.items())) + for (key, val) in list(cnf.items()): + if key in ( + "background", + "bg", + "foreground", + "fg", + "selectbackground", + "selectforeground", + ): + for i in range(lb.size()): + lb.itemconfigure(i, {key: val}) + else: + lb.configure({key: val}) + + def itemconfigure(self, row_index, col_index, cnf=None, **kw): + """ + Configure the table cell at the given row and column. Valid + keyword arguments are: ``background``, ``bg``, ``foreground``, + ``fg``, ``selectbackground``, ``selectforeground``. + """ + lb = self._listboxes[col_index] + return lb.itemconfigure(row_index, cnf, **kw) + + # ///////////////////////////////////////////////////////////////// + # Value Access + # ///////////////////////////////////////////////////////////////// + + def insert(self, index, *rows): + """ + Insert the given row or rows into the table, at the given + index. Each row value should be a tuple of cell values, one + for each column in the row. Index may be an integer or any of + the special strings (such as ``'end'``) accepted by + ``Tkinter.Listbox``. + """ + for elt in rows: + if len(elt) != len(self._column_names): + raise ValueError( + "rows should be tuples whose length " + "is equal to the number of columns" + ) + for (lb, elts) in zip(self._listboxes, list(zip(*rows))): + lb.insert(index, *elts) + + def get(self, first, last=None): + """ + Return the value(s) of the specified row(s). If ``last`` is + not specified, then return a single row value; otherwise, + return a list of row values. Each row value is a tuple of + cell values, one for each column in the row. + """ + values = [lb.get(first, last) for lb in self._listboxes] + if last: + return [tuple(row) for row in zip(*values)] + else: + return tuple(values) + + def bbox(self, row, col): + """ + Return the bounding box for the given table cell, relative to + this widget's top-left corner. The bounding box is a tuple + of integers ``(left, top, width, height)``. + """ + dx, dy, _, _ = self.grid_bbox(row=0, column=col) + x, y, w, h = self._listboxes[col].bbox(row) + return int(x) + int(dx), int(y) + int(dy), int(w), int(h) + + # ///////////////////////////////////////////////////////////////// + # Hide/Show Columns + # ///////////////////////////////////////////////////////////////// + + def hide_column(self, col_index): + """ + Hide the given column. The column's state is still + maintained: its values will still be returned by ``get()``, and + you must supply its values when calling ``insert()``. It is + safe to call this on a column that is already hidden. + + :see: ``show_column()`` + """ + if self._labels: + self._labels[col_index].grid_forget() + self.listboxes[col_index].grid_forget() + self.grid_columnconfigure(col_index, weight=0) + + def show_column(self, col_index): + """ + Display a column that has been hidden using ``hide_column()``. + It is safe to call this on a column that is not hidden. + """ + weight = self._column_weights[col_index] + if self._labels: + self._labels[col_index].grid( + column=col_index, row=0, sticky="news", padx=0, pady=0 + ) + self._listboxes[col_index].grid( + column=col_index, row=1, sticky="news", padx=0, pady=0 + ) + self.grid_columnconfigure(col_index, weight=weight) + + # ///////////////////////////////////////////////////////////////// + # Binding Methods + # ///////////////////////////////////////////////////////////////// + + def bind_to_labels(self, sequence=None, func=None, add=None): + """ + Add a binding to each ``Tkinter.Label`` widget in this + mult-column listbox that will call ``func`` in response to the + event sequence. + + :return: A list of the identifiers of replaced binding + functions (if any), allowing for their deletion (to + prevent a memory leak). + """ + return [label.bind(sequence, func, add) for label in self.column_labels] + + def bind_to_listboxes(self, sequence=None, func=None, add=None): + """ + Add a binding to each ``Tkinter.Listbox`` widget in this + mult-column listbox that will call ``func`` in response to the + event sequence. + + :return: A list of the identifiers of replaced binding + functions (if any), allowing for their deletion (to + prevent a memory leak). + """ + for listbox in self.listboxes: + listbox.bind(sequence, func, add) + + def bind_to_columns(self, sequence=None, func=None, add=None): + """ + Add a binding to each ``Tkinter.Label`` and ``Tkinter.Listbox`` + widget in this mult-column listbox that will call ``func`` in + response to the event sequence. + + :return: A list of the identifiers of replaced binding + functions (if any), allowing for their deletion (to + prevent a memory leak). + """ + return self.bind_to_labels(sequence, func, add) + self.bind_to_listboxes( + sequence, func, add + ) + + # ///////////////////////////////////////////////////////////////// + # Simple Delegation + # ///////////////////////////////////////////////////////////////// + + # These methods delegate to the first listbox: + def curselection(self, *args, **kwargs): + return self._listboxes[0].curselection(*args, **kwargs) + + def selection_includes(self, *args, **kwargs): + return self._listboxes[0].selection_includes(*args, **kwargs) + + def itemcget(self, *args, **kwargs): + return self._listboxes[0].itemcget(*args, **kwargs) + + def size(self, *args, **kwargs): + return self._listboxes[0].size(*args, **kwargs) + + def index(self, *args, **kwargs): + return self._listboxes[0].index(*args, **kwargs) + + def nearest(self, *args, **kwargs): + return self._listboxes[0].nearest(*args, **kwargs) + + # These methods delegate to each listbox (and return None): + def activate(self, *args, **kwargs): + for lb in self._listboxes: + lb.activate(*args, **kwargs) + + def delete(self, *args, **kwargs): + for lb in self._listboxes: + lb.delete(*args, **kwargs) + + def scan_mark(self, *args, **kwargs): + for lb in self._listboxes: + lb.scan_mark(*args, **kwargs) + + def scan_dragto(self, *args, **kwargs): + for lb in self._listboxes: + lb.scan_dragto(*args, **kwargs) + + def see(self, *args, **kwargs): + for lb in self._listboxes: + lb.see(*args, **kwargs) + + def selection_anchor(self, *args, **kwargs): + for lb in self._listboxes: + lb.selection_anchor(*args, **kwargs) + + def selection_clear(self, *args, **kwargs): + for lb in self._listboxes: + lb.selection_clear(*args, **kwargs) + + def selection_set(self, *args, **kwargs): + for lb in self._listboxes: + lb.selection_set(*args, **kwargs) + + def yview(self, *args, **kwargs): + for lb in self._listboxes: + v = lb.yview(*args, **kwargs) + return v # if called with no arguments + + def yview_moveto(self, *args, **kwargs): + for lb in self._listboxes: + lb.yview_moveto(*args, **kwargs) + + def yview_scroll(self, *args, **kwargs): + for lb in self._listboxes: + lb.yview_scroll(*args, **kwargs) + + # ///////////////////////////////////////////////////////////////// + # Aliases + # ///////////////////////////////////////////////////////////////// + + itemconfig = itemconfigure + rowconfig = rowconfigure + columnconfig = columnconfigure + select_anchor = selection_anchor + select_clear = selection_clear + select_includes = selection_includes + select_set = selection_set + + # ///////////////////////////////////////////////////////////////// + # These listbox methods are not defined for multi-listbox + # ///////////////////////////////////////////////////////////////// + # def xview(self, *what): pass + # def xview_moveto(self, fraction): pass + # def xview_scroll(self, number, what): pass + + +###################################################################### +# Table +###################################################################### + + +class Table: + """ + A display widget for a table of values, based on a ``MultiListbox`` + widget. For many purposes, ``Table`` can be treated as a + list-of-lists. E.g., table[i] is a list of the values for row i; + and table.append(row) adds a new row with the given list of + values. Individual cells can be accessed using table[i,j], which + refers to the j-th column of the i-th row. This can be used to + both read and write values from the table. E.g.: + + >>> table[i,j] = 'hello' # doctest: +SKIP + + The column (j) can be given either as an index number, or as a + column name. E.g., the following prints the value in the 3rd row + for the 'First Name' column: + + >>> print(table[3, 'First Name']) # doctest: +SKIP + John + + You can configure the colors for individual rows, columns, or + cells using ``rowconfig()``, ``columnconfig()``, and ``itemconfig()``. + The color configuration for each row will be preserved if the + table is modified; however, when new rows are added, any color + configurations that have been made for *columns* will not be + applied to the new row. + + Note: Although ``Table`` acts like a widget in some ways (e.g., it + defines ``grid()``, ``pack()``, and ``bind()``), it is not itself a + widget; it just contains one. This is because widgets need to + define ``__getitem__()``, ``__setitem__()``, and ``__nonzero__()`` in + a way that's incompatible with the fact that ``Table`` behaves as a + list-of-lists. + + :ivar _mlb: The multi-column listbox used to display this table's data. + :ivar _rows: A list-of-lists used to hold the cell values of this + table. Each element of _rows is a row value, i.e., a list of + cell values, one for each column in the row. + """ + + def __init__( + self, + master, + column_names, + rows=None, + column_weights=None, + scrollbar=True, + click_to_sort=True, + reprfunc=None, + cnf={}, + **kw + ): + """ + Construct a new Table widget. + + :type master: Tkinter.Widget + :param master: The widget that should contain the new table. + :type column_names: list(str) + :param column_names: A list of names for the columns; these + names will be used to create labels for each column; + and can be used as an index when reading or writing + cell values from the table. + :type rows: list(list) + :param rows: A list of row values used to initialize the table. + Each row value should be a tuple of cell values, one for + each column in the row. + :type scrollbar: bool + :param scrollbar: If true, then create a scrollbar for the + new table widget. + :type click_to_sort: bool + :param click_to_sort: If true, then create bindings that will + sort the table's rows by a given column's values if the + user clicks on that colum's label. + :type reprfunc: function + :param reprfunc: If specified, then use this function to + convert each table cell value to a string suitable for + display. ``reprfunc`` has the following signature: + reprfunc(row_index, col_index, cell_value) -> str + (Note that the column is specified by index, not by name.) + :param cnf, kw: Configuration parameters for this widget's + contained ``MultiListbox``. See ``MultiListbox.__init__()`` + for details. + """ + self._num_columns = len(column_names) + self._reprfunc = reprfunc + self._frame = Frame(master) + + self._column_name_to_index = {c: i for (i, c) in enumerate(column_names)} + + # Make a copy of the rows & check that it's valid. + if rows is None: + self._rows = [] + else: + self._rows = [[v for v in row] for row in rows] + for row in self._rows: + self._checkrow(row) + + # Create our multi-list box. + self._mlb = MultiListbox(self._frame, column_names, column_weights, cnf, **kw) + self._mlb.pack(side="left", expand=True, fill="both") + + # Optional scrollbar + if scrollbar: + sb = Scrollbar(self._frame, orient="vertical", command=self._mlb.yview) + self._mlb.listboxes[0]["yscrollcommand"] = sb.set + # for listbox in self._mlb.listboxes: + # listbox['yscrollcommand'] = sb.set + sb.pack(side="right", fill="y") + self._scrollbar = sb + + # Set up sorting + self._sortkey = None + if click_to_sort: + for i, l in enumerate(self._mlb.column_labels): + l.bind("", self._sort) + + # Fill in our multi-list box. + self._fill_table() + + # ///////////////////////////////////////////////////////////////// + # { Widget-like Methods + # ///////////////////////////////////////////////////////////////// + # These all just delegate to either our frame or our MLB. + + def pack(self, *args, **kwargs): + """Position this table's main frame widget in its parent + widget. See ``Tkinter.Frame.pack()`` for more info.""" + self._frame.pack(*args, **kwargs) + + def grid(self, *args, **kwargs): + """Position this table's main frame widget in its parent + widget. See ``Tkinter.Frame.grid()`` for more info.""" + self._frame.grid(*args, **kwargs) + + def focus(self): + """Direct (keyboard) input foxus to this widget.""" + self._mlb.focus() + + def bind(self, sequence=None, func=None, add=None): + """Add a binding to this table's main frame that will call + ``func`` in response to the event sequence.""" + self._mlb.bind(sequence, func, add) + + def rowconfigure(self, row_index, cnf={}, **kw): + """:see: ``MultiListbox.rowconfigure()``""" + self._mlb.rowconfigure(row_index, cnf, **kw) + + def columnconfigure(self, col_index, cnf={}, **kw): + """:see: ``MultiListbox.columnconfigure()``""" + col_index = self.column_index(col_index) + self._mlb.columnconfigure(col_index, cnf, **kw) + + def itemconfigure(self, row_index, col_index, cnf=None, **kw): + """:see: ``MultiListbox.itemconfigure()``""" + col_index = self.column_index(col_index) + return self._mlb.itemconfigure(row_index, col_index, cnf, **kw) + + def bind_to_labels(self, sequence=None, func=None, add=None): + """:see: ``MultiListbox.bind_to_labels()``""" + return self._mlb.bind_to_labels(sequence, func, add) + + def bind_to_listboxes(self, sequence=None, func=None, add=None): + """:see: ``MultiListbox.bind_to_listboxes()``""" + return self._mlb.bind_to_listboxes(sequence, func, add) + + def bind_to_columns(self, sequence=None, func=None, add=None): + """:see: ``MultiListbox.bind_to_columns()``""" + return self._mlb.bind_to_columns(sequence, func, add) + + rowconfig = rowconfigure + columnconfig = columnconfigure + itemconfig = itemconfigure + + # ///////////////////////////////////////////////////////////////// + # { Table as list-of-lists + # ///////////////////////////////////////////////////////////////// + + def insert(self, row_index, rowvalue): + """ + Insert a new row into the table, so that its row index will be + ``row_index``. If the table contains any rows whose row index + is greater than or equal to ``row_index``, then they will be + shifted down. + + :param rowvalue: A tuple of cell values, one for each column + in the new row. + """ + self._checkrow(rowvalue) + self._rows.insert(row_index, rowvalue) + if self._reprfunc is not None: + rowvalue = [ + self._reprfunc(row_index, j, v) for (j, v) in enumerate(rowvalue) + ] + self._mlb.insert(row_index, rowvalue) + if self._DEBUG: + self._check_table_vs_mlb() + + def extend(self, rowvalues): + """ + Add new rows at the end of the table. + + :param rowvalues: A list of row values used to initialize the + table. Each row value should be a tuple of cell values, + one for each column in the row. + """ + for rowvalue in rowvalues: + self.append(rowvalue) + if self._DEBUG: + self._check_table_vs_mlb() + + def append(self, rowvalue): + """ + Add a new row to the end of the table. + + :param rowvalue: A tuple of cell values, one for each column + in the new row. + """ + self.insert(len(self._rows), rowvalue) + if self._DEBUG: + self._check_table_vs_mlb() + + def clear(self): + """ + Delete all rows in this table. + """ + self._rows = [] + self._mlb.delete(0, "end") + if self._DEBUG: + self._check_table_vs_mlb() + + def __getitem__(self, index): + """ + Return the value of a row or a cell in this table. If + ``index`` is an integer, then the row value for the ``index``th + row. This row value consists of a tuple of cell values, one + for each column in the row. If ``index`` is a tuple of two + integers, ``(i,j)``, then return the value of the cell in the + ``i``th row and the ``j``th column. + """ + if isinstance(index, slice): + raise ValueError("Slicing not supported") + elif isinstance(index, tuple) and len(index) == 2: + return self._rows[index[0]][self.column_index(index[1])] + else: + return tuple(self._rows[index]) + + def __setitem__(self, index, val): + """ + Replace the value of a row or a cell in this table with + ``val``. + + If ``index`` is an integer, then ``val`` should be a row value + (i.e., a tuple of cell values, one for each column). In this + case, the values of the ``index``th row of the table will be + replaced with the values in ``val``. + + If ``index`` is a tuple of integers, ``(i,j)``, then replace the + value of the cell in the ``i``th row and ``j``th column with + ``val``. + """ + if isinstance(index, slice): + raise ValueError("Slicing not supported") + + # table[i,j] = val + elif isinstance(index, tuple) and len(index) == 2: + i, j = index[0], self.column_index(index[1]) + config_cookie = self._save_config_info([i]) + self._rows[i][j] = val + if self._reprfunc is not None: + val = self._reprfunc(i, j, val) + self._mlb.listboxes[j].insert(i, val) + self._mlb.listboxes[j].delete(i + 1) + self._restore_config_info(config_cookie) + + # table[i] = val + else: + config_cookie = self._save_config_info([index]) + self._checkrow(val) + self._rows[index] = list(val) + if self._reprfunc is not None: + val = [self._reprfunc(index, j, v) for (j, v) in enumerate(val)] + self._mlb.insert(index, val) + self._mlb.delete(index + 1) + self._restore_config_info(config_cookie) + + def __delitem__(self, row_index): + """ + Delete the ``row_index``th row from this table. + """ + if isinstance(row_index, slice): + raise ValueError("Slicing not supported") + if isinstance(row_index, tuple) and len(row_index) == 2: + raise ValueError("Cannot delete a single cell!") + del self._rows[row_index] + self._mlb.delete(row_index) + if self._DEBUG: + self._check_table_vs_mlb() + + def __len__(self): + """ + :return: the number of rows in this table. + """ + return len(self._rows) + + def _checkrow(self, rowvalue): + """ + Helper function: check that a given row value has the correct + number of elements; and if not, raise an exception. + """ + if len(rowvalue) != self._num_columns: + raise ValueError( + "Row %r has %d columns; expected %d" + % (rowvalue, len(rowvalue), self._num_columns) + ) + + # ///////////////////////////////////////////////////////////////// + # Columns + # ///////////////////////////////////////////////////////////////// + + @property + def column_names(self): + """A list of the names of the columns in this table.""" + return self._mlb.column_names + + def column_index(self, i): + """ + If ``i`` is a valid column index integer, then return it as is. + Otherwise, check if ``i`` is used as the name for any column; + if so, return that column's index. Otherwise, raise a + ``KeyError`` exception. + """ + if isinstance(i, int) and 0 <= i < self._num_columns: + return i + else: + # This raises a key error if the column is not found. + return self._column_name_to_index[i] + + def hide_column(self, column_index): + """:see: ``MultiListbox.hide_column()``""" + self._mlb.hide_column(self.column_index(column_index)) + + def show_column(self, column_index): + """:see: ``MultiListbox.show_column()``""" + self._mlb.show_column(self.column_index(column_index)) + + # ///////////////////////////////////////////////////////////////// + # Selection + # ///////////////////////////////////////////////////////////////// + + def selected_row(self): + """ + Return the index of the currently selected row, or None if + no row is selected. To get the row value itself, use + ``table[table.selected_row()]``. + """ + sel = self._mlb.curselection() + if sel: + return int(sel[0]) + else: + return None + + def select(self, index=None, delta=None, see=True): + """:see: ``MultiListbox.select()``""" + self._mlb.select(index, delta, see) + + # ///////////////////////////////////////////////////////////////// + # Sorting + # ///////////////////////////////////////////////////////////////// + + def sort_by(self, column_index, order="toggle"): + """ + Sort the rows in this table, using the specified column's + values as a sort key. + + :param column_index: Specifies which column to sort, using + either a column index (int) or a column's label name + (str). + + :param order: Specifies whether to sort the values in + ascending or descending order: + + - ``'ascending'``: Sort from least to greatest. + - ``'descending'``: Sort from greatest to least. + - ``'toggle'``: If the most recent call to ``sort_by()`` + sorted the table by the same column (``column_index``), + then reverse the rows; otherwise sort in ascending + order. + """ + if order not in ("ascending", "descending", "toggle"): + raise ValueError( + 'sort_by(): order should be "ascending", ' '"descending", or "toggle".' + ) + column_index = self.column_index(column_index) + config_cookie = self._save_config_info(index_by_id=True) + + # Sort the rows. + if order == "toggle" and column_index == self._sortkey: + self._rows.reverse() + else: + self._rows.sort( + key=operator.itemgetter(column_index), reverse=(order == "descending") + ) + self._sortkey = column_index + + # Redraw the table. + self._fill_table() + self._restore_config_info(config_cookie, index_by_id=True, see=True) + if self._DEBUG: + self._check_table_vs_mlb() + + def _sort(self, event): + """Event handler for clicking on a column label -- sort by + that column.""" + column_index = event.widget.column_index + + # If they click on the far-left of far-right of a column's + # label, then resize rather than sorting. + if self._mlb._resize_column(event): + return "continue" + + # Otherwise, sort. + else: + self.sort_by(column_index) + return "continue" + + # ///////////////////////////////////////////////////////////////// + # { Table Drawing Helpers + # ///////////////////////////////////////////////////////////////// + + def _fill_table(self, save_config=True): + """ + Re-draw the table from scratch, by clearing out the table's + multi-column listbox; and then filling it in with values from + ``self._rows``. Note that any cell-, row-, or column-specific + color configuration that has been done will be lost. The + selection will also be lost -- i.e., no row will be selected + after this call completes. + """ + self._mlb.delete(0, "end") + for i, row in enumerate(self._rows): + if self._reprfunc is not None: + row = [self._reprfunc(i, j, v) for (j, v) in enumerate(row)] + self._mlb.insert("end", row) + + def _get_itemconfig(self, r, c): + return { + k: self._mlb.itemconfig(r, c, k)[-1] + for k in ( + "foreground", + "selectforeground", + "background", + "selectbackground", + ) + } + + def _save_config_info(self, row_indices=None, index_by_id=False): + """ + Return a 'cookie' containing information about which row is + selected, and what color configurations have been applied. + this information can the be re-applied to the table (after + making modifications) using ``_restore_config_info()``. Color + configuration information will be saved for any rows in + ``row_indices``, or in the entire table, if + ``row_indices=None``. If ``index_by_id=True``, the the cookie + will associate rows with their configuration information based + on the rows' python id. This is useful when performing + operations that re-arrange the rows (e.g. ``sort``). If + ``index_by_id=False``, then it is assumed that all rows will be + in the same order when ``_restore_config_info()`` is called. + """ + # Default value for row_indices is all rows. + if row_indices is None: + row_indices = list(range(len(self._rows))) + + # Look up our current selection. + selection = self.selected_row() + if index_by_id and selection is not None: + selection = id(self._rows[selection]) + + # Look up the color configuration info for each row. + if index_by_id: + config = { + id(self._rows[r]): [ + self._get_itemconfig(r, c) for c in range(self._num_columns) + ] + for r in row_indices + } + else: + config = { + r: [self._get_itemconfig(r, c) for c in range(self._num_columns)] + for r in row_indices + } + + return selection, config + + def _restore_config_info(self, cookie, index_by_id=False, see=False): + """ + Restore selection & color configuration information that was + saved using ``_save_config_info``. + """ + selection, config = cookie + + # Clear the selection. + if selection is None: + self._mlb.selection_clear(0, "end") + + # Restore selection & color config + if index_by_id: + for r, row in enumerate(self._rows): + if id(row) in config: + for c in range(self._num_columns): + self._mlb.itemconfigure(r, c, config[id(row)][c]) + if id(row) == selection: + self._mlb.select(r, see=see) + else: + if selection is not None: + self._mlb.select(selection, see=see) + for r in config: + for c in range(self._num_columns): + self._mlb.itemconfigure(r, c, config[r][c]) + + # ///////////////////////////////////////////////////////////////// + # Debugging (Invariant Checker) + # ///////////////////////////////////////////////////////////////// + + _DEBUG = False + """If true, then run ``_check_table_vs_mlb()`` after any operation + that modifies the table.""" + + def _check_table_vs_mlb(self): + """ + Verify that the contents of the table's ``_rows`` variable match + the contents of its multi-listbox (``_mlb``). This is just + included for debugging purposes, to make sure that the + list-modifying operations are working correctly. + """ + for col in self._mlb.listboxes: + assert len(self) == col.size() + for row in self: + assert len(row) == self._num_columns + assert self._num_columns == len(self._mlb.column_names) + # assert self._column_names == self._mlb.column_names + for i, row in enumerate(self): + for j, cell in enumerate(row): + if self._reprfunc is not None: + cell = self._reprfunc(i, j, cell) + assert self._mlb.get(i)[j] == cell + + +###################################################################### +# Demo/Test Function +###################################################################### + +# update this to use new WordNet API +def demo(): + root = Tk() + root.bind("", lambda e: root.destroy()) + + table = Table( + root, + "Word Synset Hypernym Hyponym".split(), + column_weights=[0, 1, 1, 1], + reprfunc=(lambda i, j, s: " %s" % s), + ) + table.pack(expand=True, fill="both") + + from nltk.corpus import brown, wordnet + + for word, pos in sorted(set(brown.tagged_words()[:500])): + if pos[0] != "N": + continue + word = word.lower() + for synset in wordnet.synsets(word): + try: + hyper_def = synset.hypernyms()[0].definition() + except: + hyper_def = "*none*" + try: + hypo_def = synset.hypernyms()[0].definition() + except: + hypo_def = "*none*" + table.append([word, synset.definition(), hyper_def, hypo_def]) + + table.columnconfig("Word", background="#afa") + table.columnconfig("Synset", background="#efe") + table.columnconfig("Hypernym", background="#fee") + table.columnconfig("Hyponym", background="#ffe") + for row in range(len(table)): + for column in ("Hypernym", "Hyponym"): + if table[row, column] == "*none*": + table.itemconfig( + row, column, foreground="#666", selectforeground="#666" + ) + root.mainloop() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/tree.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/tree.py new file mode 100644 index 0000000..da78e3b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/tree.py @@ -0,0 +1,1129 @@ +# Natural Language Toolkit: Graphical Representations for Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Graphically display a Tree. +""" + +from tkinter import IntVar, Menu, Tk + +from nltk.draw.util import ( + BoxWidget, + CanvasFrame, + CanvasWidget, + OvalWidget, + ParenWidget, + TextWidget, +) +from nltk.tree import Tree +from nltk.util import in_idle + +##////////////////////////////////////////////////////// +## Tree Segment +##////////////////////////////////////////////////////// + + +class TreeSegmentWidget(CanvasWidget): + """ + A canvas widget that displays a single segment of a hierarchical + tree. Each ``TreeSegmentWidget`` connects a single "node widget" + to a sequence of zero or more "subtree widgets". By default, the + bottom of the node is connected to the top of each subtree by a + single line. However, if the ``roof`` attribute is set, then a + single triangular "roof" will connect the node to all of its + children. + + Attributes: + - ``roof``: What sort of connection to draw between the node and + its subtrees. If ``roof`` is true, draw a single triangular + "roof" over the subtrees. If ``roof`` is false, draw a line + between each subtree and the node. Default value is false. + - ``xspace``: The amount of horizontal space to leave between + subtrees when managing this widget. Default value is 10. + - ``yspace``: The amount of space to place between the node and + its children when managing this widget. Default value is 15. + - ``color``: The color of the lines connecting the node to its + subtrees; and of the outline of the triangular roof. Default + value is ``'#006060'``. + - ``fill``: The fill color for the triangular roof. Default + value is ``''`` (no fill). + - ``width``: The width of the lines connecting the node to its + subtrees; and of the outline of the triangular roof. Default + value is 1. + - ``orientation``: Determines whether the tree branches downwards + or rightwards. Possible values are ``'horizontal'`` and + ``'vertical'``. The default value is ``'vertical'`` (i.e., + branch downwards). + - ``draggable``: whether the widget can be dragged by the user. + """ + + def __init__(self, canvas, label, subtrees, **attribs): + """ + :type node: + :type subtrees: list(CanvasWidgetI) + """ + self._label = label + self._subtrees = subtrees + + # Attributes + self._horizontal = 0 + self._roof = 0 + self._xspace = 10 + self._yspace = 15 + self._ordered = False + + # Create canvas objects. + self._lines = [canvas.create_line(0, 0, 0, 0, fill="#006060") for c in subtrees] + self._polygon = canvas.create_polygon( + 0, 0, fill="", state="hidden", outline="#006060" + ) + + # Register child widgets (label + subtrees) + self._add_child_widget(label) + for subtree in subtrees: + self._add_child_widget(subtree) + + # Are we currently managing? + self._managing = False + + CanvasWidget.__init__(self, canvas, **attribs) + + def __setitem__(self, attr, value): + canvas = self.canvas() + if attr == "roof": + self._roof = value + if self._roof: + for l in self._lines: + canvas.itemconfig(l, state="hidden") + canvas.itemconfig(self._polygon, state="normal") + else: + for l in self._lines: + canvas.itemconfig(l, state="normal") + canvas.itemconfig(self._polygon, state="hidden") + elif attr == "orientation": + if value == "horizontal": + self._horizontal = 1 + elif value == "vertical": + self._horizontal = 0 + else: + raise ValueError("orientation must be horizontal or vertical") + elif attr == "color": + for l in self._lines: + canvas.itemconfig(l, fill=value) + canvas.itemconfig(self._polygon, outline=value) + elif isinstance(attr, tuple) and attr[0] == "color": + # Set the color of an individual line. + l = self._lines[int(attr[1])] + canvas.itemconfig(l, fill=value) + elif attr == "fill": + canvas.itemconfig(self._polygon, fill=value) + elif attr == "width": + canvas.itemconfig(self._polygon, {attr: value}) + for l in self._lines: + canvas.itemconfig(l, {attr: value}) + elif attr in ("xspace", "yspace"): + if attr == "xspace": + self._xspace = value + elif attr == "yspace": + self._yspace = value + self.update(self._label) + elif attr == "ordered": + self._ordered = value + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "roof": + return self._roof + elif attr == "width": + return self.canvas().itemcget(self._polygon, attr) + elif attr == "color": + return self.canvas().itemcget(self._polygon, "outline") + elif isinstance(attr, tuple) and attr[0] == "color": + l = self._lines[int(attr[1])] + return self.canvas().itemcget(l, "fill") + elif attr == "xspace": + return self._xspace + elif attr == "yspace": + return self._yspace + elif attr == "orientation": + if self._horizontal: + return "horizontal" + else: + return "vertical" + elif attr == "ordered": + return self._ordered + else: + return CanvasWidget.__getitem__(self, attr) + + def label(self): + return self._label + + def subtrees(self): + return self._subtrees[:] + + def set_label(self, label): + """ + Set the node label to ``label``. + """ + self._remove_child_widget(self._label) + self._add_child_widget(label) + self._label = label + self.update(self._label) + + def replace_child(self, oldchild, newchild): + """ + Replace the child ``oldchild`` with ``newchild``. + """ + index = self._subtrees.index(oldchild) + self._subtrees[index] = newchild + self._remove_child_widget(oldchild) + self._add_child_widget(newchild) + self.update(newchild) + + def remove_child(self, child): + index = self._subtrees.index(child) + del self._subtrees[index] + self._remove_child_widget(child) + self.canvas().delete(self._lines.pop()) + self.update(self._label) + + def insert_child(self, index, child): + canvas = self.canvas() + self._subtrees.insert(index, child) + self._add_child_widget(child) + self._lines.append(canvas.create_line(0, 0, 0, 0, fill="#006060")) + self.update(self._label) + + # but.. lines??? + + def _tags(self): + if self._roof: + return [self._polygon] + else: + return self._lines + + def _subtree_top(self, child): + if isinstance(child, TreeSegmentWidget): + bbox = child.label().bbox() + else: + bbox = child.bbox() + if self._horizontal: + return (bbox[0], (bbox[1] + bbox[3]) / 2.0) + else: + return ((bbox[0] + bbox[2]) / 2.0, bbox[1]) + + def _node_bottom(self): + bbox = self._label.bbox() + if self._horizontal: + return (bbox[2], (bbox[1] + bbox[3]) / 2.0) + else: + return ((bbox[0] + bbox[2]) / 2.0, bbox[3]) + + def _update(self, child): + if len(self._subtrees) == 0: + return + if self._label.bbox() is None: + return # [XX] ??? + + # Which lines need to be redrawn? + if child is self._label: + need_update = self._subtrees + else: + need_update = [child] + + if self._ordered and not self._managing: + need_update = self._maintain_order(child) + + # Update the polygon. + (nodex, nodey) = self._node_bottom() + (xmin, ymin, xmax, ymax) = self._subtrees[0].bbox() + for subtree in self._subtrees[1:]: + bbox = subtree.bbox() + xmin = min(xmin, bbox[0]) + ymin = min(ymin, bbox[1]) + xmax = max(xmax, bbox[2]) + ymax = max(ymax, bbox[3]) + + if self._horizontal: + self.canvas().coords( + self._polygon, nodex, nodey, xmin, ymin, xmin, ymax, nodex, nodey + ) + else: + self.canvas().coords( + self._polygon, nodex, nodey, xmin, ymin, xmax, ymin, nodex, nodey + ) + + # Redraw all lines that need it. + for subtree in need_update: + (nodex, nodey) = self._node_bottom() + line = self._lines[self._subtrees.index(subtree)] + (subtreex, subtreey) = self._subtree_top(subtree) + self.canvas().coords(line, nodex, nodey, subtreex, subtreey) + + def _maintain_order(self, child): + if self._horizontal: + return self._maintain_order_horizontal(child) + else: + return self._maintain_order_vertical(child) + + def _maintain_order_vertical(self, child): + (left, top, right, bot) = child.bbox() + + if child is self._label: + # Check all the leaves + for subtree in self._subtrees: + (x1, y1, x2, y2) = subtree.bbox() + if bot + self._yspace > y1: + subtree.move(0, bot + self._yspace - y1) + + return self._subtrees + else: + moved = [child] + index = self._subtrees.index(child) + + # Check leaves to our right. + x = right + self._xspace + for i in range(index + 1, len(self._subtrees)): + (x1, y1, x2, y2) = self._subtrees[i].bbox() + if x > x1: + self._subtrees[i].move(x - x1, 0) + x += x2 - x1 + self._xspace + moved.append(self._subtrees[i]) + + # Check leaves to our left. + x = left - self._xspace + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._subtrees[i].bbox() + if x < x2: + self._subtrees[i].move(x - x2, 0) + x -= x2 - x1 + self._xspace + moved.append(self._subtrees[i]) + + # Check the node + (x1, y1, x2, y2) = self._label.bbox() + if y2 > top - self._yspace: + self._label.move(0, top - self._yspace - y2) + moved = self._subtrees + + # Return a list of the nodes we moved + return moved + + def _maintain_order_horizontal(self, child): + (left, top, right, bot) = child.bbox() + + if child is self._label: + # Check all the leaves + for subtree in self._subtrees: + (x1, y1, x2, y2) = subtree.bbox() + if right + self._xspace > x1: + subtree.move(right + self._xspace - x1) + + return self._subtrees + else: + moved = [child] + index = self._subtrees.index(child) + + # Check leaves below us. + y = bot + self._yspace + for i in range(index + 1, len(self._subtrees)): + (x1, y1, x2, y2) = self._subtrees[i].bbox() + if y > y1: + self._subtrees[i].move(0, y - y1) + y += y2 - y1 + self._yspace + moved.append(self._subtrees[i]) + + # Check leaves above us + y = top - self._yspace + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._subtrees[i].bbox() + if y < y2: + self._subtrees[i].move(0, y - y2) + y -= y2 - y1 + self._yspace + moved.append(self._subtrees[i]) + + # Check the node + (x1, y1, x2, y2) = self._label.bbox() + if x2 > left - self._xspace: + self._label.move(left - self._xspace - x2, 0) + moved = self._subtrees + + # Return a list of the nodes we moved + return moved + + def _manage_horizontal(self): + (nodex, nodey) = self._node_bottom() + + # Put the subtrees in a line. + y = 20 + for subtree in self._subtrees: + subtree_bbox = subtree.bbox() + dx = nodex - subtree_bbox[0] + self._xspace + dy = y - subtree_bbox[1] + subtree.move(dx, dy) + y += subtree_bbox[3] - subtree_bbox[1] + self._yspace + + # Find the center of their tops. + center = 0.0 + for subtree in self._subtrees: + center += self._subtree_top(subtree)[1] + center /= len(self._subtrees) + + # Center the subtrees with the node. + for subtree in self._subtrees: + subtree.move(0, nodey - center) + + def _manage_vertical(self): + (nodex, nodey) = self._node_bottom() + + # Put the subtrees in a line. + x = 0 + for subtree in self._subtrees: + subtree_bbox = subtree.bbox() + dy = nodey - subtree_bbox[1] + self._yspace + dx = x - subtree_bbox[0] + subtree.move(dx, dy) + x += subtree_bbox[2] - subtree_bbox[0] + self._xspace + + # Find the center of their tops. + center = 0.0 + for subtree in self._subtrees: + center += self._subtree_top(subtree)[0] / len(self._subtrees) + + # Center the subtrees with the node. + for subtree in self._subtrees: + subtree.move(nodex - center, 0) + + def _manage(self): + self._managing = True + (nodex, nodey) = self._node_bottom() + if len(self._subtrees) == 0: + return + + if self._horizontal: + self._manage_horizontal() + else: + self._manage_vertical() + + # Update lines to subtrees. + for subtree in self._subtrees: + self._update(subtree) + + self._managing = False + + def __repr__(self): + return f"[TreeSeg {self._label}: {self._subtrees}]" + + +def _tree_to_treeseg( + canvas, + t, + make_node, + make_leaf, + tree_attribs, + node_attribs, + leaf_attribs, + loc_attribs, +): + if isinstance(t, Tree): + label = make_node(canvas, t.label(), **node_attribs) + subtrees = [ + _tree_to_treeseg( + canvas, + child, + make_node, + make_leaf, + tree_attribs, + node_attribs, + leaf_attribs, + loc_attribs, + ) + for child in t + ] + return TreeSegmentWidget(canvas, label, subtrees, **tree_attribs) + else: + return make_leaf(canvas, t, **leaf_attribs) + + +def tree_to_treesegment( + canvas, t, make_node=TextWidget, make_leaf=TextWidget, **attribs +): + """ + Convert a Tree into a ``TreeSegmentWidget``. + + :param make_node: A ``CanvasWidget`` constructor or a function that + creates ``CanvasWidgets``. ``make_node`` is used to convert + the Tree's nodes into ``CanvasWidgets``. If no constructor + is specified, then ``TextWidget`` will be used. + :param make_leaf: A ``CanvasWidget`` constructor or a function that + creates ``CanvasWidgets``. ``make_leaf`` is used to convert + the Tree's leafs into ``CanvasWidgets``. If no constructor + is specified, then ``TextWidget`` will be used. + :param attribs: Attributes for the canvas widgets that make up the + returned ``TreeSegmentWidget``. Any attribute beginning with + ``'tree_'`` will be passed to all ``TreeSegmentWidgets`` (with + the ``'tree_'`` prefix removed. Any attribute beginning with + ``'node_'`` will be passed to all nodes. Any attribute + beginning with ``'leaf_'`` will be passed to all leaves. And + any attribute beginning with ``'loc_'`` will be passed to all + text locations (for Trees). + """ + # Process attribs. + tree_attribs = {} + node_attribs = {} + leaf_attribs = {} + loc_attribs = {} + + for (key, value) in list(attribs.items()): + if key[:5] == "tree_": + tree_attribs[key[5:]] = value + elif key[:5] == "node_": + node_attribs[key[5:]] = value + elif key[:5] == "leaf_": + leaf_attribs[key[5:]] = value + elif key[:4] == "loc_": + loc_attribs[key[4:]] = value + else: + raise ValueError("Bad attribute: %s" % key) + return _tree_to_treeseg( + canvas, + t, + make_node, + make_leaf, + tree_attribs, + node_attribs, + leaf_attribs, + loc_attribs, + ) + + +##////////////////////////////////////////////////////// +## Tree Widget +##////////////////////////////////////////////////////// + + +class TreeWidget(CanvasWidget): + """ + A canvas widget that displays a single Tree. + ``TreeWidget`` manages a group of ``TreeSegmentWidgets`` that are + used to display a Tree. + + Attributes: + + - ``node_attr``: Sets the attribute ``attr`` on all of the + node widgets for this ``TreeWidget``. + - ``node_attr``: Sets the attribute ``attr`` on all of the + leaf widgets for this ``TreeWidget``. + - ``loc_attr``: Sets the attribute ``attr`` on all of the + location widgets for this ``TreeWidget`` (if it was built from + a Tree). Note that a location widget is a ``TextWidget``. + + - ``xspace``: The amount of horizontal space to leave between + subtrees when managing this widget. Default value is 10. + - ``yspace``: The amount of space to place between the node and + its children when managing this widget. Default value is 15. + + - ``line_color``: The color of the lines connecting each expanded + node to its subtrees. + - ``roof_color``: The color of the outline of the triangular roof + for collapsed trees. + - ``roof_fill``: The fill color for the triangular roof for + collapsed trees. + - ``width`` + + - ``orientation``: Determines whether the tree branches downwards + or rightwards. Possible values are ``'horizontal'`` and + ``'vertical'``. The default value is ``'vertical'`` (i.e., + branch downwards). + + - ``shapeable``: whether the subtrees can be independently + dragged by the user. THIS property simply sets the + ``DRAGGABLE`` property on all of the ``TreeWidget``'s tree + segments. + - ``draggable``: whether the widget can be dragged by the user. + """ + + def __init__( + self, canvas, t, make_node=TextWidget, make_leaf=TextWidget, **attribs + ): + # Node & leaf canvas widget constructors + self._make_node = make_node + self._make_leaf = make_leaf + self._tree = t + + # Attributes. + self._nodeattribs = {} + self._leafattribs = {} + self._locattribs = {"color": "#008000"} + self._line_color = "#008080" + self._line_width = 1 + self._roof_color = "#008080" + self._roof_fill = "#c0c0c0" + self._shapeable = False + self._xspace = 10 + self._yspace = 10 + self._orientation = "vertical" + self._ordered = False + + # Build trees. + self._keys = {} # treeseg -> key + self._expanded_trees = {} + self._collapsed_trees = {} + self._nodes = [] + self._leaves = [] + # self._locs = [] + self._make_collapsed_trees(canvas, t, ()) + self._treeseg = self._make_expanded_tree(canvas, t, ()) + self._add_child_widget(self._treeseg) + + CanvasWidget.__init__(self, canvas, **attribs) + + def expanded_tree(self, *path_to_tree): + """ + Return the ``TreeSegmentWidget`` for the specified subtree. + + :param path_to_tree: A list of indices i1, i2, ..., in, where + the desired widget is the widget corresponding to + ``tree.children()[i1].children()[i2]....children()[in]``. + For the root, the path is ``()``. + """ + return self._expanded_trees[path_to_tree] + + def collapsed_tree(self, *path_to_tree): + """ + Return the ``TreeSegmentWidget`` for the specified subtree. + + :param path_to_tree: A list of indices i1, i2, ..., in, where + the desired widget is the widget corresponding to + ``tree.children()[i1].children()[i2]....children()[in]``. + For the root, the path is ``()``. + """ + return self._collapsed_trees[path_to_tree] + + def bind_click_trees(self, callback, button=1): + """ + Add a binding to all tree segments. + """ + for tseg in list(self._expanded_trees.values()): + tseg.bind_click(callback, button) + for tseg in list(self._collapsed_trees.values()): + tseg.bind_click(callback, button) + + def bind_drag_trees(self, callback, button=1): + """ + Add a binding to all tree segments. + """ + for tseg in list(self._expanded_trees.values()): + tseg.bind_drag(callback, button) + for tseg in list(self._collapsed_trees.values()): + tseg.bind_drag(callback, button) + + def bind_click_leaves(self, callback, button=1): + """ + Add a binding to all leaves. + """ + for leaf in self._leaves: + leaf.bind_click(callback, button) + for leaf in self._leaves: + leaf.bind_click(callback, button) + + def bind_drag_leaves(self, callback, button=1): + """ + Add a binding to all leaves. + """ + for leaf in self._leaves: + leaf.bind_drag(callback, button) + for leaf in self._leaves: + leaf.bind_drag(callback, button) + + def bind_click_nodes(self, callback, button=1): + """ + Add a binding to all nodes. + """ + for node in self._nodes: + node.bind_click(callback, button) + for node in self._nodes: + node.bind_click(callback, button) + + def bind_drag_nodes(self, callback, button=1): + """ + Add a binding to all nodes. + """ + for node in self._nodes: + node.bind_drag(callback, button) + for node in self._nodes: + node.bind_drag(callback, button) + + def _make_collapsed_trees(self, canvas, t, key): + if not isinstance(t, Tree): + return + make_node = self._make_node + make_leaf = self._make_leaf + + node = make_node(canvas, t.label(), **self._nodeattribs) + self._nodes.append(node) + leaves = [make_leaf(canvas, l, **self._leafattribs) for l in t.leaves()] + self._leaves += leaves + treeseg = TreeSegmentWidget( + canvas, + node, + leaves, + roof=1, + color=self._roof_color, + fill=self._roof_fill, + width=self._line_width, + ) + + self._collapsed_trees[key] = treeseg + self._keys[treeseg] = key + # self._add_child_widget(treeseg) + treeseg.hide() + + # Build trees for children. + for i in range(len(t)): + child = t[i] + self._make_collapsed_trees(canvas, child, key + (i,)) + + def _make_expanded_tree(self, canvas, t, key): + make_node = self._make_node + make_leaf = self._make_leaf + + if isinstance(t, Tree): + node = make_node(canvas, t.label(), **self._nodeattribs) + self._nodes.append(node) + children = t + subtrees = [ + self._make_expanded_tree(canvas, children[i], key + (i,)) + for i in range(len(children)) + ] + treeseg = TreeSegmentWidget( + canvas, node, subtrees, color=self._line_color, width=self._line_width + ) + self._expanded_trees[key] = treeseg + self._keys[treeseg] = key + return treeseg + else: + leaf = make_leaf(canvas, t, **self._leafattribs) + self._leaves.append(leaf) + return leaf + + def __setitem__(self, attr, value): + if attr[:5] == "node_": + for node in self._nodes: + node[attr[5:]] = value + elif attr[:5] == "leaf_": + for leaf in self._leaves: + leaf[attr[5:]] = value + elif attr == "line_color": + self._line_color = value + for tseg in list(self._expanded_trees.values()): + tseg["color"] = value + elif attr == "line_width": + self._line_width = value + for tseg in list(self._expanded_trees.values()): + tseg["width"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["width"] = value + elif attr == "roof_color": + self._roof_color = value + for tseg in list(self._collapsed_trees.values()): + tseg["color"] = value + elif attr == "roof_fill": + self._roof_fill = value + for tseg in list(self._collapsed_trees.values()): + tseg["fill"] = value + elif attr == "shapeable": + self._shapeable = value + for tseg in list(self._expanded_trees.values()): + tseg["draggable"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["draggable"] = value + for leaf in self._leaves: + leaf["draggable"] = value + elif attr == "xspace": + self._xspace = value + for tseg in list(self._expanded_trees.values()): + tseg["xspace"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["xspace"] = value + self.manage() + elif attr == "yspace": + self._yspace = value + for tseg in list(self._expanded_trees.values()): + tseg["yspace"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["yspace"] = value + self.manage() + elif attr == "orientation": + self._orientation = value + for tseg in list(self._expanded_trees.values()): + tseg["orientation"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["orientation"] = value + self.manage() + elif attr == "ordered": + self._ordered = value + for tseg in list(self._expanded_trees.values()): + tseg["ordered"] = value + for tseg in list(self._collapsed_trees.values()): + tseg["ordered"] = value + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr[:5] == "node_": + return self._nodeattribs.get(attr[5:], None) + elif attr[:5] == "leaf_": + return self._leafattribs.get(attr[5:], None) + elif attr[:4] == "loc_": + return self._locattribs.get(attr[4:], None) + elif attr == "line_color": + return self._line_color + elif attr == "line_width": + return self._line_width + elif attr == "roof_color": + return self._roof_color + elif attr == "roof_fill": + return self._roof_fill + elif attr == "shapeable": + return self._shapeable + elif attr == "xspace": + return self._xspace + elif attr == "yspace": + return self._yspace + elif attr == "orientation": + return self._orientation + else: + return CanvasWidget.__getitem__(self, attr) + + def _tags(self): + return [] + + def _manage(self): + segs = list(self._expanded_trees.values()) + list( + self._collapsed_trees.values() + ) + for tseg in segs: + if tseg.hidden(): + tseg.show() + tseg.manage() + tseg.hide() + + def toggle_collapsed(self, treeseg): + """ + Collapse/expand a tree. + """ + old_treeseg = treeseg + if old_treeseg["roof"]: + new_treeseg = self._expanded_trees[self._keys[old_treeseg]] + else: + new_treeseg = self._collapsed_trees[self._keys[old_treeseg]] + + # Replace the old tree with the new tree. + if old_treeseg.parent() is self: + self._remove_child_widget(old_treeseg) + self._add_child_widget(new_treeseg) + self._treeseg = new_treeseg + else: + old_treeseg.parent().replace_child(old_treeseg, new_treeseg) + + # Move the new tree to where the old tree was. Show it first, + # so we can find its bounding box. + new_treeseg.show() + (newx, newy) = new_treeseg.label().bbox()[:2] + (oldx, oldy) = old_treeseg.label().bbox()[:2] + new_treeseg.move(oldx - newx, oldy - newy) + + # Hide the old tree + old_treeseg.hide() + + # We could do parent.manage() here instead, if we wanted. + new_treeseg.parent().update(new_treeseg) + + +##////////////////////////////////////////////////////// +## draw_trees +##////////////////////////////////////////////////////// + + +class TreeView: + def __init__(self, *trees): + from math import ceil, sqrt + + self._trees = trees + + self._top = Tk() + self._top.title("NLTK") + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + + cf = self._cframe = CanvasFrame(self._top) + self._top.bind("", self._cframe.print_to_file) + + # Size is variable. + self._size = IntVar(self._top) + self._size.set(12) + bold = ("helvetica", -self._size.get(), "bold") + helv = ("helvetica", -self._size.get()) + + # Lay the trees out in a square. + self._width = int(ceil(sqrt(len(trees)))) + self._widgets = [] + for i in range(len(trees)): + widget = TreeWidget( + cf.canvas(), + trees[i], + node_font=bold, + leaf_color="#008040", + node_color="#004080", + roof_color="#004040", + roof_fill="white", + line_color="#004040", + draggable=1, + leaf_font=helv, + ) + widget.bind_click_trees(widget.toggle_collapsed) + self._widgets.append(widget) + cf.add_widget(widget, 0, 0) + + self._layout() + self._cframe.pack(expand=1, fill="both") + self._init_menubar() + + def _layout(self): + i = x = y = ymax = 0 + width = self._width + for i in range(len(self._widgets)): + widget = self._widgets[i] + (oldx, oldy) = widget.bbox()[:2] + if i % width == 0: + y = ymax + x = 0 + widget.move(x - oldx, y - oldy) + x = widget.bbox()[2] + 10 + ymax = max(ymax, widget.bbox()[3] + 10) + + def _init_menubar(self): + menubar = Menu(self._top) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Print to Postscript", + underline=0, + command=self._cframe.print_to_file, + accelerator="Ctrl-p", + ) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + zoommenu = Menu(menubar, tearoff=0) + zoommenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=12, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=14, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=28, + command=self.resize, + ) + zoommenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=50, + command=self.resize, + ) + menubar.add_cascade(label="Zoom", underline=0, menu=zoommenu) + + self._top.config(menu=menubar) + + def resize(self, *e): + bold = ("helvetica", -self._size.get(), "bold") + helv = ("helvetica", -self._size.get()) + xspace = self._size.get() + yspace = self._size.get() + for widget in self._widgets: + widget["node_font"] = bold + widget["leaf_font"] = helv + widget["xspace"] = xspace + widget["yspace"] = yspace + if self._size.get() < 20: + widget["line_width"] = 1 + elif self._size.get() < 30: + widget["line_width"] = 2 + else: + widget["line_width"] = 3 + self._layout() + + def destroy(self, *e): + if self._top is None: + return + self._top.destroy() + self._top = None + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self._top.mainloop(*args, **kwargs) + + +def draw_trees(*trees): + """ + Open a new window containing a graphical diagram of the given + trees. + + :rtype: None + """ + TreeView(*trees).mainloop() + return + + +##////////////////////////////////////////////////////// +## Demo Code +##////////////////////////////////////////////////////// + + +def demo(): + import random + + def fill(cw): + cw["fill"] = "#%06d" % random.randint(0, 999999) + + cf = CanvasFrame(width=550, height=450, closeenough=2) + + t = Tree.fromstring( + """ + (S (NP the very big cat) + (VP (Adv sorta) (V saw) (NP (Det the) (N dog))))""" + ) + + tc = TreeWidget( + cf.canvas(), + t, + draggable=1, + node_font=("helvetica", -14, "bold"), + leaf_font=("helvetica", -12, "italic"), + roof_fill="white", + roof_color="black", + leaf_color="green4", + node_color="blue2", + ) + cf.add_widget(tc, 10, 10) + + def boxit(canvas, text): + big = ("helvetica", -16, "bold") + return BoxWidget(canvas, TextWidget(canvas, text, font=big), fill="green") + + def ovalit(canvas, text): + return OvalWidget(canvas, TextWidget(canvas, text), fill="cyan") + + treetok = Tree.fromstring("(S (NP this tree) (VP (V is) (AdjP shapeable)))") + tc2 = TreeWidget(cf.canvas(), treetok, boxit, ovalit, shapeable=1) + + def color(node): + node["color"] = "#%04d00" % random.randint(0, 9999) + + def color2(treeseg): + treeseg.label()["fill"] = "#%06d" % random.randint(0, 9999) + treeseg.label().child()["color"] = "white" + + tc.bind_click_trees(tc.toggle_collapsed) + tc2.bind_click_trees(tc2.toggle_collapsed) + tc.bind_click_nodes(color, 3) + tc2.expanded_tree(1).bind_click(color2, 3) + tc2.expanded_tree().bind_click(color2, 3) + + paren = ParenWidget(cf.canvas(), tc2) + cf.add_widget(paren, tc.bbox()[2] + 10, 10) + + tree3 = Tree.fromstring( + """ + (S (NP this tree) (AUX was) + (VP (V built) (PP (P with) (NP (N tree_to_treesegment)))))""" + ) + tc3 = tree_to_treesegment( + cf.canvas(), tree3, tree_color="green4", tree_xspace=2, tree_width=2 + ) + tc3["draggable"] = 1 + cf.add_widget(tc3, 10, tc.bbox()[3] + 10) + + def orientswitch(treewidget): + if treewidget["orientation"] == "horizontal": + treewidget.expanded_tree(1, 1).subtrees()[0].set_text("vertical") + treewidget.collapsed_tree(1, 1).subtrees()[0].set_text("vertical") + treewidget.collapsed_tree(1).subtrees()[1].set_text("vertical") + treewidget.collapsed_tree().subtrees()[3].set_text("vertical") + treewidget["orientation"] = "vertical" + else: + treewidget.expanded_tree(1, 1).subtrees()[0].set_text("horizontal") + treewidget.collapsed_tree(1, 1).subtrees()[0].set_text("horizontal") + treewidget.collapsed_tree(1).subtrees()[1].set_text("horizontal") + treewidget.collapsed_tree().subtrees()[3].set_text("horizontal") + treewidget["orientation"] = "horizontal" + + text = """ +Try clicking, right clicking, and dragging +different elements of each of the trees. +The top-left tree is a TreeWidget built from +a Tree. The top-right is a TreeWidget built +from a Tree, using non-default widget +constructors for the nodes & leaves (BoxWidget +and OvalWidget). The bottom-left tree is +built from tree_to_treesegment.""" + twidget = TextWidget(cf.canvas(), text.strip()) + textbox = BoxWidget(cf.canvas(), twidget, fill="white", draggable=1) + cf.add_widget(textbox, tc3.bbox()[2] + 10, tc2.bbox()[3] + 10) + + tree4 = Tree.fromstring("(S (NP this tree) (VP (V is) (Adj horizontal)))") + tc4 = TreeWidget( + cf.canvas(), + tree4, + draggable=1, + line_color="brown2", + roof_color="brown2", + node_font=("helvetica", -12, "bold"), + node_color="brown4", + orientation="horizontal", + ) + tc4.manage() + cf.add_widget(tc4, tc3.bbox()[2] + 10, textbox.bbox()[3] + 10) + tc4.bind_click(orientswitch) + tc4.bind_click_trees(tc4.toggle_collapsed, 3) + + # Run mainloop + cf.mainloop() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/draw/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/util.py new file mode 100644 index 0000000..18ef4f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/draw/util.py @@ -0,0 +1,2575 @@ +# Natural Language Toolkit: Drawing utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Tools for graphically displaying and interacting with the objects and +processing classes defined by the Toolkit. These tools are primarily +intended to help students visualize the objects that they create. + +The graphical tools are typically built using "canvas widgets", each +of which encapsulates the graphical elements and bindings used to +display a complex object on a Tkinter ``Canvas``. For example, NLTK +defines canvas widgets for displaying trees and directed graphs, as +well as a number of simpler widgets. These canvas widgets make it +easier to build new graphical tools and demos. See the class +documentation for ``CanvasWidget`` for more information. + +The ``nltk.draw`` module defines the abstract ``CanvasWidget`` base +class, and a number of simple canvas widgets. The remaining canvas +widgets are defined by submodules, such as ``nltk.draw.tree``. + +The ``nltk.draw`` module also defines ``CanvasFrame``, which +encapsulates a ``Canvas`` and its scrollbars. It uses a +``ScrollWatcherWidget`` to ensure that all canvas widgets contained on +its canvas are within the scroll region. + +Acknowledgements: Many of the ideas behind the canvas widget system +are derived from ``CLIG``, a Tk-based grapher for linguistic data +structures. For more information, see the CLIG +homepage (http://www.ags.uni-sb.de/~konrad/clig.html). + +""" +from abc import ABCMeta, abstractmethod +from tkinter import ( + RAISED, + Button, + Canvas, + Entry, + Frame, + Label, + Menu, + Menubutton, + Scrollbar, + StringVar, + Text, + Tk, + Toplevel, + Widget, +) +from tkinter.filedialog import asksaveasfilename + +from nltk.util import in_idle + +##////////////////////////////////////////////////////// +## CanvasWidget +##////////////////////////////////////////////////////// + + +class CanvasWidget(metaclass=ABCMeta): + """ + A collection of graphical elements and bindings used to display a + complex object on a Tkinter ``Canvas``. A canvas widget is + responsible for managing the ``Canvas`` tags and callback bindings + necessary to display and interact with the object. Canvas widgets + are often organized into hierarchies, where parent canvas widgets + control aspects of their child widgets. + + Each canvas widget is bound to a single ``Canvas``. This ``Canvas`` + is specified as the first argument to the ``CanvasWidget``'s + constructor. + + Attributes. Each canvas widget can support a variety of + "attributes", which control how the canvas widget is displayed. + Some typical examples attributes are ``color``, ``font``, and + ``radius``. Each attribute has a default value. This default + value can be overridden in the constructor, using keyword + arguments of the form ``attribute=value``: + + >>> from nltk.draw.util import TextWidget + >>> cn = TextWidget(Canvas(), 'test', color='red') # doctest: +SKIP + + Attribute values can also be changed after a canvas widget has + been constructed, using the ``__setitem__`` operator: + + >>> cn['font'] = 'times' # doctest: +SKIP + + The current value of an attribute value can be queried using the + ``__getitem__`` operator: + + >>> cn['color'] # doctest: +SKIP + 'red' + + For a list of the attributes supported by a type of canvas widget, + see its class documentation. + + Interaction. The attribute ``'draggable'`` controls whether the + user can drag a canvas widget around the canvas. By default, + canvas widgets are not draggable. + + ``CanvasWidget`` provides callback support for two types of user + interaction: clicking and dragging. The method ``bind_click`` + registers a callback function that is called whenever the canvas + widget is clicked. The method ``bind_drag`` registers a callback + function that is called after the canvas widget is dragged. If + the user clicks or drags a canvas widget with no registered + callback function, then the interaction event will propagate to + its parent. For each canvas widget, only one callback function + may be registered for an interaction event. Callback functions + can be deregistered with the ``unbind_click`` and ``unbind_drag`` + methods. + + Subclassing. ``CanvasWidget`` is an abstract class. Subclasses + are required to implement the following methods: + + - ``__init__``: Builds a new canvas widget. It must perform the + following three tasks (in order): + + - Create any new graphical elements. + - Call ``_add_child_widget`` on each child widget. + - Call the ``CanvasWidget`` constructor. + - ``_tags``: Returns a list of the canvas tags for all graphical + elements managed by this canvas widget, not including + graphical elements managed by its child widgets. + - ``_manage``: Arranges the child widgets of this canvas widget. + This is typically only called when the canvas widget is + created. + - ``_update``: Update this canvas widget in response to a + change in a single child. + + For a ``CanvasWidget`` with no child widgets, the default + definitions for ``_manage`` and ``_update`` may be used. + + If a subclass defines any attributes, then it should implement + ``__getitem__`` and ``__setitem__``. If either of these methods is + called with an unknown attribute, then they should propagate the + request to ``CanvasWidget``. + + Most subclasses implement a number of additional methods that + modify the ``CanvasWidget`` in some way. These methods must call + ``parent.update(self)`` after making any changes to the canvas + widget's graphical elements. The canvas widget must also call + ``parent.update(self)`` after changing any attribute value that + affects the shape or position of the canvas widget's graphical + elements. + + :type __canvas: Tkinter.Canvas + :ivar __canvas: This ``CanvasWidget``'s canvas. + + :type __parent: CanvasWidget or None + :ivar __parent: This ``CanvasWidget``'s hierarchical parent widget. + :type __children: list(CanvasWidget) + :ivar __children: This ``CanvasWidget``'s hierarchical child widgets. + + :type __updating: bool + :ivar __updating: Is this canvas widget currently performing an + update? If it is, then it will ignore any new update requests + from child widgets. + + :type __draggable: bool + :ivar __draggable: Is this canvas widget draggable? + :type __press: event + :ivar __press: The ButtonPress event that we're currently handling. + :type __drag_x: int + :ivar __drag_x: Where it's been moved to (to find dx) + :type __drag_y: int + :ivar __drag_y: Where it's been moved to (to find dy) + :type __callbacks: dictionary + :ivar __callbacks: Registered callbacks. Currently, four keys are + used: ``1``, ``2``, ``3``, and ``'drag'``. The values are + callback functions. Each callback function takes a single + argument, which is the ``CanvasWidget`` that triggered the + callback. + """ + + def __init__(self, canvas, parent=None, **attribs): + """ + Create a new canvas widget. This constructor should only be + called by subclass constructors; and it should be called only + "after" the subclass has constructed all graphical canvas + objects and registered all child widgets. + + :param canvas: This canvas widget's canvas. + :type canvas: Tkinter.Canvas + :param parent: This canvas widget's hierarchical parent. + :type parent: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + if self.__class__ == CanvasWidget: + raise TypeError("CanvasWidget is an abstract base class") + + if not isinstance(canvas, Canvas): + raise TypeError("Expected a canvas!") + + self.__canvas = canvas + self.__parent = parent + + # If the subclass constructor called _add_child_widget, then + # self.__children will already exist. + if not hasattr(self, "_CanvasWidget__children"): + self.__children = [] + + # Is this widget hidden? + self.__hidden = 0 + + # Update control (prevents infinite loops) + self.__updating = 0 + + # Button-press and drag callback handling. + self.__press = None + self.__drag_x = self.__drag_y = 0 + self.__callbacks = {} + self.__draggable = 0 + + # Set up attributes. + for (attr, value) in list(attribs.items()): + self[attr] = value + + # Manage this canvas widget + self._manage() + + # Register any new bindings + for tag in self._tags(): + self.__canvas.tag_bind(tag, "", self.__press_cb) + self.__canvas.tag_bind(tag, "", self.__press_cb) + self.__canvas.tag_bind(tag, "", self.__press_cb) + + ##////////////////////////////////////////////////////// + ## Inherited methods. + ##////////////////////////////////////////////////////// + + def bbox(self): + """ + :return: A bounding box for this ``CanvasWidget``. The bounding + box is a tuple of four coordinates, *(xmin, ymin, xmax, ymax)*, + for a rectangle which encloses all of the canvas + widget's graphical elements. Bounding box coordinates are + specified with respect to the coordinate space of the ``Canvas``. + :rtype: tuple(int, int, int, int) + """ + if self.__hidden: + return (0, 0, 0, 0) + if len(self.tags()) == 0: + raise ValueError("No tags") + return self.__canvas.bbox(*self.tags()) + + def width(self): + """ + :return: The width of this canvas widget's bounding box, in + its ``Canvas``'s coordinate space. + :rtype: int + """ + if len(self.tags()) == 0: + raise ValueError("No tags") + bbox = self.__canvas.bbox(*self.tags()) + return bbox[2] - bbox[0] + + def height(self): + """ + :return: The height of this canvas widget's bounding box, in + its ``Canvas``'s coordinate space. + :rtype: int + """ + if len(self.tags()) == 0: + raise ValueError("No tags") + bbox = self.__canvas.bbox(*self.tags()) + return bbox[3] - bbox[1] + + def parent(self): + """ + :return: The hierarchical parent of this canvas widget. + ``self`` is considered a subpart of its parent for + purposes of user interaction. + :rtype: CanvasWidget or None + """ + return self.__parent + + def child_widgets(self): + """ + :return: A list of the hierarchical children of this canvas + widget. These children are considered part of ``self`` + for purposes of user interaction. + :rtype: list of CanvasWidget + """ + return self.__children + + def canvas(self): + """ + :return: The canvas that this canvas widget is bound to. + :rtype: Tkinter.Canvas + """ + return self.__canvas + + def move(self, dx, dy): + """ + Move this canvas widget by a given distance. In particular, + shift the canvas widget right by ``dx`` pixels, and down by + ``dy`` pixels. Both ``dx`` and ``dy`` may be negative, resulting + in leftward or upward movement. + + :type dx: int + :param dx: The number of pixels to move this canvas widget + rightwards. + :type dy: int + :param dy: The number of pixels to move this canvas widget + downwards. + :rtype: None + """ + if dx == dy == 0: + return + for tag in self.tags(): + self.__canvas.move(tag, dx, dy) + if self.__parent: + self.__parent.update(self) + + def moveto(self, x, y, anchor="NW"): + """ + Move this canvas widget to the given location. In particular, + shift the canvas widget such that the corner or side of the + bounding box specified by ``anchor`` is at location (``x``, + ``y``). + + :param x,y: The location that the canvas widget should be moved + to. + :param anchor: The corner or side of the canvas widget that + should be moved to the specified location. ``'N'`` + specifies the top center; ``'NE'`` specifies the top right + corner; etc. + """ + x1, y1, x2, y2 = self.bbox() + if anchor == "NW": + self.move(x - x1, y - y1) + if anchor == "N": + self.move(x - x1 / 2 - x2 / 2, y - y1) + if anchor == "NE": + self.move(x - x2, y - y1) + if anchor == "E": + self.move(x - x2, y - y1 / 2 - y2 / 2) + if anchor == "SE": + self.move(x - x2, y - y2) + if anchor == "S": + self.move(x - x1 / 2 - x2 / 2, y - y2) + if anchor == "SW": + self.move(x - x1, y - y2) + if anchor == "W": + self.move(x - x1, y - y1 / 2 - y2 / 2) + + def destroy(self): + """ + Remove this ``CanvasWidget`` from its ``Canvas``. After a + ``CanvasWidget`` has been destroyed, it should not be accessed. + + Note that you only need to destroy a top-level + ``CanvasWidget``; its child widgets will be destroyed + automatically. If you destroy a non-top-level + ``CanvasWidget``, then the entire top-level widget will be + destroyed. + + :raise ValueError: if this ``CanvasWidget`` has a parent. + :rtype: None + """ + if self.__parent is not None: + self.__parent.destroy() + return + + for tag in self.tags(): + self.__canvas.tag_unbind(tag, "") + self.__canvas.tag_unbind(tag, "") + self.__canvas.tag_unbind(tag, "") + self.__canvas.delete(*self.tags()) + self.__canvas = None + + def update(self, child): + """ + Update the graphical display of this canvas widget, and all of + its ancestors, in response to a change in one of this canvas + widget's children. + + :param child: The child widget that changed. + :type child: CanvasWidget + """ + if self.__hidden or child.__hidden: + return + # If we're already updating, then do nothing. This prevents + # infinite loops when _update modifies its children. + if self.__updating: + return + self.__updating = 1 + + # Update this CanvasWidget. + self._update(child) + + # Propagate update request to the parent. + if self.__parent: + self.__parent.update(self) + + # We're done updating. + self.__updating = 0 + + def manage(self): + """ + Arrange this canvas widget and all of its descendants. + + :rtype: None + """ + if self.__hidden: + return + for child in self.__children: + child.manage() + self._manage() + + def tags(self): + """ + :return: a list of the canvas tags for all graphical + elements managed by this canvas widget, including + graphical elements managed by its child widgets. + :rtype: list of int + """ + if self.__canvas is None: + raise ValueError("Attempt to access a destroyed canvas widget") + tags = [] + tags += self._tags() + for child in self.__children: + tags += child.tags() + return tags + + def __setitem__(self, attr, value): + """ + Set the value of the attribute ``attr`` to ``value``. See the + class documentation for a list of attributes supported by this + canvas widget. + + :rtype: None + """ + if attr == "draggable": + self.__draggable = value + else: + raise ValueError("Unknown attribute %r" % attr) + + def __getitem__(self, attr): + """ + :return: the value of the attribute ``attr``. See the class + documentation for a list of attributes supported by this + canvas widget. + :rtype: (any) + """ + if attr == "draggable": + return self.__draggable + else: + raise ValueError("Unknown attribute %r" % attr) + + def __repr__(self): + """ + :return: a string representation of this canvas widget. + :rtype: str + """ + return "<%s>" % self.__class__.__name__ + + def hide(self): + """ + Temporarily hide this canvas widget. + + :rtype: None + """ + self.__hidden = 1 + for tag in self.tags(): + self.__canvas.itemconfig(tag, state="hidden") + + def show(self): + """ + Show a hidden canvas widget. + + :rtype: None + """ + self.__hidden = 0 + for tag in self.tags(): + self.__canvas.itemconfig(tag, state="normal") + + def hidden(self): + """ + :return: True if this canvas widget is hidden. + :rtype: bool + """ + return self.__hidden + + ##////////////////////////////////////////////////////// + ## Callback interface + ##////////////////////////////////////////////////////// + + def bind_click(self, callback, button=1): + """ + Register a new callback that will be called whenever this + ``CanvasWidget`` is clicked on. + + :type callback: function + :param callback: The callback function that will be called + whenever this ``CanvasWidget`` is clicked. This function + will be called with this ``CanvasWidget`` as its argument. + :type button: int + :param button: Which button the user should use to click on + this ``CanvasWidget``. Typically, this should be 1 (left + button), 3 (right button), or 2 (middle button). + """ + self.__callbacks[button] = callback + + def bind_drag(self, callback): + """ + Register a new callback that will be called after this + ``CanvasWidget`` is dragged. This implicitly makes this + ``CanvasWidget`` draggable. + + :type callback: function + :param callback: The callback function that will be called + whenever this ``CanvasWidget`` is clicked. This function + will be called with this ``CanvasWidget`` as its argument. + """ + self.__draggable = 1 + self.__callbacks["drag"] = callback + + def unbind_click(self, button=1): + """ + Remove a callback that was registered with ``bind_click``. + + :type button: int + :param button: Which button the user should use to click on + this ``CanvasWidget``. Typically, this should be 1 (left + button), 3 (right button), or 2 (middle button). + """ + try: + del self.__callbacks[button] + except: + pass + + def unbind_drag(self): + """ + Remove a callback that was registered with ``bind_drag``. + """ + try: + del self.__callbacks["drag"] + except: + pass + + ##////////////////////////////////////////////////////// + ## Callback internals + ##////////////////////////////////////////////////////// + + def __press_cb(self, event): + """ + Handle a button-press event: + - record the button press event in ``self.__press`` + - register a button-release callback. + - if this CanvasWidget or any of its ancestors are + draggable, then register the appropriate motion callback. + """ + # If we're already waiting for a button release, then ignore + # this new button press. + if ( + self.__canvas.bind("") + or self.__canvas.bind("") + or self.__canvas.bind("") + ): + return + + # Unbind motion (just in case; this shouldn't be necessary) + self.__canvas.unbind("") + + # Record the button press event. + self.__press = event + + # If any ancestor is draggable, set up a motion callback. + # (Only if they pressed button number 1) + if event.num == 1: + widget = self + while widget is not None: + if widget["draggable"]: + widget.__start_drag(event) + break + widget = widget.parent() + + # Set up the button release callback. + self.__canvas.bind("" % event.num, self.__release_cb) + + def __start_drag(self, event): + """ + Begin dragging this object: + - register a motion callback + - record the drag coordinates + """ + self.__canvas.bind("", self.__motion_cb) + self.__drag_x = event.x + self.__drag_y = event.y + + def __motion_cb(self, event): + """ + Handle a motion event: + - move this object to the new location + - record the new drag coordinates + """ + self.move(event.x - self.__drag_x, event.y - self.__drag_y) + self.__drag_x = event.x + self.__drag_y = event.y + + def __release_cb(self, event): + """ + Handle a release callback: + - unregister motion & button release callbacks. + - decide whether they clicked, dragged, or cancelled + - call the appropriate handler. + """ + # Unbind the button release & motion callbacks. + self.__canvas.unbind("" % event.num) + self.__canvas.unbind("") + + # Is it a click or a drag? + if ( + event.time - self.__press.time < 100 + and abs(event.x - self.__press.x) + abs(event.y - self.__press.y) < 5 + ): + # Move it back, if we were dragging. + if self.__draggable and event.num == 1: + self.move( + self.__press.x - self.__drag_x, self.__press.y - self.__drag_y + ) + self.__click(event.num) + elif event.num == 1: + self.__drag() + + self.__press = None + + def __drag(self): + """ + If this ``CanvasWidget`` has a drag callback, then call it; + otherwise, find the closest ancestor with a drag callback, and + call it. If no ancestors have a drag callback, do nothing. + """ + if self.__draggable: + if "drag" in self.__callbacks: + cb = self.__callbacks["drag"] + try: + cb(self) + except: + print("Error in drag callback for %r" % self) + elif self.__parent is not None: + self.__parent.__drag() + + def __click(self, button): + """ + If this ``CanvasWidget`` has a drag callback, then call it; + otherwise, find the closest ancestor with a click callback, and + call it. If no ancestors have a click callback, do nothing. + """ + if button in self.__callbacks: + cb = self.__callbacks[button] + # try: + cb(self) + # except: + # print('Error in click callback for %r' % self) + # raise + elif self.__parent is not None: + self.__parent.__click(button) + + ##////////////////////////////////////////////////////// + ## Child/parent Handling + ##////////////////////////////////////////////////////// + + def _add_child_widget(self, child): + """ + Register a hierarchical child widget. The child will be + considered part of this canvas widget for purposes of user + interaction. ``_add_child_widget`` has two direct effects: + - It sets ``child``'s parent to this canvas widget. + - It adds ``child`` to the list of canvas widgets returned by + the ``child_widgets`` member function. + + :param child: The new child widget. ``child`` must not already + have a parent. + :type child: CanvasWidget + """ + if not hasattr(self, "_CanvasWidget__children"): + self.__children = [] + if child.__parent is not None: + raise ValueError(f"{child} already has a parent") + child.__parent = self + self.__children.append(child) + + def _remove_child_widget(self, child): + """ + Remove a hierarchical child widget. This child will no longer + be considered part of this canvas widget for purposes of user + interaction. ``_add_child_widget`` has two direct effects: + - It sets ``child``'s parent to None. + - It removes ``child`` from the list of canvas widgets + returned by the ``child_widgets`` member function. + + :param child: The child widget to remove. ``child`` must be a + child of this canvas widget. + :type child: CanvasWidget + """ + self.__children.remove(child) + child.__parent = None + + ##////////////////////////////////////////////////////// + ## Defined by subclass + ##////////////////////////////////////////////////////// + + @abstractmethod + def _tags(self): + """ + :return: a list of canvas tags for all graphical elements + managed by this canvas widget, not including graphical + elements managed by its child widgets. + :rtype: list of int + """ + + def _manage(self): + """ + Arrange the child widgets of this canvas widget. This method + is called when the canvas widget is initially created. It is + also called if the user calls the ``manage`` method on this + canvas widget or any of its ancestors. + + :rtype: None + """ + + def _update(self, child): + """ + Update this canvas widget in response to a change in one of + its children. + + :param child: The child that changed. + :type child: CanvasWidget + :rtype: None + """ + + +##////////////////////////////////////////////////////// +## Basic widgets. +##////////////////////////////////////////////////////// + + +class TextWidget(CanvasWidget): + """ + A canvas widget that displays a single string of text. + + Attributes: + - ``color``: the color of the text. + - ``font``: the font used to display the text. + - ``justify``: justification for multi-line texts. Valid values + are ``left``, ``center``, and ``right``. + - ``width``: the width of the text. If the text is wider than + this width, it will be line-wrapped at whitespace. + - ``draggable``: whether the text can be dragged by the user. + """ + + def __init__(self, canvas, text, **attribs): + """ + Create a new text widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :type text: str + :param text: The string of text to display. + :param attribs: The new canvas widget's attributes. + """ + self._text = text + self._tag = canvas.create_text(1, 1, text=text) + CanvasWidget.__init__(self, canvas, **attribs) + + def __setitem__(self, attr, value): + if attr in ("color", "font", "justify", "width"): + if attr == "color": + attr = "fill" + self.canvas().itemconfig(self._tag, {attr: value}) + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "width": + return int(self.canvas().itemcget(self._tag, attr)) + elif attr in ("color", "font", "justify"): + if attr == "color": + attr = "fill" + return self.canvas().itemcget(self._tag, attr) + else: + return CanvasWidget.__getitem__(self, attr) + + def _tags(self): + return [self._tag] + + def text(self): + """ + :return: The text displayed by this text widget. + :rtype: str + """ + return self.canvas().itemcget(self._tag, "TEXT") + + def set_text(self, text): + """ + Change the text that is displayed by this text widget. + + :type text: str + :param text: The string of text to display. + :rtype: None + """ + self.canvas().itemconfig(self._tag, text=text) + if self.parent() is not None: + self.parent().update(self) + + def __repr__(self): + return "[Text: %r]" % self._text + + +class SymbolWidget(TextWidget): + """ + A canvas widget that displays special symbols, such as the + negation sign and the exists operator. Symbols are specified by + name. Currently, the following symbol names are defined: ``neg``, + ``disj``, ``conj``, ``lambda``, ``merge``, ``forall``, ``exists``, + ``subseteq``, ``subset``, ``notsubset``, ``emptyset``, ``imp``, + ``rightarrow``, ``equal``, ``notequal``, ``epsilon``. + + Attributes: + + - ``color``: the color of the text. + - ``draggable``: whether the text can be dragged by the user. + + :cvar SYMBOLS: A dictionary mapping from symbols to the character + in the ``symbol`` font used to render them. + """ + + SYMBOLS = { + "neg": "\330", + "disj": "\332", + "conj": "\331", + "lambda": "\154", + "merge": "\304", + "forall": "\042", + "exists": "\044", + "subseteq": "\315", + "subset": "\314", + "notsubset": "\313", + "emptyset": "\306", + "imp": "\336", + "rightarrow": chr(222), #'\256', + "equal": "\75", + "notequal": "\271", + "intersection": "\307", + "union": "\310", + "epsilon": "e", + } + + def __init__(self, canvas, symbol, **attribs): + """ + Create a new symbol widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :type symbol: str + :param symbol: The name of the symbol to display. + :param attribs: The new canvas widget's attributes. + """ + attribs["font"] = "symbol" + TextWidget.__init__(self, canvas, "", **attribs) + self.set_symbol(symbol) + + def symbol(self): + """ + :return: the name of the symbol that is displayed by this + symbol widget. + :rtype: str + """ + return self._symbol + + def set_symbol(self, symbol): + """ + Change the symbol that is displayed by this symbol widget. + + :type symbol: str + :param symbol: The name of the symbol to display. + """ + if symbol not in SymbolWidget.SYMBOLS: + raise ValueError("Unknown symbol: %s" % symbol) + self._symbol = symbol + self.set_text(SymbolWidget.SYMBOLS[symbol]) + + def __repr__(self): + return "[Symbol: %r]" % self._symbol + + @staticmethod + def symbolsheet(size=20): + """ + Open a new Tkinter window that displays the entire alphabet + for the symbol font. This is useful for constructing the + ``SymbolWidget.SYMBOLS`` dictionary. + """ + top = Tk() + + def destroy(e, top=top): + top.destroy() + + top.bind("q", destroy) + Button(top, text="Quit", command=top.destroy).pack(side="bottom") + text = Text(top, font=("helvetica", -size), width=20, height=30) + text.pack(side="left") + sb = Scrollbar(top, command=text.yview) + text["yscrollcommand"] = sb.set + sb.pack(side="right", fill="y") + text.tag_config("symbol", font=("symbol", -size)) + for i in range(256): + if i in (0, 10): + continue # null and newline + for k, v in list(SymbolWidget.SYMBOLS.items()): + if v == chr(i): + text.insert("end", "%-10s\t" % k) + break + else: + text.insert("end", "%-10d \t" % i) + text.insert("end", "[%s]\n" % chr(i), "symbol") + top.mainloop() + + +class AbstractContainerWidget(CanvasWidget): + """ + An abstract class for canvas widgets that contain a single child, + such as ``BoxWidget`` and ``OvalWidget``. Subclasses must define + a constructor, which should create any new graphical elements and + then call the ``AbstractCanvasContainer`` constructor. Subclasses + must also define the ``_update`` method and the ``_tags`` method; + and any subclasses that define attributes should define + ``__setitem__`` and ``__getitem__``. + """ + + def __init__(self, canvas, child, **attribs): + """ + Create a new container widget. This constructor should only + be called by subclass constructors. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param child: The container's child widget. ``child`` must not + have a parent. + :type child: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + self._child = child + self._add_child_widget(child) + CanvasWidget.__init__(self, canvas, **attribs) + + def _manage(self): + self._update(self._child) + + def child(self): + """ + :return: The child widget contained by this container widget. + :rtype: CanvasWidget + """ + return self._child + + def set_child(self, child): + """ + Change the child widget contained by this container widget. + + :param child: The new child widget. ``child`` must not have a + parent. + :type child: CanvasWidget + :rtype: None + """ + self._remove_child_widget(self._child) + self._add_child_widget(child) + self._child = child + self.update(child) + + def __repr__(self): + name = self.__class__.__name__ + if name[-6:] == "Widget": + name = name[:-6] + return f"[{name}: {self._child!r}]" + + +class BoxWidget(AbstractContainerWidget): + """ + A canvas widget that places a box around a child widget. + + Attributes: + - ``fill``: The color used to fill the interior of the box. + - ``outline``: The color used to draw the outline of the box. + - ``width``: The width of the outline of the box. + - ``margin``: The number of pixels space left between the child + and the box. + - ``draggable``: whether the text can be dragged by the user. + """ + + def __init__(self, canvas, child, **attribs): + """ + Create a new box widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param child: The child widget. ``child`` must not have a + parent. + :type child: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + self._child = child + self._margin = 1 + self._box = canvas.create_rectangle(1, 1, 1, 1) + canvas.tag_lower(self._box) + AbstractContainerWidget.__init__(self, canvas, child, **attribs) + + def __setitem__(self, attr, value): + if attr == "margin": + self._margin = value + elif attr in ("outline", "fill", "width"): + self.canvas().itemconfig(self._box, {attr: value}) + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "margin": + return self._margin + elif attr == "width": + return float(self.canvas().itemcget(self._box, attr)) + elif attr in ("outline", "fill", "width"): + return self.canvas().itemcget(self._box, attr) + else: + return CanvasWidget.__getitem__(self, attr) + + def _update(self, child): + (x1, y1, x2, y2) = child.bbox() + margin = self._margin + self["width"] / 2 + self.canvas().coords( + self._box, x1 - margin, y1 - margin, x2 + margin, y2 + margin + ) + + def _tags(self): + return [self._box] + + +class OvalWidget(AbstractContainerWidget): + """ + A canvas widget that places a oval around a child widget. + + Attributes: + - ``fill``: The color used to fill the interior of the oval. + - ``outline``: The color used to draw the outline of the oval. + - ``width``: The width of the outline of the oval. + - ``margin``: The number of pixels space left between the child + and the oval. + - ``draggable``: whether the text can be dragged by the user. + - ``double``: If true, then a double-oval is drawn. + """ + + def __init__(self, canvas, child, **attribs): + """ + Create a new oval widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param child: The child widget. ``child`` must not have a + parent. + :type child: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + self._child = child + self._margin = 1 + self._oval = canvas.create_oval(1, 1, 1, 1) + self._circle = attribs.pop("circle", False) + self._double = attribs.pop("double", False) + if self._double: + self._oval2 = canvas.create_oval(1, 1, 1, 1) + else: + self._oval2 = None + canvas.tag_lower(self._oval) + AbstractContainerWidget.__init__(self, canvas, child, **attribs) + + def __setitem__(self, attr, value): + c = self.canvas() + if attr == "margin": + self._margin = value + elif attr == "double": + if value == True and self._oval2 is None: + # Copy attributes & position from self._oval. + x1, y1, x2, y2 = c.bbox(self._oval) + w = self["width"] * 2 + self._oval2 = c.create_oval( + x1 - w, + y1 - w, + x2 + w, + y2 + w, + outline=c.itemcget(self._oval, "outline"), + width=c.itemcget(self._oval, "width"), + ) + c.tag_lower(self._oval2) + if value == False and self._oval2 is not None: + c.delete(self._oval2) + self._oval2 = None + elif attr in ("outline", "fill", "width"): + c.itemconfig(self._oval, {attr: value}) + if self._oval2 is not None and attr != "fill": + c.itemconfig(self._oval2, {attr: value}) + if self._oval2 is not None and attr != "fill": + self.canvas().itemconfig(self._oval2, {attr: value}) + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "margin": + return self._margin + elif attr == "double": + return self._double is not None + elif attr == "width": + return float(self.canvas().itemcget(self._oval, attr)) + elif attr in ("outline", "fill", "width"): + return self.canvas().itemcget(self._oval, attr) + else: + return CanvasWidget.__getitem__(self, attr) + + # The ratio between inscribed & circumscribed ovals + RATIO = 1.4142135623730949 + + def _update(self, child): + R = OvalWidget.RATIO + (x1, y1, x2, y2) = child.bbox() + margin = self._margin + + # If we're a circle, pretend our contents are square. + if self._circle: + dx, dy = abs(x1 - x2), abs(y1 - y2) + if dx > dy: + y = (y1 + y2) / 2 + y1, y2 = y - dx / 2, y + dx / 2 + elif dy > dx: + x = (x1 + x2) / 2 + x1, x2 = x - dy / 2, x + dy / 2 + + # Find the four corners. + left = int((x1 * (1 + R) + x2 * (1 - R)) / 2) + right = left + int((x2 - x1) * R) + top = int((y1 * (1 + R) + y2 * (1 - R)) / 2) + bot = top + int((y2 - y1) * R) + self.canvas().coords( + self._oval, left - margin, top - margin, right + margin, bot + margin + ) + if self._oval2 is not None: + self.canvas().coords( + self._oval2, + left - margin + 2, + top - margin + 2, + right + margin - 2, + bot + margin - 2, + ) + + def _tags(self): + if self._oval2 is None: + return [self._oval] + else: + return [self._oval, self._oval2] + + +class ParenWidget(AbstractContainerWidget): + """ + A canvas widget that places a pair of parenthases around a child + widget. + + Attributes: + - ``color``: The color used to draw the parenthases. + - ``width``: The width of the parenthases. + - ``draggable``: whether the text can be dragged by the user. + """ + + def __init__(self, canvas, child, **attribs): + """ + Create a new parenthasis widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param child: The child widget. ``child`` must not have a + parent. + :type child: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + self._child = child + self._oparen = canvas.create_arc(1, 1, 1, 1, style="arc", start=90, extent=180) + self._cparen = canvas.create_arc(1, 1, 1, 1, style="arc", start=-90, extent=180) + AbstractContainerWidget.__init__(self, canvas, child, **attribs) + + def __setitem__(self, attr, value): + if attr == "color": + self.canvas().itemconfig(self._oparen, outline=value) + self.canvas().itemconfig(self._cparen, outline=value) + elif attr == "width": + self.canvas().itemconfig(self._oparen, width=value) + self.canvas().itemconfig(self._cparen, width=value) + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "color": + return self.canvas().itemcget(self._oparen, "outline") + elif attr == "width": + return self.canvas().itemcget(self._oparen, "width") + else: + return CanvasWidget.__getitem__(self, attr) + + def _update(self, child): + (x1, y1, x2, y2) = child.bbox() + width = max((y2 - y1) / 6, 4) + self.canvas().coords(self._oparen, x1 - width, y1, x1 + width, y2) + self.canvas().coords(self._cparen, x2 - width, y1, x2 + width, y2) + + def _tags(self): + return [self._oparen, self._cparen] + + +class BracketWidget(AbstractContainerWidget): + """ + A canvas widget that places a pair of brackets around a child + widget. + + Attributes: + - ``color``: The color used to draw the brackets. + - ``width``: The width of the brackets. + - ``draggable``: whether the text can be dragged by the user. + """ + + def __init__(self, canvas, child, **attribs): + """ + Create a new bracket widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param child: The child widget. ``child`` must not have a + parent. + :type child: CanvasWidget + :param attribs: The new canvas widget's attributes. + """ + self._child = child + self._obrack = canvas.create_line(1, 1, 1, 1, 1, 1, 1, 1) + self._cbrack = canvas.create_line(1, 1, 1, 1, 1, 1, 1, 1) + AbstractContainerWidget.__init__(self, canvas, child, **attribs) + + def __setitem__(self, attr, value): + if attr == "color": + self.canvas().itemconfig(self._obrack, fill=value) + self.canvas().itemconfig(self._cbrack, fill=value) + elif attr == "width": + self.canvas().itemconfig(self._obrack, width=value) + self.canvas().itemconfig(self._cbrack, width=value) + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "color": + return self.canvas().itemcget(self._obrack, "outline") + elif attr == "width": + return self.canvas().itemcget(self._obrack, "width") + else: + return CanvasWidget.__getitem__(self, attr) + + def _update(self, child): + (x1, y1, x2, y2) = child.bbox() + width = max((y2 - y1) / 8, 2) + self.canvas().coords( + self._obrack, x1, y1, x1 - width, y1, x1 - width, y2, x1, y2 + ) + self.canvas().coords( + self._cbrack, x2, y1, x2 + width, y1, x2 + width, y2, x2, y2 + ) + + def _tags(self): + return [self._obrack, self._cbrack] + + +class SequenceWidget(CanvasWidget): + """ + A canvas widget that keeps a list of canvas widgets in a + horizontal line. + + Attributes: + - ``align``: The vertical alignment of the children. Possible + values are ``'top'``, ``'center'``, and ``'bottom'``. By + default, children are center-aligned. + - ``space``: The amount of horizontal space to place between + children. By default, one pixel of space is used. + - ``ordered``: If true, then keep the children in their + original order. + """ + + def __init__(self, canvas, *children, **attribs): + """ + Create a new sequence widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param children: The widgets that should be aligned + horizontally. Each child must not have a parent. + :type children: list(CanvasWidget) + :param attribs: The new canvas widget's attributes. + """ + self._align = "center" + self._space = 1 + self._ordered = False + self._children = list(children) + for child in children: + self._add_child_widget(child) + CanvasWidget.__init__(self, canvas, **attribs) + + def __setitem__(self, attr, value): + if attr == "align": + if value not in ("top", "bottom", "center"): + raise ValueError("Bad alignment: %r" % value) + self._align = value + elif attr == "space": + self._space = value + elif attr == "ordered": + self._ordered = value + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "align": + return self._align + elif attr == "space": + return self._space + elif attr == "ordered": + return self._ordered + else: + return CanvasWidget.__getitem__(self, attr) + + def _tags(self): + return [] + + def _yalign(self, top, bot): + if self._align == "top": + return top + if self._align == "bottom": + return bot + if self._align == "center": + return (top + bot) / 2 + + def _update(self, child): + # Align all children with child. + (left, top, right, bot) = child.bbox() + y = self._yalign(top, bot) + for c in self._children: + (x1, y1, x2, y2) = c.bbox() + c.move(0, y - self._yalign(y1, y2)) + + if self._ordered and len(self._children) > 1: + index = self._children.index(child) + + x = right + self._space + for i in range(index + 1, len(self._children)): + (x1, y1, x2, y2) = self._children[i].bbox() + if x > x1: + self._children[i].move(x - x1, 0) + x += x2 - x1 + self._space + + x = left - self._space + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._children[i].bbox() + if x < x2: + self._children[i].move(x - x2, 0) + x -= x2 - x1 + self._space + + def _manage(self): + if len(self._children) == 0: + return + child = self._children[0] + + # Align all children with child. + (left, top, right, bot) = child.bbox() + y = self._yalign(top, bot) + + index = self._children.index(child) + + # Line up children to the right of child. + x = right + self._space + for i in range(index + 1, len(self._children)): + (x1, y1, x2, y2) = self._children[i].bbox() + self._children[i].move(x - x1, y - self._yalign(y1, y2)) + x += x2 - x1 + self._space + + # Line up children to the left of child. + x = left - self._space + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._children[i].bbox() + self._children[i].move(x - x2, y - self._yalign(y1, y2)) + x -= x2 - x1 + self._space + + def __repr__(self): + return "[Sequence: " + repr(self._children)[1:-1] + "]" + + # Provide an alias for the child_widgets() member. + children = CanvasWidget.child_widgets + + def replace_child(self, oldchild, newchild): + """ + Replace the child canvas widget ``oldchild`` with ``newchild``. + ``newchild`` must not have a parent. ``oldchild``'s parent will + be set to None. + + :type oldchild: CanvasWidget + :param oldchild: The child canvas widget to remove. + :type newchild: CanvasWidget + :param newchild: The canvas widget that should replace + ``oldchild``. + """ + index = self._children.index(oldchild) + self._children[index] = newchild + self._remove_child_widget(oldchild) + self._add_child_widget(newchild) + self.update(newchild) + + def remove_child(self, child): + """ + Remove the given child canvas widget. ``child``'s parent will + be set to None. + + :type child: CanvasWidget + :param child: The child canvas widget to remove. + """ + index = self._children.index(child) + del self._children[index] + self._remove_child_widget(child) + if len(self._children) > 0: + self.update(self._children[0]) + + def insert_child(self, index, child): + """ + Insert a child canvas widget before a given index. + + :type child: CanvasWidget + :param child: The canvas widget that should be inserted. + :type index: int + :param index: The index where the child widget should be + inserted. In particular, the index of ``child`` will be + ``index``; and the index of any children whose indices were + greater than equal to ``index`` before ``child`` was + inserted will be incremented by one. + """ + self._children.insert(index, child) + self._add_child_widget(child) + + +class StackWidget(CanvasWidget): + """ + A canvas widget that keeps a list of canvas widgets in a vertical + line. + + Attributes: + - ``align``: The horizontal alignment of the children. Possible + values are ``'left'``, ``'center'``, and ``'right'``. By + default, children are center-aligned. + - ``space``: The amount of vertical space to place between + children. By default, one pixel of space is used. + - ``ordered``: If true, then keep the children in their + original order. + """ + + def __init__(self, canvas, *children, **attribs): + """ + Create a new stack widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :param children: The widgets that should be aligned + vertically. Each child must not have a parent. + :type children: list(CanvasWidget) + :param attribs: The new canvas widget's attributes. + """ + self._align = "center" + self._space = 1 + self._ordered = False + self._children = list(children) + for child in children: + self._add_child_widget(child) + CanvasWidget.__init__(self, canvas, **attribs) + + def __setitem__(self, attr, value): + if attr == "align": + if value not in ("left", "right", "center"): + raise ValueError("Bad alignment: %r" % value) + self._align = value + elif attr == "space": + self._space = value + elif attr == "ordered": + self._ordered = value + else: + CanvasWidget.__setitem__(self, attr, value) + + def __getitem__(self, attr): + if attr == "align": + return self._align + elif attr == "space": + return self._space + elif attr == "ordered": + return self._ordered + else: + return CanvasWidget.__getitem__(self, attr) + + def _tags(self): + return [] + + def _xalign(self, left, right): + if self._align == "left": + return left + if self._align == "right": + return right + if self._align == "center": + return (left + right) / 2 + + def _update(self, child): + # Align all children with child. + (left, top, right, bot) = child.bbox() + x = self._xalign(left, right) + for c in self._children: + (x1, y1, x2, y2) = c.bbox() + c.move(x - self._xalign(x1, x2), 0) + + if self._ordered and len(self._children) > 1: + index = self._children.index(child) + + y = bot + self._space + for i in range(index + 1, len(self._children)): + (x1, y1, x2, y2) = self._children[i].bbox() + if y > y1: + self._children[i].move(0, y - y1) + y += y2 - y1 + self._space + + y = top - self._space + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._children[i].bbox() + if y < y2: + self._children[i].move(0, y - y2) + y -= y2 - y1 + self._space + + def _manage(self): + if len(self._children) == 0: + return + child = self._children[0] + + # Align all children with child. + (left, top, right, bot) = child.bbox() + x = self._xalign(left, right) + + index = self._children.index(child) + + # Line up children below the child. + y = bot + self._space + for i in range(index + 1, len(self._children)): + (x1, y1, x2, y2) = self._children[i].bbox() + self._children[i].move(x - self._xalign(x1, x2), y - y1) + y += y2 - y1 + self._space + + # Line up children above the child. + y = top - self._space + for i in range(index - 1, -1, -1): + (x1, y1, x2, y2) = self._children[i].bbox() + self._children[i].move(x - self._xalign(x1, x2), y - y2) + y -= y2 - y1 + self._space + + def __repr__(self): + return "[Stack: " + repr(self._children)[1:-1] + "]" + + # Provide an alias for the child_widgets() member. + children = CanvasWidget.child_widgets + + def replace_child(self, oldchild, newchild): + """ + Replace the child canvas widget ``oldchild`` with ``newchild``. + ``newchild`` must not have a parent. ``oldchild``'s parent will + be set to None. + + :type oldchild: CanvasWidget + :param oldchild: The child canvas widget to remove. + :type newchild: CanvasWidget + :param newchild: The canvas widget that should replace + ``oldchild``. + """ + index = self._children.index(oldchild) + self._children[index] = newchild + self._remove_child_widget(oldchild) + self._add_child_widget(newchild) + self.update(newchild) + + def remove_child(self, child): + """ + Remove the given child canvas widget. ``child``'s parent will + be set to None. + + :type child: CanvasWidget + :param child: The child canvas widget to remove. + """ + index = self._children.index(child) + del self._children[index] + self._remove_child_widget(child) + if len(self._children) > 0: + self.update(self._children[0]) + + def insert_child(self, index, child): + """ + Insert a child canvas widget before a given index. + + :type child: CanvasWidget + :param child: The canvas widget that should be inserted. + :type index: int + :param index: The index where the child widget should be + inserted. In particular, the index of ``child`` will be + ``index``; and the index of any children whose indices were + greater than equal to ``index`` before ``child`` was + inserted will be incremented by one. + """ + self._children.insert(index, child) + self._add_child_widget(child) + + +class SpaceWidget(CanvasWidget): + """ + A canvas widget that takes up space but does not display + anything. A ``SpaceWidget`` can be used to add space between + elements. Each space widget is characterized by a width and a + height. If you wish to only create horizontal space, then use a + height of zero; and if you wish to only create vertical space, use + a width of zero. + """ + + def __init__(self, canvas, width, height, **attribs): + """ + Create a new space widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :type width: int + :param width: The width of the new space widget. + :type height: int + :param height: The height of the new space widget. + :param attribs: The new canvas widget's attributes. + """ + # For some reason, + if width > 4: + width -= 4 + if height > 4: + height -= 4 + self._tag = canvas.create_line(1, 1, width, height, fill="") + CanvasWidget.__init__(self, canvas, **attribs) + + # note: width() and height() are already defined by CanvasWidget. + def set_width(self, width): + """ + Change the width of this space widget. + + :param width: The new width. + :type width: int + :rtype: None + """ + [x1, y1, x2, y2] = self.bbox() + self.canvas().coords(self._tag, x1, y1, x1 + width, y2) + + def set_height(self, height): + """ + Change the height of this space widget. + + :param height: The new height. + :type height: int + :rtype: None + """ + [x1, y1, x2, y2] = self.bbox() + self.canvas().coords(self._tag, x1, y1, x2, y1 + height) + + def _tags(self): + return [self._tag] + + def __repr__(self): + return "[Space]" + + +class ScrollWatcherWidget(CanvasWidget): + """ + A special canvas widget that adjusts its ``Canvas``'s scrollregion + to always include the bounding boxes of all of its children. The + scroll-watcher widget will only increase the size of the + ``Canvas``'s scrollregion; it will never decrease it. + """ + + def __init__(self, canvas, *children, **attribs): + """ + Create a new scroll-watcher widget. + + :type canvas: Tkinter.Canvas + :param canvas: This canvas widget's canvas. + :type children: list(CanvasWidget) + :param children: The canvas widgets watched by the + scroll-watcher. The scroll-watcher will ensure that these + canvas widgets are always contained in their canvas's + scrollregion. + :param attribs: The new canvas widget's attributes. + """ + for child in children: + self._add_child_widget(child) + CanvasWidget.__init__(self, canvas, **attribs) + + def add_child(self, canvaswidget): + """ + Add a new canvas widget to the scroll-watcher. The + scroll-watcher will ensure that the new canvas widget is + always contained in its canvas's scrollregion. + + :param canvaswidget: The new canvas widget. + :type canvaswidget: CanvasWidget + :rtype: None + """ + self._add_child_widget(canvaswidget) + self.update(canvaswidget) + + def remove_child(self, canvaswidget): + """ + Remove a canvas widget from the scroll-watcher. The + scroll-watcher will no longer ensure that the new canvas + widget is always contained in its canvas's scrollregion. + + :param canvaswidget: The canvas widget to remove. + :type canvaswidget: CanvasWidget + :rtype: None + """ + self._remove_child_widget(canvaswidget) + + def _tags(self): + return [] + + def _update(self, child): + self._adjust_scrollregion() + + def _adjust_scrollregion(self): + """ + Adjust the scrollregion of this scroll-watcher's ``Canvas`` to + include the bounding boxes of all of its children. + """ + bbox = self.bbox() + canvas = self.canvas() + scrollregion = [int(n) for n in canvas["scrollregion"].split()] + if len(scrollregion) != 4: + return + if ( + bbox[0] < scrollregion[0] + or bbox[1] < scrollregion[1] + or bbox[2] > scrollregion[2] + or bbox[3] > scrollregion[3] + ): + scrollregion = "%d %d %d %d" % ( + min(bbox[0], scrollregion[0]), + min(bbox[1], scrollregion[1]), + max(bbox[2], scrollregion[2]), + max(bbox[3], scrollregion[3]), + ) + canvas["scrollregion"] = scrollregion + + +##////////////////////////////////////////////////////// +## Canvas Frame +##////////////////////////////////////////////////////// + + +class CanvasFrame: + """ + A ``Tkinter`` frame containing a canvas and scrollbars. + ``CanvasFrame`` uses a ``ScrollWatcherWidget`` to ensure that all of + the canvas widgets contained on its canvas are within its + scrollregion. In order for ``CanvasFrame`` to make these checks, + all canvas widgets must be registered with ``add_widget`` when they + are added to the canvas; and destroyed with ``destroy_widget`` when + they are no longer needed. + + If a ``CanvasFrame`` is created with no parent, then it will create + its own main window, including a "Done" button and a "Print" + button. + """ + + def __init__(self, parent=None, **kw): + """ + Create a new ``CanvasFrame``. + + :type parent: Tkinter.BaseWidget or Tkinter.Tk + :param parent: The parent ``Tkinter`` widget. If no parent is + specified, then ``CanvasFrame`` will create a new main + window. + :param kw: Keyword arguments for the new ``Canvas``. See the + documentation for ``Tkinter.Canvas`` for more information. + """ + # If no parent was given, set up a top-level window. + if parent is None: + self._parent = Tk() + self._parent.title("NLTK") + self._parent.bind("", lambda e: self.print_to_file()) + self._parent.bind("", self.destroy) + self._parent.bind("", self.destroy) + else: + self._parent = parent + + # Create a frame for the canvas & scrollbars + self._frame = frame = Frame(self._parent) + self._canvas = canvas = Canvas(frame, **kw) + xscrollbar = Scrollbar(self._frame, orient="horizontal") + yscrollbar = Scrollbar(self._frame, orient="vertical") + xscrollbar["command"] = canvas.xview + yscrollbar["command"] = canvas.yview + canvas["xscrollcommand"] = xscrollbar.set + canvas["yscrollcommand"] = yscrollbar.set + yscrollbar.pack(fill="y", side="right") + xscrollbar.pack(fill="x", side="bottom") + canvas.pack(expand=1, fill="both", side="left") + + # Set initial scroll region. + scrollregion = "0 0 {} {}".format(canvas["width"], canvas["height"]) + canvas["scrollregion"] = scrollregion + + self._scrollwatcher = ScrollWatcherWidget(canvas) + + # If no parent was given, pack the frame, and add a menu. + if parent is None: + self.pack(expand=1, fill="both") + self._init_menubar() + + def _init_menubar(self): + menubar = Menu(self._parent) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Print to Postscript", + underline=0, + command=self.print_to_file, + accelerator="Ctrl-p", + ) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="Ctrl-x" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + self._parent.config(menu=menubar) + + def print_to_file(self, filename=None): + """ + Print the contents of this ``CanvasFrame`` to a postscript + file. If no filename is given, then prompt the user for one. + + :param filename: The name of the file to print the tree to. + :type filename: str + :rtype: None + """ + if filename is None: + ftypes = [("Postscript files", ".ps"), ("All files", "*")] + filename = asksaveasfilename(filetypes=ftypes, defaultextension=".ps") + if not filename: + return + (x0, y0, w, h) = self.scrollregion() + postscript = self._canvas.postscript( + x=x0, + y=y0, + width=w + 2, + height=h + 2, + pagewidth=w + 2, # points = 1/72 inch + pageheight=h + 2, # points = 1/72 inch + pagex=0, + pagey=0, + ) + # workaround for bug in Tk font handling + postscript = postscript.replace(" 0 scalefont ", " 9 scalefont ") + with open(filename, "wb") as f: + f.write(postscript.encode("utf8")) + + def scrollregion(self): + """ + :return: The current scroll region for the canvas managed by + this ``CanvasFrame``. + :rtype: 4-tuple of int + """ + (x1, y1, x2, y2) = self._canvas["scrollregion"].split() + return (int(x1), int(y1), int(x2), int(y2)) + + def canvas(self): + """ + :return: The canvas managed by this ``CanvasFrame``. + :rtype: Tkinter.Canvas + """ + return self._canvas + + def add_widget(self, canvaswidget, x=None, y=None): + """ + Register a canvas widget with this ``CanvasFrame``. The + ``CanvasFrame`` will ensure that this canvas widget is always + within the ``Canvas``'s scrollregion. If no coordinates are + given for the canvas widget, then the ``CanvasFrame`` will + attempt to find a clear area of the canvas for it. + + :type canvaswidget: CanvasWidget + :param canvaswidget: The new canvas widget. ``canvaswidget`` + must have been created on this ``CanvasFrame``'s canvas. + :type x: int + :param x: The initial x coordinate for the upper left hand + corner of ``canvaswidget``, in the canvas's coordinate + space. + :type y: int + :param y: The initial y coordinate for the upper left hand + corner of ``canvaswidget``, in the canvas's coordinate + space. + """ + if x is None or y is None: + (x, y) = self._find_room(canvaswidget, x, y) + + # Move to (x,y) + (x1, y1, x2, y2) = canvaswidget.bbox() + canvaswidget.move(x - x1, y - y1) + + # Register with scrollwatcher. + self._scrollwatcher.add_child(canvaswidget) + + def _find_room(self, widget, desired_x, desired_y): + """ + Try to find a space for a given widget. + """ + (left, top, right, bot) = self.scrollregion() + w = widget.width() + h = widget.height() + + if w >= (right - left): + return (0, 0) + if h >= (bot - top): + return (0, 0) + + # Move the widget out of the way, for now. + (x1, y1, x2, y2) = widget.bbox() + widget.move(left - x2 - 50, top - y2 - 50) + + if desired_x is not None: + x = desired_x + for y in range(top, bot - h, int((bot - top - h) / 10)): + if not self._canvas.find_overlapping( + x - 5, y - 5, x + w + 5, y + h + 5 + ): + return (x, y) + + if desired_y is not None: + y = desired_y + for x in range(left, right - w, int((right - left - w) / 10)): + if not self._canvas.find_overlapping( + x - 5, y - 5, x + w + 5, y + h + 5 + ): + return (x, y) + + for y in range(top, bot - h, int((bot - top - h) / 10)): + for x in range(left, right - w, int((right - left - w) / 10)): + if not self._canvas.find_overlapping( + x - 5, y - 5, x + w + 5, y + h + 5 + ): + return (x, y) + return (0, 0) + + def destroy_widget(self, canvaswidget): + """ + Remove a canvas widget from this ``CanvasFrame``. This + deregisters the canvas widget, and destroys it. + """ + self.remove_widget(canvaswidget) + canvaswidget.destroy() + + def remove_widget(self, canvaswidget): + # Deregister with scrollwatcher. + self._scrollwatcher.remove_child(canvaswidget) + + def pack(self, cnf={}, **kw): + """ + Pack this ``CanvasFrame``. See the documentation for + ``Tkinter.Pack`` for more information. + """ + self._frame.pack(cnf, **kw) + # Adjust to be big enough for kids? + + def destroy(self, *e): + """ + Destroy this ``CanvasFrame``. If this ``CanvasFrame`` created a + top-level window, then this will close that window. + """ + if self._parent is None: + return + self._parent.destroy() + self._parent = None + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this frame is created from a non-interactive program (e.g. + from a secript); otherwise, the frame will close as soon as + the script completes. + """ + if in_idle(): + return + self._parent.mainloop(*args, **kwargs) + + +##////////////////////////////////////////////////////// +## Text display +##////////////////////////////////////////////////////// + + +class ShowText: + """ + A ``Tkinter`` window used to display a text. ``ShowText`` is + typically used by graphical tools to display help text, or similar + information. + """ + + def __init__(self, root, title, text, width=None, height=None, **textbox_options): + if width is None or height is None: + (width, height) = self.find_dimentions(text, width, height) + + # Create the main window. + if root is None: + self._top = top = Tk() + else: + self._top = top = Toplevel(root) + top.title(title) + + b = Button(top, text="Ok", command=self.destroy) + b.pack(side="bottom") + + tbf = Frame(top) + tbf.pack(expand=1, fill="both") + scrollbar = Scrollbar(tbf, orient="vertical") + scrollbar.pack(side="right", fill="y") + textbox = Text(tbf, wrap="word", width=width, height=height, **textbox_options) + textbox.insert("end", text) + textbox["state"] = "disabled" + textbox.pack(side="left", expand=1, fill="both") + scrollbar["command"] = textbox.yview + textbox["yscrollcommand"] = scrollbar.set + + # Make it easy to close the window. + top.bind("q", self.destroy) + top.bind("x", self.destroy) + top.bind("c", self.destroy) + top.bind("", self.destroy) + top.bind("", self.destroy) + + # Focus the scrollbar, so they can use up/down, etc. + scrollbar.focus() + + def find_dimentions(self, text, width, height): + lines = text.split("\n") + if width is None: + maxwidth = max(len(line) for line in lines) + width = min(maxwidth, 80) + + # Now, find height. + height = 0 + for line in lines: + while len(line) > width: + brk = line[:width].rfind(" ") + line = line[brk:] + height += 1 + height += 1 + height = min(height, 25) + + return (width, height) + + def destroy(self, *e): + if self._top is None: + return + self._top.destroy() + self._top = None + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this window is created from a non-interactive program (e.g. + from a secript); otherwise, the window will close as soon as + the script completes. + """ + if in_idle(): + return + self._top.mainloop(*args, **kwargs) + + +##////////////////////////////////////////////////////// +## Entry dialog +##////////////////////////////////////////////////////// + + +class EntryDialog: + """ + A dialog box for entering + """ + + def __init__( + self, parent, original_text="", instructions="", set_callback=None, title=None + ): + self._parent = parent + self._original_text = original_text + self._set_callback = set_callback + + width = int(max(30, len(original_text) * 3 / 2)) + self._top = Toplevel(parent) + + if title: + self._top.title(title) + + # The text entry box. + entryframe = Frame(self._top) + entryframe.pack(expand=1, fill="both", padx=5, pady=5, ipady=10) + if instructions: + l = Label(entryframe, text=instructions) + l.pack(side="top", anchor="w", padx=30) + self._entry = Entry(entryframe, width=width) + self._entry.pack(expand=1, fill="x", padx=30) + self._entry.insert(0, original_text) + + # A divider + divider = Frame(self._top, borderwidth=1, relief="sunken") + divider.pack(fill="x", ipady=1, padx=10) + + # The buttons. + buttons = Frame(self._top) + buttons.pack(expand=0, fill="x", padx=5, pady=5) + b = Button(buttons, text="Cancel", command=self._cancel, width=8) + b.pack(side="right", padx=5) + b = Button(buttons, text="Ok", command=self._ok, width=8, default="active") + b.pack(side="left", padx=5) + b = Button(buttons, text="Apply", command=self._apply, width=8) + b.pack(side="left") + + self._top.bind("", self._ok) + self._top.bind("", self._cancel) + self._top.bind("", self._cancel) + + self._entry.focus() + + def _reset(self, *e): + self._entry.delete(0, "end") + self._entry.insert(0, self._original_text) + if self._set_callback: + self._set_callback(self._original_text) + + def _cancel(self, *e): + try: + self._reset() + except: + pass + self._destroy() + + def _ok(self, *e): + self._apply() + self._destroy() + + def _apply(self, *e): + if self._set_callback: + self._set_callback(self._entry.get()) + + def _destroy(self, *e): + if self._top is None: + return + self._top.destroy() + self._top = None + + +##////////////////////////////////////////////////////// +## Colorized List +##////////////////////////////////////////////////////// + + +class ColorizedList: + """ + An abstract base class for displaying a colorized list of items. + Subclasses should define: + + - ``_init_colortags``, which sets up Text color tags that + will be used by the list. + - ``_item_repr``, which returns a list of (text,colortag) + tuples that make up the colorized representation of the + item. + + :note: Typically, you will want to register a callback for + ``'select'`` that calls ``mark`` on the given item. + """ + + def __init__(self, parent, items=[], **options): + """ + Construct a new list. + + :param parent: The Tk widget that contains the colorized list + :param items: The initial contents of the colorized list. + :param options: + """ + self._parent = parent + self._callbacks = {} + + # Which items are marked? + self._marks = {} + + # Initialize the Tkinter frames. + self._init_itemframe(options.copy()) + + # Set up key & mouse bindings. + self._textwidget.bind("", self._keypress) + self._textwidget.bind("", self._buttonpress) + + # Fill in the given CFG's items. + self._items = None + self.set(items) + + # //////////////////////////////////////////////////////////// + # Abstract methods + # //////////////////////////////////////////////////////////// + @abstractmethod + def _init_colortags(self, textwidget, options): + """ + Set up any colortags that will be used by this colorized list. + E.g.: + textwidget.tag_config('terminal', foreground='black') + """ + + @abstractmethod + def _item_repr(self, item): + """ + Return a list of (text, colortag) tuples that make up the + colorized representation of the item. Colorized + representations may not span multiple lines. I.e., the text + strings returned may not contain newline characters. + """ + + # //////////////////////////////////////////////////////////// + # Item Access + # //////////////////////////////////////////////////////////// + + def get(self, index=None): + """ + :return: A list of the items contained by this list. + """ + if index is None: + return self._items[:] + else: + return self._items[index] + + def set(self, items): + """ + Modify the list of items contained by this list. + """ + items = list(items) + if self._items == items: + return + self._items = list(items) + + self._textwidget["state"] = "normal" + self._textwidget.delete("1.0", "end") + for item in items: + for (text, colortag) in self._item_repr(item): + assert "\n" not in text, "item repr may not contain newline" + self._textwidget.insert("end", text, colortag) + self._textwidget.insert("end", "\n") + # Remove the final newline + self._textwidget.delete("end-1char", "end") + self._textwidget.mark_set("insert", "1.0") + self._textwidget["state"] = "disabled" + # Clear all marks + self._marks.clear() + + def unmark(self, item=None): + """ + Remove highlighting from the given item; or from every item, + if no item is given. + :raise ValueError: If ``item`` is not contained in the list. + :raise KeyError: If ``item`` is not marked. + """ + if item is None: + self._marks.clear() + self._textwidget.tag_remove("highlight", "1.0", "end+1char") + else: + index = self._items.index(item) + del self._marks[item] + (start, end) = ("%d.0" % (index + 1), "%d.0" % (index + 2)) + self._textwidget.tag_remove("highlight", start, end) + + def mark(self, item): + """ + Highlight the given item. + :raise ValueError: If ``item`` is not contained in the list. + """ + self._marks[item] = 1 + index = self._items.index(item) + (start, end) = ("%d.0" % (index + 1), "%d.0" % (index + 2)) + self._textwidget.tag_add("highlight", start, end) + + def markonly(self, item): + """ + Remove any current highlighting, and mark the given item. + :raise ValueError: If ``item`` is not contained in the list. + """ + self.unmark() + self.mark(item) + + def view(self, item): + """ + Adjust the view such that the given item is visible. If + the item is already visible, then do nothing. + """ + index = self._items.index(item) + self._textwidget.see("%d.0" % (index + 1)) + + # //////////////////////////////////////////////////////////// + # Callbacks + # //////////////////////////////////////////////////////////// + + def add_callback(self, event, func): + """ + Register a callback function with the list. This function + will be called whenever the given event occurs. + + :param event: The event that will trigger the callback + function. Valid events are: click1, click2, click3, + space, return, select, up, down, next, prior, move + :param func: The function that should be called when + the event occurs. ``func`` will be called with a + single item as its argument. (The item selected + or the item moved to). + """ + if event == "select": + events = ["click1", "space", "return"] + elif event == "move": + events = ["up", "down", "next", "prior"] + else: + events = [event] + + for e in events: + self._callbacks.setdefault(e, {})[func] = 1 + + def remove_callback(self, event, func=None): + """ + Deregister a callback function. If ``func`` is none, then + all callbacks are removed for the given event. + """ + if event is None: + events = list(self._callbacks.keys()) + elif event == "select": + events = ["click1", "space", "return"] + elif event == "move": + events = ["up", "down", "next", "prior"] + else: + events = [event] + + for e in events: + if func is None: + del self._callbacks[e] + else: + try: + del self._callbacks[e][func] + except: + pass + + # //////////////////////////////////////////////////////////// + # Tkinter Methods + # //////////////////////////////////////////////////////////// + + def pack(self, cnf={}, **kw): + # "@include: Tkinter.Pack.pack" + self._itemframe.pack(cnf, **kw) + + def grid(self, cnf={}, **kw): + # "@include: Tkinter.Grid.grid" + self._itemframe.grid(cnf, *kw) + + def focus(self): + # "@include: Tkinter.Widget.focus" + self._textwidget.focus() + + # //////////////////////////////////////////////////////////// + # Internal Methods + # //////////////////////////////////////////////////////////// + + def _init_itemframe(self, options): + self._itemframe = Frame(self._parent) + + # Create the basic Text widget & scrollbar. + options.setdefault("background", "#e0e0e0") + self._textwidget = Text(self._itemframe, **options) + self._textscroll = Scrollbar(self._itemframe, takefocus=0, orient="vertical") + self._textwidget.config(yscrollcommand=self._textscroll.set) + self._textscroll.config(command=self._textwidget.yview) + self._textscroll.pack(side="right", fill="y") + self._textwidget.pack(expand=1, fill="both", side="left") + + # Initialize the colorization tags + self._textwidget.tag_config( + "highlight", background="#e0ffff", border="1", relief="raised" + ) + self._init_colortags(self._textwidget, options) + + # How do I want to mark keyboard selection? + self._textwidget.tag_config("sel", foreground="") + self._textwidget.tag_config( + "sel", foreground="", background="", border="", underline=1 + ) + self._textwidget.tag_lower("highlight", "sel") + + def _fire_callback(self, event, itemnum): + if event not in self._callbacks: + return + if 0 <= itemnum < len(self._items): + item = self._items[itemnum] + else: + item = None + for cb_func in list(self._callbacks[event].keys()): + cb_func(item) + + def _buttonpress(self, event): + clickloc = "@%d,%d" % (event.x, event.y) + insert_point = self._textwidget.index(clickloc) + itemnum = int(insert_point.split(".")[0]) - 1 + self._fire_callback("click%d" % event.num, itemnum) + + def _keypress(self, event): + if event.keysym == "Return" or event.keysym == "space": + insert_point = self._textwidget.index("insert") + itemnum = int(insert_point.split(".")[0]) - 1 + self._fire_callback(event.keysym.lower(), itemnum) + return + elif event.keysym == "Down": + delta = "+1line" + elif event.keysym == "Up": + delta = "-1line" + elif event.keysym == "Next": + delta = "+10lines" + elif event.keysym == "Prior": + delta = "-10lines" + else: + return "continue" + + self._textwidget.mark_set("insert", "insert" + delta) + self._textwidget.see("insert") + self._textwidget.tag_remove("sel", "1.0", "end+1char") + self._textwidget.tag_add("sel", "insert linestart", "insert lineend") + + insert_point = self._textwidget.index("insert") + itemnum = int(insert_point.split(".")[0]) - 1 + self._fire_callback(event.keysym.lower(), itemnum) + + return "break" + + +##////////////////////////////////////////////////////// +## Improved OptionMenu +##////////////////////////////////////////////////////// + + +class MutableOptionMenu(Menubutton): + def __init__(self, master, values, **options): + self._callback = options.get("command") + if "command" in options: + del options["command"] + + # Create a variable + self._variable = variable = StringVar() + if len(values) > 0: + variable.set(values[0]) + + kw = { + "borderwidth": 2, + "textvariable": variable, + "indicatoron": 1, + "relief": RAISED, + "anchor": "c", + "highlightthickness": 2, + } + kw.update(options) + Widget.__init__(self, master, "menubutton", kw) + self.widgetName = "tk_optionMenu" + self._menu = Menu(self, name="menu", tearoff=0) + self.menuname = self._menu._w + + self._values = [] + for value in values: + self.add(value) + + self["menu"] = self._menu + + def add(self, value): + if value in self._values: + return + + def set(value=value): + self.set(value) + + self._menu.add_command(label=value, command=set) + self._values.append(value) + + def set(self, value): + self._variable.set(value) + if self._callback: + self._callback(value) + + def remove(self, value): + # Might raise indexerror: pass to parent. + i = self._values.index(value) + del self._values[i] + self._menu.delete(i, i) + + def __getitem__(self, name): + if name == "menu": + return self.__menu + return Widget.__getitem__(self, name) + + def destroy(self): + """Destroy this widget and the associated menu.""" + Menubutton.destroy(self) + self._menu = None + + +##////////////////////////////////////////////////////// +## Test code. +##////////////////////////////////////////////////////// + + +def demo(): + """ + A simple demonstration showing how to use canvas widgets. + """ + + def fill(cw): + from random import randint + + cw["fill"] = "#00%04d" % randint(0, 9999) + + def color(cw): + from random import randint + + cw["color"] = "#ff%04d" % randint(0, 9999) + + cf = CanvasFrame(closeenough=10, width=300, height=300) + c = cf.canvas() + ct3 = TextWidget(c, "hiya there", draggable=1) + ct2 = TextWidget(c, "o o\n||\n___\n U", draggable=1, justify="center") + co = OvalWidget(c, ct2, outline="red") + ct = TextWidget(c, "o o\n||\n\\___/", draggable=1, justify="center") + cp = ParenWidget(c, ct, color="red") + cb = BoxWidget(c, cp, fill="cyan", draggable=1, width=3, margin=10) + equation = SequenceWidget( + c, + SymbolWidget(c, "forall"), + TextWidget(c, "x"), + SymbolWidget(c, "exists"), + TextWidget(c, "y: "), + TextWidget(c, "x"), + SymbolWidget(c, "notequal"), + TextWidget(c, "y"), + ) + space = SpaceWidget(c, 0, 30) + cstack = StackWidget(c, cb, ct3, space, co, equation, align="center") + prompt_msg = TextWidget( + c, "try clicking\nand dragging", draggable=1, justify="center" + ) + cs = SequenceWidget(c, cstack, prompt_msg) + zz = BracketWidget(c, cs, color="green4", width=3) + cf.add_widget(zz, 60, 30) + + cb.bind_click(fill) + ct.bind_click(color) + co.bind_click(fill) + ct2.bind_click(color) + ct3.bind_click(color) + + cf.mainloop() + # ShowText(None, 'title', ((('this is text'*150)+'\n')*5)) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/featstruct.py b/.pythonlibs/lib/python3.10/site-packages/nltk/featstruct.py new file mode 100644 index 0000000..ca01ac0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/featstruct.py @@ -0,0 +1,2779 @@ +# Natural Language Toolkit: Feature Structures +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper , +# Rob Speer, +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Basic data classes for representing feature structures, and for +performing basic operations on those feature structures. A feature +structure is a mapping from feature identifiers to feature values, +where each feature value is either a basic value (such as a string or +an integer), or a nested feature structure. There are two types of +feature structure, implemented by two subclasses of ``FeatStruct``: + + - feature dictionaries, implemented by ``FeatDict``, act like + Python dictionaries. Feature identifiers may be strings or + instances of the ``Feature`` class. + - feature lists, implemented by ``FeatList``, act like Python + lists. Feature identifiers are integers. + +Feature structures are typically used to represent partial information +about objects. A feature identifier that is not mapped to a value +stands for a feature whose value is unknown (*not* a feature without +a value). Two feature structures that represent (potentially +overlapping) information about the same object can be combined by +unification. When two inconsistent feature structures are unified, +the unification fails and returns None. + +Features can be specified using "feature paths", or tuples of feature +identifiers that specify path through the nested feature structures to +a value. Feature structures may contain reentrant feature values. A +"reentrant feature value" is a single feature value that can be +accessed via multiple feature paths. Unification preserves the +reentrance relations imposed by both of the unified feature +structures. In the feature structure resulting from unification, any +modifications to a reentrant feature value will be visible using any +of its feature paths. + +Feature structure variables are encoded using the ``nltk.sem.Variable`` +class. The variables' values are tracked using a bindings +dictionary, which maps variables to their values. When two feature +structures are unified, a fresh bindings dictionary is created to +track their values; and before unification completes, all bound +variables are replaced by their values. Thus, the bindings +dictionaries are usually strictly internal to the unification process. +However, it is possible to track the bindings of variables if you +choose to, by supplying your own initial bindings dictionary to the +``unify()`` function. + +When unbound variables are unified with one another, they become +aliased. This is encoded by binding one variable to the other. + +Lightweight Feature Structures +============================== +Many of the functions defined by ``nltk.featstruct`` can be applied +directly to simple Python dictionaries and lists, rather than to +full-fledged ``FeatDict`` and ``FeatList`` objects. In other words, +Python ``dicts`` and ``lists`` can be used as "light-weight" feature +structures. + + >>> from nltk.featstruct import unify + >>> unify(dict(x=1, y=dict()), dict(a='a', y=dict(b='b'))) # doctest: +SKIP + {'y': {'b': 'b'}, 'x': 1, 'a': 'a'} + +However, you should keep in mind the following caveats: + + - Python dictionaries & lists ignore reentrance when checking for + equality between values. But two FeatStructs with different + reentrances are considered nonequal, even if all their base + values are equal. + + - FeatStructs can be easily frozen, allowing them to be used as + keys in hash tables. Python dictionaries and lists can not. + + - FeatStructs display reentrance in their string representations; + Python dictionaries and lists do not. + + - FeatStructs may *not* be mixed with Python dictionaries and lists + (e.g., when performing unification). + + - FeatStructs provide a number of useful methods, such as ``walk()`` + and ``cyclic()``, which are not available for Python dicts and lists. + +In general, if your feature structures will contain any reentrances, +or if you plan to use them as dictionary keys, it is strongly +recommended that you use full-fledged ``FeatStruct`` objects. +""" + +import copy +import re +from functools import total_ordering + +from nltk.internals import raise_unorderable_types, read_str +from nltk.sem.logic import ( + Expression, + LogicalExpressionException, + LogicParser, + SubstituteBindingsI, + Variable, +) + +###################################################################### +# Feature Structure +###################################################################### + + +@total_ordering +class FeatStruct(SubstituteBindingsI): + """ + A mapping from feature identifiers to feature values, where each + feature value is either a basic value (such as a string or an + integer), or a nested feature structure. There are two types of + feature structure: + + - feature dictionaries, implemented by ``FeatDict``, act like + Python dictionaries. Feature identifiers may be strings or + instances of the ``Feature`` class. + - feature lists, implemented by ``FeatList``, act like Python + lists. Feature identifiers are integers. + + Feature structures may be indexed using either simple feature + identifiers or 'feature paths.' A feature path is a sequence + of feature identifiers that stand for a corresponding sequence of + indexing operations. In particular, ``fstruct[(f1,f2,...,fn)]`` is + equivalent to ``fstruct[f1][f2]...[fn]``. + + Feature structures may contain reentrant feature structures. A + "reentrant feature structure" is a single feature structure + object that can be accessed via multiple feature paths. Feature + structures may also be cyclic. A feature structure is "cyclic" + if there is any feature path from the feature structure to itself. + + Two feature structures are considered equal if they assign the + same values to all features, and have the same reentrancies. + + By default, feature structures are mutable. They may be made + immutable with the ``freeze()`` method. Once they have been + frozen, they may be hashed, and thus used as dictionary keys. + """ + + _frozen = False + """:ivar: A flag indicating whether this feature structure is + frozen or not. Once this flag is set, it should never be + un-set; and no further modification should be made to this + feature structure.""" + + ##//////////////////////////////////////////////////////////// + # { Constructor + ##//////////////////////////////////////////////////////////// + + def __new__(cls, features=None, **morefeatures): + """ + Construct and return a new feature structure. If this + constructor is called directly, then the returned feature + structure will be an instance of either the ``FeatDict`` class + or the ``FeatList`` class. + + :param features: The initial feature values for this feature + structure: + + - FeatStruct(string) -> FeatStructReader().read(string) + - FeatStruct(mapping) -> FeatDict(mapping) + - FeatStruct(sequence) -> FeatList(sequence) + - FeatStruct() -> FeatDict() + :param morefeatures: If ``features`` is a mapping or None, + then ``morefeatures`` provides additional features for the + ``FeatDict`` constructor. + """ + # If the FeatStruct constructor is called directly, then decide + # whether to create a FeatDict or a FeatList, based on the + # contents of the `features` argument. + if cls is FeatStruct: + if features is None: + return FeatDict.__new__(FeatDict, **morefeatures) + elif _is_mapping(features): + return FeatDict.__new__(FeatDict, features, **morefeatures) + elif morefeatures: + raise TypeError( + "Keyword arguments may only be specified " + "if features is None or is a mapping." + ) + if isinstance(features, str): + if FeatStructReader._START_FDICT_RE.match(features): + return FeatDict.__new__(FeatDict, features, **morefeatures) + else: + return FeatList.__new__(FeatList, features, **morefeatures) + elif _is_sequence(features): + return FeatList.__new__(FeatList, features) + else: + raise TypeError("Expected string or mapping or sequence") + + # Otherwise, construct the object as normal. + else: + return super().__new__(cls, features, **morefeatures) + + ##//////////////////////////////////////////////////////////// + # { Uniform Accessor Methods + ##//////////////////////////////////////////////////////////// + # These helper functions allow the methods defined by FeatStruct + # to treat all feature structures as mappings, even if they're + # really lists. (Lists are treated as mappings from ints to vals) + + def _keys(self): + """Return an iterable of the feature identifiers used by this + FeatStruct.""" + raise NotImplementedError() # Implemented by subclasses. + + def _values(self): + """Return an iterable of the feature values directly defined + by this FeatStruct.""" + raise NotImplementedError() # Implemented by subclasses. + + def _items(self): + """Return an iterable of (fid,fval) pairs, where fid is a + feature identifier and fval is the corresponding feature + value, for all features defined by this FeatStruct.""" + raise NotImplementedError() # Implemented by subclasses. + + ##//////////////////////////////////////////////////////////// + # { Equality & Hashing + ##//////////////////////////////////////////////////////////// + + def equal_values(self, other, check_reentrance=False): + """ + Return True if ``self`` and ``other`` assign the same value to + to every feature. In particular, return true if + ``self[p]==other[p]`` for every feature path *p* such + that ``self[p]`` or ``other[p]`` is a base value (i.e., + not a nested feature structure). + + :param check_reentrance: If True, then also return False if + there is any difference between the reentrances of ``self`` + and ``other``. + :note: the ``==`` is equivalent to ``equal_values()`` with + ``check_reentrance=True``. + """ + return self._equal(other, check_reentrance, set(), set(), set()) + + def __eq__(self, other): + """ + Return true if ``self`` and ``other`` are both feature structures, + assign the same values to all features, and contain the same + reentrances. I.e., return + ``self.equal_values(other, check_reentrance=True)``. + + :see: ``equal_values()`` + """ + return self._equal(other, True, set(), set(), set()) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, FeatStruct): + # raise_unorderable_types("<", self, other) + # Sometimes feature values can be pure strings, + # so we need to be able to compare with non-featstructs: + return self.__class__.__name__ < other.__class__.__name__ + else: + return len(self) < len(other) + + def __hash__(self): + """ + If this feature structure is frozen, return its hash value; + otherwise, raise ``TypeError``. + """ + if not self._frozen: + raise TypeError("FeatStructs must be frozen before they " "can be hashed.") + try: + return self._hash + except AttributeError: + self._hash = self._calculate_hashvalue(set()) + return self._hash + + def _equal( + self, other, check_reentrance, visited_self, visited_other, visited_pairs + ): + """ + Return True iff self and other have equal values. + + :param visited_self: A set containing the ids of all ``self`` + feature structures we've already visited. + :param visited_other: A set containing the ids of all ``other`` + feature structures we've already visited. + :param visited_pairs: A set containing ``(selfid, otherid)`` pairs + for all pairs of feature structures we've already visited. + """ + # If we're the same object, then we're equal. + if self is other: + return True + + # If we have different classes, we're definitely not equal. + if self.__class__ != other.__class__: + return False + + # If we define different features, we're definitely not equal. + # (Perform len test first because it's faster -- we should + # do profiling to see if this actually helps) + if len(self) != len(other): + return False + if set(self._keys()) != set(other._keys()): + return False + + # If we're checking reentrance, then any time we revisit a + # structure, make sure that it was paired with the same + # feature structure that it is now. Note: if check_reentrance, + # then visited_pairs will never contain two pairs whose first + # values are equal, or two pairs whose second values are equal. + if check_reentrance: + if id(self) in visited_self or id(other) in visited_other: + return (id(self), id(other)) in visited_pairs + + # If we're not checking reentrance, then we still need to deal + # with cycles. If we encounter the same (self, other) pair a + # second time, then we won't learn anything more by examining + # their children a second time, so just return true. + else: + if (id(self), id(other)) in visited_pairs: + return True + + # Keep track of which nodes we've visited. + visited_self.add(id(self)) + visited_other.add(id(other)) + visited_pairs.add((id(self), id(other))) + + # Now we have to check all values. If any of them don't match, + # then return false. + for (fname, self_fval) in self._items(): + other_fval = other[fname] + if isinstance(self_fval, FeatStruct): + if not self_fval._equal( + other_fval, + check_reentrance, + visited_self, + visited_other, + visited_pairs, + ): + return False + else: + if self_fval != other_fval: + return False + + # Everything matched up; return true. + return True + + def _calculate_hashvalue(self, visited): + """ + Return a hash value for this feature structure. + + :require: ``self`` must be frozen. + :param visited: A set containing the ids of all feature + structures we've already visited while hashing. + """ + if id(self) in visited: + return 1 + visited.add(id(self)) + + hashval = 5831 + for (fname, fval) in sorted(self._items()): + hashval *= 37 + hashval += hash(fname) + hashval *= 37 + if isinstance(fval, FeatStruct): + hashval += fval._calculate_hashvalue(visited) + else: + hashval += hash(fval) + # Convert to a 32 bit int. + hashval = int(hashval & 0x7FFFFFFF) + return hashval + + ##//////////////////////////////////////////////////////////// + # { Freezing + ##//////////////////////////////////////////////////////////// + + #: Error message used by mutating methods when called on a frozen + #: feature structure. + _FROZEN_ERROR = "Frozen FeatStructs may not be modified." + + def freeze(self): + """ + Make this feature structure, and any feature structures it + contains, immutable. Note: this method does not attempt to + 'freeze' any feature value that is not a ``FeatStruct``; it + is recommended that you use only immutable feature values. + """ + if self._frozen: + return + self._freeze(set()) + + def frozen(self): + """ + Return True if this feature structure is immutable. Feature + structures can be made immutable with the ``freeze()`` method. + Immutable feature structures may not be made mutable again, + but new mutable copies can be produced with the ``copy()`` method. + """ + return self._frozen + + def _freeze(self, visited): + """ + Make this feature structure, and any feature structure it + contains, immutable. + + :param visited: A set containing the ids of all feature + structures we've already visited while freezing. + """ + if id(self) in visited: + return + visited.add(id(self)) + self._frozen = True + for (fname, fval) in sorted(self._items()): + if isinstance(fval, FeatStruct): + fval._freeze(visited) + + ##//////////////////////////////////////////////////////////// + # { Copying + ##//////////////////////////////////////////////////////////// + + def copy(self, deep=True): + """ + Return a new copy of ``self``. The new copy will not be frozen. + + :param deep: If true, create a deep copy; if false, create + a shallow copy. + """ + if deep: + return copy.deepcopy(self) + else: + return self.__class__(self) + + # Subclasses should define __deepcopy__ to ensure that the new + # copy will not be frozen. + def __deepcopy__(self, memo): + raise NotImplementedError() # Implemented by subclasses. + + ##//////////////////////////////////////////////////////////// + # { Structural Information + ##//////////////////////////////////////////////////////////// + + def cyclic(self): + """ + Return True if this feature structure contains itself. + """ + return self._find_reentrances({})[id(self)] + + def walk(self): + """ + Return an iterator that generates this feature structure, and + each feature structure it contains. Each feature structure will + be generated exactly once. + """ + return self._walk(set()) + + def _walk(self, visited): + """ + Return an iterator that generates this feature structure, and + each feature structure it contains. + + :param visited: A set containing the ids of all feature + structures we've already visited while freezing. + """ + raise NotImplementedError() # Implemented by subclasses. + + def _walk(self, visited): + if id(self) in visited: + return + visited.add(id(self)) + yield self + for fval in self._values(): + if isinstance(fval, FeatStruct): + yield from fval._walk(visited) + + # Walk through the feature tree. The first time we see a feature + # value, map it to False (not reentrant). If we see a feature + # value more than once, then map it to True (reentrant). + def _find_reentrances(self, reentrances): + """ + Return a dictionary that maps from the ``id`` of each feature + structure contained in ``self`` (including ``self``) to a + boolean value, indicating whether it is reentrant or not. + """ + if id(self) in reentrances: + # We've seen it more than once. + reentrances[id(self)] = True + else: + # This is the first time we've seen it. + reentrances[id(self)] = False + + # Recurse to contained feature structures. + for fval in self._values(): + if isinstance(fval, FeatStruct): + fval._find_reentrances(reentrances) + + return reentrances + + ##//////////////////////////////////////////////////////////// + # { Variables & Bindings + ##//////////////////////////////////////////////////////////// + + def substitute_bindings(self, bindings): + """:see: ``nltk.featstruct.substitute_bindings()``""" + return substitute_bindings(self, bindings) + + def retract_bindings(self, bindings): + """:see: ``nltk.featstruct.retract_bindings()``""" + return retract_bindings(self, bindings) + + def variables(self): + """:see: ``nltk.featstruct.find_variables()``""" + return find_variables(self) + + def rename_variables(self, vars=None, used_vars=(), new_vars=None): + """:see: ``nltk.featstruct.rename_variables()``""" + return rename_variables(self, vars, used_vars, new_vars) + + def remove_variables(self): + """ + Return the feature structure that is obtained by deleting + any feature whose value is a ``Variable``. + + :rtype: FeatStruct + """ + return remove_variables(self) + + ##//////////////////////////////////////////////////////////// + # { Unification + ##//////////////////////////////////////////////////////////// + + def unify(self, other, bindings=None, trace=False, fail=None, rename_vars=True): + return unify(self, other, bindings, trace, fail, rename_vars) + + def subsumes(self, other): + """ + Return True if ``self`` subsumes ``other``. I.e., return true + If unifying ``self`` with ``other`` would result in a feature + structure equal to ``other``. + """ + return subsumes(self, other) + + ##//////////////////////////////////////////////////////////// + # { String Representations + ##//////////////////////////////////////////////////////////// + + def __repr__(self): + """ + Display a single-line representation of this feature structure, + suitable for embedding in other representations. + """ + return self._repr(self._find_reentrances({}), {}) + + def _repr(self, reentrances, reentrance_ids): + """ + Return a string representation of this feature structure. + + :param reentrances: A dictionary that maps from the ``id`` of + each feature value in self, indicating whether that value + is reentrant or not. + :param reentrance_ids: A dictionary mapping from each ``id`` + of a feature value to a unique identifier. This is modified + by ``repr``: the first time a reentrant feature value is + displayed, an identifier is added to ``reentrance_ids`` for it. + """ + raise NotImplementedError() + + +# Mutation: disable if frozen. +_FROZEN_ERROR = "Frozen FeatStructs may not be modified." +_FROZEN_NOTICE = "\n%sIf self is frozen, raise ValueError." + + +def _check_frozen(method, indent=""): + """ + Given a method function, return a new method function that first + checks if ``self._frozen`` is true; and if so, raises ``ValueError`` + with an appropriate message. Otherwise, call the method and return + its result. + """ + + def wrapped(self, *args, **kwargs): + if self._frozen: + raise ValueError(_FROZEN_ERROR) + else: + return method(self, *args, **kwargs) + + wrapped.__name__ = method.__name__ + wrapped.__doc__ = (method.__doc__ or "") + (_FROZEN_NOTICE % indent) + return wrapped + + +###################################################################### +# Feature Dictionary +###################################################################### + + +class FeatDict(FeatStruct, dict): + """ + A feature structure that acts like a Python dictionary. I.e., a + mapping from feature identifiers to feature values, where a feature + identifier can be a string or a ``Feature``; and where a feature value + can be either a basic value (such as a string or an integer), or a nested + feature structure. A feature identifiers for a ``FeatDict`` is + sometimes called a "feature name". + + Two feature dicts are considered equal if they assign the same + values to all features, and have the same reentrances. + + :see: ``FeatStruct`` for information about feature paths, reentrance, + cyclic feature structures, mutability, freezing, and hashing. + """ + + def __init__(self, features=None, **morefeatures): + """ + Create a new feature dictionary, with the specified features. + + :param features: The initial value for this feature + dictionary. If ``features`` is a ``FeatStruct``, then its + features are copied (shallow copy). If ``features`` is a + dict, then a feature is created for each item, mapping its + key to its value. If ``features`` is a string, then it is + processed using ``FeatStructReader``. If ``features`` is a list of + tuples ``(name, val)``, then a feature is created for each tuple. + :param morefeatures: Additional features for the new feature + dictionary. If a feature is listed under both ``features`` and + ``morefeatures``, then the value from ``morefeatures`` will be + used. + """ + if isinstance(features, str): + FeatStructReader().fromstring(features, self) + self.update(**morefeatures) + else: + # update() checks the types of features. + self.update(features, **morefeatures) + + # //////////////////////////////////////////////////////////// + # { Dict methods + # //////////////////////////////////////////////////////////// + _INDEX_ERROR = "Expected feature name or path. Got %r." + + def __getitem__(self, name_or_path): + """If the feature with the given name or path exists, return + its value; otherwise, raise ``KeyError``.""" + if isinstance(name_or_path, (str, Feature)): + return dict.__getitem__(self, name_or_path) + elif isinstance(name_or_path, tuple): + try: + val = self + for fid in name_or_path: + if not isinstance(val, FeatStruct): + raise KeyError # path contains base value + val = val[fid] + return val + except (KeyError, IndexError) as e: + raise KeyError(name_or_path) from e + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + def get(self, name_or_path, default=None): + """If the feature with the given name or path exists, return its + value; otherwise, return ``default``.""" + try: + return self[name_or_path] + except KeyError: + return default + + def __contains__(self, name_or_path): + """Return true if a feature with the given name or path exists.""" + try: + self[name_or_path] + return True + except KeyError: + return False + + def has_key(self, name_or_path): + """Return true if a feature with the given name or path exists.""" + return name_or_path in self + + def __delitem__(self, name_or_path): + """If the feature with the given name or path exists, delete + its value; otherwise, raise ``KeyError``.""" + if self._frozen: + raise ValueError(_FROZEN_ERROR) + if isinstance(name_or_path, (str, Feature)): + return dict.__delitem__(self, name_or_path) + elif isinstance(name_or_path, tuple): + if len(name_or_path) == 0: + raise ValueError("The path () can not be set") + else: + parent = self[name_or_path[:-1]] + if not isinstance(parent, FeatStruct): + raise KeyError(name_or_path) # path contains base value + del parent[name_or_path[-1]] + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + def __setitem__(self, name_or_path, value): + """Set the value for the feature with the given name or path + to ``value``. If ``name_or_path`` is an invalid path, raise + ``KeyError``.""" + if self._frozen: + raise ValueError(_FROZEN_ERROR) + if isinstance(name_or_path, (str, Feature)): + return dict.__setitem__(self, name_or_path, value) + elif isinstance(name_or_path, tuple): + if len(name_or_path) == 0: + raise ValueError("The path () can not be set") + else: + parent = self[name_or_path[:-1]] + if not isinstance(parent, FeatStruct): + raise KeyError(name_or_path) # path contains base value + parent[name_or_path[-1]] = value + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + clear = _check_frozen(dict.clear) + pop = _check_frozen(dict.pop) + popitem = _check_frozen(dict.popitem) + setdefault = _check_frozen(dict.setdefault) + + def update(self, features=None, **morefeatures): + if self._frozen: + raise ValueError(_FROZEN_ERROR) + if features is None: + items = () + elif hasattr(features, "items") and callable(features.items): + items = features.items() + elif hasattr(features, "__iter__"): + items = features + else: + raise ValueError("Expected mapping or list of tuples") + + for key, val in items: + if not isinstance(key, (str, Feature)): + raise TypeError("Feature names must be strings") + self[key] = val + for key, val in morefeatures.items(): + if not isinstance(key, (str, Feature)): + raise TypeError("Feature names must be strings") + self[key] = val + + ##//////////////////////////////////////////////////////////// + # { Copying + ##//////////////////////////////////////////////////////////// + + def __deepcopy__(self, memo): + memo[id(self)] = selfcopy = self.__class__() + for (key, val) in self._items(): + selfcopy[copy.deepcopy(key, memo)] = copy.deepcopy(val, memo) + return selfcopy + + ##//////////////////////////////////////////////////////////// + # { Uniform Accessor Methods + ##//////////////////////////////////////////////////////////// + + def _keys(self): + return self.keys() + + def _values(self): + return self.values() + + def _items(self): + return self.items() + + ##//////////////////////////////////////////////////////////// + # { String Representations + ##//////////////////////////////////////////////////////////// + + def __str__(self): + """ + Display a multi-line representation of this feature dictionary + as an FVM (feature value matrix). + """ + return "\n".join(self._str(self._find_reentrances({}), {})) + + def _repr(self, reentrances, reentrance_ids): + segments = [] + prefix = "" + suffix = "" + + # If this is the first time we've seen a reentrant structure, + # then assign it a unique identifier. + if reentrances[id(self)]: + assert id(self) not in reentrance_ids + reentrance_ids[id(self)] = repr(len(reentrance_ids) + 1) + + # sorting note: keys are unique strings, so we'll never fall + # through to comparing values. + for (fname, fval) in sorted(self.items()): + display = getattr(fname, "display", None) + if id(fval) in reentrance_ids: + segments.append(f"{fname}->({reentrance_ids[id(fval)]})") + elif ( + display == "prefix" and not prefix and isinstance(fval, (Variable, str)) + ): + prefix = "%s" % fval + elif display == "slash" and not suffix: + if isinstance(fval, Variable): + suffix = "/%s" % fval.name + else: + suffix = "/%s" % repr(fval) + elif isinstance(fval, Variable): + segments.append(f"{fname}={fval.name}") + elif fval is True: + segments.append("+%s" % fname) + elif fval is False: + segments.append("-%s" % fname) + elif isinstance(fval, Expression): + segments.append(f"{fname}=<{fval}>") + elif not isinstance(fval, FeatStruct): + segments.append(f"{fname}={repr(fval)}") + else: + fval_repr = fval._repr(reentrances, reentrance_ids) + segments.append(f"{fname}={fval_repr}") + # If it's reentrant, then add on an identifier tag. + if reentrances[id(self)]: + prefix = f"({reentrance_ids[id(self)]}){prefix}" + return "{}[{}]{}".format(prefix, ", ".join(segments), suffix) + + def _str(self, reentrances, reentrance_ids): + """ + :return: A list of lines composing a string representation of + this feature dictionary. + :param reentrances: A dictionary that maps from the ``id`` of + each feature value in self, indicating whether that value + is reentrant or not. + :param reentrance_ids: A dictionary mapping from each ``id`` + of a feature value to a unique identifier. This is modified + by ``repr``: the first time a reentrant feature value is + displayed, an identifier is added to ``reentrance_ids`` for + it. + """ + # If this is the first time we've seen a reentrant structure, + # then tack on an id string. + if reentrances[id(self)]: + assert id(self) not in reentrance_ids + reentrance_ids[id(self)] = repr(len(reentrance_ids) + 1) + + # Special case: empty feature dict. + if len(self) == 0: + if reentrances[id(self)]: + return ["(%s) []" % reentrance_ids[id(self)]] + else: + return ["[]"] + + # What's the longest feature name? Use this to align names. + maxfnamelen = max(len("%s" % k) for k in self.keys()) + + lines = [] + # sorting note: keys are unique strings, so we'll never fall + # through to comparing values. + for (fname, fval) in sorted(self.items()): + fname = ("%s" % fname).ljust(maxfnamelen) + if isinstance(fval, Variable): + lines.append(f"{fname} = {fval.name}") + + elif isinstance(fval, Expression): + lines.append(f"{fname} = <{fval}>") + + elif isinstance(fval, FeatList): + fval_repr = fval._repr(reentrances, reentrance_ids) + lines.append(f"{fname} = {repr(fval_repr)}") + + elif not isinstance(fval, FeatDict): + # It's not a nested feature structure -- just print it. + lines.append(f"{fname} = {repr(fval)}") + + elif id(fval) in reentrance_ids: + # It's a feature structure we've seen before -- print + # the reentrance id. + lines.append(f"{fname} -> ({reentrance_ids[id(fval)]})") + + else: + # It's a new feature structure. Separate it from + # other values by a blank line. + if lines and lines[-1] != "": + lines.append("") + + # Recursively print the feature's value (fval). + fval_lines = fval._str(reentrances, reentrance_ids) + + # Indent each line to make room for fname. + fval_lines = [(" " * (maxfnamelen + 3)) + l for l in fval_lines] + + # Pick which line we'll display fname on, & splice it in. + nameline = (len(fval_lines) - 1) // 2 + fval_lines[nameline] = ( + fname + " =" + fval_lines[nameline][maxfnamelen + 2 :] + ) + + # Add the feature structure to the output. + lines += fval_lines + + # Separate FeatStructs by a blank line. + lines.append("") + + # Get rid of any excess blank lines. + if lines[-1] == "": + lines.pop() + + # Add brackets around everything. + maxlen = max(len(line) for line in lines) + lines = ["[ {}{} ]".format(line, " " * (maxlen - len(line))) for line in lines] + + # If it's reentrant, then add on an identifier tag. + if reentrances[id(self)]: + idstr = "(%s) " % reentrance_ids[id(self)] + lines = [(" " * len(idstr)) + l for l in lines] + idline = (len(lines) - 1) // 2 + lines[idline] = idstr + lines[idline][len(idstr) :] + + return lines + + +###################################################################### +# Feature List +###################################################################### + + +class FeatList(FeatStruct, list): + """ + A list of feature values, where each feature value is either a + basic value (such as a string or an integer), or a nested feature + structure. + + Feature lists may contain reentrant feature values. A "reentrant + feature value" is a single feature value that can be accessed via + multiple feature paths. Feature lists may also be cyclic. + + Two feature lists are considered equal if they assign the same + values to all features, and have the same reentrances. + + :see: ``FeatStruct`` for information about feature paths, reentrance, + cyclic feature structures, mutability, freezing, and hashing. + """ + + def __init__(self, features=()): + """ + Create a new feature list, with the specified features. + + :param features: The initial list of features for this feature + list. If ``features`` is a string, then it is paresd using + ``FeatStructReader``. Otherwise, it should be a sequence + of basic values and nested feature structures. + """ + if isinstance(features, str): + FeatStructReader().fromstring(features, self) + else: + list.__init__(self, features) + + # //////////////////////////////////////////////////////////// + # { List methods + # //////////////////////////////////////////////////////////// + _INDEX_ERROR = "Expected int or feature path. Got %r." + + def __getitem__(self, name_or_path): + if isinstance(name_or_path, int): + return list.__getitem__(self, name_or_path) + elif isinstance(name_or_path, tuple): + try: + val = self + for fid in name_or_path: + if not isinstance(val, FeatStruct): + raise KeyError # path contains base value + val = val[fid] + return val + except (KeyError, IndexError) as e: + raise KeyError(name_or_path) from e + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + def __delitem__(self, name_or_path): + """If the feature with the given name or path exists, delete + its value; otherwise, raise ``KeyError``.""" + if self._frozen: + raise ValueError(_FROZEN_ERROR) + if isinstance(name_or_path, (int, slice)): + return list.__delitem__(self, name_or_path) + elif isinstance(name_or_path, tuple): + if len(name_or_path) == 0: + raise ValueError("The path () can not be set") + else: + parent = self[name_or_path[:-1]] + if not isinstance(parent, FeatStruct): + raise KeyError(name_or_path) # path contains base value + del parent[name_or_path[-1]] + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + def __setitem__(self, name_or_path, value): + """Set the value for the feature with the given name or path + to ``value``. If ``name_or_path`` is an invalid path, raise + ``KeyError``.""" + if self._frozen: + raise ValueError(_FROZEN_ERROR) + if isinstance(name_or_path, (int, slice)): + return list.__setitem__(self, name_or_path, value) + elif isinstance(name_or_path, tuple): + if len(name_or_path) == 0: + raise ValueError("The path () can not be set") + else: + parent = self[name_or_path[:-1]] + if not isinstance(parent, FeatStruct): + raise KeyError(name_or_path) # path contains base value + parent[name_or_path[-1]] = value + else: + raise TypeError(self._INDEX_ERROR % name_or_path) + + # __delslice__ = _check_frozen(list.__delslice__, ' ') + # __setslice__ = _check_frozen(list.__setslice__, ' ') + __iadd__ = _check_frozen(list.__iadd__) + __imul__ = _check_frozen(list.__imul__) + append = _check_frozen(list.append) + extend = _check_frozen(list.extend) + insert = _check_frozen(list.insert) + pop = _check_frozen(list.pop) + remove = _check_frozen(list.remove) + reverse = _check_frozen(list.reverse) + sort = _check_frozen(list.sort) + + ##//////////////////////////////////////////////////////////// + # { Copying + ##//////////////////////////////////////////////////////////// + + def __deepcopy__(self, memo): + memo[id(self)] = selfcopy = self.__class__() + selfcopy.extend(copy.deepcopy(fval, memo) for fval in self) + return selfcopy + + ##//////////////////////////////////////////////////////////// + # { Uniform Accessor Methods + ##//////////////////////////////////////////////////////////// + + def _keys(self): + return list(range(len(self))) + + def _values(self): + return self + + def _items(self): + return enumerate(self) + + ##//////////////////////////////////////////////////////////// + # { String Representations + ##//////////////////////////////////////////////////////////// + + # Special handling for: reentrances, variables, expressions. + def _repr(self, reentrances, reentrance_ids): + # If this is the first time we've seen a reentrant structure, + # then assign it a unique identifier. + if reentrances[id(self)]: + assert id(self) not in reentrance_ids + reentrance_ids[id(self)] = repr(len(reentrance_ids) + 1) + prefix = "(%s)" % reentrance_ids[id(self)] + else: + prefix = "" + + segments = [] + for fval in self: + if id(fval) in reentrance_ids: + segments.append("->(%s)" % reentrance_ids[id(fval)]) + elif isinstance(fval, Variable): + segments.append(fval.name) + elif isinstance(fval, Expression): + segments.append("%s" % fval) + elif isinstance(fval, FeatStruct): + segments.append(fval._repr(reentrances, reentrance_ids)) + else: + segments.append("%s" % repr(fval)) + + return "{}[{}]".format(prefix, ", ".join(segments)) + + +###################################################################### +# Variables & Bindings +###################################################################### + + +def substitute_bindings(fstruct, bindings, fs_class="default"): + """ + Return the feature structure that is obtained by replacing each + variable bound by ``bindings`` with its binding. If a variable is + aliased to a bound variable, then it will be replaced by that + variable's value. If a variable is aliased to an unbound + variable, then it will be replaced by that variable. + + :type bindings: dict(Variable -> any) + :param bindings: A dictionary mapping from variables to values. + """ + if fs_class == "default": + fs_class = _default_fs_class(fstruct) + fstruct = copy.deepcopy(fstruct) + _substitute_bindings(fstruct, bindings, fs_class, set()) + return fstruct + + +def _substitute_bindings(fstruct, bindings, fs_class, visited): + # Visit each node only once: + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + + if _is_mapping(fstruct): + items = fstruct.items() + elif _is_sequence(fstruct): + items = enumerate(fstruct) + else: + raise ValueError("Expected mapping or sequence") + for (fname, fval) in items: + while isinstance(fval, Variable) and fval in bindings: + fval = fstruct[fname] = bindings[fval] + if isinstance(fval, fs_class): + _substitute_bindings(fval, bindings, fs_class, visited) + elif isinstance(fval, SubstituteBindingsI): + fstruct[fname] = fval.substitute_bindings(bindings) + + +def retract_bindings(fstruct, bindings, fs_class="default"): + """ + Return the feature structure that is obtained by replacing each + feature structure value that is bound by ``bindings`` with the + variable that binds it. A feature structure value must be + identical to a bound value (i.e., have equal id) to be replaced. + + ``bindings`` is modified to point to this new feature structure, + rather than the original feature structure. Feature structure + values in ``bindings`` may be modified if they are contained in + ``fstruct``. + """ + if fs_class == "default": + fs_class = _default_fs_class(fstruct) + (fstruct, new_bindings) = copy.deepcopy((fstruct, bindings)) + bindings.update(new_bindings) + inv_bindings = {id(val): var for (var, val) in bindings.items()} + _retract_bindings(fstruct, inv_bindings, fs_class, set()) + return fstruct + + +def _retract_bindings(fstruct, inv_bindings, fs_class, visited): + # Visit each node only once: + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + + if _is_mapping(fstruct): + items = fstruct.items() + elif _is_sequence(fstruct): + items = enumerate(fstruct) + else: + raise ValueError("Expected mapping or sequence") + for (fname, fval) in items: + if isinstance(fval, fs_class): + if id(fval) in inv_bindings: + fstruct[fname] = inv_bindings[id(fval)] + _retract_bindings(fval, inv_bindings, fs_class, visited) + + +def find_variables(fstruct, fs_class="default"): + """ + :return: The set of variables used by this feature structure. + :rtype: set(Variable) + """ + if fs_class == "default": + fs_class = _default_fs_class(fstruct) + return _variables(fstruct, set(), fs_class, set()) + + +def _variables(fstruct, vars, fs_class, visited): + # Visit each node only once: + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + if _is_mapping(fstruct): + items = fstruct.items() + elif _is_sequence(fstruct): + items = enumerate(fstruct) + else: + raise ValueError("Expected mapping or sequence") + for (fname, fval) in items: + if isinstance(fval, Variable): + vars.add(fval) + elif isinstance(fval, fs_class): + _variables(fval, vars, fs_class, visited) + elif isinstance(fval, SubstituteBindingsI): + vars.update(fval.variables()) + return vars + + +def rename_variables( + fstruct, vars=None, used_vars=(), new_vars=None, fs_class="default" +): + """ + Return the feature structure that is obtained by replacing + any of this feature structure's variables that are in ``vars`` + with new variables. The names for these new variables will be + names that are not used by any variable in ``vars``, or in + ``used_vars``, or in this feature structure. + + :type vars: set + :param vars: The set of variables that should be renamed. + If not specified, ``find_variables(fstruct)`` is used; i.e., all + variables will be given new names. + :type used_vars: set + :param used_vars: A set of variables whose names should not be + used by the new variables. + :type new_vars: dict(Variable -> Variable) + :param new_vars: A dictionary that is used to hold the mapping + from old variables to new variables. For each variable *v* + in this feature structure: + + - If ``new_vars`` maps *v* to *v'*, then *v* will be + replaced by *v'*. + - If ``new_vars`` does not contain *v*, but ``vars`` + does contain *v*, then a new entry will be added to + ``new_vars``, mapping *v* to the new variable that is used + to replace it. + + To consistently rename the variables in a set of feature + structures, simply apply rename_variables to each one, using + the same dictionary: + + >>> from nltk.featstruct import FeatStruct + >>> fstruct1 = FeatStruct('[subj=[agr=[gender=?y]], obj=[agr=[gender=?y]]]') + >>> fstruct2 = FeatStruct('[subj=[agr=[number=?z,gender=?y]], obj=[agr=[number=?z,gender=?y]]]') + >>> new_vars = {} # Maps old vars to alpha-renamed vars + >>> fstruct1.rename_variables(new_vars=new_vars) + [obj=[agr=[gender=?y2]], subj=[agr=[gender=?y2]]] + >>> fstruct2.rename_variables(new_vars=new_vars) + [obj=[agr=[gender=?y2, number=?z2]], subj=[agr=[gender=?y2, number=?z2]]] + + If new_vars is not specified, then an empty dictionary is used. + """ + if fs_class == "default": + fs_class = _default_fs_class(fstruct) + + # Default values: + if new_vars is None: + new_vars = {} + if vars is None: + vars = find_variables(fstruct, fs_class) + else: + vars = set(vars) + + # Add our own variables to used_vars. + used_vars = find_variables(fstruct, fs_class).union(used_vars) + + # Copy ourselves, and rename variables in the copy. + return _rename_variables( + copy.deepcopy(fstruct), vars, used_vars, new_vars, fs_class, set() + ) + + +def _rename_variables(fstruct, vars, used_vars, new_vars, fs_class, visited): + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + if _is_mapping(fstruct): + items = fstruct.items() + elif _is_sequence(fstruct): + items = enumerate(fstruct) + else: + raise ValueError("Expected mapping or sequence") + for (fname, fval) in items: + if isinstance(fval, Variable): + # If it's in new_vars, then rebind it. + if fval in new_vars: + fstruct[fname] = new_vars[fval] + # If it's in vars, pick a new name for it. + elif fval in vars: + new_vars[fval] = _rename_variable(fval, used_vars) + fstruct[fname] = new_vars[fval] + used_vars.add(new_vars[fval]) + elif isinstance(fval, fs_class): + _rename_variables(fval, vars, used_vars, new_vars, fs_class, visited) + elif isinstance(fval, SubstituteBindingsI): + # Pick new names for any variables in `vars` + for var in fval.variables(): + if var in vars and var not in new_vars: + new_vars[var] = _rename_variable(var, used_vars) + used_vars.add(new_vars[var]) + # Replace all variables in `new_vars`. + fstruct[fname] = fval.substitute_bindings(new_vars) + return fstruct + + +def _rename_variable(var, used_vars): + name, n = re.sub(r"\d+$", "", var.name), 2 + if not name: + name = "?" + while Variable(f"{name}{n}") in used_vars: + n += 1 + return Variable(f"{name}{n}") + + +def remove_variables(fstruct, fs_class="default"): + """ + :rtype: FeatStruct + :return: The feature structure that is obtained by deleting + all features whose values are ``Variables``. + """ + if fs_class == "default": + fs_class = _default_fs_class(fstruct) + return _remove_variables(copy.deepcopy(fstruct), fs_class, set()) + + +def _remove_variables(fstruct, fs_class, visited): + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + + if _is_mapping(fstruct): + items = list(fstruct.items()) + elif _is_sequence(fstruct): + items = list(enumerate(fstruct)) + else: + raise ValueError("Expected mapping or sequence") + + for (fname, fval) in items: + if isinstance(fval, Variable): + del fstruct[fname] + elif isinstance(fval, fs_class): + _remove_variables(fval, fs_class, visited) + return fstruct + + +###################################################################### +# Unification +###################################################################### + + +class _UnificationFailure: + def __repr__(self): + return "nltk.featstruct.UnificationFailure" + + +UnificationFailure = _UnificationFailure() +"""A unique value used to indicate unification failure. It can be + returned by ``Feature.unify_base_values()`` or by custom ``fail()`` + functions to indicate that unificaiton should fail.""" + + +# The basic unification algorithm: +# 1. Make copies of self and other (preserving reentrance) +# 2. Destructively unify self and other +# 3. Apply forward pointers, to preserve reentrance. +# 4. Replace bound variables with their values. +def unify( + fstruct1, + fstruct2, + bindings=None, + trace=False, + fail=None, + rename_vars=True, + fs_class="default", +): + """ + Unify ``fstruct1`` with ``fstruct2``, and return the resulting feature + structure. This unified feature structure is the minimal + feature structure that contains all feature value assignments from both + ``fstruct1`` and ``fstruct2``, and that preserves all reentrancies. + + If no such feature structure exists (because ``fstruct1`` and + ``fstruct2`` specify incompatible values for some feature), then + unification fails, and ``unify`` returns None. + + Bound variables are replaced by their values. Aliased + variables are replaced by their representative variable + (if unbound) or the value of their representative variable + (if bound). I.e., if variable *v* is in ``bindings``, + then *v* is replaced by ``bindings[v]``. This will + be repeated until the variable is replaced by an unbound + variable or a non-variable value. + + Unbound variables are bound when they are unified with + values; and aliased when they are unified with variables. + I.e., if variable *v* is not in ``bindings``, and is + unified with a variable or value *x*, then + ``bindings[v]`` is set to *x*. + + If ``bindings`` is unspecified, then all variables are + assumed to be unbound. I.e., ``bindings`` defaults to an + empty dict. + + >>> from nltk.featstruct import FeatStruct + >>> FeatStruct('[a=?x]').unify(FeatStruct('[b=?x]')) + [a=?x, b=?x2] + + :type bindings: dict(Variable -> any) + :param bindings: A set of variable bindings to be used and + updated during unification. + :type trace: bool + :param trace: If true, generate trace output. + :type rename_vars: bool + :param rename_vars: If True, then rename any variables in + ``fstruct2`` that are also used in ``fstruct1``, in order to + avoid collisions on variable names. + """ + # Decide which class(es) will be treated as feature structures, + # for the purposes of unification. + if fs_class == "default": + fs_class = _default_fs_class(fstruct1) + if _default_fs_class(fstruct2) != fs_class: + raise ValueError( + "Mixing FeatStruct objects with Python " + "dicts and lists is not supported." + ) + assert isinstance(fstruct1, fs_class) + assert isinstance(fstruct2, fs_class) + + # If bindings are unspecified, use an empty set of bindings. + user_bindings = bindings is not None + if bindings is None: + bindings = {} + + # Make copies of fstruct1 and fstruct2 (since the unification + # algorithm is destructive). Do it all at once, to preserve + # reentrance links between fstruct1 and fstruct2. Copy bindings + # as well, in case there are any bound vars that contain parts + # of fstruct1 or fstruct2. + (fstruct1copy, fstruct2copy, bindings_copy) = copy.deepcopy( + (fstruct1, fstruct2, bindings) + ) + + # Copy the bindings back to the original bindings dict. + bindings.update(bindings_copy) + + if rename_vars: + vars1 = find_variables(fstruct1copy, fs_class) + vars2 = find_variables(fstruct2copy, fs_class) + _rename_variables(fstruct2copy, vars1, vars2, {}, fs_class, set()) + + # Do the actual unification. If it fails, return None. + forward = {} + if trace: + _trace_unify_start((), fstruct1copy, fstruct2copy) + try: + result = _destructively_unify( + fstruct1copy, fstruct2copy, bindings, forward, trace, fail, fs_class, () + ) + except _UnificationFailureError: + return None + + # _destructively_unify might return UnificationFailure, e.g. if we + # tried to unify a mapping with a sequence. + if result is UnificationFailure: + if fail is None: + return None + else: + return fail(fstruct1copy, fstruct2copy, ()) + + # Replace any feature structure that has a forward pointer + # with the target of its forward pointer. + result = _apply_forwards(result, forward, fs_class, set()) + if user_bindings: + _apply_forwards_to_bindings(forward, bindings) + + # Replace bound vars with values. + _resolve_aliases(bindings) + _substitute_bindings(result, bindings, fs_class, set()) + + # Return the result. + if trace: + _trace_unify_succeed((), result) + if trace: + _trace_bindings((), bindings) + return result + + +class _UnificationFailureError(Exception): + """An exception that is used by ``_destructively_unify`` to abort + unification when a failure is encountered.""" + + +def _destructively_unify( + fstruct1, fstruct2, bindings, forward, trace, fail, fs_class, path +): + """ + Attempt to unify ``fstruct1`` and ``fstruct2`` by modifying them + in-place. If the unification succeeds, then ``fstruct1`` will + contain the unified value, the value of ``fstruct2`` is undefined, + and forward[id(fstruct2)] is set to fstruct1. If the unification + fails, then a _UnificationFailureError is raised, and the + values of ``fstruct1`` and ``fstruct2`` are undefined. + + :param bindings: A dictionary mapping variables to values. + :param forward: A dictionary mapping feature structures ids + to replacement structures. When two feature structures + are merged, a mapping from one to the other will be added + to the forward dictionary; and changes will be made only + to the target of the forward dictionary. + ``_destructively_unify`` will always 'follow' any links + in the forward dictionary for fstruct1 and fstruct2 before + actually unifying them. + :param trace: If true, generate trace output + :param path: The feature path that led us to this unification + step. Used for trace output. + """ + # If fstruct1 is already identical to fstruct2, we're done. + # Note: this, together with the forward pointers, ensures + # that unification will terminate even for cyclic structures. + if fstruct1 is fstruct2: + if trace: + _trace_unify_identity(path, fstruct1) + return fstruct1 + + # Set fstruct2's forward pointer to point to fstruct1; this makes + # fstruct1 the canonical copy for fstruct2. Note that we need to + # do this before we recurse into any child structures, in case + # they're cyclic. + forward[id(fstruct2)] = fstruct1 + + # Unifying two mappings: + if _is_mapping(fstruct1) and _is_mapping(fstruct2): + for fname in fstruct1: + if getattr(fname, "default", None) is not None: + fstruct2.setdefault(fname, fname.default) + for fname in fstruct2: + if getattr(fname, "default", None) is not None: + fstruct1.setdefault(fname, fname.default) + + # Unify any values that are defined in both fstruct1 and + # fstruct2. Copy any values that are defined in fstruct2 but + # not in fstruct1 to fstruct1. Note: sorting fstruct2's + # features isn't actually necessary; but we do it to give + # deterministic behavior, e.g. for tracing. + for fname, fval2 in sorted(fstruct2.items()): + if fname in fstruct1: + fstruct1[fname] = _unify_feature_values( + fname, + fstruct1[fname], + fval2, + bindings, + forward, + trace, + fail, + fs_class, + path + (fname,), + ) + else: + fstruct1[fname] = fval2 + + return fstruct1 # Contains the unified value. + + # Unifying two sequences: + elif _is_sequence(fstruct1) and _is_sequence(fstruct2): + # If the lengths don't match, fail. + if len(fstruct1) != len(fstruct2): + return UnificationFailure + + # Unify corresponding values in fstruct1 and fstruct2. + for findex in range(len(fstruct1)): + fstruct1[findex] = _unify_feature_values( + findex, + fstruct1[findex], + fstruct2[findex], + bindings, + forward, + trace, + fail, + fs_class, + path + (findex,), + ) + + return fstruct1 # Contains the unified value. + + # Unifying sequence & mapping: fail. The failure function + # doesn't get a chance to recover in this case. + elif (_is_sequence(fstruct1) or _is_mapping(fstruct1)) and ( + _is_sequence(fstruct2) or _is_mapping(fstruct2) + ): + return UnificationFailure + + # Unifying anything else: not allowed! + raise TypeError("Expected mappings or sequences") + + +def _unify_feature_values( + fname, fval1, fval2, bindings, forward, trace, fail, fs_class, fpath +): + """ + Attempt to unify ``fval1`` and and ``fval2``, and return the + resulting unified value. The method of unification will depend on + the types of ``fval1`` and ``fval2``: + + 1. If they're both feature structures, then destructively + unify them (see ``_destructively_unify()``. + 2. If they're both unbound variables, then alias one variable + to the other (by setting bindings[v2]=v1). + 3. If one is an unbound variable, and the other is a value, + then bind the unbound variable to the value. + 4. If one is a feature structure, and the other is a base value, + then fail. + 5. If they're both base values, then unify them. By default, + this will succeed if they are equal, and fail otherwise. + """ + if trace: + _trace_unify_start(fpath, fval1, fval2) + + # Look up the "canonical" copy of fval1 and fval2 + while id(fval1) in forward: + fval1 = forward[id(fval1)] + while id(fval2) in forward: + fval2 = forward[id(fval2)] + + # If fval1 or fval2 is a bound variable, then + # replace it by the variable's bound value. This + # includes aliased variables, which are encoded as + # variables bound to other variables. + fvar1 = fvar2 = None + while isinstance(fval1, Variable) and fval1 in bindings: + fvar1 = fval1 + fval1 = bindings[fval1] + while isinstance(fval2, Variable) and fval2 in bindings: + fvar2 = fval2 + fval2 = bindings[fval2] + + # Case 1: Two feature structures (recursive case) + if isinstance(fval1, fs_class) and isinstance(fval2, fs_class): + result = _destructively_unify( + fval1, fval2, bindings, forward, trace, fail, fs_class, fpath + ) + + # Case 2: Two unbound variables (create alias) + elif isinstance(fval1, Variable) and isinstance(fval2, Variable): + if fval1 != fval2: + bindings[fval2] = fval1 + result = fval1 + + # Case 3: An unbound variable and a value (bind) + elif isinstance(fval1, Variable): + bindings[fval1] = fval2 + result = fval1 + elif isinstance(fval2, Variable): + bindings[fval2] = fval1 + result = fval2 + + # Case 4: A feature structure & a base value (fail) + elif isinstance(fval1, fs_class) or isinstance(fval2, fs_class): + result = UnificationFailure + + # Case 5: Two base values + else: + # Case 5a: Feature defines a custom unification method for base values + if isinstance(fname, Feature): + result = fname.unify_base_values(fval1, fval2, bindings) + # Case 5b: Feature value defines custom unification method + elif isinstance(fval1, CustomFeatureValue): + result = fval1.unify(fval2) + # Sanity check: unify value should be symmetric + if isinstance(fval2, CustomFeatureValue) and result != fval2.unify(fval1): + raise AssertionError( + "CustomFeatureValue objects %r and %r disagree " + "about unification value: %r vs. %r" + % (fval1, fval2, result, fval2.unify(fval1)) + ) + elif isinstance(fval2, CustomFeatureValue): + result = fval2.unify(fval1) + # Case 5c: Simple values -- check if they're equal. + else: + if fval1 == fval2: + result = fval1 + else: + result = UnificationFailure + + # If either value was a bound variable, then update the + # bindings. (This is really only necessary if fname is a + # Feature or if either value is a CustomFeatureValue.) + if result is not UnificationFailure: + if fvar1 is not None: + bindings[fvar1] = result + result = fvar1 + if fvar2 is not None and fvar2 != fvar1: + bindings[fvar2] = result + result = fvar2 + + # If we unification failed, call the failure function; it + # might decide to continue anyway. + if result is UnificationFailure: + if fail is not None: + result = fail(fval1, fval2, fpath) + if trace: + _trace_unify_fail(fpath[:-1], result) + if result is UnificationFailure: + raise _UnificationFailureError + + # Normalize the result. + if isinstance(result, fs_class): + result = _apply_forwards(result, forward, fs_class, set()) + + if trace: + _trace_unify_succeed(fpath, result) + if trace and isinstance(result, fs_class): + _trace_bindings(fpath, bindings) + + return result + + +def _apply_forwards_to_bindings(forward, bindings): + """ + Replace any feature structure that has a forward pointer with + the target of its forward pointer (to preserve reentrancy). + """ + for (var, value) in bindings.items(): + while id(value) in forward: + value = forward[id(value)] + bindings[var] = value + + +def _apply_forwards(fstruct, forward, fs_class, visited): + """ + Replace any feature structure that has a forward pointer with + the target of its forward pointer (to preserve reentrancy). + """ + # Follow our own forwards pointers (if any) + while id(fstruct) in forward: + fstruct = forward[id(fstruct)] + + # Visit each node only once: + if id(fstruct) in visited: + return + visited.add(id(fstruct)) + + if _is_mapping(fstruct): + items = fstruct.items() + elif _is_sequence(fstruct): + items = enumerate(fstruct) + else: + raise ValueError("Expected mapping or sequence") + for fname, fval in items: + if isinstance(fval, fs_class): + # Replace w/ forwarded value. + while id(fval) in forward: + fval = forward[id(fval)] + fstruct[fname] = fval + # Recurse to child. + _apply_forwards(fval, forward, fs_class, visited) + + return fstruct + + +def _resolve_aliases(bindings): + """ + Replace any bound aliased vars with their binding; and replace + any unbound aliased vars with their representative var. + """ + for (var, value) in bindings.items(): + while isinstance(value, Variable) and value in bindings: + value = bindings[var] = bindings[value] + + +def _trace_unify_start(path, fval1, fval2): + if path == (): + print("\nUnification trace:") + else: + fullname = ".".join("%s" % n for n in path) + print(" " + "| " * (len(path) - 1) + "|") + print(" " + "| " * (len(path) - 1) + "| Unify feature: %s" % fullname) + print(" " + "| " * len(path) + " / " + _trace_valrepr(fval1)) + print(" " + "| " * len(path) + "|\\ " + _trace_valrepr(fval2)) + + +def _trace_unify_identity(path, fval1): + print(" " + "| " * len(path) + "|") + print(" " + "| " * len(path) + "| (identical objects)") + print(" " + "| " * len(path) + "|") + print(" " + "| " * len(path) + "+-->" + repr(fval1)) + + +def _trace_unify_fail(path, result): + if result is UnificationFailure: + resume = "" + else: + resume = " (nonfatal)" + print(" " + "| " * len(path) + "| |") + print(" " + "X " * len(path) + "X X <-- FAIL" + resume) + + +def _trace_unify_succeed(path, fval1): + # Print the result. + print(" " + "| " * len(path) + "|") + print(" " + "| " * len(path) + "+-->" + repr(fval1)) + + +def _trace_bindings(path, bindings): + # Print the bindings (if any). + if len(bindings) > 0: + binditems = sorted(bindings.items(), key=lambda v: v[0].name) + bindstr = "{%s}" % ", ".join( + f"{var}: {_trace_valrepr(val)}" for (var, val) in binditems + ) + print(" " + "| " * len(path) + " Bindings: " + bindstr) + + +def _trace_valrepr(val): + if isinstance(val, Variable): + return "%s" % val + else: + return "%s" % repr(val) + + +def subsumes(fstruct1, fstruct2): + """ + Return True if ``fstruct1`` subsumes ``fstruct2``. I.e., return + true if unifying ``fstruct1`` with ``fstruct2`` would result in a + feature structure equal to ``fstruct2.`` + + :rtype: bool + """ + return fstruct2 == unify(fstruct1, fstruct2) + + +def conflicts(fstruct1, fstruct2, trace=0): + """ + Return a list of the feature paths of all features which are + assigned incompatible values by ``fstruct1`` and ``fstruct2``. + + :rtype: list(tuple) + """ + conflict_list = [] + + def add_conflict(fval1, fval2, path): + conflict_list.append(path) + return fval1 + + unify(fstruct1, fstruct2, fail=add_conflict, trace=trace) + return conflict_list + + +###################################################################### +# Helper Functions +###################################################################### + + +def _is_mapping(v): + return hasattr(v, "__contains__") and hasattr(v, "keys") + + +def _is_sequence(v): + return hasattr(v, "__iter__") and hasattr(v, "__len__") and not isinstance(v, str) + + +def _default_fs_class(obj): + if isinstance(obj, FeatStruct): + return FeatStruct + if isinstance(obj, (dict, list)): + return (dict, list) + else: + raise ValueError( + "To unify objects of type %s, you must specify " + "fs_class explicitly." % obj.__class__.__name__ + ) + + +###################################################################### +# FeatureValueSet & FeatureValueTuple +###################################################################### + + +class SubstituteBindingsSequence(SubstituteBindingsI): + """ + A mixin class for sequence classes that distributes variables() and + substitute_bindings() over the object's elements. + """ + + def variables(self): + return [elt for elt in self if isinstance(elt, Variable)] + sum( + ( + list(elt.variables()) + for elt in self + if isinstance(elt, SubstituteBindingsI) + ), + [], + ) + + def substitute_bindings(self, bindings): + return self.__class__([self.subst(v, bindings) for v in self]) + + def subst(self, v, bindings): + if isinstance(v, SubstituteBindingsI): + return v.substitute_bindings(bindings) + else: + return bindings.get(v, v) + + +class FeatureValueTuple(SubstituteBindingsSequence, tuple): + """ + A base feature value that is a tuple of other base feature values. + FeatureValueTuple implements ``SubstituteBindingsI``, so it any + variable substitutions will be propagated to the elements + contained by the set. A ``FeatureValueTuple`` is immutable. + """ + + def __repr__(self): # [xx] really use %s here? + if len(self) == 0: + return "()" + return "(%s)" % ", ".join(f"{b}" for b in self) + + +class FeatureValueSet(SubstituteBindingsSequence, frozenset): + """ + A base feature value that is a set of other base feature values. + FeatureValueSet implements ``SubstituteBindingsI``, so it any + variable substitutions will be propagated to the elements + contained by the set. A ``FeatureValueSet`` is immutable. + """ + + def __repr__(self): # [xx] really use %s here? + if len(self) == 0: + return "{/}" # distinguish from dict. + # n.b., we sort the string reprs of our elements, to ensure + # that our own repr is deterministic. + return "{%s}" % ", ".join(sorted(f"{b}" for b in self)) + + __str__ = __repr__ + + +class FeatureValueUnion(SubstituteBindingsSequence, frozenset): + """ + A base feature value that represents the union of two or more + ``FeatureValueSet`` or ``Variable``. + """ + + def __new__(cls, values): + # If values contains FeatureValueUnions, then collapse them. + values = _flatten(values, FeatureValueUnion) + + # If the resulting list contains no variables, then + # use a simple FeatureValueSet instead. + if sum(isinstance(v, Variable) for v in values) == 0: + values = _flatten(values, FeatureValueSet) + return FeatureValueSet(values) + + # If we contain a single variable, return that variable. + if len(values) == 1: + return list(values)[0] + + # Otherwise, build the FeatureValueUnion. + return frozenset.__new__(cls, values) + + def __repr__(self): + # n.b., we sort the string reprs of our elements, to ensure + # that our own repr is deterministic. also, note that len(self) + # is guaranteed to be 2 or more. + return "{%s}" % "+".join(sorted(f"{b}" for b in self)) + + +class FeatureValueConcat(SubstituteBindingsSequence, tuple): + """ + A base feature value that represents the concatenation of two or + more ``FeatureValueTuple`` or ``Variable``. + """ + + def __new__(cls, values): + # If values contains FeatureValueConcats, then collapse them. + values = _flatten(values, FeatureValueConcat) + + # If the resulting list contains no variables, then + # use a simple FeatureValueTuple instead. + if sum(isinstance(v, Variable) for v in values) == 0: + values = _flatten(values, FeatureValueTuple) + return FeatureValueTuple(values) + + # If we contain a single variable, return that variable. + if len(values) == 1: + return list(values)[0] + + # Otherwise, build the FeatureValueConcat. + return tuple.__new__(cls, values) + + def __repr__(self): + # n.b.: len(self) is guaranteed to be 2 or more. + return "(%s)" % "+".join(f"{b}" for b in self) + + +def _flatten(lst, cls): + """ + Helper function -- return a copy of list, with all elements of + type ``cls`` spliced in rather than appended in. + """ + result = [] + for elt in lst: + if isinstance(elt, cls): + result.extend(elt) + else: + result.append(elt) + return result + + +###################################################################### +# Specialized Features +###################################################################### + + +@total_ordering +class Feature: + """ + A feature identifier that's specialized to put additional + constraints, default values, etc. + """ + + def __init__(self, name, default=None, display=None): + assert display in (None, "prefix", "slash") + + self._name = name # [xx] rename to .identifier? + self._default = default # [xx] not implemented yet. + self._display = display + + if self._display == "prefix": + self._sortkey = (-1, self._name) + elif self._display == "slash": + self._sortkey = (1, self._name) + else: + self._sortkey = (0, self._name) + + @property + def name(self): + """The name of this feature.""" + return self._name + + @property + def default(self): + """Default value for this feature.""" + return self._default + + @property + def display(self): + """Custom display location: can be prefix, or slash.""" + return self._display + + def __repr__(self): + return "*%s*" % self.name + + def __lt__(self, other): + if isinstance(other, str): + return True + if not isinstance(other, Feature): + raise_unorderable_types("<", self, other) + return self._sortkey < other._sortkey + + def __eq__(self, other): + return type(self) == type(other) and self._name == other._name + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._name) + + # //////////////////////////////////////////////////////////// + # These can be overridden by subclasses: + # //////////////////////////////////////////////////////////// + + def read_value(self, s, position, reentrances, parser): + return parser.read_value(s, position, reentrances) + + def unify_base_values(self, fval1, fval2, bindings): + """ + If possible, return a single value.. If not, return + the value ``UnificationFailure``. + """ + if fval1 == fval2: + return fval1 + else: + return UnificationFailure + + +class SlashFeature(Feature): + def read_value(self, s, position, reentrances, parser): + return parser.read_partial(s, position, reentrances) + + +class RangeFeature(Feature): + RANGE_RE = re.compile(r"(-?\d+):(-?\d+)") + + def read_value(self, s, position, reentrances, parser): + m = self.RANGE_RE.match(s, position) + if not m: + raise ValueError("range", position) + return (int(m.group(1)), int(m.group(2))), m.end() + + def unify_base_values(self, fval1, fval2, bindings): + if fval1 is None: + return fval2 + if fval2 is None: + return fval1 + rng = max(fval1[0], fval2[0]), min(fval1[1], fval2[1]) + if rng[1] < rng[0]: + return UnificationFailure + return rng + + +SLASH = SlashFeature("slash", default=False, display="slash") +TYPE = Feature("type", display="prefix") + + +###################################################################### +# Specialized Feature Values +###################################################################### + + +@total_ordering +class CustomFeatureValue: + """ + An abstract base class for base values that define a custom + unification method. The custom unification method of + ``CustomFeatureValue`` will be used during unification if: + + - The ``CustomFeatureValue`` is unified with another base value. + - The ``CustomFeatureValue`` is not the value of a customized + ``Feature`` (which defines its own unification method). + + If two ``CustomFeatureValue`` objects are unified with one another + during feature structure unification, then the unified base values + they return *must* be equal; otherwise, an ``AssertionError`` will + be raised. + + Subclasses must define ``unify()``, ``__eq__()`` and ``__lt__()``. + Subclasses may also wish to define ``__hash__()``. + """ + + def unify(self, other): + """ + If this base value unifies with ``other``, then return the + unified value. Otherwise, return ``UnificationFailure``. + """ + raise NotImplementedError("abstract base class") + + def __eq__(self, other): + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + return NotImplemented + + def __hash__(self): + raise TypeError("%s objects or unhashable" % self.__class__.__name__) + + +###################################################################### +# Feature Structure Reader +###################################################################### + + +class FeatStructReader: + def __init__( + self, + features=(SLASH, TYPE), + fdict_class=FeatStruct, + flist_class=FeatList, + logic_parser=None, + ): + self._features = {f.name: f for f in features} + self._fdict_class = fdict_class + self._flist_class = flist_class + self._prefix_feature = None + self._slash_feature = None + for feature in features: + if feature.display == "slash": + if self._slash_feature: + raise ValueError("Multiple features w/ display=slash") + self._slash_feature = feature + if feature.display == "prefix": + if self._prefix_feature: + raise ValueError("Multiple features w/ display=prefix") + self._prefix_feature = feature + self._features_with_defaults = [ + feature for feature in features if feature.default is not None + ] + if logic_parser is None: + logic_parser = LogicParser() + self._logic_parser = logic_parser + + def fromstring(self, s, fstruct=None): + """ + Convert a string representation of a feature structure (as + displayed by repr) into a ``FeatStruct``. This process + imposes the following restrictions on the string + representation: + + - Feature names cannot contain any of the following: + whitespace, parentheses, quote marks, equals signs, + dashes, commas, and square brackets. Feature names may + not begin with plus signs or minus signs. + - Only the following basic feature value are supported: + strings, integers, variables, None, and unquoted + alphanumeric strings. + - For reentrant values, the first mention must specify + a reentrance identifier and a value; and any subsequent + mentions must use arrows (``'->'``) to reference the + reentrance identifier. + """ + s = s.strip() + value, position = self.read_partial(s, 0, {}, fstruct) + if position != len(s): + self._error(s, "end of string", position) + return value + + _START_FSTRUCT_RE = re.compile(r"\s*(?:\((\d+)\)\s*)?(\??[\w-]+)?(\[)") + _END_FSTRUCT_RE = re.compile(r"\s*]\s*") + _SLASH_RE = re.compile(r"/") + _FEATURE_NAME_RE = re.compile(r'\s*([+-]?)([^\s\(\)<>"\'\-=\[\],]+)\s*') + _REENTRANCE_RE = re.compile(r"\s*->\s*") + _TARGET_RE = re.compile(r"\s*\((\d+)\)\s*") + _ASSIGN_RE = re.compile(r"\s*=\s*") + _COMMA_RE = re.compile(r"\s*,\s*") + _BARE_PREFIX_RE = re.compile(r"\s*(?:\((\d+)\)\s*)?(\??[\w-]+\s*)()") + # This one is used to distinguish fdicts from flists: + _START_FDICT_RE = re.compile( + r"(%s)|(%s\s*(%s\s*(=|->)|[+-]%s|\]))" + % ( + _BARE_PREFIX_RE.pattern, + _START_FSTRUCT_RE.pattern, + _FEATURE_NAME_RE.pattern, + _FEATURE_NAME_RE.pattern, + ) + ) + + def read_partial(self, s, position=0, reentrances=None, fstruct=None): + """ + Helper function that reads in a feature structure. + + :param s: The string to read. + :param position: The position in the string to start parsing. + :param reentrances: A dictionary from reentrance ids to values. + Defaults to an empty dictionary. + :return: A tuple (val, pos) of the feature structure created by + parsing and the position where the parsed feature structure ends. + :rtype: bool + """ + if reentrances is None: + reentrances = {} + try: + return self._read_partial(s, position, reentrances, fstruct) + except ValueError as e: + if len(e.args) != 2: + raise + self._error(s, *e.args) + + def _read_partial(self, s, position, reentrances, fstruct=None): + # Create the new feature structure + if fstruct is None: + if self._START_FDICT_RE.match(s, position): + fstruct = self._fdict_class() + else: + fstruct = self._flist_class() + + # Read up to the open bracket. + match = self._START_FSTRUCT_RE.match(s, position) + if not match: + match = self._BARE_PREFIX_RE.match(s, position) + if not match: + raise ValueError("open bracket or identifier", position) + position = match.end() + + # If there as an identifier, record it. + if match.group(1): + identifier = match.group(1) + if identifier in reentrances: + raise ValueError("new identifier", match.start(1)) + reentrances[identifier] = fstruct + + if isinstance(fstruct, FeatDict): + fstruct.clear() + return self._read_partial_featdict(s, position, match, reentrances, fstruct) + else: + del fstruct[:] + return self._read_partial_featlist(s, position, match, reentrances, fstruct) + + def _read_partial_featlist(self, s, position, match, reentrances, fstruct): + # Prefix features are not allowed: + if match.group(2): + raise ValueError("open bracket") + # Bare prefixes are not allowed: + if not match.group(3): + raise ValueError("open bracket") + + # Build a list of the features defined by the structure. + while position < len(s): + # Check for the close bracket. + match = self._END_FSTRUCT_RE.match(s, position) + if match is not None: + return fstruct, match.end() + + # Reentances have the form "-> (target)" + match = self._REENTRANCE_RE.match(s, position) + if match: + position = match.end() + match = self._TARGET_RE.match(s, position) + if not match: + raise ValueError("identifier", position) + target = match.group(1) + if target not in reentrances: + raise ValueError("bound identifier", position) + position = match.end() + fstruct.append(reentrances[target]) + + # Anything else is a value. + else: + value, position = self._read_value(0, s, position, reentrances) + fstruct.append(value) + + # If there's a close bracket, handle it at the top of the loop. + if self._END_FSTRUCT_RE.match(s, position): + continue + + # Otherwise, there should be a comma + match = self._COMMA_RE.match(s, position) + if match is None: + raise ValueError("comma", position) + position = match.end() + + # We never saw a close bracket. + raise ValueError("close bracket", position) + + def _read_partial_featdict(self, s, position, match, reentrances, fstruct): + # If there was a prefix feature, record it. + if match.group(2): + if self._prefix_feature is None: + raise ValueError("open bracket or identifier", match.start(2)) + prefixval = match.group(2).strip() + if prefixval.startswith("?"): + prefixval = Variable(prefixval) + fstruct[self._prefix_feature] = prefixval + + # If group 3 is empty, then we just have a bare prefix, so + # we're done. + if not match.group(3): + return self._finalize(s, match.end(), reentrances, fstruct) + + # Build a list of the features defined by the structure. + # Each feature has one of the three following forms: + # name = value + # name -> (target) + # +name + # -name + while position < len(s): + # Use these variables to hold info about each feature: + name = value = None + + # Check for the close bracket. + match = self._END_FSTRUCT_RE.match(s, position) + if match is not None: + return self._finalize(s, match.end(), reentrances, fstruct) + + # Get the feature name's name + match = self._FEATURE_NAME_RE.match(s, position) + if match is None: + raise ValueError("feature name", position) + name = match.group(2) + position = match.end() + + # Check if it's a special feature. + if name[0] == "*" and name[-1] == "*": + name = self._features.get(name[1:-1]) + if name is None: + raise ValueError("known special feature", match.start(2)) + + # Check if this feature has a value already. + if name in fstruct: + raise ValueError("new name", match.start(2)) + + # Boolean value ("+name" or "-name") + if match.group(1) == "+": + value = True + if match.group(1) == "-": + value = False + + # Reentrance link ("-> (target)") + if value is None: + match = self._REENTRANCE_RE.match(s, position) + if match is not None: + position = match.end() + match = self._TARGET_RE.match(s, position) + if not match: + raise ValueError("identifier", position) + target = match.group(1) + if target not in reentrances: + raise ValueError("bound identifier", position) + position = match.end() + value = reentrances[target] + + # Assignment ("= value"). + if value is None: + match = self._ASSIGN_RE.match(s, position) + if match: + position = match.end() + value, position = self._read_value(name, s, position, reentrances) + # None of the above: error. + else: + raise ValueError("equals sign", position) + + # Store the value. + fstruct[name] = value + + # If there's a close bracket, handle it at the top of the loop. + if self._END_FSTRUCT_RE.match(s, position): + continue + + # Otherwise, there should be a comma + match = self._COMMA_RE.match(s, position) + if match is None: + raise ValueError("comma", position) + position = match.end() + + # We never saw a close bracket. + raise ValueError("close bracket", position) + + def _finalize(self, s, pos, reentrances, fstruct): + """ + Called when we see the close brace -- checks for a slash feature, + and adds in default values. + """ + # Add the slash feature (if any) + match = self._SLASH_RE.match(s, pos) + if match: + name = self._slash_feature + v, pos = self._read_value(name, s, match.end(), reentrances) + fstruct[name] = v + ## Add any default features. -- handle in unficiation instead? + # for feature in self._features_with_defaults: + # fstruct.setdefault(feature, feature.default) + # Return the value. + return fstruct, pos + + def _read_value(self, name, s, position, reentrances): + if isinstance(name, Feature): + return name.read_value(s, position, reentrances, self) + else: + return self.read_value(s, position, reentrances) + + def read_value(self, s, position, reentrances): + for (handler, regexp) in self.VALUE_HANDLERS: + match = regexp.match(s, position) + if match: + handler_func = getattr(self, handler) + return handler_func(s, position, reentrances, match) + raise ValueError("value", position) + + def _error(self, s, expected, position): + lines = s.split("\n") + while position > len(lines[0]): + position -= len(lines.pop(0)) + 1 # +1 for the newline. + estr = ( + "Error parsing feature structure\n " + + lines[0] + + "\n " + + " " * position + + "^ " + + "Expected %s" % expected + ) + raise ValueError(estr) + + # //////////////////////////////////////////////////////////// + # { Value Readers + # //////////////////////////////////////////////////////////// + + #: A table indicating how feature values should be processed. Each + #: entry in the table is a pair (handler, regexp). The first entry + #: with a matching regexp will have its handler called. Handlers + #: should have the following signature:: + #: + #: def handler(s, position, reentrances, match): ... + #: + #: and should return a tuple (value, position), where position is + #: the string position where the value ended. (n.b.: order is + #: important here!) + VALUE_HANDLERS = [ + ("read_fstruct_value", _START_FSTRUCT_RE), + ("read_var_value", re.compile(r"\?[a-zA-Z_][a-zA-Z0-9_]*")), + ("read_str_value", re.compile("[uU]?[rR]?(['\"])")), + ("read_int_value", re.compile(r"-?\d+")), + ("read_sym_value", re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")), + ( + "read_app_value", + re.compile(r"<(app)\((\?[a-z][a-z]*)\s*," r"\s*(\?[a-z][a-z]*)\)>"), + ), + # ('read_logic_value', re.compile(r'<([^>]*)>')), + # lazily match any character after '<' until we hit a '>' not preceded by '-' + ("read_logic_value", re.compile(r"<(.*?)(?")), + ("read_set_value", re.compile(r"{")), + ("read_tuple_value", re.compile(r"\(")), + ] + + def read_fstruct_value(self, s, position, reentrances, match): + return self.read_partial(s, position, reentrances) + + def read_str_value(self, s, position, reentrances, match): + return read_str(s, position) + + def read_int_value(self, s, position, reentrances, match): + return int(match.group()), match.end() + + # Note: the '?' is included in the variable name. + def read_var_value(self, s, position, reentrances, match): + return Variable(match.group()), match.end() + + _SYM_CONSTS = {"None": None, "True": True, "False": False} + + def read_sym_value(self, s, position, reentrances, match): + val, end = match.group(), match.end() + return self._SYM_CONSTS.get(val, val), end + + def read_app_value(self, s, position, reentrances, match): + """Mainly included for backwards compat.""" + return self._logic_parser.parse("%s(%s)" % match.group(2, 3)), match.end() + + def read_logic_value(self, s, position, reentrances, match): + try: + try: + expr = self._logic_parser.parse(match.group(1)) + except LogicalExpressionException as e: + raise ValueError from e + return expr, match.end() + except ValueError as e: + raise ValueError("logic expression", match.start(1)) from e + + def read_tuple_value(self, s, position, reentrances, match): + return self._read_seq_value( + s, position, reentrances, match, ")", FeatureValueTuple, FeatureValueConcat + ) + + def read_set_value(self, s, position, reentrances, match): + return self._read_seq_value( + s, position, reentrances, match, "}", FeatureValueSet, FeatureValueUnion + ) + + def _read_seq_value( + self, s, position, reentrances, match, close_paren, seq_class, plus_class + ): + """ + Helper function used by read_tuple_value and read_set_value. + """ + cp = re.escape(close_paren) + position = match.end() + # Special syntax of empty tuples: + m = re.compile(r"\s*/?\s*%s" % cp).match(s, position) + if m: + return seq_class(), m.end() + # Read values: + values = [] + seen_plus = False + while True: + # Close paren: return value. + m = re.compile(r"\s*%s" % cp).match(s, position) + if m: + if seen_plus: + return plus_class(values), m.end() + else: + return seq_class(values), m.end() + + # Read the next value. + val, position = self.read_value(s, position, reentrances) + values.append(val) + + # Comma or looking at close paren + m = re.compile(r"\s*(,|\+|(?=%s))\s*" % cp).match(s, position) + if not m: + raise ValueError("',' or '+' or '%s'" % cp, position) + if m.group(1) == "+": + seen_plus = True + position = m.end() + + +###################################################################### +# { Demo +###################################################################### + + +def display_unification(fs1, fs2, indent=" "): + # Print the two input feature structures, side by side. + fs1_lines = ("%s" % fs1).split("\n") + fs2_lines = ("%s" % fs2).split("\n") + if len(fs1_lines) > len(fs2_lines): + blankline = "[" + " " * (len(fs2_lines[0]) - 2) + "]" + fs2_lines += [blankline] * len(fs1_lines) + else: + blankline = "[" + " " * (len(fs1_lines[0]) - 2) + "]" + fs1_lines += [blankline] * len(fs2_lines) + for (fs1_line, fs2_line) in zip(fs1_lines, fs2_lines): + print(indent + fs1_line + " " + fs2_line) + print(indent + "-" * len(fs1_lines[0]) + " " + "-" * len(fs2_lines[0])) + + linelen = len(fs1_lines[0]) * 2 + 3 + print(indent + "| |".center(linelen)) + print(indent + "+-----UNIFY-----+".center(linelen)) + print(indent + "|".center(linelen)) + print(indent + "V".center(linelen)) + + bindings = {} + + result = fs1.unify(fs2, bindings) + if result is None: + print(indent + "(FAILED)".center(linelen)) + else: + print( + "\n".join(indent + l.center(linelen) for l in ("%s" % result).split("\n")) + ) + if bindings and len(bindings.bound_variables()) > 0: + print(repr(bindings).center(linelen)) + return result + + +def interactive_demo(trace=False): + import random + import sys + + HELP = """ + 1-%d: Select the corresponding feature structure + q: Quit + t: Turn tracing on or off + l: List all feature structures + ?: Help + """ + + print( + """ + This demo will repeatedly present you with a list of feature + structures, and ask you to choose two for unification. Whenever a + new feature structure is generated, it is added to the list of + choices that you can pick from. However, since this can be a + large number of feature structures, the demo will only print out a + random subset for you to choose between at a given time. If you + want to see the complete lists, type "l". For a list of valid + commands, type "?". + """ + ) + print('Press "Enter" to continue...') + sys.stdin.readline() + + fstruct_strings = [ + "[agr=[number=sing, gender=masc]]", + "[agr=[gender=masc, person=3]]", + "[agr=[gender=fem, person=3]]", + "[subj=[agr=(1)[]], agr->(1)]", + "[obj=?x]", + "[subj=?x]", + "[/=None]", + "[/=NP]", + "[cat=NP]", + "[cat=VP]", + "[cat=PP]", + "[subj=[agr=[gender=?y]], obj=[agr=[gender=?y]]]", + "[gender=masc, agr=?C]", + "[gender=?S, agr=[gender=?S,person=3]]", + ] + + all_fstructs = [ + (i, FeatStruct(fstruct_strings[i])) for i in range(len(fstruct_strings)) + ] + + def list_fstructs(fstructs): + for i, fstruct in fstructs: + print() + lines = ("%s" % fstruct).split("\n") + print("%3d: %s" % (i + 1, lines[0])) + for line in lines[1:]: + print(" " + line) + print() + + while True: + # Pick 5 feature structures at random from the master list. + MAX_CHOICES = 5 + if len(all_fstructs) > MAX_CHOICES: + fstructs = sorted(random.sample(all_fstructs, MAX_CHOICES)) + else: + fstructs = all_fstructs + + print("_" * 75) + + print("Choose two feature structures to unify:") + list_fstructs(fstructs) + + selected = [None, None] + for (nth, i) in (("First", 0), ("Second", 1)): + while selected[i] is None: + print( + ( + "%s feature structure (1-%d,q,t,l,?): " + % (nth, len(all_fstructs)) + ), + end=" ", + ) + try: + input = sys.stdin.readline().strip() + if input in ("q", "Q", "x", "X"): + return + if input in ("t", "T"): + trace = not trace + print(" Trace = %s" % trace) + continue + if input in ("h", "H", "?"): + print(HELP % len(fstructs)) + continue + if input in ("l", "L"): + list_fstructs(all_fstructs) + continue + num = int(input) - 1 + selected[i] = all_fstructs[num][1] + print() + except: + print("Bad sentence number") + continue + + if trace: + result = selected[0].unify(selected[1], trace=1) + else: + result = display_unification(selected[0], selected[1]) + if result is not None: + for i, fstruct in all_fstructs: + if repr(result) == repr(fstruct): + break + else: + all_fstructs.append((len(all_fstructs), result)) + + print('\nType "Enter" to continue unifying; or "q" to quit.') + input = sys.stdin.readline().strip() + if input in ("q", "Q", "x", "X"): + return + + +def demo(trace=False): + """ + Just for testing + """ + # import random + + # processor breaks with values like '3rd' + fstruct_strings = [ + "[agr=[number=sing, gender=masc]]", + "[agr=[gender=masc, person=3]]", + "[agr=[gender=fem, person=3]]", + "[subj=[agr=(1)[]], agr->(1)]", + "[obj=?x]", + "[subj=?x]", + "[/=None]", + "[/=NP]", + "[cat=NP]", + "[cat=VP]", + "[cat=PP]", + "[subj=[agr=[gender=?y]], obj=[agr=[gender=?y]]]", + "[gender=masc, agr=?C]", + "[gender=?S, agr=[gender=?S,person=3]]", + ] + all_fstructs = [FeatStruct(fss) for fss in fstruct_strings] + # MAX_CHOICES = 5 + # if len(all_fstructs) > MAX_CHOICES: + # fstructs = random.sample(all_fstructs, MAX_CHOICES) + # fstructs.sort() + # else: + # fstructs = all_fstructs + + for fs1 in all_fstructs: + for fs2 in all_fstructs: + print( + "\n*******************\nfs1 is:\n%s\n\nfs2 is:\n%s\n\nresult is:\n%s" + % (fs1, fs2, unify(fs1, fs2)) + ) + + +if __name__ == "__main__": + demo() + +__all__ = [ + "FeatStruct", + "FeatDict", + "FeatList", + "unify", + "subsumes", + "conflicts", + "Feature", + "SlashFeature", + "RangeFeature", + "SLASH", + "TYPE", + "FeatStructReader", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/grammar.py b/.pythonlibs/lib/python3.10/site-packages/nltk/grammar.py new file mode 100644 index 0000000..6216692 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/grammar.py @@ -0,0 +1,1708 @@ +# Natural Language Toolkit: Context Free Grammars +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# Jason Narad +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT +# + +""" +Basic data classes for representing context free grammars. A +"grammar" specifies which trees can represent the structure of a +given text. Each of these trees is called a "parse tree" for the +text (or simply a "parse"). In a "context free" grammar, the set of +parse trees for any piece of a text can depend only on that piece, and +not on the rest of the text (i.e., the piece's context). Context free +grammars are often used to find possible syntactic structures for +sentences. In this context, the leaves of a parse tree are word +tokens; and the node values are phrasal categories, such as ``NP`` +and ``VP``. + +The ``CFG`` class is used to encode context free grammars. Each +``CFG`` consists of a start symbol and a set of productions. +The "start symbol" specifies the root node value for parse trees. For example, +the start symbol for syntactic parsing is usually ``S``. Start +symbols are encoded using the ``Nonterminal`` class, which is discussed +below. + +A Grammar's "productions" specify what parent-child relationships a parse +tree can contain. Each production specifies that a particular +node can be the parent of a particular set of children. For example, +the production `` -> `` specifies that an ``S`` node can +be the parent of an ``NP`` node and a ``VP`` node. + +Grammar productions are implemented by the ``Production`` class. +Each ``Production`` consists of a left hand side and a right hand +side. The "left hand side" is a ``Nonterminal`` that specifies the +node type for a potential parent; and the "right hand side" is a list +that specifies allowable children for that parent. This lists +consists of ``Nonterminals`` and text types: each ``Nonterminal`` +indicates that the corresponding child may be a ``TreeToken`` with the +specified node type; and each text type indicates that the +corresponding child may be a ``Token`` with the with that type. + +The ``Nonterminal`` class is used to distinguish node values from leaf +values. This prevents the grammar from accidentally using a leaf +value (such as the English word "A") as the node of a subtree. Within +a ``CFG``, all node values are wrapped in the ``Nonterminal`` +class. Note, however, that the trees that are specified by the grammar do +*not* include these ``Nonterminal`` wrappers. + +Grammars can also be given a more procedural interpretation. According to +this interpretation, a Grammar specifies any tree structure *tree* that +can be produced by the following procedure: + +| Set tree to the start symbol +| Repeat until tree contains no more nonterminal leaves: +| Choose a production prod with whose left hand side +| lhs is a nonterminal leaf of tree. +| Replace the nonterminal leaf with a subtree, whose node +| value is the value wrapped by the nonterminal lhs, and +| whose children are the right hand side of prod. + +The operation of replacing the left hand side (*lhs*) of a production +with the right hand side (*rhs*) in a tree (*tree*) is known as +"expanding" *lhs* to *rhs* in *tree*. +""" +import re +from functools import total_ordering + +from nltk.featstruct import SLASH, TYPE, FeatDict, FeatStruct, FeatStructReader +from nltk.internals import raise_unorderable_types +from nltk.probability import ImmutableProbabilisticMixIn +from nltk.util import invert_graph, transitive_closure + +################################################################# +# Nonterminal +################################################################# + + +@total_ordering +class Nonterminal: + """ + A non-terminal symbol for a context free grammar. ``Nonterminal`` + is a wrapper class for node values; it is used by ``Production`` + objects to distinguish node values from leaf values. + The node value that is wrapped by a ``Nonterminal`` is known as its + "symbol". Symbols are typically strings representing phrasal + categories (such as ``"NP"`` or ``"VP"``). However, more complex + symbol types are sometimes used (e.g., for lexicalized grammars). + Since symbols are node values, they must be immutable and + hashable. Two ``Nonterminals`` are considered equal if their + symbols are equal. + + :see: ``CFG``, ``Production`` + :type _symbol: any + :ivar _symbol: The node value corresponding to this + ``Nonterminal``. This value must be immutable and hashable. + """ + + def __init__(self, symbol): + """ + Construct a new non-terminal from the given symbol. + + :type symbol: any + :param symbol: The node value corresponding to this + ``Nonterminal``. This value must be immutable and + hashable. + """ + self._symbol = symbol + + def symbol(self): + """ + Return the node value corresponding to this ``Nonterminal``. + + :rtype: (any) + """ + return self._symbol + + def __eq__(self, other): + """ + Return True if this non-terminal is equal to ``other``. In + particular, return True if ``other`` is a ``Nonterminal`` + and this non-terminal's symbol is equal to ``other`` 's symbol. + + :rtype: bool + """ + return type(self) == type(other) and self._symbol == other._symbol + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, Nonterminal): + raise_unorderable_types("<", self, other) + return self._symbol < other._symbol + + def __hash__(self): + return hash(self._symbol) + + def __repr__(self): + """ + Return a string representation for this ``Nonterminal``. + + :rtype: str + """ + if isinstance(self._symbol, str): + return "%s" % self._symbol + else: + return "%s" % repr(self._symbol) + + def __str__(self): + """ + Return a string representation for this ``Nonterminal``. + + :rtype: str + """ + if isinstance(self._symbol, str): + return "%s" % self._symbol + else: + return "%s" % repr(self._symbol) + + def __div__(self, rhs): + """ + Return a new nonterminal whose symbol is ``A/B``, where ``A`` is + the symbol for this nonterminal, and ``B`` is the symbol for rhs. + + :param rhs: The nonterminal used to form the right hand side + of the new nonterminal. + :type rhs: Nonterminal + :rtype: Nonterminal + """ + return Nonterminal(f"{self._symbol}/{rhs._symbol}") + + def __truediv__(self, rhs): + """ + Return a new nonterminal whose symbol is ``A/B``, where ``A`` is + the symbol for this nonterminal, and ``B`` is the symbol for rhs. + This function allows use of the slash ``/`` operator with + the future import of division. + + :param rhs: The nonterminal used to form the right hand side + of the new nonterminal. + :type rhs: Nonterminal + :rtype: Nonterminal + """ + return self.__div__(rhs) + + +def nonterminals(symbols): + """ + Given a string containing a list of symbol names, return a list of + ``Nonterminals`` constructed from those symbols. + + :param symbols: The symbol name string. This string can be + delimited by either spaces or commas. + :type symbols: str + :return: A list of ``Nonterminals`` constructed from the symbol + names given in ``symbols``. The ``Nonterminals`` are sorted + in the same order as the symbols names. + :rtype: list(Nonterminal) + """ + if "," in symbols: + symbol_list = symbols.split(",") + else: + symbol_list = symbols.split() + return [Nonterminal(s.strip()) for s in symbol_list] + + +class FeatStructNonterminal(FeatDict, Nonterminal): + """A feature structure that's also a nonterminal. It acts as its + own symbol, and automatically freezes itself when hashed.""" + + def __hash__(self): + self.freeze() + return FeatStruct.__hash__(self) + + def symbol(self): + return self + + +def is_nonterminal(item): + """ + :return: True if the item is a ``Nonterminal``. + :rtype: bool + """ + return isinstance(item, Nonterminal) + + +################################################################# +# Terminals +################################################################# + + +def is_terminal(item): + """ + Return True if the item is a terminal, which currently is + if it is hashable and not a ``Nonterminal``. + + :rtype: bool + """ + return hasattr(item, "__hash__") and not isinstance(item, Nonterminal) + + +################################################################# +# Productions +################################################################# + + +@total_ordering +class Production: + """ + A grammar production. Each production maps a single symbol + on the "left-hand side" to a sequence of symbols on the + "right-hand side". (In the case of context-free productions, + the left-hand side must be a ``Nonterminal``, and the right-hand + side is a sequence of terminals and ``Nonterminals``.) + "terminals" can be any immutable hashable object that is + not a ``Nonterminal``. Typically, terminals are strings + representing words, such as ``"dog"`` or ``"under"``. + + :see: ``CFG`` + :see: ``DependencyGrammar`` + :see: ``Nonterminal`` + :type _lhs: Nonterminal + :ivar _lhs: The left-hand side of the production. + :type _rhs: tuple(Nonterminal, terminal) + :ivar _rhs: The right-hand side of the production. + """ + + def __init__(self, lhs, rhs): + """ + Construct a new ``Production``. + + :param lhs: The left-hand side of the new ``Production``. + :type lhs: Nonterminal + :param rhs: The right-hand side of the new ``Production``. + :type rhs: sequence(Nonterminal and terminal) + """ + if isinstance(rhs, str): + raise TypeError( + "production right hand side should be a list, " "not a string" + ) + self._lhs = lhs + self._rhs = tuple(rhs) + + def lhs(self): + """ + Return the left-hand side of this ``Production``. + + :rtype: Nonterminal + """ + return self._lhs + + def rhs(self): + """ + Return the right-hand side of this ``Production``. + + :rtype: sequence(Nonterminal and terminal) + """ + return self._rhs + + def __len__(self): + """ + Return the length of the right-hand side. + + :rtype: int + """ + return len(self._rhs) + + def is_nonlexical(self): + """ + Return True if the right-hand side only contains ``Nonterminals`` + + :rtype: bool + """ + return all(is_nonterminal(n) for n in self._rhs) + + def is_lexical(self): + """ + Return True if the right-hand contain at least one terminal token. + + :rtype: bool + """ + return not self.is_nonlexical() + + def __str__(self): + """ + Return a verbose string representation of the ``Production``. + + :rtype: str + """ + result = "%s -> " % repr(self._lhs) + result += " ".join(repr(el) for el in self._rhs) + return result + + def __repr__(self): + """ + Return a concise string representation of the ``Production``. + + :rtype: str + """ + return "%s" % self + + def __eq__(self, other): + """ + Return True if this ``Production`` is equal to ``other``. + + :rtype: bool + """ + return ( + type(self) == type(other) + and self._lhs == other._lhs + and self._rhs == other._rhs + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, Production): + raise_unorderable_types("<", self, other) + return (self._lhs, self._rhs) < (other._lhs, other._rhs) + + def __hash__(self): + """ + Return a hash value for the ``Production``. + + :rtype: int + """ + return hash((self._lhs, self._rhs)) + + +class DependencyProduction(Production): + """ + A dependency grammar production. Each production maps a single + head word to an unordered list of one or more modifier words. + """ + + def __str__(self): + """ + Return a verbose string representation of the ``DependencyProduction``. + + :rtype: str + """ + result = f"'{self._lhs}' ->" + for elt in self._rhs: + result += f" '{elt}'" + return result + + +class ProbabilisticProduction(Production, ImmutableProbabilisticMixIn): + """ + A probabilistic context free grammar production. + A PCFG ``ProbabilisticProduction`` is essentially just a ``Production`` that + has an associated probability, which represents how likely it is that + this production will be used. In particular, the probability of a + ``ProbabilisticProduction`` records the likelihood that its right-hand side is + the correct instantiation for any given occurrence of its left-hand side. + + :see: ``Production`` + """ + + def __init__(self, lhs, rhs, **prob): + """ + Construct a new ``ProbabilisticProduction``. + + :param lhs: The left-hand side of the new ``ProbabilisticProduction``. + :type lhs: Nonterminal + :param rhs: The right-hand side of the new ``ProbabilisticProduction``. + :type rhs: sequence(Nonterminal and terminal) + :param prob: Probability parameters of the new ``ProbabilisticProduction``. + """ + ImmutableProbabilisticMixIn.__init__(self, **prob) + Production.__init__(self, lhs, rhs) + + def __str__(self): + return super().__str__() + ( + " [1.0]" if (self.prob() == 1.0) else " [%g]" % self.prob() + ) + + def __eq__(self, other): + return ( + type(self) == type(other) + and self._lhs == other._lhs + and self._rhs == other._rhs + and self.prob() == other.prob() + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self._lhs, self._rhs, self.prob())) + + +################################################################# +# Grammars +################################################################# + + +class CFG: + """ + A context-free grammar. A grammar consists of a start state and + a set of productions. The set of terminals and nonterminals is + implicitly specified by the productions. + + If you need efficient key-based access to productions, you + can use a subclass to implement it. + """ + + def __init__(self, start, productions, calculate_leftcorners=True): + """ + Create a new context-free grammar, from the given start state + and set of ``Production`` instances. + + :param start: The start symbol + :type start: Nonterminal + :param productions: The list of productions that defines the grammar + :type productions: list(Production) + :param calculate_leftcorners: False if we don't want to calculate the + leftcorner relation. In that case, some optimized chart parsers won't work. + :type calculate_leftcorners: bool + """ + if not is_nonterminal(start): + raise TypeError( + "start should be a Nonterminal object," + " not a %s" % type(start).__name__ + ) + + self._start = start + self._productions = productions + self._categories = {prod.lhs() for prod in productions} + self._calculate_indexes() + self._calculate_grammar_forms() + if calculate_leftcorners: + self._calculate_leftcorners() + + def _calculate_indexes(self): + self._lhs_index = {} + self._rhs_index = {} + self._empty_index = {} + self._lexical_index = {} + for prod in self._productions: + # Left hand side. + lhs = prod._lhs + if lhs not in self._lhs_index: + self._lhs_index[lhs] = [] + self._lhs_index[lhs].append(prod) + if prod._rhs: + # First item in right hand side. + rhs0 = prod._rhs[0] + if rhs0 not in self._rhs_index: + self._rhs_index[rhs0] = [] + self._rhs_index[rhs0].append(prod) + else: + # The right hand side is empty. + self._empty_index[prod.lhs()] = prod + # Lexical tokens in the right hand side. + for token in prod._rhs: + if is_terminal(token): + self._lexical_index.setdefault(token, set()).add(prod) + + def _calculate_leftcorners(self): + # Calculate leftcorner relations, for use in optimized parsing. + self._immediate_leftcorner_categories = {cat: {cat} for cat in self._categories} + self._immediate_leftcorner_words = {cat: set() for cat in self._categories} + for prod in self.productions(): + if len(prod) > 0: + cat, left = prod.lhs(), prod.rhs()[0] + if is_nonterminal(left): + self._immediate_leftcorner_categories[cat].add(left) + else: + self._immediate_leftcorner_words[cat].add(left) + + lc = transitive_closure(self._immediate_leftcorner_categories, reflexive=True) + self._leftcorners = lc + self._leftcorner_parents = invert_graph(lc) + + nr_leftcorner_categories = sum( + map(len, self._immediate_leftcorner_categories.values()) + ) + nr_leftcorner_words = sum(map(len, self._immediate_leftcorner_words.values())) + if nr_leftcorner_words > nr_leftcorner_categories > 10000: + # If the grammar is big, the leftcorner-word dictionary will be too large. + # In that case it is better to calculate the relation on demand. + self._leftcorner_words = None + return + + self._leftcorner_words = {} + for cat in self._leftcorners: + lefts = self._leftcorners[cat] + lc = self._leftcorner_words[cat] = set() + for left in lefts: + lc.update(self._immediate_leftcorner_words.get(left, set())) + + @classmethod + def fromstring(cls, input, encoding=None): + """ + Return the grammar instance corresponding to the input string(s). + + :param input: a grammar, either in the form of a string or as a list of strings. + """ + start, productions = read_grammar( + input, standard_nonterm_parser, encoding=encoding + ) + return cls(start, productions) + + def start(self): + """ + Return the start symbol of the grammar + + :rtype: Nonterminal + """ + return self._start + + # tricky to balance readability and efficiency here! + # can't use set operations as they don't preserve ordering + def productions(self, lhs=None, rhs=None, empty=False): + """ + Return the grammar productions, filtered by the left-hand side + or the first item in the right-hand side. + + :param lhs: Only return productions with the given left-hand side. + :param rhs: Only return productions with the given first item + in the right-hand side. + :param empty: Only return productions with an empty right-hand side. + :return: A list of productions matching the given constraints. + :rtype: list(Production) + """ + if rhs and empty: + raise ValueError( + "You cannot select empty and non-empty " "productions at the same time." + ) + + # no constraints so return everything + if not lhs and not rhs: + if not empty: + return self._productions + else: + return self._empty_index.values() + + # only lhs specified so look up its index + elif lhs and not rhs: + if not empty: + return self._lhs_index.get(lhs, []) + elif lhs in self._empty_index: + return [self._empty_index[lhs]] + else: + return [] + + # only rhs specified so look up its index + elif rhs and not lhs: + return self._rhs_index.get(rhs, []) + + # intersect + else: + return [ + prod + for prod in self._lhs_index.get(lhs, []) + if prod in self._rhs_index.get(rhs, []) + ] + + def leftcorners(self, cat): + """ + Return the set of all nonterminals that the given nonterminal + can start with, including itself. + + This is the reflexive, transitive closure of the immediate + leftcorner relation: (A > B) iff (A -> B beta) + + :param cat: the parent of the leftcorners + :type cat: Nonterminal + :return: the set of all leftcorners + :rtype: set(Nonterminal) + """ + return self._leftcorners.get(cat, {cat}) + + def is_leftcorner(self, cat, left): + """ + True if left is a leftcorner of cat, where left can be a + terminal or a nonterminal. + + :param cat: the parent of the leftcorner + :type cat: Nonterminal + :param left: the suggested leftcorner + :type left: Terminal or Nonterminal + :rtype: bool + """ + if is_nonterminal(left): + return left in self.leftcorners(cat) + elif self._leftcorner_words: + return left in self._leftcorner_words.get(cat, set()) + else: + return any( + left in self._immediate_leftcorner_words.get(parent, set()) + for parent in self.leftcorners(cat) + ) + + def leftcorner_parents(self, cat): + """ + Return the set of all nonterminals for which the given category + is a left corner. This is the inverse of the leftcorner relation. + + :param cat: the suggested leftcorner + :type cat: Nonterminal + :return: the set of all parents to the leftcorner + :rtype: set(Nonterminal) + """ + return self._leftcorner_parents.get(cat, {cat}) + + def check_coverage(self, tokens): + """ + Check whether the grammar rules cover the given list of tokens. + If not, then raise an exception. + + :type tokens: list(str) + """ + missing = [tok for tok in tokens if not self._lexical_index.get(tok)] + if missing: + missing = ", ".join(f"{w!r}" for w in missing) + raise ValueError( + "Grammar does not cover some of the " "input words: %r." % missing + ) + + def _calculate_grammar_forms(self): + """ + Pre-calculate of which form(s) the grammar is. + """ + prods = self._productions + self._is_lexical = all(p.is_lexical() for p in prods) + self._is_nonlexical = all(p.is_nonlexical() for p in prods if len(p) != 1) + self._min_len = min(len(p) for p in prods) + self._max_len = max(len(p) for p in prods) + self._all_unary_are_lexical = all(p.is_lexical() for p in prods if len(p) == 1) + + def is_lexical(self): + """ + Return True if all productions are lexicalised. + """ + return self._is_lexical + + def is_nonlexical(self): + """ + Return True if all lexical rules are "preterminals", that is, + unary rules which can be separated in a preprocessing step. + + This means that all productions are of the forms + A -> B1 ... Bn (n>=0), or A -> "s". + + Note: is_lexical() and is_nonlexical() are not opposites. + There are grammars which are neither, and grammars which are both. + """ + return self._is_nonlexical + + def min_len(self): + """ + Return the right-hand side length of the shortest grammar production. + """ + return self._min_len + + def max_len(self): + """ + Return the right-hand side length of the longest grammar production. + """ + return self._max_len + + def is_nonempty(self): + """ + Return True if there are no empty productions. + """ + return self._min_len > 0 + + def is_binarised(self): + """ + Return True if all productions are at most binary. + Note that there can still be empty and unary productions. + """ + return self._max_len <= 2 + + def is_flexible_chomsky_normal_form(self): + """ + Return True if all productions are of the forms + A -> B C, A -> B, or A -> "s". + """ + return self.is_nonempty() and self.is_nonlexical() and self.is_binarised() + + def is_chomsky_normal_form(self): + """ + Return True if the grammar is of Chomsky Normal Form, i.e. all productions + are of the form A -> B C, or A -> "s". + """ + return self.is_flexible_chomsky_normal_form() and self._all_unary_are_lexical + + def chomsky_normal_form(self, new_token_padding="@$@", flexible=False): + """ + Returns a new Grammar that is in chomsky normal + + :param: new_token_padding + Customise new rule formation during binarisation + """ + if self.is_chomsky_normal_form(): + return self + if self.productions(empty=True): + raise ValueError( + "Grammar has Empty rules. " "Cannot deal with them at the moment" + ) + + # check for mixed rules + for rule in self.productions(): + if rule.is_lexical() and len(rule.rhs()) > 1: + raise ValueError( + f"Cannot handled mixed rule {rule.lhs()} => {rule.rhs()}" + ) + + step1 = CFG.eliminate_start(self) + step2 = CFG.binarize(step1, new_token_padding) + if flexible: + return step2 + step3 = CFG.remove_unitary_rules(step2) + step4 = CFG(step3.start(), list(set(step3.productions()))) + return step4 + + @classmethod + def remove_unitary_rules(cls, grammar): + """ + Remove nonlexical unitary rules and convert them to + lexical + """ + result = [] + unitary = [] + for rule in grammar.productions(): + if len(rule) == 1 and rule.is_nonlexical(): + unitary.append(rule) + else: + result.append(rule) + + while unitary: + rule = unitary.pop(0) + for item in grammar.productions(lhs=rule.rhs()[0]): + new_rule = Production(rule.lhs(), item.rhs()) + if len(new_rule) != 1 or new_rule.is_lexical(): + result.append(new_rule) + else: + unitary.append(new_rule) + + n_grammar = CFG(grammar.start(), result) + return n_grammar + + @classmethod + def binarize(cls, grammar, padding="@$@"): + """ + Convert all non-binary rules into binary by introducing + new tokens. + Example:: + + Original: + A => B C D + After Conversion: + A => B A@$@B + A@$@B => C D + """ + result = [] + + for rule in grammar.productions(): + if len(rule.rhs()) > 2: + # this rule needs to be broken down + left_side = rule.lhs() + for k in range(0, len(rule.rhs()) - 2): + tsym = rule.rhs()[k] + new_sym = Nonterminal(left_side.symbol() + padding + tsym.symbol()) + new_production = Production(left_side, (tsym, new_sym)) + left_side = new_sym + result.append(new_production) + last_prd = Production(left_side, rule.rhs()[-2:]) + result.append(last_prd) + else: + result.append(rule) + + n_grammar = CFG(grammar.start(), result) + return n_grammar + + @classmethod + def eliminate_start(cls, grammar): + """ + Eliminate start rule in case it appears on RHS + Example: S -> S0 S1 and S0 -> S1 S + Then another rule S0_Sigma -> S is added + """ + start = grammar.start() + result = [] + need_to_add = None + for rule in grammar.productions(): + if start in rule.rhs(): + need_to_add = True + result.append(rule) + if need_to_add: + start = Nonterminal("S0_SIGMA") + result.append(Production(start, [grammar.start()])) + n_grammar = CFG(start, result) + return n_grammar + return grammar + + def __repr__(self): + return "" % len(self._productions) + + def __str__(self): + result = "Grammar with %d productions" % len(self._productions) + result += " (start state = %r)" % self._start + for production in self._productions: + result += "\n %s" % production + return result + + +class FeatureGrammar(CFG): + """ + A feature-based grammar. This is equivalent to a + ``CFG`` whose nonterminals are all + ``FeatStructNonterminal``. + + A grammar consists of a start state and a set of + productions. The set of terminals and nonterminals + is implicitly specified by the productions. + """ + + def __init__(self, start, productions): + """ + Create a new feature-based grammar, from the given start + state and set of ``Productions``. + + :param start: The start symbol + :type start: FeatStructNonterminal + :param productions: The list of productions that defines the grammar + :type productions: list(Production) + """ + CFG.__init__(self, start, productions) + + # The difference with CFG is that the productions are + # indexed on the TYPE feature of the nonterminals. + # This is calculated by the method _get_type_if_possible(). + + def _calculate_indexes(self): + self._lhs_index = {} + self._rhs_index = {} + self._empty_index = {} + self._empty_productions = [] + self._lexical_index = {} + for prod in self._productions: + # Left hand side. + lhs = self._get_type_if_possible(prod._lhs) + if lhs not in self._lhs_index: + self._lhs_index[lhs] = [] + self._lhs_index[lhs].append(prod) + if prod._rhs: + # First item in right hand side. + rhs0 = self._get_type_if_possible(prod._rhs[0]) + if rhs0 not in self._rhs_index: + self._rhs_index[rhs0] = [] + self._rhs_index[rhs0].append(prod) + else: + # The right hand side is empty. + if lhs not in self._empty_index: + self._empty_index[lhs] = [] + self._empty_index[lhs].append(prod) + self._empty_productions.append(prod) + # Lexical tokens in the right hand side. + for token in prod._rhs: + if is_terminal(token): + self._lexical_index.setdefault(token, set()).add(prod) + + @classmethod + def fromstring( + cls, input, features=None, logic_parser=None, fstruct_reader=None, encoding=None + ): + """ + Return a feature structure based grammar. + + :param input: a grammar, either in the form of a string or else + as a list of strings. + :param features: a tuple of features (default: SLASH, TYPE) + :param logic_parser: a parser for lambda-expressions, + by default, ``LogicParser()`` + :param fstruct_reader: a feature structure parser + (only if features and logic_parser is None) + """ + if features is None: + features = (SLASH, TYPE) + + if fstruct_reader is None: + fstruct_reader = FeatStructReader( + features, FeatStructNonterminal, logic_parser=logic_parser + ) + elif logic_parser is not None: + raise Exception( + "'logic_parser' and 'fstruct_reader' must " "not both be set" + ) + + start, productions = read_grammar( + input, fstruct_reader.read_partial, encoding=encoding + ) + return cls(start, productions) + + def productions(self, lhs=None, rhs=None, empty=False): + """ + Return the grammar productions, filtered by the left-hand side + or the first item in the right-hand side. + + :param lhs: Only return productions with the given left-hand side. + :param rhs: Only return productions with the given first item + in the right-hand side. + :param empty: Only return productions with an empty right-hand side. + :rtype: list(Production) + """ + if rhs and empty: + raise ValueError( + "You cannot select empty and non-empty " "productions at the same time." + ) + + # no constraints so return everything + if not lhs and not rhs: + if empty: + return self._empty_productions + else: + return self._productions + + # only lhs specified so look up its index + elif lhs and not rhs: + if empty: + return self._empty_index.get(self._get_type_if_possible(lhs), []) + else: + return self._lhs_index.get(self._get_type_if_possible(lhs), []) + + # only rhs specified so look up its index + elif rhs and not lhs: + return self._rhs_index.get(self._get_type_if_possible(rhs), []) + + # intersect + else: + return [ + prod + for prod in self._lhs_index.get(self._get_type_if_possible(lhs), []) + if prod in self._rhs_index.get(self._get_type_if_possible(rhs), []) + ] + + def leftcorners(self, cat): + """ + Return the set of all words that the given category can start with. + Also called the "first set" in compiler construction. + """ + raise NotImplementedError("Not implemented yet") + + def leftcorner_parents(self, cat): + """ + Return the set of all categories for which the given category + is a left corner. + """ + raise NotImplementedError("Not implemented yet") + + def _get_type_if_possible(self, item): + """ + Helper function which returns the ``TYPE`` feature of the ``item``, + if it exists, otherwise it returns the ``item`` itself + """ + if isinstance(item, dict) and TYPE in item: + return FeatureValueType(item[TYPE]) + else: + return item + + +@total_ordering +class FeatureValueType: + """ + A helper class for ``FeatureGrammars``, designed to be different + from ordinary strings. This is to stop the ``FeatStruct`` + ``FOO[]`` from being compare equal to the terminal "FOO". + """ + + def __init__(self, value): + self._value = value + + def __repr__(self): + return "<%s>" % self._value + + def __eq__(self, other): + return type(self) == type(other) and self._value == other._value + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, FeatureValueType): + raise_unorderable_types("<", self, other) + return self._value < other._value + + def __hash__(self): + return hash(self._value) + + +class DependencyGrammar: + """ + A dependency grammar. A DependencyGrammar consists of a set of + productions. Each production specifies a head/modifier relationship + between a pair of words. + """ + + def __init__(self, productions): + """ + Create a new dependency grammar, from the set of ``Productions``. + + :param productions: The list of productions that defines the grammar + :type productions: list(Production) + """ + self._productions = productions + + @classmethod + def fromstring(cls, input): + productions = [] + for linenum, line in enumerate(input.split("\n")): + line = line.strip() + if line.startswith("#") or line == "": + continue + try: + productions += _read_dependency_production(line) + except ValueError as e: + raise ValueError(f"Unable to parse line {linenum}: {line}") from e + if len(productions) == 0: + raise ValueError("No productions found!") + return cls(productions) + + def contains(self, head, mod): + """ + :param head: A head word. + :type head: str + :param mod: A mod word, to test as a modifier of 'head'. + :type mod: str + + :return: true if this ``DependencyGrammar`` contains a + ``DependencyProduction`` mapping 'head' to 'mod'. + :rtype: bool + """ + for production in self._productions: + for possibleMod in production._rhs: + if production._lhs == head and possibleMod == mod: + return True + return False + + def __contains__(self, head_mod): + """ + Return True if this ``DependencyGrammar`` contains a + ``DependencyProduction`` mapping 'head' to 'mod'. + + :param head_mod: A tuple of a head word and a mod word, + to test as a modifier of 'head'. + :type head: Tuple[str, str] + :rtype: bool + """ + try: + head, mod = head_mod + except ValueError as e: + raise ValueError( + "Must use a tuple of strings, e.g. `('price', 'of') in grammar`" + ) from e + return self.contains(head, mod) + + # # should be rewritten, the set comp won't work in all comparisons + # def contains_exactly(self, head, modlist): + # for production in self._productions: + # if(len(production._rhs) == len(modlist)): + # if(production._lhs == head): + # set1 = Set(production._rhs) + # set2 = Set(modlist) + # if(set1 == set2): + # return True + # return False + + def __str__(self): + """ + Return a verbose string representation of the ``DependencyGrammar`` + + :rtype: str + """ + str = "Dependency grammar with %d productions" % len(self._productions) + for production in self._productions: + str += "\n %s" % production + return str + + def __repr__(self): + """ + Return a concise string representation of the ``DependencyGrammar`` + """ + return "Dependency grammar with %d productions" % len(self._productions) + + +class ProbabilisticDependencyGrammar: + """ """ + + def __init__(self, productions, events, tags): + self._productions = productions + self._events = events + self._tags = tags + + def contains(self, head, mod): + """ + Return True if this ``DependencyGrammar`` contains a + ``DependencyProduction`` mapping 'head' to 'mod'. + + :param head: A head word. + :type head: str + :param mod: A mod word, to test as a modifier of 'head'. + :type mod: str + :rtype: bool + """ + for production in self._productions: + for possibleMod in production._rhs: + if production._lhs == head and possibleMod == mod: + return True + return False + + def __str__(self): + """ + Return a verbose string representation of the ``ProbabilisticDependencyGrammar`` + + :rtype: str + """ + str = "Statistical dependency grammar with %d productions" % len( + self._productions + ) + for production in self._productions: + str += "\n %s" % production + str += "\nEvents:" + for event in self._events: + str += "\n %d:%s" % (self._events[event], event) + str += "\nTags:" + for tag_word in self._tags: + str += f"\n {tag_word}:\t({self._tags[tag_word]})" + return str + + def __repr__(self): + """ + Return a concise string representation of the ``ProbabilisticDependencyGrammar`` + """ + return "Statistical Dependency grammar with %d productions" % len( + self._productions + ) + + +class PCFG(CFG): + """ + A probabilistic context-free grammar. A PCFG consists of a + start state and a set of productions with probabilities. The set of + terminals and nonterminals is implicitly specified by the productions. + + PCFG productions use the ``ProbabilisticProduction`` class. + ``PCFGs`` impose the constraint that the set of productions with + any given left-hand-side must have probabilities that sum to 1 + (allowing for a small margin of error). + + If you need efficient key-based access to productions, you can use + a subclass to implement it. + + :type EPSILON: float + :cvar EPSILON: The acceptable margin of error for checking that + productions with a given left-hand side have probabilities + that sum to 1. + """ + + EPSILON = 0.01 + + def __init__(self, start, productions, calculate_leftcorners=True): + """ + Create a new context-free grammar, from the given start state + and set of ``ProbabilisticProductions``. + + :param start: The start symbol + :type start: Nonterminal + :param productions: The list of productions that defines the grammar + :type productions: list(Production) + :raise ValueError: if the set of productions with any left-hand-side + do not have probabilities that sum to a value within + EPSILON of 1. + :param calculate_leftcorners: False if we don't want to calculate the + leftcorner relation. In that case, some optimized chart parsers won't work. + :type calculate_leftcorners: bool + """ + CFG.__init__(self, start, productions, calculate_leftcorners) + + # Make sure that the probabilities sum to one. + probs = {} + for production in productions: + probs[production.lhs()] = probs.get(production.lhs(), 0) + production.prob() + for (lhs, p) in probs.items(): + if not ((1 - PCFG.EPSILON) < p < (1 + PCFG.EPSILON)): + raise ValueError("Productions for %r do not sum to 1" % lhs) + + @classmethod + def fromstring(cls, input, encoding=None): + """ + Return a probabilistic context-free grammar corresponding to the + input string(s). + + :param input: a grammar, either in the form of a string or else + as a list of strings. + """ + start, productions = read_grammar( + input, standard_nonterm_parser, probabilistic=True, encoding=encoding + ) + return cls(start, productions) + + +################################################################# +# Inducing Grammars +################################################################# + +# Contributed by Nathan Bodenstab + + +def induce_pcfg(start, productions): + r""" + Induce a PCFG grammar from a list of productions. + + The probability of a production A -> B C in a PCFG is: + + | count(A -> B C) + | P(B, C | A) = --------------- where \* is any right hand side + | count(A -> \*) + + :param start: The start symbol + :type start: Nonterminal + :param productions: The list of productions that defines the grammar + :type productions: list(Production) + """ + # Production count: the number of times a given production occurs + pcount = {} + + # LHS-count: counts the number of times a given lhs occurs + lcount = {} + + for prod in productions: + lcount[prod.lhs()] = lcount.get(prod.lhs(), 0) + 1 + pcount[prod] = pcount.get(prod, 0) + 1 + + prods = [ + ProbabilisticProduction(p.lhs(), p.rhs(), prob=pcount[p] / lcount[p.lhs()]) + for p in pcount + ] + return PCFG(start, prods) + + +################################################################# +# Helper functions for reading productions +################################################################# + + +def _read_cfg_production(input): + """ + Return a list of context-free ``Productions``. + """ + return _read_production(input, standard_nonterm_parser) + + +def _read_pcfg_production(input): + """ + Return a list of PCFG ``ProbabilisticProductions``. + """ + return _read_production(input, standard_nonterm_parser, probabilistic=True) + + +def _read_fcfg_production(input, fstruct_reader): + """ + Return a list of feature-based ``Productions``. + """ + return _read_production(input, fstruct_reader) + + +# Parsing generic grammars + +_ARROW_RE = re.compile(r"\s* -> \s*", re.VERBOSE) +_PROBABILITY_RE = re.compile(r"( \[ [\d\.]+ \] ) \s*", re.VERBOSE) +_TERMINAL_RE = re.compile(r'( "[^"]*" | \'[^\']*\' ) \s*', re.VERBOSE) +_DISJUNCTION_RE = re.compile(r"\| \s*", re.VERBOSE) + + +def _read_production(line, nonterm_parser, probabilistic=False): + """ + Parse a grammar rule, given as a string, and return + a list of productions. + """ + pos = 0 + + # Parse the left-hand side. + lhs, pos = nonterm_parser(line, pos) + + # Skip over the arrow. + m = _ARROW_RE.match(line, pos) + if not m: + raise ValueError("Expected an arrow") + pos = m.end() + + # Parse the right hand side. + probabilities = [0.0] + rhsides = [[]] + while pos < len(line): + # Probability. + m = _PROBABILITY_RE.match(line, pos) + if probabilistic and m: + pos = m.end() + probabilities[-1] = float(m.group(1)[1:-1]) + if probabilities[-1] > 1.0: + raise ValueError( + "Production probability %f, " + "should not be greater than 1.0" % (probabilities[-1],) + ) + + # String -- add terminal. + elif line[pos] in "'\"": + m = _TERMINAL_RE.match(line, pos) + if not m: + raise ValueError("Unterminated string") + rhsides[-1].append(m.group(1)[1:-1]) + pos = m.end() + + # Vertical bar -- start new rhside. + elif line[pos] == "|": + m = _DISJUNCTION_RE.match(line, pos) + probabilities.append(0.0) + rhsides.append([]) + pos = m.end() + + # Anything else -- nonterminal. + else: + nonterm, pos = nonterm_parser(line, pos) + rhsides[-1].append(nonterm) + + if probabilistic: + return [ + ProbabilisticProduction(lhs, rhs, prob=probability) + for (rhs, probability) in zip(rhsides, probabilities) + ] + else: + return [Production(lhs, rhs) for rhs in rhsides] + + +################################################################# +# Reading Phrase Structure Grammars +################################################################# + + +def read_grammar(input, nonterm_parser, probabilistic=False, encoding=None): + """ + Return a pair consisting of a starting category and a list of + ``Productions``. + + :param input: a grammar, either in the form of a string or else + as a list of strings. + :param nonterm_parser: a function for parsing nonterminals. + It should take a ``(string, position)`` as argument and + return a ``(nonterminal, position)`` as result. + :param probabilistic: are the grammar rules probabilistic? + :type probabilistic: bool + :param encoding: the encoding of the grammar, if it is a binary string + :type encoding: str + """ + if encoding is not None: + input = input.decode(encoding) + if isinstance(input, str): + lines = input.split("\n") + else: + lines = input + + start = None + productions = [] + continue_line = "" + for linenum, line in enumerate(lines): + line = continue_line + line.strip() + if line.startswith("#") or line == "": + continue + if line.endswith("\\"): + continue_line = line[:-1].rstrip() + " " + continue + continue_line = "" + try: + if line[0] == "%": + directive, args = line[1:].split(None, 1) + if directive == "start": + start, pos = nonterm_parser(args, 0) + if pos != len(args): + raise ValueError("Bad argument to start directive") + else: + raise ValueError("Bad directive") + else: + # expand out the disjunctions on the RHS + productions += _read_production(line, nonterm_parser, probabilistic) + except ValueError as e: + raise ValueError(f"Unable to parse line {linenum + 1}: {line}\n{e}") from e + + if not productions: + raise ValueError("No productions found!") + if not start: + start = productions[0].lhs() + return (start, productions) + + +_STANDARD_NONTERM_RE = re.compile(r"( [\w/][\w/^<>-]* ) \s*", re.VERBOSE) + + +def standard_nonterm_parser(string, pos): + m = _STANDARD_NONTERM_RE.match(string, pos) + if not m: + raise ValueError("Expected a nonterminal, found: " + string[pos:]) + return (Nonterminal(m.group(1)), m.end()) + + +################################################################# +# Reading Dependency Grammars +################################################################# + +_READ_DG_RE = re.compile( + r"""^\s* # leading whitespace + ('[^']+')\s* # single-quoted lhs + (?:[-=]+>)\s* # arrow + (?:( # rhs: + "[^"]+" # doubled-quoted terminal + | '[^']+' # single-quoted terminal + | \| # disjunction + ) + \s*) # trailing space + *$""", # zero or more copies + re.VERBOSE, +) +_SPLIT_DG_RE = re.compile(r"""('[^']'|[-=]+>|"[^"]+"|'[^']+'|\|)""") + + +def _read_dependency_production(s): + if not _READ_DG_RE.match(s): + raise ValueError("Bad production string") + pieces = _SPLIT_DG_RE.split(s) + pieces = [p for i, p in enumerate(pieces) if i % 2 == 1] + lhside = pieces[0].strip("'\"") + rhsides = [[]] + for piece in pieces[2:]: + if piece == "|": + rhsides.append([]) + else: + rhsides[-1].append(piece.strip("'\"")) + return [DependencyProduction(lhside, rhside) for rhside in rhsides] + + +################################################################# +# Demonstration +################################################################# + + +def cfg_demo(): + """ + A demonstration showing how ``CFGs`` can be created and used. + """ + + from nltk import CFG, Production, nonterminals + + # Create some nonterminals + S, NP, VP, PP = nonterminals("S, NP, VP, PP") + N, V, P, Det = nonterminals("N, V, P, Det") + VP_slash_NP = VP / NP + + print("Some nonterminals:", [S, NP, VP, PP, N, V, P, Det, VP / NP]) + print(" S.symbol() =>", repr(S.symbol())) + print() + + print(Production(S, [NP])) + + # Create some Grammar Productions + grammar = CFG.fromstring( + """ + S -> NP VP + PP -> P NP + NP -> Det N | NP PP + VP -> V NP | VP PP + Det -> 'a' | 'the' + N -> 'dog' | 'cat' + V -> 'chased' | 'sat' + P -> 'on' | 'in' + """ + ) + + print("A Grammar:", repr(grammar)) + print(" grammar.start() =>", repr(grammar.start())) + print(" grammar.productions() =>", end=" ") + # Use string.replace(...) is to line-wrap the output. + print(repr(grammar.productions()).replace(",", ",\n" + " " * 25)) + print() + + +def pcfg_demo(): + """ + A demonstration showing how a ``PCFG`` can be created and used. + """ + + from nltk import induce_pcfg, treetransforms + from nltk.corpus import treebank + from nltk.parse import pchart + + toy_pcfg1 = PCFG.fromstring( + """ + S -> NP VP [1.0] + NP -> Det N [0.5] | NP PP [0.25] | 'John' [0.1] | 'I' [0.15] + Det -> 'the' [0.8] | 'my' [0.2] + N -> 'man' [0.5] | 'telescope' [0.5] + VP -> VP PP [0.1] | V NP [0.7] | V [0.2] + V -> 'ate' [0.35] | 'saw' [0.65] + PP -> P NP [1.0] + P -> 'with' [0.61] | 'under' [0.39] + """ + ) + + toy_pcfg2 = PCFG.fromstring( + """ + S -> NP VP [1.0] + VP -> V NP [.59] + VP -> V [.40] + VP -> VP PP [.01] + NP -> Det N [.41] + NP -> Name [.28] + NP -> NP PP [.31] + PP -> P NP [1.0] + V -> 'saw' [.21] + V -> 'ate' [.51] + V -> 'ran' [.28] + N -> 'boy' [.11] + N -> 'cookie' [.12] + N -> 'table' [.13] + N -> 'telescope' [.14] + N -> 'hill' [.5] + Name -> 'Jack' [.52] + Name -> 'Bob' [.48] + P -> 'with' [.61] + P -> 'under' [.39] + Det -> 'the' [.41] + Det -> 'a' [.31] + Det -> 'my' [.28] + """ + ) + + pcfg_prods = toy_pcfg1.productions() + + pcfg_prod = pcfg_prods[2] + print("A PCFG production:", repr(pcfg_prod)) + print(" pcfg_prod.lhs() =>", repr(pcfg_prod.lhs())) + print(" pcfg_prod.rhs() =>", repr(pcfg_prod.rhs())) + print(" pcfg_prod.prob() =>", repr(pcfg_prod.prob())) + print() + + grammar = toy_pcfg2 + print("A PCFG grammar:", repr(grammar)) + print(" grammar.start() =>", repr(grammar.start())) + print(" grammar.productions() =>", end=" ") + # Use .replace(...) is to line-wrap the output. + print(repr(grammar.productions()).replace(",", ",\n" + " " * 26)) + print() + + # extract productions from three trees and induce the PCFG + print("Induce PCFG grammar from treebank data:") + + productions = [] + item = treebank._fileids[0] + for tree in treebank.parsed_sents(item)[:3]: + # perform optional tree transformations, e.g.: + tree.collapse_unary(collapsePOS=False) + tree.chomsky_normal_form(horzMarkov=2) + + productions += tree.productions() + + S = Nonterminal("S") + grammar = induce_pcfg(S, productions) + print(grammar) + print() + + print("Parse sentence using induced grammar:") + + parser = pchart.InsideChartParser(grammar) + parser.trace(3) + + # doesn't work as tokens are different: + # sent = treebank.tokenized('wsj_0001.mrg')[0] + + sent = treebank.parsed_sents(item)[0].leaves() + print(sent) + for parse in parser.parse(sent): + print(parse) + + +def fcfg_demo(): + import nltk.data + + g = nltk.data.load("grammars/book_grammars/feat0.fcfg") + print(g) + print() + + +def dg_demo(): + """ + A demonstration showing the creation and inspection of a + ``DependencyGrammar``. + """ + grammar = DependencyGrammar.fromstring( + """ + 'scratch' -> 'cats' | 'walls' + 'walls' -> 'the' + 'cats' -> 'the' + """ + ) + print(grammar) + + +def sdg_demo(): + """ + A demonstration of how to read a string representation of + a CoNLL format dependency tree. + """ + from nltk.parse import DependencyGraph + + dg = DependencyGraph( + """ + 1 Ze ze Pron Pron per|3|evofmv|nom 2 su _ _ + 2 had heb V V trans|ovt|1of2of3|ev 0 ROOT _ _ + 3 met met Prep Prep voor 8 mod _ _ + 4 haar haar Pron Pron bez|3|ev|neut|attr 5 det _ _ + 5 moeder moeder N N soort|ev|neut 3 obj1 _ _ + 6 kunnen kan V V hulp|ott|1of2of3|mv 2 vc _ _ + 7 gaan ga V V hulp|inf 6 vc _ _ + 8 winkelen winkel V V intrans|inf 11 cnj _ _ + 9 , , Punc Punc komma 8 punct _ _ + 10 zwemmen zwem V V intrans|inf 11 cnj _ _ + 11 of of Conj Conj neven 7 vc _ _ + 12 terrassen terras N N soort|mv|neut 11 cnj _ _ + 13 . . Punc Punc punt 12 punct _ _ + """ + ) + tree = dg.tree() + print(tree.pprint()) + + +def demo(): + cfg_demo() + pcfg_demo() + fcfg_demo() + dg_demo() + sdg_demo() + + +if __name__ == "__main__": + demo() + +__all__ = [ + "Nonterminal", + "nonterminals", + "CFG", + "Production", + "PCFG", + "ProbabilisticProduction", + "DependencyGrammar", + "DependencyProduction", + "ProbabilisticDependencyGrammar", + "induce_pcfg", + "read_grammar", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/help.py b/.pythonlibs/lib/python3.10/site-packages/nltk/help.py new file mode 100644 index 0000000..3205f5a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/help.py @@ -0,0 +1,64 @@ +# Natural Language Toolkit (NLTK) Help +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Provide structured access to documentation. +""" + +import re +from textwrap import wrap + +from nltk.data import load + + +def brown_tagset(tagpattern=None): + _format_tagset("brown_tagset", tagpattern) + + +def claws5_tagset(tagpattern=None): + _format_tagset("claws5_tagset", tagpattern) + + +def upenn_tagset(tagpattern=None): + _format_tagset("upenn_tagset", tagpattern) + + +##################################################################### +# UTILITIES +##################################################################### + + +def _print_entries(tags, tagdict): + for tag in tags: + entry = tagdict[tag] + defn = [tag + ": " + entry[0]] + examples = wrap( + entry[1], width=75, initial_indent=" ", subsequent_indent=" " + ) + print("\n".join(defn + examples)) + + +def _format_tagset(tagset, tagpattern=None): + tagdict = load("help/tagsets/" + tagset + ".pickle") + if not tagpattern: + _print_entries(sorted(tagdict), tagdict) + elif tagpattern in tagdict: + _print_entries([tagpattern], tagdict) + else: + tagpattern = re.compile(tagpattern) + tags = [tag for tag in sorted(tagdict) if tagpattern.match(tag)] + if tags: + _print_entries(tags, tagdict) + else: + print("No matching tags found.") + + +if __name__ == "__main__": + brown_tagset(r"NN.*") + upenn_tagset(r".*\$") + claws5_tagset("UNDEFINED") + brown_tagset(r"NN") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__init__.py new file mode 100644 index 0000000..d002a01 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__init__.py @@ -0,0 +1,24 @@ +# Natural Language Toolkit: Inference +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Dan Garrette +# Ewan Klein +# +# URL: +# For license information, see LICENSE.TXT + +""" +Classes and interfaces for theorem proving and model building. +""" + +from nltk.inference.api import ParallelProverBuilder, ParallelProverBuilderCommand +from nltk.inference.discourse import ( + CfgReadingCommand, + DiscourseTester, + DrtGlueReadingCommand, + ReadingCommand, +) +from nltk.inference.mace import Mace, MaceCommand +from nltk.inference.prover9 import Prover9, Prover9Command +from nltk.inference.resolution import ResolutionProver, ResolutionProverCommand +from nltk.inference.tableau import TableauProver, TableauProverCommand diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d8ca4df Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..287d955 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/discourse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/discourse.cpython-310.pyc new file mode 100644 index 0000000..1adc329 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/discourse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/mace.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/mace.cpython-310.pyc new file mode 100644 index 0000000..f2f673a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/mace.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/nonmonotonic.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/nonmonotonic.cpython-310.pyc new file mode 100644 index 0000000..be57f49 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/nonmonotonic.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/prover9.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/prover9.cpython-310.pyc new file mode 100644 index 0000000..fb23abc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/prover9.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/resolution.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/resolution.cpython-310.pyc new file mode 100644 index 0000000..1cc99b6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/resolution.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/tableau.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/tableau.cpython-310.pyc new file mode 100644 index 0000000..59ab4b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/__pycache__/tableau.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/api.py new file mode 100644 index 0000000..93c513f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/api.py @@ -0,0 +1,614 @@ +# Natural Language Toolkit: Classifier Interface +# +# Author: Ewan Klein +# Dan Garrette +# +# URL: +# For license information, see LICENSE.TXT + +""" +Interfaces and base classes for theorem provers and model builders. + +``Prover`` is a standard interface for a theorem prover which tries to prove a goal from a +list of assumptions. + +``ModelBuilder`` is a standard interface for a model builder. Given just a set of assumptions. +the model builder tries to build a model for the assumptions. Given a set of assumptions and a +goal *G*, the model builder tries to find a counter-model, in the sense of a model that will satisfy +the assumptions plus the negation of *G*. +""" + +import threading +import time +from abc import ABCMeta, abstractmethod + + +class Prover(metaclass=ABCMeta): + """ + Interface for trying to prove a goal from assumptions. Both the goal and + the assumptions are constrained to be formulas of ``logic.Expression``. + """ + + def prove(self, goal=None, assumptions=None, verbose=False): + """ + :return: Whether the proof was successful or not. + :rtype: bool + """ + return self._prove(goal, assumptions, verbose)[0] + + @abstractmethod + def _prove(self, goal=None, assumptions=None, verbose=False): + """ + :return: Whether the proof was successful or not, along with the proof + :rtype: tuple: (bool, str) + """ + + +class ModelBuilder(metaclass=ABCMeta): + """ + Interface for trying to build a model of set of formulas. + Open formulas are assumed to be universally quantified. + Both the goal and the assumptions are constrained to be formulas + of ``logic.Expression``. + """ + + def build_model(self, goal=None, assumptions=None, verbose=False): + """ + Perform the actual model building. + :return: Whether a model was generated + :rtype: bool + """ + return self._build_model(goal, assumptions, verbose)[0] + + @abstractmethod + def _build_model(self, goal=None, assumptions=None, verbose=False): + """ + Perform the actual model building. + :return: Whether a model was generated, and the model itself + :rtype: tuple(bool, sem.Valuation) + """ + + +class TheoremToolCommand(metaclass=ABCMeta): + """ + This class holds a goal and a list of assumptions to be used in proving + or model building. + """ + + @abstractmethod + def add_assumptions(self, new_assumptions): + """ + Add new assumptions to the assumption list. + + :param new_assumptions: new assumptions + :type new_assumptions: list(sem.Expression) + """ + + @abstractmethod + def retract_assumptions(self, retracted, debug=False): + """ + Retract assumptions from the assumption list. + + :param debug: If True, give warning when ``retracted`` is not present on + assumptions list. + :type debug: bool + :param retracted: assumptions to be retracted + :type retracted: list(sem.Expression) + """ + + @abstractmethod + def assumptions(self): + """ + List the current assumptions. + + :return: list of ``Expression`` + """ + + @abstractmethod + def goal(self): + """ + Return the goal + + :return: ``Expression`` + """ + + @abstractmethod + def print_assumptions(self): + """ + Print the list of the current assumptions. + """ + + +class ProverCommand(TheoremToolCommand): + """ + This class holds a ``Prover``, a goal, and a list of assumptions. When + prove() is called, the ``Prover`` is executed with the goal and assumptions. + """ + + @abstractmethod + def prove(self, verbose=False): + """ + Perform the actual proof. + """ + + @abstractmethod + def proof(self, simplify=True): + """ + Return the proof string + :param simplify: bool simplify the proof? + :return: str + """ + + @abstractmethod + def get_prover(self): + """ + Return the prover object + :return: ``Prover`` + """ + + +class ModelBuilderCommand(TheoremToolCommand): + """ + This class holds a ``ModelBuilder``, a goal, and a list of assumptions. + When build_model() is called, the ``ModelBuilder`` is executed with the goal + and assumptions. + """ + + @abstractmethod + def build_model(self, verbose=False): + """ + Perform the actual model building. + :return: A model if one is generated; None otherwise. + :rtype: sem.Valuation + """ + + @abstractmethod + def model(self, format=None): + """ + Return a string representation of the model + + :param simplify: bool simplify the proof? + :return: str + """ + + @abstractmethod + def get_model_builder(self): + """ + Return the model builder object + :return: ``ModelBuilder`` + """ + + +class BaseTheoremToolCommand(TheoremToolCommand): + """ + This class holds a goal and a list of assumptions to be used in proving + or model building. + """ + + def __init__(self, goal=None, assumptions=None): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in + the proof. + :type assumptions: list(sem.Expression) + """ + self._goal = goal + + if not assumptions: + self._assumptions = [] + else: + self._assumptions = list(assumptions) + + self._result = None + """A holder for the result, to prevent unnecessary re-proving""" + + def add_assumptions(self, new_assumptions): + """ + Add new assumptions to the assumption list. + + :param new_assumptions: new assumptions + :type new_assumptions: list(sem.Expression) + """ + self._assumptions.extend(new_assumptions) + self._result = None + + def retract_assumptions(self, retracted, debug=False): + """ + Retract assumptions from the assumption list. + + :param debug: If True, give warning when ``retracted`` is not present on + assumptions list. + :type debug: bool + :param retracted: assumptions to be retracted + :type retracted: list(sem.Expression) + """ + retracted = set(retracted) + result_list = list(filter(lambda a: a not in retracted, self._assumptions)) + if debug and result_list == self._assumptions: + print(Warning("Assumptions list has not been changed:")) + self.print_assumptions() + + self._assumptions = result_list + + self._result = None + + def assumptions(self): + """ + List the current assumptions. + + :return: list of ``Expression`` + """ + return self._assumptions + + def goal(self): + """ + Return the goal + + :return: ``Expression`` + """ + return self._goal + + def print_assumptions(self): + """ + Print the list of the current assumptions. + """ + for a in self.assumptions(): + print(a) + + +class BaseProverCommand(BaseTheoremToolCommand, ProverCommand): + """ + This class holds a ``Prover``, a goal, and a list of assumptions. When + prove() is called, the ``Prover`` is executed with the goal and assumptions. + """ + + def __init__(self, prover, goal=None, assumptions=None): + """ + :param prover: The theorem tool to execute with the assumptions + :type prover: Prover + :see: ``BaseTheoremToolCommand`` + """ + self._prover = prover + """The theorem tool to execute with the assumptions""" + + BaseTheoremToolCommand.__init__(self, goal, assumptions) + + self._proof = None + + def prove(self, verbose=False): + """ + Perform the actual proof. Store the result to prevent unnecessary + re-proving. + """ + if self._result is None: + self._result, self._proof = self._prover._prove( + self.goal(), self.assumptions(), verbose + ) + return self._result + + def proof(self, simplify=True): + """ + Return the proof string + :param simplify: bool simplify the proof? + :return: str + """ + if self._result is None: + raise LookupError("You have to call prove() first to get a proof!") + else: + return self.decorate_proof(self._proof, simplify) + + def decorate_proof(self, proof_string, simplify=True): + """ + Modify and return the proof string + :param proof_string: str the proof to decorate + :param simplify: bool simplify the proof? + :return: str + """ + return proof_string + + def get_prover(self): + return self._prover + + +class BaseModelBuilderCommand(BaseTheoremToolCommand, ModelBuilderCommand): + """ + This class holds a ``ModelBuilder``, a goal, and a list of assumptions. When + build_model() is called, the ``ModelBuilder`` is executed with the goal and + assumptions. + """ + + def __init__(self, modelbuilder, goal=None, assumptions=None): + """ + :param modelbuilder: The theorem tool to execute with the assumptions + :type modelbuilder: ModelBuilder + :see: ``BaseTheoremToolCommand`` + """ + self._modelbuilder = modelbuilder + """The theorem tool to execute with the assumptions""" + + BaseTheoremToolCommand.__init__(self, goal, assumptions) + + self._model = None + + def build_model(self, verbose=False): + """ + Attempt to build a model. Store the result to prevent unnecessary + re-building. + """ + if self._result is None: + self._result, self._model = self._modelbuilder._build_model( + self.goal(), self.assumptions(), verbose + ) + return self._result + + def model(self, format=None): + """ + Return a string representation of the model + + :param simplify: bool simplify the proof? + :return: str + """ + if self._result is None: + raise LookupError("You have to call build_model() first to " "get a model!") + else: + return self._decorate_model(self._model, format) + + def _decorate_model(self, valuation_str, format=None): + """ + :param valuation_str: str with the model builder's output + :param format: str indicating the format for displaying + :return: str + """ + return valuation_str + + def get_model_builder(self): + return self._modelbuilder + + +class TheoremToolCommandDecorator(TheoremToolCommand): + """ + A base decorator for the ``ProverCommandDecorator`` and + ``ModelBuilderCommandDecorator`` classes from which decorators can extend. + """ + + def __init__(self, command): + """ + :param command: ``TheoremToolCommand`` to decorate + """ + self._command = command + + # The decorator has its own versions of 'result' different from the + # underlying command + self._result = None + + def assumptions(self): + return self._command.assumptions() + + def goal(self): + return self._command.goal() + + def add_assumptions(self, new_assumptions): + self._command.add_assumptions(new_assumptions) + self._result = None + + def retract_assumptions(self, retracted, debug=False): + self._command.retract_assumptions(retracted, debug) + self._result = None + + def print_assumptions(self): + self._command.print_assumptions() + + +class ProverCommandDecorator(TheoremToolCommandDecorator, ProverCommand): + """ + A base decorator for the ``ProverCommand`` class from which other + prover command decorators can extend. + """ + + def __init__(self, proverCommand): + """ + :param proverCommand: ``ProverCommand`` to decorate + """ + TheoremToolCommandDecorator.__init__(self, proverCommand) + + # The decorator has its own versions of 'result' and 'proof' + # because they may be different from the underlying command + self._proof = None + + def prove(self, verbose=False): + if self._result is None: + prover = self.get_prover() + self._result, self._proof = prover._prove( + self.goal(), self.assumptions(), verbose + ) + return self._result + + def proof(self, simplify=True): + """ + Return the proof string + :param simplify: bool simplify the proof? + :return: str + """ + if self._result is None: + raise LookupError("You have to call prove() first to get a proof!") + else: + return self.decorate_proof(self._proof, simplify) + + def decorate_proof(self, proof_string, simplify=True): + """ + Modify and return the proof string + :param proof_string: str the proof to decorate + :param simplify: bool simplify the proof? + :return: str + """ + return self._command.decorate_proof(proof_string, simplify) + + def get_prover(self): + return self._command.get_prover() + + +class ModelBuilderCommandDecorator(TheoremToolCommandDecorator, ModelBuilderCommand): + """ + A base decorator for the ``ModelBuilderCommand`` class from which other + prover command decorators can extend. + """ + + def __init__(self, modelBuilderCommand): + """ + :param modelBuilderCommand: ``ModelBuilderCommand`` to decorate + """ + TheoremToolCommandDecorator.__init__(self, modelBuilderCommand) + + # The decorator has its own versions of 'result' and 'valuation' + # because they may be different from the underlying command + self._model = None + + def build_model(self, verbose=False): + """ + Attempt to build a model. Store the result to prevent unnecessary + re-building. + """ + if self._result is None: + modelbuilder = self.get_model_builder() + self._result, self._model = modelbuilder._build_model( + self.goal(), self.assumptions(), verbose + ) + return self._result + + def model(self, format=None): + """ + Return a string representation of the model + + :param simplify: bool simplify the proof? + :return: str + """ + if self._result is None: + raise LookupError("You have to call build_model() first to " "get a model!") + else: + return self._decorate_model(self._model, format) + + def _decorate_model(self, valuation_str, format=None): + """ + Modify and return the proof string + :param valuation_str: str with the model builder's output + :param format: str indicating the format for displaying + :return: str + """ + return self._command._decorate_model(valuation_str, format) + + def get_model_builder(self): + return self._command.get_prover() + + +class ParallelProverBuilder(Prover, ModelBuilder): + """ + This class stores both a prover and a model builder and when either + prove() or build_model() is called, then both theorem tools are run in + parallel. Whichever finishes first, the prover or the model builder, is the + result that will be used. + """ + + def __init__(self, prover, modelbuilder): + self._prover = prover + self._modelbuilder = modelbuilder + + def _prove(self, goal=None, assumptions=None, verbose=False): + return self._run(goal, assumptions, verbose), "" + + def _build_model(self, goal=None, assumptions=None, verbose=False): + return not self._run(goal, assumptions, verbose), "" + + def _run(self, goal, assumptions, verbose): + # Set up two thread, Prover and ModelBuilder to run in parallel + tp_thread = TheoremToolThread( + lambda: self._prover.prove(goal, assumptions, verbose), verbose, "TP" + ) + mb_thread = TheoremToolThread( + lambda: self._modelbuilder.build_model(goal, assumptions, verbose), + verbose, + "MB", + ) + + tp_thread.start() + mb_thread.start() + + while tp_thread.is_alive() and mb_thread.is_alive(): + # wait until either the prover or the model builder is done + pass + + if tp_thread.result is not None: + return tp_thread.result + elif mb_thread.result is not None: + return not mb_thread.result + else: + return None + + +class ParallelProverBuilderCommand(BaseProverCommand, BaseModelBuilderCommand): + """ + This command stores both a prover and a model builder and when either + prove() or build_model() is called, then both theorem tools are run in + parallel. Whichever finishes first, the prover or the model builder, is the + result that will be used. + + Because the theorem prover result is the opposite of the model builder + result, we will treat self._result as meaning "proof found/no model found". + """ + + def __init__(self, prover, modelbuilder, goal=None, assumptions=None): + BaseProverCommand.__init__(self, prover, goal, assumptions) + BaseModelBuilderCommand.__init__(self, modelbuilder, goal, assumptions) + + def prove(self, verbose=False): + return self._run(verbose) + + def build_model(self, verbose=False): + return not self._run(verbose) + + def _run(self, verbose): + # Set up two thread, Prover and ModelBuilder to run in parallel + tp_thread = TheoremToolThread( + lambda: BaseProverCommand.prove(self, verbose), verbose, "TP" + ) + mb_thread = TheoremToolThread( + lambda: BaseModelBuilderCommand.build_model(self, verbose), verbose, "MB" + ) + + tp_thread.start() + mb_thread.start() + + while tp_thread.is_alive() and mb_thread.is_alive(): + # wait until either the prover or the model builder is done + pass + + if tp_thread.result is not None: + self._result = tp_thread.result + elif mb_thread.result is not None: + self._result = not mb_thread.result + return self._result + + +class TheoremToolThread(threading.Thread): + def __init__(self, command, verbose, name=None): + threading.Thread.__init__(self) + self._command = command + self._result = None + self._verbose = verbose + self._name = name + + def run(self): + try: + self._result = self._command() + if self._verbose: + print( + "Thread %s finished with result %s at %s" + % (self._name, self._result, time.localtime(time.time())) + ) + except Exception as e: + print(e) + print("Thread %s completed abnormally" % (self._name)) + + @property + def result(self): + return self._result diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/discourse.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/discourse.py new file mode 100644 index 0000000..719912a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/discourse.py @@ -0,0 +1,651 @@ +# Natural Language Toolkit: Discourse Processing +# +# Author: Ewan Klein +# Dan Garrette +# +# URL: +# For license information, see LICENSE.TXT + +r""" +Module for incrementally developing simple discourses, and checking for semantic ambiguity, +consistency and informativeness. + +Many of the ideas are based on the CURT family of programs of Blackburn and Bos +(see http://homepages.inf.ed.ac.uk/jbos/comsem/book1.html). + +Consistency checking is carried out by using the ``mace`` module to call the Mace4 model builder. +Informativeness checking is carried out with a call to ``Prover.prove()`` from +the ``inference`` module. + +``DiscourseTester`` is a constructor for discourses. +The basic data structure is a list of sentences, stored as ``self._sentences``. Each sentence in the list +is assigned a "sentence ID" (``sid``) of the form ``s``\ *i*. For example:: + + s0: A boxer walks + s1: Every boxer chases a girl + +Each sentence can be ambiguous between a number of readings, each of which receives a +"reading ID" (``rid``) of the form ``s``\ *i* -``r``\ *j*. For example:: + + s0 readings: + + s0-r1: some x.(boxer(x) & walk(x)) + s0-r0: some x.(boxerdog(x) & walk(x)) + +A "thread" is a list of readings, represented as a list of ``rid``\ s. +Each thread receives a "thread ID" (``tid``) of the form ``d``\ *i*. +For example:: + + d0: ['s0-r0', 's1-r0'] + +The set of all threads for a discourse is the Cartesian product of all the readings of the sequences of sentences. +(This is not intended to scale beyond very short discourses!) The method ``readings(filter=True)`` will only show +those threads which are consistent (taking into account any background assumptions). +""" + +import os +from abc import ABCMeta, abstractmethod +from functools import reduce +from operator import add, and_ + +from nltk.data import show_cfg +from nltk.inference.mace import MaceCommand +from nltk.inference.prover9 import Prover9Command +from nltk.parse import load_parser +from nltk.parse.malt import MaltParser +from nltk.sem.drt import AnaphoraResolutionException, resolve_anaphora +from nltk.sem.glue import DrtGlue +from nltk.sem.logic import Expression +from nltk.tag import RegexpTagger + + +class ReadingCommand(metaclass=ABCMeta): + @abstractmethod + def parse_to_readings(self, sentence): + """ + :param sentence: the sentence to read + :type sentence: str + """ + + def process_thread(self, sentence_readings): + """ + This method should be used to handle dependencies between readings such + as resolving anaphora. + + :param sentence_readings: readings to process + :type sentence_readings: list(Expression) + :return: the list of readings after processing + :rtype: list(Expression) + """ + return sentence_readings + + @abstractmethod + def combine_readings(self, readings): + """ + :param readings: readings to combine + :type readings: list(Expression) + :return: one combined reading + :rtype: Expression + """ + + @abstractmethod + def to_fol(self, expression): + """ + Convert this expression into a First-Order Logic expression. + + :param expression: an expression + :type expression: Expression + :return: a FOL version of the input expression + :rtype: Expression + """ + + +class CfgReadingCommand(ReadingCommand): + def __init__(self, gramfile=None): + """ + :param gramfile: name of file where grammar can be loaded + :type gramfile: str + """ + self._gramfile = ( + gramfile if gramfile else "grammars/book_grammars/discourse.fcfg" + ) + self._parser = load_parser(self._gramfile) + + def parse_to_readings(self, sentence): + """:see: ReadingCommand.parse_to_readings()""" + from nltk.sem import root_semrep + + tokens = sentence.split() + trees = self._parser.parse(tokens) + return [root_semrep(tree) for tree in trees] + + def combine_readings(self, readings): + """:see: ReadingCommand.combine_readings()""" + return reduce(and_, readings) + + def to_fol(self, expression): + """:see: ReadingCommand.to_fol()""" + return expression + + +class DrtGlueReadingCommand(ReadingCommand): + def __init__(self, semtype_file=None, remove_duplicates=False, depparser=None): + """ + :param semtype_file: name of file where grammar can be loaded + :param remove_duplicates: should duplicates be removed? + :param depparser: the dependency parser + """ + if semtype_file is None: + semtype_file = os.path.join( + "grammars", "sample_grammars", "drt_glue.semtype" + ) + self._glue = DrtGlue( + semtype_file=semtype_file, + remove_duplicates=remove_duplicates, + depparser=depparser, + ) + + def parse_to_readings(self, sentence): + """:see: ReadingCommand.parse_to_readings()""" + return self._glue.parse_to_meaning(sentence) + + def process_thread(self, sentence_readings): + """:see: ReadingCommand.process_thread()""" + try: + return [self.combine_readings(sentence_readings)] + except AnaphoraResolutionException: + return [] + + def combine_readings(self, readings): + """:see: ReadingCommand.combine_readings()""" + thread_reading = reduce(add, readings) + return resolve_anaphora(thread_reading.simplify()) + + def to_fol(self, expression): + """:see: ReadingCommand.to_fol()""" + return expression.fol() + + +class DiscourseTester: + """ + Check properties of an ongoing discourse. + """ + + def __init__(self, input, reading_command=None, background=None): + """ + Initialize a ``DiscourseTester``. + + :param input: the discourse sentences + :type input: list of str + :param background: Formulas which express background assumptions + :type background: list(Expression) + """ + self._input = input + self._sentences = {"s%s" % i: sent for i, sent in enumerate(input)} + self._models = None + self._readings = {} + self._reading_command = ( + reading_command if reading_command else CfgReadingCommand() + ) + self._threads = {} + self._filtered_threads = {} + if background is not None: + from nltk.sem.logic import Expression + + for e in background: + assert isinstance(e, Expression) + self._background = background + else: + self._background = [] + + ############################### + # Sentences + ############################### + + def sentences(self): + """ + Display the list of sentences in the current discourse. + """ + for id in sorted(self._sentences): + print(f"{id}: {self._sentences[id]}") + + def add_sentence(self, sentence, informchk=False, consistchk=False): + """ + Add a sentence to the current discourse. + + Updates ``self._input`` and ``self._sentences``. + :param sentence: An input sentence + :type sentence: str + :param informchk: if ``True``, check that the result of adding the sentence is thread-informative. Updates ``self._readings``. + :param consistchk: if ``True``, check that the result of adding the sentence is thread-consistent. Updates ``self._readings``. + + """ + # check whether the new sentence is informative (i.e. not entailed by the previous discourse) + if informchk: + self.readings(verbose=False) + for tid in sorted(self._threads): + assumptions = [reading for (rid, reading) in self.expand_threads(tid)] + assumptions += self._background + for sent_reading in self._get_readings(sentence): + tp = Prover9Command(goal=sent_reading, assumptions=assumptions) + if tp.prove(): + print( + "Sentence '%s' under reading '%s':" + % (sentence, str(sent_reading)) + ) + print("Not informative relative to thread '%s'" % tid) + + self._input.append(sentence) + self._sentences = {"s%s" % i: sent for i, sent in enumerate(self._input)} + # check whether adding the new sentence to the discourse preserves consistency (i.e. a model can be found for the combined set of + # of assumptions + if consistchk: + self.readings(verbose=False) + self.models(show=False) + + def retract_sentence(self, sentence, verbose=True): + """ + Remove a sentence from the current discourse. + + Updates ``self._input``, ``self._sentences`` and ``self._readings``. + :param sentence: An input sentence + :type sentence: str + :param verbose: If ``True``, report on the updated list of sentences. + """ + try: + self._input.remove(sentence) + except ValueError: + print( + "Retraction failed. The sentence '%s' is not part of the current discourse:" + % sentence + ) + self.sentences() + return None + self._sentences = {"s%s" % i: sent for i, sent in enumerate(self._input)} + self.readings(verbose=False) + if verbose: + print("Current sentences are ") + self.sentences() + + def grammar(self): + """ + Print out the grammar in use for parsing input sentences + """ + show_cfg(self._reading_command._gramfile) + + ############################### + # Readings and Threads + ############################### + + def _get_readings(self, sentence): + """ + Build a list of semantic readings for a sentence. + + :rtype: list(Expression) + """ + return self._reading_command.parse_to_readings(sentence) + + def _construct_readings(self): + """ + Use ``self._sentences`` to construct a value for ``self._readings``. + """ + # re-initialize self._readings in case we have retracted a sentence + self._readings = {} + for sid in sorted(self._sentences): + sentence = self._sentences[sid] + readings = self._get_readings(sentence) + self._readings[sid] = { + f"{sid}-r{rid}": reading.simplify() + for rid, reading in enumerate(sorted(readings, key=str)) + } + + def _construct_threads(self): + """ + Use ``self._readings`` to construct a value for ``self._threads`` + and use the model builder to construct a value for ``self._filtered_threads`` + """ + thread_list = [[]] + for sid in sorted(self._readings): + thread_list = self.multiply(thread_list, sorted(self._readings[sid])) + self._threads = {"d%s" % tid: thread for tid, thread in enumerate(thread_list)} + # re-initialize the filtered threads + self._filtered_threads = {} + # keep the same ids, but only include threads which get models + consistency_checked = self._check_consistency(self._threads) + for (tid, thread) in self._threads.items(): + if (tid, True) in consistency_checked: + self._filtered_threads[tid] = thread + + def _show_readings(self, sentence=None): + """ + Print out the readings for the discourse (or a single sentence). + """ + if sentence is not None: + print("The sentence '%s' has these readings:" % sentence) + for r in [str(reading) for reading in (self._get_readings(sentence))]: + print(" %s" % r) + else: + for sid in sorted(self._readings): + print() + print("%s readings:" % sid) + print() #'-' * 30 + for rid in sorted(self._readings[sid]): + lf = self._readings[sid][rid] + print(f"{rid}: {lf.normalize()}") + + def _show_threads(self, filter=False, show_thread_readings=False): + """ + Print out the value of ``self._threads`` or ``self._filtered_hreads`` + """ + threads = self._filtered_threads if filter else self._threads + for tid in sorted(threads): + if show_thread_readings: + readings = [ + self._readings[rid.split("-")[0]][rid] for rid in self._threads[tid] + ] + try: + thread_reading = ( + ": %s" + % self._reading_command.combine_readings(readings).normalize() + ) + except Exception as e: + thread_reading = ": INVALID: %s" % e.__class__.__name__ + else: + thread_reading = "" + + print("%s:" % tid, self._threads[tid], thread_reading) + + def readings( + self, + sentence=None, + threaded=False, + verbose=True, + filter=False, + show_thread_readings=False, + ): + """ + Construct and show the readings of the discourse (or of a single sentence). + + :param sentence: test just this sentence + :type sentence: str + :param threaded: if ``True``, print out each thread ID and the corresponding thread. + :param filter: if ``True``, only print out consistent thread IDs and threads. + """ + self._construct_readings() + self._construct_threads() + + # if we are filtering or showing thread readings, show threads + if filter or show_thread_readings: + threaded = True + + if verbose: + if not threaded: + self._show_readings(sentence=sentence) + else: + self._show_threads( + filter=filter, show_thread_readings=show_thread_readings + ) + + def expand_threads(self, thread_id, threads=None): + """ + Given a thread ID, find the list of ``logic.Expression`` objects corresponding to the reading IDs in that thread. + + :param thread_id: thread ID + :type thread_id: str + :param threads: a mapping from thread IDs to lists of reading IDs + :type threads: dict + :return: A list of pairs ``(rid, reading)`` where reading is the ``logic.Expression`` associated with a reading ID + :rtype: list of tuple + """ + if threads is None: + threads = self._threads + return [ + (rid, self._readings[sid][rid]) + for rid in threads[thread_id] + for sid in rid.split("-")[:1] + ] + + ############################### + # Models and Background + ############################### + + def _check_consistency(self, threads, show=False, verbose=False): + results = [] + for tid in sorted(threads): + assumptions = [ + reading for (rid, reading) in self.expand_threads(tid, threads=threads) + ] + assumptions = list( + map( + self._reading_command.to_fol, + self._reading_command.process_thread(assumptions), + ) + ) + if assumptions: + assumptions += self._background + # if Mace4 finds a model, it always seems to find it quickly + mb = MaceCommand(None, assumptions, max_models=20) + modelfound = mb.build_model() + else: + modelfound = False + results.append((tid, modelfound)) + if show: + spacer(80) + print("Model for Discourse Thread %s" % tid) + spacer(80) + if verbose: + for a in assumptions: + print(a) + spacer(80) + if modelfound: + print(mb.model(format="cooked")) + else: + print("No model found!\n") + return results + + def models(self, thread_id=None, show=True, verbose=False): + """ + Call Mace4 to build a model for each current discourse thread. + + :param thread_id: thread ID + :type thread_id: str + :param show: If ``True``, display the model that has been found. + """ + self._construct_readings() + self._construct_threads() + threads = {thread_id: self._threads[thread_id]} if thread_id else self._threads + + for (tid, modelfound) in self._check_consistency( + threads, show=show, verbose=verbose + ): + idlist = [rid for rid in threads[tid]] + + if not modelfound: + print(f"Inconsistent discourse: {tid} {idlist}:") + for rid, reading in self.expand_threads(tid): + print(f" {rid}: {reading.normalize()}") + print() + else: + print(f"Consistent discourse: {tid} {idlist}:") + for rid, reading in self.expand_threads(tid): + print(f" {rid}: {reading.normalize()}") + print() + + def add_background(self, background, verbose=False): + """ + Add a list of background assumptions for reasoning about the discourse. + + When called, this method also updates the discourse model's set of readings and threads. + :param background: Formulas which contain background information + :type background: list(Expression) + """ + from nltk.sem.logic import Expression + + for (count, e) in enumerate(background): + assert isinstance(e, Expression) + if verbose: + print("Adding assumption %s to background" % count) + self._background.append(e) + + # update the state + self._construct_readings() + self._construct_threads() + + def background(self): + """ + Show the current background assumptions. + """ + for e in self._background: + print(str(e)) + + ############################### + # Misc + ############################### + + @staticmethod + def multiply(discourse, readings): + """ + Multiply every thread in ``discourse`` by every reading in ``readings``. + + Given discourse = [['A'], ['B']], readings = ['a', 'b', 'c'] , returns + [['A', 'a'], ['A', 'b'], ['A', 'c'], ['B', 'a'], ['B', 'b'], ['B', 'c']] + + :param discourse: the current list of readings + :type discourse: list of lists + :param readings: an additional list of readings + :type readings: list(Expression) + :rtype: A list of lists + """ + result = [] + for sublist in discourse: + for r in readings: + new = [] + new += sublist + new.append(r) + result.append(new) + return result + + +def load_fol(s): + """ + Temporarily duplicated from ``nltk.sem.util``. + Convert a file of first order formulas into a list of ``Expression`` objects. + + :param s: the contents of the file + :type s: str + :return: a list of parsed formulas. + :rtype: list(Expression) + """ + statements = [] + for linenum, line in enumerate(s.splitlines()): + line = line.strip() + if line.startswith("#") or line == "": + continue + try: + statements.append(Expression.fromstring(line)) + except Exception as e: + raise ValueError(f"Unable to parse line {linenum}: {line}") from e + return statements + + +############################### +# Demo +############################### +def discourse_demo(reading_command=None): + """ + Illustrate the various methods of ``DiscourseTester`` + """ + dt = DiscourseTester( + ["A boxer walks", "Every boxer chases a girl"], reading_command + ) + dt.models() + print() + # dt.grammar() + print() + dt.sentences() + print() + dt.readings() + print() + dt.readings(threaded=True) + print() + dt.models("d1") + dt.add_sentence("John is a boxer") + print() + dt.sentences() + print() + dt.readings(threaded=True) + print() + dt = DiscourseTester( + ["A student dances", "Every student is a person"], reading_command + ) + print() + dt.add_sentence("No person dances", consistchk=True) + print() + dt.readings() + print() + dt.retract_sentence("No person dances", verbose=True) + print() + dt.models() + print() + dt.readings("A person dances") + print() + dt.add_sentence("A person dances", informchk=True) + dt = DiscourseTester( + ["Vincent is a boxer", "Fido is a boxer", "Vincent is married", "Fido barks"], + reading_command, + ) + dt.readings(filter=True) + import nltk.data + + background_file = os.path.join("grammars", "book_grammars", "background.fol") + background = nltk.data.load(background_file) + + print() + dt.add_background(background, verbose=False) + dt.background() + print() + dt.readings(filter=True) + print() + dt.models() + + +def drt_discourse_demo(reading_command=None): + """ + Illustrate the various methods of ``DiscourseTester`` + """ + dt = DiscourseTester(["every dog chases a boy", "he runs"], reading_command) + dt.models() + print() + dt.sentences() + print() + dt.readings() + print() + dt.readings(show_thread_readings=True) + print() + dt.readings(filter=True, show_thread_readings=True) + + +def spacer(num=30): + print("-" * num) + + +def demo(): + discourse_demo() + + tagger = RegexpTagger( + [ + ("^(chases|runs)$", "VB"), + ("^(a)$", "ex_quant"), + ("^(every)$", "univ_quant"), + ("^(dog|boy)$", "NN"), + ("^(he)$", "PRP"), + ] + ) + depparser = MaltParser(tagger=tagger) + drt_discourse_demo( + DrtGlueReadingCommand(remove_duplicates=False, depparser=depparser) + ) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/mace.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/mace.py new file mode 100644 index 0000000..ba00f78 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/mace.py @@ -0,0 +1,383 @@ +# Natural Language Toolkit: Interface to the Mace4 Model Builder +# +# Author: Dan Garrette +# Ewan Klein + +# URL: +# For license information, see LICENSE.TXT + +""" +A model builder that makes use of the external 'Mace4' package. +""" + +import os +import tempfile + +from nltk.inference.api import BaseModelBuilderCommand, ModelBuilder +from nltk.inference.prover9 import Prover9CommandParent, Prover9Parent +from nltk.sem import Expression, Valuation +from nltk.sem.logic import is_indvar + + +class MaceCommand(Prover9CommandParent, BaseModelBuilderCommand): + """ + A ``MaceCommand`` specific to the ``Mace`` model builder. It contains + a print_assumptions() method that is used to print the list + of assumptions in multiple formats. + """ + + _interpformat_bin = None + + def __init__(self, goal=None, assumptions=None, max_models=500, model_builder=None): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in + the proof. + :type assumptions: list(sem.Expression) + :param max_models: The maximum number of models that Mace will try before + simply returning false. (Use 0 for no maximum.) + :type max_models: int + """ + if model_builder is not None: + assert isinstance(model_builder, Mace) + else: + model_builder = Mace(max_models) + + BaseModelBuilderCommand.__init__(self, model_builder, goal, assumptions) + + @property + def valuation(mbc): + return mbc.model("valuation") + + def _convert2val(self, valuation_str): + """ + Transform the output file into an NLTK-style Valuation. + + :return: A model if one is generated; None otherwise. + :rtype: sem.Valuation + """ + valuation_standard_format = self._transform_output(valuation_str, "standard") + + val = [] + for line in valuation_standard_format.splitlines(False): + l = line.strip() + + if l.startswith("interpretation"): + # find the number of entities in the model + num_entities = int(l[l.index("(") + 1 : l.index(",")].strip()) + + elif l.startswith("function") and l.find("_") == -1: + # replace the integer identifier with a corresponding alphabetic character + name = l[l.index("(") + 1 : l.index(",")].strip() + if is_indvar(name): + name = name.upper() + value = int(l[l.index("[") + 1 : l.index("]")].strip()) + val.append((name, MaceCommand._make_model_var(value))) + + elif l.startswith("relation"): + l = l[l.index("(") + 1 :] + if "(" in l: + # relation is not nullary + name = l[: l.index("(")].strip() + values = [ + int(v.strip()) + for v in l[l.index("[") + 1 : l.index("]")].split(",") + ] + val.append( + (name, MaceCommand._make_relation_set(num_entities, values)) + ) + else: + # relation is nullary + name = l[: l.index(",")].strip() + value = int(l[l.index("[") + 1 : l.index("]")].strip()) + val.append((name, value == 1)) + + return Valuation(val) + + @staticmethod + def _make_relation_set(num_entities, values): + """ + Convert a Mace4-style relation table into a dictionary. + + :param num_entities: the number of entities in the model; determines the row length in the table. + :type num_entities: int + :param values: a list of 1's and 0's that represent whether a relation holds in a Mace4 model. + :type values: list of int + """ + r = set() + for position in [pos for (pos, v) in enumerate(values) if v == 1]: + r.add( + tuple(MaceCommand._make_relation_tuple(position, values, num_entities)) + ) + return r + + @staticmethod + def _make_relation_tuple(position, values, num_entities): + if len(values) == 1: + return [] + else: + sublist_size = len(values) // num_entities + sublist_start = position // sublist_size + sublist_position = int(position % sublist_size) + + sublist = values[ + sublist_start * sublist_size : (sublist_start + 1) * sublist_size + ] + return [ + MaceCommand._make_model_var(sublist_start) + ] + MaceCommand._make_relation_tuple( + sublist_position, sublist, num_entities + ) + + @staticmethod + def _make_model_var(value): + """ + Pick an alphabetic character as identifier for an entity in the model. + + :param value: where to index into the list of characters + :type value: int + """ + letter = [ + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + ][value] + num = value // 26 + return letter + str(num) if num > 0 else letter + + def _decorate_model(self, valuation_str, format): + """ + Print out a Mace4 model using any Mace4 ``interpformat`` format. + See https://www.cs.unm.edu/~mccune/mace4/manual/ for details. + + :param valuation_str: str with the model builder's output + :param format: str indicating the format for displaying + models. Defaults to 'standard' format. + :return: str + """ + if not format: + return valuation_str + elif format == "valuation": + return self._convert2val(valuation_str) + else: + return self._transform_output(valuation_str, format) + + def _transform_output(self, valuation_str, format): + """ + Transform the output file into any Mace4 ``interpformat`` format. + + :param format: Output format for displaying models. + :type format: str + """ + if format in [ + "standard", + "standard2", + "portable", + "tabular", + "raw", + "cooked", + "xml", + "tex", + ]: + return self._call_interpformat(valuation_str, [format])[0] + else: + raise LookupError("The specified format does not exist") + + def _call_interpformat(self, input_str, args=[], verbose=False): + """ + Call the ``interpformat`` binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param args: A list of command-line arguments. + :return: A tuple (stdout, returncode) + :see: ``config_prover9`` + """ + if self._interpformat_bin is None: + self._interpformat_bin = self._modelbuilder._find_binary( + "interpformat", verbose + ) + + return self._modelbuilder._call( + input_str, self._interpformat_bin, args, verbose + ) + + +class Mace(Prover9Parent, ModelBuilder): + _mace4_bin = None + + def __init__(self, end_size=500): + self._end_size = end_size + """The maximum model size that Mace will try before + simply returning false. (Use -1 for no maximum.)""" + + def _build_model(self, goal=None, assumptions=None, verbose=False): + """ + Use Mace4 to build a first order model. + + :return: ``True`` if a model was found (i.e. Mace returns value of 0), + else ``False`` + """ + if not assumptions: + assumptions = [] + + stdout, returncode = self._call_mace4( + self.prover9_input(goal, assumptions), verbose=verbose + ) + return (returncode == 0, stdout) + + def _call_mace4(self, input_str, args=[], verbose=False): + """ + Call the ``mace4`` binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param args: A list of command-line arguments. + :return: A tuple (stdout, returncode) + :see: ``config_prover9`` + """ + if self._mace4_bin is None: + self._mace4_bin = self._find_binary("mace4", verbose) + + updated_input_str = "" + if self._end_size > 0: + updated_input_str += "assign(end_size, %d).\n\n" % self._end_size + updated_input_str += input_str + + return self._call(updated_input_str, self._mace4_bin, args, verbose) + + +def spacer(num=30): + print("-" * num) + + +def decode_result(found): + """ + Decode the result of model_found() + + :param found: The output of model_found() + :type found: bool + """ + return {True: "Countermodel found", False: "No countermodel found", None: "None"}[ + found + ] + + +def test_model_found(arguments): + """ + Try some proofs and exhibit the results. + """ + for (goal, assumptions) in arguments: + g = Expression.fromstring(goal) + alist = [lp.parse(a) for a in assumptions] + m = MaceCommand(g, assumptions=alist, max_models=50) + found = m.build_model() + for a in alist: + print(" %s" % a) + print(f"|- {g}: {decode_result(found)}\n") + + +def test_build_model(arguments): + """ + Try to build a ``nltk.sem.Valuation``. + """ + g = Expression.fromstring("all x.man(x)") + alist = [ + Expression.fromstring(a) + for a in [ + "man(John)", + "man(Socrates)", + "man(Bill)", + "some x.(-(x = John) & man(x) & sees(John,x))", + "some x.(-(x = Bill) & man(x))", + "all x.some y.(man(x) -> gives(Socrates,x,y))", + ] + ] + + m = MaceCommand(g, assumptions=alist) + m.build_model() + spacer() + print("Assumptions and Goal") + spacer() + for a in alist: + print(" %s" % a) + print(f"|- {g}: {decode_result(m.build_model())}\n") + spacer() + # print(m.model('standard')) + # print(m.model('cooked')) + print("Valuation") + spacer() + print(m.valuation, "\n") + + +def test_transform_output(argument_pair): + """ + Transform the model into various Mace4 ``interpformat`` formats. + """ + g = Expression.fromstring(argument_pair[0]) + alist = [lp.parse(a) for a in argument_pair[1]] + m = MaceCommand(g, assumptions=alist) + m.build_model() + for a in alist: + print(" %s" % a) + print(f"|- {g}: {m.build_model()}\n") + for format in ["standard", "portable", "xml", "cooked"]: + spacer() + print("Using '%s' format" % format) + spacer() + print(m.model(format=format)) + + +def test_make_relation_set(): + print( + MaceCommand._make_relation_set(num_entities=3, values=[1, 0, 1]) + == {("c",), ("a",)} + ) + print( + MaceCommand._make_relation_set( + num_entities=3, values=[0, 0, 0, 0, 0, 0, 1, 0, 0] + ) + == {("c", "a")} + ) + print( + MaceCommand._make_relation_set(num_entities=2, values=[0, 0, 1, 0, 0, 0, 1, 0]) + == {("a", "b", "a"), ("b", "b", "a")} + ) + + +arguments = [ + ("mortal(Socrates)", ["all x.(man(x) -> mortal(x))", "man(Socrates)"]), + ("(not mortal(Socrates))", ["all x.(man(x) -> mortal(x))", "man(Socrates)"]), +] + + +def demo(): + test_model_found(arguments) + test_build_model(arguments) + test_transform_output(arguments[1]) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/nonmonotonic.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/nonmonotonic.py new file mode 100644 index 0000000..5be7782 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/nonmonotonic.py @@ -0,0 +1,561 @@ +# Natural Language Toolkit: Nonmonotonic Reasoning +# +# Author: Daniel H. Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +A module to perform nonmonotonic reasoning. The ideas and demonstrations in +this module are based on "Logical Foundations of Artificial Intelligence" by +Michael R. Genesereth and Nils J. Nilsson. +""" + +from collections import defaultdict +from functools import reduce + +from nltk.inference.api import Prover, ProverCommandDecorator +from nltk.inference.prover9 import Prover9, Prover9Command +from nltk.sem.logic import ( + AbstractVariableExpression, + AllExpression, + AndExpression, + ApplicationExpression, + BooleanExpression, + EqualityExpression, + ExistsExpression, + Expression, + ImpExpression, + NegatedExpression, + Variable, + VariableExpression, + operator, + unique_variable, +) + + +class ProverParseError(Exception): + pass + + +def get_domain(goal, assumptions): + if goal is None: + all_expressions = assumptions + else: + all_expressions = assumptions + [-goal] + return reduce(operator.or_, (a.constants() for a in all_expressions), set()) + + +class ClosedDomainProver(ProverCommandDecorator): + """ + This is a prover decorator that adds domain closure assumptions before + proving. + """ + + def assumptions(self): + assumptions = [a for a in self._command.assumptions()] + goal = self._command.goal() + domain = get_domain(goal, assumptions) + return [self.replace_quants(ex, domain) for ex in assumptions] + + def goal(self): + goal = self._command.goal() + domain = get_domain(goal, self._command.assumptions()) + return self.replace_quants(goal, domain) + + def replace_quants(self, ex, domain): + """ + Apply the closed domain assumption to the expression + + - Domain = union([e.free()|e.constants() for e in all_expressions]) + - translate "exists x.P" to "(z=d1 | z=d2 | ... ) & P.replace(x,z)" OR + "P.replace(x, d1) | P.replace(x, d2) | ..." + - translate "all x.P" to "P.replace(x, d1) & P.replace(x, d2) & ..." + + :param ex: ``Expression`` + :param domain: set of {Variable}s + :return: ``Expression`` + """ + if isinstance(ex, AllExpression): + conjuncts = [ + ex.term.replace(ex.variable, VariableExpression(d)) for d in domain + ] + conjuncts = [self.replace_quants(c, domain) for c in conjuncts] + return reduce(lambda x, y: x & y, conjuncts) + elif isinstance(ex, BooleanExpression): + return ex.__class__( + self.replace_quants(ex.first, domain), + self.replace_quants(ex.second, domain), + ) + elif isinstance(ex, NegatedExpression): + return -self.replace_quants(ex.term, domain) + elif isinstance(ex, ExistsExpression): + disjuncts = [ + ex.term.replace(ex.variable, VariableExpression(d)) for d in domain + ] + disjuncts = [self.replace_quants(d, domain) for d in disjuncts] + return reduce(lambda x, y: x | y, disjuncts) + else: + return ex + + +class UniqueNamesProver(ProverCommandDecorator): + """ + This is a prover decorator that adds unique names assumptions before + proving. + """ + + def assumptions(self): + """ + - Domain = union([e.free()|e.constants() for e in all_expressions]) + - if "d1 = d2" cannot be proven from the premises, then add "d1 != d2" + """ + assumptions = self._command.assumptions() + + domain = list(get_domain(self._command.goal(), assumptions)) + + # build a dictionary of obvious equalities + eq_sets = SetHolder() + for a in assumptions: + if isinstance(a, EqualityExpression): + av = a.first.variable + bv = a.second.variable + # put 'a' and 'b' in the same set + eq_sets[av].add(bv) + + new_assumptions = [] + for i, a in enumerate(domain): + for b in domain[i + 1 :]: + # if a and b are not already in the same equality set + if b not in eq_sets[a]: + newEqEx = EqualityExpression( + VariableExpression(a), VariableExpression(b) + ) + if Prover9().prove(newEqEx, assumptions): + # we can prove that the names are the same entity. + # remember that they are equal so we don't re-check. + eq_sets[a].add(b) + else: + # we can't prove it, so assume unique names + new_assumptions.append(-newEqEx) + + return assumptions + new_assumptions + + +class SetHolder(list): + """ + A list of sets of Variables. + """ + + def __getitem__(self, item): + """ + :param item: ``Variable`` + :return: the set containing 'item' + """ + assert isinstance(item, Variable) + for s in self: + if item in s: + return s + # item is not found in any existing set. so create a new set + new = {item} + self.append(new) + return new + + +class ClosedWorldProver(ProverCommandDecorator): + """ + This is a prover decorator that completes predicates before proving. + + If the assumptions contain "P(A)", then "all x.(P(x) -> (x=A))" is the completion of "P". + If the assumptions contain "all x.(ostrich(x) -> bird(x))", then "all x.(bird(x) -> ostrich(x))" is the completion of "bird". + If the assumptions don't contain anything that are "P", then "all x.-P(x)" is the completion of "P". + + walk(Socrates) + Socrates != Bill + + all x.(walk(x) -> (x=Socrates)) + ---------------- + -walk(Bill) + + see(Socrates, John) + see(John, Mary) + Socrates != John + John != Mary + + all x.all y.(see(x,y) -> ((x=Socrates & y=John) | (x=John & y=Mary))) + ---------------- + -see(Socrates, Mary) + + all x.(ostrich(x) -> bird(x)) + bird(Tweety) + -ostrich(Sam) + Sam != Tweety + + all x.(bird(x) -> (ostrich(x) | x=Tweety)) + + all x.-ostrich(x) + ------------------- + -bird(Sam) + """ + + def assumptions(self): + assumptions = self._command.assumptions() + + predicates = self._make_predicate_dict(assumptions) + + new_assumptions = [] + for p in predicates: + predHolder = predicates[p] + new_sig = self._make_unique_signature(predHolder) + new_sig_exs = [VariableExpression(v) for v in new_sig] + + disjuncts = [] + + # Turn the signatures into disjuncts + for sig in predHolder.signatures: + equality_exs = [] + for v1, v2 in zip(new_sig_exs, sig): + equality_exs.append(EqualityExpression(v1, v2)) + disjuncts.append(reduce(lambda x, y: x & y, equality_exs)) + + # Turn the properties into disjuncts + for prop in predHolder.properties: + # replace variables from the signature with new sig variables + bindings = {} + for v1, v2 in zip(new_sig_exs, prop[0]): + bindings[v2] = v1 + disjuncts.append(prop[1].substitute_bindings(bindings)) + + # make the assumption + if disjuncts: + # disjuncts exist, so make an implication + antecedent = self._make_antecedent(p, new_sig) + consequent = reduce(lambda x, y: x | y, disjuncts) + accum = ImpExpression(antecedent, consequent) + else: + # nothing has property 'p' + accum = NegatedExpression(self._make_antecedent(p, new_sig)) + + # quantify the implication + for new_sig_var in new_sig[::-1]: + accum = AllExpression(new_sig_var, accum) + new_assumptions.append(accum) + + return assumptions + new_assumptions + + def _make_unique_signature(self, predHolder): + """ + This method figures out how many arguments the predicate takes and + returns a tuple containing that number of unique variables. + """ + return tuple(unique_variable() for i in range(predHolder.signature_len)) + + def _make_antecedent(self, predicate, signature): + """ + Return an application expression with 'predicate' as the predicate + and 'signature' as the list of arguments. + """ + antecedent = predicate + for v in signature: + antecedent = antecedent(VariableExpression(v)) + return antecedent + + def _make_predicate_dict(self, assumptions): + """ + Create a dictionary of predicates from the assumptions. + + :param assumptions: a list of ``Expression``s + :return: dict mapping ``AbstractVariableExpression`` to ``PredHolder`` + """ + predicates = defaultdict(PredHolder) + for a in assumptions: + self._map_predicates(a, predicates) + return predicates + + def _map_predicates(self, expression, predDict): + if isinstance(expression, ApplicationExpression): + func, args = expression.uncurry() + if isinstance(func, AbstractVariableExpression): + predDict[func].append_sig(tuple(args)) + elif isinstance(expression, AndExpression): + self._map_predicates(expression.first, predDict) + self._map_predicates(expression.second, predDict) + elif isinstance(expression, AllExpression): + # collect all the universally quantified variables + sig = [expression.variable] + term = expression.term + while isinstance(term, AllExpression): + sig.append(term.variable) + term = term.term + if isinstance(term, ImpExpression): + if isinstance(term.first, ApplicationExpression) and isinstance( + term.second, ApplicationExpression + ): + func1, args1 = term.first.uncurry() + func2, args2 = term.second.uncurry() + if ( + isinstance(func1, AbstractVariableExpression) + and isinstance(func2, AbstractVariableExpression) + and sig == [v.variable for v in args1] + and sig == [v.variable for v in args2] + ): + predDict[func2].append_prop((tuple(sig), term.first)) + predDict[func1].validate_sig_len(sig) + + +class PredHolder: + """ + This class will be used by a dictionary that will store information + about predicates to be used by the ``ClosedWorldProver``. + + The 'signatures' property is a list of tuples defining signatures for + which the predicate is true. For instance, 'see(john, mary)' would be + result in the signature '(john,mary)' for 'see'. + + The second element of the pair is a list of pairs such that the first + element of the pair is a tuple of variables and the second element is an + expression of those variables that makes the predicate true. For instance, + 'all x.all y.(see(x,y) -> know(x,y))' would result in "((x,y),('see(x,y)'))" + for 'know'. + """ + + def __init__(self): + self.signatures = [] + self.properties = [] + self.signature_len = None + + def append_sig(self, new_sig): + self.validate_sig_len(new_sig) + self.signatures.append(new_sig) + + def append_prop(self, new_prop): + self.validate_sig_len(new_prop[0]) + self.properties.append(new_prop) + + def validate_sig_len(self, new_sig): + if self.signature_len is None: + self.signature_len = len(new_sig) + elif self.signature_len != len(new_sig): + raise Exception("Signature lengths do not match") + + def __str__(self): + return f"({self.signatures},{self.properties},{self.signature_len})" + + def __repr__(self): + return "%s" % self + + +def closed_domain_demo(): + lexpr = Expression.fromstring + + p1 = lexpr(r"exists x.walk(x)") + p2 = lexpr(r"man(Socrates)") + c = lexpr(r"walk(Socrates)") + prover = Prover9Command(c, [p1, p2]) + print(prover.prove()) + cdp = ClosedDomainProver(prover) + print("assumptions:") + for a in cdp.assumptions(): + print(" ", a) + print("goal:", cdp.goal()) + print(cdp.prove()) + + p1 = lexpr(r"exists x.walk(x)") + p2 = lexpr(r"man(Socrates)") + p3 = lexpr(r"-walk(Bill)") + c = lexpr(r"walk(Socrates)") + prover = Prover9Command(c, [p1, p2, p3]) + print(prover.prove()) + cdp = ClosedDomainProver(prover) + print("assumptions:") + for a in cdp.assumptions(): + print(" ", a) + print("goal:", cdp.goal()) + print(cdp.prove()) + + p1 = lexpr(r"exists x.walk(x)") + p2 = lexpr(r"man(Socrates)") + p3 = lexpr(r"-walk(Bill)") + c = lexpr(r"walk(Socrates)") + prover = Prover9Command(c, [p1, p2, p3]) + print(prover.prove()) + cdp = ClosedDomainProver(prover) + print("assumptions:") + for a in cdp.assumptions(): + print(" ", a) + print("goal:", cdp.goal()) + print(cdp.prove()) + + p1 = lexpr(r"walk(Socrates)") + p2 = lexpr(r"walk(Bill)") + c = lexpr(r"all x.walk(x)") + prover = Prover9Command(c, [p1, p2]) + print(prover.prove()) + cdp = ClosedDomainProver(prover) + print("assumptions:") + for a in cdp.assumptions(): + print(" ", a) + print("goal:", cdp.goal()) + print(cdp.prove()) + + p1 = lexpr(r"girl(mary)") + p2 = lexpr(r"dog(rover)") + p3 = lexpr(r"all x.(girl(x) -> -dog(x))") + p4 = lexpr(r"all x.(dog(x) -> -girl(x))") + p5 = lexpr(r"chase(mary, rover)") + c = lexpr(r"exists y.(dog(y) & all x.(girl(x) -> chase(x,y)))") + prover = Prover9Command(c, [p1, p2, p3, p4, p5]) + print(prover.prove()) + cdp = ClosedDomainProver(prover) + print("assumptions:") + for a in cdp.assumptions(): + print(" ", a) + print("goal:", cdp.goal()) + print(cdp.prove()) + + +def unique_names_demo(): + lexpr = Expression.fromstring + + p1 = lexpr(r"man(Socrates)") + p2 = lexpr(r"man(Bill)") + c = lexpr(r"exists x.exists y.(x != y)") + prover = Prover9Command(c, [p1, p2]) + print(prover.prove()) + unp = UniqueNamesProver(prover) + print("assumptions:") + for a in unp.assumptions(): + print(" ", a) + print("goal:", unp.goal()) + print(unp.prove()) + + p1 = lexpr(r"all x.(walk(x) -> (x = Socrates))") + p2 = lexpr(r"Bill = William") + p3 = lexpr(r"Bill = Billy") + c = lexpr(r"-walk(William)") + prover = Prover9Command(c, [p1, p2, p3]) + print(prover.prove()) + unp = UniqueNamesProver(prover) + print("assumptions:") + for a in unp.assumptions(): + print(" ", a) + print("goal:", unp.goal()) + print(unp.prove()) + + +def closed_world_demo(): + lexpr = Expression.fromstring + + p1 = lexpr(r"walk(Socrates)") + p2 = lexpr(r"(Socrates != Bill)") + c = lexpr(r"-walk(Bill)") + prover = Prover9Command(c, [p1, p2]) + print(prover.prove()) + cwp = ClosedWorldProver(prover) + print("assumptions:") + for a in cwp.assumptions(): + print(" ", a) + print("goal:", cwp.goal()) + print(cwp.prove()) + + p1 = lexpr(r"see(Socrates, John)") + p2 = lexpr(r"see(John, Mary)") + p3 = lexpr(r"(Socrates != John)") + p4 = lexpr(r"(John != Mary)") + c = lexpr(r"-see(Socrates, Mary)") + prover = Prover9Command(c, [p1, p2, p3, p4]) + print(prover.prove()) + cwp = ClosedWorldProver(prover) + print("assumptions:") + for a in cwp.assumptions(): + print(" ", a) + print("goal:", cwp.goal()) + print(cwp.prove()) + + p1 = lexpr(r"all x.(ostrich(x) -> bird(x))") + p2 = lexpr(r"bird(Tweety)") + p3 = lexpr(r"-ostrich(Sam)") + p4 = lexpr(r"Sam != Tweety") + c = lexpr(r"-bird(Sam)") + prover = Prover9Command(c, [p1, p2, p3, p4]) + print(prover.prove()) + cwp = ClosedWorldProver(prover) + print("assumptions:") + for a in cwp.assumptions(): + print(" ", a) + print("goal:", cwp.goal()) + print(cwp.prove()) + + +def combination_prover_demo(): + lexpr = Expression.fromstring + + p1 = lexpr(r"see(Socrates, John)") + p2 = lexpr(r"see(John, Mary)") + c = lexpr(r"-see(Socrates, Mary)") + prover = Prover9Command(c, [p1, p2]) + print(prover.prove()) + command = ClosedDomainProver(UniqueNamesProver(ClosedWorldProver(prover))) + for a in command.assumptions(): + print(a) + print(command.prove()) + + +def default_reasoning_demo(): + lexpr = Expression.fromstring + + premises = [] + + # define taxonomy + premises.append(lexpr(r"all x.(elephant(x) -> animal(x))")) + premises.append(lexpr(r"all x.(bird(x) -> animal(x))")) + premises.append(lexpr(r"all x.(dove(x) -> bird(x))")) + premises.append(lexpr(r"all x.(ostrich(x) -> bird(x))")) + premises.append(lexpr(r"all x.(flying_ostrich(x) -> ostrich(x))")) + + # default properties + premises.append( + lexpr(r"all x.((animal(x) & -Ab1(x)) -> -fly(x))") + ) # normal animals don't fly + premises.append( + lexpr(r"all x.((bird(x) & -Ab2(x)) -> fly(x))") + ) # normal birds fly + premises.append( + lexpr(r"all x.((ostrich(x) & -Ab3(x)) -> -fly(x))") + ) # normal ostriches don't fly + + # specify abnormal entities + premises.append(lexpr(r"all x.(bird(x) -> Ab1(x))")) # flight + premises.append(lexpr(r"all x.(ostrich(x) -> Ab2(x))")) # non-flying bird + premises.append(lexpr(r"all x.(flying_ostrich(x) -> Ab3(x))")) # flying ostrich + + # define entities + premises.append(lexpr(r"elephant(E)")) + premises.append(lexpr(r"dove(D)")) + premises.append(lexpr(r"ostrich(O)")) + + # print the assumptions + prover = Prover9Command(None, premises) + command = UniqueNamesProver(ClosedWorldProver(prover)) + for a in command.assumptions(): + print(a) + + print_proof("-fly(E)", premises) + print_proof("fly(D)", premises) + print_proof("-fly(O)", premises) + + +def print_proof(goal, premises): + lexpr = Expression.fromstring + prover = Prover9Command(lexpr(goal), premises) + command = UniqueNamesProver(ClosedWorldProver(prover)) + print(goal, prover.prove(), command.prove()) + + +def demo(): + closed_domain_demo() + unique_names_demo() + closed_world_demo() + combination_prover_demo() + default_reasoning_demo() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/prover9.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/prover9.py new file mode 100644 index 0000000..f236cac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/prover9.py @@ -0,0 +1,508 @@ +# Natural Language Toolkit: Interface to the Prover9 Theorem Prover +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Dan Garrette +# Ewan Klein +# +# URL: +# For license information, see LICENSE.TXT +""" +A theorem prover that makes use of the external 'Prover9' package. +""" + +import os +import subprocess + +import nltk +from nltk.inference.api import BaseProverCommand, Prover +from nltk.sem.logic import ( + AllExpression, + AndExpression, + EqualityExpression, + ExistsExpression, + Expression, + IffExpression, + ImpExpression, + NegatedExpression, + OrExpression, +) + +# +# Following is not yet used. Return code for 2 actually realized as 512. +# +p9_return_codes = { + 0: True, + 1: "(FATAL)", # A fatal error occurred (user's syntax error). + 2: False, # (SOS_EMPTY) Prover9 ran out of things to do + # (sos list exhausted). + 3: "(MAX_MEGS)", # The max_megs (memory limit) parameter was exceeded. + 4: "(MAX_SECONDS)", # The max_seconds parameter was exceeded. + 5: "(MAX_GIVEN)", # The max_given parameter was exceeded. + 6: "(MAX_KEPT)", # The max_kept parameter was exceeded. + 7: "(ACTION)", # A Prover9 action terminated the search. + 101: "(SIGSEGV)", # Prover9 crashed, most probably due to a bug. +} + + +class Prover9CommandParent: + """ + A common base class used by both ``Prover9Command`` and ``MaceCommand``, + which is responsible for maintaining a goal and a set of assumptions, + and generating prover9-style input files from them. + """ + + def print_assumptions(self, output_format="nltk"): + """ + Print the list of the current assumptions. + """ + if output_format.lower() == "nltk": + for a in self.assumptions(): + print(a) + elif output_format.lower() == "prover9": + for a in convert_to_prover9(self.assumptions()): + print(a) + else: + raise NameError( + "Unrecognized value for 'output_format': %s" % output_format + ) + + +class Prover9Command(Prover9CommandParent, BaseProverCommand): + """ + A ``ProverCommand`` specific to the ``Prover9`` prover. It contains + the a print_assumptions() method that is used to print the list + of assumptions in multiple formats. + """ + + def __init__(self, goal=None, assumptions=None, timeout=60, prover=None): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in + the proof. + :type assumptions: list(sem.Expression) + :param timeout: number of seconds before timeout; set to 0 for + no timeout. + :type timeout: int + :param prover: a prover. If not set, one will be created. + :type prover: Prover9 + """ + if not assumptions: + assumptions = [] + + if prover is not None: + assert isinstance(prover, Prover9) + else: + prover = Prover9(timeout) + + BaseProverCommand.__init__(self, prover, goal, assumptions) + + def decorate_proof(self, proof_string, simplify=True): + """ + :see BaseProverCommand.decorate_proof() + """ + if simplify: + return self._prover._call_prooftrans(proof_string, ["striplabels"])[ + 0 + ].rstrip() + else: + return proof_string.rstrip() + + +class Prover9Parent: + """ + A common class extended by both ``Prover9`` and ``Mace ``. + It contains the functionality required to convert NLTK-style + expressions into Prover9-style expressions. + """ + + _binary_location = None + + def config_prover9(self, binary_location, verbose=False): + if binary_location is None: + self._binary_location = None + self._prover9_bin = None + else: + name = "prover9" + self._prover9_bin = nltk.internals.find_binary( + name, + path_to_bin=binary_location, + env_vars=["PROVER9"], + url="https://www.cs.unm.edu/~mccune/prover9/", + binary_names=[name, name + ".exe"], + verbose=verbose, + ) + self._binary_location = self._prover9_bin.rsplit(os.path.sep, 1) + + def prover9_input(self, goal, assumptions): + """ + :return: The input string that should be provided to the + prover9 binary. This string is formed based on the goal, + assumptions, and timeout value of this object. + """ + s = "" + + if assumptions: + s += "formulas(assumptions).\n" + for p9_assumption in convert_to_prover9(assumptions): + s += " %s.\n" % p9_assumption + s += "end_of_list.\n\n" + + if goal: + s += "formulas(goals).\n" + s += " %s.\n" % convert_to_prover9(goal) + s += "end_of_list.\n\n" + + return s + + def binary_locations(self): + """ + A list of directories that should be searched for the prover9 + executables. This list is used by ``config_prover9`` when searching + for the prover9 executables. + """ + return [ + "/usr/local/bin/prover9", + "/usr/local/bin/prover9/bin", + "/usr/local/bin", + "/usr/bin", + "/usr/local/prover9", + "/usr/local/share/prover9", + ] + + def _find_binary(self, name, verbose=False): + binary_locations = self.binary_locations() + if self._binary_location is not None: + binary_locations += [self._binary_location] + return nltk.internals.find_binary( + name, + searchpath=binary_locations, + env_vars=["PROVER9"], + url="https://www.cs.unm.edu/~mccune/prover9/", + binary_names=[name, name + ".exe"], + verbose=verbose, + ) + + def _call(self, input_str, binary, args=[], verbose=False): + """ + Call the binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param binary: The location of the binary to call + :param args: A list of command-line arguments. + :return: A tuple (stdout, returncode) + :see: ``config_prover9`` + """ + if verbose: + print("Calling:", binary) + print("Args:", args) + print("Input:\n", input_str, "\n") + + # Call prover9 via a subprocess + cmd = [binary] + args + try: + input_str = input_str.encode("utf8") + except AttributeError: + pass + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE + ) + (stdout, stderr) = p.communicate(input=input_str) + + if verbose: + print("Return code:", p.returncode) + if stdout: + print("stdout:\n", stdout, "\n") + if stderr: + print("stderr:\n", stderr, "\n") + + return (stdout.decode("utf-8"), p.returncode) + + +def convert_to_prover9(input): + """ + Convert a ``logic.Expression`` to Prover9 format. + """ + if isinstance(input, list): + result = [] + for s in input: + try: + result.append(_convert_to_prover9(s.simplify())) + except: + print("input %s cannot be converted to Prover9 input syntax" % input) + raise + return result + else: + try: + return _convert_to_prover9(input.simplify()) + except: + print("input %s cannot be converted to Prover9 input syntax" % input) + raise + + +def _convert_to_prover9(expression): + """ + Convert ``logic.Expression`` to Prover9 formatted string. + """ + if isinstance(expression, ExistsExpression): + return ( + "exists " + + str(expression.variable) + + " " + + _convert_to_prover9(expression.term) + ) + elif isinstance(expression, AllExpression): + return ( + "all " + + str(expression.variable) + + " " + + _convert_to_prover9(expression.term) + ) + elif isinstance(expression, NegatedExpression): + return "-(" + _convert_to_prover9(expression.term) + ")" + elif isinstance(expression, AndExpression): + return ( + "(" + + _convert_to_prover9(expression.first) + + " & " + + _convert_to_prover9(expression.second) + + ")" + ) + elif isinstance(expression, OrExpression): + return ( + "(" + + _convert_to_prover9(expression.first) + + " | " + + _convert_to_prover9(expression.second) + + ")" + ) + elif isinstance(expression, ImpExpression): + return ( + "(" + + _convert_to_prover9(expression.first) + + " -> " + + _convert_to_prover9(expression.second) + + ")" + ) + elif isinstance(expression, IffExpression): + return ( + "(" + + _convert_to_prover9(expression.first) + + " <-> " + + _convert_to_prover9(expression.second) + + ")" + ) + elif isinstance(expression, EqualityExpression): + return ( + "(" + + _convert_to_prover9(expression.first) + + " = " + + _convert_to_prover9(expression.second) + + ")" + ) + else: + return str(expression) + + +class Prover9(Prover9Parent, Prover): + _prover9_bin = None + _prooftrans_bin = None + + def __init__(self, timeout=60): + self._timeout = timeout + """The timeout value for prover9. If a proof can not be found + in this amount of time, then prover9 will return false. + (Use 0 for no timeout.)""" + + def _prove(self, goal=None, assumptions=None, verbose=False): + """ + Use Prover9 to prove a theorem. + :return: A pair whose first element is a boolean indicating if the + proof was successful (i.e. returns value of 0) and whose second element + is the output of the prover. + """ + if not assumptions: + assumptions = [] + + stdout, returncode = self._call_prover9( + self.prover9_input(goal, assumptions), verbose=verbose + ) + return (returncode == 0, stdout) + + def prover9_input(self, goal, assumptions): + """ + :see: Prover9Parent.prover9_input + """ + s = "clear(auto_denials).\n" # only one proof required + return s + Prover9Parent.prover9_input(self, goal, assumptions) + + def _call_prover9(self, input_str, args=[], verbose=False): + """ + Call the ``prover9`` binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param args: A list of command-line arguments. + :return: A tuple (stdout, returncode) + :see: ``config_prover9`` + """ + if self._prover9_bin is None: + self._prover9_bin = self._find_binary("prover9", verbose) + + updated_input_str = "" + if self._timeout > 0: + updated_input_str += "assign(max_seconds, %d).\n\n" % self._timeout + updated_input_str += input_str + + stdout, returncode = self._call( + updated_input_str, self._prover9_bin, args, verbose + ) + + if returncode not in [0, 2]: + errormsgprefix = "%%ERROR:" + if errormsgprefix in stdout: + msgstart = stdout.index(errormsgprefix) + errormsg = stdout[msgstart:].strip() + else: + errormsg = None + if returncode in [3, 4, 5, 6]: + raise Prover9LimitExceededException(returncode, errormsg) + else: + raise Prover9FatalException(returncode, errormsg) + + return stdout, returncode + + def _call_prooftrans(self, input_str, args=[], verbose=False): + """ + Call the ``prooftrans`` binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param args: A list of command-line arguments. + :return: A tuple (stdout, returncode) + :see: ``config_prover9`` + """ + if self._prooftrans_bin is None: + self._prooftrans_bin = self._find_binary("prooftrans", verbose) + + return self._call(input_str, self._prooftrans_bin, args, verbose) + + +class Prover9Exception(Exception): + def __init__(self, returncode, message): + msg = p9_return_codes[returncode] + if message: + msg += "\n%s" % message + Exception.__init__(self, msg) + + +class Prover9FatalException(Prover9Exception): + pass + + +class Prover9LimitExceededException(Prover9Exception): + pass + + +###################################################################### +# { Tests and Demos +###################################################################### + + +def test_config(): + + a = Expression.fromstring("(walk(j) & sing(j))") + g = Expression.fromstring("walk(j)") + p = Prover9Command(g, assumptions=[a]) + p._executable_path = None + p.prover9_search = [] + p.prove() + # config_prover9('/usr/local/bin') + print(p.prove()) + print(p.proof()) + + +def test_convert_to_prover9(expr): + """ + Test that parsing works OK. + """ + for t in expr: + e = Expression.fromstring(t) + print(convert_to_prover9(e)) + + +def test_prove(arguments): + """ + Try some proofs and exhibit the results. + """ + for (goal, assumptions) in arguments: + g = Expression.fromstring(goal) + alist = [Expression.fromstring(a) for a in assumptions] + p = Prover9Command(g, assumptions=alist).prove() + for a in alist: + print(" %s" % a) + print(f"|- {g}: {p}\n") + + +arguments = [ + ("(man(x) <-> (not (not man(x))))", []), + ("(not (man(x) & (not man(x))))", []), + ("(man(x) | (not man(x)))", []), + ("(man(x) & (not man(x)))", []), + ("(man(x) -> man(x))", []), + ("(not (man(x) & (not man(x))))", []), + ("(man(x) | (not man(x)))", []), + ("(man(x) -> man(x))", []), + ("(man(x) <-> man(x))", []), + ("(not (man(x) <-> (not man(x))))", []), + ("mortal(Socrates)", ["all x.(man(x) -> mortal(x))", "man(Socrates)"]), + ("((all x.(man(x) -> walks(x)) & man(Socrates)) -> some y.walks(y))", []), + ("(all x.man(x) -> all x.man(x))", []), + ("some x.all y.sees(x,y)", []), + ( + "some e3.(walk(e3) & subj(e3, mary))", + [ + "some e1.(see(e1) & subj(e1, john) & some e2.(pred(e1, e2) & walk(e2) & subj(e2, mary)))" + ], + ), + ( + "some x e1.(see(e1) & subj(e1, x) & some e2.(pred(e1, e2) & walk(e2) & subj(e2, mary)))", + [ + "some e1.(see(e1) & subj(e1, john) & some e2.(pred(e1, e2) & walk(e2) & subj(e2, mary)))" + ], + ), +] + +expressions = [ + r"some x y.sees(x,y)", + r"some x.(man(x) & walks(x))", + r"\x.(man(x) & walks(x))", + r"\x y.sees(x,y)", + r"walks(john)", + r"\x.big(x, \y.mouse(y))", + r"(walks(x) & (runs(x) & (threes(x) & fours(x))))", + r"(walks(x) -> runs(x))", + r"some x.(PRO(x) & sees(John, x))", + r"some x.(man(x) & (not walks(x)))", + r"all x.(man(x) -> walks(x))", +] + + +def spacer(num=45): + print("-" * num) + + +def demo(): + print("Testing configuration") + spacer() + test_config() + print() + print("Testing conversion to Prover9 format") + spacer() + test_convert_to_prover9(expressions) + print() + print("Testing proofs") + spacer() + test_prove(arguments) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/resolution.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/resolution.py new file mode 100644 index 0000000..567754b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/resolution.py @@ -0,0 +1,759 @@ +# Natural Language Toolkit: First-order Resolution-based Theorem Prover +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +Module for a resolution-based First Order theorem prover. +""" + +import operator +from collections import defaultdict +from functools import reduce + +from nltk.inference.api import BaseProverCommand, Prover +from nltk.sem import skolemize +from nltk.sem.logic import ( + AndExpression, + ApplicationExpression, + EqualityExpression, + Expression, + IndividualVariableExpression, + NegatedExpression, + OrExpression, + Variable, + VariableExpression, + is_indvar, + unique_variable, +) + + +class ProverParseError(Exception): + pass + + +class ResolutionProver(Prover): + ANSWER_KEY = "ANSWER" + _assume_false = True + + def _prove(self, goal=None, assumptions=None, verbose=False): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in the proof + :type assumptions: list(sem.Expression) + """ + if not assumptions: + assumptions = [] + + result = None + try: + clauses = [] + if goal: + clauses.extend(clausify(-goal)) + for a in assumptions: + clauses.extend(clausify(a)) + result, clauses = self._attempt_proof(clauses) + if verbose: + print(ResolutionProverCommand._decorate_clauses(clauses)) + except RuntimeError as e: + if self._assume_false and str(e).startswith( + "maximum recursion depth exceeded" + ): + result = False + clauses = [] + else: + if verbose: + print(e) + else: + raise e + return (result, clauses) + + def _attempt_proof(self, clauses): + # map indices to lists of indices, to store attempted unifications + tried = defaultdict(list) + + i = 0 + while i < len(clauses): + if not clauses[i].is_tautology(): + # since we try clauses in order, we should start after the last + # index tried + if tried[i]: + j = tried[i][-1] + 1 + else: + j = i + 1 # nothing tried yet for 'i', so start with the next + + while j < len(clauses): + # don't: 1) unify a clause with itself, + # 2) use tautologies + if i != j and j and not clauses[j].is_tautology(): + tried[i].append(j) + newclauses = clauses[i].unify(clauses[j]) + if newclauses: + for newclause in newclauses: + newclause._parents = (i + 1, j + 1) + clauses.append(newclause) + if not len(newclause): # if there's an empty clause + return (True, clauses) + i = -1 # since we added a new clause, restart from the top + break + j += 1 + i += 1 + return (False, clauses) + + +class ResolutionProverCommand(BaseProverCommand): + def __init__(self, goal=None, assumptions=None, prover=None): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in + the proof. + :type assumptions: list(sem.Expression) + """ + if prover is not None: + assert isinstance(prover, ResolutionProver) + else: + prover = ResolutionProver() + + BaseProverCommand.__init__(self, prover, goal, assumptions) + self._clauses = None + + def prove(self, verbose=False): + """ + Perform the actual proof. Store the result to prevent unnecessary + re-proving. + """ + if self._result is None: + self._result, clauses = self._prover._prove( + self.goal(), self.assumptions(), verbose + ) + self._clauses = clauses + self._proof = ResolutionProverCommand._decorate_clauses(clauses) + return self._result + + def find_answers(self, verbose=False): + self.prove(verbose) + + answers = set() + answer_ex = VariableExpression(Variable(ResolutionProver.ANSWER_KEY)) + for clause in self._clauses: + for term in clause: + if ( + isinstance(term, ApplicationExpression) + and term.function == answer_ex + and not isinstance(term.argument, IndividualVariableExpression) + ): + answers.add(term.argument) + return answers + + @staticmethod + def _decorate_clauses(clauses): + """ + Decorate the proof output. + """ + out = "" + max_clause_len = max(len(str(clause)) for clause in clauses) + max_seq_len = len(str(len(clauses))) + for i in range(len(clauses)): + parents = "A" + taut = "" + if clauses[i].is_tautology(): + taut = "Tautology" + if clauses[i]._parents: + parents = str(clauses[i]._parents) + parents = " " * (max_clause_len - len(str(clauses[i])) + 1) + parents + seq = " " * (max_seq_len - len(str(i + 1))) + str(i + 1) + out += f"[{seq}] {clauses[i]} {parents} {taut}\n" + return out + + +class Clause(list): + def __init__(self, data): + list.__init__(self, data) + self._is_tautology = None + self._parents = None + + def unify(self, other, bindings=None, used=None, skipped=None, debug=False): + """ + Attempt to unify this Clause with the other, returning a list of + resulting, unified, Clauses. + + :param other: ``Clause`` with which to unify + :param bindings: ``BindingDict`` containing bindings that should be used + during the unification + :param used: tuple of two lists of atoms. The first lists the + atoms from 'self' that were successfully unified with atoms from + 'other'. The second lists the atoms from 'other' that were successfully + unified with atoms from 'self'. + :param skipped: tuple of two ``Clause`` objects. The first is a list of all + the atoms from the 'self' Clause that have not been unified with + anything on the path. The second is same thing for the 'other' Clause. + :param debug: bool indicating whether debug statements should print + :return: list containing all the resulting ``Clause`` objects that could be + obtained by unification + """ + if bindings is None: + bindings = BindingDict() + if used is None: + used = ([], []) + if skipped is None: + skipped = ([], []) + if isinstance(debug, bool): + debug = DebugObject(debug) + + newclauses = _iterate_first( + self, other, bindings, used, skipped, _complete_unify_path, debug + ) + + # remove subsumed clauses. make a list of all indices of subsumed + # clauses, and then remove them from the list + subsumed = [] + for i, c1 in enumerate(newclauses): + if i not in subsumed: + for j, c2 in enumerate(newclauses): + if i != j and j not in subsumed and c1.subsumes(c2): + subsumed.append(j) + result = [] + for i in range(len(newclauses)): + if i not in subsumed: + result.append(newclauses[i]) + + return result + + def isSubsetOf(self, other): + """ + Return True iff every term in 'self' is a term in 'other'. + + :param other: ``Clause`` + :return: bool + """ + for a in self: + if a not in other: + return False + return True + + def subsumes(self, other): + """ + Return True iff 'self' subsumes 'other', this is, if there is a + substitution such that every term in 'self' can be unified with a term + in 'other'. + + :param other: ``Clause`` + :return: bool + """ + negatedother = [] + for atom in other: + if isinstance(atom, NegatedExpression): + negatedother.append(atom.term) + else: + negatedother.append(-atom) + + negatedotherClause = Clause(negatedother) + + bindings = BindingDict() + used = ([], []) + skipped = ([], []) + debug = DebugObject(False) + + return ( + len( + _iterate_first( + self, + negatedotherClause, + bindings, + used, + skipped, + _subsumes_finalize, + debug, + ) + ) + > 0 + ) + + def __getslice__(self, start, end): + return Clause(list.__getslice__(self, start, end)) + + def __sub__(self, other): + return Clause([a for a in self if a not in other]) + + def __add__(self, other): + return Clause(list.__add__(self, other)) + + def is_tautology(self): + """ + Self is a tautology if it contains ground terms P and -P. The ground + term, P, must be an exact match, ie, not using unification. + """ + if self._is_tautology is not None: + return self._is_tautology + for i, a in enumerate(self): + if not isinstance(a, EqualityExpression): + j = len(self) - 1 + while j > i: + b = self[j] + if isinstance(a, NegatedExpression): + if a.term == b: + self._is_tautology = True + return True + elif isinstance(b, NegatedExpression): + if a == b.term: + self._is_tautology = True + return True + j -= 1 + self._is_tautology = False + return False + + def free(self): + return reduce(operator.or_, ((atom.free() | atom.constants()) for atom in self)) + + def replace(self, variable, expression): + """ + Replace every instance of variable with expression across every atom + in the clause + + :param variable: ``Variable`` + :param expression: ``Expression`` + """ + return Clause([atom.replace(variable, expression) for atom in self]) + + def substitute_bindings(self, bindings): + """ + Replace every binding + + :param bindings: A list of tuples mapping Variable Expressions to the + Expressions to which they are bound. + :return: ``Clause`` + """ + return Clause([atom.substitute_bindings(bindings) for atom in self]) + + def __str__(self): + return "{" + ", ".join("%s" % item for item in self) + "}" + + def __repr__(self): + return "%s" % self + + +def _iterate_first(first, second, bindings, used, skipped, finalize_method, debug): + """ + This method facilitates movement through the terms of 'self' + """ + debug.line(f"unify({first},{second}) {bindings}") + + if not len(first) or not len(second): # if no more recursions can be performed + return finalize_method(first, second, bindings, used, skipped, debug) + else: + # explore this 'self' atom + result = _iterate_second( + first, second, bindings, used, skipped, finalize_method, debug + 1 + ) + + # skip this possible 'self' atom + newskipped = (skipped[0] + [first[0]], skipped[1]) + result += _iterate_first( + first[1:], second, bindings, used, newskipped, finalize_method, debug + 1 + ) + + try: + newbindings, newused, unused = _unify_terms( + first[0], second[0], bindings, used + ) + # Unification found, so progress with this line of unification + # put skipped and unused terms back into play for later unification. + newfirst = first[1:] + skipped[0] + unused[0] + newsecond = second[1:] + skipped[1] + unused[1] + result += _iterate_first( + newfirst, + newsecond, + newbindings, + newused, + ([], []), + finalize_method, + debug + 1, + ) + except BindingException: + # the atoms could not be unified, + pass + + return result + + +def _iterate_second(first, second, bindings, used, skipped, finalize_method, debug): + """ + This method facilitates movement through the terms of 'other' + """ + debug.line(f"unify({first},{second}) {bindings}") + + if not len(first) or not len(second): # if no more recursions can be performed + return finalize_method(first, second, bindings, used, skipped, debug) + else: + # skip this possible pairing and move to the next + newskipped = (skipped[0], skipped[1] + [second[0]]) + result = _iterate_second( + first, second[1:], bindings, used, newskipped, finalize_method, debug + 1 + ) + + try: + newbindings, newused, unused = _unify_terms( + first[0], second[0], bindings, used + ) + # Unification found, so progress with this line of unification + # put skipped and unused terms back into play for later unification. + newfirst = first[1:] + skipped[0] + unused[0] + newsecond = second[1:] + skipped[1] + unused[1] + result += _iterate_second( + newfirst, + newsecond, + newbindings, + newused, + ([], []), + finalize_method, + debug + 1, + ) + except BindingException: + # the atoms could not be unified, + pass + + return result + + +def _unify_terms(a, b, bindings=None, used=None): + """ + This method attempts to unify two terms. Two expressions are unifiable + if there exists a substitution function S such that S(a) == S(-b). + + :param a: ``Expression`` + :param b: ``Expression`` + :param bindings: ``BindingDict`` a starting set of bindings with which + the unification must be consistent + :return: ``BindingDict`` A dictionary of the bindings required to unify + :raise ``BindingException``: If the terms cannot be unified + """ + assert isinstance(a, Expression) + assert isinstance(b, Expression) + + if bindings is None: + bindings = BindingDict() + if used is None: + used = ([], []) + + # Use resolution + if isinstance(a, NegatedExpression) and isinstance(b, ApplicationExpression): + newbindings = most_general_unification(a.term, b, bindings) + newused = (used[0] + [a], used[1] + [b]) + unused = ([], []) + elif isinstance(a, ApplicationExpression) and isinstance(b, NegatedExpression): + newbindings = most_general_unification(a, b.term, bindings) + newused = (used[0] + [a], used[1] + [b]) + unused = ([], []) + + # Use demodulation + elif isinstance(a, EqualityExpression): + newbindings = BindingDict([(a.first.variable, a.second)]) + newused = (used[0] + [a], used[1]) + unused = ([], [b]) + elif isinstance(b, EqualityExpression): + newbindings = BindingDict([(b.first.variable, b.second)]) + newused = (used[0], used[1] + [b]) + unused = ([a], []) + + else: + raise BindingException((a, b)) + + return newbindings, newused, unused + + +def _complete_unify_path(first, second, bindings, used, skipped, debug): + if used[0] or used[1]: # if bindings were made along the path + newclause = Clause(skipped[0] + skipped[1] + first + second) + debug.line(" -> New Clause: %s" % newclause) + return [newclause.substitute_bindings(bindings)] + else: # no bindings made means no unification occurred. so no result + debug.line(" -> End") + return [] + + +def _subsumes_finalize(first, second, bindings, used, skipped, debug): + if not len(skipped[0]) and not len(first): + # If there are no skipped terms and no terms left in 'first', then + # all of the terms in the original 'self' were unified with terms + # in 'other'. Therefore, there exists a binding (this one) such that + # every term in self can be unified with a term in other, which + # is the definition of subsumption. + return [True] + else: + return [] + + +def clausify(expression): + """ + Skolemize, clausify, and standardize the variables apart. + """ + clause_list = [] + for clause in _clausify(skolemize(expression)): + for free in clause.free(): + if is_indvar(free.name): + newvar = VariableExpression(unique_variable()) + clause = clause.replace(free, newvar) + clause_list.append(clause) + return clause_list + + +def _clausify(expression): + """ + :param expression: a skolemized expression in CNF + """ + if isinstance(expression, AndExpression): + return _clausify(expression.first) + _clausify(expression.second) + elif isinstance(expression, OrExpression): + first = _clausify(expression.first) + second = _clausify(expression.second) + assert len(first) == 1 + assert len(second) == 1 + return [first[0] + second[0]] + elif isinstance(expression, EqualityExpression): + return [Clause([expression])] + elif isinstance(expression, ApplicationExpression): + return [Clause([expression])] + elif isinstance(expression, NegatedExpression): + if isinstance(expression.term, ApplicationExpression): + return [Clause([expression])] + elif isinstance(expression.term, EqualityExpression): + return [Clause([expression])] + raise ProverParseError() + + +class BindingDict: + def __init__(self, binding_list=None): + """ + :param binding_list: list of (``AbstractVariableExpression``, ``AtomicExpression``) to initialize the dictionary + """ + self.d = {} + + if binding_list: + for (v, b) in binding_list: + self[v] = b + + def __setitem__(self, variable, binding): + """ + A binding is consistent with the dict if its variable is not already bound, OR if its + variable is already bound to its argument. + + :param variable: ``Variable`` The variable to bind + :param binding: ``Expression`` The atomic to which 'variable' should be bound + :raise BindingException: If the variable cannot be bound in this dictionary + """ + assert isinstance(variable, Variable) + assert isinstance(binding, Expression) + + try: + existing = self[variable] + except KeyError: + existing = None + + if not existing or binding == existing: + self.d[variable] = binding + elif isinstance(binding, IndividualVariableExpression): + # Since variable is already bound, try to bind binding to variable + try: + existing = self[binding.variable] + except KeyError: + existing = None + + binding2 = VariableExpression(variable) + + if not existing or binding2 == existing: + self.d[binding.variable] = binding2 + else: + raise BindingException( + "Variable %s already bound to another " "value" % (variable) + ) + else: + raise BindingException( + "Variable %s already bound to another " "value" % (variable) + ) + + def __getitem__(self, variable): + """ + Return the expression to which 'variable' is bound + """ + assert isinstance(variable, Variable) + + intermediate = self.d[variable] + while intermediate: + try: + intermediate = self.d[intermediate] + except KeyError: + return intermediate + + def __contains__(self, item): + return item in self.d + + def __add__(self, other): + """ + :param other: ``BindingDict`` The dict with which to combine self + :return: ``BindingDict`` A new dict containing all the elements of both parameters + :raise BindingException: If the parameter dictionaries are not consistent with each other + """ + try: + combined = BindingDict() + for v in self.d: + combined[v] = self.d[v] + for v in other.d: + combined[v] = other.d[v] + return combined + except BindingException as e: + raise BindingException( + "Attempting to add two contradicting " + "BindingDicts: '%s' and '%s'" % (self, other) + ) from e + + def __len__(self): + return len(self.d) + + def __str__(self): + data_str = ", ".join(f"{v}: {self.d[v]}" for v in sorted(self.d.keys())) + return "{" + data_str + "}" + + def __repr__(self): + return "%s" % self + + +def most_general_unification(a, b, bindings=None): + """ + Find the most general unification of the two given expressions + + :param a: ``Expression`` + :param b: ``Expression`` + :param bindings: ``BindingDict`` a starting set of bindings with which the + unification must be consistent + :return: a list of bindings + :raise BindingException: if the Expressions cannot be unified + """ + if bindings is None: + bindings = BindingDict() + + if a == b: + return bindings + elif isinstance(a, IndividualVariableExpression): + return _mgu_var(a, b, bindings) + elif isinstance(b, IndividualVariableExpression): + return _mgu_var(b, a, bindings) + elif isinstance(a, ApplicationExpression) and isinstance(b, ApplicationExpression): + return most_general_unification( + a.function, b.function, bindings + ) + most_general_unification(a.argument, b.argument, bindings) + raise BindingException((a, b)) + + +def _mgu_var(var, expression, bindings): + if var.variable in expression.free() | expression.constants(): + raise BindingException((var, expression)) + else: + return BindingDict([(var.variable, expression)]) + bindings + + +class BindingException(Exception): + def __init__(self, arg): + if isinstance(arg, tuple): + Exception.__init__(self, "'%s' cannot be bound to '%s'" % arg) + else: + Exception.__init__(self, arg) + + +class UnificationException(Exception): + def __init__(self, a, b): + Exception.__init__(self, f"'{a}' cannot unify with '{b}'") + + +class DebugObject: + def __init__(self, enabled=True, indent=0): + self.enabled = enabled + self.indent = indent + + def __add__(self, i): + return DebugObject(self.enabled, self.indent + i) + + def line(self, line): + if self.enabled: + print(" " * self.indent + line) + + +def testResolutionProver(): + resolution_test(r"man(x)") + resolution_test(r"(man(x) -> man(x))") + resolution_test(r"(man(x) -> --man(x))") + resolution_test(r"-(man(x) and -man(x))") + resolution_test(r"(man(x) or -man(x))") + resolution_test(r"(man(x) -> man(x))") + resolution_test(r"-(man(x) and -man(x))") + resolution_test(r"(man(x) or -man(x))") + resolution_test(r"(man(x) -> man(x))") + resolution_test(r"(man(x) iff man(x))") + resolution_test(r"-(man(x) iff -man(x))") + resolution_test("all x.man(x)") + resolution_test("-all x.some y.F(x,y) & some x.all y.(-F(x,y))") + resolution_test("some x.all y.sees(x,y)") + + p1 = Expression.fromstring(r"all x.(man(x) -> mortal(x))") + p2 = Expression.fromstring(r"man(Socrates)") + c = Expression.fromstring(r"mortal(Socrates)") + print(f"{p1}, {p2} |- {c}: {ResolutionProver().prove(c, [p1, p2])}") + + p1 = Expression.fromstring(r"all x.(man(x) -> walks(x))") + p2 = Expression.fromstring(r"man(John)") + c = Expression.fromstring(r"some y.walks(y)") + print(f"{p1}, {p2} |- {c}: {ResolutionProver().prove(c, [p1, p2])}") + + p = Expression.fromstring(r"some e1.some e2.(believe(e1,john,e2) & walk(e2,mary))") + c = Expression.fromstring(r"some e0.walk(e0,mary)") + print(f"{p} |- {c}: {ResolutionProver().prove(c, [p])}") + + +def resolution_test(e): + f = Expression.fromstring(e) + t = ResolutionProver().prove(f) + print(f"|- {f}: {t}") + + +def test_clausify(): + lexpr = Expression.fromstring + + print(clausify(lexpr("P(x) | Q(x)"))) + print(clausify(lexpr("(P(x) & Q(x)) | R(x)"))) + print(clausify(lexpr("P(x) | (Q(x) & R(x))"))) + print(clausify(lexpr("(P(x) & Q(x)) | (R(x) & S(x))"))) + + print(clausify(lexpr("P(x) | Q(x) | R(x)"))) + print(clausify(lexpr("P(x) | (Q(x) & R(x)) | S(x)"))) + + print(clausify(lexpr("exists x.P(x) | Q(x)"))) + + print(clausify(lexpr("-(-P(x) & Q(x))"))) + print(clausify(lexpr("P(x) <-> Q(x)"))) + print(clausify(lexpr("-(P(x) <-> Q(x))"))) + print(clausify(lexpr("-(all x.P(x))"))) + print(clausify(lexpr("-(some x.P(x))"))) + + print(clausify(lexpr("some x.P(x)"))) + print(clausify(lexpr("some x.all y.P(x,y)"))) + print(clausify(lexpr("all y.some x.P(x,y)"))) + print(clausify(lexpr("all z.all y.some x.P(x,y,z)"))) + print(clausify(lexpr("all x.(all y.P(x,y) -> -all y.(Q(x,y) -> R(x,y)))"))) + + +def demo(): + test_clausify() + print() + testResolutionProver() + print() + + p = Expression.fromstring("man(x)") + print(ResolutionProverCommand(p, [p]).prove()) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/inference/tableau.py b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/tableau.py new file mode 100644 index 0000000..142f90d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/inference/tableau.py @@ -0,0 +1,712 @@ +# Natural Language Toolkit: First-Order Tableau Theorem Prover +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Dan Garrette +# +# URL: +# For license information, see LICENSE.TXT + +""" +Module for a tableau-based First Order theorem prover. +""" + +from nltk.inference.api import BaseProverCommand, Prover +from nltk.internals import Counter +from nltk.sem.logic import ( + AbstractVariableExpression, + AllExpression, + AndExpression, + ApplicationExpression, + EqualityExpression, + ExistsExpression, + Expression, + FunctionVariableExpression, + IffExpression, + ImpExpression, + LambdaExpression, + NegatedExpression, + OrExpression, + Variable, + VariableExpression, + unique_variable, +) + +_counter = Counter() + + +class ProverParseError(Exception): + pass + + +class TableauProver(Prover): + _assume_false = False + + def _prove(self, goal=None, assumptions=None, verbose=False): + if not assumptions: + assumptions = [] + + result = None + try: + agenda = Agenda() + if goal: + agenda.put(-goal) + agenda.put_all(assumptions) + debugger = Debug(verbose) + result = self._attempt_proof(agenda, set(), set(), debugger) + except RuntimeError as e: + if self._assume_false and str(e).startswith( + "maximum recursion depth exceeded" + ): + result = False + else: + if verbose: + print(e) + else: + raise e + return (result, "\n".join(debugger.lines)) + + def _attempt_proof(self, agenda, accessible_vars, atoms, debug): + (current, context), category = agenda.pop_first() + + # if there's nothing left in the agenda, and we haven't closed the path + if not current: + debug.line("AGENDA EMPTY") + return False + + proof_method = { + Categories.ATOM: self._attempt_proof_atom, + Categories.PROP: self._attempt_proof_prop, + Categories.N_ATOM: self._attempt_proof_n_atom, + Categories.N_PROP: self._attempt_proof_n_prop, + Categories.APP: self._attempt_proof_app, + Categories.N_APP: self._attempt_proof_n_app, + Categories.N_EQ: self._attempt_proof_n_eq, + Categories.D_NEG: self._attempt_proof_d_neg, + Categories.N_ALL: self._attempt_proof_n_all, + Categories.N_EXISTS: self._attempt_proof_n_some, + Categories.AND: self._attempt_proof_and, + Categories.N_OR: self._attempt_proof_n_or, + Categories.N_IMP: self._attempt_proof_n_imp, + Categories.OR: self._attempt_proof_or, + Categories.IMP: self._attempt_proof_imp, + Categories.N_AND: self._attempt_proof_n_and, + Categories.IFF: self._attempt_proof_iff, + Categories.N_IFF: self._attempt_proof_n_iff, + Categories.EQ: self._attempt_proof_eq, + Categories.EXISTS: self._attempt_proof_some, + Categories.ALL: self._attempt_proof_all, + }[category] + + debug.line((current, context)) + return proof_method(current, context, agenda, accessible_vars, atoms, debug) + + def _attempt_proof_atom( + self, current, context, agenda, accessible_vars, atoms, debug + ): + # Check if the branch is closed. Return 'True' if it is + if (current, True) in atoms: + debug.line("CLOSED", 1) + return True + + if context: + if isinstance(context.term, NegatedExpression): + current = current.negate() + agenda.put(context(current).simplify()) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + else: + # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, + accessible_vars | set(current.args), + atoms | {(current, False)}, + debug + 1, + ) + + def _attempt_proof_n_atom( + self, current, context, agenda, accessible_vars, atoms, debug + ): + # Check if the branch is closed. Return 'True' if it is + if (current.term, False) in atoms: + debug.line("CLOSED", 1) + return True + + if context: + if isinstance(context.term, NegatedExpression): + current = current.negate() + agenda.put(context(current).simplify()) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + else: + # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, + accessible_vars | set(current.term.args), + atoms | {(current.term, True)}, + debug + 1, + ) + + def _attempt_proof_prop( + self, current, context, agenda, accessible_vars, atoms, debug + ): + # Check if the branch is closed. Return 'True' if it is + if (current, True) in atoms: + debug.line("CLOSED", 1) + return True + + # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, accessible_vars, atoms | {(current, False)}, debug + 1 + ) + + def _attempt_proof_n_prop( + self, current, context, agenda, accessible_vars, atoms, debug + ): + # Check if the branch is closed. Return 'True' if it is + if (current.term, False) in atoms: + debug.line("CLOSED", 1) + return True + + # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, accessible_vars, atoms | {(current.term, True)}, debug + 1 + ) + + def _attempt_proof_app( + self, current, context, agenda, accessible_vars, atoms, debug + ): + f, args = current.uncurry() + for i, arg in enumerate(args): + if not TableauProver.is_atom(arg): + ctx = f + nv = Variable("X%s" % _counter.get()) + for j, a in enumerate(args): + ctx = ctx(VariableExpression(nv)) if i == j else ctx(a) + if context: + ctx = context(ctx).simplify() + ctx = LambdaExpression(nv, ctx) + agenda.put(arg, ctx) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + raise Exception("If this method is called, there must be a non-atomic argument") + + def _attempt_proof_n_app( + self, current, context, agenda, accessible_vars, atoms, debug + ): + f, args = current.term.uncurry() + for i, arg in enumerate(args): + if not TableauProver.is_atom(arg): + ctx = f + nv = Variable("X%s" % _counter.get()) + for j, a in enumerate(args): + ctx = ctx(VariableExpression(nv)) if i == j else ctx(a) + if context: + # combine new context with existing + ctx = context(ctx).simplify() + ctx = LambdaExpression(nv, -ctx) + agenda.put(-arg, ctx) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + raise Exception("If this method is called, there must be a non-atomic argument") + + def _attempt_proof_n_eq( + self, current, context, agenda, accessible_vars, atoms, debug + ): + ########################################################################### + # Since 'current' is of type '~(a=b)', the path is closed if 'a' == 'b' + ########################################################################### + if current.term.first == current.term.second: + debug.line("CLOSED", 1) + return True + + agenda[Categories.N_EQ].add((current, context)) + current._exhausted = True + return self._attempt_proof( + agenda, + accessible_vars | {current.term.first, current.term.second}, + atoms, + debug + 1, + ) + + def _attempt_proof_d_neg( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda.put(current.term.term, context) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_all( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda[Categories.EXISTS].add( + (ExistsExpression(current.term.variable, -current.term.term), context) + ) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_some( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda[Categories.ALL].add( + (AllExpression(current.term.variable, -current.term.term), context) + ) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_and( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda.put(current.first, context) + agenda.put(current.second, context) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_or( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda.put(-current.term.first, context) + agenda.put(-current.term.second, context) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_imp( + self, current, context, agenda, accessible_vars, atoms, debug + ): + agenda.put(current.term.first, context) + agenda.put(-current.term.second, context) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_or( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_agenda = agenda.clone() + agenda.put(current.first, context) + new_agenda.put(current.second, context) + return self._attempt_proof( + agenda, accessible_vars, atoms, debug + 1 + ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_imp( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_agenda = agenda.clone() + agenda.put(-current.first, context) + new_agenda.put(current.second, context) + return self._attempt_proof( + agenda, accessible_vars, atoms, debug + 1 + ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_and( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_agenda = agenda.clone() + agenda.put(-current.term.first, context) + new_agenda.put(-current.term.second, context) + return self._attempt_proof( + agenda, accessible_vars, atoms, debug + 1 + ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_iff( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_agenda = agenda.clone() + agenda.put(current.first, context) + agenda.put(current.second, context) + new_agenda.put(-current.first, context) + new_agenda.put(-current.second, context) + return self._attempt_proof( + agenda, accessible_vars, atoms, debug + 1 + ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_n_iff( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_agenda = agenda.clone() + agenda.put(current.term.first, context) + agenda.put(-current.term.second, context) + new_agenda.put(-current.term.first, context) + new_agenda.put(current.term.second, context) + return self._attempt_proof( + agenda, accessible_vars, atoms, debug + 1 + ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1) + + def _attempt_proof_eq( + self, current, context, agenda, accessible_vars, atoms, debug + ): + ######################################################################### + # Since 'current' is of the form '(a = b)', replace ALL free instances + # of 'a' with 'b' + ######################################################################### + agenda.put_atoms(atoms) + agenda.replace_all(current.first, current.second) + accessible_vars.discard(current.first) + agenda.mark_neqs_fresh() + return self._attempt_proof(agenda, accessible_vars, set(), debug + 1) + + def _attempt_proof_some( + self, current, context, agenda, accessible_vars, atoms, debug + ): + new_unique_variable = VariableExpression(unique_variable()) + agenda.put(current.term.replace(current.variable, new_unique_variable), context) + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, accessible_vars | {new_unique_variable}, atoms, debug + 1 + ) + + def _attempt_proof_all( + self, current, context, agenda, accessible_vars, atoms, debug + ): + try: + current._used_vars + except AttributeError: + current._used_vars = set() + + # if there are accessible_vars on the path + if accessible_vars: + # get the set of bound variables that have not be used by this AllExpression + bv_available = accessible_vars - current._used_vars + + if bv_available: + variable_to_use = list(bv_available)[0] + debug.line("--> Using '%s'" % variable_to_use, 2) + current._used_vars |= {variable_to_use} + agenda.put( + current.term.replace(current.variable, variable_to_use), context + ) + agenda[Categories.ALL].add((current, context)) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + else: + # no more available variables to substitute + debug.line("--> Variables Exhausted", 2) + current._exhausted = True + agenda[Categories.ALL].add((current, context)) + return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1) + + else: + new_unique_variable = VariableExpression(unique_variable()) + debug.line("--> Using '%s'" % new_unique_variable, 2) + current._used_vars |= {new_unique_variable} + agenda.put( + current.term.replace(current.variable, new_unique_variable), context + ) + agenda[Categories.ALL].add((current, context)) + agenda.mark_alls_fresh() + return self._attempt_proof( + agenda, accessible_vars | {new_unique_variable}, atoms, debug + 1 + ) + + @staticmethod + def is_atom(e): + if isinstance(e, NegatedExpression): + e = e.term + + if isinstance(e, ApplicationExpression): + for arg in e.args: + if not TableauProver.is_atom(arg): + return False + return True + elif isinstance(e, AbstractVariableExpression) or isinstance( + e, LambdaExpression + ): + return True + else: + return False + + +class TableauProverCommand(BaseProverCommand): + def __init__(self, goal=None, assumptions=None, prover=None): + """ + :param goal: Input expression to prove + :type goal: sem.Expression + :param assumptions: Input expressions to use as assumptions in + the proof. + :type assumptions: list(sem.Expression) + """ + if prover is not None: + assert isinstance(prover, TableauProver) + else: + prover = TableauProver() + + BaseProverCommand.__init__(self, prover, goal, assumptions) + + +class Agenda: + def __init__(self): + self.sets = tuple(set() for i in range(21)) + + def clone(self): + new_agenda = Agenda() + set_list = [s.copy() for s in self.sets] + + new_allExs = set() + for allEx, _ in set_list[Categories.ALL]: + new_allEx = AllExpression(allEx.variable, allEx.term) + try: + new_allEx._used_vars = {used for used in allEx._used_vars} + except AttributeError: + new_allEx._used_vars = set() + new_allExs.add((new_allEx, None)) + set_list[Categories.ALL] = new_allExs + + set_list[Categories.N_EQ] = { + (NegatedExpression(n_eq.term), ctx) + for (n_eq, ctx) in set_list[Categories.N_EQ] + } + + new_agenda.sets = tuple(set_list) + return new_agenda + + def __getitem__(self, index): + return self.sets[index] + + def put(self, expression, context=None): + if isinstance(expression, AllExpression): + ex_to_add = AllExpression(expression.variable, expression.term) + try: + ex_to_add._used_vars = {used for used in expression._used_vars} + except AttributeError: + ex_to_add._used_vars = set() + else: + ex_to_add = expression + self.sets[self._categorize_expression(ex_to_add)].add((ex_to_add, context)) + + def put_all(self, expressions): + for expression in expressions: + self.put(expression) + + def put_atoms(self, atoms): + for atom, neg in atoms: + if neg: + self[Categories.N_ATOM].add((-atom, None)) + else: + self[Categories.ATOM].add((atom, None)) + + def pop_first(self): + """Pop the first expression that appears in the agenda""" + for i, s in enumerate(self.sets): + if s: + if i in [Categories.N_EQ, Categories.ALL]: + for ex in s: + try: + if not ex[0]._exhausted: + s.remove(ex) + return (ex, i) + except AttributeError: + s.remove(ex) + return (ex, i) + else: + return (s.pop(), i) + return ((None, None), None) + + def replace_all(self, old, new): + for s in self.sets: + for ex, ctx in s: + ex.replace(old.variable, new) + if ctx is not None: + ctx.replace(old.variable, new) + + def mark_alls_fresh(self): + for u, _ in self.sets[Categories.ALL]: + u._exhausted = False + + def mark_neqs_fresh(self): + for neq, _ in self.sets[Categories.N_EQ]: + neq._exhausted = False + + def _categorize_expression(self, current): + if isinstance(current, NegatedExpression): + return self._categorize_NegatedExpression(current) + elif isinstance(current, FunctionVariableExpression): + return Categories.PROP + elif TableauProver.is_atom(current): + return Categories.ATOM + elif isinstance(current, AllExpression): + return Categories.ALL + elif isinstance(current, AndExpression): + return Categories.AND + elif isinstance(current, OrExpression): + return Categories.OR + elif isinstance(current, ImpExpression): + return Categories.IMP + elif isinstance(current, IffExpression): + return Categories.IFF + elif isinstance(current, EqualityExpression): + return Categories.EQ + elif isinstance(current, ExistsExpression): + return Categories.EXISTS + elif isinstance(current, ApplicationExpression): + return Categories.APP + else: + raise ProverParseError("cannot categorize %s" % current.__class__.__name__) + + def _categorize_NegatedExpression(self, current): + negated = current.term + + if isinstance(negated, NegatedExpression): + return Categories.D_NEG + elif isinstance(negated, FunctionVariableExpression): + return Categories.N_PROP + elif TableauProver.is_atom(negated): + return Categories.N_ATOM + elif isinstance(negated, AllExpression): + return Categories.N_ALL + elif isinstance(negated, AndExpression): + return Categories.N_AND + elif isinstance(negated, OrExpression): + return Categories.N_OR + elif isinstance(negated, ImpExpression): + return Categories.N_IMP + elif isinstance(negated, IffExpression): + return Categories.N_IFF + elif isinstance(negated, EqualityExpression): + return Categories.N_EQ + elif isinstance(negated, ExistsExpression): + return Categories.N_EXISTS + elif isinstance(negated, ApplicationExpression): + return Categories.N_APP + else: + raise ProverParseError("cannot categorize %s" % negated.__class__.__name__) + + +class Debug: + def __init__(self, verbose, indent=0, lines=None): + self.verbose = verbose + self.indent = indent + + if not lines: + lines = [] + self.lines = lines + + def __add__(self, increment): + return Debug(self.verbose, self.indent + 1, self.lines) + + def line(self, data, indent=0): + if isinstance(data, tuple): + ex, ctx = data + if ctx: + data = f"{ex}, {ctx}" + else: + data = "%s" % ex + + if isinstance(ex, AllExpression): + try: + used_vars = "[%s]" % ( + ",".join("%s" % ve.variable.name for ve in ex._used_vars) + ) + data += ": %s" % used_vars + except AttributeError: + data += ": []" + + newline = "{}{}".format(" " * (self.indent + indent), data) + self.lines.append(newline) + + if self.verbose: + print(newline) + + +class Categories: + ATOM = 0 + PROP = 1 + N_ATOM = 2 + N_PROP = 3 + APP = 4 + N_APP = 5 + N_EQ = 6 + D_NEG = 7 + N_ALL = 8 + N_EXISTS = 9 + AND = 10 + N_OR = 11 + N_IMP = 12 + OR = 13 + IMP = 14 + N_AND = 15 + IFF = 16 + N_IFF = 17 + EQ = 18 + EXISTS = 19 + ALL = 20 + + +def testTableauProver(): + tableau_test("P | -P") + tableau_test("P & -P") + tableau_test("Q", ["P", "(P -> Q)"]) + tableau_test("man(x)") + tableau_test("(man(x) -> man(x))") + tableau_test("(man(x) -> --man(x))") + tableau_test("-(man(x) and -man(x))") + tableau_test("(man(x) or -man(x))") + tableau_test("(man(x) -> man(x))") + tableau_test("-(man(x) and -man(x))") + tableau_test("(man(x) or -man(x))") + tableau_test("(man(x) -> man(x))") + tableau_test("(man(x) iff man(x))") + tableau_test("-(man(x) iff -man(x))") + tableau_test("all x.man(x)") + tableau_test("all x.all y.((x = y) -> (y = x))") + tableau_test("all x.all y.all z.(((x = y) & (y = z)) -> (x = z))") + # tableau_test('-all x.some y.F(x,y) & some x.all y.(-F(x,y))') + # tableau_test('some x.all y.sees(x,y)') + + p1 = "all x.(man(x) -> mortal(x))" + p2 = "man(Socrates)" + c = "mortal(Socrates)" + tableau_test(c, [p1, p2]) + + p1 = "all x.(man(x) -> walks(x))" + p2 = "man(John)" + c = "some y.walks(y)" + tableau_test(c, [p1, p2]) + + p = "((x = y) & walks(y))" + c = "walks(x)" + tableau_test(c, [p]) + + p = "((x = y) & ((y = z) & (z = w)))" + c = "(x = w)" + tableau_test(c, [p]) + + p = "some e1.some e2.(believe(e1,john,e2) & walk(e2,mary))" + c = "some e0.walk(e0,mary)" + tableau_test(c, [p]) + + c = "(exists x.exists z3.((x = Mary) & ((z3 = John) & sees(z3,x))) <-> exists x.exists z4.((x = John) & ((z4 = Mary) & sees(x,z4))))" + tableau_test(c) + + +# p = 'some e1.some e2.((believe e1 john e2) and (walk e2 mary))' +# c = 'some x.some e3.some e4.((believe e3 x e4) and (walk e4 mary))' +# tableau_test(c, [p]) + + +def testHigherOrderTableauProver(): + tableau_test("believe(j, -lie(b))", ["believe(j, -lie(b) & -cheat(b))"]) + tableau_test("believe(j, lie(b) & cheat(b))", ["believe(j, lie(b))"]) + tableau_test( + "believe(j, lie(b))", ["lie(b)"] + ) # how do we capture that John believes all things that are true + tableau_test( + "believe(j, know(b, cheat(b)))", + ["believe(j, know(b, lie(b)) & know(b, steals(b) & cheat(b)))"], + ) + tableau_test("P(Q(y), R(y) & R(z))", ["P(Q(x) & Q(y), R(y) & R(z))"]) + + tableau_test("believe(j, cheat(b) & lie(b))", ["believe(j, lie(b) & cheat(b))"]) + tableau_test("believe(j, -cheat(b) & -lie(b))", ["believe(j, -lie(b) & -cheat(b))"]) + + +def tableau_test(c, ps=None, verbose=False): + pc = Expression.fromstring(c) + pps = [Expression.fromstring(p) for p in ps] if ps else [] + if not ps: + ps = [] + print( + "%s |- %s: %s" + % (", ".join(ps), pc, TableauProver().prove(pc, pps, verbose=verbose)) + ) + + +def demo(): + testTableauProver() + testHigherOrderTableauProver() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/internals.py b/.pythonlibs/lib/python3.10/site-packages/nltk/internals.py new file mode 100644 index 0000000..db64f7e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/internals.py @@ -0,0 +1,1123 @@ +# Natural Language Toolkit: Internal utility functions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# Nitin Madnani +# URL: +# For license information, see LICENSE.TXT + +import fnmatch +import locale +import os +import re +import stat +import subprocess +import sys +import textwrap +import types +import warnings +from xml.etree import ElementTree + +########################################################################## +# Java Via Command-Line +########################################################################## + +_java_bin = None +_java_options = [] +# [xx] add classpath option to config_java? +def config_java(bin=None, options=None, verbose=False): + """ + Configure nltk's java interface, by letting nltk know where it can + find the Java binary, and what extra options (if any) should be + passed to Java when it is run. + + :param bin: The full path to the Java binary. If not specified, + then nltk will search the system for a Java binary; and if + one is not found, it will raise a ``LookupError`` exception. + :type bin: str + :param options: A list of options that should be passed to the + Java binary when it is called. A common value is + ``'-Xmx512m'``, which tells Java binary to increase + the maximum heap size to 512 megabytes. If no options are + specified, then do not modify the options list. + :type options: list(str) + """ + global _java_bin, _java_options + _java_bin = find_binary( + "java", + bin, + env_vars=["JAVAHOME", "JAVA_HOME"], + verbose=verbose, + binary_names=["java.exe"], + ) + + if options is not None: + if isinstance(options, str): + options = options.split() + _java_options = list(options) + + +def java(cmd, classpath=None, stdin=None, stdout=None, stderr=None, blocking=True): + """ + Execute the given java command, by opening a subprocess that calls + Java. If java has not yet been configured, it will be configured + by calling ``config_java()`` with no arguments. + + :param cmd: The java command that should be called, formatted as + a list of strings. Typically, the first string will be the name + of the java class; and the remaining strings will be arguments + for that java class. + :type cmd: list(str) + + :param classpath: A ``':'`` separated list of directories, JAR + archives, and ZIP archives to search for class files. + :type classpath: str + + :param stdin: Specify the executed program's + standard input file handles, respectively. Valid values are ``subprocess.PIPE``, + an existing file descriptor (a positive integer), an existing + file object, 'pipe', 'stdout', 'devnull' and None. ``subprocess.PIPE`` indicates that a + new pipe to the child should be created. With None, no + redirection will occur; the child's file handles will be + inherited from the parent. Additionally, stderr can be + ``subprocess.STDOUT``, which indicates that the stderr data + from the applications should be captured into the same file + handle as for stdout. + + :param stdout: Specify the executed program's standard output file + handle. See ``stdin`` for valid values. + + :param stderr: Specify the executed program's standard error file + handle. See ``stdin`` for valid values. + + + :param blocking: If ``false``, then return immediately after + spawning the subprocess. In this case, the return value is + the ``Popen`` object, and not a ``(stdout, stderr)`` tuple. + + :return: If ``blocking=True``, then return a tuple ``(stdout, + stderr)``, containing the stdout and stderr outputs generated + by the java command if the ``stdout`` and ``stderr`` parameters + were set to ``subprocess.PIPE``; or None otherwise. If + ``blocking=False``, then return a ``subprocess.Popen`` object. + + :raise OSError: If the java command returns a nonzero return code. + """ + + subprocess_output_dict = { + "pipe": subprocess.PIPE, + "stdout": subprocess.STDOUT, + "devnull": subprocess.DEVNULL, + } + + stdin = subprocess_output_dict.get(stdin, stdin) + stdout = subprocess_output_dict.get(stdout, stdout) + stderr = subprocess_output_dict.get(stderr, stderr) + + if isinstance(cmd, str): + raise TypeError("cmd should be a list of strings") + + # Make sure we know where a java binary is. + if _java_bin is None: + config_java() + + # Set up the classpath. + if isinstance(classpath, str): + classpaths = [classpath] + else: + classpaths = list(classpath) + classpath = os.path.pathsep.join(classpaths) + + # Construct the full command string. + cmd = list(cmd) + cmd = ["-cp", classpath] + cmd + cmd = [_java_bin] + _java_options + cmd + + # Call java via a subprocess + p = subprocess.Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr) + if not blocking: + return p + (stdout, stderr) = p.communicate() + + # Check the return code. + if p.returncode != 0: + print(_decode_stdoutdata(stderr)) + raise OSError("Java command failed : " + str(cmd)) + + return (stdout, stderr) + + +###################################################################### +# Parsing +###################################################################### + + +class ReadError(ValueError): + """ + Exception raised by read_* functions when they fail. + :param position: The index in the input string where an error occurred. + :param expected: What was expected when an error occurred. + """ + + def __init__(self, expected, position): + ValueError.__init__(self, expected, position) + self.expected = expected + self.position = position + + def __str__(self): + return f"Expected {self.expected} at {self.position}" + + +_STRING_START_RE = re.compile(r"[uU]?[rR]?(\"\"\"|\'\'\'|\"|\')") + + +def read_str(s, start_position): + """ + If a Python string literal begins at the specified position in the + given string, then return a tuple ``(val, end_position)`` + containing the value of the string literal and the position where + it ends. Otherwise, raise a ``ReadError``. + + :param s: A string that will be checked to see if within which a + Python string literal exists. + :type s: str + + :param start_position: The specified beginning position of the string ``s`` + to begin regex matching. + :type start_position: int + + :return: A tuple containing the matched string literal evaluated as a + string and the end position of the string literal. + :rtype: tuple(str, int) + + :raise ReadError: If the ``_STRING_START_RE`` regex doesn't return a + match in ``s`` at ``start_position``, i.e., open quote. If the + ``_STRING_END_RE`` regex doesn't return a match in ``s`` at the + end of the first match, i.e., close quote. + :raise ValueError: If an invalid string (i.e., contains an invalid + escape sequence) is passed into the ``eval``. + + :Example: + + >>> from nltk.internals import read_str + >>> read_str('"Hello", World!', 0) + ('Hello', 7) + + """ + # Read the open quote, and any modifiers. + m = _STRING_START_RE.match(s, start_position) + if not m: + raise ReadError("open quote", start_position) + quotemark = m.group(1) + + # Find the close quote. + _STRING_END_RE = re.compile(r"\\|%s" % quotemark) + position = m.end() + while True: + match = _STRING_END_RE.search(s, position) + if not match: + raise ReadError("close quote", position) + if match.group(0) == "\\": + position = match.end() + 1 + else: + break + + # Process it, using eval. Strings with invalid escape sequences + # might raise ValueError. + try: + return eval(s[start_position : match.end()]), match.end() + except ValueError as e: + raise ReadError("valid escape sequence", start_position) from e + + +_READ_INT_RE = re.compile(r"-?\d+") + + +def read_int(s, start_position): + """ + If an integer begins at the specified position in the given + string, then return a tuple ``(val, end_position)`` containing the + value of the integer and the position where it ends. Otherwise, + raise a ``ReadError``. + + :param s: A string that will be checked to see if within which a + Python integer exists. + :type s: str + + :param start_position: The specified beginning position of the string ``s`` + to begin regex matching. + :type start_position: int + + :return: A tuple containing the matched integer casted to an int, + and the end position of the int in ``s``. + :rtype: tuple(int, int) + + :raise ReadError: If the ``_READ_INT_RE`` regex doesn't return a + match in ``s`` at ``start_position``. + + :Example: + + >>> from nltk.internals import read_int + >>> read_int('42 is the answer', 0) + (42, 2) + + """ + m = _READ_INT_RE.match(s, start_position) + if not m: + raise ReadError("integer", start_position) + return int(m.group()), m.end() + + +_READ_NUMBER_VALUE = re.compile(r"-?(\d*)([.]?\d*)?") + + +def read_number(s, start_position): + """ + If an integer or float begins at the specified position in the + given string, then return a tuple ``(val, end_position)`` + containing the value of the number and the position where it ends. + Otherwise, raise a ``ReadError``. + + :param s: A string that will be checked to see if within which a + Python number exists. + :type s: str + + :param start_position: The specified beginning position of the string ``s`` + to begin regex matching. + :type start_position: int + + :return: A tuple containing the matched number casted to a ``float``, + and the end position of the number in ``s``. + :rtype: tuple(float, int) + + :raise ReadError: If the ``_READ_NUMBER_VALUE`` regex doesn't return a + match in ``s`` at ``start_position``. + + :Example: + + >>> from nltk.internals import read_number + >>> read_number('Pi is 3.14159', 6) + (3.14159, 13) + + """ + m = _READ_NUMBER_VALUE.match(s, start_position) + if not m or not (m.group(1) or m.group(2)): + raise ReadError("number", start_position) + if m.group(2): + return float(m.group()), m.end() + else: + return int(m.group()), m.end() + + +###################################################################### +# Check if a method has been overridden +###################################################################### + + +def overridden(method): + """ + :return: True if ``method`` overrides some method with the same + name in a base class. This is typically used when defining + abstract base classes or interfaces, to allow subclasses to define + either of two related methods: + + >>> class EaterI: + ... '''Subclass must define eat() or batch_eat().''' + ... def eat(self, food): + ... if overridden(self.batch_eat): + ... return self.batch_eat([food])[0] + ... else: + ... raise NotImplementedError() + ... def batch_eat(self, foods): + ... return [self.eat(food) for food in foods] + + :type method: instance method + """ + if isinstance(method, types.MethodType) and method.__self__.__class__ is not None: + name = method.__name__ + funcs = [ + cls.__dict__[name] + for cls in _mro(method.__self__.__class__) + if name in cls.__dict__ + ] + return len(funcs) > 1 + else: + raise TypeError("Expected an instance method.") + + +def _mro(cls): + """ + Return the method resolution order for ``cls`` -- i.e., a list + containing ``cls`` and all its base classes, in the order in which + they would be checked by ``getattr``. For new-style classes, this + is just cls.__mro__. For classic classes, this can be obtained by + a depth-first left-to-right traversal of ``__bases__``. + """ + if isinstance(cls, type): + return cls.__mro__ + else: + mro = [cls] + for base in cls.__bases__: + mro.extend(_mro(base)) + return mro + + +###################################################################### +# Deprecation decorator & base class +###################################################################### +# [xx] dedent msg first if it comes from a docstring. + + +def _add_epytext_field(obj, field, message): + """Add an epytext @field to a given object's docstring.""" + indent = "" + # If we already have a docstring, then add a blank line to separate + # it from the new field, and check its indentation. + if obj.__doc__: + obj.__doc__ = obj.__doc__.rstrip() + "\n\n" + indents = re.findall(r"(?<=\n)[ ]+(?!\s)", obj.__doc__.expandtabs()) + if indents: + indent = min(indents) + # If we don't have a docstring, add an empty one. + else: + obj.__doc__ = "" + + obj.__doc__ += textwrap.fill( + f"@{field}: {message}", + initial_indent=indent, + subsequent_indent=indent + " ", + ) + + +def deprecated(message): + """ + A decorator used to mark functions as deprecated. This will cause + a warning to be printed the when the function is used. Usage: + + >>> from nltk.internals import deprecated + >>> @deprecated('Use foo() instead') + ... def bar(x): + ... print(x/10) + + """ + + def decorator(func): + msg = f"Function {func.__name__}() has been deprecated. {message}" + msg = "\n" + textwrap.fill(msg, initial_indent=" ", subsequent_indent=" ") + + def newFunc(*args, **kwargs): + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + return func(*args, **kwargs) + + # Copy the old function's name, docstring, & dict + newFunc.__dict__.update(func.__dict__) + newFunc.__name__ = func.__name__ + newFunc.__doc__ = func.__doc__ + newFunc.__deprecated__ = True + # Add a @deprecated field to the docstring. + _add_epytext_field(newFunc, "deprecated", message) + return newFunc + + return decorator + + +class Deprecated: + """ + A base class used to mark deprecated classes. A typical usage is to + alert users that the name of a class has changed: + + >>> from nltk.internals import Deprecated + >>> class NewClassName: + ... pass # All logic goes here. + ... + >>> class OldClassName(Deprecated, NewClassName): + ... "Use NewClassName instead." + + The docstring of the deprecated class will be used in the + deprecation warning message. + """ + + def __new__(cls, *args, **kwargs): + # Figure out which class is the deprecated one. + dep_cls = None + for base in _mro(cls): + if Deprecated in base.__bases__: + dep_cls = base + break + assert dep_cls, "Unable to determine which base is deprecated." + + # Construct an appropriate warning. + doc = dep_cls.__doc__ or "".strip() + # If there's a @deprecated field, strip off the field marker. + doc = re.sub(r"\A\s*@deprecated:", r"", doc) + # Strip off any indentation. + doc = re.sub(r"(?m)^\s*", "", doc) + # Construct a 'name' string. + name = "Class %s" % dep_cls.__name__ + if cls != dep_cls: + name += " (base class for %s)" % cls.__name__ + # Put it all together. + msg = f"{name} has been deprecated. {doc}" + # Wrap it. + msg = "\n" + textwrap.fill(msg, initial_indent=" ", subsequent_indent=" ") + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + # Do the actual work of __new__. + return object.__new__(cls) + + +########################################################################## +# COUNTER, FOR UNIQUE NAMING +########################################################################## + + +class Counter: + """ + A counter that auto-increments each time its value is read. + """ + + def __init__(self, initial_value=0): + self._value = initial_value + + def get(self): + self._value += 1 + return self._value + + +########################################################################## +# Search for files/binaries +########################################################################## + + +def find_file_iter( + filename, + env_vars=(), + searchpath=(), + file_names=None, + url=None, + verbose=False, + finding_dir=False, +): + """ + Search for a file to be used by nltk. + + :param filename: The name or path of the file. + :param env_vars: A list of environment variable names to check. + :param file_names: A list of alternative file names to check. + :param searchpath: List of directories to search. + :param url: URL presented to user for download help. + :param verbose: Whether or not to print path when a file is found. + """ + file_names = [filename] + (file_names or []) + assert isinstance(filename, str) + assert not isinstance(file_names, str) + assert not isinstance(searchpath, str) + if isinstance(env_vars, str): + env_vars = env_vars.split() + yielded = False + + # File exists, no magic + for alternative in file_names: + path_to_file = os.path.join(filename, alternative) + if os.path.isfile(path_to_file): + if verbose: + print(f"[Found {filename}: {path_to_file}]") + yielded = True + yield path_to_file + # Check the bare alternatives + if os.path.isfile(alternative): + if verbose: + print(f"[Found {filename}: {alternative}]") + yielded = True + yield alternative + # Check if the alternative is inside a 'file' directory + path_to_file = os.path.join(filename, "file", alternative) + if os.path.isfile(path_to_file): + if verbose: + print(f"[Found {filename}: {path_to_file}]") + yielded = True + yield path_to_file + + # Check environment variables + for env_var in env_vars: + if env_var in os.environ: + if finding_dir: # This is to file a directory instead of file + yielded = True + yield os.environ[env_var] + + for env_dir in os.environ[env_var].split(os.pathsep): + # Check if the environment variable contains a direct path to the bin + if os.path.isfile(env_dir): + if verbose: + print(f"[Found {filename}: {env_dir}]") + yielded = True + yield env_dir + # Check if the possible bin names exist inside the environment variable directories + for alternative in file_names: + path_to_file = os.path.join(env_dir, alternative) + if os.path.isfile(path_to_file): + if verbose: + print(f"[Found {filename}: {path_to_file}]") + yielded = True + yield path_to_file + # Check if the alternative is inside a 'file' directory + # path_to_file = os.path.join(env_dir, 'file', alternative) + + # Check if the alternative is inside a 'bin' directory + path_to_file = os.path.join(env_dir, "bin", alternative) + + if os.path.isfile(path_to_file): + if verbose: + print(f"[Found {filename}: {path_to_file}]") + yielded = True + yield path_to_file + + # Check the path list. + for directory in searchpath: + for alternative in file_names: + path_to_file = os.path.join(directory, alternative) + if os.path.isfile(path_to_file): + yielded = True + yield path_to_file + + # If we're on a POSIX system, then try using the 'which' command + # to find the file. + if os.name == "posix": + for alternative in file_names: + try: + p = subprocess.Popen( + ["which", alternative], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = p.communicate() + path = _decode_stdoutdata(stdout).strip() + if path.endswith(alternative) and os.path.exists(path): + if verbose: + print(f"[Found {filename}: {path}]") + yielded = True + yield path + except (KeyboardInterrupt, SystemExit, OSError): + raise + finally: + pass + + if not yielded: + msg = ( + "NLTK was unable to find the %s file!" + "\nUse software specific " + "configuration parameters" % filename + ) + if env_vars: + msg += " or set the %s environment variable" % env_vars[0] + msg += "." + if searchpath: + msg += "\n\n Searched in:" + msg += "".join("\n - %s" % d for d in searchpath) + if url: + msg += f"\n\n For more information on {filename}, see:\n <{url}>" + div = "=" * 75 + raise LookupError(f"\n\n{div}\n{msg}\n{div}") + + +def find_file( + filename, env_vars=(), searchpath=(), file_names=None, url=None, verbose=False +): + return next( + find_file_iter(filename, env_vars, searchpath, file_names, url, verbose) + ) + + +def find_dir( + filename, env_vars=(), searchpath=(), file_names=None, url=None, verbose=False +): + return next( + find_file_iter( + filename, env_vars, searchpath, file_names, url, verbose, finding_dir=True + ) + ) + + +def find_binary_iter( + name, + path_to_bin=None, + env_vars=(), + searchpath=(), + binary_names=None, + url=None, + verbose=False, +): + """ + Search for a file to be used by nltk. + + :param name: The name or path of the file. + :param path_to_bin: The user-supplied binary location (deprecated) + :param env_vars: A list of environment variable names to check. + :param file_names: A list of alternative file names to check. + :param searchpath: List of directories to search. + :param url: URL presented to user for download help. + :param verbose: Whether or not to print path when a file is found. + """ + yield from find_file_iter( + path_to_bin or name, env_vars, searchpath, binary_names, url, verbose + ) + + +def find_binary( + name, + path_to_bin=None, + env_vars=(), + searchpath=(), + binary_names=None, + url=None, + verbose=False, +): + return next( + find_binary_iter( + name, path_to_bin, env_vars, searchpath, binary_names, url, verbose + ) + ) + + +def find_jar_iter( + name_pattern, + path_to_jar=None, + env_vars=(), + searchpath=(), + url=None, + verbose=False, + is_regex=False, +): + """ + Search for a jar that is used by nltk. + + :param name_pattern: The name of the jar file + :param path_to_jar: The user-supplied jar location, or None. + :param env_vars: A list of environment variable names to check + in addition to the CLASSPATH variable which is + checked by default. + :param searchpath: List of directories to search. + :param is_regex: Whether name is a regular expression. + """ + + assert isinstance(name_pattern, str) + assert not isinstance(searchpath, str) + if isinstance(env_vars, str): + env_vars = env_vars.split() + yielded = False + + # Make sure we check the CLASSPATH first + env_vars = ["CLASSPATH"] + list(env_vars) + + # If an explicit location was given, then check it, and yield it if + # it's present; otherwise, complain. + if path_to_jar is not None: + if os.path.isfile(path_to_jar): + yielded = True + yield path_to_jar + else: + raise LookupError( + f"Could not find {name_pattern} jar file at {path_to_jar}" + ) + + # Check environment variables + for env_var in env_vars: + if env_var in os.environ: + if env_var == "CLASSPATH": + classpath = os.environ["CLASSPATH"] + for cp in classpath.split(os.path.pathsep): + cp = os.path.expanduser(cp) + if os.path.isfile(cp): + filename = os.path.basename(cp) + if ( + is_regex + and re.match(name_pattern, filename) + or (not is_regex and filename == name_pattern) + ): + if verbose: + print(f"[Found {name_pattern}: {cp}]") + yielded = True + yield cp + # The case where user put directory containing the jar file in the classpath + if os.path.isdir(cp): + if not is_regex: + if os.path.isfile(os.path.join(cp, name_pattern)): + if verbose: + print(f"[Found {name_pattern}: {cp}]") + yielded = True + yield os.path.join(cp, name_pattern) + else: + # Look for file using regular expression + for file_name in os.listdir(cp): + if re.match(name_pattern, file_name): + if verbose: + print( + "[Found %s: %s]" + % ( + name_pattern, + os.path.join(cp, file_name), + ) + ) + yielded = True + yield os.path.join(cp, file_name) + + else: + jar_env = os.path.expanduser(os.environ[env_var]) + jar_iter = ( + ( + os.path.join(jar_env, path_to_jar) + for path_to_jar in os.listdir(jar_env) + ) + if os.path.isdir(jar_env) + else (jar_env,) + ) + for path_to_jar in jar_iter: + if os.path.isfile(path_to_jar): + filename = os.path.basename(path_to_jar) + if ( + is_regex + and re.match(name_pattern, filename) + or (not is_regex and filename == name_pattern) + ): + if verbose: + print(f"[Found {name_pattern}: {path_to_jar}]") + yielded = True + yield path_to_jar + + # Check the path list. + for directory in searchpath: + if is_regex: + for filename in os.listdir(directory): + path_to_jar = os.path.join(directory, filename) + if os.path.isfile(path_to_jar): + if re.match(name_pattern, filename): + if verbose: + print(f"[Found {filename}: {path_to_jar}]") + yielded = True + yield path_to_jar + else: + path_to_jar = os.path.join(directory, name_pattern) + if os.path.isfile(path_to_jar): + if verbose: + print(f"[Found {name_pattern}: {path_to_jar}]") + yielded = True + yield path_to_jar + + if not yielded: + # If nothing was found, raise an error + msg = "NLTK was unable to find %s!" % name_pattern + if env_vars: + msg += " Set the %s environment variable" % env_vars[0] + msg = textwrap.fill(msg + ".", initial_indent=" ", subsequent_indent=" ") + if searchpath: + msg += "\n\n Searched in:" + msg += "".join("\n - %s" % d for d in searchpath) + if url: + msg += "\n\n For more information, on {}, see:\n <{}>".format( + name_pattern, + url, + ) + div = "=" * 75 + raise LookupError(f"\n\n{div}\n{msg}\n{div}") + + +def find_jar( + name_pattern, + path_to_jar=None, + env_vars=(), + searchpath=(), + url=None, + verbose=False, + is_regex=False, +): + return next( + find_jar_iter( + name_pattern, path_to_jar, env_vars, searchpath, url, verbose, is_regex + ) + ) + + +def find_jars_within_path(path_to_jars): + return [ + os.path.join(root, filename) + for root, dirnames, filenames in os.walk(path_to_jars) + for filename in fnmatch.filter(filenames, "*.jar") + ] + + +def _decode_stdoutdata(stdoutdata): + """Convert data read from stdout/stderr to unicode""" + if not isinstance(stdoutdata, bytes): + return stdoutdata + + encoding = getattr(sys.__stdout__, "encoding", locale.getpreferredencoding()) + if encoding is None: + return stdoutdata.decode() + return stdoutdata.decode(encoding) + + +########################################################################## +# Import Stdlib Module +########################################################################## + + +def import_from_stdlib(module): + """ + When python is run from within the nltk/ directory tree, the + current directory is included at the beginning of the search path. + Unfortunately, that means that modules within nltk can sometimes + shadow standard library modules. As an example, the stdlib + 'inspect' module will attempt to import the stdlib 'tokenize' + module, but will instead end up importing NLTK's 'tokenize' module + instead (causing the import to fail). + """ + old_path = sys.path + sys.path = [d for d in sys.path if d not in ("", ".")] + m = __import__(module) + sys.path = old_path + return m + + +########################################################################## +# Wrapper for ElementTree Elements +########################################################################## + + +class ElementWrapper: + """ + A wrapper around ElementTree Element objects whose main purpose is + to provide nicer __repr__ and __str__ methods. In addition, any + of the wrapped Element's methods that return other Element objects + are overridden to wrap those values before returning them. + + This makes Elements more convenient to work with in + interactive sessions and doctests, at the expense of some + efficiency. + """ + + # Prevent double-wrapping: + def __new__(cls, etree): + """ + Create and return a wrapper around a given Element object. + If ``etree`` is an ``ElementWrapper``, then ``etree`` is + returned as-is. + """ + if isinstance(etree, ElementWrapper): + return etree + else: + return object.__new__(ElementWrapper) + + def __init__(self, etree): + r""" + Initialize a new Element wrapper for ``etree``. + + If ``etree`` is a string, then it will be converted to an + Element object using ``ElementTree.fromstring()`` first: + + >>> ElementWrapper("") + \n"> + + """ + if isinstance(etree, str): + etree = ElementTree.fromstring(etree) + self.__dict__["_etree"] = etree + + def unwrap(self): + """ + Return the Element object wrapped by this wrapper. + """ + return self._etree + + ##//////////////////////////////////////////////////////////// + # { String Representation + ##//////////////////////////////////////////////////////////// + + def __repr__(self): + s = ElementTree.tostring(self._etree, encoding="utf8").decode("utf8") + if len(s) > 60: + e = s.rfind("<") + if (len(s) - e) > 30: + e = -20 + s = f"{s[:30]}...{s[e:]}" + return "" % s + + def __str__(self): + """ + :return: the result of applying ``ElementTree.tostring()`` to + the wrapped Element object. + """ + return ( + ElementTree.tostring(self._etree, encoding="utf8").decode("utf8").rstrip() + ) + + ##//////////////////////////////////////////////////////////// + # { Element interface Delegation (pass-through) + ##//////////////////////////////////////////////////////////// + + def __getattr__(self, attrib): + return getattr(self._etree, attrib) + + def __setattr__(self, attr, value): + return setattr(self._etree, attr, value) + + def __delattr__(self, attr): + return delattr(self._etree, attr) + + def __setitem__(self, index, element): + self._etree[index] = element + + def __delitem__(self, index): + del self._etree[index] + + def __setslice__(self, start, stop, elements): + self._etree[start:stop] = elements + + def __delslice__(self, start, stop): + del self._etree[start:stop] + + def __len__(self): + return len(self._etree) + + ##//////////////////////////////////////////////////////////// + # { Element interface Delegation (wrap result) + ##//////////////////////////////////////////////////////////// + + def __getitem__(self, index): + return ElementWrapper(self._etree[index]) + + def __getslice__(self, start, stop): + return [ElementWrapper(elt) for elt in self._etree[start:stop]] + + def getchildren(self): + return [ElementWrapper(elt) for elt in self._etree] + + def getiterator(self, tag=None): + return (ElementWrapper(elt) for elt in self._etree.getiterator(tag)) + + def makeelement(self, tag, attrib): + return ElementWrapper(self._etree.makeelement(tag, attrib)) + + def find(self, path): + elt = self._etree.find(path) + if elt is None: + return elt + else: + return ElementWrapper(elt) + + def findall(self, path): + return [ElementWrapper(elt) for elt in self._etree.findall(path)] + + +###################################################################### +# Helper for Handling Slicing +###################################################################### + + +def slice_bounds(sequence, slice_obj, allow_step=False): + """ + Given a slice, return the corresponding (start, stop) bounds, + taking into account None indices and negative indices. The + following guarantees are made for the returned start and stop values: + + - 0 <= start <= len(sequence) + - 0 <= stop <= len(sequence) + - start <= stop + + :raise ValueError: If ``slice_obj.step`` is not None. + :param allow_step: If true, then the slice object may have a + non-None step. If it does, then return a tuple + (start, stop, step). + """ + start, stop = (slice_obj.start, slice_obj.stop) + + # If allow_step is true, then include the step in our return + # value tuple. + if allow_step: + step = slice_obj.step + if step is None: + step = 1 + # Use a recursive call without allow_step to find the slice + # bounds. If step is negative, then the roles of start and + # stop (in terms of default values, etc), are swapped. + if step < 0: + start, stop = slice_bounds(sequence, slice(stop, start)) + else: + start, stop = slice_bounds(sequence, slice(start, stop)) + return start, stop, step + + # Otherwise, make sure that no non-default step value is used. + elif slice_obj.step not in (None, 1): + raise ValueError( + "slices with steps are not supported by %s" % sequence.__class__.__name__ + ) + + # Supply default offsets. + if start is None: + start = 0 + if stop is None: + stop = len(sequence) + + # Handle negative indices. + if start < 0: + start = max(0, len(sequence) + start) + if stop < 0: + stop = max(0, len(sequence) + stop) + + # Make sure stop doesn't go past the end of the list. Note that + # we avoid calculating len(sequence) if possible, because for lazy + # sequences, calculating the length of a sequence can be expensive. + if stop > 0: + try: + sequence[stop - 1] + except IndexError: + stop = len(sequence) + + # Make sure start isn't past stop. + start = min(start, stop) + + # That's all folks! + return start, stop + + +###################################################################### +# Permission Checking +###################################################################### + + +def is_writable(path): + # Ensure that it exists. + if not os.path.exists(path): + return False + + # If we're on a posix system, check its permissions. + if hasattr(os, "getuid"): + statdata = os.stat(path) + perm = stat.S_IMODE(statdata.st_mode) + # is it world-writable? + if perm & 0o002: + return True + # do we own it? + elif statdata.st_uid == os.getuid() and (perm & 0o200): + return True + # are we in a group that can write to it? + elif (statdata.st_gid in [os.getgid()] + os.getgroups()) and (perm & 0o020): + return True + # otherwise, we can't write to it. + else: + return False + + # Otherwise, we'll assume it's writable. + # [xx] should we do other checks on other platforms? + return True + + +###################################################################### +# NLTK Error reporting +###################################################################### + + +def raise_unorderable_types(ordering, a, b): + raise TypeError( + "unorderable types: %s() %s %s()" + % (type(a).__name__, ordering, type(b).__name__) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/jsontags.py b/.pythonlibs/lib/python3.10/site-packages/nltk/jsontags.py new file mode 100644 index 0000000..2f0c932 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/jsontags.py @@ -0,0 +1,65 @@ +# Natural Language Toolkit: JSON Encoder/Decoder Helpers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Xu +# +# URL: +# For license information, see LICENSE.TXT + +""" +Register JSON tags, so the nltk data loader knows what module and class to look for. + +NLTK uses simple '!' tags to mark the types of objects, but the fully-qualified +"tag:nltk.org,2011:" prefix is also accepted in case anyone ends up +using it. +""" + +import json + +json_tags = {} + +TAG_PREFIX = "!" + + +def register_tag(cls): + """ + Decorates a class to register it's json tag. + """ + json_tags[TAG_PREFIX + getattr(cls, "json_tag")] = cls + return cls + + +class JSONTaggedEncoder(json.JSONEncoder): + def default(self, obj): + obj_tag = getattr(obj, "json_tag", None) + if obj_tag is None: + return super().default(obj) + obj_tag = TAG_PREFIX + obj_tag + obj = obj.encode_json_obj() + return {obj_tag: obj} + + +class JSONTaggedDecoder(json.JSONDecoder): + def decode(self, s): + return self.decode_obj(super().decode(s)) + + @classmethod + def decode_obj(cls, obj): + # Decode nested objects first. + if isinstance(obj, dict): + obj = {key: cls.decode_obj(val) for (key, val) in obj.items()} + elif isinstance(obj, list): + obj = list(cls.decode_obj(val) for val in obj) + # Check if we have a tagged object. + if not isinstance(obj, dict) or len(obj) != 1: + return obj + obj_tag = next(iter(obj.keys())) + if not obj_tag.startswith("!"): + return obj + if obj_tag not in json_tags: + raise ValueError("Unknown tag", obj_tag) + obj_cls = json_tags[obj_tag] + return obj_cls.decode_json_obj(obj[obj_tag]) + + +__all__ = ["register_tag", "json_tags", "JSONTaggedEncoder", "JSONTaggedDecoder"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/langnames.py b/.pythonlibs/lib/python3.10/site-packages/nltk/langnames.py new file mode 100644 index 0000000..3db2093 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/langnames.py @@ -0,0 +1,730 @@ +# Natural Language Toolkit: Language Codes +# +# Copyright (C) 2022-2023 NLTK Project +# Author: Eric Kafe +# URL: +# For license information, see LICENSE.TXT +# +# iso639-3 language codes (C) https://iso639-3.sil.org/ + +""" +Translate between language names and language codes. + +The iso639-3 language codes were downloaded from the registration authority at +https://iso639-3.sil.org/ + +The iso639-3 codeset is evolving, so retired language codes are kept in the +"iso639retired" dictionary, which is used as fallback by the wrapper functions +"langname" and "langcode", in order to support the lookup of retired codes. + +The "langcode" function returns the current iso639-3 code if there is one, +and falls back to the retired code otherwise. As specified by BCP-47, +it returns the shortest (2-letter) code by default, but 3-letter codes +are also available: + + >>> import nltk.langnames as lgn + >>> lgn.langname('fri') #'fri' is a retired code + 'Western Frisian' + + The current code is different from the retired one: + >>> lgn.langcode('Western Frisian') + 'fy' + + >>> lgn.langcode('Western Frisian', typ = 3) + 'fry' + +""" + +import re +from warnings import warn + +from nltk.corpus import bcp47 + +codepattern = re.compile("[a-z][a-z][a-z]?") + + +def langname(tag, typ="full"): + """ + Convert a composite BCP-47 tag to a language name + + >>> from nltk.langnames import langname + >>> langname('ca-Latn-ES-valencia') + 'Catalan: Latin: Spain: Valencian' + + >>> langname('ca-Latn-ES-valencia', typ="short") + 'Catalan' + """ + tags = tag.split("-") + code = tags[0].lower() + if codepattern.fullmatch(code): + if code in iso639retired: # retired codes + return iso639retired[code] + elif code in iso639short: # 3-letter codes + code2 = iso639short[code] # convert to 2-letter code + warn(f"Shortening {code!r} to {code2!r}", stacklevel=2) + tag = "-".join([code2] + tags[1:]) + name = bcp47.name(tag) # parse according to BCP-47 + if typ == "full": + return name # include all subtags + elif name: + return name.split(":")[0] # only the language subtag + else: + warn(f"Could not find code in {code!r}", stacklevel=2) + + +def langcode(name, typ=2): + """ + Convert language name to iso639-3 language code. Returns the short 2-letter + code by default, if one is available, and the 3-letter code otherwise: + + >>> from nltk.langnames import langcode + >>> langcode('Modern Greek (1453-)') + 'el' + + Specify 'typ=3' to get the 3-letter code: + + >>> langcode('Modern Greek (1453-)', typ=3) + 'ell' + """ + if name in bcp47.langcode: + code = bcp47.langcode[name] + if typ == 3 and code in iso639long: + code = iso639long[code] # convert to 3-letter code + return code + elif name in iso639code_retired: + return iso639code_retired[name] + else: + warn(f"Could not find language in {name!r}", stacklevel=2) + + +# ======================================================================= +# Translate betwwen Wikidata Q-codes and BCP-47 codes or names +# ....................................................................... + + +def tag2q(tag): + """ + Convert BCP-47 tag to Wikidata Q-code + + >>> tag2q('nds-u-sd-demv') + 'Q4289225' + """ + return bcp47.wiki_q[tag] + + +def q2tag(qcode): + """ + Convert Wikidata Q-code to BCP-47 tag + + >>> q2tag('Q4289225') + 'nds-u-sd-demv' + """ + return wiki_bcp47[qcode] + + +def q2name(qcode, typ="full"): + """ + Convert Wikidata Q-code to BCP-47 (full or short) language name + + >>> q2name('Q4289225') + 'Low German: Mecklenburg-Vorpommern' + + >>> q2name('Q4289225', "short") + 'Low German' + """ + return langname(q2tag(qcode), typ) + + +def lang2q(name): + """ + Convert simple language name to Wikidata Q-code + + >>> lang2q('Low German') + 'Q25433' + """ + return tag2q(langcode(name)) + + +# ====================================================================== +# Data dictionaries +# ...................................................................... + + +def inverse_dict(dic): + """Return inverse mapping, but only if it is bijective""" + if len(dic.keys()) == len(set(dic.values())): + return {val: key for (key, val) in dic.items()} + else: + warn("This dictionary has no bijective inverse mapping.") + + +bcp47.load_wiki_q() # Wikidata conversion table needs to be loaded explicitly +wiki_bcp47 = inverse_dict(bcp47.wiki_q) + +iso639short = { + "aar": "aa", + "abk": "ab", + "afr": "af", + "aka": "ak", + "amh": "am", + "ara": "ar", + "arg": "an", + "asm": "as", + "ava": "av", + "ave": "ae", + "aym": "ay", + "aze": "az", + "bak": "ba", + "bam": "bm", + "bel": "be", + "ben": "bn", + "bis": "bi", + "bod": "bo", + "bos": "bs", + "bre": "br", + "bul": "bg", + "cat": "ca", + "ces": "cs", + "cha": "ch", + "che": "ce", + "chu": "cu", + "chv": "cv", + "cor": "kw", + "cos": "co", + "cre": "cr", + "cym": "cy", + "dan": "da", + "deu": "de", + "div": "dv", + "dzo": "dz", + "ell": "el", + "eng": "en", + "epo": "eo", + "est": "et", + "eus": "eu", + "ewe": "ee", + "fao": "fo", + "fas": "fa", + "fij": "fj", + "fin": "fi", + "fra": "fr", + "fry": "fy", + "ful": "ff", + "gla": "gd", + "gle": "ga", + "glg": "gl", + "glv": "gv", + "grn": "gn", + "guj": "gu", + "hat": "ht", + "hau": "ha", + "hbs": "sh", + "heb": "he", + "her": "hz", + "hin": "hi", + "hmo": "ho", + "hrv": "hr", + "hun": "hu", + "hye": "hy", + "ibo": "ig", + "ido": "io", + "iii": "ii", + "iku": "iu", + "ile": "ie", + "ina": "ia", + "ind": "id", + "ipk": "ik", + "isl": "is", + "ita": "it", + "jav": "jv", + "jpn": "ja", + "kal": "kl", + "kan": "kn", + "kas": "ks", + "kat": "ka", + "kau": "kr", + "kaz": "kk", + "khm": "km", + "kik": "ki", + "kin": "rw", + "kir": "ky", + "kom": "kv", + "kon": "kg", + "kor": "ko", + "kua": "kj", + "kur": "ku", + "lao": "lo", + "lat": "la", + "lav": "lv", + "lim": "li", + "lin": "ln", + "lit": "lt", + "ltz": "lb", + "lub": "lu", + "lug": "lg", + "mah": "mh", + "mal": "ml", + "mar": "mr", + "mkd": "mk", + "mlg": "mg", + "mlt": "mt", + "mon": "mn", + "mri": "mi", + "msa": "ms", + "mya": "my", + "nau": "na", + "nav": "nv", + "nbl": "nr", + "nde": "nd", + "ndo": "ng", + "nep": "ne", + "nld": "nl", + "nno": "nn", + "nob": "nb", + "nor": "no", + "nya": "ny", + "oci": "oc", + "oji": "oj", + "ori": "or", + "orm": "om", + "oss": "os", + "pan": "pa", + "pli": "pi", + "pol": "pl", + "por": "pt", + "pus": "ps", + "que": "qu", + "roh": "rm", + "ron": "ro", + "run": "rn", + "rus": "ru", + "sag": "sg", + "san": "sa", + "sin": "si", + "slk": "sk", + "slv": "sl", + "sme": "se", + "smo": "sm", + "sna": "sn", + "snd": "sd", + "som": "so", + "sot": "st", + "spa": "es", + "sqi": "sq", + "srd": "sc", + "srp": "sr", + "ssw": "ss", + "sun": "su", + "swa": "sw", + "swe": "sv", + "tah": "ty", + "tam": "ta", + "tat": "tt", + "tel": "te", + "tgk": "tg", + "tgl": "tl", + "tha": "th", + "tir": "ti", + "ton": "to", + "tsn": "tn", + "tso": "ts", + "tuk": "tk", + "tur": "tr", + "twi": "tw", + "uig": "ug", + "ukr": "uk", + "urd": "ur", + "uzb": "uz", + "ven": "ve", + "vie": "vi", + "vol": "vo", + "wln": "wa", + "wol": "wo", + "xho": "xh", + "yid": "yi", + "yor": "yo", + "zha": "za", + "zho": "zh", + "zul": "zu", +} + + +iso639retired = { + "fri": "Western Frisian", + "auv": "Auvergnat", + "gsc": "Gascon", + "lms": "Limousin", + "lnc": "Languedocien", + "prv": "Provençal", + "amd": "Amapá Creole", + "bgh": "Bogan", + "bnh": "Banawá", + "bvs": "Belgian Sign Language", + "ccy": "Southern Zhuang", + "cit": "Chittagonian", + "flm": "Falam Chin", + "jap": "Jaruára", + "kob": "Kohoroxitari", + "mob": "Moinba", + "mzf": "Aiku", + "nhj": "Tlalitzlipa Nahuatl", + "nhs": "Southeastern Puebla Nahuatl", + "occ": "Occidental", + "tmx": "Tomyang", + "tot": "Patla-Chicontla Totonac", + "xmi": "Miarrã", + "yib": "Yinglish", + "ztc": "Lachirioag Zapotec", + "atf": "Atuence", + "bqe": "Navarro-Labourdin Basque", + "bsz": "Souletin Basque", + "aex": "Amerax", + "ahe": "Ahe", + "aiz": "Aari", + "akn": "Amikoana", + "arf": "Arafundi", + "azr": "Adzera", + "bcx": "Pamona", + "bii": "Bisu", + "bke": "Bengkulu", + "blu": "Hmong Njua", + "boc": "Bakung Kenyah", + "bsd": "Sarawak Bisaya", + "bwv": "Bahau River Kenyah", + "bxt": "Buxinhua", + "byu": "Buyang", + "ccx": "Northern Zhuang", + "cru": "Carútana", + "dat": "Darang Deng", + "dyk": "Land Dayak", + "eni": "Enim", + "fiz": "Izere", + "gen": "Geman Deng", + "ggh": "Garreh-Ajuran", + "itu": "Itutang", + "kds": "Lahu Shi", + "knh": "Kayan River Kenyah", + "krg": "North Korowai", + "krq": "Krui", + "kxg": "Katingan", + "lmt": "Lematang", + "lnt": "Lintang", + "lod": "Berawan", + "mbg": "Northern Nambikuára", + "mdo": "Southwest Gbaya", + "mhv": "Arakanese", + "miv": "Mimi", + "mqd": "Madang", + "nky": "Khiamniungan Naga", + "nxj": "Nyadu", + "ogn": "Ogan", + "ork": "Orokaiva", + "paj": "Ipeka-Tapuia", + "pec": "Southern Pesisir", + "pen": "Penesak", + "plm": "Palembang", + "poj": "Lower Pokomo", + "pun": "Pubian", + "rae": "Ranau", + "rjb": "Rajbanshi", + "rws": "Rawas", + "sdd": "Semendo", + "sdi": "Sindang Kelingi", + "skl": "Selako", + "slb": "Kahumamahon Saluan", + "srj": "Serawai", + "suf": "Tarpia", + "suh": "Suba", + "suu": "Sungkai", + "szk": "Sizaki", + "tle": "Southern Marakwet", + "tnj": "Tanjong", + "ttx": "Tutong 1", + "ubm": "Upper Baram Kenyah", + "vky": "Kayu Agung", + "vmo": "Muko-Muko", + "wre": "Ware", + "xah": "Kahayan", + "xkm": "Mahakam Kenyah", + "xuf": "Kunfal", + "yio": "Dayao Yi", + "ymj": "Muji Yi", + "ypl": "Pula Yi", + "ypw": "Puwa Yi", + "ywm": "Wumeng Yi", + "yym": "Yuanjiang-Mojiang Yi", + "mly": "Malay (individual language)", + "muw": "Mundari", + "xst": "Silt'e", + "ope": "Old Persian", + "scc": "Serbian", + "scr": "Croatian", + "xsk": "Sakan", + "mol": "Moldavian", + "aay": "Aariya", + "acc": "Cubulco Achí", + "cbm": "Yepocapa Southwestern Cakchiquel", + "chs": "Chumash", + "ckc": "Northern Cakchiquel", + "ckd": "South Central Cakchiquel", + "cke": "Eastern Cakchiquel", + "ckf": "Southern Cakchiquel", + "cki": "Santa María De Jesús Cakchiquel", + "ckj": "Santo Domingo Xenacoj Cakchiquel", + "ckk": "Acatenango Southwestern Cakchiquel", + "ckw": "Western Cakchiquel", + "cnm": "Ixtatán Chuj", + "cti": "Tila Chol", + "cun": "Cunén Quiché", + "eml": "Emiliano-Romagnolo", + "eur": "Europanto", + "gmo": "Gamo-Gofa-Dawro", + "hsf": "Southeastern Huastec", + "hva": "San Luís Potosí Huastec", + "ixi": "Nebaj Ixil", + "ixj": "Chajul Ixil", + "jai": "Western Jacalteco", + "mms": "Southern Mam", + "mpf": "Tajumulco Mam", + "mtz": "Tacanec", + "mvc": "Central Mam", + "mvj": "Todos Santos Cuchumatán Mam", + "poa": "Eastern Pokomam", + "pob": "Western Pokomchí", + "pou": "Southern Pokomam", + "ppv": "Papavô", + "quj": "Joyabaj Quiché", + "qut": "West Central Quiché", + "quu": "Eastern Quiché", + "qxi": "San Andrés Quiché", + "sic": "Malinguat", + "stc": "Santa Cruz", + "tlz": "Toala'", + "tzb": "Bachajón Tzeltal", + "tzc": "Chamula Tzotzil", + "tze": "Chenalhó Tzotzil", + "tzs": "San Andrés Larrainzar Tzotzil", + "tzt": "Western Tzutujil", + "tzu": "Huixtán Tzotzil", + "tzz": "Zinacantán Tzotzil", + "vlr": "Vatrata", + "yus": "Chan Santa Cruz Maya", + "nfg": "Nyeng", + "nfk": "Shakara", + "agp": "Paranan", + "bhk": "Albay Bicolano", + "bkb": "Finallig", + "btb": "Beti (Cameroon)", + "cjr": "Chorotega", + "cmk": "Chimakum", + "drh": "Darkhat", + "drw": "Darwazi", + "gav": "Gabutamon", + "mof": "Mohegan-Montauk-Narragansett", + "mst": "Cataelano Mandaya", + "myt": "Sangab Mandaya", + "rmr": "Caló", + "sgl": "Sanglechi-Ishkashimi", + "sul": "Surigaonon", + "sum": "Sumo-Mayangna", + "tnf": "Tangshewi", + "wgw": "Wagawaga", + "ayx": "Ayi (China)", + "bjq": "Southern Betsimisaraka Malagasy", + "dha": "Dhanwar (India)", + "dkl": "Kolum So Dogon", + "mja": "Mahei", + "nbf": "Naxi", + "noo": "Nootka", + "tie": "Tingal", + "tkk": "Takpa", + "baz": "Tunen", + "bjd": "Bandjigali", + "ccq": "Chaungtha", + "cka": "Khumi Awa Chin", + "dap": "Nisi (India)", + "dwl": "Walo Kumbe Dogon", + "elp": "Elpaputih", + "gbc": "Garawa", + "gio": "Gelao", + "hrr": "Horuru", + "ibi": "Ibilo", + "jar": "Jarawa (Nigeria)", + "kdv": "Kado", + "kgh": "Upper Tanudan Kalinga", + "kpp": "Paku Karen", + "kzh": "Kenuzi-Dongola", + "lcq": "Luhu", + "mgx": "Omati", + "nln": "Durango Nahuatl", + "pbz": "Palu", + "pgy": "Pongyong", + "sca": "Sansu", + "tlw": "South Wemale", + "unp": "Worora", + "wiw": "Wirangu", + "ybd": "Yangbye", + "yen": "Yendang", + "yma": "Yamphe", + "daf": "Dan", + "djl": "Djiwarli", + "ggr": "Aghu Tharnggalu", + "ilw": "Talur", + "izi": "Izi-Ezaa-Ikwo-Mgbo", + "meg": "Mea", + "mld": "Malakhel", + "mnt": "Maykulan", + "mwd": "Mudbura", + "myq": "Forest Maninka", + "nbx": "Ngura", + "nlr": "Ngarla", + "pcr": "Panang", + "ppr": "Piru", + "tgg": "Tangga", + "wit": "Wintu", + "xia": "Xiandao", + "yiy": "Yir Yoront", + "yos": "Yos", + "emo": "Emok", + "ggm": "Gugu Mini", + "leg": "Lengua", + "lmm": "Lamam", + "mhh": "Maskoy Pidgin", + "puz": "Purum Naga", + "sap": "Sanapaná", + "yuu": "Yugh", + "aam": "Aramanik", + "adp": "Adap", + "aue": "ǂKxʼauǁʼein", + "bmy": "Bemba (Democratic Republic of Congo)", + "bxx": "Borna (Democratic Republic of Congo)", + "byy": "Buya", + "dzd": "Daza", + "gfx": "Mangetti Dune ǃXung", + "gti": "Gbati-ri", + "ime": "Imeraguen", + "kbf": "Kakauhua", + "koj": "Sara Dunjo", + "kwq": "Kwak", + "kxe": "Kakihum", + "lii": "Lingkhim", + "mwj": "Maligo", + "nnx": "Ngong", + "oun": "ǃOǃung", + "pmu": "Mirpur Panjabi", + "sgo": "Songa", + "thx": "The", + "tsf": "Southwestern Tamang", + "uok": "Uokha", + "xsj": "Subi", + "yds": "Yiddish Sign Language", + "ymt": "Mator-Taygi-Karagas", + "ynh": "Yangho", + "bgm": "Baga Mboteni", + "btl": "Bhatola", + "cbe": "Chipiajes", + "cbh": "Cagua", + "coy": "Coyaima", + "cqu": "Chilean Quechua", + "cum": "Cumeral", + "duj": "Dhuwal", + "ggn": "Eastern Gurung", + "ggo": "Southern Gondi", + "guv": "Gey", + "iap": "Iapama", + "ill": "Iranun", + "kgc": "Kasseng", + "kox": "Coxima", + "ktr": "Kota Marudu Tinagas", + "kvs": "Kunggara", + "kzj": "Coastal Kadazan", + "kzt": "Tambunan Dusun", + "nad": "Nijadali", + "nts": "Natagaimas", + "ome": "Omejes", + "pmc": "Palumata", + "pod": "Ponares", + "ppa": "Pao", + "pry": "Pray 3", + "rna": "Runa", + "svr": "Savara", + "tdu": "Tempasuk Dusun", + "thc": "Tai Hang Tong", + "tid": "Tidong", + "tmp": "Tai Mène", + "tne": "Tinoc Kallahan", + "toe": "Tomedes", + "xba": "Kamba (Brazil)", + "xbx": "Kabixí", + "xip": "Xipináwa", + "xkh": "Karahawyana", + "yri": "Yarí", + "jeg": "Jeng", + "kgd": "Kataang", + "krm": "Krim", + "prb": "Lua'", + "puk": "Pu Ko", + "rie": "Rien", + "rsi": "Rennellese Sign Language", + "skk": "Sok", + "snh": "Shinabo", + "lsg": "Lyons Sign Language", + "mwx": "Mediak", + "mwy": "Mosiro", + "ncp": "Ndaktup", + "ais": "Nataoran Amis", + "asd": "Asas", + "dit": "Dirari", + "dud": "Hun-Saare", + "lba": "Lui", + "llo": "Khlor", + "myd": "Maramba", + "myi": "Mina (India)", + "nns": "Ningye", + "aoh": "Arma", + "ayy": "Tayabas Ayta", + "bbz": "Babalia Creole Arabic", + "bpb": "Barbacoas", + "cca": "Cauca", + "cdg": "Chamari", + "dgu": "Degaru", + "drr": "Dororo", + "ekc": "Eastern Karnic", + "gli": "Guliguli", + "kjf": "Khalaj", + "kxl": "Nepali Kurux", + "kxu": "Kui (India)", + "lmz": "Lumbee", + "nxu": "Narau", + "plp": "Palpa", + "sdm": "Semandang", + "tbb": "Tapeba", + "xrq": "Karranga", + "xtz": "Tasmanian", + "zir": "Ziriya", + "thw": "Thudam", + "bic": "Bikaru", + "bij": "Vaghat-Ya-Bijim-Legeri", + "blg": "Balau", + "gji": "Geji", + "mvm": "Muya", + "ngo": "Ngoni", + "pat": "Papitalai", + "vki": "Ija-Zuba", + "wra": "Warapu", + "ajt": "Judeo-Tunisian Arabic", + "cug": "Chungmboko", + "lak": "Laka (Nigeria)", + "lno": "Lango (South Sudan)", + "pii": "Pini", + "smd": "Sama", + "snb": "Sebuyau", + "uun": "Kulon-Pazeh", + "wrd": "Warduji", + "wya": "Wyandot", +} + + +iso639long = inverse_dict(iso639short) + +iso639code_retired = inverse_dict(iso639retired) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lazyimport.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lazyimport.py new file mode 100644 index 0000000..7f744b6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lazyimport.py @@ -0,0 +1,142 @@ +# This module is from mx/DateTime/LazyModule.py and is +# distributed under the terms of the eGenix.com Public License Agreement +# https://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf + +""" Helper to enable simple lazy module import. + + 'Lazy' means the actual import is deferred until an attribute is + requested from the module's namespace. This has the advantage of + allowing all imports to be done at the top of a script (in a + prominent and visible place) without having a great impact + on startup time. + + Copyright (c) 1999-2005, Marc-Andre Lemburg; mailto:mal@lemburg.com + See the documentation for further information on copyrights, + or contact the author. All Rights Reserved. +""" + +### Constants + +_debug = 0 + +### + + +class LazyModule: + + """Lazy module class. + + Lazy modules are imported into the given namespaces whenever a + non-special attribute (there are some attributes like __doc__ + that class instances handle without calling __getattr__) is + requested. The module is then registered under the given name + in locals usually replacing the import wrapper instance. The + import itself is done using globals as global namespace. + + Example of creating a lazy load module: + + ISO = LazyModule('ISO',locals(),globals()) + + Later, requesting an attribute from ISO will load the module + automatically into the locals() namespace, overriding the + LazyModule instance: + + t = ISO.Week(1998,1,1) + + """ + + # Flag which indicates whether the LazyModule is initialized or not + __lazymodule_init = 0 + + # Name of the module to load + __lazymodule_name = "" + + # Flag which indicates whether the module was loaded or not + __lazymodule_loaded = 0 + + # Locals dictionary where to register the module + __lazymodule_locals = None + + # Globals dictionary to use for the module import + __lazymodule_globals = None + + def __init__(self, name, locals, globals=None): + + """Create a LazyModule instance wrapping module name. + + The module will later on be registered in locals under the + given module name. + + globals is optional and defaults to locals. + + """ + self.__lazymodule_locals = locals + if globals is None: + globals = locals + self.__lazymodule_globals = globals + mainname = globals.get("__name__", "") + if mainname: + self.__name__ = mainname + "." + name + self.__lazymodule_name = name + else: + self.__name__ = self.__lazymodule_name = name + self.__lazymodule_init = 1 + + def __lazymodule_import(self): + + """Import the module now.""" + # Load and register module + local_name = self.__lazymodule_name # e.g. "toolbox" + full_name = self.__name__ # e.g. "nltk.toolbox" + if self.__lazymodule_loaded: + return self.__lazymodule_locals[local_name] + if _debug: + print("LazyModule: Loading module %r" % full_name) + self.__lazymodule_locals[local_name] = module = __import__( + full_name, self.__lazymodule_locals, self.__lazymodule_globals, "*" + ) + + # Fill namespace with all symbols from original module to + # provide faster access. + self.__dict__.update(module.__dict__) + + # Set import flag + self.__dict__["__lazymodule_loaded"] = 1 + + if _debug: + print("LazyModule: Module %r loaded" % full_name) + return module + + def __getattr__(self, name): + + """Import the module on demand and get the attribute.""" + if self.__lazymodule_loaded: + raise AttributeError(name) + if _debug: + print( + "LazyModule: " + "Module load triggered by attribute %r read access" % name + ) + module = self.__lazymodule_import() + return getattr(module, name) + + def __setattr__(self, name, value): + + """Import the module on demand and set the attribute.""" + if not self.__lazymodule_init: + self.__dict__[name] = value + return + if self.__lazymodule_loaded: + self.__lazymodule_locals[self.__lazymodule_name] = value + self.__dict__[name] = value + return + if _debug: + print( + "LazyModule: " + "Module load triggered by attribute %r write access" % name + ) + module = self.__lazymodule_import() + setattr(module, name, value) + + def __repr__(self): + return "" % self.__name__ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__init__.py new file mode 100644 index 0000000..2fc0669 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__init__.py @@ -0,0 +1,235 @@ +# Natural Language Toolkit: Language Models +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Ilia Kurenkov +# URL: >> text = [['a', 'b', 'c'], ['a', 'c', 'd', 'c', 'e', 'f']] + +If we want to train a bigram model, we need to turn this text into bigrams. +Here's what the first sentence of our text would look like if we use a function +from NLTK for this. + + >>> from nltk.util import bigrams + >>> list(bigrams(text[0])) + [('a', 'b'), ('b', 'c')] + +Notice how "b" occurs both as the first and second member of different bigrams +but "a" and "c" don't? Wouldn't it be nice to somehow indicate how often sentences +start with "a" and end with "c"? +A standard way to deal with this is to add special "padding" symbols to the +sentence before splitting it into ngrams. +Fortunately, NLTK also has a function for that, let's see what it does to the +first sentence. + + >>> from nltk.util import pad_sequence + >>> list(pad_sequence(text[0], + ... pad_left=True, + ... left_pad_symbol="", + ... pad_right=True, + ... right_pad_symbol="", + ... n=2)) + ['', 'a', 'b', 'c', ''] + +Note the `n` argument, that tells the function we need padding for bigrams. +Now, passing all these parameters every time is tedious and in most cases they +can be safely assumed as defaults anyway. +Thus our module provides a convenience function that has all these arguments +already set while the other arguments remain the same as for `pad_sequence`. + + >>> from nltk.lm.preprocessing import pad_both_ends + >>> list(pad_both_ends(text[0], n=2)) + ['', 'a', 'b', 'c', ''] + +Combining the two parts discussed so far we get the following preparation steps +for one sentence. + + >>> list(bigrams(pad_both_ends(text[0], n=2))) + [('', 'a'), ('a', 'b'), ('b', 'c'), ('c', '')] + +To make our model more robust we could also train it on unigrams (single words) +as well as bigrams, its main source of information. +NLTK once again helpfully provides a function called `everygrams`. +While not the most efficient, it is conceptually simple. + + + >>> from nltk.util import everygrams + >>> padded_bigrams = list(pad_both_ends(text[0], n=2)) + >>> list(everygrams(padded_bigrams, max_len=2)) + [('',), ('', 'a'), ('a',), ('a', 'b'), ('b',), ('b', 'c'), ('c',), ('c', ''), ('',)] + +We are almost ready to start counting ngrams, just one more step left. +During training and evaluation our model will rely on a vocabulary that +defines which words are "known" to the model. +To create this vocabulary we need to pad our sentences (just like for counting +ngrams) and then combine the sentences into one flat stream of words. + + >>> from nltk.lm.preprocessing import flatten + >>> list(flatten(pad_both_ends(sent, n=2) for sent in text)) + ['', 'a', 'b', 'c', '', '', 'a', 'c', 'd', 'c', 'e', 'f', ''] + +In most cases we want to use the same text as the source for both vocabulary +and ngram counts. +Now that we understand what this means for our preprocessing, we can simply import +a function that does everything for us. + + >>> from nltk.lm.preprocessing import padded_everygram_pipeline + >>> train, vocab = padded_everygram_pipeline(2, text) + +So as to avoid re-creating the text in memory, both `train` and `vocab` are lazy +iterators. They are evaluated on demand at training time. + + +Training +======== +Having prepared our data we are ready to start training a model. +As a simple example, let us train a Maximum Likelihood Estimator (MLE). +We only need to specify the highest ngram order to instantiate it. + + >>> from nltk.lm import MLE + >>> lm = MLE(2) + +This automatically creates an empty vocabulary... + + >>> len(lm.vocab) + 0 + +... which gets filled as we fit the model. + + >>> lm.fit(train, vocab) + >>> print(lm.vocab) + + >>> len(lm.vocab) + 9 + +The vocabulary helps us handle words that have not occurred during training. + + >>> lm.vocab.lookup(text[0]) + ('a', 'b', 'c') + >>> lm.vocab.lookup(["aliens", "from", "Mars"]) + ('', '', '') + +Moreover, in some cases we want to ignore words that we did see during training +but that didn't occur frequently enough, to provide us useful information. +You can tell the vocabulary to ignore such words. +To find out how that works, check out the docs for the `Vocabulary` class. + + +Using a Trained Model +===================== +When it comes to ngram models the training boils down to counting up the ngrams +from the training corpus. + + >>> print(lm.counts) + + +This provides a convenient interface to access counts for unigrams... + + >>> lm.counts['a'] + 2 + +...and bigrams (in this case "a b") + + >>> lm.counts[['a']]['b'] + 1 + +And so on. However, the real purpose of training a language model is to have it +score how probable words are in certain contexts. +This being MLE, the model returns the item's relative frequency as its score. + + >>> lm.score("a") + 0.15384615384615385 + +Items that are not seen during training are mapped to the vocabulary's +"unknown label" token. This is "" by default. + + >>> lm.score("") == lm.score("aliens") + True + +Here's how you get the score for a word given some preceding context. +For example we want to know what is the chance that "b" is preceded by "a". + + >>> lm.score("b", ["a"]) + 0.5 + +To avoid underflow when working with many small score values it makes sense to +take their logarithm. +For convenience this can be done with the `logscore` method. + + >>> lm.logscore("a") + -2.700439718141092 + +Building on this method, we can also evaluate our model's cross-entropy and +perplexity with respect to sequences of ngrams. + + >>> test = [('a', 'b'), ('c', 'd')] + >>> lm.entropy(test) + 1.292481250360578 + >>> lm.perplexity(test) + 2.449489742783178 + +It is advisable to preprocess your test text exactly the same way as you did +the training text. + +One cool feature of ngram models is that they can be used to generate text. + + >>> lm.generate(1, random_seed=3) + '' + >>> lm.generate(5, random_seed=3) + ['', 'a', 'b', 'c', 'd'] + +Provide `random_seed` if you want to consistently reproduce the same text all +other things being equal. Here we are using it to test the examples. + +You can also condition your generation on some preceding text with the `context` +argument. + + >>> lm.generate(5, text_seed=['c'], random_seed=3) + ['', 'c', 'd', 'c', 'd'] + +Note that an ngram model is restricted in how much preceding context it can +take into account. For example, a trigram model can only condition its output +on 2 preceding words. If you pass in a 4-word context, the first two words +will be ignored. +""" + +from nltk.lm.counter import NgramCounter +from nltk.lm.models import ( + MLE, + AbsoluteDiscountingInterpolated, + KneserNeyInterpolated, + Laplace, + Lidstone, + StupidBackoff, + WittenBellInterpolated, +) +from nltk.lm.vocabulary import Vocabulary + +__all__ = [ + "Vocabulary", + "NgramCounter", + "MLE", + "Lidstone", + "Laplace", + "WittenBellInterpolated", + "KneserNeyInterpolated", + "AbsoluteDiscountingInterpolated", + "StupidBackoff", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..748cc94 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..62dc9f5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/counter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/counter.cpython-310.pyc new file mode 100644 index 0000000..70dca79 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/counter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000..4aaa5c7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/preprocessing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/preprocessing.cpython-310.pyc new file mode 100644 index 0000000..178b012 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/preprocessing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/smoothing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/smoothing.cpython-310.pyc new file mode 100644 index 0000000..502d540 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/smoothing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..c59ca20 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/vocabulary.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/vocabulary.cpython-310.pyc new file mode 100644 index 0000000..731fffa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/__pycache__/vocabulary.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/api.py new file mode 100644 index 0000000..5ebdf38 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/api.py @@ -0,0 +1,235 @@ +# Natural Language Toolkit: Language Models +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +"""Language Model Interface.""" + +import random +import warnings +from abc import ABCMeta, abstractmethod +from bisect import bisect +from itertools import accumulate + +from nltk.lm.counter import NgramCounter +from nltk.lm.util import log_base2 +from nltk.lm.vocabulary import Vocabulary + + +class Smoothing(metaclass=ABCMeta): + """Ngram Smoothing Interface + + Implements Chen & Goodman 1995's idea that all smoothing algorithms have + certain features in common. This should ideally allow smoothing algorithms to + work both with Backoff and Interpolation. + """ + + def __init__(self, vocabulary, counter): + """ + :param vocabulary: The Ngram vocabulary object. + :type vocabulary: nltk.lm.vocab.Vocabulary + :param counter: The counts of the vocabulary items. + :type counter: nltk.lm.counter.NgramCounter + """ + self.vocab = vocabulary + self.counts = counter + + @abstractmethod + def unigram_score(self, word): + raise NotImplementedError() + + @abstractmethod + def alpha_gamma(self, word, context): + raise NotImplementedError() + + +def _mean(items): + """Return average (aka mean) for sequence of items.""" + return sum(items) / len(items) + + +def _random_generator(seed_or_generator): + if isinstance(seed_or_generator, random.Random): + return seed_or_generator + return random.Random(seed_or_generator) + + +def _weighted_choice(population, weights, random_generator=None): + """Like random.choice, but with weights. + + Heavily inspired by python 3.6 `random.choices`. + """ + if not population: + raise ValueError("Can't choose from empty population") + if len(population) != len(weights): + raise ValueError("The number of weights does not match the population") + cum_weights = list(accumulate(weights)) + total = cum_weights[-1] + threshold = random_generator.random() + return population[bisect(cum_weights, total * threshold)] + + +class LanguageModel(metaclass=ABCMeta): + """ABC for Language Models. + + Cannot be directly instantiated itself. + + """ + + def __init__(self, order, vocabulary=None, counter=None): + """Creates new LanguageModel. + + :param vocabulary: If provided, this vocabulary will be used instead + of creating a new one when training. + :type vocabulary: `nltk.lm.Vocabulary` or None + :param counter: If provided, use this object to count ngrams. + :type counter: `nltk.lm.NgramCounter` or None + :param ngrams_fn: If given, defines how sentences in training text are turned to ngram + sequences. + :type ngrams_fn: function or None + :param pad_fn: If given, defines how sentences in training text are padded. + :type pad_fn: function or None + """ + self.order = order + if vocabulary and not isinstance(vocabulary, Vocabulary): + warnings.warn( + f"The `vocabulary` argument passed to {self.__class__.__name__!r} " + "must be an instance of `nltk.lm.Vocabulary`.", + stacklevel=3, + ) + self.vocab = Vocabulary() if vocabulary is None else vocabulary + self.counts = NgramCounter() if counter is None else counter + + def fit(self, text, vocabulary_text=None): + """Trains the model on a text. + + :param text: Training text as a sequence of sentences. + + """ + if not self.vocab: + if vocabulary_text is None: + raise ValueError( + "Cannot fit without a vocabulary or text to create it from." + ) + self.vocab.update(vocabulary_text) + self.counts.update(self.vocab.lookup(sent) for sent in text) + + def score(self, word, context=None): + """Masks out of vocab (OOV) words and computes their model score. + + For model-specific logic of calculating scores, see the `unmasked_score` + method. + """ + return self.unmasked_score( + self.vocab.lookup(word), self.vocab.lookup(context) if context else None + ) + + @abstractmethod + def unmasked_score(self, word, context=None): + """Score a word given some optional context. + + Concrete models are expected to provide an implementation. + Note that this method does not mask its arguments with the OOV label. + Use the `score` method for that. + + :param str word: Word for which we want the score + :param tuple(str) context: Context the word is in. + If `None`, compute unigram score. + :param context: tuple(str) or None + :rtype: float + """ + raise NotImplementedError() + + def logscore(self, word, context=None): + """Evaluate the log score of this word in this context. + + The arguments are the same as for `score` and `unmasked_score`. + + """ + return log_base2(self.score(word, context)) + + def context_counts(self, context): + """Helper method for retrieving counts for a given context. + + Assumes context has been checked and oov words in it masked. + :type context: tuple(str) or None + + """ + return ( + self.counts[len(context) + 1][context] if context else self.counts.unigrams + ) + + def entropy(self, text_ngrams): + """Calculate cross-entropy of model for given evaluation text. + + :param Iterable(tuple(str)) text_ngrams: A sequence of ngram tuples. + :rtype: float + + """ + return -1 * _mean( + [self.logscore(ngram[-1], ngram[:-1]) for ngram in text_ngrams] + ) + + def perplexity(self, text_ngrams): + """Calculates the perplexity of the given text. + + This is simply 2 ** cross-entropy for the text, so the arguments are the same. + + """ + return pow(2.0, self.entropy(text_ngrams)) + + def generate(self, num_words=1, text_seed=None, random_seed=None): + """Generate words from the model. + + :param int num_words: How many words to generate. By default 1. + :param text_seed: Generation can be conditioned on preceding context. + :param random_seed: A random seed or an instance of `random.Random`. If provided, + makes the random sampling part of generation reproducible. + :return: One (str) word or a list of words generated from model. + + Examples: + + >>> from nltk.lm import MLE + >>> lm = MLE(2) + >>> lm.fit([[("a", "b"), ("b", "c")]], vocabulary_text=['a', 'b', 'c']) + >>> lm.fit([[("a",), ("b",), ("c",)]]) + >>> lm.generate(random_seed=3) + 'a' + >>> lm.generate(text_seed=['a']) + 'b' + + """ + text_seed = [] if text_seed is None else list(text_seed) + random_generator = _random_generator(random_seed) + # This is the base recursion case. + if num_words == 1: + context = ( + text_seed[-self.order + 1 :] + if len(text_seed) >= self.order + else text_seed + ) + samples = self.context_counts(self.vocab.lookup(context)) + while context and not samples: + context = context[1:] if len(context) > 1 else [] + samples = self.context_counts(self.vocab.lookup(context)) + # Sorting samples achieves two things: + # - reproducible randomness when sampling + # - turns Mapping into Sequence which `_weighted_choice` expects + samples = sorted(samples) + return _weighted_choice( + samples, + tuple(self.score(w, context) for w in samples), + random_generator, + ) + # We build up text one word at a time using the preceding context. + generated = [] + for _ in range(num_words): + generated.append( + self.generate( + num_words=1, + text_seed=text_seed + generated, + random_seed=random_generator, + ) + ) + return generated diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/counter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/counter.py new file mode 100644 index 0000000..aacf3e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/counter.py @@ -0,0 +1,163 @@ +# Natural Language Toolkit +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +""" +Language Model Counter +---------------------- +""" + +from collections import defaultdict +from collections.abc import Sequence + +from nltk.probability import ConditionalFreqDist, FreqDist + + +class NgramCounter: + """Class for counting ngrams. + + Will count any ngram sequence you give it ;) + + First we need to make sure we are feeding the counter sentences of ngrams. + + >>> text = [["a", "b", "c", "d"], ["a", "c", "d", "c"]] + >>> from nltk.util import ngrams + >>> text_bigrams = [ngrams(sent, 2) for sent in text] + >>> text_unigrams = [ngrams(sent, 1) for sent in text] + + The counting itself is very simple. + + >>> from nltk.lm import NgramCounter + >>> ngram_counts = NgramCounter(text_bigrams + text_unigrams) + + You can conveniently access ngram counts using standard python dictionary notation. + String keys will give you unigram counts. + + >>> ngram_counts['a'] + 2 + >>> ngram_counts['aliens'] + 0 + + If you want to access counts for higher order ngrams, use a list or a tuple. + These are treated as "context" keys, so what you get is a frequency distribution + over all continuations after the given context. + + >>> sorted(ngram_counts[['a']].items()) + [('b', 1), ('c', 1)] + >>> sorted(ngram_counts[('a',)].items()) + [('b', 1), ('c', 1)] + + This is equivalent to specifying explicitly the order of the ngram (in this case + 2 for bigram) and indexing on the context. + + >>> ngram_counts[2][('a',)] is ngram_counts[['a']] + True + + Note that the keys in `ConditionalFreqDist` cannot be lists, only tuples! + It is generally advisable to use the less verbose and more flexible square + bracket notation. + + To get the count of the full ngram "a b", do this: + + >>> ngram_counts[['a']]['b'] + 1 + + Specifying the ngram order as a number can be useful for accessing all ngrams + in that order. + + >>> ngram_counts[2] + + + The keys of this `ConditionalFreqDist` are the contexts we discussed earlier. + Unigrams can also be accessed with a human-friendly alias. + + >>> ngram_counts.unigrams is ngram_counts[1] + True + + Similarly to `collections.Counter`, you can update counts after initialization. + + >>> ngram_counts['e'] + 0 + >>> ngram_counts.update([ngrams(["d", "e", "f"], 1)]) + >>> ngram_counts['e'] + 1 + + """ + + def __init__(self, ngram_text=None): + """Creates a new NgramCounter. + + If `ngram_text` is specified, counts ngrams from it, otherwise waits for + `update` method to be called explicitly. + + :param ngram_text: Optional text containing sentences of ngrams, as for `update` method. + :type ngram_text: Iterable(Iterable(tuple(str))) or None + + """ + self._counts = defaultdict(ConditionalFreqDist) + self._counts[1] = self.unigrams = FreqDist() + + if ngram_text: + self.update(ngram_text) + + def update(self, ngram_text): + """Updates ngram counts from `ngram_text`. + + Expects `ngram_text` to be a sequence of sentences (sequences). + Each sentence consists of ngrams as tuples of strings. + + :param Iterable(Iterable(tuple(str))) ngram_text: Text containing sentences of ngrams. + :raises TypeError: if the ngrams are not tuples. + + """ + + for sent in ngram_text: + for ngram in sent: + if not isinstance(ngram, tuple): + raise TypeError( + "Ngram <{}> isn't a tuple, " "but {}".format(ngram, type(ngram)) + ) + + ngram_order = len(ngram) + if ngram_order == 1: + self.unigrams[ngram[0]] += 1 + continue + + context, word = ngram[:-1], ngram[-1] + self[ngram_order][context][word] += 1 + + def N(self): + """Returns grand total number of ngrams stored. + + This includes ngrams from all orders, so some duplication is expected. + :rtype: int + + >>> from nltk.lm import NgramCounter + >>> counts = NgramCounter([[("a", "b"), ("c",), ("d", "e")]]) + >>> counts.N() + 3 + + """ + return sum(val.N() for val in self._counts.values()) + + def __getitem__(self, item): + """User-friendly access to ngram counts.""" + if isinstance(item, int): + return self._counts[item] + elif isinstance(item, str): + return self._counts.__getitem__(1)[item] + elif isinstance(item, Sequence): + return self._counts.__getitem__(len(item) + 1)[tuple(item)] + + def __str__(self): + return "<{} with {} ngram orders and {} ngrams>".format( + self.__class__.__name__, len(self._counts), self.N() + ) + + def __len__(self): + return self._counts.__len__() + + def __contains__(self, item): + return item in self._counts diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/models.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/models.py new file mode 100644 index 0000000..f213ac6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/models.py @@ -0,0 +1,141 @@ +# Natural Language Toolkit: Language Models +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# Manu Joseph +# URL: +# For license information, see LICENSE.TXT +"""Language Models""" + +from nltk.lm.api import LanguageModel, Smoothing +from nltk.lm.smoothing import AbsoluteDiscounting, KneserNey, WittenBell + + +class MLE(LanguageModel): + """Class for providing MLE ngram model scores. + + Inherits initialization from BaseNgramModel. + """ + + def unmasked_score(self, word, context=None): + """Returns the MLE score for a word given a context. + + Args: + - word is expected to be a string + - context is expected to be something reasonably convertible to a tuple + """ + return self.context_counts(context).freq(word) + + +class Lidstone(LanguageModel): + """Provides Lidstone-smoothed scores. + + In addition to initialization arguments from BaseNgramModel also requires + a number by which to increase the counts, gamma. + """ + + def __init__(self, gamma, *args, **kwargs): + super().__init__(*args, **kwargs) + self.gamma = gamma + + def unmasked_score(self, word, context=None): + """Add-one smoothing: Lidstone or Laplace. + + To see what kind, look at `gamma` attribute on the class. + + """ + counts = self.context_counts(context) + word_count = counts[word] + norm_count = counts.N() + return (word_count + self.gamma) / (norm_count + len(self.vocab) * self.gamma) + + +class Laplace(Lidstone): + """Implements Laplace (add one) smoothing. + + Initialization identical to BaseNgramModel because gamma is always 1. + """ + + def __init__(self, *args, **kwargs): + super().__init__(1, *args, **kwargs) + + +class StupidBackoff(LanguageModel): + """Provides StupidBackoff scores. + + In addition to initialization arguments from BaseNgramModel also requires + a parameter alpha with which we scale the lower order probabilities. + Note that this is not a true probability distribution as scores for ngrams + of the same order do not sum up to unity. + """ + + def __init__(self, alpha=0.4, *args, **kwargs): + super().__init__(*args, **kwargs) + self.alpha = alpha + + def unmasked_score(self, word, context=None): + if not context: + # Base recursion + return self.counts.unigrams.freq(word) + counts = self.context_counts(context) + word_count = counts[word] + norm_count = counts.N() + if word_count > 0: + return word_count / norm_count + else: + return self.alpha * self.unmasked_score(word, context[1:]) + + +class InterpolatedLanguageModel(LanguageModel): + """Logic common to all interpolated language models. + + The idea to abstract this comes from Chen & Goodman 1995. + Do not instantiate this class directly! + """ + + def __init__(self, smoothing_cls, order, **kwargs): + params = kwargs.pop("params", {}) + super().__init__(order, **kwargs) + self.estimator = smoothing_cls(self.vocab, self.counts, **params) + + def unmasked_score(self, word, context=None): + if not context: + # The base recursion case: no context, we only have a unigram. + return self.estimator.unigram_score(word) + if not self.counts[context]: + # It can also happen that we have no data for this context. + # In that case we defer to the lower-order ngram. + # This is the same as setting alpha to 0 and gamma to 1. + alpha, gamma = 0, 1 + else: + alpha, gamma = self.estimator.alpha_gamma(word, context) + return alpha + gamma * self.unmasked_score(word, context[1:]) + + +class WittenBellInterpolated(InterpolatedLanguageModel): + """Interpolated version of Witten-Bell smoothing.""" + + def __init__(self, order, **kwargs): + super().__init__(WittenBell, order, **kwargs) + + +class AbsoluteDiscountingInterpolated(InterpolatedLanguageModel): + """Interpolated version of smoothing with absolute discount.""" + + def __init__(self, order, discount=0.75, **kwargs): + super().__init__( + AbsoluteDiscounting, order, params={"discount": discount}, **kwargs + ) + + +class KneserNeyInterpolated(InterpolatedLanguageModel): + """Interpolated version of Kneser-Ney smoothing.""" + + def __init__(self, order, discount=0.1, **kwargs): + if not (0 <= discount <= 1): + raise ValueError( + "Discount must be between 0 and 1 for probabilities to sum to unity." + ) + super().__init__( + KneserNey, order, params={"discount": discount, "order": order}, **kwargs + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/preprocessing.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/preprocessing.py new file mode 100644 index 0000000..9a044ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/preprocessing.py @@ -0,0 +1,51 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +from functools import partial +from itertools import chain + +from nltk.util import everygrams, pad_sequence + +flatten = chain.from_iterable +pad_both_ends = partial( + pad_sequence, + pad_left=True, + left_pad_symbol="", + pad_right=True, + right_pad_symbol="", +) +pad_both_ends.__doc__ = """Pads both ends of a sentence to length specified by ngram order. + + Following convention pads the start of sentence pads its end. + """ + + +def padded_everygrams(order, sentence): + """Helper with some useful defaults. + + Applies pad_both_ends to sentence and follows it up with everygrams. + """ + return everygrams(list(pad_both_ends(sentence, n=order)), max_len=order) + + +def padded_everygram_pipeline(order, text): + """Default preprocessing for a sequence of sentences. + + Creates two iterators: + + - sentences padded and turned into sequences of `nltk.util.everygrams` + - sentences padded as above and chained together for a flat stream of words + + :param order: Largest ngram length produced by `everygrams`. + :param text: Text to iterate over. Expected to be an iterable of sentences. + :type text: Iterable[Iterable[str]] + :return: iterator over text as ngrams, iterator over text as vocabulary data + """ + padding_fn = partial(pad_both_ends, n=order) + return ( + (everygrams(list(padding_fn(sent)), max_len=order) for sent in text), + flatten(map(padding_fn, text)), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/smoothing.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/smoothing.py new file mode 100644 index 0000000..c407a54 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/smoothing.py @@ -0,0 +1,127 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# Manu Joseph +# URL: +# For license information, see LICENSE.TXT +"""Smoothing algorithms for language modeling. + +According to Chen & Goodman 1995 these should work with both Backoff and +Interpolation. +""" +from operator import methodcaller + +from nltk.lm.api import Smoothing +from nltk.probability import ConditionalFreqDist + + +def _count_values_gt_zero(distribution): + """Count values that are greater than zero in a distribution. + + Assumes distribution is either a mapping with counts as values or + an instance of `nltk.ConditionalFreqDist`. + """ + as_count = ( + methodcaller("N") + if isinstance(distribution, ConditionalFreqDist) + else lambda count: count + ) + # We explicitly check that values are > 0 to guard against negative counts. + return sum( + 1 for dist_or_count in distribution.values() if as_count(dist_or_count) > 0 + ) + + +class WittenBell(Smoothing): + """Witten-Bell smoothing.""" + + def __init__(self, vocabulary, counter, **kwargs): + super().__init__(vocabulary, counter, **kwargs) + + def alpha_gamma(self, word, context): + alpha = self.counts[context].freq(word) + gamma = self._gamma(context) + return (1.0 - gamma) * alpha, gamma + + def _gamma(self, context): + n_plus = _count_values_gt_zero(self.counts[context]) + return n_plus / (n_plus + self.counts[context].N()) + + def unigram_score(self, word): + return self.counts.unigrams.freq(word) + + +class AbsoluteDiscounting(Smoothing): + """Smoothing with absolute discount.""" + + def __init__(self, vocabulary, counter, discount=0.75, **kwargs): + super().__init__(vocabulary, counter, **kwargs) + self.discount = discount + + def alpha_gamma(self, word, context): + alpha = ( + max(self.counts[context][word] - self.discount, 0) + / self.counts[context].N() + ) + gamma = self._gamma(context) + return alpha, gamma + + def _gamma(self, context): + n_plus = _count_values_gt_zero(self.counts[context]) + return (self.discount * n_plus) / self.counts[context].N() + + def unigram_score(self, word): + return self.counts.unigrams.freq(word) + + +class KneserNey(Smoothing): + """Kneser-Ney Smoothing. + + This is an extension of smoothing with a discount. + + Resources: + - https://pages.ucsd.edu/~rlevy/lign256/winter2008/kneser_ney_mini_example.pdf + - https://www.youtube.com/watch?v=ody1ysUTD7o + - https://medium.com/@dennyc/a-simple-numerical-example-for-kneser-ney-smoothing-nlp-4600addf38b8 + - https://www.cl.uni-heidelberg.de/courses/ss15/smt/scribe6.pdf + - https://www-i6.informatik.rwth-aachen.de/publications/download/951/Kneser-ICASSP-1995.pdf + """ + + def __init__(self, vocabulary, counter, order, discount=0.1, **kwargs): + super().__init__(vocabulary, counter, **kwargs) + self.discount = discount + self._order = order + + def unigram_score(self, word): + word_continuation_count, total_count = self._continuation_counts(word) + return word_continuation_count / total_count + + def alpha_gamma(self, word, context): + prefix_counts = self.counts[context] + word_continuation_count, total_count = ( + (prefix_counts[word], prefix_counts.N()) + if len(context) + 1 == self._order + else self._continuation_counts(word, context) + ) + alpha = max(word_continuation_count - self.discount, 0.0) / total_count + gamma = self.discount * _count_values_gt_zero(prefix_counts) / total_count + return alpha, gamma + + def _continuation_counts(self, word, context=tuple()): + """Count continuations that end with context and word. + + Continuations track unique ngram "types", regardless of how many + instances were observed for each "type". + This is different than raw ngram counts which track number of instances. + """ + higher_order_ngrams_with_context = ( + counts + for prefix_ngram, counts in self.counts[len(context) + 2].items() + if prefix_ngram[1:] == context + ) + higher_order_ngrams_with_word_count, total = 0, 0 + for counts in higher_order_ngrams_with_context: + higher_order_ngrams_with_word_count += int(counts[word] > 0) + total += _count_values_gt_zero(counts) + return higher_order_ngrams_with_word_count, total diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/util.py new file mode 100644 index 0000000..4d9820a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/util.py @@ -0,0 +1,19 @@ +# Natural Language Toolkit +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +"""Language Model Utilities""" + +from math import log + +NEG_INF = float("-inf") +POS_INF = float("inf") + + +def log_base2(score): + """Convenience function for computing logarithms with base 2.""" + if score == 0.0: + return NEG_INF + return log(score, 2) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/lm/vocabulary.py b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/vocabulary.py new file mode 100644 index 0000000..1d8fe72 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/lm/vocabulary.py @@ -0,0 +1,218 @@ +# Natural Language Toolkit +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +"""Language Model Vocabulary""" + +import sys +from collections import Counter +from collections.abc import Iterable +from functools import singledispatch +from itertools import chain + + +@singledispatch +def _dispatched_lookup(words, vocab): + raise TypeError(f"Unsupported type for looking up in vocabulary: {type(words)}") + + +@_dispatched_lookup.register(Iterable) +def _(words, vocab): + """Look up a sequence of words in the vocabulary. + + Returns an iterator over looked up words. + + """ + return tuple(_dispatched_lookup(w, vocab) for w in words) + + +@_dispatched_lookup.register(str) +def _string_lookup(word, vocab): + """Looks up one word in the vocabulary.""" + return word if word in vocab else vocab.unk_label + + +class Vocabulary: + """Stores language model vocabulary. + + Satisfies two common language modeling requirements for a vocabulary: + + - When checking membership and calculating its size, filters items + by comparing their counts to a cutoff value. + - Adds a special "unknown" token which unseen words are mapped to. + + >>> words = ['a', 'c', '-', 'd', 'c', 'a', 'b', 'r', 'a', 'c', 'd'] + >>> from nltk.lm import Vocabulary + >>> vocab = Vocabulary(words, unk_cutoff=2) + + Tokens with counts greater than or equal to the cutoff value will + be considered part of the vocabulary. + + >>> vocab['c'] + 3 + >>> 'c' in vocab + True + >>> vocab['d'] + 2 + >>> 'd' in vocab + True + + Tokens with frequency counts less than the cutoff value will be considered not + part of the vocabulary even though their entries in the count dictionary are + preserved. + + >>> vocab['b'] + 1 + >>> 'b' in vocab + False + >>> vocab['aliens'] + 0 + >>> 'aliens' in vocab + False + + Keeping the count entries for seen words allows us to change the cutoff value + without having to recalculate the counts. + + >>> vocab2 = Vocabulary(vocab.counts, unk_cutoff=1) + >>> "b" in vocab2 + True + + The cutoff value influences not only membership checking but also the result of + getting the size of the vocabulary using the built-in `len`. + Note that while the number of keys in the vocabulary's counter stays the same, + the items in the vocabulary differ depending on the cutoff. + We use `sorted` to demonstrate because it keeps the order consistent. + + >>> sorted(vocab2.counts) + ['-', 'a', 'b', 'c', 'd', 'r'] + >>> sorted(vocab2) + ['-', '', 'a', 'b', 'c', 'd', 'r'] + >>> sorted(vocab.counts) + ['-', 'a', 'b', 'c', 'd', 'r'] + >>> sorted(vocab) + ['', 'a', 'c', 'd'] + + In addition to items it gets populated with, the vocabulary stores a special + token that stands in for so-called "unknown" items. By default it's "". + + >>> "" in vocab + True + + We can look up words in a vocabulary using its `lookup` method. + "Unseen" words (with counts less than cutoff) are looked up as the unknown label. + If given one word (a string) as an input, this method will return a string. + + >>> vocab.lookup("a") + 'a' + >>> vocab.lookup("aliens") + '' + + If given a sequence, it will return an tuple of the looked up words. + + >>> vocab.lookup(["p", 'a', 'r', 'd', 'b', 'c']) + ('', 'a', '', 'd', '', 'c') + + It's possible to update the counts after the vocabulary has been created. + In general, the interface is the same as that of `collections.Counter`. + + >>> vocab['b'] + 1 + >>> vocab.update(["b", "b", "c"]) + >>> vocab['b'] + 3 + """ + + def __init__(self, counts=None, unk_cutoff=1, unk_label=""): + """Create a new Vocabulary. + + :param counts: Optional iterable or `collections.Counter` instance to + pre-seed the Vocabulary. In case it is iterable, counts + are calculated. + :param int unk_cutoff: Words that occur less frequently than this value + are not considered part of the vocabulary. + :param unk_label: Label for marking words not part of vocabulary. + + """ + self.unk_label = unk_label + if unk_cutoff < 1: + raise ValueError(f"Cutoff value cannot be less than 1. Got: {unk_cutoff}") + self._cutoff = unk_cutoff + + self.counts = Counter() + self.update(counts if counts is not None else "") + + @property + def cutoff(self): + """Cutoff value. + + Items with count below this value are not considered part of vocabulary. + + """ + return self._cutoff + + def update(self, *counter_args, **counter_kwargs): + """Update vocabulary counts. + + Wraps `collections.Counter.update` method. + + """ + self.counts.update(*counter_args, **counter_kwargs) + self._len = sum(1 for _ in self) + + def lookup(self, words): + """Look up one or more words in the vocabulary. + + If passed one word as a string will return that word or `self.unk_label`. + Otherwise will assume it was passed a sequence of words, will try to look + each of them up and return an iterator over the looked up words. + + :param words: Word(s) to look up. + :type words: Iterable(str) or str + :rtype: generator(str) or str + :raises: TypeError for types other than strings or iterables + + >>> from nltk.lm import Vocabulary + >>> vocab = Vocabulary(["a", "b", "c", "a", "b"], unk_cutoff=2) + >>> vocab.lookup("a") + 'a' + >>> vocab.lookup("aliens") + '' + >>> vocab.lookup(["a", "b", "c", ["x", "b"]]) + ('a', 'b', '', ('', 'b')) + + """ + return _dispatched_lookup(words, self) + + def __getitem__(self, item): + return self._cutoff if item == self.unk_label else self.counts[item] + + def __contains__(self, item): + """Only consider items with counts GE to cutoff as being in the + vocabulary.""" + return self[item] >= self.cutoff + + def __iter__(self): + """Building on membership check define how to iterate over + vocabulary.""" + return chain( + (item for item in self.counts if item in self), + [self.unk_label] if self.counts else [], + ) + + def __len__(self): + """Computing size of vocabulary reflects the cutoff.""" + return self._len + + def __eq__(self, other): + return ( + self.unk_label == other.unk_label + and self.cutoff == other.cutoff + and self.counts == other.counts + ) + + def __str__(self): + return "<{} with cutoff={} unk_label='{}' and {} items>".format( + self.__class__.__name__, self.cutoff, self.unk_label, len(self) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__init__.py new file mode 100644 index 0000000..2b4b431 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__init__.py @@ -0,0 +1,51 @@ +# Natural Language Toolkit: Metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +# + +""" +NLTK Metrics + +Classes and methods for scoring processing modules. +""" + +from nltk.metrics.agreement import AnnotationTask +from nltk.metrics.aline import align +from nltk.metrics.association import ( + BigramAssocMeasures, + ContingencyMeasures, + NgramAssocMeasures, + QuadgramAssocMeasures, + TrigramAssocMeasures, +) +from nltk.metrics.confusionmatrix import ConfusionMatrix +from nltk.metrics.distance import ( + binary_distance, + custom_distance, + edit_distance, + edit_distance_align, + fractional_presence, + interval_distance, + jaccard_distance, + masi_distance, + presence, +) +from nltk.metrics.paice import Paice +from nltk.metrics.scores import ( + accuracy, + approxrand, + f_measure, + log_likelihood, + precision, + recall, +) +from nltk.metrics.segmentation import ghd, pk, windowdiff +from nltk.metrics.spearman import ( + ranks_from_scores, + ranks_from_sequence, + spearman_correlation, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f3b1d22 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/agreement.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/agreement.cpython-310.pyc new file mode 100644 index 0000000..8138b82 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/agreement.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/aline.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/aline.cpython-310.pyc new file mode 100644 index 0000000..29dbf6d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/aline.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/association.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/association.cpython-310.pyc new file mode 100644 index 0000000..199cc81 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/association.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/confusionmatrix.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/confusionmatrix.cpython-310.pyc new file mode 100644 index 0000000..066220f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/confusionmatrix.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/distance.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/distance.cpython-310.pyc new file mode 100644 index 0000000..af80708 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/distance.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/paice.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/paice.cpython-310.pyc new file mode 100644 index 0000000..5cae516 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/paice.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/scores.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/scores.cpython-310.pyc new file mode 100644 index 0000000..1e67006 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/scores.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/segmentation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/segmentation.cpython-310.pyc new file mode 100644 index 0000000..153541c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/segmentation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/spearman.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/spearman.cpython-310.pyc new file mode 100644 index 0000000..6168e5f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/__pycache__/spearman.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/agreement.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/agreement.py new file mode 100644 index 0000000..4dde35e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/agreement.py @@ -0,0 +1,465 @@ +# Natural Language Toolkit: Agreement Metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tom Lippincott +# URL: +# For license information, see LICENSE.TXT +# + +""" +Implementations of inter-annotator agreement coefficients surveyed by Artstein +and Poesio (2007), Inter-Coder Agreement for Computational Linguistics. + +An agreement coefficient calculates the amount that annotators agreed on label +assignments beyond what is expected by chance. + +In defining the AnnotationTask class, we use naming conventions similar to the +paper's terminology. There are three types of objects in an annotation task: + + the coders (variables "c" and "C") + the items to be annotated (variables "i" and "I") + the potential categories to be assigned (variables "k" and "K") + +Additionally, it is often the case that we don't want to treat two different +labels as complete disagreement, and so the AnnotationTask constructor can also +take a distance metric as a final argument. Distance metrics are simply +functions that take two arguments, and return a value between 0.0 and 1.0 +indicating the distance between them. If not supplied, the default is binary +comparison between the arguments. + +The simplest way to initialize an AnnotationTask is with a list of triples, +each containing a coder's assignment for one object in the task: + + task = AnnotationTask(data=[('c1', '1', 'v1'),('c2', '1', 'v1'),...]) + +Note that the data list needs to contain the same number of triples for each +individual coder, containing category values for the same set of items. + +Alpha (Krippendorff 1980) +Kappa (Cohen 1960) +S (Bennet, Albert and Goldstein 1954) +Pi (Scott 1955) + + +TODO: Describe handling of multiple coders and missing data + +Expected results from the Artstein and Poesio survey paper: + + >>> from nltk.metrics.agreement import AnnotationTask + >>> import os.path + >>> t = AnnotationTask(data=[x.split() for x in open(os.path.join(os.path.dirname(__file__), "artstein_poesio_example.txt"))]) + >>> t.avg_Ao() + 0.88 + >>> round(t.pi(), 5) + 0.79953 + >>> round(t.S(), 2) + 0.82 + + This would have returned a wrong value (0.0) in @785fb79 as coders are in + the wrong order. Subsequently, all values for pi(), S(), and kappa() would + have been wrong as they are computed with avg_Ao(). + >>> t2 = AnnotationTask(data=[('b','1','stat'),('a','1','stat')]) + >>> t2.avg_Ao() + 1.0 + + The following, of course, also works. + >>> t3 = AnnotationTask(data=[('a','1','othr'),('b','1','othr')]) + >>> t3.avg_Ao() + 1.0 + +""" + +import logging +from itertools import groupby +from operator import itemgetter + +from nltk.internals import deprecated +from nltk.metrics.distance import binary_distance +from nltk.probability import ConditionalFreqDist, FreqDist + +log = logging.getLogger(__name__) + + +class AnnotationTask: + """Represents an annotation task, i.e. people assign labels to items. + + Notation tries to match notation in Artstein and Poesio (2007). + + In general, coders and items can be represented as any hashable object. + Integers, for example, are fine, though strings are more readable. + Labels must support the distance functions applied to them, so e.g. + a string-edit-distance makes no sense if your labels are integers, + whereas interval distance needs numeric values. A notable case of this + is the MASI metric, which requires Python sets. + """ + + def __init__(self, data=None, distance=binary_distance): + """Initialize an annotation task. + + The data argument can be None (to create an empty annotation task) or a sequence of 3-tuples, + each representing a coder's labeling of an item: + ``(coder,item,label)`` + + The distance argument is a function taking two arguments (labels) and producing a numerical distance. + The distance from a label to itself should be zero: + ``distance(l,l) = 0`` + """ + self.distance = distance + self.I = set() + self.K = set() + self.C = set() + self.data = [] + if data is not None: + self.load_array(data) + + def __str__(self): + return "\r\n".join( + map( + lambda x: "%s\t%s\t%s" + % (x["coder"], x["item"].replace("_", "\t"), ",".join(x["labels"])), + self.data, + ) + ) + + def load_array(self, array): + """Load an sequence of annotation results, appending to any data already loaded. + + The argument is a sequence of 3-tuples, each representing a coder's labeling of an item: + (coder,item,label) + """ + for coder, item, labels in array: + self.C.add(coder) + self.K.add(labels) + self.I.add(item) + self.data.append({"coder": coder, "labels": labels, "item": item}) + + def agr(self, cA, cB, i, data=None): + """Agreement between two coders on a given item""" + data = data or self.data + # cfedermann: we don't know what combination of coder/item will come + # first in x; to avoid StopIteration problems due to assuming an order + # cA,cB, we allow either for k1 and then look up the missing as k2. + k1 = next(x for x in data if x["coder"] in (cA, cB) and x["item"] == i) + if k1["coder"] == cA: + k2 = next(x for x in data if x["coder"] == cB and x["item"] == i) + else: + k2 = next(x for x in data if x["coder"] == cA and x["item"] == i) + + ret = 1.0 - float(self.distance(k1["labels"], k2["labels"])) + log.debug("Observed agreement between %s and %s on %s: %f", cA, cB, i, ret) + log.debug( + 'Distance between "%r" and "%r": %f', k1["labels"], k2["labels"], 1.0 - ret + ) + return ret + + def Nk(self, k): + return float(sum(1 for x in self.data if x["labels"] == k)) + + def Nik(self, i, k): + return float(sum(1 for x in self.data if x["item"] == i and x["labels"] == k)) + + def Nck(self, c, k): + return float(sum(1 for x in self.data if x["coder"] == c and x["labels"] == k)) + + @deprecated("Use Nk, Nik or Nck instead") + def N(self, k=None, i=None, c=None): + """Implements the "n-notation" used in Artstein and Poesio (2007)""" + if k is not None and i is None and c is None: + ret = self.Nk(k) + elif k is not None and i is not None and c is None: + ret = self.Nik(i, k) + elif k is not None and c is not None and i is None: + ret = self.Nck(c, k) + else: + raise ValueError( + f"You must pass either i or c, not both! (k={k!r},i={i!r},c={c!r})" + ) + log.debug("Count on N[%s,%s,%s]: %d", k, i, c, ret) + return ret + + def _grouped_data(self, field, data=None): + data = data or self.data + return groupby(sorted(data, key=itemgetter(field)), itemgetter(field)) + + def Ao(self, cA, cB): + """Observed agreement between two coders on all items.""" + data = self._grouped_data( + "item", (x for x in self.data if x["coder"] in (cA, cB)) + ) + ret = sum(self.agr(cA, cB, item, item_data) for item, item_data in data) / len( + self.I + ) + log.debug("Observed agreement between %s and %s: %f", cA, cB, ret) + return ret + + def _pairwise_average(self, function): + """ + Calculates the average of function results for each coder pair + """ + total = 0 + n = 0 + s = self.C.copy() + for cA in self.C: + s.remove(cA) + for cB in s: + total += function(cA, cB) + n += 1 + ret = total / n + return ret + + def avg_Ao(self): + """Average observed agreement across all coders and items.""" + ret = self._pairwise_average(self.Ao) + log.debug("Average observed agreement: %f", ret) + return ret + + def Do_Kw_pairwise(self, cA, cB, max_distance=1.0): + """The observed disagreement for the weighted kappa coefficient.""" + total = 0.0 + data = (x for x in self.data if x["coder"] in (cA, cB)) + for i, itemdata in self._grouped_data("item", data): + # we should have two items; distance doesn't care which comes first + total += self.distance(next(itemdata)["labels"], next(itemdata)["labels"]) + + ret = total / (len(self.I) * max_distance) + log.debug("Observed disagreement between %s and %s: %f", cA, cB, ret) + return ret + + def Do_Kw(self, max_distance=1.0): + """Averaged over all labelers""" + ret = self._pairwise_average( + lambda cA, cB: self.Do_Kw_pairwise(cA, cB, max_distance) + ) + log.debug("Observed disagreement: %f", ret) + return ret + + # Agreement Coefficients + def S(self): + """Bennett, Albert and Goldstein 1954""" + Ae = 1.0 / len(self.K) + ret = (self.avg_Ao() - Ae) / (1.0 - Ae) + return ret + + def pi(self): + """Scott 1955; here, multi-pi. + Equivalent to K from Siegel and Castellan (1988). + + """ + total = 0.0 + label_freqs = FreqDist(x["labels"] for x in self.data) + for k, f in label_freqs.items(): + total += f**2 + Ae = total / ((len(self.I) * len(self.C)) ** 2) + return (self.avg_Ao() - Ae) / (1 - Ae) + + def Ae_kappa(self, cA, cB): + Ae = 0.0 + nitems = float(len(self.I)) + label_freqs = ConditionalFreqDist((x["labels"], x["coder"]) for x in self.data) + for k in label_freqs.conditions(): + Ae += (label_freqs[k][cA] / nitems) * (label_freqs[k][cB] / nitems) + return Ae + + def kappa_pairwise(self, cA, cB): + """ """ + Ae = self.Ae_kappa(cA, cB) + ret = (self.Ao(cA, cB) - Ae) / (1.0 - Ae) + log.debug("Expected agreement between %s and %s: %f", cA, cB, Ae) + return ret + + def kappa(self): + """Cohen 1960 + Averages naively over kappas for each coder pair. + + """ + return self._pairwise_average(self.kappa_pairwise) + + def multi_kappa(self): + """Davies and Fleiss 1982 + Averages over observed and expected agreements for each coder pair. + + """ + Ae = self._pairwise_average(self.Ae_kappa) + return (self.avg_Ao() - Ae) / (1.0 - Ae) + + def Disagreement(self, label_freqs): + total_labels = sum(label_freqs.values()) + pairs = 0.0 + for j, nj in label_freqs.items(): + for l, nl in label_freqs.items(): + pairs += float(nj * nl) * self.distance(l, j) + return 1.0 * pairs / (total_labels * (total_labels - 1)) + + def alpha(self): + """Krippendorff 1980""" + # check for degenerate cases + if len(self.K) == 0: + raise ValueError("Cannot calculate alpha, no data present!") + if len(self.K) == 1: + log.debug("Only one annotation value, alpha returning 1.") + return 1 + if len(self.C) == 1 and len(self.I) == 1: + raise ValueError("Cannot calculate alpha, only one coder and item present!") + + total_disagreement = 0.0 + total_ratings = 0 + all_valid_labels_freq = FreqDist([]) + + total_do = 0.0 # Total observed disagreement for all items. + for i, itemdata in self._grouped_data("item"): + label_freqs = FreqDist(x["labels"] for x in itemdata) + labels_count = sum(label_freqs.values()) + if labels_count < 2: + # Ignore the item. + continue + all_valid_labels_freq += label_freqs + total_do += self.Disagreement(label_freqs) * labels_count + + do = total_do / sum(all_valid_labels_freq.values()) + + de = self.Disagreement(all_valid_labels_freq) # Expected disagreement. + k_alpha = 1.0 - do / de + + return k_alpha + + def weighted_kappa_pairwise(self, cA, cB, max_distance=1.0): + """Cohen 1968""" + total = 0.0 + label_freqs = ConditionalFreqDist( + (x["coder"], x["labels"]) for x in self.data if x["coder"] in (cA, cB) + ) + for j in self.K: + for l in self.K: + total += label_freqs[cA][j] * label_freqs[cB][l] * self.distance(j, l) + De = total / (max_distance * pow(len(self.I), 2)) + log.debug("Expected disagreement between %s and %s: %f", cA, cB, De) + Do = self.Do_Kw_pairwise(cA, cB) + ret = 1.0 - (Do / De) + return ret + + def weighted_kappa(self, max_distance=1.0): + """Cohen 1968""" + return self._pairwise_average( + lambda cA, cB: self.weighted_kappa_pairwise(cA, cB, max_distance) + ) + + +if __name__ == "__main__": + + import optparse + import re + + from nltk.metrics import distance + + # process command-line arguments + parser = optparse.OptionParser() + parser.add_option( + "-d", + "--distance", + dest="distance", + default="binary_distance", + help="distance metric to use", + ) + parser.add_option( + "-a", + "--agreement", + dest="agreement", + default="kappa", + help="agreement coefficient to calculate", + ) + parser.add_option( + "-e", + "--exclude", + dest="exclude", + action="append", + default=[], + help="coder names to exclude (may be specified multiple times)", + ) + parser.add_option( + "-i", + "--include", + dest="include", + action="append", + default=[], + help="coder names to include, same format as exclude", + ) + parser.add_option( + "-f", + "--file", + dest="file", + help="file to read labelings from, each line with three columns: 'labeler item labels'", + ) + parser.add_option( + "-v", + "--verbose", + dest="verbose", + default="0", + help="how much debugging to print on stderr (0-4)", + ) + parser.add_option( + "-c", + "--columnsep", + dest="columnsep", + default="\t", + help="char/string that separates the three columns in the file, defaults to tab", + ) + parser.add_option( + "-l", + "--labelsep", + dest="labelsep", + default=",", + help="char/string that separates labels (if labelers can assign more than one), defaults to comma", + ) + parser.add_option( + "-p", + "--presence", + dest="presence", + default=None, + help="convert each labeling into 1 or 0, based on presence of LABEL", + ) + parser.add_option( + "-T", + "--thorough", + dest="thorough", + default=False, + action="store_true", + help="calculate agreement for every subset of the annotators", + ) + (options, remainder) = parser.parse_args() + + if not options.file: + parser.print_help() + exit() + + logging.basicConfig(level=50 - 10 * int(options.verbose)) + + # read in data from the specified file + data = [] + with open(options.file) as infile: + for l in infile: + toks = l.split(options.columnsep) + coder, object_, labels = ( + toks[0], + str(toks[1:-1]), + frozenset(toks[-1].strip().split(options.labelsep)), + ) + if ( + (options.include == options.exclude) + or (len(options.include) > 0 and coder in options.include) + or (len(options.exclude) > 0 and coder not in options.exclude) + ): + data.append((coder, object_, labels)) + + if options.presence: + task = AnnotationTask( + data, getattr(distance, options.distance)(options.presence) + ) + else: + task = AnnotationTask(data, getattr(distance, options.distance)) + + if options.thorough: + pass + else: + print(getattr(task, options.agreement)()) + + logging.shutdown() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/aline.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/aline.py new file mode 100644 index 0000000..664f562 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/aline.py @@ -0,0 +1,1354 @@ +# Natural Language Toolkit: ALINE +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Greg Kondrak +# Geoff Bacon (Python port) +# URL: +# For license information, see LICENSE.TXT + +""" +ALINE +https://webdocs.cs.ualberta.ca/~kondrak/ +Copyright 2002 by Grzegorz Kondrak. + +ALINE is an algorithm for aligning phonetic sequences, described in [1]. +This module is a port of Kondrak's (2002) ALINE. It provides functions for +phonetic sequence alignment and similarity analysis. These are useful in +historical linguistics, sociolinguistics and synchronic phonology. + +ALINE has parameters that can be tuned for desired output. These parameters are: +- C_skip, C_sub, C_exp, C_vwl +- Salience weights +- Segmental features + +In this implementation, some parameters have been changed from their default +values as described in [1], in order to replicate published results. All changes +are noted in comments. + +Example usage +------------- + +# Get optimal alignment of two phonetic sequences + +>>> align('θin', 'tenwis') # doctest: +SKIP +[[('θ', 't'), ('i', 'e'), ('n', 'n'), ('-', 'w'), ('-', 'i'), ('-', 's')]] + +[1] G. Kondrak. Algorithms for Language Reconstruction. PhD dissertation, +University of Toronto. +""" + +try: + import numpy as np +except ImportError: + np = None + +# === Constants === + +inf = float("inf") + +# Default values for maximum similarity scores (Kondrak 2002: 54) +C_skip = -10 # Indels +C_sub = 35 # Substitutions +C_exp = 45 # Expansions/compressions +C_vwl = 5 # Vowel/consonant relative weight (decreased from 10) + +consonants = [ + "B", + "N", + "R", + "b", + "c", + "d", + "f", + "g", + "h", + "j", + "k", + "l", + "m", + "n", + "p", + "q", + "r", + "s", + "t", + "v", + "x", + "z", + "ç", + "ð", + "ħ", + "ŋ", + "ɖ", + "ɟ", + "ɢ", + "ɣ", + "ɦ", + "ɬ", + "ɮ", + "ɰ", + "ɱ", + "ɲ", + "ɳ", + "ɴ", + "ɸ", + "ɹ", + "ɻ", + "ɽ", + "ɾ", + "ʀ", + "ʁ", + "ʂ", + "ʃ", + "ʈ", + "ʋ", + "ʐ ", + "ʒ", + "ʔ", + "ʕ", + "ʙ", + "ʝ", + "β", + "θ", + "χ", + "ʐ", + "w", +] + +# Relevant features for comparing consonants and vowels +R_c = [ + "aspirated", + "lateral", + "manner", + "nasal", + "place", + "retroflex", + "syllabic", + "voice", +] +# 'high' taken out of R_v because same as manner +R_v = [ + "back", + "lateral", + "long", + "manner", + "nasal", + "place", + "retroflex", + "round", + "syllabic", + "voice", +] + +# Flattened feature matrix (Kondrak 2002: 56) +similarity_matrix = { + # place + "bilabial": 1.0, + "labiodental": 0.95, + "dental": 0.9, + "alveolar": 0.85, + "retroflex": 0.8, + "palato-alveolar": 0.75, + "palatal": 0.7, + "velar": 0.6, + "uvular": 0.5, + "pharyngeal": 0.3, + "glottal": 0.1, + "labiovelar": 1.0, + "vowel": -1.0, # added 'vowel' + # manner + "stop": 1.0, + "affricate": 0.9, + "fricative": 0.85, # increased fricative from 0.8 + "trill": 0.7, + "tap": 0.65, + "approximant": 0.6, + "high vowel": 0.4, + "mid vowel": 0.2, + "low vowel": 0.0, + "vowel2": 0.5, # added vowel + # high + "high": 1.0, + "mid": 0.5, + "low": 0.0, + # back + "front": 1.0, + "central": 0.5, + "back": 0.0, + # binary features + "plus": 1.0, + "minus": 0.0, +} + +# Relative weights of phonetic features (Kondrak 2002: 55) +salience = { + "syllabic": 5, + "place": 40, + "manner": 50, + "voice": 5, # decreased from 10 + "nasal": 20, # increased from 10 + "retroflex": 10, + "lateral": 10, + "aspirated": 5, + "long": 0, # decreased from 1 + "high": 3, # decreased from 5 + "back": 2, # decreased from 5 + "round": 2, # decreased from 5 +} + +# (Kondrak 2002: 59-60) +feature_matrix = { + # Consonants + "p": { + "place": "bilabial", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "b": { + "place": "bilabial", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "t": { + "place": "alveolar", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "d": { + "place": "alveolar", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʈ": { + "place": "retroflex", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɖ": { + "place": "retroflex", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "c": { + "place": "palatal", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɟ": { + "place": "palatal", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "k": { + "place": "velar", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "g": { + "place": "velar", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "q": { + "place": "uvular", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɢ": { + "place": "uvular", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʔ": { + "place": "glottal", + "manner": "stop", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "m": { + "place": "bilabial", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɱ": { + "place": "labiodental", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "n": { + "place": "alveolar", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɳ": { + "place": "retroflex", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɲ": { + "place": "palatal", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ŋ": { + "place": "velar", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɴ": { + "place": "uvular", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "N": { + "place": "uvular", + "manner": "stop", + "syllabic": "minus", + "voice": "plus", + "nasal": "plus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʙ": { + "place": "bilabial", + "manner": "trill", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "B": { + "place": "bilabial", + "manner": "trill", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "r": { + "place": "alveolar", + "manner": "trill", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʀ": { + "place": "uvular", + "manner": "trill", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "R": { + "place": "uvular", + "manner": "trill", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɾ": { + "place": "alveolar", + "manner": "tap", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɽ": { + "place": "retroflex", + "manner": "tap", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɸ": { + "place": "bilabial", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "β": { + "place": "bilabial", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "f": { + "place": "labiodental", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "v": { + "place": "labiodental", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "θ": { + "place": "dental", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ð": { + "place": "dental", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "s": { + "place": "alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "z": { + "place": "alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʃ": { + "place": "palato-alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʒ": { + "place": "palato-alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʂ": { + "place": "retroflex", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʐ": { + "place": "retroflex", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "ç": { + "place": "palatal", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʝ": { + "place": "palatal", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "x": { + "place": "velar", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɣ": { + "place": "velar", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "χ": { + "place": "uvular", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʁ": { + "place": "uvular", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ħ": { + "place": "pharyngeal", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ʕ": { + "place": "pharyngeal", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "h": { + "place": "glottal", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɦ": { + "place": "glottal", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɬ": { + "place": "alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "minus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "plus", + "aspirated": "minus", + }, + "ɮ": { + "place": "alveolar", + "manner": "fricative", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "plus", + "aspirated": "minus", + }, + "ʋ": { + "place": "labiodental", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɹ": { + "place": "alveolar", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɻ": { + "place": "retroflex", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "plus", + "lateral": "minus", + "aspirated": "minus", + }, + "j": { + "place": "palatal", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "ɰ": { + "place": "velar", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + "l": { + "place": "alveolar", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "plus", + "aspirated": "minus", + }, + "w": { + "place": "labiovelar", + "manner": "approximant", + "syllabic": "minus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "aspirated": "minus", + }, + # Vowels + "i": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "front", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "y": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "front", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "e": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "front", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "E": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "front", + "round": "minus", + "long": "plus", + "aspirated": "minus", + }, + "ø": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "front", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "ɛ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "front", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "œ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "front", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "æ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "low", + "back": "front", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "a": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "low", + "back": "front", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "A": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "low", + "back": "front", + "round": "minus", + "long": "plus", + "aspirated": "minus", + }, + "ɨ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "central", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "ʉ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "central", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "ə": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "central", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "u": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "back", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "U": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "back", + "round": "plus", + "long": "plus", + "aspirated": "minus", + }, + "o": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "back", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "O": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "back", + "round": "plus", + "long": "plus", + "aspirated": "minus", + }, + "ɔ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "mid", + "back": "back", + "round": "plus", + "long": "minus", + "aspirated": "minus", + }, + "ɒ": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "low", + "back": "back", + "round": "minus", + "long": "minus", + "aspirated": "minus", + }, + "I": { + "place": "vowel", + "manner": "vowel2", + "syllabic": "plus", + "voice": "plus", + "nasal": "minus", + "retroflex": "minus", + "lateral": "minus", + "high": "high", + "back": "front", + "round": "minus", + "long": "plus", + "aspirated": "minus", + }, +} + +# === Algorithm === + + +def align(str1, str2, epsilon=0): + """ + Compute the alignment of two phonetic strings. + + :param str str1: First string to be aligned + :param str str2: Second string to be aligned + + :type epsilon: float (0.0 to 1.0) + :param epsilon: Adjusts threshold similarity score for near-optimal alignments + + :rtype: list(list(tuple(str, str))) + :return: Alignment(s) of str1 and str2 + + (Kondrak 2002: 51) + """ + if np is None: + raise ImportError("You need numpy in order to use the align function") + + assert 0.0 <= epsilon <= 1.0, "Epsilon must be between 0.0 and 1.0." + m = len(str1) + n = len(str2) + # This includes Kondrak's initialization of row 0 and column 0 to all 0s. + S = np.zeros((m + 1, n + 1), dtype=float) + + # If i <= 1 or j <= 1, don't allow expansions as it doesn't make sense, + # and breaks array and string indices. Make sure they never get chosen + # by setting them to -inf. + for i in range(1, m + 1): + for j in range(1, n + 1): + edit1 = S[i - 1, j] + sigma_skip(str1[i - 1]) + edit2 = S[i, j - 1] + sigma_skip(str2[j - 1]) + edit3 = S[i - 1, j - 1] + sigma_sub(str1[i - 1], str2[j - 1]) + if i > 1: + edit4 = S[i - 2, j - 1] + sigma_exp(str2[j - 1], str1[i - 2 : i]) + else: + edit4 = -inf + if j > 1: + edit5 = S[i - 1, j - 2] + sigma_exp(str1[i - 1], str2[j - 2 : j]) + else: + edit5 = -inf + S[i, j] = max(edit1, edit2, edit3, edit4, edit5, 0) + + T = (1 - epsilon) * np.amax(S) # Threshold score for near-optimal alignments + + alignments = [] + for i in range(1, m + 1): + for j in range(1, n + 1): + if S[i, j] >= T: + alignments.append(_retrieve(i, j, 0, S, T, str1, str2, [])) + return alignments + + +def _retrieve(i, j, s, S, T, str1, str2, out): + """ + Retrieve the path through the similarity matrix S starting at (i, j). + + :rtype: list(tuple(str, str)) + :return: Alignment of str1 and str2 + """ + if S[i, j] == 0: + return out + else: + if j > 1 and S[i - 1, j - 2] + sigma_exp(str1[i - 1], str2[j - 2 : j]) + s >= T: + out.insert(0, (str1[i - 1], str2[j - 2 : j])) + _retrieve( + i - 1, + j - 2, + s + sigma_exp(str1[i - 1], str2[j - 2 : j]), + S, + T, + str1, + str2, + out, + ) + elif ( + i > 1 and S[i - 2, j - 1] + sigma_exp(str2[j - 1], str1[i - 2 : i]) + s >= T + ): + out.insert(0, (str1[i - 2 : i], str2[j - 1])) + _retrieve( + i - 2, + j - 1, + s + sigma_exp(str2[j - 1], str1[i - 2 : i]), + S, + T, + str1, + str2, + out, + ) + elif S[i, j - 1] + sigma_skip(str2[j - 1]) + s >= T: + out.insert(0, ("-", str2[j - 1])) + _retrieve(i, j - 1, s + sigma_skip(str2[j - 1]), S, T, str1, str2, out) + elif S[i - 1, j] + sigma_skip(str1[i - 1]) + s >= T: + out.insert(0, (str1[i - 1], "-")) + _retrieve(i - 1, j, s + sigma_skip(str1[i - 1]), S, T, str1, str2, out) + elif S[i - 1, j - 1] + sigma_sub(str1[i - 1], str2[j - 1]) + s >= T: + out.insert(0, (str1[i - 1], str2[j - 1])) + _retrieve( + i - 1, + j - 1, + s + sigma_sub(str1[i - 1], str2[j - 1]), + S, + T, + str1, + str2, + out, + ) + return out + + +def sigma_skip(p): + """ + Returns score of an indel of P. + + (Kondrak 2002: 54) + """ + return C_skip + + +def sigma_sub(p, q): + """ + Returns score of a substitution of P with Q. + + (Kondrak 2002: 54) + """ + return C_sub - delta(p, q) - V(p) - V(q) + + +def sigma_exp(p, q): + """ + Returns score of an expansion/compression. + + (Kondrak 2002: 54) + """ + q1 = q[0] + q2 = q[1] + return C_exp - delta(p, q1) - delta(p, q2) - V(p) - max(V(q1), V(q2)) + + +def delta(p, q): + """ + Return weighted sum of difference between P and Q. + + (Kondrak 2002: 54) + """ + features = R(p, q) + total = 0 + for f in features: + total += diff(p, q, f) * salience[f] + return total + + +def diff(p, q, f): + """ + Returns difference between phonetic segments P and Q for feature F. + + (Kondrak 2002: 52, 54) + """ + p_features, q_features = feature_matrix[p], feature_matrix[q] + return abs(similarity_matrix[p_features[f]] - similarity_matrix[q_features[f]]) + + +def R(p, q): + """ + Return relevant features for segment comparison. + + (Kondrak 2002: 54) + """ + if p in consonants or q in consonants: + return R_c + return R_v + + +def V(p): + """ + Return vowel weight if P is vowel. + + (Kondrak 2002: 54) + """ + if p in consonants: + return 0 + return C_vwl + + +# === Test === + + +def demo(): + """ + A demonstration of the result of aligning phonetic sequences + used in Kondrak's (2002) dissertation. + """ + data = [pair.split(",") for pair in cognate_data.split("\n")] + for pair in data: + alignment = align(pair[0], pair[1])[0] + alignment = [f"({a[0]}, {a[1]})" for a in alignment] + alignment = " ".join(alignment) + print(f"{pair[0]} ~ {pair[1]} : {alignment}") + + +cognate_data = """jo,ʒə +tu,ty +nosotros,nu +kjen,ki +ke,kwa +todos,tu +una,ən +dos,dø +tres,trwa +ombre,om +arbol,arbrə +pluma,plym +kabeθa,kap +boka,buʃ +pje,pje +koraθon,kœr +ber,vwar +benir,vənir +deθir,dir +pobre,povrə +ðis,dIzes +ðæt,das +wat,vas +nat,nixt +loŋ,laŋ +mæn,man +fleʃ,flajʃ +bləd,blyt +feðər,fEdər +hær,hAr +ir,Or +aj,awgə +nowz,nAzə +mawθ,munt +təŋ,tsuŋə +fut,fys +nij,knI +hænd,hant +hart,herts +livər,lEbər +ænd,ante +æt,ad +blow,flAre +ir,awris +ijt,edere +fiʃ,piʃkis +flow,fluere +staɾ,stella +ful,plenus +græs,gramen +hart,kordis +horn,korny +aj,ego +nij,genU +məðər,mAter +mawntən,mons +nejm,nomen +njuw,nowus +wən,unus +rawnd,rotundus +sow,suere +sit,sedere +θrij,tres +tuwθ,dentis +θin,tenwis +kinwawa,kenuaʔ +nina,nenah +napewa,napɛw +wapimini,wapemen +namesa,namɛʔs +okimawa,okemaw +ʃiʃipa,seʔsep +ahkohkwa,ahkɛh +pematesiweni,pematesewen +asenja,aʔsɛn""" + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/association.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/association.py new file mode 100644 index 0000000..dd65158 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/association.py @@ -0,0 +1,476 @@ +# Natural Language Toolkit: Ngram Association Measures +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Joel Nothman +# URL: +# For license information, see LICENSE.TXT + +""" +Provides scoring functions for a number of association measures through a +generic, abstract implementation in ``NgramAssocMeasures``, and n-specific +``BigramAssocMeasures`` and ``TrigramAssocMeasures``. +""" + +import math as _math +from abc import ABCMeta, abstractmethod +from functools import reduce + +_log2 = lambda x: _math.log2(x) +_ln = _math.log + +_product = lambda s: reduce(lambda x, y: x * y, s) + +_SMALL = 1e-20 + +try: + from scipy.stats import fisher_exact +except ImportError: + + def fisher_exact(*_args, **_kwargs): + raise NotImplementedError + + +### Indices to marginals arguments: + +NGRAM = 0 +"""Marginals index for the ngram count""" + +UNIGRAMS = -2 +"""Marginals index for a tuple of each unigram count""" + +TOTAL = -1 +"""Marginals index for the number of words in the data""" + + +class NgramAssocMeasures(metaclass=ABCMeta): + """ + An abstract class defining a collection of generic association measures. + Each public method returns a score, taking the following arguments:: + + score_fn(count_of_ngram, + (count_of_n-1gram_1, ..., count_of_n-1gram_j), + (count_of_n-2gram_1, ..., count_of_n-2gram_k), + ..., + (count_of_1gram_1, ..., count_of_1gram_n), + count_of_total_words) + + See ``BigramAssocMeasures`` and ``TrigramAssocMeasures`` + + Inheriting classes should define a property _n, and a method _contingency + which calculates contingency values from marginals in order for all + association measures defined here to be usable. + """ + + _n = 0 + + @staticmethod + @abstractmethod + def _contingency(*marginals): + """Calculates values of a contingency table from marginal values.""" + raise NotImplementedError( + "The contingency table is not available" "in the general ngram case" + ) + + @staticmethod + @abstractmethod + def _marginals(*contingency): + """Calculates values of contingency table marginals from its values.""" + raise NotImplementedError( + "The contingency table is not available" "in the general ngram case" + ) + + @classmethod + def _expected_values(cls, cont): + """Calculates expected values for a contingency table.""" + n_all = sum(cont) + bits = [1 << i for i in range(cls._n)] + + # For each contingency table cell + for i in range(len(cont)): + # Yield the expected value + yield ( + _product( + sum(cont[x] for x in range(2**cls._n) if (x & j) == (i & j)) + for j in bits + ) + / (n_all ** (cls._n - 1)) + ) + + @staticmethod + def raw_freq(*marginals): + """Scores ngrams by their frequency""" + return marginals[NGRAM] / marginals[TOTAL] + + @classmethod + def student_t(cls, *marginals): + """Scores ngrams using Student's t test with independence hypothesis + for unigrams, as in Manning and Schutze 5.3.1. + """ + return ( + marginals[NGRAM] + - _product(marginals[UNIGRAMS]) / (marginals[TOTAL] ** (cls._n - 1)) + ) / (marginals[NGRAM] + _SMALL) ** 0.5 + + @classmethod + def chi_sq(cls, *marginals): + """Scores ngrams using Pearson's chi-square as in Manning and Schutze + 5.3.3. + """ + cont = cls._contingency(*marginals) + exps = cls._expected_values(cont) + return sum((obs - exp) ** 2 / (exp + _SMALL) for obs, exp in zip(cont, exps)) + + @staticmethod + def mi_like(*marginals, **kwargs): + """Scores ngrams using a variant of mutual information. The keyword + argument power sets an exponent (default 3) for the numerator. No + logarithm of the result is calculated. + """ + return marginals[NGRAM] ** kwargs.get("power", 3) / _product( + marginals[UNIGRAMS] + ) + + @classmethod + def pmi(cls, *marginals): + """Scores ngrams by pointwise mutual information, as in Manning and + Schutze 5.4. + """ + return _log2(marginals[NGRAM] * marginals[TOTAL] ** (cls._n - 1)) - _log2( + _product(marginals[UNIGRAMS]) + ) + + @classmethod + def likelihood_ratio(cls, *marginals): + """Scores ngrams using likelihood ratios as in Manning and Schutze 5.3.4.""" + cont = cls._contingency(*marginals) + return 2 * sum( + obs * _ln(obs / (exp + _SMALL) + _SMALL) + for obs, exp in zip(cont, cls._expected_values(cont)) + ) + + @classmethod + def poisson_stirling(cls, *marginals): + """Scores ngrams using the Poisson-Stirling measure.""" + exp = _product(marginals[UNIGRAMS]) / (marginals[TOTAL] ** (cls._n - 1)) + return marginals[NGRAM] * (_log2(marginals[NGRAM] / exp) - 1) + + @classmethod + def jaccard(cls, *marginals): + """Scores ngrams using the Jaccard index.""" + cont = cls._contingency(*marginals) + return cont[0] / sum(cont[:-1]) + + +class BigramAssocMeasures(NgramAssocMeasures): + """ + A collection of bigram association measures. Each association measure + is provided as a function with three arguments:: + + bigram_score_fn(n_ii, (n_ix, n_xi), n_xx) + + The arguments constitute the marginals of a contingency table, counting + the occurrences of particular events in a corpus. The letter i in the + suffix refers to the appearance of the word in question, while x indicates + the appearance of any word. Thus, for example: + + - n_ii counts ``(w1, w2)``, i.e. the bigram being scored + - n_ix counts ``(w1, *)`` + - n_xi counts ``(*, w2)`` + - n_xx counts ``(*, *)``, i.e. any bigram + + This may be shown with respect to a contingency table:: + + w1 ~w1 + ------ ------ + w2 | n_ii | n_oi | = n_xi + ------ ------ + ~w2 | n_io | n_oo | + ------ ------ + = n_ix TOTAL = n_xx + """ + + _n = 2 + + @staticmethod + def _contingency(n_ii, n_ix_xi_tuple, n_xx): + """Calculates values of a bigram contingency table from marginal values.""" + (n_ix, n_xi) = n_ix_xi_tuple + n_oi = n_xi - n_ii + n_io = n_ix - n_ii + return (n_ii, n_oi, n_io, n_xx - n_ii - n_oi - n_io) + + @staticmethod + def _marginals(n_ii, n_oi, n_io, n_oo): + """Calculates values of contingency table marginals from its values.""" + return (n_ii, (n_oi + n_ii, n_io + n_ii), n_oo + n_oi + n_io + n_ii) + + @staticmethod + def _expected_values(cont): + """Calculates expected values for a contingency table.""" + n_xx = sum(cont) + # For each contingency table cell + for i in range(4): + yield (cont[i] + cont[i ^ 1]) * (cont[i] + cont[i ^ 2]) / n_xx + + @classmethod + def phi_sq(cls, *marginals): + """Scores bigrams using phi-square, the square of the Pearson correlation + coefficient. + """ + n_ii, n_io, n_oi, n_oo = cls._contingency(*marginals) + + return (n_ii * n_oo - n_io * n_oi) ** 2 / ( + (n_ii + n_io) * (n_ii + n_oi) * (n_io + n_oo) * (n_oi + n_oo) + ) + + @classmethod + def chi_sq(cls, n_ii, n_ix_xi_tuple, n_xx): + """Scores bigrams using chi-square, i.e. phi-sq multiplied by the number + of bigrams, as in Manning and Schutze 5.3.3. + """ + (n_ix, n_xi) = n_ix_xi_tuple + return n_xx * cls.phi_sq(n_ii, (n_ix, n_xi), n_xx) + + @classmethod + def fisher(cls, *marginals): + """Scores bigrams using Fisher's Exact Test (Pedersen 1996). Less + sensitive to small counts than PMI or Chi Sq, but also more expensive + to compute. Requires scipy. + """ + + n_ii, n_io, n_oi, n_oo = cls._contingency(*marginals) + + (odds, pvalue) = fisher_exact([[n_ii, n_io], [n_oi, n_oo]], alternative="less") + return pvalue + + @staticmethod + def dice(n_ii, n_ix_xi_tuple, n_xx): + """Scores bigrams using Dice's coefficient.""" + (n_ix, n_xi) = n_ix_xi_tuple + return 2 * n_ii / (n_ix + n_xi) + + +class TrigramAssocMeasures(NgramAssocMeasures): + """ + A collection of trigram association measures. Each association measure + is provided as a function with four arguments:: + + trigram_score_fn(n_iii, + (n_iix, n_ixi, n_xii), + (n_ixx, n_xix, n_xxi), + n_xxx) + + The arguments constitute the marginals of a contingency table, counting + the occurrences of particular events in a corpus. The letter i in the + suffix refers to the appearance of the word in question, while x indicates + the appearance of any word. Thus, for example: + + - n_iii counts ``(w1, w2, w3)``, i.e. the trigram being scored + - n_ixx counts ``(w1, *, *)`` + - n_xxx counts ``(*, *, *)``, i.e. any trigram + """ + + _n = 3 + + @staticmethod + def _contingency(n_iii, n_iix_tuple, n_ixx_tuple, n_xxx): + """Calculates values of a trigram contingency table (or cube) from + marginal values. + >>> TrigramAssocMeasures._contingency(1, (1, 1, 1), (1, 73, 1), 2000) + (1, 0, 0, 0, 0, 72, 0, 1927) + """ + (n_iix, n_ixi, n_xii) = n_iix_tuple + (n_ixx, n_xix, n_xxi) = n_ixx_tuple + n_oii = n_xii - n_iii + n_ioi = n_ixi - n_iii + n_iio = n_iix - n_iii + n_ooi = n_xxi - n_iii - n_oii - n_ioi + n_oio = n_xix - n_iii - n_oii - n_iio + n_ioo = n_ixx - n_iii - n_ioi - n_iio + n_ooo = n_xxx - n_iii - n_oii - n_ioi - n_iio - n_ooi - n_oio - n_ioo + + return (n_iii, n_oii, n_ioi, n_ooi, n_iio, n_oio, n_ioo, n_ooo) + + @staticmethod + def _marginals(*contingency): + """Calculates values of contingency table marginals from its values. + >>> TrigramAssocMeasures._marginals(1, 0, 0, 0, 0, 72, 0, 1927) + (1, (1, 1, 1), (1, 73, 1), 2000) + """ + n_iii, n_oii, n_ioi, n_ooi, n_iio, n_oio, n_ioo, n_ooo = contingency + return ( + n_iii, + (n_iii + n_iio, n_iii + n_ioi, n_iii + n_oii), + ( + n_iii + n_ioi + n_iio + n_ioo, + n_iii + n_oii + n_iio + n_oio, + n_iii + n_oii + n_ioi + n_ooi, + ), + sum(contingency), + ) + + +class QuadgramAssocMeasures(NgramAssocMeasures): + """ + A collection of quadgram association measures. Each association measure + is provided as a function with five arguments:: + + trigram_score_fn(n_iiii, + (n_iiix, n_iixi, n_ixii, n_xiii), + (n_iixx, n_ixix, n_ixxi, n_xixi, n_xxii, n_xiix), + (n_ixxx, n_xixx, n_xxix, n_xxxi), + n_all) + + The arguments constitute the marginals of a contingency table, counting + the occurrences of particular events in a corpus. The letter i in the + suffix refers to the appearance of the word in question, while x indicates + the appearance of any word. Thus, for example: + + - n_iiii counts ``(w1, w2, w3, w4)``, i.e. the quadgram being scored + - n_ixxi counts ``(w1, *, *, w4)`` + - n_xxxx counts ``(*, *, *, *)``, i.e. any quadgram + """ + + _n = 4 + + @staticmethod + def _contingency(n_iiii, n_iiix_tuple, n_iixx_tuple, n_ixxx_tuple, n_xxxx): + """Calculates values of a quadgram contingency table from + marginal values. + """ + (n_iiix, n_iixi, n_ixii, n_xiii) = n_iiix_tuple + (n_iixx, n_ixix, n_ixxi, n_xixi, n_xxii, n_xiix) = n_iixx_tuple + (n_ixxx, n_xixx, n_xxix, n_xxxi) = n_ixxx_tuple + n_oiii = n_xiii - n_iiii + n_ioii = n_ixii - n_iiii + n_iioi = n_iixi - n_iiii + n_ooii = n_xxii - n_iiii - n_oiii - n_ioii + n_oioi = n_xixi - n_iiii - n_oiii - n_iioi + n_iooi = n_ixxi - n_iiii - n_ioii - n_iioi + n_oooi = n_xxxi - n_iiii - n_oiii - n_ioii - n_iioi - n_ooii - n_iooi - n_oioi + n_iiio = n_iiix - n_iiii + n_oiio = n_xiix - n_iiii - n_oiii - n_iiio + n_ioio = n_ixix - n_iiii - n_ioii - n_iiio + n_ooio = n_xxix - n_iiii - n_oiii - n_ioii - n_iiio - n_ooii - n_ioio - n_oiio + n_iioo = n_iixx - n_iiii - n_iioi - n_iiio + n_oioo = n_xixx - n_iiii - n_oiii - n_iioi - n_iiio - n_oioi - n_oiio - n_iioo + n_iooo = n_ixxx - n_iiii - n_ioii - n_iioi - n_iiio - n_iooi - n_iioo - n_ioio + n_oooo = ( + n_xxxx + - n_iiii + - n_oiii + - n_ioii + - n_iioi + - n_ooii + - n_oioi + - n_iooi + - n_oooi + - n_iiio + - n_oiio + - n_ioio + - n_ooio + - n_iioo + - n_oioo + - n_iooo + ) + + return ( + n_iiii, + n_oiii, + n_ioii, + n_ooii, + n_iioi, + n_oioi, + n_iooi, + n_oooi, + n_iiio, + n_oiio, + n_ioio, + n_ooio, + n_iioo, + n_oioo, + n_iooo, + n_oooo, + ) + + @staticmethod + def _marginals(*contingency): + """Calculates values of contingency table marginals from its values. + QuadgramAssocMeasures._marginals(1, 0, 2, 46, 552, 825, 2577, 34967, 1, 0, 2, 48, 7250, 9031, 28585, 356653) + (1, (2, 553, 3, 1), (7804, 6, 3132, 1378, 49, 2), (38970, 17660, 100, 38970), 440540) + """ + ( + n_iiii, + n_oiii, + n_ioii, + n_ooii, + n_iioi, + n_oioi, + n_iooi, + n_oooi, + n_iiio, + n_oiio, + n_ioio, + n_ooio, + n_iioo, + n_oioo, + n_iooo, + n_oooo, + ) = contingency + + n_iiix = n_iiii + n_iiio + n_iixi = n_iiii + n_iioi + n_ixii = n_iiii + n_ioii + n_xiii = n_iiii + n_oiii + + n_iixx = n_iiii + n_iioi + n_iiio + n_iioo + n_ixix = n_iiii + n_ioii + n_iiio + n_ioio + n_ixxi = n_iiii + n_ioii + n_iioi + n_iooi + n_xixi = n_iiii + n_oiii + n_iioi + n_oioi + n_xxii = n_iiii + n_oiii + n_ioii + n_ooii + n_xiix = n_iiii + n_oiii + n_iiio + n_oiio + + n_ixxx = n_iiii + n_ioii + n_iioi + n_iiio + n_iooi + n_iioo + n_ioio + n_iooo + n_xixx = n_iiii + n_oiii + n_iioi + n_iiio + n_oioi + n_oiio + n_iioo + n_oioo + n_xxix = n_iiii + n_oiii + n_ioii + n_iiio + n_ooii + n_ioio + n_oiio + n_ooio + n_xxxi = n_iiii + n_oiii + n_ioii + n_iioi + n_ooii + n_iooi + n_oioi + n_oooi + + n_all = sum(contingency) + + return ( + n_iiii, + (n_iiix, n_iixi, n_ixii, n_xiii), + (n_iixx, n_ixix, n_ixxi, n_xixi, n_xxii, n_xiix), + (n_ixxx, n_xixx, n_xxix, n_xxxi), + n_all, + ) + + +class ContingencyMeasures: + """Wraps NgramAssocMeasures classes such that the arguments of association + measures are contingency table values rather than marginals. + """ + + def __init__(self, measures): + """Constructs a ContingencyMeasures given a NgramAssocMeasures class""" + self.__class__.__name__ = "Contingency" + measures.__class__.__name__ + for k in dir(measures): + if k.startswith("__"): + continue + v = getattr(measures, k) + if not k.startswith("_"): + v = self._make_contingency_fn(measures, v) + setattr(self, k, v) + + @staticmethod + def _make_contingency_fn(measures, old_fn): + """From an association measure function, produces a new function which + accepts contingency table values as its arguments. + """ + + def res(*contingency): + return old_fn(*measures._marginals(*contingency)) + + res.__doc__ = old_fn.__doc__ + res.__name__ = old_fn.__name__ + return res diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/confusionmatrix.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/confusionmatrix.py new file mode 100644 index 0000000..cd82951 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/confusionmatrix.py @@ -0,0 +1,353 @@ +# Natural Language Toolkit: Confusion Matrices +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +from nltk.probability import FreqDist + + +class ConfusionMatrix: + """ + The confusion matrix between a list of reference values and a + corresponding list of test values. Entry *[r,t]* of this + matrix is a count of the number of times that the reference value + *r* corresponds to the test value *t*. E.g.: + + >>> from nltk.metrics import ConfusionMatrix + >>> ref = 'DET NN VB DET JJ NN NN IN DET NN'.split() + >>> test = 'DET VB VB DET NN NN NN IN DET NN'.split() + >>> cm = ConfusionMatrix(ref, test) + >>> print(cm['NN', 'NN']) + 3 + + Note that the diagonal entries *Ri=Tj* of this matrix + corresponds to correct values; and the off-diagonal entries + correspond to incorrect values. + """ + + def __init__(self, reference, test, sort_by_count=False): + """ + Construct a new confusion matrix from a list of reference + values and a corresponding list of test values. + + :type reference: list + :param reference: An ordered list of reference values. + :type test: list + :param test: A list of values to compare against the + corresponding reference values. + :raise ValueError: If ``reference`` and ``length`` do not have + the same length. + """ + if len(reference) != len(test): + raise ValueError("Lists must have the same length.") + + # Get a list of all values. + if sort_by_count: + ref_fdist = FreqDist(reference) + test_fdist = FreqDist(test) + + def key(v): + return -(ref_fdist[v] + test_fdist[v]) + + values = sorted(set(reference + test), key=key) + else: + values = sorted(set(reference + test)) + + # Construct a value->index dictionary + indices = {val: i for (i, val) in enumerate(values)} + + # Make a confusion matrix table. + confusion = [[0 for _ in values] for _ in values] + max_conf = 0 # Maximum confusion + for w, g in zip(reference, test): + confusion[indices[w]][indices[g]] += 1 + max_conf = max(max_conf, confusion[indices[w]][indices[g]]) + + #: A list of all values in ``reference`` or ``test``. + self._values = values + #: A dictionary mapping values in ``self._values`` to their indices. + self._indices = indices + #: The confusion matrix itself (as a list of lists of counts). + self._confusion = confusion + #: The greatest count in ``self._confusion`` (used for printing). + self._max_conf = max_conf + #: The total number of values in the confusion matrix. + self._total = len(reference) + #: The number of correct (on-diagonal) values in the matrix. + self._correct = sum(confusion[i][i] for i in range(len(values))) + + def __getitem__(self, li_lj_tuple): + """ + :return: The number of times that value ``li`` was expected and + value ``lj`` was given. + :rtype: int + """ + (li, lj) = li_lj_tuple + i = self._indices[li] + j = self._indices[lj] + return self._confusion[i][j] + + def __repr__(self): + return f"" + + def __str__(self): + return self.pretty_format() + + def pretty_format( + self, + show_percents=False, + values_in_chart=True, + truncate=None, + sort_by_count=False, + ): + """ + :return: A multi-line string representation of this confusion matrix. + :type truncate: int + :param truncate: If specified, then only show the specified + number of values. Any sorting (e.g., sort_by_count) + will be performed before truncation. + :param sort_by_count: If true, then sort by the count of each + label in the reference data. I.e., labels that occur more + frequently in the reference label will be towards the left + edge of the matrix, and labels that occur less frequently + will be towards the right edge. + + @todo: add marginals? + """ + confusion = self._confusion + + values = self._values + if sort_by_count: + values = sorted( + values, key=lambda v: -sum(self._confusion[self._indices[v]]) + ) + + if truncate: + values = values[:truncate] + + if values_in_chart: + value_strings = ["%s" % val for val in values] + else: + value_strings = [str(n + 1) for n in range(len(values))] + + # Construct a format string for row values + valuelen = max(len(val) for val in value_strings) + value_format = "%" + repr(valuelen) + "s | " + # Construct a format string for matrix entries + if show_percents: + entrylen = 6 + entry_format = "%5.1f%%" + zerostr = " ." + else: + entrylen = len(repr(self._max_conf)) + entry_format = "%" + repr(entrylen) + "d" + zerostr = " " * (entrylen - 1) + "." + + # Write the column values. + s = "" + for i in range(valuelen): + s += (" " * valuelen) + " |" + for val in value_strings: + if i >= valuelen - len(val): + s += val[i - valuelen + len(val)].rjust(entrylen + 1) + else: + s += " " * (entrylen + 1) + s += " |\n" + + # Write a dividing line + s += "{}-+-{}+\n".format("-" * valuelen, "-" * ((entrylen + 1) * len(values))) + + # Write the entries. + for val, li in zip(value_strings, values): + i = self._indices[li] + s += value_format % val + for lj in values: + j = self._indices[lj] + if confusion[i][j] == 0: + s += zerostr + elif show_percents: + s += entry_format % (100.0 * confusion[i][j] / self._total) + else: + s += entry_format % confusion[i][j] + if i == j: + prevspace = s.rfind(" ") + s = s[:prevspace] + "<" + s[prevspace + 1 :] + ">" + else: + s += " " + s += "|\n" + + # Write a dividing line + s += "{}-+-{}+\n".format("-" * valuelen, "-" * ((entrylen + 1) * len(values))) + + # Write a key + s += "(row = reference; col = test)\n" + if not values_in_chart: + s += "Value key:\n" + for i, value in enumerate(values): + s += "%6d: %s\n" % (i + 1, value) + + return s + + def key(self): + values = self._values + str = "Value key:\n" + indexlen = len(repr(len(values) - 1)) + key_format = " %" + repr(indexlen) + "d: %s\n" + for i in range(len(values)): + str += key_format % (i, values[i]) + + return str + + def recall(self, value): + """Given a value in the confusion matrix, return the recall + that corresponds to this value. The recall is defined as: + + - *r* = true positive / (true positive + false positive) + + and can loosely be considered the ratio of how often ``value`` + was predicted correctly relative to how often ``value`` was + the true result. + + :param value: value used in the ConfusionMatrix + :return: the recall corresponding to ``value``. + :rtype: float + """ + # Number of times `value` was correct, and also predicted + TP = self[value, value] + # Number of times `value` was correct + TP_FN = sum(self[value, pred_value] for pred_value in self._values) + if TP_FN == 0: + return 0.0 + return TP / TP_FN + + def precision(self, value): + """Given a value in the confusion matrix, return the precision + that corresponds to this value. The precision is defined as: + + - *p* = true positive / (true positive + false negative) + + and can loosely be considered the ratio of how often ``value`` + was predicted correctly relative to the number of predictions + for ``value``. + + :param value: value used in the ConfusionMatrix + :return: the precision corresponding to ``value``. + :rtype: float + """ + # Number of times `value` was correct, and also predicted + TP = self[value, value] + # Number of times `value` was predicted + TP_FP = sum(self[real_value, value] for real_value in self._values) + if TP_FP == 0: + return 0.0 + return TP / TP_FP + + def f_measure(self, value, alpha=0.5): + """ + Given a value used in the confusion matrix, return the f-measure + that corresponds to this value. The f-measure is the harmonic mean + of the ``precision`` and ``recall``, weighted by ``alpha``. + In particular, given the precision *p* and recall *r* defined by: + + - *p* = true positive / (true positive + false negative) + - *r* = true positive / (true positive + false positive) + + The f-measure is: + + - *1/(alpha/p + (1-alpha)/r)* + + With ``alpha = 0.5``, this reduces to: + + - *2pr / (p + r)* + + :param value: value used in the ConfusionMatrix + :param alpha: Ratio of the cost of false negative compared to false + positives. Defaults to 0.5, where the costs are equal. + :type alpha: float + :return: the F-measure corresponding to ``value``. + :rtype: float + """ + p = self.precision(value) + r = self.recall(value) + if p == 0.0 or r == 0.0: + return 0.0 + return 1.0 / (alpha / p + (1 - alpha) / r) + + def evaluate(self, alpha=0.5, truncate=None, sort_by_count=False): + """ + Tabulate the **recall**, **precision** and **f-measure** + for each value in this confusion matrix. + + >>> reference = "DET NN VB DET JJ NN NN IN DET NN".split() + >>> test = "DET VB VB DET NN NN NN IN DET NN".split() + >>> cm = ConfusionMatrix(reference, test) + >>> print(cm.evaluate()) + Tag | Prec. | Recall | F-measure + ----+--------+--------+----------- + DET | 1.0000 | 1.0000 | 1.0000 + IN | 1.0000 | 1.0000 | 1.0000 + JJ | 0.0000 | 0.0000 | 0.0000 + NN | 0.7500 | 0.7500 | 0.7500 + VB | 0.5000 | 1.0000 | 0.6667 + + + :param alpha: Ratio of the cost of false negative compared to false + positives, as used in the f-measure computation. Defaults to 0.5, + where the costs are equal. + :type alpha: float + :param truncate: If specified, then only show the specified + number of values. Any sorting (e.g., sort_by_count) + will be performed before truncation. Defaults to None + :type truncate: int, optional + :param sort_by_count: Whether to sort the outputs on frequency + in the reference label. Defaults to False. + :type sort_by_count: bool, optional + :return: A tabulated recall, precision and f-measure string + :rtype: str + """ + tags = self._values + + # Apply keyword parameters + if sort_by_count: + tags = sorted(tags, key=lambda v: -sum(self._confusion[self._indices[v]])) + if truncate: + tags = tags[:truncate] + + tag_column_len = max(max(len(tag) for tag in tags), 3) + + # Construct the header + s = ( + f"{' ' * (tag_column_len - 3)}Tag | Prec. | Recall | F-measure\n" + f"{'-' * tag_column_len}-+--------+--------+-----------\n" + ) + + # Construct the body + for tag in tags: + s += ( + f"{tag:>{tag_column_len}} | " + f"{self.precision(tag):<6.4f} | " + f"{self.recall(tag):<6.4f} | " + f"{self.f_measure(tag, alpha=alpha):.4f}\n" + ) + + return s + + +def demo(): + reference = "DET NN VB DET JJ NN NN IN DET NN".split() + test = "DET VB VB DET NN NN NN IN DET NN".split() + print("Reference =", reference) + print("Test =", test) + print("Confusion matrix:") + print(ConfusionMatrix(reference, test)) + print(ConfusionMatrix(reference, test).pretty_format(sort_by_count=True)) + + print(ConfusionMatrix(reference, test).recall("VB")) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/distance.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/distance.py new file mode 100644 index 0000000..9ac0c28 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/distance.py @@ -0,0 +1,508 @@ +# Natural Language Toolkit: Distance Metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Tom Lippincott +# URL: +# For license information, see LICENSE.TXT +# + +""" +Distance Metrics. + +Compute the distance between two items (usually strings). +As metrics, they must satisfy the following three requirements: + +1. d(a, a) = 0 +2. d(a, b) >= 0 +3. d(a, c) <= d(a, b) + d(b, c) +""" + +import operator +import warnings + + +def _edit_dist_init(len1, len2): + lev = [] + for i in range(len1): + lev.append([0] * len2) # initialize 2D array to zero + for i in range(len1): + lev[i][0] = i # column 0: 0,1,2,3,4,... + for j in range(len2): + lev[0][j] = j # row 0: 0,1,2,3,4,... + return lev + + +def _last_left_t_init(sigma): + return {c: 0 for c in sigma} + + +def _edit_dist_step( + lev, i, j, s1, s2, last_left, last_right, substitution_cost=1, transpositions=False +): + c1 = s1[i - 1] + c2 = s2[j - 1] + + # skipping a character in s1 + a = lev[i - 1][j] + 1 + # skipping a character in s2 + b = lev[i][j - 1] + 1 + # substitution + c = lev[i - 1][j - 1] + (substitution_cost if c1 != c2 else 0) + + # transposition + d = c + 1 # never picked by default + if transpositions and last_left > 0 and last_right > 0: + d = lev[last_left - 1][last_right - 1] + i - last_left + j - last_right - 1 + + # pick the cheapest + lev[i][j] = min(a, b, c, d) + + +def edit_distance(s1, s2, substitution_cost=1, transpositions=False): + """ + Calculate the Levenshtein edit-distance between two strings. + The edit distance is the number of characters that need to be + substituted, inserted, or deleted, to transform s1 into s2. For + example, transforming "rain" to "shine" requires three steps, + consisting of two substitutions and one insertion: + "rain" -> "sain" -> "shin" -> "shine". These operations could have + been done in other orders, but at least three steps are needed. + + Allows specifying the cost of substitution edits (e.g., "a" -> "b"), + because sometimes it makes sense to assign greater penalties to + substitutions. + + This also optionally allows transposition edits (e.g., "ab" -> "ba"), + though this is disabled by default. + + :param s1, s2: The strings to be analysed + :param transpositions: Whether to allow transposition edits + :type s1: str + :type s2: str + :type substitution_cost: int + :type transpositions: bool + :rtype: int + """ + # set up a 2-D array + len1 = len(s1) + len2 = len(s2) + lev = _edit_dist_init(len1 + 1, len2 + 1) + + # retrieve alphabet + sigma = set() + sigma.update(s1) + sigma.update(s2) + + # set up table to remember positions of last seen occurrence in s1 + last_left_t = _last_left_t_init(sigma) + + # iterate over the array + # i and j start from 1 and not 0 to stay close to the wikipedia pseudo-code + # see https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance + for i in range(1, len1 + 1): + last_right_buf = 0 + for j in range(1, len2 + 1): + last_left = last_left_t[s2[j - 1]] + last_right = last_right_buf + if s1[i - 1] == s2[j - 1]: + last_right_buf = j + _edit_dist_step( + lev, + i, + j, + s1, + s2, + last_left, + last_right, + substitution_cost=substitution_cost, + transpositions=transpositions, + ) + last_left_t[s1[i - 1]] = i + return lev[len1][len2] + + +def _edit_dist_backtrace(lev): + i, j = len(lev) - 1, len(lev[0]) - 1 + alignment = [(i, j)] + + while (i, j) != (0, 0): + directions = [ + (i - 1, j - 1), # substitution + (i - 1, j), # skip s1 + (i, j - 1), # skip s2 + ] + + direction_costs = ( + (lev[i][j] if (i >= 0 and j >= 0) else float("inf"), (i, j)) + for i, j in directions + ) + _, (i, j) = min(direction_costs, key=operator.itemgetter(0)) + + alignment.append((i, j)) + return list(reversed(alignment)) + + +def edit_distance_align(s1, s2, substitution_cost=1): + """ + Calculate the minimum Levenshtein edit-distance based alignment + mapping between two strings. The alignment finds the mapping + from string s1 to s2 that minimizes the edit distance cost. + For example, mapping "rain" to "shine" would involve 2 + substitutions, 2 matches and an insertion resulting in + the following mapping: + [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (4, 5)] + NB: (0, 0) is the start state without any letters associated + See more: https://web.stanford.edu/class/cs124/lec/med.pdf + + In case of multiple valid minimum-distance alignments, the + backtrace has the following operation precedence: + + 1. Substitute s1 and s2 characters + 2. Skip s1 character + 3. Skip s2 character + + The backtrace is carried out in reverse string order. + + This function does not support transposition. + + :param s1, s2: The strings to be aligned + :type s1: str + :type s2: str + :type substitution_cost: int + :rtype: List[Tuple(int, int)] + """ + # set up a 2-D array + len1 = len(s1) + len2 = len(s2) + lev = _edit_dist_init(len1 + 1, len2 + 1) + + # iterate over the array + for i in range(len1): + for j in range(len2): + _edit_dist_step( + lev, + i + 1, + j + 1, + s1, + s2, + 0, + 0, + substitution_cost=substitution_cost, + transpositions=False, + ) + + # backtrace to find alignment + alignment = _edit_dist_backtrace(lev) + return alignment + + +def binary_distance(label1, label2): + """Simple equality test. + + 0.0 if the labels are identical, 1.0 if they are different. + + >>> from nltk.metrics import binary_distance + >>> binary_distance(1,1) + 0.0 + + >>> binary_distance(1,3) + 1.0 + """ + + return 0.0 if label1 == label2 else 1.0 + + +def jaccard_distance(label1, label2): + """Distance metric comparing set-similarity.""" + return (len(label1.union(label2)) - len(label1.intersection(label2))) / len( + label1.union(label2) + ) + + +def masi_distance(label1, label2): + """Distance metric that takes into account partial agreement when multiple + labels are assigned. + + >>> from nltk.metrics import masi_distance + >>> masi_distance(set([1, 2]), set([1, 2, 3, 4])) + 0.665 + + Passonneau 2006, Measuring Agreement on Set-Valued Items (MASI) + for Semantic and Pragmatic Annotation. + """ + + len_intersection = len(label1.intersection(label2)) + len_union = len(label1.union(label2)) + len_label1 = len(label1) + len_label2 = len(label2) + if len_label1 == len_label2 and len_label1 == len_intersection: + m = 1 + elif len_intersection == min(len_label1, len_label2): + m = 0.67 + elif len_intersection > 0: + m = 0.33 + else: + m = 0 + + return 1 - len_intersection / len_union * m + + +def interval_distance(label1, label2): + """Krippendorff's interval distance metric + + >>> from nltk.metrics import interval_distance + >>> interval_distance(1,10) + 81 + + Krippendorff 1980, Content Analysis: An Introduction to its Methodology + """ + + try: + return pow(label1 - label2, 2) + # return pow(list(label1)[0]-list(label2)[0],2) + except: + print("non-numeric labels not supported with interval distance") + + +def presence(label): + """Higher-order function to test presence of a given label""" + + return lambda x, y: 1.0 * ((label in x) == (label in y)) + + +def fractional_presence(label): + return ( + lambda x, y: abs((1.0 / len(x)) - (1.0 / len(y))) * (label in x and label in y) + or 0.0 * (label not in x and label not in y) + or abs(1.0 / len(x)) * (label in x and label not in y) + or (1.0 / len(y)) * (label not in x and label in y) + ) + + +def custom_distance(file): + data = {} + with open(file) as infile: + for l in infile: + labelA, labelB, dist = l.strip().split("\t") + labelA = frozenset([labelA]) + labelB = frozenset([labelB]) + data[frozenset([labelA, labelB])] = float(dist) + return lambda x, y: data[frozenset([x, y])] + + +def jaro_similarity(s1, s2): + """ + Computes the Jaro similarity between 2 sequences from: + + Matthew A. Jaro (1989). Advances in record linkage methodology + as applied to the 1985 census of Tampa Florida. Journal of the + American Statistical Association. 84 (406): 414-20. + + The Jaro distance between is the min no. of single-character transpositions + required to change one word into another. The Jaro similarity formula from + https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance : + + ``jaro_sim = 0 if m = 0 else 1/3 * (m/|s_1| + m/s_2 + (m-t)/m)`` + + where + - `|s_i|` is the length of string `s_i` + - `m` is the no. of matching characters + - `t` is the half no. of possible transpositions. + """ + # First, store the length of the strings + # because they will be re-used several times. + len_s1, len_s2 = len(s1), len(s2) + + # The upper bound of the distance for being a matched character. + match_bound = max(len_s1, len_s2) // 2 - 1 + + # Initialize the counts for matches and transpositions. + matches = 0 # no.of matched characters in s1 and s2 + transpositions = 0 # no. of transpositions between s1 and s2 + flagged_1 = [] # positions in s1 which are matches to some character in s2 + flagged_2 = [] # positions in s2 which are matches to some character in s1 + + # Iterate through sequences, check for matches and compute transpositions. + for i in range(len_s1): # Iterate through each character. + upperbound = min(i + match_bound, len_s2 - 1) + lowerbound = max(0, i - match_bound) + for j in range(lowerbound, upperbound + 1): + if s1[i] == s2[j] and j not in flagged_2: + matches += 1 + flagged_1.append(i) + flagged_2.append(j) + break + flagged_2.sort() + for i, j in zip(flagged_1, flagged_2): + if s1[i] != s2[j]: + transpositions += 1 + + if matches == 0: + return 0 + else: + return ( + 1 + / 3 + * ( + matches / len_s1 + + matches / len_s2 + + (matches - transpositions // 2) / matches + ) + ) + + +def jaro_winkler_similarity(s1, s2, p=0.1, max_l=4): + """ + The Jaro Winkler distance is an extension of the Jaro similarity in: + + William E. Winkler. 1990. String Comparator Metrics and Enhanced + Decision Rules in the Fellegi-Sunter Model of Record Linkage. + Proceedings of the Section on Survey Research Methods. + American Statistical Association: 354-359. + + such that: + + jaro_winkler_sim = jaro_sim + ( l * p * (1 - jaro_sim) ) + + where, + + - jaro_sim is the output from the Jaro Similarity, + see jaro_similarity() + - l is the length of common prefix at the start of the string + - this implementation provides an upperbound for the l value + to keep the prefixes.A common value of this upperbound is 4. + - p is the constant scaling factor to overweigh common prefixes. + The Jaro-Winkler similarity will fall within the [0, 1] bound, + given that max(p)<=0.25 , default is p=0.1 in Winkler (1990) + + + Test using outputs from https://www.census.gov/srd/papers/pdf/rr93-8.pdf + from "Table 5 Comparison of String Comparators Rescaled between 0 and 1" + + >>> winkler_examples = [("billy", "billy"), ("billy", "bill"), ("billy", "blily"), + ... ("massie", "massey"), ("yvette", "yevett"), ("billy", "bolly"), ("dwayne", "duane"), + ... ("dixon", "dickson"), ("billy", "susan")] + + >>> winkler_scores = [1.000, 0.967, 0.947, 0.944, 0.911, 0.893, 0.858, 0.853, 0.000] + >>> jaro_scores = [1.000, 0.933, 0.933, 0.889, 0.889, 0.867, 0.822, 0.790, 0.000] + + One way to match the values on the Winkler's paper is to provide a different + p scaling factor for different pairs of strings, e.g. + + >>> p_factors = [0.1, 0.125, 0.20, 0.125, 0.20, 0.20, 0.20, 0.15, 0.1] + + >>> for (s1, s2), jscore, wscore, p in zip(winkler_examples, jaro_scores, winkler_scores, p_factors): + ... assert round(jaro_similarity(s1, s2), 3) == jscore + ... assert round(jaro_winkler_similarity(s1, s2, p=p), 3) == wscore + + + Test using outputs from https://www.census.gov/srd/papers/pdf/rr94-5.pdf from + "Table 2.1. Comparison of String Comparators Using Last Names, First Names, and Street Names" + + >>> winkler_examples = [('SHACKLEFORD', 'SHACKELFORD'), ('DUNNINGHAM', 'CUNNIGHAM'), + ... ('NICHLESON', 'NICHULSON'), ('JONES', 'JOHNSON'), ('MASSEY', 'MASSIE'), + ... ('ABROMS', 'ABRAMS'), ('HARDIN', 'MARTINEZ'), ('ITMAN', 'SMITH'), + ... ('JERALDINE', 'GERALDINE'), ('MARHTA', 'MARTHA'), ('MICHELLE', 'MICHAEL'), + ... ('JULIES', 'JULIUS'), ('TANYA', 'TONYA'), ('DWAYNE', 'DUANE'), ('SEAN', 'SUSAN'), + ... ('JON', 'JOHN'), ('JON', 'JAN'), ('BROOKHAVEN', 'BRROKHAVEN'), + ... ('BROOK HALLOW', 'BROOK HLLW'), ('DECATUR', 'DECATIR'), ('FITZRUREITER', 'FITZENREITER'), + ... ('HIGBEE', 'HIGHEE'), ('HIGBEE', 'HIGVEE'), ('LACURA', 'LOCURA'), ('IOWA', 'IONA'), ('1ST', 'IST')] + + >>> jaro_scores = [0.970, 0.896, 0.926, 0.790, 0.889, 0.889, 0.722, 0.467, 0.926, + ... 0.944, 0.869, 0.889, 0.867, 0.822, 0.783, 0.917, 0.000, 0.933, 0.944, 0.905, + ... 0.856, 0.889, 0.889, 0.889, 0.833, 0.000] + + >>> winkler_scores = [0.982, 0.896, 0.956, 0.832, 0.944, 0.922, 0.722, 0.467, 0.926, + ... 0.961, 0.921, 0.933, 0.880, 0.858, 0.805, 0.933, 0.000, 0.947, 0.967, 0.943, + ... 0.913, 0.922, 0.922, 0.900, 0.867, 0.000] + + One way to match the values on the Winkler's paper is to provide a different + p scaling factor for different pairs of strings, e.g. + + >>> p_factors = [0.1, 0.1, 0.1, 0.1, 0.125, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.20, + ... 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] + + + >>> for (s1, s2), jscore, wscore, p in zip(winkler_examples, jaro_scores, winkler_scores, p_factors): + ... if (s1, s2) in [('JON', 'JAN'), ('1ST', 'IST')]: + ... continue # Skip bad examples from the paper. + ... assert round(jaro_similarity(s1, s2), 3) == jscore + ... assert round(jaro_winkler_similarity(s1, s2, p=p), 3) == wscore + + + + This test-case proves that the output of Jaro-Winkler similarity depends on + the product l * p and not on the product max_l * p. Here the product max_l * p > 1 + however the product l * p <= 1 + + >>> round(jaro_winkler_similarity('TANYA', 'TONYA', p=0.1, max_l=100), 3) + 0.88 + """ + # To ensure that the output of the Jaro-Winkler's similarity + # falls between [0,1], the product of l * p needs to be + # also fall between [0,1]. + if not 0 <= max_l * p <= 1: + warnings.warn( + str( + "The product `max_l * p` might not fall between [0,1]." + "Jaro-Winkler similarity might not be between 0 and 1." + ) + ) + + # Compute the Jaro similarity + jaro_sim = jaro_similarity(s1, s2) + + # Initialize the upper bound for the no. of prefixes. + # if user did not pre-define the upperbound, + # use shorter length between s1 and s2 + + # Compute the prefix matches. + l = 0 + # zip() will automatically loop until the end of shorter string. + for s1_i, s2_i in zip(s1, s2): + if s1_i == s2_i: + l += 1 + else: + break + if l == max_l: + break + # Return the similarity value as described in docstring. + return jaro_sim + (l * p * (1 - jaro_sim)) + + +def demo(): + string_distance_examples = [ + ("rain", "shine"), + ("abcdef", "acbdef"), + ("language", "lnaguaeg"), + ("language", "lnaugage"), + ("language", "lngauage"), + ] + for s1, s2 in string_distance_examples: + print(f"Edit distance btwn '{s1}' and '{s2}':", edit_distance(s1, s2)) + print( + f"Edit dist with transpositions btwn '{s1}' and '{s2}':", + edit_distance(s1, s2, transpositions=True), + ) + print(f"Jaro similarity btwn '{s1}' and '{s2}':", jaro_similarity(s1, s2)) + print( + f"Jaro-Winkler similarity btwn '{s1}' and '{s2}':", + jaro_winkler_similarity(s1, s2), + ) + print( + f"Jaro-Winkler distance btwn '{s1}' and '{s2}':", + 1 - jaro_winkler_similarity(s1, s2), + ) + s1 = {1, 2, 3, 4} + s2 = {3, 4, 5} + print("s1:", s1) + print("s2:", s2) + print("Binary distance:", binary_distance(s1, s2)) + print("Jaccard distance:", jaccard_distance(s1, s2)) + print("MASI distance:", masi_distance(s1, s2)) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/paice.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/paice.py new file mode 100644 index 0000000..aa54cfb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/paice.py @@ -0,0 +1,389 @@ +# Natural Language Toolkit: Agreement Metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Lauri Hallila +# URL: +# For license information, see LICENSE.TXT +# + +"""Counts Paice's performance statistics for evaluating stemming algorithms. + +What is required: + - A dictionary of words grouped by their real lemmas + - A dictionary of words grouped by stems from a stemming algorithm + +When these are given, Understemming Index (UI), Overstemming Index (OI), +Stemming Weight (SW) and Error-rate relative to truncation (ERRT) are counted. + +References: +Chris D. Paice (1994). An evaluation method for stemming algorithms. +In Proceedings of SIGIR, 42--50. +""" + +from math import sqrt + + +def get_words_from_dictionary(lemmas): + """ + Get original set of words used for analysis. + + :param lemmas: A dictionary where keys are lemmas and values are sets + or lists of words corresponding to that lemma. + :type lemmas: dict(str): list(str) + :return: Set of words that exist as values in the dictionary + :rtype: set(str) + """ + words = set() + for lemma in lemmas: + words.update(set(lemmas[lemma])) + return words + + +def _truncate(words, cutlength): + """Group words by stems defined by truncating them at given length. + + :param words: Set of words used for analysis + :param cutlength: Words are stemmed by cutting at this length. + :type words: set(str) or list(str) + :type cutlength: int + :return: Dictionary where keys are stems and values are sets of words + corresponding to that stem. + :rtype: dict(str): set(str) + """ + stems = {} + for word in words: + stem = word[:cutlength] + try: + stems[stem].update([word]) + except KeyError: + stems[stem] = {word} + return stems + + +# Reference: https://en.wikipedia.org/wiki/Line-line_intersection +def _count_intersection(l1, l2): + """Count intersection between two line segments defined by coordinate pairs. + + :param l1: Tuple of two coordinate pairs defining the first line segment + :param l2: Tuple of two coordinate pairs defining the second line segment + :type l1: tuple(float, float) + :type l2: tuple(float, float) + :return: Coordinates of the intersection + :rtype: tuple(float, float) + """ + x1, y1 = l1[0] + x2, y2 = l1[1] + x3, y3 = l2[0] + x4, y4 = l2[1] + + denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) + + if denominator == 0.0: # lines are parallel + if x1 == x2 == x3 == x4 == 0.0: + # When lines are parallel, they must be on the y-axis. + # We can ignore x-axis because we stop counting the + # truncation line when we get there. + # There are no other options as UI (x-axis) grows and + # OI (y-axis) diminishes when we go along the truncation line. + return (0.0, y4) + + x = ( + (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4) + ) / denominator + y = ( + (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4) + ) / denominator + return (x, y) + + +def _get_derivative(coordinates): + """Get derivative of the line from (0,0) to given coordinates. + + :param coordinates: A coordinate pair + :type coordinates: tuple(float, float) + :return: Derivative; inf if x is zero + :rtype: float + """ + try: + return coordinates[1] / coordinates[0] + except ZeroDivisionError: + return float("inf") + + +def _calculate_cut(lemmawords, stems): + """Count understemmed and overstemmed pairs for (lemma, stem) pair with common words. + + :param lemmawords: Set or list of words corresponding to certain lemma. + :param stems: A dictionary where keys are stems and values are sets + or lists of words corresponding to that stem. + :type lemmawords: set(str) or list(str) + :type stems: dict(str): set(str) + :return: Amount of understemmed and overstemmed pairs contributed by words + existing in both lemmawords and stems. + :rtype: tuple(float, float) + """ + umt, wmt = 0.0, 0.0 + for stem in stems: + cut = set(lemmawords) & set(stems[stem]) + if cut: + cutcount = len(cut) + stemcount = len(stems[stem]) + # Unachieved merge total + umt += cutcount * (len(lemmawords) - cutcount) + # Wrongly merged total + wmt += cutcount * (stemcount - cutcount) + return (umt, wmt) + + +def _calculate(lemmas, stems): + """Calculate actual and maximum possible amounts of understemmed and overstemmed word pairs. + + :param lemmas: A dictionary where keys are lemmas and values are sets + or lists of words corresponding to that lemma. + :param stems: A dictionary where keys are stems and values are sets + or lists of words corresponding to that stem. + :type lemmas: dict(str): list(str) + :type stems: dict(str): set(str) + :return: Global unachieved merge total (gumt), + global desired merge total (gdmt), + global wrongly merged total (gwmt) and + global desired non-merge total (gdnt). + :rtype: tuple(float, float, float, float) + """ + + n = sum(len(lemmas[word]) for word in lemmas) + + gdmt, gdnt, gumt, gwmt = (0.0, 0.0, 0.0, 0.0) + + for lemma in lemmas: + lemmacount = len(lemmas[lemma]) + + # Desired merge total + gdmt += lemmacount * (lemmacount - 1) + + # Desired non-merge total + gdnt += lemmacount * (n - lemmacount) + + # For each (lemma, stem) pair with common words, count how many + # pairs are understemmed and overstemmed. + umt, wmt = _calculate_cut(lemmas[lemma], stems) + + # Add to total undesired and wrongly-merged totals + gumt += umt + gwmt += wmt + + # Each object is counted twice, so divide by two + return (gumt / 2, gdmt / 2, gwmt / 2, gdnt / 2) + + +def _indexes(gumt, gdmt, gwmt, gdnt): + """Count Understemming Index (UI), Overstemming Index (OI) and Stemming Weight (SW). + + :param gumt, gdmt, gwmt, gdnt: Global unachieved merge total (gumt), + global desired merge total (gdmt), + global wrongly merged total (gwmt) and + global desired non-merge total (gdnt). + :type gumt, gdmt, gwmt, gdnt: float + :return: Understemming Index (UI), + Overstemming Index (OI) and + Stemming Weight (SW). + :rtype: tuple(float, float, float) + """ + # Calculate Understemming Index (UI), + # Overstemming Index (OI) and Stemming Weight (SW) + try: + ui = gumt / gdmt + except ZeroDivisionError: + # If GDMT (max merge total) is 0, define UI as 0 + ui = 0.0 + try: + oi = gwmt / gdnt + except ZeroDivisionError: + # IF GDNT (max non-merge total) is 0, define OI as 0 + oi = 0.0 + try: + sw = oi / ui + except ZeroDivisionError: + if oi == 0.0: + # OI and UI are 0, define SW as 'not a number' + sw = float("nan") + else: + # UI is 0, define SW as infinity + sw = float("inf") + return (ui, oi, sw) + + +class Paice: + """Class for storing lemmas, stems and evaluation metrics.""" + + def __init__(self, lemmas, stems): + """ + :param lemmas: A dictionary where keys are lemmas and values are sets + or lists of words corresponding to that lemma. + :param stems: A dictionary where keys are stems and values are sets + or lists of words corresponding to that stem. + :type lemmas: dict(str): list(str) + :type stems: dict(str): set(str) + """ + self.lemmas = lemmas + self.stems = stems + self.coords = [] + self.gumt, self.gdmt, self.gwmt, self.gdnt = (None, None, None, None) + self.ui, self.oi, self.sw = (None, None, None) + self.errt = None + self.update() + + def __str__(self): + text = ["Global Unachieved Merge Total (GUMT): %s\n" % self.gumt] + text.append("Global Desired Merge Total (GDMT): %s\n" % self.gdmt) + text.append("Global Wrongly-Merged Total (GWMT): %s\n" % self.gwmt) + text.append("Global Desired Non-merge Total (GDNT): %s\n" % self.gdnt) + text.append("Understemming Index (GUMT / GDMT): %s\n" % self.ui) + text.append("Overstemming Index (GWMT / GDNT): %s\n" % self.oi) + text.append("Stemming Weight (OI / UI): %s\n" % self.sw) + text.append("Error-Rate Relative to Truncation (ERRT): %s\r\n" % self.errt) + coordinates = " ".join(["(%s, %s)" % item for item in self.coords]) + text.append("Truncation line: %s" % coordinates) + return "".join(text) + + def _get_truncation_indexes(self, words, cutlength): + """Count (UI, OI) when stemming is done by truncating words at \'cutlength\'. + + :param words: Words used for the analysis + :param cutlength: Words are stemmed by cutting them at this length + :type words: set(str) or list(str) + :type cutlength: int + :return: Understemming and overstemming indexes + :rtype: tuple(int, int) + """ + + truncated = _truncate(words, cutlength) + gumt, gdmt, gwmt, gdnt = _calculate(self.lemmas, truncated) + ui, oi = _indexes(gumt, gdmt, gwmt, gdnt)[:2] + return (ui, oi) + + def _get_truncation_coordinates(self, cutlength=0): + """Count (UI, OI) pairs for truncation points until we find the segment where (ui, oi) crosses the truncation line. + + :param cutlength: Optional parameter to start counting from (ui, oi) + coordinates gotten by stemming at this length. Useful for speeding up + the calculations when you know the approximate location of the + intersection. + :type cutlength: int + :return: List of coordinate pairs that define the truncation line + :rtype: list(tuple(float, float)) + """ + words = get_words_from_dictionary(self.lemmas) + maxlength = max(len(word) for word in words) + + # Truncate words from different points until (0, 0) - (ui, oi) segment crosses the truncation line + coords = [] + while cutlength <= maxlength: + # Get (UI, OI) pair of current truncation point + pair = self._get_truncation_indexes(words, cutlength) + + # Store only new coordinates so we'll have an actual + # line segment when counting the intersection point + if pair not in coords: + coords.append(pair) + if pair == (0.0, 0.0): + # Stop counting if truncation line goes through origo; + # length from origo to truncation line is 0 + return coords + if len(coords) >= 2 and pair[0] > 0.0: + derivative1 = _get_derivative(coords[-2]) + derivative2 = _get_derivative(coords[-1]) + # Derivative of the truncation line is a decreasing value; + # when it passes Stemming Weight, we've found the segment + # of truncation line intersecting with (0, 0) - (ui, oi) segment + if derivative1 >= self.sw >= derivative2: + return coords + cutlength += 1 + return coords + + def _errt(self): + """Count Error-Rate Relative to Truncation (ERRT). + + :return: ERRT, length of the line from origo to (UI, OI) divided by + the length of the line from origo to the point defined by the same + line when extended until the truncation line. + :rtype: float + """ + # Count (UI, OI) pairs for truncation points until we find the segment where (ui, oi) crosses the truncation line + self.coords = self._get_truncation_coordinates() + if (0.0, 0.0) in self.coords: + # Truncation line goes through origo, so ERRT cannot be counted + if (self.ui, self.oi) != (0.0, 0.0): + return float("inf") + else: + return float("nan") + if (self.ui, self.oi) == (0.0, 0.0): + # (ui, oi) is origo; define errt as 0.0 + return 0.0 + # Count the intersection point + # Note that (self.ui, self.oi) cannot be (0.0, 0.0) and self.coords has different coordinates + # so we have actual line segments instead of a line segment and a point + intersection = _count_intersection( + ((0, 0), (self.ui, self.oi)), self.coords[-2:] + ) + # Count OP (length of the line from origo to (ui, oi)) + op = sqrt(self.ui**2 + self.oi**2) + # Count OT (length of the line from origo to truncation line that goes through (ui, oi)) + ot = sqrt(intersection[0] ** 2 + intersection[1] ** 2) + # OP / OT tells how well the stemming algorithm works compared to just truncating words + return op / ot + + def update(self): + """Update statistics after lemmas and stems have been set.""" + self.gumt, self.gdmt, self.gwmt, self.gdnt = _calculate(self.lemmas, self.stems) + self.ui, self.oi, self.sw = _indexes(self.gumt, self.gdmt, self.gwmt, self.gdnt) + self.errt = self._errt() + + +def demo(): + """Demonstration of the module.""" + # Some words with their real lemmas + lemmas = { + "kneel": ["kneel", "knelt"], + "range": ["range", "ranged"], + "ring": ["ring", "rang", "rung"], + } + # Same words with stems from a stemming algorithm + stems = { + "kneel": ["kneel"], + "knelt": ["knelt"], + "rang": ["rang", "range", "ranged"], + "ring": ["ring"], + "rung": ["rung"], + } + print("Words grouped by their lemmas:") + for lemma in sorted(lemmas): + print("{} => {}".format(lemma, " ".join(lemmas[lemma]))) + print() + print("Same words grouped by a stemming algorithm:") + for stem in sorted(stems): + print("{} => {}".format(stem, " ".join(stems[stem]))) + print() + p = Paice(lemmas, stems) + print(p) + print() + # Let's "change" results from a stemming algorithm + stems = { + "kneel": ["kneel"], + "knelt": ["knelt"], + "rang": ["rang"], + "range": ["range", "ranged"], + "ring": ["ring"], + "rung": ["rung"], + } + print("Counting stats after changing stemming results:") + for stem in sorted(stems): + print("{} => {}".format(stem, " ".join(stems[stem]))) + print() + p.stems = stems + p.update() + print(p) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/scores.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/scores.py new file mode 100644 index 0000000..8072a45 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/scores.py @@ -0,0 +1,228 @@ +# Natural Language Toolkit: Evaluation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +import operator +from functools import reduce +from math import fabs +from random import shuffle + +try: + from scipy.stats.stats import betai +except ImportError: + betai = None + +from nltk.util import LazyConcatenation, LazyMap + + +def accuracy(reference, test): + """ + Given a list of reference values and a corresponding list of test + values, return the fraction of corresponding values that are + equal. In particular, return the fraction of indices + ``0= actual_stat: + c += 1 + + if verbose and i % 10 == 0: + print("pseudo-statistic: %f" % pseudo_stat) + print("significance: %f" % ((c + 1) / (i + 1))) + print("-" * 60) + + significance = (c + 1) / (shuffles + 1) + + if verbose: + print("significance: %f" % significance) + if betai: + for phi in [0.01, 0.05, 0.10, 0.15, 0.25, 0.50]: + print(f"prob(phi<={phi:f}): {betai(c, shuffles, phi):f}") + + return (significance, c, shuffles) + + +def demo(): + print("-" * 75) + reference = "DET NN VB DET JJ NN NN IN DET NN".split() + test = "DET VB VB DET NN NN NN IN DET NN".split() + print("Reference =", reference) + print("Test =", test) + print("Accuracy:", accuracy(reference, test)) + + print("-" * 75) + reference_set = set(reference) + test_set = set(test) + print("Reference =", reference_set) + print("Test = ", test_set) + print("Precision:", precision(reference_set, test_set)) + print(" Recall:", recall(reference_set, test_set)) + print("F-Measure:", f_measure(reference_set, test_set)) + print("-" * 75) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/segmentation.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/segmentation.py new file mode 100644 index 0000000..4eb2986 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/segmentation.py @@ -0,0 +1,222 @@ +# Natural Language Toolkit: Text Segmentation Metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# David Doukhan +# URL: +# For license information, see LICENSE.TXT + + +""" +Text Segmentation Metrics + +1. Windowdiff + +Pevzner, L., and Hearst, M., A Critique and Improvement of + an Evaluation Metric for Text Segmentation, + Computational Linguistics 28, 19-36 + + +2. Generalized Hamming Distance + +Bookstein A., Kulyukin V.A., Raita T. +Generalized Hamming Distance +Information Retrieval 5, 2002, pp 353-375 + +Baseline implementation in C++ +http://digital.cs.usu.edu/~vkulyukin/vkweb/software/ghd/ghd.html + +Study describing benefits of Generalized Hamming Distance Versus +WindowDiff for evaluating text segmentation tasks +Begsten, Y. Quel indice pour mesurer l'efficacite en segmentation de textes ? +TALN 2009 + + +3. Pk text segmentation metric + +Beeferman D., Berger A., Lafferty J. (1999) +Statistical Models for Text Segmentation +Machine Learning, 34, 177-210 +""" + +try: + import numpy as np +except ImportError: + pass + + +def windowdiff(seg1, seg2, k, boundary="1", weighted=False): + """ + Compute the windowdiff score for a pair of segmentations. A + segmentation is any sequence over a vocabulary of two items + (e.g. "0", "1"), where the specified boundary value is used to + mark the edge of a segmentation. + + >>> s1 = "000100000010" + >>> s2 = "000010000100" + >>> s3 = "100000010000" + >>> '%.2f' % windowdiff(s1, s1, 3) + '0.00' + >>> '%.2f' % windowdiff(s1, s2, 3) + '0.30' + >>> '%.2f' % windowdiff(s2, s3, 3) + '0.80' + + :param seg1: a segmentation + :type seg1: str or list + :param seg2: a segmentation + :type seg2: str or list + :param k: window width + :type k: int + :param boundary: boundary value + :type boundary: str or int or bool + :param weighted: use the weighted variant of windowdiff + :type weighted: boolean + :rtype: float + """ + + if len(seg1) != len(seg2): + raise ValueError("Segmentations have unequal length") + if k > len(seg1): + raise ValueError( + "Window width k should be smaller or equal than segmentation lengths" + ) + wd = 0 + for i in range(len(seg1) - k + 1): + ndiff = abs(seg1[i : i + k].count(boundary) - seg2[i : i + k].count(boundary)) + if weighted: + wd += ndiff + else: + wd += min(1, ndiff) + return wd / (len(seg1) - k + 1.0) + + +# Generalized Hamming Distance + + +def _init_mat(nrows, ncols, ins_cost, del_cost): + mat = np.empty((nrows, ncols)) + mat[0, :] = ins_cost * np.arange(ncols) + mat[:, 0] = del_cost * np.arange(nrows) + return mat + + +def _ghd_aux(mat, rowv, colv, ins_cost, del_cost, shift_cost_coeff): + for i, rowi in enumerate(rowv): + for j, colj in enumerate(colv): + shift_cost = shift_cost_coeff * abs(rowi - colj) + mat[i, j] + if rowi == colj: + # boundaries are at the same location, no transformation required + tcost = mat[i, j] + elif rowi > colj: + # boundary match through a deletion + tcost = del_cost + mat[i, j + 1] + else: + # boundary match through an insertion + tcost = ins_cost + mat[i + 1, j] + mat[i + 1, j + 1] = min(tcost, shift_cost) + + +def ghd(ref, hyp, ins_cost=2.0, del_cost=2.0, shift_cost_coeff=1.0, boundary="1"): + """ + Compute the Generalized Hamming Distance for a reference and a hypothetical + segmentation, corresponding to the cost related to the transformation + of the hypothetical segmentation into the reference segmentation + through boundary insertion, deletion and shift operations. + + A segmentation is any sequence over a vocabulary of two items + (e.g. "0", "1"), where the specified boundary value is used to + mark the edge of a segmentation. + + Recommended parameter values are a shift_cost_coeff of 2. + Associated with a ins_cost, and del_cost equal to the mean segment + length in the reference segmentation. + + >>> # Same examples as Kulyukin C++ implementation + >>> ghd('1100100000', '1100010000', 1.0, 1.0, 0.5) + 0.5 + >>> ghd('1100100000', '1100000001', 1.0, 1.0, 0.5) + 2.0 + >>> ghd('011', '110', 1.0, 1.0, 0.5) + 1.0 + >>> ghd('1', '0', 1.0, 1.0, 0.5) + 1.0 + >>> ghd('111', '000', 1.0, 1.0, 0.5) + 3.0 + >>> ghd('000', '111', 1.0, 2.0, 0.5) + 6.0 + + :param ref: the reference segmentation + :type ref: str or list + :param hyp: the hypothetical segmentation + :type hyp: str or list + :param ins_cost: insertion cost + :type ins_cost: float + :param del_cost: deletion cost + :type del_cost: float + :param shift_cost_coeff: constant used to compute the cost of a shift. + ``shift cost = shift_cost_coeff * |i - j|`` where ``i`` and ``j`` + are the positions indicating the shift + :type shift_cost_coeff: float + :param boundary: boundary value + :type boundary: str or int or bool + :rtype: float + """ + + ref_idx = [i for (i, val) in enumerate(ref) if val == boundary] + hyp_idx = [i for (i, val) in enumerate(hyp) if val == boundary] + + nref_bound = len(ref_idx) + nhyp_bound = len(hyp_idx) + + if nref_bound == 0 and nhyp_bound == 0: + return 0.0 + elif nref_bound > 0 and nhyp_bound == 0: + return nref_bound * ins_cost + elif nref_bound == 0 and nhyp_bound > 0: + return nhyp_bound * del_cost + + mat = _init_mat(nhyp_bound + 1, nref_bound + 1, ins_cost, del_cost) + _ghd_aux(mat, hyp_idx, ref_idx, ins_cost, del_cost, shift_cost_coeff) + return mat[-1, -1] + + +# Beeferman's Pk text segmentation evaluation metric + + +def pk(ref, hyp, k=None, boundary="1"): + """ + Compute the Pk metric for a pair of segmentations A segmentation + is any sequence over a vocabulary of two items (e.g. "0", "1"), + where the specified boundary value is used to mark the edge of a + segmentation. + + >>> '%.2f' % pk('0100'*100, '1'*400, 2) + '0.50' + >>> '%.2f' % pk('0100'*100, '0'*400, 2) + '0.50' + >>> '%.2f' % pk('0100'*100, '0100'*100, 2) + '0.00' + + :param ref: the reference segmentation + :type ref: str or list + :param hyp: the segmentation to evaluate + :type hyp: str or list + :param k: window size, if None, set to half of the average reference segment length + :type boundary: str or int or bool + :param boundary: boundary value + :type boundary: str or int or bool + :rtype: float + """ + + if k is None: + k = int(round(len(ref) / (ref.count(boundary) * 2.0))) + + err = 0 + for i in range(len(ref) - k + 1): + r = ref[i : i + k].count(boundary) > 0 + h = hyp[i : i + k].count(boundary) > 0 + if r != h: + err += 1 + return err / (len(ref) - k + 1.0) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/spearman.py b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/spearman.py new file mode 100644 index 0000000..aa56c1c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/metrics/spearman.py @@ -0,0 +1,68 @@ +# Natural Language Toolkit: Spearman Rank Correlation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Joel Nothman +# URL: +# For license information, see LICENSE.TXT + +""" +Tools for comparing ranked lists. +""" + + +def _rank_dists(ranks1, ranks2): + """Finds the difference between the values in ranks1 and ranks2 for keys + present in both dicts. If the arguments are not dicts, they are converted + from (key, rank) sequences. + """ + ranks1 = dict(ranks1) + ranks2 = dict(ranks2) + for k in ranks1: + try: + yield k, ranks1[k] - ranks2[k] + except KeyError: + pass + + +def spearman_correlation(ranks1, ranks2): + """Returns the Spearman correlation coefficient for two rankings, which + should be dicts or sequences of (key, rank). The coefficient ranges from + -1.0 (ranks are opposite) to 1.0 (ranks are identical), and is only + calculated for keys in both rankings (for meaningful results, remove keys + present in only one list before ranking).""" + n = 0 + res = 0 + for k, d in _rank_dists(ranks1, ranks2): + res += d * d + n += 1 + try: + return 1 - (6 * res / (n * (n * n - 1))) + except ZeroDivisionError: + # Result is undefined if only one item is ranked + return 0.0 + + +def ranks_from_sequence(seq): + """Given a sequence, yields each element with an increasing rank, suitable + for use as an argument to ``spearman_correlation``. + """ + return ((k, i) for i, k in enumerate(seq)) + + +def ranks_from_scores(scores, rank_gap=1e-15): + """Given a sequence of (key, score) tuples, yields each key with an + increasing rank, tying with previous key's rank if the difference between + their scores is less than rank_gap. Suitable for use as an argument to + ``spearman_correlation``. + """ + prev_score = None + rank = 0 + for i, (key, score) in enumerate(scores): + try: + if abs(score - prev_score) > rank_gap: + rank = i + except TypeError: + pass + + yield key, rank + prev_score = score diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__init__.py new file mode 100644 index 0000000..0b356bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__init__.py @@ -0,0 +1,11 @@ +# Natural Language Toolkit: Miscellaneous modules +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from nltk.misc.babelfish import babelize_shell +from nltk.misc.chomsky import generate_chomsky +from nltk.misc.minimalset import MinimalSet +from nltk.misc.wordfinder import word_finder diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..44ca410 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/babelfish.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/babelfish.cpython-310.pyc new file mode 100644 index 0000000..33a0438 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/babelfish.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/chomsky.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/chomsky.cpython-310.pyc new file mode 100644 index 0000000..1c1a96c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/chomsky.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/minimalset.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/minimalset.cpython-310.pyc new file mode 100644 index 0000000..1f15d5a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/minimalset.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/sort.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/sort.cpython-310.pyc new file mode 100644 index 0000000..382f9b3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/sort.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/wordfinder.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/wordfinder.cpython-310.pyc new file mode 100644 index 0000000..4f54936 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/__pycache__/wordfinder.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/babelfish.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/babelfish.py new file mode 100644 index 0000000..a43fd4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/babelfish.py @@ -0,0 +1,10 @@ +""" +This module previously provided an interface to Babelfish online +translation service; this service is no longer available; this +module is kept in NLTK source code in order to provide better error +messages for people following the NLTK Book 2.0. +""" + + +def babelize_shell(): + print("Babelfish online translation service is no longer available.") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/chomsky.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/chomsky.py new file mode 100644 index 0000000..297d20c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/chomsky.py @@ -0,0 +1,134 @@ +# Chomsky random text generator, version 1.1, Raymond Hettinger, 2005/09/13 +# https://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440546 + +""" +CHOMSKY is an aid to writing linguistic papers in the style +of the great master. It is based on selected phrases taken +from actual books and articles written by Noam Chomsky. +Upon request, it assembles the phrases in the elegant +stylistic patterns that Chomsky is noted for. +To generate n sentences of linguistic wisdom, type + + (CHOMSKY n) -- for example + (CHOMSKY 5) generates half a screen of linguistic truth. +""" + +leadins = """To characterize a linguistic level L, + On the other hand, + This suggests that + It appears that + Furthermore, + We will bring evidence in favor of the following thesis: + To provide a constituent structure for T(Z,K), + From C1, it follows that + For any transformation which is sufficiently diversified in \ +application to be of any interest, + Analogously, + Clearly, + Note that + Of course, + Suppose, for instance, that + Thus + With this clarification, + Conversely, + We have already seen that + By combining adjunctions and certain deformations, + I suggested that these results would follow from the assumption that + If the position of the trace in (99c) were only relatively \ +inaccessible to movement, + However, this assumption is not correct, since + Comparing these examples with their parasitic gap counterparts in \ +(96) and (97), we see that + In the discussion of resumptive pronouns following (81), + So far, + Nevertheless, + For one thing, + Summarizing, then, we assume that + A consequence of the approach just outlined is that + Presumably, + On our assumptions, + It may be, then, that + It must be emphasized, once again, that + Let us continue to suppose that + Notice, incidentally, that """ +# List of LEADINs to buy time. + +subjects = """ the notion of level of grammaticalness + a case of semigrammaticalness of a different sort + most of the methodological work in modern linguistics + a subset of English sentences interesting on quite independent grounds + the natural general principle that will subsume this case + an important property of these three types of EC + any associated supporting element + the appearance of parasitic gaps in domains relatively inaccessible \ +to ordinary extraction + the speaker-hearer's linguistic intuition + the descriptive power of the base component + the earlier discussion of deviance + this analysis of a formative as a pair of sets of features + this selectionally introduced contextual feature + a descriptively adequate grammar + the fundamental error of regarding functional notions as categorial + relational information + the systematic use of complex symbols + the theory of syntactic features developed earlier""" +# List of SUBJECTs chosen for maximum professorial macho. + +verbs = """can be defined in such a way as to impose + delimits + suffices to account for + cannot be arbitrary in + is not subject to + does not readily tolerate + raises serious doubts about + is not quite equivalent to + does not affect the structure of + may remedy and, at the same time, eliminate + is not to be considered in determining + is to be regarded as + is unspecified with respect to + is, apparently, determined by + is necessary to impose an interpretation on + appears to correlate rather closely with + is rather different from""" +# List of VERBs chosen for autorecursive obfuscation. + +objects = """ problems of phonemic and morphological analysis. + a corpus of utterance tokens upon which conformity has been defined \ +by the paired utterance test. + the traditional practice of grammarians. + the levels of acceptability from fairly high (e.g. (99a)) to virtual \ +gibberish (e.g. (98d)). + a stipulation to place the constructions into these various categories. + a descriptive fact. + a parasitic gap construction. + the extended c-command discussed in connection with (34). + the ultimate standard that determines the accuracy of any proposed grammar. + the system of base rules exclusive of the lexicon. + irrelevant intervening contexts in selectional rules. + nondistinctness in the sense of distinctive feature theory. + a general convention regarding the forms of the grammar. + an abstract underlying order. + an important distinction in language use. + the requirement that branching is not tolerated within the dominance \ +scope of a complex symbol. + the strong generative capacity of the theory.""" +# List of OBJECTs selected for profound sententiousness. + +import random +import textwrap +from itertools import chain, islice + + +def generate_chomsky(times=5, line_length=72): + parts = [] + for part in (leadins, subjects, verbs, objects): + phraselist = list(map(str.strip, part.splitlines())) + random.shuffle(phraselist) + parts.append(phraselist) + output = chain.from_iterable(islice(zip(*parts), 0, times)) + print(textwrap.fill(" ".join(output), line_length)) + + +if __name__ == "__main__": + generate_chomsky() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/minimalset.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/minimalset.py new file mode 100644 index 0000000..e91640d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/minimalset.py @@ -0,0 +1,85 @@ +# Natural Language Toolkit: Minimal Sets +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from collections import defaultdict + + +class MinimalSet: + """ + Find contexts where more than one possible target value can + appear. E.g. if targets are word-initial letters, and contexts + are the remainders of words, then we would like to find cases like + "fat" vs "cat", and "training" vs "draining". If targets are + parts-of-speech and contexts are words, then we would like to find + cases like wind (noun) 'air in rapid motion', vs wind (verb) + 'coil, wrap'. + """ + + def __init__(self, parameters=None): + """ + Create a new minimal set. + + :param parameters: The (context, target, display) tuples for the item + :type parameters: list(tuple(str, str, str)) + """ + self._targets = set() # the contrastive information + self._contexts = set() # what we are controlling for + self._seen = defaultdict(set) # to record what we have seen + self._displays = {} # what we will display + + if parameters: + for context, target, display in parameters: + self.add(context, target, display) + + def add(self, context, target, display): + """ + Add a new item to the minimal set, having the specified + context, target, and display form. + + :param context: The context in which the item of interest appears + :type context: str + :param target: The item of interest + :type target: str + :param display: The information to be reported for each item + :type display: str + """ + # Store the set of targets that occurred in this context + self._seen[context].add(target) + + # Keep track of which contexts and targets we have seen + self._contexts.add(context) + self._targets.add(target) + + # For a given context and target, store the display form + self._displays[(context, target)] = display + + def contexts(self, minimum=2): + """ + Determine which contexts occurred with enough distinct targets. + + :param minimum: the minimum number of distinct target forms + :type minimum: int + :rtype: list + """ + return [c for c in self._contexts if len(self._seen[c]) >= minimum] + + def display(self, context, target, default=""): + if (context, target) in self._displays: + return self._displays[(context, target)] + else: + return default + + def display_all(self, context): + result = [] + for target in self._targets: + x = self.display(context, target) + if x: + result.append(x) + return result + + def targets(self): + return self._targets diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/sort.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/sort.py new file mode 100644 index 0000000..4e5ef9f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/sort.py @@ -0,0 +1,176 @@ +# Natural Language Toolkit: List Sorting +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +This module provides a variety of list sorting algorithms, to +illustrate the many different algorithms (recipes) for solving a +problem, and how to analyze algorithms experimentally. +""" +# These algorithms are taken from: +# Levitin (2004) The Design and Analysis of Algorithms + +################################################################## +# Selection Sort +################################################################## + + +def selection(a): + """ + Selection Sort: scan the list to find its smallest element, then + swap it with the first element. The remainder of the list is one + element smaller; apply the same method to this list, and so on. + """ + count = 0 + + for i in range(len(a) - 1): + min = i + + for j in range(i + 1, len(a)): + if a[j] < a[min]: + min = j + + count += 1 + + a[min], a[i] = a[i], a[min] + + return count + + +################################################################## +# Bubble Sort +################################################################## + + +def bubble(a): + """ + Bubble Sort: compare adjacent elements of the list left-to-right, + and swap them if they are out of order. After one pass through + the list swapping adjacent items, the largest item will be in + the rightmost position. The remainder is one element smaller; + apply the same method to this list, and so on. + """ + count = 0 + for i in range(len(a) - 1): + for j in range(len(a) - i - 1): + if a[j + 1] < a[j]: + a[j], a[j + 1] = a[j + 1], a[j] + count += 1 + return count + + +################################################################## +# Merge Sort +################################################################## + + +def _merge_lists(b, c): + count = 0 + i = j = 0 + a = [] + while i < len(b) and j < len(c): + count += 1 + if b[i] <= c[j]: + a.append(b[i]) + i += 1 + else: + a.append(c[j]) + j += 1 + if i == len(b): + a += c[j:] + else: + a += b[i:] + return a, count + + +def merge(a): + """ + Merge Sort: split the list in half, and sort each half, then + combine the sorted halves. + """ + count = 0 + if len(a) > 1: + midpoint = len(a) // 2 + b = a[:midpoint] + c = a[midpoint:] + count_b = merge(b) + count_c = merge(c) + result, count_a = _merge_lists(b, c) + a[:] = result # copy the result back into a. + count = count_a + count_b + count_c + return count + + +################################################################## +# Quick Sort +################################################################## + + +def _partition(a, l, r): + p = a[l] + i = l + j = r + 1 + count = 0 + while True: + while i < r: + i += 1 + if a[i] >= p: + break + while j > l: + j -= 1 + if j < l or a[j] <= p: + break + a[i], a[j] = a[j], a[i] # swap + count += 1 + if i >= j: + break + a[i], a[j] = a[j], a[i] # undo last swap + a[l], a[j] = a[j], a[l] + return j, count + + +def _quick(a, l, r): + count = 0 + if l < r: + s, count = _partition(a, l, r) + count += _quick(a, l, s - 1) + count += _quick(a, s + 1, r) + return count + + +def quick(a): + return _quick(a, 0, len(a) - 1) + + +################################################################## +# Demonstration +################################################################## + + +def demo(): + from random import shuffle + + for size in (10, 20, 50, 100, 200, 500, 1000): + a = list(range(size)) + + # various sort methods + shuffle(a) + count_selection = selection(a) + shuffle(a) + count_bubble = bubble(a) + shuffle(a) + count_merge = merge(a) + shuffle(a) + count_quick = quick(a) + + print( + ("size=%5d: selection=%8d, bubble=%8d, " "merge=%6d, quick=%6d") + % (size, count_selection, count_bubble, count_merge, count_quick) + ) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/misc/wordfinder.py b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/wordfinder.py new file mode 100644 index 0000000..a1751e9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/misc/wordfinder.py @@ -0,0 +1,139 @@ +# Natural Language Toolkit: Word Finder +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +# Simplified from PHP version by Robert Klein +# http://fswordfinder.sourceforge.net/ + +import random + + +# reverse a word with probability 0.5 +def revword(word): + if random.randint(1, 2) == 1: + return word[::-1] + return word + + +# try to insert word at position x,y; direction encoded in xf,yf +def step(word, x, xf, y, yf, grid): + for i in range(len(word)): + if grid[xf(i)][yf(i)] != "" and grid[xf(i)][yf(i)] != word[i]: + return False + for i in range(len(word)): + grid[xf(i)][yf(i)] = word[i] + return True + + +# try to insert word at position x,y, in direction dir +def check(word, dir, x, y, grid, rows, cols): + if dir == 1: + if x - len(word) < 0 or y - len(word) < 0: + return False + return step(word, x, lambda i: x - i, y, lambda i: y - i, grid) + elif dir == 2: + if x - len(word) < 0: + return False + return step(word, x, lambda i: x - i, y, lambda i: y, grid) + elif dir == 3: + if x - len(word) < 0 or y + (len(word) - 1) >= cols: + return False + return step(word, x, lambda i: x - i, y, lambda i: y + i, grid) + elif dir == 4: + if y - len(word) < 0: + return False + return step(word, x, lambda i: x, y, lambda i: y - i, grid) + + +def wordfinder(words, rows=20, cols=20, attempts=50, alph="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): + """ + Attempt to arrange words into a letter-grid with the specified + number of rows and columns. Try each word in several positions + and directions, until it can be fitted into the grid, or the + maximum number of allowable attempts is exceeded. Returns a tuple + consisting of the grid and the words that were successfully + placed. + + :param words: the list of words to be put into the grid + :type words: list + :param rows: the number of rows in the grid + :type rows: int + :param cols: the number of columns in the grid + :type cols: int + :param attempts: the number of times to attempt placing a word + :type attempts: int + :param alph: the alphabet, to be used for filling blank cells + :type alph: list + :rtype: tuple + """ + + # place longer words first + words = sorted(words, key=len, reverse=True) + + grid = [] # the letter grid + used = [] # the words we used + + # initialize the grid + for i in range(rows): + grid.append([""] * cols) + + # try to place each word + for word in words: + word = word.strip().upper() # normalize + save = word # keep a record of the word + word = revword(word) + for attempt in range(attempts): + r = random.randint(0, len(word)) + dir = random.choice([1, 2, 3, 4]) + x = random.randint(0, rows) + y = random.randint(0, cols) + if dir == 1: + x += r + y += r + elif dir == 2: + x += r + elif dir == 3: + x += r + y -= r + elif dir == 4: + y += r + if 0 <= x < rows and 0 <= y < cols: + if check(word, dir, x, y, grid, rows, cols): + # used.append((save, dir, x, y, word)) + used.append(save) + break + + # Fill up the remaining spaces + for i in range(rows): + for j in range(cols): + if grid[i][j] == "": + grid[i][j] = random.choice(alph) + + return grid, used + + +def word_finder(): + from nltk.corpus import words + + wordlist = words.words() + random.shuffle(wordlist) + wordlist = wordlist[:200] + wordlist = [w for w in wordlist if 3 <= len(w) <= 12] + grid, used = wordfinder(wordlist) + + print("Word Finder\n") + for i in range(len(grid)): + for j in range(len(grid[i])): + print(grid[i][j], end=" ") + print() + print() + + for i in range(len(used)): + print("%d:" % (i + 1), used[i]) + + +if __name__ == "__main__": + word_finder() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__init__.py new file mode 100644 index 0000000..f222229 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__init__.py @@ -0,0 +1,102 @@ +# Natural Language Toolkit: Parsers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +# + +""" +NLTK Parsers + +Classes and interfaces for producing tree structures that represent +the internal organization of a text. This task is known as "parsing" +the text, and the resulting tree structures are called the text's +"parses". Typically, the text is a single sentence, and the tree +structure represents the syntactic structure of the sentence. +However, parsers can also be used in other domains. For example, +parsers can be used to derive the morphological structure of the +morphemes that make up a word, or to derive the discourse structure +for a set of utterances. + +Sometimes, a single piece of text can be represented by more than one +tree structure. Texts represented by more than one tree structure are +called "ambiguous" texts. Note that there are actually two ways in +which a text can be ambiguous: + + - The text has multiple correct parses. + - There is not enough information to decide which of several + candidate parses is correct. + +However, the parser module does *not* distinguish these two types of +ambiguity. + +The parser module defines ``ParserI``, a standard interface for parsing +texts; and two simple implementations of that interface, +``ShiftReduceParser`` and ``RecursiveDescentParser``. It also contains +three sub-modules for specialized kinds of parsing: + + - ``nltk.parser.chart`` defines chart parsing, which uses dynamic + programming to efficiently parse texts. + - ``nltk.parser.probabilistic`` defines probabilistic parsing, which + associates a probability with each parse. +""" + +from nltk.parse.api import ParserI +from nltk.parse.bllip import BllipParser +from nltk.parse.chart import ( + BottomUpChartParser, + BottomUpLeftCornerChartParser, + ChartParser, + LeftCornerChartParser, + SteppingChartParser, + TopDownChartParser, +) +from nltk.parse.corenlp import CoreNLPDependencyParser, CoreNLPParser +from nltk.parse.dependencygraph import DependencyGraph +from nltk.parse.earleychart import ( + EarleyChartParser, + FeatureEarleyChartParser, + FeatureIncrementalBottomUpChartParser, + FeatureIncrementalBottomUpLeftCornerChartParser, + FeatureIncrementalChartParser, + FeatureIncrementalTopDownChartParser, + IncrementalBottomUpChartParser, + IncrementalBottomUpLeftCornerChartParser, + IncrementalChartParser, + IncrementalLeftCornerChartParser, + IncrementalTopDownChartParser, +) +from nltk.parse.evaluate import DependencyEvaluator +from nltk.parse.featurechart import ( + FeatureBottomUpChartParser, + FeatureBottomUpLeftCornerChartParser, + FeatureChartParser, + FeatureTopDownChartParser, +) +from nltk.parse.malt import MaltParser +from nltk.parse.nonprojectivedependencyparser import ( + NaiveBayesDependencyScorer, + NonprojectiveDependencyParser, + ProbabilisticNonprojectiveParser, +) +from nltk.parse.pchart import ( + BottomUpProbabilisticChartParser, + InsideChartParser, + LongestChartParser, + RandomChartParser, + UnsortedChartParser, +) +from nltk.parse.projectivedependencyparser import ( + ProbabilisticProjectiveDependencyParser, + ProjectiveDependencyParser, +) +from nltk.parse.recursivedescent import ( + RecursiveDescentParser, + SteppingRecursiveDescentParser, +) +from nltk.parse.shiftreduce import ShiftReduceParser, SteppingShiftReduceParser +from nltk.parse.transitionparser import TransitionParser +from nltk.parse.util import TestGrammar, extract_test_sentences, load_parser +from nltk.parse.viterbi import ViterbiParser diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..059a674 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..8781339 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/bllip.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/bllip.cpython-310.pyc new file mode 100644 index 0000000..6efff02 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/bllip.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/chart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/chart.cpython-310.pyc new file mode 100644 index 0000000..c62cf2d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/chart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/corenlp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/corenlp.cpython-310.pyc new file mode 100644 index 0000000..f224f81 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/corenlp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/dependencygraph.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/dependencygraph.cpython-310.pyc new file mode 100644 index 0000000..56f5028 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/dependencygraph.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/earleychart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/earleychart.cpython-310.pyc new file mode 100644 index 0000000..14734db Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/earleychart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/evaluate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/evaluate.cpython-310.pyc new file mode 100644 index 0000000..04a7127 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/evaluate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/featurechart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/featurechart.cpython-310.pyc new file mode 100644 index 0000000..01ad9f7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/featurechart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/generate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/generate.cpython-310.pyc new file mode 100644 index 0000000..ff26548 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/generate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/malt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/malt.cpython-310.pyc new file mode 100644 index 0000000..7e11e27 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/malt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/nonprojectivedependencyparser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/nonprojectivedependencyparser.cpython-310.pyc new file mode 100644 index 0000000..205ca44 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/nonprojectivedependencyparser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/pchart.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/pchart.cpython-310.pyc new file mode 100644 index 0000000..3190023 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/pchart.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/projectivedependencyparser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/projectivedependencyparser.cpython-310.pyc new file mode 100644 index 0000000..338782f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/projectivedependencyparser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/recursivedescent.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/recursivedescent.cpython-310.pyc new file mode 100644 index 0000000..b99b0da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/recursivedescent.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/shiftreduce.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/shiftreduce.cpython-310.pyc new file mode 100644 index 0000000..f2e1f9f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/shiftreduce.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/stanford.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/stanford.cpython-310.pyc new file mode 100644 index 0000000..0ce3a1a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/stanford.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/transitionparser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/transitionparser.cpython-310.pyc new file mode 100644 index 0000000..4d5bda0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/transitionparser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..c59ae29 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/viterbi.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/viterbi.cpython-310.pyc new file mode 100644 index 0000000..1e04ae4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/__pycache__/viterbi.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/api.py new file mode 100644 index 0000000..63de586 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/api.py @@ -0,0 +1,72 @@ +# Natural Language Toolkit: Parser API +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT +# + +import itertools + +from nltk.internals import overridden + + +class ParserI: + """ + A processing class for deriving trees that represent possible + structures for a sequence of tokens. These tree structures are + known as "parses". Typically, parsers are used to derive syntax + trees for sentences. But parsers can also be used to derive other + kinds of tree structure, such as morphological trees and discourse + structures. + + Subclasses must define: + - at least one of: ``parse()``, ``parse_sents()``. + + Subclasses may define: + - ``grammar()`` + """ + + def grammar(self): + """ + :return: The grammar used by this parser. + """ + raise NotImplementedError() + + def parse(self, sent, *args, **kwargs): + """ + :return: An iterator that generates parse trees for the sentence. + When possible this list is sorted from most likely to least likely. + + :param sent: The sentence to be parsed + :type sent: list(str) + :rtype: iter(Tree) + """ + if overridden(self.parse_sents): + return next(self.parse_sents([sent], *args, **kwargs)) + elif overridden(self.parse_one): + return ( + tree + for tree in [self.parse_one(sent, *args, **kwargs)] + if tree is not None + ) + elif overridden(self.parse_all): + return iter(self.parse_all(sent, *args, **kwargs)) + else: + raise NotImplementedError() + + def parse_sents(self, sents, *args, **kwargs): + """ + Apply ``self.parse()`` to each element of ``sents``. + :rtype: iter(iter(Tree)) + """ + return (self.parse(sent, *args, **kwargs) for sent in sents) + + def parse_all(self, sent, *args, **kwargs): + """:rtype: list(Tree)""" + return list(self.parse(sent, *args, **kwargs)) + + def parse_one(self, sent, *args, **kwargs): + """:rtype: Tree or None""" + return next(self.parse(sent, *args, **kwargs), None) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/bllip.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/bllip.py new file mode 100644 index 0000000..1de19ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/bllip.py @@ -0,0 +1,299 @@ +# Natural Language Toolkit: Interface to BLLIP Parser +# +# Author: David McClosky +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +from nltk.parse.api import ParserI +from nltk.tree import Tree + +""" +Interface for parsing with BLLIP Parser. Requires the Python +bllipparser module. BllipParser objects can be constructed with the +``BllipParser.from_unified_model_dir`` class method or manually using the +``BllipParser`` constructor. The former is generally easier if you have +a BLLIP Parser unified model directory -- a basic model can be obtained +from NLTK's downloader. More unified parsing models can be obtained with +BLLIP Parser's ModelFetcher (run ``python -m bllipparser.ModelFetcher`` +or see docs for ``bllipparser.ModelFetcher.download_and_install_model``). + +Basic usage:: + + # download and install a basic unified parsing model (Wall Street Journal) + # sudo python -m nltk.downloader bllip_wsj_no_aux + + >>> from nltk.data import find + >>> model_dir = find('models/bllip_wsj_no_aux').path + >>> bllip = BllipParser.from_unified_model_dir(model_dir) + + # 1-best parsing + >>> sentence1 = 'British left waffles on Falklands .'.split() + >>> top_parse = bllip.parse_one(sentence1) + >>> print(top_parse) + (S1 + (S + (NP (JJ British) (NN left)) + (VP (VBZ waffles) (PP (IN on) (NP (NNP Falklands)))) + (. .))) + + # n-best parsing + >>> sentence2 = 'Time flies'.split() + >>> all_parses = bllip.parse_all(sentence2) + >>> print(len(all_parses)) + 50 + >>> print(all_parses[0]) + (S1 (S (NP (NNP Time)) (VP (VBZ flies)))) + + # incorporating external tagging constraints (None means unconstrained tag) + >>> constrained1 = bllip.tagged_parse([('Time', 'VB'), ('flies', 'NNS')]) + >>> print(next(constrained1)) + (S1 (NP (VB Time) (NNS flies))) + >>> constrained2 = bllip.tagged_parse([('Time', 'NN'), ('flies', None)]) + >>> print(next(constrained2)) + (S1 (NP (NN Time) (VBZ flies))) + +References +---------- + +- Charniak, Eugene. "A maximum-entropy-inspired parser." Proceedings of + the 1st North American chapter of the Association for Computational + Linguistics conference. Association for Computational Linguistics, + 2000. + +- Charniak, Eugene, and Mark Johnson. "Coarse-to-fine n-best parsing + and MaxEnt discriminative reranking." Proceedings of the 43rd Annual + Meeting on Association for Computational Linguistics. Association + for Computational Linguistics, 2005. + +Known issues +------------ + +Note that BLLIP Parser is not currently threadsafe. Since this module +uses a SWIG interface, it is potentially unsafe to create multiple +``BllipParser`` objects in the same process. BLLIP Parser currently +has issues with non-ASCII text and will raise an error if given any. + +See https://pypi.python.org/pypi/bllipparser/ for more information +on BLLIP Parser's Python interface. +""" + +__all__ = ["BllipParser"] + +# this block allows this module to be imported even if bllipparser isn't +# available +try: + from bllipparser import RerankingParser + from bllipparser.RerankingParser import get_unified_model_parameters + + def _ensure_bllip_import_or_error(): + pass + +except ImportError as ie: + + def _ensure_bllip_import_or_error(ie=ie): + raise ImportError("Couldn't import bllipparser module: %s" % ie) + + +def _ensure_ascii(words): + try: + for i, word in enumerate(words): + word.encode("ascii") + except UnicodeEncodeError as e: + raise ValueError( + f"Token {i} ({word!r}) is non-ASCII. BLLIP Parser " + "currently doesn't support non-ASCII inputs." + ) from e + + +def _scored_parse_to_nltk_tree(scored_parse): + return Tree.fromstring(str(scored_parse.ptb_parse)) + + +class BllipParser(ParserI): + """ + Interface for parsing with BLLIP Parser. BllipParser objects can be + constructed with the ``BllipParser.from_unified_model_dir`` class + method or manually using the ``BllipParser`` constructor. + """ + + def __init__( + self, + parser_model=None, + reranker_features=None, + reranker_weights=None, + parser_options=None, + reranker_options=None, + ): + """ + Load a BLLIP Parser model from scratch. You'll typically want to + use the ``from_unified_model_dir()`` class method to construct + this object. + + :param parser_model: Path to parser model directory + :type parser_model: str + + :param reranker_features: Path the reranker model's features file + :type reranker_features: str + + :param reranker_weights: Path the reranker model's weights file + :type reranker_weights: str + + :param parser_options: optional dictionary of parser options, see + ``bllipparser.RerankingParser.RerankingParser.load_parser_options()`` + for more information. + :type parser_options: dict(str) + + :param reranker_options: optional + dictionary of reranker options, see + ``bllipparser.RerankingParser.RerankingParser.load_reranker_model()`` + for more information. + :type reranker_options: dict(str) + """ + _ensure_bllip_import_or_error() + + parser_options = parser_options or {} + reranker_options = reranker_options or {} + + self.rrp = RerankingParser() + self.rrp.load_parser_model(parser_model, **parser_options) + if reranker_features and reranker_weights: + self.rrp.load_reranker_model( + features_filename=reranker_features, + weights_filename=reranker_weights, + **reranker_options, + ) + + def parse(self, sentence): + """ + Use BLLIP Parser to parse a sentence. Takes a sentence as a list + of words; it will be automatically tagged with this BLLIP Parser + instance's tagger. + + :return: An iterator that generates parse trees for the sentence + from most likely to least likely. + + :param sentence: The sentence to be parsed + :type sentence: list(str) + :rtype: iter(Tree) + """ + _ensure_ascii(sentence) + nbest_list = self.rrp.parse(sentence) + for scored_parse in nbest_list: + yield _scored_parse_to_nltk_tree(scored_parse) + + def tagged_parse(self, word_and_tag_pairs): + """ + Use BLLIP to parse a sentence. Takes a sentence as a list of + (word, tag) tuples; the sentence must have already been tokenized + and tagged. BLLIP will attempt to use the tags provided but may + use others if it can't come up with a complete parse subject + to those constraints. You may also specify a tag as ``None`` + to leave a token's tag unconstrained. + + :return: An iterator that generates parse trees for the sentence + from most likely to least likely. + + :param sentence: Input sentence to parse as (word, tag) pairs + :type sentence: list(tuple(str, str)) + :rtype: iter(Tree) + """ + words = [] + tag_map = {} + for i, (word, tag) in enumerate(word_and_tag_pairs): + words.append(word) + if tag is not None: + tag_map[i] = tag + + _ensure_ascii(words) + nbest_list = self.rrp.parse_tagged(words, tag_map) + for scored_parse in nbest_list: + yield _scored_parse_to_nltk_tree(scored_parse) + + @classmethod + def from_unified_model_dir( + cls, model_dir, parser_options=None, reranker_options=None + ): + """ + Create a ``BllipParser`` object from a unified parsing model + directory. Unified parsing model directories are a standardized + way of storing BLLIP parser and reranker models together on disk. + See ``bllipparser.RerankingParser.get_unified_model_parameters()`` + for more information about unified model directories. + + :return: A ``BllipParser`` object using the parser and reranker + models in the model directory. + + :param model_dir: Path to the unified model directory. + :type model_dir: str + :param parser_options: optional dictionary of parser options, see + ``bllipparser.RerankingParser.RerankingParser.load_parser_options()`` + for more information. + :type parser_options: dict(str) + :param reranker_options: optional dictionary of reranker options, see + ``bllipparser.RerankingParser.RerankingParser.load_reranker_model()`` + for more information. + :type reranker_options: dict(str) + :rtype: BllipParser + """ + ( + parser_model_dir, + reranker_features_filename, + reranker_weights_filename, + ) = get_unified_model_parameters(model_dir) + return cls( + parser_model_dir, + reranker_features_filename, + reranker_weights_filename, + parser_options, + reranker_options, + ) + + +def demo(): + """This assumes the Python module bllipparser is installed.""" + + # download and install a basic unified parsing model (Wall Street Journal) + # sudo python -m nltk.downloader bllip_wsj_no_aux + + from nltk.data import find + + model_dir = find("models/bllip_wsj_no_aux").path + + print("Loading BLLIP Parsing models...") + # the easiest way to get started is to use a unified model + bllip = BllipParser.from_unified_model_dir(model_dir) + print("Done.") + + sentence1 = "British left waffles on Falklands .".split() + sentence2 = "I saw the man with the telescope .".split() + # this sentence is known to fail under the WSJ parsing model + fail1 = "# ! ? : -".split() + for sentence in (sentence1, sentence2, fail1): + print("Sentence: %r" % " ".join(sentence)) + try: + tree = next(bllip.parse(sentence)) + print(tree) + except StopIteration: + print("(parse failed)") + + # n-best parsing demo + for i, parse in enumerate(bllip.parse(sentence1)): + print("parse %d:\n%s" % (i, parse)) + + # using external POS tag constraints + print( + "forcing 'tree' to be 'NN':", + next(bllip.tagged_parse([("A", None), ("tree", "NN")])), + ) + print( + "forcing 'A' to be 'DT' and 'tree' to be 'NNP':", + next(bllip.tagged_parse([("A", "DT"), ("tree", "NNP")])), + ) + # constraints don't have to make sense... (though on more complicated + # sentences, they may cause the parse to fail) + print( + "forcing 'A' to be 'NNP':", + next(bllip.tagged_parse([("A", "NNP"), ("tree", None)])), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/chart.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/chart.py new file mode 100644 index 0000000..ba17b08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/chart.py @@ -0,0 +1,1848 @@ +# Natural Language Toolkit: A Chart Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Jean Mark Gawron +# Peter Ljunglöf +# URL: +# For license information, see LICENSE.TXT + +""" +Data classes and parser implementations for "chart parsers", which +use dynamic programming to efficiently parse a text. A chart +parser derives parse trees for a text by iteratively adding "edges" +to a "chart." Each edge represents a hypothesis about the tree +structure for a subsequence of the text. The chart is a +"blackboard" for composing and combining these hypotheses. + +When a chart parser begins parsing a text, it creates a new (empty) +chart, spanning the text. It then incrementally adds new edges to the +chart. A set of "chart rules" specifies the conditions under which +new edges should be added to the chart. Once the chart reaches a +stage where none of the chart rules adds any new edges, parsing is +complete. + +Charts are encoded with the ``Chart`` class, and edges are encoded with +the ``TreeEdge`` and ``LeafEdge`` classes. The chart parser module +defines three chart parsers: + + - ``ChartParser`` is a simple and flexible chart parser. Given a + set of chart rules, it will apply those rules to the chart until + no more edges are added. + + - ``SteppingChartParser`` is a subclass of ``ChartParser`` that can + be used to step through the parsing process. +""" + +import itertools +import re +import warnings +from functools import total_ordering + +from nltk.grammar import PCFG, is_nonterminal, is_terminal +from nltk.internals import raise_unorderable_types +from nltk.parse.api import ParserI +from nltk.tree import Tree +from nltk.util import OrderedDict + +######################################################################## +## Edges +######################################################################## + + +@total_ordering +class EdgeI: + """ + A hypothesis about the structure of part of a sentence. + Each edge records the fact that a structure is (partially) + consistent with the sentence. An edge contains: + + - A span, indicating what part of the sentence is + consistent with the hypothesized structure. + - A left-hand side, specifying what kind of structure is + hypothesized. + - A right-hand side, specifying the contents of the + hypothesized structure. + - A dot position, indicating how much of the hypothesized + structure is consistent with the sentence. + + Every edge is either complete or incomplete: + + - An edge is complete if its structure is fully consistent + with the sentence. + - An edge is incomplete if its structure is partially + consistent with the sentence. For every incomplete edge, the + span specifies a possible prefix for the edge's structure. + + There are two kinds of edge: + + - A ``TreeEdge`` records which trees have been found to + be (partially) consistent with the text. + - A ``LeafEdge`` records the tokens occurring in the text. + + The ``EdgeI`` interface provides a common interface to both types + of edge, allowing chart parsers to treat them in a uniform manner. + """ + + def __init__(self): + if self.__class__ == EdgeI: + raise TypeError("Edge is an abstract interface") + + # //////////////////////////////////////////////////////////// + # Span + # //////////////////////////////////////////////////////////// + + def span(self): + """ + Return a tuple ``(s, e)``, where ``tokens[s:e]`` is the + portion of the sentence that is consistent with this + edge's structure. + + :rtype: tuple(int, int) + """ + raise NotImplementedError() + + def start(self): + """ + Return the start index of this edge's span. + + :rtype: int + """ + raise NotImplementedError() + + def end(self): + """ + Return the end index of this edge's span. + + :rtype: int + """ + raise NotImplementedError() + + def length(self): + """ + Return the length of this edge's span. + + :rtype: int + """ + raise NotImplementedError() + + # //////////////////////////////////////////////////////////// + # Left Hand Side + # //////////////////////////////////////////////////////////// + + def lhs(self): + """ + Return this edge's left-hand side, which specifies what kind + of structure is hypothesized by this edge. + + :see: ``TreeEdge`` and ``LeafEdge`` for a description of + the left-hand side values for each edge type. + """ + raise NotImplementedError() + + # //////////////////////////////////////////////////////////// + # Right Hand Side + # //////////////////////////////////////////////////////////// + + def rhs(self): + """ + Return this edge's right-hand side, which specifies + the content of the structure hypothesized by this edge. + + :see: ``TreeEdge`` and ``LeafEdge`` for a description of + the right-hand side values for each edge type. + """ + raise NotImplementedError() + + def dot(self): + """ + Return this edge's dot position, which indicates how much of + the hypothesized structure is consistent with the + sentence. In particular, ``self.rhs[:dot]`` is consistent + with ``tokens[self.start():self.end()]``. + + :rtype: int + """ + raise NotImplementedError() + + def nextsym(self): + """ + Return the element of this edge's right-hand side that + immediately follows its dot. + + :rtype: Nonterminal or terminal or None + """ + raise NotImplementedError() + + def is_complete(self): + """ + Return True if this edge's structure is fully consistent + with the text. + + :rtype: bool + """ + raise NotImplementedError() + + def is_incomplete(self): + """ + Return True if this edge's structure is partially consistent + with the text. + + :rtype: bool + """ + raise NotImplementedError() + + # //////////////////////////////////////////////////////////// + # Comparisons & hashing + # //////////////////////////////////////////////////////////// + + def __eq__(self, other): + return ( + self.__class__ is other.__class__ + and self._comparison_key == other._comparison_key + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, EdgeI): + raise_unorderable_types("<", self, other) + if self.__class__ is other.__class__: + return self._comparison_key < other._comparison_key + else: + return self.__class__.__name__ < other.__class__.__name__ + + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self._comparison_key) + return self._hash + + +class TreeEdge(EdgeI): + """ + An edge that records the fact that a tree is (partially) + consistent with the sentence. A tree edge consists of: + + - A span, indicating what part of the sentence is + consistent with the hypothesized tree. + - A left-hand side, specifying the hypothesized tree's node + value. + - A right-hand side, specifying the hypothesized tree's + children. Each element of the right-hand side is either a + terminal, specifying a token with that terminal as its leaf + value; or a nonterminal, specifying a subtree with that + nonterminal's symbol as its node value. + - A dot position, indicating which children are consistent + with part of the sentence. In particular, if ``dot`` is the + dot position, ``rhs`` is the right-hand size, ``(start,end)`` + is the span, and ``sentence`` is the list of tokens in the + sentence, then ``tokens[start:end]`` can be spanned by the + children specified by ``rhs[:dot]``. + + For more information about edges, see the ``EdgeI`` interface. + """ + + def __init__(self, span, lhs, rhs, dot=0): + """ + Construct a new ``TreeEdge``. + + :type span: tuple(int, int) + :param span: A tuple ``(s, e)``, where ``tokens[s:e]`` is the + portion of the sentence that is consistent with the new + edge's structure. + :type lhs: Nonterminal + :param lhs: The new edge's left-hand side, specifying the + hypothesized tree's node value. + :type rhs: list(Nonterminal and str) + :param rhs: The new edge's right-hand side, specifying the + hypothesized tree's children. + :type dot: int + :param dot: The position of the new edge's dot. This position + specifies what prefix of the production's right hand side + is consistent with the text. In particular, if + ``sentence`` is the list of tokens in the sentence, then + ``okens[span[0]:span[1]]`` can be spanned by the + children specified by ``rhs[:dot]``. + """ + self._span = span + self._lhs = lhs + rhs = tuple(rhs) + self._rhs = rhs + self._dot = dot + self._comparison_key = (span, lhs, rhs, dot) + + @staticmethod + def from_production(production, index): + """ + Return a new ``TreeEdge`` formed from the given production. + The new edge's left-hand side and right-hand side will + be taken from ``production``; its span will be + ``(index,index)``; and its dot position will be ``0``. + + :rtype: TreeEdge + """ + return TreeEdge( + span=(index, index), lhs=production.lhs(), rhs=production.rhs(), dot=0 + ) + + def move_dot_forward(self, new_end): + """ + Return a new ``TreeEdge`` formed from this edge. + The new edge's dot position is increased by ``1``, + and its end index will be replaced by ``new_end``. + + :param new_end: The new end index. + :type new_end: int + :rtype: TreeEdge + """ + return TreeEdge( + span=(self._span[0], new_end), + lhs=self._lhs, + rhs=self._rhs, + dot=self._dot + 1, + ) + + # Accessors + def lhs(self): + return self._lhs + + def span(self): + return self._span + + def start(self): + return self._span[0] + + def end(self): + return self._span[1] + + def length(self): + return self._span[1] - self._span[0] + + def rhs(self): + return self._rhs + + def dot(self): + return self._dot + + def is_complete(self): + return self._dot == len(self._rhs) + + def is_incomplete(self): + return self._dot != len(self._rhs) + + def nextsym(self): + if self._dot >= len(self._rhs): + return None + else: + return self._rhs[self._dot] + + # String representation + def __str__(self): + str = f"[{self._span[0]}:{self._span[1]}] " + str += "%-2r ->" % (self._lhs,) + + for i in range(len(self._rhs)): + if i == self._dot: + str += " *" + str += " %s" % repr(self._rhs[i]) + if len(self._rhs) == self._dot: + str += " *" + return str + + def __repr__(self): + return "[Edge: %s]" % self + + +class LeafEdge(EdgeI): + """ + An edge that records the fact that a leaf value is consistent with + a word in the sentence. A leaf edge consists of: + + - An index, indicating the position of the word. + - A leaf, specifying the word's content. + + A leaf edge's left-hand side is its leaf value, and its right hand + side is ``()``. Its span is ``[index, index+1]``, and its dot + position is ``0``. + """ + + def __init__(self, leaf, index): + """ + Construct a new ``LeafEdge``. + + :param leaf: The new edge's leaf value, specifying the word + that is recorded by this edge. + :param index: The new edge's index, specifying the position of + the word that is recorded by this edge. + """ + self._leaf = leaf + self._index = index + self._comparison_key = (leaf, index) + + # Accessors + def lhs(self): + return self._leaf + + def span(self): + return (self._index, self._index + 1) + + def start(self): + return self._index + + def end(self): + return self._index + 1 + + def length(self): + return 1 + + def rhs(self): + return () + + def dot(self): + return 0 + + def is_complete(self): + return True + + def is_incomplete(self): + return False + + def nextsym(self): + return None + + # String representations + def __str__(self): + return f"[{self._index}:{self._index + 1}] {repr(self._leaf)}" + + def __repr__(self): + return "[Edge: %s]" % (self) + + +######################################################################## +## Chart +######################################################################## + + +class Chart: + """ + A blackboard for hypotheses about the syntactic constituents of a + sentence. A chart contains a set of edges, and each edge encodes + a single hypothesis about the structure of some portion of the + sentence. + + The ``select`` method can be used to select a specific collection + of edges. For example ``chart.select(is_complete=True, start=0)`` + yields all complete edges whose start indices are 0. To ensure + the efficiency of these selection operations, ``Chart`` dynamically + creates and maintains an index for each set of attributes that + have been selected on. + + In order to reconstruct the trees that are represented by an edge, + the chart associates each edge with a set of child pointer lists. + A child pointer list is a list of the edges that license an + edge's right-hand side. + + :ivar _tokens: The sentence that the chart covers. + :ivar _num_leaves: The number of tokens. + :ivar _edges: A list of the edges in the chart + :ivar _edge_to_cpls: A dictionary mapping each edge to a set + of child pointer lists that are associated with that edge. + :ivar _indexes: A dictionary mapping tuples of edge attributes + to indices, where each index maps the corresponding edge + attribute values to lists of edges. + """ + + def __init__(self, tokens): + """ + Construct a new chart. The chart is initialized with the + leaf edges corresponding to the terminal leaves. + + :type tokens: list + :param tokens: The sentence that this chart will be used to parse. + """ + # Record the sentence token and the sentence length. + self._tokens = tuple(tokens) + self._num_leaves = len(self._tokens) + + # Initialise the chart. + self.initialize() + + def initialize(self): + """ + Clear the chart. + """ + # A list of edges contained in this chart. + self._edges = [] + + # The set of child pointer lists associated with each edge. + self._edge_to_cpls = {} + + # Indexes mapping attribute values to lists of edges + # (used by select()). + self._indexes = {} + + # //////////////////////////////////////////////////////////// + # Sentence Access + # //////////////////////////////////////////////////////////// + + def num_leaves(self): + """ + Return the number of words in this chart's sentence. + + :rtype: int + """ + return self._num_leaves + + def leaf(self, index): + """ + Return the leaf value of the word at the given index. + + :rtype: str + """ + return self._tokens[index] + + def leaves(self): + """ + Return a list of the leaf values of each word in the + chart's sentence. + + :rtype: list(str) + """ + return self._tokens + + # //////////////////////////////////////////////////////////// + # Edge access + # //////////////////////////////////////////////////////////// + + def edges(self): + """ + Return a list of all edges in this chart. New edges + that are added to the chart after the call to edges() + will *not* be contained in this list. + + :rtype: list(EdgeI) + :see: ``iteredges``, ``select`` + """ + return self._edges[:] + + def iteredges(self): + """ + Return an iterator over the edges in this chart. It is + not guaranteed that new edges which are added to the + chart before the iterator is exhausted will also be generated. + + :rtype: iter(EdgeI) + :see: ``edges``, ``select`` + """ + return iter(self._edges) + + # Iterating over the chart yields its edges. + __iter__ = iteredges + + def num_edges(self): + """ + Return the number of edges contained in this chart. + + :rtype: int + """ + return len(self._edge_to_cpls) + + def select(self, **restrictions): + """ + Return an iterator over the edges in this chart. Any + new edges that are added to the chart before the iterator + is exahusted will also be generated. ``restrictions`` + can be used to restrict the set of edges that will be + generated. + + :param span: Only generate edges ``e`` where ``e.span()==span`` + :param start: Only generate edges ``e`` where ``e.start()==start`` + :param end: Only generate edges ``e`` where ``e.end()==end`` + :param length: Only generate edges ``e`` where ``e.length()==length`` + :param lhs: Only generate edges ``e`` where ``e.lhs()==lhs`` + :param rhs: Only generate edges ``e`` where ``e.rhs()==rhs`` + :param nextsym: Only generate edges ``e`` where + ``e.nextsym()==nextsym`` + :param dot: Only generate edges ``e`` where ``e.dot()==dot`` + :param is_complete: Only generate edges ``e`` where + ``e.is_complete()==is_complete`` + :param is_incomplete: Only generate edges ``e`` where + ``e.is_incomplete()==is_incomplete`` + :rtype: iter(EdgeI) + """ + # If there are no restrictions, then return all edges. + if restrictions == {}: + return iter(self._edges) + + # Find the index corresponding to the given restrictions. + restr_keys = sorted(restrictions.keys()) + restr_keys = tuple(restr_keys) + + # If it doesn't exist, then create it. + if restr_keys not in self._indexes: + self._add_index(restr_keys) + + vals = tuple(restrictions[key] for key in restr_keys) + return iter(self._indexes[restr_keys].get(vals, [])) + + def _add_index(self, restr_keys): + """ + A helper function for ``select``, which creates a new index for + a given set of attributes (aka restriction keys). + """ + # Make sure it's a valid index. + for key in restr_keys: + if not hasattr(EdgeI, key): + raise ValueError("Bad restriction: %s" % key) + + # Create the index. + index = self._indexes[restr_keys] = {} + + # Add all existing edges to the index. + for edge in self._edges: + vals = tuple(getattr(edge, key)() for key in restr_keys) + index.setdefault(vals, []).append(edge) + + def _register_with_indexes(self, edge): + """ + A helper function for ``insert``, which registers the new + edge with all existing indexes. + """ + for (restr_keys, index) in self._indexes.items(): + vals = tuple(getattr(edge, key)() for key in restr_keys) + index.setdefault(vals, []).append(edge) + + # //////////////////////////////////////////////////////////// + # Edge Insertion + # //////////////////////////////////////////////////////////// + + def insert_with_backpointer(self, new_edge, previous_edge, child_edge): + """ + Add a new edge to the chart, using a pointer to the previous edge. + """ + cpls = self.child_pointer_lists(previous_edge) + new_cpls = [cpl + (child_edge,) for cpl in cpls] + return self.insert(new_edge, *new_cpls) + + def insert(self, edge, *child_pointer_lists): + """ + Add a new edge to the chart, and return True if this operation + modified the chart. In particular, return true iff the chart + did not already contain ``edge``, or if it did not already associate + ``child_pointer_lists`` with ``edge``. + + :type edge: EdgeI + :param edge: The new edge + :type child_pointer_lists: sequence of tuple(EdgeI) + :param child_pointer_lists: A sequence of lists of the edges that + were used to form this edge. This list is used to reconstruct + the trees (or partial trees) that are associated with ``edge``. + :rtype: bool + """ + # Is it a new edge? + if edge not in self._edge_to_cpls: + # Add it to the list of edges. + self._append_edge(edge) + # Register with indexes. + self._register_with_indexes(edge) + + # Get the set of child pointer lists for this edge. + cpls = self._edge_to_cpls.setdefault(edge, OrderedDict()) + chart_was_modified = False + for child_pointer_list in child_pointer_lists: + child_pointer_list = tuple(child_pointer_list) + if child_pointer_list not in cpls: + # It's a new CPL; register it, and return true. + cpls[child_pointer_list] = True + chart_was_modified = True + return chart_was_modified + + def _append_edge(self, edge): + self._edges.append(edge) + + # //////////////////////////////////////////////////////////// + # Tree extraction & child pointer lists + # //////////////////////////////////////////////////////////// + + def parses(self, root, tree_class=Tree): + """ + Return an iterator of the complete tree structures that span + the entire chart, and whose root node is ``root``. + """ + for edge in self.select(start=0, end=self._num_leaves, lhs=root): + yield from self.trees(edge, tree_class=tree_class, complete=True) + + def trees(self, edge, tree_class=Tree, complete=False): + """ + Return an iterator of the tree structures that are associated + with ``edge``. + + If ``edge`` is incomplete, then the unexpanded children will be + encoded as childless subtrees, whose node value is the + corresponding terminal or nonterminal. + + :rtype: list(Tree) + :note: If two trees share a common subtree, then the same + Tree may be used to encode that subtree in + both trees. If you need to eliminate this subtree + sharing, then create a deep copy of each tree. + """ + return iter(self._trees(edge, complete, memo={}, tree_class=tree_class)) + + def _trees(self, edge, complete, memo, tree_class): + """ + A helper function for ``trees``. + + :param memo: A dictionary used to record the trees that we've + generated for each edge, so that when we see an edge more + than once, we can reuse the same trees. + """ + # If we've seen this edge before, then reuse our old answer. + if edge in memo: + return memo[edge] + + # when we're reading trees off the chart, don't use incomplete edges + if complete and edge.is_incomplete(): + return [] + + # Leaf edges. + if isinstance(edge, LeafEdge): + leaf = self._tokens[edge.start()] + memo[edge] = [leaf] + return [leaf] + + # Until we're done computing the trees for edge, set + # memo[edge] to be empty. This has the effect of filtering + # out any cyclic trees (i.e., trees that contain themselves as + # descendants), because if we reach this edge via a cycle, + # then it will appear that the edge doesn't generate any trees. + memo[edge] = [] + trees = [] + lhs = edge.lhs().symbol() + + # Each child pointer list can be used to form trees. + for cpl in self.child_pointer_lists(edge): + # Get the set of child choices for each child pointer. + # child_choices[i] is the set of choices for the tree's + # ith child. + child_choices = [self._trees(cp, complete, memo, tree_class) for cp in cpl] + + # For each combination of children, add a tree. + for children in itertools.product(*child_choices): + trees.append(tree_class(lhs, children)) + + # If the edge is incomplete, then extend it with "partial trees": + if edge.is_incomplete(): + unexpanded = [tree_class(elt, []) for elt in edge.rhs()[edge.dot() :]] + for tree in trees: + tree.extend(unexpanded) + + # Update the memoization dictionary. + memo[edge] = trees + + # Return the list of trees. + return trees + + def child_pointer_lists(self, edge): + """ + Return the set of child pointer lists for the given edge. + Each child pointer list is a list of edges that have + been used to form this edge. + + :rtype: list(list(EdgeI)) + """ + # Make a copy, in case they modify it. + return self._edge_to_cpls.get(edge, {}).keys() + + # //////////////////////////////////////////////////////////// + # Display + # //////////////////////////////////////////////////////////// + def pretty_format_edge(self, edge, width=None): + """ + Return a pretty-printed string representation of a given edge + in this chart. + + :rtype: str + :param width: The number of characters allotted to each + index in the sentence. + """ + if width is None: + width = 50 // (self.num_leaves() + 1) + (start, end) = (edge.start(), edge.end()) + + str = "|" + ("." + " " * (width - 1)) * start + + # Zero-width edges are "#" if complete, ">" if incomplete + if start == end: + if edge.is_complete(): + str += "#" + else: + str += ">" + + # Spanning complete edges are "[===]"; Other edges are + # "[---]" if complete, "[--->" if incomplete + elif edge.is_complete() and edge.span() == (0, self._num_leaves): + str += "[" + ("=" * width) * (end - start - 1) + "=" * (width - 1) + "]" + elif edge.is_complete(): + str += "[" + ("-" * width) * (end - start - 1) + "-" * (width - 1) + "]" + else: + str += "[" + ("-" * width) * (end - start - 1) + "-" * (width - 1) + ">" + + str += (" " * (width - 1) + ".") * (self._num_leaves - end) + return str + "| %s" % edge + + def pretty_format_leaves(self, width=None): + """ + Return a pretty-printed string representation of this + chart's leaves. This string can be used as a header + for calls to ``pretty_format_edge``. + """ + if width is None: + width = 50 // (self.num_leaves() + 1) + + if self._tokens is not None and width > 1: + header = "|." + for tok in self._tokens: + header += tok[: width - 1].center(width - 1) + "." + header += "|" + else: + header = "" + + return header + + def pretty_format(self, width=None): + """ + Return a pretty-printed string representation of this chart. + + :param width: The number of characters allotted to each + index in the sentence. + :rtype: str + """ + if width is None: + width = 50 // (self.num_leaves() + 1) + # sort edges: primary key=length, secondary key=start index. + # (and filter out the token edges) + edges = sorted((e.length(), e.start(), e) for e in self) + edges = [e for (_, _, e) in edges] + + return ( + self.pretty_format_leaves(width) + + "\n" + + "\n".join(self.pretty_format_edge(edge, width) for edge in edges) + ) + + # //////////////////////////////////////////////////////////// + # Display: Dot (AT&T Graphviz) + # //////////////////////////////////////////////////////////// + + def dot_digraph(self): + # Header + s = "digraph nltk_chart {\n" + # s += ' size="5,5";\n' + s += " rankdir=LR;\n" + s += " node [height=0.1,width=0.1];\n" + s += ' node [style=filled, color="lightgray"];\n' + + # Set up the nodes + for y in range(self.num_edges(), -1, -1): + if y == 0: + s += ' node [style=filled, color="black"];\n' + for x in range(self.num_leaves() + 1): + if y == 0 or ( + x <= self._edges[y - 1].start() or x >= self._edges[y - 1].end() + ): + s += ' %04d.%04d [label=""];\n' % (x, y) + + # Add a spacer + s += " x [style=invis]; x->0000.0000 [style=invis];\n" + + # Declare ranks. + for x in range(self.num_leaves() + 1): + s += " {rank=same;" + for y in range(self.num_edges() + 1): + if y == 0 or ( + x <= self._edges[y - 1].start() or x >= self._edges[y - 1].end() + ): + s += " %04d.%04d" % (x, y) + s += "}\n" + + # Add the leaves + s += " edge [style=invis, weight=100];\n" + s += " node [shape=plaintext]\n" + s += " 0000.0000" + for x in range(self.num_leaves()): + s += "->%s->%04d.0000" % (self.leaf(x), x + 1) + s += ";\n\n" + + # Add the edges + s += " edge [style=solid, weight=1];\n" + for y, edge in enumerate(self): + for x in range(edge.start()): + s += ' %04d.%04d -> %04d.%04d [style="invis"];\n' % ( + x, + y + 1, + x + 1, + y + 1, + ) + s += ' %04d.%04d -> %04d.%04d [label="%s"];\n' % ( + edge.start(), + y + 1, + edge.end(), + y + 1, + edge, + ) + for x in range(edge.end(), self.num_leaves()): + s += ' %04d.%04d -> %04d.%04d [style="invis"];\n' % ( + x, + y + 1, + x + 1, + y + 1, + ) + s += "}\n" + return s + + +######################################################################## +## Chart Rules +######################################################################## + + +class ChartRuleI: + """ + A rule that specifies what new edges are licensed by any given set + of existing edges. Each chart rule expects a fixed number of + edges, as indicated by the class variable ``NUM_EDGES``. In + particular: + + - A chart rule with ``NUM_EDGES=0`` specifies what new edges are + licensed, regardless of existing edges. + - A chart rule with ``NUM_EDGES=1`` specifies what new edges are + licensed by a single existing edge. + - A chart rule with ``NUM_EDGES=2`` specifies what new edges are + licensed by a pair of existing edges. + + :type NUM_EDGES: int + :cvar NUM_EDGES: The number of existing edges that this rule uses + to license new edges. Typically, this number ranges from zero + to two. + """ + + def apply(self, chart, grammar, *edges): + """ + Return a generator that will add edges licensed by this rule + and the given edges to the chart, one at a time. Each + time the generator is resumed, it will either add a new + edge and yield that edge; or return. + + :type edges: list(EdgeI) + :param edges: A set of existing edges. The number of edges + that should be passed to ``apply()`` is specified by the + ``NUM_EDGES`` class variable. + :rtype: iter(EdgeI) + """ + raise NotImplementedError() + + def apply_everywhere(self, chart, grammar): + """ + Return a generator that will add all edges licensed by + this rule, given the edges that are currently in the + chart, one at a time. Each time the generator is resumed, + it will either add a new edge and yield that edge; or return. + + :rtype: iter(EdgeI) + """ + raise NotImplementedError() + + +class AbstractChartRule(ChartRuleI): + """ + An abstract base class for chart rules. ``AbstractChartRule`` + provides: + + - A default implementation for ``apply``. + - A default implementation for ``apply_everywhere``, + (Currently, this implementation assumes that ``NUM_EDGES <= 3``.) + - A default implementation for ``__str__``, which returns a + name based on the rule's class name. + """ + + # Subclasses must define apply. + def apply(self, chart, grammar, *edges): + raise NotImplementedError() + + # Default: loop through the given number of edges, and call + # self.apply() for each set of edges. + def apply_everywhere(self, chart, grammar): + if self.NUM_EDGES == 0: + yield from self.apply(chart, grammar) + + elif self.NUM_EDGES == 1: + for e1 in chart: + yield from self.apply(chart, grammar, e1) + + elif self.NUM_EDGES == 2: + for e1 in chart: + for e2 in chart: + yield from self.apply(chart, grammar, e1, e2) + + elif self.NUM_EDGES == 3: + for e1 in chart: + for e2 in chart: + for e3 in chart: + yield from self.apply(chart, grammar, e1, e2, e3) + + else: + raise AssertionError("NUM_EDGES>3 is not currently supported") + + # Default: return a name based on the class name. + def __str__(self): + # Add spaces between InitialCapsWords. + return re.sub("([a-z])([A-Z])", r"\1 \2", self.__class__.__name__) + + +# //////////////////////////////////////////////////////////// +# Fundamental Rule +# //////////////////////////////////////////////////////////// + + +class FundamentalRule(AbstractChartRule): + r""" + A rule that joins two adjacent edges to form a single combined + edge. In particular, this rule specifies that any pair of edges + + - ``[A -> alpha \* B beta][i:j]`` + - ``[B -> gamma \*][j:k]`` + + licenses the edge: + + - ``[A -> alpha B * beta][i:j]`` + """ + + NUM_EDGES = 2 + + def apply(self, chart, grammar, left_edge, right_edge): + # Make sure the rule is applicable. + if not ( + left_edge.is_incomplete() + and right_edge.is_complete() + and left_edge.end() == right_edge.start() + and left_edge.nextsym() == right_edge.lhs() + ): + return + + # Construct the new edge. + new_edge = left_edge.move_dot_forward(right_edge.end()) + + # Insert it into the chart. + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + +class SingleEdgeFundamentalRule(FundamentalRule): + r""" + A rule that joins a given edge with adjacent edges in the chart, + to form combined edges. In particular, this rule specifies that + either of the edges: + + - ``[A -> alpha \* B beta][i:j]`` + - ``[B -> gamma \*][j:k]`` + + licenses the edge: + + - ``[A -> alpha B * beta][i:j]`` + + if the other edge is already in the chart. + + :note: This is basically ``FundamentalRule``, with one edge left + unspecified. + """ + + NUM_EDGES = 1 + + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + yield from self._apply_incomplete(chart, grammar, edge) + else: + yield from self._apply_complete(chart, grammar, edge) + + def _apply_complete(self, chart, grammar, right_edge): + for left_edge in chart.select( + end=right_edge.start(), is_complete=False, nextsym=right_edge.lhs() + ): + new_edge = left_edge.move_dot_forward(right_edge.end()) + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + def _apply_incomplete(self, chart, grammar, left_edge): + for right_edge in chart.select( + start=left_edge.end(), is_complete=True, lhs=left_edge.nextsym() + ): + new_edge = left_edge.move_dot_forward(right_edge.end()) + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + +# //////////////////////////////////////////////////////////// +# Inserting Terminal Leafs +# //////////////////////////////////////////////////////////// + + +class LeafInitRule(AbstractChartRule): + NUM_EDGES = 0 + + def apply(self, chart, grammar): + for index in range(chart.num_leaves()): + new_edge = LeafEdge(chart.leaf(index), index) + if chart.insert(new_edge, ()): + yield new_edge + + +# //////////////////////////////////////////////////////////// +# Top-Down Prediction +# //////////////////////////////////////////////////////////// + + +class TopDownInitRule(AbstractChartRule): + r""" + A rule licensing edges corresponding to the grammar productions for + the grammar's start symbol. In particular, this rule specifies that + ``[S -> \* alpha][0:i]`` is licensed for each grammar production + ``S -> alpha``, where ``S`` is the grammar's start symbol. + """ + + NUM_EDGES = 0 + + def apply(self, chart, grammar): + for prod in grammar.productions(lhs=grammar.start()): + new_edge = TreeEdge.from_production(prod, 0) + if chart.insert(new_edge, ()): + yield new_edge + + +class TopDownPredictRule(AbstractChartRule): + r""" + A rule licensing edges corresponding to the grammar productions + for the nonterminal following an incomplete edge's dot. In + particular, this rule specifies that + ``[A -> alpha \* B beta][i:j]`` licenses the edge + ``[B -> \* gamma][j:j]`` for each grammar production ``B -> gamma``. + + :note: This rule corresponds to the Predictor Rule in Earley parsing. + """ + + NUM_EDGES = 1 + + def apply(self, chart, grammar, edge): + if edge.is_complete(): + return + for prod in grammar.productions(lhs=edge.nextsym()): + new_edge = TreeEdge.from_production(prod, edge.end()) + if chart.insert(new_edge, ()): + yield new_edge + + +class CachedTopDownPredictRule(TopDownPredictRule): + r""" + A cached version of ``TopDownPredictRule``. After the first time + this rule is applied to an edge with a given ``end`` and ``next``, + it will not generate any more edges for edges with that ``end`` and + ``next``. + + If ``chart`` or ``grammar`` are changed, then the cache is flushed. + """ + + def __init__(self): + TopDownPredictRule.__init__(self) + self._done = {} + + def apply(self, chart, grammar, edge): + if edge.is_complete(): + return + nextsym, index = edge.nextsym(), edge.end() + if not is_nonterminal(nextsym): + return + + # If we've already applied this rule to an edge with the same + # next & end, and the chart & grammar have not changed, then + # just return (no new edges to add). + done = self._done.get((nextsym, index), (None, None)) + if done[0] is chart and done[1] is grammar: + return + + # Add all the edges indicated by the top down expand rule. + for prod in grammar.productions(lhs=nextsym): + # If the left corner in the predicted production is + # leaf, it must match with the input. + if prod.rhs(): + first = prod.rhs()[0] + if is_terminal(first): + if index >= chart.num_leaves() or first != chart.leaf(index): + continue + + new_edge = TreeEdge.from_production(prod, index) + if chart.insert(new_edge, ()): + yield new_edge + + # Record the fact that we've applied this rule. + self._done[nextsym, index] = (chart, grammar) + + +# //////////////////////////////////////////////////////////// +# Bottom-Up Prediction +# //////////////////////////////////////////////////////////// + + +class BottomUpPredictRule(AbstractChartRule): + r""" + A rule licensing any edge corresponding to a production whose + right-hand side begins with a complete edge's left-hand side. In + particular, this rule specifies that ``[A -> alpha \*]`` licenses + the edge ``[B -> \* A beta]`` for each grammar production ``B -> A beta``. + """ + + NUM_EDGES = 1 + + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + for prod in grammar.productions(rhs=edge.lhs()): + new_edge = TreeEdge.from_production(prod, edge.start()) + if chart.insert(new_edge, ()): + yield new_edge + + +class BottomUpPredictCombineRule(BottomUpPredictRule): + r""" + A rule licensing any edge corresponding to a production whose + right-hand side begins with a complete edge's left-hand side. In + particular, this rule specifies that ``[A -> alpha \*]`` + licenses the edge ``[B -> A \* beta]`` for each grammar + production ``B -> A beta``. + + :note: This is like ``BottomUpPredictRule``, but it also applies + the ``FundamentalRule`` to the resulting edge. + """ + + NUM_EDGES = 1 + + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + for prod in grammar.productions(rhs=edge.lhs()): + new_edge = TreeEdge(edge.span(), prod.lhs(), prod.rhs(), 1) + if chart.insert(new_edge, (edge,)): + yield new_edge + + +class EmptyPredictRule(AbstractChartRule): + """ + A rule that inserts all empty productions as passive edges, + in every position in the chart. + """ + + NUM_EDGES = 0 + + def apply(self, chart, grammar): + for prod in grammar.productions(empty=True): + for index in range(chart.num_leaves() + 1): + new_edge = TreeEdge.from_production(prod, index) + if chart.insert(new_edge, ()): + yield new_edge + + +######################################################################## +## Filtered Bottom Up +######################################################################## + + +class FilteredSingleEdgeFundamentalRule(SingleEdgeFundamentalRule): + def _apply_complete(self, chart, grammar, right_edge): + end = right_edge.end() + nexttoken = end < chart.num_leaves() and chart.leaf(end) + for left_edge in chart.select( + end=right_edge.start(), is_complete=False, nextsym=right_edge.lhs() + ): + if _bottomup_filter(grammar, nexttoken, left_edge.rhs(), left_edge.dot()): + new_edge = left_edge.move_dot_forward(right_edge.end()) + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + def _apply_incomplete(self, chart, grammar, left_edge): + for right_edge in chart.select( + start=left_edge.end(), is_complete=True, lhs=left_edge.nextsym() + ): + end = right_edge.end() + nexttoken = end < chart.num_leaves() and chart.leaf(end) + if _bottomup_filter(grammar, nexttoken, left_edge.rhs(), left_edge.dot()): + new_edge = left_edge.move_dot_forward(right_edge.end()) + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + +class FilteredBottomUpPredictCombineRule(BottomUpPredictCombineRule): + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + + end = edge.end() + nexttoken = end < chart.num_leaves() and chart.leaf(end) + for prod in grammar.productions(rhs=edge.lhs()): + if _bottomup_filter(grammar, nexttoken, prod.rhs()): + new_edge = TreeEdge(edge.span(), prod.lhs(), prod.rhs(), 1) + if chart.insert(new_edge, (edge,)): + yield new_edge + + +def _bottomup_filter(grammar, nexttoken, rhs, dot=0): + if len(rhs) <= dot + 1: + return True + _next = rhs[dot + 1] + if is_terminal(_next): + return nexttoken == _next + else: + return grammar.is_leftcorner(_next, nexttoken) + + +######################################################################## +## Generic Chart Parser +######################################################################## + +TD_STRATEGY = [ + LeafInitRule(), + TopDownInitRule(), + CachedTopDownPredictRule(), + SingleEdgeFundamentalRule(), +] +BU_STRATEGY = [ + LeafInitRule(), + EmptyPredictRule(), + BottomUpPredictRule(), + SingleEdgeFundamentalRule(), +] +BU_LC_STRATEGY = [ + LeafInitRule(), + EmptyPredictRule(), + BottomUpPredictCombineRule(), + SingleEdgeFundamentalRule(), +] + +LC_STRATEGY = [ + LeafInitRule(), + FilteredBottomUpPredictCombineRule(), + FilteredSingleEdgeFundamentalRule(), +] + + +class ChartParser(ParserI): + """ + A generic chart parser. A "strategy", or list of + ``ChartRuleI`` instances, is used to decide what edges to add to + the chart. In particular, ``ChartParser`` uses the following + algorithm to parse texts: + + | Until no new edges are added: + | For each *rule* in *strategy*: + | Apply *rule* to any applicable edges in the chart. + | Return any complete parses in the chart + """ + + def __init__( + self, + grammar, + strategy=BU_LC_STRATEGY, + trace=0, + trace_chart_width=50, + use_agenda=True, + chart_class=Chart, + ): + """ + Create a new chart parser, that uses ``grammar`` to parse + texts. + + :type grammar: CFG + :param grammar: The grammar used to parse texts. + :type strategy: list(ChartRuleI) + :param strategy: A list of rules that should be used to decide + what edges to add to the chart (top-down strategy by default). + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + :type trace_chart_width: int + :param trace_chart_width: The default total width reserved for + the chart in trace output. The remainder of each line will + be used to display edges. + :type use_agenda: bool + :param use_agenda: Use an optimized agenda-based algorithm, + if possible. + :param chart_class: The class that should be used to create + the parse charts. + """ + self._grammar = grammar + self._strategy = strategy + self._trace = trace + self._trace_chart_width = trace_chart_width + # If the strategy only consists of axioms (NUM_EDGES==0) and + # inference rules (NUM_EDGES==1), we can use an agenda-based algorithm: + self._use_agenda = use_agenda + self._chart_class = chart_class + + self._axioms = [] + self._inference_rules = [] + for rule in strategy: + if rule.NUM_EDGES == 0: + self._axioms.append(rule) + elif rule.NUM_EDGES == 1: + self._inference_rules.append(rule) + else: + self._use_agenda = False + + def grammar(self): + return self._grammar + + def _trace_new_edges(self, chart, rule, new_edges, trace, edge_width): + if not trace: + return + print_rule_header = trace > 1 + for edge in new_edges: + if print_rule_header: + print("%s:" % rule) + print_rule_header = False + print(chart.pretty_format_edge(edge, edge_width)) + + def chart_parse(self, tokens, trace=None): + """ + Return the final parse ``Chart`` from which all possible + parse trees can be extracted. + + :param tokens: The sentence to be parsed + :type tokens: list(str) + :rtype: Chart + """ + if trace is None: + trace = self._trace + trace_new_edges = self._trace_new_edges + + tokens = list(tokens) + self._grammar.check_coverage(tokens) + chart = self._chart_class(tokens) + grammar = self._grammar + + # Width, for printing trace edges. + trace_edge_width = self._trace_chart_width // (chart.num_leaves() + 1) + if trace: + print(chart.pretty_format_leaves(trace_edge_width)) + + if self._use_agenda: + # Use an agenda-based algorithm. + for axiom in self._axioms: + new_edges = list(axiom.apply(chart, grammar)) + trace_new_edges(chart, axiom, new_edges, trace, trace_edge_width) + + inference_rules = self._inference_rules + agenda = chart.edges() + # We reverse the initial agenda, since it is a stack + # but chart.edges() functions as a queue. + agenda.reverse() + while agenda: + edge = agenda.pop() + for rule in inference_rules: + new_edges = list(rule.apply(chart, grammar, edge)) + if trace: + trace_new_edges(chart, rule, new_edges, trace, trace_edge_width) + agenda += new_edges + + else: + # Do not use an agenda-based algorithm. + edges_added = True + while edges_added: + edges_added = False + for rule in self._strategy: + new_edges = list(rule.apply_everywhere(chart, grammar)) + edges_added = len(new_edges) + trace_new_edges(chart, rule, new_edges, trace, trace_edge_width) + + # Return the final chart. + return chart + + def parse(self, tokens, tree_class=Tree): + chart = self.chart_parse(tokens) + return iter(chart.parses(self._grammar.start(), tree_class=tree_class)) + + +class TopDownChartParser(ChartParser): + """ + A ``ChartParser`` using a top-down parsing strategy. + See ``ChartParser`` for more information. + """ + + def __init__(self, grammar, **parser_args): + ChartParser.__init__(self, grammar, TD_STRATEGY, **parser_args) + + +class BottomUpChartParser(ChartParser): + """ + A ``ChartParser`` using a bottom-up parsing strategy. + See ``ChartParser`` for more information. + """ + + def __init__(self, grammar, **parser_args): + if isinstance(grammar, PCFG): + warnings.warn( + "BottomUpChartParser only works for CFG, " + "use BottomUpProbabilisticChartParser instead", + category=DeprecationWarning, + ) + ChartParser.__init__(self, grammar, BU_STRATEGY, **parser_args) + + +class BottomUpLeftCornerChartParser(ChartParser): + """ + A ``ChartParser`` using a bottom-up left-corner parsing strategy. + This strategy is often more efficient than standard bottom-up. + See ``ChartParser`` for more information. + """ + + def __init__(self, grammar, **parser_args): + ChartParser.__init__(self, grammar, BU_LC_STRATEGY, **parser_args) + + +class LeftCornerChartParser(ChartParser): + def __init__(self, grammar, **parser_args): + if not grammar.is_nonempty(): + raise ValueError( + "LeftCornerParser only works for grammars " "without empty productions." + ) + ChartParser.__init__(self, grammar, LC_STRATEGY, **parser_args) + + +######################################################################## +## Stepping Chart Parser +######################################################################## + + +class SteppingChartParser(ChartParser): + """ + A ``ChartParser`` that allows you to step through the parsing + process, adding a single edge at a time. It also allows you to + change the parser's strategy or grammar midway through parsing a + text. + + The ``initialize`` method is used to start parsing a text. ``step`` + adds a single edge to the chart. ``set_strategy`` changes the + strategy used by the chart parser. ``parses`` returns the set of + parses that has been found by the chart parser. + + :ivar _restart: Records whether the parser's strategy, grammar, + or chart has been changed. If so, then ``step`` must restart + the parsing algorithm. + """ + + def __init__(self, grammar, strategy=[], trace=0): + self._chart = None + self._current_chartrule = None + self._restart = False + ChartParser.__init__(self, grammar, strategy, trace) + + # //////////////////////////////////////////////////////////// + # Initialization + # //////////////////////////////////////////////////////////// + + def initialize(self, tokens): + "Begin parsing the given tokens." + self._chart = Chart(list(tokens)) + self._restart = True + + # //////////////////////////////////////////////////////////// + # Stepping + # //////////////////////////////////////////////////////////// + + def step(self): + """ + Return a generator that adds edges to the chart, one at a + time. Each time the generator is resumed, it adds a single + edge and yields that edge. If no more edges can be added, + then it yields None. + + If the parser's strategy, grammar, or chart is changed, then + the generator will continue adding edges using the new + strategy, grammar, or chart. + + Note that this generator never terminates, since the grammar + or strategy might be changed to values that would add new + edges. Instead, it yields None when no more edges can be + added with the current strategy and grammar. + """ + if self._chart is None: + raise ValueError("Parser must be initialized first") + while True: + self._restart = False + w = 50 // (self._chart.num_leaves() + 1) + + for e in self._parse(): + if self._trace > 1: + print(self._current_chartrule) + if self._trace > 0: + print(self._chart.pretty_format_edge(e, w)) + yield e + if self._restart: + break + else: + yield None # No more edges. + + def _parse(self): + """ + A generator that implements the actual parsing algorithm. + ``step`` iterates through this generator, and restarts it + whenever the parser's strategy, grammar, or chart is modified. + """ + chart = self._chart + grammar = self._grammar + edges_added = 1 + while edges_added > 0: + edges_added = 0 + for rule in self._strategy: + self._current_chartrule = rule + for e in rule.apply_everywhere(chart, grammar): + edges_added += 1 + yield e + + # //////////////////////////////////////////////////////////// + # Accessors + # //////////////////////////////////////////////////////////// + + def strategy(self): + "Return the strategy used by this parser." + return self._strategy + + def grammar(self): + "Return the grammar used by this parser." + return self._grammar + + def chart(self): + "Return the chart that is used by this parser." + return self._chart + + def current_chartrule(self): + "Return the chart rule used to generate the most recent edge." + return self._current_chartrule + + def parses(self, tree_class=Tree): + "Return the parse trees currently contained in the chart." + return self._chart.parses(self._grammar.start(), tree_class) + + # //////////////////////////////////////////////////////////// + # Parser modification + # //////////////////////////////////////////////////////////// + + def set_strategy(self, strategy): + """ + Change the strategy that the parser uses to decide which edges + to add to the chart. + + :type strategy: list(ChartRuleI) + :param strategy: A list of rules that should be used to decide + what edges to add to the chart. + """ + if strategy == self._strategy: + return + self._strategy = strategy[:] # Make a copy. + self._restart = True + + def set_grammar(self, grammar): + "Change the grammar used by the parser." + if grammar is self._grammar: + return + self._grammar = grammar + self._restart = True + + def set_chart(self, chart): + "Load a given chart into the chart parser." + if chart is self._chart: + return + self._chart = chart + self._restart = True + + # //////////////////////////////////////////////////////////// + # Standard parser methods + # //////////////////////////////////////////////////////////// + + def parse(self, tokens, tree_class=Tree): + tokens = list(tokens) + self._grammar.check_coverage(tokens) + + # Initialize ourselves. + self.initialize(tokens) + + # Step until no more edges are generated. + for e in self.step(): + if e is None: + break + + # Return an iterator of complete parses. + return self.parses(tree_class=tree_class) + + +######################################################################## +## Demo Code +######################################################################## + + +def demo_grammar(): + from nltk.grammar import CFG + + return CFG.fromstring( + """ +S -> NP VP +PP -> "with" NP +NP -> NP PP +VP -> VP PP +VP -> Verb NP +VP -> Verb +NP -> Det Noun +NP -> "John" +NP -> "I" +Det -> "the" +Det -> "my" +Det -> "a" +Noun -> "dog" +Noun -> "cookie" +Verb -> "ate" +Verb -> "saw" +Prep -> "with" +Prep -> "under" +""" + ) + + +def demo( + choice=None, + print_times=True, + print_grammar=False, + print_trees=True, + trace=2, + sent="I saw John with a dog with my cookie", + numparses=5, +): + """ + A demonstration of the chart parsers. + """ + import sys + import time + + from nltk import CFG, Production, nonterminals + + # The grammar for ChartParser and SteppingChartParser: + grammar = demo_grammar() + if print_grammar: + print("* Grammar") + print(grammar) + + # Tokenize the sample sentence. + print("* Sentence:") + print(sent) + tokens = sent.split() + print(tokens) + print() + + # Ask the user which parser to test, + # if the parser wasn't provided as an argument + if choice is None: + print(" 1: Top-down chart parser") + print(" 2: Bottom-up chart parser") + print(" 3: Bottom-up left-corner chart parser") + print(" 4: Left-corner chart parser with bottom-up filter") + print(" 5: Stepping chart parser (alternating top-down & bottom-up)") + print(" 6: All parsers") + print("\nWhich parser (1-6)? ", end=" ") + choice = sys.stdin.readline().strip() + print() + + choice = str(choice) + if choice not in "123456": + print("Bad parser number") + return + + # Keep track of how long each parser takes. + times = {} + + strategies = { + "1": ("Top-down", TD_STRATEGY), + "2": ("Bottom-up", BU_STRATEGY), + "3": ("Bottom-up left-corner", BU_LC_STRATEGY), + "4": ("Filtered left-corner", LC_STRATEGY), + } + choices = [] + if choice in strategies: + choices = [choice] + if choice == "6": + choices = "1234" + + # Run the requested chart parser(s), except the stepping parser. + for strategy in choices: + print("* Strategy: " + strategies[strategy][0]) + print() + cp = ChartParser(grammar, strategies[strategy][1], trace=trace) + t = time.time() + chart = cp.chart_parse(tokens) + parses = list(chart.parses(grammar.start())) + + times[strategies[strategy][0]] = time.time() - t + print("Nr edges in chart:", len(chart.edges())) + if numparses: + assert len(parses) == numparses, "Not all parses found" + if print_trees: + for tree in parses: + print(tree) + else: + print("Nr trees:", len(parses)) + print() + + # Run the stepping parser, if requested. + if choice in "56": + print("* Strategy: Stepping (top-down vs bottom-up)") + print() + t = time.time() + cp = SteppingChartParser(grammar, trace=trace) + cp.initialize(tokens) + for i in range(5): + print("*** SWITCH TO TOP DOWN") + cp.set_strategy(TD_STRATEGY) + for j, e in enumerate(cp.step()): + if j > 20 or e is None: + break + print("*** SWITCH TO BOTTOM UP") + cp.set_strategy(BU_STRATEGY) + for j, e in enumerate(cp.step()): + if j > 20 or e is None: + break + times["Stepping"] = time.time() - t + print("Nr edges in chart:", len(cp.chart().edges())) + if numparses: + assert len(list(cp.parses())) == numparses, "Not all parses found" + if print_trees: + for tree in cp.parses(): + print(tree) + else: + print("Nr trees:", len(list(cp.parses()))) + print() + + # Print the times of all parsers: + if not (print_times and times): + return + print("* Parsing times") + print() + maxlen = max(len(key) for key in times) + format = "%" + repr(maxlen) + "s parser: %6.3fsec" + times_items = times.items() + for (parser, t) in sorted(times_items, key=lambda a: a[1]): + print(format % (parser, t)) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/corenlp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/corenlp.py new file mode 100644 index 0000000..06cd603 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/corenlp.py @@ -0,0 +1,800 @@ +# Natural Language Toolkit: Interface to the CoreNLP REST API. +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Dmitrijs Milajevs +# +# URL: +# For license information, see LICENSE.TXT + +import json +import os # required for doctests +import re +import socket +import time +from typing import List, Tuple + +from nltk.internals import _java_options, config_java, find_jar_iter, java +from nltk.parse.api import ParserI +from nltk.parse.dependencygraph import DependencyGraph +from nltk.tag.api import TaggerI +from nltk.tokenize.api import TokenizerI +from nltk.tree import Tree + +_stanford_url = "https://stanfordnlp.github.io/CoreNLP/" + + +class CoreNLPServerError(EnvironmentError): + """Exceptions associated with the Core NLP server.""" + + +def try_port(port=0): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(("", port)) + + p = sock.getsockname()[1] + sock.close() + + return p + + +class CoreNLPServer: + + _MODEL_JAR_PATTERN = r"stanford-corenlp-(\d+)\.(\d+)\.(\d+)-models\.jar" + _JAR = r"stanford-corenlp-(\d+)\.(\d+)\.(\d+)\.jar" + + def __init__( + self, + path_to_jar=None, + path_to_models_jar=None, + verbose=False, + java_options=None, + corenlp_options=None, + port=None, + ): + + if corenlp_options is None: + corenlp_options = ["-preload", "tokenize,ssplit,pos,lemma,parse,depparse"] + + jars = list( + find_jar_iter( + self._JAR, + path_to_jar, + env_vars=("CORENLP",), + searchpath=(), + url=_stanford_url, + verbose=verbose, + is_regex=True, + ) + ) + + # find the most recent code and model jar + stanford_jar = max(jars, key=lambda model_name: re.match(self._JAR, model_name)) + + if port is None: + try: + port = try_port(9000) + except OSError: + port = try_port() + corenlp_options.extend(["-port", str(port)]) + else: + try_port(port) + corenlp_options.extend(["-port", str(port)]) + + self.url = f"http://localhost:{port}" + + model_jar = max( + find_jar_iter( + self._MODEL_JAR_PATTERN, + path_to_models_jar, + env_vars=("CORENLP_MODELS",), + searchpath=(), + url=_stanford_url, + verbose=verbose, + is_regex=True, + ), + key=lambda model_name: re.match(self._MODEL_JAR_PATTERN, model_name), + ) + + self.verbose = verbose + + self._classpath = stanford_jar, model_jar + + self.corenlp_options = corenlp_options + self.java_options = java_options or ["-mx2g"] + + def start(self, stdout="devnull", stderr="devnull"): + """Starts the CoreNLP server + + :param stdout, stderr: Specifies where CoreNLP output is redirected. Valid values are 'devnull', 'stdout', 'pipe' + """ + import requests + + cmd = ["edu.stanford.nlp.pipeline.StanfordCoreNLPServer"] + + if self.corenlp_options: + cmd.extend(self.corenlp_options) + + # Configure java. + default_options = " ".join(_java_options) + config_java(options=self.java_options, verbose=self.verbose) + + try: + self.popen = java( + cmd, + classpath=self._classpath, + blocking=False, + stdout=stdout, + stderr=stderr, + ) + finally: + # Return java configurations to their default values. + config_java(options=default_options, verbose=self.verbose) + + # Check that the server is istill running. + returncode = self.popen.poll() + if returncode is not None: + _, stderrdata = self.popen.communicate() + raise CoreNLPServerError( + returncode, + "Could not start the server. " + "The error was: {}".format(stderrdata.decode("ascii")), + ) + + for i in range(30): + try: + response = requests.get(requests.compat.urljoin(self.url, "live")) + except requests.exceptions.ConnectionError: + time.sleep(1) + else: + if response.ok: + break + else: + raise CoreNLPServerError("Could not connect to the server.") + + for i in range(60): + try: + response = requests.get(requests.compat.urljoin(self.url, "ready")) + except requests.exceptions.ConnectionError: + time.sleep(1) + else: + if response.ok: + break + else: + raise CoreNLPServerError("The server is not ready.") + + def stop(self): + self.popen.terminate() + self.popen.wait() + + def __enter__(self): + self.start() + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + return False + + +class GenericCoreNLPParser(ParserI, TokenizerI, TaggerI): + """Interface to the CoreNLP Parser.""" + + def __init__( + self, + url="http://localhost:9000", + encoding="utf8", + tagtype=None, + strict_json=True, + ): + import requests + + self.url = url + self.encoding = encoding + + if tagtype not in ["pos", "ner", None]: + raise ValueError("tagtype must be either 'pos', 'ner' or None") + + self.tagtype = tagtype + self.strict_json = strict_json + + self.session = requests.Session() + + def parse_sents(self, sentences, *args, **kwargs): + """Parse multiple sentences. + + Takes multiple sentences as a list where each sentence is a list of + words. Each sentence will be automatically tagged with this + CoreNLPParser instance's tagger. + + If a whitespace exists inside a token, then the token will be treated as + several tokens. + + :param sentences: Input sentences to parse + :type sentences: list(list(str)) + :rtype: iter(iter(Tree)) + """ + # Converting list(list(str)) -> list(str) + sentences = (" ".join(words) for words in sentences) + return self.raw_parse_sents(sentences, *args, **kwargs) + + def raw_parse(self, sentence, properties=None, *args, **kwargs): + """Parse a sentence. + + Takes a sentence as a string; before parsing, it will be automatically + tokenized and tagged by the CoreNLP Parser. + + :param sentence: Input sentence to parse + :type sentence: str + :rtype: iter(Tree) + """ + default_properties = {"tokenize.whitespace": "false"} + default_properties.update(properties or {}) + + return next( + self.raw_parse_sents( + [sentence], properties=default_properties, *args, **kwargs + ) + ) + + def api_call(self, data, properties=None, timeout=60): + default_properties = { + "outputFormat": "json", + "annotators": "tokenize,pos,lemma,ssplit,{parser_annotator}".format( + parser_annotator=self.parser_annotator + ), + } + + default_properties.update(properties or {}) + + response = self.session.post( + self.url, + params={"properties": json.dumps(default_properties)}, + data=data.encode(self.encoding), + headers={"Content-Type": f"text/plain; charset={self.encoding}"}, + timeout=timeout, + ) + + response.raise_for_status() + + return response.json(strict=self.strict_json) + + def raw_parse_sents( + self, sentences, verbose=False, properties=None, *args, **kwargs + ): + """Parse multiple sentences. + + Takes multiple sentences as a list of strings. Each sentence will be + automatically tokenized and tagged. + + :param sentences: Input sentences to parse. + :type sentences: list(str) + :rtype: iter(iter(Tree)) + + """ + default_properties = { + # Only splits on '\n', never inside the sentence. + "ssplit.eolonly": "true" + } + + default_properties.update(properties or {}) + + """ + for sentence in sentences: + parsed_data = self.api_call(sentence, properties=default_properties) + + assert len(parsed_data['sentences']) == 1 + + for parse in parsed_data['sentences']: + tree = self.make_tree(parse) + yield iter([tree]) + """ + parsed_data = self.api_call("\n".join(sentences), properties=default_properties) + for parsed_sent in parsed_data["sentences"]: + tree = self.make_tree(parsed_sent) + yield iter([tree]) + + def parse_text(self, text, *args, **kwargs): + """Parse a piece of text. + + The text might contain several sentences which will be split by CoreNLP. + + :param str text: text to be split. + :returns: an iterable of syntactic structures. # TODO: should it be an iterable of iterables? + + """ + parsed_data = self.api_call(text, *args, **kwargs) + + for parse in parsed_data["sentences"]: + yield self.make_tree(parse) + + def tokenize(self, text, properties=None): + """Tokenize a string of text. + + Skip these tests if CoreNLP is likely not ready. + >>> from nltk.test.setup_fixt import check_jar + >>> check_jar(CoreNLPServer._JAR, env_vars=("CORENLP",), is_regex=True) + + The CoreNLP server can be started using the following notation, although + we recommend the `with CoreNLPServer() as server:` context manager notation + to ensure that the server is always stopped. + >>> server = CoreNLPServer() + >>> server.start() + >>> parser = CoreNLPParser(url=server.url) + + >>> text = 'Good muffins cost $3.88\\nin New York. Please buy me\\ntwo of them.\\nThanks.' + >>> list(parser.tokenize(text)) + ['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.', 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + + >>> s = "The colour of the wall is blue." + >>> list( + ... parser.tokenize( + ... 'The colour of the wall is blue.', + ... properties={'tokenize.options': 'americanize=true'}, + ... ) + ... ) + ['The', 'colour', 'of', 'the', 'wall', 'is', 'blue', '.'] + >>> server.stop() + + """ + default_properties = {"annotators": "tokenize,ssplit"} + + default_properties.update(properties or {}) + + result = self.api_call(text, properties=default_properties) + + for sentence in result["sentences"]: + for token in sentence["tokens"]: + yield token["originalText"] or token["word"] + + def tag_sents(self, sentences): + """ + Tag multiple sentences. + + Takes multiple sentences as a list where each sentence is a list of + tokens. + + :param sentences: Input sentences to tag + :type sentences: list(list(str)) + :rtype: list(list(tuple(str, str)) + """ + # Converting list(list(str)) -> list(str) + sentences = (" ".join(words) for words in sentences) + return [sentences[0] for sentences in self.raw_tag_sents(sentences)] + + def tag(self, sentence: str) -> List[Tuple[str, str]]: + """ + Tag a list of tokens. + + :rtype: list(tuple(str, str)) + + Skip these tests if CoreNLP is likely not ready. + >>> from nltk.test.setup_fixt import check_jar + >>> check_jar(CoreNLPServer._JAR, env_vars=("CORENLP",), is_regex=True) + + The CoreNLP server can be started using the following notation, although + we recommend the `with CoreNLPServer() as server:` context manager notation + to ensure that the server is always stopped. + >>> server = CoreNLPServer() + >>> server.start() + >>> parser = CoreNLPParser(url=server.url, tagtype='ner') + >>> tokens = 'Rami Eid is studying at Stony Brook University in NY'.split() + >>> parser.tag(tokens) # doctest: +NORMALIZE_WHITESPACE + [('Rami', 'PERSON'), ('Eid', 'PERSON'), ('is', 'O'), ('studying', 'O'), ('at', 'O'), ('Stony', 'ORGANIZATION'), + ('Brook', 'ORGANIZATION'), ('University', 'ORGANIZATION'), ('in', 'O'), ('NY', 'STATE_OR_PROVINCE')] + + >>> parser = CoreNLPParser(url=server.url, tagtype='pos') + >>> tokens = "What is the airspeed of an unladen swallow ?".split() + >>> parser.tag(tokens) # doctest: +NORMALIZE_WHITESPACE + [('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), + ('airspeed', 'NN'), ('of', 'IN'), ('an', 'DT'), + ('unladen', 'JJ'), ('swallow', 'VB'), ('?', '.')] + >>> server.stop() + """ + return self.tag_sents([sentence])[0] + + def raw_tag_sents(self, sentences): + """ + Tag multiple sentences. + + Takes multiple sentences as a list where each sentence is a string. + + :param sentences: Input sentences to tag + :type sentences: list(str) + :rtype: list(list(list(tuple(str, str))) + """ + default_properties = { + "ssplit.isOneSentence": "true", + "annotators": "tokenize,ssplit,", + } + + # Supports only 'pos' or 'ner' tags. + assert self.tagtype in ["pos", "ner"] + default_properties["annotators"] += self.tagtype + for sentence in sentences: + tagged_data = self.api_call(sentence, properties=default_properties) + yield [ + [ + (token["word"], token[self.tagtype]) + for token in tagged_sentence["tokens"] + ] + for tagged_sentence in tagged_data["sentences"] + ] + + +class CoreNLPParser(GenericCoreNLPParser): + """ + Skip these tests if CoreNLP is likely not ready. + >>> from nltk.test.setup_fixt import check_jar + >>> check_jar(CoreNLPServer._JAR, env_vars=("CORENLP",), is_regex=True) + + The recommended usage of `CoreNLPParser` is using the context manager notation: + >>> with CoreNLPServer() as server: + ... parser = CoreNLPParser(url=server.url) + ... next( + ... parser.raw_parse('The quick brown fox jumps over the lazy dog.') + ... ).pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _______________|__________________________ + | VP | + | _________|___ | + | | PP | + | | ________|___ | + NP | | NP | + ____|__________ | | _______|____ | + DT JJ JJ NN VBZ IN DT JJ NN . + | | | | | | | | | | + The quick brown fox jumps over the lazy dog . + + Alternatively, the server can be started using the following notation. + Note that `CoreNLPServer` does not need to be used if the CoreNLP server is started + outside of Python. + >>> server = CoreNLPServer() + >>> server.start() + >>> parser = CoreNLPParser(url=server.url) + + >>> (parse_fox, ), (parse_wolf, ) = parser.raw_parse_sents( + ... [ + ... 'The quick brown fox jumps over the lazy dog.', + ... 'The quick grey wolf jumps over the lazy fox.', + ... ] + ... ) + + >>> parse_fox.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _______________|__________________________ + | VP | + | _________|___ | + | | PP | + | | ________|___ | + NP | | NP | + ____|__________ | | _______|____ | + DT JJ JJ NN VBZ IN DT JJ NN . + | | | | | | | | | | + The quick brown fox jumps over the lazy dog . + + >>> parse_wolf.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _______________|__________________________ + | VP | + | _________|___ | + | | PP | + | | ________|___ | + NP | | NP | + ____|_________ | | _______|____ | + DT JJ JJ NN VBZ IN DT JJ NN . + | | | | | | | | | | + The quick grey wolf jumps over the lazy fox . + + >>> (parse_dog, ), (parse_friends, ) = parser.parse_sents( + ... [ + ... "I 'm a dog".split(), + ... "This is my friends ' cat ( the tabby )".split(), + ... ] + ... ) + + >>> parse_dog.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _______|____ + | VP + | ________|___ + NP | NP + | | ___|___ + PRP VBP DT NN + | | | | + I 'm a dog + + >>> parse_friends.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + ____|___________ + | VP + | ___________|_____________ + | | NP + | | _______|________________________ + | | NP | | | + | | _____|_______ | | | + NP | NP | | NP | + | | ______|_________ | | ___|____ | + DT VBZ PRP$ NNS POS NN -LRB- DT NN -RRB- + | | | | | | | | | | + This is my friends ' cat -LRB- the tabby -RRB- + + >>> parse_john, parse_mary, = parser.parse_text( + ... 'John loves Mary. Mary walks.' + ... ) + + >>> parse_john.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _____|_____________ + | VP | + | ____|___ | + NP | NP | + | | | | + NNP VBZ NNP . + | | | | + John loves Mary . + + >>> parse_mary.pretty_print() # doctest: +NORMALIZE_WHITESPACE + ROOT + | + S + _____|____ + NP VP | + | | | + NNP VBZ . + | | | + Mary walks . + + Special cases + + >>> next( + ... parser.raw_parse( + ... 'NASIRIYA, Iraq—Iraqi doctors who treated former prisoner of war ' + ... 'Jessica Lynch have angrily dismissed claims made in her biography ' + ... 'that she was raped by her Iraqi captors.' + ... ) + ... ).height() + 14 + + >>> next( + ... parser.raw_parse( + ... "The broader Standard & Poor's 500 Index <.SPX> was 0.46 points lower, or " + ... '0.05 percent, at 997.02.' + ... ) + ... ).height() + 11 + + >>> server.stop() + """ + + _OUTPUT_FORMAT = "penn" + parser_annotator = "parse" + + def make_tree(self, result): + return Tree.fromstring(result["parse"]) + + +class CoreNLPDependencyParser(GenericCoreNLPParser): + """Dependency parser. + + Skip these tests if CoreNLP is likely not ready. + >>> from nltk.test.setup_fixt import check_jar + >>> check_jar(CoreNLPServer._JAR, env_vars=("CORENLP",), is_regex=True) + + The recommended usage of `CoreNLPParser` is using the context manager notation: + >>> with CoreNLPServer() as server: + ... dep_parser = CoreNLPDependencyParser(url=server.url) + ... parse, = dep_parser.raw_parse( + ... 'The quick brown fox jumps over the lazy dog.' + ... ) + ... print(parse.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + The DT 4 det + quick JJ 4 amod + brown JJ 4 amod + fox NN 5 nsubj + jumps VBZ 0 ROOT + over IN 9 case + the DT 9 det + lazy JJ 9 amod + dog NN 5 obl + . . 5 punct + + Alternatively, the server can be started using the following notation. + Note that `CoreNLPServer` does not need to be used if the CoreNLP server is started + outside of Python. + >>> server = CoreNLPServer() + >>> server.start() + >>> dep_parser = CoreNLPDependencyParser(url=server.url) + >>> parse, = dep_parser.raw_parse('The quick brown fox jumps over the lazy dog.') + >>> print(parse.tree()) # doctest: +NORMALIZE_WHITESPACE + (jumps (fox The quick brown) (dog over the lazy) .) + + >>> for governor, dep, dependent in parse.triples(): + ... print(governor, dep, dependent) # doctest: +NORMALIZE_WHITESPACE + ('jumps', 'VBZ') nsubj ('fox', 'NN') + ('fox', 'NN') det ('The', 'DT') + ('fox', 'NN') amod ('quick', 'JJ') + ('fox', 'NN') amod ('brown', 'JJ') + ('jumps', 'VBZ') obl ('dog', 'NN') + ('dog', 'NN') case ('over', 'IN') + ('dog', 'NN') det ('the', 'DT') + ('dog', 'NN') amod ('lazy', 'JJ') + ('jumps', 'VBZ') punct ('.', '.') + + >>> (parse_fox, ), (parse_dog, ) = dep_parser.raw_parse_sents( + ... [ + ... 'The quick brown fox jumps over the lazy dog.', + ... 'The quick grey wolf jumps over the lazy fox.', + ... ] + ... ) + >>> print(parse_fox.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + The DT 4 det + quick JJ 4 amod + brown JJ 4 amod + fox NN 5 nsubj + jumps VBZ 0 ROOT + over IN 9 case + the DT 9 det + lazy JJ 9 amod + dog NN 5 obl + . . 5 punct + + >>> print(parse_dog.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + The DT 4 det + quick JJ 4 amod + grey JJ 4 amod + wolf NN 5 nsubj + jumps VBZ 0 ROOT + over IN 9 case + the DT 9 det + lazy JJ 9 amod + fox NN 5 obl + . . 5 punct + + >>> (parse_dog, ), (parse_friends, ) = dep_parser.parse_sents( + ... [ + ... "I 'm a dog".split(), + ... "This is my friends ' cat ( the tabby )".split(), + ... ] + ... ) + >>> print(parse_dog.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + I PRP 4 nsubj + 'm VBP 4 cop + a DT 4 det + dog NN 0 ROOT + + >>> print(parse_friends.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + This DT 6 nsubj + is VBZ 6 cop + my PRP$ 4 nmod:poss + friends NNS 6 nmod:poss + ' POS 4 case + cat NN 0 ROOT + ( -LRB- 9 punct + the DT 9 det + tabby NN 6 dep + ) -RRB- 9 punct + + >>> parse_john, parse_mary, = dep_parser.parse_text( + ... 'John loves Mary. Mary walks.' + ... ) + + >>> print(parse_john.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + John NNP 2 nsubj + loves VBZ 0 ROOT + Mary NNP 2 obj + . . 2 punct + + >>> print(parse_mary.to_conll(4)) # doctest: +NORMALIZE_WHITESPACE + Mary NNP 2 nsubj + walks VBZ 0 ROOT + . . 2 punct + + Special cases + + Non-breaking space inside of a token. + + >>> len( + ... next( + ... dep_parser.raw_parse( + ... 'Anhalt said children typically treat a 20-ounce soda bottle as one ' + ... 'serving, while it actually contains 2 1/2 servings.' + ... ) + ... ).nodes + ... ) + 23 + + Phone numbers. + + >>> len( + ... next( + ... dep_parser.raw_parse('This is not going to crash: 01 111 555.') + ... ).nodes + ... ) + 10 + + >>> print( + ... next( + ... dep_parser.raw_parse('The underscore _ should not simply disappear.') + ... ).to_conll(4) + ... ) # doctest: +NORMALIZE_WHITESPACE + The DT 2 det + underscore NN 7 nsubj + _ NFP 7 punct + should MD 7 aux + not RB 7 advmod + simply RB 7 advmod + disappear VB 0 ROOT + . . 7 punct + + >>> print( + ... next( + ... dep_parser.raw_parse( + ... 'for all of its insights into the dream world of teen life , and its electronic expression through ' + ... 'cyber culture , the film gives no quarter to anyone seeking to pull a cohesive story out of its 2 ' + ... '1/2-hour running time .' + ... ) + ... ).to_conll(4) + ... ) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + for IN 2 case + all DT 24 obl + of IN 5 case + its PRP$ 5 nmod:poss + insights NNS 2 nmod + into IN 9 case + the DT 9 det + dream NN 9 compound + world NN 5 nmod + of IN 12 case + teen NN 12 compound + ... + + >>> server.stop() + """ + + _OUTPUT_FORMAT = "conll2007" + parser_annotator = "depparse" + + def make_tree(self, result): + + return DependencyGraph( + ( + " ".join(n_items[1:]) # NLTK expects an iterable of strings... + for n_items in sorted(transform(result)) + ), + cell_separator=" ", # To make sure that a non-breaking space is kept inside of a token. + ) + + +def transform(sentence): + for dependency in sentence["basicDependencies"]: + + dependent_index = dependency["dependent"] + token = sentence["tokens"][dependent_index - 1] + + # Return values that we don't know as '_'. Also, consider tag and ctag + # to be equal. + yield ( + dependent_index, + "_", + token["word"], + token["lemma"], + token["pos"], + token["pos"], + "_", + str(dependency["governor"]), + dependency["dep"], + "_", + "_", + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/dependencygraph.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/dependencygraph.py new file mode 100644 index 0000000..242a10c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/dependencygraph.py @@ -0,0 +1,799 @@ +# Natural Language Toolkit: Dependency Grammars +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Jason Narad +# Steven Bird (modifications) +# +# URL: +# For license information, see LICENSE.TXT +# + +""" +Tools for reading and writing dependency trees. +The input is assumed to be in Malt-TAB format +(https://stp.lingfil.uu.se/~nivre/research/MaltXML.html). +""" + +import subprocess +import warnings +from collections import defaultdict +from itertools import chain +from pprint import pformat + +from nltk.internals import find_binary +from nltk.tree import Tree + +################################################################# +# DependencyGraph Class +################################################################# + + +class DependencyGraph: + """ + A container for the nodes and labelled edges of a dependency structure. + """ + + def __init__( + self, + tree_str=None, + cell_extractor=None, + zero_based=False, + cell_separator=None, + top_relation_label="ROOT", + ): + """Dependency graph. + + We place a dummy `TOP` node with the index 0, since the root node is + often assigned 0 as its head. This also means that the indexing of the + nodes corresponds directly to the Malt-TAB format, which starts at 1. + + If zero-based is True, then Malt-TAB-like input with node numbers + starting at 0 and the root node assigned -1 (as produced by, e.g., + zpar). + + :param str cell_separator: the cell separator. If not provided, cells + are split by whitespace. + + :param str top_relation_label: the label by which the top relation is + identified, for examlple, `ROOT`, `null` or `TOP`. + """ + self.nodes = defaultdict( + lambda: { + "address": None, + "word": None, + "lemma": None, + "ctag": None, + "tag": None, + "feats": None, + "head": None, + "deps": defaultdict(list), + "rel": None, + } + ) + + self.nodes[0].update({"ctag": "TOP", "tag": "TOP", "address": 0}) + + self.root = None + + if tree_str: + self._parse( + tree_str, + cell_extractor=cell_extractor, + zero_based=zero_based, + cell_separator=cell_separator, + top_relation_label=top_relation_label, + ) + + def remove_by_address(self, address): + """ + Removes the node with the given address. References + to this node in others will still exist. + """ + del self.nodes[address] + + def redirect_arcs(self, originals, redirect): + """ + Redirects arcs to any of the nodes in the originals list + to the redirect node address. + """ + for node in self.nodes.values(): + new_deps = [] + for dep in node["deps"]: + if dep in originals: + new_deps.append(redirect) + else: + new_deps.append(dep) + node["deps"] = new_deps + + def add_arc(self, head_address, mod_address): + """ + Adds an arc from the node specified by head_address to the + node specified by the mod address. + """ + relation = self.nodes[mod_address]["rel"] + self.nodes[head_address]["deps"].setdefault(relation, []) + self.nodes[head_address]["deps"][relation].append(mod_address) + # self.nodes[head_address]['deps'].append(mod_address) + + def connect_graph(self): + """ + Fully connects all non-root nodes. All nodes are set to be dependents + of the root node. + """ + for node1 in self.nodes.values(): + for node2 in self.nodes.values(): + if node1["address"] != node2["address"] and node2["rel"] != "TOP": + relation = node2["rel"] + node1["deps"].setdefault(relation, []) + node1["deps"][relation].append(node2["address"]) + # node1['deps'].append(node2['address']) + + def get_by_address(self, node_address): + """Return the node with the given address.""" + return self.nodes[node_address] + + def contains_address(self, node_address): + """ + Returns true if the graph contains a node with the given node + address, false otherwise. + """ + return node_address in self.nodes + + def to_dot(self): + """Return a dot representation suitable for using with Graphviz. + + >>> dg = DependencyGraph( + ... 'John N 2\\n' + ... 'loves V 0\\n' + ... 'Mary N 2' + ... ) + >>> print(dg.to_dot()) + digraph G{ + edge [dir=forward] + node [shape=plaintext] + + 0 [label="0 (None)"] + 0 -> 2 [label="ROOT"] + 1 [label="1 (John)"] + 2 [label="2 (loves)"] + 2 -> 1 [label=""] + 2 -> 3 [label=""] + 3 [label="3 (Mary)"] + } + + """ + # Start the digraph specification + s = "digraph G{\n" + s += "edge [dir=forward]\n" + s += "node [shape=plaintext]\n" + + # Draw the remaining nodes + for node in sorted(self.nodes.values(), key=lambda v: v["address"]): + s += '\n{} [label="{} ({})"]'.format( + node["address"], + node["address"], + node["word"], + ) + for rel, deps in node["deps"].items(): + for dep in deps: + if rel is not None: + s += '\n{} -> {} [label="{}"]'.format(node["address"], dep, rel) + else: + s += "\n{} -> {} ".format(node["address"], dep) + s += "\n}" + + return s + + def _repr_svg_(self): + """Show SVG representation of the transducer (IPython magic). + >>> from nltk.test.setup_fixt import check_binary + >>> check_binary('dot') + >>> dg = DependencyGraph( + ... 'John N 2\\n' + ... 'loves V 0\\n' + ... 'Mary N 2' + ... ) + >>> dg._repr_svg_().split('\\n')[0] + '' + + """ + dot_string = self.to_dot() + return dot2img(dot_string) + + def __str__(self): + return pformat(self.nodes) + + def __repr__(self): + return f"" + + @staticmethod + def load( + filename, zero_based=False, cell_separator=None, top_relation_label="ROOT" + ): + """ + :param filename: a name of a file in Malt-TAB format + :param zero_based: nodes in the input file are numbered starting from 0 + rather than 1 (as produced by, e.g., zpar) + :param str cell_separator: the cell separator. If not provided, cells + are split by whitespace. + :param str top_relation_label: the label by which the top relation is + identified, for examlple, `ROOT`, `null` or `TOP`. + + :return: a list of DependencyGraphs + + """ + with open(filename) as infile: + return [ + DependencyGraph( + tree_str, + zero_based=zero_based, + cell_separator=cell_separator, + top_relation_label=top_relation_label, + ) + for tree_str in infile.read().split("\n\n") + ] + + def left_children(self, node_index): + """ + Returns the number of left children under the node specified + by the given address. + """ + children = chain.from_iterable(self.nodes[node_index]["deps"].values()) + index = self.nodes[node_index]["address"] + return sum(1 for c in children if c < index) + + def right_children(self, node_index): + """ + Returns the number of right children under the node specified + by the given address. + """ + children = chain.from_iterable(self.nodes[node_index]["deps"].values()) + index = self.nodes[node_index]["address"] + return sum(1 for c in children if c > index) + + def add_node(self, node): + if not self.contains_address(node["address"]): + self.nodes[node["address"]].update(node) + + def _parse( + self, + input_, + cell_extractor=None, + zero_based=False, + cell_separator=None, + top_relation_label="ROOT", + ): + """Parse a sentence. + + :param extractor: a function that given a tuple of cells returns a + 7-tuple, where the values are ``word, lemma, ctag, tag, feats, head, + rel``. + + :param str cell_separator: the cell separator. If not provided, cells + are split by whitespace. + + :param str top_relation_label: the label by which the top relation is + identified, for examlple, `ROOT`, `null` or `TOP`. + + """ + + def extract_3_cells(cells, index): + word, tag, head = cells + return index, word, word, tag, tag, "", head, "" + + def extract_4_cells(cells, index): + word, tag, head, rel = cells + return index, word, word, tag, tag, "", head, rel + + def extract_7_cells(cells, index): + line_index, word, lemma, tag, _, head, rel = cells + try: + index = int(line_index) + except ValueError: + # index can't be parsed as an integer, use default + pass + return index, word, lemma, tag, tag, "", head, rel + + def extract_10_cells(cells, index): + line_index, word, lemma, ctag, tag, feats, head, rel, _, _ = cells + try: + index = int(line_index) + except ValueError: + # index can't be parsed as an integer, use default + pass + return index, word, lemma, ctag, tag, feats, head, rel + + extractors = { + 3: extract_3_cells, + 4: extract_4_cells, + 7: extract_7_cells, + 10: extract_10_cells, + } + + if isinstance(input_, str): + input_ = (line for line in input_.split("\n")) + + lines = (l.rstrip() for l in input_) + lines = (l for l in lines if l) + + cell_number = None + for index, line in enumerate(lines, start=1): + cells = line.split(cell_separator) + if cell_number is None: + cell_number = len(cells) + else: + assert cell_number == len(cells) + + if cell_extractor is None: + try: + cell_extractor = extractors[cell_number] + except KeyError as e: + raise ValueError( + "Number of tab-delimited fields ({}) not supported by " + "CoNLL(10) or Malt-Tab(4) format".format(cell_number) + ) from e + + try: + index, word, lemma, ctag, tag, feats, head, rel = cell_extractor( + cells, index + ) + except (TypeError, ValueError): + # cell_extractor doesn't take 2 arguments or doesn't return 8 + # values; assume the cell_extractor is an older external + # extractor and doesn't accept or return an index. + word, lemma, ctag, tag, feats, head, rel = cell_extractor(cells) + + if head == "_": + continue + + head = int(head) + if zero_based: + head += 1 + + self.nodes[index].update( + { + "address": index, + "word": word, + "lemma": lemma, + "ctag": ctag, + "tag": tag, + "feats": feats, + "head": head, + "rel": rel, + } + ) + + # Make sure that the fake root node has labeled dependencies. + if (cell_number == 3) and (head == 0): + rel = top_relation_label + self.nodes[head]["deps"][rel].append(index) + + if self.nodes[0]["deps"][top_relation_label]: + root_address = self.nodes[0]["deps"][top_relation_label][0] + self.root = self.nodes[root_address] + self.top_relation_label = top_relation_label + else: + warnings.warn( + "The graph doesn't contain a node " "that depends on the root element." + ) + + def _word(self, node, filter=True): + w = node["word"] + if filter: + if w != ",": + return w + return w + + def _tree(self, i): + """Turn dependency graphs into NLTK trees. + + :param int i: index of a node + :return: either a word (if the indexed node is a leaf) or a ``Tree``. + """ + node = self.get_by_address(i) + word = node["word"] + deps = sorted(chain.from_iterable(node["deps"].values())) + + if deps: + return Tree(word, [self._tree(dep) for dep in deps]) + else: + return word + + def tree(self): + """ + Starting with the ``root`` node, build a dependency tree using the NLTK + ``Tree`` constructor. Dependency labels are omitted. + """ + node = self.root + + word = node["word"] + deps = sorted(chain.from_iterable(node["deps"].values())) + return Tree(word, [self._tree(dep) for dep in deps]) + + def triples(self, node=None): + """ + Extract dependency triples of the form: + ((head word, head tag), rel, (dep word, dep tag)) + """ + + if not node: + node = self.root + + head = (node["word"], node["ctag"]) + for i in sorted(chain.from_iterable(node["deps"].values())): + dep = self.get_by_address(i) + yield (head, dep["rel"], (dep["word"], dep["ctag"])) + yield from self.triples(node=dep) + + def _hd(self, i): + try: + return self.nodes[i]["head"] + except IndexError: + return None + + def _rel(self, i): + try: + return self.nodes[i]["rel"] + except IndexError: + return None + + # what's the return type? Boolean or list? + def contains_cycle(self): + """Check whether there are cycles. + + >>> dg = DependencyGraph(treebank_data) + >>> dg.contains_cycle() + False + + >>> cyclic_dg = DependencyGraph() + >>> top = {'word': None, 'deps': [1], 'rel': 'TOP', 'address': 0} + >>> child1 = {'word': None, 'deps': [2], 'rel': 'NTOP', 'address': 1} + >>> child2 = {'word': None, 'deps': [4], 'rel': 'NTOP', 'address': 2} + >>> child3 = {'word': None, 'deps': [1], 'rel': 'NTOP', 'address': 3} + >>> child4 = {'word': None, 'deps': [3], 'rel': 'NTOP', 'address': 4} + >>> cyclic_dg.nodes = { + ... 0: top, + ... 1: child1, + ... 2: child2, + ... 3: child3, + ... 4: child4, + ... } + >>> cyclic_dg.root = top + + >>> cyclic_dg.contains_cycle() + [1, 2, 4, 3] + + """ + distances = {} + + for node in self.nodes.values(): + for dep in node["deps"]: + key = tuple([node["address"], dep]) + distances[key] = 1 + + for _ in self.nodes: + new_entries = {} + + for pair1 in distances: + for pair2 in distances: + if pair1[1] == pair2[0]: + key = tuple([pair1[0], pair2[1]]) + new_entries[key] = distances[pair1] + distances[pair2] + + for pair in new_entries: + distances[pair] = new_entries[pair] + if pair[0] == pair[1]: + path = self.get_cycle_path(self.get_by_address(pair[0]), pair[0]) + return path + + return False # return []? + + def get_cycle_path(self, curr_node, goal_node_index): + for dep in curr_node["deps"]: + if dep == goal_node_index: + return [curr_node["address"]] + for dep in curr_node["deps"]: + path = self.get_cycle_path(self.get_by_address(dep), goal_node_index) + if len(path) > 0: + path.insert(0, curr_node["address"]) + return path + return [] + + def to_conll(self, style): + """ + The dependency graph in CoNLL format. + + :param style: the style to use for the format (3, 4, 10 columns) + :type style: int + :rtype: str + """ + + if style == 3: + template = "{word}\t{tag}\t{head}\n" + elif style == 4: + template = "{word}\t{tag}\t{head}\t{rel}\n" + elif style == 10: + template = ( + "{i}\t{word}\t{lemma}\t{ctag}\t{tag}\t{feats}\t{head}\t{rel}\t_\t_\n" + ) + else: + raise ValueError( + "Number of tab-delimited fields ({}) not supported by " + "CoNLL(10) or Malt-Tab(4) format".format(style) + ) + + return "".join( + template.format(i=i, **node) + for i, node in sorted(self.nodes.items()) + if node["tag"] != "TOP" + ) + + def nx_graph(self): + """Convert the data in a ``nodelist`` into a networkx labeled directed graph.""" + import networkx + + nx_nodelist = list(range(1, len(self.nodes))) + nx_edgelist = [ + (n, self._hd(n), self._rel(n)) for n in nx_nodelist if self._hd(n) + ] + self.nx_labels = {} + for n in nx_nodelist: + self.nx_labels[n] = self.nodes[n]["word"] + + g = networkx.MultiDiGraph() + g.add_nodes_from(nx_nodelist) + g.add_edges_from(nx_edgelist) + + return g + + +def dot2img(dot_string, t="svg"): + """ + Create image representation fom dot_string, using the 'dot' program + from the Graphviz package. + + Use the 't' argument to specify the image file format, for ex. 'jpeg', 'eps', + 'json', 'png' or 'webp' (Running 'dot -T:' lists all available formats). + + Note that the "capture_output" option of subprocess.run() is only available + with text formats (like svg), but not with binary image formats (like png). + """ + + try: + find_binary("dot") + try: + if t in ["dot", "dot_json", "json", "svg"]: + proc = subprocess.run( + ["dot", "-T%s" % t], + capture_output=True, + input=dot_string, + text=True, + ) + else: + proc = subprocess.run( + ["dot", "-T%s" % t], + input=bytes(dot_string, encoding="utf8"), + ) + return proc.stdout + except: + raise Exception( + "Cannot create image representation by running dot from string: {}" + "".format(dot_string) + ) + except OSError as e: + raise Exception("Cannot find the dot binary from Graphviz package") from e + + +class DependencyGraphError(Exception): + """Dependency graph exception.""" + + +def demo(): + malt_demo() + conll_demo() + conll_file_demo() + cycle_finding_demo() + + +def malt_demo(nx=False): + """ + A demonstration of the result of reading a dependency + version of the first sentence of the Penn Treebank. + """ + dg = DependencyGraph( + """Pierre NNP 2 NMOD +Vinken NNP 8 SUB +, , 2 P +61 CD 5 NMOD +years NNS 6 AMOD +old JJ 2 NMOD +, , 2 P +will MD 0 ROOT +join VB 8 VC +the DT 11 NMOD +board NN 9 OBJ +as IN 9 VMOD +a DT 15 NMOD +nonexecutive JJ 15 NMOD +director NN 12 PMOD +Nov. NNP 9 VMOD +29 CD 16 NMOD +. . 9 VMOD +""" + ) + tree = dg.tree() + tree.pprint() + if nx: + # currently doesn't work + import networkx + from matplotlib import pylab + + g = dg.nx_graph() + g.info() + pos = networkx.spring_layout(g, dim=1) + networkx.draw_networkx_nodes(g, pos, node_size=50) + # networkx.draw_networkx_edges(g, pos, edge_color='k', width=8) + networkx.draw_networkx_labels(g, pos, dg.nx_labels) + pylab.xticks([]) + pylab.yticks([]) + pylab.savefig("tree.png") + pylab.show() + + +def conll_demo(): + """ + A demonstration of how to read a string representation of + a CoNLL format dependency tree. + """ + dg = DependencyGraph(conll_data1) + tree = dg.tree() + tree.pprint() + print(dg) + print(dg.to_conll(4)) + + +def conll_file_demo(): + print("Mass conll_read demo...") + graphs = [DependencyGraph(entry) for entry in conll_data2.split("\n\n") if entry] + for graph in graphs: + tree = graph.tree() + print("\n") + tree.pprint() + + +def cycle_finding_demo(): + dg = DependencyGraph(treebank_data) + print(dg.contains_cycle()) + cyclic_dg = DependencyGraph() + cyclic_dg.add_node({"word": None, "deps": [1], "rel": "TOP", "address": 0}) + cyclic_dg.add_node({"word": None, "deps": [2], "rel": "NTOP", "address": 1}) + cyclic_dg.add_node({"word": None, "deps": [4], "rel": "NTOP", "address": 2}) + cyclic_dg.add_node({"word": None, "deps": [1], "rel": "NTOP", "address": 3}) + cyclic_dg.add_node({"word": None, "deps": [3], "rel": "NTOP", "address": 4}) + print(cyclic_dg.contains_cycle()) + + +treebank_data = """Pierre NNP 2 NMOD +Vinken NNP 8 SUB +, , 2 P +61 CD 5 NMOD +years NNS 6 AMOD +old JJ 2 NMOD +, , 2 P +will MD 0 ROOT +join VB 8 VC +the DT 11 NMOD +board NN 9 OBJ +as IN 9 VMOD +a DT 15 NMOD +nonexecutive JJ 15 NMOD +director NN 12 PMOD +Nov. NNP 9 VMOD +29 CD 16 NMOD +. . 9 VMOD +""" + +conll_data1 = """ +1 Ze ze Pron Pron per|3|evofmv|nom 2 su _ _ +2 had heb V V trans|ovt|1of2of3|ev 0 ROOT _ _ +3 met met Prep Prep voor 8 mod _ _ +4 haar haar Pron Pron bez|3|ev|neut|attr 5 det _ _ +5 moeder moeder N N soort|ev|neut 3 obj1 _ _ +6 kunnen kan V V hulp|ott|1of2of3|mv 2 vc _ _ +7 gaan ga V V hulp|inf 6 vc _ _ +8 winkelen winkel V V intrans|inf 11 cnj _ _ +9 , , Punc Punc komma 8 punct _ _ +10 zwemmen zwem V V intrans|inf 11 cnj _ _ +11 of of Conj Conj neven 7 vc _ _ +12 terrassen terras N N soort|mv|neut 11 cnj _ _ +13 . . Punc Punc punt 12 punct _ _ +""" + +conll_data2 = """1 Cathy Cathy N N eigen|ev|neut 2 su _ _ +2 zag zie V V trans|ovt|1of2of3|ev 0 ROOT _ _ +3 hen hen Pron Pron per|3|mv|datofacc 2 obj1 _ _ +4 wild wild Adj Adj attr|stell|onverv 5 mod _ _ +5 zwaaien zwaai N N soort|mv|neut 2 vc _ _ +6 . . Punc Punc punt 5 punct _ _ + +1 Ze ze Pron Pron per|3|evofmv|nom 2 su _ _ +2 had heb V V trans|ovt|1of2of3|ev 0 ROOT _ _ +3 met met Prep Prep voor 8 mod _ _ +4 haar haar Pron Pron bez|3|ev|neut|attr 5 det _ _ +5 moeder moeder N N soort|ev|neut 3 obj1 _ _ +6 kunnen kan V V hulp|ott|1of2of3|mv 2 vc _ _ +7 gaan ga V V hulp|inf 6 vc _ _ +8 winkelen winkel V V intrans|inf 11 cnj _ _ +9 , , Punc Punc komma 8 punct _ _ +10 zwemmen zwem V V intrans|inf 11 cnj _ _ +11 of of Conj Conj neven 7 vc _ _ +12 terrassen terras N N soort|mv|neut 11 cnj _ _ +13 . . Punc Punc punt 12 punct _ _ + +1 Dat dat Pron Pron aanw|neut|attr 2 det _ _ +2 werkwoord werkwoord N N soort|ev|neut 6 obj1 _ _ +3 had heb V V hulp|ovt|1of2of3|ev 0 ROOT _ _ +4 ze ze Pron Pron per|3|evofmv|nom 6 su _ _ +5 zelf zelf Pron Pron aanw|neut|attr|wzelf 3 predm _ _ +6 uitgevonden vind V V trans|verldw|onverv 3 vc _ _ +7 . . Punc Punc punt 6 punct _ _ + +1 Het het Pron Pron onbep|neut|zelfst 2 su _ _ +2 hoorde hoor V V trans|ovt|1of2of3|ev 0 ROOT _ _ +3 bij bij Prep Prep voor 2 ld _ _ +4 de de Art Art bep|zijdofmv|neut 6 det _ _ +5 warme warm Adj Adj attr|stell|vervneut 6 mod _ _ +6 zomerdag zomerdag N N soort|ev|neut 3 obj1 _ _ +7 die die Pron Pron betr|neut|zelfst 6 mod _ _ +8 ze ze Pron Pron per|3|evofmv|nom 12 su _ _ +9 ginds ginds Adv Adv gew|aanw 12 mod _ _ +10 achter achter Adv Adv gew|geenfunc|stell|onverv 12 svp _ _ +11 had heb V V hulp|ovt|1of2of3|ev 7 body _ _ +12 gelaten laat V V trans|verldw|onverv 11 vc _ _ +13 . . Punc Punc punt 12 punct _ _ + +1 Ze ze Pron Pron per|3|evofmv|nom 2 su _ _ +2 hadden heb V V trans|ovt|1of2of3|mv 0 ROOT _ _ +3 languit languit Adv Adv gew|geenfunc|stell|onverv 11 mod _ _ +4 naast naast Prep Prep voor 11 mod _ _ +5 elkaar elkaar Pron Pron rec|neut 4 obj1 _ _ +6 op op Prep Prep voor 11 ld _ _ +7 de de Art Art bep|zijdofmv|neut 8 det _ _ +8 strandstoelen strandstoel N N soort|mv|neut 6 obj1 _ _ +9 kunnen kan V V hulp|inf 2 vc _ _ +10 gaan ga V V hulp|inf 9 vc _ _ +11 liggen lig V V intrans|inf 10 vc _ _ +12 . . Punc Punc punt 11 punct _ _ + +1 Zij zij Pron Pron per|3|evofmv|nom 2 su _ _ +2 zou zal V V hulp|ovt|1of2of3|ev 7 cnj _ _ +3 mams mams N N soort|ev|neut 4 det _ _ +4 rug rug N N soort|ev|neut 5 obj1 _ _ +5 ingewreven wrijf V V trans|verldw|onverv 6 vc _ _ +6 hebben heb V V hulp|inf 2 vc _ _ +7 en en Conj Conj neven 0 ROOT _ _ +8 mam mam V V trans|ovt|1of2of3|ev 7 cnj _ _ +9 de de Art Art bep|zijdofmv|neut 10 det _ _ +10 hare hare Pron Pron bez|3|ev|neut|attr 8 obj1 _ _ +11 . . Punc Punc punt 10 punct _ _ + +1 Of of Conj Conj onder|metfin 0 ROOT _ _ +2 ze ze Pron Pron per|3|evofmv|nom 3 su _ _ +3 had heb V V hulp|ovt|1of2of3|ev 0 ROOT _ _ +4 gewoon gewoon Adj Adj adv|stell|onverv 10 mod _ _ +5 met met Prep Prep voor 10 mod _ _ +6 haar haar Pron Pron bez|3|ev|neut|attr 7 det _ _ +7 vriendinnen vriendin N N soort|mv|neut 5 obj1 _ _ +8 rond rond Adv Adv deelv 10 svp _ _ +9 kunnen kan V V hulp|inf 3 vc _ _ +10 slenteren slenter V V intrans|inf 9 vc _ _ +11 in in Prep Prep voor 10 mod _ _ +12 de de Art Art bep|zijdofmv|neut 13 det _ _ +13 buurt buurt N N soort|ev|neut 11 obj1 _ _ +14 van van Prep Prep voor 13 mod _ _ +15 Trafalgar_Square Trafalgar_Square MWU N_N eigen|ev|neut_eigen|ev|neut 14 obj1 _ _ +16 . . Punc Punc punt 15 punct _ _ +""" + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/earleychart.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/earleychart.py new file mode 100644 index 0000000..efb2cd3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/earleychart.py @@ -0,0 +1,552 @@ +# Natural Language Toolkit: An Incremental Earley Chart Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Peter Ljunglöf +# Rob Speer +# Edward Loper +# Steven Bird +# Jean Mark Gawron +# URL: +# For license information, see LICENSE.TXT + +""" +Data classes and parser implementations for *incremental* chart +parsers, which use dynamic programming to efficiently parse a text. +A "chart parser" derives parse trees for a text by iteratively adding +\"edges\" to a \"chart\". Each "edge" represents a hypothesis about the tree +structure for a subsequence of the text. The "chart" is a +\"blackboard\" for composing and combining these hypotheses. + +A parser is "incremental", if it guarantees that for all i, j where i < j, +all edges ending at i are built before any edges ending at j. +This is appealing for, say, speech recognizer hypothesis filtering. + +The main parser class is ``EarleyChartParser``, which is a top-down +algorithm, originally formulated by Jay Earley (1970). +""" + +from time import perf_counter + +from nltk.parse.chart import ( + BottomUpPredictCombineRule, + BottomUpPredictRule, + CachedTopDownPredictRule, + Chart, + ChartParser, + EdgeI, + EmptyPredictRule, + FilteredBottomUpPredictCombineRule, + FilteredSingleEdgeFundamentalRule, + LeafEdge, + LeafInitRule, + SingleEdgeFundamentalRule, + TopDownInitRule, +) +from nltk.parse.featurechart import ( + FeatureBottomUpPredictCombineRule, + FeatureBottomUpPredictRule, + FeatureChart, + FeatureChartParser, + FeatureEmptyPredictRule, + FeatureSingleEdgeFundamentalRule, + FeatureTopDownInitRule, + FeatureTopDownPredictRule, +) + +# //////////////////////////////////////////////////////////// +# Incremental Chart +# //////////////////////////////////////////////////////////// + + +class IncrementalChart(Chart): + def initialize(self): + # A sequence of edge lists contained in this chart. + self._edgelists = tuple([] for x in self._positions()) + + # The set of child pointer lists associated with each edge. + self._edge_to_cpls = {} + + # Indexes mapping attribute values to lists of edges + # (used by select()). + self._indexes = {} + + def edges(self): + return list(self.iteredges()) + + def iteredges(self): + return (edge for edgelist in self._edgelists for edge in edgelist) + + def select(self, end, **restrictions): + edgelist = self._edgelists[end] + + # If there are no restrictions, then return all edges. + if restrictions == {}: + return iter(edgelist) + + # Find the index corresponding to the given restrictions. + restr_keys = sorted(restrictions.keys()) + restr_keys = tuple(restr_keys) + + # If it doesn't exist, then create it. + if restr_keys not in self._indexes: + self._add_index(restr_keys) + + vals = tuple(restrictions[key] for key in restr_keys) + return iter(self._indexes[restr_keys][end].get(vals, [])) + + def _add_index(self, restr_keys): + # Make sure it's a valid index. + for key in restr_keys: + if not hasattr(EdgeI, key): + raise ValueError("Bad restriction: %s" % key) + + # Create the index. + index = self._indexes[restr_keys] = tuple({} for x in self._positions()) + + # Add all existing edges to the index. + for end, edgelist in enumerate(self._edgelists): + this_index = index[end] + for edge in edgelist: + vals = tuple(getattr(edge, key)() for key in restr_keys) + this_index.setdefault(vals, []).append(edge) + + def _register_with_indexes(self, edge): + end = edge.end() + for (restr_keys, index) in self._indexes.items(): + vals = tuple(getattr(edge, key)() for key in restr_keys) + index[end].setdefault(vals, []).append(edge) + + def _append_edge(self, edge): + self._edgelists[edge.end()].append(edge) + + def _positions(self): + return range(self.num_leaves() + 1) + + +class FeatureIncrementalChart(IncrementalChart, FeatureChart): + def select(self, end, **restrictions): + edgelist = self._edgelists[end] + + # If there are no restrictions, then return all edges. + if restrictions == {}: + return iter(edgelist) + + # Find the index corresponding to the given restrictions. + restr_keys = sorted(restrictions.keys()) + restr_keys = tuple(restr_keys) + + # If it doesn't exist, then create it. + if restr_keys not in self._indexes: + self._add_index(restr_keys) + + vals = tuple( + self._get_type_if_possible(restrictions[key]) for key in restr_keys + ) + return iter(self._indexes[restr_keys][end].get(vals, [])) + + def _add_index(self, restr_keys): + # Make sure it's a valid index. + for key in restr_keys: + if not hasattr(EdgeI, key): + raise ValueError("Bad restriction: %s" % key) + + # Create the index. + index = self._indexes[restr_keys] = tuple({} for x in self._positions()) + + # Add all existing edges to the index. + for end, edgelist in enumerate(self._edgelists): + this_index = index[end] + for edge in edgelist: + vals = tuple( + self._get_type_if_possible(getattr(edge, key)()) + for key in restr_keys + ) + this_index.setdefault(vals, []).append(edge) + + def _register_with_indexes(self, edge): + end = edge.end() + for (restr_keys, index) in self._indexes.items(): + vals = tuple( + self._get_type_if_possible(getattr(edge, key)()) for key in restr_keys + ) + index[end].setdefault(vals, []).append(edge) + + +# //////////////////////////////////////////////////////////// +# Incremental CFG Rules +# //////////////////////////////////////////////////////////// + + +class CompleteFundamentalRule(SingleEdgeFundamentalRule): + def _apply_incomplete(self, chart, grammar, left_edge): + end = left_edge.end() + # When the chart is incremental, we only have to look for + # empty complete edges here. + for right_edge in chart.select( + start=end, end=end, is_complete=True, lhs=left_edge.nextsym() + ): + new_edge = left_edge.move_dot_forward(right_edge.end()) + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + +class CompleterRule(CompleteFundamentalRule): + _fundamental_rule = CompleteFundamentalRule() + + def apply(self, chart, grammar, edge): + if not isinstance(edge, LeafEdge): + yield from self._fundamental_rule.apply(chart, grammar, edge) + + +class ScannerRule(CompleteFundamentalRule): + _fundamental_rule = CompleteFundamentalRule() + + def apply(self, chart, grammar, edge): + if isinstance(edge, LeafEdge): + yield from self._fundamental_rule.apply(chart, grammar, edge) + + +class PredictorRule(CachedTopDownPredictRule): + pass + + +class FilteredCompleteFundamentalRule(FilteredSingleEdgeFundamentalRule): + def apply(self, chart, grammar, edge): + # Since the Filtered rule only works for grammars without empty productions, + # we only have to bother with complete edges here. + if edge.is_complete(): + yield from self._apply_complete(chart, grammar, edge) + + +# //////////////////////////////////////////////////////////// +# Incremental FCFG Rules +# //////////////////////////////////////////////////////////// + + +class FeatureCompleteFundamentalRule(FeatureSingleEdgeFundamentalRule): + def _apply_incomplete(self, chart, grammar, left_edge): + fr = self._fundamental_rule + end = left_edge.end() + # When the chart is incremental, we only have to look for + # empty complete edges here. + for right_edge in chart.select( + start=end, end=end, is_complete=True, lhs=left_edge.nextsym() + ): + yield from fr.apply(chart, grammar, left_edge, right_edge) + + +class FeatureCompleterRule(CompleterRule): + _fundamental_rule = FeatureCompleteFundamentalRule() + + +class FeatureScannerRule(ScannerRule): + _fundamental_rule = FeatureCompleteFundamentalRule() + + +class FeaturePredictorRule(FeatureTopDownPredictRule): + pass + + +# //////////////////////////////////////////////////////////// +# Incremental CFG Chart Parsers +# //////////////////////////////////////////////////////////// + +EARLEY_STRATEGY = [ + LeafInitRule(), + TopDownInitRule(), + CompleterRule(), + ScannerRule(), + PredictorRule(), +] +TD_INCREMENTAL_STRATEGY = [ + LeafInitRule(), + TopDownInitRule(), + CachedTopDownPredictRule(), + CompleteFundamentalRule(), +] +BU_INCREMENTAL_STRATEGY = [ + LeafInitRule(), + EmptyPredictRule(), + BottomUpPredictRule(), + CompleteFundamentalRule(), +] +BU_LC_INCREMENTAL_STRATEGY = [ + LeafInitRule(), + EmptyPredictRule(), + BottomUpPredictCombineRule(), + CompleteFundamentalRule(), +] + +LC_INCREMENTAL_STRATEGY = [ + LeafInitRule(), + FilteredBottomUpPredictCombineRule(), + FilteredCompleteFundamentalRule(), +] + + +class IncrementalChartParser(ChartParser): + """ + An *incremental* chart parser implementing Jay Earley's + parsing algorithm: + + | For each index end in [0, 1, ..., N]: + | For each edge such that edge.end = end: + | If edge is incomplete and edge.next is not a part of speech: + | Apply PredictorRule to edge + | If edge is incomplete and edge.next is a part of speech: + | Apply ScannerRule to edge + | If edge is complete: + | Apply CompleterRule to edge + | Return any complete parses in the chart + """ + + def __init__( + self, + grammar, + strategy=BU_LC_INCREMENTAL_STRATEGY, + trace=0, + trace_chart_width=50, + chart_class=IncrementalChart, + ): + """ + Create a new Earley chart parser, that uses ``grammar`` to + parse texts. + + :type grammar: CFG + :param grammar: The grammar used to parse texts. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + :type trace_chart_width: int + :param trace_chart_width: The default total width reserved for + the chart in trace output. The remainder of each line will + be used to display edges. + :param chart_class: The class that should be used to create + the charts used by this parser. + """ + self._grammar = grammar + self._trace = trace + self._trace_chart_width = trace_chart_width + self._chart_class = chart_class + + self._axioms = [] + self._inference_rules = [] + for rule in strategy: + if rule.NUM_EDGES == 0: + self._axioms.append(rule) + elif rule.NUM_EDGES == 1: + self._inference_rules.append(rule) + else: + raise ValueError( + "Incremental inference rules must have " "NUM_EDGES == 0 or 1" + ) + + def chart_parse(self, tokens, trace=None): + if trace is None: + trace = self._trace + trace_new_edges = self._trace_new_edges + + tokens = list(tokens) + self._grammar.check_coverage(tokens) + chart = self._chart_class(tokens) + grammar = self._grammar + + # Width, for printing trace edges. + trace_edge_width = self._trace_chart_width // (chart.num_leaves() + 1) + if trace: + print(chart.pretty_format_leaves(trace_edge_width)) + + for axiom in self._axioms: + new_edges = list(axiom.apply(chart, grammar)) + trace_new_edges(chart, axiom, new_edges, trace, trace_edge_width) + + inference_rules = self._inference_rules + for end in range(chart.num_leaves() + 1): + if trace > 1: + print("\n* Processing queue:", end, "\n") + agenda = list(chart.select(end=end)) + while agenda: + edge = agenda.pop() + for rule in inference_rules: + new_edges = list(rule.apply(chart, grammar, edge)) + trace_new_edges(chart, rule, new_edges, trace, trace_edge_width) + for new_edge in new_edges: + if new_edge.end() == end: + agenda.append(new_edge) + + return chart + + +class EarleyChartParser(IncrementalChartParser): + def __init__(self, grammar, **parser_args): + IncrementalChartParser.__init__(self, grammar, EARLEY_STRATEGY, **parser_args) + + +class IncrementalTopDownChartParser(IncrementalChartParser): + def __init__(self, grammar, **parser_args): + IncrementalChartParser.__init__( + self, grammar, TD_INCREMENTAL_STRATEGY, **parser_args + ) + + +class IncrementalBottomUpChartParser(IncrementalChartParser): + def __init__(self, grammar, **parser_args): + IncrementalChartParser.__init__( + self, grammar, BU_INCREMENTAL_STRATEGY, **parser_args + ) + + +class IncrementalBottomUpLeftCornerChartParser(IncrementalChartParser): + def __init__(self, grammar, **parser_args): + IncrementalChartParser.__init__( + self, grammar, BU_LC_INCREMENTAL_STRATEGY, **parser_args + ) + + +class IncrementalLeftCornerChartParser(IncrementalChartParser): + def __init__(self, grammar, **parser_args): + if not grammar.is_nonempty(): + raise ValueError( + "IncrementalLeftCornerParser only works for grammars " + "without empty productions." + ) + IncrementalChartParser.__init__( + self, grammar, LC_INCREMENTAL_STRATEGY, **parser_args + ) + + +# //////////////////////////////////////////////////////////// +# Incremental FCFG Chart Parsers +# //////////////////////////////////////////////////////////// + +EARLEY_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureTopDownInitRule(), + FeatureCompleterRule(), + FeatureScannerRule(), + FeaturePredictorRule(), +] +TD_INCREMENTAL_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureTopDownInitRule(), + FeatureTopDownPredictRule(), + FeatureCompleteFundamentalRule(), +] +BU_INCREMENTAL_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureEmptyPredictRule(), + FeatureBottomUpPredictRule(), + FeatureCompleteFundamentalRule(), +] +BU_LC_INCREMENTAL_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureEmptyPredictRule(), + FeatureBottomUpPredictCombineRule(), + FeatureCompleteFundamentalRule(), +] + + +class FeatureIncrementalChartParser(IncrementalChartParser, FeatureChartParser): + def __init__( + self, + grammar, + strategy=BU_LC_INCREMENTAL_FEATURE_STRATEGY, + trace_chart_width=20, + chart_class=FeatureIncrementalChart, + **parser_args + ): + IncrementalChartParser.__init__( + self, + grammar, + strategy=strategy, + trace_chart_width=trace_chart_width, + chart_class=chart_class, + **parser_args + ) + + +class FeatureEarleyChartParser(FeatureIncrementalChartParser): + def __init__(self, grammar, **parser_args): + FeatureIncrementalChartParser.__init__( + self, grammar, EARLEY_FEATURE_STRATEGY, **parser_args + ) + + +class FeatureIncrementalTopDownChartParser(FeatureIncrementalChartParser): + def __init__(self, grammar, **parser_args): + FeatureIncrementalChartParser.__init__( + self, grammar, TD_INCREMENTAL_FEATURE_STRATEGY, **parser_args + ) + + +class FeatureIncrementalBottomUpChartParser(FeatureIncrementalChartParser): + def __init__(self, grammar, **parser_args): + FeatureIncrementalChartParser.__init__( + self, grammar, BU_INCREMENTAL_FEATURE_STRATEGY, **parser_args + ) + + +class FeatureIncrementalBottomUpLeftCornerChartParser(FeatureIncrementalChartParser): + def __init__(self, grammar, **parser_args): + FeatureIncrementalChartParser.__init__( + self, grammar, BU_LC_INCREMENTAL_FEATURE_STRATEGY, **parser_args + ) + + +# //////////////////////////////////////////////////////////// +# Demonstration +# //////////////////////////////////////////////////////////// + + +def demo( + print_times=True, + print_grammar=False, + print_trees=True, + trace=2, + sent="I saw John with a dog with my cookie", + numparses=5, +): + """ + A demonstration of the Earley parsers. + """ + import sys + import time + + from nltk.parse.chart import demo_grammar + + # The grammar for ChartParser and SteppingChartParser: + grammar = demo_grammar() + if print_grammar: + print("* Grammar") + print(grammar) + + # Tokenize the sample sentence. + print("* Sentence:") + print(sent) + tokens = sent.split() + print(tokens) + print() + + # Do the parsing. + earley = EarleyChartParser(grammar, trace=trace) + t = perf_counter() + chart = earley.chart_parse(tokens) + parses = list(chart.parses(grammar.start())) + t = perf_counter() - t + + # Print results. + if numparses: + assert len(parses) == numparses, "Not all parses found" + if print_trees: + for tree in parses: + print(tree) + else: + print("Nr trees:", len(parses)) + if print_times: + print("Time:", t) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/evaluate.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/evaluate.py new file mode 100644 index 0000000..4d6994a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/evaluate.py @@ -0,0 +1,129 @@ +# Natural Language Toolkit: evaluation of dependency parser +# +# Author: Long Duong +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +import unicodedata + + +class DependencyEvaluator: + """ + Class for measuring labelled and unlabelled attachment score for + dependency parsing. Note that the evaluation ignores punctuation. + + >>> from nltk.parse import DependencyGraph, DependencyEvaluator + + >>> gold_sent = DependencyGraph(\""" + ... Pierre NNP 2 NMOD + ... Vinken NNP 8 SUB + ... , , 2 P + ... 61 CD 5 NMOD + ... years NNS 6 AMOD + ... old JJ 2 NMOD + ... , , 2 P + ... will MD 0 ROOT + ... join VB 8 VC + ... the DT 11 NMOD + ... board NN 9 OBJ + ... as IN 9 VMOD + ... a DT 15 NMOD + ... nonexecutive JJ 15 NMOD + ... director NN 12 PMOD + ... Nov. NNP 9 VMOD + ... 29 CD 16 NMOD + ... . . 9 VMOD + ... \""") + + >>> parsed_sent = DependencyGraph(\""" + ... Pierre NNP 8 NMOD + ... Vinken NNP 1 SUB + ... , , 3 P + ... 61 CD 6 NMOD + ... years NNS 6 AMOD + ... old JJ 2 NMOD + ... , , 3 AMOD + ... will MD 0 ROOT + ... join VB 8 VC + ... the DT 11 AMOD + ... board NN 9 OBJECT + ... as IN 9 NMOD + ... a DT 15 NMOD + ... nonexecutive JJ 15 NMOD + ... director NN 12 PMOD + ... Nov. NNP 9 VMOD + ... 29 CD 16 NMOD + ... . . 9 VMOD + ... \""") + + >>> de = DependencyEvaluator([parsed_sent],[gold_sent]) + >>> las, uas = de.eval() + >>> las + 0.6 + >>> uas + 0.8 + >>> abs(uas - 0.8) < 0.00001 + True + """ + + def __init__(self, parsed_sents, gold_sents): + """ + :param parsed_sents: the list of parsed_sents as the output of parser + :type parsed_sents: list(DependencyGraph) + """ + self._parsed_sents = parsed_sents + self._gold_sents = gold_sents + + def _remove_punct(self, inStr): + """ + Function to remove punctuation from Unicode string. + :param input: the input string + :return: Unicode string after remove all punctuation + """ + punc_cat = {"Pc", "Pd", "Ps", "Pe", "Pi", "Pf", "Po"} + return "".join(x for x in inStr if unicodedata.category(x) not in punc_cat) + + def eval(self): + """ + Return the Labeled Attachment Score (LAS) and Unlabeled Attachment Score (UAS) + + :return : tuple(float,float) + """ + if len(self._parsed_sents) != len(self._gold_sents): + raise ValueError( + " Number of parsed sentence is different with number of gold sentence." + ) + + corr = 0 + corrL = 0 + total = 0 + + for i in range(len(self._parsed_sents)): + parsed_sent_nodes = self._parsed_sents[i].nodes + gold_sent_nodes = self._gold_sents[i].nodes + + if len(parsed_sent_nodes) != len(gold_sent_nodes): + raise ValueError("Sentences must have equal length.") + + for parsed_node_address, parsed_node in parsed_sent_nodes.items(): + gold_node = gold_sent_nodes[parsed_node_address] + + if parsed_node["word"] is None: + continue + if parsed_node["word"] != gold_node["word"]: + raise ValueError("Sentence sequence is not matched.") + + # Ignore if word is punctuation by default + # if (parsed_sent[j]["word"] in string.punctuation): + if self._remove_punct(parsed_node["word"]) == "": + continue + + total += 1 + if parsed_node["head"] == gold_node["head"]: + corr += 1 + if parsed_node["rel"] == gold_node["rel"]: + corrL += 1 + + return corrL / total, corr / total diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/featurechart.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/featurechart.py new file mode 100644 index 0000000..ec64586 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/featurechart.py @@ -0,0 +1,674 @@ +# Natural Language Toolkit: Chart Parser for Feature-Based Grammars +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Rob Speer +# Peter Ljunglöf +# URL: +# For license information, see LICENSE.TXT + +""" +Extension of chart parsing implementation to handle grammars with +feature structures as nodes. +""" +from time import perf_counter + +from nltk.featstruct import TYPE, FeatStruct, find_variables, unify +from nltk.grammar import ( + CFG, + FeatStructNonterminal, + Nonterminal, + Production, + is_nonterminal, + is_terminal, +) +from nltk.parse.chart import ( + BottomUpPredictCombineRule, + BottomUpPredictRule, + CachedTopDownPredictRule, + Chart, + ChartParser, + EdgeI, + EmptyPredictRule, + FundamentalRule, + LeafInitRule, + SingleEdgeFundamentalRule, + TopDownInitRule, + TreeEdge, +) +from nltk.sem import logic +from nltk.tree import Tree + +# //////////////////////////////////////////////////////////// +# Tree Edge +# //////////////////////////////////////////////////////////// + + +class FeatureTreeEdge(TreeEdge): + """ + A specialized tree edge that allows shared variable bindings + between nonterminals on the left-hand side and right-hand side. + + Each ``FeatureTreeEdge`` contains a set of ``bindings``, i.e., a + dictionary mapping from variables to values. If the edge is not + complete, then these bindings are simply stored. However, if the + edge is complete, then the constructor applies these bindings to + every nonterminal in the edge whose symbol implements the + interface ``SubstituteBindingsI``. + """ + + def __init__(self, span, lhs, rhs, dot=0, bindings=None): + """ + Construct a new edge. If the edge is incomplete (i.e., if + ``dot alpha \* B1 beta][i:j]`` + - ``[B2 -> gamma \*][j:k]`` + + licenses the edge: + + - ``[A -> alpha B3 \* beta][i:j]`` + + assuming that B1 and B2 can be unified to generate B3. + """ + + def apply(self, chart, grammar, left_edge, right_edge): + # Make sure the rule is applicable. + if not ( + left_edge.end() == right_edge.start() + and left_edge.is_incomplete() + and right_edge.is_complete() + and isinstance(left_edge, FeatureTreeEdge) + ): + return + found = right_edge.lhs() + nextsym = left_edge.nextsym() + if isinstance(right_edge, FeatureTreeEdge): + if not is_nonterminal(nextsym): + return + if left_edge.nextsym()[TYPE] != right_edge.lhs()[TYPE]: + return + # Create a copy of the bindings. + bindings = left_edge.bindings() + # We rename vars here, because we don't want variables + # from the two different productions to match. + found = found.rename_variables(used_vars=left_edge.variables()) + # Unify B1 (left_edge.nextsym) with B2 (right_edge.lhs) to + # generate B3 (result). + result = unify(nextsym, found, bindings, rename_vars=False) + if result is None: + return + else: + if nextsym != found: + return + # Create a copy of the bindings. + bindings = left_edge.bindings() + + # Construct the new edge. + new_edge = left_edge.move_dot_forward(right_edge.end(), bindings) + + # Add it to the chart, with appropriate child pointers. + if chart.insert_with_backpointer(new_edge, left_edge, right_edge): + yield new_edge + + +class FeatureSingleEdgeFundamentalRule(SingleEdgeFundamentalRule): + """ + A specialized version of the completer / single edge fundamental rule + that operates on nonterminals whose symbols are ``FeatStructNonterminal``. + Rather than simply comparing the nonterminals for equality, they are + unified. + """ + + _fundamental_rule = FeatureFundamentalRule() + + def _apply_complete(self, chart, grammar, right_edge): + fr = self._fundamental_rule + for left_edge in chart.select( + end=right_edge.start(), is_complete=False, nextsym=right_edge.lhs() + ): + yield from fr.apply(chart, grammar, left_edge, right_edge) + + def _apply_incomplete(self, chart, grammar, left_edge): + fr = self._fundamental_rule + for right_edge in chart.select( + start=left_edge.end(), is_complete=True, lhs=left_edge.nextsym() + ): + yield from fr.apply(chart, grammar, left_edge, right_edge) + + +# //////////////////////////////////////////////////////////// +# Top-Down Prediction +# //////////////////////////////////////////////////////////// + + +class FeatureTopDownInitRule(TopDownInitRule): + def apply(self, chart, grammar): + for prod in grammar.productions(lhs=grammar.start()): + new_edge = FeatureTreeEdge.from_production(prod, 0) + if chart.insert(new_edge, ()): + yield new_edge + + +class FeatureTopDownPredictRule(CachedTopDownPredictRule): + r""" + A specialized version of the (cached) top down predict rule that operates + on nonterminals whose symbols are ``FeatStructNonterminal``. Rather + than simply comparing the nonterminals for equality, they are + unified. + + The top down expand rule states that: + + - ``[A -> alpha \* B1 beta][i:j]`` + + licenses the edge: + + - ``[B2 -> \* gamma][j:j]`` + + for each grammar production ``B2 -> gamma``, assuming that B1 + and B2 can be unified. + """ + + def apply(self, chart, grammar, edge): + if edge.is_complete(): + return + nextsym, index = edge.nextsym(), edge.end() + if not is_nonterminal(nextsym): + return + + # If we've already applied this rule to an edge with the same + # next & end, and the chart & grammar have not changed, then + # just return (no new edges to add). + nextsym_with_bindings = edge.next_with_bindings() + done = self._done.get((nextsym_with_bindings, index), (None, None)) + if done[0] is chart and done[1] is grammar: + return + + for prod in grammar.productions(lhs=nextsym): + # If the left corner in the predicted production is + # leaf, it must match with the input. + if prod.rhs(): + first = prod.rhs()[0] + if is_terminal(first): + if index >= chart.num_leaves(): + continue + if first != chart.leaf(index): + continue + + # We rename vars here, because we don't want variables + # from the two different productions to match. + if unify(prod.lhs(), nextsym_with_bindings, rename_vars=True): + new_edge = FeatureTreeEdge.from_production(prod, edge.end()) + if chart.insert(new_edge, ()): + yield new_edge + + # Record the fact that we've applied this rule. + self._done[nextsym_with_bindings, index] = (chart, grammar) + + +# //////////////////////////////////////////////////////////// +# Bottom-Up Prediction +# //////////////////////////////////////////////////////////// + + +class FeatureBottomUpPredictRule(BottomUpPredictRule): + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + for prod in grammar.productions(rhs=edge.lhs()): + if isinstance(edge, FeatureTreeEdge): + _next = prod.rhs()[0] + if not is_nonterminal(_next): + continue + + new_edge = FeatureTreeEdge.from_production(prod, edge.start()) + if chart.insert(new_edge, ()): + yield new_edge + + +class FeatureBottomUpPredictCombineRule(BottomUpPredictCombineRule): + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + found = edge.lhs() + for prod in grammar.productions(rhs=found): + bindings = {} + if isinstance(edge, FeatureTreeEdge): + _next = prod.rhs()[0] + if not is_nonterminal(_next): + continue + + # We rename vars here, because we don't want variables + # from the two different productions to match. + used_vars = find_variables( + (prod.lhs(),) + prod.rhs(), fs_class=FeatStruct + ) + found = found.rename_variables(used_vars=used_vars) + + result = unify(_next, found, bindings, rename_vars=False) + if result is None: + continue + + new_edge = FeatureTreeEdge.from_production( + prod, edge.start() + ).move_dot_forward(edge.end(), bindings) + if chart.insert(new_edge, (edge,)): + yield new_edge + + +class FeatureEmptyPredictRule(EmptyPredictRule): + def apply(self, chart, grammar): + for prod in grammar.productions(empty=True): + for index in range(chart.num_leaves() + 1): + new_edge = FeatureTreeEdge.from_production(prod, index) + if chart.insert(new_edge, ()): + yield new_edge + + +# //////////////////////////////////////////////////////////// +# Feature Chart Parser +# //////////////////////////////////////////////////////////// + +TD_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureTopDownInitRule(), + FeatureTopDownPredictRule(), + FeatureSingleEdgeFundamentalRule(), +] +BU_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureEmptyPredictRule(), + FeatureBottomUpPredictRule(), + FeatureSingleEdgeFundamentalRule(), +] +BU_LC_FEATURE_STRATEGY = [ + LeafInitRule(), + FeatureEmptyPredictRule(), + FeatureBottomUpPredictCombineRule(), + FeatureSingleEdgeFundamentalRule(), +] + + +class FeatureChartParser(ChartParser): + def __init__( + self, + grammar, + strategy=BU_LC_FEATURE_STRATEGY, + trace_chart_width=20, + chart_class=FeatureChart, + **parser_args, + ): + ChartParser.__init__( + self, + grammar, + strategy=strategy, + trace_chart_width=trace_chart_width, + chart_class=chart_class, + **parser_args, + ) + + +class FeatureTopDownChartParser(FeatureChartParser): + def __init__(self, grammar, **parser_args): + FeatureChartParser.__init__(self, grammar, TD_FEATURE_STRATEGY, **parser_args) + + +class FeatureBottomUpChartParser(FeatureChartParser): + def __init__(self, grammar, **parser_args): + FeatureChartParser.__init__(self, grammar, BU_FEATURE_STRATEGY, **parser_args) + + +class FeatureBottomUpLeftCornerChartParser(FeatureChartParser): + def __init__(self, grammar, **parser_args): + FeatureChartParser.__init__( + self, grammar, BU_LC_FEATURE_STRATEGY, **parser_args + ) + + +# //////////////////////////////////////////////////////////// +# Instantiate Variable Chart +# //////////////////////////////////////////////////////////// + + +class InstantiateVarsChart(FeatureChart): + """ + A specialized chart that 'instantiates' variables whose names + start with '@', by replacing them with unique new variables. + In particular, whenever a complete edge is added to the chart, any + variables in the edge's ``lhs`` whose names start with '@' will be + replaced by unique new ``Variable``. + """ + + def __init__(self, tokens): + FeatureChart.__init__(self, tokens) + + def initialize(self): + self._instantiated = set() + FeatureChart.initialize(self) + + def insert(self, edge, child_pointer_list): + if edge in self._instantiated: + return False + self.instantiate_edge(edge) + return FeatureChart.insert(self, edge, child_pointer_list) + + def instantiate_edge(self, edge): + """ + If the edge is a ``FeatureTreeEdge``, and it is complete, + then instantiate all variables whose names start with '@', + by replacing them with unique new variables. + + Note that instantiation is done in-place, since the + parsing algorithms might already hold a reference to + the edge for future use. + """ + # If the edge is a leaf, or is not complete, or is + # already in the chart, then just return it as-is. + if not isinstance(edge, FeatureTreeEdge): + return + if not edge.is_complete(): + return + if edge in self._edge_to_cpls: + return + + # Get a list of variables that need to be instantiated. + # If there are none, then return as-is. + inst_vars = self.inst_vars(edge) + if not inst_vars: + return + + # Instantiate the edge! + self._instantiated.add(edge) + edge._lhs = edge.lhs().substitute_bindings(inst_vars) + + def inst_vars(self, edge): + return { + var: logic.unique_variable() + for var in edge.lhs().variables() + if var.name.startswith("@") + } + + +# //////////////////////////////////////////////////////////// +# Demo +# //////////////////////////////////////////////////////////// + + +def demo_grammar(): + from nltk.grammar import FeatureGrammar + + return FeatureGrammar.fromstring( + """ +S -> NP VP +PP -> Prep NP +NP -> NP PP +VP -> VP PP +VP -> Verb NP +VP -> Verb +NP -> Det[pl=?x] Noun[pl=?x] +NP -> "John" +NP -> "I" +Det -> "the" +Det -> "my" +Det[-pl] -> "a" +Noun[-pl] -> "dog" +Noun[-pl] -> "cookie" +Verb -> "ate" +Verb -> "saw" +Prep -> "with" +Prep -> "under" +""" + ) + + +def demo( + print_times=True, + print_grammar=True, + print_trees=True, + print_sentence=True, + trace=1, + parser=FeatureChartParser, + sent="I saw John with a dog with my cookie", +): + import sys + import time + + print() + grammar = demo_grammar() + if print_grammar: + print(grammar) + print() + print("*", parser.__name__) + if print_sentence: + print("Sentence:", sent) + tokens = sent.split() + t = perf_counter() + cp = parser(grammar, trace=trace) + chart = cp.chart_parse(tokens) + trees = list(chart.parses(grammar.start())) + if print_times: + print("Time: %s" % (perf_counter() - t)) + if print_trees: + for tree in trees: + print(tree) + else: + print("Nr trees:", len(trees)) + + +def run_profile(): + import profile + + profile.run("for i in range(1): demo()", "/tmp/profile.out") + import pstats + + p = pstats.Stats("/tmp/profile.out") + p.strip_dirs().sort_stats("time", "cum").print_stats(60) + p.strip_dirs().sort_stats("cum", "time").print_stats(60) + + +if __name__ == "__main__": + from nltk.data import load + + demo() + print() + grammar = load("grammars/book_grammars/feat0.fcfg") + cp = FeatureChartParser(grammar, trace=2) + sent = "Kim likes children" + tokens = sent.split() + trees = cp.parse(tokens) + for tree in trees: + print(tree) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/generate.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/generate.py new file mode 100644 index 0000000..3c70981 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/generate.py @@ -0,0 +1,85 @@ +# Natural Language Toolkit: Generating from a CFG +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Peter Ljunglöf +# URL: +# For license information, see LICENSE.TXT +# + +import itertools +import sys + +from nltk.grammar import Nonterminal + + +def generate(grammar, start=None, depth=None, n=None): + """ + Generates an iterator of all sentences from a CFG. + + :param grammar: The Grammar used to generate sentences. + :param start: The Nonterminal from which to start generate sentences. + :param depth: The maximal depth of the generated tree. + :param n: The maximum number of sentences to return. + :return: An iterator of lists of terminal tokens. + """ + if not start: + start = grammar.start() + if depth is None: + depth = sys.maxsize + + iter = _generate_all(grammar, [start], depth) + + if n: + iter = itertools.islice(iter, n) + + return iter + + +def _generate_all(grammar, items, depth): + if items: + try: + for frag1 in _generate_one(grammar, items[0], depth): + for frag2 in _generate_all(grammar, items[1:], depth): + yield frag1 + frag2 + except RecursionError as error: + # Helpful error message while still showing the recursion stack. + raise RuntimeError( + "The grammar has rule(s) that yield infinite recursion!" + ) from error + else: + yield [] + + +def _generate_one(grammar, item, depth): + if depth > 0: + if isinstance(item, Nonterminal): + for prod in grammar.productions(lhs=item): + yield from _generate_all(grammar, prod.rhs(), depth - 1) + else: + yield [item] + + +demo_grammar = """ + S -> NP VP + NP -> Det N + PP -> P NP + VP -> 'slept' | 'saw' NP | 'walked' PP + Det -> 'the' | 'a' + N -> 'man' | 'park' | 'dog' + P -> 'in' | 'with' +""" + + +def demo(N=23): + from nltk.grammar import CFG + + print("Generating the first %d sentences for demo grammar:" % (N,)) + print(demo_grammar) + grammar = CFG.fromstring(demo_grammar) + for n, sent in enumerate(generate(grammar, n=N), 1): + print("%3d. %s" % (n, " ".join(sent))) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/malt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/malt.py new file mode 100644 index 0000000..77f955b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/malt.py @@ -0,0 +1,393 @@ +# Natural Language Toolkit: Interface to MaltParser +# +# Author: Dan Garrette +# Contributor: Liling Tan, Mustufain, osamamukhtar11 +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +import inspect +import os +import subprocess +import sys +import tempfile + +from nltk.data import ZipFilePathPointer +from nltk.internals import find_dir, find_file, find_jars_within_path +from nltk.parse.api import ParserI +from nltk.parse.dependencygraph import DependencyGraph +from nltk.parse.util import taggedsents_to_conll + + +def malt_regex_tagger(): + from nltk.tag import RegexpTagger + + _tagger = RegexpTagger( + [ + (r"\.$", "."), + (r"\,$", ","), + (r"\?$", "?"), # fullstop, comma, Qmark + (r"\($", "("), + (r"\)$", ")"), # round brackets + (r"\[$", "["), + (r"\]$", "]"), # square brackets + (r"^-?[0-9]+(\.[0-9]+)?$", "CD"), # cardinal numbers + (r"(The|the|A|a|An|an)$", "DT"), # articles + (r"(He|he|She|she|It|it|I|me|Me|You|you)$", "PRP"), # pronouns + (r"(His|his|Her|her|Its|its)$", "PRP$"), # possessive + (r"(my|Your|your|Yours|yours)$", "PRP$"), # possessive + (r"(on|On|in|In|at|At|since|Since)$", "IN"), # time prepopsitions + (r"(for|For|ago|Ago|before|Before)$", "IN"), # time prepopsitions + (r"(till|Till|until|Until)$", "IN"), # time prepopsitions + (r"(by|By|beside|Beside)$", "IN"), # space prepopsitions + (r"(under|Under|below|Below)$", "IN"), # space prepopsitions + (r"(over|Over|above|Above)$", "IN"), # space prepopsitions + (r"(across|Across|through|Through)$", "IN"), # space prepopsitions + (r"(into|Into|towards|Towards)$", "IN"), # space prepopsitions + (r"(onto|Onto|from|From)$", "IN"), # space prepopsitions + (r".*able$", "JJ"), # adjectives + (r".*ness$", "NN"), # nouns formed from adjectives + (r".*ly$", "RB"), # adverbs + (r".*s$", "NNS"), # plural nouns + (r".*ing$", "VBG"), # gerunds + (r".*ed$", "VBD"), # past tense verbs + (r".*", "NN"), # nouns (default) + ] + ) + return _tagger.tag + + +def find_maltparser(parser_dirname): + """ + A module to find MaltParser .jar file and its dependencies. + """ + if os.path.exists(parser_dirname): # If a full path is given. + _malt_dir = parser_dirname + else: # Try to find path to maltparser directory in environment variables. + _malt_dir = find_dir(parser_dirname, env_vars=("MALT_PARSER",)) + # Checks that that the found directory contains all the necessary .jar + malt_dependencies = ["", "", ""] + _malt_jars = set(find_jars_within_path(_malt_dir)) + _jars = {os.path.split(jar)[1] for jar in _malt_jars} + malt_dependencies = {"log4j.jar", "libsvm.jar", "liblinear-1.8.jar"} + + assert malt_dependencies.issubset(_jars) + assert any( + filter(lambda i: i.startswith("maltparser-") and i.endswith(".jar"), _jars) + ) + return list(_malt_jars) + + +def find_malt_model(model_filename): + """ + A module to find pre-trained MaltParser model. + """ + if model_filename is None: + return "malt_temp.mco" + elif os.path.exists(model_filename): # If a full path is given. + return model_filename + else: # Try to find path to malt model in environment variables. + return find_file(model_filename, env_vars=("MALT_MODEL",), verbose=False) + + +class MaltParser(ParserI): + """ + A class for dependency parsing with MaltParser. The input is the paths to: + - (optionally) a maltparser directory + - (optionally) the path to a pre-trained MaltParser .mco model file + - (optionally) the tagger to use for POS tagging before parsing + - (optionally) additional Java arguments + + Example: + >>> from nltk.parse import malt + >>> # With MALT_PARSER and MALT_MODEL environment set. + >>> mp = malt.MaltParser(model_filename='engmalt.linear-1.7.mco') # doctest: +SKIP + >>> mp.parse_one('I shot an elephant in my pajamas .'.split()).tree() # doctest: +SKIP + (shot I (elephant an) (in (pajamas my)) .) + >>> # Without MALT_PARSER and MALT_MODEL environment. + >>> mp = malt.MaltParser('/home/user/maltparser-1.9.2/', '/home/user/engmalt.linear-1.7.mco') # doctest: +SKIP + >>> mp.parse_one('I shot an elephant in my pajamas .'.split()).tree() # doctest: +SKIP + (shot I (elephant an) (in (pajamas my)) .) + """ + + def __init__( + self, + parser_dirname="", + model_filename=None, + tagger=None, + additional_java_args=None, + ): + """ + An interface for parsing with the Malt Parser. + + :param parser_dirname: The path to the maltparser directory that + contains the maltparser-1.x.jar + :type parser_dirname: str + :param model_filename: The name of the pre-trained model with .mco file + extension. If provided, training will not be required. + (see http://www.maltparser.org/mco/mco.html and + see http://www.patful.com/chalk/node/185) + :type model_filename: str + :param tagger: The tagger used to POS tag the raw string before + formatting to CONLL format. It should behave like `nltk.pos_tag` + :type tagger: function + :param additional_java_args: This is the additional Java arguments that + one can use when calling Maltparser, usually this is the heapsize + limits, e.g. `additional_java_args=['-Xmx1024m']` + (see https://goo.gl/mpDBvQ) + :type additional_java_args: list + """ + + # Find all the necessary jar files for MaltParser. + self.malt_jars = find_maltparser(parser_dirname) + # Initialize additional java arguments. + self.additional_java_args = ( + additional_java_args if additional_java_args is not None else [] + ) + # Initialize model. + self.model = find_malt_model(model_filename) + self._trained = self.model != "malt_temp.mco" + # Set the working_dir parameters i.e. `-w` from MaltParser's option. + self.working_dir = tempfile.gettempdir() + # Initialize POS tagger. + self.tagger = tagger if tagger is not None else malt_regex_tagger() + + def parse_tagged_sents(self, sentences, verbose=False, top_relation_label="null"): + """ + Use MaltParser to parse multiple POS tagged sentences. Takes multiple + sentences where each sentence is a list of (word, tag) tuples. + The sentences must have already been tokenized and tagged. + + :param sentences: Input sentences to parse + :type sentence: list(list(tuple(str, str))) + :return: iter(iter(``DependencyGraph``)) the dependency graph + representation of each sentence + """ + if not self._trained: + raise Exception("Parser has not been trained. Call train() first.") + + with tempfile.NamedTemporaryFile( + prefix="malt_input.conll.", dir=self.working_dir, mode="w", delete=False + ) as input_file: + with tempfile.NamedTemporaryFile( + prefix="malt_output.conll.", + dir=self.working_dir, + mode="w", + delete=False, + ) as output_file: + # Convert list of sentences to CONLL format. + for line in taggedsents_to_conll(sentences): + input_file.write(str(line)) + input_file.close() + + # Generate command to run maltparser. + cmd = self.generate_malt_command( + input_file.name, output_file.name, mode="parse" + ) + + # This is a maltparser quirk, it needs to be run + # where the model file is. otherwise it goes into an awkward + # missing .jars or strange -w working_dir problem. + _current_path = os.getcwd() # Remembers the current path. + try: # Change to modelfile path + os.chdir(os.path.split(self.model)[0]) + except: + pass + ret = self._execute(cmd, verbose) # Run command. + os.chdir(_current_path) # Change back to current path. + + if ret != 0: + raise Exception( + "MaltParser parsing (%s) failed with exit " + "code %d" % (" ".join(cmd), ret) + ) + + # Must return iter(iter(Tree)) + with open(output_file.name) as infile: + for tree_str in infile.read().split("\n\n"): + yield ( + iter( + [ + DependencyGraph( + tree_str, top_relation_label=top_relation_label + ) + ] + ) + ) + + os.remove(input_file.name) + os.remove(output_file.name) + + def parse_sents(self, sentences, verbose=False, top_relation_label="null"): + """ + Use MaltParser to parse multiple sentences. + Takes a list of sentences, where each sentence is a list of words. + Each sentence will be automatically tagged with this + MaltParser instance's tagger. + + :param sentences: Input sentences to parse + :type sentence: list(list(str)) + :return: iter(DependencyGraph) + """ + tagged_sentences = (self.tagger(sentence) for sentence in sentences) + return self.parse_tagged_sents( + tagged_sentences, verbose, top_relation_label=top_relation_label + ) + + def generate_malt_command(self, inputfilename, outputfilename=None, mode=None): + """ + This function generates the maltparser command use at the terminal. + + :param inputfilename: path to the input file + :type inputfilename: str + :param outputfilename: path to the output file + :type outputfilename: str + """ + + cmd = ["java"] + cmd += self.additional_java_args # Adds additional java arguments + # Joins classpaths with ";" if on Windows and on Linux/Mac use ":" + classpaths_separator = ";" if sys.platform.startswith("win") else ":" + cmd += [ + "-cp", + classpaths_separator.join(self.malt_jars), + ] # Adds classpaths for jars + cmd += ["org.maltparser.Malt"] # Adds the main function. + + # Adds the model file. + if os.path.exists(self.model): # when parsing + cmd += ["-c", os.path.split(self.model)[-1]] + else: # when learning + cmd += ["-c", self.model] + + cmd += ["-i", inputfilename] + if mode == "parse": + cmd += ["-o", outputfilename] + cmd += ["-m", mode] # mode use to generate parses. + return cmd + + @staticmethod + def _execute(cmd, verbose=False): + output = None if verbose else subprocess.PIPE + p = subprocess.Popen(cmd, stdout=output, stderr=output) + return p.wait() + + def train(self, depgraphs, verbose=False): + """ + Train MaltParser from a list of ``DependencyGraph`` objects + + :param depgraphs: list of ``DependencyGraph`` objects for training input data + :type depgraphs: DependencyGraph + """ + + # Write the conll_str to malt_train.conll file in /tmp/ + with tempfile.NamedTemporaryFile( + prefix="malt_train.conll.", dir=self.working_dir, mode="w", delete=False + ) as input_file: + input_str = "\n".join(dg.to_conll(10) for dg in depgraphs) + input_file.write(str(input_str)) + # Trains the model with the malt_train.conll + self.train_from_file(input_file.name, verbose=verbose) + # Removes the malt_train.conll once training finishes. + os.remove(input_file.name) + + def train_from_file(self, conll_file, verbose=False): + """ + Train MaltParser from a file + :param conll_file: str for the filename of the training input data + :type conll_file: str + """ + + # If conll_file is a ZipFilePathPointer, + # then we need to do some extra massaging + if isinstance(conll_file, ZipFilePathPointer): + with tempfile.NamedTemporaryFile( + prefix="malt_train.conll.", dir=self.working_dir, mode="w", delete=False + ) as input_file: + with conll_file.open() as conll_input_file: + conll_str = conll_input_file.read() + input_file.write(str(conll_str)) + return self.train_from_file(input_file.name, verbose=verbose) + + # Generate command to run maltparser. + cmd = self.generate_malt_command(conll_file, mode="learn") + ret = self._execute(cmd, verbose) + if ret != 0: + raise Exception( + "MaltParser training (%s) failed with exit " + "code %d" % (" ".join(cmd), ret) + ) + self._trained = True + + +if __name__ == "__main__": + """ + A demonstration function to show how NLTK users can use the malt parser API. + + >>> from nltk import pos_tag + >>> assert 'MALT_PARSER' in os.environ, str( + ... "Please set MALT_PARSER in your global environment, e.g.:\n" + ... "$ export MALT_PARSER='/home/user/maltparser-1.9.2/'") + >>> + >>> assert 'MALT_MODEL' in os.environ, str( + ... "Please set MALT_MODEL in your global environment, e.g.:\n" + ... "$ export MALT_MODEL='/home/user/engmalt.linear-1.7.mco'") + >>> + >>> _dg1_str = str("1 John _ NNP _ _ 2 SUBJ _ _\n" + ... "2 sees _ VB _ _ 0 ROOT _ _\n" + ... "3 a _ DT _ _ 4 SPEC _ _\n" + ... "4 dog _ NN _ _ 2 OBJ _ _\n" + ... "5 . _ . _ _ 2 PUNCT _ _\n") + >>> + >>> + >>> _dg2_str = str("1 John _ NNP _ _ 2 SUBJ _ _\n" + ... "2 walks _ VB _ _ 0 ROOT _ _\n" + ... "3 . _ . _ _ 2 PUNCT _ _\n") + >>> dg1 = DependencyGraph(_dg1_str) + >>> dg2 = DependencyGraph(_dg2_str) + >>> # Initialize a MaltParser object + >>> mp = MaltParser() + >>> + >>> # Trains a model. + >>> mp.train([dg1,dg2], verbose=False) + >>> sent1 = ['John','sees','Mary', '.'] + >>> sent2 = ['John', 'walks', 'a', 'dog', '.'] + >>> + >>> # Parse a single sentence. + >>> parsed_sent1 = mp.parse_one(sent1) + >>> parsed_sent2 = mp.parse_one(sent2) + >>> print(parsed_sent1.tree()) + (sees John Mary .) + >>> print(parsed_sent2.tree()) + (walks John (dog a) .) + >>> + >>> # Parsing multiple sentences. + >>> sentences = [sent1,sent2] + >>> parsed_sents = mp.parse_sents(sentences) + >>> print(next(next(parsed_sents)).tree()) + (sees John Mary .) + >>> print(next(next(parsed_sents)).tree()) + (walks John (dog a) .) + >>> + >>> # Initialize a MaltParser object with an English pre-trained model. + >>> parser_dirname = 'maltparser-1.9.2' + >>> model_name = 'engmalt.linear-1.7.mco' + >>> mp = MaltParser(parser_dirname=parser_dirname, model_filename=model_name, tagger=pos_tag) + >>> sent1 = 'I shot an elephant in my pajamas .'.split() + >>> sent2 = 'Time flies like banana .'.split() + >>> # Parse a single sentence. + >>> print(mp.parse_one(sent1).tree()) + (shot I (elephant an) (in (pajamas my)) .) + # Parsing multiple sentences + >>> sentences = [sent1,sent2] + >>> parsed_sents = mp.parse_sents(sentences) + >>> print(next(next(parsed_sents)).tree()) + (shot I (elephant an) (in (pajamas my)) .) + >>> print(next(next(parsed_sents)).tree()) + (flies Time (like banana) .) + """ + + import doctest + + doctest.testmod() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/nonprojectivedependencyparser.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/nonprojectivedependencyparser.py new file mode 100644 index 0000000..ffbe7a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/nonprojectivedependencyparser.py @@ -0,0 +1,772 @@ +# Natural Language Toolkit: Dependency Grammars +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Jason Narad +# +# URL: +# For license information, see LICENSE.TXT +# + +import logging +import math + +from nltk.parse.dependencygraph import DependencyGraph + +logger = logging.getLogger(__name__) + +################################################################# +# DependencyScorerI - Interface for Graph-Edge Weight Calculation +################################################################# + + +class DependencyScorerI: + """ + A scorer for calculated the weights on the edges of a weighted + dependency graph. This is used by a + ``ProbabilisticNonprojectiveParser`` to initialize the edge + weights of a ``DependencyGraph``. While typically this would be done + by training a binary classifier, any class that can return a + multidimensional list representation of the edge weights can + implement this interface. As such, it has no necessary + fields. + """ + + def __init__(self): + if self.__class__ == DependencyScorerI: + raise TypeError("DependencyScorerI is an abstract interface") + + def train(self, graphs): + """ + :type graphs: list(DependencyGraph) + :param graphs: A list of dependency graphs to train the scorer. + Typically the edges present in the graphs can be used as + positive training examples, and the edges not present as negative + examples. + """ + raise NotImplementedError() + + def score(self, graph): + """ + :type graph: DependencyGraph + :param graph: A dependency graph whose set of edges need to be + scored. + :rtype: A three-dimensional list of numbers. + :return: The score is returned in a multidimensional(3) list, such + that the outer-dimension refers to the head, and the + inner-dimension refers to the dependencies. For instance, + scores[0][1] would reference the list of scores corresponding to + arcs from node 0 to node 1. The node's 'address' field can be used + to determine its number identification. + + For further illustration, a score list corresponding to Fig.2 of + Keith Hall's 'K-best Spanning Tree Parsing' paper:: + + scores = [[[], [5], [1], [1]], + [[], [], [11], [4]], + [[], [10], [], [5]], + [[], [8], [8], []]] + + When used in conjunction with a MaxEntClassifier, each score would + correspond to the confidence of a particular edge being classified + with the positive training examples. + """ + raise NotImplementedError() + + +################################################################# +# NaiveBayesDependencyScorer +################################################################# + + +class NaiveBayesDependencyScorer(DependencyScorerI): + """ + A dependency scorer built around a MaxEnt classifier. In this + particular class that classifier is a ``NaiveBayesClassifier``. + It uses head-word, head-tag, child-word, and child-tag features + for classification. + + >>> from nltk.parse.dependencygraph import DependencyGraph, conll_data2 + + >>> graphs = [DependencyGraph(entry) for entry in conll_data2.split('\\n\\n') if entry] + >>> npp = ProbabilisticNonprojectiveParser() + >>> npp.train(graphs, NaiveBayesDependencyScorer()) + >>> parses = npp.parse(['Cathy', 'zag', 'hen', 'zwaaien', '.'], ['N', 'V', 'Pron', 'Adj', 'N', 'Punc']) + >>> len(list(parses)) + 1 + + """ + + def __init__(self): + pass # Do nothing without throwing error + + def train(self, graphs): + """ + Trains a ``NaiveBayesClassifier`` using the edges present in + graphs list as positive examples, the edges not present as + negative examples. Uses a feature vector of head-word, + head-tag, child-word, and child-tag. + + :type graphs: list(DependencyGraph) + :param graphs: A list of dependency graphs to train the scorer. + """ + + from nltk.classify import NaiveBayesClassifier + + # Create training labeled training examples + labeled_examples = [] + for graph in graphs: + for head_node in graph.nodes.values(): + for child_index, child_node in graph.nodes.items(): + if child_index in head_node["deps"]: + label = "T" + else: + label = "F" + labeled_examples.append( + ( + dict( + a=head_node["word"], + b=head_node["tag"], + c=child_node["word"], + d=child_node["tag"], + ), + label, + ) + ) + + self.classifier = NaiveBayesClassifier.train(labeled_examples) + + def score(self, graph): + """ + Converts the graph into a feature-based representation of + each edge, and then assigns a score to each based on the + confidence of the classifier in assigning it to the + positive label. Scores are returned in a multidimensional list. + + :type graph: DependencyGraph + :param graph: A dependency graph to score. + :rtype: 3 dimensional list + :return: Edge scores for the graph parameter. + """ + # Convert graph to feature representation + edges = [] + for head_node in graph.nodes.values(): + for child_node in graph.nodes.values(): + edges.append( + dict( + a=head_node["word"], + b=head_node["tag"], + c=child_node["word"], + d=child_node["tag"], + ) + ) + + # Score edges + edge_scores = [] + row = [] + count = 0 + for pdist in self.classifier.prob_classify_many(edges): + logger.debug("%.4f %.4f", pdist.prob("T"), pdist.prob("F")) + # smoothing in case the probability = 0 + row.append([math.log(pdist.prob("T") + 0.00000000001)]) + count += 1 + if count == len(graph.nodes): + edge_scores.append(row) + row = [] + count = 0 + return edge_scores + + +################################################################# +# A Scorer for Demo Purposes +################################################################# +# A short class necessary to show parsing example from paper +class DemoScorer(DependencyScorerI): + def train(self, graphs): + print("Training...") + + def score(self, graph): + # scores for Keith Hall 'K-best Spanning Tree Parsing' paper + return [ + [[], [5], [1], [1]], + [[], [], [11], [4]], + [[], [10], [], [5]], + [[], [8], [8], []], + ] + + +################################################################# +# Non-Projective Probabilistic Parsing +################################################################# + + +class ProbabilisticNonprojectiveParser: + """A probabilistic non-projective dependency parser. + + Nonprojective dependencies allows for "crossing branches" in the parse tree + which is necessary for representing particular linguistic phenomena, or even + typical parses in some languages. This parser follows the MST parsing + algorithm, outlined in McDonald(2005), which likens the search for the best + non-projective parse to finding the maximum spanning tree in a weighted + directed graph. + + >>> class Scorer(DependencyScorerI): + ... def train(self, graphs): + ... pass + ... + ... def score(self, graph): + ... return [ + ... [[], [5], [1], [1]], + ... [[], [], [11], [4]], + ... [[], [10], [], [5]], + ... [[], [8], [8], []], + ... ] + + + >>> npp = ProbabilisticNonprojectiveParser() + >>> npp.train([], Scorer()) + + >>> parses = npp.parse(['v1', 'v2', 'v3'], [None, None, None]) + >>> len(list(parses)) + 1 + + Rule based example + + >>> from nltk.grammar import DependencyGrammar + + >>> grammar = DependencyGrammar.fromstring(''' + ... 'taught' -> 'play' | 'man' + ... 'man' -> 'the' | 'in' + ... 'in' -> 'corner' + ... 'corner' -> 'the' + ... 'play' -> 'golf' | 'dachshund' | 'to' + ... 'dachshund' -> 'his' + ... ''') + + >>> ndp = NonprojectiveDependencyParser(grammar) + >>> parses = ndp.parse(['the', 'man', 'in', 'the', 'corner', 'taught', 'his', 'dachshund', 'to', 'play', 'golf']) + >>> len(list(parses)) + 4 + + """ + + def __init__(self): + """ + Creates a new non-projective parser. + """ + logging.debug("initializing prob. nonprojective...") + + def train(self, graphs, dependency_scorer): + """ + Trains a ``DependencyScorerI`` from a set of ``DependencyGraph`` objects, + and establishes this as the parser's scorer. This is used to + initialize the scores on a ``DependencyGraph`` during the parsing + procedure. + + :type graphs: list(DependencyGraph) + :param graphs: A list of dependency graphs to train the scorer. + :type dependency_scorer: DependencyScorerI + :param dependency_scorer: A scorer which implements the + ``DependencyScorerI`` interface. + """ + self._scorer = dependency_scorer + self._scorer.train(graphs) + + def initialize_edge_scores(self, graph): + """ + Assigns a score to every edge in the ``DependencyGraph`` graph. + These scores are generated via the parser's scorer which + was assigned during the training process. + + :type graph: DependencyGraph + :param graph: A dependency graph to assign scores to. + """ + self.scores = self._scorer.score(graph) + + def collapse_nodes(self, new_node, cycle_path, g_graph, b_graph, c_graph): + """ + Takes a list of nodes that have been identified to belong to a cycle, + and collapses them into on larger node. The arcs of all nodes in + the graph must be updated to account for this. + + :type new_node: Node. + :param new_node: A Node (Dictionary) to collapse the cycle nodes into. + :type cycle_path: A list of integers. + :param cycle_path: A list of node addresses, each of which is in the cycle. + :type g_graph, b_graph, c_graph: DependencyGraph + :param g_graph, b_graph, c_graph: Graphs which need to be updated. + """ + logger.debug("Collapsing nodes...") + # Collapse all cycle nodes into v_n+1 in G_Graph + for cycle_node_index in cycle_path: + g_graph.remove_by_address(cycle_node_index) + g_graph.add_node(new_node) + g_graph.redirect_arcs(cycle_path, new_node["address"]) + + def update_edge_scores(self, new_node, cycle_path): + """ + Updates the edge scores to reflect a collapse operation into + new_node. + + :type new_node: A Node. + :param new_node: The node which cycle nodes are collapsed into. + :type cycle_path: A list of integers. + :param cycle_path: A list of node addresses that belong to the cycle. + """ + logger.debug("cycle %s", cycle_path) + + cycle_path = self.compute_original_indexes(cycle_path) + + logger.debug("old cycle %s", cycle_path) + logger.debug("Prior to update: %s", self.scores) + + for i, row in enumerate(self.scores): + for j, column in enumerate(self.scores[i]): + logger.debug(self.scores[i][j]) + if j in cycle_path and i not in cycle_path and self.scores[i][j]: + subtract_val = self.compute_max_subtract_score(j, cycle_path) + + logger.debug("%s - %s", self.scores[i][j], subtract_val) + + new_vals = [] + for cur_val in self.scores[i][j]: + new_vals.append(cur_val - subtract_val) + + self.scores[i][j] = new_vals + + for i, row in enumerate(self.scores): + for j, cell in enumerate(self.scores[i]): + if i in cycle_path and j in cycle_path: + self.scores[i][j] = [] + + logger.debug("After update: %s", self.scores) + + def compute_original_indexes(self, new_indexes): + """ + As nodes are collapsed into others, they are replaced + by the new node in the graph, but it's still necessary + to keep track of what these original nodes were. This + takes a list of node addresses and replaces any collapsed + node addresses with their original addresses. + + :type new_indexes: A list of integers. + :param new_indexes: A list of node addresses to check for + subsumed nodes. + """ + swapped = True + while swapped: + originals = [] + swapped = False + for new_index in new_indexes: + if new_index in self.inner_nodes: + for old_val in self.inner_nodes[new_index]: + if old_val not in originals: + originals.append(old_val) + swapped = True + else: + originals.append(new_index) + new_indexes = originals + return new_indexes + + def compute_max_subtract_score(self, column_index, cycle_indexes): + """ + When updating scores the score of the highest-weighted incoming + arc is subtracted upon collapse. This returns the correct + amount to subtract from that edge. + + :type column_index: integer. + :param column_index: A index representing the column of incoming arcs + to a particular node being updated + :type cycle_indexes: A list of integers. + :param cycle_indexes: Only arcs from cycle nodes are considered. This + is a list of such nodes addresses. + """ + max_score = -100000 + for row_index in cycle_indexes: + for subtract_val in self.scores[row_index][column_index]: + if subtract_val > max_score: + max_score = subtract_val + return max_score + + def best_incoming_arc(self, node_index): + """ + Returns the source of the best incoming arc to the + node with address: node_index + + :type node_index: integer. + :param node_index: The address of the 'destination' node, + the node that is arced to. + """ + originals = self.compute_original_indexes([node_index]) + logger.debug("originals: %s", originals) + + max_arc = None + max_score = None + for row_index in range(len(self.scores)): + for col_index in range(len(self.scores[row_index])): + if col_index in originals and ( + max_score is None or self.scores[row_index][col_index] > max_score + ): + max_score = self.scores[row_index][col_index] + max_arc = row_index + logger.debug("%s, %s", row_index, col_index) + + logger.debug(max_score) + + for key in self.inner_nodes: + replaced_nodes = self.inner_nodes[key] + if max_arc in replaced_nodes: + return key + + return max_arc + + def original_best_arc(self, node_index): + originals = self.compute_original_indexes([node_index]) + max_arc = None + max_score = None + max_orig = None + for row_index in range(len(self.scores)): + for col_index in range(len(self.scores[row_index])): + if col_index in originals and ( + max_score is None or self.scores[row_index][col_index] > max_score + ): + max_score = self.scores[row_index][col_index] + max_arc = row_index + max_orig = col_index + return [max_arc, max_orig] + + def parse(self, tokens, tags): + """ + Parses a list of tokens in accordance to the MST parsing algorithm + for non-projective dependency parses. Assumes that the tokens to + be parsed have already been tagged and those tags are provided. Various + scoring methods can be used by implementing the ``DependencyScorerI`` + interface and passing it to the training algorithm. + + :type tokens: list(str) + :param tokens: A list of words or punctuation to be parsed. + :type tags: list(str) + :param tags: A list of tags corresponding by index to the words in the tokens list. + :return: An iterator of non-projective parses. + :rtype: iter(DependencyGraph) + """ + self.inner_nodes = {} + + # Initialize g_graph + g_graph = DependencyGraph() + for index, token in enumerate(tokens): + g_graph.nodes[index + 1].update( + {"word": token, "tag": tags[index], "rel": "NTOP", "address": index + 1} + ) + + # Fully connect non-root nodes in g_graph + g_graph.connect_graph() + original_graph = DependencyGraph() + for index, token in enumerate(tokens): + original_graph.nodes[index + 1].update( + {"word": token, "tag": tags[index], "rel": "NTOP", "address": index + 1} + ) + + b_graph = DependencyGraph() + c_graph = DependencyGraph() + + for index, token in enumerate(tokens): + c_graph.nodes[index + 1].update( + {"word": token, "tag": tags[index], "rel": "NTOP", "address": index + 1} + ) + + # Assign initial scores to g_graph edges + self.initialize_edge_scores(g_graph) + logger.debug(self.scores) + # Initialize a list of unvisited vertices (by node address) + unvisited_vertices = [vertex["address"] for vertex in c_graph.nodes.values()] + # Iterate over unvisited vertices + nr_vertices = len(tokens) + betas = {} + while unvisited_vertices: + # Mark current node as visited + current_vertex = unvisited_vertices.pop(0) + logger.debug("current_vertex: %s", current_vertex) + # Get corresponding node n_i to vertex v_i + current_node = g_graph.get_by_address(current_vertex) + logger.debug("current_node: %s", current_node) + # Get best in-edge node b for current node + best_in_edge = self.best_incoming_arc(current_vertex) + betas[current_vertex] = self.original_best_arc(current_vertex) + logger.debug("best in arc: %s --> %s", best_in_edge, current_vertex) + # b_graph = Union(b_graph, b) + for new_vertex in [current_vertex, best_in_edge]: + b_graph.nodes[new_vertex].update( + {"word": "TEMP", "rel": "NTOP", "address": new_vertex} + ) + b_graph.add_arc(best_in_edge, current_vertex) + # Beta(current node) = b - stored for parse recovery + # If b_graph contains a cycle, collapse it + cycle_path = b_graph.contains_cycle() + if cycle_path: + # Create a new node v_n+1 with address = len(nodes) + 1 + new_node = {"word": "NONE", "rel": "NTOP", "address": nr_vertices + 1} + # c_graph = Union(c_graph, v_n+1) + c_graph.add_node(new_node) + # Collapse all nodes in cycle C into v_n+1 + self.update_edge_scores(new_node, cycle_path) + self.collapse_nodes(new_node, cycle_path, g_graph, b_graph, c_graph) + for cycle_index in cycle_path: + c_graph.add_arc(new_node["address"], cycle_index) + # self.replaced_by[cycle_index] = new_node['address'] + + self.inner_nodes[new_node["address"]] = cycle_path + + # Add v_n+1 to list of unvisited vertices + unvisited_vertices.insert(0, nr_vertices + 1) + + # increment # of nodes counter + nr_vertices += 1 + + # Remove cycle nodes from b_graph; B = B - cycle c + for cycle_node_address in cycle_path: + b_graph.remove_by_address(cycle_node_address) + + logger.debug("g_graph: %s", g_graph) + logger.debug("b_graph: %s", b_graph) + logger.debug("c_graph: %s", c_graph) + logger.debug("Betas: %s", betas) + logger.debug("replaced nodes %s", self.inner_nodes) + + # Recover parse tree + logger.debug("Final scores: %s", self.scores) + + logger.debug("Recovering parse...") + for i in range(len(tokens) + 1, nr_vertices + 1): + betas[betas[i][1]] = betas[i] + + logger.debug("Betas: %s", betas) + for node in original_graph.nodes.values(): + # TODO: It's dangerous to assume that deps it a dictionary + # because it's a default dictionary. Ideally, here we should not + # be concerned how dependencies are stored inside of a dependency + # graph. + node["deps"] = {} + for i in range(1, len(tokens) + 1): + original_graph.add_arc(betas[i][0], betas[i][1]) + + logger.debug("Done.") + yield original_graph + + +################################################################# +# Rule-based Non-Projective Parser +################################################################# + + +class NonprojectiveDependencyParser: + """ + A non-projective, rule-based, dependency parser. This parser + will return the set of all possible non-projective parses based on + the word-to-word relations defined in the parser's dependency + grammar, and will allow the branches of the parse tree to cross + in order to capture a variety of linguistic phenomena that a + projective parser will not. + """ + + def __init__(self, dependency_grammar): + """ + Creates a new ``NonprojectiveDependencyParser``. + + :param dependency_grammar: a grammar of word-to-word relations. + :type dependency_grammar: DependencyGrammar + """ + self._grammar = dependency_grammar + + def parse(self, tokens): + """ + Parses the input tokens with respect to the parser's grammar. Parsing + is accomplished by representing the search-space of possible parses as + a fully-connected directed graph. Arcs that would lead to ungrammatical + parses are removed and a lattice is constructed of length n, where n is + the number of input tokens, to represent all possible grammatical + traversals. All possible paths through the lattice are then enumerated + to produce the set of non-projective parses. + + param tokens: A list of tokens to parse. + type tokens: list(str) + return: An iterator of non-projective parses. + rtype: iter(DependencyGraph) + """ + # Create graph representation of tokens + self._graph = DependencyGraph() + + for index, token in enumerate(tokens): + self._graph.nodes[index] = { + "word": token, + "deps": [], + "rel": "NTOP", + "address": index, + } + + for head_node in self._graph.nodes.values(): + deps = [] + for dep_node in self._graph.nodes.values(): + if ( + self._grammar.contains(head_node["word"], dep_node["word"]) + and head_node["word"] != dep_node["word"] + ): + deps.append(dep_node["address"]) + head_node["deps"] = deps + + # Create lattice of possible heads + roots = [] + possible_heads = [] + for i, word in enumerate(tokens): + heads = [] + for j, head in enumerate(tokens): + if (i != j) and self._grammar.contains(head, word): + heads.append(j) + if len(heads) == 0: + roots.append(i) + possible_heads.append(heads) + + # Set roots to attempt + if len(roots) < 2: + if len(roots) == 0: + for i in range(len(tokens)): + roots.append(i) + + # Traverse lattice + analyses = [] + for _ in roots: + stack = [] + analysis = [[] for i in range(len(possible_heads))] + i = 0 + forward = True + while i >= 0: + if forward: + if len(possible_heads[i]) == 1: + analysis[i] = possible_heads[i][0] + elif len(possible_heads[i]) == 0: + analysis[i] = -1 + else: + head = possible_heads[i].pop() + analysis[i] = head + stack.append([i, head]) + if not forward: + index_on_stack = False + for stack_item in stack: + if stack_item[0] == i: + index_on_stack = True + orig_length = len(possible_heads[i]) + + if index_on_stack and orig_length == 0: + for j in range(len(stack) - 1, -1, -1): + stack_item = stack[j] + if stack_item[0] == i: + possible_heads[i].append(stack.pop(j)[1]) + + elif index_on_stack and orig_length > 0: + head = possible_heads[i].pop() + analysis[i] = head + stack.append([i, head]) + forward = True + + if i + 1 == len(possible_heads): + analyses.append(analysis[:]) + forward = False + if forward: + i += 1 + else: + i -= 1 + + # Filter parses + # ensure 1 root, every thing has 1 head + for analysis in analyses: + if analysis.count(-1) > 1: + # there are several root elements! + continue + + graph = DependencyGraph() + graph.root = graph.nodes[analysis.index(-1) + 1] + + for address, (token, head_index) in enumerate( + zip(tokens, analysis), start=1 + ): + head_address = head_index + 1 + + node = graph.nodes[address] + node.update({"word": token, "address": address}) + + if head_address == 0: + rel = "ROOT" + else: + rel = "" + graph.nodes[head_index + 1]["deps"][rel].append(address) + + # TODO: check for cycles + yield graph + + +################################################################# +# Demos +################################################################# + + +def demo(): + # hall_demo() + nonprojective_conll_parse_demo() + rule_based_demo() + + +def hall_demo(): + npp = ProbabilisticNonprojectiveParser() + npp.train([], DemoScorer()) + for parse_graph in npp.parse(["v1", "v2", "v3"], [None, None, None]): + print(parse_graph) + + +def nonprojective_conll_parse_demo(): + from nltk.parse.dependencygraph import conll_data2 + + graphs = [DependencyGraph(entry) for entry in conll_data2.split("\n\n") if entry] + npp = ProbabilisticNonprojectiveParser() + npp.train(graphs, NaiveBayesDependencyScorer()) + for parse_graph in npp.parse( + ["Cathy", "zag", "hen", "zwaaien", "."], ["N", "V", "Pron", "Adj", "N", "Punc"] + ): + print(parse_graph) + + +def rule_based_demo(): + from nltk.grammar import DependencyGrammar + + grammar = DependencyGrammar.fromstring( + """ + 'taught' -> 'play' | 'man' + 'man' -> 'the' | 'in' + 'in' -> 'corner' + 'corner' -> 'the' + 'play' -> 'golf' | 'dachshund' | 'to' + 'dachshund' -> 'his' + """ + ) + print(grammar) + ndp = NonprojectiveDependencyParser(grammar) + graphs = ndp.parse( + [ + "the", + "man", + "in", + "the", + "corner", + "taught", + "his", + "dachshund", + "to", + "play", + "golf", + ] + ) + print("Graphs:") + for graph in graphs: + print(graph) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/pchart.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/pchart.py new file mode 100644 index 0000000..151164a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/pchart.py @@ -0,0 +1,579 @@ +# Natural Language Toolkit: Probabilistic Chart Parsers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Classes and interfaces for associating probabilities with tree +structures that represent the internal organization of a text. The +probabilistic parser module defines ``BottomUpProbabilisticChartParser``. + +``BottomUpProbabilisticChartParser`` is an abstract class that implements +a bottom-up chart parser for ``PCFG`` grammars. It maintains a queue of edges, +and adds them to the chart one at a time. The ordering of this queue +is based on the probabilities associated with the edges, allowing the +parser to expand more likely edges before less likely ones. Each +subclass implements a different queue ordering, producing different +search strategies. Currently the following subclasses are defined: + + - ``InsideChartParser`` searches edges in decreasing order of + their trees' inside probabilities. + - ``RandomChartParser`` searches edges in random order. + - ``LongestChartParser`` searches edges in decreasing order of their + location's length. + +The ``BottomUpProbabilisticChartParser`` constructor has an optional +argument beam_size. If non-zero, this controls the size of the beam +(aka the edge queue). This option is most useful with InsideChartParser. +""" + +##////////////////////////////////////////////////////// +## Bottom-Up PCFG Chart Parser +##////////////////////////////////////////////////////// + +# [XX] This might not be implemented quite right -- it would be better +# to associate probabilities with child pointer lists. + +import random +from functools import reduce + +from nltk.grammar import PCFG, Nonterminal +from nltk.parse.api import ParserI +from nltk.parse.chart import AbstractChartRule, Chart, LeafEdge, TreeEdge +from nltk.tree import ProbabilisticTree, Tree + + +# Probabilistic edges +class ProbabilisticLeafEdge(LeafEdge): + def prob(self): + return 1.0 + + +class ProbabilisticTreeEdge(TreeEdge): + def __init__(self, prob, *args, **kwargs): + TreeEdge.__init__(self, *args, **kwargs) + self._prob = prob + # two edges with different probabilities are not equal. + self._comparison_key = (self._comparison_key, prob) + + def prob(self): + return self._prob + + @staticmethod + def from_production(production, index, p): + return ProbabilisticTreeEdge( + p, (index, index), production.lhs(), production.rhs(), 0 + ) + + +# Rules using probabilistic edges +class ProbabilisticBottomUpInitRule(AbstractChartRule): + NUM_EDGES = 0 + + def apply(self, chart, grammar): + for index in range(chart.num_leaves()): + new_edge = ProbabilisticLeafEdge(chart.leaf(index), index) + if chart.insert(new_edge, ()): + yield new_edge + + +class ProbabilisticBottomUpPredictRule(AbstractChartRule): + NUM_EDGES = 1 + + def apply(self, chart, grammar, edge): + if edge.is_incomplete(): + return + for prod in grammar.productions(): + if edge.lhs() == prod.rhs()[0]: + new_edge = ProbabilisticTreeEdge.from_production( + prod, edge.start(), prod.prob() + ) + if chart.insert(new_edge, ()): + yield new_edge + + +class ProbabilisticFundamentalRule(AbstractChartRule): + NUM_EDGES = 2 + + def apply(self, chart, grammar, left_edge, right_edge): + # Make sure the rule is applicable. + if not ( + left_edge.end() == right_edge.start() + and left_edge.nextsym() == right_edge.lhs() + and left_edge.is_incomplete() + and right_edge.is_complete() + ): + return + + # Construct the new edge. + p = left_edge.prob() * right_edge.prob() + new_edge = ProbabilisticTreeEdge( + p, + span=(left_edge.start(), right_edge.end()), + lhs=left_edge.lhs(), + rhs=left_edge.rhs(), + dot=left_edge.dot() + 1, + ) + + # Add it to the chart, with appropriate child pointers. + changed_chart = False + for cpl1 in chart.child_pointer_lists(left_edge): + if chart.insert(new_edge, cpl1 + (right_edge,)): + changed_chart = True + + # If we changed the chart, then generate the edge. + if changed_chart: + yield new_edge + + +class SingleEdgeProbabilisticFundamentalRule(AbstractChartRule): + NUM_EDGES = 1 + + _fundamental_rule = ProbabilisticFundamentalRule() + + def apply(self, chart, grammar, edge1): + fr = self._fundamental_rule + if edge1.is_incomplete(): + # edge1 = left_edge; edge2 = right_edge + for edge2 in chart.select( + start=edge1.end(), is_complete=True, lhs=edge1.nextsym() + ): + yield from fr.apply(chart, grammar, edge1, edge2) + else: + # edge2 = left_edge; edge1 = right_edge + for edge2 in chart.select( + end=edge1.start(), is_complete=False, nextsym=edge1.lhs() + ): + yield from fr.apply(chart, grammar, edge2, edge1) + + def __str__(self): + return "Fundamental Rule" + + +class BottomUpProbabilisticChartParser(ParserI): + """ + An abstract bottom-up parser for ``PCFG`` grammars that uses a ``Chart`` to + record partial results. ``BottomUpProbabilisticChartParser`` maintains + a queue of edges that can be added to the chart. This queue is + initialized with edges for each token in the text that is being + parsed. ``BottomUpProbabilisticChartParser`` inserts these edges into + the chart one at a time, starting with the most likely edges, and + proceeding to less likely edges. For each edge that is added to + the chart, it may become possible to insert additional edges into + the chart; these are added to the queue. This process continues + until enough complete parses have been generated, or until the + queue is empty. + + The sorting order for the queue is not specified by + ``BottomUpProbabilisticChartParser``. Different sorting orders will + result in different search strategies. The sorting order for the + queue is defined by the method ``sort_queue``; subclasses are required + to provide a definition for this method. + + :type _grammar: PCFG + :ivar _grammar: The grammar used to parse sentences. + :type _trace: int + :ivar _trace: The level of tracing output that should be generated + when parsing a text. + """ + + def __init__(self, grammar, beam_size=0, trace=0): + """ + Create a new ``BottomUpProbabilisticChartParser``, that uses + ``grammar`` to parse texts. + + :type grammar: PCFG + :param grammar: The grammar used to parse texts. + :type beam_size: int + :param beam_size: The maximum length for the parser's edge queue. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + """ + if not isinstance(grammar, PCFG): + raise ValueError("The grammar must be probabilistic PCFG") + self._grammar = grammar + self.beam_size = beam_size + self._trace = trace + + def grammar(self): + return self._grammar + + def trace(self, trace=2): + """ + Set the level of tracing output that should be generated when + parsing a text. + + :type trace: int + :param trace: The trace level. A trace level of ``0`` will + generate no tracing output; and higher trace levels will + produce more verbose tracing output. + :rtype: None + """ + self._trace = trace + + # TODO: change this to conform more with the standard ChartParser + def parse(self, tokens): + self._grammar.check_coverage(tokens) + chart = Chart(list(tokens)) + grammar = self._grammar + + # Chart parser rules. + bu_init = ProbabilisticBottomUpInitRule() + bu = ProbabilisticBottomUpPredictRule() + fr = SingleEdgeProbabilisticFundamentalRule() + + # Our queue + queue = [] + + # Initialize the chart. + for edge in bu_init.apply(chart, grammar): + if self._trace > 1: + print( + " %-50s [%s]" + % (chart.pretty_format_edge(edge, width=2), edge.prob()) + ) + queue.append(edge) + + while len(queue) > 0: + # Re-sort the queue. + self.sort_queue(queue, chart) + + # Prune the queue to the correct size if a beam was defined + if self.beam_size: + self._prune(queue, chart) + + # Get the best edge. + edge = queue.pop() + if self._trace > 0: + print( + " %-50s [%s]" + % (chart.pretty_format_edge(edge, width=2), edge.prob()) + ) + + # Apply BU & FR to it. + queue.extend(bu.apply(chart, grammar, edge)) + queue.extend(fr.apply(chart, grammar, edge)) + + # Get a list of complete parses. + parses = list(chart.parses(grammar.start(), ProbabilisticTree)) + + # Assign probabilities to the trees. + prod_probs = {} + for prod in grammar.productions(): + prod_probs[prod.lhs(), prod.rhs()] = prod.prob() + for parse in parses: + self._setprob(parse, prod_probs) + + # Sort by probability + parses.sort(reverse=True, key=lambda tree: tree.prob()) + + return iter(parses) + + def _setprob(self, tree, prod_probs): + if tree.prob() is not None: + return + + # Get the prob of the CFG production. + lhs = Nonterminal(tree.label()) + rhs = [] + for child in tree: + if isinstance(child, Tree): + rhs.append(Nonterminal(child.label())) + else: + rhs.append(child) + prob = prod_probs[lhs, tuple(rhs)] + + # Get the probs of children. + for child in tree: + if isinstance(child, Tree): + self._setprob(child, prod_probs) + prob *= child.prob() + + tree.set_prob(prob) + + def sort_queue(self, queue, chart): + """ + Sort the given queue of ``Edge`` objects, placing the edge that should + be tried first at the beginning of the queue. This method + will be called after each ``Edge`` is added to the queue. + + :param queue: The queue of ``Edge`` objects to sort. Each edge in + this queue is an edge that could be added to the chart by + the fundamental rule; but that has not yet been added. + :type queue: list(Edge) + :param chart: The chart being used to parse the text. This + chart can be used to provide extra information for sorting + the queue. + :type chart: Chart + :rtype: None + """ + raise NotImplementedError() + + def _prune(self, queue, chart): + """Discard items in the queue if the queue is longer than the beam.""" + if len(queue) > self.beam_size: + split = len(queue) - self.beam_size + if self._trace > 2: + for edge in queue[:split]: + print(" %-50s [DISCARDED]" % chart.pretty_format_edge(edge, 2)) + del queue[:split] + + +class InsideChartParser(BottomUpProbabilisticChartParser): + """ + A bottom-up parser for ``PCFG`` grammars that tries edges in descending + order of the inside probabilities of their trees. The "inside + probability" of a tree is simply the + probability of the entire tree, ignoring its context. In + particular, the inside probability of a tree generated by + production *p* with children *c[1], c[2], ..., c[n]* is + *P(p)P(c[1])P(c[2])...P(c[n])*; and the inside + probability of a token is 1 if it is present in the text, and 0 if + it is absent. + + This sorting order results in a type of lowest-cost-first search + strategy. + """ + + # Inherit constructor. + def sort_queue(self, queue, chart): + """ + Sort the given queue of edges, in descending order of the + inside probabilities of the edges' trees. + + :param queue: The queue of ``Edge`` objects to sort. Each edge in + this queue is an edge that could be added to the chart by + the fundamental rule; but that has not yet been added. + :type queue: list(Edge) + :param chart: The chart being used to parse the text. This + chart can be used to provide extra information for sorting + the queue. + :type chart: Chart + :rtype: None + """ + queue.sort(key=lambda edge: edge.prob()) + + +# Eventually, this will become some sort of inside-outside parser: +# class InsideOutsideParser(BottomUpProbabilisticChartParser): +# def __init__(self, grammar, trace=0): +# # Inherit docs. +# BottomUpProbabilisticChartParser.__init__(self, grammar, trace) +# +# # Find the best path from S to each nonterminal +# bestp = {} +# for production in grammar.productions(): bestp[production.lhs()]=0 +# bestp[grammar.start()] = 1.0 +# +# for i in range(len(grammar.productions())): +# for production in grammar.productions(): +# lhs = production.lhs() +# for elt in production.rhs(): +# bestp[elt] = max(bestp[lhs]*production.prob(), +# bestp.get(elt,0)) +# +# self._bestp = bestp +# for (k,v) in self._bestp.items(): print(k,v) +# +# def _sortkey(self, edge): +# return edge.structure()[PROB] * self._bestp[edge.lhs()] +# +# def sort_queue(self, queue, chart): +# queue.sort(key=self._sortkey) + + +class RandomChartParser(BottomUpProbabilisticChartParser): + """ + A bottom-up parser for ``PCFG`` grammars that tries edges in random order. + This sorting order results in a random search strategy. + """ + + # Inherit constructor + def sort_queue(self, queue, chart): + i = random.randint(0, len(queue) - 1) + (queue[-1], queue[i]) = (queue[i], queue[-1]) + + +class UnsortedChartParser(BottomUpProbabilisticChartParser): + """ + A bottom-up parser for ``PCFG`` grammars that tries edges in whatever order. + """ + + # Inherit constructor + def sort_queue(self, queue, chart): + return + + +class LongestChartParser(BottomUpProbabilisticChartParser): + """ + A bottom-up parser for ``PCFG`` grammars that tries longer edges before + shorter ones. This sorting order results in a type of best-first + search strategy. + """ + + # Inherit constructor + def sort_queue(self, queue, chart): + queue.sort(key=lambda edge: edge.length()) + + +##////////////////////////////////////////////////////// +## Test Code +##////////////////////////////////////////////////////// + + +def demo(choice=None, draw_parses=None, print_parses=None): + """ + A demonstration of the probabilistic parsers. The user is + prompted to select which demo to run, and how many parses should + be found; and then each parser is run on the same demo, and a + summary of the results are displayed. + """ + import sys + import time + + from nltk import tokenize + from nltk.parse import pchart + + # Define two demos. Each demo has a sentence and a grammar. + toy_pcfg1 = PCFG.fromstring( + """ + S -> NP VP [1.0] + NP -> Det N [0.5] | NP PP [0.25] | 'John' [0.1] | 'I' [0.15] + Det -> 'the' [0.8] | 'my' [0.2] + N -> 'man' [0.5] | 'telescope' [0.5] + VP -> VP PP [0.1] | V NP [0.7] | V [0.2] + V -> 'ate' [0.35] | 'saw' [0.65] + PP -> P NP [1.0] + P -> 'with' [0.61] | 'under' [0.39] + """ + ) + + toy_pcfg2 = PCFG.fromstring( + """ + S -> NP VP [1.0] + VP -> V NP [.59] + VP -> V [.40] + VP -> VP PP [.01] + NP -> Det N [.41] + NP -> Name [.28] + NP -> NP PP [.31] + PP -> P NP [1.0] + V -> 'saw' [.21] + V -> 'ate' [.51] + V -> 'ran' [.28] + N -> 'boy' [.11] + N -> 'cookie' [.12] + N -> 'table' [.13] + N -> 'telescope' [.14] + N -> 'hill' [.5] + Name -> 'Jack' [.52] + Name -> 'Bob' [.48] + P -> 'with' [.61] + P -> 'under' [.39] + Det -> 'the' [.41] + Det -> 'a' [.31] + Det -> 'my' [.28] + """ + ) + + demos = [ + ("I saw John with my telescope", toy_pcfg1), + ("the boy saw Jack with Bob under the table with a telescope", toy_pcfg2), + ] + + if choice is None: + # Ask the user which demo they want to use. + print() + for i in range(len(demos)): + print(f"{i + 1:>3}: {demos[i][0]}") + print(" %r" % demos[i][1]) + print() + print("Which demo (%d-%d)? " % (1, len(demos)), end=" ") + choice = int(sys.stdin.readline().strip()) - 1 + try: + sent, grammar = demos[choice] + except: + print("Bad sentence number") + return + + # Tokenize the sentence. + tokens = sent.split() + + # Define a list of parsers. We'll use all parsers. + parsers = [ + pchart.InsideChartParser(grammar), + pchart.RandomChartParser(grammar), + pchart.UnsortedChartParser(grammar), + pchart.LongestChartParser(grammar), + pchart.InsideChartParser(grammar, beam_size=len(tokens) + 1), # was BeamParser + ] + + # Run the parsers on the tokenized sentence. + times = [] + average_p = [] + num_parses = [] + all_parses = {} + for parser in parsers: + print(f"\ns: {sent}\nparser: {parser}\ngrammar: {grammar}") + parser.trace(3) + t = time.time() + parses = list(parser.parse(tokens)) + times.append(time.time() - t) + p = reduce(lambda a, b: a + b.prob(), parses, 0) / len(parses) if parses else 0 + average_p.append(p) + num_parses.append(len(parses)) + for p in parses: + all_parses[p.freeze()] = 1 + + # Print some summary statistics + print() + print(" Parser Beam | Time (secs) # Parses Average P(parse)") + print("------------------------+------------------------------------------") + for i in range(len(parsers)): + print( + "%18s %4d |%11.4f%11d%19.14f" + % ( + parsers[i].__class__.__name__, + parsers[i].beam_size, + times[i], + num_parses[i], + average_p[i], + ) + ) + parses = all_parses.keys() + if parses: + p = reduce(lambda a, b: a + b.prob(), parses, 0) / len(parses) + else: + p = 0 + print("------------------------+------------------------------------------") + print("%18s |%11s%11d%19.14f" % ("(All Parses)", "n/a", len(parses), p)) + + if draw_parses is None: + # Ask the user if we should draw the parses. + print() + print("Draw parses (y/n)? ", end=" ") + draw_parses = sys.stdin.readline().strip().lower().startswith("y") + if draw_parses: + from nltk.draw.tree import draw_trees + + print(" please wait...") + draw_trees(*parses) + + if print_parses is None: + # Ask the user if we should print the parses. + print() + print("Print parses (y/n)? ", end=" ") + print_parses = sys.stdin.readline().strip().lower().startswith("y") + if print_parses: + for parse in parses: + print(parse) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/projectivedependencyparser.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/projectivedependencyparser.py new file mode 100644 index 0000000..f14796c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/projectivedependencyparser.py @@ -0,0 +1,716 @@ +# Natural Language Toolkit: Dependency Grammars +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Jason Narad +# +# URL: +# For license information, see LICENSE.TXT +# + +from collections import defaultdict +from functools import total_ordering +from itertools import chain + +from nltk.grammar import ( + DependencyGrammar, + DependencyProduction, + ProbabilisticDependencyGrammar, +) +from nltk.internals import raise_unorderable_types +from nltk.parse.dependencygraph import DependencyGraph + +################################################################# +# Dependency Span +################################################################# + + +@total_ordering +class DependencySpan: + """ + A contiguous span over some part of the input string representing + dependency (head -> modifier) relationships amongst words. An atomic + span corresponds to only one word so it isn't a 'span' in the conventional + sense, as its _start_index = _end_index = _head_index for concatenation + purposes. All other spans are assumed to have arcs between all nodes + within the start and end indexes of the span, and one head index corresponding + to the head word for the entire span. This is the same as the root node if + the dependency structure were depicted as a graph. + """ + + def __init__(self, start_index, end_index, head_index, arcs, tags): + self._start_index = start_index + self._end_index = end_index + self._head_index = head_index + self._arcs = arcs + self._tags = tags + self._comparison_key = (start_index, end_index, head_index, tuple(arcs)) + self._hash = hash(self._comparison_key) + + def head_index(self): + """ + :return: An value indexing the head of the entire ``DependencySpan``. + :rtype: int + """ + return self._head_index + + def __repr__(self): + """ + :return: A concise string representatino of the ``DependencySpan``. + :rtype: str. + """ + return "Span %d-%d; Head Index: %d" % ( + self._start_index, + self._end_index, + self._head_index, + ) + + def __str__(self): + """ + :return: A verbose string representation of the ``DependencySpan``. + :rtype: str + """ + str = "Span %d-%d; Head Index: %d" % ( + self._start_index, + self._end_index, + self._head_index, + ) + for i in range(len(self._arcs)): + str += "\n%d <- %d, %s" % (i, self._arcs[i], self._tags[i]) + return str + + def __eq__(self, other): + return ( + type(self) == type(other) and self._comparison_key == other._comparison_key + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, DependencySpan): + raise_unorderable_types("<", self, other) + return self._comparison_key < other._comparison_key + + def __hash__(self): + """ + :return: The hash value of this ``DependencySpan``. + """ + return self._hash + + +################################################################# +# Chart Cell +################################################################# + + +class ChartCell: + """ + A cell from the parse chart formed when performing the CYK algorithm. + Each cell keeps track of its x and y coordinates (though this will probably + be discarded), and a list of spans serving as the cell's entries. + """ + + def __init__(self, x, y): + """ + :param x: This cell's x coordinate. + :type x: int. + :param y: This cell's y coordinate. + :type y: int. + """ + self._x = x + self._y = y + self._entries = set() + + def add(self, span): + """ + Appends the given span to the list of spans + representing the chart cell's entries. + + :param span: The span to add. + :type span: DependencySpan + """ + self._entries.add(span) + + def __str__(self): + """ + :return: A verbose string representation of this ``ChartCell``. + :rtype: str. + """ + return "CC[%d,%d]: %s" % (self._x, self._y, self._entries) + + def __repr__(self): + """ + :return: A concise string representation of this ``ChartCell``. + :rtype: str. + """ + return "%s" % self + + +################################################################# +# Parsing with Dependency Grammars +################################################################# + + +class ProjectiveDependencyParser: + """ + A projective, rule-based, dependency parser. A ProjectiveDependencyParser + is created with a DependencyGrammar, a set of productions specifying + word-to-word dependency relations. The parse() method will then + return the set of all parses, in tree representation, for a given input + sequence of tokens. Each parse must meet the requirements of the both + the grammar and the projectivity constraint which specifies that the + branches of the dependency tree are not allowed to cross. Alternatively, + this can be understood as stating that each parent node and its children + in the parse tree form a continuous substring of the input sequence. + """ + + def __init__(self, dependency_grammar): + """ + Create a new ProjectiveDependencyParser, from a word-to-word + dependency grammar ``DependencyGrammar``. + + :param dependency_grammar: A word-to-word relation dependencygrammar. + :type dependency_grammar: DependencyGrammar + """ + self._grammar = dependency_grammar + + def parse(self, tokens): + """ + Performs a projective dependency parse on the list of tokens using + a chart-based, span-concatenation algorithm similar to Eisner (1996). + + :param tokens: The list of input tokens. + :type tokens: list(str) + :return: An iterator over parse trees. + :rtype: iter(Tree) + """ + self._tokens = list(tokens) + chart = [] + for i in range(0, len(self._tokens) + 1): + chart.append([]) + for j in range(0, len(self._tokens) + 1): + chart[i].append(ChartCell(i, j)) + if i == j + 1: + chart[i][j].add(DependencySpan(i - 1, i, i - 1, [-1], ["null"])) + + for i in range(1, len(self._tokens) + 1): + for j in range(i - 2, -1, -1): + for k in range(i - 1, j, -1): + for span1 in chart[k][j]._entries: + for span2 in chart[i][k]._entries: + for newspan in self.concatenate(span1, span2): + chart[i][j].add(newspan) + + for parse in chart[len(self._tokens)][0]._entries: + conll_format = "" + # malt_format = "" + for i in range(len(tokens)): + # malt_format += '%s\t%s\t%d\t%s\n' % (tokens[i], 'null', parse._arcs[i] + 1, 'null') + # conll_format += '\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n' % (i+1, tokens[i], tokens[i], 'null', 'null', 'null', parse._arcs[i] + 1, 'null', '-', '-') + # Modify to comply with the new Dependency Graph requirement (at least must have an root elements) + conll_format += "\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n" % ( + i + 1, + tokens[i], + tokens[i], + "null", + "null", + "null", + parse._arcs[i] + 1, + "ROOT", + "-", + "-", + ) + dg = DependencyGraph(conll_format) + # if self.meets_arity(dg): + yield dg.tree() + + def concatenate(self, span1, span2): + """ + Concatenates the two spans in whichever way possible. This + includes rightward concatenation (from the leftmost word of the + leftmost span to the rightmost word of the rightmost span) and + leftward concatenation (vice-versa) between adjacent spans. Unlike + Eisner's presentation of span concatenation, these spans do not + share or pivot on a particular word/word-index. + + :return: A list of new spans formed through concatenation. + :rtype: list(DependencySpan) + """ + spans = [] + if span1._start_index == span2._start_index: + print("Error: Mismatched spans - replace this with thrown error") + if span1._start_index > span2._start_index: + temp_span = span1 + span1 = span2 + span2 = temp_span + # adjacent rightward covered concatenation + new_arcs = span1._arcs + span2._arcs + new_tags = span1._tags + span2._tags + if self._grammar.contains( + self._tokens[span1._head_index], self._tokens[span2._head_index] + ): + # print('Performing rightward cover %d to %d' % (span1._head_index, span2._head_index)) + new_arcs[span2._head_index - span1._start_index] = span1._head_index + spans.append( + DependencySpan( + span1._start_index, + span2._end_index, + span1._head_index, + new_arcs, + new_tags, + ) + ) + # adjacent leftward covered concatenation + new_arcs = span1._arcs + span2._arcs + if self._grammar.contains( + self._tokens[span2._head_index], self._tokens[span1._head_index] + ): + # print('performing leftward cover %d to %d' % (span2._head_index, span1._head_index)) + new_arcs[span1._head_index - span1._start_index] = span2._head_index + spans.append( + DependencySpan( + span1._start_index, + span2._end_index, + span2._head_index, + new_arcs, + new_tags, + ) + ) + return spans + + +################################################################# +# Parsing with Probabilistic Dependency Grammars +################################################################# + + +class ProbabilisticProjectiveDependencyParser: + """A probabilistic, projective dependency parser. + + This parser returns the most probable projective parse derived from the + probabilistic dependency grammar derived from the train() method. The + probabilistic model is an implementation of Eisner's (1996) Model C, which + conditions on head-word, head-tag, child-word, and child-tag. The decoding + uses a bottom-up chart-based span concatenation algorithm that's identical + to the one utilized by the rule-based projective parser. + + Usage example + + >>> from nltk.parse.dependencygraph import conll_data2 + + >>> graphs = [ + ... DependencyGraph(entry) for entry in conll_data2.split('\\n\\n') if entry + ... ] + + >>> ppdp = ProbabilisticProjectiveDependencyParser() + >>> ppdp.train(graphs) + + >>> sent = ['Cathy', 'zag', 'hen', 'wild', 'zwaaien', '.'] + >>> list(ppdp.parse(sent)) + [Tree('zag', ['Cathy', 'hen', Tree('zwaaien', ['wild', '.'])])] + + """ + + def __init__(self): + """ + Create a new probabilistic dependency parser. No additional + operations are necessary. + """ + + def parse(self, tokens): + """ + Parses the list of tokens subject to the projectivity constraint + and the productions in the parser's grammar. This uses a method + similar to the span-concatenation algorithm defined in Eisner (1996). + It returns the most probable parse derived from the parser's + probabilistic dependency grammar. + """ + self._tokens = list(tokens) + chart = [] + for i in range(0, len(self._tokens) + 1): + chart.append([]) + for j in range(0, len(self._tokens) + 1): + chart[i].append(ChartCell(i, j)) + if i == j + 1: + if tokens[i - 1] in self._grammar._tags: + for tag in self._grammar._tags[tokens[i - 1]]: + chart[i][j].add( + DependencySpan(i - 1, i, i - 1, [-1], [tag]) + ) + else: + print( + "No tag found for input token '%s', parse is impossible." + % tokens[i - 1] + ) + return [] + for i in range(1, len(self._tokens) + 1): + for j in range(i - 2, -1, -1): + for k in range(i - 1, j, -1): + for span1 in chart[k][j]._entries: + for span2 in chart[i][k]._entries: + for newspan in self.concatenate(span1, span2): + chart[i][j].add(newspan) + trees = [] + max_parse = None + max_score = 0 + for parse in chart[len(self._tokens)][0]._entries: + conll_format = "" + malt_format = "" + for i in range(len(tokens)): + malt_format += "%s\t%s\t%d\t%s\n" % ( + tokens[i], + "null", + parse._arcs[i] + 1, + "null", + ) + # conll_format += '\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n' % (i+1, tokens[i], tokens[i], parse._tags[i], parse._tags[i], 'null', parse._arcs[i] + 1, 'null', '-', '-') + # Modify to comply with recent change in dependency graph such that there must be a ROOT element. + conll_format += "\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\n" % ( + i + 1, + tokens[i], + tokens[i], + parse._tags[i], + parse._tags[i], + "null", + parse._arcs[i] + 1, + "ROOT", + "-", + "-", + ) + dg = DependencyGraph(conll_format) + score = self.compute_prob(dg) + trees.append((score, dg.tree())) + trees.sort() + return (tree for (score, tree) in trees) + + def concatenate(self, span1, span2): + """ + Concatenates the two spans in whichever way possible. This + includes rightward concatenation (from the leftmost word of the + leftmost span to the rightmost word of the rightmost span) and + leftward concatenation (vice-versa) between adjacent spans. Unlike + Eisner's presentation of span concatenation, these spans do not + share or pivot on a particular word/word-index. + + :return: A list of new spans formed through concatenation. + :rtype: list(DependencySpan) + """ + spans = [] + if span1._start_index == span2._start_index: + print("Error: Mismatched spans - replace this with thrown error") + if span1._start_index > span2._start_index: + temp_span = span1 + span1 = span2 + span2 = temp_span + # adjacent rightward covered concatenation + new_arcs = span1._arcs + span2._arcs + new_tags = span1._tags + span2._tags + if self._grammar.contains( + self._tokens[span1._head_index], self._tokens[span2._head_index] + ): + new_arcs[span2._head_index - span1._start_index] = span1._head_index + spans.append( + DependencySpan( + span1._start_index, + span2._end_index, + span1._head_index, + new_arcs, + new_tags, + ) + ) + # adjacent leftward covered concatenation + new_arcs = span1._arcs + span2._arcs + new_tags = span1._tags + span2._tags + if self._grammar.contains( + self._tokens[span2._head_index], self._tokens[span1._head_index] + ): + new_arcs[span1._head_index - span1._start_index] = span2._head_index + spans.append( + DependencySpan( + span1._start_index, + span2._end_index, + span2._head_index, + new_arcs, + new_tags, + ) + ) + return spans + + def train(self, graphs): + """ + Trains a ProbabilisticDependencyGrammar based on the list of input + DependencyGraphs. This model is an implementation of Eisner's (1996) + Model C, which derives its statistics from head-word, head-tag, + child-word, and child-tag relationships. + + :param graphs: A list of dependency graphs to train from. + :type: list(DependencyGraph) + """ + productions = [] + events = defaultdict(int) + tags = {} + for dg in graphs: + for node_index in range(1, len(dg.nodes)): + # children = dg.nodes[node_index]['deps'] + children = list( + chain.from_iterable(dg.nodes[node_index]["deps"].values()) + ) + + nr_left_children = dg.left_children(node_index) + nr_right_children = dg.right_children(node_index) + nr_children = nr_left_children + nr_right_children + for child_index in range( + 0 - (nr_left_children + 1), nr_right_children + 2 + ): + head_word = dg.nodes[node_index]["word"] + head_tag = dg.nodes[node_index]["tag"] + if head_word in tags: + tags[head_word].add(head_tag) + else: + tags[head_word] = {head_tag} + child = "STOP" + child_tag = "STOP" + prev_word = "START" + prev_tag = "START" + if child_index < 0: + array_index = child_index + nr_left_children + if array_index >= 0: + child = dg.nodes[children[array_index]]["word"] + child_tag = dg.nodes[children[array_index]]["tag"] + if child_index != -1: + prev_word = dg.nodes[children[array_index + 1]]["word"] + prev_tag = dg.nodes[children[array_index + 1]]["tag"] + if child != "STOP": + productions.append(DependencyProduction(head_word, [child])) + head_event = "(head ({} {}) (mods ({}, {}, {}) left))".format( + child, + child_tag, + prev_tag, + head_word, + head_tag, + ) + mod_event = "(mods ({}, {}, {}) left))".format( + prev_tag, + head_word, + head_tag, + ) + events[head_event] += 1 + events[mod_event] += 1 + elif child_index > 0: + array_index = child_index + nr_left_children - 1 + if array_index < nr_children: + child = dg.nodes[children[array_index]]["word"] + child_tag = dg.nodes[children[array_index]]["tag"] + if child_index != 1: + prev_word = dg.nodes[children[array_index - 1]]["word"] + prev_tag = dg.nodes[children[array_index - 1]]["tag"] + if child != "STOP": + productions.append(DependencyProduction(head_word, [child])) + head_event = "(head ({} {}) (mods ({}, {}, {}) right))".format( + child, + child_tag, + prev_tag, + head_word, + head_tag, + ) + mod_event = "(mods ({}, {}, {}) right))".format( + prev_tag, + head_word, + head_tag, + ) + events[head_event] += 1 + events[mod_event] += 1 + self._grammar = ProbabilisticDependencyGrammar(productions, events, tags) + + def compute_prob(self, dg): + """ + Computes the probability of a dependency graph based + on the parser's probability model (defined by the parser's + statistical dependency grammar). + + :param dg: A dependency graph to score. + :type dg: DependencyGraph + :return: The probability of the dependency graph. + :rtype: int + """ + prob = 1.0 + for node_index in range(1, len(dg.nodes)): + # children = dg.nodes[node_index]['deps'] + children = list(chain.from_iterable(dg.nodes[node_index]["deps"].values())) + + nr_left_children = dg.left_children(node_index) + nr_right_children = dg.right_children(node_index) + nr_children = nr_left_children + nr_right_children + for child_index in range(0 - (nr_left_children + 1), nr_right_children + 2): + head_word = dg.nodes[node_index]["word"] + head_tag = dg.nodes[node_index]["tag"] + child = "STOP" + child_tag = "STOP" + prev_word = "START" + prev_tag = "START" + if child_index < 0: + array_index = child_index + nr_left_children + if array_index >= 0: + child = dg.nodes[children[array_index]]["word"] + child_tag = dg.nodes[children[array_index]]["tag"] + if child_index != -1: + prev_word = dg.nodes[children[array_index + 1]]["word"] + prev_tag = dg.nodes[children[array_index + 1]]["tag"] + head_event = "(head ({} {}) (mods ({}, {}, {}) left))".format( + child, + child_tag, + prev_tag, + head_word, + head_tag, + ) + mod_event = "(mods ({}, {}, {}) left))".format( + prev_tag, + head_word, + head_tag, + ) + h_count = self._grammar._events[head_event] + m_count = self._grammar._events[mod_event] + + # If the grammar is not covered + if m_count != 0: + prob *= h_count / m_count + else: + prob = 0.00000001 # Very small number + + elif child_index > 0: + array_index = child_index + nr_left_children - 1 + if array_index < nr_children: + child = dg.nodes[children[array_index]]["word"] + child_tag = dg.nodes[children[array_index]]["tag"] + if child_index != 1: + prev_word = dg.nodes[children[array_index - 1]]["word"] + prev_tag = dg.nodes[children[array_index - 1]]["tag"] + head_event = "(head ({} {}) (mods ({}, {}, {}) right))".format( + child, + child_tag, + prev_tag, + head_word, + head_tag, + ) + mod_event = "(mods ({}, {}, {}) right))".format( + prev_tag, + head_word, + head_tag, + ) + h_count = self._grammar._events[head_event] + m_count = self._grammar._events[mod_event] + + if m_count != 0: + prob *= h_count / m_count + else: + prob = 0.00000001 # Very small number + + return prob + + +################################################################# +# Demos +################################################################# + + +def demo(): + projective_rule_parse_demo() + # arity_parse_demo() + projective_prob_parse_demo() + + +def projective_rule_parse_demo(): + """ + A demonstration showing the creation and use of a + ``DependencyGrammar`` to perform a projective dependency + parse. + """ + grammar = DependencyGrammar.fromstring( + """ + 'scratch' -> 'cats' | 'walls' + 'walls' -> 'the' + 'cats' -> 'the' + """ + ) + print(grammar) + pdp = ProjectiveDependencyParser(grammar) + trees = pdp.parse(["the", "cats", "scratch", "the", "walls"]) + for tree in trees: + print(tree) + + +def arity_parse_demo(): + """ + A demonstration showing the creation of a ``DependencyGrammar`` + in which a specific number of modifiers is listed for a given + head. This can further constrain the number of possible parses + created by a ``ProjectiveDependencyParser``. + """ + print() + print("A grammar with no arity constraints. Each DependencyProduction") + print("specifies a relationship between one head word and only one") + print("modifier word.") + grammar = DependencyGrammar.fromstring( + """ + 'fell' -> 'price' | 'stock' + 'price' -> 'of' | 'the' + 'of' -> 'stock' + 'stock' -> 'the' + """ + ) + print(grammar) + + print() + print("For the sentence 'The price of the stock fell', this grammar") + print("will produce the following three parses:") + pdp = ProjectiveDependencyParser(grammar) + trees = pdp.parse(["the", "price", "of", "the", "stock", "fell"]) + for tree in trees: + print(tree) + + print() + print("By contrast, the following grammar contains a ") + print("DependencyProduction that specifies a relationship") + print("between a single head word, 'price', and two modifier") + print("words, 'of' and 'the'.") + grammar = DependencyGrammar.fromstring( + """ + 'fell' -> 'price' | 'stock' + 'price' -> 'of' 'the' + 'of' -> 'stock' + 'stock' -> 'the' + """ + ) + print(grammar) + + print() + print( + "This constrains the number of possible parses to just one:" + ) # unimplemented, soon to replace + pdp = ProjectiveDependencyParser(grammar) + trees = pdp.parse(["the", "price", "of", "the", "stock", "fell"]) + for tree in trees: + print(tree) + + +def projective_prob_parse_demo(): + """ + A demo showing the training and use of a projective + dependency parser. + """ + from nltk.parse.dependencygraph import conll_data2 + + graphs = [DependencyGraph(entry) for entry in conll_data2.split("\n\n") if entry] + ppdp = ProbabilisticProjectiveDependencyParser() + print("Training Probabilistic Projective Dependency Parser...") + ppdp.train(graphs) + + sent = ["Cathy", "zag", "hen", "wild", "zwaaien", "."] + print("Parsing '", " ".join(sent), "'...") + print("Parse:") + for tree in ppdp.parse(sent): + print(tree) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/recursivedescent.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/recursivedescent.py new file mode 100644 index 0000000..a4f0c58 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/recursivedescent.py @@ -0,0 +1,684 @@ +# Natural Language Toolkit: Recursive Descent Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from nltk.grammar import Nonterminal +from nltk.parse.api import ParserI +from nltk.tree import ImmutableTree, Tree + + +##////////////////////////////////////////////////////// +## Recursive Descent Parser +##////////////////////////////////////////////////////// +class RecursiveDescentParser(ParserI): + """ + A simple top-down CFG parser that parses texts by recursively + expanding the fringe of a Tree, and matching it against a + text. + + ``RecursiveDescentParser`` uses a list of tree locations called a + "frontier" to remember which subtrees have not yet been expanded + and which leaves have not yet been matched against the text. Each + tree location consists of a list of child indices specifying the + path from the root of the tree to a subtree or a leaf; see the + reference documentation for Tree for more information + about tree locations. + + When the parser begins parsing a text, it constructs a tree + containing only the start symbol, and a frontier containing the + location of the tree's root node. It then extends the tree to + cover the text, using the following recursive procedure: + + - If the frontier is empty, and the text is covered by the tree, + then return the tree as a possible parse. + - If the frontier is empty, and the text is not covered by the + tree, then return no parses. + - If the first element of the frontier is a subtree, then + use CFG productions to "expand" it. For each applicable + production, add the expanded subtree's children to the + frontier, and recursively find all parses that can be + generated by the new tree and frontier. + - If the first element of the frontier is a token, then "match" + it against the next token from the text. Remove the token + from the frontier, and recursively find all parses that can be + generated by the new tree and frontier. + + :see: ``nltk.grammar`` + """ + + def __init__(self, grammar, trace=0): + """ + Create a new ``RecursiveDescentParser``, that uses ``grammar`` + to parse texts. + + :type grammar: CFG + :param grammar: The grammar used to parse texts. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + """ + self._grammar = grammar + self._trace = trace + + def grammar(self): + return self._grammar + + def parse(self, tokens): + # Inherit docs from ParserI + + tokens = list(tokens) + self._grammar.check_coverage(tokens) + + # Start a recursive descent parse, with an initial tree + # containing just the start symbol. + start = self._grammar.start().symbol() + initial_tree = Tree(start, []) + frontier = [()] + if self._trace: + self._trace_start(initial_tree, frontier, tokens) + return self._parse(tokens, initial_tree, frontier) + + def _parse(self, remaining_text, tree, frontier): + """ + Recursively expand and match each elements of ``tree`` + specified by ``frontier``, to cover ``remaining_text``. Return + a list of all parses found. + + :return: An iterator of all parses that can be generated by + matching and expanding the elements of ``tree`` + specified by ``frontier``. + :rtype: iter(Tree) + :type tree: Tree + :param tree: A partial structure for the text that is + currently being parsed. The elements of ``tree`` + that are specified by ``frontier`` have not yet been + expanded or matched. + :type remaining_text: list(str) + :param remaining_text: The portion of the text that is not yet + covered by ``tree``. + :type frontier: list(tuple(int)) + :param frontier: A list of the locations within ``tree`` of + all subtrees that have not yet been expanded, and all + leaves that have not yet been matched. This list sorted + in left-to-right order of location within the tree. + """ + + # If the tree covers the text, and there's nothing left to + # expand, then we've found a complete parse; return it. + if len(remaining_text) == 0 and len(frontier) == 0: + if self._trace: + self._trace_succeed(tree, frontier) + yield tree + + # If there's still text, but nothing left to expand, we failed. + elif len(frontier) == 0: + if self._trace: + self._trace_backtrack(tree, frontier) + + # If the next element on the frontier is a tree, expand it. + elif isinstance(tree[frontier[0]], Tree): + yield from self._expand(remaining_text, tree, frontier) + + # If the next element on the frontier is a token, match it. + else: + yield from self._match(remaining_text, tree, frontier) + + def _match(self, rtext, tree, frontier): + """ + :rtype: iter(Tree) + :return: an iterator of all parses that can be generated by + matching the first element of ``frontier`` against the + first token in ``rtext``. In particular, if the first + element of ``frontier`` has the same type as the first + token in ``rtext``, then substitute the token into + ``tree``; and return all parses that can be generated by + matching and expanding the remaining elements of + ``frontier``. If the first element of ``frontier`` does not + have the same type as the first token in ``rtext``, then + return empty list. + + :type tree: Tree + :param tree: A partial structure for the text that is + currently being parsed. The elements of ``tree`` + that are specified by ``frontier`` have not yet been + expanded or matched. + :type rtext: list(str) + :param rtext: The portion of the text that is not yet + covered by ``tree``. + :type frontier: list of tuple of int + :param frontier: A list of the locations within ``tree`` of + all subtrees that have not yet been expanded, and all + leaves that have not yet been matched. + """ + + tree_leaf = tree[frontier[0]] + if len(rtext) > 0 and tree_leaf == rtext[0]: + # If it's a terminal that matches rtext[0], then substitute + # in the token, and continue parsing. + newtree = tree.copy(deep=True) + newtree[frontier[0]] = rtext[0] + if self._trace: + self._trace_match(newtree, frontier[1:], rtext[0]) + yield from self._parse(rtext[1:], newtree, frontier[1:]) + else: + # If it's a non-matching terminal, fail. + if self._trace: + self._trace_backtrack(tree, frontier, rtext[:1]) + + def _expand(self, remaining_text, tree, frontier, production=None): + """ + :rtype: iter(Tree) + :return: An iterator of all parses that can be generated by + expanding the first element of ``frontier`` with + ``production``. In particular, if the first element of + ``frontier`` is a subtree whose node type is equal to + ``production``'s left hand side, then add a child to that + subtree for each element of ``production``'s right hand + side; and return all parses that can be generated by + matching and expanding the remaining elements of + ``frontier``. If the first element of ``frontier`` is not a + subtree whose node type is equal to ``production``'s left + hand side, then return an empty list. If ``production`` is + not specified, then return a list of all parses that can + be generated by expanding the first element of ``frontier`` + with *any* CFG production. + + :type tree: Tree + :param tree: A partial structure for the text that is + currently being parsed. The elements of ``tree`` + that are specified by ``frontier`` have not yet been + expanded or matched. + :type remaining_text: list(str) + :param remaining_text: The portion of the text that is not yet + covered by ``tree``. + :type frontier: list(tuple(int)) + :param frontier: A list of the locations within ``tree`` of + all subtrees that have not yet been expanded, and all + leaves that have not yet been matched. + """ + + if production is None: + productions = self._grammar.productions() + else: + productions = [production] + + for production in productions: + lhs = production.lhs().symbol() + if lhs == tree[frontier[0]].label(): + subtree = self._production_to_tree(production) + if frontier[0] == (): + newtree = subtree + else: + newtree = tree.copy(deep=True) + newtree[frontier[0]] = subtree + new_frontier = [ + frontier[0] + (i,) for i in range(len(production.rhs())) + ] + if self._trace: + self._trace_expand(newtree, new_frontier, production) + yield from self._parse( + remaining_text, newtree, new_frontier + frontier[1:] + ) + + def _production_to_tree(self, production): + """ + :rtype: Tree + :return: The Tree that is licensed by ``production``. + In particular, given the production ``[lhs -> elt[1] ... elt[n]]`` + return a tree that has a node ``lhs.symbol``, and + ``n`` children. For each nonterminal element + ``elt[i]`` in the production, the tree token has a + childless subtree with node value ``elt[i].symbol``; and + for each terminal element ``elt[j]``, the tree token has + a leaf token with type ``elt[j]``. + + :param production: The CFG production that licenses the tree + token that should be returned. + :type production: Production + """ + children = [] + for elt in production.rhs(): + if isinstance(elt, Nonterminal): + children.append(Tree(elt.symbol(), [])) + else: + # This will be matched. + children.append(elt) + return Tree(production.lhs().symbol(), children) + + def trace(self, trace=2): + """ + Set the level of tracing output that should be generated when + parsing a text. + + :type trace: int + :param trace: The trace level. A trace level of ``0`` will + generate no tracing output; and higher trace levels will + produce more verbose tracing output. + :rtype: None + """ + self._trace = trace + + def _trace_fringe(self, tree, treeloc=None): + """ + Print trace output displaying the fringe of ``tree``. The + fringe of ``tree`` consists of all of its leaves and all of + its childless subtrees. + + :rtype: None + """ + + if treeloc == (): + print("*", end=" ") + if isinstance(tree, Tree): + if len(tree) == 0: + print(repr(Nonterminal(tree.label())), end=" ") + for i in range(len(tree)): + if treeloc is not None and i == treeloc[0]: + self._trace_fringe(tree[i], treeloc[1:]) + else: + self._trace_fringe(tree[i]) + else: + print(repr(tree), end=" ") + + def _trace_tree(self, tree, frontier, operation): + """ + Print trace output displaying the parser's current state. + + :param operation: A character identifying the operation that + generated the current state. + :rtype: None + """ + if self._trace == 2: + print(" %c [" % operation, end=" ") + else: + print(" [", end=" ") + if len(frontier) > 0: + self._trace_fringe(tree, frontier[0]) + else: + self._trace_fringe(tree) + print("]") + + def _trace_start(self, tree, frontier, text): + print("Parsing %r" % " ".join(text)) + if self._trace > 2: + print("Start:") + if self._trace > 1: + self._trace_tree(tree, frontier, " ") + + def _trace_expand(self, tree, frontier, production): + if self._trace > 2: + print("Expand: %s" % production) + if self._trace > 1: + self._trace_tree(tree, frontier, "E") + + def _trace_match(self, tree, frontier, tok): + if self._trace > 2: + print("Match: %r" % tok) + if self._trace > 1: + self._trace_tree(tree, frontier, "M") + + def _trace_succeed(self, tree, frontier): + if self._trace > 2: + print("GOOD PARSE:") + if self._trace == 1: + print("Found a parse:\n%s" % tree) + if self._trace > 1: + self._trace_tree(tree, frontier, "+") + + def _trace_backtrack(self, tree, frontier, toks=None): + if self._trace > 2: + if toks: + print("Backtrack: %r match failed" % toks[0]) + else: + print("Backtrack") + + +##////////////////////////////////////////////////////// +## Stepping Recursive Descent Parser +##////////////////////////////////////////////////////// +class SteppingRecursiveDescentParser(RecursiveDescentParser): + """ + A ``RecursiveDescentParser`` that allows you to step through the + parsing process, performing a single operation at a time. + + The ``initialize`` method is used to start parsing a text. + ``expand`` expands the first element on the frontier using a single + CFG production, and ``match`` matches the first element on the + frontier against the next text token. ``backtrack`` undoes the most + recent expand or match operation. ``step`` performs a single + expand, match, or backtrack operation. ``parses`` returns the set + of parses that have been found by the parser. + + :ivar _history: A list of ``(rtext, tree, frontier)`` tripples, + containing the previous states of the parser. This history is + used to implement the ``backtrack`` operation. + :ivar _tried_e: A record of all productions that have been tried + for a given tree. This record is used by ``expand`` to perform + the next untried production. + :ivar _tried_m: A record of what tokens have been matched for a + given tree. This record is used by ``step`` to decide whether + or not to match a token. + :see: ``nltk.grammar`` + """ + + def __init__(self, grammar, trace=0): + super().__init__(grammar, trace) + self._rtext = None + self._tree = None + self._frontier = [()] + self._tried_e = {} + self._tried_m = {} + self._history = [] + self._parses = [] + + # [XX] TEMPORARY HACK WARNING! This should be replaced with + # something nicer when we get the chance. + def _freeze(self, tree): + c = tree.copy() + # for pos in c.treepositions('leaves'): + # c[pos] = c[pos].freeze() + return ImmutableTree.convert(c) + + def parse(self, tokens): + tokens = list(tokens) + self.initialize(tokens) + while self.step() is not None: + pass + return self.parses() + + def initialize(self, tokens): + """ + Start parsing a given text. This sets the parser's tree to + the start symbol, its frontier to the root node, and its + remaining text to ``token['SUBTOKENS']``. + """ + + self._rtext = tokens + start = self._grammar.start().symbol() + self._tree = Tree(start, []) + self._frontier = [()] + self._tried_e = {} + self._tried_m = {} + self._history = [] + self._parses = [] + if self._trace: + self._trace_start(self._tree, self._frontier, self._rtext) + + def remaining_text(self): + """ + :return: The portion of the text that is not yet covered by the + tree. + :rtype: list(str) + """ + return self._rtext + + def frontier(self): + """ + :return: A list of the tree locations of all subtrees that + have not yet been expanded, and all leaves that have not + yet been matched. + :rtype: list(tuple(int)) + """ + return self._frontier + + def tree(self): + """ + :return: A partial structure for the text that is + currently being parsed. The elements specified by the + frontier have not yet been expanded or matched. + :rtype: Tree + """ + return self._tree + + def step(self): + """ + Perform a single parsing operation. If an untried match is + possible, then perform the match, and return the matched + token. If an untried expansion is possible, then perform the + expansion, and return the production that it is based on. If + backtracking is possible, then backtrack, and return True. + Otherwise, return None. + + :return: None if no operation was performed; a token if a match + was performed; a production if an expansion was performed; + and True if a backtrack operation was performed. + :rtype: Production or String or bool + """ + # Try matching (if we haven't already) + if self.untried_match(): + token = self.match() + if token is not None: + return token + + # Try expanding. + production = self.expand() + if production is not None: + return production + + # Try backtracking + if self.backtrack(): + self._trace_backtrack(self._tree, self._frontier) + return True + + # Nothing left to do. + return None + + def expand(self, production=None): + """ + Expand the first element of the frontier. In particular, if + the first element of the frontier is a subtree whose node type + is equal to ``production``'s left hand side, then add a child + to that subtree for each element of ``production``'s right hand + side. If ``production`` is not specified, then use the first + untried expandable production. If all expandable productions + have been tried, do nothing. + + :return: The production used to expand the frontier, if an + expansion was performed. If no expansion was performed, + return None. + :rtype: Production or None + """ + + # Make sure we *can* expand. + if len(self._frontier) == 0: + return None + if not isinstance(self._tree[self._frontier[0]], Tree): + return None + + # If they didn't specify a production, check all untried ones. + if production is None: + productions = self.untried_expandable_productions() + else: + productions = [production] + + parses = [] + for prod in productions: + # Record that we've tried this production now. + self._tried_e.setdefault(self._freeze(self._tree), []).append(prod) + + # Try expanding. + for _result in self._expand(self._rtext, self._tree, self._frontier, prod): + return prod + + # We didn't expand anything. + return None + + def match(self): + """ + Match the first element of the frontier. In particular, if + the first element of the frontier has the same type as the + next text token, then substitute the text token into the tree. + + :return: The token matched, if a match operation was + performed. If no match was performed, return None + :rtype: str or None + """ + + # Record that we've tried matching this token. + tok = self._rtext[0] + self._tried_m.setdefault(self._freeze(self._tree), []).append(tok) + + # Make sure we *can* match. + if len(self._frontier) == 0: + return None + if isinstance(self._tree[self._frontier[0]], Tree): + return None + + for _result in self._match(self._rtext, self._tree, self._frontier): + # Return the token we just matched. + return self._history[-1][0][0] + return None + + def backtrack(self): + """ + Return the parser to its state before the most recent + match or expand operation. Calling ``undo`` repeatedly return + the parser to successively earlier states. If no match or + expand operations have been performed, ``undo`` will make no + changes. + + :return: true if an operation was successfully undone. + :rtype: bool + """ + if len(self._history) == 0: + return False + (self._rtext, self._tree, self._frontier) = self._history.pop() + return True + + def expandable_productions(self): + """ + :return: A list of all the productions for which expansions + are available for the current parser state. + :rtype: list(Production) + """ + # Make sure we *can* expand. + if len(self._frontier) == 0: + return [] + frontier_child = self._tree[self._frontier[0]] + if len(self._frontier) == 0 or not isinstance(frontier_child, Tree): + return [] + + return [ + p + for p in self._grammar.productions() + if p.lhs().symbol() == frontier_child.label() + ] + + def untried_expandable_productions(self): + """ + :return: A list of all the untried productions for which + expansions are available for the current parser state. + :rtype: list(Production) + """ + + tried_expansions = self._tried_e.get(self._freeze(self._tree), []) + return [p for p in self.expandable_productions() if p not in tried_expansions] + + def untried_match(self): + """ + :return: Whether the first element of the frontier is a token + that has not yet been matched. + :rtype: bool + """ + + if len(self._rtext) == 0: + return False + tried_matches = self._tried_m.get(self._freeze(self._tree), []) + return self._rtext[0] not in tried_matches + + def currently_complete(self): + """ + :return: Whether the parser's current state represents a + complete parse. + :rtype: bool + """ + return len(self._frontier) == 0 and len(self._rtext) == 0 + + def _parse(self, remaining_text, tree, frontier): + """ + A stub version of ``_parse`` that sets the parsers current + state to the given arguments. In ``RecursiveDescentParser``, + the ``_parse`` method is used to recursively continue parsing a + text. ``SteppingRecursiveDescentParser`` overrides it to + capture these recursive calls. It records the parser's old + state in the history (to allow for backtracking), and updates + the parser's new state using the given arguments. Finally, it + returns ``[1]``, which is used by ``match`` and ``expand`` to + detect whether their operations were successful. + + :return: ``[1]`` + :rtype: list of int + """ + self._history.append((self._rtext, self._tree, self._frontier)) + self._rtext = remaining_text + self._tree = tree + self._frontier = frontier + + # Is it a good parse? If so, record it. + if len(frontier) == 0 and len(remaining_text) == 0: + self._parses.append(tree) + self._trace_succeed(self._tree, self._frontier) + + return [1] + + def parses(self): + """ + :return: An iterator of the parses that have been found by this + parser so far. + :rtype: list of Tree + """ + return iter(self._parses) + + def set_grammar(self, grammar): + """ + Change the grammar used to parse texts. + + :param grammar: The new grammar. + :type grammar: CFG + """ + self._grammar = grammar + + +##////////////////////////////////////////////////////// +## Demonstration Code +##////////////////////////////////////////////////////// + + +def demo(): + """ + A demonstration of the recursive descent parser. + """ + + from nltk import CFG, parse + + grammar = CFG.fromstring( + """ + S -> NP VP + NP -> Det N | Det N PP + VP -> V NP | V NP PP + PP -> P NP + NP -> 'I' + N -> 'man' | 'park' | 'telescope' | 'dog' + Det -> 'the' | 'a' + P -> 'in' | 'with' + V -> 'saw' + """ + ) + + for prod in grammar.productions(): + print(prod) + + sent = "I saw a man in the park".split() + parser = parse.RecursiveDescentParser(grammar, trace=2) + for p in parser.parse(sent): + print(p) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/shiftreduce.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/shiftreduce.py new file mode 100644 index 0000000..cd6a79f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/shiftreduce.py @@ -0,0 +1,479 @@ +# Natural Language Toolkit: Shift-Reduce Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from nltk.grammar import Nonterminal +from nltk.parse.api import ParserI +from nltk.tree import Tree + + +##////////////////////////////////////////////////////// +## Shift/Reduce Parser +##////////////////////////////////////////////////////// +class ShiftReduceParser(ParserI): + """ + A simple bottom-up CFG parser that uses two operations, "shift" + and "reduce", to find a single parse for a text. + + ``ShiftReduceParser`` maintains a stack, which records the + structure of a portion of the text. This stack is a list of + strings and Trees that collectively cover a portion of + the text. For example, while parsing the sentence "the dog saw + the man" with a typical grammar, ``ShiftReduceParser`` will produce + the following stack, which covers "the dog saw":: + + [(NP: (Det: 'the') (N: 'dog')), (V: 'saw')] + + ``ShiftReduceParser`` attempts to extend the stack to cover the + entire text, and to combine the stack elements into a single tree, + producing a complete parse for the sentence. + + Initially, the stack is empty. It is extended to cover the text, + from left to right, by repeatedly applying two operations: + + - "shift" moves a token from the beginning of the text to the + end of the stack. + - "reduce" uses a CFG production to combine the rightmost stack + elements into a single Tree. + + Often, more than one operation can be performed on a given stack. + In this case, ``ShiftReduceParser`` uses the following heuristics + to decide which operation to perform: + + - Only shift if no reductions are available. + - If multiple reductions are available, then apply the reduction + whose CFG production is listed earliest in the grammar. + + Note that these heuristics are not guaranteed to choose an + operation that leads to a parse of the text. Also, if multiple + parses exists, ``ShiftReduceParser`` will return at most one of + them. + + :see: ``nltk.grammar`` + """ + + def __init__(self, grammar, trace=0): + """ + Create a new ``ShiftReduceParser``, that uses ``grammar`` to + parse texts. + + :type grammar: Grammar + :param grammar: The grammar used to parse texts. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + """ + self._grammar = grammar + self._trace = trace + self._check_grammar() + + def grammar(self): + return self._grammar + + def parse(self, tokens): + tokens = list(tokens) + self._grammar.check_coverage(tokens) + + # initialize the stack. + stack = [] + remaining_text = tokens + + # Trace output. + if self._trace: + print("Parsing %r" % " ".join(tokens)) + self._trace_stack(stack, remaining_text) + + # iterate through the text, pushing the token onto + # the stack, then reducing the stack. + while len(remaining_text) > 0: + self._shift(stack, remaining_text) + while self._reduce(stack, remaining_text): + pass + + # Did we reduce everything? + if len(stack) == 1: + # Did we end up with the right category? + if stack[0].label() == self._grammar.start().symbol(): + yield stack[0] + + def _shift(self, stack, remaining_text): + """ + Move a token from the beginning of ``remaining_text`` to the + end of ``stack``. + + :type stack: list(str and Tree) + :param stack: A list of strings and Trees, encoding + the structure of the text that has been parsed so far. + :type remaining_text: list(str) + :param remaining_text: The portion of the text that is not yet + covered by ``stack``. + :rtype: None + """ + stack.append(remaining_text[0]) + remaining_text.remove(remaining_text[0]) + if self._trace: + self._trace_shift(stack, remaining_text) + + def _match_rhs(self, rhs, rightmost_stack): + """ + :rtype: bool + :return: true if the right hand side of a CFG production + matches the rightmost elements of the stack. ``rhs`` + matches ``rightmost_stack`` if they are the same length, + and each element of ``rhs`` matches the corresponding + element of ``rightmost_stack``. A nonterminal element of + ``rhs`` matches any Tree whose node value is equal + to the nonterminal's symbol. A terminal element of ``rhs`` + matches any string whose type is equal to the terminal. + :type rhs: list(terminal and Nonterminal) + :param rhs: The right hand side of a CFG production. + :type rightmost_stack: list(string and Tree) + :param rightmost_stack: The rightmost elements of the parser's + stack. + """ + + if len(rightmost_stack) != len(rhs): + return False + for i in range(len(rightmost_stack)): + if isinstance(rightmost_stack[i], Tree): + if not isinstance(rhs[i], Nonterminal): + return False + if rightmost_stack[i].label() != rhs[i].symbol(): + return False + else: + if isinstance(rhs[i], Nonterminal): + return False + if rightmost_stack[i] != rhs[i]: + return False + return True + + def _reduce(self, stack, remaining_text, production=None): + """ + Find a CFG production whose right hand side matches the + rightmost stack elements; and combine those stack elements + into a single Tree, with the node specified by the + production's left-hand side. If more than one CFG production + matches the stack, then use the production that is listed + earliest in the grammar. The new Tree replaces the + elements in the stack. + + :rtype: Production or None + :return: If a reduction is performed, then return the CFG + production that the reduction is based on; otherwise, + return false. + :type stack: list(string and Tree) + :param stack: A list of strings and Trees, encoding + the structure of the text that has been parsed so far. + :type remaining_text: list(str) + :param remaining_text: The portion of the text that is not yet + covered by ``stack``. + """ + if production is None: + productions = self._grammar.productions() + else: + productions = [production] + + # Try each production, in order. + for production in productions: + rhslen = len(production.rhs()) + + # check if the RHS of a production matches the top of the stack + if self._match_rhs(production.rhs(), stack[-rhslen:]): + + # combine the tree to reflect the reduction + tree = Tree(production.lhs().symbol(), stack[-rhslen:]) + stack[-rhslen:] = [tree] + + # We reduced something + if self._trace: + self._trace_reduce(stack, production, remaining_text) + return production + + # We didn't reduce anything + return None + + def trace(self, trace=2): + """ + Set the level of tracing output that should be generated when + parsing a text. + + :type trace: int + :param trace: The trace level. A trace level of ``0`` will + generate no tracing output; and higher trace levels will + produce more verbose tracing output. + :rtype: None + """ + # 1: just show shifts. + # 2: show shifts & reduces + # 3: display which tokens & productions are shifed/reduced + self._trace = trace + + def _trace_stack(self, stack, remaining_text, marker=" "): + """ + Print trace output displaying the given stack and text. + + :rtype: None + :param marker: A character that is printed to the left of the + stack. This is used with trace level 2 to print 'S' + before shifted stacks and 'R' before reduced stacks. + """ + s = " " + marker + " [ " + for elt in stack: + if isinstance(elt, Tree): + s += repr(Nonterminal(elt.label())) + " " + else: + s += repr(elt) + " " + s += "* " + " ".join(remaining_text) + "]" + print(s) + + def _trace_shift(self, stack, remaining_text): + """ + Print trace output displaying that a token has been shifted. + + :rtype: None + """ + if self._trace > 2: + print("Shift %r:" % stack[-1]) + if self._trace == 2: + self._trace_stack(stack, remaining_text, "S") + elif self._trace > 0: + self._trace_stack(stack, remaining_text) + + def _trace_reduce(self, stack, production, remaining_text): + """ + Print trace output displaying that ``production`` was used to + reduce ``stack``. + + :rtype: None + """ + if self._trace > 2: + rhs = " ".join(production.rhs()) + print(f"Reduce {production.lhs()!r} <- {rhs}") + if self._trace == 2: + self._trace_stack(stack, remaining_text, "R") + elif self._trace > 1: + self._trace_stack(stack, remaining_text) + + def _check_grammar(self): + """ + Check to make sure that all of the CFG productions are + potentially useful. If any productions can never be used, + then print a warning. + + :rtype: None + """ + productions = self._grammar.productions() + + # Any production whose RHS is an extension of another production's RHS + # will never be used. + for i in range(len(productions)): + for j in range(i + 1, len(productions)): + rhs1 = productions[i].rhs() + rhs2 = productions[j].rhs() + if rhs1[: len(rhs2)] == rhs2: + print("Warning: %r will never be used" % productions[i]) + + +##////////////////////////////////////////////////////// +## Stepping Shift/Reduce Parser +##////////////////////////////////////////////////////// +class SteppingShiftReduceParser(ShiftReduceParser): + """ + A ``ShiftReduceParser`` that allows you to setp through the parsing + process, performing a single operation at a time. It also allows + you to change the parser's grammar midway through parsing a text. + + The ``initialize`` method is used to start parsing a text. + ``shift`` performs a single shift operation, and ``reduce`` performs + a single reduce operation. ``step`` will perform a single reduce + operation if possible; otherwise, it will perform a single shift + operation. ``parses`` returns the set of parses that have been + found by the parser. + + :ivar _history: A list of ``(stack, remaining_text)`` pairs, + containing all of the previous states of the parser. This + history is used to implement the ``undo`` operation. + :see: ``nltk.grammar`` + """ + + def __init__(self, grammar, trace=0): + super().__init__(grammar, trace) + self._stack = None + self._remaining_text = None + self._history = [] + + def parse(self, tokens): + tokens = list(tokens) + self.initialize(tokens) + while self.step(): + pass + return self.parses() + + def stack(self): + """ + :return: The parser's stack. + :rtype: list(str and Tree) + """ + return self._stack + + def remaining_text(self): + """ + :return: The portion of the text that is not yet covered by the + stack. + :rtype: list(str) + """ + return self._remaining_text + + def initialize(self, tokens): + """ + Start parsing a given text. This sets the parser's stack to + ``[]`` and sets its remaining text to ``tokens``. + """ + self._stack = [] + self._remaining_text = tokens + self._history = [] + + def step(self): + """ + Perform a single parsing operation. If a reduction is + possible, then perform that reduction, and return the + production that it is based on. Otherwise, if a shift is + possible, then perform it, and return True. Otherwise, + return False. + + :return: False if no operation was performed; True if a shift was + performed; and the CFG production used to reduce if a + reduction was performed. + :rtype: Production or bool + """ + return self.reduce() or self.shift() + + def shift(self): + """ + Move a token from the beginning of the remaining text to the + end of the stack. If there are no more tokens in the + remaining text, then do nothing. + + :return: True if the shift operation was successful. + :rtype: bool + """ + if len(self._remaining_text) == 0: + return False + self._history.append((self._stack[:], self._remaining_text[:])) + self._shift(self._stack, self._remaining_text) + return True + + def reduce(self, production=None): + """ + Use ``production`` to combine the rightmost stack elements into + a single Tree. If ``production`` does not match the + rightmost stack elements, then do nothing. + + :return: The production used to reduce the stack, if a + reduction was performed. If no reduction was performed, + return None. + + :rtype: Production or None + """ + self._history.append((self._stack[:], self._remaining_text[:])) + return_val = self._reduce(self._stack, self._remaining_text, production) + + if not return_val: + self._history.pop() + return return_val + + def undo(self): + """ + Return the parser to its state before the most recent + shift or reduce operation. Calling ``undo`` repeatedly return + the parser to successively earlier states. If no shift or + reduce operations have been performed, ``undo`` will make no + changes. + + :return: true if an operation was successfully undone. + :rtype: bool + """ + if len(self._history) == 0: + return False + (self._stack, self._remaining_text) = self._history.pop() + return True + + def reducible_productions(self): + """ + :return: A list of the productions for which reductions are + available for the current parser state. + :rtype: list(Production) + """ + productions = [] + for production in self._grammar.productions(): + rhslen = len(production.rhs()) + if self._match_rhs(production.rhs(), self._stack[-rhslen:]): + productions.append(production) + return productions + + def parses(self): + """ + :return: An iterator of the parses that have been found by this + parser so far. + :rtype: iter(Tree) + """ + if ( + len(self._remaining_text) == 0 + and len(self._stack) == 1 + and self._stack[0].label() == self._grammar.start().symbol() + ): + yield self._stack[0] + + # copied from nltk.parser + + def set_grammar(self, grammar): + """ + Change the grammar used to parse texts. + + :param grammar: The new grammar. + :type grammar: CFG + """ + self._grammar = grammar + + +##////////////////////////////////////////////////////// +## Demonstration Code +##////////////////////////////////////////////////////// + + +def demo(): + """ + A demonstration of the shift-reduce parser. + """ + + from nltk import CFG, parse + + grammar = CFG.fromstring( + """ + S -> NP VP + NP -> Det N | Det N PP + VP -> V NP | V NP PP + PP -> P NP + NP -> 'I' + N -> 'man' | 'park' | 'telescope' | 'dog' + Det -> 'the' | 'a' + P -> 'in' | 'with' + V -> 'saw' + """ + ) + + sent = "I saw a man in the park".split() + + parser = parse.ShiftReduceParser(grammar, trace=2) + for p in parser.parse(sent): + print(p) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/stanford.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/stanford.py new file mode 100644 index 0000000..188b6ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/stanford.py @@ -0,0 +1,470 @@ +# Natural Language Toolkit: Interface to the Stanford Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Xu +# +# URL: +# For license information, see LICENSE.TXT + +import os +import tempfile +import warnings +from subprocess import PIPE + +from nltk.internals import ( + _java_options, + config_java, + find_jar_iter, + find_jars_within_path, + java, +) +from nltk.parse.api import ParserI +from nltk.parse.dependencygraph import DependencyGraph +from nltk.tree import Tree + +_stanford_url = "https://nlp.stanford.edu/software/lex-parser.shtml" + + +class GenericStanfordParser(ParserI): + """Interface to the Stanford Parser""" + + _MODEL_JAR_PATTERN = r"stanford-parser-(\d+)(\.(\d+))+-models\.jar" + _JAR = r"stanford-parser\.jar" + _MAIN_CLASS = "edu.stanford.nlp.parser.lexparser.LexicalizedParser" + + _USE_STDIN = False + _DOUBLE_SPACED_OUTPUT = False + + def __init__( + self, + path_to_jar=None, + path_to_models_jar=None, + model_path="edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz", + encoding="utf8", + verbose=False, + java_options="-mx4g", + corenlp_options="", + ): + + # find the most recent code and model jar + stanford_jar = max( + find_jar_iter( + self._JAR, + path_to_jar, + env_vars=("STANFORD_PARSER", "STANFORD_CORENLP"), + searchpath=(), + url=_stanford_url, + verbose=verbose, + is_regex=True, + ), + key=lambda model_path: os.path.dirname(model_path), + ) + + model_jar = max( + find_jar_iter( + self._MODEL_JAR_PATTERN, + path_to_models_jar, + env_vars=("STANFORD_MODELS", "STANFORD_CORENLP"), + searchpath=(), + url=_stanford_url, + verbose=verbose, + is_regex=True, + ), + key=lambda model_path: os.path.dirname(model_path), + ) + + # self._classpath = (stanford_jar, model_jar) + + # Adding logging jar files to classpath + stanford_dir = os.path.split(stanford_jar)[0] + self._classpath = tuple([model_jar] + find_jars_within_path(stanford_dir)) + + self.model_path = model_path + self._encoding = encoding + self.corenlp_options = corenlp_options + self.java_options = java_options + + def _parse_trees_output(self, output_): + res = [] + cur_lines = [] + cur_trees = [] + blank = False + for line in output_.splitlines(False): + if line == "": + if blank: + res.append(iter(cur_trees)) + cur_trees = [] + blank = False + elif self._DOUBLE_SPACED_OUTPUT: + cur_trees.append(self._make_tree("\n".join(cur_lines))) + cur_lines = [] + blank = True + else: + res.append(iter([self._make_tree("\n".join(cur_lines))])) + cur_lines = [] + else: + cur_lines.append(line) + blank = False + return iter(res) + + def parse_sents(self, sentences, verbose=False): + """ + Use StanfordParser to parse multiple sentences. Takes multiple sentences as a + list where each sentence is a list of words. + Each sentence will be automatically tagged with this StanfordParser instance's + tagger. + If whitespaces exists inside a token, then the token will be treated as + separate tokens. + + :param sentences: Input sentences to parse + :type sentences: list(list(str)) + :rtype: iter(iter(Tree)) + """ + cmd = [ + self._MAIN_CLASS, + "-model", + self.model_path, + "-sentences", + "newline", + "-outputFormat", + self._OUTPUT_FORMAT, + "-tokenized", + "-escaper", + "edu.stanford.nlp.process.PTBEscapingProcessor", + ] + return self._parse_trees_output( + self._execute( + cmd, "\n".join(" ".join(sentence) for sentence in sentences), verbose + ) + ) + + def raw_parse(self, sentence, verbose=False): + """ + Use StanfordParser to parse a sentence. Takes a sentence as a string; + before parsing, it will be automatically tokenized and tagged by + the Stanford Parser. + + :param sentence: Input sentence to parse + :type sentence: str + :rtype: iter(Tree) + """ + return next(self.raw_parse_sents([sentence], verbose)) + + def raw_parse_sents(self, sentences, verbose=False): + """ + Use StanfordParser to parse multiple sentences. Takes multiple sentences as a + list of strings. + Each sentence will be automatically tokenized and tagged by the Stanford Parser. + + :param sentences: Input sentences to parse + :type sentences: list(str) + :rtype: iter(iter(Tree)) + """ + cmd = [ + self._MAIN_CLASS, + "-model", + self.model_path, + "-sentences", + "newline", + "-outputFormat", + self._OUTPUT_FORMAT, + ] + return self._parse_trees_output( + self._execute(cmd, "\n".join(sentences), verbose) + ) + + def tagged_parse(self, sentence, verbose=False): + """ + Use StanfordParser to parse a sentence. Takes a sentence as a list of + (word, tag) tuples; the sentence must have already been tokenized and + tagged. + + :param sentence: Input sentence to parse + :type sentence: list(tuple(str, str)) + :rtype: iter(Tree) + """ + return next(self.tagged_parse_sents([sentence], verbose)) + + def tagged_parse_sents(self, sentences, verbose=False): + """ + Use StanfordParser to parse multiple sentences. Takes multiple sentences + where each sentence is a list of (word, tag) tuples. + The sentences must have already been tokenized and tagged. + + :param sentences: Input sentences to parse + :type sentences: list(list(tuple(str, str))) + :rtype: iter(iter(Tree)) + """ + tag_separator = "/" + cmd = [ + self._MAIN_CLASS, + "-model", + self.model_path, + "-sentences", + "newline", + "-outputFormat", + self._OUTPUT_FORMAT, + "-tokenized", + "-tagSeparator", + tag_separator, + "-tokenizerFactory", + "edu.stanford.nlp.process.WhitespaceTokenizer", + "-tokenizerMethod", + "newCoreLabelTokenizerFactory", + ] + # We don't need to escape slashes as "splitting is done on the last instance of the character in the token" + return self._parse_trees_output( + self._execute( + cmd, + "\n".join( + " ".join(tag_separator.join(tagged) for tagged in sentence) + for sentence in sentences + ), + verbose, + ) + ) + + def _execute(self, cmd, input_, verbose=False): + encoding = self._encoding + cmd.extend(["-encoding", encoding]) + if self.corenlp_options: + cmd.extend(self.corenlp_options.split()) + + default_options = " ".join(_java_options) + + # Configure java. + config_java(options=self.java_options, verbose=verbose) + + # Windows is incompatible with NamedTemporaryFile() without passing in delete=False. + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as input_file: + # Write the actual sentences to the temporary input file + if isinstance(input_, str) and encoding: + input_ = input_.encode(encoding) + input_file.write(input_) + input_file.flush() + + # Run the tagger and get the output. + if self._USE_STDIN: + input_file.seek(0) + stdout, stderr = java( + cmd, + classpath=self._classpath, + stdin=input_file, + stdout=PIPE, + stderr=PIPE, + ) + else: + cmd.append(input_file.name) + stdout, stderr = java( + cmd, classpath=self._classpath, stdout=PIPE, stderr=PIPE + ) + + stdout = stdout.replace(b"\xc2\xa0", b" ") + stdout = stdout.replace(b"\x00\xa0", b" ") + stdout = stdout.decode(encoding) + + os.unlink(input_file.name) + + # Return java configurations to their default values. + config_java(options=default_options, verbose=False) + + return stdout + + +class StanfordParser(GenericStanfordParser): + """ + >>> parser=StanfordParser( + ... model_path="edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz" + ... ) # doctest: +SKIP + + >>> list(parser.raw_parse("the quick brown fox jumps over the lazy dog")) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('ROOT', [Tree('NP', [Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['quick']), Tree('JJ', ['brown']), + Tree('NN', ['fox'])]), Tree('NP', [Tree('NP', [Tree('NNS', ['jumps'])]), Tree('PP', [Tree('IN', ['over']), + Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['lazy']), Tree('NN', ['dog'])])])])])])] + + >>> sum([list(dep_graphs) for dep_graphs in parser.raw_parse_sents(( + ... "the quick brown fox jumps over the lazy dog", + ... "the quick grey wolf jumps over the lazy fox" + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('ROOT', [Tree('NP', [Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['quick']), Tree('JJ', ['brown']), + Tree('NN', ['fox'])]), Tree('NP', [Tree('NP', [Tree('NNS', ['jumps'])]), Tree('PP', [Tree('IN', ['over']), + Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['lazy']), Tree('NN', ['dog'])])])])])]), Tree('ROOT', [Tree('NP', + [Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['quick']), Tree('JJ', ['grey']), Tree('NN', ['wolf'])]), Tree('NP', + [Tree('NP', [Tree('NNS', ['jumps'])]), Tree('PP', [Tree('IN', ['over']), Tree('NP', [Tree('DT', ['the']), + Tree('JJ', ['lazy']), Tree('NN', ['fox'])])])])])])] + + >>> sum([list(dep_graphs) for dep_graphs in parser.parse_sents(( + ... "I 'm a dog".split(), + ... "This is my friends ' cat ( the tabby )".split(), + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('ROOT', [Tree('S', [Tree('NP', [Tree('PRP', ['I'])]), Tree('VP', [Tree('VBP', ["'m"]), + Tree('NP', [Tree('DT', ['a']), Tree('NN', ['dog'])])])])]), Tree('ROOT', [Tree('S', [Tree('NP', + [Tree('DT', ['This'])]), Tree('VP', [Tree('VBZ', ['is']), Tree('NP', [Tree('NP', [Tree('NP', [Tree('PRP$', ['my']), + Tree('NNS', ['friends']), Tree('POS', ["'"])]), Tree('NN', ['cat'])]), Tree('PRN', [Tree('-LRB-', [Tree('', []), + Tree('NP', [Tree('DT', ['the']), Tree('NN', ['tabby'])]), Tree('-RRB-', [])])])])])])])] + + >>> sum([list(dep_graphs) for dep_graphs in parser.tagged_parse_sents(( + ... ( + ... ("The", "DT"), + ... ("quick", "JJ"), + ... ("brown", "JJ"), + ... ("fox", "NN"), + ... ("jumped", "VBD"), + ... ("over", "IN"), + ... ("the", "DT"), + ... ("lazy", "JJ"), + ... ("dog", "NN"), + ... (".", "."), + ... ), + ... ))],[]) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('ROOT', [Tree('S', [Tree('NP', [Tree('DT', ['The']), Tree('JJ', ['quick']), Tree('JJ', ['brown']), + Tree('NN', ['fox'])]), Tree('VP', [Tree('VBD', ['jumped']), Tree('PP', [Tree('IN', ['over']), Tree('NP', + [Tree('DT', ['the']), Tree('JJ', ['lazy']), Tree('NN', ['dog'])])])]), Tree('.', ['.'])])])] + """ + + _OUTPUT_FORMAT = "penn" + + def __init__(self, *args, **kwargs): + warnings.warn( + "The StanfordParser will be deprecated\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPParser\033[0m instead.", + DeprecationWarning, + stacklevel=2, + ) + + super().__init__(*args, **kwargs) + + def _make_tree(self, result): + return Tree.fromstring(result) + + +class StanfordDependencyParser(GenericStanfordParser): + + """ + >>> dep_parser=StanfordDependencyParser( + ... model_path="edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz" + ... ) # doctest: +SKIP + + >>> [parse.tree() for parse in dep_parser.raw_parse("The quick brown fox jumps over the lazy dog.")] # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('jumps', [Tree('fox', ['The', 'quick', 'brown']), Tree('dog', ['over', 'the', 'lazy'])])] + + >>> [list(parse.triples()) for parse in dep_parser.raw_parse("The quick brown fox jumps over the lazy dog.")] # doctest: +NORMALIZE_WHITESPACE +SKIP + [[((u'jumps', u'VBZ'), u'nsubj', (u'fox', u'NN')), ((u'fox', u'NN'), u'det', (u'The', u'DT')), + ((u'fox', u'NN'), u'amod', (u'quick', u'JJ')), ((u'fox', u'NN'), u'amod', (u'brown', u'JJ')), + ((u'jumps', u'VBZ'), u'nmod', (u'dog', u'NN')), ((u'dog', u'NN'), u'case', (u'over', u'IN')), + ((u'dog', u'NN'), u'det', (u'the', u'DT')), ((u'dog', u'NN'), u'amod', (u'lazy', u'JJ'))]] + + >>> sum([[parse.tree() for parse in dep_graphs] for dep_graphs in dep_parser.raw_parse_sents(( + ... "The quick brown fox jumps over the lazy dog.", + ... "The quick grey wolf jumps over the lazy fox." + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('jumps', [Tree('fox', ['The', 'quick', 'brown']), Tree('dog', ['over', 'the', 'lazy'])]), + Tree('jumps', [Tree('wolf', ['The', 'quick', 'grey']), Tree('fox', ['over', 'the', 'lazy'])])] + + >>> sum([[parse.tree() for parse in dep_graphs] for dep_graphs in dep_parser.parse_sents(( + ... "I 'm a dog".split(), + ... "This is my friends ' cat ( the tabby )".split(), + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('dog', ['I', "'m", 'a']), Tree('cat', ['This', 'is', Tree('friends', ['my', "'"]), Tree('tabby', ['the'])])] + + >>> sum([[list(parse.triples()) for parse in dep_graphs] for dep_graphs in dep_parser.tagged_parse_sents(( + ... ( + ... ("The", "DT"), + ... ("quick", "JJ"), + ... ("brown", "JJ"), + ... ("fox", "NN"), + ... ("jumped", "VBD"), + ... ("over", "IN"), + ... ("the", "DT"), + ... ("lazy", "JJ"), + ... ("dog", "NN"), + ... (".", "."), + ... ), + ... ))],[]) # doctest: +NORMALIZE_WHITESPACE +SKIP + [[((u'jumped', u'VBD'), u'nsubj', (u'fox', u'NN')), ((u'fox', u'NN'), u'det', (u'The', u'DT')), + ((u'fox', u'NN'), u'amod', (u'quick', u'JJ')), ((u'fox', u'NN'), u'amod', (u'brown', u'JJ')), + ((u'jumped', u'VBD'), u'nmod', (u'dog', u'NN')), ((u'dog', u'NN'), u'case', (u'over', u'IN')), + ((u'dog', u'NN'), u'det', (u'the', u'DT')), ((u'dog', u'NN'), u'amod', (u'lazy', u'JJ'))]] + + """ + + _OUTPUT_FORMAT = "conll2007" + + def __init__(self, *args, **kwargs): + warnings.warn( + "The StanfordDependencyParser will be deprecated\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPDependencyParser\033[0m instead.", + DeprecationWarning, + stacklevel=2, + ) + + super().__init__(*args, **kwargs) + + def _make_tree(self, result): + return DependencyGraph(result, top_relation_label="root") + + +class StanfordNeuralDependencyParser(GenericStanfordParser): + """ + >>> from nltk.parse.stanford import StanfordNeuralDependencyParser # doctest: +SKIP + >>> dep_parser=StanfordNeuralDependencyParser(java_options='-mx4g')# doctest: +SKIP + + >>> [parse.tree() for parse in dep_parser.raw_parse("The quick brown fox jumps over the lazy dog.")] # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('jumps', [Tree('fox', ['The', 'quick', 'brown']), Tree('dog', ['over', 'the', 'lazy']), '.'])] + + >>> [list(parse.triples()) for parse in dep_parser.raw_parse("The quick brown fox jumps over the lazy dog.")] # doctest: +NORMALIZE_WHITESPACE +SKIP + [[((u'jumps', u'VBZ'), u'nsubj', (u'fox', u'NN')), ((u'fox', u'NN'), u'det', + (u'The', u'DT')), ((u'fox', u'NN'), u'amod', (u'quick', u'JJ')), ((u'fox', u'NN'), + u'amod', (u'brown', u'JJ')), ((u'jumps', u'VBZ'), u'nmod', (u'dog', u'NN')), + ((u'dog', u'NN'), u'case', (u'over', u'IN')), ((u'dog', u'NN'), u'det', + (u'the', u'DT')), ((u'dog', u'NN'), u'amod', (u'lazy', u'JJ')), ((u'jumps', u'VBZ'), + u'punct', (u'.', u'.'))]] + + >>> sum([[parse.tree() for parse in dep_graphs] for dep_graphs in dep_parser.raw_parse_sents(( + ... "The quick brown fox jumps over the lazy dog.", + ... "The quick grey wolf jumps over the lazy fox." + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('jumps', [Tree('fox', ['The', 'quick', 'brown']), Tree('dog', ['over', + 'the', 'lazy']), '.']), Tree('jumps', [Tree('wolf', ['The', 'quick', 'grey']), + Tree('fox', ['over', 'the', 'lazy']), '.'])] + + >>> sum([[parse.tree() for parse in dep_graphs] for dep_graphs in dep_parser.parse_sents(( + ... "I 'm a dog".split(), + ... "This is my friends ' cat ( the tabby )".split(), + ... ))], []) # doctest: +NORMALIZE_WHITESPACE +SKIP + [Tree('dog', ['I', "'m", 'a']), Tree('cat', ['This', 'is', Tree('friends', + ['my', "'"]), Tree('tabby', ['-LRB-', 'the', '-RRB-'])])] + """ + + _OUTPUT_FORMAT = "conll" + _MAIN_CLASS = "edu.stanford.nlp.pipeline.StanfordCoreNLP" + _JAR = r"stanford-corenlp-(\d+)(\.(\d+))+\.jar" + _MODEL_JAR_PATTERN = r"stanford-corenlp-(\d+)(\.(\d+))+-models\.jar" + _USE_STDIN = True + _DOUBLE_SPACED_OUTPUT = True + + def __init__(self, *args, **kwargs): + warnings.warn( + "The StanfordNeuralDependencyParser will be deprecated\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPDependencyParser\033[0m instead.", + DeprecationWarning, + stacklevel=2, + ) + + super().__init__(*args, **kwargs) + self.corenlp_options += "-annotators tokenize,ssplit,pos,depparse" + + def tagged_parse_sents(self, sentences, verbose=False): + """ + Currently unimplemented because the neural dependency parser (and + the StanfordCoreNLP pipeline class) doesn't support passing in pre- + tagged tokens. + """ + raise NotImplementedError( + "tagged_parse[_sents] is not supported by " + "StanfordNeuralDependencyParser; use " + "parse[_sents] or raw_parse[_sents] instead." + ) + + def _make_tree(self, result): + return DependencyGraph(result, top_relation_label="ROOT") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/transitionparser.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/transitionparser.py new file mode 100644 index 0000000..b0dd022 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/transitionparser.py @@ -0,0 +1,794 @@ +# Natural Language Toolkit: Arc-Standard and Arc-eager Transition Based Parsers +# +# Author: Long Duong +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +import pickle +import tempfile +from copy import deepcopy +from operator import itemgetter +from os import remove + +try: + from numpy import array + from scipy import sparse + from sklearn import svm + from sklearn.datasets import load_svmlight_file +except ImportError: + pass + +from nltk.parse import DependencyEvaluator, DependencyGraph, ParserI + + +class Configuration: + """ + Class for holding configuration which is the partial analysis of the input sentence. + The transition based parser aims at finding set of operators that transfer the initial + configuration to the terminal configuration. + + The configuration includes: + - Stack: for storing partially proceeded words + - Buffer: for storing remaining input words + - Set of arcs: for storing partially built dependency tree + + This class also provides a method to represent a configuration as list of features. + """ + + def __init__(self, dep_graph): + """ + :param dep_graph: the representation of an input in the form of dependency graph. + :type dep_graph: DependencyGraph where the dependencies are not specified. + """ + # dep_graph.nodes contain list of token for a sentence + self.stack = [0] # The root element + self.buffer = list(range(1, len(dep_graph.nodes))) # The rest is in the buffer + self.arcs = [] # empty set of arc + self._tokens = dep_graph.nodes + self._max_address = len(self.buffer) + + def __str__(self): + return ( + "Stack : " + + str(self.stack) + + " Buffer : " + + str(self.buffer) + + " Arcs : " + + str(self.arcs) + ) + + def _check_informative(self, feat, flag=False): + """ + Check whether a feature is informative + The flag control whether "_" is informative or not + """ + if feat is None: + return False + if feat == "": + return False + if flag is False: + if feat == "_": + return False + return True + + def extract_features(self): + """ + Extract the set of features for the current configuration. Implement standard features as describe in + Table 3.2 (page 31) in Dependency Parsing book by Sandra Kubler, Ryan McDonal, Joakim Nivre. + Please note that these features are very basic. + :return: list(str) + """ + result = [] + # Todo : can come up with more complicated features set for better + # performance. + if len(self.stack) > 0: + # Stack 0 + stack_idx0 = self.stack[len(self.stack) - 1] + token = self._tokens[stack_idx0] + if self._check_informative(token["word"], True): + result.append("STK_0_FORM_" + token["word"]) + if "lemma" in token and self._check_informative(token["lemma"]): + result.append("STK_0_LEMMA_" + token["lemma"]) + if self._check_informative(token["tag"]): + result.append("STK_0_POS_" + token["tag"]) + if "feats" in token and self._check_informative(token["feats"]): + feats = token["feats"].split("|") + for feat in feats: + result.append("STK_0_FEATS_" + feat) + # Stack 1 + if len(self.stack) > 1: + stack_idx1 = self.stack[len(self.stack) - 2] + token = self._tokens[stack_idx1] + if self._check_informative(token["tag"]): + result.append("STK_1_POS_" + token["tag"]) + + # Left most, right most dependency of stack[0] + left_most = 1000000 + right_most = -1 + dep_left_most = "" + dep_right_most = "" + for (wi, r, wj) in self.arcs: + if wi == stack_idx0: + if (wj > wi) and (wj > right_most): + right_most = wj + dep_right_most = r + if (wj < wi) and (wj < left_most): + left_most = wj + dep_left_most = r + if self._check_informative(dep_left_most): + result.append("STK_0_LDEP_" + dep_left_most) + if self._check_informative(dep_right_most): + result.append("STK_0_RDEP_" + dep_right_most) + + # Check Buffered 0 + if len(self.buffer) > 0: + # Buffer 0 + buffer_idx0 = self.buffer[0] + token = self._tokens[buffer_idx0] + if self._check_informative(token["word"], True): + result.append("BUF_0_FORM_" + token["word"]) + if "lemma" in token and self._check_informative(token["lemma"]): + result.append("BUF_0_LEMMA_" + token["lemma"]) + if self._check_informative(token["tag"]): + result.append("BUF_0_POS_" + token["tag"]) + if "feats" in token and self._check_informative(token["feats"]): + feats = token["feats"].split("|") + for feat in feats: + result.append("BUF_0_FEATS_" + feat) + # Buffer 1 + if len(self.buffer) > 1: + buffer_idx1 = self.buffer[1] + token = self._tokens[buffer_idx1] + if self._check_informative(token["word"], True): + result.append("BUF_1_FORM_" + token["word"]) + if self._check_informative(token["tag"]): + result.append("BUF_1_POS_" + token["tag"]) + if len(self.buffer) > 2: + buffer_idx2 = self.buffer[2] + token = self._tokens[buffer_idx2] + if self._check_informative(token["tag"]): + result.append("BUF_2_POS_" + token["tag"]) + if len(self.buffer) > 3: + buffer_idx3 = self.buffer[3] + token = self._tokens[buffer_idx3] + if self._check_informative(token["tag"]): + result.append("BUF_3_POS_" + token["tag"]) + # Left most, right most dependency of stack[0] + left_most = 1000000 + right_most = -1 + dep_left_most = "" + dep_right_most = "" + for (wi, r, wj) in self.arcs: + if wi == buffer_idx0: + if (wj > wi) and (wj > right_most): + right_most = wj + dep_right_most = r + if (wj < wi) and (wj < left_most): + left_most = wj + dep_left_most = r + if self._check_informative(dep_left_most): + result.append("BUF_0_LDEP_" + dep_left_most) + if self._check_informative(dep_right_most): + result.append("BUF_0_RDEP_" + dep_right_most) + + return result + + +class Transition: + """ + This class defines a set of transition which is applied to a configuration to get another configuration + Note that for different parsing algorithm, the transition is different. + """ + + # Define set of transitions + LEFT_ARC = "LEFTARC" + RIGHT_ARC = "RIGHTARC" + SHIFT = "SHIFT" + REDUCE = "REDUCE" + + def __init__(self, alg_option): + """ + :param alg_option: the algorithm option of this parser. Currently support `arc-standard` and `arc-eager` algorithm + :type alg_option: str + """ + self._algo = alg_option + if alg_option not in [ + TransitionParser.ARC_STANDARD, + TransitionParser.ARC_EAGER, + ]: + raise ValueError( + " Currently we only support %s and %s " + % (TransitionParser.ARC_STANDARD, TransitionParser.ARC_EAGER) + ) + + def left_arc(self, conf, relation): + """ + Note that the algorithm for left-arc is quite similar except for precondition for both arc-standard and arc-eager + + :param configuration: is the current configuration + :return: A new configuration or -1 if the pre-condition is not satisfied + """ + if (len(conf.buffer) <= 0) or (len(conf.stack) <= 0): + return -1 + if conf.buffer[0] == 0: + # here is the Root element + return -1 + + idx_wi = conf.stack[len(conf.stack) - 1] + + flag = True + if self._algo == TransitionParser.ARC_EAGER: + for (idx_parent, r, idx_child) in conf.arcs: + if idx_child == idx_wi: + flag = False + + if flag: + conf.stack.pop() + idx_wj = conf.buffer[0] + conf.arcs.append((idx_wj, relation, idx_wi)) + else: + return -1 + + def right_arc(self, conf, relation): + """ + Note that the algorithm for right-arc is DIFFERENT for arc-standard and arc-eager + + :param configuration: is the current configuration + :return: A new configuration or -1 if the pre-condition is not satisfied + """ + if (len(conf.buffer) <= 0) or (len(conf.stack) <= 0): + return -1 + if self._algo == TransitionParser.ARC_STANDARD: + idx_wi = conf.stack.pop() + idx_wj = conf.buffer[0] + conf.buffer[0] = idx_wi + conf.arcs.append((idx_wi, relation, idx_wj)) + else: # arc-eager + idx_wi = conf.stack[len(conf.stack) - 1] + idx_wj = conf.buffer.pop(0) + conf.stack.append(idx_wj) + conf.arcs.append((idx_wi, relation, idx_wj)) + + def reduce(self, conf): + """ + Note that the algorithm for reduce is only available for arc-eager + + :param configuration: is the current configuration + :return: A new configuration or -1 if the pre-condition is not satisfied + """ + + if self._algo != TransitionParser.ARC_EAGER: + return -1 + if len(conf.stack) <= 0: + return -1 + + idx_wi = conf.stack[len(conf.stack) - 1] + flag = False + for (idx_parent, r, idx_child) in conf.arcs: + if idx_child == idx_wi: + flag = True + if flag: + conf.stack.pop() # reduce it + else: + return -1 + + def shift(self, conf): + """ + Note that the algorithm for shift is the SAME for arc-standard and arc-eager + + :param configuration: is the current configuration + :return: A new configuration or -1 if the pre-condition is not satisfied + """ + if len(conf.buffer) <= 0: + return -1 + idx_wi = conf.buffer.pop(0) + conf.stack.append(idx_wi) + + +class TransitionParser(ParserI): + + """ + Class for transition based parser. Implement 2 algorithms which are "arc-standard" and "arc-eager" + """ + + ARC_STANDARD = "arc-standard" + ARC_EAGER = "arc-eager" + + def __init__(self, algorithm): + """ + :param algorithm: the algorithm option of this parser. Currently support `arc-standard` and `arc-eager` algorithm + :type algorithm: str + """ + if not (algorithm in [self.ARC_STANDARD, self.ARC_EAGER]): + raise ValueError( + " Currently we only support %s and %s " + % (self.ARC_STANDARD, self.ARC_EAGER) + ) + self._algorithm = algorithm + + self._dictionary = {} + self._transition = {} + self._match_transition = {} + + def _get_dep_relation(self, idx_parent, idx_child, depgraph): + p_node = depgraph.nodes[idx_parent] + c_node = depgraph.nodes[idx_child] + + if c_node["word"] is None: + return None # Root word + + if c_node["head"] == p_node["address"]: + return c_node["rel"] + else: + return None + + def _convert_to_binary_features(self, features): + """ + :param features: list of feature string which is needed to convert to binary features + :type features: list(str) + :return : string of binary features in libsvm format which is 'featureID:value' pairs + """ + unsorted_result = [] + for feature in features: + self._dictionary.setdefault(feature, len(self._dictionary)) + unsorted_result.append(self._dictionary[feature]) + + # Default value of each feature is 1.0 + return " ".join( + str(featureID) + ":1.0" for featureID in sorted(unsorted_result) + ) + + def _is_projective(self, depgraph): + arc_list = [] + for key in depgraph.nodes: + node = depgraph.nodes[key] + + if "head" in node: + childIdx = node["address"] + parentIdx = node["head"] + if parentIdx is not None: + arc_list.append((parentIdx, childIdx)) + + for (parentIdx, childIdx) in arc_list: + # Ensure that childIdx < parentIdx + if childIdx > parentIdx: + temp = childIdx + childIdx = parentIdx + parentIdx = temp + for k in range(childIdx + 1, parentIdx): + for m in range(len(depgraph.nodes)): + if (m < childIdx) or (m > parentIdx): + if (k, m) in arc_list: + return False + if (m, k) in arc_list: + return False + return True + + def _write_to_file(self, key, binary_features, input_file): + """ + write the binary features to input file and update the transition dictionary + """ + self._transition.setdefault(key, len(self._transition) + 1) + self._match_transition[self._transition[key]] = key + + input_str = str(self._transition[key]) + " " + binary_features + "\n" + input_file.write(input_str.encode("utf-8")) + + def _create_training_examples_arc_std(self, depgraphs, input_file): + """ + Create the training example in the libsvm format and write it to the input_file. + Reference : Page 32, Chapter 3. Dependency Parsing by Sandra Kubler, Ryan McDonal and Joakim Nivre (2009) + """ + operation = Transition(self.ARC_STANDARD) + count_proj = 0 + training_seq = [] + + for depgraph in depgraphs: + if not self._is_projective(depgraph): + continue + + count_proj += 1 + conf = Configuration(depgraph) + while len(conf.buffer) > 0: + b0 = conf.buffer[0] + features = conf.extract_features() + binary_features = self._convert_to_binary_features(features) + + if len(conf.stack) > 0: + s0 = conf.stack[len(conf.stack) - 1] + # Left-arc operation + rel = self._get_dep_relation(b0, s0, depgraph) + if rel is not None: + key = Transition.LEFT_ARC + ":" + rel + self._write_to_file(key, binary_features, input_file) + operation.left_arc(conf, rel) + training_seq.append(key) + continue + + # Right-arc operation + rel = self._get_dep_relation(s0, b0, depgraph) + if rel is not None: + precondition = True + # Get the max-index of buffer + maxID = conf._max_address + + for w in range(maxID + 1): + if w != b0: + relw = self._get_dep_relation(b0, w, depgraph) + if relw is not None: + if (b0, relw, w) not in conf.arcs: + precondition = False + + if precondition: + key = Transition.RIGHT_ARC + ":" + rel + self._write_to_file(key, binary_features, input_file) + operation.right_arc(conf, rel) + training_seq.append(key) + continue + + # Shift operation as the default + key = Transition.SHIFT + self._write_to_file(key, binary_features, input_file) + operation.shift(conf) + training_seq.append(key) + + print(" Number of training examples : " + str(len(depgraphs))) + print(" Number of valid (projective) examples : " + str(count_proj)) + return training_seq + + def _create_training_examples_arc_eager(self, depgraphs, input_file): + """ + Create the training example in the libsvm format and write it to the input_file. + Reference : 'A Dynamic Oracle for Arc-Eager Dependency Parsing' by Joav Goldberg and Joakim Nivre + """ + operation = Transition(self.ARC_EAGER) + countProj = 0 + training_seq = [] + + for depgraph in depgraphs: + if not self._is_projective(depgraph): + continue + + countProj += 1 + conf = Configuration(depgraph) + while len(conf.buffer) > 0: + b0 = conf.buffer[0] + features = conf.extract_features() + binary_features = self._convert_to_binary_features(features) + + if len(conf.stack) > 0: + s0 = conf.stack[len(conf.stack) - 1] + # Left-arc operation + rel = self._get_dep_relation(b0, s0, depgraph) + if rel is not None: + key = Transition.LEFT_ARC + ":" + rel + self._write_to_file(key, binary_features, input_file) + operation.left_arc(conf, rel) + training_seq.append(key) + continue + + # Right-arc operation + rel = self._get_dep_relation(s0, b0, depgraph) + if rel is not None: + key = Transition.RIGHT_ARC + ":" + rel + self._write_to_file(key, binary_features, input_file) + operation.right_arc(conf, rel) + training_seq.append(key) + continue + + # reduce operation + flag = False + for k in range(s0): + if self._get_dep_relation(k, b0, depgraph) is not None: + flag = True + if self._get_dep_relation(b0, k, depgraph) is not None: + flag = True + if flag: + key = Transition.REDUCE + self._write_to_file(key, binary_features, input_file) + operation.reduce(conf) + training_seq.append(key) + continue + + # Shift operation as the default + key = Transition.SHIFT + self._write_to_file(key, binary_features, input_file) + operation.shift(conf) + training_seq.append(key) + + print(" Number of training examples : " + str(len(depgraphs))) + print(" Number of valid (projective) examples : " + str(countProj)) + return training_seq + + def train(self, depgraphs, modelfile, verbose=True): + """ + :param depgraphs : list of DependencyGraph as the training data + :type depgraphs : DependencyGraph + :param modelfile : file name to save the trained model + :type modelfile : str + """ + + try: + input_file = tempfile.NamedTemporaryFile( + prefix="transition_parse.train", dir=tempfile.gettempdir(), delete=False + ) + + if self._algorithm == self.ARC_STANDARD: + self._create_training_examples_arc_std(depgraphs, input_file) + else: + self._create_training_examples_arc_eager(depgraphs, input_file) + + input_file.close() + # Using the temporary file to train the libsvm classifier + x_train, y_train = load_svmlight_file(input_file.name) + # The parameter is set according to the paper: + # Algorithms for Deterministic Incremental Dependency Parsing by Joakim Nivre + # Todo : because of probability = True => very slow due to + # cross-validation. Need to improve the speed here + model = svm.SVC( + kernel="poly", + degree=2, + coef0=0, + gamma=0.2, + C=0.5, + verbose=verbose, + probability=True, + ) + + model.fit(x_train, y_train) + # Save the model to file name (as pickle) + pickle.dump(model, open(modelfile, "wb")) + finally: + remove(input_file.name) + + def parse(self, depgraphs, modelFile): + """ + :param depgraphs: the list of test sentence, each sentence is represented as a dependency graph where the 'head' information is dummy + :type depgraphs: list(DependencyGraph) + :param modelfile: the model file + :type modelfile: str + :return: list (DependencyGraph) with the 'head' and 'rel' information + """ + result = [] + # First load the model + model = pickle.load(open(modelFile, "rb")) + operation = Transition(self._algorithm) + + for depgraph in depgraphs: + conf = Configuration(depgraph) + while len(conf.buffer) > 0: + features = conf.extract_features() + col = [] + row = [] + data = [] + for feature in features: + if feature in self._dictionary: + col.append(self._dictionary[feature]) + row.append(0) + data.append(1.0) + np_col = array(sorted(col)) # NB : index must be sorted + np_row = array(row) + np_data = array(data) + + x_test = sparse.csr_matrix( + (np_data, (np_row, np_col)), shape=(1, len(self._dictionary)) + ) + + # It's best to use decision function as follow BUT it's not supported yet for sparse SVM + # Using decision function to build the votes array + # dec_func = model.decision_function(x_test)[0] + # votes = {} + # k = 0 + # for i in range(len(model.classes_)): + # for j in range(i+1, len(model.classes_)): + # #if dec_func[k] > 0: + # votes.setdefault(i,0) + # votes[i] +=1 + # else: + # votes.setdefault(j,0) + # votes[j] +=1 + # k +=1 + # Sort votes according to the values + # sorted_votes = sorted(votes.items(), key=itemgetter(1), reverse=True) + + # We will use predict_proba instead of decision_function + prob_dict = {} + pred_prob = model.predict_proba(x_test)[0] + for i in range(len(pred_prob)): + prob_dict[i] = pred_prob[i] + sorted_Prob = sorted(prob_dict.items(), key=itemgetter(1), reverse=True) + + # Note that SHIFT is always a valid operation + for (y_pred_idx, confidence) in sorted_Prob: + # y_pred = model.predict(x_test)[0] + # From the prediction match to the operation + y_pred = model.classes_[y_pred_idx] + + if y_pred in self._match_transition: + strTransition = self._match_transition[y_pred] + baseTransition = strTransition.split(":")[0] + + if baseTransition == Transition.LEFT_ARC: + if ( + operation.left_arc(conf, strTransition.split(":")[1]) + != -1 + ): + break + elif baseTransition == Transition.RIGHT_ARC: + if ( + operation.right_arc(conf, strTransition.split(":")[1]) + != -1 + ): + break + elif baseTransition == Transition.REDUCE: + if operation.reduce(conf) != -1: + break + elif baseTransition == Transition.SHIFT: + if operation.shift(conf) != -1: + break + else: + raise ValueError( + "The predicted transition is not recognized, expected errors" + ) + + # Finish with operations build the dependency graph from Conf.arcs + + new_depgraph = deepcopy(depgraph) + for key in new_depgraph.nodes: + node = new_depgraph.nodes[key] + node["rel"] = "" + # With the default, all the token depend on the Root + node["head"] = 0 + for (head, rel, child) in conf.arcs: + c_node = new_depgraph.nodes[child] + c_node["head"] = head + c_node["rel"] = rel + result.append(new_depgraph) + + return result + + +def demo(): + """ + >>> from nltk.parse import DependencyGraph, DependencyEvaluator + >>> from nltk.parse.transitionparser import TransitionParser, Configuration, Transition + >>> gold_sent = DependencyGraph(\""" + ... Economic JJ 2 ATT + ... news NN 3 SBJ + ... has VBD 0 ROOT + ... little JJ 5 ATT + ... effect NN 3 OBJ + ... on IN 5 ATT + ... financial JJ 8 ATT + ... markets NNS 6 PC + ... . . 3 PU + ... \""") + + >>> conf = Configuration(gold_sent) + + ###################### Check the Initial Feature ######################## + + >>> print(', '.join(conf.extract_features())) + STK_0_POS_TOP, BUF_0_FORM_Economic, BUF_0_LEMMA_Economic, BUF_0_POS_JJ, BUF_1_FORM_news, BUF_1_POS_NN, BUF_2_POS_VBD, BUF_3_POS_JJ + + ###################### Check The Transition ####################### + Check the Initialized Configuration + >>> print(conf) + Stack : [0] Buffer : [1, 2, 3, 4, 5, 6, 7, 8, 9] Arcs : [] + + A. Do some transition checks for ARC-STANDARD + + >>> operation = Transition('arc-standard') + >>> operation.shift(conf) + >>> operation.left_arc(conf, "ATT") + >>> operation.shift(conf) + >>> operation.left_arc(conf,"SBJ") + >>> operation.shift(conf) + >>> operation.shift(conf) + >>> operation.left_arc(conf, "ATT") + >>> operation.shift(conf) + >>> operation.shift(conf) + >>> operation.shift(conf) + >>> operation.left_arc(conf, "ATT") + + Middle Configuration and Features Check + >>> print(conf) + Stack : [0, 3, 5, 6] Buffer : [8, 9] Arcs : [(2, 'ATT', 1), (3, 'SBJ', 2), (5, 'ATT', 4), (8, 'ATT', 7)] + + >>> print(', '.join(conf.extract_features())) + STK_0_FORM_on, STK_0_LEMMA_on, STK_0_POS_IN, STK_1_POS_NN, BUF_0_FORM_markets, BUF_0_LEMMA_markets, BUF_0_POS_NNS, BUF_1_FORM_., BUF_1_POS_., BUF_0_LDEP_ATT + + >>> operation.right_arc(conf, "PC") + >>> operation.right_arc(conf, "ATT") + >>> operation.right_arc(conf, "OBJ") + >>> operation.shift(conf) + >>> operation.right_arc(conf, "PU") + >>> operation.right_arc(conf, "ROOT") + >>> operation.shift(conf) + + Terminated Configuration Check + >>> print(conf) + Stack : [0] Buffer : [] Arcs : [(2, 'ATT', 1), (3, 'SBJ', 2), (5, 'ATT', 4), (8, 'ATT', 7), (6, 'PC', 8), (5, 'ATT', 6), (3, 'OBJ', 5), (3, 'PU', 9), (0, 'ROOT', 3)] + + + B. Do some transition checks for ARC-EAGER + + >>> conf = Configuration(gold_sent) + >>> operation = Transition('arc-eager') + >>> operation.shift(conf) + >>> operation.left_arc(conf,'ATT') + >>> operation.shift(conf) + >>> operation.left_arc(conf,'SBJ') + >>> operation.right_arc(conf,'ROOT') + >>> operation.shift(conf) + >>> operation.left_arc(conf,'ATT') + >>> operation.right_arc(conf,'OBJ') + >>> operation.right_arc(conf,'ATT') + >>> operation.shift(conf) + >>> operation.left_arc(conf,'ATT') + >>> operation.right_arc(conf,'PC') + >>> operation.reduce(conf) + >>> operation.reduce(conf) + >>> operation.reduce(conf) + >>> operation.right_arc(conf,'PU') + >>> print(conf) + Stack : [0, 3, 9] Buffer : [] Arcs : [(2, 'ATT', 1), (3, 'SBJ', 2), (0, 'ROOT', 3), (5, 'ATT', 4), (3, 'OBJ', 5), (5, 'ATT', 6), (8, 'ATT', 7), (6, 'PC', 8), (3, 'PU', 9)] + + ###################### Check The Training Function ####################### + + A. Check the ARC-STANDARD training + >>> import tempfile + >>> import os + >>> input_file = tempfile.NamedTemporaryFile(prefix='transition_parse.train', dir=tempfile.gettempdir(), delete=False) + + >>> parser_std = TransitionParser('arc-standard') + >>> print(', '.join(parser_std._create_training_examples_arc_std([gold_sent], input_file))) + Number of training examples : 1 + Number of valid (projective) examples : 1 + SHIFT, LEFTARC:ATT, SHIFT, LEFTARC:SBJ, SHIFT, SHIFT, LEFTARC:ATT, SHIFT, SHIFT, SHIFT, LEFTARC:ATT, RIGHTARC:PC, RIGHTARC:ATT, RIGHTARC:OBJ, SHIFT, RIGHTARC:PU, RIGHTARC:ROOT, SHIFT + + >>> parser_std.train([gold_sent],'temp.arcstd.model', verbose=False) + Number of training examples : 1 + Number of valid (projective) examples : 1 + >>> input_file.close() + >>> remove(input_file.name) + + B. Check the ARC-EAGER training + + >>> input_file = tempfile.NamedTemporaryFile(prefix='transition_parse.train', dir=tempfile.gettempdir(),delete=False) + >>> parser_eager = TransitionParser('arc-eager') + >>> print(', '.join(parser_eager._create_training_examples_arc_eager([gold_sent], input_file))) + Number of training examples : 1 + Number of valid (projective) examples : 1 + SHIFT, LEFTARC:ATT, SHIFT, LEFTARC:SBJ, RIGHTARC:ROOT, SHIFT, LEFTARC:ATT, RIGHTARC:OBJ, RIGHTARC:ATT, SHIFT, LEFTARC:ATT, RIGHTARC:PC, REDUCE, REDUCE, REDUCE, RIGHTARC:PU + + >>> parser_eager.train([gold_sent],'temp.arceager.model', verbose=False) + Number of training examples : 1 + Number of valid (projective) examples : 1 + + >>> input_file.close() + >>> remove(input_file.name) + + ###################### Check The Parsing Function ######################## + + A. Check the ARC-STANDARD parser + + >>> result = parser_std.parse([gold_sent], 'temp.arcstd.model') + >>> de = DependencyEvaluator(result, [gold_sent]) + >>> de.eval() >= (0, 0) + True + + B. Check the ARC-EAGER parser + >>> result = parser_eager.parse([gold_sent], 'temp.arceager.model') + >>> de = DependencyEvaluator(result, [gold_sent]) + >>> de.eval() >= (0, 0) + True + + Remove test temporary files + >>> remove('temp.arceager.model') + >>> remove('temp.arcstd.model') + + Note that result is very poor because of only one training example. + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/util.py new file mode 100644 index 0000000..84f366a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/util.py @@ -0,0 +1,234 @@ +# Natural Language Toolkit: Parser Utility Functions +# +# Author: Ewan Klein +# Tom Aarsen <> +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + + +""" +Utility functions for parsers. +""" + +from nltk.data import load +from nltk.grammar import CFG, PCFG, FeatureGrammar +from nltk.parse.chart import Chart, ChartParser +from nltk.parse.featurechart import FeatureChart, FeatureChartParser +from nltk.parse.pchart import InsideChartParser + + +def load_parser( + grammar_url, trace=0, parser=None, chart_class=None, beam_size=0, **load_args +): + """ + Load a grammar from a file, and build a parser based on that grammar. + The parser depends on the grammar format, and might also depend + on properties of the grammar itself. + + The following grammar formats are currently supported: + - ``'cfg'`` (CFGs: ``CFG``) + - ``'pcfg'`` (probabilistic CFGs: ``PCFG``) + - ``'fcfg'`` (feature-based CFGs: ``FeatureGrammar``) + + :type grammar_url: str + :param grammar_url: A URL specifying where the grammar is located. + The default protocol is ``"nltk:"``, which searches for the file + in the the NLTK data package. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing output. + :param parser: The class used for parsing; should be ``ChartParser`` + or a subclass. + If None, the class depends on the grammar format. + :param chart_class: The class used for storing the chart; + should be ``Chart`` or a subclass. + Only used for CFGs and feature CFGs. + If None, the chart class depends on the grammar format. + :type beam_size: int + :param beam_size: The maximum length for the parser's edge queue. + Only used for probabilistic CFGs. + :param load_args: Keyword parameters used when loading the grammar. + See ``data.load`` for more information. + """ + grammar = load(grammar_url, **load_args) + if not isinstance(grammar, CFG): + raise ValueError("The grammar must be a CFG, " "or a subclass thereof.") + if isinstance(grammar, PCFG): + if parser is None: + parser = InsideChartParser + return parser(grammar, trace=trace, beam_size=beam_size) + + elif isinstance(grammar, FeatureGrammar): + if parser is None: + parser = FeatureChartParser + if chart_class is None: + chart_class = FeatureChart + return parser(grammar, trace=trace, chart_class=chart_class) + + else: # Plain CFG. + if parser is None: + parser = ChartParser + if chart_class is None: + chart_class = Chart + return parser(grammar, trace=trace, chart_class=chart_class) + + +def taggedsent_to_conll(sentence): + """ + A module to convert a single POS tagged sentence into CONLL format. + + >>> from nltk import word_tokenize, pos_tag + >>> text = "This is a foobar sentence." + >>> for line in taggedsent_to_conll(pos_tag(word_tokenize(text))): # doctest: +NORMALIZE_WHITESPACE + ... print(line, end="") + 1 This _ DT DT _ 0 a _ _ + 2 is _ VBZ VBZ _ 0 a _ _ + 3 a _ DT DT _ 0 a _ _ + 4 foobar _ JJ JJ _ 0 a _ _ + 5 sentence _ NN NN _ 0 a _ _ + 6 . _ . . _ 0 a _ _ + + :param sentence: A single input sentence to parse + :type sentence: list(tuple(str, str)) + :rtype: iter(str) + :return: a generator yielding a single sentence in CONLL format. + """ + for (i, (word, tag)) in enumerate(sentence, start=1): + input_str = [str(i), word, "_", tag, tag, "_", "0", "a", "_", "_"] + input_str = "\t".join(input_str) + "\n" + yield input_str + + +def taggedsents_to_conll(sentences): + """ + A module to convert the a POS tagged document stream + (i.e. list of list of tuples, a list of sentences) and yield lines + in CONLL format. This module yields one line per word and two newlines + for end of sentence. + + >>> from nltk import word_tokenize, sent_tokenize, pos_tag + >>> text = "This is a foobar sentence. Is that right?" + >>> sentences = [pos_tag(word_tokenize(sent)) for sent in sent_tokenize(text)] + >>> for line in taggedsents_to_conll(sentences): # doctest: +NORMALIZE_WHITESPACE + ... if line: + ... print(line, end="") + 1 This _ DT DT _ 0 a _ _ + 2 is _ VBZ VBZ _ 0 a _ _ + 3 a _ DT DT _ 0 a _ _ + 4 foobar _ JJ JJ _ 0 a _ _ + 5 sentence _ NN NN _ 0 a _ _ + 6 . _ . . _ 0 a _ _ + + + 1 Is _ VBZ VBZ _ 0 a _ _ + 2 that _ IN IN _ 0 a _ _ + 3 right _ NN NN _ 0 a _ _ + 4 ? _ . . _ 0 a _ _ + + + + :param sentences: Input sentences to parse + :type sentence: list(list(tuple(str, str))) + :rtype: iter(str) + :return: a generator yielding sentences in CONLL format. + """ + for sentence in sentences: + yield from taggedsent_to_conll(sentence) + yield "\n\n" + + +###################################################################### +# { Test Suites +###################################################################### + + +class TestGrammar: + """ + Unit tests for CFG. + """ + + def __init__(self, grammar, suite, accept=None, reject=None): + self.test_grammar = grammar + + self.cp = load_parser(grammar, trace=0) + self.suite = suite + self._accept = accept + self._reject = reject + + def run(self, show_trees=False): + """ + Sentences in the test suite are divided into two classes: + + - grammatical (``accept``) and + - ungrammatical (``reject``). + + If a sentence should parse according to the grammar, the value of + ``trees`` will be a non-empty list. If a sentence should be rejected + according to the grammar, then the value of ``trees`` will be None. + """ + for test in self.suite: + print(test["doc"] + ":", end=" ") + for key in ["accept", "reject"]: + for sent in test[key]: + tokens = sent.split() + trees = list(self.cp.parse(tokens)) + if show_trees and trees: + print() + print(sent) + for tree in trees: + print(tree) + if key == "accept": + if trees == []: + raise ValueError("Sentence '%s' failed to parse'" % sent) + else: + accepted = True + else: + if trees: + raise ValueError("Sentence '%s' received a parse'" % sent) + else: + rejected = True + if accepted and rejected: + print("All tests passed!") + + +def extract_test_sentences(string, comment_chars="#%;", encoding=None): + """ + Parses a string with one test sentence per line. + Lines can optionally begin with: + + - a bool, saying if the sentence is grammatical or not, or + - an int, giving the number of parse trees is should have, + + The result information is followed by a colon, and then the sentence. + Empty lines and lines beginning with a comment char are ignored. + + :return: a list of tuple of sentences and expected results, + where a sentence is a list of str, + and a result is None, or bool, or int + + :param comment_chars: ``str`` of possible comment characters. + :param encoding: the encoding of the string, if it is binary + """ + if encoding is not None: + string = string.decode(encoding) + sentences = [] + for sentence in string.split("\n"): + if sentence == "" or sentence[0] in comment_chars: + continue + split_info = sentence.split(":", 1) + result = None + if len(split_info) == 2: + if split_info[0] in ["True", "true", "False", "false"]: + result = split_info[0] in ["True", "true"] + sentence = split_info[1] + else: + result = int(split_info[0]) + sentence = split_info[1] + tokens = sentence.split() + if tokens == []: + continue + sentences += [(tokens, result)] + return sentences diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/parse/viterbi.py b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/viterbi.py new file mode 100644 index 0000000..1c57f0c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/parse/viterbi.py @@ -0,0 +1,453 @@ +# Natural Language Toolkit: Viterbi Probabilistic Parser +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from functools import reduce + +from nltk.parse.api import ParserI +from nltk.tree import ProbabilisticTree, Tree + +##////////////////////////////////////////////////////// +## Viterbi PCFG Parser +##////////////////////////////////////////////////////// + + +class ViterbiParser(ParserI): + """ + A bottom-up ``PCFG`` parser that uses dynamic programming to find + the single most likely parse for a text. The ``ViterbiParser`` parser + parses texts by filling in a "most likely constituent table". + This table records the most probable tree representation for any + given span and node value. In particular, it has an entry for + every start index, end index, and node value, recording the most + likely subtree that spans from the start index to the end index, + and has the given node value. + + The ``ViterbiParser`` parser fills in this table incrementally. It starts + by filling in all entries for constituents that span one element + of text (i.e., entries where the end index is one greater than the + start index). After it has filled in all table entries for + constituents that span one element of text, it fills in the + entries for constitutants that span two elements of text. It + continues filling in the entries for constituents spanning larger + and larger portions of the text, until the entire table has been + filled. Finally, it returns the table entry for a constituent + spanning the entire text, whose node value is the grammar's start + symbol. + + In order to find the most likely constituent with a given span and + node value, the ``ViterbiParser`` parser considers all productions that + could produce that node value. For each production, it finds all + children that collectively cover the span and have the node values + specified by the production's right hand side. If the probability + of the tree formed by applying the production to the children is + greater than the probability of the current entry in the table, + then the table is updated with this new tree. + + A pseudo-code description of the algorithm used by + ``ViterbiParser`` is: + + | Create an empty most likely constituent table, *MLC*. + | For width in 1...len(text): + | For start in 1...len(text)-width: + | For prod in grammar.productions: + | For each sequence of subtrees [t[1], t[2], ..., t[n]] in MLC, + | where t[i].label()==prod.rhs[i], + | and the sequence covers [start:start+width]: + | old_p = MLC[start, start+width, prod.lhs] + | new_p = P(t[1])P(t[1])...P(t[n])P(prod) + | if new_p > old_p: + | new_tree = Tree(prod.lhs, t[1], t[2], ..., t[n]) + | MLC[start, start+width, prod.lhs] = new_tree + | Return MLC[0, len(text), start_symbol] + + :type _grammar: PCFG + :ivar _grammar: The grammar used to parse sentences. + :type _trace: int + :ivar _trace: The level of tracing output that should be generated + when parsing a text. + """ + + def __init__(self, grammar, trace=0): + """ + Create a new ``ViterbiParser`` parser, that uses ``grammar`` to + parse texts. + + :type grammar: PCFG + :param grammar: The grammar used to parse texts. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + and higher numbers will produce more verbose tracing + output. + """ + self._grammar = grammar + self._trace = trace + + def grammar(self): + return self._grammar + + def trace(self, trace=2): + """ + Set the level of tracing output that should be generated when + parsing a text. + + :type trace: int + :param trace: The trace level. A trace level of ``0`` will + generate no tracing output; and higher trace levels will + produce more verbose tracing output. + :rtype: None + """ + self._trace = trace + + def parse(self, tokens): + # Inherit docs from ParserI + + tokens = list(tokens) + self._grammar.check_coverage(tokens) + + # The most likely constituent table. This table specifies the + # most likely constituent for a given span and type. + # Constituents can be either Trees or tokens. For Trees, + # the "type" is the Nonterminal for the tree's root node + # value. For Tokens, the "type" is the token's type. + # The table is stored as a dictionary, since it is sparse. + constituents = {} + + # Initialize the constituents dictionary with the words from + # the text. + if self._trace: + print("Inserting tokens into the most likely" + " constituents table...") + for index in range(len(tokens)): + token = tokens[index] + constituents[index, index + 1, token] = token + if self._trace > 1: + self._trace_lexical_insertion(token, index, len(tokens)) + + # Consider each span of length 1, 2, ..., n; and add any trees + # that might cover that span to the constituents dictionary. + for length in range(1, len(tokens) + 1): + if self._trace: + print( + "Finding the most likely constituents" + + " spanning %d text elements..." % length + ) + for start in range(len(tokens) - length + 1): + span = (start, start + length) + self._add_constituents_spanning(span, constituents, tokens) + + # Return the tree that spans the entire text & have the right cat + tree = constituents.get((0, len(tokens), self._grammar.start())) + if tree is not None: + yield tree + + def _add_constituents_spanning(self, span, constituents, tokens): + """ + Find any constituents that might cover ``span``, and add them + to the most likely constituents table. + + :rtype: None + :type span: tuple(int, int) + :param span: The section of the text for which we are + trying to find possible constituents. The span is + specified as a pair of integers, where the first integer + is the index of the first token that should be included in + the constituent; and the second integer is the index of + the first token that should not be included in the + constituent. I.e., the constituent should cover + ``text[span[0]:span[1]]``, where ``text`` is the text + that we are parsing. + + :type constituents: dict(tuple(int,int,Nonterminal) -> ProbabilisticToken or ProbabilisticTree) + :param constituents: The most likely constituents table. This + table records the most probable tree representation for + any given span and node value. In particular, + ``constituents(s,e,nv)`` is the most likely + ``ProbabilisticTree`` that covers ``text[s:e]`` + and has a node value ``nv.symbol()``, where ``text`` + is the text that we are parsing. When + ``_add_constituents_spanning`` is called, ``constituents`` + should contain all possible constituents that are shorter + than ``span``. + + :type tokens: list of tokens + :param tokens: The text we are parsing. This is only used for + trace output. + """ + # Since some of the grammar productions may be unary, we need to + # repeatedly try all of the productions until none of them add any + # new constituents. + changed = True + while changed: + changed = False + + # Find all ways instantiations of the grammar productions that + # cover the span. + instantiations = self._find_instantiations(span, constituents) + + # For each production instantiation, add a new + # ProbabilisticTree whose probability is the product + # of the childrens' probabilities and the production's + # probability. + for (production, children) in instantiations: + subtrees = [c for c in children if isinstance(c, Tree)] + p = reduce(lambda pr, t: pr * t.prob(), subtrees, production.prob()) + node = production.lhs().symbol() + tree = ProbabilisticTree(node, children, prob=p) + + # If it's new a constituent, then add it to the + # constituents dictionary. + c = constituents.get((span[0], span[1], production.lhs())) + if self._trace > 1: + if c is None or c != tree: + if c is None or c.prob() < tree.prob(): + print(" Insert:", end=" ") + else: + print(" Discard:", end=" ") + self._trace_production(production, p, span, len(tokens)) + if c is None or c.prob() < tree.prob(): + constituents[span[0], span[1], production.lhs()] = tree + changed = True + + def _find_instantiations(self, span, constituents): + """ + :return: a list of the production instantiations that cover a + given span of the text. A "production instantiation" is + a tuple containing a production and a list of children, + where the production's right hand side matches the list of + children; and the children cover ``span``. :rtype: list + of ``pair`` of ``Production``, (list of + (``ProbabilisticTree`` or token. + + :type span: tuple(int, int) + :param span: The section of the text for which we are + trying to find production instantiations. The span is + specified as a pair of integers, where the first integer + is the index of the first token that should be covered by + the production instantiation; and the second integer is + the index of the first token that should not be covered by + the production instantiation. + :type constituents: dict(tuple(int,int,Nonterminal) -> ProbabilisticToken or ProbabilisticTree) + :param constituents: The most likely constituents table. This + table records the most probable tree representation for + any given span and node value. See the module + documentation for more information. + """ + rv = [] + for production in self._grammar.productions(): + childlists = self._match_rhs(production.rhs(), span, constituents) + + for childlist in childlists: + rv.append((production, childlist)) + return rv + + def _match_rhs(self, rhs, span, constituents): + """ + :return: a set of all the lists of children that cover ``span`` + and that match ``rhs``. + :rtype: list(list(ProbabilisticTree or token) + + :type rhs: list(Nonterminal or any) + :param rhs: The list specifying what kinds of children need to + cover ``span``. Each nonterminal in ``rhs`` specifies + that the corresponding child should be a tree whose node + value is that nonterminal's symbol. Each terminal in ``rhs`` + specifies that the corresponding child should be a token + whose type is that terminal. + :type span: tuple(int, int) + :param span: The section of the text for which we are + trying to find child lists. The span is specified as a + pair of integers, where the first integer is the index of + the first token that should be covered by the child list; + and the second integer is the index of the first token + that should not be covered by the child list. + :type constituents: dict(tuple(int,int,Nonterminal) -> ProbabilisticToken or ProbabilisticTree) + :param constituents: The most likely constituents table. This + table records the most probable tree representation for + any given span and node value. See the module + documentation for more information. + """ + (start, end) = span + + # Base case + if start >= end and rhs == (): + return [[]] + if start >= end or rhs == (): + return [] + + # Find everything that matches the 1st symbol of the RHS + childlists = [] + for split in range(start, end + 1): + l = constituents.get((start, split, rhs[0])) + if l is not None: + rights = self._match_rhs(rhs[1:], (split, end), constituents) + childlists += [[l] + r for r in rights] + + return childlists + + def _trace_production(self, production, p, span, width): + """ + Print trace output indicating that a given production has been + applied at a given location. + + :param production: The production that has been applied + :type production: Production + :param p: The probability of the tree produced by the production. + :type p: float + :param span: The span of the production + :type span: tuple + :rtype: None + """ + + str = "|" + "." * span[0] + str += "=" * (span[1] - span[0]) + str += "." * (width - span[1]) + "| " + str += "%s" % production + if self._trace > 2: + str = f"{str:<40} {p:12.10f} " + + print(str) + + def _trace_lexical_insertion(self, token, index, width): + str = " Insert: |" + "." * index + "=" + "." * (width - index - 1) + "| " + str += f"{token}" + print(str) + + def __repr__(self): + return "" % self._grammar + + +##////////////////////////////////////////////////////// +## Test Code +##////////////////////////////////////////////////////// + + +def demo(): + """ + A demonstration of the probabilistic parsers. The user is + prompted to select which demo to run, and how many parses should + be found; and then each parser is run on the same demo, and a + summary of the results are displayed. + """ + import sys + import time + + from nltk import tokenize + from nltk.grammar import PCFG + from nltk.parse import ViterbiParser + + toy_pcfg1 = PCFG.fromstring( + """ + S -> NP VP [1.0] + NP -> Det N [0.5] | NP PP [0.25] | 'John' [0.1] | 'I' [0.15] + Det -> 'the' [0.8] | 'my' [0.2] + N -> 'man' [0.5] | 'telescope' [0.5] + VP -> VP PP [0.1] | V NP [0.7] | V [0.2] + V -> 'ate' [0.35] | 'saw' [0.65] + PP -> P NP [1.0] + P -> 'with' [0.61] | 'under' [0.39] + """ + ) + + toy_pcfg2 = PCFG.fromstring( + """ + S -> NP VP [1.0] + VP -> V NP [.59] + VP -> V [.40] + VP -> VP PP [.01] + NP -> Det N [.41] + NP -> Name [.28] + NP -> NP PP [.31] + PP -> P NP [1.0] + V -> 'saw' [.21] + V -> 'ate' [.51] + V -> 'ran' [.28] + N -> 'boy' [.11] + N -> 'cookie' [.12] + N -> 'table' [.13] + N -> 'telescope' [.14] + N -> 'hill' [.5] + Name -> 'Jack' [.52] + Name -> 'Bob' [.48] + P -> 'with' [.61] + P -> 'under' [.39] + Det -> 'the' [.41] + Det -> 'a' [.31] + Det -> 'my' [.28] + """ + ) + + # Define two demos. Each demo has a sentence and a grammar. + demos = [ + ("I saw the man with my telescope", toy_pcfg1), + ("the boy saw Jack with Bob under the table with a telescope", toy_pcfg2), + ] + + # Ask the user which demo they want to use. + print() + for i in range(len(demos)): + print(f"{i + 1:>3}: {demos[i][0]}") + print(" %r" % demos[i][1]) + print() + print("Which demo (%d-%d)? " % (1, len(demos)), end=" ") + try: + snum = int(sys.stdin.readline().strip()) - 1 + sent, grammar = demos[snum] + except: + print("Bad sentence number") + return + + # Tokenize the sentence. + tokens = sent.split() + + parser = ViterbiParser(grammar) + all_parses = {} + + print(f"\nsent: {sent}\nparser: {parser}\ngrammar: {grammar}") + parser.trace(3) + t = time.time() + parses = parser.parse_all(tokens) + time = time.time() - t + average = ( + reduce(lambda a, b: a + b.prob(), parses, 0) / len(parses) if parses else 0 + ) + num_parses = len(parses) + for p in parses: + all_parses[p.freeze()] = 1 + + # Print some summary statistics + print() + print("Time (secs) # Parses Average P(parse)") + print("-----------------------------------------") + print("%11.4f%11d%19.14f" % (time, num_parses, average)) + parses = all_parses.keys() + if parses: + p = reduce(lambda a, b: a + b.prob(), parses, 0) / len(parses) + else: + p = 0 + print("------------------------------------------") + print("%11s%11d%19.14f" % ("n/a", len(parses), p)) + + # Ask the user if we should draw the parses. + print() + print("Draw parses (y/n)? ", end=" ") + if sys.stdin.readline().strip().lower().startswith("y"): + from nltk.draw.tree import draw_trees + + print(" please wait...") + draw_trees(*parses) + + # Ask the user if we should print the parses. + print() + print("Print parses (y/n)? ", end=" ") + if sys.stdin.readline().strip().lower().startswith("y"): + for parse in parses: + print(parse) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/probability.py b/.pythonlibs/lib/python3.10/site-packages/nltk/probability.py new file mode 100644 index 0000000..d713fe5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/probability.py @@ -0,0 +1,2578 @@ +# Natural Language Toolkit: Probability and Statistics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (additions) +# Trevor Cohn (additions) +# Peter Ljunglöf (additions) +# Liang Dong (additions) +# Geoffrey Sampson (additions) +# Ilia Kurenkov (additions) +# +# URL: +# For license information, see LICENSE.TXT + +""" +Classes for representing and processing probabilistic information. + +The ``FreqDist`` class is used to encode "frequency distributions", +which count the number of times that each outcome of an experiment +occurs. + +The ``ProbDistI`` class defines a standard interface for "probability +distributions", which encode the probability of each outcome for an +experiment. There are two types of probability distribution: + + - "derived probability distributions" are created from frequency + distributions. They attempt to model the probability distribution + that generated the frequency distribution. + - "analytic probability distributions" are created directly from + parameters (such as variance). + +The ``ConditionalFreqDist`` class and ``ConditionalProbDistI`` interface +are used to encode conditional distributions. Conditional probability +distributions can be derived or analytic; but currently the only +implementation of the ``ConditionalProbDistI`` interface is +``ConditionalProbDist``, a derived distribution. + +""" + +import array +import math +import random +import warnings +from abc import ABCMeta, abstractmethod +from collections import Counter, defaultdict +from functools import reduce + +from nltk.internals import raise_unorderable_types + +_NINF = float("-1e300") + +##////////////////////////////////////////////////////// +## Frequency Distributions +##////////////////////////////////////////////////////// + + +class FreqDist(Counter): + """ + A frequency distribution for the outcomes of an experiment. A + frequency distribution records the number of times each outcome of + an experiment has occurred. For example, a frequency distribution + could be used to record the frequency of each word type in a + document. Formally, a frequency distribution can be defined as a + function mapping from each sample to the number of times that + sample occurred as an outcome. + + Frequency distributions are generally constructed by running a + number of experiments, and incrementing the count for a sample + every time it is an outcome of an experiment. For example, the + following code will produce a frequency distribution that encodes + how often each word occurs in a text: + + >>> from nltk.tokenize import word_tokenize + >>> from nltk.probability import FreqDist + >>> sent = 'This is an example sentence' + >>> fdist = FreqDist() + >>> for word in word_tokenize(sent): + ... fdist[word.lower()] += 1 + + An equivalent way to do this is with the initializer: + + >>> fdist = FreqDist(word.lower() for word in word_tokenize(sent)) + + """ + + def __init__(self, samples=None): + """ + Construct a new frequency distribution. If ``samples`` is + given, then the frequency distribution will be initialized + with the count of each object in ``samples``; otherwise, it + will be initialized to be empty. + + In particular, ``FreqDist()`` returns an empty frequency + distribution; and ``FreqDist(samples)`` first creates an empty + frequency distribution, and then calls ``update`` with the + list ``samples``. + + :param samples: The samples to initialize the frequency + distribution with. + :type samples: Sequence + """ + Counter.__init__(self, samples) + + # Cached number of samples in this FreqDist + self._N = None + + def N(self): + """ + Return the total number of sample outcomes that have been + recorded by this FreqDist. For the number of unique + sample values (or bins) with counts greater than zero, use + ``FreqDist.B()``. + + :rtype: int + """ + if self._N is None: + # Not already cached, or cache has been invalidated + self._N = sum(self.values()) + return self._N + + def __setitem__(self, key, val): + """ + Override ``Counter.__setitem__()`` to invalidate the cached N + """ + self._N = None + super().__setitem__(key, val) + + def __delitem__(self, key): + """ + Override ``Counter.__delitem__()`` to invalidate the cached N + """ + self._N = None + super().__delitem__(key) + + def update(self, *args, **kwargs): + """ + Override ``Counter.update()`` to invalidate the cached N + """ + self._N = None + super().update(*args, **kwargs) + + def setdefault(self, key, val): + """ + Override ``Counter.setdefault()`` to invalidate the cached N + """ + self._N = None + super().setdefault(key, val) + + def B(self): + """ + Return the total number of sample values (or "bins") that + have counts greater than zero. For the total + number of sample outcomes recorded, use ``FreqDist.N()``. + (FreqDist.B() is the same as len(FreqDist).) + + :rtype: int + """ + return len(self) + + def hapaxes(self): + """ + Return a list of all samples that occur once (hapax legomena) + + :rtype: list + """ + return [item for item in self if self[item] == 1] + + def Nr(self, r, bins=None): + return self.r_Nr(bins)[r] + + def r_Nr(self, bins=None): + """ + Return the dictionary mapping r to Nr, the number of samples with frequency r, where Nr > 0. + + :type bins: int + :param bins: The number of possible sample outcomes. ``bins`` + is used to calculate Nr(0). In particular, Nr(0) is + ``bins-self.B()``. If ``bins`` is not specified, it + defaults to ``self.B()`` (so Nr(0) will be 0). + :rtype: int + """ + + _r_Nr = defaultdict(int) + for count in self.values(): + _r_Nr[count] += 1 + + # Special case for Nr[0]: + _r_Nr[0] = bins - self.B() if bins is not None else 0 + + return _r_Nr + + def _cumulative_frequencies(self, samples): + """ + Return the cumulative frequencies of the specified samples. + If no samples are specified, all counts are returned, starting + with the largest. + + :param samples: the samples whose frequencies should be returned. + :type samples: any + :rtype: list(float) + """ + cf = 0.0 + for sample in samples: + cf += self[sample] + yield cf + + # slightly odd nomenclature freq() if FreqDist does counts and ProbDist does probs, + # here, freq() does probs + def freq(self, sample): + """ + Return the frequency of a given sample. The frequency of a + sample is defined as the count of that sample divided by the + total number of sample outcomes that have been recorded by + this FreqDist. The count of a sample is defined as the + number of times that sample outcome was recorded by this + FreqDist. Frequencies are always real numbers in the range + [0, 1]. + + :param sample: the sample whose frequency + should be returned. + :type sample: any + :rtype: float + """ + n = self.N() + if n == 0: + return 0 + return self[sample] / n + + def max(self): + """ + Return the sample with the greatest number of outcomes in this + frequency distribution. If two or more samples have the same + number of outcomes, return one of them; which sample is + returned is undefined. If no outcomes have occurred in this + frequency distribution, return None. + + :return: The sample with the maximum number of outcomes in this + frequency distribution. + :rtype: any or None + """ + if len(self) == 0: + raise ValueError( + "A FreqDist must have at least one sample before max is defined." + ) + return self.most_common(1)[0][0] + + def plot( + self, *args, title="", cumulative=False, percents=False, show=True, **kwargs + ): + """ + Plot samples from the frequency distribution + displaying the most frequent sample first. If an integer + parameter is supplied, stop after this many samples have been + plotted. For a cumulative plot, specify cumulative=True. Additional + ``**kwargs`` are passed to matplotlib's plot function. + (Requires Matplotlib to be installed.) + + :param title: The title for the graph. + :type title: str + :param cumulative: Whether the plot is cumulative. (default = False) + :type cumulative: bool + :param percents: Whether the plot uses percents instead of counts. (default = False) + :type percents: bool + :param show: Whether to show the plot, or only return the ax. + :type show: bool + """ + try: + import matplotlib.pyplot as plt + except ImportError as e: + raise ValueError( + "The plot function requires matplotlib to be installed." + "See https://matplotlib.org/" + ) from e + + if len(args) == 0: + args = [len(self)] + samples = [item for item, _ in self.most_common(*args)] + + if cumulative: + freqs = list(self._cumulative_frequencies(samples)) + ylabel = "Cumulative " + else: + freqs = [self[sample] for sample in samples] + ylabel = "" + + if percents: + freqs = [f / self.N() * 100 for f in freqs] + ylabel += "Percents" + else: + ylabel += "Counts" + + ax = plt.gca() + ax.grid(True, color="silver") + + if "linewidth" not in kwargs: + kwargs["linewidth"] = 2 + if title: + ax.set_title(title) + + ax.plot(freqs, **kwargs) + ax.set_xticks(range(len(samples))) + ax.set_xticklabels([str(s) for s in samples], rotation=90) + ax.set_xlabel("Samples") + ax.set_ylabel(ylabel) + + if show: + plt.show() + + return ax + + def tabulate(self, *args, **kwargs): + """ + Tabulate the given samples from the frequency distribution (cumulative), + displaying the most frequent sample first. If an integer + parameter is supplied, stop after this many samples have been + plotted. + + :param samples: The samples to plot (default is all samples) + :type samples: list + :param cumulative: A flag to specify whether the freqs are cumulative (default = False) + :type title: bool + """ + if len(args) == 0: + args = [len(self)] + samples = _get_kwarg( + kwargs, "samples", [item for item, _ in self.most_common(*args)] + ) + + cumulative = _get_kwarg(kwargs, "cumulative", False) + if cumulative: + freqs = list(self._cumulative_frequencies(samples)) + else: + freqs = [self[sample] for sample in samples] + # percents = [f * 100 for f in freqs] only in ProbDist? + + width = max(len(f"{s}") for s in samples) + width = max(width, max(len("%d" % f) for f in freqs)) + + for i in range(len(samples)): + print("%*s" % (width, samples[i]), end=" ") + print() + for i in range(len(samples)): + print("%*d" % (width, freqs[i]), end=" ") + print() + + def copy(self): + """ + Create a copy of this frequency distribution. + + :rtype: FreqDist + """ + return self.__class__(self) + + # Mathematical operatiors + + def __add__(self, other): + """ + Add counts from two counters. + + >>> FreqDist('abbb') + FreqDist('bcc') + FreqDist({'b': 4, 'c': 2, 'a': 1}) + + """ + return self.__class__(super().__add__(other)) + + def __sub__(self, other): + """ + Subtract count, but keep only results with positive counts. + + >>> FreqDist('abbbc') - FreqDist('bccd') + FreqDist({'b': 2, 'a': 1}) + + """ + return self.__class__(super().__sub__(other)) + + def __or__(self, other): + """ + Union is the maximum of value in either of the input counters. + + >>> FreqDist('abbb') | FreqDist('bcc') + FreqDist({'b': 3, 'c': 2, 'a': 1}) + + """ + return self.__class__(super().__or__(other)) + + def __and__(self, other): + """ + Intersection is the minimum of corresponding counts. + + >>> FreqDist('abbb') & FreqDist('bcc') + FreqDist({'b': 1}) + + """ + return self.__class__(super().__and__(other)) + + def __le__(self, other): + """ + Returns True if this frequency distribution is a subset of the other + and for no key the value exceeds the value of the same key from + the other frequency distribution. + + The <= operator forms partial order and satisfying the axioms + reflexivity, antisymmetry and transitivity. + + >>> FreqDist('a') <= FreqDist('a') + True + >>> a = FreqDist('abc') + >>> b = FreqDist('aabc') + >>> (a <= b, b <= a) + (True, False) + >>> FreqDist('a') <= FreqDist('abcd') + True + >>> FreqDist('abc') <= FreqDist('xyz') + False + >>> FreqDist('xyz') <= FreqDist('abc') + False + >>> c = FreqDist('a') + >>> d = FreqDist('aa') + >>> e = FreqDist('aaa') + >>> c <= d and d <= e and c <= e + True + """ + if not isinstance(other, FreqDist): + raise_unorderable_types("<=", self, other) + return set(self).issubset(other) and all( + self[key] <= other[key] for key in self + ) + + def __ge__(self, other): + if not isinstance(other, FreqDist): + raise_unorderable_types(">=", self, other) + return set(self).issuperset(other) and all( + self[key] >= other[key] for key in other + ) + + __lt__ = lambda self, other: self <= other and not self == other + __gt__ = lambda self, other: self >= other and not self == other + + def __repr__(self): + """ + Return a string representation of this FreqDist. + + :rtype: string + """ + return self.pformat() + + def pprint(self, maxlen=10, stream=None): + """ + Print a string representation of this FreqDist to 'stream' + + :param maxlen: The maximum number of items to print + :type maxlen: int + :param stream: The stream to print to. stdout by default + """ + print(self.pformat(maxlen=maxlen), file=stream) + + def pformat(self, maxlen=10): + """ + Return a string representation of this FreqDist. + + :param maxlen: The maximum number of items to display + :type maxlen: int + :rtype: string + """ + items = ["{!r}: {!r}".format(*item) for item in self.most_common(maxlen)] + if len(self) > maxlen: + items.append("...") + return "FreqDist({{{0}}})".format(", ".join(items)) + + def __str__(self): + """ + Return a string representation of this FreqDist. + + :rtype: string + """ + return "" % (len(self), self.N()) + + def __iter__(self): + """ + Return an iterator which yields tokens ordered by frequency. + + :rtype: iterator + """ + for token, _ in self.most_common(self.B()): + yield token + + +##////////////////////////////////////////////////////// +## Probability Distributions +##////////////////////////////////////////////////////// + + +class ProbDistI(metaclass=ABCMeta): + """ + A probability distribution for the outcomes of an experiment. A + probability distribution specifies how likely it is that an + experiment will have any given outcome. For example, a + probability distribution could be used to predict the probability + that a token in a document will have a given type. Formally, a + probability distribution can be defined as a function mapping from + samples to nonnegative real numbers, such that the sum of every + number in the function's range is 1.0. A ``ProbDist`` is often + used to model the probability distribution of the experiment used + to generate a frequency distribution. + """ + + SUM_TO_ONE = True + """True if the probabilities of the samples in this probability + distribution will always sum to one.""" + + @abstractmethod + def __init__(self): + """ + Classes inheriting from ProbDistI should implement __init__. + """ + + @abstractmethod + def prob(self, sample): + """ + Return the probability for a given sample. Probabilities + are always real numbers in the range [0, 1]. + + :param sample: The sample whose probability + should be returned. + :type sample: any + :rtype: float + """ + + def logprob(self, sample): + """ + Return the base 2 logarithm of the probability for a given sample. + + :param sample: The sample whose probability + should be returned. + :type sample: any + :rtype: float + """ + # Default definition, in terms of prob() + p = self.prob(sample) + return math.log(p, 2) if p != 0 else _NINF + + @abstractmethod + def max(self): + """ + Return the sample with the greatest probability. If two or + more samples have the same probability, return one of them; + which sample is returned is undefined. + + :rtype: any + """ + + @abstractmethod + def samples(self): + """ + Return a list of all samples that have nonzero probabilities. + Use ``prob`` to find the probability of each sample. + + :rtype: list + """ + + # cf self.SUM_TO_ONE + def discount(self): + """ + Return the ratio by which counts are discounted on average: c*/c + + :rtype: float + """ + return 0.0 + + # Subclasses should define more efficient implementations of this, + # where possible. + def generate(self): + """ + Return a randomly selected sample from this probability distribution. + The probability of returning each sample ``samp`` is equal to + ``self.prob(samp)``. + """ + p = random.random() + p_init = p + for sample in self.samples(): + p -= self.prob(sample) + if p <= 0: + return sample + # allow for some rounding error: + if p < 0.0001: + return sample + # we *should* never get here + if self.SUM_TO_ONE: + warnings.warn( + "Probability distribution %r sums to %r; generate()" + " is returning an arbitrary sample." % (self, p_init - p) + ) + return random.choice(list(self.samples())) + + +class UniformProbDist(ProbDistI): + """ + A probability distribution that assigns equal probability to each + sample in a given set; and a zero probability to all other + samples. + """ + + def __init__(self, samples): + """ + Construct a new uniform probability distribution, that assigns + equal probability to each sample in ``samples``. + + :param samples: The samples that should be given uniform + probability. + :type samples: list + :raise ValueError: If ``samples`` is empty. + """ + if len(samples) == 0: + raise ValueError( + "A Uniform probability distribution must " + "have at least one sample." + ) + self._sampleset = set(samples) + self._prob = 1.0 / len(self._sampleset) + self._samples = list(self._sampleset) + + def prob(self, sample): + return self._prob if sample in self._sampleset else 0 + + def max(self): + return self._samples[0] + + def samples(self): + return self._samples + + def __repr__(self): + return "" % len(self._sampleset) + + +class RandomProbDist(ProbDistI): + """ + Generates a random probability distribution whereby each sample + will be between 0 and 1 with equal probability (uniform random distribution. + Also called a continuous uniform distribution). + """ + + def __init__(self, samples): + if len(samples) == 0: + raise ValueError( + "A probability distribution must " + "have at least one sample." + ) + self._probs = self.unirand(samples) + self._samples = list(self._probs.keys()) + + @classmethod + def unirand(cls, samples): + """ + The key function that creates a randomized initial distribution + that still sums to 1. Set as a dictionary of prob values so that + it can still be passed to MutableProbDist and called with identical + syntax to UniformProbDist + """ + samples = set(samples) + randrow = [random.random() for i in range(len(samples))] + total = sum(randrow) + for i, x in enumerate(randrow): + randrow[i] = x / total + + total = sum(randrow) + if total != 1: + # this difference, if present, is so small (near NINF) that it + # can be subtracted from any element without risking probs not (0 1) + randrow[-1] -= total - 1 + + return {s: randrow[i] for i, s in enumerate(samples)} + + def max(self): + if not hasattr(self, "_max"): + self._max = max((p, v) for (v, p) in self._probs.items())[1] + return self._max + + def prob(self, sample): + return self._probs.get(sample, 0) + + def samples(self): + return self._samples + + def __repr__(self): + return "" % len(self._probs) + + +class DictionaryProbDist(ProbDistI): + """ + A probability distribution whose probabilities are directly + specified by a given dictionary. The given dictionary maps + samples to probabilities. + """ + + def __init__(self, prob_dict=None, log=False, normalize=False): + """ + Construct a new probability distribution from the given + dictionary, which maps values to probabilities (or to log + probabilities, if ``log`` is true). If ``normalize`` is + true, then the probability values are scaled by a constant + factor such that they sum to 1. + + If called without arguments, the resulting probability + distribution assigns zero probability to all values. + """ + + self._prob_dict = prob_dict.copy() if prob_dict is not None else {} + self._log = log + + # Normalize the distribution, if requested. + if normalize: + if len(prob_dict) == 0: + raise ValueError( + "A DictionaryProbDist must have at least one sample " + + "before it can be normalized." + ) + if log: + value_sum = sum_logs(list(self._prob_dict.values())) + if value_sum <= _NINF: + logp = math.log(1.0 / len(prob_dict), 2) + for x in prob_dict: + self._prob_dict[x] = logp + else: + for (x, p) in self._prob_dict.items(): + self._prob_dict[x] -= value_sum + else: + value_sum = sum(self._prob_dict.values()) + if value_sum == 0: + p = 1.0 / len(prob_dict) + for x in prob_dict: + self._prob_dict[x] = p + else: + norm_factor = 1.0 / value_sum + for (x, p) in self._prob_dict.items(): + self._prob_dict[x] *= norm_factor + + def prob(self, sample): + if self._log: + return 2 ** (self._prob_dict[sample]) if sample in self._prob_dict else 0 + else: + return self._prob_dict.get(sample, 0) + + def logprob(self, sample): + if self._log: + return self._prob_dict.get(sample, _NINF) + else: + if sample not in self._prob_dict: + return _NINF + elif self._prob_dict[sample] == 0: + return _NINF + else: + return math.log(self._prob_dict[sample], 2) + + def max(self): + if not hasattr(self, "_max"): + self._max = max((p, v) for (v, p) in self._prob_dict.items())[1] + return self._max + + def samples(self): + return self._prob_dict.keys() + + def __repr__(self): + return "" % len(self._prob_dict) + + +class MLEProbDist(ProbDistI): + """ + The maximum likelihood estimate for the probability distribution + of the experiment used to generate a frequency distribution. The + "maximum likelihood estimate" approximates the probability of + each sample as the frequency of that sample in the frequency + distribution. + """ + + def __init__(self, freqdist, bins=None): + """ + Use the maximum likelihood estimate to create a probability + distribution for the experiment used to generate ``freqdist``. + + :type freqdist: FreqDist + :param freqdist: The frequency distribution that the + probability estimates should be based on. + """ + self._freqdist = freqdist + + def freqdist(self): + """ + Return the frequency distribution that this probability + distribution is based on. + + :rtype: FreqDist + """ + return self._freqdist + + def prob(self, sample): + return self._freqdist.freq(sample) + + def max(self): + return self._freqdist.max() + + def samples(self): + return self._freqdist.keys() + + def __repr__(self): + """ + :rtype: str + :return: A string representation of this ``ProbDist``. + """ + return "" % self._freqdist.N() + + +class LidstoneProbDist(ProbDistI): + """ + The Lidstone estimate for the probability distribution of the + experiment used to generate a frequency distribution. The + "Lidstone estimate" is parameterized by a real number *gamma*, + which typically ranges from 0 to 1. The Lidstone estimate + approximates the probability of a sample with count *c* from an + experiment with *N* outcomes and *B* bins as + ``c+gamma)/(N+B*gamma)``. This is equivalent to adding + *gamma* to the count for each bin, and taking the maximum + likelihood estimate of the resulting frequency distribution. + """ + + SUM_TO_ONE = False + + def __init__(self, freqdist, gamma, bins=None): + """ + Use the Lidstone estimate to create a probability distribution + for the experiment used to generate ``freqdist``. + + :type freqdist: FreqDist + :param freqdist: The frequency distribution that the + probability estimates should be based on. + :type gamma: float + :param gamma: A real number used to parameterize the + estimate. The Lidstone estimate is equivalent to adding + *gamma* to the count for each bin, and taking the + maximum likelihood estimate of the resulting frequency + distribution. + :type bins: int + :param bins: The number of sample values that can be generated + by the experiment that is described by the probability + distribution. This value must be correctly set for the + probabilities of the sample values to sum to one. If + ``bins`` is not specified, it defaults to ``freqdist.B()``. + """ + if (bins == 0) or (bins is None and freqdist.N() == 0): + name = self.__class__.__name__[:-8] + raise ValueError( + "A %s probability distribution " % name + "must have at least one bin." + ) + if (bins is not None) and (bins < freqdist.B()): + name = self.__class__.__name__[:-8] + raise ValueError( + "\nThe number of bins in a %s distribution " % name + + "(%d) must be greater than or equal to\n" % bins + + "the number of bins in the FreqDist used " + + "to create it (%d)." % freqdist.B() + ) + + self._freqdist = freqdist + self._gamma = float(gamma) + self._N = self._freqdist.N() + + if bins is None: + bins = freqdist.B() + self._bins = bins + + self._divisor = self._N + bins * gamma + if self._divisor == 0.0: + # In extreme cases we force the probability to be 0, + # which it will be, since the count will be 0: + self._gamma = 0 + self._divisor = 1 + + def freqdist(self): + """ + Return the frequency distribution that this probability + distribution is based on. + + :rtype: FreqDist + """ + return self._freqdist + + def prob(self, sample): + c = self._freqdist[sample] + return (c + self._gamma) / self._divisor + + def max(self): + # For Lidstone distributions, probability is monotonic with + # frequency, so the most probable sample is the one that + # occurs most frequently. + return self._freqdist.max() + + def samples(self): + return self._freqdist.keys() + + def discount(self): + gb = self._gamma * self._bins + return gb / (self._N + gb) + + def __repr__(self): + """ + Return a string representation of this ``ProbDist``. + + :rtype: str + """ + return "" % self._freqdist.N() + + +class LaplaceProbDist(LidstoneProbDist): + """ + The Laplace estimate for the probability distribution of the + experiment used to generate a frequency distribution. The + "Laplace estimate" approximates the probability of a sample with + count *c* from an experiment with *N* outcomes and *B* bins as + *(c+1)/(N+B)*. This is equivalent to adding one to the count for + each bin, and taking the maximum likelihood estimate of the + resulting frequency distribution. + """ + + def __init__(self, freqdist, bins=None): + """ + Use the Laplace estimate to create a probability distribution + for the experiment used to generate ``freqdist``. + + :type freqdist: FreqDist + :param freqdist: The frequency distribution that the + probability estimates should be based on. + :type bins: int + :param bins: The number of sample values that can be generated + by the experiment that is described by the probability + distribution. This value must be correctly set for the + probabilities of the sample values to sum to one. If + ``bins`` is not specified, it defaults to ``freqdist.B()``. + """ + LidstoneProbDist.__init__(self, freqdist, 1, bins) + + def __repr__(self): + """ + :rtype: str + :return: A string representation of this ``ProbDist``. + """ + return "" % self._freqdist.N() + + +class ELEProbDist(LidstoneProbDist): + """ + The expected likelihood estimate for the probability distribution + of the experiment used to generate a frequency distribution. The + "expected likelihood estimate" approximates the probability of a + sample with count *c* from an experiment with *N* outcomes and + *B* bins as *(c+0.5)/(N+B/2)*. This is equivalent to adding 0.5 + to the count for each bin, and taking the maximum likelihood + estimate of the resulting frequency distribution. + """ + + def __init__(self, freqdist, bins=None): + """ + Use the expected likelihood estimate to create a probability + distribution for the experiment used to generate ``freqdist``. + + :type freqdist: FreqDist + :param freqdist: The frequency distribution that the + probability estimates should be based on. + :type bins: int + :param bins: The number of sample values that can be generated + by the experiment that is described by the probability + distribution. This value must be correctly set for the + probabilities of the sample values to sum to one. If + ``bins`` is not specified, it defaults to ``freqdist.B()``. + """ + LidstoneProbDist.__init__(self, freqdist, 0.5, bins) + + def __repr__(self): + """ + Return a string representation of this ``ProbDist``. + + :rtype: str + """ + return "" % self._freqdist.N() + + +class HeldoutProbDist(ProbDistI): + """ + The heldout estimate for the probability distribution of the + experiment used to generate two frequency distributions. These + two frequency distributions are called the "heldout frequency + distribution" and the "base frequency distribution." The + "heldout estimate" uses uses the "heldout frequency + distribution" to predict the probability of each sample, given its + frequency in the "base frequency distribution". + + In particular, the heldout estimate approximates the probability + for a sample that occurs *r* times in the base distribution as + the average frequency in the heldout distribution of all samples + that occur *r* times in the base distribution. + + This average frequency is *Tr[r]/(Nr[r].N)*, where: + + - *Tr[r]* is the total count in the heldout distribution for + all samples that occur *r* times in the base distribution. + - *Nr[r]* is the number of samples that occur *r* times in + the base distribution. + - *N* is the number of outcomes recorded by the heldout + frequency distribution. + + In order to increase the efficiency of the ``prob`` member + function, *Tr[r]/(Nr[r].N)* is precomputed for each value of *r* + when the ``HeldoutProbDist`` is created. + + :type _estimate: list(float) + :ivar _estimate: A list mapping from *r*, the number of + times that a sample occurs in the base distribution, to the + probability estimate for that sample. ``_estimate[r]`` is + calculated by finding the average frequency in the heldout + distribution of all samples that occur *r* times in the base + distribution. In particular, ``_estimate[r]`` = + *Tr[r]/(Nr[r].N)*. + :type _max_r: int + :ivar _max_r: The maximum number of times that any sample occurs + in the base distribution. ``_max_r`` is used to decide how + large ``_estimate`` must be. + """ + + SUM_TO_ONE = False + + def __init__(self, base_fdist, heldout_fdist, bins=None): + """ + Use the heldout estimate to create a probability distribution + for the experiment used to generate ``base_fdist`` and + ``heldout_fdist``. + + :type base_fdist: FreqDist + :param base_fdist: The base frequency distribution. + :type heldout_fdist: FreqDist + :param heldout_fdist: The heldout frequency distribution. + :type bins: int + :param bins: The number of sample values that can be generated + by the experiment that is described by the probability + distribution. This value must be correctly set for the + probabilities of the sample values to sum to one. If + ``bins`` is not specified, it defaults to ``freqdist.B()``. + """ + + self._base_fdist = base_fdist + self._heldout_fdist = heldout_fdist + + # The max number of times any sample occurs in base_fdist. + self._max_r = base_fdist[base_fdist.max()] + + # Calculate Tr, Nr, and N. + Tr = self._calculate_Tr() + r_Nr = base_fdist.r_Nr(bins) + Nr = [r_Nr[r] for r in range(self._max_r + 1)] + N = heldout_fdist.N() + + # Use Tr, Nr, and N to compute the probability estimate for + # each value of r. + self._estimate = self._calculate_estimate(Tr, Nr, N) + + def _calculate_Tr(self): + """ + Return the list *Tr*, where *Tr[r]* is the total count in + ``heldout_fdist`` for all samples that occur *r* + times in ``base_fdist``. + + :rtype: list(float) + """ + Tr = [0.0] * (self._max_r + 1) + for sample in self._heldout_fdist: + r = self._base_fdist[sample] + Tr[r] += self._heldout_fdist[sample] + return Tr + + def _calculate_estimate(self, Tr, Nr, N): + """ + Return the list *estimate*, where *estimate[r]* is the probability + estimate for any sample that occurs *r* times in the base frequency + distribution. In particular, *estimate[r]* is *Tr[r]/(N[r].N)*. + In the special case that *N[r]=0*, *estimate[r]* will never be used; + so we define *estimate[r]=None* for those cases. + + :rtype: list(float) + :type Tr: list(float) + :param Tr: the list *Tr*, where *Tr[r]* is the total count in + the heldout distribution for all samples that occur *r* + times in base distribution. + :type Nr: list(float) + :param Nr: The list *Nr*, where *Nr[r]* is the number of + samples that occur *r* times in the base distribution. + :type N: int + :param N: The total number of outcomes recorded by the heldout + frequency distribution. + """ + estimate = [] + for r in range(self._max_r + 1): + if Nr[r] == 0: + estimate.append(None) + else: + estimate.append(Tr[r] / (Nr[r] * N)) + return estimate + + def base_fdist(self): + """ + Return the base frequency distribution that this probability + distribution is based on. + + :rtype: FreqDist + """ + return self._base_fdist + + def heldout_fdist(self): + """ + Return the heldout frequency distribution that this + probability distribution is based on. + + :rtype: FreqDist + """ + return self._heldout_fdist + + def samples(self): + return self._base_fdist.keys() + + def prob(self, sample): + # Use our precomputed probability estimate. + r = self._base_fdist[sample] + return self._estimate[r] + + def max(self): + # Note: the Heldout estimation is *not* necessarily monotonic; + # so this implementation is currently broken. However, it + # should give the right answer *most* of the time. :) + return self._base_fdist.max() + + def discount(self): + raise NotImplementedError() + + def __repr__(self): + """ + :rtype: str + :return: A string representation of this ``ProbDist``. + """ + s = "" + return s % (self._base_fdist.N(), self._heldout_fdist.N()) + + +class CrossValidationProbDist(ProbDistI): + """ + The cross-validation estimate for the probability distribution of + the experiment used to generate a set of frequency distribution. + The "cross-validation estimate" for the probability of a sample + is found by averaging the held-out estimates for the sample in + each pair of frequency distributions. + """ + + SUM_TO_ONE = False + + def __init__(self, freqdists, bins): + """ + Use the cross-validation estimate to create a probability + distribution for the experiment used to generate + ``freqdists``. + + :type freqdists: list(FreqDist) + :param freqdists: A list of the frequency distributions + generated by the experiment. + :type bins: int + :param bins: The number of sample values that can be generated + by the experiment that is described by the probability + distribution. This value must be correctly set for the + probabilities of the sample values to sum to one. If + ``bins`` is not specified, it defaults to ``freqdist.B()``. + """ + self._freqdists = freqdists + + # Create a heldout probability distribution for each pair of + # frequency distributions in freqdists. + self._heldout_probdists = [] + for fdist1 in freqdists: + for fdist2 in freqdists: + if fdist1 is not fdist2: + probdist = HeldoutProbDist(fdist1, fdist2, bins) + self._heldout_probdists.append(probdist) + + def freqdists(self): + """ + Return the list of frequency distributions that this ``ProbDist`` is based on. + + :rtype: list(FreqDist) + """ + return self._freqdists + + def samples(self): + # [xx] nb: this is not too efficient + return set(sum((list(fd) for fd in self._freqdists), [])) + + def prob(self, sample): + # Find the average probability estimate returned by each + # heldout distribution. + prob = 0.0 + for heldout_probdist in self._heldout_probdists: + prob += heldout_probdist.prob(sample) + return prob / len(self._heldout_probdists) + + def discount(self): + raise NotImplementedError() + + def __repr__(self): + """ + Return a string representation of this ``ProbDist``. + + :rtype: str + """ + return "" % len(self._freqdists) + + +class WittenBellProbDist(ProbDistI): + """ + The Witten-Bell estimate of a probability distribution. This distribution + allocates uniform probability mass to as yet unseen events by using the + number of events that have only been seen once. The probability mass + reserved for unseen events is equal to *T / (N + T)* + where *T* is the number of observed event types and *N* is the total + number of observed events. This equates to the maximum likelihood estimate + of a new type event occurring. The remaining probability mass is discounted + such that all probability estimates sum to one, yielding: + + - *p = T / Z (N + T)*, if count = 0 + - *p = c / (N + T)*, otherwise + """ + + def __init__(self, freqdist, bins=None): + """ + Creates a distribution of Witten-Bell probability estimates. This + distribution allocates uniform probability mass to as yet unseen + events by using the number of events that have only been seen once. The + probability mass reserved for unseen events is equal to *T / (N + T)* + where *T* is the number of observed event types and *N* is the total + number of observed events. This equates to the maximum likelihood + estimate of a new type event occurring. The remaining probability mass + is discounted such that all probability estimates sum to one, + yielding: + + - *p = T / Z (N + T)*, if count = 0 + - *p = c / (N + T)*, otherwise + + The parameters *T* and *N* are taken from the ``freqdist`` parameter + (the ``B()`` and ``N()`` values). The normalizing factor *Z* is + calculated using these values along with the ``bins`` parameter. + + :param freqdist: The frequency counts upon which to base the + estimation. + :type freqdist: FreqDist + :param bins: The number of possible event types. This must be at least + as large as the number of bins in the ``freqdist``. If None, then + it's assumed to be equal to that of the ``freqdist`` + :type bins: int + """ + assert bins is None or bins >= freqdist.B(), ( + "bins parameter must not be less than %d=freqdist.B()" % freqdist.B() + ) + if bins is None: + bins = freqdist.B() + self._freqdist = freqdist + self._T = self._freqdist.B() + self._Z = bins - self._freqdist.B() + self._N = self._freqdist.N() + # self._P0 is P(0), precalculated for efficiency: + if self._N == 0: + # if freqdist is empty, we approximate P(0) by a UniformProbDist: + self._P0 = 1.0 / self._Z + else: + self._P0 = self._T / (self._Z * (self._N + self._T)) + + def prob(self, sample): + # inherit docs from ProbDistI + c = self._freqdist[sample] + return c / (self._N + self._T) if c != 0 else self._P0 + + def max(self): + return self._freqdist.max() + + def samples(self): + return self._freqdist.keys() + + def freqdist(self): + return self._freqdist + + def discount(self): + raise NotImplementedError() + + def __repr__(self): + """ + Return a string representation of this ``ProbDist``. + + :rtype: str + """ + return "" % self._freqdist.N() + + +##////////////////////////////////////////////////////// +## Good-Turing Probability Distributions +##////////////////////////////////////////////////////// + +# Good-Turing frequency estimation was contributed by Alan Turing and +# his statistical assistant I.J. Good, during their collaboration in +# the WWII. It is a statistical technique for predicting the +# probability of occurrence of objects belonging to an unknown number +# of species, given past observations of such objects and their +# species. (In drawing balls from an urn, the 'objects' would be balls +# and the 'species' would be the distinct colors of the balls (finite +# but unknown in number). +# +# Good-Turing method calculates the probability mass to assign to +# events with zero or low counts based on the number of events with +# higher counts. It does so by using the adjusted count *c\**: +# +# - *c\* = (c + 1) N(c + 1) / N(c)* for c >= 1 +# - *things with frequency zero in training* = N(1) for c == 0 +# +# where *c* is the original count, *N(i)* is the number of event types +# observed with count *i*. We can think the count of unseen as the count +# of frequency one (see Jurafsky & Martin 2nd Edition, p101). +# +# This method is problematic because the situation ``N(c+1) == 0`` +# is quite common in the original Good-Turing estimation; smoothing or +# interpolation of *N(i)* values is essential in practice. +# +# Bill Gale and Geoffrey Sampson present a simple and effective approach, +# Simple Good-Turing. As a smoothing curve they simply use a power curve: +# +# Nr = a*r^b (with b < -1 to give the appropriate hyperbolic +# relationship) +# +# They estimate a and b by simple linear regression technique on the +# logarithmic form of the equation: +# +# log Nr = a + b*log(r) +# +# However, they suggest that such a simple curve is probably only +# appropriate for high values of r. For low values of r, they use the +# measured Nr directly. (see M&S, p.213) +# +# Gale and Sampson propose to use r while the difference between r and +# r* is 1.96 greater than the standard deviation, and switch to r* if +# it is less or equal: +# +# |r - r*| > 1.96 * sqrt((r + 1)^2 (Nr+1 / Nr^2) (1 + Nr+1 / Nr)) +# +# The 1.96 coefficient correspond to a 0.05 significance criterion, +# some implementations can use a coefficient of 1.65 for a 0.1 +# significance criterion. +# + +##////////////////////////////////////////////////////// +## Simple Good-Turing Probablity Distributions +##////////////////////////////////////////////////////// + + +class SimpleGoodTuringProbDist(ProbDistI): + """ + SimpleGoodTuring ProbDist approximates from frequency to frequency of + frequency into a linear line under log space by linear regression. + Details of Simple Good-Turing algorithm can be found in: + + - Good Turing smoothing without tears" (Gale & Sampson 1995), + Journal of Quantitative Linguistics, vol. 2 pp. 217-237. + - "Speech and Language Processing (Jurafsky & Martin), + 2nd Edition, Chapter 4.5 p103 (log(Nc) = a + b*log(c)) + - https://www.grsampson.net/RGoodTur.html + + Given a set of pair (xi, yi), where the xi denotes the frequency and + yi denotes the frequency of frequency, we want to minimize their + square variation. E(x) and E(y) represent the mean of xi and yi. + + - slope: b = sigma ((xi-E(x)(yi-E(y))) / sigma ((xi-E(x))(xi-E(x))) + - intercept: a = E(y) - b.E(x) + """ + + SUM_TO_ONE = False + + def __init__(self, freqdist, bins=None): + """ + :param freqdist: The frequency counts upon which to base the + estimation. + :type freqdist: FreqDist + :param bins: The number of possible event types. This must be + larger than the number of bins in the ``freqdist``. If None, + then it's assumed to be equal to ``freqdist``.B() + 1 + :type bins: int + """ + assert ( + bins is None or bins > freqdist.B() + ), "bins parameter must not be less than %d=freqdist.B()+1" % (freqdist.B() + 1) + if bins is None: + bins = freqdist.B() + 1 + self._freqdist = freqdist + self._bins = bins + r, nr = self._r_Nr() + self.find_best_fit(r, nr) + self._switch(r, nr) + self._renormalize(r, nr) + + def _r_Nr_non_zero(self): + r_Nr = self._freqdist.r_Nr() + del r_Nr[0] + return r_Nr + + def _r_Nr(self): + """ + Split the frequency distribution in two list (r, Nr), where Nr(r) > 0 + """ + nonzero = self._r_Nr_non_zero() + + if not nonzero: + return [], [] + return zip(*sorted(nonzero.items())) + + def find_best_fit(self, r, nr): + """ + Use simple linear regression to tune parameters self._slope and + self._intercept in the log-log space based on count and Nr(count) + (Work in log space to avoid floating point underflow.) + """ + # For higher sample frequencies the data points becomes horizontal + # along line Nr=1. To create a more evident linear model in log-log + # space, we average positive Nr values with the surrounding zero + # values. (Church and Gale, 1991) + + if not r or not nr: + # Empty r or nr? + return + + zr = [] + for j in range(len(r)): + i = r[j - 1] if j > 0 else 0 + k = 2 * r[j] - i if j == len(r) - 1 else r[j + 1] + zr_ = 2.0 * nr[j] / (k - i) + zr.append(zr_) + + log_r = [math.log(i) for i in r] + log_zr = [math.log(i) for i in zr] + + xy_cov = x_var = 0.0 + x_mean = sum(log_r) / len(log_r) + y_mean = sum(log_zr) / len(log_zr) + for (x, y) in zip(log_r, log_zr): + xy_cov += (x - x_mean) * (y - y_mean) + x_var += (x - x_mean) ** 2 + self._slope = xy_cov / x_var if x_var != 0 else 0.0 + if self._slope >= -1: + warnings.warn( + "SimpleGoodTuring did not find a proper best fit " + "line for smoothing probabilities of occurrences. " + "The probability estimates are likely to be " + "unreliable." + ) + self._intercept = y_mean - self._slope * x_mean + + def _switch(self, r, nr): + """ + Calculate the r frontier where we must switch from Nr to Sr + when estimating E[Nr]. + """ + for i, r_ in enumerate(r): + if len(r) == i + 1 or r[i + 1] != r_ + 1: + # We are at the end of r, or there is a gap in r + self._switch_at = r_ + break + + Sr = self.smoothedNr + smooth_r_star = (r_ + 1) * Sr(r_ + 1) / Sr(r_) + unsmooth_r_star = (r_ + 1) * nr[i + 1] / nr[i] + + std = math.sqrt(self._variance(r_, nr[i], nr[i + 1])) + if abs(unsmooth_r_star - smooth_r_star) <= 1.96 * std: + self._switch_at = r_ + break + + def _variance(self, r, nr, nr_1): + r = float(r) + nr = float(nr) + nr_1 = float(nr_1) + return (r + 1.0) ** 2 * (nr_1 / nr**2) * (1.0 + nr_1 / nr) + + def _renormalize(self, r, nr): + """ + It is necessary to renormalize all the probability estimates to + ensure a proper probability distribution results. This can be done + by keeping the estimate of the probability mass for unseen items as + N(1)/N and renormalizing all the estimates for previously seen items + (as Gale and Sampson (1995) propose). (See M&S P.213, 1999) + """ + prob_cov = 0.0 + for r_, nr_ in zip(r, nr): + prob_cov += nr_ * self._prob_measure(r_) + if prob_cov: + self._renormal = (1 - self._prob_measure(0)) / prob_cov + + def smoothedNr(self, r): + """ + Return the number of samples with count r. + + :param r: The amount of frequency. + :type r: int + :rtype: float + """ + + # Nr = a*r^b (with b < -1 to give the appropriate hyperbolic + # relationship) + # Estimate a and b by simple linear regression technique on + # the logarithmic form of the equation: log Nr = a + b*log(r) + + return math.exp(self._intercept + self._slope * math.log(r)) + + def prob(self, sample): + """ + Return the sample's probability. + + :param sample: sample of the event + :type sample: str + :rtype: float + """ + count = self._freqdist[sample] + p = self._prob_measure(count) + if count == 0: + if self._bins == self._freqdist.B(): + p = 0.0 + else: + p = p / (self._bins - self._freqdist.B()) + else: + p = p * self._renormal + return p + + def _prob_measure(self, count): + if count == 0 and self._freqdist.N() == 0: + return 1.0 + elif count == 0 and self._freqdist.N() != 0: + return self._freqdist.Nr(1) / self._freqdist.N() + + if self._switch_at > count: + Er_1 = self._freqdist.Nr(count + 1) + Er = self._freqdist.Nr(count) + else: + Er_1 = self.smoothedNr(count + 1) + Er = self.smoothedNr(count) + + r_star = (count + 1) * Er_1 / Er + return r_star / self._freqdist.N() + + def check(self): + prob_sum = 0.0 + for i in range(0, len(self._Nr)): + prob_sum += self._Nr[i] * self._prob_measure(i) / self._renormal + print("Probability Sum:", prob_sum) + # assert prob_sum != 1.0, "probability sum should be one!" + + def discount(self): + """ + This function returns the total mass of probability transfers from the + seen samples to the unseen samples. + """ + return self.smoothedNr(1) / self._freqdist.N() + + def max(self): + return self._freqdist.max() + + def samples(self): + return self._freqdist.keys() + + def freqdist(self): + return self._freqdist + + def __repr__(self): + """ + Return a string representation of this ``ProbDist``. + + :rtype: str + """ + return "" % self._freqdist.N() + + +class MutableProbDist(ProbDistI): + """ + An mutable probdist where the probabilities may be easily modified. This + simply copies an existing probdist, storing the probability values in a + mutable dictionary and providing an update method. + """ + + def __init__(self, prob_dist, samples, store_logs=True): + """ + Creates the mutable probdist based on the given prob_dist and using + the list of samples given. These values are stored as log + probabilities if the store_logs flag is set. + + :param prob_dist: the distribution from which to garner the + probabilities + :type prob_dist: ProbDist + :param samples: the complete set of samples + :type samples: sequence of any + :param store_logs: whether to store the probabilities as logarithms + :type store_logs: bool + """ + self._samples = samples + self._sample_dict = {samples[i]: i for i in range(len(samples))} + self._data = array.array("d", [0.0]) * len(samples) + for i in range(len(samples)): + if store_logs: + self._data[i] = prob_dist.logprob(samples[i]) + else: + self._data[i] = prob_dist.prob(samples[i]) + self._logs = store_logs + + def max(self): + # inherit documentation + return max((p, v) for (v, p) in self._sample_dict.items())[1] + + def samples(self): + # inherit documentation + return self._samples + + def prob(self, sample): + # inherit documentation + i = self._sample_dict.get(sample) + if i is None: + return 0.0 + return 2 ** (self._data[i]) if self._logs else self._data[i] + + def logprob(self, sample): + # inherit documentation + i = self._sample_dict.get(sample) + if i is None: + return float("-inf") + return self._data[i] if self._logs else math.log(self._data[i], 2) + + def update(self, sample, prob, log=True): + """ + Update the probability for the given sample. This may cause the object + to stop being the valid probability distribution - the user must + ensure that they update the sample probabilities such that all samples + have probabilities between 0 and 1 and that all probabilities sum to + one. + + :param sample: the sample for which to update the probability + :type sample: any + :param prob: the new probability + :type prob: float + :param log: is the probability already logged + :type log: bool + """ + i = self._sample_dict.get(sample) + assert i is not None + if self._logs: + self._data[i] = prob if log else math.log(prob, 2) + else: + self._data[i] = 2 ** (prob) if log else prob + + +##///////////////////////////////////////////////////// +## Kneser-Ney Probability Distribution +##////////////////////////////////////////////////////// + +# This method for calculating probabilities was introduced in 1995 by Reinhard +# Kneser and Hermann Ney. It was meant to improve the accuracy of language +# models that use backing-off to deal with sparse data. The authors propose two +# ways of doing so: a marginal distribution constraint on the back-off +# distribution and a leave-one-out distribution. For a start, the first one is +# implemented as a class below. +# +# The idea behind a back-off n-gram model is that we have a series of +# frequency distributions for our n-grams so that in case we have not seen a +# given n-gram during training (and as a result have a 0 probability for it) we +# can 'back off' (hence the name!) and try testing whether we've seen the +# n-1-gram part of the n-gram in training. +# +# The novelty of Kneser and Ney's approach was that they decided to fiddle +# around with the way this latter, backed off probability was being calculated +# whereas their peers seemed to focus on the primary probability. +# +# The implementation below uses one of the techniques described in their paper +# titled "Improved backing-off for n-gram language modeling." In the same paper +# another technique is introduced to attempt to smooth the back-off +# distribution as well as the primary one. There is also a much-cited +# modification of this method proposed by Chen and Goodman. +# +# In order for the implementation of Kneser-Ney to be more efficient, some +# changes have been made to the original algorithm. Namely, the calculation of +# the normalizing function gamma has been significantly simplified and +# combined slightly differently with beta. None of these changes affect the +# nature of the algorithm, but instead aim to cut out unnecessary calculations +# and take advantage of storing and retrieving information in dictionaries +# where possible. + + +class KneserNeyProbDist(ProbDistI): + """ + Kneser-Ney estimate of a probability distribution. This is a version of + back-off that counts how likely an n-gram is provided the n-1-gram had + been seen in training. Extends the ProbDistI interface, requires a trigram + FreqDist instance to train on. Optionally, a different from default discount + value can be specified. The default discount is set to 0.75. + + """ + + def __init__(self, freqdist, bins=None, discount=0.75): + """ + :param freqdist: The trigram frequency distribution upon which to base + the estimation + :type freqdist: FreqDist + :param bins: Included for compatibility with nltk.tag.hmm + :type bins: int or float + :param discount: The discount applied when retrieving counts of + trigrams + :type discount: float (preferred, but can be set to int) + """ + + if not bins: + self._bins = freqdist.B() + else: + self._bins = bins + self._D = discount + + # cache for probability calculation + self._cache = {} + + # internal bigram and trigram frequency distributions + self._bigrams = defaultdict(int) + self._trigrams = freqdist + + # helper dictionaries used to calculate probabilities + self._wordtypes_after = defaultdict(float) + self._trigrams_contain = defaultdict(float) + self._wordtypes_before = defaultdict(float) + for w0, w1, w2 in freqdist: + self._bigrams[(w0, w1)] += freqdist[(w0, w1, w2)] + self._wordtypes_after[(w0, w1)] += 1 + self._trigrams_contain[w1] += 1 + self._wordtypes_before[(w1, w2)] += 1 + + def prob(self, trigram): + # sample must be a triple + if len(trigram) != 3: + raise ValueError("Expected an iterable with 3 members.") + trigram = tuple(trigram) + w0, w1, w2 = trigram + + if trigram in self._cache: + return self._cache[trigram] + else: + # if the sample trigram was seen during training + if trigram in self._trigrams: + prob = (self._trigrams[trigram] - self.discount()) / self._bigrams[ + (w0, w1) + ] + + # else if the 'rougher' environment was seen during training + elif (w0, w1) in self._bigrams and (w1, w2) in self._wordtypes_before: + aftr = self._wordtypes_after[(w0, w1)] + bfr = self._wordtypes_before[(w1, w2)] + + # the probability left over from alphas + leftover_prob = (aftr * self.discount()) / self._bigrams[(w0, w1)] + + # the beta (including normalization) + beta = bfr / (self._trigrams_contain[w1] - aftr) + + prob = leftover_prob * beta + + # else the sample was completely unseen during training + else: + prob = 0.0 + + self._cache[trigram] = prob + return prob + + def discount(self): + """ + Return the value by which counts are discounted. By default set to 0.75. + + :rtype: float + """ + return self._D + + def set_discount(self, discount): + """ + Set the value by which counts are discounted to the value of discount. + + :param discount: the new value to discount counts by + :type discount: float (preferred, but int possible) + :rtype: None + """ + self._D = discount + + def samples(self): + return self._trigrams.keys() + + def max(self): + return self._trigrams.max() + + def __repr__(self): + """ + Return a string representation of this ProbDist + + :rtype: str + """ + return f">> from nltk.probability import ConditionalFreqDist + >>> from nltk.tokenize import word_tokenize + >>> sent = "the the the dog dog some other words that we do not care about" + >>> cfdist = ConditionalFreqDist() + >>> for word in word_tokenize(sent): + ... condition = len(word) + ... cfdist[condition][word] += 1 + + An equivalent way to do this is with the initializer: + + >>> cfdist = ConditionalFreqDist((len(word), word) for word in word_tokenize(sent)) + + The frequency distribution for each condition is accessed using + the indexing operator: + + >>> cfdist[3] + FreqDist({'the': 3, 'dog': 2, 'not': 1}) + >>> cfdist[3].freq('the') + 0.5 + >>> cfdist[3]['dog'] + 2 + + When the indexing operator is used to access the frequency + distribution for a condition that has not been accessed before, + ``ConditionalFreqDist`` creates a new empty FreqDist for that + condition. + + """ + + def __init__(self, cond_samples=None): + """ + Construct a new empty conditional frequency distribution. In + particular, the count for every sample, under every condition, + is zero. + + :param cond_samples: The samples to initialize the conditional + frequency distribution with + :type cond_samples: Sequence of (condition, sample) tuples + """ + defaultdict.__init__(self, FreqDist) + + if cond_samples: + for (cond, sample) in cond_samples: + self[cond][sample] += 1 + + def __reduce__(self): + kv_pairs = ((cond, self[cond]) for cond in self.conditions()) + return (self.__class__, (), None, None, kv_pairs) + + def conditions(self): + """ + Return a list of the conditions that have been accessed for + this ``ConditionalFreqDist``. Use the indexing operator to + access the frequency distribution for a given condition. + Note that the frequency distributions for some conditions + may contain zero sample outcomes. + + :rtype: list + """ + return list(self.keys()) + + def N(self): + """ + Return the total number of sample outcomes that have been + recorded by this ``ConditionalFreqDist``. + + :rtype: int + """ + return sum(fdist.N() for fdist in self.values()) + + def plot( + self, + *args, + samples=None, + title="", + cumulative=False, + percents=False, + conditions=None, + show=True, + **kwargs, + ): + """ + Plot the given samples from the conditional frequency distribution. + For a cumulative plot, specify cumulative=True. Additional ``*args`` and + ``**kwargs`` are passed to matplotlib's plot function. + (Requires Matplotlib to be installed.) + + :param samples: The samples to plot + :type samples: list + :param title: The title for the graph + :type title: str + :param cumulative: Whether the plot is cumulative. (default = False) + :type cumulative: bool + :param percents: Whether the plot uses percents instead of counts. (default = False) + :type percents: bool + :param conditions: The conditions to plot (default is all) + :type conditions: list + :param show: Whether to show the plot, or only return the ax. + :type show: bool + """ + try: + import matplotlib.pyplot as plt # import statement fix + except ImportError as e: + raise ValueError( + "The plot function requires matplotlib to be installed." + "See https://matplotlib.org/" + ) from e + + if not conditions: + conditions = self.conditions() + else: + conditions = [c for c in conditions if c in self] + if not samples: + samples = sorted({v for c in conditions for v in self[c]}) + if "linewidth" not in kwargs: + kwargs["linewidth"] = 2 + ax = plt.gca() + if conditions: + freqs = [] + for condition in conditions: + if cumulative: + # freqs should be a list of list where each sub list will be a frequency of a condition + freq = list(self[condition]._cumulative_frequencies(samples)) + else: + freq = [self[condition][sample] for sample in samples] + + if percents: + freq = [f / self[condition].N() * 100 for f in freq] + + freqs.append(freq) + + if cumulative: + ylabel = "Cumulative " + legend_loc = "lower right" + else: + ylabel = "" + legend_loc = "upper right" + + if percents: + ylabel += "Percents" + else: + ylabel += "Counts" + + i = 0 + for freq in freqs: + kwargs["label"] = conditions[i] # label for each condition + i += 1 + ax.plot(freq, *args, **kwargs) + ax.legend(loc=legend_loc) + ax.grid(True, color="silver") + ax.set_xticks(range(len(samples))) + ax.set_xticklabels([str(s) for s in samples], rotation=90) + if title: + ax.set_title(title) + ax.set_xlabel("Samples") + ax.set_ylabel(ylabel) + + if show: + plt.show() + + return ax + + def tabulate(self, *args, **kwargs): + """ + Tabulate the given samples from the conditional frequency distribution. + + :param samples: The samples to plot + :type samples: list + :param conditions: The conditions to plot (default is all) + :type conditions: list + :param cumulative: A flag to specify whether the freqs are cumulative (default = False) + :type title: bool + """ + + cumulative = _get_kwarg(kwargs, "cumulative", False) + conditions = _get_kwarg(kwargs, "conditions", sorted(self.conditions())) + samples = _get_kwarg( + kwargs, + "samples", + sorted({v for c in conditions if c in self for v in self[c]}), + ) # this computation could be wasted + + width = max(len("%s" % s) for s in samples) + freqs = dict() + for c in conditions: + if cumulative: + freqs[c] = list(self[c]._cumulative_frequencies(samples)) + else: + freqs[c] = [self[c][sample] for sample in samples] + width = max(width, max(len("%d" % f) for f in freqs[c])) + + condition_size = max(len("%s" % c) for c in conditions) + print(" " * condition_size, end=" ") + for s in samples: + print("%*s" % (width, s), end=" ") + print() + for c in conditions: + print("%*s" % (condition_size, c), end=" ") + for f in freqs[c]: + print("%*d" % (width, f), end=" ") + print() + + # Mathematical operators + + def __add__(self, other): + """ + Add counts from two ConditionalFreqDists. + """ + if not isinstance(other, ConditionalFreqDist): + return NotImplemented + result = self.copy() + for cond in other.conditions(): + result[cond] += other[cond] + return result + + def __sub__(self, other): + """ + Subtract count, but keep only results with positive counts. + """ + if not isinstance(other, ConditionalFreqDist): + return NotImplemented + result = self.copy() + for cond in other.conditions(): + result[cond] -= other[cond] + if not result[cond]: + del result[cond] + return result + + def __or__(self, other): + """ + Union is the maximum of value in either of the input counters. + """ + if not isinstance(other, ConditionalFreqDist): + return NotImplemented + result = self.copy() + for cond in other.conditions(): + result[cond] |= other[cond] + return result + + def __and__(self, other): + """ + Intersection is the minimum of corresponding counts. + """ + if not isinstance(other, ConditionalFreqDist): + return NotImplemented + result = ConditionalFreqDist() + for cond in self.conditions(): + newfreqdist = self[cond] & other[cond] + if newfreqdist: + result[cond] = newfreqdist + return result + + # @total_ordering doesn't work here, since the class inherits from a builtin class + def __le__(self, other): + if not isinstance(other, ConditionalFreqDist): + raise_unorderable_types("<=", self, other) + return set(self.conditions()).issubset(other.conditions()) and all( + self[c] <= other[c] for c in self.conditions() + ) + + def __lt__(self, other): + if not isinstance(other, ConditionalFreqDist): + raise_unorderable_types("<", self, other) + return self <= other and self != other + + def __ge__(self, other): + if not isinstance(other, ConditionalFreqDist): + raise_unorderable_types(">=", self, other) + return other <= self + + def __gt__(self, other): + if not isinstance(other, ConditionalFreqDist): + raise_unorderable_types(">", self, other) + return other < self + + def deepcopy(self): + from copy import deepcopy + + return deepcopy(self) + + copy = deepcopy + + def __repr__(self): + """ + Return a string representation of this ``ConditionalFreqDist``. + + :rtype: str + """ + return "" % len(self) + + +class ConditionalProbDistI(dict, metaclass=ABCMeta): + """ + A collection of probability distributions for a single experiment + run under different conditions. Conditional probability + distributions are used to estimate the likelihood of each sample, + given the condition under which the experiment was run. For + example, a conditional probability distribution could be used to + estimate the probability of each word type in a document, given + the length of the word type. Formally, a conditional probability + distribution can be defined as a function that maps from each + condition to the ``ProbDist`` for the experiment under that + condition. + """ + + @abstractmethod + def __init__(self): + """ + Classes inheriting from ConditionalProbDistI should implement __init__. + """ + + def conditions(self): + """ + Return a list of the conditions that are represented by + this ``ConditionalProbDist``. Use the indexing operator to + access the probability distribution for a given condition. + + :rtype: list + """ + return list(self.keys()) + + def __repr__(self): + """ + Return a string representation of this ``ConditionalProbDist``. + + :rtype: str + """ + return "<%s with %d conditions>" % (type(self).__name__, len(self)) + + +class ConditionalProbDist(ConditionalProbDistI): + """ + A conditional probability distribution modeling the experiments + that were used to generate a conditional frequency distribution. + A ConditionalProbDist is constructed from a + ``ConditionalFreqDist`` and a ``ProbDist`` factory: + + - The ``ConditionalFreqDist`` specifies the frequency + distribution for each condition. + - The ``ProbDist`` factory is a function that takes a + condition's frequency distribution, and returns its + probability distribution. A ``ProbDist`` class's name (such as + ``MLEProbDist`` or ``HeldoutProbDist``) can be used to specify + that class's constructor. + + The first argument to the ``ProbDist`` factory is the frequency + distribution that it should model; and the remaining arguments are + specified by the ``factory_args`` parameter to the + ``ConditionalProbDist`` constructor. For example, the following + code constructs a ``ConditionalProbDist``, where the probability + distribution for each condition is an ``ELEProbDist`` with 10 bins: + + >>> from nltk.corpus import brown + >>> from nltk.probability import ConditionalFreqDist + >>> from nltk.probability import ConditionalProbDist, ELEProbDist + >>> cfdist = ConditionalFreqDist(brown.tagged_words()[:5000]) + >>> cpdist = ConditionalProbDist(cfdist, ELEProbDist, 10) + >>> cpdist['passed'].max() + 'VBD' + >>> cpdist['passed'].prob('VBD') #doctest: +ELLIPSIS + 0.423... + + """ + + def __init__(self, cfdist, probdist_factory, *factory_args, **factory_kw_args): + """ + Construct a new conditional probability distribution, based on + the given conditional frequency distribution and ``ProbDist`` + factory. + + :type cfdist: ConditionalFreqDist + :param cfdist: The ``ConditionalFreqDist`` specifying the + frequency distribution for each condition. + :type probdist_factory: class or function + :param probdist_factory: The function or class that maps + a condition's frequency distribution to its probability + distribution. The function is called with the frequency + distribution as its first argument, + ``factory_args`` as its remaining arguments, and + ``factory_kw_args`` as keyword arguments. + :type factory_args: (any) + :param factory_args: Extra arguments for ``probdist_factory``. + These arguments are usually used to specify extra + properties for the probability distributions of individual + conditions, such as the number of bins they contain. + :type factory_kw_args: (any) + :param factory_kw_args: Extra keyword arguments for ``probdist_factory``. + """ + self._probdist_factory = probdist_factory + self._factory_args = factory_args + self._factory_kw_args = factory_kw_args + + for condition in cfdist: + self[condition] = probdist_factory( + cfdist[condition], *factory_args, **factory_kw_args + ) + + def __missing__(self, key): + self[key] = self._probdist_factory( + FreqDist(), *self._factory_args, **self._factory_kw_args + ) + return self[key] + + +class DictionaryConditionalProbDist(ConditionalProbDistI): + """ + An alternative ConditionalProbDist that simply wraps a dictionary of + ProbDists rather than creating these from FreqDists. + """ + + def __init__(self, probdist_dict): + """ + :param probdist_dict: a dictionary containing the probdists indexed + by the conditions + :type probdist_dict: dict any -> probdist + """ + self.update(probdist_dict) + + def __missing__(self, key): + self[key] = DictionaryProbDist() + return self[key] + + +##////////////////////////////////////////////////////// +## Adding in log-space. +##////////////////////////////////////////////////////// + +# If the difference is bigger than this, then just take the bigger one: +_ADD_LOGS_MAX_DIFF = math.log(1e-30, 2) + + +def add_logs(logx, logy): + """ + Given two numbers ``logx`` = *log(x)* and ``logy`` = *log(y)*, return + *log(x+y)*. Conceptually, this is the same as returning + ``log(2**(logx)+2**(logy))``, but the actual implementation + avoids overflow errors that could result from direct computation. + """ + if logx < logy + _ADD_LOGS_MAX_DIFF: + return logy + if logy < logx + _ADD_LOGS_MAX_DIFF: + return logx + base = min(logx, logy) + return base + math.log(2 ** (logx - base) + 2 ** (logy - base), 2) + + +def sum_logs(logs): + return reduce(add_logs, logs[1:], logs[0]) if len(logs) != 0 else _NINF + + +##////////////////////////////////////////////////////// +## Probabilistic Mix-in +##////////////////////////////////////////////////////// + + +class ProbabilisticMixIn: + """ + A mix-in class to associate probabilities with other classes + (trees, rules, etc.). To use the ``ProbabilisticMixIn`` class, + define a new class that derives from an existing class and from + ProbabilisticMixIn. You will need to define a new constructor for + the new class, which explicitly calls the constructors of both its + parent classes. For example: + + >>> from nltk.probability import ProbabilisticMixIn + >>> class A: + ... def __init__(self, x, y): self.data = (x,y) + ... + >>> class ProbabilisticA(A, ProbabilisticMixIn): + ... def __init__(self, x, y, **prob_kwarg): + ... A.__init__(self, x, y) + ... ProbabilisticMixIn.__init__(self, **prob_kwarg) + + See the documentation for the ProbabilisticMixIn + ``constructor<__init__>`` for information about the arguments it + expects. + + You should generally also redefine the string representation + methods, the comparison methods, and the hashing method. + """ + + def __init__(self, **kwargs): + """ + Initialize this object's probability. This initializer should + be called by subclass constructors. ``prob`` should generally be + the first argument for those constructors. + + :param prob: The probability associated with the object. + :type prob: float + :param logprob: The log of the probability associated with + the object. + :type logprob: float + """ + if "prob" in kwargs: + if "logprob" in kwargs: + raise TypeError("Must specify either prob or logprob " "(not both)") + else: + ProbabilisticMixIn.set_prob(self, kwargs["prob"]) + elif "logprob" in kwargs: + ProbabilisticMixIn.set_logprob(self, kwargs["logprob"]) + else: + self.__prob = self.__logprob = None + + def set_prob(self, prob): + """ + Set the probability associated with this object to ``prob``. + + :param prob: The new probability + :type prob: float + """ + self.__prob = prob + self.__logprob = None + + def set_logprob(self, logprob): + """ + Set the log probability associated with this object to + ``logprob``. I.e., set the probability associated with this + object to ``2**(logprob)``. + + :param logprob: The new log probability + :type logprob: float + """ + self.__logprob = logprob + self.__prob = None + + def prob(self): + """ + Return the probability associated with this object. + + :rtype: float + """ + if self.__prob is None: + if self.__logprob is None: + return None + self.__prob = 2 ** (self.__logprob) + return self.__prob + + def logprob(self): + """ + Return ``log(p)``, where ``p`` is the probability associated + with this object. + + :rtype: float + """ + if self.__logprob is None: + if self.__prob is None: + return None + self.__logprob = math.log(self.__prob, 2) + return self.__logprob + + +class ImmutableProbabilisticMixIn(ProbabilisticMixIn): + def set_prob(self, prob): + raise ValueError("%s is immutable" % self.__class__.__name__) + + def set_logprob(self, prob): + raise ValueError("%s is immutable" % self.__class__.__name__) + + +## Helper function for processing keyword arguments + + +def _get_kwarg(kwargs, key, default): + if key in kwargs: + arg = kwargs[key] + del kwargs[key] + else: + arg = default + return arg + + +##////////////////////////////////////////////////////// +## Demonstration +##////////////////////////////////////////////////////// + + +def _create_rand_fdist(numsamples, numoutcomes): + """ + Create a new frequency distribution, with random samples. The + samples are numbers from 1 to ``numsamples``, and are generated by + summing two numbers, each of which has a uniform distribution. + """ + + fdist = FreqDist() + for x in range(numoutcomes): + y = random.randint(1, (1 + numsamples) // 2) + random.randint( + 0, numsamples // 2 + ) + fdist[y] += 1 + return fdist + + +def _create_sum_pdist(numsamples): + """ + Return the true probability distribution for the experiment + ``_create_rand_fdist(numsamples, x)``. + """ + fdist = FreqDist() + for x in range(1, (1 + numsamples) // 2 + 1): + for y in range(0, numsamples // 2 + 1): + fdist[x + y] += 1 + return MLEProbDist(fdist) + + +def demo(numsamples=6, numoutcomes=500): + """ + A demonstration of frequency distributions and probability + distributions. This demonstration creates three frequency + distributions with, and uses them to sample a random process with + ``numsamples`` samples. Each frequency distribution is sampled + ``numoutcomes`` times. These three frequency distributions are + then used to build six probability distributions. Finally, the + probability estimates of these distributions are compared to the + actual probability of each sample. + + :type numsamples: int + :param numsamples: The number of samples to use in each demo + frequency distributions. + :type numoutcomes: int + :param numoutcomes: The total number of outcomes for each + demo frequency distribution. These outcomes are divided into + ``numsamples`` bins. + :rtype: None + """ + + # Randomly sample a stochastic process three times. + fdist1 = _create_rand_fdist(numsamples, numoutcomes) + fdist2 = _create_rand_fdist(numsamples, numoutcomes) + fdist3 = _create_rand_fdist(numsamples, numoutcomes) + + # Use our samples to create probability distributions. + pdists = [ + MLEProbDist(fdist1), + LidstoneProbDist(fdist1, 0.5, numsamples), + HeldoutProbDist(fdist1, fdist2, numsamples), + HeldoutProbDist(fdist2, fdist1, numsamples), + CrossValidationProbDist([fdist1, fdist2, fdist3], numsamples), + SimpleGoodTuringProbDist(fdist1), + SimpleGoodTuringProbDist(fdist1, 7), + _create_sum_pdist(numsamples), + ] + + # Find the probability of each sample. + vals = [] + for n in range(1, numsamples + 1): + vals.append(tuple([n, fdist1.freq(n)] + [pdist.prob(n) for pdist in pdists])) + + # Print the results in a formatted table. + print( + "%d samples (1-%d); %d outcomes were sampled for each FreqDist" + % (numsamples, numsamples, numoutcomes) + ) + print("=" * 9 * (len(pdists) + 2)) + FORMATSTR = " FreqDist " + "%8s " * (len(pdists) - 1) + "| Actual" + print(FORMATSTR % tuple(repr(pdist)[1:9] for pdist in pdists[:-1])) + print("-" * 9 * (len(pdists) + 2)) + FORMATSTR = "%3d %8.6f " + "%8.6f " * (len(pdists) - 1) + "| %8.6f" + for val in vals: + print(FORMATSTR % val) + + # Print the totals for each column (should all be 1.0) + zvals = list(zip(*vals)) + sums = [sum(val) for val in zvals[1:]] + print("-" * 9 * (len(pdists) + 2)) + FORMATSTR = "Total " + "%8.6f " * (len(pdists)) + "| %8.6f" + print(FORMATSTR % tuple(sums)) + print("=" * 9 * (len(pdists) + 2)) + + # Display the distributions themselves, if they're short enough. + if len("%s" % fdist1) < 70: + print(" fdist1: %s" % fdist1) + print(" fdist2: %s" % fdist2) + print(" fdist3: %s" % fdist3) + print() + + print("Generating:") + for pdist in pdists: + fdist = FreqDist(pdist.generate() for i in range(5000)) + print("{:>20} {}".format(pdist.__class__.__name__[:20], ("%s" % fdist)[:55])) + print() + + +def gt_demo(): + from nltk import corpus + + emma_words = corpus.gutenberg.words("austen-emma.txt") + fd = FreqDist(emma_words) + sgt = SimpleGoodTuringProbDist(fd) + print("{:>18} {:>8} {:>14}".format("word", "frequency", "SimpleGoodTuring")) + fd_keys_sorted = ( + key for key, value in sorted(fd.items(), key=lambda item: item[1], reverse=True) + ) + for key in fd_keys_sorted: + print("%18s %8d %14e" % (key, fd[key], sgt.prob(key))) + + +if __name__ == "__main__": + demo(6, 10) + demo(5, 5000) + gt_demo() + +__all__ = [ + "ConditionalFreqDist", + "ConditionalProbDist", + "ConditionalProbDistI", + "CrossValidationProbDist", + "DictionaryConditionalProbDist", + "DictionaryProbDist", + "ELEProbDist", + "FreqDist", + "SimpleGoodTuringProbDist", + "HeldoutProbDist", + "ImmutableProbabilisticMixIn", + "LaplaceProbDist", + "LidstoneProbDist", + "MLEProbDist", + "MutableProbDist", + "KneserNeyProbDist", + "ProbDistI", + "ProbabilisticMixIn", + "UniformProbDist", + "WittenBellProbDist", + "add_logs", + "log_likelihood", + "sum_logs", + "entropy", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__init__.py new file mode 100644 index 0000000..6e4d79d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__init__.py @@ -0,0 +1,75 @@ +# Natural Language Toolkit: Semantic Interpretation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK Semantic Interpretation Package + +This package contains classes for representing semantic structure in +formulas of first-order logic and for evaluating such formulas in +set-theoretic models. + + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + +The package has two main components: + + - ``logic`` provides support for analyzing expressions of First + Order Logic (FOL). + - ``evaluate`` allows users to recursively determine truth in a + model for formulas of FOL. + +A model consists of a domain of discourse and a valuation function, +which assigns values to non-logical constants. We assume that entities +in the domain are represented as strings such as ``'b1'``, ``'g1'``, +etc. A ``Valuation`` is initialized with a list of (symbol, value) +pairs, where values are entities, sets of entities or sets of tuples +of entities. +The domain of discourse can be inferred from the valuation, and model +is then created with domain and valuation as parameters. + + >>> from nltk.sem import Valuation, Model + >>> v = [('adam', 'b1'), ('betty', 'g1'), ('fido', 'd1'), + ... ('girl', set(['g1', 'g2'])), ('boy', set(['b1', 'b2'])), + ... ('dog', set(['d1'])), + ... ('love', set([('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')]))] + >>> val = Valuation(v) + >>> dom = val.domain + >>> m = Model(dom, val) +""" + +from nltk.sem.boxer import Boxer +from nltk.sem.drt import DRS, DrtExpression +from nltk.sem.evaluate import ( + Assignment, + Model, + Undefined, + Valuation, + arity, + is_rel, + read_valuation, + set2rel, +) +from nltk.sem.lfg import FStructure +from nltk.sem.logic import ( + ApplicationExpression, + Expression, + LogicalExpressionException, + Variable, + binding_ops, + boolean_ops, + equality_preds, + read_logic, +) +from nltk.sem.relextract import clause, extract_rels, rtuple +from nltk.sem.skolemize import skolemize +from nltk.sem.util import evaluate_sents, interpret_sents, parse_sents, root_semrep + +# from nltk.sem.glue import Glue +# from nltk.sem.hole import HoleSemantics +# from nltk.sem.cooper_storage import CooperStore + +# don't import chat80 as its names are too generic diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8c9b1bd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/boxer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/boxer.cpython-310.pyc new file mode 100644 index 0000000..f199193 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/boxer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/chat80.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/chat80.cpython-310.pyc new file mode 100644 index 0000000..9ab84c7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/chat80.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/cooper_storage.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/cooper_storage.cpython-310.pyc new file mode 100644 index 0000000..3ca57f1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/cooper_storage.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt.cpython-310.pyc new file mode 100644 index 0000000..80eff4e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt_glue_demo.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt_glue_demo.cpython-310.pyc new file mode 100644 index 0000000..851c4d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/drt_glue_demo.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/evaluate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/evaluate.cpython-310.pyc new file mode 100644 index 0000000..41a8b07 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/evaluate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/glue.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/glue.cpython-310.pyc new file mode 100644 index 0000000..8633543 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/glue.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/hole.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/hole.cpython-310.pyc new file mode 100644 index 0000000..63e5203 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/hole.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/lfg.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/lfg.cpython-310.pyc new file mode 100644 index 0000000..6677187 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/lfg.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/linearlogic.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/linearlogic.cpython-310.pyc new file mode 100644 index 0000000..99e0976 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/linearlogic.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/logic.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/logic.cpython-310.pyc new file mode 100644 index 0000000..0bd8658 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/logic.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/relextract.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/relextract.cpython-310.pyc new file mode 100644 index 0000000..633a7b9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/relextract.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/skolemize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/skolemize.cpython-310.pyc new file mode 100644 index 0000000..b947a32 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/skolemize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..bba1483 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/boxer.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/boxer.py new file mode 100644 index 0000000..df3026f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/boxer.py @@ -0,0 +1,1605 @@ +# Natural Language Toolkit: Interface to Boxer +# +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +An interface to Boxer. + +This interface relies on the latest version of the development (subversion) version of +C&C and Boxer. + +Usage +===== + +Set the environment variable CANDC to the bin directory of your CandC installation. +The models directory should be in the CandC root directory. +For example:: + + /path/to/candc/ + bin/ + candc + boxer + models/ + boxer/ +""" + +import operator +import os +import re +import subprocess +import tempfile +from functools import reduce +from optparse import OptionParser + +from nltk.internals import find_binary +from nltk.sem.drt import ( + DRS, + DrtApplicationExpression, + DrtEqualityExpression, + DrtNegatedExpression, + DrtOrExpression, + DrtParser, + DrtProposition, + DrtTokens, + DrtVariableExpression, +) +from nltk.sem.logic import ( + ExpectedMoreTokensException, + LogicalExpressionException, + UnexpectedTokenException, + Variable, +) + + +class Boxer: + """ + This class is an interface to Johan Bos's program Boxer, a wide-coverage + semantic parser that produces Discourse Representation Structures (DRSs). + """ + + def __init__( + self, + boxer_drs_interpreter=None, + elimeq=False, + bin_dir=None, + verbose=False, + resolve=True, + ): + """ + :param boxer_drs_interpreter: A class that converts from the + ``AbstractBoxerDrs`` object hierarchy to a different object. The + default is ``NltkDrtBoxerDrsInterpreter``, which converts to the NLTK + DRT hierarchy. + :param elimeq: When set to true, Boxer removes all equalities from the + DRSs and discourse referents standing in the equality relation are + unified, but only if this can be done in a meaning-preserving manner. + :param resolve: When set to true, Boxer will resolve all anaphoric DRSs and perform merge-reduction. + Resolution follows Van der Sandt's theory of binding and accommodation. + """ + if boxer_drs_interpreter is None: + boxer_drs_interpreter = NltkDrtBoxerDrsInterpreter() + self._boxer_drs_interpreter = boxer_drs_interpreter + + self._resolve = resolve + self._elimeq = elimeq + + self.set_bin_dir(bin_dir, verbose) + + def set_bin_dir(self, bin_dir, verbose=False): + self._candc_bin = self._find_binary("candc", bin_dir, verbose) + self._candc_models_path = os.path.normpath( + os.path.join(self._candc_bin[:-5], "../models") + ) + self._boxer_bin = self._find_binary("boxer", bin_dir, verbose) + + def interpret(self, input, discourse_id=None, question=False, verbose=False): + """ + Use Boxer to give a first order representation. + + :param input: str Input sentence to parse + :param occur_index: bool Should predicates be occurrence indexed? + :param discourse_id: str An identifier to be inserted to each occurrence-indexed predicate. + :return: ``drt.DrtExpression`` + """ + discourse_ids = [discourse_id] if discourse_id is not None else None + (d,) = self.interpret_multi_sents([[input]], discourse_ids, question, verbose) + if not d: + raise Exception(f'Unable to interpret: "{input}"') + return d + + def interpret_multi(self, input, discourse_id=None, question=False, verbose=False): + """ + Use Boxer to give a first order representation. + + :param input: list of str Input sentences to parse as a single discourse + :param occur_index: bool Should predicates be occurrence indexed? + :param discourse_id: str An identifier to be inserted to each occurrence-indexed predicate. + :return: ``drt.DrtExpression`` + """ + discourse_ids = [discourse_id] if discourse_id is not None else None + (d,) = self.interpret_multi_sents([input], discourse_ids, question, verbose) + if not d: + raise Exception(f'Unable to interpret: "{input}"') + return d + + def interpret_sents( + self, inputs, discourse_ids=None, question=False, verbose=False + ): + """ + Use Boxer to give a first order representation. + + :param inputs: list of str Input sentences to parse as individual discourses + :param occur_index: bool Should predicates be occurrence indexed? + :param discourse_ids: list of str Identifiers to be inserted to each occurrence-indexed predicate. + :return: list of ``drt.DrtExpression`` + """ + return self.interpret_multi_sents( + [[input] for input in inputs], discourse_ids, question, verbose + ) + + def interpret_multi_sents( + self, inputs, discourse_ids=None, question=False, verbose=False + ): + """ + Use Boxer to give a first order representation. + + :param inputs: list of list of str Input discourses to parse + :param occur_index: bool Should predicates be occurrence indexed? + :param discourse_ids: list of str Identifiers to be inserted to each occurrence-indexed predicate. + :return: ``drt.DrtExpression`` + """ + if discourse_ids is not None: + assert len(inputs) == len(discourse_ids) + assert reduce(operator.and_, (id is not None for id in discourse_ids)) + use_disc_id = True + else: + discourse_ids = list(map(str, range(len(inputs)))) + use_disc_id = False + + candc_out = self._call_candc(inputs, discourse_ids, question, verbose=verbose) + boxer_out = self._call_boxer(candc_out, verbose=verbose) + + # if 'ERROR: input file contains no ccg/2 terms.' in boxer_out: + # raise UnparseableInputException('Could not parse with candc: "%s"' % input_str) + + drs_dict = self._parse_to_drs_dict(boxer_out, use_disc_id) + return [drs_dict.get(id, None) for id in discourse_ids] + + def _call_candc(self, inputs, discourse_ids, question, verbose=False): + """ + Call the ``candc`` binary with the given input. + + :param inputs: list of list of str Input discourses to parse + :param discourse_ids: list of str Identifiers to be inserted to each occurrence-indexed predicate. + :param filename: str A filename for the output file + :return: stdout + """ + args = [ + "--models", + os.path.join(self._candc_models_path, ["boxer", "questions"][question]), + "--candc-printer", + "boxer", + ] + return self._call( + "\n".join( + sum( + ([f"'{id}'"] + d for d, id in zip(inputs, discourse_ids)), + [], + ) + ), + self._candc_bin, + args, + verbose, + ) + + def _call_boxer(self, candc_out, verbose=False): + """ + Call the ``boxer`` binary with the given input. + + :param candc_out: str output from C&C parser + :return: stdout + """ + f = None + try: + fd, temp_filename = tempfile.mkstemp( + prefix="boxer-", suffix=".in", text=True + ) + f = os.fdopen(fd, "w") + f.write(candc_out.decode("utf-8")) + finally: + if f: + f.close() + + args = [ + "--box", + "false", + "--semantics", + "drs", + #'--flat', 'false', # removed from boxer + "--resolve", + ["false", "true"][self._resolve], + "--elimeq", + ["false", "true"][self._elimeq], + "--format", + "prolog", + "--instantiate", + "true", + "--input", + temp_filename, + ] + stdout = self._call(None, self._boxer_bin, args, verbose) + os.remove(temp_filename) + return stdout + + def _find_binary(self, name, bin_dir, verbose=False): + return find_binary( + name, + path_to_bin=bin_dir, + env_vars=["CANDC"], + url="http://svn.ask.it.usyd.edu.au/trac/candc/", + binary_names=[name, name + ".exe"], + verbose=verbose, + ) + + def _call(self, input_str, binary, args=[], verbose=False): + """ + Call the binary with the given input. + + :param input_str: A string whose contents are used as stdin. + :param binary: The location of the binary to call + :param args: A list of command-line arguments. + :return: stdout + """ + if verbose: + print("Calling:", binary) + print("Args:", args) + print("Input:", input_str) + print("Command:", binary + " " + " ".join(args)) + + # Call via a subprocess + if input_str is None: + cmd = [binary] + args + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + else: + cmd = 'echo "{}" | {} {}'.format(input_str, binary, " ".join(args)) + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) + stdout, stderr = p.communicate() + + if verbose: + print("Return code:", p.returncode) + if stdout: + print("stdout:\n", stdout, "\n") + if stderr: + print("stderr:\n", stderr, "\n") + if p.returncode != 0: + raise Exception( + "ERROR CALLING: {} {}\nReturncode: {}\n{}".format( + binary, " ".join(args), p.returncode, stderr + ) + ) + + return stdout + + def _parse_to_drs_dict(self, boxer_out, use_disc_id): + lines = boxer_out.decode("utf-8").split("\n") + drs_dict = {} + i = 0 + while i < len(lines): + line = lines[i] + if line.startswith("id("): + comma_idx = line.index(",") + discourse_id = line[3:comma_idx] + if discourse_id[0] == "'" and discourse_id[-1] == "'": + discourse_id = discourse_id[1:-1] + drs_id = line[comma_idx + 1 : line.index(")")] + i += 1 + line = lines[i] + assert line.startswith(f"sem({drs_id},") + if line[-4:] == "').'": + line = line[:-4] + ")." + assert line.endswith(")."), f"can't parse line: {line}" + + search_start = len(f"sem({drs_id},[") + brace_count = 1 + drs_start = -1 + for j, c in enumerate(line[search_start:]): + if c == "[": + brace_count += 1 + if c == "]": + brace_count -= 1 + if brace_count == 0: + drs_start = search_start + j + 1 + if line[drs_start : drs_start + 3] == "','": + drs_start = drs_start + 3 + else: + drs_start = drs_start + 1 + break + assert drs_start > -1 + + drs_input = line[drs_start:-2].strip() + parsed = self._parse_drs(drs_input, discourse_id, use_disc_id) + drs_dict[discourse_id] = self._boxer_drs_interpreter.interpret(parsed) + i += 1 + return drs_dict + + def _parse_drs(self, drs_string, discourse_id, use_disc_id): + return BoxerOutputDrsParser([None, discourse_id][use_disc_id]).parse(drs_string) + + +class BoxerOutputDrsParser(DrtParser): + def __init__(self, discourse_id=None): + """ + This class is used to parse the Prolog DRS output from Boxer into a + hierarchy of python objects. + """ + DrtParser.__init__(self) + self.discourse_id = discourse_id + self.sentence_id_offset = None + self.quote_chars = [("'", "'", "\\", False)] + + def parse(self, data, signature=None): + return DrtParser.parse(self, data, signature) + + def get_all_symbols(self): + return ["(", ")", ",", "[", "]", ":"] + + def handle(self, tok, context): + return self.handle_drs(tok) + + def attempt_adjuncts(self, expression, context): + return expression + + def parse_condition(self, indices): + """ + Parse a DRS condition + + :return: list of ``DrtExpression`` + """ + tok = self.token() + accum = self.handle_condition(tok, indices) + if accum is None: + raise UnexpectedTokenException(tok) + return accum + + def handle_drs(self, tok): + if tok == "drs": + return self.parse_drs() + elif tok in ["merge", "smerge"]: + return self._handle_binary_expression(self._make_merge_expression)(None, []) + elif tok in ["alfa"]: + return self._handle_alfa(self._make_merge_expression)(None, []) + + def handle_condition(self, tok, indices): + """ + Handle a DRS condition + + :param indices: list of int + :return: list of ``DrtExpression`` + """ + if tok == "not": + return [self._handle_not()] + + if tok == "or": + conds = [self._handle_binary_expression(self._make_or_expression)] + elif tok == "imp": + conds = [self._handle_binary_expression(self._make_imp_expression)] + elif tok == "eq": + conds = [self._handle_eq()] + elif tok == "prop": + conds = [self._handle_prop()] + + elif tok == "pred": + conds = [self._handle_pred()] + elif tok == "named": + conds = [self._handle_named()] + elif tok == "rel": + conds = [self._handle_rel()] + elif tok == "timex": + conds = self._handle_timex() + elif tok == "card": + conds = [self._handle_card()] + + elif tok == "whq": + conds = [self._handle_whq()] + elif tok == "duplex": + conds = [self._handle_duplex()] + + else: + conds = [] + + return sum( + ( + [cond(sent_index, word_indices) for cond in conds] + for sent_index, word_indices in self._sent_and_word_indices(indices) + ), + [], + ) + + def _handle_not(self): + self.assertToken(self.token(), "(") + drs = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return BoxerNot(drs) + + def _handle_pred(self): + # pred(_G3943, dog, n, 0) + self.assertToken(self.token(), "(") + variable = self.parse_variable() + self.assertToken(self.token(), ",") + name = self.token() + self.assertToken(self.token(), ",") + pos = self.token() + self.assertToken(self.token(), ",") + sense = int(self.token()) + self.assertToken(self.token(), ")") + + def _handle_pred_f(sent_index, word_indices): + return BoxerPred( + self.discourse_id, sent_index, word_indices, variable, name, pos, sense + ) + + return _handle_pred_f + + def _handle_duplex(self): + # duplex(whq, drs(...), var, drs(...)) + self.assertToken(self.token(), "(") + # self.assertToken(self.token(), '[') + ans_types = [] + # while self.token(0) != ']': + # cat = self.token() + # self.assertToken(self.token(), ':') + # if cat == 'des': + # ans_types.append(self.token()) + # elif cat == 'num': + # ans_types.append('number') + # typ = self.token() + # if typ == 'cou': + # ans_types.append('count') + # else: + # ans_types.append(typ) + # else: + # ans_types.append(self.token()) + # self.token() #swallow the ']' + + self.assertToken(self.token(), "whq") + self.assertToken(self.token(), ",") + d1 = self.process_next_expression(None) + self.assertToken(self.token(), ",") + ref = self.parse_variable() + self.assertToken(self.token(), ",") + d2 = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerWhq( + self.discourse_id, sent_index, word_indices, ans_types, d1, ref, d2 + ) + + def _handle_named(self): + # named(x0, john, per, 0) + self.assertToken(self.token(), "(") + variable = self.parse_variable() + self.assertToken(self.token(), ",") + name = self.token() + self.assertToken(self.token(), ",") + type = self.token() + self.assertToken(self.token(), ",") + sense = self.token() # as per boxer rev 2554 + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerNamed( + self.discourse_id, sent_index, word_indices, variable, name, type, sense + ) + + def _handle_rel(self): + # rel(_G3993, _G3943, agent, 0) + self.assertToken(self.token(), "(") + var1 = self.parse_variable() + self.assertToken(self.token(), ",") + var2 = self.parse_variable() + self.assertToken(self.token(), ",") + rel = self.token() + self.assertToken(self.token(), ",") + sense = int(self.token()) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerRel( + self.discourse_id, sent_index, word_indices, var1, var2, rel, sense + ) + + def _handle_timex(self): + # timex(_G18322, date([]: (+), []:'XXXX', [1004]:'04', []:'XX')) + self.assertToken(self.token(), "(") + arg = self.parse_variable() + self.assertToken(self.token(), ",") + new_conds = self._handle_time_expression(arg) + self.assertToken(self.token(), ")") + return new_conds + + def _handle_time_expression(self, arg): + # date([]: (+), []:'XXXX', [1004]:'04', []:'XX') + tok = self.token() + self.assertToken(self.token(), "(") + if tok == "date": + conds = self._handle_date(arg) + elif tok == "time": + conds = self._handle_time(arg) + else: + return None + self.assertToken(self.token(), ")") + return [ + lambda sent_index, word_indices: BoxerPred( + self.discourse_id, sent_index, word_indices, arg, tok, "n", 0 + ) + ] + [lambda sent_index, word_indices: cond for cond in conds] + + def _handle_date(self, arg): + # []: (+), []:'XXXX', [1004]:'04', []:'XX' + conds = [] + ((sent_index, word_indices),) = self._sent_and_word_indices( + self._parse_index_list() + ) + self.assertToken(self.token(), "(") + pol = self.token() + self.assertToken(self.token(), ")") + conds.append( + BoxerPred( + self.discourse_id, + sent_index, + word_indices, + arg, + f"date_pol_{pol}", + "a", + 0, + ) + ) + self.assertToken(self.token(), ",") + + ((sent_index, word_indices),) = self._sent_and_word_indices( + self._parse_index_list() + ) + year = self.token() + if year != "XXXX": + year = year.replace(":", "_") + conds.append( + BoxerPred( + self.discourse_id, + sent_index, + word_indices, + arg, + f"date_year_{year}", + "a", + 0, + ) + ) + self.assertToken(self.token(), ",") + + ((sent_index, word_indices),) = self._sent_and_word_indices( + self._parse_index_list() + ) + month = self.token() + if month != "XX": + conds.append( + BoxerPred( + self.discourse_id, + sent_index, + word_indices, + arg, + f"date_month_{month}", + "a", + 0, + ) + ) + self.assertToken(self.token(), ",") + + ((sent_index, word_indices),) = self._sent_and_word_indices( + self._parse_index_list() + ) + day = self.token() + if day != "XX": + conds.append( + BoxerPred( + self.discourse_id, + sent_index, + word_indices, + arg, + f"date_day_{day}", + "a", + 0, + ) + ) + + return conds + + def _handle_time(self, arg): + # time([1018]:'18', []:'XX', []:'XX') + conds = [] + self._parse_index_list() + hour = self.token() + if hour != "XX": + conds.append(self._make_atom("r_hour_2", arg, hour)) + self.assertToken(self.token(), ",") + + self._parse_index_list() + min = self.token() + if min != "XX": + conds.append(self._make_atom("r_min_2", arg, min)) + self.assertToken(self.token(), ",") + + self._parse_index_list() + sec = self.token() + if sec != "XX": + conds.append(self._make_atom("r_sec_2", arg, sec)) + + return conds + + def _handle_card(self): + # card(_G18535, 28, ge) + self.assertToken(self.token(), "(") + variable = self.parse_variable() + self.assertToken(self.token(), ",") + value = self.token() + self.assertToken(self.token(), ",") + type = self.token() + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerCard( + self.discourse_id, sent_index, word_indices, variable, value, type + ) + + def _handle_prop(self): + # prop(_G15949, drs(...)) + self.assertToken(self.token(), "(") + variable = self.parse_variable() + self.assertToken(self.token(), ",") + drs = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerProp( + self.discourse_id, sent_index, word_indices, variable, drs + ) + + def _parse_index_list(self): + # [1001,1002]: + indices = [] + self.assertToken(self.token(), "[") + while self.token(0) != "]": + indices.append(self.parse_index()) + if self.token(0) == ",": + self.token() # swallow ',' + self.token() # swallow ']' + self.assertToken(self.token(), ":") + return indices + + def parse_drs(self): + # drs([[1001]:_G3943], + # [[1002]:pred(_G3943, dog, n, 0)] + # ) + self.assertToken(self.token(), "(") + self.assertToken(self.token(), "[") + refs = set() + while self.token(0) != "]": + indices = self._parse_index_list() + refs.add(self.parse_variable()) + if self.token(0) == ",": + self.token() # swallow ',' + self.token() # swallow ']' + self.assertToken(self.token(), ",") + self.assertToken(self.token(), "[") + conds = [] + while self.token(0) != "]": + indices = self._parse_index_list() + conds.extend(self.parse_condition(indices)) + if self.token(0) == ",": + self.token() # swallow ',' + self.token() # swallow ']' + self.assertToken(self.token(), ")") + return BoxerDrs(list(refs), conds) + + def _handle_binary_expression(self, make_callback): + self.assertToken(self.token(), "(") + drs1 = self.process_next_expression(None) + self.assertToken(self.token(), ",") + drs2 = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: make_callback( + sent_index, word_indices, drs1, drs2 + ) + + def _handle_alfa(self, make_callback): + self.assertToken(self.token(), "(") + type = self.token() + self.assertToken(self.token(), ",") + drs1 = self.process_next_expression(None) + self.assertToken(self.token(), ",") + drs2 = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: make_callback( + sent_index, word_indices, drs1, drs2 + ) + + def _handle_eq(self): + self.assertToken(self.token(), "(") + var1 = self.parse_variable() + self.assertToken(self.token(), ",") + var2 = self.parse_variable() + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerEq( + self.discourse_id, sent_index, word_indices, var1, var2 + ) + + def _handle_whq(self): + self.assertToken(self.token(), "(") + self.assertToken(self.token(), "[") + ans_types = [] + while self.token(0) != "]": + cat = self.token() + self.assertToken(self.token(), ":") + if cat == "des": + ans_types.append(self.token()) + elif cat == "num": + ans_types.append("number") + typ = self.token() + if typ == "cou": + ans_types.append("count") + else: + ans_types.append(typ) + else: + ans_types.append(self.token()) + self.token() # swallow the ']' + + self.assertToken(self.token(), ",") + d1 = self.process_next_expression(None) + self.assertToken(self.token(), ",") + ref = self.parse_variable() + self.assertToken(self.token(), ",") + d2 = self.process_next_expression(None) + self.assertToken(self.token(), ")") + return lambda sent_index, word_indices: BoxerWhq( + self.discourse_id, sent_index, word_indices, ans_types, d1, ref, d2 + ) + + def _make_merge_expression(self, sent_index, word_indices, drs1, drs2): + return BoxerDrs(drs1.refs + drs2.refs, drs1.conds + drs2.conds) + + def _make_or_expression(self, sent_index, word_indices, drs1, drs2): + return BoxerOr(self.discourse_id, sent_index, word_indices, drs1, drs2) + + def _make_imp_expression(self, sent_index, word_indices, drs1, drs2): + return BoxerDrs(drs1.refs, drs1.conds, drs2) + + def parse_variable(self): + var = self.token() + assert re.match(r"^[exps]\d+$", var), var + return var + + def parse_index(self): + return int(self.token()) + + def _sent_and_word_indices(self, indices): + """ + :return: list of (sent_index, word_indices) tuples + """ + sent_indices = {(i / 1000) - 1 for i in indices if i >= 0} + if sent_indices: + pairs = [] + for sent_index in sent_indices: + word_indices = [ + (i % 1000) - 1 for i in indices if sent_index == (i / 1000) - 1 + ] + pairs.append((sent_index, word_indices)) + return pairs + else: + word_indices = [(i % 1000) - 1 for i in indices] + return [(None, word_indices)] + + +class BoxerDrsParser(DrtParser): + """ + Reparse the str form of subclasses of ``AbstractBoxerDrs`` + """ + + def __init__(self, discourse_id=None): + DrtParser.__init__(self) + self.discourse_id = discourse_id + + def get_all_symbols(self): + return [ + DrtTokens.OPEN, + DrtTokens.CLOSE, + DrtTokens.COMMA, + DrtTokens.OPEN_BRACKET, + DrtTokens.CLOSE_BRACKET, + ] + + def attempt_adjuncts(self, expression, context): + return expression + + def handle(self, tok, context): + try: + # if tok == 'drs': + # self.assertNextToken(DrtTokens.OPEN) + # label = int(self.token()) + # self.assertNextToken(DrtTokens.COMMA) + # refs = list(map(int, self.handle_refs())) + # self.assertNextToken(DrtTokens.COMMA) + # conds = self.handle_conds(None) + # self.assertNextToken(DrtTokens.CLOSE) + # return BoxerDrs(label, refs, conds) + if tok == "pred": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = list(map(int, self.handle_refs())) + self.assertNextToken(DrtTokens.COMMA) + variable = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + name = self.token() + self.assertNextToken(DrtTokens.COMMA) + pos = self.token() + self.assertNextToken(DrtTokens.COMMA) + sense = int(self.token()) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerPred(disc_id, sent_id, word_ids, variable, name, pos, sense) + elif tok == "named": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + word_ids = map(int, self.handle_refs()) + self.assertNextToken(DrtTokens.COMMA) + variable = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + name = self.token() + self.assertNextToken(DrtTokens.COMMA) + type = self.token() + self.assertNextToken(DrtTokens.COMMA) + sense = int(self.token()) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerNamed( + disc_id, sent_id, word_ids, variable, name, type, sense + ) + elif tok == "rel": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = list(map(int, self.handle_refs())) + self.assertNextToken(DrtTokens.COMMA) + var1 = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + var2 = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + rel = self.token() + self.assertNextToken(DrtTokens.COMMA) + sense = int(self.token()) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerRel(disc_id, sent_id, word_ids, var1, var2, rel, sense) + elif tok == "prop": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + word_ids = list(map(int, self.handle_refs())) + self.assertNextToken(DrtTokens.COMMA) + variable = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + drs = self.process_next_expression(None) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerProp(disc_id, sent_id, word_ids, variable, drs) + elif tok == "not": + self.assertNextToken(DrtTokens.OPEN) + drs = self.process_next_expression(None) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerNot(drs) + elif tok == "imp": + self.assertNextToken(DrtTokens.OPEN) + drs1 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.COMMA) + drs2 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerDrs(drs1.refs, drs1.conds, drs2) + elif tok == "or": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = map(int, self.handle_refs()) + self.assertNextToken(DrtTokens.COMMA) + drs1 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.COMMA) + drs2 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerOr(disc_id, sent_id, word_ids, drs1, drs2) + elif tok == "eq": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = list(map(int, self.handle_refs())) + self.assertNextToken(DrtTokens.COMMA) + var1 = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + var2 = int(self.token()) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerEq(disc_id, sent_id, word_ids, var1, var2) + elif tok == "card": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = map(int, self.handle_refs()) + self.assertNextToken(DrtTokens.COMMA) + var = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + value = self.token() + self.assertNextToken(DrtTokens.COMMA) + type = self.token() + self.assertNextToken(DrtTokens.CLOSE) + return BoxerCard(disc_id, sent_id, word_ids, var, value, type) + elif tok == "whq": + self.assertNextToken(DrtTokens.OPEN) + disc_id = ( + self.discourse_id if self.discourse_id is not None else self.token() + ) + self.assertNextToken(DrtTokens.COMMA) + sent_id = self.nullableIntToken() + self.assertNextToken(DrtTokens.COMMA) + word_ids = list(map(int, self.handle_refs())) + self.assertNextToken(DrtTokens.COMMA) + ans_types = self.handle_refs() + self.assertNextToken(DrtTokens.COMMA) + drs1 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.COMMA) + var = int(self.token()) + self.assertNextToken(DrtTokens.COMMA) + drs2 = self.process_next_expression(None) + self.assertNextToken(DrtTokens.CLOSE) + return BoxerWhq(disc_id, sent_id, word_ids, ans_types, drs1, var, drs2) + except Exception as e: + raise LogicalExpressionException(self._currentIndex, str(e)) from e + assert False, repr(tok) + + def nullableIntToken(self): + t = self.token() + return int(t) if t != "None" else None + + def get_next_token_variable(self, description): + try: + return self.token() + except ExpectedMoreTokensException as e: + raise ExpectedMoreTokensException(e.index, "Variable expected.") from e + + +class AbstractBoxerDrs: + def variables(self): + """ + :return: (set, set, set) + """ + variables, events, propositions = self._variables() + return (variables - (events | propositions), events, propositions - events) + + def variable_types(self): + vartypes = {} + for t, vars in zip(("z", "e", "p"), self.variables()): + for v in vars: + vartypes[v] = t + return vartypes + + def _variables(self): + """ + :return: (set, set, set) + """ + return (set(), set(), set()) + + def atoms(self): + return set() + + def clean(self): + return self + + def _clean_name(self, name): + return name.replace("-", "_").replace("'", "_") + + def renumber_sentences(self, f): + return self + + def __hash__(self): + return hash(f"{self}") + + +class BoxerDrs(AbstractBoxerDrs): + def __init__(self, refs, conds, consequent=None): + AbstractBoxerDrs.__init__(self) + self.refs = refs + self.conds = conds + self.consequent = consequent + + def _variables(self): + variables = (set(), set(), set()) + for cond in self.conds: + for s, v in zip(variables, cond._variables()): + s.update(v) + if self.consequent is not None: + for s, v in zip(variables, self.consequent._variables()): + s.update(v) + return variables + + def atoms(self): + atoms = reduce(operator.or_, (cond.atoms() for cond in self.conds), set()) + if self.consequent is not None: + atoms.update(self.consequent.atoms()) + return atoms + + def clean(self): + consequent = self.consequent.clean() if self.consequent else None + return BoxerDrs(self.refs, [c.clean() for c in self.conds], consequent) + + def renumber_sentences(self, f): + consequent = self.consequent.renumber_sentences(f) if self.consequent else None + return BoxerDrs( + self.refs, [c.renumber_sentences(f) for c in self.conds], consequent + ) + + def __repr__(self): + s = "drs([{}], [{}])".format( + ", ".join("%s" % r for r in self.refs), + ", ".join("%s" % c for c in self.conds), + ) + if self.consequent is not None: + s = f"imp({s}, {self.consequent})" + return s + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.refs == other.refs + and len(self.conds) == len(other.conds) + and reduce( + operator.and_, (c1 == c2 for c1, c2 in zip(self.conds, other.conds)) + ) + and self.consequent == other.consequent + ) + + def __ne__(self, other): + return not self == other + + __hash__ = AbstractBoxerDrs.__hash__ + + +class BoxerNot(AbstractBoxerDrs): + def __init__(self, drs): + AbstractBoxerDrs.__init__(self) + self.drs = drs + + def _variables(self): + return self.drs._variables() + + def atoms(self): + return self.drs.atoms() + + def clean(self): + return BoxerNot(self.drs.clean()) + + def renumber_sentences(self, f): + return BoxerNot(self.drs.renumber_sentences(f)) + + def __repr__(self): + return "not(%s)" % (self.drs) + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.drs == other.drs + + def __ne__(self, other): + return not self == other + + __hash__ = AbstractBoxerDrs.__hash__ + + +class BoxerIndexed(AbstractBoxerDrs): + def __init__(self, discourse_id, sent_index, word_indices): + AbstractBoxerDrs.__init__(self) + self.discourse_id = discourse_id + self.sent_index = sent_index + self.word_indices = word_indices + + def atoms(self): + return {self} + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.discourse_id == other.discourse_id + and self.sent_index == other.sent_index + and self.word_indices == other.word_indices + and reduce(operator.and_, (s == o for s, o in zip(self, other))) + ) + + def __ne__(self, other): + return not self == other + + __hash__ = AbstractBoxerDrs.__hash__ + + def __repr__(self): + s = "{}({}, {}, [{}]".format( + self._pred(), + self.discourse_id, + self.sent_index, + ", ".join("%s" % wi for wi in self.word_indices), + ) + for v in self: + s += ", %s" % v + return s + ")" + + +class BoxerPred(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var, name, pos, sense): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var = var + self.name = name + self.pos = pos + self.sense = sense + + def _variables(self): + return ({self.var}, set(), set()) + + def change_var(self, var): + return BoxerPred( + self.discourse_id, + self.sent_index, + self.word_indices, + var, + self.name, + self.pos, + self.sense, + ) + + def clean(self): + return BoxerPred( + self.discourse_id, + self.sent_index, + self.word_indices, + self.var, + self._clean_name(self.name), + self.pos, + self.sense, + ) + + def renumber_sentences(self, f): + new_sent_index = f(self.sent_index) + return BoxerPred( + self.discourse_id, + new_sent_index, + self.word_indices, + self.var, + self.name, + self.pos, + self.sense, + ) + + def __iter__(self): + return iter((self.var, self.name, self.pos, self.sense)) + + def _pred(self): + return "pred" + + +class BoxerNamed(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var, name, type, sense): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var = var + self.name = name + self.type = type + self.sense = sense + + def _variables(self): + return ({self.var}, set(), set()) + + def change_var(self, var): + return BoxerNamed( + self.discourse_id, + self.sent_index, + self.word_indices, + var, + self.name, + self.type, + self.sense, + ) + + def clean(self): + return BoxerNamed( + self.discourse_id, + self.sent_index, + self.word_indices, + self.var, + self._clean_name(self.name), + self.type, + self.sense, + ) + + def renumber_sentences(self, f): + return BoxerNamed( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.var, + self.name, + self.type, + self.sense, + ) + + def __iter__(self): + return iter((self.var, self.name, self.type, self.sense)) + + def _pred(self): + return "named" + + +class BoxerRel(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var1, var2, rel, sense): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var1 = var1 + self.var2 = var2 + self.rel = rel + self.sense = sense + + def _variables(self): + return ({self.var1, self.var2}, set(), set()) + + def clean(self): + return BoxerRel( + self.discourse_id, + self.sent_index, + self.word_indices, + self.var1, + self.var2, + self._clean_name(self.rel), + self.sense, + ) + + def renumber_sentences(self, f): + return BoxerRel( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.var1, + self.var2, + self.rel, + self.sense, + ) + + def __iter__(self): + return iter((self.var1, self.var2, self.rel, self.sense)) + + def _pred(self): + return "rel" + + +class BoxerProp(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var, drs): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var = var + self.drs = drs + + def _variables(self): + return tuple( + map(operator.or_, (set(), set(), {self.var}), self.drs._variables()) + ) + + def referenced_labels(self): + return {self.drs} + + def atoms(self): + return self.drs.atoms() + + def clean(self): + return BoxerProp( + self.discourse_id, + self.sent_index, + self.word_indices, + self.var, + self.drs.clean(), + ) + + def renumber_sentences(self, f): + return BoxerProp( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.var, + self.drs.renumber_sentences(f), + ) + + def __iter__(self): + return iter((self.var, self.drs)) + + def _pred(self): + return "prop" + + +class BoxerEq(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var1, var2): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var1 = var1 + self.var2 = var2 + + def _variables(self): + return ({self.var1, self.var2}, set(), set()) + + def atoms(self): + return set() + + def renumber_sentences(self, f): + return BoxerEq( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.var1, + self.var2, + ) + + def __iter__(self): + return iter((self.var1, self.var2)) + + def _pred(self): + return "eq" + + +class BoxerCard(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, var, value, type): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.var = var + self.value = value + self.type = type + + def _variables(self): + return ({self.var}, set(), set()) + + def renumber_sentences(self, f): + return BoxerCard( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.var, + self.value, + self.type, + ) + + def __iter__(self): + return iter((self.var, self.value, self.type)) + + def _pred(self): + return "card" + + +class BoxerOr(BoxerIndexed): + def __init__(self, discourse_id, sent_index, word_indices, drs1, drs2): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.drs1 = drs1 + self.drs2 = drs2 + + def _variables(self): + return tuple(map(operator.or_, self.drs1._variables(), self.drs2._variables())) + + def atoms(self): + return self.drs1.atoms() | self.drs2.atoms() + + def clean(self): + return BoxerOr( + self.discourse_id, + self.sent_index, + self.word_indices, + self.drs1.clean(), + self.drs2.clean(), + ) + + def renumber_sentences(self, f): + return BoxerOr( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.drs1, + self.drs2, + ) + + def __iter__(self): + return iter((self.drs1, self.drs2)) + + def _pred(self): + return "or" + + +class BoxerWhq(BoxerIndexed): + def __init__( + self, discourse_id, sent_index, word_indices, ans_types, drs1, variable, drs2 + ): + BoxerIndexed.__init__(self, discourse_id, sent_index, word_indices) + self.ans_types = ans_types + self.drs1 = drs1 + self.variable = variable + self.drs2 = drs2 + + def _variables(self): + return tuple( + map( + operator.or_, + ({self.variable}, set(), set()), + self.drs1._variables(), + self.drs2._variables(), + ) + ) + + def atoms(self): + return self.drs1.atoms() | self.drs2.atoms() + + def clean(self): + return BoxerWhq( + self.discourse_id, + self.sent_index, + self.word_indices, + self.ans_types, + self.drs1.clean(), + self.variable, + self.drs2.clean(), + ) + + def renumber_sentences(self, f): + return BoxerWhq( + self.discourse_id, + f(self.sent_index), + self.word_indices, + self.ans_types, + self.drs1, + self.variable, + self.drs2, + ) + + def __iter__(self): + return iter( + ("[" + ",".join(self.ans_types) + "]", self.drs1, self.variable, self.drs2) + ) + + def _pred(self): + return "whq" + + +class PassthroughBoxerDrsInterpreter: + def interpret(self, ex): + return ex + + +class NltkDrtBoxerDrsInterpreter: + def __init__(self, occur_index=False): + self._occur_index = occur_index + + def interpret(self, ex): + """ + :param ex: ``AbstractBoxerDrs`` + :return: ``DrtExpression`` + """ + if isinstance(ex, BoxerDrs): + drs = DRS( + [Variable(r) for r in ex.refs], list(map(self.interpret, ex.conds)) + ) + if ex.consequent is not None: + drs.consequent = self.interpret(ex.consequent) + return drs + elif isinstance(ex, BoxerNot): + return DrtNegatedExpression(self.interpret(ex.drs)) + elif isinstance(ex, BoxerPred): + pred = self._add_occur_indexing(f"{ex.pos}_{ex.name}", ex) + return self._make_atom(pred, ex.var) + elif isinstance(ex, BoxerNamed): + pred = self._add_occur_indexing(f"ne_{ex.type}_{ex.name}", ex) + return self._make_atom(pred, ex.var) + elif isinstance(ex, BoxerRel): + pred = self._add_occur_indexing("%s" % (ex.rel), ex) + return self._make_atom(pred, ex.var1, ex.var2) + elif isinstance(ex, BoxerProp): + return DrtProposition(Variable(ex.var), self.interpret(ex.drs)) + elif isinstance(ex, BoxerEq): + return DrtEqualityExpression( + DrtVariableExpression(Variable(ex.var1)), + DrtVariableExpression(Variable(ex.var2)), + ) + elif isinstance(ex, BoxerCard): + pred = self._add_occur_indexing(f"card_{ex.type}_{ex.value}", ex) + return self._make_atom(pred, ex.var) + elif isinstance(ex, BoxerOr): + return DrtOrExpression(self.interpret(ex.drs1), self.interpret(ex.drs2)) + elif isinstance(ex, BoxerWhq): + drs1 = self.interpret(ex.drs1) + drs2 = self.interpret(ex.drs2) + return DRS(drs1.refs + drs2.refs, drs1.conds + drs2.conds) + assert False, f"{ex.__class__.__name__}: {ex}" + + def _make_atom(self, pred, *args): + accum = DrtVariableExpression(Variable(pred)) + for arg in args: + accum = DrtApplicationExpression( + accum, DrtVariableExpression(Variable(arg)) + ) + return accum + + def _add_occur_indexing(self, base, ex): + if self._occur_index and ex.sent_index is not None: + if ex.discourse_id: + base += "_%s" % ex.discourse_id + base += "_s%s" % ex.sent_index + base += "_w%s" % sorted(ex.word_indices)[0] + return base + + +class UnparseableInputException(Exception): + pass + + +if __name__ == "__main__": + opts = OptionParser("usage: %prog TEXT [options]") + opts.add_option( + "--verbose", + "-v", + help="display verbose logs", + action="store_true", + default=False, + dest="verbose", + ) + opts.add_option( + "--fol", "-f", help="output FOL", action="store_true", default=False, dest="fol" + ) + opts.add_option( + "--question", + "-q", + help="input is a question", + action="store_true", + default=False, + dest="question", + ) + opts.add_option( + "--occur", + "-o", + help="occurrence index", + action="store_true", + default=False, + dest="occur_index", + ) + (options, args) = opts.parse_args() + + if len(args) != 1: + opts.error("incorrect number of arguments") + + interpreter = NltkDrtBoxerDrsInterpreter(occur_index=options.occur_index) + drs = Boxer(interpreter).interpret_multi( + args[0].split(r"\n"), question=options.question, verbose=options.verbose + ) + if drs is None: + print(None) + else: + drs = drs.simplify().eliminate_equality() + if options.fol: + print(drs.fol().normalize()) + else: + drs.pretty_print() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/chat80.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/chat80.py new file mode 100644 index 0000000..5974804 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/chat80.py @@ -0,0 +1,857 @@ +# Natural Language Toolkit: Chat-80 KB Reader +# See https://www.w3.org/TR/swbp-skos-core-guide/ +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein , +# URL: +# For license information, see LICENSE.TXT + +r""" +Overview +======== + +Chat-80 was a natural language system which allowed the user to +interrogate a Prolog knowledge base in the domain of world +geography. It was developed in the early '80s by Warren and Pereira; see +``https://www.aclweb.org/anthology/J82-3002.pdf`` for a description and +``http://www.cis.upenn.edu/~pereira/oldies.html`` for the source +files. + +This module contains functions to extract data from the Chat-80 +relation files ('the world database'), and convert then into a format +that can be incorporated in the FOL models of +``nltk.sem.evaluate``. The code assumes that the Prolog +input files are available in the NLTK corpora directory. + +The Chat-80 World Database consists of the following files:: + + world0.pl + rivers.pl + cities.pl + countries.pl + contain.pl + borders.pl + +This module uses a slightly modified version of ``world0.pl``, in which +a set of Prolog rules have been omitted. The modified file is named +``world1.pl``. Currently, the file ``rivers.pl`` is not read in, since +it uses a list rather than a string in the second field. + +Reading Chat-80 Files +===================== + +Chat-80 relations are like tables in a relational database. The +relation acts as the name of the table; the first argument acts as the +'primary key'; and subsequent arguments are further fields in the +table. In general, the name of the table provides a label for a unary +predicate whose extension is all the primary keys. For example, +relations in ``cities.pl`` are of the following form:: + + 'city(athens,greece,1368).' + +Here, ``'athens'`` is the key, and will be mapped to a member of the +unary predicate *city*. + +The fields in the table are mapped to binary predicates. The first +argument of the predicate is the primary key, while the second +argument is the data in the relevant field. Thus, in the above +example, the third field is mapped to the binary predicate +*population_of*, whose extension is a set of pairs such as +``'(athens, 1368)'``. + +An exception to this general framework is required by the relations in +the files ``borders.pl`` and ``contains.pl``. These contain facts of the +following form:: + + 'borders(albania,greece).' + + 'contains0(africa,central_africa).' + +We do not want to form a unary concept out the element in +the first field of these records, and we want the label of the binary +relation just to be ``'border'``/``'contain'`` respectively. + +In order to drive the extraction process, we use 'relation metadata bundles' +which are Python dictionaries such as the following:: + + city = {'label': 'city', + 'closures': [], + 'schema': ['city', 'country', 'population'], + 'filename': 'cities.pl'} + +According to this, the file ``city['filename']`` contains a list of +relational tuples (or more accurately, the corresponding strings in +Prolog form) whose predicate symbol is ``city['label']`` and whose +relational schema is ``city['schema']``. The notion of a ``closure`` is +discussed in the next section. + +Concepts +======== +In order to encapsulate the results of the extraction, a class of +``Concept`` objects is introduced. A ``Concept`` object has a number of +attributes, in particular a ``prefLabel`` and ``extension``, which make +it easier to inspect the output of the extraction. In addition, the +``extension`` can be further processed: in the case of the ``'border'`` +relation, we check that the relation is symmetric, and in the case +of the ``'contain'`` relation, we carry out the transitive +closure. The closure properties associated with a concept is +indicated in the relation metadata, as indicated earlier. + +The ``extension`` of a ``Concept`` object is then incorporated into a +``Valuation`` object. + +Persistence +=========== +The functions ``val_dump`` and ``val_load`` are provided to allow a +valuation to be stored in a persistent database and re-loaded, rather +than having to be re-computed each time. + +Individuals and Lexical Items +============================= +As well as deriving relations from the Chat-80 data, we also create a +set of individual constants, one for each entity in the domain. The +individual constants are string-identical to the entities. For +example, given a data item such as ``'zloty'``, we add to the valuation +a pair ``('zloty', 'zloty')``. In order to parse English sentences that +refer to these entities, we also create a lexical item such as the +following for each individual constant:: + + PropN[num=sg, sem=<\P.(P zloty)>] -> 'Zloty' + +The set of rules is written to the file ``chat_pnames.cfg`` in the +current directory. + +""" + +import os +import re +import shelve +import sys + +import nltk.data + +########################################################################### +# Chat-80 relation metadata bundles needed to build the valuation +########################################################################### + +borders = { + "rel_name": "borders", + "closures": ["symmetric"], + "schema": ["region", "border"], + "filename": "borders.pl", +} + +contains = { + "rel_name": "contains0", + "closures": ["transitive"], + "schema": ["region", "contain"], + "filename": "contain.pl", +} + +city = { + "rel_name": "city", + "closures": [], + "schema": ["city", "country", "population"], + "filename": "cities.pl", +} + +country = { + "rel_name": "country", + "closures": [], + "schema": [ + "country", + "region", + "latitude", + "longitude", + "area", + "population", + "capital", + "currency", + ], + "filename": "countries.pl", +} + +circle_of_lat = { + "rel_name": "circle_of_latitude", + "closures": [], + "schema": ["circle_of_latitude", "degrees"], + "filename": "world1.pl", +} + +circle_of_long = { + "rel_name": "circle_of_longitude", + "closures": [], + "schema": ["circle_of_longitude", "degrees"], + "filename": "world1.pl", +} + +continent = { + "rel_name": "continent", + "closures": [], + "schema": ["continent"], + "filename": "world1.pl", +} + +region = { + "rel_name": "in_continent", + "closures": [], + "schema": ["region", "continent"], + "filename": "world1.pl", +} + +ocean = { + "rel_name": "ocean", + "closures": [], + "schema": ["ocean"], + "filename": "world1.pl", +} + +sea = {"rel_name": "sea", "closures": [], "schema": ["sea"], "filename": "world1.pl"} + + +items = [ + "borders", + "contains", + "city", + "country", + "circle_of_lat", + "circle_of_long", + "continent", + "region", + "ocean", + "sea", +] +items = tuple(sorted(items)) + +item_metadata = { + "borders": borders, + "contains": contains, + "city": city, + "country": country, + "circle_of_lat": circle_of_lat, + "circle_of_long": circle_of_long, + "continent": continent, + "region": region, + "ocean": ocean, + "sea": sea, +} + +rels = item_metadata.values() + +not_unary = ["borders.pl", "contain.pl"] + +########################################################################### + + +class Concept: + """ + A Concept class, loosely based on SKOS + (https://www.w3.org/TR/swbp-skos-core-guide/). + """ + + def __init__(self, prefLabel, arity, altLabels=[], closures=[], extension=set()): + """ + :param prefLabel: the preferred label for the concept + :type prefLabel: str + :param arity: the arity of the concept + :type arity: int + :param altLabels: other (related) labels + :type altLabels: list + :param closures: closure properties of the extension + (list items can be ``symmetric``, ``reflexive``, ``transitive``) + :type closures: list + :param extension: the extensional value of the concept + :type extension: set + """ + self.prefLabel = prefLabel + self.arity = arity + self.altLabels = altLabels + self.closures = closures + # keep _extension internally as a set + self._extension = extension + # public access is via a list (for slicing) + self.extension = sorted(list(extension)) + + def __str__(self): + # _extension = '' + # for element in sorted(self.extension): + # if isinstance(element, tuple): + # element = '(%s, %s)' % (element) + # _extension += element + ', ' + # _extension = _extension[:-1] + + return "Label = '{}'\nArity = {}\nExtension = {}".format( + self.prefLabel, + self.arity, + self.extension, + ) + + def __repr__(self): + return "Concept('%s')" % self.prefLabel + + def augment(self, data): + """ + Add more data to the ``Concept``'s extension set. + + :param data: a new semantic value + :type data: string or pair of strings + :rtype: set + + """ + self._extension.add(data) + self.extension = sorted(list(self._extension)) + return self._extension + + def _make_graph(self, s): + """ + Convert a set of pairs into an adjacency linked list encoding of a graph. + """ + g = {} + for (x, y) in s: + if x in g: + g[x].append(y) + else: + g[x] = [y] + return g + + def _transclose(self, g): + """ + Compute the transitive closure of a graph represented as a linked list. + """ + for x in g: + for adjacent in g[x]: + # check that adjacent is a key + if adjacent in g: + for y in g[adjacent]: + if y not in g[x]: + g[x].append(y) + return g + + def _make_pairs(self, g): + """ + Convert an adjacency linked list back into a set of pairs. + """ + pairs = [] + for node in g: + for adjacent in g[node]: + pairs.append((node, adjacent)) + return set(pairs) + + def close(self): + """ + Close a binary relation in the ``Concept``'s extension set. + + :return: a new extension for the ``Concept`` in which the + relation is closed under a given property + """ + from nltk.sem import is_rel + + assert is_rel(self._extension) + if "symmetric" in self.closures: + pairs = [] + for (x, y) in self._extension: + pairs.append((y, x)) + sym = set(pairs) + self._extension = self._extension.union(sym) + if "transitive" in self.closures: + all = self._make_graph(self._extension) + closed = self._transclose(all) + trans = self._make_pairs(closed) + self._extension = self._extension.union(trans) + self.extension = sorted(list(self._extension)) + + +def clause2concepts(filename, rel_name, schema, closures=[]): + """ + Convert a file of Prolog clauses into a list of ``Concept`` objects. + + :param filename: filename containing the relations + :type filename: str + :param rel_name: name of the relation + :type rel_name: str + :param schema: the schema used in a set of relational tuples + :type schema: list + :param closures: closure properties for the extension of the concept + :type closures: list + :return: a list of ``Concept`` objects + :rtype: list + """ + concepts = [] + # position of the subject of a binary relation + subj = 0 + # label of the 'primary key' + pkey = schema[0] + # fields other than the primary key + fields = schema[1:] + + # convert a file into a list of lists + records = _str2records(filename, rel_name) + + # add a unary concept corresponding to the set of entities + # in the primary key position + # relations in 'not_unary' are more like ordinary binary relations + if not filename in not_unary: + concepts.append(unary_concept(pkey, subj, records)) + + # add a binary concept for each non-key field + for field in fields: + obj = schema.index(field) + concepts.append(binary_concept(field, closures, subj, obj, records)) + + return concepts + + +def cities2table(filename, rel_name, dbname, verbose=False, setup=False): + """ + Convert a file of Prolog clauses into a database table. + + This is not generic, since it doesn't allow arbitrary + schemas to be set as a parameter. + + Intended usage:: + + cities2table('cities.pl', 'city', 'city.db', verbose=True, setup=True) + + :param filename: filename containing the relations + :type filename: str + :param rel_name: name of the relation + :type rel_name: str + :param dbname: filename of persistent store + :type schema: str + """ + import sqlite3 + + records = _str2records(filename, rel_name) + connection = sqlite3.connect(dbname) + cur = connection.cursor() + if setup: + cur.execute( + """CREATE TABLE city_table + (City text, Country text, Population int)""" + ) + + table_name = "city_table" + for t in records: + cur.execute("insert into %s values (?,?,?)" % table_name, t) + if verbose: + print("inserting values into %s: " % table_name, t) + connection.commit() + if verbose: + print("Committing update to %s" % dbname) + cur.close() + + +def sql_query(dbname, query): + """ + Execute an SQL query over a database. + :param dbname: filename of persistent store + :type schema: str + :param query: SQL query + :type rel_name: str + """ + import sqlite3 + + try: + path = nltk.data.find(dbname) + connection = sqlite3.connect(str(path)) + cur = connection.cursor() + return cur.execute(query) + except (ValueError, sqlite3.OperationalError): + import warnings + + warnings.warn( + "Make sure the database file %s is installed and uncompressed." % dbname + ) + raise + + +def _str2records(filename, rel): + """ + Read a file into memory and convert each relation clause into a list. + """ + recs = [] + contents = nltk.data.load("corpora/chat80/%s" % filename, format="text") + for line in contents.splitlines(): + if line.startswith(rel): + line = re.sub(rel + r"\(", "", line) + line = re.sub(r"\)\.$", "", line) + record = line.split(",") + recs.append(record) + return recs + + +def unary_concept(label, subj, records): + """ + Make a unary concept out of the primary key in a record. + + A record is a list of entities in some relation, such as + ``['france', 'paris']``, where ``'france'`` is acting as the primary + key. + + :param label: the preferred label for the concept + :type label: string + :param subj: position in the record of the subject of the predicate + :type subj: int + :param records: a list of records + :type records: list of lists + :return: ``Concept`` of arity 1 + :rtype: Concept + """ + c = Concept(label, arity=1, extension=set()) + for record in records: + c.augment(record[subj]) + return c + + +def binary_concept(label, closures, subj, obj, records): + """ + Make a binary concept out of the primary key and another field in a record. + + A record is a list of entities in some relation, such as + ``['france', 'paris']``, where ``'france'`` is acting as the primary + key, and ``'paris'`` stands in the ``'capital_of'`` relation to + ``'france'``. + + More generally, given a record such as ``['a', 'b', 'c']``, where + label is bound to ``'B'``, and ``obj`` bound to 1, the derived + binary concept will have label ``'B_of'``, and its extension will + be a set of pairs such as ``('a', 'b')``. + + + :param label: the base part of the preferred label for the concept + :type label: str + :param closures: closure properties for the extension of the concept + :type closures: list + :param subj: position in the record of the subject of the predicate + :type subj: int + :param obj: position in the record of the object of the predicate + :type obj: int + :param records: a list of records + :type records: list of lists + :return: ``Concept`` of arity 2 + :rtype: Concept + """ + if not label == "border" and not label == "contain": + label = label + "_of" + c = Concept(label, arity=2, closures=closures, extension=set()) + for record in records: + c.augment((record[subj], record[obj])) + # close the concept's extension according to the properties in closures + c.close() + return c + + +def process_bundle(rels): + """ + Given a list of relation metadata bundles, make a corresponding + dictionary of concepts, indexed by the relation name. + + :param rels: bundle of metadata needed for constructing a concept + :type rels: list(dict) + :return: a dictionary of concepts, indexed by the relation name. + :rtype: dict(str): Concept + """ + concepts = {} + for rel in rels: + rel_name = rel["rel_name"] + closures = rel["closures"] + schema = rel["schema"] + filename = rel["filename"] + + concept_list = clause2concepts(filename, rel_name, schema, closures) + for c in concept_list: + label = c.prefLabel + if label in concepts: + for data in c.extension: + concepts[label].augment(data) + concepts[label].close() + else: + concepts[label] = c + return concepts + + +def make_valuation(concepts, read=False, lexicon=False): + """ + Convert a list of ``Concept`` objects into a list of (label, extension) pairs; + optionally create a ``Valuation`` object. + + :param concepts: concepts + :type concepts: list(Concept) + :param read: if ``True``, ``(symbol, set)`` pairs are read into a ``Valuation`` + :type read: bool + :rtype: list or Valuation + """ + vals = [] + + for c in concepts: + vals.append((c.prefLabel, c.extension)) + if lexicon: + read = True + if read: + from nltk.sem import Valuation + + val = Valuation({}) + val.update(vals) + # add labels for individuals + val = label_indivs(val, lexicon=lexicon) + return val + else: + return vals + + +def val_dump(rels, db): + """ + Make a ``Valuation`` from a list of relation metadata bundles and dump to + persistent database. + + :param rels: bundle of metadata needed for constructing a concept + :type rels: list of dict + :param db: name of file to which data is written. + The suffix '.db' will be automatically appended. + :type db: str + """ + concepts = process_bundle(rels).values() + valuation = make_valuation(concepts, read=True) + db_out = shelve.open(db, "n") + + db_out.update(valuation) + + db_out.close() + + +def val_load(db): + """ + Load a ``Valuation`` from a persistent database. + + :param db: name of file from which data is read. + The suffix '.db' should be omitted from the name. + :type db: str + """ + dbname = db + ".db" + + if not os.access(dbname, os.R_OK): + sys.exit("Cannot read file: %s" % dbname) + else: + db_in = shelve.open(db) + from nltk.sem import Valuation + + val = Valuation(db_in) + # val.read(db_in.items()) + return val + + +# def alpha(str): +# """ +# Utility to filter out non-alphabetic constants. + +#:param str: candidate constant +#:type str: string +#:rtype: bool +# """ +# try: +# int(str) +# return False +# except ValueError: +## some unknown values in records are labeled '?' +# if not str == '?': +# return True + + +def label_indivs(valuation, lexicon=False): + """ + Assign individual constants to the individuals in the domain of a ``Valuation``. + + Given a valuation with an entry of the form ``{'rel': {'a': True}}``, + add a new entry ``{'a': 'a'}``. + + :type valuation: Valuation + :rtype: Valuation + """ + # collect all the individuals into a domain + domain = valuation.domain + # convert the domain into a sorted list of alphabetic terms + # use the same string as a label + pairs = [(e, e) for e in domain] + if lexicon: + lex = make_lex(domain) + with open("chat_pnames.cfg", "w") as outfile: + outfile.writelines(lex) + # read the pairs into the valuation + valuation.update(pairs) + return valuation + + +def make_lex(symbols): + """ + Create lexical CFG rules for each individual symbol. + + Given a valuation with an entry of the form ``{'zloty': 'zloty'}``, + create a lexical rule for the proper name 'Zloty'. + + :param symbols: a list of individual constants in the semantic representation + :type symbols: sequence -- set(str) + :rtype: list(str) + """ + lex = [] + header = """ +################################################################## +# Lexical rules automatically generated by running 'chat80.py -x'. +################################################################## + +""" + lex.append(header) + template = r"PropN[num=sg, sem=<\P.(P %s)>] -> '%s'\n" + + for s in symbols: + parts = s.split("_") + caps = [p.capitalize() for p in parts] + pname = "_".join(caps) + rule = template % (s, pname) + lex.append(rule) + return lex + + +########################################################################### +# Interface function to emulate other corpus readers +########################################################################### + + +def concepts(items=items): + """ + Build a list of concepts corresponding to the relation names in ``items``. + + :param items: names of the Chat-80 relations to extract + :type items: list(str) + :return: the ``Concept`` objects which are extracted from the relations + :rtype: list(Concept) + """ + if isinstance(items, str): + items = (items,) + + rels = [item_metadata[r] for r in items] + + concept_map = process_bundle(rels) + return concept_map.values() + + +########################################################################### + + +def main(): + import sys + from optparse import OptionParser + + description = """ +Extract data from the Chat-80 Prolog files and convert them into a +Valuation object for use in the NLTK semantics package. + """ + + opts = OptionParser(description=description) + opts.set_defaults(verbose=True, lex=False, vocab=False) + opts.add_option( + "-s", "--store", dest="outdb", help="store a valuation in DB", metavar="DB" + ) + opts.add_option( + "-l", + "--load", + dest="indb", + help="load a stored valuation from DB", + metavar="DB", + ) + opts.add_option( + "-c", + "--concepts", + action="store_true", + help="print concepts instead of a valuation", + ) + opts.add_option( + "-r", + "--relation", + dest="label", + help="print concept with label REL (check possible labels with '-v' option)", + metavar="REL", + ) + opts.add_option( + "-q", + "--quiet", + action="store_false", + dest="verbose", + help="don't print out progress info", + ) + opts.add_option( + "-x", + "--lex", + action="store_true", + dest="lex", + help="write a file of lexical entries for country names, then exit", + ) + opts.add_option( + "-v", + "--vocab", + action="store_true", + dest="vocab", + help="print out the vocabulary of concept labels and their arity, then exit", + ) + + (options, args) = opts.parse_args() + if options.outdb and options.indb: + opts.error("Options --store and --load are mutually exclusive") + + if options.outdb: + # write the valuation to a persistent database + if options.verbose: + outdb = options.outdb + ".db" + print("Dumping a valuation to %s" % outdb) + val_dump(rels, options.outdb) + sys.exit(0) + else: + # try to read in a valuation from a database + if options.indb is not None: + dbname = options.indb + ".db" + if not os.access(dbname, os.R_OK): + sys.exit("Cannot read file: %s" % dbname) + else: + valuation = val_load(options.indb) + # we need to create the valuation from scratch + else: + # build some concepts + concept_map = process_bundle(rels) + concepts = concept_map.values() + # just print out the vocabulary + if options.vocab: + items = sorted((c.arity, c.prefLabel) for c in concepts) + for (arity, label) in items: + print(label, arity) + sys.exit(0) + # show all the concepts + if options.concepts: + for c in concepts: + print(c) + print() + if options.label: + print(concept_map[options.label]) + sys.exit(0) + else: + # turn the concepts into a Valuation + if options.lex: + if options.verbose: + print("Writing out lexical rules") + make_valuation(concepts, lexicon=True) + else: + valuation = make_valuation(concepts, read=True) + print(valuation) + + +def sql_demo(): + """ + Print out every row from the 'city.db' database. + """ + print() + print("Using SQL to extract rows from 'city.db' RDB.") + for row in sql_query("corpora/city_database/city.db", "SELECT * FROM city_table"): + print(row) + + +if __name__ == "__main__": + main() + sql_demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/cooper_storage.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/cooper_storage.py new file mode 100644 index 0000000..66ebaeb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/cooper_storage.py @@ -0,0 +1,124 @@ +# Natural Language Toolkit: Cooper storage for Quantifier Ambiguity +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +from nltk.parse import load_parser +from nltk.parse.featurechart import InstantiateVarsChart +from nltk.sem.logic import ApplicationExpression, LambdaExpression, Variable + + +class CooperStore: + """ + A container for handling quantifier ambiguity via Cooper storage. + """ + + def __init__(self, featstruct): + """ + :param featstruct: The value of the ``sem`` node in a tree from + ``parse_with_bindops()`` + :type featstruct: FeatStruct (with features ``core`` and ``store``) + + """ + self.featstruct = featstruct + self.readings = [] + try: + self.core = featstruct["CORE"] + self.store = featstruct["STORE"] + except KeyError: + print("%s is not a Cooper storage structure" % featstruct) + + def _permute(self, lst): + """ + :return: An iterator over the permutations of the input list + :type lst: list + :rtype: iter + """ + remove = lambda lst0, index: lst0[:index] + lst0[index + 1 :] + if lst: + for index, x in enumerate(lst): + for y in self._permute(remove(lst, index)): + yield (x,) + y + else: + yield () + + def s_retrieve(self, trace=False): + r""" + Carry out S-Retrieval of binding operators in store. If hack=True, + serialize the bindop and core as strings and reparse. Ugh. + + Each permutation of the store (i.e. list of binding operators) is + taken to be a possible scoping of quantifiers. We iterate through the + binding operators in each permutation, and successively apply them to + the current term, starting with the core semantic representation, + working from the inside out. + + Binding operators are of the form:: + + bo(\P.all x.(man(x) -> P(x)),z1) + """ + for perm, store_perm in enumerate(self._permute(self.store)): + if trace: + print("Permutation %s" % (perm + 1)) + term = self.core + for bindop in store_perm: + # we just want the arguments that are wrapped by the 'bo' predicate + quant, varex = tuple(bindop.args) + # use var to make an abstraction over the current term and then + # apply the quantifier to it + term = ApplicationExpression( + quant, LambdaExpression(varex.variable, term) + ) + if trace: + print(" ", term) + term = term.simplify() + self.readings.append(term) + + +def parse_with_bindops(sentence, grammar=None, trace=0): + """ + Use a grammar with Binding Operators to parse a sentence. + """ + if not grammar: + grammar = "grammars/book_grammars/storage.fcfg" + parser = load_parser(grammar, trace=trace, chart_class=InstantiateVarsChart) + # Parse the sentence. + tokens = sentence.split() + return list(parser.parse(tokens)) + + +def demo(): + from nltk.sem import cooper_storage as cs + + sentence = "every girl chases a dog" + # sentence = "a man gives a bone to every dog" + print() + print("Analysis of sentence '%s'" % sentence) + print("=" * 50) + trees = cs.parse_with_bindops(sentence, trace=0) + for tree in trees: + semrep = cs.CooperStore(tree.label()["SEM"]) + print() + print("Binding operators:") + print("-" * 15) + for s in semrep.store: + print(s) + print() + print("Core:") + print("-" * 15) + print(semrep.core) + print() + print("S-Retrieval:") + print("-" * 15) + semrep.s_retrieve(trace=True) + print("Readings:") + print("-" * 15) + + for i, reading in enumerate(semrep.readings): + print(f"{i + 1}: {reading}") + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt.py new file mode 100644 index 0000000..aed8f59 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt.py @@ -0,0 +1,1460 @@ +# Natural Language Toolkit: Discourse Representation Theory (DRT) +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +import operator +from functools import reduce +from itertools import chain + +from nltk.sem.logic import ( + APP, + AbstractVariableExpression, + AllExpression, + AndExpression, + ApplicationExpression, + BinaryExpression, + BooleanExpression, + ConstantExpression, + EqualityExpression, + EventVariableExpression, + ExistsExpression, + Expression, + FunctionVariableExpression, + ImpExpression, + IndividualVariableExpression, + LambdaExpression, + LogicParser, + NegatedExpression, + OrExpression, + Tokens, + Variable, + is_eventvar, + is_funcvar, + is_indvar, + unique_variable, +) + +# Import Tkinter-based modules if they are available +try: + from tkinter import Canvas, Tk + from tkinter.font import Font + + from nltk.util import in_idle + +except ImportError: + # No need to print a warning here, nltk.draw has already printed one. + pass + + +class DrtTokens(Tokens): + DRS = "DRS" + DRS_CONC = "+" + PRONOUN = "PRO" + OPEN_BRACKET = "[" + CLOSE_BRACKET = "]" + COLON = ":" + + PUNCT = [DRS_CONC, OPEN_BRACKET, CLOSE_BRACKET, COLON] + + SYMBOLS = Tokens.SYMBOLS + PUNCT + + TOKENS = Tokens.TOKENS + [DRS] + PUNCT + + +class DrtParser(LogicParser): + """A lambda calculus expression parser.""" + + def __init__(self): + LogicParser.__init__(self) + + self.operator_precedence = dict( + [(x, 1) for x in DrtTokens.LAMBDA_LIST] + + [(x, 2) for x in DrtTokens.NOT_LIST] + + [(APP, 3)] + + [(x, 4) for x in DrtTokens.EQ_LIST + Tokens.NEQ_LIST] + + [(DrtTokens.COLON, 5)] + + [(DrtTokens.DRS_CONC, 6)] + + [(x, 7) for x in DrtTokens.OR_LIST] + + [(x, 8) for x in DrtTokens.IMP_LIST] + + [(None, 9)] + ) + + def get_all_symbols(self): + """This method exists to be overridden""" + return DrtTokens.SYMBOLS + + def isvariable(self, tok): + return tok not in DrtTokens.TOKENS + + def handle(self, tok, context): + """This method is intended to be overridden for logics that + use different operators or expressions""" + if tok in DrtTokens.NOT_LIST: + return self.handle_negation(tok, context) + + elif tok in DrtTokens.LAMBDA_LIST: + return self.handle_lambda(tok, context) + + elif tok == DrtTokens.OPEN: + if self.inRange(0) and self.token(0) == DrtTokens.OPEN_BRACKET: + return self.handle_DRS(tok, context) + else: + return self.handle_open(tok, context) + + elif tok.upper() == DrtTokens.DRS: + self.assertNextToken(DrtTokens.OPEN) + return self.handle_DRS(tok, context) + + elif self.isvariable(tok): + if self.inRange(0) and self.token(0) == DrtTokens.COLON: + return self.handle_prop(tok, context) + else: + return self.handle_variable(tok, context) + + def make_NegatedExpression(self, expression): + return DrtNegatedExpression(expression) + + def handle_DRS(self, tok, context): + # a DRS + refs = self.handle_refs() + if ( + self.inRange(0) and self.token(0) == DrtTokens.COMMA + ): # if there is a comma (it's optional) + self.token() # swallow the comma + conds = self.handle_conds(context) + self.assertNextToken(DrtTokens.CLOSE) + return DRS(refs, conds, None) + + def handle_refs(self): + self.assertNextToken(DrtTokens.OPEN_BRACKET) + refs = [] + while self.inRange(0) and self.token(0) != DrtTokens.CLOSE_BRACKET: + # Support expressions like: DRS([x y],C) == DRS([x,y],C) + if refs and self.token(0) == DrtTokens.COMMA: + self.token() # swallow the comma + refs.append(self.get_next_token_variable("quantified")) + self.assertNextToken(DrtTokens.CLOSE_BRACKET) + return refs + + def handle_conds(self, context): + self.assertNextToken(DrtTokens.OPEN_BRACKET) + conds = [] + while self.inRange(0) and self.token(0) != DrtTokens.CLOSE_BRACKET: + # Support expressions like: DRS([x y],C) == DRS([x, y],C) + if conds and self.token(0) == DrtTokens.COMMA: + self.token() # swallow the comma + conds.append(self.process_next_expression(context)) + self.assertNextToken(DrtTokens.CLOSE_BRACKET) + return conds + + def handle_prop(self, tok, context): + variable = self.make_VariableExpression(tok) + self.assertNextToken(":") + drs = self.process_next_expression(DrtTokens.COLON) + return DrtProposition(variable, drs) + + def make_EqualityExpression(self, first, second): + """This method serves as a hook for other logic parsers that + have different equality expression classes""" + return DrtEqualityExpression(first, second) + + def get_BooleanExpression_factory(self, tok): + """This method serves as a hook for other logic parsers that + have different boolean operators""" + if tok == DrtTokens.DRS_CONC: + return lambda first, second: DrtConcatenation(first, second, None) + elif tok in DrtTokens.OR_LIST: + return DrtOrExpression + elif tok in DrtTokens.IMP_LIST: + + def make_imp_expression(first, second): + if isinstance(first, DRS): + return DRS(first.refs, first.conds, second) + if isinstance(first, DrtConcatenation): + return DrtConcatenation(first.first, first.second, second) + raise Exception("Antecedent of implication must be a DRS") + + return make_imp_expression + else: + return None + + def make_BooleanExpression(self, factory, first, second): + return factory(first, second) + + def make_ApplicationExpression(self, function, argument): + return DrtApplicationExpression(function, argument) + + def make_VariableExpression(self, name): + return DrtVariableExpression(Variable(name)) + + def make_LambdaExpression(self, variables, term): + return DrtLambdaExpression(variables, term) + + +class DrtExpression: + """ + This is the base abstract DRT Expression from which every DRT + Expression extends. + """ + + _drt_parser = DrtParser() + + @classmethod + def fromstring(cls, s): + return cls._drt_parser.parse(s) + + def applyto(self, other): + return DrtApplicationExpression(self, other) + + def __neg__(self): + return DrtNegatedExpression(self) + + def __and__(self, other): + return NotImplemented + + def __or__(self, other): + assert isinstance(other, DrtExpression) + return DrtOrExpression(self, other) + + def __gt__(self, other): + assert isinstance(other, DrtExpression) + if isinstance(self, DRS): + return DRS(self.refs, self.conds, other) + if isinstance(self, DrtConcatenation): + return DrtConcatenation(self.first, self.second, other) + raise Exception("Antecedent of implication must be a DRS") + + def equiv(self, other, prover=None): + """ + Check for logical equivalence. + Pass the expression (self <-> other) to the theorem prover. + If the prover says it is valid, then the self and other are equal. + + :param other: an ``DrtExpression`` to check equality against + :param prover: a ``nltk.inference.api.Prover`` + """ + assert isinstance(other, DrtExpression) + + f1 = self.simplify().fol() + f2 = other.simplify().fol() + return f1.equiv(f2, prover) + + @property + def type(self): + raise AttributeError( + "'%s' object has no attribute 'type'" % self.__class__.__name__ + ) + + def typecheck(self, signature=None): + raise NotImplementedError() + + def __add__(self, other): + return DrtConcatenation(self, other, None) + + def get_refs(self, recursive=False): + """ + Return the set of discourse referents in this DRS. + :param recursive: bool Also find discourse referents in subterms? + :return: list of ``Variable`` objects + """ + raise NotImplementedError() + + def is_pronoun_function(self): + """Is self of the form "PRO(x)"?""" + return ( + isinstance(self, DrtApplicationExpression) + and isinstance(self.function, DrtAbstractVariableExpression) + and self.function.variable.name == DrtTokens.PRONOUN + and isinstance(self.argument, DrtIndividualVariableExpression) + ) + + def make_EqualityExpression(self, first, second): + return DrtEqualityExpression(first, second) + + def make_VariableExpression(self, variable): + return DrtVariableExpression(variable) + + def resolve_anaphora(self): + return resolve_anaphora(self) + + def eliminate_equality(self): + return self.visit_structured(lambda e: e.eliminate_equality(), self.__class__) + + def pretty_format(self): + """ + Draw the DRS + :return: the pretty print string + """ + return "\n".join(self._pretty()) + + def pretty_print(self): + print(self.pretty_format()) + + def draw(self): + DrsDrawer(self).draw() + + +class DRS(DrtExpression, Expression): + """A Discourse Representation Structure.""" + + def __init__(self, refs, conds, consequent=None): + """ + :param refs: list of ``DrtIndividualVariableExpression`` for the + discourse referents + :param conds: list of ``Expression`` for the conditions + """ + self.refs = refs + self.conds = conds + self.consequent = consequent + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """Replace all instances of variable v with expression E in self, + where v is free in self.""" + if variable in self.refs: + # if a bound variable is the thing being replaced + if not replace_bound: + return self + else: + i = self.refs.index(variable) + if self.consequent: + consequent = self.consequent.replace( + variable, expression, True, alpha_convert + ) + else: + consequent = None + return DRS( + self.refs[:i] + [expression.variable] + self.refs[i + 1 :], + [ + cond.replace(variable, expression, True, alpha_convert) + for cond in self.conds + ], + consequent, + ) + else: + if alpha_convert: + # any bound variable that appears in the expression must + # be alpha converted to avoid a conflict + for ref in set(self.refs) & expression.free(): + newvar = unique_variable(ref) + newvarex = DrtVariableExpression(newvar) + i = self.refs.index(ref) + if self.consequent: + consequent = self.consequent.replace( + ref, newvarex, True, alpha_convert + ) + else: + consequent = None + self = DRS( + self.refs[:i] + [newvar] + self.refs[i + 1 :], + [ + cond.replace(ref, newvarex, True, alpha_convert) + for cond in self.conds + ], + consequent, + ) + + # replace in the conditions + if self.consequent: + consequent = self.consequent.replace( + variable, expression, replace_bound, alpha_convert + ) + else: + consequent = None + return DRS( + self.refs, + [ + cond.replace(variable, expression, replace_bound, alpha_convert) + for cond in self.conds + ], + consequent, + ) + + def free(self): + """:see: Expression.free()""" + conds_free = reduce(operator.or_, [c.free() for c in self.conds], set()) + if self.consequent: + conds_free.update(self.consequent.free()) + return conds_free - set(self.refs) + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + if recursive: + conds_refs = self.refs + list( + chain.from_iterable(c.get_refs(True) for c in self.conds) + ) + if self.consequent: + conds_refs.extend(self.consequent.get_refs(True)) + return conds_refs + else: + return self.refs + + def visit(self, function, combinator): + """:see: Expression.visit()""" + parts = list(map(function, self.conds)) + if self.consequent: + parts.append(function(self.consequent)) + return combinator(parts) + + def visit_structured(self, function, combinator): + """:see: Expression.visit_structured()""" + consequent = function(self.consequent) if self.consequent else None + return combinator(self.refs, list(map(function, self.conds)), consequent) + + def eliminate_equality(self): + drs = self + i = 0 + while i < len(drs.conds): + cond = drs.conds[i] + if ( + isinstance(cond, EqualityExpression) + and isinstance(cond.first, AbstractVariableExpression) + and isinstance(cond.second, AbstractVariableExpression) + ): + drs = DRS( + list(set(drs.refs) - {cond.second.variable}), + drs.conds[:i] + drs.conds[i + 1 :], + drs.consequent, + ) + if cond.second.variable != cond.first.variable: + drs = drs.replace(cond.second.variable, cond.first, False, False) + i = 0 + i -= 1 + i += 1 + + conds = [] + for cond in drs.conds: + new_cond = cond.eliminate_equality() + new_cond_simp = new_cond.simplify() + if ( + not isinstance(new_cond_simp, DRS) + or new_cond_simp.refs + or new_cond_simp.conds + or new_cond_simp.consequent + ): + conds.append(new_cond) + + consequent = drs.consequent.eliminate_equality() if drs.consequent else None + return DRS(drs.refs, conds, consequent) + + def fol(self): + if self.consequent: + accum = None + if self.conds: + accum = reduce(AndExpression, [c.fol() for c in self.conds]) + + if accum: + accum = ImpExpression(accum, self.consequent.fol()) + else: + accum = self.consequent.fol() + + for ref in self.refs[::-1]: + accum = AllExpression(ref, accum) + + return accum + + else: + if not self.conds: + raise Exception("Cannot convert DRS with no conditions to FOL.") + accum = reduce(AndExpression, [c.fol() for c in self.conds]) + for ref in map(Variable, self._order_ref_strings(self.refs)[::-1]): + accum = ExistsExpression(ref, accum) + return accum + + def _pretty(self): + refs_line = " ".join(self._order_ref_strings(self.refs)) + + cond_lines = [ + cond + for cond_line in [ + filter(lambda s: s.strip(), cond._pretty()) for cond in self.conds + ] + for cond in cond_line + ] + length = max([len(refs_line)] + list(map(len, cond_lines))) + drs = ( + [ + " _" + "_" * length + "_ ", + "| " + refs_line.ljust(length) + " |", + "|-" + "-" * length + "-|", + ] + + ["| " + line.ljust(length) + " |" for line in cond_lines] + + ["|_" + "_" * length + "_|"] + ) + if self.consequent: + return DrtBinaryExpression._assemble_pretty( + drs, DrtTokens.IMP, self.consequent._pretty() + ) + return drs + + def _order_ref_strings(self, refs): + strings = ["%s" % ref for ref in refs] + ind_vars = [] + func_vars = [] + event_vars = [] + other_vars = [] + for s in strings: + if is_indvar(s): + ind_vars.append(s) + elif is_funcvar(s): + func_vars.append(s) + elif is_eventvar(s): + event_vars.append(s) + else: + other_vars.append(s) + return ( + sorted(other_vars) + + sorted(event_vars, key=lambda v: int([v[2:], -1][len(v[2:]) == 0])) + + sorted(func_vars, key=lambda v: (v[0], int([v[1:], -1][len(v[1:]) == 0]))) + + sorted(ind_vars, key=lambda v: (v[0], int([v[1:], -1][len(v[1:]) == 0]))) + ) + + def __eq__(self, other): + r"""Defines equality modulo alphabetic variance. + If we are comparing \x.M and \y.N, then check equality of M and N[x/y].""" + if isinstance(other, DRS): + if len(self.refs) == len(other.refs): + converted_other = other + for (r1, r2) in zip(self.refs, converted_other.refs): + varex = self.make_VariableExpression(r1) + converted_other = converted_other.replace(r2, varex, True) + if self.consequent == converted_other.consequent and len( + self.conds + ) == len(converted_other.conds): + for c1, c2 in zip(self.conds, converted_other.conds): + if not (c1 == c2): + return False + return True + return False + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + def __str__(self): + drs = "([{}],[{}])".format( + ",".join(self._order_ref_strings(self.refs)), + ", ".join("%s" % cond for cond in self.conds), + ) # map(str, self.conds))) + if self.consequent: + return ( + DrtTokens.OPEN + + drs + + " " + + DrtTokens.IMP + + " " + + "%s" % self.consequent + + DrtTokens.CLOSE + ) + return drs + + +def DrtVariableExpression(variable): + """ + This is a factory method that instantiates and returns a subtype of + ``DrtAbstractVariableExpression`` appropriate for the given variable. + """ + if is_indvar(variable.name): + return DrtIndividualVariableExpression(variable) + elif is_funcvar(variable.name): + return DrtFunctionVariableExpression(variable) + elif is_eventvar(variable.name): + return DrtEventVariableExpression(variable) + else: + return DrtConstantExpression(variable) + + +class DrtAbstractVariableExpression(DrtExpression, AbstractVariableExpression): + def fol(self): + return self + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + return [] + + def _pretty(self): + s = "%s" % self + blank = " " * len(s) + return [blank, blank, s, blank] + + def eliminate_equality(self): + return self + + +class DrtIndividualVariableExpression( + DrtAbstractVariableExpression, IndividualVariableExpression +): + pass + + +class DrtFunctionVariableExpression( + DrtAbstractVariableExpression, FunctionVariableExpression +): + pass + + +class DrtEventVariableExpression( + DrtIndividualVariableExpression, EventVariableExpression +): + pass + + +class DrtConstantExpression(DrtAbstractVariableExpression, ConstantExpression): + pass + + +class DrtProposition(DrtExpression, Expression): + def __init__(self, variable, drs): + self.variable = variable + self.drs = drs + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + if self.variable == variable: + assert isinstance( + expression, DrtAbstractVariableExpression + ), "Can only replace a proposition label with a variable" + return DrtProposition( + expression.variable, + self.drs.replace(variable, expression, replace_bound, alpha_convert), + ) + else: + return DrtProposition( + self.variable, + self.drs.replace(variable, expression, replace_bound, alpha_convert), + ) + + def eliminate_equality(self): + return DrtProposition(self.variable, self.drs.eliminate_equality()) + + def get_refs(self, recursive=False): + return self.drs.get_refs(True) if recursive else [] + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.variable == other.variable + and self.drs == other.drs + ) + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + def fol(self): + return self.drs.fol() + + def _pretty(self): + drs_s = self.drs._pretty() + blank = " " * len("%s" % self.variable) + return ( + [blank + " " + line for line in drs_s[:1]] + + ["%s" % self.variable + ":" + line for line in drs_s[1:2]] + + [blank + " " + line for line in drs_s[2:]] + ) + + def visit(self, function, combinator): + """:see: Expression.visit()""" + return combinator([function(self.drs)]) + + def visit_structured(self, function, combinator): + """:see: Expression.visit_structured()""" + return combinator(self.variable, function(self.drs)) + + def __str__(self): + return f"prop({self.variable}, {self.drs})" + + +class DrtNegatedExpression(DrtExpression, NegatedExpression): + def fol(self): + return NegatedExpression(self.term.fol()) + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + return self.term.get_refs(recursive) + + def _pretty(self): + term_lines = self.term._pretty() + return ( + [" " + line for line in term_lines[:2]] + + ["__ " + line for line in term_lines[2:3]] + + [" | " + line for line in term_lines[3:4]] + + [" " + line for line in term_lines[4:]] + ) + + +class DrtLambdaExpression(DrtExpression, LambdaExpression): + def alpha_convert(self, newvar): + """Rename all occurrences of the variable introduced by this variable + binder in the expression to ``newvar``. + :param newvar: ``Variable``, for the new variable + """ + return self.__class__( + newvar, + self.term.replace(self.variable, DrtVariableExpression(newvar), True), + ) + + def fol(self): + return LambdaExpression(self.variable, self.term.fol()) + + def _pretty(self): + variables = [self.variable] + term = self.term + while term.__class__ == self.__class__: + variables.append(term.variable) + term = term.term + var_string = " ".join("%s" % v for v in variables) + DrtTokens.DOT + term_lines = term._pretty() + blank = " " * len(var_string) + return ( + [" " + blank + line for line in term_lines[:1]] + + [r" \ " + blank + line for line in term_lines[1:2]] + + [r" /\ " + var_string + line for line in term_lines[2:3]] + + [" " + blank + line for line in term_lines[3:]] + ) + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + return ( + [self.variable] + self.term.get_refs(True) if recursive else [self.variable] + ) + + +class DrtBinaryExpression(DrtExpression, BinaryExpression): + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + return ( + self.first.get_refs(True) + self.second.get_refs(True) if recursive else [] + ) + + def _pretty(self): + return DrtBinaryExpression._assemble_pretty( + self._pretty_subex(self.first), + self.getOp(), + self._pretty_subex(self.second), + ) + + @staticmethod + def _assemble_pretty(first_lines, op, second_lines): + max_lines = max(len(first_lines), len(second_lines)) + first_lines = _pad_vertically(first_lines, max_lines) + second_lines = _pad_vertically(second_lines, max_lines) + blank = " " * len(op) + first_second_lines = list(zip(first_lines, second_lines)) + return ( + [ + " " + first_line + " " + blank + " " + second_line + " " + for first_line, second_line in first_second_lines[:2] + ] + + [ + "(" + first_line + " " + op + " " + second_line + ")" + for first_line, second_line in first_second_lines[2:3] + ] + + [ + " " + first_line + " " + blank + " " + second_line + " " + for first_line, second_line in first_second_lines[3:] + ] + ) + + def _pretty_subex(self, subex): + return subex._pretty() + + +class DrtBooleanExpression(DrtBinaryExpression, BooleanExpression): + pass + + +class DrtOrExpression(DrtBooleanExpression, OrExpression): + def fol(self): + return OrExpression(self.first.fol(), self.second.fol()) + + def _pretty_subex(self, subex): + if isinstance(subex, DrtOrExpression): + return [line[1:-1] for line in subex._pretty()] + return DrtBooleanExpression._pretty_subex(self, subex) + + +class DrtEqualityExpression(DrtBinaryExpression, EqualityExpression): + def fol(self): + return EqualityExpression(self.first.fol(), self.second.fol()) + + +class DrtConcatenation(DrtBooleanExpression): + """DRS of the form '(DRS + DRS)'""" + + def __init__(self, first, second, consequent=None): + DrtBooleanExpression.__init__(self, first, second) + self.consequent = consequent + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """Replace all instances of variable v with expression E in self, + where v is free in self.""" + first = self.first + second = self.second + consequent = self.consequent + + # If variable is bound + if variable in self.get_refs(): + if replace_bound: + first = first.replace( + variable, expression, replace_bound, alpha_convert + ) + second = second.replace( + variable, expression, replace_bound, alpha_convert + ) + if consequent: + consequent = consequent.replace( + variable, expression, replace_bound, alpha_convert + ) + else: + if alpha_convert: + # alpha convert every ref that is free in 'expression' + for ref in set(self.get_refs(True)) & expression.free(): + v = DrtVariableExpression(unique_variable(ref)) + first = first.replace(ref, v, True, alpha_convert) + second = second.replace(ref, v, True, alpha_convert) + if consequent: + consequent = consequent.replace(ref, v, True, alpha_convert) + + first = first.replace(variable, expression, replace_bound, alpha_convert) + second = second.replace(variable, expression, replace_bound, alpha_convert) + if consequent: + consequent = consequent.replace( + variable, expression, replace_bound, alpha_convert + ) + + return self.__class__(first, second, consequent) + + def eliminate_equality(self): + # TODO: at some point. for now, simplify. + drs = self.simplify() + assert not isinstance(drs, DrtConcatenation) + return drs.eliminate_equality() + + def simplify(self): + first = self.first.simplify() + second = self.second.simplify() + consequent = self.consequent.simplify() if self.consequent else None + + if isinstance(first, DRS) and isinstance(second, DRS): + # For any ref that is in both 'first' and 'second' + for ref in set(first.get_refs(True)) & set(second.get_refs(True)): + # alpha convert the ref in 'second' to prevent collision + newvar = DrtVariableExpression(unique_variable(ref)) + second = second.replace(ref, newvar, True) + + return DRS(first.refs + second.refs, first.conds + second.conds, consequent) + else: + return self.__class__(first, second, consequent) + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + refs = self.first.get_refs(recursive) + self.second.get_refs(recursive) + if self.consequent and recursive: + refs.extend(self.consequent.get_refs(True)) + return refs + + def getOp(self): + return DrtTokens.DRS_CONC + + def __eq__(self, other): + r"""Defines equality modulo alphabetic variance. + If we are comparing \x.M and \y.N, then check equality of M and N[x/y].""" + if isinstance(other, DrtConcatenation): + self_refs = self.get_refs() + other_refs = other.get_refs() + if len(self_refs) == len(other_refs): + converted_other = other + for (r1, r2) in zip(self_refs, other_refs): + varex = self.make_VariableExpression(r1) + converted_other = converted_other.replace(r2, varex, True) + return ( + self.first == converted_other.first + and self.second == converted_other.second + and self.consequent == converted_other.consequent + ) + return False + + def __ne__(self, other): + return not self == other + + __hash__ = DrtBooleanExpression.__hash__ + + def fol(self): + e = AndExpression(self.first.fol(), self.second.fol()) + if self.consequent: + e = ImpExpression(e, self.consequent.fol()) + return e + + def _pretty(self): + drs = DrtBinaryExpression._assemble_pretty( + self._pretty_subex(self.first), + self.getOp(), + self._pretty_subex(self.second), + ) + if self.consequent: + drs = DrtBinaryExpression._assemble_pretty( + drs, DrtTokens.IMP, self.consequent._pretty() + ) + return drs + + def _pretty_subex(self, subex): + if isinstance(subex, DrtConcatenation): + return [line[1:-1] for line in subex._pretty()] + return DrtBooleanExpression._pretty_subex(self, subex) + + def visit(self, function, combinator): + """:see: Expression.visit()""" + if self.consequent: + return combinator( + [function(self.first), function(self.second), function(self.consequent)] + ) + else: + return combinator([function(self.first), function(self.second)]) + + def __str__(self): + first = self._str_subex(self.first) + second = self._str_subex(self.second) + drs = Tokens.OPEN + first + " " + self.getOp() + " " + second + Tokens.CLOSE + if self.consequent: + return ( + DrtTokens.OPEN + + drs + + " " + + DrtTokens.IMP + + " " + + "%s" % self.consequent + + DrtTokens.CLOSE + ) + return drs + + def _str_subex(self, subex): + s = "%s" % subex + if isinstance(subex, DrtConcatenation) and subex.consequent is None: + return s[1:-1] + return s + + +class DrtApplicationExpression(DrtExpression, ApplicationExpression): + def fol(self): + return ApplicationExpression(self.function.fol(), self.argument.fol()) + + def get_refs(self, recursive=False): + """:see: AbstractExpression.get_refs()""" + return ( + self.function.get_refs(True) + self.argument.get_refs(True) + if recursive + else [] + ) + + def _pretty(self): + function, args = self.uncurry() + function_lines = function._pretty() + args_lines = [arg._pretty() for arg in args] + max_lines = max(map(len, [function_lines] + args_lines)) + function_lines = _pad_vertically(function_lines, max_lines) + args_lines = [_pad_vertically(arg_lines, max_lines) for arg_lines in args_lines] + func_args_lines = list(zip(function_lines, list(zip(*args_lines)))) + return ( + [ + func_line + " " + " ".join(args_line) + " " + for func_line, args_line in func_args_lines[:2] + ] + + [ + func_line + "(" + ",".join(args_line) + ")" + for func_line, args_line in func_args_lines[2:3] + ] + + [ + func_line + " " + " ".join(args_line) + " " + for func_line, args_line in func_args_lines[3:] + ] + ) + + +def _pad_vertically(lines, max_lines): + pad_line = [" " * len(lines[0])] + return lines + pad_line * (max_lines - len(lines)) + + +class PossibleAntecedents(list, DrtExpression, Expression): + def free(self): + """Set of free variables.""" + return set(self) + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """Replace all instances of variable v with expression E in self, + where v is free in self.""" + result = PossibleAntecedents() + for item in self: + if item == variable: + self.append(expression) + else: + self.append(item) + return result + + def _pretty(self): + s = "%s" % self + blank = " " * len(s) + return [blank, blank, s] + + def __str__(self): + return "[" + ",".join("%s" % it for it in self) + "]" + + +class AnaphoraResolutionException(Exception): + pass + + +def resolve_anaphora(expression, trail=[]): + if isinstance(expression, ApplicationExpression): + if expression.is_pronoun_function(): + possible_antecedents = PossibleAntecedents() + for ancestor in trail: + for ref in ancestor.get_refs(): + refex = expression.make_VariableExpression(ref) + + # ========================================================== + # Don't allow resolution to itself or other types + # ========================================================== + if refex.__class__ == expression.argument.__class__ and not ( + refex == expression.argument + ): + possible_antecedents.append(refex) + + if len(possible_antecedents) == 1: + resolution = possible_antecedents[0] + else: + resolution = possible_antecedents + return expression.make_EqualityExpression(expression.argument, resolution) + else: + r_function = resolve_anaphora(expression.function, trail + [expression]) + r_argument = resolve_anaphora(expression.argument, trail + [expression]) + return expression.__class__(r_function, r_argument) + + elif isinstance(expression, DRS): + r_conds = [] + for cond in expression.conds: + r_cond = resolve_anaphora(cond, trail + [expression]) + + # if the condition is of the form '(x = [])' then raise exception + if isinstance(r_cond, EqualityExpression): + if isinstance(r_cond.first, PossibleAntecedents): + # Reverse the order so that the variable is on the left + temp = r_cond.first + r_cond.first = r_cond.second + r_cond.second = temp + if isinstance(r_cond.second, PossibleAntecedents): + if not r_cond.second: + raise AnaphoraResolutionException( + "Variable '%s' does not " + "resolve to anything." % r_cond.first + ) + + r_conds.append(r_cond) + if expression.consequent: + consequent = resolve_anaphora(expression.consequent, trail + [expression]) + else: + consequent = None + return expression.__class__(expression.refs, r_conds, consequent) + + elif isinstance(expression, AbstractVariableExpression): + return expression + + elif isinstance(expression, NegatedExpression): + return expression.__class__( + resolve_anaphora(expression.term, trail + [expression]) + ) + + elif isinstance(expression, DrtConcatenation): + if expression.consequent: + consequent = resolve_anaphora(expression.consequent, trail + [expression]) + else: + consequent = None + return expression.__class__( + resolve_anaphora(expression.first, trail + [expression]), + resolve_anaphora(expression.second, trail + [expression]), + consequent, + ) + + elif isinstance(expression, BinaryExpression): + return expression.__class__( + resolve_anaphora(expression.first, trail + [expression]), + resolve_anaphora(expression.second, trail + [expression]), + ) + + elif isinstance(expression, LambdaExpression): + return expression.__class__( + expression.variable, resolve_anaphora(expression.term, trail + [expression]) + ) + + +class DrsDrawer: + BUFFER = 3 # Space between elements + TOPSPACE = 10 # Space above whole DRS + OUTERSPACE = 6 # Space to the left, right, and bottom of the while DRS + + def __init__(self, drs, size_canvas=True, canvas=None): + """ + :param drs: ``DrtExpression``, The DRS to be drawn + :param size_canvas: bool, True if the canvas size should be the exact size of the DRS + :param canvas: ``Canvas`` The canvas on which to draw the DRS. If none is given, create a new canvas. + """ + master = None + if not canvas: + master = Tk() + master.title("DRT") + + font = Font(family="helvetica", size=12) + + if size_canvas: + canvas = Canvas(master, width=0, height=0) + canvas.font = font + self.canvas = canvas + (right, bottom) = self._visit(drs, self.OUTERSPACE, self.TOPSPACE) + + width = max(right + self.OUTERSPACE, 100) + height = bottom + self.OUTERSPACE + canvas = Canvas(master, width=width, height=height) # , bg='white') + else: + canvas = Canvas(master, width=300, height=300) + + canvas.pack() + canvas.font = font + + self.canvas = canvas + self.drs = drs + self.master = master + + def _get_text_height(self): + """Get the height of a line of text""" + return self.canvas.font.metrics("linespace") + + def draw(self, x=OUTERSPACE, y=TOPSPACE): + """Draw the DRS""" + self._handle(self.drs, self._draw_command, x, y) + + if self.master and not in_idle(): + self.master.mainloop() + else: + return self._visit(self.drs, x, y) + + def _visit(self, expression, x, y): + """ + Return the bottom-rightmost point without actually drawing the item + + :param expression: the item to visit + :param x: the top of the current drawing area + :param y: the left side of the current drawing area + :return: the bottom-rightmost point + """ + return self._handle(expression, self._visit_command, x, y) + + def _draw_command(self, item, x, y): + """ + Draw the given item at the given location + + :param item: the item to draw + :param x: the top of the current drawing area + :param y: the left side of the current drawing area + :return: the bottom-rightmost point + """ + if isinstance(item, str): + self.canvas.create_text(x, y, anchor="nw", font=self.canvas.font, text=item) + elif isinstance(item, tuple): + # item is the lower-right of a box + (right, bottom) = item + self.canvas.create_rectangle(x, y, right, bottom) + horiz_line_y = ( + y + self._get_text_height() + (self.BUFFER * 2) + ) # the line separating refs from conds + self.canvas.create_line(x, horiz_line_y, right, horiz_line_y) + + return self._visit_command(item, x, y) + + def _visit_command(self, item, x, y): + """ + Return the bottom-rightmost point without actually drawing the item + + :param item: the item to visit + :param x: the top of the current drawing area + :param y: the left side of the current drawing area + :return: the bottom-rightmost point + """ + if isinstance(item, str): + return (x + self.canvas.font.measure(item), y + self._get_text_height()) + elif isinstance(item, tuple): + return item + + def _handle(self, expression, command, x=0, y=0): + """ + :param expression: the expression to handle + :param command: the function to apply, either _draw_command or _visit_command + :param x: the top of the current drawing area + :param y: the left side of the current drawing area + :return: the bottom-rightmost point + """ + if command == self._visit_command: + # if we don't need to draw the item, then we can use the cached values + try: + # attempt to retrieve cached values + right = expression._drawing_width + x + bottom = expression._drawing_height + y + return (right, bottom) + except AttributeError: + # the values have not been cached yet, so compute them + pass + + if isinstance(expression, DrtAbstractVariableExpression): + factory = self._handle_VariableExpression + elif isinstance(expression, DRS): + factory = self._handle_DRS + elif isinstance(expression, DrtNegatedExpression): + factory = self._handle_NegatedExpression + elif isinstance(expression, DrtLambdaExpression): + factory = self._handle_LambdaExpression + elif isinstance(expression, BinaryExpression): + factory = self._handle_BinaryExpression + elif isinstance(expression, DrtApplicationExpression): + factory = self._handle_ApplicationExpression + elif isinstance(expression, PossibleAntecedents): + factory = self._handle_VariableExpression + elif isinstance(expression, DrtProposition): + factory = self._handle_DrtProposition + else: + raise Exception(expression.__class__.__name__) + + (right, bottom) = factory(expression, command, x, y) + + # cache the values + expression._drawing_width = right - x + expression._drawing_height = bottom - y + + return (right, bottom) + + def _handle_VariableExpression(self, expression, command, x, y): + return command("%s" % expression, x, y) + + def _handle_NegatedExpression(self, expression, command, x, y): + # Find the width of the negation symbol + right = self._visit_command(DrtTokens.NOT, x, y)[0] + + # Handle term + (right, bottom) = self._handle(expression.term, command, right, y) + + # Handle variables now that we know the y-coordinate + command( + DrtTokens.NOT, + x, + self._get_centered_top(y, bottom - y, self._get_text_height()), + ) + + return (right, bottom) + + def _handle_DRS(self, expression, command, x, y): + left = x + self.BUFFER # indent the left side + bottom = y + self.BUFFER # indent the top + + # Handle Discourse Referents + if expression.refs: + refs = " ".join("%s" % r for r in expression.refs) + else: + refs = " " + (max_right, bottom) = command(refs, left, bottom) + bottom += self.BUFFER * 2 + + # Handle Conditions + if expression.conds: + for cond in expression.conds: + (right, bottom) = self._handle(cond, command, left, bottom) + max_right = max(max_right, right) + bottom += self.BUFFER + else: + bottom += self._get_text_height() + self.BUFFER + + # Handle Box + max_right += self.BUFFER + return command((max_right, bottom), x, y) + + def _handle_ApplicationExpression(self, expression, command, x, y): + function, args = expression.uncurry() + if not isinstance(function, DrtAbstractVariableExpression): + # It's not a predicate expression ("P(x,y)"), so leave arguments curried + function = expression.function + args = [expression.argument] + + # Get the max bottom of any element on the line + function_bottom = self._visit(function, x, y)[1] + max_bottom = max( + [function_bottom] + [self._visit(arg, x, y)[1] for arg in args] + ) + + line_height = max_bottom - y + + # Handle 'function' + function_drawing_top = self._get_centered_top( + y, line_height, function._drawing_height + ) + right = self._handle(function, command, x, function_drawing_top)[0] + + # Handle open paren + centred_string_top = self._get_centered_top( + y, line_height, self._get_text_height() + ) + right = command(DrtTokens.OPEN, right, centred_string_top)[0] + + # Handle each arg + for (i, arg) in enumerate(args): + arg_drawing_top = self._get_centered_top( + y, line_height, arg._drawing_height + ) + right = self._handle(arg, command, right, arg_drawing_top)[0] + + if i + 1 < len(args): + # since it's not the last arg, add a comma + right = command(DrtTokens.COMMA + " ", right, centred_string_top)[0] + + # Handle close paren + right = command(DrtTokens.CLOSE, right, centred_string_top)[0] + + return (right, max_bottom) + + def _handle_LambdaExpression(self, expression, command, x, y): + # Find the width of the lambda symbol and abstracted variables + variables = DrtTokens.LAMBDA + "%s" % expression.variable + DrtTokens.DOT + right = self._visit_command(variables, x, y)[0] + + # Handle term + (right, bottom) = self._handle(expression.term, command, right, y) + + # Handle variables now that we know the y-coordinate + command( + variables, x, self._get_centered_top(y, bottom - y, self._get_text_height()) + ) + + return (right, bottom) + + def _handle_BinaryExpression(self, expression, command, x, y): + # Get the full height of the line, based on the operands + first_height = self._visit(expression.first, 0, 0)[1] + second_height = self._visit(expression.second, 0, 0)[1] + line_height = max(first_height, second_height) + + # Handle open paren + centred_string_top = self._get_centered_top( + y, line_height, self._get_text_height() + ) + right = command(DrtTokens.OPEN, x, centred_string_top)[0] + + # Handle the first operand + first_height = expression.first._drawing_height + (right, first_bottom) = self._handle( + expression.first, + command, + right, + self._get_centered_top(y, line_height, first_height), + ) + + # Handle the operator + right = command(" %s " % expression.getOp(), right, centred_string_top)[0] + + # Handle the second operand + second_height = expression.second._drawing_height + (right, second_bottom) = self._handle( + expression.second, + command, + right, + self._get_centered_top(y, line_height, second_height), + ) + + # Handle close paren + right = command(DrtTokens.CLOSE, right, centred_string_top)[0] + + return (right, max(first_bottom, second_bottom)) + + def _handle_DrtProposition(self, expression, command, x, y): + # Find the width of the negation symbol + right = command(expression.variable, x, y)[0] + + # Handle term + (right, bottom) = self._handle(expression.term, command, right, y) + + return (right, bottom) + + def _get_centered_top(self, top, full_height, item_height): + """Get the y-coordinate of the point that a figure should start at if + its height is 'item_height' and it needs to be centered in an area that + starts at 'top' and is 'full_height' tall.""" + return top + (full_height - item_height) / 2 + + +def demo(): + print("=" * 20 + "TEST PARSE" + "=" * 20) + dexpr = DrtExpression.fromstring + print(dexpr(r"([x,y],[sees(x,y)])")) + print(dexpr(r"([x],[man(x), walks(x)])")) + print(dexpr(r"\x.\y.([],[sees(x,y)])")) + print(dexpr(r"\x.([],[walks(x)])(john)")) + print(dexpr(r"(([x],[walks(x)]) + ([y],[runs(y)]))")) + print(dexpr(r"(([],[walks(x)]) -> ([],[runs(x)]))")) + print(dexpr(r"([x],[PRO(x), sees(John,x)])")) + print(dexpr(r"([x],[man(x), -([],[walks(x)])])")) + print(dexpr(r"([],[(([x],[man(x)]) -> ([],[walks(x)]))])")) + + print("=" * 20 + "Test fol()" + "=" * 20) + print(dexpr(r"([x,y],[sees(x,y)])").fol()) + + print("=" * 20 + "Test alpha conversion and lambda expression equality" + "=" * 20) + e1 = dexpr(r"\x.([],[P(x)])") + print(e1) + e2 = e1.alpha_convert(Variable("z")) + print(e2) + print(e1 == e2) + + print("=" * 20 + "Test resolve_anaphora()" + "=" * 20) + print(resolve_anaphora(dexpr(r"([x,y,z],[dog(x), cat(y), walks(z), PRO(z)])"))) + print( + resolve_anaphora(dexpr(r"([],[(([x],[dog(x)]) -> ([y],[walks(y), PRO(y)]))])")) + ) + print(resolve_anaphora(dexpr(r"(([x,y],[]) + ([],[PRO(x)]))"))) + + print("=" * 20 + "Test pretty_print()" + "=" * 20) + dexpr(r"([],[])").pretty_print() + dexpr( + r"([],[([x],[big(x), dog(x)]) -> ([],[bark(x)]) -([x],[walk(x)])])" + ).pretty_print() + dexpr(r"([x,y],[x=y]) + ([z],[dog(z), walk(z)])").pretty_print() + dexpr(r"([],[([x],[]) | ([y],[]) | ([z],[dog(z), walk(z)])])").pretty_print() + dexpr(r"\P.\Q.(([x],[]) + P(x) + Q(x))(\x.([],[dog(x)]))").pretty_print() + + +def test_draw(): + try: + from tkinter import Tk + except ImportError as e: + raise ValueError("tkinter is required, but it's not available.") + + expressions = [ + r"x", + r"([],[])", + r"([x],[])", + r"([x],[man(x)])", + r"([x,y],[sees(x,y)])", + r"([x],[man(x), walks(x)])", + r"\x.([],[man(x), walks(x)])", + r"\x y.([],[sees(x,y)])", + r"([],[(([],[walks(x)]) + ([],[runs(x)]))])", + r"([x],[man(x), -([],[walks(x)])])", + r"([],[(([x],[man(x)]) -> ([],[walks(x)]))])", + ] + + for e in expressions: + d = DrtExpression.fromstring(e) + d.draw() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt_glue_demo.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt_glue_demo.py new file mode 100644 index 0000000..04390e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/drt_glue_demo.py @@ -0,0 +1,553 @@ +# Natural Language Toolkit: GUI Demo for Glue Semantics with Discourse +# Representation Theory (DRT) as meaning language +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +try: + from tkinter import Button, Frame, IntVar, Label, Listbox, Menu, Scrollbar, Tk + from tkinter.font import Font + + from nltk.draw.util import CanvasFrame, ShowText + +except ImportError: + """Ignore ImportError because tkinter might not be available.""" + +from nltk.parse import MaltParser +from nltk.sem.drt import DrsDrawer, DrtVariableExpression +from nltk.sem.glue import DrtGlue +from nltk.sem.logic import Variable +from nltk.tag import RegexpTagger +from nltk.util import in_idle + + +class DrtGlueDemo: + def __init__(self, examples): + # Set up the main window. + self._top = Tk() + self._top.title("DRT Glue Demo") + + # Set up key bindings. + self._init_bindings() + + # Initialize the fonts.self._error = None + self._init_fonts(self._top) + + self._examples = examples + self._readingCache = [None for example in examples] + + # The user can hide the grammar. + self._show_grammar = IntVar(self._top) + self._show_grammar.set(1) + + # Set the data to None + self._curExample = -1 + self._readings = [] + self._drs = None + self._drsWidget = None + self._error = None + + self._init_glue() + + # Create the basic frames. + self._init_menubar(self._top) + self._init_buttons(self._top) + self._init_exampleListbox(self._top) + self._init_readingListbox(self._top) + self._init_canvas(self._top) + + # Resize callback + self._canvas.bind("", self._configure) + + ######################################### + ## Initialization Helpers + ######################################### + + def _init_glue(self): + tagger = RegexpTagger( + [ + ("^(David|Mary|John)$", "NNP"), + ( + "^(walks|sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", + "VB", + ), + ("^(go|order|vanish|find|approach)$", "VB"), + ("^(a)$", "ex_quant"), + ("^(every)$", "univ_quant"), + ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), + ("^(big|gray|former)$", "JJ"), + ("^(him|himself)$", "PRP"), + ] + ) + + depparser = MaltParser(tagger=tagger) + self._glue = DrtGlue(depparser=depparser, remove_duplicates=False) + + def _init_fonts(self, root): + # See: + self._sysfont = Font(font=Button()["font"]) + root.option_add("*Font", self._sysfont) + + # TWhat's our font size (default=same as sysfont) + self._size = IntVar(root) + self._size.set(self._sysfont.cget("size")) + + self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) + self._font = Font(family="helvetica", size=self._size.get()) + if self._size.get() < 0: + big = self._size.get() - 2 + else: + big = self._size.get() + 2 + self._bigfont = Font(family="helvetica", weight="bold", size=big) + + def _init_exampleListbox(self, parent): + self._exampleFrame = listframe = Frame(parent) + self._exampleFrame.pack(fill="both", side="left", padx=2) + self._exampleList_label = Label( + self._exampleFrame, font=self._boldfont, text="Examples" + ) + self._exampleList_label.pack() + self._exampleList = Listbox( + self._exampleFrame, + selectmode="single", + relief="groove", + background="white", + foreground="#909090", + font=self._font, + selectforeground="#004040", + selectbackground="#c0f0c0", + ) + + self._exampleList.pack(side="right", fill="both", expand=1) + + for example in self._examples: + self._exampleList.insert("end", (" %s" % example)) + self._exampleList.config(height=min(len(self._examples), 25), width=40) + + # Add a scrollbar if there are more than 25 examples. + if len(self._examples) > 25: + listscroll = Scrollbar(self._exampleFrame, orient="vertical") + self._exampleList.config(yscrollcommand=listscroll.set) + listscroll.config(command=self._exampleList.yview) + listscroll.pack(side="left", fill="y") + + # If they select a example, apply it. + self._exampleList.bind("<>", self._exampleList_select) + + def _init_readingListbox(self, parent): + self._readingFrame = listframe = Frame(parent) + self._readingFrame.pack(fill="both", side="left", padx=2) + self._readingList_label = Label( + self._readingFrame, font=self._boldfont, text="Readings" + ) + self._readingList_label.pack() + self._readingList = Listbox( + self._readingFrame, + selectmode="single", + relief="groove", + background="white", + foreground="#909090", + font=self._font, + selectforeground="#004040", + selectbackground="#c0f0c0", + ) + + self._readingList.pack(side="right", fill="both", expand=1) + + # Add a scrollbar if there are more than 25 examples. + listscroll = Scrollbar(self._readingFrame, orient="vertical") + self._readingList.config(yscrollcommand=listscroll.set) + listscroll.config(command=self._readingList.yview) + listscroll.pack(side="right", fill="y") + + self._populate_readingListbox() + + def _populate_readingListbox(self): + # Populate the listbox with integers + self._readingList.delete(0, "end") + for i in range(len(self._readings)): + self._readingList.insert("end", (" %s" % (i + 1))) + self._readingList.config(height=min(len(self._readings), 25), width=5) + + # If they select a example, apply it. + self._readingList.bind("<>", self._readingList_select) + + def _init_bindings(self): + # Key bindings are a good thing. + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("", self.destroy) + self._top.bind("n", self.next) + self._top.bind("", self.next) + self._top.bind("p", self.prev) + self._top.bind("", self.prev) + + def _init_buttons(self, parent): + # Set up the frames. + self._buttonframe = buttonframe = Frame(parent) + buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) + Button( + buttonframe, + text="Prev", + background="#90c0d0", + foreground="black", + command=self.prev, + ).pack(side="left") + Button( + buttonframe, + text="Next", + background="#90c0d0", + foreground="black", + command=self.next, + ).pack(side="left") + + def _configure(self, event): + self._autostep = 0 + (x1, y1, x2, y2) = self._cframe.scrollregion() + y2 = event.height - 6 + self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) + self._redraw() + + def _init_canvas(self, parent): + self._cframe = CanvasFrame( + parent, + background="white", + # width=525, height=250, + closeenough=10, + border=2, + relief="sunken", + ) + self._cframe.pack(expand=1, fill="both", side="top", pady=2) + canvas = self._canvas = self._cframe.canvas() + + # Initially, there's no tree or text + self._tree = None + self._textwidgets = [] + self._textline = None + + def _init_menubar(self, parent): + menubar = Menu(parent) + + filemenu = Menu(menubar, tearoff=0) + filemenu.add_command( + label="Exit", underline=1, command=self.destroy, accelerator="q" + ) + menubar.add_cascade(label="File", underline=0, menu=filemenu) + + actionmenu = Menu(menubar, tearoff=0) + actionmenu.add_command( + label="Next", underline=0, command=self.next, accelerator="n, Space" + ) + actionmenu.add_command( + label="Previous", underline=0, command=self.prev, accelerator="p, Backspace" + ) + menubar.add_cascade(label="Action", underline=0, menu=actionmenu) + + optionmenu = Menu(menubar, tearoff=0) + optionmenu.add_checkbutton( + label="Remove Duplicates", + underline=0, + variable=self._glue.remove_duplicates, + command=self._toggle_remove_duplicates, + accelerator="r", + ) + menubar.add_cascade(label="Options", underline=0, menu=optionmenu) + + viewmenu = Menu(menubar, tearoff=0) + viewmenu.add_radiobutton( + label="Tiny", + variable=self._size, + underline=0, + value=10, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Small", + variable=self._size, + underline=0, + value=12, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Medium", + variable=self._size, + underline=0, + value=14, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Large", + variable=self._size, + underline=0, + value=18, + command=self.resize, + ) + viewmenu.add_radiobutton( + label="Huge", + variable=self._size, + underline=0, + value=24, + command=self.resize, + ) + menubar.add_cascade(label="View", underline=0, menu=viewmenu) + + helpmenu = Menu(menubar, tearoff=0) + helpmenu.add_command(label="About", underline=0, command=self.about) + menubar.add_cascade(label="Help", underline=0, menu=helpmenu) + + parent.config(menu=menubar) + + ######################################### + ## Main draw procedure + ######################################### + + def _redraw(self): + canvas = self._canvas + + # Delete the old DRS, widgets, etc. + if self._drsWidget is not None: + self._drsWidget.clear() + + if self._drs: + self._drsWidget = DrsWidget(self._canvas, self._drs) + self._drsWidget.draw() + + if self._error: + self._drsWidget = DrsWidget(self._canvas, self._error) + self._drsWidget.draw() + + ######################################### + ## Button Callbacks + ######################################### + + def destroy(self, *e): + self._autostep = 0 + if self._top is None: + return + self._top.destroy() + self._top = None + + def prev(self, *e): + selection = self._readingList.curselection() + readingListSize = self._readingList.size() + + # there are readings + if readingListSize > 0: + # if one reading is currently selected + if len(selection) == 1: + index = int(selection[0]) + + # if it's on (or before) the first item + if index <= 0: + self._select_previous_example() + else: + self._readingList_store_selection(index - 1) + + else: + # select its first reading + self._readingList_store_selection(readingListSize - 1) + + else: + self._select_previous_example() + + def _select_previous_example(self): + # if the current example is not the first example + if self._curExample > 0: + self._exampleList_store_selection(self._curExample - 1) + else: + # go to the last example + self._exampleList_store_selection(len(self._examples) - 1) + + def next(self, *e): + selection = self._readingList.curselection() + readingListSize = self._readingList.size() + + # if there are readings + if readingListSize > 0: + # if one reading is currently selected + if len(selection) == 1: + index = int(selection[0]) + + # if it's on (or past) the last item + if index >= (readingListSize - 1): + self._select_next_example() + else: + self._readingList_store_selection(index + 1) + + else: + # select its first reading + self._readingList_store_selection(0) + + else: + self._select_next_example() + + def _select_next_example(self): + # if the current example is not the last example + if self._curExample < len(self._examples) - 1: + self._exampleList_store_selection(self._curExample + 1) + else: + # go to the first example + self._exampleList_store_selection(0) + + def about(self, *e): + ABOUT = ( + "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + + "Written by Daniel H. Garrette" + ) + TITLE = "About: NLTK DRT Glue Demo" + try: + from tkinter.messagebox import Message + + Message(message=ABOUT, title=TITLE).show() + except: + ShowText(self._top, TITLE, ABOUT) + + def postscript(self, *e): + self._autostep = 0 + self._cframe.print_to_file() + + def mainloop(self, *args, **kwargs): + """ + Enter the Tkinter mainloop. This function must be called if + this demo is created from a non-interactive program (e.g. + from a secript); otherwise, the demo will close as soon as + the script completes. + """ + if in_idle(): + return + self._top.mainloop(*args, **kwargs) + + def resize(self, size=None): + if size is not None: + self._size.set(size) + size = self._size.get() + self._font.configure(size=-(abs(size))) + self._boldfont.configure(size=-(abs(size))) + self._sysfont.configure(size=-(abs(size))) + self._bigfont.configure(size=-(abs(size + 2))) + self._redraw() + + def _toggle_remove_duplicates(self): + self._glue.remove_duplicates = not self._glue.remove_duplicates + + self._exampleList.selection_clear(0, "end") + self._readings = [] + self._populate_readingListbox() + self._readingCache = [None for ex in self._examples] + self._curExample = -1 + self._error = None + + self._drs = None + self._redraw() + + def _exampleList_select(self, event): + selection = self._exampleList.curselection() + if len(selection) != 1: + return + self._exampleList_store_selection(int(selection[0])) + + def _exampleList_store_selection(self, index): + self._curExample = index + example = self._examples[index] + + self._exampleList.selection_clear(0, "end") + if example: + cache = self._readingCache[index] + if cache: + if isinstance(cache, list): + self._readings = cache + self._error = None + else: + self._readings = [] + self._error = cache + else: + try: + self._readings = self._glue.parse_to_meaning(example) + self._error = None + self._readingCache[index] = self._readings + except Exception as e: + self._readings = [] + self._error = DrtVariableExpression(Variable("Error: " + str(e))) + self._readingCache[index] = self._error + + # add a star to the end of the example + self._exampleList.delete(index) + self._exampleList.insert(index, (" %s *" % example)) + self._exampleList.config( + height=min(len(self._examples), 25), width=40 + ) + + self._populate_readingListbox() + + self._exampleList.selection_set(index) + + self._drs = None + self._redraw() + + def _readingList_select(self, event): + selection = self._readingList.curselection() + if len(selection) != 1: + return + self._readingList_store_selection(int(selection[0])) + + def _readingList_store_selection(self, index): + reading = self._readings[index] + + self._readingList.selection_clear(0, "end") + if reading: + self._readingList.selection_set(index) + + self._drs = reading.simplify().normalize().resolve_anaphora() + + self._redraw() + + +class DrsWidget: + def __init__(self, canvas, drs, **attribs): + self._drs = drs + self._canvas = canvas + canvas.font = Font( + font=canvas.itemcget(canvas.create_text(0, 0, text=""), "font") + ) + canvas._BUFFER = 3 + self.bbox = (0, 0, 0, 0) + + def draw(self): + (right, bottom) = DrsDrawer(self._drs, canvas=self._canvas).draw() + self.bbox = (0, 0, right + 1, bottom + 1) + + def clear(self): + self._canvas.create_rectangle(self.bbox, fill="white", width="0") + + +def demo(): + examples = [ + "John walks", + "David sees Mary", + "David eats a sandwich", + "every man chases a dog", + # 'every man believes a dog yawns', + # 'John gives David a sandwich', + "John chases himself", + # 'John persuades David to order a pizza', + # 'John tries to go', + # 'John tries to find a unicorn', + # 'John seems to vanish', + # 'a unicorn seems to approach', + # 'every big cat leaves', + # 'every gray cat leaves', + # 'every big gray cat leaves', + # 'a former senator leaves', + # 'John likes a cat', + # 'John likes every cat', + # 'he walks', + # 'John walks and he leaves' + ] + DrtGlueDemo(examples).mainloop() + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/evaluate.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/evaluate.py new file mode 100644 index 0000000..291865b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/evaluate.py @@ -0,0 +1,829 @@ +# Natural Language Toolkit: Models for first-order languages with lambda +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein , +# URL: +# For license information, see LICENSE.TXT + +# TODO: +# - fix tracing +# - fix iterator-based approach to existentials + +""" +This module provides data structures for representing first-order +models. +""" + +import inspect +import re +import sys +import textwrap +from pprint import pformat + +from nltk.decorators import decorator # this used in code that is commented out +from nltk.sem.logic import ( + AbstractVariableExpression, + AllExpression, + AndExpression, + ApplicationExpression, + EqualityExpression, + ExistsExpression, + Expression, + IffExpression, + ImpExpression, + IndividualVariableExpression, + IotaExpression, + LambdaExpression, + NegatedExpression, + OrExpression, + Variable, + is_indvar, +) + + +class Error(Exception): + pass + + +class Undefined(Error): + pass + + +def trace(f, *args, **kw): + argspec = inspect.getfullargspec(f) + d = dict(zip(argspec[0], args)) + if d.pop("trace", None): + print() + for item in d.items(): + print("%s => %s" % item) + return f(*args, **kw) + + +def is_rel(s): + """ + Check whether a set represents a relation (of any arity). + + :param s: a set containing tuples of str elements + :type s: set + :rtype: bool + """ + # we have the empty relation, i.e. set() + if len(s) == 0: + return True + # all the elements are tuples of the same length + elif all(isinstance(el, tuple) for el in s) and len(max(s)) == len(min(s)): + return True + else: + raise ValueError("Set %r contains sequences of different lengths" % s) + + +def set2rel(s): + """ + Convert a set containing individuals (strings or numbers) into a set of + unary tuples. Any tuples of strings already in the set are passed through + unchanged. + + For example: + - set(['a', 'b']) => set([('a',), ('b',)]) + - set([3, 27]) => set([('3',), ('27',)]) + + :type s: set + :rtype: set of tuple of str + """ + new = set() + for elem in s: + if isinstance(elem, str): + new.add((elem,)) + elif isinstance(elem, int): + new.add(str(elem)) + else: + new.add(elem) + return new + + +def arity(rel): + """ + Check the arity of a relation. + :type rel: set of tuples + :rtype: int of tuple of str + """ + if len(rel) == 0: + return 0 + return len(list(rel)[0]) + + +class Valuation(dict): + """ + A dictionary which represents a model-theoretic Valuation of non-logical constants. + Keys are strings representing the constants to be interpreted, and values correspond + to individuals (represented as strings) and n-ary relations (represented as sets of tuples + of strings). + + An instance of ``Valuation`` will raise a KeyError exception (i.e., + just behave like a standard dictionary) if indexed with an expression that + is not in its list of symbols. + """ + + def __init__(self, xs): + """ + :param xs: a list of (symbol, value) pairs. + """ + super().__init__() + for (sym, val) in xs: + if isinstance(val, str) or isinstance(val, bool): + self[sym] = val + elif isinstance(val, set): + self[sym] = set2rel(val) + else: + msg = textwrap.fill( + "Error in initializing Valuation. " + "Unrecognized value for symbol '%s':\n%s" % (sym, val), + width=66, + ) + + raise ValueError(msg) + + def __getitem__(self, key): + if key in self: + return dict.__getitem__(self, key) + else: + raise Undefined("Unknown expression: '%s'" % key) + + def __str__(self): + return pformat(self) + + @property + def domain(self): + """Set-theoretic domain of the value-space of a Valuation.""" + dom = [] + for val in self.values(): + if isinstance(val, str): + dom.append(val) + elif not isinstance(val, bool): + dom.extend( + [elem for tuple_ in val for elem in tuple_ if elem is not None] + ) + return set(dom) + + @property + def symbols(self): + """The non-logical constants which the Valuation recognizes.""" + return sorted(self.keys()) + + @classmethod + def fromstring(cls, s): + return read_valuation(s) + + +########################################## +# REs used by the _read_valuation function +########################################## +_VAL_SPLIT_RE = re.compile(r"\s*=+>\s*") +_ELEMENT_SPLIT_RE = re.compile(r"\s*,\s*") +_TUPLES_RE = re.compile( + r"""\s* + (\([^)]+\)) # tuple-expression + \s*""", + re.VERBOSE, +) + + +def _read_valuation_line(s): + """ + Read a line in a valuation file. + + Lines are expected to be of the form:: + + noosa => n + girl => {g1, g2} + chase => {(b1, g1), (b2, g1), (g1, d1), (g2, d2)} + + :param s: input line + :type s: str + :return: a pair (symbol, value) + :rtype: tuple + """ + pieces = _VAL_SPLIT_RE.split(s) + symbol = pieces[0] + value = pieces[1] + # check whether the value is meant to be a set + if value.startswith("{"): + value = value[1:-1] + tuple_strings = _TUPLES_RE.findall(value) + # are the set elements tuples? + if tuple_strings: + set_elements = [] + for ts in tuple_strings: + ts = ts[1:-1] + element = tuple(_ELEMENT_SPLIT_RE.split(ts)) + set_elements.append(element) + else: + set_elements = _ELEMENT_SPLIT_RE.split(value) + value = set(set_elements) + return symbol, value + + +def read_valuation(s, encoding=None): + """ + Convert a valuation string into a valuation. + + :param s: a valuation string + :type s: str + :param encoding: the encoding of the input string, if it is binary + :type encoding: str + :return: a ``nltk.sem`` valuation + :rtype: Valuation + """ + if encoding is not None: + s = s.decode(encoding) + statements = [] + for linenum, line in enumerate(s.splitlines()): + line = line.strip() + if line.startswith("#") or line == "": + continue + try: + statements.append(_read_valuation_line(line)) + except ValueError as e: + raise ValueError(f"Unable to parse line {linenum}: {line}") from e + return Valuation(statements) + + +class Assignment(dict): + r""" + A dictionary which represents an assignment of values to variables. + + An assignment can only assign values from its domain. + + If an unknown expression *a* is passed to a model *M*\ 's + interpretation function *i*, *i* will first check whether *M*\ 's + valuation assigns an interpretation to *a* as a constant, and if + this fails, *i* will delegate the interpretation of *a* to + *g*. *g* only assigns values to individual variables (i.e., + members of the class ``IndividualVariableExpression`` in the ``logic`` + module. If a variable is not assigned a value by *g*, it will raise + an ``Undefined`` exception. + + A variable *Assignment* is a mapping from individual variables to + entities in the domain. Individual variables are usually indicated + with the letters ``'x'``, ``'y'``, ``'w'`` and ``'z'``, optionally + followed by an integer (e.g., ``'x0'``, ``'y332'``). Assignments are + created using the ``Assignment`` constructor, which also takes the + domain as a parameter. + + >>> from nltk.sem.evaluate import Assignment + >>> dom = set(['u1', 'u2', 'u3', 'u4']) + >>> g3 = Assignment(dom, [('x', 'u1'), ('y', 'u2')]) + >>> g3 == {'x': 'u1', 'y': 'u2'} + True + + There is also a ``print`` format for assignments which uses a notation + closer to that in logic textbooks: + + >>> print(g3) + g[u1/x][u2/y] + + It is also possible to update an assignment using the ``add`` method: + + >>> dom = set(['u1', 'u2', 'u3', 'u4']) + >>> g4 = Assignment(dom) + >>> g4.add('x', 'u1') + {'x': 'u1'} + + With no arguments, ``purge()`` is equivalent to ``clear()`` on a dictionary: + + >>> g4.purge() + >>> g4 + {} + + :param domain: the domain of discourse + :type domain: set + :param assign: a list of (varname, value) associations + :type assign: list + """ + + def __init__(self, domain, assign=None): + super().__init__() + self.domain = domain + if assign: + for (var, val) in assign: + assert val in self.domain, "'{}' is not in the domain: {}".format( + val, + self.domain, + ) + assert is_indvar(var), ( + "Wrong format for an Individual Variable: '%s'" % var + ) + self[var] = val + self.variant = None + self._addvariant() + + def __getitem__(self, key): + if key in self: + return dict.__getitem__(self, key) + else: + raise Undefined("Not recognized as a variable: '%s'" % key) + + def copy(self): + new = Assignment(self.domain) + new.update(self) + return new + + def purge(self, var=None): + """ + Remove one or all keys (i.e. logic variables) from an + assignment, and update ``self.variant``. + + :param var: a Variable acting as a key for the assignment. + """ + if var: + del self[var] + else: + self.clear() + self._addvariant() + return None + + def __str__(self): + """ + Pretty printing for assignments. {'x', 'u'} appears as 'g[u/x]' + """ + gstring = "g" + # Deterministic output for unit testing. + variant = sorted(self.variant) + for (val, var) in variant: + gstring += f"[{val}/{var}]" + return gstring + + def _addvariant(self): + """ + Create a more pretty-printable version of the assignment. + """ + list_ = [] + for item in self.items(): + pair = (item[1], item[0]) + list_.append(pair) + self.variant = list_ + return None + + def add(self, var, val): + """ + Add a new variable-value pair to the assignment, and update + ``self.variant``. + + """ + assert val in self.domain, f"{val} is not in the domain {self.domain}" + assert is_indvar(var), "Wrong format for an Individual Variable: '%s'" % var + self[var] = val + self._addvariant() + return self + + +class Model: + """ + A first order model is a domain *D* of discourse and a valuation *V*. + + A domain *D* is a set, and a valuation *V* is a map that associates + expressions with values in the model. + The domain of *V* should be a subset of *D*. + + Construct a new ``Model``. + + :type domain: set + :param domain: A set of entities representing the domain of discourse of the model. + :type valuation: Valuation + :param valuation: the valuation of the model. + :param prop: If this is set, then we are building a propositional\ + model and don't require the domain of *V* to be subset of *D*. + """ + + def __init__(self, domain, valuation): + assert isinstance(domain, set) + self.domain = domain + self.valuation = valuation + if not domain.issuperset(valuation.domain): + raise Error( + "The valuation domain, %s, must be a subset of the model's domain, %s" + % (valuation.domain, domain) + ) + + def __repr__(self): + return f"({self.domain!r}, {self.valuation!r})" + + def __str__(self): + return f"Domain = {self.domain},\nValuation = \n{self.valuation}" + + def evaluate(self, expr, g, trace=None): + """ + Read input expressions, and provide a handler for ``satisfy`` + that blocks further propagation of the ``Undefined`` error. + :param expr: An ``Expression`` of ``logic``. + :type g: Assignment + :param g: an assignment to individual variables. + :rtype: bool or 'Undefined' + """ + try: + parsed = Expression.fromstring(expr) + value = self.satisfy(parsed, g, trace=trace) + if trace: + print() + print(f"'{expr}' evaluates to {value} under M, {g}") + return value + except Undefined: + if trace: + print() + print(f"'{expr}' is undefined under M, {g}") + return "Undefined" + + def satisfy(self, parsed, g, trace=None): + """ + Recursive interpretation function for a formula of first-order logic. + + Raises an ``Undefined`` error when ``parsed`` is an atomic string + but is not a symbol or an individual variable. + + :return: Returns a truth value or ``Undefined`` if ``parsed`` is\ + complex, and calls the interpretation function ``i`` if ``parsed``\ + is atomic. + + :param parsed: An expression of ``logic``. + :type g: Assignment + :param g: an assignment to individual variables. + """ + + if isinstance(parsed, ApplicationExpression): + function, arguments = parsed.uncurry() + if isinstance(function, AbstractVariableExpression): + # It's a predicate expression ("P(x,y)"), so used uncurried arguments + funval = self.satisfy(function, g) + argvals = tuple(self.satisfy(arg, g) for arg in arguments) + return argvals in funval + else: + # It must be a lambda expression, so use curried form + funval = self.satisfy(parsed.function, g) + argval = self.satisfy(parsed.argument, g) + return funval[argval] + elif isinstance(parsed, NegatedExpression): + return not self.satisfy(parsed.term, g) + elif isinstance(parsed, AndExpression): + return self.satisfy(parsed.first, g) and self.satisfy(parsed.second, g) + elif isinstance(parsed, OrExpression): + return self.satisfy(parsed.first, g) or self.satisfy(parsed.second, g) + elif isinstance(parsed, ImpExpression): + return (not self.satisfy(parsed.first, g)) or self.satisfy(parsed.second, g) + elif isinstance(parsed, IffExpression): + return self.satisfy(parsed.first, g) == self.satisfy(parsed.second, g) + elif isinstance(parsed, EqualityExpression): + return self.satisfy(parsed.first, g) == self.satisfy(parsed.second, g) + elif isinstance(parsed, AllExpression): + new_g = g.copy() + for u in self.domain: + new_g.add(parsed.variable.name, u) + if not self.satisfy(parsed.term, new_g): + return False + return True + elif isinstance(parsed, ExistsExpression): + new_g = g.copy() + for u in self.domain: + new_g.add(parsed.variable.name, u) + if self.satisfy(parsed.term, new_g): + return True + return False + elif isinstance(parsed, IotaExpression): + new_g = g.copy() + for u in self.domain: + new_g.add(parsed.variable.name, u) + if self.satisfy(parsed.term, new_g): + return True + return False + elif isinstance(parsed, LambdaExpression): + cf = {} + var = parsed.variable.name + for u in self.domain: + val = self.satisfy(parsed.term, g.add(var, u)) + # NB the dict would be a lot smaller if we do this: + # if val: cf[u] = val + # But then need to deal with cases where f(a) should yield + # a function rather than just False. + cf[u] = val + return cf + else: + return self.i(parsed, g, trace) + + # @decorator(trace_eval) + def i(self, parsed, g, trace=False): + """ + An interpretation function. + + Assuming that ``parsed`` is atomic: + + - if ``parsed`` is a non-logical constant, calls the valuation *V* + - else if ``parsed`` is an individual variable, calls assignment *g* + - else returns ``Undefined``. + + :param parsed: an ``Expression`` of ``logic``. + :type g: Assignment + :param g: an assignment to individual variables. + :return: a semantic value + """ + # If parsed is a propositional letter 'p', 'q', etc, it could be in valuation.symbols + # and also be an IndividualVariableExpression. We want to catch this first case. + # So there is a procedural consequence to the ordering of clauses here: + if parsed.variable.name in self.valuation.symbols: + return self.valuation[parsed.variable.name] + elif isinstance(parsed, IndividualVariableExpression): + return g[parsed.variable.name] + + else: + raise Undefined("Can't find a value for %s" % parsed) + + def satisfiers(self, parsed, varex, g, trace=None, nesting=0): + """ + Generate the entities from the model's domain that satisfy an open formula. + + :param parsed: an open formula + :type parsed: Expression + :param varex: the relevant free individual variable in ``parsed``. + :type varex: VariableExpression or str + :param g: a variable assignment + :type g: Assignment + :return: a set of the entities that satisfy ``parsed``. + """ + + spacer = " " + indent = spacer + (spacer * nesting) + candidates = [] + + if isinstance(varex, str): + var = Variable(varex) + else: + var = varex + + if var in parsed.free(): + if trace: + print() + print( + (spacer * nesting) + + f"Open formula is '{parsed}' with assignment {g}" + ) + for u in self.domain: + new_g = g.copy() + new_g.add(var.name, u) + if trace and trace > 1: + lowtrace = trace - 1 + else: + lowtrace = 0 + value = self.satisfy(parsed, new_g, lowtrace) + + if trace: + print(indent + "(trying assignment %s)" % new_g) + + # parsed == False under g[u/var]? + if value == False: + if trace: + print(indent + f"value of '{parsed}' under {new_g} is False") + + # so g[u/var] is a satisfying assignment + else: + candidates.append(u) + if trace: + print(indent + f"value of '{parsed}' under {new_g} is {value}") + + result = {c for c in candidates} + # var isn't free in parsed + else: + raise Undefined(f"{var.name} is not free in {parsed}") + + return result + + +# ////////////////////////////////////////////////////////////////////// +# Demo.. +# ////////////////////////////////////////////////////////////////////// +# number of spacer chars +mult = 30 + +# Demo 1: Propositional Logic +################# +def propdemo(trace=None): + """Example of a propositional model.""" + + global val1, dom1, m1, g1 + val1 = Valuation([("P", True), ("Q", True), ("R", False)]) + dom1 = set() + m1 = Model(dom1, val1) + g1 = Assignment(dom1) + + print() + print("*" * mult) + print("Propositional Formulas Demo") + print("*" * mult) + print("(Propositional constants treated as nullary predicates)") + print() + print("Model m1:\n", m1) + print("*" * mult) + sentences = [ + "(P & Q)", + "(P & R)", + "- P", + "- R", + "- - P", + "- (P & R)", + "(P | R)", + "(R | P)", + "(R | R)", + "(- P | R)", + "(P | - P)", + "(P -> Q)", + "(P -> R)", + "(R -> P)", + "(P <-> P)", + "(R <-> R)", + "(P <-> R)", + ] + + for sent in sentences: + if trace: + print() + m1.evaluate(sent, g1, trace) + else: + print(f"The value of '{sent}' is: {m1.evaluate(sent, g1)}") + + +# Demo 2: FOL Model +############# + + +def folmodel(quiet=False, trace=None): + """Example of a first-order model.""" + + global val2, v2, dom2, m2, g2 + + v2 = [ + ("adam", "b1"), + ("betty", "g1"), + ("fido", "d1"), + ("girl", {"g1", "g2"}), + ("boy", {"b1", "b2"}), + ("dog", {"d1"}), + ("love", {("b1", "g1"), ("b2", "g2"), ("g1", "b1"), ("g2", "b1")}), + ] + val2 = Valuation(v2) + dom2 = val2.domain + m2 = Model(dom2, val2) + g2 = Assignment(dom2, [("x", "b1"), ("y", "g2")]) + + if not quiet: + print() + print("*" * mult) + print("Models Demo") + print("*" * mult) + print("Model m2:\n", "-" * 14, "\n", m2) + print("Variable assignment = ", g2) + + exprs = ["adam", "boy", "love", "walks", "x", "y", "z"] + parsed_exprs = [Expression.fromstring(e) for e in exprs] + + print() + for parsed in parsed_exprs: + try: + print( + "The interpretation of '%s' in m2 is %s" + % (parsed, m2.i(parsed, g2)) + ) + except Undefined: + print("The interpretation of '%s' in m2 is Undefined" % parsed) + + applications = [ + ("boy", ("adam")), + ("walks", ("adam",)), + ("love", ("adam", "y")), + ("love", ("y", "adam")), + ] + + for (fun, args) in applications: + try: + funval = m2.i(Expression.fromstring(fun), g2) + argsval = tuple(m2.i(Expression.fromstring(arg), g2) for arg in args) + print(f"{fun}({args}) evaluates to {argsval in funval}") + except Undefined: + print(f"{fun}({args}) evaluates to Undefined") + + +# Demo 3: FOL +######### + + +def foldemo(trace=None): + """ + Interpretation of closed expressions in a first-order model. + """ + folmodel(quiet=True) + + print() + print("*" * mult) + print("FOL Formulas Demo") + print("*" * mult) + + formulas = [ + "love (adam, betty)", + "(adam = mia)", + "\\x. (boy(x) | girl(x))", + "\\x. boy(x)(adam)", + "\\x y. love(x, y)", + "\\x y. love(x, y)(adam)(betty)", + "\\x y. love(x, y)(adam, betty)", + "\\x y. (boy(x) & love(x, y))", + "\\x. exists y. (boy(x) & love(x, y))", + "exists z1. boy(z1)", + "exists x. (boy(x) & -(x = adam))", + "exists x. (boy(x) & all y. love(y, x))", + "all x. (boy(x) | girl(x))", + "all x. (girl(x) -> exists y. boy(y) & love(x, y))", # Every girl loves exists boy. + "exists x. (boy(x) & all y. (girl(y) -> love(y, x)))", # There is exists boy that every girl loves. + "exists x. (boy(x) & all y. (girl(y) -> love(x, y)))", # exists boy loves every girl. + "all x. (dog(x) -> - girl(x))", + "exists x. exists y. (love(x, y) & love(x, y))", + ] + + for fmla in formulas: + g2.purge() + if trace: + m2.evaluate(fmla, g2, trace) + else: + print(f"The value of '{fmla}' is: {m2.evaluate(fmla, g2)}") + + +# Demo 3: Satisfaction +############# + + +def satdemo(trace=None): + """Satisfiers of an open formula in a first order model.""" + + print() + print("*" * mult) + print("Satisfiers Demo") + print("*" * mult) + + folmodel(quiet=True) + + formulas = [ + "boy(x)", + "(x = x)", + "(boy(x) | girl(x))", + "(boy(x) & girl(x))", + "love(adam, x)", + "love(x, adam)", + "-(x = adam)", + "exists z22. love(x, z22)", + "exists y. love(y, x)", + "all y. (girl(y) -> love(x, y))", + "all y. (girl(y) -> love(y, x))", + "all y. (girl(y) -> (boy(x) & love(y, x)))", + "(boy(x) & all y. (girl(y) -> love(x, y)))", + "(boy(x) & all y. (girl(y) -> love(y, x)))", + "(boy(x) & exists y. (girl(y) & love(y, x)))", + "(girl(x) -> dog(x))", + "all y. (dog(y) -> (x = y))", + "exists y. love(y, x)", + "exists y. (love(adam, y) & love(y, x))", + ] + + if trace: + print(m2) + + for fmla in formulas: + print(fmla) + Expression.fromstring(fmla) + + parsed = [Expression.fromstring(fmla) for fmla in formulas] + + for p in parsed: + g2.purge() + print( + "The satisfiers of '{}' are: {}".format(p, m2.satisfiers(p, "x", g2, trace)) + ) + + +def demo(num=0, trace=None): + """ + Run exists demos. + + - num = 1: propositional logic demo + - num = 2: first order model demo (only if trace is set) + - num = 3: first order sentences demo + - num = 4: satisfaction of open formulas demo + - any other value: run all the demos + + :param trace: trace = 1, or trace = 2 for more verbose tracing + """ + demos = {1: propdemo, 2: folmodel, 3: foldemo, 4: satdemo} + + try: + demos[num](trace=trace) + except KeyError: + for num in demos: + demos[num](trace=trace) + + +if __name__ == "__main__": + demo(2, trace=0) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/glue.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/glue.py new file mode 100644 index 0000000..1bda7dc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/glue.py @@ -0,0 +1,835 @@ +# Natural Language Toolkit: Glue Semantics +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +import os +from itertools import chain + +import nltk +from nltk.internals import Counter +from nltk.sem import drt, linearlogic +from nltk.sem.logic import ( + AbstractVariableExpression, + Expression, + LambdaExpression, + Variable, + VariableExpression, +) +from nltk.tag import BigramTagger, RegexpTagger, TrigramTagger, UnigramTagger + +SPEC_SEMTYPES = { + "a": "ex_quant", + "an": "ex_quant", + "every": "univ_quant", + "the": "def_art", + "no": "no_quant", + "default": "ex_quant", +} + +OPTIONAL_RELATIONSHIPS = ["nmod", "vmod", "punct"] + + +class GlueFormula: + def __init__(self, meaning, glue, indices=None): + if not indices: + indices = set() + + if isinstance(meaning, str): + self.meaning = Expression.fromstring(meaning) + elif isinstance(meaning, Expression): + self.meaning = meaning + else: + raise RuntimeError( + "Meaning term neither string or expression: %s, %s" + % (meaning, meaning.__class__) + ) + + if isinstance(glue, str): + self.glue = linearlogic.LinearLogicParser().parse(glue) + elif isinstance(glue, linearlogic.Expression): + self.glue = glue + else: + raise RuntimeError( + "Glue term neither string or expression: %s, %s" + % (glue, glue.__class__) + ) + + self.indices = indices + + def applyto(self, arg): + """self = (\\x.(walk x), (subj -o f)) + arg = (john , subj) + returns ((walk john), f) + """ + if self.indices & arg.indices: # if the sets are NOT disjoint + raise linearlogic.LinearLogicApplicationException( + f"'{self}' applied to '{arg}'. Indices are not disjoint." + ) + else: # if the sets ARE disjoint + return_indices = self.indices | arg.indices + + try: + return_glue = linearlogic.ApplicationExpression( + self.glue, arg.glue, arg.indices + ) + except linearlogic.LinearLogicApplicationException as e: + raise linearlogic.LinearLogicApplicationException( + f"'{self.simplify()}' applied to '{arg.simplify()}'" + ) from e + + arg_meaning_abstracted = arg.meaning + if return_indices: + for dep in self.glue.simplify().antecedent.dependencies[ + ::-1 + ]: # if self.glue is (A -o B), dep is in A.dependencies + arg_meaning_abstracted = self.make_LambdaExpression( + Variable("v%s" % dep), arg_meaning_abstracted + ) + return_meaning = self.meaning.applyto(arg_meaning_abstracted) + + return self.__class__(return_meaning, return_glue, return_indices) + + def make_VariableExpression(self, name): + return VariableExpression(name) + + def make_LambdaExpression(self, variable, term): + return LambdaExpression(variable, term) + + def lambda_abstract(self, other): + assert isinstance(other, GlueFormula) + assert isinstance(other.meaning, AbstractVariableExpression) + return self.__class__( + self.make_LambdaExpression(other.meaning.variable, self.meaning), + linearlogic.ImpExpression(other.glue, self.glue), + ) + + def compile(self, counter=None): + """From Iddo Lev's PhD Dissertation p108-109""" + if not counter: + counter = Counter() + (compiled_glue, new_forms) = self.glue.simplify().compile_pos( + counter, self.__class__ + ) + return new_forms + [ + self.__class__(self.meaning, compiled_glue, {counter.get()}) + ] + + def simplify(self): + return self.__class__( + self.meaning.simplify(), self.glue.simplify(), self.indices + ) + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.meaning == other.meaning + and self.glue == other.glue + ) + + def __ne__(self, other): + return not self == other + + # sorting for use in doctests which must be deterministic + def __lt__(self, other): + return str(self) < str(other) + + def __str__(self): + assert isinstance(self.indices, set) + accum = f"{self.meaning} : {self.glue}" + if self.indices: + accum += ( + " : {" + ", ".join(str(index) for index in sorted(self.indices)) + "}" + ) + return accum + + def __repr__(self): + return "%s" % self + + +class GlueDict(dict): + def __init__(self, filename, encoding=None): + self.filename = filename + self.file_encoding = encoding + self.read_file() + + def read_file(self, empty_first=True): + if empty_first: + self.clear() + + try: + contents = nltk.data.load( + self.filename, format="text", encoding=self.file_encoding + ) + # TODO: the above can't handle zip files, but this should anyway be fixed in nltk.data.load() + except LookupError as e: + try: + contents = nltk.data.load( + "file:" + self.filename, format="text", encoding=self.file_encoding + ) + except LookupError: + raise e + lines = contents.splitlines() + + for line in lines: # example: 'n : (\\x.( x), (v-or))' + # lambdacalc -^ linear logic -^ + line = line.strip() # remove trailing newline + if not len(line): + continue # skip empty lines + if line[0] == "#": + continue # skip commented out lines + + parts = line.split( + " : ", 2 + ) # ['verb', '(\\x.( x), ( subj -o f ))', '[subj]'] + + glue_formulas = [] + paren_count = 0 + tuple_start = 0 + tuple_comma = 0 + + relationships = None + + if len(parts) > 1: + for (i, c) in enumerate(parts[1]): + if c == "(": + if paren_count == 0: # if it's the first '(' of a tuple + tuple_start = i + 1 # then save the index + paren_count += 1 + elif c == ")": + paren_count -= 1 + if paren_count == 0: # if it's the last ')' of a tuple + meaning_term = parts[1][ + tuple_start:tuple_comma + ] # '\\x.( x)' + glue_term = parts[1][tuple_comma + 1 : i] # '(v-r)' + glue_formulas.append( + [meaning_term, glue_term] + ) # add the GlueFormula to the list + elif c == ",": + if ( + paren_count == 1 + ): # if it's a comma separating the parts of the tuple + tuple_comma = i # then save the index + elif c == "#": # skip comments at the ends of lines + if ( + paren_count != 0 + ): # if the line hasn't parsed correctly so far + raise RuntimeError( + "Formula syntax is incorrect for entry " + line + ) + break # break to the next line + + if len(parts) > 2: # if there is a relationship entry at the end + rel_start = parts[2].index("[") + 1 + rel_end = parts[2].index("]") + if rel_start == rel_end: + relationships = frozenset() + else: + relationships = frozenset( + r.strip() for r in parts[2][rel_start:rel_end].split(",") + ) + + try: + start_inheritance = parts[0].index("(") + end_inheritance = parts[0].index(")") + sem = parts[0][:start_inheritance].strip() + supertype = parts[0][start_inheritance + 1 : end_inheritance] + except: + sem = parts[0].strip() + supertype = None + + if sem not in self: + self[sem] = {} + + if ( + relationships is None + ): # if not specified for a specific relationship set + # add all relationship entries for parents + if supertype: + for rels in self[supertype]: + if rels not in self[sem]: + self[sem][rels] = [] + glue = self[supertype][rels] + self[sem][rels].extend(glue) + self[sem][rels].extend( + glue_formulas + ) # add the glue formulas to every rel entry + else: + if None not in self[sem]: + self[sem][None] = [] + self[sem][None].extend( + glue_formulas + ) # add the glue formulas to every rel entry + else: + if relationships not in self[sem]: + self[sem][relationships] = [] + if supertype: + self[sem][relationships].extend(self[supertype][relationships]) + self[sem][relationships].extend( + glue_formulas + ) # add the glue entry to the dictionary + + def __str__(self): + accum = "" + for pos in self: + str_pos = "%s" % pos + for relset in self[pos]: + i = 1 + for gf in self[pos][relset]: + if i == 1: + accum += str_pos + ": " + else: + accum += " " * (len(str_pos) + 2) + accum += "%s" % gf + if relset and i == len(self[pos][relset]): + accum += " : %s" % relset + accum += "\n" + i += 1 + return accum + + def to_glueformula_list(self, depgraph, node=None, counter=None, verbose=False): + if node is None: + # TODO: should it be depgraph.root? Is this code tested? + top = depgraph.nodes[0] + depList = list(chain.from_iterable(top["deps"].values())) + root = depgraph.nodes[depList[0]] + + return self.to_glueformula_list(depgraph, root, Counter(), verbose) + + glueformulas = self.lookup(node, depgraph, counter) + for dep_idx in chain.from_iterable(node["deps"].values()): + dep = depgraph.nodes[dep_idx] + glueformulas.extend( + self.to_glueformula_list(depgraph, dep, counter, verbose) + ) + return glueformulas + + def lookup(self, node, depgraph, counter): + semtype_names = self.get_semtypes(node) + + semtype = None + for name in semtype_names: + if name in self: + semtype = self[name] + break + if semtype is None: + # raise KeyError, "There is no GlueDict entry for sem type '%s' (for '%s')" % (sem, word) + return [] + + self.add_missing_dependencies(node, depgraph) + + lookup = self._lookup_semtype_option(semtype, node, depgraph) + + if not len(lookup): + raise KeyError( + "There is no GlueDict entry for sem type of '%s' " + "with tag '%s', and rel '%s'" % (node["word"], node["tag"], node["rel"]) + ) + + return self.get_glueformulas_from_semtype_entry( + lookup, node["word"], node, depgraph, counter + ) + + def add_missing_dependencies(self, node, depgraph): + rel = node["rel"].lower() + + if rel == "main": + headnode = depgraph.nodes[node["head"]] + subj = self.lookup_unique("subj", headnode, depgraph) + relation = subj["rel"] + node["deps"].setdefault(relation, []) + node["deps"][relation].append(subj["address"]) + # node['deps'].append(subj['address']) + + def _lookup_semtype_option(self, semtype, node, depgraph): + relationships = frozenset( + depgraph.nodes[dep]["rel"].lower() + for dep in chain.from_iterable(node["deps"].values()) + if depgraph.nodes[dep]["rel"].lower() not in OPTIONAL_RELATIONSHIPS + ) + + try: + lookup = semtype[relationships] + except KeyError: + # An exact match is not found, so find the best match where + # 'best' is defined as the glue entry whose relationship set has the + # most relations of any possible relationship set that is a subset + # of the actual depgraph + best_match = frozenset() + for relset_option in set(semtype) - {None}: + if ( + len(relset_option) > len(best_match) + and relset_option < relationships + ): + best_match = relset_option + if not best_match: + if None in semtype: + best_match = None + else: + return None + lookup = semtype[best_match] + + return lookup + + def get_semtypes(self, node): + """ + Based on the node, return a list of plausible semtypes in order of + plausibility. + """ + rel = node["rel"].lower() + word = node["word"].lower() + + if rel == "spec": + if word in SPEC_SEMTYPES: + return [SPEC_SEMTYPES[word]] + else: + return [SPEC_SEMTYPES["default"]] + elif rel in ["nmod", "vmod"]: + return [node["tag"], rel] + else: + return [node["tag"]] + + def get_glueformulas_from_semtype_entry( + self, lookup, word, node, depgraph, counter + ): + glueformulas = [] + + glueFormulaFactory = self.get_GlueFormula_factory() + for meaning, glue in lookup: + gf = glueFormulaFactory(self.get_meaning_formula(meaning, word), glue) + if not len(glueformulas): + gf.word = word + else: + gf.word = f"{word}{len(glueformulas) + 1}" + + gf.glue = self.initialize_labels(gf.glue, node, depgraph, counter.get()) + + glueformulas.append(gf) + return glueformulas + + def get_meaning_formula(self, generic, word): + """ + :param generic: A meaning formula string containing the + parameter "" + :param word: The actual word to be replace "" + """ + word = word.replace(".", "") + return generic.replace("", word) + + def initialize_labels(self, expr, node, depgraph, unique_index): + if isinstance(expr, linearlogic.AtomicExpression): + name = self.find_label_name(expr.name.lower(), node, depgraph, unique_index) + if name[0].isupper(): + return linearlogic.VariableExpression(name) + else: + return linearlogic.ConstantExpression(name) + else: + return linearlogic.ImpExpression( + self.initialize_labels(expr.antecedent, node, depgraph, unique_index), + self.initialize_labels(expr.consequent, node, depgraph, unique_index), + ) + + def find_label_name(self, name, node, depgraph, unique_index): + try: + dot = name.index(".") + + before_dot = name[:dot] + after_dot = name[dot + 1 :] + if before_dot == "super": + return self.find_label_name( + after_dot, depgraph.nodes[node["head"]], depgraph, unique_index + ) + else: + return self.find_label_name( + after_dot, + self.lookup_unique(before_dot, node, depgraph), + depgraph, + unique_index, + ) + except ValueError: + lbl = self.get_label(node) + if name == "f": + return lbl + elif name == "v": + return "%sv" % lbl + elif name == "r": + return "%sr" % lbl + elif name == "super": + return self.get_label(depgraph.nodes[node["head"]]) + elif name == "var": + return f"{lbl.upper()}{unique_index}" + elif name == "a": + return self.get_label(self.lookup_unique("conja", node, depgraph)) + elif name == "b": + return self.get_label(self.lookup_unique("conjb", node, depgraph)) + else: + return self.get_label(self.lookup_unique(name, node, depgraph)) + + def get_label(self, node): + """ + Pick an alphabetic character as identifier for an entity in the model. + + :param value: where to index into the list of characters + :type value: int + """ + value = node["address"] + + letter = [ + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + ][value - 1] + num = int(value) // 26 + if num > 0: + return letter + str(num) + else: + return letter + + def lookup_unique(self, rel, node, depgraph): + """ + Lookup 'key'. There should be exactly one item in the associated relation. + """ + deps = [ + depgraph.nodes[dep] + for dep in chain.from_iterable(node["deps"].values()) + if depgraph.nodes[dep]["rel"].lower() == rel.lower() + ] + + if len(deps) == 0: + raise KeyError( + "'{}' doesn't contain a feature '{}'".format(node["word"], rel) + ) + elif len(deps) > 1: + raise KeyError( + "'{}' should only have one feature '{}'".format(node["word"], rel) + ) + else: + return deps[0] + + def get_GlueFormula_factory(self): + return GlueFormula + + +class Glue: + def __init__( + self, semtype_file=None, remove_duplicates=False, depparser=None, verbose=False + ): + self.verbose = verbose + self.remove_duplicates = remove_duplicates + self.depparser = depparser + + from nltk import Prover9 + + self.prover = Prover9() + + if semtype_file: + self.semtype_file = semtype_file + else: + self.semtype_file = os.path.join( + "grammars", "sample_grammars", "glue.semtype" + ) + + def train_depparser(self, depgraphs=None): + if depgraphs: + self.depparser.train(depgraphs) + else: + self.depparser.train_from_file( + nltk.data.find( + os.path.join("grammars", "sample_grammars", "glue_train.conll") + ) + ) + + def parse_to_meaning(self, sentence): + readings = [] + for agenda in self.parse_to_compiled(sentence): + readings.extend(self.get_readings(agenda)) + return readings + + def get_readings(self, agenda): + readings = [] + agenda_length = len(agenda) + atomics = dict() + nonatomics = dict() + while agenda: # is not empty + cur = agenda.pop() + glue_simp = cur.glue.simplify() + if isinstance( + glue_simp, linearlogic.ImpExpression + ): # if cur.glue is non-atomic + for key in atomics: + try: + if isinstance(cur.glue, linearlogic.ApplicationExpression): + bindings = cur.glue.bindings + else: + bindings = linearlogic.BindingDict() + glue_simp.antecedent.unify(key, bindings) + for atomic in atomics[key]: + if not ( + cur.indices & atomic.indices + ): # if the sets of indices are disjoint + try: + agenda.append(cur.applyto(atomic)) + except linearlogic.LinearLogicApplicationException: + pass + except linearlogic.UnificationException: + pass + try: + nonatomics[glue_simp.antecedent].append(cur) + except KeyError: + nonatomics[glue_simp.antecedent] = [cur] + + else: # else cur.glue is atomic + for key in nonatomics: + for nonatomic in nonatomics[key]: + try: + if isinstance( + nonatomic.glue, linearlogic.ApplicationExpression + ): + bindings = nonatomic.glue.bindings + else: + bindings = linearlogic.BindingDict() + glue_simp.unify(key, bindings) + if not ( + cur.indices & nonatomic.indices + ): # if the sets of indices are disjoint + try: + agenda.append(nonatomic.applyto(cur)) + except linearlogic.LinearLogicApplicationException: + pass + except linearlogic.UnificationException: + pass + try: + atomics[glue_simp].append(cur) + except KeyError: + atomics[glue_simp] = [cur] + + for entry in atomics: + for gf in atomics[entry]: + if len(gf.indices) == agenda_length: + self._add_to_reading_list(gf, readings) + for entry in nonatomics: + for gf in nonatomics[entry]: + if len(gf.indices) == agenda_length: + self._add_to_reading_list(gf, readings) + return readings + + def _add_to_reading_list(self, glueformula, reading_list): + add_reading = True + if self.remove_duplicates: + for reading in reading_list: + try: + if reading.equiv(glueformula.meaning, self.prover): + add_reading = False + break + except Exception as e: + # if there is an exception, the syntax of the formula + # may not be understandable by the prover, so don't + # throw out the reading. + print("Error when checking logical equality of statements", e) + + if add_reading: + reading_list.append(glueformula.meaning) + + def parse_to_compiled(self, sentence): + gfls = [self.depgraph_to_glue(dg) for dg in self.dep_parse(sentence)] + return [self.gfl_to_compiled(gfl) for gfl in gfls] + + def dep_parse(self, sentence): + """ + Return a dependency graph for the sentence. + + :param sentence: the sentence to be parsed + :type sentence: list(str) + :rtype: DependencyGraph + """ + + # Lazy-initialize the depparser + if self.depparser is None: + from nltk.parse import MaltParser + + self.depparser = MaltParser(tagger=self.get_pos_tagger()) + if not self.depparser._trained: + self.train_depparser() + return self.depparser.parse(sentence, verbose=self.verbose) + + def depgraph_to_glue(self, depgraph): + return self.get_glue_dict().to_glueformula_list(depgraph) + + def get_glue_dict(self): + return GlueDict(self.semtype_file) + + def gfl_to_compiled(self, gfl): + index_counter = Counter() + return_list = [] + for gf in gfl: + return_list.extend(gf.compile(index_counter)) + + if self.verbose: + print("Compiled Glue Premises:") + for cgf in return_list: + print(cgf) + + return return_list + + def get_pos_tagger(self): + from nltk.corpus import brown + + regexp_tagger = RegexpTagger( + [ + (r"^-?[0-9]+(\.[0-9]+)?$", "CD"), # cardinal numbers + (r"(The|the|A|a|An|an)$", "AT"), # articles + (r".*able$", "JJ"), # adjectives + (r".*ness$", "NN"), # nouns formed from adjectives + (r".*ly$", "RB"), # adverbs + (r".*s$", "NNS"), # plural nouns + (r".*ing$", "VBG"), # gerunds + (r".*ed$", "VBD"), # past tense verbs + (r".*", "NN"), # nouns (default) + ] + ) + brown_train = brown.tagged_sents(categories="news") + unigram_tagger = UnigramTagger(brown_train, backoff=regexp_tagger) + bigram_tagger = BigramTagger(brown_train, backoff=unigram_tagger) + trigram_tagger = TrigramTagger(brown_train, backoff=bigram_tagger) + + # Override particular words + main_tagger = RegexpTagger( + [(r"(A|a|An|an)$", "ex_quant"), (r"(Every|every|All|all)$", "univ_quant")], + backoff=trigram_tagger, + ) + + return main_tagger + + +class DrtGlueFormula(GlueFormula): + def __init__(self, meaning, glue, indices=None): + if not indices: + indices = set() + + if isinstance(meaning, str): + self.meaning = drt.DrtExpression.fromstring(meaning) + elif isinstance(meaning, drt.DrtExpression): + self.meaning = meaning + else: + raise RuntimeError( + "Meaning term neither string or expression: %s, %s" + % (meaning, meaning.__class__) + ) + + if isinstance(glue, str): + self.glue = linearlogic.LinearLogicParser().parse(glue) + elif isinstance(glue, linearlogic.Expression): + self.glue = glue + else: + raise RuntimeError( + "Glue term neither string or expression: %s, %s" + % (glue, glue.__class__) + ) + + self.indices = indices + + def make_VariableExpression(self, name): + return drt.DrtVariableExpression(name) + + def make_LambdaExpression(self, variable, term): + return drt.DrtLambdaExpression(variable, term) + + +class DrtGlueDict(GlueDict): + def get_GlueFormula_factory(self): + return DrtGlueFormula + + +class DrtGlue(Glue): + def __init__( + self, semtype_file=None, remove_duplicates=False, depparser=None, verbose=False + ): + if not semtype_file: + semtype_file = os.path.join( + "grammars", "sample_grammars", "drt_glue.semtype" + ) + Glue.__init__(self, semtype_file, remove_duplicates, depparser, verbose) + + def get_glue_dict(self): + return DrtGlueDict(self.semtype_file) + + +def demo(show_example=-1): + from nltk.parse import MaltParser + + examples = [ + "David sees Mary", + "David eats a sandwich", + "every man chases a dog", + "every man believes a dog sleeps", + "John gives David a sandwich", + "John chases himself", + ] + # 'John persuades David to order a pizza', + # 'John tries to go', + # 'John tries to find a unicorn', + # 'John seems to vanish', + # 'a unicorn seems to approach', + # 'every big cat leaves', + # 'every gray cat leaves', + # 'every big gray cat leaves', + # 'a former senator leaves', + + print("============== DEMO ==============") + + tagger = RegexpTagger( + [ + ("^(David|Mary|John)$", "NNP"), + ( + "^(sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", + "VB", + ), + ("^(go|order|vanish|find|approach)$", "VB"), + ("^(a)$", "ex_quant"), + ("^(every)$", "univ_quant"), + ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), + ("^(big|gray|former)$", "JJ"), + ("^(him|himself)$", "PRP"), + ] + ) + + depparser = MaltParser(tagger=tagger) + glue = Glue(depparser=depparser, verbose=False) + + for (i, sentence) in enumerate(examples): + if i == show_example or show_example == -1: + print(f"[[[Example {i}]]] {sentence}") + for reading in glue.parse_to_meaning(sentence.split()): + print(reading.simplify()) + print("") + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/hole.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/hole.py new file mode 100644 index 0000000..b13cf1a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/hole.py @@ -0,0 +1,395 @@ +# Natural Language Toolkit: Logic +# +# Author: Peter Wang +# Updated by: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +An implementation of the Hole Semantics model, following Blackburn and Bos, +Representation and Inference for Natural Language (CSLI, 2005). + +The semantic representations are built by the grammar hole.fcfg. +This module contains driver code to read in sentences and parse them +according to a hole semantics grammar. + +After parsing, the semantic representation is in the form of an underspecified +representation that is not easy to read. We use a "plugging" algorithm to +convert that representation into first-order logic formulas. +""" + +from functools import reduce + +from nltk.parse import load_parser +from nltk.sem.logic import ( + AllExpression, + AndExpression, + ApplicationExpression, + ExistsExpression, + IffExpression, + ImpExpression, + LambdaExpression, + NegatedExpression, + OrExpression, +) +from nltk.sem.skolemize import skolemize + +# Note that in this code there may be multiple types of trees being referred to: +# +# 1. parse trees +# 2. the underspecified representation +# 3. first-order logic formula trees +# 4. the search space when plugging (search tree) +# + + +class Constants: + ALL = "ALL" + EXISTS = "EXISTS" + NOT = "NOT" + AND = "AND" + OR = "OR" + IMP = "IMP" + IFF = "IFF" + PRED = "PRED" + LEQ = "LEQ" + HOLE = "HOLE" + LABEL = "LABEL" + + MAP = { + ALL: lambda v, e: AllExpression(v.variable, e), + EXISTS: lambda v, e: ExistsExpression(v.variable, e), + NOT: NegatedExpression, + AND: AndExpression, + OR: OrExpression, + IMP: ImpExpression, + IFF: IffExpression, + PRED: ApplicationExpression, + } + + +class HoleSemantics: + """ + This class holds the broken-down components of a hole semantics, i.e. it + extracts the holes, labels, logic formula fragments and constraints out of + a big conjunction of such as produced by the hole semantics grammar. It + then provides some operations on the semantics dealing with holes, labels + and finding legal ways to plug holes with labels. + """ + + def __init__(self, usr): + """ + Constructor. `usr' is a ``sem.Expression`` representing an + Underspecified Representation Structure (USR). A USR has the following + special predicates: + ALL(l,v,n), + EXISTS(l,v,n), + AND(l,n,n), + OR(l,n,n), + IMP(l,n,n), + IFF(l,n,n), + PRED(l,v,n,v[,v]*) where the brackets and star indicate zero or more repetitions, + LEQ(n,n), + HOLE(n), + LABEL(n) + where l is the label of the node described by the predicate, n is either + a label or a hole, and v is a variable. + """ + self.holes = set() + self.labels = set() + self.fragments = {} # mapping of label -> formula fragment + self.constraints = set() # set of Constraints + self._break_down(usr) + self.top_most_labels = self._find_top_most_labels() + self.top_hole = self._find_top_hole() + + def is_node(self, x): + """ + Return true if x is a node (label or hole) in this semantic + representation. + """ + return x in (self.labels | self.holes) + + def _break_down(self, usr): + """ + Extract holes, labels, formula fragments and constraints from the hole + semantics underspecified representation (USR). + """ + if isinstance(usr, AndExpression): + self._break_down(usr.first) + self._break_down(usr.second) + elif isinstance(usr, ApplicationExpression): + func, args = usr.uncurry() + if func.variable.name == Constants.LEQ: + self.constraints.add(Constraint(args[0], args[1])) + elif func.variable.name == Constants.HOLE: + self.holes.add(args[0]) + elif func.variable.name == Constants.LABEL: + self.labels.add(args[0]) + else: + label = args[0] + assert label not in self.fragments + self.fragments[label] = (func, args[1:]) + else: + raise ValueError(usr.label()) + + def _find_top_nodes(self, node_list): + top_nodes = node_list.copy() + for f in self.fragments.values(): + # the label is the first argument of the predicate + args = f[1] + for arg in args: + if arg in node_list: + top_nodes.discard(arg) + return top_nodes + + def _find_top_most_labels(self): + """ + Return the set of labels which are not referenced directly as part of + another formula fragment. These will be the top-most labels for the + subtree that they are part of. + """ + return self._find_top_nodes(self.labels) + + def _find_top_hole(self): + """ + Return the hole that will be the top of the formula tree. + """ + top_holes = self._find_top_nodes(self.holes) + assert len(top_holes) == 1 # it must be unique + return top_holes.pop() + + def pluggings(self): + """ + Calculate and return all the legal pluggings (mappings of labels to + holes) of this semantics given the constraints. + """ + record = [] + self._plug_nodes([(self.top_hole, [])], self.top_most_labels, {}, record) + return record + + def _plug_nodes(self, queue, potential_labels, plug_acc, record): + """ + Plug the nodes in `queue' with the labels in `potential_labels'. + + Each element of `queue' is a tuple of the node to plug and the list of + ancestor holes from the root of the graph to that node. + + `potential_labels' is a set of the labels which are still available for + plugging. + + `plug_acc' is the incomplete mapping of holes to labels made on the + current branch of the search tree so far. + + `record' is a list of all the complete pluggings that we have found in + total so far. It is the only parameter that is destructively updated. + """ + if queue != []: + (node, ancestors) = queue[0] + if node in self.holes: + # The node is a hole, try to plug it. + self._plug_hole( + node, ancestors, queue[1:], potential_labels, plug_acc, record + ) + else: + assert node in self.labels + # The node is a label. Replace it in the queue by the holes and + # labels in the formula fragment named by that label. + args = self.fragments[node][1] + head = [(a, ancestors) for a in args if self.is_node(a)] + self._plug_nodes(head + queue[1:], potential_labels, plug_acc, record) + else: + raise Exception("queue empty") + + def _plug_hole(self, hole, ancestors0, queue, potential_labels0, plug_acc0, record): + """ + Try all possible ways of plugging a single hole. + See _plug_nodes for the meanings of the parameters. + """ + # Add the current hole we're trying to plug into the list of ancestors. + assert hole not in ancestors0 + ancestors = [hole] + ancestors0 + + # Try each potential label in this hole in turn. + for l in potential_labels0: + # Is the label valid in this hole? + if self._violates_constraints(l, ancestors): + continue + + plug_acc = plug_acc0.copy() + plug_acc[hole] = l + potential_labels = potential_labels0.copy() + potential_labels.remove(l) + + if len(potential_labels) == 0: + # No more potential labels. That must mean all the holes have + # been filled so we have found a legal plugging so remember it. + # + # Note that the queue might not be empty because there might + # be labels on there that point to formula fragments with + # no holes in them. _sanity_check_plugging will make sure + # all holes are filled. + self._sanity_check_plugging(plug_acc, self.top_hole, []) + record.append(plug_acc) + else: + # Recursively try to fill in the rest of the holes in the + # queue. The label we just plugged into the hole could have + # holes of its own so at the end of the queue. Putting it on + # the end of the queue gives us a breadth-first search, so that + # all the holes at level i of the formula tree are filled + # before filling level i+1. + # A depth-first search would work as well since the trees must + # be finite but the bookkeeping would be harder. + self._plug_nodes( + queue + [(l, ancestors)], potential_labels, plug_acc, record + ) + + def _violates_constraints(self, label, ancestors): + """ + Return True if the `label' cannot be placed underneath the holes given + by the set `ancestors' because it would violate the constraints imposed + on it. + """ + for c in self.constraints: + if c.lhs == label: + if c.rhs not in ancestors: + return True + return False + + def _sanity_check_plugging(self, plugging, node, ancestors): + """ + Make sure that a given plugging is legal. We recursively go through + each node and make sure that no constraints are violated. + We also check that all holes have been filled. + """ + if node in self.holes: + ancestors = [node] + ancestors + label = plugging[node] + else: + label = node + assert label in self.labels + for c in self.constraints: + if c.lhs == label: + assert c.rhs in ancestors + args = self.fragments[label][1] + for arg in args: + if self.is_node(arg): + self._sanity_check_plugging(plugging, arg, [label] + ancestors) + + def formula_tree(self, plugging): + """ + Return the first-order logic formula tree for this underspecified + representation using the plugging given. + """ + return self._formula_tree(plugging, self.top_hole) + + def _formula_tree(self, plugging, node): + if node in plugging: + return self._formula_tree(plugging, plugging[node]) + elif node in self.fragments: + pred, args = self.fragments[node] + children = [self._formula_tree(plugging, arg) for arg in args] + return reduce(Constants.MAP[pred.variable.name], children) + else: + return node + + +class Constraint: + """ + This class represents a constraint of the form (L =< N), + where L is a label and N is a node (a label or a hole). + """ + + def __init__(self, lhs, rhs): + self.lhs = lhs + self.rhs = rhs + + def __eq__(self, other): + if self.__class__ == other.__class__: + return self.lhs == other.lhs and self.rhs == other.rhs + else: + return False + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(repr(self)) + + def __repr__(self): + return f"({self.lhs} < {self.rhs})" + + +def hole_readings(sentence, grammar_filename=None, verbose=False): + if not grammar_filename: + grammar_filename = "grammars/sample_grammars/hole.fcfg" + + if verbose: + print("Reading grammar file", grammar_filename) + + parser = load_parser(grammar_filename) + + # Parse the sentence. + tokens = sentence.split() + trees = list(parser.parse(tokens)) + if verbose: + print("Got %d different parses" % len(trees)) + + all_readings = [] + for tree in trees: + # Get the semantic feature from the top of the parse tree. + sem = tree.label()["SEM"].simplify() + + # Print the raw semantic representation. + if verbose: + print("Raw: ", sem) + + # Skolemize away all quantifiers. All variables become unique. + while isinstance(sem, LambdaExpression): + sem = sem.term + skolemized = skolemize(sem) + + if verbose: + print("Skolemized:", skolemized) + + # Break the hole semantics representation down into its components + # i.e. holes, labels, formula fragments and constraints. + hole_sem = HoleSemantics(skolemized) + + # Maybe show the details of the semantic representation. + if verbose: + print("Holes: ", hole_sem.holes) + print("Labels: ", hole_sem.labels) + print("Constraints: ", hole_sem.constraints) + print("Top hole: ", hole_sem.top_hole) + print("Top labels: ", hole_sem.top_most_labels) + print("Fragments:") + for l, f in hole_sem.fragments.items(): + print(f"\t{l}: {f}") + + # Find all the possible ways to plug the formulas together. + pluggings = hole_sem.pluggings() + + # Build FOL formula trees using the pluggings. + readings = list(map(hole_sem.formula_tree, pluggings)) + + # Print out the formulas in a textual format. + if verbose: + for i, r in enumerate(readings): + print() + print("%d. %s" % (i, r)) + print() + + all_readings.extend(readings) + + return all_readings + + +if __name__ == "__main__": + for r in hole_readings("a dog barks"): + print(r) + print() + for r in hole_readings("every girl chases a dog"): + print(r) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/lfg.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/lfg.py new file mode 100644 index 0000000..35a7791 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/lfg.py @@ -0,0 +1,261 @@ +# Natural Language Toolkit: Lexical Functional Grammar +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +from itertools import chain + +from nltk.internals import Counter + + +class FStructure(dict): + def safeappend(self, key, item): + """ + Append 'item' to the list at 'key'. If no list exists for 'key', then + construct one. + """ + if key not in self: + self[key] = [] + self[key].append(item) + + def __setitem__(self, key, value): + dict.__setitem__(self, key.lower(), value) + + def __getitem__(self, key): + return dict.__getitem__(self, key.lower()) + + def __contains__(self, key): + return dict.__contains__(self, key.lower()) + + def to_glueformula_list(self, glue_dict): + depgraph = self.to_depgraph() + return glue_dict.to_glueformula_list(depgraph) + + def to_depgraph(self, rel=None): + from nltk.parse.dependencygraph import DependencyGraph + + depgraph = DependencyGraph() + nodes = depgraph.nodes + + self._to_depgraph(nodes, 0, "ROOT") + + # Add all the dependencies for all the nodes + for address, node in nodes.items(): + for n2 in (n for n in nodes.values() if n["rel"] != "TOP"): + if n2["head"] == address: + relation = n2["rel"] + node["deps"].setdefault(relation, []) + node["deps"][relation].append(n2["address"]) + + depgraph.root = nodes[1] + + return depgraph + + def _to_depgraph(self, nodes, head, rel): + index = len(nodes) + + nodes[index].update( + { + "address": index, + "word": self.pred[0], + "tag": self.pred[1], + "head": head, + "rel": rel, + } + ) + + for feature in sorted(self): + for item in sorted(self[feature]): + if isinstance(item, FStructure): + item._to_depgraph(nodes, index, feature) + elif isinstance(item, tuple): + new_index = len(nodes) + nodes[new_index].update( + { + "address": new_index, + "word": item[0], + "tag": item[1], + "head": index, + "rel": feature, + } + ) + elif isinstance(item, list): + for n in item: + n._to_depgraph(nodes, index, feature) + else: + raise Exception( + "feature %s is not an FStruct, a list, or a tuple" % feature + ) + + @staticmethod + def read_depgraph(depgraph): + return FStructure._read_depgraph(depgraph.root, depgraph) + + @staticmethod + def _read_depgraph(node, depgraph, label_counter=None, parent=None): + if not label_counter: + label_counter = Counter() + + if node["rel"].lower() in ["spec", "punct"]: + # the value of a 'spec' entry is a word, not an FStructure + return (node["word"], node["tag"]) + + else: + fstruct = FStructure() + fstruct.pred = None + fstruct.label = FStructure._make_label(label_counter.get()) + + fstruct.parent = parent + + word, tag = node["word"], node["tag"] + if tag[:2] == "VB": + if tag[2:3] == "D": + fstruct.safeappend("tense", ("PAST", "tense")) + fstruct.pred = (word, tag[:2]) + + if not fstruct.pred: + fstruct.pred = (word, tag) + + children = [ + depgraph.nodes[idx] + for idx in chain.from_iterable(node["deps"].values()) + ] + for child in children: + fstruct.safeappend( + child["rel"], + FStructure._read_depgraph(child, depgraph, label_counter, fstruct), + ) + + return fstruct + + @staticmethod + def _make_label(value): + """ + Pick an alphabetic character as identifier for an entity in the model. + + :param value: where to index into the list of characters + :type value: int + """ + letter = [ + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "a", + "b", + "c", + "d", + "e", + ][value - 1] + num = int(value) // 26 + if num > 0: + return letter + str(num) + else: + return letter + + def __repr__(self): + return self.__str__().replace("\n", "") + + def __str__(self): + return self.pretty_format() + + def pretty_format(self, indent=3): + try: + accum = "%s:[" % self.label + except NameError: + accum = "[" + try: + accum += "pred '%s'" % (self.pred[0]) + except NameError: + pass + + for feature in sorted(self): + for item in self[feature]: + if isinstance(item, FStructure): + next_indent = indent + len(feature) + 3 + len(self.label) + accum += "\n{}{} {}".format( + " " * (indent), + feature, + item.pretty_format(next_indent), + ) + elif isinstance(item, tuple): + accum += "\n{}{} '{}'".format(" " * (indent), feature, item[0]) + elif isinstance(item, list): + accum += "\n{}{} {{{}}}".format( + " " * (indent), + feature, + ("\n%s" % (" " * (indent + len(feature) + 2))).join(item), + ) + else: # ERROR + raise Exception( + "feature %s is not an FStruct, a list, or a tuple" % feature + ) + return accum + "]" + + +def demo_read_depgraph(): + from nltk.parse.dependencygraph import DependencyGraph + + dg1 = DependencyGraph( + """\ +Esso NNP 2 SUB +said VBD 0 ROOT +the DT 5 NMOD +Whiting NNP 5 NMOD +field NN 6 SUB +started VBD 2 VMOD +production NN 6 OBJ +Tuesday NNP 6 VMOD +""" + ) + dg2 = DependencyGraph( + """\ +John NNP 2 SUB +sees VBP 0 ROOT +Mary NNP 2 OBJ +""" + ) + dg3 = DependencyGraph( + """\ +a DT 2 SPEC +man NN 3 SUBJ +walks VB 0 ROOT +""" + ) + dg4 = DependencyGraph( + """\ +every DT 2 SPEC +girl NN 3 SUBJ +chases VB 0 ROOT +a DT 5 SPEC +dog NN 3 OBJ +""" + ) + + depgraphs = [dg1, dg2, dg3, dg4] + for dg in depgraphs: + print(FStructure.read_depgraph(dg)) + + +if __name__ == "__main__": + demo_read_depgraph() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/linearlogic.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/linearlogic.py new file mode 100644 index 0000000..e455036 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/linearlogic.py @@ -0,0 +1,482 @@ +# Natural Language Toolkit: Linear Logic +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +from nltk.internals import Counter +from nltk.sem.logic import APP, LogicParser + +_counter = Counter() + + +class Tokens: + # Punctuation + OPEN = "(" + CLOSE = ")" + + # Operations + IMP = "-o" + + PUNCT = [OPEN, CLOSE] + TOKENS = PUNCT + [IMP] + + +class LinearLogicParser(LogicParser): + """A linear logic expression parser.""" + + def __init__(self): + LogicParser.__init__(self) + + self.operator_precedence = {APP: 1, Tokens.IMP: 2, None: 3} + self.right_associated_operations += [Tokens.IMP] + + def get_all_symbols(self): + return Tokens.TOKENS + + def handle(self, tok, context): + if tok not in Tokens.TOKENS: + return self.handle_variable(tok, context) + elif tok == Tokens.OPEN: + return self.handle_open(tok, context) + + def get_BooleanExpression_factory(self, tok): + if tok == Tokens.IMP: + return ImpExpression + else: + return None + + def make_BooleanExpression(self, factory, first, second): + return factory(first, second) + + def attempt_ApplicationExpression(self, expression, context): + """Attempt to make an application expression. If the next tokens + are an argument in parens, then the argument expression is a + function being applied to the arguments. Otherwise, return the + argument expression.""" + if self.has_priority(APP, context): + if self.inRange(0) and self.token(0) == Tokens.OPEN: + self.token() # swallow then open paren + argument = self.process_next_expression(APP) + self.assertNextToken(Tokens.CLOSE) + expression = ApplicationExpression(expression, argument, None) + return expression + + def make_VariableExpression(self, name): + if name[0].isupper(): + return VariableExpression(name) + else: + return ConstantExpression(name) + + +class Expression: + + _linear_logic_parser = LinearLogicParser() + + @classmethod + def fromstring(cls, s): + return cls._linear_logic_parser.parse(s) + + def applyto(self, other, other_indices=None): + return ApplicationExpression(self, other, other_indices) + + def __call__(self, other): + return self.applyto(other) + + def __repr__(self): + return f"<{self.__class__.__name__} {self}>" + + +class AtomicExpression(Expression): + def __init__(self, name, dependencies=None): + """ + :param name: str for the constant name + :param dependencies: list of int for the indices on which this atom is dependent + """ + assert isinstance(name, str) + self.name = name + + if not dependencies: + dependencies = [] + self.dependencies = dependencies + + def simplify(self, bindings=None): + """ + If 'self' is bound by 'bindings', return the atomic to which it is bound. + Otherwise, return self. + + :param bindings: ``BindingDict`` A dictionary of bindings used to simplify + :return: ``AtomicExpression`` + """ + if bindings and self in bindings: + return bindings[self] + else: + return self + + def compile_pos(self, index_counter, glueFormulaFactory): + """ + From Iddo Lev's PhD Dissertation p108-109 + + :param index_counter: ``Counter`` for unique indices + :param glueFormulaFactory: ``GlueFormula`` for creating new glue formulas + :return: (``Expression``,set) for the compiled linear logic and any newly created glue formulas + """ + self.dependencies = [] + return (self, []) + + def compile_neg(self, index_counter, glueFormulaFactory): + """ + From Iddo Lev's PhD Dissertation p108-109 + + :param index_counter: ``Counter`` for unique indices + :param glueFormulaFactory: ``GlueFormula`` for creating new glue formulas + :return: (``Expression``,set) for the compiled linear logic and any newly created glue formulas + """ + self.dependencies = [] + return (self, []) + + def initialize_labels(self, fstruct): + self.name = fstruct.initialize_label(self.name.lower()) + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.name == other.name + + def __ne__(self, other): + return not self == other + + def __str__(self): + accum = self.name + if self.dependencies: + accum += "%s" % self.dependencies + return accum + + def __hash__(self): + return hash(self.name) + + +class ConstantExpression(AtomicExpression): + def unify(self, other, bindings): + """ + If 'other' is a constant, then it must be equal to 'self'. If 'other' is a variable, + then it must not be bound to anything other than 'self'. + + :param other: ``Expression`` + :param bindings: ``BindingDict`` A dictionary of all current bindings + :return: ``BindingDict`` A new combined dictionary of of 'bindings' and any new binding + :raise UnificationException: If 'self' and 'other' cannot be unified in the context of 'bindings' + """ + assert isinstance(other, Expression) + if isinstance(other, VariableExpression): + try: + return bindings + BindingDict([(other, self)]) + except VariableBindingException: + pass + elif self == other: + return bindings + raise UnificationException(self, other, bindings) + + +class VariableExpression(AtomicExpression): + def unify(self, other, bindings): + """ + 'self' must not be bound to anything other than 'other'. + + :param other: ``Expression`` + :param bindings: ``BindingDict`` A dictionary of all current bindings + :return: ``BindingDict`` A new combined dictionary of of 'bindings' and the new binding + :raise UnificationException: If 'self' and 'other' cannot be unified in the context of 'bindings' + """ + assert isinstance(other, Expression) + try: + if self == other: + return bindings + else: + return bindings + BindingDict([(self, other)]) + except VariableBindingException as e: + raise UnificationException(self, other, bindings) from e + + +class ImpExpression(Expression): + def __init__(self, antecedent, consequent): + """ + :param antecedent: ``Expression`` for the antecedent + :param consequent: ``Expression`` for the consequent + """ + assert isinstance(antecedent, Expression) + assert isinstance(consequent, Expression) + self.antecedent = antecedent + self.consequent = consequent + + def simplify(self, bindings=None): + return self.__class__( + self.antecedent.simplify(bindings), self.consequent.simplify(bindings) + ) + + def unify(self, other, bindings): + """ + Both the antecedent and consequent of 'self' and 'other' must unify. + + :param other: ``ImpExpression`` + :param bindings: ``BindingDict`` A dictionary of all current bindings + :return: ``BindingDict`` A new combined dictionary of of 'bindings' and any new bindings + :raise UnificationException: If 'self' and 'other' cannot be unified in the context of 'bindings' + """ + assert isinstance(other, ImpExpression) + try: + return ( + bindings + + self.antecedent.unify(other.antecedent, bindings) + + self.consequent.unify(other.consequent, bindings) + ) + except VariableBindingException as e: + raise UnificationException(self, other, bindings) from e + + def compile_pos(self, index_counter, glueFormulaFactory): + """ + From Iddo Lev's PhD Dissertation p108-109 + + :param index_counter: ``Counter`` for unique indices + :param glueFormulaFactory: ``GlueFormula`` for creating new glue formulas + :return: (``Expression``,set) for the compiled linear logic and any newly created glue formulas + """ + (a, a_new) = self.antecedent.compile_neg(index_counter, glueFormulaFactory) + (c, c_new) = self.consequent.compile_pos(index_counter, glueFormulaFactory) + return (ImpExpression(a, c), a_new + c_new) + + def compile_neg(self, index_counter, glueFormulaFactory): + """ + From Iddo Lev's PhD Dissertation p108-109 + + :param index_counter: ``Counter`` for unique indices + :param glueFormulaFactory: ``GlueFormula`` for creating new glue formulas + :return: (``Expression``,list of ``GlueFormula``) for the compiled linear logic and any newly created glue formulas + """ + (a, a_new) = self.antecedent.compile_pos(index_counter, glueFormulaFactory) + (c, c_new) = self.consequent.compile_neg(index_counter, glueFormulaFactory) + fresh_index = index_counter.get() + c.dependencies.append(fresh_index) + new_v = glueFormulaFactory("v%s" % fresh_index, a, {fresh_index}) + return (c, a_new + c_new + [new_v]) + + def initialize_labels(self, fstruct): + self.antecedent.initialize_labels(fstruct) + self.consequent.initialize_labels(fstruct) + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.antecedent == other.antecedent + and self.consequent == other.consequent + ) + + def __ne__(self, other): + return not self == other + + def __str__(self): + return "{}{} {} {}{}".format( + Tokens.OPEN, + self.antecedent, + Tokens.IMP, + self.consequent, + Tokens.CLOSE, + ) + + def __hash__(self): + return hash(f"{hash(self.antecedent)}{Tokens.IMP}{hash(self.consequent)}") + + +class ApplicationExpression(Expression): + def __init__(self, function, argument, argument_indices=None): + """ + :param function: ``Expression`` for the function + :param argument: ``Expression`` for the argument + :param argument_indices: set for the indices of the glue formula from which the argument came + :raise LinearLogicApplicationException: If 'function' cannot be applied to 'argument' given 'argument_indices'. + """ + function_simp = function.simplify() + argument_simp = argument.simplify() + + assert isinstance(function_simp, ImpExpression) + assert isinstance(argument_simp, Expression) + + bindings = BindingDict() + + try: + if isinstance(function, ApplicationExpression): + bindings += function.bindings + if isinstance(argument, ApplicationExpression): + bindings += argument.bindings + bindings += function_simp.antecedent.unify(argument_simp, bindings) + except UnificationException as e: + raise LinearLogicApplicationException( + f"Cannot apply {function_simp} to {argument_simp}. {e}" + ) from e + + # If you are running it on complied premises, more conditions apply + if argument_indices: + # A.dependencies of (A -o (B -o C)) must be a proper subset of argument_indices + if not set(function_simp.antecedent.dependencies) < argument_indices: + raise LinearLogicApplicationException( + "Dependencies unfulfilled when attempting to apply Linear Logic formula %s to %s" + % (function_simp, argument_simp) + ) + if set(function_simp.antecedent.dependencies) == argument_indices: + raise LinearLogicApplicationException( + "Dependencies not a proper subset of indices when attempting to apply Linear Logic formula %s to %s" + % (function_simp, argument_simp) + ) + + self.function = function + self.argument = argument + self.bindings = bindings + + def simplify(self, bindings=None): + """ + Since function is an implication, return its consequent. There should be + no need to check that the application is valid since the checking is done + by the constructor. + + :param bindings: ``BindingDict`` A dictionary of bindings used to simplify + :return: ``Expression`` + """ + if not bindings: + bindings = self.bindings + + return self.function.simplify(bindings).consequent + + def __eq__(self, other): + return ( + self.__class__ == other.__class__ + and self.function == other.function + and self.argument == other.argument + ) + + def __ne__(self, other): + return not self == other + + def __str__(self): + return "%s" % self.function + Tokens.OPEN + "%s" % self.argument + Tokens.CLOSE + + def __hash__(self): + return hash(f"{hash(self.antecedent)}{Tokens.OPEN}{hash(self.consequent)}") + + +class BindingDict: + def __init__(self, bindings=None): + """ + :param bindings: + list [(``VariableExpression``, ``AtomicExpression``)] to initialize the dictionary + dict {``VariableExpression``: ``AtomicExpression``} to initialize the dictionary + """ + self.d = {} + + if isinstance(bindings, dict): + bindings = bindings.items() + + if bindings: + for (v, b) in bindings: + self[v] = b + + def __setitem__(self, variable, binding): + """ + A binding is consistent with the dict if its variable is not already bound, OR if its + variable is already bound to its argument. + + :param variable: ``VariableExpression`` The variable bind + :param binding: ``Expression`` The expression to which 'variable' should be bound + :raise VariableBindingException: If the variable cannot be bound in this dictionary + """ + assert isinstance(variable, VariableExpression) + assert isinstance(binding, Expression) + + assert variable != binding + + existing = self.d.get(variable, None) + + if not existing or binding == existing: + self.d[variable] = binding + else: + raise VariableBindingException( + "Variable %s already bound to another value" % (variable) + ) + + def __getitem__(self, variable): + """ + Return the expression to which 'variable' is bound + """ + assert isinstance(variable, VariableExpression) + + intermediate = self.d[variable] + while intermediate: + try: + intermediate = self.d[intermediate] + except KeyError: + return intermediate + + def __contains__(self, item): + return item in self.d + + def __add__(self, other): + """ + :param other: ``BindingDict`` The dict with which to combine self + :return: ``BindingDict`` A new dict containing all the elements of both parameters + :raise VariableBindingException: If the parameter dictionaries are not consistent with each other + """ + try: + combined = BindingDict() + for v in self.d: + combined[v] = self.d[v] + for v in other.d: + combined[v] = other.d[v] + return combined + except VariableBindingException as e: + raise VariableBindingException( + "Attempting to add two contradicting" + " VariableBindingsLists: %s, %s" % (self, other) + ) from e + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + if not isinstance(other, BindingDict): + raise TypeError + return self.d == other.d + + def __str__(self): + return "{" + ", ".join(f"{v}: {self.d[v]}" for v in sorted(self.d.keys())) + "}" + + def __repr__(self): + return "BindingDict: %s" % self + + +class VariableBindingException(Exception): + pass + + +class UnificationException(Exception): + def __init__(self, a, b, bindings): + Exception.__init__(self, f"Cannot unify {a} with {b} given {bindings}") + + +class LinearLogicApplicationException(Exception): + pass + + +def demo(): + lexpr = Expression.fromstring + + print(lexpr(r"f")) + print(lexpr(r"(g -o f)")) + print(lexpr(r"((g -o G) -o G)")) + print(lexpr(r"g -o h -o f")) + print(lexpr(r"(g -o f)(g)").simplify()) + print(lexpr(r"(H -o f)(g)").simplify()) + print(lexpr(r"((g -o G) -o G)((g -o f))").simplify()) + print(lexpr(r"(H -o H)((g -o f))").simplify()) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/logic.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/logic.py new file mode 100644 index 0000000..daf3262 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/logic.py @@ -0,0 +1,2065 @@ +# Natural Language Toolkit: Logic +# +# Author: Dan Garrette +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +A version of first order predicate logic, built on +top of the typed lambda calculus. +""" + +import operator +import re +from collections import defaultdict +from functools import reduce, total_ordering + +from nltk.internals import Counter +from nltk.util import Trie + +APP = "APP" + +_counter = Counter() + + +class Tokens: + LAMBDA = "\\" + LAMBDA_LIST = ["\\"] + + # Quantifiers + EXISTS = "exists" + EXISTS_LIST = ["some", "exists", "exist"] + ALL = "all" + ALL_LIST = ["all", "forall"] + IOTA = "iota" + IOTA_LIST = ["iota"] + + # Punctuation + DOT = "." + OPEN = "(" + CLOSE = ")" + COMMA = "," + + # Operations + NOT = "-" + NOT_LIST = ["not", "-", "!"] + AND = "&" + AND_LIST = ["and", "&", "^"] + OR = "|" + OR_LIST = ["or", "|"] + IMP = "->" + IMP_LIST = ["implies", "->", "=>"] + IFF = "<->" + IFF_LIST = ["iff", "<->", "<=>"] + EQ = "=" + EQ_LIST = ["=", "=="] + NEQ = "!=" + NEQ_LIST = ["!="] + + # Collections of tokens + BINOPS = AND_LIST + OR_LIST + IMP_LIST + IFF_LIST + QUANTS = EXISTS_LIST + ALL_LIST + IOTA_LIST + PUNCT = [DOT, OPEN, CLOSE, COMMA] + + TOKENS = BINOPS + EQ_LIST + NEQ_LIST + QUANTS + LAMBDA_LIST + PUNCT + NOT_LIST + + # Special + SYMBOLS = [x for x in TOKENS if re.match(r"^[-\\.(),!&^|>=<]*$", x)] + + +def boolean_ops(): + """ + Boolean operators + """ + names = ["negation", "conjunction", "disjunction", "implication", "equivalence"] + for pair in zip(names, [Tokens.NOT, Tokens.AND, Tokens.OR, Tokens.IMP, Tokens.IFF]): + print("%-15s\t%s" % pair) + + +def equality_preds(): + """ + Equality predicates + """ + names = ["equality", "inequality"] + for pair in zip(names, [Tokens.EQ, Tokens.NEQ]): + print("%-15s\t%s" % pair) + + +def binding_ops(): + """ + Binding operators + """ + names = ["existential", "universal", "lambda"] + for pair in zip(names, [Tokens.EXISTS, Tokens.ALL, Tokens.LAMBDA, Tokens.IOTA]): + print("%-15s\t%s" % pair) + + +class LogicParser: + """A lambda calculus expression parser.""" + + def __init__(self, type_check=False): + """ + :param type_check: should type checking be performed + to their types? + :type type_check: bool + """ + assert isinstance(type_check, bool) + + self._currentIndex = 0 + self._buffer = [] + self.type_check = type_check + + """A list of tuples of quote characters. The 4-tuple is comprised + of the start character, the end character, the escape character, and + a boolean indicating whether the quotes should be included in the + result. Quotes are used to signify that a token should be treated as + atomic, ignoring any special characters within the token. The escape + character allows the quote end character to be used within the quote. + If True, the boolean indicates that the final token should contain the + quote and escape characters. + This method exists to be overridden""" + self.quote_chars = [] + + self.operator_precedence = dict( + [(x, 1) for x in Tokens.LAMBDA_LIST] + + [(x, 2) for x in Tokens.NOT_LIST] + + [(APP, 3)] + + [(x, 4) for x in Tokens.EQ_LIST + Tokens.NEQ_LIST] + + [(x, 5) for x in Tokens.QUANTS] + + [(x, 6) for x in Tokens.AND_LIST] + + [(x, 7) for x in Tokens.OR_LIST] + + [(x, 8) for x in Tokens.IMP_LIST] + + [(x, 9) for x in Tokens.IFF_LIST] + + [(None, 10)] + ) + self.right_associated_operations = [APP] + + def parse(self, data, signature=None): + """ + Parse the expression. + + :param data: str for the input to be parsed + :param signature: ``dict`` that maps variable names to type + strings + :returns: a parsed Expression + """ + data = data.rstrip() + + self._currentIndex = 0 + self._buffer, mapping = self.process(data) + + try: + result = self.process_next_expression(None) + if self.inRange(0): + raise UnexpectedTokenException(self._currentIndex + 1, self.token(0)) + except LogicalExpressionException as e: + msg = "{}\n{}\n{}^".format(e, data, " " * mapping[e.index - 1]) + raise LogicalExpressionException(None, msg) from e + + if self.type_check: + result.typecheck(signature) + + return result + + def process(self, data): + """Split the data into tokens""" + out = [] + mapping = {} + tokenTrie = Trie(self.get_all_symbols()) + token = "" + data_idx = 0 + token_start_idx = data_idx + while data_idx < len(data): + cur_data_idx = data_idx + quoted_token, data_idx = self.process_quoted_token(data_idx, data) + if quoted_token: + if not token: + token_start_idx = cur_data_idx + token += quoted_token + continue + + st = tokenTrie + c = data[data_idx] + symbol = "" + while c in st: + symbol += c + st = st[c] + if len(data) - data_idx > len(symbol): + c = data[data_idx + len(symbol)] + else: + break + if Trie.LEAF in st: + # token is a complete symbol + if token: + mapping[len(out)] = token_start_idx + out.append(token) + token = "" + mapping[len(out)] = data_idx + out.append(symbol) + data_idx += len(symbol) + else: + if data[data_idx] in " \t\n": # any whitespace + if token: + mapping[len(out)] = token_start_idx + out.append(token) + token = "" + else: + if not token: + token_start_idx = data_idx + token += data[data_idx] + data_idx += 1 + if token: + mapping[len(out)] = token_start_idx + out.append(token) + mapping[len(out)] = len(data) + mapping[len(out) + 1] = len(data) + 1 + return out, mapping + + def process_quoted_token(self, data_idx, data): + token = "" + c = data[data_idx] + i = data_idx + for start, end, escape, incl_quotes in self.quote_chars: + if c == start: + if incl_quotes: + token += c + i += 1 + while data[i] != end: + if data[i] == escape: + if incl_quotes: + token += data[i] + i += 1 + if len(data) == i: # if there are no more chars + raise LogicalExpressionException( + None, + "End of input reached. " + "Escape character [%s] found at end." % escape, + ) + token += data[i] + else: + token += data[i] + i += 1 + if len(data) == i: + raise LogicalExpressionException( + None, "End of input reached. " "Expected: [%s]" % end + ) + if incl_quotes: + token += data[i] + i += 1 + if not token: + raise LogicalExpressionException(None, "Empty quoted token found") + break + return token, i + + def get_all_symbols(self): + """This method exists to be overridden""" + return Tokens.SYMBOLS + + def inRange(self, location): + """Return TRUE if the given location is within the buffer""" + return self._currentIndex + location < len(self._buffer) + + def token(self, location=None): + """Get the next waiting token. If a location is given, then + return the token at currentIndex+location without advancing + currentIndex; setting it gives lookahead/lookback capability.""" + try: + if location is None: + tok = self._buffer[self._currentIndex] + self._currentIndex += 1 + else: + tok = self._buffer[self._currentIndex + location] + return tok + except IndexError as e: + raise ExpectedMoreTokensException(self._currentIndex + 1) from e + + def isvariable(self, tok): + return tok not in Tokens.TOKENS + + def process_next_expression(self, context): + """Parse the next complete expression from the stream and return it.""" + try: + tok = self.token() + except ExpectedMoreTokensException as e: + raise ExpectedMoreTokensException( + self._currentIndex + 1, message="Expression expected." + ) from e + + accum = self.handle(tok, context) + + if not accum: + raise UnexpectedTokenException( + self._currentIndex, tok, message="Expression expected." + ) + + return self.attempt_adjuncts(accum, context) + + def handle(self, tok, context): + """This method is intended to be overridden for logics that + use different operators or expressions""" + if self.isvariable(tok): + return self.handle_variable(tok, context) + + elif tok in Tokens.NOT_LIST: + return self.handle_negation(tok, context) + + elif tok in Tokens.LAMBDA_LIST: + return self.handle_lambda(tok, context) + + elif tok in Tokens.QUANTS: + return self.handle_quant(tok, context) + + elif tok == Tokens.OPEN: + return self.handle_open(tok, context) + + def attempt_adjuncts(self, expression, context): + cur_idx = None + while cur_idx != self._currentIndex: # while adjuncts are added + cur_idx = self._currentIndex + expression = self.attempt_EqualityExpression(expression, context) + expression = self.attempt_ApplicationExpression(expression, context) + expression = self.attempt_BooleanExpression(expression, context) + return expression + + def handle_negation(self, tok, context): + return self.make_NegatedExpression(self.process_next_expression(Tokens.NOT)) + + def make_NegatedExpression(self, expression): + return NegatedExpression(expression) + + def handle_variable(self, tok, context): + # It's either: 1) a predicate expression: sees(x,y) + # 2) an application expression: P(x) + # 3) a solo variable: john OR x + accum = self.make_VariableExpression(tok) + if self.inRange(0) and self.token(0) == Tokens.OPEN: + # The predicate has arguments + if not isinstance(accum, FunctionVariableExpression) and not isinstance( + accum, ConstantExpression + ): + raise LogicalExpressionException( + self._currentIndex, + "'%s' is an illegal predicate name. " + "Individual variables may not be used as " + "predicates." % tok, + ) + self.token() # swallow the Open Paren + + # curry the arguments + accum = self.make_ApplicationExpression( + accum, self.process_next_expression(APP) + ) + while self.inRange(0) and self.token(0) == Tokens.COMMA: + self.token() # swallow the comma + accum = self.make_ApplicationExpression( + accum, self.process_next_expression(APP) + ) + self.assertNextToken(Tokens.CLOSE) + return accum + + def get_next_token_variable(self, description): + try: + tok = self.token() + except ExpectedMoreTokensException as e: + raise ExpectedMoreTokensException(e.index, "Variable expected.") from e + if isinstance(self.make_VariableExpression(tok), ConstantExpression): + raise LogicalExpressionException( + self._currentIndex, + "'%s' is an illegal variable name. " + "Constants may not be %s." % (tok, description), + ) + return Variable(tok) + + def handle_lambda(self, tok, context): + # Expression is a lambda expression + if not self.inRange(0): + raise ExpectedMoreTokensException( + self._currentIndex + 2, + message="Variable and Expression expected following lambda operator.", + ) + vars = [self.get_next_token_variable("abstracted")] + while True: + if not self.inRange(0) or ( + self.token(0) == Tokens.DOT and not self.inRange(1) + ): + raise ExpectedMoreTokensException( + self._currentIndex + 2, message="Expression expected." + ) + if not self.isvariable(self.token(0)): + break + # Support expressions like: \x y.M == \x.\y.M + vars.append(self.get_next_token_variable("abstracted")) + if self.inRange(0) and self.token(0) == Tokens.DOT: + self.token() # swallow the dot + + accum = self.process_next_expression(tok) + while vars: + accum = self.make_LambdaExpression(vars.pop(), accum) + return accum + + def handle_quant(self, tok, context): + # Expression is a quantified expression: some x.M + factory = self.get_QuantifiedExpression_factory(tok) + + if not self.inRange(0): + raise ExpectedMoreTokensException( + self._currentIndex + 2, + message="Variable and Expression expected following quantifier '%s'." + % tok, + ) + vars = [self.get_next_token_variable("quantified")] + while True: + if not self.inRange(0) or ( + self.token(0) == Tokens.DOT and not self.inRange(1) + ): + raise ExpectedMoreTokensException( + self._currentIndex + 2, message="Expression expected." + ) + if not self.isvariable(self.token(0)): + break + # Support expressions like: some x y.M == some x.some y.M + vars.append(self.get_next_token_variable("quantified")) + if self.inRange(0) and self.token(0) == Tokens.DOT: + self.token() # swallow the dot + + accum = self.process_next_expression(tok) + while vars: + accum = self.make_QuanifiedExpression(factory, vars.pop(), accum) + return accum + + def get_QuantifiedExpression_factory(self, tok): + """This method serves as a hook for other logic parsers that + have different quantifiers""" + if tok in Tokens.EXISTS_LIST: + return ExistsExpression + elif tok in Tokens.ALL_LIST: + return AllExpression + elif tok in Tokens.IOTA_LIST: + return IotaExpression + else: + self.assertToken(tok, Tokens.QUANTS) + + def make_QuanifiedExpression(self, factory, variable, term): + return factory(variable, term) + + def handle_open(self, tok, context): + # Expression is in parens + accum = self.process_next_expression(None) + self.assertNextToken(Tokens.CLOSE) + return accum + + def attempt_EqualityExpression(self, expression, context): + """Attempt to make an equality expression. If the next token is an + equality operator, then an EqualityExpression will be returned. + Otherwise, the parameter will be returned.""" + if self.inRange(0): + tok = self.token(0) + if tok in Tokens.EQ_LIST + Tokens.NEQ_LIST and self.has_priority( + tok, context + ): + self.token() # swallow the "=" or "!=" + expression = self.make_EqualityExpression( + expression, self.process_next_expression(tok) + ) + if tok in Tokens.NEQ_LIST: + expression = self.make_NegatedExpression(expression) + return expression + + def make_EqualityExpression(self, first, second): + """This method serves as a hook for other logic parsers that + have different equality expression classes""" + return EqualityExpression(first, second) + + def attempt_BooleanExpression(self, expression, context): + """Attempt to make a boolean expression. If the next token is a boolean + operator, then a BooleanExpression will be returned. Otherwise, the + parameter will be returned.""" + while self.inRange(0): + tok = self.token(0) + factory = self.get_BooleanExpression_factory(tok) + if factory and self.has_priority(tok, context): + self.token() # swallow the operator + expression = self.make_BooleanExpression( + factory, expression, self.process_next_expression(tok) + ) + else: + break + return expression + + def get_BooleanExpression_factory(self, tok): + """This method serves as a hook for other logic parsers that + have different boolean operators""" + if tok in Tokens.AND_LIST: + return AndExpression + elif tok in Tokens.OR_LIST: + return OrExpression + elif tok in Tokens.IMP_LIST: + return ImpExpression + elif tok in Tokens.IFF_LIST: + return IffExpression + else: + return None + + def make_BooleanExpression(self, factory, first, second): + return factory(first, second) + + def attempt_ApplicationExpression(self, expression, context): + """Attempt to make an application expression. The next tokens are + a list of arguments in parens, then the argument expression is a + function being applied to the arguments. Otherwise, return the + argument expression.""" + if self.has_priority(APP, context): + if self.inRange(0) and self.token(0) == Tokens.OPEN: + if ( + not isinstance(expression, LambdaExpression) + and not isinstance(expression, ApplicationExpression) + and not isinstance(expression, FunctionVariableExpression) + and not isinstance(expression, ConstantExpression) + ): + raise LogicalExpressionException( + self._currentIndex, + ("The function '%s" % expression) + + "' is not a Lambda Expression, an " + "Application Expression, or a " + "functional predicate, so it may " + "not take arguments.", + ) + self.token() # swallow then open paren + # curry the arguments + accum = self.make_ApplicationExpression( + expression, self.process_next_expression(APP) + ) + while self.inRange(0) and self.token(0) == Tokens.COMMA: + self.token() # swallow the comma + accum = self.make_ApplicationExpression( + accum, self.process_next_expression(APP) + ) + self.assertNextToken(Tokens.CLOSE) + return accum + return expression + + def make_ApplicationExpression(self, function, argument): + return ApplicationExpression(function, argument) + + def make_VariableExpression(self, name): + return VariableExpression(Variable(name)) + + def make_LambdaExpression(self, variable, term): + return LambdaExpression(variable, term) + + def has_priority(self, operation, context): + return self.operator_precedence[operation] < self.operator_precedence[ + context + ] or ( + operation in self.right_associated_operations + and self.operator_precedence[operation] == self.operator_precedence[context] + ) + + def assertNextToken(self, expected): + try: + tok = self.token() + except ExpectedMoreTokensException as e: + raise ExpectedMoreTokensException( + e.index, message="Expected token '%s'." % expected + ) from e + + if isinstance(expected, list): + if tok not in expected: + raise UnexpectedTokenException(self._currentIndex, tok, expected) + else: + if tok != expected: + raise UnexpectedTokenException(self._currentIndex, tok, expected) + + def assertToken(self, tok, expected): + if isinstance(expected, list): + if tok not in expected: + raise UnexpectedTokenException(self._currentIndex, tok, expected) + else: + if tok != expected: + raise UnexpectedTokenException(self._currentIndex, tok, expected) + + def __repr__(self): + if self.inRange(0): + msg = "Next token: " + self.token(0) + else: + msg = "No more tokens" + return "<" + self.__class__.__name__ + ": " + msg + ">" + + +def read_logic(s, logic_parser=None, encoding=None): + """ + Convert a file of First Order Formulas into a list of {Expression}s. + + :param s: the contents of the file + :type s: str + :param logic_parser: The parser to be used to parse the logical expression + :type logic_parser: LogicParser + :param encoding: the encoding of the input string, if it is binary + :type encoding: str + :return: a list of parsed formulas. + :rtype: list(Expression) + """ + if encoding is not None: + s = s.decode(encoding) + if logic_parser is None: + logic_parser = LogicParser() + + statements = [] + for linenum, line in enumerate(s.splitlines()): + line = line.strip() + if line.startswith("#") or line == "": + continue + try: + statements.append(logic_parser.parse(line)) + except LogicalExpressionException as e: + raise ValueError(f"Unable to parse line {linenum}: {line}") from e + return statements + + +@total_ordering +class Variable: + def __init__(self, name): + """ + :param name: the name of the variable + """ + assert isinstance(name, str), "%s is not a string" % name + self.name = name + + def __eq__(self, other): + return isinstance(other, Variable) and self.name == other.name + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, Variable): + raise TypeError + return self.name < other.name + + def substitute_bindings(self, bindings): + return bindings.get(self, self) + + def __hash__(self): + return hash(self.name) + + def __str__(self): + return self.name + + def __repr__(self): + return "Variable('%s')" % self.name + + +def unique_variable(pattern=None, ignore=None): + """ + Return a new, unique variable. + + :param pattern: ``Variable`` that is being replaced. The new variable must + be the same type. + :param term: a set of ``Variable`` objects that should not be returned from + this function. + :rtype: Variable + """ + if pattern is not None: + if is_indvar(pattern.name): + prefix = "z" + elif is_funcvar(pattern.name): + prefix = "F" + elif is_eventvar(pattern.name): + prefix = "e0" + else: + assert False, "Cannot generate a unique constant" + else: + prefix = "z" + + v = Variable(f"{prefix}{_counter.get()}") + while ignore is not None and v in ignore: + v = Variable(f"{prefix}{_counter.get()}") + return v + + +def skolem_function(univ_scope=None): + """ + Return a skolem function over the variables in univ_scope + param univ_scope + """ + skolem = VariableExpression(Variable("F%s" % _counter.get())) + if univ_scope: + for v in list(univ_scope): + skolem = skolem(VariableExpression(v)) + return skolem + + +class Type: + def __repr__(self): + return "%s" % self + + def __hash__(self): + return hash("%s" % self) + + @classmethod + def fromstring(cls, s): + return read_type(s) + + +class ComplexType(Type): + def __init__(self, first, second): + assert isinstance(first, Type), "%s is not a Type" % first + assert isinstance(second, Type), "%s is not a Type" % second + self.first = first + self.second = second + + def __eq__(self, other): + return ( + isinstance(other, ComplexType) + and self.first == other.first + and self.second == other.second + ) + + def __ne__(self, other): + return not self == other + + __hash__ = Type.__hash__ + + def matches(self, other): + if isinstance(other, ComplexType): + return self.first.matches(other.first) and self.second.matches(other.second) + else: + return self == ANY_TYPE + + def resolve(self, other): + if other == ANY_TYPE: + return self + elif isinstance(other, ComplexType): + f = self.first.resolve(other.first) + s = self.second.resolve(other.second) + if f and s: + return ComplexType(f, s) + else: + return None + elif self == ANY_TYPE: + return other + else: + return None + + def __str__(self): + if self == ANY_TYPE: + return "%s" % ANY_TYPE + else: + return f"<{self.first},{self.second}>" + + def str(self): + if self == ANY_TYPE: + return ANY_TYPE.str() + else: + return f"({self.first.str()} -> {self.second.str()})" + + +class BasicType(Type): + def __eq__(self, other): + return isinstance(other, BasicType) and ("%s" % self) == ("%s" % other) + + def __ne__(self, other): + return not self == other + + __hash__ = Type.__hash__ + + def matches(self, other): + return other == ANY_TYPE or self == other + + def resolve(self, other): + if self.matches(other): + return self + else: + return None + + +class EntityType(BasicType): + def __str__(self): + return "e" + + def str(self): + return "IND" + + +class TruthValueType(BasicType): + def __str__(self): + return "t" + + def str(self): + return "BOOL" + + +class EventType(BasicType): + def __str__(self): + return "v" + + def str(self): + return "EVENT" + + +class AnyType(BasicType, ComplexType): + def __init__(self): + pass + + @property + def first(self): + return self + + @property + def second(self): + return self + + def __eq__(self, other): + return isinstance(other, AnyType) or other.__eq__(self) + + def __ne__(self, other): + return not self == other + + __hash__ = Type.__hash__ + + def matches(self, other): + return True + + def resolve(self, other): + return other + + def __str__(self): + return "?" + + def str(self): + return "ANY" + + +TRUTH_TYPE = TruthValueType() +ENTITY_TYPE = EntityType() +EVENT_TYPE = EventType() +ANY_TYPE = AnyType() + + +def read_type(type_string): + assert isinstance(type_string, str) + type_string = type_string.replace(" ", "") # remove spaces + + if type_string[0] == "<": + assert type_string[-1] == ">" + paren_count = 0 + for i, char in enumerate(type_string): + if char == "<": + paren_count += 1 + elif char == ">": + paren_count -= 1 + assert paren_count > 0 + elif char == ",": + if paren_count == 1: + break + return ComplexType( + read_type(type_string[1:i]), read_type(type_string[i + 1 : -1]) + ) + elif type_string[0] == "%s" % ENTITY_TYPE: + return ENTITY_TYPE + elif type_string[0] == "%s" % TRUTH_TYPE: + return TRUTH_TYPE + elif type_string[0] == "%s" % ANY_TYPE: + return ANY_TYPE + else: + raise LogicalExpressionException( + None, "Unexpected character: '%s'." % type_string[0] + ) + + +class TypeException(Exception): + def __init__(self, msg): + super().__init__(msg) + + +class InconsistentTypeHierarchyException(TypeException): + def __init__(self, variable, expression=None): + if expression: + msg = ( + "The variable '%s' was found in multiple places with different" + " types in '%s'." % (variable, expression) + ) + else: + msg = ( + "The variable '%s' was found in multiple places with different" + " types." % (variable) + ) + super().__init__(msg) + + +class TypeResolutionException(TypeException): + def __init__(self, expression, other_type): + super().__init__( + "The type of '%s', '%s', cannot be resolved with type '%s'" + % (expression, expression.type, other_type) + ) + + +class IllegalTypeException(TypeException): + def __init__(self, expression, other_type, allowed_type): + super().__init__( + "Cannot set type of %s '%s' to '%s'; must match type '%s'." + % (expression.__class__.__name__, expression, other_type, allowed_type) + ) + + +def typecheck(expressions, signature=None): + """ + Ensure correct typing across a collection of ``Expression`` objects. + :param expressions: a collection of expressions + :param signature: dict that maps variable names to types (or string + representations of types) + """ + # typecheck and create master signature + for expression in expressions: + signature = expression.typecheck(signature) + # apply master signature to all expressions + for expression in expressions[:-1]: + expression.typecheck(signature) + return signature + + +class SubstituteBindingsI: + """ + An interface for classes that can perform substitutions for + variables. + """ + + def substitute_bindings(self, bindings): + """ + :return: The object that is obtained by replacing + each variable bound by ``bindings`` with its values. + Aliases are already resolved. (maybe?) + :rtype: (any) + """ + raise NotImplementedError() + + def variables(self): + """ + :return: A list of all variables in this object. + """ + raise NotImplementedError() + + +class Expression(SubstituteBindingsI): + """This is the base abstract object for all logical expressions""" + + _logic_parser = LogicParser() + _type_checking_logic_parser = LogicParser(type_check=True) + + @classmethod + def fromstring(cls, s, type_check=False, signature=None): + if type_check: + return cls._type_checking_logic_parser.parse(s, signature) + else: + return cls._logic_parser.parse(s, signature) + + def __call__(self, other, *additional): + accum = self.applyto(other) + for a in additional: + accum = accum(a) + return accum + + def applyto(self, other): + assert isinstance(other, Expression), "%s is not an Expression" % other + return ApplicationExpression(self, other) + + def __neg__(self): + return NegatedExpression(self) + + def negate(self): + """If this is a negated expression, remove the negation. + Otherwise add a negation.""" + return -self + + def __and__(self, other): + if not isinstance(other, Expression): + raise TypeError("%s is not an Expression" % other) + return AndExpression(self, other) + + def __or__(self, other): + if not isinstance(other, Expression): + raise TypeError("%s is not an Expression" % other) + return OrExpression(self, other) + + def __gt__(self, other): + if not isinstance(other, Expression): + raise TypeError("%s is not an Expression" % other) + return ImpExpression(self, other) + + def __lt__(self, other): + if not isinstance(other, Expression): + raise TypeError("%s is not an Expression" % other) + return IffExpression(self, other) + + def __eq__(self, other): + return NotImplemented + + def __ne__(self, other): + return not self == other + + def equiv(self, other, prover=None): + """ + Check for logical equivalence. + Pass the expression (self <-> other) to the theorem prover. + If the prover says it is valid, then the self and other are equal. + + :param other: an ``Expression`` to check equality against + :param prover: a ``nltk.inference.api.Prover`` + """ + assert isinstance(other, Expression), "%s is not an Expression" % other + + if prover is None: + from nltk.inference import Prover9 + + prover = Prover9() + bicond = IffExpression(self.simplify(), other.simplify()) + return prover.prove(bicond) + + def __hash__(self): + return hash(repr(self)) + + def substitute_bindings(self, bindings): + expr = self + for var in expr.variables(): + if var in bindings: + val = bindings[var] + if isinstance(val, Variable): + val = self.make_VariableExpression(val) + elif not isinstance(val, Expression): + raise ValueError( + "Can not substitute a non-expression " + "value into an expression: %r" % (val,) + ) + # Substitute bindings in the target value. + val = val.substitute_bindings(bindings) + # Replace var w/ the target value. + expr = expr.replace(var, val) + return expr.simplify() + + def typecheck(self, signature=None): + """ + Infer and check types. Raise exceptions if necessary. + + :param signature: dict that maps variable names to types (or string + representations of types) + :return: the signature, plus any additional type mappings + """ + sig = defaultdict(list) + if signature: + for key in signature: + val = signature[key] + varEx = VariableExpression(Variable(key)) + if isinstance(val, Type): + varEx.type = val + else: + varEx.type = read_type(val) + sig[key].append(varEx) + + self._set_type(signature=sig) + + return {key: sig[key][0].type for key in sig} + + def findtype(self, variable): + """ + Find the type of the given variable as it is used in this expression. + For example, finding the type of "P" in "P(x) & Q(x,y)" yields "" + + :param variable: Variable + """ + raise NotImplementedError() + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """ + Set the type of this expression to be the given type. Raise type + exceptions where applicable. + + :param other_type: Type + :param signature: dict(str -> list(AbstractVariableExpression)) + """ + raise NotImplementedError() + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """ + Replace every instance of 'variable' with 'expression' + :param variable: ``Variable`` The variable to replace + :param expression: ``Expression`` The expression with which to replace it + :param replace_bound: bool Should bound variables be replaced? + :param alpha_convert: bool Alpha convert automatically to avoid name clashes? + """ + assert isinstance(variable, Variable), "%s is not a Variable" % variable + assert isinstance(expression, Expression), ( + "%s is not an Expression" % expression + ) + + return self.visit_structured( + lambda e: e.replace(variable, expression, replace_bound, alpha_convert), + self.__class__, + ) + + def normalize(self, newvars=None): + """Rename auto-generated unique variables""" + + def get_indiv_vars(e): + if isinstance(e, IndividualVariableExpression): + return {e} + elif isinstance(e, AbstractVariableExpression): + return set() + else: + return e.visit( + get_indiv_vars, lambda parts: reduce(operator.or_, parts, set()) + ) + + result = self + for i, e in enumerate(sorted(get_indiv_vars(self), key=lambda e: e.variable)): + if isinstance(e, EventVariableExpression): + newVar = e.__class__(Variable("e0%s" % (i + 1))) + elif isinstance(e, IndividualVariableExpression): + newVar = e.__class__(Variable("z%s" % (i + 1))) + else: + newVar = e + result = result.replace(e.variable, newVar, True) + return result + + def visit(self, function, combinator): + """ + Recursively visit subexpressions. Apply 'function' to each + subexpression and pass the result of each function application + to the 'combinator' for aggregation: + + return combinator(map(function, self.subexpressions)) + + Bound variables are neither applied upon by the function nor given to + the combinator. + :param function: ``Function`` to call on each subexpression + :param combinator: ``Function,R>`` to combine the results of the + function calls + :return: result of combination ``R`` + """ + raise NotImplementedError() + + def visit_structured(self, function, combinator): + """ + Recursively visit subexpressions. Apply 'function' to each + subexpression and pass the result of each function application + to the 'combinator' for aggregation. The combinator must have + the same signature as the constructor. The function is not + applied to bound variables, but they are passed to the + combinator. + :param function: ``Function`` to call on each subexpression + :param combinator: ``Function`` with the same signature as the + constructor, to combine the results of the function calls + :return: result of combination + """ + return self.visit(function, lambda parts: combinator(*parts)) + + def __repr__(self): + return f"<{self.__class__.__name__} {self}>" + + def __str__(self): + return self.str() + + def variables(self): + """ + Return a set of all the variables for binding substitution. + The variables returned include all free (non-bound) individual + variables and any variable starting with '?' or '@'. + :return: set of ``Variable`` objects + """ + return self.free() | { + p for p in self.predicates() | self.constants() if re.match("^[?@]", p.name) + } + + def free(self): + """ + Return a set of all the free (non-bound) variables. This includes + both individual and predicate variables, but not constants. + :return: set of ``Variable`` objects + """ + return self.visit( + lambda e: e.free(), lambda parts: reduce(operator.or_, parts, set()) + ) + + def constants(self): + """ + Return a set of individual constants (non-predicates). + :return: set of ``Variable`` objects + """ + return self.visit( + lambda e: e.constants(), lambda parts: reduce(operator.or_, parts, set()) + ) + + def predicates(self): + """ + Return a set of predicates (constants, not variables). + :return: set of ``Variable`` objects + """ + return self.visit( + lambda e: e.predicates(), lambda parts: reduce(operator.or_, parts, set()) + ) + + def simplify(self): + """ + :return: beta-converted version of this expression + """ + return self.visit_structured(lambda e: e.simplify(), self.__class__) + + def make_VariableExpression(self, variable): + return VariableExpression(variable) + + +class ApplicationExpression(Expression): + r""" + This class is used to represent two related types of logical expressions. + + The first is a Predicate Expression, such as "P(x,y)". A predicate + expression is comprised of a ``FunctionVariableExpression`` or + ``ConstantExpression`` as the predicate and a list of Expressions as the + arguments. + + The second is a an application of one expression to another, such as + "(\x.dog(x))(fido)". + + The reason Predicate Expressions are treated as Application Expressions is + that the Variable Expression predicate of the expression may be replaced + with another Expression, such as a LambdaExpression, which would mean that + the Predicate should be thought of as being applied to the arguments. + + The logical expression reader will always curry arguments in a application expression. + So, "\x y.see(x,y)(john,mary)" will be represented internally as + "((\x y.(see(x))(y))(john))(mary)". This simplifies the internals since + there will always be exactly one argument in an application. + + The str() method will usually print the curried forms of application + expressions. The one exception is when the the application expression is + really a predicate expression (ie, underlying function is an + ``AbstractVariableExpression``). This means that the example from above + will be returned as "(\x y.see(x,y)(john))(mary)". + """ + + def __init__(self, function, argument): + """ + :param function: ``Expression``, for the function expression + :param argument: ``Expression``, for the argument + """ + assert isinstance(function, Expression), "%s is not an Expression" % function + assert isinstance(argument, Expression), "%s is not an Expression" % argument + self.function = function + self.argument = argument + + def simplify(self): + function = self.function.simplify() + argument = self.argument.simplify() + if isinstance(function, LambdaExpression): + return function.term.replace(function.variable, argument).simplify() + else: + return self.__class__(function, argument) + + @property + def type(self): + if isinstance(self.function.type, ComplexType): + return self.function.type.second + else: + return ANY_TYPE + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + self.argument._set_type(ANY_TYPE, signature) + try: + self.function._set_type( + ComplexType(self.argument.type, other_type), signature + ) + except TypeResolutionException as e: + raise TypeException( + "The function '%s' is of type '%s' and cannot be applied " + "to '%s' of type '%s'. Its argument must match type '%s'." + % ( + self.function, + self.function.type, + self.argument, + self.argument.type, + self.function.type.first, + ) + ) from e + + def findtype(self, variable): + """:see Expression.findtype()""" + assert isinstance(variable, Variable), "%s is not a Variable" % variable + if self.is_atom(): + function, args = self.uncurry() + else: + # It's not a predicate expression ("P(x,y)"), so leave args curried + function = self.function + args = [self.argument] + + found = [arg.findtype(variable) for arg in [function] + args] + + unique = [] + for f in found: + if f != ANY_TYPE: + if unique: + for u in unique: + if f.matches(u): + break + else: + unique.append(f) + + if len(unique) == 1: + return list(unique)[0] + else: + return ANY_TYPE + + def constants(self): + """:see: Expression.constants()""" + if isinstance(self.function, AbstractVariableExpression): + function_constants = set() + else: + function_constants = self.function.constants() + return function_constants | self.argument.constants() + + def predicates(self): + """:see: Expression.predicates()""" + if isinstance(self.function, ConstantExpression): + function_preds = {self.function.variable} + else: + function_preds = self.function.predicates() + return function_preds | self.argument.predicates() + + def visit(self, function, combinator): + """:see: Expression.visit()""" + return combinator([function(self.function), function(self.argument)]) + + def __eq__(self, other): + return ( + isinstance(other, ApplicationExpression) + and self.function == other.function + and self.argument == other.argument + ) + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + def __str__(self): + # uncurry the arguments and find the base function + if self.is_atom(): + function, args = self.uncurry() + arg_str = ",".join("%s" % arg for arg in args) + else: + # Leave arguments curried + function = self.function + arg_str = "%s" % self.argument + + function_str = "%s" % function + parenthesize_function = False + if isinstance(function, LambdaExpression): + if isinstance(function.term, ApplicationExpression): + if not isinstance(function.term.function, AbstractVariableExpression): + parenthesize_function = True + elif not isinstance(function.term, BooleanExpression): + parenthesize_function = True + elif isinstance(function, ApplicationExpression): + parenthesize_function = True + + if parenthesize_function: + function_str = Tokens.OPEN + function_str + Tokens.CLOSE + + return function_str + Tokens.OPEN + arg_str + Tokens.CLOSE + + def uncurry(self): + """ + Uncurry this application expression + + return: A tuple (base-function, arg-list) + """ + function = self.function + args = [self.argument] + while isinstance(function, ApplicationExpression): + # (\x.\y.sees(x,y)(john))(mary) + args.insert(0, function.argument) + function = function.function + return (function, args) + + @property + def pred(self): + """ + Return uncurried base-function. + If this is an atom, then the result will be a variable expression. + Otherwise, it will be a lambda expression. + """ + return self.uncurry()[0] + + @property + def args(self): + """ + Return uncurried arg-list + """ + return self.uncurry()[1] + + def is_atom(self): + """ + Is this expression an atom (as opposed to a lambda expression applied + to a term)? + """ + return isinstance(self.pred, AbstractVariableExpression) + + +@total_ordering +class AbstractVariableExpression(Expression): + """This class represents a variable to be used as a predicate or entity""" + + def __init__(self, variable): + """ + :param variable: ``Variable``, for the variable + """ + assert isinstance(variable, Variable), "%s is not a Variable" % variable + self.variable = variable + + def simplify(self): + return self + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """:see: Expression.replace()""" + assert isinstance(variable, Variable), "%s is not an Variable" % variable + assert isinstance(expression, Expression), ( + "%s is not an Expression" % expression + ) + if self.variable == variable: + return expression + else: + return self + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + resolution = other_type + for varEx in signature[self.variable.name]: + resolution = varEx.type.resolve(resolution) + if not resolution: + raise InconsistentTypeHierarchyException(self) + + signature[self.variable.name].append(self) + for varEx in signature[self.variable.name]: + varEx.type = resolution + + def findtype(self, variable): + """:see Expression.findtype()""" + assert isinstance(variable, Variable), "%s is not a Variable" % variable + if self.variable == variable: + return self.type + else: + return ANY_TYPE + + def predicates(self): + """:see: Expression.predicates()""" + return set() + + def __eq__(self, other): + """Allow equality between instances of ``AbstractVariableExpression`` + subtypes.""" + return ( + isinstance(other, AbstractVariableExpression) + and self.variable == other.variable + ) + + def __ne__(self, other): + return not self == other + + def __lt__(self, other): + if not isinstance(other, AbstractVariableExpression): + raise TypeError + return self.variable < other.variable + + __hash__ = Expression.__hash__ + + def __str__(self): + return "%s" % self.variable + + +class IndividualVariableExpression(AbstractVariableExpression): + """This class represents variables that take the form of a single lowercase + character (other than 'e') followed by zero or more digits.""" + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if not other_type.matches(ENTITY_TYPE): + raise IllegalTypeException(self, other_type, ENTITY_TYPE) + + signature[self.variable.name].append(self) + + def _get_type(self): + return ENTITY_TYPE + + type = property(_get_type, _set_type) + + def free(self): + """:see: Expression.free()""" + return {self.variable} + + def constants(self): + """:see: Expression.constants()""" + return set() + + +class FunctionVariableExpression(AbstractVariableExpression): + """This class represents variables that take the form of a single uppercase + character followed by zero or more digits.""" + + type = ANY_TYPE + + def free(self): + """:see: Expression.free()""" + return {self.variable} + + def constants(self): + """:see: Expression.constants()""" + return set() + + +class EventVariableExpression(IndividualVariableExpression): + """This class represents variables that take the form of a single lowercase + 'e' character followed by zero or more digits.""" + + type = EVENT_TYPE + + +class ConstantExpression(AbstractVariableExpression): + """This class represents variables that do not take the form of a single + character followed by zero or more digits.""" + + type = ENTITY_TYPE + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if other_type == ANY_TYPE: + # entity type by default, for individuals + resolution = ENTITY_TYPE + else: + resolution = other_type + if self.type != ENTITY_TYPE: + resolution = resolution.resolve(self.type) + + for varEx in signature[self.variable.name]: + resolution = varEx.type.resolve(resolution) + if not resolution: + raise InconsistentTypeHierarchyException(self) + + signature[self.variable.name].append(self) + for varEx in signature[self.variable.name]: + varEx.type = resolution + + def free(self): + """:see: Expression.free()""" + return set() + + def constants(self): + """:see: Expression.constants()""" + return {self.variable} + + +def VariableExpression(variable): + """ + This is a factory method that instantiates and returns a subtype of + ``AbstractVariableExpression`` appropriate for the given variable. + """ + assert isinstance(variable, Variable), "%s is not a Variable" % variable + if is_indvar(variable.name): + return IndividualVariableExpression(variable) + elif is_funcvar(variable.name): + return FunctionVariableExpression(variable) + elif is_eventvar(variable.name): + return EventVariableExpression(variable) + else: + return ConstantExpression(variable) + + +class VariableBinderExpression(Expression): + """This an abstract class for any Expression that binds a variable in an + Expression. This includes LambdaExpressions and Quantified Expressions""" + + def __init__(self, variable, term): + """ + :param variable: ``Variable``, for the variable + :param term: ``Expression``, for the term + """ + assert isinstance(variable, Variable), "%s is not a Variable" % variable + assert isinstance(term, Expression), "%s is not an Expression" % term + self.variable = variable + self.term = term + + def replace(self, variable, expression, replace_bound=False, alpha_convert=True): + """:see: Expression.replace()""" + assert isinstance(variable, Variable), "%s is not a Variable" % variable + assert isinstance(expression, Expression), ( + "%s is not an Expression" % expression + ) + # if the bound variable is the thing being replaced + if self.variable == variable: + if replace_bound: + assert isinstance(expression, AbstractVariableExpression), ( + "%s is not a AbstractVariableExpression" % expression + ) + return self.__class__( + expression.variable, + self.term.replace(variable, expression, True, alpha_convert), + ) + else: + return self + else: + # if the bound variable appears in the expression, then it must + # be alpha converted to avoid a conflict + if alpha_convert and self.variable in expression.free(): + self = self.alpha_convert(unique_variable(pattern=self.variable)) + + # replace in the term + return self.__class__( + self.variable, + self.term.replace(variable, expression, replace_bound, alpha_convert), + ) + + def alpha_convert(self, newvar): + """Rename all occurrences of the variable introduced by this variable + binder in the expression to ``newvar``. + :param newvar: ``Variable``, for the new variable + """ + assert isinstance(newvar, Variable), "%s is not a Variable" % newvar + return self.__class__( + newvar, self.term.replace(self.variable, VariableExpression(newvar), True) + ) + + def free(self): + """:see: Expression.free()""" + return self.term.free() - {self.variable} + + def findtype(self, variable): + """:see Expression.findtype()""" + assert isinstance(variable, Variable), "%s is not a Variable" % variable + if variable == self.variable: + return ANY_TYPE + else: + return self.term.findtype(variable) + + def visit(self, function, combinator): + """:see: Expression.visit()""" + return combinator([function(self.term)]) + + def visit_structured(self, function, combinator): + """:see: Expression.visit_structured()""" + return combinator(self.variable, function(self.term)) + + def __eq__(self, other): + r"""Defines equality modulo alphabetic variance. If we are comparing + \x.M and \y.N, then check equality of M and N[x/y].""" + if isinstance(self, other.__class__) or isinstance(other, self.__class__): + if self.variable == other.variable: + return self.term == other.term + else: + # Comparing \x.M and \y.N. Relabel y in N with x and continue. + varex = VariableExpression(self.variable) + return self.term == other.term.replace(other.variable, varex) + else: + return False + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + +class LambdaExpression(VariableBinderExpression): + @property + def type(self): + return ComplexType(self.term.findtype(self.variable), self.term.type) + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + self.term._set_type(other_type.second, signature) + if not self.type.resolve(other_type): + raise TypeResolutionException(self, other_type) + + def __str__(self): + variables = [self.variable] + term = self.term + while term.__class__ == self.__class__: + variables.append(term.variable) + term = term.term + return ( + Tokens.LAMBDA + + " ".join("%s" % v for v in variables) + + Tokens.DOT + + "%s" % term + ) + + +class QuantifiedExpression(VariableBinderExpression): + @property + def type(self): + return TRUTH_TYPE + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if not other_type.matches(TRUTH_TYPE): + raise IllegalTypeException(self, other_type, TRUTH_TYPE) + self.term._set_type(TRUTH_TYPE, signature) + + def __str__(self): + variables = [self.variable] + term = self.term + while term.__class__ == self.__class__: + variables.append(term.variable) + term = term.term + return ( + self.getQuantifier() + + " " + + " ".join("%s" % v for v in variables) + + Tokens.DOT + + "%s" % term + ) + + +class ExistsExpression(QuantifiedExpression): + def getQuantifier(self): + return Tokens.EXISTS + + +class AllExpression(QuantifiedExpression): + def getQuantifier(self): + return Tokens.ALL + + +class IotaExpression(QuantifiedExpression): + def getQuantifier(self): + return Tokens.IOTA + + +class NegatedExpression(Expression): + def __init__(self, term): + assert isinstance(term, Expression), "%s is not an Expression" % term + self.term = term + + @property + def type(self): + return TRUTH_TYPE + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if not other_type.matches(TRUTH_TYPE): + raise IllegalTypeException(self, other_type, TRUTH_TYPE) + self.term._set_type(TRUTH_TYPE, signature) + + def findtype(self, variable): + assert isinstance(variable, Variable), "%s is not a Variable" % variable + return self.term.findtype(variable) + + def visit(self, function, combinator): + """:see: Expression.visit()""" + return combinator([function(self.term)]) + + def negate(self): + """:see: Expression.negate()""" + return self.term + + def __eq__(self, other): + return isinstance(other, NegatedExpression) and self.term == other.term + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + def __str__(self): + return Tokens.NOT + "%s" % self.term + + +class BinaryExpression(Expression): + def __init__(self, first, second): + assert isinstance(first, Expression), "%s is not an Expression" % first + assert isinstance(second, Expression), "%s is not an Expression" % second + self.first = first + self.second = second + + @property + def type(self): + return TRUTH_TYPE + + def findtype(self, variable): + """:see Expression.findtype()""" + assert isinstance(variable, Variable), "%s is not a Variable" % variable + f = self.first.findtype(variable) + s = self.second.findtype(variable) + if f == s or s == ANY_TYPE: + return f + elif f == ANY_TYPE: + return s + else: + return ANY_TYPE + + def visit(self, function, combinator): + """:see: Expression.visit()""" + return combinator([function(self.first), function(self.second)]) + + def __eq__(self, other): + return ( + (isinstance(self, other.__class__) or isinstance(other, self.__class__)) + and self.first == other.first + and self.second == other.second + ) + + def __ne__(self, other): + return not self == other + + __hash__ = Expression.__hash__ + + def __str__(self): + first = self._str_subex(self.first) + second = self._str_subex(self.second) + return Tokens.OPEN + first + " " + self.getOp() + " " + second + Tokens.CLOSE + + def _str_subex(self, subex): + return "%s" % subex + + +class BooleanExpression(BinaryExpression): + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if not other_type.matches(TRUTH_TYPE): + raise IllegalTypeException(self, other_type, TRUTH_TYPE) + self.first._set_type(TRUTH_TYPE, signature) + self.second._set_type(TRUTH_TYPE, signature) + + +class AndExpression(BooleanExpression): + """This class represents conjunctions""" + + def getOp(self): + return Tokens.AND + + def _str_subex(self, subex): + s = "%s" % subex + if isinstance(subex, AndExpression): + return s[1:-1] + return s + + +class OrExpression(BooleanExpression): + """This class represents disjunctions""" + + def getOp(self): + return Tokens.OR + + def _str_subex(self, subex): + s = "%s" % subex + if isinstance(subex, OrExpression): + return s[1:-1] + return s + + +class ImpExpression(BooleanExpression): + """This class represents implications""" + + def getOp(self): + return Tokens.IMP + + +class IffExpression(BooleanExpression): + """This class represents biconditionals""" + + def getOp(self): + return Tokens.IFF + + +class EqualityExpression(BinaryExpression): + """This class represents equality expressions like "(x = y)".""" + + def _set_type(self, other_type=ANY_TYPE, signature=None): + """:see Expression._set_type()""" + assert isinstance(other_type, Type) + + if signature is None: + signature = defaultdict(list) + + if not other_type.matches(TRUTH_TYPE): + raise IllegalTypeException(self, other_type, TRUTH_TYPE) + self.first._set_type(ENTITY_TYPE, signature) + self.second._set_type(ENTITY_TYPE, signature) + + def getOp(self): + return Tokens.EQ + + +### Utilities + + +class LogicalExpressionException(Exception): + def __init__(self, index, message): + self.index = index + Exception.__init__(self, message) + + +class UnexpectedTokenException(LogicalExpressionException): + def __init__(self, index, unexpected=None, expected=None, message=None): + if unexpected and expected: + msg = "Unexpected token: '%s'. " "Expected token '%s'." % ( + unexpected, + expected, + ) + elif unexpected: + msg = "Unexpected token: '%s'." % unexpected + if message: + msg += " " + message + else: + msg = "Expected token '%s'." % expected + LogicalExpressionException.__init__(self, index, msg) + + +class ExpectedMoreTokensException(LogicalExpressionException): + def __init__(self, index, message=None): + if not message: + message = "More tokens expected." + LogicalExpressionException.__init__( + self, index, "End of input found. " + message + ) + + +def is_indvar(expr): + """ + An individual variable must be a single lowercase character other than 'e', + followed by zero or more digits. + + :param expr: str + :return: bool True if expr is of the correct form + """ + assert isinstance(expr, str), "%s is not a string" % expr + return re.match(r"^[a-df-z]\d*$", expr) is not None + + +def is_funcvar(expr): + """ + A function variable must be a single uppercase character followed by + zero or more digits. + + :param expr: str + :return: bool True if expr is of the correct form + """ + assert isinstance(expr, str), "%s is not a string" % expr + return re.match(r"^[A-Z]\d*$", expr) is not None + + +def is_eventvar(expr): + """ + An event variable must be a single lowercase 'e' character followed by + zero or more digits. + + :param expr: str + :return: bool True if expr is of the correct form + """ + assert isinstance(expr, str), "%s is not a string" % expr + return re.match(r"^e\d*$", expr) is not None + + +def demo(): + lexpr = Expression.fromstring + print("=" * 20 + "Test reader" + "=" * 20) + print(lexpr(r"john")) + print(lexpr(r"man(x)")) + print(lexpr(r"-man(x)")) + print(lexpr(r"(man(x) & tall(x) & walks(x))")) + print(lexpr(r"exists x.(man(x) & tall(x) & walks(x))")) + print(lexpr(r"\x.man(x)")) + print(lexpr(r"\x.man(x)(john)")) + print(lexpr(r"\x y.sees(x,y)")) + print(lexpr(r"\x y.sees(x,y)(a,b)")) + print(lexpr(r"(\x.exists y.walks(x,y))(x)")) + print(lexpr(r"exists x.x = y")) + print(lexpr(r"exists x.(x = y)")) + print(lexpr("P(x) & x=y & P(y)")) + print(lexpr(r"\P Q.exists x.(P(x) & Q(x))")) + print(lexpr(r"man(x) <-> tall(x)")) + + print("=" * 20 + "Test simplify" + "=" * 20) + print(lexpr(r"\x.\y.sees(x,y)(john)(mary)").simplify()) + print(lexpr(r"\x.\y.sees(x,y)(john, mary)").simplify()) + print(lexpr(r"all x.(man(x) & (\x.exists y.walks(x,y))(x))").simplify()) + print(lexpr(r"(\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x))(\x.bark(x))").simplify()) + + print("=" * 20 + "Test alpha conversion and binder expression equality" + "=" * 20) + e1 = lexpr("exists x.P(x)") + print(e1) + e2 = e1.alpha_convert(Variable("z")) + print(e2) + print(e1 == e2) + + +def demo_errors(): + print("=" * 20 + "Test reader errors" + "=" * 20) + demoException("(P(x) & Q(x)") + demoException("((P(x) &) & Q(x))") + demoException("P(x) -> ") + demoException("P(x") + demoException("P(x,") + demoException("P(x,)") + demoException("exists") + demoException("exists x.") + demoException("\\") + demoException("\\ x y.") + demoException("P(x)Q(x)") + demoException("(P(x)Q(x)") + demoException("exists x -> y") + + +def demoException(s): + try: + Expression.fromstring(s) + except LogicalExpressionException as e: + print(f"{e.__class__.__name__}: {e}") + + +def printtype(ex): + print(f"{ex.str()} : {ex.type}") + + +if __name__ == "__main__": + demo() +# demo_errors() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/relextract.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/relextract.py new file mode 100644 index 0000000..2bcc933 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/relextract.py @@ -0,0 +1,539 @@ +# Natural Language Toolkit: Relation Extraction +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +Code for extracting relational triples from the ieer and conll2002 corpora. + +Relations are stored internally as dictionaries ('reldicts'). + +The two serialization outputs are "rtuple" and "clause". + +- An rtuple is a tuple of the form ``(subj, filler, obj)``, + where ``subj`` and ``obj`` are pairs of Named Entity mentions, and ``filler`` is the string of words + occurring between ``sub`` and ``obj`` (with no intervening NEs). Strings are printed via ``repr()`` to + circumvent locale variations in rendering utf-8 encoded strings. +- A clause is an atom of the form ``relsym(subjsym, objsym)``, + where the relation, subject and object have been canonicalized to single strings. +""" + +# todo: get a more general solution to canonicalized symbols for clauses -- maybe use xmlcharrefs? + +import html +import re +from collections import defaultdict + +# Dictionary that associates corpora with NE classes +NE_CLASSES = { + "ieer": [ + "LOCATION", + "ORGANIZATION", + "PERSON", + "DURATION", + "DATE", + "CARDINAL", + "PERCENT", + "MONEY", + "MEASURE", + ], + "conll2002": ["LOC", "PER", "ORG"], + "ace": [ + "LOCATION", + "ORGANIZATION", + "PERSON", + "DURATION", + "DATE", + "CARDINAL", + "PERCENT", + "MONEY", + "MEASURE", + "FACILITY", + "GPE", + ], +} + +# Allow abbreviated class labels +short2long = dict(LOC="LOCATION", ORG="ORGANIZATION", PER="PERSON") +long2short = dict(LOCATION="LOC", ORGANIZATION="ORG", PERSON="PER") + + +def _expand(type): + """ + Expand an NE class name. + :type type: str + :rtype: str + """ + try: + return short2long[type] + except KeyError: + return type + + +def class_abbrev(type): + """ + Abbreviate an NE class name. + :type type: str + :rtype: str + """ + try: + return long2short[type] + except KeyError: + return type + + +def _join(lst, sep=" ", untag=False): + """ + Join a list into a string, turning tags tuples into tag strings or just words. + :param untag: if ``True``, omit the tag from tagged input strings. + :type lst: list + :rtype: str + """ + try: + return sep.join(lst) + except TypeError: + if untag: + return sep.join(tup[0] for tup in lst) + from nltk.tag import tuple2str + + return sep.join(tuple2str(tup) for tup in lst) + + +def descape_entity(m, defs=html.entities.entitydefs): + """ + Translate one entity to its ISO Latin value. + Inspired by example from effbot.org + + + """ + try: + return defs[m.group(1)] + + except KeyError: + return m.group(0) # use as is + + +def list2sym(lst): + """ + Convert a list of strings into a canonical symbol. + :type lst: list + :return: a Unicode string without whitespace + :rtype: unicode + """ + sym = _join(lst, "_", untag=True) + sym = sym.lower() + ENT = re.compile(r"&(\w+?);") + sym = ENT.sub(descape_entity, sym) + sym = sym.replace(".", "") + return sym + + +def tree2semi_rel(tree): + """ + Group a chunk structure into a list of 'semi-relations' of the form (list(str), ``Tree``). + + In order to facilitate the construction of (``Tree``, string, ``Tree``) triples, this + identifies pairs whose first member is a list (possibly empty) of terminal + strings, and whose second member is a ``Tree`` of the form (NE_label, terminals). + + :param tree: a chunk tree + :return: a list of pairs (list(str), ``Tree``) + :rtype: list of tuple + """ + + from nltk.tree import Tree + + semi_rels = [] + semi_rel = [[], None] + + for dtr in tree: + if not isinstance(dtr, Tree): + semi_rel[0].append(dtr) + else: + # dtr is a Tree + semi_rel[1] = dtr + semi_rels.append(semi_rel) + semi_rel = [[], None] + return semi_rels + + +def semi_rel2reldict(pairs, window=5, trace=False): + """ + Converts the pairs generated by ``tree2semi_rel`` into a 'reldict': a dictionary which + stores information about the subject and object NEs plus the filler between them. + Additionally, a left and right context of length =< window are captured (within + a given input sentence). + + :param pairs: a pair of list(str) and ``Tree``, as generated by + :param window: a threshold for the number of items to include in the left and right context + :type window: int + :return: 'relation' dictionaries whose keys are 'lcon', 'subjclass', 'subjtext', 'subjsym', 'filler', objclass', objtext', 'objsym' and 'rcon' + :rtype: list(defaultdict) + """ + result = [] + while len(pairs) > 2: + reldict = defaultdict(str) + reldict["lcon"] = _join(pairs[0][0][-window:]) + reldict["subjclass"] = pairs[0][1].label() + reldict["subjtext"] = _join(pairs[0][1].leaves()) + reldict["subjsym"] = list2sym(pairs[0][1].leaves()) + reldict["filler"] = _join(pairs[1][0]) + reldict["untagged_filler"] = _join(pairs[1][0], untag=True) + reldict["objclass"] = pairs[1][1].label() + reldict["objtext"] = _join(pairs[1][1].leaves()) + reldict["objsym"] = list2sym(pairs[1][1].leaves()) + reldict["rcon"] = _join(pairs[2][0][:window]) + if trace: + print( + "(%s(%s, %s)" + % ( + reldict["untagged_filler"], + reldict["subjclass"], + reldict["objclass"], + ) + ) + result.append(reldict) + pairs = pairs[1:] + return result + + +def extract_rels(subjclass, objclass, doc, corpus="ace", pattern=None, window=10): + """ + Filter the output of ``semi_rel2reldict`` according to specified NE classes and a filler pattern. + + The parameters ``subjclass`` and ``objclass`` can be used to restrict the + Named Entities to particular types (any of 'LOCATION', 'ORGANIZATION', + 'PERSON', 'DURATION', 'DATE', 'CARDINAL', 'PERCENT', 'MONEY', 'MEASURE'). + + :param subjclass: the class of the subject Named Entity. + :type subjclass: str + :param objclass: the class of the object Named Entity. + :type objclass: str + :param doc: input document + :type doc: ieer document or a list of chunk trees + :param corpus: name of the corpus to take as input; possible values are + 'ieer' and 'conll2002' + :type corpus: str + :param pattern: a regular expression for filtering the fillers of + retrieved triples. + :type pattern: SRE_Pattern + :param window: filters out fillers which exceed this threshold + :type window: int + :return: see ``mk_reldicts`` + :rtype: list(defaultdict) + """ + + if subjclass and subjclass not in NE_CLASSES[corpus]: + if _expand(subjclass) in NE_CLASSES[corpus]: + subjclass = _expand(subjclass) + else: + raise ValueError( + "your value for the subject type has not been recognized: %s" + % subjclass + ) + if objclass and objclass not in NE_CLASSES[corpus]: + if _expand(objclass) in NE_CLASSES[corpus]: + objclass = _expand(objclass) + else: + raise ValueError( + "your value for the object type has not been recognized: %s" % objclass + ) + + if corpus == "ace" or corpus == "conll2002": + pairs = tree2semi_rel(doc) + elif corpus == "ieer": + pairs = tree2semi_rel(doc.text) + tree2semi_rel(doc.headline) + else: + raise ValueError("corpus type not recognized") + + reldicts = semi_rel2reldict(pairs) + + relfilter = lambda x: ( + x["subjclass"] == subjclass + and len(x["filler"].split()) <= window + and pattern.match(x["filler"]) + and x["objclass"] == objclass + ) + + return list(filter(relfilter, reldicts)) + + +def rtuple(reldict, lcon=False, rcon=False): + """ + Pretty print the reldict as an rtuple. + :param reldict: a relation dictionary + :type reldict: defaultdict + """ + items = [ + class_abbrev(reldict["subjclass"]), + reldict["subjtext"], + reldict["filler"], + class_abbrev(reldict["objclass"]), + reldict["objtext"], + ] + format = "[%s: %r] %r [%s: %r]" + if lcon: + items = [reldict["lcon"]] + items + format = "...%r)" + format + if rcon: + items.append(reldict["rcon"]) + format = format + "(%r..." + printargs = tuple(items) + return format % printargs + + +def clause(reldict, relsym): + """ + Print the relation in clausal form. + :param reldict: a relation dictionary + :type reldict: defaultdict + :param relsym: a label for the relation + :type relsym: str + """ + items = (relsym, reldict["subjsym"], reldict["objsym"]) + return "%s(%r, %r)" % items + + +####################################################### +# Demos of relation extraction with regular expressions +####################################################### + +############################################ +# Example of in(ORG, LOC) +############################################ +def in_demo(trace=0, sql=True): + """ + Select pairs of organizations and locations whose mentions occur with an + intervening occurrence of the preposition "in". + + If the sql parameter is set to True, then the entity pairs are loaded into + an in-memory database, and subsequently pulled out using an SQL "SELECT" + query. + """ + from nltk.corpus import ieer + + if sql: + try: + import sqlite3 + + connection = sqlite3.connect(":memory:") + cur = connection.cursor() + cur.execute( + """create table Locations + (OrgName text, LocationName text, DocID text)""" + ) + except ImportError: + import warnings + + warnings.warn("Cannot import sqlite; sql flag will be ignored.") + + IN = re.compile(r".*\bin\b(?!\b.+ing)") + + print() + print("IEER: in(ORG, LOC) -- just the clauses:") + print("=" * 45) + + for file in ieer.fileids(): + for doc in ieer.parsed_docs(file): + if trace: + print(doc.docno) + print("=" * 15) + for rel in extract_rels("ORG", "LOC", doc, corpus="ieer", pattern=IN): + print(clause(rel, relsym="IN")) + if sql: + try: + rtuple = (rel["subjtext"], rel["objtext"], doc.docno) + cur.execute( + """insert into Locations + values (?, ?, ?)""", + rtuple, + ) + connection.commit() + except NameError: + pass + + if sql: + try: + cur.execute( + """select OrgName from Locations + where LocationName = 'Atlanta'""" + ) + print() + print("Extract data from SQL table: ORGs in Atlanta") + print("-" * 15) + for row in cur: + print(row) + except NameError: + pass + + +############################################ +# Example of has_role(PER, LOC) +############################################ + + +def roles_demo(trace=0): + from nltk.corpus import ieer + + roles = r""" + (.*( # assorted roles + analyst| + chair(wo)?man| + commissioner| + counsel| + director| + economist| + editor| + executive| + foreman| + governor| + head| + lawyer| + leader| + librarian).*)| + manager| + partner| + president| + producer| + professor| + researcher| + spokes(wo)?man| + writer| + ,\sof\sthe?\s* # "X, of (the) Y" + """ + ROLES = re.compile(roles, re.VERBOSE) + + print() + print("IEER: has_role(PER, ORG) -- raw rtuples:") + print("=" * 45) + + for file in ieer.fileids(): + for doc in ieer.parsed_docs(file): + lcon = rcon = False + if trace: + print(doc.docno) + print("=" * 15) + lcon = rcon = True + for rel in extract_rels("PER", "ORG", doc, corpus="ieer", pattern=ROLES): + print(rtuple(rel, lcon=lcon, rcon=rcon)) + + +############################################## +### Show what's in the IEER Headlines +############################################## + + +def ieer_headlines(): + + from nltk.corpus import ieer + from nltk.tree import Tree + + print("IEER: First 20 Headlines") + print("=" * 45) + + trees = [ + (doc.docno, doc.headline) + for file in ieer.fileids() + for doc in ieer.parsed_docs(file) + ] + for tree in trees[:20]: + print() + print("%s:\n%s" % tree) + + +############################################# +## Dutch CONLL2002: take_on_role(PER, ORG +############################################# + + +def conllned(trace=1): + """ + Find the copula+'van' relation ('of') in the Dutch tagged training corpus + from CoNLL 2002. + """ + + from nltk.corpus import conll2002 + + vnv = """ + ( + is/V| # 3rd sing present and + was/V| # past forms of the verb zijn ('be') + werd/V| # and also present + wordt/V # past of worden ('become) + ) + .* # followed by anything + van/Prep # followed by van ('of') + """ + VAN = re.compile(vnv, re.VERBOSE) + + print() + print("Dutch CoNLL2002: van(PER, ORG) -- raw rtuples with context:") + print("=" * 45) + + for doc in conll2002.chunked_sents("ned.train"): + lcon = rcon = False + if trace: + lcon = rcon = True + for rel in extract_rels( + "PER", "ORG", doc, corpus="conll2002", pattern=VAN, window=10 + ): + print(rtuple(rel, lcon=lcon, rcon=rcon)) + + +############################################# +## Spanish CONLL2002: (PER, ORG) +############################################# + + +def conllesp(): + from nltk.corpus import conll2002 + + de = """ + .* + ( + de/SP| + del/SP + ) + """ + DE = re.compile(de, re.VERBOSE) + + print() + print("Spanish CoNLL2002: de(ORG, LOC) -- just the first 10 clauses:") + print("=" * 45) + rels = [ + rel + for doc in conll2002.chunked_sents("esp.train") + for rel in extract_rels("ORG", "LOC", doc, corpus="conll2002", pattern=DE) + ] + for r in rels[:10]: + print(clause(r, relsym="DE")) + print() + + +def ne_chunked(): + print() + print("1500 Sentences from Penn Treebank, as processed by NLTK NE Chunker") + print("=" * 45) + ROLE = re.compile( + r".*(chairman|president|trader|scientist|economist|analyst|partner).*" + ) + rels = [] + for i, sent in enumerate(nltk.corpus.treebank.tagged_sents()[:1500]): + sent = nltk.ne_chunk(sent) + rels = extract_rels("PER", "ORG", sent, corpus="ace", pattern=ROLE, window=7) + for rel in rels: + print(f"{i:<5}{rtuple(rel)}") + + +if __name__ == "__main__": + import nltk + from nltk.sem import relextract + + in_demo(trace=0) + roles_demo(trace=0) + conllned() + conllesp() + ieer_headlines() + ne_chunked() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/skolemize.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/skolemize.py new file mode 100644 index 0000000..d3fc02a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/skolemize.py @@ -0,0 +1,148 @@ +# Natural Language Toolkit: Semantic Interpretation +# +# Author: Ewan Klein +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +from nltk.sem.logic import ( + AllExpression, + AndExpression, + ApplicationExpression, + EqualityExpression, + ExistsExpression, + IffExpression, + ImpExpression, + NegatedExpression, + OrExpression, + VariableExpression, + skolem_function, + unique_variable, +) + + +def skolemize(expression, univ_scope=None, used_variables=None): + """ + Skolemize the expression and convert to conjunctive normal form (CNF) + """ + if univ_scope is None: + univ_scope = set() + if used_variables is None: + used_variables = set() + + if isinstance(expression, AllExpression): + term = skolemize( + expression.term, + univ_scope | {expression.variable}, + used_variables | {expression.variable}, + ) + return term.replace( + expression.variable, + VariableExpression(unique_variable(ignore=used_variables)), + ) + elif isinstance(expression, AndExpression): + return skolemize(expression.first, univ_scope, used_variables) & skolemize( + expression.second, univ_scope, used_variables + ) + elif isinstance(expression, OrExpression): + return to_cnf( + skolemize(expression.first, univ_scope, used_variables), + skolemize(expression.second, univ_scope, used_variables), + ) + elif isinstance(expression, ImpExpression): + return to_cnf( + skolemize(-expression.first, univ_scope, used_variables), + skolemize(expression.second, univ_scope, used_variables), + ) + elif isinstance(expression, IffExpression): + return to_cnf( + skolemize(-expression.first, univ_scope, used_variables), + skolemize(expression.second, univ_scope, used_variables), + ) & to_cnf( + skolemize(expression.first, univ_scope, used_variables), + skolemize(-expression.second, univ_scope, used_variables), + ) + elif isinstance(expression, EqualityExpression): + return expression + elif isinstance(expression, NegatedExpression): + negated = expression.term + if isinstance(negated, AllExpression): + term = skolemize( + -negated.term, univ_scope, used_variables | {negated.variable} + ) + if univ_scope: + return term.replace(negated.variable, skolem_function(univ_scope)) + else: + skolem_constant = VariableExpression( + unique_variable(ignore=used_variables) + ) + return term.replace(negated.variable, skolem_constant) + elif isinstance(negated, AndExpression): + return to_cnf( + skolemize(-negated.first, univ_scope, used_variables), + skolemize(-negated.second, univ_scope, used_variables), + ) + elif isinstance(negated, OrExpression): + return skolemize(-negated.first, univ_scope, used_variables) & skolemize( + -negated.second, univ_scope, used_variables + ) + elif isinstance(negated, ImpExpression): + return skolemize(negated.first, univ_scope, used_variables) & skolemize( + -negated.second, univ_scope, used_variables + ) + elif isinstance(negated, IffExpression): + return to_cnf( + skolemize(-negated.first, univ_scope, used_variables), + skolemize(-negated.second, univ_scope, used_variables), + ) & to_cnf( + skolemize(negated.first, univ_scope, used_variables), + skolemize(negated.second, univ_scope, used_variables), + ) + elif isinstance(negated, EqualityExpression): + return expression + elif isinstance(negated, NegatedExpression): + return skolemize(negated.term, univ_scope, used_variables) + elif isinstance(negated, ExistsExpression): + term = skolemize( + -negated.term, + univ_scope | {negated.variable}, + used_variables | {negated.variable}, + ) + return term.replace( + negated.variable, + VariableExpression(unique_variable(ignore=used_variables)), + ) + elif isinstance(negated, ApplicationExpression): + return expression + else: + raise Exception("'%s' cannot be skolemized" % expression) + elif isinstance(expression, ExistsExpression): + term = skolemize( + expression.term, univ_scope, used_variables | {expression.variable} + ) + if univ_scope: + return term.replace(expression.variable, skolem_function(univ_scope)) + else: + skolem_constant = VariableExpression(unique_variable(ignore=used_variables)) + return term.replace(expression.variable, skolem_constant) + elif isinstance(expression, ApplicationExpression): + return expression + else: + raise Exception("'%s' cannot be skolemized" % expression) + + +def to_cnf(first, second): + """ + Convert this split disjunction to conjunctive normal form (CNF) + """ + if isinstance(first, AndExpression): + r_first = to_cnf(first.first, second) + r_second = to_cnf(first.second, second) + return r_first & r_second + elif isinstance(second, AndExpression): + r_first = to_cnf(first, second.first) + r_second = to_cnf(first, second.second) + return r_first & r_second + else: + return first | second diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sem/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/util.py new file mode 100644 index 0000000..38fa804 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sem/util.py @@ -0,0 +1,309 @@ +# Natural Language Toolkit: Semantic Interpretation +# +# Author: Ewan Klein +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +""" +Utility functions for batch-processing sentences: parsing and +extraction of the semantic representation of the root node of the the +syntax tree, followed by evaluation of the semantic representation in +a first-order model. +""" + +import codecs + +from nltk.sem import evaluate + +############################################################## +## Utility functions for connecting parse output to semantics +############################################################## + + +def parse_sents(inputs, grammar, trace=0): + """ + Convert input sentences into syntactic trees. + + :param inputs: sentences to be parsed + :type inputs: list(str) + :param grammar: ``FeatureGrammar`` or name of feature-based grammar + :type grammar: nltk.grammar.FeatureGrammar + :rtype: list(nltk.tree.Tree) or dict(list(str)): list(Tree) + :return: a mapping from input sentences to a list of ``Tree`` instances. + """ + # put imports here to avoid circult dependencies + from nltk.grammar import FeatureGrammar + from nltk.parse import FeatureChartParser, load_parser + + if isinstance(grammar, FeatureGrammar): + cp = FeatureChartParser(grammar) + else: + cp = load_parser(grammar, trace=trace) + parses = [] + for sent in inputs: + tokens = sent.split() # use a tokenizer? + syntrees = list(cp.parse(tokens)) + parses.append(syntrees) + return parses + + +def root_semrep(syntree, semkey="SEM"): + """ + Find the semantic representation at the root of a tree. + + :param syntree: a parse ``Tree`` + :param semkey: the feature label to use for the root semantics in the tree + :return: the semantic representation at the root of a ``Tree`` + :rtype: sem.Expression + """ + from nltk.grammar import FeatStructNonterminal + + node = syntree.label() + assert isinstance(node, FeatStructNonterminal) + try: + return node[semkey] + except KeyError: + print(node, end=" ") + print("has no specification for the feature %s" % semkey) + raise + + +def interpret_sents(inputs, grammar, semkey="SEM", trace=0): + """ + Add the semantic representation to each syntactic parse tree + of each input sentence. + + :param inputs: a list of sentences + :type inputs: list(str) + :param grammar: ``FeatureGrammar`` or name of feature-based grammar + :type grammar: nltk.grammar.FeatureGrammar + :return: a mapping from sentences to lists of pairs (parse-tree, semantic-representations) + :rtype: list(list(tuple(nltk.tree.Tree, nltk.sem.logic.ConstantExpression))) + """ + return [ + [(syn, root_semrep(syn, semkey)) for syn in syntrees] + for syntrees in parse_sents(inputs, grammar, trace=trace) + ] + + +def evaluate_sents(inputs, grammar, model, assignment, trace=0): + """ + Add the truth-in-a-model value to each semantic representation + for each syntactic parse of each input sentences. + + :param inputs: a list of sentences + :type inputs: list(str) + :param grammar: ``FeatureGrammar`` or name of feature-based grammar + :type grammar: nltk.grammar.FeatureGrammar + :return: a mapping from sentences to lists of triples (parse-tree, semantic-representations, evaluation-in-model) + :rtype: list(list(tuple(nltk.tree.Tree, nltk.sem.logic.ConstantExpression, bool or dict(str): bool))) + """ + return [ + [ + (syn, sem, model.evaluate("%s" % sem, assignment, trace=trace)) + for (syn, sem) in interpretations + ] + for interpretations in interpret_sents(inputs, grammar) + ] + + +def demo_model0(): + global m0, g0 + # Initialize a valuation of non-logical constants.""" + v = [ + ("john", "b1"), + ("mary", "g1"), + ("suzie", "g2"), + ("fido", "d1"), + ("tess", "d2"), + ("noosa", "n"), + ("girl", {"g1", "g2"}), + ("boy", {"b1", "b2"}), + ("dog", {"d1", "d2"}), + ("bark", {"d1", "d2"}), + ("walk", {"b1", "g2", "d1"}), + ("chase", {("b1", "g1"), ("b2", "g1"), ("g1", "d1"), ("g2", "d2")}), + ( + "see", + {("b1", "g1"), ("b2", "d2"), ("g1", "b1"), ("d2", "b1"), ("g2", "n")}, + ), + ("in", {("b1", "n"), ("b2", "n"), ("d2", "n")}), + ("with", {("b1", "g1"), ("g1", "b1"), ("d1", "b1"), ("b1", "d1")}), + ] + # Read in the data from ``v`` + val = evaluate.Valuation(v) + # Bind ``dom`` to the ``domain`` property of ``val`` + dom = val.domain + # Initialize a model with parameters ``dom`` and ``val``. + m0 = evaluate.Model(dom, val) + # Initialize a variable assignment with parameter ``dom`` + g0 = evaluate.Assignment(dom) + + +def read_sents(filename, encoding="utf8"): + with codecs.open(filename, "r", encoding) as fp: + sents = [l.rstrip() for l in fp] + + # get rid of blank lines + sents = [l for l in sents if len(l) > 0] + sents = [l for l in sents if not l[0] == "#"] + return sents + + +def demo_legacy_grammar(): + """ + Check that interpret_sents() is compatible with legacy grammars that use + a lowercase 'sem' feature. + + Define 'test.fcfg' to be the following + + """ + from nltk.grammar import FeatureGrammar + + g = FeatureGrammar.fromstring( + """ + % start S + S[sem=] -> 'hello' + """ + ) + print("Reading grammar: %s" % g) + print("*" * 20) + for reading in interpret_sents(["hello"], g, semkey="sem"): + syn, sem = reading[0] + print() + print("output: ", sem) + + +def demo(): + import sys + from optparse import OptionParser + + description = """ + Parse and evaluate some sentences. + """ + + opts = OptionParser(description=description) + + opts.set_defaults( + evaluate=True, + beta=True, + syntrace=0, + semtrace=0, + demo="default", + grammar="", + sentences="", + ) + + opts.add_option( + "-d", + "--demo", + dest="demo", + help="choose demo D; omit this for the default demo, or specify 'chat80'", + metavar="D", + ) + opts.add_option( + "-g", "--gram", dest="grammar", help="read in grammar G", metavar="G" + ) + opts.add_option( + "-m", + "--model", + dest="model", + help="import model M (omit '.py' suffix)", + metavar="M", + ) + opts.add_option( + "-s", + "--sentences", + dest="sentences", + help="read in a file of test sentences S", + metavar="S", + ) + opts.add_option( + "-e", + "--no-eval", + action="store_false", + dest="evaluate", + help="just do a syntactic analysis", + ) + opts.add_option( + "-b", + "--no-beta-reduction", + action="store_false", + dest="beta", + help="don't carry out beta-reduction", + ) + opts.add_option( + "-t", + "--syntrace", + action="count", + dest="syntrace", + help="set syntactic tracing on; requires '-e' option", + ) + opts.add_option( + "-T", + "--semtrace", + action="count", + dest="semtrace", + help="set semantic tracing on", + ) + + (options, args) = opts.parse_args() + + SPACER = "-" * 30 + + demo_model0() + + sents = [ + "Fido sees a boy with Mary", + "John sees Mary", + "every girl chases a dog", + "every boy chases a girl", + "John walks with a girl in Noosa", + "who walks", + ] + + gramfile = "grammars/sample_grammars/sem2.fcfg" + + if options.sentences: + sentsfile = options.sentences + if options.grammar: + gramfile = options.grammar + if options.model: + exec("import %s as model" % options.model) + + if sents is None: + sents = read_sents(sentsfile) + + # Set model and assignment + model = m0 + g = g0 + + if options.evaluate: + evaluations = evaluate_sents(sents, gramfile, model, g, trace=options.semtrace) + else: + semreps = interpret_sents(sents, gramfile, trace=options.syntrace) + + for i, sent in enumerate(sents): + n = 1 + print("\nSentence: %s" % sent) + print(SPACER) + if options.evaluate: + + for (syntree, semrep, value) in evaluations[i]: + if isinstance(value, dict): + value = set(value.keys()) + print("%d: %s" % (n, semrep)) + print(value) + n += 1 + else: + + for (syntree, semrep) in semreps[i]: + print("%d: %s" % (n, semrep)) + n += 1 + + +if __name__ == "__main__": + demo() + demo_legacy_grammar() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__init__.py new file mode 100644 index 0000000..a823874 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__init__.py @@ -0,0 +1,13 @@ +# Natural Language Toolkit: Sentiment Analysis +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK Sentiment Analysis Package + +""" +from nltk.sentiment.sentiment_analyzer import SentimentAnalyzer +from nltk.sentiment.vader import SentimentIntensityAnalyzer diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0323a14 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/sentiment_analyzer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/sentiment_analyzer.cpython-310.pyc new file mode 100644 index 0000000..1162e6e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/sentiment_analyzer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..aabc7d3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/vader.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/vader.cpython-310.pyc new file mode 100644 index 0000000..dce0fde Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/__pycache__/vader.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/sentiment_analyzer.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/sentiment_analyzer.py new file mode 100644 index 0000000..183295d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/sentiment_analyzer.py @@ -0,0 +1,255 @@ +# +# Natural Language Toolkit: Sentiment Analyzer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +A SentimentAnalyzer is a tool to implement and facilitate Sentiment Analysis tasks +using NLTK features and classifiers, especially for teaching and demonstrative +purposes. +""" + +import sys +from collections import defaultdict + +from nltk.classify.util import accuracy as eval_accuracy +from nltk.classify.util import apply_features +from nltk.collocations import BigramCollocationFinder +from nltk.metrics import BigramAssocMeasures +from nltk.metrics import f_measure as eval_f_measure +from nltk.metrics import precision as eval_precision +from nltk.metrics import recall as eval_recall +from nltk.probability import FreqDist + + +class SentimentAnalyzer: + """ + A Sentiment Analysis tool based on machine learning approaches. + """ + + def __init__(self, classifier=None): + self.feat_extractors = defaultdict(list) + self.classifier = classifier + + def all_words(self, documents, labeled=None): + """ + Return all words/tokens from the documents (with duplicates). + + :param documents: a list of (words, label) tuples. + :param labeled: if `True`, assume that each document is represented by a + (words, label) tuple: (list(str), str). If `False`, each document is + considered as being a simple list of strings: list(str). + :rtype: list(str) + :return: A list of all words/tokens in `documents`. + """ + all_words = [] + if labeled is None: + labeled = documents and isinstance(documents[0], tuple) + if labeled: + for words, _sentiment in documents: + all_words.extend(words) + elif not labeled: + for words in documents: + all_words.extend(words) + return all_words + + def apply_features(self, documents, labeled=None): + """ + Apply all feature extractor functions to the documents. This is a wrapper + around `nltk.classify.util.apply_features`. + + If `labeled=False`, return featuresets as: + [feature_func(doc) for doc in documents] + If `labeled=True`, return featuresets as: + [(feature_func(tok), label) for (tok, label) in toks] + + :param documents: a list of documents. `If labeled=True`, the method expects + a list of (words, label) tuples. + :rtype: LazyMap + """ + return apply_features(self.extract_features, documents, labeled) + + def unigram_word_feats(self, words, top_n=None, min_freq=0): + """ + Return most common top_n word features. + + :param words: a list of words/tokens. + :param top_n: number of best words/tokens to use, sorted by frequency. + :rtype: list(str) + :return: A list of `top_n` words/tokens (with no duplicates) sorted by + frequency. + """ + # Stopwords are not removed + unigram_feats_freqs = FreqDist(word for word in words) + return [ + w + for w, f in unigram_feats_freqs.most_common(top_n) + if unigram_feats_freqs[w] > min_freq + ] + + def bigram_collocation_feats( + self, documents, top_n=None, min_freq=3, assoc_measure=BigramAssocMeasures.pmi + ): + """ + Return `top_n` bigram features (using `assoc_measure`). + Note that this method is based on bigram collocations measures, and not + on simple bigram frequency. + + :param documents: a list (or iterable) of tokens. + :param top_n: number of best words/tokens to use, sorted by association + measure. + :param assoc_measure: bigram association measure to use as score function. + :param min_freq: the minimum number of occurrencies of bigrams to take + into consideration. + + :return: `top_n` ngrams scored by the given association measure. + """ + finder = BigramCollocationFinder.from_documents(documents) + finder.apply_freq_filter(min_freq) + return finder.nbest(assoc_measure, top_n) + + def classify(self, instance): + """ + Classify a single instance applying the features that have already been + stored in the SentimentAnalyzer. + + :param instance: a list (or iterable) of tokens. + :return: the classification result given by applying the classifier. + """ + instance_feats = self.apply_features([instance], labeled=False) + return self.classifier.classify(instance_feats[0]) + + def add_feat_extractor(self, function, **kwargs): + """ + Add a new function to extract features from a document. This function will + be used in extract_features(). + Important: in this step our kwargs are only representing additional parameters, + and NOT the document we have to parse. The document will always be the first + parameter in the parameter list, and it will be added in the extract_features() + function. + + :param function: the extractor function to add to the list of feature extractors. + :param kwargs: additional parameters required by the `function` function. + """ + self.feat_extractors[function].append(kwargs) + + def extract_features(self, document): + """ + Apply extractor functions (and their parameters) to the present document. + We pass `document` as the first parameter of the extractor functions. + If we want to use the same extractor function multiple times, we have to + add it to the extractors with `add_feat_extractor` using multiple sets of + parameters (one for each call of the extractor function). + + :param document: the document that will be passed as argument to the + feature extractor functions. + :return: A dictionary of populated features extracted from the document. + :rtype: dict + """ + all_features = {} + for extractor in self.feat_extractors: + for param_set in self.feat_extractors[extractor]: + feats = extractor(document, **param_set) + all_features.update(feats) + return all_features + + def train(self, trainer, training_set, save_classifier=None, **kwargs): + """ + Train classifier on the training set, optionally saving the output in the + file specified by `save_classifier`. + Additional arguments depend on the specific trainer used. For example, + a MaxentClassifier can use `max_iter` parameter to specify the number + of iterations, while a NaiveBayesClassifier cannot. + + :param trainer: `train` method of a classifier. + E.g.: NaiveBayesClassifier.train + :param training_set: the training set to be passed as argument to the + classifier `train` method. + :param save_classifier: the filename of the file where the classifier + will be stored (optional). + :param kwargs: additional parameters that will be passed as arguments to + the classifier `train` function. + :return: A classifier instance trained on the training set. + :rtype: + """ + print("Training classifier") + self.classifier = trainer(training_set, **kwargs) + if save_classifier: + self.save_file(self.classifier, save_classifier) + + return self.classifier + + def save_file(self, content, filename): + """ + Store `content` in `filename`. Can be used to store a SentimentAnalyzer. + """ + print("Saving", filename, file=sys.stderr) + with open(filename, "wb") as storage_file: + import pickle + + # The protocol=2 parameter is for python2 compatibility + pickle.dump(content, storage_file, protocol=2) + + def evaluate( + self, + test_set, + classifier=None, + accuracy=True, + f_measure=True, + precision=True, + recall=True, + verbose=False, + ): + """ + Evaluate and print classifier performance on the test set. + + :param test_set: A list of (tokens, label) tuples to use as gold set. + :param classifier: a classifier instance (previously trained). + :param accuracy: if `True`, evaluate classifier accuracy. + :param f_measure: if `True`, evaluate classifier f_measure. + :param precision: if `True`, evaluate classifier precision. + :param recall: if `True`, evaluate classifier recall. + :return: evaluation results. + :rtype: dict(str): float + """ + if classifier is None: + classifier = self.classifier + print(f"Evaluating {type(classifier).__name__} results...") + metrics_results = {} + if accuracy: + accuracy_score = eval_accuracy(classifier, test_set) + metrics_results["Accuracy"] = accuracy_score + + gold_results = defaultdict(set) + test_results = defaultdict(set) + labels = set() + for i, (feats, label) in enumerate(test_set): + labels.add(label) + gold_results[label].add(i) + observed = classifier.classify(feats) + test_results[observed].add(i) + + for label in labels: + if precision: + precision_score = eval_precision( + gold_results[label], test_results[label] + ) + metrics_results[f"Precision [{label}]"] = precision_score + if recall: + recall_score = eval_recall(gold_results[label], test_results[label]) + metrics_results[f"Recall [{label}]"] = recall_score + if f_measure: + f_measure_score = eval_f_measure( + gold_results[label], test_results[label] + ) + metrics_results[f"F-measure [{label}]"] = f_measure_score + + # Print evaluation results (in alphabetical order) + if verbose: + for result in sorted(metrics_results): + print(f"{result}: {metrics_results[result]}") + + return metrics_results diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/util.py new file mode 100644 index 0000000..7f028fd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/util.py @@ -0,0 +1,887 @@ +# +# Natural Language Toolkit: Sentiment Analyzer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Pierpaolo Pantone <24alsecondo@gmail.com> +# URL: +# For license information, see LICENSE.TXT + +""" +Utility methods for Sentiment Analysis. +""" + +import codecs +import csv +import json +import pickle +import random +import re +import sys +import time +from copy import deepcopy + +import nltk +from nltk.corpus import CategorizedPlaintextCorpusReader +from nltk.data import load +from nltk.tokenize.casual import EMOTICON_RE + +# //////////////////////////////////////////////////////////// +# { Regular expressions +# //////////////////////////////////////////////////////////// + +# Regular expression for negation by Christopher Potts +NEGATION = r""" + (?: + ^(?:never|no|nothing|nowhere|noone|none|not| + havent|hasnt|hadnt|cant|couldnt|shouldnt| + wont|wouldnt|dont|doesnt|didnt|isnt|arent|aint + )$ + ) + | + n't""" + +NEGATION_RE = re.compile(NEGATION, re.VERBOSE) + +CLAUSE_PUNCT = r"^[.:;!?]$" +CLAUSE_PUNCT_RE = re.compile(CLAUSE_PUNCT) + +# Happy and sad emoticons + +HAPPY = { + ":-)", + ":)", + ";)", + ":o)", + ":]", + ":3", + ":c)", + ":>", + "=]", + "8)", + "=)", + ":}", + ":^)", + ":-D", + ":D", + "8-D", + "8D", + "x-D", + "xD", + "X-D", + "XD", + "=-D", + "=D", + "=-3", + "=3", + ":-))", + ":'-)", + ":')", + ":*", + ":^*", + ">:P", + ":-P", + ":P", + "X-P", + "x-p", + "xp", + "XP", + ":-p", + ":p", + "=p", + ":-b", + ":b", + ">:)", + ">;)", + ">:-)", + "<3", +} + +SAD = { + ":L", + ":-/", + ">:/", + ":S", + ">:[", + ":@", + ":-(", + ":[", + ":-||", + "=L", + ":<", + ":-[", + ":-<", + "=\\", + "=/", + ">:(", + ":(", + ">.<", + ":'-(", + ":'(", + ":\\", + ":-c", + ":c", + ":{", + ">:\\", + ";(", +} + + +def timer(method): + """ + A timer decorator to measure execution performance of methods. + """ + + def timed(*args, **kw): + start = time.time() + result = method(*args, **kw) + end = time.time() + tot_time = end - start + hours = tot_time // 3600 + mins = tot_time // 60 % 60 + # in Python 2.x round() will return a float, so we convert it to int + secs = int(round(tot_time % 60)) + if hours == 0 and mins == 0 and secs < 10: + print(f"[TIMER] {method.__name__}(): {method.__name__:.3f} seconds") + else: + print(f"[TIMER] {method.__name__}(): {hours}h {mins}m {secs}s") + return result + + return timed + + +# //////////////////////////////////////////////////////////// +# { Feature extractor functions +# //////////////////////////////////////////////////////////// +""" +Feature extractor functions are declared outside the SentimentAnalyzer class. +Users should have the possibility to create their own feature extractors +without modifying SentimentAnalyzer. +""" + + +def extract_unigram_feats(document, unigrams, handle_negation=False): + """ + Populate a dictionary of unigram features, reflecting the presence/absence in + the document of each of the tokens in `unigrams`. + + :param document: a list of words/tokens. + :param unigrams: a list of words/tokens whose presence/absence has to be + checked in `document`. + :param handle_negation: if `handle_negation == True` apply `mark_negation` + method to `document` before checking for unigram presence/absence. + :return: a dictionary of unigram features {unigram : boolean}. + + >>> words = ['ice', 'police', 'riot'] + >>> document = 'ice is melting due to global warming'.split() + >>> sorted(extract_unigram_feats(document, words).items()) + [('contains(ice)', True), ('contains(police)', False), ('contains(riot)', False)] + """ + features = {} + if handle_negation: + document = mark_negation(document) + for word in unigrams: + features[f"contains({word})"] = word in set(document) + return features + + +def extract_bigram_feats(document, bigrams): + """ + Populate a dictionary of bigram features, reflecting the presence/absence in + the document of each of the tokens in `bigrams`. This extractor function only + considers contiguous bigrams obtained by `nltk.bigrams`. + + :param document: a list of words/tokens. + :param unigrams: a list of bigrams whose presence/absence has to be + checked in `document`. + :return: a dictionary of bigram features {bigram : boolean}. + + >>> bigrams = [('global', 'warming'), ('police', 'prevented'), ('love', 'you')] + >>> document = 'ice is melting due to global warming'.split() + >>> sorted(extract_bigram_feats(document, bigrams).items()) # doctest: +NORMALIZE_WHITESPACE + [('contains(global - warming)', True), ('contains(love - you)', False), + ('contains(police - prevented)', False)] + """ + features = {} + for bigr in bigrams: + features[f"contains({bigr[0]} - {bigr[1]})"] = bigr in nltk.bigrams(document) + return features + + +# //////////////////////////////////////////////////////////// +# { Helper Functions +# //////////////////////////////////////////////////////////// + + +def mark_negation(document, double_neg_flip=False, shallow=False): + """ + Append _NEG suffix to words that appear in the scope between a negation + and a punctuation mark. + + :param document: a list of words/tokens, or a tuple (words, label). + :param shallow: if True, the method will modify the original document in place. + :param double_neg_flip: if True, double negation is considered affirmation + (we activate/deactivate negation scope every time we find a negation). + :return: if `shallow == True` the method will modify the original document + and return it. If `shallow == False` the method will return a modified + document, leaving the original unmodified. + + >>> sent = "I didn't like this movie . It was bad .".split() + >>> mark_negation(sent) + ['I', "didn't", 'like_NEG', 'this_NEG', 'movie_NEG', '.', 'It', 'was', 'bad', '.'] + """ + if not shallow: + document = deepcopy(document) + # check if the document is labeled. If so, do not consider the label. + labeled = document and isinstance(document[0], (tuple, list)) + if labeled: + doc = document[0] + else: + doc = document + neg_scope = False + for i, word in enumerate(doc): + if NEGATION_RE.search(word): + if not neg_scope or (neg_scope and double_neg_flip): + neg_scope = not neg_scope + continue + else: + doc[i] += "_NEG" + elif neg_scope and CLAUSE_PUNCT_RE.search(word): + neg_scope = not neg_scope + elif neg_scope and not CLAUSE_PUNCT_RE.search(word): + doc[i] += "_NEG" + + return document + + +def output_markdown(filename, **kwargs): + """ + Write the output of an analysis to a file. + """ + with codecs.open(filename, "at") as outfile: + text = "\n*** \n\n" + text += "{} \n\n".format(time.strftime("%d/%m/%Y, %H:%M")) + for k in sorted(kwargs): + if isinstance(kwargs[k], dict): + dictionary = kwargs[k] + text += f" - **{k}:**\n" + for entry in sorted(dictionary): + text += f" - {entry}: {dictionary[entry]} \n" + elif isinstance(kwargs[k], list): + text += f" - **{k}:**\n" + for entry in kwargs[k]: + text += f" - {entry}\n" + else: + text += f" - **{k}:** {kwargs[k]} \n" + outfile.write(text) + + +def split_train_test(all_instances, n=None): + """ + Randomly split `n` instances of the dataset into train and test sets. + + :param all_instances: a list of instances (e.g. documents) that will be split. + :param n: the number of instances to consider (in case we want to use only a + subset). + :return: two lists of instances. Train set is 8/10 of the total and test set + is 2/10 of the total. + """ + random.seed(12345) + random.shuffle(all_instances) + if not n or n > len(all_instances): + n = len(all_instances) + train_set = all_instances[: int(0.8 * n)] + test_set = all_instances[int(0.8 * n) : n] + + return train_set, test_set + + +def _show_plot(x_values, y_values, x_labels=None, y_labels=None): + try: + import matplotlib.pyplot as plt + except ImportError as e: + raise ImportError( + "The plot function requires matplotlib to be installed." + "See https://matplotlib.org/" + ) from e + + plt.locator_params(axis="y", nbins=3) + axes = plt.axes() + axes.yaxis.grid() + plt.plot(x_values, y_values, "ro", color="red") + plt.ylim(ymin=-1.2, ymax=1.2) + plt.tight_layout(pad=5) + if x_labels: + plt.xticks(x_values, x_labels, rotation="vertical") + if y_labels: + plt.yticks([-1, 0, 1], y_labels, rotation="horizontal") + # Pad margins so that markers are not clipped by the axes + plt.margins(0.2) + plt.show() + + +# //////////////////////////////////////////////////////////// +# { Parsing and conversion functions +# //////////////////////////////////////////////////////////// + + +def json2csv_preprocess( + json_file, + outfile, + fields, + encoding="utf8", + errors="replace", + gzip_compress=False, + skip_retweets=True, + skip_tongue_tweets=True, + skip_ambiguous_tweets=True, + strip_off_emoticons=True, + remove_duplicates=True, + limit=None, +): + """ + Convert json file to csv file, preprocessing each row to obtain a suitable + dataset for tweets Semantic Analysis. + + :param json_file: the original json file containing tweets. + :param outfile: the output csv filename. + :param fields: a list of fields that will be extracted from the json file and + kept in the output csv file. + :param encoding: the encoding of the files. + :param errors: the error handling strategy for the output writer. + :param gzip_compress: if True, create a compressed GZIP file. + + :param skip_retweets: if True, remove retweets. + :param skip_tongue_tweets: if True, remove tweets containing ":P" and ":-P" + emoticons. + :param skip_ambiguous_tweets: if True, remove tweets containing both happy + and sad emoticons. + :param strip_off_emoticons: if True, strip off emoticons from all tweets. + :param remove_duplicates: if True, remove tweets appearing more than once. + :param limit: an integer to set the number of tweets to convert. After the + limit is reached the conversion will stop. It can be useful to create + subsets of the original tweets json data. + """ + with codecs.open(json_file, encoding=encoding) as fp: + (writer, outf) = _outf_writer(outfile, encoding, errors, gzip_compress) + # write the list of fields as header + writer.writerow(fields) + + if remove_duplicates == True: + tweets_cache = [] + i = 0 + for line in fp: + tweet = json.loads(line) + row = extract_fields(tweet, fields) + try: + text = row[fields.index("text")] + # Remove retweets + if skip_retweets == True: + if re.search(r"\bRT\b", text): + continue + # Remove tweets containing ":P" and ":-P" emoticons + if skip_tongue_tweets == True: + if re.search(r"\:\-?P\b", text): + continue + # Remove tweets containing both happy and sad emoticons + if skip_ambiguous_tweets == True: + all_emoticons = EMOTICON_RE.findall(text) + if all_emoticons: + if (set(all_emoticons) & HAPPY) and (set(all_emoticons) & SAD): + continue + # Strip off emoticons from all tweets + if strip_off_emoticons == True: + row[fields.index("text")] = re.sub( + r"(?!\n)\s+", " ", EMOTICON_RE.sub("", text) + ) + # Remove duplicate tweets + if remove_duplicates == True: + if row[fields.index("text")] in tweets_cache: + continue + else: + tweets_cache.append(row[fields.index("text")]) + except ValueError: + pass + writer.writerow(row) + i += 1 + if limit and i >= limit: + break + outf.close() + + +def parse_tweets_set( + filename, label, word_tokenizer=None, sent_tokenizer=None, skip_header=True +): + """ + Parse csv file containing tweets and output data a list of (text, label) tuples. + + :param filename: the input csv filename. + :param label: the label to be appended to each tweet contained in the csv file. + :param word_tokenizer: the tokenizer instance that will be used to tokenize + each sentence into tokens (e.g. WordPunctTokenizer() or BlanklineTokenizer()). + If no word_tokenizer is specified, tweets will not be tokenized. + :param sent_tokenizer: the tokenizer that will be used to split each tweet into + sentences. + :param skip_header: if True, skip the first line of the csv file (which usually + contains headers). + + :return: a list of (text, label) tuples. + """ + tweets = [] + if not sent_tokenizer: + sent_tokenizer = load("tokenizers/punkt/english.pickle") + + with codecs.open(filename, "rt") as csvfile: + reader = csv.reader(csvfile) + if skip_header == True: + next(reader, None) # skip the header + i = 0 + for tweet_id, text in reader: + # text = text[1] + i += 1 + sys.stdout.write(f"Loaded {i} tweets\r") + # Apply sentence and word tokenizer to text + if word_tokenizer: + tweet = [ + w + for sent in sent_tokenizer.tokenize(text) + for w in word_tokenizer.tokenize(sent) + ] + else: + tweet = text + tweets.append((tweet, label)) + + print(f"Loaded {i} tweets") + return tweets + + +# //////////////////////////////////////////////////////////// +# { Demos +# //////////////////////////////////////////////////////////// + + +def demo_tweets(trainer, n_instances=None, output=None): + """ + Train and test Naive Bayes classifier on 10000 tweets, tokenized using + TweetTokenizer. + Features are composed of: + + - 1000 most frequent unigrams + - 100 top bigrams (using BigramAssocMeasures.pmi) + + :param trainer: `train` method of a classifier. + :param n_instances: the number of total tweets that have to be used for + training and testing. Tweets will be equally split between positive and + negative. + :param output: the output file where results have to be reported. + """ + from nltk.corpus import stopwords, twitter_samples + from nltk.sentiment import SentimentAnalyzer + from nltk.tokenize import TweetTokenizer + + # Different customizations for the TweetTokenizer + tokenizer = TweetTokenizer(preserve_case=False) + # tokenizer = TweetTokenizer(preserve_case=True, strip_handles=True) + # tokenizer = TweetTokenizer(reduce_len=True, strip_handles=True) + + if n_instances is not None: + n_instances = int(n_instances / 2) + + fields = ["id", "text"] + positive_json = twitter_samples.abspath("positive_tweets.json") + positive_csv = "positive_tweets.csv" + json2csv_preprocess(positive_json, positive_csv, fields, limit=n_instances) + + negative_json = twitter_samples.abspath("negative_tweets.json") + negative_csv = "negative_tweets.csv" + json2csv_preprocess(negative_json, negative_csv, fields, limit=n_instances) + + neg_docs = parse_tweets_set(negative_csv, label="neg", word_tokenizer=tokenizer) + pos_docs = parse_tweets_set(positive_csv, label="pos", word_tokenizer=tokenizer) + + # We separately split subjective and objective instances to keep a balanced + # uniform class distribution in both train and test sets. + train_pos_docs, test_pos_docs = split_train_test(pos_docs) + train_neg_docs, test_neg_docs = split_train_test(neg_docs) + + training_tweets = train_pos_docs + train_neg_docs + testing_tweets = test_pos_docs + test_neg_docs + + sentim_analyzer = SentimentAnalyzer() + # stopwords = stopwords.words('english') + # all_words = [word for word in sentim_analyzer.all_words(training_tweets) if word.lower() not in stopwords] + all_words = [word for word in sentim_analyzer.all_words(training_tweets)] + + # Add simple unigram word features + unigram_feats = sentim_analyzer.unigram_word_feats(all_words, top_n=1000) + sentim_analyzer.add_feat_extractor(extract_unigram_feats, unigrams=unigram_feats) + + # Add bigram collocation features + bigram_collocs_feats = sentim_analyzer.bigram_collocation_feats( + [tweet[0] for tweet in training_tweets], top_n=100, min_freq=12 + ) + sentim_analyzer.add_feat_extractor( + extract_bigram_feats, bigrams=bigram_collocs_feats + ) + + training_set = sentim_analyzer.apply_features(training_tweets) + test_set = sentim_analyzer.apply_features(testing_tweets) + + classifier = sentim_analyzer.train(trainer, training_set) + # classifier = sentim_analyzer.train(trainer, training_set, max_iter=4) + try: + classifier.show_most_informative_features() + except AttributeError: + print( + "Your classifier does not provide a show_most_informative_features() method." + ) + results = sentim_analyzer.evaluate(test_set) + + if output: + extr = [f.__name__ for f in sentim_analyzer.feat_extractors] + output_markdown( + output, + Dataset="labeled_tweets", + Classifier=type(classifier).__name__, + Tokenizer=tokenizer.__class__.__name__, + Feats=extr, + Results=results, + Instances=n_instances, + ) + + +def demo_movie_reviews(trainer, n_instances=None, output=None): + """ + Train classifier on all instances of the Movie Reviews dataset. + The corpus has been preprocessed using the default sentence tokenizer and + WordPunctTokenizer. + Features are composed of: + + - most frequent unigrams + + :param trainer: `train` method of a classifier. + :param n_instances: the number of total reviews that have to be used for + training and testing. Reviews will be equally split between positive and + negative. + :param output: the output file where results have to be reported. + """ + from nltk.corpus import movie_reviews + from nltk.sentiment import SentimentAnalyzer + + if n_instances is not None: + n_instances = int(n_instances / 2) + + pos_docs = [ + (list(movie_reviews.words(pos_id)), "pos") + for pos_id in movie_reviews.fileids("pos")[:n_instances] + ] + neg_docs = [ + (list(movie_reviews.words(neg_id)), "neg") + for neg_id in movie_reviews.fileids("neg")[:n_instances] + ] + # We separately split positive and negative instances to keep a balanced + # uniform class distribution in both train and test sets. + train_pos_docs, test_pos_docs = split_train_test(pos_docs) + train_neg_docs, test_neg_docs = split_train_test(neg_docs) + + training_docs = train_pos_docs + train_neg_docs + testing_docs = test_pos_docs + test_neg_docs + + sentim_analyzer = SentimentAnalyzer() + all_words = sentim_analyzer.all_words(training_docs) + + # Add simple unigram word features + unigram_feats = sentim_analyzer.unigram_word_feats(all_words, min_freq=4) + sentim_analyzer.add_feat_extractor(extract_unigram_feats, unigrams=unigram_feats) + # Apply features to obtain a feature-value representation of our datasets + training_set = sentim_analyzer.apply_features(training_docs) + test_set = sentim_analyzer.apply_features(testing_docs) + + classifier = sentim_analyzer.train(trainer, training_set) + try: + classifier.show_most_informative_features() + except AttributeError: + print( + "Your classifier does not provide a show_most_informative_features() method." + ) + results = sentim_analyzer.evaluate(test_set) + + if output: + extr = [f.__name__ for f in sentim_analyzer.feat_extractors] + output_markdown( + output, + Dataset="Movie_reviews", + Classifier=type(classifier).__name__, + Tokenizer="WordPunctTokenizer", + Feats=extr, + Results=results, + Instances=n_instances, + ) + + +def demo_subjectivity(trainer, save_analyzer=False, n_instances=None, output=None): + """ + Train and test a classifier on instances of the Subjective Dataset by Pang and + Lee. The dataset is made of 5000 subjective and 5000 objective sentences. + All tokens (words and punctuation marks) are separated by a whitespace, so + we use the basic WhitespaceTokenizer to parse the data. + + :param trainer: `train` method of a classifier. + :param save_analyzer: if `True`, store the SentimentAnalyzer in a pickle file. + :param n_instances: the number of total sentences that have to be used for + training and testing. Sentences will be equally split between positive + and negative. + :param output: the output file where results have to be reported. + """ + from nltk.corpus import subjectivity + from nltk.sentiment import SentimentAnalyzer + + if n_instances is not None: + n_instances = int(n_instances / 2) + + subj_docs = [ + (sent, "subj") for sent in subjectivity.sents(categories="subj")[:n_instances] + ] + obj_docs = [ + (sent, "obj") for sent in subjectivity.sents(categories="obj")[:n_instances] + ] + + # We separately split subjective and objective instances to keep a balanced + # uniform class distribution in both train and test sets. + train_subj_docs, test_subj_docs = split_train_test(subj_docs) + train_obj_docs, test_obj_docs = split_train_test(obj_docs) + + training_docs = train_subj_docs + train_obj_docs + testing_docs = test_subj_docs + test_obj_docs + + sentim_analyzer = SentimentAnalyzer() + all_words_neg = sentim_analyzer.all_words( + [mark_negation(doc) for doc in training_docs] + ) + + # Add simple unigram word features handling negation + unigram_feats = sentim_analyzer.unigram_word_feats(all_words_neg, min_freq=4) + sentim_analyzer.add_feat_extractor(extract_unigram_feats, unigrams=unigram_feats) + + # Apply features to obtain a feature-value representation of our datasets + training_set = sentim_analyzer.apply_features(training_docs) + test_set = sentim_analyzer.apply_features(testing_docs) + + classifier = sentim_analyzer.train(trainer, training_set) + try: + classifier.show_most_informative_features() + except AttributeError: + print( + "Your classifier does not provide a show_most_informative_features() method." + ) + results = sentim_analyzer.evaluate(test_set) + + if save_analyzer == True: + sentim_analyzer.save_file(sentim_analyzer, "sa_subjectivity.pickle") + + if output: + extr = [f.__name__ for f in sentim_analyzer.feat_extractors] + output_markdown( + output, + Dataset="subjectivity", + Classifier=type(classifier).__name__, + Tokenizer="WhitespaceTokenizer", + Feats=extr, + Instances=n_instances, + Results=results, + ) + + return sentim_analyzer + + +def demo_sent_subjectivity(text): + """ + Classify a single sentence as subjective or objective using a stored + SentimentAnalyzer. + + :param text: a sentence whose subjectivity has to be classified. + """ + from nltk.classify import NaiveBayesClassifier + from nltk.tokenize import regexp + + word_tokenizer = regexp.WhitespaceTokenizer() + try: + sentim_analyzer = load("sa_subjectivity.pickle") + except LookupError: + print("Cannot find the sentiment analyzer you want to load.") + print("Training a new one using NaiveBayesClassifier.") + sentim_analyzer = demo_subjectivity(NaiveBayesClassifier.train, True) + + # Tokenize and convert to lower case + tokenized_text = [word.lower() for word in word_tokenizer.tokenize(text)] + print(sentim_analyzer.classify(tokenized_text)) + + +def demo_liu_hu_lexicon(sentence, plot=False): + """ + Basic example of sentiment classification using Liu and Hu opinion lexicon. + This function simply counts the number of positive, negative and neutral words + in the sentence and classifies it depending on which polarity is more represented. + Words that do not appear in the lexicon are considered as neutral. + + :param sentence: a sentence whose polarity has to be classified. + :param plot: if True, plot a visual representation of the sentence polarity. + """ + from nltk.corpus import opinion_lexicon + from nltk.tokenize import treebank + + tokenizer = treebank.TreebankWordTokenizer() + pos_words = 0 + neg_words = 0 + tokenized_sent = [word.lower() for word in tokenizer.tokenize(sentence)] + + x = list(range(len(tokenized_sent))) # x axis for the plot + y = [] + + for word in tokenized_sent: + if word in opinion_lexicon.positive(): + pos_words += 1 + y.append(1) # positive + elif word in opinion_lexicon.negative(): + neg_words += 1 + y.append(-1) # negative + else: + y.append(0) # neutral + + if pos_words > neg_words: + print("Positive") + elif pos_words < neg_words: + print("Negative") + elif pos_words == neg_words: + print("Neutral") + + if plot == True: + _show_plot( + x, y, x_labels=tokenized_sent, y_labels=["Negative", "Neutral", "Positive"] + ) + + +def demo_vader_instance(text): + """ + Output polarity scores for a text using Vader approach. + + :param text: a text whose polarity has to be evaluated. + """ + from nltk.sentiment import SentimentIntensityAnalyzer + + vader_analyzer = SentimentIntensityAnalyzer() + print(vader_analyzer.polarity_scores(text)) + + +def demo_vader_tweets(n_instances=None, output=None): + """ + Classify 10000 positive and negative tweets using Vader approach. + + :param n_instances: the number of total tweets that have to be classified. + :param output: the output file where results have to be reported. + """ + from collections import defaultdict + + from nltk.corpus import twitter_samples + from nltk.metrics import accuracy as eval_accuracy + from nltk.metrics import f_measure as eval_f_measure + from nltk.metrics import precision as eval_precision + from nltk.metrics import recall as eval_recall + from nltk.sentiment import SentimentIntensityAnalyzer + + if n_instances is not None: + n_instances = int(n_instances / 2) + + fields = ["id", "text"] + positive_json = twitter_samples.abspath("positive_tweets.json") + positive_csv = "positive_tweets.csv" + json2csv_preprocess( + positive_json, + positive_csv, + fields, + strip_off_emoticons=False, + limit=n_instances, + ) + + negative_json = twitter_samples.abspath("negative_tweets.json") + negative_csv = "negative_tweets.csv" + json2csv_preprocess( + negative_json, + negative_csv, + fields, + strip_off_emoticons=False, + limit=n_instances, + ) + + pos_docs = parse_tweets_set(positive_csv, label="pos") + neg_docs = parse_tweets_set(negative_csv, label="neg") + + # We separately split subjective and objective instances to keep a balanced + # uniform class distribution in both train and test sets. + train_pos_docs, test_pos_docs = split_train_test(pos_docs) + train_neg_docs, test_neg_docs = split_train_test(neg_docs) + + training_tweets = train_pos_docs + train_neg_docs + testing_tweets = test_pos_docs + test_neg_docs + + vader_analyzer = SentimentIntensityAnalyzer() + + gold_results = defaultdict(set) + test_results = defaultdict(set) + acc_gold_results = [] + acc_test_results = [] + labels = set() + num = 0 + for i, (text, label) in enumerate(testing_tweets): + labels.add(label) + gold_results[label].add(i) + acc_gold_results.append(label) + score = vader_analyzer.polarity_scores(text)["compound"] + if score > 0: + observed = "pos" + else: + observed = "neg" + num += 1 + acc_test_results.append(observed) + test_results[observed].add(i) + metrics_results = {} + for label in labels: + accuracy_score = eval_accuracy(acc_gold_results, acc_test_results) + metrics_results["Accuracy"] = accuracy_score + precision_score = eval_precision(gold_results[label], test_results[label]) + metrics_results[f"Precision [{label}]"] = precision_score + recall_score = eval_recall(gold_results[label], test_results[label]) + metrics_results[f"Recall [{label}]"] = recall_score + f_measure_score = eval_f_measure(gold_results[label], test_results[label]) + metrics_results[f"F-measure [{label}]"] = f_measure_score + + for result in sorted(metrics_results): + print(f"{result}: {metrics_results[result]}") + + if output: + output_markdown( + output, + Approach="Vader", + Dataset="labeled_tweets", + Instances=n_instances, + Results=metrics_results, + ) + + +if __name__ == "__main__": + from sklearn.svm import LinearSVC + + from nltk.classify import MaxentClassifier, NaiveBayesClassifier + from nltk.classify.scikitlearn import SklearnClassifier + from nltk.twitter.common import _outf_writer, extract_fields + + naive_bayes = NaiveBayesClassifier.train + svm = SklearnClassifier(LinearSVC()).train + maxent = MaxentClassifier.train + + demo_tweets(naive_bayes) + # demo_movie_reviews(svm) + # demo_subjectivity(svm) + # demo_sent_subjectivity("she's an artist , but hasn't picked up a brush in a year . ") + # demo_liu_hu_lexicon("This movie was actually neither that funny, nor super witty.", plot=True) + # demo_vader_instance("This movie was actually neither that funny, nor super witty.") + # demo_vader_tweets() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/vader.py b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/vader.py new file mode 100644 index 0000000..c9a5862 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/sentiment/vader.py @@ -0,0 +1,633 @@ +# Natural Language Toolkit: vader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: C.J. Hutto +# Ewan Klein (modifications) +# Pierpaolo Pantone <24alsecondo@gmail.com> (modifications) +# George Berry (modifications) +# Malavika Suresh (modifications) +# URL: +# For license information, see LICENSE.TXT +# +# Modifications to the original VADER code have been made in order to +# integrate it into NLTK. These have involved changes to +# ensure Python 3 compatibility, and refactoring to achieve greater modularity. + +""" +If you use the VADER sentiment analysis tools, please cite: + +Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for +Sentiment Analysis of Social Media Text. Eighth International Conference on +Weblogs and Social Media (ICWSM-14). Ann Arbor, MI, June 2014. +""" + +import math +import re +import string +from itertools import product + +import nltk.data +from nltk.util import pairwise + + +class VaderConstants: + """ + A class to keep the Vader lists and constants. + """ + + ##Constants## + # (empirically derived mean sentiment intensity rating increase for booster words) + B_INCR = 0.293 + B_DECR = -0.293 + + # (empirically derived mean sentiment intensity rating increase for using + # ALLCAPs to emphasize a word) + C_INCR = 0.733 + + N_SCALAR = -0.74 + + NEGATE = { + "aint", + "arent", + "cannot", + "cant", + "couldnt", + "darent", + "didnt", + "doesnt", + "ain't", + "aren't", + "can't", + "couldn't", + "daren't", + "didn't", + "doesn't", + "dont", + "hadnt", + "hasnt", + "havent", + "isnt", + "mightnt", + "mustnt", + "neither", + "don't", + "hadn't", + "hasn't", + "haven't", + "isn't", + "mightn't", + "mustn't", + "neednt", + "needn't", + "never", + "none", + "nope", + "nor", + "not", + "nothing", + "nowhere", + "oughtnt", + "shant", + "shouldnt", + "uhuh", + "wasnt", + "werent", + "oughtn't", + "shan't", + "shouldn't", + "uh-uh", + "wasn't", + "weren't", + "without", + "wont", + "wouldnt", + "won't", + "wouldn't", + "rarely", + "seldom", + "despite", + } + + # booster/dampener 'intensifiers' or 'degree adverbs' + # https://en.wiktionary.org/wiki/Category:English_degree_adverbs + + BOOSTER_DICT = { + "absolutely": B_INCR, + "amazingly": B_INCR, + "awfully": B_INCR, + "completely": B_INCR, + "considerably": B_INCR, + "decidedly": B_INCR, + "deeply": B_INCR, + "effing": B_INCR, + "enormously": B_INCR, + "entirely": B_INCR, + "especially": B_INCR, + "exceptionally": B_INCR, + "extremely": B_INCR, + "fabulously": B_INCR, + "flipping": B_INCR, + "flippin": B_INCR, + "fricking": B_INCR, + "frickin": B_INCR, + "frigging": B_INCR, + "friggin": B_INCR, + "fully": B_INCR, + "fucking": B_INCR, + "greatly": B_INCR, + "hella": B_INCR, + "highly": B_INCR, + "hugely": B_INCR, + "incredibly": B_INCR, + "intensely": B_INCR, + "majorly": B_INCR, + "more": B_INCR, + "most": B_INCR, + "particularly": B_INCR, + "purely": B_INCR, + "quite": B_INCR, + "really": B_INCR, + "remarkably": B_INCR, + "so": B_INCR, + "substantially": B_INCR, + "thoroughly": B_INCR, + "totally": B_INCR, + "tremendously": B_INCR, + "uber": B_INCR, + "unbelievably": B_INCR, + "unusually": B_INCR, + "utterly": B_INCR, + "very": B_INCR, + "almost": B_DECR, + "barely": B_DECR, + "hardly": B_DECR, + "just enough": B_DECR, + "kind of": B_DECR, + "kinda": B_DECR, + "kindof": B_DECR, + "kind-of": B_DECR, + "less": B_DECR, + "little": B_DECR, + "marginally": B_DECR, + "occasionally": B_DECR, + "partly": B_DECR, + "scarcely": B_DECR, + "slightly": B_DECR, + "somewhat": B_DECR, + "sort of": B_DECR, + "sorta": B_DECR, + "sortof": B_DECR, + "sort-of": B_DECR, + } + + # check for special case idioms using a sentiment-laden keyword known to SAGE + SPECIAL_CASE_IDIOMS = { + "the shit": 3, + "the bomb": 3, + "bad ass": 1.5, + "yeah right": -2, + "cut the mustard": 2, + "kiss of death": -1.5, + "hand to mouth": -2, + } + + # for removing punctuation + REGEX_REMOVE_PUNCTUATION = re.compile(f"[{re.escape(string.punctuation)}]") + + PUNC_LIST = [ + ".", + "!", + "?", + ",", + ";", + ":", + "-", + "'", + '"', + "!!", + "!!!", + "??", + "???", + "?!?", + "!?!", + "?!?!", + "!?!?", + ] + + def __init__(self): + pass + + def negated(self, input_words, include_nt=True): + """ + Determine if input contains negation words + """ + neg_words = self.NEGATE + if any(word.lower() in neg_words for word in input_words): + return True + if include_nt: + if any("n't" in word.lower() for word in input_words): + return True + for first, second in pairwise(input_words): + if second.lower() == "least" and first.lower() != "at": + return True + return False + + def normalize(self, score, alpha=15): + """ + Normalize the score to be between -1 and 1 using an alpha that + approximates the max expected value + """ + norm_score = score / math.sqrt((score * score) + alpha) + return norm_score + + def scalar_inc_dec(self, word, valence, is_cap_diff): + """ + Check if the preceding words increase, decrease, or negate/nullify the + valence + """ + scalar = 0.0 + word_lower = word.lower() + if word_lower in self.BOOSTER_DICT: + scalar = self.BOOSTER_DICT[word_lower] + if valence < 0: + scalar *= -1 + # check if booster/dampener word is in ALLCAPS (while others aren't) + if word.isupper() and is_cap_diff: + if valence > 0: + scalar += self.C_INCR + else: + scalar -= self.C_INCR + return scalar + + +class SentiText: + """ + Identify sentiment-relevant string-level properties of input text. + """ + + def __init__(self, text, punc_list, regex_remove_punctuation): + if not isinstance(text, str): + text = str(text.encode("utf-8")) + self.text = text + self.PUNC_LIST = punc_list + self.REGEX_REMOVE_PUNCTUATION = regex_remove_punctuation + self.words_and_emoticons = self._words_and_emoticons() + # doesn't separate words from + # adjacent punctuation (keeps emoticons & contractions) + self.is_cap_diff = self.allcap_differential(self.words_and_emoticons) + + def _words_plus_punc(self): + """ + Returns mapping of form: + { + 'cat,': 'cat', + ',cat': 'cat', + } + """ + no_punc_text = self.REGEX_REMOVE_PUNCTUATION.sub("", self.text) + # removes punctuation (but loses emoticons & contractions) + words_only = no_punc_text.split() + # remove singletons + words_only = {w for w in words_only if len(w) > 1} + # the product gives ('cat', ',') and (',', 'cat') + punc_before = {"".join(p): p[1] for p in product(self.PUNC_LIST, words_only)} + punc_after = {"".join(p): p[0] for p in product(words_only, self.PUNC_LIST)} + words_punc_dict = punc_before + words_punc_dict.update(punc_after) + return words_punc_dict + + def _words_and_emoticons(self): + """ + Removes leading and trailing puncutation + Leaves contractions and most emoticons + Does not preserve punc-plus-letter emoticons (e.g. :D) + """ + wes = self.text.split() + words_punc_dict = self._words_plus_punc() + wes = [we for we in wes if len(we) > 1] + for i, we in enumerate(wes): + if we in words_punc_dict: + wes[i] = words_punc_dict[we] + return wes + + def allcap_differential(self, words): + """ + Check whether just some words in the input are ALL CAPS + + :param list words: The words to inspect + :returns: `True` if some but not all items in `words` are ALL CAPS + """ + is_different = False + allcap_words = 0 + for word in words: + if word.isupper(): + allcap_words += 1 + cap_differential = len(words) - allcap_words + if 0 < cap_differential < len(words): + is_different = True + return is_different + + +class SentimentIntensityAnalyzer: + """ + Give a sentiment intensity score to sentences. + """ + + def __init__( + self, + lexicon_file="sentiment/vader_lexicon.zip/vader_lexicon/vader_lexicon.txt", + ): + self.lexicon_file = nltk.data.load(lexicon_file) + self.lexicon = self.make_lex_dict() + self.constants = VaderConstants() + + def make_lex_dict(self): + """ + Convert lexicon file to a dictionary + """ + lex_dict = {} + for line in self.lexicon_file.split("\n"): + (word, measure) = line.strip().split("\t")[0:2] + lex_dict[word] = float(measure) + return lex_dict + + def polarity_scores(self, text): + """ + Return a float for sentiment strength based on the input text. + Positive values are positive valence, negative value are negative + valence. + + :note: Hashtags are not taken into consideration (e.g. #BAD is neutral). If you + are interested in processing the text in the hashtags too, then we recommend + preprocessing your data to remove the #, after which the hashtag text may be + matched as if it was a normal word in the sentence. + """ + # text, words_and_emoticons, is_cap_diff = self.preprocess(text) + sentitext = SentiText( + text, self.constants.PUNC_LIST, self.constants.REGEX_REMOVE_PUNCTUATION + ) + sentiments = [] + words_and_emoticons = sentitext.words_and_emoticons + for item in words_and_emoticons: + valence = 0 + i = words_and_emoticons.index(item) + if ( + i < len(words_and_emoticons) - 1 + and item.lower() == "kind" + and words_and_emoticons[i + 1].lower() == "of" + ) or item.lower() in self.constants.BOOSTER_DICT: + sentiments.append(valence) + continue + + sentiments = self.sentiment_valence(valence, sentitext, item, i, sentiments) + + sentiments = self._but_check(words_and_emoticons, sentiments) + + return self.score_valence(sentiments, text) + + def sentiment_valence(self, valence, sentitext, item, i, sentiments): + is_cap_diff = sentitext.is_cap_diff + words_and_emoticons = sentitext.words_and_emoticons + item_lowercase = item.lower() + if item_lowercase in self.lexicon: + # get the sentiment valence + valence = self.lexicon[item_lowercase] + + # check if sentiment laden word is in ALL CAPS (while others aren't) + if item.isupper() and is_cap_diff: + if valence > 0: + valence += self.constants.C_INCR + else: + valence -= self.constants.C_INCR + + for start_i in range(0, 3): + if ( + i > start_i + and words_and_emoticons[i - (start_i + 1)].lower() + not in self.lexicon + ): + # dampen the scalar modifier of preceding words and emoticons + # (excluding the ones that immediately preceed the item) based + # on their distance from the current item. + s = self.constants.scalar_inc_dec( + words_and_emoticons[i - (start_i + 1)], valence, is_cap_diff + ) + if start_i == 1 and s != 0: + s = s * 0.95 + if start_i == 2 and s != 0: + s = s * 0.9 + valence = valence + s + valence = self._never_check( + valence, words_and_emoticons, start_i, i + ) + if start_i == 2: + valence = self._idioms_check(valence, words_and_emoticons, i) + + # future work: consider other sentiment-laden idioms + # other_idioms = + # {"back handed": -2, "blow smoke": -2, "blowing smoke": -2, + # "upper hand": 1, "break a leg": 2, + # "cooking with gas": 2, "in the black": 2, "in the red": -2, + # "on the ball": 2,"under the weather": -2} + + valence = self._least_check(valence, words_and_emoticons, i) + + sentiments.append(valence) + return sentiments + + def _least_check(self, valence, words_and_emoticons, i): + # check for negation case using "least" + if ( + i > 1 + and words_and_emoticons[i - 1].lower() not in self.lexicon + and words_and_emoticons[i - 1].lower() == "least" + ): + if ( + words_and_emoticons[i - 2].lower() != "at" + and words_and_emoticons[i - 2].lower() != "very" + ): + valence = valence * self.constants.N_SCALAR + elif ( + i > 0 + and words_and_emoticons[i - 1].lower() not in self.lexicon + and words_and_emoticons[i - 1].lower() == "least" + ): + valence = valence * self.constants.N_SCALAR + return valence + + def _but_check(self, words_and_emoticons, sentiments): + words_and_emoticons = [w_e.lower() for w_e in words_and_emoticons] + but = {"but"} & set(words_and_emoticons) + if but: + bi = words_and_emoticons.index(next(iter(but))) + for sidx, sentiment in enumerate(sentiments): + if sidx < bi: + sentiments[sidx] = sentiment * 0.5 + elif sidx > bi: + sentiments[sidx] = sentiment * 1.5 + return sentiments + + def _idioms_check(self, valence, words_and_emoticons, i): + onezero = f"{words_and_emoticons[i - 1]} {words_and_emoticons[i]}" + + twoonezero = "{} {} {}".format( + words_and_emoticons[i - 2], + words_and_emoticons[i - 1], + words_and_emoticons[i], + ) + + twoone = f"{words_and_emoticons[i - 2]} {words_and_emoticons[i - 1]}" + + threetwoone = "{} {} {}".format( + words_and_emoticons[i - 3], + words_and_emoticons[i - 2], + words_and_emoticons[i - 1], + ) + + threetwo = "{} {}".format( + words_and_emoticons[i - 3], words_and_emoticons[i - 2] + ) + + sequences = [onezero, twoonezero, twoone, threetwoone, threetwo] + + for seq in sequences: + if seq in self.constants.SPECIAL_CASE_IDIOMS: + valence = self.constants.SPECIAL_CASE_IDIOMS[seq] + break + + if len(words_and_emoticons) - 1 > i: + zeroone = f"{words_and_emoticons[i]} {words_and_emoticons[i + 1]}" + if zeroone in self.constants.SPECIAL_CASE_IDIOMS: + valence = self.constants.SPECIAL_CASE_IDIOMS[zeroone] + if len(words_and_emoticons) - 1 > i + 1: + zeroonetwo = "{} {} {}".format( + words_and_emoticons[i], + words_and_emoticons[i + 1], + words_and_emoticons[i + 2], + ) + if zeroonetwo in self.constants.SPECIAL_CASE_IDIOMS: + valence = self.constants.SPECIAL_CASE_IDIOMS[zeroonetwo] + + # check for booster/dampener bi-grams such as 'sort of' or 'kind of' + if ( + threetwo in self.constants.BOOSTER_DICT + or twoone in self.constants.BOOSTER_DICT + ): + valence = valence + self.constants.B_DECR + return valence + + def _never_check(self, valence, words_and_emoticons, start_i, i): + if start_i == 0: + if self.constants.negated([words_and_emoticons[i - 1]]): + valence = valence * self.constants.N_SCALAR + if start_i == 1: + if words_and_emoticons[i - 2] == "never" and ( + words_and_emoticons[i - 1] == "so" + or words_and_emoticons[i - 1] == "this" + ): + valence = valence * 1.5 + elif self.constants.negated([words_and_emoticons[i - (start_i + 1)]]): + valence = valence * self.constants.N_SCALAR + if start_i == 2: + if ( + words_and_emoticons[i - 3] == "never" + and ( + words_and_emoticons[i - 2] == "so" + or words_and_emoticons[i - 2] == "this" + ) + or ( + words_and_emoticons[i - 1] == "so" + or words_and_emoticons[i - 1] == "this" + ) + ): + valence = valence * 1.25 + elif self.constants.negated([words_and_emoticons[i - (start_i + 1)]]): + valence = valence * self.constants.N_SCALAR + return valence + + def _punctuation_emphasis(self, sum_s, text): + # add emphasis from exclamation points and question marks + ep_amplifier = self._amplify_ep(text) + qm_amplifier = self._amplify_qm(text) + punct_emph_amplifier = ep_amplifier + qm_amplifier + return punct_emph_amplifier + + def _amplify_ep(self, text): + # check for added emphasis resulting from exclamation points (up to 4 of them) + ep_count = text.count("!") + if ep_count > 4: + ep_count = 4 + # (empirically derived mean sentiment intensity rating increase for + # exclamation points) + ep_amplifier = ep_count * 0.292 + return ep_amplifier + + def _amplify_qm(self, text): + # check for added emphasis resulting from question marks (2 or 3+) + qm_count = text.count("?") + qm_amplifier = 0 + if qm_count > 1: + if qm_count <= 3: + # (empirically derived mean sentiment intensity rating increase for + # question marks) + qm_amplifier = qm_count * 0.18 + else: + qm_amplifier = 0.96 + return qm_amplifier + + def _sift_sentiment_scores(self, sentiments): + # want separate positive versus negative sentiment scores + pos_sum = 0.0 + neg_sum = 0.0 + neu_count = 0 + for sentiment_score in sentiments: + if sentiment_score > 0: + pos_sum += ( + float(sentiment_score) + 1 + ) # compensates for neutral words that are counted as 1 + if sentiment_score < 0: + neg_sum += ( + float(sentiment_score) - 1 + ) # when used with math.fabs(), compensates for neutrals + if sentiment_score == 0: + neu_count += 1 + return pos_sum, neg_sum, neu_count + + def score_valence(self, sentiments, text): + if sentiments: + sum_s = float(sum(sentiments)) + # compute and add emphasis from punctuation in text + punct_emph_amplifier = self._punctuation_emphasis(sum_s, text) + if sum_s > 0: + sum_s += punct_emph_amplifier + elif sum_s < 0: + sum_s -= punct_emph_amplifier + + compound = self.constants.normalize(sum_s) + # discriminate between positive, negative and neutral sentiment scores + pos_sum, neg_sum, neu_count = self._sift_sentiment_scores(sentiments) + + if pos_sum > math.fabs(neg_sum): + pos_sum += punct_emph_amplifier + elif pos_sum < math.fabs(neg_sum): + neg_sum -= punct_emph_amplifier + + total = pos_sum + math.fabs(neg_sum) + neu_count + pos = math.fabs(pos_sum / total) + neg = math.fabs(neg_sum / total) + neu = math.fabs(neu_count / total) + + else: + compound = 0.0 + pos = 0.0 + neg = 0.0 + neu = 0.0 + + sentiment_dict = { + "neg": round(neg, 3), + "neu": round(neu, 3), + "pos": round(pos, 3), + "compound": round(compound, 4), + } + + return sentiment_dict diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__init__.py new file mode 100644 index 0000000..ff2623a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__init__.py @@ -0,0 +1,34 @@ +# Natural Language Toolkit: Stemmers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK Stemmers + +Interfaces used to remove morphological affixes from words, leaving +only the word stem. Stemming algorithms aim to remove those affixes +required for eg. grammatical role, tense, derivational morphology +leaving only the stem of the word. This is a difficult problem due to +irregular words (eg. common verbs in English), complicated +morphological rules, and part-of-speech and sense ambiguities +(eg. ``ceil-`` is not the stem of ``ceiling``). + +StemmerI defines a standard interface for stemmers. +""" + +from nltk.stem.api import StemmerI +from nltk.stem.arlstem import ARLSTem +from nltk.stem.arlstem2 import ARLSTem2 +from nltk.stem.cistem import Cistem +from nltk.stem.isri import ISRIStemmer +from nltk.stem.lancaster import LancasterStemmer +from nltk.stem.porter import PorterStemmer +from nltk.stem.regexp import RegexpStemmer +from nltk.stem.rslp import RSLPStemmer +from nltk.stem.snowball import SnowballStemmer +from nltk.stem.wordnet import WordNetLemmatizer diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b42d9f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..7000217 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem.cpython-310.pyc new file mode 100644 index 0000000..2f8e080 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem2.cpython-310.pyc new file mode 100644 index 0000000..592c3a6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/arlstem2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/cistem.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/cistem.cpython-310.pyc new file mode 100644 index 0000000..d39904f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/cistem.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/isri.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/isri.cpython-310.pyc new file mode 100644 index 0000000..6c7208a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/isri.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/lancaster.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/lancaster.cpython-310.pyc new file mode 100644 index 0000000..b6ac522 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/lancaster.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/porter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/porter.cpython-310.pyc new file mode 100644 index 0000000..5367be6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/porter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/regexp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/regexp.cpython-310.pyc new file mode 100644 index 0000000..d930e66 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/regexp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/rslp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/rslp.cpython-310.pyc new file mode 100644 index 0000000..3dde087 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/rslp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/snowball.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/snowball.cpython-310.pyc new file mode 100644 index 0000000..6154fdd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/snowball.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..159b9d0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/wordnet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/wordnet.cpython-310.pyc new file mode 100644 index 0000000..7bc99e0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/__pycache__/wordnet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/api.py new file mode 100644 index 0000000..b478363 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/api.py @@ -0,0 +1,27 @@ +# Natural Language Toolkit: Stemmer Interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from abc import ABCMeta, abstractmethod + + +class StemmerI(metaclass=ABCMeta): + """ + A processing interface for removing morphological affixes from + words. This process is known as stemming. + + """ + + @abstractmethod + def stem(self, token): + """ + Strip affixes from the token and return the stem. + + :param token: The token that should be stemmed. + :type token: str + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem.py new file mode 100644 index 0000000..e19c794 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem.py @@ -0,0 +1,361 @@ +# +# Natural Language Toolkit: ARLSTem Stemmer +# +# Copyright (C) 2001-2023 NLTK Project +# +# Author: Kheireddine Abainia (x-programer) +# Algorithms: Kheireddine Abainia +# Siham Ouamour +# Halim Sayoud +# URL: +# For license information, see LICENSE.TXT + + +""" +ARLSTem Arabic Stemmer +The details about the implementation of this algorithm are described in: +K. Abainia, S. Ouamour and H. Sayoud, A Novel Robust Arabic Light Stemmer , +Journal of Experimental & Theoretical Artificial Intelligence (JETAI'17), +Vol. 29, No. 3, 2017, pp. 557-573. +The ARLSTem is a light Arabic stemmer that is based on removing the affixes +from the word (i.e. prefixes, suffixes and infixes). It was evaluated and +compared to several other stemmers using Paice's parameters (under-stemming +index, over-stemming index and stemming weight), and the results showed that +ARLSTem is promising and producing high performances. This stemmer is not +based on any dictionary and can be used on-line effectively. +""" +import re + +from nltk.stem.api import StemmerI + + +class ARLSTem(StemmerI): + """ + ARLSTem stemmer : a light Arabic Stemming algorithm without any dictionary. + Department of Telecommunication & Information Processing. USTHB University, + Algiers, Algeria. + ARLSTem.stem(token) returns the Arabic stem for the input token. + The ARLSTem Stemmer requires that all tokens are encoded using Unicode + encoding. + """ + + def __init__(self): + # different Alif with hamza + self.re_hamzated_alif = re.compile(r"[\u0622\u0623\u0625]") + self.re_alifMaqsura = re.compile(r"[\u0649]") + self.re_diacritics = re.compile(r"[\u064B-\u065F]") + + # Alif Laam, Laam Laam, Fa Laam, Fa Ba + self.pr2 = ["\u0627\u0644", "\u0644\u0644", "\u0641\u0644", "\u0641\u0628"] + # Ba Alif Laam, Kaaf Alif Laam, Waaw Alif Laam + self.pr3 = ["\u0628\u0627\u0644", "\u0643\u0627\u0644", "\u0648\u0627\u0644"] + # Fa Laam Laam, Waaw Laam Laam + self.pr32 = ["\u0641\u0644\u0644", "\u0648\u0644\u0644"] + # Fa Ba Alif Laam, Waaw Ba Alif Laam, Fa Kaaf Alif Laam + self.pr4 = [ + "\u0641\u0628\u0627\u0644", + "\u0648\u0628\u0627\u0644", + "\u0641\u0643\u0627\u0644", + ] + + # Kaf Yaa, Kaf Miim + self.su2 = ["\u0643\u064A", "\u0643\u0645"] + # Ha Alif, Ha Miim + self.su22 = ["\u0647\u0627", "\u0647\u0645"] + # Kaf Miim Alif, Kaf Noon Shadda + self.su3 = ["\u0643\u0645\u0627", "\u0643\u0646\u0651"] + # Ha Miim Alif, Ha Noon Shadda + self.su32 = ["\u0647\u0645\u0627", "\u0647\u0646\u0651"] + + # Alif Noon, Ya Noon, Waaw Noon + self.pl_si2 = ["\u0627\u0646", "\u064A\u0646", "\u0648\u0646"] + # Taa Alif Noon, Taa Ya Noon + self.pl_si3 = ["\u062A\u0627\u0646", "\u062A\u064A\u0646"] + + # Alif Noon, Waaw Noon + self.verb_su2 = ["\u0627\u0646", "\u0648\u0646"] + # Siin Taa, Siin Yaa + self.verb_pr2 = ["\u0633\u062A", "\u0633\u064A"] + # Siin Alif, Siin Noon + self.verb_pr22 = ["\u0633\u0627", "\u0633\u0646"] + # Lam Noon, Lam Taa, Lam Yaa, Lam Hamza + self.verb_pr33 = [ + "\u0644\u0646", + "\u0644\u062A", + "\u0644\u064A", + "\u0644\u0623", + ] + # Taa Miim Alif, Taa Noon Shadda + self.verb_suf3 = ["\u062A\u0645\u0627", "\u062A\u0646\u0651"] + # Noon Alif, Taa Miim, Taa Alif, Waaw Alif + self.verb_suf2 = [ + "\u0646\u0627", + "\u062A\u0645", + "\u062A\u0627", + "\u0648\u0627", + ] + # Taa, Alif, Noon + self.verb_suf1 = ["\u062A", "\u0627", "\u0646"] + + def stem(self, token): + """ + call this function to get the word's stem based on ARLSTem . + """ + try: + if token is None: + raise ValueError( + "The word could not be stemmed, because \ + it is empty !" + ) + # remove Arabic diacritics and replace some letters with others + token = self.norm(token) + # strip common prefixes of the nouns + pre = self.pref(token) + if pre is not None: + token = pre + # strip the suffixes which are common to nouns and verbs + token = self.suff(token) + # transform a plural noun to a singular noun + ps = self.plur2sing(token) + if ps is None: + # transform from the feminine form to the masculine form + fm = self.fem2masc(token) + if fm is not None: + return fm + else: + if pre is None: # if the prefixes are not stripped + # strip the verb prefixes and suffixes + return self.verb(token) + else: + return ps + return token + except ValueError as e: + print(e) + + def norm(self, token): + """ + normalize the word by removing diacritics, replacing hamzated Alif + with Alif replacing AlifMaqsura with Yaa and removing Waaw at the + beginning. + """ + # strip Arabic diacritics + token = self.re_diacritics.sub("", token) + # replace Hamzated Alif with Alif bare + token = self.re_hamzated_alif.sub("\u0627", token) + # replace alifMaqsura with Yaa + token = self.re_alifMaqsura.sub("\u064A", token) + # strip the Waaw from the word beginning if the remaining is 3 letters + # at least + if token.startswith("\u0648") and len(token) > 3: + token = token[1:] + return token + + def pref(self, token): + """ + remove prefixes from the words' beginning. + """ + if len(token) > 5: + for p3 in self.pr3: + if token.startswith(p3): + return token[3:] + if len(token) > 6: + for p4 in self.pr4: + if token.startswith(p4): + return token[4:] + if len(token) > 5: + for p3 in self.pr32: + if token.startswith(p3): + return token[3:] + if len(token) > 4: + for p2 in self.pr2: + if token.startswith(p2): + return token[2:] + + def suff(self, token): + """ + remove suffixes from the word's end. + """ + if token.endswith("\u0643") and len(token) > 3: + return token[:-1] + if len(token) > 4: + for s2 in self.su2: + if token.endswith(s2): + return token[:-2] + if len(token) > 5: + for s3 in self.su3: + if token.endswith(s3): + return token[:-3] + if token.endswith("\u0647") and len(token) > 3: + token = token[:-1] + return token + if len(token) > 4: + for s2 in self.su22: + if token.endswith(s2): + return token[:-2] + if len(token) > 5: + for s3 in self.su32: + if token.endswith(s3): + return token[:-3] + if token.endswith("\u0646\u0627") and len(token) > 4: + return token[:-2] + return token + + def fem2masc(self, token): + """ + transform the word from the feminine form to the masculine form. + """ + if token.endswith("\u0629") and len(token) > 3: + return token[:-1] + + def plur2sing(self, token): + """ + transform the word from the plural form to the singular form. + """ + if len(token) > 4: + for ps2 in self.pl_si2: + if token.endswith(ps2): + return token[:-2] + if len(token) > 5: + for ps3 in self.pl_si3: + if token.endswith(ps3): + return token[:-3] + if len(token) > 3 and token.endswith("\u0627\u062A"): + return token[:-2] + if len(token) > 3 and token.startswith("\u0627") and token[2] == "\u0627": + return token[:2] + token[3:] + if len(token) > 4 and token.startswith("\u0627") and token[-2] == "\u0627": + return token[1:-2] + token[-1] + + def verb(self, token): + """ + stem the verb prefixes and suffixes or both + """ + vb = self.verb_t1(token) + if vb is not None: + return vb + vb = self.verb_t2(token) + if vb is not None: + return vb + vb = self.verb_t3(token) + if vb is not None: + return vb + vb = self.verb_t4(token) + if vb is not None: + return vb + vb = self.verb_t5(token) + if vb is not None: + return vb + return self.verb_t6(token) + + def verb_t1(self, token): + """ + stem the present prefixes and suffixes + """ + if len(token) > 5 and token.startswith("\u062A"): # Taa + for s2 in self.pl_si2: + if token.endswith(s2): + return token[1:-2] + if len(token) > 5 and token.startswith("\u064A"): # Yaa + for s2 in self.verb_su2: + if token.endswith(s2): + return token[1:-2] + if len(token) > 4 and token.startswith("\u0627"): # Alif + # Waaw Alif + if len(token) > 5 and token.endswith("\u0648\u0627"): + return token[1:-2] + # Yaa + if token.endswith("\u064A"): + return token[1:-1] + # Alif + if token.endswith("\u0627"): + return token[1:-1] + # Noon + if token.endswith("\u0646"): + return token[1:-1] + # ^Yaa, Noon$ + if len(token) > 4 and token.startswith("\u064A") and token.endswith("\u0646"): + return token[1:-1] + # ^Taa, Noon$ + if len(token) > 4 and token.startswith("\u062A") and token.endswith("\u0646"): + return token[1:-1] + + def verb_t2(self, token): + """ + stem the future prefixes and suffixes + """ + if len(token) > 6: + for s2 in self.pl_si2: + # ^Siin Taa + if token.startswith(self.verb_pr2[0]) and token.endswith(s2): + return token[2:-2] + # ^Siin Yaa, Alif Noon$ + if token.startswith(self.verb_pr2[1]) and token.endswith(self.pl_si2[0]): + return token[2:-2] + # ^Siin Yaa, Waaw Noon$ + if token.startswith(self.verb_pr2[1]) and token.endswith(self.pl_si2[2]): + return token[2:-2] + # ^Siin Taa, Noon$ + if ( + len(token) > 5 + and token.startswith(self.verb_pr2[0]) + and token.endswith("\u0646") + ): + return token[2:-1] + # ^Siin Yaa, Noon$ + if ( + len(token) > 5 + and token.startswith(self.verb_pr2[1]) + and token.endswith("\u0646") + ): + return token[2:-1] + + def verb_t3(self, token): + """ + stem the present suffixes + """ + if len(token) > 5: + for su3 in self.verb_suf3: + if token.endswith(su3): + return token[:-3] + if len(token) > 4: + for su2 in self.verb_suf2: + if token.endswith(su2): + return token[:-2] + if len(token) > 3: + for su1 in self.verb_suf1: + if token.endswith(su1): + return token[:-1] + + def verb_t4(self, token): + """ + stem the present prefixes + """ + if len(token) > 3: + for pr1 in self.verb_suf1: + if token.startswith(pr1): + return token[1:] + if token.startswith("\u064A"): + return token[1:] + + def verb_t5(self, token): + """ + stem the future prefixes + """ + if len(token) > 4: + for pr2 in self.verb_pr22: + if token.startswith(pr2): + return token[2:] + for pr2 in self.verb_pr2: + if token.startswith(pr2): + return token[2:] + return token + + def verb_t6(self, token): + """ + stem the order prefixes + """ + if len(token) > 4: + for pr3 in self.verb_pr33: + if token.startswith(pr3): + return token[2:] + return token diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem2.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem2.py new file mode 100644 index 0000000..1a07f0f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/arlstem2.py @@ -0,0 +1,457 @@ +# +# Natural Language Toolkit: ARLSTem Stemmer v2 +# +# Copyright (C) 2001-2023 NLTK Project +# +# Author: Kheireddine Abainia (x-programer) +# Algorithms: Kheireddine Abainia +# Hamza Rebbani +# URL: +# For license information, see LICENSE.TXT + + +""" +ARLSTem2 Arabic Light Stemmer +The details about the implementation of this algorithm are described in: +K. Abainia and H. Rebbani, Comparing the Effectiveness of the Improved ARLSTem +Algorithm with Existing Arabic Light Stemmers, International Conference on +Theoretical and Applicative Aspects of Computer Science (ICTAACS'19), Skikda, +Algeria, December 15-16, 2019. +ARLSTem2 is an Arabic light stemmer based on removing the affixes from +the words (i.e. prefixes, suffixes and infixes). It is an improvement +of the previous Arabic light stemmer (ARLSTem). The new version was compared to +the original algorithm and several existing Arabic light stemmers, where the +results showed that the new version considerably improves the under-stemming +errors that are common to light stemmers. Both ARLSTem and ARLSTem2 can be run +online and do not use any dictionary. +""" +import re + +from nltk.stem.api import StemmerI + + +class ARLSTem2(StemmerI): + """ + Return a stemmed Arabic word after removing affixes. This an improved + version of the previous algorithm, which reduces under-stemming errors. + Typically used in Arabic search engine, information retrieval and NLP. + + >>> from nltk.stem import arlstem2 + >>> stemmer = ARLSTem2() + >>> word = stemmer.stem('يعمل') + >>> print(word) + عمل + + :param token: The input Arabic word (unicode) to be stemmed + :type token: unicode + :return: A unicode Arabic word + """ + + def __init__(self): + # different Alif with hamza + self.re_hamzated_alif = re.compile(r"[\u0622\u0623\u0625]") + self.re_alifMaqsura = re.compile(r"[\u0649]") + self.re_diacritics = re.compile(r"[\u064B-\u065F]") + + # Alif Laam, Laam Laam, Fa Laam, Fa Ba + self.pr2 = ["\u0627\u0644", "\u0644\u0644", "\u0641\u0644", "\u0641\u0628"] + # Ba Alif Laam, Kaaf Alif Laam, Waaw Alif Laam + self.pr3 = ["\u0628\u0627\u0644", "\u0643\u0627\u0644", "\u0648\u0627\u0644"] + # Fa Laam Laam, Waaw Laam Laam + self.pr32 = ["\u0641\u0644\u0644", "\u0648\u0644\u0644"] + # Fa Ba Alif Laam, Waaw Ba Alif Laam, Fa Kaaf Alif Laam + self.pr4 = [ + "\u0641\u0628\u0627\u0644", + "\u0648\u0628\u0627\u0644", + "\u0641\u0643\u0627\u0644", + ] + + # Kaf Yaa, Kaf Miim + self.su2 = ["\u0643\u064A", "\u0643\u0645"] + # Ha Alif, Ha Miim + self.su22 = ["\u0647\u0627", "\u0647\u0645"] + # Kaf Miim Alif, Kaf Noon Shadda + self.su3 = ["\u0643\u0645\u0627", "\u0643\u0646\u0651"] + # Ha Miim Alif, Ha Noon Shadda + self.su32 = ["\u0647\u0645\u0627", "\u0647\u0646\u0651"] + + # Alif Noon, Ya Noon, Waaw Noon + self.pl_si2 = ["\u0627\u0646", "\u064A\u0646", "\u0648\u0646"] + # Taa Alif Noon, Taa Ya Noon + self.pl_si3 = ["\u062A\u0627\u0646", "\u062A\u064A\u0646"] + + # Alif Noon, Waaw Noon + self.verb_su2 = ["\u0627\u0646", "\u0648\u0646"] + # Siin Taa, Siin Yaa + self.verb_pr2 = ["\u0633\u062A", "\u0633\u064A"] + # Siin Alif, Siin Noon + self.verb_pr22 = ["\u0633\u0627", "\u0633\u0646"] + # Lam Noon, Lam Taa, Lam Yaa, Lam Hamza + self.verb_pr33 = [ + "\u0644\u0646", + "\u0644\u062A", + "\u0644\u064A", + "\u0644\u0623", + ] + # Taa Miim Alif, Taa Noon Shadda + self.verb_suf3 = ["\u062A\u0645\u0627", "\u062A\u0646\u0651"] + # Noon Alif, Taa Miim, Taa Alif, Waaw Alif + self.verb_suf2 = [ + "\u0646\u0627", + "\u062A\u0645", + "\u062A\u0627", + "\u0648\u0627", + ] + # Taa, Alif, Noon + self.verb_suf1 = ["\u062A", "\u0627", "\u0646"] + + def stem1(self, token): + """ + call this function to get the first stem + """ + try: + if token is None: + raise ValueError( + "The word could not be stemmed, because \ + it is empty !" + ) + self.is_verb = False + # remove Arabic diacritics and replace some letters with others + token = self.norm(token) + # strip the common noun prefixes + pre = self.pref(token) + if pre is not None: + token = pre + # transform the feminine form to masculine form + fm = self.fem2masc(token) + if fm is not None: + return fm + # strip the adjective affixes + adj = self.adjective(token) + if adj is not None: + return adj + # strip the suffixes that are common to nouns and verbs + token = self.suff(token) + # transform a plural noun to a singular noun + ps = self.plur2sing(token) + if ps is None: + if pre is None: # if the noun prefixes are not stripped + # strip the verb prefixes and suffixes + verb = self.verb(token) + if verb is not None: + self.is_verb = True + return verb + else: + return ps + return token + except ValueError as e: + print(e) + + def stem(self, token): + # stem the input word + try: + if token is None: + raise ValueError( + "The word could not be stemmed, because \ + it is empty !" + ) + # run the first round of stemming + token = self.stem1(token) + # check if there is some additional noun affixes + if len(token) > 4: + # ^Taa, $Yaa + char + if token.startswith("\u062A") and token[-2] == "\u064A": + token = token[1:-2] + token[-1] + return token + # ^Miim, $Waaw + char + if token.startswith("\u0645") and token[-2] == "\u0648": + token = token[1:-2] + token[-1] + return token + if len(token) > 3: + # !^Alif, $Yaa + if not token.startswith("\u0627") and token.endswith("\u064A"): + token = token[:-1] + return token + # $Laam + if token.startswith("\u0644"): + return token[1:] + return token + except ValueError as e: + print(e) + + def norm(self, token): + """ + normalize the word by removing diacritics, replace hamzated Alif + with Alif bare, replace AlifMaqsura with Yaa and remove Waaw at the + beginning. + """ + # strip Arabic diacritics + token = self.re_diacritics.sub("", token) + # replace Hamzated Alif with Alif bare + token = self.re_hamzated_alif.sub("\u0627", token) + # replace alifMaqsura with Yaa + token = self.re_alifMaqsura.sub("\u064A", token) + # strip the Waaw from the word beginning if the remaining is + # tri-literal at least + if token.startswith("\u0648") and len(token) > 3: + token = token[1:] + return token + + def pref(self, token): + """ + remove prefixes from the words' beginning. + """ + if len(token) > 5: + for p3 in self.pr3: + if token.startswith(p3): + return token[3:] + if len(token) > 6: + for p4 in self.pr4: + if token.startswith(p4): + return token[4:] + if len(token) > 5: + for p3 in self.pr32: + if token.startswith(p3): + return token[3:] + if len(token) > 4: + for p2 in self.pr2: + if token.startswith(p2): + return token[2:] + + def adjective(self, token): + """ + remove the infixes from adjectives + """ + # ^Alif, Alif, $Yaa + if len(token) > 5: + if ( + token.startswith("\u0627") + and token[-3] == "\u0627" + and token.endswith("\u064A") + ): + return token[:-3] + token[-2] + + def suff(self, token): + """ + remove the suffixes from the word's ending. + """ + if token.endswith("\u0643") and len(token) > 3: + return token[:-1] + if len(token) > 4: + for s2 in self.su2: + if token.endswith(s2): + return token[:-2] + if len(token) > 5: + for s3 in self.su3: + if token.endswith(s3): + return token[:-3] + if token.endswith("\u0647") and len(token) > 3: + token = token[:-1] + return token + if len(token) > 4: + for s2 in self.su22: + if token.endswith(s2): + return token[:-2] + if len(token) > 5: + for s3 in self.su32: + if token.endswith(s3): + return token[:-3] + # $Noon and Alif + if token.endswith("\u0646\u0627") and len(token) > 4: + return token[:-2] + return token + + def fem2masc(self, token): + """ + transform the word from the feminine form to the masculine form. + """ + if len(token) > 6: + # ^Taa, Yaa, $Yaa and Taa Marbuta + if ( + token.startswith("\u062A") + and token[-4] == "\u064A" + and token.endswith("\u064A\u0629") + ): + return token[1:-4] + token[-3] + # ^Alif, Yaa, $Yaa and Taa Marbuta + if ( + token.startswith("\u0627") + and token[-4] == "\u0627" + and token.endswith("\u064A\u0629") + ): + return token[:-4] + token[-3] + # $Alif, Yaa and Taa Marbuta + if token.endswith("\u0627\u064A\u0629") and len(token) > 5: + return token[:-2] + if len(token) > 4: + # Alif, $Taa Marbuta + if token[1] == "\u0627" and token.endswith("\u0629"): + return token[0] + token[2:-1] + # $Yaa and Taa Marbuta + if token.endswith("\u064A\u0629"): + return token[:-2] + # $Taa Marbuta + if token.endswith("\u0629") and len(token) > 3: + return token[:-1] + + def plur2sing(self, token): + """ + transform the word from the plural form to the singular form. + """ + # ^Haa, $Noon, Waaw + if len(token) > 5: + if token.startswith("\u0645") and token.endswith("\u0648\u0646"): + return token[1:-2] + if len(token) > 4: + for ps2 in self.pl_si2: + if token.endswith(ps2): + return token[:-2] + if len(token) > 5: + for ps3 in self.pl_si3: + if token.endswith(ps3): + return token[:-3] + if len(token) > 4: + # $Alif, Taa + if token.endswith("\u0627\u062A"): + return token[:-2] + # ^Alif Alif + if token.startswith("\u0627") and token[2] == "\u0627": + return token[:2] + token[3:] + # ^Alif Alif + if token.startswith("\u0627") and token[-2] == "\u0627": + return token[1:-2] + token[-1] + + def verb(self, token): + """ + stem the verb prefixes and suffixes or both + """ + vb = self.verb_t1(token) + if vb is not None: + return vb + vb = self.verb_t2(token) + if vb is not None: + return vb + vb = self.verb_t3(token) + if vb is not None: + return vb + vb = self.verb_t4(token) + if vb is not None: + return vb + vb = self.verb_t5(token) + if vb is not None: + return vb + vb = self.verb_t6(token) + return vb + + def verb_t1(self, token): + """ + stem the present tense co-occurred prefixes and suffixes + """ + if len(token) > 5 and token.startswith("\u062A"): # Taa + for s2 in self.pl_si2: + if token.endswith(s2): + return token[1:-2] + if len(token) > 5 and token.startswith("\u064A"): # Yaa + for s2 in self.verb_su2: + if token.endswith(s2): + return token[1:-2] + if len(token) > 4 and token.startswith("\u0627"): # Alif + # Waaw Alif + if len(token) > 5 and token.endswith("\u0648\u0627"): + return token[1:-2] + # Yaa + if token.endswith("\u064A"): + return token[1:-1] + # Alif + if token.endswith("\u0627"): + return token[1:-1] + # Noon + if token.endswith("\u0646"): + return token[1:-1] + # ^Yaa, Noon$ + if len(token) > 4 and token.startswith("\u064A") and token.endswith("\u0646"): + return token[1:-1] + # ^Taa, Noon$ + if len(token) > 4 and token.startswith("\u062A") and token.endswith("\u0646"): + return token[1:-1] + + def verb_t2(self, token): + """ + stem the future tense co-occurred prefixes and suffixes + """ + if len(token) > 6: + for s2 in self.pl_si2: + # ^Siin Taa + if token.startswith(self.verb_pr2[0]) and token.endswith(s2): + return token[2:-2] + # ^Siin Yaa, Alif Noon$ + if token.startswith(self.verb_pr2[1]) and token.endswith(self.pl_si2[0]): + return token[2:-2] + # ^Siin Yaa, Waaw Noon$ + if token.startswith(self.verb_pr2[1]) and token.endswith(self.pl_si2[2]): + return token[2:-2] + # ^Siin Taa, Noon$ + if ( + len(token) > 5 + and token.startswith(self.verb_pr2[0]) + and token.endswith("\u0646") + ): + return token[2:-1] + # ^Siin Yaa, Noon$ + if ( + len(token) > 5 + and token.startswith(self.verb_pr2[1]) + and token.endswith("\u0646") + ): + return token[2:-1] + + def verb_t3(self, token): + """ + stem the present tense suffixes + """ + if len(token) > 5: + for su3 in self.verb_suf3: + if token.endswith(su3): + return token[:-3] + if len(token) > 4: + for su2 in self.verb_suf2: + if token.endswith(su2): + return token[:-2] + if len(token) > 3: + for su1 in self.verb_suf1: + if token.endswith(su1): + return token[:-1] + + def verb_t4(self, token): + """ + stem the present tense prefixes + """ + if len(token) > 3: + for pr1 in self.verb_suf1: + if token.startswith(pr1): + return token[1:] + if token.startswith("\u064A"): + return token[1:] + + def verb_t5(self, token): + """ + stem the future tense prefixes + """ + if len(token) > 4: + for pr2 in self.verb_pr22: + if token.startswith(pr2): + return token[2:] + for pr2 in self.verb_pr2: + if token.startswith(pr2): + return token[2:] + + def verb_t6(self, token): + """ + stem the imperative tense prefixes + """ + if len(token) > 4: + for pr3 in self.verb_pr33: + if token.startswith(pr3): + return token[2:] + + return token diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/cistem.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/cistem.py new file mode 100644 index 0000000..f4ed299 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/cistem.py @@ -0,0 +1,209 @@ +# Natural Language Toolkit: CISTEM Stemmer for German +# Copyright (C) 2001-2023 NLTK Project +# Author: Leonie Weissweiler +# Tom Aarsen <> (modifications) +# Algorithm: Leonie Weissweiler +# Alexander Fraser +# URL: +# For license information, see LICENSE.TXT + +import re +from typing import Tuple + +from nltk.stem.api import StemmerI + + +class Cistem(StemmerI): + """ + CISTEM Stemmer for German + + This is the official Python implementation of the CISTEM stemmer. + It is based on the paper + Leonie Weissweiler, Alexander Fraser (2017). Developing a Stemmer for German + Based on a Comparative Analysis of Publicly Available Stemmers. + In Proceedings of the German Society for Computational Linguistics and Language + Technology (GSCL) + which can be read here: + https://www.cis.lmu.de/~weissweiler/cistem/ + + In the paper, we conducted an analysis of publicly available stemmers, + developed two gold standards for German stemming and evaluated the stemmers + based on the two gold standards. We then proposed the stemmer implemented here + and show that it achieves slightly better f-measure than the other stemmers and + is thrice as fast as the Snowball stemmer for German while being about as fast + as most other stemmers. + + case_insensitive is a a boolean specifying if case-insensitive stemming + should be used. Case insensitivity improves performance only if words in the + text may be incorrectly upper case. For all-lowercase and correctly cased + text, best performance is achieved by setting case_insensitive for false. + + :param case_insensitive: if True, the stemming is case insensitive. False by default. + :type case_insensitive: bool + """ + + strip_ge = re.compile(r"^ge(.{4,})") + repl_xx = re.compile(r"(.)\1") + strip_emr = re.compile(r"e[mr]$") + strip_nd = re.compile(r"nd$") + strip_t = re.compile(r"t$") + strip_esn = re.compile(r"[esn]$") + repl_xx_back = re.compile(r"(.)\*") + + def __init__(self, case_insensitive: bool = False): + self._case_insensitive = case_insensitive + + @staticmethod + def replace_to(word: str) -> str: + word = word.replace("sch", "$") + word = word.replace("ei", "%") + word = word.replace("ie", "&") + word = Cistem.repl_xx.sub(r"\1*", word) + + return word + + @staticmethod + def replace_back(word: str) -> str: + word = Cistem.repl_xx_back.sub(r"\1\1", word) + word = word.replace("%", "ei") + word = word.replace("&", "ie") + word = word.replace("$", "sch") + + return word + + def stem(self, word: str) -> str: + """Stems the input word. + + :param word: The word that is to be stemmed. + :type word: str + :return: The stemmed word. + :rtype: str + + >>> from nltk.stem.cistem import Cistem + >>> stemmer = Cistem() + >>> s1 = "Speicherbehältern" + >>> stemmer.stem(s1) + 'speicherbehalt' + >>> s2 = "Grenzpostens" + >>> stemmer.stem(s2) + 'grenzpost' + >>> s3 = "Ausgefeiltere" + >>> stemmer.stem(s3) + 'ausgefeilt' + >>> stemmer = Cistem(True) + >>> stemmer.stem(s1) + 'speicherbehal' + >>> stemmer.stem(s2) + 'grenzpo' + >>> stemmer.stem(s3) + 'ausgefeil' + """ + if len(word) == 0: + return word + + upper = word[0].isupper() + word = word.lower() + + word = word.replace("ü", "u") + word = word.replace("ö", "o") + word = word.replace("ä", "a") + word = word.replace("ß", "ss") + + word = Cistem.strip_ge.sub(r"\1", word) + + return self._segment_inner(word, upper)[0] + + def segment(self, word: str) -> Tuple[str, str]: + """ + This method works very similarly to stem (:func:'cistem.stem'). The difference is that in + addition to returning the stem, it also returns the rest that was removed at + the end. To be able to return the stem unchanged so the stem and the rest + can be concatenated to form the original word, all subsitutions that altered + the stem in any other way than by removing letters at the end were left out. + + :param word: The word that is to be stemmed. + :type word: str + :return: A tuple of the stemmed word and the removed suffix. + :rtype: Tuple[str, str] + + >>> from nltk.stem.cistem import Cistem + >>> stemmer = Cistem() + >>> s1 = "Speicherbehältern" + >>> stemmer.segment(s1) + ('speicherbehält', 'ern') + >>> s2 = "Grenzpostens" + >>> stemmer.segment(s2) + ('grenzpost', 'ens') + >>> s3 = "Ausgefeiltere" + >>> stemmer.segment(s3) + ('ausgefeilt', 'ere') + >>> stemmer = Cistem(True) + >>> stemmer.segment(s1) + ('speicherbehäl', 'tern') + >>> stemmer.segment(s2) + ('grenzpo', 'stens') + >>> stemmer.segment(s3) + ('ausgefeil', 'tere') + """ + if len(word) == 0: + return ("", "") + + upper = word[0].isupper() + word = word.lower() + + return self._segment_inner(word, upper) + + def _segment_inner(self, word: str, upper: bool): + """Inner method for iteratively applying the code stemming regexes. + This method receives a pre-processed variant of the word to be stemmed, + or the word to be segmented, and returns a tuple of the word and the + removed suffix. + + :param word: A pre-processed variant of the word that is to be stemmed. + :type word: str + :param upper: Whether the original word started with a capital letter. + :type upper: bool + :return: A tuple of the stemmed word and the removed suffix. + :rtype: Tuple[str, str] + """ + + rest_length = 0 + word_copy = word[:] + + # Pre-processing before applying the substitution patterns + word = Cistem.replace_to(word) + rest = "" + + # Apply the substitution patterns + while len(word) > 3: + if len(word) > 5: + word, n = Cistem.strip_emr.subn("", word) + if n != 0: + rest_length += 2 + continue + + word, n = Cistem.strip_nd.subn("", word) + if n != 0: + rest_length += 2 + continue + + if not upper or self._case_insensitive: + word, n = Cistem.strip_t.subn("", word) + if n != 0: + rest_length += 1 + continue + + word, n = Cistem.strip_esn.subn("", word) + if n != 0: + rest_length += 1 + continue + else: + break + + # Post-processing after applying the substitution patterns + word = Cistem.replace_back(word) + + if rest_length: + rest = word_copy[-rest_length:] + + return (word, rest) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/isri.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/isri.py new file mode 100644 index 0000000..5bd3cf1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/isri.py @@ -0,0 +1,395 @@ +# +# Natural Language Toolkit: The ISRI Arabic Stemmer +# +# Copyright (C) 2001-2023 NLTK Project +# Algorithm: Kazem Taghva, Rania Elkhoury, and Jeffrey Coombs (2005) +# Author: Hosam Algasaier +# URL: +# For license information, see LICENSE.TXT + +""" +ISRI Arabic Stemmer + +The algorithm for this stemmer is described in: + +Taghva, K., Elkoury, R., and Coombs, J. 2005. Arabic Stemming without a root dictionary. +Information Science Research Institute. University of Nevada, Las Vegas, USA. + +The Information Science Research Institute’s (ISRI) Arabic stemmer shares many features +with the Khoja stemmer. However, the main difference is that ISRI stemmer does not use root +dictionary. Also, if a root is not found, ISRI stemmer returned normalized form, rather than +returning the original unmodified word. + +Additional adjustments were made to improve the algorithm: + +1- Adding 60 stop words. +2- Adding the pattern (تفاعيل) to ISRI pattern set. +3- The step 2 in the original algorithm was normalizing all hamza. This step is discarded because it +increases the word ambiguities and changes the original root. + +""" +import re + +from nltk.stem.api import StemmerI + + +class ISRIStemmer(StemmerI): + """ + ISRI Arabic stemmer based on algorithm: Arabic Stemming without a root dictionary. + Information Science Research Institute. University of Nevada, Las Vegas, USA. + + A few minor modifications have been made to ISRI basic algorithm. + See the source code of this module for more information. + + isri.stem(token) returns Arabic root for the given token. + + The ISRI Stemmer requires that all tokens have Unicode string types. + If you use Python IDLE on Arabic Windows you have to decode text first + using Arabic '1256' coding. + """ + + def __init__(self): + # length three prefixes + self.p3 = [ + "\u0643\u0627\u0644", + "\u0628\u0627\u0644", + "\u0648\u0644\u0644", + "\u0648\u0627\u0644", + ] + + # length two prefixes + self.p2 = ["\u0627\u0644", "\u0644\u0644"] + + # length one prefixes + self.p1 = [ + "\u0644", + "\u0628", + "\u0641", + "\u0633", + "\u0648", + "\u064a", + "\u062a", + "\u0646", + "\u0627", + ] + + # length three suffixes + self.s3 = [ + "\u062a\u0645\u0644", + "\u0647\u0645\u0644", + "\u062a\u0627\u0646", + "\u062a\u064a\u0646", + "\u0643\u0645\u0644", + ] + + # length two suffixes + self.s2 = [ + "\u0648\u0646", + "\u0627\u062a", + "\u0627\u0646", + "\u064a\u0646", + "\u062a\u0646", + "\u0643\u0645", + "\u0647\u0646", + "\u0646\u0627", + "\u064a\u0627", + "\u0647\u0627", + "\u062a\u0645", + "\u0643\u0646", + "\u0646\u064a", + "\u0648\u0627", + "\u0645\u0627", + "\u0647\u0645", + ] + + # length one suffixes + self.s1 = ["\u0629", "\u0647", "\u064a", "\u0643", "\u062a", "\u0627", "\u0646"] + + # groups of length four patterns + self.pr4 = { + 0: ["\u0645"], + 1: ["\u0627"], + 2: ["\u0627", "\u0648", "\u064A"], + 3: ["\u0629"], + } + + # Groups of length five patterns and length three roots + self.pr53 = { + 0: ["\u0627", "\u062a"], + 1: ["\u0627", "\u064a", "\u0648"], + 2: ["\u0627", "\u062a", "\u0645"], + 3: ["\u0645", "\u064a", "\u062a"], + 4: ["\u0645", "\u062a"], + 5: ["\u0627", "\u0648"], + 6: ["\u0627", "\u0645"], + } + + self.re_short_vowels = re.compile(r"[\u064B-\u0652]") + self.re_hamza = re.compile(r"[\u0621\u0624\u0626]") + self.re_initial_hamza = re.compile(r"^[\u0622\u0623\u0625]") + + self.stop_words = [ + "\u064a\u0643\u0648\u0646", + "\u0648\u0644\u064a\u0633", + "\u0648\u0643\u0627\u0646", + "\u0643\u0630\u0644\u0643", + "\u0627\u0644\u062a\u064a", + "\u0648\u0628\u064a\u0646", + "\u0639\u0644\u064a\u0647\u0627", + "\u0645\u0633\u0627\u0621", + "\u0627\u0644\u0630\u064a", + "\u0648\u0643\u0627\u0646\u062a", + "\u0648\u0644\u0643\u0646", + "\u0648\u0627\u0644\u062a\u064a", + "\u062a\u0643\u0648\u0646", + "\u0627\u0644\u064a\u0648\u0645", + "\u0627\u0644\u0644\u0630\u064a\u0646", + "\u0639\u0644\u064a\u0647", + "\u0643\u0627\u0646\u062a", + "\u0644\u0630\u0644\u0643", + "\u0623\u0645\u0627\u0645", + "\u0647\u0646\u0627\u0643", + "\u0645\u0646\u0647\u0627", + "\u0645\u0627\u0632\u0627\u0644", + "\u0644\u0627\u0632\u0627\u0644", + "\u0644\u0627\u064a\u0632\u0627\u0644", + "\u0645\u0627\u064a\u0632\u0627\u0644", + "\u0627\u0635\u0628\u062d", + "\u0623\u0635\u0628\u062d", + "\u0623\u0645\u0633\u0649", + "\u0627\u0645\u0633\u0649", + "\u0623\u0636\u062d\u0649", + "\u0627\u0636\u062d\u0649", + "\u0645\u0627\u0628\u0631\u062d", + "\u0645\u0627\u0641\u062a\u0626", + "\u0645\u0627\u0627\u0646\u0641\u0643", + "\u0644\u0627\u0633\u064a\u0645\u0627", + "\u0648\u0644\u0627\u064a\u0632\u0627\u0644", + "\u0627\u0644\u062d\u0627\u0644\u064a", + "\u0627\u0644\u064a\u0647\u0627", + "\u0627\u0644\u0630\u064a\u0646", + "\u0641\u0627\u0646\u0647", + "\u0648\u0627\u0644\u0630\u064a", + "\u0648\u0647\u0630\u0627", + "\u0644\u0647\u0630\u0627", + "\u0641\u0643\u0627\u0646", + "\u0633\u062a\u0643\u0648\u0646", + "\u0627\u0644\u064a\u0647", + "\u064a\u0645\u0643\u0646", + "\u0628\u0647\u0630\u0627", + "\u0627\u0644\u0630\u0649", + ] + + def stem(self, token): + """ + Stemming a word token using the ISRI stemmer. + """ + token = self.norm( + token, 1 + ) # remove diacritics which representing Arabic short vowels + if token in self.stop_words: + return token # exclude stop words from being processed + token = self.pre32( + token + ) # remove length three and length two prefixes in this order + token = self.suf32( + token + ) # remove length three and length two suffixes in this order + token = self.waw( + token + ) # remove connective ‘و’ if it precedes a word beginning with ‘و’ + token = self.norm(token, 2) # normalize initial hamza to bare alif + # if 4 <= word length <= 7, then stem; otherwise, no stemming + if len(token) == 4: # length 4 word + token = self.pro_w4(token) + elif len(token) == 5: # length 5 word + token = self.pro_w53(token) + token = self.end_w5(token) + elif len(token) == 6: # length 6 word + token = self.pro_w6(token) + token = self.end_w6(token) + elif len(token) == 7: # length 7 word + token = self.suf1(token) + if len(token) == 7: + token = self.pre1(token) + if len(token) == 6: + token = self.pro_w6(token) + token = self.end_w6(token) + return token + + def norm(self, word, num=3): + """ + normalization: + num=1 normalize diacritics + num=2 normalize initial hamza + num=3 both 1&2 + """ + if num == 1: + word = self.re_short_vowels.sub("", word) + elif num == 2: + word = self.re_initial_hamza.sub("\u0627", word) + elif num == 3: + word = self.re_short_vowels.sub("", word) + word = self.re_initial_hamza.sub("\u0627", word) + return word + + def pre32(self, word): + """remove length three and length two prefixes in this order""" + if len(word) >= 6: + for pre3 in self.p3: + if word.startswith(pre3): + return word[3:] + if len(word) >= 5: + for pre2 in self.p2: + if word.startswith(pre2): + return word[2:] + return word + + def suf32(self, word): + """remove length three and length two suffixes in this order""" + if len(word) >= 6: + for suf3 in self.s3: + if word.endswith(suf3): + return word[:-3] + if len(word) >= 5: + for suf2 in self.s2: + if word.endswith(suf2): + return word[:-2] + return word + + def waw(self, word): + """remove connective ‘و’ if it precedes a word beginning with ‘و’""" + if len(word) >= 4 and word[:2] == "\u0648\u0648": + word = word[1:] + return word + + def pro_w4(self, word): + """process length four patterns and extract length three roots""" + if word[0] in self.pr4[0]: # مفعل + word = word[1:] + elif word[1] in self.pr4[1]: # فاعل + word = word[:1] + word[2:] + elif word[2] in self.pr4[2]: # فعال - فعول - فعيل + word = word[:2] + word[3] + elif word[3] in self.pr4[3]: # فعلة + word = word[:-1] + else: + word = self.suf1(word) # do - normalize short sufix + if len(word) == 4: + word = self.pre1(word) # do - normalize short prefix + return word + + def pro_w53(self, word): + """process length five patterns and extract length three roots""" + if word[2] in self.pr53[0] and word[0] == "\u0627": # افتعل - افاعل + word = word[1] + word[3:] + elif word[3] in self.pr53[1] and word[0] == "\u0645": # مفعول - مفعال - مفعيل + word = word[1:3] + word[4] + elif word[0] in self.pr53[2] and word[4] == "\u0629": # مفعلة - تفعلة - افعلة + word = word[1:4] + elif word[0] in self.pr53[3] and word[2] == "\u062a": # مفتعل - يفتعل - تفتعل + word = word[1] + word[3:] + elif word[0] in self.pr53[4] and word[2] == "\u0627": # مفاعل - تفاعل + word = word[1] + word[3:] + elif word[2] in self.pr53[5] and word[4] == "\u0629": # فعولة - فعالة + word = word[:2] + word[3] + elif word[0] in self.pr53[6] and word[1] == "\u0646": # انفعل - منفعل + word = word[2:] + elif word[3] == "\u0627" and word[0] == "\u0627": # افعال + word = word[1:3] + word[4] + elif word[4] == "\u0646" and word[3] == "\u0627": # فعلان + word = word[:3] + elif word[3] == "\u064a" and word[0] == "\u062a": # تفعيل + word = word[1:3] + word[4] + elif word[3] == "\u0648" and word[1] == "\u0627": # فاعول + word = word[0] + word[2] + word[4] + elif word[2] == "\u0627" and word[1] == "\u0648": # فواعل + word = word[0] + word[3:] + elif word[3] == "\u0626" and word[2] == "\u0627": # فعائل + word = word[:2] + word[4] + elif word[4] == "\u0629" and word[1] == "\u0627": # فاعلة + word = word[0] + word[2:4] + elif word[4] == "\u064a" and word[2] == "\u0627": # فعالي + word = word[:2] + word[3] + else: + word = self.suf1(word) # do - normalize short sufix + if len(word) == 5: + word = self.pre1(word) # do - normalize short prefix + return word + + def pro_w54(self, word): + """process length five patterns and extract length four roots""" + if word[0] in self.pr53[2]: # تفعلل - افعلل - مفعلل + word = word[1:] + elif word[4] == "\u0629": # فعللة + word = word[:4] + elif word[2] == "\u0627": # فعالل + word = word[:2] + word[3:] + return word + + def end_w5(self, word): + """ending step (word of length five)""" + if len(word) == 4: + word = self.pro_w4(word) + elif len(word) == 5: + word = self.pro_w54(word) + return word + + def pro_w6(self, word): + """process length six patterns and extract length three roots""" + if word.startswith("\u0627\u0633\u062a") or word.startswith( + "\u0645\u0633\u062a" + ): # مستفعل - استفعل + word = word[3:] + elif ( + word[0] == "\u0645" and word[3] == "\u0627" and word[5] == "\u0629" + ): # مفعالة + word = word[1:3] + word[4] + elif ( + word[0] == "\u0627" and word[2] == "\u062a" and word[4] == "\u0627" + ): # افتعال + word = word[1] + word[3] + word[5] + elif ( + word[0] == "\u0627" and word[3] == "\u0648" and word[2] == word[4] + ): # افعوعل + word = word[1] + word[4:] + elif ( + word[0] == "\u062a" and word[2] == "\u0627" and word[4] == "\u064a" + ): # تفاعيل new pattern + word = word[1] + word[3] + word[5] + else: + word = self.suf1(word) # do - normalize short sufix + if len(word) == 6: + word = self.pre1(word) # do - normalize short prefix + return word + + def pro_w64(self, word): + """process length six patterns and extract length four roots""" + if word[0] == "\u0627" and word[4] == "\u0627": # افعلال + word = word[1:4] + word[5] + elif word.startswith("\u0645\u062a"): # متفعلل + word = word[2:] + return word + + def end_w6(self, word): + """ending step (word of length six)""" + if len(word) == 5: + word = self.pro_w53(word) + word = self.end_w5(word) + elif len(word) == 6: + word = self.pro_w64(word) + return word + + def suf1(self, word): + """normalize short sufix""" + for sf1 in self.s1: + if word.endswith(sf1): + return word[:-1] + return word + + def pre1(self, word): + """normalize short prefix""" + for sp1 in self.p1: + if word.startswith(sp1): + return word[1:] + return word diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/lancaster.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/lancaster.py new file mode 100644 index 0000000..bc5135d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/lancaster.py @@ -0,0 +1,343 @@ +# Natural Language Toolkit: Stemmers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Tomcavage +# URL: +# For license information, see LICENSE.TXT + +""" +A word stemmer based on the Lancaster (Paice/Husk) stemming algorithm. +Paice, Chris D. "Another Stemmer." ACM SIGIR Forum 24.3 (1990): 56-61. +""" +import re + +from nltk.stem.api import StemmerI + + +class LancasterStemmer(StemmerI): + """ + Lancaster Stemmer + + >>> from nltk.stem.lancaster import LancasterStemmer + >>> st = LancasterStemmer() + >>> st.stem('maximum') # Remove "-um" when word is intact + 'maxim' + >>> st.stem('presumably') # Don't remove "-um" when word is not intact + 'presum' + >>> st.stem('multiply') # No action taken if word ends with "-ply" + 'multiply' + >>> st.stem('provision') # Replace "-sion" with "-j" to trigger "j" set of rules + 'provid' + >>> st.stem('owed') # Word starting with vowel must contain at least 2 letters + 'ow' + >>> st.stem('ear') # ditto + 'ear' + >>> st.stem('saying') # Words starting with consonant must contain at least 3 + 'say' + >>> st.stem('crying') # letters and one of those letters must be a vowel + 'cry' + >>> st.stem('string') # ditto + 'string' + >>> st.stem('meant') # ditto + 'meant' + >>> st.stem('cement') # ditto + 'cem' + >>> st_pre = LancasterStemmer(strip_prefix_flag=True) + >>> st_pre.stem('kilometer') # Test Prefix + 'met' + >>> st_custom = LancasterStemmer(rule_tuple=("ssen4>", "s1t.")) + >>> st_custom.stem("ness") # Change s to t + 'nest' + """ + + # The rule list is static since it doesn't change between instances + default_rule_tuple = ( + "ai*2.", # -ia > - if intact + "a*1.", # -a > - if intact + "bb1.", # -bb > -b + "city3s.", # -ytic > -ys + "ci2>", # -ic > - + "cn1t>", # -nc > -nt + "dd1.", # -dd > -d + "dei3y>", # -ied > -y + "deec2ss.", # -ceed >", -cess + "dee1.", # -eed > -ee + "de2>", # -ed > - + "dooh4>", # -hood > - + "e1>", # -e > - + "feil1v.", # -lief > -liev + "fi2>", # -if > - + "gni3>", # -ing > - + "gai3y.", # -iag > -y + "ga2>", # -ag > - + "gg1.", # -gg > -g + "ht*2.", # -th > - if intact + "hsiug5ct.", # -guish > -ct + "hsi3>", # -ish > - + "i*1.", # -i > - if intact + "i1y>", # -i > -y + "ji1d.", # -ij > -id -- see nois4j> & vis3j> + "juf1s.", # -fuj > -fus + "ju1d.", # -uj > -ud + "jo1d.", # -oj > -od + "jeh1r.", # -hej > -her + "jrev1t.", # -verj > -vert + "jsim2t.", # -misj > -mit + "jn1d.", # -nj > -nd + "j1s.", # -j > -s + "lbaifi6.", # -ifiabl > - + "lbai4y.", # -iabl > -y + "lba3>", # -abl > - + "lbi3.", # -ibl > - + "lib2l>", # -bil > -bl + "lc1.", # -cl > c + "lufi4y.", # -iful > -y + "luf3>", # -ful > - + "lu2.", # -ul > - + "lai3>", # -ial > - + "lau3>", # -ual > - + "la2>", # -al > - + "ll1.", # -ll > -l + "mui3.", # -ium > - + "mu*2.", # -um > - if intact + "msi3>", # -ism > - + "mm1.", # -mm > -m + "nois4j>", # -sion > -j + "noix4ct.", # -xion > -ct + "noi3>", # -ion > - + "nai3>", # -ian > - + "na2>", # -an > - + "nee0.", # protect -een + "ne2>", # -en > - + "nn1.", # -nn > -n + "pihs4>", # -ship > - + "pp1.", # -pp > -p + "re2>", # -er > - + "rae0.", # protect -ear + "ra2.", # -ar > - + "ro2>", # -or > - + "ru2>", # -ur > - + "rr1.", # -rr > -r + "rt1>", # -tr > -t + "rei3y>", # -ier > -y + "sei3y>", # -ies > -y + "sis2.", # -sis > -s + "si2>", # -is > - + "ssen4>", # -ness > - + "ss0.", # protect -ss + "suo3>", # -ous > - + "su*2.", # -us > - if intact + "s*1>", # -s > - if intact + "s0.", # -s > -s + "tacilp4y.", # -plicat > -ply + "ta2>", # -at > - + "tnem4>", # -ment > - + "tne3>", # -ent > - + "tna3>", # -ant > - + "tpir2b.", # -ript > -rib + "tpro2b.", # -orpt > -orb + "tcud1.", # -duct > -duc + "tpmus2.", # -sumpt > -sum + "tpec2iv.", # -cept > -ceiv + "tulo2v.", # -olut > -olv + "tsis0.", # protect -sist + "tsi3>", # -ist > - + "tt1.", # -tt > -t + "uqi3.", # -iqu > - + "ugo1.", # -ogu > -og + "vis3j>", # -siv > -j + "vie0.", # protect -eiv + "vi2>", # -iv > - + "ylb1>", # -bly > -bl + "yli3y>", # -ily > -y + "ylp0.", # protect -ply + "yl2>", # -ly > - + "ygo1.", # -ogy > -og + "yhp1.", # -phy > -ph + "ymo1.", # -omy > -om + "ypo1.", # -opy > -op + "yti3>", # -ity > - + "yte3>", # -ety > - + "ytl2.", # -lty > -l + "yrtsi5.", # -istry > - + "yra3>", # -ary > - + "yro3>", # -ory > - + "yfi3.", # -ify > - + "ycn2t>", # -ncy > -nt + "yca3>", # -acy > - + "zi2>", # -iz > - + "zy1s.", # -yz > -ys + ) + + def __init__(self, rule_tuple=None, strip_prefix_flag=False): + """Create an instance of the Lancaster stemmer.""" + # Setup an empty rule dictionary - this will be filled in later + self.rule_dictionary = {} + # Check if a user wants to strip prefix + self._strip_prefix = strip_prefix_flag + # Check if a user wants to use his/her own rule tuples. + self._rule_tuple = rule_tuple if rule_tuple else self.default_rule_tuple + + def parseRules(self, rule_tuple=None): + """Validate the set of rules used in this stemmer. + + If this function is called as an individual method, without using stem + method, rule_tuple argument will be compiled into self.rule_dictionary. + If this function is called within stem, self._rule_tuple will be used. + + """ + # If there is no argument for the function, use class' own rule tuple. + rule_tuple = rule_tuple if rule_tuple else self._rule_tuple + valid_rule = re.compile(r"^[a-z]+\*?\d[a-z]*[>\.]?$") + # Empty any old rules from the rule set before adding new ones + self.rule_dictionary = {} + + for rule in rule_tuple: + if not valid_rule.match(rule): + raise ValueError(f"The rule {rule} is invalid") + first_letter = rule[0:1] + if first_letter in self.rule_dictionary: + self.rule_dictionary[first_letter].append(rule) + else: + self.rule_dictionary[first_letter] = [rule] + + def stem(self, word): + """Stem a word using the Lancaster stemmer.""" + # Lower-case the word, since all the rules are lower-cased + word = word.lower() + word = self.__stripPrefix(word) if self._strip_prefix else word + + # Save a copy of the original word + intact_word = word + + # If rule dictionary is empty, parse rule tuple. + if not self.rule_dictionary: + self.parseRules() + + return self.__doStemming(word, intact_word) + + def __doStemming(self, word, intact_word): + """Perform the actual word stemming""" + + valid_rule = re.compile(r"^([a-z]+)(\*?)(\d)([a-z]*)([>\.]?)$") + + proceed = True + + while proceed: + + # Find the position of the last letter of the word to be stemmed + last_letter_position = self.__getLastLetter(word) + + # Only stem the word if it has a last letter and a rule matching that last letter + if ( + last_letter_position < 0 + or word[last_letter_position] not in self.rule_dictionary + ): + proceed = False + + else: + rule_was_applied = False + + # Go through each rule that matches the word's final letter + for rule in self.rule_dictionary[word[last_letter_position]]: + rule_match = valid_rule.match(rule) + if rule_match: + ( + ending_string, + intact_flag, + remove_total, + append_string, + cont_flag, + ) = rule_match.groups() + + # Convert the number of chars to remove when stemming + # from a string to an integer + remove_total = int(remove_total) + + # Proceed if word's ending matches rule's word ending + if word.endswith(ending_string[::-1]): + if intact_flag: + if word == intact_word and self.__isAcceptable( + word, remove_total + ): + word = self.__applyRule( + word, remove_total, append_string + ) + rule_was_applied = True + if cont_flag == ".": + proceed = False + break + elif self.__isAcceptable(word, remove_total): + word = self.__applyRule( + word, remove_total, append_string + ) + rule_was_applied = True + if cont_flag == ".": + proceed = False + break + # If no rules apply, the word doesn't need any more stemming + if rule_was_applied == False: + proceed = False + return word + + def __getLastLetter(self, word): + """Get the zero-based index of the last alphabetic character in this string""" + last_letter = -1 + for position in range(len(word)): + if word[position].isalpha(): + last_letter = position + else: + break + return last_letter + + def __isAcceptable(self, word, remove_total): + """Determine if the word is acceptable for stemming.""" + word_is_acceptable = False + # If the word starts with a vowel, it must be at least 2 + # characters long to be stemmed + if word[0] in "aeiouy": + if len(word) - remove_total >= 2: + word_is_acceptable = True + # If the word starts with a consonant, it must be at least 3 + # characters long (including one vowel) to be stemmed + elif len(word) - remove_total >= 3: + if word[1] in "aeiouy": + word_is_acceptable = True + elif word[2] in "aeiouy": + word_is_acceptable = True + return word_is_acceptable + + def __applyRule(self, word, remove_total, append_string): + """Apply the stemming rule to the word""" + # Remove letters from the end of the word + new_word_length = len(word) - remove_total + word = word[0:new_word_length] + + # And add new letters to the end of the truncated word + if append_string: + word += append_string + return word + + def __stripPrefix(self, word): + """Remove prefix from a word. + + This function originally taken from Whoosh. + + """ + for prefix in ( + "kilo", + "micro", + "milli", + "intra", + "ultra", + "mega", + "nano", + "pico", + "pseudo", + ): + if word.startswith(prefix): + return word[len(prefix) :] + return word + + def __repr__(self): + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/porter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/porter.py new file mode 100644 index 0000000..d7ea610 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/porter.py @@ -0,0 +1,715 @@ +""" +Porter Stemmer + +This is the Porter stemming algorithm. It follows the algorithm +presented in + +Porter, M. "An algorithm for suffix stripping." Program 14.3 (1980): 130-137. + +with some optional deviations that can be turned on or off with the +`mode` argument to the constructor. + +Martin Porter, the algorithm's inventor, maintains a web page about the +algorithm at + + https://www.tartarus.org/~martin/PorterStemmer/ + +which includes another Python implementation and other implementations +in many languages. +""" + +__docformat__ = "plaintext" + +import re + +from nltk.stem.api import StemmerI + + +class PorterStemmer(StemmerI): + """ + A word stemmer based on the Porter stemming algorithm. + + Porter, M. "An algorithm for suffix stripping." + Program 14.3 (1980): 130-137. + + See https://www.tartarus.org/~martin/PorterStemmer/ for the homepage + of the algorithm. + + Martin Porter has endorsed several modifications to the Porter + algorithm since writing his original paper, and those extensions are + included in the implementations on his website. Additionally, others + have proposed further improvements to the algorithm, including NLTK + contributors. There are thus three modes that can be selected by + passing the appropriate constant to the class constructor's `mode` + attribute: + + - PorterStemmer.ORIGINAL_ALGORITHM + + An implementation that is faithful to the original paper. + + Note that Martin Porter has deprecated this version of the + algorithm. Martin distributes implementations of the Porter + Stemmer in many languages, hosted at: + + https://www.tartarus.org/~martin/PorterStemmer/ + + and all of these implementations include his extensions. He + strongly recommends against using the original, published + version of the algorithm; only use this mode if you clearly + understand why you are choosing to do so. + + - PorterStemmer.MARTIN_EXTENSIONS + + An implementation that only uses the modifications to the + algorithm that are included in the implementations on Martin + Porter's website. He has declared Porter frozen, so the + behaviour of those implementations should never change. + + - PorterStemmer.NLTK_EXTENSIONS (default) + + An implementation that includes further improvements devised by + NLTK contributors or taken from other modified implementations + found on the web. + + For the best stemming, you should use the default NLTK_EXTENSIONS + version. However, if you need to get the same results as either the + original algorithm or one of Martin Porter's hosted versions for + compatibility with an existing implementation or dataset, you can use + one of the other modes instead. + """ + + # Modes the Stemmer can be instantiated in + NLTK_EXTENSIONS = "NLTK_EXTENSIONS" + MARTIN_EXTENSIONS = "MARTIN_EXTENSIONS" + ORIGINAL_ALGORITHM = "ORIGINAL_ALGORITHM" + + def __init__(self, mode=NLTK_EXTENSIONS): + if mode not in ( + self.NLTK_EXTENSIONS, + self.MARTIN_EXTENSIONS, + self.ORIGINAL_ALGORITHM, + ): + raise ValueError( + "Mode must be one of PorterStemmer.NLTK_EXTENSIONS, " + "PorterStemmer.MARTIN_EXTENSIONS, or " + "PorterStemmer.ORIGINAL_ALGORITHM" + ) + + self.mode = mode + + if self.mode == self.NLTK_EXTENSIONS: + # This is a table of irregular forms. It is quite short, + # but still reflects the errors actually drawn to Martin + # Porter's attention over a 20 year period! + irregular_forms = { + "sky": ["sky", "skies"], + "die": ["dying"], + "lie": ["lying"], + "tie": ["tying"], + "news": ["news"], + "inning": ["innings", "inning"], + "outing": ["outings", "outing"], + "canning": ["cannings", "canning"], + "howe": ["howe"], + "proceed": ["proceed"], + "exceed": ["exceed"], + "succeed": ["succeed"], + } + + self.pool = {} + for key in irregular_forms: + for val in irregular_forms[key]: + self.pool[val] = key + + self.vowels = frozenset(["a", "e", "i", "o", "u"]) + + def _is_consonant(self, word, i): + """Returns True if word[i] is a consonant, False otherwise + + A consonant is defined in the paper as follows: + + A consonant in a word is a letter other than A, E, I, O or + U, and other than Y preceded by a consonant. (The fact that + the term `consonant' is defined to some extent in terms of + itself does not make it ambiguous.) So in TOY the consonants + are T and Y, and in SYZYGY they are S, Z and G. If a letter + is not a consonant it is a vowel. + """ + if word[i] in self.vowels: + return False + if word[i] == "y": + if i == 0: + return True + else: + return not self._is_consonant(word, i - 1) + return True + + def _measure(self, stem): + r"""Returns the 'measure' of stem, per definition in the paper + + From the paper: + + A consonant will be denoted by c, a vowel by v. A list + ccc... of length greater than 0 will be denoted by C, and a + list vvv... of length greater than 0 will be denoted by V. + Any word, or part of a word, therefore has one of the four + forms: + + CVCV ... C + CVCV ... V + VCVC ... C + VCVC ... V + + These may all be represented by the single form + + [C]VCVC ... [V] + + where the square brackets denote arbitrary presence of their + contents. Using (VC){m} to denote VC repeated m times, this + may again be written as + + [C](VC){m}[V]. + + m will be called the \measure\ of any word or word part when + represented in this form. The case m = 0 covers the null + word. Here are some examples: + + m=0 TR, EE, TREE, Y, BY. + m=1 TROUBLE, OATS, TREES, IVY. + m=2 TROUBLES, PRIVATE, OATEN, ORRERY. + """ + cv_sequence = "" + + # Construct a string of 'c's and 'v's representing whether each + # character in `stem` is a consonant or a vowel. + # e.g. 'falafel' becomes 'cvcvcvc', + # 'architecture' becomes 'vcccvcvccvcv' + for i in range(len(stem)): + if self._is_consonant(stem, i): + cv_sequence += "c" + else: + cv_sequence += "v" + + # Count the number of 'vc' occurrences, which is equivalent to + # the number of 'VC' occurrences in Porter's reduced form in the + # docstring above, which is in turn equivalent to `m` + return cv_sequence.count("vc") + + def _has_positive_measure(self, stem): + return self._measure(stem) > 0 + + def _contains_vowel(self, stem): + """Returns True if stem contains a vowel, else False""" + for i in range(len(stem)): + if not self._is_consonant(stem, i): + return True + return False + + def _ends_double_consonant(self, word): + """Implements condition *d from the paper + + Returns True if word ends with a double consonant + """ + return ( + len(word) >= 2 + and word[-1] == word[-2] + and self._is_consonant(word, len(word) - 1) + ) + + def _ends_cvc(self, word): + """Implements condition *o from the paper + + From the paper: + + *o - the stem ends cvc, where the second c is not W, X or Y + (e.g. -WIL, -HOP). + """ + return ( + len(word) >= 3 + and self._is_consonant(word, len(word) - 3) + and not self._is_consonant(word, len(word) - 2) + and self._is_consonant(word, len(word) - 1) + and word[-1] not in ("w", "x", "y") + ) or ( + self.mode == self.NLTK_EXTENSIONS + and len(word) == 2 + and not self._is_consonant(word, 0) + and self._is_consonant(word, 1) + ) + + def _replace_suffix(self, word, suffix, replacement): + """Replaces `suffix` of `word` with `replacement""" + assert word.endswith(suffix), "Given word doesn't end with given suffix" + if suffix == "": + return word + replacement + else: + return word[: -len(suffix)] + replacement + + def _apply_rule_list(self, word, rules): + """Applies the first applicable suffix-removal rule to the word + + Takes a word and a list of suffix-removal rules represented as + 3-tuples, with the first element being the suffix to remove, + the second element being the string to replace it with, and the + final element being the condition for the rule to be applicable, + or None if the rule is unconditional. + """ + for rule in rules: + suffix, replacement, condition = rule + if suffix == "*d" and self._ends_double_consonant(word): + stem = word[:-2] + if condition is None or condition(stem): + return stem + replacement + else: + # Don't try any further rules + return word + if word.endswith(suffix): + stem = self._replace_suffix(word, suffix, "") + if condition is None or condition(stem): + return stem + replacement + else: + # Don't try any further rules + return word + + return word + + def _step1a(self, word): + """Implements Step 1a from "An algorithm for suffix stripping" + + From the paper: + + SSES -> SS caresses -> caress + IES -> I ponies -> poni + ties -> ti + SS -> SS caress -> caress + S -> cats -> cat + """ + # this NLTK-only rule extends the original algorithm, so + # that 'flies'->'fli' but 'dies'->'die' etc + if self.mode == self.NLTK_EXTENSIONS: + if word.endswith("ies") and len(word) == 4: + return self._replace_suffix(word, "ies", "ie") + + return self._apply_rule_list( + word, + [ + ("sses", "ss", None), # SSES -> SS + ("ies", "i", None), # IES -> I + ("ss", "ss", None), # SS -> SS + ("s", "", None), # S -> + ], + ) + + def _step1b(self, word): + """Implements Step 1b from "An algorithm for suffix stripping" + + From the paper: + + (m>0) EED -> EE feed -> feed + agreed -> agree + (*v*) ED -> plastered -> plaster + bled -> bled + (*v*) ING -> motoring -> motor + sing -> sing + + If the second or third of the rules in Step 1b is successful, + the following is done: + + AT -> ATE conflat(ed) -> conflate + BL -> BLE troubl(ed) -> trouble + IZ -> IZE siz(ed) -> size + (*d and not (*L or *S or *Z)) + -> single letter + hopp(ing) -> hop + tann(ed) -> tan + fall(ing) -> fall + hiss(ing) -> hiss + fizz(ed) -> fizz + (m=1 and *o) -> E fail(ing) -> fail + fil(ing) -> file + + The rule to map to a single letter causes the removal of one of + the double letter pair. The -E is put back on -AT, -BL and -IZ, + so that the suffixes -ATE, -BLE and -IZE can be recognised + later. This E may be removed in step 4. + """ + # this NLTK-only block extends the original algorithm, so that + # 'spied'->'spi' but 'died'->'die' etc + if self.mode == self.NLTK_EXTENSIONS: + if word.endswith("ied"): + if len(word) == 4: + return self._replace_suffix(word, "ied", "ie") + else: + return self._replace_suffix(word, "ied", "i") + + # (m>0) EED -> EE + if word.endswith("eed"): + stem = self._replace_suffix(word, "eed", "") + if self._measure(stem) > 0: + return stem + "ee" + else: + return word + + rule_2_or_3_succeeded = False + + for suffix in ["ed", "ing"]: + if word.endswith(suffix): + intermediate_stem = self._replace_suffix(word, suffix, "") + if self._contains_vowel(intermediate_stem): + rule_2_or_3_succeeded = True + break + + if not rule_2_or_3_succeeded: + return word + + return self._apply_rule_list( + intermediate_stem, + [ + ("at", "ate", None), # AT -> ATE + ("bl", "ble", None), # BL -> BLE + ("iz", "ize", None), # IZ -> IZE + # (*d and not (*L or *S or *Z)) + # -> single letter + ( + "*d", + intermediate_stem[-1], + lambda stem: intermediate_stem[-1] not in ("l", "s", "z"), + ), + # (m=1 and *o) -> E + ( + "", + "e", + lambda stem: (self._measure(stem) == 1 and self._ends_cvc(stem)), + ), + ], + ) + + def _step1c(self, word): + """Implements Step 1c from "An algorithm for suffix stripping" + + From the paper: + + Step 1c + + (*v*) Y -> I happy -> happi + sky -> sky + """ + + def nltk_condition(stem): + """ + This has been modified from the original Porter algorithm so + that y->i is only done when y is preceded by a consonant, + but not if the stem is only a single consonant, i.e. + + (*c and not c) Y -> I + + So 'happy' -> 'happi', but + 'enjoy' -> 'enjoy' etc + + This is a much better rule. Formerly 'enjoy'->'enjoi' and + 'enjoyment'->'enjoy'. Step 1c is perhaps done too soon; but + with this modification that no longer really matters. + + Also, the removal of the contains_vowel(z) condition means + that 'spy', 'fly', 'try' ... stem to 'spi', 'fli', 'tri' and + conflate with 'spied', 'tried', 'flies' ... + """ + return len(stem) > 1 and self._is_consonant(stem, len(stem) - 1) + + def original_condition(stem): + return self._contains_vowel(stem) + + return self._apply_rule_list( + word, + [ + ( + "y", + "i", + nltk_condition + if self.mode == self.NLTK_EXTENSIONS + else original_condition, + ) + ], + ) + + def _step2(self, word): + """Implements Step 2 from "An algorithm for suffix stripping" + + From the paper: + + Step 2 + + (m>0) ATIONAL -> ATE relational -> relate + (m>0) TIONAL -> TION conditional -> condition + rational -> rational + (m>0) ENCI -> ENCE valenci -> valence + (m>0) ANCI -> ANCE hesitanci -> hesitance + (m>0) IZER -> IZE digitizer -> digitize + (m>0) ABLI -> ABLE conformabli -> conformable + (m>0) ALLI -> AL radicalli -> radical + (m>0) ENTLI -> ENT differentli -> different + (m>0) ELI -> E vileli - > vile + (m>0) OUSLI -> OUS analogousli -> analogous + (m>0) IZATION -> IZE vietnamization -> vietnamize + (m>0) ATION -> ATE predication -> predicate + (m>0) ATOR -> ATE operator -> operate + (m>0) ALISM -> AL feudalism -> feudal + (m>0) IVENESS -> IVE decisiveness -> decisive + (m>0) FULNESS -> FUL hopefulness -> hopeful + (m>0) OUSNESS -> OUS callousness -> callous + (m>0) ALITI -> AL formaliti -> formal + (m>0) IVITI -> IVE sensitiviti -> sensitive + (m>0) BILITI -> BLE sensibiliti -> sensible + """ + + if self.mode == self.NLTK_EXTENSIONS: + # Instead of applying the ALLI -> AL rule after '(a)bli' per + # the published algorithm, instead we apply it first, and, + # if it succeeds, run the result through step2 again. + if word.endswith("alli") and self._has_positive_measure( + self._replace_suffix(word, "alli", "") + ): + return self._step2(self._replace_suffix(word, "alli", "al")) + + bli_rule = ("bli", "ble", self._has_positive_measure) + abli_rule = ("abli", "able", self._has_positive_measure) + + rules = [ + ("ational", "ate", self._has_positive_measure), + ("tional", "tion", self._has_positive_measure), + ("enci", "ence", self._has_positive_measure), + ("anci", "ance", self._has_positive_measure), + ("izer", "ize", self._has_positive_measure), + abli_rule if self.mode == self.ORIGINAL_ALGORITHM else bli_rule, + ("alli", "al", self._has_positive_measure), + ("entli", "ent", self._has_positive_measure), + ("eli", "e", self._has_positive_measure), + ("ousli", "ous", self._has_positive_measure), + ("ization", "ize", self._has_positive_measure), + ("ation", "ate", self._has_positive_measure), + ("ator", "ate", self._has_positive_measure), + ("alism", "al", self._has_positive_measure), + ("iveness", "ive", self._has_positive_measure), + ("fulness", "ful", self._has_positive_measure), + ("ousness", "ous", self._has_positive_measure), + ("aliti", "al", self._has_positive_measure), + ("iviti", "ive", self._has_positive_measure), + ("biliti", "ble", self._has_positive_measure), + ] + + if self.mode == self.NLTK_EXTENSIONS: + rules.append(("fulli", "ful", self._has_positive_measure)) + + # The 'l' of the 'logi' -> 'log' rule is put with the stem, + # so that short stems like 'geo' 'theo' etc work like + # 'archaeo' 'philo' etc. + rules.append( + ("logi", "log", lambda stem: self._has_positive_measure(word[:-3])) + ) + + if self.mode == self.MARTIN_EXTENSIONS: + rules.append(("logi", "log", self._has_positive_measure)) + + return self._apply_rule_list(word, rules) + + def _step3(self, word): + """Implements Step 3 from "An algorithm for suffix stripping" + + From the paper: + + Step 3 + + (m>0) ICATE -> IC triplicate -> triplic + (m>0) ATIVE -> formative -> form + (m>0) ALIZE -> AL formalize -> formal + (m>0) ICITI -> IC electriciti -> electric + (m>0) ICAL -> IC electrical -> electric + (m>0) FUL -> hopeful -> hope + (m>0) NESS -> goodness -> good + """ + return self._apply_rule_list( + word, + [ + ("icate", "ic", self._has_positive_measure), + ("ative", "", self._has_positive_measure), + ("alize", "al", self._has_positive_measure), + ("iciti", "ic", self._has_positive_measure), + ("ical", "ic", self._has_positive_measure), + ("ful", "", self._has_positive_measure), + ("ness", "", self._has_positive_measure), + ], + ) + + def _step4(self, word): + """Implements Step 4 from "An algorithm for suffix stripping" + + Step 4 + + (m>1) AL -> revival -> reviv + (m>1) ANCE -> allowance -> allow + (m>1) ENCE -> inference -> infer + (m>1) ER -> airliner -> airlin + (m>1) IC -> gyroscopic -> gyroscop + (m>1) ABLE -> adjustable -> adjust + (m>1) IBLE -> defensible -> defens + (m>1) ANT -> irritant -> irrit + (m>1) EMENT -> replacement -> replac + (m>1) MENT -> adjustment -> adjust + (m>1) ENT -> dependent -> depend + (m>1 and (*S or *T)) ION -> adoption -> adopt + (m>1) OU -> homologou -> homolog + (m>1) ISM -> communism -> commun + (m>1) ATE -> activate -> activ + (m>1) ITI -> angulariti -> angular + (m>1) OUS -> homologous -> homolog + (m>1) IVE -> effective -> effect + (m>1) IZE -> bowdlerize -> bowdler + + The suffixes are now removed. All that remains is a little + tidying up. + """ + measure_gt_1 = lambda stem: self._measure(stem) > 1 + + return self._apply_rule_list( + word, + [ + ("al", "", measure_gt_1), + ("ance", "", measure_gt_1), + ("ence", "", measure_gt_1), + ("er", "", measure_gt_1), + ("ic", "", measure_gt_1), + ("able", "", measure_gt_1), + ("ible", "", measure_gt_1), + ("ant", "", measure_gt_1), + ("ement", "", measure_gt_1), + ("ment", "", measure_gt_1), + ("ent", "", measure_gt_1), + # (m>1 and (*S or *T)) ION -> + ( + "ion", + "", + lambda stem: self._measure(stem) > 1 and stem[-1] in ("s", "t"), + ), + ("ou", "", measure_gt_1), + ("ism", "", measure_gt_1), + ("ate", "", measure_gt_1), + ("iti", "", measure_gt_1), + ("ous", "", measure_gt_1), + ("ive", "", measure_gt_1), + ("ize", "", measure_gt_1), + ], + ) + + def _step5a(self, word): + """Implements Step 5a from "An algorithm for suffix stripping" + + From the paper: + + Step 5a + + (m>1) E -> probate -> probat + rate -> rate + (m=1 and not *o) E -> cease -> ceas + """ + # Note that Martin's test vocabulary and reference + # implementations are inconsistent in how they handle the case + # where two rules both refer to a suffix that matches the word + # to be stemmed, but only the condition of the second one is + # true. + # Earlier in step2b we had the rules: + # (m>0) EED -> EE + # (*v*) ED -> + # but the examples in the paper included "feed"->"feed", even + # though (*v*) is true for "fe" and therefore the second rule + # alone would map "feed"->"fe". + # However, in THIS case, we need to handle the consecutive rules + # differently and try both conditions (obviously; the second + # rule here would be redundant otherwise). Martin's paper makes + # no explicit mention of the inconsistency; you have to infer it + # from the examples. + # For this reason, we can't use _apply_rule_list here. + if word.endswith("e"): + stem = self._replace_suffix(word, "e", "") + if self._measure(stem) > 1: + return stem + if self._measure(stem) == 1 and not self._ends_cvc(stem): + return stem + return word + + def _step5b(self, word): + """Implements Step 5a from "An algorithm for suffix stripping" + + From the paper: + + Step 5b + + (m > 1 and *d and *L) -> single letter + controll -> control + roll -> roll + """ + return self._apply_rule_list( + word, [("ll", "l", lambda stem: self._measure(word[:-1]) > 1)] + ) + + def stem(self, word, to_lowercase=True): + """ + :param to_lowercase: if `to_lowercase=True` the word always lowercase + """ + stem = word.lower() if to_lowercase else word + + if self.mode == self.NLTK_EXTENSIONS and word in self.pool: + return self.pool[stem] + + if self.mode != self.ORIGINAL_ALGORITHM and len(word) <= 2: + # With this line, strings of length 1 or 2 don't go through + # the stemming process, although no mention is made of this + # in the published algorithm. + return stem + + stem = self._step1a(stem) + stem = self._step1b(stem) + stem = self._step1c(stem) + stem = self._step2(stem) + stem = self._step3(stem) + stem = self._step4(stem) + stem = self._step5a(stem) + stem = self._step5b(stem) + + return stem + + def __repr__(self): + return "" + + +def demo(): + """ + A demonstration of the porter stemmer on a sample from + the Penn Treebank corpus. + """ + + from nltk import stem + from nltk.corpus import treebank + + stemmer = stem.PorterStemmer() + + orig = [] + stemmed = [] + for item in treebank.fileids()[:3]: + for (word, tag) in treebank.tagged_words(item): + orig.append(word) + stemmed.append(stemmer.stem(word)) + + # Convert the results to a string, and word-wrap them. + results = " ".join(stemmed) + results = re.sub(r"(.{,70})\s", r"\1\n", results + " ").rstrip() + + # Convert the original to a string, and word wrap it. + original = " ".join(orig) + original = re.sub(r"(.{,70})\s", r"\1\n", original + " ").rstrip() + + # Print the results. + print("-Original-".center(70).replace(" ", "*").replace("-", " ")) + print(original) + print("-Results-".center(70).replace(" ", "*").replace("-", " ")) + print(results) + print("*" * 70) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/regexp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/regexp.py new file mode 100644 index 0000000..c0cc9cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/regexp.py @@ -0,0 +1,56 @@ +# Natural Language Toolkit: Stemmers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT +import re + +from nltk.stem.api import StemmerI + + +class RegexpStemmer(StemmerI): + """ + A stemmer that uses regular expressions to identify morphological + affixes. Any substrings that match the regular expressions will + be removed. + + >>> from nltk.stem import RegexpStemmer + >>> st = RegexpStemmer('ing$|s$|e$|able$', min=4) + >>> st.stem('cars') + 'car' + >>> st.stem('mass') + 'mas' + >>> st.stem('was') + 'was' + >>> st.stem('bee') + 'bee' + >>> st.stem('compute') + 'comput' + >>> st.stem('advisable') + 'advis' + + :type regexp: str or regexp + :param regexp: The regular expression that should be used to + identify morphological affixes. + :type min: int + :param min: The minimum length of string to stem + """ + + def __init__(self, regexp, min=0): + + if not hasattr(regexp, "pattern"): + regexp = re.compile(regexp) + self._regexp = regexp + self._min = min + + def stem(self, word): + if len(word) < self._min: + return word + else: + return self._regexp.sub("", word) + + def __repr__(self): + return f"" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/rslp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/rslp.py new file mode 100644 index 0000000..eb66837 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/rslp.py @@ -0,0 +1,137 @@ +# Natural Language Toolkit: RSLP Stemmer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tiago Tresoldi +# URL: +# For license information, see LICENSE.TXT + +# This code is based on the algorithm presented in the paper "A Stemming +# Algorithm for the Portuguese Language" by Viviane Moreira Orengo and +# Christian Huyck, which unfortunately I had no access to. The code is a +# Python version, with some minor modifications of mine, to the description +# presented at https://www.webcitation.org/5NnvdIzOb and to the C source code +# available at http://www.inf.ufrgs.br/~arcoelho/rslp/integrando_rslp.html. +# Please note that this stemmer is intended for demonstration and educational +# purposes only. Feel free to write me for any comments, including the +# development of a different and/or better stemmer for Portuguese. I also +# suggest using NLTK's mailing list for Portuguese for any discussion. + +# Este código é baseado no algoritmo apresentado no artigo "A Stemming +# Algorithm for the Portuguese Language" de Viviane Moreira Orengo e +# Christian Huyck, o qual infelizmente não tive a oportunidade de ler. O +# código é uma conversão para Python, com algumas pequenas modificações +# minhas, daquele apresentado em https://www.webcitation.org/5NnvdIzOb e do +# código para linguagem C disponível em +# http://www.inf.ufrgs.br/~arcoelho/rslp/integrando_rslp.html. Por favor, +# lembre-se de que este stemmer foi desenvolvido com finalidades unicamente +# de demonstração e didáticas. Sinta-se livre para me escrever para qualquer +# comentário, inclusive sobre o desenvolvimento de um stemmer diferente +# e/ou melhor para o português. Também sugiro utilizar-se a lista de discussão +# do NLTK para o português para qualquer debate. + +from nltk.data import load +from nltk.stem.api import StemmerI + + +class RSLPStemmer(StemmerI): + """ + A stemmer for Portuguese. + + >>> from nltk.stem import RSLPStemmer + >>> st = RSLPStemmer() + >>> # opening lines of Erico Verissimo's "Música ao Longe" + >>> text = ''' + ... Clarissa risca com giz no quadro-negro a paisagem que os alunos + ... devem copiar . Uma casinha de porta e janela , em cima duma + ... coxilha .''' + >>> for token in text.split(): # doctest: +NORMALIZE_WHITESPACE + ... print(st.stem(token)) + clariss risc com giz no quadro-negr a pais que os alun dev copi . + uma cas de port e janel , em cim dum coxilh . + """ + + def __init__(self): + self._model = [] + + self._model.append(self.read_rule("step0.pt")) + self._model.append(self.read_rule("step1.pt")) + self._model.append(self.read_rule("step2.pt")) + self._model.append(self.read_rule("step3.pt")) + self._model.append(self.read_rule("step4.pt")) + self._model.append(self.read_rule("step5.pt")) + self._model.append(self.read_rule("step6.pt")) + + def read_rule(self, filename): + rules = load("nltk:stemmers/rslp/" + filename, format="raw").decode("utf8") + lines = rules.split("\n") + + lines = [line for line in lines if line != ""] # remove blank lines + lines = [line for line in lines if line[0] != "#"] # remove comments + + # NOTE: a simple but ugly hack to make this parser happy with double '\t's + lines = [line.replace("\t\t", "\t") for line in lines] + + # parse rules + rules = [] + for line in lines: + rule = [] + tokens = line.split("\t") + + # text to be searched for at the end of the string + rule.append(tokens[0][1:-1]) # remove quotes + + # minimum stem size to perform the replacement + rule.append(int(tokens[1])) + + # text to be replaced into + rule.append(tokens[2][1:-1]) # remove quotes + + # exceptions to this rule + rule.append([token[1:-1] for token in tokens[3].split(",")]) + + # append to the results + rules.append(rule) + + return rules + + def stem(self, word): + word = word.lower() + + # the word ends in 's'? apply rule for plural reduction + if word[-1] == "s": + word = self.apply_rule(word, 0) + + # the word ends in 'a'? apply rule for feminine reduction + if word[-1] == "a": + word = self.apply_rule(word, 1) + + # augmentative reduction + word = self.apply_rule(word, 3) + + # adverb reduction + word = self.apply_rule(word, 2) + + # noun reduction + prev_word = word + word = self.apply_rule(word, 4) + if word == prev_word: + # verb reduction + prev_word = word + word = self.apply_rule(word, 5) + if word == prev_word: + # vowel removal + word = self.apply_rule(word, 6) + + return word + + def apply_rule(self, word, rule_index): + rules = self._model[rule_index] + for rule in rules: + suffix_length = len(rule[0]) + if word[-suffix_length:] == rule[0]: # if suffix matches + if len(word) >= suffix_length + rule[1]: # if we have minimum size + if word not in rule[3]: # if not an exception + word = word[:-suffix_length] + rule[2] + break + + return word diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/snowball.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/snowball.py new file mode 100644 index 0000000..1e9361b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/snowball.py @@ -0,0 +1,5946 @@ +# +# Natural Language Toolkit: Snowball Stemmer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Peter Michael Stahl +# Peter Ljunglof (revisions) +# Lakhdar Benzahia (co-writer) +# Assem Chelli (reviewer arabicstemmer) +# Abdelkrim Aries (reviewer arabicstemmer) +# Algorithms: Dr Martin Porter +# Assem Chelli arabic stemming algorithm +# Benzahia Lakhdar +# URL: +# For license information, see LICENSE.TXT + +""" +Snowball stemmers + +This module provides a port of the Snowball stemmers +developed by Martin Porter. + +There is also a demo function: `snowball.demo()`. + +""" + +import re + +from nltk.corpus import stopwords +from nltk.stem import porter +from nltk.stem.api import StemmerI +from nltk.stem.util import prefix_replace, suffix_replace + + +class SnowballStemmer(StemmerI): + + """ + Snowball Stemmer + + The following languages are supported: + Arabic, Danish, Dutch, English, Finnish, French, German, + Hungarian, Italian, Norwegian, Portuguese, Romanian, Russian, + Spanish and Swedish. + + The algorithm for English is documented here: + + Porter, M. \"An algorithm for suffix stripping.\" + Program 14.3 (1980): 130-137. + + The algorithms have been developed by Martin Porter. + These stemmers are called Snowball, because Porter created + a programming language with this name for creating + new stemming algorithms. There is more information available + at http://snowball.tartarus.org/ + + The stemmer is invoked as shown below: + + >>> from nltk.stem import SnowballStemmer # See which languages are supported + >>> print(" ".join(SnowballStemmer.languages)) # doctest: +NORMALIZE_WHITESPACE + arabic danish dutch english finnish french german hungarian + italian norwegian porter portuguese romanian russian + spanish swedish + >>> stemmer = SnowballStemmer("german") # Choose a language + >>> stemmer.stem("Autobahnen") # Stem a word + 'autobahn' + + Invoking the stemmers that way is useful if you do not know the + language to be stemmed at runtime. Alternatively, if you already know + the language, then you can invoke the language specific stemmer directly: + + >>> from nltk.stem.snowball import GermanStemmer + >>> stemmer = GermanStemmer() + >>> stemmer.stem("Autobahnen") + 'autobahn' + + :param language: The language whose subclass is instantiated. + :type language: str or unicode + :param ignore_stopwords: If set to True, stopwords are + not stemmed and returned unchanged. + Set to False by default. + :type ignore_stopwords: bool + :raise ValueError: If there is no stemmer for the specified + language, a ValueError is raised. + """ + + languages = ( + "arabic", + "danish", + "dutch", + "english", + "finnish", + "french", + "german", + "hungarian", + "italian", + "norwegian", + "porter", + "portuguese", + "romanian", + "russian", + "spanish", + "swedish", + ) + + def __init__(self, language, ignore_stopwords=False): + if language not in self.languages: + raise ValueError(f"The language '{language}' is not supported.") + stemmerclass = globals()[language.capitalize() + "Stemmer"] + self.stemmer = stemmerclass(ignore_stopwords) + self.stem = self.stemmer.stem + self.stopwords = self.stemmer.stopwords + + def stem(self, token): + return self.stemmer.stem(self, token) + + +class _LanguageSpecificStemmer(StemmerI): + + """ + This helper subclass offers the possibility + to invoke a specific stemmer directly. + This is useful if you already know the language to be stemmed at runtime. + + Create an instance of the Snowball stemmer. + + :param ignore_stopwords: If set to True, stopwords are + not stemmed and returned unchanged. + Set to False by default. + :type ignore_stopwords: bool + """ + + def __init__(self, ignore_stopwords=False): + # The language is the name of the class, minus the final "Stemmer". + language = type(self).__name__.lower() + if language.endswith("stemmer"): + language = language[:-7] + + self.stopwords = set() + if ignore_stopwords: + try: + for word in stopwords.words(language): + self.stopwords.add(word) + except OSError as e: + raise ValueError( + "{!r} has no list of stopwords. Please set" + " 'ignore_stopwords' to 'False'.".format(self) + ) from e + + def __repr__(self): + """ + Print out the string representation of the respective class. + + """ + return f"<{type(self).__name__}>" + + +class PorterStemmer(_LanguageSpecificStemmer, porter.PorterStemmer): + """ + A word stemmer based on the original Porter stemming algorithm. + + Porter, M. \"An algorithm for suffix stripping.\" + Program 14.3 (1980): 130-137. + + A few minor modifications have been made to Porter's basic + algorithm. See the source code of the module + nltk.stem.porter for more information. + + """ + + def __init__(self, ignore_stopwords=False): + _LanguageSpecificStemmer.__init__(self, ignore_stopwords) + porter.PorterStemmer.__init__(self) + + +class _ScandinavianStemmer(_LanguageSpecificStemmer): + + """ + This subclass encapsulates a method for defining the string region R1. + It is used by the Danish, Norwegian, and Swedish stemmer. + + """ + + def _r1_scandinavian(self, word, vowels): + """ + Return the region R1 that is used by the Scandinavian stemmers. + + R1 is the region after the first non-vowel following a vowel, + or is the null region at the end of the word if there is no + such non-vowel. But then R1 is adjusted so that the region + before it contains at least three letters. + + :param word: The word whose region R1 is determined. + :type word: str or unicode + :param vowels: The vowels of the respective language that are + used to determine the region R1. + :type vowels: unicode + :return: the region R1 for the respective word. + :rtype: unicode + :note: This helper method is invoked by the respective stem method of + the subclasses DanishStemmer, NorwegianStemmer, and + SwedishStemmer. It is not to be invoked directly! + + """ + r1 = "" + for i in range(1, len(word)): + if word[i] not in vowels and word[i - 1] in vowels: + if 3 > len(word[: i + 1]) > 0: + r1 = word[3:] + elif len(word[: i + 1]) >= 3: + r1 = word[i + 1 :] + else: + return word + break + + return r1 + + +class _StandardStemmer(_LanguageSpecificStemmer): + + """ + This subclass encapsulates two methods for defining the standard versions + of the string regions R1, R2, and RV. + + """ + + def _r1r2_standard(self, word, vowels): + """ + Return the standard interpretations of the string regions R1 and R2. + + R1 is the region after the first non-vowel following a vowel, + or is the null region at the end of the word if there is no + such non-vowel. + + R2 is the region after the first non-vowel following a vowel + in R1, or is the null region at the end of the word if there + is no such non-vowel. + + :param word: The word whose regions R1 and R2 are determined. + :type word: str or unicode + :param vowels: The vowels of the respective language that are + used to determine the regions R1 and R2. + :type vowels: unicode + :return: (r1,r2), the regions R1 and R2 for the respective word. + :rtype: tuple + :note: This helper method is invoked by the respective stem method of + the subclasses DutchStemmer, FinnishStemmer, + FrenchStemmer, GermanStemmer, ItalianStemmer, + PortugueseStemmer, RomanianStemmer, and SpanishStemmer. + It is not to be invoked directly! + :note: A detailed description of how to define R1 and R2 + can be found at http://snowball.tartarus.org/texts/r1r2.html + + """ + r1 = "" + r2 = "" + for i in range(1, len(word)): + if word[i] not in vowels and word[i - 1] in vowels: + r1 = word[i + 1 :] + break + + for i in range(1, len(r1)): + if r1[i] not in vowels and r1[i - 1] in vowels: + r2 = r1[i + 1 :] + break + + return (r1, r2) + + def _rv_standard(self, word, vowels): + """ + Return the standard interpretation of the string region RV. + + If the second letter is a consonant, RV is the region after the + next following vowel. If the first two letters are vowels, RV is + the region after the next following consonant. Otherwise, RV is + the region after the third letter. + + :param word: The word whose region RV is determined. + :type word: str or unicode + :param vowels: The vowels of the respective language that are + used to determine the region RV. + :type vowels: unicode + :return: the region RV for the respective word. + :rtype: unicode + :note: This helper method is invoked by the respective stem method of + the subclasses ItalianStemmer, PortugueseStemmer, + RomanianStemmer, and SpanishStemmer. It is not to be + invoked directly! + + """ + rv = "" + if len(word) >= 2: + if word[1] not in vowels: + for i in range(2, len(word)): + if word[i] in vowels: + rv = word[i + 1 :] + break + + elif word[0] in vowels and word[1] in vowels: + for i in range(2, len(word)): + if word[i] not in vowels: + rv = word[i + 1 :] + break + else: + rv = word[3:] + + return rv + + +class ArabicStemmer(_StandardStemmer): + """ + https://github.com/snowballstem/snowball/blob/master/algorithms/arabic/stem_Unicode.sbl (Original Algorithm) + The Snowball Arabic light Stemmer + Algorithm: + + - Assem Chelli + - Abdelkrim Aries + - Lakhdar Benzahia + + NLTK Version Author: + + - Lakhdar Benzahia + """ + + # Normalize_pre stes + __vocalization = re.compile( + r"[\u064b-\u064c-\u064d-\u064e-\u064f-\u0650-\u0651-\u0652]" + ) # ً، ٌ، ٍ، َ، ُ، ِ، ّ، ْ + + __kasheeda = re.compile(r"[\u0640]") # ـ tatweel/kasheeda + + __arabic_punctuation_marks = re.compile(r"[\u060C-\u061B-\u061F]") # ؛ ، ؟ + + # Normalize_post + __last_hamzat = ("\u0623", "\u0625", "\u0622", "\u0624", "\u0626") # أ، إ، آ، ؤ، ئ + + # normalize other hamza's + __initial_hamzat = re.compile(r"^[\u0622\u0623\u0625]") # أ، إ، آ + + __waw_hamza = re.compile(r"[\u0624]") # ؤ + + __yeh_hamza = re.compile(r"[\u0626]") # ئ + + __alefat = re.compile(r"[\u0623\u0622\u0625]") # أ، إ، آ + + # Checks + __checks1 = ( + "\u0643\u0627\u0644", + "\u0628\u0627\u0644", # بال، كال + "\u0627\u0644", + "\u0644\u0644", # لل، ال + ) + + __checks2 = ("\u0629", "\u0627\u062a") # ة # female plural ات + + # Suffixes + __suffix_noun_step1a = ( + "\u064a", + "\u0643", + "\u0647", # ي، ك، ه + "\u0646\u0627", + "\u0643\u0645", + "\u0647\u0627", + "\u0647\u0646", + "\u0647\u0645", # نا، كم، ها، هن، هم + "\u0643\u0645\u0627", + "\u0647\u0645\u0627", # كما، هما + ) + + __suffix_noun_step1b = "\u0646" # ن + + __suffix_noun_step2a = ("\u0627", "\u064a", "\u0648") # ا، ي، و + + __suffix_noun_step2b = "\u0627\u062a" # ات + + __suffix_noun_step2c1 = "\u062a" # ت + + __suffix_noun_step2c2 = "\u0629" # ة + + __suffix_noun_step3 = "\u064a" # ي + + __suffix_verb_step1 = ( + "\u0647", + "\u0643", # ه، ك + "\u0646\u064a", + "\u0646\u0627", + "\u0647\u0627", + "\u0647\u0645", # ني، نا، ها، هم + "\u0647\u0646", + "\u0643\u0645", + "\u0643\u0646", # هن، كم، كن + "\u0647\u0645\u0627", + "\u0643\u0645\u0627", + "\u0643\u0645\u0648", # هما، كما، كمو + ) + + __suffix_verb_step2a = ( + "\u062a", + "\u0627", + "\u0646", + "\u064a", # ت، ا، ن، ي + "\u0646\u0627", + "\u062a\u0627", + "\u062a\u0646", # نا، تا، تن Past + "\u0627\u0646", + "\u0648\u0646", + "\u064a\u0646", # ان، هن، ين Present + "\u062a\u0645\u0627", # تما + ) + + __suffix_verb_step2b = ("\u0648\u0627", "\u062a\u0645") # وا، تم + + __suffix_verb_step2c = ("\u0648", "\u062a\u0645\u0648") # و # تمو + + __suffix_all_alef_maqsura = "\u0649" # ى + + # Prefixes + __prefix_step1 = ( + "\u0623", # أ + "\u0623\u0623", + "\u0623\u0622", + "\u0623\u0624", + "\u0623\u0627", + "\u0623\u0625", # أأ، أآ، أؤ، أا، أإ + ) + + __prefix_step2a = ("\u0641\u0627\u0644", "\u0648\u0627\u0644") # فال، وال + + __prefix_step2b = ("\u0641", "\u0648") # ف، و + + __prefix_step3a_noun = ( + "\u0627\u0644", + "\u0644\u0644", # لل، ال + "\u0643\u0627\u0644", + "\u0628\u0627\u0644", # بال، كال + ) + + __prefix_step3b_noun = ( + "\u0628", + "\u0643", + "\u0644", # ب، ك، ل + "\u0628\u0628", + "\u0643\u0643", # بب، كك + ) + + __prefix_step3_verb = ( + "\u0633\u064a", + "\u0633\u062a", + "\u0633\u0646", + "\u0633\u0623", + ) # سي، ست، سن، سأ + + __prefix_step4_verb = ( + "\u064a\u0633\u062a", + "\u0646\u0633\u062a", + "\u062a\u0633\u062a", + ) # يست، نست، تست + + # Suffixes added due to Conjugation Verbs + __conjugation_suffix_verb_1 = ("\u0647", "\u0643") # ه، ك + + __conjugation_suffix_verb_2 = ( + "\u0646\u064a", + "\u0646\u0627", + "\u0647\u0627", # ني، نا، ها + "\u0647\u0645", + "\u0647\u0646", + "\u0643\u0645", # هم، هن، كم + "\u0643\u0646", # كن + ) + __conjugation_suffix_verb_3 = ( + "\u0647\u0645\u0627", + "\u0643\u0645\u0627", + "\u0643\u0645\u0648", + ) # هما، كما، كمو + + __conjugation_suffix_verb_4 = ("\u0627", "\u0646", "\u064a") # ا، ن، ي + + __conjugation_suffix_verb_past = ( + "\u0646\u0627", + "\u062a\u0627", + "\u062a\u0646", + ) # نا، تا، تن + + __conjugation_suffix_verb_present = ( + "\u0627\u0646", + "\u0648\u0646", + "\u064a\u0646", + ) # ان، ون، ين + + # Suffixes added due to derivation Names + __conjugation_suffix_noun_1 = ("\u064a", "\u0643", "\u0647") # ي، ك، ه + + __conjugation_suffix_noun_2 = ( + "\u0646\u0627", + "\u0643\u0645", # نا، كم + "\u0647\u0627", + "\u0647\u0646", + "\u0647\u0645", # ها، هن، هم + ) + + __conjugation_suffix_noun_3 = ( + "\u0643\u0645\u0627", + "\u0647\u0645\u0627", + ) # كما، هما + + # Prefixes added due to derivation Names + __prefixes1 = ("\u0648\u0627", "\u0641\u0627") # فا، وا + + __articles_3len = ("\u0643\u0627\u0644", "\u0628\u0627\u0644") # بال كال + + __articles_2len = ("\u0627\u0644", "\u0644\u0644") # ال لل + + # Prepositions letters + __prepositions1 = ("\u0643", "\u0644") # ك، ل + __prepositions2 = ("\u0628\u0628", "\u0643\u0643") # بب، كك + + is_verb = True + is_noun = True + is_defined = False + + suffixes_verb_step1_success = False + suffix_verb_step2a_success = False + suffix_verb_step2b_success = False + suffix_noun_step2c2_success = False + suffix_noun_step1a_success = False + suffix_noun_step2a_success = False + suffix_noun_step2b_success = False + suffixe_noun_step1b_success = False + prefix_step2a_success = False + prefix_step3a_noun_success = False + prefix_step3b_noun_success = False + + def __normalize_pre(self, token): + """ + :param token: string + :return: normalized token type string + """ + # strip diacritics + token = self.__vocalization.sub("", token) + # strip kasheeda + token = self.__kasheeda.sub("", token) + # strip punctuation marks + token = self.__arabic_punctuation_marks.sub("", token) + return token + + def __normalize_post(self, token): + # normalize last hamza + for hamza in self.__last_hamzat: + if token.endswith(hamza): + token = suffix_replace(token, hamza, "\u0621") + break + # normalize other hamzat + token = self.__initial_hamzat.sub("\u0627", token) + token = self.__waw_hamza.sub("\u0648", token) + token = self.__yeh_hamza.sub("\u064a", token) + token = self.__alefat.sub("\u0627", token) + return token + + def __checks_1(self, token): + for prefix in self.__checks1: + if token.startswith(prefix): + if prefix in self.__articles_3len and len(token) > 4: + self.is_noun = True + self.is_verb = False + self.is_defined = True + break + + if prefix in self.__articles_2len and len(token) > 3: + self.is_noun = True + self.is_verb = False + self.is_defined = True + break + + def __checks_2(self, token): + for suffix in self.__checks2: + if token.endswith(suffix): + if suffix == "\u0629" and len(token) > 2: + self.is_noun = True + self.is_verb = False + break + + if suffix == "\u0627\u062a" and len(token) > 3: + self.is_noun = True + self.is_verb = False + break + + def __Suffix_Verb_Step1(self, token): + for suffix in self.__suffix_verb_step1: + if token.endswith(suffix): + if suffix in self.__conjugation_suffix_verb_1 and len(token) >= 4: + token = token[:-1] + self.suffixes_verb_step1_success = True + break + + if suffix in self.__conjugation_suffix_verb_2 and len(token) >= 5: + token = token[:-2] + self.suffixes_verb_step1_success = True + break + + if suffix in self.__conjugation_suffix_verb_3 and len(token) >= 6: + token = token[:-3] + self.suffixes_verb_step1_success = True + break + return token + + def __Suffix_Verb_Step2a(self, token): + for suffix in self.__suffix_verb_step2a: + if token.endswith(suffix) and len(token) > 3: + if suffix == "\u062a" and len(token) >= 4: + token = token[:-1] + self.suffix_verb_step2a_success = True + break + + if suffix in self.__conjugation_suffix_verb_4 and len(token) >= 4: + token = token[:-1] + self.suffix_verb_step2a_success = True + break + + if suffix in self.__conjugation_suffix_verb_past and len(token) >= 5: + token = token[:-2] # past + self.suffix_verb_step2a_success = True + break + + if suffix in self.__conjugation_suffix_verb_present and len(token) > 5: + token = token[:-2] # present + self.suffix_verb_step2a_success = True + break + + if suffix == "\u062a\u0645\u0627" and len(token) >= 6: + token = token[:-3] + self.suffix_verb_step2a_success = True + break + return token + + def __Suffix_Verb_Step2c(self, token): + for suffix in self.__suffix_verb_step2c: + if token.endswith(suffix): + if suffix == "\u062a\u0645\u0648" and len(token) >= 6: + token = token[:-3] + break + + if suffix == "\u0648" and len(token) >= 4: + token = token[:-1] + break + return token + + def __Suffix_Verb_Step2b(self, token): + for suffix in self.__suffix_verb_step2b: + if token.endswith(suffix) and len(token) >= 5: + token = token[:-2] + self.suffix_verb_step2b_success = True + break + return token + + def __Suffix_Noun_Step2c2(self, token): + for suffix in self.__suffix_noun_step2c2: + if token.endswith(suffix) and len(token) >= 3: + token = token[:-1] + self.suffix_noun_step2c2_success = True + break + return token + + def __Suffix_Noun_Step1a(self, token): + for suffix in self.__suffix_noun_step1a: + if token.endswith(suffix): + if suffix in self.__conjugation_suffix_noun_1 and len(token) >= 4: + token = token[:-1] + self.suffix_noun_step1a_success = True + break + + if suffix in self.__conjugation_suffix_noun_2 and len(token) >= 5: + token = token[:-2] + self.suffix_noun_step1a_success = True + break + + if suffix in self.__conjugation_suffix_noun_3 and len(token) >= 6: + token = token[:-3] + self.suffix_noun_step1a_success = True + break + return token + + def __Suffix_Noun_Step2a(self, token): + for suffix in self.__suffix_noun_step2a: + if token.endswith(suffix) and len(token) > 4: + token = token[:-1] + self.suffix_noun_step2a_success = True + break + return token + + def __Suffix_Noun_Step2b(self, token): + for suffix in self.__suffix_noun_step2b: + if token.endswith(suffix) and len(token) >= 5: + token = token[:-2] + self.suffix_noun_step2b_success = True + break + return token + + def __Suffix_Noun_Step2c1(self, token): + for suffix in self.__suffix_noun_step2c1: + if token.endswith(suffix) and len(token) >= 4: + token = token[:-1] + break + return token + + def __Suffix_Noun_Step1b(self, token): + for suffix in self.__suffix_noun_step1b: + if token.endswith(suffix) and len(token) > 5: + token = token[:-1] + self.suffixe_noun_step1b_success = True + break + return token + + def __Suffix_Noun_Step3(self, token): + for suffix in self.__suffix_noun_step3: + if token.endswith(suffix) and len(token) >= 3: + token = token[:-1] # ya' nisbiya + break + return token + + def __Suffix_All_alef_maqsura(self, token): + for suffix in self.__suffix_all_alef_maqsura: + if token.endswith(suffix): + token = suffix_replace(token, suffix, "\u064a") + return token + + def __Prefix_Step1(self, token): + for prefix in self.__prefix_step1: + if token.startswith(prefix) and len(token) > 3: + if prefix == "\u0623\u0623": + token = prefix_replace(token, prefix, "\u0623") + break + + elif prefix == "\u0623\u0622": + token = prefix_replace(token, prefix, "\u0622") + break + + elif prefix == "\u0623\u0624": + token = prefix_replace(token, prefix, "\u0624") + break + + elif prefix == "\u0623\u0627": + token = prefix_replace(token, prefix, "\u0627") + break + + elif prefix == "\u0623\u0625": + token = prefix_replace(token, prefix, "\u0625") + break + return token + + def __Prefix_Step2a(self, token): + for prefix in self.__prefix_step2a: + if token.startswith(prefix) and len(token) > 5: + token = token[len(prefix) :] + self.prefix_step2a_success = True + break + return token + + def __Prefix_Step2b(self, token): + for prefix in self.__prefix_step2b: + if token.startswith(prefix) and len(token) > 3: + if token[:2] not in self.__prefixes1: + token = token[len(prefix) :] + break + return token + + def __Prefix_Step3a_Noun(self, token): + for prefix in self.__prefix_step3a_noun: + if token.startswith(prefix): + if prefix in self.__articles_2len and len(token) > 4: + token = token[len(prefix) :] + self.prefix_step3a_noun_success = True + break + if prefix in self.__articles_3len and len(token) > 5: + token = token[len(prefix) :] + break + return token + + def __Prefix_Step3b_Noun(self, token): + for prefix in self.__prefix_step3b_noun: + if token.startswith(prefix): + if len(token) > 3: + if prefix == "\u0628": + token = token[len(prefix) :] + self.prefix_step3b_noun_success = True + break + + if prefix in self.__prepositions2: + token = prefix_replace(token, prefix, prefix[1]) + self.prefix_step3b_noun_success = True + break + + if prefix in self.__prepositions1 and len(token) > 4: + token = token[len(prefix) :] # BUG: cause confusion + self.prefix_step3b_noun_success = True + break + return token + + def __Prefix_Step3_Verb(self, token): + for prefix in self.__prefix_step3_verb: + if token.startswith(prefix) and len(token) > 4: + token = prefix_replace(token, prefix, prefix[1]) + break + return token + + def __Prefix_Step4_Verb(self, token): + for prefix in self.__prefix_step4_verb: + if token.startswith(prefix) and len(token) > 4: + token = prefix_replace(token, prefix, "\u0627\u0633\u062a") + self.is_verb = True + self.is_noun = False + break + return token + + def stem(self, word): + """ + Stem an Arabic word and return the stemmed form. + + :param word: string + :return: string + """ + # set initial values + self.is_verb = True + self.is_noun = True + self.is_defined = False + + self.suffix_verb_step2a_success = False + self.suffix_verb_step2b_success = False + self.suffix_noun_step2c2_success = False + self.suffix_noun_step1a_success = False + self.suffix_noun_step2a_success = False + self.suffix_noun_step2b_success = False + self.suffixe_noun_step1b_success = False + self.prefix_step2a_success = False + self.prefix_step3a_noun_success = False + self.prefix_step3b_noun_success = False + + modified_word = word + # guess type and properties + # checks1 + self.__checks_1(modified_word) + # checks2 + self.__checks_2(modified_word) + # Pre_Normalization + modified_word = self.__normalize_pre(modified_word) + # Avoid stopwords + if modified_word in self.stopwords or len(modified_word) <= 2: + return modified_word + # Start stemming + if self.is_verb: + modified_word = self.__Suffix_Verb_Step1(modified_word) + if self.suffixes_verb_step1_success: + modified_word = self.__Suffix_Verb_Step2a(modified_word) + if not self.suffix_verb_step2a_success: + modified_word = self.__Suffix_Verb_Step2c(modified_word) + # or next TODO: How to deal with or next instruction + else: + modified_word = self.__Suffix_Verb_Step2b(modified_word) + if not self.suffix_verb_step2b_success: + modified_word = self.__Suffix_Verb_Step2a(modified_word) + if self.is_noun: + modified_word = self.__Suffix_Noun_Step2c2(modified_word) + if not self.suffix_noun_step2c2_success: + if not self.is_defined: + modified_word = self.__Suffix_Noun_Step1a(modified_word) + # if self.suffix_noun_step1a_success: + modified_word = self.__Suffix_Noun_Step2a(modified_word) + if not self.suffix_noun_step2a_success: + modified_word = self.__Suffix_Noun_Step2b(modified_word) + if ( + not self.suffix_noun_step2b_success + and not self.suffix_noun_step2a_success + ): + modified_word = self.__Suffix_Noun_Step2c1(modified_word) + # or next ? todo : how to deal with or next + else: + modified_word = self.__Suffix_Noun_Step1b(modified_word) + if self.suffixe_noun_step1b_success: + modified_word = self.__Suffix_Noun_Step2a(modified_word) + if not self.suffix_noun_step2a_success: + modified_word = self.__Suffix_Noun_Step2b(modified_word) + if ( + not self.suffix_noun_step2b_success + and not self.suffix_noun_step2a_success + ): + modified_word = self.__Suffix_Noun_Step2c1(modified_word) + else: + if not self.is_defined: + modified_word = self.__Suffix_Noun_Step2a(modified_word) + modified_word = self.__Suffix_Noun_Step2b(modified_word) + modified_word = self.__Suffix_Noun_Step3(modified_word) + if not self.is_noun and self.is_verb: + modified_word = self.__Suffix_All_alef_maqsura(modified_word) + + # prefixes + modified_word = self.__Prefix_Step1(modified_word) + modified_word = self.__Prefix_Step2a(modified_word) + if not self.prefix_step2a_success: + modified_word = self.__Prefix_Step2b(modified_word) + modified_word = self.__Prefix_Step3a_Noun(modified_word) + if not self.prefix_step3a_noun_success and self.is_noun: + modified_word = self.__Prefix_Step3b_Noun(modified_word) + else: + if not self.prefix_step3b_noun_success and self.is_verb: + modified_word = self.__Prefix_Step3_Verb(modified_word) + modified_word = self.__Prefix_Step4_Verb(modified_word) + + # post normalization stemming + modified_word = self.__normalize_post(modified_word) + stemmed_word = modified_word + return stemmed_word + + +class DanishStemmer(_ScandinavianStemmer): + + """ + The Danish Snowball stemmer. + + :cvar __vowels: The Danish vowels. + :type __vowels: unicode + :cvar __consonants: The Danish consonants. + :type __consonants: unicode + :cvar __double_consonants: The Danish double consonants. + :type __double_consonants: tuple + :cvar __s_ending: Letters that may directly appear before a word final 's'. + :type __s_ending: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the Danish + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/danish/stemmer.html + + """ + + # The language's vowels and other important characters are defined. + __vowels = "aeiouy\xE6\xE5\xF8" + __consonants = "bcdfghjklmnpqrstvwxz" + __double_consonants = ( + "bb", + "cc", + "dd", + "ff", + "gg", + "hh", + "jj", + "kk", + "ll", + "mm", + "nn", + "pp", + "qq", + "rr", + "ss", + "tt", + "vv", + "ww", + "xx", + "zz", + ) + __s_ending = "abcdfghjklmnoprtvyz\xE5" + + # The different suffixes, divided into the algorithm's steps + # and organized by length, are listed in tuples. + __step1_suffixes = ( + "erendes", + "erende", + "hedens", + "ethed", + "erede", + "heden", + "heder", + "endes", + "ernes", + "erens", + "erets", + "ered", + "ende", + "erne", + "eren", + "erer", + "heds", + "enes", + "eres", + "eret", + "hed", + "ene", + "ere", + "ens", + "ers", + "ets", + "en", + "er", + "es", + "et", + "e", + "s", + ) + __step2_suffixes = ("gd", "dt", "gt", "kt") + __step3_suffixes = ("elig", "l\xF8st", "lig", "els", "ig") + + def stem(self, word): + """ + Stem a Danish word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + # Every word is put into lower case for normalization. + word = word.lower() + + if word in self.stopwords: + return word + + # After this, the required regions are generated + # by the respective helper method. + r1 = self._r1_scandinavian(word, self.__vowels) + + # Then the actual stemming process starts. + # Every new step is explicitly indicated + # according to the descriptions on the Snowball website. + + # STEP 1 + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if suffix == "s": + if word[-2] in self.__s_ending: + word = word[:-1] + r1 = r1[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 2 + for suffix in self.__step2_suffixes: + if r1.endswith(suffix): + word = word[:-1] + r1 = r1[:-1] + break + + # STEP 3 + if r1.endswith("igst"): + word = word[:-2] + r1 = r1[:-2] + + for suffix in self.__step3_suffixes: + if r1.endswith(suffix): + if suffix == "l\xF8st": + word = word[:-1] + r1 = r1[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + + if r1.endswith(self.__step2_suffixes): + word = word[:-1] + r1 = r1[:-1] + break + + # STEP 4: Undouble + for double_cons in self.__double_consonants: + if word.endswith(double_cons) and len(word) > 3: + word = word[:-1] + break + + return word + + +class DutchStemmer(_StandardStemmer): + + """ + The Dutch Snowball stemmer. + + :cvar __vowels: The Dutch vowels. + :type __vowels: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step3b_suffixes: Suffixes to be deleted in step 3b of the algorithm. + :type __step3b_suffixes: tuple + :note: A detailed description of the Dutch + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/dutch/stemmer.html + + """ + + __vowels = "aeiouy\xE8" + __step1_suffixes = ("heden", "ene", "en", "se", "s") + __step3b_suffixes = ("baar", "lijk", "bar", "end", "ing", "ig") + + def stem(self, word): + """ + Stem a Dutch word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step2_success = False + + # Vowel accents are removed. + word = ( + word.replace("\xE4", "a") + .replace("\xE1", "a") + .replace("\xEB", "e") + .replace("\xE9", "e") + .replace("\xED", "i") + .replace("\xEF", "i") + .replace("\xF6", "o") + .replace("\xF3", "o") + .replace("\xFC", "u") + .replace("\xFA", "u") + ) + + # An initial 'y', a 'y' after a vowel, + # and an 'i' between self.__vowels is put into upper case. + # As from now these are treated as consonants. + if word.startswith("y"): + word = "".join(("Y", word[1:])) + + for i in range(1, len(word)): + if word[i - 1] in self.__vowels and word[i] == "y": + word = "".join((word[:i], "Y", word[i + 1 :])) + + for i in range(1, len(word) - 1): + if ( + word[i - 1] in self.__vowels + and word[i] == "i" + and word[i + 1] in self.__vowels + ): + word = "".join((word[:i], "I", word[i + 1 :])) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + + # R1 is adjusted so that the region before it + # contains at least 3 letters. + for i in range(1, len(word)): + if word[i] not in self.__vowels and word[i - 1] in self.__vowels: + if 3 > len(word[: i + 1]) > 0: + r1 = word[3:] + elif len(word[: i + 1]) == 0: + return word + break + + # STEP 1 + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if suffix == "heden": + word = suffix_replace(word, suffix, "heid") + r1 = suffix_replace(r1, suffix, "heid") + if r2.endswith("heden"): + r2 = suffix_replace(r2, suffix, "heid") + + elif ( + suffix in ("ene", "en") + and not word.endswith("heden") + and word[-len(suffix) - 1] not in self.__vowels + and word[-len(suffix) - 3 : -len(suffix)] != "gem" + ): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + if word.endswith(("kk", "dd", "tt")): + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + elif ( + suffix in ("se", "s") + and word[-len(suffix) - 1] not in self.__vowels + and word[-len(suffix) - 1] != "j" + ): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 2 + if r1.endswith("e") and word[-2] not in self.__vowels: + step2_success = True + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + if word.endswith(("kk", "dd", "tt")): + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + # STEP 3a + if r2.endswith("heid") and word[-5] != "c": + word = word[:-4] + r1 = r1[:-4] + r2 = r2[:-4] + + if ( + r1.endswith("en") + and word[-3] not in self.__vowels + and word[-5:-2] != "gem" + ): + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + if word.endswith(("kk", "dd", "tt")): + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + # STEP 3b: Derivational suffixes + for suffix in self.__step3b_suffixes: + if r2.endswith(suffix): + if suffix in ("end", "ing"): + word = word[:-3] + r2 = r2[:-3] + + if r2.endswith("ig") and word[-3] != "e": + word = word[:-2] + else: + if word.endswith(("kk", "dd", "tt")): + word = word[:-1] + + elif suffix == "ig" and word[-3] != "e": + word = word[:-2] + + elif suffix == "lijk": + word = word[:-4] + r1 = r1[:-4] + + if r1.endswith("e") and word[-2] not in self.__vowels: + word = word[:-1] + if word.endswith(("kk", "dd", "tt")): + word = word[:-1] + + elif suffix == "baar": + word = word[:-4] + + elif suffix == "bar" and step2_success: + word = word[:-3] + break + + # STEP 4: Undouble vowel + if len(word) >= 4: + if word[-1] not in self.__vowels and word[-1] != "I": + if word[-3:-1] in ("aa", "ee", "oo", "uu"): + if word[-4] not in self.__vowels: + word = "".join((word[:-3], word[-3], word[-1])) + + # All occurrences of 'I' and 'Y' are put back into lower case. + word = word.replace("I", "i").replace("Y", "y") + + return word + + +class EnglishStemmer(_StandardStemmer): + + """ + The English Snowball stemmer. + + :cvar __vowels: The English vowels. + :type __vowels: unicode + :cvar __double_consonants: The English double consonants. + :type __double_consonants: tuple + :cvar __li_ending: Letters that may directly appear before a word final 'li'. + :type __li_ending: unicode + :cvar __step0_suffixes: Suffixes to be deleted in step 0 of the algorithm. + :type __step0_suffixes: tuple + :cvar __step1a_suffixes: Suffixes to be deleted in step 1a of the algorithm. + :type __step1a_suffixes: tuple + :cvar __step1b_suffixes: Suffixes to be deleted in step 1b of the algorithm. + :type __step1b_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :cvar __step4_suffixes: Suffixes to be deleted in step 4 of the algorithm. + :type __step4_suffixes: tuple + :cvar __step5_suffixes: Suffixes to be deleted in step 5 of the algorithm. + :type __step5_suffixes: tuple + :cvar __special_words: A dictionary containing words + which have to be stemmed specially. + :type __special_words: dict + :note: A detailed description of the English + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/english/stemmer.html + """ + + __vowels = "aeiouy" + __double_consonants = ("bb", "dd", "ff", "gg", "mm", "nn", "pp", "rr", "tt") + __li_ending = "cdeghkmnrt" + __step0_suffixes = ("'s'", "'s", "'") + __step1a_suffixes = ("sses", "ied", "ies", "us", "ss", "s") + __step1b_suffixes = ("eedly", "ingly", "edly", "eed", "ing", "ed") + __step2_suffixes = ( + "ization", + "ational", + "fulness", + "ousness", + "iveness", + "tional", + "biliti", + "lessli", + "entli", + "ation", + "alism", + "aliti", + "ousli", + "iviti", + "fulli", + "enci", + "anci", + "abli", + "izer", + "ator", + "alli", + "bli", + "ogi", + "li", + ) + __step3_suffixes = ( + "ational", + "tional", + "alize", + "icate", + "iciti", + "ative", + "ical", + "ness", + "ful", + ) + __step4_suffixes = ( + "ement", + "ance", + "ence", + "able", + "ible", + "ment", + "ant", + "ent", + "ism", + "ate", + "iti", + "ous", + "ive", + "ize", + "ion", + "al", + "er", + "ic", + ) + __step5_suffixes = ("e", "l") + __special_words = { + "skis": "ski", + "skies": "sky", + "dying": "die", + "lying": "lie", + "tying": "tie", + "idly": "idl", + "gently": "gentl", + "ugly": "ugli", + "early": "earli", + "only": "onli", + "singly": "singl", + "sky": "sky", + "news": "news", + "howe": "howe", + "atlas": "atlas", + "cosmos": "cosmos", + "bias": "bias", + "andes": "andes", + "inning": "inning", + "innings": "inning", + "outing": "outing", + "outings": "outing", + "canning": "canning", + "cannings": "canning", + "herring": "herring", + "herrings": "herring", + "earring": "earring", + "earrings": "earring", + "proceed": "proceed", + "proceeds": "proceed", + "proceeded": "proceed", + "proceeding": "proceed", + "exceed": "exceed", + "exceeds": "exceed", + "exceeded": "exceed", + "exceeding": "exceed", + "succeed": "succeed", + "succeeds": "succeed", + "succeeded": "succeed", + "succeeding": "succeed", + } + + def stem(self, word): + + """ + Stem an English word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords or len(word) <= 2: + return word + + elif word in self.__special_words: + return self.__special_words[word] + + # Map the different apostrophe characters to a single consistent one + word = ( + word.replace("\u2019", "\x27") + .replace("\u2018", "\x27") + .replace("\u201B", "\x27") + ) + + if word.startswith("\x27"): + word = word[1:] + + if word.startswith("y"): + word = "".join(("Y", word[1:])) + + for i in range(1, len(word)): + if word[i - 1] in self.__vowels and word[i] == "y": + word = "".join((word[:i], "Y", word[i + 1 :])) + + step1a_vowel_found = False + step1b_vowel_found = False + + r1 = "" + r2 = "" + + if word.startswith(("gener", "commun", "arsen")): + if word.startswith(("gener", "arsen")): + r1 = word[5:] + else: + r1 = word[6:] + + for i in range(1, len(r1)): + if r1[i] not in self.__vowels and r1[i - 1] in self.__vowels: + r2 = r1[i + 1 :] + break + else: + r1, r2 = self._r1r2_standard(word, self.__vowels) + + # STEP 0 + for suffix in self.__step0_suffixes: + if word.endswith(suffix): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 1a + for suffix in self.__step1a_suffixes: + if word.endswith(suffix): + + if suffix == "sses": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix in ("ied", "ies"): + if len(word[: -len(suffix)]) > 1: + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + else: + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + elif suffix == "s": + for letter in word[:-2]: + if letter in self.__vowels: + step1a_vowel_found = True + break + + if step1a_vowel_found: + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + break + + # STEP 1b + for suffix in self.__step1b_suffixes: + if word.endswith(suffix): + if suffix in ("eed", "eedly"): + + if r1.endswith(suffix): + word = suffix_replace(word, suffix, "ee") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ee") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ee") + else: + r2 = "" + else: + for letter in word[: -len(suffix)]: + if letter in self.__vowels: + step1b_vowel_found = True + break + + if step1b_vowel_found: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + + if word.endswith(("at", "bl", "iz")): + word = "".join((word, "e")) + r1 = "".join((r1, "e")) + + if len(word) > 5 or len(r1) >= 3: + r2 = "".join((r2, "e")) + + elif word.endswith(self.__double_consonants): + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + elif ( + r1 == "" + and len(word) >= 3 + and word[-1] not in self.__vowels + and word[-1] not in "wxY" + and word[-2] in self.__vowels + and word[-3] not in self.__vowels + ) or ( + r1 == "" + and len(word) == 2 + and word[0] in self.__vowels + and word[1] not in self.__vowels + ): + + word = "".join((word, "e")) + + if len(r1) > 0: + r1 = "".join((r1, "e")) + + if len(r2) > 0: + r2 = "".join((r2, "e")) + break + + # STEP 1c + if len(word) > 2 and word[-1] in "yY" and word[-2] not in self.__vowels: + word = "".join((word[:-1], "i")) + if len(r1) >= 1: + r1 = "".join((r1[:-1], "i")) + else: + r1 = "" + + if len(r2) >= 1: + r2 = "".join((r2[:-1], "i")) + else: + r2 = "" + + # STEP 2 + for suffix in self.__step2_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + if suffix == "tional": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix in ("enci", "anci", "abli"): + word = "".join((word[:-1], "e")) + + if len(r1) >= 1: + r1 = "".join((r1[:-1], "e")) + else: + r1 = "" + + if len(r2) >= 1: + r2 = "".join((r2[:-1], "e")) + else: + r2 = "" + + elif suffix == "entli": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix in ("izer", "ization"): + word = suffix_replace(word, suffix, "ize") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ize") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ize") + else: + r2 = "" + + elif suffix in ("ational", "ation", "ator"): + word = suffix_replace(word, suffix, "ate") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ate") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ate") + else: + r2 = "e" + + elif suffix in ("alism", "aliti", "alli"): + word = suffix_replace(word, suffix, "al") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "al") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "al") + else: + r2 = "" + + elif suffix == "fulness": + word = word[:-4] + r1 = r1[:-4] + r2 = r2[:-4] + + elif suffix in ("ousli", "ousness"): + word = suffix_replace(word, suffix, "ous") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ous") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ous") + else: + r2 = "" + + elif suffix in ("iveness", "iviti"): + word = suffix_replace(word, suffix, "ive") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ive") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ive") + else: + r2 = "e" + + elif suffix in ("biliti", "bli"): + word = suffix_replace(word, suffix, "ble") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ble") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ble") + else: + r2 = "" + + elif suffix == "ogi" and word[-4] == "l": + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + + elif suffix in ("fulli", "lessli"): + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix == "li" and word[-3] in self.__li_ending: + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + break + + # STEP 3 + for suffix in self.__step3_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + if suffix == "tional": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix == "ational": + word = suffix_replace(word, suffix, "ate") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ate") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ate") + else: + r2 = "" + + elif suffix == "alize": + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + + elif suffix in ("icate", "iciti", "ical"): + word = suffix_replace(word, suffix, "ic") + + if len(r1) >= len(suffix): + r1 = suffix_replace(r1, suffix, "ic") + else: + r1 = "" + + if len(r2) >= len(suffix): + r2 = suffix_replace(r2, suffix, "ic") + else: + r2 = "" + + elif suffix in ("ful", "ness"): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + + elif suffix == "ative" and r2.endswith(suffix): + word = word[:-5] + r1 = r1[:-5] + r2 = r2[:-5] + break + + # STEP 4 + for suffix in self.__step4_suffixes: + if word.endswith(suffix): + if r2.endswith(suffix): + if suffix == "ion": + if word[-4] in "st": + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 5 + if r2.endswith("l") and word[-2] == "l": + word = word[:-1] + elif r2.endswith("e"): + word = word[:-1] + elif r1.endswith("e"): + if len(word) >= 4 and ( + word[-2] in self.__vowels + or word[-2] in "wxY" + or word[-3] not in self.__vowels + or word[-4] in self.__vowels + ): + word = word[:-1] + + word = word.replace("Y", "y") + + return word + + +class FinnishStemmer(_StandardStemmer): + + """ + The Finnish Snowball stemmer. + + :cvar __vowels: The Finnish vowels. + :type __vowels: unicode + :cvar __restricted_vowels: A subset of the Finnish vowels. + :type __restricted_vowels: unicode + :cvar __long_vowels: The Finnish vowels in their long forms. + :type __long_vowels: tuple + :cvar __consonants: The Finnish consonants. + :type __consonants: unicode + :cvar __double_consonants: The Finnish double consonants. + :type __double_consonants: tuple + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :cvar __step4_suffixes: Suffixes to be deleted in step 4 of the algorithm. + :type __step4_suffixes: tuple + :note: A detailed description of the Finnish + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/finnish/stemmer.html + """ + + __vowels = "aeiouy\xE4\xF6" + __restricted_vowels = "aeiou\xE4\xF6" + __long_vowels = ("aa", "ee", "ii", "oo", "uu", "\xE4\xE4", "\xF6\xF6") + __consonants = "bcdfghjklmnpqrstvwxz" + __double_consonants = ( + "bb", + "cc", + "dd", + "ff", + "gg", + "hh", + "jj", + "kk", + "ll", + "mm", + "nn", + "pp", + "qq", + "rr", + "ss", + "tt", + "vv", + "ww", + "xx", + "zz", + ) + __step1_suffixes = ( + "kaan", + "k\xE4\xE4n", + "sti", + "kin", + "han", + "h\xE4n", + "ko", + "k\xF6", + "pa", + "p\xE4", + ) + __step2_suffixes = ("nsa", "ns\xE4", "mme", "nne", "si", "ni", "an", "\xE4n", "en") + __step3_suffixes = ( + "siin", + "tten", + "seen", + "han", + "hen", + "hin", + "hon", + "h\xE4n", + "h\xF6n", + "den", + "tta", + "tt\xE4", + "ssa", + "ss\xE4", + "sta", + "st\xE4", + "lla", + "ll\xE4", + "lta", + "lt\xE4", + "lle", + "ksi", + "ine", + "ta", + "t\xE4", + "na", + "n\xE4", + "a", + "\xE4", + "n", + ) + __step4_suffixes = ( + "impi", + "impa", + "imp\xE4", + "immi", + "imma", + "imm\xE4", + "mpi", + "mpa", + "mp\xE4", + "mmi", + "mma", + "mm\xE4", + "eja", + "ej\xE4", + ) + + def stem(self, word): + """ + Stem a Finnish word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step3_success = False + + r1, r2 = self._r1r2_standard(word, self.__vowels) + + # STEP 1: Particles etc. + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if suffix == "sti": + if suffix in r2: + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + else: + if word[-len(suffix) - 1] in "ntaeiouy\xE4\xF6": + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 2: Possessives + for suffix in self.__step2_suffixes: + if r1.endswith(suffix): + if suffix == "si": + if word[-3] != "k": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix == "ni": + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + if word.endswith("kse"): + word = suffix_replace(word, "kse", "ksi") + + if r1.endswith("kse"): + r1 = suffix_replace(r1, "kse", "ksi") + + if r2.endswith("kse"): + r2 = suffix_replace(r2, "kse", "ksi") + + elif suffix == "an": + if word[-4:-2] in ("ta", "na") or word[-5:-2] in ( + "ssa", + "sta", + "lla", + "lta", + ): + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix == "\xE4n": + if word[-4:-2] in ("t\xE4", "n\xE4") or word[-5:-2] in ( + "ss\xE4", + "st\xE4", + "ll\xE4", + "lt\xE4", + ): + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + + elif suffix == "en": + if word[-5:-2] in ("lle", "ine"): + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + else: + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + break + + # STEP 3: Cases + for suffix in self.__step3_suffixes: + if r1.endswith(suffix): + if suffix in ("han", "hen", "hin", "hon", "h\xE4n", "h\xF6n"): + if ( + (suffix == "han" and word[-4] == "a") + or (suffix == "hen" and word[-4] == "e") + or (suffix == "hin" and word[-4] == "i") + or (suffix == "hon" and word[-4] == "o") + or (suffix == "h\xE4n" and word[-4] == "\xE4") + or (suffix == "h\xF6n" and word[-4] == "\xF6") + ): + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + step3_success = True + + elif suffix in ("siin", "den", "tten"): + if ( + word[-len(suffix) - 1] == "i" + and word[-len(suffix) - 2] in self.__restricted_vowels + ): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + step3_success = True + else: + continue + + elif suffix == "seen": + if word[-6:-4] in self.__long_vowels: + word = word[:-4] + r1 = r1[:-4] + r2 = r2[:-4] + step3_success = True + else: + continue + + elif suffix in ("a", "\xE4"): + if word[-2] in self.__vowels and word[-3] in self.__consonants: + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + step3_success = True + + elif suffix in ("tta", "tt\xE4"): + if word[-4] == "e": + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + step3_success = True + + elif suffix == "n": + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + step3_success = True + + if word[-2:] == "ie" or word[-2:] in self.__long_vowels: + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + step3_success = True + break + + # STEP 4: Other endings + for suffix in self.__step4_suffixes: + if r2.endswith(suffix): + if suffix in ("mpi", "mpa", "mp\xE4", "mmi", "mma", "mm\xE4"): + if word[-5:-3] != "po": + word = word[:-3] + r1 = r1[:-3] + r2 = r2[:-3] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 5: Plurals + if step3_success and len(r1) >= 1 and r1[-1] in "ij": + word = word[:-1] + r1 = r1[:-1] + + elif ( + not step3_success + and len(r1) >= 2 + and r1[-1] == "t" + and r1[-2] in self.__vowels + ): + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + if r2.endswith("imma"): + word = word[:-4] + r1 = r1[:-4] + elif r2.endswith("mma") and r2[-5:-3] != "po": + word = word[:-3] + r1 = r1[:-3] + + # STEP 6: Tidying up + if r1[-2:] in self.__long_vowels: + word = word[:-1] + r1 = r1[:-1] + + if len(r1) >= 2 and r1[-2] in self.__consonants and r1[-1] in "a\xE4ei": + word = word[:-1] + r1 = r1[:-1] + + if r1.endswith(("oj", "uj")): + word = word[:-1] + r1 = r1[:-1] + + if r1.endswith("jo"): + word = word[:-1] + r1 = r1[:-1] + + # If the word ends with a double consonant + # followed by zero or more vowels, the last consonant is removed. + for i in range(1, len(word)): + if word[-i] in self.__vowels: + continue + else: + if i == 1: + if word[-i - 1 :] in self.__double_consonants: + word = word[:-1] + else: + if word[-i - 1 : -i + 1] in self.__double_consonants: + word = "".join((word[:-i], word[-i + 1 :])) + break + + return word + + +class FrenchStemmer(_StandardStemmer): + + """ + The French Snowball stemmer. + + :cvar __vowels: The French vowels. + :type __vowels: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2a_suffixes: Suffixes to be deleted in step 2a of the algorithm. + :type __step2a_suffixes: tuple + :cvar __step2b_suffixes: Suffixes to be deleted in step 2b of the algorithm. + :type __step2b_suffixes: tuple + :cvar __step4_suffixes: Suffixes to be deleted in step 4 of the algorithm. + :type __step4_suffixes: tuple + :note: A detailed description of the French + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/french/stemmer.html + """ + + __vowels = "aeiouy\xE2\xE0\xEB\xE9\xEA\xE8\xEF\xEE\xF4\xFB\xF9" + __step1_suffixes = ( + "issements", + "issement", + "atrices", + "atrice", + "ateurs", + "ations", + "logies", + "usions", + "utions", + "ements", + "amment", + "emment", + "ances", + "iqUes", + "ismes", + "ables", + "istes", + "ateur", + "ation", + "logie", + "usion", + "ution", + "ences", + "ement", + "euses", + "ments", + "ance", + "iqUe", + "isme", + "able", + "iste", + "ence", + "it\xE9s", + "ives", + "eaux", + "euse", + "ment", + "eux", + "it\xE9", + "ive", + "ifs", + "aux", + "if", + ) + __step2a_suffixes = ( + "issaIent", + "issantes", + "iraIent", + "issante", + "issants", + "issions", + "irions", + "issais", + "issait", + "issant", + "issent", + "issiez", + "issons", + "irais", + "irait", + "irent", + "iriez", + "irons", + "iront", + "isses", + "issez", + "\xEEmes", + "\xEEtes", + "irai", + "iras", + "irez", + "isse", + "ies", + "ira", + "\xEEt", + "ie", + "ir", + "is", + "it", + "i", + ) + __step2b_suffixes = ( + "eraIent", + "assions", + "erions", + "assent", + "assiez", + "\xE8rent", + "erais", + "erait", + "eriez", + "erons", + "eront", + "aIent", + "antes", + "asses", + "ions", + "erai", + "eras", + "erez", + "\xE2mes", + "\xE2tes", + "ante", + "ants", + "asse", + "\xE9es", + "era", + "iez", + "ais", + "ait", + "ant", + "\xE9e", + "\xE9s", + "er", + "ez", + "\xE2t", + "ai", + "as", + "\xE9", + "a", + ) + __step4_suffixes = ("i\xE8re", "I\xE8re", "ion", "ier", "Ier", "e", "\xEB") + + def stem(self, word): + """ + Stem a French word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step1_success = False + rv_ending_found = False + step2a_success = False + step2b_success = False + + # Every occurrence of 'u' after 'q' is put into upper case. + for i in range(1, len(word)): + if word[i - 1] == "q" and word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + # Every occurrence of 'u' and 'i' + # between vowels is put into upper case. + # Every occurrence of 'y' preceded or + # followed by a vowel is also put into upper case. + for i in range(1, len(word) - 1): + if word[i - 1] in self.__vowels and word[i + 1] in self.__vowels: + if word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + elif word[i] == "i": + word = "".join((word[:i], "I", word[i + 1 :])) + + if word[i - 1] in self.__vowels or word[i + 1] in self.__vowels: + if word[i] == "y": + word = "".join((word[:i], "Y", word[i + 1 :])) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + rv = self.__rv_french(word, self.__vowels) + + # STEP 1: Standard suffix removal + for suffix in self.__step1_suffixes: + if word.endswith(suffix): + if suffix == "eaux": + word = word[:-1] + step1_success = True + + elif suffix in ("euse", "euses"): + if suffix in r2: + word = word[: -len(suffix)] + step1_success = True + + elif suffix in r1: + word = suffix_replace(word, suffix, "eux") + step1_success = True + + elif suffix in ("ement", "ements") and suffix in rv: + word = word[: -len(suffix)] + step1_success = True + + if word[-2:] == "iv" and "iv" in r2: + word = word[:-2] + + if word[-2:] == "at" and "at" in r2: + word = word[:-2] + + elif word[-3:] == "eus": + if "eus" in r2: + word = word[:-3] + elif "eus" in r1: + word = "".join((word[:-1], "x")) + + elif word[-3:] in ("abl", "iqU"): + if "abl" in r2 or "iqU" in r2: + word = word[:-3] + + elif word[-3:] in ("i\xE8r", "I\xE8r"): + if "i\xE8r" in rv or "I\xE8r" in rv: + word = "".join((word[:-3], "i")) + + elif suffix == "amment" and suffix in rv: + word = suffix_replace(word, "amment", "ant") + rv = suffix_replace(rv, "amment", "ant") + rv_ending_found = True + + elif suffix == "emment" and suffix in rv: + word = suffix_replace(word, "emment", "ent") + rv_ending_found = True + + elif ( + suffix in ("ment", "ments") + and suffix in rv + and not rv.startswith(suffix) + and rv[rv.rindex(suffix) - 1] in self.__vowels + ): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + rv_ending_found = True + + elif suffix == "aux" and suffix in r1: + word = "".join((word[:-2], "l")) + step1_success = True + + elif ( + suffix in ("issement", "issements") + and suffix in r1 + and word[-len(suffix) - 1] not in self.__vowels + ): + word = word[: -len(suffix)] + step1_success = True + + elif ( + suffix + in ( + "ance", + "iqUe", + "isme", + "able", + "iste", + "eux", + "ances", + "iqUes", + "ismes", + "ables", + "istes", + ) + and suffix in r2 + ): + word = word[: -len(suffix)] + step1_success = True + + elif ( + suffix + in ("atrice", "ateur", "ation", "atrices", "ateurs", "ations") + and suffix in r2 + ): + word = word[: -len(suffix)] + step1_success = True + + if word[-2:] == "ic": + if "ic" in r2: + word = word[:-2] + else: + word = "".join((word[:-2], "iqU")) + + elif suffix in ("logie", "logies") and suffix in r2: + word = suffix_replace(word, suffix, "log") + step1_success = True + + elif suffix in ("usion", "ution", "usions", "utions") and suffix in r2: + word = suffix_replace(word, suffix, "u") + step1_success = True + + elif suffix in ("ence", "ences") and suffix in r2: + word = suffix_replace(word, suffix, "ent") + step1_success = True + + elif suffix in ("it\xE9", "it\xE9s") and suffix in r2: + word = word[: -len(suffix)] + step1_success = True + + if word[-4:] == "abil": + if "abil" in r2: + word = word[:-4] + else: + word = "".join((word[:-2], "l")) + + elif word[-2:] == "ic": + if "ic" in r2: + word = word[:-2] + else: + word = "".join((word[:-2], "iqU")) + + elif word[-2:] == "iv": + if "iv" in r2: + word = word[:-2] + + elif suffix in ("if", "ive", "ifs", "ives") and suffix in r2: + word = word[: -len(suffix)] + step1_success = True + + if word[-2:] == "at" and "at" in r2: + word = word[:-2] + + if word[-2:] == "ic": + if "ic" in r2: + word = word[:-2] + else: + word = "".join((word[:-2], "iqU")) + break + + # STEP 2a: Verb suffixes beginning 'i' + if not step1_success or rv_ending_found: + for suffix in self.__step2a_suffixes: + if word.endswith(suffix): + if ( + suffix in rv + and len(rv) > len(suffix) + and rv[rv.rindex(suffix) - 1] not in self.__vowels + ): + word = word[: -len(suffix)] + step2a_success = True + break + + # STEP 2b: Other verb suffixes + if not step2a_success: + for suffix in self.__step2b_suffixes: + if rv.endswith(suffix): + if suffix == "ions" and "ions" in r2: + word = word[:-4] + step2b_success = True + + elif suffix in ( + "eraIent", + "erions", + "\xE8rent", + "erais", + "erait", + "eriez", + "erons", + "eront", + "erai", + "eras", + "erez", + "\xE9es", + "era", + "iez", + "\xE9e", + "\xE9s", + "er", + "ez", + "\xE9", + ): + word = word[: -len(suffix)] + step2b_success = True + + elif suffix in ( + "assions", + "assent", + "assiez", + "aIent", + "antes", + "asses", + "\xE2mes", + "\xE2tes", + "ante", + "ants", + "asse", + "ais", + "ait", + "ant", + "\xE2t", + "ai", + "as", + "a", + ): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + step2b_success = True + if rv.endswith("e"): + word = word[:-1] + break + + # STEP 3 + if step1_success or step2a_success or step2b_success: + if word[-1] == "Y": + word = "".join((word[:-1], "i")) + elif word[-1] == "\xE7": + word = "".join((word[:-1], "c")) + + # STEP 4: Residual suffixes + else: + if len(word) >= 2 and word[-1] == "s" and word[-2] not in "aiou\xE8s": + word = word[:-1] + + for suffix in self.__step4_suffixes: + if word.endswith(suffix): + if suffix in rv: + if suffix == "ion" and suffix in r2 and rv[-4] in "st": + word = word[:-3] + + elif suffix in ("ier", "i\xE8re", "Ier", "I\xE8re"): + word = suffix_replace(word, suffix, "i") + + elif suffix == "e": + word = word[:-1] + + elif suffix == "\xEB" and word[-3:-1] == "gu": + word = word[:-1] + break + + # STEP 5: Undouble + if word.endswith(("enn", "onn", "ett", "ell", "eill")): + word = word[:-1] + + # STEP 6: Un-accent + for i in range(1, len(word)): + if word[-i] not in self.__vowels: + i += 1 + else: + if i != 1 and word[-i] in ("\xE9", "\xE8"): + word = "".join((word[:-i], "e", word[-i + 1 :])) + break + + word = word.replace("I", "i").replace("U", "u").replace("Y", "y") + + return word + + def __rv_french(self, word, vowels): + """ + Return the region RV that is used by the French stemmer. + + If the word begins with two vowels, RV is the region after + the third letter. Otherwise, it is the region after the first + vowel not at the beginning of the word, or the end of the word + if these positions cannot be found. (Exceptionally, u'par', + u'col' or u'tap' at the beginning of a word is also taken to + define RV as the region to their right.) + + :param word: The French word whose region RV is determined. + :type word: str or unicode + :param vowels: The French vowels that are used to determine + the region RV. + :type vowels: unicode + :return: the region RV for the respective French word. + :rtype: unicode + :note: This helper method is invoked by the stem method of + the subclass FrenchStemmer. It is not to be invoked directly! + + """ + rv = "" + if len(word) >= 2: + if word.startswith(("par", "col", "tap")) or ( + word[0] in vowels and word[1] in vowels + ): + rv = word[3:] + else: + for i in range(1, len(word)): + if word[i] in vowels: + rv = word[i + 1 :] + break + + return rv + + +class GermanStemmer(_StandardStemmer): + + """ + The German Snowball stemmer. + + :cvar __vowels: The German vowels. + :type __vowels: unicode + :cvar __s_ending: Letters that may directly appear before a word final 's'. + :type __s_ending: unicode + :cvar __st_ending: Letter that may directly appear before a word final 'st'. + :type __st_ending: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the German + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/german/stemmer.html + + """ + + __vowels = "aeiouy\xE4\xF6\xFC" + __s_ending = "bdfghklmnrt" + __st_ending = "bdfghklmnt" + + __step1_suffixes = ("ern", "em", "er", "en", "es", "e", "s") + __step2_suffixes = ("est", "en", "er", "st") + __step3_suffixes = ("isch", "lich", "heit", "keit", "end", "ung", "ig", "ik") + + def stem(self, word): + """ + Stem a German word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + word = word.replace("\xDF", "ss") + + # Every occurrence of 'u' and 'y' + # between vowels is put into upper case. + for i in range(1, len(word) - 1): + if word[i - 1] in self.__vowels and word[i + 1] in self.__vowels: + if word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + elif word[i] == "y": + word = "".join((word[:i], "Y", word[i + 1 :])) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + + # R1 is adjusted so that the region before it + # contains at least 3 letters. + for i in range(1, len(word)): + if word[i] not in self.__vowels and word[i - 1] in self.__vowels: + if 3 > len(word[: i + 1]) > 0: + r1 = word[3:] + elif len(word[: i + 1]) == 0: + return word + break + + # STEP 1 + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if ( + suffix in ("en", "es", "e") + and word[-len(suffix) - 4 : -len(suffix)] == "niss" + ): + word = word[: -len(suffix) - 1] + r1 = r1[: -len(suffix) - 1] + r2 = r2[: -len(suffix) - 1] + + elif suffix == "s": + if word[-2] in self.__s_ending: + word = word[:-1] + r1 = r1[:-1] + r2 = r2[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 2 + for suffix in self.__step2_suffixes: + if r1.endswith(suffix): + if suffix == "st": + if word[-3] in self.__st_ending and len(word[:-3]) >= 3: + word = word[:-2] + r1 = r1[:-2] + r2 = r2[:-2] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + break + + # STEP 3: Derivational suffixes + for suffix in self.__step3_suffixes: + if r2.endswith(suffix): + if suffix in ("end", "ung"): + if ( + "ig" in r2[-len(suffix) - 2 : -len(suffix)] + and "e" not in r2[-len(suffix) - 3 : -len(suffix) - 2] + ): + word = word[: -len(suffix) - 2] + else: + word = word[: -len(suffix)] + + elif ( + suffix in ("ig", "ik", "isch") + and "e" not in r2[-len(suffix) - 1 : -len(suffix)] + ): + word = word[: -len(suffix)] + + elif suffix in ("lich", "heit"): + if ( + "er" in r1[-len(suffix) - 2 : -len(suffix)] + or "en" in r1[-len(suffix) - 2 : -len(suffix)] + ): + word = word[: -len(suffix) - 2] + else: + word = word[: -len(suffix)] + + elif suffix == "keit": + if "lich" in r2[-len(suffix) - 4 : -len(suffix)]: + word = word[: -len(suffix) - 4] + + elif "ig" in r2[-len(suffix) - 2 : -len(suffix)]: + word = word[: -len(suffix) - 2] + else: + word = word[: -len(suffix)] + break + + # Umlaut accents are removed and + # 'u' and 'y' are put back into lower case. + word = ( + word.replace("\xE4", "a") + .replace("\xF6", "o") + .replace("\xFC", "u") + .replace("U", "u") + .replace("Y", "y") + ) + + return word + + +class HungarianStemmer(_LanguageSpecificStemmer): + + """ + The Hungarian Snowball stemmer. + + :cvar __vowels: The Hungarian vowels. + :type __vowels: unicode + :cvar __digraphs: The Hungarian digraphs. + :type __digraphs: tuple + :cvar __double_consonants: The Hungarian double consonants. + :type __double_consonants: tuple + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :cvar __step4_suffixes: Suffixes to be deleted in step 4 of the algorithm. + :type __step4_suffixes: tuple + :cvar __step5_suffixes: Suffixes to be deleted in step 5 of the algorithm. + :type __step5_suffixes: tuple + :cvar __step6_suffixes: Suffixes to be deleted in step 6 of the algorithm. + :type __step6_suffixes: tuple + :cvar __step7_suffixes: Suffixes to be deleted in step 7 of the algorithm. + :type __step7_suffixes: tuple + :cvar __step8_suffixes: Suffixes to be deleted in step 8 of the algorithm. + :type __step8_suffixes: tuple + :cvar __step9_suffixes: Suffixes to be deleted in step 9 of the algorithm. + :type __step9_suffixes: tuple + :note: A detailed description of the Hungarian + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/hungarian/stemmer.html + + """ + + __vowels = "aeiou\xF6\xFC\xE1\xE9\xED\xF3\xF5\xFA\xFB" + __digraphs = ("cs", "dz", "dzs", "gy", "ly", "ny", "ty", "zs") + __double_consonants = ( + "bb", + "cc", + "ccs", + "dd", + "ff", + "gg", + "ggy", + "jj", + "kk", + "ll", + "lly", + "mm", + "nn", + "nny", + "pp", + "rr", + "ss", + "ssz", + "tt", + "tty", + "vv", + "zz", + "zzs", + ) + + __step1_suffixes = ("al", "el") + __step2_suffixes = ( + "k\xE9ppen", + "onk\xE9nt", + "enk\xE9nt", + "ank\xE9nt", + "k\xE9pp", + "k\xE9nt", + "ban", + "ben", + "nak", + "nek", + "val", + "vel", + "t\xF3l", + "t\xF5l", + "r\xF3l", + "r\xF5l", + "b\xF3l", + "b\xF5l", + "hoz", + "hez", + "h\xF6z", + "n\xE1l", + "n\xE9l", + "\xE9rt", + "kor", + "ba", + "be", + "ra", + "re", + "ig", + "at", + "et", + "ot", + "\xF6t", + "ul", + "\xFCl", + "v\xE1", + "v\xE9", + "en", + "on", + "an", + "\xF6n", + "n", + "t", + ) + __step3_suffixes = ("\xE1nk\xE9nt", "\xE1n", "\xE9n") + __step4_suffixes = ( + "astul", + "est\xFCl", + "\xE1stul", + "\xE9st\xFCl", + "stul", + "st\xFCl", + ) + __step5_suffixes = ("\xE1", "\xE9") + __step6_suffixes = ( + "ok\xE9", + "\xF6k\xE9", + "ak\xE9", + "ek\xE9", + "\xE1k\xE9", + "\xE1\xE9i", + "\xE9k\xE9", + "\xE9\xE9i", + "k\xE9", + "\xE9i", + "\xE9\xE9", + "\xE9", + ) + __step7_suffixes = ( + "\xE1juk", + "\xE9j\xFCk", + "\xFCnk", + "unk", + "juk", + "j\xFCk", + "\xE1nk", + "\xE9nk", + "nk", + "uk", + "\xFCk", + "em", + "om", + "am", + "od", + "ed", + "ad", + "\xF6d", + "ja", + "je", + "\xE1m", + "\xE1d", + "\xE9m", + "\xE9d", + "m", + "d", + "a", + "e", + "o", + "\xE1", + "\xE9", + ) + __step8_suffixes = ( + "jaitok", + "jeitek", + "jaink", + "jeink", + "aitok", + "eitek", + "\xE1itok", + "\xE9itek", + "jaim", + "jeim", + "jaid", + "jeid", + "eink", + "aink", + "itek", + "jeik", + "jaik", + "\xE1ink", + "\xE9ink", + "aim", + "eim", + "aid", + "eid", + "jai", + "jei", + "ink", + "aik", + "eik", + "\xE1im", + "\xE1id", + "\xE1ik", + "\xE9im", + "\xE9id", + "\xE9ik", + "im", + "id", + "ai", + "ei", + "ik", + "\xE1i", + "\xE9i", + "i", + ) + __step9_suffixes = ("\xE1k", "\xE9k", "\xF6k", "ok", "ek", "ak", "k") + + def stem(self, word): + """ + Stem an Hungarian word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + r1 = self.__r1_hungarian(word, self.__vowels, self.__digraphs) + + # STEP 1: Remove instrumental case + if r1.endswith(self.__step1_suffixes): + for double_cons in self.__double_consonants: + if word[-2 - len(double_cons) : -2] == double_cons: + word = "".join((word[:-4], word[-3])) + + if r1[-2 - len(double_cons) : -2] == double_cons: + r1 = "".join((r1[:-4], r1[-3])) + break + + # STEP 2: Remove frequent cases + for suffix in self.__step2_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + + if r1.endswith("\xE1"): + word = "".join((word[:-1], "a")) + r1 = suffix_replace(r1, "\xE1", "a") + + elif r1.endswith("\xE9"): + word = "".join((word[:-1], "e")) + r1 = suffix_replace(r1, "\xE9", "e") + break + + # STEP 3: Remove special cases + for suffix in self.__step3_suffixes: + if r1.endswith(suffix): + if suffix == "\xE9n": + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + else: + word = suffix_replace(word, suffix, "a") + r1 = suffix_replace(r1, suffix, "a") + break + + # STEP 4: Remove other cases + for suffix in self.__step4_suffixes: + if r1.endswith(suffix): + if suffix == "\xE1stul": + word = suffix_replace(word, suffix, "a") + r1 = suffix_replace(r1, suffix, "a") + + elif suffix == "\xE9st\xFCl": + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 5: Remove factive case + for suffix in self.__step5_suffixes: + if r1.endswith(suffix): + for double_cons in self.__double_consonants: + if word[-1 - len(double_cons) : -1] == double_cons: + word = "".join((word[:-3], word[-2])) + + if r1[-1 - len(double_cons) : -1] == double_cons: + r1 = "".join((r1[:-3], r1[-2])) + break + + # STEP 6: Remove owned + for suffix in self.__step6_suffixes: + if r1.endswith(suffix): + if suffix in ("\xE1k\xE9", "\xE1\xE9i"): + word = suffix_replace(word, suffix, "a") + r1 = suffix_replace(r1, suffix, "a") + + elif suffix in ("\xE9k\xE9", "\xE9\xE9i", "\xE9\xE9"): + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 7: Remove singular owner suffixes + for suffix in self.__step7_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + if suffix in ("\xE1nk", "\xE1juk", "\xE1m", "\xE1d", "\xE1"): + word = suffix_replace(word, suffix, "a") + r1 = suffix_replace(r1, suffix, "a") + + elif suffix in ("\xE9nk", "\xE9j\xFCk", "\xE9m", "\xE9d", "\xE9"): + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 8: Remove plural owner suffixes + for suffix in self.__step8_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + if suffix in ( + "\xE1im", + "\xE1id", + "\xE1i", + "\xE1ink", + "\xE1itok", + "\xE1ik", + ): + word = suffix_replace(word, suffix, "a") + r1 = suffix_replace(r1, suffix, "a") + + elif suffix in ( + "\xE9im", + "\xE9id", + "\xE9i", + "\xE9ink", + "\xE9itek", + "\xE9ik", + ): + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 9: Remove plural suffixes + for suffix in self.__step9_suffixes: + if word.endswith(suffix): + if r1.endswith(suffix): + if suffix == "\xE1k": + word = suffix_replace(word, suffix, "a") + elif suffix == "\xE9k": + word = suffix_replace(word, suffix, "e") + else: + word = word[: -len(suffix)] + break + + return word + + def __r1_hungarian(self, word, vowels, digraphs): + """ + Return the region R1 that is used by the Hungarian stemmer. + + If the word begins with a vowel, R1 is defined as the region + after the first consonant or digraph (= two letters stand for + one phoneme) in the word. If the word begins with a consonant, + it is defined as the region after the first vowel in the word. + If the word does not contain both a vowel and consonant, R1 + is the null region at the end of the word. + + :param word: The Hungarian word whose region R1 is determined. + :type word: str or unicode + :param vowels: The Hungarian vowels that are used to determine + the region R1. + :type vowels: unicode + :param digraphs: The digraphs that are used to determine the + region R1. + :type digraphs: tuple + :return: the region R1 for the respective word. + :rtype: unicode + :note: This helper method is invoked by the stem method of the subclass + HungarianStemmer. It is not to be invoked directly! + + """ + r1 = "" + if word[0] in vowels: + for digraph in digraphs: + if digraph in word[1:]: + r1 = word[word.index(digraph[-1]) + 1 :] + return r1 + + for i in range(1, len(word)): + if word[i] not in vowels: + r1 = word[i + 1 :] + break + else: + for i in range(1, len(word)): + if word[i] in vowels: + r1 = word[i + 1 :] + break + + return r1 + + +class ItalianStemmer(_StandardStemmer): + + """ + The Italian Snowball stemmer. + + :cvar __vowels: The Italian vowels. + :type __vowels: unicode + :cvar __step0_suffixes: Suffixes to be deleted in step 0 of the algorithm. + :type __step0_suffixes: tuple + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :note: A detailed description of the Italian + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/italian/stemmer.html + + """ + + __vowels = "aeiou\xE0\xE8\xEC\xF2\xF9" + __step0_suffixes = ( + "gliela", + "gliele", + "glieli", + "glielo", + "gliene", + "sene", + "mela", + "mele", + "meli", + "melo", + "mene", + "tela", + "tele", + "teli", + "telo", + "tene", + "cela", + "cele", + "celi", + "celo", + "cene", + "vela", + "vele", + "veli", + "velo", + "vene", + "gli", + "ci", + "la", + "le", + "li", + "lo", + "mi", + "ne", + "si", + "ti", + "vi", + ) + __step1_suffixes = ( + "atrice", + "atrici", + "azione", + "azioni", + "uzione", + "uzioni", + "usione", + "usioni", + "amento", + "amenti", + "imento", + "imenti", + "amente", + "abile", + "abili", + "ibile", + "ibili", + "mente", + "atore", + "atori", + "logia", + "logie", + "anza", + "anze", + "iche", + "ichi", + "ismo", + "ismi", + "ista", + "iste", + "isti", + "ist\xE0", + "ist\xE8", + "ist\xEC", + "ante", + "anti", + "enza", + "enze", + "ico", + "ici", + "ica", + "ice", + "oso", + "osi", + "osa", + "ose", + "it\xE0", + "ivo", + "ivi", + "iva", + "ive", + ) + __step2_suffixes = ( + "erebbero", + "irebbero", + "assero", + "assimo", + "eranno", + "erebbe", + "eremmo", + "ereste", + "eresti", + "essero", + "iranno", + "irebbe", + "iremmo", + "ireste", + "iresti", + "iscano", + "iscono", + "issero", + "arono", + "avamo", + "avano", + "avate", + "eremo", + "erete", + "erono", + "evamo", + "evano", + "evate", + "iremo", + "irete", + "irono", + "ivamo", + "ivano", + "ivate", + "ammo", + "ando", + "asse", + "assi", + "emmo", + "enda", + "ende", + "endi", + "endo", + "erai", + "erei", + "Yamo", + "iamo", + "immo", + "irai", + "irei", + "isca", + "isce", + "isci", + "isco", + "ano", + "are", + "ata", + "ate", + "ati", + "ato", + "ava", + "avi", + "avo", + "er\xE0", + "ere", + "er\xF2", + "ete", + "eva", + "evi", + "evo", + "ir\xE0", + "ire", + "ir\xF2", + "ita", + "ite", + "iti", + "ito", + "iva", + "ivi", + "ivo", + "ono", + "uta", + "ute", + "uti", + "uto", + "ar", + "ir", + ) + + def stem(self, word): + """ + Stem an Italian word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step1_success = False + + # All acute accents are replaced by grave accents. + word = ( + word.replace("\xE1", "\xE0") + .replace("\xE9", "\xE8") + .replace("\xED", "\xEC") + .replace("\xF3", "\xF2") + .replace("\xFA", "\xF9") + ) + + # Every occurrence of 'u' after 'q' + # is put into upper case. + for i in range(1, len(word)): + if word[i - 1] == "q" and word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + # Every occurrence of 'u' and 'i' + # between vowels is put into upper case. + for i in range(1, len(word) - 1): + if word[i - 1] in self.__vowels and word[i + 1] in self.__vowels: + if word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + elif word[i] == "i": + word = "".join((word[:i], "I", word[i + 1 :])) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + rv = self._rv_standard(word, self.__vowels) + + # STEP 0: Attached pronoun + for suffix in self.__step0_suffixes: + if rv.endswith(suffix): + if rv[-len(suffix) - 4 : -len(suffix)] in ("ando", "endo"): + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + elif rv[-len(suffix) - 2 : -len(suffix)] in ("ar", "er", "ir"): + word = suffix_replace(word, suffix, "e") + r1 = suffix_replace(r1, suffix, "e") + r2 = suffix_replace(r2, suffix, "e") + rv = suffix_replace(rv, suffix, "e") + break + + # STEP 1: Standard suffix removal + for suffix in self.__step1_suffixes: + if word.endswith(suffix): + if suffix == "amente" and r1.endswith(suffix): + step1_success = True + word = word[:-6] + r2 = r2[:-6] + rv = rv[:-6] + + if r2.endswith("iv"): + word = word[:-2] + r2 = r2[:-2] + rv = rv[:-2] + + if r2.endswith("at"): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith(("os", "ic")): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith("abil"): + word = word[:-4] + rv = rv[:-4] + + elif suffix in ("amento", "amenti", "imento", "imenti") and rv.endswith( + suffix + ): + step1_success = True + word = word[:-6] + rv = rv[:-6] + + elif r2.endswith(suffix): + step1_success = True + if suffix in ("azione", "azioni", "atore", "atori"): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + if r2.endswith("ic"): + word = word[:-2] + rv = rv[:-2] + + elif suffix in ("logia", "logie"): + word = word[:-2] + rv = word[:-2] + + elif suffix in ("uzione", "uzioni", "usione", "usioni"): + word = word[:-5] + rv = rv[:-5] + + elif suffix in ("enza", "enze"): + word = suffix_replace(word, suffix, "te") + rv = suffix_replace(rv, suffix, "te") + + elif suffix == "it\xE0": + word = word[:-3] + r2 = r2[:-3] + rv = rv[:-3] + + if r2.endswith(("ic", "iv")): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith("abil"): + word = word[:-4] + rv = rv[:-4] + + elif suffix in ("ivo", "ivi", "iva", "ive"): + word = word[:-3] + r2 = r2[:-3] + rv = rv[:-3] + + if r2.endswith("at"): + word = word[:-2] + r2 = r2[:-2] + rv = rv[:-2] + + if r2.endswith("ic"): + word = word[:-2] + rv = rv[:-2] + else: + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 2: Verb suffixes + if not step1_success: + for suffix in self.__step2_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 3a + if rv.endswith(("a", "e", "i", "o", "\xE0", "\xE8", "\xEC", "\xF2")): + word = word[:-1] + rv = rv[:-1] + + if rv.endswith("i"): + word = word[:-1] + rv = rv[:-1] + + # STEP 3b + if rv.endswith(("ch", "gh")): + word = word[:-1] + + word = word.replace("I", "i").replace("U", "u") + + return word + + +class NorwegianStemmer(_ScandinavianStemmer): + + """ + The Norwegian Snowball stemmer. + + :cvar __vowels: The Norwegian vowels. + :type __vowels: unicode + :cvar __s_ending: Letters that may directly appear before a word final 's'. + :type __s_ending: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the Norwegian + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/norwegian/stemmer.html + + """ + + __vowels = "aeiouy\xE6\xE5\xF8" + __s_ending = "bcdfghjlmnoprtvyz" + __step1_suffixes = ( + "hetenes", + "hetene", + "hetens", + "heter", + "heten", + "endes", + "ande", + "ende", + "edes", + "enes", + "erte", + "ede", + "ane", + "ene", + "ens", + "ers", + "ets", + "het", + "ast", + "ert", + "en", + "ar", + "er", + "as", + "es", + "et", + "a", + "e", + "s", + ) + + __step2_suffixes = ("dt", "vt") + + __step3_suffixes = ( + "hetslov", + "eleg", + "elig", + "elov", + "slov", + "leg", + "eig", + "lig", + "els", + "lov", + "ig", + ) + + def stem(self, word): + """ + Stem a Norwegian word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + r1 = self._r1_scandinavian(word, self.__vowels) + + # STEP 1 + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if suffix in ("erte", "ert"): + word = suffix_replace(word, suffix, "er") + r1 = suffix_replace(r1, suffix, "er") + + elif suffix == "s": + if word[-2] in self.__s_ending or ( + word[-2] == "k" and word[-3] not in self.__vowels + ): + word = word[:-1] + r1 = r1[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 2 + for suffix in self.__step2_suffixes: + if r1.endswith(suffix): + word = word[:-1] + r1 = r1[:-1] + break + + # STEP 3 + for suffix in self.__step3_suffixes: + if r1.endswith(suffix): + word = word[: -len(suffix)] + break + + return word + + +class PortugueseStemmer(_StandardStemmer): + + """ + The Portuguese Snowball stemmer. + + :cvar __vowels: The Portuguese vowels. + :type __vowels: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step4_suffixes: Suffixes to be deleted in step 4 of the algorithm. + :type __step4_suffixes: tuple + :note: A detailed description of the Portuguese + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/portuguese/stemmer.html + + """ + + __vowels = "aeiou\xE1\xE9\xED\xF3\xFA\xE2\xEA\xF4" + __step1_suffixes = ( + "amentos", + "imentos", + "uço~es", + "amento", + "imento", + "adoras", + "adores", + "a\xE7o~es", + "logias", + "\xEAncias", + "amente", + "idades", + "an\xE7as", + "ismos", + "istas", + "adora", + "a\xE7a~o", + "antes", + "\xE2ncia", + "logia", + "uça~o", + "\xEAncia", + "mente", + "idade", + "an\xE7a", + "ezas", + "icos", + "icas", + "ismo", + "\xE1vel", + "\xEDvel", + "ista", + "osos", + "osas", + "ador", + "ante", + "ivas", + "ivos", + "iras", + "eza", + "ico", + "ica", + "oso", + "osa", + "iva", + "ivo", + "ira", + ) + __step2_suffixes = ( + "ar\xEDamos", + "er\xEDamos", + "ir\xEDamos", + "\xE1ssemos", + "\xEAssemos", + "\xEDssemos", + "ar\xEDeis", + "er\xEDeis", + "ir\xEDeis", + "\xE1sseis", + "\xE9sseis", + "\xEDsseis", + "\xE1ramos", + "\xE9ramos", + "\xEDramos", + "\xE1vamos", + "aremos", + "eremos", + "iremos", + "ariam", + "eriam", + "iriam", + "assem", + "essem", + "issem", + "ara~o", + "era~o", + "ira~o", + "arias", + "erias", + "irias", + "ardes", + "erdes", + "irdes", + "asses", + "esses", + "isses", + "astes", + "estes", + "istes", + "\xE1reis", + "areis", + "\xE9reis", + "ereis", + "\xEDreis", + "ireis", + "\xE1veis", + "\xEDamos", + "armos", + "ermos", + "irmos", + "aria", + "eria", + "iria", + "asse", + "esse", + "isse", + "aste", + "este", + "iste", + "arei", + "erei", + "irei", + "aram", + "eram", + "iram", + "avam", + "arem", + "erem", + "irem", + "ando", + "endo", + "indo", + "adas", + "idas", + "ar\xE1s", + "aras", + "er\xE1s", + "eras", + "ir\xE1s", + "avas", + "ares", + "eres", + "ires", + "\xEDeis", + "ados", + "idos", + "\xE1mos", + "amos", + "emos", + "imos", + "iras", + "ada", + "ida", + "ar\xE1", + "ara", + "er\xE1", + "era", + "ir\xE1", + "ava", + "iam", + "ado", + "ido", + "ias", + "ais", + "eis", + "ira", + "ia", + "ei", + "am", + "em", + "ar", + "er", + "ir", + "as", + "es", + "is", + "eu", + "iu", + "ou", + ) + __step4_suffixes = ("os", "a", "i", "o", "\xE1", "\xED", "\xF3") + + def stem(self, word): + """ + Stem a Portuguese word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step1_success = False + step2_success = False + + word = ( + word.replace("\xE3", "a~") + .replace("\xF5", "o~") + .replace("q\xFC", "qu") + .replace("g\xFC", "gu") + ) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + rv = self._rv_standard(word, self.__vowels) + + # STEP 1: Standard suffix removal + for suffix in self.__step1_suffixes: + if word.endswith(suffix): + if suffix == "amente" and r1.endswith(suffix): + step1_success = True + + word = word[:-6] + r2 = r2[:-6] + rv = rv[:-6] + + if r2.endswith("iv"): + word = word[:-2] + r2 = r2[:-2] + rv = rv[:-2] + + if r2.endswith("at"): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith(("os", "ic", "ad")): + word = word[:-2] + rv = rv[:-2] + + elif ( + suffix in ("ira", "iras") + and rv.endswith(suffix) + and word[-len(suffix) - 1 : -len(suffix)] == "e" + ): + step1_success = True + + word = suffix_replace(word, suffix, "ir") + rv = suffix_replace(rv, suffix, "ir") + + elif r2.endswith(suffix): + step1_success = True + + if suffix in ("logia", "logias"): + word = suffix_replace(word, suffix, "log") + rv = suffix_replace(rv, suffix, "log") + + elif suffix in ("uça~o", "uço~es"): + word = suffix_replace(word, suffix, "u") + rv = suffix_replace(rv, suffix, "u") + + elif suffix in ("\xEAncia", "\xEAncias"): + word = suffix_replace(word, suffix, "ente") + rv = suffix_replace(rv, suffix, "ente") + + elif suffix == "mente": + word = word[:-5] + r2 = r2[:-5] + rv = rv[:-5] + + if r2.endswith(("ante", "avel", "ivel")): + word = word[:-4] + rv = rv[:-4] + + elif suffix in ("idade", "idades"): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + if r2.endswith(("ic", "iv")): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith("abil"): + word = word[:-4] + rv = rv[:-4] + + elif suffix in ("iva", "ivo", "ivas", "ivos"): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + if r2.endswith("at"): + word = word[:-2] + rv = rv[:-2] + else: + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 2: Verb suffixes + if not step1_success: + for suffix in self.__step2_suffixes: + if rv.endswith(suffix): + step2_success = True + + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 3 + if step1_success or step2_success: + if rv.endswith("i") and word[-2] == "c": + word = word[:-1] + rv = rv[:-1] + + ### STEP 4: Residual suffix + if not step1_success and not step2_success: + for suffix in self.__step4_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 5 + if rv.endswith(("e", "\xE9", "\xEA")): + word = word[:-1] + rv = rv[:-1] + + if (word.endswith("gu") and rv.endswith("u")) or ( + word.endswith("ci") and rv.endswith("i") + ): + word = word[:-1] + + elif word.endswith("\xE7"): + word = suffix_replace(word, "\xE7", "c") + + word = word.replace("a~", "\xE3").replace("o~", "\xF5") + + return word + + +class RomanianStemmer(_StandardStemmer): + + """ + The Romanian Snowball stemmer. + + :cvar __vowels: The Romanian vowels. + :type __vowels: unicode + :cvar __step0_suffixes: Suffixes to be deleted in step 0 of the algorithm. + :type __step0_suffixes: tuple + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the Romanian + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/romanian/stemmer.html + + """ + + __vowels = "aeiou\u0103\xE2\xEE" + __step0_suffixes = ( + "iilor", + "ului", + "elor", + "iile", + "ilor", + "atei", + "a\u0163ie", + "a\u0163ia", + "aua", + "ele", + "iua", + "iei", + "ile", + "ul", + "ea", + "ii", + ) + __step1_suffixes = ( + "abilitate", + "abilitati", + "abilit\u0103\u0163i", + "ibilitate", + "abilit\u0103i", + "ivitate", + "ivitati", + "ivit\u0103\u0163i", + "icitate", + "icitati", + "icit\u0103\u0163i", + "icatori", + "ivit\u0103i", + "icit\u0103i", + "icator", + "a\u0163iune", + "atoare", + "\u0103toare", + "i\u0163iune", + "itoare", + "iciva", + "icive", + "icivi", + "iciv\u0103", + "icala", + "icale", + "icali", + "ical\u0103", + "ativa", + "ative", + "ativi", + "ativ\u0103", + "atori", + "\u0103tori", + "itiva", + "itive", + "itivi", + "itiv\u0103", + "itori", + "iciv", + "ical", + "ativ", + "ator", + "\u0103tor", + "itiv", + "itor", + ) + __step2_suffixes = ( + "abila", + "abile", + "abili", + "abil\u0103", + "ibila", + "ibile", + "ibili", + "ibil\u0103", + "atori", + "itate", + "itati", + "it\u0103\u0163i", + "abil", + "ibil", + "oasa", + "oas\u0103", + "oase", + "anta", + "ante", + "anti", + "ant\u0103", + "ator", + "it\u0103i", + "iune", + "iuni", + "isme", + "ista", + "iste", + "isti", + "ist\u0103", + "i\u015Fti", + "ata", + "at\u0103", + "ati", + "ate", + "uta", + "ut\u0103", + "uti", + "ute", + "ita", + "it\u0103", + "iti", + "ite", + "ica", + "ice", + "ici", + "ic\u0103", + "osi", + "o\u015Fi", + "ant", + "iva", + "ive", + "ivi", + "iv\u0103", + "ism", + "ist", + "at", + "ut", + "it", + "ic", + "os", + "iv", + ) + __step3_suffixes = ( + "seser\u0103\u0163i", + "aser\u0103\u0163i", + "iser\u0103\u0163i", + "\xE2ser\u0103\u0163i", + "user\u0103\u0163i", + "seser\u0103m", + "aser\u0103m", + "iser\u0103m", + "\xE2ser\u0103m", + "user\u0103m", + "ser\u0103\u0163i", + "sese\u015Fi", + "seser\u0103", + "easc\u0103", + "ar\u0103\u0163i", + "ur\u0103\u0163i", + "ir\u0103\u0163i", + "\xE2r\u0103\u0163i", + "ase\u015Fi", + "aser\u0103", + "ise\u015Fi", + "iser\u0103", + "\xe2se\u015Fi", + "\xE2ser\u0103", + "use\u015Fi", + "user\u0103", + "ser\u0103m", + "sesem", + "indu", + "\xE2ndu", + "eaz\u0103", + "e\u015Fti", + "e\u015Fte", + "\u0103\u015Fti", + "\u0103\u015Fte", + "ea\u0163i", + "ia\u0163i", + "ar\u0103m", + "ur\u0103m", + "ir\u0103m", + "\xE2r\u0103m", + "asem", + "isem", + "\xE2sem", + "usem", + "se\u015Fi", + "ser\u0103", + "sese", + "are", + "ere", + "ire", + "\xE2re", + "ind", + "\xE2nd", + "eze", + "ezi", + "esc", + "\u0103sc", + "eam", + "eai", + "eau", + "iam", + "iai", + "iau", + "a\u015Fi", + "ar\u0103", + "u\u015Fi", + "ur\u0103", + "i\u015Fi", + "ir\u0103", + "\xE2\u015Fi", + "\xe2r\u0103", + "ase", + "ise", + "\xE2se", + "use", + "a\u0163i", + "e\u0163i", + "i\u0163i", + "\xe2\u0163i", + "sei", + "ez", + "am", + "ai", + "au", + "ea", + "ia", + "ui", + "\xE2i", + "\u0103m", + "em", + "im", + "\xE2m", + "se", + ) + + def stem(self, word): + """ + Stem a Romanian word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step1_success = False + step2_success = False + + for i in range(1, len(word) - 1): + if word[i - 1] in self.__vowels and word[i + 1] in self.__vowels: + if word[i] == "u": + word = "".join((word[:i], "U", word[i + 1 :])) + + elif word[i] == "i": + word = "".join((word[:i], "I", word[i + 1 :])) + + r1, r2 = self._r1r2_standard(word, self.__vowels) + rv = self._rv_standard(word, self.__vowels) + + # STEP 0: Removal of plurals and other simplifications + for suffix in self.__step0_suffixes: + if word.endswith(suffix): + if suffix in r1: + if suffix in ("ul", "ului"): + word = word[: -len(suffix)] + + if suffix in rv: + rv = rv[: -len(suffix)] + else: + rv = "" + + elif ( + suffix == "aua" + or suffix == "atei" + or (suffix == "ile" and word[-5:-3] != "ab") + ): + word = word[:-2] + + elif suffix in ("ea", "ele", "elor"): + word = suffix_replace(word, suffix, "e") + + if suffix in rv: + rv = suffix_replace(rv, suffix, "e") + else: + rv = "" + + elif suffix in ("ii", "iua", "iei", "iile", "iilor", "ilor"): + word = suffix_replace(word, suffix, "i") + + if suffix in rv: + rv = suffix_replace(rv, suffix, "i") + else: + rv = "" + + elif suffix in ("a\u0163ie", "a\u0163ia"): + word = word[:-1] + break + + # STEP 1: Reduction of combining suffixes + while True: + + replacement_done = False + + for suffix in self.__step1_suffixes: + if word.endswith(suffix): + if suffix in r1: + step1_success = True + replacement_done = True + + if suffix in ( + "abilitate", + "abilitati", + "abilit\u0103i", + "abilit\u0103\u0163i", + ): + word = suffix_replace(word, suffix, "abil") + + elif suffix == "ibilitate": + word = word[:-5] + + elif suffix in ( + "ivitate", + "ivitati", + "ivit\u0103i", + "ivit\u0103\u0163i", + ): + word = suffix_replace(word, suffix, "iv") + + elif suffix in ( + "icitate", + "icitati", + "icit\u0103i", + "icit\u0103\u0163i", + "icator", + "icatori", + "iciv", + "iciva", + "icive", + "icivi", + "iciv\u0103", + "ical", + "icala", + "icale", + "icali", + "ical\u0103", + ): + word = suffix_replace(word, suffix, "ic") + + elif suffix in ( + "ativ", + "ativa", + "ative", + "ativi", + "ativ\u0103", + "a\u0163iune", + "atoare", + "ator", + "atori", + "\u0103toare", + "\u0103tor", + "\u0103tori", + ): + word = suffix_replace(word, suffix, "at") + + if suffix in r2: + r2 = suffix_replace(r2, suffix, "at") + + elif suffix in ( + "itiv", + "itiva", + "itive", + "itivi", + "itiv\u0103", + "i\u0163iune", + "itoare", + "itor", + "itori", + ): + word = suffix_replace(word, suffix, "it") + + if suffix in r2: + r2 = suffix_replace(r2, suffix, "it") + else: + step1_success = False + break + + if not replacement_done: + break + + # STEP 2: Removal of standard suffixes + for suffix in self.__step2_suffixes: + if word.endswith(suffix): + if suffix in r2: + step2_success = True + + if suffix in ("iune", "iuni"): + if word[-5] == "\u0163": + word = "".join((word[:-5], "t")) + + elif suffix in ( + "ism", + "isme", + "ist", + "ista", + "iste", + "isti", + "ist\u0103", + "i\u015Fti", + ): + word = suffix_replace(word, suffix, "ist") + + else: + word = word[: -len(suffix)] + break + + # STEP 3: Removal of verb suffixes + if not step1_success and not step2_success: + for suffix in self.__step3_suffixes: + if word.endswith(suffix): + if suffix in rv: + if suffix in ( + "seser\u0103\u0163i", + "seser\u0103m", + "ser\u0103\u0163i", + "sese\u015Fi", + "seser\u0103", + "ser\u0103m", + "sesem", + "se\u015Fi", + "ser\u0103", + "sese", + "a\u0163i", + "e\u0163i", + "i\u0163i", + "\xE2\u0163i", + "sei", + "\u0103m", + "em", + "im", + "\xE2m", + "se", + ): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + else: + if ( + not rv.startswith(suffix) + and rv[rv.index(suffix) - 1] not in "aeio\u0103\xE2\xEE" + ): + word = word[: -len(suffix)] + break + + # STEP 4: Removal of final vowel + for suffix in ("ie", "a", "e", "i", "\u0103"): + if word.endswith(suffix): + if suffix in rv: + word = word[: -len(suffix)] + break + + word = word.replace("I", "i").replace("U", "u") + + return word + + +class RussianStemmer(_LanguageSpecificStemmer): + + """ + The Russian Snowball stemmer. + + :cvar __perfective_gerund_suffixes: Suffixes to be deleted. + :type __perfective_gerund_suffixes: tuple + :cvar __adjectival_suffixes: Suffixes to be deleted. + :type __adjectival_suffixes: tuple + :cvar __reflexive_suffixes: Suffixes to be deleted. + :type __reflexive_suffixes: tuple + :cvar __verb_suffixes: Suffixes to be deleted. + :type __verb_suffixes: tuple + :cvar __noun_suffixes: Suffixes to be deleted. + :type __noun_suffixes: tuple + :cvar __superlative_suffixes: Suffixes to be deleted. + :type __superlative_suffixes: tuple + :cvar __derivational_suffixes: Suffixes to be deleted. + :type __derivational_suffixes: tuple + :note: A detailed description of the Russian + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/russian/stemmer.html + + """ + + __perfective_gerund_suffixes = ( + "ivshis'", + "yvshis'", + "vshis'", + "ivshi", + "yvshi", + "vshi", + "iv", + "yv", + "v", + ) + __adjectival_suffixes = ( + "ui^ushchi^ui^u", + "ui^ushchi^ai^a", + "ui^ushchimi", + "ui^ushchymi", + "ui^ushchego", + "ui^ushchogo", + "ui^ushchemu", + "ui^ushchomu", + "ui^ushchikh", + "ui^ushchykh", + "ui^ushchui^u", + "ui^ushchaia", + "ui^ushchoi^u", + "ui^ushchei^u", + "i^ushchi^ui^u", + "i^ushchi^ai^a", + "ui^ushchee", + "ui^ushchie", + "ui^ushchye", + "ui^ushchoe", + "ui^ushchei`", + "ui^ushchii`", + "ui^ushchyi`", + "ui^ushchoi`", + "ui^ushchem", + "ui^ushchim", + "ui^ushchym", + "ui^ushchom", + "i^ushchimi", + "i^ushchymi", + "i^ushchego", + "i^ushchogo", + "i^ushchemu", + "i^ushchomu", + "i^ushchikh", + "i^ushchykh", + "i^ushchui^u", + "i^ushchai^a", + "i^ushchoi^u", + "i^ushchei^u", + "i^ushchee", + "i^ushchie", + "i^ushchye", + "i^ushchoe", + "i^ushchei`", + "i^ushchii`", + "i^ushchyi`", + "i^ushchoi`", + "i^ushchem", + "i^ushchim", + "i^ushchym", + "i^ushchom", + "shchi^ui^u", + "shchi^ai^a", + "ivshi^ui^u", + "ivshi^ai^a", + "yvshi^ui^u", + "yvshi^ai^a", + "shchimi", + "shchymi", + "shchego", + "shchogo", + "shchemu", + "shchomu", + "shchikh", + "shchykh", + "shchui^u", + "shchai^a", + "shchoi^u", + "shchei^u", + "ivshimi", + "ivshymi", + "ivshego", + "ivshogo", + "ivshemu", + "ivshomu", + "ivshikh", + "ivshykh", + "ivshui^u", + "ivshai^a", + "ivshoi^u", + "ivshei^u", + "yvshimi", + "yvshymi", + "yvshego", + "yvshogo", + "yvshemu", + "yvshomu", + "yvshikh", + "yvshykh", + "yvshui^u", + "yvshai^a", + "yvshoi^u", + "yvshei^u", + "vshi^ui^u", + "vshi^ai^a", + "shchee", + "shchie", + "shchye", + "shchoe", + "shchei`", + "shchii`", + "shchyi`", + "shchoi`", + "shchem", + "shchim", + "shchym", + "shchom", + "ivshee", + "ivshie", + "ivshye", + "ivshoe", + "ivshei`", + "ivshii`", + "ivshyi`", + "ivshoi`", + "ivshem", + "ivshim", + "ivshym", + "ivshom", + "yvshee", + "yvshie", + "yvshye", + "yvshoe", + "yvshei`", + "yvshii`", + "yvshyi`", + "yvshoi`", + "yvshem", + "yvshim", + "yvshym", + "yvshom", + "vshimi", + "vshymi", + "vshego", + "vshogo", + "vshemu", + "vshomu", + "vshikh", + "vshykh", + "vshui^u", + "vshai^a", + "vshoi^u", + "vshei^u", + "emi^ui^u", + "emi^ai^a", + "nni^ui^u", + "nni^ai^a", + "vshee", + "vshie", + "vshye", + "vshoe", + "vshei`", + "vshii`", + "vshyi`", + "vshoi`", + "vshem", + "vshim", + "vshym", + "vshom", + "emimi", + "emymi", + "emego", + "emogo", + "ememu", + "emomu", + "emikh", + "emykh", + "emui^u", + "emai^a", + "emoi^u", + "emei^u", + "nnimi", + "nnymi", + "nnego", + "nnogo", + "nnemu", + "nnomu", + "nnikh", + "nnykh", + "nnui^u", + "nnai^a", + "nnoi^u", + "nnei^u", + "emee", + "emie", + "emye", + "emoe", + "emei`", + "emii`", + "emyi`", + "emoi`", + "emem", + "emim", + "emym", + "emom", + "nnee", + "nnie", + "nnye", + "nnoe", + "nnei`", + "nnii`", + "nnyi`", + "nnoi`", + "nnem", + "nnim", + "nnym", + "nnom", + "i^ui^u", + "i^ai^a", + "imi", + "ymi", + "ego", + "ogo", + "emu", + "omu", + "ikh", + "ykh", + "ui^u", + "ai^a", + "oi^u", + "ei^u", + "ee", + "ie", + "ye", + "oe", + "ei`", + "ii`", + "yi`", + "oi`", + "em", + "im", + "ym", + "om", + ) + __reflexive_suffixes = ("si^a", "s'") + __verb_suffixes = ( + "esh'", + "ei`te", + "ui`te", + "ui^ut", + "ish'", + "ete", + "i`te", + "i^ut", + "nno", + "ila", + "yla", + "ena", + "ite", + "ili", + "yli", + "ilo", + "ylo", + "eno", + "i^at", + "uet", + "eny", + "it'", + "yt'", + "ui^u", + "la", + "na", + "li", + "em", + "lo", + "no", + "et", + "ny", + "t'", + "ei`", + "ui`", + "il", + "yl", + "im", + "ym", + "en", + "it", + "yt", + "i^u", + "i`", + "l", + "n", + ) + __noun_suffixes = ( + "ii^ami", + "ii^akh", + "i^ami", + "ii^am", + "i^akh", + "ami", + "iei`", + "i^am", + "iem", + "akh", + "ii^u", + "'i^u", + "ii^a", + "'i^a", + "ev", + "ov", + "ie", + "'e", + "ei", + "ii", + "ei`", + "oi`", + "ii`", + "em", + "am", + "om", + "i^u", + "i^a", + "a", + "e", + "i", + "i`", + "o", + "u", + "y", + "'", + ) + __superlative_suffixes = ("ei`she", "ei`sh") + __derivational_suffixes = ("ost'", "ost") + + def stem(self, word): + """ + Stem a Russian word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + if word in self.stopwords: + return word + + chr_exceeded = False + for i in range(len(word)): + if ord(word[i]) > 255: + chr_exceeded = True + break + + if not chr_exceeded: + return word + + word = self.__cyrillic_to_roman(word) + + step1_success = False + adjectival_removed = False + verb_removed = False + undouble_success = False + superlative_removed = False + + rv, r2 = self.__regions_russian(word) + + # Step 1 + for suffix in self.__perfective_gerund_suffixes: + if rv.endswith(suffix): + if suffix in ("v", "vshi", "vshis'"): + if ( + rv[-len(suffix) - 3 : -len(suffix)] == "i^a" + or rv[-len(suffix) - 1 : -len(suffix)] == "a" + ): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + step1_success = True + break + else: + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + step1_success = True + break + + if not step1_success: + for suffix in self.__reflexive_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + for suffix in self.__adjectival_suffixes: + if rv.endswith(suffix): + if suffix in ( + "i^ushchi^ui^u", + "i^ushchi^ai^a", + "i^ushchui^u", + "i^ushchai^a", + "i^ushchoi^u", + "i^ushchei^u", + "i^ushchimi", + "i^ushchymi", + "i^ushchego", + "i^ushchogo", + "i^ushchemu", + "i^ushchomu", + "i^ushchikh", + "i^ushchykh", + "shchi^ui^u", + "shchi^ai^a", + "i^ushchee", + "i^ushchie", + "i^ushchye", + "i^ushchoe", + "i^ushchei`", + "i^ushchii`", + "i^ushchyi`", + "i^ushchoi`", + "i^ushchem", + "i^ushchim", + "i^ushchym", + "i^ushchom", + "vshi^ui^u", + "vshi^ai^a", + "shchui^u", + "shchai^a", + "shchoi^u", + "shchei^u", + "emi^ui^u", + "emi^ai^a", + "nni^ui^u", + "nni^ai^a", + "shchimi", + "shchymi", + "shchego", + "shchogo", + "shchemu", + "shchomu", + "shchikh", + "shchykh", + "vshui^u", + "vshai^a", + "vshoi^u", + "vshei^u", + "shchee", + "shchie", + "shchye", + "shchoe", + "shchei`", + "shchii`", + "shchyi`", + "shchoi`", + "shchem", + "shchim", + "shchym", + "shchom", + "vshimi", + "vshymi", + "vshego", + "vshogo", + "vshemu", + "vshomu", + "vshikh", + "vshykh", + "emui^u", + "emai^a", + "emoi^u", + "emei^u", + "nnui^u", + "nnai^a", + "nnoi^u", + "nnei^u", + "vshee", + "vshie", + "vshye", + "vshoe", + "vshei`", + "vshii`", + "vshyi`", + "vshoi`", + "vshem", + "vshim", + "vshym", + "vshom", + "emimi", + "emymi", + "emego", + "emogo", + "ememu", + "emomu", + "emikh", + "emykh", + "nnimi", + "nnymi", + "nnego", + "nnogo", + "nnemu", + "nnomu", + "nnikh", + "nnykh", + "emee", + "emie", + "emye", + "emoe", + "emei`", + "emii`", + "emyi`", + "emoi`", + "emem", + "emim", + "emym", + "emom", + "nnee", + "nnie", + "nnye", + "nnoe", + "nnei`", + "nnii`", + "nnyi`", + "nnoi`", + "nnem", + "nnim", + "nnym", + "nnom", + ): + if ( + rv[-len(suffix) - 3 : -len(suffix)] == "i^a" + or rv[-len(suffix) - 1 : -len(suffix)] == "a" + ): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + adjectival_removed = True + break + else: + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + adjectival_removed = True + break + + if not adjectival_removed: + for suffix in self.__verb_suffixes: + if rv.endswith(suffix): + if suffix in ( + "la", + "na", + "ete", + "i`te", + "li", + "i`", + "l", + "em", + "n", + "lo", + "no", + "et", + "i^ut", + "ny", + "t'", + "esh'", + "nno", + ): + if ( + rv[-len(suffix) - 3 : -len(suffix)] == "i^a" + or rv[-len(suffix) - 1 : -len(suffix)] == "a" + ): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + verb_removed = True + break + else: + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + verb_removed = True + break + + if not adjectival_removed and not verb_removed: + for suffix in self.__noun_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # Step 2 + if rv.endswith("i"): + word = word[:-1] + r2 = r2[:-1] + + # Step 3 + for suffix in self.__derivational_suffixes: + if r2.endswith(suffix): + word = word[: -len(suffix)] + break + + # Step 4 + if word.endswith("nn"): + word = word[:-1] + undouble_success = True + + if not undouble_success: + for suffix in self.__superlative_suffixes: + if word.endswith(suffix): + word = word[: -len(suffix)] + superlative_removed = True + break + if word.endswith("nn"): + word = word[:-1] + + if not undouble_success and not superlative_removed: + if word.endswith("'"): + word = word[:-1] + + word = self.__roman_to_cyrillic(word) + + return word + + def __regions_russian(self, word): + """ + Return the regions RV and R2 which are used by the Russian stemmer. + + In any word, RV is the region after the first vowel, + or the end of the word if it contains no vowel. + + R2 is the region after the first non-vowel following + a vowel in R1, or the end of the word if there is no such non-vowel. + + R1 is the region after the first non-vowel following a vowel, + or the end of the word if there is no such non-vowel. + + :param word: The Russian word whose regions RV and R2 are determined. + :type word: str or unicode + :return: the regions RV and R2 for the respective Russian word. + :rtype: tuple + :note: This helper method is invoked by the stem method of the subclass + RussianStemmer. It is not to be invoked directly! + + """ + r1 = "" + r2 = "" + rv = "" + + vowels = ("A", "U", "E", "a", "e", "i", "o", "u", "y") + word = word.replace("i^a", "A").replace("i^u", "U").replace("e`", "E") + + for i in range(1, len(word)): + if word[i] not in vowels and word[i - 1] in vowels: + r1 = word[i + 1 :] + break + + for i in range(1, len(r1)): + if r1[i] not in vowels and r1[i - 1] in vowels: + r2 = r1[i + 1 :] + break + + for i in range(len(word)): + if word[i] in vowels: + rv = word[i + 1 :] + break + + r2 = r2.replace("A", "i^a").replace("U", "i^u").replace("E", "e`") + rv = rv.replace("A", "i^a").replace("U", "i^u").replace("E", "e`") + + return (rv, r2) + + def __cyrillic_to_roman(self, word): + """ + Transliterate a Russian word into the Roman alphabet. + + A Russian word whose letters consist of the Cyrillic + alphabet are transliterated into the Roman alphabet + in order to ease the forthcoming stemming process. + + :param word: The word that is transliterated. + :type word: unicode + :return: the transliterated word. + :rtype: unicode + :note: This helper method is invoked by the stem method of the subclass + RussianStemmer. It is not to be invoked directly! + + """ + word = ( + word.replace("\u0410", "a") + .replace("\u0430", "a") + .replace("\u0411", "b") + .replace("\u0431", "b") + .replace("\u0412", "v") + .replace("\u0432", "v") + .replace("\u0413", "g") + .replace("\u0433", "g") + .replace("\u0414", "d") + .replace("\u0434", "d") + .replace("\u0415", "e") + .replace("\u0435", "e") + .replace("\u0401", "e") + .replace("\u0451", "e") + .replace("\u0416", "zh") + .replace("\u0436", "zh") + .replace("\u0417", "z") + .replace("\u0437", "z") + .replace("\u0418", "i") + .replace("\u0438", "i") + .replace("\u0419", "i`") + .replace("\u0439", "i`") + .replace("\u041A", "k") + .replace("\u043A", "k") + .replace("\u041B", "l") + .replace("\u043B", "l") + .replace("\u041C", "m") + .replace("\u043C", "m") + .replace("\u041D", "n") + .replace("\u043D", "n") + .replace("\u041E", "o") + .replace("\u043E", "o") + .replace("\u041F", "p") + .replace("\u043F", "p") + .replace("\u0420", "r") + .replace("\u0440", "r") + .replace("\u0421", "s") + .replace("\u0441", "s") + .replace("\u0422", "t") + .replace("\u0442", "t") + .replace("\u0423", "u") + .replace("\u0443", "u") + .replace("\u0424", "f") + .replace("\u0444", "f") + .replace("\u0425", "kh") + .replace("\u0445", "kh") + .replace("\u0426", "t^s") + .replace("\u0446", "t^s") + .replace("\u0427", "ch") + .replace("\u0447", "ch") + .replace("\u0428", "sh") + .replace("\u0448", "sh") + .replace("\u0429", "shch") + .replace("\u0449", "shch") + .replace("\u042A", "''") + .replace("\u044A", "''") + .replace("\u042B", "y") + .replace("\u044B", "y") + .replace("\u042C", "'") + .replace("\u044C", "'") + .replace("\u042D", "e`") + .replace("\u044D", "e`") + .replace("\u042E", "i^u") + .replace("\u044E", "i^u") + .replace("\u042F", "i^a") + .replace("\u044F", "i^a") + ) + + return word + + def __roman_to_cyrillic(self, word): + """ + Transliterate a Russian word back into the Cyrillic alphabet. + + A Russian word formerly transliterated into the Roman alphabet + in order to ease the stemming process, is transliterated back + into the Cyrillic alphabet, its original form. + + :param word: The word that is transliterated. + :type word: str or unicode + :return: word, the transliterated word. + :rtype: unicode + :note: This helper method is invoked by the stem method of the subclass + RussianStemmer. It is not to be invoked directly! + + """ + word = ( + word.replace("i^u", "\u044E") + .replace("i^a", "\u044F") + .replace("shch", "\u0449") + .replace("kh", "\u0445") + .replace("t^s", "\u0446") + .replace("ch", "\u0447") + .replace("e`", "\u044D") + .replace("i`", "\u0439") + .replace("sh", "\u0448") + .replace("k", "\u043A") + .replace("e", "\u0435") + .replace("zh", "\u0436") + .replace("a", "\u0430") + .replace("b", "\u0431") + .replace("v", "\u0432") + .replace("g", "\u0433") + .replace("d", "\u0434") + .replace("e", "\u0435") + .replace("z", "\u0437") + .replace("i", "\u0438") + .replace("l", "\u043B") + .replace("m", "\u043C") + .replace("n", "\u043D") + .replace("o", "\u043E") + .replace("p", "\u043F") + .replace("r", "\u0440") + .replace("s", "\u0441") + .replace("t", "\u0442") + .replace("u", "\u0443") + .replace("f", "\u0444") + .replace("''", "\u044A") + .replace("y", "\u044B") + .replace("'", "\u044C") + ) + + return word + + +class SpanishStemmer(_StandardStemmer): + + """ + The Spanish Snowball stemmer. + + :cvar __vowels: The Spanish vowels. + :type __vowels: unicode + :cvar __step0_suffixes: Suffixes to be deleted in step 0 of the algorithm. + :type __step0_suffixes: tuple + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2a_suffixes: Suffixes to be deleted in step 2a of the algorithm. + :type __step2a_suffixes: tuple + :cvar __step2b_suffixes: Suffixes to be deleted in step 2b of the algorithm. + :type __step2b_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the Spanish + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/spanish/stemmer.html + + """ + + __vowels = "aeiou\xE1\xE9\xED\xF3\xFA\xFC" + __step0_suffixes = ( + "selas", + "selos", + "sela", + "selo", + "las", + "les", + "los", + "nos", + "me", + "se", + "la", + "le", + "lo", + ) + __step1_suffixes = ( + "amientos", + "imientos", + "amiento", + "imiento", + "acion", + "aciones", + "uciones", + "adoras", + "adores", + "ancias", + "log\xEDas", + "encias", + "amente", + "idades", + "anzas", + "ismos", + "ables", + "ibles", + "istas", + "adora", + "aci\xF3n", + "antes", + "ancia", + "log\xEDa", + "uci\xf3n", + "encia", + "mente", + "anza", + "icos", + "icas", + "ismo", + "able", + "ible", + "ista", + "osos", + "osas", + "ador", + "ante", + "idad", + "ivas", + "ivos", + "ico", + "ica", + "oso", + "osa", + "iva", + "ivo", + ) + __step2a_suffixes = ( + "yeron", + "yendo", + "yamos", + "yais", + "yan", + "yen", + "yas", + "yes", + "ya", + "ye", + "yo", + "y\xF3", + ) + __step2b_suffixes = ( + "ar\xEDamos", + "er\xEDamos", + "ir\xEDamos", + "i\xE9ramos", + "i\xE9semos", + "ar\xEDais", + "aremos", + "er\xEDais", + "eremos", + "ir\xEDais", + "iremos", + "ierais", + "ieseis", + "asteis", + "isteis", + "\xE1bamos", + "\xE1ramos", + "\xE1semos", + "ar\xEDan", + "ar\xEDas", + "ar\xE9is", + "er\xEDan", + "er\xEDas", + "er\xE9is", + "ir\xEDan", + "ir\xEDas", + "ir\xE9is", + "ieran", + "iesen", + "ieron", + "iendo", + "ieras", + "ieses", + "abais", + "arais", + "aseis", + "\xE9amos", + "ar\xE1n", + "ar\xE1s", + "ar\xEDa", + "er\xE1n", + "er\xE1s", + "er\xEDa", + "ir\xE1n", + "ir\xE1s", + "ir\xEDa", + "iera", + "iese", + "aste", + "iste", + "aban", + "aran", + "asen", + "aron", + "ando", + "abas", + "adas", + "idas", + "aras", + "ases", + "\xEDais", + "ados", + "idos", + "amos", + "imos", + "emos", + "ar\xE1", + "ar\xE9", + "er\xE1", + "er\xE9", + "ir\xE1", + "ir\xE9", + "aba", + "ada", + "ida", + "ara", + "ase", + "\xEDan", + "ado", + "ido", + "\xEDas", + "\xE1is", + "\xE9is", + "\xEDa", + "ad", + "ed", + "id", + "an", + "i\xF3", + "ar", + "er", + "ir", + "as", + "\xEDs", + "en", + "es", + ) + __step3_suffixes = ("os", "a", "e", "o", "\xE1", "\xE9", "\xED", "\xF3") + + def stem(self, word): + """ + Stem a Spanish word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + step1_success = False + + r1, r2 = self._r1r2_standard(word, self.__vowels) + rv = self._rv_standard(word, self.__vowels) + + # STEP 0: Attached pronoun + for suffix in self.__step0_suffixes: + if not (word.endswith(suffix) and rv.endswith(suffix)): + continue + + if ( + rv[: -len(suffix)].endswith( + ( + "ando", + "\xE1ndo", + "ar", + "\xE1r", + "er", + "\xE9r", + "iendo", + "i\xE9ndo", + "ir", + "\xEDr", + ) + ) + ) or ( + rv[: -len(suffix)].endswith("yendo") + and word[: -len(suffix)].endswith("uyendo") + ): + + word = self.__replace_accented(word[: -len(suffix)]) + r1 = self.__replace_accented(r1[: -len(suffix)]) + r2 = self.__replace_accented(r2[: -len(suffix)]) + rv = self.__replace_accented(rv[: -len(suffix)]) + break + + # STEP 1: Standard suffix removal + for suffix in self.__step1_suffixes: + if not word.endswith(suffix): + continue + + if suffix == "amente" and r1.endswith(suffix): + step1_success = True + word = word[:-6] + r2 = r2[:-6] + rv = rv[:-6] + + if r2.endswith("iv"): + word = word[:-2] + r2 = r2[:-2] + rv = rv[:-2] + + if r2.endswith("at"): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith(("os", "ic", "ad")): + word = word[:-2] + rv = rv[:-2] + + elif r2.endswith(suffix): + step1_success = True + if suffix in ( + "adora", + "ador", + "aci\xF3n", + "adoras", + "adores", + "acion", + "aciones", + "ante", + "antes", + "ancia", + "ancias", + ): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + if r2.endswith("ic"): + word = word[:-2] + rv = rv[:-2] + + elif suffix in ("log\xEDa", "log\xEDas"): + word = suffix_replace(word, suffix, "log") + rv = suffix_replace(rv, suffix, "log") + + elif suffix in ("uci\xF3n", "uciones"): + word = suffix_replace(word, suffix, "u") + rv = suffix_replace(rv, suffix, "u") + + elif suffix in ("encia", "encias"): + word = suffix_replace(word, suffix, "ente") + rv = suffix_replace(rv, suffix, "ente") + + elif suffix == "mente": + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + if r2.endswith(("ante", "able", "ible")): + word = word[:-4] + rv = rv[:-4] + + elif suffix in ("idad", "idades"): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + + for pre_suff in ("abil", "ic", "iv"): + if r2.endswith(pre_suff): + word = word[: -len(pre_suff)] + rv = rv[: -len(pre_suff)] + + elif suffix in ("ivo", "iva", "ivos", "ivas"): + word = word[: -len(suffix)] + r2 = r2[: -len(suffix)] + rv = rv[: -len(suffix)] + if r2.endswith("at"): + word = word[:-2] + rv = rv[:-2] + else: + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 2a: Verb suffixes beginning 'y' + if not step1_success: + for suffix in self.__step2a_suffixes: + if rv.endswith(suffix) and word[-len(suffix) - 1 : -len(suffix)] == "u": + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + break + + # STEP 2b: Other verb suffixes + for suffix in self.__step2b_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + rv = rv[: -len(suffix)] + if suffix in ("en", "es", "\xE9is", "emos"): + if word.endswith("gu"): + word = word[:-1] + + if rv.endswith("gu"): + rv = rv[:-1] + break + + # STEP 3: Residual suffix + for suffix in self.__step3_suffixes: + if rv.endswith(suffix): + word = word[: -len(suffix)] + if suffix in ("e", "\xE9"): + rv = rv[: -len(suffix)] + + if word[-2:] == "gu" and rv.endswith("u"): + word = word[:-1] + break + + word = self.__replace_accented(word) + + return word + + def __replace_accented(self, word): + """ + Replaces all accented letters on a word with their non-accented + counterparts. + + :param word: A spanish word, with or without accents + :type word: str or unicode + :return: a word with the accented letters (á, é, í, ó, ú) replaced with + their non-accented counterparts (a, e, i, o, u) + :rtype: str or unicode + """ + return ( + word.replace("\xE1", "a") + .replace("\xE9", "e") + .replace("\xED", "i") + .replace("\xF3", "o") + .replace("\xFA", "u") + ) + + +class SwedishStemmer(_ScandinavianStemmer): + + """ + The Swedish Snowball stemmer. + + :cvar __vowels: The Swedish vowels. + :type __vowels: unicode + :cvar __s_ending: Letters that may directly appear before a word final 's'. + :type __s_ending: unicode + :cvar __step1_suffixes: Suffixes to be deleted in step 1 of the algorithm. + :type __step1_suffixes: tuple + :cvar __step2_suffixes: Suffixes to be deleted in step 2 of the algorithm. + :type __step2_suffixes: tuple + :cvar __step3_suffixes: Suffixes to be deleted in step 3 of the algorithm. + :type __step3_suffixes: tuple + :note: A detailed description of the Swedish + stemming algorithm can be found under + http://snowball.tartarus.org/algorithms/swedish/stemmer.html + + """ + + __vowels = "aeiouy\xE4\xE5\xF6" + __s_ending = "bcdfghjklmnoprtvy" + __step1_suffixes = ( + "heterna", + "hetens", + "heter", + "heten", + "anden", + "arnas", + "ernas", + "ornas", + "andes", + "andet", + "arens", + "arna", + "erna", + "orna", + "ande", + "arne", + "aste", + "aren", + "ades", + "erns", + "ade", + "are", + "ern", + "ens", + "het", + "ast", + "ad", + "en", + "ar", + "er", + "or", + "as", + "es", + "at", + "a", + "e", + "s", + ) + __step2_suffixes = ("dd", "gd", "nn", "dt", "gt", "kt", "tt") + __step3_suffixes = ("fullt", "l\xF6st", "els", "lig", "ig") + + def stem(self, word): + """ + Stem a Swedish word and return the stemmed form. + + :param word: The word that is stemmed. + :type word: str or unicode + :return: The stemmed form. + :rtype: unicode + + """ + word = word.lower() + + if word in self.stopwords: + return word + + r1 = self._r1_scandinavian(word, self.__vowels) + + # STEP 1 + for suffix in self.__step1_suffixes: + if r1.endswith(suffix): + if suffix == "s": + if word[-2] in self.__s_ending: + word = word[:-1] + r1 = r1[:-1] + else: + word = word[: -len(suffix)] + r1 = r1[: -len(suffix)] + break + + # STEP 2 + for suffix in self.__step2_suffixes: + if r1.endswith(suffix): + word = word[:-1] + r1 = r1[:-1] + break + + # STEP 3 + for suffix in self.__step3_suffixes: + if r1.endswith(suffix): + if suffix in ("els", "lig", "ig"): + word = word[: -len(suffix)] + elif suffix in ("fullt", "l\xF6st"): + word = word[:-1] + break + + return word + + +def demo(): + """ + This function provides a demonstration of the Snowball stemmers. + + After invoking this function and specifying a language, + it stems an excerpt of the Universal Declaration of Human Rights + (which is a part of the NLTK corpus collection) and then prints + out the original and the stemmed text. + + """ + + from nltk.corpus import udhr + + udhr_corpus = { + "arabic": "Arabic_Alarabia-Arabic", + "danish": "Danish_Dansk-Latin1", + "dutch": "Dutch_Nederlands-Latin1", + "english": "English-Latin1", + "finnish": "Finnish_Suomi-Latin1", + "french": "French_Francais-Latin1", + "german": "German_Deutsch-Latin1", + "hungarian": "Hungarian_Magyar-UTF8", + "italian": "Italian_Italiano-Latin1", + "norwegian": "Norwegian-Latin1", + "porter": "English-Latin1", + "portuguese": "Portuguese_Portugues-Latin1", + "romanian": "Romanian_Romana-Latin2", + "russian": "Russian-UTF8", + "spanish": "Spanish-Latin1", + "swedish": "Swedish_Svenska-Latin1", + } + + print("\n") + print("******************************") + print("Demo for the Snowball stemmers") + print("******************************") + + while True: + + language = input( + "Please enter the name of the language " + + "to be demonstrated\n" + + "/".join(SnowballStemmer.languages) + + "\n" + + "(enter 'exit' in order to leave): " + ) + + if language == "exit": + break + + if language not in SnowballStemmer.languages: + print( + "\nOops, there is no stemmer for this language. " + + "Please try again.\n" + ) + continue + + stemmer = SnowballStemmer(language) + excerpt = udhr.words(udhr_corpus[language])[:300] + + stemmed = " ".join(stemmer.stem(word) for word in excerpt) + stemmed = re.sub(r"(.{,70})\s", r"\1\n", stemmed + " ").rstrip() + excerpt = " ".join(excerpt) + excerpt = re.sub(r"(.{,70})\s", r"\1\n", excerpt + " ").rstrip() + + print("\n") + print("-" * 70) + print("ORIGINAL".center(70)) + print(excerpt) + print("\n\n") + print("STEMMED RESULTS".center(70)) + print(stemmed) + print("-" * 70) + print("\n") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/util.py new file mode 100644 index 0000000..40b37ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/util.py @@ -0,0 +1,25 @@ +# Natural Language Toolkit: Stemmer Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Helder +# URL: +# For license information, see LICENSE.TXT + + +def suffix_replace(original, old, new): + """ + Replaces the old suffix of the original string by a new suffix + """ + return original[: -len(old)] + new + + +def prefix_replace(original, old, new): + """ + Replaces the old prefix of the original string by a new suffix + + :param original: string + :param old: string + :param new: string + :return: string + """ + return new + original[len(old) :] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/stem/wordnet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/wordnet.py new file mode 100644 index 0000000..6cc2082 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/stem/wordnet.py @@ -0,0 +1,49 @@ +# Natural Language Toolkit: WordNet stemmer interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +from nltk.corpus import wordnet as wn + + +class WordNetLemmatizer: + """ + WordNet Lemmatizer + + Lemmatize using WordNet's built-in morphy function. + Returns the input word unchanged if it cannot be found in WordNet. + + >>> from nltk.stem import WordNetLemmatizer + >>> wnl = WordNetLemmatizer() + >>> print(wnl.lemmatize('dogs')) + dog + >>> print(wnl.lemmatize('churches')) + church + >>> print(wnl.lemmatize('aardwolves')) + aardwolf + >>> print(wnl.lemmatize('abaci')) + abacus + >>> print(wnl.lemmatize('hardrock')) + hardrock + """ + + def lemmatize(self, word: str, pos: str = "n") -> str: + """Lemmatize `word` using WordNet's built-in morphy function. + Returns the input word unchanged if it cannot be found in WordNet. + + :param word: The input word to lemmatize. + :type word: str + :param pos: The Part Of Speech tag. Valid options are `"n"` for nouns, + `"v"` for verbs, `"a"` for adjectives, `"r"` for adverbs and `"s"` + for satellite adjectives. + :param pos: str + :return: The lemma of `word`, for the given `pos`. + """ + lemmas = wn._morphy(word, pos) + return min(lemmas, key=len) if lemmas else word + + def __repr__(self): + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__init__.py new file mode 100644 index 0000000..44db8d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__init__.py @@ -0,0 +1,184 @@ +# Natural Language Toolkit: Taggers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# URL: +# For license information, see LICENSE.TXT +""" +NLTK Taggers + +This package contains classes and interfaces for part-of-speech +tagging, or simply "tagging". + +A "tag" is a case-sensitive string that specifies some property of a token, +such as its part of speech. Tagged tokens are encoded as tuples +``(tag, token)``. For example, the following tagged token combines +the word ``'fly'`` with a noun part of speech tag (``'NN'``): + + >>> tagged_tok = ('fly', 'NN') + +An off-the-shelf tagger is available for English. It uses the Penn Treebank tagset: + + >>> from nltk import pos_tag, word_tokenize + >>> pos_tag(word_tokenize("John's big idea isn't all that bad.")) # doctest: +NORMALIZE_WHITESPACE + [('John', 'NNP'), ("'s", 'POS'), ('big', 'JJ'), ('idea', 'NN'), ('is', 'VBZ'), + ("n't", 'RB'), ('all', 'PDT'), ('that', 'DT'), ('bad', 'JJ'), ('.', '.')] + +A Russian tagger is also available if you specify lang="rus". It uses +the Russian National Corpus tagset: + + >>> pos_tag(word_tokenize("Илья оторопел и дважды перечитал бумажку."), lang='rus') # doctest: +SKIP + [('Илья', 'S'), ('оторопел', 'V'), ('и', 'CONJ'), ('дважды', 'ADV'), ('перечитал', 'V'), + ('бумажку', 'S'), ('.', 'NONLEX')] + +This package defines several taggers, which take a list of tokens, +assign a tag to each one, and return the resulting list of tagged tokens. +Most of the taggers are built automatically based on a training corpus. +For example, the unigram tagger tags each word *w* by checking what +the most frequent tag for *w* was in a training corpus: + + >>> from nltk.corpus import brown + >>> from nltk.tag import UnigramTagger + >>> tagger = UnigramTagger(brown.tagged_sents(categories='news')[:500]) + >>> sent = ['Mitchell', 'decried', 'the', 'high', 'rate', 'of', 'unemployment'] + >>> for word, tag in tagger.tag(sent): + ... print(word, '->', tag) + Mitchell -> NP + decried -> None + the -> AT + high -> JJ + rate -> NN + of -> IN + unemployment -> None + +Note that words that the tagger has not seen during training receive a tag +of ``None``. + +We evaluate a tagger on data that was not seen during training: + + >>> round(tagger.accuracy(brown.tagged_sents(categories='news')[500:600]), 3) + 0.735 + +For more information, please consult chapter 5 of the NLTK Book. + +isort:skip_file +""" + +from nltk.tag.api import TaggerI +from nltk.tag.util import str2tuple, tuple2str, untag +from nltk.tag.sequential import ( + SequentialBackoffTagger, + ContextTagger, + DefaultTagger, + NgramTagger, + UnigramTagger, + BigramTagger, + TrigramTagger, + AffixTagger, + RegexpTagger, + ClassifierBasedTagger, + ClassifierBasedPOSTagger, +) +from nltk.tag.brill import BrillTagger +from nltk.tag.brill_trainer import BrillTaggerTrainer +from nltk.tag.tnt import TnT +from nltk.tag.hunpos import HunposTagger +from nltk.tag.stanford import StanfordTagger, StanfordPOSTagger, StanfordNERTagger +from nltk.tag.hmm import HiddenMarkovModelTagger, HiddenMarkovModelTrainer +from nltk.tag.senna import SennaTagger, SennaChunkTagger, SennaNERTagger +from nltk.tag.mapping import tagset_mapping, map_tag +from nltk.tag.crf import CRFTagger +from nltk.tag.perceptron import PerceptronTagger + +from nltk.data import load, find + +RUS_PICKLE = ( + "taggers/averaged_perceptron_tagger_ru/averaged_perceptron_tagger_ru.pickle" +) + + +def _get_tagger(lang=None): + if lang == "rus": + tagger = PerceptronTagger(False) + ap_russian_model_loc = "file:" + str(find(RUS_PICKLE)) + tagger.load(ap_russian_model_loc) + else: + tagger = PerceptronTagger() + return tagger + + +def _pos_tag(tokens, tagset=None, tagger=None, lang=None): + # Currently only supports English and Russian. + if lang not in ["eng", "rus"]: + raise NotImplementedError( + "Currently, NLTK pos_tag only supports English and Russian " + "(i.e. lang='eng' or lang='rus')" + ) + # Throws Error if tokens is of string type + elif isinstance(tokens, str): + raise TypeError("tokens: expected a list of strings, got a string") + + else: + tagged_tokens = tagger.tag(tokens) + if tagset: # Maps to the specified tagset. + if lang == "eng": + tagged_tokens = [ + (token, map_tag("en-ptb", tagset, tag)) + for (token, tag) in tagged_tokens + ] + elif lang == "rus": + # Note that the new Russian pos tags from the model contains suffixes, + # see https://github.com/nltk/nltk/issues/2151#issuecomment-430709018 + tagged_tokens = [ + (token, map_tag("ru-rnc-new", tagset, tag.partition("=")[0])) + for (token, tag) in tagged_tokens + ] + return tagged_tokens + + +def pos_tag(tokens, tagset=None, lang="eng"): + """ + Use NLTK's currently recommended part of speech tagger to + tag the given list of tokens. + + >>> from nltk.tag import pos_tag + >>> from nltk.tokenize import word_tokenize + >>> pos_tag(word_tokenize("John's big idea isn't all that bad.")) # doctest: +NORMALIZE_WHITESPACE + [('John', 'NNP'), ("'s", 'POS'), ('big', 'JJ'), ('idea', 'NN'), ('is', 'VBZ'), + ("n't", 'RB'), ('all', 'PDT'), ('that', 'DT'), ('bad', 'JJ'), ('.', '.')] + >>> pos_tag(word_tokenize("John's big idea isn't all that bad."), tagset='universal') # doctest: +NORMALIZE_WHITESPACE + [('John', 'NOUN'), ("'s", 'PRT'), ('big', 'ADJ'), ('idea', 'NOUN'), ('is', 'VERB'), + ("n't", 'ADV'), ('all', 'DET'), ('that', 'DET'), ('bad', 'ADJ'), ('.', '.')] + + NB. Use `pos_tag_sents()` for efficient tagging of more than one sentence. + + :param tokens: Sequence of tokens to be tagged + :type tokens: list(str) + :param tagset: the tagset to be used, e.g. universal, wsj, brown + :type tagset: str + :param lang: the ISO 639 code of the language, e.g. 'eng' for English, 'rus' for Russian + :type lang: str + :return: The tagged tokens + :rtype: list(tuple(str, str)) + """ + tagger = _get_tagger(lang) + return _pos_tag(tokens, tagset, tagger, lang) + + +def pos_tag_sents(sentences, tagset=None, lang="eng"): + """ + Use NLTK's currently recommended part of speech tagger to tag the + given list of sentences, each consisting of a list of tokens. + + :param sentences: List of sentences to be tagged + :type sentences: list(list(str)) + :param tagset: the tagset to be used, e.g. universal, wsj, brown + :type tagset: str + :param lang: the ISO 639 code of the language, e.g. 'eng' for English, 'rus' for Russian + :type lang: str + :return: The list of tagged sentences + :rtype: list(list(tuple(str, str))) + """ + tagger = _get_tagger(lang) + return [_pos_tag(sent, tagset, tagger, lang) for sent in sentences] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f9e2883 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..73dae20 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill.cpython-310.pyc new file mode 100644 index 0000000..73a6b7f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill_trainer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill_trainer.cpython-310.pyc new file mode 100644 index 0000000..fa1ac74 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/brill_trainer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/crf.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/crf.cpython-310.pyc new file mode 100644 index 0000000..3dfb92f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/crf.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hmm.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hmm.cpython-310.pyc new file mode 100644 index 0000000..7c3819a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hmm.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hunpos.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hunpos.cpython-310.pyc new file mode 100644 index 0000000..367b0a8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/hunpos.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/mapping.cpython-310.pyc new file mode 100644 index 0000000..7185fae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/perceptron.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/perceptron.cpython-310.pyc new file mode 100644 index 0000000..b0de1db Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/perceptron.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/senna.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/senna.cpython-310.pyc new file mode 100644 index 0000000..b2752f9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/senna.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/sequential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/sequential.cpython-310.pyc new file mode 100644 index 0000000..8fa19a8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/sequential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/stanford.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/stanford.cpython-310.pyc new file mode 100644 index 0000000..8edb026 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/stanford.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/tnt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/tnt.cpython-310.pyc new file mode 100644 index 0000000..eb23724 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/tnt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..9ff24ff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/api.py new file mode 100644 index 0000000..1c213c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/api.py @@ -0,0 +1,296 @@ +# Natural Language Toolkit: Tagger Interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +""" +Interface for tagging each token in a sentence with supplementary +information, such as its part of speech. +""" +from abc import ABCMeta, abstractmethod +from functools import lru_cache +from itertools import chain +from typing import Dict + +from nltk.internals import deprecated, overridden +from nltk.metrics import ConfusionMatrix, accuracy +from nltk.tag.util import untag + + +class TaggerI(metaclass=ABCMeta): + """ + A processing interface for assigning a tag to each token in a list. + Tags are case sensitive strings that identify some property of each + token, such as its part of speech or its sense. + + Some taggers require specific types for their tokens. This is + generally indicated by the use of a sub-interface to ``TaggerI``. + For example, featureset taggers, which are subclassed from + ``FeaturesetTagger``, require that each token be a ``featureset``. + + Subclasses must define: + - either ``tag()`` or ``tag_sents()`` (or both) + """ + + @abstractmethod + def tag(self, tokens): + """ + Determine the most appropriate tag sequence for the given + token sequence, and return a corresponding list of tagged + tokens. A tagged token is encoded as a tuple ``(token, tag)``. + + :rtype: list(tuple(str, str)) + """ + if overridden(self.tag_sents): + return self.tag_sents([tokens])[0] + + def tag_sents(self, sentences): + """ + Apply ``self.tag()`` to each element of *sentences*. I.e.:: + + return [self.tag(sent) for sent in sentences] + """ + return [self.tag(sent) for sent in sentences] + + @deprecated("Use accuracy(gold) instead.") + def evaluate(self, gold): + return self.accuracy(gold) + + def accuracy(self, gold): + """ + Score the accuracy of the tagger against the gold standard. + Strip the tags from the gold standard text, retag it using + the tagger, then compute the accuracy score. + + :param gold: The list of tagged sentences to score the tagger on. + :type gold: list(list(tuple(str, str))) + :rtype: float + """ + + tagged_sents = self.tag_sents(untag(sent) for sent in gold) + gold_tokens = list(chain.from_iterable(gold)) + test_tokens = list(chain.from_iterable(tagged_sents)) + return accuracy(gold_tokens, test_tokens) + + @lru_cache(maxsize=1) + def _confusion_cached(self, gold): + """ + Inner function used after ``gold`` is converted to a + ``tuple(tuple(tuple(str, str)))``. That way, we can use caching on + creating a ConfusionMatrix. + + :param gold: The list of tagged sentences to run the tagger with, + also used as the reference values in the generated confusion matrix. + :type gold: tuple(tuple(tuple(str, str))) + :rtype: ConfusionMatrix + """ + + tagged_sents = self.tag_sents(untag(sent) for sent in gold) + gold_tokens = [token for _word, token in chain.from_iterable(gold)] + test_tokens = [token for _word, token in chain.from_iterable(tagged_sents)] + return ConfusionMatrix(gold_tokens, test_tokens) + + def confusion(self, gold): + """ + Return a ConfusionMatrix with the tags from ``gold`` as the reference + values, with the predictions from ``tag_sents`` as the predicted values. + + >>> from nltk.tag import PerceptronTagger + >>> from nltk.corpus import treebank + >>> tagger = PerceptronTagger() + >>> gold_data = treebank.tagged_sents()[:10] + >>> print(tagger.confusion(gold_data)) + | - | + | N | + | O P | + | N J J N N P P R R V V V V V W | + | ' E C C D E I J J J M N N N O R P R B R T V B B B B B D ` | + | ' , - . C D T X N J R S D N P S S P $ B R P O B D G N P Z T ` | + -------+----------------------------------------------------------------------------------------------+ + '' | <1> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + , | .<15> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + -NONE- | . . <.> . . 2 . . . 2 . . . 5 1 . . . . 2 . . . . . . . . . . . | + . | . . .<10> . . . . . . . . . . . . . . . . . . . . . . . . . . . | + CC | . . . . <1> . . . . . . . . . . . . . . . . . . . . . . . . . . | + CD | . . . . . <5> . . . . . . . . . . . . . . . . . . . . . . . . . | + DT | . . . . . .<20> . . . . . . . . . . . . . . . . . . . . . . . . | + EX | . . . . . . . <1> . . . . . . . . . . . . . . . . . . . . . . . | + IN | . . . . . . . .<22> . . . . . . . . . . 3 . . . . . . . . . . . | + JJ | . . . . . . . . .<16> . . . . 1 . . . . 1 . . . . . . . . . . . | + JJR | . . . . . . . . . . <.> . . . . . . . . . . . . . . . . . . . . | + JJS | . . . . . . . . . . . <1> . . . . . . . . . . . . . . . . . . . | + MD | . . . . . . . . . . . . <1> . . . . . . . . . . . . . . . . . . | + NN | . . . . . . . . . . . . .<28> 1 1 . . . . . . . . . . . . . . . | + NNP | . . . . . . . . . . . . . .<25> . . . . . . . . . . . . . . . . | + NNS | . . . . . . . . . . . . . . .<19> . . . . . . . . . . . . . . . | + POS | . . . . . . . . . . . . . . . . <1> . . . . . . . . . . . . . . | + PRP | . . . . . . . . . . . . . . . . . <4> . . . . . . . . . . . . . | + PRP$ | . . . . . . . . . . . . . . . . . . <2> . . . . . . . . . . . . | + RB | . . . . . . . . . . . . . . . . . . . <4> . . . . . . . . . . . | + RBR | . . . . . . . . . . 1 . . . . . . . . . <1> . . . . . . . . . . | + RP | . . . . . . . . . . . . . . . . . . . . . <1> . . . . . . . . . | + TO | . . . . . . . . . . . . . . . . . . . . . . <5> . . . . . . . . | + VB | . . . . . . . . . . . . . . . . . . . . . . . <3> . . . . . . . | + VBD | . . . . . . . . . . . . . 1 . . . . . . . . . . <6> . . . . . . | + VBG | . . . . . . . . . . . . . 1 . . . . . . . . . . . <4> . . . . . | + VBN | . . . . . . . . . . . . . . . . . . . . . . . . 1 . <4> . . . . | + VBP | . . . . . . . . . . . . . . . . . . . . . . . . . . . <3> . . . | + VBZ | . . . . . . . . . . . . . . . . . . . . . . . . . . . . <7> . . | + WDT | . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . <.> . | + `` | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <1>| + -------+----------------------------------------------------------------------------------------------+ + (row = reference; col = test) + + + :param gold: The list of tagged sentences to run the tagger with, + also used as the reference values in the generated confusion matrix. + :type gold: list(list(tuple(str, str))) + :rtype: ConfusionMatrix + """ + + return self._confusion_cached(tuple(tuple(sent) for sent in gold)) + + def recall(self, gold) -> Dict[str, float]: + """ + Compute the recall for each tag from ``gold`` or from running ``tag`` + on the tokenized sentences from ``gold``. Then, return the dictionary + with mappings from tag to recall. The recall is defined as: + + - *r* = true positive / (true positive + false positive) + + :param gold: The list of tagged sentences to score the tagger on. + :type gold: list(list(tuple(str, str))) + :return: A mapping from tags to recall + :rtype: Dict[str, float] + """ + + cm = self.confusion(gold) + return {tag: cm.recall(tag) for tag in cm._values} + + def precision(self, gold): + """ + Compute the precision for each tag from ``gold`` or from running ``tag`` + on the tokenized sentences from ``gold``. Then, return the dictionary + with mappings from tag to precision. The precision is defined as: + + - *p* = true positive / (true positive + false negative) + + :param gold: The list of tagged sentences to score the tagger on. + :type gold: list(list(tuple(str, str))) + :return: A mapping from tags to precision + :rtype: Dict[str, float] + """ + + cm = self.confusion(gold) + return {tag: cm.precision(tag) for tag in cm._values} + + def f_measure(self, gold, alpha=0.5): + """ + Compute the f-measure for each tag from ``gold`` or from running ``tag`` + on the tokenized sentences from ``gold``. Then, return the dictionary + with mappings from tag to f-measure. The f-measure is the harmonic mean + of the ``precision`` and ``recall``, weighted by ``alpha``. + In particular, given the precision *p* and recall *r* defined by: + + - *p* = true positive / (true positive + false negative) + - *r* = true positive / (true positive + false positive) + + The f-measure is: + + - *1/(alpha/p + (1-alpha)/r)* + + With ``alpha = 0.5``, this reduces to: + + - *2pr / (p + r)* + + :param gold: The list of tagged sentences to score the tagger on. + :type gold: list(list(tuple(str, str))) + :param alpha: Ratio of the cost of false negative compared to false + positives. Defaults to 0.5, where the costs are equal. + :type alpha: float + :return: A mapping from tags to precision + :rtype: Dict[str, float] + """ + cm = self.confusion(gold) + return {tag: cm.f_measure(tag, alpha) for tag in cm._values} + + def evaluate_per_tag(self, gold, alpha=0.5, truncate=None, sort_by_count=False): + """Tabulate the **recall**, **precision** and **f-measure** + for each tag from ``gold`` or from running ``tag`` on the tokenized + sentences from ``gold``. + + >>> from nltk.tag import PerceptronTagger + >>> from nltk.corpus import treebank + >>> tagger = PerceptronTagger() + >>> gold_data = treebank.tagged_sents()[:10] + >>> print(tagger.evaluate_per_tag(gold_data)) + Tag | Prec. | Recall | F-measure + -------+--------+--------+----------- + '' | 1.0000 | 1.0000 | 1.0000 + , | 1.0000 | 1.0000 | 1.0000 + -NONE- | 0.0000 | 0.0000 | 0.0000 + . | 1.0000 | 1.0000 | 1.0000 + CC | 1.0000 | 1.0000 | 1.0000 + CD | 0.7143 | 1.0000 | 0.8333 + DT | 1.0000 | 1.0000 | 1.0000 + EX | 1.0000 | 1.0000 | 1.0000 + IN | 0.9167 | 0.8800 | 0.8980 + JJ | 0.8889 | 0.8889 | 0.8889 + JJR | 0.0000 | 0.0000 | 0.0000 + JJS | 1.0000 | 1.0000 | 1.0000 + MD | 1.0000 | 1.0000 | 1.0000 + NN | 0.8000 | 0.9333 | 0.8615 + NNP | 0.8929 | 1.0000 | 0.9434 + NNS | 0.9500 | 1.0000 | 0.9744 + POS | 1.0000 | 1.0000 | 1.0000 + PRP | 1.0000 | 1.0000 | 1.0000 + PRP$ | 1.0000 | 1.0000 | 1.0000 + RB | 0.4000 | 1.0000 | 0.5714 + RBR | 1.0000 | 0.5000 | 0.6667 + RP | 1.0000 | 1.0000 | 1.0000 + TO | 1.0000 | 1.0000 | 1.0000 + VB | 1.0000 | 1.0000 | 1.0000 + VBD | 0.8571 | 0.8571 | 0.8571 + VBG | 1.0000 | 0.8000 | 0.8889 + VBN | 1.0000 | 0.8000 | 0.8889 + VBP | 1.0000 | 1.0000 | 1.0000 + VBZ | 1.0000 | 1.0000 | 1.0000 + WDT | 0.0000 | 0.0000 | 0.0000 + `` | 1.0000 | 1.0000 | 1.0000 + + + :param gold: The list of tagged sentences to score the tagger on. + :type gold: list(list(tuple(str, str))) + :param alpha: Ratio of the cost of false negative compared to false + positives, as used in the f-measure computation. Defaults to 0.5, + where the costs are equal. + :type alpha: float + :param truncate: If specified, then only show the specified + number of values. Any sorting (e.g., sort_by_count) + will be performed before truncation. Defaults to None + :type truncate: int, optional + :param sort_by_count: Whether to sort the outputs on number of + occurrences of that tag in the ``gold`` data, defaults to False + :type sort_by_count: bool, optional + :return: A tabulated recall, precision and f-measure string + :rtype: str + """ + cm = self.confusion(gold) + return cm.evaluate(alpha=alpha, truncate=truncate, sort_by_count=sort_by_count) + + def _check_params(self, train, model): + if (train and model) or (not train and not model): + raise ValueError("Must specify either training data or trained model.") + + +class FeaturesetTaggerI(TaggerI): + """ + A tagger that requires tokens to be ``featuresets``. A featureset + is a dictionary that maps from feature names to feature + values. See ``nltk.classify`` for more information about features + and featuresets. + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill.py new file mode 100644 index 0000000..080fee9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill.py @@ -0,0 +1,449 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from collections import Counter, defaultdict + +from nltk import jsontags +from nltk.tag import TaggerI +from nltk.tbl import Feature, Template + +###################################################################### +# Brill Templates +###################################################################### + + +@jsontags.register_tag +class Word(Feature): + """ + Feature which examines the text (word) of nearby tokens. + """ + + json_tag = "nltk.tag.brill.Word" + + @staticmethod + def extract_property(tokens, index): + """@return: The given token's text.""" + return tokens[index][0] + + +@jsontags.register_tag +class Pos(Feature): + """ + Feature which examines the tags of nearby tokens. + """ + + json_tag = "nltk.tag.brill.Pos" + + @staticmethod + def extract_property(tokens, index): + """@return: The given token's tag.""" + return tokens[index][1] + + +def nltkdemo18(): + """ + Return 18 templates, from the original nltk demo, in multi-feature syntax + """ + return [ + Template(Pos([-1])), + Template(Pos([1])), + Template(Pos([-2])), + Template(Pos([2])), + Template(Pos([-2, -1])), + Template(Pos([1, 2])), + Template(Pos([-3, -2, -1])), + Template(Pos([1, 2, 3])), + Template(Pos([-1]), Pos([1])), + Template(Word([-1])), + Template(Word([1])), + Template(Word([-2])), + Template(Word([2])), + Template(Word([-2, -1])), + Template(Word([1, 2])), + Template(Word([-3, -2, -1])), + Template(Word([1, 2, 3])), + Template(Word([-1]), Word([1])), + ] + + +def nltkdemo18plus(): + """ + Return 18 templates, from the original nltk demo, and additionally a few + multi-feature ones (the motivation is easy comparison with nltkdemo18) + """ + return nltkdemo18() + [ + Template(Word([-1]), Pos([1])), + Template(Pos([-1]), Word([1])), + Template(Word([-1]), Word([0]), Pos([1])), + Template(Pos([-1]), Word([0]), Word([1])), + Template(Pos([-1]), Word([0]), Pos([1])), + ] + + +def fntbl37(): + """ + Return 37 templates taken from the postagging task of the + fntbl distribution https://www.cs.jhu.edu/~rflorian/fntbl/ + (37 is after excluding a handful which do not condition on Pos[0]; + fntbl can do that but the current nltk implementation cannot.) + """ + return [ + Template(Word([0]), Word([1]), Word([2])), + Template(Word([-1]), Word([0]), Word([1])), + Template(Word([0]), Word([-1])), + Template(Word([0]), Word([1])), + Template(Word([0]), Word([2])), + Template(Word([0]), Word([-2])), + Template(Word([1, 2])), + Template(Word([-2, -1])), + Template(Word([1, 2, 3])), + Template(Word([-3, -2, -1])), + Template(Word([0]), Pos([2])), + Template(Word([0]), Pos([-2])), + Template(Word([0]), Pos([1])), + Template(Word([0]), Pos([-1])), + Template(Word([0])), + Template(Word([-2])), + Template(Word([2])), + Template(Word([1])), + Template(Word([-1])), + Template(Pos([-1]), Pos([1])), + Template(Pos([1]), Pos([2])), + Template(Pos([-1]), Pos([-2])), + Template(Pos([1])), + Template(Pos([-1])), + Template(Pos([-2])), + Template(Pos([2])), + Template(Pos([1, 2, 3])), + Template(Pos([1, 2])), + Template(Pos([-3, -2, -1])), + Template(Pos([-2, -1])), + Template(Pos([1]), Word([0]), Word([1])), + Template(Pos([1]), Word([0]), Word([-1])), + Template(Pos([-1]), Word([-1]), Word([0])), + Template(Pos([-1]), Word([0]), Word([1])), + Template(Pos([-2]), Pos([-1])), + Template(Pos([1]), Pos([2])), + Template(Pos([1]), Pos([2]), Word([1])), + ] + + +def brill24(): + """ + Return 24 templates of the seminal TBL paper, Brill (1995) + """ + return [ + Template(Pos([-1])), + Template(Pos([1])), + Template(Pos([-2])), + Template(Pos([2])), + Template(Pos([-2, -1])), + Template(Pos([1, 2])), + Template(Pos([-3, -2, -1])), + Template(Pos([1, 2, 3])), + Template(Pos([-1]), Pos([1])), + Template(Pos([-2]), Pos([-1])), + Template(Pos([1]), Pos([2])), + Template(Word([-1])), + Template(Word([1])), + Template(Word([-2])), + Template(Word([2])), + Template(Word([-2, -1])), + Template(Word([1, 2])), + Template(Word([-1, 0])), + Template(Word([0, 1])), + Template(Word([0])), + Template(Word([-1]), Pos([-1])), + Template(Word([1]), Pos([1])), + Template(Word([0]), Word([-1]), Pos([-1])), + Template(Word([0]), Word([1]), Pos([1])), + ] + + +def describe_template_sets(): + """ + Print the available template sets in this demo, with a short description" + """ + import inspect + import sys + + # a bit of magic to get all functions in this module + templatesets = inspect.getmembers(sys.modules[__name__], inspect.isfunction) + for (name, obj) in templatesets: + if name == "describe_template_sets": + continue + print(name, obj.__doc__, "\n") + + +###################################################################### +# The Brill Tagger +###################################################################### + + +@jsontags.register_tag +class BrillTagger(TaggerI): + """ + Brill's transformational rule-based tagger. Brill taggers use an + initial tagger (such as ``tag.DefaultTagger``) to assign an initial + tag sequence to a text; and then apply an ordered list of + transformational rules to correct the tags of individual tokens. + These transformation rules are specified by the ``TagRule`` + interface. + + Brill taggers can be created directly, from an initial tagger and + a list of transformational rules; but more often, Brill taggers + are created by learning rules from a training corpus, using one + of the TaggerTrainers available. + """ + + json_tag = "nltk.tag.BrillTagger" + + def __init__(self, initial_tagger, rules, training_stats=None): + """ + :param initial_tagger: The initial tagger + :type initial_tagger: TaggerI + + :param rules: An ordered list of transformation rules that + should be used to correct the initial tagging. + :type rules: list(TagRule) + + :param training_stats: A dictionary of statistics collected + during training, for possible later use + :type training_stats: dict + + """ + self._initial_tagger = initial_tagger + self._rules = tuple(rules) + self._training_stats = training_stats + + def encode_json_obj(self): + return self._initial_tagger, self._rules, self._training_stats + + @classmethod + def decode_json_obj(cls, obj): + _initial_tagger, _rules, _training_stats = obj + return cls(_initial_tagger, _rules, _training_stats) + + def rules(self): + """ + Return the ordered list of transformation rules that this tagger has learnt + + :return: the ordered list of transformation rules that correct the initial tagging + :rtype: list of Rules + """ + return self._rules + + def train_stats(self, statistic=None): + """ + Return a named statistic collected during training, or a dictionary of all + available statistics if no name given + + :param statistic: name of statistic + :type statistic: str + :return: some statistic collected during training of this tagger + :rtype: any (but usually a number) + """ + if statistic is None: + return self._training_stats + else: + return self._training_stats.get(statistic) + + def tag(self, tokens): + # Inherit documentation from TaggerI + + # Run the initial tagger. + tagged_tokens = self._initial_tagger.tag(tokens) + + # Create a dictionary that maps each tag to a list of the + # indices of tokens that have that tag. + tag_to_positions = defaultdict(set) + for i, (token, tag) in enumerate(tagged_tokens): + tag_to_positions[tag].add(i) + + # Apply each rule, in order. Only try to apply rules at + # positions that have the desired original tag. + for rule in self._rules: + # Find the positions where it might apply + positions = tag_to_positions.get(rule.original_tag, []) + # Apply the rule at those positions. + changed = rule.apply(tagged_tokens, positions) + # Update tag_to_positions with the positions of tags that + # were modified. + for i in changed: + tag_to_positions[rule.original_tag].remove(i) + tag_to_positions[rule.replacement_tag].add(i) + + return tagged_tokens + + def print_template_statistics(self, test_stats=None, printunused=True): + """ + Print a list of all templates, ranked according to efficiency. + + If test_stats is available, the templates are ranked according to their + relative contribution (summed for all rules created from a given template, + weighted by score) to the performance on the test set. If no test_stats, then + statistics collected during training are used instead. There is also + an unweighted measure (just counting the rules). This is less informative, + though, as many low-score rules will appear towards end of training. + + :param test_stats: dictionary of statistics collected during testing + :type test_stats: dict of str -> any (but usually numbers) + :param printunused: if True, print a list of all unused templates + :type printunused: bool + :return: None + :rtype: None + """ + tids = [r.templateid for r in self._rules] + train_stats = self.train_stats() + + trainscores = train_stats["rulescores"] + assert len(trainscores) == len( + tids + ), "corrupt statistics: " "{} train scores for {} rules".format( + trainscores, tids + ) + template_counts = Counter(tids) + weighted_traincounts = Counter() + for (tid, score) in zip(tids, trainscores): + weighted_traincounts[tid] += score + tottrainscores = sum(trainscores) + + # det_tplsort() is for deterministic sorting; + # the otherwise convenient Counter.most_common() unfortunately + # does not break ties deterministically + # between python versions and will break cross-version tests + def det_tplsort(tpl_value): + return (tpl_value[1], repr(tpl_value[0])) + + def print_train_stats(): + print( + "TEMPLATE STATISTICS (TRAIN) {} templates, {} rules)".format( + len(template_counts), len(tids) + ) + ) + print( + "TRAIN ({tokencount:7d} tokens) initial {initialerrors:5d} {initialacc:.4f} " + "final: {finalerrors:5d} {finalacc:.4f}".format(**train_stats) + ) + head = "#ID | Score (train) | #Rules | Template" + print(head, "\n", "-" * len(head), sep="") + train_tplscores = sorted( + weighted_traincounts.items(), key=det_tplsort, reverse=True + ) + for (tid, trainscore) in train_tplscores: + s = "{} | {:5d} {:5.3f} |{:4d} {:.3f} | {}".format( + tid, + trainscore, + trainscore / tottrainscores, + template_counts[tid], + template_counts[tid] / len(tids), + Template.ALLTEMPLATES[int(tid)], + ) + print(s) + + def print_testtrain_stats(): + testscores = test_stats["rulescores"] + print( + "TEMPLATE STATISTICS (TEST AND TRAIN) ({} templates, {} rules)".format( + len(template_counts), len(tids) + ) + ) + print( + "TEST ({tokencount:7d} tokens) initial {initialerrors:5d} {initialacc:.4f} " + "final: {finalerrors:5d} {finalacc:.4f} ".format(**test_stats) + ) + print( + "TRAIN ({tokencount:7d} tokens) initial {initialerrors:5d} {initialacc:.4f} " + "final: {finalerrors:5d} {finalacc:.4f} ".format(**train_stats) + ) + weighted_testcounts = Counter() + for (tid, score) in zip(tids, testscores): + weighted_testcounts[tid] += score + tottestscores = sum(testscores) + head = "#ID | Score (test) | Score (train) | #Rules | Template" + print(head, "\n", "-" * len(head), sep="") + test_tplscores = sorted( + weighted_testcounts.items(), key=det_tplsort, reverse=True + ) + for (tid, testscore) in test_tplscores: + s = "{:s} |{:5d} {:6.3f} | {:4d} {:.3f} |{:4d} {:.3f} | {:s}".format( + tid, + testscore, + testscore / tottestscores, + weighted_traincounts[tid], + weighted_traincounts[tid] / tottrainscores, + template_counts[tid], + template_counts[tid] / len(tids), + Template.ALLTEMPLATES[int(tid)], + ) + print(s) + + def print_unused_templates(): + usedtpls = {int(tid) for tid in tids} + unused = [ + (tid, tpl) + for (tid, tpl) in enumerate(Template.ALLTEMPLATES) + if tid not in usedtpls + ] + print(f"UNUSED TEMPLATES ({len(unused)})") + + for (tid, tpl) in unused: + print(f"{tid:03d} {str(tpl):s}") + + if test_stats is None: + print_train_stats() + else: + print_testtrain_stats() + print() + if printunused: + print_unused_templates() + print() + + def batch_tag_incremental(self, sequences, gold): + """ + Tags by applying each rule to the entire corpus (rather than all rules to a + single sequence). The point is to collect statistics on the test set for + individual rules. + + NOTE: This is inefficient (does not build any index, so will traverse the entire + corpus N times for N rules) -- usually you would not care about statistics for + individual rules and thus use batch_tag() instead + + :param sequences: lists of token sequences (sentences, in some applications) to be tagged + :type sequences: list of list of strings + :param gold: the gold standard + :type gold: list of list of strings + :returns: tuple of (tagged_sequences, ordered list of rule scores (one for each rule)) + """ + + def counterrors(xs): + return sum(t[1] != g[1] for pair in zip(xs, gold) for (t, g) in zip(*pair)) + + testing_stats = {} + testing_stats["tokencount"] = sum(len(t) for t in sequences) + testing_stats["sequencecount"] = len(sequences) + tagged_tokenses = [self._initial_tagger.tag(tokens) for tokens in sequences] + testing_stats["initialerrors"] = counterrors(tagged_tokenses) + testing_stats["initialacc"] = ( + 1 - testing_stats["initialerrors"] / testing_stats["tokencount"] + ) + # Apply each rule to the entire corpus, in order + errors = [testing_stats["initialerrors"]] + for rule in self._rules: + for tagged_tokens in tagged_tokenses: + rule.apply(tagged_tokens) + errors.append(counterrors(tagged_tokenses)) + testing_stats["rulescores"] = [ + err0 - err1 for (err0, err1) in zip(errors, errors[1:]) + ] + testing_stats["finalerrors"] = errors[-1] + testing_stats["finalacc"] = ( + 1 - testing_stats["finalerrors"] / testing_stats["tokencount"] + ) + return (tagged_tokenses, testing_stats) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill_trainer.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill_trainer.py new file mode 100644 index 0000000..d9642c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/brill_trainer.py @@ -0,0 +1,629 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2013 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +import bisect +import textwrap +from collections import defaultdict + +from nltk.tag import BrillTagger, untag + +###################################################################### +# Brill Tagger Trainer +###################################################################### + + +class BrillTaggerTrainer: + """ + A trainer for tbl taggers. + """ + + def __init__( + self, initial_tagger, templates, trace=0, deterministic=None, ruleformat="str" + ): + """ + Construct a Brill tagger from a baseline tagger and a + set of templates + + :param initial_tagger: the baseline tagger + :type initial_tagger: Tagger + :param templates: templates to be used in training + :type templates: list of Templates + :param trace: verbosity level + :type trace: int + :param deterministic: if True, adjudicate ties deterministically + :type deterministic: bool + :param ruleformat: format of reported Rules + :type ruleformat: str + :return: An untrained BrillTagger + :rtype: BrillTagger + """ + + if deterministic is None: + deterministic = trace > 0 + self._initial_tagger = initial_tagger + self._templates = templates + self._trace = trace + self._deterministic = deterministic + self._ruleformat = ruleformat + + self._tag_positions = None + """Mapping from tags to lists of positions that use that tag.""" + + self._rules_by_position = None + """Mapping from positions to the set of rules that are known + to occur at that position. Position is (sentnum, wordnum). + Initially, this will only contain positions where each rule + applies in a helpful way; but when we examine a rule, we'll + extend this list to also include positions where each rule + applies in a harmful or neutral way.""" + + self._positions_by_rule = None + """Mapping from rule to position to effect, specifying the + effect that each rule has on the overall score, at each + position. Position is (sentnum, wordnum); and effect is + -1, 0, or 1. As with _rules_by_position, this mapping starts + out only containing rules with positive effects; but when + we examine a rule, we'll extend this mapping to include + the positions where the rule is harmful or neutral.""" + + self._rules_by_score = None + """Mapping from scores to the set of rules whose effect on the + overall score is upper bounded by that score. Invariant: + rulesByScore[s] will contain r iff the sum of + _positions_by_rule[r] is s.""" + + self._rule_scores = None + """Mapping from rules to upper bounds on their effects on the + overall score. This is the inverse mapping to _rules_by_score. + Invariant: ruleScores[r] = sum(_positions_by_rule[r])""" + + self._first_unknown_position = None + """Mapping from rules to the first position where we're unsure + if the rule applies. This records the next position we + need to check to see if the rule messed anything up.""" + + # Training + + def train(self, train_sents, max_rules=200, min_score=2, min_acc=None): + r""" + Trains the Brill tagger on the corpus *train_sents*, + producing at most *max_rules* transformations, each of which + reduces the net number of errors in the corpus by at least + *min_score*, and each of which has accuracy not lower than + *min_acc*. + + >>> # Relevant imports + >>> from nltk.tbl.template import Template + >>> from nltk.tag.brill import Pos, Word + >>> from nltk.tag import untag, RegexpTagger, BrillTaggerTrainer + + >>> # Load some data + >>> from nltk.corpus import treebank + >>> training_data = treebank.tagged_sents()[:100] + >>> baseline_data = treebank.tagged_sents()[100:200] + >>> gold_data = treebank.tagged_sents()[200:300] + >>> testing_data = [untag(s) for s in gold_data] + + >>> backoff = RegexpTagger([ + ... (r'^-?[0-9]+(\.[0-9]+)?$', 'CD'), # cardinal numbers + ... (r'(The|the|A|a|An|an)$', 'AT'), # articles + ... (r'.*able$', 'JJ'), # adjectives + ... (r'.*ness$', 'NN'), # nouns formed from adjectives + ... (r'.*ly$', 'RB'), # adverbs + ... (r'.*s$', 'NNS'), # plural nouns + ... (r'.*ing$', 'VBG'), # gerunds + ... (r'.*ed$', 'VBD'), # past tense verbs + ... (r'.*', 'NN') # nouns (default) + ... ]) + + >>> baseline = backoff #see NOTE1 + >>> baseline.accuracy(gold_data) #doctest: +ELLIPSIS + 0.243... + + >>> # Set up templates + >>> Template._cleartemplates() #clear any templates created in earlier tests + >>> templates = [Template(Pos([-1])), Template(Pos([-1]), Word([0]))] + + >>> # Construct a BrillTaggerTrainer + >>> tt = BrillTaggerTrainer(baseline, templates, trace=3) + + >>> tagger1 = tt.train(training_data, max_rules=10) + TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: None) + Finding initial useful rules... + Found 847 useful rules. + + B | + S F r O | Score = Fixed - Broken + c i o t | R Fixed = num tags changed incorrect -> correct + o x k h | u Broken = num tags changed correct -> incorrect + r e e e | l Other = num tags changed incorrect -> incorrect + e d n r | e + ------------------+------------------------------------------------------- + 132 132 0 0 | AT->DT if Pos:NN@[-1] + 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0] + 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0] + 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0] + 47 63 16 162 | NN->IN if Pos:NNS@[-1] + 33 33 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0] + 26 26 0 0 | IN->. if Pos:NNS@[-1] & Word:.@[0] + 24 24 0 0 | IN->, if Pos:NNS@[-1] & Word:,@[0] + 22 27 5 24 | NN->-NONE- if Pos:VBD@[-1] + 17 17 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0] + + >>> tagger1.rules()[1:3] + (Rule('001', 'NN', ',', [(Pos([-1]),'NN'), (Word([0]),',')]), Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')])) + + >>> train_stats = tagger1.train_stats() + >>> [train_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']] + [1776, 1270, [132, 85, 69, 51, 47, 33, 26, 24, 22, 17]] + + >>> tagger1.print_template_statistics(printunused=False) + TEMPLATE STATISTICS (TRAIN) 2 templates, 10 rules) + TRAIN ( 2417 tokens) initial 1776 0.2652 final: 1270 0.4746 + #ID | Score (train) | #Rules | Template + -------------------------------------------- + 001 | 305 0.603 | 7 0.700 | Template(Pos([-1]),Word([0])) + 000 | 201 0.397 | 3 0.300 | Template(Pos([-1])) + + + + >>> round(tagger1.accuracy(gold_data),5) + 0.43834 + + >>> tagged, test_stats = tagger1.batch_tag_incremental(testing_data, gold_data) + + >>> tagged[33][12:] == [('foreign', 'IN'), ('debt', 'NN'), ('of', 'IN'), ('$', 'NN'), ('64', 'CD'), + ... ('billion', 'NN'), ('*U*', 'NN'), ('--', 'NN'), ('the', 'DT'), ('third-highest', 'NN'), ('in', 'NN'), + ... ('the', 'DT'), ('developing', 'VBG'), ('world', 'NN'), ('.', '.')] + True + + >>> [test_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']] + [1859, 1380, [100, 85, 67, 58, 27, 36, 27, 16, 31, 32]] + + >>> # A high-accuracy tagger + >>> tagger2 = tt.train(training_data, max_rules=10, min_acc=0.99) + TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: 0.99) + Finding initial useful rules... + Found 847 useful rules. + + B | + S F r O | Score = Fixed - Broken + c i o t | R Fixed = num tags changed incorrect -> correct + o x k h | u Broken = num tags changed correct -> incorrect + r e e e | l Other = num tags changed incorrect -> incorrect + e d n r | e + ------------------+------------------------------------------------------- + 132 132 0 0 | AT->DT if Pos:NN@[-1] + 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0] + 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0] + 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0] + 36 36 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0] + 26 26 0 0 | NN->. if Pos:NNS@[-1] & Word:.@[0] + 24 24 0 0 | NN->, if Pos:NNS@[-1] & Word:,@[0] + 19 19 0 6 | NN->VB if Pos:TO@[-1] + 18 18 0 0 | CD->-NONE- if Pos:NN@[-1] & Word:0@[0] + 18 18 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0] + + >>> round(tagger2.accuracy(gold_data), 8) + 0.43996744 + + >>> tagger2.rules()[2:4] + (Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')]), Rule('001', 'NN', 'IN', [(Pos([-1]),'NN'), (Word([0]),'of')])) + + # NOTE1: (!!FIXME) A far better baseline uses nltk.tag.UnigramTagger, + # with a RegexpTagger only as backoff. For instance, + # >>> baseline = UnigramTagger(baseline_data, backoff=backoff) + # However, as of Nov 2013, nltk.tag.UnigramTagger does not yield consistent results + # between python versions. The simplistic backoff above is a workaround to make doctests + # get consistent input. + + :param train_sents: training data + :type train_sents: list(list(tuple)) + :param max_rules: output at most max_rules rules + :type max_rules: int + :param min_score: stop training when no rules better than min_score can be found + :type min_score: int + :param min_acc: discard any rule with lower accuracy than min_acc + :type min_acc: float or None + :return: the learned tagger + :rtype: BrillTagger + """ + # FIXME: several tests are a bit too dependent on tracing format + # FIXME: tests in trainer.fast and trainer.brillorig are exact duplicates + + # Basic idea: Keep track of the rules that apply at each position. + # And keep track of the positions to which each rule applies. + + # Create a new copy of the training corpus, and run the + # initial tagger on it. We will progressively update this + # test corpus to look more like the training corpus. + test_sents = [ + list(self._initial_tagger.tag(untag(sent))) for sent in train_sents + ] + + # Collect some statistics on the training process + trainstats = {} + trainstats["min_acc"] = min_acc + trainstats["min_score"] = min_score + trainstats["tokencount"] = sum(len(t) for t in test_sents) + trainstats["sequencecount"] = len(test_sents) + trainstats["templatecount"] = len(self._templates) + trainstats["rulescores"] = [] + trainstats["initialerrors"] = sum( + tag[1] != truth[1] + for paired in zip(test_sents, train_sents) + for (tag, truth) in zip(*paired) + ) + trainstats["initialacc"] = ( + 1 - trainstats["initialerrors"] / trainstats["tokencount"] + ) + if self._trace > 0: + print( + "TBL train (fast) (seqs: {sequencecount}; tokens: {tokencount}; " + "tpls: {templatecount}; min score: {min_score}; min acc: {min_acc})".format( + **trainstats + ) + ) + + # Initialize our mappings. This will find any errors made + # by the initial tagger, and use those to generate repair + # rules, which are added to the rule mappings. + if self._trace: + print("Finding initial useful rules...") + self._init_mappings(test_sents, train_sents) + if self._trace: + print(f" Found {len(self._rule_scores)} useful rules.") + + # Let the user know what we're up to. + if self._trace > 2: + self._trace_header() + elif self._trace == 1: + print("Selecting rules...") + + # Repeatedly select the best rule, and add it to `rules`. + rules = [] + try: + while len(rules) < max_rules: + # Find the best rule, and add it to our rule list. + rule = self._best_rule(train_sents, test_sents, min_score, min_acc) + if rule: + rules.append(rule) + score = self._rule_scores[rule] + trainstats["rulescores"].append(score) + else: + break # No more good rules left! + + # Report the rule that we found. + if self._trace > 1: + self._trace_rule(rule) + + # Apply the new rule at the relevant sites + self._apply_rule(rule, test_sents) + + # Update _tag_positions[rule.original_tag] and + # _tag_positions[rule.replacement_tag] for the affected + # positions (i.e., self._positions_by_rule[rule]). + self._update_tag_positions(rule) + + # Update rules that were affected by the change. + self._update_rules(rule, train_sents, test_sents) + + # The user can cancel training manually: + except KeyboardInterrupt: + print(f"Training stopped manually -- {len(rules)} rules found") + + # Discard our tag position mapping & rule mappings. + self._clean() + trainstats["finalerrors"] = trainstats["initialerrors"] - sum( + trainstats["rulescores"] + ) + trainstats["finalacc"] = ( + 1 - trainstats["finalerrors"] / trainstats["tokencount"] + ) + # Create and return a tagger from the rules we found. + return BrillTagger(self._initial_tagger, rules, trainstats) + + def _init_mappings(self, test_sents, train_sents): + """ + Initialize the tag position mapping & the rule related + mappings. For each error in test_sents, find new rules that + would correct them, and add them to the rule mappings. + """ + self._tag_positions = defaultdict(list) + self._rules_by_position = defaultdict(set) + self._positions_by_rule = defaultdict(dict) + self._rules_by_score = defaultdict(set) + self._rule_scores = defaultdict(int) + self._first_unknown_position = defaultdict(int) + # Scan through the corpus, initializing the tag_positions + # mapping and all the rule-related mappings. + for sentnum, sent in enumerate(test_sents): + for wordnum, (word, tag) in enumerate(sent): + + # Initialize tag_positions + self._tag_positions[tag].append((sentnum, wordnum)) + + # If it's an error token, update the rule-related mappings. + correct_tag = train_sents[sentnum][wordnum][1] + if tag != correct_tag: + for rule in self._find_rules(sent, wordnum, correct_tag): + self._update_rule_applies(rule, sentnum, wordnum, train_sents) + + def _clean(self): + self._tag_positions = None + self._rules_by_position = None + self._positions_by_rule = None + self._rules_by_score = None + self._rule_scores = None + self._first_unknown_position = None + + def _find_rules(self, sent, wordnum, new_tag): + """ + Use the templates to find rules that apply at index *wordnum* + in the sentence *sent* and generate the tag *new_tag*. + """ + for template in self._templates: + yield from template.applicable_rules(sent, wordnum, new_tag) + + def _update_rule_applies(self, rule, sentnum, wordnum, train_sents): + """ + Update the rule data tables to reflect the fact that + *rule* applies at the position *(sentnum, wordnum)*. + """ + pos = sentnum, wordnum + + # If the rule is already known to apply here, ignore. + # (This only happens if the position's tag hasn't changed.) + if pos in self._positions_by_rule[rule]: + return + + # Update self._positions_by_rule. + correct_tag = train_sents[sentnum][wordnum][1] + if rule.replacement_tag == correct_tag: + self._positions_by_rule[rule][pos] = 1 + elif rule.original_tag == correct_tag: + self._positions_by_rule[rule][pos] = -1 + else: # was wrong, remains wrong + self._positions_by_rule[rule][pos] = 0 + + # Update _rules_by_position + self._rules_by_position[pos].add(rule) + + # Update _rule_scores. + old_score = self._rule_scores[rule] + self._rule_scores[rule] += self._positions_by_rule[rule][pos] + + # Update _rules_by_score. + self._rules_by_score[old_score].discard(rule) + self._rules_by_score[self._rule_scores[rule]].add(rule) + + def _update_rule_not_applies(self, rule, sentnum, wordnum): + """ + Update the rule data tables to reflect the fact that *rule* + does not apply at the position *(sentnum, wordnum)*. + """ + pos = sentnum, wordnum + + # Update _rule_scores. + old_score = self._rule_scores[rule] + self._rule_scores[rule] -= self._positions_by_rule[rule][pos] + + # Update _rules_by_score. + self._rules_by_score[old_score].discard(rule) + self._rules_by_score[self._rule_scores[rule]].add(rule) + + # Update _positions_by_rule + del self._positions_by_rule[rule][pos] + self._rules_by_position[pos].remove(rule) + + # Optional addition: if the rule now applies nowhere, delete + # all its dictionary entries. + + def _best_rule(self, train_sents, test_sents, min_score, min_acc): + """ + Find the next best rule. This is done by repeatedly taking a + rule with the highest score and stepping through the corpus to + see where it applies. When it makes an error (decreasing its + score) it's bumped down, and we try a new rule with the + highest score. When we find a rule which has the highest + score *and* which has been tested against the entire corpus, we + can conclude that it's the next best rule. + """ + for max_score in sorted(self._rules_by_score.keys(), reverse=True): + if len(self._rules_by_score) == 0: + return None + if max_score < min_score or max_score <= 0: + return None + best_rules = list(self._rules_by_score[max_score]) + if self._deterministic: + best_rules.sort(key=repr) + for rule in best_rules: + positions = self._tag_positions[rule.original_tag] + + unk = self._first_unknown_position.get(rule, (0, -1)) + start = bisect.bisect_left(positions, unk) + + for i in range(start, len(positions)): + sentnum, wordnum = positions[i] + if rule.applies(test_sents[sentnum], wordnum): + self._update_rule_applies(rule, sentnum, wordnum, train_sents) + if self._rule_scores[rule] < max_score: + self._first_unknown_position[rule] = (sentnum, wordnum + 1) + break # The update demoted the rule. + + if self._rule_scores[rule] == max_score: + self._first_unknown_position[rule] = (len(train_sents) + 1, 0) + # optimization: if no min_acc threshold given, don't bother computing accuracy + if min_acc is None: + return rule + else: + changes = self._positions_by_rule[rule].values() + num_fixed = len([c for c in changes if c == 1]) + num_broken = len([c for c in changes if c == -1]) + # acc here is fixed/(fixed+broken); could also be + # fixed/(fixed+broken+other) == num_fixed/len(changes) + acc = num_fixed / (num_fixed + num_broken) + if acc >= min_acc: + return rule + # else: rule too inaccurate, discard and try next + + # We demoted (or skipped due to < min_acc, if that was given) + # all the rules with score==max_score. + + assert min_acc is not None or not self._rules_by_score[max_score] + if not self._rules_by_score[max_score]: + del self._rules_by_score[max_score] + + def _apply_rule(self, rule, test_sents): + """ + Update *test_sents* by applying *rule* everywhere where its + conditions are met. + """ + update_positions = set(self._positions_by_rule[rule]) + new_tag = rule.replacement_tag + + if self._trace > 3: + self._trace_apply(len(update_positions)) + + # Update test_sents. + for (sentnum, wordnum) in update_positions: + text = test_sents[sentnum][wordnum][0] + test_sents[sentnum][wordnum] = (text, new_tag) + + def _update_tag_positions(self, rule): + """ + Update _tag_positions to reflect the changes to tags that are + made by *rule*. + """ + # Update the tag index. + for pos in self._positions_by_rule[rule]: + # Delete the old tag. + old_tag_positions = self._tag_positions[rule.original_tag] + old_index = bisect.bisect_left(old_tag_positions, pos) + del old_tag_positions[old_index] + # Insert the new tag. + new_tag_positions = self._tag_positions[rule.replacement_tag] + bisect.insort_left(new_tag_positions, pos) + + def _update_rules(self, rule, train_sents, test_sents): + """ + Check if we should add or remove any rules from consideration, + given the changes made by *rule*. + """ + # Collect a list of all positions that might be affected. + neighbors = set() + for sentnum, wordnum in self._positions_by_rule[rule]: + for template in self._templates: + n = template.get_neighborhood(test_sents[sentnum], wordnum) + neighbors.update([(sentnum, i) for i in n]) + + # Update the rules at each position. + num_obsolete = num_new = num_unseen = 0 + for sentnum, wordnum in neighbors: + test_sent = test_sents[sentnum] + correct_tag = train_sents[sentnum][wordnum][1] + + # Check if the change causes any rule at this position to + # stop matching; if so, then update our rule mappings + # accordingly. + old_rules = set(self._rules_by_position[sentnum, wordnum]) + for old_rule in old_rules: + if not old_rule.applies(test_sent, wordnum): + num_obsolete += 1 + self._update_rule_not_applies(old_rule, sentnum, wordnum) + + # Check if the change causes our templates to propose any + # new rules for this position. + for template in self._templates: + for new_rule in template.applicable_rules( + test_sent, wordnum, correct_tag + ): + if new_rule not in old_rules: + num_new += 1 + if new_rule not in self._rule_scores: + num_unseen += 1 + old_rules.add(new_rule) + self._update_rule_applies( + new_rule, sentnum, wordnum, train_sents + ) + + # We may have caused other rules to match here, that are + # not proposed by our templates -- in particular, rules + # that are harmful or neutral. We therefore need to + # update any rule whose first_unknown_position is past + # this rule. + for new_rule, pos in self._first_unknown_position.items(): + if pos > (sentnum, wordnum): + if new_rule not in old_rules: + num_new += 1 + if new_rule.applies(test_sent, wordnum): + self._update_rule_applies( + new_rule, sentnum, wordnum, train_sents + ) + + if self._trace > 3: + self._trace_update_rules(num_obsolete, num_new, num_unseen) + + # Tracing + + def _trace_header(self): + print( + """ + B | + S F r O | Score = Fixed - Broken + c i o t | R Fixed = num tags changed incorrect -> correct + o x k h | u Broken = num tags changed correct -> incorrect + r e e e | l Other = num tags changed incorrect -> incorrect + e d n r | e +------------------+------------------------------------------------------- + """.rstrip() + ) + + def _trace_rule(self, rule): + assert self._rule_scores[rule] == sum(self._positions_by_rule[rule].values()) + + changes = self._positions_by_rule[rule].values() + num_fixed = len([c for c in changes if c == 1]) + num_broken = len([c for c in changes if c == -1]) + num_other = len([c for c in changes if c == 0]) + score = self._rule_scores[rule] + + rulestr = rule.format(self._ruleformat) + if self._trace > 2: + print( + "{:4d}{:4d}{:4d}{:4d} |".format( + score, num_fixed, num_broken, num_other + ), + end=" ", + ) + print( + textwrap.fill( + rulestr, + initial_indent=" " * 20, + width=79, + subsequent_indent=" " * 18 + "| ", + ).strip() + ) + else: + print(rulestr) + + def _trace_apply(self, num_updates): + prefix = " " * 18 + "|" + print(prefix) + print(prefix, f"Applying rule to {num_updates} positions.") + + def _trace_update_rules(self, num_obsolete, num_new, num_unseen): + prefix = " " * 18 + "|" + print(prefix, "Updated rule tables:") + print(prefix, (f" - {num_obsolete} rule applications removed")) + print( + prefix, + (f" - {num_new} rule applications added ({num_unseen} novel)"), + ) + print(prefix) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/crf.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/crf.py new file mode 100644 index 0000000..49c5447 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/crf.py @@ -0,0 +1,207 @@ +# Natural Language Toolkit: Interface to the CRFSuite Tagger +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Long Duong +# URL: +# For license information, see LICENSE.TXT + +""" +A module for POS tagging using CRFSuite +""" + +import re +import unicodedata + +from nltk.tag.api import TaggerI + +try: + import pycrfsuite +except ImportError: + pass + + +class CRFTagger(TaggerI): + """ + A module for POS tagging using CRFSuite https://pypi.python.org/pypi/python-crfsuite + + >>> from nltk.tag import CRFTagger + >>> ct = CRFTagger() # doctest: +SKIP + + >>> train_data = [[('University','Noun'), ('is','Verb'), ('a','Det'), ('good','Adj'), ('place','Noun')], + ... [('dog','Noun'),('eat','Verb'),('meat','Noun')]] + + >>> ct.train(train_data,'model.crf.tagger') # doctest: +SKIP + >>> ct.tag_sents([['dog','is','good'], ['Cat','eat','meat']]) # doctest: +SKIP + [[('dog', 'Noun'), ('is', 'Verb'), ('good', 'Adj')], [('Cat', 'Noun'), ('eat', 'Verb'), ('meat', 'Noun')]] + + >>> gold_sentences = [[('dog','Noun'),('is','Verb'),('good','Adj')] , [('Cat','Noun'),('eat','Verb'), ('meat','Noun')]] + >>> ct.accuracy(gold_sentences) # doctest: +SKIP + 1.0 + + Setting learned model file + >>> ct = CRFTagger() # doctest: +SKIP + >>> ct.set_model_file('model.crf.tagger') # doctest: +SKIP + >>> ct.accuracy(gold_sentences) # doctest: +SKIP + 1.0 + """ + + def __init__(self, feature_func=None, verbose=False, training_opt={}): + """ + Initialize the CRFSuite tagger + + :param feature_func: The function that extracts features for each token of a sentence. This function should take + 2 parameters: tokens and index which extract features at index position from tokens list. See the build in + _get_features function for more detail. + :param verbose: output the debugging messages during training. + :type verbose: boolean + :param training_opt: python-crfsuite training options + :type training_opt: dictionary + + Set of possible training options (using LBFGS training algorithm). + :'feature.minfreq': The minimum frequency of features. + :'feature.possible_states': Force to generate possible state features. + :'feature.possible_transitions': Force to generate possible transition features. + :'c1': Coefficient for L1 regularization. + :'c2': Coefficient for L2 regularization. + :'max_iterations': The maximum number of iterations for L-BFGS optimization. + :'num_memories': The number of limited memories for approximating the inverse hessian matrix. + :'epsilon': Epsilon for testing the convergence of the objective. + :'period': The duration of iterations to test the stopping criterion. + :'delta': The threshold for the stopping criterion; an L-BFGS iteration stops when the + improvement of the log likelihood over the last ${period} iterations is no greater than this threshold. + :'linesearch': The line search algorithm used in L-BFGS updates: + + - 'MoreThuente': More and Thuente's method, + - 'Backtracking': Backtracking method with regular Wolfe condition, + - 'StrongBacktracking': Backtracking method with strong Wolfe condition + :'max_linesearch': The maximum number of trials for the line search algorithm. + """ + + self._model_file = "" + self._tagger = pycrfsuite.Tagger() + + if feature_func is None: + self._feature_func = self._get_features + else: + self._feature_func = feature_func + + self._verbose = verbose + self._training_options = training_opt + self._pattern = re.compile(r"\d") + + def set_model_file(self, model_file): + self._model_file = model_file + self._tagger.open(self._model_file) + + def _get_features(self, tokens, idx): + """ + Extract basic features about this word including + - Current word + - is it capitalized? + - Does it have punctuation? + - Does it have a number? + - Suffixes up to length 3 + + Note that : we might include feature over previous word, next word etc. + + :return: a list which contains the features + :rtype: list(str) + """ + token = tokens[idx] + + feature_list = [] + + if not token: + return feature_list + + # Capitalization + if token[0].isupper(): + feature_list.append("CAPITALIZATION") + + # Number + if re.search(self._pattern, token) is not None: + feature_list.append("HAS_NUM") + + # Punctuation + punc_cat = {"Pc", "Pd", "Ps", "Pe", "Pi", "Pf", "Po"} + if all(unicodedata.category(x) in punc_cat for x in token): + feature_list.append("PUNCTUATION") + + # Suffix up to length 3 + if len(token) > 1: + feature_list.append("SUF_" + token[-1:]) + if len(token) > 2: + feature_list.append("SUF_" + token[-2:]) + if len(token) > 3: + feature_list.append("SUF_" + token[-3:]) + + feature_list.append("WORD_" + token) + + return feature_list + + def tag_sents(self, sents): + """ + Tag a list of sentences. NB before using this function, user should specify the mode_file either by + + - Train a new model using ``train`` function + - Use the pre-trained model which is set via ``set_model_file`` function + + :params sentences: list of sentences needed to tag. + :type sentences: list(list(str)) + :return: list of tagged sentences. + :rtype: list(list(tuple(str,str))) + """ + if self._model_file == "": + raise Exception( + " No model file is found !! Please use train or set_model_file function" + ) + + # We need the list of sentences instead of the list generator for matching the input and output + result = [] + for tokens in sents: + features = [self._feature_func(tokens, i) for i in range(len(tokens))] + labels = self._tagger.tag(features) + + if len(labels) != len(tokens): + raise Exception(" Predicted Length Not Matched, Expect Errors !") + + tagged_sent = list(zip(tokens, labels)) + result.append(tagged_sent) + + return result + + def train(self, train_data, model_file): + """ + Train the CRF tagger using CRFSuite + :params train_data : is the list of annotated sentences. + :type train_data : list (list(tuple(str,str))) + :params model_file : the model will be saved to this file. + + """ + trainer = pycrfsuite.Trainer(verbose=self._verbose) + trainer.set_params(self._training_options) + + for sent in train_data: + tokens, labels = zip(*sent) + features = [self._feature_func(tokens, i) for i in range(len(tokens))] + trainer.append(features, labels) + + # Now train the model, the output should be model_file + trainer.train(model_file) + # Save the model file + self.set_model_file(model_file) + + def tag(self, tokens): + """ + Tag a sentence using Python CRFSuite Tagger. NB before using this function, user should specify the mode_file either by + + - Train a new model using ``train`` function + - Use the pre-trained model which is set via ``set_model_file`` function + + :params tokens: list of tokens needed to tag. + :type tokens: list(str) + :return: list of tagged tokens. + :rtype: list(tuple(str,str)) + """ + + return self.tag_sents([tokens])[0] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hmm.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hmm.py new file mode 100644 index 0000000..52dbf4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hmm.py @@ -0,0 +1,1329 @@ +# Natural Language Toolkit: Hidden Markov Model +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Trevor Cohn +# Philip Blunsom +# Tiago Tresoldi (fixes) +# Steven Bird (fixes) +# Joseph Frazee (fixes) +# Steven Xu (fixes) +# URL: +# For license information, see LICENSE.TXT + +""" +Hidden Markov Models (HMMs) largely used to assign the correct label sequence +to sequential data or assess the probability of a given label and data +sequence. These models are finite state machines characterised by a number of +states, transitions between these states, and output symbols emitted while in +each state. The HMM is an extension to the Markov chain, where each state +corresponds deterministically to a given event. In the HMM the observation is +a probabilistic function of the state. HMMs share the Markov chain's +assumption, being that the probability of transition from one state to another +only depends on the current state - i.e. the series of states that led to the +current state are not used. They are also time invariant. + +The HMM is a directed graph, with probability weighted edges (representing the +probability of a transition between the source and sink states) where each +vertex emits an output symbol when entered. The symbol (or observation) is +non-deterministically generated. For this reason, knowing that a sequence of +output observations was generated by a given HMM does not mean that the +corresponding sequence of states (and what the current state is) is known. +This is the 'hidden' in the hidden markov model. + +Formally, a HMM can be characterised by: + +- the output observation alphabet. This is the set of symbols which may be + observed as output of the system. +- the set of states. +- the transition probabilities *a_{ij} = P(s_t = j | s_{t-1} = i)*. These + represent the probability of transition to each state from a given state. +- the output probability matrix *b_i(k) = P(X_t = o_k | s_t = i)*. These + represent the probability of observing each symbol in a given state. +- the initial state distribution. This gives the probability of starting + in each state. + +To ground this discussion, take a common NLP application, part-of-speech (POS) +tagging. An HMM is desirable for this task as the highest probability tag +sequence can be calculated for a given sequence of word forms. This differs +from other tagging techniques which often tag each word individually, seeking +to optimise each individual tagging greedily without regard to the optimal +combination of tags for a larger unit, such as a sentence. The HMM does this +with the Viterbi algorithm, which efficiently computes the optimal path +through the graph given the sequence of words forms. + +In POS tagging the states usually have a 1:1 correspondence with the tag +alphabet - i.e. each state represents a single tag. The output observation +alphabet is the set of word forms (the lexicon), and the remaining three +parameters are derived by a training regime. With this information the +probability of a given sentence can be easily derived, by simply summing the +probability of each distinct path through the model. Similarly, the highest +probability tagging sequence can be derived with the Viterbi algorithm, +yielding a state sequence which can be mapped into a tag sequence. + +This discussion assumes that the HMM has been trained. This is probably the +most difficult task with the model, and requires either MLE estimates of the +parameters or unsupervised learning using the Baum-Welch algorithm, a variant +of EM. + +For more information, please consult the source code for this module, +which includes extensive demonstration code. +""" + +import itertools +import re + +try: + import numpy as np +except ImportError: + pass + +from nltk.metrics import accuracy +from nltk.probability import ( + ConditionalFreqDist, + ConditionalProbDist, + DictionaryConditionalProbDist, + DictionaryProbDist, + FreqDist, + LidstoneProbDist, + MLEProbDist, + MutableProbDist, + RandomProbDist, +) +from nltk.tag.api import TaggerI +from nltk.util import LazyMap, unique_list + +_TEXT = 0 # index of text in a tuple +_TAG = 1 # index of tag in a tuple + + +def _identity(labeled_symbols): + return labeled_symbols + + +class HiddenMarkovModelTagger(TaggerI): + """ + Hidden Markov model class, a generative model for labelling sequence data. + These models define the joint probability of a sequence of symbols and + their labels (state transitions) as the product of the starting state + probability, the probability of each state transition, and the probability + of each observation being generated from each state. This is described in + more detail in the module documentation. + + This implementation is based on the HMM description in Chapter 8, Huang, + Acero and Hon, Spoken Language Processing and includes an extension for + training shallow HMM parsers or specialized HMMs as in Molina et. + al, 2002. A specialized HMM modifies training data by applying a + specialization function to create a new training set that is more + appropriate for sequential tagging with an HMM. A typical use case is + chunking. + + :param symbols: the set of output symbols (alphabet) + :type symbols: seq of any + :param states: a set of states representing state space + :type states: seq of any + :param transitions: transition probabilities; Pr(s_i | s_j) is the + probability of transition from state i given the model is in + state_j + :type transitions: ConditionalProbDistI + :param outputs: output probabilities; Pr(o_k | s_i) is the probability + of emitting symbol k when entering state i + :type outputs: ConditionalProbDistI + :param priors: initial state distribution; Pr(s_i) is the probability + of starting in state i + :type priors: ProbDistI + :param transform: an optional function for transforming training + instances, defaults to the identity function. + :type transform: callable + """ + + def __init__( + self, symbols, states, transitions, outputs, priors, transform=_identity + ): + self._symbols = unique_list(symbols) + self._states = unique_list(states) + self._transitions = transitions + self._outputs = outputs + self._priors = priors + self._cache = None + self._transform = transform + + @classmethod + def _train( + cls, + labeled_sequence, + test_sequence=None, + unlabeled_sequence=None, + transform=_identity, + estimator=None, + **kwargs, + ): + + if estimator is None: + + def estimator(fd, bins): + return LidstoneProbDist(fd, 0.1, bins) + + labeled_sequence = LazyMap(transform, labeled_sequence) + symbols = unique_list(word for sent in labeled_sequence for word, tag in sent) + tag_set = unique_list(tag for sent in labeled_sequence for word, tag in sent) + + trainer = HiddenMarkovModelTrainer(tag_set, symbols) + hmm = trainer.train_supervised(labeled_sequence, estimator=estimator) + hmm = cls( + hmm._symbols, + hmm._states, + hmm._transitions, + hmm._outputs, + hmm._priors, + transform=transform, + ) + + if test_sequence: + hmm.test(test_sequence, verbose=kwargs.get("verbose", False)) + + if unlabeled_sequence: + max_iterations = kwargs.get("max_iterations", 5) + hmm = trainer.train_unsupervised( + unlabeled_sequence, model=hmm, max_iterations=max_iterations + ) + if test_sequence: + hmm.test(test_sequence, verbose=kwargs.get("verbose", False)) + + return hmm + + @classmethod + def train( + cls, labeled_sequence, test_sequence=None, unlabeled_sequence=None, **kwargs + ): + """ + Train a new HiddenMarkovModelTagger using the given labeled and + unlabeled training instances. Testing will be performed if test + instances are provided. + + :return: a hidden markov model tagger + :rtype: HiddenMarkovModelTagger + :param labeled_sequence: a sequence of labeled training instances, + i.e. a list of sentences represented as tuples + :type labeled_sequence: list(list) + :param test_sequence: a sequence of labeled test instances + :type test_sequence: list(list) + :param unlabeled_sequence: a sequence of unlabeled training instances, + i.e. a list of sentences represented as words + :type unlabeled_sequence: list(list) + :param transform: an optional function for transforming training + instances, defaults to the identity function, see ``transform()`` + :type transform: function + :param estimator: an optional function or class that maps a + condition's frequency distribution to its probability + distribution, defaults to a Lidstone distribution with gamma = 0.1 + :type estimator: class or function + :param verbose: boolean flag indicating whether training should be + verbose or include printed output + :type verbose: bool + :param max_iterations: number of Baum-Welch iterations to perform + :type max_iterations: int + """ + return cls._train(labeled_sequence, test_sequence, unlabeled_sequence, **kwargs) + + def probability(self, sequence): + """ + Returns the probability of the given symbol sequence. If the sequence + is labelled, then returns the joint probability of the symbol, state + sequence. Otherwise, uses the forward algorithm to find the + probability over all label sequences. + + :return: the probability of the sequence + :rtype: float + :param sequence: the sequence of symbols which must contain the TEXT + property, and optionally the TAG property + :type sequence: Token + """ + return 2 ** (self.log_probability(self._transform(sequence))) + + def log_probability(self, sequence): + """ + Returns the log-probability of the given symbol sequence. If the + sequence is labelled, then returns the joint log-probability of the + symbol, state sequence. Otherwise, uses the forward algorithm to find + the log-probability over all label sequences. + + :return: the log-probability of the sequence + :rtype: float + :param sequence: the sequence of symbols which must contain the TEXT + property, and optionally the TAG property + :type sequence: Token + """ + sequence = self._transform(sequence) + + T = len(sequence) + + if T > 0 and sequence[0][_TAG]: + last_state = sequence[0][_TAG] + p = self._priors.logprob(last_state) + self._output_logprob( + last_state, sequence[0][_TEXT] + ) + for t in range(1, T): + state = sequence[t][_TAG] + p += self._transitions[last_state].logprob( + state + ) + self._output_logprob(state, sequence[t][_TEXT]) + last_state = state + return p + else: + alpha = self._forward_probability(sequence) + p = logsumexp2(alpha[T - 1]) + return p + + def tag(self, unlabeled_sequence): + """ + Tags the sequence with the highest probability state sequence. This + uses the best_path method to find the Viterbi path. + + :return: a labelled sequence of symbols + :rtype: list + :param unlabeled_sequence: the sequence of unlabeled symbols + :type unlabeled_sequence: list + """ + unlabeled_sequence = self._transform(unlabeled_sequence) + return self._tag(unlabeled_sequence) + + def _tag(self, unlabeled_sequence): + path = self._best_path(unlabeled_sequence) + return list(zip(unlabeled_sequence, path)) + + def _output_logprob(self, state, symbol): + """ + :return: the log probability of the symbol being observed in the given + state + :rtype: float + """ + return self._outputs[state].logprob(symbol) + + def _create_cache(self): + """ + The cache is a tuple (P, O, X, S) where: + + - S maps symbols to integers. I.e., it is the inverse + mapping from self._symbols; for each symbol s in + self._symbols, the following is true:: + + self._symbols[S[s]] == s + + - O is the log output probabilities:: + + O[i,k] = log( P(token[t]=sym[k]|tag[t]=state[i]) ) + + - X is the log transition probabilities:: + + X[i,j] = log( P(tag[t]=state[j]|tag[t-1]=state[i]) ) + + - P is the log prior probabilities:: + + P[i] = log( P(tag[0]=state[i]) ) + """ + if not self._cache: + N = len(self._states) + M = len(self._symbols) + P = np.zeros(N, np.float32) + X = np.zeros((N, N), np.float32) + O = np.zeros((N, M), np.float32) + for i in range(N): + si = self._states[i] + P[i] = self._priors.logprob(si) + for j in range(N): + X[i, j] = self._transitions[si].logprob(self._states[j]) + for k in range(M): + O[i, k] = self._output_logprob(si, self._symbols[k]) + S = {} + for k in range(M): + S[self._symbols[k]] = k + self._cache = (P, O, X, S) + + def _update_cache(self, symbols): + # add new symbols to the symbol table and repopulate the output + # probabilities and symbol table mapping + if symbols: + self._create_cache() + P, O, X, S = self._cache + for symbol in symbols: + if symbol not in self._symbols: + self._cache = None + self._symbols.append(symbol) + # don't bother with the work if there aren't any new symbols + if not self._cache: + N = len(self._states) + M = len(self._symbols) + Q = O.shape[1] + # add new columns to the output probability table without + # destroying the old probabilities + O = np.hstack([O, np.zeros((N, M - Q), np.float32)]) + for i in range(N): + si = self._states[i] + # only calculate probabilities for new symbols + for k in range(Q, M): + O[i, k] = self._output_logprob(si, self._symbols[k]) + # only create symbol mappings for new symbols + for k in range(Q, M): + S[self._symbols[k]] = k + self._cache = (P, O, X, S) + + def reset_cache(self): + self._cache = None + + def best_path(self, unlabeled_sequence): + """ + Returns the state sequence of the optimal (most probable) path through + the HMM. Uses the Viterbi algorithm to calculate this part by dynamic + programming. + + :return: the state sequence + :rtype: sequence of any + :param unlabeled_sequence: the sequence of unlabeled symbols + :type unlabeled_sequence: list + """ + unlabeled_sequence = self._transform(unlabeled_sequence) + return self._best_path(unlabeled_sequence) + + def _best_path(self, unlabeled_sequence): + T = len(unlabeled_sequence) + N = len(self._states) + self._create_cache() + self._update_cache(unlabeled_sequence) + P, O, X, S = self._cache + + V = np.zeros((T, N), np.float32) + B = -np.ones((T, N), int) + + V[0] = P + O[:, S[unlabeled_sequence[0]]] + for t in range(1, T): + for j in range(N): + vs = V[t - 1, :] + X[:, j] + best = np.argmax(vs) + V[t, j] = vs[best] + O[j, S[unlabeled_sequence[t]]] + B[t, j] = best + + current = np.argmax(V[T - 1, :]) + sequence = [current] + for t in range(T - 1, 0, -1): + last = B[t, current] + sequence.append(last) + current = last + + sequence.reverse() + return list(map(self._states.__getitem__, sequence)) + + def best_path_simple(self, unlabeled_sequence): + """ + Returns the state sequence of the optimal (most probable) path through + the HMM. Uses the Viterbi algorithm to calculate this part by dynamic + programming. This uses a simple, direct method, and is included for + teaching purposes. + + :return: the state sequence + :rtype: sequence of any + :param unlabeled_sequence: the sequence of unlabeled symbols + :type unlabeled_sequence: list + """ + unlabeled_sequence = self._transform(unlabeled_sequence) + return self._best_path_simple(unlabeled_sequence) + + def _best_path_simple(self, unlabeled_sequence): + T = len(unlabeled_sequence) + N = len(self._states) + V = np.zeros((T, N), np.float64) + B = {} + + # find the starting log probabilities for each state + symbol = unlabeled_sequence[0] + for i, state in enumerate(self._states): + V[0, i] = self._priors.logprob(state) + self._output_logprob(state, symbol) + B[0, state] = None + + # find the maximum log probabilities for reaching each state at time t + for t in range(1, T): + symbol = unlabeled_sequence[t] + for j in range(N): + sj = self._states[j] + best = None + for i in range(N): + si = self._states[i] + va = V[t - 1, i] + self._transitions[si].logprob(sj) + if not best or va > best[0]: + best = (va, si) + V[t, j] = best[0] + self._output_logprob(sj, symbol) + B[t, sj] = best[1] + + # find the highest probability final state + best = None + for i in range(N): + val = V[T - 1, i] + if not best or val > best[0]: + best = (val, self._states[i]) + + # traverse the back-pointers B to find the state sequence + current = best[1] + sequence = [current] + for t in range(T - 1, 0, -1): + last = B[t, current] + sequence.append(last) + current = last + + sequence.reverse() + return sequence + + def random_sample(self, rng, length): + """ + Randomly sample the HMM to generate a sentence of a given length. This + samples the prior distribution then the observation distribution and + transition distribution for each subsequent observation and state. + This will mostly generate unintelligible garbage, but can provide some + amusement. + + :return: the randomly created state/observation sequence, + generated according to the HMM's probability + distributions. The SUBTOKENS have TEXT and TAG + properties containing the observation and state + respectively. + :rtype: list + :param rng: random number generator + :type rng: Random (or any object with a random() method) + :param length: desired output length + :type length: int + """ + + # sample the starting state and symbol prob dists + tokens = [] + state = self._sample_probdist(self._priors, rng.random(), self._states) + symbol = self._sample_probdist( + self._outputs[state], rng.random(), self._symbols + ) + tokens.append((symbol, state)) + + for i in range(1, length): + # sample the state transition and symbol prob dists + state = self._sample_probdist( + self._transitions[state], rng.random(), self._states + ) + symbol = self._sample_probdist( + self._outputs[state], rng.random(), self._symbols + ) + tokens.append((symbol, state)) + + return tokens + + def _sample_probdist(self, probdist, p, samples): + cum_p = 0 + for sample in samples: + add_p = probdist.prob(sample) + if cum_p <= p <= cum_p + add_p: + return sample + cum_p += add_p + raise Exception("Invalid probability distribution - " "does not sum to one") + + def entropy(self, unlabeled_sequence): + """ + Returns the entropy over labellings of the given sequence. This is + given by:: + + H(O) = - sum_S Pr(S | O) log Pr(S | O) + + where the summation ranges over all state sequences, S. Let + *Z = Pr(O) = sum_S Pr(S, O)}* where the summation ranges over all state + sequences and O is the observation sequence. As such the entropy can + be re-expressed as:: + + H = - sum_S Pr(S | O) log [ Pr(S, O) / Z ] + = log Z - sum_S Pr(S | O) log Pr(S, 0) + = log Z - sum_S Pr(S | O) [ log Pr(S_0) + sum_t Pr(S_t | S_{t-1}) + sum_t Pr(O_t | S_t) ] + + The order of summation for the log terms can be flipped, allowing + dynamic programming to be used to calculate the entropy. Specifically, + we use the forward and backward probabilities (alpha, beta) giving:: + + H = log Z - sum_s0 alpha_0(s0) beta_0(s0) / Z * log Pr(s0) + + sum_t,si,sj alpha_t(si) Pr(sj | si) Pr(O_t+1 | sj) beta_t(sj) / Z * log Pr(sj | si) + + sum_t,st alpha_t(st) beta_t(st) / Z * log Pr(O_t | st) + + This simply uses alpha and beta to find the probabilities of partial + sequences, constrained to include the given state(s) at some point in + time. + """ + unlabeled_sequence = self._transform(unlabeled_sequence) + + T = len(unlabeled_sequence) + N = len(self._states) + + alpha = self._forward_probability(unlabeled_sequence) + beta = self._backward_probability(unlabeled_sequence) + normalisation = logsumexp2(alpha[T - 1]) + + entropy = normalisation + + # starting state, t = 0 + for i, state in enumerate(self._states): + p = 2 ** (alpha[0, i] + beta[0, i] - normalisation) + entropy -= p * self._priors.logprob(state) + # print('p(s_0 = %s) =' % state, p) + + # state transitions + for t0 in range(T - 1): + t1 = t0 + 1 + for i0, s0 in enumerate(self._states): + for i1, s1 in enumerate(self._states): + p = 2 ** ( + alpha[t0, i0] + + self._transitions[s0].logprob(s1) + + self._outputs[s1].logprob(unlabeled_sequence[t1][_TEXT]) + + beta[t1, i1] + - normalisation + ) + entropy -= p * self._transitions[s0].logprob(s1) + # print('p(s_%d = %s, s_%d = %s) =' % (t0, s0, t1, s1), p) + + # symbol emissions + for t in range(T): + for i, state in enumerate(self._states): + p = 2 ** (alpha[t, i] + beta[t, i] - normalisation) + entropy -= p * self._outputs[state].logprob( + unlabeled_sequence[t][_TEXT] + ) + # print('p(s_%d = %s) =' % (t, state), p) + + return entropy + + def point_entropy(self, unlabeled_sequence): + """ + Returns the pointwise entropy over the possible states at each + position in the chain, given the observation sequence. + """ + unlabeled_sequence = self._transform(unlabeled_sequence) + + T = len(unlabeled_sequence) + N = len(self._states) + + alpha = self._forward_probability(unlabeled_sequence) + beta = self._backward_probability(unlabeled_sequence) + normalisation = logsumexp2(alpha[T - 1]) + + entropies = np.zeros(T, np.float64) + probs = np.zeros(N, np.float64) + for t in range(T): + for s in range(N): + probs[s] = alpha[t, s] + beta[t, s] - normalisation + + for s in range(N): + entropies[t] -= 2 ** (probs[s]) * probs[s] + + return entropies + + def _exhaustive_entropy(self, unlabeled_sequence): + unlabeled_sequence = self._transform(unlabeled_sequence) + + T = len(unlabeled_sequence) + N = len(self._states) + + labellings = [[state] for state in self._states] + for t in range(T - 1): + current = labellings + labellings = [] + for labelling in current: + for state in self._states: + labellings.append(labelling + [state]) + + log_probs = [] + for labelling in labellings: + labeled_sequence = unlabeled_sequence[:] + for t, label in enumerate(labelling): + labeled_sequence[t] = (labeled_sequence[t][_TEXT], label) + lp = self.log_probability(labeled_sequence) + log_probs.append(lp) + normalisation = _log_add(*log_probs) + + entropy = 0 + for lp in log_probs: + lp -= normalisation + entropy -= 2 ** (lp) * lp + + return entropy + + def _exhaustive_point_entropy(self, unlabeled_sequence): + unlabeled_sequence = self._transform(unlabeled_sequence) + + T = len(unlabeled_sequence) + N = len(self._states) + + labellings = [[state] for state in self._states] + for t in range(T - 1): + current = labellings + labellings = [] + for labelling in current: + for state in self._states: + labellings.append(labelling + [state]) + + log_probs = [] + for labelling in labellings: + labelled_sequence = unlabeled_sequence[:] + for t, label in enumerate(labelling): + labelled_sequence[t] = (labelled_sequence[t][_TEXT], label) + lp = self.log_probability(labelled_sequence) + log_probs.append(lp) + + normalisation = _log_add(*log_probs) + + probabilities = _ninf_array((T, N)) + + for labelling, lp in zip(labellings, log_probs): + lp -= normalisation + for t, label in enumerate(labelling): + index = self._states.index(label) + probabilities[t, index] = _log_add(probabilities[t, index], lp) + + entropies = np.zeros(T, np.float64) + for t in range(T): + for s in range(N): + entropies[t] -= 2 ** (probabilities[t, s]) * probabilities[t, s] + + return entropies + + def _transitions_matrix(self): + """Return a matrix of transition log probabilities.""" + trans_iter = ( + self._transitions[sj].logprob(si) + for sj in self._states + for si in self._states + ) + + transitions_logprob = np.fromiter(trans_iter, dtype=np.float64) + N = len(self._states) + return transitions_logprob.reshape((N, N)).T + + def _outputs_vector(self, symbol): + """ + Return a vector with log probabilities of emitting a symbol + when entering states. + """ + out_iter = (self._output_logprob(sj, symbol) for sj in self._states) + return np.fromiter(out_iter, dtype=np.float64) + + def _forward_probability(self, unlabeled_sequence): + """ + Return the forward probability matrix, a T by N array of + log-probabilities, where T is the length of the sequence and N is the + number of states. Each entry (t, s) gives the probability of being in + state s at time t after observing the partial symbol sequence up to + and including t. + + :param unlabeled_sequence: the sequence of unlabeled symbols + :type unlabeled_sequence: list + :return: the forward log probability matrix + :rtype: array + """ + T = len(unlabeled_sequence) + N = len(self._states) + alpha = _ninf_array((T, N)) + + transitions_logprob = self._transitions_matrix() + + # Initialization + symbol = unlabeled_sequence[0][_TEXT] + for i, state in enumerate(self._states): + alpha[0, i] = self._priors.logprob(state) + self._output_logprob( + state, symbol + ) + + # Induction + for t in range(1, T): + symbol = unlabeled_sequence[t][_TEXT] + output_logprob = self._outputs_vector(symbol) + + for i in range(N): + summand = alpha[t - 1] + transitions_logprob[i] + alpha[t, i] = logsumexp2(summand) + output_logprob[i] + + return alpha + + def _backward_probability(self, unlabeled_sequence): + """ + Return the backward probability matrix, a T by N array of + log-probabilities, where T is the length of the sequence and N is the + number of states. Each entry (t, s) gives the probability of being in + state s at time t after observing the partial symbol sequence from t + .. T. + + :return: the backward log probability matrix + :rtype: array + :param unlabeled_sequence: the sequence of unlabeled symbols + :type unlabeled_sequence: list + """ + T = len(unlabeled_sequence) + N = len(self._states) + beta = _ninf_array((T, N)) + + transitions_logprob = self._transitions_matrix().T + + # initialise the backward values; + # "1" is an arbitrarily chosen value from Rabiner tutorial + beta[T - 1, :] = np.log2(1) + + # inductively calculate remaining backward values + for t in range(T - 2, -1, -1): + symbol = unlabeled_sequence[t + 1][_TEXT] + outputs = self._outputs_vector(symbol) + + for i in range(N): + summand = transitions_logprob[i] + beta[t + 1] + outputs + beta[t, i] = logsumexp2(summand) + + return beta + + def test(self, test_sequence, verbose=False, **kwargs): + """ + Tests the HiddenMarkovModelTagger instance. + + :param test_sequence: a sequence of labeled test instances + :type test_sequence: list(list) + :param verbose: boolean flag indicating whether training should be + verbose or include printed output + :type verbose: bool + """ + + def words(sent): + return [word for (word, tag) in sent] + + def tags(sent): + return [tag for (word, tag) in sent] + + def flatten(seq): + return list(itertools.chain(*seq)) + + test_sequence = self._transform(test_sequence) + predicted_sequence = list(map(self._tag, map(words, test_sequence))) + + if verbose: + for test_sent, predicted_sent in zip(test_sequence, predicted_sequence): + print( + "Test:", + " ".join(f"{token}/{tag}" for (token, tag) in test_sent), + ) + print() + print("Untagged:", " ".join("%s" % token for (token, tag) in test_sent)) + print() + print( + "HMM-tagged:", + " ".join(f"{token}/{tag}" for (token, tag) in predicted_sent), + ) + print() + print( + "Entropy:", + self.entropy([(token, None) for (token, tag) in predicted_sent]), + ) + print() + print("-" * 60) + + test_tags = flatten(map(tags, test_sequence)) + predicted_tags = flatten(map(tags, predicted_sequence)) + + acc = accuracy(test_tags, predicted_tags) + count = sum(len(sent) for sent in test_sequence) + print("accuracy over %d tokens: %.2f" % (count, acc * 100)) + + def __repr__(self): + return "" % ( + len(self._states), + len(self._symbols), + ) + + +class HiddenMarkovModelTrainer: + """ + Algorithms for learning HMM parameters from training data. These include + both supervised learning (MLE) and unsupervised learning (Baum-Welch). + + Creates an HMM trainer to induce an HMM with the given states and + output symbol alphabet. A supervised and unsupervised training + method may be used. If either of the states or symbols are not given, + these may be derived from supervised training. + + :param states: the set of state labels + :type states: sequence of any + :param symbols: the set of observation symbols + :type symbols: sequence of any + """ + + def __init__(self, states=None, symbols=None): + self._states = states if states else [] + self._symbols = symbols if symbols else [] + + def train(self, labeled_sequences=None, unlabeled_sequences=None, **kwargs): + """ + Trains the HMM using both (or either of) supervised and unsupervised + techniques. + + :return: the trained model + :rtype: HiddenMarkovModelTagger + :param labelled_sequences: the supervised training data, a set of + labelled sequences of observations + ex: [ (word_1, tag_1),...,(word_n,tag_n) ] + :type labelled_sequences: list + :param unlabeled_sequences: the unsupervised training data, a set of + sequences of observations + ex: [ word_1, ..., word_n ] + :type unlabeled_sequences: list + :param kwargs: additional arguments to pass to the training methods + """ + assert labeled_sequences or unlabeled_sequences + model = None + if labeled_sequences: + model = self.train_supervised(labeled_sequences, **kwargs) + if unlabeled_sequences: + if model: + kwargs["model"] = model + model = self.train_unsupervised(unlabeled_sequences, **kwargs) + return model + + def _baum_welch_step(self, sequence, model, symbol_to_number): + + N = len(model._states) + M = len(model._symbols) + T = len(sequence) + + # compute forward and backward probabilities + alpha = model._forward_probability(sequence) + beta = model._backward_probability(sequence) + + # find the log probability of the sequence + lpk = logsumexp2(alpha[T - 1]) + + A_numer = _ninf_array((N, N)) + B_numer = _ninf_array((N, M)) + A_denom = _ninf_array(N) + B_denom = _ninf_array(N) + + transitions_logprob = model._transitions_matrix().T + + for t in range(T): + symbol = sequence[t][_TEXT] # not found? FIXME + next_symbol = None + if t < T - 1: + next_symbol = sequence[t + 1][_TEXT] # not found? FIXME + xi = symbol_to_number[symbol] + + next_outputs_logprob = model._outputs_vector(next_symbol) + alpha_plus_beta = alpha[t] + beta[t] + + if t < T - 1: + numer_add = ( + transitions_logprob + + next_outputs_logprob + + beta[t + 1] + + alpha[t].reshape(N, 1) + ) + A_numer = np.logaddexp2(A_numer, numer_add) + A_denom = np.logaddexp2(A_denom, alpha_plus_beta) + else: + B_denom = np.logaddexp2(A_denom, alpha_plus_beta) + + B_numer[:, xi] = np.logaddexp2(B_numer[:, xi], alpha_plus_beta) + + return lpk, A_numer, A_denom, B_numer, B_denom + + def train_unsupervised(self, unlabeled_sequences, update_outputs=True, **kwargs): + """ + Trains the HMM using the Baum-Welch algorithm to maximise the + probability of the data sequence. This is a variant of the EM + algorithm, and is unsupervised in that it doesn't need the state + sequences for the symbols. The code is based on 'A Tutorial on Hidden + Markov Models and Selected Applications in Speech Recognition', + Lawrence Rabiner, IEEE, 1989. + + :return: the trained model + :rtype: HiddenMarkovModelTagger + :param unlabeled_sequences: the training data, a set of + sequences of observations + :type unlabeled_sequences: list + + kwargs may include following parameters: + + :param model: a HiddenMarkovModelTagger instance used to begin + the Baum-Welch algorithm + :param max_iterations: the maximum number of EM iterations + :param convergence_logprob: the maximum change in log probability to + allow convergence + """ + + # create a uniform HMM, which will be iteratively refined, unless + # given an existing model + model = kwargs.get("model") + if not model: + priors = RandomProbDist(self._states) + transitions = DictionaryConditionalProbDist( + {state: RandomProbDist(self._states) for state in self._states} + ) + outputs = DictionaryConditionalProbDist( + {state: RandomProbDist(self._symbols) for state in self._states} + ) + model = HiddenMarkovModelTagger( + self._symbols, self._states, transitions, outputs, priors + ) + + self._states = model._states + self._symbols = model._symbols + + N = len(self._states) + M = len(self._symbols) + symbol_numbers = {sym: i for i, sym in enumerate(self._symbols)} + + # update model prob dists so that they can be modified + # model._priors = MutableProbDist(model._priors, self._states) + + model._transitions = DictionaryConditionalProbDist( + { + s: MutableProbDist(model._transitions[s], self._states) + for s in self._states + } + ) + + if update_outputs: + model._outputs = DictionaryConditionalProbDist( + { + s: MutableProbDist(model._outputs[s], self._symbols) + for s in self._states + } + ) + + model.reset_cache() + + # iterate until convergence + converged = False + last_logprob = None + iteration = 0 + max_iterations = kwargs.get("max_iterations", 1000) + epsilon = kwargs.get("convergence_logprob", 1e-6) + + while not converged and iteration < max_iterations: + A_numer = _ninf_array((N, N)) + B_numer = _ninf_array((N, M)) + A_denom = _ninf_array(N) + B_denom = _ninf_array(N) + + logprob = 0 + for sequence in unlabeled_sequences: + sequence = list(sequence) + if not sequence: + continue + + ( + lpk, + seq_A_numer, + seq_A_denom, + seq_B_numer, + seq_B_denom, + ) = self._baum_welch_step(sequence, model, symbol_numbers) + + # add these sums to the global A and B values + for i in range(N): + A_numer[i] = np.logaddexp2(A_numer[i], seq_A_numer[i] - lpk) + B_numer[i] = np.logaddexp2(B_numer[i], seq_B_numer[i] - lpk) + + A_denom = np.logaddexp2(A_denom, seq_A_denom - lpk) + B_denom = np.logaddexp2(B_denom, seq_B_denom - lpk) + + logprob += lpk + + # use the calculated values to update the transition and output + # probability values + for i in range(N): + logprob_Ai = A_numer[i] - A_denom[i] + logprob_Bi = B_numer[i] - B_denom[i] + + # We should normalize all probabilities (see p.391 Huang et al) + # Let sum(P) be K. + # We can divide each Pi by K to make sum(P) == 1. + # Pi' = Pi/K + # log2(Pi') = log2(Pi) - log2(K) + logprob_Ai -= logsumexp2(logprob_Ai) + logprob_Bi -= logsumexp2(logprob_Bi) + + # update output and transition probabilities + si = self._states[i] + + for j in range(N): + sj = self._states[j] + model._transitions[si].update(sj, logprob_Ai[j]) + + if update_outputs: + for k in range(M): + ok = self._symbols[k] + model._outputs[si].update(ok, logprob_Bi[k]) + + # Rabiner says the priors don't need to be updated. I don't + # believe him. FIXME + + # test for convergence + if iteration > 0 and abs(logprob - last_logprob) < epsilon: + converged = True + + print("iteration", iteration, "logprob", logprob) + iteration += 1 + last_logprob = logprob + + return model + + def train_supervised(self, labelled_sequences, estimator=None): + """ + Supervised training maximising the joint probability of the symbol and + state sequences. This is done via collecting frequencies of + transitions between states, symbol observations while within each + state and which states start a sentence. These frequency distributions + are then normalised into probability estimates, which can be + smoothed if desired. + + :return: the trained model + :rtype: HiddenMarkovModelTagger + :param labelled_sequences: the training data, a set of + labelled sequences of observations + :type labelled_sequences: list + :param estimator: a function taking + a FreqDist and a number of bins and returning a CProbDistI; + otherwise a MLE estimate is used + """ + + # default to the MLE estimate + if estimator is None: + estimator = lambda fdist, bins: MLEProbDist(fdist) + + # count occurrences of starting states, transitions out of each state + # and output symbols observed in each state + known_symbols = set(self._symbols) + known_states = set(self._states) + + starting = FreqDist() + transitions = ConditionalFreqDist() + outputs = ConditionalFreqDist() + for sequence in labelled_sequences: + lasts = None + for token in sequence: + state = token[_TAG] + symbol = token[_TEXT] + if lasts is None: + starting[state] += 1 + else: + transitions[lasts][state] += 1 + outputs[state][symbol] += 1 + lasts = state + + # update the state and symbol lists + if state not in known_states: + self._states.append(state) + known_states.add(state) + + if symbol not in known_symbols: + self._symbols.append(symbol) + known_symbols.add(symbol) + + # create probability distributions (with smoothing) + N = len(self._states) + pi = estimator(starting, N) + A = ConditionalProbDist(transitions, estimator, N) + B = ConditionalProbDist(outputs, estimator, len(self._symbols)) + + return HiddenMarkovModelTagger(self._symbols, self._states, A, B, pi) + + +def _ninf_array(shape): + res = np.empty(shape, np.float64) + res.fill(-np.inf) + return res + + +def logsumexp2(arr): + max_ = arr.max() + return np.log2(np.sum(2 ** (arr - max_))) + max_ + + +def _log_add(*values): + """ + Adds the logged values, returning the logarithm of the addition. + """ + x = max(values) + if x > -np.inf: + sum_diffs = 0 + for value in values: + sum_diffs += 2 ** (value - x) + return x + np.log2(sum_diffs) + else: + return x + + +def _create_hmm_tagger(states, symbols, A, B, pi): + def pd(values, samples): + d = dict(zip(samples, values)) + return DictionaryProbDist(d) + + def cpd(array, conditions, samples): + d = {} + for values, condition in zip(array, conditions): + d[condition] = pd(values, samples) + return DictionaryConditionalProbDist(d) + + A = cpd(A, states, states) + B = cpd(B, states, symbols) + pi = pd(pi, states) + return HiddenMarkovModelTagger( + symbols=symbols, states=states, transitions=A, outputs=B, priors=pi + ) + + +def _market_hmm_example(): + """ + Return an example HMM (described at page 381, Huang et al) + """ + states = ["bull", "bear", "static"] + symbols = ["up", "down", "unchanged"] + A = np.array([[0.6, 0.2, 0.2], [0.5, 0.3, 0.2], [0.4, 0.1, 0.5]], np.float64) + B = np.array([[0.7, 0.1, 0.2], [0.1, 0.6, 0.3], [0.3, 0.3, 0.4]], np.float64) + pi = np.array([0.5, 0.2, 0.3], np.float64) + + model = _create_hmm_tagger(states, symbols, A, B, pi) + return model, states, symbols + + +def demo(): + # demonstrates HMM probability calculation + + print() + print("HMM probability calculation demo") + print() + + model, states, symbols = _market_hmm_example() + + print("Testing", model) + + for test in [ + ["up", "up"], + ["up", "down", "up"], + ["down"] * 5, + ["unchanged"] * 5 + ["up"], + ]: + + sequence = [(t, None) for t in test] + + print("Testing with state sequence", test) + print("probability =", model.probability(sequence)) + print("tagging = ", model.tag([word for (word, tag) in sequence])) + print("p(tagged) = ", model.probability(sequence)) + print("H = ", model.entropy(sequence)) + print("H_exh = ", model._exhaustive_entropy(sequence)) + print("H(point) = ", model.point_entropy(sequence)) + print("H_exh(point)=", model._exhaustive_point_entropy(sequence)) + print() + + +def load_pos(num_sents): + from nltk.corpus import brown + + sentences = brown.tagged_sents(categories="news")[:num_sents] + + tag_re = re.compile(r"[*]|--|[^+*-]+") + tag_set = set() + symbols = set() + + cleaned_sentences = [] + for sentence in sentences: + for i in range(len(sentence)): + word, tag = sentence[i] + word = word.lower() # normalize + symbols.add(word) # log this word + # Clean up the tag. + tag = tag_re.match(tag).group() + tag_set.add(tag) + sentence[i] = (word, tag) # store cleaned-up tagged token + cleaned_sentences += [sentence] + + return cleaned_sentences, list(tag_set), list(symbols) + + +def demo_pos(): + # demonstrates POS tagging using supervised training + + print() + print("HMM POS tagging demo") + print() + + print("Training HMM...") + labelled_sequences, tag_set, symbols = load_pos(20000) + trainer = HiddenMarkovModelTrainer(tag_set, symbols) + hmm = trainer.train_supervised( + labelled_sequences[10:], + estimator=lambda fd, bins: LidstoneProbDist(fd, 0.1, bins), + ) + + print("Testing...") + hmm.test(labelled_sequences[:10], verbose=True) + + +def _untag(sentences): + unlabeled = [] + for sentence in sentences: + unlabeled.append([(token[_TEXT], None) for token in sentence]) + return unlabeled + + +def demo_pos_bw( + test=10, supervised=20, unsupervised=10, verbose=True, max_iterations=5 +): + # demonstrates the Baum-Welch algorithm in POS tagging + + print() + print("Baum-Welch demo for POS tagging") + print() + + print("Training HMM (supervised, %d sentences)..." % supervised) + + sentences, tag_set, symbols = load_pos(test + supervised + unsupervised) + + symbols = set() + for sentence in sentences: + for token in sentence: + symbols.add(token[_TEXT]) + + trainer = HiddenMarkovModelTrainer(tag_set, list(symbols)) + hmm = trainer.train_supervised( + sentences[test : test + supervised], + estimator=lambda fd, bins: LidstoneProbDist(fd, 0.1, bins), + ) + + hmm.test(sentences[:test], verbose=verbose) + + print("Training (unsupervised, %d sentences)..." % unsupervised) + # it's rather slow - so only use 10 samples by default + unlabeled = _untag(sentences[test + supervised :]) + hmm = trainer.train_unsupervised( + unlabeled, model=hmm, max_iterations=max_iterations + ) + hmm.test(sentences[:test], verbose=verbose) + + +def demo_bw(): + # demo Baum Welch by generating some sequences and then performing + # unsupervised training on them + + print() + print("Baum-Welch demo for market example") + print() + + model, states, symbols = _market_hmm_example() + + # generate some random sequences + training = [] + import random + + rng = random.Random() + rng.seed(0) + for i in range(10): + item = model.random_sample(rng, 5) + training.append([(i[0], None) for i in item]) + + # train on those examples, starting with the model that generated them + trainer = HiddenMarkovModelTrainer(states, symbols) + hmm = trainer.train_unsupervised(training, model=model, max_iterations=1000) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hunpos.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hunpos.py new file mode 100644 index 0000000..a0b84a1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/hunpos.py @@ -0,0 +1,142 @@ +# Natural Language Toolkit: Interface to the HunPos POS-tagger +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Peter Ljunglöf +# Dávid Márk Nemeskey (modifications) +# Attila Zséder (modifications) +# URL: +# For license information, see LICENSE.TXT + +""" +A module for interfacing with the HunPos open-source POS-tagger. +""" + +import os +from subprocess import PIPE, Popen + +from nltk.internals import find_binary, find_file +from nltk.tag.api import TaggerI + +_hunpos_url = "https://code.google.com/p/hunpos/" + +_hunpos_charset = "ISO-8859-1" +"""The default encoding used by hunpos: ISO-8859-1.""" + + +class HunposTagger(TaggerI): + """ + A class for pos tagging with HunPos. The input is the paths to: + - a model trained on training data + - (optionally) the path to the hunpos-tag binary + - (optionally) the encoding of the training data (default: ISO-8859-1) + + Check whether the required "hunpos-tag" binary is available: + + >>> from nltk.test.setup_fixt import check_binary + >>> check_binary('hunpos-tag') + + Example: + >>> from nltk.tag import HunposTagger + >>> ht = HunposTagger('en_wsj.model') + >>> ht.tag('What is the airspeed of an unladen swallow ?'.split()) + [('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), ('airspeed', 'NN'), ('of', 'IN'), ('an', 'DT'), ('unladen', 'NN'), ('swallow', 'VB'), ('?', '.')] + >>> ht.close() + + This class communicates with the hunpos-tag binary via pipes. When the + tagger object is no longer needed, the close() method should be called to + free system resources. The class supports the context manager interface; if + used in a with statement, the close() method is invoked automatically: + + >>> with HunposTagger('en_wsj.model') as ht: + ... ht.tag('What is the airspeed of an unladen swallow ?'.split()) + ... + [('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), ('airspeed', 'NN'), ('of', 'IN'), ('an', 'DT'), ('unladen', 'NN'), ('swallow', 'VB'), ('?', '.')] + """ + + def __init__( + self, path_to_model, path_to_bin=None, encoding=_hunpos_charset, verbose=False + ): + """ + Starts the hunpos-tag executable and establishes a connection with it. + + :param path_to_model: The model file. + :param path_to_bin: The hunpos-tag binary. + :param encoding: The encoding used by the model. Unicode tokens + passed to the tag() and tag_sents() methods are converted to + this charset when they are sent to hunpos-tag. + The default is ISO-8859-1 (Latin-1). + + This parameter is ignored for str tokens, which are sent as-is. + The caller must ensure that tokens are encoded in the right charset. + """ + self._closed = True + hunpos_paths = [ + ".", + "/usr/bin", + "/usr/local/bin", + "/opt/local/bin", + "/Applications/bin", + "~/bin", + "~/Applications/bin", + ] + hunpos_paths = list(map(os.path.expanduser, hunpos_paths)) + + self._hunpos_bin = find_binary( + "hunpos-tag", + path_to_bin, + env_vars=("HUNPOS_TAGGER",), + searchpath=hunpos_paths, + url=_hunpos_url, + verbose=verbose, + ) + + self._hunpos_model = find_file( + path_to_model, env_vars=("HUNPOS_TAGGER",), verbose=verbose + ) + self._encoding = encoding + self._hunpos = Popen( + [self._hunpos_bin, self._hunpos_model], + shell=False, + stdin=PIPE, + stdout=PIPE, + stderr=PIPE, + ) + self._closed = False + + def __del__(self): + self.close() + + def close(self): + """Closes the pipe to the hunpos executable.""" + if not self._closed: + self._hunpos.communicate() + self._closed = True + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def tag(self, tokens): + """Tags a single sentence: a list of words. + The tokens should not contain any newline characters. + """ + for token in tokens: + assert "\n" not in token, "Tokens should not contain newlines" + if isinstance(token, str): + token = token.encode(self._encoding) + self._hunpos.stdin.write(token + b"\n") + # We write a final empty line to tell hunpos that the sentence is finished: + self._hunpos.stdin.write(b"\n") + self._hunpos.stdin.flush() + + tagged_tokens = [] + for token in tokens: + tagged = self._hunpos.stdout.readline().strip().split(b"\t") + tag = tagged[1] if len(tagged) > 1 else None + tagged_tokens.append((token, tag)) + # We have to read (and dismiss) the final empty line: + self._hunpos.stdout.readline() + + return tagged_tokens diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/mapping.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/mapping.py new file mode 100644 index 0000000..c09467b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/mapping.py @@ -0,0 +1,136 @@ +# Natural Language Toolkit: Tagset Mapping +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Nathan Schneider +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Interface for converting POS tags from various treebanks +to the universal tagset of Petrov, Das, & McDonald. + +The tagset consists of the following 12 coarse tags: + +VERB - verbs (all tenses and modes) +NOUN - nouns (common and proper) +PRON - pronouns +ADJ - adjectives +ADV - adverbs +ADP - adpositions (prepositions and postpositions) +CONJ - conjunctions +DET - determiners +NUM - cardinal numbers +PRT - particles or other function words +X - other: foreign words, typos, abbreviations +. - punctuation + +@see: https://arxiv.org/abs/1104.2086 and https://code.google.com/p/universal-pos-tags/ + +""" + +from collections import defaultdict +from os.path import join + +from nltk.data import load + +_UNIVERSAL_DATA = "taggers/universal_tagset" +_UNIVERSAL_TAGS = ( + "VERB", + "NOUN", + "PRON", + "ADJ", + "ADV", + "ADP", + "CONJ", + "DET", + "NUM", + "PRT", + "X", + ".", +) + +# _MAPPINGS = defaultdict(lambda: defaultdict(dict)) +# the mapping between tagset T1 and T2 returns UNK if applied to an unrecognized tag +_MAPPINGS = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: "UNK"))) + + +def _load_universal_map(fileid): + contents = load(join(_UNIVERSAL_DATA, fileid + ".map"), format="text") + + # When mapping to the Universal Tagset, + # map unknown inputs to 'X' not 'UNK' + _MAPPINGS[fileid]["universal"].default_factory = lambda: "X" + + for line in contents.splitlines(): + line = line.strip() + if line == "": + continue + fine, coarse = line.split("\t") + + assert coarse in _UNIVERSAL_TAGS, f"Unexpected coarse tag: {coarse}" + assert ( + fine not in _MAPPINGS[fileid]["universal"] + ), f"Multiple entries for original tag: {fine}" + + _MAPPINGS[fileid]["universal"][fine] = coarse + + +def tagset_mapping(source, target): + """ + Retrieve the mapping dictionary between tagsets. + + >>> tagset_mapping('ru-rnc', 'universal') == {'!': '.', 'A': 'ADJ', 'C': 'CONJ', 'AD': 'ADV',\ + 'NN': 'NOUN', 'VG': 'VERB', 'COMP': 'CONJ', 'NC': 'NUM', 'VP': 'VERB', 'P': 'ADP',\ + 'IJ': 'X', 'V': 'VERB', 'Z': 'X', 'VI': 'VERB', 'YES_NO_SENT': 'X', 'PTCL': 'PRT'} + True + """ + + if source not in _MAPPINGS or target not in _MAPPINGS[source]: + if target == "universal": + _load_universal_map(source) + # Added the new Russian National Corpus mappings because the + # Russian model for nltk.pos_tag() uses it. + _MAPPINGS["ru-rnc-new"]["universal"] = { + "A": "ADJ", + "A-PRO": "PRON", + "ADV": "ADV", + "ADV-PRO": "PRON", + "ANUM": "ADJ", + "CONJ": "CONJ", + "INTJ": "X", + "NONLEX": ".", + "NUM": "NUM", + "PARENTH": "PRT", + "PART": "PRT", + "PR": "ADP", + "PRAEDIC": "PRT", + "PRAEDIC-PRO": "PRON", + "S": "NOUN", + "S-PRO": "PRON", + "V": "VERB", + } + + return _MAPPINGS[source][target] + + +def map_tag(source, target, source_tag): + """ + Maps the tag from the source tagset to the target tagset. + + >>> map_tag('en-ptb', 'universal', 'VBZ') + 'VERB' + >>> map_tag('en-ptb', 'universal', 'VBP') + 'VERB' + >>> map_tag('en-ptb', 'universal', '``') + '.' + """ + + # we need a systematic approach to naming + if target == "universal": + if source == "wsj": + source = "en-ptb" + if source == "brown": + source = "en-brown" + + return tagset_mapping(source, target)[source_tag] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/perceptron.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/perceptron.py new file mode 100644 index 0000000..02ff086 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/perceptron.py @@ -0,0 +1,371 @@ +# This module is a port of the Textblob Averaged Perceptron Tagger +# Author: Matthew Honnibal , +# Long Duong (NLTK port) +# URL: +# +# Copyright 2013 Matthew Honnibal +# NLTK modifications Copyright 2015 The NLTK Project +# +# This module is provided under the terms of the MIT License. + +import logging +import pickle +import random +from collections import defaultdict + +from nltk import jsontags +from nltk.data import find, load +from nltk.tag.api import TaggerI + +try: + import numpy as np +except ImportError: + pass + +PICKLE = "averaged_perceptron_tagger.pickle" + + +@jsontags.register_tag +class AveragedPerceptron: + + """An averaged perceptron, as implemented by Matthew Honnibal. + + See more implementation details here: + https://explosion.ai/blog/part-of-speech-pos-tagger-in-python + """ + + json_tag = "nltk.tag.perceptron.AveragedPerceptron" + + def __init__(self, weights=None): + # Each feature gets its own weight vector, so weights is a dict-of-dicts + self.weights = weights if weights else {} + self.classes = set() + # The accumulated values, for the averaging. These will be keyed by + # feature/clas tuples + self._totals = defaultdict(int) + # The last time the feature was changed, for the averaging. Also + # keyed by feature/clas tuples + # (tstamps is short for timestamps) + self._tstamps = defaultdict(int) + # Number of instances seen + self.i = 0 + + def _softmax(self, scores): + s = np.fromiter(scores.values(), dtype=float) + exps = np.exp(s) + return exps / np.sum(exps) + + def predict(self, features, return_conf=False): + """Dot-product the features and current weights and return the best label.""" + scores = defaultdict(float) + for feat, value in features.items(): + if feat not in self.weights or value == 0: + continue + weights = self.weights[feat] + for label, weight in weights.items(): + scores[label] += value * weight + + # Do a secondary alphabetic sort, for stability + best_label = max(self.classes, key=lambda label: (scores[label], label)) + # compute the confidence + conf = max(self._softmax(scores)) if return_conf == True else None + + return best_label, conf + + def update(self, truth, guess, features): + """Update the feature weights.""" + + def upd_feat(c, f, w, v): + param = (f, c) + self._totals[param] += (self.i - self._tstamps[param]) * w + self._tstamps[param] = self.i + self.weights[f][c] = w + v + + self.i += 1 + if truth == guess: + return None + for f in features: + weights = self.weights.setdefault(f, {}) + upd_feat(truth, f, weights.get(truth, 0.0), 1.0) + upd_feat(guess, f, weights.get(guess, 0.0), -1.0) + + def average_weights(self): + """Average weights from all iterations.""" + for feat, weights in self.weights.items(): + new_feat_weights = {} + for clas, weight in weights.items(): + param = (feat, clas) + total = self._totals[param] + total += (self.i - self._tstamps[param]) * weight + averaged = round(total / self.i, 3) + if averaged: + new_feat_weights[clas] = averaged + self.weights[feat] = new_feat_weights + + def save(self, path): + """Save the pickled model weights.""" + with open(path, "wb") as fout: + return pickle.dump(dict(self.weights), fout) + + def load(self, path): + """Load the pickled model weights.""" + self.weights = load(path) + + def encode_json_obj(self): + return self.weights + + @classmethod + def decode_json_obj(cls, obj): + return cls(obj) + + +@jsontags.register_tag +class PerceptronTagger(TaggerI): + + """ + Greedy Averaged Perceptron tagger, as implemented by Matthew Honnibal. + See more implementation details here: + https://explosion.ai/blog/part-of-speech-pos-tagger-in-python + + >>> from nltk.tag.perceptron import PerceptronTagger + + Train the model + + >>> tagger = PerceptronTagger(load=False) + + >>> tagger.train([[('today','NN'),('is','VBZ'),('good','JJ'),('day','NN')], + ... [('yes','NNS'),('it','PRP'),('beautiful','JJ')]]) + + >>> tagger.tag(['today','is','a','beautiful','day']) + [('today', 'NN'), ('is', 'PRP'), ('a', 'PRP'), ('beautiful', 'JJ'), ('day', 'NN')] + + Use the pretrain model (the default constructor) + + >>> pretrain = PerceptronTagger() + + >>> pretrain.tag('The quick brown fox jumps over the lazy dog'.split()) + [('The', 'DT'), ('quick', 'JJ'), ('brown', 'NN'), ('fox', 'NN'), ('jumps', 'VBZ'), ('over', 'IN'), ('the', 'DT'), ('lazy', 'JJ'), ('dog', 'NN')] + + >>> pretrain.tag("The red cat".split()) + [('The', 'DT'), ('red', 'JJ'), ('cat', 'NN')] + """ + + json_tag = "nltk.tag.sequential.PerceptronTagger" + + START = ["-START-", "-START2-"] + END = ["-END-", "-END2-"] + + def __init__(self, load=True): + """ + :param load: Load the pickled model upon instantiation. + """ + self.model = AveragedPerceptron() + self.tagdict = {} + self.classes = set() + if load: + AP_MODEL_LOC = "file:" + str( + find("taggers/averaged_perceptron_tagger/" + PICKLE) + ) + self.load(AP_MODEL_LOC) + + def tag(self, tokens, return_conf=False, use_tagdict=True): + """ + Tag tokenized sentences. + :params tokens: list of word + :type tokens: list(str) + """ + prev, prev2 = self.START + output = [] + + context = self.START + [self.normalize(w) for w in tokens] + self.END + for i, word in enumerate(tokens): + tag, conf = ( + (self.tagdict.get(word), 1.0) if use_tagdict == True else (None, None) + ) + if not tag: + features = self._get_features(i, word, context, prev, prev2) + tag, conf = self.model.predict(features, return_conf) + output.append((word, tag, conf) if return_conf == True else (word, tag)) + + prev2 = prev + prev = tag + + return output + + def train(self, sentences, save_loc=None, nr_iter=5): + """Train a model from sentences, and save it at ``save_loc``. ``nr_iter`` + controls the number of Perceptron training iterations. + + :param sentences: A list or iterator of sentences, where each sentence + is a list of (words, tags) tuples. + :param save_loc: If not ``None``, saves a pickled model in this location. + :param nr_iter: Number of training iterations. + """ + # We'd like to allow ``sentences`` to be either a list or an iterator, + # the latter being especially important for a large training dataset. + # Because ``self._make_tagdict(sentences)`` runs regardless, we make + # it populate ``self._sentences`` (a list) with all the sentences. + # This saves the overheard of just iterating through ``sentences`` to + # get the list by ``sentences = list(sentences)``. + + self._sentences = list() # to be populated by self._make_tagdict... + self._make_tagdict(sentences) + self.model.classes = self.classes + for iter_ in range(nr_iter): + c = 0 + n = 0 + for sentence in self._sentences: + words, tags = zip(*sentence) + + prev, prev2 = self.START + context = self.START + [self.normalize(w) for w in words] + self.END + for i, word in enumerate(words): + guess = self.tagdict.get(word) + if not guess: + feats = self._get_features(i, word, context, prev, prev2) + guess, _ = self.model.predict(feats) + self.model.update(tags[i], guess, feats) + prev2 = prev + prev = guess + c += guess == tags[i] + n += 1 + random.shuffle(self._sentences) + logging.info(f"Iter {iter_}: {c}/{n}={_pc(c, n)}") + + # We don't need the training sentences anymore, and we don't want to + # waste space on them when we pickle the trained tagger. + self._sentences = None + + self.model.average_weights() + # Pickle as a binary file + if save_loc is not None: + with open(save_loc, "wb") as fout: + # changed protocol from -1 to 2 to make pickling Python 2 compatible + pickle.dump((self.model.weights, self.tagdict, self.classes), fout, 2) + + def load(self, loc): + """ + :param loc: Load a pickled model at location. + :type loc: str + """ + + self.model.weights, self.tagdict, self.classes = load(loc) + self.model.classes = self.classes + + def encode_json_obj(self): + return self.model.weights, self.tagdict, list(self.classes) + + @classmethod + def decode_json_obj(cls, obj): + tagger = cls(load=False) + tagger.model.weights, tagger.tagdict, tagger.classes = obj + tagger.classes = set(tagger.classes) + tagger.model.classes = tagger.classes + return tagger + + def normalize(self, word): + """ + Normalization used in pre-processing. + - All words are lower cased + - Groups of digits of length 4 are represented as !YEAR; + - Other digits are represented as !DIGITS + + :rtype: str + """ + if "-" in word and word[0] != "-": + return "!HYPHEN" + if word.isdigit() and len(word) == 4: + return "!YEAR" + if word and word[0].isdigit(): + return "!DIGITS" + return word.lower() + + def _get_features(self, i, word, context, prev, prev2): + """Map tokens into a feature representation, implemented as a + {hashable: int} dict. If the features change, a new model must be + trained. + """ + + def add(name, *args): + features[" ".join((name,) + tuple(args))] += 1 + + i += len(self.START) + features = defaultdict(int) + # It's useful to have a constant feature, which acts sort of like a prior + add("bias") + add("i suffix", word[-3:]) + add("i pref1", word[0] if word else "") + add("i-1 tag", prev) + add("i-2 tag", prev2) + add("i tag+i-2 tag", prev, prev2) + add("i word", context[i]) + add("i-1 tag+i word", prev, context[i]) + add("i-1 word", context[i - 1]) + add("i-1 suffix", context[i - 1][-3:]) + add("i-2 word", context[i - 2]) + add("i+1 word", context[i + 1]) + add("i+1 suffix", context[i + 1][-3:]) + add("i+2 word", context[i + 2]) + return features + + def _make_tagdict(self, sentences): + """ + Make a tag dictionary for single-tag words. + :param sentences: A list of list of (word, tag) tuples. + """ + counts = defaultdict(lambda: defaultdict(int)) + for sentence in sentences: + self._sentences.append(sentence) + for word, tag in sentence: + counts[word][tag] += 1 + self.classes.add(tag) + freq_thresh = 20 + ambiguity_thresh = 0.97 + for word, tag_freqs in counts.items(): + tag, mode = max(tag_freqs.items(), key=lambda item: item[1]) + n = sum(tag_freqs.values()) + # Don't add rare words to the tag dictionary + # Only add quite unambiguous words + if n >= freq_thresh and (mode / n) >= ambiguity_thresh: + self.tagdict[word] = tag + + +def _pc(n, d): + return (n / d) * 100 + + +def _load_data_conll_format(filename): + print("Read from file: ", filename) + with open(filename, "rb") as fin: + sentences = [] + sentence = [] + for line in fin.readlines(): + line = line.strip() + # print line + if len(line) == 0: + sentences.append(sentence) + sentence = [] + continue + tokens = line.split("\t") + word = tokens[1] + tag = tokens[4] + sentence.append((word, tag)) + return sentences + + +def _get_pretrain_model(): + # Train and test on English part of ConLL data (WSJ part of Penn Treebank) + # Train: section 2-11 + # Test : section 23 + tagger = PerceptronTagger() + training = _load_data_conll_format("english_ptb_train.conll") + testing = _load_data_conll_format("english_ptb_test.conll") + print("Size of training and testing (sentence)", len(training), len(testing)) + # Train and save the model + tagger.train(training, PICKLE) + print("Accuracy : ", tagger.accuracy(testing)) + + +if __name__ == "__main__": + # _get_pretrain_model() + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/senna.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/senna.py new file mode 100644 index 0000000..5b5c160 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/senna.py @@ -0,0 +1,134 @@ +# Natural Language Toolkit: Senna POS Tagger +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Rami Al-Rfou' +# URL: +# For license information, see LICENSE.TXT + +""" +Senna POS tagger, NER Tagger, Chunk Tagger + +The input is: + +- path to the directory that contains SENNA executables. If the path is incorrect, + SennaTagger will automatically search for executable file specified in SENNA environment variable +- (optionally) the encoding of the input data (default:utf-8) + +Note: Unit tests for this module can be found in test/unit/test_senna.py + +>>> from nltk.tag import SennaTagger +>>> tagger = SennaTagger('/usr/share/senna-v3.0') # doctest: +SKIP +>>> tagger.tag('What is the airspeed of an unladen swallow ?'.split()) # doctest: +SKIP +[('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), ('airspeed', 'NN'), +('of', 'IN'), ('an', 'DT'), ('unladen', 'NN'), ('swallow', 'NN'), ('?', '.')] + +>>> from nltk.tag import SennaChunkTagger +>>> chktagger = SennaChunkTagger('/usr/share/senna-v3.0') # doctest: +SKIP +>>> chktagger.tag('What is the airspeed of an unladen swallow ?'.split()) # doctest: +SKIP +[('What', 'B-NP'), ('is', 'B-VP'), ('the', 'B-NP'), ('airspeed', 'I-NP'), +('of', 'B-PP'), ('an', 'B-NP'), ('unladen', 'I-NP'), ('swallow', 'I-NP'), +('?', 'O')] + +>>> from nltk.tag import SennaNERTagger +>>> nertagger = SennaNERTagger('/usr/share/senna-v3.0') # doctest: +SKIP +>>> nertagger.tag('Shakespeare theatre was in London .'.split()) # doctest: +SKIP +[('Shakespeare', 'B-PER'), ('theatre', 'O'), ('was', 'O'), ('in', 'O'), +('London', 'B-LOC'), ('.', 'O')] +>>> nertagger.tag('UN headquarters are in NY , USA .'.split()) # doctest: +SKIP +[('UN', 'B-ORG'), ('headquarters', 'O'), ('are', 'O'), ('in', 'O'), +('NY', 'B-LOC'), (',', 'O'), ('USA', 'B-LOC'), ('.', 'O')] +""" + +from nltk.classify import Senna + + +class SennaTagger(Senna): + def __init__(self, path, encoding="utf-8"): + super().__init__(path, ["pos"], encoding) + + def tag_sents(self, sentences): + """ + Applies the tag method over a list of sentences. This method will return + for each sentence a list of tuples of (word, tag). + """ + tagged_sents = super().tag_sents(sentences) + for i in range(len(tagged_sents)): + for j in range(len(tagged_sents[i])): + annotations = tagged_sents[i][j] + tagged_sents[i][j] = (annotations["word"], annotations["pos"]) + return tagged_sents + + +class SennaChunkTagger(Senna): + def __init__(self, path, encoding="utf-8"): + super().__init__(path, ["chk"], encoding) + + def tag_sents(self, sentences): + """ + Applies the tag method over a list of sentences. This method will return + for each sentence a list of tuples of (word, tag). + """ + tagged_sents = super().tag_sents(sentences) + for i in range(len(tagged_sents)): + for j in range(len(tagged_sents[i])): + annotations = tagged_sents[i][j] + tagged_sents[i][j] = (annotations["word"], annotations["chk"]) + return tagged_sents + + def bio_to_chunks(self, tagged_sent, chunk_type): + """ + Extracts the chunks in a BIO chunk-tagged sentence. + + >>> from nltk.tag import SennaChunkTagger + >>> chktagger = SennaChunkTagger('/usr/share/senna-v3.0') # doctest: +SKIP + >>> sent = 'What is the airspeed of an unladen swallow ?'.split() + >>> tagged_sent = chktagger.tag(sent) # doctest: +SKIP + >>> tagged_sent # doctest: +SKIP + [('What', 'B-NP'), ('is', 'B-VP'), ('the', 'B-NP'), ('airspeed', 'I-NP'), + ('of', 'B-PP'), ('an', 'B-NP'), ('unladen', 'I-NP'), ('swallow', 'I-NP'), + ('?', 'O')] + >>> list(chktagger.bio_to_chunks(tagged_sent, chunk_type='NP')) # doctest: +SKIP + [('What', '0'), ('the airspeed', '2-3'), ('an unladen swallow', '5-6-7')] + + :param tagged_sent: A list of tuples of word and BIO chunk tag. + :type tagged_sent: list(tuple) + :param tagged_sent: The chunk tag that users want to extract, e.g. 'NP' or 'VP' + :type tagged_sent: str + + :return: An iterable of tuples of chunks that users want to extract + and their corresponding indices. + :rtype: iter(tuple(str)) + """ + current_chunk = [] + current_chunk_position = [] + for idx, word_pos in enumerate(tagged_sent): + word, pos = word_pos + if "-" + chunk_type in pos: # Append the word to the current_chunk. + current_chunk.append(word) + current_chunk_position.append(idx) + else: + if current_chunk: # Flush the full chunk when out of an NP. + _chunk_str = " ".join(current_chunk) + _chunk_pos_str = "-".join(map(str, current_chunk_position)) + yield _chunk_str, _chunk_pos_str + current_chunk = [] + current_chunk_position = [] + if current_chunk: # Flush the last chunk. + yield " ".join(current_chunk), "-".join(map(str, current_chunk_position)) + + +class SennaNERTagger(Senna): + def __init__(self, path, encoding="utf-8"): + super().__init__(path, ["ner"], encoding) + + def tag_sents(self, sentences): + """ + Applies the tag method over a list of sentences. This method will return + for each sentence a list of tuples of (word, tag). + """ + tagged_sents = super().tag_sents(sentences) + for i in range(len(tagged_sents)): + for j in range(len(tagged_sents[i])): + annotations = tagged_sents[i][j] + tagged_sents[i][j] = (annotations["word"], annotations["ner"]) + return tagged_sents diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/sequential.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/sequential.py new file mode 100644 index 0000000..2aed134 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/sequential.py @@ -0,0 +1,755 @@ +# Natural Language Toolkit: Sequential Backoff Taggers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# Tiago Tresoldi (original affix tagger) +# URL: +# For license information, see LICENSE.TXT + +""" +Classes for tagging sentences sequentially, left to right. The +abstract base class SequentialBackoffTagger serves as the base +class for all the taggers in this module. Tagging of individual words +is performed by the method ``choose_tag()``, which is defined by +subclasses of SequentialBackoffTagger. If a tagger is unable to +determine a tag for the specified token, then its backoff tagger is +consulted instead. Any SequentialBackoffTagger may serve as a +backoff tagger for any other SequentialBackoffTagger. +""" +import ast +import re +from abc import abstractmethod +from typing import List, Optional, Tuple + +from nltk import jsontags +from nltk.classify import NaiveBayesClassifier +from nltk.probability import ConditionalFreqDist +from nltk.tag.api import FeaturesetTaggerI, TaggerI + + +###################################################################### +# Abstract Base Classes +###################################################################### +class SequentialBackoffTagger(TaggerI): + """ + An abstract base class for taggers that tags words sequentially, + left to right. Tagging of individual words is performed by the + ``choose_tag()`` method, which should be defined by subclasses. If + a tagger is unable to determine a tag for the specified token, + then its backoff tagger is consulted. + + :ivar _taggers: A list of all the taggers that should be tried to + tag a token (i.e., self and its backoff taggers). + """ + + def __init__(self, backoff=None): + if backoff is None: + self._taggers = [self] + else: + self._taggers = [self] + backoff._taggers + + @property + def backoff(self): + """The backoff tagger for this tagger.""" + return self._taggers[1] if len(self._taggers) > 1 else None + + def tag(self, tokens): + # docs inherited from TaggerI + tags = [] + for i in range(len(tokens)): + tags.append(self.tag_one(tokens, i, tags)) + return list(zip(tokens, tags)) + + def tag_one(self, tokens, index, history): + """ + Determine an appropriate tag for the specified token, and + return that tag. If this tagger is unable to determine a tag + for the specified token, then its backoff tagger is consulted. + + :rtype: str + :type tokens: list + :param tokens: The list of words that are being tagged. + :type index: int + :param index: The index of the word whose tag should be + returned. + :type history: list(str) + :param history: A list of the tags for all words before *index*. + """ + tag = None + for tagger in self._taggers: + tag = tagger.choose_tag(tokens, index, history) + if tag is not None: + break + return tag + + @abstractmethod + def choose_tag(self, tokens, index, history): + """ + Decide which tag should be used for the specified token, and + return that tag. If this tagger is unable to determine a tag + for the specified token, return None -- do not consult + the backoff tagger. This method should be overridden by + subclasses of SequentialBackoffTagger. + + :rtype: str + :type tokens: list + :param tokens: The list of words that are being tagged. + :type index: int + :param index: The index of the word whose tag should be + returned. + :type history: list(str) + :param history: A list of the tags for all words before *index*. + """ + + +class ContextTagger(SequentialBackoffTagger): + """ + An abstract base class for sequential backoff taggers that choose + a tag for a token based on the value of its "context". Different + subclasses are used to define different contexts. + + A ContextTagger chooses the tag for a token by calculating the + token's context, and looking up the corresponding tag in a table. + This table can be constructed manually; or it can be automatically + constructed based on a training corpus, using the ``_train()`` + factory method. + + :ivar _context_to_tag: Dictionary mapping contexts to tags. + """ + + def __init__(self, context_to_tag, backoff=None): + """ + :param context_to_tag: A dictionary mapping contexts to tags. + :param backoff: The backoff tagger that should be used for this tagger. + """ + super().__init__(backoff) + self._context_to_tag = context_to_tag if context_to_tag else {} + + @abstractmethod + def context(self, tokens, index, history): + """ + :return: the context that should be used to look up the tag + for the specified token; or None if the specified token + should not be handled by this tagger. + :rtype: (hashable) + """ + + def choose_tag(self, tokens, index, history): + context = self.context(tokens, index, history) + return self._context_to_tag.get(context) + + def size(self): + """ + :return: The number of entries in the table used by this + tagger to map from contexts to tags. + """ + return len(self._context_to_tag) + + def __repr__(self): + return f"<{self.__class__.__name__}: size={self.size()}>" + + def _train(self, tagged_corpus, cutoff=0, verbose=False): + """ + Initialize this ContextTagger's ``_context_to_tag`` table + based on the given training data. In particular, for each + context ``c`` in the training data, set + ``_context_to_tag[c]`` to the most frequent tag for that + context. However, exclude any contexts that are already + tagged perfectly by the backoff tagger(s). + + The old value of ``self._context_to_tag`` (if any) is discarded. + + :param tagged_corpus: A tagged corpus. Each item should be + a list of (word, tag tuples. + :param cutoff: If the most likely tag for a context occurs + fewer than cutoff times, then exclude it from the + context-to-tag table for the new tagger. + """ + + token_count = hit_count = 0 + + # A context is considered 'useful' if it's not already tagged + # perfectly by the backoff tagger. + useful_contexts = set() + + # Count how many times each tag occurs in each context. + fd = ConditionalFreqDist() + for sentence in tagged_corpus: + tokens, tags = zip(*sentence) + for index, (token, tag) in enumerate(sentence): + # Record the event. + token_count += 1 + context = self.context(tokens, index, tags[:index]) + if context is None: + continue + fd[context][tag] += 1 + # If the backoff got it wrong, this context is useful: + if self.backoff is None or tag != self.backoff.tag_one( + tokens, index, tags[:index] + ): + useful_contexts.add(context) + + # Build the context_to_tag table -- for each context, figure + # out what the most likely tag is. Only include contexts that + # we've seen at least `cutoff` times. + for context in useful_contexts: + best_tag = fd[context].max() + hits = fd[context][best_tag] + if hits > cutoff: + self._context_to_tag[context] = best_tag + hit_count += hits + + # Display some stats, if requested. + if verbose: + size = len(self._context_to_tag) + backoff = 100 - (hit_count * 100.0) / token_count + pruning = 100 - (size * 100.0) / len(fd.conditions()) + print("[Trained Unigram tagger:", end=" ") + print( + "size={}, backoff={:.2f}%, pruning={:.2f}%]".format( + size, backoff, pruning + ) + ) + + +###################################################################### +# Tagger Classes +###################################################################### + + +@jsontags.register_tag +class DefaultTagger(SequentialBackoffTagger): + """ + A tagger that assigns the same tag to every token. + + >>> from nltk.tag import DefaultTagger + >>> default_tagger = DefaultTagger('NN') + >>> list(default_tagger.tag('This is a test'.split())) + [('This', 'NN'), ('is', 'NN'), ('a', 'NN'), ('test', 'NN')] + + This tagger is recommended as a backoff tagger, in cases where + a more powerful tagger is unable to assign a tag to the word + (e.g. because the word was not seen during training). + + :param tag: The tag to assign to each token + :type tag: str + """ + + json_tag = "nltk.tag.sequential.DefaultTagger" + + def __init__(self, tag): + self._tag = tag + super().__init__(None) + + def encode_json_obj(self): + return self._tag + + @classmethod + def decode_json_obj(cls, obj): + tag = obj + return cls(tag) + + def choose_tag(self, tokens, index, history): + return self._tag # ignore token and history + + def __repr__(self): + return f"" + + +@jsontags.register_tag +class NgramTagger(ContextTagger): + """ + A tagger that chooses a token's tag based on its word string and + on the preceding n word's tags. In particular, a tuple + (tags[i-n:i-1], words[i]) is looked up in a table, and the + corresponding tag is returned. N-gram taggers are typically + trained on a tagged corpus. + + Train a new NgramTagger using the given training data or + the supplied model. In particular, construct a new tagger + whose table maps from each context (tag[i-n:i-1], word[i]) + to the most frequent tag for that context. But exclude any + contexts that are already tagged perfectly by the backoff + tagger. + + :param train: A tagged corpus consisting of a list of tagged + sentences, where each sentence is a list of (word, tag) tuples. + :param backoff: A backoff tagger, to be used by the new + tagger if it encounters an unknown context. + :param cutoff: If the most likely tag for a context occurs + fewer than *cutoff* times, then exclude it from the + context-to-tag table for the new tagger. + """ + + json_tag = "nltk.tag.sequential.NgramTagger" + + def __init__( + self, n, train=None, model=None, backoff=None, cutoff=0, verbose=False + ): + self._n = n + self._check_params(train, model) + + super().__init__(model, backoff) + + if train: + self._train(train, cutoff, verbose) + + def encode_json_obj(self): + _context_to_tag = {repr(k): v for k, v in self._context_to_tag.items()} + if "NgramTagger" in self.__class__.__name__: + return self._n, _context_to_tag, self.backoff + else: + return _context_to_tag, self.backoff + + @classmethod + def decode_json_obj(cls, obj): + try: + _n, _context_to_tag, backoff = obj + except ValueError: + _context_to_tag, backoff = obj + + if not _context_to_tag: + return backoff + + _context_to_tag = {ast.literal_eval(k): v for k, v in _context_to_tag.items()} + + if "NgramTagger" in cls.__name__: + return cls(_n, model=_context_to_tag, backoff=backoff) + else: + return cls(model=_context_to_tag, backoff=backoff) + + def context(self, tokens, index, history): + tag_context = tuple(history[max(0, index - self._n + 1) : index]) + return tag_context, tokens[index] + + +@jsontags.register_tag +class UnigramTagger(NgramTagger): + """ + Unigram Tagger + + The UnigramTagger finds the most likely tag for each word in a training + corpus, and then uses that information to assign tags to new tokens. + + >>> from nltk.corpus import brown + >>> from nltk.tag import UnigramTagger + >>> test_sent = brown.sents(categories='news')[0] + >>> unigram_tagger = UnigramTagger(brown.tagged_sents(categories='news')[:500]) + >>> for tok, tag in unigram_tagger.tag(test_sent): + ... print("({}, {}), ".format(tok, tag)) # doctest: +NORMALIZE_WHITESPACE + (The, AT), (Fulton, NP-TL), (County, NN-TL), (Grand, JJ-TL), + (Jury, NN-TL), (said, VBD), (Friday, NR), (an, AT), + (investigation, NN), (of, IN), (Atlanta's, NP$), (recent, JJ), + (primary, NN), (election, NN), (produced, VBD), (``, ``), + (no, AT), (evidence, NN), ('', ''), (that, CS), (any, DTI), + (irregularities, NNS), (took, VBD), (place, NN), (., .), + + :param train: The corpus of training data, a list of tagged sentences + :type train: list(list(tuple(str, str))) + :param model: The tagger model + :type model: dict + :param backoff: Another tagger which this tagger will consult when it is + unable to tag a word + :type backoff: TaggerI + :param cutoff: The number of instances of training data the tagger must see + in order not to use the backoff tagger + :type cutoff: int + """ + + json_tag = "nltk.tag.sequential.UnigramTagger" + + def __init__(self, train=None, model=None, backoff=None, cutoff=0, verbose=False): + super().__init__(1, train, model, backoff, cutoff, verbose) + + def context(self, tokens, index, history): + return tokens[index] + + +@jsontags.register_tag +class BigramTagger(NgramTagger): + """ + A tagger that chooses a token's tag based its word string and on + the preceding words' tag. In particular, a tuple consisting + of the previous tag and the word is looked up in a table, and + the corresponding tag is returned. + + :param train: The corpus of training data, a list of tagged sentences + :type train: list(list(tuple(str, str))) + :param model: The tagger model + :type model: dict + :param backoff: Another tagger which this tagger will consult when it is + unable to tag a word + :type backoff: TaggerI + :param cutoff: The number of instances of training data the tagger must see + in order not to use the backoff tagger + :type cutoff: int + """ + + json_tag = "nltk.tag.sequential.BigramTagger" + + def __init__(self, train=None, model=None, backoff=None, cutoff=0, verbose=False): + super().__init__(2, train, model, backoff, cutoff, verbose) + + +@jsontags.register_tag +class TrigramTagger(NgramTagger): + """ + A tagger that chooses a token's tag based its word string and on + the preceding two words' tags. In particular, a tuple consisting + of the previous two tags and the word is looked up in a table, and + the corresponding tag is returned. + + :param train: The corpus of training data, a list of tagged sentences + :type train: list(list(tuple(str, str))) + :param model: The tagger model + :type model: dict + :param backoff: Another tagger which this tagger will consult when it is + unable to tag a word + :type backoff: TaggerI + :param cutoff: The number of instances of training data the tagger must see + in order not to use the backoff tagger + :type cutoff: int + """ + + json_tag = "nltk.tag.sequential.TrigramTagger" + + def __init__(self, train=None, model=None, backoff=None, cutoff=0, verbose=False): + super().__init__(3, train, model, backoff, cutoff, verbose) + + +@jsontags.register_tag +class AffixTagger(ContextTagger): + """ + A tagger that chooses a token's tag based on a leading or trailing + substring of its word string. (It is important to note that these + substrings are not necessarily "true" morphological affixes). In + particular, a fixed-length substring of the word is looked up in a + table, and the corresponding tag is returned. Affix taggers are + typically constructed by training them on a tagged corpus. + + Construct a new affix tagger. + + :param affix_length: The length of the affixes that should be + considered during training and tagging. Use negative + numbers for suffixes. + :param min_stem_length: Any words whose length is less than + min_stem_length+abs(affix_length) will be assigned a + tag of None by this tagger. + """ + + json_tag = "nltk.tag.sequential.AffixTagger" + + def __init__( + self, + train=None, + model=None, + affix_length=-3, + min_stem_length=2, + backoff=None, + cutoff=0, + verbose=False, + ): + + self._check_params(train, model) + + super().__init__(model, backoff) + + self._affix_length = affix_length + self._min_word_length = min_stem_length + abs(affix_length) + + if train: + self._train(train, cutoff, verbose) + + def encode_json_obj(self): + return ( + self._affix_length, + self._min_word_length, + self._context_to_tag, + self.backoff, + ) + + @classmethod + def decode_json_obj(cls, obj): + _affix_length, _min_word_length, _context_to_tag, backoff = obj + return cls( + affix_length=_affix_length, + min_stem_length=_min_word_length - abs(_affix_length), + model=_context_to_tag, + backoff=backoff, + ) + + def context(self, tokens, index, history): + token = tokens[index] + if len(token) < self._min_word_length: + return None + elif self._affix_length > 0: + return token[: self._affix_length] + else: + return token[self._affix_length :] + + +@jsontags.register_tag +class RegexpTagger(SequentialBackoffTagger): + r""" + Regular Expression Tagger + + The RegexpTagger assigns tags to tokens by comparing their + word strings to a series of regular expressions. The following tagger + uses word suffixes to make guesses about the correct Brown Corpus part + of speech tag: + + >>> from nltk.corpus import brown + >>> from nltk.tag import RegexpTagger + >>> test_sent = brown.sents(categories='news')[0] + >>> regexp_tagger = RegexpTagger( + ... [(r'^-?[0-9]+(\.[0-9]+)?$', 'CD'), # cardinal numbers + ... (r'(The|the|A|a|An|an)$', 'AT'), # articles + ... (r'.*able$', 'JJ'), # adjectives + ... (r'.*ness$', 'NN'), # nouns formed from adjectives + ... (r'.*ly$', 'RB'), # adverbs + ... (r'.*s$', 'NNS'), # plural nouns + ... (r'.*ing$', 'VBG'), # gerunds + ... (r'.*ed$', 'VBD'), # past tense verbs + ... (r'.*', 'NN') # nouns (default) + ... ]) + >>> regexp_tagger + + >>> regexp_tagger.tag(test_sent) # doctest: +NORMALIZE_WHITESPACE + [('The', 'AT'), ('Fulton', 'NN'), ('County', 'NN'), ('Grand', 'NN'), ('Jury', 'NN'), + ('said', 'NN'), ('Friday', 'NN'), ('an', 'AT'), ('investigation', 'NN'), ('of', 'NN'), + ("Atlanta's", 'NNS'), ('recent', 'NN'), ('primary', 'NN'), ('election', 'NN'), + ('produced', 'VBD'), ('``', 'NN'), ('no', 'NN'), ('evidence', 'NN'), ("''", 'NN'), + ('that', 'NN'), ('any', 'NN'), ('irregularities', 'NNS'), ('took', 'NN'), + ('place', 'NN'), ('.', 'NN')] + + :type regexps: list(tuple(str, str)) + :param regexps: A list of ``(regexp, tag)`` pairs, each of + which indicates that a word matching ``regexp`` should + be tagged with ``tag``. The pairs will be evaluated in + order. If none of the regexps match a word, then the + optional backoff tagger is invoked, else it is + assigned the tag None. + """ + + json_tag = "nltk.tag.sequential.RegexpTagger" + + def __init__( + self, regexps: List[Tuple[str, str]], backoff: Optional[TaggerI] = None + ): + super().__init__(backoff) + self._regexps = [] + for regexp, tag in regexps: + try: + self._regexps.append((re.compile(regexp), tag)) + except Exception as e: + raise Exception( + f"Invalid RegexpTagger regexp: {e}\n- regexp: {regexp!r}\n- tag: {tag!r}" + ) from e + + def encode_json_obj(self): + return [(regexp.pattern, tag) for regexp, tag in self._regexps], self.backoff + + @classmethod + def decode_json_obj(cls, obj): + regexps, backoff = obj + return cls(regexps, backoff) + + def choose_tag(self, tokens, index, history): + for regexp, tag in self._regexps: + if re.match(regexp, tokens[index]): + return tag + return None + + def __repr__(self): + return f"" + + +class ClassifierBasedTagger(SequentialBackoffTagger, FeaturesetTaggerI): + """ + A sequential tagger that uses a classifier to choose the tag for + each token in a sentence. The featureset input for the classifier + is generated by a feature detector function:: + + feature_detector(tokens, index, history) -> featureset + + Where tokens is the list of unlabeled tokens in the sentence; + index is the index of the token for which feature detection + should be performed; and history is list of the tags for all + tokens before index. + + Construct a new classifier-based sequential tagger. + + :param feature_detector: A function used to generate the + featureset input for the classifier:: + feature_detector(tokens, index, history) -> featureset + + :param train: A tagged corpus consisting of a list of tagged + sentences, where each sentence is a list of (word, tag) tuples. + + :param backoff: A backoff tagger, to be used by the new tagger + if it encounters an unknown context. + + :param classifier_builder: A function used to train a new + classifier based on the data in *train*. It should take + one argument, a list of labeled featuresets (i.e., + (featureset, label) tuples). + + :param classifier: The classifier that should be used by the + tagger. This is only useful if you want to manually + construct the classifier; normally, you would use *train* + instead. + + :param backoff: A backoff tagger, used if this tagger is + unable to determine a tag for a given token. + + :param cutoff_prob: If specified, then this tagger will fall + back on its backoff tagger if the probability of the most + likely tag is less than *cutoff_prob*. + """ + + def __init__( + self, + feature_detector=None, + train=None, + classifier_builder=NaiveBayesClassifier.train, + classifier=None, + backoff=None, + cutoff_prob=None, + verbose=False, + ): + self._check_params(train, classifier) + + super().__init__(backoff) + + if (train and classifier) or (not train and not classifier): + raise ValueError( + "Must specify either training data or " "trained classifier." + ) + + if feature_detector is not None: + self._feature_detector = feature_detector + # The feature detector function, used to generate a featureset + # or each token: feature_detector(tokens, index, history) -> featureset + + self._cutoff_prob = cutoff_prob + """Cutoff probability for tagging -- if the probability of the + most likely tag is less than this, then use backoff.""" + + self._classifier = classifier + """The classifier used to choose a tag for each token.""" + + if train: + self._train(train, classifier_builder, verbose) + + def choose_tag(self, tokens, index, history): + # Use our feature detector to get the featureset. + featureset = self.feature_detector(tokens, index, history) + + # Use the classifier to pick a tag. If a cutoff probability + # was specified, then check that the tag's probability is + # higher than that cutoff first; otherwise, return None. + if self._cutoff_prob is None: + return self._classifier.classify(featureset) + + pdist = self._classifier.prob_classify(featureset) + tag = pdist.max() + return tag if pdist.prob(tag) >= self._cutoff_prob else None + + def _train(self, tagged_corpus, classifier_builder, verbose): + """ + Build a new classifier, based on the given training data + *tagged_corpus*. + """ + + classifier_corpus = [] + if verbose: + print("Constructing training corpus for classifier.") + + for sentence in tagged_corpus: + history = [] + untagged_sentence, tags = zip(*sentence) + for index in range(len(sentence)): + featureset = self.feature_detector(untagged_sentence, index, history) + classifier_corpus.append((featureset, tags[index])) + history.append(tags[index]) + + if verbose: + print(f"Training classifier ({len(classifier_corpus)} instances)") + self._classifier = classifier_builder(classifier_corpus) + + def __repr__(self): + return f"" + + def feature_detector(self, tokens, index, history): + """ + Return the feature detector that this tagger uses to generate + featuresets for its classifier. The feature detector is a + function with the signature:: + + feature_detector(tokens, index, history) -> featureset + + See ``classifier()`` + """ + return self._feature_detector(tokens, index, history) + + def classifier(self): + """ + Return the classifier that this tagger uses to choose a tag + for each word in a sentence. The input for this classifier is + generated using this tagger's feature detector. + See ``feature_detector()`` + """ + return self._classifier + + +class ClassifierBasedPOSTagger(ClassifierBasedTagger): + """ + A classifier based part of speech tagger. + """ + + def feature_detector(self, tokens, index, history): + word = tokens[index] + if index == 0: + prevword = prevprevword = None + prevtag = prevprevtag = None + elif index == 1: + prevword = tokens[index - 1].lower() + prevprevword = None + prevtag = history[index - 1] + prevprevtag = None + else: + prevword = tokens[index - 1].lower() + prevprevword = tokens[index - 2].lower() + prevtag = history[index - 1] + prevprevtag = history[index - 2] + + if re.match(r"[0-9]+(\.[0-9]*)?|[0-9]*\.[0-9]+$", word): + shape = "number" + elif re.match(r"\W+$", word): + shape = "punct" + elif re.match("[A-Z][a-z]+$", word): + shape = "upcase" + elif re.match("[a-z]+$", word): + shape = "downcase" + elif re.match(r"\w+$", word): + shape = "mixedcase" + else: + shape = "other" + + features = { + "prevtag": prevtag, + "prevprevtag": prevprevtag, + "word": word, + "word.lower": word.lower(), + "suffix3": word.lower()[-3:], + "suffix2": word.lower()[-2:], + "suffix1": word.lower()[-1:], + "prevprevword": prevprevword, + "prevword": prevword, + "prevtag+word": f"{prevtag}+{word.lower()}", + "prevprevtag+word": f"{prevprevtag}+{word.lower()}", + "prevword+word": f"{prevword}+{word.lower()}", + "shape": shape, + } + return features diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/stanford.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/stanford.py new file mode 100644 index 0000000..1ad48bb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/stanford.py @@ -0,0 +1,236 @@ +# Natural Language Toolkit: Interface to the Stanford Part-of-speech and Named-Entity Taggers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Nitin Madnani +# Rami Al-Rfou' +# URL: +# For license information, see LICENSE.TXT + +""" +A module for interfacing with the Stanford taggers. + +Tagger models need to be downloaded from https://nlp.stanford.edu/software +and the STANFORD_MODELS environment variable set (a colon-separated +list of paths). + +For more details see the documentation for StanfordPOSTagger and StanfordNERTagger. +""" + +import os +import tempfile +import warnings +from abc import abstractmethod +from subprocess import PIPE + +from nltk.internals import _java_options, config_java, find_file, find_jar, java +from nltk.tag.api import TaggerI + +_stanford_url = "https://nlp.stanford.edu/software" + + +class StanfordTagger(TaggerI): + """ + An interface to Stanford taggers. Subclasses must define: + + - ``_cmd`` property: A property that returns the command that will be + executed. + - ``_SEPARATOR``: Class constant that represents that character that + is used to separate the tokens from their tags. + - ``_JAR`` file: Class constant that represents the jar file name. + """ + + _SEPARATOR = "" + _JAR = "" + + def __init__( + self, + model_filename, + path_to_jar=None, + encoding="utf8", + verbose=False, + java_options="-mx1000m", + ): + # Raise deprecation warning. + warnings.warn( + str( + "\nThe StanfordTokenizer will " + "be deprecated in version 3.2.6.\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPParser\033[0m instead." + ), + DeprecationWarning, + stacklevel=2, + ) + + if not self._JAR: + warnings.warn( + "The StanfordTagger class is not meant to be " + "instantiated directly. Did you mean " + "StanfordPOSTagger or StanfordNERTagger?" + ) + self._stanford_jar = find_jar( + self._JAR, path_to_jar, searchpath=(), url=_stanford_url, verbose=verbose + ) + + self._stanford_model = find_file( + model_filename, env_vars=("STANFORD_MODELS",), verbose=verbose + ) + + self._encoding = encoding + self.java_options = java_options + + @property + @abstractmethod + def _cmd(self): + """ + A property that returns the command that will be executed. + """ + + def tag(self, tokens): + # This function should return list of tuple rather than list of list + return sum(self.tag_sents([tokens]), []) + + def tag_sents(self, sentences): + encoding = self._encoding + default_options = " ".join(_java_options) + config_java(options=self.java_options, verbose=False) + + # Create a temporary input file + _input_fh, self._input_file_path = tempfile.mkstemp(text=True) + + cmd = list(self._cmd) + cmd.extend(["-encoding", encoding]) + + # Write the actual sentences to the temporary input file + _input_fh = os.fdopen(_input_fh, "wb") + _input = "\n".join(" ".join(x) for x in sentences) + if isinstance(_input, str) and encoding: + _input = _input.encode(encoding) + _input_fh.write(_input) + _input_fh.close() + + # Run the tagger and get the output + stanpos_output, _stderr = java( + cmd, classpath=self._stanford_jar, stdout=PIPE, stderr=PIPE + ) + stanpos_output = stanpos_output.decode(encoding) + + # Delete the temporary file + os.unlink(self._input_file_path) + + # Return java configurations to their default values + config_java(options=default_options, verbose=False) + + return self.parse_output(stanpos_output, sentences) + + def parse_output(self, text, sentences=None): + # Output the tagged sentences + tagged_sentences = [] + for tagged_sentence in text.strip().split("\n"): + sentence = [] + for tagged_word in tagged_sentence.strip().split(): + word_tags = tagged_word.strip().split(self._SEPARATOR) + sentence.append( + ("".join(word_tags[:-1]), word_tags[-1].replace("0", "").upper()) + ) + tagged_sentences.append(sentence) + return tagged_sentences + + +class StanfordPOSTagger(StanfordTagger): + """ + A class for pos tagging with Stanford Tagger. The input is the paths to: + - a model trained on training data + - (optionally) the path to the stanford tagger jar file. If not specified here, + then this jar file must be specified in the CLASSPATH environment variable. + - (optionally) the encoding of the training data (default: UTF-8) + + Example: + + >>> from nltk.tag import StanfordPOSTagger + >>> st = StanfordPOSTagger('english-bidirectional-distsim.tagger') # doctest: +SKIP + >>> st.tag('What is the airspeed of an unladen swallow ?'.split()) # doctest: +SKIP + [('What', 'WP'), ('is', 'VBZ'), ('the', 'DT'), ('airspeed', 'NN'), ('of', 'IN'), ('an', 'DT'), ('unladen', 'JJ'), ('swallow', 'VB'), ('?', '.')] + """ + + _SEPARATOR = "_" + _JAR = "stanford-postagger.jar" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @property + def _cmd(self): + return [ + "edu.stanford.nlp.tagger.maxent.MaxentTagger", + "-model", + self._stanford_model, + "-textFile", + self._input_file_path, + "-tokenize", + "false", + "-outputFormatOptions", + "keepEmptySentences", + ] + + +class StanfordNERTagger(StanfordTagger): + """ + A class for Named-Entity Tagging with Stanford Tagger. The input is the paths to: + + - a model trained on training data + - (optionally) the path to the stanford tagger jar file. If not specified here, + then this jar file must be specified in the CLASSPATH environment variable. + - (optionally) the encoding of the training data (default: UTF-8) + + Example: + + >>> from nltk.tag import StanfordNERTagger + >>> st = StanfordNERTagger('english.all.3class.distsim.crf.ser.gz') # doctest: +SKIP + >>> st.tag('Rami Eid is studying at Stony Brook University in NY'.split()) # doctest: +SKIP + [('Rami', 'PERSON'), ('Eid', 'PERSON'), ('is', 'O'), ('studying', 'O'), + ('at', 'O'), ('Stony', 'ORGANIZATION'), ('Brook', 'ORGANIZATION'), + ('University', 'ORGANIZATION'), ('in', 'O'), ('NY', 'LOCATION')] + """ + + _SEPARATOR = "/" + _JAR = "stanford-ner.jar" + _FORMAT = "slashTags" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @property + def _cmd(self): + # Adding -tokenizerFactory edu.stanford.nlp.process.WhitespaceTokenizer -tokenizerOptions tokenizeNLs=false for not using stanford Tokenizer + return [ + "edu.stanford.nlp.ie.crf.CRFClassifier", + "-loadClassifier", + self._stanford_model, + "-textFile", + self._input_file_path, + "-outputFormat", + self._FORMAT, + "-tokenizerFactory", + "edu.stanford.nlp.process.WhitespaceTokenizer", + "-tokenizerOptions", + '"tokenizeNLs=false"', + ] + + def parse_output(self, text, sentences): + if self._FORMAT == "slashTags": + # Joint together to a big list + tagged_sentences = [] + for tagged_sentence in text.strip().split("\n"): + for tagged_word in tagged_sentence.strip().split(): + word_tags = tagged_word.strip().split(self._SEPARATOR) + tagged_sentences.append(("".join(word_tags[:-1]), word_tags[-1])) + + # Separate it according to the input + result = [] + start = 0 + for sent in sentences: + result.append(tagged_sentences[start : start + len(sent)]) + start += len(sent) + return result + + raise NotImplementedError diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/tnt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/tnt.py new file mode 100644 index 0000000..b12fe49 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/tnt.py @@ -0,0 +1,579 @@ +# Natural Language Toolkit: TnT Tagger +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Sam Huston +# +# URL: +# For license information, see LICENSE.TXT + +""" +Implementation of 'TnT - A Statisical Part of Speech Tagger' +by Thorsten Brants + +https://aclanthology.org/A00-1031.pdf +""" + +from math import log +from operator import itemgetter + +from nltk.probability import ConditionalFreqDist, FreqDist +from nltk.tag.api import TaggerI + + +class TnT(TaggerI): + """ + TnT - Statistical POS tagger + + IMPORTANT NOTES: + + * DOES NOT AUTOMATICALLY DEAL WITH UNSEEN WORDS + + - It is possible to provide an untrained POS tagger to + create tags for unknown words, see __init__ function + + * SHOULD BE USED WITH SENTENCE-DELIMITED INPUT + + - Due to the nature of this tagger, it works best when + trained over sentence delimited input. + - However it still produces good results if the training + data and testing data are separated on all punctuation eg: [,.?!] + - Input for training is expected to be a list of sentences + where each sentence is a list of (word, tag) tuples + - Input for tag function is a single sentence + Input for tagdata function is a list of sentences + Output is of a similar form + + * Function provided to process text that is unsegmented + + - Please see basic_sent_chop() + + + TnT uses a second order Markov model to produce tags for + a sequence of input, specifically: + + argmax [Proj(P(t_i|t_i-1,t_i-2)P(w_i|t_i))] P(t_T+1 | t_T) + + IE: the maximum projection of a set of probabilities + + The set of possible tags for a given word is derived + from the training data. It is the set of all tags + that exact word has been assigned. + + To speed up and get more precision, we can use log addition + to instead multiplication, specifically: + + argmax [Sigma(log(P(t_i|t_i-1,t_i-2))+log(P(w_i|t_i)))] + + log(P(t_T+1|t_T)) + + The probability of a tag for a given word is the linear + interpolation of 3 markov models; a zero-order, first-order, + and a second order model. + + P(t_i| t_i-1, t_i-2) = l1*P(t_i) + l2*P(t_i| t_i-1) + + l3*P(t_i| t_i-1, t_i-2) + + A beam search is used to limit the memory usage of the algorithm. + The degree of the beam can be changed using N in the initialization. + N represents the maximum number of possible solutions to maintain + while tagging. + + It is possible to differentiate the tags which are assigned to + capitalized words. However this does not result in a significant + gain in the accuracy of the results. + """ + + def __init__(self, unk=None, Trained=False, N=1000, C=False): + """ + Construct a TnT statistical tagger. Tagger must be trained + before being used to tag input. + + :param unk: instance of a POS tagger, conforms to TaggerI + :type unk: TaggerI + :param Trained: Indication that the POS tagger is trained or not + :type Trained: bool + :param N: Beam search degree (see above) + :type N: int + :param C: Capitalization flag + :type C: bool + + Initializer, creates frequency distributions to be used + for tagging + + _lx values represent the portion of the tri/bi/uni taggers + to be used to calculate the probability + + N value is the number of possible solutions to maintain + while tagging. A good value for this is 1000 + + C is a boolean value which specifies to use or + not use the Capitalization of the word as additional + information for tagging. + NOTE: using capitalization may not increase the accuracy + of the tagger + """ + + self._uni = FreqDist() + self._bi = ConditionalFreqDist() + self._tri = ConditionalFreqDist() + self._wd = ConditionalFreqDist() + self._eos = ConditionalFreqDist() + self._l1 = 0.0 + self._l2 = 0.0 + self._l3 = 0.0 + self._N = N + self._C = C + self._T = Trained + + self._unk = unk + + # statistical tools (ignore or delete me) + self.unknown = 0 + self.known = 0 + + def train(self, data): + """ + Uses a set of tagged data to train the tagger. + If an unknown word tagger is specified, + it is trained on the same data. + + :param data: List of lists of (word, tag) tuples + :type data: tuple(str) + """ + + # Ensure that local C flag is initialized before use + C = False + + if self._unk is not None and self._T == False: + self._unk.train(data) + + for sent in data: + history = [("BOS", False), ("BOS", False)] + for w, t in sent: + + # if capitalization is requested, + # and the word begins with a capital + # set local flag C to True + if self._C and w[0].isupper(): + C = True + + self._wd[w][t] += 1 + self._uni[(t, C)] += 1 + self._bi[history[1]][(t, C)] += 1 + self._tri[tuple(history)][(t, C)] += 1 + + history.append((t, C)) + history.pop(0) + + # set local flag C to false for the next word + C = False + + self._eos[t]["EOS"] += 1 + + # compute lambda values from the trained frequency distributions + self._compute_lambda() + + def _compute_lambda(self): + """ + creates lambda values based upon training data + + NOTE: no need to explicitly reference C, + it is contained within the tag variable :: tag == (tag,C) + + for each tag trigram (t1, t2, t3) + depending on the maximum value of + - f(t1,t2,t3)-1 / f(t1,t2)-1 + - f(t2,t3)-1 / f(t2)-1 + - f(t3)-1 / N-1 + + increment l3,l2, or l1 by f(t1,t2,t3) + + ISSUES -- Resolutions: + if 2 values are equal, increment both lambda values + by (f(t1,t2,t3) / 2) + """ + + # temporary lambda variables + tl1 = 0.0 + tl2 = 0.0 + tl3 = 0.0 + + # for each t1,t2 in system + for history in self._tri.conditions(): + (h1, h2) = history + + # for each t3 given t1,t2 in system + # (NOTE: tag actually represents (tag,C)) + # However no effect within this function + for tag in self._tri[history].keys(): + + # if there has only been 1 occurrence of this tag in the data + # then ignore this trigram. + if self._uni[tag] == 1: + continue + + # safe_div provides a safe floating point division + # it returns -1 if the denominator is 0 + c3 = self._safe_div( + (self._tri[history][tag] - 1), (self._tri[history].N() - 1) + ) + c2 = self._safe_div((self._bi[h2][tag] - 1), (self._bi[h2].N() - 1)) + c1 = self._safe_div((self._uni[tag] - 1), (self._uni.N() - 1)) + + # if c1 is the maximum value: + if (c1 > c3) and (c1 > c2): + tl1 += self._tri[history][tag] + + # if c2 is the maximum value + elif (c2 > c3) and (c2 > c1): + tl2 += self._tri[history][tag] + + # if c3 is the maximum value + elif (c3 > c2) and (c3 > c1): + tl3 += self._tri[history][tag] + + # if c3, and c2 are equal and larger than c1 + elif (c3 == c2) and (c3 > c1): + tl2 += self._tri[history][tag] / 2.0 + tl3 += self._tri[history][tag] / 2.0 + + # if c1, and c2 are equal and larger than c3 + # this might be a dumb thing to do....(not sure yet) + elif (c2 == c1) and (c1 > c3): + tl1 += self._tri[history][tag] / 2.0 + tl2 += self._tri[history][tag] / 2.0 + + # otherwise there might be a problem + # eg: all values = 0 + else: + pass + + # Lambda normalisation: + # ensures that l1+l2+l3 = 1 + self._l1 = tl1 / (tl1 + tl2 + tl3) + self._l2 = tl2 / (tl1 + tl2 + tl3) + self._l3 = tl3 / (tl1 + tl2 + tl3) + + def _safe_div(self, v1, v2): + """ + Safe floating point division function, does not allow division by 0 + returns -1 if the denominator is 0 + """ + if v2 == 0: + return -1 + else: + return v1 / v2 + + def tagdata(self, data): + """ + Tags each sentence in a list of sentences + + :param data:list of list of words + :type data: [[string,],] + :return: list of list of (word, tag) tuples + + Invokes tag(sent) function for each sentence + compiles the results into a list of tagged sentences + each tagged sentence is a list of (word, tag) tuples + """ + res = [] + for sent in data: + res1 = self.tag(sent) + res.append(res1) + return res + + def tag(self, data): + """ + Tags a single sentence + + :param data: list of words + :type data: [string,] + + :return: [(word, tag),] + + Calls recursive function '_tagword' + to produce a list of tags + + Associates the sequence of returned tags + with the correct words in the input sequence + + returns a list of (word, tag) tuples + """ + + current_state = [(["BOS", "BOS"], 0.0)] + + sent = list(data) + + tags = self._tagword(sent, current_state) + + res = [] + for i in range(len(sent)): + # unpack and discard the C flags + (t, C) = tags[i + 2] + res.append((sent[i], t)) + + return res + + def _tagword(self, sent, current_states): + """ + :param sent : List of words remaining in the sentence + :type sent : [word,] + :param current_states : List of possible tag combinations for + the sentence so far, and the log probability + associated with each tag combination + :type current_states : [([tag, ], logprob), ] + + Tags the first word in the sentence and + recursively tags the reminder of sentence + + Uses formula specified above to calculate the probability + of a particular tag + """ + + # if this word marks the end of the sentence, + # return the most probable tag + if sent == []: + (h, logp) = current_states[0] + return h + + # otherwise there are more words to be tagged + word = sent[0] + sent = sent[1:] + new_states = [] + + # if the Capitalisation is requested, + # initialise the flag for this word + C = False + if self._C and word[0].isupper(): + C = True + + # if word is known + # compute the set of possible tags + # and their associated log probabilities + if word in self._wd: + self.known += 1 + + for (history, curr_sent_logprob) in current_states: + logprobs = [] + + for t in self._wd[word].keys(): + tC = (t, C) + p_uni = self._uni.freq(tC) + p_bi = self._bi[history[-1]].freq(tC) + p_tri = self._tri[tuple(history[-2:])].freq(tC) + p_wd = self._wd[word][t] / self._uni[tC] + p = self._l1 * p_uni + self._l2 * p_bi + self._l3 * p_tri + p2 = log(p, 2) + log(p_wd, 2) + + # compute the result of appending each tag to this history + new_states.append((history + [tC], curr_sent_logprob + p2)) + + # otherwise a new word, set of possible tags is unknown + else: + self.unknown += 1 + + # since a set of possible tags, + # and the probability of each specific tag + # can not be returned from most classifiers: + # specify that any unknown words are tagged with certainty + p = 1 + + # if no unknown word tagger has been specified + # then use the tag 'Unk' + if self._unk is None: + tag = ("Unk", C) + + # otherwise apply the unknown word tagger + else: + [(_w, t)] = list(self._unk.tag([word])) + tag = (t, C) + + for (history, logprob) in current_states: + history.append(tag) + + new_states = current_states + + # now have computed a set of possible new_states + + # sort states by log prob + # set is now ordered greatest to least log probability + new_states.sort(reverse=True, key=itemgetter(1)) + + # del everything after N (threshold) + # this is the beam search cut + if len(new_states) > self._N: + new_states = new_states[: self._N] + + # compute the tags for the rest of the sentence + # return the best list of tags for the sentence + return self._tagword(sent, new_states) + + +######################################## +# helper function -- basic sentence tokenizer +######################################## + + +def basic_sent_chop(data, raw=True): + """ + Basic method for tokenizing input into sentences + for this tagger: + + :param data: list of tokens (words or (word, tag) tuples) + :type data: str or tuple(str, str) + :param raw: boolean flag marking the input data + as a list of words or a list of tagged words + :type raw: bool + :return: list of sentences + sentences are a list of tokens + tokens are the same as the input + + Function takes a list of tokens and separates the tokens into lists + where each list represents a sentence fragment + This function can separate both tagged and raw sequences into + basic sentences. + + Sentence markers are the set of [,.!?] + + This is a simple method which enhances the performance of the TnT + tagger. Better sentence tokenization will further enhance the results. + """ + + new_data = [] + curr_sent = [] + sent_mark = [",", ".", "?", "!"] + + if raw: + for word in data: + if word in sent_mark: + curr_sent.append(word) + new_data.append(curr_sent) + curr_sent = [] + else: + curr_sent.append(word) + + else: + for (word, tag) in data: + if word in sent_mark: + curr_sent.append((word, tag)) + new_data.append(curr_sent) + curr_sent = [] + else: + curr_sent.append((word, tag)) + return new_data + + +def demo(): + from nltk.corpus import brown + + sents = list(brown.tagged_sents()) + test = list(brown.sents()) + + tagger = TnT() + tagger.train(sents[200:1000]) + + tagged_data = tagger.tagdata(test[100:120]) + + for j in range(len(tagged_data)): + s = tagged_data[j] + t = sents[j + 100] + for i in range(len(s)): + print(s[i], "--", t[i]) + print() + + +def demo2(): + from nltk.corpus import treebank + + d = list(treebank.tagged_sents()) + + t = TnT(N=1000, C=False) + s = TnT(N=1000, C=True) + t.train(d[(11) * 100 :]) + s.train(d[(11) * 100 :]) + + for i in range(10): + tacc = t.accuracy(d[i * 100 : ((i + 1) * 100)]) + tp_un = t.unknown / (t.known + t.unknown) + tp_kn = t.known / (t.known + t.unknown) + t.unknown = 0 + t.known = 0 + + print("Capitalization off:") + print("Accuracy:", tacc) + print("Percentage known:", tp_kn) + print("Percentage unknown:", tp_un) + print("Accuracy over known words:", (tacc / tp_kn)) + + sacc = s.accuracy(d[i * 100 : ((i + 1) * 100)]) + sp_un = s.unknown / (s.known + s.unknown) + sp_kn = s.known / (s.known + s.unknown) + s.unknown = 0 + s.known = 0 + + print("Capitalization on:") + print("Accuracy:", sacc) + print("Percentage known:", sp_kn) + print("Percentage unknown:", sp_un) + print("Accuracy over known words:", (sacc / sp_kn)) + + +def demo3(): + from nltk.corpus import brown, treebank + + d = list(treebank.tagged_sents()) + e = list(brown.tagged_sents()) + + d = d[:1000] + e = e[:1000] + + d10 = int(len(d) * 0.1) + e10 = int(len(e) * 0.1) + + tknacc = 0 + sknacc = 0 + tallacc = 0 + sallacc = 0 + tknown = 0 + sknown = 0 + + for i in range(10): + + t = TnT(N=1000, C=False) + s = TnT(N=1000, C=False) + + dtest = d[(i * d10) : ((i + 1) * d10)] + etest = e[(i * e10) : ((i + 1) * e10)] + + dtrain = d[: (i * d10)] + d[((i + 1) * d10) :] + etrain = e[: (i * e10)] + e[((i + 1) * e10) :] + + t.train(dtrain) + s.train(etrain) + + tacc = t.accuracy(dtest) + tp_un = t.unknown / (t.known + t.unknown) + tp_kn = t.known / (t.known + t.unknown) + tknown += tp_kn + t.unknown = 0 + t.known = 0 + + sacc = s.accuracy(etest) + sp_un = s.unknown / (s.known + s.unknown) + sp_kn = s.known / (s.known + s.unknown) + sknown += sp_kn + s.unknown = 0 + s.known = 0 + + tknacc += tacc / tp_kn + sknacc += sacc / tp_kn + tallacc += tacc + sallacc += sacc + + # print(i+1, (tacc / tp_kn), i+1, (sacc / tp_kn), i+1, tacc, i+1, sacc) + + print("brown: acc over words known:", 10 * tknacc) + print(" : overall accuracy:", 10 * tallacc) + print(" : words known:", 10 * tknown) + print("treebank: acc over words known:", 10 * sknacc) + print(" : overall accuracy:", 10 * sallacc) + print(" : words known:", 10 * sknown) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tag/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/util.py new file mode 100644 index 0000000..cb31699 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tag/util.py @@ -0,0 +1,72 @@ +# Natural Language Toolkit: Tagger Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + + +def str2tuple(s, sep="/"): + """ + Given the string representation of a tagged token, return the + corresponding tuple representation. The rightmost occurrence of + *sep* in *s* will be used to divide *s* into a word string and + a tag string. If *sep* does not occur in *s*, return (s, None). + + >>> from nltk.tag.util import str2tuple + >>> str2tuple('fly/NN') + ('fly', 'NN') + + :type s: str + :param s: The string representation of a tagged token. + :type sep: str + :param sep: The separator string used to separate word strings + from tags. + """ + loc = s.rfind(sep) + if loc >= 0: + return (s[:loc], s[loc + len(sep) :].upper()) + else: + return (s, None) + + +def tuple2str(tagged_token, sep="/"): + """ + Given the tuple representation of a tagged token, return the + corresponding string representation. This representation is + formed by concatenating the token's word string, followed by the + separator, followed by the token's tag. (If the tag is None, + then just return the bare word string.) + + >>> from nltk.tag.util import tuple2str + >>> tagged_token = ('fly', 'NN') + >>> tuple2str(tagged_token) + 'fly/NN' + + :type tagged_token: tuple(str, str) + :param tagged_token: The tuple representation of a tagged token. + :type sep: str + :param sep: The separator string used to separate word strings + from tags. + """ + word, tag = tagged_token + if tag is None: + return word + else: + assert sep not in tag, "tag may not contain sep!" + return f"{word}{sep}{tag}" + + +def untag(tagged_sentence): + """ + Given a tagged sentence, return an untagged version of that + sentence. I.e., return a list containing the first element + of each tuple in *tagged_sentence*. + + >>> from nltk.tag.util import untag + >>> untag([('John', 'NNP'), ('saw', 'VBD'), ('Mary', 'NNP')]) + ['John', 'saw', 'Mary'] + + """ + return [w for (w, t) in tagged_sentence] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__init__.py new file mode 100644 index 0000000..050a71f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__init__.py @@ -0,0 +1,31 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Transformation Based Learning + +A general purpose package for Transformation Based Learning, +currently used by nltk.tag.BrillTagger. + +isort:skip_file +""" + +from nltk.tbl.template import Template + +# API: Template(...), Template.expand(...) + +from nltk.tbl.feature import Feature + +# API: Feature(...), Feature.expand(...) + +from nltk.tbl.rule import Rule + +# API: Rule.format(...), Rule.templatetid + +from nltk.tbl.erroranalysis import error_list diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b4f3328 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..bf571e2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/demo.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/demo.cpython-310.pyc new file mode 100644 index 0000000..8290cdf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/demo.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/erroranalysis.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/erroranalysis.cpython-310.pyc new file mode 100644 index 0000000..0da57d4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/erroranalysis.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/feature.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/feature.cpython-310.pyc new file mode 100644 index 0000000..eff7dce Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/feature.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/rule.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/rule.cpython-310.pyc new file mode 100644 index 0000000..3041631 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/rule.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/template.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/template.cpython-310.pyc new file mode 100644 index 0000000..2e64b65 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/__pycache__/template.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/api.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/demo.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/demo.py new file mode 100644 index 0000000..243e84e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/demo.py @@ -0,0 +1,418 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +import os +import pickle +import random +import time + +from nltk.corpus import treebank +from nltk.tag import BrillTaggerTrainer, RegexpTagger, UnigramTagger +from nltk.tag.brill import Pos, Word +from nltk.tbl import Template, error_list + + +def demo(): + """ + Run a demo with defaults. See source comments for details, + or docstrings of any of the more specific demo_* functions. + """ + postag() + + +def demo_repr_rule_format(): + """ + Exemplify repr(Rule) (see also str(Rule) and Rule.format("verbose")) + """ + postag(ruleformat="repr") + + +def demo_str_rule_format(): + """ + Exemplify repr(Rule) (see also str(Rule) and Rule.format("verbose")) + """ + postag(ruleformat="str") + + +def demo_verbose_rule_format(): + """ + Exemplify Rule.format("verbose") + """ + postag(ruleformat="verbose") + + +def demo_multiposition_feature(): + """ + The feature/s of a template takes a list of positions + relative to the current word where the feature should be + looked for, conceptually joined by logical OR. For instance, + Pos([-1, 1]), given a value V, will hold whenever V is found + one step to the left and/or one step to the right. + + For contiguous ranges, a 2-arg form giving inclusive end + points can also be used: Pos(-3, -1) is the same as the arg + below. + """ + postag(templates=[Template(Pos([-3, -2, -1]))]) + + +def demo_multifeature_template(): + """ + Templates can have more than a single feature. + """ + postag(templates=[Template(Word([0]), Pos([-2, -1]))]) + + +def demo_template_statistics(): + """ + Show aggregate statistics per template. Little used templates are + candidates for deletion, much used templates may possibly be refined. + + Deleting unused templates is mostly about saving time and/or space: + training is basically O(T) in the number of templates T + (also in terms of memory usage, which often will be the limiting factor). + """ + postag(incremental_stats=True, template_stats=True) + + +def demo_generated_templates(): + """ + Template.expand and Feature.expand are class methods facilitating + generating large amounts of templates. See their documentation for + details. + + Note: training with 500 templates can easily fill all available + even on relatively small corpora + """ + wordtpls = Word.expand([-1, 0, 1], [1, 2], excludezero=False) + tagtpls = Pos.expand([-2, -1, 0, 1], [1, 2], excludezero=True) + templates = list(Template.expand([wordtpls, tagtpls], combinations=(1, 3))) + print( + "Generated {} templates for transformation-based learning".format( + len(templates) + ) + ) + postag(templates=templates, incremental_stats=True, template_stats=True) + + +def demo_learning_curve(): + """ + Plot a learning curve -- the contribution on tagging accuracy of + the individual rules. + Note: requires matplotlib + """ + postag( + incremental_stats=True, + separate_baseline_data=True, + learning_curve_output="learningcurve.png", + ) + + +def demo_error_analysis(): + """ + Writes a file with context for each erroneous word after tagging testing data + """ + postag(error_output="errors.txt") + + +def demo_serialize_tagger(): + """ + Serializes the learned tagger to a file in pickle format; reloads it + and validates the process. + """ + postag(serialize_output="tagger.pcl") + + +def demo_high_accuracy_rules(): + """ + Discard rules with low accuracy. This may hurt performance a bit, + but will often produce rules which are more interesting read to a human. + """ + postag(num_sents=3000, min_acc=0.96, min_score=10) + + +def postag( + templates=None, + tagged_data=None, + num_sents=1000, + max_rules=300, + min_score=3, + min_acc=None, + train=0.8, + trace=3, + randomize=False, + ruleformat="str", + incremental_stats=False, + template_stats=False, + error_output=None, + serialize_output=None, + learning_curve_output=None, + learning_curve_take=300, + baseline_backoff_tagger=None, + separate_baseline_data=False, + cache_baseline_tagger=None, +): + """ + Brill Tagger Demonstration + :param templates: how many sentences of training and testing data to use + :type templates: list of Template + + :param tagged_data: maximum number of rule instances to create + :type tagged_data: C{int} + + :param num_sents: how many sentences of training and testing data to use + :type num_sents: C{int} + + :param max_rules: maximum number of rule instances to create + :type max_rules: C{int} + + :param min_score: the minimum score for a rule in order for it to be considered + :type min_score: C{int} + + :param min_acc: the minimum score for a rule in order for it to be considered + :type min_acc: C{float} + + :param train: the fraction of the the corpus to be used for training (1=all) + :type train: C{float} + + :param trace: the level of diagnostic tracing output to produce (0-4) + :type trace: C{int} + + :param randomize: whether the training data should be a random subset of the corpus + :type randomize: C{bool} + + :param ruleformat: rule output format, one of "str", "repr", "verbose" + :type ruleformat: C{str} + + :param incremental_stats: if true, will tag incrementally and collect stats for each rule (rather slow) + :type incremental_stats: C{bool} + + :param template_stats: if true, will print per-template statistics collected in training and (optionally) testing + :type template_stats: C{bool} + + :param error_output: the file where errors will be saved + :type error_output: C{string} + + :param serialize_output: the file where the learned tbl tagger will be saved + :type serialize_output: C{string} + + :param learning_curve_output: filename of plot of learning curve(s) (train and also test, if available) + :type learning_curve_output: C{string} + + :param learning_curve_take: how many rules plotted + :type learning_curve_take: C{int} + + :param baseline_backoff_tagger: the file where rules will be saved + :type baseline_backoff_tagger: tagger + + :param separate_baseline_data: use a fraction of the training data exclusively for training baseline + :type separate_baseline_data: C{bool} + + :param cache_baseline_tagger: cache baseline tagger to this file (only interesting as a temporary workaround to get + deterministic output from the baseline unigram tagger between python versions) + :type cache_baseline_tagger: C{string} + + + Note on separate_baseline_data: if True, reuse training data both for baseline and rule learner. This + is fast and fine for a demo, but is likely to generalize worse on unseen data. + Also cannot be sensibly used for learning curves on training data (the baseline will be artificially high). + """ + + # defaults + baseline_backoff_tagger = baseline_backoff_tagger or REGEXP_TAGGER + if templates is None: + from nltk.tag.brill import brill24, describe_template_sets + + # some pre-built template sets taken from typical systems or publications are + # available. Print a list with describe_template_sets() + # for instance: + templates = brill24() + (training_data, baseline_data, gold_data, testing_data) = _demo_prepare_data( + tagged_data, train, num_sents, randomize, separate_baseline_data + ) + + # creating (or reloading from cache) a baseline tagger (unigram tagger) + # this is just a mechanism for getting deterministic output from the baseline between + # python versions + if cache_baseline_tagger: + if not os.path.exists(cache_baseline_tagger): + baseline_tagger = UnigramTagger( + baseline_data, backoff=baseline_backoff_tagger + ) + with open(cache_baseline_tagger, "w") as print_rules: + pickle.dump(baseline_tagger, print_rules) + print( + "Trained baseline tagger, pickled it to {}".format( + cache_baseline_tagger + ) + ) + with open(cache_baseline_tagger) as print_rules: + baseline_tagger = pickle.load(print_rules) + print(f"Reloaded pickled tagger from {cache_baseline_tagger}") + else: + baseline_tagger = UnigramTagger(baseline_data, backoff=baseline_backoff_tagger) + print("Trained baseline tagger") + if gold_data: + print( + " Accuracy on test set: {:0.4f}".format( + baseline_tagger.accuracy(gold_data) + ) + ) + + # creating a Brill tagger + tbrill = time.time() + trainer = BrillTaggerTrainer( + baseline_tagger, templates, trace, ruleformat=ruleformat + ) + print("Training tbl tagger...") + brill_tagger = trainer.train(training_data, max_rules, min_score, min_acc) + print(f"Trained tbl tagger in {time.time() - tbrill:0.2f} seconds") + if gold_data: + print(" Accuracy on test set: %.4f" % brill_tagger.accuracy(gold_data)) + + # printing the learned rules, if learned silently + if trace == 1: + print("\nLearned rules: ") + for (ruleno, rule) in enumerate(brill_tagger.rules(), 1): + print(f"{ruleno:4d} {rule.format(ruleformat):s}") + + # printing template statistics (optionally including comparison with the training data) + # note: if not separate_baseline_data, then baseline accuracy will be artificially high + if incremental_stats: + print( + "Incrementally tagging the test data, collecting individual rule statistics" + ) + (taggedtest, teststats) = brill_tagger.batch_tag_incremental( + testing_data, gold_data + ) + print(" Rule statistics collected") + if not separate_baseline_data: + print( + "WARNING: train_stats asked for separate_baseline_data=True; the baseline " + "will be artificially high" + ) + trainstats = brill_tagger.train_stats() + if template_stats: + brill_tagger.print_template_statistics(teststats) + if learning_curve_output: + _demo_plot( + learning_curve_output, teststats, trainstats, take=learning_curve_take + ) + print(f"Wrote plot of learning curve to {learning_curve_output}") + else: + print("Tagging the test data") + taggedtest = brill_tagger.tag_sents(testing_data) + if template_stats: + brill_tagger.print_template_statistics() + + # writing error analysis to file + if error_output is not None: + with open(error_output, "w") as f: + f.write("Errors for Brill Tagger %r\n\n" % serialize_output) + f.write("\n".join(error_list(gold_data, taggedtest)).encode("utf-8") + "\n") + print(f"Wrote tagger errors including context to {error_output}") + + # serializing the tagger to a pickle file and reloading (just to see it works) + if serialize_output is not None: + taggedtest = brill_tagger.tag_sents(testing_data) + with open(serialize_output, "w") as print_rules: + pickle.dump(brill_tagger, print_rules) + print(f"Wrote pickled tagger to {serialize_output}") + with open(serialize_output) as print_rules: + brill_tagger_reloaded = pickle.load(print_rules) + print(f"Reloaded pickled tagger from {serialize_output}") + taggedtest_reloaded = brill_tagger.tag_sents(testing_data) + if taggedtest == taggedtest_reloaded: + print("Reloaded tagger tried on test set, results identical") + else: + print("PROBLEM: Reloaded tagger gave different results on test set") + + +def _demo_prepare_data( + tagged_data, train, num_sents, randomize, separate_baseline_data +): + # train is the proportion of data used in training; the rest is reserved + # for testing. + if tagged_data is None: + print("Loading tagged data from treebank... ") + tagged_data = treebank.tagged_sents() + if num_sents is None or len(tagged_data) <= num_sents: + num_sents = len(tagged_data) + if randomize: + random.seed(len(tagged_data)) + random.shuffle(tagged_data) + cutoff = int(num_sents * train) + training_data = tagged_data[:cutoff] + gold_data = tagged_data[cutoff:num_sents] + testing_data = [[t[0] for t in sent] for sent in gold_data] + if not separate_baseline_data: + baseline_data = training_data + else: + bl_cutoff = len(training_data) // 3 + (baseline_data, training_data) = ( + training_data[:bl_cutoff], + training_data[bl_cutoff:], + ) + (trainseqs, traintokens) = corpus_size(training_data) + (testseqs, testtokens) = corpus_size(testing_data) + (bltrainseqs, bltraintokens) = corpus_size(baseline_data) + print(f"Read testing data ({testseqs:d} sents/{testtokens:d} wds)") + print(f"Read training data ({trainseqs:d} sents/{traintokens:d} wds)") + print( + "Read baseline data ({:d} sents/{:d} wds) {:s}".format( + bltrainseqs, + bltraintokens, + "" if separate_baseline_data else "[reused the training set]", + ) + ) + return (training_data, baseline_data, gold_data, testing_data) + + +def _demo_plot(learning_curve_output, teststats, trainstats=None, take=None): + testcurve = [teststats["initialerrors"]] + for rulescore in teststats["rulescores"]: + testcurve.append(testcurve[-1] - rulescore) + testcurve = [1 - x / teststats["tokencount"] for x in testcurve[:take]] + + traincurve = [trainstats["initialerrors"]] + for rulescore in trainstats["rulescores"]: + traincurve.append(traincurve[-1] - rulescore) + traincurve = [1 - x / trainstats["tokencount"] for x in traincurve[:take]] + + import matplotlib.pyplot as plt + + r = list(range(len(testcurve))) + plt.plot(r, testcurve, r, traincurve) + plt.axis([None, None, None, 1.0]) + plt.savefig(learning_curve_output) + + +NN_CD_TAGGER = RegexpTagger([(r"^-?[0-9]+(\.[0-9]+)?$", "CD"), (r".*", "NN")]) + +REGEXP_TAGGER = RegexpTagger( + [ + (r"^-?[0-9]+(\.[0-9]+)?$", "CD"), # cardinal numbers + (r"(The|the|A|a|An|an)$", "AT"), # articles + (r".*able$", "JJ"), # adjectives + (r".*ness$", "NN"), # nouns formed from adjectives + (r".*ly$", "RB"), # adverbs + (r".*s$", "NNS"), # plural nouns + (r".*ing$", "VBG"), # gerunds + (r".*ed$", "VBD"), # past tense verbs + (r".*", "NN"), # nouns (default) + ] +) + + +def corpus_size(seqs): + return (len(seqs), sum(len(x) for x in seqs)) + + +if __name__ == "__main__": + demo_learning_curve() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/erroranalysis.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/erroranalysis.py new file mode 100644 index 0000000..779d2c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/erroranalysis.py @@ -0,0 +1,38 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +# returns a list of errors in string format + + +def error_list(train_sents, test_sents): + """ + Returns a list of human-readable strings indicating the errors in the + given tagging of the corpus. + + :param train_sents: The correct tagging of the corpus + :type train_sents: list(tuple) + :param test_sents: The tagged corpus + :type test_sents: list(tuple) + """ + hdr = ("%25s | %s | %s\n" + "-" * 26 + "+" + "-" * 24 + "+" + "-" * 26) % ( + "left context", + "word/test->gold".center(22), + "right context", + ) + errors = [hdr] + for (train_sent, test_sent) in zip(train_sents, test_sents): + for wordnum, (word, train_pos) in enumerate(train_sent): + test_pos = test_sent[wordnum][1] + if train_pos != test_pos: + left = " ".join("%s/%s" % w for w in train_sent[:wordnum]) + right = " ".join("%s/%s" % w for w in train_sent[wordnum + 1 :]) + mid = f"{word}/{test_pos}->{train_pos}" + errors.append(f"{left[-25:]:>25} | {mid.center(22)} | {right[:25]}") + + return errors diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/feature.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/feature.py new file mode 100644 index 0000000..fbc59ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/feature.py @@ -0,0 +1,267 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from abc import ABCMeta, abstractmethod + + +class Feature(metaclass=ABCMeta): + """ + An abstract base class for Features. A Feature is a combination of + a specific property-computing method and a list of relative positions + to apply that method to. + + The property-computing method, M{extract_property(tokens, index)}, + must be implemented by every subclass. It extracts or computes a specific + property for the token at the current index. Typical extract_property() + methods return features such as the token text or tag; but more involved + methods may consider the entire sequence M{tokens} and + for instance compute the length of the sentence the token belongs to. + + In addition, the subclass may have a PROPERTY_NAME, which is how + it will be printed (in Rules and Templates, etc). If not given, defaults + to the classname. + + """ + + json_tag = "nltk.tbl.Feature" + PROPERTY_NAME = None + + def __init__(self, positions, end=None): + """ + Construct a Feature which may apply at C{positions}. + + >>> # For instance, importing some concrete subclasses (Feature is abstract) + >>> from nltk.tag.brill import Word, Pos + + >>> # Feature Word, applying at one of [-2, -1] + >>> Word([-2,-1]) + Word([-2, -1]) + + >>> # Positions need not be contiguous + >>> Word([-2,-1, 1]) + Word([-2, -1, 1]) + + >>> # Contiguous ranges can alternatively be specified giving the + >>> # two endpoints (inclusive) + >>> Pos(-3, -1) + Pos([-3, -2, -1]) + + >>> # In two-arg form, start <= end is enforced + >>> Pos(2, 1) + Traceback (most recent call last): + File "", line 1, in + File "nltk/tbl/template.py", line 306, in __init__ + raise TypeError + ValueError: illegal interval specification: (start=2, end=1) + + :type positions: list of int + :param positions: the positions at which this features should apply + :raises ValueError: illegal position specifications + + An alternative calling convention, for contiguous positions only, + is Feature(start, end): + + :type start: int + :param start: start of range where this feature should apply + :type end: int + :param end: end of range (NOTE: inclusive!) where this feature should apply + """ + self.positions = None # to avoid warnings + if end is None: + self.positions = tuple(sorted({int(i) for i in positions})) + else: # positions was actually not a list, but only the start index + try: + if positions > end: + raise TypeError + self.positions = tuple(range(positions, end + 1)) + except TypeError as e: + # let any kind of erroneous spec raise ValueError + raise ValueError( + "illegal interval specification: (start={}, end={})".format( + positions, end + ) + ) from e + + # set property name given in subclass, or otherwise name of subclass + self.PROPERTY_NAME = self.__class__.PROPERTY_NAME or self.__class__.__name__ + + def encode_json_obj(self): + return self.positions + + @classmethod + def decode_json_obj(cls, obj): + positions = obj + return cls(positions) + + def __repr__(self): + return f"{self.__class__.__name__}({list(self.positions)!r})" + + @classmethod + def expand(cls, starts, winlens, excludezero=False): + """ + Return a list of features, one for each start point in starts + and for each window length in winlen. If excludezero is True, + no Features containing 0 in its positions will be generated + (many tbl trainers have a special representation for the + target feature at [0]) + + For instance, importing a concrete subclass (Feature is abstract) + + >>> from nltk.tag.brill import Word + + First argument gives the possible start positions, second the + possible window lengths + + >>> Word.expand([-3,-2,-1], [1]) + [Word([-3]), Word([-2]), Word([-1])] + + >>> Word.expand([-2,-1], [1]) + [Word([-2]), Word([-1])] + + >>> Word.expand([-3,-2,-1], [1,2]) + [Word([-3]), Word([-2]), Word([-1]), Word([-3, -2]), Word([-2, -1])] + + >>> Word.expand([-2,-1], [1]) + [Word([-2]), Word([-1])] + + A third optional argument excludes all Features whose positions contain zero + + >>> Word.expand([-2,-1,0], [1,2], excludezero=False) + [Word([-2]), Word([-1]), Word([0]), Word([-2, -1]), Word([-1, 0])] + + >>> Word.expand([-2,-1,0], [1,2], excludezero=True) + [Word([-2]), Word([-1]), Word([-2, -1])] + + All window lengths must be positive + + >>> Word.expand([-2,-1], [0]) + Traceback (most recent call last): + File "", line 1, in + File "nltk/tag/tbl/template.py", line 371, in expand + :param starts: where to start looking for Feature + ValueError: non-positive window length in [0] + + :param starts: where to start looking for Feature + :type starts: list of ints + :param winlens: window lengths where to look for Feature + :type starts: list of ints + :param excludezero: do not output any Feature with 0 in any of its positions. + :type excludezero: bool + :returns: list of Features + :raises ValueError: for non-positive window lengths + """ + if not all(x > 0 for x in winlens): + raise ValueError(f"non-positive window length in {winlens}") + xs = (starts[i : i + w] for w in winlens for i in range(len(starts) - w + 1)) + return [cls(x) for x in xs if not (excludezero and 0 in x)] + + def issuperset(self, other): + """ + Return True if this Feature always returns True when other does + + More precisely, return True if this feature refers to the same property as other; + and this Feature looks at all positions that other does (and possibly + other positions in addition). + + #For instance, importing a concrete subclass (Feature is abstract) + >>> from nltk.tag.brill import Word, Pos + + >>> Word([-3,-2,-1]).issuperset(Word([-3,-2])) + True + + >>> Word([-3,-2,-1]).issuperset(Word([-3,-2, 0])) + False + + #Feature subclasses must agree + >>> Word([-3,-2,-1]).issuperset(Pos([-3,-2])) + False + + :param other: feature with which to compare + :type other: (subclass of) Feature + :return: True if this feature is superset, otherwise False + :rtype: bool + + + """ + return self.__class__ is other.__class__ and set(self.positions) >= set( + other.positions + ) + + def intersects(self, other): + """ + Return True if the positions of this Feature intersects with those of other + + More precisely, return True if this feature refers to the same property as other; + and there is some overlap in the positions they look at. + + #For instance, importing a concrete subclass (Feature is abstract) + >>> from nltk.tag.brill import Word, Pos + + >>> Word([-3,-2,-1]).intersects(Word([-3,-2])) + True + + >>> Word([-3,-2,-1]).intersects(Word([-3,-2, 0])) + True + + >>> Word([-3,-2,-1]).intersects(Word([0])) + False + + #Feature subclasses must agree + >>> Word([-3,-2,-1]).intersects(Pos([-3,-2])) + False + + :param other: feature with which to compare + :type other: (subclass of) Feature + :return: True if feature classes agree and there is some overlap in the positions they look at + :rtype: bool + """ + + return bool( + self.__class__ is other.__class__ + and set(self.positions) & set(other.positions) + ) + + # Rich comparisons for Features. With @functools.total_ordering (Python 2.7+), + # it will be enough to define __lt__ and __eq__ + def __eq__(self, other): + return self.__class__ is other.__class__ and self.positions == other.positions + + def __lt__(self, other): + return ( + self.__class__.__name__ < other.__class__.__name__ + or + # self.positions is a sorted tuple of ints + self.positions < other.positions + ) + + def __ne__(self, other): + return not (self == other) + + def __gt__(self, other): + return other < self + + def __ge__(self, other): + return not self < other + + def __le__(self, other): + return self < other or self == other + + @staticmethod + @abstractmethod + def extract_property(tokens, index): + """ + Any subclass of Feature must define static method extract_property(tokens, index) + + :param tokens: the sequence of tokens + :type tokens: list of tokens + :param index: the current index + :type index: int + :return: feature value + :rtype: any (but usually scalar) + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/rule.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/rule.py new file mode 100644 index 0000000..e63f210 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/rule.py @@ -0,0 +1,322 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from abc import ABCMeta, abstractmethod + +from nltk import jsontags + + +###################################################################### +# Tag Rules +###################################################################### +class TagRule(metaclass=ABCMeta): + """ + An interface for tag transformations on a tagged corpus, as + performed by tbl taggers. Each transformation finds all tokens + in the corpus that are tagged with a specific original tag and + satisfy a specific condition, and replaces their tags with a + replacement tag. For any given transformation, the original + tag, replacement tag, and condition are fixed. Conditions may + depend on the token under consideration, as well as any other + tokens in the corpus. + + Tag rules must be comparable and hashable. + """ + + def __init__(self, original_tag, replacement_tag): + + self.original_tag = original_tag + """The tag which this TagRule may cause to be replaced.""" + + self.replacement_tag = replacement_tag + """The tag with which this TagRule may replace another tag.""" + + def apply(self, tokens, positions=None): + """ + Apply this rule at every position in positions where it + applies to the given sentence. I.e., for each position p + in *positions*, if *tokens[p]* is tagged with this rule's + original tag, and satisfies this rule's condition, then set + its tag to be this rule's replacement tag. + + :param tokens: The tagged sentence + :type tokens: list(tuple(str, str)) + :type positions: list(int) + :param positions: The positions where the transformation is to + be tried. If not specified, try it at all positions. + :return: The indices of tokens whose tags were changed by this + rule. + :rtype: int + """ + if positions is None: + positions = list(range(len(tokens))) + + # Determine the indices at which this rule applies. + change = [i for i in positions if self.applies(tokens, i)] + + # Make the changes. Note: this must be done in a separate + # step from finding applicable locations, since we don't want + # the rule to interact with itself. + for i in change: + tokens[i] = (tokens[i][0], self.replacement_tag) + + return change + + @abstractmethod + def applies(self, tokens, index): + """ + :return: True if the rule would change the tag of + ``tokens[index]``, False otherwise + :rtype: bool + :param tokens: A tagged sentence + :type tokens: list(str) + :param index: The index to check + :type index: int + """ + + # Rules must be comparable and hashable for the algorithm to work + def __eq__(self, other): + raise TypeError("Rules must implement __eq__()") + + def __ne__(self, other): + raise TypeError("Rules must implement __ne__()") + + def __hash__(self): + raise TypeError("Rules must implement __hash__()") + + +@jsontags.register_tag +class Rule(TagRule): + """ + A Rule checks the current corpus position for a certain set of conditions; + if they are all fulfilled, the Rule is triggered, meaning that it + will change tag A to tag B. For other tags than A, nothing happens. + + The conditions are parameters to the Rule instance. Each condition is a feature-value pair, + with a set of positions to check for the value of the corresponding feature. + Conceptually, the positions are joined by logical OR, and the feature set by logical AND. + + More formally, the Rule is then applicable to the M{n}th token iff: + + - The M{n}th token is tagged with the Rule's original tag; and + - For each (Feature(positions), M{value}) tuple: + + - The value of Feature of at least one token in {n+p for p in positions} + is M{value}. + """ + + json_tag = "nltk.tbl.Rule" + + def __init__(self, templateid, original_tag, replacement_tag, conditions): + """ + Construct a new Rule that changes a token's tag from + C{original_tag} to C{replacement_tag} if all of the properties + specified in C{conditions} hold. + + :param templateid: the template id (a zero-padded string, '001' etc, + so it will sort nicely) + :type templateid: string + + :param conditions: A list of Feature(positions), + each of which specifies that the property (computed by + Feature.extract_property()) of at least one + token in M{n} + p in positions is C{value}. + :type conditions: C{iterable} of C{Feature} + + """ + TagRule.__init__(self, original_tag, replacement_tag) + self._conditions = conditions + self.templateid = templateid + + def encode_json_obj(self): + return { + "templateid": self.templateid, + "original": self.original_tag, + "replacement": self.replacement_tag, + "conditions": self._conditions, + } + + @classmethod + def decode_json_obj(cls, obj): + return cls( + obj["templateid"], + obj["original"], + obj["replacement"], + tuple(tuple(feat) for feat in obj["conditions"]), + ) + + def applies(self, tokens, index): + # Inherit docs from TagRule + + # Does the given token have this Rule's "original tag"? + if tokens[index][1] != self.original_tag: + return False + + # Check to make sure that every condition holds. + for (feature, val) in self._conditions: + + # Look for *any* token that satisfies the condition. + for pos in feature.positions: + if not (0 <= index + pos < len(tokens)): + continue + if feature.extract_property(tokens, index + pos) == val: + break + else: + # No token satisfied the condition; return false. + return False + + # Every condition checked out, so the Rule is applicable. + return True + + def __eq__(self, other): + return self is other or ( + other is not None + and other.__class__ == self.__class__ + and self.original_tag == other.original_tag + and self.replacement_tag == other.replacement_tag + and self._conditions == other._conditions + ) + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + + # Cache our hash value (justified by profiling.) + try: + return self.__hash + except AttributeError: + self.__hash = hash(repr(self)) + return self.__hash + + def __repr__(self): + # Cache the repr (justified by profiling -- this is used as + # a sort key when deterministic=True.) + try: + return self.__repr + except AttributeError: + self.__repr = "{}('{}', {}, {}, [{}])".format( + self.__class__.__name__, + self.templateid, + repr(self.original_tag), + repr(self.replacement_tag), + # list(self._conditions) would be simpler but will not generate + # the same Rule.__repr__ in python 2 and 3 and thus break some tests + ", ".join(f"({f},{repr(v)})" for (f, v) in self._conditions), + ) + + return self.__repr + + def __str__(self): + def _condition_to_logic(feature, value): + """ + Return a compact, predicate-logic styled string representation + of the given condition. + """ + return "{}:{}@[{}]".format( + feature.PROPERTY_NAME, + value, + ",".join(str(w) for w in feature.positions), + ) + + conditions = " & ".join( + [_condition_to_logic(f, v) for (f, v) in self._conditions] + ) + s = f"{self.original_tag}->{self.replacement_tag} if {conditions}" + + return s + + def format(self, fmt): + """ + Return a string representation of this rule. + + >>> from nltk.tbl.rule import Rule + >>> from nltk.tag.brill import Pos + + >>> r = Rule("23", "VB", "NN", [(Pos([-2,-1]), 'DT')]) + + r.format("str") == str(r) + True + >>> r.format("str") + 'VB->NN if Pos:DT@[-2,-1]' + + r.format("repr") == repr(r) + True + >>> r.format("repr") + "Rule('23', 'VB', 'NN', [(Pos([-2, -1]),'DT')])" + + >>> r.format("verbose") + 'VB -> NN if the Pos of words i-2...i-1 is "DT"' + + >>> r.format("not_found") + Traceback (most recent call last): + File "", line 1, in + File "nltk/tbl/rule.py", line 256, in format + raise ValueError("unknown rule format spec: {0}".format(fmt)) + ValueError: unknown rule format spec: not_found + >>> + + :param fmt: format specification + :type fmt: str + :return: string representation + :rtype: str + """ + if fmt == "str": + return self.__str__() + elif fmt == "repr": + return self.__repr__() + elif fmt == "verbose": + return self._verbose_format() + else: + raise ValueError(f"unknown rule format spec: {fmt}") + + def _verbose_format(self): + """ + Return a wordy, human-readable string representation + of the given rule. + + Not sure how useful this is. + """ + + def condition_to_str(feature, value): + return 'the {} of {} is "{}"'.format( + feature.PROPERTY_NAME, + range_to_str(feature.positions), + value, + ) + + def range_to_str(positions): + if len(positions) == 1: + p = positions[0] + if p == 0: + return "this word" + if p == -1: + return "the preceding word" + elif p == 1: + return "the following word" + elif p < 0: + return "word i-%d" % -p + elif p > 0: + return "word i+%d" % p + else: + # for complete compatibility with the wordy format of nltk2 + mx = max(positions) + mn = min(positions) + if mx - mn == len(positions) - 1: + return "words i%+d...i%+d" % (mn, mx) + else: + return "words {{{}}}".format( + ",".join("i%+d" % d for d in positions) + ) + + replacement = f"{self.original_tag} -> {self.replacement_tag}" + conditions = (" if " if self._conditions else "") + ", and ".join( + condition_to_str(f, v) for (f, v) in self._conditions + ) + return replacement + conditions diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/template.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/template.py new file mode 100644 index 0000000..ee4ca25 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tbl/template.py @@ -0,0 +1,325 @@ +# Natural Language Toolkit: Transformation-based learning +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Marcus Uneson +# based on previous (nltk2) version by +# Christopher Maloof, Edward Loper, Steven Bird +# URL: +# For license information, see LICENSE.TXT + +import itertools as it +from abc import ABCMeta, abstractmethod + +from nltk.tbl.feature import Feature +from nltk.tbl.rule import Rule + + +class BrillTemplateI(metaclass=ABCMeta): + """ + An interface for generating lists of transformational rules that + apply at given sentence positions. ``BrillTemplateI`` is used by + ``Brill`` training algorithms to generate candidate rules. + """ + + @abstractmethod + def applicable_rules(self, tokens, i, correctTag): + """ + Return a list of the transformational rules that would correct + the ``i``-th subtoken's tag in the given token. In particular, + return a list of zero or more rules that would change + ``tokens[i][1]`` to ``correctTag``, if applied to ``token[i]``. + + If the ``i``-th token already has the correct tag (i.e., if + ``tagged_tokens[i][1] == correctTag``), then + ``applicable_rules()`` should return the empty list. + + :param tokens: The tagged tokens being tagged. + :type tokens: list(tuple) + :param i: The index of the token whose tag should be corrected. + :type i: int + :param correctTag: The correct tag for the ``i``-th token. + :type correctTag: any + :rtype: list(BrillRule) + """ + + @abstractmethod + def get_neighborhood(self, token, index): + """ + Returns the set of indices *i* such that + ``applicable_rules(token, i, ...)`` depends on the value of + the *index*th token of *token*. + + This method is used by the "fast" Brill tagger trainer. + + :param token: The tokens being tagged. + :type token: list(tuple) + :param index: The index whose neighborhood should be returned. + :type index: int + :rtype: set + """ + + +class Template(BrillTemplateI): + """ + A tbl Template that generates a list of L{Rule}s that apply at a given sentence + position. In particular, each C{Template} is parameterized by a list of + independent features (a combination of a specific + property to extract and a list C{L} of relative positions at which to extract + it) and generates all Rules that: + + - use the given features, each at its own independent position; and + - are applicable to the given token. + """ + + ALLTEMPLATES = [] + # record a unique id of form "001", for each template created + # _ids = it.count(0) + + def __init__(self, *features): + + """ + Construct a Template for generating Rules. + + Takes a list of Features. A C{Feature} is a combination + of a specific property and its relative positions and should be + a subclass of L{nltk.tbl.feature.Feature}. + + An alternative calling convention (kept for backwards compatibility, + but less expressive as it only permits one feature type) is + Template(Feature, (start1, end1), (start2, end2), ...) + In new code, that would be better written + Template(Feature(start1, end1), Feature(start2, end2), ...) + + For instance, importing some features + + >>> from nltk.tbl.template import Template + >>> from nltk.tag.brill import Word, Pos + + Create some features + + >>> wfeat1, wfeat2, pfeat = (Word([-1]), Word([1,2]), Pos([-2,-1])) + + Create a single-feature template + + >>> Template(wfeat1) + Template(Word([-1])) + + Or a two-feature one + + >>> Template(wfeat1, wfeat2) + Template(Word([-1]),Word([1, 2])) + + Or a three-feature one with two different feature types + + >>> Template(wfeat1, wfeat2, pfeat) + Template(Word([-1]),Word([1, 2]),Pos([-2, -1])) + + deprecated api: Feature subclass, followed by list of (start,end) pairs + (permits only a single Feature) + + >>> Template(Word, (-2,-1), (0,0)) + Template(Word([-2, -1]),Word([0])) + + Incorrect specification raises TypeError + + >>> Template(Word, (-2,-1), Pos, (0,0)) + Traceback (most recent call last): + File "", line 1, in + File "nltk/tag/tbl/template.py", line 143, in __init__ + raise TypeError( + TypeError: expected either Feature1(args), Feature2(args), ... or Feature, (start1, end1), (start2, end2), ... + + :type features: list of Features + :param features: the features to build this Template on + """ + # determine the calling form: either + # Template(Feature, args1, [args2, ...)] + # Template(Feature1(args), Feature2(args), ...) + if all(isinstance(f, Feature) for f in features): + self._features = features + elif issubclass(features[0], Feature) and all( + isinstance(a, tuple) for a in features[1:] + ): + self._features = [features[0](*tp) for tp in features[1:]] + else: + raise TypeError( + "expected either Feature1(args), Feature2(args), ... or Feature, (start1, end1), (start2, end2), ..." + ) + self.id = f"{len(self.ALLTEMPLATES):03d}" + self.ALLTEMPLATES.append(self) + + def __repr__(self): + return "{}({})".format( + self.__class__.__name__, + ",".join([str(f) for f in self._features]), + ) + + def applicable_rules(self, tokens, index, correct_tag): + if tokens[index][1] == correct_tag: + return [] + + # For each of this Template's features, find the conditions + # that are applicable for the given token. + # Then, generate one Rule for each combination of features + # (the crossproduct of the conditions). + + applicable_conditions = self._applicable_conditions(tokens, index) + xs = list(it.product(*applicable_conditions)) + return [Rule(self.id, tokens[index][1], correct_tag, tuple(x)) for x in xs] + + def _applicable_conditions(self, tokens, index): + """ + :returns: A set of all conditions for rules + that are applicable to C{tokens[index]}. + """ + conditions = [] + + for feature in self._features: + conditions.append([]) + for pos in feature.positions: + if not (0 <= index + pos < len(tokens)): + continue + value = feature.extract_property(tokens, index + pos) + conditions[-1].append((feature, value)) + return conditions + + def get_neighborhood(self, tokens, index): + # inherit docs from BrillTemplateI + + # applicable_rules(tokens, index, ...) depends on index. + neighborhood = {index} # set literal for python 2.7+ + + # applicable_rules(tokens, i, ...) depends on index if + # i+start < index <= i+end. + + allpositions = [0] + [p for feat in self._features for p in feat.positions] + start, end = min(allpositions), max(allpositions) + s = max(0, index + (-end)) + e = min(index + (-start) + 1, len(tokens)) + for i in range(s, e): + neighborhood.add(i) + return neighborhood + + @classmethod + def expand(cls, featurelists, combinations=None, skipintersecting=True): + + """ + Factory method to mass generate Templates from a list L of lists of Features. + + #With combinations=(k1, k2), the function will in all possible ways choose k1 ... k2 + #of the sublists in L; it will output all Templates formed by the Cartesian product + #of this selection, with duplicates and other semantically equivalent + #forms removed. Default for combinations is (1, len(L)). + + The feature lists may have been specified + manually, or generated from Feature.expand(). For instance, + + >>> from nltk.tbl.template import Template + >>> from nltk.tag.brill import Word, Pos + + #creating some features + >>> (wd_0, wd_01) = (Word([0]), Word([0,1])) + + >>> (pos_m2, pos_m33) = (Pos([-2]), Pos([3-2,-1,0,1,2,3])) + + >>> list(Template.expand([[wd_0], [pos_m2]])) + [Template(Word([0])), Template(Pos([-2])), Template(Pos([-2]),Word([0]))] + + >>> list(Template.expand([[wd_0, wd_01], [pos_m2]])) + [Template(Word([0])), Template(Word([0, 1])), Template(Pos([-2])), Template(Pos([-2]),Word([0])), Template(Pos([-2]),Word([0, 1]))] + + #note: with Feature.expand(), it is very easy to generate more templates + #than your system can handle -- for instance, + >>> wordtpls = Word.expand([-2,-1,0,1], [1,2], excludezero=False) + >>> len(wordtpls) + 7 + + >>> postpls = Pos.expand([-3,-2,-1,0,1,2], [1,2,3], excludezero=True) + >>> len(postpls) + 9 + + #and now the Cartesian product of all non-empty combinations of two wordtpls and + #two postpls, with semantic equivalents removed + >>> templates = list(Template.expand([wordtpls, wordtpls, postpls, postpls])) + >>> len(templates) + 713 + + + will return a list of eight templates + Template(Word([0])), + Template(Word([0, 1])), + Template(Pos([-2])), + Template(Pos([-1])), + Template(Pos([-2]),Word([0])), + Template(Pos([-1]),Word([0])), + Template(Pos([-2]),Word([0, 1])), + Template(Pos([-1]),Word([0, 1]))] + + + #Templates where one feature is a subset of another, such as + #Template(Word([0,1]), Word([1]), will not appear in the output. + #By default, this non-subset constraint is tightened to disjointness: + #Templates of type Template(Word([0,1]), Word([1,2]) will also be filtered out. + #With skipintersecting=False, then such Templates are allowed + + WARNING: this method makes it very easy to fill all your memory when training + generated templates on any real-world corpus + + :param featurelists: lists of Features, whose Cartesian product will return a set of Templates + :type featurelists: list of (list of Features) + :param combinations: given n featurelists: if combinations=k, all generated Templates will have + k features; if combinations=(k1,k2) they will have k1..k2 features; if None, defaults to 1..n + :type combinations: None, int, or (int, int) + :param skipintersecting: if True, do not output intersecting Templates (non-disjoint positions for some feature) + :type skipintersecting: bool + :returns: generator of Templates + + """ + + def nonempty_powerset(xs): # xs is a list + # itertools docnonempty_powerset([1,2,3]) --> (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3) + + # find the correct tuple given combinations, one of {None, k, (k1,k2)} + k = combinations # for brevity + combrange = ( + (1, len(xs) + 1) + if k is None + else (k, k + 1) # n over 1 .. n over n (all non-empty combinations) + if isinstance(k, int) + else (k[0], k[1] + 1) # n over k (only + ) # n over k1, n over k1+1... n over k2 + return it.chain.from_iterable( + it.combinations(xs, r) for r in range(*combrange) + ) + + seentemplates = set() + for picks in nonempty_powerset(featurelists): + for pick in it.product(*picks): + if any( + i != j and x.issuperset(y) + for (i, x) in enumerate(pick) + for (j, y) in enumerate(pick) + ): + continue + if skipintersecting and any( + i != j and x.intersects(y) + for (i, x) in enumerate(pick) + for (j, y) in enumerate(pick) + ): + continue + thistemplate = cls(*sorted(pick)) + strpick = str(thistemplate) + #!!FIXME --this is hackish + if strpick in seentemplates: # already added + cls._poptemplate() + continue + seentemplates.add(strpick) + yield thistemplate + + @classmethod + def _cleartemplates(cls): + cls.ALLTEMPLATES = [] + + @classmethod + def _poptemplate(cls): + return cls.ALLTEMPLATES.pop() if cls.ALLTEMPLATES else None diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__init__.py new file mode 100644 index 0000000..fa9d96b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__init__.py @@ -0,0 +1,18 @@ +# Natural Language Toolkit: Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +Unit tests for the NLTK modules. These tests are intended to ensure +that source code changes don't accidentally introduce bugs. +For instructions, please see: + +../../web/dev/local_testing.rst + +https://github.com/nltk/nltk/blob/develop/web/dev/local_testing.rst + + +""" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..aaf8f03 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/all.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/all.cpython-310.pyc new file mode 100644 index 0000000..63e0b88 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/all.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/childes_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/childes_fixt.cpython-310.pyc new file mode 100644 index 0000000..1202553 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/childes_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/classify_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/classify_fixt.cpython-310.pyc new file mode 100644 index 0000000..05afc2d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/classify_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/conftest.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/conftest.cpython-310.pyc new file mode 100644 index 0000000..7c5f31d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/conftest.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gensim_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gensim_fixt.cpython-310.pyc new file mode 100644 index 0000000..4f0f405 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gensim_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gluesemantics_malt_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gluesemantics_malt_fixt.cpython-310.pyc new file mode 100644 index 0000000..f0e75e4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/gluesemantics_malt_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/portuguese_en_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/portuguese_en_fixt.cpython-310.pyc new file mode 100644 index 0000000..0db5a3a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/portuguese_en_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/probability_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/probability_fixt.cpython-310.pyc new file mode 100644 index 0000000..156fcff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/probability_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/setup_fixt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/setup_fixt.cpython-310.pyc new file mode 100644 index 0000000..930e3a3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/__pycache__/setup_fixt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/all.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/all.py new file mode 100644 index 0000000..6682135 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/all.py @@ -0,0 +1,25 @@ +"""Test suite that runs all NLTK tests. + +This module, `nltk.test.all`, is named as the NLTK ``test_suite`` in the +project's ``setup-eggs.py`` file. Here, we create a test suite that +runs all of our doctests, and return it for processing by the setuptools +test harness. + +""" +import doctest +import os.path +import unittest +from glob import glob + + +def additional_tests(): + # print("here-000000000000000") + # print("-----", glob(os.path.join(os.path.dirname(__file__), '*.doctest'))) + dir = os.path.dirname(__file__) + paths = glob(os.path.join(dir, "*.doctest")) + files = [os.path.basename(path) for path in paths] + return unittest.TestSuite([doctest.DocFileSuite(file) for file in files]) + + +# if os.path.split(path)[-1] != 'index.rst' +# skips time-dependent doctest in index.rst diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/bleu.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/bleu.doctest new file mode 100644 index 0000000..4ea6b8a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/bleu.doctest @@ -0,0 +1,29 @@ +========== +BLEU tests +========== + +>>> from nltk.translate import bleu + +If the candidate has no alignment to any of the references, the BLEU score is 0. + +>>> bleu( +... ['The candidate has no alignment to any of the references'.split()], +... 'John loves Mary'.split(), +... (1,), +... ) +0 + +This is an implementation of the smoothing techniques +for segment-level BLEU scores that was presented in +Boxing Chen and Collin Cherry (2014) A Systematic Comparison of +Smoothing Techniques for Sentence-Level BLEU. In WMT14. +http://acl2014.org/acl2014/W14-33/pdf/W14-3346.pdf +>>> from nltk.translate.bleu_score import sentence_bleu,SmoothingFunction + + +>>> sentence_bleu( +... ['It is a place of quiet contemplation .'.split()], +... 'It is .'.split(), +... smoothing_function=SmoothingFunction().method4, +... )*100 +4.4267... diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/bnc.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/bnc.doctest new file mode 100644 index 0000000..ef835ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/bnc.doctest @@ -0,0 +1,60 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + + >>> import os.path + + >>> from nltk.corpus.reader import BNCCorpusReader + >>> import nltk.test + + >>> root = os.path.dirname(nltk.test.__file__) + >>> bnc = BNCCorpusReader(root=root, fileids='FX8.xml') + +Checking the word access. +------------------------- + + >>> len(bnc.words()) + 151 + + >>> bnc.words()[:6] + ['Ah', 'there', 'we', 'are', ',', '.'] + >>> bnc.words(stem=True)[:6] + ['ah', 'there', 'we', 'be', ',', '.'] + + >>> bnc.tagged_words()[:6] + [('Ah', 'INTERJ'), ('there', 'ADV'), ('we', 'PRON'), ('are', 'VERB'), (',', 'PUN'), ('.', 'PUN')] + + >>> bnc.tagged_words(c5=True)[:6] + [('Ah', 'ITJ'), ('there', 'AV0'), ('we', 'PNP'), ('are', 'VBB'), (',', 'PUN'), ('.', 'PUN')] + +Testing access to the sentences. +-------------------------------- + + >>> len(bnc.sents()) + 15 + + >>> bnc.sents()[0] + ['Ah', 'there', 'we', 'are', ',', '.'] + >>> bnc.sents(stem=True)[0] + ['ah', 'there', 'we', 'be', ',', '.'] + + >>> bnc.tagged_sents()[0] + [('Ah', 'INTERJ'), ('there', 'ADV'), ('we', 'PRON'), ('are', 'VERB'), (',', 'PUN'), ('.', 'PUN')] + >>> bnc.tagged_sents(c5=True)[0] + [('Ah', 'ITJ'), ('there', 'AV0'), ('we', 'PNP'), ('are', 'VBB'), (',', 'PUN'), ('.', 'PUN')] + +A not lazy loader. +------------------ + + >>> eager = BNCCorpusReader(root=root, fileids=r'FX8.xml', lazy=False) + + >>> len(eager.words()) + 151 + >>> eager.words(stem=True)[6:17] + ['right', 'abdominal', 'wound', ',', 'she', 'be', 'a', 'wee', 'bit', 'confuse', '.'] + + >>> eager.tagged_words()[6:11] + [('Right', 'ADV'), ('abdominal', 'ADJ'), ('wound', 'SUBST'), (',', 'PUN'), ('she', 'PRON')] + >>> eager.tagged_words(c5=True)[6:17] + [('Right', 'AV0'), ('abdominal', 'AJ0'), ('wound', 'NN1'), (',', 'PUN'), ('she', 'PNP'), ("'s", 'VBZ'), ('a', 'AT0'), ('wee', 'AJ0-NN1'), ('bit', 'NN1'), ('confused', 'VVN-AJ0'), ('.', 'PUN')] + >>> len(eager.sents()) + 15 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg.doctest new file mode 100644 index 0000000..bc85734 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg.doctest @@ -0,0 +1,376 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============================== +Combinatory Categorial Grammar +============================== + +Relative Clauses +---------------- + + >>> from nltk.ccg import chart, lexicon + +Construct a lexicon: + + >>> lex = lexicon.fromstring(''' + ... :- S, NP, N, VP + ... + ... Det :: NP/N + ... Pro :: NP + ... Modal :: S\\NP/VP + ... + ... TV :: VP/NP + ... DTV :: TV/NP + ... + ... the => Det + ... + ... that => Det + ... that => NP + ... + ... I => Pro + ... you => Pro + ... we => Pro + ... + ... chef => N + ... cake => N + ... children => N + ... dough => N + ... + ... will => Modal + ... should => Modal + ... might => Modal + ... must => Modal + ... + ... and => var\\.,var/.,var + ... + ... to => VP[to]/VP + ... + ... without => (VP\\VP)/VP[ing] + ... + ... be => TV + ... cook => TV + ... eat => TV + ... + ... cooking => VP[ing]/NP + ... + ... give => DTV + ... + ... is => (S\\NP)/NP + ... prefer => (S\\NP)/NP + ... + ... which => (N\\N)/(S/NP) + ... + ... persuade => (VP/VP[to])/NP + ... ''') + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> for parse in parser.parse("you prefer that cake".split()): + ... chart.printCCGDerivation(parse) + ... break + ... + you prefer that cake + NP ((S\NP)/NP) (NP/N) N + --------------> + NP + ---------------------------> + (S\NP) + --------------------------------< + S + + >>> for parse in parser.parse("that is the cake which you prefer".split()): + ... chart.printCCGDerivation(parse) + ... break + ... + that is the cake which you prefer + NP ((S\NP)/NP) (NP/N) N ((N\N)/(S/NP)) NP ((S\NP)/NP) + ----->T + (S/(S\NP)) + ------------------>B + (S/NP) + ----------------------------------> + (N\N) + ----------------------------------------< + N + ------------------------------------------------> + NP + -------------------------------------------------------------> + (S\NP) + -------------------------------------------------------------------< + S + + +Some other sentences to try: +"that is the cake which we will persuade the chef to cook" +"that is the cake which we will persuade the chef to give the children" + + >>> sent = "that is the dough which you will eat without cooking".split() + >>> nosub_parser = chart.CCGChartParser(lex, chart.ApplicationRuleSet + + ... chart.CompositionRuleSet + chart.TypeRaiseRuleSet) + +Without Substitution (no output) + + >>> for parse in nosub_parser.parse(sent): + ... chart.printCCGDerivation(parse) + +With Substitution: + + >>> for parse in parser.parse(sent): + ... chart.printCCGDerivation(parse) + ... break + ... + that is the dough which you will eat without cooking + NP ((S\NP)/NP) (NP/N) N ((N\N)/(S/NP)) NP ((S\NP)/VP) (VP/NP) ((VP\VP)/VP['ing']) (VP['ing']/NP) + ----->T + (S/(S\NP)) + ------------------------------------->B + ((VP\VP)/NP) + ----------------------------------------------B + ((S\NP)/NP) + ---------------------------------------------------------------->B + (S/NP) + --------------------------------------------------------------------------------> + (N\N) + ---------------------------------------------------------------------------------------< + N + -----------------------------------------------------------------------------------------------> + NP + ------------------------------------------------------------------------------------------------------------> + (S\NP) + ------------------------------------------------------------------------------------------------------------------< + S + + +Conjunction +----------- + + >>> from nltk.ccg.chart import CCGChartParser, ApplicationRuleSet, CompositionRuleSet + >>> from nltk.ccg.chart import SubstitutionRuleSet, TypeRaiseRuleSet, printCCGDerivation + >>> from nltk.ccg import lexicon + +Lexicons for the tests: + + >>> test1_lex = ''' + ... :- S,N,NP,VP + ... I => NP + ... you => NP + ... will => S\\NP/VP + ... cook => VP/NP + ... which => (N\\N)/(S/NP) + ... and => var\\.,var/.,var + ... might => S\\NP/VP + ... eat => VP/NP + ... the => NP/N + ... mushrooms => N + ... parsnips => N''' + >>> test2_lex = ''' + ... :- N, S, NP, VP + ... articles => N + ... the => NP/N + ... and => var\\.,var/.,var + ... which => (N\\N)/(S/NP) + ... I => NP + ... anyone => NP + ... will => (S/VP)\\NP + ... file => VP/NP + ... without => (VP\\VP)/VP[ing] + ... forget => VP/NP + ... reading => VP[ing]/NP + ... ''' + +Tests handling of conjunctions. +Note that while the two derivations are different, they are semantically equivalent. + + >>> lex = lexicon.fromstring(test1_lex) + >>> parser = CCGChartParser(lex, ApplicationRuleSet + CompositionRuleSet + SubstitutionRuleSet) + >>> for parse in parser.parse("I will cook and might eat the mushrooms and parsnips".split()): + ... printCCGDerivation(parse) + I will cook and might eat the mushrooms and parsnips + NP ((S\NP)/VP) (VP/NP) ((_var0\.,_var0)/.,_var0) ((S\NP)/VP) (VP/NP) (NP/N) N ((_var0\.,_var0)/.,_var0) N + ---------------------->B + ((S\NP)/NP) + ---------------------->B + ((S\NP)/NP) + -------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) + -----------------------------------------------------------------------< + ((S\NP)/NP) + -------------------------------------> + (N\.,N) + ------------------------------------------------< + N + --------------------------------------------------------> + NP + -------------------------------------------------------------------------------------------------------------------------------> + (S\NP) + -----------------------------------------------------------------------------------------------------------------------------------< + S + I will cook and might eat the mushrooms and parsnips + NP ((S\NP)/VP) (VP/NP) ((_var0\.,_var0)/.,_var0) ((S\NP)/VP) (VP/NP) (NP/N) N ((_var0\.,_var0)/.,_var0) N + ---------------------->B + ((S\NP)/NP) + ---------------------->B + ((S\NP)/NP) + -------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) + -----------------------------------------------------------------------< + ((S\NP)/NP) + ------------------------------------------------------------------------------->B + ((S\NP)/N) + -------------------------------------> + (N\.,N) + ------------------------------------------------< + N + -------------------------------------------------------------------------------------------------------------------------------> + (S\NP) + -----------------------------------------------------------------------------------------------------------------------------------< + S + + +Tests handling subject extraction. +Interesting to point that the two parses are clearly semantically different. + + >>> lex = lexicon.fromstring(test2_lex) + >>> parser = CCGChartParser(lex, ApplicationRuleSet + CompositionRuleSet + SubstitutionRuleSet) + >>> for parse in parser.parse("articles which I will file and forget without reading".split()): + ... printCCGDerivation(parse) + articles which I will file and forget without reading + N ((N\N)/(S/NP)) NP ((S/VP)\NP) (VP/NP) ((_var0\.,_var0)/.,_var0) (VP/NP) ((VP\VP)/VP['ing']) (VP['ing']/NP) + -----------------< + (S/VP) + ------------------------------------->B + ((VP\VP)/NP) + ---------------------------------------------- + ((VP/NP)\.,(VP/NP)) + ----------------------------------------------------------------------------------< + (VP/NP) + --------------------------------------------------------------------------------------------------->B + (S/NP) + -------------------------------------------------------------------------------------------------------------------> + (N\N) + -----------------------------------------------------------------------------------------------------------------------------< + N + articles which I will file and forget without reading + N ((N\N)/(S/NP)) NP ((S/VP)\NP) (VP/NP) ((_var0\.,_var0)/.,_var0) (VP/NP) ((VP\VP)/VP['ing']) (VP['ing']/NP) + -----------------< + (S/VP) + ------------------------------------> + ((VP/NP)\.,(VP/NP)) + ---------------------------------------------< + (VP/NP) + ------------------------------------->B + ((VP\VP)/NP) + ----------------------------------------------------------------------------------B + (S/NP) + -------------------------------------------------------------------------------------------------------------------> + (N\N) + -----------------------------------------------------------------------------------------------------------------------------< + N + + +Unicode support +--------------- + +Unicode words are supported. + + >>> from nltk.ccg import chart, lexicon + +Lexicons for the tests: + + >>> lex = lexicon.fromstring(''' + ... :- S, N, NP, PP + ... + ... AdjI :: N\\N + ... AdjD :: N/N + ... AdvD :: S/S + ... AdvI :: S\\S + ... Det :: NP/N + ... PrepNPCompl :: PP/NP + ... PrepNAdjN :: S\\S/N + ... PrepNAdjNP :: S\\S/NP + ... VPNP :: S\\NP/NP + ... VPPP :: S\\NP/PP + ... VPser :: S\\NP/AdjI + ... + ... auto => N + ... bebidas => N + ... cine => N + ... ley => N + ... libro => N + ... ministro => N + ... panadería => N + ... presidente => N + ... super => N + ... + ... el => Det + ... la => Det + ... las => Det + ... un => Det + ... + ... Ana => NP + ... Pablo => NP + ... + ... y => var\\.,var/.,var + ... + ... pero => (S/NP)\\(S/NP)/(S/NP) + ... + ... anunció => VPNP + ... compró => VPNP + ... cree => S\\NP/S[dep] + ... desmintió => VPNP + ... lee => VPNP + ... fueron => VPPP + ... + ... es => VPser + ... + ... interesante => AdjD + ... interesante => AdjI + ... nueva => AdjD + ... nueva => AdjI + ... + ... a => PrepNPCompl + ... en => PrepNAdjN + ... en => PrepNAdjNP + ... + ... ayer => AdvI + ... + ... que => (NP\\NP)/(S/NP) + ... que => S[dep]/S + ... ''') + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> for parse in parser.parse(u"el ministro anunció pero el presidente desmintió la nueva ley".split()): + ... printCCGDerivation(parse) # doctest: +SKIP + ... # it fails on python2.7 because of the unicode problem explained in https://github.com/nltk/nltk/pull/1354 + ... break + el ministro anunció pero el presidente desmintió la nueva ley + (NP/N) N ((S\NP)/NP) (((S/NP)\(S/NP))/(S/NP)) (NP/N) N ((S\NP)/NP) (NP/N) (N/N) N + ------------------> + NP + ------------------>T + (S/(S\NP)) + --------------------> + NP + -------------------->T + (S/(S\NP)) + --------------------------------->B + (S/NP) + -----------------------------------------------------------> + ((S/NP)\(S/NP)) + ------------> + N + --------------------> + NP + -------------------- + S diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg_semantics.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg_semantics.doctest new file mode 100644 index 0000000..f4aec65 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/ccg_semantics.doctest @@ -0,0 +1,552 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============================================== +Combinatory Categorial Grammar with semantics +============================================== + +----- +Chart +----- + + + >>> from nltk.ccg import chart, lexicon + >>> from nltk.ccg.chart import printCCGDerivation + +No semantics +------------------- + + >>> lex = lexicon.fromstring(''' + ... :- S, NP, N + ... She => NP + ... has => (S\\NP)/NP + ... books => NP + ... ''', + ... False) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("She has books".split())) + >>> print(str(len(parses)) + " parses") + 3 parses + + >>> printCCGDerivation(parses[0]) + She has books + NP ((S\NP)/NP) NP + --------------------> + (S\NP) + -------------------------< + S + + >>> printCCGDerivation(parses[1]) + She has books + NP ((S\NP)/NP) NP + ----->T + (S/(S\NP)) + --------------------> + (S\NP) + -------------------------> + S + + + >>> printCCGDerivation(parses[2]) + She has books + NP ((S\NP)/NP) NP + ----->T + (S/(S\NP)) + ------------------>B + (S/NP) + -------------------------> + S + +Simple semantics +------------------- + + >>> lex = lexicon.fromstring(''' + ... :- S, NP, N + ... She => NP {she} + ... has => (S\\NP)/NP {\\x y.have(y, x)} + ... a => NP/N {\\P.exists z.P(z)} + ... book => N {book} + ... ''', + ... True) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("She has a book".split())) + >>> print(str(len(parses)) + " parses") + 7 parses + + >>> printCCGDerivation(parses[0]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + -------------------------------------> + NP {exists z.book(z)} + -------------------------------------------------------------------> + (S\NP) {\y.have(y,exists z.book(z))} + -----------------------------------------------------------------------------< + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[1]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + --------------------------------------------------------->B + ((S\NP)/N) {\P y.have(y,exists z.P(z))} + -------------------------------------------------------------------> + (S\NP) {\y.have(y,exists z.book(z))} + -----------------------------------------------------------------------------< + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[2]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + -------------------------------------> + NP {exists z.book(z)} + -------------------------------------------------------------------> + (S\NP) {\y.have(y,exists z.book(z))} + -----------------------------------------------------------------------------> + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[3]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + --------------------------------------------------------->B + ((S\NP)/N) {\P y.have(y,exists z.P(z))} + -------------------------------------------------------------------> + (S\NP) {\y.have(y,exists z.book(z))} + -----------------------------------------------------------------------------> + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[4]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + ---------------------------------------->B + (S/NP) {\x.have(she,x)} + -------------------------------------> + NP {exists z.book(z)} + -----------------------------------------------------------------------------> + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[5]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + --------------------------------------------------------->B + ((S\NP)/N) {\P y.have(y,exists z.P(z))} + ------------------------------------------------------------------->B + (S/N) {\P.have(she,exists z.P(z))} + -----------------------------------------------------------------------------> + S {have(she,exists z.book(z))} + + >>> printCCGDerivation(parses[6]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (NP/N) {\P.exists z.P(z)} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + ---------------------------------------->B + (S/NP) {\x.have(she,x)} + ------------------------------------------------------------------->B + (S/N) {\P.have(she,exists z.P(z))} + -----------------------------------------------------------------------------> + S {have(she,exists z.book(z))} + +Complex semantics +------------------- + + >>> lex = lexicon.fromstring(''' + ... :- S, NP, N + ... She => NP {she} + ... has => (S\\NP)/NP {\\x y.have(y, x)} + ... a => ((S\\NP)\\((S\\NP)/NP))/N {\\P R x.(exists z.P(z) & R(z,x))} + ... book => N {book} + ... ''', + ... True) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("She has a book".split())) + >>> print(str(len(parses)) + " parses") + 2 parses + + >>> printCCGDerivation(parses[0]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (((S\NP)\((S\NP)/NP))/N) {\P R x.(exists z.P(z) & R(z,x))} N {book} + ----------------------------------------------------------------------> + ((S\NP)\((S\NP)/NP)) {\R x.(exists z.book(z) & R(z,x))} + ----------------------------------------------------------------------------------------------------< + (S\NP) {\x.(exists z.book(z) & have(x,z))} + --------------------------------------------------------------------------------------------------------------< + S {(exists z.book(z) & have(she,z))} + + >>> printCCGDerivation(parses[1]) + She has a book + NP {she} ((S\NP)/NP) {\x y.have(y,x)} (((S\NP)\((S\NP)/NP))/N) {\P R x.(exists z.P(z) & R(z,x))} N {book} + ---------->T + (S/(S\NP)) {\F.F(she)} + ----------------------------------------------------------------------> + ((S\NP)\((S\NP)/NP)) {\R x.(exists z.book(z) & R(z,x))} + ----------------------------------------------------------------------------------------------------< + (S\NP) {\x.(exists z.book(z) & have(x,z))} + --------------------------------------------------------------------------------------------------------------> + S {(exists z.book(z) & have(she,z))} + +Using conjunctions +--------------------- + + # TODO: The semantics of "and" should have been more flexible + >>> lex = lexicon.fromstring(''' + ... :- S, NP, N + ... I => NP {I} + ... cook => (S\\NP)/NP {\\x y.cook(x,y)} + ... and => var\\.,var/.,var {\\P Q x y.(P(x,y) & Q(x,y))} + ... eat => (S\\NP)/NP {\\x y.eat(x,y)} + ... the => NP/N {\\x.the(x)} + ... bacon => N {bacon} + ... ''', + ... True) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("I cook and eat the bacon".split())) + >>> print(str(len(parses)) + " parses") + 7 parses + + >>> printCCGDerivation(parses[0]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + -------------------------------> + NP {the(bacon)} + --------------------------------------------------------------------------------------------------------------------------------------------------> + (S\NP) {\y.(eat(the(bacon),y) & cook(the(bacon),y))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------< + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[1]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + --------------------------------------------------------------------------------------------------------------------------------------->B + ((S\NP)/N) {\x y.(eat(the(x),y) & cook(the(x),y))} + --------------------------------------------------------------------------------------------------------------------------------------------------> + (S\NP) {\y.(eat(the(bacon),y) & cook(the(bacon),y))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------< + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[2]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------->T + (S/(S\NP)) {\F.F(I)} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + -------------------------------> + NP {the(bacon)} + --------------------------------------------------------------------------------------------------------------------------------------------------> + (S\NP) {\y.(eat(the(bacon),y) & cook(the(bacon),y))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------> + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[3]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------->T + (S/(S\NP)) {\F.F(I)} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + --------------------------------------------------------------------------------------------------------------------------------------->B + ((S\NP)/N) {\x y.(eat(the(x),y) & cook(the(x),y))} + --------------------------------------------------------------------------------------------------------------------------------------------------> + (S\NP) {\y.(eat(the(bacon),y) & cook(the(bacon),y))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------> + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[4]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------->T + (S/(S\NP)) {\F.F(I)} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + --------------------------------------------------------------------------------------------------------------------------->B + (S/NP) {\x.(eat(x,I) & cook(x,I))} + -------------------------------> + NP {the(bacon)} + ----------------------------------------------------------------------------------------------------------------------------------------------------------> + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[5]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------->T + (S/(S\NP)) {\F.F(I)} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + --------------------------------------------------------------------------------------------------------------------------------------->B + ((S\NP)/N) {\x y.(eat(the(x),y) & cook(the(x),y))} + ----------------------------------------------------------------------------------------------------------------------------------------------->B + (S/N) {\x.(eat(the(x),I) & cook(the(x),I))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------> + S {(eat(the(bacon),I) & cook(the(bacon),I))} + + >>> printCCGDerivation(parses[6]) + I cook and eat the bacon + NP {I} ((S\NP)/NP) {\x y.cook(x,y)} ((_var0\.,_var0)/.,_var0) {\P Q x y.(P(x,y) & Q(x,y))} ((S\NP)/NP) {\x y.eat(x,y)} (NP/N) {\x.the(x)} N {bacon} + -------->T + (S/(S\NP)) {\F.F(I)} + -------------------------------------------------------------------------------------> + (((S\NP)/NP)\.,((S\NP)/NP)) {\Q x y.(eat(x,y) & Q(x,y))} + -------------------------------------------------------------------------------------------------------------------< + ((S\NP)/NP) {\x y.(eat(x,y) & cook(x,y))} + --------------------------------------------------------------------------------------------------------------------------->B + (S/NP) {\x.(eat(x,I) & cook(x,I))} + ----------------------------------------------------------------------------------------------------------------------------------------------->B + (S/N) {\x.(eat(the(x),I) & cook(the(x),I))} + ----------------------------------------------------------------------------------------------------------------------------------------------------------> + S {(eat(the(bacon),I) & cook(the(bacon),I))} + +Tests from published papers +------------------------------ + +An example from "CCGbank: A Corpus of CCG Derivations and Dependency Structures Extracted from the Penn Treebank", Hockenmaier and Steedman, 2007, Page 359, https://www.aclweb.org/anthology/J/J07/J07-3004.pdf + + >>> lex = lexicon.fromstring(''' + ... :- S, NP + ... I => NP {I} + ... give => ((S\\NP)/NP)/NP {\\x y z.give(y,x,z)} + ... them => NP {them} + ... money => NP {money} + ... ''', + ... True) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("I give them money".split())) + >>> print(str(len(parses)) + " parses") + 3 parses + + >>> printCCGDerivation(parses[0]) + I give them money + NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} NP {money} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + --------------------------------------------------------------> + (S\NP) {\z.give(money,them,z)} + ----------------------------------------------------------------------< + S {give(money,them,I)} + + >>> printCCGDerivation(parses[1]) + I give them money + NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} NP {money} + -------->T + (S/(S\NP)) {\F.F(I)} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + --------------------------------------------------------------> + (S\NP) {\z.give(money,them,z)} + ----------------------------------------------------------------------> + S {give(money,them,I)} + + + >>> printCCGDerivation(parses[2]) + I give them money + NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} NP {money} + -------->T + (S/(S\NP)) {\F.F(I)} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + ---------------------------------------------------------->B + (S/NP) {\y.give(y,them,I)} + ----------------------------------------------------------------------> + S {give(money,them,I)} + + +An example from "CCGbank: A Corpus of CCG Derivations and Dependency Structures Extracted from the Penn Treebank", Hockenmaier and Steedman, 2007, Page 359, https://www.aclweb.org/anthology/J/J07/J07-3004.pdf + + >>> lex = lexicon.fromstring(''' + ... :- N, NP, S + ... money => N {money} + ... that => (N\\N)/(S/NP) {\\P Q x.(P(x) & Q(x))} + ... I => NP {I} + ... give => ((S\\NP)/NP)/NP {\\x y z.give(y,x,z)} + ... them => NP {them} + ... ''', + ... True) + + >>> parser = chart.CCGChartParser(lex, chart.DefaultRuleSet) + >>> parses = list(parser.parse("money that I give them".split())) + >>> print(str(len(parses)) + " parses") + 3 parses + + >>> printCCGDerivation(parses[0]) + money that I give them + N {money} ((N\N)/(S/NP)) {\P Q x.(P(x) & Q(x))} NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} + -------->T + (S/(S\NP)) {\F.F(I)} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + ---------------------------------------------------------->B + (S/NP) {\y.give(y,them,I)} + -------------------------------------------------------------------------------------------------> + (N\N) {\Q x.(give(x,them,I) & Q(x))} + ------------------------------------------------------------------------------------------------------------< + N {\x.(give(x,them,I) & money(x))} + + >>> printCCGDerivation(parses[1]) + money that I give them + N {money} ((N\N)/(S/NP)) {\P Q x.(P(x) & Q(x))} NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} + ----------->T + (N/(N\N)) {\F.F(money)} + -------->T + (S/(S\NP)) {\F.F(I)} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + ---------------------------------------------------------->B + (S/NP) {\y.give(y,them,I)} + -------------------------------------------------------------------------------------------------> + (N\N) {\Q x.(give(x,them,I) & Q(x))} + ------------------------------------------------------------------------------------------------------------> + N {\x.(give(x,them,I) & money(x))} + + >>> printCCGDerivation(parses[2]) + money that I give them + N {money} ((N\N)/(S/NP)) {\P Q x.(P(x) & Q(x))} NP {I} (((S\NP)/NP)/NP) {\x y z.give(y,x,z)} NP {them} + ----------->T + (N/(N\N)) {\F.F(money)} + -------------------------------------------------->B + (N/(S/NP)) {\P x.(P(x) & money(x))} + -------->T + (S/(S\NP)) {\F.F(I)} + --------------------------------------------------> + ((S\NP)/NP) {\y z.give(y,them,z)} + ---------------------------------------------------------->B + (S/NP) {\y.give(y,them,I)} + ------------------------------------------------------------------------------------------------------------> + N {\x.(give(x,them,I) & money(x))} + + +------- +Lexicon +------- + + >>> from nltk.ccg import lexicon + +Parse lexicon with semantics + + >>> print(str(lexicon.fromstring( + ... ''' + ... :- S,NP + ... + ... IntransVsg :: S\\NP[sg] + ... + ... sleeps => IntransVsg {\\x.sleep(x)} + ... eats => S\\NP[sg]/NP {\\x y.eat(x,y)} + ... + ... and => var\\var/var {\\x y.x & y} + ... ''', + ... True + ... ))) + and => ((_var0\_var0)/_var0) {(\x y.x & y)} + eats => ((S\NP['sg'])/NP) {\x y.eat(x,y)} + sleeps => (S\NP['sg']) {\x.sleep(x)} + +Parse lexicon without semantics + + >>> print(str(lexicon.fromstring( + ... ''' + ... :- S,NP + ... + ... IntransVsg :: S\\NP[sg] + ... + ... sleeps => IntransVsg + ... eats => S\\NP[sg]/NP {sem=\\x y.eat(x,y)} + ... + ... and => var\\var/var + ... ''', + ... False + ... ))) + and => ((_var0\_var0)/_var0) + eats => ((S\NP['sg'])/NP) + sleeps => (S\NP['sg']) + +Semantics are missing + + >>> print(str(lexicon.fromstring( + ... ''' + ... :- S,NP + ... + ... eats => S\\NP[sg]/NP + ... ''', + ... True + ... ))) + Traceback (most recent call last): + ... + AssertionError: eats => S\NP[sg]/NP must contain semantics because include_semantics is set to True + + +------------------------------------ +CCG combinator semantics computation +------------------------------------ + + >>> from nltk.sem.logic import * + >>> from nltk.ccg.logic import * + + >>> read_expr = Expression.fromstring + +Compute semantics from function application + + >>> print(str(compute_function_semantics(read_expr(r'\x.P(x)'), read_expr(r'book')))) + P(book) + + >>> print(str(compute_function_semantics(read_expr(r'\P.P(book)'), read_expr(r'read')))) + read(book) + + >>> print(str(compute_function_semantics(read_expr(r'\P.P(book)'), read_expr(r'\x.read(x)')))) + read(book) + +Compute semantics from composition + + >>> print(str(compute_composition_semantics(read_expr(r'\x.P(x)'), read_expr(r'\x.Q(x)')))) + \x.P(Q(x)) + + >>> print(str(compute_composition_semantics(read_expr(r'\x.P(x)'), read_expr(r'read')))) + Traceback (most recent call last): + ... + AssertionError: `read` must be a lambda expression + +Compute semantics from substitution + + >>> print(str(compute_substitution_semantics(read_expr(r'\x y.P(x,y)'), read_expr(r'\x.Q(x)')))) + \x.P(x,Q(x)) + + >>> print(str(compute_substitution_semantics(read_expr(r'\x.P(x)'), read_expr(r'read')))) + Traceback (most recent call last): + ... + AssertionError: `\x.P(x)` must be a lambda expression with 2 arguments + +Compute type-raise semantics + + >>> print(str(compute_type_raised_semantics(read_expr(r'\x.P(x)')))) + \F x.F(P(x)) + + >>> print(str(compute_type_raised_semantics(read_expr(r'\x.F(x)')))) + \F1 x.F1(F(x)) + + >>> print(str(compute_type_raised_semantics(read_expr(r'\x y z.P(x,y,z)')))) + \F x y z.F(P(x,y,z)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/chat80.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/chat80.doctest new file mode 100644 index 0000000..b9eefd8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/chat80.doctest @@ -0,0 +1,232 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======= +Chat-80 +======= + +Chat-80 was a natural language system which allowed the user to +interrogate a Prolog knowledge base in the domain of world +geography. It was developed in the early '80s by Warren and Pereira; see +``_ for a description and +``_ for the source +files. + +The ``chat80`` module contains functions to extract data from the Chat-80 +relation files ('the world database'), and convert then into a format +that can be incorporated in the FOL models of +``nltk.sem.evaluate``. The code assumes that the Prolog +input files are available in the NLTK corpora directory. + +The Chat-80 World Database consists of the following files:: + + world0.pl + rivers.pl + cities.pl + countries.pl + contain.pl + borders.pl + +This module uses a slightly modified version of ``world0.pl``, in which +a set of Prolog rules have been omitted. The modified file is named +``world1.pl``. Currently, the file ``rivers.pl`` is not read in, since +it uses a list rather than a string in the second field. + +Reading Chat-80 Files +===================== + +Chat-80 relations are like tables in a relational database. The +relation acts as the name of the table; the first argument acts as the +'primary key'; and subsequent arguments are further fields in the +table. In general, the name of the table provides a label for a unary +predicate whose extension is all the primary keys. For example, +relations in ``cities.pl`` are of the following form:: + + 'city(athens,greece,1368).' + +Here, ``'athens'`` is the key, and will be mapped to a member of the +unary predicate *city*. + +By analogy with NLTK corpora, ``chat80`` defines a number of 'items' +which correspond to the relations. + + >>> from nltk.sem import chat80 + >>> print(chat80.items) + ('borders', 'circle_of_lat', 'circle_of_long', 'city', ...) + +The fields in the table are mapped to binary predicates. The first +argument of the predicate is the primary key, while the second +argument is the data in the relevant field. Thus, in the above +example, the third field is mapped to the binary predicate +*population_of*, whose extension is a set of pairs such as +``'(athens, 1368)'``. + +An exception to this general framework is required by the relations in +the files ``borders.pl`` and ``contains.pl``. These contain facts of the +following form:: + + 'borders(albania,greece).' + + 'contains0(africa,central_africa).' + +We do not want to form a unary concept out the element in +the first field of these records, and we want the label of the binary +relation just to be ``'border'``/``'contain'`` respectively. + +In order to drive the extraction process, we use 'relation metadata bundles' +which are Python dictionaries such as the following:: + + city = {'label': 'city', + 'closures': [], + 'schema': ['city', 'country', 'population'], + 'filename': 'cities.pl'} + +According to this, the file ``city['filename']`` contains a list of +relational tuples (or more accurately, the corresponding strings in +Prolog form) whose predicate symbol is ``city['label']`` and whose +relational schema is ``city['schema']``. The notion of a ``closure`` is +discussed in the next section. + +Concepts +======== +In order to encapsulate the results of the extraction, a class of +``Concept``\ s is introduced. A ``Concept`` object has a number of +attributes, in particular a ``prefLabel``, an arity and ``extension``. + + >>> c1 = chat80.Concept('dog', arity=1, extension=set(['d1', 'd2'])) + >>> print(c1) + Label = 'dog' + Arity = 1 + Extension = ['d1', 'd2'] + + + +The ``extension`` attribute makes it easier to inspect the output of +the extraction. + + >>> schema = ['city', 'country', 'population'] + >>> concepts = chat80.clause2concepts('cities.pl', 'city', schema) + >>> concepts + [Concept('city'), Concept('country_of'), Concept('population_of')] + >>> for c in concepts: + ... print("%s:\n\t%s" % (c.prefLabel, c.extension[:4])) + city: + ['athens', 'bangkok', 'barcelona', 'berlin'] + country_of: + [('athens', 'greece'), ('bangkok', 'thailand'), ('barcelona', 'spain'), ('berlin', 'east_germany')] + population_of: + [('athens', '1368'), ('bangkok', '1178'), ('barcelona', '1280'), ('berlin', '3481')] + +In addition, the ``extension`` can be further +processed: in the case of the ``'border'`` relation, we check that the +relation is **symmetric**, and in the case of the ``'contain'`` +relation, we carry out the **transitive closure**. The closure +properties associated with a concept is indicated in the relation +metadata, as indicated earlier. + + >>> borders = set([('a1', 'a2'), ('a2', 'a3')]) + >>> c2 = chat80.Concept('borders', arity=2, extension=borders) + >>> print(c2) + Label = 'borders' + Arity = 2 + Extension = [('a1', 'a2'), ('a2', 'a3')] + >>> c3 = chat80.Concept('borders', arity=2, closures=['symmetric'], extension=borders) + >>> c3.close() + >>> print(c3) + Label = 'borders' + Arity = 2 + Extension = [('a1', 'a2'), ('a2', 'a1'), ('a2', 'a3'), ('a3', 'a2')] + +The ``extension`` of a ``Concept`` object is then incorporated into a +``Valuation`` object. + +Persistence +=========== +The functions ``val_dump`` and ``val_load`` are provided to allow a +valuation to be stored in a persistent database and re-loaded, rather +than having to be re-computed each time. + +Individuals and Lexical Items +============================= +As well as deriving relations from the Chat-80 data, we also create a +set of individual constants, one for each entity in the domain. The +individual constants are string-identical to the entities. For +example, given a data item such as ``'zloty'``, we add to the valuation +a pair ``('zloty', 'zloty')``. In order to parse English sentences that +refer to these entities, we also create a lexical item such as the +following for each individual constant:: + + PropN[num=sg, sem=<\P.(P zloty)>] -> 'Zloty' + +The set of rules is written to the file ``chat_pnames.fcfg`` in the +current directory. + +SQL Query +========= + +The ``city`` relation is also available in RDB form and can be queried +using SQL statements. + + >>> import nltk + >>> q = "SELECT City, Population FROM city_table WHERE Country = 'china' and Population > 1000" + >>> for answer in chat80.sql_query('corpora/city_database/city.db', q): + ... print("%-10s %4s" % answer) + canton 1496 + chungking 1100 + mukden 1551 + peking 2031 + shanghai 5407 + tientsin 1795 + +The (deliberately naive) grammar ``sql.fcfg`` translates from English +to SQL: + + >>> nltk.data.show_cfg('grammars/book_grammars/sql0.fcfg') + % start S + S[SEM=(?np + WHERE + ?vp)] -> NP[SEM=?np] VP[SEM=?vp] + VP[SEM=(?v + ?pp)] -> IV[SEM=?v] PP[SEM=?pp] + VP[SEM=(?v + ?ap)] -> IV[SEM=?v] AP[SEM=?ap] + NP[SEM=(?det + ?n)] -> Det[SEM=?det] N[SEM=?n] + PP[SEM=(?p + ?np)] -> P[SEM=?p] NP[SEM=?np] + AP[SEM=?pp] -> A[SEM=?a] PP[SEM=?pp] + NP[SEM='Country="greece"'] -> 'Greece' + NP[SEM='Country="china"'] -> 'China' + Det[SEM='SELECT'] -> 'Which' | 'What' + N[SEM='City FROM city_table'] -> 'cities' + IV[SEM=''] -> 'are' + A[SEM=''] -> 'located' + P[SEM=''] -> 'in' + +Given this grammar, we can express, and then execute, queries in English. + + >>> cp = nltk.parse.load_parser('grammars/book_grammars/sql0.fcfg') + >>> query = 'What cities are in China' + >>> for tree in cp.parse(query.split()): + ... answer = tree.label()['SEM'] + ... q = " ".join(answer) + ... print(q) + ... + SELECT City FROM city_table WHERE Country="china" + + >>> rows = chat80.sql_query('corpora/city_database/city.db', q) + >>> for r in rows: print("%s" % r, end=' ') + canton chungking dairen harbin kowloon mukden peking shanghai sian tientsin + + +Using Valuations +----------------- + +In order to convert such an extension into a valuation, we use the +``make_valuation()`` method; setting ``read=True`` creates and returns +a new ``Valuation`` object which contains the results. + + >>> val = chat80.make_valuation(concepts, read=True) + >>> 'calcutta' in val['city'] + True + >>> [town for (town, country) in val['country_of'] if country == 'india'] + ['bombay', 'calcutta', 'delhi', 'hyderabad', 'madras'] + >>> dom = val.domain + >>> g = nltk.sem.Assignment(dom) + >>> m = nltk.sem.Model(dom, val) + >>> m.evaluate(r'population_of(jakarta, 533)', g) + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes.doctest new file mode 100644 index 0000000..c0703fc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes.doctest @@ -0,0 +1,190 @@ +======================= + CHILDES Corpus Readers +======================= + +Read the XML version of the CHILDES corpus. + +Setup +===== + + >>> from nltk.test.childes_fixt import setup_module + >>> setup_module() + +How to use CHILDESCorpusReader +============================== + +Read the CHILDESCorpusReader class and read the CHILDES corpus saved in +the nltk_data directory. + + >>> import nltk + >>> from nltk.corpus.reader import CHILDESCorpusReader + >>> corpus_root = nltk.data.find('corpora/childes/data-xml/Eng-USA-MOR/') + +Reading files in the Valian corpus (Valian, 1991). + + >>> valian = CHILDESCorpusReader(corpus_root, 'Valian/.*.xml') + >>> valian.fileids() + ['Valian/01a.xml', 'Valian/01b.xml', 'Valian/02a.xml', 'Valian/02b.xml',... + +Count the number of files + + >>> len(valian.fileids()) + 43 + +Printing properties of the corpus files. + + >>> corpus_data = valian.corpus(valian.fileids()) + >>> print(corpus_data[0]['Lang']) + eng + >>> for key in sorted(corpus_data[0].keys()): + ... print(key, ": ", corpus_data[0][key]) + Corpus : valian + Date : 1986-03-04 + Id : 01a + Lang : eng + Version : 2.0.1 + {http://www.w3.org/2001/XMLSchema-instance}schemaLocation : http://www.talkbank.org/ns/talkbank http://talkbank.org/software/talkbank.xsd + +Printing information of participants of the corpus. The most common codes for +the participants are 'CHI' (target child), 'MOT' (mother), and 'INV' (investigator). + + >>> corpus_participants = valian.participants(valian.fileids()) + >>> for this_corpus_participants in corpus_participants[:2]: + ... for key in sorted(this_corpus_participants.keys()): + ... dct = this_corpus_participants[key] + ... print(key, ": ", [(k, dct[k]) for k in sorted(dct.keys())]) + CHI : [('age', 'P2Y1M3D'), ('group', 'normal'), ('id', 'CHI'), ('language', 'eng'), ('role', 'Target_Child'), ('sex', 'female')] + INV : [('id', 'INV'), ('language', 'eng'), ('role', 'Investigator')] + MOT : [('id', 'MOT'), ('language', 'eng'), ('role', 'Mother')] + CHI : [('age', 'P2Y1M12D'), ('group', 'normal'), ('id', 'CHI'), ('language', 'eng'), ('role', 'Target_Child'), ('sex', 'female')] + INV : [('id', 'INV'), ('language', 'eng'), ('role', 'Investigator')] + MOT : [('id', 'MOT'), ('language', 'eng'), ('role', 'Mother')] + +printing words. + + >>> valian.words('Valian/01a.xml') + ['at', 'Parent', "Lastname's", 'house', 'with', 'Child', 'Lastname', ... + +printing sentences. + + >>> valian.sents('Valian/01a.xml') + [['at', 'Parent', "Lastname's", 'house', 'with', 'Child', 'Lastname', + 'and', 'it', 'is', 'March', 'fourth', 'I', 'believe', 'and', 'when', + 'was', "Parent's", 'birthday'], ["Child's"], ['oh', "I'm", 'sorry'], + ["that's", 'okay'], ... + +You can specify the participants with the argument *speaker*. + + >>> valian.words('Valian/01a.xml',speaker=['INV']) + ['at', 'Parent', "Lastname's", 'house', 'with', 'Child', 'Lastname', ... + >>> valian.words('Valian/01a.xml',speaker=['MOT']) + ["Child's", "that's", 'okay', 'February', 'first', 'nineteen', ... + >>> valian.words('Valian/01a.xml',speaker=['CHI']) + ['tape', 'it', 'up', 'and', 'two', 'tape', 'players', 'have',... + + +tagged_words() and tagged_sents() return the usual (word,pos) tuple lists. +POS tags in the CHILDES are automatically assigned by MOR and POST programs +(MacWhinney, 2000). + + >>> valian.tagged_words('Valian/01a.xml')[:30] + [('at', 'prep'), ('Parent', 'n:prop'), ("Lastname's", 'n:prop'), ('house', 'n'), + ('with', 'prep'), ('Child', 'n:prop'), ('Lastname', 'n:prop'), ('and', 'coord'), + ('it', 'pro'), ('is', 'v:cop'), ('March', 'n:prop'), ('fourth', 'adj'), + ('I', 'pro:sub'), ('believe', 'v'), ('and', 'coord'), ('when', 'adv:wh'), + ('was', 'v:cop'), ("Parent's", 'n:prop'), ('birthday', 'n'), ("Child's", 'n:prop'), + ('oh', 'co'), ("I'm", 'pro:sub'), ('sorry', 'adj'), ("that's", 'pro:dem'), + ('okay', 'adj'), ('February', 'n:prop'), ('first', 'adj'), + ('nineteen', 'det:num'), ('eighty', 'det:num'), ('four', 'det:num')] + + >>> valian.tagged_sents('Valian/01a.xml')[:10] + [[('at', 'prep'), ('Parent', 'n:prop'), ("Lastname's", 'n:prop'), ('house', 'n'), + ('with', 'prep'), ('Child', 'n:prop'), ('Lastname', 'n:prop'), ('and', 'coord'), + ('it', 'pro'), ('is', 'v:cop'), ('March', 'n:prop'), ('fourth', 'adj'), + ('I', 'pro:sub'), ('believe', 'v'), ('and', 'coord'), ('when', 'adv:wh'), + ('was', 'v:cop'), ("Parent's", 'n:prop'), ('birthday', 'n')], + [("Child's", 'n:prop')], [('oh', 'co'), ("I'm", 'pro:sub'), ('sorry', 'adj')], + [("that's", 'pro:dem'), ('okay', 'adj')], + [('February', 'n:prop'), ('first', 'adj'), ('nineteen', 'det:num'), + ('eighty', 'det:num'), ('four', 'det:num')], + [('great', 'adj')], + [('and', 'coord'), ("she's", 'pro:sub'), ('two', 'det:num'), ('years', 'n'), ('old', 'adj')], + [('correct', 'adj')], + [('okay', 'co')], [('she', 'pro:sub'), ('just', 'adv:int'), ('turned', 'part'), ('two', 'det:num'), + ('a', 'det'), ('month', 'n'), ('ago', 'adv')]] + +When the argument *stem* is true, the word stems (e.g., 'is' -> 'be-3PS') are +used instead of the original words. + + >>> valian.words('Valian/01a.xml')[:30] + ['at', 'Parent', "Lastname's", 'house', 'with', 'Child', 'Lastname', 'and', 'it', 'is', ... + >>> valian.words('Valian/01a.xml',stem=True)[:30] + ['at', 'Parent', 'Lastname', 's', 'house', 'with', 'Child', 'Lastname', 'and', 'it', 'be-3S', ... + +When the argument *replace* is true, the replaced words are used instead of +the original words. + + >>> valian.words('Valian/01a.xml',speaker='CHI')[247] + 'tikteat' + >>> valian.words('Valian/01a.xml',speaker='CHI',replace=True)[247] + 'trick' + +When the argument *relation* is true, the relational relationships in the +sentence are returned. See Sagae et al. (2010) for details of the relational +structure adopted in the CHILDES. + + >>> valian.words('Valian/01a.xml',relation=True)[:10] + [[('at', 'prep', '1|0|ROOT'), ('Parent', 'n', '2|5|VOC'), ('Lastname', 'n', '3|5|MOD'), ('s', 'poss', '4|5|MOD'), ('house', 'n', '5|1|POBJ'), ('with', 'prep', '6|1|JCT'), ('Child', 'n', '7|8|NAME'), ('Lastname', 'n', '8|6|POBJ'), ('and', 'coord', '9|8|COORD'), ('it', 'pro', '10|11|SUBJ'), ('be-3S', 'v', '11|9|COMP'), ('March', 'n', '12|11|PRED'), ('fourth', 'adj', '13|12|MOD'), ('I', 'pro', '15|16|SUBJ'), ('believe', 'v', '16|14|ROOT'), ('and', 'coord', '18|17|ROOT'), ('when', 'adv', '19|20|PRED'), ('be-PAST', 'v', '20|18|COMP'), ('Parent', 'n', '21|23|MOD'), ('s', 'poss', '22|23|MOD'), ('birth', 'n', '23|20|SUBJ')], [('Child', 'n', '1|2|MOD'), ('s', 'poss', '2|0|ROOT')], [('oh', 'co', '1|4|COM'), ('I', 'pro', '3|4|SUBJ'), ('be', 'v', '4|0|ROOT'), ('sorry', 'adj', '5|4|PRED')], [('that', 'pro', '1|2|SUBJ'), ('be', 'v', '2|0|ROOT'), ('okay', 'adj', '3|2|PRED')], [('February', 'n', '1|6|VOC'), ('first', 'adj', '2|6|ENUM'), ('nineteen', 'det', '4|6|ENUM'), ('eighty', 'det', '5|6|ENUM'), ('four', 'det', '6|0|ROOT')], [('great', 'adj', '1|0|ROOT')], [('and', 'coord', '1|0|ROOT'), ('she', 'pro', '2|1|ROOT'), ('be', 'aux', '3|5|AUX'), ('two', 'det', '4|5|QUANT'), ('year-PL', 'n', '5|2|ROOT'), ('old', 'adj', '6|5|MOD')], [('correct', 'adj', '1|0|ROOT')], [('okay', 'co', '1|0|ROOT')], [('she', 'pro', '1|0|ROOT'), ('just', 'adv', '2|3|JCT'), ('turn-PERF', 'part', '3|1|XCOMP'), ('two', 'det', '4|6|QUANT'), ('a', 'det', '5|6|DET'), ('month', 'n', '6|3|OBJ'), ('ago', 'adv', '7|3|JCT')]] + +Printing age. When the argument *month* is true, the age information in +the CHILDES format is converted into the number of months. + + >>> valian.age() + ['P2Y1M3D', 'P2Y1M12D', 'P1Y9M21D', 'P1Y9M28D', 'P2Y1M23D', ... + >>> valian.age('Valian/01a.xml') + ['P2Y1M3D'] + >>> valian.age('Valian/01a.xml',month=True) + [25] + +Printing MLU. The criteria for the MLU computation is broadly based on +Brown (1973). + + >>> valian.MLU() + [2.3574660633484..., 2.292682926829..., 3.492857142857..., 2.961783439490..., + 2.0842696629213..., 3.169811320754..., 3.137404580152..., 3.0578034682080..., + 4.090163934426..., 3.488372093023..., 2.8773584905660..., 3.4792899408284..., + 4.0111940298507..., 3.456790123456..., 4.487603305785..., 4.007936507936..., + 5.25, 5.154696132596..., ...] + + >>> valian.MLU('Valian/01a.xml') + [2.35746606334...] + + +Basic stuff +============================== + +Count the number of words and sentences of each file. + + >>> valian = CHILDESCorpusReader(corpus_root, 'Valian/.*.xml') + >>> for this_file in valian.fileids()[:6]: + ... print(valian.corpus(this_file)[0]['Corpus'], valian.corpus(this_file)[0]['Id']) + ... print("num of words: %i" % len(valian.words(this_file))) + ... print("num of sents: %i" % len(valian.sents(this_file))) + valian 01a + num of words: 3606 + num of sents: 1027 + valian 01b + num of words: 4376 + num of sents: 1274 + valian 02a + num of words: 2673 + num of sents: 801 + valian 02b + num of words: 5020 + num of sents: 1583 + valian 03a + num of words: 2743 + num of sents: 988 + valian 03b + num of words: 4409 + num of sents: 1397 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes_fixt.py new file mode 100644 index 0000000..3e9a69e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/childes_fixt.py @@ -0,0 +1,13 @@ +def setup_module(): + import pytest + + import nltk.data + + try: + nltk.data.find("corpora/childes/data-xml/Eng-USA-MOR/") + except LookupError as e: + pytest.skip( + "The CHILDES corpus is not found. " + "It should be manually downloaded and saved/unpacked " + "to [NLTK_Data_Dir]/corpora/childes/" + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/chunk.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/chunk.doctest new file mode 100644 index 0000000..5ebe868 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/chunk.doctest @@ -0,0 +1,372 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========== + Chunking +========== + + >>> from nltk.chunk import * + >>> from nltk.chunk.util import * + >>> from nltk.chunk.regexp import * + >>> from nltk import Tree + + >>> tagged_text = "[ The/DT cat/NN ] sat/VBD on/IN [ the/DT mat/NN ] [ the/DT dog/NN ] chewed/VBD ./." + >>> gold_chunked_text = tagstr2tree(tagged_text) + >>> unchunked_text = gold_chunked_text.flatten() + +Chunking uses a special regexp syntax for rules that delimit the chunks. These +rules must be converted to 'regular' regular expressions before a sentence can +be chunked. + + >>> tag_pattern = "
    ?*" + >>> regexp_pattern = tag_pattern2re_pattern(tag_pattern) + >>> regexp_pattern + '(<(DT)>)?(<(JJ)>)*(<(NN[^\\{\\}<>]*)>)' + +Construct some new chunking rules. + + >>> chunk_rule = ChunkRule(r"<.*>+", "Chunk everything") + >>> strip_rule = StripRule(r"", "Strip on verbs/prepositions") + >>> split_rule = SplitRule("
    ", "
    ", + ... "Split successive determiner/noun pairs") + + +Create and score a series of chunk parsers, successively more complex. + + >>> chunk_parser = RegexpChunkParser([chunk_rule], chunk_label='NP') + >>> chunked_text = chunk_parser.parse(unchunked_text) + >>> print(chunked_text) + (S + (NP + The/DT + cat/NN + sat/VBD + on/IN + the/DT + mat/NN + the/DT + dog/NN + chewed/VBD + ./.)) + + >>> chunkscore = ChunkScore() + >>> chunkscore.score(gold_chunked_text, chunked_text) + >>> print(chunkscore.precision()) + 0.0 + + >>> print(chunkscore.recall()) + 0.0 + + >>> print(chunkscore.f_measure()) + 0 + + >>> for chunk in sorted(chunkscore.missed()): print(chunk) + (NP The/DT cat/NN) + (NP the/DT dog/NN) + (NP the/DT mat/NN) + + >>> for chunk in chunkscore.incorrect(): print(chunk) + (NP + The/DT + cat/NN + sat/VBD + on/IN + the/DT + mat/NN + the/DT + dog/NN + chewed/VBD + ./.) + + >>> chunk_parser = RegexpChunkParser([chunk_rule, strip_rule], + ... chunk_label='NP') + >>> chunked_text = chunk_parser.parse(unchunked_text) + >>> print(chunked_text) + (S + (NP The/DT cat/NN) + sat/VBD + on/IN + (NP the/DT mat/NN the/DT dog/NN) + chewed/VBD + ./.) + >>> assert chunked_text == chunk_parser.parse(list(unchunked_text)) + + >>> chunkscore = ChunkScore() + >>> chunkscore.score(gold_chunked_text, chunked_text) + >>> chunkscore.precision() + 0.5 + + >>> print(chunkscore.recall()) + 0.33333333... + + >>> print(chunkscore.f_measure()) + 0.4 + + >>> for chunk in sorted(chunkscore.missed()): print(chunk) + (NP the/DT dog/NN) + (NP the/DT mat/NN) + + >>> for chunk in chunkscore.incorrect(): print(chunk) + (NP the/DT mat/NN the/DT dog/NN) + + >>> chunk_parser = RegexpChunkParser([chunk_rule, strip_rule, split_rule], + ... chunk_label='NP') + >>> chunked_text = chunk_parser.parse(unchunked_text, trace=True) + # Input: +
    <.> + # Chunk everything: + {
    <.>} + # Strip on verbs/prepositions: + {
    } {
    } <.> + # Split successive determiner/noun pairs: + {
    } {
    }{
    } <.> + >>> print(chunked_text) + (S + (NP The/DT cat/NN) + sat/VBD + on/IN + (NP the/DT mat/NN) + (NP the/DT dog/NN) + chewed/VBD + ./.) + + >>> chunkscore = ChunkScore() + >>> chunkscore.score(gold_chunked_text, chunked_text) + >>> chunkscore.precision() + 1.0 + + >>> chunkscore.recall() + 1.0 + + >>> chunkscore.f_measure() + 1.0 + + >>> chunkscore.missed() + [] + + >>> chunkscore.incorrect() + [] + + >>> chunk_parser.rules() + [+'>, '>, + ', '
    '>] + +Printing parsers: + + >>> print(repr(chunk_parser)) + + >>> print(chunk_parser) + RegexpChunkParser with 3 rules: + Chunk everything + +'> + Strip on verbs/prepositions + '> + Split successive determiner/noun pairs + ', '
    '> + +Regression Tests +~~~~~~~~~~~~~~~~ +ChunkParserI +------------ +`ChunkParserI` is an abstract interface -- it is not meant to be +instantiated directly. + + >>> ChunkParserI().parse([]) + Traceback (most recent call last): + . . . + NotImplementedError + + +ChunkString +----------- +ChunkString can be built from a tree of tagged tuples, a tree of +trees, or a mixed list of both: + + >>> t1 = Tree('S', [('w%d' % i, 't%d' % i) for i in range(10)]) + >>> t2 = Tree('S', [Tree('t0', []), Tree('t1', ['c1'])]) + >>> t3 = Tree('S', [('w0', 't0'), Tree('t1', ['c1'])]) + >>> ChunkString(t1) + '> + >>> ChunkString(t2) + '> + >>> ChunkString(t3) + '> + +Other values generate an error: + + >>> ChunkString(Tree('S', ['x'])) + Traceback (most recent call last): + . . . + ValueError: chunk structures must contain tagged tokens or trees + +The `str()` for a chunk string adds spaces to it, which makes it line +up with `str()` output for other chunk strings over the same +underlying input. + + >>> cs = ChunkString(t1) + >>> print(cs) + + >>> cs.xform('', '{}') + >>> print(cs) + {} + +The `_verify()` method makes sure that our transforms don't corrupt +the chunk string. By setting debug_level=2, `_verify()` will be +called at the end of every call to `xform`. + + >>> cs = ChunkString(t1, debug_level=3) + + >>> # tag not marked with <...>: + >>> cs.xform('', 't3') + Traceback (most recent call last): + . . . + ValueError: Transformation generated invalid chunkstring: + t3 + + >>> # brackets not balanced: + >>> cs.xform('', '{') + Traceback (most recent call last): + . . . + ValueError: Transformation generated invalid chunkstring: + { + + >>> # nested brackets: + >>> cs.xform('', '{{}}') + Traceback (most recent call last): + . . . + ValueError: Transformation generated invalid chunkstring: + {{}} + + >>> # modified tags: + >>> cs.xform('', '') + Traceback (most recent call last): + . . . + ValueError: Transformation generated invalid chunkstring: tag changed + + >>> # added tags: + >>> cs.xform('', '') + Traceback (most recent call last): + . . . + ValueError: Transformation generated invalid chunkstring: tag changed + +Chunking Rules +-------------- + +Test the different rule constructors & __repr__ methods: + + >>> r1 = RegexpChunkRule(''+ChunkString.IN_STRIP_PATTERN, + ... '{}', 'chunk and ') + >>> r2 = RegexpChunkRule(re.compile(''+ChunkString.IN_STRIP_PATTERN), + ... '{}', 'chunk and ') + >>> r3 = ChunkRule('', 'chunk and ') + >>> r4 = StripRule('', 'strip and ') + >>> r5 = UnChunkRule('', 'unchunk and ') + >>> r6 = MergeRule('', '', 'merge w/ ') + >>> r7 = SplitRule('', '', 'split from ') + >>> r8 = ExpandLeftRule('', '', 'expand left ') + >>> r9 = ExpandRightRule('', '', 'expand right ') + >>> for rule in r1, r2, r3, r4, r5, r6, r7, r8, r9: + ... print(rule) + (?=[^\\}]*(\\{|$))'->'{}'> + (?=[^\\}]*(\\{|$))'->'{}'> + '> + '> + '> + ', ''> + ', ''> + ', ''> + ', ''> + +`tag_pattern2re_pattern()` complains if the tag pattern looks problematic: + + >>> tag_pattern2re_pattern('{}') + Traceback (most recent call last): + . . . + ValueError: Bad tag pattern: '{}' + +RegexpChunkParser +----------------- + +A warning is printed when parsing an empty sentence: + + >>> parser = RegexpChunkParser([ChunkRule('', '')]) + >>> parser.parse(Tree('S', [])) + Warning: parsing empty text + Tree('S', []) + +RegexpParser +------------ + + >>> parser = RegexpParser(''' + ... NP: {
    ? * *} # NP + ... P: {} # Preposition + ... V: {} # Verb + ... PP: {

    } # PP -> P NP + ... VP: { *} # VP -> V (NP|PP)* + ... ''') + >>> print(repr(parser)) + + >>> print(parser) + chunk.RegexpParser with 5 stages: + RegexpChunkParser with 1 rules: + NP ? * *'> + RegexpChunkParser with 1 rules: + Preposition '> + RegexpChunkParser with 1 rules: + Verb '> + RegexpChunkParser with 1 rules: + PP -> P NP '> + RegexpChunkParser with 1 rules: + VP -> V (NP|PP)* *'> + >>> print(parser.parse(unchunked_text, trace=True)) + # Input: +

    <.> + # NP: + {
    } {
    }{
    } <.> + # Input: + <.> + # Preposition: + {} <.> + # Input: +

    <.> + # Verb: + {}

    {} <.> + # Input: +

    <.> + # PP -> P NP: + {

    } <.> + # Input: + <.> + # VP -> V (NP|PP)*: + { }{} <.> + (S + (NP The/DT cat/NN) + (VP + (V sat/VBD) + (PP (P on/IN) (NP the/DT mat/NN)) + (NP the/DT dog/NN)) + (VP (V chewed/VBD)) + ./.) + +Test parsing of other rule types: + + >>> print(RegexpParser(''' + ... X: + ... }{ # strip rule + ... }{ # split rule + ... {} # merge rule + ... {} # chunk rule w/ context + ... ''')) + chunk.RegexpParser with 1 stages: + RegexpChunkParser with 4 rules: + strip rule '> + split rule ', ''> + merge rule ', ''> + chunk rule w/ context ', '', ''> + +Illegal patterns give an error message: + + >>> print(RegexpParser('X: {} {}')) + Traceback (most recent call last): + . . . + ValueError: Illegal chunk pattern: {} {} diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify.doctest new file mode 100644 index 0000000..de2767c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify.doctest @@ -0,0 +1,202 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============= + Classifiers +============= + + >>> from nltk.test.classify_fixt import setup_module + >>> setup_module() + +Classifiers label tokens with category labels (or *class labels*). +Typically, labels are represented with strings (such as ``"health"`` +or ``"sports"``. In NLTK, classifiers are defined using classes that +implement the `ClassifierI` interface, which supports the following operations: + +- self.classify(featureset) +- self.classify_many(featuresets) +- self.labels() +- self.prob_classify(featureset) +- self.prob_classify_many(featuresets) + +NLTK defines several classifier classes: + +- `ConditionalExponentialClassifier` +- `DecisionTreeClassifier` +- `MaxentClassifier` +- `NaiveBayesClassifier` +- `WekaClassifier` + +Classifiers are typically created by training them on a training +corpus. + + +Regression Tests +~~~~~~~~~~~~~~~~ + +We define a very simple training corpus with 3 binary features: ['a', +'b', 'c'], and are two labels: ['x', 'y']. We use a simple feature set so +that the correct answers can be calculated analytically (although we +haven't done this yet for all tests). + + >>> import nltk + >>> train = [ + ... (dict(a=1,b=1,c=1), 'y'), + ... (dict(a=1,b=1,c=1), 'x'), + ... (dict(a=1,b=1,c=0), 'y'), + ... (dict(a=0,b=1,c=1), 'x'), + ... (dict(a=0,b=1,c=1), 'y'), + ... (dict(a=0,b=0,c=1), 'y'), + ... (dict(a=0,b=1,c=0), 'x'), + ... (dict(a=0,b=0,c=0), 'x'), + ... (dict(a=0,b=1,c=1), 'y'), + ... (dict(a=None,b=1,c=0), 'x'), + ... ] + >>> test = [ + ... (dict(a=1,b=0,c=1)), # unseen + ... (dict(a=1,b=0,c=0)), # unseen + ... (dict(a=0,b=1,c=1)), # seen 3 times, labels=y,y,x + ... (dict(a=0,b=1,c=0)), # seen 1 time, label=x + ... ] + +Test the Naive Bayes classifier: + + >>> classifier = nltk.classify.NaiveBayesClassifier.train(train) + >>> sorted(classifier.labels()) + ['x', 'y'] + >>> classifier.classify_many(test) + ['y', 'x', 'y', 'x'] + >>> for pdist in classifier.prob_classify_many(test): + ... print('%.4f %.4f' % (pdist.prob('x'), pdist.prob('y'))) + 0.2500 0.7500 + 0.5833 0.4167 + 0.3571 0.6429 + 0.7000 0.3000 + >>> classifier.show_most_informative_features() + Most Informative Features + c = 0 x : y = 2.3 : 1.0 + c = 1 y : x = 1.8 : 1.0 + a = 1 y : x = 1.7 : 1.0 + a = 0 x : y = 1.0 : 1.0 + b = 0 x : y = 1.0 : 1.0 + b = 1 x : y = 1.0 : 1.0 + +Test the Decision Tree classifier (without None): + + >>> classifier = nltk.classify.DecisionTreeClassifier.train( + ... train[:-1], entropy_cutoff=0, + ... support_cutoff=0) + >>> sorted(classifier.labels()) + ['x', 'y'] + >>> print(classifier) + c=0? .................................................. x + a=0? ................................................ x + a=1? ................................................ y + c=1? .................................................. y + + >>> classifier.classify_many(test) + ['y', 'y', 'y', 'x'] + >>> for pdist in classifier.prob_classify_many(test): + ... print('%.4f %.4f' % (pdist.prob('x'), pdist.prob('y'))) + Traceback (most recent call last): + . . . + NotImplementedError + + +Test the Decision Tree classifier (with None): + + >>> classifier = nltk.classify.DecisionTreeClassifier.train( + ... train, entropy_cutoff=0, + ... support_cutoff=0) + >>> sorted(classifier.labels()) + ['x', 'y'] + >>> print(classifier) + c=0? .................................................. x + a=0? ................................................ x + a=1? ................................................ y + a=None? ............................................. x + c=1? .................................................. y + + + +Test SklearnClassifier, which requires the scikit-learn package. + + >>> from nltk.classify import SklearnClassifier + >>> from sklearn.naive_bayes import BernoulliNB + >>> from sklearn.svm import SVC + >>> train_data = [({"a": 4, "b": 1, "c": 0}, "ham"), + ... ({"a": 5, "b": 2, "c": 1}, "ham"), + ... ({"a": 0, "b": 3, "c": 4}, "spam"), + ... ({"a": 5, "b": 1, "c": 1}, "ham"), + ... ({"a": 1, "b": 4, "c": 3}, "spam")] + >>> classif = SklearnClassifier(BernoulliNB()).train(train_data) + >>> test_data = [{"a": 3, "b": 2, "c": 1}, + ... {"a": 0, "b": 3, "c": 7}] + >>> classif.classify_many(test_data) + ['ham', 'spam'] + >>> classif = SklearnClassifier(SVC(), sparse=False).train(train_data) + >>> classif.classify_many(test_data) + ['ham', 'spam'] + +Test the Maximum Entropy classifier training algorithms; they should all +generate the same results. + + >>> def print_maxent_test_header(): + ... print(' '*11+''.join([' test[%s] ' % i + ... for i in range(len(test))])) + ... print(' '*11+' p(x) p(y)'*len(test)) + ... print('-'*(11+15*len(test))) + + >>> def test_maxent(algorithm): + ... print('%11s' % algorithm, end=' ') + ... try: + ... classifier = nltk.classify.MaxentClassifier.train( + ... train, algorithm, trace=0, max_iter=1000) + ... except Exception as e: + ... print('Error: %r' % e) + ... return + ... + ... for featureset in test: + ... pdist = classifier.prob_classify(featureset) + ... print('%8.2f%6.2f' % (pdist.prob('x'), pdist.prob('y')), end=' ') + ... print() + + >>> print_maxent_test_header(); test_maxent('GIS'); test_maxent('IIS') + test[0] test[1] test[2] test[3] + p(x) p(y) p(x) p(y) p(x) p(y) p(x) p(y) + ----------------------------------------------------------------------- + GIS 0.16 0.84 0.46 0.54 0.41 0.59 0.76 0.24 + IIS 0.16 0.84 0.46 0.54 0.41 0.59 0.76 0.24 + + >>> test_maxent('MEGAM'); test_maxent('TADM') # doctest: +SKIP + MEGAM 0.16 0.84 0.46 0.54 0.41 0.59 0.76 0.24 + TADM 0.16 0.84 0.46 0.54 0.41 0.59 0.76 0.24 + + + +Regression tests for TypedMaxentFeatureEncoding +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from nltk.classify import maxent + >>> train = [ + ... ({'a': 1, 'b': 1, 'c': 1}, 'y'), + ... ({'a': 5, 'b': 5, 'c': 5}, 'x'), + ... ({'a': 0.9, 'b': 0.9, 'c': 0.9}, 'y'), + ... ({'a': 5.5, 'b': 5.4, 'c': 5.3}, 'x'), + ... ({'a': 0.8, 'b': 1.2, 'c': 1}, 'y'), + ... ({'a': 5.1, 'b': 4.9, 'c': 5.2}, 'x') + ... ] + + >>> test = [ + ... {'a': 1, 'b': 0.8, 'c': 1.2}, + ... {'a': 5.2, 'b': 5.1, 'c': 5} + ... ] + + >>> encoding = maxent.TypedMaxentFeatureEncoding.train( + ... train, count_cutoff=3, alwayson_features=True) + + >>> classifier = maxent.MaxentClassifier.train( + ... train, bernoulli=False, encoding=encoding, trace=0) + + >>> classifier.classify_many(test) + ['y', 'x'] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify_fixt.py new file mode 100644 index 0000000..3ee9e74 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/classify_fixt.py @@ -0,0 +1,5 @@ +# most of classify.doctest requires numpy +def setup_module(): + import pytest + + pytest.importorskip("numpy") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/collections.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/collections.doctest new file mode 100644 index 0000000..03dd9af --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/collections.doctest @@ -0,0 +1,31 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=========== +Collections +=========== + + >>> import nltk + >>> from nltk.collections import * + +Trie +---- + +Trie can be pickled: + + >>> import pickle + >>> trie = nltk.collections.Trie(['a']) + >>> s = pickle.dumps(trie) + >>> pickle.loads(s) + {'a': {True: None}} + +LazyIteratorList +---------------- + +Fetching the length of a LazyIteratorList object does not throw a StopIteration exception: + + >>> lil = LazyIteratorList(i for i in range(1, 11)) + >>> lil[-1] + 10 + >>> len(lil) + 10 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/collocations.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/collocations.doctest new file mode 100644 index 0000000..7a4e902 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/collocations.doctest @@ -0,0 +1,307 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============== + Collocations +============== + +Overview +~~~~~~~~ + +Collocations are expressions of multiple words which commonly co-occur. For +example, the top ten bigram collocations in Genesis are listed below, as +measured using Pointwise Mutual Information. + + >>> import nltk + >>> from nltk.collocations import * + >>> bigram_measures = nltk.collocations.BigramAssocMeasures() + >>> trigram_measures = nltk.collocations.TrigramAssocMeasures() + >>> fourgram_measures = nltk.collocations.QuadgramAssocMeasures() + >>> finder = BigramCollocationFinder.from_words( + ... nltk.corpus.genesis.words('english-web.txt')) + >>> finder.nbest(bigram_measures.pmi, 10) + [('Allon', 'Bacuth'), ('Ashteroth', 'Karnaim'), ('Ben', 'Ammi'), + ('En', 'Mishpat'), ('Jegar', 'Sahadutha'), ('Salt', 'Sea'), + ('Whoever', 'sheds'), ('appoint', 'overseers'), ('aromatic', 'resin'), + ('cutting', 'instrument')] + +While these words are highly collocated, the expressions are also very +infrequent. Therefore it is useful to apply filters, such as ignoring all +bigrams which occur less than three times in the corpus: + + >>> finder.apply_freq_filter(3) + >>> finder.nbest(bigram_measures.pmi, 10) + [('Beer', 'Lahai'), ('Lahai', 'Roi'), ('gray', 'hairs'), + ('ewe', 'lambs'), ('Most', 'High'), ('many', 'colors'), + ('burnt', 'offering'), ('Paddan', 'Aram'), ('east', 'wind'), + ('living', 'creature')] + +We may similarly find collocations among tagged words: + + >>> finder = BigramCollocationFinder.from_words( + ... nltk.corpus.brown.tagged_words('ca01', tagset='universal')) + >>> finder.nbest(bigram_measures.pmi, 5) + [(('1,119', 'NUM'), ('votes', 'NOUN')), + (('1962', 'NUM'), ("governor's", 'NOUN')), + (('637', 'NUM'), ('E.', 'NOUN')), + (('Alpharetta', 'NOUN'), ('prison', 'NOUN')), + (('Bar', 'NOUN'), ('Association', 'NOUN'))] + +Or tags alone: + + >>> finder = BigramCollocationFinder.from_words(t for w, t in + ... nltk.corpus.brown.tagged_words('ca01', tagset='universal')) + >>> finder.nbest(bigram_measures.pmi, 10) + [('PRT', 'VERB'), ('PRON', 'VERB'), ('ADP', 'DET'), ('.', 'PRON'), ('DET', 'ADJ'), + ('CONJ', 'PRON'), ('ADP', 'NUM'), ('NUM', '.'), ('ADV', 'ADV'), ('VERB', 'ADV')] + +Or spanning intervening words: + + >>> finder = BigramCollocationFinder.from_words( + ... nltk.corpus.genesis.words('english-web.txt'), + ... window_size = 20) + >>> finder.apply_freq_filter(2) + >>> ignored_words = nltk.corpus.stopwords.words('english') + >>> finder.apply_word_filter(lambda w: len(w) < 3 or w.lower() in ignored_words) + >>> finder.nbest(bigram_measures.likelihood_ratio, 10) + [('chief', 'chief'), ('became', 'father'), ('years', 'became'), + ('hundred', 'years'), ('lived', 'became'), ('king', 'king'), + ('lived', 'years'), ('became', 'became'), ('chief', 'chiefs'), + ('hundred', 'became')] + +Finders +~~~~~~~ + +The collocations package provides collocation finders which by default +consider all ngrams in a text as candidate collocations: + + >>> text = "I do not like green eggs and ham, I do not like them Sam I am!" + >>> tokens = nltk.wordpunct_tokenize(text) + >>> finder = BigramCollocationFinder.from_words(tokens) + >>> scored = finder.score_ngrams(bigram_measures.raw_freq) + >>> sorted(bigram for bigram, score in scored) + [(',', 'I'), ('I', 'am'), ('I', 'do'), ('Sam', 'I'), ('am', '!'), + ('and', 'ham'), ('do', 'not'), ('eggs', 'and'), ('green', 'eggs'), + ('ham', ','), ('like', 'green'), ('like', 'them'), ('not', 'like'), + ('them', 'Sam')] + +We could otherwise construct the collocation finder from manually-derived +FreqDists: + + >>> word_fd = nltk.FreqDist(tokens) + >>> bigram_fd = nltk.FreqDist(nltk.bigrams(tokens)) + >>> finder = BigramCollocationFinder(word_fd, bigram_fd) + >>> scored == finder.score_ngrams(bigram_measures.raw_freq) + True + +A similar interface is provided for trigrams: + + >>> finder = TrigramCollocationFinder.from_words(tokens) + >>> scored = finder.score_ngrams(trigram_measures.raw_freq) + >>> set(trigram for trigram, score in scored) == set(nltk.trigrams(tokens)) + True + +We may want to select only the top n results: + + >>> sorted(finder.nbest(trigram_measures.raw_freq, 2)) + [('I', 'do', 'not'), ('do', 'not', 'like')] + +Alternatively, we can select those above a minimum score value: + + >>> sorted(finder.above_score(trigram_measures.raw_freq, + ... 1.0 / len(tuple(nltk.trigrams(tokens))))) + [('I', 'do', 'not'), ('do', 'not', 'like')] + +Now spanning intervening words: + + >>> finder = TrigramCollocationFinder.from_words(tokens) + >>> finder = TrigramCollocationFinder.from_words(tokens, window_size=4) + >>> sorted(finder.nbest(trigram_measures.raw_freq, 4)) + [('I', 'do', 'like'), ('I', 'do', 'not'), ('I', 'not', 'like'), ('do', 'not', 'like')] + +A closer look at the finder's ngram frequencies: + + >>> sorted(finder.ngram_fd.items(), key=lambda t: (-t[1], t[0]))[:10] + [(('I', 'do', 'like'), 2), (('I', 'do', 'not'), 2), (('I', 'not', 'like'), 2), + (('do', 'not', 'like'), 2), ((',', 'I', 'do'), 1), ((',', 'I', 'not'), 1), + ((',', 'do', 'not'), 1), (('I', 'am', '!'), 1), (('Sam', 'I', '!'), 1), + (('Sam', 'I', 'am'), 1)] + +A similar interface is provided for fourgrams: + + >>> finder_4grams = QuadgramCollocationFinder.from_words(tokens) + >>> scored_4grams = finder_4grams.score_ngrams(fourgram_measures.raw_freq) + >>> set(fourgram for fourgram, score in scored_4grams) == set(nltk.ngrams(tokens, n=4)) + True + +Filtering candidates +~~~~~~~~~~~~~~~~~~~~ + +All the ngrams in a text are often too many to be useful when finding +collocations. It is generally useful to remove some words or punctuation, +and to require a minimum frequency for candidate collocations. + +Given our sample text above, if we remove all trigrams containing personal +pronouns from candidature, score_ngrams should return 6 less results, and +'do not like' will be the only candidate which occurs more than once: + + >>> finder = TrigramCollocationFinder.from_words(tokens) + >>> len(finder.score_ngrams(trigram_measures.raw_freq)) + 14 + >>> finder.apply_word_filter(lambda w: w in ('I', 'me')) + >>> len(finder.score_ngrams(trigram_measures.raw_freq)) + 8 + >>> sorted(finder.above_score(trigram_measures.raw_freq, + ... 1.0 / len(tuple(nltk.trigrams(tokens))))) + [('do', 'not', 'like')] + +Sometimes a filter is a function on the whole ngram, rather than each word, +such as if we may permit 'and' to appear in the middle of a trigram, but +not on either edge: + + >>> finder.apply_ngram_filter(lambda w1, w2, w3: 'and' in (w1, w3)) + >>> len(finder.score_ngrams(trigram_measures.raw_freq)) + 6 + +Finally, it is often important to remove low frequency candidates, as we +lack sufficient evidence about their significance as collocations: + + >>> finder.apply_freq_filter(2) + >>> len(finder.score_ngrams(trigram_measures.raw_freq)) + 1 + +Association measures +~~~~~~~~~~~~~~~~~~~~ + +A number of measures are available to score collocations or other associations. +The arguments to measure functions are marginals of a contingency table, in the +bigram case (n_ii, (n_ix, n_xi), n_xx):: + + w1 ~w1 + ------ ------ + w2 | n_ii | n_oi | = n_xi + ------ ------ + ~w2 | n_io | n_oo | + ------ ------ + = n_ix TOTAL = n_xx + +We test their calculation using some known values presented in Manning and +Schutze's text and other papers. + +Student's t: examples from Manning and Schutze 5.3.2 + + >>> print('%0.4f' % bigram_measures.student_t(8, (15828, 4675), 14307668)) + 0.9999 + >>> print('%0.4f' % bigram_measures.student_t(20, (42, 20), 14307668)) + 4.4721 + +Chi-square: examples from Manning and Schutze 5.3.3 + + >>> print('%0.2f' % bigram_measures.chi_sq(8, (15828, 4675), 14307668)) + 1.55 + >>> print('%0.0f' % bigram_measures.chi_sq(59, (67, 65), 571007)) + 456400 + +Likelihood ratios: examples from Dunning, CL, 1993 + + >>> print('%0.2f' % bigram_measures.likelihood_ratio(110, (2552, 221), 31777)) + 270.72 + >>> print('%0.2f' % bigram_measures.likelihood_ratio(8, (13, 32), 31777)) + 95.29 + +Pointwise Mutual Information: examples from Manning and Schutze 5.4 + + >>> print('%0.2f' % bigram_measures.pmi(20, (42, 20), 14307668)) + 18.38 + >>> print('%0.2f' % bigram_measures.pmi(20, (15019, 15629), 14307668)) + 0.29 + +TODO: Find authoritative results for trigrams. + +Using contingency table values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While frequency counts make marginals readily available for collocation +finding, it is common to find published contingency table values. The +collocations package therefore provides a wrapper, ContingencyMeasures, which +wraps an association measures class, providing association measures which +take contingency values as arguments, (n_ii, n_io, n_oi, n_oo) in the +bigram case. + + >>> from nltk.metrics import ContingencyMeasures + >>> cont_bigram_measures = ContingencyMeasures(bigram_measures) + >>> print('%0.2f' % cont_bigram_measures.likelihood_ratio(8, 5, 24, 31740)) + 95.29 + >>> print('%0.2f' % cont_bigram_measures.chi_sq(8, 15820, 4667, 14287173)) + 1.55 + +Ranking and correlation +~~~~~~~~~~~~~~~~~~~~~~~ + +It is useful to consider the results of finding collocations as a ranking, and +the rankings output using different association measures can be compared using +the Spearman correlation coefficient. + +Ranks can be assigned to a sorted list of results trivially by assigning +strictly increasing ranks to each result: + + >>> from nltk.metrics.spearman import * + >>> results_list = ['item1', 'item2', 'item3', 'item4', 'item5'] + >>> print(list(ranks_from_sequence(results_list))) + [('item1', 0), ('item2', 1), ('item3', 2), ('item4', 3), ('item5', 4)] + +If scores are available for each result, we may allow sufficiently similar +results (differing by no more than rank_gap) to be assigned the same rank: + + >>> results_scored = [('item1', 50.0), ('item2', 40.0), ('item3', 38.0), + ... ('item4', 35.0), ('item5', 14.0)] + >>> print(list(ranks_from_scores(results_scored, rank_gap=5))) + [('item1', 0), ('item2', 1), ('item3', 1), ('item4', 1), ('item5', 4)] + +The Spearman correlation coefficient gives a number from -1.0 to 1.0 comparing +two rankings. A coefficient of 1.0 indicates identical rankings; -1.0 indicates +exact opposite rankings. + + >>> print('%0.1f' % spearman_correlation( + ... ranks_from_sequence(results_list), + ... ranks_from_sequence(results_list))) + 1.0 + >>> print('%0.1f' % spearman_correlation( + ... ranks_from_sequence(reversed(results_list)), + ... ranks_from_sequence(results_list))) + -1.0 + >>> results_list2 = ['item2', 'item3', 'item1', 'item5', 'item4'] + >>> print('%0.1f' % spearman_correlation( + ... ranks_from_sequence(results_list), + ... ranks_from_sequence(results_list2))) + 0.6 + >>> print('%0.1f' % spearman_correlation( + ... ranks_from_sequence(reversed(results_list)), + ... ranks_from_sequence(results_list2))) + -0.6 + +Keywords +~~~~~~~~ + +Bigram association metrics can also be used to perform keyword analysis. . For example, this finds the keywords +associated with the "romance" section of the Brown corpus as measured by likelihood ratio: + + >>> romance = nltk.FreqDist(w.lower() for w in nltk.corpus.brown.words(categories='romance') if w.isalpha()) + >>> freq = nltk.FreqDist(w.lower() for w in nltk.corpus.brown.words() if w.isalpha()) + + >>> key = nltk.FreqDist() + >>> for w in romance: + ... key[w] = bigram_measures.likelihood_ratio(romance[w], (freq[w], romance.N()), freq.N()) + + >>> for k,v in key.most_common(10): + ... print(f'{k:10s} {v:9.3f}') + she 1163.325 + i 995.961 + her 930.528 + you 513.149 + of 501.891 + is 463.386 + had 421.615 + he 411.000 + the 347.632 + said 300.811 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/concordance.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/concordance.doctest new file mode 100644 index 0000000..c24c3f2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/concordance.doctest @@ -0,0 +1,75 @@ +.. Copyright (C) 2001-2016 NLTK Project +.. For license information, see LICENSE.TXT + +================================== +Concordance Example +================================== + +A concordance view shows us every occurrence of a given +word, together with some context. Here we look up the word monstrous +in Moby Dick by entering text1 followed by a period, then the term +concordance, and then placing "monstrous" in parentheses: + +>>> from nltk.corpus import gutenberg +>>> from nltk.text import Text +>>> corpus = gutenberg.words('melville-moby_dick.txt') +>>> text = Text(corpus) + +>>> text.concordance("monstrous") +Displaying 11 of 11 matches: +ong the former , one was of a most monstrous size . ... This came towards us , +ON OF THE PSALMS . " Touching that monstrous bulk of the whale or ork we have r +ll over with a heathenish array of monstrous clubs and spears . Some were thick +d as you gazed , and wondered what monstrous cannibal and savage could ever hav +that has survived the flood ; most monstrous and most mountainous ! That Himmal +they might scout at Moby Dick as a monstrous fable , or still worse and more de +th of Radney .'" CHAPTER 55 Of the Monstrous Pictures of Whales . I shall ere l +ing Scenes . In connexion with the monstrous pictures of whales , I am strongly +ere to enter upon those still more monstrous stories of them which are to be fo +ght have been rummaged out of this monstrous cabinet there is no telling . But +of Whale - Bones ; for Whales of a monstrous size are oftentimes cast up dead u + +>>> text.concordance("monstrous") +Displaying 11 of 11 matches: +ong the former , one was of a most monstrous size . ... This came towards us , +ON OF THE PSALMS . " Touching that monstrous bulk of the whale or ork we have r +ll over with a heathenish array of monstrous clubs and spears . Some were thick +... + +We can also search for a multi-word phrase by passing a list of strings: + +>>> text.concordance(["monstrous", "size"]) +Displaying 2 of 2 matches: +the former , one was of a most monstrous size . ... This came towards us , op +Whale - Bones ; for Whales of a monstrous size are oftentimes cast up dead upo + +================================= +Concordance List +================================= + +Often we need to store the results of concordance for further usage. +To do so, call the concordance function with the stdout argument set +to false: + +>>> from nltk.corpus import gutenberg +>>> from nltk.text import Text +>>> corpus = gutenberg.words('melville-moby_dick.txt') +>>> text = Text(corpus) +>>> con_list = text.concordance_list("monstrous") +>>> con_list[2].line +'ll over with a heathenish array of monstrous clubs and spears . Some were thick' +>>> len(con_list) +11 + +================================= +Patching Issue #2088 +================================= + +Patching https://github.com/nltk/nltk/issues/2088 +The left slice of the left context should be clip to 0 if the `i-context` < 0. + +>>> from nltk import Text, word_tokenize +>>> jane_eyre = 'Chapter 1\nTHERE was no possibility of taking a walk that day. We had been wandering, indeed, in the leafless shrubbery an hour in the morning; but since dinner (Mrs. Reed, when there was no company, dined early) the cold winter wind had brought with it clouds so sombre, and a rain so penetrating, that further outdoor exercise was now out of the question.' +>>> text = Text(word_tokenize(jane_eyre)) +>>> text.concordance_list('taking')[0].left +['Chapter', '1', 'THERE', 'was', 'no', 'possibility', 'of'] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/conftest.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/conftest.py new file mode 100644 index 0000000..b6c7003 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/conftest.py @@ -0,0 +1,33 @@ +import pytest + +from nltk.corpus.reader import CorpusReader + + +@pytest.fixture(autouse=True) +def mock_plot(mocker): + """Disable matplotlib plotting in test code""" + + try: + import matplotlib.pyplot as plt + + mocker.patch.object(plt, "gca") + mocker.patch.object(plt, "show") + except ImportError: + pass + + +@pytest.fixture(scope="module", autouse=True) +def teardown_loaded_corpora(): + """ + After each test session ends (either doctest or unit test), + unload any loaded corpora + """ + + yield # first, wait for the test to end + + import nltk.corpus + + for name in dir(nltk.corpus): + obj = getattr(nltk.corpus, name, None) + if isinstance(obj, CorpusReader) and hasattr(obj, "_unload"): + obj._unload() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/corpus.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/corpus.doctest new file mode 100644 index 0000000..76730cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/corpus.doctest @@ -0,0 +1,2196 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================ + Corpus Readers +================ + +The `nltk.corpus` package defines a collection of *corpus reader* +classes, which can be used to access the contents of a diverse set of +corpora. The list of available corpora is given at: + +https://www.nltk.org/nltk_data/ + +Each corpus reader class is specialized to handle a specific +corpus format. In addition, the `nltk.corpus` package automatically +creates a set of corpus reader instances that can be used to access +the corpora in the NLTK data package. +Section `Corpus Reader Objects`_ ("Corpus Reader Objects") describes +the corpus reader instances that can be used to read the corpora in +the NLTK data package. Section `Corpus Reader Classes`_ ("Corpus +Reader Classes") describes the corpus reader classes themselves, and +discusses the issues involved in creating new corpus reader objects +and new corpus reader classes. Section `Regression Tests`_ +("Regression Tests") contains regression tests for the corpus readers +and associated functions and classes. + +.. contents:: **Table of Contents** + :depth: 4 + :backlinks: none + +--------------------- +Corpus Reader Objects +--------------------- + +Overview +======== + +NLTK includes a diverse set of corpora which can be +read using the ``nltk.corpus`` package. Each corpus is accessed by +means of a "corpus reader" object from ``nltk.corpus``: + + >>> import nltk.corpus + >>> # The Brown corpus: + >>> print(str(nltk.corpus.brown).replace('\\\\','/')) + + >>> # The Penn Treebank Corpus: + >>> print(str(nltk.corpus.treebank).replace('\\\\','/')) + + >>> # The Name Genders Corpus: + >>> print(str(nltk.corpus.names).replace('\\\\','/')) + + >>> # The Inaugural Address Corpus: + >>> print(str(nltk.corpus.inaugural).replace('\\\\','/')) + + +Most corpora consist of a set of files, each containing a document (or +other pieces of text). A list of identifiers for these files is +accessed via the ``fileids()`` method of the corpus reader: + + >>> nltk.corpus.treebank.fileids() + ['wsj_0001.mrg', 'wsj_0002.mrg', 'wsj_0003.mrg', 'wsj_0004.mrg', ...] + >>> nltk.corpus.inaugural.fileids() + ['1789-Washington.txt', '1793-Washington.txt', '1797-Adams.txt', ...] + +Each corpus reader provides a variety of methods to read data from the +corpus, depending on the format of the corpus. For example, plaintext +corpora support methods to read the corpus as raw text, a list of +words, a list of sentences, or a list of paragraphs. + + >>> from nltk.corpus import inaugural + >>> inaugural.raw('1789-Washington.txt') + 'Fellow-Citizens of the Senate ...' + >>> inaugural.words('1789-Washington.txt') + ['Fellow', '-', 'Citizens', 'of', 'the', ...] + >>> inaugural.sents('1789-Washington.txt') + [['Fellow', '-', 'Citizens'...], ['Among', 'the', 'vicissitudes'...]...] + >>> inaugural.paras('1789-Washington.txt') + [[['Fellow', '-', 'Citizens'...]], + [['Among', 'the', 'vicissitudes'...], + ['On', 'the', 'one', 'hand', ',', 'I'...]...]...] + +Each of these reader methods may be given a single document's item +name or a list of document item names. When given a list of document +item names, the reader methods will concatenate together the contents +of the individual documents. + + >>> l1 = len(inaugural.words('1789-Washington.txt')) + >>> l2 = len(inaugural.words('1793-Washington.txt')) + >>> l3 = len(inaugural.words(['1789-Washington.txt', '1793-Washington.txt'])) + >>> print('%s+%s == %s' % (l1, l2, l3)) + 1538+147 == 1685 + +If the reader methods are called without any arguments, they will +typically load all documents in the corpus. + + >>> len(inaugural.words()) + 152901 + +If a corpus contains a README file, it can be accessed with a ``readme()`` method: + + >>> inaugural.readme()[:32] + 'C-Span Inaugural Address Corpus\n' + +Plaintext Corpora +================= + +Here are the first few words from each of NLTK's plaintext corpora: + + >>> nltk.corpus.abc.words() + ['PM', 'denies', 'knowledge', 'of', 'AWB', ...] + >>> nltk.corpus.genesis.words() + ['In', 'the', 'beginning', 'God', 'created', ...] + >>> nltk.corpus.gutenberg.words(fileids='austen-emma.txt') + ['[', 'Emma', 'by', 'Jane', 'Austen', '1816', ...] + >>> nltk.corpus.inaugural.words() + ['Fellow', '-', 'Citizens', 'of', 'the', ...] + >>> nltk.corpus.state_union.words() + ['PRESIDENT', 'HARRY', 'S', '.', 'TRUMAN', "'", ...] + >>> nltk.corpus.webtext.words() + ['Cookie', 'Manager', ':', '"', 'Don', "'", 't', ...] + +Tagged Corpora +============== + +In addition to the plaintext corpora, NLTK's data package also +contains a wide variety of annotated corpora. For example, the Brown +Corpus is annotated with part-of-speech tags, and defines additional +methods ``tagged_*()`` which words as `(word,tag)` tuples, rather +than just bare word strings. + + >>> from nltk.corpus import brown + >>> print(brown.words()) + ['The', 'Fulton', 'County', 'Grand', 'Jury', ...] + >>> print(brown.tagged_words()) + [('The', 'AT'), ('Fulton', 'NP-TL'), ...] + >>> print(brown.sents()) + [['The', 'Fulton', 'County'...], ['The', 'jury', 'further'...], ...] + >>> print(brown.tagged_sents()) + [[('The', 'AT'), ('Fulton', 'NP-TL')...], + [('The', 'AT'), ('jury', 'NN'), ('further', 'RBR')...]...] + >>> print(brown.paras(categories='reviews')) + [[['It', 'is', 'not', 'news', 'that', 'Nathan', 'Milstein'...], + ['Certainly', 'not', 'in', 'Orchestra', 'Hall', 'where'...]], + [['There', 'was', 'about', 'that', 'song', 'something', ...], + ['Not', 'the', 'noblest', 'performance', 'we', 'have', ...], ...], ...] + >>> print(brown.tagged_paras(categories='reviews')) + [[[('It', 'PPS'), ('is', 'BEZ'), ('not', '*'), ...], + [('Certainly', 'RB'), ('not', '*'), ('in', 'IN'), ...]], + [[('There', 'EX'), ('was', 'BEDZ'), ('about', 'IN'), ...], + [('Not', '*'), ('the', 'AT'), ('noblest', 'JJT'), ...], ...], ...] + +Similarly, the Indian Language POS-Tagged Corpus includes samples of +Indian text annotated with part-of-speech tags: + + >>> from nltk.corpus import indian + >>> print(indian.words()) # doctest: +SKIP + ['\xe0\xa6\xae\xe0\xa6\xb9\xe0\xa6\xbf\...', + '\xe0\xa6\xb8\xe0\xa6\xa8\xe0\xa7\x8d\xe0...', ...] + >>> print(indian.tagged_words()) # doctest: +SKIP + [('\xe0\xa6\xae\xe0\xa6\xb9\xe0\xa6\xbf...', 'NN'), + ('\xe0\xa6\xb8\xe0\xa6\xa8\xe0\xa7\x8d\xe0...', 'NN'), ...] + +Several tagged corpora support access to a simplified, universal tagset, e.g. where all nouns +tags are collapsed to a single category ``NOUN``: + + >>> print(brown.tagged_sents(tagset='universal')) + [[('The', 'DET'), ('Fulton', 'NOUN'), ('County', 'NOUN'), ('Grand', 'ADJ'), ('Jury', 'NOUN'), ...], + [('The', 'DET'), ('jury', 'NOUN'), ('further', 'ADV'), ('said', 'VERB'), ('in', 'ADP'), ...]...] + >>> from nltk.corpus import conll2000, switchboard + >>> print(conll2000.tagged_words(tagset='universal')) + [('Confidence', 'NOUN'), ('in', 'ADP'), ...] + +Use ``nltk.app.pos_concordance()`` to access a GUI for searching tagged corpora. + +Chunked Corpora +=============== + +The CoNLL corpora also provide chunk structures, which are encoded as +flat trees. The CoNLL 2000 Corpus includes phrasal chunks; and the +CoNLL 2002 Corpus includes named entity chunks. + + >>> from nltk.corpus import conll2000, conll2002 + >>> print(conll2000.sents()) + [['Confidence', 'in', 'the', 'pound', 'is', 'widely', ...], + ['Chancellor', 'of', 'the', 'Exchequer', ...], ...] + >>> for tree in conll2000.chunked_sents()[:2]: + ... print(tree) + (S + (NP Confidence/NN) + (PP in/IN) + (NP the/DT pound/NN) + (VP is/VBZ widely/RB expected/VBN to/TO take/VB) + (NP another/DT sharp/JJ dive/NN) + if/IN + ...) + (S + Chancellor/NNP + (PP of/IN) + (NP the/DT Exchequer/NNP) + ...) + >>> print(conll2002.sents()) + [['Sao', 'Paulo', '(', 'Brasil', ')', ',', ...], ['-'], ...] + >>> for tree in conll2002.chunked_sents()[:2]: + ... print(tree) + (S + (LOC Sao/NC Paulo/VMI) + (/Fpa + (LOC Brasil/NC) + )/Fpt + ...) + (S -/Fg) + +.. note:: Since the CONLL corpora do not contain paragraph break + information, these readers do not support the ``para()`` method.) + +.. warning:: if you call the conll corpora reader methods without any + arguments, they will return the contents of the entire corpus, + *including* the 'test' portions of the corpus.) + +SemCor is a subset of the Brown corpus tagged with WordNet senses and +named entities. Both kinds of lexical items include multiword units, +which are encoded as chunks (senses and part-of-speech tags pertain +to the entire chunk). + + >>> from nltk.corpus import semcor + >>> semcor.words() + ['The', 'Fulton', 'County', 'Grand', 'Jury', ...] + >>> semcor.chunks() + [['The'], ['Fulton', 'County', 'Grand', 'Jury'], ...] + >>> semcor.sents() + [['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', ...], + ['The', 'jury', 'further', 'said', ...], ...] + >>> semcor.chunk_sents() + [[['The'], ['Fulton', 'County', 'Grand', 'Jury'], ['said'], ... + ['.']], [['The'], ['jury'], ['further'], ['said'], ... ['.']], ...] + >>> list(map(str, semcor.tagged_chunks(tag='both')[:3])) + ['(DT The)', "(Lemma('group.n.01.group') (NE (NNP Fulton County Grand Jury)))", "(Lemma('state.v.01.say') (VB said))"] + >>> [[str(c) for c in s] for s in semcor.tagged_sents(tag='both')[:2]] + [['(DT The)', "(Lemma('group.n.01.group') (NE (NNP Fulton County Grand Jury)))", ... + '(None .)'], ['(DT The)', ... '(None .)']] + + +The IEER corpus is another chunked corpus. This corpus is unusual in +that each corpus item contains multiple documents. (This reflects the +fact that each corpus file contains multiple documents.) The IEER +corpus defines the `parsed_docs` method, which returns the documents +in a given item as `IEERDocument` objects: + + >>> from nltk.corpus import ieer + >>> ieer.fileids() + ['APW_19980314', 'APW_19980424', 'APW_19980429', + 'NYT_19980315', 'NYT_19980403', 'NYT_19980407'] + >>> docs = ieer.parsed_docs('APW_19980314') + >>> print(docs[0]) + + >>> print(docs[0].docno) + APW19980314.0391 + >>> print(docs[0].doctype) + NEWS STORY + >>> print(docs[0].date_time) + 03/14/1998 10:36:00 + >>> print(docs[0].headline) + (DOCUMENT Kenyans protest tax hikes) + >>> print(docs[0].text) + (DOCUMENT + (LOCATION NAIROBI) + , + (LOCATION Kenya) + ( + (ORGANIZATION AP) + ) + _ + (CARDINAL Thousands) + of + laborers, + ... + on + (DATE Saturday) + ...) + +Parsed Corpora +============== + +The Treebank corpora provide a syntactic parse for each sentence. The +NLTK data package includes a 10% sample of the Penn Treebank (in +``treebank``), as well as the Sinica Treebank (in ``sinica_treebank``). + +Reading the Penn Treebank (Wall Street Journal sample): + + >>> from nltk.corpus import treebank + >>> print(treebank.fileids()) + ['wsj_0001.mrg', 'wsj_0002.mrg', 'wsj_0003.mrg', 'wsj_0004.mrg', ...] + >>> print(treebank.words('wsj_0003.mrg')) + ['A', 'form', 'of', 'asbestos', 'once', 'used', ...] + >>> print(treebank.tagged_words('wsj_0003.mrg')) + [('A', 'DT'), ('form', 'NN'), ('of', 'IN'), ...] + >>> print(treebank.parsed_sents('wsj_0003.mrg')[0]) + (S + (S-TPC-1 + (NP-SBJ + (NP (NP (DT A) (NN form)) (PP (IN of) (NP (NN asbestos)))) + (RRC ...)...)...) + ... + (VP (VBD reported) (SBAR (-NONE- 0) (S (-NONE- *T*-1)))) + (. .)) + +If you have access to a full installation of the Penn Treebank, NLTK +can be configured to load it as well. Download the ``ptb`` package, +and in the directory ``nltk_data/corpora/ptb`` place the ``BROWN`` +and ``WSJ`` directories of the Treebank installation (symlinks work +as well). Then use the ``ptb`` module instead of ``treebank``: + + >>> from nltk.corpus import ptb + >>> print(ptb.fileids()) # doctest: +SKIP + ['BROWN/CF/CF01.MRG', 'BROWN/CF/CF02.MRG', 'BROWN/CF/CF03.MRG', 'BROWN/CF/CF04.MRG', ...] + >>> print(ptb.words('WSJ/00/WSJ_0003.MRG')) # doctest: +SKIP + ['A', 'form', 'of', 'asbestos', 'once', 'used', '*', ...] + >>> print(ptb.tagged_words('WSJ/00/WSJ_0003.MRG')) # doctest: +SKIP + [('A', 'DT'), ('form', 'NN'), ('of', 'IN'), ...] + +...and so forth, like ``treebank`` but with extended fileids. Categories +specified in ``allcats.txt`` can be used to filter by genre; they consist +of ``news`` (for WSJ articles) and names of the Brown subcategories +(``fiction``, ``humor``, ``romance``, etc.): + + >>> ptb.categories() # doctest: +SKIP + ['adventure', 'belles_lettres', 'fiction', 'humor', 'lore', 'mystery', 'news', 'romance', 'science_fiction'] + >>> print(ptb.fileids('news')) # doctest: +SKIP + ['WSJ/00/WSJ_0001.MRG', 'WSJ/00/WSJ_0002.MRG', 'WSJ/00/WSJ_0003.MRG', ...] + >>> print(ptb.words(categories=['humor','fiction'])) # doctest: +SKIP + ['Thirty-three', 'Scotty', 'did', 'not', 'go', 'back', ...] + +As PropBank and NomBank depend on the (WSJ portion of the) Penn Treebank, +the modules ``propbank_ptb`` and ``nombank_ptb`` are provided for access +to a full PTB installation. + +Reading the Sinica Treebank: + + >>> from nltk.corpus import sinica_treebank + >>> print(sinica_treebank.sents()) # doctest: +SKIP + [['\xe4\xb8\x80'], ['\xe5\x8f\x8b\xe6\x83\x85'], ...] + >>> sinica_treebank.parsed_sents()[25] # doctest: +SKIP + Tree('S', + [Tree('NP', + [Tree('Nba', ['\xe5\x98\x89\xe7\x8f\x8d'])]), + Tree('V\xe2\x80\xa7\xe5\x9c\xb0', + [Tree('VA11', ['\xe4\xb8\x8d\xe5\x81\x9c']), + Tree('DE', ['\xe7\x9a\x84'])]), + Tree('VA4', ['\xe5\x93\xad\xe6\xb3\xa3'])]) + +Reading the CoNLL 2007 Dependency Treebanks: + + >>> from nltk.corpus import conll2007 + >>> conll2007.sents('esp.train')[0] # doctest: +SKIP + ['El', 'aumento', 'del', 'índice', 'de', 'desempleo', ...] + >>> conll2007.parsed_sents('esp.train')[0] # doctest: +SKIP + + >>> print(conll2007.parsed_sents('esp.train')[0].tree()) # doctest: +SKIP + (fortaleció + (aumento El (del (índice (de (desempleo estadounidense))))) + hoy + considerablemente + (al + (euro + (cotizaba + , + que + (a (15.35 las GMT)) + se + (en (mercado el (de divisas) (de Fráncfort))) + (a 0,9452_dólares) + (frente_a , (0,9349_dólares los (de (mañana esta))))))) + .) + +Word Lists and Lexicons +======================= + +The NLTK data package also includes a number of lexicons and word +lists. These are accessed just like text corpora. The following +examples illustrate the use of the wordlist corpora: + + >>> from nltk.corpus import names, stopwords, words + >>> words.fileids() + ['en', 'en-basic'] + >>> words.words('en') + ['A', 'a', 'aa', 'aal', 'aalii', 'aam', 'Aani', 'aardvark', 'aardwolf', ...] + + >>> stopwords.fileids() # doctest: +SKIP + ['arabic', 'azerbaijani', 'bengali', 'danish', 'dutch', 'english', 'finnish', 'french', ...] + >>> sorted(stopwords.words('portuguese')) + ['a', 'ao', 'aos', 'aquela', 'aquelas', 'aquele', 'aqueles', ...] + >>> names.fileids() + ['female.txt', 'male.txt'] + >>> names.words('male.txt') + ['Aamir', 'Aaron', 'Abbey', 'Abbie', 'Abbot', 'Abbott', ...] + >>> names.words('female.txt') + ['Abagael', 'Abagail', 'Abbe', 'Abbey', 'Abbi', 'Abbie', ...] + +The CMU Pronunciation Dictionary corpus contains pronunciation +transcriptions for over 100,000 words. It can be accessed as a list +of entries (where each entry consists of a word, an identifier, and a +transcription) or as a dictionary from words to lists of +transcriptions. Transcriptions are encoded as tuples of phoneme +strings. + + >>> from nltk.corpus import cmudict + >>> print(cmudict.entries()[653:659]) + [('acetate', ['AE1', 'S', 'AH0', 'T', 'EY2', 'T']), + ('acetic', ['AH0', 'S', 'EH1', 'T', 'IH0', 'K']), + ('acetic', ['AH0', 'S', 'IY1', 'T', 'IH0', 'K']), + ('aceto', ['AA0', 'S', 'EH1', 'T', 'OW0']), + ('acetochlor', ['AA0', 'S', 'EH1', 'T', 'OW0', 'K', 'L', 'AO2', 'R']), + ('acetone', ['AE1', 'S', 'AH0', 'T', 'OW2', 'N'])] + >>> # Load the entire cmudict corpus into a Python dictionary: + >>> transcr = cmudict.dict() + >>> print([transcr[w][0] for w in 'Natural Language Tool Kit'.lower().split()]) + [['N', 'AE1', 'CH', 'ER0', 'AH0', 'L'], + ['L', 'AE1', 'NG', 'G', 'W', 'AH0', 'JH'], + ['T', 'UW1', 'L'], + ['K', 'IH1', 'T']] + + +WordNet +======= + +Please see the separate WordNet howto. + +FrameNet +======== + +Please see the separate FrameNet howto. + +PropBank +======== + +Please see the separate PropBank howto. + +SentiWordNet +============ + +Please see the separate SentiWordNet howto. + +Categorized Corpora +=================== + +Several corpora included with NLTK contain documents that have been categorized for +topic, genre, polarity, etc. In addition to the standard corpus interface, these +corpora provide access to the list of categories and the mapping between the documents +and their categories (in both directions). Access the categories using the ``categories()`` +method, e.g.: + + >>> from nltk.corpus import brown, movie_reviews, reuters + >>> brown.categories() + ['adventure', 'belles_lettres', 'editorial', 'fiction', 'government', 'hobbies', 'humor', + 'learned', 'lore', 'mystery', 'news', 'religion', 'reviews', 'romance', 'science_fiction'] + >>> movie_reviews.categories() + ['neg', 'pos'] + >>> reuters.categories() + ['acq', 'alum', 'barley', 'bop', 'carcass', 'castor-oil', 'cocoa', + 'coconut', 'coconut-oil', 'coffee', 'copper', 'copra-cake', 'corn', + 'cotton', 'cotton-oil', 'cpi', 'cpu', 'crude', 'dfl', 'dlr', ...] + +This method has an optional argument that specifies a document or a list +of documents, allowing us to map from (one or more) documents to (one or more) categories: + + >>> brown.categories('ca01') + ['news'] + >>> brown.categories(['ca01','cb01']) + ['editorial', 'news'] + >>> reuters.categories('training/9865') + ['barley', 'corn', 'grain', 'wheat'] + >>> reuters.categories(['training/9865', 'training/9880']) + ['barley', 'corn', 'grain', 'money-fx', 'wheat'] + +We can go back the other way using the optional argument of the ``fileids()`` method: + + >>> reuters.fileids('barley') + ['test/15618', 'test/15649', 'test/15676', 'test/15728', 'test/15871', ...] + +Both the ``categories()`` and ``fileids()`` methods return a sorted list containing +no duplicates. + +In addition to mapping between categories and documents, these corpora permit +direct access to their contents via the categories. Instead of accessing a subset +of a corpus by specifying one or more fileids, we can identify one or more categories, e.g.: + + >>> brown.tagged_words(categories='news') + [('The', 'AT'), ('Fulton', 'NP-TL'), ...] + >>> brown.sents(categories=['editorial','reviews']) + [['Assembly', 'session', 'brought', 'much', 'good'], ['The', 'General', + 'Assembly', ',', 'which', 'adjourns', 'today', ',', 'has', 'performed', + 'in', 'an', 'atmosphere', 'of', 'crisis', 'and', 'struggle', 'from', + 'the', 'day', 'it', 'convened', '.'], ...] + +Note that it is an error to specify both documents and categories. + +In the context of a text categorization system, we can easily test if the +category assigned to a document is correct as follows: + + >>> def classify(doc): return 'news' # Trivial classifier + >>> doc = 'ca01' + >>> classify(doc) in brown.categories(doc) + True + + +Other Corpora +============= + +comparative_sentences +--------------------- +A list of sentences from various sources, especially reviews and articles. Each +line contains one sentence; sentences were separated by using a sentence tokenizer. +Comparative sentences have been annotated with their type, entities, features and +keywords. + + >>> from nltk.corpus import comparative_sentences + >>> comparison = comparative_sentences.comparisons()[0] + >>> comparison.text + ['its', 'fast-forward', 'and', 'rewind', 'work', 'much', 'more', 'smoothly', + 'and', 'consistently', 'than', 'those', 'of', 'other', 'models', 'i', "'ve", + 'had', '.'] + >>> comparison.entity_2 + 'models' + >>> (comparison.feature, comparison.keyword) + ('rewind', 'more') + >>> len(comparative_sentences.comparisons()) + 853 + +opinion_lexicon +--------------- +A list of positive and negative opinion words or sentiment words for English. + + >>> from nltk.corpus import opinion_lexicon + >>> opinion_lexicon.words()[:4] + ['2-faced', '2-faces', 'abnormal', 'abolish'] + +The OpinionLexiconCorpusReader also provides shortcuts to retrieve positive/negative +words: + + >>> opinion_lexicon.negative()[:4] + ['2-faced', '2-faces', 'abnormal', 'abolish'] + +Note that words from `words()` method in opinion_lexicon are sorted by file id, +not alphabetically: + + >>> opinion_lexicon.words()[0:10] + ['2-faced', '2-faces', 'abnormal', 'abolish', 'abominable', 'abominably', + 'abominate', 'abomination', 'abort', 'aborted'] + >>> sorted(opinion_lexicon.words())[0:10] + ['2-faced', '2-faces', 'a+', 'abnormal', 'abolish', 'abominable', 'abominably', + 'abominate', 'abomination', 'abort'] + +ppattach +-------- +The Prepositional Phrase Attachment corpus is a corpus of +prepositional phrase attachment decisions. Each instance in the +corpus is encoded as a ``PPAttachment`` object: + + >>> from nltk.corpus import ppattach + >>> ppattach.attachments('training') + [PPAttachment(sent='0', verb='join', noun1='board', + prep='as', noun2='director', attachment='V'), + PPAttachment(sent='1', verb='is', noun1='chairman', + prep='of', noun2='N.V.', attachment='N'), + ...] + >>> inst = ppattach.attachments('training')[0] + >>> (inst.sent, inst.verb, inst.noun1, inst.prep, inst.noun2) + ('0', 'join', 'board', 'as', 'director') + >>> inst.attachment + 'V' + +product_reviews_1 and product_reviews_2 +--------------------------------------- +These two datasets respectively contain annotated customer reviews of 5 and 9 +products from amazon.com. + + >>> from nltk.corpus import product_reviews_1 + >>> camera_reviews = product_reviews_1.reviews('Canon_G3.txt') + >>> review = camera_reviews[0] + >>> review.sents()[0] + ['i', 'recently', 'purchased', 'the', 'canon', 'powershot', 'g3', 'and', 'am', + 'extremely', 'satisfied', 'with', 'the', 'purchase', '.'] + >>> review.features() + [('canon powershot g3', '+3'), ('use', '+2'), ('picture', '+2'), + ('picture quality', '+1'), ('picture quality', '+1'), ('camera', '+2'), + ('use', '+2'), ('feature', '+1'), ('picture quality', '+3'), ('use', '+1'), + ('option', '+1')] + +It is also possible to reach the same information directly from the stream: + + >>> product_reviews_1.features('Canon_G3.txt') + [('canon powershot g3', '+3'), ('use', '+2'), ...] + +We can compute stats for specific product features: + + >>> n_reviews = len([(feat,score) for (feat,score) in product_reviews_1.features('Canon_G3.txt') if feat=='picture']) + >>> tot = sum([int(score) for (feat,score) in product_reviews_1.features('Canon_G3.txt') if feat=='picture']) + >>> mean = tot / n_reviews + >>> print(n_reviews, tot, mean) + 15 24 1.6 + +pros_cons +--------- +A list of pros/cons sentences for determining context (aspect) dependent +sentiment words, which are then applied to sentiment analysis of comparative +sentences. + + >>> from nltk.corpus import pros_cons + >>> pros_cons.sents(categories='Cons') + [['East', 'batteries', '!', 'On', '-', 'off', 'switch', 'too', 'easy', + 'to', 'maneuver', '.'], ['Eats', '...', 'no', ',', 'GULPS', 'batteries'], + ...] + >>> pros_cons.words('IntegratedPros.txt') + ['Easy', 'to', 'use', ',', 'economical', '!', ...] + +semcor +------ +The Brown Corpus, annotated with WordNet senses. + + >>> from nltk.corpus import semcor + >>> semcor.words('brown2/tagfiles/br-n12.xml') + ['When', 'several', 'minutes', 'had', 'passed', ...] + +senseval +-------- +The Senseval 2 corpus is a word sense disambiguation corpus. Each +item in the corpus corresponds to a single ambiguous word. For each +of these words, the corpus contains a list of instances, corresponding +to occurrences of that word. Each instance provides the word; a list +of word senses that apply to the word occurrence; and the word's +context. + + >>> from nltk.corpus import senseval + >>> senseval.fileids() + ['hard.pos', 'interest.pos', 'line.pos', 'serve.pos'] + >>> senseval.instances('hard.pos') + ... + [SensevalInstance(word='hard-a', + position=20, + context=[('``', '``'), ('he', 'PRP'), ...('hard', 'JJ'), ...], + senses=('HARD1',)), + SensevalInstance(word='hard-a', + position=10, + context=[('clever', 'NNP'), ...('hard', 'JJ'), ('time', 'NN'), ...], + senses=('HARD1',)), ...] + +The following code looks at instances of the word 'interest', and +displays their local context (2 words on each side) and word sense(s): + + >>> for inst in senseval.instances('interest.pos')[:10]: + ... p = inst.position + ... left = ' '.join(w for (w,t) in inst.context[p-2:p]) + ... word = ' '.join(w for (w,t) in inst.context[p:p+1]) + ... right = ' '.join(w for (w,t) in inst.context[p+1:p+3]) + ... senses = ' '.join(inst.senses) + ... print('%20s |%10s | %-15s -> %s' % (left, word, right, senses)) + declines in | interest | rates . -> interest_6 + indicate declining | interest | rates because -> interest_6 + in short-term | interest | rates . -> interest_6 + 4 % | interest | in this -> interest_5 + company with | interests | in the -> interest_5 + , plus | interest | . -> interest_6 + set the | interest | rate on -> interest_6 + 's own | interest | , prompted -> interest_4 + principal and | interest | is the -> interest_6 + increase its | interest | to 70 -> interest_5 + +sentence_polarity +----------------- +The Sentence Polarity dataset contains 5331 positive and 5331 negative processed +sentences. + + >>> from nltk.corpus import sentence_polarity + >>> sentence_polarity.sents() + [['simplistic', ',', 'silly', 'and', 'tedious', '.'], ["it's", 'so', 'laddish', + 'and', 'juvenile', ',', 'only', 'teenage', 'boys', 'could', 'possibly', 'find', + 'it', 'funny', '.'], ...] + >>> sentence_polarity.categories() + ['neg', 'pos'] + >>> sentence_polarity.sents()[1] + ["it's", 'so', 'laddish', 'and', 'juvenile', ',', 'only', 'teenage', 'boys', + 'could', 'possibly', 'find', 'it', 'funny', '.'] + +shakespeare +----------- +The Shakespeare corpus contains a set of Shakespeare plays, formatted +as XML files. These corpora are returned as ElementTree objects: + + >>> from nltk.corpus import shakespeare + >>> from xml.etree import ElementTree + >>> shakespeare.fileids() + ['a_and_c.xml', 'dream.xml', 'hamlet.xml', 'j_caesar.xml', ...] + >>> play = shakespeare.xml('dream.xml') + >>> print(play) + + >>> print('%s: %s' % (play[0].tag, play[0].text)) + TITLE: A Midsummer Night's Dream + >>> personae = [persona.text for persona in + ... play.findall('PERSONAE/PERSONA')] + >>> print(personae) + ['THESEUS, Duke of Athens.', 'EGEUS, father to Hermia.', ...] + >>> # Find and print speakers not listed as personae + >>> names = [persona.split(',')[0] for persona in personae] + >>> speakers = set(speaker.text for speaker in + ... play.findall('*/*/*/SPEAKER')) + >>> print(sorted(speakers.difference(names))) + ['ALL', 'COBWEB', 'DEMETRIUS', 'Fairy', 'HERNIA', 'LYSANDER', + 'Lion', 'MOTH', 'MUSTARDSEED', 'Moonshine', 'PEASEBLOSSOM', + 'Prologue', 'Pyramus', 'Thisbe', 'Wall'] + +subjectivity +------------ +The Subjectivity Dataset contains 5000 subjective and 5000 objective processed +sentences. + + >>> from nltk.corpus import subjectivity + >>> subjectivity.categories() + ['obj', 'subj'] + >>> subjectivity.sents()[23] + ['television', 'made', 'him', 'famous', ',', 'but', 'his', 'biggest', 'hits', + 'happened', 'off', 'screen', '.'] + >>> subjectivity.words(categories='subj') + ['smart', 'and', 'alert', ',', 'thirteen', ...] + +toolbox +------- +The Toolbox corpus distributed with NLTK contains a sample lexicon and +several sample texts from the Rotokas language. The Toolbox corpus +reader returns Toolbox files as XML ElementTree objects. The +following example loads the Rotokas dictionary, and figures out the +distribution of part-of-speech tags for reduplicated words. + +.. doctest: +SKIP + + >>> from nltk.corpus import toolbox + >>> from nltk.probability import FreqDist + >>> from xml.etree import ElementTree + >>> import re + >>> rotokas = toolbox.xml('rotokas.dic') + >>> redup_pos_freqdist = FreqDist() + >>> # Note: we skip over the first record, which is actually + >>> # the header. + >>> for record in rotokas[1:]: + ... lexeme = record.find('lx').text + ... if re.match(r'(.*)\1$', lexeme): + ... redup_pos_freqdist[record.find('ps').text] += 1 + >>> for item, count in redup_pos_freqdist.most_common(): + ... print(item, count) + V 41 + N 14 + ??? 4 + +This example displays some records from a Rotokas text: + +.. doctest: +SKIP + + >>> river = toolbox.xml('rotokas/river.txt', key='ref') + >>> for record in river.findall('record')[:3]: + ... for piece in record: + ... if len(piece.text) > 60: + ... print('%-6s %s...' % (piece.tag, piece.text[:57])) + ... else: + ... print('%-6s %s' % (piece.tag, piece.text)) + ref Paragraph 1 + t ``Viapau oisio ra ovaupasi ... + m viapau oisio ra ovau -pa -si ... + g NEG this way/like this and forget -PROG -2/3.DL... + p NEG ??? CONJ V.I -SUFF.V.3 -SUFF.V... + f ``No ken lus tingting wanema samting papa i bin tok,'' Na... + fe ``Don't forget what Dad said,'' yelled Naomi. + ref 2 + t Osa Ira ora Reviti viapau uvupasiva. + m osa Ira ora Reviti viapau uvu -pa -si ... + g as/like name and name NEG hear/smell -PROG -2/3... + p CONJ N.PN CONJ N.PN NEG V.T -SUFF.V.3 -SUF... + f Tasol Ila na David no bin harim toktok. + fe But Ila and David took no notice. + ref 3 + t Ikaupaoro rokosiva ... + m ikau -pa -oro roko -si -va ... + g run/hurry -PROG -SIM go down -2/3.DL.M -RP ... + p V.T -SUFF.V.3 -SUFF.V.4 ADV -SUFF.V.4 -SUFF.VT.... + f Tupela i bin hariap i go long wara . + fe They raced to the river. + +timit +----- +The NLTK data package includes a fragment of the TIMIT +Acoustic-Phonetic Continuous Speech Corpus. This corpus is broken +down into small speech samples, each of which is available as a wave +file, a phonetic transcription, and a tokenized word list. + + >>> from nltk.corpus import timit + >>> print(timit.utteranceids()) + ['dr1-fvmh0/sa1', 'dr1-fvmh0/sa2', 'dr1-fvmh0/si1466', + 'dr1-fvmh0/si2096', 'dr1-fvmh0/si836', 'dr1-fvmh0/sx116', + 'dr1-fvmh0/sx206', 'dr1-fvmh0/sx26', 'dr1-fvmh0/sx296', ...] + + >>> item = timit.utteranceids()[5] + >>> print(timit.phones(item)) + ['h#', 'k', 'l', 'ae', 's', 'pcl', 'p', 'dh', 'ax', + 's', 'kcl', 'k', 'r', 'ux', 'ix', 'nx', 'y', 'ax', + 'l', 'eh', 'f', 'tcl', 't', 'hh', 'ae', 'n', 'dcl', + 'd', 'h#'] + >>> print(timit.words(item)) + ['clasp', 'the', 'screw', 'in', 'your', 'left', 'hand'] + >>> timit.play(item) # doctest: +SKIP + +The corpus reader can combine the word segmentation information with +the phonemes to produce a single tree structure: + + >>> for tree in timit.phone_trees(item): + ... print(tree) + (S + h# + (clasp k l ae s pcl p) + (the dh ax) + (screw s kcl k r ux) + (in ix nx) + (your y ax) + (left l eh f tcl t) + (hand hh ae n dcl d) + h#) + +The start time and stop time of each phoneme, word, and sentence are +also available: + + >>> print(timit.phone_times(item)) + [('h#', 0, 2190), ('k', 2190, 3430), ('l', 3430, 4326), ...] + >>> print(timit.word_times(item)) + [('clasp', 2190, 8804), ('the', 8804, 9734), ...] + >>> print(timit.sent_times(item)) + [('Clasp the screw in your left hand.', 0, 32154)] + +We can use these times to play selected pieces of a speech sample: + + >>> timit.play(item, 2190, 8804) # 'clasp' # doctest: +SKIP + +The corpus reader can also be queried for information about the +speaker and sentence identifier for a given speech sample: + + >>> print(timit.spkrid(item)) + dr1-fvmh0 + >>> print(timit.sentid(item)) + sx116 + >>> print(timit.spkrinfo(timit.spkrid(item))) + SpeakerInfo(id='VMH0', + sex='F', + dr='1', + use='TRN', + recdate='03/11/86', + birthdate='01/08/60', + ht='5\'05"', + race='WHT', + edu='BS', + comments='BEST NEW ENGLAND ACCENT SO FAR') + + >>> # List the speech samples from the same speaker: + >>> timit.utteranceids(spkrid=timit.spkrid(item)) + ['dr1-fvmh0/sa1', 'dr1-fvmh0/sa2', 'dr1-fvmh0/si1466', ...] + +twitter_samples +--------------- + +Twitter is well-known microblog service that allows public data to be +collected via APIs. NLTK's twitter corpus currently contains a sample of 20k Tweets +retrieved from the Twitter Streaming API. + + >>> from nltk.corpus import twitter_samples + >>> twitter_samples.fileids() + ['negative_tweets.json', 'positive_tweets.json', 'tweets.20150430-223406.json'] + +We follow standard practice in storing full Tweets as line-separated +JSON. These data structures can be accessed via `tweets.docs()`. However, in general it +is more practical to focus just on the text field of the Tweets, which +are accessed via the `strings()` method. + + >>> twitter_samples.strings('tweets.20150430-223406.json')[:5] + ['RT @KirkKus: Indirect cost of the UK being in the EU is estimated to be costing Britain \xa3170 billion per year! #BetterOffOut #UKIP', ...] + +The default tokenizer for Tweets is specialised for 'casual' text, and +the `tokenized()` method returns a list of lists of tokens. + + >>> twitter_samples.tokenized('tweets.20150430-223406.json')[:5] + [['RT', '@KirkKus', ':', 'Indirect', 'cost', 'of', 'the', 'UK', 'being', 'in', ...], + ['VIDEO', ':', 'Sturgeon', 'on', 'post-election', 'deals', 'http://t.co/BTJwrpbmOY'], ...] + +rte +--- +The RTE (Recognizing Textual Entailment) corpus was derived from the +RTE1, RTE2 and RTE3 datasets (dev and test data), and consists of a +list of XML-formatted 'text'/'hypothesis' pairs. + + >>> from nltk.corpus import rte + >>> print(rte.fileids()) + ['rte1_dev.xml', 'rte1_test.xml', 'rte2_dev.xml', ..., 'rte3_test.xml'] + >>> rtepairs = rte.pairs(['rte2_test.xml', 'rte3_test.xml']) + >>> print(rtepairs) + [, , , ...] + +In the gold standard test sets, each pair is labeled according to +whether or not the text 'entails' the hypothesis; the +entailment value is mapped to an integer 1 (True) or 0 (False). + + >>> rtepairs[5] + + >>> rtepairs[5].text + 'His wife Strida won a seat in parliament after forging an alliance + with the main anti-Syrian coalition in the recent election.' + >>> rtepairs[5].hyp + 'Strida elected to parliament.' + >>> rtepairs[5].value + 1 + +The RTE corpus also supports an ``xml()`` method which produces ElementTrees. + + >>> xmltree = rte.xml('rte3_dev.xml') + >>> xmltree # doctest: +SKIP + + >>> xmltree[7].findtext('t') + "Mrs. Bush's approval ratings have remained very high, above 80%, + even as her husband's have recently dropped below 50%." + +verbnet +------- +The VerbNet corpus is a lexicon that divides verbs into classes, based +on their syntax-semantics linking behavior. The basic elements in the +lexicon are verb lemmas, such as 'abandon' and 'accept', and verb +classes, which have identifiers such as 'remove-10.1' and +'admire-31.2-1'. These class identifiers consist of a representative +verb selected from the class, followed by a numerical identifier. The +list of verb lemmas, and the list of class identifiers, can be +retrieved with the following methods: + + >>> from nltk.corpus import verbnet + >>> verbnet.lemmas()[20:25] + ['accelerate', 'accept', 'acclaim', 'accompany', 'accrue'] + >>> verbnet.classids()[:5] + ['accompany-51.7', 'admire-31.2', 'admire-31.2-1', 'admit-65', 'adopt-93'] + +The `classids()` method may also be used to retrieve the classes that +a given lemma belongs to: + + >>> verbnet.classids('accept') + ['approve-77', 'characterize-29.2-1-1', 'obtain-13.5.2'] + +The `classids()` method may additionally be used to retrieve all classes +within verbnet if nothing is passed: + + >>> verbnet.classids() + ['accompany-51.7', 'admire-31.2', 'admire-31.2-1', 'admit-65', 'adopt-93', 'advise-37.9', 'advise-37.9-1', 'allow-64', 'amalgamate-22.2', 'amalgamate-22.2-1', 'amalgamate-22.2-1-1', 'amalgamate-22.2-2', 'amalgamate-22.2-2-1', 'amalgamate-22.2-3', 'amalgamate-22.2-3-1', 'amalgamate-22.2-3-1-1', 'amalgamate-22.2-3-2', 'amuse-31.1', 'animal_sounds-38', 'appeal-31.4', 'appeal-31.4-1', 'appeal-31.4-2', 'appeal-31.4-3', 'appear-48.1.1', 'appoint-29.1', 'approve-77', 'assessment-34', 'assuming_position-50', 'avoid-52', 'banish-10.2', 'battle-36.4', 'battle-36.4-1', 'begin-55.1', 'begin-55.1-1', 'being_dressed-41.3.3', 'bend-45.2', 'berry-13.7', 'bill-54.5', 'body_internal_motion-49', 'body_internal_states-40.6', 'braid-41.2.2', 'break-45.1', 'breathe-40.1.2', 'breathe-40.1.2-1', 'bring-11.3', 'bring-11.3-1', 'build-26.1', 'build-26.1-1', 'bulge-47.5.3', 'bump-18.4', 'bump-18.4-1', 'butter-9.9', 'calibratable_cos-45.6', 'calibratable_cos-45.6-1', 'calve-28', 'captain-29.8', 'captain-29.8-1', 'captain-29.8-1-1', 'care-88', 'care-88-1', 'carry-11.4', 'carry-11.4-1', 'carry-11.4-1-1', 'carve-21.2', 'carve-21.2-1', 'carve-21.2-2', 'change_bodily_state-40.8.4', 'characterize-29.2', 'characterize-29.2-1', 'characterize-29.2-1-1', 'characterize-29.2-1-2', 'chase-51.6', 'cheat-10.6', 'cheat-10.6-1', 'cheat-10.6-1-1', 'chew-39.2', 'chew-39.2-1', 'chew-39.2-2', 'chit_chat-37.6', 'clear-10.3', 'clear-10.3-1', 'cling-22.5', 'coil-9.6', 'coil-9.6-1', 'coloring-24', 'complain-37.8', 'complete-55.2', 'concealment-16', 'concealment-16-1', 'confess-37.10', 'confine-92', 'confine-92-1', 'conjecture-29.5', 'conjecture-29.5-1', 'conjecture-29.5-2', 'consider-29.9', 'consider-29.9-1', 'consider-29.9-1-1', 'consider-29.9-1-1-1', 'consider-29.9-2', 'conspire-71', 'consume-66', 'consume-66-1', 'contiguous_location-47.8', 'contiguous_location-47.8-1', 'contiguous_location-47.8-2', 'continue-55.3', 'contribute-13.2', 'contribute-13.2-1', 'contribute-13.2-1-1', 'contribute-13.2-1-1-1', 'contribute-13.2-2', 'contribute-13.2-2-1', 'convert-26.6.2', 'convert-26.6.2-1', 'cooking-45.3', 'cooperate-73', 'cooperate-73-1', 'cooperate-73-2', 'cooperate-73-3', 'cope-83', 'cope-83-1', 'cope-83-1-1', 'correlate-86', 'correspond-36.1', 'correspond-36.1-1', 'correspond-36.1-1-1', 'cost-54.2', 'crane-40.3.2', 'create-26.4', 'create-26.4-1', 'curtsey-40.3.3', 'cut-21.1', 'cut-21.1-1', 'debone-10.8', 'declare-29.4', 'declare-29.4-1', 'declare-29.4-1-1', 'declare-29.4-1-1-1', 'declare-29.4-1-1-2', 'declare-29.4-1-1-3', 'declare-29.4-2', 'dedicate-79', 'defend-85', 'destroy-44', 'devour-39.4', 'devour-39.4-1', 'devour-39.4-2', 'differ-23.4', 'dine-39.5', 'disappearance-48.2', 'disassemble-23.3', 'discover-84', 'discover-84-1', 'discover-84-1-1', 'dress-41.1.1', 'dressing_well-41.3.2', 'drive-11.5', 'drive-11.5-1', 'dub-29.3', 'dub-29.3-1', 'eat-39.1', 'eat-39.1-1', 'eat-39.1-2', 'enforce-63', 'engender-27', 'entity_specific_cos-45.5', 'entity_specific_modes_being-47.2', 'equip-13.4.2', 'equip-13.4.2-1', 'equip-13.4.2-1-1', 'escape-51.1', 'escape-51.1-1', 'escape-51.1-2', 'escape-51.1-2-1', 'exceed-90', 'exchange-13.6', 'exchange-13.6-1', 'exchange-13.6-1-1', 'exhale-40.1.3', 'exhale-40.1.3-1', 'exhale-40.1.3-2', 'exist-47.1', 'exist-47.1-1', 'exist-47.1-1-1', 'feeding-39.7', 'ferret-35.6', 'fill-9.8', 'fill-9.8-1', 'fit-54.3', 'flinch-40.5', 'floss-41.2.1', 'focus-87', 'forbid-67', 'force-59', 'force-59-1', 'free-80', 'free-80-1', 'fulfilling-13.4.1', 'fulfilling-13.4.1-1', 'fulfilling-13.4.1-2', 'funnel-9.3', 'funnel-9.3-1', 'funnel-9.3-2', 'funnel-9.3-2-1', 'future_having-13.3', 'get-13.5.1', 'get-13.5.1-1', 'give-13.1', 'give-13.1-1', 'gobble-39.3', 'gobble-39.3-1', 'gobble-39.3-2', 'gorge-39.6', 'groom-41.1.2', 'grow-26.2', 'help-72', 'help-72-1', 'herd-47.5.2', 'hiccup-40.1.1', 'hit-18.1', 'hit-18.1-1', 'hold-15.1', 'hold-15.1-1', 'hunt-35.1', 'hurt-40.8.3', 'hurt-40.8.3-1', 'hurt-40.8.3-1-1', 'hurt-40.8.3-2', 'illustrate-25.3', 'image_impression-25.1', 'indicate-78', 'indicate-78-1', 'indicate-78-1-1', 'inquire-37.1.2', 'instr_communication-37.4', 'investigate-35.4', 'judgement-33', 'keep-15.2', 'knead-26.5', 'learn-14', 'learn-14-1', 'learn-14-2', 'learn-14-2-1', 'leave-51.2', 'leave-51.2-1', 'lecture-37.11', 'lecture-37.11-1', 'lecture-37.11-1-1', 'lecture-37.11-2', 'light_emission-43.1', 'limit-76', 'linger-53.1', 'linger-53.1-1', 'lodge-46', 'long-32.2', 'long-32.2-1', 'long-32.2-2', 'manner_speaking-37.3', 'marry-36.2', 'marvel-31.3', 'marvel-31.3-1', 'marvel-31.3-2', 'marvel-31.3-3', 'marvel-31.3-4', 'marvel-31.3-5', 'marvel-31.3-6', 'marvel-31.3-7', 'marvel-31.3-8', 'marvel-31.3-9', 'masquerade-29.6', 'masquerade-29.6-1', 'masquerade-29.6-2', 'matter-91', 'meander-47.7', 'meet-36.3', 'meet-36.3-1', 'meet-36.3-2', 'mine-10.9', 'mix-22.1', 'mix-22.1-1', 'mix-22.1-1-1', 'mix-22.1-2', 'mix-22.1-2-1', 'modes_of_being_with_motion-47.3', 'murder-42.1', 'murder-42.1-1', 'neglect-75', 'neglect-75-1', 'neglect-75-1-1', 'neglect-75-2', 'nonvehicle-51.4.2', 'nonverbal_expression-40.2', 'obtain-13.5.2', 'obtain-13.5.2-1', 'occurrence-48.3', 'order-60', 'order-60-1', 'orphan-29.7', 'other_cos-45.4', 'pain-40.8.1', 'pay-68', 'peer-30.3', 'pelt-17.2', 'performance-26.7', 'performance-26.7-1', 'performance-26.7-1-1', 'performance-26.7-2', 'performance-26.7-2-1', 'pit-10.7', 'pocket-9.10', 'pocket-9.10-1', 'poison-42.2', 'poke-19', 'pour-9.5', 'preparing-26.3', 'preparing-26.3-1', 'preparing-26.3-2', 'price-54.4', 'push-12', 'push-12-1', 'push-12-1-1', 'put-9.1', 'put-9.1-1', 'put-9.1-2', 'put_direction-9.4', 'put_spatial-9.2', 'put_spatial-9.2-1', 'reach-51.8', 'reflexive_appearance-48.1.2', 'refrain-69', 'register-54.1', 'rely-70', 'remove-10.1', 'risk-94', 'risk-94-1', 'roll-51.3.1', 'rummage-35.5', 'run-51.3.2', 'rush-53.2', 'say-37.7', 'say-37.7-1', 'say-37.7-1-1', 'say-37.7-2', 'scribble-25.2', 'search-35.2', 'see-30.1', 'see-30.1-1', 'see-30.1-1-1', 'send-11.1', 'send-11.1-1', 'separate-23.1', 'separate-23.1-1', 'separate-23.1-2', 'settle-89', 'shake-22.3', 'shake-22.3-1', 'shake-22.3-1-1', 'shake-22.3-2', 'shake-22.3-2-1', 'sight-30.2', 'simple_dressing-41.3.1', 'slide-11.2', 'slide-11.2-1-1', 'smell_emission-43.3', 'snooze-40.4', 'sound_emission-43.2', 'sound_existence-47.4', 'spank-18.3', 'spatial_configuration-47.6', 'split-23.2', 'spray-9.7', 'spray-9.7-1', 'spray-9.7-1-1', 'spray-9.7-2', 'stalk-35.3', 'steal-10.5', 'stimulus_subject-30.4', 'stop-55.4', 'stop-55.4-1', 'substance_emission-43.4', 'succeed-74', 'succeed-74-1', 'succeed-74-1-1', 'succeed-74-2', 'suffocate-40.7', 'suspect-81', 'swarm-47.5.1', 'swarm-47.5.1-1', 'swarm-47.5.1-2', 'swarm-47.5.1-2-1', 'swat-18.2', 'talk-37.5', 'tape-22.4', 'tape-22.4-1', 'tell-37.2', 'throw-17.1', 'throw-17.1-1', 'throw-17.1-1-1', 'tingle-40.8.2', 'touch-20', 'touch-20-1', 'transcribe-25.4', 'transfer_mesg-37.1.1', 'transfer_mesg-37.1.1-1', 'transfer_mesg-37.1.1-1-1', 'try-61', 'turn-26.6.1', 'turn-26.6.1-1', 'urge-58', 'vehicle-51.4.1', 'vehicle-51.4.1-1', 'waltz-51.5', 'want-32.1', 'want-32.1-1', 'want-32.1-1-1', 'weather-57', 'weekend-56', 'wink-40.3.1', 'wink-40.3.1-1', 'wipe_instr-10.4.2', 'wipe_instr-10.4.2-1', 'wipe_manner-10.4.1', 'wipe_manner-10.4.1-1', 'wish-62', 'withdraw-82', 'withdraw-82-1', 'withdraw-82-2', 'withdraw-82-3'] + +The primary object in the lexicon is a class record, which is stored +as an ElementTree xml object. The class record for a given class +identifier is returned by the `vnclass()` method: + + >>> verbnet.vnclass('remove-10.1') + + +The `vnclass()` method also accepts "short" identifiers, such as '10.1': + + >>> verbnet.vnclass('10.1') + + +See the Verbnet documentation, or the Verbnet files, for information +about the structure of this xml. As an example, we can retrieve a +list of thematic roles for a given Verbnet class: + + >>> vn_31_2 = verbnet.vnclass('admire-31.2') + >>> for themrole in vn_31_2.findall('THEMROLES/THEMROLE'): + ... print(themrole.attrib['type'], end=' ') + ... for selrestr in themrole.findall('SELRESTRS/SELRESTR'): + ... print('[%(Value)s%(type)s]' % selrestr.attrib, end=' ') + ... print() + Theme + Experiencer [+animate] + Predicate + +The Verbnet corpus also provides a variety of pretty printing +functions that can be used to display the xml contents in a more +concise form. The simplest such method is `pprint()`: + + >>> print(verbnet.pprint('57')) + weather-57 + Subclasses: (none) + Members: blow clear drizzle fog freeze gust hail howl lightning mist + mizzle pelt pour precipitate rain roar shower sleet snow spit spot + sprinkle storm swelter teem thaw thunder + Thematic roles: + * Theme[+concrete +force] + Frames: + Intransitive (Expletive Subject) + Example: It's raining. + Syntax: LEX[it] LEX[[+be]] VERB + Semantics: + * weather(during(E), Weather_type, ?Theme) + NP (Expletive Subject, Theme Object) + Example: It's raining cats and dogs. + Syntax: LEX[it] LEX[[+be]] VERB NP[Theme] + Semantics: + * weather(during(E), Weather_type, Theme) + PP (Expletive Subject, Theme-PP) + Example: It was pelting with rain. + Syntax: LEX[it[+be]] VERB PREP[with] NP[Theme] + Semantics: + * weather(during(E), Weather_type, Theme) + +Verbnet gives us frames that link the syntax and semantics using an example. +These frames are part of the corpus and we can use `frames()` to get a frame +for a given verbnet class. + + >>> frame = verbnet.frames('57') + >>> frame == [{'example': "It's raining.", 'description': {'primary': 'Intransitive', 'secondary': 'Expletive Subject'}, 'syntax': [{'pos_tag': 'LEX', 'modifiers': {'value': 'it', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'LEX', 'modifiers': {'value': '[+be]', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'VERB', 'modifiers': {'value': '', 'selrestrs': [], 'synrestrs': []}}], 'semantics': [{'predicate_value': 'weather', 'arguments': [{'type': 'Event', 'value': 'during(E)'}, {'type': 'VerbSpecific', 'value': 'Weather_type'}, {'type': 'ThemRole', 'value': '?Theme'}], 'negated': False}]}, {'example': "It's raining cats and dogs.", 'description': {'primary': 'NP', 'secondary': 'Expletive Subject, Theme Object'}, 'syntax': [{'pos_tag': 'LEX', 'modifiers': {'value': 'it', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'LEX', 'modifiers': {'value': '[+be]', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'VERB', 'modifiers': {'value': '', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'NP', 'modifiers': {'value': 'Theme', 'selrestrs': [], 'synrestrs': []}}], 'semantics': [{'predicate_value': 'weather', 'arguments': [{'type': 'Event', 'value': 'during(E)'}, {'type': 'VerbSpecific', 'value': 'Weather_type'}, {'type': 'ThemRole', 'value': 'Theme'}], 'negated': False}]}, {'example': 'It was pelting with rain.', 'description': {'primary': 'PP', 'secondary': 'Expletive Subject, Theme-PP'}, 'syntax': [{'pos_tag': 'LEX', 'modifiers': {'value': 'it[+be]', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'VERB', 'modifiers': {'value': '', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'PREP', 'modifiers': {'value': 'with', 'selrestrs': [], 'synrestrs': []}}, {'pos_tag': 'NP', 'modifiers': {'value': 'Theme', 'selrestrs': [], 'synrestrs': []}}], 'semantics': [{'predicate_value': 'weather', 'arguments': [{'type': 'Event', 'value': 'during(E)'}, {'type': 'VerbSpecific', 'value': 'Weather_type'}, {'type': 'ThemRole', 'value': 'Theme'}], 'negated': False}]}] + True + +Verbnet corpus lets us access thematic roles individually using `themroles()`. + + >>> themroles = verbnet.themroles('57') + >>> themroles == [{'modifiers': [{'type': 'concrete', 'value': '+'}, {'type': 'force', 'value': '+'}], 'type': 'Theme'}] + True + +Verbnet classes may also have subclasses sharing similar syntactic and semantic properties +while having differences with the superclass. The Verbnet corpus allows us to access these +subclasses using `subclasses()`. + + >>> print(verbnet.subclasses('9.1')) #Testing for 9.1 since '57' does not have subclasses + ['put-9.1-1', 'put-9.1-2'] + + +nps_chat +-------- + +The NPS Chat Corpus, Release 1.0 consists of over 10,000 posts in age-specific +chat rooms, which have been anonymized, POS-tagged and dialogue-act tagged. + + >>> print(nltk.corpus.nps_chat.words()) + ['now', 'im', 'left', 'with', 'this', 'gay', ...] + >>> print(nltk.corpus.nps_chat.tagged_words()) + [('now', 'RB'), ('im', 'PRP'), ('left', 'VBD'), ...] + >>> print(nltk.corpus.nps_chat.tagged_posts()) + [[('now', 'RB'), ('im', 'PRP'), ('left', 'VBD'), ('with', 'IN'), + ('this', 'DT'), ('gay', 'JJ'), ('name', 'NN')], [(':P', 'UH')], ...] + +We can access the XML elements corresponding to individual posts. These elements +have ``class`` and ``user`` attributes that we can access using ``p.attrib['class']`` +and ``p.attrib['user']``. They also have text content, accessed using ``p.text``. + + >>> print(nltk.corpus.nps_chat.xml_posts()) + [, , ...] + >>> posts = nltk.corpus.nps_chat.xml_posts() + >>> sorted(nltk.FreqDist(p.attrib['class'] for p in posts).keys()) + ['Accept', 'Bye', 'Clarify', 'Continuer', 'Emotion', 'Emphasis', + 'Greet', 'Other', 'Reject', 'Statement', 'System', 'nAnswer', + 'whQuestion', 'yAnswer', 'ynQuestion'] + >>> posts[0].text + 'now im left with this gay name' + +In addition to the above methods for accessing tagged text, we can navigate +the XML structure directly, as follows: + + >>> tokens = posts[0].findall('terminals/t') + >>> [t.attrib['pos'] + "/" + t.attrib['word'] for t in tokens] + ['RB/now', 'PRP/im', 'VBD/left', 'IN/with', 'DT/this', 'JJ/gay', 'NN/name'] + +multext_east +------------ + +The Multext-East Corpus consists of POS-tagged versions of George Orwell's book +1984 in 12 languages: English, Czech, Hungarian, Macedonian, Slovenian, Serbian, +Slovak, Romanian, Estonian, Farsi, Bulgarian and Polish. +The corpus can be accessed using the usual methods for tagged corpora. The tagset +can be transformed from the Multext-East specific MSD tags to the Universal tagset +using the "tagset" parameter of all functions returning tagged parts of the corpus. + + >>> print(nltk.corpus.multext_east.words("oana-en.xml")) + ['It', 'was', 'a', 'bright', ...] + >>> print(nltk.corpus.multext_east.tagged_words("oana-en.xml")) + [('It', '#Pp3ns'), ('was', '#Vmis3s'), ('a', '#Di'), ...] + >>> print(nltk.corpus.multext_east.tagged_sents("oana-en.xml", "universal")) + [[('It', 'PRON'), ('was', 'VERB'), ('a', 'DET'), ...] + + + +--------------------- +Corpus Reader Classes +--------------------- + +NLTK's *corpus reader* classes are used to access the contents of a +diverse set of corpora. Each corpus reader class is specialized to +handle a specific corpus format. Examples include the +`PlaintextCorpusReader`, which handles corpora that consist of a set +of unannotated text files, and the `BracketParseCorpusReader`, which +handles corpora that consist of files containing +parenthesis-delineated parse trees. + +Automatically Created Corpus Reader Instances +============================================= + +When the `nltk.corpus` module is imported, it automatically creates a +set of corpus reader instances that can be used to access the corpora +in the NLTK data distribution. Here is a small sample of those +corpus reader instances: + + >>> import nltk + >>> nltk.corpus.brown + + >>> nltk.corpus.treebank + + >>> nltk.corpus.names + + >>> nltk.corpus.genesis + + >>> nltk.corpus.inaugural + + +This sample illustrates that different corpus reader classes are used +to read different corpora; but that the same corpus reader class may +be used for more than one corpus (e.g., ``genesis`` and ``inaugural``). + +Creating New Corpus Reader Instances +==================================== + +Although the `nltk.corpus` module automatically creates corpus reader +instances for the corpora in the NLTK data distribution, you may +sometimes need to create your own corpus reader. In particular, you +would need to create your own corpus reader if you want... + +- To access a corpus that is not included in the NLTK data + distribution. + +- To access a full copy of a corpus for which the NLTK data + distribution only provides a sample. + +- To access a corpus using a customized corpus reader (e.g., with + a customized tokenizer). + +To create a new corpus reader, you will first need to look up the +signature for that corpus reader's constructor. Different corpus +readers have different constructor signatures, but most of the +constructor signatures have the basic form:: + + SomeCorpusReader(root, files, ...options...) + +Where ``root`` is an absolute path to the directory containing the +corpus data files; ``files`` is either a list of file names (relative +to ``root``) or a regexp specifying which files should be included; +and ``options`` are additional reader-specific options. For example, +we can create a customized corpus reader for the genesis corpus that +uses a different sentence tokenizer as follows: + + >>> # Find the directory where the corpus lives. + >>> genesis_dir = nltk.data.find('corpora/genesis') + >>> # Create our custom sentence tokenizer. + >>> my_sent_tokenizer = nltk.RegexpTokenizer('[^.!?]+') + >>> # Create the new corpus reader object. + >>> my_genesis = nltk.corpus.PlaintextCorpusReader( + ... genesis_dir, r'.*\.txt', sent_tokenizer=my_sent_tokenizer) + >>> # Use the new corpus reader object. + >>> print(my_genesis.sents('english-kjv.txt')[0]) + ['In', 'the', 'beginning', 'God', 'created', 'the', 'heaven', + 'and', 'the', 'earth'] + +If you wish to read your own plaintext corpus, which is stored in the +directory '/usr/share/some-corpus', then you can create a corpus +reader for it with:: + + >>> my_corpus = nltk.corpus.PlaintextCorpusReader( + ... '/usr/share/some-corpus', r'.*\.txt') # doctest: +SKIP + +For a complete list of corpus reader subclasses, see the API +documentation for `nltk.corpus.reader`. + +Corpus Types +============ + +Corpora vary widely in the types of content they include. This is +reflected in the fact that the base class `CorpusReader` only defines +a few general-purpose methods for listing and accessing the files that +make up a corpus. It is up to the subclasses to define *data access +methods* that provide access to the information in the corpus. +However, corpus reader subclasses should be consistent in their +definitions of these data access methods wherever possible. + +At a high level, corpora can be divided into three basic types: + +- A *token corpus* contains information about specific occurrences of + language use (or linguistic tokens), such as dialogues or written + texts. Examples of token corpora are collections of written text + and collections of speech. + +- A *type corpus*, or *lexicon*, contains information about a coherent + set of lexical items (or linguistic types). Examples of lexicons + are dictionaries and word lists. + +- A *language description corpus* contains information about a set of + non-lexical linguistic constructs, such as grammar rules. + +However, many individual corpora blur the distinctions between these +types. For example, corpora that are primarily lexicons may include +token data in the form of example sentences; and corpora that are +primarily token corpora may be accompanied by one or more word lists +or other lexical data sets. + +Because corpora vary so widely in their information content, we have +decided that it would not be wise to use separate corpus reader base +classes for different corpus types. Instead, we simply try to make +the corpus readers consistent wherever possible, but let them differ +where the underlying data itself differs. + +Common Corpus Reader Methods +============================ + +As mentioned above, there are only a handful of methods that all +corpus readers are guaranteed to implement. These methods provide +access to the files that contain the corpus data. Every corpus is +assumed to consist of one or more files, all located in a common root +directory (or in subdirectories of that root directory). The absolute +path to the root directory is stored in the ``root`` property: + + >>> import os + >>> str(nltk.corpus.genesis.root).replace(os.path.sep,'/') + '.../nltk_data/corpora/genesis' + +Each file within the corpus is identified by a platform-independent +identifier, which is basically a path string that uses ``/`` as the +path separator. I.e., this identifier can be converted to a relative +path as follows: + + >>> some_corpus_file_id = nltk.corpus.reuters.fileids()[0] + >>> import os.path + >>> os.path.normpath(some_corpus_file_id).replace(os.path.sep,'/') + 'test/14826' + +To get a list of all data files that make up a corpus, use the +``fileids()`` method. In some corpora, these files will not all contain +the same type of data; for example, for the ``nltk.corpus.timit`` +corpus, ``fileids()`` will return a list including text files, word +segmentation files, phonetic transcription files, sound files, and +metadata files. For corpora with diverse file types, the ``fileids()`` +method will often take one or more optional arguments, which can be +used to get a list of the files with a specific file type: + + >>> nltk.corpus.timit.fileids() + ['dr1-fvmh0/sa1.phn', 'dr1-fvmh0/sa1.txt', 'dr1-fvmh0/sa1.wav', ...] + >>> nltk.corpus.timit.fileids('phn') + ['dr1-fvmh0/sa1.phn', 'dr1-fvmh0/sa2.phn', 'dr1-fvmh0/si1466.phn', ...] + +In some corpora, the files are divided into distinct categories. For +these corpora, the ``fileids()`` method takes an optional argument, +which can be used to get a list of the files within a specific category: + + >>> nltk.corpus.brown.fileids('hobbies') + ['ce01', 'ce02', 'ce03', 'ce04', 'ce05', 'ce06', 'ce07', ...] + +The ``abspath()`` method can be used to find the absolute path to a +corpus file, given its file identifier: + + >>> str(nltk.corpus.brown.abspath('ce06')).replace(os.path.sep,'/') + '.../corpora/brown/ce06' + +The ``abspaths()`` method can be used to find the absolute paths for +one corpus file, a list of corpus files, or (if no fileids are specified), +all corpus files. + +This method is mainly useful as a helper method when defining corpus +data access methods, since data access methods can usually be called +with a string argument (to get a view for a specific file), with a +list argument (to get a view for a specific list of files), or with no +argument (to get a view for the whole corpus). + +Data Access Methods +=================== + +Individual corpus reader subclasses typically extend this basic set of +file-access methods with one or more *data access methods*, which provide +easy access to the data contained in the corpus. The signatures for +data access methods often have the basic form:: + + corpus_reader.some_data access(fileids=None, ...options...) + +Where ``fileids`` can be a single file identifier string (to get a view +for a specific file); a list of file identifier strings (to get a view +for a specific list of files); or None (to get a view for the entire +corpus). Some of the common data access methods, and their return +types, are: + + - I{corpus}.words(): list of str + - I{corpus}.sents(): list of (list of str) + - I{corpus}.paras(): list of (list of (list of str)) + - I{corpus}.tagged_words(): list of (str,str) tuple + - I{corpus}.tagged_sents(): list of (list of (str,str)) + - I{corpus}.tagged_paras(): list of (list of (list of (str,str))) + - I{corpus}.chunked_sents(): list of (Tree w/ (str,str) leaves) + - I{corpus}.parsed_sents(): list of (Tree with str leaves) + - I{corpus}.parsed_paras(): list of (list of (Tree with str leaves)) + - I{corpus}.xml(): A single xml ElementTree + - I{corpus}.raw(): str (unprocessed corpus contents) + +For example, the `words()` method is supported by many different +corpora, and returns a flat list of word strings: + + >>> nltk.corpus.brown.words() + ['The', 'Fulton', 'County', 'Grand', 'Jury', ...] + >>> nltk.corpus.treebank.words() + ['Pierre', 'Vinken', ',', '61', 'years', 'old', ...] + >>> nltk.corpus.conll2002.words() + ['Sao', 'Paulo', '(', 'Brasil', ')', ',', '23', ...] + >>> nltk.corpus.genesis.words() + ['In', 'the', 'beginning', 'God', 'created', ...] + +On the other hand, the `tagged_words()` method is only supported by +corpora that include part-of-speech annotations: + + >>> nltk.corpus.brown.tagged_words() + [('The', 'AT'), ('Fulton', 'NP-TL'), ...] + >>> nltk.corpus.treebank.tagged_words() + [('Pierre', 'NNP'), ('Vinken', 'NNP'), ...] + >>> nltk.corpus.conll2002.tagged_words() + [('Sao', 'NC'), ('Paulo', 'VMI'), ('(', 'Fpa'), ...] + >>> nltk.corpus.genesis.tagged_words() + Traceback (most recent call last): + ... + AttributeError: 'PlaintextCorpusReader' object has no attribute 'tagged_words' + +Although most corpus readers use file identifiers to index their +content, some corpora use different identifiers instead. For example, +the data access methods for the ``timit`` corpus uses *utterance +identifiers* to select which corpus items should be returned: + + >>> nltk.corpus.timit.utteranceids() + ['dr1-fvmh0/sa1', 'dr1-fvmh0/sa2', 'dr1-fvmh0/si1466', ...] + >>> nltk.corpus.timit.words('dr1-fvmh0/sa2') + ["don't", 'ask', 'me', 'to', 'carry', 'an', 'oily', 'rag', 'like', 'that'] + +Attempting to call ``timit``\ 's data access methods with a file +identifier will result in an exception: + + >>> nltk.corpus.timit.fileids() + ['dr1-fvmh0/sa1.phn', 'dr1-fvmh0/sa1.txt', 'dr1-fvmh0/sa1.wav', ...] + >>> nltk.corpus.timit.words('dr1-fvmh0/sa1.txt') # doctest: +SKIP + Traceback (most recent call last): + ... + IOError: No such file or directory: '.../dr1-fvmh0/sa1.txt.wrd' + +As another example, the ``propbank`` corpus defines the ``roleset()`` +method, which expects a roleset identifier, not a file identifier: + + >>> roleset = nltk.corpus.propbank.roleset('eat.01') + >>> from xml.etree import ElementTree as ET + >>> print(ET.tostring(roleset).decode('utf8')) + + + ...... + ... + ... + +Stream Backed Corpus Views +========================== +An important feature of NLTK's corpus readers is that many of them +access the underlying data files using "corpus views." A *corpus +view* is an object that acts like a simple data structure (such as a +list), but does not store the data elements in memory; instead, data +elements are read from the underlying data files on an as-needed +basis. + +By only loading items from the file on an as-needed basis, corpus +views maintain both memory efficiency and responsiveness. The memory +efficiency of corpus readers is important because some corpora contain +very large amounts of data, and storing the entire data set in memory +could overwhelm many machines. The responsiveness is important when +experimenting with corpora in interactive sessions and in in-class +demonstrations. + +The most common corpus view is the `StreamBackedCorpusView`, which +acts as a read-only list of tokens. Two additional corpus view +classes, `ConcatenatedCorpusView` and `LazySubsequence`, make it +possible to create concatenations and take slices of +`StreamBackedCorpusView` objects without actually storing the +resulting list-like object's elements in memory. + +In the future, we may add additional corpus views that act like other +basic data structures, such as dictionaries. + +Writing New Corpus Readers +========================== + +In order to add support for new corpus formats, it is necessary to +define new corpus reader classes. For many corpus formats, writing +new corpus readers is relatively straight-forward. In this section, +we'll describe what's involved in creating a new corpus reader. If +you do create a new corpus reader, we encourage you to contribute it +back to the NLTK project. + +Don't Reinvent the Wheel +------------------------ +Before you start writing a new corpus reader, you should check to be +sure that the desired format can't be read using an existing corpus +reader with appropriate constructor arguments. For example, although +the `TaggedCorpusReader` assumes that words and tags are separated by +``/`` characters by default, an alternative tag-separation character +can be specified via the ``sep`` constructor argument. You should +also check whether the new corpus format can be handled by subclassing +an existing corpus reader, and tweaking a few methods or variables. + +Design +------ +If you decide to write a new corpus reader from scratch, then you +should first decide which data access methods you want the reader to +provide, and what their signatures should be. You should look at +existing corpus readers that process corpora with similar data +contents, and try to be consistent with those corpus readers whenever +possible. + +You should also consider what sets of identifiers are appropriate for +the corpus format. Where it's practical, file identifiers should be +used. However, for some corpora, it may make sense to use additional +sets of identifiers. Each set of identifiers should have a distinct +name (e.g., fileids, utteranceids, rolesets); and you should be consistent +in using that name to refer to that identifier. Do not use parameter +names like ``id``, which leave it unclear what type of identifier is +required. + +Once you've decided what data access methods and identifiers are +appropriate for your corpus, you should decide if there are any +customizable parameters that you'd like the corpus reader to handle. +These parameters make it possible to use a single corpus reader to +handle a wider variety of corpora. The ``sep`` argument for +`TaggedCorpusReader`, mentioned above, is an example of a customizable +corpus reader parameter. + +Implementation +-------------- + +Constructor +~~~~~~~~~~~ +If your corpus reader implements any customizable parameters, then +you'll need to override the constructor. Typically, the new +constructor will first call its base class's constructor, and then +store the customizable parameters. For example, the +`ConllChunkCorpusReader`\ 's constructor is defined as follows: + + >>> def __init__(self, root, fileids, chunk_types, encoding='utf8', + ... tagset=None, separator=None): + ... ConllCorpusReader.__init__( + ... self, root, fileids, ('words', 'pos', 'chunk'), + ... chunk_types=chunk_types, encoding=encoding, + ... tagset=tagset, separator=separator) + +If your corpus reader does not implement any customization parameters, +then you can often just inherit the base class's constructor. + +Data Access Methods +~~~~~~~~~~~~~~~~~~~ + +The most common type of data access method takes an argument +identifying which files to access, and returns a view covering those +files. This argument may be a single file identifier string (to get a +view for a specific file); a list of file identifier strings (to get a +view for a specific list of files); or None (to get a view for the +entire corpus). The method's implementation converts this argument to +a list of path names using the `abspaths()` method, which handles all +three value types (string, list, and None): + + >>> print(str(nltk.corpus.brown.abspaths()).replace('\\\\','/')) + [FileSystemPathPointer('.../corpora/brown/ca01'), + FileSystemPathPointer('.../corpora/brown/ca02'), ...] + >>> print(str(nltk.corpus.brown.abspaths('ce06')).replace('\\\\','/')) + [FileSystemPathPointer('.../corpora/brown/ce06')] + >>> print(str(nltk.corpus.brown.abspaths(['ce06', 'ce07'])).replace('\\\\','/')) + [FileSystemPathPointer('.../corpora/brown/ce06'), + FileSystemPathPointer('.../corpora/brown/ce07')] + +An example of this type of method is the `words()` method, defined by +the `PlaintextCorpusReader` as follows: + + >>> def words(self, fileids=None): + ... return concat([self.CorpusView(fileid, self._read_word_block) + ... for fileid in self.abspaths(fileids)]) + +This method first uses `abspaths()` to convert ``fileids`` to a list of +absolute paths. It then creates a corpus view for each file, using +the `PlaintextCorpusReader._read_word_block()` method to read elements +from the data file (see the discussion of corpus views below). +Finally, it combines these corpus views using the +`nltk.corpus.reader.util.concat()` function. + +When writing a corpus reader for a corpus that is never expected to be +very large, it can sometimes be appropriate to read the files +directly, rather than using a corpus view. For example, the +`WordListCorpusView` class defines its `words()` method as follows: + + >>> def words(self, fileids=None): + ... return concat([[w for w in open(fileid).read().split('\n') if w] + ... for fileid in self.abspaths(fileids)]) + +(This is usually more appropriate for lexicons than for token corpora.) + +If the type of data returned by a data access method is one for which +NLTK has a conventional representation (e.g., words, tagged words, and +parse trees), then you should use that representation. Otherwise, you +may find it necessary to define your own representation. For data +structures that are relatively corpus-specific, it's usually best to +define new classes for these elements. For example, the ``propbank`` +corpus defines the `PropbankInstance` class to store the semantic role +labeling instances described by the corpus; and the ``ppattach`` +corpus defines the `PPAttachment` class to store the prepositional +attachment instances described by the corpus. + +Corpus Views +~~~~~~~~~~~~ +.. (Much of the content for this section is taken from the + StreamBackedCorpusView docstring.) + +The heart of a `StreamBackedCorpusView` is its *block reader* +function, which reads zero or more tokens from a stream, and returns +them as a list. A very simple example of a block reader is: + + >>> def simple_block_reader(stream): + ... return stream.readline().split() + +This simple block reader reads a single line at a time, and returns a +single token (consisting of a string) for each whitespace-separated +substring on the line. A `StreamBackedCorpusView` built from this +block reader will act like a read-only list of all the +whitespace-separated tokens in an underlying file. + +When deciding how to define the block reader for a given corpus, +careful consideration should be given to the size of blocks handled by +the block reader. Smaller block sizes will increase the memory +requirements of the corpus view's internal data structures (by 2 +integers per block). On the other hand, larger block sizes may +decrease performance for random access to the corpus. (But note that +larger block sizes will *not* decrease performance for iteration.) + +Internally, the `StreamBackedCorpusView` class maintains a partial +mapping from token index to file position, with one entry per block. +When a token with a given index *i* is requested, the corpus view +constructs it as follows: + +1. First, it searches the toknum/filepos mapping for the token index + closest to (but less than or equal to) *i*. + +2. Then, starting at the file position corresponding to that index, it + reads one block at a time using the block reader until it reaches + the requested token. + +The toknum/filepos mapping is created lazily: it is initially empty, +but every time a new block is read, the block's initial token is added +to the mapping. (Thus, the toknum/filepos map has one entry per +block.) + +You can create your own corpus view in one of two ways: + +1. Call the `StreamBackedCorpusView` constructor, and provide your + block reader function via the ``block_reader`` argument. + +2. Subclass `StreamBackedCorpusView`, and override the + `read_block()` method. + +The first option is usually easier, but the second option can allow +you to write a single `read_block` method whose behavior can be +customized by different parameters to the subclass's constructor. For +an example of this design pattern, see the `TaggedCorpusView` class, +which is used by `TaggedCorpusView`. + +---------------- +Regression Tests +---------------- + +The following helper functions are used to create and then delete +testing corpora that are stored in temporary directories. These +testing corpora are used to make sure the readers work correctly. + + >>> import tempfile, os.path, textwrap + >>> def make_testcorpus(ext='', **fileids): + ... root = tempfile.mkdtemp() + ... for fileid, contents in fileids.items(): + ... fileid += ext + ... f = open(os.path.join(root, fileid), 'w') + ... f.write(textwrap.dedent(contents)) + ... f.close() + ... return root + >>> def del_testcorpus(root): + ... for fileid in os.listdir(root): + ... os.remove(os.path.join(root, fileid)) + ... os.rmdir(root) + +Plaintext Corpus Reader +======================= +The plaintext corpus reader is used to access corpora that consist of +unprocessed plaintext data. It assumes that paragraph breaks are +indicated by blank lines. Sentences and words can be tokenized using +the default tokenizers, or by custom tokenizers specified as +parameters to the constructor. + + >>> root = make_testcorpus(ext='.txt', + ... a="""\ + ... This is the first sentence. Here is another + ... sentence! And here's a third sentence. + ... + ... This is the second paragraph. Tokenization is currently + ... fairly simple, so the period in Mr. gets tokenized. + ... """, + ... b="""This is the second file.""") + + >>> from nltk.corpus.reader.plaintext import PlaintextCorpusReader + +The list of documents can be specified explicitly, or implicitly (using a +regexp). The ``ext`` argument specifies a file extension. + + >>> corpus = PlaintextCorpusReader(root, ['a.txt', 'b.txt']) + >>> corpus.fileids() + ['a.txt', 'b.txt'] + >>> corpus = PlaintextCorpusReader(root, r'.*\.txt') + >>> corpus.fileids() + ['a.txt', 'b.txt'] + +The directory containing the corpus is corpus.root: + + >>> str(corpus.root) == str(root) + True + +We can get a list of words, or the raw string: + + >>> corpus.words() + ['This', 'is', 'the', 'first', 'sentence', '.', ...] + >>> corpus.raw()[:40] + 'This is the first sentence. Here is ano' + +Check that reading individual documents works, and reading all documents at +once works: + + >>> len(corpus.words()), [len(corpus.words(d)) for d in corpus.fileids()] + (46, [40, 6]) + >>> corpus.words('a.txt') + ['This', 'is', 'the', 'first', 'sentence', '.', ...] + >>> corpus.words('b.txt') + ['This', 'is', 'the', 'second', 'file', '.'] + >>> corpus.words()[:4], corpus.words()[-4:] + (['This', 'is', 'the', 'first'], ['the', 'second', 'file', '.']) + +We're done with the test corpus: + + >>> del_testcorpus(root) + +Test the plaintext corpora that come with nltk: + + >>> from nltk.corpus import abc, genesis, inaugural + >>> from nltk.corpus import state_union, webtext + >>> for corpus in (abc, genesis, inaugural, state_union, + ... webtext): + ... print(str(corpus).replace('\\\\','/')) + ... print(' ', repr(corpus.fileids())[:60]) + ... print(' ', repr(corpus.words()[:10])[:60]) + + ['rural.txt', 'science.txt'] + ['PM', 'denies', 'knowledge', 'of', 'AWB', ... + + ['english-kjv.txt', 'english-web.txt', 'finnish.txt', ... + ['In', 'the', 'beginning', 'God', 'created', 'the', ... + + ['1789-Washington.txt', '1793-Washington.txt', ... + ['Fellow', '-', 'Citizens', 'of', 'the', 'Senate', ... + + ['1945-Truman.txt', '1946-Truman.txt', ... + ['PRESIDENT', 'HARRY', 'S', '.', 'TRUMAN', "'", ... + + ['firefox.txt', 'grail.txt', 'overheard.txt', ... + ['Cookie', 'Manager', ':', '"', 'Don', "'", 't', ... + + +Tagged Corpus Reader +==================== +The Tagged Corpus reader can give us words, sentences, and paragraphs, +each tagged or untagged. All of the read methods can take one item +(in which case they return the contents of that file) or a list of +documents (in which case they concatenate the contents of those files). +By default, they apply to all documents in the corpus. + + >>> root = make_testcorpus( + ... a="""\ + ... This/det is/verb the/det first/adj sentence/noun ./punc + ... Here/det is/verb another/adj sentence/noun ./punc + ... Note/verb that/comp you/pron can/verb use/verb \ + ... any/noun tag/noun set/noun + ... + ... This/det is/verb the/det second/adj paragraph/noun ./punc + ... word/n without/adj a/det tag/noun :/: hello ./punc + ... """, + ... b="""\ + ... This/det is/verb the/det second/adj file/noun ./punc + ... """) + + >>> from nltk.corpus.reader.tagged import TaggedCorpusReader + >>> corpus = TaggedCorpusReader(root, list('ab')) + >>> corpus.fileids() + ['a', 'b'] + >>> str(corpus.root) == str(root) + True + >>> corpus.words() + ['This', 'is', 'the', 'first', 'sentence', '.', ...] + >>> corpus.sents() + [['This', 'is', 'the', 'first', ...], ['Here', 'is', 'another'...], ...] + >>> corpus.paras() + [[['This', ...], ['Here', ...], ...], [['This', ...], ...], ...] + >>> corpus.tagged_words() + [('This', 'DET'), ('is', 'VERB'), ('the', 'DET'), ...] + >>> corpus.tagged_sents() + [[('This', 'DET'), ('is', 'VERB'), ...], [('Here', 'DET'), ...], ...] + >>> corpus.tagged_paras() + [[[('This', 'DET'), ...], ...], [[('This', 'DET'), ...], ...], ...] + >>> corpus.raw()[:40] + 'This/det is/verb the/det first/adj sente' + >>> len(corpus.words()), [len(corpus.words(d)) for d in corpus.fileids()] + (38, [32, 6]) + >>> len(corpus.sents()), [len(corpus.sents(d)) for d in corpus.fileids()] + (6, [5, 1]) + >>> len(corpus.paras()), [len(corpus.paras(d)) for d in corpus.fileids()] + (3, [2, 1]) + >>> print(corpus.words('a')) + ['This', 'is', 'the', 'first', 'sentence', '.', ...] + >>> print(corpus.words('b')) + ['This', 'is', 'the', 'second', 'file', '.'] + >>> del_testcorpus(root) + +The Brown Corpus uses the tagged corpus reader: + + >>> from nltk.corpus import brown + >>> brown.fileids() + ['ca01', 'ca02', 'ca03', 'ca04', 'ca05', 'ca06', 'ca07', ...] + >>> brown.categories() + ['adventure', 'belles_lettres', 'editorial', 'fiction', 'government', 'hobbies', 'humor', + 'learned', 'lore', 'mystery', 'news', 'religion', 'reviews', 'romance', 'science_fiction'] + >>> print(repr(brown.root).replace('\\\\','/')) + FileSystemPathPointer('.../corpora/brown') + >>> brown.words() + ['The', 'Fulton', 'County', 'Grand', 'Jury', ...] + >>> brown.sents() + [['The', 'Fulton', 'County', 'Grand', ...], ...] + >>> brown.paras() + [[['The', 'Fulton', 'County', ...]], [['The', 'jury', ...]], ...] + >>> brown.tagged_words() + [('The', 'AT'), ('Fulton', 'NP-TL'), ...] + >>> brown.tagged_sents() + [[('The', 'AT'), ('Fulton', 'NP-TL'), ('County', 'NN-TL'), ...], ...] + >>> brown.tagged_paras() + [[[('The', 'AT'), ...]], [[('The', 'AT'), ...]], ...] + +Verbnet Corpus Reader +===================== + +Make sure we're picking up the right number of elements: + + >>> from nltk.corpus import verbnet + >>> len(verbnet.lemmas()) + 3621 + >>> len(verbnet.wordnetids()) + 4953 + >>> len(verbnet.classids()) + 429 + +Selecting classids based on various selectors: + + >>> verbnet.classids(lemma='take') + ['bring-11.3', 'characterize-29.2', 'convert-26.6.2', 'cost-54.2', + 'fit-54.3', 'performance-26.7-2', 'steal-10.5'] + >>> verbnet.classids(wordnetid='lead%2:38:01') + ['accompany-51.7'] + >>> verbnet.classids(fileid='approve-77.xml') + ['approve-77'] + >>> verbnet.classids(classid='admire-31.2') # subclasses + ['admire-31.2-1'] + +vnclass() accepts filenames, long ids, and short ids: + + >>> a = ElementTree.tostring(verbnet.vnclass('admire-31.2.xml')) + >>> b = ElementTree.tostring(verbnet.vnclass('admire-31.2')) + >>> c = ElementTree.tostring(verbnet.vnclass('31.2')) + >>> a == b == c + True + +fileids() can be used to get files based on verbnet class ids: + + >>> verbnet.fileids('admire-31.2') + ['admire-31.2.xml'] + >>> verbnet.fileids(['admire-31.2', 'obtain-13.5.2']) + ['admire-31.2.xml', 'obtain-13.5.2.xml'] + >>> verbnet.fileids('badidentifier') + Traceback (most recent call last): + . . . + ValueError: vnclass identifier 'badidentifier' not found + +longid() and shortid() can be used to convert identifiers: + + >>> verbnet.longid('31.2') + 'admire-31.2' + >>> verbnet.longid('admire-31.2') + 'admire-31.2' + >>> verbnet.shortid('31.2') + '31.2' + >>> verbnet.shortid('admire-31.2') + '31.2' + >>> verbnet.longid('badidentifier') + Traceback (most recent call last): + . . . + ValueError: vnclass identifier 'badidentifier' not found + >>> verbnet.shortid('badidentifier') + Traceback (most recent call last): + . . . + ValueError: vnclass identifier 'badidentifier' not found + +Corpus View Regression Tests +============================ + +Select some corpus files to play with: + + >>> import nltk.data + >>> # A very short file (160 chars): + >>> f1 = nltk.data.find('corpora/inaugural/README') + >>> # A relatively short file (791 chars): + >>> f2 = nltk.data.find('corpora/inaugural/1793-Washington.txt') + >>> # A longer file (32k chars): + >>> f3 = nltk.data.find('corpora/inaugural/1909-Taft.txt') + >>> fileids = [f1, f2, f3] + + +Concatenation +------------- +Check that concatenation works as intended. + + >>> from nltk.corpus.reader.util import * + + >>> c1 = StreamBackedCorpusView(f1, read_whitespace_block, encoding='utf-8') + >>> c2 = StreamBackedCorpusView(f2, read_whitespace_block, encoding='utf-8') + >>> c3 = StreamBackedCorpusView(f3, read_whitespace_block, encoding='utf-8') + >>> c123 = c1+c2+c3 + >>> print(c123) + ['C-Span', 'Inaugural', 'Address', 'Corpus', 'US', ...] + + >>> l1 = f1.open(encoding='utf-8').read().split() + >>> l2 = f2.open(encoding='utf-8').read().split() + >>> l3 = f3.open(encoding='utf-8').read().split() + >>> l123 = l1+l2+l3 + + >>> list(c123) == l123 + True + + >>> (c1+c2+c3)[100] == l123[100] + True + +Slicing +------- +First, do some tests with fairly small slices. These will all +generate tuple values. + + >>> from nltk.util import LazySubsequence + >>> c1 = StreamBackedCorpusView(f1, read_whitespace_block, encoding='utf-8') + >>> l1 = f1.open(encoding='utf-8').read().split() + >>> print(len(c1)) + 21 + >>> len(c1) < LazySubsequence.MIN_SIZE + True + +Choose a list of indices, based on the length, that covers the +important corner cases: + + >>> indices = [-60, -30, -22, -21, -20, -1, + ... 0, 1, 10, 20, 21, 22, 30, 60] + +Test slicing with explicit start & stop value: + + >>> for s in indices: + ... for e in indices: + ... assert list(c1[s:e]) == l1[s:e] + +Test slicing with stop=None: + + >>> for s in indices: + ... assert list(c1[s:]) == l1[s:] + +Test slicing with start=None: + + >>> for e in indices: + ... assert list(c1[:e]) == l1[:e] + +Test slicing with start=stop=None: + + >>> list(c1[:]) == list(l1[:]) + True + +Next, we'll do some tests with much longer slices. These will +generate LazySubsequence objects. + + >>> c3 = StreamBackedCorpusView(f3, read_whitespace_block, encoding='utf-8') + >>> l3 = f3.open(encoding='utf-8').read().split() + >>> print(len(c3)) + 5430 + >>> len(c3) > LazySubsequence.MIN_SIZE*2 + True + +Choose a list of indices, based on the length, that covers the +important corner cases: + + >>> indices = [-12000, -6000, -5431, -5430, -5429, -3000, -200, -1, + ... 0, 1, 200, 3000, 5000, 5429, 5430, 5431, 6000, 12000] + +Test slicing with explicit start & stop value: + + >>> for s in indices: + ... for e in indices: + ... assert list(c3[s:e]) == l3[s:e] + +Test slicing with stop=None: + + >>> for s in indices: + ... assert list(c3[s:]) == l3[s:] + +Test slicing with start=None: + + >>> for e in indices: + ... assert list(c3[:e]) == l3[:e] + +Test slicing with start=stop=None: + + >>> list(c3[:]) == list(l3[:]) + True + +Multiple Iterators +------------------ +If multiple iterators are created for the same corpus view, their +iteration can be interleaved: + + >>> c3 = StreamBackedCorpusView(f3, read_whitespace_block) + >>> iterators = [c3.iterate_from(n) for n in [0,15,30,45]] + >>> for i in range(15): + ... for iterator in iterators: + ... print('%-15s' % next(iterator), end=' ') + ... print() + My a duties in + fellow heavy of a + citizens: weight the proper + Anyone of office sense + who responsibility. upon of + has If which the + taken not, he obligation + the he is which + oath has about the + I no to oath + have conception enter, imposes. + just of or The + taken the he office + must powers is of + feel and lacking an + +SeekableUnicodeStreamReader +=========================== + +The file-like objects provided by the ``codecs`` module unfortunately +suffer from a bug that prevents them from working correctly with +corpus view objects. In particular, although the expose ``seek()`` +and ``tell()`` methods, those methods do not exhibit the expected +behavior, because they are not synchronized with the internal buffers +that are kept by the file-like objects. For example, the ``tell()`` +method will return the file position at the end of the buffers (whose +contents have not yet been returned by the stream); and therefore this +file position can not be used to return to the 'current' location in +the stream (since ``seek()`` has no way to reconstruct the buffers). + +To get around these problems, we define a new class, +`SeekableUnicodeStreamReader`, to act as a file-like interface to +files containing encoded unicode data. This class is loosely based on +the ``codecs.StreamReader`` class. To construct a new reader, we call +the constructor with an underlying stream and an encoding name: + + >>> from io import StringIO, BytesIO + >>> from nltk.data import SeekableUnicodeStreamReader + >>> stream = BytesIO(b"""\ + ... This is a test file. + ... It is encoded in ascii. + ... """.decode('ascii').encode('ascii')) + >>> reader = SeekableUnicodeStreamReader(stream, 'ascii') + +`SeekableUnicodeStreamReader`\ s support all of the normal operations +supplied by a read-only stream. Note that all of the read operations +return ``unicode`` objects (not ``str`` objects). + + >>> reader.read() # read the entire file. + 'This is a test file.\nIt is encoded in ascii.\n' + >>> reader.seek(0) # rewind to the start. + >>> reader.read(5) # read at most 5 bytes. + 'This ' + >>> reader.readline() # read to the end of the line. + 'is a test file.\n' + >>> reader.seek(0) # rewind to the start. + >>> for line in reader: + ... print(repr(line)) # iterate over lines + 'This is a test file.\n' + 'It is encoded in ascii.\n' + >>> reader.seek(0) # rewind to the start. + >>> reader.readlines() # read a list of line strings + ['This is a test file.\n', 'It is encoded in ascii.\n'] + >>> reader.close() + +Size argument to ``read()`` +--------------------------- +The ``size`` argument to ``read()`` specifies the maximum number of +*bytes* to read, not the maximum number of *characters*. Thus, for +encodings that use multiple bytes per character, it may return fewer +characters than the ``size`` argument: + + >>> stream = BytesIO(b"""\ + ... This is a test file. + ... It is encoded in utf-16. + ... """.decode('ascii').encode('utf-16')) + >>> reader = SeekableUnicodeStreamReader(stream, 'utf-16') + >>> reader.read(10) + 'This ' + +If a read block ends in the middle of the byte string encoding a +single character, then that byte string is stored in an internal +buffer, and re-used on the next call to ``read()``. However, if the +size argument is too small to read even a single character, even +though at least one character is available, then the ``read()`` method +will read additional bytes until it can return a single character. +This ensures that the ``read()`` method does not return an empty +string, which could be mistaken for indicating the end of the file. + + >>> reader.seek(0) # rewind to the start. + >>> reader.read(1) # we actually need to read 4 bytes + 'T' + >>> int(reader.tell()) + 4 + +The ``readline()`` method may read more than a single line of text, in +which case it stores the text that it does not return in a buffer. If +this buffer is not empty, then its contents will be included in the +value returned by the next call to ``read()``, regardless of the +``size`` argument, since they are available without reading any new +bytes from the stream: + + >>> reader.seek(0) # rewind to the start. + >>> reader.readline() # stores extra text in a buffer + 'This is a test file.\n' + >>> print(reader.linebuffer) # examine the buffer contents + ['It is encoded i'] + >>> reader.read(0) # returns the contents of the buffer + 'It is encoded i' + >>> print(reader.linebuffer) # examine the buffer contents + None + +Seek and Tell +------------- +In addition to these basic read operations, +`SeekableUnicodeStreamReader` also supports the ``seek()`` and +``tell()`` operations. However, some care must still be taken when +using these operations. In particular, the only file offsets that +should be passed to ``seek()`` are ``0`` and any offset that has been +returned by ``tell``. + + >>> stream = BytesIO(b"""\ + ... This is a test file. + ... It is encoded in utf-16. + ... """.decode('ascii').encode('utf-16')) + >>> reader = SeekableUnicodeStreamReader(stream, 'utf-16') + >>> reader.read(20) + 'This is a ' + >>> pos = reader.tell(); print(pos) + 22 + >>> reader.read(20) + 'test file.' + >>> reader.seek(pos) # rewind to the position from tell. + >>> reader.read(20) + 'test file.' + +The ``seek()`` and ``tell()`` methods work property even when +``readline()`` is used. + + >>> stream = BytesIO(b"""\ + ... This is a test file. + ... It is encoded in utf-16. + ... """.decode('ascii').encode('utf-16')) + >>> reader = SeekableUnicodeStreamReader(stream, 'utf-16') + >>> reader.readline() + 'This is a test file.\n' + >>> pos = reader.tell(); print(pos) + 44 + >>> reader.readline() + 'It is encoded in utf-16.\n' + >>> reader.seek(pos) # rewind to the position from tell. + >>> reader.readline() + 'It is encoded in utf-16.\n' + + +Squashed Bugs +============= + +svn 5276 fixed a bug in the comment-stripping behavior of +parse_sexpr_block. + + >>> from io import StringIO + >>> from nltk.corpus.reader.util import read_sexpr_block + >>> f = StringIO(b""" + ... (a b c) + ... # This line is a comment. + ... (d e f\ng h)""".decode('ascii')) + >>> print(read_sexpr_block(f, block_size=38, comment_char='#')) + ['(a b c)'] + >>> print(read_sexpr_block(f, block_size=38, comment_char='#')) + ['(d e f\ng h)'] + +svn 5277 fixed a bug in parse_sexpr_block, which would cause it to +enter an infinite loop if a file ended mid-sexpr, or ended with a +token that was not followed by whitespace. A related bug caused +an infinite loop if the corpus ended in an unmatched close paren -- +this was fixed in svn 5279 + + >>> f = StringIO(b""" + ... This file ends mid-sexpr + ... (hello (world""".decode('ascii')) + >>> for i in range(3): print(read_sexpr_block(f)) + ['This', 'file', 'ends', 'mid-sexpr'] + ['(hello (world'] + [] + + >>> f = StringIO(b"This file has no trailing whitespace.".decode('ascii')) + >>> for i in range(3): print(read_sexpr_block(f)) + ['This', 'file', 'has', 'no', 'trailing'] + ['whitespace.'] + [] + + >>> # Bug fixed in 5279: + >>> f = StringIO(b"a b c)".decode('ascii')) + >>> for i in range(3): print(read_sexpr_block(f)) + ['a', 'b'] + ['c)'] + [] + + +svn 5624 & 5265 fixed a bug in ConcatenatedCorpusView, which caused it +to return the wrong items when indexed starting at any index beyond +the first file. + + >>> import nltk + >>> sents = nltk.corpus.brown.sents() + >>> print(sents[6000]) + ['Cholesterol', 'and', 'thyroid'] + >>> print(sents[6000]) + ['Cholesterol', 'and', 'thyroid'] + +svn 5728 fixed a bug in Categorized*CorpusReader, which caused them +to return words from *all* files when just one file was specified. + + >>> from nltk.corpus import reuters + >>> reuters.words('training/13085') + ['SNYDER', '&', 'lt', ';', 'SOI', '>', 'MAKES', ...] + >>> reuters.words('training/5082') + ['SHEPPARD', 'RESOURCES', 'TO', 'MERGE', 'WITH', ...] + +svn 7227 fixed a bug in the qc corpus reader, which prevented +access to its tuples() method + + >>> from nltk.corpus import qc + >>> qc.tuples('test.txt') + [('NUM:dist', 'How far is it from Denver to Aspen ?'), ('LOC:city', 'What county is Modesto , California in ?'), ...] + +Ensure that KEYWORD from `comparative_sents.py` no longer contains a ReDoS vulnerability. + + >>> import re + >>> import time + >>> from nltk.corpus.reader.comparative_sents import KEYWORD + >>> sizes = { + ... "short": 4000, + ... "long": 40000 + ... } + >>> exec_times = { + ... "short": [], + ... "long": [], + ... } + >>> for size_name, size in sizes.items(): + ... for j in range(9): + ... start_t = time.perf_counter() + ... payload = "( " + "(" * size + ... output = KEYWORD.findall(payload) + ... exec_times[size_name].append(time.perf_counter() - start_t) + ... exec_times[size_name] = sorted(exec_times[size_name])[4] # Get the median + +Ideally, the execution time of such a regular expression is linear +in the length of the input. As such, we would expect exec_times["long"] +to be roughly 10 times as big as exec_times["short"]. +With the ReDoS in place, it took roughly 80 times as long. +For now, we accept values below 30 (times as long), due to the potential +for variance. This ensures that the ReDoS has certainly been reduced, +if not removed. + + >>> exec_times["long"] / exec_times["short"] < 30 # doctest: +SKIP + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/crubadan.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/crubadan.doctest new file mode 100644 index 0000000..fb89547 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/crubadan.doctest @@ -0,0 +1,65 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +Crubadan Corpus Reader +====================== + +Crubadan is an NLTK corpus reader for ngram files provided +by the Crubadan project. It supports several languages. + + >>> from nltk.corpus import crubadan + >>> crubadan.langs() + ['abk', 'abn',..., 'zpa', 'zul'] + +---------------------------------------- +Language code mapping and helper methods +---------------------------------------- + +The web crawler that generates the 3-gram frequencies works at the +level of "writing systems" rather than languages. Writing systems +are assigned internal 2-3 letter codes that require mapping to the +standard ISO 639-3 codes. For more information, please refer to +the README in nltk_data/crubadan folder after installing it. + +To translate ISO 639-3 codes to "Crubadan Code": + + >>> crubadan.iso_to_crubadan('eng') + 'en' + >>> crubadan.iso_to_crubadan('fra') + 'fr' + >>> crubadan.iso_to_crubadan('aaa') + +In reverse, print ISO 639-3 code if we have the Crubadan Code: + + >>> crubadan.crubadan_to_iso('en') + 'eng' + >>> crubadan.crubadan_to_iso('fr') + 'fra' + >>> crubadan.crubadan_to_iso('aa') + +--------------------------- +Accessing ngram frequencies +--------------------------- + +On initialization the reader will create a dictionary of every +language supported by the Crubadan project, mapping the ISO 639-3 +language code to its corresponding ngram frequency. + +You can access individual language FreqDist and the ngrams within them as follows: + + >>> english_fd = crubadan.lang_freq('eng') + >>> english_fd['the'] + 728135 + +Above accesses the FreqDist of English and returns the frequency of the ngram 'the'. +A ngram that isn't found within the language will return 0: + + >>> english_fd['sometest'] + 0 + +A language that isn't supported will raise an exception: + + >>> crubadan.lang_freq('elvish') + Traceback (most recent call last): + ... + RuntimeError: Unsupported language. diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/data.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/data.doctest new file mode 100644 index 0000000..7e989a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/data.doctest @@ -0,0 +1,387 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================================= + Loading Resources From the Data Package +========================================= + + >>> import nltk.data + +Overview +~~~~~~~~ +The `nltk.data` module contains functions that can be used to load +NLTK resource files, such as corpora, grammars, and saved processing +objects. + +Loading Data Files +~~~~~~~~~~~~~~~~~~ +Resources are loaded using the function `nltk.data.load()`, which +takes as its first argument a URL specifying what file should be +loaded. The ``nltk:`` protocol loads files from the NLTK data +distribution: + + >>> tokenizer = nltk.data.load('nltk:tokenizers/punkt/english.pickle') + >>> tokenizer.tokenize('Hello. This is a test. It works!') + ['Hello.', 'This is a test.', 'It works!'] + +It is important to note that there should be no space following the +colon (':') in the URL; 'nltk: tokenizers/punkt/english.pickle' will +not work! + +The ``nltk:`` protocol is used by default if no protocol is specified: + + >>> nltk.data.load('tokenizers/punkt/english.pickle') + + +But it is also possible to load resources from ``http:``, ``ftp:``, +and ``file:`` URLs: + + >>> # Load a grammar from the NLTK webpage. + >>> cfg = nltk.data.load('https://raw.githubusercontent.com/nltk/nltk/develop/nltk/test/toy.cfg') + >>> print(cfg) # doctest: +ELLIPSIS + Grammar with 14 productions (start state = S) + S -> NP VP + PP -> P NP + ... + P -> 'on' + P -> 'in' + + >>> # Load a grammar using an absolute path. + >>> url = 'file:%s' % nltk.data.find('grammars/sample_grammars/toy.cfg') + >>> url.replace('\\', '/') + 'file:...toy.cfg' + >>> print(nltk.data.load(url)) + Grammar with 14 productions (start state = S) + S -> NP VP + PP -> P NP + ... + P -> 'on' + P -> 'in' + +The second argument to the `nltk.data.load()` function specifies the +file format, which determines how the file's contents are processed +before they are returned by ``load()``. The formats that are +currently supported by the data module are described by the dictionary +`nltk.data.FORMATS`: + + >>> for format, descr in sorted(nltk.data.FORMATS.items()): + ... print('{0:<7} {1:}'.format(format, descr)) + cfg A context free grammar. + fcfg A feature CFG. + fol A list of first order logic expressions, parsed with + nltk.sem.logic.Expression.fromstring. + json A serialized python object, stored using the json module. + logic A list of first order logic expressions, parsed with + nltk.sem.logic.LogicParser. Requires an additional logic_parser + parameter + pcfg A probabilistic CFG. + pickle A serialized python object, stored using the pickle + module. + raw The raw (byte string) contents of a file. + text The raw (unicode string) contents of a file. + val A semantic valuation, parsed by + nltk.sem.Valuation.fromstring. + yaml A serialized python object, stored using the yaml module. + +`nltk.data.load()` will raise a ValueError if a bad format name is +specified: + + >>> nltk.data.load('grammars/sample_grammars/toy.cfg', 'bar') + Traceback (most recent call last): + . . . + ValueError: Unknown format type! + +By default, the ``"auto"`` format is used, which chooses a format +based on the filename's extension. The mapping from file extensions +to format names is specified by `nltk.data.AUTO_FORMATS`: + + >>> for ext, format in sorted(nltk.data.AUTO_FORMATS.items()): + ... print('.%-7s -> %s' % (ext, format)) + .cfg -> cfg + .fcfg -> fcfg + .fol -> fol + .json -> json + .logic -> logic + .pcfg -> pcfg + .pickle -> pickle + .text -> text + .txt -> text + .val -> val + .yaml -> yaml + +If `nltk.data.load()` is unable to determine the format based on the +filename's extension, it will raise a ValueError: + + >>> nltk.data.load('foo.bar') + Traceback (most recent call last): + . . . + ValueError: Could not determine format for foo.bar based on its file + extension; use the "format" argument to specify the format explicitly. + +Note that by explicitly specifying the ``format`` argument, you can +override the load method's default processing behavior. For example, +to get the raw contents of any file, simply use ``format="raw"``: + + >>> s = nltk.data.load('grammars/sample_grammars/toy.cfg', 'text') + >>> print(s) + S -> NP VP + PP -> P NP + NP -> Det N | NP PP + VP -> V NP | VP PP + ... + +Making Local Copies +~~~~~~~~~~~~~~~~~~~ +.. This will not be visible in the html output: create a tempdir to + play in. + >>> import tempfile, os + >>> tempdir = tempfile.mkdtemp() + >>> old_dir = os.path.abspath('.') + >>> os.chdir(tempdir) + +The function `nltk.data.retrieve()` copies a given resource to a local +file. This can be useful, for example, if you want to edit one of the +sample grammars. + + >>> nltk.data.retrieve('grammars/sample_grammars/toy.cfg') + Retrieving 'nltk:grammars/sample_grammars/toy.cfg', saving to 'toy.cfg' + + >>> # Simulate editing the grammar. + >>> with open('toy.cfg') as inp: + ... s = inp.read().replace('NP', 'DP') + >>> with open('toy.cfg', 'w') as out: + ... _bytes_written = out.write(s) + + >>> # Load the edited grammar, & display it. + >>> cfg = nltk.data.load('file:///' + os.path.abspath('toy.cfg')) + >>> print(cfg) + Grammar with 14 productions (start state = S) + S -> DP VP + PP -> P DP + ... + P -> 'on' + P -> 'in' + +The second argument to `nltk.data.retrieve()` specifies the filename +for the new copy of the file. By default, the source file's filename +is used. + + >>> nltk.data.retrieve('grammars/sample_grammars/toy.cfg', 'mytoy.cfg') + Retrieving 'nltk:grammars/sample_grammars/toy.cfg', saving to 'mytoy.cfg' + >>> os.path.isfile('./mytoy.cfg') + True + >>> nltk.data.retrieve('grammars/sample_grammars/np.fcfg') + Retrieving 'nltk:grammars/sample_grammars/np.fcfg', saving to 'np.fcfg' + >>> os.path.isfile('./np.fcfg') + True + +If a file with the specified (or default) filename already exists in +the current directory, then `nltk.data.retrieve()` will raise a +ValueError exception. It will *not* overwrite the file: + + >>> os.path.isfile('./toy.cfg') + True + >>> nltk.data.retrieve('grammars/sample_grammars/toy.cfg') + Traceback (most recent call last): + . . . + ValueError: File '...toy.cfg' already exists! + +.. This will not be visible in the html output: clean up the tempdir. + >>> os.chdir(old_dir) + >>> for f in os.listdir(tempdir): + ... os.remove(os.path.join(tempdir, f)) + >>> os.rmdir(tempdir) + +Finding Files in the NLTK Data Package +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The `nltk.data.find()` function searches the NLTK data package for a +given file, and returns a pointer to that file. This pointer can +either be a `FileSystemPathPointer` (whose `path` attribute gives the +absolute path of the file); or a `ZipFilePathPointer`, specifying a +zipfile and the name of an entry within that zipfile. Both pointer +types define the `open()` method, which can be used to read the string +contents of the file. + + >>> path = nltk.data.find('corpora/abc/rural.txt') + >>> str(path) + '...rural.txt' + >>> print(path.open().read(60).decode()) + PM denies knowledge of AWB kickbacks + The Prime Minister has + +Alternatively, the `nltk.data.load()` function can be used with the +keyword argument ``format="raw"``: + + >>> s = nltk.data.load('corpora/abc/rural.txt', format='raw')[:60] + >>> print(s.decode()) + PM denies knowledge of AWB kickbacks + The Prime Minister has + +Alternatively, you can use the keyword argument ``format="text"``: + + >>> s = nltk.data.load('corpora/abc/rural.txt', format='text')[:60] + >>> print(s) + PM denies knowledge of AWB kickbacks + The Prime Minister has + +Resource Caching +~~~~~~~~~~~~~~~~ + +NLTK uses a weakref dictionary to maintain a cache of resources that +have been loaded. If you load a resource that is already stored in +the cache, then the cached copy will be returned. This behavior can +be seen by the trace output generated when verbose=True: + + >>> feat0 = nltk.data.load('grammars/book_grammars/feat0.fcfg', verbose=True) + <> + >>> feat0 = nltk.data.load('grammars/book_grammars/feat0.fcfg', verbose=True) + <> + +If you wish to load a resource from its source, bypassing the cache, +use the ``cache=False`` argument to `nltk.data.load()`. This can be +useful, for example, if the resource is loaded from a local file, and +you are actively editing that file: + + >>> feat0 = nltk.data.load('grammars/book_grammars/feat0.fcfg',cache=False,verbose=True) + <> + +The cache *no longer* uses weak references. A resource will not be +automatically expunged from the cache when no more objects are using +it. In the following example, when we clear the variable ``feat0``, +the reference count for the feature grammar object drops to zero. +However, the object remains cached: + + >>> del feat0 + >>> feat0 = nltk.data.load('grammars/book_grammars/feat0.fcfg', + ... verbose=True) + <> + +You can clear the entire contents of the cache, using +`nltk.data.clear_cache()`: + + >>> nltk.data.clear_cache() + +Retrieving other Data Sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + >>> formulas = nltk.data.load('grammars/book_grammars/background.fol') + >>> for f in formulas: print(str(f)) + all x.(boxerdog(x) -> dog(x)) + all x.(boxer(x) -> person(x)) + all x.-(dog(x) & person(x)) + all x.(married(x) <-> exists y.marry(x,y)) + all x.(bark(x) -> dog(x)) + all x y.(marry(x,y) -> (person(x) & person(y))) + -(Vincent = Mia) + -(Vincent = Fido) + -(Mia = Fido) + +Regression Tests +~~~~~~~~~~~~~~~~ +Create a temp dir for tests that write files: + + >>> import tempfile, os + >>> tempdir = tempfile.mkdtemp() + >>> old_dir = os.path.abspath('.') + >>> os.chdir(tempdir) + +The `retrieve()` function accepts all url types: + + >>> urls = ['https://raw.githubusercontent.com/nltk/nltk/develop/nltk/test/toy.cfg', + ... 'file:%s' % nltk.data.find('grammars/sample_grammars/toy.cfg'), + ... 'nltk:grammars/sample_grammars/toy.cfg', + ... 'grammars/sample_grammars/toy.cfg'] + >>> for i, url in enumerate(urls): + ... nltk.data.retrieve(url, 'toy-%d.cfg' % i) + Retrieving 'https://raw.githubusercontent.com/nltk/nltk/develop/nltk/test/toy.cfg', saving to 'toy-0.cfg' + Retrieving 'file:...toy.cfg', saving to 'toy-1.cfg' + Retrieving 'nltk:grammars/sample_grammars/toy.cfg', saving to 'toy-2.cfg' + Retrieving 'nltk:grammars/sample_grammars/toy.cfg', saving to 'toy-3.cfg' + +Clean up the temp dir: + + >>> os.chdir(old_dir) + >>> for f in os.listdir(tempdir): + ... os.remove(os.path.join(tempdir, f)) + >>> os.rmdir(tempdir) + +Lazy Loader +----------- +A lazy loader is a wrapper object that defers loading a resource until +it is accessed or used in any way. This is mainly intended for +internal use by NLTK's corpus readers. + + >>> # Create a lazy loader for toy.cfg. + >>> ll = nltk.data.LazyLoader('grammars/sample_grammars/toy.cfg') + + >>> # Show that it's not loaded yet: + >>> object.__repr__(ll) + '' + + >>> # printing it is enough to cause it to be loaded: + >>> print(ll) + + + >>> # Show that it's now been loaded: + >>> object.__repr__(ll) + '' + + + >>> # Test that accessing an attribute also loads it: + >>> ll = nltk.data.LazyLoader('grammars/sample_grammars/toy.cfg') + >>> ll.start() + S + >>> object.__repr__(ll) + '' + +Buffered Gzip Reading and Writing +--------------------------------- +Write performance to gzip-compressed is extremely poor when the files become large. +File creation can become a bottleneck in those cases. + +Read performance from large gzipped pickle files was improved in data.py by +buffering the reads. A similar fix can be applied to writes by buffering +the writes to a StringIO object first. + +This is mainly intended for internal use. The test simply tests that reading +and writing work as intended and does not test how much improvement buffering +provides. + + >>> from io import StringIO + >>> test = nltk.data.BufferedGzipFile('testbuf.gz', 'wb', size=2**10) + >>> ans = [] + >>> for i in range(10000): + ... ans.append(str(i).encode('ascii')) + ... test.write(str(i).encode('ascii')) + >>> test.close() + >>> test = nltk.data.BufferedGzipFile('testbuf.gz', 'rb') + >>> test.read() == b''.join(ans) + True + >>> test.close() + >>> import os + >>> os.unlink('testbuf.gz') + +JSON Encoding and Decoding +-------------------------- +JSON serialization is used instead of pickle for some classes. + + >>> from nltk import jsontags + >>> from nltk.jsontags import JSONTaggedEncoder, JSONTaggedDecoder, register_tag + >>> @jsontags.register_tag + ... class JSONSerializable: + ... json_tag = 'JSONSerializable' + ... + ... def __init__(self, n): + ... self.n = n + ... + ... def encode_json_obj(self): + ... return self.n + ... + ... @classmethod + ... def decode_json_obj(cls, obj): + ... n = obj + ... return cls(n) + ... + >>> JSONTaggedEncoder().encode(JSONSerializable(1)) + '{"!JSONSerializable": 1}' + >>> JSONTaggedDecoder().decode('{"!JSONSerializable": 1}').n + 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/dependency.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/dependency.doctest new file mode 100644 index 0000000..30de030 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/dependency.doctest @@ -0,0 +1,241 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=================== +Dependency Grammars +=================== + + >>> from nltk.grammar import DependencyGrammar + >>> from nltk.parse import ( + ... DependencyGraph, + ... ProjectiveDependencyParser, + ... NonprojectiveDependencyParser, + ... ) + +CoNLL Data +---------- + + >>> treebank_data = """Pierre NNP 2 NMOD + ... Vinken NNP 8 SUB + ... , , 2 P + ... 61 CD 5 NMOD + ... years NNS 6 AMOD + ... old JJ 2 NMOD + ... , , 2 P + ... will MD 0 ROOT + ... join VB 8 VC + ... the DT 11 NMOD + ... board NN 9 OBJ + ... as IN 9 VMOD + ... a DT 15 NMOD + ... nonexecutive JJ 15 NMOD + ... director NN 12 PMOD + ... Nov. NNP 9 VMOD + ... 29 CD 16 NMOD + ... . . 9 VMOD + ... """ + + >>> dg = DependencyGraph(treebank_data) + >>> dg.tree().pprint() + (will + (Vinken Pierre , (old (years 61)) ,) + (join (board the) (as (director a nonexecutive)) (Nov. 29) .)) + >>> for head, rel, dep in dg.triples(): + ... print( + ... '({h[0]}, {h[1]}), {r}, ({d[0]}, {d[1]})' + ... .format(h=head, r=rel, d=dep) + ... ) + (will, MD), SUB, (Vinken, NNP) + (Vinken, NNP), NMOD, (Pierre, NNP) + (Vinken, NNP), P, (,, ,) + (Vinken, NNP), NMOD, (old, JJ) + (old, JJ), AMOD, (years, NNS) + (years, NNS), NMOD, (61, CD) + (Vinken, NNP), P, (,, ,) + (will, MD), VC, (join, VB) + (join, VB), OBJ, (board, NN) + (board, NN), NMOD, (the, DT) + (join, VB), VMOD, (as, IN) + (as, IN), PMOD, (director, NN) + (director, NN), NMOD, (a, DT) + (director, NN), NMOD, (nonexecutive, JJ) + (join, VB), VMOD, (Nov., NNP) + (Nov., NNP), NMOD, (29, CD) + (join, VB), VMOD, (., .) + +Using a custom cell extractor. + + >>> def custom_extractor(cells): + ... _, tag, head, rel = cells + ... return 'spam', 'spam', tag, tag, '', head, rel + >>> dg = DependencyGraph(treebank_data, cell_extractor=custom_extractor) + >>> dg.tree().pprint() + (spam + (spam spam spam (spam (spam spam)) spam) + (spam (spam spam) (spam (spam spam spam)) (spam spam) spam)) + +Custom cell extractors can take in and return an index. + + >>> def custom_extractor(cells, index): + ... word, tag, head, rel = cells + ... return (index, '{}-{}'.format(word, index), word, + ... tag, tag, '', head, rel) + >>> dg = DependencyGraph(treebank_data, cell_extractor=custom_extractor) + >>> dg.tree().pprint() + (will-8 + (Vinken-2 Pierre-1 ,-3 (old-6 (years-5 61-4)) ,-7) + (join-9 + (board-11 the-10) + (as-12 (director-15 a-13 nonexecutive-14)) + (Nov.-16 29-17) + .-18)) + +Using the dependency-parsed version of the Penn Treebank corpus sample. + + >>> from nltk.corpus import dependency_treebank + >>> t = dependency_treebank.parsed_sents()[0] + >>> print(t.to_conll(3)) + Pierre NNP 2 + Vinken NNP 8 + , , 2 + 61 CD 5 + years NNS 6 + old JJ 2 + , , 2 + will MD 0 + join VB 8 + the DT 11 + board NN 9 + as IN 9 + a DT 15 + nonexecutive JJ 15 + director NN 12 + Nov. NNP 9 + 29 CD 16 + . . 8 + +Using the output of zpar (like Malt-TAB but with zero-based indexing) + + >>> zpar_data = """ + ... Pierre NNP 1 NMOD + ... Vinken NNP 7 SUB + ... , , 1 P + ... 61 CD 4 NMOD + ... years NNS 5 AMOD + ... old JJ 1 NMOD + ... , , 1 P + ... will MD -1 ROOT + ... join VB 7 VC + ... the DT 10 NMOD + ... board NN 8 OBJ + ... as IN 8 VMOD + ... a DT 14 NMOD + ... nonexecutive JJ 14 NMOD + ... director NN 11 PMOD + ... Nov. NNP 8 VMOD + ... 29 CD 15 NMOD + ... . . 7 P + ... """ + + >>> zdg = DependencyGraph(zpar_data, zero_based=True) + >>> print(zdg.tree()) + (will + (Vinken Pierre , (old (years 61)) ,) + (join (board the) (as (director a nonexecutive)) (Nov. 29)) + .) + + +Projective Dependency Parsing +----------------------------- + + >>> grammar = DependencyGrammar.fromstring(""" + ... 'fell' -> 'price' | 'stock' + ... 'price' -> 'of' 'the' + ... 'of' -> 'stock' + ... 'stock' -> 'the' + ... """) + >>> print(grammar) + Dependency grammar with 5 productions + 'fell' -> 'price' + 'fell' -> 'stock' + 'price' -> 'of' 'the' + 'of' -> 'stock' + 'stock' -> 'the' + + >>> dp = ProjectiveDependencyParser(grammar) + >>> for t in sorted(dp.parse(['the', 'price', 'of', 'the', 'stock', 'fell'])): + ... print(t) + (fell (price the (of (stock the)))) + (fell (price the of) (stock the)) + (fell (price the of the) stock) + +Non-Projective Dependency Parsing +--------------------------------- + + >>> grammar = DependencyGrammar.fromstring(""" + ... 'taught' -> 'play' | 'man' + ... 'man' -> 'the' + ... 'play' -> 'golf' | 'dog' | 'to' + ... 'dog' -> 'his' + ... """) + >>> print(grammar) + Dependency grammar with 7 productions + 'taught' -> 'play' + 'taught' -> 'man' + 'man' -> 'the' + 'play' -> 'golf' + 'play' -> 'dog' + 'play' -> 'to' + 'dog' -> 'his' + + >>> dp = NonprojectiveDependencyParser(grammar) + >>> g, = dp.parse(['the', 'man', 'taught', 'his', 'dog', 'to', 'play', 'golf']) + + >>> print(g.root['word']) + taught + + >>> for _, node in sorted(g.nodes.items()): + ... if node['word'] is not None: + ... print('{address} {word}: {d}'.format(d=node['deps'][''], **node)) + 1 the: [] + 2 man: [1] + 3 taught: [2, 7] + 4 his: [] + 5 dog: [4] + 6 to: [] + 7 play: [5, 6, 8] + 8 golf: [] + + >>> print(g.tree()) + (taught (man the) (play (dog his) to golf)) + +Integration with MALT parser +============================ + +In case the top relation is different from the default, we can set it. In case +of MALT parser, it's set to `'null'`. + +>>> dg_str = """1 I _ NN NN _ 2 nn _ _ +... 2 shot _ NN NN _ 0 null _ _ +... 3 an _ AT AT _ 2 dep _ _ +... 4 elephant _ NN NN _ 7 nn _ _ +... 5 in _ NN NN _ 7 nn _ _ +... 6 my _ NN NN _ 7 nn _ _ +... 7 pajamas _ NNS NNS _ 3 dobj _ _ +... """ +>>> dg = DependencyGraph(dg_str, top_relation_label='null') + +>>> len(dg.nodes) +8 + +>>> dg.root['word'], dg.root['address'] +('shot', 2) + +>>> print(dg.to_conll(10)) +1 I _ NN NN _ 2 nn _ _ +2 shot _ NN NN _ 0 null _ _ +3 an _ AT AT _ 2 dep _ _ +4 elephant _ NN NN _ 7 nn _ _ +5 in _ NN NN _ 7 nn _ _ +6 my _ NN NN _ 7 nn _ _ +7 pajamas _ NNS NNS _ 3 dobj _ _ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/discourse.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/discourse.doctest new file mode 100644 index 0000000..2c6e743 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/discourse.doctest @@ -0,0 +1,552 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================== +Discourse Checking +================== + + >>> from nltk import * + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + +Setup +===== + + >>> from nltk.test.childes_fixt import setup_module + >>> setup_module() + +Introduction +============ + +The NLTK discourse module makes it possible to test consistency and +redundancy of simple discourses, using theorem-proving and +model-building from `nltk.inference`. + +The ``DiscourseTester`` constructor takes a list of sentences as a +parameter. + + >>> dt = DiscourseTester(['a boxer walks', 'every boxer chases a girl']) + +The ``DiscourseTester`` parses each sentence into a list of logical +forms. Once we have created ``DiscourseTester`` object, we can +inspect various properties of the discourse. First off, we might want +to double-check what sentences are currently stored as the discourse. + + >>> dt.sentences() + s0: a boxer walks + s1: every boxer chases a girl + +As you will see, each sentence receives an identifier `s`\ :subscript:`i`. +We might also want to check what grammar the ``DiscourseTester`` is +using (by default, ``book_grammars/discourse.fcfg``): + + >>> dt.grammar() + % start S + # Grammar Rules + S[SEM = ] -> NP[NUM=?n,SEM=?subj] VP[NUM=?n,SEM=?vp] + NP[NUM=?n,SEM= ] -> Det[NUM=?n,SEM=?det] Nom[NUM=?n,SEM=?nom] + NP[LOC=?l,NUM=?n,SEM=?np] -> PropN[LOC=?l,NUM=?n,SEM=?np] + ... + +A different grammar can be invoked by using the optional ``gramfile`` +parameter when a ``DiscourseTester`` object is created. + +Readings and Threads +==================== + +Depending on +the grammar used, we may find some sentences have more than one +logical form. To check this, use the ``readings()`` method. Given a +sentence identifier of the form `s`\ :subscript:`i`, each reading of +that sentence is given an identifier `s`\ :sub:`i`-`r`\ :sub:`j`. + + + >>> dt.readings() + + s0 readings: + + s0-r0: exists z1.(boxer(z1) & walk(z1)) + s0-r1: exists z1.(boxerdog(z1) & walk(z1)) + + s1 readings: + + s1-r0: all z2.(boxer(z2) -> exists z3.(girl(z3) & chase(z2,z3))) + s1-r1: all z1.(boxerdog(z1) -> exists z2.(girl(z2) & chase(z1,z2))) + + +In this case, the only source of ambiguity lies in the word *boxer*, +which receives two translations: ``boxer`` and ``boxerdog``. The +intention is that one of these corresponds to the ``person`` sense and +one to the ``dog`` sense. In principle, we would also expect to see a +quantifier scope ambiguity in ``s1``. However, the simple grammar we +are using, namely `sem4.fcfg `_, doesn't support quantifier +scope ambiguity. + +We can also investigate the readings of a specific sentence: + + >>> dt.readings('a boxer walks') + The sentence 'a boxer walks' has these readings: + exists x.(boxer(x) & walk(x)) + exists x.(boxerdog(x) & walk(x)) + +Given that each sentence is two-ways ambiguous, we potentially have +four different discourse 'threads', taking all combinations of +readings. To see these, specify the ``threaded=True`` parameter on +the ``readings()`` method. Again, each thread is assigned an +identifier of the form `d`\ :sub:`i`. Following the identifier is a +list of the readings that constitute that thread. + + >>> dt.readings(threaded=True) + d0: ['s0-r0', 's1-r0'] + d1: ['s0-r0', 's1-r1'] + d2: ['s0-r1', 's1-r0'] + d3: ['s0-r1', 's1-r1'] + +Of course, this simple-minded approach doesn't scale: a discourse with, say, three +sentences, each of which has 3 readings, will generate 27 different +threads. It is an interesting exercise to consider how to manage +discourse ambiguity more efficiently. + +Checking Consistency +==================== + +Now, we can check whether some or all of the discourse threads are +consistent, using the ``models()`` method. With no parameter, this +method will try to find a model for every discourse thread in the +current discourse. However, we can also specify just one thread, say ``d1``. + + >>> dt.models('d1') + -------------------------------------------------------------------------------- + Model for Discourse Thread d1 + -------------------------------------------------------------------------------- + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + c1 = 0. + + f1(0) = 0. + f1(1) = 0. + + boxer(0). + - boxer(1). + + - boxerdog(0). + - boxerdog(1). + + - girl(0). + - girl(1). + + walk(0). + - walk(1). + + - chase(0,0). + - chase(0,1). + - chase(1,0). + - chase(1,1). + + Consistent discourse: d1 ['s0-r0', 's1-r1']: + s0-r0: exists z1.(boxer(z1) & walk(z1)) + s1-r1: all z1.(boxerdog(z1) -> exists z2.(girl(z2) & chase(z1,z2))) + + +There are various formats for rendering **Mace4** models --- here, +we have used the 'cooked' format (which is intended to be +human-readable). There are a number of points to note. + +#. The entities in the domain are all treated as non-negative + integers. In this case, there are only two entities, ``0`` and + ``1``. + +#. The ``-`` symbol indicates negation. So ``0`` is the only + ``boxerdog`` and the only thing that ``walk``\ s. Nothing is a + ``boxer``, or a ``girl`` or in the ``chase`` relation. Thus the + universal sentence is vacuously true. + +#. ``c1`` is an introduced constant that denotes ``0``. + +#. ``f1`` is a Skolem function, but it plays no significant role in + this model. + + +We might want to now add another sentence to the discourse, and there +is method ``add_sentence()`` for doing just this. + + >>> dt.add_sentence('John is a boxer') + >>> dt.sentences() + s0: a boxer walks + s1: every boxer chases a girl + s2: John is a boxer + +We can now test all the properties as before; here, we just show a +couple of them. + + >>> dt.readings() + + s0 readings: + + s0-r0: exists z1.(boxer(z1) & walk(z1)) + s0-r1: exists z1.(boxerdog(z1) & walk(z1)) + + s1 readings: + + s1-r0: all z1.(boxer(z1) -> exists z2.(girl(z2) & chase(z1,z2))) + s1-r1: all z1.(boxerdog(z1) -> exists z2.(girl(z2) & chase(z1,z2))) + + s2 readings: + + s2-r0: boxer(John) + s2-r1: boxerdog(John) + >>> dt.readings(threaded=True) + d0: ['s0-r0', 's1-r0', 's2-r0'] + d1: ['s0-r0', 's1-r0', 's2-r1'] + d2: ['s0-r0', 's1-r1', 's2-r0'] + d3: ['s0-r0', 's1-r1', 's2-r1'] + d4: ['s0-r1', 's1-r0', 's2-r0'] + d5: ['s0-r1', 's1-r0', 's2-r1'] + d6: ['s0-r1', 's1-r1', 's2-r0'] + d7: ['s0-r1', 's1-r1', 's2-r1'] + +If you are interested in a particular thread, the ``expand_threads()`` +method will remind you of what readings it consists of: + + >>> thread = dt.expand_threads('d1') + >>> for rid, reading in thread: + ... print(rid, str(reading.normalize())) + s0-r0 exists z1.(boxer(z1) & walk(z1)) + s1-r0 all z1.(boxer(z1) -> exists z2.(girl(z2) & chase(z1,z2))) + s2-r1 boxerdog(John) + +Suppose we have already defined a discourse, as follows: + + >>> dt = DiscourseTester(['A student dances', 'Every student is a person']) + +Now, when we add a new sentence, is it consistent with what we already +have? The `` consistchk=True`` parameter of ``add_sentence()`` allows +us to check: + + >>> dt.add_sentence('No person dances', consistchk=True) + Inconsistent discourse: d0 ['s0-r0', 's1-r0', 's2-r0']: + s0-r0: exists z1.(student(z1) & dance(z1)) + s1-r0: all z1.(student(z1) -> person(z1)) + s2-r0: -exists z1.(person(z1) & dance(z1)) + + >>> dt.readings() + + s0 readings: + + s0-r0: exists z1.(student(z1) & dance(z1)) + + s1 readings: + + s1-r0: all z1.(student(z1) -> person(z1)) + + s2 readings: + + s2-r0: -exists z1.(person(z1) & dance(z1)) + +So let's retract the inconsistent sentence: + + >>> dt.retract_sentence('No person dances', verbose=True) + Current sentences are + s0: A student dances + s1: Every student is a person + +We can now verify that result is consistent. + + >>> dt.models() + -------------------------------------------------------------------------------- + Model for Discourse Thread d0 + -------------------------------------------------------------------------------- + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + c1 = 0. + + dance(0). + - dance(1). + + person(0). + - person(1). + + student(0). + - student(1). + + Consistent discourse: d0 ['s0-r0', 's1-r0']: + s0-r0: exists z1.(student(z1) & dance(z1)) + s1-r0: all z1.(student(z1) -> person(z1)) + + +Checking Informativity +====================== + +Let's assume that we are still trying to extend the discourse *A +student dances.* *Every student is a person.* We add a new sentence, +but this time, we check whether it is informative with respect to what +has gone before. + + >>> dt.add_sentence('A person dances', informchk=True) + Sentence 'A person dances' under reading 'exists x.(person(x) & dance(x))': + Not informative relative to thread 'd0' + +In fact, we are just checking whether the new sentence is entailed by +the preceding discourse. + + >>> dt.models() + -------------------------------------------------------------------------------- + Model for Discourse Thread d0 + -------------------------------------------------------------------------------- + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + c1 = 0. + + c2 = 0. + + dance(0). + - dance(1). + + person(0). + - person(1). + + student(0). + - student(1). + + Consistent discourse: d0 ['s0-r0', 's1-r0', 's2-r0']: + s0-r0: exists z1.(student(z1) & dance(z1)) + s1-r0: all z1.(student(z1) -> person(z1)) + s2-r0: exists z1.(person(z1) & dance(z1)) + + + + +Adding Background Knowledge +=========================== + +Let's build a new discourse, and look at the readings of the component sentences: + + >>> dt = DiscourseTester(['Vincent is a boxer', 'Fido is a boxer', 'Vincent is married', 'Fido barks']) + >>> dt.readings() + + s0 readings: + + s0-r0: boxer(Vincent) + s0-r1: boxerdog(Vincent) + + s1 readings: + + s1-r0: boxer(Fido) + s1-r1: boxerdog(Fido) + + s2 readings: + + s2-r0: married(Vincent) + + s3 readings: + + s3-r0: bark(Fido) + +This gives us a lot of threads: + + >>> dt.readings(threaded=True) + d0: ['s0-r0', 's1-r0', 's2-r0', 's3-r0'] + d1: ['s0-r0', 's1-r1', 's2-r0', 's3-r0'] + d2: ['s0-r1', 's1-r0', 's2-r0', 's3-r0'] + d3: ['s0-r1', 's1-r1', 's2-r0', 's3-r0'] + + +We can eliminate some of the readings, and hence some of the threads, +by adding background information. + + >>> import nltk.data + >>> bg = nltk.data.load('grammars/book_grammars/background.fol') + >>> dt.add_background(bg) + >>> dt.background() + all x.(boxerdog(x) -> dog(x)) + all x.(boxer(x) -> person(x)) + all x.-(dog(x) & person(x)) + all x.(married(x) <-> exists y.marry(x,y)) + all x.(bark(x) -> dog(x)) + all x y.(marry(x,y) -> (person(x) & person(y))) + -(Vincent = Mia) + -(Vincent = Fido) + -(Mia = Fido) + +The background information allows us to reject three of the threads as +inconsistent. To see what remains, use the ``filter=True`` parameter +on ``readings()``. + + >>> dt.readings(filter=True) + d1: ['s0-r0', 's1-r1', 's2-r0', 's3-r0'] + +The ``models()`` method gives us more information about the surviving thread. + + >>> dt.models() + -------------------------------------------------------------------------------- + Model for Discourse Thread d0 + -------------------------------------------------------------------------------- + No model found! + + -------------------------------------------------------------------------------- + Model for Discourse Thread d1 + -------------------------------------------------------------------------------- + % number = 1 + % seconds = 0 + + % Interpretation of size 3 + + Fido = 0. + + Mia = 1. + + Vincent = 2. + + f1(0) = 0. + f1(1) = 0. + f1(2) = 2. + + bark(0). + - bark(1). + - bark(2). + + - boxer(0). + - boxer(1). + boxer(2). + + boxerdog(0). + - boxerdog(1). + - boxerdog(2). + + dog(0). + - dog(1). + - dog(2). + + - married(0). + - married(1). + married(2). + + - person(0). + - person(1). + person(2). + + - marry(0,0). + - marry(0,1). + - marry(0,2). + - marry(1,0). + - marry(1,1). + - marry(1,2). + - marry(2,0). + - marry(2,1). + marry(2,2). + + -------------------------------------------------------------------------------- + Model for Discourse Thread d2 + -------------------------------------------------------------------------------- + No model found! + + -------------------------------------------------------------------------------- + Model for Discourse Thread d3 + -------------------------------------------------------------------------------- + No model found! + + Inconsistent discourse: d0 ['s0-r0', 's1-r0', 's2-r0', 's3-r0']: + s0-r0: boxer(Vincent) + s1-r0: boxer(Fido) + s2-r0: married(Vincent) + s3-r0: bark(Fido) + + Consistent discourse: d1 ['s0-r0', 's1-r1', 's2-r0', 's3-r0']: + s0-r0: boxer(Vincent) + s1-r1: boxerdog(Fido) + s2-r0: married(Vincent) + s3-r0: bark(Fido) + + Inconsistent discourse: d2 ['s0-r1', 's1-r0', 's2-r0', 's3-r0']: + s0-r1: boxerdog(Vincent) + s1-r0: boxer(Fido) + s2-r0: married(Vincent) + s3-r0: bark(Fido) + + Inconsistent discourse: d3 ['s0-r1', 's1-r1', 's2-r0', 's3-r0']: + s0-r1: boxerdog(Vincent) + s1-r1: boxerdog(Fido) + s2-r0: married(Vincent) + s3-r0: bark(Fido) + + + +.. This will not be visible in the html output: create a tempdir to + play in. + >>> import tempfile, os + >>> tempdir = tempfile.mkdtemp() + >>> old_dir = os.path.abspath('.') + >>> os.chdir(tempdir) + +In order to play around with your own version of background knowledge, +you might want to start off with a local copy of ``background.fol``: + + >>> nltk.data.retrieve('grammars/book_grammars/background.fol') + Retrieving 'nltk:grammars/book_grammars/background.fol', saving to 'background.fol' + +After you have modified the file, the ``load_fol()`` function will parse +the strings in the file into expressions of ``nltk.sem.logic``. + + >>> from nltk.inference.discourse import load_fol + >>> mybg = load_fol(open('background.fol').read()) + +The result can be loaded as an argument of ``add_background()`` in the +manner shown earlier. + +.. This will not be visible in the html output: clean up the tempdir. + >>> os.chdir(old_dir) + >>> for f in os.listdir(tempdir): + ... os.remove(os.path.join(tempdir, f)) + >>> os.rmdir(tempdir) + >>> nltk.data.clear_cache() + + +Regression Testing from book +============================ + + >>> logic._counter._value = 0 + + >>> from nltk.tag import RegexpTagger + >>> tagger = RegexpTagger( + ... [('^(chases|runs)$', 'VB'), + ... ('^(a)$', 'ex_quant'), + ... ('^(every)$', 'univ_quant'), + ... ('^(dog|boy)$', 'NN'), + ... ('^(He)$', 'PRP') + ... ]) + >>> rc = DrtGlueReadingCommand(depparser=MaltParser(tagger=tagger)) + >>> dt = DiscourseTester(map(str.split, ['Every dog chases a boy', 'He runs']), rc) + >>> dt.readings() + + s0 readings: + + s0-r0: ([z2],[boy(z2), (([z5],[dog(z5)]) -> ([],[chases(z5,z2)]))]) + s0-r1: ([],[(([z1],[dog(z1)]) -> ([z2],[boy(z2), chases(z1,z2)]))]) + + s1 readings: + + s1-r0: ([z1],[PRO(z1), runs(z1)]) + >>> dt.readings(show_thread_readings=True) + d0: ['s0-r0', 's1-r0'] : ([z1,z2],[boy(z1), (([z3],[dog(z3)]) -> ([],[chases(z3,z1)])), (z2 = z1), runs(z2)]) + d1: ['s0-r1', 's1-r0'] : INVALID: AnaphoraResolutionException + >>> dt.readings(filter=True, show_thread_readings=True) + d0: ['s0-r0', 's1-r0'] : ([z1,z3],[boy(z1), (([z2],[dog(z2)]) -> ([],[chases(z2,z1)])), (z3 = z1), runs(z3)]) + + >>> logic._counter._value = 0 + + >>> from nltk.parse import FeatureEarleyChartParser + >>> from nltk.sem.drt import DrtParser + >>> grammar = nltk.data.load('grammars/book_grammars/drt.fcfg', logic_parser=DrtParser()) + >>> parser = FeatureEarleyChartParser(grammar, trace=0) + >>> trees = parser.parse('Angus owns a dog'.split()) + >>> print(list(trees)[0].label()['SEM'].simplify().normalize()) + ([z1,z2],[Angus(z1), dog(z2), own(z1,z2)]) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/drt.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/drt.doctest new file mode 100644 index 0000000..06b09e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/drt.doctest @@ -0,0 +1,515 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================================ + Discourse Representation Theory +================================ + + >>> from nltk.sem import logic + >>> from nltk.inference import TableauProver + +Overview +======== + +A DRS can be created with the ``DRS()`` constructor. This takes two arguments: a list of +discourse referents and list of conditions. . + + >>> from nltk.sem.drt import * + >>> dexpr = DrtExpression.fromstring + >>> man_x = dexpr('man(x)') + >>> walk_x = dexpr('walk(x)') + >>> x = dexpr('x') + >>> print(DRS([x], [man_x, walk_x])) + ([x],[man(x), walk(x)]) + +The ``parse()`` method can also be applied directly to DRS +expressions, which allows them to be specified more +easily. + + >>> drs1 = dexpr('([x],[man(x),walk(x)])') + >>> print(drs1) + ([x],[man(x), walk(x)]) + +DRSs can be *merged* using the ``+`` operator. + + >>> drs2 = dexpr('([y],[woman(y),stop(y)])') + >>> drs3 = drs1 + drs2 + >>> print(drs3) + (([x],[man(x), walk(x)]) + ([y],[woman(y), stop(y)])) + >>> print(drs3.simplify()) + ([x,y],[man(x), walk(x), woman(y), stop(y)]) + +We can embed DRSs as components of an ``implies`` condition. + + >>> s = '([], [(%s -> %s)])' % (drs1, drs2) + >>> print(dexpr(s)) + ([],[(([x],[man(x), walk(x)]) -> ([y],[woman(y), stop(y)]))]) + +The ``fol()`` method converts DRSs into FOL formulae. + + >>> print(dexpr(r'([x],[man(x), walks(x)])').fol()) + exists x.(man(x) & walks(x)) + >>> print(dexpr(r'([],[(([x],[man(x)]) -> ([],[walks(x)]))])').fol()) + all x.(man(x) -> walks(x)) + +In order to visualize a DRS, the ``pretty_format()`` method can be used. + + >>> print(drs3.pretty_format()) + _________ __________ + | x | | y | + (|---------| + |----------|) + | man(x) | | woman(y) | + | walk(x) | | stop(y) | + |_________| |__________| + + +Parse to semantics +------------------ + +.. + >>> logic._counter._value = 0 + +DRSs can be used for building compositional semantics in a feature +based grammar. To specify that we want to use DRSs, the appropriate +logic parser needs be passed as a parameter to ``load_earley()`` + + >>> from nltk.parse import load_parser + >>> from nltk.sem.drt import DrtParser + >>> parser = load_parser('grammars/book_grammars/drt.fcfg', trace=0, logic_parser=DrtParser()) + >>> for tree in parser.parse('a dog barks'.split()): + ... print(tree.label()['SEM'].simplify()) + ... + ([x],[dog(x), bark(x)]) + +Alternatively, a ``FeatStructReader`` can be passed with the ``logic_parser`` set on it + + >>> from nltk.featstruct import FeatStructReader + >>> from nltk.grammar import FeatStructNonterminal + >>> parser = load_parser('grammars/book_grammars/drt.fcfg', trace=0, fstruct_reader=FeatStructReader(fdict_class=FeatStructNonterminal, logic_parser=DrtParser())) + >>> for tree in parser.parse('every girl chases a dog'.split()): + ... print(tree.label()['SEM'].simplify().normalize()) + ... + ([],[(([z1],[girl(z1)]) -> ([z2],[dog(z2), chase(z1,z2)]))]) + + + +Unit Tests +========== + +Parser +------ + + >>> print(dexpr(r'([x,y],[sees(x,y)])')) + ([x,y],[sees(x,y)]) + >>> print(dexpr(r'([x],[man(x), walks(x)])')) + ([x],[man(x), walks(x)]) + >>> print(dexpr(r'\x.([],[man(x), walks(x)])')) + \x.([],[man(x), walks(x)]) + >>> print(dexpr(r'\x.\y.([],[sees(x,y)])')) + \x y.([],[sees(x,y)]) + + >>> print(dexpr(r'([x,y],[(x = y)])')) + ([x,y],[(x = y)]) + >>> print(dexpr(r'([x,y],[(x != y)])')) + ([x,y],[-(x = y)]) + + >>> print(dexpr(r'\x.([],[walks(x)])(john)')) + (\x.([],[walks(x)]))(john) + >>> print(dexpr(r'\R.\x.([],[big(x,R)])(\y.([],[mouse(y)]))')) + (\R x.([],[big(x,R)]))(\y.([],[mouse(y)])) + + >>> print(dexpr(r'(([x],[walks(x)]) + ([y],[runs(y)]))')) + (([x],[walks(x)]) + ([y],[runs(y)])) + >>> print(dexpr(r'(([x,y],[walks(x), jumps(y)]) + (([z],[twos(z)]) + ([w],[runs(w)])))')) + (([x,y],[walks(x), jumps(y)]) + ([z],[twos(z)]) + ([w],[runs(w)])) + >>> print(dexpr(r'((([],[walks(x)]) + ([],[twos(x)])) + ([],[runs(x)]))')) + (([],[walks(x)]) + ([],[twos(x)]) + ([],[runs(x)])) + >>> print(dexpr(r'((([],[walks(x)]) + ([],[runs(x)])) + (([],[threes(x)]) + ([],[fours(x)])))')) + (([],[walks(x)]) + ([],[runs(x)]) + ([],[threes(x)]) + ([],[fours(x)])) + + >>> print(dexpr(r'(([],[walks(x)]) -> ([],[runs(x)]))')) + (([],[walks(x)]) -> ([],[runs(x)])) + + >>> print(dexpr(r'([x],[PRO(x), sees(John,x)])')) + ([x],[PRO(x), sees(John,x)]) + >>> print(dexpr(r'([x],[man(x), -([],[walks(x)])])')) + ([x],[man(x), -([],[walks(x)])]) + >>> print(dexpr(r'([],[(([x],[man(x)]) -> ([],[walks(x)]))])')) + ([],[(([x],[man(x)]) -> ([],[walks(x)]))]) + + >>> print(dexpr(r'DRS([x],[walk(x)])')) + ([x],[walk(x)]) + >>> print(dexpr(r'DRS([x][walk(x)])')) + ([x],[walk(x)]) + >>> print(dexpr(r'([x][walk(x)])')) + ([x],[walk(x)]) + +``simplify()`` +-------------- + + >>> print(dexpr(r'\x.([],[man(x), walks(x)])(john)').simplify()) + ([],[man(john), walks(john)]) + >>> print(dexpr(r'\x.\y.([z],[dog(z),sees(x,y)])(john)(mary)').simplify()) + ([z],[dog(z), sees(john,mary)]) + >>> print(dexpr(r'\R x.([],[big(x,R)])(\y.([],[mouse(y)]))').simplify()) + \x.([],[big(x,\y.([],[mouse(y)]))]) + + >>> print(dexpr(r'(([x],[walks(x)]) + ([y],[runs(y)]))').simplify()) + ([x,y],[walks(x), runs(y)]) + >>> print(dexpr(r'(([x,y],[walks(x), jumps(y)]) + (([z],[twos(z)]) + ([w],[runs(w)])))').simplify()) + ([w,x,y,z],[walks(x), jumps(y), twos(z), runs(w)]) + >>> print(dexpr(r'((([],[walks(x)]) + ([],[runs(x)]) + ([],[threes(x)]) + ([],[fours(x)])))').simplify()) + ([],[walks(x), runs(x), threes(x), fours(x)]) + >>> dexpr(r'([x],[man(x)])+([x],[walks(x)])').simplify() == \ + ... dexpr(r'([x,z1],[man(x), walks(z1)])') + True + >>> dexpr(r'([y],[boy(y), (([x],[dog(x)]) -> ([],[chase(x,y)]))])+([x],[run(x)])').simplify() == \ + ... dexpr(r'([y,z1],[boy(y), (([x],[dog(x)]) -> ([],[chase(x,y)])), run(z1)])') + True + + >>> dexpr(r'\Q.(([x],[john(x),walks(x)]) + Q)(([x],[PRO(x),leaves(x)]))').simplify() == \ + ... dexpr(r'([x,z1],[john(x), walks(x), PRO(z1), leaves(z1)])') + True + + >>> logic._counter._value = 0 + >>> print(dexpr('([],[(([x],[dog(x)]) -> ([e,y],[boy(y), chase(e), subj(e,x), obj(e,y)]))])+([e,x],[PRO(x), run(e), subj(e,x)])').simplify().normalize().normalize()) + ([e02,z5],[(([z3],[dog(z3)]) -> ([e01,z4],[boy(z4), chase(e01), subj(e01,z3), obj(e01,z4)])), PRO(z5), run(e02), subj(e02,z5)]) + +``fol()`` +----------- + + >>> print(dexpr(r'([x,y],[sees(x,y)])').fol()) + exists x y.sees(x,y) + >>> print(dexpr(r'([x],[man(x), walks(x)])').fol()) + exists x.(man(x) & walks(x)) + >>> print(dexpr(r'\x.([],[man(x), walks(x)])').fol()) + \x.(man(x) & walks(x)) + >>> print(dexpr(r'\x y.([],[sees(x,y)])').fol()) + \x y.sees(x,y) + + >>> print(dexpr(r'\x.([],[walks(x)])(john)').fol()) + \x.walks(x)(john) + >>> print(dexpr(r'\R x.([],[big(x,R)])(\y.([],[mouse(y)]))').fol()) + (\R x.big(x,R))(\y.mouse(y)) + + >>> print(dexpr(r'(([x],[walks(x)]) + ([y],[runs(y)]))').fol()) + (exists x.walks(x) & exists y.runs(y)) + + >>> print(dexpr(r'(([],[walks(x)]) -> ([],[runs(x)]))').fol()) + (walks(x) -> runs(x)) + + >>> print(dexpr(r'([x],[PRO(x), sees(John,x)])').fol()) + exists x.(PRO(x) & sees(John,x)) + >>> print(dexpr(r'([x],[man(x), -([],[walks(x)])])').fol()) + exists x.(man(x) & -walks(x)) + >>> print(dexpr(r'([],[(([x],[man(x)]) -> ([],[walks(x)]))])').fol()) + all x.(man(x) -> walks(x)) + + >>> print(dexpr(r'([x],[man(x) | walks(x)])').fol()) + exists x.(man(x) | walks(x)) + >>> print(dexpr(r'P(x) + ([x],[walks(x)])').fol()) + (P(x) & exists x.walks(x)) + +``resolve_anaphora()`` +---------------------- + + >>> from nltk.sem.drt import AnaphoraResolutionException + + >>> print(resolve_anaphora(dexpr(r'([x,y,z],[dog(x), cat(y), walks(z), PRO(z)])'))) + ([x,y,z],[dog(x), cat(y), walks(z), (z = [x,y])]) + >>> print(resolve_anaphora(dexpr(r'([],[(([x],[dog(x)]) -> ([y],[walks(y), PRO(y)]))])'))) + ([],[(([x],[dog(x)]) -> ([y],[walks(y), (y = x)]))]) + >>> print(resolve_anaphora(dexpr(r'(([x,y],[]) + ([],[PRO(x)]))')).simplify()) + ([x,y],[(x = y)]) + >>> try: print(resolve_anaphora(dexpr(r'([x],[walks(x), PRO(x)])'))) + ... except AnaphoraResolutionException as e: print(e) + Variable 'x' does not resolve to anything. + >>> print(resolve_anaphora(dexpr('([e01,z6,z7],[boy(z6), PRO(z7), run(e01), subj(e01,z7)])'))) + ([e01,z6,z7],[boy(z6), (z7 = z6), run(e01), subj(e01,z7)]) + +``equiv()``: +---------------- + + >>> a = dexpr(r'([x],[man(x), walks(x)])') + >>> b = dexpr(r'([x],[walks(x), man(x)])') + >>> print(a.equiv(b, TableauProver())) + True + + +``replace()``: +-------------- + + >>> a = dexpr(r'a') + >>> w = dexpr(r'w') + >>> x = dexpr(r'x') + >>> y = dexpr(r'y') + >>> z = dexpr(r'z') + + +replace bound +------------- + + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(x.variable, a, False)) + ([x],[give(x,y,z)]) + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(x.variable, a, True)) + ([a],[give(a,y,z)]) + +replace unbound +--------------- + + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(y.variable, a, False)) + ([x],[give(x,a,z)]) + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(y.variable, a, True)) + ([x],[give(x,a,z)]) + +replace unbound with bound +-------------------------- + + >>> dexpr(r'([x],[give(x,y,z)])').replace(y.variable, x, False) == \ + ... dexpr('([z1],[give(z1,x,z)])') + True + >>> dexpr(r'([x],[give(x,y,z)])').replace(y.variable, x, True) == \ + ... dexpr('([z1],[give(z1,x,z)])') + True + +replace unbound with unbound +---------------------------- + + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(y.variable, z, False)) + ([x],[give(x,z,z)]) + >>> print(dexpr(r'([x],[give(x,y,z)])').replace(y.variable, z, True)) + ([x],[give(x,z,z)]) + + +replace unbound +--------------- + + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(z.variable, a, False)) + (([x],[P(x,y,a)]) + ([y],[Q(x,y,a)])) + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(z.variable, a, True)) + (([x],[P(x,y,a)]) + ([y],[Q(x,y,a)])) + +replace bound +------------- + + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(x.variable, a, False)) + (([x],[P(x,y,z)]) + ([y],[Q(x,y,z)])) + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(x.variable, a, True)) + (([a],[P(a,y,z)]) + ([y],[Q(a,y,z)])) + +replace unbound with unbound +---------------------------- + + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(z.variable, a, False)) + (([x],[P(x,y,a)]) + ([y],[Q(x,y,a)])) + >>> print(dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,z)])').replace(z.variable, a, True)) + (([x],[P(x,y,a)]) + ([y],[Q(x,y,a)])) + +replace unbound with bound on same side +--------------------------------------- + + >>> dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,w)])').replace(z.variable, x, False) == \ + ... dexpr(r'(([z1],[P(z1,y,x)]) + ([y],[Q(z1,y,w)]))') + True + >>> dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,w)])').replace(z.variable, x, True) == \ + ... dexpr(r'(([z1],[P(z1,y,x)]) + ([y],[Q(z1,y,w)]))') + True + +replace unbound with bound on other side +---------------------------------------- + + >>> dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,w)])').replace(w.variable, x, False) == \ + ... dexpr(r'(([z1],[P(z1,y,z)]) + ([y],[Q(z1,y,x)]))') + True + >>> dexpr(r'([x],[P(x,y,z)])+([y],[Q(x,y,w)])').replace(w.variable, x, True) == \ + ... dexpr(r'(([z1],[P(z1,y,z)]) + ([y],[Q(z1,y,x)]))') + True + +replace unbound with double bound +--------------------------------- + + >>> dexpr(r'([x],[P(x,y,z)])+([x],[Q(x,y,w)])').replace(z.variable, x, False) == \ + ... dexpr(r'(([z1],[P(z1,y,x)]) + ([z1],[Q(z1,y,w)]))') + True + >>> dexpr(r'([x],[P(x,y,z)])+([x],[Q(x,y,w)])').replace(z.variable, x, True) == \ + ... dexpr(r'(([z1],[P(z1,y,x)]) + ([z1],[Q(z1,y,w)]))') + True + + +regression tests +---------------- + + >>> d = dexpr('([x],[A(c), ([y],[B(x,y,z,a)])->([z],[C(x,y,z,a)])])') + >>> print(d) + ([x],[A(c), (([y],[B(x,y,z,a)]) -> ([z],[C(x,y,z,a)]))]) + >>> print(d.pretty_format()) + ____________________________________ + | x | + |------------------------------------| + | A(c) | + | ____________ ____________ | + | | y | | z | | + | (|------------| -> |------------|) | + | | B(x,y,z,a) | | C(x,y,z,a) | | + | |____________| |____________| | + |____________________________________| + >>> print(str(d)) + ([x],[A(c), (([y],[B(x,y,z,a)]) -> ([z],[C(x,y,z,a)]))]) + >>> print(d.fol()) + exists x.(A(c) & all y.(B(x,y,z,a) -> exists z.C(x,y,z,a))) + >>> print(d.replace(Variable('a'), DrtVariableExpression(Variable('r')))) + ([x],[A(c), (([y],[B(x,y,z,r)]) -> ([z],[C(x,y,z,r)]))]) + >>> print(d.replace(Variable('x'), DrtVariableExpression(Variable('r')))) + ([x],[A(c), (([y],[B(x,y,z,a)]) -> ([z],[C(x,y,z,a)]))]) + >>> print(d.replace(Variable('y'), DrtVariableExpression(Variable('r')))) + ([x],[A(c), (([y],[B(x,y,z,a)]) -> ([z],[C(x,y,z,a)]))]) + >>> print(d.replace(Variable('z'), DrtVariableExpression(Variable('r')))) + ([x],[A(c), (([y],[B(x,y,r,a)]) -> ([z],[C(x,y,z,a)]))]) + >>> print(d.replace(Variable('x'), DrtVariableExpression(Variable('r')), True)) + ([r],[A(c), (([y],[B(r,y,z,a)]) -> ([z],[C(r,y,z,a)]))]) + >>> print(d.replace(Variable('y'), DrtVariableExpression(Variable('r')), True)) + ([x],[A(c), (([r],[B(x,r,z,a)]) -> ([z],[C(x,r,z,a)]))]) + >>> print(d.replace(Variable('z'), DrtVariableExpression(Variable('r')), True)) + ([x],[A(c), (([y],[B(x,y,r,a)]) -> ([r],[C(x,y,r,a)]))]) + >>> print(d == dexpr('([l],[A(c), ([m],[B(l,m,z,a)])->([n],[C(l,m,n,a)])])')) + True + >>> d = dexpr('([],[([x,y],[B(x,y,h), ([a,b],[dee(x,a,g)])])->([z,w],[cee(x,y,f), ([c,d],[E(x,c,d,e)])])])') + >>> sorted(d.free()) + [Variable('B'), Variable('E'), Variable('e'), Variable('f'), Variable('g'), Variable('h')] + >>> sorted(d.variables()) + [Variable('B'), Variable('E'), Variable('e'), Variable('f'), Variable('g'), Variable('h')] + >>> sorted(d.get_refs(True)) + [Variable('a'), Variable('b'), Variable('c'), Variable('d'), Variable('w'), Variable('x'), Variable('y'), Variable('z')] + >>> sorted(d.conds[0].get_refs(False)) + [Variable('x'), Variable('y')] + >>> print(dexpr('([x,y],[A(x,y), (x=y), ([],[B(x,y)])->([],[C(x,y)]), ([x,y],[D(x,y)])->([],[E(x,y)]), ([],[F(x,y)])->([x,y],[G(x,y)])])').eliminate_equality()) + ([x],[A(x,x), (([],[B(x,x)]) -> ([],[C(x,x)])), (([x,y],[D(x,y)]) -> ([],[E(x,y)])), (([],[F(x,x)]) -> ([x,y],[G(x,y)]))]) + >>> print(dexpr('([x,y],[A(x,y), (x=y)]) -> ([],[B(x,y)])').eliminate_equality()) + (([x],[A(x,x)]) -> ([],[B(x,x)])) + >>> print(dexpr('([x,y],[A(x,y)]) -> ([],[B(x,y), (x=y)])').eliminate_equality()) + (([x,y],[A(x,y)]) -> ([],[B(x,x)])) + >>> print(dexpr('([x,y],[A(x,y), (x=y), ([],[B(x,y)])])').eliminate_equality()) + ([x],[A(x,x), ([],[B(x,x)])]) + >>> print(dexpr('([x,y],[A(x,y), ([],[B(x,y), (x=y)])])').eliminate_equality()) + ([x,y],[A(x,y), ([],[B(x,x)])]) + >>> print(dexpr('([z8 z9 z10],[A(z8), z8=z10, z9=z10, B(z9), C(z10), D(z10)])').eliminate_equality()) + ([z9],[A(z9), B(z9), C(z9), D(z9)]) + + >>> print(dexpr('([x,y],[A(x,y), (x=y), ([],[B(x,y)]), ([x,y],[C(x,y)])])').eliminate_equality()) + ([x],[A(x,x), ([],[B(x,x)]), ([x,y],[C(x,y)])]) + >>> print(dexpr('([x,y],[A(x,y)]) + ([],[B(x,y), (x=y)]) + ([],[C(x,y)])').eliminate_equality()) + ([x],[A(x,x), B(x,x), C(x,x)]) + >>> print(dexpr('([x,y],[B(x,y)])+([x,y],[C(x,y)])').replace(Variable('y'), DrtVariableExpression(Variable('x')))) + (([x,y],[B(x,y)]) + ([x,y],[C(x,y)])) + >>> print(dexpr('(([x,y],[B(x,y)])+([],[C(x,y)]))+([],[D(x,y)])').replace(Variable('y'), DrtVariableExpression(Variable('x')))) + (([x,y],[B(x,y)]) + ([],[C(x,y)]) + ([],[D(x,y)])) + >>> print(dexpr('(([],[B(x,y)])+([],[C(x,y)]))+([],[D(x,y)])').replace(Variable('y'), DrtVariableExpression(Variable('x')))) + (([],[B(x,x)]) + ([],[C(x,x)]) + ([],[D(x,x)])) + >>> print(dexpr('(([],[B(x,y), ([x,y],[A(x,y)])])+([],[C(x,y)]))+([],[D(x,y)])').replace(Variable('y'), DrtVariableExpression(Variable('x'))).normalize()) + (([],[B(z3,z1), ([z2,z3],[A(z3,z2)])]) + ([],[C(z3,z1)]) + ([],[D(z3,z1)])) + + +Parse errors +============ + + >>> def parse_error(drtstring): + ... try: dexpr(drtstring) + ... except logic.LogicalExpressionException as e: print(e) + + >>> parse_error(r'') + End of input found. Expression expected. + + ^ + >>> parse_error(r'(') + End of input found. Expression expected. + ( + ^ + >>> parse_error(r'()') + Unexpected token: ')'. Expression expected. + () + ^ + >>> parse_error(r'([') + End of input found. Expected token ']'. + ([ + ^ + >>> parse_error(r'([,') + ',' is an illegal variable name. Constants may not be quantified. + ([, + ^ + >>> parse_error(r'([x,') + End of input found. Variable expected. + ([x, + ^ + >>> parse_error(r'([]') + End of input found. Expected token '['. + ([] + ^ + >>> parse_error(r'([][') + End of input found. Expected token ']'. + ([][ + ^ + >>> parse_error(r'([][,') + Unexpected token: ','. Expression expected. + ([][, + ^ + >>> parse_error(r'([][]') + End of input found. Expected token ')'. + ([][] + ^ + >>> parse_error(r'([x][man(x)]) |') + End of input found. Expression expected. + ([x][man(x)]) | + ^ + +Pretty Printing +=============== + + >>> dexpr(r"([],[])").pretty_print() + __ + | | + |--| + |__| + + >>> dexpr(r"([],[([x],[big(x), dog(x)]) -> ([],[bark(x)]) -([x],[walk(x)])])").pretty_print() + _____________________________ + | | + |-----------------------------| + | ________ _________ | + | | x | | | | + | (|--------| -> |---------|) | + | | big(x) | | bark(x) | | + | | dog(x) | |_________| | + | |________| | + | _________ | + | | x | | + | __ |---------| | + | | | walk(x) | | + | |_________| | + |_____________________________| + + >>> dexpr(r"([x,y],[x=y]) + ([z],[dog(z), walk(z)])").pretty_print() + _________ _________ + | x y | | z | + (|---------| + |---------|) + | (x = y) | | dog(z) | + |_________| | walk(z) | + |_________| + + >>> dexpr(r"([],[([x],[]) | ([y],[]) | ([z],[dog(z), walk(z)])])").pretty_print() + _______________________________ + | | + |-------------------------------| + | ___ ___ _________ | + | | x | | y | | z | | + | (|---| | |---| | |---------|) | + | |___| |___| | dog(z) | | + | | walk(z) | | + | |_________| | + |_______________________________| + + >>> dexpr(r"\P.\Q.(([x],[]) + P(x) + Q(x))(\x.([],[dog(x)]))").pretty_print() + ___ ________ + \ | x | \ | | + /\ P Q.(|---| + P(x) + Q(x))( /\ x.|--------|) + |___| | dog(x) | + |________| diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/featgram.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/featgram.doctest new file mode 100644 index 0000000..dc231ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/featgram.doctest @@ -0,0 +1,610 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================= + Feature Grammar Parsing +========================= + +.. definitions from nltk_book/definitions.rst + +.. role:: feat + :class: feature +.. role:: fval + :class: fval +.. |rarr| unicode:: U+2192 .. right arrow +.. |dot| unicode:: U+2022 .. bullet +.. |pi| unicode:: U+03C0 + +Grammars can be parsed from strings. + + >>> import nltk + >>> from nltk import grammar, parse + >>> g = """ + ... % start DP + ... DP[AGR=?a] -> D[AGR=?a] N[AGR=?a] + ... D[AGR=[NUM='sg', PERS=3]] -> 'this' | 'that' + ... D[AGR=[NUM='pl', PERS=3]] -> 'these' | 'those' + ... D[AGR=[NUM='pl', PERS=1]] -> 'we' + ... D[AGR=[PERS=2]] -> 'you' + ... N[AGR=[NUM='sg', GND='m']] -> 'boy' + ... N[AGR=[NUM='pl', GND='m']] -> 'boys' + ... N[AGR=[NUM='sg', GND='f']] -> 'girl' + ... N[AGR=[NUM='pl', GND='f']] -> 'girls' + ... N[AGR=[NUM='sg']] -> 'student' + ... N[AGR=[NUM='pl']] -> 'students' + ... """ + >>> grammar = grammar.FeatureGrammar.fromstring(g) + >>> tokens = 'these girls'.split() + >>> parser = parse.FeatureEarleyChartParser(grammar) + >>> trees = parser.parse(tokens) + >>> for tree in trees: print(tree) + (DP[AGR=[GND='f', NUM='pl', PERS=3]] + (D[AGR=[NUM='pl', PERS=3]] these) + (N[AGR=[GND='f', NUM='pl']] girls)) + +In general, when we are trying to develop even a very small grammar, +it is convenient to put the rules in a file where they can be edited, +tested and revised. Let's assume that we have saved feat0cfg as a file named +``'feat0.fcfg'`` and placed it in the NLTK ``data`` directory. We can +inspect it as follows: + + >>> nltk.data.show_cfg('grammars/book_grammars/feat0.fcfg') + % start S + # ################### + # Grammar Productions + # ################### + # S expansion productions + S -> NP[NUM=?n] VP[NUM=?n] + # NP expansion productions + NP[NUM=?n] -> N[NUM=?n] + NP[NUM=?n] -> PropN[NUM=?n] + NP[NUM=?n] -> Det[NUM=?n] N[NUM=?n] + NP[NUM=pl] -> N[NUM=pl] + # VP expansion productions + VP[TENSE=?t, NUM=?n] -> IV[TENSE=?t, NUM=?n] + VP[TENSE=?t, NUM=?n] -> TV[TENSE=?t, NUM=?n] NP + # ################### + # Lexical Productions + # ################### + Det[NUM=sg] -> 'this' | 'every' + Det[NUM=pl] -> 'these' | 'all' + Det -> 'the' | 'some' | 'several' + PropN[NUM=sg]-> 'Kim' | 'Jody' + N[NUM=sg] -> 'dog' | 'girl' | 'car' | 'child' + N[NUM=pl] -> 'dogs' | 'girls' | 'cars' | 'children' + IV[TENSE=pres, NUM=sg] -> 'disappears' | 'walks' + TV[TENSE=pres, NUM=sg] -> 'sees' | 'likes' + IV[TENSE=pres, NUM=pl] -> 'disappear' | 'walk' + TV[TENSE=pres, NUM=pl] -> 'see' | 'like' + IV[TENSE=past] -> 'disappeared' | 'walked' + TV[TENSE=past] -> 'saw' | 'liked' + +Assuming we have saved feat0cfg as a file named +``'feat0.fcfg'``, the function ``parse.load_parser`` allows us to +read the grammar into NLTK, ready for use in parsing. + + + >>> cp = parse.load_parser('grammars/book_grammars/feat0.fcfg', trace=1) + >>> sent = 'Kim likes children' + >>> tokens = sent.split() + >>> tokens + ['Kim', 'likes', 'children'] + >>> trees = cp.parse(tokens) + |.Kim .like.chil.| + |[----] . .| [0:1] 'Kim' + |. [----] .| [1:2] 'likes' + |. . [----]| [2:3] 'children' + |[----] . .| [0:1] PropN[NUM='sg'] -> 'Kim' * + |[----] . .| [0:1] NP[NUM='sg'] -> PropN[NUM='sg'] * + |[----> . .| [0:1] S[] -> NP[NUM=?n] * VP[NUM=?n] {?n: 'sg'} + |. [----] .| [1:2] TV[NUM='sg', TENSE='pres'] -> 'likes' * + |. [----> .| [1:2] VP[NUM=?n, TENSE=?t] -> TV[NUM=?n, TENSE=?t] * NP[] {?n: 'sg', ?t: 'pres'} + |. . [----]| [2:3] N[NUM='pl'] -> 'children' * + |. . [----]| [2:3] NP[NUM='pl'] -> N[NUM='pl'] * + |. . [---->| [2:3] S[] -> NP[NUM=?n] * VP[NUM=?n] {?n: 'pl'} + |. [---------]| [1:3] VP[NUM='sg', TENSE='pres'] -> TV[NUM='sg', TENSE='pres'] NP[] * + |[==============]| [0:3] S[] -> NP[NUM='sg'] VP[NUM='sg'] * + >>> for tree in trees: print(tree) + (S[] + (NP[NUM='sg'] (PropN[NUM='sg'] Kim)) + (VP[NUM='sg', TENSE='pres'] + (TV[NUM='sg', TENSE='pres'] likes) + (NP[NUM='pl'] (N[NUM='pl'] children)))) + +The parser works directly with +the underspecified productions given by the grammar. That is, the +Predictor rule does not attempt to compile out all admissible feature +combinations before trying to expand the non-terminals on the left hand +side of a production. However, when the Scanner matches an input word +against a lexical production that has been predicted, the new edge will +typically contain fully specified features; e.g., the edge +[PropN[`num`:feat: = `sg`:fval:] |rarr| 'Kim', (0, 1)]. Recall from +Chapter 8 that the Fundamental (or Completer) Rule in +standard CFGs is used to combine an incomplete edge that's expecting a +nonterminal *B* with a following, complete edge whose left hand side +matches *B*. In our current setting, rather than checking for a +complete match, we test whether the expected category *B* will +unify with the left hand side *B'* of a following complete +edge. We will explain in more detail in Section 9.2 how +unification works; for the moment, it is enough to know that as a +result of unification, any variable values of features in *B* will be +instantiated by constant values in the corresponding feature structure +in *B'*, and these instantiated values will be used in the new edge +added by the Completer. This instantiation can be seen, for example, +in the edge +[NP [`num`:feat:\ =\ `sg`:fval:] |rarr| PropN[`num`:feat:\ =\ `sg`:fval:] |dot|, (0, 1)] +in Example 9.2, where the feature `num`:feat: has been assigned the value `sg`:fval:. + +Feature structures in NLTK are ... Atomic feature values can be strings or +integers. + + >>> fs1 = nltk.FeatStruct(TENSE='past', NUM='sg') + >>> print(fs1) + [ NUM = 'sg' ] + [ TENSE = 'past' ] + +We can think of a feature structure as being like a Python dictionary, +and access its values by indexing in the usual way. + + >>> fs1 = nltk.FeatStruct(PER=3, NUM='pl', GND='fem') + >>> print(fs1['GND']) + fem + +We can also define feature structures which have complex values, as +discussed earlier. + + >>> fs2 = nltk.FeatStruct(POS='N', AGR=fs1) + >>> print(fs2) + [ [ GND = 'fem' ] ] + [ AGR = [ NUM = 'pl' ] ] + [ [ PER = 3 ] ] + [ ] + [ POS = 'N' ] + >>> print(fs2['AGR']) + [ GND = 'fem' ] + [ NUM = 'pl' ] + [ PER = 3 ] + >>> print(fs2['AGR']['PER']) + 3 + +Feature structures can also be constructed using the ``parse()`` +method of the ``nltk.FeatStruct`` class. Note that in this case, atomic +feature values do not need to be enclosed in quotes. + + >>> f1 = nltk.FeatStruct("[NUMBER = sg]") + >>> f2 = nltk.FeatStruct("[PERSON = 3]") + >>> print(nltk.unify(f1, f2)) + [ NUMBER = 'sg' ] + [ PERSON = 3 ] + + >>> f1 = nltk.FeatStruct("[A = [B = b, D = d]]") + >>> f2 = nltk.FeatStruct("[A = [C = c, D = d]]") + >>> print(nltk.unify(f1, f2)) + [ [ B = 'b' ] ] + [ A = [ C = 'c' ] ] + [ [ D = 'd' ] ] + + +Feature Structures as Graphs +---------------------------- + +Feature structures are not inherently tied to linguistic objects; they are +general purpose structures for representing knowledge. For example, we +could encode information about a person in a feature structure: + + >>> person01 = nltk.FeatStruct("[NAME=Lee, TELNO='01 27 86 42 96',AGE=33]") + >>> print(person01) + [ AGE = 33 ] + [ NAME = 'Lee' ] + [ TELNO = '01 27 86 42 96' ] + +There are a number of notations for representing reentrancy in +matrix-style representations of feature structures. In NLTK, we adopt +the following convention: the first occurrence of a shared feature structure +is prefixed with an integer in parentheses, such as ``(1)``, and any +subsequent reference to that structure uses the notation +``->(1)``, as shown below. + + + >>> fs = nltk.FeatStruct("""[NAME=Lee, ADDRESS=(1)[NUMBER=74, STREET='rue Pascal'], + ... SPOUSE=[NAME=Kim, ADDRESS->(1)]]""") + >>> print(fs) + [ ADDRESS = (1) [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ NAME = 'Lee' ] + [ ] + [ SPOUSE = [ ADDRESS -> (1) ] ] + [ [ NAME = 'Kim' ] ] + +There can be any number of tags within a single feature structure. + + >>> fs3 = nltk.FeatStruct("[A=(1)[B=b], C=(2)[], D->(1), E->(2)]") + >>> print(fs3) + [ A = (1) [ B = 'b' ] ] + [ ] + [ C = (2) [] ] + [ ] + [ D -> (1) ] + [ E -> (2) ] + >>> fs1 = nltk.FeatStruct(NUMBER=74, STREET='rue Pascal') + >>> fs2 = nltk.FeatStruct(CITY='Paris') + >>> print(nltk.unify(fs1, fs2)) + [ CITY = 'Paris' ] + [ NUMBER = 74 ] + [ STREET = 'rue Pascal' ] + +Unification is symmetric: + + >>> nltk.unify(fs1, fs2) == nltk.unify(fs2, fs1) + True + +Unification is commutative: + + >>> fs3 = nltk.FeatStruct(TELNO='01 27 86 42 96') + >>> nltk.unify(nltk.unify(fs1, fs2), fs3) == nltk.unify(fs1, nltk.unify(fs2, fs3)) + True + +Unification between *FS*:math:`_0` and *FS*:math:`_1` will fail if the +two feature structures share a path |pi|, +but the value of |pi| in *FS*:math:`_0` is a distinct +atom from the value of |pi| in *FS*:math:`_1`. In NLTK, +this is implemented by setting the result of unification to be +``None``. + + >>> fs0 = nltk.FeatStruct(A='a') + >>> fs1 = nltk.FeatStruct(A='b') + >>> print(nltk.unify(fs0, fs1)) + None + +Now, if we look at how unification interacts with structure-sharing, +things become really interesting. + + + + >>> fs0 = nltk.FeatStruct("""[NAME=Lee, + ... ADDRESS=[NUMBER=74, + ... STREET='rue Pascal'], + ... SPOUSE= [NAME=Kim, + ... ADDRESS=[NUMBER=74, + ... STREET='rue Pascal']]]""") + >>> print(fs0) + [ ADDRESS = [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ NAME = 'Lee' ] + [ ] + [ [ ADDRESS = [ NUMBER = 74 ] ] ] + [ SPOUSE = [ [ STREET = 'rue Pascal' ] ] ] + [ [ ] ] + [ [ NAME = 'Kim' ] ] + + + >>> fs1 = nltk.FeatStruct("[SPOUSE=[ADDRESS=[CITY=Paris]]]") + >>> print(nltk.unify(fs0, fs1)) + [ ADDRESS = [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ NAME = 'Lee' ] + [ ] + [ [ [ CITY = 'Paris' ] ] ] + [ [ ADDRESS = [ NUMBER = 74 ] ] ] + [ SPOUSE = [ [ STREET = 'rue Pascal' ] ] ] + [ [ ] ] + [ [ NAME = 'Kim' ] ] + + >>> fs2 = nltk.FeatStruct("""[NAME=Lee, ADDRESS=(1)[NUMBER=74, STREET='rue Pascal'], + ... SPOUSE=[NAME=Kim, ADDRESS->(1)]]""") + + + >>> print(fs2) + [ ADDRESS = (1) [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ NAME = 'Lee' ] + [ ] + [ SPOUSE = [ ADDRESS -> (1) ] ] + [ [ NAME = 'Kim' ] ] + + + >>> print(nltk.unify(fs2, fs1)) + [ [ CITY = 'Paris' ] ] + [ ADDRESS = (1) [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ NAME = 'Lee' ] + [ ] + [ SPOUSE = [ ADDRESS -> (1) ] ] + [ [ NAME = 'Kim' ] ] + + + >>> fs1 = nltk.FeatStruct("[ADDRESS1=[NUMBER=74, STREET='rue Pascal']]") + >>> fs2 = nltk.FeatStruct("[ADDRESS1=?x, ADDRESS2=?x]") + >>> print(fs2) + [ ADDRESS1 = ?x ] + [ ADDRESS2 = ?x ] + >>> print(nltk.unify(fs1, fs2)) + [ ADDRESS1 = (1) [ NUMBER = 74 ] ] + [ [ STREET = 'rue Pascal' ] ] + [ ] + [ ADDRESS2 -> (1) ] + + + + + >>> sent = 'who do you claim that you like' + >>> tokens = sent.split() + >>> cp = parse.load_parser('grammars/book_grammars/feat1.fcfg', trace=1) + >>> trees = cp.parse(tokens) + |.w.d.y.c.t.y.l.| + |[-] . . . . . .| [0:1] 'who' + |. [-] . . . . .| [1:2] 'do' + |. . [-] . . . .| [2:3] 'you' + |. . . [-] . . .| [3:4] 'claim' + |. . . . [-] . .| [4:5] 'that' + |. . . . . [-] .| [5:6] 'you' + |. . . . . . [-]| [6:7] 'like' + |# . . . . . . .| [0:0] NP[]/NP[] -> * + |. # . . . . . .| [1:1] NP[]/NP[] -> * + |. . # . . . . .| [2:2] NP[]/NP[] -> * + |. . . # . . . .| [3:3] NP[]/NP[] -> * + |. . . . # . . .| [4:4] NP[]/NP[] -> * + |. . . . . # . .| [5:5] NP[]/NP[] -> * + |. . . . . . # .| [6:6] NP[]/NP[] -> * + |. . . . . . . #| [7:7] NP[]/NP[] -> * + |[-] . . . . . .| [0:1] NP[+WH] -> 'who' * + |[-> . . . . . .| [0:1] S[-INV] -> NP[] * VP[] {} + |[-> . . . . . .| [0:1] S[-INV]/?x[] -> NP[] * VP[]/?x[] {} + |[-> . . . . . .| [0:1] S[-INV] -> NP[] * S[]/NP[] {} + |. [-] . . . . .| [1:2] V[+AUX] -> 'do' * + |. [-> . . . . .| [1:2] S[+INV] -> V[+AUX] * NP[] VP[] {} + |. [-> . . . . .| [1:2] S[+INV]/?x[] -> V[+AUX] * NP[] VP[]/?x[] {} + |. [-> . . . . .| [1:2] VP[] -> V[+AUX] * VP[] {} + |. [-> . . . . .| [1:2] VP[]/?x[] -> V[+AUX] * VP[]/?x[] {} + |. . [-] . . . .| [2:3] NP[-WH] -> 'you' * + |. . [-> . . . .| [2:3] S[-INV] -> NP[] * VP[] {} + |. . [-> . . . .| [2:3] S[-INV]/?x[] -> NP[] * VP[]/?x[] {} + |. . [-> . . . .| [2:3] S[-INV] -> NP[] * S[]/NP[] {} + |. [---> . . . .| [1:3] S[+INV] -> V[+AUX] NP[] * VP[] {} + |. [---> . . . .| [1:3] S[+INV]/?x[] -> V[+AUX] NP[] * VP[]/?x[] {} + |. . . [-] . . .| [3:4] V[-AUX, SUBCAT='clause'] -> 'claim' * + |. . . [-> . . .| [3:4] VP[] -> V[-AUX, SUBCAT='clause'] * SBar[] {} + |. . . [-> . . .| [3:4] VP[]/?x[] -> V[-AUX, SUBCAT='clause'] * SBar[]/?x[] {} + |. . . . [-] . .| [4:5] Comp[] -> 'that' * + |. . . . [-> . .| [4:5] SBar[] -> Comp[] * S[-INV] {} + |. . . . [-> . .| [4:5] SBar[]/?x[] -> Comp[] * S[-INV]/?x[] {} + |. . . . . [-] .| [5:6] NP[-WH] -> 'you' * + |. . . . . [-> .| [5:6] S[-INV] -> NP[] * VP[] {} + |. . . . . [-> .| [5:6] S[-INV]/?x[] -> NP[] * VP[]/?x[] {} + |. . . . . [-> .| [5:6] S[-INV] -> NP[] * S[]/NP[] {} + |. . . . . . [-]| [6:7] V[-AUX, SUBCAT='trans'] -> 'like' * + |. . . . . . [->| [6:7] VP[] -> V[-AUX, SUBCAT='trans'] * NP[] {} + |. . . . . . [->| [6:7] VP[]/?x[] -> V[-AUX, SUBCAT='trans'] * NP[]/?x[] {} + |. . . . . . [-]| [6:7] VP[]/NP[] -> V[-AUX, SUBCAT='trans'] NP[]/NP[] * + |. . . . . [---]| [5:7] S[-INV]/NP[] -> NP[] VP[]/NP[] * + |. . . . [-----]| [4:7] SBar[]/NP[] -> Comp[] S[-INV]/NP[] * + |. . . [-------]| [3:7] VP[]/NP[] -> V[-AUX, SUBCAT='clause'] SBar[]/NP[] * + |. . [---------]| [2:7] S[-INV]/NP[] -> NP[] VP[]/NP[] * + |. [-----------]| [1:7] S[+INV]/NP[] -> V[+AUX] NP[] VP[]/NP[] * + |[=============]| [0:7] S[-INV] -> NP[] S[]/NP[] * + + >>> trees = list(trees) + >>> for tree in trees: print(tree) + (S[-INV] + (NP[+WH] who) + (S[+INV]/NP[] + (V[+AUX] do) + (NP[-WH] you) + (VP[]/NP[] + (V[-AUX, SUBCAT='clause'] claim) + (SBar[]/NP[] + (Comp[] that) + (S[-INV]/NP[] + (NP[-WH] you) + (VP[]/NP[] (V[-AUX, SUBCAT='trans'] like) (NP[]/NP[] ))))))) + +A different parser should give the same parse trees, but perhaps in a different order: + + >>> cp2 = parse.load_parser('grammars/book_grammars/feat1.fcfg', trace=1, + ... parser=parse.FeatureEarleyChartParser) + >>> trees2 = cp2.parse(tokens) + |.w.d.y.c.t.y.l.| + |[-] . . . . . .| [0:1] 'who' + |. [-] . . . . .| [1:2] 'do' + |. . [-] . . . .| [2:3] 'you' + |. . . [-] . . .| [3:4] 'claim' + |. . . . [-] . .| [4:5] 'that' + |. . . . . [-] .| [5:6] 'you' + |. . . . . . [-]| [6:7] 'like' + |> . . . . . . .| [0:0] S[-INV] -> * NP[] VP[] {} + |> . . . . . . .| [0:0] S[-INV]/?x[] -> * NP[] VP[]/?x[] {} + |> . . . . . . .| [0:0] S[-INV] -> * NP[] S[]/NP[] {} + |> . . . . . . .| [0:0] S[-INV] -> * Adv[+NEG] S[+INV] {} + |> . . . . . . .| [0:0] S[+INV] -> * V[+AUX] NP[] VP[] {} + |> . . . . . . .| [0:0] S[+INV]/?x[] -> * V[+AUX] NP[] VP[]/?x[] {} + |> . . . . . . .| [0:0] NP[+WH] -> * 'who' {} + |[-] . . . . . .| [0:1] NP[+WH] -> 'who' * + |[-> . . . . . .| [0:1] S[-INV] -> NP[] * VP[] {} + |[-> . . . . . .| [0:1] S[-INV]/?x[] -> NP[] * VP[]/?x[] {} + |[-> . . . . . .| [0:1] S[-INV] -> NP[] * S[]/NP[] {} + |. > . . . . . .| [1:1] S[-INV]/?x[] -> * NP[] VP[]/?x[] {} + |. > . . . . . .| [1:1] S[+INV]/?x[] -> * V[+AUX] NP[] VP[]/?x[] {} + |. > . . . . . .| [1:1] V[+AUX] -> * 'do' {} + |. > . . . . . .| [1:1] VP[]/?x[] -> * V[-AUX, SUBCAT='trans'] NP[]/?x[] {} + |. > . . . . . .| [1:1] VP[]/?x[] -> * V[-AUX, SUBCAT='clause'] SBar[]/?x[] {} + |. > . . . . . .| [1:1] VP[]/?x[] -> * V[+AUX] VP[]/?x[] {} + |. > . . . . . .| [1:1] VP[] -> * V[-AUX, SUBCAT='intrans'] {} + |. > . . . . . .| [1:1] VP[] -> * V[-AUX, SUBCAT='trans'] NP[] {} + |. > . . . . . .| [1:1] VP[] -> * V[-AUX, SUBCAT='clause'] SBar[] {} + |. > . . . . . .| [1:1] VP[] -> * V[+AUX] VP[] {} + |. [-] . . . . .| [1:2] V[+AUX] -> 'do' * + |. [-> . . . . .| [1:2] S[+INV]/?x[] -> V[+AUX] * NP[] VP[]/?x[] {} + |. [-> . . . . .| [1:2] VP[]/?x[] -> V[+AUX] * VP[]/?x[] {} + |. [-> . . . . .| [1:2] VP[] -> V[+AUX] * VP[] {} + |. . > . . . . .| [2:2] VP[] -> * V[-AUX, SUBCAT='intrans'] {} + |. . > . . . . .| [2:2] VP[] -> * V[-AUX, SUBCAT='trans'] NP[] {} + |. . > . . . . .| [2:2] VP[] -> * V[-AUX, SUBCAT='clause'] SBar[] {} + |. . > . . . . .| [2:2] VP[] -> * V[+AUX] VP[] {} + |. . > . . . . .| [2:2] VP[]/?x[] -> * V[-AUX, SUBCAT='trans'] NP[]/?x[] {} + |. . > . . . . .| [2:2] VP[]/?x[] -> * V[-AUX, SUBCAT='clause'] SBar[]/?x[] {} + |. . > . . . . .| [2:2] VP[]/?x[] -> * V[+AUX] VP[]/?x[] {} + |. . > . . . . .| [2:2] NP[-WH] -> * 'you' {} + |. . [-] . . . .| [2:3] NP[-WH] -> 'you' * + |. [---> . . . .| [1:3] S[+INV]/?x[] -> V[+AUX] NP[] * VP[]/?x[] {} + |. . . > . . . .| [3:3] VP[]/?x[] -> * V[-AUX, SUBCAT='trans'] NP[]/?x[] {} + |. . . > . . . .| [3:3] VP[]/?x[] -> * V[-AUX, SUBCAT='clause'] SBar[]/?x[] {} + |. . . > . . . .| [3:3] VP[]/?x[] -> * V[+AUX] VP[]/?x[] {} + |. . . > . . . .| [3:3] V[-AUX, SUBCAT='clause'] -> * 'claim' {} + |. . . [-] . . .| [3:4] V[-AUX, SUBCAT='clause'] -> 'claim' * + |. . . [-> . . .| [3:4] VP[]/?x[] -> V[-AUX, SUBCAT='clause'] * SBar[]/?x[] {} + |. . . . > . . .| [4:4] SBar[]/?x[] -> * Comp[] S[-INV]/?x[] {} + |. . . . > . . .| [4:4] Comp[] -> * 'that' {} + |. . . . [-] . .| [4:5] Comp[] -> 'that' * + |. . . . [-> . .| [4:5] SBar[]/?x[] -> Comp[] * S[-INV]/?x[] {} + |. . . . . > . .| [5:5] S[-INV]/?x[] -> * NP[] VP[]/?x[] {} + |. . . . . > . .| [5:5] NP[-WH] -> * 'you' {} + |. . . . . [-] .| [5:6] NP[-WH] -> 'you' * + |. . . . . [-> .| [5:6] S[-INV]/?x[] -> NP[] * VP[]/?x[] {} + |. . . . . . > .| [6:6] VP[]/?x[] -> * V[-AUX, SUBCAT='trans'] NP[]/?x[] {} + |. . . . . . > .| [6:6] VP[]/?x[] -> * V[-AUX, SUBCAT='clause'] SBar[]/?x[] {} + |. . . . . . > .| [6:6] VP[]/?x[] -> * V[+AUX] VP[]/?x[] {} + |. . . . . . > .| [6:6] V[-AUX, SUBCAT='trans'] -> * 'like' {} + |. . . . . . [-]| [6:7] V[-AUX, SUBCAT='trans'] -> 'like' * + |. . . . . . [->| [6:7] VP[]/?x[] -> V[-AUX, SUBCAT='trans'] * NP[]/?x[] {} + |. . . . . . . #| [7:7] NP[]/NP[] -> * + |. . . . . . [-]| [6:7] VP[]/NP[] -> V[-AUX, SUBCAT='trans'] NP[]/NP[] * + |. . . . . [---]| [5:7] S[-INV]/NP[] -> NP[] VP[]/NP[] * + |. . . . [-----]| [4:7] SBar[]/NP[] -> Comp[] S[-INV]/NP[] * + |. . . [-------]| [3:7] VP[]/NP[] -> V[-AUX, SUBCAT='clause'] SBar[]/NP[] * + |. [-----------]| [1:7] S[+INV]/NP[] -> V[+AUX] NP[] VP[]/NP[] * + |[=============]| [0:7] S[-INV] -> NP[] S[]/NP[] * + + >>> sorted(trees) == sorted(trees2) + True + + +Let's load a German grammar: + + >>> cp = parse.load_parser('grammars/book_grammars/german.fcfg', trace=0) + >>> sent = 'die Katze sieht den Hund' + >>> tokens = sent.split() + >>> trees = cp.parse(tokens) + >>> for tree in trees: print(tree) + (S[] + (NP[AGR=[GND='fem', NUM='sg', PER=3], CASE='nom'] + (Det[AGR=[GND='fem', NUM='sg', PER=3], CASE='nom'] die) + (N[AGR=[GND='fem', NUM='sg', PER=3]] Katze)) + (VP[AGR=[NUM='sg', PER=3]] + (TV[AGR=[NUM='sg', PER=3], OBJCASE='acc'] sieht) + (NP[AGR=[GND='masc', NUM='sg', PER=3], CASE='acc'] + (Det[AGR=[GND='masc', NUM='sg', PER=3], CASE='acc'] den) + (N[AGR=[GND='masc', NUM='sg', PER=3]] Hund)))) + +Grammar with Binding Operators +------------------------------ +The bindop.fcfg grammar is a semantic grammar that uses lambda +calculus. Each element has a core semantics, which is a single lambda +calculus expression; and a set of binding operators, which bind +variables. + +In order to make the binding operators work right, they need to +instantiate their bound variable every time they are added to the +chart. To do this, we use a special subclass of `Chart`, called +`InstantiateVarsChart`. + + >>> from nltk.parse.featurechart import InstantiateVarsChart + >>> cp = parse.load_parser('grammars/sample_grammars/bindop.fcfg', trace=1, + ... chart_class=InstantiateVarsChart) + >>> print(cp.grammar()) + Grammar with 15 productions (start state = S[]) + S[SEM=[BO={?b1+?b2}, CORE=]] -> NP[SEM=[BO=?b1, CORE=?subj]] VP[SEM=[BO=?b2, CORE=?vp]] + VP[SEM=[BO={?b1+?b2}, CORE=]] -> TV[SEM=[BO=?b1, CORE=?v]] NP[SEM=[BO=?b2, CORE=?obj]] + VP[SEM=?s] -> IV[SEM=?s] + NP[SEM=[BO={?b1+?b2+{bo(?det(?n),@x)}}, CORE=<@x>]] -> Det[SEM=[BO=?b1, CORE=?det]] N[SEM=[BO=?b2, CORE=?n]] + Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] -> 'a' + N[SEM=[BO={/}, CORE=]] -> 'dog' + N[SEM=[BO={/}, CORE=]] -> 'cat' + N[SEM=[BO={/}, CORE=]] -> 'mouse' + IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] -> 'barks' + IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] -> 'eats' + IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] -> 'walks' + TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] -> 'feeds' + TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] -> 'walks' + NP[SEM=[BO={bo(\P.P(John),@x)}, CORE=<@x>]] -> 'john' + NP[SEM=[BO={bo(\P.P(John),@x)}, CORE=<@x>]] -> 'alex' + +A simple intransitive sentence: + + >>> from nltk.sem import logic + >>> logic._counter._value = 100 + + >>> trees = cp.parse('john barks'.split()) + |. john.barks.| + |[-----] .| [0:1] 'john' + |. [-----]| [1:2] 'barks' + |[-----] .| [0:1] NP[SEM=[BO={bo(\P.P(John),z101)}, CORE=]] -> 'john' * + |[-----> .| [0:1] S[SEM=[BO={?b1+?b2}, CORE=]] -> NP[SEM=[BO=?b1, CORE=?subj]] * VP[SEM=[BO=?b2, CORE=?vp]] {?b1: {bo(\P.P(John),z2)}, ?subj: } + |. [-----]| [1:2] IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] -> 'barks' * + |. [-----]| [1:2] VP[SEM=[BO={/}, CORE=<\x.bark(x)>]] -> IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] * + |[===========]| [0:2] S[SEM=[BO={bo(\P.P(John),z2)}, CORE=]] -> NP[SEM=[BO={bo(\P.P(John),z2)}, CORE=]] VP[SEM=[BO={/}, CORE=<\x.bark(x)>]] * + >>> for tree in trees: print(tree) + (S[SEM=[BO={bo(\P.P(John),z2)}, CORE=]] + (NP[SEM=[BO={bo(\P.P(John),z101)}, CORE=]] john) + (VP[SEM=[BO={/}, CORE=<\x.bark(x)>]] + (IV[SEM=[BO={/}, CORE=<\x.bark(x)>]] barks))) + +A transitive sentence: + + >>> trees = cp.parse('john feeds a dog'.split()) + |.joh.fee. a .dog.| + |[---] . . .| [0:1] 'john' + |. [---] . .| [1:2] 'feeds' + |. . [---] .| [2:3] 'a' + |. . . [---]| [3:4] 'dog' + |[---] . . .| [0:1] NP[SEM=[BO={bo(\P.P(John),z102)}, CORE=]] -> 'john' * + |[---> . . .| [0:1] S[SEM=[BO={?b1+?b2}, CORE=]] -> NP[SEM=[BO=?b1, CORE=?subj]] * VP[SEM=[BO=?b2, CORE=?vp]] {?b1: {bo(\P.P(John),z2)}, ?subj: } + |. [---] . .| [1:2] TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] -> 'feeds' * + |. [---> . .| [1:2] VP[SEM=[BO={?b1+?b2}, CORE=]] -> TV[SEM=[BO=?b1, CORE=?v]] * NP[SEM=[BO=?b2, CORE=?obj]] {?b1: {/}, ?v: } + |. . [---] .| [2:3] Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] -> 'a' * + |. . [---> .| [2:3] NP[SEM=[BO={?b1+?b2+{bo(?det(?n),@x)}}, CORE=<@x>]] -> Det[SEM=[BO=?b1, CORE=?det]] * N[SEM=[BO=?b2, CORE=?n]] {?b1: {/}, ?det: } + |. . . [---]| [3:4] N[SEM=[BO={/}, CORE=]] -> 'dog' * + |. . [-------]| [2:4] NP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z103)}, CORE=]] -> Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] N[SEM=[BO={/}, CORE=]] * + |. . [------->| [2:4] S[SEM=[BO={?b1+?b2}, CORE=]] -> NP[SEM=[BO=?b1, CORE=?subj]] * VP[SEM=[BO=?b2, CORE=?vp]] {?b1: {bo(\P.exists x.(dog(x) & P(x)),z2)}, ?subj: } + |. [-----------]| [1:4] VP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z2)}, CORE=<\y.feed(y,z2)>]] -> TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] NP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z2)}, CORE=]] * + |[===============]| [0:4] S[SEM=[BO={bo(\P.P(John),z2), bo(\P.exists x.(dog(x) & P(x)),z3)}, CORE=]] -> NP[SEM=[BO={bo(\P.P(John),z2)}, CORE=]] VP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z3)}, CORE=<\y.feed(y,z3)>]] * + + >>> for tree in trees: print(tree) + (S[SEM=[BO={bo(\P.P(John),z2), bo(\P.exists x.(dog(x) & P(x)),z3)}, CORE=]] + (NP[SEM=[BO={bo(\P.P(John),z102)}, CORE=]] john) + (VP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z2)}, CORE=<\y.feed(y,z2)>]] + (TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] feeds) + (NP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z103)}, CORE=]] + (Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] a) + (N[SEM=[BO={/}, CORE=]] dog)))) + +Turn down the verbosity: + + >>> cp = parse.load_parser('grammars/sample_grammars/bindop.fcfg', trace=0, + ... chart_class=InstantiateVarsChart) + +Reuse the same lexical item twice: + + >>> trees = cp.parse('john feeds john'.split()) + >>> for tree in trees: print(tree) + (S[SEM=[BO={bo(\P.P(John),z2), bo(\P.P(John),z3)}, CORE=]] + (NP[SEM=[BO={bo(\P.P(John),z104)}, CORE=]] john) + (VP[SEM=[BO={bo(\P.P(John),z2)}, CORE=<\y.feed(y,z2)>]] + (TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] feeds) + (NP[SEM=[BO={bo(\P.P(John),z105)}, CORE=]] john))) + + >>> trees = cp.parse('a dog feeds a dog'.split()) + >>> for tree in trees: print(tree) + (S[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z2), bo(\P.exists x.(dog(x) & P(x)),z3)}, CORE=]] + (NP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z106)}, CORE=]] + (Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] a) + (N[SEM=[BO={/}, CORE=]] dog)) + (VP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z2)}, CORE=<\y.feed(y,z2)>]] + (TV[SEM=[BO={/}, CORE=<\x y.feed(y,x)>]] feeds) + (NP[SEM=[BO={bo(\P.exists x.(dog(x) & P(x)),z107)}, CORE=]] + (Det[SEM=[BO={/}, CORE=<\Q P.exists x.(Q(x) & P(x))>]] a) + (N[SEM=[BO={/}, CORE=]] dog)))) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/featstruct.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/featstruct.doctest new file mode 100644 index 0000000..37ca654 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/featstruct.doctest @@ -0,0 +1,1229 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================================== + Feature Structures & Unification +================================== + >>> from nltk.featstruct import FeatStruct + >>> from nltk.sem.logic import Variable, VariableExpression, Expression + +.. note:: For now, featstruct uses the older lambdalogic semantics + module. Eventually, it should be updated to use the new first + order predicate logic module. + +Overview +~~~~~~~~ +A feature structure is a mapping from feature identifiers to feature +values, where feature values can be simple values (like strings or +ints), nested feature structures, or variables: + + >>> fs1 = FeatStruct(number='singular', person=3) + >>> print(fs1) + [ number = 'singular' ] + [ person = 3 ] + +Feature structure may be nested: + + >>> fs2 = FeatStruct(type='NP', agr=fs1) + >>> print(fs2) + [ agr = [ number = 'singular' ] ] + [ [ person = 3 ] ] + [ ] + [ type = 'NP' ] + +Variables are used to indicate that two features should be assigned +the same value. For example, the following feature structure requires +that the feature fs3['agr']['number'] be bound to the same value as the +feature fs3['subj']['number']. + + >>> fs3 = FeatStruct(agr=FeatStruct(number=Variable('?n')), + ... subj=FeatStruct(number=Variable('?n'))) + >>> print(fs3) + [ agr = [ number = ?n ] ] + [ ] + [ subj = [ number = ?n ] ] + +Feature structures are typically used to represent partial information +about objects. A feature name that is not mapped to a value stands +for a feature whose value is unknown (*not* a feature without a +value). Two feature structures that represent (potentially +overlapping) information about the same object can be combined by +*unification*. + + >>> print(fs2.unify(fs3)) + [ agr = [ number = 'singular' ] ] + [ [ person = 3 ] ] + [ ] + [ subj = [ number = 'singular' ] ] + [ ] + [ type = 'NP' ] + +When two inconsistent feature structures are unified, the unification +fails and returns ``None``. + + >>> fs4 = FeatStruct(agr=FeatStruct(person=1)) + >>> print(fs4.unify(fs2)) + None + >>> print(fs2.unify(fs4)) + None + +.. + >>> del fs1, fs2, fs3, fs4 # clean-up + +Feature Structure Types +----------------------- +There are actually two types of feature structure: + +- *feature dictionaries*, implemented by `FeatDict`, act like + Python dictionaries. Feature identifiers may be strings or + instances of the `Feature` class. +- *feature lists*, implemented by `FeatList`, act like Python + lists. Feature identifiers are integers. + +When you construct a feature structure using the `FeatStruct` +constructor, it will automatically decide which type is appropriate: + + >>> type(FeatStruct(number='singular')) + + >>> type(FeatStruct([1,2,3])) + + +Usually, we will just use feature dictionaries; but sometimes feature +lists can be useful too. Two feature lists will unify with each other +only if they have equal lengths, and all of their feature values +match. If you wish to write a feature list that contains 'unknown' +values, you must use variables: + + >>> fs1 = FeatStruct([1,2,Variable('?y')]) + >>> fs2 = FeatStruct([1,Variable('?x'),3]) + >>> fs1.unify(fs2) + [1, 2, 3] + +.. + >>> del fs1, fs2 # clean-up + +Parsing Feature Structure Strings +--------------------------------- +Feature structures can be constructed directly from strings. Often, +this is more convenient than constructing them directly. NLTK can +parse most feature strings to produce the corresponding feature +structures. (But you must restrict your base feature values to +strings, ints, logic expressions (`nltk.sem.logic.Expression`), and a +few other types discussed below). + +Feature dictionaries are written like Python dictionaries, except that +keys are not put in quotes; and square brackets (``[]``) are used +instead of braces (``{}``): + + >>> FeatStruct('[tense="past", agr=[number="sing", person=3]]') + [agr=[number='sing', person=3], tense='past'] + +If a feature value is a single alphanumeric word, then it does not +need to be quoted -- it will be automatically treated as a string: + + >>> FeatStruct('[tense=past, agr=[number=sing, person=3]]') + [agr=[number='sing', person=3], tense='past'] + +Feature lists are written like python lists: + + >>> FeatStruct('[1, 2, 3]') + [1, 2, 3] + +The expression ``[]`` is treated as an empty feature dictionary, not +an empty feature list: + + >>> type(FeatStruct('[]')) + + +Feature Paths +------------- +Features can be specified using *feature paths*, or tuples of feature +identifiers that specify path through the nested feature structures to +a value. + + >>> fs1 = FeatStruct('[x=1, y=[1,2,[z=3]]]') + >>> fs1['y'] + [1, 2, [z=3]] + >>> fs1['y', 2] + [z=3] + >>> fs1['y', 2, 'z'] + 3 + +.. + >>> del fs1 # clean-up + +Reentrance +---------- +Feature structures may contain reentrant feature values. A *reentrant +feature value* is a single feature structure that can be accessed via +multiple feature paths. + + >>> fs1 = FeatStruct(x='val') + >>> fs2 = FeatStruct(a=fs1, b=fs1) + >>> print(fs2) + [ a = (1) [ x = 'val' ] ] + [ ] + [ b -> (1) ] + >>> fs2 + [a=(1)[x='val'], b->(1)] + +As you can see, reentrane is displayed by marking a feature structure +with a unique identifier, in this case ``(1)``, the first time it is +encountered; and then using the special form ``var -> id`` whenever it +is encountered again. You can use the same notation to directly +create reentrant feature structures from strings. + + >>> FeatStruct('[a=(1)[], b->(1), c=[d->(1)]]') + [a=(1)[], b->(1), c=[d->(1)]] + +Reentrant feature structures may contain cycles: + + >>> fs3 = FeatStruct('(1)[a->(1)]') + >>> fs3['a', 'a', 'a', 'a'] + (1)[a->(1)] + >>> fs3['a', 'a', 'a', 'a'] is fs3 + True + +Unification preserves the reentrance relations imposed by both of the +unified feature structures. In the feature structure resulting from +unification, any modifications to a reentrant feature value will be +visible using any of its feature paths. + + >>> fs3.unify(FeatStruct('[a=[b=12], c=33]')) + (1)[a->(1), b=12, c=33] + +.. + >>> del fs1, fs2, fs3 # clean-up + +Feature Structure Equality +-------------------------- +Two feature structures are considered equal if they assign the same +values to all features, *and* they contain the same reentrances. + + >>> fs1 = FeatStruct('[a=(1)[x=1], b->(1)]') + >>> fs2 = FeatStruct('[a=(1)[x=1], b->(1)]') + >>> fs3 = FeatStruct('[a=[x=1], b=[x=1]]') + >>> fs1 == fs1, fs1 is fs1 + (True, True) + >>> fs1 == fs2, fs1 is fs2 + (True, False) + >>> fs1 == fs3, fs1 is fs3 + (False, False) + +Note that this differs from how Python dictionaries and lists define +equality -- in particular, Python dictionaries and lists ignore +reentrance relations. To test two feature structures for equality +while ignoring reentrance relations, use the `equal_values()` method: + + >>> fs1.equal_values(fs1) + True + >>> fs1.equal_values(fs2) + True + >>> fs1.equal_values(fs3) + True + +.. + >>> del fs1, fs2, fs3 # clean-up + +Feature Value Sets & Feature Value Tuples +----------------------------------------- +`nltk.featstruct` defines two new data types that are intended to be +used as feature values: `FeatureValueTuple` and `FeatureValueSet`. +Both of these types are considered base values -- i.e., unification +does *not* apply to them. However, variable binding *does* apply to +any values that they contain. + +Feature value tuples are written with parentheses: + + >>> fs1 = FeatStruct('[x=(?x, ?y)]') + >>> fs1 + [x=(?x, ?y)] + >>> fs1.substitute_bindings({Variable('?x'): 1, Variable('?y'): 2}) + [x=(1, 2)] + +Feature sets are written with braces: + + >>> fs1 = FeatStruct('[x={?x, ?y}]') + >>> fs1 + [x={?x, ?y}] + >>> fs1.substitute_bindings({Variable('?x'): 1, Variable('?y'): 2}) + [x={1, 2}] + +In addition to the basic feature value tuple & set classes, nltk +defines feature value unions (for sets) and feature value +concatenations (for tuples). These are written using '+', and can be +used to combine sets & tuples: + + >>> fs1 = FeatStruct('[x=((1, 2)+?z), z=?z]') + >>> fs1 + [x=((1, 2)+?z), z=?z] + >>> fs1.unify(FeatStruct('[z=(3, 4, 5)]')) + [x=(1, 2, 3, 4, 5), z=(3, 4, 5)] + +Thus, feature value tuples and sets can be used to build up tuples +and sets of values over the course of unification. For example, when +parsing sentences using a semantic feature grammar, feature sets or +feature tuples can be used to build a list of semantic predicates as +the sentence is parsed. + +As was mentioned above, unification does not apply to feature value +tuples and sets. One reason for this that it's impossible to define a +single correct answer for unification when concatenation is used. +Consider the following example: + + >>> fs1 = FeatStruct('[x=(1, 2, 3, 4)]') + >>> fs2 = FeatStruct('[x=(?a+?b), a=?a, b=?b]') + +If unification applied to feature tuples, then the unification +algorithm would have to arbitrarily choose how to divide the tuple +(1,2,3,4) into two parts. Instead, the unification algorithm refuses +to make this decision, and simply unifies based on value. Because +(1,2,3,4) is not equal to (?a+?b), fs1 and fs2 will not unify: + + >>> print(fs1.unify(fs2)) + None + +If you need a list-like structure that unification does apply to, use +`FeatList`. + +.. + >>> del fs1, fs2 # clean-up + +Light-weight Feature Structures +------------------------------- +Many of the functions defined by `nltk.featstruct` can be applied +directly to simple Python dictionaries and lists, rather than to +full-fledged `FeatDict` and `FeatList` objects. In other words, +Python ``dicts`` and ``lists`` can be used as "light-weight" feature +structures. + + >>> # Note: pprint prints dicts sorted + >>> from pprint import pprint + >>> from nltk.featstruct import unify + >>> pprint(unify(dict(x=1, y=dict()), dict(a='a', y=dict(b='b')))) + {'a': 'a', 'x': 1, 'y': {'b': 'b'}} + +However, you should keep in mind the following caveats: + +- Python dictionaries & lists ignore reentrance when checking for + equality between values. But two FeatStructs with different + reentrances are considered nonequal, even if all their base + values are equal. + +- FeatStructs can be easily frozen, allowing them to be used as + keys in hash tables. Python dictionaries and lists can not. + +- FeatStructs display reentrance in their string representations; + Python dictionaries and lists do not. + +- FeatStructs may *not* be mixed with Python dictionaries and lists + (e.g., when performing unification). + +- FeatStructs provide a number of useful methods, such as `walk()` + and `cyclic()`, which are not available for Python dicts & lists. + +In general, if your feature structures will contain any reentrances, +or if you plan to use them as dictionary keys, it is strongly +recommended that you use full-fledged `FeatStruct` objects. + +Custom Feature Values +--------------------- +The abstract base class `CustomFeatureValue` can be used to define new +base value types that have custom unification methods. For example, +the following feature value type encodes a range, and defines +unification as taking the intersection on the ranges: + + >>> from functools import total_ordering + >>> from nltk.featstruct import CustomFeatureValue, UnificationFailure + >>> @total_ordering + ... class Range(CustomFeatureValue): + ... def __init__(self, low, high): + ... assert low <= high + ... self.low = low + ... self.high = high + ... def unify(self, other): + ... if not isinstance(other, Range): + ... return UnificationFailure + ... low = max(self.low, other.low) + ... high = min(self.high, other.high) + ... if low <= high: return Range(low, high) + ... else: return UnificationFailure + ... def __repr__(self): + ... return '(%s>> fs1 = FeatStruct(x=Range(5,8), y=FeatStruct(z=Range(7,22))) + >>> print(fs1.unify(FeatStruct(x=Range(6, 22)))) + [ x = (6>> print(fs1.unify(FeatStruct(x=Range(9, 12)))) + None + >>> print(fs1.unify(FeatStruct(x=12))) + None + >>> print(fs1.unify(FeatStruct('[x=?x, y=[z=?x]]'))) + [ x = (7>> fs1 = FeatStruct(a=1, b=2, c=3) + >>> fs2 = FeatStruct(x=fs1, y='x') + +Feature structures support all dictionary methods (excluding the class +method `dict.fromkeys()`). Non-mutating methods: + + >>> sorted(fs2.keys()) # keys() + ['x', 'y'] + >>> sorted(fs2.values()) # values() + [[a=1, b=2, c=3], 'x'] + >>> sorted(fs2.items()) # items() + [('x', [a=1, b=2, c=3]), ('y', 'x')] + >>> sorted(fs2) # __iter__() + ['x', 'y'] + >>> 'a' in fs2, 'x' in fs2 # __contains__() + (False, True) + >>> fs2.has_key('a'), fs2.has_key('x') # has_key() + (False, True) + >>> fs2['x'], fs2['y'] # __getitem__() + ([a=1, b=2, c=3], 'x') + >>> fs2['a'] # __getitem__() + Traceback (most recent call last): + . . . + KeyError: 'a' + >>> fs2.get('x'), fs2.get('y'), fs2.get('a') # get() + ([a=1, b=2, c=3], 'x', None) + >>> fs2.get('x', 'hello'), fs2.get('a', 'hello') # get() + ([a=1, b=2, c=3], 'hello') + >>> len(fs1), len(fs2) # __len__ + (3, 2) + >>> fs2.copy() # copy() + [x=[a=1, b=2, c=3], y='x'] + >>> fs2.copy() is fs2 # copy() + False + +Note: by default, `FeatStruct.copy()` does a deep copy. Use +`FeatStruct.copy(deep=False)` for a shallow copy. + +.. + >>> del fs1, fs2 # clean-up. + +Dictionary access methods (mutating) +------------------------------------ + >>> fs1 = FeatStruct(a=1, b=2, c=3) + >>> fs2 = FeatStruct(x=fs1, y='x') + +Setting features (`__setitem__()`) + + >>> fs1['c'] = 5 + >>> fs1 + [a=1, b=2, c=5] + >>> fs1['x'] = 12 + >>> fs1 + [a=1, b=2, c=5, x=12] + >>> fs2['x', 'a'] = 2 + >>> fs2 + [x=[a=2, b=2, c=5, x=12], y='x'] + >>> fs1 + [a=2, b=2, c=5, x=12] + +Deleting features (`__delitem__()`) + + >>> del fs1['x'] + >>> fs1 + [a=2, b=2, c=5] + >>> del fs2['x', 'a'] + >>> fs1 + [b=2, c=5] + +`setdefault()`: + + >>> fs1.setdefault('b', 99) + 2 + >>> fs1 + [b=2, c=5] + >>> fs1.setdefault('x', 99) + 99 + >>> fs1 + [b=2, c=5, x=99] + +`update()`: + + >>> fs2.update({'a':'A', 'b':'B'}, c='C') + >>> fs2 + [a='A', b='B', c='C', x=[b=2, c=5, x=99], y='x'] + +`pop()`: + + >>> fs2.pop('a') + 'A' + >>> fs2 + [b='B', c='C', x=[b=2, c=5, x=99], y='x'] + >>> fs2.pop('a') + Traceback (most recent call last): + . . . + KeyError: 'a' + >>> fs2.pop('a', 'foo') + 'foo' + >>> fs2 + [b='B', c='C', x=[b=2, c=5, x=99], y='x'] + +`clear()`: + + >>> fs1.clear() + >>> fs1 + [] + >>> fs2 + [b='B', c='C', x=[], y='x'] + +`popitem()`: + + >>> sorted([fs2.popitem() for i in range(len(fs2))]) + [('b', 'B'), ('c', 'C'), ('x', []), ('y', 'x')] + >>> fs2 + [] + +Once a feature structure has been frozen, it may not be mutated. + + >>> fs1 = FeatStruct('[x=1, y=2, z=[a=3]]') + >>> fs1.freeze() + >>> fs1.frozen() + True + >>> fs1['z'].frozen() + True + + >>> fs1['x'] = 5 + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> del fs1['x'] + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> fs1.clear() + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> fs1.pop('x') + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> fs1.popitem() + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> fs1.setdefault('x') + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + >>> fs1.update(z=22) + Traceback (most recent call last): + . . . + ValueError: Frozen FeatStructs may not be modified. + +.. + >>> del fs1, fs2 # clean-up. + +Feature Paths +------------- +Make sure that __getitem__ with feature paths works as intended: + + >>> fs1 = FeatStruct(a=1, b=2, + ... c=FeatStruct( + ... d=FeatStruct(e=12), + ... f=FeatStruct(g=55, h='hello'))) + >>> fs1[()] + [a=1, b=2, c=[d=[e=12], f=[g=55, h='hello']]] + >>> fs1['a'], fs1[('a',)] + (1, 1) + >>> fs1['c','d','e'] + 12 + >>> fs1['c','f','g'] + 55 + +Feature paths that select unknown features raise KeyError: + + >>> fs1['c', 'f', 'e'] + Traceback (most recent call last): + . . . + KeyError: ('c', 'f', 'e') + >>> fs1['q', 'p'] + Traceback (most recent call last): + . . . + KeyError: ('q', 'p') + +Feature paths that try to go 'through' a feature that's not a feature +structure raise KeyError: + + >>> fs1['a', 'b'] + Traceback (most recent call last): + . . . + KeyError: ('a', 'b') + +Feature paths can go through reentrant structures: + + >>> fs2 = FeatStruct('(1)[a=[b=[c->(1), d=5], e=11]]') + >>> fs2['a', 'b', 'c', 'a', 'e'] + 11 + >>> fs2['a', 'b', 'c', 'a', 'b', 'd'] + 5 + >>> fs2[tuple('abcabcabcabcabcabcabcabcabcabca')] + (1)[b=[c=[a->(1)], d=5], e=11] + +Indexing requires strings, `Feature`\s, or tuples; other types raise a +TypeError: + + >>> fs2[12] + Traceback (most recent call last): + . . . + TypeError: Expected feature name or path. Got 12. + >>> fs2[list('abc')] + Traceback (most recent call last): + . . . + TypeError: Expected feature name or path. Got ['a', 'b', 'c']. + +Feature paths can also be used with `get()`, `has_key()`, and +`__contains__()`. + + >>> fpath1 = tuple('abcabc') + >>> fpath2 = tuple('abcabz') + >>> fs2.get(fpath1), fs2.get(fpath2) + ((1)[a=[b=[c->(1), d=5], e=11]], None) + >>> fpath1 in fs2, fpath2 in fs2 + (True, False) + >>> fs2.has_key(fpath1), fs2.has_key(fpath2) + (True, False) + +.. + >>> del fs1, fs2 # clean-up + +Reading Feature Structures +-------------------------- + +Empty feature struct: + + >>> FeatStruct('[]') + [] + +Test features with integer values: + + >>> FeatStruct('[a=12, b=-33, c=0]') + [a=12, b=-33, c=0] + +Test features with string values. Either single or double quotes may +be used. Strings are evaluated just like python strings -- in +particular, you can use escape sequences and 'u' and 'r' prefixes, and +triple-quoted strings. + + >>> FeatStruct('[a="", b="hello", c="\'", d=\'\', e=\'"\']') + [a='', b='hello', c="'", d='', e='"'] + >>> FeatStruct(r'[a="\\", b="\"", c="\x6f\\y", d="12"]') + [a='\\', b='"', c='o\\y', d='12'] + >>> FeatStruct(r'[b=r"a\b\c"]') + [b='a\\b\\c'] + >>> FeatStruct('[x="""a"""]') + [x='a'] + +Test parsing of reentrant feature structures. + + >>> FeatStruct('[a=(1)[], b->(1)]') + [a=(1)[], b->(1)] + >>> FeatStruct('[a=(1)[x=1, y=2], b->(1)]') + [a=(1)[x=1, y=2], b->(1)] + +Test parsing of cyclic feature structures. + + >>> FeatStruct('[a=(1)[b->(1)]]') + [a=(1)[b->(1)]] + >>> FeatStruct('(1)[a=[b=[c->(1)]]]') + (1)[a=[b=[c->(1)]]] + +Strings of the form "+name" and "-name" may be used to specify boolean +values. + + >>> FeatStruct('[-bar, +baz, +foo]') + [-bar, +baz, +foo] + +None, True, and False are recognized as values: + + >>> FeatStruct('[bar=True, baz=False, foo=None]') + [+bar, -baz, foo=None] + +Special features: + + >>> FeatStruct('NP/VP') + NP[]/VP[] + >>> FeatStruct('?x/?x') + ?x[]/?x[] + >>> print(FeatStruct('VP[+fin, agr=?x, tense=past]/NP[+pl, agr=?x]')) + [ *type* = 'VP' ] + [ ] + [ [ *type* = 'NP' ] ] + [ *slash* = [ agr = ?x ] ] + [ [ pl = True ] ] + [ ] + [ agr = ?x ] + [ fin = True ] + [ tense = 'past' ] + +Here the slash feature gets coerced: + + >>> FeatStruct('[*slash*=a, x=b, *type*="NP"]') + NP[x='b']/a[] + + >>> FeatStruct('NP[sem=]/NP') + NP[sem=]/NP[] + >>> FeatStruct('S[sem=]') + S[sem=] + >>> print(FeatStruct('NP[sem=]/NP')) + [ *type* = 'NP' ] + [ ] + [ *slash* = [ *type* = 'NP' ] ] + [ ] + [ sem = ] + +Playing with ranges: + + >>> from nltk.featstruct import RangeFeature, FeatStructReader + >>> width = RangeFeature('width') + >>> reader = FeatStructReader([width]) + >>> fs1 = reader.fromstring('[*width*=-5:12]') + >>> fs2 = reader.fromstring('[*width*=2:123]') + >>> fs3 = reader.fromstring('[*width*=-7:-2]') + >>> fs1.unify(fs2) + [*width*=(2, 12)] + >>> fs1.unify(fs3) + [*width*=(-5, -2)] + >>> print(fs2.unify(fs3)) # no overlap in width. + None + +The slash feature has a default value of 'False': + + >>> print(FeatStruct('NP[]/VP').unify(FeatStruct('NP[]'), trace=1)) + + Unification trace: + / NP[]/VP[] + |\ NP[] + | + | Unify feature: *type* + | / 'NP' + | |\ 'NP' + | | + | +-->'NP' + | + | Unify feature: *slash* + | / VP[] + | |\ False + | | + X X <-- FAIL + None + +The demo structures from category.py. They all parse, but they don't +do quite the right thing, -- ?x vs x. + + >>> FeatStruct(pos='n', agr=FeatStruct(number='pl', gender='f')) + [agr=[gender='f', number='pl'], pos='n'] + >>> FeatStruct(r'NP[sem=]/NP') + NP[sem=]/NP[] + >>> FeatStruct(r'S[sem=]') + S[sem=] + >>> FeatStruct('?x/?x') + ?x[]/?x[] + >>> FeatStruct('VP[+fin, agr=?x, tense=past]/NP[+pl, agr=?x]') + VP[agr=?x, +fin, tense='past']/NP[agr=?x, +pl] + >>> FeatStruct('S[sem = ]') + S[sem=] + + >>> FeatStruct('S') + S[] + +The parser also includes support for reading sets and tuples. + + >>> FeatStruct('[x={1,2,2,2}, y={/}]') + [x={1, 2}, y={/}] + >>> FeatStruct('[x=(1,2,2,2), y=()]') + [x=(1, 2, 2, 2), y=()] + >>> print(FeatStruct('[x=(1,[z=(1,2,?x)],?z,{/})]')) + [ x = (1, [ z = (1, 2, ?x) ], ?z, {/}) ] + +Note that we can't put a featstruct inside a tuple, because doing so +would hash it, and it's not frozen yet: + + >>> print(FeatStruct('[x={[]}]')) + Traceback (most recent call last): + . . . + TypeError: FeatStructs must be frozen before they can be hashed. + +There's a special syntax for taking the union of sets: "{...+...}". +The elements should only be variables or sets. + + >>> FeatStruct('[x={?a+?b+{1,2,3}}]') + [x={?a+?b+{1, 2, 3}}] + +There's a special syntax for taking the concatenation of tuples: +"(...+...)". The elements should only be variables or tuples. + + >>> FeatStruct('[x=(?a+?b+(1,2,3))]') + [x=(?a+?b+(1, 2, 3))] + +Parsing gives helpful messages if your string contains an error. + + >>> FeatStruct('[a=, b=5]]') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + [a=, b=5]] + ^ Expected value + >>> FeatStruct('[a=12 22, b=33]') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + [a=12 22, b=33] + ^ Expected comma + >>> FeatStruct('[a=5] [b=6]') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + [a=5] [b=6] + ^ Expected end of string + >>> FeatStruct(' *++*') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + *++* + ^ Expected open bracket or identifier + >>> FeatStruct('[x->(1)]') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + [x->(1)] + ^ Expected bound identifier + >>> FeatStruct('[x->y]') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + [x->y] + ^ Expected identifier + >>> FeatStruct('') + Traceback (most recent call last): + . . . + ValueError: Error parsing feature structure + + ^ Expected open bracket or identifier + + +Unification +----------- +Very simple unifications give the expected results: + + >>> FeatStruct().unify(FeatStruct()) + [] + >>> FeatStruct(number='singular').unify(FeatStruct()) + [number='singular'] + >>> FeatStruct().unify(FeatStruct(number='singular')) + [number='singular'] + >>> FeatStruct(number='singular').unify(FeatStruct(person=3)) + [number='singular', person=3] + +Merging nested structures: + + >>> fs1 = FeatStruct('[A=[B=b]]') + >>> fs2 = FeatStruct('[A=[C=c]]') + >>> fs1.unify(fs2) + [A=[B='b', C='c']] + >>> fs2.unify(fs1) + [A=[B='b', C='c']] + +A basic case of reentrant unification + + >>> fs4 = FeatStruct('[A=(1)[B=b], E=[F->(1)]]') + >>> fs5 = FeatStruct("[A=[C='c'], E=[F=[D='d']]]") + >>> fs4.unify(fs5) + [A=(1)[B='b', C='c', D='d'], E=[F->(1)]] + >>> fs5.unify(fs4) + [A=(1)[B='b', C='c', D='d'], E=[F->(1)]] + +More than 2 paths to a value + + >>> fs1 = FeatStruct("[a=[],b=[],c=[],d=[]]") + >>> fs2 = FeatStruct('[a=(1)[], b->(1), c->(1), d->(1)]') + >>> fs1.unify(fs2) + [a=(1)[], b->(1), c->(1), d->(1)] + +fs1[a] gets unified with itself + + >>> fs1 = FeatStruct('[x=(1)[], y->(1)]') + >>> fs2 = FeatStruct('[x=(1)[], y->(1)]') + >>> fs1.unify(fs2) + [x=(1)[], y->(1)] + +Bound variables should get forwarded appropriately + + >>> fs1 = FeatStruct('[A=(1)[X=x], B->(1), C=?cvar, D=?dvar]') + >>> fs2 = FeatStruct('[A=(1)[Y=y], B=(2)[Z=z], C->(1), D->(2)]') + >>> fs1.unify(fs2) + [A=(1)[X='x', Y='y', Z='z'], B->(1), C->(1), D->(1)] + >>> fs2.unify(fs1) + [A=(1)[X='x', Y='y', Z='z'], B->(1), C->(1), D->(1)] + +Cyclic structure created by unification. + + >>> fs1 = FeatStruct('[F=(1)[], G->(1)]') + >>> fs2 = FeatStruct('[F=[H=(2)[]], G->(2)]') + >>> fs3 = fs1.unify(fs2) + >>> fs3 + [F=(1)[H->(1)], G->(1)] + >>> fs3['F'] is fs3['G'] + True + >>> fs3['F'] is fs3['G']['H'] + True + >>> fs3['F'] is fs3['G']['H']['H'] + True + >>> fs3['F'] is fs3['F']['H']['H']['H']['H']['H']['H']['H']['H'] + True + +Cyclic structure created w/ variables. + + >>> fs1 = FeatStruct('[F=[H=?x]]') + >>> fs2 = FeatStruct('[F=?x]') + >>> fs3 = fs1.unify(fs2, rename_vars=False) + >>> fs3 + [F=(1)[H->(1)]] + >>> fs3['F'] is fs3['F']['H'] + True + >>> fs3['F'] is fs3['F']['H']['H'] + True + >>> fs3['F'] is fs3['F']['H']['H']['H']['H']['H']['H']['H']['H'] + True + +Unifying w/ a cyclic feature structure. + + >>> fs4 = FeatStruct('[F=[H=[H=[H=(1)[]]]], K->(1)]') + >>> fs3.unify(fs4) + [F=(1)[H->(1)], K->(1)] + >>> fs4.unify(fs3) + [F=(1)[H->(1)], K->(1)] + +Variable bindings should preserve reentrance. + + >>> bindings = {} + >>> fs1 = FeatStruct("[a=?x]") + >>> fs2 = fs1.unify(FeatStruct("[a=[]]"), bindings) + >>> fs2['a'] is bindings[Variable('?x')] + True + >>> fs2.unify(FeatStruct("[b=?x]"), bindings) + [a=(1)[], b->(1)] + +Aliased variable tests + + >>> fs1 = FeatStruct("[a=?x, b=?x]") + >>> fs2 = FeatStruct("[b=?y, c=?y]") + >>> bindings = {} + >>> fs3 = fs1.unify(fs2, bindings) + >>> fs3 + [a=?x, b=?x, c=?x] + >>> bindings + {Variable('?y'): Variable('?x')} + >>> fs3.unify(FeatStruct("[a=1]")) + [a=1, b=1, c=1] + +If we keep track of the bindings, then we can use the same variable +over multiple calls to unify. + + >>> bindings = {} + >>> fs1 = FeatStruct('[a=?x]') + >>> fs2 = fs1.unify(FeatStruct('[a=[]]'), bindings) + >>> fs2.unify(FeatStruct('[b=?x]'), bindings) + [a=(1)[], b->(1)] + >>> bindings + {Variable('?x'): []} + +.. + >>> del fs1, fs2, fs3, fs4, fs5 # clean-up + +Unification Bindings +-------------------- + + >>> bindings = {} + >>> fs1 = FeatStruct('[a=?x]') + >>> fs2 = FeatStruct('[a=12]') + >>> fs3 = FeatStruct('[b=?x]') + >>> fs1.unify(fs2, bindings) + [a=12] + >>> bindings + {Variable('?x'): 12} + >>> fs3.substitute_bindings(bindings) + [b=12] + >>> fs3 # substitute_bindings didn't mutate fs3. + [b=?x] + >>> fs2.unify(fs3, bindings) + [a=12, b=12] + + >>> bindings = {} + >>> fs1 = FeatStruct('[a=?x, b=1]') + >>> fs2 = FeatStruct('[a=5, b=?x]') + >>> fs1.unify(fs2, bindings) + [a=5, b=1] + >>> sorted(bindings.items()) + [(Variable('?x'), 5), (Variable('?x2'), 1)] + +.. + >>> del fs1, fs2, fs3 # clean-up + +Expressions +----------- + + >>> e = Expression.fromstring('\\P y.P(z,y)') + >>> fs1 = FeatStruct(x=e, y=Variable('z')) + >>> fs2 = FeatStruct(y=VariableExpression(Variable('John'))) + >>> fs1.unify(fs2) + [x=<\P y.P(John,y)>, y=] + +Remove Variables +---------------- + + >>> FeatStruct('[a=?x, b=12, c=[d=?y]]').remove_variables() + [b=12, c=[]] + >>> FeatStruct('(1)[a=[b=?x,c->(1)]]').remove_variables() + (1)[a=[c->(1)]] + +Equality & Hashing +------------------ +The `equal_values` method checks whether two feature structures assign +the same value to every feature. If the optional argument +``check_reentrances`` is supplied, then it also returns false if there +is any difference in the reentrances. + + >>> a = FeatStruct('(1)[x->(1)]') + >>> b = FeatStruct('(1)[x->(1)]') + >>> c = FeatStruct('(1)[x=[x->(1)]]') + >>> d = FeatStruct('[x=(1)[x->(1)]]') + >>> e = FeatStruct('(1)[x=[x->(1), y=1], y=1]') + >>> def compare(x,y): + ... assert x.equal_values(y, True) == y.equal_values(x, True) + ... assert x.equal_values(y, False) == y.equal_values(x, False) + ... if x.equal_values(y, True): + ... assert x.equal_values(y, False) + ... print('equal values, same reentrance') + ... elif x.equal_values(y, False): + ... print('equal values, different reentrance') + ... else: + ... print('different values') + + >>> compare(a, a) + equal values, same reentrance + >>> compare(a, b) + equal values, same reentrance + >>> compare(a, c) + equal values, different reentrance + >>> compare(a, d) + equal values, different reentrance + >>> compare(c, d) + equal values, different reentrance + >>> compare(a, e) + different values + >>> compare(c, e) + different values + >>> compare(d, e) + different values + >>> compare(e, e) + equal values, same reentrance + +Feature structures may not be hashed until they are frozen: + + >>> hash(a) + Traceback (most recent call last): + . . . + TypeError: FeatStructs must be frozen before they can be hashed. + >>> a.freeze() + >>> v = hash(a) + +Feature structures define hash consistently. The following example +looks at the hash value for each (fs1,fs2) pair; if their hash values +are not equal, then they must not be equal. If their hash values are +equal, then display a message, and indicate whether their values are +indeed equal. Note that c and d currently have the same hash value, +even though they are not equal. That is not a bug, strictly speaking, +but it wouldn't be a bad thing if it changed. + + >>> for fstruct in (a, b, c, d, e): + ... fstruct.freeze() + >>> for fs1_name in 'abcde': + ... for fs2_name in 'abcde': + ... fs1 = locals()[fs1_name] + ... fs2 = locals()[fs2_name] + ... if hash(fs1) != hash(fs2): + ... assert fs1 != fs2 + ... else: + ... print('%s and %s have the same hash value,' % + ... (fs1_name, fs2_name)) + ... if fs1 == fs2: print('and are equal') + ... else: print('and are not equal') + a and a have the same hash value, and are equal + a and b have the same hash value, and are equal + b and a have the same hash value, and are equal + b and b have the same hash value, and are equal + c and c have the same hash value, and are equal + c and d have the same hash value, and are not equal + d and c have the same hash value, and are not equal + d and d have the same hash value, and are equal + e and e have the same hash value, and are equal + +.. + >>> del a, b, c, d, e, v # clean-up + +Tracing +------- + + >>> fs1 = FeatStruct('[a=[b=(1)[], c=?x], d->(1), e=[f=?x]]') + >>> fs2 = FeatStruct('[a=(1)[c="C"], e=[g->(1)]]') + >>> fs1.unify(fs2, trace=True) + + Unification trace: + / [a=[b=(1)[], c=?x], d->(1), e=[f=?x]] + |\ [a=(1)[c='C'], e=[g->(1)]] + | + | Unify feature: a + | / [b=[], c=?x] + | |\ [c='C'] + | | + | | Unify feature: a.c + | | / ?x + | | |\ 'C' + | | | + | | +-->Variable('?x') + | | + | +-->[b=[], c=?x] + | Bindings: {?x: 'C'} + | + | Unify feature: e + | / [f=?x] + | |\ [g=[c='C']] + | | + | +-->[f=?x, g=[b=[], c=?x]] + | Bindings: {?x: 'C'} + | + +-->[a=(1)[b=(2)[], c='C'], d->(2), e=[f='C', g->(1)]] + Bindings: {?x: 'C'} + [a=(1)[b=(2)[], c='C'], d->(2), e=[f='C', g->(1)]] + >>> + >>> fs1 = FeatStruct('[a=?x, b=?z, c=?z]') + >>> fs2 = FeatStruct('[a=?y, b=?y, c=?q]') + >>> #fs1.unify(fs2, trace=True) + >>> + +.. + >>> del fs1, fs2 # clean-up + +Unification on Dicts & Lists +---------------------------- +It's possible to do unification on dictionaries: + + >>> from nltk.featstruct import unify + >>> pprint(unify(dict(x=1, y=dict(z=2)), dict(x=1, q=5)), width=1) + {'q': 5, 'x': 1, 'y': {'z': 2}} + +It's possible to do unification on lists as well: + + >>> unify([1, 2, 3], [1, Variable('x'), 3]) + [1, 2, 3] + +Mixing dicts and lists is fine: + + >>> pprint(unify([dict(x=1, y=dict(z=2)),3], [dict(x=1, q=5),3]), + ... width=1) + [{'q': 5, 'x': 1, 'y': {'z': 2}}, 3] + +Mixing dicts and FeatStructs is discouraged: + + >>> unify(dict(x=1), FeatStruct(x=1)) + Traceback (most recent call last): + . . . + ValueError: Mixing FeatStruct objects with Python dicts and lists is not supported. + +But you can do it if you really want, by explicitly stating that both +dictionaries and FeatStructs should be treated as feature structures: + + >>> unify(dict(x=1), FeatStruct(x=1), fs_class=(dict, FeatStruct)) + {'x': 1} + +Finding Conflicts +----------------- + + >>> from nltk.featstruct import conflicts + >>> fs1 = FeatStruct('[a=[b=(1)[c=2], d->(1), e=[f->(1)]]]') + >>> fs2 = FeatStruct('[a=[b=[c=[x=5]], d=[c=2], e=[f=[c=3]]]]') + >>> for path in conflicts(fs1, fs2): + ... print('%-8s: %r vs %r' % ('.'.join(path), fs1[path], fs2[path])) + a.b.c : 2 vs [x=5] + a.e.f.c : 2 vs 3 + +.. + >>> del fs1, fs2 # clean-up + +Retracting Bindings +------------------- + + >>> from nltk.featstruct import retract_bindings + >>> bindings = {} + >>> fs1 = FeatStruct('[a=?x, b=[c=?y]]') + >>> fs2 = FeatStruct('[a=(1)[c=[d=1]], b->(1)]') + >>> fs3 = fs1.unify(fs2, bindings) + >>> print(fs3) + [ a = (1) [ c = [ d = 1 ] ] ] + [ ] + [ b -> (1) ] + >>> pprint(bindings) + {Variable('?x'): [c=[d=1]], Variable('?y'): [d=1]} + >>> retract_bindings(fs3, bindings) + [a=?x, b=?x] + >>> pprint(bindings) + {Variable('?x'): [c=?y], Variable('?y'): [d=1]} + +Squashed Bugs +~~~~~~~~~~~~~ +In svn rev 5167, unifying two feature structures that used the same +variable would cause those variables to become aliased in the output. + + >>> fs1 = FeatStruct('[a=?x]') + >>> fs2 = FeatStruct('[b=?x]') + >>> fs1.unify(fs2) + [a=?x, b=?x2] + +There was a bug in svn revision 5172 that caused `rename_variables` to +rename variables to names that are already used. + + >>> FeatStruct('[a=?x, b=?x2]').rename_variables( + ... vars=[Variable('?x')]) + [a=?x3, b=?x2] + >>> fs1 = FeatStruct('[a=?x]') + >>> fs2 = FeatStruct('[a=?x, b=?x2]') + >>> fs1.unify(fs2) + [a=?x, b=?x2] + +There was a bug in svn rev 5167 that caused us to get the following +example wrong. Basically the problem was that we only followed +'forward' pointers for other, not self, when unifying two feature +structures. (nb: this test assumes that features are unified in +alphabetical order -- if they are not, it might pass even if the bug +is present.) + + >>> fs1 = FeatStruct('[a=[x=1], b=?x, c=?x]') + >>> fs2 = FeatStruct('[a=(1)[], b->(1), c=[x=2]]') + >>> print(fs1.unify(fs2)) + None + +.. + >>> del fs1, fs2 # clean-up diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/framenet.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/framenet.doctest new file mode 100644 index 0000000..a71b234 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/framenet.doctest @@ -0,0 +1,288 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======== +FrameNet +======== + +The FrameNet corpus is a lexical database of English that is both human- +and machine-readable, based on annotating examples of how words are used +in actual texts. FrameNet is based on a theory of meaning called Frame +Semantics, deriving from the work of Charles J. Fillmore and colleagues. +The basic idea is straightforward: that the meanings of most words can +best be understood on the basis of a semantic frame: a description of a +type of event, relation, or entity and the participants in it. For +example, the concept of cooking typically involves a person doing the +cooking (Cook), the food that is to be cooked (Food), something to hold +the food while cooking (Container) and a source of heat +(Heating_instrument). In the FrameNet project, this is represented as a +frame called Apply_heat, and the Cook, Food, Heating_instrument and +Container are called frame elements (FEs). Words that evoke this frame, +such as fry, bake, boil, and broil, are called lexical units (LUs) of +the Apply_heat frame. The job of FrameNet is to define the frames +and to annotate sentences to show how the FEs fit syntactically around +the word that evokes the frame. + +------ +Frames +------ + +A Frame is a script-like conceptual structure that describes a +particular type of situation, object, or event along with the +participants and props that are needed for that Frame. For +example, the "Apply_heat" frame describes a common situation +involving a Cook, some Food, and a Heating_Instrument, and is +evoked by words such as bake, blanch, boil, broil, brown, +simmer, steam, etc. + +We call the roles of a Frame "frame elements" (FEs) and the +frame-evoking words are called "lexical units" (LUs). + +FrameNet includes relations between Frames. Several types of +relations are defined, of which the most important are: + +- Inheritance: An IS-A relation. The child frame is a subtype + of the parent frame, and each FE in the parent is bound to + a corresponding FE in the child. An example is the + "Revenge" frame which inherits from the + "Rewards_and_punishments" frame. + +- Using: The child frame presupposes the parent frame as + background, e.g the "Speed" frame "uses" (or presupposes) + the "Motion" frame; however, not all parent FEs need to be + bound to child FEs. + +- Subframe: The child frame is a subevent of a complex event + represented by the parent, e.g. the "Criminal_process" frame + has subframes of "Arrest", "Arraignment", "Trial", and + "Sentencing". + +- Perspective_on: The child frame provides a particular + perspective on an un-perspectivized parent frame. A pair of + examples consists of the "Hiring" and "Get_a_job" frames, + which perspectivize the "Employment_start" frame from the + Employer's and the Employee's point of view, respectively. + +To get a list of all of the Frames in FrameNet, you can use the +`frames()` function. If you supply a regular expression pattern to the +`frames()` function, you will get a list of all Frames whose names match +that pattern: + + >>> from pprint import pprint + >>> from operator import itemgetter + >>> from nltk.corpus import framenet as fn + >>> from nltk.corpus.reader.framenet import PrettyList + >>> x = fn.frames(r'(?i)crim') + >>> x.sort(key=itemgetter('ID')) + >>> x + [, , ...] + >>> PrettyList(sorted(x, key=itemgetter('ID'))) + [, , ...] + +To get the details of a particular Frame, you can use the `frame()` +function passing in the frame number: + + >>> from pprint import pprint + >>> from nltk.corpus import framenet as fn + >>> f = fn.frame(202) + >>> f.ID + 202 + >>> f.name + 'Arrest' + >>> f.definition + "Authorities charge a Suspect, who is under suspicion of having committed a crime..." + >>> len(f.lexUnit) + 11 + >>> pprint(sorted([x for x in f.FE])) + ['Authorities', + 'Charges', + 'Co-participant', + 'Manner', + 'Means', + 'Offense', + 'Place', + 'Purpose', + 'Source_of_legal_authority', + 'Suspect', + 'Time', + 'Type'] + >>> pprint(f.frameRelations) + [ Child=Arrest>, Component=Arrest>, ...] + +The `frame()` function shown above returns a dict object containing +detailed information about the Frame. See the documentation on the +`frame()` function for the specifics. + +You can also search for Frames by their Lexical Units (LUs). The +`frames_by_lemma()` function returns a list of all frames that contain +LUs in which the 'name' attribute of the LU matches the given regular +expression. Note that LU names are composed of "lemma.POS", where the +"lemma" part can be made up of either a single lexeme (e.g. 'run') or +multiple lexemes (e.g. 'a little') (see below). + + >>> PrettyList(sorted(fn.frames_by_lemma(r'(?i)a little'), key=itemgetter('ID'))) + [, ] + +------------- +Lexical Units +------------- + +A lexical unit (LU) is a pairing of a word with a meaning. For +example, the "Apply_heat" Frame describes a common situation +involving a Cook, some Food, and a Heating Instrument, and is +_evoked_ by words such as bake, blanch, boil, broil, brown, +simmer, steam, etc. These frame-evoking words are the LUs in the +Apply_heat frame. Each sense of a polysemous word is a different +LU. + +We have used the word "word" in talking about LUs. The reality +is actually rather complex. When we say that the word "bake" is +polysemous, we mean that the lemma "bake.v" (which has the +word-forms "bake", "bakes", "baked", and "baking") is linked to +three different frames: + +- Apply_heat: "Michelle baked the potatoes for 45 minutes." + +- Cooking_creation: "Michelle baked her mother a cake for her birthday." + +- Absorb_heat: "The potatoes have to bake for more than 30 minutes." + +These constitute three different LUs, with different +definitions. + +Multiword expressions such as "given name" and hyphenated words +like "shut-eye" can also be LUs. Idiomatic phrases such as +"middle of nowhere" and "give the slip (to)" are also defined as +LUs in the appropriate frames ("Isolated_places" and "Evading", +respectively), and their internal structure is not analyzed. + +Framenet provides multiple annotated examples of each sense of a +word (i.e. each LU). Moreover, the set of examples +(approximately 20 per LU) illustrates all of the combinatorial +possibilities of the lexical unit. + +Each LU is linked to a Frame, and hence to the other words which +evoke that Frame. This makes the FrameNet database similar to a +thesaurus, grouping together semantically similar words. + +In the simplest case, frame-evoking words are verbs such as +"fried" in: + + "Matilde fried the catfish in a heavy iron skillet." + +Sometimes event nouns may evoke a Frame. For example, +"reduction" evokes "Cause_change_of_scalar_position" in: + + "...the reduction of debt levels to $665 million from $2.6 billion." + +Adjectives may also evoke a Frame. For example, "asleep" may +evoke the "Sleep" frame as in: + + "They were asleep for hours." + +Many common nouns, such as artifacts like "hat" or "tower", +typically serve as dependents rather than clearly evoking their +own frames. + +Details for a specific lexical unit can be obtained using this class's +`lus()` function, which takes an optional regular expression +pattern that will be matched against the name of the lexical unit: + + >>> from pprint import pprint + >>> PrettyList(sorted(fn.lus(r'(?i)a little'), key=itemgetter('ID'))) + [, , ...] + +You can obtain detailed information on a particular LU by calling the +`lu()` function and passing in an LU's 'ID' number: + + >>> from pprint import pprint + >>> from nltk.corpus import framenet as fn + >>> fn.lu(256).name + 'foresee.v' + >>> fn.lu(256).definition + 'COD: be aware of beforehand; predict.' + >>> fn.lu(256).frame.name + 'Expectation' + >>> fn.lu(256).lexemes[0].name + 'foresee' + +Note that LU names take the form of a dotted string (e.g. "run.v" or "a +little.adv") in which a lemma precedes the "." and a part of speech +(POS) follows the dot. The lemma may be composed of a single lexeme +(e.g. "run") or of multiple lexemes (e.g. "a little"). The list of +POSs used in the LUs is: + +v - verb +n - noun +a - adjective +adv - adverb +prep - preposition +num - numbers +intj - interjection +art - article +c - conjunction +scon - subordinating conjunction + +For more detailed information about the info that is contained in the +dict that is returned by the `lu()` function, see the documentation on +the `lu()` function. + +------------------- +Annotated Documents +------------------- + +The FrameNet corpus contains a small set of annotated documents. A list +of these documents can be obtained by calling the `docs()` function: + + >>> from pprint import pprint + >>> from nltk.corpus import framenet as fn + >>> d = fn.docs('BellRinging')[0] + >>> d.corpname + 'PropBank' + >>> d.sentence[49] + full-text sentence (...) in BellRinging: + + + [POS] 17 tags + + [POS_tagset] PENN + + [text] + [annotationSet] + + `` I live in hopes that the ringers themselves will be drawn into + ***** ******* ***** + Desir Cause_t Cause + [1] [3] [2] + + that fuller life . + ****** + Comple + [4] + (Desir=Desiring, Cause_t=Cause_to_make_noise, Cause=Cause_motion, Comple=Completeness) + + + >>> d.sentence[49].annotationSet[1] + annotation set (...): + + [status] MANUAL + + [LU] (6605) hope.n in Desiring + + [frame] (366) Desiring + + [GF] 2 relations + + [PT] 2 phrases + + [text] + [Target] + [FE] + [Noun] + + `` I live in hopes that the ringers themselves will be drawn into + - ^^^^ ^^ ***** ---------------------------------------------- + E supp su Event + + that fuller life . + ----------------- + + (E=Experiencer, su=supp) + + diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/generate.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/generate.doctest new file mode 100644 index 0000000..20ba37d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/generate.doctest @@ -0,0 +1,78 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=============================================== +Generating sentences from context-free grammars +=============================================== + +An example grammar: + + >>> from nltk.parse.generate import generate, demo_grammar + >>> from nltk import CFG + >>> grammar = CFG.fromstring(demo_grammar) + >>> print(grammar) + Grammar with 13 productions (start state = S) + S -> NP VP + NP -> Det N + PP -> P NP + VP -> 'slept' + VP -> 'saw' NP + VP -> 'walked' PP + Det -> 'the' + Det -> 'a' + N -> 'man' + N -> 'park' + N -> 'dog' + P -> 'in' + P -> 'with' + +The first 10 generated sentences: + + >>> for sentence in generate(grammar, n=10): + ... print(' '.join(sentence)) + the man slept + the man saw the man + the man saw the park + the man saw the dog + the man saw a man + the man saw a park + the man saw a dog + the man walked in the man + the man walked in the park + the man walked in the dog + +All sentences of max depth 4: + + >>> for sentence in generate(grammar, depth=4): + ... print(' '.join(sentence)) + the man slept + the park slept + the dog slept + a man slept + a park slept + a dog slept + +The number of sentences of different max depths: + + >>> len(list(generate(grammar, depth=3))) + 0 + >>> len(list(generate(grammar, depth=4))) + 6 + >>> len(list(generate(grammar, depth=5))) + 42 + >>> len(list(generate(grammar, depth=6))) + 114 + >>> len(list(generate(grammar))) + 114 + +Infinite grammars will throw a RecursionError when not bounded by some ``depth``: + + >>> grammar = CFG.fromstring(""" + ... S -> A B + ... A -> B + ... B -> "b" | A + ... """) + >>> list(generate(grammar)) + Traceback (most recent call last): + ... + RuntimeError: The grammar has rule(s) that yield infinite recursion! diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim.doctest new file mode 100644 index 0000000..bd2cb45 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim.doctest @@ -0,0 +1,141 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======================================= +Demonstrate word embedding using Gensim +======================================= + + >>> from nltk.test.gensim_fixt import setup_module + >>> setup_module() + +We demonstrate three functions: +- Train the word embeddings using brown corpus; +- Load the pre-trained model and perform simple tasks; and +- Pruning the pre-trained binary model. + + >>> import gensim + +--------------- +Train the model +--------------- + +Here we train a word embedding using the Brown Corpus: + + >>> from nltk.corpus import brown + >>> train_set = brown.sents()[:10000] + >>> model = gensim.models.Word2Vec(train_set) + +It might take some time to train the model. So, after it is trained, it can be saved as follows: + + >>> model.save('brown.embedding') + >>> new_model = gensim.models.Word2Vec.load('brown.embedding') + +The model will be the list of words with their embedding. We can easily get the vector representation of a word. + + >>> len(new_model.wv['university']) + 100 + +There are some supporting functions already implemented in Gensim to manipulate with word embeddings. +For example, to compute the cosine similarity between 2 words: + + >>> new_model.wv.similarity('university','school') > 0.3 + True + +--------------------------- +Using the pre-trained model +--------------------------- + +NLTK includes a pre-trained model which is part of a model that is trained on 100 billion words from the Google News Dataset. +The full model is from https://code.google.com/p/word2vec/ (about 3 GB). + + >>> from nltk.data import find + >>> word2vec_sample = str(find('models/word2vec_sample/pruned.word2vec.txt')) + >>> model = gensim.models.KeyedVectors.load_word2vec_format(word2vec_sample, binary=False) + +We pruned the model to only include the most common words (~44k words). + + >>> len(model) + 43981 + +Each word is represented in the space of 300 dimensions: + + >>> len(model['university']) + 300 + +Finding the top n words that are similar to a target word is simple. The result is the list of n words with the score. + + >>> model.most_similar(positive=['university'], topn = 3) + [('universities', 0.70039...), ('faculty', 0.67809...), ('undergraduate', 0.65870...)] + +Finding a word that is not in a list is also supported, although, implementing this by yourself is simple. + + >>> model.doesnt_match('breakfast cereal dinner lunch'.split()) + 'cereal' + +Mikolov et al. (2013) figured out that word embedding captures much of syntactic and semantic regularities. For example, +the vector 'King - Man + Woman' is close to 'Queen' and 'Germany - Berlin + Paris' is close to 'France'. + + >>> model.most_similar(positive=['woman','king'], negative=['man'], topn = 1) + [('queen', 0.71181...)] + + >>> model.most_similar(positive=['Paris','Germany'], negative=['Berlin'], topn = 1) + [('France', 0.78840...)] + +We can visualize the word embeddings using t-SNE (https://lvdmaaten.github.io/tsne/). For this demonstration, we visualize the first 1000 words. + +| import numpy as np +| labels = [] +| count = 0 +| max_count = 1000 +| X = np.zeros(shape=(max_count,len(model['university']))) +| +| for term in model.index_to_key: +| X[count] = model[term] +| labels.append(term) +| count+= 1 +| if count >= max_count: break +| +| # It is recommended to use PCA first to reduce to ~50 dimensions +| from sklearn.decomposition import PCA +| pca = PCA(n_components=50) +| X_50 = pca.fit_transform(X) +| +| # Using TSNE to further reduce to 2 dimensions +| from sklearn.manifold import TSNE +| model_tsne = TSNE(n_components=2, random_state=0) +| Y = model_tsne.fit_transform(X_50) +| +| # Show the scatter plot +| import matplotlib.pyplot as plt +| plt.scatter(Y[:,0], Y[:,1], 20) +| +| # Add labels +| for label, x, y in zip(labels, Y[:, 0], Y[:, 1]): +| plt.annotate(label, xy = (x,y), xytext = (0, 0), textcoords = 'offset points', size = 10) +| +| plt.show() + +------------------------------ +Prune the trained binary model +------------------------------ + +Here is the supporting code to extract part of the binary model (GoogleNews-vectors-negative300.bin.gz) from https://code.google.com/p/word2vec/ +We use this code to get the `word2vec_sample` model. + +| import gensim +| # Load the binary model +| model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin.gz', binary = True) +| +| # Only output word that appear in the Brown corpus +| from nltk.corpus import brown +| words = set(brown.words()) +| print(len(words)) +| +| # Output presented word to a temporary file +| out_file = 'pruned.word2vec.txt' +| with open(out_file,'w') as f: +| word_presented = words.intersection(model.index_to_key) +| f.write('{} {}\n'.format(len(word_presented),len(model['word']))) +| +| for word in word_presented: +| f.write('{} {}\n'.format(word, ' '.join(str(value) for value in model[word]))) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim_fixt.py new file mode 100644 index 0000000..c0f1870 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gensim_fixt.py @@ -0,0 +1,4 @@ +def setup_module(): + import pytest + + pytest.importorskip("gensim") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics.doctest new file mode 100644 index 0000000..51b5872 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics.doctest @@ -0,0 +1,383 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============================================================================== + Glue Semantics +============================================================================== + + + +====================== +Linear logic +====================== + + >>> from nltk.sem import logic + >>> from nltk.sem.glue import * + >>> from nltk.sem.linearlogic import * + + >>> from nltk.sem.linearlogic import Expression + >>> read_expr = Expression.fromstring + +Parser + + >>> print(read_expr(r'f')) + f + >>> print(read_expr(r'(g -o f)')) + (g -o f) + >>> print(read_expr(r'(g -o (h -o f))')) + (g -o (h -o f)) + >>> print(read_expr(r'((g -o G) -o G)')) + ((g -o G) -o G) + >>> print(read_expr(r'(g -o f)(g)')) + (g -o f)(g) + >>> print(read_expr(r'((g -o G) -o G)((g -o f))')) + ((g -o G) -o G)((g -o f)) + +Simplify + + >>> print(read_expr(r'f').simplify()) + f + >>> print(read_expr(r'(g -o f)').simplify()) + (g -o f) + >>> print(read_expr(r'((g -o G) -o G)').simplify()) + ((g -o G) -o G) + >>> print(read_expr(r'(g -o f)(g)').simplify()) + f + >>> try: read_expr(r'(g -o f)(f)').simplify() + ... except LinearLogicApplicationException as e: print(e) + ... + Cannot apply (g -o f) to f. Cannot unify g with f given {} + >>> print(read_expr(r'(G -o f)(g)').simplify()) + f + >>> print(read_expr(r'((g -o G) -o G)((g -o f))').simplify()) + f + +Test BindingDict + + >>> h = ConstantExpression('h') + >>> g = ConstantExpression('g') + >>> f = ConstantExpression('f') + + >>> H = VariableExpression('H') + >>> G = VariableExpression('G') + >>> F = VariableExpression('F') + + >>> d1 = BindingDict({H: h}) + >>> d2 = BindingDict({F: f, G: F}) + >>> d12 = d1 + d2 + >>> all12 = ['%s: %s' % (v, d12[v]) for v in d12.d] + >>> all12.sort() + >>> print(all12) + ['F: f', 'G: f', 'H: h'] + + >>> BindingDict([(F,f),(G,g),(H,h)]) == BindingDict({F:f, G:g, H:h}) + True + + >>> d4 = BindingDict({F: f}) + >>> try: d4[F] = g + ... except VariableBindingException as e: print(e) + Variable F already bound to another value + +Test Unify + + >>> try: f.unify(g, BindingDict()) + ... except UnificationException as e: print(e) + ... + Cannot unify f with g given {} + + >>> f.unify(G, BindingDict()) == BindingDict({G: f}) + True + >>> try: f.unify(G, BindingDict({G: h})) + ... except UnificationException as e: print(e) + ... + Cannot unify f with G given {G: h} + >>> f.unify(G, BindingDict({G: f})) == BindingDict({G: f}) + True + >>> f.unify(G, BindingDict({H: f})) == BindingDict({G: f, H: f}) + True + + >>> G.unify(f, BindingDict()) == BindingDict({G: f}) + True + >>> try: G.unify(f, BindingDict({G: h})) + ... except UnificationException as e: print(e) + ... + Cannot unify G with f given {G: h} + >>> G.unify(f, BindingDict({G: f})) == BindingDict({G: f}) + True + >>> G.unify(f, BindingDict({H: f})) == BindingDict({G: f, H: f}) + True + + >>> G.unify(F, BindingDict()) == BindingDict({G: F}) + True + >>> try: G.unify(F, BindingDict({G: H})) + ... except UnificationException as e: print(e) + ... + Cannot unify G with F given {G: H} + >>> G.unify(F, BindingDict({G: F})) == BindingDict({G: F}) + True + >>> G.unify(F, BindingDict({H: F})) == BindingDict({G: F, H: F}) + True + +Test Compile + + >>> print(read_expr('g').compile_pos(Counter(), GlueFormula)) + (, []) + >>> print(read_expr('(g -o f)').compile_pos(Counter(), GlueFormula)) + (, []) + >>> print(read_expr('(g -o (h -o f))').compile_pos(Counter(), GlueFormula)) + (, []) + + +====================== +Glue +====================== + +Demo of "John walks" +-------------------- + + >>> john = GlueFormula("John", "g") + >>> print(john) + John : g + >>> walks = GlueFormula(r"\x.walks(x)", "(g -o f)") + >>> print(walks) + \x.walks(x) : (g -o f) + >>> print(walks.applyto(john)) + \x.walks(x)(John) : (g -o f)(g) + >>> print(walks.applyto(john).simplify()) + walks(John) : f + + +Demo of "A dog walks" +--------------------- + + >>> a = GlueFormula("\\P Q.some x.(P(x) and Q(x))", "((gv -o gr) -o ((g -o G) -o G))") + >>> print(a) + \P Q.exists x.(P(x) & Q(x)) : ((gv -o gr) -o ((g -o G) -o G)) + >>> man = GlueFormula(r"\x.man(x)", "(gv -o gr)") + >>> print(man) + \x.man(x) : (gv -o gr) + >>> walks = GlueFormula(r"\x.walks(x)", "(g -o f)") + >>> print(walks) + \x.walks(x) : (g -o f) + >>> a_man = a.applyto(man) + >>> print(a_man.simplify()) + \Q.exists x.(man(x) & Q(x)) : ((g -o G) -o G) + >>> a_man_walks = a_man.applyto(walks) + >>> print(a_man_walks.simplify()) + exists x.(man(x) & walks(x)) : f + + +Demo of 'every girl chases a dog' +--------------------------------- + +Individual words: + + >>> every = GlueFormula("\\P Q.all x.(P(x) -> Q(x))", "((gv -o gr) -o ((g -o G) -o G))") + >>> print(every) + \P Q.all x.(P(x) -> Q(x)) : ((gv -o gr) -o ((g -o G) -o G)) + >>> girl = GlueFormula(r"\x.girl(x)", "(gv -o gr)") + >>> print(girl) + \x.girl(x) : (gv -o gr) + >>> chases = GlueFormula(r"\x y.chases(x,y)", "(g -o (h -o f))") + >>> print(chases) + \x y.chases(x,y) : (g -o (h -o f)) + >>> a = GlueFormula("\\P Q.some x.(P(x) and Q(x))", "((hv -o hr) -o ((h -o H) -o H))") + >>> print(a) + \P Q.exists x.(P(x) & Q(x)) : ((hv -o hr) -o ((h -o H) -o H)) + >>> dog = GlueFormula(r"\x.dog(x)", "(hv -o hr)") + >>> print(dog) + \x.dog(x) : (hv -o hr) + +Noun Quantification can only be done one way: + + >>> every_girl = every.applyto(girl) + >>> print(every_girl.simplify()) + \Q.all x.(girl(x) -> Q(x)) : ((g -o G) -o G) + >>> a_dog = a.applyto(dog) + >>> print(a_dog.simplify()) + \Q.exists x.(dog(x) & Q(x)) : ((h -o H) -o H) + +The first reading is achieved by combining 'chases' with 'a dog' first. +Since 'a girl' requires something of the form '(h -o H)' we must +get rid of the 'g' in the glue of 'see'. We will do this with +the '-o elimination' rule. So, x1 will be our subject placeholder. + + >>> xPrime = GlueFormula("x1", "g") + >>> print(xPrime) + x1 : g + >>> xPrime_chases = chases.applyto(xPrime) + >>> print(xPrime_chases.simplify()) + \y.chases(x1,y) : (h -o f) + >>> xPrime_chases_a_dog = a_dog.applyto(xPrime_chases) + >>> print(xPrime_chases_a_dog.simplify()) + exists x.(dog(x) & chases(x1,x)) : f + +Now we can retract our subject placeholder using lambda-abstraction and +combine with the true subject. + + >>> chases_a_dog = xPrime_chases_a_dog.lambda_abstract(xPrime) + >>> print(chases_a_dog.simplify()) + \x1.exists x.(dog(x) & chases(x1,x)) : (g -o f) + >>> every_girl_chases_a_dog = every_girl.applyto(chases_a_dog) + >>> r1 = every_girl_chases_a_dog.simplify() + >>> r2 = GlueFormula(r'all x.(girl(x) -> exists z1.(dog(z1) & chases(x,z1)))', 'f') + >>> r1 == r2 + True + +The second reading is achieved by combining 'every girl' with 'chases' first. + + >>> xPrime = GlueFormula("x1", "g") + >>> print(xPrime) + x1 : g + >>> xPrime_chases = chases.applyto(xPrime) + >>> print(xPrime_chases.simplify()) + \y.chases(x1,y) : (h -o f) + >>> yPrime = GlueFormula("x2", "h") + >>> print(yPrime) + x2 : h + >>> xPrime_chases_yPrime = xPrime_chases.applyto(yPrime) + >>> print(xPrime_chases_yPrime.simplify()) + chases(x1,x2) : f + >>> chases_yPrime = xPrime_chases_yPrime.lambda_abstract(xPrime) + >>> print(chases_yPrime.simplify()) + \x1.chases(x1,x2) : (g -o f) + >>> every_girl_chases_yPrime = every_girl.applyto(chases_yPrime) + >>> print(every_girl_chases_yPrime.simplify()) + all x.(girl(x) -> chases(x,x2)) : f + >>> every_girl_chases = every_girl_chases_yPrime.lambda_abstract(yPrime) + >>> print(every_girl_chases.simplify()) + \x2.all x.(girl(x) -> chases(x,x2)) : (h -o f) + >>> every_girl_chases_a_dog = a_dog.applyto(every_girl_chases) + >>> r1 = every_girl_chases_a_dog.simplify() + >>> r2 = GlueFormula(r'exists x.(dog(x) & all z2.(girl(z2) -> chases(z2,x)))', 'f') + >>> r1 == r2 + True + + +Compilation +----------- + + >>> for cp in GlueFormula('m', '(b -o a)').compile(Counter()): print(cp) + m : (b -o a) : {1} + >>> for cp in GlueFormula('m', '((c -o b) -o a)').compile(Counter()): print(cp) + v1 : c : {1} + m : (b[1] -o a) : {2} + >>> for cp in GlueFormula('m', '((d -o (c -o b)) -o a)').compile(Counter()): print(cp) + v1 : c : {1} + v2 : d : {2} + m : (b[1, 2] -o a) : {3} + >>> for cp in GlueFormula('m', '((d -o e) -o ((c -o b) -o a))').compile(Counter()): print(cp) + v1 : d : {1} + v2 : c : {2} + m : (e[1] -o (b[2] -o a)) : {3} + >>> for cp in GlueFormula('m', '(((d -o c) -o b) -o a)').compile(Counter()): print(cp) + v1 : (d -o c) : {1} + m : (b[1] -o a) : {2} + >>> for cp in GlueFormula('m', '((((e -o d) -o c) -o b) -o a)').compile(Counter()): print(cp) + v1 : e : {1} + v2 : (d[1] -o c) : {2} + m : (b[2] -o a) : {3} + + +Demo of 'a man walks' using Compilation +--------------------------------------- + +Premises + + >>> a = GlueFormula('\\P Q.some x.(P(x) and Q(x))', '((gv -o gr) -o ((g -o G) -o G))') + >>> print(a) + \P Q.exists x.(P(x) & Q(x)) : ((gv -o gr) -o ((g -o G) -o G)) + + >>> man = GlueFormula('\\x.man(x)', '(gv -o gr)') + >>> print(man) + \x.man(x) : (gv -o gr) + + >>> walks = GlueFormula('\\x.walks(x)', '(g -o f)') + >>> print(walks) + \x.walks(x) : (g -o f) + +Compiled Premises: + + >>> counter = Counter() + >>> ahc = a.compile(counter) + >>> g1 = ahc[0] + >>> print(g1) + v1 : gv : {1} + >>> g2 = ahc[1] + >>> print(g2) + v2 : g : {2} + >>> g3 = ahc[2] + >>> print(g3) + \P Q.exists x.(P(x) & Q(x)) : (gr[1] -o (G[2] -o G)) : {3} + >>> g4 = man.compile(counter)[0] + >>> print(g4) + \x.man(x) : (gv -o gr) : {4} + >>> g5 = walks.compile(counter)[0] + >>> print(g5) + \x.walks(x) : (g -o f) : {5} + +Derivation: + + >>> g14 = g4.applyto(g1) + >>> print(g14.simplify()) + man(v1) : gr : {1, 4} + >>> g134 = g3.applyto(g14) + >>> print(g134.simplify()) + \Q.exists x.(man(x) & Q(x)) : (G[2] -o G) : {1, 3, 4} + >>> g25 = g5.applyto(g2) + >>> print(g25.simplify()) + walks(v2) : f : {2, 5} + >>> g12345 = g134.applyto(g25) + >>> print(g12345.simplify()) + exists x.(man(x) & walks(x)) : f : {1, 2, 3, 4, 5} + +--------------------------------- +Dependency Graph to Glue Formulas +--------------------------------- + >>> from nltk.corpus.reader.dependency import DependencyGraph + + >>> depgraph = DependencyGraph("""1 John _ NNP NNP _ 2 SUBJ _ _ + ... 2 sees _ VB VB _ 0 ROOT _ _ + ... 3 a _ ex_quant ex_quant _ 4 SPEC _ _ + ... 4 dog _ NN NN _ 2 OBJ _ _ + ... """) + >>> gfl = GlueDict('nltk:grammars/sample_grammars/glue.semtype').to_glueformula_list(depgraph) + >>> print(gfl) # doctest: +SKIP + [\x y.sees(x,y) : (f -o (i -o g)), + \x.dog(x) : (iv -o ir), + \P Q.exists x.(P(x) & Q(x)) : ((iv -o ir) -o ((i -o I3) -o I3)), + \P Q.exists x.(P(x) & Q(x)) : ((fv -o fr) -o ((f -o F4) -o F4)), + \x.John(x) : (fv -o fr)] + >>> glue = Glue() + >>> for r in sorted([r.simplify().normalize() for r in glue.get_readings(glue.gfl_to_compiled(gfl))], key=str): + ... print(r) + exists z1.(John(z1) & exists z2.(dog(z2) & sees(z1,z2))) + exists z1.(dog(z1) & exists z2.(John(z2) & sees(z2,z1))) + +----------------------------------- +Dependency Graph to LFG f-structure +----------------------------------- + >>> from nltk.sem.lfg import FStructure + + >>> fstruct = FStructure.read_depgraph(depgraph) + + >>> print(fstruct) # doctest: +SKIP + f:[pred 'sees' + obj h:[pred 'dog' + spec 'a'] + subj g:[pred 'John']] + + >>> fstruct.to_depgraph().tree().pprint() + (sees (dog a) John) + +--------------------------------- +LFG f-structure to Glue +--------------------------------- + >>> fstruct.to_glueformula_list(GlueDict('nltk:grammars/sample_grammars/glue.semtype')) # doctest: +SKIP + [\x y.sees(x,y) : (i -o (g -o f)), + \x.dog(x) : (gv -o gr), + \P Q.exists x.(P(x) & Q(x)) : ((gv -o gr) -o ((g -o G3) -o G3)), + \P Q.exists x.(P(x) & Q(x)) : ((iv -o ir) -o ((i -o I4) -o I4)), + \x.John(x) : (iv -o ir)] + +.. see gluesemantics_malt.doctest for more diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt.doctest new file mode 100644 index 0000000..dc7e3d6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt.doctest @@ -0,0 +1,69 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. see also: gluesemantics.doctest + +============================================================================== + Glue Semantics +============================================================================== + + >>> from nltk.test.gluesemantics_malt_fixt import setup_module + >>> setup_module() + + >>> from nltk.sem.glue import * + >>> nltk.sem.logic._counter._value = 0 + +-------------------------------- +Initialize the Dependency Parser +-------------------------------- + >>> from nltk.parse.malt import MaltParser + + >>> tagger = RegexpTagger( + ... [('^(John|Mary)$', 'NNP'), + ... ('^(sees|chases)$', 'VB'), + ... ('^(a)$', 'ex_quant'), + ... ('^(every)$', 'univ_quant'), + ... ('^(girl|dog)$', 'NN') + ... ]).tag + >>> depparser = MaltParser(tagger=tagger) + +-------------------- +Automated Derivation +-------------------- + >>> glue = Glue(depparser=depparser) + >>> readings = glue.parse_to_meaning('every girl chases a dog'.split()) + >>> for reading in sorted([r.simplify().normalize() for r in readings], key=str): + ... print(reading.normalize()) + all z1.(girl(z1) -> exists z2.(dog(z2) & chases(z1,z2))) + exists z1.(dog(z1) & all z2.(girl(z2) -> chases(z2,z1))) + + >>> drtglue = DrtGlue(depparser=depparser) + >>> readings = drtglue.parse_to_meaning('every girl chases a dog'.split()) + >>> for reading in sorted([r.simplify().normalize() for r in readings], key=str): + ... print(reading) + ([],[(([z1],[girl(z1)]) -> ([z2],[dog(z2), chases(z1,z2)]))]) + ([z1],[dog(z1), (([z2],[girl(z2)]) -> ([],[chases(z2,z1)]))]) + +-------------- +With inference +-------------- + +Checking for equality of two DRSs is very useful when generating readings of a sentence. +For example, the ``glue`` module generates two readings for the sentence +*John sees Mary*: + + >>> from nltk.sem.glue import DrtGlue + >>> readings = drtglue.parse_to_meaning('John sees Mary'.split()) + >>> for drs in sorted([r.simplify().normalize() for r in readings], key=str): + ... print(drs) + ([z1,z2],[John(z1), Mary(z2), sees(z1,z2)]) + ([z1,z2],[Mary(z1), John(z2), sees(z2,z1)]) + +However, it is easy to tell that these two readings are logically the +same, and therefore one of them is superfluous. We can use the theorem prover +to determine this equivalence, and then delete one of them. A particular +theorem prover may be specified, or the argument may be left off to use the +default. + + >>> readings[0].equiv(readings[1]) + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt_fixt.py new file mode 100644 index 0000000..b802641 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/gluesemantics_malt_fixt.py @@ -0,0 +1,9 @@ +def setup_module(): + import pytest + + from nltk.parse.malt import MaltParser + + try: + depparser = MaltParser() + except (AssertionError, LookupError) as e: + pytest.skip("MaltParser is not available") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammar.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammar.doctest new file mode 100644 index 0000000..ffe427b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammar.doctest @@ -0,0 +1,69 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=============== +Grammar Parsing +=============== + +Grammars can be parsed from strings: + + >>> from nltk import CFG + >>> grammar = CFG.fromstring(""" + ... S -> NP VP + ... PP -> P NP + ... NP -> Det N | NP PP + ... VP -> V NP | VP PP + ... Det -> 'a' | 'the' + ... N -> 'dog' | 'cat' + ... V -> 'chased' | 'sat' + ... P -> 'on' | 'in' + ... """) + >>> grammar + + >>> grammar.start() + S + >>> grammar.productions() + [S -> NP VP, PP -> P NP, NP -> Det N, NP -> NP PP, VP -> V NP, VP -> VP PP, + Det -> 'a', Det -> 'the', N -> 'dog', N -> 'cat', V -> 'chased', V -> 'sat', + P -> 'on', P -> 'in'] + +Probabilistic CFGs: + + >>> from nltk import PCFG + >>> toy_pcfg1 = PCFG.fromstring(""" + ... S -> NP VP [1.0] + ... NP -> Det N [0.5] | NP PP [0.25] | 'John' [0.1] | 'I' [0.15] + ... Det -> 'the' [0.8] | 'my' [0.2] + ... N -> 'man' [0.5] | 'telescope' [0.5] + ... VP -> VP PP [0.1] | V NP [0.7] | V [0.2] + ... V -> 'ate' [0.35] | 'saw' [0.65] + ... PP -> P NP [1.0] + ... P -> 'with' [0.61] | 'under' [0.39] + ... """) + +Chomsky Normal Form grammar (Test for bug 474) + + >>> g = CFG.fromstring("VP^ -> VBP NP^") + >>> g.productions()[0].lhs() + VP^ + +Grammars can contain both empty strings and empty productions: + + >>> from nltk.grammar import CFG + >>> from nltk.parse.generate import generate + >>> grammar = CFG.fromstring(""" + ... S -> A B + ... A -> 'a' + ... # An empty string: + ... B -> 'b' | '' + ... """) + >>> list(generate(grammar)) + [['a', 'b'], ['a', '']] + >>> grammar = CFG.fromstring(""" + ... S -> A B + ... A -> 'a' + ... # An empty production: + ... B -> 'b' | + ... """) + >>> list(generate(grammar)) + [['a', 'b'], ['a']] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammartestsuites.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammartestsuites.doctest new file mode 100644 index 0000000..c18c46a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/grammartestsuites.doctest @@ -0,0 +1,109 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================== + Test Suites for Grammars +========================== + +Sentences in the test suite are divided into two classes: + +- grammatical (*accept*) and +- ungrammatical (*reject*). + +If a sentence should parse according to the grammar, the value of +``trees`` will be a non-empty list. If a sentence should be rejected +according to the grammar, then the value of ``trees`` will be ``None``. + + >>> from nltk.parse import TestGrammar + >>> germantest1 = {} + >>> germantest1['doc'] = "Tests for person agreement" + >>> germantest1['accept'] = [ + ... 'ich komme', + ... 'ich sehe mich', + ... 'du kommst', + ... 'du siehst mich', + ... 'sie kommt', + ... 'sie sieht mich', + ... 'ihr kommt', + ... 'wir kommen', + ... 'sie kommen', + ... 'du magst mich', + ... 'er mag mich', + ... 'du folgst mir', + ... 'sie hilft mir', + ... ] + >>> germantest1['reject'] = [ + ... 'ich kommt', + ... 'ich kommst', + ... 'ich siehst mich', + ... 'du komme', + ... 'du sehe mich', + ... 'du kommt', + ... 'er komme', + ... 'er siehst mich', + ... 'wir komme', + ... 'wir kommst', + ... 'die Katzen kommst', + ... 'sie komme', + ... 'sie kommst', + ... 'du mag mich', + ... 'er magst mich', + ... 'du folgt mir', + ... 'sie hilfst mir', + ... ] + >>> germantest2 = {} + >>> germantest2['doc'] = "Tests for number agreement" + >>> germantest2['accept'] = [ + ... 'der Hund kommt', + ... 'die Hunde kommen', + ... 'ich komme', + ... 'wir kommen', + ... 'ich sehe die Katzen', + ... 'ich folge den Katzen', + ... 'ich sehe die Katzen', + ... 'ich folge den Katzen', + ... 'wir sehen die Katzen', + ... 'wir folgen den Katzen' + ... ] + >>> germantest2['reject'] = [ + ... 'ich kommen', + ... 'wir komme', + ... 'der Hunde kommt', + ... 'der Hunde kommen', + ... 'die Katzen kommt', + ... 'ich sehe der Hunde', + ... 'ich folge den Hund', + ... 'ich sehen der Hunde', + ... 'ich folgen den Hund', + ... 'wir sehe die Katzen', + ... 'wir folge den Katzen' + ... ] + >>> germantest3 = {} + >>> germantest3['doc'] = "Tests for case government and subcategorization" + >>> germantest3['accept'] = [ + ... 'der Hund sieht mich', + ... 'der Hund kommt', + ... 'ich sehe den Hund', + ... 'ich helfe dem Hund', + ... ] + >>> germantest3['reject'] = [ + ... 'ich sehe', + ... 'ich helfe', + ... 'ich komme den Hund', + ... 'ich sehe den Hund die Katzen', + ... 'du hilfst mich', + ... 'du siehst mir', + ... 'du siehst ich', + ... 'der Hunde kommt mich', + ... 'die Hunde sehe die Hunde', + ... 'der Hund sehe die Hunde', + ... 'ich hilft den Hund', + ... 'ich hilft der Hund', + ... 'ich sehe dem Hund', + ... ] + >>> germantestsuites = [germantest1, germantest2, germantest3] + >>> tester = TestGrammar('grammars/book_grammars/german.fcfg', germantestsuites) + >>> tester.run() + Tests for person agreement: All tests passed! + Tests for number agreement: All tests passed! + Tests for case government and subcategorization: All tests passed! diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/index.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/index.doctest new file mode 100644 index 0000000..2229e18 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/index.doctest @@ -0,0 +1,100 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. _align howto: align.html +.. _ccg howto: ccg.html +.. _chat80 howto: chat80.html +.. _childes howto: childes.html +.. _chunk howto: chunk.html +.. _classify howto: classify.html +.. _collocations howto: collocations.html +.. _compat howto: compat.html +.. _corpus howto: corpus.html +.. _data howto: data.html +.. _dependency howto: dependency.html +.. _discourse howto: discourse.html +.. _drt howto: drt.html +.. _featgram howto: featgram.html +.. _featstruct howto: featstruct.html +.. _framenet howto: framenet.html +.. _generate howto: generate.html +.. _gluesemantics howto: gluesemantics.html +.. _gluesemantics_malt howto: gluesemantics_malt.html +.. _grammar howto: grammar.html +.. _grammartestsuites howto: grammartestsuites.html +.. _index howto: index.html +.. _inference howto: inference.html +.. _internals howto: internals.html +.. _japanese howto: japanese.html +.. _logic howto: logic.html +.. _metrics howto: metrics.html +.. _misc howto: misc.html +.. _nonmonotonic howto: nonmonotonic.html +.. _parse howto: parse.html +.. _portuguese_en howto: portuguese_en.html +.. _probability howto: probability.html +.. _propbank howto: propbank.html +.. _relextract howto: relextract.html +.. _resolution howto: resolution.html +.. _semantics howto: semantics.html +.. _simple howto: simple.html +.. _stem howto: stem.html +.. _tag howto: tag.html +.. _tokenize howto: tokenize.html +.. _toolbox howto: toolbox.html +.. _tree howto: tree.html +.. _treetransforms howto: treetransforms.html +.. _util howto: util.html +.. _wordnet howto: wordnet.html +.. _wordnet_lch howto: wordnet_lch.html + +=========== +NLTK HOWTOs +=========== + +* `align HOWTO`_ +* `ccg HOWTO`_ +* `chat80 HOWTO`_ +* `childes HOWTO`_ +* `chunk HOWTO`_ +* `classify HOWTO`_ +* `collocations HOWTO`_ +* `compat HOWTO`_ +* `corpus HOWTO`_ +* `data HOWTO`_ +* `dependency HOWTO`_ +* `discourse HOWTO`_ +* `drt HOWTO`_ +* `featgram HOWTO`_ +* `featstruct HOWTO`_ +* `framenet HOWTO`_ +* `generate HOWTO`_ +* `gluesemantics HOWTO`_ +* `gluesemantics_malt HOWTO`_ +* `grammar HOWTO`_ +* `grammartestsuites HOWTO`_ +* `index HOWTO`_ +* `inference HOWTO`_ +* `internals HOWTO`_ +* `japanese HOWTO`_ +* `logic HOWTO`_ +* `metrics HOWTO`_ +* `misc HOWTO`_ +* `nonmonotonic HOWTO`_ +* `parse HOWTO`_ +* `portuguese_en HOWTO`_ +* `probability HOWTO`_ +* `propbank HOWTO`_ +* `relextract HOWTO`_ +* `resolution HOWTO`_ +* `semantics HOWTO`_ +* `simple HOWTO`_ +* `stem HOWTO`_ +* `tag HOWTO`_ +* `tokenize HOWTO`_ +* `toolbox HOWTO`_ +* `tree HOWTO`_ +* `treetransforms HOWTO`_ +* `util HOWTO`_ +* `wordnet HOWTO`_ +* `wordnet_lch HOWTO`_ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/inference.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/inference.doctest new file mode 100644 index 0000000..ff7eb21 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/inference.doctest @@ -0,0 +1,536 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +==================================== +Logical Inference and Model Building +==================================== + + >>> from nltk.test.setup_fixt import check_binary + >>> check_binary('mace4') + + >>> from nltk import * + >>> from nltk.sem.drt import DrtParser + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + +------------ +Introduction +------------ + +Within the area of automated reasoning, first order theorem proving +and model building (or model generation) have both received much +attention, and have given rise to highly sophisticated techniques. We +focus therefore on providing an NLTK interface to third party tools +for these tasks. In particular, the module ``nltk.inference`` can be +used to access both theorem provers and model builders. + +--------------------------------- +NLTK Interface to Theorem Provers +--------------------------------- + +The main class used to interface with a theorem prover is the ``Prover`` +class, found in ``nltk.api``. The ``prove()`` method takes three optional +arguments: a goal, a list of assumptions, and a ``verbose`` boolean to +indicate whether the proof should be printed to the console. The proof goal +and any assumptions need to be instances of the ``Expression`` class +specified by ``nltk.sem.logic``. There are currently three theorem provers +included with NLTK: ``Prover9``, ``TableauProver``, and +``ResolutionProver``. The first is an off-the-shelf prover, while the other +two are written in Python and included in the ``nltk.inference`` package. + + >>> from nltk.sem import Expression + >>> read_expr = Expression.fromstring + >>> p1 = read_expr('man(socrates)') + >>> p2 = read_expr('all x.(man(x) -> mortal(x))') + >>> c = read_expr('mortal(socrates)') + >>> Prover9().prove(c, [p1,p2]) + True + >>> TableauProver().prove(c, [p1,p2]) + True + >>> ResolutionProver().prove(c, [p1,p2], verbose=True) + [1] {-mortal(socrates)} A + [2] {man(socrates)} A + [3] {-man(z2), mortal(z2)} A + [4] {-man(socrates)} (1, 3) + [5] {mortal(socrates)} (2, 3) + [6] {} (1, 5) + + True + +--------------------- +The ``ProverCommand`` +--------------------- + +A ``ProverCommand`` is a stateful holder for a theorem +prover. The command stores a theorem prover instance (of type ``Prover``), +a goal, a list of assumptions, the result of the proof, and a string version +of the entire proof. Corresponding to the three included ``Prover`` +implementations, there are three ``ProverCommand`` implementations: +``Prover9Command``, ``TableauProverCommand``, and +``ResolutionProverCommand``. + +The ``ProverCommand``'s constructor takes its goal and assumptions. The +``prove()`` command executes the ``Prover`` and ``proof()`` +returns a String form of the proof +If the ``prove()`` method has not been called, +then the prover command will be unable to display a proof. + + >>> prover = ResolutionProverCommand(c, [p1,p2]) + >>> print(prover.proof()) + Traceback (most recent call last): + File "...", line 1212, in __run + compileflags, 1) in test.globs + File "", line 1, in + File "...", line ..., in proof + raise LookupError("You have to call prove() first to get a proof!") + LookupError: You have to call prove() first to get a proof! + >>> prover.prove() + True + >>> print(prover.proof()) + [1] {-mortal(socrates)} A + [2] {man(socrates)} A + [3] {-man(z4), mortal(z4)} A + [4] {-man(socrates)} (1, 3) + [5] {mortal(socrates)} (2, 3) + [6] {} (1, 5) + + +The prover command stores the result of proving so that if ``prove()`` is +called again, then the command can return the result without executing the +prover again. This allows the user to access the result of the proof without +wasting time re-computing what it already knows. + + >>> prover.prove() + True + >>> prover.prove() + True + +The assumptions and goal may be accessed using the ``assumptions()`` and +``goal()`` methods, respectively. + + >>> prover.assumptions() + [, mortal(x))>] + >>> prover.goal() + + +The assumptions list may be modified using the ``add_assumptions()`` and +``retract_assumptions()`` methods. Both methods take a list of ``Expression`` +objects. Since adding or removing assumptions may change the result of the +proof, the stored result is cleared when either of these methods are called. +That means that ``proof()`` will be unavailable until ``prove()`` is called and +a call to ``prove()`` will execute the theorem prover. + + >>> prover.retract_assumptions([read_expr('man(socrates)')]) + >>> print(prover.proof()) + Traceback (most recent call last): + File "...", line 1212, in __run + compileflags, 1) in test.globs + File "", line 1, in + File "...", line ..., in proof + raise LookupError("You have to call prove() first to get a proof!") + LookupError: You have to call prove() first to get a proof! + >>> prover.prove() + False + >>> print(prover.proof()) + [1] {-mortal(socrates)} A + [2] {-man(z6), mortal(z6)} A + [3] {-man(socrates)} (1, 2) + + >>> prover.add_assumptions([read_expr('man(socrates)')]) + >>> prover.prove() + True + +------- +Prover9 +------- + +Prover9 Installation +~~~~~~~~~~~~~~~~~~~~ + +You can download Prover9 from https://www.cs.unm.edu/~mccune/prover9/. + +Extract the source code into a suitable directory and follow the +instructions in the Prover9 ``README.make`` file to compile the executables. +Install these into an appropriate location; the +``prover9_search`` variable is currently configured to look in the +following locations: + + >>> p = Prover9() + >>> p.binary_locations() + ['/usr/local/bin/prover9', + '/usr/local/bin/prover9/bin', + '/usr/local/bin', + '/usr/bin', + '/usr/local/prover9', + '/usr/local/share/prover9'] + +Alternatively, the environment variable ``PROVER9HOME`` may be configured with +the binary's location. + +The path to the correct directory can be set manually in the following +manner: + + >>> config_prover9(path='/usr/local/bin') # doctest: +SKIP + [Found prover9: /usr/local/bin/prover9] + +If the executables cannot be found, ``Prover9`` will issue a warning message: + + >>> p.prove() # doctest: +SKIP + Traceback (most recent call last): + ... + LookupError: + =========================================================================== + NLTK was unable to find the prover9 executable! Use config_prover9() or + set the PROVER9HOME environment variable. + + >> config_prover9('/path/to/prover9') + + For more information, on prover9, see: + + =========================================================================== + + +Using Prover9 +~~~~~~~~~~~~~ + +The general case in theorem proving is to determine whether ``S |- g`` +holds, where ``S`` is a possibly empty set of assumptions, and ``g`` +is a proof goal. + +As mentioned earlier, NLTK input to ``Prover9`` must be +``Expression``\ s of ``nltk.sem.logic``. A ``Prover9`` instance is +initialized with a proof goal and, possibly, some assumptions. The +``prove()`` method attempts to find a proof of the goal, given the +list of assumptions (in this case, none). + + >>> goal = read_expr('(man(x) <-> --man(x))') + >>> prover = Prover9Command(goal) + >>> prover.prove() + True + +Given a ``ProverCommand`` instance ``prover``, the method +``prover.proof()`` will return a String of the extensive proof information +provided by Prover9, shown in abbreviated form here:: + + ============================== Prover9 =============================== + Prover9 (32) version ... + Process ... was started by ... on ... + ... + The command was ".../prover9 -f ...". + ============================== end of head =========================== + + ============================== INPUT ================================= + + % Reading from file /var/... + + + formulas(goals). + (all x (man(x) -> man(x))). + end_of_list. + + ... + ============================== end of search ========================= + + THEOREM PROVED + + Exiting with 1 proof. + + Process 6317 exit (max_proofs) Mon Jan 21 15:23:28 2008 + + +As mentioned earlier, we may want to list some assumptions for +the proof, as shown here. + + >>> g = read_expr('mortal(socrates)') + >>> a1 = read_expr('all x.(man(x) -> mortal(x))') + >>> prover = Prover9Command(g, assumptions=[a1]) + >>> prover.print_assumptions() + all x.(man(x) -> mortal(x)) + +However, the assumptions are not sufficient to derive the goal: + + >>> print(prover.prove()) + False + +So let's add another assumption: + + >>> a2 = read_expr('man(socrates)') + >>> prover.add_assumptions([a2]) + >>> prover.print_assumptions() + all x.(man(x) -> mortal(x)) + man(socrates) + >>> print(prover.prove()) + True + +We can also show the assumptions in ``Prover9`` format. + + >>> prover.print_assumptions(output_format='Prover9') + all x (man(x) -> mortal(x)) + man(socrates) + + >>> prover.print_assumptions(output_format='Spass') + Traceback (most recent call last): + . . . + NameError: Unrecognized value for 'output_format': Spass + +Assumptions can be retracted from the list of assumptions. + + >>> prover.retract_assumptions([a1]) + >>> prover.print_assumptions() + man(socrates) + >>> prover.retract_assumptions([a1]) + +Statements can be loaded from a file and parsed. We can then add these +statements as new assumptions. + + >>> g = read_expr('all x.(boxer(x) -> -boxerdog(x))') + >>> prover = Prover9Command(g) + >>> prover.prove() + False + >>> import nltk.data + >>> new = nltk.data.load('grammars/sample_grammars/background0.fol') + >>> for a in new: + ... print(a) + all x.(boxerdog(x) -> dog(x)) + all x.(boxer(x) -> person(x)) + all x.-(dog(x) & person(x)) + exists x.boxer(x) + exists x.boxerdog(x) + >>> prover.add_assumptions(new) + >>> print(prover.prove()) + True + >>> print(prover.proof()) + ============================== prooftrans ============================ + Prover9 (...) version ... + Process ... was started by ... on ... + ... + The command was ".../prover9". + ============================== end of head =========================== + + ============================== end of input ========================== + + ============================== PROOF ================================= + + % -------- Comments from original proof -------- + % Proof 1 at ... seconds. + % Length of proof is 13. + % Level of proof is 4. + % Maximum clause weight is 0. + % Given clauses 0. + + 1 (all x (boxerdog(x) -> dog(x))). [assumption]. + 2 (all x (boxer(x) -> person(x))). [assumption]. + 3 (all x -(dog(x) & person(x))). [assumption]. + 6 (all x (boxer(x) -> -boxerdog(x))). [goal]. + 8 -boxerdog(x) | dog(x). [clausify(1)]. + 9 boxerdog(c3). [deny(6)]. + 11 -boxer(x) | person(x). [clausify(2)]. + 12 boxer(c3). [deny(6)]. + 14 -dog(x) | -person(x). [clausify(3)]. + 15 dog(c3). [resolve(9,a,8,a)]. + 18 person(c3). [resolve(12,a,11,a)]. + 19 -person(c3). [resolve(15,a,14,a)]. + 20 $F. [resolve(19,a,18,a)]. + + ============================== end of proof ========================== + +---------------------- +The equiv() method +---------------------- + +One application of the theorem prover functionality is to check if +two Expressions have the same meaning. +The ``equiv()`` method calls a theorem prover to determine whether two +Expressions are logically equivalent. + + >>> a = read_expr(r'exists x.(man(x) & walks(x))') + >>> b = read_expr(r'exists x.(walks(x) & man(x))') + >>> print(a.equiv(b)) + True + +The same method can be used on Discourse Representation Structures (DRSs). +In this case, each DRS is converted to a first order logic form, and then +passed to the theorem prover. + + >>> dp = DrtParser() + >>> a = dp.parse(r'([x],[man(x), walks(x)])') + >>> b = dp.parse(r'([x],[walks(x), man(x)])') + >>> print(a.equiv(b)) + True + + +-------------------------------- +NLTK Interface to Model Builders +-------------------------------- + +The top-level to model builders is parallel to that for +theorem-provers. The ``ModelBuilder`` interface is located +in ``nltk.inference.api``. It is currently only implemented by +``Mace``, which interfaces with the Mace4 model builder. + +Typically we use a model builder to show that some set of formulas has +a model, and is therefore consistent. One way of doing this is by +treating our candidate set of sentences as assumptions, and leaving +the goal unspecified. +Thus, the following interaction shows how both ``{a, c1}`` and ``{a, c2}`` +are consistent sets, since Mace succeeds in a building a +model for each of them, while ``{c1, c2}`` is inconsistent. + + >>> a3 = read_expr('exists x.(man(x) and walks(x))') + >>> c1 = read_expr('mortal(socrates)') + >>> c2 = read_expr('-mortal(socrates)') + >>> mace = Mace() + >>> print(mace.build_model(None, [a3, c1])) + True + >>> print(mace.build_model(None, [a3, c2])) + True + +We can also use the model builder as an adjunct to theorem prover. +Let's suppose we are trying to prove ``S |- g``, i.e. that ``g`` +is logically entailed by assumptions ``S = {s1, s2, ..., sn}``. +We can this same input to Mace4, and the model builder will try to +find a counterexample, that is, to show that ``g`` does *not* follow +from ``S``. So, given this input, Mace4 will try to find a model for +the set ``S' = {s1, s2, ..., sn, (not g)}``. If ``g`` fails to follow +from ``S``, then Mace4 may well return with a counterexample faster +than Prover9 concludes that it cannot find the required proof. +Conversely, if ``g`` *is* provable from ``S``, Mace4 may take a long +time unsuccessfully trying to find a counter model, and will eventually give up. + +In the following example, we see that the model builder does succeed +in building a model of the assumptions together with the negation of +the goal. That is, it succeeds in finding a model +where there is a woman that every man loves; Adam is a man; Eve is a +woman; but Adam does not love Eve. + + >>> a4 = read_expr('exists y. (woman(y) & all x. (man(x) -> love(x,y)))') + >>> a5 = read_expr('man(adam)') + >>> a6 = read_expr('woman(eve)') + >>> g = read_expr('love(adam,eve)') + >>> print(mace.build_model(g, [a4, a5, a6])) + True + +The Model Builder will fail to find a model if the assumptions do entail +the goal. Mace will continue to look for models of ever-increasing sizes +until the end_size number is reached. By default, end_size is 500, +but it can be set manually for quicker response time. + + >>> a7 = read_expr('all x.(man(x) -> mortal(x))') + >>> a8 = read_expr('man(socrates)') + >>> g2 = read_expr('mortal(socrates)') + >>> print(Mace(end_size=50).build_model(g2, [a7, a8])) + False + +There is also a ``ModelBuilderCommand`` class that, like ``ProverCommand``, +stores a ``ModelBuilder``, a goal, assumptions, a result, and a model. The +only implementation in NLTK is ``MaceCommand``. + + +----- +Mace4 +----- + +Mace4 Installation +~~~~~~~~~~~~~~~~~~ + +Mace4 is packaged with Prover9, and can be downloaded from the same +source, namely https://www.cs.unm.edu/~mccune/prover9/. It is installed +in the same manner as Prover9. + +Using Mace4 +~~~~~~~~~~~ + +Check whether Mace4 can find a model. + + >>> a = read_expr('(see(mary,john) & -(mary = john))') + >>> mb = MaceCommand(assumptions=[a]) + >>> mb.build_model() + True + +Show the model in 'tabular' format. + + >>> print(mb.model(format='tabular')) + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + john : 0 + + mary : 1 + + see : + | 0 1 + ---+---- + 0 | 0 0 + 1 | 1 0 + + +Show the model in 'tabular' format. + + >>> print(mb.model(format='cooked')) + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + john = 0. + + mary = 1. + + - see(0,0). + - see(0,1). + see(1,0). + - see(1,1). + + +The property ``valuation`` accesses the stored ``Valuation``. + + >>> print(mb.valuation) + {'john': 'a', 'mary': 'b', 'see': {('b', 'a')}} + +We can return to our earlier example and inspect the model: + + >>> mb = MaceCommand(g, assumptions=[a4, a5, a6]) + >>> m = mb.build_model() + >>> print(mb.model(format='cooked')) + % number = 1 + % seconds = 0 + + % Interpretation of size 2 + + adam = 0. + + eve = 0. + + c1 = 1. + + man(0). + - man(1). + + woman(0). + woman(1). + + - love(0,0). + love(0,1). + - love(1,0). + - love(1,1). + + +Here, we can see that ``adam`` and ``eve`` have been assigned the same +individual, namely ``0`` as value; ``0`` is both a man and a woman; a second +individual ``1`` is also a woman; and ``0`` loves ``1``. Thus, this is +an interpretation in which there is a woman that every man loves but +Adam doesn't love Eve. + +Mace can also be used with propositional logic. + + >>> p = read_expr('P') + >>> q = read_expr('Q') + >>> mb = MaceCommand(q, [p, p>-q]) + >>> mb.build_model() + True + >>> mb.valuation['P'] + True + >>> mb.valuation['Q'] + False diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/internals.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/internals.doctest new file mode 100644 index 0000000..c992b23 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/internals.doctest @@ -0,0 +1,161 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================================== + Unit tests for the nltk.utilities module +========================================== + +overridden() +~~~~~~~~~~~~ + >>> from nltk.internals import overridden + +The typical use case is in defining methods for an interface or +abstract base class, in such a way that subclasses don't have to +implement all of the methods: + + >>> class EaterI(object): + ... '''Subclass must define eat() or batch_eat().''' + ... def eat(self, food): + ... if overridden(self.batch_eat): + ... return self.batch_eat([food])[0] + ... else: + ... raise NotImplementedError() + ... def batch_eat(self, foods): + ... return [self.eat(food) for food in foods] + +As long as a subclass implements one method, it will be used to +perform the other method: + + >>> class GoodEater1(EaterI): + ... def eat(self, food): + ... return 'yum' + >>> GoodEater1().eat('steak') + 'yum' + >>> GoodEater1().batch_eat(['steak', 'peas']) + ['yum', 'yum'] + + >>> class GoodEater2(EaterI): + ... def batch_eat(self, foods): + ... return ['yum' for food in foods] + >>> GoodEater2().eat('steak') + 'yum' + >>> GoodEater2().batch_eat(['steak', 'peas']) + ['yum', 'yum'] + +But if a subclass doesn't implement either one, then they'll get an +error when they try to call them. (nb this is better than infinite +recursion): + + >>> class BadEater1(EaterI): + ... pass + >>> BadEater1().eat('steak') + Traceback (most recent call last): + . . . + NotImplementedError + >>> BadEater1().batch_eat(['steak', 'peas']) + Traceback (most recent call last): + . . . + NotImplementedError + +Trying to use the abstract base class itself will also result in an +error: + + >>> class EaterI(EaterI): + ... pass + >>> EaterI().eat('steak') + Traceback (most recent call last): + . . . + NotImplementedError + >>> EaterI().batch_eat(['steak', 'peas']) + Traceback (most recent call last): + . . . + NotImplementedError + +It's ok to use intermediate abstract classes: + + >>> class AbstractEater(EaterI): + ... pass + + >>> class GoodEater3(AbstractEater): + ... def eat(self, food): + ... return 'yum' + ... + >>> GoodEater3().eat('steak') + 'yum' + >>> GoodEater3().batch_eat(['steak', 'peas']) + ['yum', 'yum'] + + >>> class GoodEater4(AbstractEater): + ... def batch_eat(self, foods): + ... return ['yum' for food in foods] + >>> GoodEater4().eat('steak') + 'yum' + >>> GoodEater4().batch_eat(['steak', 'peas']) + ['yum', 'yum'] + + >>> class BadEater2(AbstractEater): + ... pass + >>> BadEater2().eat('steak') + Traceback (most recent call last): + . . . + NotImplementedError + >>> BadEater2().batch_eat(['steak', 'peas']) + Traceback (most recent call last): + . . . + NotImplementedError + +Here's some extra tests: + + >>> class A(object): + ... def f(x): pass + >>> class B(A): + ... def f(x): pass + >>> class C(A): pass + >>> class D(B): pass + + >>> overridden(A().f) + False + >>> overridden(B().f) + True + >>> overridden(C().f) + False + >>> overridden(D().f) + True + +It works for classic classes, too: + + >>> class A: + ... def f(x): pass + >>> class B(A): + ... def f(x): pass + >>> class C(A): pass + >>> class D(B): pass + >>> overridden(A().f) + False + >>> overridden(B().f) + True + >>> overridden(C().f) + False + >>> overridden(D().f) + True + + +read_str() +~~~~~~~~~~~~ + >>> from nltk.internals import read_str + +Test valid scenarios + + >>> read_str("'valid string'", 0) + ('valid string', 14) + +Now test invalid scenarios + + >>> read_str("should error", 0) + Traceback (most recent call last): + ... + nltk.internals.ReadError: Expected open quote at 0 + >>> read_str("'should error", 0) + Traceback (most recent call last): + ... + nltk.internals.ReadError: Expected close quote at 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/japanese.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/japanese.doctest new file mode 100644 index 0000000..01df8ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/japanese.doctest @@ -0,0 +1,48 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +============================ +Japanese Language Processing +============================ + + >>> from nltk import * + +------------- +Corpus Access +------------- + +KNB Corpus +---------- + + >>> from nltk.corpus import knbc + +Access the words: this should produce a list of strings: + + >>> type(knbc.words()[0]) is not bytes + True + +Access the sentences: this should produce a list of lists of strings: + + >>> type(knbc.sents()[0][0]) is not bytes + True + +Access the tagged words: this should produce a list of word, tag pairs: + + >>> type(knbc.tagged_words()[0]) + <... 'tuple'> + +Access the tagged sentences: this should produce a list of lists of word, tag pairs: + + >>> type(knbc.tagged_sents()[0][0]) + <... 'tuple'> + + +JEITA Corpus +------------ + + >>> from nltk.corpus import jeita + +Access the tagged words: this should produce a list of word, tag pairs, where a tag is a string: + + >>> type(jeita.tagged_words()[0][1]) is not bytes + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/lm.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/lm.doctest new file mode 100644 index 0000000..b7cb429 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/lm.doctest @@ -0,0 +1,135 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. -*- coding: utf-8 -*- + + +Regression Tests +================ + + +Issue 167 +--------- +https://github.com/nltk/nltk/issues/167 + + >>> from nltk.corpus import brown + >>> from nltk.lm.preprocessing import padded_everygram_pipeline + >>> ngram_order = 3 + >>> train_data, vocab_data = padded_everygram_pipeline( + ... ngram_order, + ... brown.sents(categories="news") + ... ) + + >>> from nltk.lm import WittenBellInterpolated + >>> lm = WittenBellInterpolated(ngram_order) + >>> lm.fit(train_data, vocab_data) + + + + +Sentence containing an unseen word should result in infinite entropy because +Witten-Bell is based ultimately on MLE, which cannot handle unseen ngrams. +Crucially, it shouldn't raise any exceptions for unseen words. + + >>> from nltk.util import ngrams + >>> sent = ngrams("This is a sentence with the word aaddvark".split(), 3) + >>> lm.entropy(sent) + inf + +If we remove all unseen ngrams from the sentence, we'll get a non-infinite value +for the entropy. + + >>> sent = ngrams("This is a sentence".split(), 3) + >>> round(lm.entropy(sent), 14) + 10.23701322869105 + + +Issue 367 +--------- +https://github.com/nltk/nltk/issues/367 + +Reproducing Dan Blanchard's example: +https://github.com/nltk/nltk/issues/367#issuecomment-14646110 + + >>> from nltk.lm import Lidstone, Vocabulary + >>> word_seq = list('aaaababaaccbacb') + >>> ngram_order = 2 + >>> from nltk.util import everygrams + >>> train_data = [everygrams(word_seq, max_len=ngram_order)] + >>> V = Vocabulary(['a', 'b', 'c', '']) + >>> lm = Lidstone(0.2, ngram_order, vocabulary=V) + >>> lm.fit(train_data) + +For doctest to work we have to sort the vocabulary keys. + + >>> V_keys = sorted(V) + >>> round(sum(lm.score(w, ("b",)) for w in V_keys), 6) + 1.0 + >>> round(sum(lm.score(w, ("a",)) for w in V_keys), 6) + 1.0 + + >>> [lm.score(w, ("b",)) for w in V_keys] + [0.05, 0.05, 0.8, 0.05, 0.05] + >>> [round(lm.score(w, ("a",)), 4) for w in V_keys] + [0.0222, 0.0222, 0.4667, 0.2444, 0.2444] + + +Here's reproducing @afourney's comment: +https://github.com/nltk/nltk/issues/367#issuecomment-15686289 + + >>> sent = ['foo', 'foo', 'foo', 'foo', 'bar', 'baz'] + >>> ngram_order = 3 + >>> from nltk.lm.preprocessing import padded_everygram_pipeline + >>> train_data, vocab_data = padded_everygram_pipeline(ngram_order, [sent]) + >>> from nltk.lm import Lidstone + >>> lm = Lidstone(0.2, ngram_order) + >>> lm.fit(train_data, vocab_data) + +The vocabulary includes the "UNK" symbol as well as two padding symbols. + + >>> len(lm.vocab) + 6 + >>> word = "foo" + >>> context = ("bar", "baz") + +The raw counts. + + >>> lm.context_counts(context)[word] + 0 + >>> lm.context_counts(context).N() + 1 + +Counts with Lidstone smoothing. + + >>> lm.context_counts(context)[word] + lm.gamma + 0.2 + >>> lm.context_counts(context).N() + len(lm.vocab) * lm.gamma + 2.2 + +Without any backoff, just using Lidstone smoothing, P("foo" | "bar", "baz") should be: +0.2 / 2.2 ~= 0.090909 + + >>> round(lm.score(word, context), 6) + 0.090909 + + +Issue 380 +--------- +https://github.com/nltk/nltk/issues/380 + +Reproducing setup akin to this comment: +https://github.com/nltk/nltk/issues/380#issue-12879030 + +For speed take only the first 100 sentences of reuters. Shouldn't affect the test. + + >>> from nltk.corpus import reuters + >>> sents = reuters.sents()[:100] + >>> ngram_order = 3 + >>> from nltk.lm.preprocessing import padded_everygram_pipeline + >>> train_data, vocab_data = padded_everygram_pipeline(ngram_order, sents) + + >>> from nltk.lm import Lidstone + >>> lm = Lidstone(0.2, ngram_order) + >>> lm.fit(train_data, vocab_data) + >>> lm.score("said", ("",)) < 1 + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/logic.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/logic.doctest new file mode 100644 index 0000000..6b99501 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/logic.doctest @@ -0,0 +1,1096 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======================= +Logic & Lambda Calculus +======================= + +The `nltk.logic` package allows expressions of First-Order Logic (FOL) to be +parsed into ``Expression`` objects. In addition to FOL, the parser +handles lambda-abstraction with variables of higher order. + +-------- +Overview +-------- + + >>> from nltk.sem.logic import * + +The default inventory of logical constants is the following: + + >>> boolean_ops() + negation - + conjunction & + disjunction | + implication -> + equivalence <-> + >>> equality_preds() + equality = + inequality != + >>> binding_ops() + existential exists + universal all + lambda \ + +---------------- +Regression Tests +---------------- + + +Untyped Logic ++++++++++++++ + +Process logical expressions conveniently: + + >>> read_expr = Expression.fromstring + +Test for equality under alpha-conversion +======================================== + + >>> e1 = read_expr('exists x.P(x)') + >>> print(e1) + exists x.P(x) + >>> e2 = e1.alpha_convert(Variable('z')) + >>> print(e2) + exists z.P(z) + >>> e1 == e2 + True + + + >>> l = read_expr(r'\X.\X.X(X)(1)').simplify() + >>> id = read_expr(r'\X.X(X)') + >>> l == id + True + +Test numerals +============= + + >>> zero = read_expr(r'\F x.x') + >>> one = read_expr(r'\F x.F(x)') + >>> two = read_expr(r'\F x.F(F(x))') + >>> three = read_expr(r'\F x.F(F(F(x)))') + >>> four = read_expr(r'\F x.F(F(F(F(x))))') + >>> succ = read_expr(r'\N F x.F(N(F,x))') + >>> plus = read_expr(r'\M N F x.M(F,N(F,x))') + >>> mult = read_expr(r'\M N F.M(N(F))') + >>> pred = read_expr(r'\N F x.(N(\G H.H(G(F)))(\u.x)(\u.u))') + >>> v1 = ApplicationExpression(succ, zero).simplify() + >>> v1 == one + True + >>> v2 = ApplicationExpression(succ, v1).simplify() + >>> v2 == two + True + >>> v3 = ApplicationExpression(ApplicationExpression(plus, v1), v2).simplify() + >>> v3 == three + True + >>> v4 = ApplicationExpression(ApplicationExpression(mult, v2), v2).simplify() + >>> v4 == four + True + >>> v5 = ApplicationExpression(pred, ApplicationExpression(pred, v4)).simplify() + >>> v5 == two + True + +Overloaded operators also exist, for convenience. + + >>> print(succ(zero).simplify() == one) + True + >>> print(plus(one,two).simplify() == three) + True + >>> print(mult(two,two).simplify() == four) + True + >>> print(pred(pred(four)).simplify() == two) + True + + >>> john = read_expr(r'john') + >>> man = read_expr(r'\x.man(x)') + >>> walk = read_expr(r'\x.walk(x)') + >>> man(john).simplify() + + >>> print(-walk(john).simplify()) + -walk(john) + >>> print((man(john) & walk(john)).simplify()) + (man(john) & walk(john)) + >>> print((man(john) | walk(john)).simplify()) + (man(john) | walk(john)) + >>> print((man(john) > walk(john)).simplify()) + (man(john) -> walk(john)) + >>> print((man(john) < walk(john)).simplify()) + (man(john) <-> walk(john)) + +Python's built-in lambda operator can also be used with Expressions + + >>> john = VariableExpression(Variable('john')) + >>> run_var = VariableExpression(Variable('run')) + >>> run = lambda x: run_var(x) + >>> run(john) + + + +``betaConversionTestSuite.pl`` +------------------------------ + +Tests based on Blackburn & Bos' book, *Representation and Inference +for Natural Language*. + + >>> x1 = read_expr(r'\P.P(mia)(\x.walk(x))').simplify() + >>> x2 = read_expr(r'walk(mia)').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'exists x.(man(x) & ((\P.exists x.(woman(x) & P(x)))(\y.love(x,y))))').simplify() + >>> x2 = read_expr(r'exists x.(man(x) & exists y.(woman(y) & love(x,y)))').simplify() + >>> x1 == x2 + True + >>> x1 = read_expr(r'\a.sleep(a)(mia)').simplify() + >>> x2 = read_expr(r'sleep(mia)').simplify() + >>> x1 == x2 + True + >>> x1 = read_expr(r'\a.\b.like(b,a)(mia)').simplify() + >>> x2 = read_expr(r'\b.like(b,mia)').simplify() + >>> x1 == x2 + True + >>> x1 = read_expr(r'\a.(\b.like(b,a)(vincent))').simplify() + >>> x2 = read_expr(r'\a.like(vincent,a)').simplify() + >>> x1 == x2 + True + >>> x1 = read_expr(r'\a.((\b.like(b,a)(vincent)) & sleep(a))').simplify() + >>> x2 = read_expr(r'\a.(like(vincent,a) & sleep(a))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\a.\b.like(b,a)(mia)(vincent))').simplify() + >>> x2 = read_expr(r'like(vincent,mia)').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'P((\a.sleep(a)(vincent)))').simplify() + >>> x2 = read_expr(r'P(sleep(vincent))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.A((\b.sleep(b)(vincent)))').simplify() + >>> x2 = read_expr(r'\A.A(sleep(vincent))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.A(sleep(vincent))').simplify() + >>> x2 = read_expr(r'\A.A(sleep(vincent))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\A.A(vincent)(\b.sleep(b)))').simplify() + >>> x2 = read_expr(r'sleep(vincent)').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.believe(mia,A(vincent))(\b.sleep(b))').simplify() + >>> x2 = read_expr(r'believe(mia,sleep(vincent))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\A.(A(vincent) & A(mia)))(\b.sleep(b))').simplify() + >>> x2 = read_expr(r'(sleep(vincent) & sleep(mia))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.\B.(\C.C(A(vincent))(\d.probably(d)) & (\C.C(B(mia))(\d.improbably(d))))(\f.walk(f))(\f.talk(f))').simplify() + >>> x2 = read_expr(r'(probably(walk(vincent)) & improbably(talk(mia)))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\a.\b.(\C.C(a,b)(\d.\f.love(d,f))))(jules)(mia)').simplify() + >>> x2 = read_expr(r'love(jules,mia)').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\A.\B.exists c.(A(c) & B(c)))(\d.boxer(d),\d.sleep(d))').simplify() + >>> x2 = read_expr(r'exists c.(boxer(c) & sleep(c))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.Z(A)(\c.\a.like(a,c))').simplify() + >>> x2 = read_expr(r'Z(\c.\a.like(a,c))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'\A.\b.A(b)(\c.\b.like(b,c))').simplify() + >>> x2 = read_expr(r'\b.(\c.\b.like(b,c)(b))').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\a.\b.(\C.C(a,b)(\b.\a.loves(b,a))))(jules)(mia)').simplify() + >>> x2 = read_expr(r'loves(jules,mia)').simplify() + >>> x1 == x2 + True + + >>> x1 = read_expr(r'(\A.\b.(exists b.A(b) & A(b)))(\c.boxer(c))(vincent)').simplify() + >>> x2 = read_expr(r'((exists b.boxer(b)) & boxer(vincent))').simplify() + >>> x1 == x2 + True + +Test Parser +=========== + + >>> print(read_expr(r'john')) + john + >>> print(read_expr(r'x')) + x + >>> print(read_expr(r'-man(x)')) + -man(x) + >>> print(read_expr(r'--man(x)')) + --man(x) + >>> print(read_expr(r'(man(x))')) + man(x) + >>> print(read_expr(r'((man(x)))')) + man(x) + >>> print(read_expr(r'man(x) <-> tall(x)')) + (man(x) <-> tall(x)) + >>> print(read_expr(r'(man(x) <-> tall(x))')) + (man(x) <-> tall(x)) + >>> print(read_expr(r'(man(x) & tall(x) & walks(x))')) + (man(x) & tall(x) & walks(x)) + >>> print(read_expr(r'(man(x) & tall(x) & walks(x))').first) + (man(x) & tall(x)) + >>> print(read_expr(r'man(x) | tall(x) & walks(x)')) + (man(x) | (tall(x) & walks(x))) + >>> print(read_expr(r'((man(x) & tall(x)) | walks(x))')) + ((man(x) & tall(x)) | walks(x)) + >>> print(read_expr(r'man(x) & (tall(x) | walks(x))')) + (man(x) & (tall(x) | walks(x))) + >>> print(read_expr(r'(man(x) & (tall(x) | walks(x)))')) + (man(x) & (tall(x) | walks(x))) + >>> print(read_expr(r'P(x) -> Q(x) <-> R(x) | S(x) & T(x)')) + ((P(x) -> Q(x)) <-> (R(x) | (S(x) & T(x)))) + >>> print(read_expr(r'exists x.man(x)')) + exists x.man(x) + >>> print(read_expr(r'exists x.(man(x) & tall(x))')) + exists x.(man(x) & tall(x)) + >>> print(read_expr(r'exists x.(man(x) & tall(x) & walks(x))')) + exists x.(man(x) & tall(x) & walks(x)) + >>> print(read_expr(r'-P(x) & Q(x)')) + (-P(x) & Q(x)) + >>> read_expr(r'-P(x) & Q(x)') == read_expr(r'(-P(x)) & Q(x)') + True + >>> print(read_expr(r'\x.man(x)')) + \x.man(x) + >>> print(read_expr(r'\x.man(x)(john)')) + \x.man(x)(john) + >>> print(read_expr(r'\x.man(x)(john) & tall(x)')) + (\x.man(x)(john) & tall(x)) + >>> print(read_expr(r'\x.\y.sees(x,y)')) + \x y.sees(x,y) + >>> print(read_expr(r'\x y.sees(x,y)')) + \x y.sees(x,y) + >>> print(read_expr(r'\x.\y.sees(x,y)(a)')) + (\x y.sees(x,y))(a) + >>> print(read_expr(r'\x y.sees(x,y)(a)')) + (\x y.sees(x,y))(a) + >>> print(read_expr(r'\x.\y.sees(x,y)(a)(b)')) + ((\x y.sees(x,y))(a))(b) + >>> print(read_expr(r'\x y.sees(x,y)(a)(b)')) + ((\x y.sees(x,y))(a))(b) + >>> print(read_expr(r'\x.\y.sees(x,y)(a,b)')) + ((\x y.sees(x,y))(a))(b) + >>> print(read_expr(r'\x y.sees(x,y)(a,b)')) + ((\x y.sees(x,y))(a))(b) + >>> print(read_expr(r'((\x.\y.sees(x,y))(a))(b)')) + ((\x y.sees(x,y))(a))(b) + >>> print(read_expr(r'P(x)(y)(z)')) + P(x,y,z) + >>> print(read_expr(r'P(Q)')) + P(Q) + >>> print(read_expr(r'P(Q(x))')) + P(Q(x)) + >>> print(read_expr(r'(\x.exists y.walks(x,y))(x)')) + (\x.exists y.walks(x,y))(x) + >>> print(read_expr(r'exists x.(x = john)')) + exists x.(x = john) + >>> print(read_expr(r'((\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x)))(\x.bark(x))')) + ((\P Q.exists x.(P(x) & Q(x)))(\x.dog(x)))(\x.bark(x)) + >>> a = read_expr(r'exists c.exists b.A(b,c) & A(b,c)') + >>> b = read_expr(r'(exists c.(exists b.A(b,c))) & A(b,c)') + >>> print(a == b) + True + >>> a = read_expr(r'exists c.(exists b.A(b,c) & A(b,c))') + >>> b = read_expr(r'exists c.((exists b.A(b,c)) & A(b,c))') + >>> print(a == b) + True + >>> print(read_expr(r'exists x.x = y')) + exists x.(x = y) + >>> print(read_expr('A(B)(C)')) + A(B,C) + >>> print(read_expr('(A(B))(C)')) + A(B,C) + >>> print(read_expr('A((B)(C))')) + A(B(C)) + >>> print(read_expr('A(B(C))')) + A(B(C)) + >>> print(read_expr('(A)(B(C))')) + A(B(C)) + >>> print(read_expr('(((A)))(((B))(((C))))')) + A(B(C)) + >>> print(read_expr(r'A != B')) + -(A = B) + >>> print(read_expr('P(x) & x=y & P(y)')) + (P(x) & (x = y) & P(y)) + >>> try: print(read_expr(r'\walk.walk(x)')) + ... except LogicalExpressionException as e: print(e) + 'walk' is an illegal variable name. Constants may not be abstracted. + \walk.walk(x) + ^ + >>> try: print(read_expr(r'all walk.walk(john)')) + ... except LogicalExpressionException as e: print(e) + 'walk' is an illegal variable name. Constants may not be quantified. + all walk.walk(john) + ^ + >>> try: print(read_expr(r'x(john)')) + ... except LogicalExpressionException as e: print(e) + 'x' is an illegal predicate name. Individual variables may not be used as predicates. + x(john) + ^ + + >>> from nltk.sem.logic import LogicParser # hack to give access to custom quote chars + >>> lpq = LogicParser() + >>> lpq.quote_chars = [("'", "'", "\\", False)] + >>> print(lpq.parse(r"(man(x) & 'tall\'s,' (x) & walks (x) )")) + (man(x) & tall's,(x) & walks(x)) + >>> lpq.quote_chars = [("'", "'", "\\", True)] + >>> print(lpq.parse(r"'tall\'s,'")) + 'tall\'s,' + >>> print(lpq.parse(r"'spaced name(x)'")) + 'spaced name(x)' + >>> print(lpq.parse(r"-'tall\'s,'(x)")) + -'tall\'s,'(x) + >>> print(lpq.parse(r"(man(x) & 'tall\'s,' (x) & walks (x) )")) + (man(x) & 'tall\'s,'(x) & walks(x)) + + +Simplify +======== + + >>> print(read_expr(r'\x.man(x)(john)').simplify()) + man(john) + >>> print(read_expr(r'\x.((man(x)))(john)').simplify()) + man(john) + >>> print(read_expr(r'\x.\y.sees(x,y)(john, mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'\x y.sees(x,y)(john, mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'\x.\y.sees(x,y)(john)(mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'\x y.sees(x,y)(john)(mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'\x.\y.sees(x,y)(john)').simplify()) + \y.sees(john,y) + >>> print(read_expr(r'\x y.sees(x,y)(john)').simplify()) + \y.sees(john,y) + >>> print(read_expr(r'(\x.\y.sees(x,y)(john))(mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'(\x y.sees(x,y)(john))(mary)').simplify()) + sees(john,mary) + >>> print(read_expr(r'exists x.(man(x) & (\x.exists y.walks(x,y))(x))').simplify()) + exists x.(man(x) & exists y.walks(x,y)) + >>> e1 = read_expr(r'exists x.(man(x) & (\x.exists y.walks(x,y))(y))').simplify() + >>> e2 = read_expr(r'exists x.(man(x) & exists z1.walks(y,z1))') + >>> e1 == e2 + True + >>> print(read_expr(r'(\P Q.exists x.(P(x) & Q(x)))(\x.dog(x))').simplify()) + \Q.exists x.(dog(x) & Q(x)) + >>> print(read_expr(r'((\P.\Q.exists x.(P(x) & Q(x)))(\x.dog(x)))(\x.bark(x))').simplify()) + exists x.(dog(x) & bark(x)) + >>> print(read_expr(r'\P.(P(x)(y))(\a b.Q(a,b))').simplify()) + Q(x,y) + +Replace +======= + + >>> a = read_expr(r'a') + >>> x = read_expr(r'x') + >>> y = read_expr(r'y') + >>> z = read_expr(r'z') + + >>> print(read_expr(r'man(x)').replace(x.variable, a, False)) + man(a) + >>> print(read_expr(r'(man(x) & tall(x))').replace(x.variable, a, False)) + (man(a) & tall(a)) + >>> print(read_expr(r'exists x.man(x)').replace(x.variable, a, False)) + exists x.man(x) + >>> print(read_expr(r'exists x.man(x)').replace(x.variable, a, True)) + exists a.man(a) + >>> print(read_expr(r'exists x.give(x,y,z)').replace(y.variable, a, False)) + exists x.give(x,a,z) + >>> print(read_expr(r'exists x.give(x,y,z)').replace(y.variable, a, True)) + exists x.give(x,a,z) + >>> e1 = read_expr(r'exists x.give(x,y,z)').replace(y.variable, x, False) + >>> e2 = read_expr(r'exists z1.give(z1,x,z)') + >>> e1 == e2 + True + >>> e1 = read_expr(r'exists x.give(x,y,z)').replace(y.variable, x, True) + >>> e2 = read_expr(r'exists z1.give(z1,x,z)') + >>> e1 == e2 + True + >>> print(read_expr(r'\x y z.give(x,y,z)').replace(y.variable, a, False)) + \x y z.give(x,y,z) + >>> print(read_expr(r'\x y z.give(x,y,z)').replace(y.variable, a, True)) + \x a z.give(x,a,z) + >>> print(read_expr(r'\x.\y.give(x,y,z)').replace(z.variable, a, False)) + \x y.give(x,y,a) + >>> print(read_expr(r'\x.\y.give(x,y,z)').replace(z.variable, a, True)) + \x y.give(x,y,a) + >>> e1 = read_expr(r'\x.\y.give(x,y,z)').replace(z.variable, x, False) + >>> e2 = read_expr(r'\z1.\y.give(z1,y,x)') + >>> e1 == e2 + True + >>> e1 = read_expr(r'\x.\y.give(x,y,z)').replace(z.variable, x, True) + >>> e2 = read_expr(r'\z1.\y.give(z1,y,x)') + >>> e1 == e2 + True + >>> print(read_expr(r'\x.give(x,y,z)').replace(z.variable, y, False)) + \x.give(x,y,y) + >>> print(read_expr(r'\x.give(x,y,z)').replace(z.variable, y, True)) + \x.give(x,y,y) + + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + >>> e1 = read_expr('e1') + >>> e2 = read_expr('e2') + >>> print(read_expr('exists e1 e2.(walk(e1) & talk(e2))').replace(e1.variable, e2, True)) + exists e2 e01.(walk(e2) & talk(e01)) + + +Variables / Free +================ + + >>> examples = [r'walk(john)', + ... r'walk(x)', + ... r'?vp(?np)', + ... r'see(john,mary)', + ... r'exists x.walk(x)', + ... r'\x.see(john,x)', + ... r'\x.see(john,x)(mary)', + ... r'P(x)', + ... r'\P.P(x)', + ... r'aa(x,bb(y),cc(z),P(w),u)', + ... r'bo(?det(?n),@x)'] + >>> examples = [read_expr(e) for e in examples] + + >>> for e in examples: + ... print('%-25s' % e, sorted(e.free())) + walk(john) [] + walk(x) [Variable('x')] + ?vp(?np) [] + see(john,mary) [] + exists x.walk(x) [] + \x.see(john,x) [] + (\x.see(john,x))(mary) [] + P(x) [Variable('P'), Variable('x')] + \P.P(x) [Variable('x')] + aa(x,bb(y),cc(z),P(w),u) [Variable('P'), Variable('u'), Variable('w'), Variable('x'), Variable('y'), Variable('z')] + bo(?det(?n),@x) [] + + >>> for e in examples: + ... print('%-25s' % e, sorted(e.constants())) + walk(john) [Variable('john')] + walk(x) [] + ?vp(?np) [Variable('?np')] + see(john,mary) [Variable('john'), Variable('mary')] + exists x.walk(x) [] + \x.see(john,x) [Variable('john')] + (\x.see(john,x))(mary) [Variable('john'), Variable('mary')] + P(x) [] + \P.P(x) [] + aa(x,bb(y),cc(z),P(w),u) [] + bo(?det(?n),@x) [Variable('?n'), Variable('@x')] + + >>> for e in examples: + ... print('%-25s' % e, sorted(e.predicates())) + walk(john) [Variable('walk')] + walk(x) [Variable('walk')] + ?vp(?np) [Variable('?vp')] + see(john,mary) [Variable('see')] + exists x.walk(x) [Variable('walk')] + \x.see(john,x) [Variable('see')] + (\x.see(john,x))(mary) [Variable('see')] + P(x) [] + \P.P(x) [] + aa(x,bb(y),cc(z),P(w),u) [Variable('aa'), Variable('bb'), Variable('cc')] + bo(?det(?n),@x) [Variable('?det'), Variable('bo')] + + >>> for e in examples: + ... print('%-25s' % e, sorted(e.variables())) + walk(john) [] + walk(x) [Variable('x')] + ?vp(?np) [Variable('?np'), Variable('?vp')] + see(john,mary) [] + exists x.walk(x) [] + \x.see(john,x) [] + (\x.see(john,x))(mary) [] + P(x) [Variable('P'), Variable('x')] + \P.P(x) [Variable('x')] + aa(x,bb(y),cc(z),P(w),u) [Variable('P'), Variable('u'), Variable('w'), Variable('x'), Variable('y'), Variable('z')] + bo(?det(?n),@x) [Variable('?det'), Variable('?n'), Variable('@x')] + + + +`normalize` + >>> print(read_expr(r'\e083.(walk(e083, z472) & talk(e092, z938))').normalize()) + \e01.(walk(e01,z3) & talk(e02,z4)) + +Typed Logic ++++++++++++ + + >>> from nltk.sem.logic import LogicParser + >>> tlp = LogicParser(True) + >>> print(tlp.parse(r'man(x)').type) + ? + >>> print(tlp.parse(r'walk(angus)').type) + ? + >>> print(tlp.parse(r'-man(x)').type) + t + >>> print(tlp.parse(r'(man(x) <-> tall(x))').type) + t + >>> print(tlp.parse(r'exists x.(man(x) & tall(x))').type) + t + >>> print(tlp.parse(r'\x.man(x)').type) + + >>> print(tlp.parse(r'john').type) + e + >>> print(tlp.parse(r'\x y.sees(x,y)').type) + > + >>> print(tlp.parse(r'\x.man(x)(john)').type) + ? + >>> print(tlp.parse(r'\x.\y.sees(x,y)(john)').type) + + >>> print(tlp.parse(r'\x.\y.sees(x,y)(john)(mary)').type) + ? + >>> print(tlp.parse(r'\P.\Q.exists x.(P(x) & Q(x))').type) + <,<,t>> + >>> print(tlp.parse(r'\x.y').type) + + >>> print(tlp.parse(r'\P.P(x)').type) + <,?> + + >>> parsed = tlp.parse('see(john,mary)') + >>> print(parsed.type) + ? + >>> print(parsed.function) + see(john) + >>> print(parsed.function.type) + + >>> print(parsed.function.function) + see + >>> print(parsed.function.function.type) + > + + >>> parsed = tlp.parse('P(x,y)') + >>> print(parsed) + P(x,y) + >>> print(parsed.type) + ? + >>> print(parsed.function) + P(x) + >>> print(parsed.function.type) + + >>> print(parsed.function.function) + P + >>> print(parsed.function.function.type) + > + + >>> print(tlp.parse(r'P').type) + ? + + >>> print(tlp.parse(r'P', {'P': 't'}).type) + t + + >>> a = tlp.parse(r'P(x)') + >>> print(a.type) + ? + >>> print(a.function.type) + + >>> print(a.argument.type) + e + + >>> a = tlp.parse(r'-P(x)') + >>> print(a.type) + t + >>> print(a.term.type) + t + >>> print(a.term.function.type) + + >>> print(a.term.argument.type) + e + + >>> a = tlp.parse(r'P & Q') + >>> print(a.type) + t + >>> print(a.first.type) + t + >>> print(a.second.type) + t + + >>> a = tlp.parse(r'(P(x) & Q(x))') + >>> print(a.type) + t + >>> print(a.first.type) + t + >>> print(a.first.function.type) + + >>> print(a.first.argument.type) + e + >>> print(a.second.type) + t + >>> print(a.second.function.type) + + >>> print(a.second.argument.type) + e + + >>> a = tlp.parse(r'\x.P(x)') + >>> print(a.type) + + >>> print(a.term.function.type) + + >>> print(a.term.argument.type) + e + + >>> a = tlp.parse(r'\P.P(x)') + >>> print(a.type) + <,?> + >>> print(a.term.function.type) + + >>> print(a.term.argument.type) + e + + >>> a = tlp.parse(r'(\x.P(x)(john)) & Q(x)') + >>> print(a.type) + t + >>> print(a.first.type) + t + >>> print(a.first.function.type) + + >>> print(a.first.function.term.function.type) + + >>> print(a.first.function.term.argument.type) + e + >>> print(a.first.argument.type) + e + + >>> a = tlp.parse(r'\x y.P(x,y)(john)(mary) & Q(x)') + >>> print(a.type) + t + >>> print(a.first.type) + t + >>> print(a.first.function.type) + + >>> print(a.first.function.function.type) + > + + >>> a = tlp.parse(r'--P') + >>> print(a.type) + t + >>> print(a.term.type) + t + >>> print(a.term.term.type) + t + + >>> tlp.parse(r'\x y.P(x,y)').type + > + >>> tlp.parse(r'\x y.P(x,y)', {'P': '>'}).type + > + + >>> a = tlp.parse(r'\P y.P(john,y)(\x y.see(x,y))') + >>> a.type + + >>> a.function.type + <>,> + >>> a.function.term.term.function.function.type + > + >>> a.argument.type + > + + >>> a = tlp.parse(r'exists c f.(father(c) = f)') + >>> a.type + t + >>> a.term.term.type + t + >>> a.term.term.first.type + e + >>> a.term.term.first.function.type + + >>> a.term.term.second.type + e + +typecheck() + + >>> a = tlp.parse('P(x)') + >>> b = tlp.parse('Q(x)') + >>> a.type + ? + >>> c = a & b + >>> c.first.type + ? + >>> c.typecheck() + {...} + >>> c.first.type + t + + >>> a = tlp.parse('P(x)') + >>> b = tlp.parse('P(x) & Q(x)') + >>> a.type + ? + >>> typecheck([a,b]) + {...} + >>> a.type + t + + >>> e = tlp.parse(r'man(x)') + >>> print(dict((k,str(v)) for k,v in e.typecheck().items()) == {'x': 'e', 'man': ''}) + True + >>> sig = {'man': ''} + >>> e = tlp.parse(r'man(x)', sig) + >>> print(e.function.type) + + >>> print(dict((k,str(v)) for k,v in e.typecheck().items()) == {'x': 'e', 'man': ''}) + True + >>> print(e.function.type) + + >>> print(dict((k,str(v)) for k,v in e.typecheck(sig).items()) == {'x': 'e', 'man': ''}) + True + +findtype() + + >>> print(tlp.parse(r'man(x)').findtype(Variable('man'))) + + >>> print(tlp.parse(r'see(x,y)').findtype(Variable('see'))) + > + >>> print(tlp.parse(r'P(Q(R(x)))').findtype(Variable('Q'))) + ? + +reading types from strings + + >>> Type.fromstring('e') + e + >>> Type.fromstring('') + + >>> Type.fromstring('<,>') + <,> + >>> Type.fromstring('<,?>') + <,?> + +alternative type format + + >>> Type.fromstring('e').str() + 'IND' + >>> Type.fromstring('').str() + '(IND -> ANY)' + >>> Type.fromstring('<,t>').str() + '((IND -> BOOL) -> BOOL)' + +Type.__eq__() + + >>> from nltk.sem.logic import * + + >>> e = ENTITY_TYPE + >>> t = TRUTH_TYPE + >>> a = ANY_TYPE + >>> et = ComplexType(e,t) + >>> eet = ComplexType(e,ComplexType(e,t)) + >>> at = ComplexType(a,t) + >>> ea = ComplexType(e,a) + >>> aa = ComplexType(a,a) + + >>> e == e + True + >>> t == t + True + >>> e == t + False + >>> a == t + False + >>> t == a + False + >>> a == a + True + >>> et == et + True + >>> a == et + False + >>> et == a + False + >>> a == ComplexType(a,aa) + True + >>> ComplexType(a,aa) == a + True + +matches() + + >>> e.matches(t) + False + >>> a.matches(t) + True + >>> t.matches(a) + True + >>> a.matches(et) + True + >>> et.matches(a) + True + >>> ea.matches(eet) + True + >>> eet.matches(ea) + True + >>> aa.matches(et) + True + >>> aa.matches(t) + True + +Type error during parsing +========================= + + >>> try: print(tlp.parse(r'exists x y.(P(x) & P(x,y))')) + ... except InconsistentTypeHierarchyException as e: print(e) + The variable 'P' was found in multiple places with different types. + >>> try: tlp.parse(r'\x y.see(x,y)(\x.man(x))') + ... except TypeException as e: print(e) + The function '\x y.see(x,y)' is of type '>' and cannot be applied to '\x.man(x)' of type ''. Its argument must match type 'e'. + >>> try: tlp.parse(r'\P x y.-P(x,y)(\x.-man(x))') + ... except TypeException as e: print(e) + The function '\P x y.-P(x,y)' is of type '<>,>>' and cannot be applied to '\x.-man(x)' of type ''. Its argument must match type '>'. + + >>> a = tlp.parse(r'-talk(x)') + >>> signature = a.typecheck() + >>> try: print(tlp.parse(r'-talk(x,y)', signature)) + ... except InconsistentTypeHierarchyException as e: print(e) + The variable 'talk' was found in multiple places with different types. + + >>> a = tlp.parse(r'-P(x)') + >>> b = tlp.parse(r'-P(x,y)') + >>> a.typecheck() + {...} + >>> b.typecheck() + {...} + >>> try: typecheck([a,b]) + ... except InconsistentTypeHierarchyException as e: print(e) + The variable 'P' was found in multiple places with different types. + + >>> a = tlp.parse(r'P(x)') + >>> b = tlp.parse(r'P(x,y)') + >>> signature = {'P': ''} + >>> a.typecheck(signature) + {...} + >>> try: typecheck([a,b], signature) + ... except InconsistentTypeHierarchyException as e: print(e) + The variable 'P' was found in multiple places with different types. + +Parse errors +============ + + >>> try: read_expr(r'') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + + ^ + >>> try: read_expr(r'(') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + ( + ^ + >>> try: read_expr(r')') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + ) + ^ + >>> try: read_expr(r'()') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + () + ^ + >>> try: read_expr(r'(P(x) & Q(x)') + ... except LogicalExpressionException as e: print(e) + End of input found. Expected token ')'. + (P(x) & Q(x) + ^ + >>> try: read_expr(r'(P(x) &') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + (P(x) & + ^ + >>> try: read_expr(r'(P(x) | )') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + (P(x) | ) + ^ + >>> try: read_expr(r'P(x) ->') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + P(x) -> + ^ + >>> try: read_expr(r'P(x') + ... except LogicalExpressionException as e: print(e) + End of input found. Expected token ')'. + P(x + ^ + >>> try: read_expr(r'P(x,') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + P(x, + ^ + >>> try: read_expr(r'P(x,)') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + P(x,) + ^ + >>> try: read_expr(r'exists') + ... except LogicalExpressionException as e: print(e) + End of input found. Variable and Expression expected following quantifier 'exists'. + exists + ^ + >>> try: read_expr(r'exists x') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + exists x + ^ + >>> try: read_expr(r'exists x.') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + exists x. + ^ + >>> try: read_expr(r'\ ') + ... except LogicalExpressionException as e: print(e) + End of input found. Variable and Expression expected following lambda operator. + \ + ^ + >>> try: read_expr(r'\ x') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + \ x + ^ + >>> try: read_expr(r'\ x y') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + \ x y + ^ + >>> try: read_expr(r'\ x.') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + \ x. + ^ + >>> try: read_expr(r'P(x)Q(x)') + ... except LogicalExpressionException as e: print(e) + Unexpected token: 'Q'. + P(x)Q(x) + ^ + >>> try: read_expr(r'(P(x)Q(x)') + ... except LogicalExpressionException as e: print(e) + Unexpected token: 'Q'. Expected token ')'. + (P(x)Q(x) + ^ + >>> try: read_expr(r'exists x y') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + exists x y + ^ + >>> try: read_expr(r'exists x y.') + ... except LogicalExpressionException as e: print(e) + End of input found. Expression expected. + exists x y. + ^ + >>> try: read_expr(r'exists x -> y') + ... except LogicalExpressionException as e: print(e) + Unexpected token: '->'. Expression expected. + exists x -> y + ^ + + + >>> try: read_expr(r'A -> ((P(x) & Q(x)) -> Z') + ... except LogicalExpressionException as e: print(e) + End of input found. Expected token ')'. + A -> ((P(x) & Q(x)) -> Z + ^ + >>> try: read_expr(r'A -> ((P(x) &) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> ((P(x) &) -> Z + ^ + >>> try: read_expr(r'A -> ((P(x) | )) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> ((P(x) | )) -> Z + ^ + >>> try: read_expr(r'A -> (P(x) ->) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (P(x) ->) -> Z + ^ + >>> try: read_expr(r'A -> (P(x) -> Z') + ... except LogicalExpressionException as e: print(e) + End of input found. Expected token ')'. + A -> (P(x) -> Z + ^ + >>> try: read_expr(r'A -> (P(x,) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (P(x,) -> Z + ^ + >>> try: read_expr(r'A -> (P(x,)) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (P(x,)) -> Z + ^ + >>> try: read_expr(r'A -> (exists) -> Z') + ... except LogicalExpressionException as e: print(e) + ')' is an illegal variable name. Constants may not be quantified. + A -> (exists) -> Z + ^ + >>> try: read_expr(r'A -> (exists x) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (exists x) -> Z + ^ + >>> try: read_expr(r'A -> (exists x.) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (exists x.) -> Z + ^ + >>> try: read_expr(r'A -> (\ ) -> Z') + ... except LogicalExpressionException as e: print(e) + ')' is an illegal variable name. Constants may not be abstracted. + A -> (\ ) -> Z + ^ + >>> try: read_expr(r'A -> (\ x) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (\ x) -> Z + ^ + >>> try: read_expr(r'A -> (\ x y) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (\ x y) -> Z + ^ + >>> try: read_expr(r'A -> (\ x.) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (\ x.) -> Z + ^ + >>> try: read_expr(r'A -> (P(x)Q(x)) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: 'Q'. Expected token ')'. + A -> (P(x)Q(x)) -> Z + ^ + >>> try: read_expr(r'A -> ((P(x)Q(x)) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: 'Q'. Expected token ')'. + A -> ((P(x)Q(x)) -> Z + ^ + >>> try: read_expr(r'A -> (all x y) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (all x y) -> Z + ^ + >>> try: read_expr(r'A -> (exists x y.) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: ')'. Expression expected. + A -> (exists x y.) -> Z + ^ + >>> try: read_expr(r'A -> (exists x -> y) -> Z') + ... except LogicalExpressionException as e: print(e) + Unexpected token: '->'. Expression expected. + A -> (exists x -> y) -> Z + ^ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/meteor.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/meteor.doctest new file mode 100644 index 0000000..9e0dc6e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/meteor.doctest @@ -0,0 +1,54 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. -*- coding: utf-8 -*- + +============= +METEOR tests +============= + +No Alignment test +------------------ + + >>> from nltk.translate import meteor + >>> from nltk import word_tokenize + +If the candidate has no alignment to any of the references, the METEOR score is 0. + + >>> round(meteor( + ... [word_tokenize('The candidate has no alignment to any of the references')], + ... word_tokenize('John loves Mary') + ... ), 4) + 0.0 + +Tests based on wikipedia examples +--------------------------------- + +Testing on `wikipedia examples `_ + + >>> same_res = round(meteor( + ... [word_tokenize('The cat sat on the mat')], + ... word_tokenize('The cat sat on the mat') + ... ), 4) + >>> abs(same_res - 0.9977) < 1e-2 + True + + >>> meteor( + ... [word_tokenize('The cat sat on the mat')], + ... word_tokenize('on the mat sat the cat') + ... ) + 0.5 + + >>> round(meteor( + ... [word_tokenize('The cat sat on the mat')], + ... word_tokenize('The cat was sat on the mat') + ... ), 4) + 0.9654 + +Test corresponding to issue #2751, where METEOR score > 1 + + >>> round(meteor( + ... [word_tokenize('create or update a vm set')], + ... word_tokenize('creates or updates a virtual machine scale set') + ... ), 4) + 0.7806 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/metrics.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/metrics.doctest new file mode 100644 index 0000000..4f141a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/metrics.doctest @@ -0,0 +1,321 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======= +Metrics +======= + +----- +Setup +----- + + >>> import pytest + >>> _ = pytest.importorskip("numpy") + + +The `nltk.metrics` package provides a variety of *evaluation measures* +which can be used for a wide variety of NLP tasks. + + >>> from nltk.metrics import * + +------------------ +Standard IR Scores +------------------ + +We can use standard scores from information retrieval to test the +performance of taggers, chunkers, etc. + + >>> reference = 'DET NN VB DET JJ NN NN IN DET NN'.split() + >>> test = 'DET VB VB DET NN NN NN IN DET NN'.split() + >>> print(accuracy(reference, test)) + 0.8 + + +The following measures apply to sets: + + >>> reference_set = set(reference) + >>> test_set = set(test) + >>> precision(reference_set, test_set) + 1.0 + >>> print(recall(reference_set, test_set)) + 0.8 + >>> print(f_measure(reference_set, test_set)) + 0.88888888888... + +Measuring the likelihood of the data, given probability distributions: + + >>> from nltk import FreqDist, MLEProbDist + >>> pdist1 = MLEProbDist(FreqDist("aldjfalskfjaldsf")) + >>> pdist2 = MLEProbDist(FreqDist("aldjfalssjjlldss")) + >>> print(log_likelihood(['a', 'd'], [pdist1, pdist2])) + -2.7075187496... + + +---------------- +Distance Metrics +---------------- + +String edit distance (Levenshtein): + + >>> edit_distance("rain", "shine") + 3 + >>> edit_distance_align("shine", "shine") + [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] + >>> edit_distance_align("rain", "brainy") + [(0, 0), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (4, 6)] + >>> edit_distance_align("", "brainy") + [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)] + >>> edit_distance_align("", "") + [(0, 0)] + +Other distance measures: + + >>> s1 = set([1,2,3,4]) + >>> s2 = set([3,4,5]) + >>> binary_distance(s1, s2) + 1.0 + >>> print(jaccard_distance(s1, s2)) + 0.6 + >>> print(masi_distance(s1, s2)) + 0.868 + +---------------------- +Miscellaneous Measures +---------------------- + +Rank Correlation works with two dictionaries mapping keys to ranks. +The dictionaries should have the same set of keys. + + >>> spearman_correlation({'e':1, 't':2, 'a':3}, {'e':1, 'a':2, 't':3}) + 0.5 + +Windowdiff uses a sliding window in comparing two segmentations of the same input (e.g. tokenizations, chunkings). +Segmentations are represented using strings of zeros and ones. + + >>> s1 = "000100000010" + >>> s2 = "000010000100" + >>> s3 = "100000010000" + >>> s4 = "000000000000" + >>> s5 = "111111111111" + >>> windowdiff(s1, s1, 3) + 0.0 + >>> abs(windowdiff(s1, s2, 3) - 0.3) < 1e-6 # windowdiff(s1, s2, 3) == 0.3 + True + >>> abs(windowdiff(s2, s3, 3) - 0.8) < 1e-6 # windowdiff(s2, s3, 3) == 0.8 + True + >>> windowdiff(s1, s4, 3) + 0.5 + >>> windowdiff(s1, s5, 3) + 1.0 + +---------------- +Confusion Matrix +---------------- + + >>> reference = 'This is the reference data. Testing 123. aoaeoeoe' + >>> test = 'Thos iz_the rifirenci data. Testeng 123. aoaeoeoe' + >>> print(ConfusionMatrix(reference, test)) + | . 1 2 3 T _ a c d e f g h i n o r s t z | + --+-------------------------------------------+ + |<8>. . . . . 1 . . . . . . . . . . . . . . | + . | .<2>. . . . . . . . . . . . . . . . . . . | + 1 | . .<1>. . . . . . . . . . . . . . . . . . | + 2 | . . .<1>. . . . . . . . . . . . . . . . . | + 3 | . . . .<1>. . . . . . . . . . . . . . . . | + T | . . . . .<2>. . . . . . . . . . . . . . . | + _ | . . . . . .<.>. . . . . . . . . . . . . . | + a | . . . . . . .<4>. . . . . . . . . . . . . | + c | . . . . . . . .<1>. . . . . . . . . . . . | + d | . . . . . . . . .<1>. . . . . . . . . . . | + e | . . . . . . . . . .<6>. . . 3 . . . . . . | + f | . . . . . . . . . . .<1>. . . . . . . . . | + g | . . . . . . . . . . . .<1>. . . . . . . . | + h | . . . . . . . . . . . . .<2>. . . . . . . | + i | . . . . . . . . . . 1 . . .<1>. 1 . . . . | + n | . . . . . . . . . . . . . . .<2>. . . . . | + o | . . . . . . . . . . . . . . . .<3>. . . . | + r | . . . . . . . . . . . . . . . . .<2>. . . | + s | . . . . . . . . . . . . . . . . . .<2>. 1 | + t | . . . . . . . . . . . . . . . . . . .<3>. | + z | . . . . . . . . . . . . . . . . . . . .<.>| + --+-------------------------------------------+ + (row = reference; col = test) + + + >>> cm = ConfusionMatrix(reference, test) + >>> print(cm.pretty_format(sort_by_count=True)) + | e a i o s t . T h n r 1 2 3 c d f g _ z | + --+-------------------------------------------+ + |<8>. . . . . . . . . . . . . . . . . . 1 . | + e | .<6>. 3 . . . . . . . . . . . . . . . . . | + a | . .<4>. . . . . . . . . . . . . . . . . . | + i | . 1 .<1>1 . . . . . . . . . . . . . . . . | + o | . . . .<3>. . . . . . . . . . . . . . . . | + s | . . . . .<2>. . . . . . . . . . . . . . 1 | + t | . . . . . .<3>. . . . . . . . . . . . . . | + . | . . . . . . .<2>. . . . . . . . . . . . . | + T | . . . . . . . .<2>. . . . . . . . . . . . | + h | . . . . . . . . .<2>. . . . . . . . . . . | + n | . . . . . . . . . .<2>. . . . . . . . . . | + r | . . . . . . . . . . .<2>. . . . . . . . . | + 1 | . . . . . . . . . . . .<1>. . . . . . . . | + 2 | . . . . . . . . . . . . .<1>. . . . . . . | + 3 | . . . . . . . . . . . . . .<1>. . . . . . | + c | . . . . . . . . . . . . . . .<1>. . . . . | + d | . . . . . . . . . . . . . . . .<1>. . . . | + f | . . . . . . . . . . . . . . . . .<1>. . . | + g | . . . . . . . . . . . . . . . . . .<1>. . | + _ | . . . . . . . . . . . . . . . . . . .<.>. | + z | . . . . . . . . . . . . . . . . . . . .<.>| + --+-------------------------------------------+ + (row = reference; col = test) + + + >>> print(cm.pretty_format(sort_by_count=True, truncate=10)) + | e a i o s t . T h | + --+---------------------+ + |<8>. . . . . . . . . | + e | .<6>. 3 . . . . . . | + a | . .<4>. . . . . . . | + i | . 1 .<1>1 . . . . . | + o | . . . .<3>. . . . . | + s | . . . . .<2>. . . . | + t | . . . . . .<3>. . . | + . | . . . . . . .<2>. . | + T | . . . . . . . .<2>. | + h | . . . . . . . . .<2>| + --+---------------------+ + (row = reference; col = test) + + + >>> print(cm.pretty_format(sort_by_count=True, truncate=10, values_in_chart=False)) + | 1 | + | 1 2 3 4 5 6 7 8 9 0 | + ---+---------------------+ + 1 |<8>. . . . . . . . . | + 2 | .<6>. 3 . . . . . . | + 3 | . .<4>. . . . . . . | + 4 | . 1 .<1>1 . . . . . | + 5 | . . . .<3>. . . . . | + 6 | . . . . .<2>. . . . | + 7 | . . . . . .<3>. . . | + 8 | . . . . . . .<2>. . | + 9 | . . . . . . . .<2>. | + 10 | . . . . . . . . .<2>| + ---+---------------------+ + (row = reference; col = test) + Value key: + 1: + 2: e + 3: a + 4: i + 5: o + 6: s + 7: t + 8: . + 9: T + 10: h + + +For "e", the number of true positives should be 6, while the number of false negatives is 3. +So, the recall ought to be 6 / (6 + 3): + + >>> cm.recall("e") # doctest: +ELLIPSIS + 0.666666... + +For "e", the false positive is just 1, so the precision should be 6 / (6 + 1): + + >>> cm.precision("e") # doctest: +ELLIPSIS + 0.857142... + +The f-measure with default value of ``alpha = 0.5`` should then be: + +* *1/(alpha/p + (1-alpha)/r) =* +* *1/(0.5/p + 0.5/r) =* +* *2pr / (p + r) =* +* *2 * 0.857142... * 0.666666... / (0.857142... + 0.666666...) =* +* *0.749999...* + + >>> cm.f_measure("e") # doctest: +ELLIPSIS + 0.749999... + +-------------------- +Association measures +-------------------- + +These measures are useful to determine whether the coocurrence of two random +events is meaningful. They are used, for instance, to distinguish collocations +from other pairs of adjacent words. + +We bring some examples of bigram association calculations from Manning and +Schutze's SNLP, 2nd Ed. chapter 5. + + >>> n_new_companies, n_new, n_companies, N = 8, 15828, 4675, 14307668 + >>> bam = BigramAssocMeasures + >>> bam.raw_freq(20, (42, 20), N) == 20. / N + True + >>> bam.student_t(n_new_companies, (n_new, n_companies), N) + 0.999... + >>> bam.chi_sq(n_new_companies, (n_new, n_companies), N) + 1.54... + >>> bam.likelihood_ratio(150, (12593, 932), N) + 1291... + +For other associations, we ensure the ordering of the measures: + + >>> bam.mi_like(20, (42, 20), N) > bam.mi_like(20, (41, 27), N) + True + >>> bam.pmi(20, (42, 20), N) > bam.pmi(20, (41, 27), N) + True + >>> bam.phi_sq(20, (42, 20), N) > bam.phi_sq(20, (41, 27), N) + True + >>> bam.poisson_stirling(20, (42, 20), N) > bam.poisson_stirling(20, (41, 27), N) + True + >>> bam.jaccard(20, (42, 20), N) > bam.jaccard(20, (41, 27), N) + True + >>> bam.dice(20, (42, 20), N) > bam.dice(20, (41, 27), N) + True + >>> bam.fisher(20, (42, 20), N) > bam.fisher(20, (41, 27), N) # doctest: +SKIP + False + +For trigrams, we have to provide more count information: + + >>> n_w1_w2_w3 = 20 + >>> n_w1_w2, n_w1_w3, n_w2_w3 = 35, 60, 40 + >>> pair_counts = (n_w1_w2, n_w1_w3, n_w2_w3) + >>> n_w1, n_w2, n_w3 = 100, 200, 300 + >>> uni_counts = (n_w1, n_w2, n_w3) + >>> N = 14307668 + >>> tam = TrigramAssocMeasures + >>> tam.raw_freq(n_w1_w2_w3, pair_counts, uni_counts, N) == 1. * n_w1_w2_w3 / N + True + >>> uni_counts2 = (n_w1, n_w2, 100) + >>> tam.student_t(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.student_t(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.chi_sq(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.chi_sq(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.mi_like(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.mi_like(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.pmi(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.pmi(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.likelihood_ratio(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.likelihood_ratio(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.poisson_stirling(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.poisson_stirling(n_w1_w2_w3, pair_counts, uni_counts, N) + True + >>> tam.jaccard(n_w1_w2_w3, pair_counts, uni_counts2, N) > tam.jaccard(n_w1_w2_w3, pair_counts, uni_counts, N) + True + + +For fourgrams, we have to provide more count information: + + >>> n_w1_w2_w3_w4 = 5 + >>> n_w1_w2, n_w1_w3, n_w2_w3 = 35, 60, 40 + >>> n_w1_w2_w3, n_w2_w3_w4 = 20, 10 + >>> pair_counts = (n_w1_w2, n_w1_w3, n_w2_w3) + >>> triplet_counts = (n_w1_w2_w3, n_w2_w3_w4) + >>> n_w1, n_w2, n_w3, n_w4 = 100, 200, 300, 400 + >>> uni_counts = (n_w1, n_w2, n_w3, n_w4) + >>> N = 14307668 + >>> qam = QuadgramAssocMeasures + >>> qam.raw_freq(n_w1_w2_w3_w4, pair_counts, triplet_counts, uni_counts, N) == 1. * n_w1_w2_w3_w4 / N + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/misc.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/misc.doctest new file mode 100644 index 0000000..88c6fe8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/misc.doctest @@ -0,0 +1,118 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +-------------------------------------------------------------------------------- +Unit tests for the miscellaneous sort functions. +-------------------------------------------------------------------------------- + + >>> from copy import deepcopy + >>> from nltk.misc.sort import * + +A (very) small list of unsorted integers. + + >>> test_data = [12, 67, 7, 28, 92, 56, 53, 720, 91, 57, 20, 20] + +Test each sorting method - each method returns the number of operations +required to sort the data, and sorts in-place (desctructively - hence the need +for multiple copies). + + >>> sorted_data = deepcopy(test_data) + >>> selection(sorted_data) + 66 + + >>> sorted_data + [7, 12, 20, 20, 28, 53, 56, 57, 67, 91, 92, 720] + + >>> sorted_data = deepcopy(test_data) + >>> bubble(sorted_data) + 30 + + >>> sorted_data + [7, 12, 20, 20, 28, 53, 56, 57, 67, 91, 92, 720] + + >>> sorted_data = deepcopy(test_data) + >>> merge(sorted_data) + 30 + + >>> sorted_data + [7, 12, 20, 20, 28, 53, 56, 57, 67, 91, 92, 720] + + >>> sorted_data = deepcopy(test_data) + >>> quick(sorted_data) + 13 + + >>> sorted_data + [7, 12, 20, 20, 28, 53, 56, 57, 67, 91, 92, 720] + +-------------------------------------------------------------------------------- +Unit tests for Wordfinder class +-------------------------------------------------------------------------------- + + >>> import random + + >>> # The following is not enough for reproducibility under Python 2/3 + >>> # (see https://bugs.python.org/issue9025) so this test is skipped. + >>> random.seed(12345) + + >>> from nltk.misc import wordfinder + >>> wordfinder.word_finder() # doctest: +SKIP + Word Finder + + J V L A I R O T A T I S I V O D E R E T + H U U B E A R O E P O C S O R E T N E P + A D A U Z E E S R A P P A L L M E N T R + C X A D Q S Z T P E O R S N G P J A D E + I G Y K K T I A A R G F I D T E L C N S + R E C N B H T R L T N N B W N T A O A I + A Y I L O E I A M E I A A Y U R P L L D + G L T V S T S F E A D I P H D O O H N I + R L S E C I N I L R N N M E C G R U E A + A A Y G I C E N L L E O I G Q R T A E L + M R C E T I S T A E T L L E U A E N R L + O U O T A S E E C S O O N H Y P A T G Y + E M H O M M D R E S F P U L T H C F N V + L A C A I M A M A N L B R U T E D O M I + O R I L N E E E E E U A R S C R Y L I P + H T R K E S N N M S I L A S R E V I N U + T X T A A O U T K S E T A R R E S I B J + A E D L E L J I F O O R P E L K N I R W + K H A I D E Q O P R I C K T I M B E R P + Z K D O O H G N I H T U R V E Y D R O P + + 1: INTERCHANGER + 2: TEARLESSNESS + 3: UNIVERSALISM + 4: DESENSITIZER + 5: INTERMENTION + 6: TRICHOCYSTIC + 7: EXTRAMURALLY + 8: VEGETOALKALI + 9: PALMELLACEAE + 10: AESTHETICISM + 11: PETROGRAPHER + 12: VISITATORIAL + 13: OLEOMARGARIC + 14: WRINKLEPROOF + 15: PRICKTIMBER + 16: PRESIDIALLY + 17: SCITAMINEAE + 18: ENTEROSCOPE + 19: APPALLMENT + 20: TURVEYDROP + 21: THINGHOOD + 22: BISERRATE + 23: GREENLAND + 24: BRUTEDOM + 25: POLONIAN + 26: ACOLHUAN + 27: LAPORTEA + 28: TENDING + 29: TEREDO + 30: MESOLE + 31: UNLIMP + 32: OSTARA + 33: PILY + 34: DUNT + 35: ONYX + 36: KATH + 37: JUNE diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/nonmonotonic.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/nonmonotonic.doctest new file mode 100644 index 0000000..560c506 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/nonmonotonic.doctest @@ -0,0 +1,293 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +====================== +Nonmonotonic Reasoning +====================== + + >>> from nltk.test.setup_fixt import check_binary + >>> check_binary('mace4') + + >>> from nltk import * + >>> from nltk.inference.nonmonotonic import * + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + >>> read_expr = logic.Expression.fromstring + +------------------------ +Closed Domain Assumption +------------------------ + +The only entities in the domain are those found in the assumptions or goal. +If the domain only contains "A" and "B", then the expression "exists x.P(x)" can +be replaced with "P(A) | P(B)" and an expression "all x.P(x)" can be replaced +with "P(A) & P(B)". + + >>> p1 = read_expr(r'all x.(man(x) -> mortal(x))') + >>> p2 = read_expr(r'man(Socrates)') + >>> c = read_expr(r'mortal(Socrates)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> prover.prove() + True + >>> cdp = ClosedDomainProver(prover) + >>> for a in cdp.assumptions(): print(a) # doctest: +SKIP + (man(Socrates) -> mortal(Socrates)) + man(Socrates) + >>> cdp.prove() + True + + >>> p1 = read_expr(r'exists x.walk(x)') + >>> p2 = read_expr(r'man(Socrates)') + >>> c = read_expr(r'walk(Socrates)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> prover.prove() + False + >>> cdp = ClosedDomainProver(prover) + >>> for a in cdp.assumptions(): print(a) # doctest: +SKIP + walk(Socrates) + man(Socrates) + >>> cdp.prove() + True + + >>> p1 = read_expr(r'exists x.walk(x)') + >>> p2 = read_expr(r'man(Socrates)') + >>> p3 = read_expr(r'-walk(Bill)') + >>> c = read_expr(r'walk(Socrates)') + >>> prover = Prover9Command(c, [p1,p2,p3]) + >>> prover.prove() + False + >>> cdp = ClosedDomainProver(prover) + >>> for a in cdp.assumptions(): print(a) # doctest: +SKIP + (walk(Socrates) | walk(Bill)) + man(Socrates) + -walk(Bill) + >>> cdp.prove() + True + + >>> p1 = read_expr(r'walk(Socrates)') + >>> p2 = read_expr(r'walk(Bill)') + >>> c = read_expr(r'all x.walk(x)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> prover.prove() + False + >>> cdp = ClosedDomainProver(prover) + >>> for a in cdp.assumptions(): print(a) # doctest: +SKIP + walk(Socrates) + walk(Bill) + >>> print(cdp.goal()) # doctest: +SKIP + (walk(Socrates) & walk(Bill)) + >>> cdp.prove() + True + + >>> p1 = read_expr(r'girl(mary)') + >>> p2 = read_expr(r'dog(rover)') + >>> p3 = read_expr(r'all x.(girl(x) -> -dog(x))') + >>> p4 = read_expr(r'all x.(dog(x) -> -girl(x))') + >>> p5 = read_expr(r'chase(mary, rover)') + >>> c = read_expr(r'exists y.(dog(y) & all x.(girl(x) -> chase(x,y)))') + >>> prover = Prover9Command(c, [p1,p2,p3,p4,p5]) + >>> print(prover.prove()) + False + >>> cdp = ClosedDomainProver(prover) + >>> for a in cdp.assumptions(): print(a) # doctest: +SKIP + girl(mary) + dog(rover) + ((girl(rover) -> -dog(rover)) & (girl(mary) -> -dog(mary))) + ((dog(rover) -> -girl(rover)) & (dog(mary) -> -girl(mary))) + chase(mary,rover) + >>> print(cdp.goal()) # doctest: +SKIP + ((dog(rover) & (girl(rover) -> chase(rover,rover)) & (girl(mary) -> chase(mary,rover))) | (dog(mary) & (girl(rover) -> chase(rover,mary)) & (girl(mary) -> chase(mary,mary)))) + >>> print(cdp.prove()) + True + +----------------------- +Unique Names Assumption +----------------------- + +No two entities in the domain represent the same entity unless it can be +explicitly proven that they do. Therefore, if the domain contains "A" and "B", +then add the assumption "-(A = B)" if it is not the case that +" \|- (A = B)". + + >>> p1 = read_expr(r'man(Socrates)') + >>> p2 = read_expr(r'man(Bill)') + >>> c = read_expr(r'exists x.exists y.-(x = y)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> prover.prove() + False + >>> unp = UniqueNamesProver(prover) + >>> for a in unp.assumptions(): print(a) # doctest: +SKIP + man(Socrates) + man(Bill) + -(Socrates = Bill) + >>> unp.prove() + True + + >>> p1 = read_expr(r'all x.(walk(x) -> (x = Socrates))') + >>> p2 = read_expr(r'Bill = William') + >>> p3 = read_expr(r'Bill = Billy') + >>> c = read_expr(r'-walk(William)') + >>> prover = Prover9Command(c, [p1,p2,p3]) + >>> prover.prove() + False + >>> unp = UniqueNamesProver(prover) + >>> for a in unp.assumptions(): print(a) # doctest: +SKIP + all x.(walk(x) -> (x = Socrates)) + (Bill = William) + (Bill = Billy) + -(William = Socrates) + -(Billy = Socrates) + -(Socrates = Bill) + >>> unp.prove() + True + +----------------------- +Closed World Assumption +----------------------- + +The only entities that have certain properties are those that is it stated +have the properties. We accomplish this assumption by "completing" predicates. + +If the assumptions contain "P(A)", then "all x.(P(x) -> (x=A))" is the completion +of "P". If the assumptions contain "all x.(ostrich(x) -> bird(x))", then +"all x.(bird(x) -> ostrich(x))" is the completion of "bird". If the +assumptions don't contain anything that are "P", then "all x.-P(x)" is the +completion of "P". + + >>> p1 = read_expr(r'walk(Socrates)') + >>> p2 = read_expr(r'-(Socrates = Bill)') + >>> c = read_expr(r'-walk(Bill)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> prover.prove() + False + >>> cwp = ClosedWorldProver(prover) + >>> for a in cwp.assumptions(): print(a) # doctest: +SKIP + walk(Socrates) + -(Socrates = Bill) + all z1.(walk(z1) -> (z1 = Socrates)) + >>> cwp.prove() + True + + >>> p1 = read_expr(r'see(Socrates, John)') + >>> p2 = read_expr(r'see(John, Mary)') + >>> p3 = read_expr(r'-(Socrates = John)') + >>> p4 = read_expr(r'-(John = Mary)') + >>> c = read_expr(r'-see(Socrates, Mary)') + >>> prover = Prover9Command(c, [p1,p2,p3,p4]) + >>> prover.prove() + False + >>> cwp = ClosedWorldProver(prover) + >>> for a in cwp.assumptions(): print(a) # doctest: +SKIP + see(Socrates,John) + see(John,Mary) + -(Socrates = John) + -(John = Mary) + all z3 z4.(see(z3,z4) -> (((z3 = Socrates) & (z4 = John)) | ((z3 = John) & (z4 = Mary)))) + >>> cwp.prove() + True + + >>> p1 = read_expr(r'all x.(ostrich(x) -> bird(x))') + >>> p2 = read_expr(r'bird(Tweety)') + >>> p3 = read_expr(r'-ostrich(Sam)') + >>> p4 = read_expr(r'Sam != Tweety') + >>> c = read_expr(r'-bird(Sam)') + >>> prover = Prover9Command(c, [p1,p2,p3,p4]) + >>> prover.prove() + False + >>> cwp = ClosedWorldProver(prover) + >>> for a in cwp.assumptions(): print(a) # doctest: +SKIP + all x.(ostrich(x) -> bird(x)) + bird(Tweety) + -ostrich(Sam) + -(Sam = Tweety) + all z7.-ostrich(z7) + all z8.(bird(z8) -> ((z8 = Tweety) | ostrich(z8))) + >>> print(cwp.prove()) + True + +----------------------- +Multi-Decorator Example +----------------------- + +Decorators can be nested to utilize multiple assumptions. + + >>> p1 = read_expr(r'see(Socrates, John)') + >>> p2 = read_expr(r'see(John, Mary)') + >>> c = read_expr(r'-see(Socrates, Mary)') + >>> prover = Prover9Command(c, [p1,p2]) + >>> print(prover.prove()) + False + >>> cmd = ClosedDomainProver(UniqueNamesProver(ClosedWorldProver(prover))) + >>> print(cmd.prove()) + True + +----------------- +Default Reasoning +----------------- + >>> logic._counter._value = 0 + >>> premises = [] + +define the taxonomy + + >>> premises.append(read_expr(r'all x.(elephant(x) -> animal(x))')) + >>> premises.append(read_expr(r'all x.(bird(x) -> animal(x))')) + >>> premises.append(read_expr(r'all x.(dove(x) -> bird(x))')) + >>> premises.append(read_expr(r'all x.(ostrich(x) -> bird(x))')) + >>> premises.append(read_expr(r'all x.(flying_ostrich(x) -> ostrich(x))')) + +default the properties using abnormalities + + >>> premises.append(read_expr(r'all x.((animal(x) & -Ab1(x)) -> -fly(x))')) #normal animals don't fly + >>> premises.append(read_expr(r'all x.((bird(x) & -Ab2(x)) -> fly(x))')) #normal birds fly + >>> premises.append(read_expr(r'all x.((ostrich(x) & -Ab3(x)) -> -fly(x))')) #normal ostriches don't fly + +specify abnormal entities + + >>> premises.append(read_expr(r'all x.(bird(x) -> Ab1(x))')) #flight + >>> premises.append(read_expr(r'all x.(ostrich(x) -> Ab2(x))')) #non-flying bird + >>> premises.append(read_expr(r'all x.(flying_ostrich(x) -> Ab3(x))')) #flying ostrich + +define entities + + >>> premises.append(read_expr(r'elephant(el)')) + >>> premises.append(read_expr(r'dove(do)')) + >>> premises.append(read_expr(r'ostrich(os)')) + +print the augmented assumptions list + + >>> prover = Prover9Command(None, premises) + >>> command = UniqueNamesProver(ClosedWorldProver(prover)) + >>> for a in command.assumptions(): print(a) # doctest: +SKIP + all x.(elephant(x) -> animal(x)) + all x.(bird(x) -> animal(x)) + all x.(dove(x) -> bird(x)) + all x.(ostrich(x) -> bird(x)) + all x.(flying_ostrich(x) -> ostrich(x)) + all x.((animal(x) & -Ab1(x)) -> -fly(x)) + all x.((bird(x) & -Ab2(x)) -> fly(x)) + all x.((ostrich(x) & -Ab3(x)) -> -fly(x)) + all x.(bird(x) -> Ab1(x)) + all x.(ostrich(x) -> Ab2(x)) + all x.(flying_ostrich(x) -> Ab3(x)) + elephant(el) + dove(do) + ostrich(os) + all z1.(animal(z1) -> (elephant(z1) | bird(z1))) + all z2.(Ab1(z2) -> bird(z2)) + all z3.(bird(z3) -> (dove(z3) | ostrich(z3))) + all z4.(dove(z4) -> (z4 = do)) + all z5.(Ab2(z5) -> ostrich(z5)) + all z6.(Ab3(z6) -> flying_ostrich(z6)) + all z7.(ostrich(z7) -> ((z7 = os) | flying_ostrich(z7))) + all z8.-flying_ostrich(z8) + all z9.(elephant(z9) -> (z9 = el)) + -(el = os) + -(el = do) + -(os = do) + + >>> UniqueNamesProver(ClosedWorldProver(Prover9Command(read_expr('-fly(el)'), premises))).prove() + True + >>> UniqueNamesProver(ClosedWorldProver(Prover9Command(read_expr('fly(do)'), premises))).prove() + True + >>> UniqueNamesProver(ClosedWorldProver(Prover9Command(read_expr('-fly(os)'), premises))).prove() + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/paice.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/paice.doctest new file mode 100644 index 0000000..1e3a65c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/paice.doctest @@ -0,0 +1,35 @@ + +===================================================== +PAICE's evaluation statistics for stemming algorithms +===================================================== + +Given a list of words with their real lemmas and stems according to stemming algorithm under evaluation, +counts Understemming Index (UI), Overstemming Index (OI), Stemming Weight (SW) and Error-rate relative to truncation (ERRT). + + >>> from nltk.metrics import Paice + + +------------------------------------- +Understemming and Overstemming values +------------------------------------- + + >>> lemmas = {'kneel': ['kneel', 'knelt'], + ... 'range': ['range', 'ranged'], + ... 'ring': ['ring', 'rang', 'rung']} + >>> stems = {'kneel': ['kneel'], + ... 'knelt': ['knelt'], + ... 'rang': ['rang', 'range', 'ranged'], + ... 'ring': ['ring'], + ... 'rung': ['rung']} + >>> p = Paice(lemmas, stems) + >>> p.gumt, p.gdmt, p.gwmt, p.gdnt + (4.0, 5.0, 2.0, 16.0) + + >>> p.ui, p.oi, p.sw + (0.8..., 0.125..., 0.15625...) + + >>> p.errt + 1.0 + + >>> [('{0:.3f}'.format(a), '{0:.3f}'.format(b)) for a, b in p.coords] + [('0.000', '1.000'), ('0.000', '0.375'), ('0.600', '0.125'), ('0.800', '0.125')] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/parse.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/parse.doctest new file mode 100644 index 0000000..ddd0cff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/parse.doctest @@ -0,0 +1,933 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========= + Parsing +========= + +Unit tests for the Context Free Grammar class +--------------------------------------------- + + >>> import pickle + >>> import subprocess + >>> import sys + >>> from nltk import Nonterminal, nonterminals, Production, CFG + + >>> nt1 = Nonterminal('NP') + >>> nt2 = Nonterminal('VP') + + >>> nt1.symbol() + 'NP' + + >>> nt1 == Nonterminal('NP') + True + + >>> nt1 == nt2 + False + + >>> S, NP, VP, PP = nonterminals('S, NP, VP, PP') + >>> N, V, P, DT = nonterminals('N, V, P, DT') + + >>> prod1 = Production(S, [NP, VP]) + >>> prod2 = Production(NP, [DT, NP]) + + >>> prod1.lhs() + S + + >>> prod1.rhs() + (NP, VP) + + >>> prod1 == Production(S, [NP, VP]) + True + + >>> prod1 == prod2 + False + + >>> grammar = CFG.fromstring(""" + ... S -> NP VP + ... PP -> P NP + ... NP -> 'the' N | N PP | 'the' N PP + ... VP -> V NP | V PP | V NP PP + ... N -> 'cat' + ... N -> 'dog' + ... N -> 'rug' + ... V -> 'chased' + ... V -> 'sat' + ... P -> 'in' + ... P -> 'on' + ... """) + + >>> cmd = """import pickle + ... from nltk import Production + ... p = Production('S', ['NP', 'VP']) + ... print(pickle.dumps(p)) + ... """ + + >>> # Start a subprocess to simulate pickling in another process + >>> proc = subprocess.run([sys.executable, '-c', cmd], stdout=subprocess.PIPE) + >>> p1 = pickle.loads(eval(proc.stdout)) + >>> p2 = Production('S', ['NP', 'VP']) + >>> print(hash(p1) == hash(p2)) + True + +Unit tests for the rd (Recursive Descent Parser) class +------------------------------------------------------ + +Create and run a recursive descent parser over both a syntactically ambiguous +and unambiguous sentence. + + >>> from nltk.parse import RecursiveDescentParser + >>> rd = RecursiveDescentParser(grammar) + + >>> sentence1 = 'the cat chased the dog'.split() + >>> sentence2 = 'the cat chased the dog on the rug'.split() + + >>> for t in rd.parse(sentence1): + ... print(t) + (S (NP the (N cat)) (VP (V chased) (NP the (N dog)))) + + >>> for t in rd.parse(sentence2): + ... print(t) + (S + (NP the (N cat)) + (VP (V chased) (NP the (N dog) (PP (P on) (NP the (N rug)))))) + (S + (NP the (N cat)) + (VP (V chased) (NP the (N dog)) (PP (P on) (NP the (N rug))))) + + +(dolist (expr doctest-font-lock-keywords) + (add-to-list 'font-lock-keywords expr)) + + font-lock-keywords +(add-to-list 'font-lock-keywords + (car doctest-font-lock-keywords)) + + +Unit tests for the sr (Shift Reduce Parser) class +------------------------------------------------- + +Create and run a shift reduce parser over both a syntactically ambiguous +and unambiguous sentence. Note that unlike the recursive descent parser, one +and only one parse is ever returned. + + >>> from nltk.parse import ShiftReduceParser + >>> sr = ShiftReduceParser(grammar) + + >>> sentence1 = 'the cat chased the dog'.split() + >>> sentence2 = 'the cat chased the dog on the rug'.split() + + >>> for t in sr.parse(sentence1): + ... print(t) + (S (NP the (N cat)) (VP (V chased) (NP the (N dog)))) + + +The shift reduce parser uses heuristics to decide what to do when there are +multiple possible shift or reduce operations available - for the supplied +grammar clearly the wrong operation is selected. + + >>> for t in sr.parse(sentence2): + ... print(t) + + +Unit tests for the Chart Parser class +------------------------------------- + +We use the demo() function for testing. +We must turn off showing of times. + + >>> import nltk + +First we test tracing with a short sentence + + >>> nltk.parse.chart.demo(2, print_times=False, trace=1, + ... sent='I saw a dog', numparses=1) + * Sentence: + I saw a dog + ['I', 'saw', 'a', 'dog'] + + * Strategy: Bottom-up + + |. I . saw . a . dog .| + |[---------] . . .| [0:1] 'I' + |. [---------] . .| [1:2] 'saw' + |. . [---------] .| [2:3] 'a' + |. . . [---------]| [3:4] 'dog' + |> . . . .| [0:0] NP -> * 'I' + |[---------] . . .| [0:1] NP -> 'I' * + |> . . . .| [0:0] S -> * NP VP + |> . . . .| [0:0] NP -> * NP PP + |[---------> . . .| [0:1] S -> NP * VP + |[---------> . . .| [0:1] NP -> NP * PP + |. > . . .| [1:1] Verb -> * 'saw' + |. [---------] . .| [1:2] Verb -> 'saw' * + |. > . . .| [1:1] VP -> * Verb NP + |. > . . .| [1:1] VP -> * Verb + |. [---------> . .| [1:2] VP -> Verb * NP + |. [---------] . .| [1:2] VP -> Verb * + |. > . . .| [1:1] VP -> * VP PP + |[-------------------] . .| [0:2] S -> NP VP * + |. [---------> . .| [1:2] VP -> VP * PP + |. . > . .| [2:2] Det -> * 'a' + |. . [---------] .| [2:3] Det -> 'a' * + |. . > . .| [2:2] NP -> * Det Noun + |. . [---------> .| [2:3] NP -> Det * Noun + |. . . > .| [3:3] Noun -> * 'dog' + |. . . [---------]| [3:4] Noun -> 'dog' * + |. . [-------------------]| [2:4] NP -> Det Noun * + |. . > . .| [2:2] S -> * NP VP + |. . > . .| [2:2] NP -> * NP PP + |. [-----------------------------]| [1:4] VP -> Verb NP * + |. . [------------------->| [2:4] S -> NP * VP + |. . [------------------->| [2:4] NP -> NP * PP + |[=======================================]| [0:4] S -> NP VP * + |. [----------------------------->| [1:4] VP -> VP * PP + Nr edges in chart: 33 + (S (NP I) (VP (Verb saw) (NP (Det a) (Noun dog)))) + + +Then we test the different parsing Strategies. +Note that the number of edges differ between the strategies. + +Top-down + + >>> nltk.parse.chart.demo(1, print_times=False, trace=0, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + * Strategy: Top-down + + Nr edges in chart: 48 + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + + +Bottom-up + + >>> nltk.parse.chart.demo(2, print_times=False, trace=0, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + * Strategy: Bottom-up + + Nr edges in chart: 53 + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + + +Bottom-up Left-Corner + + >>> nltk.parse.chart.demo(3, print_times=False, trace=0, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + * Strategy: Bottom-up left-corner + + Nr edges in chart: 36 + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + + +Left-Corner with Bottom-Up Filter + + >>> nltk.parse.chart.demo(4, print_times=False, trace=0, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + * Strategy: Filtered left-corner + + Nr edges in chart: 28 + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + + +The stepping chart parser + + >>> nltk.parse.chart.demo(5, print_times=False, trace=1, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + * Strategy: Stepping (top-down vs bottom-up) + + *** SWITCH TO TOP DOWN + |[------] . . . . .| [0:1] 'I' + |. [------] . . . .| [1:2] 'saw' + |. . [------] . . .| [2:3] 'John' + |. . . [------] . .| [3:4] 'with' + |. . . . [------] .| [4:5] 'a' + |. . . . . [------]| [5:6] 'dog' + |> . . . . . .| [0:0] S -> * NP VP + |> . . . . . .| [0:0] NP -> * NP PP + |> . . . . . .| [0:0] NP -> * Det Noun + |> . . . . . .| [0:0] NP -> * 'I' + |[------] . . . . .| [0:1] NP -> 'I' * + |[------> . . . . .| [0:1] S -> NP * VP + |[------> . . . . .| [0:1] NP -> NP * PP + |. > . . . . .| [1:1] VP -> * VP PP + |. > . . . . .| [1:1] VP -> * Verb NP + |. > . . . . .| [1:1] VP -> * Verb + |. > . . . . .| [1:1] Verb -> * 'saw' + |. [------] . . . .| [1:2] Verb -> 'saw' * + |. [------> . . . .| [1:2] VP -> Verb * NP + |. [------] . . . .| [1:2] VP -> Verb * + |[-------------] . . . .| [0:2] S -> NP VP * + |. [------> . . . .| [1:2] VP -> VP * PP + *** SWITCH TO BOTTOM UP + |. . > . . . .| [2:2] NP -> * 'John' + |. . . > . . .| [3:3] PP -> * 'with' NP + |. . . > . . .| [3:3] Prep -> * 'with' + |. . . . > . .| [4:4] Det -> * 'a' + |. . . . . > .| [5:5] Noun -> * 'dog' + |. . [------] . . .| [2:3] NP -> 'John' * + |. . . [------> . .| [3:4] PP -> 'with' * NP + |. . . [------] . .| [3:4] Prep -> 'with' * + |. . . . [------] .| [4:5] Det -> 'a' * + |. . . . . [------]| [5:6] Noun -> 'dog' * + |. [-------------] . . .| [1:3] VP -> Verb NP * + |[--------------------] . . .| [0:3] S -> NP VP * + |. [-------------> . . .| [1:3] VP -> VP * PP + |. . > . . . .| [2:2] S -> * NP VP + |. . > . . . .| [2:2] NP -> * NP PP + |. . . . > . .| [4:4] NP -> * Det Noun + |. . [------> . . .| [2:3] S -> NP * VP + |. . [------> . . .| [2:3] NP -> NP * PP + |. . . . [------> .| [4:5] NP -> Det * Noun + |. . . . [-------------]| [4:6] NP -> Det Noun * + |. . . [--------------------]| [3:6] PP -> 'with' NP * + |. [----------------------------------]| [1:6] VP -> VP PP * + *** SWITCH TO TOP DOWN + |. . > . . . .| [2:2] NP -> * Det Noun + |. . . . > . .| [4:4] NP -> * NP PP + |. . . > . . .| [3:3] VP -> * VP PP + |. . . > . . .| [3:3] VP -> * Verb NP + |. . . > . . .| [3:3] VP -> * Verb + |[=========================================]| [0:6] S -> NP VP * + |. [---------------------------------->| [1:6] VP -> VP * PP + |. . [---------------------------]| [2:6] NP -> NP PP * + |. . . . [------------->| [4:6] NP -> NP * PP + |. [----------------------------------]| [1:6] VP -> Verb NP * + |. . [--------------------------->| [2:6] S -> NP * VP + |. . [--------------------------->| [2:6] NP -> NP * PP + |[=========================================]| [0:6] S -> NP VP * + |. [---------------------------------->| [1:6] VP -> VP * PP + |. . . . . . >| [6:6] VP -> * VP PP + |. . . . . . >| [6:6] VP -> * Verb NP + |. . . . . . >| [6:6] VP -> * Verb + *** SWITCH TO BOTTOM UP + |. . . . > . .| [4:4] S -> * NP VP + |. . . . [------------->| [4:6] S -> NP * VP + *** SWITCH TO TOP DOWN + *** SWITCH TO BOTTOM UP + *** SWITCH TO TOP DOWN + *** SWITCH TO BOTTOM UP + *** SWITCH TO TOP DOWN + *** SWITCH TO BOTTOM UP + Nr edges in chart: 61 + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + + + +Unit tests for the Incremental Chart Parser class +------------------------------------------------- + +The incremental chart parsers are defined in earleychart.py. +We use the demo() function for testing. We must turn off showing of times. + + >>> import nltk + +Earley Chart Parser + + >>> nltk.parse.earleychart.demo(print_times=False, trace=1, + ... sent='I saw John with a dog', numparses=2) + * Sentence: + I saw John with a dog + ['I', 'saw', 'John', 'with', 'a', 'dog'] + + |. I . saw . John . with . a . dog .| + |[------] . . . . .| [0:1] 'I' + |. [------] . . . .| [1:2] 'saw' + |. . [------] . . .| [2:3] 'John' + |. . . [------] . .| [3:4] 'with' + |. . . . [------] .| [4:5] 'a' + |. . . . . [------]| [5:6] 'dog' + |> . . . . . .| [0:0] S -> * NP VP + |> . . . . . .| [0:0] NP -> * NP PP + |> . . . . . .| [0:0] NP -> * Det Noun + |> . . . . . .| [0:0] NP -> * 'I' + |[------] . . . . .| [0:1] NP -> 'I' * + |[------> . . . . .| [0:1] S -> NP * VP + |[------> . . . . .| [0:1] NP -> NP * PP + |. > . . . . .| [1:1] VP -> * VP PP + |. > . . . . .| [1:1] VP -> * Verb NP + |. > . . . . .| [1:1] VP -> * Verb + |. > . . . . .| [1:1] Verb -> * 'saw' + |. [------] . . . .| [1:2] Verb -> 'saw' * + |. [------> . . . .| [1:2] VP -> Verb * NP + |. [------] . . . .| [1:2] VP -> Verb * + |[-------------] . . . .| [0:2] S -> NP VP * + |. [------> . . . .| [1:2] VP -> VP * PP + |. . > . . . .| [2:2] NP -> * NP PP + |. . > . . . .| [2:2] NP -> * Det Noun + |. . > . . . .| [2:2] NP -> * 'John' + |. . [------] . . .| [2:3] NP -> 'John' * + |. [-------------] . . .| [1:3] VP -> Verb NP * + |. . [------> . . .| [2:3] NP -> NP * PP + |. . . > . . .| [3:3] PP -> * 'with' NP + |[--------------------] . . .| [0:3] S -> NP VP * + |. [-------------> . . .| [1:3] VP -> VP * PP + |. . . [------> . .| [3:4] PP -> 'with' * NP + |. . . . > . .| [4:4] NP -> * NP PP + |. . . . > . .| [4:4] NP -> * Det Noun + |. . . . > . .| [4:4] Det -> * 'a' + |. . . . [------] .| [4:5] Det -> 'a' * + |. . . . [------> .| [4:5] NP -> Det * Noun + |. . . . . > .| [5:5] Noun -> * 'dog' + |. . . . . [------]| [5:6] Noun -> 'dog' * + |. . . . [-------------]| [4:6] NP -> Det Noun * + |. . . [--------------------]| [3:6] PP -> 'with' NP * + |. . . . [------------->| [4:6] NP -> NP * PP + |. . [---------------------------]| [2:6] NP -> NP PP * + |. [----------------------------------]| [1:6] VP -> VP PP * + |[=========================================]| [0:6] S -> NP VP * + |. [---------------------------------->| [1:6] VP -> VP * PP + |. [----------------------------------]| [1:6] VP -> Verb NP * + |. . [--------------------------->| [2:6] NP -> NP * PP + |[=========================================]| [0:6] S -> NP VP * + |. [---------------------------------->| [1:6] VP -> VP * PP + (S + (NP I) + (VP (VP (Verb saw) (NP John)) (PP with (NP (Det a) (Noun dog))))) + (S + (NP I) + (VP (Verb saw) (NP (NP John) (PP with (NP (Det a) (Noun dog)))))) + + +Unit tests for LARGE context-free grammars +------------------------------------------ + +Reading the ATIS grammar. + + >>> grammar = nltk.data.load('grammars/large_grammars/atis.cfg') + >>> grammar + + +Reading the test sentences. + + >>> sentences = nltk.data.load('grammars/large_grammars/atis_sentences.txt') + >>> sentences = nltk.parse.util.extract_test_sentences(sentences) + >>> len(sentences) + 98 + >>> testsentence = sentences[22] + >>> testsentence[0] + ['show', 'me', 'northwest', 'flights', 'to', 'detroit', '.'] + >>> testsentence[1] + 17 + >>> sentence = testsentence[0] + +Now we test all different parsing strategies. +Note that the number of edges differ between the strategies. + +Bottom-up parsing. + + >>> parser = nltk.parse.BottomUpChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 7661 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Bottom-up Left-corner parsing. + + >>> parser = nltk.parse.BottomUpLeftCornerChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 4986 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Left-corner parsing with bottom-up filter. + + >>> parser = nltk.parse.LeftCornerChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 1342 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Top-down parsing. + + >>> parser = nltk.parse.TopDownChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 28352 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Incremental Bottom-up parsing. + + >>> parser = nltk.parse.IncrementalBottomUpChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 7661 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Incremental Bottom-up Left-corner parsing. + + >>> parser = nltk.parse.IncrementalBottomUpLeftCornerChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 4986 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Incremental Left-corner parsing with bottom-up filter. + + >>> parser = nltk.parse.IncrementalLeftCornerChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 1342 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Incremental Top-down parsing. + + >>> parser = nltk.parse.IncrementalTopDownChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 28352 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + +Earley parsing. This is similar to the incremental top-down algorithm. + + >>> parser = nltk.parse.EarleyChartParser(grammar) + >>> chart = parser.chart_parse(sentence) + >>> print((chart.num_edges())) + 28352 + >>> print((len(list(chart.parses(grammar.start()))))) + 17 + + +Unit tests for the Probabilistic CFG class +------------------------------------------ + + >>> from nltk.corpus import treebank + >>> from itertools import islice + >>> from nltk.grammar import PCFG, induce_pcfg + >>> toy_pcfg1 = PCFG.fromstring(""" + ... S -> NP VP [1.0] + ... NP -> Det N [0.5] | NP PP [0.25] | 'John' [0.1] | 'I' [0.15] + ... Det -> 'the' [0.8] | 'my' [0.2] + ... N -> 'man' [0.5] | 'telescope' [0.5] + ... VP -> VP PP [0.1] | V NP [0.7] | V [0.2] + ... V -> 'ate' [0.35] | 'saw' [0.65] + ... PP -> P NP [1.0] + ... P -> 'with' [0.61] | 'under' [0.39] + ... """) + + >>> toy_pcfg2 = PCFG.fromstring(""" + ... S -> NP VP [1.0] + ... VP -> V NP [.59] + ... VP -> V [.40] + ... VP -> VP PP [.01] + ... NP -> Det N [.41] + ... NP -> Name [.28] + ... NP -> NP PP [.31] + ... PP -> P NP [1.0] + ... V -> 'saw' [.21] + ... V -> 'ate' [.51] + ... V -> 'ran' [.28] + ... N -> 'boy' [.11] + ... N -> 'cookie' [.12] + ... N -> 'table' [.13] + ... N -> 'telescope' [.14] + ... N -> 'hill' [.5] + ... Name -> 'Jack' [.52] + ... Name -> 'Bob' [.48] + ... P -> 'with' [.61] + ... P -> 'under' [.39] + ... Det -> 'the' [.41] + ... Det -> 'a' [.31] + ... Det -> 'my' [.28] + ... """) + +Create a set of PCFG productions. + + >>> grammar = PCFG.fromstring(""" + ... A -> B B [.3] | C B C [.7] + ... B -> B D [.5] | C [.5] + ... C -> 'a' [.1] | 'b' [0.9] + ... D -> 'b' [1.0] + ... """) + >>> prod = grammar.productions()[0] + >>> prod + A -> B B [0.3] + + >>> prod.lhs() + A + + >>> prod.rhs() + (B, B) + + >>> print((prod.prob())) + 0.3 + + >>> grammar.start() + A + + >>> grammar.productions() + [A -> B B [0.3], A -> C B C [0.7], B -> B D [0.5], B -> C [0.5], C -> 'a' [0.1], C -> 'b' [0.9], D -> 'b' [1.0]] + +Induce some productions using parsed Treebank data. + + >>> productions = [] + >>> for fileid in treebank.fileids()[:2]: + ... for t in treebank.parsed_sents(fileid): + ... productions += t.productions() + + >>> grammar = induce_pcfg(S, productions) + >>> grammar + + + >>> sorted(grammar.productions(lhs=Nonterminal('PP')))[:2] + [PP -> IN NP [1.0]] + >>> sorted(grammar.productions(lhs=Nonterminal('NNP')))[:2] + [NNP -> 'Agnew' [0.0714286], NNP -> 'Consolidated' [0.0714286]] + >>> sorted(grammar.productions(lhs=Nonterminal('JJ')))[:2] + [JJ -> 'British' [0.142857], JJ -> 'former' [0.142857]] + >>> sorted(grammar.productions(lhs=Nonterminal('NP')))[:2] + [NP -> CD NNS [0.133333], NP -> DT JJ JJ NN [0.0666667]] + +Unit tests for the Probabilistic Chart Parse classes +---------------------------------------------------- + + >>> tokens = "Jack saw Bob with my cookie".split() + >>> grammar = toy_pcfg2 + >>> print(grammar) + Grammar with 23 productions (start state = S) + S -> NP VP [1.0] + VP -> V NP [0.59] + VP -> V [0.4] + VP -> VP PP [0.01] + NP -> Det N [0.41] + NP -> Name [0.28] + NP -> NP PP [0.31] + PP -> P NP [1.0] + V -> 'saw' [0.21] + V -> 'ate' [0.51] + V -> 'ran' [0.28] + N -> 'boy' [0.11] + N -> 'cookie' [0.12] + N -> 'table' [0.13] + N -> 'telescope' [0.14] + N -> 'hill' [0.5] + Name -> 'Jack' [0.52] + Name -> 'Bob' [0.48] + P -> 'with' [0.61] + P -> 'under' [0.39] + Det -> 'the' [0.41] + Det -> 'a' [0.31] + Det -> 'my' [0.28] + +Create several parsers using different queuing strategies and show the +resulting parses. + + >>> from nltk.parse import pchart + + >>> parser = pchart.InsideChartParser(grammar) + >>> for t in parser.parse(tokens): + ... print(t) + (S + (NP (Name Jack)) + (VP + (V saw) + (NP + (NP (Name Bob)) + (PP (P with) (NP (Det my) (N cookie)))))) (p=6.31607e-06) + (S + (NP (Name Jack)) + (VP + (VP (V saw) (NP (Name Bob))) + (PP (P with) (NP (Det my) (N cookie))))) (p=2.03744e-07) + + >>> parser = pchart.RandomChartParser(grammar) + >>> for t in parser.parse(tokens): + ... print(t) + (S + (NP (Name Jack)) + (VP + (V saw) + (NP + (NP (Name Bob)) + (PP (P with) (NP (Det my) (N cookie)))))) (p=6.31607e-06) + (S + (NP (Name Jack)) + (VP + (VP (V saw) (NP (Name Bob))) + (PP (P with) (NP (Det my) (N cookie))))) (p=2.03744e-07) + + >>> parser = pchart.UnsortedChartParser(grammar) + >>> for t in parser.parse(tokens): + ... print(t) + (S + (NP (Name Jack)) + (VP + (V saw) + (NP + (NP (Name Bob)) + (PP (P with) (NP (Det my) (N cookie)))))) (p=6.31607e-06) + (S + (NP (Name Jack)) + (VP + (VP (V saw) (NP (Name Bob))) + (PP (P with) (NP (Det my) (N cookie))))) (p=2.03744e-07) + + >>> parser = pchart.LongestChartParser(grammar) + >>> for t in parser.parse(tokens): + ... print(t) + (S + (NP (Name Jack)) + (VP + (V saw) + (NP + (NP (Name Bob)) + (PP (P with) (NP (Det my) (N cookie)))))) (p=6.31607e-06) + (S + (NP (Name Jack)) + (VP + (VP (V saw) (NP (Name Bob))) + (PP (P with) (NP (Det my) (N cookie))))) (p=2.03744e-07) + + >>> parser = pchart.InsideChartParser(grammar, beam_size = len(tokens)+1) + >>> for t in parser.parse(tokens): + ... print(t) + + +Unit tests for the Viterbi Parse classes +---------------------------------------- + + >>> from nltk.parse import ViterbiParser + >>> tokens = "Jack saw Bob with my cookie".split() + >>> grammar = toy_pcfg2 + +Parse the tokenized sentence. + + >>> parser = ViterbiParser(grammar) + >>> for t in parser.parse(tokens): + ... print(t) + (S + (NP (Name Jack)) + (VP + (V saw) + (NP + (NP (Name Bob)) + (PP (P with) (NP (Det my) (N cookie)))))) (p=6.31607e-06) + + +Unit tests for the FeatStructNonterminal class +---------------------------------------------- + + >>> from nltk.grammar import FeatStructNonterminal + >>> FeatStructNonterminal( + ... pos='n', agr=FeatStructNonterminal(number='pl', gender='f')) + [agr=[gender='f', number='pl'], pos='n'] + + >>> FeatStructNonterminal('VP[+fin]/NP[+pl]') + VP[+fin]/NP[+pl] + + +Tracing the Feature Chart Parser +-------------------------------- + +We use the featurechart.demo() function for tracing the Feature Chart Parser. + + >>> nltk.parse.featurechart.demo(print_times=False, + ... print_grammar=True, + ... parser=nltk.parse.featurechart.FeatureChartParser, + ... sent='I saw John with a dog') + + Grammar with 18 productions (start state = S[]) + S[] -> NP[] VP[] + PP[] -> Prep[] NP[] + NP[] -> NP[] PP[] + VP[] -> VP[] PP[] + VP[] -> Verb[] NP[] + VP[] -> Verb[] + NP[] -> Det[pl=?x] Noun[pl=?x] + NP[] -> 'John' + NP[] -> 'I' + Det[] -> 'the' + Det[] -> 'my' + Det[-pl] -> 'a' + Noun[-pl] -> 'dog' + Noun[-pl] -> 'cookie' + Verb[] -> 'ate' + Verb[] -> 'saw' + Prep[] -> 'with' + Prep[] -> 'under' + + * FeatureChartParser + Sentence: I saw John with a dog + |.I.s.J.w.a.d.| + |[-] . . . . .| [0:1] 'I' + |. [-] . . . .| [1:2] 'saw' + |. . [-] . . .| [2:3] 'John' + |. . . [-] . .| [3:4] 'with' + |. . . . [-] .| [4:5] 'a' + |. . . . . [-]| [5:6] 'dog' + |[-] . . . . .| [0:1] NP[] -> 'I' * + |[-> . . . . .| [0:1] S[] -> NP[] * VP[] {} + |[-> . . . . .| [0:1] NP[] -> NP[] * PP[] {} + |. [-] . . . .| [1:2] Verb[] -> 'saw' * + |. [-> . . . .| [1:2] VP[] -> Verb[] * NP[] {} + |. [-] . . . .| [1:2] VP[] -> Verb[] * + |. [-> . . . .| [1:2] VP[] -> VP[] * PP[] {} + |[---] . . . .| [0:2] S[] -> NP[] VP[] * + |. . [-] . . .| [2:3] NP[] -> 'John' * + |. . [-> . . .| [2:3] S[] -> NP[] * VP[] {} + |. . [-> . . .| [2:3] NP[] -> NP[] * PP[] {} + |. [---] . . .| [1:3] VP[] -> Verb[] NP[] * + |. [---> . . .| [1:3] VP[] -> VP[] * PP[] {} + |[-----] . . .| [0:3] S[] -> NP[] VP[] * + |. . . [-] . .| [3:4] Prep[] -> 'with' * + |. . . [-> . .| [3:4] PP[] -> Prep[] * NP[] {} + |. . . . [-] .| [4:5] Det[-pl] -> 'a' * + |. . . . [-> .| [4:5] NP[] -> Det[pl=?x] * Noun[pl=?x] {?x: False} + |. . . . . [-]| [5:6] Noun[-pl] -> 'dog' * + |. . . . [---]| [4:6] NP[] -> Det[-pl] Noun[-pl] * + |. . . . [--->| [4:6] S[] -> NP[] * VP[] {} + |. . . . [--->| [4:6] NP[] -> NP[] * PP[] {} + |. . . [-----]| [3:6] PP[] -> Prep[] NP[] * + |. . [-------]| [2:6] NP[] -> NP[] PP[] * + |. [---------]| [1:6] VP[] -> VP[] PP[] * + |. [--------->| [1:6] VP[] -> VP[] * PP[] {} + |[===========]| [0:6] S[] -> NP[] VP[] * + |. . [------->| [2:6] S[] -> NP[] * VP[] {} + |. . [------->| [2:6] NP[] -> NP[] * PP[] {} + |. [---------]| [1:6] VP[] -> Verb[] NP[] * + |. [--------->| [1:6] VP[] -> VP[] * PP[] {} + |[===========]| [0:6] S[] -> NP[] VP[] * + (S[] + (NP[] I) + (VP[] + (VP[] (Verb[] saw) (NP[] John)) + (PP[] (Prep[] with) (NP[] (Det[-pl] a) (Noun[-pl] dog))))) + (S[] + (NP[] I) + (VP[] + (Verb[] saw) + (NP[] + (NP[] John) + (PP[] (Prep[] with) (NP[] (Det[-pl] a) (Noun[-pl] dog)))))) + + +Unit tests for the Feature Chart Parser classes +----------------------------------------------- + +The list of parsers we want to test. + + >>> parsers = [nltk.parse.featurechart.FeatureChartParser, + ... nltk.parse.featurechart.FeatureTopDownChartParser, + ... nltk.parse.featurechart.FeatureBottomUpChartParser, + ... nltk.parse.featurechart.FeatureBottomUpLeftCornerChartParser, + ... nltk.parse.earleychart.FeatureIncrementalChartParser, + ... nltk.parse.earleychart.FeatureEarleyChartParser, + ... nltk.parse.earleychart.FeatureIncrementalTopDownChartParser, + ... nltk.parse.earleychart.FeatureIncrementalBottomUpChartParser, + ... nltk.parse.earleychart.FeatureIncrementalBottomUpLeftCornerChartParser, + ... ] + +A helper function that tests each parser on the given grammar and sentence. +We check that the number of trees are correct, and that all parsers +return the same trees. Otherwise an error is printed. + + >>> def unittest(grammar, sentence, nr_trees): + ... sentence = sentence.split() + ... trees = None + ... for P in parsers: + ... result = P(grammar).parse(sentence) + ... result = set(tree.freeze() for tree in result) + ... if len(result) != nr_trees: + ... print("Wrong nr of trees:", len(result)) + ... elif trees is None: + ... trees = result + ... elif result != trees: + ... print("Trees differ for parser:", P.__name__) + +The demo grammar from before, with an ambiguous sentence. + + >>> isawjohn = nltk.parse.featurechart.demo_grammar() + >>> unittest(isawjohn, "I saw John with a dog with my cookie", 5) + +This grammar tests that variables in different grammar rules are renamed +before unification. (The problematic variable is in this case ?X). + + >>> whatwasthat = nltk.grammar.FeatureGrammar.fromstring(''' + ... S[] -> NP[num=?N] VP[num=?N, slash=?X] + ... NP[num=?X] -> "what" + ... NP[num=?X] -> "that" + ... VP[num=?P, slash=none] -> V[num=?P] NP[] + ... V[num=sg] -> "was" + ... ''') + >>> unittest(whatwasthat, "what was that", 1) + +This grammar tests that the same rule can be used in different places +in another rule, and that the variables are properly renamed. + + >>> thislovesthat = nltk.grammar.FeatureGrammar.fromstring(''' + ... S[] -> NP[case=nom] V[] NP[case=acc] + ... NP[case=?X] -> Pron[case=?X] + ... Pron[] -> "this" + ... Pron[] -> "that" + ... V[] -> "loves" + ... ''') + >>> unittest(thislovesthat, "this loves that", 1) + + +Tests for loading feature grammar files +--------------------------------------- + +Alternative 1: first load the grammar, then create the parser. + + >>> fcfg = nltk.data.load('grammars/book_grammars/feat0.fcfg') + >>> fcp1 = nltk.parse.FeatureChartParser(fcfg) + >>> print((type(fcp1))) + + +Alternative 2: directly load the parser. + + >>> fcp2 = nltk.parse.load_parser('grammars/book_grammars/feat0.fcfg') + >>> print((type(fcp2))) + diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en.doctest new file mode 100644 index 0000000..f5ec022 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en.doctest @@ -0,0 +1,568 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================================== +Examples for Portuguese Processing +================================== + +This HOWTO contains a variety of examples relating to the Portuguese language. +It is intended to be read in conjunction with the NLTK book +(``https://www.nltk.org/book/``). For instructions on running the Python +interpreter, please see the section *Getting Started with Python*, in Chapter 1. + +-------------------------------------------- +Python Programming, with Portuguese Examples +-------------------------------------------- + +Chapter 1 of the NLTK book contains many elementary programming examples, all +with English texts. In this section, we'll see some corresponding examples +using Portuguese. Please refer to the chapter for full discussion. *Vamos!* + + >>> from nltk.test.portuguese_en_fixt import setup_module + >>> setup_module() + + >>> from nltk.examples.pt import * + *** Introductory Examples for the NLTK Book *** + Loading ptext1, ... and psent1, ... + Type the name of the text or sentence to view it. + Type: 'texts()' or 'sents()' to list the materials. + ptext1: Memórias Póstumas de Brás Cubas (1881) + ptext2: Dom Casmurro (1899) + ptext3: Gênesis + ptext4: Folha de Sao Paulo (1994) + + +Any time we want to find out about these texts, we just have +to enter their names at the Python prompt: + + >>> ptext2 + + +Searching Text +-------------- + +A concordance permits us to see words in context. + + >>> ptext1.concordance('olhos') + Building index... + Displaying 25 of 138 matches: + De pé , à cabeceira da cama , com os olhos estúpidos , a boca entreaberta , a t + orelhas . Pela minha parte fechei os olhos e deixei - me ir à ventura . Já agor + xões de cérebro enfermo . Como ia de olhos fechados , não via o caminho ; lembr + gelos eternos . Com efeito , abri os olhos e vi que o meu animal galopava numa + me apareceu então , fitando - me uns olhos rutilantes como o sol . Tudo nessa f + mim mesmo . Então , encarei - a com olhos súplices , e pedi mais alguns anos . + ... + +For a given word, we can find words with a similar text distribution: + + >>> ptext1.similar('chegar') + Building word-context index... + acabada acudir aludir avistar bramanismo casamento cheguei com contar + contrário corpo dali deixei desferirem dizer fazer filhos já leitor lhe + >>> ptext3.similar('chegar') + Building word-context index... + achar alumiar arrombar destruir governar guardar ir lavrar passar que + toda tomar ver vir + +We can search for the statistically significant collocations in a text: + + >>> ptext1.collocations() + Building collocations list + Quincas Borba; Lobo Neves; alguma coisa; Brás Cubas; meu pai; dia + seguinte; não sei; Meu pai; alguns instantes; outra vez; outra coisa; + por exemplo; mim mesmo; coisa nenhuma; mesma coisa; não era; dias + depois; Passeio Público; olhar para; das coisas + +We can search for words in context, with the help of *regular expressions*, e.g.: + + >>> ptext1.findall(" (<.*>)") + estúpidos; e; fechados; rutilantes; súplices; a; do; babavam; + na; moles; se; da; umas; espraiavam; chamejantes; espetados; + ... + +We can automatically generate random text based on a given text, e.g.: + + >>> ptext3.generate() # doctest: +SKIP + No princípio , criou Deus os abençoou , dizendo : Onde { estão } e até + à ave dos céus , { que } será . Disse mais Abrão : Dá - me a mulher + que tomaste ; porque daquele poço Eseque , { tinha .} E disse : Não + poderemos descer ; mas , do campo ainda não estava na casa do teu + pescoço . E viveu Serugue , depois Simeão e Levi { são } estes ? E o + varão , porque habitava na terra de Node , da mão de Esaú : Jeús , + Jalão e Corá + +Texts as List of Words +---------------------- + +A few sentences have been defined for you. + + >>> psent1 + ['o', 'amor', 'da', 'gl\xf3ria', 'era', 'a', 'coisa', 'mais', + 'verdadeiramente', 'humana', 'que', 'h\xe1', 'no', 'homem', ',', + 'e', ',', 'conseq\xfcentemente', ',', 'a', 'sua', 'mais', + 'genu\xedna', 'fei\xe7\xe3o', '.'] + >>> + +Notice that the sentence has been *tokenized*. Each token is +represented as a string, represented using quotes, e.g. ``'coisa'``. +Some strings contain special characters, e.g. ``\xf3``, +the internal representation for ó. +The tokens are combined in the form of a *list*. How long is this list? + + >>> len(psent1) + 25 + >>> + +What is the vocabulary of this sentence? + + >>> sorted(set(psent1)) + [',', '.', 'a', 'amor', 'coisa', 'conseqüentemente', 'da', 'e', 'era', + 'feição', 'genuína', 'glória', 'homem', 'humana', 'há', 'mais', 'no', + 'o', 'que', 'sua', 'verdadeiramente'] + >>> + +Let's iterate over each item in ``psent2``, and print information for each: + + >>> for w in psent2: + ... print(w, len(w), w[-1]) + ... + Não 3 o + consultes 9 s + dicionários 11 s + . 1 . + +Observe how we make a human-readable version of a string, using ``decode()``. +Also notice that we accessed the last character of a string ``w`` using ``w[-1]``. + +We just saw a ``for`` loop above. Another useful control structure is a +*list comprehension*. + + >>> [w.upper() for w in psent2] + ['N\xc3O', 'CONSULTES', 'DICION\xc1RIOS', '.'] + >>> [w for w in psent1 if w.endswith('a')] + ['da', 'gl\xf3ria', 'era', 'a', 'coisa', 'humana', 'a', 'sua', 'genu\xedna'] + >>> [w for w in ptext4 if len(w) > 15] + ['norte-irlandeses', 'pan-nacionalismo', 'predominatemente', 'primeiro-ministro', + 'primeiro-ministro', 'irlandesa-americana', 'responsabilidades', 'significativamente'] + +We can examine the relative frequency of words in a text, using ``FreqDist``: + + >>> fd1 = FreqDist(ptext1) + >>> fd1 + + >>> fd1['olhos'] + 137 + >>> fd1.max() + ',' + >>> fd1.samples()[:100] + [',', '.', 'a', 'que', 'de', 'e', '-', 'o', ';', 'me', 'um', 'n\xe3o', + '\x97', 'se', 'do', 'da', 'uma', 'com', 'os', '\xe9', 'era', 'as', 'eu', + 'lhe', 'ao', 'em', 'para', 'mas', '...', '!', '\xe0', 'na', 'mais', '?', + 'no', 'como', 'por', 'N\xe3o', 'dos', 'o', 'ele', ':', 'Virg\xedlia', + 'me', 'disse', 'minha', 'das', 'O', '/', 'A', 'CAP\xcdTULO', 'muito', + 'depois', 'coisa', 'foi', 'sem', 'olhos', 'ela', 'nos', 'tinha', 'nem', + 'E', 'outro', 'vida', 'nada', 'tempo', 'menos', 'outra', 'casa', 'homem', + 'porque', 'quando', 'mim', 'mesmo', 'ser', 'pouco', 'estava', 'dia', + 't\xe3o', 'tudo', 'Mas', 'at\xe9', 'D', 'ainda', 's\xf3', 'alguma', + 'la', 'vez', 'anos', 'h\xe1', 'Era', 'pai', 'esse', 'lo', 'dizer', 'assim', + 'ent\xe3o', 'dizia', 'aos', 'Borba'] + +--------------- +Reading Corpora +--------------- + +Accessing the Machado Text Corpus +--------------------------------- + +NLTK includes the complete works of Machado de Assis. + + >>> from nltk.corpus import machado + >>> machado.fileids() + ['contos/macn001.txt', 'contos/macn002.txt', 'contos/macn003.txt', ...] + +Each file corresponds to one of the works of Machado de Assis. To see a complete +list of works, you can look at the corpus README file: ``print machado.readme()``. +Let's access the text of the *Posthumous Memories of Brás Cubas*. + +We can access the text as a list of characters, and access 200 characters starting +from position 10,000. + + >>> raw_text = machado.raw('romance/marm05.txt') + >>> raw_text[10000:10200] + u', primou no\nEstado, e foi um dos amigos particulares do vice-rei Conde + da Cunha.\n\nComo este apelido de Cubas lhe\ncheirasse excessivamente a + tanoaria, alegava meu pai, bisneto de Dami\xe3o, que o\ndito ape' + +However, this is not a very useful way to work with a text. We generally think +of a text as a sequence of words and punctuation, not characters: + + >>> text1 = machado.words('romance/marm05.txt') + >>> text1 + ['Romance', ',', 'Mem\xf3rias', 'P\xf3stumas', 'de', ...] + >>> len(text1) + 77098 + >>> len(set(text1)) + 10848 + +Here's a program that finds the most common ngrams that contain a +particular target word. + + >>> from nltk import ngrams, FreqDist + >>> target_word = 'olhos' + >>> fd = FreqDist(ng + ... for ng in ngrams(text1, 5) + ... if target_word in ng) + >>> for hit in fd.samples(): + ... print(' '.join(hit)) + ... + , com os olhos no + com os olhos no ar + com os olhos no chão + e todos com os olhos + me estar com os olhos + os olhos estúpidos , a + os olhos na costura , + os olhos no ar , + , com os olhos espetados + , com os olhos estúpidos + , com os olhos fitos + , com os olhos naquele + , com os olhos para + + +Accessing the MacMorpho Tagged Corpus +------------------------------------- + +NLTK includes the MAC-MORPHO Brazilian Portuguese POS-tagged news text, +with over a million words of +journalistic texts extracted from ten sections of +the daily newspaper *Folha de Sao Paulo*, 1994. + +We can access this corpus as a sequence of words or tagged words as follows: + + >>> import nltk.corpus + >>> nltk.corpus.mac_morpho.words() + ['Jersei', 'atinge', 'm\xe9dia', 'de', 'Cr$', '1,4', ...] + >>> nltk.corpus.mac_morpho.sents() + [['Jersei', 'atinge', 'm\xe9dia', 'de', 'Cr$', '1,4', 'milh\xe3o', + 'em', 'a', 'venda', 'de', 'a', 'Pinhal', 'em', 'S\xe3o', 'Paulo'], + ['Programe', 'sua', 'viagem', 'a', 'a', 'Exposi\xe7\xe3o', 'Nacional', + 'do', 'Zeb', ',', 'que', 'come\xe7a', 'dia', '25'], ...] + >>> nltk.corpus.mac_morpho.tagged_words() + [('Jersei', 'N'), ('atinge', 'V'), ('m\xe9dia', 'N'), ...] + +We can also access it in sentence chunks. + + >>> nltk.corpus.mac_morpho.tagged_sents() + [[('Jersei', 'N'), ('atinge', 'V'), ('m\xe9dia', 'N'), ('de', 'PREP'), + ('Cr$', 'CUR'), ('1,4', 'NUM'), ('milh\xe3o', 'N'), ('em', 'PREP|+'), + ('a', 'ART'), ('venda', 'N'), ('de', 'PREP|+'), ('a', 'ART'), + ('Pinhal', 'NPROP'), ('em', 'PREP'), ('S\xe3o', 'NPROP'), + ('Paulo', 'NPROP')], + [('Programe', 'V'), ('sua', 'PROADJ'), ('viagem', 'N'), ('a', 'PREP|+'), + ('a', 'ART'), ('Exposi\xe7\xe3o', 'NPROP'), ('Nacional', 'NPROP'), + ('do', 'NPROP'), ('Zeb', 'NPROP'), (',', ','), ('que', 'PRO-KS-REL'), + ('come\xe7a', 'V'), ('dia', 'N'), ('25', 'N|AP')], ...] + +This data can be used to train taggers (examples below for the Floresta treebank). + +Accessing the Floresta Portuguese Treebank +------------------------------------------ + +The NLTK data distribution includes the +"Floresta Sinta(c)tica Corpus" version 7.4, available from +``https://www.linguateca.pt/Floresta/``. + +We can access this corpus as a sequence of words or tagged words as follows: + + >>> from nltk.corpus import floresta + >>> floresta.words() + ['Um', 'revivalismo', 'refrescante', 'O', '7_e_Meio', ...] + >>> floresta.tagged_words() + [('Um', '>N+art'), ('revivalismo', 'H+n'), ...] + +The tags consist of some syntactic information, followed by a plus sign, +followed by a conventional part-of-speech tag. Let's strip off the material before +the plus sign: + + >>> def simplify_tag(t): + ... if "+" in t: + ... return t[t.index("+")+1:] + ... else: + ... return t + >>> twords = floresta.tagged_words() + >>> twords = [(w.lower(), simplify_tag(t)) for (w,t) in twords] + >>> twords[:10] + [('um', 'art'), ('revivalismo', 'n'), ('refrescante', 'adj'), ('o', 'art'), ('7_e_meio', 'prop'), + ('\xe9', 'v-fin'), ('um', 'art'), ('ex-libris', 'n'), ('de', 'prp'), ('a', 'art')] + +Pretty printing the tagged words: + + >>> print(' '.join(word + '/' + tag for (word, tag) in twords[:10])) + um/art revivalismo/n refrescante/adj o/art 7_e_meio/prop é/v-fin um/art ex-libris/n de/prp a/art + +Count the word tokens and types, and determine the most common word: + + >>> words = floresta.words() + >>> len(words) + 211852 + >>> fd = nltk.FreqDist(words) + >>> len(fd) + 29421 + >>> fd.max() + 'de' + +List the 20 most frequent tags, in order of decreasing frequency: + + >>> tags = [simplify_tag(tag) for (word,tag) in floresta.tagged_words()] + >>> fd = nltk.FreqDist(tags) + >>> fd.keys()[:20] + ['n', 'prp', 'art', 'v-fin', ',', 'prop', 'adj', 'adv', '.', + 'conj-c', 'v-inf', 'pron-det', 'v-pcp', 'num', 'pron-indp', + 'pron-pers', '\xab', '\xbb', 'conj-s', '}'] + +We can also access the corpus grouped by sentence: + + >>> floresta.sents() + [['Um', 'revivalismo', 'refrescante'], + ['O', '7_e_Meio', '\xe9', 'um', 'ex-libris', 'de', 'a', 'noite', + 'algarvia', '.'], ...] + >>> floresta.tagged_sents() + [[('Um', '>N+art'), ('revivalismo', 'H+n'), ('refrescante', 'N<+adj')], + [('O', '>N+art'), ('7_e_Meio', 'H+prop'), ('\xe9', 'P+v-fin'), + ('um', '>N+art'), ('ex-libris', 'H+n'), ('de', 'H+prp'), + ('a', '>N+art'), ('noite', 'H+n'), ('algarvia', 'N<+adj'), ('.', '.')], + ...] + >>> floresta.parsed_sents() + [Tree('UTT+np', [Tree('>N+art', ['Um']), Tree('H+n', ['revivalismo']), + Tree('N<+adj', ['refrescante'])]), + Tree('STA+fcl', + [Tree('SUBJ+np', [Tree('>N+art', ['O']), + Tree('H+prop', ['7_e_Meio'])]), + Tree('P+v-fin', ['\xe9']), + Tree('SC+np', + [Tree('>N+art', ['um']), + Tree('H+n', ['ex-libris']), + Tree('N<+pp', [Tree('H+prp', ['de']), + Tree('P<+np', [Tree('>N+art', ['a']), + Tree('H+n', ['noite']), + Tree('N<+adj', ['algarvia'])])])]), + Tree('.', ['.'])]), ...] + +To view a parse tree, use the ``draw()`` method, e.g.: + + >>> psents = floresta.parsed_sents() + >>> psents[5].draw() # doctest: +SKIP + +Character Encodings +------------------- + +Python understands the common character encoding used for Portuguese, ISO 8859-1 (ISO Latin 1). + + >>> import os, nltk.test + >>> testdir = os.path.split(nltk.test.__file__)[0] + >>> text = open(os.path.join(testdir, 'floresta.txt'), 'rb').read().decode('ISO 8859-1') + >>> text[:60] + 'O 7 e Meio \xe9 um ex-libris da noite algarvia.\n\xc9 uma das mais ' + >>> print(text[:60]) + O 7 e Meio é um ex-libris da noite algarvia. + É uma das mais + +For more information about character encodings and Python, please see section 3.3 of the book. + +---------------- +Processing Tasks +---------------- + + +Simple Concordancing +-------------------- + +Here's a function that takes a word and a specified amount of context (measured +in characters), and generates a concordance for that word. + + >>> def concordance(word, context=30): + ... for sent in floresta.sents(): + ... if word in sent: + ... pos = sent.index(word) + ... left = ' '.join(sent[:pos]) + ... right = ' '.join(sent[pos+1:]) + ... print('%*s %s %-*s' % + ... (context, left[-context:], word, context, right[:context])) + + >>> concordance("dar") # doctest: +SKIP + anduru , foi o suficiente para dar a volta a o resultado . + 1. O P?BLICO veio dar a a imprensa di?ria portuguesa + A fartura de pensamento pode dar maus resultados e n?s n?o quer + Come?a a dar resultados a pol?tica de a Uni + ial come?ar a incorporar- lo e dar forma a um ' site ' que tem se + r com Constantino para ele lhe dar tamb?m os pap?is assinados . + va a brincar , pois n?o lhe ia dar procura??o nenhuma enquanto n? + ?rica como o ant?doto capaz de dar sentido a o seu enorme poder . + . . . + >>> concordance("vender") # doctest: +SKIP + er recebido uma encomenda para vender 4000 blindados a o Iraque . + m?rico_Amorim caso conseguisse vender o lote de ac??es de o empres?r + mpre ter jovens simp?ticos a ? vender ? chega ! } + Disse que o governo vai vender ? desde autom?vel at? particip + ndiciou ontem duas pessoas por vender carro com ?gio . + A inten??o de Fleury ? vender as a??es para equilibrar as fi + +Part-of-Speech Tagging +---------------------- + +Let's begin by getting the tagged sentence data, and simplifying the tags +as described earlier. + + >>> from nltk.corpus import floresta + >>> tsents = floresta.tagged_sents() + >>> tsents = [[(w.lower(),simplify_tag(t)) for (w,t) in sent] for sent in tsents if sent] + >>> train = tsents[100:] + >>> test = tsents[:100] + +We already know that ``n`` is the most common tag, so we can set up a +default tagger that tags every word as a noun, and see how well it does: + + >>> tagger0 = nltk.DefaultTagger('n') + >>> nltk.tag.accuracy(tagger0, test) + 0.17697228144989338 + +Evidently, about one in every six words is a noun. Let's improve on this by +training a unigram tagger: + + >>> tagger1 = nltk.UnigramTagger(train, backoff=tagger0) + >>> nltk.tag.accuracy(tagger1, test) + 0.87029140014214645 + +Next a bigram tagger: + + >>> tagger2 = nltk.BigramTagger(train, backoff=tagger1) + >>> nltk.tag.accuracy(tagger2, test) + 0.89019189765458417 + + +Sentence Segmentation +--------------------- + +Punkt is a language-neutral sentence segmentation tool. We + + >>> sent_tokenizer=nltk.data.load('tokenizers/punkt/portuguese.pickle') + >>> raw_text = machado.raw('romance/marm05.txt') + >>> sentences = sent_tokenizer.tokenize(raw_text) + >>> for sent in sentences[1000:1005]: + ... print("<<", sent, ">>") + ... + << Em verdade, parecia ainda mais mulher do que era; + seria criança nos seus folgares de moça; mas assim quieta, impassível, tinha a + compostura da mulher casada. >> + << Talvez essa circunstância lhe diminuía um pouco da + graça virginal. >> + << Depressa nos familiarizamos; a mãe fazia-lhe grandes elogios, eu + escutava-os de boa sombra, e ela sorria com os olhos fúlgidos, como se lá dentro + do cérebro lhe estivesse a voar uma borboletinha de asas de ouro e olhos de + diamante... >> + << Digo lá dentro, porque cá fora o + que esvoaçou foi uma borboleta preta, que subitamente penetrou na varanda, e + começou a bater as asas em derredor de D. Eusébia. >> + << D. Eusébia deu um grito, + levantou-se, praguejou umas palavras soltas: - T'esconjuro!... >> + +The sentence tokenizer can be trained and evaluated on other text. +The source text (from the Floresta Portuguese Treebank) contains one sentence per line. +We read the text, split it into its lines, and then join these lines together using +spaces. Now the information about sentence breaks has been discarded. We split this +material into training and testing data: + + >>> import os, nltk.test + >>> testdir = os.path.split(nltk.test.__file__)[0] + >>> text = open(os.path.join(testdir, 'floresta.txt'), 'rb').read().decode('ISO-8859-1') + >>> lines = text.split('\n') + >>> train = ' '.join(lines[10:]) + >>> test = ' '.join(lines[:10]) + +Now we train the sentence segmenter (or sentence tokenizer) and use it on our test sentences: + + >>> stok = nltk.PunktSentenceTokenizer(train) + >>> print(stok.tokenize(test)) + ['O 7 e Meio \xe9 um ex-libris da noite algarvia.', + '\xc9 uma das mais antigas discotecas do Algarve, situada em Albufeira, + que continua a manter os tra\xe7os decorativos e as clientelas de sempre.', + '\xc9 um pouco a vers\xe3o de uma esp\xe9cie de \xaboutro lado\xbb da noite, + a meio caminho entre os devaneios de uma fauna perif\xe9rica, seja de Lisboa, + Londres, Dublin ou Faro e Portim\xe3o, e a postura circunspecta dos fi\xe9is da casa, + que dela esperam a m\xfasica \xabgeracionista\xbb dos 60 ou dos 70.', + 'N\xe3o deixa de ser, nos tempos que correm, um certo \xabvery typical\xbb algarvio, + cabe\xe7a de cartaz para os que querem fugir a algumas movimenta\xe7\xf5es nocturnas + j\xe1 a caminho da ritualiza\xe7\xe3o de massas, do g\xe9nero \xabvamos todos ao + Calypso e encontramo-nos na Locomia\xbb.', + 'E assim, aos 2,5 milh\xf5es que o Minist\xe9rio do Planeamento e Administra\xe7\xe3o + do Territ\xf3rio j\xe1 gasta no pagamento do pessoal afecto a estes organismos, + v\xeam juntar-se os montantes das obras propriamente ditas, que os munic\xedpios, + j\xe1 com projectos na m\xe3o, v\xeam reivindicar junto do Executivo, como salienta + aquele membro do Governo.', + 'E o dinheiro \xabn\xe3o falta s\xf3 \xe0s c\xe2maras\xbb, lembra o secret\xe1rio de Estado, + que considera que a solu\xe7\xe3o para as autarquias \xe9 \xabespecializarem-se em + fundos comunit\xe1rios\xbb.', + 'Mas como, se muitas n\xe3o disp\xf5em, nos seus quadros, dos t\xe9cnicos necess\xe1rios?', + '\xabEncomendem-nos a projectistas de fora\xbb porque, se as obras vierem a ser financiadas, + eles at\xe9 saem de gra\xe7a, j\xe1 que, nesse caso, \xabos fundos comunit\xe1rios pagam + os projectos, o mesmo n\xe3o acontecendo quando eles s\xe3o feitos pelos GAT\xbb, + dado serem organismos do Estado.', + 'Essa poder\xe1 vir a ser uma hip\xf3tese, at\xe9 porque, no terreno, a capacidade dos GAT + est\xe1 cada vez mais enfraquecida.', + 'Alguns at\xe9 j\xe1 desapareceram, como o de Castro Verde, e outros t\xeam vindo a perder quadros.'] + +NLTK's data collection includes a trained model for Portuguese sentence +segmentation, which can be loaded as follows. It is faster to load a trained model than +to retrain it. + + >>> stok = nltk.data.load('tokenizers/punkt/portuguese.pickle') + +Stemming +-------- + +NLTK includes the RSLP Portuguese stemmer. Here we use it to stem some Portuguese text: + + >>> stemmer = nltk.stem.RSLPStemmer() + >>> stemmer.stem("copiar") + 'copi' + >>> stemmer.stem("paisagem") + 'pais' + + +Stopwords +--------- + +NLTK includes Portuguese stopwords: + + >>> stopwords = nltk.corpus.stopwords.words('portuguese') + >>> stopwords[:10] + ['a', 'ao', 'aos', 'aquela', 'aquelas', 'aquele', 'aqueles', 'aquilo', 'as', 'at\xe9'] + +Now we can use these to filter text. Let's find the most frequent words (other than stopwords) +and print them in descending order of frequency: + + >>> fd = nltk.FreqDist(w.lower() for w in floresta.words() if w not in stopwords) + >>> for word in list(fd.keys())[:20]: + ... print(word, fd[word]) + , 13444 + . 7725 + « 2369 + » 2310 + é 1305 + o 1086 + } 1047 + { 1044 + a 897 + ; 633 + em 516 + ser 466 + sobre 349 + os 313 + anos 301 + ontem 292 + ainda 279 + segundo 256 + ter 249 + dois 231 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en_fixt.py new file mode 100644 index 0000000..d68a935 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/portuguese_en_fixt.py @@ -0,0 +1,4 @@ +def setup_module(): + import pytest + + pytest.skip("portuguese_en.doctest imports nltk.examples.pt which doesn't exist!") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability.doctest new file mode 100644 index 0000000..37b5d77 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability.doctest @@ -0,0 +1,306 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=========== +Probability +=========== + + >>> from nltk.test.probability_fixt import setup_module + >>> setup_module() + + >>> import nltk + >>> from nltk.probability import * + +FreqDist +-------- + + >>> text1 = ['no', 'good', 'fish', 'goes', 'anywhere', 'without', 'a', 'porpoise', '!'] + >>> text2 = ['no', 'good', 'porpoise', 'likes', 'to', 'fish', 'fish', 'anywhere', '.'] + + >>> fd1 = nltk.FreqDist(text1) + >>> fd1 == nltk.FreqDist(text1) + True + +Note that items are sorted in order of decreasing frequency; two items of the same frequency appear in indeterminate order. + + >>> import itertools + >>> both = nltk.FreqDist(text1 + text2) + >>> both_most_common = both.most_common() + >>> list(itertools.chain(*(sorted(ys) for k, ys in itertools.groupby(both_most_common, key=lambda t: t[1])))) + [('fish', 3), ('anywhere', 2), ('good', 2), ('no', 2), ('porpoise', 2), ('!', 1), ('.', 1), ('a', 1), ('goes', 1), ('likes', 1), ('to', 1), ('without', 1)] + + >>> both == fd1 + nltk.FreqDist(text2) + True + >>> fd1 == nltk.FreqDist(text1) # But fd1 is unchanged + True + + >>> fd2 = nltk.FreqDist(text2) + >>> fd1.update(fd2) + >>> fd1 == both + True + + >>> fd1 = nltk.FreqDist(text1) + >>> fd1.update(text2) + >>> fd1 == both + True + + >>> fd1 = nltk.FreqDist(text1) + >>> fd2 = nltk.FreqDist(fd1) + >>> fd2 == fd1 + True + +``nltk.FreqDist`` can be pickled: + + >>> import pickle + >>> fd1 = nltk.FreqDist(text1) + >>> pickled = pickle.dumps(fd1) + >>> fd1 == pickle.loads(pickled) + True + +Mathematical operations: + + >>> FreqDist('abbb') + FreqDist('bcc') + FreqDist({'b': 4, 'c': 2, 'a': 1}) + >>> FreqDist('abbbc') - FreqDist('bccd') + FreqDist({'b': 2, 'a': 1}) + >>> FreqDist('abbb') | FreqDist('bcc') + FreqDist({'b': 3, 'c': 2, 'a': 1}) + >>> FreqDist('abbb') & FreqDist('bcc') + FreqDist({'b': 1}) + +ConditionalFreqDist +------------------- + + >>> cfd1 = ConditionalFreqDist() + >>> cfd1[1] = FreqDist('abbbb') + >>> cfd1[2] = FreqDist('xxxxyy') + >>> cfd1 + + + >>> cfd2 = ConditionalFreqDist() + >>> cfd2[1] = FreqDist('bbccc') + >>> cfd2[2] = FreqDist('xxxyyyzz') + >>> cfd2[3] = FreqDist('m') + >>> cfd2 + + + >>> r = cfd1 + cfd2 + >>> [(i,r[i]) for i in r.conditions()] + [(1, FreqDist({'b': 6, 'c': 3, 'a': 1})), (2, FreqDist({'x': 7, 'y': 5, 'z': 2})), (3, FreqDist({'m': 1}))] + + >>> r = cfd1 - cfd2 + >>> [(i,r[i]) for i in r.conditions()] + [(1, FreqDist({'b': 2, 'a': 1})), (2, FreqDist({'x': 1}))] + + >>> r = cfd1 | cfd2 + >>> [(i,r[i]) for i in r.conditions()] + [(1, FreqDist({'b': 4, 'c': 3, 'a': 1})), (2, FreqDist({'x': 4, 'y': 3, 'z': 2})), (3, FreqDist({'m': 1}))] + + >>> r = cfd1 & cfd2 + >>> [(i,r[i]) for i in r.conditions()] + [(1, FreqDist({'b': 2})), (2, FreqDist({'x': 3, 'y': 2}))] + +Testing some HMM estimators +--------------------------- + +We extract a small part (500 sentences) of the Brown corpus + + >>> corpus = nltk.corpus.brown.tagged_sents(categories='adventure')[:500] + >>> print(len(corpus)) + 500 + +We create a HMM trainer - note that we need the tags and symbols +from the whole corpus, not just the training corpus + + >>> from nltk.util import unique_list + >>> tag_set = unique_list(tag for sent in corpus for (word,tag) in sent) + >>> print(len(tag_set)) + 92 + >>> symbols = unique_list(word for sent in corpus for (word,tag) in sent) + >>> print(len(symbols)) + 1464 + >>> trainer = nltk.tag.HiddenMarkovModelTrainer(tag_set, symbols) + +We divide the corpus into 90% training and 10% testing + + >>> train_corpus = [] + >>> test_corpus = [] + >>> for i in range(len(corpus)): + ... if i % 10: + ... train_corpus += [corpus[i]] + ... else: + ... test_corpus += [corpus[i]] + >>> print(len(train_corpus)) + 450 + >>> print(len(test_corpus)) + 50 + +And now we can test the estimators + + >>> def train_and_test(est): + ... hmm = trainer.train_supervised(train_corpus, estimator=est) + ... print('%.2f%%' % (100 * hmm.accuracy(test_corpus))) + +Maximum Likelihood Estimation +----------------------------- +- this resulted in an initialization error before r7209 + + >>> mle = lambda fd, bins: MLEProbDist(fd) + >>> train_and_test(mle) + 22.75% + +Laplace (= Lidstone with gamma==1) + + >>> train_and_test(LaplaceProbDist) + 66.04% + +Expected Likelihood Estimation (= Lidstone with gamma==0.5) + + >>> train_and_test(ELEProbDist) + 73.01% + +Lidstone Estimation, for gamma==0.1, 0.5 and 1 +(the later two should be exactly equal to MLE and ELE above) + + >>> def lidstone(gamma): + ... return lambda fd, bins: LidstoneProbDist(fd, gamma, bins) + >>> train_and_test(lidstone(0.1)) + 82.51% + >>> train_and_test(lidstone(0.5)) + 73.01% + >>> train_and_test(lidstone(1.0)) + 66.04% + +Witten Bell Estimation +---------------------- +- This resulted in ZeroDivisionError before r7209 + + >>> train_and_test(WittenBellProbDist) + 88.12% + +Good Turing Estimation + + >>> gt = lambda fd, bins: SimpleGoodTuringProbDist(fd, bins=1e5) + >>> train_and_test(gt) + 86.93% + +Kneser Ney Estimation +--------------------- +Since the Kneser-Ney distribution is best suited for trigrams, we must adjust +our testing accordingly. + + >>> corpus = [[((x[0],y[0],z[0]),(x[1],y[1],z[1])) + ... for x, y, z in nltk.trigrams(sent)] + ... for sent in corpus[:100]] + +We will then need to redefine the rest of the training/testing variables + + >>> tag_set = unique_list(tag for sent in corpus for (word,tag) in sent) + >>> len(tag_set) + 906 + + >>> symbols = unique_list(word for sent in corpus for (word,tag) in sent) + >>> len(symbols) + 1341 + + >>> trainer = nltk.tag.HiddenMarkovModelTrainer(tag_set, symbols) + >>> train_corpus = [] + >>> test_corpus = [] + + >>> for i in range(len(corpus)): + ... if i % 10: + ... train_corpus += [corpus[i]] + ... else: + ... test_corpus += [corpus[i]] + + >>> len(train_corpus) + 90 + >>> len(test_corpus) + 10 + + >>> kn = lambda fd, bins: KneserNeyProbDist(fd) + >>> train_and_test(kn) + 0.86% + +Remains to be added: +- Tests for HeldoutProbDist, CrossValidationProbDist and MutableProbDist + +Squashed bugs +------------- + +Issue 511: override pop and popitem to invalidate the cache + + >>> fd = nltk.FreqDist('a') + >>> list(fd.keys()) + ['a'] + >>> fd.pop('a') + 1 + >>> list(fd.keys()) + [] + +Issue 533: access cumulative frequencies with no arguments + + >>> fd = nltk.FreqDist('aab') + >>> list(fd._cumulative_frequencies(['a'])) + [2.0] + >>> list(fd._cumulative_frequencies(['a', 'b'])) + [2.0, 3.0] + +Issue 579: override clear to reset some variables + + >>> fd = FreqDist('aab') + >>> fd.clear() + >>> fd.N() + 0 + +Issue 351: fix fileids method of CategorizedCorpusReader to inadvertently +add errant categories + + >>> from nltk.corpus import brown + >>> brown.fileids('blah') + Traceback (most recent call last): + ... + ValueError: Category blah not found + >>> brown.categories() + ['adventure', 'belles_lettres', 'editorial', 'fiction', 'government', 'hobbies', 'humor', 'learned', 'lore', 'mystery', 'news', 'religion', 'reviews', 'romance', 'science_fiction'] + +Issue 175: add the unseen bin to SimpleGoodTuringProbDist by default +otherwise any unseen events get a probability of zero, i.e., +they don't get smoothed + + >>> from nltk import SimpleGoodTuringProbDist, FreqDist + >>> fd = FreqDist({'a':1, 'b':1, 'c': 2, 'd': 3, 'e': 4, 'f': 4, 'g': 4, 'h': 5, 'i': 5, 'j': 6, 'k': 6, 'l': 6, 'm': 7, 'n': 7, 'o': 8, 'p': 9, 'q': 10}) + >>> p = SimpleGoodTuringProbDist(fd) + >>> p.prob('a') + 0.017649766667026317... + >>> p.prob('o') + 0.08433050215340411... + >>> p.prob('z') + 0.022727272727272728... + >>> p.prob('foobar') + 0.022727272727272728... + +``MLEProbDist``, ``ConditionalProbDist'', ``DictionaryConditionalProbDist`` and +``ConditionalFreqDist`` can be pickled: + + >>> import pickle + >>> pd = MLEProbDist(fd) + >>> sorted(pd.samples()) == sorted(pickle.loads(pickle.dumps(pd)).samples()) + True + >>> dpd = DictionaryConditionalProbDist({'x': pd}) + >>> unpickled = pickle.loads(pickle.dumps(dpd)) + >>> dpd['x'].prob('a') + 0.011363636... + >>> dpd['x'].prob('a') == unpickled['x'].prob('a') + True + >>> cfd = nltk.probability.ConditionalFreqDist() + >>> cfd['foo']['hello'] += 1 + >>> cfd['foo']['hello'] += 1 + >>> cfd['bar']['hello'] += 1 + >>> cfd2 = pickle.loads(pickle.dumps(cfd)) + >>> cfd2 == cfd + True + >>> cpd = ConditionalProbDist(cfd, SimpleGoodTuringProbDist) + >>> cpd2 = pickle.loads(pickle.dumps(cpd)) + >>> cpd['foo'].prob('hello') == cpd2['foo'].prob('hello') + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability_fixt.py new file mode 100644 index 0000000..2eb8b38 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/probability_fixt.py @@ -0,0 +1,8 @@ +# probability.doctest uses HMM which requires numpy; +# skip probability.doctest if numpy is not available + + +def setup_module(): + import pytest + + pytest.importorskip("numpy") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/propbank.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/propbank.doctest new file mode 100644 index 0000000..e130be0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/propbank.doctest @@ -0,0 +1,176 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +======== +PropBank +======== + +The PropBank Corpus provides predicate-argument annotation for the +entire Penn Treebank. Each verb in the treebank is annotated by a single +instance in PropBank, containing information about the location of +the verb, and the location and identity of its arguments: + + >>> from nltk.corpus import propbank + >>> pb_instances = propbank.instances() + >>> print(pb_instances) + [, + , ...] + +Each propbank instance defines the following member variables: + + - Location information: `fileid`, `sentnum`, `wordnum` + - Annotator information: `tagger` + - Inflection information: `inflection` + - Roleset identifier: `roleset` + - Verb (aka predicate) location: `predicate` + - Argument locations and types: `arguments` + +The following examples show the types of these arguments: + + >>> inst = pb_instances[103] + >>> (inst.fileid, inst.sentnum, inst.wordnum) + ('wsj_0004.mrg', 8, 16) + >>> inst.tagger + 'gold' + >>> inst.inflection + + >>> infl = inst.inflection + >>> infl.form, infl.tense, infl.aspect, infl.person, infl.voice + ('v', 'p', '-', '-', 'a') + >>> inst.roleset + 'rise.01' + >>> inst.predicate + PropbankTreePointer(16, 0) + >>> inst.arguments + ((PropbankTreePointer(0, 2), 'ARG1'), + (PropbankTreePointer(13, 1), 'ARGM-DIS'), + (PropbankTreePointer(17, 1), 'ARG4-to'), + (PropbankTreePointer(20, 1), 'ARG3-from')) + +The location of the predicate and of the arguments are encoded using +`PropbankTreePointer` objects, as well as `PropbankChainTreePointer` +objects and `PropbankSplitTreePointer` objects. A +`PropbankTreePointer` consists of a `wordnum` and a `height`: + + >>> print(inst.predicate.wordnum, inst.predicate.height) + 16 0 + +This identifies the tree constituent that is headed by the word that +is the `wordnum`\ 'th token in the sentence, and whose span is found +by going `height` nodes up in the tree. This type of pointer is only +useful if we also have the corresponding tree structure, since it +includes empty elements such as traces in the word number count. The +trees for 10% of the standard PropBank Corpus are contained in the +`treebank` corpus: + + >>> tree = inst.tree + + >>> from nltk.corpus import treebank + >>> assert tree == treebank.parsed_sents(inst.fileid)[inst.sentnum] + + >>> inst.predicate.select(tree) + Tree('VBD', ['rose']) + >>> for (argloc, argid) in inst.arguments: + ... print('%-10s %s' % (argid, argloc.select(tree).pformat(500)[:50])) + ARG1 (NP-SBJ (NP (DT The) (NN yield)) (PP (IN on) (NP ( + ARGM-DIS (PP (IN for) (NP (NN example))) + ARG4-to (PP-DIR (TO to) (NP (CD 8.04) (NN %))) + ARG3-from (PP-DIR (IN from) (NP (CD 7.90) (NN %))) + +Propbank tree pointers can be converted to standard tree locations, +which are usually easier to work with, using the `treepos()` method: + + >>> treepos = inst.predicate.treepos(tree) + >>> print (treepos, tree[treepos]) + (4, 0) (VBD rose) + +In some cases, argument locations will be encoded using +`PropbankChainTreePointer`\ s (for trace chains) or +`PropbankSplitTreePointer`\ s (for discontinuous constituents). Both +of these objects contain a single member variable, `pieces`, +containing a list of the constituent pieces. They also define the +method `select()`, which will return a tree containing all the +elements of the argument. (A new head node is created, labeled +"*CHAIN*" or "*SPLIT*", since the argument is not a single constituent +in the original tree). Sentence #6 contains an example of an argument +that is both discontinuous and contains a chain: + + >>> inst = pb_instances[6] + >>> inst.roleset + 'expose.01' + >>> argloc, argid = inst.arguments[2] + >>> argloc + + >>> argloc.pieces + [, PropbankTreePointer(27, 0)] + >>> argloc.pieces[0].pieces + ... + [PropbankTreePointer(22, 1), PropbankTreePointer(24, 0), + PropbankTreePointer(25, 1)] + >>> print(argloc.select(inst.tree)) + (*CHAIN* + (*SPLIT* (NP (DT a) (NN group)) (IN of) (NP (NNS workers))) + (-NONE- *)) + +The PropBank Corpus also provides access to the frameset files, which +define the argument labels used by the annotations, on a per-verb +basis. Each frameset file contains one or more predicates, such as +'turn' or 'turn_on', each of which is divided into coarse-grained word +senses called rolesets. For each roleset, the frameset file provides +descriptions of the argument roles, along with examples. + + >>> expose_01 = propbank.roleset('expose.01') + >>> turn_01 = propbank.roleset('turn.01') + >>> print(turn_01) + + >>> for role in turn_01.findall("roles/role"): + ... print(role.attrib['n'], role.attrib['descr']) + 0 turner + 1 thing turning + m direction, location + + >>> from xml.etree import ElementTree + >>> print(ElementTree.tostring(turn_01.find('example')).decode('utf8').strip()) + + + John turned the key in the lock. + + John + turned + the key + in the lock + + +Note that the standard corpus distribution only contains 10% of the +treebank, so the parse trees are not available for instances starting +at 9353: + + >>> inst = pb_instances[9352] + >>> inst.fileid + 'wsj_0199.mrg' + >>> print(inst.tree) + (S (NP-SBJ (NNP Trinity)) (VP (VBD said) (SBAR (-NONE- 0) ...)) + >>> print(inst.predicate.select(inst.tree)) + (VB begin) + + >>> inst = pb_instances[9353] + >>> inst.fileid + 'wsj_0200.mrg' + >>> print(inst.tree) + None + >>> print(inst.predicate.select(inst.tree)) + Traceback (most recent call last): + . . . + ValueError: Parse tree not available + +However, if you supply your own version of the treebank corpus (by +putting it before the nltk-provided version on `nltk.data.path`, or +by creating a `ptb` directory as described above and using the +`propbank_ptb` module), then you can access the trees for all +instances. + +A list of the verb lemmas contained in PropBank is returned by the +`propbank.verbs()` method: + + >>> propbank.verbs() + ['abandon', 'abate', 'abdicate', 'abet', 'abide', ...] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/relextract.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/relextract.doctest new file mode 100644 index 0000000..b44c44a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/relextract.doctest @@ -0,0 +1,263 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +====================== +Information Extraction +====================== + +Information Extraction standardly consists of three subtasks: + +#. Named Entity Recognition + +#. Relation Extraction + +#. Template Filling + +Named Entities +~~~~~~~~~~~~~~ + +The IEER corpus is marked up for a variety of Named Entities. A Named +Entity (more strictly, a Named Entity mention) is a name of an +entity belonging to a specified class. For example, the Named Entity +classes in IEER include PERSON, LOCATION, ORGANIZATION, DATE and so +on. Within NLTK, Named Entities are represented as subtrees within a +chunk structure: the class name is treated as node label, while the +entity mention itself appears as the leaves of the subtree. This is +illustrated below, where we have show an extract of the chunk +representation of document NYT_19980315.064: + + >>> from nltk.corpus import ieer + >>> docs = ieer.parsed_docs('NYT_19980315') + >>> tree = docs[1].text + >>> print(tree) + (DOCUMENT + ... + ``It's + a + chance + to + think + about + first-level + questions,'' + said + Ms. + (PERSON Cohn) + , + a + partner + in + the + (ORGANIZATION McGlashan & Sarrail) + firm + in + (LOCATION San Mateo) + , + (LOCATION Calif.) + ...) + +Thus, the Named Entity mentions in this example are *Cohn*, *McGlashan & +Sarrail*, *San Mateo* and *Calif.*. + +The CoNLL2002 Dutch and Spanish data is treated similarly, although in +this case, the strings are also POS tagged. + + >>> from nltk.corpus import conll2002 + >>> for doc in conll2002.chunked_sents('ned.train')[27]: + ... print(doc) + ('Het', 'Art') + (ORG Hof/N van/Prep Cassatie/N) + ('verbrak', 'V') + ('het', 'Art') + ('arrest', 'N') + ('zodat', 'Conj') + ('het', 'Pron') + ('moest', 'V') + ('worden', 'V') + ('overgedaan', 'V') + ('door', 'Prep') + ('het', 'Art') + ('hof', 'N') + ('van', 'Prep') + ('beroep', 'N') + ('van', 'Prep') + (LOC Antwerpen/N) + ('.', 'Punc') + +Relation Extraction +~~~~~~~~~~~~~~~~~~~ + +Relation Extraction standardly consists of identifying specified +relations between Named Entities. For example, assuming that we can +recognize ORGANIZATIONs and LOCATIONs in text, we might want to also +recognize pairs *(o, l)* of these kinds of entities such that *o* is +located in *l*. + +The `sem.relextract` module provides some tools to help carry out a +simple version of this task. The `tree2semi_rel()` function splits a chunk +document into a list of two-member lists, each of which consists of a +(possibly empty) string followed by a `Tree` (i.e., a Named Entity): + + >>> from nltk.sem import relextract + >>> pairs = relextract.tree2semi_rel(tree) + >>> for s, tree in pairs[18:22]: + ... print('("...%s", %s)' % (" ".join(s[-5:]),tree)) + ("...about first-level questions,'' said Ms.", (PERSON Cohn)) + ("..., a partner in the", (ORGANIZATION McGlashan & Sarrail)) + ("...firm in", (LOCATION San Mateo)) + ("...,", (LOCATION Calif.)) + +The function `semi_rel2reldict()` processes triples of these pairs, i.e., +pairs of the form ``((string1, Tree1), (string2, Tree2), (string3, +Tree3))`` and outputs a dictionary (a `reldict`) in which ``Tree1`` is +the subject of the relation, ``string2`` is the filler +and ``Tree3`` is the object of the relation. ``string1`` and ``string3`` are +stored as left and right context respectively. + + >>> reldicts = relextract.semi_rel2reldict(pairs) + >>> for k, v in sorted(reldicts[0].items()): + ... print(k, '=>', v) + filler => of messages to their own ``Cyberia'' ... + lcon => transactions.'' Each week, they post + objclass => ORGANIZATION + objsym => white_house + objtext => White House + rcon => for access to its planned + subjclass => CARDINAL + subjsym => hundreds + subjtext => hundreds + untagged_filler => of messages to their own ``Cyberia'' ... + +The next example shows some of the values for two `reldict`\ s +corresponding to the ``'NYT_19980315'`` text extract shown earlier. + + >>> for r in reldicts[18:20]: + ... print('=' * 20) + ... print(r['subjtext']) + ... print(r['filler']) + ... print(r['objtext']) + ==================== + Cohn + , a partner in the + McGlashan & Sarrail + ==================== + McGlashan & Sarrail + firm in + San Mateo + +The function `relextract()` allows us to filter the `reldict`\ s +according to the classes of the subject and object named entities. In +addition, we can specify that the filler text has to match a given +regular expression, as illustrated in the next example. Here, we are +looking for pairs of entities in the IN relation, where IN has +signature . + + >>> import re + >>> IN = re.compile(r'.*\bin\b(?!\b.+ing\b)') + >>> for fileid in ieer.fileids(): + ... for doc in ieer.parsed_docs(fileid): + ... for rel in relextract.extract_rels('ORG', 'LOC', doc, corpus='ieer', pattern = IN): + ... print(relextract.rtuple(rel)) + [ORG: 'Christian Democrats'] ', the leading political forces in' [LOC: 'Italy'] + [ORG: 'AP'] ') _ Lebanese guerrillas attacked Israeli forces in southern' [LOC: 'Lebanon'] + [ORG: 'Security Council'] 'adopted Resolution 425. Huge yellow banners hung across intersections in' [LOC: 'Beirut'] + [ORG: 'U.N.'] 'failures in' [LOC: 'Africa'] + [ORG: 'U.N.'] 'peacekeeping operation in' [LOC: 'Somalia'] + [ORG: 'U.N.'] 'partners on a more effective role in' [LOC: 'Africa'] + [ORG: 'AP'] ') _ A bomb exploded in a mosque in central' [LOC: 'San`a'] + [ORG: 'Krasnoye Sormovo'] 'shipyard in the Soviet city of' [LOC: 'Gorky'] + [ORG: 'Kelab Golf Darul Ridzuan'] 'in' [LOC: 'Perak'] + [ORG: 'U.N.'] 'peacekeeping operation in' [LOC: 'Somalia'] + [ORG: 'WHYY'] 'in' [LOC: 'Philadelphia'] + [ORG: 'McGlashan & Sarrail'] 'firm in' [LOC: 'San Mateo'] + [ORG: 'Freedom Forum'] 'in' [LOC: 'Arlington'] + [ORG: 'Brookings Institution'] ', the research group in' [LOC: 'Washington'] + [ORG: 'Idealab'] ', a self-described business incubator based in' [LOC: 'Los Angeles'] + [ORG: 'Open Text'] ', based in' [LOC: 'Waterloo'] + ... + +The next example illustrates a case where the pattern is a disjunction +of roles that a PERSON can occupy in an ORGANIZATION. + + >>> roles = r""" + ... (.*( + ... analyst| + ... chair(wo)?man| + ... commissioner| + ... counsel| + ... director| + ... economist| + ... editor| + ... executive| + ... foreman| + ... governor| + ... head| + ... lawyer| + ... leader| + ... librarian).*)| + ... manager| + ... partner| + ... president| + ... producer| + ... professor| + ... researcher| + ... spokes(wo)?man| + ... writer| + ... ,\sof\sthe?\s* # "X, of (the) Y" + ... """ + >>> ROLES = re.compile(roles, re.VERBOSE) + >>> for fileid in ieer.fileids(): + ... for doc in ieer.parsed_docs(fileid): + ... for rel in relextract.extract_rels('PER', 'ORG', doc, corpus='ieer', pattern=ROLES): + ... print(relextract.rtuple(rel)) + [PER: 'Kivutha Kibwana'] ', of the' [ORG: 'National Convention Assembly'] + [PER: 'Boban Boskovic'] ', chief executive of the' [ORG: 'Plastika'] + [PER: 'Annan'] ', the first sub-Saharan African to head the' [ORG: 'United Nations'] + [PER: 'Kiriyenko'] 'became a foreman at the' [ORG: 'Krasnoye Sormovo'] + [PER: 'Annan'] ', the first sub-Saharan African to head the' [ORG: 'United Nations'] + [PER: 'Mike Godwin'] ', chief counsel for the' [ORG: 'Electronic Frontier Foundation'] + ... + +In the case of the CoNLL2002 data, we can include POS tags in the +query pattern. This example also illustrates how the output can be +presented as something that looks more like a clause in a logical language. + + >>> de = """ + ... .* + ... ( + ... de/SP| + ... del/SP + ... ) + ... """ + >>> DE = re.compile(de, re.VERBOSE) + >>> rels = [rel for doc in conll2002.chunked_sents('esp.train') + ... for rel in relextract.extract_rels('ORG', 'LOC', doc, corpus='conll2002', pattern = DE)] + >>> for r in rels[:10]: + ... print(relextract.clause(r, relsym='DE')) + DE('tribunal_supremo', 'victoria') + DE('museo_de_arte', 'alcorc\xf3n') + DE('museo_de_bellas_artes', 'a_coru\xf1a') + DE('siria', 'l\xedbano') + DE('uni\xf3n_europea', 'pek\xedn') + DE('ej\xe9rcito', 'rogberi') + DE('juzgado_de_instrucci\xf3n_n\xfamero_1', 'san_sebasti\xe1n') + DE('psoe', 'villanueva_de_la_serena') + DE('ej\xe9rcito', 'l\xedbano') + DE('juzgado_de_lo_penal_n\xfamero_2', 'ceuta') + >>> vnv = """ + ... ( + ... is/V| + ... was/V| + ... werd/V| + ... wordt/V + ... ) + ... .* + ... van/Prep + ... """ + >>> VAN = re.compile(vnv, re.VERBOSE) + >>> for doc in conll2002.chunked_sents('ned.train'): + ... for r in relextract.extract_rels('PER', 'ORG', doc, corpus='conll2002', pattern=VAN): + ... print(relextract.clause(r, relsym="VAN")) + VAN("cornet_d'elzius", 'buitenlandse_handel') + VAN('johan_rottiers', 'kardinaal_van_roey_instituut') + VAN('annie_lennox', 'eurythmics') diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/resolution.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/resolution.doctest new file mode 100644 index 0000000..eafc7fa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/resolution.doctest @@ -0,0 +1,222 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================= +Resolution Theorem Prover +========================= + + >>> from nltk.inference.resolution import * + >>> from nltk.sem import logic + >>> from nltk.sem.logic import * + >>> logic._counter._value = 0 + >>> read_expr = logic.Expression.fromstring + + >>> P = read_expr('P') + >>> Q = read_expr('Q') + >>> R = read_expr('R') + >>> A = read_expr('A') + >>> B = read_expr('B') + >>> x = read_expr('x') + >>> y = read_expr('y') + >>> z = read_expr('z') + +------------------------------- +Test most_general_unification() +------------------------------- + >>> print(most_general_unification(x, x)) + {} + >>> print(most_general_unification(A, A)) + {} + >>> print(most_general_unification(A, x)) + {x: A} + >>> print(most_general_unification(x, A)) + {x: A} + >>> print(most_general_unification(x, y)) + {x: y} + >>> print(most_general_unification(P(x), P(A))) + {x: A} + >>> print(most_general_unification(P(x,B), P(A,y))) + {x: A, y: B} + >>> print(most_general_unification(P(x,B), P(B,x))) + {x: B} + >>> print(most_general_unification(P(x,y), P(A,x))) + {x: A, y: x} + >>> print(most_general_unification(P(Q(x)), P(y))) + {y: Q(x)} + +------------ +Test unify() +------------ + >>> print(Clause([]).unify(Clause([]))) + [] + >>> print(Clause([P(x)]).unify(Clause([-P(A)]))) + [{}] + >>> print(Clause([P(A), Q(x)]).unify(Clause([-P(x), R(x)]))) + [{R(A), Q(A)}] + >>> print(Clause([P(A), Q(x), R(x,y)]).unify(Clause([-P(x), Q(y)]))) + [{Q(y), Q(A), R(A,y)}] + >>> print(Clause([P(A), -Q(y)]).unify(Clause([-P(x), Q(B)]))) + [{}] + >>> print(Clause([P(x), Q(x)]).unify(Clause([-P(A), -Q(B)]))) + [{-Q(B), Q(A)}, {-P(A), P(B)}] + >>> print(Clause([P(x,x), Q(x), R(x)]).unify(Clause([-P(A,z), -Q(B)]))) + [{-Q(B), Q(A), R(A)}, {-P(A,z), R(B), P(B,B)}] + + >>> a = clausify(read_expr('P(A)')) + >>> b = clausify(read_expr('A=B')) + >>> print(a[0].unify(b[0])) + [{P(B)}] + +------------------------- +Test is_tautology() +------------------------- + >>> print(Clause([P(A), -P(A)]).is_tautology()) + True + >>> print(Clause([-P(A), P(A)]).is_tautology()) + True + >>> print(Clause([P(x), -P(A)]).is_tautology()) + False + >>> print(Clause([Q(B), -P(A), P(A)]).is_tautology()) + True + >>> print(Clause([-Q(A), P(R(A)), -P(R(A)), Q(x), -R(y)]).is_tautology()) + True + >>> print(Clause([P(x), -Q(A)]).is_tautology()) + False + +------------------------- +Test subsumes() +------------------------- + >>> print(Clause([P(A), Q(B)]).subsumes(Clause([P(A), Q(B)]))) + True + >>> print(Clause([-P(A)]).subsumes(Clause([P(A)]))) + False + >>> print(Clause([P(A), Q(B)]).subsumes(Clause([Q(B), P(A)]))) + True + >>> print(Clause([P(A), Q(B)]).subsumes(Clause([Q(B), R(A), P(A)]))) + True + >>> print(Clause([P(A), R(A), Q(B)]).subsumes(Clause([Q(B), P(A)]))) + False + >>> print(Clause([P(x)]).subsumes(Clause([P(A)]))) + True + >>> print(Clause([P(A)]).subsumes(Clause([P(x)]))) + True + +------------ +Test prove() +------------ + >>> print(ResolutionProverCommand(read_expr('man(x)')).prove()) + False + >>> print(ResolutionProverCommand(read_expr('(man(x) -> man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) -> --man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('-(man(x) & -man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) | -man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) -> man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('-(man(x) & -man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) | -man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) -> man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('(man(x) <-> man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('-(man(x) <-> -man(x))')).prove()) + True + >>> print(ResolutionProverCommand(read_expr('all x.man(x)')).prove()) + False + >>> print(ResolutionProverCommand(read_expr('-all x.some y.F(x,y) & some x.all y.(-F(x,y))')).prove()) + False + >>> print(ResolutionProverCommand(read_expr('some x.all y.sees(x,y)')).prove()) + False + + >>> p1 = read_expr('all x.(man(x) -> mortal(x))') + >>> p2 = read_expr('man(Socrates)') + >>> c = read_expr('mortal(Socrates)') + >>> ResolutionProverCommand(c, [p1,p2]).prove() + True + + >>> p1 = read_expr('all x.(man(x) -> walks(x))') + >>> p2 = read_expr('man(John)') + >>> c = read_expr('some y.walks(y)') + >>> ResolutionProverCommand(c, [p1,p2]).prove() + True + + >>> p = read_expr('some e1.some e2.(believe(e1,john,e2) & walk(e2,mary))') + >>> c = read_expr('some e0.walk(e0,mary)') + >>> ResolutionProverCommand(c, [p]).prove() + True + +------------ +Test proof() +------------ + >>> p1 = read_expr('all x.(man(x) -> mortal(x))') + >>> p2 = read_expr('man(Socrates)') + >>> c = read_expr('mortal(Socrates)') + >>> logic._counter._value = 0 + >>> tp = ResolutionProverCommand(c, [p1,p2]) + >>> tp.prove() + True + >>> print(tp.proof()) + [1] {-mortal(Socrates)} A + [2] {-man(z2), mortal(z2)} A + [3] {man(Socrates)} A + [4] {-man(Socrates)} (1, 2) + [5] {mortal(Socrates)} (2, 3) + [6] {} (1, 5) + + +------------------ +Question Answering +------------------ +One answer + + >>> p1 = read_expr('father_of(art,john)') + >>> p2 = read_expr('father_of(bob,kim)') + >>> p3 = read_expr('all x.all y.(father_of(x,y) -> parent_of(x,y))') + >>> c = read_expr('all x.(parent_of(x,john) -> ANSWER(x))') + >>> logic._counter._value = 0 + >>> tp = ResolutionProverCommand(None, [p1,p2,p3,c]) + >>> sorted(tp.find_answers()) + [] + >>> print(tp.proof()) # doctest: +SKIP + [1] {father_of(art,john)} A + [2] {father_of(bob,kim)} A + [3] {-father_of(z3,z4), parent_of(z3,z4)} A + [4] {-parent_of(z6,john), ANSWER(z6)} A + [5] {parent_of(art,john)} (1, 3) + [6] {parent_of(bob,kim)} (2, 3) + [7] {ANSWER(z6), -father_of(z6,john)} (3, 4) + [8] {ANSWER(art)} (1, 7) + [9] {ANSWER(art)} (4, 5) + + +Multiple answers + + >>> p1 = read_expr('father_of(art,john)') + >>> p2 = read_expr('mother_of(ann,john)') + >>> p3 = read_expr('all x.all y.(father_of(x,y) -> parent_of(x,y))') + >>> p4 = read_expr('all x.all y.(mother_of(x,y) -> parent_of(x,y))') + >>> c = read_expr('all x.(parent_of(x,john) -> ANSWER(x))') + >>> logic._counter._value = 0 + >>> tp = ResolutionProverCommand(None, [p1,p2,p3,p4,c]) + >>> sorted(tp.find_answers()) + [, ] + >>> print(tp.proof()) # doctest: +SKIP + [ 1] {father_of(art,john)} A + [ 2] {mother_of(ann,john)} A + [ 3] {-father_of(z3,z4), parent_of(z3,z4)} A + [ 4] {-mother_of(z7,z8), parent_of(z7,z8)} A + [ 5] {-parent_of(z10,john), ANSWER(z10)} A + [ 6] {parent_of(art,john)} (1, 3) + [ 7] {parent_of(ann,john)} (2, 4) + [ 8] {ANSWER(z10), -father_of(z10,john)} (3, 5) + [ 9] {ANSWER(art)} (1, 8) + [10] {ANSWER(z10), -mother_of(z10,john)} (4, 5) + [11] {ANSWER(ann)} (2, 10) + [12] {ANSWER(art)} (5, 6) + [13] {ANSWER(ann)} (5, 7) + diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/semantics.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/semantics.doctest new file mode 100644 index 0000000..5ad6cfe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/semantics.doctest @@ -0,0 +1,667 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========= +Semantics +========= + + >>> # Setup tests by setting the counter to 0 + >>> from nltk.sem import logic + >>> logic._counter._value = 0 + + >>> import nltk + >>> from nltk.sem import Valuation, Model + >>> v = [('adam', 'b1'), ('betty', 'g1'), ('fido', 'd1'), + ... ('girl', set(['g1', 'g2'])), ('boy', set(['b1', 'b2'])), + ... ('dog', set(['d1'])), + ... ('love', set([('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')]))] + >>> val = Valuation(v) + >>> dom = val.domain + >>> m = Model(dom, val) + +Evaluation +---------- + +The top-level method of a ``Model`` instance is ``evaluate()``, which +assigns a semantic value to expressions of the ``logic`` module, under +an assignment ``g``: + + >>> dom = val.domain + >>> g = nltk.sem.Assignment(dom) + >>> m.evaluate('all x.(boy(x) -> - girl(x))', g) + True + + +``evaluate()`` calls a recursive function ``satisfy()``, which in turn +calls a function ``i()`` to interpret non-logical constants and +individual variables. ``i()`` delegates the interpretation of these to +the the model's ``Valuation`` and the variable assignment ``g`` +respectively. Any atomic expression which cannot be assigned a value +by ``i`` raises an ``Undefined`` exception; this is caught by +``evaluate``, which returns the string ``'Undefined'``. + + >>> m.evaluate('walk(adam)', g, trace=2) + + 'walk(adam)' is undefined under M, g + 'Undefined' + +Batch Processing +---------------- + +The utility functions ``interpret_sents()`` and ``evaluate_sents()`` are intended to +help with processing multiple sentences. Here's an example of the first of these: + + >>> sents = ['Mary walks'] + >>> results = nltk.sem.util.interpret_sents(sents, 'grammars/sample_grammars/sem2.fcfg') + >>> for result in results: + ... for (synrep, semrep) in result: + ... print(synrep) + (S[SEM=] + (NP[-LOC, NUM='sg', SEM=<\P.P(mary)>] + (PropN[-LOC, NUM='sg', SEM=<\P.P(mary)>] Mary)) + (VP[NUM='sg', SEM=<\x.walk(x)>] + (IV[NUM='sg', SEM=<\x.walk(x)>, TNS='pres'] walks))) + +In order to provide backwards compatibility with 'legacy' grammars where the semantics value +is specified with a lowercase +``sem`` feature, the relevant feature name can be passed to the function using the +``semkey`` parameter, as shown here: + + >>> sents = ['raining'] + >>> g = nltk.grammar.FeatureGrammar.fromstring(""" + ... % start S + ... S[sem=] -> 'raining' + ... """) + >>> results = nltk.sem.util.interpret_sents(sents, g, semkey='sem') + >>> for result in results: + ... for (synrep, semrep) in result: + ... print(semrep) + raining + +The function ``evaluate_sents()`` works in a similar manner, but also needs to be +passed a ``Model`` against which the semantic representations are evaluated. + +Unit Tests +========== + + +Unit tests for relations and valuations +--------------------------------------- + + >>> from nltk.sem import * + +Relations are sets of tuples, all of the same length. + + >>> s1 = set([('d1', 'd2'), ('d1', 'd1'), ('d2', 'd1')]) + >>> is_rel(s1) + True + >>> s2 = set([('d1', 'd2'), ('d1', 'd2'), ('d1',)]) + >>> is_rel(s2) + Traceback (most recent call last): + . . . + ValueError: Set set([('d1', 'd2'), ('d1',)]) contains sequences of different lengths + >>> s3 = set(['d1', 'd2']) + >>> is_rel(s3) + Traceback (most recent call last): + . . . + ValueError: Set set(['d2', 'd1']) contains sequences of different lengths + >>> s4 = set2rel(s3) + >>> is_rel(s4) + True + >>> is_rel(set()) + True + >>> null_binary_rel = set([(None, None)]) + >>> is_rel(null_binary_rel) + True + +Sets of entities are converted into sets of singleton tuples +(containing strings). + + >>> sorted(set2rel(s3)) + [('d1',), ('d2',)] + >>> sorted(set2rel(set([1,3,5,]))) + ['1', '3', '5'] + >>> set2rel(set()) == set() + True + >>> set2rel(set2rel(s3)) == set2rel(s3) + True + +Predication is evaluated by set membership. + + >>> ('d1', 'd2') in s1 + True + >>> ('d2', 'd2') in s1 + False + >>> ('d1',) in s1 + False + >>> 'd2' in s1 + False + >>> ('d1',) in s4 + True + >>> ('d1',) in set() + False + >>> 'd1' in null_binary_rel + False + + + >>> val = Valuation([('Fido', 'd1'), ('dog', set(['d1', 'd2'])), ('walk', set())]) + >>> sorted(val['dog']) + [('d1',), ('d2',)] + >>> val.domain == set(['d1', 'd2']) + True + >>> print(val.symbols) + ['Fido', 'dog', 'walk'] + + +Parse a valuation from a string. + + >>> v = """ + ... john => b1 + ... mary => g1 + ... suzie => g2 + ... fido => d1 + ... tess => d2 + ... noosa => n + ... girl => {g1, g2} + ... boy => {b1, b2} + ... dog => {d1, d2} + ... bark => {d1, d2} + ... walk => {b1, g2, d1} + ... chase => {(b1, g1), (b2, g1), (g1, d1), (g2, d2)} + ... see => {(b1, g1), (b2, d2), (g1, b1),(d2, b1), (g2, n)} + ... in => {(b1, n), (b2, n), (d2, n)} + ... with => {(b1, g1), (g1, b1), (d1, b1), (b1, d1)} + ... """ + >>> val = Valuation.fromstring(v) + + >>> print(val) # doctest: +SKIP + {'bark': set([('d1',), ('d2',)]), + 'boy': set([('b1',), ('b2',)]), + 'chase': set([('b1', 'g1'), ('g2', 'd2'), ('g1', 'd1'), ('b2', 'g1')]), + 'dog': set([('d1',), ('d2',)]), + 'fido': 'd1', + 'girl': set([('g2',), ('g1',)]), + 'in': set([('d2', 'n'), ('b1', 'n'), ('b2', 'n')]), + 'john': 'b1', + 'mary': 'g1', + 'noosa': 'n', + 'see': set([('b1', 'g1'), ('b2', 'd2'), ('d2', 'b1'), ('g2', 'n'), ('g1', 'b1')]), + 'suzie': 'g2', + 'tess': 'd2', + 'walk': set([('d1',), ('b1',), ('g2',)]), + 'with': set([('b1', 'g1'), ('d1', 'b1'), ('b1', 'd1'), ('g1', 'b1')])} + + +Unit tests for function argument application in a Model +------------------------------------------------------- + + >>> v = [('adam', 'b1'), ('betty', 'g1'), ('fido', 'd1'),\ + ... ('girl', set(['g1', 'g2'])), ('boy', set(['b1', 'b2'])), ('dog', set(['d1'])), + ... ('love', set([('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')])), + ... ('kiss', null_binary_rel)] + >>> val = Valuation(v) + >>> dom = val.domain + >>> m = Model(dom, val) + >>> g = Assignment(dom) + >>> sorted(val['boy']) + [('b1',), ('b2',)] + >>> ('b1',) in val['boy'] + True + >>> ('g1',) in val['boy'] + False + >>> ('foo',) in val['boy'] + False + >>> ('b1', 'g1') in val['love'] + True + >>> ('b1', 'b1') in val['kiss'] + False + >>> sorted(val.domain) + ['b1', 'b2', 'd1', 'g1', 'g2'] + + +Model Tests +=========== + +Extension of Lambda expressions + + >>> v0 = [('adam', 'b1'), ('betty', 'g1'), ('fido', 'd1'),\ + ... ('girl', set(['g1', 'g2'])), ('boy', set(['b1', 'b2'])), + ... ('dog', set(['d1'])), + ... ('love', set([('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')]))] + + >>> val0 = Valuation(v0) + >>> dom0 = val0.domain + >>> m0 = Model(dom0, val0) + >>> g0 = Assignment(dom0) + + >>> print(m0.evaluate(r'\x. \y. love(x, y)', g0) == {'g2': {'g2': False, 'b2': False, 'b1': True, 'g1': False, 'd1': False}, 'b2': {'g2': True, 'b2': False, 'b1': False, 'g1': False, 'd1': False}, 'b1': {'g2': False, 'b2': False, 'b1': False, 'g1': True, 'd1': False}, 'g1': {'g2': False, 'b2': False, 'b1': True, 'g1': False, 'd1': False}, 'd1': {'g2': False, 'b2': False, 'b1': False, 'g1': False, 'd1': False}}) + True + >>> print(m0.evaluate(r'\x. dog(x) (adam)', g0)) + False + >>> print(m0.evaluate(r'\x. (dog(x) | boy(x)) (adam)', g0)) + True + >>> print(m0.evaluate(r'\x. \y. love(x, y)(fido)', g0) == {'g2': False, 'b2': False, 'b1': False, 'g1': False, 'd1': False}) + True + >>> print(m0.evaluate(r'\x. \y. love(x, y)(adam)', g0) == {'g2': False, 'b2': False, 'b1': False, 'g1': True, 'd1': False}) + True + >>> print(m0.evaluate(r'\x. \y. love(x, y)(betty)', g0) == {'g2': False, 'b2': False, 'b1': True, 'g1': False, 'd1': False}) + True + >>> print(m0.evaluate(r'\x. \y. love(x, y)(betty)(adam)', g0)) + True + >>> print(m0.evaluate(r'\x. \y. love(x, y)(betty, adam)', g0)) + True + >>> print(m0.evaluate(r'\y. \x. love(x, y)(fido)(adam)', g0)) + False + >>> print(m0.evaluate(r'\y. \x. love(x, y)(betty, adam)', g0)) + True + >>> print(m0.evaluate(r'\x. exists y. love(x, y)', g0) == {'g2': True, 'b2': True, 'b1': True, 'g1': True, 'd1': False}) + True + >>> print(m0.evaluate(r'\z. adam', g0) == {'g2': 'b1', 'b2': 'b1', 'b1': 'b1', 'g1': 'b1', 'd1': 'b1'}) + True + >>> print(m0.evaluate(r'\z. love(x, y)', g0) == {'g2': False, 'b2': False, 'b1': False, 'g1': False, 'd1': False}) + True + + +Propositional Model Test +------------------------ + + >>> tests = [ + ... ('P & Q', True), + ... ('P & R', False), + ... ('- P', False), + ... ('- R', True), + ... ('- - P', True), + ... ('- (P & R)', True), + ... ('P | R', True), + ... ('R | P', True), + ... ('R | R', False), + ... ('- P | R', False), + ... ('P | - P', True), + ... ('P -> Q', True), + ... ('P -> R', False), + ... ('R -> P', True), + ... ('P <-> P', True), + ... ('R <-> R', True), + ... ('P <-> R', False), + ... ] + >>> val1 = Valuation([('P', True), ('Q', True), ('R', False)]) + >>> dom = set([]) + >>> m = Model(dom, val1) + >>> g = Assignment(dom) + >>> for (sent, testvalue) in tests: + ... semvalue = m.evaluate(sent, g) + ... if semvalue == testvalue: + ... print('*', end=' ') + * * * * * * * * * * * * * * * * * + + +Test of i Function +------------------ + + >>> from nltk.sem import Expression + >>> v = [('adam', 'b1'), ('betty', 'g1'), ('fido', 'd1'), + ... ('girl', set(['g1', 'g2'])), ('boy', set(['b1', 'b2'])), ('dog', set(['d1'])), + ... ('love', set([('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')]))] + >>> val = Valuation(v) + >>> dom = val.domain + >>> m = Model(dom, val) + >>> g = Assignment(dom, [('x', 'b1'), ('y', 'g2')]) + >>> exprs = ['adam', 'girl', 'love', 'walks', 'x', 'y', 'z'] + >>> parsed_exprs = [Expression.fromstring(e) for e in exprs] + >>> sorted_set = lambda x: sorted(x) if isinstance(x, set) else x + >>> for parsed in parsed_exprs: + ... try: + ... print("'%s' gets value %s" % (parsed, sorted_set(m.i(parsed, g)))) + ... except Undefined: + ... print("'%s' is Undefined" % parsed) + 'adam' gets value b1 + 'girl' gets value [('g1',), ('g2',)] + 'love' gets value [('b1', 'g1'), ('b2', 'g2'), ('g1', 'b1'), ('g2', 'b1')] + 'walks' is Undefined + 'x' gets value b1 + 'y' gets value g2 + 'z' is Undefined + +Test for formulas in Model +-------------------------- + + >>> tests = [ + ... ('love(adam, betty)', True), + ... ('love(adam, sue)', 'Undefined'), + ... ('dog(fido)', True), + ... ('- dog(fido)', False), + ... ('- - dog(fido)', True), + ... ('- dog(sue)', 'Undefined'), + ... ('dog(fido) & boy(adam)', True), + ... ('- (dog(fido) & boy(adam))', False), + ... ('- dog(fido) & boy(adam)', False), + ... ('dog(fido) | boy(adam)', True), + ... ('- (dog(fido) | boy(adam))', False), + ... ('- dog(fido) | boy(adam)', True), + ... ('- dog(fido) | - boy(adam)', False), + ... ('dog(fido) -> boy(adam)', True), + ... ('- (dog(fido) -> boy(adam))', False), + ... ('- dog(fido) -> boy(adam)', True), + ... ('exists x . love(adam, x)', True), + ... ('all x . love(adam, x)', False), + ... ('fido = fido', True), + ... ('exists x . all y. love(x, y)', False), + ... ('exists x . (x = fido)', True), + ... ('all x . (dog(x) | - dog(x))', True), + ... ('adam = mia', 'Undefined'), + ... ('\\x. (boy(x) | girl(x))', {'g2': True, 'b2': True, 'b1': True, 'g1': True, 'd1': False}), + ... ('\\x. exists y. (boy(x) & love(x, y))', {'g2': False, 'b2': True, 'b1': True, 'g1': False, 'd1': False}), + ... ('exists z1. boy(z1)', True), + ... ('exists x. (boy(x) & - (x = adam))', True), + ... ('exists x. (boy(x) & all y. love(y, x))', False), + ... ('all x. (boy(x) | girl(x))', False), + ... ('all x. (girl(x) -> exists y. boy(y) & love(x, y))', False), + ... ('exists x. (boy(x) & all y. (girl(y) -> love(y, x)))', True), + ... ('exists x. (boy(x) & all y. (girl(y) -> love(x, y)))', False), + ... ('all x. (dog(x) -> - girl(x))', True), + ... ('exists x. exists y. (love(x, y) & love(x, y))', True), + ... ] + >>> for (sent, testvalue) in tests: + ... semvalue = m.evaluate(sent, g) + ... if semvalue == testvalue: + ... print('*', end=' ') + ... else: + ... print(sent, semvalue) + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + + +Satisfier Tests +--------------- + + >>> formulas = [ + ... 'boy(x)', + ... '(x = x)', + ... '(boy(x) | girl(x))', + ... '(boy(x) & girl(x))', + ... 'love(adam, x)', + ... 'love(x, adam)', + ... '- (x = adam)', + ... 'exists z22. love(x, z22)', + ... 'exists y. love(y, x)', + ... 'all y. (girl(y) -> love(x, y))', + ... 'all y. (girl(y) -> love(y, x))', + ... 'all y. (girl(y) -> (boy(x) & love(y, x)))', + ... 'boy(x) & all y. (girl(y) -> love(x, y))', + ... 'boy(x) & all y. (girl(y) -> love(y, x))', + ... 'boy(x) & exists y. (girl(y) & love(y, x))', + ... 'girl(x) -> dog(x)', + ... 'all y. (dog(y) -> (x = y))', + ... '- exists y. love(y, x)', + ... 'exists y. (love(adam, y) & love(y, x))' + ... ] + >>> g.purge() + >>> g.add('x', 'b1') + {'x': 'b1'} + >>> for f in formulas: + ... try: + ... print("'%s' gets value: %s" % (f, m.evaluate(f, g))) + ... except Undefined: + ... print("'%s' is Undefined" % f) + 'boy(x)' gets value: True + '(x = x)' gets value: True + '(boy(x) | girl(x))' gets value: True + '(boy(x) & girl(x))' gets value: False + 'love(adam, x)' gets value: False + 'love(x, adam)' gets value: False + '- (x = adam)' gets value: False + 'exists z22. love(x, z22)' gets value: True + 'exists y. love(y, x)' gets value: True + 'all y. (girl(y) -> love(x, y))' gets value: False + 'all y. (girl(y) -> love(y, x))' gets value: True + 'all y. (girl(y) -> (boy(x) & love(y, x)))' gets value: True + 'boy(x) & all y. (girl(y) -> love(x, y))' gets value: False + 'boy(x) & all y. (girl(y) -> love(y, x))' gets value: True + 'boy(x) & exists y. (girl(y) & love(y, x))' gets value: True + 'girl(x) -> dog(x)' gets value: True + 'all y. (dog(y) -> (x = y))' gets value: False + '- exists y. love(y, x)' gets value: False + 'exists y. (love(adam, y) & love(y, x))' gets value: True + + >>> from nltk.sem import Expression + >>> for fmla in formulas: + ... p = Expression.fromstring(fmla) + ... g.purge() + ... print("Satisfiers of '%s':\n\t%s" % (p, sorted(m.satisfiers(p, 'x', g)))) + Satisfiers of 'boy(x)': + ['b1', 'b2'] + Satisfiers of '(x = x)': + ['b1', 'b2', 'd1', 'g1', 'g2'] + Satisfiers of '(boy(x) | girl(x))': + ['b1', 'b2', 'g1', 'g2'] + Satisfiers of '(boy(x) & girl(x))': + [] + Satisfiers of 'love(adam,x)': + ['g1'] + Satisfiers of 'love(x,adam)': + ['g1', 'g2'] + Satisfiers of '-(x = adam)': + ['b2', 'd1', 'g1', 'g2'] + Satisfiers of 'exists z22.love(x,z22)': + ['b1', 'b2', 'g1', 'g2'] + Satisfiers of 'exists y.love(y,x)': + ['b1', 'g1', 'g2'] + Satisfiers of 'all y.(girl(y) -> love(x,y))': + [] + Satisfiers of 'all y.(girl(y) -> love(y,x))': + ['b1'] + Satisfiers of 'all y.(girl(y) -> (boy(x) & love(y,x)))': + ['b1'] + Satisfiers of '(boy(x) & all y.(girl(y) -> love(x,y)))': + [] + Satisfiers of '(boy(x) & all y.(girl(y) -> love(y,x)))': + ['b1'] + Satisfiers of '(boy(x) & exists y.(girl(y) & love(y,x)))': + ['b1'] + Satisfiers of '(girl(x) -> dog(x))': + ['b1', 'b2', 'd1'] + Satisfiers of 'all y.(dog(y) -> (x = y))': + ['d1'] + Satisfiers of '-exists y.love(y,x)': + ['b2', 'd1'] + Satisfiers of 'exists y.(love(adam,y) & love(y,x))': + ['b1'] + + +Tests based on the Blackburn & Bos testsuite +-------------------------------------------- + + >>> v1 = [('jules', 'd1'), ('vincent', 'd2'), ('pumpkin', 'd3'), + ... ('honey_bunny', 'd4'), ('yolanda', 'd5'), + ... ('customer', set(['d1', 'd2'])), + ... ('robber', set(['d3', 'd4'])), + ... ('love', set([('d3', 'd4')]))] + >>> val1 = Valuation(v1) + >>> dom1 = val1.domain + >>> m1 = Model(dom1, val1) + >>> g1 = Assignment(dom1) + + >>> v2 = [('jules', 'd1'), ('vincent', 'd2'), ('pumpkin', 'd3'), + ... ('honey_bunny', 'd4'), ('yolanda', 'd4'), + ... ('customer', set(['d1', 'd2', 'd5', 'd6'])), + ... ('robber', set(['d3', 'd4'])), + ... ('love', set([(None, None)]))] + >>> val2 = Valuation(v2) + >>> dom2 = set(['d1', 'd2', 'd3', 'd4', 'd5', 'd6']) + >>> m2 = Model(dom2, val2) + >>> g2 = Assignment(dom2) + >>> g21 = Assignment(dom2) + >>> g21.add('y', 'd3') + {'y': 'd3'} + + >>> v3 = [('mia', 'd1'), ('jody', 'd2'), ('jules', 'd3'), + ... ('vincent', 'd4'), + ... ('woman', set(['d1', 'd2'])), ('man', set(['d3', 'd4'])), + ... ('joke', set(['d5', 'd6'])), ('episode', set(['d7', 'd8'])), + ... ('in', set([('d5', 'd7'), ('d5', 'd8')])), + ... ('tell', set([('d1', 'd5'), ('d2', 'd6')]))] + >>> val3 = Valuation(v3) + >>> dom3 = set(['d1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8']) + >>> m3 = Model(dom3, val3) + >>> g3 = Assignment(dom3) + + >>> tests = [ + ... ('exists x. robber(x)', m1, g1, True), + ... ('exists x. exists y. love(y, x)', m1, g1, True), + ... ('exists x0. exists x1. love(x1, x0)', m2, g2, False), + ... ('all x. all y. love(y, x)', m2, g2, False), + ... ('- (all x. all y. love(y, x))', m2, g2, True), + ... ('all x. all y. - love(y, x)', m2, g2, True), + ... ('yolanda = honey_bunny', m2, g2, True), + ... ('mia = honey_bunny', m2, g2, 'Undefined'), + ... ('- (yolanda = honey_bunny)', m2, g2, False), + ... ('- (mia = honey_bunny)', m2, g2, 'Undefined'), + ... ('all x. (robber(x) | customer(x))', m2, g2, True), + ... ('- (all x. (robber(x) | customer(x)))', m2, g2, False), + ... ('(robber(x) | customer(x))', m2, g2, 'Undefined'), + ... ('(robber(y) | customer(y))', m2, g21, True), + ... ('exists x. (man(x) & exists x. woman(x))', m3, g3, True), + ... ('exists x. (man(x) & exists x. woman(x))', m3, g3, True), + ... ('- exists x. woman(x)', m3, g3, False), + ... ('exists x. (tasty(x) & burger(x))', m3, g3, 'Undefined'), + ... ('- exists x. (tasty(x) & burger(x))', m3, g3, 'Undefined'), + ... ('exists x. (man(x) & - exists y. woman(y))', m3, g3, False), + ... ('exists x. (man(x) & - exists x. woman(x))', m3, g3, False), + ... ('exists x. (woman(x) & - exists x. customer(x))', m2, g2, 'Undefined'), + ... ] + + >>> for item in tests: + ... sentence, model, g, testvalue = item + ... semvalue = model.evaluate(sentence, g) + ... if semvalue == testvalue: + ... print('*', end=' ') + ... g.purge() + * * * * * * * * * * * * * * * * * * * * * * + + +Tests for mapping from syntax to semantics +------------------------------------------ + +Load a valuation from a file. + + >>> import nltk.data + >>> from nltk.sem.util import parse_sents + >>> val = nltk.data.load('grammars/sample_grammars/valuation1.val') + >>> dom = val.domain + >>> m = Model(dom, val) + >>> g = Assignment(dom) + >>> gramfile = 'grammars/sample_grammars/sem2.fcfg' + >>> inputs = ['John sees a girl', 'every dog barks'] + >>> parses = parse_sents(inputs, gramfile) + >>> for sent, trees in zip(inputs, parses): + ... print() + ... print("Sentence: %s" % sent) + ... for tree in trees: + ... print("Parse:\n %s" %tree) + ... print("Semantics: %s" % root_semrep(tree)) + + Sentence: John sees a girl + Parse: + (S[SEM=] + (NP[-LOC, NUM='sg', SEM=<\P.P(john)>] + (PropN[-LOC, NUM='sg', SEM=<\P.P(john)>] John)) + (VP[NUM='sg', SEM=<\y.exists x.(girl(x) & see(y,x))>] + (TV[NUM='sg', SEM=<\X y.X(\x.see(y,x))>, TNS='pres'] sees) + (NP[NUM='sg', SEM=<\Q.exists x.(girl(x) & Q(x))>] + (Det[NUM='sg', SEM=<\P Q.exists x.(P(x) & Q(x))>] a) + (Nom[NUM='sg', SEM=<\x.girl(x)>] + (N[NUM='sg', SEM=<\x.girl(x)>] girl))))) + Semantics: exists x.(girl(x) & see(john,x)) + + Sentence: every dog barks + Parse: + (S[SEM= bark(x))>] + (NP[NUM='sg', SEM=<\Q.all x.(dog(x) -> Q(x))>] + (Det[NUM='sg', SEM=<\P Q.all x.(P(x) -> Q(x))>] every) + (Nom[NUM='sg', SEM=<\x.dog(x)>] + (N[NUM='sg', SEM=<\x.dog(x)>] dog))) + (VP[NUM='sg', SEM=<\x.bark(x)>] + (IV[NUM='sg', SEM=<\x.bark(x)>, TNS='pres'] barks))) + Semantics: all x.(dog(x) -> bark(x)) + + >>> sent = "every dog barks" + >>> result = nltk.sem.util.interpret_sents([sent], gramfile)[0] + >>> for (syntree, semrep) in result: + ... print(syntree) + ... print() + ... print(semrep) + (S[SEM= bark(x))>] + (NP[NUM='sg', SEM=<\Q.all x.(dog(x) -> Q(x))>] + (Det[NUM='sg', SEM=<\P Q.all x.(P(x) -> Q(x))>] every) + (Nom[NUM='sg', SEM=<\x.dog(x)>] + (N[NUM='sg', SEM=<\x.dog(x)>] dog))) + (VP[NUM='sg', SEM=<\x.bark(x)>] + (IV[NUM='sg', SEM=<\x.bark(x)>, TNS='pres'] barks))) + + all x.(dog(x) -> bark(x)) + + >>> result = nltk.sem.util.evaluate_sents([sent], gramfile, m, g)[0] + >>> for (syntree, semrel, value) in result: + ... print(syntree) + ... print() + ... print(semrep) + ... print() + ... print(value) + (S[SEM= bark(x))>] + (NP[NUM='sg', SEM=<\Q.all x.(dog(x) -> Q(x))>] + (Det[NUM='sg', SEM=<\P Q.all x.(P(x) -> Q(x))>] every) + (Nom[NUM='sg', SEM=<\x.dog(x)>] + (N[NUM='sg', SEM=<\x.dog(x)>] dog))) + (VP[NUM='sg', SEM=<\x.bark(x)>] + (IV[NUM='sg', SEM=<\x.bark(x)>, TNS='pres'] barks))) + + all x.(dog(x) -> bark(x)) + + True + + >>> sents = ['Mary walks', 'John sees a dog'] + >>> results = nltk.sem.util.interpret_sents(sents, 'grammars/sample_grammars/sem2.fcfg') + >>> for result in results: + ... for (synrep, semrep) in result: + ... print(synrep) + (S[SEM=] + (NP[-LOC, NUM='sg', SEM=<\P.P(mary)>] + (PropN[-LOC, NUM='sg', SEM=<\P.P(mary)>] Mary)) + (VP[NUM='sg', SEM=<\x.walk(x)>] + (IV[NUM='sg', SEM=<\x.walk(x)>, TNS='pres'] walks))) + (S[SEM=] + (NP[-LOC, NUM='sg', SEM=<\P.P(john)>] + (PropN[-LOC, NUM='sg', SEM=<\P.P(john)>] John)) + (VP[NUM='sg', SEM=<\y.exists x.(dog(x) & see(y,x))>] + (TV[NUM='sg', SEM=<\X y.X(\x.see(y,x))>, TNS='pres'] sees) + (NP[NUM='sg', SEM=<\Q.exists x.(dog(x) & Q(x))>] + (Det[NUM='sg', SEM=<\P Q.exists x.(P(x) & Q(x))>] a) + (Nom[NUM='sg', SEM=<\x.dog(x)>] + (N[NUM='sg', SEM=<\x.dog(x)>] dog))))) + +Cooper Storage +-------------- + + >>> from nltk.sem import cooper_storage as cs + >>> sentence = 'every girl chases a dog' + >>> trees = cs.parse_with_bindops(sentence, grammar='grammars/book_grammars/storage.fcfg') + >>> semrep = trees[0].label()['SEM'] + >>> cs_semrep = cs.CooperStore(semrep) + >>> print(cs_semrep.core) + chase(z2,z4) + >>> for bo in cs_semrep.store: + ... print(bo) + bo(\P.all x.(girl(x) -> P(x)),z2) + bo(\P.exists x.(dog(x) & P(x)),z4) + >>> cs_semrep.s_retrieve(trace=True) + Permutation 1 + (\P.all x.(girl(x) -> P(x)))(\z2.chase(z2,z4)) + (\P.exists x.(dog(x) & P(x)))(\z4.all x.(girl(x) -> chase(x,z4))) + Permutation 2 + (\P.exists x.(dog(x) & P(x)))(\z4.chase(z2,z4)) + (\P.all x.(girl(x) -> P(x)))(\z2.exists x.(dog(x) & chase(z2,x))) + + >>> for reading in cs_semrep.readings: + ... print(reading) + exists x.(dog(x) & all z3.(girl(z3) -> chase(z3,x))) + all x.(girl(x) -> exists z4.(dog(z4) & chase(x,z4))) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiment.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiment.doctest new file mode 100644 index 0000000..d981569 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiment.doctest @@ -0,0 +1,236 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=================== +Sentiment Analysis +=================== + + >>> from nltk.classify import NaiveBayesClassifier + >>> from nltk.corpus import subjectivity + >>> from nltk.sentiment import SentimentAnalyzer + >>> from nltk.sentiment.util import * + + >>> n_instances = 100 + >>> subj_docs = [(sent, 'subj') for sent in subjectivity.sents(categories='subj')[:n_instances]] + >>> obj_docs = [(sent, 'obj') for sent in subjectivity.sents(categories='obj')[:n_instances]] + >>> len(subj_docs), len(obj_docs) + (100, 100) + +Each document is represented by a tuple (sentence, label). The sentence is tokenized, +so it is represented by a list of strings: + + >>> subj_docs[0] + (['smart', 'and', 'alert', ',', 'thirteen', 'conversations', 'about', 'one', + 'thing', 'is', 'a', 'small', 'gem', '.'], 'subj') + +We separately split subjective and objective instances to keep a balanced uniform +class distribution in both train and test sets. + + >>> train_subj_docs = subj_docs[:80] + >>> test_subj_docs = subj_docs[80:100] + >>> train_obj_docs = obj_docs[:80] + >>> test_obj_docs = obj_docs[80:100] + >>> training_docs = train_subj_docs+train_obj_docs + >>> testing_docs = test_subj_docs+test_obj_docs + + >>> sentim_analyzer = SentimentAnalyzer() + >>> all_words_neg = sentim_analyzer.all_words([mark_negation(doc) for doc in training_docs]) + +We use simple unigram word features, handling negation: + + >>> unigram_feats = sentim_analyzer.unigram_word_feats(all_words_neg, min_freq=4) + >>> len(unigram_feats) + 83 + >>> sentim_analyzer.add_feat_extractor(extract_unigram_feats, unigrams=unigram_feats) + +We apply features to obtain a feature-value representation of our datasets: + + >>> training_set = sentim_analyzer.apply_features(training_docs) + >>> test_set = sentim_analyzer.apply_features(testing_docs) + +We can now train our classifier on the training set, and subsequently output the +evaluation results: + + >>> trainer = NaiveBayesClassifier.train + >>> classifier = sentim_analyzer.train(trainer, training_set) + Training classifier + >>> for key,value in sorted(sentim_analyzer.evaluate(test_set).items()): + ... print('{0}: {1}'.format(key, value)) + Evaluating NaiveBayesClassifier results... + Accuracy: 0.8 + F-measure [obj]: 0.8 + F-measure [subj]: 0.8 + Precision [obj]: 0.8 + Precision [subj]: 0.8 + Recall [obj]: 0.8 + Recall [subj]: 0.8 + + +Vader +------ + + >>> from nltk.sentiment.vader import SentimentIntensityAnalyzer + >>> sentences = ["VADER is smart, handsome, and funny.", # positive sentence example + ... "VADER is smart, handsome, and funny!", # punctuation emphasis handled correctly (sentiment intensity adjusted) + ... "VADER is very smart, handsome, and funny.", # booster words handled correctly (sentiment intensity adjusted) + ... "VADER is VERY SMART, handsome, and FUNNY.", # emphasis for ALLCAPS handled + ... "VADER is VERY SMART, handsome, and FUNNY!!!",# combination of signals - VADER appropriately adjusts intensity + ... "VADER is VERY SMART, really handsome, and INCREDIBLY FUNNY!!!",# booster words & punctuation make this close to ceiling for score + ... "The book was good.", # positive sentence + ... "The book was kind of good.", # qualified positive sentence is handled correctly (intensity adjusted) + ... "The plot was good, but the characters are uncompelling and the dialog is not great.", # mixed negation sentence + ... "A really bad, horrible book.", # negative sentence with booster words + ... "At least it isn't a horrible book.", # negated negative sentence with contraction + ... ":) and :D", # emoticons handled + ... "", # an empty string is correctly handled + ... "Today sux", # negative slang handled + ... "Today sux!", # negative slang with punctuation emphasis handled + ... "Today SUX!", # negative slang with capitalization emphasis + ... "Today kinda sux! But I'll get by, lol" # mixed sentiment example with slang and constrastive conjunction "but" + ... ] + >>> paragraph = "It was one of the worst movies I've seen, despite good reviews. \ + ... Unbelievably bad acting!! Poor direction. VERY poor production. \ + ... The movie was bad. Very bad movie. VERY bad movie. VERY BAD movie. VERY BAD movie!" + + >>> from nltk import tokenize + >>> lines_list = tokenize.sent_tokenize(paragraph) + >>> sentences.extend(lines_list) + + >>> tricky_sentences = [ + ... "Most automated sentiment analysis tools are shit.", + ... "VADER sentiment analysis is the shit.", + ... "Sentiment analysis has never been good.", + ... "Sentiment analysis with VADER has never been this good.", + ... "Warren Beatty has never been so entertaining.", + ... "I won't say that the movie is astounding and I wouldn't claim that \ + ... the movie is too banal either.", + ... "I like to hate Michael Bay films, but I couldn't fault this one", + ... "I like to hate Michael Bay films, BUT I couldn't help but fault this one", + ... "It's one thing to watch an Uwe Boll film, but another thing entirely \ + ... to pay for it", + ... "The movie was too good", + ... "This movie was actually neither that funny, nor super witty.", + ... "This movie doesn't care about cleverness, wit or any other kind of \ + ... intelligent humor.", + ... "Those who find ugly meanings in beautiful things are corrupt without \ + ... being charming.", + ... "There are slow and repetitive parts, BUT it has just enough spice to \ + ... keep it interesting.", + ... "The script is not fantastic, but the acting is decent and the cinematography \ + ... is EXCELLENT!", + ... "Roger Dodger is one of the most compelling variations on this theme.", + ... "Roger Dodger is one of the least compelling variations on this theme.", + ... "Roger Dodger is at least compelling as a variation on the theme.", + ... "they fall in love with the product", + ... "but then it breaks", + ... "usually around the time the 90 day warranty expires", + ... "the twin towers collapsed today", + ... "However, Mr. Carter solemnly argues, his client carried out the kidnapping \ + ... under orders and in the ''least offensive way possible.''" + ... ] + >>> sentences.extend(tricky_sentences) + >>> for sentence in sentences: + ... sid = SentimentIntensityAnalyzer() + ... print(sentence) + ... ss = sid.polarity_scores(sentence) + ... for k in sorted(ss): + ... print('{0}: {1}, '.format(k, ss[k]), end='') + ... print() + VADER is smart, handsome, and funny. + compound: 0.8316, neg: 0.0, neu: 0.254, pos: 0.746, + VADER is smart, handsome, and funny! + compound: 0.8439, neg: 0.0, neu: 0.248, pos: 0.752, + VADER is very smart, handsome, and funny. + compound: 0.8545, neg: 0.0, neu: 0.299, pos: 0.701, + VADER is VERY SMART, handsome, and FUNNY. + compound: 0.9227, neg: 0.0, neu: 0.246, pos: 0.754, + VADER is VERY SMART, handsome, and FUNNY!!! + compound: 0.9342, neg: 0.0, neu: 0.233, pos: 0.767, + VADER is VERY SMART, really handsome, and INCREDIBLY FUNNY!!! + compound: 0.9469, neg: 0.0, neu: 0.294, pos: 0.706, + The book was good. + compound: 0.4404, neg: 0.0, neu: 0.508, pos: 0.492, + The book was kind of good. + compound: 0.3832, neg: 0.0, neu: 0.657, pos: 0.343, + The plot was good, but the characters are uncompelling and the dialog is not great. + compound: -0.7042, neg: 0.327, neu: 0.579, pos: 0.094, + A really bad, horrible book. + compound: -0.8211, neg: 0.791, neu: 0.209, pos: 0.0, + At least it isn't a horrible book. + compound: 0.431, neg: 0.0, neu: 0.637, pos: 0.363, + :) and :D + compound: 0.7925, neg: 0.0, neu: 0.124, pos: 0.876, + + compound: 0.0, neg: 0.0, neu: 0.0, pos: 0.0, + Today sux + compound: -0.3612, neg: 0.714, neu: 0.286, pos: 0.0, + Today sux! + compound: -0.4199, neg: 0.736, neu: 0.264, pos: 0.0, + Today SUX! + compound: -0.5461, neg: 0.779, neu: 0.221, pos: 0.0, + Today kinda sux! But I'll get by, lol + compound: 0.5249, neg: 0.138, neu: 0.517, pos: 0.344, + It was one of the worst movies I've seen, despite good reviews. + compound: -0.7584, neg: 0.394, neu: 0.606, pos: 0.0, + Unbelievably bad acting!! + compound: -0.6572, neg: 0.686, neu: 0.314, pos: 0.0, + Poor direction. + compound: -0.4767, neg: 0.756, neu: 0.244, pos: 0.0, + VERY poor production. + compound: -0.6281, neg: 0.674, neu: 0.326, pos: 0.0, + The movie was bad. + compound: -0.5423, neg: 0.538, neu: 0.462, pos: 0.0, + Very bad movie. + compound: -0.5849, neg: 0.655, neu: 0.345, pos: 0.0, + VERY bad movie. + compound: -0.6732, neg: 0.694, neu: 0.306, pos: 0.0, + VERY BAD movie. + compound: -0.7398, neg: 0.724, neu: 0.276, pos: 0.0, + VERY BAD movie! + compound: -0.7616, neg: 0.735, neu: 0.265, pos: 0.0, + Most automated sentiment analysis tools are shit. + compound: -0.5574, neg: 0.375, neu: 0.625, pos: 0.0, + VADER sentiment analysis is the shit. + compound: 0.6124, neg: 0.0, neu: 0.556, pos: 0.444, + Sentiment analysis has never been good. + compound: -0.3412, neg: 0.325, neu: 0.675, pos: 0.0, + Sentiment analysis with VADER has never been this good. + compound: 0.5228, neg: 0.0, neu: 0.703, pos: 0.297, + Warren Beatty has never been so entertaining. + compound: 0.5777, neg: 0.0, neu: 0.616, pos: 0.384, + I won't say that the movie is astounding and I wouldn't claim that the movie is too banal either. + compound: 0.4215, neg: 0.0, neu: 0.851, pos: 0.149, + I like to hate Michael Bay films, but I couldn't fault this one + compound: 0.3153, neg: 0.157, neu: 0.534, pos: 0.309, + I like to hate Michael Bay films, BUT I couldn't help but fault this one + compound: -0.1531, neg: 0.277, neu: 0.477, pos: 0.246, + It's one thing to watch an Uwe Boll film, but another thing entirely to pay for it + compound: -0.2541, neg: 0.112, neu: 0.888, pos: 0.0, + The movie was too good + compound: 0.4404, neg: 0.0, neu: 0.58, pos: 0.42, + This movie was actually neither that funny, nor super witty. + compound: -0.6759, neg: 0.41, neu: 0.59, pos: 0.0, + This movie doesn't care about cleverness, wit or any other kind of intelligent humor. + compound: -0.1338, neg: 0.265, neu: 0.497, pos: 0.239, + Those who find ugly meanings in beautiful things are corrupt without being charming. + compound: -0.3553, neg: 0.314, neu: 0.493, pos: 0.192, + There are slow and repetitive parts, BUT it has just enough spice to keep it interesting. + compound: 0.4678, neg: 0.079, neu: 0.735, pos: 0.186, + The script is not fantastic, but the acting is decent and the cinematography is EXCELLENT! + compound: 0.7565, neg: 0.092, neu: 0.607, pos: 0.301, + Roger Dodger is one of the most compelling variations on this theme. + compound: 0.2944, neg: 0.0, neu: 0.834, pos: 0.166, + Roger Dodger is one of the least compelling variations on this theme. + compound: -0.1695, neg: 0.132, neu: 0.868, pos: 0.0, + Roger Dodger is at least compelling as a variation on the theme. + compound: 0.2263, neg: 0.0, neu: 0.84, pos: 0.16, + they fall in love with the product + compound: 0.6369, neg: 0.0, neu: 0.588, pos: 0.412, + but then it breaks + compound: 0.0, neg: 0.0, neu: 1.0, pos: 0.0, + usually around the time the 90 day warranty expires + compound: 0.0, neg: 0.0, neu: 1.0, pos: 0.0, + the twin towers collapsed today + compound: -0.2732, neg: 0.344, neu: 0.656, pos: 0.0, + However, Mr. Carter solemnly argues, his client carried out the kidnapping under orders and in the ''least offensive way possible.'' + compound: -0.5859, neg: 0.23, neu: 0.697, pos: 0.074, diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiwordnet.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiwordnet.doctest new file mode 100644 index 0000000..2595c91 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/sentiwordnet.doctest @@ -0,0 +1,41 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +====================== +SentiWordNet Interface +====================== + +SentiWordNet can be imported like this: + + >>> from nltk.corpus import sentiwordnet as swn + +------------ +SentiSynsets +------------ + + >>> breakdown = swn.senti_synset('breakdown.n.03') + >>> print(breakdown) + + >>> breakdown.pos_score() + 0.0 + >>> breakdown.neg_score() + 0.25 + >>> breakdown.obj_score() + 0.75 + + +------ +Lookup +------ + + >>> list(swn.senti_synsets('slow')) + [SentiSynset('decelerate.v.01'), SentiSynset('slow.v.02'), + SentiSynset('slow.v.03'), SentiSynset('slow.a.01'), + SentiSynset('slow.a.02'), SentiSynset('dense.s.04'), + SentiSynset('slow.a.04'), SentiSynset('boring.s.01'), + SentiSynset('dull.s.08'), SentiSynset('slowly.r.01'), + SentiSynset('behind.r.03')] + + >>> happy = swn.senti_synsets('happy', 'a') + + >>> all = swn.all_senti_synsets() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/setup_fixt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/setup_fixt.py new file mode 100644 index 0000000..818ce0c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/setup_fixt.py @@ -0,0 +1,26 @@ +from nltk.internals import find_binary, find_jar + + +def check_binary(binary: str, **args): + """Skip a test via `pytest.skip` if the `binary` executable is not found. + Keyword arguments are passed to `nltk.internals.find_binary`.""" + import pytest + + try: + find_binary(binary, **args) + except LookupError: + pytest.skip(f"Skipping test because the {binary} binary was not found.") + + +def check_jar(name_pattern: str, **args): + """Skip a test via `pytest.skip` if the `name_pattern` jar is not found. + Keyword arguments are passed to `nltk.internals.find_jar`. + + TODO: Investigate why the CoreNLP tests that rely on this check_jar failed + on the CI. https://github.com/nltk/nltk/pull/3060#issuecomment-1268355108 + """ + import pytest + + pytest.skip( + "Skipping test because the doctests requiring jars are inconsistent on the CI." + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/simple.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/simple.doctest new file mode 100644 index 0000000..e656d61 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/simple.doctest @@ -0,0 +1,83 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================= +EasyInstall Tests +================= + +This file contains some simple tests that will be run by EasyInstall in +order to test the installation when NLTK-Data is absent. + + +------------ +Tokenization +------------ + + >>> from nltk.tokenize import wordpunct_tokenize + >>> s = ("Good muffins cost $3.88\nin New York. Please buy me\n" + ... "two of them.\n\nThanks.") + >>> wordpunct_tokenize(s) + ['Good', 'muffins', 'cost', '$', '3', '.', '88', 'in', 'New', 'York', '.', + 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + +------- +Metrics +------- + + >>> from nltk.metrics import precision, recall, f_measure + >>> reference = 'DET NN VB DET JJ NN NN IN DET NN'.split() + >>> test = 'DET VB VB DET NN NN NN IN DET NN'.split() + >>> reference_set = set(reference) + >>> test_set = set(test) + >>> precision(reference_set, test_set) + 1.0 + >>> print(recall(reference_set, test_set)) + 0.8 + >>> print(f_measure(reference_set, test_set)) + 0.88888888888... + +------------------ +Feature Structures +------------------ + + >>> from nltk import FeatStruct + >>> fs1 = FeatStruct(PER=3, NUM='pl', GND='fem') + >>> fs2 = FeatStruct(POS='N', AGR=fs1) + >>> print(fs2) + [ [ GND = 'fem' ] ] + [ AGR = [ NUM = 'pl' ] ] + [ [ PER = 3 ] ] + [ ] + [ POS = 'N' ] + >>> print(fs2['AGR']) + [ GND = 'fem' ] + [ NUM = 'pl' ] + [ PER = 3 ] + >>> print(fs2['AGR']['PER']) + 3 + +------- +Parsing +------- + + >>> from nltk.parse.recursivedescent import RecursiveDescentParser + >>> from nltk.grammar import CFG + >>> grammar = CFG.fromstring(""" + ... S -> NP VP + ... PP -> P NP + ... NP -> 'the' N | N PP | 'the' N PP + ... VP -> V NP | V PP | V NP PP + ... N -> 'cat' | 'dog' | 'rug' + ... V -> 'chased' + ... P -> 'on' + ... """) + >>> rd = RecursiveDescentParser(grammar) + >>> sent = 'the cat chased the dog on the rug'.split() + >>> for t in rd.parse(sent): + ... print(t) + (S + (NP the (N cat)) + (VP (V chased) (NP the (N dog) (PP (P on) (NP the (N rug)))))) + (S + (NP the (N cat)) + (VP (V chased) (NP the (N dog)) (PP (P on) (NP the (N rug))))) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/stem.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/stem.doctest new file mode 100644 index 0000000..80b4b8a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/stem.doctest @@ -0,0 +1,105 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========== + Stemmers +========== + +Overview +~~~~~~~~ + +Stemmers remove morphological affixes from words, leaving only the +word stem. + + >>> from nltk.stem import * + +Unit tests for the Porter stemmer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from nltk.stem.porter import * + +Create a new Porter stemmer. + + >>> stemmer = PorterStemmer() + +Test the stemmer on various pluralised words. + + >>> plurals = ['caresses', 'flies', 'dies', 'mules', 'denied', + ... 'died', 'agreed', 'owned', 'humbled', 'sized', + ... 'meeting', 'stating', 'siezing', 'itemization', + ... 'sensational', 'traditional', 'reference', 'colonizer', + ... 'plotted'] + + >>> singles = [stemmer.stem(plural) for plural in plurals] + + >>> print(' '.join(singles)) + caress fli die mule deni die agre own humbl size meet + state siez item sensat tradit refer colon plot + + +Unit tests for Snowball stemmer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from nltk.stem.snowball import SnowballStemmer + +See which languages are supported. + + >>> print(" ".join(SnowballStemmer.languages)) + arabic danish dutch english finnish french german hungarian italian + norwegian porter portuguese romanian russian spanish swedish + +Create a new instance of a language specific subclass. + + >>> stemmer = SnowballStemmer("english") + +Stem a word. + + >>> print(stemmer.stem("running")) + run + +Decide not to stem stopwords. + + >>> stemmer2 = SnowballStemmer("english", ignore_stopwords=True) + >>> print(stemmer.stem("having")) + have + >>> print(stemmer2.stem("having")) + having + +The 'english' stemmer is better than the original 'porter' stemmer. + + >>> print(SnowballStemmer("english").stem("generously")) + generous + >>> print(SnowballStemmer("porter").stem("generously")) + gener + +.. note:: + + Extra stemmer tests can be found in `nltk.test.unit.test_stem`. + +Unit tests for ARLSTem Stemmer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from nltk.stem.arlstem import ARLSTem + +Create a Stemmer instance. + + >>> stemmer = ARLSTem() + +Stem a word. + + >>> stemmer.stem('يعمل') + 'عمل' + +Unit tests for ARLSTem2 Stemmer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from nltk.stem.arlstem2 import ARLSTem2 + +Create a Stemmer instance. + + >>> stemmer = ARLSTem2() + +Stem a word. + + >>> stemmer.stem('يعمل') + 'عمل' diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/tag.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tag.doctest new file mode 100644 index 0000000..c86178d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tag.doctest @@ -0,0 +1,475 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +Evaluation of Taggers +===================== + +Evaluating the standard NLTK PerceptronTagger using Accuracy, +Precision, Recall and F-measure for each of the tags. + + >>> from nltk.tag import PerceptronTagger + >>> from nltk.corpus import treebank + >>> tagger = PerceptronTagger() + >>> gold_data = treebank.tagged_sents()[10:20] + >>> print(tagger.accuracy(gold_data)) # doctest: +ELLIPSIS + 0.885931... + + >>> print(tagger.evaluate_per_tag(gold_data)) + Tag | Prec. | Recall | F-measure + -------+--------+--------+----------- + '' | 1.0000 | 1.0000 | 1.0000 + , | 1.0000 | 1.0000 | 1.0000 + -NONE- | 0.0000 | 0.0000 | 0.0000 + . | 1.0000 | 1.0000 | 1.0000 + : | 1.0000 | 1.0000 | 1.0000 + CC | 1.0000 | 1.0000 | 1.0000 + CD | 0.7647 | 1.0000 | 0.8667 + DT | 1.0000 | 1.0000 | 1.0000 + IN | 1.0000 | 1.0000 | 1.0000 + JJ | 0.5882 | 0.8333 | 0.6897 + JJR | 1.0000 | 1.0000 | 1.0000 + JJS | 1.0000 | 1.0000 | 1.0000 + NN | 0.7647 | 0.9630 | 0.8525 + NNP | 0.8929 | 1.0000 | 0.9434 + NNS | 1.0000 | 1.0000 | 1.0000 + POS | 1.0000 | 1.0000 | 1.0000 + PRP | 1.0000 | 1.0000 | 1.0000 + RB | 0.8000 | 1.0000 | 0.8889 + RBR | 0.0000 | 0.0000 | 0.0000 + TO | 1.0000 | 1.0000 | 1.0000 + VB | 1.0000 | 1.0000 | 1.0000 + VBD | 0.8571 | 0.9231 | 0.8889 + VBG | 1.0000 | 1.0000 | 1.0000 + VBN | 0.8333 | 0.5556 | 0.6667 + VBP | 0.5714 | 0.8000 | 0.6667 + VBZ | 1.0000 | 1.0000 | 1.0000 + WP | 1.0000 | 1.0000 | 1.0000 + `` | 1.0000 | 1.0000 | 1.0000 + + +List only the 10 most common tags: + + >>> print(tagger.evaluate_per_tag(gold_data, truncate=10, sort_by_count=True)) + Tag | Prec. | Recall | F-measure + -------+--------+--------+----------- + IN | 1.0000 | 1.0000 | 1.0000 + DT | 1.0000 | 1.0000 | 1.0000 + NN | 0.7647 | 0.9630 | 0.8525 + NNP | 0.8929 | 1.0000 | 0.9434 + NNS | 1.0000 | 1.0000 | 1.0000 + -NONE- | 0.0000 | 0.0000 | 0.0000 + CD | 0.7647 | 1.0000 | 0.8667 + VBD | 0.8571 | 0.9231 | 0.8889 + JJ | 0.5882 | 0.8333 | 0.6897 + , | 1.0000 | 1.0000 | 1.0000 + + +Similarly, we can display the confusion matrix for this tagger. + + >>> print(tagger.confusion(gold_data)) + | - | + | N | + | O | + | N J J N N P P R V V V V V | + | ' E C C D I J J J N N N O R R B T V B B B B B W ` | + | ' , - . : C D T N J R S N P S S P B R O B D G N P Z P ` | + -------+-------------------------------------------------------------------------------------+ + '' | <3> . . . . . . . . . . . . . . . . . . . . . . . . . . . | + , | .<11> . . . . . . . . . . . . . . . . . . . . . . . . . . | + -NONE- | . . <.> . . . 4 . . 4 . . 7 2 . . . 1 . . . . . . 3 . . . | + . | . . .<10> . . . . . . . . . . . . . . . . . . . . . . . . | + : | . . . . <1> . . . . . . . . . . . . . . . . . . . . . . . | + CC | . . . . . <5> . . . . . . . . . . . . . . . . . . . . . . | + CD | . . . . . .<13> . . . . . . . . . . . . . . . . . . . . . | + DT | . . . . . . .<28> . . . . . . . . . . . . . . . . . . . . | + IN | . . . . . . . .<34> . . . . . . . . . . . . . . . . . . . | + JJ | . . . . . . . . .<10> . . . 1 . . . . 1 . . . . . . . . . | + JJR | . . . . . . . . . . <1> . . . . . . . . . . . . . . . . . | + JJS | . . . . . . . . . . . <1> . . . . . . . . . . . . . . . . | + NN | . . . . . . . . . 1 . .<26> . . . . . . . . . . . . . . . | + NNP | . . . . . . . . . . . . .<25> . . . . . . . . . . . . . . | + NNS | . . . . . . . . . . . . . .<22> . . . . . . . . . . . . . | + POS | . . . . . . . . . . . . . . . <1> . . . . . . . . . . . . | + PRP | . . . . . . . . . . . . . . . . <3> . . . . . . . . . . . | + RB | . . . . . . . . . . . . . . . . . <4> . . . . . . . . . . | + RBR | . . . . . . . . . . . . . . . . . . <.> . . . . . . . . . | + TO | . . . . . . . . . . . . . . . . . . . <2> . . . . . . . . | + VB | . . . . . . . . . . . . . . . . . . . . <1> . . . . . . . | + VBD | . . . . . . . . . . . . . . . . . . . . .<12> . 1 . . . . | + VBG | . . . . . . . . . . . . . . . . . . . . . . <3> . . . . . | + VBN | . . . . . . . . . 2 . . . . . . . . . . . 2 . <5> . . . . | + VBP | . . . . . . . . . . . . 1 . . . . . . . . . . . <4> . . . | + VBZ | . . . . . . . . . . . . . . . . . . . . . . . . . <2> . . | + WP | . . . . . . . . . . . . . . . . . . . . . . . . . . <3> . | + `` | . . . . . . . . . . . . . . . . . . . . . . . . . . . <3>| + -------+-------------------------------------------------------------------------------------+ + (row = reference; col = test) + + +Brill Trainer with evaluation +============================= + + >>> # Perform the relevant imports. + >>> from nltk.tbl.template import Template + >>> from nltk.tag.brill import Pos, Word + >>> from nltk.tag import untag, RegexpTagger, BrillTaggerTrainer, UnigramTagger + + >>> # Load some data + >>> from nltk.corpus import treebank + >>> training_data = treebank.tagged_sents()[:100] + >>> baseline_data = treebank.tagged_sents()[100:200] + >>> gold_data = treebank.tagged_sents()[200:300] + >>> testing_data = [untag(s) for s in gold_data] + + >>> backoff = RegexpTagger([ + ... (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers + ... (r'(The|the|A|a|An|an)$', 'AT'), # articles + ... (r'.*able$', 'JJ'), # adjectives + ... (r'.*ness$', 'NN'), # nouns formed from adjectives + ... (r'.*ly$', 'RB'), # adverbs + ... (r'.*s$', 'NNS'), # plural nouns + ... (r'.*ing$', 'VBG'), # gerunds + ... (r'.*ed$', 'VBD'), # past tense verbs + ... (r'.*', 'NN') # nouns (default) + ... ]) + +We've now created a simple ``RegexpTagger``, which tags according to the regular expression +rules it has been supplied. This tagger in and of itself does not have a great accuracy. + + >>> backoff.accuracy(gold_data) #doctest: +ELLIPSIS + 0.245014... + +Neither does a simple ``UnigramTagger``. This tagger is trained on some data, +and will then first try to match unigrams (i.e. tokens) of the sentence it has +to tag to the learned data. + + >>> unigram_tagger = UnigramTagger(baseline_data) + >>> unigram_tagger.accuracy(gold_data) #doctest: +ELLIPSIS + 0.581196... + +The lackluster accuracy here can be explained with the following example: + + >>> unigram_tagger.tag(["I", "would", "like", "this", "sentence", "to", "be", "tagged"]) + [('I', 'NNP'), ('would', 'MD'), ('like', None), ('this', 'DT'), ('sentence', None), + ('to', 'TO'), ('be', 'VB'), ('tagged', None)] + +As you can see, many tokens are tagged as ``None``, as these tokens are OOV (out of vocabulary). +The ``UnigramTagger`` has never seen them, and as a result they are not in its database of known terms. + +In practice, a ``UnigramTagger`` is exclusively used in conjunction with a *backoff*. Our real +baseline which will use such a backoff. We'll create a ``UnigramTagger`` like before, but now +the ``RegexpTagger`` will be used as a backoff for the situations where the ``UnigramTagger`` +encounters an OOV token. + + >>> baseline = UnigramTagger(baseline_data, backoff=backoff) + >>> baseline.accuracy(gold_data) #doctest: +ELLIPSIS + 0.7537647... + +That is already much better. We can investigate the performance further by running +``evaluate_per_tag``. This method will output the *Precision*, *Recall* and *F-measure* +of each tag. + + >>> print(baseline.evaluate_per_tag(gold_data, sort_by_count=True)) + Tag | Prec. | Recall | F-measure + -------+--------+--------+----------- + NNP | 0.9674 | 0.2738 | 0.4269 + NN | 0.4111 | 0.9136 | 0.5670 + IN | 0.9383 | 0.9580 | 0.9480 + DT | 0.9819 | 0.8859 | 0.9314 + JJ | 0.8167 | 0.2970 | 0.4356 + NNS | 0.7393 | 0.9630 | 0.8365 + -NONE- | 1.0000 | 0.8345 | 0.9098 + , | 1.0000 | 1.0000 | 1.0000 + . | 1.0000 | 1.0000 | 1.0000 + VBD | 0.6429 | 0.8804 | 0.7431 + CD | 1.0000 | 0.9872 | 0.9935 + CC | 1.0000 | 0.9355 | 0.9667 + VB | 0.7778 | 0.3684 | 0.5000 + VBN | 0.9375 | 0.3000 | 0.4545 + RB | 0.7778 | 0.7447 | 0.7609 + TO | 1.0000 | 1.0000 | 1.0000 + VBZ | 0.9643 | 0.6429 | 0.7714 + VBG | 0.6415 | 0.9444 | 0.7640 + PRP$ | 1.0000 | 1.0000 | 1.0000 + PRP | 1.0000 | 0.5556 | 0.7143 + MD | 1.0000 | 1.0000 | 1.0000 + VBP | 0.6471 | 0.5789 | 0.6111 + POS | 1.0000 | 1.0000 | 1.0000 + $ | 1.0000 | 0.8182 | 0.9000 + '' | 1.0000 | 1.0000 | 1.0000 + : | 1.0000 | 1.0000 | 1.0000 + WDT | 0.4000 | 0.2000 | 0.2667 + `` | 1.0000 | 1.0000 | 1.0000 + JJR | 1.0000 | 0.5000 | 0.6667 + NNPS | 0.0000 | 0.0000 | 0.0000 + RBR | 1.0000 | 1.0000 | 1.0000 + -LRB- | 0.0000 | 0.0000 | 0.0000 + -RRB- | 0.0000 | 0.0000 | 0.0000 + RP | 0.6667 | 0.6667 | 0.6667 + EX | 0.5000 | 0.5000 | 0.5000 + JJS | 0.0000 | 0.0000 | 0.0000 + WP | 1.0000 | 1.0000 | 1.0000 + PDT | 0.0000 | 0.0000 | 0.0000 + AT | 0.0000 | 0.0000 | 0.0000 + + +It's clear that although the precision of tagging `"NNP"` is high, the recall is very low. +With other words, we're missing a lot of cases where the true label is `"NNP"`. We can see +a similar effect with `"JJ"`. + +We can also see a very expected result: The precision of `"NN"` is low, while the recall +is high. If a term is OOV (i.e. ``UnigramTagger`` defers it to ``RegexpTagger``) and +``RegexpTagger`` doesn't have a good rule for it, then it will be tagged as `"NN"`. So, +we catch almost all tokens that are truly labeled as `"NN"`, but we also tag as `"NN"` +for many tokens that shouldn't be `"NN"`. + +This method gives us some insight in what parts of the tagger needs more attention, and why. +However, it doesn't tell us what the terms with true label `"NNP"` or `"JJ"` are actually +tagged as. +To help that, we can create a confusion matrix. + + >>> print(baseline.confusion(gold_data)) + | - | + | - N - | + | L O R N P | + | R N R J J N N N P P P R R V V V V V W | + | ' B E B A C C D E I J J J M N N P N D O R P R B R T V B B B B B D W ` | + | $ ' , - - - . : T C D T X N J R S D N P S S T S P $ B R P O B D G N P Z T P ` | + -------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + $ | <9> . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . | + '' | . <10> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + , | . .<115> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + -LRB- | . . . <.> . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . | + -NONE- | . . . .<121> . . . . . . . . . . . . . 24 . . . . . . . . . . . . . . . . . . . . | + -RRB- | . . . . . <.> . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . . . | + . | . . . . . .<100> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + : | . . . . . . . <10> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + AT | . . . . . . . . <.> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + CC | . . . . . . . . . <58> . . . . . . . . 4 . . . . . . . . . . . . . . . . . . . . | + CD | . . . . . . . . . . <77> . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . | + DT | . . . . . . . . 1 . .<163> . 4 . . . . 13 . . . . . . . . . . . . . . . . . 3 . . | + EX | . . . . . . . . . . . . <1> . . . . . 1 . . . . . . . . . . . . . . . . . . . . | + IN | . . . . . . . . . . . . .<228> . . . . 8 . . . . . . . . . . . . . 2 . . . . . . | + JJ | . . . . . . . . . . . . . . <49> . . . 86 2 . 4 . . . . 6 . . . . 12 3 . 3 . . . . | + JJR | . . . . . . . . . . . . . . . <3> . . 3 . . . . . . . . . . . . . . . . . . . . | + JJS | . . . . . . . . . . . . . . . . <.> . 2 . . . . . . . . . . . . . . . . . . . . | + MD | . . . . . . . . . . . . . . . . . <19> . . . . . . . . . . . . . . . . . . . . . | + NN | . . . . . . . . . . . . . . 9 . . .<296> . . 5 . . . . . . . . 5 . 9 . . . . . . | + NNP | . . . . . . . . . . . 2 . . . . . . 199 <89> . 26 . . . . 2 . . . . 2 5 . . . . . . | + NNPS | . . . . . . . . . . . . . . . . . . . 1 <.> 3 . . . . . . . . . . . . . . . . . | + NNS | . . . . . . . . . . . . . . . . . . 5 . .<156> . . . . . . . . . . . . . 1 . . . | + PDT | . . . . . . . . . . . 1 . . . . . . . . . . <.> . . . . . . . . . . . . . . . . | + POS | . . . . . . . . . . . . . . . . . . . . . . . <14> . . . . . . . . . . . . . . . | + PRP | . . . . . . . . . . . . . . . . . . 10 . . 2 . . <15> . . . . . . . . . . . . . . | + PRP$ | . . . . . . . . . . . . . . . . . . . . . . . . . <28> . . . . . . . . . . . . . | + RB | . . . . . . . . . . . . 1 4 . . . . 6 . . . . . . . <35> . 1 . . . . . . . . . . | + RBR | . . . . . . . . . . . . . . . . . . . . . . . . . . . <4> . . . . . . . . . . . | + RP | . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . <2> . . . . . . . . . . | + TO | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <47> . . . . . . . . . | + VB | . . . . . . . . . . . . . . 2 . . . 30 . . . . . . . 1 . . . <21> . . . 3 . . . . | + VBD | . . . . . . . . . . . . . . . . . . 10 . . . . . . . . . . . . <81> . 1 . . . . . | + VBG | . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . <34> . . . . . . | + VBN | . . . . . . . . . . . . . . . . . . 4 . . . . . . . . . . . . 31 . <15> . . . . . | + VBP | . . . . . . . . . . . . . . . . . . 7 . . . . . . . . . . . 1 . . . <11> . . . . | + VBZ | . . . . . . . . . . . . . . . . . . . . . 15 . . . . . . . . . . . . . <27> . . . | + WDT | . . . . . . . . . . . . . 7 . . . . 1 . . . . . . . . . . . . . . . . . <2> . . | + WP | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <2> . | + `` | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <10>| + -------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + (row = reference; col = test) + + +Once again we can see that `"NN"` is the default if the tagger isn't sure. Beyond that, +we can see why the recall for `"NNP"` is so low: these tokens are often tagged as `"NN"`. +This effect can also be seen for `"JJ"`, where the majority of tokens that ought to be +tagged as `"JJ"` are actually tagged as `"NN"` by our tagger. + +This tagger will only serve as a baseline for the ``BrillTaggerTrainer``, which uses +templates to attempt to improve the performance of the tagger. + + >>> # Set up templates + >>> Template._cleartemplates() #clear any templates created in earlier tests + >>> templates = [Template(Pos([-1])), Template(Pos([-1]), Word([0]))] + + >>> # Construct a BrillTaggerTrainer + >>> tt = BrillTaggerTrainer(baseline, templates, trace=3) + >>> tagger1 = tt.train(training_data, max_rules=10) + TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: None) + Finding initial useful rules... + Found 618 useful rules. + + B | + S F r O | Score = Fixed - Broken + c i o t | R Fixed = num tags changed incorrect -> correct + o x k h | u Broken = num tags changed correct -> incorrect + r e e e | l Other = num tags changed incorrect -> incorrect + e d n r | e + ------------------+------------------------------------------------------- + 13 14 1 4 | NN->VB if Pos:TO@[-1] + 8 8 0 0 | NN->VB if Pos:MD@[-1] + 7 10 3 22 | NN->IN if Pos:NNS@[-1] + 5 5 0 0 | NN->VBP if Pos:PRP@[-1] + 5 5 0 0 | VBD->VBN if Pos:VBZ@[-1] + 5 5 0 0 | NNS->NN if Pos:IN@[-1] & Word:asbestos@[0] + 4 4 0 0 | NN->-NONE- if Pos:WP@[-1] + 4 4 0 3 | NN->NNP if Pos:-NONE-@[-1] + 4 6 2 2 | NN->NNP if Pos:NNP@[-1] + 4 4 0 0 | NNS->VBZ if Pos:PRP@[-1] + + >>> tagger1.rules()[1:3] + (Rule('000', 'NN', 'VB', [(Pos([-1]),'MD')]), Rule('000', 'NN', 'IN', [(Pos([-1]),'NNS')])) + + >>> tagger1.print_template_statistics(printunused=False) + TEMPLATE STATISTICS (TRAIN) 2 templates, 10 rules) + TRAIN ( 2417 tokens) initial 555 0.7704 final: 496 0.7948 + #ID | Score (train) | #Rules | Template + -------------------------------------------- + 000 | 54 0.915 | 9 0.900 | Template(Pos([-1])) + 001 | 5 0.085 | 1 0.100 | Template(Pos([-1]),Word([0])) + + + + >>> tagger1.accuracy(gold_data) # doctest: +ELLIPSIS + 0.769230... + + >>> print(tagger1.evaluate_per_tag(gold_data, sort_by_count=True)) + Tag | Prec. | Recall | F-measure + -------+--------+--------+----------- + NNP | 0.8298 | 0.3600 | 0.5021 + NN | 0.4435 | 0.8364 | 0.5797 + IN | 0.8476 | 0.9580 | 0.8994 + DT | 0.9819 | 0.8859 | 0.9314 + JJ | 0.8167 | 0.2970 | 0.4356 + NNS | 0.7464 | 0.9630 | 0.8410 + -NONE- | 1.0000 | 0.8414 | 0.9139 + , | 1.0000 | 1.0000 | 1.0000 + . | 1.0000 | 1.0000 | 1.0000 + VBD | 0.6723 | 0.8696 | 0.7583 + CD | 1.0000 | 0.9872 | 0.9935 + CC | 1.0000 | 0.9355 | 0.9667 + VB | 0.8103 | 0.8246 | 0.8174 + VBN | 0.9130 | 0.4200 | 0.5753 + RB | 0.7778 | 0.7447 | 0.7609 + TO | 1.0000 | 1.0000 | 1.0000 + VBZ | 0.9667 | 0.6905 | 0.8056 + VBG | 0.6415 | 0.9444 | 0.7640 + PRP$ | 1.0000 | 1.0000 | 1.0000 + PRP | 1.0000 | 0.5556 | 0.7143 + MD | 1.0000 | 1.0000 | 1.0000 + VBP | 0.6316 | 0.6316 | 0.6316 + POS | 1.0000 | 1.0000 | 1.0000 + $ | 1.0000 | 0.8182 | 0.9000 + '' | 1.0000 | 1.0000 | 1.0000 + : | 1.0000 | 1.0000 | 1.0000 + WDT | 0.4000 | 0.2000 | 0.2667 + `` | 1.0000 | 1.0000 | 1.0000 + JJR | 1.0000 | 0.5000 | 0.6667 + NNPS | 0.0000 | 0.0000 | 0.0000 + RBR | 1.0000 | 1.0000 | 1.0000 + -LRB- | 0.0000 | 0.0000 | 0.0000 + -RRB- | 0.0000 | 0.0000 | 0.0000 + RP | 0.6667 | 0.6667 | 0.6667 + EX | 0.5000 | 0.5000 | 0.5000 + JJS | 0.0000 | 0.0000 | 0.0000 + WP | 1.0000 | 1.0000 | 1.0000 + PDT | 0.0000 | 0.0000 | 0.0000 + AT | 0.0000 | 0.0000 | 0.0000 + + + >>> print(tagger1.confusion(gold_data)) + | - | + | - N - | + | L O R N P | + | R N R J J N N N P P P R R V V V V V W | + | ' B E B A C C D E I J J J M N N P N D O R P R B R T V B B B B B D W ` | + | $ ' , - - - . : T C D T X N J R S D N P S S T S P $ B R P O B D G N P Z T P ` | + -------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + $ | <9> . . . . . . . . . . . . . . . . . 1 . . . . . . . . . . . 1 . . . . . . . . | + '' | . <10> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + , | . .<115> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + -LRB- | . . . <.> . . . . . . . . . 1 . . . . 2 . . . . . . . . . . . . . . . . . . . . | + -NONE- | . . . .<122> . . . . . . . . 1 . . . . 22 . . . . . . . . . . . . . . . . . . . . | + -RRB- | . . . . . <.> . . . . . . . . . . . . 2 1 . . . . . . . . . . . . . . . . . . . | + . | . . . . . .<100> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + : | . . . . . . . <10> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + AT | . . . . . . . . <.> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | + CC | . . . . . . . . . <58> . . . . . . . . 2 1 . . . . . . . . . . . . . . 1 . . . . | + CD | . . . . . . . . . . <77> . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . | + DT | . . . . . . . . 1 . .<163> . 5 . . . . 12 . . . . . . . . . . . . . . . . . 3 . . | + EX | . . . . . . . . . . . . <1> . . . . . 1 . . . . . . . . . . . . . . . . . . . . | + IN | . . . . . . . . . . . . .<228> . . . . 8 . . . . . . . . . . . . . 2 . . . . . . | + JJ | . . . . . . . . . . . . . 4 <49> . . . 79 4 . 4 . . . . 6 . . . 1 12 3 . 3 . . . . | + JJR | . . . . . . . . . . . . . 2 . <3> . . 1 . . . . . . . . . . . . . . . . . . . . | + JJS | . . . . . . . . . . . . . . . . <.> . 2 . . . . . . . . . . . . . . . . . . . . | + MD | . . . . . . . . . . . . . . . . . <19> . . . . . . . . . . . . . . . . . . . . . | + NN | . . . . . . . . . . . . . 7 9 . . .<271> 16 . 5 . . . . . . . . 7 . 9 . . . . . . | + NNP | . . . . . . . . . . . 2 . 7 . . . . 163<117> . 26 . . . . 2 . . . 1 2 5 . . . . . . | + NNPS | . . . . . . . . . . . . . . . . . . . 1 <.> 3 . . . . . . . . . . . . . . . . . | + NNS | . . . . . . . . . . . . . . . . . . 5 . .<156> . . . . . . . . . . . . . 1 . . . | + PDT | . . . . . . . . . . . 1 . . . . . . . . . . <.> . . . . . . . . . . . . . . . . | + POS | . . . . . . . . . . . . . . . . . . . . . . . <14> . . . . . . . . . . . . . . . | + PRP | . . . . . . . . . . . . . . . . . . 10 . . 2 . . <15> . . . . . . . . . . . . . . | + PRP$ | . . . . . . . . . . . . . . . . . . . . . . . . . <28> . . . . . . . . . . . . . | + RB | . . . . . . . . . . . . 1 4 . . . . 6 . . . . . . . <35> . 1 . . . . . . . . . . | + RBR | . . . . . . . . . . . . . . . . . . . . . . . . . . . <4> . . . . . . . . . . . | + RP | . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . <2> . . . . . . . . . . | + TO | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <47> . . . . . . . . . | + VB | . . . . . . . . . . . . . . 2 . . . 4 . . . . . . . 1 . . . <47> . . . 3 . . . . | + VBD | . . . . . . . . . . . . . 1 . . . . 8 1 . . . . . . . . . . . <80> . 2 . . . . . | + VBG | . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . <34> . . . . . . | + VBN | . . . . . . . . . . . . . . . . . . 4 . . . . . . . . . . . . 25 . <21> . . . . . | + VBP | . . . . . . . . . . . . . 2 . . . . 4 . . . . . . . . . . . 1 . . . <12> . . . . | + VBZ | . . . . . . . . . . . . . . . . . . . . . 13 . . . . . . . . . . . . . <29> . . . | + WDT | . . . . . . . . . . . . . 7 . . . . 1 . . . . . . . . . . . . . . . . . <2> . . | + WP | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <2> . | + `` | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . <10>| + -------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + (row = reference; col = test) + + + >>> tagged, test_stats = tagger1.batch_tag_incremental(testing_data, gold_data) + >>> tagged[33][12:] + [('foreign', 'NN'), ('debt', 'NN'), ('of', 'IN'), ('$', '$'), ('64', 'CD'), + ('billion', 'CD'), ('*U*', '-NONE-'), ('--', ':'), ('the', 'DT'), ('third-highest', 'NN'), + ('in', 'IN'), ('the', 'DT'), ('developing', 'VBG'), ('world', 'NN'), ('.', '.')] + +Regression Tests +~~~~~~~~~~~~~~~~ + +Sequential Taggers +------------------ + +Add tests for: + - make sure backoff is being done correctly. + - make sure ngram taggers don't use previous sentences for context. + - make sure ngram taggers see 'beginning of the sentence' as a + unique context + - make sure regexp tagger's regexps are tried in order + - train on some simple examples, & make sure that the size & the + generated models are correct. + - make sure cutoff works as intended + - make sure that ngram models only exclude contexts covered by the + backoff tagger if the backoff tagger gets that context correct at + *all* locations. + + +Regression Testing for issue #1025 +================================== + +We want to ensure that a RegexpTagger can be created with more than 100 patterns +and does not fail with: "AssertionError: sorry, but this version only supports 100 named groups" + + >>> from nltk.tag import RegexpTagger + >>> patterns = [(str(i), 'NNP',) for i in range(200)] + >>> tagger = RegexpTagger(patterns) + +Regression Testing for issue #2483 +================================== + +Ensure that tagging with pos_tag (PerceptronTagger) does not throw an IndexError +when attempting tagging an empty string. What it must return instead is not +strictly defined. + + >>> from nltk.tag import pos_tag + >>> pos_tag(['', 'is', 'a', 'beautiful', 'day']) + [...] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/tokenize.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tokenize.doctest new file mode 100644 index 0000000..8418abc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tokenize.doctest @@ -0,0 +1,397 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + + >>> from nltk.tokenize import * + +Regression Tests: NLTKWordTokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tokenizing some test strings. + + >>> s1 = "On a $50,000 mortgage of 30 years at 8 percent, the monthly payment would be $366.88." + >>> word_tokenize(s1) + ['On', 'a', '$', '50,000', 'mortgage', 'of', '30', 'years', 'at', '8', 'percent', ',', 'the', 'monthly', 'payment', 'would', 'be', '$', '366.88', '.'] + >>> s2 = "\"We beat some pretty good teams to get here,\" Slocum said." + >>> word_tokenize(s2) + ['``', 'We', 'beat', 'some', 'pretty', 'good', 'teams', 'to', 'get', 'here', ',', "''", 'Slocum', 'said', '.'] + >>> s3 = "Well, we couldn't have this predictable, cliche-ridden, \"Touched by an Angel\" (a show creator John Masius worked on) wanna-be if she didn't." + >>> word_tokenize(s3) + ['Well', ',', 'we', 'could', "n't", 'have', 'this', 'predictable', ',', 'cliche-ridden', ',', '``', 'Touched', 'by', 'an', 'Angel', "''", '(', 'a', 'show', 'creator', 'John', 'Masius', 'worked', 'on', ')', 'wanna-be', 'if', 'she', 'did', "n't", '.'] + >>> s4 = "I cannot cannot work under these conditions!" + >>> word_tokenize(s4) + ['I', 'can', 'not', 'can', 'not', 'work', 'under', 'these', 'conditions', '!'] + >>> s5 = "The company spent $30,000,000 last year." + >>> word_tokenize(s5) + ['The', 'company', 'spent', '$', '30,000,000', 'last', 'year', '.'] + >>> s6 = "The company spent 40.75% of its income last year." + >>> word_tokenize(s6) + ['The', 'company', 'spent', '40.75', '%', 'of', 'its', 'income', 'last', 'year', '.'] + >>> s7 = "He arrived at 3:00 pm." + >>> word_tokenize(s7) + ['He', 'arrived', 'at', '3:00', 'pm', '.'] + >>> s8 = "I bought these items: books, pencils, and pens." + >>> word_tokenize(s8) + ['I', 'bought', 'these', 'items', ':', 'books', ',', 'pencils', ',', 'and', 'pens', '.'] + >>> s9 = "Though there were 150, 100 of them were old." + >>> word_tokenize(s9) + ['Though', 'there', 'were', '150', ',', '100', 'of', 'them', 'were', 'old', '.'] + >>> s10 = "There were 300,000, but that wasn't enough." + >>> word_tokenize(s10) + ['There', 'were', '300,000', ',', 'but', 'that', 'was', "n't", 'enough', '.'] + >>> s11 = "It's more'n enough." + >>> word_tokenize(s11) + ['It', "'s", 'more', "'n", 'enough', '.'] + +Gathering the spans of the tokenized strings. + + >>> s = '''Good muffins cost $3.88\nin New (York). Please (buy) me\ntwo of them.\n(Thanks).''' + >>> expected = [(0, 4), (5, 12), (13, 17), (18, 19), (19, 23), + ... (24, 26), (27, 30), (31, 32), (32, 36), (36, 37), (37, 38), + ... (40, 46), (47, 48), (48, 51), (51, 52), (53, 55), (56, 59), + ... (60, 62), (63, 68), (69, 70), (70, 76), (76, 77), (77, 78)] + >>> list(NLTKWordTokenizer().span_tokenize(s)) == expected + True + >>> expected = ['Good', 'muffins', 'cost', '$', '3.88', 'in', + ... 'New', '(', 'York', ')', '.', 'Please', '(', 'buy', ')', + ... 'me', 'two', 'of', 'them.', '(', 'Thanks', ')', '.'] + >>> [s[start:end] for start, end in NLTKWordTokenizer().span_tokenize(s)] == expected + True + + >>> s = '''I said, "I'd like to buy some ''good muffins" which cost $3.88\n each in New (York)."''' + >>> expected = [(0, 1), (2, 6), (6, 7), (8, 9), (9, 10), (10, 12), + ... (13, 17), (18, 20), (21, 24), (25, 29), (30, 32), (32, 36), + ... (37, 44), (44, 45), (46, 51), (52, 56), (57, 58), (58, 62), + ... (64, 68), (69, 71), (72, 75), (76, 77), (77, 81), (81, 82), + ... (82, 83), (83, 84)] + >>> list(NLTKWordTokenizer().span_tokenize(s)) == expected + True + >>> expected = ['I', 'said', ',', '"', 'I', "'d", 'like', 'to', + ... 'buy', 'some', "''", "good", 'muffins', '"', 'which', 'cost', + ... '$', '3.88', 'each', 'in', 'New', '(', 'York', ')', '.', '"'] + >>> [s[start:end] for start, end in NLTKWordTokenizer().span_tokenize(s)] == expected + True + +Testing improvement made to the TreebankWordTokenizer + + >>> sx1 = '\xabNow that I can do.\xbb' + >>> expected = ['\xab', 'Now', 'that', 'I', 'can', 'do', '.', '\xbb'] + >>> word_tokenize(sx1) == expected + True + >>> sx2 = 'The unicode 201C and 201D \u201cLEFT(RIGHT) DOUBLE QUOTATION MARK\u201d is also OPEN_PUNCT and CLOSE_PUNCT.' + >>> expected = ['The', 'unicode', '201C', 'and', '201D', '\u201c', 'LEFT', '(', 'RIGHT', ')', 'DOUBLE', 'QUOTATION', 'MARK', '\u201d', 'is', 'also', 'OPEN_PUNCT', 'and', 'CLOSE_PUNCT', '.'] + >>> word_tokenize(sx2) == expected + True + + +Testing treebank's detokenizer + + >>> from nltk.tokenize.treebank import TreebankWordDetokenizer + >>> detokenizer = TreebankWordDetokenizer() + >>> s = "On a $50,000 mortgage of 30 years at 8 percent, the monthly payment would be $366.88." + >>> detokenizer.detokenize(word_tokenize(s)) + 'On a $50,000 mortgage of 30 years at 8 percent, the monthly payment would be $366.88.' + >>> s = "\"We beat some pretty good teams to get here,\" Slocum said." + >>> detokenizer.detokenize(word_tokenize(s)) + '"We beat some pretty good teams to get here," Slocum said.' + >>> s = "Well, we couldn't have this predictable, cliche-ridden, \"Touched by an Angel\" (a show creator John Masius worked on) wanna-be if she didn't." + >>> detokenizer.detokenize(word_tokenize(s)) + 'Well, we couldn\'t have this predictable, cliche-ridden, "Touched by an Angel" (a show creator John Masius worked on) wanna-be if she didn\'t.' + >>> s = "I cannot cannot work under these conditions!" + >>> detokenizer.detokenize(word_tokenize(s)) + 'I cannot cannot work under these conditions!' + >>> s = "The company spent $30,000,000 last year." + >>> detokenizer.detokenize(word_tokenize(s)) + 'The company spent $30,000,000 last year.' + >>> s = "The company spent 40.75% of its income last year." + >>> detokenizer.detokenize(word_tokenize(s)) + 'The company spent 40.75% of its income last year.' + >>> s = "He arrived at 3:00 pm." + >>> detokenizer.detokenize(word_tokenize(s)) + 'He arrived at 3:00 pm.' + >>> s = "I bought these items: books, pencils, and pens." + >>> detokenizer.detokenize(word_tokenize(s)) + 'I bought these items: books, pencils, and pens.' + >>> s = "Though there were 150, 100 of them were old." + >>> detokenizer.detokenize(word_tokenize(s)) + 'Though there were 150, 100 of them were old.' + >>> s = "There were 300,000, but that wasn't enough." + >>> detokenizer.detokenize(word_tokenize(s)) + "There were 300,000, but that wasn't enough." + >>> s = 'How "are" you?' + >>> detokenizer.detokenize(word_tokenize(s)) + 'How "are" you?' + >>> s = "Hello (world)" + >>> detokenizer.detokenize(word_tokenize(s)) + 'Hello (world)' + >>> s = ' with (many) [kinds] of {parentheses}. "Sometimes it\'s inside (quotes)". ("Sometimes the otherway around").' + >>> detokenizer.detokenize(word_tokenize(s)) + ' with (many) [kinds] of {parentheses}. "Sometimes it\'s inside (quotes)". ("Sometimes the otherway around").' + >>> s = "Sentence ending with (parentheses)" + >>> detokenizer.detokenize(word_tokenize(s)) + 'Sentence ending with (parentheses)' + >>> s = "(Sentence) starting with parentheses." + >>> detokenizer.detokenize(word_tokenize(s)) + '(Sentence) starting with parentheses.' + >>> s = "I've" + >>> detokenizer.detokenize(word_tokenize(s)) + "I've" + >>> s = "Don't" + >>> detokenizer.detokenize(word_tokenize(s)) + "Don't" + >>> s = "I'd" + >>> detokenizer.detokenize(word_tokenize(s)) + "I'd" + + +Sentence tokenization in word_tokenize: + + >>> s11 = "I called Dr. Jones. I called Dr. Jones." + >>> word_tokenize(s11) + ['I', 'called', 'Dr.', 'Jones', '.', 'I', 'called', 'Dr.', 'Jones', '.'] + >>> s12 = ("Ich muss unbedingt daran denken, Mehl, usw. fur einen " + ... "Kuchen einzukaufen. Ich muss.") + >>> word_tokenize(s12) + ['Ich', 'muss', 'unbedingt', 'daran', 'denken', ',', 'Mehl', ',', 'usw', + '.', 'fur', 'einen', 'Kuchen', 'einzukaufen', '.', 'Ich', 'muss', '.'] + >>> word_tokenize(s12, 'german') + ['Ich', 'muss', 'unbedingt', 'daran', 'denken', ',', 'Mehl', ',', 'usw.', + 'fur', 'einen', 'Kuchen', 'einzukaufen', '.', 'Ich', 'muss', '.'] + + +Regression Tests: Regexp Tokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some additional test strings. + + >>> s = ("Good muffins cost $3.88\nin New York. Please buy me\n" + ... "two of them.\n\nThanks.") + >>> s2 = ("Alas, it has not rained today. When, do you think, " + ... "will it rain again?") + >>> s3 = ("

    Although this is not the case here, we must " + ... "not relax our vigilance!

    ") + + >>> regexp_tokenize(s2, r'[,\.\?!"]\s*', gaps=False) + [', ', '. ', ', ', ', ', '?'] + >>> regexp_tokenize(s2, r'[,\.\?!"]\s*', gaps=True) + ['Alas', 'it has not rained today', 'When', 'do you think', + 'will it rain again'] + +Take care to avoid using capturing groups: + + >>> regexp_tokenize(s3, r'', gaps=False) + ['

    ', '', '', '

    '] + >>> regexp_tokenize(s3, r'', gaps=False) + ['

    ', '', '', '

    '] + >>> regexp_tokenize(s3, r'', gaps=True) + ['Although this is ', 'not', + ' the case here, we must not relax our vigilance!'] + +Named groups are capturing groups, and confuse the tokenizer: + + >>> regexp_tokenize(s3, r'b|p)>', gaps=False) + ['p', 'b', 'b', 'p'] + >>> regexp_tokenize(s3, r'b|p)>', gaps=True) + ['p', 'Although this is ', 'b', 'not', 'b', + ' the case here, we must not relax our vigilance!', 'p'] + +Make sure that nested groups don't confuse the tokenizer: + + >>> regexp_tokenize(s2, r'(?:h|r|l)a(?:s|(?:i|n0))', gaps=False) + ['las', 'has', 'rai', 'rai'] + >>> regexp_tokenize(s2, r'(?:h|r|l)a(?:s|(?:i|n0))', gaps=True) + ['A', ', it ', ' not ', 'ned today. When, do you think, will it ', + 'n again?'] + +Back-references require capturing groups, and these are not supported: + + >>> regexp_tokenize("aabbbcccc", r'(.)\1') + ['a', 'b', 'c', 'c'] + +A simple sentence tokenizer '\.(\s+|$)' + + >>> regexp_tokenize(s, pattern=r'\.(?:\s+|$)', gaps=True) + ['Good muffins cost $3.88\nin New York', + 'Please buy me\ntwo of them', 'Thanks'] + + +Regression Tests: TweetTokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TweetTokenizer is a tokenizer specifically designed for micro-blogging tokenization tasks. + + >>> from nltk.tokenize import TweetTokenizer + >>> tknzr = TweetTokenizer() + >>> s0 = "This is a cooool #dummysmiley: :-) :-P <3 and some arrows < > -> <--" + >>> tknzr.tokenize(s0) + ['This', 'is', 'a', 'cooool', '#dummysmiley', ':', ':-)', ':-P', '<3', 'and', 'some', 'arrows', '<', '>', '->', '<--'] + >>> s1 = "@Joyster2012 @CathStaincliffe Good for you, girl!! Best wishes :-)" + >>> tknzr.tokenize(s1) + ['@Joyster2012', '@CathStaincliffe', 'Good', 'for', 'you', ',', 'girl', '!', '!', 'Best', 'wishes', ':-)'] + >>> s2 = "3Points for #DreamTeam Gooo BAILEY! :) #PBB737Gold @PBBabscbn" + >>> tknzr.tokenize(s2) + ['3Points', 'for', '#DreamTeam', 'Gooo', 'BAILEY', '!', ':)', '#PBB737Gold', '@PBBabscbn'] + >>> s3 = "@Insanomania They do... Their mentality doesn't :(" + >>> tknzr.tokenize(s3) + ['@Insanomania', 'They', 'do', '...', 'Their', 'mentality', "doesn't", ':('] + >>> s4 = "RT @facugambande: Ya por arrancar a grabar !!! #TirenTirenTiren vamoo !!" + >>> tknzr.tokenize(s4) + ['RT', '@facugambande', ':', 'Ya', 'por', 'arrancar', 'a', 'grabar', '!', '!', '!', '#TirenTirenTiren', 'vamoo', '!', '!'] + >>> tknzr = TweetTokenizer(reduce_len=True) + >>> s5 = "@crushinghes the summer holidays are great but I'm so bored already :(" + >>> tknzr.tokenize(s5) + ['@crushinghes', 'the', 'summer', 'holidays', 'are', 'great', 'but', "I'm", 'so', 'bored', 'already', ':('] + +It is possible to specify `strip_handles` and `reduce_len` parameters for a TweetTokenizer instance. Setting `strip_handles` to True, the tokenizer will remove Twitter handles (e.g. usernames). Setting `reduce_len` to True, repeated character sequences of length 3 or greater will be replaced with sequences of length 3. + + >>> tknzr = TweetTokenizer(strip_handles=True, reduce_len=True) + >>> s6 = '@remy: This is waaaaayyyy too much for you!!!!!!' + >>> tknzr.tokenize(s6) + [':', 'This', 'is', 'waaayyy', 'too', 'much', 'for', 'you', '!', '!', '!'] + >>> s7 = '@_willy65: No place for @chuck tonight. Sorry.' + >>> tknzr.tokenize(s7) + [':', 'No', 'place', 'for', 'tonight', '.', 'Sorry', '.'] + >>> s8 = '@mar_tin is a great developer. Contact him at mar_tin@email.com.' + >>> tknzr.tokenize(s8) + ['is', 'a', 'great', 'developer', '.', 'Contact', 'him', 'at', 'mar_tin@email.com', '.'] + +The `preserve_case` parameter (default: True) allows to convert uppercase tokens to lowercase tokens. Emoticons are not affected: + + >>> tknzr = TweetTokenizer(preserve_case=False) + >>> s9 = "@jrmy: I'm REALLY HAPPYYY about that! NICEEEE :D :P" + >>> tknzr.tokenize(s9) + ['@jrmy', ':', "i'm", 'really', 'happyyy', 'about', 'that', '!', 'niceeee', ':D', ':P'] + +It should not hang on long sequences of the same punctuation character. + + >>> tknzr = TweetTokenizer() + >>> s10 = "Photo: Aujourd'hui sur http://t.co/0gebOFDUzn Projet... http://t.co/bKfIUbydz2.............................. http://fb.me/3b6uXpz0L" + >>> tknzr.tokenize(s10) + ['Photo', ':', "Aujourd'hui", 'sur', 'http://t.co/0gebOFDUzn', 'Projet', '...', 'http://t.co/bKfIUbydz2', '...', 'http://fb.me/3b6uXpz0L'] + +Tokenizing multiple sentences at once: + + >>> tknzr = TweetTokenizer() + >>> sentences = [ + ... "This is a cooool #dummysmiley: :-) :-P <3 and some arrows < > -> <--", + ... "@jrmy: I'm REALLY HAPPYYY about that! NICEEEE :D :P", + ... "@_willy65: No place for @chuck tonight. Sorry." + ... ] + >>> tknzr.tokenize_sents(sentences) # doctest: +NORMALIZE_WHITESPACE + [['This', 'is', 'a', 'cooool', '#dummysmiley', ':', ':-)', ':-P', '<3', 'and', 'some', 'arrows', '<', '>', '->', '<--'], + ['@jrmy', ':', "I'm", 'REALLY', 'HAPPYYY', 'about', 'that', '!', 'NICEEEE', ':D', ':P'], + ['@_willy65', ':', 'No', 'place', 'for', '@chuck', 'tonight', '.', 'Sorry', '.']] + + +Regression Tests: PunktSentenceTokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The sentence splitter should remove whitespace following the sentence boundary. + + >>> pst = PunktSentenceTokenizer() + >>> pst.tokenize('See Section 3). Or Section 2). ') + ['See Section 3).', 'Or Section 2).'] + >>> pst.tokenize('See Section 3.) Or Section 2.) ') + ['See Section 3.)', 'Or Section 2.)'] + >>> pst.tokenize('See Section 3.) Or Section 2.) ', realign_boundaries=False) + ['See Section 3.', ') Or Section 2.', ')'] + + +Two instances of PunktSentenceTokenizer should not share PunktParameters. + + >>> pst = PunktSentenceTokenizer() + >>> pst2 = PunktSentenceTokenizer() + >>> pst._params is pst2._params + False + +Testing mutable default arguments for https://github.com/nltk/nltk/pull/2067 + + >>> from nltk.tokenize.punkt import PunktBaseClass, PunktTrainer, PunktSentenceTokenizer + >>> from nltk.tokenize.punkt import PunktLanguageVars, PunktParameters + >>> pbc = PunktBaseClass(lang_vars=None, params=None) + >>> type(pbc._params) + + >>> type(pbc._lang_vars) + + >>> pt = PunktTrainer(lang_vars=None) + >>> type(pt._lang_vars) + + >>> pst = PunktSentenceTokenizer(lang_vars=None) + >>> type(pst._lang_vars) + + +Testing that inputs can start with dots. + + >>> pst = PunktSentenceTokenizer(lang_vars=None) + >>> pst.tokenize(". This input starts with a dot. This used to cause issues.") + ['.', 'This input starts with a dot.', 'This used to cause issues.'] + +Regression Tests: align_tokens +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Post-hoc alignment of tokens with a source string + + >>> from nltk.tokenize.util import align_tokens + >>> list(align_tokens([''], "")) + [(0, 0)] + >>> list(align_tokens([''], " ")) + [(0, 0)] + >>> list(align_tokens([], "")) + [] + >>> list(align_tokens([], " ")) + [] + >>> list(align_tokens(['a'], "a")) + [(0, 1)] + >>> list(align_tokens(['abc', 'def'], "abcdef")) + [(0, 3), (3, 6)] + >>> list(align_tokens(['abc', 'def'], "abc def")) + [(0, 3), (4, 7)] + >>> list(align_tokens(['ab', 'cd'], "ab cd ef")) + [(0, 2), (3, 5)] + >>> list(align_tokens(['ab', 'cd', 'ef'], "ab cd ef")) + [(0, 2), (3, 5), (6, 8)] + >>> list(align_tokens(['ab', 'cd', 'efg'], "ab cd ef")) + Traceback (most recent call last): + .... + ValueError: substring "efg" not found in "ab cd ef" + >>> list(align_tokens(['ab', 'cd', 'ef', 'gh'], "ab cd ef")) + Traceback (most recent call last): + .... + ValueError: substring "gh" not found in "ab cd ef" + >>> list(align_tokens(['The', 'plane', ',', 'bound', 'for', 'St', 'Petersburg', ',', 'crashed', 'in', 'Egypt', "'s", 'Sinai', 'desert', 'just', '23', 'minutes', 'after', 'take-off', 'from', 'Sharm', 'el-Sheikh', 'on', 'Saturday', '.'], "The plane, bound for St Petersburg, crashed in Egypt's Sinai desert just 23 minutes after take-off from Sharm el-Sheikh on Saturday.")) + [(0, 3), (4, 9), (9, 10), (11, 16), (17, 20), (21, 23), (24, 34), (34, 35), (36, 43), (44, 46), (47, 52), (52, 54), (55, 60), (61, 67), (68, 72), (73, 75), (76, 83), (84, 89), (90, 98), (99, 103), (104, 109), (110, 119), (120, 122), (123, 131), (131, 132)] + + +Regression Tests: MWETokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Pickle an MWETokenizer + + >>> from nltk.tokenize import MWETokenizer + >>> import pickle + + >>> tokenizer = MWETokenizer([('hors', "d'oeuvre")], separator='+') + >>> p = pickle.dumps(tokenizer) + >>> unpickeled = pickle.loads(p) + >>> unpickeled.tokenize("An hors d'oeuvre tonight, sir?".split()) + ['An', "hors+d'oeuvre", 'tonight,', 'sir?'] + + +Regression Tests: TextTilingTokenizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TextTilingTokenizer tokenizes text into coherent subtopic chunks based upon Hearst's TextTiling algorithm. + + >>> from nltk.tokenize import TextTilingTokenizer + >>> from nltk.corpus import brown + >>> tt = TextTilingTokenizer() + >>> tt.tokenize(brown.raw()[0:1000]) + ["\n\n\tThe/at Fulton/np-tl County/nn-tl Grand/jj-tl Jury/nn-tl said/vbd Friday/nr an/at investigation/nn of/in Atlanta's/np$ recent/jj primary/nn election/nn produced/vbd ``/`` no/at evidence/nn ''/'' that/cs any/dti irregularities/nns took/vbd place/nn ./.\n\n\n\tThe/at jury/nn further/rbr said/vbd in/in term-end/nn presentments/nns that/cs the/at City/nn-tl Executive/jj-tl Committee/nn-tl ,/, which/wdt had/hvd over-all/jj charge/nn of/in the/at election/nn ,/, ``/`` deserves/vbz the/at praise/nn and/cc thanks/nns of/in the/at City/nn-tl of/in-tl Atlanta/np-tl ''/'' for/in the/at manner/nn in/in which/wdt the/at election/nn was/bedz conducted/vbn ./.\n\n\n\tThe/at September-October/np term/nn jury/nn had/hvd been/ben charged/vbn by/in Fulton/np-tl Superior/jj-tl Court/nn-tl Judge/nn-tl Durwood/np Pye/np to/to investigate/vb reports/nns of/in possible/jj ``/`` irregularities/nns ''/'' in/in the/at hard-fought/jj primary/nn which/wdt was/bedz won/vbn by/in Mayor-nominate/nn-tl Ivan/np Allen/np Jr./"] + +Test that `ValueError` exceptions are raised when illegal arguments are used. + + >>> TextTilingTokenizer(similarity_method='foo').tokenize(brown.raw()[0:1000]) + Traceback (most recent call last): + ... + ValueError: Similarity method foo not recognized + >>> TextTilingTokenizer(smoothing_method='bar').tokenize(brown.raw()[0:1000]) + Traceback (most recent call last): + ... + ValueError: Smoothing method bar not recognized diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/toolbox.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/toolbox.doctest new file mode 100644 index 0000000..5f83c7f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/toolbox.doctest @@ -0,0 +1,306 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=============================== +Unit test cases for ``toolbox`` +=============================== + + >>> from nltk import toolbox + +-------------------------- +``toolbox.StandardFormat`` +-------------------------- + + >>> f = toolbox.StandardFormat() + +``toolbox.StandardFormat.open()`` +--------------------------------- + >>> import os, tempfile + >>> (fd, fname) = tempfile.mkstemp() + >>> tf = os.fdopen(fd, "w") + >>> _ = tf.write('\\lx a value\n\\lx another value\n') + >>> tf.close() + >>> f = toolbox.StandardFormat() + >>> f.open(fname) + >>> list(f.fields()) + [('lx', 'a value'), ('lx', 'another value')] + >>> f.close() + >>> os.unlink(fname) + +``toolbox.StandardFormat.open_string()`` +---------------------------------------- + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\\lx another value\n') + >>> list(f.fields()) + [('lx', 'a value'), ('lx', 'another value')] + >>> f.close() + +``toolbox.StandardFormat.close()`` +---------------------------------- + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\\lx another value\n') + >>> list(f.fields()) + [('lx', 'a value'), ('lx', 'another value')] + >>> f.close() + +``toolbox.StandardFormat.line_num`` +--------------------------------------- + +``StandardFormat.line_num`` contains the line number of the last line returned: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\\lx another value\n\\lx a third value\n') + >>> line_nums = [] + >>> for l in f.raw_fields(): + ... line_nums.append(f.line_num) + >>> line_nums + [1, 2, 3] + +``StandardFormat.line_num`` contains the line number of the last line returned: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx two\nlines\n\\lx three\nlines\n\n\\lx two\nlines\n') + >>> line_nums = [] + >>> for l in f.raw_fields(): + ... line_nums.append(f.line_num) + >>> line_nums + [2, 5, 7] + +``StandardFormat.line_num`` doesn't exist before opening or after closing +a file or string: + + >>> f = toolbox.StandardFormat() + >>> f.line_num + Traceback (most recent call last): + ... + AttributeError: 'StandardFormat' object has no attribute 'line_num' + >>> f.open_string('\\lx two\nlines\n\\lx three\nlines\n\n\\lx two\nlines\n') + >>> line_nums = [] + >>> for l in f.raw_fields(): + ... line_nums.append(f.line_num) + >>> line_nums + [2, 5, 7] + >>> f.close() + >>> f.line_num + Traceback (most recent call last): + ... + AttributeError: 'StandardFormat' object has no attribute 'line_num' + +``toolbox.StandardFormat.raw_fields()`` +--------------------------------------- +``raw_fields()`` returns an iterator over tuples of two strings representing the +marker and its value. The marker is given without the backslash and the value +without its trailing newline: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\\lx another value\n') + >>> list(f.raw_fields()) + [('lx', 'a value'), ('lx', 'another value')] + +an empty file returns nothing: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('') + >>> list(f.raw_fields()) + [] + +file with only a newline returns WHAT SHOULD IT RETURN???: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\n') + >>> list(f.raw_fields()) + [(None, '')] + +file with only one field should be parsed ok: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx one value\n') + >>> list(f.raw_fields()) + [('lx', 'one value')] + +file without a trailing newline should be parsed ok: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\\lx another value') + >>> list(f.raw_fields()) + [('lx', 'a value'), ('lx', 'another value')] + +trailing white space is preserved except for the final newline: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx trailing space \n\\lx trailing tab\t\n\\lx extra newline\n\n') + >>> list(f.raw_fields()) + [('lx', 'trailing space '), ('lx', 'trailing tab\t'), ('lx', 'extra newline\n')] + +line wrapping is preserved: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\nmore of the value\nand still more\n\\lc another val\n') + >>> list(f.raw_fields()) + [('lx', 'a value\nmore of the value\nand still more'), ('lc', 'another val')] + +file beginning with a multiline record should be parsed ok: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\nmore of the value\nand still more\n\\lc another val\n') + >>> list(f.raw_fields()) + [('lx', 'a value\nmore of the value\nand still more'), ('lc', 'another val')] + +file ending with a multiline record should be parsed ok: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lc a value\n\\lx another value\nmore of the value\nand still more\n') + >>> list(f.raw_fields()) + [('lc', 'a value'), ('lx', 'another value\nmore of the value\nand still more')] + +file beginning with a BOM should be parsed ok: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\xef\xbb\xbf\\lx a value\n\\lx another value\n') + >>> list(f.raw_fields()) + [('lx', 'a value'), ('lx', 'another value')] + +file beginning with two BOMs should ignore only the first one: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\xef\xbb\xbf\xef\xbb\xbf\\lx a value\n\\lx another value\n') + >>> list(f.raw_fields()) + [(None, '\xef\xbb\xbf\\lx a value'), ('lx', 'another value')] + +should not ignore a BOM not at the beginning of the file: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\n\xef\xbb\xbf\\lx another value\n') + >>> list(f.raw_fields()) + [('lx', 'a value\n\xef\xbb\xbf\\lx another value')] + +``toolbox.StandardFormat.fields()`` +----------------------------------- +trailing white space is not preserved: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx trailing space \n\\lx trailing tab\t\n\\lx extra newline\n\n') + >>> list(f.fields()) + [('lx', 'trailing space'), ('lx', 'trailing tab'), ('lx', 'extra newline')] + +multiline fields are unwrapped: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\lx a value\nmore of the value\nand still more\n\\lc another val\n') + >>> list(f.fields()) + [('lx', 'a value more of the value and still more'), ('lc', 'another val')] + +markers +------- +A backslash in the first position on a new line indicates the start of a +marker. The backslash is not part of the marker: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\mk a value\n') + >>> list(f.fields()) + [('mk', 'a value')] + +If the backslash occurs later in the line it does not indicate the start +of a marker: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\mk a value\n \\mk another one\n') + >>> list(f.raw_fields()) + [('mk', 'a value\n \\mk another one')] + +There is no specific limit to the length of a marker: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\this_is_an_extremely_long_marker value\n') + >>> list(f.fields()) + [('this_is_an_extremely_long_marker', 'value')] + +A marker can contain any non white space character: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\`~!@#$%^&*()_-=+[{]}\\|,<.>/?;:"0123456789 value\n') + >>> list(f.fields()) + [('`~!@#$%^&*()_-=+[{]}\\|,<.>/?;:"0123456789', 'value')] + +A marker is terminated by any white space character: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\mk a value\n\\mk\tanother one\n\\mk\rthird one\n\\mk\ffourth one') + >>> list(f.fields()) + [('mk', 'a value'), ('mk', 'another one'), ('mk', 'third one'), ('mk', 'fourth one')] + +Consecutive whitespace characters (except newline) are treated the same as one: + + >>> f = toolbox.StandardFormat() + >>> f.open_string('\\mk \t\r\fa value\n') + >>> list(f.fields()) + [('mk', 'a value')] + +----------------------- +``toolbox.ToolboxData`` +----------------------- + + >>> db = toolbox.ToolboxData() + +``toolbox.ToolboxData.parse()`` +------------------------------- +check that normal parsing works: + + >>> from xml.etree import ElementTree + >>> td = toolbox.ToolboxData() + >>> s = """\\_sh v3.0 400 Rotokas Dictionary + ... \\_DateStampHasFourDigitYear + ... + ... \\lx kaa + ... \\ps V.A + ... \\ge gag + ... \\gp nek i pas + ... + ... \\lx kaa + ... \\ps V.B + ... \\ge strangle + ... \\gp pasim nek + ... """ + >>> td.open_string(s) + >>> tree = td.parse(key='lx') + >>> tree.tag + 'toolbox_data' + >>> ElementTree.tostring(list(tree)[0]).decode('utf8') + '
    <_sh>v3.0 400 Rotokas Dictionary<_DateStampHasFourDigitYear />
    ' + >>> ElementTree.tostring(list(tree)[1]).decode('utf8') + 'kaaV.Agagnek i pas' + >>> ElementTree.tostring(list(tree)[2]).decode('utf8') + 'kaaV.Bstranglepasim nek' + +check that guessing the key marker works: + + >>> from xml.etree import ElementTree + >>> td = toolbox.ToolboxData() + >>> s = """\\_sh v3.0 400 Rotokas Dictionary + ... \\_DateStampHasFourDigitYear + ... + ... \\lx kaa + ... \\ps V.A + ... \\ge gag + ... \\gp nek i pas + ... + ... \\lx kaa + ... \\ps V.B + ... \\ge strangle + ... \\gp pasim nek + ... """ + >>> td.open_string(s) + >>> tree = td.parse() + >>> ElementTree.tostring(list(tree)[0]).decode('utf8') + '
    <_sh>v3.0 400 Rotokas Dictionary<_DateStampHasFourDigitYear />
    ' + >>> ElementTree.tostring(list(tree)[1]).decode('utf8') + 'kaaV.Agagnek i pas' + >>> ElementTree.tostring(list(tree)[2]).decode('utf8') + 'kaaV.Bstranglepasim nek' + +----------------------- +``toolbox`` functions +----------------------- + +``toolbox.to_sfm_string()`` +------------------------------- diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/translate.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/translate.doctest new file mode 100644 index 0000000..1e5b695 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/translate.doctest @@ -0,0 +1,240 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. -*- coding: utf-8 -*- + +========= +Alignment +========= + +Corpus Reader +------------- + + >>> from nltk.corpus import comtrans + >>> words = comtrans.words('alignment-en-fr.txt') + >>> for word in words[:6]: + ... print(word) + Resumption + of + the + session + I + declare + >>> als = comtrans.aligned_sents('alignment-en-fr.txt')[0] + >>> als + AlignedSent(['Resumption', 'of', 'the', 'session'], + ['Reprise', 'de', 'la', 'session'], + Alignment([(0, 0), (1, 1), (2, 2), (3, 3)])) + + +Alignment Objects +----------------- + +Aligned sentences are simply a mapping between words in a sentence: + + >>> print(" ".join(als.words)) + Resumption of the session + >>> print(" ".join(als.mots)) + Reprise de la session + >>> als.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, 3)]) + + +Usually we look at them from the perspective of a source to a target language, +but they are easily inverted: + + >>> als.invert() + AlignedSent(['Reprise', 'de', 'la', 'session'], + ['Resumption', 'of', 'the', 'session'], + Alignment([(0, 0), (1, 1), (2, 2), (3, 3)])) + + +We can create new alignments, but these need to be in the correct range of +the corresponding sentences: + + >>> from nltk.translate import Alignment, AlignedSent + >>> als = AlignedSent(['Reprise', 'de', 'la', 'session'], + ... ['Resumption', 'of', 'the', 'session'], + ... Alignment([(0, 0), (1, 4), (2, 1), (3, 3)])) + Traceback (most recent call last): + ... + IndexError: Alignment is outside boundary of mots + + +You can set alignments with any sequence of tuples, so long as the first two +indexes of the tuple are the alignment indices: + + >>> als.alignment = Alignment([(0, 0), (1, 1), (2, 2, "boat"), (3, 3, False, (1,2))]) + + >>> Alignment([(0, 0), (1, 1), (2, 2, "boat"), (3, 3, False, (1,2))]) + Alignment([(0, 0), (1, 1), (2, 2, 'boat'), (3, 3, False, (1, 2))]) + + +Alignment Algorithms +-------------------- + +EM for IBM Model 1 +~~~~~~~~~~~~~~~~~~ + +Here is an example from Koehn, 2010: + + >>> from nltk.translate import IBMModel1 + >>> corpus = [AlignedSent(['the', 'house'], ['das', 'Haus']), + ... AlignedSent(['the', 'book'], ['das', 'Buch']), + ... AlignedSent(['a', 'book'], ['ein', 'Buch'])] + >>> em_ibm1 = IBMModel1(corpus, 20) + >>> print(round(em_ibm1.translation_table['the']['das'], 1)) + 1.0 + >>> print(round(em_ibm1.translation_table['book']['das'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['house']['das'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['the']['Buch'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['book']['Buch'], 1)) + 1.0 + >>> print(round(em_ibm1.translation_table['a']['Buch'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['book']['ein'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['a']['ein'], 1)) + 1.0 + >>> print(round(em_ibm1.translation_table['the']['Haus'], 1)) + 0.0 + >>> print(round(em_ibm1.translation_table['house']['Haus'], 1)) + 1.0 + >>> print(round(em_ibm1.translation_table['book'][None], 1)) + 0.5 + +And using an NLTK corpus. We train on only 10 sentences, since it is so slow: + + >>> from nltk.corpus import comtrans + >>> com_ibm1 = IBMModel1(comtrans.aligned_sents()[:10], 20) + >>> print(round(com_ibm1.translation_table['bitte']['Please'], 1)) + 0.2 + >>> print(round(com_ibm1.translation_table['Sitzungsperiode']['session'], 1)) + 1.0 + + +Evaluation +---------- +The evaluation metrics for alignments are usually not interested in the +contents of alignments but more often the comparison to a "gold standard" +alignment that has been been constructed by human experts. For this reason we +often want to work just with raw set operations against the alignment points. +This then gives us a very clean form for defining our evaluation metrics. + +.. Note:: + The AlignedSent class has no distinction of "possible" or "sure" + alignments. Thus all alignments are treated as "sure". + +Consider the following aligned sentence for evaluation: + + >>> my_als = AlignedSent(['Resumption', 'of', 'the', 'session'], + ... ['Reprise', 'de', 'la', 'session'], + ... Alignment([(0, 0), (3, 3), (1, 2), (1, 1), (1, 3)])) + +Precision +~~~~~~~~~ +``precision = |A∩P| / |A|`` + +**Precision** is probably the most well known evaluation metric and it is implemented +in `nltk.metrics.scores.precision`_. Since precision is simply interested in the +proportion of correct alignments, we calculate the ratio of the number of our +test alignments (*A*) that match a possible alignment (*P*), over the number of +test alignments provided. There is no penalty for missing a possible alignment +in our test alignments. An easy way to game this metric is to provide just one +test alignment that is in *P* [OCH2000]_. + +Here are some examples: + + >>> from nltk.metrics import precision + >>> als.alignment = Alignment([(0,0), (1,1), (2,2), (3,3)]) + >>> precision(Alignment([]), als.alignment) + 0.0 + >>> precision(Alignment([(0,0), (1,1), (2,2), (3,3)]), als.alignment) + 1.0 + >>> precision(Alignment([(0,0), (3,3)]), als.alignment) + 0.5 + >>> precision(Alignment.fromstring('0-0 3-3'), als.alignment) + 0.5 + >>> precision(Alignment([(0,0), (1,1), (2,2), (3,3), (1,2), (2,1)]), als.alignment) + 1.0 + >>> precision(als.alignment, my_als.alignment) + 0.6 + + +.. _nltk.metrics.scores.precision: + https://www.nltk.org/api/nltk.metrics.html#nltk.metrics.scores.precision + + +Recall +~~~~~~ +``recall = |A∩S| / |S|`` + +**Recall** is another well known evaluation metric that has a set based +implementation in NLTK as `nltk.metrics.scores.recall`_. Since recall is +simply interested in the proportion of found alignments, we calculate the +ratio of the number of our test alignments (*A*) that match a sure alignment +(*S*) over the number of sure alignments. There is no penalty for producing +a lot of test alignments. An easy way to game this metric is to include every +possible alignment in our test alignments, regardless if they are correct or +not [OCH2000]_. + +Here are some examples: + + >>> from nltk.metrics import recall + >>> print(recall(Alignment([]), als.alignment)) + None + >>> recall(Alignment([(0,0), (1,1), (2,2), (3,3)]), als.alignment) + 1.0 + >>> recall(Alignment.fromstring('0-0 3-3'), als.alignment) + 1.0 + >>> recall(Alignment([(0,0), (3,3)]), als.alignment) + 1.0 + >>> recall(Alignment([(0,0), (1,1), (2,2), (3,3), (1,2), (2,1)]), als.alignment) + 0.66666... + >>> recall(als.alignment, my_als.alignment) + 0.75 + + +.. _nltk.metrics.scores.recall: + https://www.nltk.org/api/nltk.metrics.html#nltk.metrics.scores.recall + + +Alignment Error Rate (AER) +~~~~~~~~~~~~~~~~~~~~~~~~~~ +``AER = 1 - (|A∩S| + |A∩P|) / (|A| + |S|)`` + +**Alignment Error Rate** is commonly used metric for assessing sentence +alignments. It combines precision and recall metrics together such that a +perfect alignment must have all of the sure alignments and may have some +possible alignments [MIHALCEA2003]_ [KOEHN2010]_. + +.. Note:: + [KOEHN2010]_ defines the AER as ``AER = (|A∩S| + |A∩P|) / (|A| + |S|)`` + in his book, but corrects it to the above in his online errata. This is + in line with [MIHALCEA2003]_. + +Here are some examples: + + >>> from nltk.translate import alignment_error_rate + >>> alignment_error_rate(Alignment([]), als.alignment) + 1.0 + >>> alignment_error_rate(Alignment([(0,0), (1,1), (2,2), (3,3)]), als.alignment) + 0.0 + >>> alignment_error_rate(als.alignment, my_als.alignment) + 0.333333... + >>> alignment_error_rate(als.alignment, my_als.alignment, + ... als.alignment | Alignment([(1,2), (2,1)])) + 0.222222... + + +.. [OCH2000] Och, F. and Ney, H. (2000) + *Statistical Machine Translation*, EAMT Workshop + +.. [MIHALCEA2003] Mihalcea, R. and Pedersen, T. (2003) + *An evaluation exercise for word alignment*, HLT-NAACL 2003 + +.. [KOEHN2010] Koehn, P. (2010) + *Statistical Machine Translation*, Cambridge University Press diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/tree.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tree.doctest new file mode 100644 index 0000000..13d25f5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/tree.doctest @@ -0,0 +1,1223 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=============================== + Unit tests for nltk.tree.Tree +=============================== + + >>> from nltk.tree import * + +Some trees to run tests on: + + >>> dp1 = Tree('dp', [Tree('d', ['the']), Tree('np', ['dog'])]) + >>> dp2 = Tree('dp', [Tree('d', ['the']), Tree('np', ['cat'])]) + >>> vp = Tree('vp', [Tree('v', ['chased']), dp2]) + >>> tree = Tree('s', [dp1, vp]) + >>> print(tree) + (s (dp (d the) (np dog)) (vp (v chased) (dp (d the) (np cat)))) + +The node label is accessed using the `label()` method: + + >>> dp1.label(), dp2.label(), vp.label(), tree.label() + ('dp', 'dp', 'vp', 's') + + >>> print(tree[1,1,1,0]) + cat + +The `treepositions` method returns a list of the tree positions of +subtrees and leaves in a tree. By default, it gives the position of +every tree, subtree, and leaf, in prefix order: + + >>> print(tree.treepositions()) + [(), (0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1,), (1, 0), (1, 0, 0), (1, 1), (1, 1, 0), (1, 1, 0, 0), (1, 1, 1), (1, 1, 1, 0)] + +In addition to `str` and `repr`, several methods exist to convert a +tree object to one of several standard tree encodings: + + >>> print(tree.pformat_latex_qtree()) + \Tree [.s + [.dp [.d the ] [.np dog ] ] + [.vp [.v chased ] [.dp [.d the ] [.np cat ] ] ] ] + +There is also a fancy ASCII art representation: + + >>> tree.pretty_print() + s + ________|_____ + | vp + | _____|___ + dp | dp + ___|___ | ___|___ + d np v d np + | | | | | + the dog chased the cat + + >>> tree.pretty_print(unicodelines=True, nodedist=4) + s + ┌──────────────┴────────┐ + │ vp + │ ┌────────┴──────┐ + dp │ dp + ┌──────┴──────┐ │ ┌──────┴──────┐ + d np v d np + │ │ │ │ │ + the dog chased the cat + +Trees can be initialized from treebank strings: + + >>> tree2 = Tree.fromstring('(S (NP I) (VP (V enjoyed) (NP my cookie)))') + >>> print(tree2) + (S (NP I) (VP (V enjoyed) (NP my cookie))) + +Trees can be compared for equality: + + >>> tree == Tree.fromstring(str(tree)) + True + >>> tree2 == Tree.fromstring(str(tree2)) + True + >>> tree == tree2 + False + >>> tree == Tree.fromstring(str(tree2)) + False + >>> tree2 == Tree.fromstring(str(tree)) + False + + >>> tree != Tree.fromstring(str(tree)) + False + >>> tree2 != Tree.fromstring(str(tree2)) + False + >>> tree != tree2 + True + >>> tree != Tree.fromstring(str(tree2)) + True + >>> tree2 != Tree.fromstring(str(tree)) + True + + >>> tree < tree2 or tree > tree2 + True + +Tree Parsing +============ + +The class method `Tree.fromstring()` can be used to parse trees, and it +provides some additional options. + + >>> tree = Tree.fromstring('(S (NP I) (VP (V enjoyed) (NP my cookie)))') + >>> print(tree) + (S (NP I) (VP (V enjoyed) (NP my cookie))) + +When called on a subclass of `Tree`, it will create trees of that +type: + + >>> tree = ImmutableTree.fromstring('(VP (V enjoyed) (NP my cookie))') + >>> print(tree) + (VP (V enjoyed) (NP my cookie)) + >>> print(type(tree)) + + >>> tree[1] = 'x' + Traceback (most recent call last): + . . . + ValueError: ImmutableTree may not be modified + >>> del tree[0] + Traceback (most recent call last): + . . . + ValueError: ImmutableTree may not be modified + +The ``brackets`` parameter can be used to specify two characters that +should be used as brackets: + + >>> print(Tree.fromstring('[S [NP I] [VP [V enjoyed] [NP my cookie]]]', + ... brackets='[]')) + (S (NP I) (VP (V enjoyed) (NP my cookie))) + >>> print(Tree.fromstring(' >>', + ... brackets='<>')) + (S (NP I) (VP (V enjoyed) (NP my cookie))) + +If ``brackets`` is not a string, or is not exactly two characters, +then `Tree.fromstring` raises an exception: + + >>> Tree.fromstring(' >', brackets='') + Traceback (most recent call last): + . . . + TypeError: brackets must be a length-2 string + >>> Tree.fromstring(' >', brackets='<<>>') + Traceback (most recent call last): + . . . + TypeError: brackets must be a length-2 string + >>> Tree.fromstring(' >', brackets=12) + Traceback (most recent call last): + . . . + TypeError: brackets must be a length-2 string + >>> Tree.fromstring('<>', brackets=('<<','>>')) + Traceback (most recent call last): + . . . + TypeError: brackets must be a length-2 string + +(We may add support for multi-character brackets in the future, in +which case the ``brackets=('<<','>>')`` example would start working.) + +Whitespace brackets are not permitted: + + >>> Tree.fromstring('(NP my cookie\n', brackets='(\n') + Traceback (most recent call last): + . . . + TypeError: whitespace brackets not allowed + +If an invalid tree is given to Tree.fromstring, then it raises a +ValueError, with a description of the problem: + + >>> Tree.fromstring('(NP my cookie) (NP my milk)') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected 'end-of-string' but got '(NP' + at index 15. + "...y cookie) (NP my mil..." + ^ + >>> Tree.fromstring(')NP my cookie(') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected '(' but got ')' + at index 0. + ")NP my coo..." + ^ + >>> Tree.fromstring('(NP my cookie))') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected 'end-of-string' but got ')' + at index 14. + "...my cookie))" + ^ + >>> Tree.fromstring('my cookie)') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected '(' but got 'my' + at index 0. + "my cookie)" + ^ + >>> Tree.fromstring('(NP my cookie') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected ')' but got 'end-of-string' + at index 13. + "... my cookie" + ^ + >>> Tree.fromstring('') + Traceback (most recent call last): + . . . + ValueError: Tree.fromstring(): expected '(' but got 'end-of-string' + at index 0. + "" + ^ + +Trees with no children are supported: + + >>> print(Tree.fromstring('(S)')) + (S ) + >>> print(Tree.fromstring('(X (Y) (Z))')) + (X (Y ) (Z )) + +Trees with an empty node label and no children are supported: + + >>> print(Tree.fromstring('()')) + ( ) + >>> print(Tree.fromstring('(X () ())')) + (X ( ) ( )) + +Trees with an empty node label and children are supported, but only if the +first child is not a leaf (otherwise, it will be treated as the node label). + + >>> print(Tree.fromstring('((A) (B) (C))')) + ( (A ) (B ) (C )) + >>> print(Tree.fromstring('((A) leaf)')) + ( (A ) leaf) + >>> print(Tree.fromstring('(((())))')) + ( ( ( ( )))) + +The optional arguments `read_node` and `read_leaf` may be used to +transform the string values of nodes or leaves. + + >>> print(Tree.fromstring('(A b (C d e) (F (G h i)))', + ... read_node=lambda s: '<%s>' % s, + ... read_leaf=lambda s: '"%s"' % s)) + (
    "b" ( "d" "e") ( ( "h" "i"))) + +These transformation functions are typically used when the node or +leaf labels should be parsed to a non-string value (such as a feature +structure). If node and leaf labels need to be able to include +whitespace, then you must also use the optional `node_pattern` and +`leaf_pattern` arguments. + + >>> from nltk.featstruct import FeatStruct + >>> tree = Tree.fromstring('([cat=NP] [lex=the] [lex=dog])', + ... read_node=FeatStruct, read_leaf=FeatStruct) + >>> tree.set_label(tree.label().unify(FeatStruct('[num=singular]'))) + >>> print(tree) + ([cat='NP', num='singular'] [lex='the'] [lex='dog']) + +The optional argument ``remove_empty_top_bracketing`` can be used to +remove any top-level empty bracketing that occurs. + + >>> print(Tree.fromstring('((S (NP I) (VP (V enjoyed) (NP my cookie))))', + ... remove_empty_top_bracketing=True)) + (S (NP I) (VP (V enjoyed) (NP my cookie))) + +It will not remove a top-level empty bracketing with multiple children: + + >>> print(Tree.fromstring('((A a) (B b))')) + ( (A a) (B b)) + + +Tree.fromlist() +--------------- +The class method `Tree.fromlist()` can be used to parse trees +that are expressed as nested lists, such as those produced by +the tree() function from the wordnet module. + + >>> from nltk.corpus import wordnet as wn + >>> t=Tree.fromlist(wn.synset('dog.n.01').tree(lambda s:s.hypernyms())) + >>> print(t.height()) + 14 + >>> print(t.leaves()) + ["Synset('entity.n.01')", "Synset('entity.n.01')"] + >>> t.pretty_print() + Synset('dog.n.01') + _________________|__________________ + Synset('canine.n. | + 02') | + | | + Synset('carnivor | + e.n.01') | + | | + Synset('placenta | + l.n.01') | + | | + Synset('mammal.n. | + 01') | + | | + Synset('vertebra | + te.n.01') | + | | + Synset('chordate. Synset('domestic + n.01') _animal.n.01') + | | + Synset('animal.n. Synset('animal.n. + 01') 01') + | | + Synset('organism. Synset('organism. + n.01') n.01') + | | + Synset('living_t Synset('living_t + hing.n.01') hing.n.01') + | | + Synset('whole.n. Synset('whole.n. + 02') 02') + | | + Synset('object.n. Synset('object.n. + 01') 01') + | | + Synset('physical Synset('physical + _entity.n.01') _entity.n.01') + | | + Synset('entity.n. Synset('entity.n. + 01') 01') + + + +Parented Trees +============== +`ParentedTree` is a subclass of `Tree` that automatically maintains +parent pointers for single-parented trees. Parented trees can be +created directly from a node label and a list of children: + + >>> ptree = ( + ... ParentedTree('VP', [ + ... ParentedTree('VERB', ['saw']), + ... ParentedTree('NP', [ + ... ParentedTree('DET', ['the']), + ... ParentedTree('NOUN', ['dog'])])])) + >>> print(ptree) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + +Parented trees can be created from strings using the classmethod +`ParentedTree.fromstring`: + + >>> ptree = ParentedTree.fromstring('(VP (VERB saw) (NP (DET the) (NOUN dog)))') + >>> print(ptree) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + >>> print(type(ptree)) + + +Parented trees can also be created by using the classmethod +`ParentedTree.convert` to convert another type of tree to a parented +tree: + + >>> tree = Tree.fromstring('(VP (VERB saw) (NP (DET the) (NOUN dog)))') + >>> ptree = ParentedTree.convert(tree) + >>> print(ptree) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + >>> print(type(ptree)) + + +.. clean-up: + + >>> del tree + +`ParentedTree`\ s should never be used in the same tree as `Tree`\ s +or `MultiParentedTree`\ s. Mixing tree implementations may result in +incorrect parent pointers and in `TypeError` exceptions: + + >>> # Inserting a Tree in a ParentedTree gives an exception: + >>> ParentedTree('NP', [ + ... Tree('DET', ['the']), Tree('NOUN', ['dog'])]) + Traceback (most recent call last): + . . . + TypeError: Can not insert a non-ParentedTree into a ParentedTree + + >>> # inserting a ParentedTree in a Tree gives incorrect parent pointers: + >>> broken_tree = Tree('NP', [ + ... ParentedTree('DET', ['the']), ParentedTree('NOUN', ['dog'])]) + >>> print(broken_tree[0].parent()) + None + +Parented Tree Methods +------------------------ +In addition to all the methods defined by the `Tree` class, the +`ParentedTree` class adds six new methods whose values are +automatically updated whenever a parented tree is modified: `parent()`, +`parent_index()`, `left_sibling()`, `right_sibling()`, `root()`, and +`treeposition()`. + +The `parent()` method contains a `ParentedTree`\ 's parent, if it has +one; and ``None`` otherwise. `ParentedTree`\ s that do not have +parents are known as "root trees." + + >>> for subtree in ptree.subtrees(): + ... print(subtree) + ... print(' Parent = %s' % subtree.parent()) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + Parent = None + (VERB saw) + Parent = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (NP (DET the) (NOUN dog)) + Parent = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (DET the) + Parent = (NP (DET the) (NOUN dog)) + (NOUN dog) + Parent = (NP (DET the) (NOUN dog)) + +The `parent_index()` method stores the index of a tree in its parent's +child list. If a tree does not have a parent, then its `parent_index` +is ``None``. + + >>> for subtree in ptree.subtrees(): + ... print(subtree) + ... print(' Parent Index = %s' % subtree.parent_index()) + ... assert (subtree.parent() is None or + ... subtree.parent()[subtree.parent_index()] is subtree) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + Parent Index = None + (VERB saw) + Parent Index = 0 + (NP (DET the) (NOUN dog)) + Parent Index = 1 + (DET the) + Parent Index = 0 + (NOUN dog) + Parent Index = 1 + +Note that ``ptree.parent().index(ptree)`` is *not* equivalent to +``ptree.parent_index()``. In particular, ``ptree.parent().index(ptree)`` +will return the index of the first child of ``ptree.parent()`` that is +equal to ``ptree`` (using ``==``); and that child may not be +``ptree``: + + >>> on_and_on = ParentedTree('CONJP', [ + ... ParentedTree('PREP', ['on']), + ... ParentedTree('COJN', ['and']), + ... ParentedTree('PREP', ['on'])]) + >>> second_on = on_and_on[2] + >>> print(second_on.parent_index()) + 2 + >>> print(second_on.parent().index(second_on)) + 0 + +The methods `left_sibling()` and `right_sibling()` can be used to get a +parented tree's siblings. If a tree does not have a left or right +sibling, then the corresponding method's value is ``None``: + + >>> for subtree in ptree.subtrees(): + ... print(subtree) + ... print(' Left Sibling = %s' % subtree.left_sibling()) + ... print(' Right Sibling = %s' % subtree.right_sibling()) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + Left Sibling = None + Right Sibling = None + (VERB saw) + Left Sibling = None + Right Sibling = (NP (DET the) (NOUN dog)) + (NP (DET the) (NOUN dog)) + Left Sibling = (VERB saw) + Right Sibling = None + (DET the) + Left Sibling = None + Right Sibling = (NOUN dog) + (NOUN dog) + Left Sibling = (DET the) + Right Sibling = None + +A parented tree's root tree can be accessed using the `root()` +method. This method follows the tree's parent pointers until it +finds a tree without a parent. If a tree does not have a parent, then +it is its own root: + + >>> for subtree in ptree.subtrees(): + ... print(subtree) + ... print(' Root = %s' % subtree.root()) + (VP (VERB saw) (NP (DET the) (NOUN dog))) + Root = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (VERB saw) + Root = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (NP (DET the) (NOUN dog)) + Root = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (DET the) + Root = (VP (VERB saw) (NP (DET the) (NOUN dog))) + (NOUN dog) + Root = (VP (VERB saw) (NP (DET the) (NOUN dog))) + +The `treeposition()` method can be used to find a tree's treeposition +relative to its root: + + >>> for subtree in ptree.subtrees(): + ... print(subtree) + ... print(' Tree Position = %s' % (subtree.treeposition(),)) + ... assert subtree.root()[subtree.treeposition()] is subtree + (VP (VERB saw) (NP (DET the) (NOUN dog))) + Tree Position = () + (VERB saw) + Tree Position = (0,) + (NP (DET the) (NOUN dog)) + Tree Position = (1,) + (DET the) + Tree Position = (1, 0) + (NOUN dog) + Tree Position = (1, 1) + +Whenever a parented tree is modified, all of the methods described +above (`parent()`, `parent_index()`, `left_sibling()`, `right_sibling()`, +`root()`, and `treeposition()`) are automatically updated. For example, +if we replace ``ptree``\ 's subtree for the word "dog" with a new +subtree for "cat," the method values for both the "dog" subtree and the +"cat" subtree get automatically updated: + + >>> # Replace the dog with a cat + >>> dog = ptree[1,1] + >>> cat = ParentedTree('NOUN', ['cat']) + >>> ptree[1,1] = cat + + >>> # the noun phrase is no longer the dog's parent: + >>> print(dog.parent(), dog.parent_index(), dog.left_sibling()) + None None None + >>> # dog is now its own root. + >>> print(dog.root()) + (NOUN dog) + >>> print(dog.treeposition()) + () + + >>> # the cat's parent is now the noun phrase: + >>> print(cat.parent()) + (NP (DET the) (NOUN cat)) + >>> print(cat.parent_index()) + 1 + >>> print(cat.left_sibling()) + (DET the) + >>> print(cat.root()) + (VP (VERB saw) (NP (DET the) (NOUN cat))) + >>> print(cat.treeposition()) + (1, 1) + +ParentedTree Regression Tests +----------------------------- +Keep track of all trees that we create (including subtrees) using this +variable: + + >>> all_ptrees = [] + +Define a helper function to create new parented trees: + + >>> def make_ptree(s): + ... ptree = ParentedTree.convert(Tree.fromstring(s)) + ... all_ptrees.extend(t for t in ptree.subtrees() + ... if isinstance(t, Tree)) + ... return ptree + +Define a test function that examines every subtree in all_ptrees; and +checks that all six of its methods are defined correctly. If any +ptrees are passed as arguments, then they are printed. + + >>> def pcheck(*print_ptrees): + ... for ptree in all_ptrees: + ... # Check ptree's methods. + ... if ptree.parent() is not None: + ... i = ptree.parent_index() + ... assert ptree.parent()[i] is ptree + ... if i > 0: + ... assert ptree.left_sibling() is ptree.parent()[i-1] + ... if i < (len(ptree.parent())-1): + ... assert ptree.right_sibling() is ptree.parent()[i+1] + ... assert len(ptree.treeposition()) > 0 + ... assert (ptree.treeposition() == + ... ptree.parent().treeposition() + (ptree.parent_index(),)) + ... assert ptree.root() is not ptree + ... assert ptree.root() is not None + ... assert ptree.root() is ptree.parent().root() + ... assert ptree.root()[ptree.treeposition()] is ptree + ... else: + ... assert ptree.parent_index() is None + ... assert ptree.left_sibling() is None + ... assert ptree.right_sibling() is None + ... assert ptree.root() is ptree + ... assert ptree.treeposition() == () + ... # Check ptree's children's methods: + ... for i, child in enumerate(ptree): + ... if isinstance(child, Tree): + ... # pcheck parent() & parent_index() methods + ... assert child.parent() is ptree + ... assert child.parent_index() == i + ... # pcheck sibling methods + ... if i == 0: + ... assert child.left_sibling() is None + ... else: + ... assert child.left_sibling() is ptree[i-1] + ... if i == len(ptree)-1: + ... assert child.right_sibling() is None + ... else: + ... assert child.right_sibling() is ptree[i+1] + ... if print_ptrees: + ... print('ok!', end=' ') + ... for ptree in print_ptrees: print(ptree) + ... else: + ... print('ok!') + +Run our test function on a variety of newly-created trees: + + >>> pcheck(make_ptree('(A)')) + ok! (A ) + >>> pcheck(make_ptree('(A (B (C (D) (E f)) g) h)')) + ok! (A (B (C (D ) (E f)) g) h) + >>> pcheck(make_ptree('(A (B) (C c) (D d d) (E e e e))')) + ok! (A (B ) (C c) (D d d) (E e e e)) + >>> pcheck(make_ptree('(A (B) (C (c)) (D (d) (d)) (E (e) (e) (e)))')) + ok! (A (B ) (C (c )) (D (d ) (d )) (E (e ) (e ) (e ))) + +Run our test function after performing various tree-modification +operations: + +**__delitem__()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> e = ptree[0,0,1] + >>> del ptree[0,0,1]; pcheck(ptree); pcheck(e) + ok! (A (B (C (D ) (Q p)) g) h) + ok! (E f) + >>> del ptree[0,0,0]; pcheck(ptree) + ok! (A (B (C (Q p)) g) h) + >>> del ptree[0,1]; pcheck(ptree) + ok! (A (B (C (Q p))) h) + >>> del ptree[-1]; pcheck(ptree) + ok! (A (B (C (Q p)))) + >>> del ptree[-100] + Traceback (most recent call last): + . . . + IndexError: index out of range + >>> del ptree[()] + Traceback (most recent call last): + . . . + IndexError: The tree position () may not be deleted. + + >>> # With slices: + >>> ptree = make_ptree('(A (B c) (D e) f g (H i) j (K l))') + >>> b = ptree[0] + >>> del ptree[0:0]; pcheck(ptree) + ok! (A (B c) (D e) f g (H i) j (K l)) + >>> del ptree[:1]; pcheck(ptree); pcheck(b) + ok! (A (D e) f g (H i) j (K l)) + ok! (B c) + >>> del ptree[-2:]; pcheck(ptree) + ok! (A (D e) f g (H i)) + >>> del ptree[1:3]; pcheck(ptree) + ok! (A (D e) (H i)) + >>> ptree = make_ptree('(A (B c) (D e) f g (H i) j (K l))') + >>> del ptree[5:1000]; pcheck(ptree) + ok! (A (B c) (D e) f g (H i)) + >>> del ptree[-2:1000]; pcheck(ptree) + ok! (A (B c) (D e) f) + >>> del ptree[-100:1]; pcheck(ptree) + ok! (A (D e) f) + >>> ptree = make_ptree('(A (B c) (D e) f g (H i) j (K l))') + >>> del ptree[1:-2:2]; pcheck(ptree) + ok! (A (B c) f (H i) j (K l)) + +**__setitem__()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> d, e, q = ptree[0,0] + >>> ptree[0,0,0] = 'x'; pcheck(ptree); pcheck(d) + ok! (A (B (C x (E f) (Q p)) g) h) + ok! (D ) + >>> ptree[0,0,1] = make_ptree('(X (Y z))'); pcheck(ptree); pcheck(e) + ok! (A (B (C x (X (Y z)) (Q p)) g) h) + ok! (E f) + >>> ptree[1] = d; pcheck(ptree) + ok! (A (B (C x (X (Y z)) (Q p)) g) (D )) + >>> ptree[-1] = 'x'; pcheck(ptree) + ok! (A (B (C x (X (Y z)) (Q p)) g) x) + >>> ptree[-100] = 'y' + Traceback (most recent call last): + . . . + IndexError: index out of range + >>> ptree[()] = make_ptree('(X y)') + Traceback (most recent call last): + . . . + IndexError: The tree position () may not be assigned to. + + >>> # With slices: + >>> ptree = make_ptree('(A (B c) (D e) f g (H i) j (K l))') + >>> b = ptree[0] + >>> ptree[0:0] = ('x', make_ptree('(Y)')); pcheck(ptree) + ok! (A x (Y ) (B c) (D e) f g (H i) j (K l)) + >>> ptree[2:6] = (); pcheck(ptree); pcheck(b) + ok! (A x (Y ) (H i) j (K l)) + ok! (B c) + >>> ptree[-2:] = ('z', 'p'); pcheck(ptree) + ok! (A x (Y ) (H i) z p) + >>> ptree[1:3] = [make_ptree('(X)') for x in range(10)]; pcheck(ptree) + ok! (A x (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) z p) + >>> ptree[5:1000] = []; pcheck(ptree) + ok! (A x (X ) (X ) (X ) (X )) + >>> ptree[-2:1000] = ['n']; pcheck(ptree) + ok! (A x (X ) (X ) n) + >>> ptree[-100:1] = [make_ptree('(U v)')]; pcheck(ptree) + ok! (A (U v) (X ) (X ) n) + >>> ptree[-1:] = (make_ptree('(X)') for x in range(3)); pcheck(ptree) + ok! (A (U v) (X ) (X ) (X ) (X ) (X )) + >>> ptree[1:-2:2] = ['x', 'y']; pcheck(ptree) + ok! (A (U v) x (X ) y (X ) (X )) + +**append()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> ptree.append('x'); pcheck(ptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x) + >>> ptree.append(make_ptree('(X (Y z))')); pcheck(ptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x (X (Y z))) + +**extend()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> ptree.extend(['x', 'y', make_ptree('(X (Y z))')]); pcheck(ptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z))) + >>> ptree.extend([]); pcheck(ptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z))) + >>> ptree.extend(make_ptree('(X)') for x in range(3)); pcheck(ptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z)) (X ) (X ) (X )) + +**insert()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> ptree.insert(0, make_ptree('(X (Y z))')); pcheck(ptree) + ok! (A (X (Y z)) (B (C (D ) (E f) (Q p)) g) h) + >>> ptree.insert(-1, make_ptree('(X (Y z))')); pcheck(ptree) + ok! (A (X (Y z)) (B (C (D ) (E f) (Q p)) g) (X (Y z)) h) + >>> ptree.insert(-4, make_ptree('(X (Y z))')); pcheck(ptree) + ok! (A (X (Y z)) (X (Y z)) (B (C (D ) (E f) (Q p)) g) (X (Y z)) h) + >>> # Note: as with ``list``, inserting at a negative index that + >>> # gives a position before the start of the list does *not* + >>> # raise an IndexError exception; it just inserts at 0. + >>> ptree.insert(-400, make_ptree('(X (Y z))')); pcheck(ptree) + ok! (A + (X (Y z)) + (X (Y z)) + (X (Y z)) + (B (C (D ) (E f) (Q p)) g) + (X (Y z)) + h) + +**pop()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> ptree[0,0].pop(1); pcheck(ptree) + ParentedTree('E', ['f']) + ok! (A (B (C (D ) (Q p)) g) h) + >>> ptree[0].pop(-1); pcheck(ptree) + 'g' + ok! (A (B (C (D ) (Q p))) h) + >>> ptree.pop(); pcheck(ptree) + 'h' + ok! (A (B (C (D ) (Q p)))) + >>> ptree.pop(-100) + Traceback (most recent call last): + . . . + IndexError: index out of range + +**remove()** + + >>> ptree = make_ptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> e = ptree[0,0,1] + >>> ptree[0,0].remove(ptree[0,0,1]); pcheck(ptree); pcheck(e) + ok! (A (B (C (D ) (Q p)) g) h) + ok! (E f) + >>> ptree[0,0].remove(make_ptree('(Q p)')); pcheck(ptree) + ok! (A (B (C (D )) g) h) + >>> ptree[0,0].remove(make_ptree('(Q p)')) + Traceback (most recent call last): + . . . + ValueError: ParentedTree('Q', ['p']) is not in list + >>> ptree.remove('h'); pcheck(ptree) + ok! (A (B (C (D )) g)) + >>> ptree.remove('h'); + Traceback (most recent call last): + . . . + ValueError: 'h' is not in list + >>> # remove() removes the first subtree that is equal (==) to the + >>> # given tree, which may not be the identical tree we give it: + >>> ptree = make_ptree('(A (X x) (Y y) (X x))') + >>> x1, y, x2 = ptree + >>> ptree.remove(ptree[-1]); pcheck(ptree) + ok! (A (Y y) (X x)) + >>> print(x1.parent()); pcheck(x1) + None + ok! (X x) + >>> print(x2.parent()) + (A (Y y) (X x)) + +Test that a tree can not be given multiple parents: + + >>> ptree = make_ptree('(A (X x) (Y y) (Z z))') + >>> ptree[0] = ptree[1] + Traceback (most recent call last): + . . . + ValueError: Can not insert a subtree that already has a parent. + >>> pcheck() + ok! + +[more to be written] + +Shallow copying can be tricky for Tree and several of its subclasses. +For shallow copies of Tree, only the root node is reconstructed, while +all the children are shared between the two trees. Modify the children +of one tree - and the shallowly copied tree will also update. + + >>> from nltk.tree import Tree, ParentedTree, MultiParentedTree + >>> tree = Tree.fromstring("(TOP (S (NP (NNP Bell,)) (NP (NP (DT a) (NN company)) (SBAR (WHNP (WDT which)) (S (VP (VBZ is) (VP (VBN based) (PP (IN in) (NP (NNP LA,)))))))) (VP (VBZ makes) (CC and) (VBZ distributes) (NP (NN computer))) (. products.)))") + >>> copy_tree = tree.copy(deep=False) + >>> tree == copy_tree # Ensure identical labels and nodes + True + >>> id(copy_tree[0]) == id(tree[0]) # Ensure shallow copy - the children are the same objects in memory + True + +For ParentedTree objects, this behaviour is not possible. With a shallow +copy, the children of the root node would be reused for both the original +and the shallow copy. For this to be possible, some children would need +to have multiple parents. As this is forbidden for ParentedTree objects, +attempting to make a shallow copy will cause a warning, and a deep copy +is made instead. + + >>> ptree = ParentedTree.fromstring("(TOP (S (NP (NNP Bell,)) (NP (NP (DT a) (NN company)) (SBAR (WHNP (WDT which)) (S (VP (VBZ is) (VP (VBN based) (PP (IN in) (NP (NNP LA,)))))))) (VP (VBZ makes) (CC and) (VBZ distributes) (NP (NN computer))) (. products.)))") + >>> copy_ptree = ptree.copy(deep=False) + >>> copy_ptree == ptree # Ensure identical labels and nodes + True + >>> id(copy_ptree[0]) != id(ptree[0]) # Shallow copying isn't supported - it defaults to deep copy. + True + +For MultiParentedTree objects, the issue of only allowing one parent that +can be seen for ParentedTree objects is no more. Shallow copying a +MultiParentedTree gives the children of the root node two parents: +the original and the newly copied root. + + >>> mptree = MultiParentedTree.fromstring("(TOP (S (NP (NNP Bell,)) (NP (NP (DT a) (NN company)) (SBAR (WHNP (WDT which)) (S (VP (VBZ is) (VP (VBN based) (PP (IN in) (NP (NNP LA,)))))))) (VP (VBZ makes) (CC and) (VBZ distributes) (NP (NN computer))) (. products.)))") + >>> len(mptree[0].parents()) + 1 + >>> copy_mptree = mptree.copy(deep=False) + >>> copy_mptree == mptree # Ensure identical labels and nodes + True + >>> len(mptree[0].parents()) + 2 + >>> len(copy_mptree[0].parents()) + 2 + +Shallow copying a MultiParentedTree is similar to creating a second root +which is identically labeled as the root on which the copy method was called. + + +ImmutableParentedTree Regression Tests +-------------------------------------- + + >>> iptree = ImmutableParentedTree.convert(ptree) + >>> type(iptree) + + >>> del iptree[0] + Traceback (most recent call last): + . . . + ValueError: ImmutableParentedTree may not be modified + >>> iptree.set_label('newnode') + Traceback (most recent call last): + . . . + ValueError: ImmutableParentedTree may not be modified + + +MultiParentedTree Regression Tests +---------------------------------- +Keep track of all trees that we create (including subtrees) using this +variable: + + >>> all_mptrees = [] + +Define a helper function to create new parented trees: + + >>> def make_mptree(s): + ... mptree = MultiParentedTree.convert(Tree.fromstring(s)) + ... all_mptrees.extend(t for t in mptree.subtrees() + ... if isinstance(t, Tree)) + ... return mptree + +Define a test function that examines every subtree in all_mptrees; and +checks that all six of its methods are defined correctly. If any +mptrees are passed as arguments, then they are printed. + + >>> def mpcheck(*print_mptrees): + ... def has(seq, val): # uses identity comparison + ... for item in seq: + ... if item is val: return True + ... return False + ... for mptree in all_mptrees: + ... # Check mptree's methods. + ... if len(mptree.parents()) == 0: + ... assert len(mptree.left_siblings()) == 0 + ... assert len(mptree.right_siblings()) == 0 + ... assert len(mptree.roots()) == 1 + ... assert mptree.roots()[0] is mptree + ... assert mptree.treepositions(mptree) == [()] + ... left_siblings = right_siblings = () + ... roots = {id(mptree): 1} + ... else: + ... roots = dict((id(r), 0) for r in mptree.roots()) + ... left_siblings = mptree.left_siblings() + ... right_siblings = mptree.right_siblings() + ... for parent in mptree.parents(): + ... for i in mptree.parent_indices(parent): + ... assert parent[i] is mptree + ... # check left siblings + ... if i > 0: + ... for j in range(len(left_siblings)): + ... if left_siblings[j] is parent[i-1]: + ... del left_siblings[j] + ... break + ... else: + ... assert 0, 'sibling not found!' + ... # check ight siblings + ... if i < (len(parent)-1): + ... for j in range(len(right_siblings)): + ... if right_siblings[j] is parent[i+1]: + ... del right_siblings[j] + ... break + ... else: + ... assert 0, 'sibling not found!' + ... # check roots + ... for root in parent.roots(): + ... assert id(root) in roots, 'missing root' + ... roots[id(root)] += 1 + ... # check that we don't have any unexplained values + ... assert len(left_siblings)==0, 'unexpected sibling' + ... assert len(right_siblings)==0, 'unexpected sibling' + ... for v in roots.values(): assert v>0, roots #'unexpected root' + ... # check treepositions + ... for root in mptree.roots(): + ... for treepos in mptree.treepositions(root): + ... assert root[treepos] is mptree + ... # Check mptree's children's methods: + ... for i, child in enumerate(mptree): + ... if isinstance(child, Tree): + ... # mpcheck parent() & parent_index() methods + ... assert has(child.parents(), mptree) + ... assert i in child.parent_indices(mptree) + ... # mpcheck sibling methods + ... if i > 0: + ... assert has(child.left_siblings(), mptree[i-1]) + ... if i < len(mptree)-1: + ... assert has(child.right_siblings(), mptree[i+1]) + ... if print_mptrees: + ... print('ok!', end=' ') + ... for mptree in print_mptrees: print(mptree) + ... else: + ... print('ok!') + +Run our test function on a variety of newly-created trees: + + >>> mpcheck(make_mptree('(A)')) + ok! (A ) + >>> mpcheck(make_mptree('(A (B (C (D) (E f)) g) h)')) + ok! (A (B (C (D ) (E f)) g) h) + >>> mpcheck(make_mptree('(A (B) (C c) (D d d) (E e e e))')) + ok! (A (B ) (C c) (D d d) (E e e e)) + >>> mpcheck(make_mptree('(A (B) (C (c)) (D (d) (d)) (E (e) (e) (e)))')) + ok! (A (B ) (C (c )) (D (d ) (d )) (E (e ) (e ) (e ))) + >>> subtree = make_mptree('(A (B (C (D) (E f)) g) h)') + +Including some trees that contain multiple parents: + + >>> mpcheck(MultiParentedTree('Z', [subtree, subtree])) + ok! (Z (A (B (C (D ) (E f)) g) h) (A (B (C (D ) (E f)) g) h)) + +Run our test function after performing various tree-modification +operations (n.b., these are the same tests that we ran for +`ParentedTree`, above; thus, none of these trees actually *uses* +multiple parents.) + +**__delitem__()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> e = mptree[0,0,1] + >>> del mptree[0,0,1]; mpcheck(mptree); mpcheck(e) + ok! (A (B (C (D ) (Q p)) g) h) + ok! (E f) + >>> del mptree[0,0,0]; mpcheck(mptree) + ok! (A (B (C (Q p)) g) h) + >>> del mptree[0,1]; mpcheck(mptree) + ok! (A (B (C (Q p))) h) + >>> del mptree[-1]; mpcheck(mptree) + ok! (A (B (C (Q p)))) + >>> del mptree[-100] + Traceback (most recent call last): + . . . + IndexError: index out of range + >>> del mptree[()] + Traceback (most recent call last): + . . . + IndexError: The tree position () may not be deleted. + + >>> # With slices: + >>> mptree = make_mptree('(A (B c) (D e) f g (H i) j (K l))') + >>> b = mptree[0] + >>> del mptree[0:0]; mpcheck(mptree) + ok! (A (B c) (D e) f g (H i) j (K l)) + >>> del mptree[:1]; mpcheck(mptree); mpcheck(b) + ok! (A (D e) f g (H i) j (K l)) + ok! (B c) + >>> del mptree[-2:]; mpcheck(mptree) + ok! (A (D e) f g (H i)) + >>> del mptree[1:3]; mpcheck(mptree) + ok! (A (D e) (H i)) + >>> mptree = make_mptree('(A (B c) (D e) f g (H i) j (K l))') + >>> del mptree[5:1000]; mpcheck(mptree) + ok! (A (B c) (D e) f g (H i)) + >>> del mptree[-2:1000]; mpcheck(mptree) + ok! (A (B c) (D e) f) + >>> del mptree[-100:1]; mpcheck(mptree) + ok! (A (D e) f) + >>> mptree = make_mptree('(A (B c) (D e) f g (H i) j (K l))') + >>> del mptree[1:-2:2]; mpcheck(mptree) + ok! (A (B c) f (H i) j (K l)) + +**__setitem__()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> d, e, q = mptree[0,0] + >>> mptree[0,0,0] = 'x'; mpcheck(mptree); mpcheck(d) + ok! (A (B (C x (E f) (Q p)) g) h) + ok! (D ) + >>> mptree[0,0,1] = make_mptree('(X (Y z))'); mpcheck(mptree); mpcheck(e) + ok! (A (B (C x (X (Y z)) (Q p)) g) h) + ok! (E f) + >>> mptree[1] = d; mpcheck(mptree) + ok! (A (B (C x (X (Y z)) (Q p)) g) (D )) + >>> mptree[-1] = 'x'; mpcheck(mptree) + ok! (A (B (C x (X (Y z)) (Q p)) g) x) + >>> mptree[-100] = 'y' + Traceback (most recent call last): + . . . + IndexError: index out of range + >>> mptree[()] = make_mptree('(X y)') + Traceback (most recent call last): + . . . + IndexError: The tree position () may not be assigned to. + + >>> # With slices: + >>> mptree = make_mptree('(A (B c) (D e) f g (H i) j (K l))') + >>> b = mptree[0] + >>> mptree[0:0] = ('x', make_mptree('(Y)')); mpcheck(mptree) + ok! (A x (Y ) (B c) (D e) f g (H i) j (K l)) + >>> mptree[2:6] = (); mpcheck(mptree); mpcheck(b) + ok! (A x (Y ) (H i) j (K l)) + ok! (B c) + >>> mptree[-2:] = ('z', 'p'); mpcheck(mptree) + ok! (A x (Y ) (H i) z p) + >>> mptree[1:3] = [make_mptree('(X)') for x in range(10)]; mpcheck(mptree) + ok! (A x (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) (X ) z p) + >>> mptree[5:1000] = []; mpcheck(mptree) + ok! (A x (X ) (X ) (X ) (X )) + >>> mptree[-2:1000] = ['n']; mpcheck(mptree) + ok! (A x (X ) (X ) n) + >>> mptree[-100:1] = [make_mptree('(U v)')]; mpcheck(mptree) + ok! (A (U v) (X ) (X ) n) + >>> mptree[-1:] = (make_mptree('(X)') for x in range(3)); mpcheck(mptree) + ok! (A (U v) (X ) (X ) (X ) (X ) (X )) + >>> mptree[1:-2:2] = ['x', 'y']; mpcheck(mptree) + ok! (A (U v) x (X ) y (X ) (X )) + +**append()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> mptree.append('x'); mpcheck(mptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x) + >>> mptree.append(make_mptree('(X (Y z))')); mpcheck(mptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x (X (Y z))) + +**extend()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> mptree.extend(['x', 'y', make_mptree('(X (Y z))')]); mpcheck(mptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z))) + >>> mptree.extend([]); mpcheck(mptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z))) + >>> mptree.extend(make_mptree('(X)') for x in range(3)); mpcheck(mptree) + ok! (A (B (C (D ) (E f) (Q p)) g) h x y (X (Y z)) (X ) (X ) (X )) + +**insert()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> mptree.insert(0, make_mptree('(X (Y z))')); mpcheck(mptree) + ok! (A (X (Y z)) (B (C (D ) (E f) (Q p)) g) h) + >>> mptree.insert(-1, make_mptree('(X (Y z))')); mpcheck(mptree) + ok! (A (X (Y z)) (B (C (D ) (E f) (Q p)) g) (X (Y z)) h) + >>> mptree.insert(-4, make_mptree('(X (Y z))')); mpcheck(mptree) + ok! (A (X (Y z)) (X (Y z)) (B (C (D ) (E f) (Q p)) g) (X (Y z)) h) + >>> # Note: as with ``list``, inserting at a negative index that + >>> # gives a position before the start of the list does *not* + >>> # raise an IndexError exception; it just inserts at 0. + >>> mptree.insert(-400, make_mptree('(X (Y z))')); mpcheck(mptree) + ok! (A + (X (Y z)) + (X (Y z)) + (X (Y z)) + (B (C (D ) (E f) (Q p)) g) + (X (Y z)) + h) + +**pop()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> mptree[0,0].pop(1); mpcheck(mptree) + MultiParentedTree('E', ['f']) + ok! (A (B (C (D ) (Q p)) g) h) + >>> mptree[0].pop(-1); mpcheck(mptree) + 'g' + ok! (A (B (C (D ) (Q p))) h) + >>> mptree.pop(); mpcheck(mptree) + 'h' + ok! (A (B (C (D ) (Q p)))) + >>> mptree.pop(-100) + Traceback (most recent call last): + . . . + IndexError: index out of range + +**remove()** + + >>> mptree = make_mptree('(A (B (C (D) (E f) (Q p)) g) h)') + >>> e = mptree[0,0,1] + >>> mptree[0,0].remove(mptree[0,0,1]); mpcheck(mptree); mpcheck(e) + ok! (A (B (C (D ) (Q p)) g) h) + ok! (E f) + >>> mptree[0,0].remove(make_mptree('(Q p)')); mpcheck(mptree) + ok! (A (B (C (D )) g) h) + >>> mptree[0,0].remove(make_mptree('(Q p)')) + Traceback (most recent call last): + . . . + ValueError: MultiParentedTree('Q', ['p']) is not in list + >>> mptree.remove('h'); mpcheck(mptree) + ok! (A (B (C (D )) g)) + >>> mptree.remove('h'); + Traceback (most recent call last): + . . . + ValueError: 'h' is not in list + >>> # remove() removes the first subtree that is equal (==) to the + >>> # given tree, which may not be the identical tree we give it: + >>> mptree = make_mptree('(A (X x) (Y y) (X x))') + >>> x1, y, x2 = mptree + >>> mptree.remove(mptree[-1]); mpcheck(mptree) + ok! (A (Y y) (X x)) + >>> print([str(p) for p in x1.parents()]) + [] + >>> print([str(p) for p in x2.parents()]) + ['(A (Y y) (X x))'] + + +ImmutableMultiParentedTree Regression Tests +------------------------------------------- + + >>> imptree = ImmutableMultiParentedTree.convert(mptree) + >>> type(imptree) + + >>> del imptree[0] + Traceback (most recent call last): + . . . + ValueError: ImmutableMultiParentedTree may not be modified + >>> imptree.set_label('newnode') + Traceback (most recent call last): + . . . + ValueError: ImmutableMultiParentedTree may not be modified + + +ProbabilisticTree Regression Tests +---------------------------------- + + >>> prtree = ProbabilisticTree("S", [ProbabilisticTree("NP", ["N"], prob=0.3)], prob=0.6) + >>> print(prtree) + (S (NP N)) (p=0.6) + >>> import copy + >>> prtree == copy.deepcopy(prtree) == prtree.copy(deep=True) == prtree.copy() + True + >>> prtree[0] is prtree.copy()[0] + True + >>> prtree[0] is prtree.copy(deep=True)[0] + False + + >>> imprtree = ImmutableProbabilisticTree.convert(prtree) + >>> type(imprtree) + + >>> del imprtree[0] + Traceback (most recent call last): + . . . + ValueError: ImmutableProbabilisticTree may not be modified + >>> imprtree.set_label('newnode') + Traceback (most recent call last): + . . . + ValueError: ImmutableProbabilisticTree may not be modified + + +Squashed Bugs +============= + +This used to discard the ``(B b)`` subtree (fixed in svn 6270): + + >>> print(Tree.fromstring('((A a) (B b))')) + ( (A a) (B b)) + +Pickling ParentedTree instances didn't work for Python 3.7 onwards (See #2478) + + >>> import pickle + >>> tree = ParentedTree.fromstring('(S (NN x) (NP x) (NN x))') + >>> print(tree) + (S (NN x) (NP x) (NN x)) + + >>> pickled = pickle.dumps(tree) + >>> tree_loaded = pickle.loads(pickled) + >>> print(tree_loaded) + (S (NN x) (NP x) (NN x)) + +ParentedTree used to be impossible to (deep)copy. (See #1324) + + >>> from nltk.tree import ParentedTree + >>> import copy + >>> tree = ParentedTree.fromstring("(TOP (S (NP (NNP Bell,)) (NP (NP (DT a) (NN company)) (SBAR (WHNP (WDT which)) (S (VP (VBZ is) (VP (VBN based) (PP (IN in) (NP (NNP LA,)))))))) (VP (VBZ makes) (CC and) (VBZ distributes) (NP (NN computer))) (. products.)))") + >>> tree == copy.deepcopy(tree) == copy.copy(tree) == tree.copy(deep=True) == tree.copy() + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/treeprettyprinter.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/treeprettyprinter.doctest new file mode 100644 index 0000000..c60290c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/treeprettyprinter.doctest @@ -0,0 +1,177 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +========================================================= + Unit tests for nltk.tree.prettyprinter.TreePrettyPrinter +========================================================= + + >>> from nltk.tree import Tree, TreePrettyPrinter + +Tree nr 2170 from nltk.corpus.treebank: + + >>> tree = Tree.fromstring( + ... '(S (NP-SBJ (PRP I)) (VP (VBP feel) (ADJP-PRD (RB pretty) ' + ... '(JJ good)) (PP-CLR (IN about) (NP (PRP it)))) (. .))') + >>> tpp = TreePrettyPrinter(tree) + >>> print(tpp.text()) + S + __________________________|_____________________ + | VP | + | ____________________|___________ | + | | | PP-CLR | + | | | _____|_____ | + NP-SBJ | ADJP-PRD | NP | + | | _______|______ | | | + PRP VBP RB JJ IN PRP . + | | | | | | | + I feel pretty good about it . + + >>> print(tpp.text(unicodelines=True)) + S + ┌──────────────────────────┼─────────────────────┐ + │ VP │ + │ ┌─────────────┬──────┴───────────┐ │ + │ │ │ PP-CLR │ + │ │ │ ┌─────┴─────┐ │ + NP-SBJ │ ADJP-PRD │ NP │ + │ │ ┌───────┴──────┐ │ │ │ + PRP VBP RB JJ IN PRP . + │ │ │ │ │ │ │ + I feel pretty good about it . + +A tree with long labels: + + >>> tree = Tree.fromstring( + ... '(sentence (plural-noun-phrase (plural-noun Superconductors)) ' + ... '(verb-phrase (plural-verb conduct) ' + ... '(noun-phrase (singular-noun electricity))))') + >>> tpp = TreePrettyPrinter(tree) + >>> print(tpp.text(abbreviate=8, nodedist=2)) + sentence + __________|__________ + | verb-phr. + | __________|__________ + plural-n. | noun-phr. + | | | + plural-n. plural-v. singular. + | | | + Supercon. conduct electric. + + >>> print(tpp.text(maxwidth=8, nodedist=2)) + sentence + _________|________ + | verb- + | phrase + | ________|_________ + plural- | noun- + noun- | phrase + phrase | | + | | | + plural- plural- singular- + noun verb noun + | | | + Supercon conduct electric + ductors ity + +A discontinuous tree: + + >>> tree = Tree.fromstring( + ... '(top (punct 8) (smain (noun 0) (verb 1) (inf (verb 5) (inf (verb 6) ' + ... '(conj (inf (pp (prep 2) (np (det 3) (noun 4))) (verb 7)) (inf (verb 9)) ' + ... '(vg 10) (inf (verb 11)))))) (punct 12))', read_leaf=int) + >>> sentence = ('Ze had met haar moeder kunnen gaan winkelen ,' + ... ' zwemmen of terrassen .'.split()) + >>> tpp = TreePrettyPrinter(tree, sentence) + >>> print(tpp.text()) + top + _____|______________________________________________ + smain | | + _______________________________|_____ | | + | | inf | | + | | _____|____ | | + | | | inf | | + | | | ____|_____ | | + | | | | conj | | + | | _____ | ___ | _________|______ | __________________ | + | | inf | | | | | | | + | | _________|_____ | ___ | _________ | | | | | + | | pp | | | | | | | | + | | ____|____ | | | | | | | | + | | | np | | | | inf | inf | + | | | ____|____ | | | | | | | | + noun verb prep det noun verb verb verb punct verb vg verb punct + | | | | | | | | | | | | | + Ze had met haar moeder kunnen gaan winkelen , zwemmen of terrassen . + + >>> print(tpp.text(unicodelines=True)) + top + ┌─────┴──────────────────┬───────────────────────────┐ + smain │ │ + ┌────┬──────────────────────────┴─────┐ │ │ + │ │ inf │ │ + │ │ ┌─────┴────┐ │ │ + │ │ │ inf │ │ + │ │ │ ┌────┴─────┐ │ │ + │ │ │ │ conj │ │ + │ │ ┌───── │ ─── │ ─────────┴────── │ ─────┬─────┬──────┐ │ + │ │ inf │ │ │ │ │ │ │ + │ │ ┌─────────┴───── │ ─── │ ─────────┐ │ │ │ │ │ + │ │ pp │ │ │ │ │ │ │ │ + │ │ ┌────┴────┐ │ │ │ │ │ │ │ │ + │ │ │ np │ │ │ │ inf │ inf │ + │ │ │ ┌────┴────┐ │ │ │ │ │ │ │ │ + noun verb prep det noun verb verb verb punct verb vg verb punct + │ │ │ │ │ │ │ │ │ │ │ │ │ + Ze had met haar moeder kunnen gaan winkelen , zwemmen of terrassen . + +Importing TreePrettyPrinter +--------------------------- + +First of all, a simple tree will be constructed:: + + >>> from nltk.tree import Tree + >>> tree = Tree.fromstring('(S (NP Mary) (VP walks))') + +We'll use this sample tree to show that the method of importing `TreePrettyPrinter` work correctly: + +- Recommended:: + + >>> from nltk.tree import TreePrettyPrinter + >>> print(TreePrettyPrinter(tree).text()) + S + ____|____ + NP VP + | | + Mary walks + +- Alternative but valid options:: + + >>> from nltk import TreePrettyPrinter + >>> print(TreePrettyPrinter(tree).text()) + S + ____|____ + NP VP + | | + Mary walks + + >>> from nltk.tree.prettyprinter import TreePrettyPrinter + >>> print(TreePrettyPrinter(tree).text()) + S + ____|____ + NP VP + | | + Mary walks + +- Deprecated, do not use:: + + >>> from nltk.treeprettyprinter import TreePrettyPrinter + >>> print(TreePrettyPrinter(tree).text()) + S + ____|____ + NP VP + | | + Mary walks + + This method will throw a DeprecationWarning:: + + Import `TreePrettyPrinter` using `from nltk.tree import TreePrettyPrinter` instead. diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/treetransforms.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/treetransforms.doctest new file mode 100644 index 0000000..f815673 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/treetransforms.doctest @@ -0,0 +1,154 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +------------------------------------------- +Unit tests for the TreeTransformation class +------------------------------------------- + + >>> from copy import deepcopy + >>> from nltk.tree import Tree, collapse_unary, chomsky_normal_form, un_chomsky_normal_form + + >>> tree_string = "(TOP (S (S (VP (VBN Turned) (ADVP (RB loose)) (PP (IN in) (NP (NP (NNP Shane) (NNP Longman) (POS 's)) (NN trading) (NN room))))) (, ,) (NP (DT the) (NN yuppie) (NNS dealers)) (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right)))) (. .)))" + + >>> tree = Tree.fromstring(tree_string) + >>> print(tree) + (TOP + (S + (S + (VP + (VBN Turned) + (ADVP (RB loose)) + (PP + (IN in) + (NP + (NP (NNP Shane) (NNP Longman) (POS 's)) + (NN trading) + (NN room))))) + (, ,) + (NP (DT the) (NN yuppie) (NNS dealers)) + (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right)))) + (. .))) + +Make a copy of the original tree and collapse the subtrees with only one child + + >>> collapsedTree = deepcopy(tree) + >>> collapse_unary(collapsedTree) + >>> print(collapsedTree) + (TOP + (S + (S+VP + (VBN Turned) + (ADVP (RB loose)) + (PP + (IN in) + (NP + (NP (NNP Shane) (NNP Longman) (POS 's)) + (NN trading) + (NN room)))) + (, ,) + (NP (DT the) (NN yuppie) (NNS dealers)) + (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right)))) + (. .))) + + >>> collapsedTree2 = deepcopy(tree) + >>> collapse_unary(collapsedTree2, collapsePOS=True, collapseRoot=True) + >>> print(collapsedTree2) + (TOP+S + (S+VP + (VBN Turned) + (ADVP+RB loose) + (PP + (IN in) + (NP + (NP (NNP Shane) (NNP Longman) (POS 's)) + (NN trading) + (NN room)))) + (, ,) + (NP (DT the) (NN yuppie) (NNS dealers)) + (VP (AUX do) (NP (NP+RB little) (ADJP+RB right))) + (. .)) + +Convert the tree to Chomsky Normal Form i.e. each subtree has either two +subtree children or a single leaf value. This conversion can be performed +using either left- or right-factoring. + + >>> cnfTree = deepcopy(collapsedTree) + >>> chomsky_normal_form(cnfTree, factor='left') + >>> print(cnfTree) + (TOP + (S + (S| + (S| + (S| + (S+VP + (S+VP| (VBN Turned) (ADVP (RB loose))) + (PP + (IN in) + (NP + (NP| + (NP + (NP| (NNP Shane) (NNP Longman)) + (POS 's)) + (NN trading)) + (NN room)))) + (, ,)) + (NP (NP| (DT the) (NN yuppie)) (NNS dealers))) + (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right))))) + (. .))) + + >>> cnfTree = deepcopy(collapsedTree) + >>> chomsky_normal_form(cnfTree, factor='right') + >>> print(cnfTree) + (TOP + (S + (S+VP + (VBN Turned) + (S+VP| + (ADVP (RB loose)) + (PP + (IN in) + (NP + (NP (NNP Shane) (NP| (NNP Longman) (POS 's))) + (NP| (NN trading) (NN room)))))) + (S|<,-NP-VP-.> + (, ,) + (S| + (NP (DT the) (NP| (NN yuppie) (NNS dealers))) + (S| + (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right)))) + (. .)))))) + +Employ some Markov smoothing to make the artificial node labels a bit more +readable. See the treetransforms.py documentation for more details. + + >>> markovTree = deepcopy(collapsedTree) + >>> chomsky_normal_form(markovTree, horzMarkov=2, vertMarkov=1) + >>> print(markovTree) + (TOP + (S^ + (S+VP^ + (VBN Turned) + (S+VP|^ + (ADVP^ (RB loose)) + (PP^ + (IN in) + (NP^ + (NP^ + (NNP Shane) + (NP|^ (NNP Longman) (POS 's))) + (NP|^ (NN trading) (NN room)))))) + (S|<,-NP>^ + (, ,) + (S|^ + (NP^ (DT the) (NP|^ (NN yuppie) (NNS dealers))) + (S|^ + (VP^ + (AUX do) + (NP^ (NP^ (RB little)) (ADJP^ (RB right)))) + (. .)))))) + +Convert the transformed tree back to its original form + + >>> un_chomsky_normal_form(markovTree) + >>> tree == markovTree + True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a46b6d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_aline.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_aline.cpython-310.pyc new file mode 100644 index 0000000..c59283e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_aline.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_bllip.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_bllip.cpython-310.pyc new file mode 100644 index 0000000..872e329 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_bllip.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_brill.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_brill.cpython-310.pyc new file mode 100644 index 0000000..fcf79f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_brill.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfd_mutation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfd_mutation.cpython-310.pyc new file mode 100644 index 0000000..8597ab2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfd_mutation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfg2chomsky.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfg2chomsky.cpython-310.pyc new file mode 100644 index 0000000..b2e8057 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_cfg2chomsky.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_chunk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_chunk.cpython-310.pyc new file mode 100644 index 0000000..2282b3d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_chunk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_classify.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_classify.cpython-310.pyc new file mode 100644 index 0000000..7338c08 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_classify.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_collocations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_collocations.cpython-310.pyc new file mode 100644 index 0000000..19cbf6d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_collocations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_concordance.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_concordance.cpython-310.pyc new file mode 100644 index 0000000..d933409 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_concordance.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corenlp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corenlp.cpython-310.pyc new file mode 100644 index 0000000..f93c6f4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corenlp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpora.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpora.cpython-310.pyc new file mode 100644 index 0000000..4533d42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpora.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpus_views.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpus_views.cpython-310.pyc new file mode 100644 index 0000000..731fbb1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_corpus_views.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_data.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_data.cpython-310.pyc new file mode 100644 index 0000000..93cdc3a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_data.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_disagreement.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_disagreement.cpython-310.pyc new file mode 100644 index 0000000..154bb18 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_disagreement.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_distance.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_distance.cpython-310.pyc new file mode 100644 index 0000000..caf0254 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_distance.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_downloader.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_downloader.cpython-310.pyc new file mode 100644 index 0000000..bca616c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_downloader.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_freqdist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_freqdist.cpython-310.pyc new file mode 100644 index 0000000..fcf4bfa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_freqdist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_hmm.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_hmm.cpython-310.pyc new file mode 100644 index 0000000..f66de2f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_hmm.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json2csv_corpus.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json2csv_corpus.cpython-310.pyc new file mode 100644 index 0000000..e34fe0b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json2csv_corpus.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json_serialization.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json_serialization.cpython-310.pyc new file mode 100644 index 0000000..bf87d0a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_json_serialization.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_metrics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_metrics.cpython-310.pyc new file mode 100644 index 0000000..b557ccd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_metrics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_naivebayes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_naivebayes.cpython-310.pyc new file mode 100644 index 0000000..a20d67f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_naivebayes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_nombank.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_nombank.cpython-310.pyc new file mode 100644 index 0000000..77cac84 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_nombank.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pl196x.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pl196x.cpython-310.pyc new file mode 100644 index 0000000..e757332 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pl196x.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pos_tag.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pos_tag.cpython-310.pyc new file mode 100644 index 0000000..adee6f1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_pos_tag.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_ribes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_ribes.cpython-310.pyc new file mode 100644 index 0000000..fc6173d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_ribes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_rte_classify.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_rte_classify.cpython-310.pyc new file mode 100644 index 0000000..83b92c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_rte_classify.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_seekable_unicode_stream_reader.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_seekable_unicode_stream_reader.cpython-310.pyc new file mode 100644 index 0000000..c56abbc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_seekable_unicode_stream_reader.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_senna.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_senna.cpython-310.pyc new file mode 100644 index 0000000..4d8bf55 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_senna.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_stem.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_stem.cpython-310.pyc new file mode 100644 index 0000000..82e71e2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_stem.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tag.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tag.cpython-310.pyc new file mode 100644 index 0000000..183f84d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tag.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tgrep.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tgrep.cpython-310.pyc new file mode 100644 index 0000000..3e9ace6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tgrep.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tokenize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tokenize.cpython-310.pyc new file mode 100644 index 0000000..0899431 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_tokenize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_twitter_auth.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_twitter_auth.cpython-310.pyc new file mode 100644 index 0000000..2175421 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_twitter_auth.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_util.cpython-310.pyc new file mode 100644 index 0000000..666f718 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_wordnet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_wordnet.cpython-310.pyc new file mode 100644 index 0000000..d445a7d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/__pycache__/test_wordnet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..31806fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_counter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_counter.cpython-310.pyc new file mode 100644 index 0000000..37ad54c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_counter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_models.cpython-310.pyc new file mode 100644 index 0000000..d7e5a7f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_preprocessing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_preprocessing.cpython-310.pyc new file mode 100644 index 0000000..ed77e9b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_preprocessing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_vocabulary.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_vocabulary.cpython-310.pyc new file mode 100644 index 0000000..45fb9be Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/__pycache__/test_vocabulary.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_counter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_counter.py new file mode 100644 index 0000000..e037df9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_counter.py @@ -0,0 +1,116 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT + +import unittest + +import pytest + +from nltk import FreqDist +from nltk.lm import NgramCounter +from nltk.util import everygrams + + +class TestNgramCounter: + """Tests for NgramCounter that only involve lookup, no modification.""" + + @classmethod + def setup_class(self): + text = [list("abcd"), list("egdbe")] + self.trigram_counter = NgramCounter( + everygrams(sent, max_len=3) for sent in text + ) + self.bigram_counter = NgramCounter(everygrams(sent, max_len=2) for sent in text) + self.case = unittest.TestCase() + + def test_N(self): + assert self.bigram_counter.N() == 16 + assert self.trigram_counter.N() == 21 + + def test_counter_len_changes_with_lookup(self): + assert len(self.bigram_counter) == 2 + self.bigram_counter[50] + assert len(self.bigram_counter) == 3 + + def test_ngram_order_access_unigrams(self): + assert self.bigram_counter[1] == self.bigram_counter.unigrams + + def test_ngram_conditional_freqdist(self): + case = unittest.TestCase() + expected_trigram_contexts = [ + ("a", "b"), + ("b", "c"), + ("e", "g"), + ("g", "d"), + ("d", "b"), + ] + expected_bigram_contexts = [("a",), ("b",), ("d",), ("e",), ("c",), ("g",)] + + bigrams = self.trigram_counter[2] + trigrams = self.trigram_counter[3] + + self.case.assertCountEqual(expected_bigram_contexts, bigrams.conditions()) + self.case.assertCountEqual(expected_trigram_contexts, trigrams.conditions()) + + def test_bigram_counts_seen_ngrams(self): + assert self.bigram_counter[["a"]]["b"] == 1 + assert self.bigram_counter[["b"]]["c"] == 1 + + def test_bigram_counts_unseen_ngrams(self): + assert self.bigram_counter[["b"]]["z"] == 0 + + def test_unigram_counts_seen_words(self): + assert self.bigram_counter["b"] == 2 + + def test_unigram_counts_completely_unseen_words(self): + assert self.bigram_counter["z"] == 0 + + +class TestNgramCounterTraining: + @classmethod + def setup_class(self): + self.counter = NgramCounter() + self.case = unittest.TestCase() + + @pytest.mark.parametrize("case", ["", [], None]) + def test_empty_inputs(self, case): + test = NgramCounter(case) + assert 2 not in test + assert test[1] == FreqDist() + + def test_train_on_unigrams(self): + words = list("abcd") + counter = NgramCounter([[(w,) for w in words]]) + + assert not counter[3] + assert not counter[2] + self.case.assertCountEqual(words, counter[1].keys()) + + def test_train_on_illegal_sentences(self): + str_sent = ["Check", "this", "out", "!"] + list_sent = [["Check", "this"], ["this", "out"], ["out", "!"]] + + with pytest.raises(TypeError): + NgramCounter([str_sent]) + + with pytest.raises(TypeError): + NgramCounter([list_sent]) + + def test_train_on_bigrams(self): + bigram_sent = [("a", "b"), ("c", "d")] + counter = NgramCounter([bigram_sent]) + assert not bool(counter[3]) + + def test_train_on_mix(self): + mixed_sent = [("a", "b"), ("c", "d"), ("e", "f", "g"), ("h",)] + counter = NgramCounter([mixed_sent]) + unigrams = ["h"] + bigram_contexts = [("a",), ("c",)] + trigram_contexts = [("e", "f")] + + self.case.assertCountEqual(unigrams, counter[1].keys()) + self.case.assertCountEqual(bigram_contexts, counter[2].keys()) + self.case.assertCountEqual(trigram_contexts, counter[3].keys()) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_models.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_models.py new file mode 100644 index 0000000..72641a5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_models.py @@ -0,0 +1,610 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +import math +from operator import itemgetter + +import pytest + +from nltk.lm import ( + MLE, + AbsoluteDiscountingInterpolated, + KneserNeyInterpolated, + Laplace, + Lidstone, + StupidBackoff, + Vocabulary, + WittenBellInterpolated, +) +from nltk.lm.preprocessing import padded_everygrams + + +@pytest.fixture(scope="session") +def vocabulary(): + return Vocabulary(["a", "b", "c", "d", "z", "", ""], unk_cutoff=1) + + +@pytest.fixture(scope="session") +def training_data(): + return [["a", "b", "c", "d"], ["e", "g", "a", "d", "b", "e"]] + + +@pytest.fixture(scope="session") +def bigram_training_data(training_data): + return [list(padded_everygrams(2, sent)) for sent in training_data] + + +@pytest.fixture(scope="session") +def trigram_training_data(training_data): + return [list(padded_everygrams(3, sent)) for sent in training_data] + + +@pytest.fixture +def mle_bigram_model(vocabulary, bigram_training_data): + model = MLE(2, vocabulary=vocabulary) + model.fit(bigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + ("d", ["c"], 1), + # Unseen ngrams should yield 0 + ("d", ["e"], 0), + # Unigrams should also be 0 + ("z", None, 0), + # N unigrams = 14 + # count('a') = 2 + ("a", None, 2.0 / 14), + # count('y') = 3 + ("y", None, 3.0 / 14), + ], +) +def test_mle_bigram_scores(mle_bigram_model, word, context, expected_score): + assert pytest.approx(mle_bigram_model.score(word, context), 1e-4) == expected_score + + +def test_mle_bigram_logscore_for_zero_score(mle_bigram_model): + assert math.isinf(mle_bigram_model.logscore("d", ["e"])) + + +def test_mle_bigram_entropy_perplexity_seen(mle_bigram_model): + # ngrams seen during training + trained = [ + ("", "a"), + ("a", "b"), + ("b", ""), + ("", "a"), + ("a", "d"), + ("d", ""), + ] + # Ngram = Log score + # , a = -1 + # a, b = -1 + # b, UNK = -1 + # UNK, a = -1.585 + # a, d = -1 + # d, = -1 + # TOTAL logscores = -6.585 + # - AVG logscores = 1.0975 + H = 1.0975 + perplexity = 2.1398 + assert pytest.approx(mle_bigram_model.entropy(trained), 1e-4) == H + assert pytest.approx(mle_bigram_model.perplexity(trained), 1e-4) == perplexity + + +def test_mle_bigram_entropy_perplexity_unseen(mle_bigram_model): + # In MLE, even one unseen ngram should make entropy and perplexity infinite + untrained = [("", "a"), ("a", "c"), ("c", "d"), ("d", "")] + + assert math.isinf(mle_bigram_model.entropy(untrained)) + assert math.isinf(mle_bigram_model.perplexity(untrained)) + + +def test_mle_bigram_entropy_perplexity_unigrams(mle_bigram_model): + # word = score, log score + # = 0.1429, -2.8074 + # a = 0.1429, -2.8074 + # c = 0.0714, -3.8073 + # UNK = 0.2143, -2.2224 + # d = 0.1429, -2.8074 + # c = 0.0714, -3.8073 + # = 0.1429, -2.8074 + # TOTAL logscores = -21.6243 + # - AVG logscores = 3.0095 + H = 3.0095 + perplexity = 8.0529 + + text = [("",), ("a",), ("c",), ("-",), ("d",), ("c",), ("",)] + + assert pytest.approx(mle_bigram_model.entropy(text), 1e-4) == H + assert pytest.approx(mle_bigram_model.perplexity(text), 1e-4) == perplexity + + +@pytest.fixture +def mle_trigram_model(trigram_training_data, vocabulary): + model = MLE(order=3, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # count(d | b, c) = 1 + # count(b, c) = 1 + ("d", ("b", "c"), 1), + # count(d | c) = 1 + # count(c) = 1 + ("d", ["c"], 1), + # total number of tokens is 18, of which "a" occurred 2 times + ("a", None, 2.0 / 18), + # in vocabulary but unseen + ("z", None, 0), + # out of vocabulary should use "UNK" score + ("y", None, 3.0 / 18), + ], +) +def test_mle_trigram_scores(mle_trigram_model, word, context, expected_score): + assert pytest.approx(mle_trigram_model.score(word, context), 1e-4) == expected_score + + +@pytest.fixture +def lidstone_bigram_model(bigram_training_data, vocabulary): + model = Lidstone(0.1, order=2, vocabulary=vocabulary) + model.fit(bigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # count(d | c) = 1 + # *count(d | c) = 1.1 + # Count(w | c for w in vocab) = 1 + # *Count(w | c for w in vocab) = 1.8 + ("d", ["c"], 1.1 / 1.8), + # Total unigrams: 14 + # Vocab size: 8 + # Denominator: 14 + 0.8 = 14.8 + # count("a") = 2 + # *count("a") = 2.1 + ("a", None, 2.1 / 14.8), + # in vocabulary but unseen + # count("z") = 0 + # *count("z") = 0.1 + ("z", None, 0.1 / 14.8), + # out of vocabulary should use "UNK" score + # count("") = 3 + # *count("") = 3.1 + ("y", None, 3.1 / 14.8), + ], +) +def test_lidstone_bigram_score(lidstone_bigram_model, word, context, expected_score): + assert ( + pytest.approx(lidstone_bigram_model.score(word, context), 1e-4) + == expected_score + ) + + +def test_lidstone_entropy_perplexity(lidstone_bigram_model): + text = [ + ("", "a"), + ("a", "c"), + ("c", ""), + ("", "d"), + ("d", "c"), + ("c", ""), + ] + # Unlike MLE this should be able to handle completely novel ngrams + # Ngram = score, log score + # , a = 0.3929, -1.3479 + # a, c = 0.0357, -4.8074 + # c, UNK = 0.0(5), -4.1699 + # UNK, d = 0.0263, -5.2479 + # d, c = 0.0357, -4.8074 + # c, = 0.0(5), -4.1699 + # TOTAL logscore: −24.5504 + # - AVG logscore: 4.0917 + H = 4.0917 + perplexity = 17.0504 + assert pytest.approx(lidstone_bigram_model.entropy(text), 1e-4) == H + assert pytest.approx(lidstone_bigram_model.perplexity(text), 1e-4) == perplexity + + +@pytest.fixture +def lidstone_trigram_model(trigram_training_data, vocabulary): + model = Lidstone(0.1, order=3, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # Logic behind this is the same as for bigram model + ("d", ["c"], 1.1 / 1.8), + # if we choose a word that hasn't appeared after (b, c) + ("e", ["c"], 0.1 / 1.8), + # Trigram score now + ("d", ["b", "c"], 1.1 / 1.8), + ("e", ["b", "c"], 0.1 / 1.8), + ], +) +def test_lidstone_trigram_score(lidstone_trigram_model, word, context, expected_score): + assert ( + pytest.approx(lidstone_trigram_model.score(word, context), 1e-4) + == expected_score + ) + + +@pytest.fixture +def laplace_bigram_model(bigram_training_data, vocabulary): + model = Laplace(2, vocabulary=vocabulary) + model.fit(bigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # basic sanity-check: + # count(d | c) = 1 + # *count(d | c) = 2 + # Count(w | c for w in vocab) = 1 + # *Count(w | c for w in vocab) = 9 + ("d", ["c"], 2.0 / 9), + # Total unigrams: 14 + # Vocab size: 8 + # Denominator: 14 + 8 = 22 + # count("a") = 2 + # *count("a") = 3 + ("a", None, 3.0 / 22), + # in vocabulary but unseen + # count("z") = 0 + # *count("z") = 1 + ("z", None, 1.0 / 22), + # out of vocabulary should use "UNK" score + # count("") = 3 + # *count("") = 4 + ("y", None, 4.0 / 22), + ], +) +def test_laplace_bigram_score(laplace_bigram_model, word, context, expected_score): + assert ( + pytest.approx(laplace_bigram_model.score(word, context), 1e-4) == expected_score + ) + + +def test_laplace_bigram_entropy_perplexity(laplace_bigram_model): + text = [ + ("", "a"), + ("a", "c"), + ("c", ""), + ("", "d"), + ("d", "c"), + ("c", ""), + ] + # Unlike MLE this should be able to handle completely novel ngrams + # Ngram = score, log score + # , a = 0.2, -2.3219 + # a, c = 0.1, -3.3219 + # c, UNK = 0.(1), -3.1699 + # UNK, d = 0.(09), 3.4594 + # d, c = 0.1 -3.3219 + # c, = 0.(1), -3.1699 + # Total logscores: −18.7651 + # - AVG logscores: 3.1275 + H = 3.1275 + perplexity = 8.7393 + assert pytest.approx(laplace_bigram_model.entropy(text), 1e-4) == H + assert pytest.approx(laplace_bigram_model.perplexity(text), 1e-4) == perplexity + + +def test_laplace_gamma(laplace_bigram_model): + assert laplace_bigram_model.gamma == 1 + + +@pytest.fixture +def wittenbell_trigram_model(trigram_training_data, vocabulary): + model = WittenBellInterpolated(3, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # For unigram scores by default revert to regular MLE + # Total unigrams: 18 + # Vocab Size = 7 + # count('c'): 1 + ("c", None, 1.0 / 18), + # in vocabulary but unseen + # count("z") = 0 + ("z", None, 0 / 18), + # out of vocabulary should use "UNK" score + # count("") = 3 + ("y", None, 3.0 / 18), + # 2 words follow b and b occurred a total of 2 times + # gamma(['b']) = 2 / (2 + 2) = 0.5 + # mle.score('c', ['b']) = 0.5 + # mle('c') = 1 / 18 = 0.055 + # (1 - gamma) * mle + gamma * mle('c') ~= 0.27 + 0.055 + ("c", ["b"], (1 - 0.5) * 0.5 + 0.5 * 1 / 18), + # building on that, let's try 'a b c' as the trigram + # 1 word follows 'a b' and 'a b' occurred 1 time + # gamma(['a', 'b']) = 1 / (1 + 1) = 0.5 + # mle("c", ["a", "b"]) = 1 + ("c", ["a", "b"], (1 - 0.5) + 0.5 * ((1 - 0.5) * 0.5 + 0.5 * 1 / 18)), + # P(c|zb) + # The ngram 'zbc' was not seen, so we use P(c|b). See issue #2332. + ("c", ["z", "b"], ((1 - 0.5) * 0.5 + 0.5 * 1 / 18)), + ], +) +def test_wittenbell_trigram_score( + wittenbell_trigram_model, word, context, expected_score +): + assert ( + pytest.approx(wittenbell_trigram_model.score(word, context), 1e-4) + == expected_score + ) + + +############################################################################### +# Notation Explained # +############################################################################### +# For all subsequent calculations we use the following notation: +# 1. '*': Placeholder for any word/character. E.g. '*b' stands for +# all bigrams that end in 'b'. '*b*' stands for all trigrams that +# contain 'b' in the middle. +# 1. count(ngram): Count all instances (tokens) of an ngram. +# 1. unique(ngram): Count unique instances (types) of an ngram. + + +@pytest.fixture +def kneserney_trigram_model(trigram_training_data, vocabulary): + model = KneserNeyInterpolated(order=3, discount=0.75, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # P(c) = count('*c') / unique('**') + # = 1 / 14 + ("c", None, 1.0 / 14), + # P(z) = count('*z') / unique('**') + # = 0 / 14 + # 'z' is in the vocabulary, but it was not seen during training. + ("z", None, 0.0 / 14), + # P(y) + # Out of vocabulary should use "UNK" score. + # P(y) = P(UNK) = count('*UNK') / unique('**') + ("y", None, 3 / 14), + # We start with P(c|b) + # P(c|b) = alpha('bc') + gamma('b') * P(c) + # alpha('bc') = max(unique('*bc') - discount, 0) / unique('*b*') + # = max(1 - 0.75, 0) / 2 + # = 0.125 + # gamma('b') = discount * unique('b*') / unique('*b*') + # = (0.75 * 2) / 2 + # = 0.75 + ("c", ["b"], (0.125 + 0.75 * (1 / 14))), + # Building on that, let's try P(c|ab). + # P(c|ab) = alpha('abc') + gamma('ab') * P(c|b) + # alpha('abc') = max(count('abc') - discount, 0) / count('ab*') + # = max(1 - 0.75, 0) / 1 + # = 0.25 + # gamma('ab') = (discount * unique('ab*')) / count('ab*') + # = 0.75 * 1 / 1 + ("c", ["a", "b"], 0.25 + 0.75 * (0.125 + 0.75 * (1 / 14))), + # P(c|zb) + # The ngram 'zbc' was not seen, so we use P(c|b). See issue #2332. + ("c", ["z", "b"], (0.125 + 0.75 * (1 / 14))), + ], +) +def test_kneserney_trigram_score( + kneserney_trigram_model, word, context, expected_score +): + assert ( + pytest.approx(kneserney_trigram_model.score(word, context), 1e-4) + == expected_score + ) + + +@pytest.fixture +def absolute_discounting_trigram_model(trigram_training_data, vocabulary): + model = AbsoluteDiscountingInterpolated(order=3, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # For unigram scores revert to uniform + # P(c) = count('c') / count('**') + ("c", None, 1.0 / 18), + # in vocabulary but unseen + # count('z') = 0 + ("z", None, 0.0 / 18), + # out of vocabulary should use "UNK" score + # count('') = 3 + ("y", None, 3 / 18), + # P(c|b) = alpha('bc') + gamma('b') * P(c) + # alpha('bc') = max(count('bc') - discount, 0) / count('b*') + # = max(1 - 0.75, 0) / 2 + # = 0.125 + # gamma('b') = discount * unique('b*') / count('b*') + # = (0.75 * 2) / 2 + # = 0.75 + ("c", ["b"], (0.125 + 0.75 * (2 / 2) * (1 / 18))), + # Building on that, let's try P(c|ab). + # P(c|ab) = alpha('abc') + gamma('ab') * P(c|b) + # alpha('abc') = max(count('abc') - discount, 0) / count('ab*') + # = max(1 - 0.75, 0) / 1 + # = 0.25 + # gamma('ab') = (discount * unique('ab*')) / count('ab*') + # = 0.75 * 1 / 1 + ("c", ["a", "b"], 0.25 + 0.75 * (0.125 + 0.75 * (2 / 2) * (1 / 18))), + # P(c|zb) + # The ngram 'zbc' was not seen, so we use P(c|b). See issue #2332. + ("c", ["z", "b"], (0.125 + 0.75 * (2 / 2) * (1 / 18))), + ], +) +def test_absolute_discounting_trigram_score( + absolute_discounting_trigram_model, word, context, expected_score +): + assert ( + pytest.approx(absolute_discounting_trigram_model.score(word, context), 1e-4) + == expected_score + ) + + +@pytest.fixture +def stupid_backoff_trigram_model(trigram_training_data, vocabulary): + model = StupidBackoff(order=3, vocabulary=vocabulary) + model.fit(trigram_training_data) + return model + + +@pytest.mark.parametrize( + "word, context, expected_score", + [ + # For unigram scores revert to uniform + # total bigrams = 18 + ("c", None, 1.0 / 18), + # in vocabulary but unseen + # bigrams ending with z = 0 + ("z", None, 0.0 / 18), + # out of vocabulary should use "UNK" score + # count(''): 3 + ("y", None, 3 / 18), + # c follows 1 time out of 2 after b + ("c", ["b"], 1 / 2), + # c always follows ab + ("c", ["a", "b"], 1 / 1), + # The ngram 'z b c' was not seen, so we backoff to + # the score of the ngram 'b c' * smoothing factor + ("c", ["z", "b"], (0.4 * (1 / 2))), + ], +) +def test_stupid_backoff_trigram_score( + stupid_backoff_trigram_model, word, context, expected_score +): + assert ( + pytest.approx(stupid_backoff_trigram_model.score(word, context), 1e-4) + == expected_score + ) + + +############################################################################### +# Probability Distributions Should Sum up to Unity # +############################################################################### + + +@pytest.fixture(scope="session") +def kneserney_bigram_model(bigram_training_data, vocabulary): + model = KneserNeyInterpolated(order=2, vocabulary=vocabulary) + model.fit(bigram_training_data) + return model + + +@pytest.mark.parametrize( + "model_fixture", + [ + "mle_bigram_model", + "mle_trigram_model", + "lidstone_bigram_model", + "laplace_bigram_model", + "wittenbell_trigram_model", + "absolute_discounting_trigram_model", + "kneserney_bigram_model", + pytest.param( + "stupid_backoff_trigram_model", + marks=pytest.mark.xfail( + reason="Stupid Backoff is not a valid distribution" + ), + ), + ], +) +@pytest.mark.parametrize( + "context", + [("a",), ("c",), ("",), ("b",), ("",), ("d",), ("e",), ("r",), ("w",)], + ids=itemgetter(0), +) +def test_sums_to_1(model_fixture, context, request): + model = request.getfixturevalue(model_fixture) + scores_for_context = sum(model.score(w, context) for w in model.vocab) + assert pytest.approx(scores_for_context, 1e-7) == 1.0 + + +############################################################################### +# Generating Text # +############################################################################### + + +def test_generate_one_no_context(mle_trigram_model): + assert mle_trigram_model.generate(random_seed=3) == "" + + +def test_generate_one_from_limiting_context(mle_trigram_model): + # We don't need random_seed for contexts with only one continuation + assert mle_trigram_model.generate(text_seed=["c"]) == "d" + assert mle_trigram_model.generate(text_seed=["b", "c"]) == "d" + assert mle_trigram_model.generate(text_seed=["a", "c"]) == "d" + + +def test_generate_one_from_varied_context(mle_trigram_model): + # When context doesn't limit our options enough, seed the random choice + assert mle_trigram_model.generate(text_seed=("a", ""), random_seed=2) == "a" + + +def test_generate_cycle(mle_trigram_model): + # Add a cycle to the model: bd -> b, db -> d + more_training_text = [padded_everygrams(mle_trigram_model.order, list("bdbdbd"))] + + mle_trigram_model.fit(more_training_text) + # Test that we can escape the cycle + assert mle_trigram_model.generate(7, text_seed=("b", "d"), random_seed=5) == [ + "b", + "d", + "b", + "d", + "b", + "d", + "", + ] + + +def test_generate_with_text_seed(mle_trigram_model): + assert mle_trigram_model.generate(5, text_seed=("", "e"), random_seed=3) == [ + "", + "a", + "d", + "b", + "", + ] + + +def test_generate_oov_text_seed(mle_trigram_model): + assert mle_trigram_model.generate( + text_seed=("aliens",), random_seed=3 + ) == mle_trigram_model.generate(text_seed=("",), random_seed=3) + + +def test_generate_None_text_seed(mle_trigram_model): + # should crash with type error when we try to look it up in vocabulary + with pytest.raises(TypeError): + mle_trigram_model.generate(text_seed=(None,)) + + # This will work + assert mle_trigram_model.generate( + text_seed=None, random_seed=3 + ) == mle_trigram_model.generate(random_seed=3) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_preprocessing.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_preprocessing.py new file mode 100644 index 0000000..fbccdfa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_preprocessing.py @@ -0,0 +1,30 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT +import unittest + +from nltk.lm.preprocessing import padded_everygram_pipeline + + +class TestPreprocessing(unittest.TestCase): + def test_padded_everygram_pipeline(self): + expected_train = [ + [ + ("",), + ("", "a"), + ("a",), + ("a", "b"), + ("b",), + ("b", "c"), + ("c",), + ("c", ""), + ("",), + ] + ] + expected_vocab = ["", "a", "b", "c", ""] + train_data, vocab_data = padded_everygram_pipeline(2, [["a", "b", "c"]]) + self.assertEqual([list(sent) for sent in train_data], expected_train) + self.assertEqual(list(vocab_data), expected_vocab) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_vocabulary.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_vocabulary.py new file mode 100644 index 0000000..6d3de44 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/lm/test_vocabulary.py @@ -0,0 +1,156 @@ +# Natural Language Toolkit: Language Model Unit Tests +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ilia Kurenkov +# URL: +# For license information, see LICENSE.TXT + +import unittest +from collections import Counter +from timeit import timeit + +from nltk.lm import Vocabulary + + +class NgramModelVocabularyTests(unittest.TestCase): + """tests Vocabulary Class""" + + @classmethod + def setUpClass(cls): + cls.vocab = Vocabulary( + ["z", "a", "b", "c", "f", "d", "e", "g", "a", "d", "b", "e", "w"], + unk_cutoff=2, + ) + + def test_truthiness(self): + self.assertTrue(self.vocab) + + def test_cutoff_value_set_correctly(self): + self.assertEqual(self.vocab.cutoff, 2) + + def test_unable_to_change_cutoff(self): + with self.assertRaises(AttributeError): + self.vocab.cutoff = 3 + + def test_cutoff_setter_checks_value(self): + with self.assertRaises(ValueError) as exc_info: + Vocabulary("abc", unk_cutoff=0) + expected_error_msg = "Cutoff value cannot be less than 1. Got: 0" + self.assertEqual(expected_error_msg, str(exc_info.exception)) + + def test_counts_set_correctly(self): + self.assertEqual(self.vocab.counts["a"], 2) + self.assertEqual(self.vocab.counts["b"], 2) + self.assertEqual(self.vocab.counts["c"], 1) + + def test_membership_check_respects_cutoff(self): + # a was seen 2 times, so it should be considered part of the vocabulary + self.assertTrue("a" in self.vocab) + # "c" was seen once, it shouldn't be considered part of the vocab + self.assertFalse("c" in self.vocab) + # "z" was never seen at all, also shouldn't be considered in the vocab + self.assertFalse("z" in self.vocab) + + def test_vocab_len_respects_cutoff(self): + # Vocab size is the number of unique tokens that occur at least as often + # as the cutoff value, plus 1 to account for unknown words. + self.assertEqual(5, len(self.vocab)) + + def test_vocab_iter_respects_cutoff(self): + vocab_counts = ["a", "b", "c", "d", "e", "f", "g", "w", "z"] + vocab_items = ["a", "b", "d", "e", ""] + + self.assertCountEqual(vocab_counts, list(self.vocab.counts.keys())) + self.assertCountEqual(vocab_items, list(self.vocab)) + + def test_update_empty_vocab(self): + empty = Vocabulary(unk_cutoff=2) + self.assertEqual(len(empty), 0) + self.assertFalse(empty) + self.assertIn(empty.unk_label, empty) + + empty.update(list("abcde")) + self.assertIn(empty.unk_label, empty) + + def test_lookup(self): + self.assertEqual(self.vocab.lookup("a"), "a") + self.assertEqual(self.vocab.lookup("c"), "") + + def test_lookup_iterables(self): + self.assertEqual(self.vocab.lookup(["a", "b"]), ("a", "b")) + self.assertEqual(self.vocab.lookup(("a", "b")), ("a", "b")) + self.assertEqual(self.vocab.lookup(("a", "c")), ("a", "")) + self.assertEqual( + self.vocab.lookup(map(str, range(3))), ("", "", "") + ) + + def test_lookup_empty_iterables(self): + self.assertEqual(self.vocab.lookup(()), ()) + self.assertEqual(self.vocab.lookup([]), ()) + self.assertEqual(self.vocab.lookup(iter([])), ()) + self.assertEqual(self.vocab.lookup(n for n in range(0, 0)), ()) + + def test_lookup_recursive(self): + self.assertEqual( + self.vocab.lookup([["a", "b"], ["a", "c"]]), (("a", "b"), ("a", "")) + ) + self.assertEqual(self.vocab.lookup([["a", "b"], "c"]), (("a", "b"), "")) + self.assertEqual(self.vocab.lookup([[[[["a", "b"]]]]]), ((((("a", "b"),),),),)) + + def test_lookup_None(self): + with self.assertRaises(TypeError): + self.vocab.lookup(None) + with self.assertRaises(TypeError): + list(self.vocab.lookup([None, None])) + + def test_lookup_int(self): + with self.assertRaises(TypeError): + self.vocab.lookup(1) + with self.assertRaises(TypeError): + list(self.vocab.lookup([1, 2])) + + def test_lookup_empty_str(self): + self.assertEqual(self.vocab.lookup(""), "") + + def test_eqality(self): + v1 = Vocabulary(["a", "b", "c"], unk_cutoff=1) + v2 = Vocabulary(["a", "b", "c"], unk_cutoff=1) + v3 = Vocabulary(["a", "b", "c"], unk_cutoff=1, unk_label="blah") + v4 = Vocabulary(["a", "b"], unk_cutoff=1) + + self.assertEqual(v1, v2) + self.assertNotEqual(v1, v3) + self.assertNotEqual(v1, v4) + + def test_str(self): + self.assertEqual( + str(self.vocab), "" + ) + + def test_creation_with_counter(self): + self.assertEqual( + self.vocab, + Vocabulary( + Counter( + ["z", "a", "b", "c", "f", "d", "e", "g", "a", "d", "b", "e", "w"] + ), + unk_cutoff=2, + ), + ) + + @unittest.skip( + reason="Test is known to be flaky as it compares (runtime) performance." + ) + def test_len_is_constant(self): + # Given an obviously small and an obviously large vocabulary. + small_vocab = Vocabulary("abcde") + from nltk.corpus.europarl_raw import english + + large_vocab = Vocabulary(english.words()) + + # If we time calling `len` on them. + small_vocab_len_time = timeit("len(small_vocab)", globals=locals()) + large_vocab_len_time = timeit("len(large_vocab)", globals=locals()) + + # The timing should be the same order of magnitude. + self.assertAlmostEqual(small_vocab_len_time, large_vocab_len_time, places=1) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_aline.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_aline.py new file mode 100644 index 0000000..564dd2a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_aline.py @@ -0,0 +1,48 @@ +""" +Test Aline algorithm for aligning phonetic sequences +""" +from nltk.metrics import aline + + +def test_aline(): + result = aline.align("θin", "tenwis") + expected = [[("θ", "t"), ("i", "e"), ("n", "n")]] + + assert result == expected + + result = aline.align("jo", "ʒə") + expected = [[("j", "ʒ"), ("o", "ə")]] + + assert result == expected + + result = aline.align("pematesiweni", "pematesewen") + expected = [ + [ + ("p", "p"), + ("e", "e"), + ("m", "m"), + ("a", "a"), + ("t", "t"), + ("e", "e"), + ("s", "s"), + ("i", "e"), + ("w", "w"), + ("e", "e"), + ("n", "n"), + ] + ] + + assert result == expected + + result = aline.align("tuwθ", "dentis") + expected = [[("t", "t"), ("u", "i"), ("w", "-"), ("θ", "s")]] + + assert result == expected + + +def test_aline_delta(): + """ + Test aline for computing the difference between two segments + """ + assert aline.delta("p", "q") == 20.0 + assert aline.delta("a", "A") == 0.0 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_bllip.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_bllip.py new file mode 100644 index 0000000..09d992b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_bllip.py @@ -0,0 +1,42 @@ +import pytest + +from nltk.data import find +from nltk.parse.bllip import BllipParser +from nltk.tree import Tree + + +@pytest.fixture(scope="module") +def parser(): + model_dir = find("models/bllip_wsj_no_aux").path + return BllipParser.from_unified_model_dir(model_dir) + + +def setup_module(): + pytest.importorskip("bllipparser") + + +class TestBllipParser: + def test_parser_loads_a_valid_tree(self, parser): + parsed = parser.parse("I saw the man with the telescope") + tree = next(parsed) + + assert isinstance(tree, Tree) + assert ( + tree.pformat() + == """ +(S1 + (S + (NP (PRP I)) + (VP + (VBD saw) + (NP (DT the) (NN man)) + (PP (IN with) (NP (DT the) (NN telescope)))))) +""".strip() + ) + + def test_tagged_parse_finds_matching_element(self, parser): + parsed = parser.parse("I saw the man with the telescope") + tagged_tree = next(parser.tagged_parse([("telescope", "NN")])) + + assert isinstance(tagged_tree, Tree) + assert tagged_tree.pformat() == "(S1 (NP (NN telescope)))" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_brill.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_brill.py new file mode 100644 index 0000000..017a393 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_brill.py @@ -0,0 +1,34 @@ +""" +Tests for Brill tagger. +""" + +import unittest + +from nltk.corpus import treebank +from nltk.tag import UnigramTagger, brill, brill_trainer +from nltk.tbl import demo + + +class TestBrill(unittest.TestCase): + def test_pos_template(self): + train_sents = treebank.tagged_sents()[:1000] + tagger = UnigramTagger(train_sents) + trainer = brill_trainer.BrillTaggerTrainer( + tagger, [brill.Template(brill.Pos([-1]))] + ) + brill_tagger = trainer.train(train_sents) + # Example from https://github.com/nltk/nltk/issues/769 + result = brill_tagger.tag("This is a foo bar sentence".split()) + expected = [ + ("This", "DT"), + ("is", "VBZ"), + ("a", "DT"), + ("foo", None), + ("bar", "NN"), + ("sentence", None), + ] + self.assertEqual(result, expected) + + @unittest.skip("Should be tested in __main__ of nltk.tbl.demo") + def test_brill_demo(self): + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfd_mutation.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfd_mutation.py new file mode 100644 index 0000000..952e335 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfd_mutation.py @@ -0,0 +1,39 @@ +import unittest + +import pytest + +from nltk import ConditionalFreqDist, tokenize + + +class TestEmptyCondFreq(unittest.TestCase): + def test_tabulate(self): + empty = ConditionalFreqDist() + self.assertEqual(empty.conditions(), []) + with pytest.raises(ValueError): + empty.tabulate(conditions="BUG") # nonexistent keys shouldn't be added + self.assertEqual(empty.conditions(), []) + + def test_plot(self): + empty = ConditionalFreqDist() + self.assertEqual(empty.conditions(), []) + empty.plot(conditions=["BUG"]) # nonexistent keys shouldn't be added + self.assertEqual(empty.conditions(), []) + + def test_increment(self): + # make sure that we can still mutate cfd normally + text = "cow cat mouse cat tiger" + cfd = ConditionalFreqDist() + + # create cfd with word length as condition + for word in tokenize.word_tokenize(text): + condition = len(word) + cfd[condition][word] += 1 + + self.assertEqual(cfd.conditions(), [3, 5]) + + # incrementing previously unseen key is still possible + cfd[2]["hi"] += 1 + self.assertCountEqual(cfd.conditions(), [3, 5, 2]) # new condition added + self.assertEqual( + cfd[2]["hi"], 1 + ) # key's frequency incremented from 0 (unseen) to 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfg2chomsky.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfg2chomsky.py new file mode 100644 index 0000000..d8481ab --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_cfg2chomsky.py @@ -0,0 +1,49 @@ +import unittest + +import nltk +from nltk.grammar import CFG + + +class ChomskyNormalFormForCFGTest(unittest.TestCase): + def test_simple(self): + grammar = CFG.fromstring( + """ + S -> NP VP + PP -> P NP + NP -> Det N | NP PP P + VP -> V NP | VP PP + VP -> Det + Det -> 'a' | 'the' + N -> 'dog' | 'cat' + V -> 'chased' | 'sat' + P -> 'on' | 'in' + """ + ) + self.assertFalse(grammar.is_flexible_chomsky_normal_form()) + self.assertFalse(grammar.is_chomsky_normal_form()) + grammar = grammar.chomsky_normal_form(flexible=True) + self.assertTrue(grammar.is_flexible_chomsky_normal_form()) + self.assertFalse(grammar.is_chomsky_normal_form()) + + grammar2 = CFG.fromstring( + """ + S -> NP VP + NP -> VP N P + VP -> P + N -> 'dog' | 'cat' + P -> 'on' | 'in' + """ + ) + self.assertFalse(grammar2.is_flexible_chomsky_normal_form()) + self.assertFalse(grammar2.is_chomsky_normal_form()) + grammar2 = grammar2.chomsky_normal_form() + self.assertTrue(grammar2.is_flexible_chomsky_normal_form()) + self.assertTrue(grammar2.is_chomsky_normal_form()) + + def test_complex(self): + grammar = nltk.data.load("grammars/large_grammars/atis.cfg") + self.assertFalse(grammar.is_flexible_chomsky_normal_form()) + self.assertFalse(grammar.is_chomsky_normal_form()) + grammar = grammar.chomsky_normal_form(flexible=True) + self.assertTrue(grammar.is_flexible_chomsky_normal_form()) + self.assertFalse(grammar.is_chomsky_normal_form()) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_chunk.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_chunk.py new file mode 100644 index 0000000..75d5692 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_chunk.py @@ -0,0 +1,85 @@ +import unittest + +from nltk import RegexpParser + + +class TestChunkRule(unittest.TestCase): + def test_tag_pattern2re_pattern_quantifier(self): + """Test for bug https://github.com/nltk/nltk/issues/1597 + + Ensures that curly bracket quantifiers can be used inside a chunk rule. + This type of quantifier has been used for the supplementary example + in https://www.nltk.org/book/ch07.html#exploring-text-corpora. + """ + sent = [ + ("The", "AT"), + ("September-October", "NP"), + ("term", "NN"), + ("jury", "NN"), + ("had", "HVD"), + ("been", "BEN"), + ("charged", "VBN"), + ("by", "IN"), + ("Fulton", "NP-TL"), + ("Superior", "JJ-TL"), + ("Court", "NN-TL"), + ("Judge", "NN-TL"), + ("Durwood", "NP"), + ("Pye", "NP"), + ("to", "TO"), + ("investigate", "VB"), + ("reports", "NNS"), + ("of", "IN"), + ("possible", "JJ"), + ("``", "``"), + ("irregularities", "NNS"), + ("''", "''"), + ("in", "IN"), + ("the", "AT"), + ("hard-fought", "JJ"), + ("primary", "NN"), + ("which", "WDT"), + ("was", "BEDZ"), + ("won", "VBN"), + ("by", "IN"), + ("Mayor-nominate", "NN-TL"), + ("Ivan", "NP"), + ("Allen", "NP"), + ("Jr.", "NP"), + (".", "."), + ] # source: brown corpus + cp = RegexpParser("CHUNK: {{4,}}") + tree = cp.parse(sent) + assert ( + tree.pformat() + == """(S + The/AT + September-October/NP + term/NN + jury/NN + had/HVD + been/BEN + charged/VBN + by/IN + Fulton/NP-TL + Superior/JJ-TL + (CHUNK Court/NN-TL Judge/NN-TL Durwood/NP Pye/NP) + to/TO + investigate/VB + reports/NNS + of/IN + possible/JJ + ``/`` + irregularities/NNS + ''/'' + in/IN + the/AT + hard-fought/JJ + primary/NN + which/WDT + was/BEDZ + won/VBN + by/IN + (CHUNK Mayor-nominate/NN-TL Ivan/NP Allen/NP Jr./NP) + ./.)""" + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_classify.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_classify.py new file mode 100644 index 0000000..fa3c763 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_classify.py @@ -0,0 +1,49 @@ +""" +Unit tests for nltk.classify. See also: nltk/test/classify.doctest +""" +import pytest + +from nltk import classify + +TRAIN = [ + (dict(a=1, b=1, c=1), "y"), + (dict(a=1, b=1, c=1), "x"), + (dict(a=1, b=1, c=0), "y"), + (dict(a=0, b=1, c=1), "x"), + (dict(a=0, b=1, c=1), "y"), + (dict(a=0, b=0, c=1), "y"), + (dict(a=0, b=1, c=0), "x"), + (dict(a=0, b=0, c=0), "x"), + (dict(a=0, b=1, c=1), "y"), +] + +TEST = [ + (dict(a=1, b=0, c=1)), # unseen + (dict(a=1, b=0, c=0)), # unseen + (dict(a=0, b=1, c=1)), # seen 3 times, labels=y,y,x + (dict(a=0, b=1, c=0)), # seen 1 time, label=x +] + +RESULTS = [(0.16, 0.84), (0.46, 0.54), (0.41, 0.59), (0.76, 0.24)] + + +def assert_classifier_correct(algorithm): + try: + classifier = classify.MaxentClassifier.train( + TRAIN, algorithm, trace=0, max_iter=1000 + ) + except (LookupError, AttributeError) as e: + pytest.skip(str(e)) + + for (px, py), featureset in zip(RESULTS, TEST): + pdist = classifier.prob_classify(featureset) + assert abs(pdist.prob("x") - px) < 1e-2, (pdist.prob("x"), px) + assert abs(pdist.prob("y") - py) < 1e-2, (pdist.prob("y"), py) + + +def test_megam(): + assert_classifier_correct("MEGAM") + + +def test_tadm(): + assert_classifier_correct("TADM") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_collocations.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_collocations.py new file mode 100644 index 0000000..2aa94f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_collocations.py @@ -0,0 +1,120 @@ +from nltk.collocations import BigramCollocationFinder +from nltk.metrics import BigramAssocMeasures + +## Test bigram counters with discontinuous bigrams and repeated words + +_EPSILON = 1e-8 +SENT = "this this is is a a test test".split() + + +def close_enough(x, y): + """Verify that two sequences of n-gram association values are within + _EPSILON of each other. + """ + + return all(abs(x1[1] - y1[1]) <= _EPSILON for x1, y1 in zip(x, y)) + + +def test_bigram2(): + b = BigramCollocationFinder.from_words(SENT) + + assert sorted(b.ngram_fd.items()) == [ + (("a", "a"), 1), + (("a", "test"), 1), + (("is", "a"), 1), + (("is", "is"), 1), + (("test", "test"), 1), + (("this", "is"), 1), + (("this", "this"), 1), + ] + assert sorted(b.word_fd.items()) == [("a", 2), ("is", 2), ("test", 2), ("this", 2)] + + assert len(SENT) == sum(b.word_fd.values()) == sum(b.ngram_fd.values()) + 1 + assert close_enough( + sorted(b.score_ngrams(BigramAssocMeasures.pmi)), + [ + (("a", "a"), 1.0), + (("a", "test"), 1.0), + (("is", "a"), 1.0), + (("is", "is"), 1.0), + (("test", "test"), 1.0), + (("this", "is"), 1.0), + (("this", "this"), 1.0), + ], + ) + + +def test_bigram3(): + b = BigramCollocationFinder.from_words(SENT, window_size=3) + assert sorted(b.ngram_fd.items()) == sorted( + [ + (("a", "test"), 3), + (("is", "a"), 3), + (("this", "is"), 3), + (("a", "a"), 1), + (("is", "is"), 1), + (("test", "test"), 1), + (("this", "this"), 1), + ] + ) + + assert sorted(b.word_fd.items()) == sorted( + [("a", 2), ("is", 2), ("test", 2), ("this", 2)] + ) + + assert ( + len(SENT) == sum(b.word_fd.values()) == (sum(b.ngram_fd.values()) + 2 + 1) / 2.0 + ) + assert close_enough( + sorted(b.score_ngrams(BigramAssocMeasures.pmi)), + sorted( + [ + (("a", "test"), 1.584962500721156), + (("is", "a"), 1.584962500721156), + (("this", "is"), 1.584962500721156), + (("a", "a"), 0.0), + (("is", "is"), 0.0), + (("test", "test"), 0.0), + (("this", "this"), 0.0), + ] + ), + ) + + +def test_bigram5(): + b = BigramCollocationFinder.from_words(SENT, window_size=5) + assert sorted(b.ngram_fd.items()) == sorted( + [ + (("a", "test"), 4), + (("is", "a"), 4), + (("this", "is"), 4), + (("is", "test"), 3), + (("this", "a"), 3), + (("a", "a"), 1), + (("is", "is"), 1), + (("test", "test"), 1), + (("this", "this"), 1), + ] + ) + assert sorted(b.word_fd.items()) == sorted( + [("a", 2), ("is", 2), ("test", 2), ("this", 2)] + ) + n_word_fd = sum(b.word_fd.values()) + n_ngram_fd = (sum(b.ngram_fd.values()) + 4 + 3 + 2 + 1) / 4.0 + assert len(SENT) == n_word_fd == n_ngram_fd + assert close_enough( + sorted(b.score_ngrams(BigramAssocMeasures.pmi)), + sorted( + [ + (("a", "test"), 1.0), + (("is", "a"), 1.0), + (("this", "is"), 1.0), + (("is", "test"), 0.5849625007211562), + (("this", "a"), 0.5849625007211562), + (("a", "a"), -1.0), + (("is", "is"), -1.0), + (("test", "test"), -1.0), + (("this", "this"), -1.0), + ] + ), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_concordance.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_concordance.py new file mode 100644 index 0000000..23115bf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_concordance.py @@ -0,0 +1,98 @@ +import contextlib +import sys +import unittest +from io import StringIO + +from nltk.corpus import gutenberg +from nltk.text import Text + + +@contextlib.contextmanager +def stdout_redirect(where): + sys.stdout = where + try: + yield where + finally: + sys.stdout = sys.__stdout__ + + +class TestConcordance(unittest.TestCase): + """Text constructed using: https://www.nltk.org/book/ch01.html""" + + @classmethod + def setUpClass(cls): + cls.corpus = gutenberg.words("melville-moby_dick.txt") + + @classmethod + def tearDownClass(cls): + pass + + def setUp(self): + self.text = Text(TestConcordance.corpus) + self.query = "monstrous" + self.maxDiff = None + self.list_out = [ + "ong the former , one was of a most monstrous size . ... This came towards us , ", + 'ON OF THE PSALMS . " Touching that monstrous bulk of the whale or ork we have r', + "ll over with a heathenish array of monstrous clubs and spears . Some were thick", + "d as you gazed , and wondered what monstrous cannibal and savage could ever hav", + "that has survived the flood ; most monstrous and most mountainous ! That Himmal", + "they might scout at Moby Dick as a monstrous fable , or still worse and more de", + "th of Radney .'\" CHAPTER 55 Of the Monstrous Pictures of Whales . I shall ere l", + "ing Scenes . In connexion with the monstrous pictures of whales , I am strongly", + "ere to enter upon those still more monstrous stories of them which are to be fo", + "ght have been rummaged out of this monstrous cabinet there is no telling . But ", + "of Whale - Bones ; for Whales of a monstrous size are oftentimes cast up dead u", + ] + + def tearDown(self): + pass + + def test_concordance_list(self): + concordance_out = self.text.concordance_list(self.query) + self.assertEqual(self.list_out, [c.line for c in concordance_out]) + + def test_concordance_width(self): + list_out = [ + "monstrous", + "monstrous", + "monstrous", + "monstrous", + "monstrous", + "monstrous", + "Monstrous", + "monstrous", + "monstrous", + "monstrous", + "monstrous", + ] + + concordance_out = self.text.concordance_list(self.query, width=0) + self.assertEqual(list_out, [c.query for c in concordance_out]) + + def test_concordance_lines(self): + concordance_out = self.text.concordance_list(self.query, lines=3) + self.assertEqual(self.list_out[:3], [c.line for c in concordance_out]) + + def test_concordance_print(self): + print_out = """Displaying 11 of 11 matches: + ong the former , one was of a most monstrous size . ... This came towards us , + ON OF THE PSALMS . " Touching that monstrous bulk of the whale or ork we have r + ll over with a heathenish array of monstrous clubs and spears . Some were thick + d as you gazed , and wondered what monstrous cannibal and savage could ever hav + that has survived the flood ; most monstrous and most mountainous ! That Himmal + they might scout at Moby Dick as a monstrous fable , or still worse and more de + th of Radney .'" CHAPTER 55 Of the Monstrous Pictures of Whales . I shall ere l + ing Scenes . In connexion with the monstrous pictures of whales , I am strongly + ere to enter upon those still more monstrous stories of them which are to be fo + ght have been rummaged out of this monstrous cabinet there is no telling . But + of Whale - Bones ; for Whales of a monstrous size are oftentimes cast up dead u + """ + + with stdout_redirect(StringIO()) as stdout: + self.text.concordance(self.query) + + def strip_space(raw_str): + return raw_str.replace(" ", "") + + self.assertEqual(strip_space(print_out), strip_space(stdout.getvalue())) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corenlp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corenlp.py new file mode 100644 index 0000000..9b133d5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corenlp.py @@ -0,0 +1,1436 @@ +""" +Mock test for Stanford CoreNLP wrappers. +""" + +from unittest import TestCase +from unittest.mock import MagicMock + +import pytest + +from nltk.parse import corenlp +from nltk.tree import Tree + + +def setup_module(module): + global server + + try: + server = corenlp.CoreNLPServer(port=9000) + except LookupError: + pytest.skip("Could not instantiate CoreNLPServer.") + + try: + server.start() + except corenlp.CoreNLPServerError as e: + pytest.skip( + "Skipping CoreNLP tests because the server could not be started. " + "Make sure that the 9000 port is free. " + "{}".format(e.strerror) + ) + + +def teardown_module(module): + server.stop() + + +class TestTokenizerAPI(TestCase): + def test_tokenize(self): + corenlp_tokenizer = corenlp.CoreNLPParser() + + api_return_value = { + "sentences": [ + { + "index": 0, + "tokens": [ + { + "after": " ", + "before": "", + "characterOffsetBegin": 0, + "characterOffsetEnd": 4, + "index": 1, + "originalText": "Good", + "word": "Good", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 5, + "characterOffsetEnd": 12, + "index": 2, + "originalText": "muffins", + "word": "muffins", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 13, + "characterOffsetEnd": 17, + "index": 3, + "originalText": "cost", + "word": "cost", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 18, + "characterOffsetEnd": 19, + "index": 4, + "originalText": "$", + "word": "$", + }, + { + "after": "\n", + "before": "", + "characterOffsetBegin": 19, + "characterOffsetEnd": 23, + "index": 5, + "originalText": "3.88", + "word": "3.88", + }, + { + "after": " ", + "before": "\n", + "characterOffsetBegin": 24, + "characterOffsetEnd": 26, + "index": 6, + "originalText": "in", + "word": "in", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 27, + "characterOffsetEnd": 30, + "index": 7, + "originalText": "New", + "word": "New", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 31, + "characterOffsetEnd": 35, + "index": 8, + "originalText": "York", + "word": "York", + }, + { + "after": " ", + "before": "", + "characterOffsetBegin": 35, + "characterOffsetEnd": 36, + "index": 9, + "originalText": ".", + "word": ".", + }, + ], + }, + { + "index": 1, + "tokens": [ + { + "after": " ", + "before": " ", + "characterOffsetBegin": 38, + "characterOffsetEnd": 44, + "index": 1, + "originalText": "Please", + "word": "Please", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 45, + "characterOffsetEnd": 48, + "index": 2, + "originalText": "buy", + "word": "buy", + }, + { + "after": "\n", + "before": " ", + "characterOffsetBegin": 49, + "characterOffsetEnd": 51, + "index": 3, + "originalText": "me", + "word": "me", + }, + { + "after": " ", + "before": "\n", + "characterOffsetBegin": 52, + "characterOffsetEnd": 55, + "index": 4, + "originalText": "two", + "word": "two", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 56, + "characterOffsetEnd": 58, + "index": 5, + "originalText": "of", + "word": "of", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 59, + "characterOffsetEnd": 63, + "index": 6, + "originalText": "them", + "word": "them", + }, + { + "after": "\n", + "before": "", + "characterOffsetBegin": 63, + "characterOffsetEnd": 64, + "index": 7, + "originalText": ".", + "word": ".", + }, + ], + }, + { + "index": 2, + "tokens": [ + { + "after": "", + "before": "\n", + "characterOffsetBegin": 65, + "characterOffsetEnd": 71, + "index": 1, + "originalText": "Thanks", + "word": "Thanks", + }, + { + "after": "", + "before": "", + "characterOffsetBegin": 71, + "characterOffsetEnd": 72, + "index": 2, + "originalText": ".", + "word": ".", + }, + ], + }, + ] + } + corenlp_tokenizer.api_call = MagicMock(return_value=api_return_value) + + input_string = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks." + + expected_output = [ + "Good", + "muffins", + "cost", + "$", + "3.88", + "in", + "New", + "York", + ".", + "Please", + "buy", + "me", + "two", + "of", + "them", + ".", + "Thanks", + ".", + ] + + tokenized_output = list(corenlp_tokenizer.tokenize(input_string)) + + corenlp_tokenizer.api_call.assert_called_once_with( + "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks.", + properties={"annotators": "tokenize,ssplit"}, + ) + self.assertEqual(expected_output, tokenized_output) + + +class TestTaggerAPI(TestCase): + def test_pos_tagger(self): + corenlp_tagger = corenlp.CoreNLPParser(tagtype="pos") + + api_return_value = { + "sentences": [ + { + "basicDependencies": [ + { + "dep": "ROOT", + "dependent": 1, + "dependentGloss": "What", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "cop", + "dependent": 2, + "dependentGloss": "is", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "det", + "dependent": 3, + "dependentGloss": "the", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "airspeed", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "case", + "dependent": 5, + "dependentGloss": "of", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "det", + "dependent": 6, + "dependentGloss": "an", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "compound", + "dependent": 7, + "dependentGloss": "unladen", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "nmod", + "dependent": 8, + "dependentGloss": "swallow", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "punct", + "dependent": 9, + "dependentGloss": "?", + "governor": 1, + "governorGloss": "What", + }, + ], + "enhancedDependencies": [ + { + "dep": "ROOT", + "dependent": 1, + "dependentGloss": "What", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "cop", + "dependent": 2, + "dependentGloss": "is", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "det", + "dependent": 3, + "dependentGloss": "the", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "airspeed", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "case", + "dependent": 5, + "dependentGloss": "of", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "det", + "dependent": 6, + "dependentGloss": "an", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "compound", + "dependent": 7, + "dependentGloss": "unladen", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "nmod:of", + "dependent": 8, + "dependentGloss": "swallow", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "punct", + "dependent": 9, + "dependentGloss": "?", + "governor": 1, + "governorGloss": "What", + }, + ], + "enhancedPlusPlusDependencies": [ + { + "dep": "ROOT", + "dependent": 1, + "dependentGloss": "What", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "cop", + "dependent": 2, + "dependentGloss": "is", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "det", + "dependent": 3, + "dependentGloss": "the", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "airspeed", + "governor": 1, + "governorGloss": "What", + }, + { + "dep": "case", + "dependent": 5, + "dependentGloss": "of", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "det", + "dependent": 6, + "dependentGloss": "an", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "compound", + "dependent": 7, + "dependentGloss": "unladen", + "governor": 8, + "governorGloss": "swallow", + }, + { + "dep": "nmod:of", + "dependent": 8, + "dependentGloss": "swallow", + "governor": 4, + "governorGloss": "airspeed", + }, + { + "dep": "punct", + "dependent": 9, + "dependentGloss": "?", + "governor": 1, + "governorGloss": "What", + }, + ], + "index": 0, + "parse": "(ROOT\n (SBARQ\n (WHNP (WP What))\n (SQ (VBZ is)\n (NP\n (NP (DT the) (NN airspeed))\n (PP (IN of)\n (NP (DT an) (NN unladen) (NN swallow)))))\n (. ?)))", + "tokens": [ + { + "after": " ", + "before": "", + "characterOffsetBegin": 0, + "characterOffsetEnd": 4, + "index": 1, + "lemma": "what", + "originalText": "What", + "pos": "WP", + "word": "What", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 5, + "characterOffsetEnd": 7, + "index": 2, + "lemma": "be", + "originalText": "is", + "pos": "VBZ", + "word": "is", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 8, + "characterOffsetEnd": 11, + "index": 3, + "lemma": "the", + "originalText": "the", + "pos": "DT", + "word": "the", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 12, + "characterOffsetEnd": 20, + "index": 4, + "lemma": "airspeed", + "originalText": "airspeed", + "pos": "NN", + "word": "airspeed", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 21, + "characterOffsetEnd": 23, + "index": 5, + "lemma": "of", + "originalText": "of", + "pos": "IN", + "word": "of", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 24, + "characterOffsetEnd": 26, + "index": 6, + "lemma": "a", + "originalText": "an", + "pos": "DT", + "word": "an", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 27, + "characterOffsetEnd": 34, + "index": 7, + "lemma": "unladen", + "originalText": "unladen", + "pos": "JJ", + "word": "unladen", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 35, + "characterOffsetEnd": 42, + "index": 8, + "lemma": "swallow", + "originalText": "swallow", + "pos": "VB", + "word": "swallow", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 43, + "characterOffsetEnd": 44, + "index": 9, + "lemma": "?", + "originalText": "?", + "pos": ".", + "word": "?", + }, + ], + } + ] + } + corenlp_tagger.api_call = MagicMock(return_value=api_return_value) + + input_tokens = "What is the airspeed of an unladen swallow ?".split() + expected_output = [ + ("What", "WP"), + ("is", "VBZ"), + ("the", "DT"), + ("airspeed", "NN"), + ("of", "IN"), + ("an", "DT"), + ("unladen", "JJ"), + ("swallow", "VB"), + ("?", "."), + ] + tagged_output = corenlp_tagger.tag(input_tokens) + + corenlp_tagger.api_call.assert_called_once_with( + "What is the airspeed of an unladen swallow ?", + properties={ + "ssplit.isOneSentence": "true", + "annotators": "tokenize,ssplit,pos", + }, + ) + self.assertEqual(expected_output, tagged_output) + + def test_ner_tagger(self): + corenlp_tagger = corenlp.CoreNLPParser(tagtype="ner") + + api_return_value = { + "sentences": [ + { + "index": 0, + "tokens": [ + { + "after": " ", + "before": "", + "characterOffsetBegin": 0, + "characterOffsetEnd": 4, + "index": 1, + "lemma": "Rami", + "ner": "PERSON", + "originalText": "Rami", + "pos": "NNP", + "word": "Rami", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 5, + "characterOffsetEnd": 8, + "index": 2, + "lemma": "Eid", + "ner": "PERSON", + "originalText": "Eid", + "pos": "NNP", + "word": "Eid", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 9, + "characterOffsetEnd": 11, + "index": 3, + "lemma": "be", + "ner": "O", + "originalText": "is", + "pos": "VBZ", + "word": "is", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 12, + "characterOffsetEnd": 20, + "index": 4, + "lemma": "study", + "ner": "O", + "originalText": "studying", + "pos": "VBG", + "word": "studying", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 21, + "characterOffsetEnd": 23, + "index": 5, + "lemma": "at", + "ner": "O", + "originalText": "at", + "pos": "IN", + "word": "at", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 24, + "characterOffsetEnd": 29, + "index": 6, + "lemma": "Stony", + "ner": "ORGANIZATION", + "originalText": "Stony", + "pos": "NNP", + "word": "Stony", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 30, + "characterOffsetEnd": 35, + "index": 7, + "lemma": "Brook", + "ner": "ORGANIZATION", + "originalText": "Brook", + "pos": "NNP", + "word": "Brook", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 36, + "characterOffsetEnd": 46, + "index": 8, + "lemma": "University", + "ner": "ORGANIZATION", + "originalText": "University", + "pos": "NNP", + "word": "University", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 47, + "characterOffsetEnd": 49, + "index": 9, + "lemma": "in", + "ner": "O", + "originalText": "in", + "pos": "IN", + "word": "in", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 50, + "characterOffsetEnd": 52, + "index": 10, + "lemma": "NY", + "ner": "O", + "originalText": "NY", + "pos": "NNP", + "word": "NY", + }, + ], + } + ] + } + + corenlp_tagger.api_call = MagicMock(return_value=api_return_value) + + input_tokens = "Rami Eid is studying at Stony Brook University in NY".split() + expected_output = [ + ("Rami", "PERSON"), + ("Eid", "PERSON"), + ("is", "O"), + ("studying", "O"), + ("at", "O"), + ("Stony", "ORGANIZATION"), + ("Brook", "ORGANIZATION"), + ("University", "ORGANIZATION"), + ("in", "O"), + ("NY", "O"), + ] + tagged_output = corenlp_tagger.tag(input_tokens) + + corenlp_tagger.api_call.assert_called_once_with( + "Rami Eid is studying at Stony Brook University in NY", + properties={ + "ssplit.isOneSentence": "true", + "annotators": "tokenize,ssplit,ner", + }, + ) + self.assertEqual(expected_output, tagged_output) + + def test_unexpected_tagtype(self): + with self.assertRaises(ValueError): + corenlp_tagger = corenlp.CoreNLPParser(tagtype="test") + + +class TestParserAPI(TestCase): + def test_parse(self): + corenlp_parser = corenlp.CoreNLPParser() + + api_return_value = { + "sentences": [ + { + "basicDependencies": [ + { + "dep": "ROOT", + "dependent": 4, + "dependentGloss": "fox", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "dep", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "enhancedDependencies": [ + { + "dep": "ROOT", + "dependent": 4, + "dependentGloss": "fox", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "dep", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod:over", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "enhancedPlusPlusDependencies": [ + { + "dep": "ROOT", + "dependent": 4, + "dependentGloss": "fox", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "dep", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod:over", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "index": 0, + "parse": "(ROOT\n (NP\n (NP (DT The) (JJ quick) (JJ brown) (NN fox))\n (NP\n (NP (NNS jumps))\n (PP (IN over)\n (NP (DT the) (JJ lazy) (NN dog))))))", + "tokens": [ + { + "after": " ", + "before": "", + "characterOffsetBegin": 0, + "characterOffsetEnd": 3, + "index": 1, + "lemma": "the", + "originalText": "The", + "pos": "DT", + "word": "The", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 4, + "characterOffsetEnd": 9, + "index": 2, + "lemma": "quick", + "originalText": "quick", + "pos": "JJ", + "word": "quick", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 10, + "characterOffsetEnd": 15, + "index": 3, + "lemma": "brown", + "originalText": "brown", + "pos": "JJ", + "word": "brown", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 16, + "characterOffsetEnd": 19, + "index": 4, + "lemma": "fox", + "originalText": "fox", + "pos": "NN", + "word": "fox", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 20, + "characterOffsetEnd": 25, + "index": 5, + "lemma": "jump", + "originalText": "jumps", + "pos": "VBZ", + "word": "jumps", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 26, + "characterOffsetEnd": 30, + "index": 6, + "lemma": "over", + "originalText": "over", + "pos": "IN", + "word": "over", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 31, + "characterOffsetEnd": 34, + "index": 7, + "lemma": "the", + "originalText": "the", + "pos": "DT", + "word": "the", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 35, + "characterOffsetEnd": 39, + "index": 8, + "lemma": "lazy", + "originalText": "lazy", + "pos": "JJ", + "word": "lazy", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 40, + "characterOffsetEnd": 43, + "index": 9, + "lemma": "dog", + "originalText": "dog", + "pos": "NN", + "word": "dog", + }, + ], + } + ] + } + + corenlp_parser.api_call = MagicMock(return_value=api_return_value) + + input_string = "The quick brown fox jumps over the lazy dog".split() + expected_output = Tree( + "ROOT", + [ + Tree( + "NP", + [ + Tree( + "NP", + [ + Tree("DT", ["The"]), + Tree("JJ", ["quick"]), + Tree("JJ", ["brown"]), + Tree("NN", ["fox"]), + ], + ), + Tree( + "NP", + [ + Tree("NP", [Tree("NNS", ["jumps"])]), + Tree( + "PP", + [ + Tree("IN", ["over"]), + Tree( + "NP", + [ + Tree("DT", ["the"]), + Tree("JJ", ["lazy"]), + Tree("NN", ["dog"]), + ], + ), + ], + ), + ], + ), + ], + ) + ], + ) + + parsed_data = next(corenlp_parser.parse(input_string)) + + corenlp_parser.api_call.assert_called_once_with( + "The quick brown fox jumps over the lazy dog", + properties={"ssplit.eolonly": "true"}, + ) + self.assertEqual(expected_output, parsed_data) + + def test_dependency_parser(self): + corenlp_parser = corenlp.CoreNLPDependencyParser() + + api_return_value = { + "sentences": [ + { + "basicDependencies": [ + { + "dep": "ROOT", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "fox", + "governor": 5, + "governorGloss": "jumps", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "enhancedDependencies": [ + { + "dep": "ROOT", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "fox", + "governor": 5, + "governorGloss": "jumps", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod:over", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "enhancedPlusPlusDependencies": [ + { + "dep": "ROOT", + "dependent": 5, + "dependentGloss": "jumps", + "governor": 0, + "governorGloss": "ROOT", + }, + { + "dep": "det", + "dependent": 1, + "dependentGloss": "The", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 2, + "dependentGloss": "quick", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "amod", + "dependent": 3, + "dependentGloss": "brown", + "governor": 4, + "governorGloss": "fox", + }, + { + "dep": "nsubj", + "dependent": 4, + "dependentGloss": "fox", + "governor": 5, + "governorGloss": "jumps", + }, + { + "dep": "case", + "dependent": 6, + "dependentGloss": "over", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "det", + "dependent": 7, + "dependentGloss": "the", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "amod", + "dependent": 8, + "dependentGloss": "lazy", + "governor": 9, + "governorGloss": "dog", + }, + { + "dep": "nmod:over", + "dependent": 9, + "dependentGloss": "dog", + "governor": 5, + "governorGloss": "jumps", + }, + ], + "index": 0, + "tokens": [ + { + "after": " ", + "before": "", + "characterOffsetBegin": 0, + "characterOffsetEnd": 3, + "index": 1, + "lemma": "the", + "originalText": "The", + "pos": "DT", + "word": "The", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 4, + "characterOffsetEnd": 9, + "index": 2, + "lemma": "quick", + "originalText": "quick", + "pos": "JJ", + "word": "quick", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 10, + "characterOffsetEnd": 15, + "index": 3, + "lemma": "brown", + "originalText": "brown", + "pos": "JJ", + "word": "brown", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 16, + "characterOffsetEnd": 19, + "index": 4, + "lemma": "fox", + "originalText": "fox", + "pos": "NN", + "word": "fox", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 20, + "characterOffsetEnd": 25, + "index": 5, + "lemma": "jump", + "originalText": "jumps", + "pos": "VBZ", + "word": "jumps", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 26, + "characterOffsetEnd": 30, + "index": 6, + "lemma": "over", + "originalText": "over", + "pos": "IN", + "word": "over", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 31, + "characterOffsetEnd": 34, + "index": 7, + "lemma": "the", + "originalText": "the", + "pos": "DT", + "word": "the", + }, + { + "after": " ", + "before": " ", + "characterOffsetBegin": 35, + "characterOffsetEnd": 39, + "index": 8, + "lemma": "lazy", + "originalText": "lazy", + "pos": "JJ", + "word": "lazy", + }, + { + "after": "", + "before": " ", + "characterOffsetBegin": 40, + "characterOffsetEnd": 43, + "index": 9, + "lemma": "dog", + "originalText": "dog", + "pos": "NN", + "word": "dog", + }, + ], + } + ] + } + + corenlp_parser.api_call = MagicMock(return_value=api_return_value) + + input_string = "The quick brown fox jumps over the lazy dog".split() + expected_output = Tree( + "jumps", + [ + Tree("fox", ["The", "quick", "brown"]), + Tree("dog", ["over", "the", "lazy"]), + ], + ) + + parsed_data = next(corenlp_parser.parse(input_string)) + + corenlp_parser.api_call.assert_called_once_with( + "The quick brown fox jumps over the lazy dog", + properties={"ssplit.eolonly": "true"}, + ) + self.assertEqual(expected_output, parsed_data.tree()) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpora.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpora.py new file mode 100644 index 0000000..fd3a490 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpora.py @@ -0,0 +1,274 @@ +import unittest + +import pytest + +from nltk.corpus import ( # mwa_ppdb + cess_cat, + cess_esp, + conll2007, + floresta, + indian, + ptb, + sinica_treebank, + udhr, +) +from nltk.tree import Tree + + +class TestUdhr(unittest.TestCase): + def test_words(self): + for name in udhr.fileids(): + words = list(udhr.words(name)) + self.assertTrue(words) + + def test_raw_unicode(self): + for name in udhr.fileids(): + txt = udhr.raw(name) + assert not isinstance(txt, bytes), name + + def test_polish_encoding(self): + text_pl = udhr.raw("Polish-Latin2")[:164] + text_ppl = udhr.raw("Polish_Polski-Latin2")[:164] + expected = """POWSZECHNA DEKLARACJA PRAW CZŁOWIEKA +[Preamble] +Trzecia Sesja Ogólnego Zgromadzenia ONZ, obradująca w Paryżu, \ +uchwaliła 10 grudnia 1948 roku jednomyślnie Powszechną""" + assert text_pl == expected, "Polish-Latin2" + assert text_ppl == expected, "Polish_Polski-Latin2" + + +class TestIndian(unittest.TestCase): + def test_words(self): + words = indian.words()[:3] + self.assertEqual(words, ["মহিষের", "সন্তান", ":"]) + + def test_tagged_words(self): + tagged_words = indian.tagged_words()[:3] + self.assertEqual( + tagged_words, [("মহিষের", "NN"), ("সন্তান", "NN"), (":", "SYM")] + ) + + +class TestCess(unittest.TestCase): + def test_catalan(self): + words = cess_cat.words()[:15] + txt = "El Tribunal_Suprem -Fpa- TS -Fpt- ha confirmat la condemna a quatre anys d' inhabilitació especial" + self.assertEqual(words, txt.split()) + self.assertEqual(cess_cat.tagged_sents()[0][34][0], "càrrecs") + + def test_esp(self): + words = cess_esp.words()[:15] + txt = "El grupo estatal Electricité_de_France -Fpa- EDF -Fpt- anunció hoy , jueves , la compra del" + self.assertEqual(words, txt.split()) + self.assertEqual(cess_esp.words()[115], "años") + + +class TestFloresta(unittest.TestCase): + def test_words(self): + words = floresta.words()[:10] + txt = "Um revivalismo refrescante O 7_e_Meio é um ex-libris de a" + self.assertEqual(words, txt.split()) + + +class TestSinicaTreebank(unittest.TestCase): + def test_sents(self): + first_3_sents = sinica_treebank.sents()[:3] + self.assertEqual( + first_3_sents, [["一"], ["友情"], ["嘉珍", "和", "我", "住在", "同一條", "巷子"]] + ) + + def test_parsed_sents(self): + parsed_sents = sinica_treebank.parsed_sents()[25] + self.assertEqual( + parsed_sents, + Tree( + "S", + [ + Tree("NP", [Tree("Nba", ["嘉珍"])]), + Tree("V‧地", [Tree("VA11", ["不停"]), Tree("DE", ["的"])]), + Tree("VA4", ["哭泣"]), + ], + ), + ) + + +class TestCoNLL2007(unittest.TestCase): + # Reading the CoNLL 2007 Dependency Treebanks + + def test_sents(self): + sents = conll2007.sents("esp.train")[0] + self.assertEqual( + sents[:6], ["El", "aumento", "del", "índice", "de", "desempleo"] + ) + + def test_parsed_sents(self): + + parsed_sents = conll2007.parsed_sents("esp.train")[0] + + self.assertEqual( + parsed_sents.tree(), + Tree( + "fortaleció", + [ + Tree( + "aumento", + [ + "El", + Tree( + "del", + [ + Tree( + "índice", + [ + Tree( + "de", + [Tree("desempleo", ["estadounidense"])], + ) + ], + ) + ], + ), + ], + ), + "hoy", + "considerablemente", + Tree( + "al", + [ + Tree( + "euro", + [ + Tree( + "cotizaba", + [ + ",", + "que", + Tree("a", [Tree("15.35", ["las", "GMT"])]), + "se", + Tree( + "en", + [ + Tree( + "mercado", + [ + "el", + Tree("de", ["divisas"]), + Tree("de", ["Fráncfort"]), + ], + ) + ], + ), + Tree("a", ["0,9452_dólares"]), + Tree( + "frente_a", + [ + ",", + Tree( + "0,9349_dólares", + [ + "los", + Tree( + "de", + [ + Tree( + "mañana", + ["esta"], + ) + ], + ), + ], + ), + ], + ), + ], + ) + ], + ) + ], + ), + ".", + ], + ), + ) + + +@pytest.mark.skipif( + not ptb.fileids(), + reason="A full installation of the Penn Treebank is not available", +) +class TestPTB(unittest.TestCase): + def test_fileids(self): + self.assertEqual( + ptb.fileids()[:4], + [ + "BROWN/CF/CF01.MRG", + "BROWN/CF/CF02.MRG", + "BROWN/CF/CF03.MRG", + "BROWN/CF/CF04.MRG", + ], + ) + + def test_words(self): + self.assertEqual( + ptb.words("WSJ/00/WSJ_0003.MRG")[:7], + ["A", "form", "of", "asbestos", "once", "used", "*"], + ) + + def test_tagged_words(self): + self.assertEqual( + ptb.tagged_words("WSJ/00/WSJ_0003.MRG")[:3], + [("A", "DT"), ("form", "NN"), ("of", "IN")], + ) + + def test_categories(self): + self.assertEqual( + ptb.categories(), + [ + "adventure", + "belles_lettres", + "fiction", + "humor", + "lore", + "mystery", + "news", + "romance", + "science_fiction", + ], + ) + + def test_news_fileids(self): + self.assertEqual( + ptb.fileids("news")[:3], + ["WSJ/00/WSJ_0001.MRG", "WSJ/00/WSJ_0002.MRG", "WSJ/00/WSJ_0003.MRG"], + ) + + def test_category_words(self): + self.assertEqual( + ptb.words(categories=["humor", "fiction"])[:6], + ["Thirty-three", "Scotty", "did", "not", "go", "back"], + ) + + +@pytest.mark.skip("Skipping test for mwa_ppdb.") +class TestMWAPPDB(unittest.TestCase): + def test_fileids(self): + self.assertEqual( + mwa_ppdb.fileids(), ["ppdb-1.0-xxxl-lexical.extended.synonyms.uniquepairs"] + ) + + def test_entries(self): + self.assertEqual( + mwa_ppdb.entries()[:10], + [ + ("10/17/01", "17/10/2001"), + ("102,70", "102.70"), + ("13,53", "13.53"), + ("3.2.5.3.2.1", "3.2.5.3.2.1."), + ("53,76", "53.76"), + ("6.9.5", "6.9.5."), + ("7.7.6.3", "7.7.6.3."), + ("76,20", "76.20"), + ("79,85", "79.85"), + ("93,65", "93.65"), + ], + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpus_views.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpus_views.py new file mode 100644 index 0000000..e85da0f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_corpus_views.py @@ -0,0 +1,48 @@ +""" +Corpus View Regression Tests +""" +import unittest + +import nltk.data +from nltk.corpus.reader.util import ( + StreamBackedCorpusView, + read_line_block, + read_whitespace_block, +) + + +class TestCorpusViews(unittest.TestCase): + + linetok = nltk.LineTokenizer(blanklines="keep") + names = [ + "corpora/inaugural/README", # A very short file (160 chars) + "corpora/inaugural/1793-Washington.txt", # A relatively short file (791 chars) + "corpora/inaugural/1909-Taft.txt", # A longer file (32k chars) + ] + + def data(self): + for name in self.names: + f = nltk.data.find(name) + with f.open() as fp: + file_data = fp.read().decode("utf8") + yield f, file_data + + def test_correct_values(self): + # Check that corpus views produce the correct sequence of values. + + for f, file_data in self.data(): + v = StreamBackedCorpusView(f, read_whitespace_block) + self.assertEqual(list(v), file_data.split()) + + v = StreamBackedCorpusView(f, read_line_block) + self.assertEqual(list(v), self.linetok.tokenize(file_data)) + + def test_correct_length(self): + # Check that the corpus views report the correct lengths: + + for f, file_data in self.data(): + v = StreamBackedCorpusView(f, read_whitespace_block) + self.assertEqual(len(v), len(file_data.split())) + + v = StreamBackedCorpusView(f, read_line_block) + self.assertEqual(len(v), len(self.linetok.tokenize(file_data))) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_data.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_data.py new file mode 100644 index 0000000..913440e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_data.py @@ -0,0 +1,15 @@ +import pytest + +import nltk.data + + +def test_find_raises_exception(): + with pytest.raises(LookupError): + nltk.data.find("no_such_resource/foo") + + +def test_find_raises_exception_with_full_resource_name(): + no_such_thing = "no_such_thing/bar" + with pytest.raises(LookupError) as exc: + nltk.data.find(no_such_thing) + assert no_such_thing in str(exc) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_disagreement.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_disagreement.py new file mode 100644 index 0000000..9b3f6d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_disagreement.py @@ -0,0 +1,144 @@ +import unittest + +from nltk.metrics.agreement import AnnotationTask + + +class TestDisagreement(unittest.TestCase): + + """ + Class containing unit tests for nltk.metrics.agreement.Disagreement. + """ + + def test_easy(self): + """ + Simple test, based on + https://github.com/foolswood/krippendorffs_alpha/raw/master/krippendorff.pdf. + """ + data = [ + ("coder1", "dress1", "YES"), + ("coder2", "dress1", "NO"), + ("coder3", "dress1", "NO"), + ("coder1", "dress2", "YES"), + ("coder2", "dress2", "NO"), + ("coder3", "dress3", "NO"), + ] + annotation_task = AnnotationTask(data) + self.assertAlmostEqual(annotation_task.alpha(), -0.3333333) + + def test_easy2(self): + """ + Same simple test with 1 rating removed. + Removal of that rating should not matter: K-Apha ignores items with + only 1 rating. + """ + data = [ + ("coder1", "dress1", "YES"), + ("coder2", "dress1", "NO"), + ("coder3", "dress1", "NO"), + ("coder1", "dress2", "YES"), + ("coder2", "dress2", "NO"), + ] + annotation_task = AnnotationTask(data) + self.assertAlmostEqual(annotation_task.alpha(), -0.3333333) + + def test_advanced(self): + """ + More advanced test, based on + http://www.agreestat.com/research_papers/onkrippendorffalpha.pdf + """ + data = [ + ("A", "1", "1"), + ("B", "1", "1"), + ("D", "1", "1"), + ("A", "2", "2"), + ("B", "2", "2"), + ("C", "2", "3"), + ("D", "2", "2"), + ("A", "3", "3"), + ("B", "3", "3"), + ("C", "3", "3"), + ("D", "3", "3"), + ("A", "4", "3"), + ("B", "4", "3"), + ("C", "4", "3"), + ("D", "4", "3"), + ("A", "5", "2"), + ("B", "5", "2"), + ("C", "5", "2"), + ("D", "5", "2"), + ("A", "6", "1"), + ("B", "6", "2"), + ("C", "6", "3"), + ("D", "6", "4"), + ("A", "7", "4"), + ("B", "7", "4"), + ("C", "7", "4"), + ("D", "7", "4"), + ("A", "8", "1"), + ("B", "8", "1"), + ("C", "8", "2"), + ("D", "8", "1"), + ("A", "9", "2"), + ("B", "9", "2"), + ("C", "9", "2"), + ("D", "9", "2"), + ("B", "10", "5"), + ("C", "10", "5"), + ("D", "10", "5"), + ("C", "11", "1"), + ("D", "11", "1"), + ("C", "12", "3"), + ] + annotation_task = AnnotationTask(data) + self.assertAlmostEqual(annotation_task.alpha(), 0.743421052632) + + def test_advanced2(self): + """ + Same more advanced example, but with 1 rating removed. + Again, removal of that 1 rating should not matter. + """ + data = [ + ("A", "1", "1"), + ("B", "1", "1"), + ("D", "1", "1"), + ("A", "2", "2"), + ("B", "2", "2"), + ("C", "2", "3"), + ("D", "2", "2"), + ("A", "3", "3"), + ("B", "3", "3"), + ("C", "3", "3"), + ("D", "3", "3"), + ("A", "4", "3"), + ("B", "4", "3"), + ("C", "4", "3"), + ("D", "4", "3"), + ("A", "5", "2"), + ("B", "5", "2"), + ("C", "5", "2"), + ("D", "5", "2"), + ("A", "6", "1"), + ("B", "6", "2"), + ("C", "6", "3"), + ("D", "6", "4"), + ("A", "7", "4"), + ("B", "7", "4"), + ("C", "7", "4"), + ("D", "7", "4"), + ("A", "8", "1"), + ("B", "8", "1"), + ("C", "8", "2"), + ("D", "8", "1"), + ("A", "9", "2"), + ("B", "9", "2"), + ("C", "9", "2"), + ("D", "9", "2"), + ("B", "10", "5"), + ("C", "10", "5"), + ("D", "10", "5"), + ("C", "11", "1"), + ("D", "11", "1"), + ("C", "12", "3"), + ] + annotation_task = AnnotationTask(data) + self.assertAlmostEqual(annotation_task.alpha(), 0.743421052632) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_distance.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_distance.py new file mode 100644 index 0000000..96d814d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_distance.py @@ -0,0 +1,129 @@ +from typing import Tuple + +import pytest + +from nltk.metrics.distance import edit_distance + + +class TestEditDistance: + @pytest.mark.parametrize( + "left,right,substitution_cost,expecteds", + [ + # Allowing transpositions reduces the number of edits required. + # with transpositions: + # e.g. "abc" -T-> "cba" -D-> "ca": 2 steps + # + # without transpositions: + # e.g. "abc" -D-> "ab" -D-> "a" -I-> "ca": 3 steps + ("abc", "ca", 1, (2, 3)), + ("abc", "ca", 5, (2, 3)), # Doesn't *require* substitutions + # Note, a substition_cost of higher than 2 doesn't make much + # sense, as a deletion + insertion is identical, and always + # costs 2. + # + # + # Transpositions don't always reduce the number of edits required: + # with or without transpositions: + # e.g. "wants" -D-> "wats" -D-> "was" -I-> "wasp": 3 steps + ("wants", "wasp", 1, (3, 3)), + ("wants", "wasp", 5, (3, 3)), # Doesn't *require* substitutions + # + # + # Ought to have the same results with and without transpositions + # with or without transpositions: + # e.g. "rain" -S-> "sain" -S-> "shin" -I-> "shine": 3 steps + # (but cost 5 if substitution_cost=2) + ("rain", "shine", 1, (3, 3)), + ("rain", "shine", 2, (5, 5)), # Does *require* substitutions + # + # + # Several potentially interesting typos + # with transpositions: + # e.g. "acbdef" -T-> "abcdef": 1 step + # + # without transpositions: + # e.g. "acbdef" -D-> "abdef" -I-> "abcdef": 2 steps + ("acbdef", "abcdef", 1, (1, 2)), + ("acbdef", "abcdef", 2, (1, 2)), # Doesn't *require* substitutions + # + # + # with transpositions: + # e.g. "lnaguaeg" -T-> "languaeg" -T-> "language": 2 steps + # + # without transpositions: + # e.g. "lnaguaeg" -D-> "laguaeg" -I-> "languaeg" -D-> "languag" -I-> "language": 4 steps + ("lnaguaeg", "language", 1, (2, 4)), + ("lnaguaeg", "language", 2, (2, 4)), # Doesn't *require* substitutions + # + # + # with transpositions: + # e.g. "lnaugage" -T-> "lanugage" -T-> "language": 2 steps + # + # without transpositions: + # e.g. "lnaugage" -S-> "lnangage" -D-> "langage" -I-> "language": 3 steps + # (but one substitution, so a cost of 4 if substition_cost = 2) + ("lnaugage", "language", 1, (2, 3)), + ("lnaugage", "language", 2, (2, 4)), + # Does *require* substitutions if no transpositions + # + # + # with transpositions: + # e.g. "lngauage" -T-> "lnaguage" -T-> "language": 2 steps + # without transpositions: + # e.g. "lngauage" -I-> "lanaguage" -D-> "language": 2 steps + ("lngauage", "language", 1, (2, 2)), + ("lngauage", "language", 2, (2, 2)), # Doesn't *require* substitutions + # + # + # with or without transpositions: + # e.g. "wants" -S-> "sants" -S-> "swnts" -S-> "swits" -S-> "swims" -D-> "swim": 5 steps + # + # with substitution_cost=2 and transpositions: + # e.g. "wants" -T-> "santw" -D-> "sntw" -D-> "stw" -D-> "sw" + # -I-> "swi" -I-> "swim": 6 steps + # + # with substitution_cost=2 and no transpositions: + # e.g. "wants" -I-> "swants" -D-> "swant" -D-> "swan" -D-> "swa" -D-> "sw" + # -I-> "swi" -I-> "swim": 7 steps + ("wants", "swim", 1, (5, 5)), + ("wants", "swim", 2, (6, 7)), + # + # + # with or without transpositions: + # e.g. "kitten" -S-> "sitten" -s-> "sittin" -I-> "sitting": 3 steps + # (but cost 5 if substitution_cost=2) + ("kitten", "sitting", 1, (3, 3)), + ("kitten", "sitting", 2, (5, 5)), + # + # duplicated letter + # e.g. "duplicated" -D-> "duplicated" + ("duplicated", "duuplicated", 1, (1, 1)), + ("duplicated", "duuplicated", 2, (1, 1)), + ("very duplicated", "very duuplicateed", 2, (2, 2)), + ], + ) + def test_with_transpositions( + self, left: str, right: str, substitution_cost: int, expecteds: Tuple[int, int] + ): + """ + Test `edit_distance` between two strings, given some `substitution_cost`, + and whether transpositions are allowed. + + :param str left: First input string to `edit_distance`. + :param str right: Second input string to `edit_distance`. + :param int substitution_cost: The cost of a substitution action in `edit_distance`. + :param Tuple[int, int] expecteds: A tuple of expected outputs, such that `expecteds[0]` is + the expected output with `transpositions=True`, and `expecteds[1]` is + the expected output with `transpositions=False`. + """ + # Test the input strings in both orderings + for s1, s2 in ((left, right), (right, left)): + # zip with [True, False] to get the transpositions value + for expected, transpositions in zip(expecteds, [True, False]): + predicted = edit_distance( + s1, + s2, + substitution_cost=substitution_cost, + transpositions=transpositions, + ) + assert predicted == expected diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_downloader.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_downloader.py new file mode 100644 index 0000000..036b56b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_downloader.py @@ -0,0 +1,19 @@ +from nltk import download + + +def test_downloader_using_existing_parent_download_dir(tmp_path): + """Test that download works properly when the parent folder of the download_dir exists""" + + download_dir = str(tmp_path.joinpath("another_dir")) + download_status = download("mwa_ppdb", download_dir) + assert download_status is True + + +def test_downloader_using_non_existing_parent_download_dir(tmp_path): + """Test that download works properly when the parent folder of the download_dir does not exist""" + + download_dir = str( + tmp_path.joinpath("non-existing-parent-folder", "another-non-existing-folder") + ) + download_status = download("mwa_ppdb", download_dir) + assert download_status is True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_freqdist.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_freqdist.py new file mode 100644 index 0000000..d4393b2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_freqdist.py @@ -0,0 +1,7 @@ +import nltk + + +def test_iterating_returns_an_iterator_ordered_by_frequency(): + samples = ["one", "two", "two"] + distribution = nltk.FreqDist(samples) + assert list(distribution) == ["two", "one"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_hmm.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_hmm.py new file mode 100644 index 0000000..246078e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_hmm.py @@ -0,0 +1,82 @@ +import pytest + +from nltk.tag import hmm + + +def _wikipedia_example_hmm(): + # Example from wikipedia + # (https://en.wikipedia.org/wiki/Forward%E2%80%93backward_algorithm) + + states = ["rain", "no rain"] + symbols = ["umbrella", "no umbrella"] + + A = [[0.7, 0.3], [0.3, 0.7]] # transition probabilities + B = [[0.9, 0.1], [0.2, 0.8]] # emission probabilities + pi = [0.5, 0.5] # initial probabilities + + seq = ["umbrella", "umbrella", "no umbrella", "umbrella", "umbrella"] + seq = list(zip(seq, [None] * len(seq))) + + model = hmm._create_hmm_tagger(states, symbols, A, B, pi) + return model, states, symbols, seq + + +def test_forward_probability(): + from numpy.testing import assert_array_almost_equal + + # example from p. 385, Huang et al + model, states, symbols = hmm._market_hmm_example() + seq = [("up", None), ("up", None)] + expected = [[0.35, 0.02, 0.09], [0.1792, 0.0085, 0.0357]] + + fp = 2 ** model._forward_probability(seq) + + assert_array_almost_equal(fp, expected) + + +def test_forward_probability2(): + from numpy.testing import assert_array_almost_equal + + model, states, symbols, seq = _wikipedia_example_hmm() + fp = 2 ** model._forward_probability(seq) + + # examples in wikipedia are normalized + fp = (fp.T / fp.sum(axis=1)).T + + wikipedia_results = [ + [0.8182, 0.1818], + [0.8834, 0.1166], + [0.1907, 0.8093], + [0.7308, 0.2692], + [0.8673, 0.1327], + ] + + assert_array_almost_equal(wikipedia_results, fp, 4) + + +def test_backward_probability(): + from numpy.testing import assert_array_almost_equal + + model, states, symbols, seq = _wikipedia_example_hmm() + + bp = 2 ** model._backward_probability(seq) + # examples in wikipedia are normalized + + bp = (bp.T / bp.sum(axis=1)).T + + wikipedia_results = [ + # Forward-backward algorithm doesn't need b0_5, + # so .backward_probability doesn't compute it. + # [0.6469, 0.3531], + [0.5923, 0.4077], + [0.3763, 0.6237], + [0.6533, 0.3467], + [0.6273, 0.3727], + [0.5, 0.5], + ] + + assert_array_almost_equal(wikipedia_results, bp, 4) + + +def setup_module(module): + pytest.importorskip("numpy") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json2csv_corpus.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json2csv_corpus.py new file mode 100644 index 0000000..0ad19a8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json2csv_corpus.py @@ -0,0 +1,210 @@ +# Natural Language Toolkit: Twitter client +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + +""" +Regression tests for `json2csv()` and `json2csv_entities()` in Twitter +package. +""" +from pathlib import Path + +import pytest + +from nltk.corpus import twitter_samples +from nltk.twitter.common import json2csv, json2csv_entities + + +def files_are_identical(pathA, pathB): + """ + Compare two files, ignoring carriage returns, + leading whitespace, and trailing whitespace + """ + f1 = [l.strip() for l in pathA.read_bytes().splitlines()] + f2 = [l.strip() for l in pathB.read_bytes().splitlines()] + return f1 == f2 + + +subdir = Path(__file__).parent / "files" + + +@pytest.fixture +def infile(): + with open(twitter_samples.abspath("tweets.20150430-223406.json")) as infile: + return [next(infile) for x in range(100)] + + +def test_textoutput(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.text.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.text.csv" + json2csv(infile, outfn, ["text"], gzip_compress=False) + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_metadata(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.tweet.csv.ref" + fields = [ + "created_at", + "favorite_count", + "id", + "in_reply_to_status_id", + "in_reply_to_user_id", + "retweet_count", + "retweeted", + "text", + "truncated", + "user.id", + ] + + outfn = tmp_path / "tweets.20150430-223406.tweet.csv" + json2csv(infile, outfn, fields, gzip_compress=False) + assert files_are_identical(outfn, ref_fn) + + +def test_user_metadata(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.user.csv.ref" + fields = ["id", "text", "user.id", "user.followers_count", "user.friends_count"] + + outfn = tmp_path / "tweets.20150430-223406.user.csv" + json2csv(infile, outfn, fields, gzip_compress=False) + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_hashtag(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.hashtag.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.hashtag.csv" + json2csv_entities( + infile, + outfn, + ["id", "text"], + "hashtags", + ["text"], + gzip_compress=False, + ) + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_usermention(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.usermention.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.usermention.csv" + json2csv_entities( + infile, + outfn, + ["id", "text"], + "user_mentions", + ["id", "screen_name"], + gzip_compress=False, + ) + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_media(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.media.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.media.csv" + json2csv_entities( + infile, + outfn, + ["id"], + "media", + ["media_url", "url"], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_url(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.url.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.url.csv" + json2csv_entities( + infile, + outfn, + ["id"], + "urls", + ["url", "expanded_url"], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_userurl(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.userurl.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.userurl.csv" + json2csv_entities( + infile, + outfn, + ["id", "screen_name"], + "user.urls", + ["url", "expanded_url"], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_place(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.place.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.place.csv" + json2csv_entities( + infile, + outfn, + ["id", "text"], + "place", + ["name", "country"], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_tweet_place_boundingbox(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.placeboundingbox.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.placeboundingbox.csv" + json2csv_entities( + infile, + outfn, + ["id", "name"], + "place.bounding_box", + ["coordinates"], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_retweet_original_tweet(tmp_path, infile): + ref_fn = subdir / "tweets.20150430-223406.retweet.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.retweet.csv" + json2csv_entities( + infile, + outfn, + ["id"], + "retweeted_status", + [ + "created_at", + "favorite_count", + "id", + "in_reply_to_status_id", + "in_reply_to_user_id", + "retweet_count", + "text", + "truncated", + "user.id", + ], + gzip_compress=False, + ) + + assert files_are_identical(outfn, ref_fn) + + +def test_file_is_wrong(tmp_path, infile): + """ + Sanity check that file comparison is not giving false positives. + """ + ref_fn = subdir / "tweets.20150430-223406.retweet.csv.ref" + outfn = tmp_path / "tweets.20150430-223406.text.csv" + json2csv(infile, outfn, ["text"], gzip_compress=False) + assert not files_are_identical(outfn, ref_fn) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json_serialization.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json_serialization.py new file mode 100644 index 0000000..8ed1b42 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_json_serialization.py @@ -0,0 +1,95 @@ +import unittest + +from nltk.corpus import brown +from nltk.jsontags import JSONTaggedDecoder, JSONTaggedEncoder +from nltk.tag import ( + AffixTagger, + BigramTagger, + BrillTagger, + BrillTaggerTrainer, + DefaultTagger, + NgramTagger, + PerceptronTagger, + RegexpTagger, + TrigramTagger, + UnigramTagger, +) +from nltk.tag.brill import nltkdemo18 + + +class TestJSONSerialization(unittest.TestCase): + def setUp(self): + self.corpus = brown.tagged_sents()[:35] + self.decoder = JSONTaggedDecoder() + self.encoder = JSONTaggedEncoder() + self.default_tagger = DefaultTagger("NN") + + def test_default_tagger(self): + encoded = self.encoder.encode(self.default_tagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(repr(self.default_tagger), repr(decoded)) + self.assertEqual(self.default_tagger._tag, decoded._tag) + + def test_regexp_tagger(self): + tagger = RegexpTagger([(r".*", "NN")], backoff=self.default_tagger) + + encoded = self.encoder.encode(tagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(repr(tagger), repr(decoded)) + self.assertEqual(repr(tagger.backoff), repr(decoded.backoff)) + self.assertEqual(tagger._regexps, decoded._regexps) + + def test_affix_tagger(self): + tagger = AffixTagger(self.corpus, backoff=self.default_tagger) + + encoded = self.encoder.encode(tagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(repr(tagger), repr(decoded)) + self.assertEqual(repr(tagger.backoff), repr(decoded.backoff)) + self.assertEqual(tagger._affix_length, decoded._affix_length) + self.assertEqual(tagger._min_word_length, decoded._min_word_length) + self.assertEqual(tagger._context_to_tag, decoded._context_to_tag) + + def test_ngram_taggers(self): + unitagger = UnigramTagger(self.corpus, backoff=self.default_tagger) + bitagger = BigramTagger(self.corpus, backoff=unitagger) + tritagger = TrigramTagger(self.corpus, backoff=bitagger) + ntagger = NgramTagger(4, self.corpus, backoff=tritagger) + + encoded = self.encoder.encode(ntagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(repr(ntagger), repr(decoded)) + self.assertEqual(repr(tritagger), repr(decoded.backoff)) + self.assertEqual(repr(bitagger), repr(decoded.backoff.backoff)) + self.assertEqual(repr(unitagger), repr(decoded.backoff.backoff.backoff)) + self.assertEqual( + repr(self.default_tagger), repr(decoded.backoff.backoff.backoff.backoff) + ) + + def test_perceptron_tagger(self): + tagger = PerceptronTagger(load=False) + tagger.train(self.corpus) + + encoded = self.encoder.encode(tagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(tagger.model.weights, decoded.model.weights) + self.assertEqual(tagger.tagdict, decoded.tagdict) + self.assertEqual(tagger.classes, decoded.classes) + + def test_brill_tagger(self): + trainer = BrillTaggerTrainer( + self.default_tagger, nltkdemo18(), deterministic=True + ) + tagger = trainer.train(self.corpus, max_rules=30) + + encoded = self.encoder.encode(tagger) + decoded = self.decoder.decode(encoded) + + self.assertEqual(repr(tagger._initial_tagger), repr(decoded._initial_tagger)) + self.assertEqual(tagger._rules, decoded._rules) + self.assertEqual(tagger._training_stats, decoded._training_stats) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_metrics.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_metrics.py new file mode 100644 index 0000000..479e131 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_metrics.py @@ -0,0 +1,66 @@ +import unittest + +from nltk.metrics import ( + BigramAssocMeasures, + QuadgramAssocMeasures, + TrigramAssocMeasures, +) + +## Test the likelihood ratio metric + +_DELTA = 1e-8 + + +class TestLikelihoodRatio(unittest.TestCase): + def test_lr_bigram(self): + self.assertAlmostEqual( + BigramAssocMeasures.likelihood_ratio(2, (4, 4), 20), + 2.4142743368419755, + delta=_DELTA, + ) + self.assertAlmostEqual( + BigramAssocMeasures.likelihood_ratio(1, (1, 1), 1), 0.0, delta=_DELTA + ) + self.assertRaises( + ValueError, + BigramAssocMeasures.likelihood_ratio, + *(0, (2, 2), 2), + ) + + def test_lr_trigram(self): + self.assertAlmostEqual( + TrigramAssocMeasures.likelihood_ratio(1, (1, 1, 1), (1, 1, 1), 2), + 5.545177444479562, + delta=_DELTA, + ) + self.assertAlmostEqual( + TrigramAssocMeasures.likelihood_ratio(1, (1, 1, 1), (1, 1, 1), 1), + 0.0, + delta=_DELTA, + ) + self.assertRaises( + ValueError, + TrigramAssocMeasures.likelihood_ratio, + *(1, (1, 1, 2), (1, 1, 2), 2), + ) + + def test_lr_quadgram(self): + self.assertAlmostEqual( + QuadgramAssocMeasures.likelihood_ratio( + 1, (1, 1, 1, 1), (1, 1, 1, 1, 1, 1), (1, 1, 1, 1), 2 + ), + 8.317766166719343, + delta=_DELTA, + ) + self.assertAlmostEqual( + QuadgramAssocMeasures.likelihood_ratio( + 1, (1, 1, 1, 1), (1, 1, 1, 1, 1, 1), (1, 1, 1, 1), 1 + ), + 0.0, + delta=_DELTA, + ) + self.assertRaises( + ValueError, + QuadgramAssocMeasures.likelihood_ratio, + *(1, (1, 1, 1, 1), (1, 1, 1, 1, 1, 2), (1, 1, 1, 1), 1), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_naivebayes.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_naivebayes.py new file mode 100644 index 0000000..f107bed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_naivebayes.py @@ -0,0 +1,21 @@ +import unittest + +from nltk.classify.naivebayes import NaiveBayesClassifier + + +class NaiveBayesClassifierTest(unittest.TestCase): + def test_simple(self): + training_features = [ + ({"nice": True, "good": True}, "positive"), + ({"bad": True, "mean": True}, "negative"), + ] + + classifier = NaiveBayesClassifier.train(training_features) + + result = classifier.prob_classify({"nice": True}) + self.assertTrue(result.prob("positive") > result.prob("negative")) + self.assertEqual(result.max(), "positive") + + result = classifier.prob_classify({"bad": True}) + self.assertTrue(result.prob("positive") < result.prob("negative")) + self.assertEqual(result.max(), "negative") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_nombank.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_nombank.py new file mode 100644 index 0000000..1af7256 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_nombank.py @@ -0,0 +1,27 @@ +""" +Unit tests for nltk.corpus.nombank +""" + +import unittest + +from nltk.corpus import nombank + +# Load the nombank once. +nombank.nouns() + + +class NombankDemo(unittest.TestCase): + def test_numbers(self): + # No. of instances. + self.assertEqual(len(nombank.instances()), 114574) + # No. of rolesets + self.assertEqual(len(nombank.rolesets()), 5577) + # No. of nouns. + self.assertEqual(len(nombank.nouns()), 4704) + + def test_instance(self): + self.assertEqual(nombank.instances()[0].roleset, "perc-sign.01") + + def test_framefiles_fileids(self): + self.assertEqual(len(nombank.fileids()), 4705) + self.assertTrue(all(fileid.endswith(".xml") for fileid in nombank.fileids())) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pl196x.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pl196x.py new file mode 100644 index 0000000..8153553 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pl196x.py @@ -0,0 +1,13 @@ +import unittest + +import nltk +from nltk.corpus.reader import pl196x + + +class TestCorpusViews(unittest.TestCase): + def test_corpus_reader(self): + pl196x_dir = nltk.data.find("corpora/pl196x") + pl = pl196x.Pl196xCorpusReader( + pl196x_dir, r".*\.xml", textids="textids.txt", cat_file="cats.txt" + ) + pl.tagged_words(fileids=pl.fileids(), categories="cats.txt") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pos_tag.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pos_tag.py new file mode 100644 index 0000000..135eb10 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_pos_tag.py @@ -0,0 +1,83 @@ +""" +Tests for nltk.pos_tag +""" + + +import unittest + +from nltk import pos_tag, word_tokenize + + +class TestPosTag(unittest.TestCase): + def test_pos_tag_eng(self): + text = "John's big idea isn't all that bad." + expected_tagged = [ + ("John", "NNP"), + ("'s", "POS"), + ("big", "JJ"), + ("idea", "NN"), + ("is", "VBZ"), + ("n't", "RB"), + ("all", "PDT"), + ("that", "DT"), + ("bad", "JJ"), + (".", "."), + ] + assert pos_tag(word_tokenize(text)) == expected_tagged + + def test_pos_tag_eng_universal(self): + text = "John's big idea isn't all that bad." + expected_tagged = [ + ("John", "NOUN"), + ("'s", "PRT"), + ("big", "ADJ"), + ("idea", "NOUN"), + ("is", "VERB"), + ("n't", "ADV"), + ("all", "DET"), + ("that", "DET"), + ("bad", "ADJ"), + (".", "."), + ] + assert pos_tag(word_tokenize(text), tagset="universal") == expected_tagged + + def test_pos_tag_rus(self): + text = "Илья оторопел и дважды перечитал бумажку." + expected_tagged = [ + ("Илья", "S"), + ("оторопел", "V"), + ("и", "CONJ"), + ("дважды", "ADV"), + ("перечитал", "V"), + ("бумажку", "S"), + (".", "NONLEX"), + ] + assert pos_tag(word_tokenize(text), lang="rus") == expected_tagged + + def test_pos_tag_rus_universal(self): + text = "Илья оторопел и дважды перечитал бумажку." + expected_tagged = [ + ("Илья", "NOUN"), + ("оторопел", "VERB"), + ("и", "CONJ"), + ("дважды", "ADV"), + ("перечитал", "VERB"), + ("бумажку", "NOUN"), + (".", "."), + ] + assert ( + pos_tag(word_tokenize(text), tagset="universal", lang="rus") + == expected_tagged + ) + + def test_pos_tag_unknown_lang(self): + text = "모르겠 습니 다" + self.assertRaises(NotImplementedError, pos_tag, word_tokenize(text), lang="kor") + # Test for default kwarg, `lang=None` + self.assertRaises(NotImplementedError, pos_tag, word_tokenize(text), lang=None) + + def test_unspecified_lang(self): + # Tries to force the lang='eng' option. + text = "모르겠 습니 다" + expected_but_wrong = [("모르겠", "JJ"), ("습니", "NNP"), ("다", "NN")] + assert pos_tag(word_tokenize(text)) == expected_but_wrong diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_ribes.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_ribes.py new file mode 100644 index 0000000..7970b32 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_ribes.py @@ -0,0 +1,246 @@ +from nltk.translate.ribes_score import corpus_ribes, word_rank_alignment + + +def test_ribes_empty_worder(): # worder as in word order + # Verifies that these two sentences have no alignment, + # and hence have the lowest possible RIBES score. + hyp = "This is a nice sentence which I quite like".split() + ref = "Okay well that's neat and all but the reference's different".split() + + assert word_rank_alignment(ref, hyp) == [] + + list_of_refs = [[ref]] + hypotheses = [hyp] + assert corpus_ribes(list_of_refs, hypotheses) == 0.0 + + +def test_ribes_one_worder(): + # Verifies that these two sentences have just one match, + # and the RIBES score for this sentence with very little + # correspondence is 0. + hyp = "This is a nice sentence which I quite like".split() + ref = "Okay well that's nice and all but the reference's different".split() + + assert word_rank_alignment(ref, hyp) == [3] + + list_of_refs = [[ref]] + hypotheses = [hyp] + assert corpus_ribes(list_of_refs, hypotheses) == 0.0 + + +def test_ribes_two_worder(): + # Verifies that these two sentences have two matches, + # but still get the lowest possible RIBES score due + # to the lack of similarity. + hyp = "This is a nice sentence which I quite like".split() + ref = "Okay well that's nice and all but the reference is different".split() + + assert word_rank_alignment(ref, hyp) == [9, 3] + + list_of_refs = [[ref]] + hypotheses = [hyp] + assert corpus_ribes(list_of_refs, hypotheses) == 0.0 + + +def test_ribes(): + # Based on the doctest of the corpus_ribes function + hyp1 = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "which", + "ensures", + "that", + "the", + "military", + "always", + "obeys", + "the", + "commands", + "of", + "the", + "party", + ] + ref1a = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "that", + "ensures", + "that", + "the", + "military", + "will", + "forever", + "heed", + "Party", + "commands", + ] + ref1b = [ + "It", + "is", + "the", + "guiding", + "principle", + "which", + "guarantees", + "the", + "military", + "forces", + "always", + "being", + "under", + "the", + "command", + "of", + "the", + "Party", + ] + ref1c = [ + "It", + "is", + "the", + "practical", + "guide", + "for", + "the", + "army", + "always", + "to", + "heed", + "the", + "directions", + "of", + "the", + "party", + ] + + hyp2 = [ + "he", + "read", + "the", + "book", + "because", + "he", + "was", + "interested", + "in", + "world", + "history", + ] + ref2a = [ + "he", + "was", + "interested", + "in", + "world", + "history", + "because", + "he", + "read", + "the", + "book", + ] + + list_of_refs = [[ref1a, ref1b, ref1c], [ref2a]] + hypotheses = [hyp1, hyp2] + + score = corpus_ribes(list_of_refs, hypotheses) + + assert round(score, 4) == 0.3597 + + +def test_no_zero_div(): + # Regression test for Issue 2529, assure that no ZeroDivisionError is thrown. + hyp1 = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "which", + "ensures", + "that", + "the", + "military", + "always", + "obeys", + "the", + "commands", + "of", + "the", + "party", + ] + ref1a = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "that", + "ensures", + "that", + "the", + "military", + "will", + "forever", + "heed", + "Party", + "commands", + ] + ref1b = [ + "It", + "is", + "the", + "guiding", + "principle", + "which", + "guarantees", + "the", + "military", + "forces", + "always", + "being", + "under", + "the", + "command", + "of", + "the", + "Party", + ] + ref1c = [ + "It", + "is", + "the", + "practical", + "guide", + "for", + "the", + "army", + "always", + "to", + "heed", + "the", + "directions", + "of", + "the", + "party", + ] + + hyp2 = ["he", "read", "the"] + ref2a = ["he", "was", "interested", "in", "world", "history", "because", "he"] + + list_of_refs = [[ref1a, ref1b, ref1c], [ref2a]] + hypotheses = [hyp1, hyp2] + + score = corpus_ribes(list_of_refs, hypotheses) + + assert round(score, 4) == 0.1688 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_rte_classify.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_rte_classify.py new file mode 100644 index 0000000..0a573ea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_rte_classify.py @@ -0,0 +1,94 @@ +import pytest + +from nltk import config_megam +from nltk.classify.rte_classify import RTEFeatureExtractor, rte_classifier, rte_features +from nltk.corpus import rte as rte_corpus + +expected_from_rte_feature_extration = """ +alwayson => True +ne_hyp_extra => 0 +ne_overlap => 1 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 3 +word_overlap => 3 + +alwayson => True +ne_hyp_extra => 0 +ne_overlap => 1 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 2 +word_overlap => 1 + +alwayson => True +ne_hyp_extra => 1 +ne_overlap => 1 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 1 +word_overlap => 2 + +alwayson => True +ne_hyp_extra => 1 +ne_overlap => 0 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 6 +word_overlap => 2 + +alwayson => True +ne_hyp_extra => 1 +ne_overlap => 0 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 4 +word_overlap => 0 + +alwayson => True +ne_hyp_extra => 1 +ne_overlap => 0 +neg_hyp => 0 +neg_txt => 0 +word_hyp_extra => 3 +word_overlap => 1 +""" + + +class TestRTEClassifier: + # Test the feature extraction method. + def test_rte_feature_extraction(self): + pairs = rte_corpus.pairs(["rte1_dev.xml"])[:6] + test_output = [ + f"{key:<15} => {rte_features(pair)[key]}" + for pair in pairs + for key in sorted(rte_features(pair)) + ] + expected_output = expected_from_rte_feature_extration.strip().split("\n") + # Remove null strings. + expected_output = list(filter(None, expected_output)) + assert test_output == expected_output + + # Test the RTEFeatureExtractor object. + def test_feature_extractor_object(self): + rtepair = rte_corpus.pairs(["rte3_dev.xml"])[33] + extractor = RTEFeatureExtractor(rtepair) + + assert extractor.hyp_words == {"member", "China", "SCO."} + assert extractor.overlap("word") == set() + assert extractor.overlap("ne") == {"China"} + assert extractor.hyp_extra("word") == {"member"} + + # Test the RTE classifier training. + def test_rte_classification_without_megam(self): + # Use a sample size for unit testing, since we + # don't need to fully train these classifiers + clf = rte_classifier("IIS", sample_N=100) + clf = rte_classifier("GIS", sample_N=100) + + def test_rte_classification_with_megam(self): + try: + config_megam() + except (LookupError, AttributeError) as e: + pytest.skip("Skipping tests with dependencies on MEGAM") + clf = rte_classifier("megam", sample_N=100) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_seekable_unicode_stream_reader.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_seekable_unicode_stream_reader.py new file mode 100644 index 0000000..ab6356c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_seekable_unicode_stream_reader.py @@ -0,0 +1,86 @@ +import os +from io import BytesIO + +import pytest + +from nltk.corpus.reader import SeekableUnicodeStreamReader + + +def check_reader(unicode_string, encoding): + bytestr = unicode_string.encode(encoding) + stream = BytesIO(bytestr) + reader = SeekableUnicodeStreamReader(stream, encoding) + + # Should open at the start of the file + assert reader.tell() == 0 + + # Compare original string to contents from `.readlines()` + assert unicode_string == "".join(reader.readlines()) + + # Should be at the end of the file now + stream.seek(0, os.SEEK_END) + assert reader.tell() == stream.tell() + + reader.seek(0) # go back to start + + # Compare original string to contents from `.read()` + contents = "" + char = None + while char != "": + char = reader.read(1) + contents += char + assert unicode_string == contents + + +# Call `check_reader` with a variety of input strings and encodings. +ENCODINGS = ["ascii", "latin1", "greek", "hebrew", "utf-16", "utf-8"] + +STRINGS = [ + """ + This is a test file. + It is fairly short. + """, + "This file can be encoded with latin1. \x83", + """\ + This is a test file. + Here's a blank line: + + And here's some unicode: \xee \u0123 \uffe3 + """, + """\ + This is a test file. + Unicode characters: \xf3 \u2222 \u3333\u4444 \u5555 + """, + """\ + This is a larger file. It has some lines that are longer \ + than 72 characters. It's got lots of repetition. Here's \ + some unicode chars: \xee \u0123 \uffe3 \ueeee \u2345 + + How fun! Let's repeat it twenty times. + """ + * 20, +] + + +@pytest.mark.parametrize("string", STRINGS) +def test_reader(string): + for encoding in ENCODINGS: + # skip strings that can't be encoded with the current encoding + try: + string.encode(encoding) + except UnicodeEncodeError: + continue + check_reader(string, encoding) + + +def test_reader_stream_closes_when_deleted(): + reader = SeekableUnicodeStreamReader(BytesIO(b""), "ascii") + assert not reader.stream.closed + reader.__del__() + assert reader.stream.closed + + +def teardown_module(module=None): + import gc + + gc.collect() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_senna.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_senna.py new file mode 100644 index 0000000..60d2051 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_senna.py @@ -0,0 +1,112 @@ +""" +Unit tests for Senna +""" + +import unittest +from os import environ, path, sep + +from nltk.classify import Senna +from nltk.tag import SennaChunkTagger, SennaNERTagger, SennaTagger + +# Set Senna executable path for tests if it is not specified as an environment variable +if "SENNA" in environ: + SENNA_EXECUTABLE_PATH = path.normpath(environ["SENNA"]) + sep +else: + SENNA_EXECUTABLE_PATH = "/usr/share/senna-v3.0" + +senna_is_installed = path.exists(SENNA_EXECUTABLE_PATH) + + +@unittest.skipUnless(senna_is_installed, "Requires Senna executable") +class TestSennaPipeline(unittest.TestCase): + """Unittest for nltk.classify.senna""" + + def test_senna_pipeline(self): + """Senna pipeline interface""" + + pipeline = Senna(SENNA_EXECUTABLE_PATH, ["pos", "chk", "ner"]) + sent = "Dusseldorf is an international business center".split() + result = [ + (token["word"], token["chk"], token["ner"], token["pos"]) + for token in pipeline.tag(sent) + ] + expected = [ + ("Dusseldorf", "B-NP", "B-LOC", "NNP"), + ("is", "B-VP", "O", "VBZ"), + ("an", "B-NP", "O", "DT"), + ("international", "I-NP", "O", "JJ"), + ("business", "I-NP", "O", "NN"), + ("center", "I-NP", "O", "NN"), + ] + self.assertEqual(result, expected) + + +@unittest.skipUnless(senna_is_installed, "Requires Senna executable") +class TestSennaTagger(unittest.TestCase): + """Unittest for nltk.tag.senna""" + + def test_senna_tagger(self): + tagger = SennaTagger(SENNA_EXECUTABLE_PATH) + result = tagger.tag("What is the airspeed of an unladen swallow ?".split()) + expected = [ + ("What", "WP"), + ("is", "VBZ"), + ("the", "DT"), + ("airspeed", "NN"), + ("of", "IN"), + ("an", "DT"), + ("unladen", "NN"), + ("swallow", "NN"), + ("?", "."), + ] + self.assertEqual(result, expected) + + def test_senna_chunk_tagger(self): + chktagger = SennaChunkTagger(SENNA_EXECUTABLE_PATH) + result_1 = chktagger.tag("What is the airspeed of an unladen swallow ?".split()) + expected_1 = [ + ("What", "B-NP"), + ("is", "B-VP"), + ("the", "B-NP"), + ("airspeed", "I-NP"), + ("of", "B-PP"), + ("an", "B-NP"), + ("unladen", "I-NP"), + ("swallow", "I-NP"), + ("?", "O"), + ] + + result_2 = list(chktagger.bio_to_chunks(result_1, chunk_type="NP")) + expected_2 = [ + ("What", "0"), + ("the airspeed", "2-3"), + ("an unladen swallow", "5-6-7"), + ] + self.assertEqual(result_1, expected_1) + self.assertEqual(result_2, expected_2) + + def test_senna_ner_tagger(self): + nertagger = SennaNERTagger(SENNA_EXECUTABLE_PATH) + result_1 = nertagger.tag("Shakespeare theatre was in London .".split()) + expected_1 = [ + ("Shakespeare", "B-PER"), + ("theatre", "O"), + ("was", "O"), + ("in", "O"), + ("London", "B-LOC"), + (".", "O"), + ] + + result_2 = nertagger.tag("UN headquarters are in NY , USA .".split()) + expected_2 = [ + ("UN", "B-ORG"), + ("headquarters", "O"), + ("are", "O"), + ("in", "O"), + ("NY", "B-LOC"), + (",", "O"), + ("USA", "B-LOC"), + (".", "O"), + ] + self.assertEqual(result_1, expected_1) + self.assertEqual(result_2, expected_2) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_stem.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_stem.py new file mode 100644 index 0000000..fefadbc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_stem.py @@ -0,0 +1,157 @@ +import unittest +from contextlib import closing + +from nltk import data +from nltk.stem.porter import PorterStemmer +from nltk.stem.snowball import SnowballStemmer + + +class SnowballTest(unittest.TestCase): + def test_arabic(self): + """ + this unit testing for test the snowball arabic light stemmer + this stemmer deals with prefixes and suffixes + """ + # Test where the ignore_stopwords=True. + ar_stemmer = SnowballStemmer("arabic", True) + assert ar_stemmer.stem("الْعَرَبِــــــيَّة") == "عرب" + assert ar_stemmer.stem("العربية") == "عرب" + assert ar_stemmer.stem("فقالوا") == "قال" + assert ar_stemmer.stem("الطالبات") == "طالب" + assert ar_stemmer.stem("فالطالبات") == "طالب" + assert ar_stemmer.stem("والطالبات") == "طالب" + assert ar_stemmer.stem("الطالبون") == "طالب" + assert ar_stemmer.stem("اللذان") == "اللذان" + assert ar_stemmer.stem("من") == "من" + # Test where the ignore_stopwords=False. + ar_stemmer = SnowballStemmer("arabic", False) + assert ar_stemmer.stem("اللذان") == "اللذ" # this is a stop word + assert ar_stemmer.stem("الطالبات") == "طالب" + assert ar_stemmer.stem("الكلمات") == "كلم" + # test where create the arabic stemmer without given init value to ignore_stopwords + ar_stemmer = SnowballStemmer("arabic") + assert ar_stemmer.stem("الْعَرَبِــــــيَّة") == "عرب" + assert ar_stemmer.stem("العربية") == "عرب" + assert ar_stemmer.stem("فقالوا") == "قال" + assert ar_stemmer.stem("الطالبات") == "طالب" + assert ar_stemmer.stem("الكلمات") == "كلم" + + def test_russian(self): + stemmer_russian = SnowballStemmer("russian") + assert stemmer_russian.stem("авантненькая") == "авантненьк" + + def test_german(self): + stemmer_german = SnowballStemmer("german") + stemmer_german2 = SnowballStemmer("german", ignore_stopwords=True) + + assert stemmer_german.stem("Schr\xe4nke") == "schrank" + assert stemmer_german2.stem("Schr\xe4nke") == "schrank" + + assert stemmer_german.stem("keinen") == "kein" + assert stemmer_german2.stem("keinen") == "keinen" + + def test_spanish(self): + stemmer = SnowballStemmer("spanish") + + assert stemmer.stem("Visionado") == "vision" + + # The word 'algue' was raising an IndexError + assert stemmer.stem("algue") == "algu" + + def test_short_strings_bug(self): + stemmer = SnowballStemmer("english") + assert stemmer.stem("y's") == "y" + + +class PorterTest(unittest.TestCase): + def _vocabulary(self): + with closing( + data.find("stemmers/porter_test/porter_vocabulary.txt").open( + encoding="utf-8" + ) + ) as fp: + return fp.read().splitlines() + + def _test_against_expected_output(self, stemmer_mode, expected_stems): + stemmer = PorterStemmer(mode=stemmer_mode) + for word, true_stem in zip(self._vocabulary(), expected_stems): + our_stem = stemmer.stem(word) + assert ( + our_stem == true_stem + ), "{} should stem to {} in {} mode but got {}".format( + word, + true_stem, + stemmer_mode, + our_stem, + ) + + def test_vocabulary_martin_mode(self): + """Tests all words from the test vocabulary provided by M Porter + + The sample vocabulary and output were sourced from + https://tartarus.org/martin/PorterStemmer/voc.txt and + https://tartarus.org/martin/PorterStemmer/output.txt + and are linked to from the Porter Stemmer algorithm's homepage + at https://tartarus.org/martin/PorterStemmer/ + """ + with closing( + data.find("stemmers/porter_test/porter_martin_output.txt").open( + encoding="utf-8" + ) + ) as fp: + self._test_against_expected_output( + PorterStemmer.MARTIN_EXTENSIONS, fp.read().splitlines() + ) + + def test_vocabulary_nltk_mode(self): + with closing( + data.find("stemmers/porter_test/porter_nltk_output.txt").open( + encoding="utf-8" + ) + ) as fp: + self._test_against_expected_output( + PorterStemmer.NLTK_EXTENSIONS, fp.read().splitlines() + ) + + def test_vocabulary_original_mode(self): + # The list of stems for this test was generated by taking the + # Martin-blessed stemmer from + # https://tartarus.org/martin/PorterStemmer/c.txt + # and removing all the --DEPARTURE-- sections from it and + # running it against Martin's test vocabulary. + + with closing( + data.find("stemmers/porter_test/porter_original_output.txt").open( + encoding="utf-8" + ) + ) as fp: + self._test_against_expected_output( + PorterStemmer.ORIGINAL_ALGORITHM, fp.read().splitlines() + ) + + self._test_against_expected_output( + PorterStemmer.ORIGINAL_ALGORITHM, + data.find("stemmers/porter_test/porter_original_output.txt") + .open(encoding="utf-8") + .read() + .splitlines(), + ) + + def test_oed_bug(self): + """Test for bug https://github.com/nltk/nltk/issues/1581 + + Ensures that 'oed' can be stemmed without throwing an error. + """ + assert PorterStemmer().stem("oed") == "o" + + def test_lowercase_option(self): + """Test for improvement on https://github.com/nltk/nltk/issues/2507 + + Ensures that stems are lowercased when `to_lowercase=True` + """ + porter = PorterStemmer() + assert porter.stem("On") == "on" + assert porter.stem("I") == "i" + assert porter.stem("I", to_lowercase=False) == "I" + assert porter.stem("Github") == "github" + assert porter.stem("Github", to_lowercase=False) == "Github" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tag.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tag.py new file mode 100644 index 0000000..6be90e8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tag.py @@ -0,0 +1,23 @@ +def test_basic(): + from nltk.tag import pos_tag + from nltk.tokenize import word_tokenize + + result = pos_tag(word_tokenize("John's big idea isn't all that bad.")) + assert result == [ + ("John", "NNP"), + ("'s", "POS"), + ("big", "JJ"), + ("idea", "NN"), + ("is", "VBZ"), + ("n't", "RB"), + ("all", "PDT"), + ("that", "DT"), + ("bad", "JJ"), + (".", "."), + ] + + +def setup_module(module): + import pytest + + pytest.importorskip("numpy") diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tgrep.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tgrep.py new file mode 100644 index 0000000..8c4204d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tgrep.py @@ -0,0 +1,780 @@ +#!/usr/bin/env python +# +# Natural Language Toolkit: TGrep search +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Will Roberts +# URL: +# For license information, see LICENSE.TXT + +""" +Unit tests for nltk.tgrep. +""" + + +import unittest + +from nltk import tgrep +from nltk.tree import ParentedTree + + +class TestSequenceFunctions(unittest.TestCase): + + """ + Class containing unit tests for nltk.tgrep. + """ + + def test_tokenize_simple(self): + """ + Simple test of tokenization. + """ + tokens = tgrep.tgrep_tokenize("A .. (B !< C . D) | ![<< (E , F) $ G]") + self.assertEqual( + tokens, + [ + "A", + "..", + "(", + "B", + "!", + "<", + "C", + ".", + "D", + ")", + "|", + "!", + "[", + "<<", + "(", + "E", + ",", + "F", + ")", + "$", + "G", + "]", + ], + ) + + def test_tokenize_encoding(self): + """ + Test that tokenization handles bytes and strs the same way. + """ + self.assertEqual( + tgrep.tgrep_tokenize(b"A .. (B !< C . D) | ![<< (E , F) $ G]"), + tgrep.tgrep_tokenize("A .. (B !< C . D) | ![<< (E , F) $ G]"), + ) + + def test_tokenize_link_types(self): + """ + Test tokenization of basic link types. + """ + self.assertEqual(tgrep.tgrep_tokenize("AB"), ["A", ">", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<3B"), ["A", "<3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>3B"), ["A", ">3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<,B"), ["A", "<,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>,B"), ["A", ">,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<-3B"), ["A", "<-3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>-3B"), ["A", ">-3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<-B"), ["A", "<-", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>-B"), ["A", ">-", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<'B"), ["A", "<'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>'B"), ["A", ">'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<:B"), ["A", "<:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>:B"), ["A", ">:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<>B"), ["A", ">>", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<<,B"), ["A", "<<,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>>,B"), ["A", ">>,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<<'B"), ["A", "<<'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>>'B"), ["A", ">>'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A<<:B"), ["A", "<<:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A>>:B"), ["A", ">>:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A.B"), ["A", ".", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A,B"), ["A", ",", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A..B"), ["A", "..", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A,,B"), ["A", ",,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A$B"), ["A", "$", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A$.B"), ["A", "$.", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A$,B"), ["A", "$,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A$..B"), ["A", "$..", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A$,,B"), ["A", "$,,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!B"), ["A", "!", ">", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<3B"), ["A", "!", "<3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>3B"), ["A", "!", ">3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<,B"), ["A", "!", "<,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>,B"), ["A", "!", ">,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<-3B"), ["A", "!", "<-3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>-3B"), ["A", "!", ">-3", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<-B"), ["A", "!", "<-", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>-B"), ["A", "!", ">-", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<'B"), ["A", "!", "<'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>'B"), ["A", "!", ">'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<:B"), ["A", "!", "<:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>:B"), ["A", "!", ">:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<>B"), ["A", "!", ">>", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<<,B"), ["A", "!", "<<,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>>,B"), ["A", "!", ">>,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<<'B"), ["A", "!", "<<'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>>'B"), ["A", "!", ">>'", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!<<:B"), ["A", "!", "<<:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!>>:B"), ["A", "!", ">>:", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!.B"), ["A", "!", ".", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!,B"), ["A", "!", ",", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!..B"), ["A", "!", "..", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!,,B"), ["A", "!", ",,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!$B"), ["A", "!", "$", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!$.B"), ["A", "!", "$.", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!$,B"), ["A", "!", "$,", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!$..B"), ["A", "!", "$..", "B"]) + self.assertEqual(tgrep.tgrep_tokenize("A!$,,B"), ["A", "!", "$,,", "B"]) + + def test_tokenize_examples(self): + """ + Test tokenization of the TGrep2 manual example patterns. + """ + self.assertEqual(tgrep.tgrep_tokenize("NP < PP"), ["NP", "<", "PP"]) + self.assertEqual(tgrep.tgrep_tokenize("/^NP/"), ["/^NP/"]) + self.assertEqual( + tgrep.tgrep_tokenize("NP << PP . VP"), ["NP", "<<", "PP", ".", "VP"] + ) + self.assertEqual( + tgrep.tgrep_tokenize("NP << PP | . VP"), ["NP", "<<", "PP", "|", ".", "VP"] + ) + self.assertEqual( + tgrep.tgrep_tokenize("NP !<< PP [> NP | >> VP]"), + ["NP", "!", "<<", "PP", "[", ">", "NP", "|", ">>", "VP", "]"], + ) + self.assertEqual( + tgrep.tgrep_tokenize("NP << (PP . VP)"), + ["NP", "<<", "(", "PP", ".", "VP", ")"], + ) + self.assertEqual( + tgrep.tgrep_tokenize("NP <' (PP <, (IN < on))"), + ["NP", "<'", "(", "PP", "<,", "(", "IN", "<", "on", ")", ")"], + ) + self.assertEqual( + tgrep.tgrep_tokenize("S < (A < B) < C"), + ["S", "<", "(", "A", "<", "B", ")", "<", "C"], + ) + self.assertEqual( + tgrep.tgrep_tokenize("S < ((A < B) < C)"), + ["S", "<", "(", "(", "A", "<", "B", ")", "<", "C", ")"], + ) + self.assertEqual( + tgrep.tgrep_tokenize("S < (A < B < C)"), + ["S", "<", "(", "A", "<", "B", "<", "C", ")"], + ) + self.assertEqual(tgrep.tgrep_tokenize("A3B"3B"', "<", "C"], + ) + + def test_tokenize_nodenames(self): + """ + Test tokenization of node names. + """ + self.assertEqual(tgrep.tgrep_tokenize("Robert"), ["Robert"]) + self.assertEqual(tgrep.tgrep_tokenize("/^[Bb]ob/"), ["/^[Bb]ob/"]) + self.assertEqual(tgrep.tgrep_tokenize("*"), ["*"]) + self.assertEqual(tgrep.tgrep_tokenize("__"), ["__"]) + # test tokenization of NLTK tree position syntax + self.assertEqual(tgrep.tgrep_tokenize("N()"), ["N(", ")"]) + self.assertEqual(tgrep.tgrep_tokenize("N(0,)"), ["N(", "0", ",", ")"]) + self.assertEqual(tgrep.tgrep_tokenize("N(0,0)"), ["N(", "0", ",", "0", ")"]) + self.assertEqual( + tgrep.tgrep_tokenize("N(0,0,)"), ["N(", "0", ",", "0", ",", ")"] + ) + + def test_tokenize_macros(self): + """ + Test tokenization of macro definitions. + """ + self.assertEqual( + tgrep.tgrep_tokenize( + "@ NP /^NP/;\n@ NN /^NN/;\n@NP [!< NP | < @NN] !$.. @NN" + ), + [ + "@", + "NP", + "/^NP/", + ";", + "@", + "NN", + "/^NN/", + ";", + "@NP", + "[", + "!", + "<", + "NP", + "|", + "<", + "@NN", + "]", + "!", + "$..", + "@NN", + ], + ) + + def test_node_simple(self): + """ + Test a simple use of tgrep for finding nodes matching a given + pattern. + """ + tree = ParentedTree.fromstring( + "(S (NP (DT the) (JJ big) (NN dog)) " "(VP bit) (NP (DT a) (NN cat)))" + ) + self.assertEqual(list(tgrep.tgrep_positions("NN", [tree])), [[(0, 2), (2, 1)]]) + self.assertEqual( + list(tgrep.tgrep_nodes("NN", [tree])), [[tree[0, 2], tree[2, 1]]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("NN|JJ", [tree])), [[(0, 1), (0, 2), (2, 1)]] + ) + + def test_node_printing(self): + """Test that the tgrep print operator ' is properly ignored.""" + tree = ParentedTree.fromstring("(S (n x) (N x))") + self.assertEqual( + list(tgrep.tgrep_positions("N", [tree])), + list(tgrep.tgrep_positions("'N", [tree])), + ) + self.assertEqual( + list(tgrep.tgrep_positions("/[Nn]/", [tree])), + list(tgrep.tgrep_positions("'/[Nn]/", [tree])), + ) + + def test_node_encoding(self): + """ + Test that tgrep search strings handles bytes and strs the same + way. + """ + tree = ParentedTree.fromstring( + "(S (NP (DT the) (JJ big) (NN dog)) " "(VP bit) (NP (DT a) (NN cat)))" + ) + self.assertEqual( + list(tgrep.tgrep_positions(b"NN", [tree])), + list(tgrep.tgrep_positions(b"NN", [tree])), + ) + self.assertEqual( + list(tgrep.tgrep_nodes(b"NN", [tree])), + list(tgrep.tgrep_nodes("NN", [tree])), + ) + self.assertEqual( + list(tgrep.tgrep_positions(b"NN|JJ", [tree])), + list(tgrep.tgrep_positions("NN|JJ", [tree])), + ) + + def test_node_nocase(self): + """ + Test selecting nodes using case insensitive node names. + """ + tree = ParentedTree.fromstring("(S (n x) (N x))") + self.assertEqual(list(tgrep.tgrep_positions('"N"', [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions('i@"N"', [tree])), [[(0,), (1,)]]) + + def test_node_quoted(self): + """ + Test selecting nodes using quoted node names. + """ + tree = ParentedTree.fromstring('(N ("N" x) (N" x) ("\\" x))') + self.assertEqual(list(tgrep.tgrep_positions('"N"', [tree])), [[()]]) + self.assertEqual(list(tgrep.tgrep_positions('"\\"N\\""', [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions('"N\\""', [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions('"\\"\\\\\\""', [tree])), [[(2,)]]) + + def test_node_regex(self): + """ + Test regex matching on nodes. + """ + tree = ParentedTree.fromstring("(S (NP-SBJ x) (NP x) (NNP x) (VP x))") + # This is a regular expression that matches any node whose + # name starts with NP, including NP-SBJ: + self.assertEqual(list(tgrep.tgrep_positions("/^NP/", [tree])), [[(0,), (1,)]]) + + def test_node_regex_2(self): + """ + Test regex matching on nodes. + """ + tree = ParentedTree.fromstring("(S (SBJ x) (SBJ1 x) (NP-SBJ x))") + self.assertEqual(list(tgrep.tgrep_positions("/^SBJ/", [tree])), [[(0,), (1,)]]) + # This is a regular expression that matches any node whose + # name includes SBJ, including NP-SBJ: + self.assertEqual( + list(tgrep.tgrep_positions("/SBJ/", [tree])), [[(0,), (1,), (2,)]] + ) + + def test_node_tree_position(self): + """ + Test matching on nodes based on NLTK tree position. + """ + tree = ParentedTree.fromstring("(S (NP-SBJ x) (NP x) (NNP x) (VP x))") + # test all tree positions that are not leaves + leaf_positions = {tree.leaf_treeposition(x) for x in range(len(tree.leaves()))} + tree_positions = [x for x in tree.treepositions() if x not in leaf_positions] + for position in tree_positions: + node_id = f"N{position}" + tgrep_positions = list(tgrep.tgrep_positions(node_id, [tree])) + self.assertEqual(len(tgrep_positions[0]), 1) + self.assertEqual(tgrep_positions[0][0], position) + + def test_node_noleaves(self): + """ + Test node name matching with the search_leaves flag set to False. + """ + tree = ParentedTree.fromstring("(S (A (T x)) (B (N x)))") + self.assertEqual( + list(tgrep.tgrep_positions("x", [tree])), [[(0, 0, 0), (1, 0, 0)]] + ) + self.assertEqual(list(tgrep.tgrep_positions("x", [tree], False)), [[]]) + + def tests_rel_dominance(self): + """ + Test matching nodes based on dominance relations. + """ + tree = ParentedTree.fromstring("(S (A (T x)) (B (N x)))") + self.assertEqual(list(tgrep.tgrep_positions("* < T", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* < T > S", [tree])), [[(0,)]]) + self.assertEqual( + list(tgrep.tgrep_positions("* !< T", [tree])), + [[(), (0, 0), (0, 0, 0), (1,), (1, 0), (1, 0, 0)]], + ) + self.assertEqual(list(tgrep.tgrep_positions("* !< T > S", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* > A", [tree])), [[(0, 0)]]) + self.assertEqual(list(tgrep.tgrep_positions("* > B", [tree])), [[(1, 0)]]) + self.assertEqual( + list(tgrep.tgrep_positions("* !> B", [tree])), + [[(), (0,), (0, 0), (0, 0, 0), (1,), (1, 0, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* !> B >> S", [tree])), [[(0,), (0, 0), (1,)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("* >> S", [tree])), + [[(0,), (0, 0), (1,), (1, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* >>, S", [tree])), [[(0,), (0, 0)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("* >>' S", [tree])), [[(1,), (1, 0)]] + ) + # Known issue: + # self.assertEqual(list(tgrep.tgrep_positions('* !>> S', [tree])), + # [[()]]) + self.assertEqual(list(tgrep.tgrep_positions("* << T", [tree])), [[(), (0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <<' T", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <<1 N", [tree])), [[(1,)]]) + self.assertEqual( + list(tgrep.tgrep_positions("* !<< T", [tree])), + [[(0, 0), (0, 0, 0), (1,), (1, 0), (1, 0, 0)]], + ) + tree = ParentedTree.fromstring("(S (A (T x)) (B (T x) (N x )))") + self.assertEqual(list(tgrep.tgrep_positions("* <: T", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* < T", [tree])), [[(0,), (1,)]]) + self.assertEqual( + list(tgrep.tgrep_positions("* !<: T", [tree])), + [[(), (0, 0), (0, 0, 0), (1,), (1, 0), (1, 0, 0), (1, 1), (1, 1, 0)]], + ) + self.assertEqual(list(tgrep.tgrep_positions("* !<: T > S", [tree])), [[(1,)]]) + tree = ParentedTree.fromstring("(S (T (A x) (B x)) (T (C x)))") + self.assertEqual(list(tgrep.tgrep_positions("* >: T", [tree])), [[(1, 0)]]) + self.assertEqual( + list(tgrep.tgrep_positions("* !>: T", [tree])), + [[(), (0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1,), (1, 0, 0)]], + ) + tree = ParentedTree.fromstring( + "(S (A (B (C (D (E (T x))))))" " (A (B (C (D (E (T x))) (N x)))))" + ) + self.assertEqual( + list(tgrep.tgrep_positions("* <<: T", [tree])), + [ + [ + (0,), + (0, 0), + (0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0, 0), + (1, 0, 0, 0), + (1, 0, 0, 0, 0), + ] + ], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* >>: A", [tree])), + [ + [ + (0, 0), + (0, 0, 0), + (0, 0, 0, 0), + (0, 0, 0, 0, 0), + (0, 0, 0, 0, 0, 0), + (1, 0), + (1, 0, 0), + ] + ], + ) + + def test_bad_operator(self): + """ + Test error handling of undefined tgrep operators. + """ + tree = ParentedTree.fromstring("(S (A (T x)) (B (N x)))") + self.assertRaises( + tgrep.TgrepException, list, tgrep.tgrep_positions("* >>> S", [tree]) + ) + + def test_comments(self): + """ + Test that comments are correctly filtered out of tgrep search + strings. + """ + tree = ParentedTree.fromstring("(S (NN x) (NP x) (NN x))") + search1 = """ + @ NP /^NP/; + @ NN /^NN/; + @NN + """ + self.assertEqual(list(tgrep.tgrep_positions(search1, [tree])), [[(0,), (2,)]]) + search2 = """ + # macros + @ NP /^NP/; + @ NN /^NN/; + + # search string + @NN + """ + self.assertEqual(list(tgrep.tgrep_positions(search2, [tree])), [[(0,), (2,)]]) + + def test_rel_sister_nodes(self): + """ + Test matching sister nodes in a tree. + """ + tree = ParentedTree.fromstring("(S (A x) (B x) (C x))") + self.assertEqual(list(tgrep.tgrep_positions("* $. B", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* $.. B", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* $, B", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* $,, B", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* $ B", [tree])), [[(0,), (2,)]]) + + def tests_rel_indexed_children(self): + """ + Test matching nodes based on their index in their parent node. + """ + tree = ParentedTree.fromstring("(S (A x) (B x) (C x))") + self.assertEqual(list(tgrep.tgrep_positions("* >, S", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >1 S", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >2 S", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >3 S", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >' S", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >-1 S", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >-2 S", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* >-3 S", [tree])), [[(0,)]]) + tree = ParentedTree.fromstring( + "(S (D (A x) (B x) (C x)) (E (B x) (C x) (A x)) " "(F (C x) (A x) (B x)))" + ) + self.assertEqual(list(tgrep.tgrep_positions("* <, A", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <1 A", [tree])), [[(0,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <2 A", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <3 A", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <' A", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <-1 A", [tree])), [[(1,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <-2 A", [tree])), [[(2,)]]) + self.assertEqual(list(tgrep.tgrep_positions("* <-3 A", [tree])), [[(0,)]]) + + def test_rel_precedence(self): + """ + Test matching nodes based on precedence relations. + """ + tree = ParentedTree.fromstring( + "(S (NP (NP (PP x)) (NP (AP x)))" + " (VP (AP (X (PP x)) (Y (AP x))))" + " (NP (RC (NP (AP x)))))" + ) + self.assertEqual( + list(tgrep.tgrep_positions("* . X", [tree])), [[(0,), (0, 1), (0, 1, 0)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("* . Y", [tree])), [[(1, 0, 0), (1, 0, 0, 0)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("* .. X", [tree])), + [[(0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* .. Y", [tree])), + [[(0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1, 0, 0), (1, 0, 0, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* , X", [tree])), [[(1, 0, 1), (1, 0, 1, 0)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("* , Y", [tree])), + [[(2,), (2, 0), (2, 0, 0), (2, 0, 0, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* ,, X", [tree])), + [[(1, 0, 1), (1, 0, 1, 0), (2,), (2, 0), (2, 0, 0), (2, 0, 0, 0)]], + ) + self.assertEqual( + list(tgrep.tgrep_positions("* ,, Y", [tree])), + [[(2,), (2, 0), (2, 0, 0), (2, 0, 0, 0)]], + ) + + def test_examples(self): + """ + Test the Basic Examples from the TGrep2 manual. + """ + tree = ParentedTree.fromstring("(S (NP (AP x)) (NP (PP x)))") + # This matches any NP node that immediately dominates a PP: + self.assertEqual(list(tgrep.tgrep_positions("NP < PP", [tree])), [[(1,)]]) + + tree = ParentedTree.fromstring("(S (NP x) (VP x) (NP (PP x)) (VP x))") + # This matches an NP that dominates a PP and is immediately + # followed by a VP: + self.assertEqual(list(tgrep.tgrep_positions("NP << PP . VP", [tree])), [[(2,)]]) + + tree = ParentedTree.fromstring( + "(S (NP (AP x)) (NP (PP x)) " "(NP (DET x) (NN x)) (VP x))" + ) + # This matches an NP that dominates a PP or is immediately + # followed by a VP: + self.assertEqual( + list(tgrep.tgrep_positions("NP << PP | . VP", [tree])), [[(1,), (2,)]] + ) + + tree = ParentedTree.fromstring( + "(S (NP (NP (PP x)) (NP (AP x)))" + " (VP (AP (NP (PP x)) (NP (AP x))))" + " (NP (RC (NP (AP x)))))" + ) + # This matches an NP that does not dominate a PP. Also, the NP + # must either have a parent that is an NP or be dominated by a + # VP: + self.assertEqual( + list(tgrep.tgrep_positions("NP !<< PP [> NP | >> VP]", [tree])), + [[(0, 1), (1, 0, 1)]], + ) + + tree = ParentedTree.fromstring( + "(S (NP (AP (PP x) (VP x))) " "(NP (AP (PP x) (NP x))) (NP x))" + ) + # This matches an NP that dominates a PP which itself is + # immediately followed by a VP. Note the use of parentheses to + # group ". VP" with the PP rather than with the NP: + self.assertEqual( + list(tgrep.tgrep_positions("NP << (PP . VP)", [tree])), [[(0,)]] + ) + + tree = ParentedTree.fromstring( + "(S (NP (DET a) (NN cat) (PP (IN on) (NP x)))" + " (NP (DET a) (NN cat) (PP (IN on) (NP x)) (PP x))" + " (NP x))" + ) + # This matches an NP whose last child is a PP that begins with + # the preposition "on": + self.assertEqual( + list(tgrep.tgrep_positions("NP <' (PP <, (IN < on))", [tree])), [[(0,)]] + ) + + tree = ParentedTree.fromstring( + "(S (S (C x) (A (B x))) (S (C x) (A x)) " "(S (D x) (A (B x))))" + ) + # The following pattern matches an S which has a child A and + # another child that is a C and that the A has a child B: + self.assertEqual( + list(tgrep.tgrep_positions("S < (A < B) < C", [tree])), [[(0,)]] + ) + + tree = ParentedTree.fromstring( + "(S (S (A (B x) (C x))) (S (S (C x) (A (B x)))))" + ) + # However, this pattern means that S has child A and that A + # has children B and C: + self.assertEqual( + list(tgrep.tgrep_positions("S < ((A < B) < C)", [tree])), [[(0,)]] + ) + + # It is equivalent to this: + self.assertEqual( + list(tgrep.tgrep_positions("S < (A < B < C)", [tree])), [[(0,)]] + ) + + def test_use_macros(self): + """ + Test defining and using tgrep2 macros. + """ + tree = ParentedTree.fromstring( + "(VP (VB sold) (NP (DET the) " + "(NN heiress)) (NP (NN deed) (PREP to) " + "(NP (DET the) (NN school) (NN house))))" + ) + self.assertEqual( + list( + tgrep.tgrep_positions( + "@ NP /^NP/;\n@ NN /^NN/;\n@NP !< @NP !$.. @NN", [tree] + ) + ), + [[(1,), (2, 2)]], + ) + # use undefined macro @CNP + self.assertRaises( + tgrep.TgrepException, + list, + tgrep.tgrep_positions( + "@ NP /^NP/;\n@ NN /^NN/;\n@CNP !< @NP !$.. @NN", [tree] + ), + ) + + def test_tokenize_node_labels(self): + """Test tokenization of labeled nodes.""" + self.assertEqual( + tgrep.tgrep_tokenize("S < @SBJ < (@VP < (@VB $.. @OBJ))"), + [ + "S", + "<", + "@SBJ", + "<", + "(", + "@VP", + "<", + "(", + "@VB", + "$..", + "@OBJ", + ")", + ")", + ], + ) + self.assertEqual( + tgrep.tgrep_tokenize("S < @SBJ=s < (@VP=v < (@VB $.. @OBJ))"), + [ + "S", + "<", + "@SBJ", + "=", + "s", + "<", + "(", + "@VP", + "=", + "v", + "<", + "(", + "@VB", + "$..", + "@OBJ", + ")", + ")", + ], + ) + + def test_tokenize_segmented_patterns(self): + """Test tokenization of segmented patterns.""" + self.assertEqual( + tgrep.tgrep_tokenize("S < @SBJ=s < (@VP=v < (@VB $.. @OBJ)) : =s .. =v"), + [ + "S", + "<", + "@SBJ", + "=", + "s", + "<", + "(", + "@VP", + "=", + "v", + "<", + "(", + "@VB", + "$..", + "@OBJ", + ")", + ")", + ":", + "=s", + "..", + "=v", + ], + ) + + def test_labeled_nodes(self): + """ + Test labeled nodes. + + Test case from Emily M. Bender. + """ + search = """ + # macros + @ SBJ /SBJ/; + @ VP /VP/; + @ VB /VB/; + @ VPoB /V[PB]/; + @ OBJ /OBJ/; + + # 1 svo + S < @SBJ=s < (@VP=v < (@VB $.. @OBJ)) : =s .. =v""" + sent1 = ParentedTree.fromstring( + "(S (NP-SBJ I) (VP (VB eat) (NP-OBJ (NNS apples))))" + ) + sent2 = ParentedTree.fromstring( + "(S (VP (VB eat) (NP-OBJ (NNS apples))) (NP-SBJ I))" + ) + search_firsthalf = search.split("\n\n")[0] + "S < @SBJ < (@VP < (@VB $.. @OBJ))" + search_rewrite = "S < (/.*SBJ/ $.. (/VP/ < (/VB/ $.. /.*OBJ/)))" + + self.assertTrue(list(tgrep.tgrep_positions(search_firsthalf, [sent1]))[0]) + self.assertTrue(list(tgrep.tgrep_positions(search, [sent1]))[0]) + self.assertTrue(list(tgrep.tgrep_positions(search_rewrite, [sent1]))[0]) + self.assertEqual( + list(tgrep.tgrep_positions(search, [sent1])), + list(tgrep.tgrep_positions(search_rewrite, [sent1])), + ) + self.assertTrue(list(tgrep.tgrep_positions(search_firsthalf, [sent2]))[0]) + self.assertFalse(list(tgrep.tgrep_positions(search, [sent2]))[0]) + self.assertFalse(list(tgrep.tgrep_positions(search_rewrite, [sent2]))[0]) + self.assertEqual( + list(tgrep.tgrep_positions(search, [sent2])), + list(tgrep.tgrep_positions(search_rewrite, [sent2])), + ) + + def test_multiple_conjs(self): + """ + Test that multiple (3 or more) conjunctions of node relations are + handled properly. + """ + sent = ParentedTree.fromstring("((A (B b) (C c)) (A (B b) (C c) (D d)))") + # search = '(A < B < C < D)' + # search_tworels = '(A < B < C)' + self.assertEqual( + list(tgrep.tgrep_positions("(A < B < C < D)", [sent])), [[(1,)]] + ) + self.assertEqual( + list(tgrep.tgrep_positions("(A < B < C)", [sent])), [[(0,), (1,)]] + ) + + def test_trailing_semicolon(self): + """ + Test that semicolons at the end of a tgrep2 search string won't + cause a parse failure. + """ + tree = ParentedTree.fromstring( + "(S (NP (DT the) (JJ big) (NN dog)) " "(VP bit) (NP (DT a) (NN cat)))" + ) + self.assertEqual(list(tgrep.tgrep_positions("NN", [tree])), [[(0, 2), (2, 1)]]) + self.assertEqual(list(tgrep.tgrep_positions("NN;", [tree])), [[(0, 2), (2, 1)]]) + self.assertEqual( + list(tgrep.tgrep_positions("NN;;", [tree])), [[(0, 2), (2, 1)]] + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tokenize.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tokenize.py new file mode 100644 index 0000000..7688f52 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_tokenize.py @@ -0,0 +1,867 @@ +""" +Unit tests for nltk.tokenize. +See also nltk/test/tokenize.doctest +""" +from typing import List, Tuple + +import pytest + +from nltk.tokenize import ( + LegalitySyllableTokenizer, + StanfordSegmenter, + SyllableTokenizer, + TreebankWordTokenizer, + TweetTokenizer, + punkt, + sent_tokenize, + word_tokenize, +) + + +def load_stanford_segmenter(): + try: + seg = StanfordSegmenter() + seg.default_config("ar") + seg.default_config("zh") + return True + except LookupError: + return False + + +check_stanford_segmenter = pytest.mark.skipif( + not load_stanford_segmenter(), + reason="NLTK was unable to find stanford-segmenter.jar.", +) + + +class TestTokenize: + def test_tweet_tokenizer(self): + """ + Test TweetTokenizer using words with special and accented characters. + """ + + tokenizer = TweetTokenizer(strip_handles=True, reduce_len=True) + s9 = "@myke: Let's test these words: resumé España München français" + tokens = tokenizer.tokenize(s9) + expected = [ + ":", + "Let's", + "test", + "these", + "words", + ":", + "resumé", + "España", + "München", + "français", + ] + assert tokens == expected + + @pytest.mark.parametrize( + "test_input, expecteds", + [ + ( + "My text 0106404243030 is great text", + ( + ["My", "text", "01064042430", "30", "is", "great", "text"], + ["My", "text", "0106404243030", "is", "great", "text"], + ), + ), + ( + "My ticket id is 1234543124123", + ( + ["My", "ticket", "id", "is", "12345431241", "23"], + ["My", "ticket", "id", "is", "1234543124123"], + ), + ), + ( + "@remy: This is waaaaayyyy too much for you!!!!!! 01064042430", + ( + [ + ":", + "This", + "is", + "waaayyy", + "too", + "much", + "for", + "you", + "!", + "!", + "!", + "01064042430", + ], + [ + ":", + "This", + "is", + "waaayyy", + "too", + "much", + "for", + "you", + "!", + "!", + "!", + "01064042430", + ], + ), + ), + # Further tests from https://github.com/nltk/nltk/pull/2798#issuecomment-922533085, + # showing the TweetTokenizer performance for `match_phone_numbers=True` and + # `match_phone_numbers=False`. + ( + # Some phone numbers are always tokenized, even with `match_phone_numbers=`False` + "My number is 06-46124080, except it's not.", + ( + [ + "My", + "number", + "is", + "06-46124080", + ",", + "except", + "it's", + "not", + ".", + ], + [ + "My", + "number", + "is", + "06-46124080", + ",", + "except", + "it's", + "not", + ".", + ], + ), + ), + ( + # Phone number here is only tokenized correctly if `match_phone_numbers=True` + "My number is 601-984-4813, except it's not.", + ( + [ + "My", + "number", + "is", + "601-984-4813", + ",", + "except", + "it's", + "not", + ".", + ], + [ + "My", + "number", + "is", + "601-984-", + "4813", + ",", + "except", + "it's", + "not", + ".", + ], + ), + ), + ( + # Phone number here is only tokenized correctly if `match_phone_numbers=True` + "My number is (393) 928 -3010, except it's not.", + ( + [ + "My", + "number", + "is", + "(393) 928 -3010", + ",", + "except", + "it's", + "not", + ".", + ], + [ + "My", + "number", + "is", + "(", + "393", + ")", + "928", + "-", + "3010", + ",", + "except", + "it's", + "not", + ".", + ], + ), + ), + ( + # A long number is tokenized correctly only if `match_phone_numbers=False` + "The product identification number is 48103284512.", + ( + [ + "The", + "product", + "identification", + "number", + "is", + "4810328451", + "2", + ".", + ], + [ + "The", + "product", + "identification", + "number", + "is", + "48103284512", + ".", + ], + ), + ), + ( + # `match_phone_numbers=True` can have some unforeseen + "My favourite substraction is 240 - 1353.", + ( + ["My", "favourite", "substraction", "is", "240 - 1353", "."], + ["My", "favourite", "substraction", "is", "240", "-", "1353", "."], + ), + ), + ], + ) + def test_tweet_tokenizer_expanded( + self, test_input: str, expecteds: Tuple[List[str], List[str]] + ): + """ + Test `match_phone_numbers` in TweetTokenizer. + + Note that TweetTokenizer is also passed the following for these tests: + * strip_handles=True + * reduce_len=True + + :param test_input: The input string to tokenize using TweetTokenizer. + :type test_input: str + :param expecteds: A 2-tuple of tokenized sentences. The first of the two + tokenized is the expected output of tokenization with `match_phone_numbers=True`. + The second of the two tokenized lists is the expected output of tokenization + with `match_phone_numbers=False`. + :type expecteds: Tuple[List[str], List[str]] + """ + for match_phone_numbers, expected in zip([True, False], expecteds): + tokenizer = TweetTokenizer( + strip_handles=True, + reduce_len=True, + match_phone_numbers=match_phone_numbers, + ) + predicted = tokenizer.tokenize(test_input) + assert predicted == expected + + def test_sonority_sequencing_syllable_tokenizer(self): + """ + Test SyllableTokenizer tokenizer. + """ + tokenizer = SyllableTokenizer() + tokens = tokenizer.tokenize("justification") + assert tokens == ["jus", "ti", "fi", "ca", "tion"] + + def test_syllable_tokenizer_numbers(self): + """ + Test SyllableTokenizer tokenizer. + """ + tokenizer = SyllableTokenizer() + text = "9" * 10000 + tokens = tokenizer.tokenize(text) + assert tokens == [text] + + def test_legality_principle_syllable_tokenizer(self): + """ + Test LegalitySyllableTokenizer tokenizer. + """ + from nltk.corpus import words + + test_word = "wonderful" + tokenizer = LegalitySyllableTokenizer(words.words()) + tokens = tokenizer.tokenize(test_word) + assert tokens == ["won", "der", "ful"] + + @check_stanford_segmenter + def test_stanford_segmenter_arabic(self): + """ + Test the Stanford Word Segmenter for Arabic (default config) + """ + seg = StanfordSegmenter() + seg.default_config("ar") + sent = "يبحث علم الحاسوب استخدام الحوسبة بجميع اشكالها لحل المشكلات" + segmented_sent = seg.segment(sent.split()) + assert segmented_sent.split() == [ + "يبحث", + "علم", + "الحاسوب", + "استخدام", + "الحوسبة", + "ب", + "جميع", + "اشكال", + "ها", + "ل", + "حل", + "المشكلات", + ] + + @check_stanford_segmenter + def test_stanford_segmenter_chinese(self): + """ + Test the Stanford Word Segmenter for Chinese (default config) + """ + seg = StanfordSegmenter() + seg.default_config("zh") + sent = "这是斯坦福中文分词器测试" + segmented_sent = seg.segment(sent.split()) + assert segmented_sent.split() == ["这", "是", "斯坦福", "中文", "分词器", "测试"] + + def test_phone_tokenizer(self): + """ + Test a string that resembles a phone number but contains a newline + """ + + # Should be recognized as a phone number, albeit one with multiple spaces + tokenizer = TweetTokenizer() + test1 = "(393) 928 -3010" + expected = ["(393) 928 -3010"] + result = tokenizer.tokenize(test1) + assert result == expected + + # Due to newline, first three elements aren't part of a phone number; + # fourth is + test2 = "(393)\n928 -3010" + expected = ["(", "393", ")", "928 -3010"] + result = tokenizer.tokenize(test2) + assert result == expected + + def test_emoji_tokenizer(self): + """ + Test a string that contains Emoji ZWJ Sequences and skin tone modifier + """ + tokenizer = TweetTokenizer() + + # A Emoji ZWJ Sequences, they together build as a single emoji, should not be split. + test1 = "👨‍👩‍👧‍👧" + expected = ["👨‍👩‍👧‍👧"] + result = tokenizer.tokenize(test1) + assert result == expected + + # A Emoji with skin tone modifier, the two characters build a single emoji, should not be split. + test2 = "👨🏿" + expected = ["👨🏿"] + result = tokenizer.tokenize(test2) + assert result == expected + + # A string containing both skin tone modifier and ZWJ Sequences + test3 = "🤔 🙈 me así, se😌 ds 💕👭👙 hello 👩🏾‍🎓 emoji hello 👨‍👩‍👦‍👦 how are 😊 you today🙅🏽🙅🏽" + expected = [ + "🤔", + "🙈", + "me", + "así", + ",", + "se", + "😌", + "ds", + "💕", + "👭", + "👙", + "hello", + "👩🏾\u200d🎓", + "emoji", + "hello", + "👨\u200d👩\u200d👦\u200d👦", + "how", + "are", + "😊", + "you", + "today", + "🙅🏽", + "🙅🏽", + ] + result = tokenizer.tokenize(test3) + assert result == expected + + # emoji flag sequences, including enclosed letter pairs + # Expected behavior from #3034 + test4 = "🇦🇵🇵🇱🇪" + expected = ["🇦🇵", "🇵🇱", "🇪"] + result = tokenizer.tokenize(test4) + assert result == expected + + test5 = "Hi 🇨🇦, 😍!!" + expected = ["Hi", "🇨🇦", ",", "😍", "!", "!"] + result = tokenizer.tokenize(test5) + assert result == expected + + test6 = "<3 🇨🇦 🤝 🇵🇱 <3" + expected = ["<3", "🇨🇦", "🤝", "🇵🇱", "<3"] + result = tokenizer.tokenize(test6) + assert result == expected + + def test_pad_asterisk(self): + """ + Test padding of asterisk for word tokenization. + """ + text = "This is a, *weird sentence with *asterisks in it." + expected = [ + "This", + "is", + "a", + ",", + "*", + "weird", + "sentence", + "with", + "*", + "asterisks", + "in", + "it", + ".", + ] + assert word_tokenize(text) == expected + + def test_pad_dotdot(self): + """ + Test padding of dotdot* for word tokenization. + """ + text = "Why did dotdot.. not get tokenized but dotdotdot... did? How about manydots....." + expected = [ + "Why", + "did", + "dotdot", + "..", + "not", + "get", + "tokenized", + "but", + "dotdotdot", + "...", + "did", + "?", + "How", + "about", + "manydots", + ".....", + ] + assert word_tokenize(text) == expected + + def test_remove_handle(self): + """ + Test remove_handle() from casual.py with specially crafted edge cases + """ + + tokenizer = TweetTokenizer(strip_handles=True) + + # Simple example. Handles with just numbers should be allowed + test1 = "@twitter hello @twi_tter_. hi @12345 @123news" + expected = ["hello", ".", "hi"] + result = tokenizer.tokenize(test1) + assert result == expected + + # Handles are allowed to follow any of the following characters + test2 = "@n`@n~@n(@n)@n-@n=@n+@n\\@n|@n[@n]@n{@n}@n;@n:@n'@n\"@n/@n?@n.@n,@n<@n>@n @n\n@n ñ@n.ü@n.ç@n." + expected = [ + "`", + "~", + "(", + ")", + "-", + "=", + "+", + "\\", + "|", + "[", + "]", + "{", + "}", + ";", + ":", + "'", + '"', + "/", + "?", + ".", + ",", + "<", + ">", + "ñ", + ".", + "ü", + ".", + "ç", + ".", + ] + result = tokenizer.tokenize(test2) + assert result == expected + + # Handles are NOT allowed to follow any of the following characters + test3 = "a@n j@n z@n A@n L@n Z@n 1@n 4@n 7@n 9@n 0@n _@n !@n @@n #@n $@n %@n &@n *@n" + expected = [ + "a", + "@n", + "j", + "@n", + "z", + "@n", + "A", + "@n", + "L", + "@n", + "Z", + "@n", + "1", + "@n", + "4", + "@n", + "7", + "@n", + "9", + "@n", + "0", + "@n", + "_", + "@n", + "!", + "@n", + "@", + "@n", + "#", + "@n", + "$", + "@n", + "%", + "@n", + "&", + "@n", + "*", + "@n", + ] + result = tokenizer.tokenize(test3) + assert result == expected + + # Handles are allowed to precede the following characters + test4 = "@n!a @n#a @n$a @n%a @n&a @n*a" + expected = ["!", "a", "#", "a", "$", "a", "%", "a", "&", "a", "*", "a"] + result = tokenizer.tokenize(test4) + assert result == expected + + # Tests interactions with special symbols and multiple @ + test5 = "@n!@n @n#@n @n$@n @n%@n @n&@n @n*@n @n@n @@n @n@@n @n_@n @n7@n @nj@n" + expected = [ + "!", + "@n", + "#", + "@n", + "$", + "@n", + "%", + "@n", + "&", + "@n", + "*", + "@n", + "@n", + "@n", + "@", + "@n", + "@n", + "@", + "@n", + "@n_", + "@n", + "@n7", + "@n", + "@nj", + "@n", + ] + result = tokenizer.tokenize(test5) + assert result == expected + + # Tests that handles can have a max length of 15 + test6 = "@abcdefghijklmnopqrstuvwxyz @abcdefghijklmno1234 @abcdefghijklmno_ @abcdefghijklmnoendofhandle" + expected = ["pqrstuvwxyz", "1234", "_", "endofhandle"] + result = tokenizer.tokenize(test6) + assert result == expected + + # Edge case where an @ comes directly after a long handle + test7 = "@abcdefghijklmnop@abcde @abcdefghijklmno@abcde @abcdefghijklmno_@abcde @abcdefghijklmno5@abcde" + expected = [ + "p", + "@abcde", + "@abcdefghijklmno", + "@abcde", + "_", + "@abcde", + "5", + "@abcde", + ] + result = tokenizer.tokenize(test7) + assert result == expected + + def test_treebank_span_tokenizer(self): + """ + Test TreebankWordTokenizer.span_tokenize function + """ + + tokenizer = TreebankWordTokenizer() + + # Test case in the docstring + test1 = "Good muffins cost $3.88\nin New (York). Please (buy) me\ntwo of them.\n(Thanks)." + expected = [ + (0, 4), + (5, 12), + (13, 17), + (18, 19), + (19, 23), + (24, 26), + (27, 30), + (31, 32), + (32, 36), + (36, 37), + (37, 38), + (40, 46), + (47, 48), + (48, 51), + (51, 52), + (53, 55), + (56, 59), + (60, 62), + (63, 68), + (69, 70), + (70, 76), + (76, 77), + (77, 78), + ] + result = list(tokenizer.span_tokenize(test1)) + assert result == expected + + # Test case with double quotation + test2 = 'The DUP is similar to the "religious right" in the United States and takes a hardline stance on social issues' + expected = [ + (0, 3), + (4, 7), + (8, 10), + (11, 18), + (19, 21), + (22, 25), + (26, 27), + (27, 36), + (37, 42), + (42, 43), + (44, 46), + (47, 50), + (51, 57), + (58, 64), + (65, 68), + (69, 74), + (75, 76), + (77, 85), + (86, 92), + (93, 95), + (96, 102), + (103, 109), + ] + result = list(tokenizer.span_tokenize(test2)) + assert result == expected + + # Test case with double qoutation as well as converted quotations + test3 = "The DUP is similar to the \"religious right\" in the United States and takes a ``hardline'' stance on social issues" + expected = [ + (0, 3), + (4, 7), + (8, 10), + (11, 18), + (19, 21), + (22, 25), + (26, 27), + (27, 36), + (37, 42), + (42, 43), + (44, 46), + (47, 50), + (51, 57), + (58, 64), + (65, 68), + (69, 74), + (75, 76), + (77, 79), + (79, 87), + (87, 89), + (90, 96), + (97, 99), + (100, 106), + (107, 113), + ] + result = list(tokenizer.span_tokenize(test3)) + assert result == expected + + def test_word_tokenize(self): + """ + Test word_tokenize function + """ + + sentence = "The 'v', I've been fooled but I'll seek revenge." + expected = [ + "The", + "'", + "v", + "'", + ",", + "I", + "'ve", + "been", + "fooled", + "but", + "I", + "'ll", + "seek", + "revenge", + ".", + ] + assert word_tokenize(sentence) == expected + + sentence = "'v' 're'" + expected = ["'", "v", "'", "'re", "'"] + assert word_tokenize(sentence) == expected + + def test_punkt_pair_iter(self): + + test_cases = [ + ("12", [("1", "2"), ("2", None)]), + ("123", [("1", "2"), ("2", "3"), ("3", None)]), + ("1234", [("1", "2"), ("2", "3"), ("3", "4"), ("4", None)]), + ] + + for (test_input, expected_output) in test_cases: + actual_output = [x for x in punkt._pair_iter(test_input)] + + assert actual_output == expected_output + + def test_punkt_pair_iter_handles_stop_iteration_exception(self): + # test input to trigger StopIteration from next() + it = iter([]) + # call method under test and produce a generator + gen = punkt._pair_iter(it) + # unpack generator, ensure that no error is raised + list(gen) + + def test_punkt_tokenize_words_handles_stop_iteration_exception(self): + obj = punkt.PunktBaseClass() + + class TestPunktTokenizeWordsMock: + def word_tokenize(self, s): + return iter([]) + + obj._lang_vars = TestPunktTokenizeWordsMock() + # unpack generator, ensure that no error is raised + list(obj._tokenize_words("test")) + + def test_punkt_tokenize_custom_lang_vars(self): + + # Create LangVars including a full stop end character as used in Bengali + class BengaliLanguageVars(punkt.PunktLanguageVars): + sent_end_chars = (".", "?", "!", "\u0964") + + obj = punkt.PunktSentenceTokenizer(lang_vars=BengaliLanguageVars()) + + # We now expect these sentences to be split up into the individual sentences + sentences = "উপরাষ্ট্রপতি শ্রী এম ভেঙ্কাইয়া নাইডু সোমবার আই আই টি দিল্লির হীরক জয়ন্তী উদযাপনের উদ্বোধন করেছেন। অনলাইনের মাধ্যমে এই অনুষ্ঠানে কেন্দ্রীয় মানব সম্পদ উন্নয়নমন্ত্রী শ্রী রমেশ পোখরিয়াল ‘নিশাঙ্ক’ উপস্থিত ছিলেন। এই উপলক্ষ্যে উপরাষ্ট্রপতি হীরকজয়ন্তীর লোগো এবং ২০৩০-এর জন্য প্রতিষ্ঠানের লক্ষ্য ও পরিকল্পনার নথি প্রকাশ করেছেন।" + expected = [ + "উপরাষ্ট্রপতি শ্রী এম ভেঙ্কাইয়া নাইডু সোমবার আই আই টি দিল্লির হীরক জয়ন্তী উদযাপনের উদ্বোধন করেছেন।", + "অনলাইনের মাধ্যমে এই অনুষ্ঠানে কেন্দ্রীয় মানব সম্পদ উন্নয়নমন্ত্রী শ্রী রমেশ পোখরিয়াল ‘নিশাঙ্ক’ উপস্থিত ছিলেন।", + "এই উপলক্ষ্যে উপরাষ্ট্রপতি হীরকজয়ন্তীর লোগো এবং ২০৩০-এর জন্য প্রতিষ্ঠানের লক্ষ্য ও পরিকল্পনার নথি প্রকাশ করেছেন।", + ] + + assert obj.tokenize(sentences) == expected + + def test_punkt_tokenize_no_custom_lang_vars(self): + + obj = punkt.PunktSentenceTokenizer() + + # We expect these sentences to not be split properly, as the Bengali full stop '।' is not included in the default language vars + sentences = "উপরাষ্ট্রপতি শ্রী এম ভেঙ্কাইয়া নাইডু সোমবার আই আই টি দিল্লির হীরক জয়ন্তী উদযাপনের উদ্বোধন করেছেন। অনলাইনের মাধ্যমে এই অনুষ্ঠানে কেন্দ্রীয় মানব সম্পদ উন্নয়নমন্ত্রী শ্রী রমেশ পোখরিয়াল ‘নিশাঙ্ক’ উপস্থিত ছিলেন। এই উপলক্ষ্যে উপরাষ্ট্রপতি হীরকজয়ন্তীর লোগো এবং ২০৩০-এর জন্য প্রতিষ্ঠানের লক্ষ্য ও পরিকল্পনার নথি প্রকাশ করেছেন।" + expected = [ + "উপরাষ্ট্রপতি শ্রী এম ভেঙ্কাইয়া নাইডু সোমবার আই আই টি দিল্লির হীরক জয়ন্তী উদযাপনের উদ্বোধন করেছেন। অনলাইনের মাধ্যমে এই অনুষ্ঠানে কেন্দ্রীয় মানব সম্পদ উন্নয়নমন্ত্রী শ্রী রমেশ পোখরিয়াল ‘নিশাঙ্ক’ উপস্থিত ছিলেন। এই উপলক্ষ্যে উপরাষ্ট্রপতি হীরকজয়ন্তীর লোগো এবং ২০৩০-এর জন্য প্রতিষ্ঠানের লক্ষ্য ও পরিকল্পনার নথি প্রকাশ করেছেন।" + ] + + assert obj.tokenize(sentences) == expected + + @pytest.mark.parametrize( + "input_text,n_sents,n_splits,lang_vars", + [ + # Test debug_decisions on a text with two sentences, split by a dot. + ("Subject: Some subject. Attachments: Some attachments", 2, 1), + # The sentence should be split into two sections, + # with one split and hence one decision. + # Test debug_decisions on a text with two sentences, split by an exclamation mark. + ("Subject: Some subject! Attachments: Some attachments", 2, 1), + # The sentence should be split into two sections, + # with one split and hence one decision. + # Test debug_decisions on a text with one sentences, + # which is not split. + ("This is just a normal sentence, just like any other.", 1, 0) + # Hence just 1 + ], + ) + def punkt_debug_decisions(self, input_text, n_sents, n_splits, lang_vars=None): + tokenizer = punkt.PunktSentenceTokenizer() + if lang_vars != None: + tokenizer._lang_vars = lang_vars + + assert len(tokenizer.tokenize(input_text)) == n_sents + assert len(list(tokenizer.debug_decisions(input_text))) == n_splits + + def test_punkt_debug_decisions_custom_end(self): + # Test debug_decisions on a text with two sentences, + # split by a custom end character, based on Issue #2519 + class ExtLangVars(punkt.PunktLanguageVars): + sent_end_chars = (".", "?", "!", "^") + + self.punkt_debug_decisions( + "Subject: Some subject^ Attachments: Some attachments", + n_sents=2, + n_splits=1, + lang_vars=ExtLangVars(), + ) + # The sentence should be split into two sections, + # with one split and hence one decision. + + @pytest.mark.parametrize( + "sentences, expected", + [ + ( + "this is a test. . new sentence.", + ["this is a test.", ".", "new sentence."], + ), + ("This. . . That", ["This.", ".", ".", "That"]), + ("This..... That", ["This..... That"]), + ("This... That", ["This... That"]), + ("This.. . That", ["This.. .", "That"]), + ("This. .. That", ["This.", ".. That"]), + ("This. ,. That", ["This.", ",.", "That"]), + ("This!!! That", ["This!!!", "That"]), + ("This! That", ["This!", "That"]), + ( + "1. This is R .\n2. This is A .\n3. That's all", + ["1.", "This is R .", "2.", "This is A .", "3.", "That's all"], + ), + ( + "1. This is R .\t2. This is A .\t3. That's all", + ["1.", "This is R .", "2.", "This is A .", "3.", "That's all"], + ), + ("Hello.\tThere", ["Hello.", "There"]), + ], + ) + def test_sent_tokenize(self, sentences: str, expected: List[str]): + assert sent_tokenize(sentences) == expected diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_twitter_auth.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_twitter_auth.py new file mode 100644 index 0000000..d5240a8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_twitter_auth.py @@ -0,0 +1,77 @@ +""" +Tests for static parts of Twitter package +""" + +import os + +import pytest + +pytest.importorskip("twython") + +from nltk.twitter import Authenticate + + +@pytest.fixture +def auth(): + return Authenticate() + + +class TestCredentials: + """ + Tests that Twitter credentials from a file are handled correctly. + """ + + @classmethod + def setup_class(self): + self.subdir = os.path.join(os.path.dirname(__file__), "files") + os.environ["TWITTER"] = "twitter-files" + + def test_environment(self, auth): + """ + Test that environment variable has been read correctly. + """ + fn = os.path.basename(auth.creds_subdir) + assert fn == os.environ["TWITTER"] + + @pytest.mark.parametrize( + "kwargs", + [ + # Each of the following scenarios should raise an error: + # An empty subdir path + {"subdir": ""}, + # A subdir path of None + {"subdir": None}, + # A nonexistent directory + {"subdir": "/nosuchdir"}, + # 'credentials.txt' is not in default subdir, as read from `os.environ['TWITTER']` + {}, + # Nonexistent credentials file ('foobar') + {"creds_file": "foobar"}, + # 'bad_oauth1-1.txt' is incomplete + {"creds_file": "bad_oauth1-1.txt"}, + # The first key in credentials file 'bad_oauth1-2.txt' is ill-formed + {"creds_file": "bad_oauth1-2.txt"}, + # The first two lines in 'bad_oauth1-3.txt' are collapsed + {"creds_file": "bad_oauth1-3.txt"}, + ], + ) + def test_scenarios_that_should_raise_errors(self, kwargs, auth): + """Various scenarios that should raise errors""" + try: + auth.load_creds(**kwargs) + # raises ValueError (zero length field name in format) for python 2.6 + # OSError for the rest + except (OSError, ValueError): + pass + except Exception as e: + pytest.fail("Unexpected exception thrown: %s" % e) + else: + pytest.fail("OSError exception not thrown.") + + def test_correct_file(self, auth): + """Test that a proper file succeeds and is read correctly""" + oauth = auth.load_creds(subdir=self.subdir) + + assert auth.creds_fullpath == os.path.join(self.subdir, auth.creds_file) + assert auth.creds_file == "credentials.txt" + assert oauth["app_key"] == "a" diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_util.py new file mode 100644 index 0000000..4709e84 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_util.py @@ -0,0 +1,82 @@ +import pytest + +from nltk.util import everygrams + + +@pytest.fixture +def everygram_input(): + """Form test data for tests.""" + return iter(["a", "b", "c"]) + + +def test_everygrams_without_padding(everygram_input): + expected_output = [ + ("a",), + ("a", "b"), + ("a", "b", "c"), + ("b",), + ("b", "c"), + ("c",), + ] + output = list(everygrams(everygram_input)) + assert output == expected_output + + +def test_everygrams_max_len(everygram_input): + expected_output = [ + ("a",), + ("a", "b"), + ("b",), + ("b", "c"), + ("c",), + ] + output = list(everygrams(everygram_input, max_len=2)) + assert output == expected_output + + +def test_everygrams_min_len(everygram_input): + expected_output = [ + ("a", "b"), + ("a", "b", "c"), + ("b", "c"), + ] + output = list(everygrams(everygram_input, min_len=2)) + assert output == expected_output + + +def test_everygrams_pad_right(everygram_input): + expected_output = [ + ("a",), + ("a", "b"), + ("a", "b", "c"), + ("b",), + ("b", "c"), + ("b", "c", None), + ("c",), + ("c", None), + ("c", None, None), + (None,), + (None, None), + (None,), + ] + output = list(everygrams(everygram_input, max_len=3, pad_right=True)) + assert output == expected_output + + +def test_everygrams_pad_left(everygram_input): + expected_output = [ + (None,), + (None, None), + (None, None, "a"), + (None,), + (None, "a"), + (None, "a", "b"), + ("a",), + ("a", "b"), + ("a", "b", "c"), + ("b",), + ("b", "c"), + ("c",), + ] + output = list(everygrams(everygram_input, max_len=3, pad_left=True)) + assert output == expected_output diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_wordnet.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_wordnet.py new file mode 100644 index 0000000..4b92636 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/test_wordnet.py @@ -0,0 +1,240 @@ +""" +Unit tests for nltk.corpus.wordnet +See also nltk/test/wordnet.doctest +""" +import unittest + +from nltk.corpus import wordnet as wn +from nltk.corpus import wordnet_ic as wnic + +wn.ensure_loaded() +S = wn.synset +L = wn.lemma + + +class WordnNetDemo(unittest.TestCase): + def test_retrieve_synset(self): + move_synset = S("go.v.21") + self.assertEqual(move_synset.name(), "move.v.15") + self.assertEqual(move_synset.lemma_names(), ["move", "go"]) + self.assertEqual( + move_synset.definition(), "have a turn; make one's move in a game" + ) + self.assertEqual(move_synset.examples(), ["Can I go now?"]) + + def test_retrieve_synsets(self): + self.assertEqual(sorted(wn.synsets("zap", pos="n")), [S("zap.n.01")]) + self.assertEqual( + sorted(wn.synsets("zap", pos="v")), + [S("microwave.v.01"), S("nuke.v.01"), S("zap.v.01"), S("zap.v.02")], + ) + + def test_hyperhyponyms(self): + # Not every synset as hypernyms() + self.assertEqual(S("travel.v.01").hypernyms(), []) + self.assertEqual(S("travel.v.02").hypernyms(), [S("travel.v.03")]) + self.assertEqual(S("travel.v.03").hypernyms(), []) + + # Test hyper-/hyponyms. + self.assertEqual(S("breakfast.n.1").hypernyms(), [S("meal.n.01")]) + first_five_meal_hypo = [ + S("banquet.n.02"), + S("bite.n.04"), + S("breakfast.n.01"), + S("brunch.n.01"), + S("buffet.n.02"), + ] + self.assertEqual(sorted(S("meal.n.1").hyponyms()[:5]), first_five_meal_hypo) + self.assertEqual(S("Austen.n.1").instance_hypernyms(), [S("writer.n.01")]) + first_five_composer_hypo = [ + S("ambrose.n.01"), + S("bach.n.01"), + S("barber.n.01"), + S("bartok.n.01"), + S("beethoven.n.01"), + ] + self.assertEqual( + S("composer.n.1").instance_hyponyms()[:5], first_five_composer_hypo + ) + + # Test root hyper-/hyponyms + self.assertEqual(S("person.n.01").root_hypernyms(), [S("entity.n.01")]) + self.assertEqual(S("sail.v.01").root_hypernyms(), [S("travel.v.01")]) + self.assertEqual( + S("fall.v.12").root_hypernyms(), [S("act.v.01"), S("fall.v.17")] + ) + + def test_derivationally_related_forms(self): + # Test `derivationally_related_forms()` + self.assertEqual( + L("zap.v.03.nuke").derivationally_related_forms(), + [L("atomic_warhead.n.01.nuke")], + ) + self.assertEqual( + L("zap.v.03.atomize").derivationally_related_forms(), + [L("atomization.n.02.atomization")], + ) + self.assertEqual( + L("zap.v.03.atomise").derivationally_related_forms(), + [L("atomization.n.02.atomisation")], + ) + self.assertEqual(L("zap.v.03.zap").derivationally_related_forms(), []) + + def test_meronyms_holonyms(self): + # Test meronyms, holonyms. + self.assertEqual( + S("dog.n.01").member_holonyms(), [S("canis.n.01"), S("pack.n.06")] + ) + self.assertEqual(S("dog.n.01").part_meronyms(), [S("flag.n.07")]) + + self.assertEqual(S("faculty.n.2").member_meronyms(), [S("professor.n.01")]) + self.assertEqual(S("copilot.n.1").member_holonyms(), [S("crew.n.01")]) + + self.assertEqual( + S("table.n.2").part_meronyms(), + [S("leg.n.03"), S("tabletop.n.01"), S("tableware.n.01")], + ) + self.assertEqual(S("course.n.7").part_holonyms(), [S("meal.n.01")]) + + self.assertEqual( + S("water.n.1").substance_meronyms(), [S("hydrogen.n.01"), S("oxygen.n.01")] + ) + self.assertEqual( + S("gin.n.1").substance_holonyms(), + [ + S("gin_and_it.n.01"), + S("gin_and_tonic.n.01"), + S("martini.n.01"), + S("pink_lady.n.01"), + ], + ) + + def test_antonyms(self): + # Test antonyms. + self.assertEqual( + L("leader.n.1.leader").antonyms(), [L("follower.n.01.follower")] + ) + self.assertEqual( + L("increase.v.1.increase").antonyms(), [L("decrease.v.01.decrease")] + ) + + def test_misc_relations(self): + # Test misc relations. + self.assertEqual(S("snore.v.1").entailments(), [S("sleep.v.01")]) + self.assertEqual( + S("heavy.a.1").similar_tos(), + [ + S("dense.s.03"), + S("doughy.s.01"), + S("heavier-than-air.s.01"), + S("hefty.s.02"), + S("massive.s.04"), + S("non-buoyant.s.01"), + S("ponderous.s.02"), + ], + ) + self.assertEqual(S("light.a.1").attributes(), [S("weight.n.01")]) + self.assertEqual(S("heavy.a.1").attributes(), [S("weight.n.01")]) + + # Test pertainyms. + self.assertEqual( + L("English.a.1.English").pertainyms(), [L("england.n.01.England")] + ) + + def test_lch(self): + # Test LCH. + self.assertEqual( + S("person.n.01").lowest_common_hypernyms(S("dog.n.01")), + [S("organism.n.01")], + ) + self.assertEqual( + S("woman.n.01").lowest_common_hypernyms(S("girlfriend.n.02")), + [S("woman.n.01")], + ) + + def test_domains(self): + # Test domains. + self.assertEqual(S("code.n.03").topic_domains(), [S("computer_science.n.01")]) + self.assertEqual(S("pukka.a.01").region_domains(), [S("india.n.01")]) + self.assertEqual(S("freaky.a.01").usage_domains(), [S("slang.n.02")]) + + def test_in_topic_domains(self): + # Test in domains. + self.assertEqual( + S("computer_science.n.01").in_topic_domains()[0], S("access.n.05") + ) + self.assertEqual(S("germany.n.01").in_region_domains()[23], S("trillion.n.02")) + self.assertEqual(S("slang.n.02").in_usage_domains()[1], S("airhead.n.01")) + + def test_wordnet_similarities(self): + # Path based similarities. + self.assertAlmostEqual(S("cat.n.01").path_similarity(S("cat.n.01")), 1.0) + self.assertAlmostEqual(S("dog.n.01").path_similarity(S("cat.n.01")), 0.2) + self.assertAlmostEqual( + S("car.n.01").path_similarity(S("automobile.v.01")), + S("automobile.v.01").path_similarity(S("car.n.01")), + ) + self.assertAlmostEqual( + S("big.a.01").path_similarity(S("dog.n.01")), + S("dog.n.01").path_similarity(S("big.a.01")), + ) + self.assertAlmostEqual( + S("big.a.01").path_similarity(S("long.a.01")), + S("long.a.01").path_similarity(S("big.a.01")), + ) + self.assertAlmostEqual( + S("dog.n.01").lch_similarity(S("cat.n.01")), 2.028, places=3 + ) + self.assertAlmostEqual( + S("dog.n.01").wup_similarity(S("cat.n.01")), 0.8571, places=3 + ) + self.assertAlmostEqual( + S("car.n.01").wup_similarity(S("automobile.v.01")), + S("automobile.v.01").wup_similarity(S("car.n.01")), + ) + self.assertAlmostEqual( + S("big.a.01").wup_similarity(S("dog.n.01")), + S("dog.n.01").wup_similarity(S("big.a.01")), + ) + self.assertAlmostEqual( + S("big.a.01").wup_similarity(S("long.a.01")), + S("long.a.01").wup_similarity(S("big.a.01")), + ) + self.assertAlmostEqual( + S("big.a.01").lch_similarity(S("long.a.01")), + S("long.a.01").lch_similarity(S("big.a.01")), + ) + # Information Content similarities. + brown_ic = wnic.ic("ic-brown.dat") + self.assertAlmostEqual( + S("dog.n.01").jcn_similarity(S("cat.n.01"), brown_ic), 0.4497, places=3 + ) + semcor_ic = wnic.ic("ic-semcor.dat") + self.assertAlmostEqual( + S("dog.n.01").lin_similarity(S("cat.n.01"), semcor_ic), 0.8863, places=3 + ) + + def test_omw_lemma_no_trailing_underscore(self): + expected = sorted( + [ + "popolna_sprememba_v_mišljenju", + "popoln_obrat", + "preobrat", + "preobrat_v_mišljenju", + ] + ) + self.assertEqual(sorted(S("about-face.n.02").lemma_names(lang="slv")), expected) + + def test_iterable_type_for_all_lemma_names(self): + # Duck-test for iterables. + # See https://stackoverflow.com/a/36230057/610569 + cat_lemmas = wn.all_lemma_names(lang="cat") + eng_lemmas = wn.all_lemma_names(lang="eng") + + self.assertTrue(hasattr(eng_lemmas, "__iter__")) + self.assertTrue(hasattr(eng_lemmas, "__next__") or hasattr(eng_lemmas, "next")) + self.assertTrue(eng_lemmas.__iter__() is eng_lemmas) + + self.assertTrue(hasattr(cat_lemmas, "__iter__")) + self.assertTrue(hasattr(cat_lemmas, "__next__") or hasattr(eng_lemmas, "next")) + self.assertTrue(cat_lemmas.__iter__() is cat_lemmas) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..69f28a5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_bleu.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_bleu.cpython-310.pyc new file mode 100644 index 0000000..e752cc6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_bleu.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_gdfa.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_gdfa.cpython-310.pyc new file mode 100644 index 0000000..c9808d9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_gdfa.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm1.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm1.cpython-310.pyc new file mode 100644 index 0000000..f7c4769 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm1.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm2.cpython-310.pyc new file mode 100644 index 0000000..49f92df Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm3.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm3.cpython-310.pyc new file mode 100644 index 0000000..55322b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm3.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm4.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm4.cpython-310.pyc new file mode 100644 index 0000000..6c4b90f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm4.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm5.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm5.cpython-310.pyc new file mode 100644 index 0000000..22332c2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm5.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm_model.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm_model.cpython-310.pyc new file mode 100644 index 0000000..fe8ded8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_ibm_model.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_meteor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_meteor.cpython-310.pyc new file mode 100644 index 0000000..dd677b7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_meteor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_nist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_nist.cpython-310.pyc new file mode 100644 index 0000000..696bf8e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_nist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_stack_decoder.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_stack_decoder.cpython-310.pyc new file mode 100644 index 0000000..ca88dbb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/__pycache__/test_stack_decoder.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_bleu.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_bleu.py new file mode 100644 index 0000000..ac3565e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_bleu.py @@ -0,0 +1,405 @@ +""" +Tests for BLEU translation evaluation metric +""" + +import io +import unittest + +from nltk.data import find +from nltk.translate.bleu_score import ( + SmoothingFunction, + brevity_penalty, + closest_ref_length, + corpus_bleu, + modified_precision, + sentence_bleu, +) + + +class TestBLEU(unittest.TestCase): + def test_modified_precision(self): + """ + Examples from the original BLEU paper + https://www.aclweb.org/anthology/P02-1040.pdf + """ + # Example 1: the "the*" example. + # Reference sentences. + ref1 = "the cat is on the mat".split() + ref2 = "there is a cat on the mat".split() + # Hypothesis sentence(s). + hyp1 = "the the the the the the the".split() + + references = [ref1, ref2] + + # Testing modified unigram precision. + hyp1_unigram_precision = float(modified_precision(references, hyp1, n=1)) + assert round(hyp1_unigram_precision, 4) == 0.2857 + # With assertAlmostEqual at 4 place precision. + self.assertAlmostEqual(hyp1_unigram_precision, 0.28571428, places=4) + + # Testing modified bigram precision. + assert float(modified_precision(references, hyp1, n=2)) == 0.0 + + # Example 2: the "of the" example. + # Reference sentences + ref1 = str( + "It is a guide to action that ensures that the military " + "will forever heed Party commands" + ).split() + ref2 = str( + "It is the guiding principle which guarantees the military " + "forces always being under the command of the Party" + ).split() + ref3 = str( + "It is the practical guide for the army always to heed " + "the directions of the party" + ).split() + # Hypothesis sentence(s). + hyp1 = "of the".split() + + references = [ref1, ref2, ref3] + # Testing modified unigram precision. + assert float(modified_precision(references, hyp1, n=1)) == 1.0 + + # Testing modified bigram precision. + assert float(modified_precision(references, hyp1, n=2)) == 1.0 + + # Example 3: Proper MT outputs. + hyp1 = str( + "It is a guide to action which ensures that the military " + "always obeys the commands of the party" + ).split() + hyp2 = str( + "It is to insure the troops forever hearing the activity " + "guidebook that party direct" + ).split() + + references = [ref1, ref2, ref3] + + # Unigram precision. + hyp1_unigram_precision = float(modified_precision(references, hyp1, n=1)) + hyp2_unigram_precision = float(modified_precision(references, hyp2, n=1)) + # Test unigram precision with assertAlmostEqual at 4 place precision. + self.assertAlmostEqual(hyp1_unigram_precision, 0.94444444, places=4) + self.assertAlmostEqual(hyp2_unigram_precision, 0.57142857, places=4) + # Test unigram precision with rounding. + assert round(hyp1_unigram_precision, 4) == 0.9444 + assert round(hyp2_unigram_precision, 4) == 0.5714 + + # Bigram precision + hyp1_bigram_precision = float(modified_precision(references, hyp1, n=2)) + hyp2_bigram_precision = float(modified_precision(references, hyp2, n=2)) + # Test bigram precision with assertAlmostEqual at 4 place precision. + self.assertAlmostEqual(hyp1_bigram_precision, 0.58823529, places=4) + self.assertAlmostEqual(hyp2_bigram_precision, 0.07692307, places=4) + # Test bigram precision with rounding. + assert round(hyp1_bigram_precision, 4) == 0.5882 + assert round(hyp2_bigram_precision, 4) == 0.0769 + + def test_brevity_penalty(self): + # Test case from brevity_penalty_closest function in mteval-v13a.pl. + # Same test cases as in the doctest in nltk.translate.bleu_score.py + references = [["a"] * 11, ["a"] * 8] + hypothesis = ["a"] * 7 + hyp_len = len(hypothesis) + closest_ref_len = closest_ref_length(references, hyp_len) + self.assertAlmostEqual( + brevity_penalty(closest_ref_len, hyp_len), 0.8669, places=4 + ) + + references = [["a"] * 11, ["a"] * 8, ["a"] * 6, ["a"] * 7] + hypothesis = ["a"] * 7 + hyp_len = len(hypothesis) + closest_ref_len = closest_ref_length(references, hyp_len) + assert brevity_penalty(closest_ref_len, hyp_len) == 1.0 + + def test_zero_matches(self): + # Test case where there's 0 matches + references = ["The candidate has no alignment to any of the references".split()] + hypothesis = "John loves Mary".split() + + # Test BLEU to nth order of n-grams, where n is len(hypothesis). + for n in range(1, len(hypothesis)): + weights = (1.0 / n,) * n # Uniform weights. + assert sentence_bleu(references, hypothesis, weights) == 0 + + def test_full_matches(self): + # Test case where there's 100% matches + references = ["John loves Mary".split()] + hypothesis = "John loves Mary".split() + + # Test BLEU to nth order of n-grams, where n is len(hypothesis). + for n in range(1, len(hypothesis)): + weights = (1.0 / n,) * n # Uniform weights. + assert sentence_bleu(references, hypothesis, weights) == 1.0 + + def test_partial_matches_hypothesis_longer_than_reference(self): + references = ["John loves Mary".split()] + hypothesis = "John loves Mary who loves Mike".split() + # Since no 4-grams matches were found the result should be zero + # exp(w_1 * 1 * w_2 * 1 * w_3 * 1 * w_4 * -inf) = 0 + self.assertAlmostEqual(sentence_bleu(references, hypothesis), 0.0, places=4) + # Checks that the warning has been raised because len(reference) < 4. + try: + self.assertWarns(UserWarning, sentence_bleu, references, hypothesis) + except AttributeError: + pass # unittest.TestCase.assertWarns is only supported in Python >= 3.2. + + +# @unittest.skip("Skipping fringe cases for BLEU.") +class TestBLEUFringeCases(unittest.TestCase): + def test_case_where_n_is_bigger_than_hypothesis_length(self): + # Test BLEU to nth order of n-grams, where n > len(hypothesis). + references = ["John loves Mary ?".split()] + hypothesis = "John loves Mary".split() + n = len(hypothesis) + 1 # + weights = (1.0 / n,) * n # Uniform weights. + # Since no n-grams matches were found the result should be zero + # exp(w_1 * 1 * w_2 * 1 * w_3 * 1 * w_4 * -inf) = 0 + self.assertAlmostEqual( + sentence_bleu(references, hypothesis, weights), 0.0, places=4 + ) + # Checks that the warning has been raised because len(hypothesis) < 4. + try: + self.assertWarns(UserWarning, sentence_bleu, references, hypothesis) + except AttributeError: + pass # unittest.TestCase.assertWarns is only supported in Python >= 3.2. + + # Test case where n > len(hypothesis) but so is n > len(reference), and + # it's a special case where reference == hypothesis. + references = ["John loves Mary".split()] + hypothesis = "John loves Mary".split() + # Since no 4-grams matches were found the result should be zero + # exp(w_1 * 1 * w_2 * 1 * w_3 * 1 * w_4 * -inf) = 0 + self.assertAlmostEqual( + sentence_bleu(references, hypothesis, weights), 0.0, places=4 + ) + + def test_empty_hypothesis(self): + # Test case where there's hypothesis is empty. + references = ["The candidate has no alignment to any of the references".split()] + hypothesis = [] + assert sentence_bleu(references, hypothesis) == 0 + + def test_length_one_hypothesis(self): + # Test case where there's hypothesis is of length 1 in Smoothing method 4. + references = ["The candidate has no alignment to any of the references".split()] + hypothesis = ["Foo"] + method4 = SmoothingFunction().method4 + try: + sentence_bleu(references, hypothesis, smoothing_function=method4) + except ValueError: + pass # unittest.TestCase.assertWarns is only supported in Python >= 3.2. + + def test_empty_references(self): + # Test case where there's reference is empty. + references = [[]] + hypothesis = "John loves Mary".split() + assert sentence_bleu(references, hypothesis) == 0 + + def test_empty_references_and_hypothesis(self): + # Test case where both references and hypothesis is empty. + references = [[]] + hypothesis = [] + assert sentence_bleu(references, hypothesis) == 0 + + def test_reference_or_hypothesis_shorter_than_fourgrams(self): + # Test case where the length of reference or hypothesis + # is shorter than 4. + references = ["let it go".split()] + hypothesis = "let go it".split() + # Checks that the value the hypothesis and reference returns is 0.0 + # exp(w_1 * 1 * w_2 * 1 * w_3 * 1 * w_4 * -inf) = 0 + self.assertAlmostEqual(sentence_bleu(references, hypothesis), 0.0, places=4) + # Checks that the warning has been raised. + try: + self.assertWarns(UserWarning, sentence_bleu, references, hypothesis) + except AttributeError: + pass # unittest.TestCase.assertWarns is only supported in Python >= 3.2. + + +class TestBLEUvsMteval13a(unittest.TestCase): + def test_corpus_bleu(self): + ref_file = find("models/wmt15_eval/ref.ru") + hyp_file = find("models/wmt15_eval/google.ru") + mteval_output_file = find("models/wmt15_eval/mteval-13a.output") + + # Reads the BLEU scores from the `mteval-13a.output` file. + # The order of the list corresponds to the order of the ngrams. + with open(mteval_output_file) as mteval_fin: + # The numbers are located in the last 2nd line of the file. + # The first and 2nd item in the list are the score and system names. + mteval_bleu_scores = map(float, mteval_fin.readlines()[-2].split()[1:-1]) + + with open(ref_file, encoding="utf8") as ref_fin: + with open(hyp_file, encoding="utf8") as hyp_fin: + # Whitespace tokenize the file. + # Note: split() automatically strip(). + hypothesis = list(map(lambda x: x.split(), hyp_fin)) + # Note that the corpus_bleu input is list of list of references. + references = list(map(lambda x: [x.split()], ref_fin)) + # Without smoothing. + for i, mteval_bleu in zip(range(1, 10), mteval_bleu_scores): + nltk_bleu = corpus_bleu( + references, hypothesis, weights=(1.0 / i,) * i + ) + # Check that the BLEU scores difference is less than 0.005 . + # Note: This is an approximate comparison; as much as + # +/- 0.01 BLEU might be "statistically significant", + # the actual translation quality might not be. + assert abs(mteval_bleu - nltk_bleu) < 0.005 + + # With the same smoothing method used in mteval-v13a.pl + chencherry = SmoothingFunction() + for i, mteval_bleu in zip(range(1, 10), mteval_bleu_scores): + nltk_bleu = corpus_bleu( + references, + hypothesis, + weights=(1.0 / i,) * i, + smoothing_function=chencherry.method3, + ) + assert abs(mteval_bleu - nltk_bleu) < 0.005 + + +class TestBLEUWithBadSentence(unittest.TestCase): + def test_corpus_bleu_with_bad_sentence(self): + hyp = "Teo S yb , oe uNb , R , T t , , t Tue Ar saln S , , 5istsi l , 5oe R ulO sae oR R" + ref = str( + "Their tasks include changing a pump on the faulty stokehold ." + "Likewise , two species that are very similar in morphology " + "were distinguished using genetics ." + ) + references = [[ref.split()]] + hypotheses = [hyp.split()] + try: # Check that the warning is raised since no. of 2-grams < 0. + with self.assertWarns(UserWarning): + # Verify that the BLEU output is undesired since no. of 2-grams < 0. + self.assertAlmostEqual( + corpus_bleu(references, hypotheses), 0.0, places=4 + ) + except AttributeError: # unittest.TestCase.assertWarns is only supported in Python >= 3.2. + self.assertAlmostEqual(corpus_bleu(references, hypotheses), 0.0, places=4) + + +class TestBLEUWithMultipleWeights(unittest.TestCase): + def test_corpus_bleu_with_multiple_weights(self): + hyp1 = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "which", + "ensures", + "that", + "the", + "military", + "always", + "obeys", + "the", + "commands", + "of", + "the", + "party", + ] + ref1a = [ + "It", + "is", + "a", + "guide", + "to", + "action", + "that", + "ensures", + "that", + "the", + "military", + "will", + "forever", + "heed", + "Party", + "commands", + ] + ref1b = [ + "It", + "is", + "the", + "guiding", + "principle", + "which", + "guarantees", + "the", + "military", + "forces", + "always", + "being", + "under", + "the", + "command", + "of", + "the", + "Party", + ] + ref1c = [ + "It", + "is", + "the", + "practical", + "guide", + "for", + "the", + "army", + "always", + "to", + "heed", + "the", + "directions", + "of", + "the", + "party", + ] + hyp2 = [ + "he", + "read", + "the", + "book", + "because", + "he", + "was", + "interested", + "in", + "world", + "history", + ] + ref2a = [ + "he", + "was", + "interested", + "in", + "world", + "history", + "because", + "he", + "read", + "the", + "book", + ] + weight_1 = (1, 0, 0, 0) + weight_2 = (0.25, 0.25, 0.25, 0.25) + weight_3 = (0, 0, 0, 0, 1) + + bleu_scores = corpus_bleu( + list_of_references=[[ref1a, ref1b, ref1c], [ref2a]], + hypotheses=[hyp1, hyp2], + weights=[weight_1, weight_2, weight_3], + ) + assert bleu_scores[0] == corpus_bleu( + [[ref1a, ref1b, ref1c], [ref2a]], [hyp1, hyp2], weight_1 + ) + assert bleu_scores[1] == corpus_bleu( + [[ref1a, ref1b, ref1c], [ref2a]], [hyp1, hyp2], weight_2 + ) + assert bleu_scores[2] == corpus_bleu( + [[ref1a, ref1b, ref1c], [ref2a]], [hyp1, hyp2], weight_3 + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_gdfa.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_gdfa.py new file mode 100644 index 0000000..7082043 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_gdfa.py @@ -0,0 +1,154 @@ +""" +Tests GDFA alignments +""" + +import unittest + +from nltk.translate.gdfa import grow_diag_final_and + + +class TestGDFA(unittest.TestCase): + def test_from_eflomal_outputs(self): + """ + Testing GDFA with first 10 eflomal outputs from issue #1829 + https://github.com/nltk/nltk/issues/1829 + """ + # Input. + forwards = [ + "0-0 1-2", + "0-0 1-1", + "0-0 2-1 3-2 4-3 5-4 6-5 7-6 8-7 7-8 9-9 10-10 9-11 11-12 12-13 13-14", + "0-0 1-1 1-2 2-3 3-4 4-5 4-6 5-7 6-8 8-9 9-10", + "0-0 14-1 15-2 16-3 20-5 21-6 22-7 5-8 6-9 7-10 8-11 9-12 10-13 11-14 12-15 13-16 14-17 17-18 18-19 19-20 20-21 23-22 24-23 25-24 26-25 27-27 28-28 29-29 30-30 31-31", + "0-0 1-1 0-2 2-3", + "0-0 2-2 4-4", + "0-0 1-1 2-3 3-4 5-5 7-6 8-7 9-8 10-9 11-10 12-11 13-12 14-13 15-14 16-16 17-17 18-18 19-19 20-20", + "3-0 4-1 6-2 5-3 6-4 7-5 8-6 9-7 10-8 11-9 16-10 9-12 10-13 12-14", + "1-0", + ] + backwards = [ + "0-0 1-2", + "0-0 1-1", + "0-0 2-1 3-2 4-3 5-4 6-5 7-6 8-7 9-8 10-10 11-12 12-11 13-13", + "0-0 1-2 2-3 3-4 4-6 6-8 7-5 8-7 9-8", + "0-0 1-8 2-9 3-10 4-11 5-12 6-11 8-13 9-14 10-15 11-16 12-17 13-18 14-19 15-20 16-21 17-22 18-23 19-24 20-29 21-30 22-31 23-2 24-3 25-4 26-5 27-5 28-6 29-7 30-28 31-31", + "0-0 1-1 2-3", + "0-0 1-1 2-3 4-4", + "0-0 1-1 2-3 3-4 5-5 7-6 8-7 9-8 10-9 11-10 12-11 13-12 14-13 15-14 16-16 17-17 18-18 19-19 20-16 21-18", + "0-0 1-1 3-2 4-1 5-3 6-4 7-5 8-6 9-7 10-8 11-9 12-8 13-9 14-8 15-9 16-10", + "1-0", + ] + source_lens = [2, 3, 3, 15, 11, 33, 4, 6, 23, 18] + target_lens = [2, 4, 3, 16, 12, 33, 5, 6, 22, 16] + # Expected Output. + expected = [ + [(0, 0), (1, 2)], + [(0, 0), (1, 1)], + [ + (0, 0), + (2, 1), + (3, 2), + (4, 3), + (5, 4), + (6, 5), + (7, 6), + (8, 7), + (10, 10), + (11, 12), + ], + [ + (0, 0), + (1, 1), + (1, 2), + (2, 3), + (3, 4), + (4, 5), + (4, 6), + (5, 7), + (6, 8), + (7, 5), + (8, 7), + (8, 9), + (9, 8), + (9, 10), + ], + [ + (0, 0), + (1, 8), + (2, 9), + (3, 10), + (4, 11), + (5, 8), + (6, 9), + (6, 11), + (7, 10), + (8, 11), + (31, 31), + ], + [(0, 0), (0, 2), (1, 1), (2, 3)], + [(0, 0), (1, 1), (2, 2), (2, 3), (4, 4)], + [ + (0, 0), + (1, 1), + (2, 3), + (3, 4), + (5, 5), + (7, 6), + (8, 7), + (9, 8), + (10, 9), + (11, 10), + (12, 11), + (13, 12), + (14, 13), + (15, 14), + (16, 16), + (17, 17), + (18, 18), + (19, 19), + ], + [ + (0, 0), + (1, 1), + (3, 0), + (3, 2), + (4, 1), + (5, 3), + (6, 2), + (6, 4), + (7, 5), + (8, 6), + (9, 7), + (9, 12), + (10, 8), + (10, 13), + (11, 9), + (12, 8), + (12, 14), + (13, 9), + (14, 8), + (15, 9), + (16, 10), + ], + [(1, 0)], + [ + (0, 0), + (1, 1), + (3, 2), + (4, 3), + (5, 4), + (6, 5), + (7, 6), + (9, 10), + (10, 12), + (11, 13), + (12, 14), + (13, 15), + ], + ] + + # Iterate through all 10 examples and check for expected outputs. + for fw, bw, src_len, trg_len, expect in zip( + forwards, backwards, source_lens, target_lens, expected + ): + self.assertListEqual(expect, grow_diag_final_and(src_len, trg_len, fw, bw)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm1.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm1.py new file mode 100644 index 0000000..1987d5f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm1.py @@ -0,0 +1,73 @@ +""" +Tests for IBM Model 1 training methods +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel, IBMModel1 +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel1(unittest.TestCase): + def test_set_uniform_translation_probabilities(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model1 = IBMModel1(corpus, 0) + + # act + model1.set_uniform_probabilities(corpus) + + # assert + # expected_prob = 1.0 / (target vocab size + 1) + self.assertEqual(model1.translation_table["ham"]["eier"], 1.0 / 3) + self.assertEqual(model1.translation_table["eggs"][None], 1.0 / 3) + + def test_set_uniform_translation_probabilities_of_non_domain_values(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model1 = IBMModel1(corpus, 0) + + # act + model1.set_uniform_probabilities(corpus) + + # assert + # examine target words that are not in the training data domain + self.assertEqual(model1.translation_table["parrot"]["eier"], IBMModel.MIN_PROB) + + def test_prob_t_a_given_s(self): + # arrange + src_sentence = ["ich", "esse", "ja", "gern", "räucherschinken"] + trg_sentence = ["i", "love", "to", "eat", "smoked", "ham"] + corpus = [AlignedSent(trg_sentence, src_sentence)] + alignment_info = AlignmentInfo( + (0, 1, 4, 0, 2, 5, 5), + [None] + src_sentence, + ["UNUSED"] + trg_sentence, + None, + ) + + translation_table = defaultdict(lambda: defaultdict(float)) + translation_table["i"]["ich"] = 0.98 + translation_table["love"]["gern"] = 0.98 + translation_table["to"][None] = 0.98 + translation_table["eat"]["esse"] = 0.98 + translation_table["smoked"]["räucherschinken"] = 0.98 + translation_table["ham"]["räucherschinken"] = 0.98 + + model1 = IBMModel1(corpus, 0) + model1.translation_table = translation_table + + # act + probability = model1.prob_t_a_given_s(alignment_info) + + # assert + lexical_translation = 0.98 * 0.98 * 0.98 * 0.98 * 0.98 * 0.98 + expected_probability = lexical_translation + self.assertEqual(round(probability, 4), round(expected_probability, 4)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm2.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm2.py new file mode 100644 index 0000000..bcc21f6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm2.py @@ -0,0 +1,86 @@ +""" +Tests for IBM Model 2 training methods +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel, IBMModel2 +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel2(unittest.TestCase): + def test_set_uniform_alignment_probabilities(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model2 = IBMModel2(corpus, 0) + + # act + model2.set_uniform_probabilities(corpus) + + # assert + # expected_prob = 1.0 / (length of source sentence + 1) + self.assertEqual(model2.alignment_table[0][1][3][2], 1.0 / 4) + self.assertEqual(model2.alignment_table[2][4][2][4], 1.0 / 3) + + def test_set_uniform_alignment_probabilities_of_non_domain_values(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model2 = IBMModel2(corpus, 0) + + # act + model2.set_uniform_probabilities(corpus) + + # assert + # examine i and j values that are not in the training data domain + self.assertEqual(model2.alignment_table[99][1][3][2], IBMModel.MIN_PROB) + self.assertEqual(model2.alignment_table[2][99][2][4], IBMModel.MIN_PROB) + + def test_prob_t_a_given_s(self): + # arrange + src_sentence = ["ich", "esse", "ja", "gern", "räucherschinken"] + trg_sentence = ["i", "love", "to", "eat", "smoked", "ham"] + corpus = [AlignedSent(trg_sentence, src_sentence)] + alignment_info = AlignmentInfo( + (0, 1, 4, 0, 2, 5, 5), + [None] + src_sentence, + ["UNUSED"] + trg_sentence, + None, + ) + + translation_table = defaultdict(lambda: defaultdict(float)) + translation_table["i"]["ich"] = 0.98 + translation_table["love"]["gern"] = 0.98 + translation_table["to"][None] = 0.98 + translation_table["eat"]["esse"] = 0.98 + translation_table["smoked"]["räucherschinken"] = 0.98 + translation_table["ham"]["räucherschinken"] = 0.98 + + alignment_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(float))) + ) + alignment_table[0][3][5][6] = 0.97 # None -> to + alignment_table[1][1][5][6] = 0.97 # ich -> i + alignment_table[2][4][5][6] = 0.97 # esse -> eat + alignment_table[4][2][5][6] = 0.97 # gern -> love + alignment_table[5][5][5][6] = 0.96 # räucherschinken -> smoked + alignment_table[5][6][5][6] = 0.96 # räucherschinken -> ham + + model2 = IBMModel2(corpus, 0) + model2.translation_table = translation_table + model2.alignment_table = alignment_table + + # act + probability = model2.prob_t_a_given_s(alignment_info) + + # assert + lexical_translation = 0.98 * 0.98 * 0.98 * 0.98 * 0.98 * 0.98 + alignment = 0.97 * 0.97 * 0.97 * 0.97 * 0.96 * 0.96 + expected_probability = lexical_translation * alignment + self.assertEqual(round(probability, 4), round(expected_probability, 4)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm3.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm3.py new file mode 100644 index 0000000..3e69211 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm3.py @@ -0,0 +1,105 @@ +""" +Tests for IBM Model 3 training methods +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel, IBMModel3 +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel3(unittest.TestCase): + def test_set_uniform_distortion_probabilities(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model3 = IBMModel3(corpus, 0) + + # act + model3.set_uniform_probabilities(corpus) + + # assert + # expected_prob = 1.0 / length of target sentence + self.assertEqual(model3.distortion_table[1][0][3][2], 1.0 / 2) + self.assertEqual(model3.distortion_table[4][2][2][4], 1.0 / 4) + + def test_set_uniform_distortion_probabilities_of_non_domain_values(self): + # arrange + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model3 = IBMModel3(corpus, 0) + + # act + model3.set_uniform_probabilities(corpus) + + # assert + # examine i and j values that are not in the training data domain + self.assertEqual(model3.distortion_table[0][0][3][2], IBMModel.MIN_PROB) + self.assertEqual(model3.distortion_table[9][2][2][4], IBMModel.MIN_PROB) + self.assertEqual(model3.distortion_table[2][9][2][4], IBMModel.MIN_PROB) + + def test_prob_t_a_given_s(self): + # arrange + src_sentence = ["ich", "esse", "ja", "gern", "räucherschinken"] + trg_sentence = ["i", "love", "to", "eat", "smoked", "ham"] + corpus = [AlignedSent(trg_sentence, src_sentence)] + alignment_info = AlignmentInfo( + (0, 1, 4, 0, 2, 5, 5), + [None] + src_sentence, + ["UNUSED"] + trg_sentence, + [[3], [1], [4], [], [2], [5, 6]], + ) + + distortion_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(float))) + ) + distortion_table[1][1][5][6] = 0.97 # i -> ich + distortion_table[2][4][5][6] = 0.97 # love -> gern + distortion_table[3][0][5][6] = 0.97 # to -> NULL + distortion_table[4][2][5][6] = 0.97 # eat -> esse + distortion_table[5][5][5][6] = 0.97 # smoked -> räucherschinken + distortion_table[6][5][5][6] = 0.97 # ham -> räucherschinken + + translation_table = defaultdict(lambda: defaultdict(float)) + translation_table["i"]["ich"] = 0.98 + translation_table["love"]["gern"] = 0.98 + translation_table["to"][None] = 0.98 + translation_table["eat"]["esse"] = 0.98 + translation_table["smoked"]["räucherschinken"] = 0.98 + translation_table["ham"]["räucherschinken"] = 0.98 + + fertility_table = defaultdict(lambda: defaultdict(float)) + fertility_table[1]["ich"] = 0.99 + fertility_table[1]["esse"] = 0.99 + fertility_table[0]["ja"] = 0.99 + fertility_table[1]["gern"] = 0.99 + fertility_table[2]["räucherschinken"] = 0.999 + fertility_table[1][None] = 0.99 + + probabilities = { + "p1": 0.167, + "translation_table": translation_table, + "distortion_table": distortion_table, + "fertility_table": fertility_table, + "alignment_table": None, + } + + model3 = IBMModel3(corpus, 0, probabilities) + + # act + probability = model3.prob_t_a_given_s(alignment_info) + + # assert + null_generation = 5 * pow(0.167, 1) * pow(0.833, 4) + fertility = 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 2 * 0.999 + lexical_translation = 0.98 * 0.98 * 0.98 * 0.98 * 0.98 * 0.98 + distortion = 0.97 * 0.97 * 0.97 * 0.97 * 0.97 * 0.97 + expected_probability = ( + null_generation * fertility * lexical_translation * distortion + ) + self.assertEqual(round(probability, 4), round(expected_probability, 4)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm4.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm4.py new file mode 100644 index 0000000..d80017b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm4.py @@ -0,0 +1,120 @@ +""" +Tests for IBM Model 4 training methods +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel, IBMModel4 +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel4(unittest.TestCase): + def test_set_uniform_distortion_probabilities_of_max_displacements(self): + # arrange + src_classes = {"schinken": 0, "eier": 0, "spam": 1} + trg_classes = {"ham": 0, "eggs": 1, "spam": 2} + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model4 = IBMModel4(corpus, 0, src_classes, trg_classes) + + # act + model4.set_uniform_probabilities(corpus) + + # assert + # number of displacement values = + # 2 *(number of words in longest target sentence - 1) + expected_prob = 1.0 / (2 * (4 - 1)) + + # examine the boundary values for (displacement, src_class, trg_class) + self.assertEqual(model4.head_distortion_table[3][0][0], expected_prob) + self.assertEqual(model4.head_distortion_table[-3][1][2], expected_prob) + self.assertEqual(model4.non_head_distortion_table[3][0], expected_prob) + self.assertEqual(model4.non_head_distortion_table[-3][2], expected_prob) + + def test_set_uniform_distortion_probabilities_of_non_domain_values(self): + # arrange + src_classes = {"schinken": 0, "eier": 0, "spam": 1} + trg_classes = {"ham": 0, "eggs": 1, "spam": 2} + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model4 = IBMModel4(corpus, 0, src_classes, trg_classes) + + # act + model4.set_uniform_probabilities(corpus) + + # assert + # examine displacement values that are not in the training data domain + self.assertEqual(model4.head_distortion_table[4][0][0], IBMModel.MIN_PROB) + self.assertEqual(model4.head_distortion_table[100][1][2], IBMModel.MIN_PROB) + self.assertEqual(model4.non_head_distortion_table[4][0], IBMModel.MIN_PROB) + self.assertEqual(model4.non_head_distortion_table[100][2], IBMModel.MIN_PROB) + + def test_prob_t_a_given_s(self): + # arrange + src_sentence = ["ich", "esse", "ja", "gern", "räucherschinken"] + trg_sentence = ["i", "love", "to", "eat", "smoked", "ham"] + src_classes = {"räucherschinken": 0, "ja": 1, "ich": 2, "esse": 3, "gern": 4} + trg_classes = {"ham": 0, "smoked": 1, "i": 3, "love": 4, "to": 2, "eat": 4} + corpus = [AlignedSent(trg_sentence, src_sentence)] + alignment_info = AlignmentInfo( + (0, 1, 4, 0, 2, 5, 5), + [None] + src_sentence, + ["UNUSED"] + trg_sentence, + [[3], [1], [4], [], [2], [5, 6]], + ) + + head_distortion_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(float)) + ) + head_distortion_table[1][None][3] = 0.97 # None, i + head_distortion_table[3][2][4] = 0.97 # ich, eat + head_distortion_table[-2][3][4] = 0.97 # esse, love + head_distortion_table[3][4][1] = 0.97 # gern, smoked + + non_head_distortion_table = defaultdict(lambda: defaultdict(float)) + non_head_distortion_table[1][0] = 0.96 # ham + + translation_table = defaultdict(lambda: defaultdict(float)) + translation_table["i"]["ich"] = 0.98 + translation_table["love"]["gern"] = 0.98 + translation_table["to"][None] = 0.98 + translation_table["eat"]["esse"] = 0.98 + translation_table["smoked"]["räucherschinken"] = 0.98 + translation_table["ham"]["räucherschinken"] = 0.98 + + fertility_table = defaultdict(lambda: defaultdict(float)) + fertility_table[1]["ich"] = 0.99 + fertility_table[1]["esse"] = 0.99 + fertility_table[0]["ja"] = 0.99 + fertility_table[1]["gern"] = 0.99 + fertility_table[2]["räucherschinken"] = 0.999 + fertility_table[1][None] = 0.99 + + probabilities = { + "p1": 0.167, + "translation_table": translation_table, + "head_distortion_table": head_distortion_table, + "non_head_distortion_table": non_head_distortion_table, + "fertility_table": fertility_table, + "alignment_table": None, + } + + model4 = IBMModel4(corpus, 0, src_classes, trg_classes, probabilities) + + # act + probability = model4.prob_t_a_given_s(alignment_info) + + # assert + null_generation = 5 * pow(0.167, 1) * pow(0.833, 4) + fertility = 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 2 * 0.999 + lexical_translation = 0.98 * 0.98 * 0.98 * 0.98 * 0.98 * 0.98 + distortion = 0.97 * 0.97 * 1 * 0.97 * 0.97 * 0.96 + expected_probability = ( + null_generation * fertility * lexical_translation * distortion + ) + self.assertEqual(round(probability, 4), round(expected_probability, 4)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm5.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm5.py new file mode 100644 index 0000000..8df28a3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm5.py @@ -0,0 +1,160 @@ +""" +Tests for IBM Model 5 training methods +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel, IBMModel4, IBMModel5 +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel5(unittest.TestCase): + def test_set_uniform_vacancy_probabilities_of_max_displacements(self): + # arrange + src_classes = {"schinken": 0, "eier": 0, "spam": 1} + trg_classes = {"ham": 0, "eggs": 1, "spam": 2} + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model5 = IBMModel5(corpus, 0, src_classes, trg_classes) + + # act + model5.set_uniform_probabilities(corpus) + + # assert + # number of vacancy difference values = + # 2 * number of words in longest target sentence + expected_prob = 1.0 / (2 * 4) + + # examine the boundary values for (dv, max_v, trg_class) + self.assertEqual(model5.head_vacancy_table[4][4][0], expected_prob) + self.assertEqual(model5.head_vacancy_table[-3][1][2], expected_prob) + self.assertEqual(model5.non_head_vacancy_table[4][4][0], expected_prob) + self.assertEqual(model5.non_head_vacancy_table[-3][1][2], expected_prob) + + def test_set_uniform_vacancy_probabilities_of_non_domain_values(self): + # arrange + src_classes = {"schinken": 0, "eier": 0, "spam": 1} + trg_classes = {"ham": 0, "eggs": 1, "spam": 2} + corpus = [ + AlignedSent(["ham", "eggs"], ["schinken", "schinken", "eier"]), + AlignedSent(["spam", "spam", "spam", "spam"], ["spam", "spam"]), + ] + model5 = IBMModel5(corpus, 0, src_classes, trg_classes) + + # act + model5.set_uniform_probabilities(corpus) + + # assert + # examine dv and max_v values that are not in the training data domain + self.assertEqual(model5.head_vacancy_table[5][4][0], IBMModel.MIN_PROB) + self.assertEqual(model5.head_vacancy_table[-4][1][2], IBMModel.MIN_PROB) + self.assertEqual(model5.head_vacancy_table[4][0][0], IBMModel.MIN_PROB) + self.assertEqual(model5.non_head_vacancy_table[5][4][0], IBMModel.MIN_PROB) + self.assertEqual(model5.non_head_vacancy_table[-4][1][2], IBMModel.MIN_PROB) + + def test_prob_t_a_given_s(self): + # arrange + src_sentence = ["ich", "esse", "ja", "gern", "räucherschinken"] + trg_sentence = ["i", "love", "to", "eat", "smoked", "ham"] + src_classes = {"räucherschinken": 0, "ja": 1, "ich": 2, "esse": 3, "gern": 4} + trg_classes = {"ham": 0, "smoked": 1, "i": 3, "love": 4, "to": 2, "eat": 4} + corpus = [AlignedSent(trg_sentence, src_sentence)] + alignment_info = AlignmentInfo( + (0, 1, 4, 0, 2, 5, 5), + [None] + src_sentence, + ["UNUSED"] + trg_sentence, + [[3], [1], [4], [], [2], [5, 6]], + ) + + head_vacancy_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(float)) + ) + head_vacancy_table[1 - 0][6][3] = 0.97 # ich -> i + head_vacancy_table[3 - 0][5][4] = 0.97 # esse -> eat + head_vacancy_table[1 - 2][4][4] = 0.97 # gern -> love + head_vacancy_table[2 - 0][2][1] = 0.97 # räucherschinken -> smoked + + non_head_vacancy_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(float)) + ) + non_head_vacancy_table[1 - 0][1][0] = 0.96 # räucherschinken -> ham + + translation_table = defaultdict(lambda: defaultdict(float)) + translation_table["i"]["ich"] = 0.98 + translation_table["love"]["gern"] = 0.98 + translation_table["to"][None] = 0.98 + translation_table["eat"]["esse"] = 0.98 + translation_table["smoked"]["räucherschinken"] = 0.98 + translation_table["ham"]["räucherschinken"] = 0.98 + + fertility_table = defaultdict(lambda: defaultdict(float)) + fertility_table[1]["ich"] = 0.99 + fertility_table[1]["esse"] = 0.99 + fertility_table[0]["ja"] = 0.99 + fertility_table[1]["gern"] = 0.99 + fertility_table[2]["räucherschinken"] = 0.999 + fertility_table[1][None] = 0.99 + + probabilities = { + "p1": 0.167, + "translation_table": translation_table, + "fertility_table": fertility_table, + "head_vacancy_table": head_vacancy_table, + "non_head_vacancy_table": non_head_vacancy_table, + "head_distortion_table": None, + "non_head_distortion_table": None, + "alignment_table": None, + } + + model5 = IBMModel5(corpus, 0, src_classes, trg_classes, probabilities) + + # act + probability = model5.prob_t_a_given_s(alignment_info) + + # assert + null_generation = 5 * pow(0.167, 1) * pow(0.833, 4) + fertility = 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 1 * 0.99 * 2 * 0.999 + lexical_translation = 0.98 * 0.98 * 0.98 * 0.98 * 0.98 * 0.98 + vacancy = 0.97 * 0.97 * 1 * 0.97 * 0.97 * 0.96 + expected_probability = ( + null_generation * fertility * lexical_translation * vacancy + ) + self.assertEqual(round(probability, 4), round(expected_probability, 4)) + + def test_prune(self): + # arrange + alignment_infos = [ + AlignmentInfo((1, 1), None, None, None), + AlignmentInfo((1, 2), None, None, None), + AlignmentInfo((2, 1), None, None, None), + AlignmentInfo((2, 2), None, None, None), + AlignmentInfo((0, 0), None, None, None), + ] + min_factor = IBMModel5.MIN_SCORE_FACTOR + best_score = 0.9 + scores = { + (1, 1): min(min_factor * 1.5, 1) * best_score, # above threshold + (1, 2): best_score, + (2, 1): min_factor * best_score, # at threshold + (2, 2): min_factor * best_score * 0.5, # low score + (0, 0): min(min_factor * 1.1, 1) * 1.2, # above threshold + } + corpus = [AlignedSent(["a"], ["b"])] + original_prob_function = IBMModel4.model4_prob_t_a_given_s + # mock static method + IBMModel4.model4_prob_t_a_given_s = staticmethod( + lambda a, model: scores[a.alignment] + ) + model5 = IBMModel5(corpus, 0, None, None) + + # act + pruned_alignments = model5.prune(alignment_infos) + + # assert + self.assertEqual(len(pruned_alignments), 3) + + # restore static method + IBMModel4.model4_prob_t_a_given_s = original_prob_function diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm_model.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm_model.py new file mode 100644 index 0000000..ab17112 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_ibm_model.py @@ -0,0 +1,269 @@ +""" +Tests for common methods of IBM translation models +""" + +import unittest +from collections import defaultdict + +from nltk.translate import AlignedSent, IBMModel +from nltk.translate.ibm_model import AlignmentInfo + + +class TestIBMModel(unittest.TestCase): + __TEST_SRC_SENTENCE = ["j'", "aime", "bien", "jambon"] + __TEST_TRG_SENTENCE = ["i", "love", "ham"] + + def test_vocabularies_are_initialized(self): + parallel_corpora = [ + AlignedSent(["one", "two", "three", "four"], ["un", "deux", "trois"]), + AlignedSent(["five", "one", "six"], ["quatre", "cinq", "six"]), + AlignedSent([], ["sept"]), + ] + + ibm_model = IBMModel(parallel_corpora) + self.assertEqual(len(ibm_model.src_vocab), 8) + self.assertEqual(len(ibm_model.trg_vocab), 6) + + def test_vocabularies_are_initialized_even_with_empty_corpora(self): + parallel_corpora = [] + + ibm_model = IBMModel(parallel_corpora) + self.assertEqual(len(ibm_model.src_vocab), 1) # addition of NULL token + self.assertEqual(len(ibm_model.trg_vocab), 0) + + def test_best_model2_alignment(self): + # arrange + sentence_pair = AlignedSent( + TestIBMModel.__TEST_TRG_SENTENCE, TestIBMModel.__TEST_SRC_SENTENCE + ) + # None and 'bien' have zero fertility + translation_table = { + "i": {"j'": 0.9, "aime": 0.05, "bien": 0.02, "jambon": 0.03, None: 0}, + "love": {"j'": 0.05, "aime": 0.9, "bien": 0.01, "jambon": 0.01, None: 0.03}, + "ham": {"j'": 0, "aime": 0.01, "bien": 0, "jambon": 0.99, None: 0}, + } + alignment_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0.2))) + ) + + ibm_model = IBMModel([]) + ibm_model.translation_table = translation_table + ibm_model.alignment_table = alignment_table + + # act + a_info = ibm_model.best_model2_alignment(sentence_pair) + + # assert + self.assertEqual(a_info.alignment[1:], (1, 2, 4)) # 0th element unused + self.assertEqual(a_info.cepts, [[], [1], [2], [], [3]]) + + def test_best_model2_alignment_does_not_change_pegged_alignment(self): + # arrange + sentence_pair = AlignedSent( + TestIBMModel.__TEST_TRG_SENTENCE, TestIBMModel.__TEST_SRC_SENTENCE + ) + translation_table = { + "i": {"j'": 0.9, "aime": 0.05, "bien": 0.02, "jambon": 0.03, None: 0}, + "love": {"j'": 0.05, "aime": 0.9, "bien": 0.01, "jambon": 0.01, None: 0.03}, + "ham": {"j'": 0, "aime": 0.01, "bien": 0, "jambon": 0.99, None: 0}, + } + alignment_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0.2))) + ) + + ibm_model = IBMModel([]) + ibm_model.translation_table = translation_table + ibm_model.alignment_table = alignment_table + + # act: force 'love' to be pegged to 'jambon' + a_info = ibm_model.best_model2_alignment(sentence_pair, 2, 4) + # assert + self.assertEqual(a_info.alignment[1:], (1, 4, 4)) + self.assertEqual(a_info.cepts, [[], [1], [], [], [2, 3]]) + + def test_best_model2_alignment_handles_fertile_words(self): + # arrange + sentence_pair = AlignedSent( + ["i", "really", ",", "really", "love", "ham"], + TestIBMModel.__TEST_SRC_SENTENCE, + ) + # 'bien' produces 2 target words: 'really' and another 'really' + translation_table = { + "i": {"j'": 0.9, "aime": 0.05, "bien": 0.02, "jambon": 0.03, None: 0}, + "really": {"j'": 0, "aime": 0, "bien": 0.9, "jambon": 0.01, None: 0.09}, + ",": {"j'": 0, "aime": 0, "bien": 0.3, "jambon": 0, None: 0.7}, + "love": {"j'": 0.05, "aime": 0.9, "bien": 0.01, "jambon": 0.01, None: 0.03}, + "ham": {"j'": 0, "aime": 0.01, "bien": 0, "jambon": 0.99, None: 0}, + } + alignment_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0.2))) + ) + + ibm_model = IBMModel([]) + ibm_model.translation_table = translation_table + ibm_model.alignment_table = alignment_table + + # act + a_info = ibm_model.best_model2_alignment(sentence_pair) + + # assert + self.assertEqual(a_info.alignment[1:], (1, 3, 0, 3, 2, 4)) + self.assertEqual(a_info.cepts, [[3], [1], [5], [2, 4], [6]]) + + def test_best_model2_alignment_handles_empty_src_sentence(self): + # arrange + sentence_pair = AlignedSent(TestIBMModel.__TEST_TRG_SENTENCE, []) + ibm_model = IBMModel([]) + + # act + a_info = ibm_model.best_model2_alignment(sentence_pair) + + # assert + self.assertEqual(a_info.alignment[1:], (0, 0, 0)) + self.assertEqual(a_info.cepts, [[1, 2, 3]]) + + def test_best_model2_alignment_handles_empty_trg_sentence(self): + # arrange + sentence_pair = AlignedSent([], TestIBMModel.__TEST_SRC_SENTENCE) + ibm_model = IBMModel([]) + + # act + a_info = ibm_model.best_model2_alignment(sentence_pair) + + # assert + self.assertEqual(a_info.alignment[1:], ()) + self.assertEqual(a_info.cepts, [[], [], [], [], []]) + + def test_neighboring_finds_neighbor_alignments(self): + # arrange + a_info = AlignmentInfo( + (0, 3, 2), + (None, "des", "œufs", "verts"), + ("UNUSED", "green", "eggs"), + [[], [], [2], [1]], + ) + ibm_model = IBMModel([]) + + # act + neighbors = ibm_model.neighboring(a_info) + + # assert + neighbor_alignments = set() + for neighbor in neighbors: + neighbor_alignments.add(neighbor.alignment) + expected_alignments = { + # moves + (0, 0, 2), + (0, 1, 2), + (0, 2, 2), + (0, 3, 0), + (0, 3, 1), + (0, 3, 3), + # swaps + (0, 2, 3), + # original alignment + (0, 3, 2), + } + self.assertEqual(neighbor_alignments, expected_alignments) + + def test_neighboring_sets_neighbor_alignment_info(self): + # arrange + a_info = AlignmentInfo( + (0, 3, 2), + (None, "des", "œufs", "verts"), + ("UNUSED", "green", "eggs"), + [[], [], [2], [1]], + ) + ibm_model = IBMModel([]) + + # act + neighbors = ibm_model.neighboring(a_info) + + # assert: select a few particular alignments + for neighbor in neighbors: + if neighbor.alignment == (0, 2, 2): + moved_alignment = neighbor + elif neighbor.alignment == (0, 3, 2): + swapped_alignment = neighbor + + self.assertEqual(moved_alignment.cepts, [[], [], [1, 2], []]) + self.assertEqual(swapped_alignment.cepts, [[], [], [2], [1]]) + + def test_neighboring_returns_neighbors_with_pegged_alignment(self): + # arrange + a_info = AlignmentInfo( + (0, 3, 2), + (None, "des", "œufs", "verts"), + ("UNUSED", "green", "eggs"), + [[], [], [2], [1]], + ) + ibm_model = IBMModel([]) + + # act: peg 'eggs' to align with 'œufs' + neighbors = ibm_model.neighboring(a_info, 2) + + # assert + neighbor_alignments = set() + for neighbor in neighbors: + neighbor_alignments.add(neighbor.alignment) + expected_alignments = { + # moves + (0, 0, 2), + (0, 1, 2), + (0, 2, 2), + # no swaps + # original alignment + (0, 3, 2), + } + self.assertEqual(neighbor_alignments, expected_alignments) + + def test_hillclimb(self): + # arrange + initial_alignment = AlignmentInfo((0, 3, 2), None, None, None) + + def neighboring_mock(a, j): + if a.alignment == (0, 3, 2): + return { + AlignmentInfo((0, 2, 2), None, None, None), + AlignmentInfo((0, 1, 1), None, None, None), + } + elif a.alignment == (0, 2, 2): + return { + AlignmentInfo((0, 3, 3), None, None, None), + AlignmentInfo((0, 4, 4), None, None, None), + } + return set() + + def prob_t_a_given_s_mock(a): + prob_values = { + (0, 3, 2): 0.5, + (0, 2, 2): 0.6, + (0, 1, 1): 0.4, + (0, 3, 3): 0.6, + (0, 4, 4): 0.7, + } + return prob_values.get(a.alignment, 0.01) + + ibm_model = IBMModel([]) + ibm_model.neighboring = neighboring_mock + ibm_model.prob_t_a_given_s = prob_t_a_given_s_mock + + # act + best_alignment = ibm_model.hillclimb(initial_alignment) + + # assert: hill climbing goes from (0, 3, 2) -> (0, 2, 2) -> (0, 4, 4) + self.assertEqual(best_alignment.alignment, (0, 4, 4)) + + def test_sample(self): + # arrange + sentence_pair = AlignedSent( + TestIBMModel.__TEST_TRG_SENTENCE, TestIBMModel.__TEST_SRC_SENTENCE + ) + ibm_model = IBMModel([]) + ibm_model.prob_t_a_given_s = lambda x: 0.001 + + # act + samples, best_alignment = ibm_model.sample(sentence_pair) + + # assert + self.assertEqual(len(samples), 61) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_meteor.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_meteor.py new file mode 100644 index 0000000..b7b9f89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_meteor.py @@ -0,0 +1,20 @@ +import unittest + +from nltk.translate.meteor_score import meteor_score + + +class TestMETEOR(unittest.TestCase): + reference = [["this", "is", "a", "test"], ["this", "is" "test"]] + candidate = ["THIS", "Is", "a", "tEST"] + + def test_meteor(self): + score = meteor_score(self.reference, self.candidate, preprocess=str.lower) + assert score == 0.9921875 + + def test_reference_type_check(self): + str_reference = [" ".join(ref) for ref in self.reference] + self.assertRaises(TypeError, meteor_score, str_reference, self.candidate) + + def test_candidate_type_check(self): + str_candidate = " ".join(self.candidate) + self.assertRaises(TypeError, meteor_score, self.reference, str_candidate) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_nist.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_nist.py new file mode 100644 index 0000000..817aaae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_nist.py @@ -0,0 +1,36 @@ +""" +Tests for NIST translation evaluation metric +""" + +import io +import unittest + +from nltk.data import find +from nltk.translate.nist_score import corpus_nist + + +class TestNIST(unittest.TestCase): + def test_sentence_nist(self): + ref_file = find("models/wmt15_eval/ref.ru") + hyp_file = find("models/wmt15_eval/google.ru") + mteval_output_file = find("models/wmt15_eval/mteval-13a.output") + + # Reads the NIST scores from the `mteval-13a.output` file. + # The order of the list corresponds to the order of the ngrams. + with open(mteval_output_file) as mteval_fin: + # The numbers are located in the last 4th line of the file. + # The first and 2nd item in the list are the score and system names. + mteval_nist_scores = map(float, mteval_fin.readlines()[-4].split()[1:-1]) + + with open(ref_file, encoding="utf8") as ref_fin: + with open(hyp_file, encoding="utf8") as hyp_fin: + # Whitespace tokenize the file. + # Note: split() automatically strip(). + hypotheses = list(map(lambda x: x.split(), hyp_fin)) + # Note that the corpus_bleu input is list of list of references. + references = list(map(lambda x: [x.split()], ref_fin)) + # Without smoothing. + for i, mteval_nist in zip(range(1, 10), mteval_nist_scores): + nltk_nist = corpus_nist(references, hypotheses, i) + # Check that the NIST scores difference is less than 0.5 + assert abs(mteval_nist - nltk_nist) < 0.05 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_stack_decoder.py b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_stack_decoder.py new file mode 100644 index 0000000..6af0c31 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/unit/translate/test_stack_decoder.py @@ -0,0 +1,294 @@ +# Natural Language Toolkit: Stack decoder +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +Tests for stack decoder +""" + +import unittest +from collections import defaultdict +from math import log + +from nltk.translate import PhraseTable, StackDecoder +from nltk.translate.stack_decoder import _Hypothesis, _Stack + + +class TestStackDecoder(unittest.TestCase): + def test_find_all_src_phrases(self): + # arrange + phrase_table = TestStackDecoder.create_fake_phrase_table() + stack_decoder = StackDecoder(phrase_table, None) + sentence = ("my", "hovercraft", "is", "full", "of", "eels") + + # act + src_phrase_spans = stack_decoder.find_all_src_phrases(sentence) + + # assert + self.assertEqual(src_phrase_spans[0], [2]) # 'my hovercraft' + self.assertEqual(src_phrase_spans[1], [2]) # 'hovercraft' + self.assertEqual(src_phrase_spans[2], [3]) # 'is' + self.assertEqual(src_phrase_spans[3], [5, 6]) # 'full of', 'full of eels' + self.assertFalse(src_phrase_spans[4]) # no entry starting with 'of' + self.assertEqual(src_phrase_spans[5], [6]) # 'eels' + + def test_distortion_score(self): + # arrange + stack_decoder = StackDecoder(None, None) + stack_decoder.distortion_factor = 0.5 + hypothesis = _Hypothesis() + hypothesis.src_phrase_span = (3, 5) + + # act + score = stack_decoder.distortion_score(hypothesis, (8, 10)) + + # assert + expected_score = log(stack_decoder.distortion_factor) * (8 - 5) + self.assertEqual(score, expected_score) + + def test_distortion_score_of_first_expansion(self): + # arrange + stack_decoder = StackDecoder(None, None) + stack_decoder.distortion_factor = 0.5 + hypothesis = _Hypothesis() + + # act + score = stack_decoder.distortion_score(hypothesis, (8, 10)) + + # assert + # expansion from empty hypothesis always has zero distortion cost + self.assertEqual(score, 0.0) + + def test_compute_future_costs(self): + # arrange + phrase_table = TestStackDecoder.create_fake_phrase_table() + language_model = TestStackDecoder.create_fake_language_model() + stack_decoder = StackDecoder(phrase_table, language_model) + sentence = ("my", "hovercraft", "is", "full", "of", "eels") + + # act + future_scores = stack_decoder.compute_future_scores(sentence) + + # assert + self.assertEqual( + future_scores[1][2], + ( + phrase_table.translations_for(("hovercraft",))[0].log_prob + + language_model.probability(("hovercraft",)) + ), + ) + self.assertEqual( + future_scores[0][2], + ( + phrase_table.translations_for(("my", "hovercraft"))[0].log_prob + + language_model.probability(("my", "hovercraft")) + ), + ) + + def test_compute_future_costs_for_phrases_not_in_phrase_table(self): + # arrange + phrase_table = TestStackDecoder.create_fake_phrase_table() + language_model = TestStackDecoder.create_fake_language_model() + stack_decoder = StackDecoder(phrase_table, language_model) + sentence = ("my", "hovercraft", "is", "full", "of", "eels") + + # act + future_scores = stack_decoder.compute_future_scores(sentence) + + # assert + self.assertEqual( + future_scores[1][3], # 'hovercraft is' is not in phrase table + future_scores[1][2] + future_scores[2][3], + ) # backoff + + def test_future_score(self): + # arrange: sentence with 8 words; words 2, 3, 4 already translated + hypothesis = _Hypothesis() + hypothesis.untranslated_spans = lambda _: [(0, 2), (5, 8)] # mock + future_score_table = defaultdict(lambda: defaultdict(float)) + future_score_table[0][2] = 0.4 + future_score_table[5][8] = 0.5 + stack_decoder = StackDecoder(None, None) + + # act + future_score = stack_decoder.future_score(hypothesis, future_score_table, 8) + + # assert + self.assertEqual(future_score, 0.4 + 0.5) + + def test_valid_phrases(self): + # arrange + hypothesis = _Hypothesis() + # mock untranslated_spans method + hypothesis.untranslated_spans = lambda _: [(0, 2), (3, 6)] + all_phrases_from = [[1, 4], [2], [], [5], [5, 6, 7], [], [7]] + + # act + phrase_spans = StackDecoder.valid_phrases(all_phrases_from, hypothesis) + + # assert + self.assertEqual(phrase_spans, [(0, 1), (1, 2), (3, 5), (4, 5), (4, 6)]) + + @staticmethod + def create_fake_phrase_table(): + phrase_table = PhraseTable() + phrase_table.add(("hovercraft",), ("",), 0.8) + phrase_table.add(("my", "hovercraft"), ("", ""), 0.7) + phrase_table.add(("my", "cheese"), ("", ""), 0.7) + phrase_table.add(("is",), ("",), 0.8) + phrase_table.add(("is",), ("",), 0.5) + phrase_table.add(("full", "of"), ("", ""), 0.01) + phrase_table.add(("full", "of", "eels"), ("", "", ""), 0.5) + phrase_table.add(("full", "of", "spam"), ("", ""), 0.5) + phrase_table.add(("eels",), ("",), 0.5) + phrase_table.add(("spam",), ("",), 0.5) + return phrase_table + + @staticmethod + def create_fake_language_model(): + # nltk.model should be used here once it is implemented + language_prob = defaultdict(lambda: -999.0) + language_prob[("my",)] = log(0.1) + language_prob[("hovercraft",)] = log(0.1) + language_prob[("is",)] = log(0.1) + language_prob[("full",)] = log(0.1) + language_prob[("of",)] = log(0.1) + language_prob[("eels",)] = log(0.1) + language_prob[("my", "hovercraft")] = log(0.3) + language_model = type( + "", (object,), {"probability": lambda _, phrase: language_prob[phrase]} + )() + return language_model + + +class TestHypothesis(unittest.TestCase): + def setUp(self): + root = _Hypothesis() + child = _Hypothesis( + raw_score=0.5, + src_phrase_span=(3, 7), + trg_phrase=("hello", "world"), + previous=root, + ) + grandchild = _Hypothesis( + raw_score=0.4, + src_phrase_span=(1, 2), + trg_phrase=("and", "goodbye"), + previous=child, + ) + self.hypothesis_chain = grandchild + + def test_translation_so_far(self): + # act + translation = self.hypothesis_chain.translation_so_far() + + # assert + self.assertEqual(translation, ["hello", "world", "and", "goodbye"]) + + def test_translation_so_far_for_empty_hypothesis(self): + # arrange + hypothesis = _Hypothesis() + + # act + translation = hypothesis.translation_so_far() + + # assert + self.assertEqual(translation, []) + + def test_total_translated_words(self): + # act + total_translated_words = self.hypothesis_chain.total_translated_words() + + # assert + self.assertEqual(total_translated_words, 5) + + def test_translated_positions(self): + # act + translated_positions = self.hypothesis_chain.translated_positions() + + # assert + translated_positions.sort() + self.assertEqual(translated_positions, [1, 3, 4, 5, 6]) + + def test_untranslated_spans(self): + # act + untranslated_spans = self.hypothesis_chain.untranslated_spans(10) + + # assert + self.assertEqual(untranslated_spans, [(0, 1), (2, 3), (7, 10)]) + + def test_untranslated_spans_for_empty_hypothesis(self): + # arrange + hypothesis = _Hypothesis() + + # act + untranslated_spans = hypothesis.untranslated_spans(10) + + # assert + self.assertEqual(untranslated_spans, [(0, 10)]) + + +class TestStack(unittest.TestCase): + def test_push_bumps_off_worst_hypothesis_when_stack_is_full(self): + # arrange + stack = _Stack(3) + poor_hypothesis = _Hypothesis(0.01) + + # act + stack.push(_Hypothesis(0.2)) + stack.push(poor_hypothesis) + stack.push(_Hypothesis(0.1)) + stack.push(_Hypothesis(0.3)) + + # assert + self.assertFalse(poor_hypothesis in stack) + + def test_push_removes_hypotheses_that_fall_below_beam_threshold(self): + # arrange + stack = _Stack(3, 0.5) + poor_hypothesis = _Hypothesis(0.01) + worse_hypothesis = _Hypothesis(0.009) + + # act + stack.push(poor_hypothesis) + stack.push(worse_hypothesis) + stack.push(_Hypothesis(0.9)) # greatly superior hypothesis + + # assert + self.assertFalse(poor_hypothesis in stack) + self.assertFalse(worse_hypothesis in stack) + + def test_push_does_not_add_hypothesis_that_falls_below_beam_threshold(self): + # arrange + stack = _Stack(3, 0.5) + poor_hypothesis = _Hypothesis(0.01) + + # act + stack.push(_Hypothesis(0.9)) # greatly superior hypothesis + stack.push(poor_hypothesis) + + # assert + self.assertFalse(poor_hypothesis in stack) + + def test_best_returns_the_best_hypothesis(self): + # arrange + stack = _Stack(3) + best_hypothesis = _Hypothesis(0.99) + + # act + stack.push(_Hypothesis(0.0)) + stack.push(best_hypothesis) + stack.push(_Hypothesis(0.5)) + + # assert + self.assertEqual(stack.best(), best_hypothesis) + + def test_best_returns_none_when_stack_is_empty(self): + # arrange + stack = _Stack(3) + + # assert + self.assertEqual(stack.best(), None) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/util.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/util.doctest new file mode 100644 index 0000000..69ce1a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/util.doctest @@ -0,0 +1,47 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================= +Utility functions +================= + + >>> from nltk.util import * + >>> from nltk.tree import Tree + + >>> print_string("This is a long string, therefore it should break", 25) + This is a long string, + therefore it should break + + >>> re_show("[a-z]+", "sdf123") + {sdf}123 + + >>> tree = Tree(5, + ... [Tree(4, [Tree(2, [1, 3])]), + ... Tree(8, [Tree(6, [7]), 9])]) + >>> for x in breadth_first(tree): + ... if isinstance(x, int): print(x) + ... else: print(x.label()) + 5 + 4 + 8 + 2 + 6 + 9 + 1 + 3 + 7 + >>> for x in breadth_first(tree, maxdepth=2): + ... if isinstance(x, int): print(x) + ... else: print(x.label()) + 5 + 4 + 8 + 2 + 6 + 9 + + >>> invert_dict({1: 2}) + defaultdict(<... 'list'>, {2: 1}) + + >>> invert_dict({1: [3, 4, 5]}) + defaultdict(<... 'list'>, {3: [1], 4: [1], 5: [1]}) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet.doctest new file mode 100644 index 0000000..9ecd78d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet.doctest @@ -0,0 +1,828 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +================= +WordNet Interface +================= + +WordNet is just another NLTK corpus reader, and can be imported like this: + + >>> from nltk.corpus import wordnet + +For more compact code, we recommend: + + >>> from nltk.corpus import wordnet as wn + +----- +Words +----- + +Look up a word using ``synsets()``; this function has an optional ``pos`` argument +which lets you constrain the part of speech of the word: + + >>> wn.synsets('dog') + [Synset('dog.n.01'), Synset('frump.n.01'), Synset('dog.n.03'), Synset('cad.n.01'), + Synset('frank.n.02'), Synset('pawl.n.01'), Synset('andiron.n.01'), Synset('chase.v.01')] + >>> wn.synsets('dog', pos=wn.VERB) + [Synset('chase.v.01')] + +The other parts of speech are ``NOUN``, ``ADJ`` and ``ADV``. +A synset is identified with a 3-part name of the form: word.pos.nn: + + >>> wn.synset('dog.n.01') + Synset('dog.n.01') + >>> print(wn.synset('dog.n.01').definition()) + a member of the genus Canis (probably descended from the common wolf) that has been domesticated by man since prehistoric times; occurs in many breeds + >>> len(wn.synset('dog.n.01').examples()) + 1 + >>> print(wn.synset('dog.n.01').examples()[0]) + the dog barked all night + >>> wn.synset('dog.n.01').lemmas() + [Lemma('dog.n.01.dog'), Lemma('dog.n.01.domestic_dog'), Lemma('dog.n.01.Canis_familiaris')] + >>> [str(lemma.name()) for lemma in wn.synset('dog.n.01').lemmas()] + ['dog', 'domestic_dog', 'Canis_familiaris'] + >>> wn.lemma('dog.n.01.dog').synset() + Synset('dog.n.01') + +The WordNet corpus reader gives access to the Open Multilingual +WordNet, using ISO-639 language codes. These languages are not +loaded by default, but only lazily, when needed. + + >>> wn.langs() + ['eng'] + + >>> wn.synsets(b'\xe7\x8a\xac'.decode('utf-8'), lang='jpn') + [Synset('dog.n.01'), Synset('spy.n.01')] + + >>> wn.synset('spy.n.01').lemma_names('jpn') + ['いぬ', 'まわし者', 'スパイ', '回し者', '回者', '密偵', + '工作員', '廻し者', '廻者', '探', '探り', '犬', '秘密捜査員', + '諜報員', '諜者', '間者', '間諜', '隠密'] + + >>> sorted(wn.langs()) + ['als', 'arb', 'bul', 'cat', 'cmn', 'dan', 'ell', 'eng', 'eus', + 'fin', 'fra', 'glg', 'heb', 'hrv', 'ind', 'isl', 'ita', 'ita_iwn', + 'jpn', 'lit', 'nld', 'nno', 'nob', 'pol', 'por', 'ron', 'slk', + 'slv', 'spa', 'swe', 'tha', 'zsm'] + + >>> wn.synset('dog.n.01').lemma_names('ita') + ['Canis_familiaris', 'cane'] + >>> wn.lemmas('cane', lang='ita') + [Lemma('dog.n.01.cane'), Lemma('cramp.n.02.cane'), Lemma('hammer.n.01.cane'), Lemma('bad_person.n.01.cane'), + Lemma('incompetent.n.01.cane')] + >>> sorted(wn.synset('dog.n.01').lemmas('dan')) + [Lemma('dog.n.01.hund'), Lemma('dog.n.01.k\xf8ter'), + Lemma('dog.n.01.vovhund'), Lemma('dog.n.01.vovse')] + + >>> sorted(wn.synset('dog.n.01').lemmas('por')) + [Lemma('dog.n.01.cachorra'), Lemma('dog.n.01.cachorro'), Lemma('dog.n.01.cadela'), Lemma('dog.n.01.c\xe3o')] + + >>> dog_lemma = wn.lemma(b'dog.n.01.c\xc3\xa3o'.decode('utf-8'), lang='por') + >>> dog_lemma + Lemma('dog.n.01.c\xe3o') + >>> dog_lemma.lang() + 'por' + >>> len(list(wordnet.all_lemma_names(pos='n', lang='jpn'))) + 66031 + +The synonyms of a word are returned as a nested list of synonyms of the different senses of +the input word in the given language, since these different senses are not mutual synonyms: + + >>> wn.synonyms('car') + [['auto', 'automobile', 'machine', 'motorcar'], ['railcar', 'railroad_car', 'railway_car'], ['gondola'], ['elevator_car'], ['cable_car']] + >>> wn.synonyms('coche', lang='spa') + [['auto', 'automóvil', 'carro', 'máquina', 'turismo', 'vehículo'], ['automotor', 'vagón'], ['vagón', 'vagón_de_pasajeros']] + + +------- +Synsets +------- + +`Synset`: a set of synonyms that share a common meaning. + + >>> dog = wn.synset('dog.n.01') + >>> dog.hypernyms() + [Synset('canine.n.02'), Synset('domestic_animal.n.01')] + >>> dog.hyponyms() + [Synset('basenji.n.01'), Synset('corgi.n.01'), Synset('cur.n.01'), Synset('dalmatian.n.02'), ...] + >>> dog.member_holonyms() + [Synset('canis.n.01'), Synset('pack.n.06')] + >>> dog.root_hypernyms() + [Synset('entity.n.01')] + >>> wn.synset('dog.n.01').lowest_common_hypernyms(wn.synset('cat.n.01')) + [Synset('carnivore.n.01')] + +Each synset contains one or more lemmas, which represent a specific +sense of a specific word. + +Note that some relations are defined by WordNet only over Lemmas: + + >>> good = wn.synset('good.a.01') + >>> good.antonyms() + Traceback (most recent call last): + File "", line 1, in + AttributeError: 'Synset' object has no attribute 'antonyms' + >>> good.lemmas()[0].antonyms() + [Lemma('bad.a.01.bad')] + +The relations that are currently defined in this way are `antonyms`, +`derivationally_related_forms` and `pertainyms`. + +If you know the byte offset used to identify a synset in the original +Princeton WordNet data file, you can use that to instantiate the synset +in NLTK: + + >>> wn.synset_from_pos_and_offset('n', 4543158) + Synset('wagon.n.01') + +Likewise, instantiate a synset from a known sense key: + >>> wn.synset_from_sense_key("driving%1:04:03::") + Synset('drive.n.06') + + +------ +Lemmas +------ + + >>> eat = wn.lemma('eat.v.03.eat') + >>> eat + Lemma('feed.v.06.eat') + >>> print(eat.key()) + eat%2:34:02:: + >>> eat.count() + 4 + >>> wn.lemma_from_key(eat.key()) + Lemma('feed.v.06.eat') + >>> wn.lemma_from_key(eat.key()).synset() + Synset('feed.v.06') + >>> wn.lemma_from_key('feebleminded%5:00:00:retarded:00') + Lemma('backward.s.03.feebleminded') + >>> for lemma in wn.synset('eat.v.03').lemmas(): + ... print(lemma, lemma.count()) + ... + Lemma('feed.v.06.feed') 3 + Lemma('feed.v.06.eat') 4 + >>> for lemma in wn.lemmas('eat', 'v'): + ... print(lemma, lemma.count()) + ... + Lemma('eat.v.01.eat') 61 + Lemma('eat.v.02.eat') 13 + Lemma('feed.v.06.eat') 4 + Lemma('eat.v.04.eat') 0 + Lemma('consume.v.05.eat') 0 + Lemma('corrode.v.01.eat') 0 + >>> wn.lemma('jump.v.11.jump') + Lemma('jump.v.11.jump') + +Lemmas can also have relations between them: + + >>> vocal = wn.lemma('vocal.a.01.vocal') + >>> vocal.derivationally_related_forms() + [Lemma('vocalize.v.02.vocalize')] + >>> vocal.pertainyms() + [Lemma('voice.n.02.voice')] + >>> vocal.antonyms() + [Lemma('instrumental.a.01.instrumental')] + +The three relations above exist only on lemmas, not on synsets. + +----------- +Verb Frames +----------- + + >>> wn.synset('think.v.01').frame_ids() + [5, 9] + >>> for lemma in wn.synset('think.v.01').lemmas(): + ... print(lemma, lemma.frame_ids()) + ... print(" | ".join(lemma.frame_strings())) + ... + Lemma('think.v.01.think') [5, 9] + Something think something Adjective/Noun | Somebody think somebody + Lemma('think.v.01.believe') [5, 9] + Something believe something Adjective/Noun | Somebody believe somebody + Lemma('think.v.01.consider') [5, 9] + Something consider something Adjective/Noun | Somebody consider somebody + Lemma('think.v.01.conceive') [5, 9] + Something conceive something Adjective/Noun | Somebody conceive somebody + >>> wn.synset('stretch.v.02').frame_ids() + [8] + >>> for lemma in wn.synset('stretch.v.02').lemmas(): + ... print(lemma, lemma.frame_ids()) + ... print(" | ".join(lemma.frame_strings())) + ... + Lemma('stretch.v.02.stretch') [8, 2] + Somebody stretch something | Somebody stretch + Lemma('stretch.v.02.extend') [8] + Somebody extend something + + +---------- +Similarity +---------- + + >>> dog = wn.synset('dog.n.01') + >>> cat = wn.synset('cat.n.01') + + >>> hit = wn.synset('hit.v.01') + >>> slap = wn.synset('slap.v.01') + + +``synset1.path_similarity(synset2):`` +Return a score denoting how similar two word senses are, based on the +shortest path that connects the senses in the is-a (hypernym/hypnoym) +taxonomy. The score is in the range 0 to 1. By default, there is now +a fake root node added to verbs so for cases where previously a path +could not be found---and None was returned---it should return a value. +The old behavior can be achieved by setting simulate_root to be False. +A score of 1 represents identity i.e. comparing a sense with itself +will return 1. + + >>> dog.path_similarity(cat) + 0.2... + + >>> hit.path_similarity(slap) + 0.142... + + >>> wn.path_similarity(hit, slap) + 0.142... + + >>> print(hit.path_similarity(slap, simulate_root=False)) + None + + >>> print(wn.path_similarity(hit, slap, simulate_root=False)) + None + +``synset1.lch_similarity(synset2):`` +Leacock-Chodorow Similarity: +Return a score denoting how similar two word senses are, based on the +shortest path that connects the senses (as above) and the maximum depth +of the taxonomy in which the senses occur. The relationship is given +as -log(p/2d) where p is the shortest path length and d the taxonomy +depth. + + >>> dog.lch_similarity(cat) + 2.028... + + >>> hit.lch_similarity(slap) + 1.312... + + >>> wn.lch_similarity(hit, slap) + 1.312... + + >>> print(hit.lch_similarity(slap, simulate_root=False)) + None + + >>> print(wn.lch_similarity(hit, slap, simulate_root=False)) + None + +``synset1.wup_similarity(synset2):`` +Wu-Palmer Similarity: +Return a score denoting how similar two word senses are, based on the +depth of the two senses in the taxonomy and that of their Least Common +Subsumer (most specific ancestor node). Note that at this time the +scores given do **not** always agree with those given by Pedersen's Perl +implementation of Wordnet Similarity. + +The LCS does not necessarily feature in the shortest path connecting the +two senses, as it is by definition the common ancestor deepest in the +taxonomy, not closest to the two senses. Typically, however, it will so +feature. Where multiple candidates for the LCS exist, that whose +shortest path to the root node is the longest will be selected. Where +the LCS has multiple paths to the root, the longer path is used for +the purposes of the calculation. + + >>> dog.wup_similarity(cat) + 0.857... + + >>> hit.wup_similarity(slap) + 0.25 + + >>> wn.wup_similarity(hit, slap) + 0.25 + + >>> print(hit.wup_similarity(slap, simulate_root=False)) + None + + >>> print(wn.wup_similarity(hit, slap, simulate_root=False)) + None + +``wordnet_ic`` +Information Content: +Load an information content file from the wordnet_ic corpus. + + >>> from nltk.corpus import wordnet_ic + >>> brown_ic = wordnet_ic.ic('ic-brown.dat') + >>> semcor_ic = wordnet_ic.ic('ic-semcor.dat') + +Or you can create an information content dictionary from a corpus (or +anything that has a words() method). + + >>> from nltk.corpus import genesis + >>> genesis_ic = wn.ic(genesis, False, 0.0) + +``synset1.res_similarity(synset2, ic):`` +Resnik Similarity: +Return a score denoting how similar two word senses are, based on the +Information Content (IC) of the Least Common Subsumer (most specific +ancestor node). Note that for any similarity measure that uses +information content, the result is dependent on the corpus used to +generate the information content and the specifics of how the +information content was created. + + >>> dog.res_similarity(cat, brown_ic) + 7.911... + >>> dog.res_similarity(cat, genesis_ic) + 7.204... + +``synset1.jcn_similarity(synset2, ic):`` +Jiang-Conrath Similarity +Return a score denoting how similar two word senses are, based on the +Information Content (IC) of the Least Common Subsumer (most specific +ancestor node) and that of the two input Synsets. The relationship is +given by the equation 1 / (IC(s1) + IC(s2) - 2 * IC(lcs)). + + >>> dog.jcn_similarity(cat, brown_ic) + 0.449... + >>> dog.jcn_similarity(cat, genesis_ic) + 0.285... + +``synset1.lin_similarity(synset2, ic):`` +Lin Similarity: +Return a score denoting how similar two word senses are, based on the +Information Content (IC) of the Least Common Subsumer (most specific +ancestor node) and that of the two input Synsets. The relationship is +given by the equation 2 * IC(lcs) / (IC(s1) + IC(s2)). + + >>> dog.lin_similarity(cat, semcor_ic) + 0.886... + + +--------------------- +Access to all Synsets +--------------------- + +Iterate over all the noun synsets: + + >>> for synset in list(wn.all_synsets('n'))[:10]: + ... print(synset) + ... + Synset('entity.n.01') + Synset('physical_entity.n.01') + Synset('abstraction.n.06') + Synset('thing.n.12') + Synset('object.n.01') + Synset('whole.n.02') + Synset('congener.n.03') + Synset('living_thing.n.01') + Synset('organism.n.01') + Synset('benthos.n.02') + +Get all synsets for this word, possibly restricted by POS: + + >>> wn.synsets('dog') + [Synset('dog.n.01'), Synset('frump.n.01'), Synset('dog.n.03'), Synset('cad.n.01'), ...] + >>> wn.synsets('dog', pos='v') + [Synset('chase.v.01')] + +Walk through the noun synsets looking at their hypernyms: + + >>> from itertools import islice + >>> for synset in islice(wn.all_synsets('n'), 5): + ... print(synset, synset.hypernyms()) + ... + Synset('entity.n.01') [] + Synset('physical_entity.n.01') [Synset('entity.n.01')] + Synset('abstraction.n.06') [Synset('entity.n.01')] + Synset('thing.n.12') [Synset('physical_entity.n.01')] + Synset('object.n.01') [Synset('physical_entity.n.01')] + + +------ +Morphy +------ + +Look up forms not in WordNet, with the help of Morphy: + + >>> wn.morphy('denied', wn.NOUN) + >>> print(wn.morphy('denied', wn.VERB)) + deny + >>> wn.synsets('denied', wn.NOUN) + [] + >>> wn.synsets('denied', wn.VERB) + [Synset('deny.v.01'), Synset('deny.v.02'), Synset('deny.v.03'), Synset('deny.v.04'), + Synset('deny.v.05'), Synset('traverse.v.03'), Synset('deny.v.07')] + +Morphy uses a combination of inflectional ending rules and exception +lists to handle a variety of different possibilities: + + >>> print(wn.morphy('dogs')) + dog + >>> print(wn.morphy('churches')) + church + >>> print(wn.morphy('aardwolves')) + aardwolf + >>> print(wn.morphy('abaci')) + abacus + >>> print(wn.morphy('book', wn.NOUN)) + book + >>> wn.morphy('hardrock', wn.ADV) + >>> wn.morphy('book', wn.ADJ) + >>> wn.morphy('his', wn.NOUN) + >>> + +--------------- +Synset Closures +--------------- + +Compute transitive closures of synsets + + >>> dog = wn.synset('dog.n.01') + >>> hypo = lambda s: s.hyponyms() + >>> hyper = lambda s: s.hypernyms() + >>> list(dog.closure(hypo, depth=1)) == dog.hyponyms() + True + >>> list(dog.closure(hyper, depth=1)) == dog.hypernyms() + True + >>> list(dog.closure(hypo)) + [Synset('basenji.n.01'), Synset('corgi.n.01'), Synset('cur.n.01'), + Synset('dalmatian.n.02'), Synset('great_pyrenees.n.01'), + Synset('griffon.n.02'), Synset('hunting_dog.n.01'), Synset('lapdog.n.01'), + Synset('leonberg.n.01'), Synset('mexican_hairless.n.01'), + Synset('newfoundland.n.01'), Synset('pooch.n.01'), Synset('poodle.n.01'), ...] + >>> list(dog.closure(hyper)) + [Synset('canine.n.02'), Synset('domestic_animal.n.01'), Synset('carnivore.n.01'), Synset('animal.n.01'), + Synset('placental.n.01'), Synset('organism.n.01'), Synset('mammal.n.01'), Synset('living_thing.n.01'), + Synset('vertebrate.n.01'), Synset('whole.n.02'), Synset('chordate.n.01'), Synset('object.n.01'), + Synset('physical_entity.n.01'), Synset('entity.n.01')] + + +---------------- +Regression Tests +---------------- + +Bug 85: morphy returns the base form of a word, if it's input is given +as a base form for a POS for which that word is not defined: + + >>> wn.synsets('book', wn.NOUN) + [Synset('book.n.01'), Synset('book.n.02'), Synset('record.n.05'), Synset('script.n.01'), Synset('ledger.n.01'), Synset('book.n.06'), Synset('book.n.07'), Synset('koran.n.01'), Synset('bible.n.01'), Synset('book.n.10'), Synset('book.n.11')] + >>> wn.synsets('book', wn.ADJ) + [] + >>> wn.morphy('book', wn.NOUN) + 'book' + >>> wn.morphy('book', wn.ADJ) + >>> + +Bug 160: wup_similarity breaks when the two synsets have no common hypernym + + >>> t = wn.synsets('picasso')[0] + >>> m = wn.synsets('male')[1] + >>> t.wup_similarity(m) + 0.631... + +Issue #2278: wup_similarity not commutative when comparing a noun and a verb. +Patch #2650 resolved this error. As a result, the output of the following use of wup_similarity no longer returns None. + + >>> t = wn.synsets('titan')[1] + >>> s = wn.synsets('say', wn.VERB)[0] + >>> t.wup_similarity(s) + 0.142... + +Bug 21: "instance of" not included in LCS (very similar to bug 160) + + >>> a = wn.synsets("writings")[0] + >>> b = wn.synsets("scripture")[0] + >>> brown_ic = wordnet_ic.ic('ic-brown.dat') + >>> a.jcn_similarity(b, brown_ic) + 0.175... + +Bug 221: Verb root IC is zero + + >>> from nltk.corpus.reader.wordnet import information_content + >>> s = wn.synsets('say', wn.VERB)[0] + >>> information_content(s, brown_ic) + 4.623... + +Bug 161: Comparison between WN keys/lemmas should not be case sensitive + + >>> k = wn.synsets("jefferson")[0].lemmas()[0].key() + >>> wn.lemma_from_key(k) + Lemma('jefferson.n.01.Jefferson') + >>> wn.lemma_from_key(k.upper()) + Lemma('jefferson.n.01.Jefferson') + +Bug 99: WordNet root_hypernyms gives incorrect results + + >>> from nltk.corpus import wordnet as wn + >>> for s in wn.all_synsets(wn.NOUN): + ... if s.root_hypernyms()[0] != wn.synset('entity.n.01'): + ... print(s, s.root_hypernyms()) + ... + >>> + +Bug 382: JCN Division by zero error + + >>> tow = wn.synset('tow.v.01') + >>> shlep = wn.synset('shlep.v.02') + >>> from nltk.corpus import wordnet_ic + >>> brown_ic = wordnet_ic.ic('ic-brown.dat') + >>> tow.jcn_similarity(shlep, brown_ic) + 1...e+300 + +Bug 428: Depth is zero for instance nouns + + >>> s = wn.synset("lincoln.n.01") + >>> s.max_depth() > 0 + True + +Bug 429: Information content smoothing used old reference to all_synsets + + >>> genesis_ic = wn.ic(genesis, True, 1.0) + +Bug 430: all_synsets used wrong pos lookup when synsets were cached + + >>> for ii in wn.all_synsets(): pass + >>> for ii in wn.all_synsets(): pass + +Bug 470: shortest_path_distance ignored instance hypernyms + + >>> google = wordnet.synsets("google")[0] + >>> earth = wordnet.synsets("earth")[0] + >>> google.wup_similarity(earth) + 0.1... + +Bug 484: similarity metrics returned -1 instead of None for no LCS + + >>> t = wn.synsets('fly', wn.VERB)[0] + >>> s = wn.synsets('say', wn.VERB)[0] + >>> print(s.shortest_path_distance(t)) + None + >>> print(s.path_similarity(t, simulate_root=False)) + None + >>> print(s.lch_similarity(t, simulate_root=False)) + None + >>> print(s.wup_similarity(t, simulate_root=False)) + None + +Bug 427: "pants" does not return all the senses it should + + >>> from nltk.corpus import wordnet + >>> wordnet.synsets("pants",'n') + [Synset('bloomers.n.01'), Synset('pant.n.01'), Synset('trouser.n.01'), Synset('gasp.n.01')] + +Bug 482: Some nouns not being lemmatised by WordNetLemmatizer().lemmatize + + >>> from nltk.stem.wordnet import WordNetLemmatizer + >>> WordNetLemmatizer().lemmatize("eggs", pos="n") + 'egg' + >>> WordNetLemmatizer().lemmatize("legs", pos="n") + 'leg' + +Bug 284: instance hypernyms not used in similarity calculations + + >>> wn.synset('john.n.02').lch_similarity(wn.synset('dog.n.01')) + 1.335... + >>> wn.synset('john.n.02').wup_similarity(wn.synset('dog.n.01')) + 0.571... + >>> wn.synset('john.n.02').res_similarity(wn.synset('dog.n.01'), brown_ic) + 2.224... + >>> wn.synset('john.n.02').jcn_similarity(wn.synset('dog.n.01'), brown_ic) + 0.075... + >>> wn.synset('john.n.02').lin_similarity(wn.synset('dog.n.01'), brown_ic) + 0.252... + >>> wn.synset('john.n.02').hypernym_paths() + [[Synset('entity.n.01'), ..., Synset('john.n.02')]] + +Issue 541: add domains to wordnet + + >>> wn.synset('code.n.03').topic_domains() + [Synset('computer_science.n.01')] + >>> wn.synset('pukka.a.01').region_domains() + [Synset('india.n.01')] + >>> wn.synset('freaky.a.01').usage_domains() + [Synset('slang.n.02')] + +Issue 629: wordnet failures when python run with -O optimizations + + >>> # Run the test suite with python -O to check this + >>> wn.synsets("brunch") + [Synset('brunch.n.01'), Synset('brunch.v.01')] + +Issue 395: wordnet returns incorrect result for lowest_common_hypernyms of chef and policeman + + >>> wn.synset('policeman.n.01').lowest_common_hypernyms(wn.synset('chef.n.01')) + [Synset('person.n.01')] + +Bug https://github.com/nltk/nltk/issues/1641: Non-English lemmas containing capital letters cannot be looked up using wordnet.lemmas() or wordnet.synsets() + + >>> wn.lemmas('Londres', lang='fra') + [Lemma('united_kingdom.n.01.Londres'), Lemma('london.n.01.Londres'), Lemma('london.n.02.Londres')] + >>> wn.lemmas('londres', lang='fra') + [Lemma('united_kingdom.n.01.Londres'), Lemma('london.n.01.Londres'), Lemma('london.n.02.Londres')] + +Patch-1 https://github.com/nltk/nltk/pull/2065 Adding 3 functions (relations) to WordNet class + + >>> wn.synsets("computer_science")[0].in_topic_domains()[2] + Synset('access_time.n.01') + >>> wn.synsets("France")[0].in_region_domains()[18] + Synset('french.n.01') + >>> wn.synsets("slang")[1].in_usage_domains()[18] + Synset('can-do.s.01') + +Issue 2721: WordNetCorpusReader.ic() does not add smoothing to N + + >>> class FakeCorpus: + ... def words(self): return ['word'] + ... + >>> fake_ic = wn.ic(FakeCorpus(), False, 1.0) + >>> word = wn.synset('word.n.01') + >>> information_content(word, fake_ic) > 0 + True + +Issue 3077: Incorrect part-of-speech filtering in all_synsets + + >>> next(wn.all_synsets(pos="a")) + Synset('able.a.01') + >>> next(wn.all_synsets(pos="s")) + Synset('emergent.s.02') + >>> wn.add_omw() + >>> next(wn.all_synsets(lang="hrv")) + Synset('able.a.01') + >>> next(wn.all_synsets(lang="hrv", pos="n")) + Synset('entity.n.01') + >>> next(wn.all_synsets(lang="hrv", pos="v")) + Synset('breathe.v.01') + >>> next(wn.all_synsets(lang="hrv", pos="s")) + Synset('ideological.s.02') + >>> next(wn.all_synsets(lang="hrv", pos="a")) + Synset('able.a.01') + + +------------------------------------------------ +Endlessness vs. intractability in relation trees +------------------------------------------------ + +1. Endlessness +-------------- + +Until NLTK v. 3.5, the ``tree()`` function looped forever on symmetric +relations (verb_groups, attributes, and most also_sees). But in +the current version, ``tree()`` now detects and discards these cycles: + + >>> from pprint import pprint + >>> pprint(wn.synset('bound.a.01').tree(lambda s:s.also_sees())) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + [Synset('confined.a.02'), + [Synset('restricted.a.01'), [Synset('classified.a.02')]]], + [Synset('dependent.a.01')], + [Synset('restricted.a.01'), + [Synset('classified.a.02')], + [Synset('confined.a.02')]]]] + +Specifying the "cut_mark" parameter increases verbosity, so that the cycles +are mentioned in the output, together with the level where they occur: + + >>> pprint(wn.synset('bound.a.01').tree(lambda s:s.also_sees(),cut_mark='...')) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + "Cycle(Synset('bound.a.01'),-3,...)", + [Synset('confined.a.02'), + [Synset('restricted.a.01'), + [Synset('classified.a.02')], + "Cycle(Synset('confined.a.02'),-5,...)", + "Cycle(Synset('unfree.a.02'),-5,...)"], + "Cycle(Synset('unfree.a.02'),-4,...)"], + [Synset('dependent.a.01'), "Cycle(Synset('unfree.a.02'),-4,...)"], + [Synset('restricted.a.01'), + [Synset('classified.a.02')], + [Synset('confined.a.02'), + "Cycle(Synset('restricted.a.01'),-5,...)", + "Cycle(Synset('unfree.a.02'),-5,...)"], + "Cycle(Synset('unfree.a.02'),-4,...)"]]] + + +2. Intractability +----------------- + +However, even after discarding the infinite cycles, some trees can remain +intractable, due to combinatorial explosion in a relation. This happens in +WordNet, because the ``also_sees()`` relation has a big Strongly Connected +Component (_SCC_) consisting in 758 synsets, where any member node is +transitively connected by the same relation, to all other members of the +same SCC. This produces intractable relation trees for each of these 758 +synsets, i. e. trees that are too big to compute or display on any computer. + +For example, the synset 'concrete.a.01' is a member of the largest SCC, +so its ``also_sees()`` tree is intractable, and can normally only be handled +by limiting the ``depth`` parameter to display a small number of levels: + + >>> from pprint import pprint + >>> pprint(wn.synset('concrete.a.01').tree(lambda s:s.also_sees(),cut_mark='...',depth=2)) + [Synset('concrete.a.01'), + [Synset('practical.a.01'), + "Cycle(Synset('concrete.a.01'),0,...)", + [Synset('possible.a.01'), '...'], + [Synset('realistic.a.01'), '...'], + [Synset('serviceable.a.01'), '...']], + [Synset('real.a.01'), + "Cycle(Synset('concrete.a.01'),0,...)", + [Synset('genuine.a.01'), '...'], + [Synset('realistic.a.01'), '...'], + [Synset('sincere.a.01'), '...']], + [Synset('tangible.a.01'), "Cycle(Synset('concrete.a.01'),0,...)"]] + + +2.1 First solution: ``acyclic_tree()`` +...................................... + +On the other hand, the new ``acyclic_tree()`` function is able to also handle +the intractable cases. The ``also_sees()`` acyclic tree of 'concrete.a.01' is +several hundred lines long, so here is a simpler example, concerning a much +smaller SCC: counting only five members, the SCC that includes 'bound.a.01' +is tractable with the normal ``tree()`` function, as seen above. + +But while ``tree()`` only prunes redundancy within local branches, ``acyclic_tree()`` +prunes the tree globally, thus discarding any additional redundancy, and +produces a tree that includes all reachable nodes (i.e., a **spanning tree**). +This tree is **minimal** because it includes the reachable nodes only once, +but it is not necessarily a **Minimum Spanning Tree** (MST), because the +Depth-first search strategy does not guarantee that nodes are reached +through the lowest number of links (as Breadth-first search would). + + >>> pprint(wn.synset('bound.a.01').acyclic_tree(lambda s:s.also_sees())) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + [Synset('confined.a.02'), + [Synset('restricted.a.01'), [Synset('classified.a.02')]]], + [Synset('dependent.a.01')]]] + +Again, specifying the ``cut_mark`` parameter increases verbosity, so that the +cycles are mentioned in the output, together with the level where they occur: + + >>> pprint(wn.synset('bound.a.01').acyclic_tree(lambda s:s.also_sees(),cut_mark='...')) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + "Cycle(Synset('bound.a.01'),-3,...)", + [Synset('confined.a.02'), + [Synset('restricted.a.01'), + [Synset('classified.a.02')], + "Cycle(Synset('confined.a.02'),-5,...)", + "Cycle(Synset('unfree.a.02'),-5,...)"], + "Cycle(Synset('unfree.a.02'),-4,...)"], + [Synset('dependent.a.01'), "Cycle(Synset('unfree.a.02'),-4,...)"], + "Cycle(Synset('restricted.a.01'),-3,...)"]] + + +2.2 Better solution: mst() +.......................... + +A Minimum Spanning Tree (MST) spans all the nodes of a relation subgraph once, +while guaranteeing that each node is reached through the shortest path possible. +In unweighted relation graphs like WordNet, a MST can be computed very efficiently +in linear time, using Breadth-First Search (BFS). Like acyclic_tree(), the new +``unweighted_minimum_spanning_tree()`` function (imported in the Wordnet +module as ``mst``) handles intractable trees, such as the example discussed above: +``wn.synset('concrete.a.01').mst(lambda s:s.also_sees())``. + +But, while the also_sees() acyclic_tree of 'bound.a.01' reaches +'classified.a.02' through four links, using depth-first search as seen above +(bound.a.01 > unfree.a.02 > confined.a.02 > restricted.a.01 > classified.a.02), +in the following MST, the path to 'classified.a.02' is the shortest possible, +consisting only in three links (bound.a.01 > unfree.a.02 > restricted.a.01 > +classified.a.02): + + >>> pprint(wn.synset('bound.a.01').mst(lambda s:s.also_sees())) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + [Synset('confined.a.02')], + [Synset('dependent.a.01')], + [Synset('restricted.a.01'), [Synset('classified.a.02')]]]] + + +---------------------------------------------------------------- +Loading alternative Wordnet versions +---------------------------------------------------------------- + + >>> print("Wordnet {}".format(wn.get_version())) + Wordnet 3.0 + + >>> from nltk.corpus import wordnet31 as wn31 + >>> print("Wordnet {}".format(wn31.get_version())) + Wordnet 3.1 + + >>> print(wn.synset('restrain.v.01').hyponyms()) + [Synset('confine.v.03'), Synset('control.v.02'), Synset('hold.v.36'), Synset('inhibit.v.04')] + + >>> print(wn31.synset('restrain.v.01').hyponyms()) + [Synset('enchain.v.01'), Synset('fetter.v.01'), Synset('ground.v.02'), Synset('impound.v.02'), Synset('pen_up.v.01'), Synset('pinion.v.01'), Synset('pound.v.06'), Synset('tie_down.v.01')] + + >>> print(wn31.synset('restrain.v.04').hyponyms()) + [Synset('baffle.v.03'), Synset('confine.v.02'), Synset('control.v.02'), Synset('hold.v.36'), Synset('rule.v.07'), Synset('swallow.v.06'), Synset('wink.v.04')] + + +------------- +Teardown test +------------- + + >>> from nltk.corpus import wordnet + >>> wordnet._unload() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet_lch.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet_lch.doctest new file mode 100644 index 0000000..4106064 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wordnet_lch.doctest @@ -0,0 +1,53 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +=============================== +WordNet Lowest Common Hypernyms +=============================== + +Wordnet's lowest_common_hypernyms() method is based used to locate the +lowest single hypernym that is shared by two given words: + + >>> from nltk.corpus import wordnet as wn + >>> wn.synset('kin.n.01').lowest_common_hypernyms(wn.synset('mother.n.01')) + [Synset('relative.n.01')] + + >>> wn.synset('policeman.n.01').lowest_common_hypernyms(wn.synset('chef.n.01')) + [Synset('person.n.01')] + +This method generally returns a single result, but in some cases, more than one +valid LCH is possible: + + >>> wn.synset('body.n.09').lowest_common_hypernyms(wn.synset('sidereal_day.n.01')) + [Synset('attribute.n.02'), Synset('measure.n.02')] + +In some cases, lowest_common_hypernyms() can return one of the synsets which was +passed to it as an argument: + + >>> wn.synset('woman.n.01').lowest_common_hypernyms(wn.synset('girlfriend.n.02')) + [Synset('woman.n.01')] + +In NLTK 3.0a2 the behavior of lowest_common_hypernyms() was changed to give more +accurate results in a small set of cases, generally when dealing with nouns describing +social roles or jobs. To emulate the pre v3.0a2 behavior, you can set the use_min_depth=True +flag: + + >>> wn.synset('policeman.n.01').lowest_common_hypernyms(wn.synset('chef.n.01')) + [Synset('person.n.01')] + >>> wn.synset('policeman.n.01').lowest_common_hypernyms(wn.synset('chef.n.01'), use_min_depth=True) + [Synset('organism.n.01')] + +In some cases use_min_depth=True may return more or fewer results than the default +behavior: + + >>> wn.synset('woman.n.01').lowest_common_hypernyms(wn.synset('girlfriend.n.02')) + [Synset('woman.n.01')] + >>> wn.synset('woman.n.01').lowest_common_hypernyms(wn.synset('girlfriend.n.02'), use_min_depth=True) + [Synset('organism.n.01'), Synset('woman.n.01')] + +In the general case, however, they tend to return the same results: + + >>> wn.synset('body.n.09').lowest_common_hypernyms(wn.synset('sidereal_day.n.01')) + [Synset('attribute.n.02'), Synset('measure.n.02')] + >>> wn.synset('body.n.09').lowest_common_hypernyms(wn.synset('sidereal_day.n.01'), use_min_depth=True) + [Synset('attribute.n.02'), Synset('measure.n.02')] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/test/wsd.doctest b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wsd.doctest new file mode 100644 index 0000000..27648b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/test/wsd.doctest @@ -0,0 +1,68 @@ +.. Copyright (C) 2001-2023 NLTK Project +.. For license information, see LICENSE.TXT + +.. -*- coding: utf-8 -*- + +========================= +Word Sense Disambiguation +========================= + + +Lesk Algorithm +-------------- + + +Performs the classic Lesk algorithm for Word Sense Disambiguation (WSD) using +a the definitions of the ambiguous word. + +Given an ambiguous word and the context in which the word occurs, Lesk returns +a Synset with the highest number of overlapping words between the context +sentence and different definitions from each Synset. + + >>> from nltk.wsd import lesk + >>> sent = ['I', 'went', 'to', 'the', 'bank', 'to', 'deposit', 'money', '.'] + + >>> print(lesk(sent, 'bank', 'n')) + Synset('savings_bank.n.02') + + >>> print(lesk(sent, 'bank')) + Synset('savings_bank.n.02') + +The definitions for "bank" are: + + >>> from nltk.corpus import wordnet as wn + >>> for ss in wn.synsets('bank'): + ... print(ss, ss.definition()) + ... + Synset('bank.n.01') sloping land (especially the slope beside a body of water) + Synset('depository_financial_institution.n.01') a financial institution that accepts deposits and channels the money into lending activities + Synset('bank.n.03') a long ridge or pile + Synset('bank.n.04') an arrangement of similar objects in a row or in tiers + Synset('bank.n.05') a supply or stock held in reserve for future use (especially in emergencies) + Synset('bank.n.06') the funds held by a gambling house or the dealer in some gambling games + Synset('bank.n.07') a slope in the turn of a road or track; the outside is higher than the inside in order to reduce the effects of centrifugal force + Synset('savings_bank.n.02') a container (usually with a slot in the top) for keeping money at home + Synset('bank.n.09') a building in which the business of banking transacted + Synset('bank.n.10') a flight maneuver; aircraft tips laterally about its longitudinal axis (especially in turning) + Synset('bank.v.01') tip laterally + Synset('bank.v.02') enclose with a bank + Synset('bank.v.03') do business with a bank or keep an account at a bank + Synset('bank.v.04') act as the banker in a game or in gambling + Synset('bank.v.05') be in the banking business + Synset('deposit.v.02') put into a bank account + Synset('bank.v.07') cover with ashes so to control the rate of burning + Synset('trust.v.01') have confidence or faith in + +Test disambiguation of POS tagged `able`. + + >>> [(s, s.pos()) for s in wn.synsets('able')] + [(Synset('able.a.01'), 'a'), (Synset('able.s.02'), 's'), (Synset('able.s.03'), 's'), (Synset('able.s.04'), 's')] + >>> sent = 'people should be able to marry a person of their choice'.split() + >>> lesk(sent, 'able') + Synset('able.s.04') + >>> lesk(sent, 'able', pos='a') + Synset('able.a.01') + +Test behavior if there is are no matching senses. + + >>> lesk('John loves Mary'.split(), 'loves', synsets=[]) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/text.py b/.pythonlibs/lib/python3.10/site-packages/nltk/text.py new file mode 100644 index 0000000..6c9738e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/text.py @@ -0,0 +1,779 @@ +# Natural Language Toolkit: Texts +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Edward Loper +# URL: +# For license information, see LICENSE.TXT + +""" +This module brings together a variety of NLTK functionality for +text analysis, and provides simple, interactive interfaces. +Functionality includes: concordancing, collocation discovery, +regular expression search over tokenized strings, and +distributional similarity. +""" + +import re +import sys +from collections import Counter, defaultdict, namedtuple +from functools import reduce +from math import log + +from nltk.collocations import BigramCollocationFinder +from nltk.lm import MLE +from nltk.lm.preprocessing import padded_everygram_pipeline +from nltk.metrics import BigramAssocMeasures, f_measure +from nltk.probability import ConditionalFreqDist as CFD +from nltk.probability import FreqDist +from nltk.tokenize import sent_tokenize +from nltk.util import LazyConcatenation, tokenwrap + +ConcordanceLine = namedtuple( + "ConcordanceLine", + ["left", "query", "right", "offset", "left_print", "right_print", "line"], +) + + +class ContextIndex: + """ + A bidirectional index between words and their 'contexts' in a text. + The context of a word is usually defined to be the words that occur + in a fixed window around the word; but other definitions may also + be used by providing a custom context function. + """ + + @staticmethod + def _default_context(tokens, i): + """One left token and one right token, normalized to lowercase""" + left = tokens[i - 1].lower() if i != 0 else "*START*" + right = tokens[i + 1].lower() if i != len(tokens) - 1 else "*END*" + return (left, right) + + def __init__(self, tokens, context_func=None, filter=None, key=lambda x: x): + self._key = key + self._tokens = tokens + if context_func: + self._context_func = context_func + else: + self._context_func = self._default_context + if filter: + tokens = [t for t in tokens if filter(t)] + self._word_to_contexts = CFD( + (self._key(w), self._context_func(tokens, i)) for i, w in enumerate(tokens) + ) + self._context_to_words = CFD( + (self._context_func(tokens, i), self._key(w)) for i, w in enumerate(tokens) + ) + + def tokens(self): + """ + :rtype: list(str) + :return: The document that this context index was + created from. + """ + return self._tokens + + def word_similarity_dict(self, word): + """ + Return a dictionary mapping from words to 'similarity scores,' + indicating how often these two words occur in the same + context. + """ + word = self._key(word) + word_contexts = set(self._word_to_contexts[word]) + + scores = {} + for w, w_contexts in self._word_to_contexts.items(): + scores[w] = f_measure(word_contexts, set(w_contexts)) + + return scores + + def similar_words(self, word, n=20): + scores = defaultdict(int) + for c in self._word_to_contexts[self._key(word)]: + for w in self._context_to_words[c]: + if w != word: + scores[w] += ( + self._context_to_words[c][word] * self._context_to_words[c][w] + ) + return sorted(scores, key=scores.get, reverse=True)[:n] + + def common_contexts(self, words, fail_on_unknown=False): + """ + Find contexts where the specified words can all appear; and + return a frequency distribution mapping each context to the + number of times that context was used. + + :param words: The words used to seed the similarity search + :type words: str + :param fail_on_unknown: If true, then raise a value error if + any of the given words do not occur at all in the index. + """ + words = [self._key(w) for w in words] + contexts = [set(self._word_to_contexts[w]) for w in words] + empty = [words[i] for i in range(len(words)) if not contexts[i]] + common = reduce(set.intersection, contexts) + if empty and fail_on_unknown: + raise ValueError("The following word(s) were not found:", " ".join(words)) + elif not common: + # nothing in common -- just return an empty freqdist. + return FreqDist() + else: + fd = FreqDist( + c for w in words for c in self._word_to_contexts[w] if c in common + ) + return fd + + +class ConcordanceIndex: + """ + An index that can be used to look up the offset locations at which + a given word occurs in a document. + """ + + def __init__(self, tokens, key=lambda x: x): + """ + Construct a new concordance index. + + :param tokens: The document (list of tokens) that this + concordance index was created from. This list can be used + to access the context of a given word occurrence. + :param key: A function that maps each token to a normalized + version that will be used as a key in the index. E.g., if + you use ``key=lambda s:s.lower()``, then the index will be + case-insensitive. + """ + self._tokens = tokens + """The document (list of tokens) that this concordance index + was created from.""" + + self._key = key + """Function mapping each token to an index key (or None).""" + + self._offsets = defaultdict(list) + """Dictionary mapping words (or keys) to lists of offset indices.""" + # Initialize the index (self._offsets) + for index, word in enumerate(tokens): + word = self._key(word) + self._offsets[word].append(index) + + def tokens(self): + """ + :rtype: list(str) + :return: The document that this concordance index was + created from. + """ + return self._tokens + + def offsets(self, word): + """ + :rtype: list(int) + :return: A list of the offset positions at which the given + word occurs. If a key function was specified for the + index, then given word's key will be looked up. + """ + word = self._key(word) + return self._offsets[word] + + def __repr__(self): + return "" % ( + len(self._tokens), + len(self._offsets), + ) + + def find_concordance(self, word, width=80): + """ + Find all concordance lines given the query word. + + Provided with a list of words, these will be found as a phrase. + """ + if isinstance(word, list): + phrase = word + else: + phrase = [word] + + half_width = (width - len(" ".join(phrase)) - 2) // 2 + context = width // 4 # approx number of words of context + + # Find the instances of the word to create the ConcordanceLine + concordance_list = [] + offsets = self.offsets(phrase[0]) + for i, word in enumerate(phrase[1:]): + word_offsets = {offset - i - 1 for offset in self.offsets(word)} + offsets = sorted(word_offsets.intersection(offsets)) + if offsets: + for i in offsets: + query_word = " ".join(self._tokens[i : i + len(phrase)]) + # Find the context of query word. + left_context = self._tokens[max(0, i - context) : i] + right_context = self._tokens[i + len(phrase) : i + context] + # Create the pretty lines with the query_word in the middle. + left_print = " ".join(left_context)[-half_width:] + right_print = " ".join(right_context)[:half_width] + # The WYSIWYG line of the concordance. + line_print = " ".join([left_print, query_word, right_print]) + # Create the ConcordanceLine + concordance_line = ConcordanceLine( + left_context, + query_word, + right_context, + i, + left_print, + right_print, + line_print, + ) + concordance_list.append(concordance_line) + return concordance_list + + def print_concordance(self, word, width=80, lines=25): + """ + Print concordance lines given the query word. + :param word: The target word or phrase (a list of strings) + :type word: str or list + :param lines: The number of lines to display (default=25) + :type lines: int + :param width: The width of each line, in characters (default=80) + :type width: int + :param save: The option to save the concordance. + :type save: bool + """ + concordance_list = self.find_concordance(word, width=width) + + if not concordance_list: + print("no matches") + else: + lines = min(lines, len(concordance_list)) + print(f"Displaying {lines} of {len(concordance_list)} matches:") + for i, concordance_line in enumerate(concordance_list[:lines]): + print(concordance_line.line) + + +class TokenSearcher: + """ + A class that makes it easier to use regular expressions to search + over tokenized strings. The tokenized string is converted to a + string where tokens are marked with angle brackets -- e.g., + ``''``. The regular expression + passed to the ``findall()`` method is modified to treat angle + brackets as non-capturing parentheses, in addition to matching the + token boundaries; and to have ``'.'`` not match the angle brackets. + """ + + def __init__(self, tokens): + self._raw = "".join("<" + w + ">" for w in tokens) + + def findall(self, regexp): + """ + Find instances of the regular expression in the text. + The text is a list of tokens, and a regexp pattern to match + a single token must be surrounded by angle brackets. E.g. + + >>> from nltk.text import TokenSearcher + >>> from nltk.book import text1, text5, text9 + >>> text5.findall("<.*><.*>") + you rule bro; telling you bro; u twizted bro + >>> text1.findall("(<.*>)") + monied; nervous; dangerous; white; white; white; pious; queer; good; + mature; white; Cape; great; wise; wise; butterless; white; fiendish; + pale; furious; better; certain; complete; dismasted; younger; brave; + brave; brave; brave + >>> text9.findall("{3,}") + thread through those; the thought that; that the thing; the thing + that; that that thing; through these than through; them that the; + through the thick; them that they; thought that the + + :param regexp: A regular expression + :type regexp: str + """ + # preprocess the regular expression + regexp = re.sub(r"\s", "", regexp) + regexp = re.sub(r"<", "(?:<(?:", regexp) + regexp = re.sub(r">", ")>)", regexp) + regexp = re.sub(r"(?]", regexp) + + # perform the search + hits = re.findall(regexp, self._raw) + + # Sanity check + for h in hits: + if not h.startswith("<") and h.endswith(">"): + raise ValueError("Bad regexp for TokenSearcher.findall") + + # postprocess the output + hits = [h[1:-1].split("><") for h in hits] + return hits + + +class Text: + """ + A wrapper around a sequence of simple (string) tokens, which is + intended to support initial exploration of texts (via the + interactive console). Its methods perform a variety of analyses + on the text's contexts (e.g., counting, concordancing, collocation + discovery), and display the results. If you wish to write a + program which makes use of these analyses, then you should bypass + the ``Text`` class, and use the appropriate analysis function or + class directly instead. + + A ``Text`` is typically initialized from a given document or + corpus. E.g.: + + >>> import nltk.corpus + >>> from nltk.text import Text + >>> moby = Text(nltk.corpus.gutenberg.words('melville-moby_dick.txt')) + + """ + + # This defeats lazy loading, but makes things faster. This + # *shouldn't* be necessary because the corpus view *should* be + # doing intelligent caching, but without this it's running slow. + # Look into whether the caching is working correctly. + _COPY_TOKENS = True + + def __init__(self, tokens, name=None): + """ + Create a Text object. + + :param tokens: The source text. + :type tokens: sequence of str + """ + if self._COPY_TOKENS: + tokens = list(tokens) + self.tokens = tokens + + if name: + self.name = name + elif "]" in tokens[:20]: + end = tokens[:20].index("]") + self.name = " ".join(str(tok) for tok in tokens[1:end]) + else: + self.name = " ".join(str(tok) for tok in tokens[:8]) + "..." + + # //////////////////////////////////////////////////////////// + # Support item & slice access + # //////////////////////////////////////////////////////////// + + def __getitem__(self, i): + return self.tokens[i] + + def __len__(self): + return len(self.tokens) + + # //////////////////////////////////////////////////////////// + # Interactive console methods + # //////////////////////////////////////////////////////////// + + def concordance(self, word, width=79, lines=25): + """ + Prints a concordance for ``word`` with the specified context window. + Word matching is not case-sensitive. + + :param word: The target word or phrase (a list of strings) + :type word: str or list + :param width: The width of each line, in characters (default=80) + :type width: int + :param lines: The number of lines to display (default=25) + :type lines: int + + :seealso: ``ConcordanceIndex`` + """ + if "_concordance_index" not in self.__dict__: + self._concordance_index = ConcordanceIndex( + self.tokens, key=lambda s: s.lower() + ) + + return self._concordance_index.print_concordance(word, width, lines) + + def concordance_list(self, word, width=79, lines=25): + """ + Generate a concordance for ``word`` with the specified context window. + Word matching is not case-sensitive. + + :param word: The target word or phrase (a list of strings) + :type word: str or list + :param width: The width of each line, in characters (default=80) + :type width: int + :param lines: The number of lines to display (default=25) + :type lines: int + + :seealso: ``ConcordanceIndex`` + """ + if "_concordance_index" not in self.__dict__: + self._concordance_index = ConcordanceIndex( + self.tokens, key=lambda s: s.lower() + ) + return self._concordance_index.find_concordance(word, width)[:lines] + + def collocation_list(self, num=20, window_size=2): + """ + Return collocations derived from the text, ignoring stopwords. + + >>> from nltk.book import text4 + >>> text4.collocation_list()[:2] + [('United', 'States'), ('fellow', 'citizens')] + + :param num: The maximum number of collocations to return. + :type num: int + :param window_size: The number of tokens spanned by a collocation (default=2) + :type window_size: int + :rtype: list(tuple(str, str)) + """ + if not ( + "_collocations" in self.__dict__ + and self._num == num + and self._window_size == window_size + ): + self._num = num + self._window_size = window_size + + # print("Building collocations list") + from nltk.corpus import stopwords + + ignored_words = stopwords.words("english") + finder = BigramCollocationFinder.from_words(self.tokens, window_size) + finder.apply_freq_filter(2) + finder.apply_word_filter(lambda w: len(w) < 3 or w.lower() in ignored_words) + bigram_measures = BigramAssocMeasures() + self._collocations = list( + finder.nbest(bigram_measures.likelihood_ratio, num) + ) + return self._collocations + + def collocations(self, num=20, window_size=2): + """ + Print collocations derived from the text, ignoring stopwords. + + >>> from nltk.book import text4 + >>> text4.collocations() # doctest: +NORMALIZE_WHITESPACE + United States; fellow citizens; years ago; four years; Federal + Government; General Government; American people; Vice President; God + bless; Chief Justice; one another; fellow Americans; Old World; + Almighty God; Fellow citizens; Chief Magistrate; every citizen; Indian + tribes; public debt; foreign nations + + + :param num: The maximum number of collocations to print. + :type num: int + :param window_size: The number of tokens spanned by a collocation (default=2) + :type window_size: int + """ + + collocation_strings = [ + w1 + " " + w2 for w1, w2 in self.collocation_list(num, window_size) + ] + print(tokenwrap(collocation_strings, separator="; ")) + + def count(self, word): + """ + Count the number of times this word appears in the text. + """ + return self.tokens.count(word) + + def index(self, word): + """ + Find the index of the first occurrence of the word in the text. + """ + return self.tokens.index(word) + + def readability(self, method): + # code from nltk_contrib.readability + raise NotImplementedError + + def similar(self, word, num=20): + """ + Distributional similarity: find other words which appear in the + same contexts as the specified word; list most similar words first. + + :param word: The word used to seed the similarity search + :type word: str + :param num: The number of words to generate (default=20) + :type num: int + :seealso: ContextIndex.similar_words() + """ + if "_word_context_index" not in self.__dict__: + # print('Building word-context index...') + self._word_context_index = ContextIndex( + self.tokens, filter=lambda x: x.isalpha(), key=lambda s: s.lower() + ) + + # words = self._word_context_index.similar_words(word, num) + + word = word.lower() + wci = self._word_context_index._word_to_contexts + if word in wci.conditions(): + contexts = set(wci[word]) + fd = Counter( + w + for w in wci.conditions() + for c in wci[w] + if c in contexts and not w == word + ) + words = [w for w, _ in fd.most_common(num)] + print(tokenwrap(words)) + else: + print("No matches") + + def common_contexts(self, words, num=20): + """ + Find contexts where the specified words appear; list + most frequent common contexts first. + + :param words: The words used to seed the similarity search + :type words: str + :param num: The number of words to generate (default=20) + :type num: int + :seealso: ContextIndex.common_contexts() + """ + if "_word_context_index" not in self.__dict__: + # print('Building word-context index...') + self._word_context_index = ContextIndex( + self.tokens, key=lambda s: s.lower() + ) + + try: + fd = self._word_context_index.common_contexts(words, True) + if not fd: + print("No common contexts were found") + else: + ranked_contexts = [w for w, _ in fd.most_common(num)] + print(tokenwrap(w1 + "_" + w2 for w1, w2 in ranked_contexts)) + + except ValueError as e: + print(e) + + def dispersion_plot(self, words): + """ + Produce a plot showing the distribution of the words through the text. + Requires pylab to be installed. + + :param words: The words to be plotted + :type words: list(str) + :seealso: nltk.draw.dispersion_plot() + """ + from nltk.draw import dispersion_plot + + dispersion_plot(self, words) + + def _train_default_ngram_lm(self, tokenized_sents, n=3): + train_data, padded_sents = padded_everygram_pipeline(n, tokenized_sents) + model = MLE(order=n) + model.fit(train_data, padded_sents) + return model + + def generate(self, length=100, text_seed=None, random_seed=42): + """ + Print random text, generated using a trigram language model. + See also `help(nltk.lm)`. + + :param length: The length of text to generate (default=100) + :type length: int + + :param text_seed: Generation can be conditioned on preceding context. + :type text_seed: list(str) + + :param random_seed: A random seed or an instance of `random.Random`. If provided, + makes the random sampling part of generation reproducible. (default=42) + :type random_seed: int + """ + # Create the model when using it the first time. + self._tokenized_sents = [ + sent.split(" ") for sent in sent_tokenize(" ".join(self.tokens)) + ] + if not hasattr(self, "_trigram_model"): + print("Building ngram index...", file=sys.stderr) + self._trigram_model = self._train_default_ngram_lm( + self._tokenized_sents, n=3 + ) + + generated_tokens = [] + + assert length > 0, "The `length` must be more than 0." + while len(generated_tokens) < length: + for idx, token in enumerate( + self._trigram_model.generate( + length, text_seed=text_seed, random_seed=random_seed + ) + ): + if token == "": + continue + if token == "": + break + generated_tokens.append(token) + random_seed += 1 + + prefix = " ".join(text_seed) + " " if text_seed else "" + output_str = prefix + tokenwrap(generated_tokens[:length]) + print(output_str) + return output_str + + def plot(self, *args): + """ + See documentation for FreqDist.plot() + :seealso: nltk.prob.FreqDist.plot() + """ + return self.vocab().plot(*args) + + def vocab(self): + """ + :seealso: nltk.prob.FreqDist + """ + if "_vocab" not in self.__dict__: + # print("Building vocabulary index...") + self._vocab = FreqDist(self) + return self._vocab + + def findall(self, regexp): + """ + Find instances of the regular expression in the text. + The text is a list of tokens, and a regexp pattern to match + a single token must be surrounded by angle brackets. E.g. + + >>> from nltk.book import text1, text5, text9 + >>> text5.findall("<.*><.*>") + you rule bro; telling you bro; u twizted bro + >>> text1.findall("(<.*>)") + monied; nervous; dangerous; white; white; white; pious; queer; good; + mature; white; Cape; great; wise; wise; butterless; white; fiendish; + pale; furious; better; certain; complete; dismasted; younger; brave; + brave; brave; brave + >>> text9.findall("{3,}") + thread through those; the thought that; that the thing; the thing + that; that that thing; through these than through; them that the; + through the thick; them that they; thought that the + + :param regexp: A regular expression + :type regexp: str + """ + + if "_token_searcher" not in self.__dict__: + self._token_searcher = TokenSearcher(self) + + hits = self._token_searcher.findall(regexp) + hits = [" ".join(h) for h in hits] + print(tokenwrap(hits, "; ")) + + # //////////////////////////////////////////////////////////// + # Helper Methods + # //////////////////////////////////////////////////////////// + + _CONTEXT_RE = re.compile(r"\w+|[\.\!\?]") + + def _context(self, tokens, i): + """ + One left & one right token, both case-normalized. Skip over + non-sentence-final punctuation. Used by the ``ContextIndex`` + that is created for ``similar()`` and ``common_contexts()``. + """ + # Left context + j = i - 1 + while j >= 0 and not self._CONTEXT_RE.match(tokens[j]): + j -= 1 + left = tokens[j] if j != 0 else "*START*" + + # Right context + j = i + 1 + while j < len(tokens) and not self._CONTEXT_RE.match(tokens[j]): + j += 1 + right = tokens[j] if j != len(tokens) else "*END*" + + return (left, right) + + # //////////////////////////////////////////////////////////// + # String Display + # //////////////////////////////////////////////////////////// + + def __str__(self): + return "" % self.name + + def __repr__(self): + return "" % self.name + + +# Prototype only; this approach will be slow to load +class TextCollection(Text): + """A collection of texts, which can be loaded with list of texts, or + with a corpus consisting of one or more texts, and which supports + counting, concordancing, collocation discovery, etc. Initialize a + TextCollection as follows: + + >>> import nltk.corpus + >>> from nltk.text import TextCollection + >>> from nltk.book import text1, text2, text3 + >>> gutenberg = TextCollection(nltk.corpus.gutenberg) + >>> mytexts = TextCollection([text1, text2, text3]) + + Iterating over a TextCollection produces all the tokens of all the + texts in order. + """ + + def __init__(self, source): + if hasattr(source, "words"): # bridge to the text corpus reader + source = [source.words(f) for f in source.fileids()] + + self._texts = source + Text.__init__(self, LazyConcatenation(source)) + self._idf_cache = {} + + def tf(self, term, text): + """The frequency of the term in text.""" + return text.count(term) / len(text) + + def idf(self, term): + """The number of texts in the corpus divided by the + number of texts that the term appears in. + If a term does not appear in the corpus, 0.0 is returned.""" + # idf values are cached for performance. + idf = self._idf_cache.get(term) + if idf is None: + matches = len([True for text in self._texts if term in text]) + if len(self._texts) == 0: + raise ValueError("IDF undefined for empty document collection") + idf = log(len(self._texts) / matches) if matches else 0.0 + self._idf_cache[term] = idf + return idf + + def tf_idf(self, term, text): + return self.tf(term, text) * self.idf(term) + + +def demo(): + from nltk.corpus import brown + + text = Text(brown.words(categories="news")) + print(text) + print() + print("Concordance:") + text.concordance("news") + print() + print("Distributionally similar words:") + text.similar("news") + print() + print("Collocations:") + text.collocations() + print() + # print("Automatically generated text:") + # text.generate() + # print() + print("Dispersion plot:") + text.dispersion_plot(["news", "report", "said", "announced"]) + print() + print("Vocabulary plot:") + text.plot(50) + print() + print("Indexing:") + print("text[3]:", text[3]) + print("text[3:5]:", text[3:5]) + print("text.vocab()['news']:", text.vocab()["news"]) + + +if __name__ == "__main__": + demo() + +__all__ = [ + "ContextIndex", + "ConcordanceIndex", + "TokenSearcher", + "Text", + "TextCollection", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tgrep.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tgrep.py new file mode 100644 index 0000000..6c227b4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tgrep.py @@ -0,0 +1,1039 @@ +#!/usr/bin/env python +# +# Natural Language Toolkit: TGrep search +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Will Roberts +# URL: +# For license information, see LICENSE.TXT + +""" +============================================ + TGrep search implementation for NLTK trees +============================================ + +This module supports TGrep2 syntax for matching parts of NLTK Trees. +Note that many tgrep operators require the tree passed to be a +``ParentedTree``. + +External links: + +- `Tgrep tutorial `_ +- `Tgrep2 manual `_ +- `Tgrep2 source `_ + +Usage +===== + +>>> from nltk.tree import ParentedTree +>>> from nltk.tgrep import tgrep_nodes, tgrep_positions +>>> tree = ParentedTree.fromstring('(S (NP (DT the) (JJ big) (NN dog)) (VP bit) (NP (DT a) (NN cat)))') +>>> list(tgrep_nodes('NN', [tree])) +[[ParentedTree('NN', ['dog']), ParentedTree('NN', ['cat'])]] +>>> list(tgrep_positions('NN', [tree])) +[[(0, 2), (2, 1)]] +>>> list(tgrep_nodes('DT', [tree])) +[[ParentedTree('DT', ['the']), ParentedTree('DT', ['a'])]] +>>> list(tgrep_nodes('DT $ JJ', [tree])) +[[ParentedTree('DT', ['the'])]] + +This implementation adds syntax to select nodes based on their NLTK +tree position. This syntax is ``N`` plus a Python tuple representing +the tree position. For instance, ``N()``, ``N(0,)``, ``N(0,0)`` are +valid node selectors. Example: + +>>> tree = ParentedTree.fromstring('(S (NP (DT the) (JJ big) (NN dog)) (VP bit) (NP (DT a) (NN cat)))') +>>> tree[0,0] +ParentedTree('DT', ['the']) +>>> tree[0,0].treeposition() +(0, 0) +>>> list(tgrep_nodes('N(0,0)', [tree])) +[[ParentedTree('DT', ['the'])]] + +Caveats: +======== + +- Link modifiers: "?" and "=" are not implemented. +- Tgrep compatibility: Using "@" for "!", "{" for "<", "}" for ">" are + not implemented. +- The "=" and "~" links are not implemented. + +Known Issues: +============= + +- There are some issues with link relations involving leaf nodes + (which are represented as bare strings in NLTK trees). For + instance, consider the tree:: + + (S (A x)) + + The search string ``* !>> S`` should select all nodes which are not + dominated in some way by an ``S`` node (i.e., all nodes which are + not descendants of an ``S``). Clearly, in this tree, the only node + which fulfills this criterion is the top node (since it is not + dominated by anything). However, the code here will find both the + top node and the leaf node ``x``. This is because we cannot recover + the parent of the leaf, since it is stored as a bare string. + + A possible workaround, when performing this kind of search, would be + to filter out all leaf nodes. + +Implementation notes +==================== + +This implementation is (somewhat awkwardly) based on lambda functions +which are predicates on a node. A predicate is a function which is +either True or False; using a predicate function, we can identify sets +of nodes with particular properties. A predicate function, could, for +instance, return True only if a particular node has a label matching a +particular regular expression, and has a daughter node which has no +sisters. Because tgrep2 search strings can do things statefully (such +as substituting in macros, and binding nodes with node labels), the +actual predicate function is declared with three arguments:: + + pred = lambda n, m, l: return True # some logic here + +``n`` + is a node in a tree; this argument must always be given + +``m`` + contains a dictionary, mapping macro names onto predicate functions + +``l`` + is a dictionary to map node labels onto nodes in the tree + +``m`` and ``l`` are declared to default to ``None``, and so need not be +specified in a call to a predicate. Predicates which call other +predicates must always pass the value of these arguments on. The +top-level predicate (constructed by ``_tgrep_exprs_action``) binds the +macro definitions to ``m`` and initialises ``l`` to an empty dictionary. +""" + +import functools +import re + +try: + import pyparsing +except ImportError: + print("Warning: nltk.tgrep will not work without the `pyparsing` package") + print("installed.") + +import nltk.tree + + +class TgrepException(Exception): + """Tgrep exception type.""" + + pass + + +def ancestors(node): + """ + Returns the list of all nodes dominating the given tree node. + This method will not work with leaf nodes, since there is no way + to recover the parent. + """ + results = [] + try: + current = node.parent() + except AttributeError: + # if node is a leaf, we cannot retrieve its parent + return results + while current: + results.append(current) + current = current.parent() + return results + + +def unique_ancestors(node): + """ + Returns the list of all nodes dominating the given node, where + there is only a single path of descent. + """ + results = [] + try: + current = node.parent() + except AttributeError: + # if node is a leaf, we cannot retrieve its parent + return results + while current and len(current) == 1: + results.append(current) + current = current.parent() + return results + + +def _descendants(node): + """ + Returns the list of all nodes which are descended from the given + tree node in some way. + """ + try: + treepos = node.treepositions() + except AttributeError: + return [] + return [node[x] for x in treepos[1:]] + + +def _leftmost_descendants(node): + """ + Returns the set of all nodes descended in some way through + left branches from this node. + """ + try: + treepos = node.treepositions() + except AttributeError: + return [] + return [node[x] for x in treepos[1:] if all(y == 0 for y in x)] + + +def _rightmost_descendants(node): + """ + Returns the set of all nodes descended in some way through + right branches from this node. + """ + try: + rightmost_leaf = max(node.treepositions()) + except AttributeError: + return [] + return [node[rightmost_leaf[:i]] for i in range(1, len(rightmost_leaf) + 1)] + + +def _istree(obj): + """Predicate to check whether `obj` is a nltk.tree.Tree.""" + return isinstance(obj, nltk.tree.Tree) + + +def _unique_descendants(node): + """ + Returns the list of all nodes descended from the given node, where + there is only a single path of descent. + """ + results = [] + current = node + while current and _istree(current) and len(current) == 1: + current = current[0] + results.append(current) + return results + + +def _before(node): + """ + Returns the set of all nodes that are before the given node. + """ + try: + pos = node.treeposition() + tree = node.root() + except AttributeError: + return [] + return [tree[x] for x in tree.treepositions() if x[: len(pos)] < pos[: len(x)]] + + +def _immediately_before(node): + """ + Returns the set of all nodes that are immediately before the given + node. + + Tree node A immediately precedes node B if the last terminal + symbol (word) produced by A immediately precedes the first + terminal symbol produced by B. + """ + try: + pos = node.treeposition() + tree = node.root() + except AttributeError: + return [] + # go "upwards" from pos until there is a place we can go to the left + idx = len(pos) - 1 + while 0 <= idx and pos[idx] == 0: + idx -= 1 + if idx < 0: + return [] + pos = list(pos[: idx + 1]) + pos[-1] -= 1 + before = tree[pos] + return [before] + _rightmost_descendants(before) + + +def _after(node): + """ + Returns the set of all nodes that are after the given node. + """ + try: + pos = node.treeposition() + tree = node.root() + except AttributeError: + return [] + return [tree[x] for x in tree.treepositions() if x[: len(pos)] > pos[: len(x)]] + + +def _immediately_after(node): + """ + Returns the set of all nodes that are immediately after the given + node. + + Tree node A immediately follows node B if the first terminal + symbol (word) produced by A immediately follows the last + terminal symbol produced by B. + """ + try: + pos = node.treeposition() + tree = node.root() + current = node.parent() + except AttributeError: + return [] + # go "upwards" from pos until there is a place we can go to the + # right + idx = len(pos) - 1 + while 0 <= idx and pos[idx] == len(current) - 1: + idx -= 1 + current = current.parent() + if idx < 0: + return [] + pos = list(pos[: idx + 1]) + pos[-1] += 1 + after = tree[pos] + return [after] + _leftmost_descendants(after) + + +def _tgrep_node_literal_value(node): + """ + Gets the string value of a given parse tree node, for comparison + using the tgrep node literal predicates. + """ + return node.label() if _istree(node) else str(node) + + +def _tgrep_macro_use_action(_s, _l, tokens): + """ + Builds a lambda function which looks up the macro name used. + """ + assert len(tokens) == 1 + assert tokens[0][0] == "@" + macro_name = tokens[0][1:] + + def macro_use(n, m=None, l=None): + if m is None or macro_name not in m: + raise TgrepException(f"macro {macro_name} not defined") + return m[macro_name](n, m, l) + + return macro_use + + +def _tgrep_node_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + depending on the name of its node. + """ + if tokens[0] == "'": + # strip initial apostrophe (tgrep2 print command) + tokens = tokens[1:] + if len(tokens) > 1: + # disjunctive definition of a node name + assert list(set(tokens[1::2])) == ["|"] + # recursively call self to interpret each node name definition + tokens = [_tgrep_node_action(None, None, [node]) for node in tokens[::2]] + # capture tokens and return the disjunction + return (lambda t: lambda n, m=None, l=None: any(f(n, m, l) for f in t))(tokens) + else: + if hasattr(tokens[0], "__call__"): + # this is a previously interpreted parenthetical node + # definition (lambda function) + return tokens[0] + elif tokens[0] == "*" or tokens[0] == "__": + return lambda n, m=None, l=None: True + elif tokens[0].startswith('"'): + assert tokens[0].endswith('"') + node_lit = tokens[0][1:-1].replace('\\"', '"').replace("\\\\", "\\") + return ( + lambda s: lambda n, m=None, l=None: _tgrep_node_literal_value(n) == s + )(node_lit) + elif tokens[0].startswith("/"): + assert tokens[0].endswith("/") + node_lit = tokens[0][1:-1] + return ( + lambda r: lambda n, m=None, l=None: r.search( + _tgrep_node_literal_value(n) + ) + )(re.compile(node_lit)) + elif tokens[0].startswith("i@"): + node_func = _tgrep_node_action(_s, _l, [tokens[0][2:].lower()]) + return ( + lambda f: lambda n, m=None, l=None: f( + _tgrep_node_literal_value(n).lower() + ) + )(node_func) + else: + return ( + lambda s: lambda n, m=None, l=None: _tgrep_node_literal_value(n) == s + )(tokens[0]) + + +def _tgrep_parens_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + from a parenthetical notation. + """ + assert len(tokens) == 3 + assert tokens[0] == "(" + assert tokens[2] == ")" + return tokens[1] + + +def _tgrep_nltk_tree_pos_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + which returns true if the node is located at a specific tree + position. + """ + # recover the tuple from the parsed string + node_tree_position = tuple(int(x) for x in tokens if x.isdigit()) + # capture the node's tree position + return ( + lambda i: lambda n, m=None, l=None: ( + hasattr(n, "treeposition") and n.treeposition() == i + ) + )(node_tree_position) + + +def _tgrep_relation_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + depending on its relation to other nodes in the tree. + """ + # process negation first if needed + negated = False + if tokens[0] == "!": + negated = True + tokens = tokens[1:] + if tokens[0] == "[": + # process square-bracketed relation expressions + assert len(tokens) == 3 + assert tokens[2] == "]" + retval = tokens[1] + else: + # process operator-node relation expressions + assert len(tokens) == 2 + operator, predicate = tokens + # A < B A is the parent of (immediately dominates) B. + if operator == "<": + retval = lambda n, m=None, l=None: ( + _istree(n) and any(predicate(x, m, l) for x in n) + ) + # A > B A is the child of B. + elif operator == ">": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and predicate(n.parent(), m, l) + ) + # A <, B Synonymous with A <1 B. + elif operator == "<," or operator == "<1": + retval = lambda n, m=None, l=None: ( + _istree(n) and bool(list(n)) and predicate(n[0], m, l) + ) + # A >, B Synonymous with A >1 B. + elif operator == ">," or operator == ">1": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and (n is n.parent()[0]) + and predicate(n.parent(), m, l) + ) + # A N B A is the Nth child of B (the first child is >1). + elif operator[0] == ">" and operator[1:].isdigit(): + idx = int(operator[1:]) + # capture the index parameter + retval = ( + lambda i: lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and 0 <= i < len(n.parent()) + and (n is n.parent()[i]) + and predicate(n.parent(), m, l) + ) + )(idx - 1) + # A <' B B is the last child of A (also synonymous with A <-1 B). + # A <- B B is the last child of A (synonymous with A <-1 B). + elif operator == "<'" or operator == "<-" or operator == "<-1": + retval = lambda n, m=None, l=None: ( + _istree(n) and bool(list(n)) and predicate(n[-1], m, l) + ) + # A >' B A is the last child of B (also synonymous with A >-1 B). + # A >- B A is the last child of B (synonymous with A >-1 B). + elif operator == ">'" or operator == ">-" or operator == ">-1": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and (n is n.parent()[-1]) + and predicate(n.parent(), m, l) + ) + # A <-N B B is the N th-to-last child of A (the last child is <-1). + elif operator[:2] == "<-" and operator[2:].isdigit(): + idx = -int(operator[2:]) + # capture the index parameter + retval = ( + lambda i: lambda n, m=None, l=None: ( + _istree(n) + and bool(list(n)) + and 0 <= (i + len(n)) < len(n) + and predicate(n[i + len(n)], m, l) + ) + )(idx) + # A >-N B A is the N th-to-last child of B (the last child is >-1). + elif operator[:2] == ">-" and operator[2:].isdigit(): + idx = -int(operator[2:]) + # capture the index parameter + retval = ( + lambda i: lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and 0 <= (i + len(n.parent())) < len(n.parent()) + and (n is n.parent()[i + len(n.parent())]) + and predicate(n.parent(), m, l) + ) + )(idx) + # A <: B B is the only child of A + elif operator == "<:": + retval = lambda n, m=None, l=None: ( + _istree(n) and len(n) == 1 and predicate(n[0], m, l) + ) + # A >: B A is the only child of B. + elif operator == ">:": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and len(n.parent()) == 1 + and predicate(n.parent(), m, l) + ) + # A << B A dominates B (A is an ancestor of B). + elif operator == "<<": + retval = lambda n, m=None, l=None: ( + _istree(n) and any(predicate(x, m, l) for x in _descendants(n)) + ) + # A >> B A is dominated by B (A is a descendant of B). + elif operator == ">>": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in ancestors(n) + ) + # A <<, B B is a left-most descendant of A. + elif operator == "<<," or operator == "<<1": + retval = lambda n, m=None, l=None: ( + _istree(n) and any(predicate(x, m, l) for x in _leftmost_descendants(n)) + ) + # A >>, B A is a left-most descendant of B. + elif operator == ">>,": + retval = lambda n, m=None, l=None: any( + (predicate(x, m, l) and n in _leftmost_descendants(x)) + for x in ancestors(n) + ) + # A <<' B B is a right-most descendant of A. + elif operator == "<<'": + retval = lambda n, m=None, l=None: ( + _istree(n) + and any(predicate(x, m, l) for x in _rightmost_descendants(n)) + ) + # A >>' B A is a right-most descendant of B. + elif operator == ">>'": + retval = lambda n, m=None, l=None: any( + (predicate(x, m, l) and n in _rightmost_descendants(x)) + for x in ancestors(n) + ) + # A <<: B There is a single path of descent from A and B is on it. + elif operator == "<<:": + retval = lambda n, m=None, l=None: ( + _istree(n) and any(predicate(x, m, l) for x in _unique_descendants(n)) + ) + # A >>: B There is a single path of descent from B and A is on it. + elif operator == ">>:": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in unique_ancestors(n) + ) + # A . B A immediately precedes B. + elif operator == ".": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in _immediately_after(n) + ) + # A , B A immediately follows B. + elif operator == ",": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in _immediately_before(n) + ) + # A .. B A precedes B. + elif operator == "..": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in _after(n) + ) + # A ,, B A follows B. + elif operator == ",,": + retval = lambda n, m=None, l=None: any( + predicate(x, m, l) for x in _before(n) + ) + # A $ B A is a sister of B (and A != B). + elif operator == "$" or operator == "%": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and bool(n.parent()) + and any(predicate(x, m, l) for x in n.parent() if x is not n) + ) + # A $. B A is a sister of and immediately precedes B. + elif operator == "$." or operator == "%.": + retval = lambda n, m=None, l=None: ( + hasattr(n, "right_sibling") + and bool(n.right_sibling()) + and predicate(n.right_sibling(), m, l) + ) + # A $, B A is a sister of and immediately follows B. + elif operator == "$," or operator == "%,": + retval = lambda n, m=None, l=None: ( + hasattr(n, "left_sibling") + and bool(n.left_sibling()) + and predicate(n.left_sibling(), m, l) + ) + # A $.. B A is a sister of and precedes B. + elif operator == "$.." or operator == "%..": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and hasattr(n, "parent_index") + and bool(n.parent()) + and any(predicate(x, m, l) for x in n.parent()[n.parent_index() + 1 :]) + ) + # A $,, B A is a sister of and follows B. + elif operator == "$,," or operator == "%,,": + retval = lambda n, m=None, l=None: ( + hasattr(n, "parent") + and hasattr(n, "parent_index") + and bool(n.parent()) + and any(predicate(x, m, l) for x in n.parent()[: n.parent_index()]) + ) + else: + raise TgrepException(f'cannot interpret tgrep operator "{operator}"') + # now return the built function + if negated: + return (lambda r: (lambda n, m=None, l=None: not r(n, m, l)))(retval) + else: + return retval + + +def _tgrep_conjunction_action(_s, _l, tokens, join_char="&"): + """ + Builds a lambda function representing a predicate on a tree node + from the conjunction of several other such lambda functions. + + This is prototypically called for expressions like + (`tgrep_rel_conjunction`):: + + < NP & < AP < VP + + where tokens is a list of predicates representing the relations + (`< NP`, `< AP`, and `< VP`), possibly with the character `&` + included (as in the example here). + + This is also called for expressions like (`tgrep_node_expr2`):: + + NP < NN + S=s < /NP/=n : s < /VP/=v : n .. v + + tokens[0] is a tgrep_expr predicate; tokens[1:] are an (optional) + list of segmented patterns (`tgrep_expr_labeled`, processed by + `_tgrep_segmented_pattern_action`). + """ + # filter out the ampersand + tokens = [x for x in tokens if x != join_char] + if len(tokens) == 1: + return tokens[0] + else: + return ( + lambda ts: lambda n, m=None, l=None: all( + predicate(n, m, l) for predicate in ts + ) + )(tokens) + + +def _tgrep_segmented_pattern_action(_s, _l, tokens): + """ + Builds a lambda function representing a segmented pattern. + + Called for expressions like (`tgrep_expr_labeled`):: + + =s .. =v < =n + + This is a segmented pattern, a tgrep2 expression which begins with + a node label. + + The problem is that for segemented_pattern_action (': =v < =s'), + the first element (in this case, =v) is specifically selected by + virtue of matching a particular node in the tree; to retrieve + the node, we need the label, not a lambda function. For node + labels inside a tgrep_node_expr, we need a lambda function which + returns true if the node visited is the same as =v. + + We solve this by creating two copies of a node_label_use in the + grammar; the label use inside a tgrep_expr_labeled has a separate + parse action to the pred use inside a node_expr. See + `_tgrep_node_label_use_action` and + `_tgrep_node_label_pred_use_action`. + """ + # tokens[0] is a string containing the node label + node_label = tokens[0] + # tokens[1:] is an (optional) list of predicates which must all + # hold of the bound node + reln_preds = tokens[1:] + + def pattern_segment_pred(n, m=None, l=None): + """This predicate function ignores its node argument.""" + # look up the bound node using its label + if l is None or node_label not in l: + raise TgrepException(f"node_label ={node_label} not bound in pattern") + node = l[node_label] + # match the relation predicates against the node + return all(pred(node, m, l) for pred in reln_preds) + + return pattern_segment_pred + + +def _tgrep_node_label_use_action(_s, _l, tokens): + """ + Returns the node label used to begin a tgrep_expr_labeled. See + `_tgrep_segmented_pattern_action`. + + Called for expressions like (`tgrep_node_label_use`):: + + =s + + when they appear as the first element of a `tgrep_expr_labeled` + expression (see `_tgrep_segmented_pattern_action`). + + It returns the node label. + """ + assert len(tokens) == 1 + assert tokens[0].startswith("=") + return tokens[0][1:] + + +def _tgrep_node_label_pred_use_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + which describes the use of a previously bound node label. + + Called for expressions like (`tgrep_node_label_use_pred`):: + + =s + + when they appear inside a tgrep_node_expr (for example, inside a + relation). The predicate returns true if and only if its node + argument is identical the the node looked up in the node label + dictionary using the node's label. + """ + assert len(tokens) == 1 + assert tokens[0].startswith("=") + node_label = tokens[0][1:] + + def node_label_use_pred(n, m=None, l=None): + # look up the bound node using its label + if l is None or node_label not in l: + raise TgrepException(f"node_label ={node_label} not bound in pattern") + node = l[node_label] + # truth means the given node is this node + return n is node + + return node_label_use_pred + + +def _tgrep_bind_node_label_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + which can optionally bind a matching node into the tgrep2 string's + label_dict. + + Called for expressions like (`tgrep_node_expr2`):: + + /NP/ + @NP=n + """ + # tokens[0] is a tgrep_node_expr + if len(tokens) == 1: + return tokens[0] + else: + # if present, tokens[1] is the character '=', and tokens[2] is + # a tgrep_node_label, a string value containing the node label + assert len(tokens) == 3 + assert tokens[1] == "=" + node_pred = tokens[0] + node_label = tokens[2] + + def node_label_bind_pred(n, m=None, l=None): + if node_pred(n, m, l): + # bind `n` into the dictionary `l` + if l is None: + raise TgrepException( + "cannot bind node_label {}: label_dict is None".format( + node_label + ) + ) + l[node_label] = n + return True + else: + return False + + return node_label_bind_pred + + +def _tgrep_rel_disjunction_action(_s, _l, tokens): + """ + Builds a lambda function representing a predicate on a tree node + from the disjunction of several other such lambda functions. + """ + # filter out the pipe + tokens = [x for x in tokens if x != "|"] + if len(tokens) == 1: + return tokens[0] + elif len(tokens) == 2: + return (lambda a, b: lambda n, m=None, l=None: a(n, m, l) or b(n, m, l))( + tokens[0], tokens[1] + ) + + +def _macro_defn_action(_s, _l, tokens): + """ + Builds a dictionary structure which defines the given macro. + """ + assert len(tokens) == 3 + assert tokens[0] == "@" + return {tokens[1]: tokens[2]} + + +def _tgrep_exprs_action(_s, _l, tokens): + """ + This is the top-lebel node in a tgrep2 search string; the + predicate function it returns binds together all the state of a + tgrep2 search string. + + Builds a lambda function representing a predicate on a tree node + from the disjunction of several tgrep expressions. Also handles + macro definitions and macro name binding, and node label + definitions and node label binding. + """ + if len(tokens) == 1: + return lambda n, m=None, l=None: tokens[0](n, None, {}) + # filter out all the semicolons + tokens = [x for x in tokens if x != ";"] + # collect all macro definitions + macro_dict = {} + macro_defs = [tok for tok in tokens if isinstance(tok, dict)] + for macro_def in macro_defs: + macro_dict.update(macro_def) + # collect all tgrep expressions + tgrep_exprs = [tok for tok in tokens if not isinstance(tok, dict)] + # create a new scope for the node label dictionary + def top_level_pred(n, m=macro_dict, l=None): + label_dict = {} + # bind macro definitions and OR together all tgrep_exprs + return any(predicate(n, m, label_dict) for predicate in tgrep_exprs) + + return top_level_pred + + +def _build_tgrep_parser(set_parse_actions=True): + """ + Builds a pyparsing-based parser object for tokenizing and + interpreting tgrep search strings. + """ + tgrep_op = pyparsing.Optional("!") + pyparsing.Regex("[$%,.<>][%,.<>0-9-':]*") + tgrep_qstring = pyparsing.QuotedString( + quoteChar='"', escChar="\\", unquoteResults=False + ) + tgrep_node_regex = pyparsing.QuotedString( + quoteChar="/", escChar="\\", unquoteResults=False + ) + tgrep_qstring_icase = pyparsing.Regex('i@\\"(?:[^"\\n\\r\\\\]|(?:\\\\.))*\\"') + tgrep_node_regex_icase = pyparsing.Regex("i@\\/(?:[^/\\n\\r\\\\]|(?:\\\\.))*\\/") + tgrep_node_literal = pyparsing.Regex("[^][ \r\t\n;:.,&|<>()$!@%'^=]+") + tgrep_expr = pyparsing.Forward() + tgrep_relations = pyparsing.Forward() + tgrep_parens = pyparsing.Literal("(") + tgrep_expr + ")" + tgrep_nltk_tree_pos = ( + pyparsing.Literal("N(") + + pyparsing.Optional( + pyparsing.Word(pyparsing.nums) + + "," + + pyparsing.Optional( + pyparsing.delimitedList(pyparsing.Word(pyparsing.nums), delim=",") + + pyparsing.Optional(",") + ) + ) + + ")" + ) + tgrep_node_label = pyparsing.Regex("[A-Za-z0-9]+") + tgrep_node_label_use = pyparsing.Combine("=" + tgrep_node_label) + # see _tgrep_segmented_pattern_action + tgrep_node_label_use_pred = tgrep_node_label_use.copy() + macro_name = pyparsing.Regex("[^];:.,&|<>()[$!@%'^=\r\t\n ]+") + macro_name.setWhitespaceChars("") + macro_use = pyparsing.Combine("@" + macro_name) + tgrep_node_expr = ( + tgrep_node_label_use_pred + | macro_use + | tgrep_nltk_tree_pos + | tgrep_qstring_icase + | tgrep_node_regex_icase + | tgrep_qstring + | tgrep_node_regex + | "*" + | tgrep_node_literal + ) + tgrep_node_expr2 = ( + tgrep_node_expr + + pyparsing.Literal("=").setWhitespaceChars("") + + tgrep_node_label.copy().setWhitespaceChars("") + ) | tgrep_node_expr + tgrep_node = tgrep_parens | ( + pyparsing.Optional("'") + + tgrep_node_expr2 + + pyparsing.ZeroOrMore("|" + tgrep_node_expr) + ) + tgrep_brackets = pyparsing.Optional("!") + "[" + tgrep_relations + "]" + tgrep_relation = tgrep_brackets | (tgrep_op + tgrep_node) + tgrep_rel_conjunction = pyparsing.Forward() + tgrep_rel_conjunction << ( + tgrep_relation + + pyparsing.ZeroOrMore(pyparsing.Optional("&") + tgrep_rel_conjunction) + ) + tgrep_relations << tgrep_rel_conjunction + pyparsing.ZeroOrMore( + "|" + tgrep_relations + ) + tgrep_expr << tgrep_node + pyparsing.Optional(tgrep_relations) + tgrep_expr_labeled = tgrep_node_label_use + pyparsing.Optional(tgrep_relations) + tgrep_expr2 = tgrep_expr + pyparsing.ZeroOrMore(":" + tgrep_expr_labeled) + macro_defn = ( + pyparsing.Literal("@") + pyparsing.White().suppress() + macro_name + tgrep_expr2 + ) + tgrep_exprs = ( + pyparsing.Optional(macro_defn + pyparsing.ZeroOrMore(";" + macro_defn) + ";") + + tgrep_expr2 + + pyparsing.ZeroOrMore(";" + (macro_defn | tgrep_expr2)) + + pyparsing.ZeroOrMore(";").suppress() + ) + if set_parse_actions: + tgrep_node_label_use.setParseAction(_tgrep_node_label_use_action) + tgrep_node_label_use_pred.setParseAction(_tgrep_node_label_pred_use_action) + macro_use.setParseAction(_tgrep_macro_use_action) + tgrep_node.setParseAction(_tgrep_node_action) + tgrep_node_expr2.setParseAction(_tgrep_bind_node_label_action) + tgrep_parens.setParseAction(_tgrep_parens_action) + tgrep_nltk_tree_pos.setParseAction(_tgrep_nltk_tree_pos_action) + tgrep_relation.setParseAction(_tgrep_relation_action) + tgrep_rel_conjunction.setParseAction(_tgrep_conjunction_action) + tgrep_relations.setParseAction(_tgrep_rel_disjunction_action) + macro_defn.setParseAction(_macro_defn_action) + # the whole expression is also the conjunction of two + # predicates: the first node predicate, and the remaining + # relation predicates + tgrep_expr.setParseAction(_tgrep_conjunction_action) + tgrep_expr_labeled.setParseAction(_tgrep_segmented_pattern_action) + tgrep_expr2.setParseAction( + functools.partial(_tgrep_conjunction_action, join_char=":") + ) + tgrep_exprs.setParseAction(_tgrep_exprs_action) + return tgrep_exprs.ignore("#" + pyparsing.restOfLine) + + +def tgrep_tokenize(tgrep_string): + """ + Tokenizes a TGrep search string into separate tokens. + """ + parser = _build_tgrep_parser(False) + if isinstance(tgrep_string, bytes): + tgrep_string = tgrep_string.decode() + return list(parser.parseString(tgrep_string)) + + +def tgrep_compile(tgrep_string): + """ + Parses (and tokenizes, if necessary) a TGrep search string into a + lambda function. + """ + parser = _build_tgrep_parser(True) + if isinstance(tgrep_string, bytes): + tgrep_string = tgrep_string.decode() + return list(parser.parseString(tgrep_string, parseAll=True))[0] + + +def treepositions_no_leaves(tree): + """ + Returns all the tree positions in the given tree which are not + leaf nodes. + """ + treepositions = tree.treepositions() + # leaves are treeposition tuples that are not prefixes of any + # other treeposition + prefixes = set() + for pos in treepositions: + for length in range(len(pos)): + prefixes.add(pos[:length]) + return [pos for pos in treepositions if pos in prefixes] + + +def tgrep_positions(pattern, trees, search_leaves=True): + """ + Return the tree positions in the trees which match the given pattern. + + :param pattern: a tgrep search pattern + :type pattern: str or output of tgrep_compile() + :param trees: a sequence of NLTK trees (usually ParentedTrees) + :type trees: iter(ParentedTree) or iter(Tree) + :param search_leaves: whether to return matching leaf nodes + :type search_leaves: bool + :rtype: iter(tree positions) + """ + + if isinstance(pattern, (bytes, str)): + pattern = tgrep_compile(pattern) + + for tree in trees: + try: + if search_leaves: + positions = tree.treepositions() + else: + positions = treepositions_no_leaves(tree) + yield [position for position in positions if pattern(tree[position])] + except AttributeError: + yield [] + + +def tgrep_nodes(pattern, trees, search_leaves=True): + """ + Return the tree nodes in the trees which match the given pattern. + + :param pattern: a tgrep search pattern + :type pattern: str or output of tgrep_compile() + :param trees: a sequence of NLTK trees (usually ParentedTrees) + :type trees: iter(ParentedTree) or iter(Tree) + :param search_leaves: whether to return matching leaf nodes + :type search_leaves: bool + :rtype: iter(tree nodes) + """ + + if isinstance(pattern, (bytes, str)): + pattern = tgrep_compile(pattern) + + for tree in trees: + try: + if search_leaves: + positions = tree.treepositions() + else: + positions = treepositions_no_leaves(tree) + yield [tree[position] for position in positions if pattern(tree[position])] + except AttributeError: + yield [] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__init__.py new file mode 100644 index 0000000..f38da43 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__init__.py @@ -0,0 +1,132 @@ +# Natural Language Toolkit: Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird (minor additions) +# Contributors: matthewmc, clouds56 +# URL: +# For license information, see LICENSE.TXT + +r""" +NLTK Tokenizer Package + +Tokenizers divide strings into lists of substrings. For example, +tokenizers can be used to find the words and punctuation in a string: + + >>> from nltk.tokenize import word_tokenize + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me + ... two of them.\n\nThanks.''' + >>> word_tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.', + 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + +This particular tokenizer requires the Punkt sentence tokenization +models to be installed. NLTK also provides a simpler, +regular-expression based tokenizer, which splits text on whitespace +and punctuation: + + >>> from nltk.tokenize import wordpunct_tokenize + >>> wordpunct_tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3', '.', '88', 'in', 'New', 'York', '.', + 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + +We can also operate at the level of sentences, using the sentence +tokenizer directly as follows: + + >>> from nltk.tokenize import sent_tokenize, word_tokenize + >>> sent_tokenize(s) + ['Good muffins cost $3.88\nin New York.', 'Please buy me\ntwo of them.', 'Thanks.'] + >>> [word_tokenize(t) for t in sent_tokenize(s)] # doctest: +NORMALIZE_WHITESPACE + [['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.'], + ['Please', 'buy', 'me', 'two', 'of', 'them', '.'], ['Thanks', '.']] + +Caution: when tokenizing a Unicode string, make sure you are not +using an encoded version of the string (it may be necessary to +decode it first, e.g. with ``s.decode("utf8")``. + +NLTK tokenizers can produce token-spans, represented as tuples of integers +having the same semantics as string slices, to support efficient comparison +of tokenizers. (These methods are implemented as generators.) + + >>> from nltk.tokenize import WhitespaceTokenizer + >>> list(WhitespaceTokenizer().span_tokenize(s)) # doctest: +NORMALIZE_WHITESPACE + [(0, 4), (5, 12), (13, 17), (18, 23), (24, 26), (27, 30), (31, 36), (38, 44), + (45, 48), (49, 51), (52, 55), (56, 58), (59, 64), (66, 73)] + +There are numerous ways to tokenize text. If you need more control over +tokenization, see the other methods provided in this package. + +For further information, please see Chapter 3 of the NLTK book. +""" + +import re + +from nltk.data import load +from nltk.tokenize.casual import TweetTokenizer, casual_tokenize +from nltk.tokenize.destructive import NLTKWordTokenizer +from nltk.tokenize.legality_principle import LegalitySyllableTokenizer +from nltk.tokenize.mwe import MWETokenizer +from nltk.tokenize.punkt import PunktSentenceTokenizer +from nltk.tokenize.regexp import ( + BlanklineTokenizer, + RegexpTokenizer, + WhitespaceTokenizer, + WordPunctTokenizer, + blankline_tokenize, + regexp_tokenize, + wordpunct_tokenize, +) +from nltk.tokenize.repp import ReppTokenizer +from nltk.tokenize.sexpr import SExprTokenizer, sexpr_tokenize +from nltk.tokenize.simple import ( + LineTokenizer, + SpaceTokenizer, + TabTokenizer, + line_tokenize, +) +from nltk.tokenize.sonority_sequencing import SyllableTokenizer +from nltk.tokenize.stanford_segmenter import StanfordSegmenter +from nltk.tokenize.texttiling import TextTilingTokenizer +from nltk.tokenize.toktok import ToktokTokenizer +from nltk.tokenize.treebank import TreebankWordDetokenizer, TreebankWordTokenizer +from nltk.tokenize.util import regexp_span_tokenize, string_span_tokenize + + +# Standard sentence tokenizer. +def sent_tokenize(text, language="english"): + """ + Return a sentence-tokenized copy of *text*, + using NLTK's recommended sentence tokenizer + (currently :class:`.PunktSentenceTokenizer` + for the specified language). + + :param text: text to split into sentences + :param language: the model name in the Punkt corpus + """ + tokenizer = load(f"tokenizers/punkt/{language}.pickle") + return tokenizer.tokenize(text) + + +# Standard word tokenizer. +_treebank_word_tokenizer = NLTKWordTokenizer() + + +def word_tokenize(text, language="english", preserve_line=False): + """ + Return a tokenized copy of *text*, + using NLTK's recommended word tokenizer + (currently an improved :class:`.TreebankWordTokenizer` + along with :class:`.PunktSentenceTokenizer` + for the specified language). + + :param text: text to split into words + :type text: str + :param language: the model name in the Punkt corpus + :type language: str + :param preserve_line: A flag to decide whether to sentence tokenize the text or not. + :type preserve_line: bool + """ + sentences = [text] if preserve_line else sent_tokenize(text, language) + return [ + token for sent in sentences for token in _treebank_word_tokenizer.tokenize(sent) + ] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8a42c72 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..8b4b9c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/casual.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/casual.cpython-310.pyc new file mode 100644 index 0000000..2d8e951 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/casual.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/destructive.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/destructive.cpython-310.pyc new file mode 100644 index 0000000..8c6ea61 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/destructive.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/legality_principle.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/legality_principle.cpython-310.pyc new file mode 100644 index 0000000..e38123d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/legality_principle.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/mwe.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/mwe.cpython-310.pyc new file mode 100644 index 0000000..1a28e83 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/mwe.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/nist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/nist.cpython-310.pyc new file mode 100644 index 0000000..e82d80e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/nist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/punkt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/punkt.cpython-310.pyc new file mode 100644 index 0000000..4615513 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/punkt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/regexp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/regexp.cpython-310.pyc new file mode 100644 index 0000000..3aee3d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/regexp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/repp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/repp.cpython-310.pyc new file mode 100644 index 0000000..dddadcb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/repp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sexpr.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sexpr.cpython-310.pyc new file mode 100644 index 0000000..13d2311 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sexpr.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/simple.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/simple.cpython-310.pyc new file mode 100644 index 0000000..308dc9d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/simple.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sonority_sequencing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sonority_sequencing.cpython-310.pyc new file mode 100644 index 0000000..585b430 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/sonority_sequencing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford.cpython-310.pyc new file mode 100644 index 0000000..b3801dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford_segmenter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford_segmenter.cpython-310.pyc new file mode 100644 index 0000000..bede6c1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/stanford_segmenter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/texttiling.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/texttiling.cpython-310.pyc new file mode 100644 index 0000000..34da281 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/texttiling.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/toktok.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/toktok.cpython-310.pyc new file mode 100644 index 0000000..4856e0e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/toktok.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/treebank.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/treebank.cpython-310.pyc new file mode 100644 index 0000000..1f33639 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/treebank.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..53d51ba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/api.py new file mode 100644 index 0000000..881404d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/api.py @@ -0,0 +1,83 @@ +# Natural Language Toolkit: Tokenizer Interface +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +""" +Tokenizer Interface +""" + +from abc import ABC, abstractmethod +from typing import Iterator, List, Tuple + +from nltk.internals import overridden +from nltk.tokenize.util import string_span_tokenize + + +class TokenizerI(ABC): + """ + A processing interface for tokenizing a string. + Subclasses must define ``tokenize()`` or ``tokenize_sents()`` (or both). + """ + + @abstractmethod + def tokenize(self, s: str) -> List[str]: + """ + Return a tokenized copy of *s*. + + :rtype: List[str] + """ + if overridden(self.tokenize_sents): + return self.tokenize_sents([s])[0] + + def span_tokenize(self, s: str) -> Iterator[Tuple[int, int]]: + """ + Identify the tokens using integer offsets ``(start_i, end_i)``, + where ``s[start_i:end_i]`` is the corresponding token. + + :rtype: Iterator[Tuple[int, int]] + """ + raise NotImplementedError() + + def tokenize_sents(self, strings: List[str]) -> List[List[str]]: + """ + Apply ``self.tokenize()`` to each element of ``strings``. I.e.: + + return [self.tokenize(s) for s in strings] + + :rtype: List[List[str]] + """ + return [self.tokenize(s) for s in strings] + + def span_tokenize_sents( + self, strings: List[str] + ) -> Iterator[List[Tuple[int, int]]]: + """ + Apply ``self.span_tokenize()`` to each element of ``strings``. I.e.: + + return [self.span_tokenize(s) for s in strings] + + :yield: List[Tuple[int, int]] + """ + for s in strings: + yield list(self.span_tokenize(s)) + + +class StringTokenizer(TokenizerI): + """A tokenizer that divides a string into substrings by splitting + on the specified string (defined in subclasses). + """ + + @property + @abstractmethod + def _string(self): + raise NotImplementedError + + def tokenize(self, s): + return s.split(self._string) + + def span_tokenize(self, s): + yield from string_span_tokenize(s, self._string) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/casual.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/casual.py new file mode 100644 index 0000000..e0ed06a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/casual.py @@ -0,0 +1,458 @@ +# +# Natural Language Toolkit: Twitter Tokenizer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Christopher Potts +# Ewan Klein (modifications) +# Pierpaolo Pantone <> (modifications) +# Tom Aarsen <> (modifications) +# URL: +# For license information, see LICENSE.TXT +# + + +""" +Twitter-aware tokenizer, designed to be flexible and easy to adapt to new +domains and tasks. The basic logic is this: + +1. The tuple REGEXPS defines a list of regular expression + strings. + +2. The REGEXPS strings are put, in order, into a compiled + regular expression object called WORD_RE, under the TweetTokenizer + class. + +3. The tokenization is done by WORD_RE.findall(s), where s is the + user-supplied string, inside the tokenize() method of the class + TweetTokenizer. + +4. When instantiating Tokenizer objects, there are several options: + * preserve_case. By default, it is set to True. If it is set to + False, then the tokenizer will downcase everything except for + emoticons. + * reduce_len. By default, it is set to False. It specifies whether + to replace repeated character sequences of length 3 or greater + with sequences of length 3. + * strip_handles. By default, it is set to False. It specifies + whether to remove Twitter handles of text used in the + `tokenize` method. + * match_phone_numbers. By default, it is set to True. It indicates + whether the `tokenize` method should look for phone numbers. +""" + + +###################################################################### + +import html +from typing import List + +import regex # https://github.com/nltk/nltk/issues/2409 + +from nltk.tokenize.api import TokenizerI + +###################################################################### +# The following strings are components in the regular expression +# that is used for tokenizing. It's important that phone_number +# appears first in the final regex (since it can contain whitespace). +# It also could matter that tags comes after emoticons, due to the +# possibility of having text like +# +# <:| and some text >:) +# +# Most importantly, the final element should always be last, since it +# does a last ditch whitespace-based tokenization of whatever is left. + +# ToDo: Update with https://en.wikipedia.org/wiki/List_of_emoticons ? + +# This particular element is used in a couple ways, so we define it +# with a name: +EMOTICONS = r""" + (?: + [<>]? + [:;=8] # eyes + [\-o\*\']? # optional nose + [\)\]\(\[dDpP/\:\}\{@\|\\] # mouth + | + [\)\]\(\[dDpP/\:\}\{@\|\\] # mouth + [\-o\*\']? # optional nose + [:;=8] # eyes + [<>]? + | + {}\[\]]+ # Run of non-space, non-()<>{}[] + | # or + \([^\s()]*?\([^\s()]+\)[^\s()]*?\) # balanced parens, one level deep: (...(...)...) + | + \([^\s]+?\) # balanced parens, non-recursive: (...) + )+ + (?: # End with: + \([^\s()]*?\([^\s()]+\)[^\s()]*?\) # balanced parens, one level deep: (...(...)...) + | + \([^\s]+?\) # balanced parens, non-recursive: (...) + | # or + [^\s`!()\[\]{};:'".,<>?«»“”‘’] # not a space or one of these punct chars + ) + | # OR, the following to match naked domains: + (?: + (?\s]+>""", + # ASCII Arrows + r"""[\-]+>|<[\-]+""", + # Twitter username: + r"""(?:@[\w_]+)""", + # Twitter hashtags: + r"""(?:\#+[\w_]+[\w\'_\-]*[\w_]+)""", + # email addresses + r"""[\w.+-]+@[\w-]+\.(?:[\w-]\.?)+[\w-]""", + # Zero-Width-Joiner and Skin tone modifier emojis + """.(?: + [\U0001F3FB-\U0001F3FF]?(?:\u200d.[\U0001F3FB-\U0001F3FF]?)+ + | + [\U0001F3FB-\U0001F3FF] + )""", + # flags + FLAGS, + # Remaining word types: + r""" + (?:[^\W\d_](?:[^\W\d_]|['\-_])+[^\W\d_]) # Words with apostrophes or dashes. + | + (?:[+\-]?\d+[,/.:-]\d+[+\-]?) # Numbers, including fractions, decimals. + | + (?:[\w_]+) # Words without apostrophes or dashes. + | + (?:\.(?:\s*\.){1,}) # Ellipsis dots. + | + (?:\S) # Everything else that isn't whitespace. + """, +) + +# Take the main components and add a phone regex as the second parameter +REGEXPS_PHONE = (REGEXPS[0], PHONE_REGEX, *REGEXPS[1:]) + +###################################################################### +# TweetTokenizer.WORD_RE and TweetTokenizer.PHONE_WORD_RE represent +# the core tokenizing regexes. They are compiled lazily. + +# WORD_RE performs poorly on these patterns: +HANG_RE = regex.compile(r"([^a-zA-Z0-9])\1{3,}") + +# The emoticon string gets its own regex so that we can preserve case for +# them as needed: +EMOTICON_RE = regex.compile(EMOTICONS, regex.VERBOSE | regex.I | regex.UNICODE) + +# These are for regularizing HTML entities to Unicode: +ENT_RE = regex.compile(r"&(#?(x?))([^&;\s]+);") + +# For stripping away handles from a tweet: +HANDLES_RE = regex.compile( + r"(?>> from nltk.tokenize.casual import _replace_html_entities + >>> _replace_html_entities(b'Price: £100') + 'Price: \\xa3100' + >>> print(_replace_html_entities(b'Price: £100')) + Price: £100 + >>> + """ + + def _convert_entity(match): + entity_body = match.group(3) + if match.group(1): + try: + if match.group(2): + number = int(entity_body, 16) + else: + number = int(entity_body, 10) + # Numeric character references in the 80-9F range are typically + # interpreted by browsers as representing the characters mapped + # to bytes 80-9F in the Windows-1252 encoding. For more info + # see: https://en.wikipedia.org/wiki/ISO/IEC_8859-1#Similar_character_sets + if 0x80 <= number <= 0x9F: + return bytes((number,)).decode("cp1252") + except ValueError: + number = None + else: + if entity_body in keep: + return match.group(0) + number = html.entities.name2codepoint.get(entity_body) + if number is not None: + try: + return chr(number) + except (ValueError, OverflowError): + pass + + return "" if remove_illegal else match.group(0) + + return ENT_RE.sub(_convert_entity, _str_to_unicode(text, encoding)) + + +###################################################################### + + +class TweetTokenizer(TokenizerI): + r""" + Tokenizer for tweets. + + >>> from nltk.tokenize import TweetTokenizer + >>> tknzr = TweetTokenizer() + >>> s0 = "This is a cooool #dummysmiley: :-) :-P <3 and some arrows < > -> <--" + >>> tknzr.tokenize(s0) # doctest: +NORMALIZE_WHITESPACE + ['This', 'is', 'a', 'cooool', '#dummysmiley', ':', ':-)', ':-P', '<3', 'and', 'some', 'arrows', '<', '>', '->', + '<--'] + + Examples using `strip_handles` and `reduce_len parameters`: + + >>> tknzr = TweetTokenizer(strip_handles=True, reduce_len=True) + >>> s1 = '@remy: This is waaaaayyyy too much for you!!!!!!' + >>> tknzr.tokenize(s1) + [':', 'This', 'is', 'waaayyy', 'too', 'much', 'for', 'you', '!', '!', '!'] + """ + + # Values used to lazily compile WORD_RE and PHONE_WORD_RE, + # which are the core tokenizing regexes. + _WORD_RE = None + _PHONE_WORD_RE = None + + ###################################################################### + + def __init__( + self, + preserve_case=True, + reduce_len=False, + strip_handles=False, + match_phone_numbers=True, + ): + """ + Create a `TweetTokenizer` instance with settings for use in the `tokenize` method. + + :param preserve_case: Flag indicating whether to preserve the casing (capitalisation) + of text used in the `tokenize` method. Defaults to True. + :type preserve_case: bool + :param reduce_len: Flag indicating whether to replace repeated character sequences + of length 3 or greater with sequences of length 3. Defaults to False. + :type reduce_len: bool + :param strip_handles: Flag indicating whether to remove Twitter handles of text used + in the `tokenize` method. Defaults to False. + :type strip_handles: bool + :param match_phone_numbers: Flag indicating whether the `tokenize` method should look + for phone numbers. Defaults to True. + :type match_phone_numbers: bool + """ + self.preserve_case = preserve_case + self.reduce_len = reduce_len + self.strip_handles = strip_handles + self.match_phone_numbers = match_phone_numbers + + def tokenize(self, text: str) -> List[str]: + """Tokenize the input text. + + :param text: str + :rtype: list(str) + :return: a tokenized list of strings; joining this list returns\ + the original string if `preserve_case=False`. + """ + # Fix HTML character entities: + text = _replace_html_entities(text) + # Remove username handles + if self.strip_handles: + text = remove_handles(text) + # Normalize word lengthening + if self.reduce_len: + text = reduce_lengthening(text) + # Shorten problematic sequences of characters + safe_text = HANG_RE.sub(r"\1\1\1", text) + # Recognise phone numbers during tokenization + if self.match_phone_numbers: + words = self.PHONE_WORD_RE.findall(safe_text) + else: + words = self.WORD_RE.findall(safe_text) + # Possibly alter the case, but avoid changing emoticons like :D into :d: + if not self.preserve_case: + words = list( + map((lambda x: x if EMOTICON_RE.search(x) else x.lower()), words) + ) + return words + + @property + def WORD_RE(self) -> "regex.Pattern": + """Core TweetTokenizer regex""" + # Compiles the regex for this and all future instantiations of TweetTokenizer. + if not type(self)._WORD_RE: + type(self)._WORD_RE = regex.compile( + f"({'|'.join(REGEXPS)})", + regex.VERBOSE | regex.I | regex.UNICODE, + ) + return type(self)._WORD_RE + + @property + def PHONE_WORD_RE(self) -> "regex.Pattern": + """Secondary core TweetTokenizer regex""" + # Compiles the regex for this and all future instantiations of TweetTokenizer. + if not type(self)._PHONE_WORD_RE: + type(self)._PHONE_WORD_RE = regex.compile( + f"({'|'.join(REGEXPS_PHONE)})", + regex.VERBOSE | regex.I | regex.UNICODE, + ) + return type(self)._PHONE_WORD_RE + + +###################################################################### +# Normalization Functions +###################################################################### + + +def reduce_lengthening(text): + """ + Replace repeated character sequences of length 3 or greater with sequences + of length 3. + """ + pattern = regex.compile(r"(.)\1{2,}") + return pattern.sub(r"\1\1\1", text) + + +def remove_handles(text): + """ + Remove Twitter username handles from text. + """ + # Substitute handles with ' ' to ensure that text on either side of removed handles are tokenized correctly + return HANDLES_RE.sub(" ", text) + + +###################################################################### +# Tokenization Function +###################################################################### + + +def casual_tokenize( + text, + preserve_case=True, + reduce_len=False, + strip_handles=False, + match_phone_numbers=True, +): + """ + Convenience function for wrapping the tokenizer. + """ + return TweetTokenizer( + preserve_case=preserve_case, + reduce_len=reduce_len, + strip_handles=strip_handles, + match_phone_numbers=match_phone_numbers, + ).tokenize(text) + + +############################################################################### diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/destructive.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/destructive.py new file mode 100644 index 0000000..d459c75 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/destructive.py @@ -0,0 +1,233 @@ +# Natural Language Toolkit: NLTK's very own tokenizer. +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Liling Tan +# Tom Aarsen <> (modifications) +# URL: +# For license information, see LICENSE.TXT + + +import re +import warnings +from typing import Iterator, List, Tuple + +from nltk.tokenize.api import TokenizerI +from nltk.tokenize.util import align_tokens + + +class MacIntyreContractions: + """ + List of contractions adapted from Robert MacIntyre's tokenizer. + """ + + CONTRACTIONS2 = [ + r"(?i)\b(can)(?#X)(not)\b", + r"(?i)\b(d)(?#X)('ye)\b", + r"(?i)\b(gim)(?#X)(me)\b", + r"(?i)\b(gon)(?#X)(na)\b", + r"(?i)\b(got)(?#X)(ta)\b", + r"(?i)\b(lem)(?#X)(me)\b", + r"(?i)\b(more)(?#X)('n)\b", + r"(?i)\b(wan)(?#X)(na)(?=\s)", + ] + CONTRACTIONS3 = [r"(?i) ('t)(?#X)(is)\b", r"(?i) ('t)(?#X)(was)\b"] + CONTRACTIONS4 = [r"(?i)\b(whad)(dd)(ya)\b", r"(?i)\b(wha)(t)(cha)\b"] + + +class NLTKWordTokenizer(TokenizerI): + """ + The NLTK tokenizer that has improved upon the TreebankWordTokenizer. + + This is the method that is invoked by ``word_tokenize()``. It assumes that the + text has already been segmented into sentences, e.g. using ``sent_tokenize()``. + + The tokenizer is "destructive" such that the regexes applied will munge the + input string to a state beyond re-construction. It is possible to apply + `TreebankWordDetokenizer.detokenize` to the tokenized outputs of + `NLTKDestructiveWordTokenizer.tokenize` but there's no guarantees to + revert to the original string. + """ + + # Starting quotes. + STARTING_QUOTES = [ + (re.compile("([«“‘„]|[`]+)", re.U), r" \1 "), + (re.compile(r"^\""), r"``"), + (re.compile(r"(``)"), r" \1 "), + (re.compile(r"([ \(\[{<])(\"|\'{2})"), r"\1 `` "), + (re.compile(r"(?i)(\')(?!re|ve|ll|m|t|s|d|n)(\w)\b", re.U), r"\1 \2"), + ] + + # Ending quotes. + ENDING_QUOTES = [ + (re.compile("([»”’])", re.U), r" \1 "), + (re.compile(r"''"), " '' "), + (re.compile(r'"'), " '' "), + (re.compile(r"([^' ])('[sS]|'[mM]|'[dD]|') "), r"\1 \2 "), + (re.compile(r"([^' ])('ll|'LL|'re|'RE|'ve|'VE|n't|N'T) "), r"\1 \2 "), + ] + + # For improvements for starting/closing quotes from TreebankWordTokenizer, + # see discussion on https://github.com/nltk/nltk/pull/1437 + # Adding to TreebankWordTokenizer, nltk.word_tokenize now splits on + # - chervon quotes u'\xab' and u'\xbb' . + # - unicode quotes u'\u2018', u'\u2019', u'\u201c' and u'\u201d' + # See https://github.com/nltk/nltk/issues/1995#issuecomment-376741608 + # Also, behavior of splitting on clitics now follows Stanford CoreNLP + # - clitics covered (?!re|ve|ll|m|t|s|d)(\w)\b + + # Punctuation. + PUNCTUATION = [ + (re.compile(r'([^\.])(\.)([\]\)}>"\'' "»”’ " r"]*)\s*$", re.U), r"\1 \2 \3 "), + (re.compile(r"([:,])([^\d])"), r" \1 \2"), + (re.compile(r"([:,])$"), r" \1 "), + ( + re.compile(r"\.{2,}", re.U), + r" \g<0> ", + ), # See https://github.com/nltk/nltk/pull/2322 + (re.compile(r"[;@#$%&]"), r" \g<0> "), + ( + re.compile(r'([^\.])(\.)([\]\)}>"\']*)\s*$'), + r"\1 \2\3 ", + ), # Handles the final period. + (re.compile(r"[?!]"), r" \g<0> "), + (re.compile(r"([^'])' "), r"\1 ' "), + ( + re.compile(r"[*]", re.U), + r" \g<0> ", + ), # See https://github.com/nltk/nltk/pull/2322 + ] + + # Pads parentheses + PARENS_BRACKETS = (re.compile(r"[\]\[\(\)\{\}\<\>]"), r" \g<0> ") + + # Optionally: Convert parentheses, brackets and converts them to PTB symbols. + CONVERT_PARENTHESES = [ + (re.compile(r"\("), "-LRB-"), + (re.compile(r"\)"), "-RRB-"), + (re.compile(r"\["), "-LSB-"), + (re.compile(r"\]"), "-RSB-"), + (re.compile(r"\{"), "-LCB-"), + (re.compile(r"\}"), "-RCB-"), + ] + + DOUBLE_DASHES = (re.compile(r"--"), r" -- ") + + # List of contractions adapted from Robert MacIntyre's tokenizer. + _contractions = MacIntyreContractions() + CONTRACTIONS2 = list(map(re.compile, _contractions.CONTRACTIONS2)) + CONTRACTIONS3 = list(map(re.compile, _contractions.CONTRACTIONS3)) + + def tokenize( + self, text: str, convert_parentheses: bool = False, return_str: bool = False + ) -> List[str]: + r"""Return a tokenized copy of `text`. + + >>> from nltk.tokenize import NLTKWordTokenizer + >>> s = '''Good muffins cost $3.88 (roughly 3,36 euros)\nin New York. Please buy me\ntwo of them.\nThanks.''' + >>> NLTKWordTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3.88', '(', 'roughly', '3,36', + 'euros', ')', 'in', 'New', 'York.', 'Please', 'buy', 'me', 'two', + 'of', 'them.', 'Thanks', '.'] + >>> NLTKWordTokenizer().tokenize(s, convert_parentheses=True) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3.88', '-LRB-', 'roughly', '3,36', + 'euros', '-RRB-', 'in', 'New', 'York.', 'Please', 'buy', 'me', 'two', + 'of', 'them.', 'Thanks', '.'] + + + :param text: A string with a sentence or sentences. + :type text: str + :param convert_parentheses: if True, replace parentheses to PTB symbols, + e.g. `(` to `-LRB-`. Defaults to False. + :type convert_parentheses: bool, optional + :param return_str: If True, return tokens as space-separated string, + defaults to False. + :type return_str: bool, optional + :return: List of tokens from `text`. + :rtype: List[str] + """ + if return_str: + warnings.warn( + "Parameter 'return_str' has been deprecated and should no " + "longer be used.", + category=DeprecationWarning, + stacklevel=2, + ) + + for regexp, substitution in self.STARTING_QUOTES: + text = regexp.sub(substitution, text) + + for regexp, substitution in self.PUNCTUATION: + text = regexp.sub(substitution, text) + + # Handles parentheses. + regexp, substitution = self.PARENS_BRACKETS + text = regexp.sub(substitution, text) + # Optionally convert parentheses + if convert_parentheses: + for regexp, substitution in self.CONVERT_PARENTHESES: + text = regexp.sub(substitution, text) + + # Handles double dash. + regexp, substitution = self.DOUBLE_DASHES + text = regexp.sub(substitution, text) + + # add extra space to make things easier + text = " " + text + " " + + for regexp, substitution in self.ENDING_QUOTES: + text = regexp.sub(substitution, text) + + for regexp in self.CONTRACTIONS2: + text = regexp.sub(r" \1 \2 ", text) + for regexp in self.CONTRACTIONS3: + text = regexp.sub(r" \1 \2 ", text) + + # We are not using CONTRACTIONS4 since + # they are also commented out in the SED scripts + # for regexp in self._contractions.CONTRACTIONS4: + # text = regexp.sub(r' \1 \2 \3 ', text) + + return text.split() + + def span_tokenize(self, text: str) -> Iterator[Tuple[int, int]]: + r""" + Returns the spans of the tokens in ``text``. + Uses the post-hoc nltk.tokens.align_tokens to return the offset spans. + + >>> from nltk.tokenize import NLTKWordTokenizer + >>> s = '''Good muffins cost $3.88\nin New (York). Please (buy) me\ntwo of them.\n(Thanks).''' + >>> expected = [(0, 4), (5, 12), (13, 17), (18, 19), (19, 23), + ... (24, 26), (27, 30), (31, 32), (32, 36), (36, 37), (37, 38), + ... (40, 46), (47, 48), (48, 51), (51, 52), (53, 55), (56, 59), + ... (60, 62), (63, 68), (69, 70), (70, 76), (76, 77), (77, 78)] + >>> list(NLTKWordTokenizer().span_tokenize(s)) == expected + True + >>> expected = ['Good', 'muffins', 'cost', '$', '3.88', 'in', + ... 'New', '(', 'York', ')', '.', 'Please', '(', 'buy', ')', + ... 'me', 'two', 'of', 'them.', '(', 'Thanks', ')', '.'] + >>> [s[start:end] for start, end in NLTKWordTokenizer().span_tokenize(s)] == expected + True + + :param text: A string with a sentence or sentences. + :type text: str + :yield: Tuple[int, int] + """ + raw_tokens = self.tokenize(text) + + # Convert converted quotes back to original double quotes + # Do this only if original text contains double quote(s) or double + # single-quotes (because '' might be transformed to `` if it is + # treated as starting quotes). + if ('"' in text) or ("''" in text): + # Find double quotes and converted quotes + matched = [m.group() for m in re.finditer(r"``|'{2}|\"", text)] + + # Replace converted quotes back to double quotes + tokens = [ + matched.pop(0) if tok in ['"', "``", "''"] else tok + for tok in raw_tokens + ] + else: + tokens = raw_tokens + + yield from align_tokens(tokens, text) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/legality_principle.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/legality_principle.py new file mode 100644 index 0000000..46289e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/legality_principle.py @@ -0,0 +1,147 @@ +# Natural Language Toolkit: Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Christopher Hench +# Alex Estes +# URL: +# For license information, see LICENSE.TXT + +""" +The Legality Principle is a language agnostic principle maintaining that syllable +onsets and codas (the beginning and ends of syllables not including the vowel) +are only legal if they are found as word onsets or codas in the language. The English +word ''admit'' must then be syllabified as ''ad-mit'' since ''dm'' is not found +word-initially in the English language (Bartlett et al.). This principle was first proposed +in Daniel Kahn's 1976 dissertation, ''Syllable-based generalizations in English phonology''. + +Kahn further argues that there is a ''strong tendency to syllabify in such a way that +initial clusters are of maximal length, consistent with the general constraints on +word-initial consonant clusters.'' Consequently, in addition to being legal onsets, +the longest legal onset is preferable---''Onset Maximization''. + +The default implementation assumes an English vowel set, but the `vowels` attribute +can be set to IPA or any other alphabet's vowel set for the use-case. +Both a valid set of vowels as well as a text corpus of words in the language +are necessary to determine legal onsets and subsequently syllabify words. + +The legality principle with onset maximization is a universal syllabification algorithm, +but that does not mean it performs equally across languages. Bartlett et al. (2009) +is a good benchmark for English accuracy if utilizing IPA (pg. 311). + +References: + +- Otto Jespersen. 1904. Lehrbuch der Phonetik. + Leipzig, Teubner. Chapter 13, Silbe, pp. 185-203. +- Theo Vennemann, ''On the Theory of Syllabic Phonology,'' 1972, p. 11. +- Daniel Kahn, ''Syllable-based generalizations in English phonology'', (PhD diss., MIT, 1976). +- Elisabeth Selkirk. 1984. On the major class features and syllable theory. + In Aronoff & Oehrle (eds.) Language Sound Structure: Studies in Phonology. + Cambridge, MIT Press. pp. 107-136. +- Jeremy Goslin and Ulrich Frauenfelder. 2001. A comparison of theoretical and human syllabification. Language and Speech, 44:409–436. +- Susan Bartlett, et al. 2009. On the Syllabification of Phonemes. + In HLT-NAACL. pp. 308-316. +- Christopher Hench. 2017. Resonances in Middle High German: New Methodologies in Prosody. UC Berkeley. +""" + +from collections import Counter + +from nltk.tokenize.api import TokenizerI + + +class LegalitySyllableTokenizer(TokenizerI): + """ + Syllabifies words based on the Legality Principle and Onset Maximization. + + >>> from nltk.tokenize import LegalitySyllableTokenizer + >>> from nltk import word_tokenize + >>> from nltk.corpus import words + >>> text = "This is a wonderful sentence." + >>> text_words = word_tokenize(text) + >>> LP = LegalitySyllableTokenizer(words.words()) + >>> [LP.tokenize(word) for word in text_words] + [['This'], ['is'], ['a'], ['won', 'der', 'ful'], ['sen', 'ten', 'ce'], ['.']] + """ + + def __init__( + self, tokenized_source_text, vowels="aeiouy", legal_frequency_threshold=0.001 + ): + """ + :param tokenized_source_text: List of valid tokens in the language + :type tokenized_source_text: list(str) + :param vowels: Valid vowels in language or IPA representation + :type vowels: str + :param legal_frequency_threshold: Lowest frequency of all onsets to be considered a legal onset + :type legal_frequency_threshold: float + """ + self.legal_frequency_threshold = legal_frequency_threshold + self.vowels = vowels + self.legal_onsets = self.find_legal_onsets(tokenized_source_text) + + def find_legal_onsets(self, words): + """ + Gathers all onsets and then return only those above the frequency threshold + + :param words: List of words in a language + :type words: list(str) + :return: Set of legal onsets + :rtype: set(str) + """ + onsets = [self.onset(word) for word in words] + legal_onsets = [ + k + for k, v in Counter(onsets).items() + if (v / len(onsets)) > self.legal_frequency_threshold + ] + return set(legal_onsets) + + def onset(self, word): + """ + Returns consonant cluster of word, i.e. all characters until the first vowel. + + :param word: Single word or token + :type word: str + :return: String of characters of onset + :rtype: str + """ + onset = "" + for c in word.lower(): + if c in self.vowels: + return onset + else: + onset += c + return onset + + def tokenize(self, token): + """ + Apply the Legality Principle in combination with + Onset Maximization to return a list of syllables. + + :param token: Single word or token + :type token: str + :return syllable_list: Single word or token broken up into syllables. + :rtype: list(str) + """ + syllables = [] + syllable, current_onset = "", "" + vowel, onset = False, False + for char in token[::-1]: + char_lower = char.lower() + if not vowel: + syllable += char + vowel = bool(char_lower in self.vowels) + else: + if char_lower + current_onset[::-1] in self.legal_onsets: + syllable += char + current_onset += char_lower + onset = True + elif char_lower in self.vowels and not onset: + syllable += char + current_onset += char_lower + else: + syllables.append(syllable) + syllable = char + current_onset = "" + vowel = bool(char_lower in self.vowels) + syllables.append(syllable) + syllables_ordered = [syllable[::-1] for syllable in syllables][::-1] + return syllables_ordered diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/mwe.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/mwe.py new file mode 100644 index 0000000..293fff1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/mwe.py @@ -0,0 +1,124 @@ +# Multi-Word Expression tokenizer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Rob Malouf +# URL: +# For license information, see LICENSE.TXT + +""" +Multi-Word Expression Tokenizer + +A ``MWETokenizer`` takes a string which has already been divided into tokens and +retokenizes it, merging multi-word expressions into single tokens, using a lexicon +of MWEs: + + + >>> from nltk.tokenize import MWETokenizer + + >>> tokenizer = MWETokenizer([('a', 'little'), ('a', 'little', 'bit'), ('a', 'lot')]) + >>> tokenizer.add_mwe(('in', 'spite', 'of')) + + >>> tokenizer.tokenize('Testing testing testing one two three'.split()) + ['Testing', 'testing', 'testing', 'one', 'two', 'three'] + + >>> tokenizer.tokenize('This is a test in spite'.split()) + ['This', 'is', 'a', 'test', 'in', 'spite'] + + >>> tokenizer.tokenize('In a little or a little bit or a lot in spite of'.split()) + ['In', 'a_little', 'or', 'a_little_bit', 'or', 'a_lot', 'in_spite_of'] + +""" +from nltk.tokenize.api import TokenizerI +from nltk.util import Trie + + +class MWETokenizer(TokenizerI): + """A tokenizer that processes tokenized text and merges multi-word expressions + into single tokens. + """ + + def __init__(self, mwes=None, separator="_"): + """Initialize the multi-word tokenizer with a list of expressions and a + separator + + :type mwes: list(list(str)) + :param mwes: A sequence of multi-word expressions to be merged, where + each MWE is a sequence of strings. + :type separator: str + :param separator: String that should be inserted between words in a multi-word + expression token. (Default is '_') + + """ + if not mwes: + mwes = [] + self._mwes = Trie(mwes) + self._separator = separator + + def add_mwe(self, mwe): + """Add a multi-word expression to the lexicon (stored as a word trie) + + We use ``util.Trie`` to represent the trie. Its form is a dict of dicts. + The key True marks the end of a valid MWE. + + :param mwe: The multi-word expression we're adding into the word trie + :type mwe: tuple(str) or list(str) + + :Example: + + >>> tokenizer = MWETokenizer() + >>> tokenizer.add_mwe(('a', 'b')) + >>> tokenizer.add_mwe(('a', 'b', 'c')) + >>> tokenizer.add_mwe(('a', 'x')) + >>> expected = {'a': {'x': {True: None}, 'b': {True: None, 'c': {True: None}}}} + >>> tokenizer._mwes == expected + True + + """ + self._mwes.insert(mwe) + + def tokenize(self, text): + """ + + :param text: A list containing tokenized text + :type text: list(str) + :return: A list of the tokenized text with multi-words merged together + :rtype: list(str) + + :Example: + + >>> tokenizer = MWETokenizer([('hors', "d'oeuvre")], separator='+') + >>> tokenizer.tokenize("An hors d'oeuvre tonight, sir?".split()) + ['An', "hors+d'oeuvre", 'tonight,', 'sir?'] + + """ + i = 0 + n = len(text) + result = [] + + while i < n: + if text[i] in self._mwes: + # possible MWE match + j = i + trie = self._mwes + last_match = -1 + while j < n and text[j] in trie: # and len(trie[text[j]]) > 0 : + trie = trie[text[j]] + j = j + 1 + if Trie.LEAF in trie: + last_match = j + else: + if last_match > -1: + j = last_match + + if Trie.LEAF in trie or last_match > -1: + # success! + result.append(self._separator.join(text[i:j])) + i = j + else: + # no match, so backtrack + result.append(text[i]) + i += 1 + else: + result.append(text[i]) + i += 1 + return result diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/nist.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/nist.py new file mode 100644 index 0000000..8f36d41 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/nist.py @@ -0,0 +1,179 @@ +# Natural Language Toolkit: Python port of the mteval-v14.pl tokenizer. +# +# Copyright (C) 2001-2015 NLTK Project +# Author: Liling Tan (ported from ftp://jaguar.ncsl.nist.gov/mt/resources/mteval-v14.pl) +# Contributors: Ozan Caglayan, Wiktor Stribizew +# +# URL: +# For license information, see LICENSE.TXT + +""" +This is a NLTK port of the tokenizer used in the NIST BLEU evaluation script, +https://github.com/moses-smt/mosesdecoder/blob/master/scripts/generic/mteval-v14.pl#L926 +which was also ported into Python in +https://github.com/lium-lst/nmtpy/blob/master/nmtpy/metrics/mtevalbleu.py#L162 +""" + + +import io +import re + +from nltk.corpus import perluniprops +from nltk.tokenize.api import TokenizerI +from nltk.tokenize.util import xml_unescape + + +class NISTTokenizer(TokenizerI): + """ + This NIST tokenizer is sentence-based instead of the original + paragraph-based tokenization from mteval-14.pl; The sentence-based + tokenization is consistent with the other tokenizers available in NLTK. + + >>> from nltk.tokenize.nist import NISTTokenizer + >>> nist = NISTTokenizer() + >>> s = "Good muffins cost $3.88 in New York." + >>> expected_lower = [u'good', u'muffins', u'cost', u'$', u'3.88', u'in', u'new', u'york', u'.'] + >>> expected_cased = [u'Good', u'muffins', u'cost', u'$', u'3.88', u'in', u'New', u'York', u'.'] + >>> nist.tokenize(s, lowercase=False) == expected_cased + True + >>> nist.tokenize(s, lowercase=True) == expected_lower # Lowercased. + True + + The international_tokenize() is the preferred function when tokenizing + non-european text, e.g. + + >>> from nltk.tokenize.nist import NISTTokenizer + >>> nist = NISTTokenizer() + + # Input strings. + >>> albb = u'Alibaba Group Holding Limited (Chinese: 阿里巴巴集团控股 有限公司) us a Chinese e-commerce company...' + >>> amz = u'Amazon.com, Inc. (/ˈæməzɒn/) is an American electronic commerce...' + >>> rkt = u'Rakuten, Inc. (楽天株式会社 Rakuten Kabushiki-gaisha) is a Japanese electronic commerce and Internet company based in Tokyo.' + + # Expected tokens. + >>> expected_albb = [u'Alibaba', u'Group', u'Holding', u'Limited', u'(', u'Chinese', u':', u'\u963f\u91cc\u5df4\u5df4\u96c6\u56e2\u63a7\u80a1', u'\u6709\u9650\u516c\u53f8', u')'] + >>> expected_amz = [u'Amazon', u'.', u'com', u',', u'Inc', u'.', u'(', u'/', u'\u02c8\xe6', u'm'] + >>> expected_rkt = [u'Rakuten', u',', u'Inc', u'.', u'(', u'\u697d\u5929\u682a\u5f0f\u4f1a\u793e', u'Rakuten', u'Kabushiki', u'-', u'gaisha'] + + >>> nist.international_tokenize(albb)[:10] == expected_albb + True + >>> nist.international_tokenize(amz)[:10] == expected_amz + True + >>> nist.international_tokenize(rkt)[:10] == expected_rkt + True + + # Doctest for patching issue #1926 + >>> sent = u'this is a foo\u2604sentence.' + >>> expected_sent = [u'this', u'is', u'a', u'foo', u'\u2604', u'sentence', u'.'] + >>> nist.international_tokenize(sent) == expected_sent + True + """ + + # Strip "skipped" tags + STRIP_SKIP = re.compile(""), "" + # Strip end-of-line hyphenation and join lines + STRIP_EOL_HYPHEN = re.compile("\u2028"), " " + # Tokenize punctuation. + PUNCT = re.compile(r"([\{-\~\[-\` -\&\(-\+\:-\@\/])"), " \\1 " + # Tokenize period and comma unless preceded by a digit. + PERIOD_COMMA_PRECEED = re.compile(r"([^0-9])([\.,])"), "\\1 \\2 " + # Tokenize period and comma unless followed by a digit. + PERIOD_COMMA_FOLLOW = re.compile(r"([\.,])([^0-9])"), " \\1 \\2" + # Tokenize dash when preceded by a digit + DASH_PRECEED_DIGIT = re.compile("([0-9])(-)"), "\\1 \\2 " + + LANG_DEPENDENT_REGEXES = [ + PUNCT, + PERIOD_COMMA_PRECEED, + PERIOD_COMMA_FOLLOW, + DASH_PRECEED_DIGIT, + ] + + # Perluniprops characters used in NIST tokenizer. + pup_number = str("".join(set(perluniprops.chars("Number")))) # i.e. \p{N} + pup_punct = str("".join(set(perluniprops.chars("Punctuation")))) # i.e. \p{P} + pup_symbol = str("".join(set(perluniprops.chars("Symbol")))) # i.e. \p{S} + + # Python regexes needs to escape some special symbols, see + # see https://stackoverflow.com/q/45670950/610569 + number_regex = re.sub(r"[]^\\-]", r"\\\g<0>", pup_number) + punct_regex = re.sub(r"[]^\\-]", r"\\\g<0>", pup_punct) + symbol_regex = re.sub(r"[]^\\-]", r"\\\g<0>", pup_symbol) + + # Note: In the original perl implementation, \p{Z} and \p{Zl} were used to + # (i) strip trailing and heading spaces and + # (ii) de-deuplicate spaces. + # In Python, this would do: ' '.join(str.strip().split()) + # Thus, the next two lines were commented out. + # Line_Separator = str(''.join(perluniprops.chars('Line_Separator'))) # i.e. \p{Zl} + # Separator = str(''.join(perluniprops.chars('Separator'))) # i.e. \p{Z} + + # Pads non-ascii strings with space. + NONASCII = re.compile("([\x00-\x7f]+)"), r" \1 " + # Tokenize any punctuation unless followed AND preceded by a digit. + PUNCT_1 = ( + re.compile(f"([{number_regex}])([{punct_regex}])"), + "\\1 \\2 ", + ) + PUNCT_2 = ( + re.compile(f"([{punct_regex}])([{number_regex}])"), + " \\1 \\2", + ) + # Tokenize symbols + SYMBOLS = re.compile(f"([{symbol_regex}])"), " \\1 " + + INTERNATIONAL_REGEXES = [NONASCII, PUNCT_1, PUNCT_2, SYMBOLS] + + def lang_independent_sub(self, text): + """Performs the language independent string substituitions.""" + # It's a strange order of regexes. + # It'll be better to unescape after STRIP_EOL_HYPHEN + # but let's keep it close to the original NIST implementation. + regexp, substitution = self.STRIP_SKIP + text = regexp.sub(substitution, text) + text = xml_unescape(text) + regexp, substitution = self.STRIP_EOL_HYPHEN + text = regexp.sub(substitution, text) + return text + + def tokenize(self, text, lowercase=False, western_lang=True, return_str=False): + text = str(text) + # Language independent regex. + text = self.lang_independent_sub(text) + # Language dependent regex. + if western_lang: + # Pad string with whitespace. + text = " " + text + " " + if lowercase: + text = text.lower() + for regexp, substitution in self.LANG_DEPENDENT_REGEXES: + text = regexp.sub(substitution, text) + # Remove contiguous whitespaces. + text = " ".join(text.split()) + # Finally, strips heading and trailing spaces + # and converts output string into unicode. + text = str(text.strip()) + return text if return_str else text.split() + + def international_tokenize( + self, text, lowercase=False, split_non_ascii=True, return_str=False + ): + text = str(text) + # Different from the 'normal' tokenize(), STRIP_EOL_HYPHEN is applied + # first before unescaping. + regexp, substitution = self.STRIP_SKIP + text = regexp.sub(substitution, text) + regexp, substitution = self.STRIP_EOL_HYPHEN + text = regexp.sub(substitution, text) + text = xml_unescape(text) + + if lowercase: + text = text.lower() + + for regexp, substitution in self.INTERNATIONAL_REGEXES: + text = regexp.sub(substitution, text) + + # Make sure that there's only one space only between words. + # Strip leading and trailing spaces. + text = " ".join(text.strip().split()) + return text if return_str else text.split() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/punkt.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/punkt.py new file mode 100644 index 0000000..49d9d04 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/punkt.py @@ -0,0 +1,1767 @@ +# Natural Language Toolkit: Punkt sentence tokenizer +# +# Copyright (C) 2001-2023 NLTK Project +# Algorithm: Kiss & Strunk (2006) +# Author: Willy (original Python port) +# Steven Bird (additions) +# Edward Loper (rewrite) +# Joel Nothman (almost rewrite) +# Arthur Darcet (fixes) +# Tom Aarsen <> (tackle ReDoS & performance issues) +# URL: +# For license information, see LICENSE.TXT + +r""" +Punkt Sentence Tokenizer + +This tokenizer divides a text into a list of sentences +by using an unsupervised algorithm to build a model for abbreviation +words, collocations, and words that start sentences. It must be +trained on a large collection of plaintext in the target language +before it can be used. + +The NLTK data package includes a pre-trained Punkt tokenizer for +English. + + >>> import nltk.data + >>> text = ''' + ... Punkt knows that the periods in Mr. Smith and Johann S. Bach + ... do not mark sentence boundaries. And sometimes sentences + ... can start with non-capitalized words. i is a good variable + ... name. + ... ''' + >>> sent_detector = nltk.data.load('tokenizers/punkt/english.pickle') + >>> print('\n-----\n'.join(sent_detector.tokenize(text.strip()))) + Punkt knows that the periods in Mr. Smith and Johann S. Bach + do not mark sentence boundaries. + ----- + And sometimes sentences + can start with non-capitalized words. + ----- + i is a good variable + name. + +(Note that whitespace from the original text, including newlines, is +retained in the output.) + +Punctuation following sentences is also included by default +(from NLTK 3.0 onwards). It can be excluded with the realign_boundaries +flag. + + >>> text = ''' + ... (How does it deal with this parenthesis?) "It should be part of the + ... previous sentence." "(And the same with this one.)" ('And this one!') + ... "('(And (this)) '?)" [(and this. )] + ... ''' + >>> print('\n-----\n'.join( + ... sent_detector.tokenize(text.strip()))) + (How does it deal with this parenthesis?) + ----- + "It should be part of the + previous sentence." + ----- + "(And the same with this one.)" + ----- + ('And this one!') + ----- + "('(And (this)) '?)" + ----- + [(and this. )] + >>> print('\n-----\n'.join( + ... sent_detector.tokenize(text.strip(), realign_boundaries=False))) + (How does it deal with this parenthesis? + ----- + ) "It should be part of the + previous sentence. + ----- + " "(And the same with this one. + ----- + )" ('And this one! + ----- + ') + "('(And (this)) '? + ----- + )" [(and this. + ----- + )] + +However, Punkt is designed to learn parameters (a list of abbreviations, etc.) +unsupervised from a corpus similar to the target domain. The pre-packaged models +may therefore be unsuitable: use ``PunktSentenceTokenizer(text)`` to learn +parameters from the given text. + +:class:`.PunktTrainer` learns parameters such as a list of abbreviations +(without supervision) from portions of text. Using a ``PunktTrainer`` directly +allows for incremental training and modification of the hyper-parameters used +to decide what is considered an abbreviation, etc. + +The algorithm for this tokenizer is described in:: + + Kiss, Tibor and Strunk, Jan (2006): Unsupervised Multilingual Sentence + Boundary Detection. Computational Linguistics 32: 485-525. +""" + +# TODO: Make orthographic heuristic less susceptible to overtraining +# TODO: Frequent sentence starters optionally exclude always-capitalised words +# FIXME: Problem with ending string with e.g. '!!!' -> '!! !' + +import math +import re +import string +from collections import defaultdict +from typing import Any, Dict, Iterator, List, Match, Optional, Tuple, Union + +from nltk.probability import FreqDist +from nltk.tokenize.api import TokenizerI + +###################################################################### +# { Orthographic Context Constants +###################################################################### +# The following constants are used to describe the orthographic +# contexts in which a word can occur. BEG=beginning, MID=middle, +# UNK=unknown, UC=uppercase, LC=lowercase, NC=no case. + +_ORTHO_BEG_UC = 1 << 1 +"""Orthographic context: beginning of a sentence with upper case.""" + +_ORTHO_MID_UC = 1 << 2 +"""Orthographic context: middle of a sentence with upper case.""" + +_ORTHO_UNK_UC = 1 << 3 +"""Orthographic context: unknown position in a sentence with upper case.""" + +_ORTHO_BEG_LC = 1 << 4 +"""Orthographic context: beginning of a sentence with lower case.""" + +_ORTHO_MID_LC = 1 << 5 +"""Orthographic context: middle of a sentence with lower case.""" + +_ORTHO_UNK_LC = 1 << 6 +"""Orthographic context: unknown position in a sentence with lower case.""" + +_ORTHO_UC = _ORTHO_BEG_UC + _ORTHO_MID_UC + _ORTHO_UNK_UC +"""Orthographic context: occurs with upper case.""" + +_ORTHO_LC = _ORTHO_BEG_LC + _ORTHO_MID_LC + _ORTHO_UNK_LC +"""Orthographic context: occurs with lower case.""" + +_ORTHO_MAP = { + ("initial", "upper"): _ORTHO_BEG_UC, + ("internal", "upper"): _ORTHO_MID_UC, + ("unknown", "upper"): _ORTHO_UNK_UC, + ("initial", "lower"): _ORTHO_BEG_LC, + ("internal", "lower"): _ORTHO_MID_LC, + ("unknown", "lower"): _ORTHO_UNK_LC, +} +"""A map from context position and first-letter case to the +appropriate orthographic context flag.""" + +# } (end orthographic context constants) +###################################################################### + +###################################################################### +# { Decision reasons for debugging +###################################################################### + +REASON_DEFAULT_DECISION = "default decision" +REASON_KNOWN_COLLOCATION = "known collocation (both words)" +REASON_ABBR_WITH_ORTHOGRAPHIC_HEURISTIC = "abbreviation + orthographic heuristic" +REASON_ABBR_WITH_SENTENCE_STARTER = "abbreviation + frequent sentence starter" +REASON_INITIAL_WITH_ORTHOGRAPHIC_HEURISTIC = "initial + orthographic heuristic" +REASON_NUMBER_WITH_ORTHOGRAPHIC_HEURISTIC = "initial + orthographic heuristic" +REASON_INITIAL_WITH_SPECIAL_ORTHOGRAPHIC_HEURISTIC = ( + "initial + special orthographic heuristic" +) + + +# } (end decision reasons for debugging) +###################################################################### + +###################################################################### +# { Language-dependent variables +###################################################################### + + +class PunktLanguageVars: + """ + Stores variables, mostly regular expressions, which may be + language-dependent for correct application of the algorithm. + An extension of this class may modify its properties to suit + a language other than English; an instance can then be passed + as an argument to PunktSentenceTokenizer and PunktTrainer + constructors. + """ + + __slots__ = ("_re_period_context", "_re_word_tokenizer") + + def __getstate__(self): + # All modifications to the class are performed by inheritance. + # Non-default parameters to be pickled must be defined in the inherited + # class. + return 1 + + def __setstate__(self, state): + return 1 + + sent_end_chars = (".", "?", "!") + """Characters which are candidates for sentence boundaries""" + + @property + def _re_sent_end_chars(self): + return "[%s]" % re.escape("".join(self.sent_end_chars)) + + internal_punctuation = ",:;" # might want to extend this.. + """sentence internal punctuation, which indicates an abbreviation if + preceded by a period-final token.""" + + re_boundary_realignment = re.compile(r'["\')\]}]+?(?:\s+|(?=--)|$)', re.MULTILINE) + """Used to realign punctuation that should be included in a sentence + although it follows the period (or ?, !).""" + + _re_word_start = r"[^\(\"\`{\[:;&\#\*@\)}\]\-,]" + """Excludes some characters from starting word tokens""" + + @property + def _re_non_word_chars(self): + return r"(?:[)\";}\]\*:@\'\({\[%s])" % re.escape( + "".join(set(self.sent_end_chars) - {"."}) + ) + + """Characters that cannot appear within words""" + + _re_multi_char_punct = r"(?:\-{2,}|\.{2,}|(?:\.\s){2,}\.)" + """Hyphen and ellipsis are multi-character punctuation""" + + _word_tokenize_fmt = r"""( + %(MultiChar)s + | + (?=%(WordStart)s)\S+? # Accept word characters until end is found + (?= # Sequences marking a word's end + \s| # White-space + $| # End-of-string + %(NonWord)s|%(MultiChar)s| # Punctuation + ,(?=$|\s|%(NonWord)s|%(MultiChar)s) # Comma if at end of word + ) + | + \S + )""" + """Format of a regular expression to split punctuation from words, + excluding period.""" + + def _word_tokenizer_re(self): + """Compiles and returns a regular expression for word tokenization""" + try: + return self._re_word_tokenizer + except AttributeError: + self._re_word_tokenizer = re.compile( + self._word_tokenize_fmt + % { + "NonWord": self._re_non_word_chars, + "MultiChar": self._re_multi_char_punct, + "WordStart": self._re_word_start, + }, + re.UNICODE | re.VERBOSE, + ) + return self._re_word_tokenizer + + def word_tokenize(self, s): + """Tokenize a string to split off punctuation other than periods""" + return self._word_tokenizer_re().findall(s) + + _period_context_fmt = r""" + %(SentEndChars)s # a potential sentence ending + (?=(?P + %(NonWord)s # either other punctuation + | + \s+(?P\S+) # or whitespace and some other token + ))""" + """Format of a regular expression to find contexts including possible + sentence boundaries. Matches token which the possible sentence boundary + ends, and matches the following token within a lookahead expression.""" + + def period_context_re(self): + """Compiles and returns a regular expression to find contexts + including possible sentence boundaries.""" + try: + return self._re_period_context + except: + self._re_period_context = re.compile( + self._period_context_fmt + % { + "NonWord": self._re_non_word_chars, + "SentEndChars": self._re_sent_end_chars, + }, + re.UNICODE | re.VERBOSE, + ) + return self._re_period_context + + +_re_non_punct = re.compile(r"[^\W\d]", re.UNICODE) +"""Matches token types that are not merely punctuation. (Types for +numeric tokens are changed to ##number## and hence contain alpha.)""" + + +# } +###################################################################### + + +# //////////////////////////////////////////////////////////// +# { Helper Functions +# //////////////////////////////////////////////////////////// + + +def _pair_iter(iterator): + """ + Yields pairs of tokens from the given iterator such that each input + token will appear as the first element in a yielded tuple. The last + pair will have None as its second element. + """ + iterator = iter(iterator) + try: + prev = next(iterator) + except StopIteration: + return + for el in iterator: + yield (prev, el) + prev = el + yield (prev, None) + + +###################################################################### +# { Punkt Parameters +###################################################################### + + +class PunktParameters: + """Stores data used to perform sentence boundary detection with Punkt.""" + + def __init__(self): + self.abbrev_types = set() + """A set of word types for known abbreviations.""" + + self.collocations = set() + """A set of word type tuples for known common collocations + where the first word ends in a period. E.g., ('S.', 'Bach') + is a common collocation in a text that discusses 'Johann + S. Bach'. These count as negative evidence for sentence + boundaries.""" + + self.sent_starters = set() + """A set of word types for words that often appear at the + beginning of sentences.""" + + self.ortho_context = defaultdict(int) + """A dictionary mapping word types to the set of orthographic + contexts that word type appears in. Contexts are represented + by adding orthographic context flags: ...""" + + def clear_abbrevs(self): + self.abbrev_types = set() + + def clear_collocations(self): + self.collocations = set() + + def clear_sent_starters(self): + self.sent_starters = set() + + def clear_ortho_context(self): + self.ortho_context = defaultdict(int) + + def add_ortho_context(self, typ, flag): + self.ortho_context[typ] |= flag + + def _debug_ortho_context(self, typ): + context = self.ortho_context[typ] + if context & _ORTHO_BEG_UC: + yield "BEG-UC" + if context & _ORTHO_MID_UC: + yield "MID-UC" + if context & _ORTHO_UNK_UC: + yield "UNK-UC" + if context & _ORTHO_BEG_LC: + yield "BEG-LC" + if context & _ORTHO_MID_LC: + yield "MID-LC" + if context & _ORTHO_UNK_LC: + yield "UNK-LC" + + +###################################################################### +# { PunktToken +###################################################################### + + +class PunktToken: + """Stores a token of text with annotations produced during + sentence boundary detection.""" + + _properties = ["parastart", "linestart", "sentbreak", "abbr", "ellipsis"] + __slots__ = ["tok", "type", "period_final"] + _properties + + def __init__(self, tok, **params): + self.tok = tok + self.type = self._get_type(tok) + self.period_final = tok.endswith(".") + + for prop in self._properties: + setattr(self, prop, None) + for k in params: + setattr(self, k, params[k]) + + # //////////////////////////////////////////////////////////// + # { Regular expressions for properties + # //////////////////////////////////////////////////////////// + # Note: [A-Za-z] is approximated by [^\W\d] in the general case. + _RE_ELLIPSIS = re.compile(r"\.\.+$") + _RE_NUMERIC = re.compile(r"^-?[\.,]?\d[\d,\.-]*\.?$") + _RE_INITIAL = re.compile(r"[^\W\d]\.$", re.UNICODE) + _RE_ALPHA = re.compile(r"[^\W\d]+$", re.UNICODE) + + # //////////////////////////////////////////////////////////// + # { Derived properties + # //////////////////////////////////////////////////////////// + + def _get_type(self, tok): + """Returns a case-normalized representation of the token.""" + return self._RE_NUMERIC.sub("##number##", tok.lower()) + + @property + def type_no_period(self): + """ + The type with its final period removed if it has one. + """ + if len(self.type) > 1 and self.type[-1] == ".": + return self.type[:-1] + return self.type + + @property + def type_no_sentperiod(self): + """ + The type with its final period removed if it is marked as a + sentence break. + """ + if self.sentbreak: + return self.type_no_period + return self.type + + @property + def first_upper(self): + """True if the token's first character is uppercase.""" + return self.tok[0].isupper() + + @property + def first_lower(self): + """True if the token's first character is lowercase.""" + return self.tok[0].islower() + + @property + def first_case(self): + if self.first_lower: + return "lower" + if self.first_upper: + return "upper" + return "none" + + @property + def is_ellipsis(self): + """True if the token text is that of an ellipsis.""" + return self._RE_ELLIPSIS.match(self.tok) + + @property + def is_number(self): + """True if the token text is that of a number.""" + return self.type.startswith("##number##") + + @property + def is_initial(self): + """True if the token text is that of an initial.""" + return self._RE_INITIAL.match(self.tok) + + @property + def is_alpha(self): + """True if the token text is all alphabetic.""" + return self._RE_ALPHA.match(self.tok) + + @property + def is_non_punct(self): + """True if the token is either a number or is alphabetic.""" + return _re_non_punct.search(self.type) + + # //////////////////////////////////////////////////////////// + # { String representation + # //////////////////////////////////////////////////////////// + + def __repr__(self): + """ + A string representation of the token that can reproduce it + with eval(), which lists all the token's non-default + annotations. + """ + typestr = " type=%s," % repr(self.type) if self.type != self.tok else "" + + propvals = ", ".join( + f"{p}={repr(getattr(self, p))}" + for p in self._properties + if getattr(self, p) + ) + + return "{}({},{} {})".format( + self.__class__.__name__, + repr(self.tok), + typestr, + propvals, + ) + + def __str__(self): + """ + A string representation akin to that used by Kiss and Strunk. + """ + res = self.tok + if self.abbr: + res += "" + if self.ellipsis: + res += "" + if self.sentbreak: + res += "" + return res + + +###################################################################### +# { Punkt base class +###################################################################### + + +class PunktBaseClass: + """ + Includes common components of PunktTrainer and PunktSentenceTokenizer. + """ + + def __init__(self, lang_vars=None, token_cls=PunktToken, params=None): + if lang_vars is None: + lang_vars = PunktLanguageVars() + if params is None: + params = PunktParameters() + self._params = params + self._lang_vars = lang_vars + self._Token = token_cls + """The collection of parameters that determines the behavior + of the punkt tokenizer.""" + + # //////////////////////////////////////////////////////////// + # { Word tokenization + # //////////////////////////////////////////////////////////// + + def _tokenize_words(self, plaintext): + """ + Divide the given text into tokens, using the punkt word + segmentation regular expression, and generate the resulting list + of tokens augmented as three-tuples with two boolean values for whether + the given token occurs at the start of a paragraph or a new line, + respectively. + """ + parastart = False + for line in plaintext.split("\n"): + if line.strip(): + line_toks = iter(self._lang_vars.word_tokenize(line)) + + try: + tok = next(line_toks) + except StopIteration: + continue + + yield self._Token(tok, parastart=parastart, linestart=True) + parastart = False + + for tok in line_toks: + yield self._Token(tok) + else: + parastart = True + + # //////////////////////////////////////////////////////////// + # { Annotation Procedures + # //////////////////////////////////////////////////////////// + + def _annotate_first_pass( + self, tokens: Iterator[PunktToken] + ) -> Iterator[PunktToken]: + """ + Perform the first pass of annotation, which makes decisions + based purely based on the word type of each word: + + - '?', '!', and '.' are marked as sentence breaks. + - sequences of two or more periods are marked as ellipsis. + - any word ending in '.' that's a known abbreviation is + marked as an abbreviation. + - any other word ending in '.' is marked as a sentence break. + + Return these annotations as a tuple of three sets: + + - sentbreak_toks: The indices of all sentence breaks. + - abbrev_toks: The indices of all abbreviations. + - ellipsis_toks: The indices of all ellipsis marks. + """ + for aug_tok in tokens: + self._first_pass_annotation(aug_tok) + yield aug_tok + + def _first_pass_annotation(self, aug_tok: PunktToken) -> None: + """ + Performs type-based annotation on a single token. + """ + + tok = aug_tok.tok + + if tok in self._lang_vars.sent_end_chars: + aug_tok.sentbreak = True + elif aug_tok.is_ellipsis: + aug_tok.ellipsis = True + elif aug_tok.period_final and not tok.endswith(".."): + if ( + tok[:-1].lower() in self._params.abbrev_types + or tok[:-1].lower().split("-")[-1] in self._params.abbrev_types + ): + + aug_tok.abbr = True + else: + aug_tok.sentbreak = True + + return + + +###################################################################### +# { Punkt Trainer +###################################################################### + + +class PunktTrainer(PunktBaseClass): + """Learns parameters used in Punkt sentence boundary detection.""" + + def __init__( + self, train_text=None, verbose=False, lang_vars=None, token_cls=PunktToken + ): + + PunktBaseClass.__init__(self, lang_vars=lang_vars, token_cls=token_cls) + + self._type_fdist = FreqDist() + """A frequency distribution giving the frequency of each + case-normalized token type in the training data.""" + + self._num_period_toks = 0 + """The number of words ending in period in the training data.""" + + self._collocation_fdist = FreqDist() + """A frequency distribution giving the frequency of all + bigrams in the training data where the first word ends in a + period. Bigrams are encoded as tuples of word types. + Especially common collocations are extracted from this + frequency distribution, and stored in + ``_params``.``collocations ``.""" + + self._sent_starter_fdist = FreqDist() + """A frequency distribution giving the frequency of all words + that occur at the training data at the beginning of a sentence + (after the first pass of annotation). Especially common + sentence starters are extracted from this frequency + distribution, and stored in ``_params.sent_starters``. + """ + + self._sentbreak_count = 0 + """The total number of sentence breaks identified in training, used for + calculating the frequent sentence starter heuristic.""" + + self._finalized = True + """A flag as to whether the training has been finalized by finding + collocations and sentence starters, or whether finalize_training() + still needs to be called.""" + + if train_text: + self.train(train_text, verbose, finalize=True) + + def get_params(self): + """ + Calculates and returns parameters for sentence boundary detection as + derived from training.""" + if not self._finalized: + self.finalize_training() + return self._params + + # //////////////////////////////////////////////////////////// + # { Customization Variables + # //////////////////////////////////////////////////////////// + + ABBREV = 0.3 + """cut-off value whether a 'token' is an abbreviation""" + + IGNORE_ABBREV_PENALTY = False + """allows the disabling of the abbreviation penalty heuristic, which + exponentially disadvantages words that are found at times without a + final period.""" + + ABBREV_BACKOFF = 5 + """upper cut-off for Mikheev's(2002) abbreviation detection algorithm""" + + COLLOCATION = 7.88 + """minimal log-likelihood value that two tokens need to be considered + as a collocation""" + + SENT_STARTER = 30 + """minimal log-likelihood value that a token requires to be considered + as a frequent sentence starter""" + + INCLUDE_ALL_COLLOCS = False + """this includes as potential collocations all word pairs where the first + word ends in a period. It may be useful in corpora where there is a lot + of variation that makes abbreviations like Mr difficult to identify.""" + + INCLUDE_ABBREV_COLLOCS = False + """this includes as potential collocations all word pairs where the first + word is an abbreviation. Such collocations override the orthographic + heuristic, but not the sentence starter heuristic. This is overridden by + INCLUDE_ALL_COLLOCS, and if both are false, only collocations with initials + and ordinals are considered.""" + """""" + + MIN_COLLOC_FREQ = 1 + """this sets a minimum bound on the number of times a bigram needs to + appear before it can be considered a collocation, in addition to log + likelihood statistics. This is useful when INCLUDE_ALL_COLLOCS is True.""" + + # //////////////////////////////////////////////////////////// + # { Training.. + # //////////////////////////////////////////////////////////// + + def train(self, text, verbose=False, finalize=True): + """ + Collects training data from a given text. If finalize is True, it + will determine all the parameters for sentence boundary detection. If + not, this will be delayed until get_params() or finalize_training() is + called. If verbose is True, abbreviations found will be listed. + """ + # Break the text into tokens; record which token indices correspond to + # line starts and paragraph starts; and determine their types. + self._train_tokens(self._tokenize_words(text), verbose) + if finalize: + self.finalize_training(verbose) + + def train_tokens(self, tokens, verbose=False, finalize=True): + """ + Collects training data from a given list of tokens. + """ + self._train_tokens((self._Token(t) for t in tokens), verbose) + if finalize: + self.finalize_training(verbose) + + def _train_tokens(self, tokens, verbose): + self._finalized = False + + # Ensure tokens are a list + tokens = list(tokens) + + # Find the frequency of each case-normalized type. (Don't + # strip off final periods.) Also keep track of the number of + # tokens that end in periods. + for aug_tok in tokens: + self._type_fdist[aug_tok.type] += 1 + if aug_tok.period_final: + self._num_period_toks += 1 + + # Look for new abbreviations, and for types that no longer are + unique_types = self._unique_types(tokens) + for abbr, score, is_add in self._reclassify_abbrev_types(unique_types): + if score >= self.ABBREV: + if is_add: + self._params.abbrev_types.add(abbr) + if verbose: + print(f" Abbreviation: [{score:6.4f}] {abbr}") + else: + if not is_add: + self._params.abbrev_types.remove(abbr) + if verbose: + print(f" Removed abbreviation: [{score:6.4f}] {abbr}") + + # Make a preliminary pass through the document, marking likely + # sentence breaks, abbreviations, and ellipsis tokens. + tokens = list(self._annotate_first_pass(tokens)) + + # Check what contexts each word type can appear in, given the + # case of its first letter. + self._get_orthography_data(tokens) + + # We need total number of sentence breaks to find sentence starters + self._sentbreak_count += self._get_sentbreak_count(tokens) + + # The remaining heuristics relate to pairs of tokens where the first + # ends in a period. + for aug_tok1, aug_tok2 in _pair_iter(tokens): + if not aug_tok1.period_final or not aug_tok2: + continue + + # Is the first token a rare abbreviation? + if self._is_rare_abbrev_type(aug_tok1, aug_tok2): + self._params.abbrev_types.add(aug_tok1.type_no_period) + if verbose: + print(" Rare Abbrev: %s" % aug_tok1.type) + + # Does second token have a high likelihood of starting a sentence? + if self._is_potential_sent_starter(aug_tok2, aug_tok1): + self._sent_starter_fdist[aug_tok2.type] += 1 + + # Is this bigram a potential collocation? + if self._is_potential_collocation(aug_tok1, aug_tok2): + self._collocation_fdist[ + (aug_tok1.type_no_period, aug_tok2.type_no_sentperiod) + ] += 1 + + def _unique_types(self, tokens): + return {aug_tok.type for aug_tok in tokens} + + def finalize_training(self, verbose=False): + """ + Uses data that has been gathered in training to determine likely + collocations and sentence starters. + """ + self._params.clear_sent_starters() + for typ, log_likelihood in self._find_sent_starters(): + self._params.sent_starters.add(typ) + if verbose: + print(f" Sent Starter: [{log_likelihood:6.4f}] {typ!r}") + + self._params.clear_collocations() + for (typ1, typ2), log_likelihood in self._find_collocations(): + self._params.collocations.add((typ1, typ2)) + if verbose: + print(f" Collocation: [{log_likelihood:6.4f}] {typ1!r}+{typ2!r}") + + self._finalized = True + + # //////////////////////////////////////////////////////////// + # { Overhead reduction + # //////////////////////////////////////////////////////////// + + def freq_threshold( + self, ortho_thresh=2, type_thresh=2, colloc_thres=2, sentstart_thresh=2 + ): + """ + Allows memory use to be reduced after much training by removing data + about rare tokens that are unlikely to have a statistical effect with + further training. Entries occurring above the given thresholds will be + retained. + """ + if ortho_thresh > 1: + old_oc = self._params.ortho_context + self._params.clear_ortho_context() + for tok in self._type_fdist: + count = self._type_fdist[tok] + if count >= ortho_thresh: + self._params.ortho_context[tok] = old_oc[tok] + + self._type_fdist = self._freq_threshold(self._type_fdist, type_thresh) + self._collocation_fdist = self._freq_threshold( + self._collocation_fdist, colloc_thres + ) + self._sent_starter_fdist = self._freq_threshold( + self._sent_starter_fdist, sentstart_thresh + ) + + def _freq_threshold(self, fdist, threshold): + """ + Returns a FreqDist containing only data with counts below a given + threshold, as well as a mapping (None -> count_removed). + """ + # We assume that there is more data below the threshold than above it + # and so create a new FreqDist rather than working in place. + res = FreqDist() + num_removed = 0 + for tok in fdist: + count = fdist[tok] + if count < threshold: + num_removed += 1 + else: + res[tok] += count + res[None] += num_removed + return res + + # //////////////////////////////////////////////////////////// + # { Orthographic data + # //////////////////////////////////////////////////////////// + + def _get_orthography_data(self, tokens): + """ + Collect information about whether each token type occurs + with different case patterns (i) overall, (ii) at + sentence-initial positions, and (iii) at sentence-internal + positions. + """ + # 'initial' or 'internal' or 'unknown' + context = "internal" + tokens = list(tokens) + + for aug_tok in tokens: + # If we encounter a paragraph break, then it's a good sign + # that it's a sentence break. But err on the side of + # caution (by not positing a sentence break) if we just + # saw an abbreviation. + if aug_tok.parastart and context != "unknown": + context = "initial" + + # If we're at the beginning of a line, then we can't decide + # between 'internal' and 'initial'. + if aug_tok.linestart and context == "internal": + context = "unknown" + + # Find the case-normalized type of the token. If it's a + # sentence-final token, strip off the period. + typ = aug_tok.type_no_sentperiod + + # Update the orthographic context table. + flag = _ORTHO_MAP.get((context, aug_tok.first_case), 0) + if flag: + self._params.add_ortho_context(typ, flag) + + # Decide whether the next word is at a sentence boundary. + if aug_tok.sentbreak: + if not (aug_tok.is_number or aug_tok.is_initial): + context = "initial" + else: + context = "unknown" + elif aug_tok.ellipsis or aug_tok.abbr: + context = "unknown" + else: + context = "internal" + + # //////////////////////////////////////////////////////////// + # { Abbreviations + # //////////////////////////////////////////////////////////// + + def _reclassify_abbrev_types(self, types): + """ + (Re)classifies each given token if + - it is period-final and not a known abbreviation; or + - it is not period-final and is otherwise a known abbreviation + by checking whether its previous classification still holds according + to the heuristics of section 3. + Yields triples (abbr, score, is_add) where abbr is the type in question, + score is its log-likelihood with penalties applied, and is_add specifies + whether the present type is a candidate for inclusion or exclusion as an + abbreviation, such that: + - (is_add and score >= 0.3) suggests a new abbreviation; and + - (not is_add and score < 0.3) suggests excluding an abbreviation. + """ + # (While one could recalculate abbreviations from all .-final tokens at + # every iteration, in cases requiring efficiency, the number of tokens + # in the present training document will be much less.) + + for typ in types: + # Check some basic conditions, to rule out words that are + # clearly not abbrev_types. + if not _re_non_punct.search(typ) or typ == "##number##": + continue + + if typ.endswith("."): + if typ in self._params.abbrev_types: + continue + typ = typ[:-1] + is_add = True + else: + if typ not in self._params.abbrev_types: + continue + is_add = False + + # Count how many periods & nonperiods are in the + # candidate. + num_periods = typ.count(".") + 1 + num_nonperiods = len(typ) - num_periods + 1 + + # Let be the candidate without the period, and + # be the period. Find a log likelihood ratio that + # indicates whether occurs as a single unit (high + # value of log_likelihood), or as two independent units and + # (low value of log_likelihood). + count_with_period = self._type_fdist[typ + "."] + count_without_period = self._type_fdist[typ] + log_likelihood = self._dunning_log_likelihood( + count_with_period + count_without_period, + self._num_period_toks, + count_with_period, + self._type_fdist.N(), + ) + + # Apply three scaling factors to 'tweak' the basic log + # likelihood ratio: + # F_length: long word -> less likely to be an abbrev + # F_periods: more periods -> more likely to be an abbrev + # F_penalty: penalize occurrences w/o a period + f_length = math.exp(-num_nonperiods) + f_periods = num_periods + f_penalty = int(self.IGNORE_ABBREV_PENALTY) or math.pow( + num_nonperiods, -count_without_period + ) + score = log_likelihood * f_length * f_periods * f_penalty + + yield typ, score, is_add + + def find_abbrev_types(self): + """ + Recalculates abbreviations given type frequencies, despite no prior + determination of abbreviations. + This fails to include abbreviations otherwise found as "rare". + """ + self._params.clear_abbrevs() + tokens = (typ for typ in self._type_fdist if typ and typ.endswith(".")) + for abbr, score, _is_add in self._reclassify_abbrev_types(tokens): + if score >= self.ABBREV: + self._params.abbrev_types.add(abbr) + + # This function combines the work done by the original code's + # functions `count_orthography_context`, `get_orthography_count`, + # and `get_rare_abbreviations`. + def _is_rare_abbrev_type(self, cur_tok, next_tok): + """ + A word type is counted as a rare abbreviation if... + - it's not already marked as an abbreviation + - it occurs fewer than ABBREV_BACKOFF times + - either it is followed by a sentence-internal punctuation + mark, *or* it is followed by a lower-case word that + sometimes appears with upper case, but never occurs with + lower case at the beginning of sentences. + """ + if cur_tok.abbr or not cur_tok.sentbreak: + return False + + # Find the case-normalized type of the token. If it's + # a sentence-final token, strip off the period. + typ = cur_tok.type_no_sentperiod + + # Proceed only if the type hasn't been categorized as an + # abbreviation already, and is sufficiently rare... + count = self._type_fdist[typ] + self._type_fdist[typ[:-1]] + if typ in self._params.abbrev_types or count >= self.ABBREV_BACKOFF: + return False + + # Record this token as an abbreviation if the next + # token is a sentence-internal punctuation mark. + # [XX] :1 or check the whole thing?? + if next_tok.tok[:1] in self._lang_vars.internal_punctuation: + return True + + # Record this type as an abbreviation if the next + # token... (i) starts with a lower case letter, + # (ii) sometimes occurs with an uppercase letter, + # and (iii) never occus with an uppercase letter + # sentence-internally. + # [xx] should the check for (ii) be modified?? + if next_tok.first_lower: + typ2 = next_tok.type_no_sentperiod + typ2ortho_context = self._params.ortho_context[typ2] + if (typ2ortho_context & _ORTHO_BEG_UC) and not ( + typ2ortho_context & _ORTHO_MID_UC + ): + return True + + # //////////////////////////////////////////////////////////// + # { Log Likelihoods + # //////////////////////////////////////////////////////////// + + # helper for _reclassify_abbrev_types: + @staticmethod + def _dunning_log_likelihood(count_a, count_b, count_ab, N): + """ + A function that calculates the modified Dunning log-likelihood + ratio scores for abbreviation candidates. The details of how + this works is available in the paper. + """ + p1 = count_b / N + p2 = 0.99 + + null_hypo = count_ab * math.log(p1) + (count_a - count_ab) * math.log(1.0 - p1) + alt_hypo = count_ab * math.log(p2) + (count_a - count_ab) * math.log(1.0 - p2) + + likelihood = null_hypo - alt_hypo + + return -2.0 * likelihood + + @staticmethod + def _col_log_likelihood(count_a, count_b, count_ab, N): + """ + A function that will just compute log-likelihood estimate, in + the original paper it's described in algorithm 6 and 7. + + This *should* be the original Dunning log-likelihood values, + unlike the previous log_l function where it used modified + Dunning log-likelihood values + """ + p = count_b / N + p1 = count_ab / count_a + try: + p2 = (count_b - count_ab) / (N - count_a) + except ZeroDivisionError: + p2 = 1 + + try: + summand1 = count_ab * math.log(p) + (count_a - count_ab) * math.log(1.0 - p) + except ValueError: + summand1 = 0 + + try: + summand2 = (count_b - count_ab) * math.log(p) + ( + N - count_a - count_b + count_ab + ) * math.log(1.0 - p) + except ValueError: + summand2 = 0 + + if count_a == count_ab or p1 <= 0 or p1 >= 1: + summand3 = 0 + else: + summand3 = count_ab * math.log(p1) + (count_a - count_ab) * math.log( + 1.0 - p1 + ) + + if count_b == count_ab or p2 <= 0 or p2 >= 1: + summand4 = 0 + else: + summand4 = (count_b - count_ab) * math.log(p2) + ( + N - count_a - count_b + count_ab + ) * math.log(1.0 - p2) + + likelihood = summand1 + summand2 - summand3 - summand4 + + return -2.0 * likelihood + + # //////////////////////////////////////////////////////////// + # { Collocation Finder + # //////////////////////////////////////////////////////////// + + def _is_potential_collocation(self, aug_tok1, aug_tok2): + """ + Returns True if the pair of tokens may form a collocation given + log-likelihood statistics. + """ + return ( + ( + self.INCLUDE_ALL_COLLOCS + or (self.INCLUDE_ABBREV_COLLOCS and aug_tok1.abbr) + or (aug_tok1.sentbreak and (aug_tok1.is_number or aug_tok1.is_initial)) + ) + and aug_tok1.is_non_punct + and aug_tok2.is_non_punct + ) + + def _find_collocations(self): + """ + Generates likely collocations and their log-likelihood. + """ + for types in self._collocation_fdist: + try: + typ1, typ2 = types + except TypeError: + # types may be None after calling freq_threshold() + continue + if typ2 in self._params.sent_starters: + continue + + col_count = self._collocation_fdist[types] + typ1_count = self._type_fdist[typ1] + self._type_fdist[typ1 + "."] + typ2_count = self._type_fdist[typ2] + self._type_fdist[typ2 + "."] + if ( + typ1_count > 1 + and typ2_count > 1 + and self.MIN_COLLOC_FREQ < col_count <= min(typ1_count, typ2_count) + ): + + log_likelihood = self._col_log_likelihood( + typ1_count, typ2_count, col_count, self._type_fdist.N() + ) + # Filter out the not-so-collocative + if log_likelihood >= self.COLLOCATION and ( + self._type_fdist.N() / typ1_count > typ2_count / col_count + ): + yield (typ1, typ2), log_likelihood + + # //////////////////////////////////////////////////////////// + # { Sentence-Starter Finder + # //////////////////////////////////////////////////////////// + + def _is_potential_sent_starter(self, cur_tok, prev_tok): + """ + Returns True given a token and the token that precedes it if it + seems clear that the token is beginning a sentence. + """ + # If a token (i) is preceded by a sentece break that is + # not a potential ordinal number or initial, and (ii) is + # alphabetic, then it is a a sentence-starter. + return ( + prev_tok.sentbreak + and not (prev_tok.is_number or prev_tok.is_initial) + and cur_tok.is_alpha + ) + + def _find_sent_starters(self): + """ + Uses collocation heuristics for each candidate token to + determine if it frequently starts sentences. + """ + for typ in self._sent_starter_fdist: + if not typ: + continue + + typ_at_break_count = self._sent_starter_fdist[typ] + typ_count = self._type_fdist[typ] + self._type_fdist[typ + "."] + if typ_count < typ_at_break_count: + # needed after freq_threshold + continue + + log_likelihood = self._col_log_likelihood( + self._sentbreak_count, + typ_count, + typ_at_break_count, + self._type_fdist.N(), + ) + + if ( + log_likelihood >= self.SENT_STARTER + and self._type_fdist.N() / self._sentbreak_count + > typ_count / typ_at_break_count + ): + yield typ, log_likelihood + + def _get_sentbreak_count(self, tokens): + """ + Returns the number of sentence breaks marked in a given set of + augmented tokens. + """ + return sum(1 for aug_tok in tokens if aug_tok.sentbreak) + + +###################################################################### +# { Punkt Sentence Tokenizer +###################################################################### + + +class PunktSentenceTokenizer(PunktBaseClass, TokenizerI): + """ + A sentence tokenizer which uses an unsupervised algorithm to build + a model for abbreviation words, collocations, and words that start + sentences; and then uses that model to find sentence boundaries. + This approach has been shown to work well for many European + languages. + """ + + def __init__( + self, train_text=None, verbose=False, lang_vars=None, token_cls=PunktToken + ): + """ + train_text can either be the sole training text for this sentence + boundary detector, or can be a PunktParameters object. + """ + PunktBaseClass.__init__(self, lang_vars=lang_vars, token_cls=token_cls) + + if train_text: + self._params = self.train(train_text, verbose) + + def train(self, train_text, verbose=False): + """ + Derives parameters from a given training text, or uses the parameters + given. Repeated calls to this method destroy previous parameters. For + incremental training, instantiate a separate PunktTrainer instance. + """ + if not isinstance(train_text, str): + return train_text + return PunktTrainer( + train_text, lang_vars=self._lang_vars, token_cls=self._Token + ).get_params() + + # //////////////////////////////////////////////////////////// + # { Tokenization + # //////////////////////////////////////////////////////////// + + def tokenize(self, text: str, realign_boundaries: bool = True) -> List[str]: + """ + Given a text, returns a list of the sentences in that text. + """ + return list(self.sentences_from_text(text, realign_boundaries)) + + def debug_decisions(self, text: str) -> Iterator[Dict[str, Any]]: + """ + Classifies candidate periods as sentence breaks, yielding a dict for + each that may be used to understand why the decision was made. + + See format_debug_decision() to help make this output readable. + """ + + for match, decision_text in self._match_potential_end_contexts(text): + tokens = self._tokenize_words(decision_text) + tokens = list(self._annotate_first_pass(tokens)) + while tokens and not tokens[0].tok.endswith(self._lang_vars.sent_end_chars): + tokens.pop(0) + yield { + "period_index": match.end() - 1, + "text": decision_text, + "type1": tokens[0].type, + "type2": tokens[1].type, + "type1_in_abbrs": bool(tokens[0].abbr), + "type1_is_initial": bool(tokens[0].is_initial), + "type2_is_sent_starter": tokens[1].type_no_sentperiod + in self._params.sent_starters, + "type2_ortho_heuristic": self._ortho_heuristic(tokens[1]), + "type2_ortho_contexts": set( + self._params._debug_ortho_context(tokens[1].type_no_sentperiod) + ), + "collocation": ( + tokens[0].type_no_sentperiod, + tokens[1].type_no_sentperiod, + ) + in self._params.collocations, + "reason": self._second_pass_annotation(tokens[0], tokens[1]) + or REASON_DEFAULT_DECISION, + "break_decision": tokens[0].sentbreak, + } + + def span_tokenize( + self, text: str, realign_boundaries: bool = True + ) -> Iterator[Tuple[int, int]]: + """ + Given a text, generates (start, end) spans of sentences + in the text. + """ + slices = self._slices_from_text(text) + if realign_boundaries: + slices = self._realign_boundaries(text, slices) + for sentence in slices: + yield (sentence.start, sentence.stop) + + def sentences_from_text( + self, text: str, realign_boundaries: bool = True + ) -> List[str]: + """ + Given a text, generates the sentences in that text by only + testing candidate sentence breaks. If realign_boundaries is + True, includes in the sentence closing punctuation that + follows the period. + """ + return [text[s:e] for s, e in self.span_tokenize(text, realign_boundaries)] + + def _get_last_whitespace_index(self, text: str) -> int: + """ + Given a text, find the index of the *last* occurrence of *any* + whitespace character, i.e. " ", "\n", "\t", "\r", etc. + If none is found, return 0. + """ + for i in range(len(text) - 1, -1, -1): + if text[i] in string.whitespace: + return i + return 0 + + def _match_potential_end_contexts(self, text: str) -> Iterator[Tuple[Match, str]]: + """ + Given a text, find the matches of potential sentence breaks, + alongside the contexts surrounding these sentence breaks. + + Since the fix for the ReDOS discovered in issue #2866, we no longer match + the word before a potential end of sentence token. Instead, we use a separate + regex for this. As a consequence, `finditer`'s desire to find non-overlapping + matches no longer aids us in finding the single longest match. + Where previously, we could use:: + + >>> pst = PunktSentenceTokenizer() + >>> text = "Very bad acting!!! I promise." + >>> list(pst._lang_vars.period_context_re().finditer(text)) # doctest: +SKIP + [] + + Now we have to find the word before (i.e. 'acting') separately, and `finditer` + returns:: + + >>> pst = PunktSentenceTokenizer() + >>> text = "Very bad acting!!! I promise." + >>> list(pst._lang_vars.period_context_re().finditer(text)) # doctest: +NORMALIZE_WHITESPACE + [, + , + ] + + So, we need to find the word before the match from right to left, and then manually remove + the overlaps. That is what this method does:: + + >>> pst = PunktSentenceTokenizer() + >>> text = "Very bad acting!!! I promise." + >>> list(pst._match_potential_end_contexts(text)) + [(, 'acting!!! I')] + + :param text: String of one or more sentences + :type text: str + :return: Generator of match-context tuples. + :rtype: Iterator[Tuple[Match, str]] + """ + previous_slice = slice(0, 0) + previous_match = None + for match in self._lang_vars.period_context_re().finditer(text): + + # Get the slice of the previous word + before_text = text[previous_slice.stop : match.start()] + index_after_last_space = self._get_last_whitespace_index(before_text) + if index_after_last_space: + # + 1 to exclude the space itself + index_after_last_space += previous_slice.stop + 1 + else: + index_after_last_space = previous_slice.start + prev_word_slice = slice(index_after_last_space, match.start()) + + # If the previous slice does not overlap with this slice, then + # we can yield the previous match and slice. If there is an overlap, + # then we do not yield the previous match and slice. + if previous_match and previous_slice.stop <= prev_word_slice.start: + yield ( + previous_match, + text[previous_slice] + + previous_match.group() + + previous_match.group("after_tok"), + ) + previous_match = match + previous_slice = prev_word_slice + + # Yield the last match and context, if it exists + if previous_match: + yield ( + previous_match, + text[previous_slice] + + previous_match.group() + + previous_match.group("after_tok"), + ) + + def _slices_from_text(self, text: str) -> Iterator[slice]: + last_break = 0 + for match, context in self._match_potential_end_contexts(text): + if self.text_contains_sentbreak(context): + yield slice(last_break, match.end()) + if match.group("next_tok"): + # next sentence starts after whitespace + last_break = match.start("next_tok") + else: + # next sentence starts at following punctuation + last_break = match.end() + # The last sentence should not contain trailing whitespace. + yield slice(last_break, len(text.rstrip())) + + def _realign_boundaries( + self, text: str, slices: Iterator[slice] + ) -> Iterator[slice]: + """ + Attempts to realign punctuation that falls after the period but + should otherwise be included in the same sentence. + + For example: "(Sent1.) Sent2." will otherwise be split as:: + + ["(Sent1.", ") Sent1."]. + + This method will produce:: + + ["(Sent1.)", "Sent2."]. + """ + realign = 0 + for sentence1, sentence2 in _pair_iter(slices): + sentence1 = slice(sentence1.start + realign, sentence1.stop) + if not sentence2: + if text[sentence1]: + yield sentence1 + continue + + m = self._lang_vars.re_boundary_realignment.match(text[sentence2]) + if m: + yield slice(sentence1.start, sentence2.start + len(m.group(0).rstrip())) + realign = m.end() + else: + realign = 0 + if text[sentence1]: + yield sentence1 + + def text_contains_sentbreak(self, text: str) -> bool: + """ + Returns True if the given text includes a sentence break. + """ + found = False # used to ignore last token + for tok in self._annotate_tokens(self._tokenize_words(text)): + if found: + return True + if tok.sentbreak: + found = True + return False + + def sentences_from_text_legacy(self, text: str) -> Iterator[str]: + """ + Given a text, generates the sentences in that text. Annotates all + tokens, rather than just those with possible sentence breaks. Should + produce the same results as ``sentences_from_text``. + """ + tokens = self._annotate_tokens(self._tokenize_words(text)) + return self._build_sentence_list(text, tokens) + + def sentences_from_tokens( + self, tokens: Iterator[PunktToken] + ) -> Iterator[PunktToken]: + """ + Given a sequence of tokens, generates lists of tokens, each list + corresponding to a sentence. + """ + tokens = iter(self._annotate_tokens(self._Token(t) for t in tokens)) + sentence = [] + for aug_tok in tokens: + sentence.append(aug_tok.tok) + if aug_tok.sentbreak: + yield sentence + sentence = [] + if sentence: + yield sentence + + def _annotate_tokens(self, tokens: Iterator[PunktToken]) -> Iterator[PunktToken]: + """ + Given a set of tokens augmented with markers for line-start and + paragraph-start, returns an iterator through those tokens with full + annotation including predicted sentence breaks. + """ + # Make a preliminary pass through the document, marking likely + # sentence breaks, abbreviations, and ellipsis tokens. + tokens = self._annotate_first_pass(tokens) + + # Make a second pass through the document, using token context + # information to change our preliminary decisions about where + # sentence breaks, abbreviations, and ellipsis occurs. + tokens = self._annotate_second_pass(tokens) + + ## [XX] TESTING + # tokens = list(tokens) + # self.dump(tokens) + + return tokens + + def _build_sentence_list( + self, text: str, tokens: Iterator[PunktToken] + ) -> Iterator[str]: + """ + Given the original text and the list of augmented word tokens, + construct and return a tokenized list of sentence strings. + """ + # Most of the work here is making sure that we put the right + # pieces of whitespace back in all the right places. + + # Our position in the source text, used to keep track of which + # whitespace to add: + pos = 0 + + # A regular expression that finds pieces of whitespace: + white_space_regexp = re.compile(r"\s*") + + sentence = "" + for aug_tok in tokens: + tok = aug_tok.tok + + # Find the whitespace before this token, and update pos. + white_space = white_space_regexp.match(text, pos).group() + pos += len(white_space) + + # Some of the rules used by the punkt word tokenizer + # strip whitespace out of the text, resulting in tokens + # that contain whitespace in the source text. If our + # token doesn't match, see if adding whitespace helps. + # If so, then use the version with whitespace. + if text[pos : pos + len(tok)] != tok: + pat = r"\s*".join(re.escape(c) for c in tok) + m = re.compile(pat).match(text, pos) + if m: + tok = m.group() + + # Move our position pointer to the end of the token. + assert text[pos : pos + len(tok)] == tok + pos += len(tok) + + # Add this token. If it's not at the beginning of the + # sentence, then include any whitespace that separated it + # from the previous token. + if sentence: + sentence += white_space + sentence += tok + + # If we're at a sentence break, then start a new sentence. + if aug_tok.sentbreak: + yield sentence + sentence = "" + + # If the last sentence is empty, discard it. + if sentence: + yield sentence + + # [XX] TESTING + def dump(self, tokens: Iterator[PunktToken]) -> None: + print("writing to /tmp/punkt.new...") + with open("/tmp/punkt.new", "w") as outfile: + for aug_tok in tokens: + if aug_tok.parastart: + outfile.write("\n\n") + elif aug_tok.linestart: + outfile.write("\n") + else: + outfile.write(" ") + + outfile.write(str(aug_tok)) + + # //////////////////////////////////////////////////////////// + # { Customization Variables + # //////////////////////////////////////////////////////////// + + PUNCTUATION = tuple(";:,.!?") + + # //////////////////////////////////////////////////////////// + # { Annotation Procedures + # //////////////////////////////////////////////////////////// + + def _annotate_second_pass( + self, tokens: Iterator[PunktToken] + ) -> Iterator[PunktToken]: + """ + Performs a token-based classification (section 4) over the given + tokens, making use of the orthographic heuristic (4.1.1), collocation + heuristic (4.1.2) and frequent sentence starter heuristic (4.1.3). + """ + for token1, token2 in _pair_iter(tokens): + self._second_pass_annotation(token1, token2) + yield token1 + + def _second_pass_annotation( + self, aug_tok1: PunktToken, aug_tok2: Optional[PunktToken] + ) -> Optional[str]: + """ + Performs token-based classification over a pair of contiguous tokens + updating the first. + """ + # Is it the last token? We can't do anything then. + if not aug_tok2: + return + + if not aug_tok1.period_final: + # We only care about words ending in periods. + return + typ = aug_tok1.type_no_period + next_typ = aug_tok2.type_no_sentperiod + tok_is_initial = aug_tok1.is_initial + + # [4.1.2. Collocation Heuristic] If there's a + # collocation between the word before and after the + # period, then label tok as an abbreviation and NOT + # a sentence break. Note that collocations with + # frequent sentence starters as their second word are + # excluded in training. + if (typ, next_typ) in self._params.collocations: + aug_tok1.sentbreak = False + aug_tok1.abbr = True + return REASON_KNOWN_COLLOCATION + + # [4.2. Token-Based Reclassification of Abbreviations] If + # the token is an abbreviation or an ellipsis, then decide + # whether we should *also* classify it as a sentbreak. + if (aug_tok1.abbr or aug_tok1.ellipsis) and (not tok_is_initial): + # [4.1.1. Orthographic Heuristic] Check if there's + # orthogrpahic evidence about whether the next word + # starts a sentence or not. + is_sent_starter = self._ortho_heuristic(aug_tok2) + if is_sent_starter == True: + aug_tok1.sentbreak = True + return REASON_ABBR_WITH_ORTHOGRAPHIC_HEURISTIC + + # [4.1.3. Frequent Sentence Starter Heruistic] If the + # next word is capitalized, and is a member of the + # frequent-sentence-starters list, then label tok as a + # sentence break. + if aug_tok2.first_upper and next_typ in self._params.sent_starters: + aug_tok1.sentbreak = True + return REASON_ABBR_WITH_SENTENCE_STARTER + + # [4.3. Token-Based Detection of Initials and Ordinals] + # Check if any initials or ordinals tokens that are marked + # as sentbreaks should be reclassified as abbreviations. + if tok_is_initial or typ == "##number##": + + # [4.1.1. Orthographic Heuristic] Check if there's + # orthogrpahic evidence about whether the next word + # starts a sentence or not. + is_sent_starter = self._ortho_heuristic(aug_tok2) + + if is_sent_starter == False: + aug_tok1.sentbreak = False + aug_tok1.abbr = True + if tok_is_initial: + return REASON_INITIAL_WITH_ORTHOGRAPHIC_HEURISTIC + return REASON_NUMBER_WITH_ORTHOGRAPHIC_HEURISTIC + + # Special heuristic for initials: if orthogrpahic + # heuristic is unknown, and next word is always + # capitalized, then mark as abbrev (eg: J. Bach). + if ( + is_sent_starter == "unknown" + and tok_is_initial + and aug_tok2.first_upper + and not (self._params.ortho_context[next_typ] & _ORTHO_LC) + ): + aug_tok1.sentbreak = False + aug_tok1.abbr = True + return REASON_INITIAL_WITH_SPECIAL_ORTHOGRAPHIC_HEURISTIC + + return + + def _ortho_heuristic(self, aug_tok: PunktToken) -> Union[bool, str]: + """ + Decide whether the given token is the first token in a sentence. + """ + # Sentences don't start with punctuation marks: + if aug_tok.tok in self.PUNCTUATION: + return False + + ortho_context = self._params.ortho_context[aug_tok.type_no_sentperiod] + + # If the word is capitalized, occurs at least once with a + # lower case first letter, and never occurs with an upper case + # first letter sentence-internally, then it's a sentence starter. + if ( + aug_tok.first_upper + and (ortho_context & _ORTHO_LC) + and not (ortho_context & _ORTHO_MID_UC) + ): + return True + + # If the word is lower case, and either (a) we've seen it used + # with upper case, or (b) we've never seen it used + # sentence-initially with lower case, then it's not a sentence + # starter. + if aug_tok.first_lower and ( + (ortho_context & _ORTHO_UC) or not (ortho_context & _ORTHO_BEG_LC) + ): + return False + + # Otherwise, we're not sure. + return "unknown" + + +DEBUG_DECISION_FMT = """Text: {text!r} (at offset {period_index}) +Sentence break? {break_decision} ({reason}) +Collocation? {collocation} +{type1!r}: + known abbreviation: {type1_in_abbrs} + is initial: {type1_is_initial} +{type2!r}: + known sentence starter: {type2_is_sent_starter} + orthographic heuristic suggests is a sentence starter? {type2_ortho_heuristic} + orthographic contexts in training: {type2_ortho_contexts} +""" + + +def format_debug_decision(d): + return DEBUG_DECISION_FMT.format(**d) + + +def demo(text, tok_cls=PunktSentenceTokenizer, train_cls=PunktTrainer): + """Builds a punkt model and applies it to the same text""" + cleanup = ( + lambda s: re.compile(r"(?:\r|^\s+)", re.MULTILINE).sub("", s).replace("\n", " ") + ) + trainer = train_cls() + trainer.INCLUDE_ALL_COLLOCS = True + trainer.train(text) + sbd = tok_cls(trainer.get_params()) + for sentence in sbd.sentences_from_text(text): + print(cleanup(sentence)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/regexp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/regexp.py new file mode 100644 index 0000000..cd82d5c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/regexp.py @@ -0,0 +1,220 @@ +# Natural Language Toolkit: Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Trevor Cohn +# URL: +# For license information, see LICENSE.TXT + +r""" +Regular-Expression Tokenizers + +A ``RegexpTokenizer`` splits a string into substrings using a regular expression. +For example, the following tokenizer forms tokens out of alphabetic sequences, +money expressions, and any other non-whitespace sequences: + + >>> from nltk.tokenize import RegexpTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> tokenizer = RegexpTokenizer(r'\w+|\$[\d\.]+|\S+') + >>> tokenizer.tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88', 'in', 'New', 'York', '.', + 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + +A ``RegexpTokenizer`` can use its regexp to match delimiters instead: + + >>> tokenizer = RegexpTokenizer(r'\s+', gaps=True) + >>> tokenizer.tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88', 'in', 'New', 'York.', + 'Please', 'buy', 'me', 'two', 'of', 'them.', 'Thanks.'] + +Note that empty tokens are not returned when the delimiter appears at +the start or end of the string. + +The material between the tokens is discarded. For example, +the following tokenizer selects just the capitalized words: + + >>> capword_tokenizer = RegexpTokenizer(r'[A-Z]\w+') + >>> capword_tokenizer.tokenize(s) + ['Good', 'New', 'York', 'Please', 'Thanks'] + +This module contains several subclasses of ``RegexpTokenizer`` +that use pre-defined regular expressions. + + >>> from nltk.tokenize import BlanklineTokenizer + >>> # Uses '\s*\n\s*\n\s*': + >>> BlanklineTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.', + 'Thanks.'] + +All of the regular expression tokenizers are also available as functions: + + >>> from nltk.tokenize import regexp_tokenize, wordpunct_tokenize, blankline_tokenize + >>> regexp_tokenize(s, pattern=r'\w+|\$[\d\.]+|\S+') # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88', 'in', 'New', 'York', '.', + 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + >>> wordpunct_tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3', '.', '88', 'in', 'New', 'York', + '.', 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + >>> blankline_tokenize(s) + ['Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.', 'Thanks.'] + +Caution: The function ``regexp_tokenize()`` takes the text as its +first argument, and the regular expression pattern as its second +argument. This differs from the conventions used by Python's +``re`` functions, where the pattern is always the first argument. +(This is for consistency with the other NLTK tokenizers.) +""" + +import re + +from nltk.tokenize.api import TokenizerI +from nltk.tokenize.util import regexp_span_tokenize + + +class RegexpTokenizer(TokenizerI): + r""" + A tokenizer that splits a string using a regular expression, which + matches either the tokens or the separators between tokens. + + >>> tokenizer = RegexpTokenizer(r'\w+|\$[\d\.]+|\S+') + + :type pattern: str + :param pattern: The pattern used to build this tokenizer. + (This pattern must not contain capturing parentheses; + Use non-capturing parentheses, e.g. (?:...), instead) + :type gaps: bool + :param gaps: True if this tokenizer's pattern should be used + to find separators between tokens; False if this + tokenizer's pattern should be used to find the tokens + themselves. + :type discard_empty: bool + :param discard_empty: True if any empty tokens `''` + generated by the tokenizer should be discarded. Empty + tokens can only be generated if `_gaps == True`. + :type flags: int + :param flags: The regexp flags used to compile this + tokenizer's pattern. By default, the following flags are + used: `re.UNICODE | re.MULTILINE | re.DOTALL`. + + """ + + def __init__( + self, + pattern, + gaps=False, + discard_empty=True, + flags=re.UNICODE | re.MULTILINE | re.DOTALL, + ): + # If they gave us a regexp object, extract the pattern. + pattern = getattr(pattern, "pattern", pattern) + + self._pattern = pattern + self._gaps = gaps + self._discard_empty = discard_empty + self._flags = flags + self._regexp = None + + def _check_regexp(self): + if self._regexp is None: + self._regexp = re.compile(self._pattern, self._flags) + + def tokenize(self, text): + self._check_regexp() + # If our regexp matches gaps, use re.split: + if self._gaps: + if self._discard_empty: + return [tok for tok in self._regexp.split(text) if tok] + else: + return self._regexp.split(text) + + # If our regexp matches tokens, use re.findall: + else: + return self._regexp.findall(text) + + def span_tokenize(self, text): + self._check_regexp() + + if self._gaps: + for left, right in regexp_span_tokenize(text, self._regexp): + if not (self._discard_empty and left == right): + yield left, right + else: + for m in re.finditer(self._regexp, text): + yield m.span() + + def __repr__(self): + return "{}(pattern={!r}, gaps={!r}, discard_empty={!r}, flags={!r})".format( + self.__class__.__name__, + self._pattern, + self._gaps, + self._discard_empty, + self._flags, + ) + + +class WhitespaceTokenizer(RegexpTokenizer): + r""" + Tokenize a string on whitespace (space, tab, newline). + In general, users should use the string ``split()`` method instead. + + >>> from nltk.tokenize import WhitespaceTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> WhitespaceTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88', 'in', 'New', 'York.', + 'Please', 'buy', 'me', 'two', 'of', 'them.', 'Thanks.'] + """ + + def __init__(self): + RegexpTokenizer.__init__(self, r"\s+", gaps=True) + + +class BlanklineTokenizer(RegexpTokenizer): + """ + Tokenize a string, treating any sequence of blank lines as a delimiter. + Blank lines are defined as lines containing no characters, except for + space or tab characters. + """ + + def __init__(self): + RegexpTokenizer.__init__(self, r"\s*\n\s*\n\s*", gaps=True) + + +class WordPunctTokenizer(RegexpTokenizer): + r""" + Tokenize a text into a sequence of alphabetic and + non-alphabetic characters, using the regexp ``\w+|[^\w\s]+``. + + >>> from nltk.tokenize import WordPunctTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> WordPunctTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3', '.', '88', 'in', 'New', 'York', + '.', 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + """ + + def __init__(self): + RegexpTokenizer.__init__(self, r"\w+|[^\w\s]+") + + +###################################################################### +# { Tokenization Functions +###################################################################### + + +def regexp_tokenize( + text, + pattern, + gaps=False, + discard_empty=True, + flags=re.UNICODE | re.MULTILINE | re.DOTALL, +): + """ + Return a tokenized copy of *text*. See :class:`.RegexpTokenizer` + for descriptions of the arguments. + """ + tokenizer = RegexpTokenizer(pattern, gaps, discard_empty, flags) + return tokenizer.tokenize(text) + + +blankline_tokenize = BlanklineTokenizer().tokenize +wordpunct_tokenize = WordPunctTokenizer().tokenize diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/repp.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/repp.py new file mode 100644 index 0000000..9f48966 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/repp.py @@ -0,0 +1,149 @@ +# Natural Language Toolkit: Interface to the Repp Tokenizer +# +# Copyright (C) 2001-2015 NLTK Project +# Authors: Rebecca Dridan and Stephan Oepen +# Contributors: Liling Tan +# +# URL: +# For license information, see LICENSE.TXT + +import os +import re +import subprocess +import sys +import tempfile + +from nltk.data import ZipFilePathPointer +from nltk.internals import find_dir +from nltk.tokenize.api import TokenizerI + + +class ReppTokenizer(TokenizerI): + """ + A class for word tokenization using the REPP parser described in + Rebecca Dridan and Stephan Oepen (2012) Tokenization: Returning to a + Long Solved Problem - A Survey, Contrastive Experiment, Recommendations, + and Toolkit. In ACL. http://anthology.aclweb.org/P/P12/P12-2.pdf#page=406 + + >>> sents = ['Tokenization is widely regarded as a solved problem due to the high accuracy that rulebased tokenizers achieve.' , + ... 'But rule-based tokenizers are hard to maintain and their rules language specific.' , + ... 'We evaluated our method on three languages and obtained error rates of 0.27% (English), 0.35% (Dutch) and 0.76% (Italian) for our best models.' + ... ] + >>> tokenizer = ReppTokenizer('/home/alvas/repp/') # doctest: +SKIP + >>> for sent in sents: # doctest: +SKIP + ... tokenizer.tokenize(sent) # doctest: +SKIP + ... + (u'Tokenization', u'is', u'widely', u'regarded', u'as', u'a', u'solved', u'problem', u'due', u'to', u'the', u'high', u'accuracy', u'that', u'rulebased', u'tokenizers', u'achieve', u'.') + (u'But', u'rule-based', u'tokenizers', u'are', u'hard', u'to', u'maintain', u'and', u'their', u'rules', u'language', u'specific', u'.') + (u'We', u'evaluated', u'our', u'method', u'on', u'three', u'languages', u'and', u'obtained', u'error', u'rates', u'of', u'0.27', u'%', u'(', u'English', u')', u',', u'0.35', u'%', u'(', u'Dutch', u')', u'and', u'0.76', u'%', u'(', u'Italian', u')', u'for', u'our', u'best', u'models', u'.') + + >>> for sent in tokenizer.tokenize_sents(sents): # doctest: +SKIP + ... print(sent) # doctest: +SKIP + ... + (u'Tokenization', u'is', u'widely', u'regarded', u'as', u'a', u'solved', u'problem', u'due', u'to', u'the', u'high', u'accuracy', u'that', u'rulebased', u'tokenizers', u'achieve', u'.') + (u'But', u'rule-based', u'tokenizers', u'are', u'hard', u'to', u'maintain', u'and', u'their', u'rules', u'language', u'specific', u'.') + (u'We', u'evaluated', u'our', u'method', u'on', u'three', u'languages', u'and', u'obtained', u'error', u'rates', u'of', u'0.27', u'%', u'(', u'English', u')', u',', u'0.35', u'%', u'(', u'Dutch', u')', u'and', u'0.76', u'%', u'(', u'Italian', u')', u'for', u'our', u'best', u'models', u'.') + >>> for sent in tokenizer.tokenize_sents(sents, keep_token_positions=True): # doctest: +SKIP + ... print(sent) # doctest: +SKIP + ... + [(u'Tokenization', 0, 12), (u'is', 13, 15), (u'widely', 16, 22), (u'regarded', 23, 31), (u'as', 32, 34), (u'a', 35, 36), (u'solved', 37, 43), (u'problem', 44, 51), (u'due', 52, 55), (u'to', 56, 58), (u'the', 59, 62), (u'high', 63, 67), (u'accuracy', 68, 76), (u'that', 77, 81), (u'rulebased', 82, 91), (u'tokenizers', 92, 102), (u'achieve', 103, 110), (u'.', 110, 111)] + [(u'But', 0, 3), (u'rule-based', 4, 14), (u'tokenizers', 15, 25), (u'are', 26, 29), (u'hard', 30, 34), (u'to', 35, 37), (u'maintain', 38, 46), (u'and', 47, 50), (u'their', 51, 56), (u'rules', 57, 62), (u'language', 63, 71), (u'specific', 72, 80), (u'.', 80, 81)] + [(u'We', 0, 2), (u'evaluated', 3, 12), (u'our', 13, 16), (u'method', 17, 23), (u'on', 24, 26), (u'three', 27, 32), (u'languages', 33, 42), (u'and', 43, 46), (u'obtained', 47, 55), (u'error', 56, 61), (u'rates', 62, 67), (u'of', 68, 70), (u'0.27', 71, 75), (u'%', 75, 76), (u'(', 77, 78), (u'English', 78, 85), (u')', 85, 86), (u',', 86, 87), (u'0.35', 88, 92), (u'%', 92, 93), (u'(', 94, 95), (u'Dutch', 95, 100), (u')', 100, 101), (u'and', 102, 105), (u'0.76', 106, 110), (u'%', 110, 111), (u'(', 112, 113), (u'Italian', 113, 120), (u')', 120, 121), (u'for', 122, 125), (u'our', 126, 129), (u'best', 130, 134), (u'models', 135, 141), (u'.', 141, 142)] + """ + + def __init__(self, repp_dir, encoding="utf8"): + self.repp_dir = self.find_repptokenizer(repp_dir) + # Set a directory to store the temporary files. + self.working_dir = tempfile.gettempdir() + # Set an encoding for the input strings. + self.encoding = encoding + + def tokenize(self, sentence): + """ + Use Repp to tokenize a single sentence. + + :param sentence: A single sentence string. + :type sentence: str + :return: A tuple of tokens. + :rtype: tuple(str) + """ + return next(self.tokenize_sents([sentence])) + + def tokenize_sents(self, sentences, keep_token_positions=False): + """ + Tokenize multiple sentences using Repp. + + :param sentences: A list of sentence strings. + :type sentences: list(str) + :return: A list of tuples of tokens + :rtype: iter(tuple(str)) + """ + with tempfile.NamedTemporaryFile( + prefix="repp_input.", dir=self.working_dir, mode="w", delete=False + ) as input_file: + # Write sentences to temporary input file. + for sent in sentences: + input_file.write(str(sent) + "\n") + input_file.close() + # Generate command to run REPP. + cmd = self.generate_repp_command(input_file.name) + # Decode the stdout and strips the ending newline. + repp_output = self._execute(cmd).decode(self.encoding).strip() + for tokenized_sent in self.parse_repp_outputs(repp_output): + if not keep_token_positions: + # Removes token position information. + tokenized_sent, starts, ends = zip(*tokenized_sent) + yield tokenized_sent + + def generate_repp_command(self, inputfilename): + """ + This module generates the REPP command to be used at the terminal. + + :param inputfilename: path to the input file + :type inputfilename: str + """ + cmd = [self.repp_dir + "/src/repp"] + cmd += ["-c", self.repp_dir + "/erg/repp.set"] + cmd += ["--format", "triple"] + cmd += [inputfilename] + return cmd + + @staticmethod + def _execute(cmd): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + return stdout + + @staticmethod + def parse_repp_outputs(repp_output): + """ + This module parses the tri-tuple format that REPP outputs using the + "--format triple" option and returns an generator with tuple of string + tokens. + + :param repp_output: + :type repp_output: type + :return: an iterable of the tokenized sentences as tuples of strings + :rtype: iter(tuple) + """ + line_regex = re.compile(r"^\((\d+), (\d+), (.+)\)$", re.MULTILINE) + for section in repp_output.split("\n\n"): + words_with_positions = [ + (token, int(start), int(end)) + for start, end, token in line_regex.findall(section) + ] + words = tuple(t[2] for t in words_with_positions) + yield words_with_positions + + def find_repptokenizer(self, repp_dirname): + """ + A module to find REPP tokenizer binary and its *repp.set* config file. + """ + if os.path.exists(repp_dirname): # If a full path is given. + _repp_dir = repp_dirname + else: # Try to find path to REPP directory in environment variables. + _repp_dir = find_dir(repp_dirname, env_vars=("REPP_TOKENIZER",)) + # Checks for the REPP binary and erg/repp.set config file. + assert os.path.exists(_repp_dir + "/src/repp") + assert os.path.exists(_repp_dir + "/erg/repp.set") + return _repp_dir diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sexpr.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sexpr.py new file mode 100644 index 0000000..08d9705 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sexpr.py @@ -0,0 +1,140 @@ +# Natural Language Toolkit: Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Yoav Goldberg +# Steven Bird (minor edits) +# URL: +# For license information, see LICENSE.TXT + +""" +S-Expression Tokenizer + +``SExprTokenizer`` is used to find parenthesized expressions in a +string. In particular, it divides a string into a sequence of +substrings that are either parenthesized expressions (including any +nested parenthesized expressions), or other whitespace-separated +tokens. + + >>> from nltk.tokenize import SExprTokenizer + >>> SExprTokenizer().tokenize('(a b (c d)) e f (g)') + ['(a b (c d))', 'e', 'f', '(g)'] + +By default, `SExprTokenizer` will raise a ``ValueError`` exception if +used to tokenize an expression with non-matching parentheses: + + >>> SExprTokenizer().tokenize('c) d) e (f (g') + Traceback (most recent call last): + ... + ValueError: Un-matched close paren at char 1 + +The ``strict`` argument can be set to False to allow for +non-matching parentheses. Any unmatched close parentheses will be +listed as their own s-expression; and the last partial sexpr with +unmatched open parentheses will be listed as its own sexpr: + + >>> SExprTokenizer(strict=False).tokenize('c) d) e (f (g') + ['c', ')', 'd', ')', 'e', '(f (g'] + +The characters used for open and close parentheses may be customized +using the ``parens`` argument to the `SExprTokenizer` constructor: + + >>> SExprTokenizer(parens='{}').tokenize('{a b {c d}} e f {g}') + ['{a b {c d}}', 'e', 'f', '{g}'] + +The s-expression tokenizer is also available as a function: + + >>> from nltk.tokenize import sexpr_tokenize + >>> sexpr_tokenize('(a b (c d)) e f (g)') + ['(a b (c d))', 'e', 'f', '(g)'] + +""" + +import re + +from nltk.tokenize.api import TokenizerI + + +class SExprTokenizer(TokenizerI): + """ + A tokenizer that divides strings into s-expressions. + An s-expresion can be either: + + - a parenthesized expression, including any nested parenthesized + expressions, or + - a sequence of non-whitespace non-parenthesis characters. + + For example, the string ``(a (b c)) d e (f)`` consists of four + s-expressions: ``(a (b c))``, ``d``, ``e``, and ``(f)``. + + By default, the characters ``(`` and ``)`` are treated as open and + close parentheses, but alternative strings may be specified. + + :param parens: A two-element sequence specifying the open and close parentheses + that should be used to find sexprs. This will typically be either a + two-character string, or a list of two strings. + :type parens: str or list + :param strict: If true, then raise an exception when tokenizing an ill-formed sexpr. + """ + + def __init__(self, parens="()", strict=True): + if len(parens) != 2: + raise ValueError("parens must contain exactly two strings") + self._strict = strict + self._open_paren = parens[0] + self._close_paren = parens[1] + self._paren_regexp = re.compile( + f"{re.escape(parens[0])}|{re.escape(parens[1])}" + ) + + def tokenize(self, text): + """ + Return a list of s-expressions extracted from *text*. + For example: + + >>> SExprTokenizer().tokenize('(a b (c d)) e f (g)') + ['(a b (c d))', 'e', 'f', '(g)'] + + All parentheses are assumed to mark s-expressions. + (No special processing is done to exclude parentheses that occur + inside strings, or following backslash characters.) + + If the given expression contains non-matching parentheses, + then the behavior of the tokenizer depends on the ``strict`` + parameter to the constructor. If ``strict`` is ``True``, then + raise a ``ValueError``. If ``strict`` is ``False``, then any + unmatched close parentheses will be listed as their own + s-expression; and the last partial s-expression with unmatched open + parentheses will be listed as its own s-expression: + + >>> SExprTokenizer(strict=False).tokenize('c) d) e (f (g') + ['c', ')', 'd', ')', 'e', '(f (g'] + + :param text: the string to be tokenized + :type text: str or iter(str) + :rtype: iter(str) + """ + result = [] + pos = 0 + depth = 0 + for m in self._paren_regexp.finditer(text): + paren = m.group() + if depth == 0: + result += text[pos : m.start()].split() + pos = m.start() + if paren == self._open_paren: + depth += 1 + if paren == self._close_paren: + if self._strict and depth == 0: + raise ValueError("Un-matched close paren at char %d" % m.start()) + depth = max(0, depth - 1) + if depth == 0: + result.append(text[pos : m.end()]) + pos = m.end() + if self._strict and depth > 0: + raise ValueError("Un-matched open paren at char %d" % pos) + if pos < len(text): + result.append(text[pos:]) + return result + + +sexpr_tokenize = SExprTokenizer().tokenize diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/simple.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/simple.py new file mode 100644 index 0000000..71a02d3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/simple.py @@ -0,0 +1,137 @@ +# Natural Language Toolkit: Simple Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + +r""" +Simple Tokenizers + +These tokenizers divide strings into substrings using the string +``split()`` method. +When tokenizing using a particular delimiter string, use +the string ``split()`` method directly, as this is more efficient. + +The simple tokenizers are *not* available as separate functions; +instead, you should just use the string ``split()`` method directly: + + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> s.split() # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88', 'in', 'New', 'York.', + 'Please', 'buy', 'me', 'two', 'of', 'them.', 'Thanks.'] + >>> s.split(' ') # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88\nin', 'New', 'York.', '', + 'Please', 'buy', 'me\ntwo', 'of', 'them.\n\nThanks.'] + >>> s.split('\n') # doctest: +NORMALIZE_WHITESPACE + ['Good muffins cost $3.88', 'in New York. Please buy me', + 'two of them.', '', 'Thanks.'] + +The simple tokenizers are mainly useful because they follow the +standard ``TokenizerI`` interface, and so can be used with any code +that expects a tokenizer. For example, these tokenizers can be used +to specify the tokenization conventions when building a `CorpusReader`. + +""" + +from nltk.tokenize.api import StringTokenizer, TokenizerI +from nltk.tokenize.util import regexp_span_tokenize, string_span_tokenize + + +class SpaceTokenizer(StringTokenizer): + r"""Tokenize a string using the space character as a delimiter, + which is the same as ``s.split(' ')``. + + >>> from nltk.tokenize import SpaceTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> SpaceTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$3.88\nin', 'New', 'York.', '', + 'Please', 'buy', 'me\ntwo', 'of', 'them.\n\nThanks.'] + """ + + _string = " " + + +class TabTokenizer(StringTokenizer): + r"""Tokenize a string use the tab character as a delimiter, + the same as ``s.split('\t')``. + + >>> from nltk.tokenize import TabTokenizer + >>> TabTokenizer().tokenize('a\tb c\n\t d') + ['a', 'b c\n', ' d'] + """ + + _string = "\t" + + +class CharTokenizer(StringTokenizer): + """Tokenize a string into individual characters. If this functionality + is ever required directly, use ``for char in string``. + """ + + def tokenize(self, s): + return list(s) + + def span_tokenize(self, s): + yield from enumerate(range(1, len(s) + 1)) + + +class LineTokenizer(TokenizerI): + r"""Tokenize a string into its lines, optionally discarding blank lines. + This is similar to ``s.split('\n')``. + + >>> from nltk.tokenize import LineTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\n\nThanks." + >>> LineTokenizer(blanklines='keep').tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good muffins cost $3.88', 'in New York. Please buy me', + 'two of them.', '', 'Thanks.'] + >>> # same as [l for l in s.split('\n') if l.strip()]: + >>> LineTokenizer(blanklines='discard').tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good muffins cost $3.88', 'in New York. Please buy me', + 'two of them.', 'Thanks.'] + + :param blanklines: Indicates how blank lines should be handled. Valid values are: + + - ``discard``: strip blank lines out of the token list before returning it. + A line is considered blank if it contains only whitespace characters. + - ``keep``: leave all blank lines in the token list. + - ``discard-eof``: if the string ends with a newline, then do not generate + a corresponding token ``''`` after that newline. + """ + + def __init__(self, blanklines="discard"): + valid_blanklines = ("discard", "keep", "discard-eof") + if blanklines not in valid_blanklines: + raise ValueError( + "Blank lines must be one of: %s" % " ".join(valid_blanklines) + ) + + self._blanklines = blanklines + + def tokenize(self, s): + lines = s.splitlines() + # If requested, strip off blank lines. + if self._blanklines == "discard": + lines = [l for l in lines if l.rstrip()] + elif self._blanklines == "discard-eof": + if lines and not lines[-1].strip(): + lines.pop() + return lines + + # discard-eof not implemented + def span_tokenize(self, s): + if self._blanklines == "keep": + yield from string_span_tokenize(s, r"\n") + else: + yield from regexp_span_tokenize(s, r"\n(\s+\n)*") + + +###################################################################### +# { Tokenization Functions +###################################################################### +# XXX: it is stated in module docs that there is no function versions + + +def line_tokenize(text, blanklines="discard"): + return LineTokenizer(blanklines).tokenize(text) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sonority_sequencing.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sonority_sequencing.py new file mode 100644 index 0000000..6798ae9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/sonority_sequencing.py @@ -0,0 +1,194 @@ +# Natural Language Toolkit: Tokenizers +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Christopher Hench +# Alex Estes +# URL: +# For license information, see LICENSE.TXT + +""" +The Sonority Sequencing Principle (SSP) is a language agnostic algorithm proposed +by Otto Jesperson in 1904. The sonorous quality of a phoneme is judged by the +openness of the lips. Syllable breaks occur before troughs in sonority. For more +on the SSP see Selkirk (1984). + +The default implementation uses the English alphabet, but the `sonority_hiearchy` +can be modified to IPA or any other alphabet for the use-case. The SSP is a +universal syllabification algorithm, but that does not mean it performs equally +across languages. Bartlett et al. (2009) is a good benchmark for English accuracy +if utilizing IPA (pg. 311). + +Importantly, if a custom hierarchy is supplied and vowels span across more than +one level, they should be given separately to the `vowels` class attribute. + +References: + +- Otto Jespersen. 1904. Lehrbuch der Phonetik. + Leipzig, Teubner. Chapter 13, Silbe, pp. 185-203. +- Elisabeth Selkirk. 1984. On the major class features and syllable theory. + In Aronoff & Oehrle (eds.) Language Sound Structure: Studies in Phonology. + Cambridge, MIT Press. pp. 107-136. +- Susan Bartlett, et al. 2009. On the Syllabification of Phonemes. + In HLT-NAACL. pp. 308-316. +""" + +import re +import warnings +from string import punctuation + +from nltk.tokenize.api import TokenizerI +from nltk.util import ngrams + + +class SyllableTokenizer(TokenizerI): + """ + Syllabifies words based on the Sonority Sequencing Principle (SSP). + + >>> from nltk.tokenize import SyllableTokenizer + >>> from nltk import word_tokenize + >>> SSP = SyllableTokenizer() + >>> SSP.tokenize('justification') + ['jus', 'ti', 'fi', 'ca', 'tion'] + >>> text = "This is a foobar-like sentence." + >>> [SSP.tokenize(token) for token in word_tokenize(text)] + [['This'], ['is'], ['a'], ['foo', 'bar', '-', 'li', 'ke'], ['sen', 'ten', 'ce'], ['.']] + """ + + def __init__(self, lang="en", sonority_hierarchy=False): + """ + :param lang: Language parameter, default is English, 'en' + :type lang: str + :param sonority_hierarchy: Sonority hierarchy according to the + Sonority Sequencing Principle. + :type sonority_hierarchy: list(str) + """ + # Sonority hierarchy should be provided in descending order. + # If vowels are spread across multiple levels, they should be + # passed assigned self.vowels var together, otherwise should be + # placed in first index of hierarchy. + if not sonority_hierarchy and lang == "en": + sonority_hierarchy = [ + "aeiouy", # vowels. + "lmnrw", # nasals. + "zvsf", # fricatives. + "bcdgtkpqxhj", # stops. + ] + + self.vowels = sonority_hierarchy[0] + self.phoneme_map = {} + for i, level in enumerate(sonority_hierarchy): + for c in level: + sonority_level = len(sonority_hierarchy) - i + self.phoneme_map[c] = sonority_level + self.phoneme_map[c.upper()] = sonority_level + + def assign_values(self, token): + """ + Assigns each phoneme its value from the sonority hierarchy. + Note: Sentence/text has to be tokenized first. + + :param token: Single word or token + :type token: str + :return: List of tuples, first element is character/phoneme and + second is the soronity value. + :rtype: list(tuple(str, int)) + """ + syllables_values = [] + for c in token: + try: + syllables_values.append((c, self.phoneme_map[c])) + except KeyError: + if c not in "0123456789" and c not in punctuation: + warnings.warn( + "Character not defined in sonority_hierarchy," + " assigning as vowel: '{}'".format(c) + ) + syllables_values.append((c, max(self.phoneme_map.values()))) + if c not in self.vowels: + self.vowels += c + else: # If it's a punctuation or numbers, assign -1. + syllables_values.append((c, -1)) + return syllables_values + + def validate_syllables(self, syllable_list): + """ + Ensures each syllable has at least one vowel. + If the following syllable doesn't have vowel, add it to the current one. + + :param syllable_list: Single word or token broken up into syllables. + :type syllable_list: list(str) + :return: Single word or token broken up into syllables + (with added syllables if necessary) + :rtype: list(str) + """ + valid_syllables = [] + front = "" + vowel_pattern = re.compile("|".join(self.vowels)) + for i, syllable in enumerate(syllable_list): + if syllable in punctuation: + valid_syllables.append(syllable) + continue + if not vowel_pattern.search(syllable): + if len(valid_syllables) == 0: + front += syllable + else: + valid_syllables = valid_syllables[:-1] + [ + valid_syllables[-1] + syllable + ] + else: + if len(valid_syllables) == 0: + valid_syllables.append(front + syllable) + else: + valid_syllables.append(syllable) + + return valid_syllables + + def tokenize(self, token): + """ + Apply the SSP to return a list of syllables. + Note: Sentence/text has to be tokenized first. + + :param token: Single word or token + :type token: str + :return syllable_list: Single word or token broken up into syllables. + :rtype: list(str) + """ + # assign values from hierarchy + syllables_values = self.assign_values(token) + + # if only one vowel return word + if sum(token.count(x) for x in self.vowels) <= 1: + return [token] + + syllable_list = [] + syllable = syllables_values[0][0] # start syllable with first phoneme + for trigram in ngrams(syllables_values, n=3): + phonemes, values = zip(*trigram) + # Sonority of previous, focal and following phoneme + prev_value, focal_value, next_value = values + # Focal phoneme. + focal_phoneme = phonemes[1] + + # These cases trigger syllable break. + if focal_value == -1: # If it's a punctuation, just break. + syllable_list.append(syllable) + syllable_list.append(focal_phoneme) + syllable = "" + elif prev_value >= focal_value == next_value: + syllable += focal_phoneme + syllable_list.append(syllable) + syllable = "" + + elif prev_value > focal_value < next_value: + syllable_list.append(syllable) + syllable = "" + syllable += focal_phoneme + + # no syllable break + else: + syllable += focal_phoneme + + syllable += syllables_values[-1][0] # append last phoneme + syllable_list.append(syllable) + + return self.validate_syllables(syllable_list) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford.py new file mode 100644 index 0000000..966a4a1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford.py @@ -0,0 +1,115 @@ +# Natural Language Toolkit: Interface to the Stanford Tokenizer +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Xu +# +# URL: +# For license information, see LICENSE.TXT + +import json +import os +import tempfile +import warnings +from subprocess import PIPE + +from nltk.internals import _java_options, config_java, find_jar, java +from nltk.parse.corenlp import CoreNLPParser +from nltk.tokenize.api import TokenizerI + +_stanford_url = "https://nlp.stanford.edu/software/tokenizer.shtml" + + +class StanfordTokenizer(TokenizerI): + r""" + Interface to the Stanford Tokenizer + + >>> from nltk.tokenize.stanford import StanfordTokenizer + >>> s = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks." + >>> StanfordTokenizer().tokenize(s) # doctest: +SKIP + ['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York', '.', 'Please', 'buy', 'me', 'two', 'of', 'them', '.', 'Thanks', '.'] + >>> s = "The colour of the wall is blue." + >>> StanfordTokenizer(options={"americanize": True}).tokenize(s) # doctest: +SKIP + ['The', 'color', 'of', 'the', 'wall', 'is', 'blue', '.'] + """ + + _JAR = "stanford-postagger.jar" + + def __init__( + self, + path_to_jar=None, + encoding="utf8", + options=None, + verbose=False, + java_options="-mx1000m", + ): + # Raise deprecation warning. + warnings.warn( + str( + "\nThe StanfordTokenizer will " + "be deprecated in version 3.2.5.\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPParser\033[0m instead.'" + ), + DeprecationWarning, + stacklevel=2, + ) + + self._stanford_jar = find_jar( + self._JAR, + path_to_jar, + env_vars=("STANFORD_POSTAGGER",), + searchpath=(), + url=_stanford_url, + verbose=verbose, + ) + + self._encoding = encoding + self.java_options = java_options + + options = {} if options is None else options + self._options_cmd = ",".join(f"{key}={val}" for key, val in options.items()) + + @staticmethod + def _parse_tokenized_output(s): + return s.splitlines() + + def tokenize(self, s): + """ + Use stanford tokenizer's PTBTokenizer to tokenize multiple sentences. + """ + cmd = ["edu.stanford.nlp.process.PTBTokenizer"] + return self._parse_tokenized_output(self._execute(cmd, s)) + + def _execute(self, cmd, input_, verbose=False): + encoding = self._encoding + cmd.extend(["-charset", encoding]) + _options_cmd = self._options_cmd + if _options_cmd: + cmd.extend(["-options", self._options_cmd]) + + default_options = " ".join(_java_options) + + # Configure java. + config_java(options=self.java_options, verbose=verbose) + + # Windows is incompatible with NamedTemporaryFile() without passing in delete=False. + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as input_file: + # Write the actual sentences to the temporary input file + if isinstance(input_, str) and encoding: + input_ = input_.encode(encoding) + input_file.write(input_) + input_file.flush() + + cmd.append(input_file.name) + + # Run the tagger and get the output. + stdout, stderr = java( + cmd, classpath=self._stanford_jar, stdout=PIPE, stderr=PIPE + ) + stdout = stdout.decode(encoding) + + os.unlink(input_file.name) + + # Return java configurations to their default values. + config_java(options=default_options, verbose=False) + + return stdout diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford_segmenter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford_segmenter.py new file mode 100644 index 0000000..26451b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/stanford_segmenter.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python +# Natural Language Toolkit: Interface to the Stanford Segmenter +# for Chinese and Arabic +# +# Copyright (C) 2001-2023 NLTK Project +# Author: 52nlp <52nlpcn@gmail.com> +# Casper Lehmann-Strøm +# Alex Constantin +# +# URL: +# For license information, see LICENSE.TXT + +import json +import os +import tempfile +import warnings +from subprocess import PIPE + +from nltk.internals import ( + _java_options, + config_java, + find_dir, + find_file, + find_jar, + java, +) +from nltk.tokenize.api import TokenizerI + +_stanford_url = "https://nlp.stanford.edu/software" + + +class StanfordSegmenter(TokenizerI): + """Interface to the Stanford Segmenter + + If stanford-segmenter version is older than 2016-10-31, then path_to_slf4j + should be provieded, for example:: + + seg = StanfordSegmenter(path_to_slf4j='/YOUR_PATH/slf4j-api.jar') + + >>> from nltk.tokenize.stanford_segmenter import StanfordSegmenter + >>> seg = StanfordSegmenter() # doctest: +SKIP + >>> seg.default_config('zh') # doctest: +SKIP + >>> sent = u'这是斯坦福中文分词器测试' + >>> print(seg.segment(sent)) # doctest: +SKIP + \u8fd9 \u662f \u65af\u5766\u798f \u4e2d\u6587 \u5206\u8bcd\u5668 \u6d4b\u8bd5 + + >>> seg.default_config('ar') # doctest: +SKIP + >>> sent = u'هذا هو تصنيف ستانفورد العربي للكلمات' + >>> print(seg.segment(sent.split())) # doctest: +SKIP + \u0647\u0630\u0627 \u0647\u0648 \u062a\u0635\u0646\u064a\u0641 \u0633\u062a\u0627\u0646\u0641\u0648\u0631\u062f \u0627\u0644\u0639\u0631\u0628\u064a \u0644 \u0627\u0644\u0643\u0644\u0645\u0627\u062a + + """ + + _JAR = "stanford-segmenter.jar" + + def __init__( + self, + path_to_jar=None, + path_to_slf4j=None, + java_class=None, + path_to_model=None, + path_to_dict=None, + path_to_sihan_corpora_dict=None, + sihan_post_processing="false", + keep_whitespaces="false", + encoding="UTF-8", + options=None, + verbose=False, + java_options="-mx2g", + ): + # Raise deprecation warning. + warnings.simplefilter("always", DeprecationWarning) + warnings.warn( + str( + "\nThe StanfordTokenizer will " + "be deprecated in version 3.2.5.\n" + "Please use \033[91mnltk.parse.corenlp.CoreNLPTokenizer\033[0m instead.'" + ), + DeprecationWarning, + stacklevel=2, + ) + warnings.simplefilter("ignore", DeprecationWarning) + + stanford_segmenter = find_jar( + self._JAR, + path_to_jar, + env_vars=("STANFORD_SEGMENTER",), + searchpath=(), + url=_stanford_url, + verbose=verbose, + ) + if path_to_slf4j is not None: + slf4j = find_jar( + "slf4j-api.jar", + path_to_slf4j, + env_vars=("SLF4J", "STANFORD_SEGMENTER"), + searchpath=(), + url=_stanford_url, + verbose=verbose, + ) + else: + slf4j = None + + # This is passed to java as the -cp option, the old version of segmenter needs slf4j. + # The new version of stanford-segmenter-2016-10-31 doesn't need slf4j + self._stanford_jar = os.pathsep.join( + _ for _ in [stanford_segmenter, slf4j] if _ is not None + ) + + self._java_class = java_class + self._model = path_to_model + self._sihan_corpora_dict = path_to_sihan_corpora_dict + self._sihan_post_processing = sihan_post_processing + self._keep_whitespaces = keep_whitespaces + self._dict = path_to_dict + + self._encoding = encoding + self.java_options = java_options + options = {} if options is None else options + self._options_cmd = ",".join( + f"{key}={json.dumps(val)}" for key, val in options.items() + ) + + def default_config(self, lang): + """ + Attempt to initialize Stanford Word Segmenter for the specified language + using the STANFORD_SEGMENTER and STANFORD_MODELS environment variables + """ + + search_path = () + if os.environ.get("STANFORD_SEGMENTER"): + search_path = {os.path.join(os.environ.get("STANFORD_SEGMENTER"), "data")} + + # init for Chinese-specific files + self._dict = None + self._sihan_corpora_dict = None + self._sihan_post_processing = "false" + + if lang == "ar": + self._java_class = ( + "edu.stanford.nlp.international.arabic.process.ArabicSegmenter" + ) + model = "arabic-segmenter-atb+bn+arztrain.ser.gz" + + elif lang == "zh": + self._java_class = "edu.stanford.nlp.ie.crf.CRFClassifier" + model = "pku.gz" + self._sihan_post_processing = "true" + + path_to_dict = "dict-chris6.ser.gz" + try: + self._dict = find_file( + path_to_dict, + searchpath=search_path, + url=_stanford_url, + verbose=False, + env_vars=("STANFORD_MODELS",), + ) + except LookupError as e: + raise LookupError( + "Could not find '%s' (tried using env. " + "variables STANFORD_MODELS and /data/)" + % path_to_dict + ) from e + + sihan_dir = "./data/" + try: + path_to_sihan_dir = find_dir( + sihan_dir, + url=_stanford_url, + verbose=False, + env_vars=("STANFORD_SEGMENTER",), + ) + self._sihan_corpora_dict = os.path.join(path_to_sihan_dir, sihan_dir) + except LookupError as e: + raise LookupError( + "Could not find '%s' (tried using the " + "STANFORD_SEGMENTER environment variable)" % sihan_dir + ) from e + else: + raise LookupError(f"Unsupported language {lang}") + + try: + self._model = find_file( + model, + searchpath=search_path, + url=_stanford_url, + verbose=False, + env_vars=("STANFORD_MODELS", "STANFORD_SEGMENTER"), + ) + except LookupError as e: + raise LookupError( + "Could not find '%s' (tried using env. " + "variables STANFORD_MODELS and /data/)" % model + ) from e + + def tokenize(self, s): + super().tokenize(s) + + def segment_file(self, input_file_path): + """ """ + cmd = [ + self._java_class, + "-loadClassifier", + self._model, + "-keepAllWhitespaces", + self._keep_whitespaces, + "-textFile", + input_file_path, + ] + if self._sihan_corpora_dict is not None: + cmd.extend( + [ + "-serDictionary", + self._dict, + "-sighanCorporaDict", + self._sihan_corpora_dict, + "-sighanPostProcessing", + self._sihan_post_processing, + ] + ) + + stdout = self._execute(cmd) + + return stdout + + def segment(self, tokens): + return self.segment_sents([tokens]) + + def segment_sents(self, sentences): + """ """ + encoding = self._encoding + # Create a temporary input file + _input_fh, self._input_file_path = tempfile.mkstemp(text=True) + + # Write the actural sentences to the temporary input file + _input_fh = os.fdopen(_input_fh, "wb") + _input = "\n".join(" ".join(x) for x in sentences) + if isinstance(_input, str) and encoding: + _input = _input.encode(encoding) + _input_fh.write(_input) + _input_fh.close() + + cmd = [ + self._java_class, + "-loadClassifier", + self._model, + "-keepAllWhitespaces", + self._keep_whitespaces, + "-textFile", + self._input_file_path, + ] + if self._sihan_corpora_dict is not None: + cmd.extend( + [ + "-serDictionary", + self._dict, + "-sighanCorporaDict", + self._sihan_corpora_dict, + "-sighanPostProcessing", + self._sihan_post_processing, + ] + ) + + stdout = self._execute(cmd) + + # Delete the temporary file + os.unlink(self._input_file_path) + + return stdout + + def _execute(self, cmd, verbose=False): + encoding = self._encoding + cmd.extend(["-inputEncoding", encoding]) + _options_cmd = self._options_cmd + if _options_cmd: + cmd.extend(["-options", self._options_cmd]) + + default_options = " ".join(_java_options) + + # Configure java. + config_java(options=self.java_options, verbose=verbose) + + stdout, _stderr = java( + cmd, classpath=self._stanford_jar, stdout=PIPE, stderr=PIPE + ) + stdout = stdout.decode(encoding) + + # Return java configurations to their default values. + config_java(options=default_options, verbose=False) + + return stdout diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/texttiling.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/texttiling.py new file mode 100644 index 0000000..6d78151 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/texttiling.py @@ -0,0 +1,475 @@ +# Natural Language Toolkit: TextTiling +# +# Copyright (C) 2001-2023 NLTK Project +# Author: George Boutsioukis +# +# URL: +# For license information, see LICENSE.TXT + +import math +import re + +try: + import numpy +except ImportError: + pass + +from nltk.tokenize.api import TokenizerI + +BLOCK_COMPARISON, VOCABULARY_INTRODUCTION = 0, 1 +LC, HC = 0, 1 +DEFAULT_SMOOTHING = [0] + + +class TextTilingTokenizer(TokenizerI): + """Tokenize a document into topical sections using the TextTiling algorithm. + This algorithm detects subtopic shifts based on the analysis of lexical + co-occurrence patterns. + + The process starts by tokenizing the text into pseudosentences of + a fixed size w. Then, depending on the method used, similarity + scores are assigned at sentence gaps. The algorithm proceeds by + detecting the peak differences between these scores and marking + them as boundaries. The boundaries are normalized to the closest + paragraph break and the segmented text is returned. + + :param w: Pseudosentence size + :type w: int + :param k: Size (in sentences) of the block used in the block comparison method + :type k: int + :param similarity_method: The method used for determining similarity scores: + `BLOCK_COMPARISON` (default) or `VOCABULARY_INTRODUCTION`. + :type similarity_method: constant + :param stopwords: A list of stopwords that are filtered out (defaults to NLTK's stopwords corpus) + :type stopwords: list(str) + :param smoothing_method: The method used for smoothing the score plot: + `DEFAULT_SMOOTHING` (default) + :type smoothing_method: constant + :param smoothing_width: The width of the window used by the smoothing method + :type smoothing_width: int + :param smoothing_rounds: The number of smoothing passes + :type smoothing_rounds: int + :param cutoff_policy: The policy used to determine the number of boundaries: + `HC` (default) or `LC` + :type cutoff_policy: constant + + >>> from nltk.corpus import brown + >>> tt = TextTilingTokenizer(demo_mode=True) + >>> text = brown.raw()[:4000] + >>> s, ss, d, b = tt.tokenize(text) + >>> b + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0] + """ + + def __init__( + self, + w=20, + k=10, + similarity_method=BLOCK_COMPARISON, + stopwords=None, + smoothing_method=DEFAULT_SMOOTHING, + smoothing_width=2, + smoothing_rounds=1, + cutoff_policy=HC, + demo_mode=False, + ): + + if stopwords is None: + from nltk.corpus import stopwords + + stopwords = stopwords.words("english") + self.__dict__.update(locals()) + del self.__dict__["self"] + + def tokenize(self, text): + """Return a tokenized copy of *text*, where each "token" represents + a separate topic.""" + + lowercase_text = text.lower() + paragraph_breaks = self._mark_paragraph_breaks(text) + text_length = len(lowercase_text) + + # Tokenization step starts here + + # Remove punctuation + nopunct_text = "".join( + c for c in lowercase_text if re.match(r"[a-z\-' \n\t]", c) + ) + nopunct_par_breaks = self._mark_paragraph_breaks(nopunct_text) + + tokseqs = self._divide_to_tokensequences(nopunct_text) + + # The morphological stemming step mentioned in the TextTile + # paper is not implemented. A comment in the original C + # implementation states that it offers no benefit to the + # process. It might be interesting to test the existing + # stemmers though. + # words = _stem_words(words) + + # Filter stopwords + for ts in tokseqs: + ts.wrdindex_list = [ + wi for wi in ts.wrdindex_list if wi[0] not in self.stopwords + ] + + token_table = self._create_token_table(tokseqs, nopunct_par_breaks) + # End of the Tokenization step + + # Lexical score determination + if self.similarity_method == BLOCK_COMPARISON: + gap_scores = self._block_comparison(tokseqs, token_table) + elif self.similarity_method == VOCABULARY_INTRODUCTION: + raise NotImplementedError("Vocabulary introduction not implemented") + else: + raise ValueError( + f"Similarity method {self.similarity_method} not recognized" + ) + + if self.smoothing_method == DEFAULT_SMOOTHING: + smooth_scores = self._smooth_scores(gap_scores) + else: + raise ValueError(f"Smoothing method {self.smoothing_method} not recognized") + # End of Lexical score Determination + + # Boundary identification + depth_scores = self._depth_scores(smooth_scores) + segment_boundaries = self._identify_boundaries(depth_scores) + + normalized_boundaries = self._normalize_boundaries( + text, segment_boundaries, paragraph_breaks + ) + # End of Boundary Identification + segmented_text = [] + prevb = 0 + + for b in normalized_boundaries: + if b == 0: + continue + segmented_text.append(text[prevb:b]) + prevb = b + + if prevb < text_length: # append any text that may be remaining + segmented_text.append(text[prevb:]) + + if not segmented_text: + segmented_text = [text] + + if self.demo_mode: + return gap_scores, smooth_scores, depth_scores, segment_boundaries + return segmented_text + + def _block_comparison(self, tokseqs, token_table): + """Implements the block comparison method""" + + def blk_frq(tok, block): + ts_occs = filter(lambda o: o[0] in block, token_table[tok].ts_occurences) + freq = sum(tsocc[1] for tsocc in ts_occs) + return freq + + gap_scores = [] + numgaps = len(tokseqs) - 1 + + for curr_gap in range(numgaps): + score_dividend, score_divisor_b1, score_divisor_b2 = 0.0, 0.0, 0.0 + score = 0.0 + # adjust window size for boundary conditions + if curr_gap < self.k - 1: + window_size = curr_gap + 1 + elif curr_gap > numgaps - self.k: + window_size = numgaps - curr_gap + else: + window_size = self.k + + b1 = [ts.index for ts in tokseqs[curr_gap - window_size + 1 : curr_gap + 1]] + b2 = [ts.index for ts in tokseqs[curr_gap + 1 : curr_gap + window_size + 1]] + + for t in token_table: + score_dividend += blk_frq(t, b1) * blk_frq(t, b2) + score_divisor_b1 += blk_frq(t, b1) ** 2 + score_divisor_b2 += blk_frq(t, b2) ** 2 + try: + score = score_dividend / math.sqrt(score_divisor_b1 * score_divisor_b2) + except ZeroDivisionError: + pass # score += 0.0 + + gap_scores.append(score) + + return gap_scores + + def _smooth_scores(self, gap_scores): + "Wraps the smooth function from the SciPy Cookbook" + return list( + smooth(numpy.array(gap_scores[:]), window_len=self.smoothing_width + 1) + ) + + def _mark_paragraph_breaks(self, text): + """Identifies indented text or line breaks as the beginning of + paragraphs""" + MIN_PARAGRAPH = 100 + pattern = re.compile("[ \t\r\f\v]*\n[ \t\r\f\v]*\n[ \t\r\f\v]*") + matches = pattern.finditer(text) + + last_break = 0 + pbreaks = [0] + for pb in matches: + if pb.start() - last_break < MIN_PARAGRAPH: + continue + else: + pbreaks.append(pb.start()) + last_break = pb.start() + + return pbreaks + + def _divide_to_tokensequences(self, text): + "Divides the text into pseudosentences of fixed size" + w = self.w + wrdindex_list = [] + matches = re.finditer(r"\w+", text) + for match in matches: + wrdindex_list.append((match.group(), match.start())) + return [ + TokenSequence(i / w, wrdindex_list[i : i + w]) + for i in range(0, len(wrdindex_list), w) + ] + + def _create_token_table(self, token_sequences, par_breaks): + "Creates a table of TokenTableFields" + token_table = {} + current_par = 0 + current_tok_seq = 0 + pb_iter = par_breaks.__iter__() + current_par_break = next(pb_iter) + if current_par_break == 0: + try: + current_par_break = next(pb_iter) # skip break at 0 + except StopIteration as e: + raise ValueError( + "No paragraph breaks were found(text too short perhaps?)" + ) from e + for ts in token_sequences: + for word, index in ts.wrdindex_list: + try: + while index > current_par_break: + current_par_break = next(pb_iter) + current_par += 1 + except StopIteration: + # hit bottom + pass + + if word in token_table: + token_table[word].total_count += 1 + + if token_table[word].last_par != current_par: + token_table[word].last_par = current_par + token_table[word].par_count += 1 + + if token_table[word].last_tok_seq != current_tok_seq: + token_table[word].last_tok_seq = current_tok_seq + token_table[word].ts_occurences.append([current_tok_seq, 1]) + else: + token_table[word].ts_occurences[-1][1] += 1 + else: # new word + token_table[word] = TokenTableField( + first_pos=index, + ts_occurences=[[current_tok_seq, 1]], + total_count=1, + par_count=1, + last_par=current_par, + last_tok_seq=current_tok_seq, + ) + + current_tok_seq += 1 + + return token_table + + def _identify_boundaries(self, depth_scores): + """Identifies boundaries at the peaks of similarity score + differences""" + + boundaries = [0 for x in depth_scores] + + avg = sum(depth_scores) / len(depth_scores) + stdev = numpy.std(depth_scores) + + if self.cutoff_policy == LC: + cutoff = avg - stdev + else: + cutoff = avg - stdev / 2.0 + + depth_tuples = sorted(zip(depth_scores, range(len(depth_scores)))) + depth_tuples.reverse() + hp = list(filter(lambda x: x[0] > cutoff, depth_tuples)) + + for dt in hp: + boundaries[dt[1]] = 1 + for dt2 in hp: # undo if there is a boundary close already + if ( + dt[1] != dt2[1] + and abs(dt2[1] - dt[1]) < 4 + and boundaries[dt2[1]] == 1 + ): + boundaries[dt[1]] = 0 + return boundaries + + def _depth_scores(self, scores): + """Calculates the depth of each gap, i.e. the average difference + between the left and right peaks and the gap's score""" + + depth_scores = [0 for x in scores] + # clip boundaries: this holds on the rule of thumb(my thumb) + # that a section shouldn't be smaller than at least 2 + # pseudosentences for small texts and around 5 for larger ones. + + clip = min(max(len(scores) // 10, 2), 5) + index = clip + + for gapscore in scores[clip:-clip]: + lpeak = gapscore + for score in scores[index::-1]: + if score >= lpeak: + lpeak = score + else: + break + rpeak = gapscore + for score in scores[index:]: + if score >= rpeak: + rpeak = score + else: + break + depth_scores[index] = lpeak + rpeak - 2 * gapscore + index += 1 + + return depth_scores + + def _normalize_boundaries(self, text, boundaries, paragraph_breaks): + """Normalize the boundaries identified to the original text's + paragraph breaks""" + + norm_boundaries = [] + char_count, word_count, gaps_seen = 0, 0, 0 + seen_word = False + + for char in text: + char_count += 1 + if char in " \t\n" and seen_word: + seen_word = False + word_count += 1 + if char not in " \t\n" and not seen_word: + seen_word = True + if gaps_seen < len(boundaries) and word_count > ( + max(gaps_seen * self.w, self.w) + ): + if boundaries[gaps_seen] == 1: + # find closest paragraph break + best_fit = len(text) + for br in paragraph_breaks: + if best_fit > abs(br - char_count): + best_fit = abs(br - char_count) + bestbr = br + else: + break + if bestbr not in norm_boundaries: # avoid duplicates + norm_boundaries.append(bestbr) + gaps_seen += 1 + + return norm_boundaries + + +class TokenTableField: + """A field in the token table holding parameters for each token, + used later in the process""" + + def __init__( + self, + first_pos, + ts_occurences, + total_count=1, + par_count=1, + last_par=0, + last_tok_seq=None, + ): + self.__dict__.update(locals()) + del self.__dict__["self"] + + +class TokenSequence: + "A token list with its original length and its index" + + def __init__(self, index, wrdindex_list, original_length=None): + original_length = original_length or len(wrdindex_list) + self.__dict__.update(locals()) + del self.__dict__["self"] + + +# Pasted from the SciPy cookbook: https://www.scipy.org/Cookbook/SignalSmooth +def smooth(x, window_len=11, window="flat"): + """smooth the data using a window with requested size. + + This method is based on the convolution of a scaled window with the signal. + The signal is prepared by introducing reflected copies of the signal + (with the window size) in both ends so that transient parts are minimized + in the beginning and end part of the output signal. + + :param x: the input signal + :param window_len: the dimension of the smoothing window; should be an odd integer + :param window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' + flat window will produce a moving average smoothing. + + :return: the smoothed signal + + example:: + + t=linspace(-2,2,0.1) + x=sin(t)+randn(len(t))*0.1 + y=smooth(x) + + :see also: numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve, + scipy.signal.lfilter + + TODO: the window parameter could be the window itself if an array instead of a string + """ + + if x.ndim != 1: + raise ValueError("smooth only accepts 1 dimension arrays.") + + if x.size < window_len: + raise ValueError("Input vector needs to be bigger than window size.") + + if window_len < 3: + return x + + if window not in ["flat", "hanning", "hamming", "bartlett", "blackman"]: + raise ValueError( + "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'" + ) + + s = numpy.r_[2 * x[0] - x[window_len:1:-1], x, 2 * x[-1] - x[-1:-window_len:-1]] + + # print(len(s)) + if window == "flat": # moving average + w = numpy.ones(window_len, "d") + else: + w = eval("numpy." + window + "(window_len)") + + y = numpy.convolve(w / w.sum(), s, mode="same") + + return y[window_len - 1 : -window_len + 1] + + +def demo(text=None): + from matplotlib import pylab + + from nltk.corpus import brown + + tt = TextTilingTokenizer(demo_mode=True) + if text is None: + text = brown.raw()[:10000] + s, ss, d, b = tt.tokenize(text) + pylab.xlabel("Sentence Gap index") + pylab.ylabel("Gap Scores") + pylab.plot(range(len(s)), s, label="Gap Scores") + pylab.plot(range(len(ss)), ss, label="Smoothed Gap scores") + pylab.plot(range(len(d)), d, label="Depth scores") + pylab.stem(range(len(b)), b) + pylab.legend() + pylab.show() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/toktok.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/toktok.py new file mode 100644 index 0000000..9233ac5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/toktok.py @@ -0,0 +1,179 @@ +# Natural Language Toolkit: Python port of the tok-tok.pl tokenizer. +# +# Copyright (C) 2001-2015 NLTK Project +# Author: Jon Dehdari +# Contributors: Liling Tan, Selcuk Ayguney, ikegami, Martijn Pieters +# +# URL: +# For license information, see LICENSE.TXT + +""" +The tok-tok tokenizer is a simple, general tokenizer, where the input has one +sentence per line; thus only final period is tokenized. + +Tok-tok has been tested on, and gives reasonably good results for English, +Persian, Russian, Czech, French, German, Vietnamese, Tajik, and a few others. +The input should be in UTF-8 encoding. + +Reference: +Jon Dehdari. 2014. A Neurophysiologically-Inspired Statistical Language +Model (Doctoral dissertation). Columbus, OH, USA: The Ohio State University. +""" + +import re + +from nltk.tokenize.api import TokenizerI + + +class ToktokTokenizer(TokenizerI): + """ + This is a Python port of the tok-tok.pl from + https://github.com/jonsafari/tok-tok/blob/master/tok-tok.pl + + >>> toktok = ToktokTokenizer() + >>> text = u'Is 9.5 or 525,600 my favorite number?' + >>> print(toktok.tokenize(text, return_str=True)) + Is 9.5 or 525,600 my favorite number ? + >>> text = u'The https://github.com/jonsafari/tok-tok/blob/master/tok-tok.pl is a website with/and/or slashes and sort of weird : things' + >>> print(toktok.tokenize(text, return_str=True)) + The https://github.com/jonsafari/tok-tok/blob/master/tok-tok.pl is a website with/and/or slashes and sort of weird : things + >>> text = u'\xa1This, is a sentence with weird\xbb symbols\u2026 appearing everywhere\xbf' + >>> expected = u'\xa1 This , is a sentence with weird \xbb symbols \u2026 appearing everywhere \xbf' + >>> assert toktok.tokenize(text, return_str=True) == expected + >>> toktok.tokenize(text) == [u'\xa1', u'This', u',', u'is', u'a', u'sentence', u'with', u'weird', u'\xbb', u'symbols', u'\u2026', u'appearing', u'everywhere', u'\xbf'] + True + """ + + # Replace non-breaking spaces with normal spaces. + NON_BREAKING = re.compile("\u00A0"), " " + + # Pad some funky punctuation. + FUNKY_PUNCT_1 = re.compile(r'([،;؛¿!"\])}»›”؟¡%٪°±©®।॥…])'), r" \1 " + # Pad more funky punctuation. + FUNKY_PUNCT_2 = re.compile(r"([({\[“‘„‚«‹「『])"), r" \1 " + # Pad En dash and em dash + EN_EM_DASHES = re.compile("([–—])"), r" \1 " + + # Replace problematic character with numeric character reference. + AMPERCENT = re.compile("& "), "& " + TAB = re.compile("\t"), " " + PIPE = re.compile(r"\|"), " | " + + # Pad numbers with commas to keep them from further tokenization. + COMMA_IN_NUM = re.compile(r"(? "something ..." + # "something." -> "something ." + FINAL_PERIOD_1 = re.compile(r"(? "... stuff ." + FINAL_PERIOD_2 = re.compile(r"""(? +# Michael Heilman (re-port from http://www.cis.upenn.edu/~treebank/tokenizer.sed) +# Tom Aarsen <> (modifications) +# +# URL: +# For license information, see LICENSE.TXT + +r""" + +Penn Treebank Tokenizer + +The Treebank tokenizer uses regular expressions to tokenize text as in Penn Treebank. +This implementation is a port of the tokenizer sed script written by Robert McIntyre +and available at http://www.cis.upenn.edu/~treebank/tokenizer.sed. +""" + +import re +import warnings +from typing import Iterator, List, Tuple + +from nltk.tokenize.api import TokenizerI +from nltk.tokenize.destructive import MacIntyreContractions +from nltk.tokenize.util import align_tokens + + +class TreebankWordTokenizer(TokenizerI): + r""" + The Treebank tokenizer uses regular expressions to tokenize text as in Penn Treebank. + + This tokenizer performs the following steps: + + - split standard contractions, e.g. ``don't`` -> ``do n't`` and ``they'll`` -> ``they 'll`` + - treat most punctuation characters as separate tokens + - split off commas and single quotes, when followed by whitespace + - separate periods that appear at the end of line + + >>> from nltk.tokenize import TreebankWordTokenizer + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks.''' + >>> TreebankWordTokenizer().tokenize(s) + ['Good', 'muffins', 'cost', '$', '3.88', 'in', 'New', 'York.', 'Please', 'buy', 'me', 'two', 'of', 'them.', 'Thanks', '.'] + >>> s = "They'll save and invest more." + >>> TreebankWordTokenizer().tokenize(s) + ['They', "'ll", 'save', 'and', 'invest', 'more', '.'] + >>> s = "hi, my name can't hello," + >>> TreebankWordTokenizer().tokenize(s) + ['hi', ',', 'my', 'name', 'ca', "n't", 'hello', ','] + """ + + # starting quotes + STARTING_QUOTES = [ + (re.compile(r"^\""), r"``"), + (re.compile(r"(``)"), r" \1 "), + (re.compile(r"([ \(\[{<])(\"|\'{2})"), r"\1 `` "), + ] + + # punctuation + PUNCTUATION = [ + (re.compile(r"([:,])([^\d])"), r" \1 \2"), + (re.compile(r"([:,])$"), r" \1 "), + (re.compile(r"\.\.\."), r" ... "), + (re.compile(r"[;@#$%&]"), r" \g<0> "), + ( + re.compile(r'([^\.])(\.)([\]\)}>"\']*)\s*$'), + r"\1 \2\3 ", + ), # Handles the final period. + (re.compile(r"[?!]"), r" \g<0> "), + (re.compile(r"([^'])' "), r"\1 ' "), + ] + + # Pads parentheses + PARENS_BRACKETS = (re.compile(r"[\]\[\(\)\{\}\<\>]"), r" \g<0> ") + + # Optionally: Convert parentheses, brackets and converts them to PTB symbols. + CONVERT_PARENTHESES = [ + (re.compile(r"\("), "-LRB-"), + (re.compile(r"\)"), "-RRB-"), + (re.compile(r"\["), "-LSB-"), + (re.compile(r"\]"), "-RSB-"), + (re.compile(r"\{"), "-LCB-"), + (re.compile(r"\}"), "-RCB-"), + ] + + DOUBLE_DASHES = (re.compile(r"--"), r" -- ") + + # ending quotes + ENDING_QUOTES = [ + (re.compile(r"''"), " '' "), + (re.compile(r'"'), " '' "), + (re.compile(r"([^' ])('[sS]|'[mM]|'[dD]|') "), r"\1 \2 "), + (re.compile(r"([^' ])('ll|'LL|'re|'RE|'ve|'VE|n't|N'T) "), r"\1 \2 "), + ] + + # List of contractions adapted from Robert MacIntyre's tokenizer. + _contractions = MacIntyreContractions() + CONTRACTIONS2 = list(map(re.compile, _contractions.CONTRACTIONS2)) + CONTRACTIONS3 = list(map(re.compile, _contractions.CONTRACTIONS3)) + + def tokenize( + self, text: str, convert_parentheses: bool = False, return_str: bool = False + ) -> List[str]: + r"""Return a tokenized copy of `text`. + + >>> from nltk.tokenize import TreebankWordTokenizer + >>> s = '''Good muffins cost $3.88 (roughly 3,36 euros)\nin New York. Please buy me\ntwo of them.\nThanks.''' + >>> TreebankWordTokenizer().tokenize(s) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3.88', '(', 'roughly', '3,36', + 'euros', ')', 'in', 'New', 'York.', 'Please', 'buy', 'me', 'two', + 'of', 'them.', 'Thanks', '.'] + >>> TreebankWordTokenizer().tokenize(s, convert_parentheses=True) # doctest: +NORMALIZE_WHITESPACE + ['Good', 'muffins', 'cost', '$', '3.88', '-LRB-', 'roughly', '3,36', + 'euros', '-RRB-', 'in', 'New', 'York.', 'Please', 'buy', 'me', 'two', + 'of', 'them.', 'Thanks', '.'] + + :param text: A string with a sentence or sentences. + :type text: str + :param convert_parentheses: if True, replace parentheses to PTB symbols, + e.g. `(` to `-LRB-`. Defaults to False. + :type convert_parentheses: bool, optional + :param return_str: If True, return tokens as space-separated string, + defaults to False. + :type return_str: bool, optional + :return: List of tokens from `text`. + :rtype: List[str] + """ + if return_str is not False: + warnings.warn( + "Parameter 'return_str' has been deprecated and should no " + "longer be used.", + category=DeprecationWarning, + stacklevel=2, + ) + + for regexp, substitution in self.STARTING_QUOTES: + text = regexp.sub(substitution, text) + + for regexp, substitution in self.PUNCTUATION: + text = regexp.sub(substitution, text) + + # Handles parentheses. + regexp, substitution = self.PARENS_BRACKETS + text = regexp.sub(substitution, text) + # Optionally convert parentheses + if convert_parentheses: + for regexp, substitution in self.CONVERT_PARENTHESES: + text = regexp.sub(substitution, text) + + # Handles double dash. + regexp, substitution = self.DOUBLE_DASHES + text = regexp.sub(substitution, text) + + # add extra space to make things easier + text = " " + text + " " + + for regexp, substitution in self.ENDING_QUOTES: + text = regexp.sub(substitution, text) + + for regexp in self.CONTRACTIONS2: + text = regexp.sub(r" \1 \2 ", text) + for regexp in self.CONTRACTIONS3: + text = regexp.sub(r" \1 \2 ", text) + + # We are not using CONTRACTIONS4 since + # they are also commented out in the SED scripts + # for regexp in self._contractions.CONTRACTIONS4: + # text = regexp.sub(r' \1 \2 \3 ', text) + + return text.split() + + def span_tokenize(self, text: str) -> Iterator[Tuple[int, int]]: + r""" + Returns the spans of the tokens in ``text``. + Uses the post-hoc nltk.tokens.align_tokens to return the offset spans. + + >>> from nltk.tokenize import TreebankWordTokenizer + >>> s = '''Good muffins cost $3.88\nin New (York). Please (buy) me\ntwo of them.\n(Thanks).''' + >>> expected = [(0, 4), (5, 12), (13, 17), (18, 19), (19, 23), + ... (24, 26), (27, 30), (31, 32), (32, 36), (36, 37), (37, 38), + ... (40, 46), (47, 48), (48, 51), (51, 52), (53, 55), (56, 59), + ... (60, 62), (63, 68), (69, 70), (70, 76), (76, 77), (77, 78)] + >>> list(TreebankWordTokenizer().span_tokenize(s)) == expected + True + >>> expected = ['Good', 'muffins', 'cost', '$', '3.88', 'in', + ... 'New', '(', 'York', ')', '.', 'Please', '(', 'buy', ')', + ... 'me', 'two', 'of', 'them.', '(', 'Thanks', ')', '.'] + >>> [s[start:end] for start, end in TreebankWordTokenizer().span_tokenize(s)] == expected + True + + :param text: A string with a sentence or sentences. + :type text: str + :yield: Tuple[int, int] + """ + raw_tokens = self.tokenize(text) + + # Convert converted quotes back to original double quotes + # Do this only if original text contains double quote(s) or double + # single-quotes (because '' might be transformed to `` if it is + # treated as starting quotes). + if ('"' in text) or ("''" in text): + # Find double quotes and converted quotes + matched = [m.group() for m in re.finditer(r"``|'{2}|\"", text)] + + # Replace converted quotes back to double quotes + tokens = [ + matched.pop(0) if tok in ['"', "``", "''"] else tok + for tok in raw_tokens + ] + else: + tokens = raw_tokens + + yield from align_tokens(tokens, text) + + +class TreebankWordDetokenizer(TokenizerI): + r""" + The Treebank detokenizer uses the reverse regex operations corresponding to + the Treebank tokenizer's regexes. + + Note: + + - There're additional assumption mades when undoing the padding of ``[;@#$%&]`` + punctuation symbols that isn't presupposed in the TreebankTokenizer. + - There're additional regexes added in reversing the parentheses tokenization, + such as the ``r'([\]\)\}\>])\s([:;,.])'``, which removes the additional right + padding added to the closing parentheses precedding ``[:;,.]``. + - It's not possible to return the original whitespaces as they were because + there wasn't explicit records of where `'\n'`, `'\t'` or `'\s'` were removed at + the text.split() operation. + + >>> from nltk.tokenize.treebank import TreebankWordTokenizer, TreebankWordDetokenizer + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks.''' + >>> d = TreebankWordDetokenizer() + >>> t = TreebankWordTokenizer() + >>> toks = t.tokenize(s) + >>> d.detokenize(toks) + 'Good muffins cost $3.88 in New York. Please buy me two of them. Thanks.' + + The MXPOST parentheses substitution can be undone using the ``convert_parentheses`` + parameter: + + >>> s = '''Good muffins cost $3.88\nin New (York). Please (buy) me\ntwo of them.\n(Thanks).''' + >>> expected_tokens = ['Good', 'muffins', 'cost', '$', '3.88', 'in', + ... 'New', '-LRB-', 'York', '-RRB-', '.', 'Please', '-LRB-', 'buy', + ... '-RRB-', 'me', 'two', 'of', 'them.', '-LRB-', 'Thanks', '-RRB-', '.'] + >>> expected_tokens == t.tokenize(s, convert_parentheses=True) + True + >>> expected_detoken = 'Good muffins cost $3.88 in New (York). Please (buy) me two of them. (Thanks).' + >>> expected_detoken == d.detokenize(t.tokenize(s, convert_parentheses=True), convert_parentheses=True) + True + + During tokenization it's safe to add more spaces but during detokenization, + simply undoing the padding doesn't really help. + + - During tokenization, left and right pad is added to ``[!?]``, when + detokenizing, only left shift the ``[!?]`` is needed. + Thus ``(re.compile(r'\s([?!])'), r'\g<1>')``. + + - During tokenization ``[:,]`` are left and right padded but when detokenizing, + only left shift is necessary and we keep right pad after comma/colon + if the string after is a non-digit. + Thus ``(re.compile(r'\s([:,])\s([^\d])'), r'\1 \2')``. + + >>> from nltk.tokenize.treebank import TreebankWordDetokenizer + >>> toks = ['hello', ',', 'i', 'ca', "n't", 'feel', 'my', 'feet', '!', 'Help', '!', '!'] + >>> twd = TreebankWordDetokenizer() + >>> twd.detokenize(toks) + "hello, i can't feel my feet! Help!!" + + >>> toks = ['hello', ',', 'i', "can't", 'feel', ';', 'my', 'feet', '!', + ... 'Help', '!', '!', 'He', 'said', ':', 'Help', ',', 'help', '?', '!'] + >>> twd.detokenize(toks) + "hello, i can't feel; my feet! Help!! He said: Help, help?!" + """ + + _contractions = MacIntyreContractions() + CONTRACTIONS2 = [ + re.compile(pattern.replace("(?#X)", r"\s")) + for pattern in _contractions.CONTRACTIONS2 + ] + CONTRACTIONS3 = [ + re.compile(pattern.replace("(?#X)", r"\s")) + for pattern in _contractions.CONTRACTIONS3 + ] + + # ending quotes + ENDING_QUOTES = [ + (re.compile(r"([^' ])\s('ll|'LL|'re|'RE|'ve|'VE|n't|N'T) "), r"\1\2 "), + (re.compile(r"([^' ])\s('[sS]|'[mM]|'[dD]|') "), r"\1\2 "), + (re.compile(r"(\S)\s(\'\')"), r"\1\2"), + ( + re.compile(r"(\'\')\s([.,:)\]>};%])"), + r"\1\2", + ), # Quotes followed by no-left-padded punctuations. + (re.compile(r"''"), '"'), + ] + + # Handles double dashes + DOUBLE_DASHES = (re.compile(r" -- "), r"--") + + # Optionally: Convert parentheses, brackets and converts them from PTB symbols. + CONVERT_PARENTHESES = [ + (re.compile("-LRB-"), "("), + (re.compile("-RRB-"), ")"), + (re.compile("-LSB-"), "["), + (re.compile("-RSB-"), "]"), + (re.compile("-LCB-"), "{"), + (re.compile("-RCB-"), "}"), + ] + + # Undo padding on parentheses. + PARENS_BRACKETS = [ + (re.compile(r"([\[\(\{\<])\s"), r"\g<1>"), + (re.compile(r"\s([\]\)\}\>])"), r"\g<1>"), + (re.compile(r"([\]\)\}\>])\s([:;,.])"), r"\1\2"), + ] + + # punctuation + PUNCTUATION = [ + (re.compile(r"([^'])\s'\s"), r"\1' "), + (re.compile(r"\s([?!])"), r"\g<1>"), # Strip left pad for [?!] + # (re.compile(r'\s([?!])\s'), r'\g<1>'), + (re.compile(r'([^\.])\s(\.)([\]\)}>"\']*)\s*$'), r"\1\2\3"), + # When tokenizing, [;@#$%&] are padded with whitespace regardless of + # whether there are spaces before or after them. + # But during detokenization, we need to distinguish between left/right + # pad, so we split this up. + (re.compile(r"([#$])\s"), r"\g<1>"), # Left pad. + (re.compile(r"\s([;%])"), r"\g<1>"), # Right pad. + # (re.compile(r"\s([&*])\s"), r" \g<1> "), # Unknown pad. + (re.compile(r"\s\.\.\.\s"), r"..."), + # (re.compile(r"\s([:,])\s$"), r"\1"), # .strip() takes care of it. + ( + re.compile(r"\s([:,])"), + r"\1", + ), # Just remove left padding. Punctuation in numbers won't be padded. + ] + + # starting quotes + STARTING_QUOTES = [ + (re.compile(r"([ (\[{<])\s``"), r"\1``"), + (re.compile(r"(``)\s"), r"\1"), + (re.compile(r"``"), r'"'), + ] + + def tokenize(self, tokens: List[str], convert_parentheses: bool = False) -> str: + """ + Treebank detokenizer, created by undoing the regexes from + the TreebankWordTokenizer.tokenize. + + :param tokens: A list of strings, i.e. tokenized text. + :type tokens: List[str] + :param convert_parentheses: if True, replace PTB symbols with parentheses, + e.g. `-LRB-` to `(`. Defaults to False. + :type convert_parentheses: bool, optional + :return: str + """ + text = " ".join(tokens) + + # Add extra space to make things easier + text = " " + text + " " + + # Reverse the contractions regexes. + # Note: CONTRACTIONS4 are not used in tokenization. + for regexp in self.CONTRACTIONS3: + text = regexp.sub(r"\1\2", text) + for regexp in self.CONTRACTIONS2: + text = regexp.sub(r"\1\2", text) + + # Reverse the regexes applied for ending quotes. + for regexp, substitution in self.ENDING_QUOTES: + text = regexp.sub(substitution, text) + + # Undo the space padding. + text = text.strip() + + # Reverse the padding on double dashes. + regexp, substitution = self.DOUBLE_DASHES + text = regexp.sub(substitution, text) + + if convert_parentheses: + for regexp, substitution in self.CONVERT_PARENTHESES: + text = regexp.sub(substitution, text) + + # Reverse the padding regexes applied for parenthesis/brackets. + for regexp, substitution in self.PARENS_BRACKETS: + text = regexp.sub(substitution, text) + + # Reverse the regexes applied for punctuations. + for regexp, substitution in self.PUNCTUATION: + text = regexp.sub(substitution, text) + + # Reverse the regexes applied for starting quotes. + for regexp, substitution in self.STARTING_QUOTES: + text = regexp.sub(substitution, text) + + return text.strip() + + def detokenize(self, tokens: List[str], convert_parentheses: bool = False) -> str: + """Duck-typing the abstract *tokenize()*.""" + return self.tokenize(tokens, convert_parentheses) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/util.py new file mode 100644 index 0000000..6c0966a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tokenize/util.py @@ -0,0 +1,295 @@ +# Natural Language Toolkit: Tokenizer Utilities +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# URL: +# For license information, see LICENSE.TXT + +from re import finditer +from xml.sax.saxutils import escape, unescape + + +def string_span_tokenize(s, sep): + r""" + Return the offsets of the tokens in *s*, as a sequence of ``(start, end)`` + tuples, by splitting the string at each occurrence of *sep*. + + >>> from nltk.tokenize.util import string_span_tokenize + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me + ... two of them.\n\nThanks.''' + >>> list(string_span_tokenize(s, " ")) # doctest: +NORMALIZE_WHITESPACE + [(0, 4), (5, 12), (13, 17), (18, 26), (27, 30), (31, 36), (37, 37), + (38, 44), (45, 48), (49, 55), (56, 58), (59, 73)] + + :param s: the string to be tokenized + :type s: str + :param sep: the token separator + :type sep: str + :rtype: iter(tuple(int, int)) + """ + if len(sep) == 0: + raise ValueError("Token delimiter must not be empty") + left = 0 + while True: + try: + right = s.index(sep, left) + if right != 0: + yield left, right + except ValueError: + if left != len(s): + yield left, len(s) + break + + left = right + len(sep) + + +def regexp_span_tokenize(s, regexp): + r""" + Return the offsets of the tokens in *s*, as a sequence of ``(start, end)`` + tuples, by splitting the string at each successive match of *regexp*. + + >>> from nltk.tokenize.util import regexp_span_tokenize + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me + ... two of them.\n\nThanks.''' + >>> list(regexp_span_tokenize(s, r'\s')) # doctest: +NORMALIZE_WHITESPACE + [(0, 4), (5, 12), (13, 17), (18, 23), (24, 26), (27, 30), (31, 36), + (38, 44), (45, 48), (49, 51), (52, 55), (56, 58), (59, 64), (66, 73)] + + :param s: the string to be tokenized + :type s: str + :param regexp: regular expression that matches token separators (must not be empty) + :type regexp: str + :rtype: iter(tuple(int, int)) + """ + left = 0 + for m in finditer(regexp, s): + right, next = m.span() + if right != left: + yield left, right + left = next + yield left, len(s) + + +def spans_to_relative(spans): + r""" + Return a sequence of relative spans, given a sequence of spans. + + >>> from nltk.tokenize import WhitespaceTokenizer + >>> from nltk.tokenize.util import spans_to_relative + >>> s = '''Good muffins cost $3.88\nin New York. Please buy me + ... two of them.\n\nThanks.''' + >>> list(spans_to_relative(WhitespaceTokenizer().span_tokenize(s))) # doctest: +NORMALIZE_WHITESPACE + [(0, 4), (1, 7), (1, 4), (1, 5), (1, 2), (1, 3), (1, 5), (2, 6), + (1, 3), (1, 2), (1, 3), (1, 2), (1, 5), (2, 7)] + + :param spans: a sequence of (start, end) offsets of the tokens + :type spans: iter(tuple(int, int)) + :rtype: iter(tuple(int, int)) + """ + prev = 0 + for left, right in spans: + yield left - prev, right - left + prev = right + + +class CJKChars: + """ + An object that enumerates the code points of the CJK characters as listed on + https://en.wikipedia.org/wiki/Basic_Multilingual_Plane#Basic_Multilingual_Plane + + This is a Python port of the CJK code point enumerations of Moses tokenizer: + https://github.com/moses-smt/mosesdecoder/blob/master/scripts/tokenizer/detokenizer.perl#L309 + """ + + # Hangul Jamo (1100–11FF) + Hangul_Jamo = (4352, 4607) # (ord(u"\u1100"), ord(u"\u11ff")) + + # CJK Radicals Supplement (2E80–2EFF) + # Kangxi Radicals (2F00–2FDF) + # Ideographic Description Characters (2FF0–2FFF) + # CJK Symbols and Punctuation (3000–303F) + # Hiragana (3040–309F) + # Katakana (30A0–30FF) + # Bopomofo (3100–312F) + # Hangul Compatibility Jamo (3130–318F) + # Kanbun (3190–319F) + # Bopomofo Extended (31A0–31BF) + # CJK Strokes (31C0–31EF) + # Katakana Phonetic Extensions (31F0–31FF) + # Enclosed CJK Letters and Months (3200–32FF) + # CJK Compatibility (3300–33FF) + # CJK Unified Ideographs Extension A (3400–4DBF) + # Yijing Hexagram Symbols (4DC0–4DFF) + # CJK Unified Ideographs (4E00–9FFF) + # Yi Syllables (A000–A48F) + # Yi Radicals (A490–A4CF) + CJK_Radicals = (11904, 42191) # (ord(u"\u2e80"), ord(u"\ua4cf")) + + # Phags-pa (A840–A87F) + Phags_Pa = (43072, 43135) # (ord(u"\ua840"), ord(u"\ua87f")) + + # Hangul Syllables (AC00–D7AF) + Hangul_Syllables = (44032, 55215) # (ord(u"\uAC00"), ord(u"\uD7AF")) + + # CJK Compatibility Ideographs (F900–FAFF) + CJK_Compatibility_Ideographs = (63744, 64255) # (ord(u"\uF900"), ord(u"\uFAFF")) + + # CJK Compatibility Forms (FE30–FE4F) + CJK_Compatibility_Forms = (65072, 65103) # (ord(u"\uFE30"), ord(u"\uFE4F")) + + # Range U+FF65–FFDC encodes halfwidth forms, of Katakana and Hangul characters + Katakana_Hangul_Halfwidth = (65381, 65500) # (ord(u"\uFF65"), ord(u"\uFFDC")) + + # Supplementary Ideographic Plane 20000–2FFFF + Supplementary_Ideographic_Plane = ( + 131072, + 196607, + ) # (ord(u"\U00020000"), ord(u"\U0002FFFF")) + + ranges = [ + Hangul_Jamo, + CJK_Radicals, + Phags_Pa, + Hangul_Syllables, + CJK_Compatibility_Ideographs, + CJK_Compatibility_Forms, + Katakana_Hangul_Halfwidth, + Supplementary_Ideographic_Plane, + ] + + +def is_cjk(character): + """ + Python port of Moses' code to check for CJK character. + + >>> CJKChars().ranges + [(4352, 4607), (11904, 42191), (43072, 43135), (44032, 55215), (63744, 64255), (65072, 65103), (65381, 65500), (131072, 196607)] + >>> is_cjk(u'\u33fe') + True + >>> is_cjk(u'\uFE5F') + False + + :param character: The character that needs to be checked. + :type character: char + :return: bool + """ + return any( + [ + start <= ord(character) <= end + for start, end in [ + (4352, 4607), + (11904, 42191), + (43072, 43135), + (44032, 55215), + (63744, 64255), + (65072, 65103), + (65381, 65500), + (131072, 196607), + ] + ] + ) + + +def xml_escape(text): + """ + This function transforms the input text into an "escaped" version suitable + for well-formed XML formatting. + + Note that the default xml.sax.saxutils.escape() function don't escape + some characters that Moses does so we have to manually add them to the + entities dictionary. + + >>> input_str = ''')| & < > ' " ] [''' + >>> expected_output = ''')| & < > ' " ] [''' + >>> escape(input_str) == expected_output + True + >>> xml_escape(input_str) + ')| & < > ' " ] [' + + :param text: The text that needs to be escaped. + :type text: str + :rtype: str + """ + return escape( + text, + entities={ + r"'": r"'", + r'"': r""", + r"|": r"|", + r"[": r"[", + r"]": r"]", + }, + ) + + +def xml_unescape(text): + """ + This function transforms the "escaped" version suitable + for well-formed XML formatting into humanly-readable string. + + Note that the default xml.sax.saxutils.unescape() function don't unescape + some characters that Moses does so we have to manually add them to the + entities dictionary. + + >>> from xml.sax.saxutils import unescape + >>> s = ')| & < > ' " ] [' + >>> expected = ''')| & < > \' " ] [''' + >>> xml_unescape(s) == expected + True + + :param text: The text that needs to be unescaped. + :type text: str + :rtype: str + """ + return unescape( + text, + entities={ + r"'": r"'", + r""": r'"', + r"|": r"|", + r"[": r"[", + r"]": r"]", + }, + ) + + +def align_tokens(tokens, sentence): + """ + This module attempt to find the offsets of the tokens in *s*, as a sequence + of ``(start, end)`` tuples, given the tokens and also the source string. + + >>> from nltk.tokenize import TreebankWordTokenizer + >>> from nltk.tokenize.util import align_tokens + >>> s = str("The plane, bound for St Petersburg, crashed in Egypt's " + ... "Sinai desert just 23 minutes after take-off from Sharm el-Sheikh " + ... "on Saturday.") + >>> tokens = TreebankWordTokenizer().tokenize(s) + >>> expected = [(0, 3), (4, 9), (9, 10), (11, 16), (17, 20), (21, 23), + ... (24, 34), (34, 35), (36, 43), (44, 46), (47, 52), (52, 54), + ... (55, 60), (61, 67), (68, 72), (73, 75), (76, 83), (84, 89), + ... (90, 98), (99, 103), (104, 109), (110, 119), (120, 122), + ... (123, 131), (131, 132)] + >>> output = list(align_tokens(tokens, s)) + >>> len(tokens) == len(expected) == len(output) # Check that length of tokens and tuples are the same. + True + >>> expected == list(align_tokens(tokens, s)) # Check that the output is as expected. + True + >>> tokens == [s[start:end] for start, end in output] # Check that the slices of the string corresponds to the tokens. + True + + :param tokens: The list of strings that are the result of tokenization + :type tokens: list(str) + :param sentence: The original string + :type sentence: str + :rtype: list(tuple(int,int)) + """ + point = 0 + offsets = [] + for token in tokens: + try: + start = sentence.index(token, point) + except ValueError as e: + raise ValueError(f'substring "{token}" not found in "{sentence}"') from e + point = start + len(token) + offsets.append((start, point)) + return offsets diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/toolbox.py b/.pythonlibs/lib/python3.10/site-packages/nltk/toolbox.py new file mode 100644 index 0000000..900e252 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/toolbox.py @@ -0,0 +1,524 @@ +# Natural Language Toolkit: Toolbox Reader +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Greg Aumann +# URL: +# For license information, see LICENSE.TXT + +""" +Module for reading, writing and manipulating +Toolbox databases and settings files. +""" + +import codecs +import re +from io import StringIO +from xml.etree.ElementTree import Element, ElementTree, SubElement, TreeBuilder + +from nltk.data import PathPointer, find + + +class StandardFormat: + """ + Class for reading and processing standard format marker files and strings. + """ + + def __init__(self, filename=None, encoding=None): + self._encoding = encoding + if filename is not None: + self.open(filename) + + def open(self, sfm_file): + """ + Open a standard format marker file for sequential reading. + + :param sfm_file: name of the standard format marker input file + :type sfm_file: str + """ + if isinstance(sfm_file, PathPointer): + self._file = sfm_file.open(self._encoding) + else: + self._file = codecs.open(sfm_file, "r", self._encoding) + + def open_string(self, s): + """ + Open a standard format marker string for sequential reading. + + :param s: string to parse as a standard format marker input file + :type s: str + """ + self._file = StringIO(s) + + def raw_fields(self): + """ + Return an iterator that returns the next field in a (marker, value) + tuple. Linebreaks and trailing white space are preserved except + for the final newline in each field. + + :rtype: iter(tuple(str, str)) + """ + join_string = "\n" + line_regexp = r"^%s(?:\\(\S+)\s*)?(.*)$" + # discard a BOM in the first line + first_line_pat = re.compile(line_regexp % "(?:\xef\xbb\xbf)?") + line_pat = re.compile(line_regexp % "") + # need to get first line outside the loop for correct handling + # of the first marker if it spans multiple lines + file_iter = iter(self._file) + # PEP 479, prevent RuntimeError when StopIteration is raised inside generator + try: + line = next(file_iter) + except StopIteration: + # no more data is available, terminate the generator + return + mobj = re.match(first_line_pat, line) + mkr, line_value = mobj.groups() + value_lines = [line_value] + self.line_num = 0 + for line in file_iter: + self.line_num += 1 + mobj = re.match(line_pat, line) + line_mkr, line_value = mobj.groups() + if line_mkr: + yield (mkr, join_string.join(value_lines)) + mkr = line_mkr + value_lines = [line_value] + else: + value_lines.append(line_value) + self.line_num += 1 + yield (mkr, join_string.join(value_lines)) + + def fields( + self, + strip=True, + unwrap=True, + encoding=None, + errors="strict", + unicode_fields=None, + ): + """ + Return an iterator that returns the next field in a ``(marker, value)`` + tuple, where ``marker`` and ``value`` are unicode strings if an ``encoding`` + was specified in the ``fields()`` method. Otherwise they are non-unicode strings. + + :param strip: strip trailing whitespace from the last line of each field + :type strip: bool + :param unwrap: Convert newlines in a field to spaces. + :type unwrap: bool + :param encoding: Name of an encoding to use. If it is specified then + the ``fields()`` method returns unicode strings rather than non + unicode strings. + :type encoding: str or None + :param errors: Error handling scheme for codec. Same as the ``decode()`` + builtin string method. + :type errors: str + :param unicode_fields: Set of marker names whose values are UTF-8 encoded. + Ignored if encoding is None. If the whole file is UTF-8 encoded set + ``encoding='utf8'`` and leave ``unicode_fields`` with its default + value of None. + :type unicode_fields: sequence + :rtype: iter(tuple(str, str)) + """ + if encoding is None and unicode_fields is not None: + raise ValueError("unicode_fields is set but not encoding.") + unwrap_pat = re.compile(r"\n+") + for mkr, val in self.raw_fields(): + if unwrap: + val = unwrap_pat.sub(" ", val) + if strip: + val = val.rstrip() + yield (mkr, val) + + def close(self): + """Close a previously opened standard format marker file or string.""" + self._file.close() + try: + del self.line_num + except AttributeError: + pass + + +class ToolboxData(StandardFormat): + def parse(self, grammar=None, **kwargs): + if grammar: + return self._chunk_parse(grammar=grammar, **kwargs) + else: + return self._record_parse(**kwargs) + + def _record_parse(self, key=None, **kwargs): + r""" + Returns an element tree structure corresponding to a toolbox data file with + all markers at the same level. + + Thus the following Toolbox database:: + \_sh v3.0 400 Rotokas Dictionary + \_DateStampHasFourDigitYear + + \lx kaa + \ps V.A + \ge gag + \gp nek i pas + + \lx kaa + \ps V.B + \ge strangle + \gp pasim nek + + after parsing will end up with the same structure (ignoring the extra + whitespace) as the following XML fragment after being parsed by + ElementTree:: + +
    + <_sh>v3.0 400 Rotokas Dictionary + <_DateStampHasFourDigitYear/> +
    + + + kaa + V.A + gag + nek i pas + + + + kaa + V.B + strangle + pasim nek + +
    + + :param key: Name of key marker at the start of each record. If set to + None (the default value) the first marker that doesn't begin with + an underscore is assumed to be the key. + :type key: str + :param kwargs: Keyword arguments passed to ``StandardFormat.fields()`` + :type kwargs: dict + :rtype: ElementTree._ElementInterface + :return: contents of toolbox data divided into header and records + """ + builder = TreeBuilder() + builder.start("toolbox_data", {}) + builder.start("header", {}) + in_records = False + for mkr, value in self.fields(**kwargs): + if key is None and not in_records and mkr[0] != "_": + key = mkr + if mkr == key: + if in_records: + builder.end("record") + else: + builder.end("header") + in_records = True + builder.start("record", {}) + builder.start(mkr, {}) + builder.data(value) + builder.end(mkr) + if in_records: + builder.end("record") + else: + builder.end("header") + builder.end("toolbox_data") + return builder.close() + + def _tree2etree(self, parent): + from nltk.tree import Tree + + root = Element(parent.label()) + for child in parent: + if isinstance(child, Tree): + root.append(self._tree2etree(child)) + else: + text, tag = child + e = SubElement(root, tag) + e.text = text + return root + + def _chunk_parse(self, grammar=None, root_label="record", trace=0, **kwargs): + """ + Returns an element tree structure corresponding to a toolbox data file + parsed according to the chunk grammar. + + :type grammar: str + :param grammar: Contains the chunking rules used to parse the + database. See ``chunk.RegExp`` for documentation. + :type root_label: str + :param root_label: The node value that should be used for the + top node of the chunk structure. + :type trace: int + :param trace: The level of tracing that should be used when + parsing a text. ``0`` will generate no tracing output; + ``1`` will generate normal tracing output; and ``2`` or + higher will generate verbose tracing output. + :type kwargs: dict + :param kwargs: Keyword arguments passed to ``toolbox.StandardFormat.fields()`` + :rtype: ElementTree._ElementInterface + """ + from nltk import chunk + from nltk.tree import Tree + + cp = chunk.RegexpParser(grammar, root_label=root_label, trace=trace) + db = self.parse(**kwargs) + tb_etree = Element("toolbox_data") + header = db.find("header") + tb_etree.append(header) + for record in db.findall("record"): + parsed = cp.parse([(elem.text, elem.tag) for elem in record]) + tb_etree.append(self._tree2etree(parsed)) + return tb_etree + + +_is_value = re.compile(r"\S") + + +def to_sfm_string(tree, encoding=None, errors="strict", unicode_fields=None): + """ + Return a string with a standard format representation of the toolbox + data in tree (tree can be a toolbox database or a single record). + + :param tree: flat representation of toolbox data (whole database or single record) + :type tree: ElementTree._ElementInterface + :param encoding: Name of an encoding to use. + :type encoding: str + :param errors: Error handling scheme for codec. Same as the ``encode()`` + builtin string method. + :type errors: str + :param unicode_fields: + :type unicode_fields: dict(str) or set(str) + :rtype: str + """ + if tree.tag == "record": + root = Element("toolbox_data") + root.append(tree) + tree = root + + if tree.tag != "toolbox_data": + raise ValueError("not a toolbox_data element structure") + if encoding is None and unicode_fields is not None: + raise ValueError( + "if encoding is not specified then neither should unicode_fields" + ) + l = [] + for rec in tree: + l.append("\n") + for field in rec: + mkr = field.tag + value = field.text + if encoding is not None: + if unicode_fields is not None and mkr in unicode_fields: + cur_encoding = "utf8" + else: + cur_encoding = encoding + if re.search(_is_value, value): + l.append((f"\\{mkr} {value}\n").encode(cur_encoding, errors)) + else: + l.append((f"\\{mkr}{value}\n").encode(cur_encoding, errors)) + else: + if re.search(_is_value, value): + l.append(f"\\{mkr} {value}\n") + else: + l.append(f"\\{mkr}{value}\n") + return "".join(l[1:]) + + +class ToolboxSettings(StandardFormat): + """This class is the base class for settings files.""" + + def __init__(self): + super().__init__() + + def parse(self, encoding=None, errors="strict", **kwargs): + """ + Return the contents of toolbox settings file with a nested structure. + + :param encoding: encoding used by settings file + :type encoding: str + :param errors: Error handling scheme for codec. Same as ``decode()`` builtin method. + :type errors: str + :param kwargs: Keyword arguments passed to ``StandardFormat.fields()`` + :type kwargs: dict + :rtype: ElementTree._ElementInterface + """ + builder = TreeBuilder() + for mkr, value in self.fields(encoding=encoding, errors=errors, **kwargs): + # Check whether the first char of the field marker + # indicates a block start (+) or end (-) + block = mkr[0] + if block in ("+", "-"): + mkr = mkr[1:] + else: + block = None + # Build tree on the basis of block char + if block == "+": + builder.start(mkr, {}) + builder.data(value) + elif block == "-": + builder.end(mkr) + else: + builder.start(mkr, {}) + builder.data(value) + builder.end(mkr) + return builder.close() + + +def to_settings_string(tree, encoding=None, errors="strict", unicode_fields=None): + # write XML to file + l = list() + _to_settings_string( + tree.getroot(), + l, + encoding=encoding, + errors=errors, + unicode_fields=unicode_fields, + ) + return "".join(l) + + +def _to_settings_string(node, l, **kwargs): + # write XML to file + tag = node.tag + text = node.text + if len(node) == 0: + if text: + l.append(f"\\{tag} {text}\n") + else: + l.append("\\%s\n" % tag) + else: + if text: + l.append(f"\\+{tag} {text}\n") + else: + l.append("\\+%s\n" % tag) + for n in node: + _to_settings_string(n, l, **kwargs) + l.append("\\-%s\n" % tag) + return + + +def remove_blanks(elem): + """ + Remove all elements and subelements with no text and no child elements. + + :param elem: toolbox data in an elementtree structure + :type elem: ElementTree._ElementInterface + """ + out = list() + for child in elem: + remove_blanks(child) + if child.text or len(child) > 0: + out.append(child) + elem[:] = out + + +def add_default_fields(elem, default_fields): + """ + Add blank elements and subelements specified in default_fields. + + :param elem: toolbox data in an elementtree structure + :type elem: ElementTree._ElementInterface + :param default_fields: fields to add to each type of element and subelement + :type default_fields: dict(tuple) + """ + for field in default_fields.get(elem.tag, []): + if elem.find(field) is None: + SubElement(elem, field) + for child in elem: + add_default_fields(child, default_fields) + + +def sort_fields(elem, field_orders): + """ + Sort the elements and subelements in order specified in field_orders. + + :param elem: toolbox data in an elementtree structure + :type elem: ElementTree._ElementInterface + :param field_orders: order of fields for each type of element and subelement + :type field_orders: dict(tuple) + """ + order_dicts = dict() + for field, order in field_orders.items(): + order_dicts[field] = order_key = dict() + for i, subfield in enumerate(order): + order_key[subfield] = i + _sort_fields(elem, order_dicts) + + +def _sort_fields(elem, orders_dicts): + """sort the children of elem""" + try: + order = orders_dicts[elem.tag] + except KeyError: + pass + else: + tmp = sorted( + ((order.get(child.tag, 1e9), i), child) for i, child in enumerate(elem) + ) + elem[:] = [child for key, child in tmp] + for child in elem: + if len(child): + _sort_fields(child, orders_dicts) + + +def add_blank_lines(tree, blanks_before, blanks_between): + """ + Add blank lines before all elements and subelements specified in blank_before. + + :param elem: toolbox data in an elementtree structure + :type elem: ElementTree._ElementInterface + :param blank_before: elements and subelements to add blank lines before + :type blank_before: dict(tuple) + """ + try: + before = blanks_before[tree.tag] + between = blanks_between[tree.tag] + except KeyError: + for elem in tree: + if len(elem): + add_blank_lines(elem, blanks_before, blanks_between) + else: + last_elem = None + for elem in tree: + tag = elem.tag + if last_elem is not None and last_elem.tag != tag: + if tag in before and last_elem is not None: + e = last_elem.getiterator()[-1] + e.text = (e.text or "") + "\n" + else: + if tag in between: + e = last_elem.getiterator()[-1] + e.text = (e.text or "") + "\n" + if len(elem): + add_blank_lines(elem, blanks_before, blanks_between) + last_elem = elem + + +def demo(): + from itertools import islice + + # zip_path = find('corpora/toolbox.zip') + # lexicon = ToolboxData(ZipFilePathPointer(zip_path, 'toolbox/rotokas.dic')).parse() + file_path = find("corpora/toolbox/rotokas.dic") + lexicon = ToolboxData(file_path).parse() + print("first field in fourth record:") + print(lexicon[3][0].tag) + print(lexicon[3][0].text) + + print("\nfields in sequential order:") + for field in islice(lexicon.find("record"), 10): + print(field.tag, field.text) + + print("\nlx fields:") + for field in islice(lexicon.findall("record/lx"), 10): + print(field.text) + + settings = ToolboxSettings() + file_path = find("corpora/toolbox/MDF/MDF_AltH.typ") + settings.open(file_path) + # settings.open(ZipFilePathPointer(zip_path, entry='toolbox/MDF/MDF_AltH.typ')) + tree = settings.parse(unwrap=False, encoding="cp1252") + print(tree.find("expset/expMDF/rtfPageSetup/paperSize").text) + settings_tree = ElementTree(tree) + print(to_settings_string(settings_tree).encode("utf8")) + + +if __name__ == "__main__": + demo() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__init__.py new file mode 100644 index 0000000..4ec8c40 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__init__.py @@ -0,0 +1,32 @@ +# Natural Language Toolkit: Machine Translation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird , Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +Experimental features for machine translation. +These interfaces are prone to change. + +isort:skip_file +""" + +from nltk.translate.api import AlignedSent, Alignment, PhraseTable +from nltk.translate.ibm_model import IBMModel +from nltk.translate.ibm1 import IBMModel1 +from nltk.translate.ibm2 import IBMModel2 +from nltk.translate.ibm3 import IBMModel3 +from nltk.translate.ibm4 import IBMModel4 +from nltk.translate.ibm5 import IBMModel5 +from nltk.translate.bleu_score import sentence_bleu as bleu +from nltk.translate.ribes_score import sentence_ribes as ribes +from nltk.translate.meteor_score import meteor_score as meteor +from nltk.translate.metrics import alignment_error_rate +from nltk.translate.stack_decoder import StackDecoder +from nltk.translate.nist_score import sentence_nist as nist +from nltk.translate.chrf_score import sentence_chrf as chrf +from nltk.translate.gale_church import trace +from nltk.translate.gdfa import grow_diag_final_and +from nltk.translate.gleu_score import sentence_gleu as gleu +from nltk.translate.phrase_based import extract diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2ffa449 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..4735647 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/bleu_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/bleu_score.cpython-310.pyc new file mode 100644 index 0000000..1174f22 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/bleu_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/chrf_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/chrf_score.cpython-310.pyc new file mode 100644 index 0000000..9a533ac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/chrf_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gale_church.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gale_church.cpython-310.pyc new file mode 100644 index 0000000..44e024a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gale_church.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gdfa.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gdfa.cpython-310.pyc new file mode 100644 index 0000000..44c8cd8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gdfa.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gleu_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gleu_score.cpython-310.pyc new file mode 100644 index 0000000..109d03c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/gleu_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm1.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm1.cpython-310.pyc new file mode 100644 index 0000000..2dbd551 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm1.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm2.cpython-310.pyc new file mode 100644 index 0000000..367260d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm3.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm3.cpython-310.pyc new file mode 100644 index 0000000..df313b7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm3.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm4.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm4.cpython-310.pyc new file mode 100644 index 0000000..2fff902 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm4.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm5.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm5.cpython-310.pyc new file mode 100644 index 0000000..8191453 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm5.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm_model.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm_model.cpython-310.pyc new file mode 100644 index 0000000..1445c11 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ibm_model.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/meteor_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/meteor_score.cpython-310.pyc new file mode 100644 index 0000000..3219998 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/meteor_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/metrics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/metrics.cpython-310.pyc new file mode 100644 index 0000000..a281df2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/metrics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/nist_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/nist_score.cpython-310.pyc new file mode 100644 index 0000000..4d0fd65 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/nist_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/phrase_based.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/phrase_based.cpython-310.pyc new file mode 100644 index 0000000..6963e00 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/phrase_based.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ribes_score.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ribes_score.cpython-310.pyc new file mode 100644 index 0000000..b816474 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/ribes_score.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/stack_decoder.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/stack_decoder.cpython-310.pyc new file mode 100644 index 0000000..3b26190 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/__pycache__/stack_decoder.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/api.py new file mode 100644 index 0000000..b824639 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/api.py @@ -0,0 +1,334 @@ +# Natural Language Toolkit: API for alignment and translation objects +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Will Zhang +# Guan Gui +# Steven Bird +# Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +import subprocess +from collections import namedtuple + + +class AlignedSent: + """ + Return an aligned sentence object, which encapsulates two sentences + along with an ``Alignment`` between them. + + Typically used in machine translation to represent a sentence and + its translation. + + >>> from nltk.translate import AlignedSent, Alignment + >>> algnsent = AlignedSent(['klein', 'ist', 'das', 'Haus'], + ... ['the', 'house', 'is', 'small'], Alignment.fromstring('0-3 1-2 2-0 3-1')) + >>> algnsent.words + ['klein', 'ist', 'das', 'Haus'] + >>> algnsent.mots + ['the', 'house', 'is', 'small'] + >>> algnsent.alignment + Alignment([(0, 3), (1, 2), (2, 0), (3, 1)]) + >>> from nltk.corpus import comtrans + >>> print(comtrans.aligned_sents()[54]) + 'So why should EU arm...'> + >>> print(comtrans.aligned_sents()[54].alignment) + 0-0 0-1 1-0 2-2 3-4 3-5 4-7 5-8 6-3 7-9 8-9 9-10 9-11 10-12 11-6 12-6 13-13 + + :param words: Words in the target language sentence + :type words: list(str) + :param mots: Words in the source language sentence + :type mots: list(str) + :param alignment: Word-level alignments between ``words`` and ``mots``. + Each alignment is represented as a 2-tuple (words_index, mots_index). + :type alignment: Alignment + """ + + def __init__(self, words, mots, alignment=None): + self._words = words + self._mots = mots + if alignment is None: + self.alignment = Alignment([]) + else: + assert type(alignment) is Alignment + self.alignment = alignment + + @property + def words(self): + return self._words + + @property + def mots(self): + return self._mots + + def _get_alignment(self): + return self._alignment + + def _set_alignment(self, alignment): + _check_alignment(len(self.words), len(self.mots), alignment) + self._alignment = alignment + + alignment = property(_get_alignment, _set_alignment) + + def __repr__(self): + """ + Return a string representation for this ``AlignedSent``. + + :rtype: str + """ + words = "[%s]" % (", ".join("'%s'" % w for w in self._words)) + mots = "[%s]" % (", ".join("'%s'" % w for w in self._mots)) + + return f"AlignedSent({words}, {mots}, {self._alignment!r})" + + def _to_dot(self): + """ + Dot representation of the aligned sentence + """ + s = "graph align {\n" + s += "node[shape=plaintext]\n" + + # Declare node + for w in self._words: + s += f'"{w}_source" [label="{w}"] \n' + + for w in self._mots: + s += f'"{w}_target" [label="{w}"] \n' + + # Alignment + for u, v in self._alignment: + s += f'"{self._words[u]}_source" -- "{self._mots[v]}_target" \n' + + # Connect the source words + for i in range(len(self._words) - 1): + s += '"{}_source" -- "{}_source" [style=invis]\n'.format( + self._words[i], + self._words[i + 1], + ) + + # Connect the target words + for i in range(len(self._mots) - 1): + s += '"{}_target" -- "{}_target" [style=invis]\n'.format( + self._mots[i], + self._mots[i + 1], + ) + + # Put it in the same rank + s += "{rank = same; %s}\n" % (" ".join('"%s_source"' % w for w in self._words)) + s += "{rank = same; %s}\n" % (" ".join('"%s_target"' % w for w in self._mots)) + + s += "}" + + return s + + def _repr_svg_(self): + """ + Ipython magic : show SVG representation of this ``AlignedSent``. + """ + dot_string = self._to_dot().encode("utf8") + output_format = "svg" + try: + process = subprocess.Popen( + ["dot", "-T%s" % output_format], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except OSError as e: + raise Exception("Cannot find the dot binary from Graphviz package") from e + out, err = process.communicate(dot_string) + + return out.decode("utf8") + + def __str__(self): + """ + Return a human-readable string representation for this ``AlignedSent``. + + :rtype: str + """ + source = " ".join(self._words)[:20] + "..." + target = " ".join(self._mots)[:20] + "..." + return f" '{target}'>" + + def invert(self): + """ + Return the aligned sentence pair, reversing the directionality + + :rtype: AlignedSent + """ + return AlignedSent(self._mots, self._words, self._alignment.invert()) + + +class Alignment(frozenset): + """ + A storage class for representing alignment between two sequences, s1, s2. + In general, an alignment is a set of tuples of the form (i, j, ...) + representing an alignment between the i-th element of s1 and the + j-th element of s2. Tuples are extensible (they might contain + additional data, such as a boolean to indicate sure vs possible alignments). + + >>> from nltk.translate import Alignment + >>> a = Alignment([(0, 0), (0, 1), (1, 2), (2, 2)]) + >>> a.invert() + Alignment([(0, 0), (1, 0), (2, 1), (2, 2)]) + >>> print(a.invert()) + 0-0 1-0 2-1 2-2 + >>> a[0] + [(0, 1), (0, 0)] + >>> a.invert()[2] + [(2, 1), (2, 2)] + >>> b = Alignment([(0, 0), (0, 1)]) + >>> b.issubset(a) + True + >>> c = Alignment.fromstring('0-0 0-1') + >>> b == c + True + """ + + def __new__(cls, pairs): + self = frozenset.__new__(cls, pairs) + self._len = max(p[0] for p in self) if self != frozenset([]) else 0 + self._index = None + return self + + @classmethod + def fromstring(cls, s): + """ + Read a giza-formatted string and return an Alignment object. + + >>> Alignment.fromstring('0-0 2-1 9-2 21-3 10-4 7-5') + Alignment([(0, 0), (2, 1), (7, 5), (9, 2), (10, 4), (21, 3)]) + + :type s: str + :param s: the positional alignments in giza format + :rtype: Alignment + :return: An Alignment object corresponding to the string representation ``s``. + """ + + return Alignment([_giza2pair(a) for a in s.split()]) + + def __getitem__(self, key): + """ + Look up the alignments that map from a given index or slice. + """ + if not self._index: + self._build_index() + return self._index.__getitem__(key) + + def invert(self): + """ + Return an Alignment object, being the inverted mapping. + """ + return Alignment(((p[1], p[0]) + p[2:]) for p in self) + + def range(self, positions=None): + """ + Work out the range of the mapping from the given positions. + If no positions are specified, compute the range of the entire mapping. + """ + image = set() + if not self._index: + self._build_index() + if not positions: + positions = list(range(len(self._index))) + for p in positions: + image.update(f for _, f in self._index[p]) + return sorted(image) + + def __repr__(self): + """ + Produce a Giza-formatted string representing the alignment. + """ + return "Alignment(%r)" % sorted(self) + + def __str__(self): + """ + Produce a Giza-formatted string representing the alignment. + """ + return " ".join("%d-%d" % p[:2] for p in sorted(self)) + + def _build_index(self): + """ + Build a list self._index such that self._index[i] is a list + of the alignments originating from word i. + """ + self._index = [[] for _ in range(self._len + 1)] + for p in self: + self._index[p[0]].append(p) + + +def _giza2pair(pair_string): + i, j = pair_string.split("-") + return int(i), int(j) + + +def _naacl2pair(pair_string): + i, j, p = pair_string.split("-") + return int(i), int(j) + + +def _check_alignment(num_words, num_mots, alignment): + """ + Check whether the alignments are legal. + + :param num_words: the number of source language words + :type num_words: int + :param num_mots: the number of target language words + :type num_mots: int + :param alignment: alignment to be checked + :type alignment: Alignment + :raise IndexError: if alignment falls outside the sentence + """ + + assert type(alignment) is Alignment + + if not all(0 <= pair[0] < num_words for pair in alignment): + raise IndexError("Alignment is outside boundary of words") + if not all(pair[1] is None or 0 <= pair[1] < num_mots for pair in alignment): + raise IndexError("Alignment is outside boundary of mots") + + +PhraseTableEntry = namedtuple("PhraseTableEntry", ["trg_phrase", "log_prob"]) + + +class PhraseTable: + """ + In-memory store of translations for a given phrase, and the log + probability of the those translations + """ + + def __init__(self): + self.src_phrases = dict() + + def translations_for(self, src_phrase): + """ + Get the translations for a source language phrase + + :param src_phrase: Source language phrase of interest + :type src_phrase: tuple(str) + + :return: A list of target language phrases that are translations + of ``src_phrase``, ordered in decreasing order of + likelihood. Each list element is a tuple of the target + phrase and its log probability. + :rtype: list(PhraseTableEntry) + """ + return self.src_phrases[src_phrase] + + def add(self, src_phrase, trg_phrase, log_prob): + """ + :type src_phrase: tuple(str) + :type trg_phrase: tuple(str) + + :param log_prob: Log probability that given ``src_phrase``, + ``trg_phrase`` is its translation + :type log_prob: float + """ + entry = PhraseTableEntry(trg_phrase=trg_phrase, log_prob=log_prob) + if src_phrase not in self.src_phrases: + self.src_phrases[src_phrase] = [] + self.src_phrases[src_phrase].append(entry) + self.src_phrases[src_phrase].sort(key=lambda e: e.log_prob, reverse=True) + + def __contains__(self, src_phrase): + return src_phrase in self.src_phrases diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/bleu_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/bleu_score.py new file mode 100644 index 0000000..f6c232e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/bleu_score.py @@ -0,0 +1,685 @@ +# Natural Language Toolkit: BLEU Score +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Chin Yee Lee, Hengfeng Li, Ruxin Hou, Calvin Tanujaya Lim +# Contributors: Björn Mattsson, Dmitrijs Milajevs, Liling Tan +# URL: +# For license information, see LICENSE.TXT + +"""BLEU score implementation.""" + +import math +import sys +import warnings +from collections import Counter +from fractions import Fraction + +from nltk.util import ngrams + + +def sentence_bleu( + references, + hypothesis, + weights=(0.25, 0.25, 0.25, 0.25), + smoothing_function=None, + auto_reweigh=False, +): + """ + Calculate BLEU score (Bilingual Evaluation Understudy) from + Papineni, Kishore, Salim Roukos, Todd Ward, and Wei-Jing Zhu. 2002. + "BLEU: a method for automatic evaluation of machine translation." + In Proceedings of ACL. https://www.aclweb.org/anthology/P02-1040.pdf + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + + >>> hypothesis2 = ['It', 'is', 'to', 'insure', 'the', 'troops', + ... 'forever', 'hearing', 'the', 'activity', 'guidebook', + ... 'that', 'party', 'direct'] + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', 'forever', + ... 'heed', 'Party', 'commands'] + + >>> reference2 = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', + ... 'Party'] + + >>> reference3 = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + + >>> sentence_bleu([reference1, reference2, reference3], hypothesis1) # doctest: +ELLIPSIS + 0.5045... + + If there is no ngrams overlap for any order of n-grams, BLEU returns the + value 0. This is because the precision for the order of n-grams without + overlap is 0, and the geometric mean in the final BLEU score computation + multiplies the 0 with the precision of other n-grams. This results in 0 + (independently of the precision of the other n-gram orders). The following + example has zero 3-gram and 4-gram overlaps: + + >>> round(sentence_bleu([reference1, reference2, reference3], hypothesis2),4) # doctest: +ELLIPSIS + 0.0 + + To avoid this harsh behaviour when no ngram overlaps are found a smoothing + function can be used. + + >>> chencherry = SmoothingFunction() + >>> sentence_bleu([reference1, reference2, reference3], hypothesis2, + ... smoothing_function=chencherry.method1) # doctest: +ELLIPSIS + 0.0370... + + The default BLEU calculates a score for up to 4-grams using uniform + weights (this is called BLEU-4). To evaluate your translations with + higher/lower order ngrams, use customized weights. E.g. when accounting + for up to 5-grams with uniform weights (this is called BLEU-5) use: + + >>> weights = (1./5., 1./5., 1./5., 1./5., 1./5.) + >>> sentence_bleu([reference1, reference2, reference3], hypothesis1, weights) # doctest: +ELLIPSIS + 0.3920... + + Multiple BLEU scores can be computed at once, by supplying a list of weights. + E.g. for computing BLEU-2, BLEU-3 *and* BLEU-4 in one computation, use: + >>> weights = [ + ... (1./2., 1./2.), + ... (1./3., 1./3., 1./3.), + ... (1./4., 1./4., 1./4., 1./4.) + ... ] + >>> sentence_bleu([reference1, reference2, reference3], hypothesis1, weights) # doctest: +ELLIPSIS + [0.7453..., 0.6240..., 0.5045...] + + :param references: reference sentences + :type references: list(list(str)) + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) + :param weights: weights for unigrams, bigrams, trigrams and so on (one or a list of weights) + :type weights: tuple(float) / list(tuple(float)) + :param smoothing_function: + :type smoothing_function: SmoothingFunction + :param auto_reweigh: Option to re-normalize the weights uniformly. + :type auto_reweigh: bool + :return: The sentence-level BLEU score. Returns a list if multiple weights were supplied. + :rtype: float / list(float) + """ + return corpus_bleu( + [references], [hypothesis], weights, smoothing_function, auto_reweigh + ) + + +def corpus_bleu( + list_of_references, + hypotheses, + weights=(0.25, 0.25, 0.25, 0.25), + smoothing_function=None, + auto_reweigh=False, +): + """ + Calculate a single corpus-level BLEU score (aka. system-level BLEU) for all + the hypotheses and their respective references. + + Instead of averaging the sentence level BLEU scores (i.e. macro-average + precision), the original BLEU metric (Papineni et al. 2002) accounts for + the micro-average precision (i.e. summing the numerators and denominators + for each hypothesis-reference(s) pairs before the division). + + >>> hyp1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + >>> ref1a = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', 'forever', + ... 'heed', 'Party', 'commands'] + >>> ref1b = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', 'Party'] + >>> ref1c = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + + >>> hyp2 = ['he', 'read', 'the', 'book', 'because', 'he', 'was', + ... 'interested', 'in', 'world', 'history'] + >>> ref2a = ['he', 'was', 'interested', 'in', 'world', 'history', + ... 'because', 'he', 'read', 'the', 'book'] + + >>> list_of_references = [[ref1a, ref1b, ref1c], [ref2a]] + >>> hypotheses = [hyp1, hyp2] + >>> corpus_bleu(list_of_references, hypotheses) # doctest: +ELLIPSIS + 0.5920... + + The example below show that corpus_bleu() is different from averaging + sentence_bleu() for hypotheses + + >>> score1 = sentence_bleu([ref1a, ref1b, ref1c], hyp1) + >>> score2 = sentence_bleu([ref2a], hyp2) + >>> (score1 + score2) / 2 # doctest: +ELLIPSIS + 0.6223... + + Custom weights may be supplied to fine-tune the BLEU score further. + A tuple of float weights for unigrams, bigrams, trigrams and so on can be given. + >>> weights = (0.1, 0.3, 0.5, 0.1) + >>> corpus_bleu(list_of_references, hypotheses, weights=weights) # doctest: +ELLIPSIS + 0.5818... + + This particular weight gave extra value to trigrams. + Furthermore, multiple weights can be given, resulting in multiple BLEU scores. + >>> weights = [ + ... (0.5, 0.5), + ... (0.333, 0.333, 0.334), + ... (0.25, 0.25, 0.25, 0.25), + ... (0.2, 0.2, 0.2, 0.2, 0.2) + ... ] + >>> corpus_bleu(list_of_references, hypotheses, weights=weights) # doctest: +ELLIPSIS + [0.8242..., 0.7067..., 0.5920..., 0.4719...] + + :param list_of_references: a corpus of lists of reference sentences, w.r.t. hypotheses + :type list_of_references: list(list(list(str))) + :param hypotheses: a list of hypothesis sentences + :type hypotheses: list(list(str)) + :param weights: weights for unigrams, bigrams, trigrams and so on (one or a list of weights) + :type weights: tuple(float) / list(tuple(float)) + :param smoothing_function: + :type smoothing_function: SmoothingFunction + :param auto_reweigh: Option to re-normalize the weights uniformly. + :type auto_reweigh: bool + :return: The corpus-level BLEU score. + :rtype: float + """ + # Before proceeding to compute BLEU, perform sanity checks. + + p_numerators = Counter() # Key = ngram order, and value = no. of ngram matches. + p_denominators = Counter() # Key = ngram order, and value = no. of ngram in ref. + hyp_lengths, ref_lengths = 0, 0 + + assert len(list_of_references) == len(hypotheses), ( + "The number of hypotheses and their reference(s) should be the " "same " + ) + + try: + weights[0][0] + except TypeError: + weights = [weights] + max_weight_length = max(len(weight) for weight in weights) + + # Iterate through each hypothesis and their corresponding references. + for references, hypothesis in zip(list_of_references, hypotheses): + # For each order of ngram, calculate the numerator and + # denominator for the corpus-level modified precision. + for i in range(1, max_weight_length + 1): + p_i = modified_precision(references, hypothesis, i) + p_numerators[i] += p_i.numerator + p_denominators[i] += p_i.denominator + + # Calculate the hypothesis length and the closest reference length. + # Adds them to the corpus-level hypothesis and reference counts. + hyp_len = len(hypothesis) + hyp_lengths += hyp_len + ref_lengths += closest_ref_length(references, hyp_len) + + # Calculate corpus-level brevity penalty. + bp = brevity_penalty(ref_lengths, hyp_lengths) + + # Collects the various precision values for the different ngram orders. + p_n = [ + Fraction(p_numerators[i], p_denominators[i], _normalize=False) + for i in range(1, max_weight_length + 1) + ] + + # Returns 0 if there's no matching n-grams + # We only need to check for p_numerators[1] == 0, since if there's + # no unigrams, there won't be any higher order ngrams. + if p_numerators[1] == 0: + return 0 if len(weights) == 1 else [0] * len(weights) + + # If there's no smoothing, set use method0 from SmoothinFunction class. + if not smoothing_function: + smoothing_function = SmoothingFunction().method0 + # Smoothen the modified precision. + # Note: smoothing_function() may convert values into floats; + # it tries to retain the Fraction object as much as the + # smoothing method allows. + p_n = smoothing_function( + p_n, references=references, hypothesis=hypothesis, hyp_len=hyp_lengths + ) + + bleu_scores = [] + for weight in weights: + # Uniformly re-weighting based on maximum hypothesis lengths if largest + # order of n-grams < 4 and weights is set at default. + if auto_reweigh: + if hyp_lengths < 4 and weight == (0.25, 0.25, 0.25, 0.25): + weight = (1 / hyp_lengths,) * hyp_lengths + + s = (w_i * math.log(p_i) for w_i, p_i in zip(weight, p_n) if p_i > 0) + s = bp * math.exp(math.fsum(s)) + bleu_scores.append(s) + return bleu_scores[0] if len(weights) == 1 else bleu_scores + + +def modified_precision(references, hypothesis, n): + """ + Calculate modified ngram precision. + + The normal precision method may lead to some wrong translations with + high-precision, e.g., the translation, in which a word of reference + repeats several times, has very high precision. + + This function only returns the Fraction object that contains the numerator + and denominator necessary to calculate the corpus-level precision. + To calculate the modified precision for a single pair of hypothesis and + references, cast the Fraction object into a float. + + The famous "the the the ... " example shows that you can get BLEU precision + by duplicating high frequency words. + + >>> reference1 = 'the cat is on the mat'.split() + >>> reference2 = 'there is a cat on the mat'.split() + >>> hypothesis1 = 'the the the the the the the'.split() + >>> references = [reference1, reference2] + >>> float(modified_precision(references, hypothesis1, n=1)) # doctest: +ELLIPSIS + 0.2857... + + In the modified n-gram precision, a reference word will be considered + exhausted after a matching hypothesis word is identified, e.g. + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', + ... 'forever', 'heed', 'Party', 'commands'] + >>> reference2 = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', + ... 'Party'] + >>> reference3 = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + >>> hypothesis = 'of the'.split() + >>> references = [reference1, reference2, reference3] + >>> float(modified_precision(references, hypothesis, n=1)) + 1.0 + >>> float(modified_precision(references, hypothesis, n=2)) + 1.0 + + An example of a normal machine translation hypothesis: + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + + >>> hypothesis2 = ['It', 'is', 'to', 'insure', 'the', 'troops', + ... 'forever', 'hearing', 'the', 'activity', 'guidebook', + ... 'that', 'party', 'direct'] + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', + ... 'forever', 'heed', 'Party', 'commands'] + + >>> reference2 = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', + ... 'Party'] + + >>> reference3 = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + >>> references = [reference1, reference2, reference3] + >>> float(modified_precision(references, hypothesis1, n=1)) # doctest: +ELLIPSIS + 0.9444... + >>> float(modified_precision(references, hypothesis2, n=1)) # doctest: +ELLIPSIS + 0.5714... + >>> float(modified_precision(references, hypothesis1, n=2)) # doctest: +ELLIPSIS + 0.5882352941176471 + >>> float(modified_precision(references, hypothesis2, n=2)) # doctest: +ELLIPSIS + 0.07692... + + + :param references: A list of reference translations. + :type references: list(list(str)) + :param hypothesis: A hypothesis translation. + :type hypothesis: list(str) + :param n: The ngram order. + :type n: int + :return: BLEU's modified precision for the nth order ngram. + :rtype: Fraction + """ + # Extracts all ngrams in hypothesis + # Set an empty Counter if hypothesis is empty. + counts = Counter(ngrams(hypothesis, n)) if len(hypothesis) >= n else Counter() + # Extract a union of references' counts. + # max_counts = reduce(or_, [Counter(ngrams(ref, n)) for ref in references]) + max_counts = {} + for reference in references: + reference_counts = ( + Counter(ngrams(reference, n)) if len(reference) >= n else Counter() + ) + for ngram in counts: + max_counts[ngram] = max(max_counts.get(ngram, 0), reference_counts[ngram]) + + # Assigns the intersection between hypothesis and references' counts. + clipped_counts = { + ngram: min(count, max_counts[ngram]) for ngram, count in counts.items() + } + + numerator = sum(clipped_counts.values()) + # Ensures that denominator is minimum 1 to avoid ZeroDivisionError. + # Usually this happens when the ngram order is > len(reference). + denominator = max(1, sum(counts.values())) + + return Fraction(numerator, denominator, _normalize=False) + + +def closest_ref_length(references, hyp_len): + """ + This function finds the reference that is the closest length to the + hypothesis. The closest reference length is referred to as *r* variable + from the brevity penalty formula in Papineni et. al. (2002) + + :param references: A list of reference translations. + :type references: list(list(str)) + :param hyp_len: The length of the hypothesis. + :type hyp_len: int + :return: The length of the reference that's closest to the hypothesis. + :rtype: int + """ + ref_lens = (len(reference) for reference in references) + closest_ref_len = min( + ref_lens, key=lambda ref_len: (abs(ref_len - hyp_len), ref_len) + ) + return closest_ref_len + + +def brevity_penalty(closest_ref_len, hyp_len): + """ + Calculate brevity penalty. + + As the modified n-gram precision still has the problem from the short + length sentence, brevity penalty is used to modify the overall BLEU + score according to length. + + An example from the paper. There are three references with length 12, 15 + and 17. And a concise hypothesis of the length 12. The brevity penalty is 1. + + >>> reference1 = list('aaaaaaaaaaaa') # i.e. ['a'] * 12 + >>> reference2 = list('aaaaaaaaaaaaaaa') # i.e. ['a'] * 15 + >>> reference3 = list('aaaaaaaaaaaaaaaaa') # i.e. ['a'] * 17 + >>> hypothesis = list('aaaaaaaaaaaa') # i.e. ['a'] * 12 + >>> references = [reference1, reference2, reference3] + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> brevity_penalty(closest_ref_len, hyp_len) + 1.0 + + In case a hypothesis translation is shorter than the references, penalty is + applied. + + >>> references = [['a'] * 28, ['a'] * 28] + >>> hypothesis = ['a'] * 12 + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> brevity_penalty(closest_ref_len, hyp_len) + 0.2635971381157267 + + The length of the closest reference is used to compute the penalty. If the + length of a hypothesis is 12, and the reference lengths are 13 and 2, the + penalty is applied because the hypothesis length (12) is less then the + closest reference length (13). + + >>> references = [['a'] * 13, ['a'] * 2] + >>> hypothesis = ['a'] * 12 + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> brevity_penalty(closest_ref_len, hyp_len) # doctest: +ELLIPSIS + 0.9200... + + The brevity penalty doesn't depend on reference order. More importantly, + when two reference sentences are at the same distance, the shortest + reference sentence length is used. + + >>> references = [['a'] * 13, ['a'] * 11] + >>> hypothesis = ['a'] * 12 + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> bp1 = brevity_penalty(closest_ref_len, hyp_len) + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(reversed(references), hyp_len) + >>> bp2 = brevity_penalty(closest_ref_len, hyp_len) + >>> bp1 == bp2 == 1 + True + + A test example from mteval-v13a.pl (starting from the line 705): + + >>> references = [['a'] * 11, ['a'] * 8] + >>> hypothesis = ['a'] * 7 + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> brevity_penalty(closest_ref_len, hyp_len) # doctest: +ELLIPSIS + 0.8668... + + >>> references = [['a'] * 11, ['a'] * 8, ['a'] * 6, ['a'] * 7] + >>> hypothesis = ['a'] * 7 + >>> hyp_len = len(hypothesis) + >>> closest_ref_len = closest_ref_length(references, hyp_len) + >>> brevity_penalty(closest_ref_len, hyp_len) + 1.0 + + :param hyp_len: The length of the hypothesis for a single sentence OR the + sum of all the hypotheses' lengths for a corpus + :type hyp_len: int + :param closest_ref_len: The length of the closest reference for a single + hypothesis OR the sum of all the closest references for every hypotheses. + :type closest_ref_len: int + :return: BLEU's brevity penalty. + :rtype: float + """ + if hyp_len > closest_ref_len: + return 1 + # If hypothesis is empty, brevity penalty = 0 should result in BLEU = 0.0 + elif hyp_len == 0: + return 0 + else: + return math.exp(1 - closest_ref_len / hyp_len) + + +class SmoothingFunction: + """ + This is an implementation of the smoothing techniques + for segment-level BLEU scores that was presented in + Boxing Chen and Collin Cherry (2014) A Systematic Comparison of + Smoothing Techniques for Sentence-Level BLEU. In WMT14. + http://acl2014.org/acl2014/W14-33/pdf/W14-3346.pdf + """ + + def __init__(self, epsilon=0.1, alpha=5, k=5): + """ + This will initialize the parameters required for the various smoothing + techniques, the default values are set to the numbers used in the + experiments from Chen and Cherry (2014). + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', 'ensures', + ... 'that', 'the', 'military', 'always', 'obeys', 'the', + ... 'commands', 'of', 'the', 'party'] + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', 'ensures', + ... 'that', 'the', 'military', 'will', 'forever', 'heed', + ... 'Party', 'commands'] + + >>> chencherry = SmoothingFunction() + >>> print(sentence_bleu([reference1], hypothesis1)) # doctest: +ELLIPSIS + 0.4118... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method0)) # doctest: +ELLIPSIS + 0.4118... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method1)) # doctest: +ELLIPSIS + 0.4118... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method2)) # doctest: +ELLIPSIS + 0.4452... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method3)) # doctest: +ELLIPSIS + 0.4118... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method4)) # doctest: +ELLIPSIS + 0.4118... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method5)) # doctest: +ELLIPSIS + 0.4905... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method6)) # doctest: +ELLIPSIS + 0.4135... + >>> print(sentence_bleu([reference1], hypothesis1, smoothing_function=chencherry.method7)) # doctest: +ELLIPSIS + 0.4905... + + :param epsilon: the epsilon value use in method 1 + :type epsilon: float + :param alpha: the alpha value use in method 6 + :type alpha: int + :param k: the k value use in method 4 + :type k: int + """ + self.epsilon = epsilon + self.alpha = alpha + self.k = k + + def method0(self, p_n, *args, **kwargs): + """ + No smoothing. + """ + p_n_new = [] + for i, p_i in enumerate(p_n): + if p_i.numerator != 0: + p_n_new.append(p_i) + else: + _msg = str( + "\nThe hypothesis contains 0 counts of {}-gram overlaps.\n" + "Therefore the BLEU score evaluates to 0, independently of\n" + "how many N-gram overlaps of lower order it contains.\n" + "Consider using lower n-gram order or use " + "SmoothingFunction()" + ).format(i + 1) + warnings.warn(_msg) + # When numerator==0 where denonminator==0 or !=0, the result + # for the precision score should be equal to 0 or undefined. + # Due to BLEU geometric mean computation in logarithm space, + # we we need to take the return sys.float_info.min such that + # math.log(sys.float_info.min) returns a 0 precision score. + p_n_new.append(sys.float_info.min) + return p_n_new + + def method1(self, p_n, *args, **kwargs): + """ + Smoothing method 1: Add *epsilon* counts to precision with 0 counts. + """ + return [ + (p_i.numerator + self.epsilon) / p_i.denominator + if p_i.numerator == 0 + else p_i + for p_i in p_n + ] + + def method2(self, p_n, *args, **kwargs): + """ + Smoothing method 2: Add 1 to both numerator and denominator from + Chin-Yew Lin and Franz Josef Och (2004) ORANGE: a Method for + Evaluating Automatic Evaluation Metrics for Machine Translation. + In COLING 2004. + """ + return [ + Fraction(p_n[i].numerator + 1, p_n[i].denominator + 1, _normalize=False) + if i != 0 + else p_n[0] + for i in range(len(p_n)) + ] + + def method3(self, p_n, *args, **kwargs): + """ + Smoothing method 3: NIST geometric sequence smoothing + The smoothing is computed by taking 1 / ( 2^k ), instead of 0, for each + precision score whose matching n-gram count is null. + k is 1 for the first 'n' value for which the n-gram match count is null/ + + For example, if the text contains: + + - one 2-gram match + - and (consequently) two 1-gram matches + + the n-gram count for each individual precision score would be: + + - n=1 => prec_count = 2 (two unigrams) + - n=2 => prec_count = 1 (one bigram) + - n=3 => prec_count = 1/2 (no trigram, taking 'smoothed' value of 1 / ( 2^k ), with k=1) + - n=4 => prec_count = 1/4 (no fourgram, taking 'smoothed' value of 1 / ( 2^k ), with k=2) + """ + incvnt = 1 # From the mteval-v13a.pl, it's referred to as k. + for i, p_i in enumerate(p_n): + if p_i.numerator == 0: + p_n[i] = 1 / (2**incvnt * p_i.denominator) + incvnt += 1 + return p_n + + def method4(self, p_n, references, hypothesis, hyp_len=None, *args, **kwargs): + """ + Smoothing method 4: + Shorter translations may have inflated precision values due to having + smaller denominators; therefore, we give them proportionally + smaller smoothed counts. Instead of scaling to 1/(2^k), Chen and Cherry + suggests dividing by 1/ln(len(T)), where T is the length of the translation. + """ + incvnt = 1 + hyp_len = hyp_len if hyp_len else len(hypothesis) + for i, p_i in enumerate(p_n): + if p_i.numerator == 0 and hyp_len > 1: + # incvnt = i + 1 * self.k / math.log( + # hyp_len + # ) # Note that this K is different from the K from NIST. + # p_n[i] = incvnt / p_i.denominator\ + numerator = 1 / (2**incvnt * self.k / math.log(hyp_len)) + p_n[i] = numerator / p_i.denominator + incvnt += 1 + return p_n + + def method5(self, p_n, references, hypothesis, hyp_len=None, *args, **kwargs): + """ + Smoothing method 5: + The matched counts for similar values of n should be similar. To a + calculate the n-gram matched count, it averages the n−1, n and n+1 gram + matched counts. + """ + hyp_len = hyp_len if hyp_len else len(hypothesis) + m = {} + # Requires an precision value for an addition ngram order. + p_n_plus1 = p_n + [modified_precision(references, hypothesis, 5)] + m[-1] = p_n[0] + 1 + for i, p_i in enumerate(p_n): + p_n[i] = (m[i - 1] + p_i + p_n_plus1[i + 1]) / 3 + m[i] = p_n[i] + return p_n + + def method6(self, p_n, references, hypothesis, hyp_len=None, *args, **kwargs): + """ + Smoothing method 6: + Interpolates the maximum likelihood estimate of the precision *p_n* with + a prior estimate *pi0*. The prior is estimated by assuming that the ratio + between pn and pn−1 will be the same as that between pn−1 and pn−2; from + Gao and He (2013) Training MRF-Based Phrase Translation Models using + Gradient Ascent. In NAACL. + """ + hyp_len = hyp_len if hyp_len else len(hypothesis) + # This smoothing only works when p_1 and p_2 is non-zero. + # Raise an error with an appropriate message when the input is too short + # to use this smoothing technique. + assert p_n[2], "This smoothing method requires non-zero precision for bigrams." + for i, p_i in enumerate(p_n): + if i in [0, 1]: # Skips the first 2 orders of ngrams. + continue + else: + pi0 = 0 if p_n[i - 2] == 0 else p_n[i - 1] ** 2 / p_n[i - 2] + # No. of ngrams in translation that matches the reference. + m = p_i.numerator + # No. of ngrams in translation. + l = sum(1 for _ in ngrams(hypothesis, i + 1)) + # Calculates the interpolated precision. + p_n[i] = (m + self.alpha * pi0) / (l + self.alpha) + return p_n + + def method7(self, p_n, references, hypothesis, hyp_len=None, *args, **kwargs): + """ + Smoothing method 7: + Interpolates methods 4 and 5. + """ + hyp_len = hyp_len if hyp_len else len(hypothesis) + p_n = self.method4(p_n, references, hypothesis, hyp_len) + p_n = self.method5(p_n, references, hypothesis, hyp_len) + return p_n diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/chrf_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/chrf_score.py new file mode 100644 index 0000000..ca81696 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/chrf_score.py @@ -0,0 +1,222 @@ +# Natural Language Toolkit: ChrF score +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Maja Popovic +# Contributors: Liling Tan, Aleš Tamchyna (Memsource) +# URL: +# For license information, see LICENSE.TXT + +""" ChrF score implementation """ +import re +from collections import Counter, defaultdict + +from nltk.util import ngrams + + +def sentence_chrf( + reference, hypothesis, min_len=1, max_len=6, beta=3.0, ignore_whitespace=True +): + """ + Calculates the sentence level CHRF (Character n-gram F-score) described in + - Maja Popovic. 2015. CHRF: Character n-gram F-score for Automatic MT Evaluation. + In Proceedings of the 10th Workshop on Machine Translation. + https://www.statmt.org/wmt15/pdf/WMT49.pdf + - Maja Popovic. 2016. CHRF Deconstructed: β Parameters and n-gram Weights. + In Proceedings of the 1st Conference on Machine Translation. + https://www.statmt.org/wmt16/pdf/W16-2341.pdf + + This implementation of CHRF only supports a single reference at the moment. + + For details not reported in the paper, consult Maja Popovic's original + implementation: https://github.com/m-popovic/chrF + + The code should output results equivalent to running CHRF++ with the + following options: -nw 0 -b 3 + + An example from the original BLEU paper + https://www.aclweb.org/anthology/P02-1040.pdf + + >>> ref1 = str('It is a guide to action that ensures that the military ' + ... 'will forever heed Party commands').split() + >>> hyp1 = str('It is a guide to action which ensures that the military ' + ... 'always obeys the commands of the party').split() + >>> hyp2 = str('It is to insure the troops forever hearing the activity ' + ... 'guidebook that party direct').split() + >>> sentence_chrf(ref1, hyp1) # doctest: +ELLIPSIS + 0.6349... + >>> sentence_chrf(ref1, hyp2) # doctest: +ELLIPSIS + 0.3330... + + The infamous "the the the ... " example + + >>> ref = 'the cat is on the mat'.split() + >>> hyp = 'the the the the the the the'.split() + >>> sentence_chrf(ref, hyp) # doctest: +ELLIPSIS + 0.1468... + + An example to show that this function allows users to use strings instead of + tokens, i.e. list(str) as inputs. + + >>> ref1 = str('It is a guide to action that ensures that the military ' + ... 'will forever heed Party commands') + >>> hyp1 = str('It is a guide to action which ensures that the military ' + ... 'always obeys the commands of the party') + >>> sentence_chrf(ref1, hyp1) # doctest: +ELLIPSIS + 0.6349... + >>> type(ref1) == type(hyp1) == str + True + >>> sentence_chrf(ref1.split(), hyp1.split()) # doctest: +ELLIPSIS + 0.6349... + + To skip the unigrams and only use 2- to 3-grams: + + >>> sentence_chrf(ref1, hyp1, min_len=2, max_len=3) # doctest: +ELLIPSIS + 0.6617... + + :param references: reference sentence + :type references: list(str) / str + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) / str + :param min_len: The minimum order of n-gram this function should extract. + :type min_len: int + :param max_len: The maximum order of n-gram this function should extract. + :type max_len: int + :param beta: the parameter to assign more importance to recall over precision + :type beta: float + :param ignore_whitespace: ignore whitespace characters in scoring + :type ignore_whitespace: bool + :return: the sentence level CHRF score. + :rtype: float + """ + return corpus_chrf( + [reference], + [hypothesis], + min_len, + max_len, + beta=beta, + ignore_whitespace=ignore_whitespace, + ) + + +def _preprocess(sent, ignore_whitespace): + if type(sent) != str: + # turn list of tokens into a string + sent = " ".join(sent) + + if ignore_whitespace: + sent = re.sub(r"\s+", "", sent) + return sent + + +def chrf_precision_recall_fscore_support( + reference, hypothesis, n, beta=3.0, epsilon=1e-16 +): + """ + This function computes the precision, recall and fscore from the ngram + overlaps. It returns the `support` which is the true positive score. + + By underspecifying the input type, the function will be agnostic as to how + it computes the ngrams and simply take the whichever element in the list; + it could be either token or character. + + :param reference: The reference sentence. + :type reference: list + :param hypothesis: The hypothesis sentence. + :type hypothesis: list + :param n: Extract up to the n-th order ngrams + :type n: int + :param beta: The parameter to assign more importance to recall over precision. + :type beta: float + :param epsilon: The fallback value if the hypothesis or reference is empty. + :type epsilon: float + :return: Returns the precision, recall and f-score and support (true positive). + :rtype: tuple(float) + """ + ref_ngrams = Counter(ngrams(reference, n)) + hyp_ngrams = Counter(ngrams(hypothesis, n)) + + # calculate the number of ngram matches + overlap_ngrams = ref_ngrams & hyp_ngrams + tp = sum(overlap_ngrams.values()) # True positives. + tpfp = sum(hyp_ngrams.values()) # True positives + False positives. + tpfn = sum(ref_ngrams.values()) # True positives + False negatives. + + try: + prec = tp / tpfp # precision + rec = tp / tpfn # recall + factor = beta**2 + fscore = (1 + factor) * (prec * rec) / (factor * prec + rec) + except ZeroDivisionError: + prec = rec = fscore = epsilon + return prec, rec, fscore, tp + + +def corpus_chrf( + references, hypotheses, min_len=1, max_len=6, beta=3.0, ignore_whitespace=True +): + """ + Calculates the corpus level CHRF (Character n-gram F-score), it is the + macro-averaged value of the sentence/segment level CHRF score. + + This implementation of CHRF only supports a single reference at the moment. + + >>> ref1 = str('It is a guide to action that ensures that the military ' + ... 'will forever heed Party commands').split() + >>> ref2 = str('It is the guiding principle which guarantees the military ' + ... 'forces always being under the command of the Party').split() + >>> + >>> hyp1 = str('It is a guide to action which ensures that the military ' + ... 'always obeys the commands of the party').split() + >>> hyp2 = str('It is to insure the troops forever hearing the activity ' + ... 'guidebook that party direct') + >>> corpus_chrf([ref1, ref2, ref1, ref2], [hyp1, hyp2, hyp2, hyp1]) # doctest: +ELLIPSIS + 0.3910... + + :param references: a corpus of list of reference sentences, w.r.t. hypotheses + :type references: list(list(str)) + :param hypotheses: a list of hypothesis sentences + :type hypotheses: list(list(str)) + :param min_len: The minimum order of n-gram this function should extract. + :type min_len: int + :param max_len: The maximum order of n-gram this function should extract. + :type max_len: int + :param beta: the parameter to assign more importance to recall over precision + :type beta: float + :param ignore_whitespace: ignore whitespace characters in scoring + :type ignore_whitespace: bool + :return: the sentence level CHRF score. + :rtype: float + """ + + assert len(references) == len( + hypotheses + ), "The number of hypotheses and their references should be the same" + num_sents = len(hypotheses) + + # Keep f-scores for each n-gram order separate + ngram_fscores = defaultdict(lambda: list()) + + # Iterate through each hypothesis and their corresponding references. + for reference, hypothesis in zip(references, hypotheses): + + # preprocess both reference and hypothesis + reference = _preprocess(reference, ignore_whitespace) + hypothesis = _preprocess(hypothesis, ignore_whitespace) + + # Calculate f-scores for each sentence and for each n-gram order + # separately. + for n in range(min_len, max_len + 1): + # Compute the precision, recall, fscore and support. + prec, rec, fscore, tp = chrf_precision_recall_fscore_support( + reference, hypothesis, n, beta=beta + ) + ngram_fscores[n].append(fscore) + + # how many n-gram sizes + num_ngram_sizes = len(ngram_fscores) + + # sum of f-scores over all sentences for each n-gram order + total_scores = [sum(fscores) for n, fscores in ngram_fscores.items()] + + # macro-average over n-gram orders and over all sentences + return (sum(total_scores) / num_ngram_sizes) / num_sents diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gale_church.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gale_church.py new file mode 100644 index 0000000..f963f10 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gale_church.py @@ -0,0 +1,263 @@ +# Natural Language Toolkit: Gale-Church Aligner +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Torsten Marek +# Contributor: Cassidy Laidlaw, Liling Tan +# URL: +# For license information, see LICENSE.TXT + +""" + +A port of the Gale-Church Aligner. + +Gale & Church (1993), A Program for Aligning Sentences in Bilingual Corpora. +https://aclweb.org/anthology/J93-1004.pdf + +""" + +import math + +try: + from norm import logsf as norm_logsf + from scipy.stats import norm +except ImportError: + + def erfcc(x): + """Complementary error function.""" + z = abs(x) + t = 1 / (1 + 0.5 * z) + r = t * math.exp( + -z * z + - 1.26551223 + + t + * ( + 1.00002368 + + t + * ( + 0.37409196 + + t + * ( + 0.09678418 + + t + * ( + -0.18628806 + + t + * ( + 0.27886807 + + t + * ( + -1.13520398 + + t + * (1.48851587 + t * (-0.82215223 + t * 0.17087277)) + ) + ) + ) + ) + ) + ) + ) + if x >= 0.0: + return r + else: + return 2.0 - r + + def norm_cdf(x): + """Return the area under the normal distribution from M{-∞..x}.""" + return 1 - 0.5 * erfcc(x / math.sqrt(2)) + + def norm_logsf(x): + try: + return math.log(1 - norm_cdf(x)) + except ValueError: + return float("-inf") + + +LOG2 = math.log(2) + + +class LanguageIndependent: + # These are the language-independent probabilities and parameters + # given in Gale & Church + + # for the computation, l_1 is always the language with less characters + PRIORS = { + (1, 0): 0.0099, + (0, 1): 0.0099, + (1, 1): 0.89, + (2, 1): 0.089, + (1, 2): 0.089, + (2, 2): 0.011, + } + + AVERAGE_CHARACTERS = 1 + VARIANCE_CHARACTERS = 6.8 + + +def trace(backlinks, source_sents_lens, target_sents_lens): + """ + Traverse the alignment cost from the tracebacks and retrieves + appropriate sentence pairs. + + :param backlinks: A dictionary where the key is the alignment points and value is the cost (referencing the LanguageIndependent.PRIORS) + :type backlinks: dict + :param source_sents_lens: A list of target sentences' lengths + :type source_sents_lens: list(int) + :param target_sents_lens: A list of target sentences' lengths + :type target_sents_lens: list(int) + """ + links = [] + position = (len(source_sents_lens), len(target_sents_lens)) + while position != (0, 0) and all(p >= 0 for p in position): + try: + s, t = backlinks[position] + except TypeError: + position = (position[0] - 1, position[1] - 1) + continue + for i in range(s): + for j in range(t): + links.append((position[0] - i - 1, position[1] - j - 1)) + position = (position[0] - s, position[1] - t) + + return links[::-1] + + +def align_log_prob(i, j, source_sents, target_sents, alignment, params): + """Returns the log probability of the two sentences C{source_sents[i]}, C{target_sents[j]} + being aligned with a specific C{alignment}. + + @param i: The offset of the source sentence. + @param j: The offset of the target sentence. + @param source_sents: The list of source sentence lengths. + @param target_sents: The list of target sentence lengths. + @param alignment: The alignment type, a tuple of two integers. + @param params: The sentence alignment parameters. + + @returns: The log probability of a specific alignment between the two sentences, given the parameters. + """ + l_s = sum(source_sents[i - offset - 1] for offset in range(alignment[0])) + l_t = sum(target_sents[j - offset - 1] for offset in range(alignment[1])) + try: + # actually, the paper says l_s * params.VARIANCE_CHARACTERS, this is based on the C + # reference implementation. With l_s in the denominator, insertions are impossible. + m = (l_s + l_t / params.AVERAGE_CHARACTERS) / 2 + delta = (l_s * params.AVERAGE_CHARACTERS - l_t) / math.sqrt( + m * params.VARIANCE_CHARACTERS + ) + except ZeroDivisionError: + return float("-inf") + + return -(LOG2 + norm_logsf(abs(delta)) + math.log(params.PRIORS[alignment])) + + +def align_blocks(source_sents_lens, target_sents_lens, params=LanguageIndependent): + """Return the sentence alignment of two text blocks (usually paragraphs). + + >>> align_blocks([5,5,5], [7,7,7]) + [(0, 0), (1, 1), (2, 2)] + >>> align_blocks([10,5,5], [12,20]) + [(0, 0), (1, 1), (2, 1)] + >>> align_blocks([12,20], [10,5,5]) + [(0, 0), (1, 1), (1, 2)] + >>> align_blocks([10,2,10,10,2,10], [12,3,20,3,12]) + [(0, 0), (1, 1), (2, 2), (3, 2), (4, 3), (5, 4)] + + @param source_sents_lens: The list of source sentence lengths. + @param target_sents_lens: The list of target sentence lengths. + @param params: the sentence alignment parameters. + @return: The sentence alignments, a list of index pairs. + """ + + alignment_types = list(params.PRIORS.keys()) + + # there are always three rows in the history (with the last of them being filled) + D = [[]] + + backlinks = {} + + for i in range(len(source_sents_lens) + 1): + for j in range(len(target_sents_lens) + 1): + min_dist = float("inf") + min_align = None + for a in alignment_types: + prev_i = -1 - a[0] + prev_j = j - a[1] + if prev_i < -len(D) or prev_j < 0: + continue + p = D[prev_i][prev_j] + align_log_prob( + i, j, source_sents_lens, target_sents_lens, a, params + ) + if p < min_dist: + min_dist = p + min_align = a + + if min_dist == float("inf"): + min_dist = 0 + + backlinks[(i, j)] = min_align + D[-1].append(min_dist) + + if len(D) > 2: + D.pop(0) + D.append([]) + + return trace(backlinks, source_sents_lens, target_sents_lens) + + +def align_texts(source_blocks, target_blocks, params=LanguageIndependent): + """Creates the sentence alignment of two texts. + + Texts can consist of several blocks. Block boundaries cannot be crossed by sentence + alignment links. + + Each block consists of a list that contains the lengths (in characters) of the sentences + in this block. + + @param source_blocks: The list of blocks in the source text. + @param target_blocks: The list of blocks in the target text. + @param params: the sentence alignment parameters. + + @returns: A list of sentence alignment lists + """ + if len(source_blocks) != len(target_blocks): + raise ValueError( + "Source and target texts do not have the same number of blocks." + ) + + return [ + align_blocks(source_block, target_block, params) + for source_block, target_block in zip(source_blocks, target_blocks) + ] + + +# File I/O functions; may belong in a corpus reader + + +def split_at(it, split_value): + """Splits an iterator C{it} at values of C{split_value}. + + Each instance of C{split_value} is swallowed. The iterator produces + subiterators which need to be consumed fully before the next subiterator + can be used. + """ + + def _chunk_iterator(first): + v = first + while v != split_value: + yield v + v = it.next() + + while True: + yield _chunk_iterator(it.next()) + + +def parse_token_stream(stream, soft_delimiter, hard_delimiter): + """Parses a stream of tokens and splits it into sentences (using C{soft_delimiter} tokens) + and blocks (using C{hard_delimiter} tokens) for use with the L{align_texts} function. + """ + return [ + [ + sum(len(token) for token in sentence_it) + for sentence_it in split_at(block_it, soft_delimiter) + ] + for block_it in split_at(stream, hard_delimiter) + ] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gdfa.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gdfa.py new file mode 100644 index 0000000..4bcda4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gdfa.py @@ -0,0 +1,138 @@ +# Natural Language Toolkit: GDFA word alignment symmetrization +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Liling Tan +# URL: +# For license information, see LICENSE.TXT + +from collections import defaultdict + + +def grow_diag_final_and(srclen, trglen, e2f, f2e): + """ + This module symmetrisatizes the source-to-target and target-to-source + word alignment output and produces, aka. GDFA algorithm (Koehn, 2005). + + Step 1: Find the intersection of the bidirectional alignment. + + Step 2: Search for additional neighbor alignment points to be added, given + these criteria: (i) neighbor alignments points are not in the + intersection and (ii) neighbor alignments are in the union. + + Step 3: Add all other alignment points that are not in the intersection, not in + the neighboring alignments that met the criteria but in the original + forward/backward alignment outputs. + + >>> forw = ('0-0 2-1 9-2 21-3 10-4 7-5 11-6 9-7 12-8 1-9 3-10 ' + ... '4-11 17-12 17-13 25-14 13-15 24-16 11-17 28-18') + >>> back = ('0-0 1-9 2-9 3-10 4-11 5-12 6-6 7-5 8-6 9-7 10-4 ' + ... '11-6 12-8 13-12 15-12 17-13 18-13 19-12 20-13 ' + ... '21-3 22-12 23-14 24-17 25-15 26-17 27-18 28-18') + >>> srctext = ("この よう な ハロー 白色 わい 星 の L 関数 " + ... "は L と 共 に 不連続 に 増加 する こと が " + ... "期待 さ れる こと を 示し た 。") + >>> trgtext = ("Therefore , we expect that the luminosity function " + ... "of such halo white dwarfs increases discontinuously " + ... "with the luminosity .") + >>> srclen = len(srctext.split()) + >>> trglen = len(trgtext.split()) + >>> + >>> gdfa = grow_diag_final_and(srclen, trglen, forw, back) + >>> gdfa == sorted(set([(28, 18), (6, 6), (24, 17), (2, 1), (15, 12), (13, 12), + ... (2, 9), (3, 10), (26, 17), (25, 15), (8, 6), (9, 7), (20, + ... 13), (18, 13), (0, 0), (10, 4), (13, 15), (23, 14), (7, 5), + ... (25, 14), (1, 9), (17, 13), (4, 11), (11, 17), (9, 2), (22, + ... 12), (27, 18), (24, 16), (21, 3), (19, 12), (17, 12), (5, + ... 12), (11, 6), (12, 8)])) + True + + References: + Koehn, P., A. Axelrod, A. Birch, C. Callison, M. Osborne, and D. Talbot. + 2005. Edinburgh System Description for the 2005 IWSLT Speech + Translation Evaluation. In MT Eval Workshop. + + :type srclen: int + :param srclen: the number of tokens in the source language + :type trglen: int + :param trglen: the number of tokens in the target language + :type e2f: str + :param e2f: the forward word alignment outputs from source-to-target + language (in pharaoh output format) + :type f2e: str + :param f2e: the backward word alignment outputs from target-to-source + language (in pharaoh output format) + :rtype: set(tuple(int)) + :return: the symmetrized alignment points from the GDFA algorithm + """ + + # Converts pharaoh text format into list of tuples. + e2f = [tuple(map(int, a.split("-"))) for a in e2f.split()] + f2e = [tuple(map(int, a.split("-"))) for a in f2e.split()] + + neighbors = [(-1, 0), (0, -1), (1, 0), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)] + alignment = set(e2f).intersection(set(f2e)) # Find the intersection. + union = set(e2f).union(set(f2e)) + + # *aligned* is used to check if neighbors are aligned in grow_diag() + aligned = defaultdict(set) + for i, j in alignment: + aligned["e"].add(i) + aligned["f"].add(j) + + def grow_diag(): + """ + Search for the neighbor points and them to the intersected alignment + points if criteria are met. + """ + prev_len = len(alignment) - 1 + # iterate until no new points added + while prev_len < len(alignment): + no_new_points = True + # for english word e = 0 ... en + for e in range(srclen): + # for foreign word f = 0 ... fn + for f in range(trglen): + # if ( e aligned with f) + if (e, f) in alignment: + # for each neighboring point (e-new, f-new) + for neighbor in neighbors: + neighbor = tuple(i + j for i, j in zip((e, f), neighbor)) + e_new, f_new = neighbor + # if ( ( e-new not aligned and f-new not aligned) + # and (e-new, f-new in union(e2f, f2e) ) + if ( + e_new not in aligned and f_new not in aligned + ) and neighbor in union: + alignment.add(neighbor) + aligned["e"].add(e_new) + aligned["f"].add(f_new) + prev_len += 1 + no_new_points = False + # iterate until no new points added + if no_new_points: + break + + def final_and(a): + """ + Adds remaining points that are not in the intersection, not in the + neighboring alignments but in the original *e2f* and *f2e* alignments + """ + # for english word e = 0 ... en + for e_new in range(srclen): + # for foreign word f = 0 ... fn + for f_new in range(trglen): + # if ( ( e-new not aligned and f-new not aligned) + # and (e-new, f-new in union(e2f, f2e) ) + if ( + e_new not in aligned + and f_new not in aligned + and (e_new, f_new) in union + ): + alignment.add((e_new, f_new)) + aligned["e"].add(e_new) + aligned["f"].add(f_new) + + grow_diag() + final_and(e2f) + final_and(f2e) + return sorted(alignment) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gleu_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gleu_score.py new file mode 100644 index 0000000..9054ecc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/gleu_score.py @@ -0,0 +1,190 @@ +# Natural Language Toolkit: GLEU Score +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: +# Contributors: Mike Schuster, Michael Wayne Goodman, Liling Tan +# URL: +# For license information, see LICENSE.TXT + +""" GLEU score implementation. """ + +from collections import Counter + +from nltk.util import everygrams, ngrams + + +def sentence_gleu(references, hypothesis, min_len=1, max_len=4): + """ + Calculates the sentence level GLEU (Google-BLEU) score described in + + Yonghui Wu, Mike Schuster, Zhifeng Chen, Quoc V. Le, Mohammad Norouzi, + Wolfgang Macherey, Maxim Krikun, Yuan Cao, Qin Gao, Klaus Macherey, + Jeff Klingner, Apurva Shah, Melvin Johnson, Xiaobing Liu, Lukasz Kaiser, + Stephan Gouws, Yoshikiyo Kato, Taku Kudo, Hideto Kazawa, Keith Stevens, + George Kurian, Nishant Patil, Wei Wang, Cliff Young, Jason Smith, + Jason Riesa, Alex Rudnick, Oriol Vinyals, Greg Corrado, Macduff Hughes, + Jeffrey Dean. (2016) Google’s Neural Machine Translation System: + Bridging the Gap between Human and Machine Translation. + eprint arXiv:1609.08144. https://arxiv.org/pdf/1609.08144v2.pdf + Retrieved on 27 Oct 2016. + + From Wu et al. (2016): + "The BLEU score has some undesirable properties when used for single + sentences, as it was designed to be a corpus measure. We therefore + use a slightly different score for our RL experiments which we call + the 'GLEU score'. For the GLEU score, we record all sub-sequences of + 1, 2, 3 or 4 tokens in output and target sequence (n-grams). We then + compute a recall, which is the ratio of the number of matching n-grams + to the number of total n-grams in the target (ground truth) sequence, + and a precision, which is the ratio of the number of matching n-grams + to the number of total n-grams in the generated output sequence. Then + GLEU score is simply the minimum of recall and precision. This GLEU + score's range is always between 0 (no matches) and 1 (all match) and + it is symmetrical when switching output and target. According to + our experiments, GLEU score correlates quite well with the BLEU + metric on a corpus level but does not have its drawbacks for our per + sentence reward objective." + + Note: The initial implementation only allowed a single reference, but now + a list of references is required (which is consistent with + bleu_score.sentence_bleu()). + + The infamous "the the the ... " example + + >>> ref = 'the cat is on the mat'.split() + >>> hyp = 'the the the the the the the'.split() + >>> sentence_gleu([ref], hyp) # doctest: +ELLIPSIS + 0.0909... + + An example to evaluate normal machine translation outputs + + >>> ref1 = str('It is a guide to action that ensures that the military ' + ... 'will forever heed Party commands').split() + >>> hyp1 = str('It is a guide to action which ensures that the military ' + ... 'always obeys the commands of the party').split() + >>> hyp2 = str('It is to insure the troops forever hearing the activity ' + ... 'guidebook that party direct').split() + >>> sentence_gleu([ref1], hyp1) # doctest: +ELLIPSIS + 0.4393... + >>> sentence_gleu([ref1], hyp2) # doctest: +ELLIPSIS + 0.1206... + + :param references: a list of reference sentences + :type references: list(list(str)) + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) + :param min_len: The minimum order of n-gram this function should extract. + :type min_len: int + :param max_len: The maximum order of n-gram this function should extract. + :type max_len: int + :return: the sentence level GLEU score. + :rtype: float + """ + return corpus_gleu([references], [hypothesis], min_len=min_len, max_len=max_len) + + +def corpus_gleu(list_of_references, hypotheses, min_len=1, max_len=4): + """ + Calculate a single corpus-level GLEU score (aka. system-level GLEU) for all + the hypotheses and their respective references. + + Instead of averaging the sentence level GLEU scores (i.e. macro-average + precision), Wu et al. (2016) sum up the matching tokens and the max of + hypothesis and reference tokens for each sentence, then compute using the + aggregate values. + + From Mike Schuster (via email): + "For the corpus, we just add up the two statistics n_match and + n_all = max(n_all_output, n_all_target) for all sentences, then + calculate gleu_score = n_match / n_all, so it is not just a mean of + the sentence gleu scores (in our case, longer sentences count more, + which I think makes sense as they are more difficult to translate)." + + >>> hyp1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + >>> ref1a = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', 'forever', + ... 'heed', 'Party', 'commands'] + >>> ref1b = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', 'Party'] + >>> ref1c = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + + >>> hyp2 = ['he', 'read', 'the', 'book', 'because', 'he', 'was', + ... 'interested', 'in', 'world', 'history'] + >>> ref2a = ['he', 'was', 'interested', 'in', 'world', 'history', + ... 'because', 'he', 'read', 'the', 'book'] + + >>> list_of_references = [[ref1a, ref1b, ref1c], [ref2a]] + >>> hypotheses = [hyp1, hyp2] + >>> corpus_gleu(list_of_references, hypotheses) # doctest: +ELLIPSIS + 0.5673... + + The example below show that corpus_gleu() is different from averaging + sentence_gleu() for hypotheses + + >>> score1 = sentence_gleu([ref1a], hyp1) + >>> score2 = sentence_gleu([ref2a], hyp2) + >>> (score1 + score2) / 2 # doctest: +ELLIPSIS + 0.6144... + + :param list_of_references: a list of reference sentences, w.r.t. hypotheses + :type list_of_references: list(list(list(str))) + :param hypotheses: a list of hypothesis sentences + :type hypotheses: list(list(str)) + :param min_len: The minimum order of n-gram this function should extract. + :type min_len: int + :param max_len: The maximum order of n-gram this function should extract. + :type max_len: int + :return: The corpus-level GLEU score. + :rtype: float + """ + # sanity check + assert len(list_of_references) == len( + hypotheses + ), "The number of hypotheses and their reference(s) should be the same" + + # sum matches and max-token-lengths over all sentences + corpus_n_match = 0 + corpus_n_all = 0 + + for references, hypothesis in zip(list_of_references, hypotheses): + hyp_ngrams = Counter(everygrams(hypothesis, min_len, max_len)) + tpfp = sum(hyp_ngrams.values()) # True positives + False positives. + + hyp_counts = [] + for reference in references: + ref_ngrams = Counter(everygrams(reference, min_len, max_len)) + tpfn = sum(ref_ngrams.values()) # True positives + False negatives. + + overlap_ngrams = ref_ngrams & hyp_ngrams + tp = sum(overlap_ngrams.values()) # True positives. + + # While GLEU is defined as the minimum of precision and + # recall, we can reduce the number of division operations by one by + # instead finding the maximum of the denominators for the precision + # and recall formulae, since the numerators are the same: + # precision = tp / tpfp + # recall = tp / tpfn + # gleu_score = min(precision, recall) == tp / max(tpfp, tpfn) + n_all = max(tpfp, tpfn) + + if n_all > 0: + hyp_counts.append((tp, n_all)) + + # use the reference yielding the highest score + if hyp_counts: + n_match, n_all = max(hyp_counts, key=lambda hc: hc[0] / hc[1]) + corpus_n_match += n_match + corpus_n_all += n_all + + # corner case: empty corpus or empty references---don't divide by zero! + if corpus_n_all == 0: + gleu_score = 0.0 + else: + gleu_score = corpus_n_match / corpus_n_all + + return gleu_score diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm1.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm1.py new file mode 100644 index 0000000..2ee7c0e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm1.py @@ -0,0 +1,251 @@ +# Natural Language Toolkit: IBM Model 1 +# +# Copyright (C) 2001-2013 NLTK Project +# Author: Chin Yee Lee +# Hengfeng Li +# Ruxin Hou +# Calvin Tanujaya Lim +# Based on earlier version by: +# Will Zhang +# Guan Gui +# URL: +# For license information, see LICENSE.TXT + +""" +Lexical translation model that ignores word order. + +In IBM Model 1, word order is ignored for simplicity. As long as the +word alignments are equivalent, it doesn't matter where the word occurs +in the source or target sentence. Thus, the following three alignments +are equally likely:: + + Source: je mange du jambon + Target: i eat some ham + Alignment: (0,0) (1,1) (2,2) (3,3) + + Source: je mange du jambon + Target: some ham eat i + Alignment: (0,2) (1,3) (2,1) (3,1) + + Source: du jambon je mange + Target: eat i some ham + Alignment: (0,3) (1,2) (2,0) (3,1) + +Note that an alignment is represented here as +(word_index_in_target, word_index_in_source). + +The EM algorithm used in Model 1 is: + +:E step: In the training data, count how many times a source language + word is translated into a target language word, weighted by + the prior probability of the translation. + +:M step: Estimate the new probability of translation based on the + counts from the Expectation step. + +Notations +--------- + +:i: Position in the source sentence + Valid values are 0 (for NULL), 1, 2, ..., length of source sentence +:j: Position in the target sentence + Valid values are 1, 2, ..., length of target sentence +:s: A word in the source language +:t: A word in the target language + +References +---------- + +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +import warnings +from collections import defaultdict + +from nltk.translate import AlignedSent, Alignment, IBMModel +from nltk.translate.ibm_model import Counts + + +class IBMModel1(IBMModel): + """ + Lexical translation model that ignores word order + + >>> bitext = [] + >>> bitext.append(AlignedSent(['klein', 'ist', 'das', 'haus'], ['the', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus', 'ist', 'ja', 'groß'], ['the', 'house', 'is', 'big'])) + >>> bitext.append(AlignedSent(['das', 'buch', 'ist', 'ja', 'klein'], ['the', 'book', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus'], ['the', 'house'])) + >>> bitext.append(AlignedSent(['das', 'buch'], ['the', 'book'])) + >>> bitext.append(AlignedSent(['ein', 'buch'], ['a', 'book'])) + + >>> ibm1 = IBMModel1(bitext, 5) + + >>> print(round(ibm1.translation_table['buch']['book'], 3)) + 0.889 + >>> print(round(ibm1.translation_table['das']['book'], 3)) + 0.062 + >>> print(round(ibm1.translation_table['buch'][None], 3)) + 0.113 + >>> print(round(ibm1.translation_table['ja'][None], 3)) + 0.073 + + >>> test_sentence = bitext[2] + >>> test_sentence.words + ['das', 'buch', 'ist', 'ja', 'klein'] + >>> test_sentence.mots + ['the', 'book', 'is', 'small'] + >>> test_sentence.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, 2), (4, 3)]) + + """ + + def __init__(self, sentence_aligned_corpus, iterations, probability_tables=None): + """ + Train on ``sentence_aligned_corpus`` and create a lexical + translation model. + + Translation direction is from ``AlignedSent.mots`` to + ``AlignedSent.words``. + + :param sentence_aligned_corpus: Sentence-aligned parallel corpus + :type sentence_aligned_corpus: list(AlignedSent) + + :param iterations: Number of iterations to run training algorithm + :type iterations: int + + :param probability_tables: Optional. Use this to pass in custom + probability values. If not specified, probabilities will be + set to a uniform distribution, or some other sensible value. + If specified, the following entry must be present: + ``translation_table``. + See ``IBMModel`` for the type and purpose of this table. + :type probability_tables: dict[str]: object + """ + super().__init__(sentence_aligned_corpus) + + if probability_tables is None: + self.set_uniform_probabilities(sentence_aligned_corpus) + else: + # Set user-defined probabilities + self.translation_table = probability_tables["translation_table"] + + for n in range(0, iterations): + self.train(sentence_aligned_corpus) + + self.align_all(sentence_aligned_corpus) + + def set_uniform_probabilities(self, sentence_aligned_corpus): + initial_prob = 1 / len(self.trg_vocab) + if initial_prob < IBMModel.MIN_PROB: + warnings.warn( + "Target language vocabulary is too large (" + + str(len(self.trg_vocab)) + + " words). " + "Results may be less accurate." + ) + + for t in self.trg_vocab: + self.translation_table[t] = defaultdict(lambda: initial_prob) + + def train(self, parallel_corpus): + counts = Counts() + for aligned_sentence in parallel_corpus: + trg_sentence = aligned_sentence.words + src_sentence = [None] + aligned_sentence.mots + + # E step (a): Compute normalization factors to weigh counts + total_count = self.prob_all_alignments(src_sentence, trg_sentence) + + # E step (b): Collect counts + for t in trg_sentence: + for s in src_sentence: + count = self.prob_alignment_point(s, t) + normalized_count = count / total_count[t] + counts.t_given_s[t][s] += normalized_count + counts.any_t_given_s[s] += normalized_count + + # M step: Update probabilities with maximum likelihood estimate + self.maximize_lexical_translation_probabilities(counts) + + def prob_all_alignments(self, src_sentence, trg_sentence): + """ + Computes the probability of all possible word alignments, + expressed as a marginal distribution over target words t + + Each entry in the return value represents the contribution to + the total alignment probability by the target word t. + + To obtain probability(alignment | src_sentence, trg_sentence), + simply sum the entries in the return value. + + :return: Probability of t for all s in ``src_sentence`` + :rtype: dict(str): float + """ + alignment_prob_for_t = defaultdict(lambda: 0.0) + for t in trg_sentence: + for s in src_sentence: + alignment_prob_for_t[t] += self.prob_alignment_point(s, t) + return alignment_prob_for_t + + def prob_alignment_point(self, s, t): + """ + Probability that word ``t`` in the target sentence is aligned to + word ``s`` in the source sentence + """ + return self.translation_table[t][s] + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + """ + prob = 1.0 + + for j, i in enumerate(alignment_info.alignment): + if j == 0: + continue # skip the dummy zeroeth element + trg_word = alignment_info.trg_sentence[j] + src_word = alignment_info.src_sentence[i] + prob *= self.translation_table[trg_word][src_word] + + return max(prob, IBMModel.MIN_PROB) + + def align_all(self, parallel_corpus): + for sentence_pair in parallel_corpus: + self.align(sentence_pair) + + def align(self, sentence_pair): + """ + Determines the best word alignment for one sentence pair from + the corpus that the model was trained on. + + The best alignment will be set in ``sentence_pair`` when the + method returns. In contrast with the internal implementation of + IBM models, the word indices in the ``Alignment`` are zero- + indexed, not one-indexed. + + :param sentence_pair: A sentence in the source language and its + counterpart sentence in the target language + :type sentence_pair: AlignedSent + """ + best_alignment = [] + + for j, trg_word in enumerate(sentence_pair.words): + # Initialize trg_word to align with the NULL token + best_prob = max(self.translation_table[trg_word][None], IBMModel.MIN_PROB) + best_alignment_point = None + for i, src_word in enumerate(sentence_pair.mots): + align_prob = self.translation_table[trg_word][src_word] + if align_prob >= best_prob: # prefer newer word in case of tie + best_prob = align_prob + best_alignment_point = i + + best_alignment.append((j, best_alignment_point)) + + sentence_pair.alignment = Alignment(best_alignment) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm2.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm2.py new file mode 100644 index 0000000..4067f35 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm2.py @@ -0,0 +1,319 @@ +# Natural Language Toolkit: IBM Model 2 +# +# Copyright (C) 2001-2013 NLTK Project +# Authors: Chin Yee Lee, Hengfeng Li, Ruxin Hou, Calvin Tanujaya Lim +# URL: +# For license information, see LICENSE.TXT + +""" +Lexical translation model that considers word order. + +IBM Model 2 improves on Model 1 by accounting for word order. +An alignment probability is introduced, a(i | j,l,m), which predicts +a source word position, given its aligned target word's position. + +The EM algorithm used in Model 2 is: + +:E step: In the training data, collect counts, weighted by prior + probabilities. + + - (a) count how many times a source language word is translated + into a target language word + - (b) count how many times a particular position in the source + sentence is aligned to a particular position in the target + sentence + +:M step: Estimate new probabilities based on the counts from the E step + +Notations +--------- + +:i: Position in the source sentence + Valid values are 0 (for NULL), 1, 2, ..., length of source sentence +:j: Position in the target sentence + Valid values are 1, 2, ..., length of target sentence +:l: Number of words in the source sentence, excluding NULL +:m: Number of words in the target sentence +:s: A word in the source language +:t: A word in the target language + +References +---------- + +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +import warnings +from collections import defaultdict + +from nltk.translate import AlignedSent, Alignment, IBMModel, IBMModel1 +from nltk.translate.ibm_model import Counts + + +class IBMModel2(IBMModel): + """ + Lexical translation model that considers word order + + >>> bitext = [] + >>> bitext.append(AlignedSent(['klein', 'ist', 'das', 'haus'], ['the', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus', 'ist', 'ja', 'groß'], ['the', 'house', 'is', 'big'])) + >>> bitext.append(AlignedSent(['das', 'buch', 'ist', 'ja', 'klein'], ['the', 'book', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus'], ['the', 'house'])) + >>> bitext.append(AlignedSent(['das', 'buch'], ['the', 'book'])) + >>> bitext.append(AlignedSent(['ein', 'buch'], ['a', 'book'])) + + >>> ibm2 = IBMModel2(bitext, 5) + + >>> print(round(ibm2.translation_table['buch']['book'], 3)) + 1.0 + >>> print(round(ibm2.translation_table['das']['book'], 3)) + 0.0 + >>> print(round(ibm2.translation_table['buch'][None], 3)) + 0.0 + >>> print(round(ibm2.translation_table['ja'][None], 3)) + 0.0 + + >>> print(round(ibm2.alignment_table[1][1][2][2], 3)) + 0.939 + >>> print(round(ibm2.alignment_table[1][2][2][2], 3)) + 0.0 + >>> print(round(ibm2.alignment_table[2][2][4][5], 3)) + 1.0 + + >>> test_sentence = bitext[2] + >>> test_sentence.words + ['das', 'buch', 'ist', 'ja', 'klein'] + >>> test_sentence.mots + ['the', 'book', 'is', 'small'] + >>> test_sentence.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, 2), (4, 3)]) + + """ + + def __init__(self, sentence_aligned_corpus, iterations, probability_tables=None): + """ + Train on ``sentence_aligned_corpus`` and create a lexical + translation model and an alignment model. + + Translation direction is from ``AlignedSent.mots`` to + ``AlignedSent.words``. + + :param sentence_aligned_corpus: Sentence-aligned parallel corpus + :type sentence_aligned_corpus: list(AlignedSent) + + :param iterations: Number of iterations to run training algorithm + :type iterations: int + + :param probability_tables: Optional. Use this to pass in custom + probability values. If not specified, probabilities will be + set to a uniform distribution, or some other sensible value. + If specified, all the following entries must be present: + ``translation_table``, ``alignment_table``. + See ``IBMModel`` for the type and purpose of these tables. + :type probability_tables: dict[str]: object + """ + super().__init__(sentence_aligned_corpus) + + if probability_tables is None: + # Get translation probabilities from IBM Model 1 + # Run more iterations of training for Model 1, since it is + # faster than Model 2 + ibm1 = IBMModel1(sentence_aligned_corpus, 2 * iterations) + self.translation_table = ibm1.translation_table + self.set_uniform_probabilities(sentence_aligned_corpus) + else: + # Set user-defined probabilities + self.translation_table = probability_tables["translation_table"] + self.alignment_table = probability_tables["alignment_table"] + + for n in range(0, iterations): + self.train(sentence_aligned_corpus) + + self.align_all(sentence_aligned_corpus) + + def set_uniform_probabilities(self, sentence_aligned_corpus): + # a(i | j,l,m) = 1 / (l+1) for all i, j, l, m + l_m_combinations = set() + for aligned_sentence in sentence_aligned_corpus: + l = len(aligned_sentence.mots) + m = len(aligned_sentence.words) + if (l, m) not in l_m_combinations: + l_m_combinations.add((l, m)) + initial_prob = 1 / (l + 1) + if initial_prob < IBMModel.MIN_PROB: + warnings.warn( + "A source sentence is too long (" + + str(l) + + " words). Results may be less accurate." + ) + + for i in range(0, l + 1): + for j in range(1, m + 1): + self.alignment_table[i][j][l][m] = initial_prob + + def train(self, parallel_corpus): + counts = Model2Counts() + for aligned_sentence in parallel_corpus: + src_sentence = [None] + aligned_sentence.mots + trg_sentence = ["UNUSED"] + aligned_sentence.words # 1-indexed + l = len(aligned_sentence.mots) + m = len(aligned_sentence.words) + + # E step (a): Compute normalization factors to weigh counts + total_count = self.prob_all_alignments(src_sentence, trg_sentence) + + # E step (b): Collect counts + for j in range(1, m + 1): + t = trg_sentence[j] + for i in range(0, l + 1): + s = src_sentence[i] + count = self.prob_alignment_point(i, j, src_sentence, trg_sentence) + normalized_count = count / total_count[t] + + counts.update_lexical_translation(normalized_count, s, t) + counts.update_alignment(normalized_count, i, j, l, m) + + # M step: Update probabilities with maximum likelihood estimates + self.maximize_lexical_translation_probabilities(counts) + self.maximize_alignment_probabilities(counts) + + def maximize_alignment_probabilities(self, counts): + MIN_PROB = IBMModel.MIN_PROB + for i, j_s in counts.alignment.items(): + for j, src_sentence_lengths in j_s.items(): + for l, trg_sentence_lengths in src_sentence_lengths.items(): + for m in trg_sentence_lengths: + estimate = ( + counts.alignment[i][j][l][m] + / counts.alignment_for_any_i[j][l][m] + ) + self.alignment_table[i][j][l][m] = max(estimate, MIN_PROB) + + def prob_all_alignments(self, src_sentence, trg_sentence): + """ + Computes the probability of all possible word alignments, + expressed as a marginal distribution over target words t + + Each entry in the return value represents the contribution to + the total alignment probability by the target word t. + + To obtain probability(alignment | src_sentence, trg_sentence), + simply sum the entries in the return value. + + :return: Probability of t for all s in ``src_sentence`` + :rtype: dict(str): float + """ + alignment_prob_for_t = defaultdict(lambda: 0.0) + for j in range(1, len(trg_sentence)): + t = trg_sentence[j] + for i in range(0, len(src_sentence)): + alignment_prob_for_t[t] += self.prob_alignment_point( + i, j, src_sentence, trg_sentence + ) + return alignment_prob_for_t + + def prob_alignment_point(self, i, j, src_sentence, trg_sentence): + """ + Probability that position j in ``trg_sentence`` is aligned to + position i in the ``src_sentence`` + """ + l = len(src_sentence) - 1 + m = len(trg_sentence) - 1 + s = src_sentence[i] + t = trg_sentence[j] + return self.translation_table[t][s] * self.alignment_table[i][j][l][m] + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + """ + prob = 1.0 + l = len(alignment_info.src_sentence) - 1 + m = len(alignment_info.trg_sentence) - 1 + + for j, i in enumerate(alignment_info.alignment): + if j == 0: + continue # skip the dummy zeroeth element + trg_word = alignment_info.trg_sentence[j] + src_word = alignment_info.src_sentence[i] + prob *= ( + self.translation_table[trg_word][src_word] + * self.alignment_table[i][j][l][m] + ) + + return max(prob, IBMModel.MIN_PROB) + + def align_all(self, parallel_corpus): + for sentence_pair in parallel_corpus: + self.align(sentence_pair) + + def align(self, sentence_pair): + """ + Determines the best word alignment for one sentence pair from + the corpus that the model was trained on. + + The best alignment will be set in ``sentence_pair`` when the + method returns. In contrast with the internal implementation of + IBM models, the word indices in the ``Alignment`` are zero- + indexed, not one-indexed. + + :param sentence_pair: A sentence in the source language and its + counterpart sentence in the target language + :type sentence_pair: AlignedSent + """ + best_alignment = [] + + l = len(sentence_pair.mots) + m = len(sentence_pair.words) + + for j, trg_word in enumerate(sentence_pair.words): + # Initialize trg_word to align with the NULL token + best_prob = ( + self.translation_table[trg_word][None] + * self.alignment_table[0][j + 1][l][m] + ) + best_prob = max(best_prob, IBMModel.MIN_PROB) + best_alignment_point = None + for i, src_word in enumerate(sentence_pair.mots): + align_prob = ( + self.translation_table[trg_word][src_word] + * self.alignment_table[i + 1][j + 1][l][m] + ) + if align_prob >= best_prob: + best_prob = align_prob + best_alignment_point = i + + best_alignment.append((j, best_alignment_point)) + + sentence_pair.alignment = Alignment(best_alignment) + + +class Model2Counts(Counts): + """ + Data object to store counts of various parameters during training. + Includes counts for alignment. + """ + + def __init__(self): + super().__init__() + self.alignment = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0.0))) + ) + self.alignment_for_any_i = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: 0.0)) + ) + + def update_lexical_translation(self, count, s, t): + self.t_given_s[t][s] += count + self.any_t_given_s[s] += count + + def update_alignment(self, count, i, j, l, m): + self.alignment[i][j][l][m] += count + self.alignment_for_any_i[j][l][m] += count diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm3.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm3.py new file mode 100644 index 0000000..d3c159c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm3.py @@ -0,0 +1,346 @@ +# Natural Language Toolkit: IBM Model 3 +# +# Copyright (C) 2001-2013 NLTK Project +# Authors: Chin Yee Lee, Hengfeng Li, Ruxin Hou, Calvin Tanujaya Lim +# URL: +# For license information, see LICENSE.TXT + +""" +Translation model that considers how a word can be aligned to +multiple words in another language. + +IBM Model 3 improves on Model 2 by directly modeling the phenomenon +where a word in one language may be translated into zero or more words +in another. This is expressed by the fertility probability, +n(phi | source word). + +If a source word translates into more than one word, it is possible to +generate sentences that have the same alignment in multiple ways. This +is modeled by a distortion step. The distortion probability, d(j|i,l,m), +predicts a target word position, given its aligned source word's +position. The distortion probability replaces the alignment probability +of Model 2. + +The fertility probability is not applicable for NULL. Target words that +align to NULL are assumed to be distributed uniformly in the target +sentence. The existence of these words is modeled by p1, the probability +that a target word produced by a real source word requires another +target word that is produced by NULL. + +The EM algorithm used in Model 3 is: + +:E step: In the training data, collect counts, weighted by prior + probabilities. + + - (a) count how many times a source language word is translated + into a target language word + - (b) count how many times a particular position in the target + sentence is aligned to a particular position in the source + sentence + - (c) count how many times a source word is aligned to phi number + of target words + - (d) count how many times NULL is aligned to a target word + +:M step: Estimate new probabilities based on the counts from the E step + +Because there are too many possible alignments, only the most probable +ones are considered. First, the best alignment is determined using prior +probabilities. Then, a hill climbing approach is used to find other good +candidates. + +Notations +--------- + +:i: Position in the source sentence + Valid values are 0 (for NULL), 1, 2, ..., length of source sentence +:j: Position in the target sentence + Valid values are 1, 2, ..., length of target sentence +:l: Number of words in the source sentence, excluding NULL +:m: Number of words in the target sentence +:s: A word in the source language +:t: A word in the target language +:phi: Fertility, the number of target words produced by a source word +:p1: Probability that a target word produced by a source word is + accompanied by another target word that is aligned to NULL +:p0: 1 - p1 + +References +---------- + +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +import warnings +from collections import defaultdict +from math import factorial + +from nltk.translate import AlignedSent, Alignment, IBMModel, IBMModel2 +from nltk.translate.ibm_model import Counts + + +class IBMModel3(IBMModel): + """ + Translation model that considers how a word can be aligned to + multiple words in another language + + >>> bitext = [] + >>> bitext.append(AlignedSent(['klein', 'ist', 'das', 'haus'], ['the', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus', 'war', 'ja', 'groß'], ['the', 'house', 'was', 'big'])) + >>> bitext.append(AlignedSent(['das', 'buch', 'ist', 'ja', 'klein'], ['the', 'book', 'is', 'small'])) + >>> bitext.append(AlignedSent(['ein', 'haus', 'ist', 'klein'], ['a', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus'], ['the', 'house'])) + >>> bitext.append(AlignedSent(['das', 'buch'], ['the', 'book'])) + >>> bitext.append(AlignedSent(['ein', 'buch'], ['a', 'book'])) + >>> bitext.append(AlignedSent(['ich', 'fasse', 'das', 'buch', 'zusammen'], ['i', 'summarize', 'the', 'book'])) + >>> bitext.append(AlignedSent(['fasse', 'zusammen'], ['summarize'])) + + >>> ibm3 = IBMModel3(bitext, 5) + + >>> print(round(ibm3.translation_table['buch']['book'], 3)) + 1.0 + >>> print(round(ibm3.translation_table['das']['book'], 3)) + 0.0 + >>> print(round(ibm3.translation_table['ja'][None], 3)) + 1.0 + + >>> print(round(ibm3.distortion_table[1][1][2][2], 3)) + 1.0 + >>> print(round(ibm3.distortion_table[1][2][2][2], 3)) + 0.0 + >>> print(round(ibm3.distortion_table[2][2][4][5], 3)) + 0.75 + + >>> print(round(ibm3.fertility_table[2]['summarize'], 3)) + 1.0 + >>> print(round(ibm3.fertility_table[1]['book'], 3)) + 1.0 + + >>> print(round(ibm3.p1, 3)) + 0.054 + + >>> test_sentence = bitext[2] + >>> test_sentence.words + ['das', 'buch', 'ist', 'ja', 'klein'] + >>> test_sentence.mots + ['the', 'book', 'is', 'small'] + >>> test_sentence.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, None), (4, 3)]) + + """ + + def __init__(self, sentence_aligned_corpus, iterations, probability_tables=None): + """ + Train on ``sentence_aligned_corpus`` and create a lexical + translation model, a distortion model, a fertility model, and a + model for generating NULL-aligned words. + + Translation direction is from ``AlignedSent.mots`` to + ``AlignedSent.words``. + + :param sentence_aligned_corpus: Sentence-aligned parallel corpus + :type sentence_aligned_corpus: list(AlignedSent) + + :param iterations: Number of iterations to run training algorithm + :type iterations: int + + :param probability_tables: Optional. Use this to pass in custom + probability values. If not specified, probabilities will be + set to a uniform distribution, or some other sensible value. + If specified, all the following entries must be present: + ``translation_table``, ``alignment_table``, + ``fertility_table``, ``p1``, ``distortion_table``. + See ``IBMModel`` for the type and purpose of these tables. + :type probability_tables: dict[str]: object + """ + super().__init__(sentence_aligned_corpus) + self.reset_probabilities() + + if probability_tables is None: + # Get translation and alignment probabilities from IBM Model 2 + ibm2 = IBMModel2(sentence_aligned_corpus, iterations) + self.translation_table = ibm2.translation_table + self.alignment_table = ibm2.alignment_table + self.set_uniform_probabilities(sentence_aligned_corpus) + else: + # Set user-defined probabilities + self.translation_table = probability_tables["translation_table"] + self.alignment_table = probability_tables["alignment_table"] + self.fertility_table = probability_tables["fertility_table"] + self.p1 = probability_tables["p1"] + self.distortion_table = probability_tables["distortion_table"] + + for n in range(0, iterations): + self.train(sentence_aligned_corpus) + + def reset_probabilities(self): + super().reset_probabilities() + self.distortion_table = defaultdict( + lambda: defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: self.MIN_PROB)) + ) + ) + """ + dict[int][int][int][int]: float. Probability(j | i,l,m). + Values accessed as ``distortion_table[j][i][l][m]``. + """ + + def set_uniform_probabilities(self, sentence_aligned_corpus): + # d(j | i,l,m) = 1 / m for all i, j, l, m + l_m_combinations = set() + for aligned_sentence in sentence_aligned_corpus: + l = len(aligned_sentence.mots) + m = len(aligned_sentence.words) + if (l, m) not in l_m_combinations: + l_m_combinations.add((l, m)) + initial_prob = 1 / m + if initial_prob < IBMModel.MIN_PROB: + warnings.warn( + "A target sentence is too long (" + + str(m) + + " words). Results may be less accurate." + ) + for j in range(1, m + 1): + for i in range(0, l + 1): + self.distortion_table[j][i][l][m] = initial_prob + + # simple initialization, taken from GIZA++ + self.fertility_table[0] = defaultdict(lambda: 0.2) + self.fertility_table[1] = defaultdict(lambda: 0.65) + self.fertility_table[2] = defaultdict(lambda: 0.1) + self.fertility_table[3] = defaultdict(lambda: 0.04) + MAX_FERTILITY = 10 + initial_fert_prob = 0.01 / (MAX_FERTILITY - 4) + for phi in range(4, MAX_FERTILITY): + self.fertility_table[phi] = defaultdict(lambda: initial_fert_prob) + + self.p1 = 0.5 + + def train(self, parallel_corpus): + counts = Model3Counts() + for aligned_sentence in parallel_corpus: + l = len(aligned_sentence.mots) + m = len(aligned_sentence.words) + + # Sample the alignment space + sampled_alignments, best_alignment = self.sample(aligned_sentence) + # Record the most probable alignment + aligned_sentence.alignment = Alignment( + best_alignment.zero_indexed_alignment() + ) + + # E step (a): Compute normalization factors to weigh counts + total_count = self.prob_of_alignments(sampled_alignments) + + # E step (b): Collect counts + for alignment_info in sampled_alignments: + count = self.prob_t_a_given_s(alignment_info) + normalized_count = count / total_count + + for j in range(1, m + 1): + counts.update_lexical_translation( + normalized_count, alignment_info, j + ) + counts.update_distortion(normalized_count, alignment_info, j, l, m) + + counts.update_null_generation(normalized_count, alignment_info) + counts.update_fertility(normalized_count, alignment_info) + + # M step: Update probabilities with maximum likelihood estimates + # If any probability is less than MIN_PROB, clamp it to MIN_PROB + existing_alignment_table = self.alignment_table + self.reset_probabilities() + self.alignment_table = existing_alignment_table # don't retrain + + self.maximize_lexical_translation_probabilities(counts) + self.maximize_distortion_probabilities(counts) + self.maximize_fertility_probabilities(counts) + self.maximize_null_generation_probabilities(counts) + + def maximize_distortion_probabilities(self, counts): + MIN_PROB = IBMModel.MIN_PROB + for j, i_s in counts.distortion.items(): + for i, src_sentence_lengths in i_s.items(): + for l, trg_sentence_lengths in src_sentence_lengths.items(): + for m in trg_sentence_lengths: + estimate = ( + counts.distortion[j][i][l][m] + / counts.distortion_for_any_j[i][l][m] + ) + self.distortion_table[j][i][l][m] = max(estimate, MIN_PROB) + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + """ + src_sentence = alignment_info.src_sentence + trg_sentence = alignment_info.trg_sentence + l = len(src_sentence) - 1 # exclude NULL + m = len(trg_sentence) - 1 + p1 = self.p1 + p0 = 1 - p1 + + probability = 1.0 + MIN_PROB = IBMModel.MIN_PROB + + # Combine NULL insertion probability + null_fertility = alignment_info.fertility_of_i(0) + probability *= pow(p1, null_fertility) * pow(p0, m - 2 * null_fertility) + if probability < MIN_PROB: + return MIN_PROB + + # Compute combination (m - null_fertility) choose null_fertility + for i in range(1, null_fertility + 1): + probability *= (m - null_fertility - i + 1) / i + if probability < MIN_PROB: + return MIN_PROB + + # Combine fertility probabilities + for i in range(1, l + 1): + fertility = alignment_info.fertility_of_i(i) + probability *= ( + factorial(fertility) * self.fertility_table[fertility][src_sentence[i]] + ) + if probability < MIN_PROB: + return MIN_PROB + + # Combine lexical and distortion probabilities + for j in range(1, m + 1): + t = trg_sentence[j] + i = alignment_info.alignment[j] + s = src_sentence[i] + + probability *= ( + self.translation_table[t][s] * self.distortion_table[j][i][l][m] + ) + if probability < MIN_PROB: + return MIN_PROB + + return probability + + +class Model3Counts(Counts): + """ + Data object to store counts of various parameters during training. + Includes counts for distortion. + """ + + def __init__(self): + super().__init__() + self.distortion = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0.0))) + ) + self.distortion_for_any_j = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: 0.0)) + ) + + def update_distortion(self, count, alignment_info, j, l, m): + i = alignment_info.alignment[j] + self.distortion[j][i][l][m] += count + self.distortion_for_any_j[i][l][m] += count diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm4.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm4.py new file mode 100644 index 0000000..665520e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm4.py @@ -0,0 +1,490 @@ +# Natural Language Toolkit: IBM Model 4 +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +Translation model that reorders output words based on their type and +distance from other related words in the output sentence. + +IBM Model 4 improves the distortion model of Model 3, motivated by the +observation that certain words tend to be re-ordered in a predictable +way relative to one another. For example, in English +usually has its order flipped as in French. + +Model 4 requires words in the source and target vocabularies to be +categorized into classes. This can be linguistically driven, like parts +of speech (adjective, nouns, prepositions, etc). Word classes can also +be obtained by statistical methods. The original IBM Model 4 uses an +information theoretic approach to group words into 50 classes for each +vocabulary. + +Terminology +----------- + +:Cept: + A source word with non-zero fertility i.e. aligned to one or more + target words. +:Tablet: + The set of target word(s) aligned to a cept. +:Head of cept: + The first word of the tablet of that cept. +:Center of cept: + The average position of the words in that cept's tablet. If the + value is not an integer, the ceiling is taken. + For example, for a tablet with words in positions 2, 5, 6 in the + target sentence, the center of the corresponding cept is + ceil((2 + 5 + 6) / 3) = 5 +:Displacement: + For a head word, defined as (position of head word - position of + previous cept's center). Can be positive or negative. + For a non-head word, defined as (position of non-head word - + position of previous word in the same tablet). Always positive, + because successive words in a tablet are assumed to appear to the + right of the previous word. + +In contrast to Model 3 which reorders words in a tablet independently of +other words, Model 4 distinguishes between three cases. + +1. Words generated by NULL are distributed uniformly. +2. For a head word t, its position is modeled by the probability + d_head(displacement | word_class_s(s),word_class_t(t)), + where s is the previous cept, and word_class_s and word_class_t maps + s and t to a source and target language word class respectively. +3. For a non-head word t, its position is modeled by the probability + d_non_head(displacement | word_class_t(t)) + +The EM algorithm used in Model 4 is: + +:E step: In the training data, collect counts, weighted by prior + probabilities. + + - (a) count how many times a source language word is translated + into a target language word + - (b) for a particular word class, count how many times a head + word is located at a particular displacement from the + previous cept's center + - (c) for a particular word class, count how many times a + non-head word is located at a particular displacement from + the previous target word + - (d) count how many times a source word is aligned to phi number + of target words + - (e) count how many times NULL is aligned to a target word + +:M step: Estimate new probabilities based on the counts from the E step + +Like Model 3, there are too many possible alignments to consider. Thus, +a hill climbing approach is used to sample good candidates. + +Notations +--------- + +:i: Position in the source sentence + Valid values are 0 (for NULL), 1, 2, ..., length of source sentence +:j: Position in the target sentence + Valid values are 1, 2, ..., length of target sentence +:l: Number of words in the source sentence, excluding NULL +:m: Number of words in the target sentence +:s: A word in the source language +:t: A word in the target language +:phi: Fertility, the number of target words produced by a source word +:p1: Probability that a target word produced by a source word is + accompanied by another target word that is aligned to NULL +:p0: 1 - p1 +:dj: Displacement, Δj + +References +---------- + +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +import warnings +from collections import defaultdict +from math import factorial + +from nltk.translate import AlignedSent, Alignment, IBMModel, IBMModel3 +from nltk.translate.ibm_model import Counts, longest_target_sentence_length + + +class IBMModel4(IBMModel): + """ + Translation model that reorders output words based on their type and + their distance from other related words in the output sentence + + >>> bitext = [] + >>> bitext.append(AlignedSent(['klein', 'ist', 'das', 'haus'], ['the', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus', 'war', 'ja', 'groß'], ['the', 'house', 'was', 'big'])) + >>> bitext.append(AlignedSent(['das', 'buch', 'ist', 'ja', 'klein'], ['the', 'book', 'is', 'small'])) + >>> bitext.append(AlignedSent(['ein', 'haus', 'ist', 'klein'], ['a', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus'], ['the', 'house'])) + >>> bitext.append(AlignedSent(['das', 'buch'], ['the', 'book'])) + >>> bitext.append(AlignedSent(['ein', 'buch'], ['a', 'book'])) + >>> bitext.append(AlignedSent(['ich', 'fasse', 'das', 'buch', 'zusammen'], ['i', 'summarize', 'the', 'book'])) + >>> bitext.append(AlignedSent(['fasse', 'zusammen'], ['summarize'])) + >>> src_classes = {'the': 0, 'a': 0, 'small': 1, 'big': 1, 'house': 2, 'book': 2, 'is': 3, 'was': 3, 'i': 4, 'summarize': 5 } + >>> trg_classes = {'das': 0, 'ein': 0, 'haus': 1, 'buch': 1, 'klein': 2, 'groß': 2, 'ist': 3, 'war': 3, 'ja': 4, 'ich': 5, 'fasse': 6, 'zusammen': 6 } + + >>> ibm4 = IBMModel4(bitext, 5, src_classes, trg_classes) + + >>> print(round(ibm4.translation_table['buch']['book'], 3)) + 1.0 + >>> print(round(ibm4.translation_table['das']['book'], 3)) + 0.0 + >>> print(round(ibm4.translation_table['ja'][None], 3)) + 1.0 + + >>> print(round(ibm4.head_distortion_table[1][0][1], 3)) + 1.0 + >>> print(round(ibm4.head_distortion_table[2][0][1], 3)) + 0.0 + >>> print(round(ibm4.non_head_distortion_table[3][6], 3)) + 0.5 + + >>> print(round(ibm4.fertility_table[2]['summarize'], 3)) + 1.0 + >>> print(round(ibm4.fertility_table[1]['book'], 3)) + 1.0 + + >>> print(round(ibm4.p1, 3)) + 0.033 + + >>> test_sentence = bitext[2] + >>> test_sentence.words + ['das', 'buch', 'ist', 'ja', 'klein'] + >>> test_sentence.mots + ['the', 'book', 'is', 'small'] + >>> test_sentence.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, None), (4, 3)]) + + """ + + def __init__( + self, + sentence_aligned_corpus, + iterations, + source_word_classes, + target_word_classes, + probability_tables=None, + ): + """ + Train on ``sentence_aligned_corpus`` and create a lexical + translation model, distortion models, a fertility model, and a + model for generating NULL-aligned words. + + Translation direction is from ``AlignedSent.mots`` to + ``AlignedSent.words``. + + :param sentence_aligned_corpus: Sentence-aligned parallel corpus + :type sentence_aligned_corpus: list(AlignedSent) + + :param iterations: Number of iterations to run training algorithm + :type iterations: int + + :param source_word_classes: Lookup table that maps a source word + to its word class, the latter represented by an integer id + :type source_word_classes: dict[str]: int + + :param target_word_classes: Lookup table that maps a target word + to its word class, the latter represented by an integer id + :type target_word_classes: dict[str]: int + + :param probability_tables: Optional. Use this to pass in custom + probability values. If not specified, probabilities will be + set to a uniform distribution, or some other sensible value. + If specified, all the following entries must be present: + ``translation_table``, ``alignment_table``, + ``fertility_table``, ``p1``, ``head_distortion_table``, + ``non_head_distortion_table``. See ``IBMModel`` and + ``IBMModel4`` for the type and purpose of these tables. + :type probability_tables: dict[str]: object + """ + super().__init__(sentence_aligned_corpus) + self.reset_probabilities() + self.src_classes = source_word_classes + self.trg_classes = target_word_classes + + if probability_tables is None: + # Get probabilities from IBM model 3 + ibm3 = IBMModel3(sentence_aligned_corpus, iterations) + self.translation_table = ibm3.translation_table + self.alignment_table = ibm3.alignment_table + self.fertility_table = ibm3.fertility_table + self.p1 = ibm3.p1 + self.set_uniform_probabilities(sentence_aligned_corpus) + else: + # Set user-defined probabilities + self.translation_table = probability_tables["translation_table"] + self.alignment_table = probability_tables["alignment_table"] + self.fertility_table = probability_tables["fertility_table"] + self.p1 = probability_tables["p1"] + self.head_distortion_table = probability_tables["head_distortion_table"] + self.non_head_distortion_table = probability_tables[ + "non_head_distortion_table" + ] + + for n in range(0, iterations): + self.train(sentence_aligned_corpus) + + def reset_probabilities(self): + super().reset_probabilities() + self.head_distortion_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: self.MIN_PROB)) + ) + """ + dict[int][int][int]: float. Probability(displacement of head + word | word class of previous cept,target word class). + Values accessed as ``distortion_table[dj][src_class][trg_class]``. + """ + + self.non_head_distortion_table = defaultdict( + lambda: defaultdict(lambda: self.MIN_PROB) + ) + """ + dict[int][int]: float. Probability(displacement of non-head + word | target word class). + Values accessed as ``distortion_table[dj][trg_class]``. + """ + + def set_uniform_probabilities(self, sentence_aligned_corpus): + """ + Set distortion probabilities uniformly to + 1 / cardinality of displacement values + """ + max_m = longest_target_sentence_length(sentence_aligned_corpus) + + # The maximum displacement is m-1, when a word is in the last + # position m of the target sentence and the previously placed + # word is in the first position. + # Conversely, the minimum displacement is -(m-1). + # Thus, the displacement range is (m-1) - (-(m-1)). Note that + # displacement cannot be zero and is not included in the range. + if max_m <= 1: + initial_prob = IBMModel.MIN_PROB + else: + initial_prob = 1 / (2 * (max_m - 1)) + if initial_prob < IBMModel.MIN_PROB: + warnings.warn( + "A target sentence is too long (" + + str(max_m) + + " words). Results may be less accurate." + ) + + for dj in range(1, max_m): + self.head_distortion_table[dj] = defaultdict( + lambda: defaultdict(lambda: initial_prob) + ) + self.head_distortion_table[-dj] = defaultdict( + lambda: defaultdict(lambda: initial_prob) + ) + self.non_head_distortion_table[dj] = defaultdict(lambda: initial_prob) + self.non_head_distortion_table[-dj] = defaultdict(lambda: initial_prob) + + def train(self, parallel_corpus): + counts = Model4Counts() + for aligned_sentence in parallel_corpus: + m = len(aligned_sentence.words) + + # Sample the alignment space + sampled_alignments, best_alignment = self.sample(aligned_sentence) + # Record the most probable alignment + aligned_sentence.alignment = Alignment( + best_alignment.zero_indexed_alignment() + ) + + # E step (a): Compute normalization factors to weigh counts + total_count = self.prob_of_alignments(sampled_alignments) + + # E step (b): Collect counts + for alignment_info in sampled_alignments: + count = self.prob_t_a_given_s(alignment_info) + normalized_count = count / total_count + + for j in range(1, m + 1): + counts.update_lexical_translation( + normalized_count, alignment_info, j + ) + counts.update_distortion( + normalized_count, + alignment_info, + j, + self.src_classes, + self.trg_classes, + ) + + counts.update_null_generation(normalized_count, alignment_info) + counts.update_fertility(normalized_count, alignment_info) + + # M step: Update probabilities with maximum likelihood estimates + # If any probability is less than MIN_PROB, clamp it to MIN_PROB + existing_alignment_table = self.alignment_table + self.reset_probabilities() + self.alignment_table = existing_alignment_table # don't retrain + + self.maximize_lexical_translation_probabilities(counts) + self.maximize_distortion_probabilities(counts) + self.maximize_fertility_probabilities(counts) + self.maximize_null_generation_probabilities(counts) + + def maximize_distortion_probabilities(self, counts): + head_d_table = self.head_distortion_table + for dj, src_classes in counts.head_distortion.items(): + for s_cls, trg_classes in src_classes.items(): + for t_cls in trg_classes: + estimate = ( + counts.head_distortion[dj][s_cls][t_cls] + / counts.head_distortion_for_any_dj[s_cls][t_cls] + ) + head_d_table[dj][s_cls][t_cls] = max(estimate, IBMModel.MIN_PROB) + + non_head_d_table = self.non_head_distortion_table + for dj, trg_classes in counts.non_head_distortion.items(): + for t_cls in trg_classes: + estimate = ( + counts.non_head_distortion[dj][t_cls] + / counts.non_head_distortion_for_any_dj[t_cls] + ) + non_head_d_table[dj][t_cls] = max(estimate, IBMModel.MIN_PROB) + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + """ + return IBMModel4.model4_prob_t_a_given_s(alignment_info, self) + + @staticmethod # exposed for Model 5 to use + def model4_prob_t_a_given_s(alignment_info, ibm_model): + probability = 1.0 + MIN_PROB = IBMModel.MIN_PROB + + def null_generation_term(): + # Binomial distribution: B(m - null_fertility, p1) + value = 1.0 + p1 = ibm_model.p1 + p0 = 1 - p1 + null_fertility = alignment_info.fertility_of_i(0) + m = len(alignment_info.trg_sentence) - 1 + value *= pow(p1, null_fertility) * pow(p0, m - 2 * null_fertility) + if value < MIN_PROB: + return MIN_PROB + + # Combination: (m - null_fertility) choose null_fertility + for i in range(1, null_fertility + 1): + value *= (m - null_fertility - i + 1) / i + return value + + def fertility_term(): + value = 1.0 + src_sentence = alignment_info.src_sentence + for i in range(1, len(src_sentence)): + fertility = alignment_info.fertility_of_i(i) + value *= ( + factorial(fertility) + * ibm_model.fertility_table[fertility][src_sentence[i]] + ) + if value < MIN_PROB: + return MIN_PROB + return value + + def lexical_translation_term(j): + t = alignment_info.trg_sentence[j] + i = alignment_info.alignment[j] + s = alignment_info.src_sentence[i] + return ibm_model.translation_table[t][s] + + def distortion_term(j): + t = alignment_info.trg_sentence[j] + i = alignment_info.alignment[j] + if i == 0: + # case 1: t is aligned to NULL + return 1.0 + if alignment_info.is_head_word(j): + # case 2: t is the first word of a tablet + previous_cept = alignment_info.previous_cept(j) + src_class = None + if previous_cept is not None: + previous_s = alignment_info.src_sentence[previous_cept] + src_class = ibm_model.src_classes[previous_s] + trg_class = ibm_model.trg_classes[t] + dj = j - alignment_info.center_of_cept(previous_cept) + return ibm_model.head_distortion_table[dj][src_class][trg_class] + + # case 3: t is a subsequent word of a tablet + previous_position = alignment_info.previous_in_tablet(j) + trg_class = ibm_model.trg_classes[t] + dj = j - previous_position + return ibm_model.non_head_distortion_table[dj][trg_class] + + # end nested functions + + # Abort computation whenever probability falls below MIN_PROB at + # any point, since MIN_PROB can be considered as zero + probability *= null_generation_term() + if probability < MIN_PROB: + return MIN_PROB + + probability *= fertility_term() + if probability < MIN_PROB: + return MIN_PROB + + for j in range(1, len(alignment_info.trg_sentence)): + probability *= lexical_translation_term(j) + if probability < MIN_PROB: + return MIN_PROB + + probability *= distortion_term(j) + if probability < MIN_PROB: + return MIN_PROB + + return probability + + +class Model4Counts(Counts): + """ + Data object to store counts of various parameters during training. + Includes counts for distortion. + """ + + def __init__(self): + super().__init__() + self.head_distortion = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: 0.0)) + ) + self.head_distortion_for_any_dj = defaultdict(lambda: defaultdict(lambda: 0.0)) + self.non_head_distortion = defaultdict(lambda: defaultdict(lambda: 0.0)) + self.non_head_distortion_for_any_dj = defaultdict(lambda: 0.0) + + def update_distortion(self, count, alignment_info, j, src_classes, trg_classes): + i = alignment_info.alignment[j] + t = alignment_info.trg_sentence[j] + if i == 0: + # case 1: t is aligned to NULL + pass + elif alignment_info.is_head_word(j): + # case 2: t is the first word of a tablet + previous_cept = alignment_info.previous_cept(j) + if previous_cept is not None: + previous_src_word = alignment_info.src_sentence[previous_cept] + src_class = src_classes[previous_src_word] + else: + src_class = None + trg_class = trg_classes[t] + dj = j - alignment_info.center_of_cept(previous_cept) + self.head_distortion[dj][src_class][trg_class] += count + self.head_distortion_for_any_dj[src_class][trg_class] += count + else: + # case 3: t is a subsequent word of a tablet + previous_j = alignment_info.previous_in_tablet(j) + trg_class = trg_classes[t] + dj = j - previous_j + self.non_head_distortion[dj][trg_class] += count + self.non_head_distortion_for_any_dj[trg_class] += count diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm5.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm5.py new file mode 100644 index 0000000..b4591df --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm5.py @@ -0,0 +1,663 @@ +# Natural Language Toolkit: IBM Model 5 +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +Translation model that keeps track of vacant positions in the target +sentence to decide where to place translated words. + +Translation can be viewed as a process where each word in the source +sentence is stepped through sequentially, generating translated words +for each source word. The target sentence can be viewed as being made +up of ``m`` empty slots initially, which gradually fill up as generated +words are placed in them. + +Models 3 and 4 use distortion probabilities to decide how to place +translated words. For simplicity, these models ignore the history of +which slots have already been occupied with translated words. +Consider the placement of the last translated word: there is only one +empty slot left in the target sentence, so the distortion probability +should be 1.0 for that position and 0.0 everywhere else. However, the +distortion probabilities for Models 3 and 4 are set up such that all +positions are under consideration. + +IBM Model 5 fixes this deficiency by accounting for occupied slots +during translation. It introduces the vacancy function v(j), the number +of vacancies up to, and including, position j in the target sentence. + +Terminology +----------- + +:Maximum vacancy: + The number of valid slots that a word can be placed in. + This is not necessarily the same as the number of vacant slots. + For example, if a tablet contains more than one word, the head word + cannot be placed at the last vacant slot because there will be no + space for the other words in the tablet. The number of valid slots + has to take into account the length of the tablet. + Non-head words cannot be placed before the head word, so vacancies + to the left of the head word are ignored. +:Vacancy difference: + For a head word: (v(j) - v(center of previous cept)) + Can be positive or negative. + For a non-head word: (v(j) - v(position of previously placed word)) + Always positive, because successive words in a tablet are assumed to + appear to the right of the previous word. + +Positioning of target words fall under three cases: + +1. Words generated by NULL are distributed uniformly +2. For a head word t, its position is modeled by the probability + v_head(dv | max_v,word_class_t(t)) +3. For a non-head word t, its position is modeled by the probability + v_non_head(dv | max_v,word_class_t(t)) + +dv and max_v are defined differently for head and non-head words. + +The EM algorithm used in Model 5 is: + +:E step: In the training data, collect counts, weighted by prior + probabilities. + + - (a) count how many times a source language word is translated + into a target language word + - (b) for a particular word class and maximum vacancy, count how + many times a head word and the previous cept's center have + a particular difference in number of vacancies + - (b) for a particular word class and maximum vacancy, count how + many times a non-head word and the previous target word + have a particular difference in number of vacancies + - (d) count how many times a source word is aligned to phi number + of target words + - (e) count how many times NULL is aligned to a target word + +:M step: Estimate new probabilities based on the counts from the E step + +Like Model 4, there are too many possible alignments to consider. Thus, +a hill climbing approach is used to sample good candidates. In addition, +pruning is used to weed out unlikely alignments based on Model 4 scores. + +Notations +--------- + +:i: Position in the source sentence + Valid values are 0 (for NULL), 1, 2, ..., length of source sentence +:j: Position in the target sentence + Valid values are 1, 2, ..., length of target sentence +:l: Number of words in the source sentence, excluding NULL +:m: Number of words in the target sentence +:s: A word in the source language +:t: A word in the target language +:phi: Fertility, the number of target words produced by a source word +:p1: Probability that a target word produced by a source word is + accompanied by another target word that is aligned to NULL +:p0: 1 - p1 +:max_v: Maximum vacancy +:dv: Vacancy difference, Δv + +The definition of v_head here differs from GIZA++, section 4.7 of +[Brown et al., 1993], and [Koehn, 2010]. In the latter cases, v_head is +v_head(v(j) | v(center of previous cept),max_v,word_class(t)). + +Here, we follow appendix B of [Brown et al., 1993] and combine v(j) with +v(center of previous cept) to obtain dv: +v_head(v(j) - v(center of previous cept) | max_v,word_class(t)). + +References +---------- + +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +import warnings +from collections import defaultdict +from math import factorial + +from nltk.translate import AlignedSent, Alignment, IBMModel, IBMModel4 +from nltk.translate.ibm_model import Counts, longest_target_sentence_length + + +class IBMModel5(IBMModel): + """ + Translation model that keeps track of vacant positions in the target + sentence to decide where to place translated words + + >>> bitext = [] + >>> bitext.append(AlignedSent(['klein', 'ist', 'das', 'haus'], ['the', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus', 'war', 'ja', 'groß'], ['the', 'house', 'was', 'big'])) + >>> bitext.append(AlignedSent(['das', 'buch', 'ist', 'ja', 'klein'], ['the', 'book', 'is', 'small'])) + >>> bitext.append(AlignedSent(['ein', 'haus', 'ist', 'klein'], ['a', 'house', 'is', 'small'])) + >>> bitext.append(AlignedSent(['das', 'haus'], ['the', 'house'])) + >>> bitext.append(AlignedSent(['das', 'buch'], ['the', 'book'])) + >>> bitext.append(AlignedSent(['ein', 'buch'], ['a', 'book'])) + >>> bitext.append(AlignedSent(['ich', 'fasse', 'das', 'buch', 'zusammen'], ['i', 'summarize', 'the', 'book'])) + >>> bitext.append(AlignedSent(['fasse', 'zusammen'], ['summarize'])) + >>> src_classes = {'the': 0, 'a': 0, 'small': 1, 'big': 1, 'house': 2, 'book': 2, 'is': 3, 'was': 3, 'i': 4, 'summarize': 5 } + >>> trg_classes = {'das': 0, 'ein': 0, 'haus': 1, 'buch': 1, 'klein': 2, 'groß': 2, 'ist': 3, 'war': 3, 'ja': 4, 'ich': 5, 'fasse': 6, 'zusammen': 6 } + + >>> ibm5 = IBMModel5(bitext, 5, src_classes, trg_classes) + + >>> print(round(ibm5.head_vacancy_table[1][1][1], 3)) + 1.0 + >>> print(round(ibm5.head_vacancy_table[2][1][1], 3)) + 0.0 + >>> print(round(ibm5.non_head_vacancy_table[3][3][6], 3)) + 1.0 + + >>> print(round(ibm5.fertility_table[2]['summarize'], 3)) + 1.0 + >>> print(round(ibm5.fertility_table[1]['book'], 3)) + 1.0 + + >>> print(round(ibm5.p1, 3)) + 0.033 + + >>> test_sentence = bitext[2] + >>> test_sentence.words + ['das', 'buch', 'ist', 'ja', 'klein'] + >>> test_sentence.mots + ['the', 'book', 'is', 'small'] + >>> test_sentence.alignment + Alignment([(0, 0), (1, 1), (2, 2), (3, None), (4, 3)]) + + """ + + MIN_SCORE_FACTOR = 0.2 + """ + Alignments with scores below this factor are pruned during sampling + """ + + def __init__( + self, + sentence_aligned_corpus, + iterations, + source_word_classes, + target_word_classes, + probability_tables=None, + ): + """ + Train on ``sentence_aligned_corpus`` and create a lexical + translation model, vacancy models, a fertility model, and a + model for generating NULL-aligned words. + + Translation direction is from ``AlignedSent.mots`` to + ``AlignedSent.words``. + + :param sentence_aligned_corpus: Sentence-aligned parallel corpus + :type sentence_aligned_corpus: list(AlignedSent) + + :param iterations: Number of iterations to run training algorithm + :type iterations: int + + :param source_word_classes: Lookup table that maps a source word + to its word class, the latter represented by an integer id + :type source_word_classes: dict[str]: int + + :param target_word_classes: Lookup table that maps a target word + to its word class, the latter represented by an integer id + :type target_word_classes: dict[str]: int + + :param probability_tables: Optional. Use this to pass in custom + probability values. If not specified, probabilities will be + set to a uniform distribution, or some other sensible value. + If specified, all the following entries must be present: + ``translation_table``, ``alignment_table``, + ``fertility_table``, ``p1``, ``head_distortion_table``, + ``non_head_distortion_table``, ``head_vacancy_table``, + ``non_head_vacancy_table``. See ``IBMModel``, ``IBMModel4``, + and ``IBMModel5`` for the type and purpose of these tables. + :type probability_tables: dict[str]: object + """ + super().__init__(sentence_aligned_corpus) + self.reset_probabilities() + self.src_classes = source_word_classes + self.trg_classes = target_word_classes + + if probability_tables is None: + # Get probabilities from IBM model 4 + ibm4 = IBMModel4( + sentence_aligned_corpus, + iterations, + source_word_classes, + target_word_classes, + ) + self.translation_table = ibm4.translation_table + self.alignment_table = ibm4.alignment_table + self.fertility_table = ibm4.fertility_table + self.p1 = ibm4.p1 + self.head_distortion_table = ibm4.head_distortion_table + self.non_head_distortion_table = ibm4.non_head_distortion_table + self.set_uniform_probabilities(sentence_aligned_corpus) + else: + # Set user-defined probabilities + self.translation_table = probability_tables["translation_table"] + self.alignment_table = probability_tables["alignment_table"] + self.fertility_table = probability_tables["fertility_table"] + self.p1 = probability_tables["p1"] + self.head_distortion_table = probability_tables["head_distortion_table"] + self.non_head_distortion_table = probability_tables[ + "non_head_distortion_table" + ] + self.head_vacancy_table = probability_tables["head_vacancy_table"] + self.non_head_vacancy_table = probability_tables["non_head_vacancy_table"] + + for n in range(0, iterations): + self.train(sentence_aligned_corpus) + + def reset_probabilities(self): + super().reset_probabilities() + self.head_vacancy_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: self.MIN_PROB)) + ) + """ + dict[int][int][int]: float. Probability(vacancy difference | + number of remaining valid positions,target word class). + Values accessed as ``head_vacancy_table[dv][v_max][trg_class]``. + """ + + self.non_head_vacancy_table = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: self.MIN_PROB)) + ) + """ + dict[int][int][int]: float. Probability(vacancy difference | + number of remaining valid positions,target word class). + Values accessed as ``non_head_vacancy_table[dv][v_max][trg_class]``. + """ + + def set_uniform_probabilities(self, sentence_aligned_corpus): + """ + Set vacancy probabilities uniformly to + 1 / cardinality of vacancy difference values + """ + max_m = longest_target_sentence_length(sentence_aligned_corpus) + + # The maximum vacancy difference occurs when a word is placed in + # the last available position m of the target sentence and the + # previous word position has no vacancies. + # The minimum is 1-max_v, when a word is placed in the first + # available position and the previous word is placed beyond the + # last available position. + # Thus, the number of possible vacancy difference values is + # (max_v) - (1-max_v) + 1 = 2 * max_v. + if max_m > 0 and (1 / (2 * max_m)) < IBMModel.MIN_PROB: + warnings.warn( + "A target sentence is too long (" + + str(max_m) + + " words). Results may be less accurate." + ) + + for max_v in range(1, max_m + 1): + for dv in range(1, max_m + 1): + initial_prob = 1 / (2 * max_v) + self.head_vacancy_table[dv][max_v] = defaultdict(lambda: initial_prob) + self.head_vacancy_table[-(dv - 1)][max_v] = defaultdict( + lambda: initial_prob + ) + self.non_head_vacancy_table[dv][max_v] = defaultdict( + lambda: initial_prob + ) + self.non_head_vacancy_table[-(dv - 1)][max_v] = defaultdict( + lambda: initial_prob + ) + + def train(self, parallel_corpus): + counts = Model5Counts() + for aligned_sentence in parallel_corpus: + l = len(aligned_sentence.mots) + m = len(aligned_sentence.words) + + # Sample the alignment space + sampled_alignments, best_alignment = self.sample(aligned_sentence) + # Record the most probable alignment + aligned_sentence.alignment = Alignment( + best_alignment.zero_indexed_alignment() + ) + + # E step (a): Compute normalization factors to weigh counts + total_count = self.prob_of_alignments(sampled_alignments) + + # E step (b): Collect counts + for alignment_info in sampled_alignments: + count = self.prob_t_a_given_s(alignment_info) + normalized_count = count / total_count + + for j in range(1, m + 1): + counts.update_lexical_translation( + normalized_count, alignment_info, j + ) + + slots = Slots(m) + for i in range(1, l + 1): + counts.update_vacancy( + normalized_count, alignment_info, i, self.trg_classes, slots + ) + + counts.update_null_generation(normalized_count, alignment_info) + counts.update_fertility(normalized_count, alignment_info) + + # M step: Update probabilities with maximum likelihood estimates + # If any probability is less than MIN_PROB, clamp it to MIN_PROB + existing_alignment_table = self.alignment_table + self.reset_probabilities() + self.alignment_table = existing_alignment_table # don't retrain + + self.maximize_lexical_translation_probabilities(counts) + self.maximize_vacancy_probabilities(counts) + self.maximize_fertility_probabilities(counts) + self.maximize_null_generation_probabilities(counts) + + def sample(self, sentence_pair): + """ + Sample the most probable alignments from the entire alignment + space according to Model 4 + + Note that Model 4 scoring is used instead of Model 5 because the + latter is too expensive to compute. + + First, determine the best alignment according to IBM Model 2. + With this initial alignment, use hill climbing to determine the + best alignment according to a IBM Model 4. Add this + alignment and its neighbors to the sample set. Repeat this + process with other initial alignments obtained by pegging an + alignment point. Finally, prune alignments that have + substantially lower Model 4 scores than the best alignment. + + :param sentence_pair: Source and target language sentence pair + to generate a sample of alignments from + :type sentence_pair: AlignedSent + + :return: A set of best alignments represented by their ``AlignmentInfo`` + and the best alignment of the set for convenience + :rtype: set(AlignmentInfo), AlignmentInfo + """ + sampled_alignments, best_alignment = super().sample(sentence_pair) + return self.prune(sampled_alignments), best_alignment + + def prune(self, alignment_infos): + """ + Removes alignments from ``alignment_infos`` that have + substantially lower Model 4 scores than the best alignment + + :return: Pruned alignments + :rtype: set(AlignmentInfo) + """ + alignments = [] + best_score = 0 + + for alignment_info in alignment_infos: + score = IBMModel4.model4_prob_t_a_given_s(alignment_info, self) + best_score = max(score, best_score) + alignments.append((alignment_info, score)) + + threshold = IBMModel5.MIN_SCORE_FACTOR * best_score + alignments = [a[0] for a in alignments if a[1] > threshold] + return set(alignments) + + def hillclimb(self, alignment_info, j_pegged=None): + """ + Starting from the alignment in ``alignment_info``, look at + neighboring alignments iteratively for the best one, according + to Model 4 + + Note that Model 4 scoring is used instead of Model 5 because the + latter is too expensive to compute. + + There is no guarantee that the best alignment in the alignment + space will be found, because the algorithm might be stuck in a + local maximum. + + :param j_pegged: If specified, the search will be constrained to + alignments where ``j_pegged`` remains unchanged + :type j_pegged: int + + :return: The best alignment found from hill climbing + :rtype: AlignmentInfo + """ + alignment = alignment_info # alias with shorter name + max_probability = IBMModel4.model4_prob_t_a_given_s(alignment, self) + + while True: + old_alignment = alignment + for neighbor_alignment in self.neighboring(alignment, j_pegged): + neighbor_probability = IBMModel4.model4_prob_t_a_given_s( + neighbor_alignment, self + ) + + if neighbor_probability > max_probability: + alignment = neighbor_alignment + max_probability = neighbor_probability + + if alignment == old_alignment: + # Until there are no better alignments + break + + alignment.score = max_probability + return alignment + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + """ + probability = 1.0 + MIN_PROB = IBMModel.MIN_PROB + slots = Slots(len(alignment_info.trg_sentence) - 1) + + def null_generation_term(): + # Binomial distribution: B(m - null_fertility, p1) + value = 1.0 + p1 = self.p1 + p0 = 1 - p1 + null_fertility = alignment_info.fertility_of_i(0) + m = len(alignment_info.trg_sentence) - 1 + value *= pow(p1, null_fertility) * pow(p0, m - 2 * null_fertility) + if value < MIN_PROB: + return MIN_PROB + + # Combination: (m - null_fertility) choose null_fertility + for i in range(1, null_fertility + 1): + value *= (m - null_fertility - i + 1) / i + return value + + def fertility_term(): + value = 1.0 + src_sentence = alignment_info.src_sentence + for i in range(1, len(src_sentence)): + fertility = alignment_info.fertility_of_i(i) + value *= ( + factorial(fertility) + * self.fertility_table[fertility][src_sentence[i]] + ) + if value < MIN_PROB: + return MIN_PROB + return value + + def lexical_translation_term(j): + t = alignment_info.trg_sentence[j] + i = alignment_info.alignment[j] + s = alignment_info.src_sentence[i] + return self.translation_table[t][s] + + def vacancy_term(i): + value = 1.0 + tablet = alignment_info.cepts[i] + tablet_length = len(tablet) + total_vacancies = slots.vacancies_at(len(slots)) + + # case 1: NULL-aligned words + if tablet_length == 0: + return value + + # case 2: head word + j = tablet[0] + previous_cept = alignment_info.previous_cept(j) + previous_center = alignment_info.center_of_cept(previous_cept) + dv = slots.vacancies_at(j) - slots.vacancies_at(previous_center) + max_v = total_vacancies - tablet_length + 1 + trg_class = self.trg_classes[alignment_info.trg_sentence[j]] + value *= self.head_vacancy_table[dv][max_v][trg_class] + slots.occupy(j) # mark position as occupied + total_vacancies -= 1 + if value < MIN_PROB: + return MIN_PROB + + # case 3: non-head words + for k in range(1, tablet_length): + previous_position = tablet[k - 1] + previous_vacancies = slots.vacancies_at(previous_position) + j = tablet[k] + dv = slots.vacancies_at(j) - previous_vacancies + max_v = total_vacancies - tablet_length + k + 1 - previous_vacancies + trg_class = self.trg_classes[alignment_info.trg_sentence[j]] + value *= self.non_head_vacancy_table[dv][max_v][trg_class] + slots.occupy(j) # mark position as occupied + total_vacancies -= 1 + if value < MIN_PROB: + return MIN_PROB + + return value + + # end nested functions + + # Abort computation whenever probability falls below MIN_PROB at + # any point, since MIN_PROB can be considered as zero + probability *= null_generation_term() + if probability < MIN_PROB: + return MIN_PROB + + probability *= fertility_term() + if probability < MIN_PROB: + return MIN_PROB + + for j in range(1, len(alignment_info.trg_sentence)): + probability *= lexical_translation_term(j) + if probability < MIN_PROB: + return MIN_PROB + + for i in range(1, len(alignment_info.src_sentence)): + probability *= vacancy_term(i) + if probability < MIN_PROB: + return MIN_PROB + + return probability + + def maximize_vacancy_probabilities(self, counts): + MIN_PROB = IBMModel.MIN_PROB + head_vacancy_table = self.head_vacancy_table + for dv, max_vs in counts.head_vacancy.items(): + for max_v, trg_classes in max_vs.items(): + for t_cls in trg_classes: + estimate = ( + counts.head_vacancy[dv][max_v][t_cls] + / counts.head_vacancy_for_any_dv[max_v][t_cls] + ) + head_vacancy_table[dv][max_v][t_cls] = max(estimate, MIN_PROB) + + non_head_vacancy_table = self.non_head_vacancy_table + for dv, max_vs in counts.non_head_vacancy.items(): + for max_v, trg_classes in max_vs.items(): + for t_cls in trg_classes: + estimate = ( + counts.non_head_vacancy[dv][max_v][t_cls] + / counts.non_head_vacancy_for_any_dv[max_v][t_cls] + ) + non_head_vacancy_table[dv][max_v][t_cls] = max(estimate, MIN_PROB) + + +class Model5Counts(Counts): + """ + Data object to store counts of various parameters during training. + Includes counts for vacancies. + """ + + def __init__(self): + super().__init__() + self.head_vacancy = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: 0.0)) + ) + self.head_vacancy_for_any_dv = defaultdict(lambda: defaultdict(lambda: 0.0)) + self.non_head_vacancy = defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: 0.0)) + ) + self.non_head_vacancy_for_any_dv = defaultdict(lambda: defaultdict(lambda: 0.0)) + + def update_vacancy(self, count, alignment_info, i, trg_classes, slots): + """ + :param count: Value to add to the vacancy counts + :param alignment_info: Alignment under consideration + :param i: Source word position under consideration + :param trg_classes: Target word classes + :param slots: Vacancy states of the slots in the target sentence. + Output parameter that will be modified as new words are placed + in the target sentence. + """ + tablet = alignment_info.cepts[i] + tablet_length = len(tablet) + total_vacancies = slots.vacancies_at(len(slots)) + + # case 1: NULL aligned words + if tablet_length == 0: + return # ignore zero fertility words + + # case 2: head word + j = tablet[0] + previous_cept = alignment_info.previous_cept(j) + previous_center = alignment_info.center_of_cept(previous_cept) + dv = slots.vacancies_at(j) - slots.vacancies_at(previous_center) + max_v = total_vacancies - tablet_length + 1 + trg_class = trg_classes[alignment_info.trg_sentence[j]] + self.head_vacancy[dv][max_v][trg_class] += count + self.head_vacancy_for_any_dv[max_v][trg_class] += count + slots.occupy(j) # mark position as occupied + total_vacancies -= 1 + + # case 3: non-head words + for k in range(1, tablet_length): + previous_position = tablet[k - 1] + previous_vacancies = slots.vacancies_at(previous_position) + j = tablet[k] + dv = slots.vacancies_at(j) - previous_vacancies + max_v = total_vacancies - tablet_length + k + 1 - previous_vacancies + trg_class = trg_classes[alignment_info.trg_sentence[j]] + self.non_head_vacancy[dv][max_v][trg_class] += count + self.non_head_vacancy_for_any_dv[max_v][trg_class] += count + slots.occupy(j) # mark position as occupied + total_vacancies -= 1 + + +class Slots: + """ + Represents positions in a target sentence. Used to keep track of + which slot (position) is occupied. + """ + + def __init__(self, target_sentence_length): + self._slots = [False] * (target_sentence_length + 1) # 1-indexed + + def occupy(self, position): + """ + :return: Mark slot at ``position`` as occupied + """ + self._slots[position] = True + + def vacancies_at(self, position): + """ + :return: Number of vacant slots up to, and including, ``position`` + """ + vacancies = 0 + for k in range(1, position + 1): + if not self._slots[k]: + vacancies += 1 + return vacancies + + def __len__(self): + return len(self._slots) - 1 # exclude dummy zeroeth element diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm_model.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm_model.py new file mode 100644 index 0000000..e2b3760 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ibm_model.py @@ -0,0 +1,549 @@ +# Natural Language Toolkit: IBM Model Core +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +Common methods and classes for all IBM models. See ``IBMModel1``, +``IBMModel2``, ``IBMModel3``, ``IBMModel4``, and ``IBMModel5`` +for specific implementations. + +The IBM models are a series of generative models that learn lexical +translation probabilities, p(target language word|source language word), +given a sentence-aligned parallel corpus. + +The models increase in sophistication from model 1 to 5. Typically, the +output of lower models is used to seed the higher models. All models +use the Expectation-Maximization (EM) algorithm to learn various +probability tables. + +Words in a sentence are one-indexed. The first word of a sentence has +position 1, not 0. Index 0 is reserved in the source sentence for the +NULL token. The concept of position does not apply to NULL, but it is +indexed at 0 by convention. + +Each target word is aligned to exactly one source word or the NULL +token. + +References: +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. + +Peter E Brown, Stephen A. Della Pietra, Vincent J. Della Pietra, and +Robert L. Mercer. 1993. The Mathematics of Statistical Machine +Translation: Parameter Estimation. Computational Linguistics, 19 (2), +263-311. +""" + +from bisect import insort_left +from collections import defaultdict +from copy import deepcopy +from math import ceil + + +def longest_target_sentence_length(sentence_aligned_corpus): + """ + :param sentence_aligned_corpus: Parallel corpus under consideration + :type sentence_aligned_corpus: list(AlignedSent) + :return: Number of words in the longest target language sentence + of ``sentence_aligned_corpus`` + """ + max_m = 0 + for aligned_sentence in sentence_aligned_corpus: + m = len(aligned_sentence.words) + max_m = max(m, max_m) + return max_m + + +class IBMModel: + """ + Abstract base class for all IBM models + """ + + # Avoid division by zero and precision errors by imposing a minimum + # value for probabilities. Note that this approach is theoretically + # incorrect, since it may create probabilities that sum to more + # than 1. In practice, the contribution of probabilities with MIN_PROB + # is tiny enough that the value of MIN_PROB can be treated as zero. + MIN_PROB = 1.0e-12 # GIZA++ is more liberal and uses 1.0e-7 + + def __init__(self, sentence_aligned_corpus): + self.init_vocab(sentence_aligned_corpus) + self.reset_probabilities() + + def reset_probabilities(self): + self.translation_table = defaultdict( + lambda: defaultdict(lambda: IBMModel.MIN_PROB) + ) + """ + dict[str][str]: float. Probability(target word | source word). + Values accessed as ``translation_table[target_word][source_word]``. + """ + + self.alignment_table = defaultdict( + lambda: defaultdict( + lambda: defaultdict(lambda: defaultdict(lambda: IBMModel.MIN_PROB)) + ) + ) + """ + dict[int][int][int][int]: float. Probability(i | j,l,m). + Values accessed as ``alignment_table[i][j][l][m]``. + Used in model 2 and hill climbing in models 3 and above + """ + + self.fertility_table = defaultdict(lambda: defaultdict(lambda: self.MIN_PROB)) + """ + dict[int][str]: float. Probability(fertility | source word). + Values accessed as ``fertility_table[fertility][source_word]``. + Used in model 3 and higher. + """ + + self.p1 = 0.5 + """ + Probability that a generated word requires another target word + that is aligned to NULL. + Used in model 3 and higher. + """ + + def set_uniform_probabilities(self, sentence_aligned_corpus): + """ + Initialize probability tables to a uniform distribution + + Derived classes should implement this accordingly. + """ + pass + + def init_vocab(self, sentence_aligned_corpus): + src_vocab = set() + trg_vocab = set() + for aligned_sentence in sentence_aligned_corpus: + trg_vocab.update(aligned_sentence.words) + src_vocab.update(aligned_sentence.mots) + # Add the NULL token + src_vocab.add(None) + + self.src_vocab = src_vocab + """ + set(str): All source language words used in training + """ + + self.trg_vocab = trg_vocab + """ + set(str): All target language words used in training + """ + + def sample(self, sentence_pair): + """ + Sample the most probable alignments from the entire alignment + space + + First, determine the best alignment according to IBM Model 2. + With this initial alignment, use hill climbing to determine the + best alignment according to a higher IBM Model. Add this + alignment and its neighbors to the sample set. Repeat this + process with other initial alignments obtained by pegging an + alignment point. + + Hill climbing may be stuck in a local maxima, hence the pegging + and trying out of different alignments. + + :param sentence_pair: Source and target language sentence pair + to generate a sample of alignments from + :type sentence_pair: AlignedSent + + :return: A set of best alignments represented by their ``AlignmentInfo`` + and the best alignment of the set for convenience + :rtype: set(AlignmentInfo), AlignmentInfo + """ + sampled_alignments = set() + l = len(sentence_pair.mots) + m = len(sentence_pair.words) + + # Start from the best model 2 alignment + initial_alignment = self.best_model2_alignment(sentence_pair) + potential_alignment = self.hillclimb(initial_alignment) + sampled_alignments.update(self.neighboring(potential_alignment)) + best_alignment = potential_alignment + + # Start from other model 2 alignments, + # with the constraint that j is aligned (pegged) to i + for j in range(1, m + 1): + for i in range(0, l + 1): + initial_alignment = self.best_model2_alignment(sentence_pair, j, i) + potential_alignment = self.hillclimb(initial_alignment, j) + neighbors = self.neighboring(potential_alignment, j) + sampled_alignments.update(neighbors) + if potential_alignment.score > best_alignment.score: + best_alignment = potential_alignment + + return sampled_alignments, best_alignment + + def best_model2_alignment(self, sentence_pair, j_pegged=None, i_pegged=0): + """ + Finds the best alignment according to IBM Model 2 + + Used as a starting point for hill climbing in Models 3 and + above, because it is easier to compute than the best alignments + in higher models + + :param sentence_pair: Source and target language sentence pair + to be word-aligned + :type sentence_pair: AlignedSent + + :param j_pegged: If specified, the alignment point of j_pegged + will be fixed to i_pegged + :type j_pegged: int + + :param i_pegged: Alignment point to j_pegged + :type i_pegged: int + """ + src_sentence = [None] + sentence_pair.mots + trg_sentence = ["UNUSED"] + sentence_pair.words # 1-indexed + + l = len(src_sentence) - 1 # exclude NULL + m = len(trg_sentence) - 1 + + alignment = [0] * (m + 1) # init all alignments to NULL + cepts = [[] for i in range(l + 1)] # init all cepts to empty list + + for j in range(1, m + 1): + if j == j_pegged: + # use the pegged alignment instead of searching for best one + best_i = i_pegged + else: + best_i = 0 + max_alignment_prob = IBMModel.MIN_PROB + t = trg_sentence[j] + + for i in range(0, l + 1): + s = src_sentence[i] + alignment_prob = ( + self.translation_table[t][s] * self.alignment_table[i][j][l][m] + ) + + if alignment_prob >= max_alignment_prob: + max_alignment_prob = alignment_prob + best_i = i + + alignment[j] = best_i + cepts[best_i].append(j) + + return AlignmentInfo( + tuple(alignment), tuple(src_sentence), tuple(trg_sentence), cepts + ) + + def hillclimb(self, alignment_info, j_pegged=None): + """ + Starting from the alignment in ``alignment_info``, look at + neighboring alignments iteratively for the best one + + There is no guarantee that the best alignment in the alignment + space will be found, because the algorithm might be stuck in a + local maximum. + + :param j_pegged: If specified, the search will be constrained to + alignments where ``j_pegged`` remains unchanged + :type j_pegged: int + + :return: The best alignment found from hill climbing + :rtype: AlignmentInfo + """ + alignment = alignment_info # alias with shorter name + max_probability = self.prob_t_a_given_s(alignment) + + while True: + old_alignment = alignment + for neighbor_alignment in self.neighboring(alignment, j_pegged): + neighbor_probability = self.prob_t_a_given_s(neighbor_alignment) + + if neighbor_probability > max_probability: + alignment = neighbor_alignment + max_probability = neighbor_probability + + if alignment == old_alignment: + # Until there are no better alignments + break + + alignment.score = max_probability + return alignment + + def neighboring(self, alignment_info, j_pegged=None): + """ + Determine the neighbors of ``alignment_info``, obtained by + moving or swapping one alignment point + + :param j_pegged: If specified, neighbors that have a different + alignment point from j_pegged will not be considered + :type j_pegged: int + + :return: A set neighboring alignments represented by their + ``AlignmentInfo`` + :rtype: set(AlignmentInfo) + """ + neighbors = set() + + l = len(alignment_info.src_sentence) - 1 # exclude NULL + m = len(alignment_info.trg_sentence) - 1 + original_alignment = alignment_info.alignment + original_cepts = alignment_info.cepts + + for j in range(1, m + 1): + if j != j_pegged: + # Add alignments that differ by one alignment point + for i in range(0, l + 1): + new_alignment = list(original_alignment) + new_cepts = deepcopy(original_cepts) + old_i = original_alignment[j] + + # update alignment + new_alignment[j] = i + + # update cepts + insort_left(new_cepts[i], j) + new_cepts[old_i].remove(j) + + new_alignment_info = AlignmentInfo( + tuple(new_alignment), + alignment_info.src_sentence, + alignment_info.trg_sentence, + new_cepts, + ) + neighbors.add(new_alignment_info) + + for j in range(1, m + 1): + if j != j_pegged: + # Add alignments that have two alignment points swapped + for other_j in range(1, m + 1): + if other_j != j_pegged and other_j != j: + new_alignment = list(original_alignment) + new_cepts = deepcopy(original_cepts) + other_i = original_alignment[other_j] + i = original_alignment[j] + + # update alignments + new_alignment[j] = other_i + new_alignment[other_j] = i + + # update cepts + new_cepts[other_i].remove(other_j) + insort_left(new_cepts[other_i], j) + new_cepts[i].remove(j) + insort_left(new_cepts[i], other_j) + + new_alignment_info = AlignmentInfo( + tuple(new_alignment), + alignment_info.src_sentence, + alignment_info.trg_sentence, + new_cepts, + ) + neighbors.add(new_alignment_info) + + return neighbors + + def maximize_lexical_translation_probabilities(self, counts): + for t, src_words in counts.t_given_s.items(): + for s in src_words: + estimate = counts.t_given_s[t][s] / counts.any_t_given_s[s] + self.translation_table[t][s] = max(estimate, IBMModel.MIN_PROB) + + def maximize_fertility_probabilities(self, counts): + for phi, src_words in counts.fertility.items(): + for s in src_words: + estimate = counts.fertility[phi][s] / counts.fertility_for_any_phi[s] + self.fertility_table[phi][s] = max(estimate, IBMModel.MIN_PROB) + + def maximize_null_generation_probabilities(self, counts): + p1_estimate = counts.p1 / (counts.p1 + counts.p0) + p1_estimate = max(p1_estimate, IBMModel.MIN_PROB) + # Clip p1 if it is too large, because p0 = 1 - p1 should not be + # smaller than MIN_PROB + self.p1 = min(p1_estimate, 1 - IBMModel.MIN_PROB) + + def prob_of_alignments(self, alignments): + probability = 0 + for alignment_info in alignments: + probability += self.prob_t_a_given_s(alignment_info) + return probability + + def prob_t_a_given_s(self, alignment_info): + """ + Probability of target sentence and an alignment given the + source sentence + + All required information is assumed to be in ``alignment_info`` + and self. + + Derived classes should override this method + """ + return 0.0 + + +class AlignmentInfo: + """ + Helper data object for training IBM Models 3 and up + + Read-only. For a source sentence and its counterpart in the target + language, this class holds information about the sentence pair's + alignment, cepts, and fertility. + + Warning: Alignments are one-indexed here, in contrast to + nltk.translate.Alignment and AlignedSent, which are zero-indexed + This class is not meant to be used outside of IBM models. + """ + + def __init__(self, alignment, src_sentence, trg_sentence, cepts): + if not isinstance(alignment, tuple): + raise TypeError( + "The alignment must be a tuple because it is used " + "to uniquely identify AlignmentInfo objects." + ) + + self.alignment = alignment + """ + tuple(int): Alignment function. ``alignment[j]`` is the position + in the source sentence that is aligned to the position j in the + target sentence. + """ + + self.src_sentence = src_sentence + """ + tuple(str): Source sentence referred to by this object. + Should include NULL token (None) in index 0. + """ + + self.trg_sentence = trg_sentence + """ + tuple(str): Target sentence referred to by this object. + Should have a dummy element in index 0 so that the first word + starts from index 1. + """ + + self.cepts = cepts + """ + list(list(int)): The positions of the target words, in + ascending order, aligned to a source word position. For example, + cepts[4] = (2, 3, 7) means that words in positions 2, 3 and 7 + of the target sentence are aligned to the word in position 4 of + the source sentence + """ + + self.score = None + """ + float: Optional. Probability of alignment, as defined by the + IBM model that assesses this alignment + """ + + def fertility_of_i(self, i): + """ + Fertility of word in position ``i`` of the source sentence + """ + return len(self.cepts[i]) + + def is_head_word(self, j): + """ + :return: Whether the word in position ``j`` of the target + sentence is a head word + """ + i = self.alignment[j] + return self.cepts[i][0] == j + + def center_of_cept(self, i): + """ + :return: The ceiling of the average positions of the words in + the tablet of cept ``i``, or 0 if ``i`` is None + """ + if i is None: + return 0 + + average_position = sum(self.cepts[i]) / len(self.cepts[i]) + return int(ceil(average_position)) + + def previous_cept(self, j): + """ + :return: The previous cept of ``j``, or None if ``j`` belongs to + the first cept + """ + i = self.alignment[j] + if i == 0: + raise ValueError( + "Words aligned to NULL cannot have a previous " + "cept because NULL has no position" + ) + previous_cept = i - 1 + while previous_cept > 0 and self.fertility_of_i(previous_cept) == 0: + previous_cept -= 1 + + if previous_cept <= 0: + previous_cept = None + return previous_cept + + def previous_in_tablet(self, j): + """ + :return: The position of the previous word that is in the same + tablet as ``j``, or None if ``j`` is the first word of the + tablet + """ + i = self.alignment[j] + tablet_position = self.cepts[i].index(j) + if tablet_position == 0: + return None + return self.cepts[i][tablet_position - 1] + + def zero_indexed_alignment(self): + """ + :return: Zero-indexed alignment, suitable for use in external + ``nltk.translate`` modules like ``nltk.translate.Alignment`` + :rtype: list(tuple) + """ + zero_indexed_alignment = [] + for j in range(1, len(self.trg_sentence)): + i = self.alignment[j] - 1 + if i < 0: + i = None # alignment to NULL token + zero_indexed_alignment.append((j - 1, i)) + return zero_indexed_alignment + + def __eq__(self, other): + return self.alignment == other.alignment + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.alignment) + + +class Counts: + """ + Data object to store counts of various parameters during training + """ + + def __init__(self): + self.t_given_s = defaultdict(lambda: defaultdict(lambda: 0.0)) + self.any_t_given_s = defaultdict(lambda: 0.0) + self.p0 = 0.0 + self.p1 = 0.0 + self.fertility = defaultdict(lambda: defaultdict(lambda: 0.0)) + self.fertility_for_any_phi = defaultdict(lambda: 0.0) + + def update_lexical_translation(self, count, alignment_info, j): + i = alignment_info.alignment[j] + t = alignment_info.trg_sentence[j] + s = alignment_info.src_sentence[i] + self.t_given_s[t][s] += count + self.any_t_given_s[s] += count + + def update_null_generation(self, count, alignment_info): + m = len(alignment_info.trg_sentence) - 1 + fertility_of_null = alignment_info.fertility_of_i(0) + self.p1 += fertility_of_null * count + self.p0 += (m - 2 * fertility_of_null) * count + + def update_fertility(self, count, alignment_info): + for i in range(0, len(alignment_info.src_sentence)): + s = alignment_info.src_sentence[i] + phi = alignment_info.fertility_of_i(i) + self.fertility[phi][s] += count + self.fertility_for_any_phi[s] += count diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/meteor_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/meteor_score.py new file mode 100644 index 0000000..e53fb86 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/meteor_score.py @@ -0,0 +1,409 @@ +# Natural Language Toolkit: Machine Translation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Uday Krishna +# Contributor: Tom Aarsen +# URL: +# For license information, see LICENSE.TXT + + +from itertools import chain, product +from typing import Callable, Iterable, List, Tuple + +from nltk.corpus import WordNetCorpusReader, wordnet +from nltk.stem.api import StemmerI +from nltk.stem.porter import PorterStemmer + + +def _generate_enums( + hypothesis: Iterable[str], + reference: Iterable[str], + preprocess: Callable[[str], str] = str.lower, +) -> Tuple[List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Takes in pre-tokenized inputs for hypothesis and reference and returns + enumerated word lists for each of them + + :param hypothesis: pre-tokenized hypothesis + :param reference: pre-tokenized reference + :preprocess: preprocessing method (default str.lower) + :return: enumerated words list + """ + if isinstance(hypothesis, str): + raise TypeError( + f'"hypothesis" expects pre-tokenized hypothesis (Iterable[str]): {hypothesis}' + ) + + if isinstance(reference, str): + raise TypeError( + f'"reference" expects pre-tokenized reference (Iterable[str]): {reference}' + ) + + enum_hypothesis_list = list(enumerate(map(preprocess, hypothesis))) + enum_reference_list = list(enumerate(map(preprocess, reference))) + return enum_hypothesis_list, enum_reference_list + + +def exact_match( + hypothesis: Iterable[str], reference: Iterable[str] +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + matches exact words in hypothesis and reference + and returns a word mapping based on the enumerated + word id between hypothesis and reference + + :param hypothesis: pre-tokenized hypothesis + :param reference: pre-tokenized reference + :return: enumerated matched tuples, enumerated unmatched hypothesis tuples, + enumerated unmatched reference tuples + """ + enum_hypothesis_list, enum_reference_list = _generate_enums(hypothesis, reference) + return _match_enums(enum_hypothesis_list, enum_reference_list) + + +def _match_enums( + enum_hypothesis_list: List[Tuple[int, str]], + enum_reference_list: List[Tuple[int, str]], +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + matches exact words in hypothesis and reference and returns + a word mapping between enum_hypothesis_list and enum_reference_list + based on the enumerated word id. + + :param enum_hypothesis_list: enumerated hypothesis list + :param enum_reference_list: enumerated reference list + :return: enumerated matched tuples, enumerated unmatched hypothesis tuples, + enumerated unmatched reference tuples + """ + word_match = [] + for i in range(len(enum_hypothesis_list))[::-1]: + for j in range(len(enum_reference_list))[::-1]: + if enum_hypothesis_list[i][1] == enum_reference_list[j][1]: + word_match.append( + (enum_hypothesis_list[i][0], enum_reference_list[j][0]) + ) + enum_hypothesis_list.pop(i) + enum_reference_list.pop(j) + break + return word_match, enum_hypothesis_list, enum_reference_list + + +def _enum_stem_match( + enum_hypothesis_list: List[Tuple[int, str]], + enum_reference_list: List[Tuple[int, str]], + stemmer: StemmerI = PorterStemmer(), +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Stems each word and matches them in hypothesis and reference + and returns a word mapping between enum_hypothesis_list and + enum_reference_list based on the enumerated word id. The function also + returns a enumerated list of unmatched words for hypothesis and reference. + + :param enum_hypothesis_list: enumerated hypothesis list + :param enum_reference_list: enumerated reference list + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :return: enumerated matched tuples, enumerated unmatched hypothesis tuples, + enumerated unmatched reference tuples + """ + stemmed_enum_hypothesis_list = [ + (word_pair[0], stemmer.stem(word_pair[1])) for word_pair in enum_hypothesis_list + ] + + stemmed_enum_reference_list = [ + (word_pair[0], stemmer.stem(word_pair[1])) for word_pair in enum_reference_list + ] + + return _match_enums(stemmed_enum_hypothesis_list, stemmed_enum_reference_list) + + +def stem_match( + hypothesis: Iterable[str], + reference: Iterable[str], + stemmer: StemmerI = PorterStemmer(), +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Stems each word and matches them in hypothesis and reference + and returns a word mapping between hypothesis and reference + + :param hypothesis: pre-tokenized hypothesis + :param reference: pre-tokenized reference + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :return: enumerated matched tuples, enumerated unmatched hypothesis tuples, + enumerated unmatched reference tuples + """ + enum_hypothesis_list, enum_reference_list = _generate_enums(hypothesis, reference) + return _enum_stem_match(enum_hypothesis_list, enum_reference_list, stemmer=stemmer) + + +def _enum_wordnetsyn_match( + enum_hypothesis_list: List[Tuple[int, str]], + enum_reference_list: List[Tuple[int, str]], + wordnet: WordNetCorpusReader = wordnet, +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Matches each word in reference to a word in hypothesis + if any synonym of a hypothesis word is the exact match + to the reference word. + + :param enum_hypothesis_list: enumerated hypothesis list + :param enum_reference_list: enumerated reference list + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + """ + word_match = [] + for i in range(len(enum_hypothesis_list))[::-1]: + hypothesis_syns = set( + chain.from_iterable( + ( + lemma.name() + for lemma in synset.lemmas() + if lemma.name().find("_") < 0 + ) + for synset in wordnet.synsets(enum_hypothesis_list[i][1]) + ) + ).union({enum_hypothesis_list[i][1]}) + for j in range(len(enum_reference_list))[::-1]: + if enum_reference_list[j][1] in hypothesis_syns: + word_match.append( + (enum_hypothesis_list[i][0], enum_reference_list[j][0]) + ) + enum_hypothesis_list.pop(i) + enum_reference_list.pop(j) + break + return word_match, enum_hypothesis_list, enum_reference_list + + +def wordnetsyn_match( + hypothesis: Iterable[str], + reference: Iterable[str], + wordnet: WordNetCorpusReader = wordnet, +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Matches each word in reference to a word in hypothesis if any synonym + of a hypothesis word is the exact match to the reference word. + + :param hypothesis: pre-tokenized hypothesis + :param reference: pre-tokenized reference + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + :return: list of mapped tuples + """ + enum_hypothesis_list, enum_reference_list = _generate_enums(hypothesis, reference) + return _enum_wordnetsyn_match( + enum_hypothesis_list, enum_reference_list, wordnet=wordnet + ) + + +def _enum_align_words( + enum_hypothesis_list: List[Tuple[int, str]], + enum_reference_list: List[Tuple[int, str]], + stemmer: StemmerI = PorterStemmer(), + wordnet: WordNetCorpusReader = wordnet, +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Aligns/matches words in the hypothesis to reference by sequentially + applying exact match, stemmed match and wordnet based synonym match. + in case there are multiple matches the match which has the least number + of crossing is chosen. Takes enumerated list as input instead of + string input + + :param enum_hypothesis_list: enumerated hypothesis list + :param enum_reference_list: enumerated reference list + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + :return: sorted list of matched tuples, unmatched hypothesis list, + unmatched reference list + """ + exact_matches, enum_hypothesis_list, enum_reference_list = _match_enums( + enum_hypothesis_list, enum_reference_list + ) + + stem_matches, enum_hypothesis_list, enum_reference_list = _enum_stem_match( + enum_hypothesis_list, enum_reference_list, stemmer=stemmer + ) + + wns_matches, enum_hypothesis_list, enum_reference_list = _enum_wordnetsyn_match( + enum_hypothesis_list, enum_reference_list, wordnet=wordnet + ) + + return ( + sorted( + exact_matches + stem_matches + wns_matches, key=lambda wordpair: wordpair[0] + ), + enum_hypothesis_list, + enum_reference_list, + ) + + +def align_words( + hypothesis: Iterable[str], + reference: Iterable[str], + stemmer: StemmerI = PorterStemmer(), + wordnet: WordNetCorpusReader = wordnet, +) -> Tuple[List[Tuple[int, int]], List[Tuple[int, str]], List[Tuple[int, str]]]: + """ + Aligns/matches words in the hypothesis to reference by sequentially + applying exact match, stemmed match and wordnet based synonym match. + In case there are multiple matches the match which has the least number + of crossing is chosen. + + :param hypothesis: pre-tokenized hypothesis + :param reference: pre-tokenized reference + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + :return: sorted list of matched tuples, unmatched hypothesis list, unmatched reference list + """ + enum_hypothesis_list, enum_reference_list = _generate_enums(hypothesis, reference) + return _enum_align_words( + enum_hypothesis_list, enum_reference_list, stemmer=stemmer, wordnet=wordnet + ) + + +def _count_chunks(matches: List[Tuple[int, int]]) -> int: + """ + Counts the fewest possible number of chunks such that matched unigrams + of each chunk are adjacent to each other. This is used to calculate the + fragmentation part of the metric. + + :param matches: list containing a mapping of matched words (output of align_words) + :return: Number of chunks a sentence is divided into post alignment + """ + i = 0 + chunks = 1 + while i < len(matches) - 1: + if (matches[i + 1][0] == matches[i][0] + 1) and ( + matches[i + 1][1] == matches[i][1] + 1 + ): + i += 1 + continue + i += 1 + chunks += 1 + return chunks + + +def single_meteor_score( + reference: Iterable[str], + hypothesis: Iterable[str], + preprocess: Callable[[str], str] = str.lower, + stemmer: StemmerI = PorterStemmer(), + wordnet: WordNetCorpusReader = wordnet, + alpha: float = 0.9, + beta: float = 3.0, + gamma: float = 0.5, +) -> float: + """ + Calculates METEOR score for single hypothesis and reference as per + "Meteor: An Automatic Metric for MT Evaluation with HighLevels of + Correlation with Human Judgments" by Alon Lavie and Abhaya Agarwal, + in Proceedings of ACL. + https://www.cs.cmu.edu/~alavie/METEOR/pdf/Lavie-Agarwal-2007-METEOR.pdf + + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', 'ensures', 'that', 'the', 'military', 'always', 'obeys', 'the', 'commands', 'of', 'the', 'party'] + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', 'ensures', 'that', 'the', 'military', 'will', 'forever', 'heed', 'Party', 'commands'] + + + >>> round(single_meteor_score(reference1, hypothesis1),4) + 0.6944 + + If there is no words match during the alignment the method returns the + score as 0. We can safely return a zero instead of raising a + division by zero error as no match usually implies a bad translation. + + >>> round(single_meteor_score(['this', 'is', 'a', 'cat'], ['non', 'matching', 'hypothesis']),4) + 0.0 + + :param reference: pre-tokenized reference + :param hypothesis: pre-tokenized hypothesis + :param preprocess: preprocessing function (default str.lower) + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + :param alpha: parameter for controlling relative weights of precision and recall. + :param beta: parameter for controlling shape of penalty as a + function of as a function of fragmentation. + :param gamma: relative weight assigned to fragmentation penalty. + :return: The sentence-level METEOR score. + """ + enum_hypothesis, enum_reference = _generate_enums( + hypothesis, reference, preprocess=preprocess + ) + translation_length = len(enum_hypothesis) + reference_length = len(enum_reference) + matches, _, _ = _enum_align_words( + enum_hypothesis, enum_reference, stemmer=stemmer, wordnet=wordnet + ) + matches_count = len(matches) + try: + precision = float(matches_count) / translation_length + recall = float(matches_count) / reference_length + fmean = (precision * recall) / (alpha * precision + (1 - alpha) * recall) + chunk_count = float(_count_chunks(matches)) + frag_frac = chunk_count / matches_count + except ZeroDivisionError: + return 0.0 + penalty = gamma * frag_frac**beta + return (1 - penalty) * fmean + + +def meteor_score( + references: Iterable[Iterable[str]], + hypothesis: Iterable[str], + preprocess: Callable[[str], str] = str.lower, + stemmer: StemmerI = PorterStemmer(), + wordnet: WordNetCorpusReader = wordnet, + alpha: float = 0.9, + beta: float = 3.0, + gamma: float = 0.5, +) -> float: + """ + Calculates METEOR score for hypothesis with multiple references as + described in "Meteor: An Automatic Metric for MT Evaluation with + HighLevels of Correlation with Human Judgments" by Alon Lavie and + Abhaya Agarwal, in Proceedings of ACL. + https://www.cs.cmu.edu/~alavie/METEOR/pdf/Lavie-Agarwal-2007-METEOR.pdf + + + In case of multiple references the best score is chosen. This method + iterates over single_meteor_score and picks the best pair among all + the references for a given hypothesis + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', 'ensures', 'that', 'the', 'military', 'always', 'obeys', 'the', 'commands', 'of', 'the', 'party'] + >>> hypothesis2 = ['It', 'is', 'to', 'insure', 'the', 'troops', 'forever', 'hearing', 'the', 'activity', 'guidebook', 'that', 'party', 'direct'] + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', 'ensures', 'that', 'the', 'military', 'will', 'forever', 'heed', 'Party', 'commands'] + >>> reference2 = ['It', 'is', 'the', 'guiding', 'principle', 'which', 'guarantees', 'the', 'military', 'forces', 'always', 'being', 'under', 'the', 'command', 'of', 'the', 'Party'] + >>> reference3 = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', 'army', 'always', 'to', 'heed', 'the', 'directions', 'of', 'the', 'party'] + + >>> round(meteor_score([reference1, reference2, reference3], hypothesis1),4) + 0.6944 + + If there is no words match during the alignment the method returns the + score as 0. We can safely return a zero instead of raising a + division by zero error as no match usually implies a bad translation. + + >>> round(meteor_score([['this', 'is', 'a', 'cat']], ['non', 'matching', 'hypothesis']),4) + 0.0 + + :param references: pre-tokenized reference sentences + :param hypothesis: a pre-tokenized hypothesis sentence + :param preprocess: preprocessing function (default str.lower) + :param stemmer: nltk.stem.api.StemmerI object (default PorterStemmer()) + :param wordnet: a wordnet corpus reader object (default nltk.corpus.wordnet) + :param alpha: parameter for controlling relative weights of precision and recall. + :param beta: parameter for controlling shape of penalty as a function + of as a function of fragmentation. + :param gamma: relative weight assigned to fragmentation penalty. + :return: The sentence-level METEOR score. + """ + return max( + single_meteor_score( + reference, + hypothesis, + preprocess=preprocess, + stemmer=stemmer, + wordnet=wordnet, + alpha=alpha, + beta=beta, + gamma=gamma, + ) + for reference in references + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/metrics.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/metrics.py new file mode 100644 index 0000000..241c93e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/metrics.py @@ -0,0 +1,41 @@ +# Natural Language Toolkit: Translation metrics +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Will Zhang +# Guan Gui +# Steven Bird +# URL: +# For license information, see LICENSE.TXT + + +def alignment_error_rate(reference, hypothesis, possible=None): + """ + Return the Alignment Error Rate (AER) of an alignment + with respect to a "gold standard" reference alignment. + Return an error rate between 0.0 (perfect alignment) and 1.0 (no + alignment). + + >>> from nltk.translate import Alignment + >>> ref = Alignment([(0, 0), (1, 1), (2, 2)]) + >>> test = Alignment([(0, 0), (1, 2), (2, 1)]) + >>> alignment_error_rate(ref, test) # doctest: +ELLIPSIS + 0.6666666666666667 + + :type reference: Alignment + :param reference: A gold standard alignment (sure alignments) + :type hypothesis: Alignment + :param hypothesis: A hypothesis alignment (aka. candidate alignments) + :type possible: Alignment or None + :param possible: A gold standard reference of possible alignments + (defaults to *reference* if None) + :rtype: float or None + """ + + if possible is None: + possible = reference + else: + assert reference.issubset(possible) # sanity check + + return 1.0 - (len(hypothesis & reference) + len(hypothesis & possible)) / float( + len(hypothesis) + len(reference) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/nist_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/nist_score.py new file mode 100644 index 0000000..6e3d42a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/nist_score.py @@ -0,0 +1,195 @@ +# Natural Language Toolkit: NIST Score +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: +# Contributors: +# URL: +# For license information, see LICENSE.TXT + +"""NIST score implementation.""" + +import fractions +import math +from collections import Counter + +from nltk.util import ngrams + + +def sentence_nist(references, hypothesis, n=5): + """ + Calculate NIST score from + George Doddington. 2002. "Automatic evaluation of machine translation quality + using n-gram co-occurrence statistics." Proceedings of HLT. + Morgan Kaufmann Publishers Inc. https://dl.acm.org/citation.cfm?id=1289189.1289273 + + DARPA commissioned NIST to develop an MT evaluation facility based on the BLEU + score. The official script used by NIST to compute BLEU and NIST score is + mteval-14.pl. The main differences are: + + - BLEU uses geometric mean of the ngram overlaps, NIST uses arithmetic mean. + - NIST has a different brevity penalty + - NIST score from mteval-14.pl has a self-contained tokenizer + + Note: The mteval-14.pl includes a smoothing function for BLEU score that is NOT + used in the NIST score computation. + + >>> hypothesis1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + + >>> hypothesis2 = ['It', 'is', 'to', 'insure', 'the', 'troops', + ... 'forever', 'hearing', 'the', 'activity', 'guidebook', + ... 'that', 'party', 'direct'] + + >>> reference1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', 'forever', + ... 'heed', 'Party', 'commands'] + + >>> reference2 = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', + ... 'Party'] + + >>> reference3 = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + + >>> sentence_nist([reference1, reference2, reference3], hypothesis1) # doctest: +ELLIPSIS + 3.3709... + + >>> sentence_nist([reference1, reference2, reference3], hypothesis2) # doctest: +ELLIPSIS + 1.4619... + + :param references: reference sentences + :type references: list(list(str)) + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) + :param n: highest n-gram order + :type n: int + """ + return corpus_nist([references], [hypothesis], n) + + +def corpus_nist(list_of_references, hypotheses, n=5): + """ + Calculate a single corpus-level NIST score (aka. system-level BLEU) for all + the hypotheses and their respective references. + + :param references: a corpus of lists of reference sentences, w.r.t. hypotheses + :type references: list(list(list(str))) + :param hypotheses: a list of hypothesis sentences + :type hypotheses: list(list(str)) + :param n: highest n-gram order + :type n: int + """ + # Before proceeding to compute NIST, perform sanity checks. + assert len(list_of_references) == len( + hypotheses + ), "The number of hypotheses and their reference(s) should be the same" + + # Collect the ngram coounts from the reference sentences. + ngram_freq = Counter() + total_reference_words = 0 + for ( + references + ) in list_of_references: # For each source sent, there's a list of reference sents. + for reference in references: + # For each order of ngram, count the ngram occurrences. + for i in range(1, n + 1): + ngram_freq.update(ngrams(reference, i)) + total_reference_words += len(reference) + + # Compute the information weights based on the reference sentences. + # Eqn 2 in Doddington (2002): + # Info(w_1 ... w_n) = log_2 [ (# of occurrences of w_1 ... w_n-1) / (# of occurrences of w_1 ... w_n) ] + information_weights = {} + for _ngram in ngram_freq: # w_1 ... w_n + _mgram = _ngram[:-1] # w_1 ... w_n-1 + # From https://github.com/moses-smt/mosesdecoder/blob/master/scripts/generic/mteval-v13a.pl#L546 + # it's computed as such: + # denominator = ngram_freq[_mgram] if _mgram and _mgram in ngram_freq else denominator = total_reference_words + # information_weights[_ngram] = -1 * math.log(ngram_freq[_ngram]/denominator) / math.log(2) + # + # Mathematically, it's equivalent to the our implementation: + if _mgram and _mgram in ngram_freq: + numerator = ngram_freq[_mgram] + else: + numerator = total_reference_words + information_weights[_ngram] = math.log(numerator / ngram_freq[_ngram], 2) + + # Micro-average. + nist_precision_numerator_per_ngram = Counter() + nist_precision_denominator_per_ngram = Counter() + l_ref, l_sys = 0, 0 + # For each order of ngram. + for i in range(1, n + 1): + # Iterate through each hypothesis and their corresponding references. + for references, hypothesis in zip(list_of_references, hypotheses): + hyp_len = len(hypothesis) + + # Find reference with the best NIST score. + nist_score_per_ref = [] + for reference in references: + _ref_len = len(reference) + # Counter of ngrams in hypothesis. + hyp_ngrams = ( + Counter(ngrams(hypothesis, i)) + if len(hypothesis) >= i + else Counter() + ) + ref_ngrams = ( + Counter(ngrams(reference, i)) if len(reference) >= i else Counter() + ) + ngram_overlaps = hyp_ngrams & ref_ngrams + # Precision part of the score in Eqn 3 + _numerator = sum( + information_weights[_ngram] * count + for _ngram, count in ngram_overlaps.items() + ) + _denominator = sum(hyp_ngrams.values()) + _precision = 0 if _denominator == 0 else _numerator / _denominator + nist_score_per_ref.append( + (_precision, _numerator, _denominator, _ref_len) + ) + # Best reference. + precision, numerator, denominator, ref_len = max(nist_score_per_ref) + nist_precision_numerator_per_ngram[i] += numerator + nist_precision_denominator_per_ngram[i] += denominator + l_ref += ref_len + l_sys += hyp_len + + # Final NIST micro-average mean aggregation. + nist_precision = 0 + for i in nist_precision_numerator_per_ngram: + precision = ( + nist_precision_numerator_per_ngram[i] + / nist_precision_denominator_per_ngram[i] + ) + nist_precision += precision + # Eqn 3 in Doddington(2002) + return nist_precision * nist_length_penalty(l_ref, l_sys) + + +def nist_length_penalty(ref_len, hyp_len): + """ + Calculates the NIST length penalty, from Eq. 3 in Doddington (2002) + + penalty = exp( beta * log( min( len(hyp)/len(ref) , 1.0 ))) + + where, + + `beta` is chosen to make the brevity penalty factor = 0.5 when the + no. of words in the system output (hyp) is 2/3 of the average + no. of words in the reference translation (ref) + + The NIST penalty is different from BLEU's such that it minimize the impact + of the score of small variations in the length of a translation. + See Fig. 4 in Doddington (2002) + """ + ratio = hyp_len / ref_len + if 0 < ratio < 1: + ratio_x, score_x = 1.5, 0.5 + beta = math.log(score_x) / math.log(ratio_x) ** 2 + return math.exp(beta * math.log(ratio) ** 2) + else: # ratio <= 0 or ratio >= 1 + return max(min(ratio, 1.0), 0.0) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/phrase_based.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/phrase_based.py new file mode 100644 index 0000000..b0d7ad1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/phrase_based.py @@ -0,0 +1,193 @@ +# Natural Language Toolkit: Phrase Extraction Algorithm +# +# Copyright (C) 2001-2023 NLTK Project +# Authors: Liling Tan, Fredrik Hedman, Petra Barancikova +# URL: +# For license information, see LICENSE.TXT + + +def extract( + f_start, + f_end, + e_start, + e_end, + alignment, + f_aligned, + srctext, + trgtext, + srclen, + trglen, + max_phrase_length, +): + """ + This function checks for alignment point consistency and extracts + phrases using the chunk of consistent phrases. + + A phrase pair (e, f ) is consistent with an alignment A if and only if: + + (i) No English words in the phrase pair are aligned to words outside it. + + ∀e i ∈ e, (e i , f j ) ∈ A ⇒ f j ∈ f + + (ii) No Foreign words in the phrase pair are aligned to words outside it. + + ∀f j ∈ f , (e i , f j ) ∈ A ⇒ e i ∈ e + + (iii) The phrase pair contains at least one alignment point. + + ∃e i ∈ e ̄ , f j ∈ f ̄ s.t. (e i , f j ) ∈ A + + :type f_start: int + :param f_start: Starting index of the possible foreign language phrases + :type f_end: int + :param f_end: End index of the possible foreign language phrases + :type e_start: int + :param e_start: Starting index of the possible source language phrases + :type e_end: int + :param e_end: End index of the possible source language phrases + :type srctext: list + :param srctext: The source language tokens, a list of string. + :type trgtext: list + :param trgtext: The target language tokens, a list of string. + :type srclen: int + :param srclen: The number of tokens in the source language tokens. + :type trglen: int + :param trglen: The number of tokens in the target language tokens. + """ + + if f_end < 0: # 0-based indexing. + return {} + # Check if alignment points are consistent. + for e, f in alignment: + if (f_start <= f <= f_end) and (e < e_start or e > e_end): + return {} + + # Add phrase pairs (incl. additional unaligned f) + phrases = set() + fs = f_start + while True: + fe = min(f_end, f_start + max_phrase_length - 1) + while True: + # add phrase pair ([e_start, e_end], [fs, fe]) to set E + # Need to +1 in range to include the end-point. + src_phrase = " ".join(srctext[e_start : e_end + 1]) + trg_phrase = " ".join(trgtext[fs : fe + 1]) + # Include more data for later ordering. + phrases.add(((e_start, e_end + 1), (fs, fe + 1), src_phrase, trg_phrase)) + fe += 1 + if fe in f_aligned or fe >= trglen: + break + fs -= 1 + if fs in f_aligned or fs < 0: + break + return phrases + + +def phrase_extraction(srctext, trgtext, alignment, max_phrase_length=0): + """ + Phrase extraction algorithm extracts all consistent phrase pairs from + a word-aligned sentence pair. + + The idea is to loop over all possible source language (e) phrases and find + the minimal foreign phrase (f) that matches each of them. Matching is done + by identifying all alignment points for the source phrase and finding the + shortest foreign phrase that includes all the foreign counterparts for the + source words. + + In short, a phrase alignment has to + (a) contain all alignment points for all covered words + (b) contain at least one alignment point + + >>> srctext = "michael assumes that he will stay in the house" + >>> trgtext = "michael geht davon aus , dass er im haus bleibt" + >>> alignment = [(0,0), (1,1), (1,2), (1,3), (2,5), (3,6), (4,9), + ... (5,9), (6,7), (7,7), (8,8)] + >>> phrases = phrase_extraction(srctext, trgtext, alignment) + >>> for i in sorted(phrases): + ... print(i) + ... + ((0, 1), (0, 1), 'michael', 'michael') + ((0, 2), (0, 4), 'michael assumes', 'michael geht davon aus') + ((0, 2), (0, 5), 'michael assumes', 'michael geht davon aus ,') + ((0, 3), (0, 6), 'michael assumes that', 'michael geht davon aus , dass') + ((0, 4), (0, 7), 'michael assumes that he', 'michael geht davon aus , dass er') + ((0, 9), (0, 10), 'michael assumes that he will stay in the house', 'michael geht davon aus , dass er im haus bleibt') + ((1, 2), (1, 4), 'assumes', 'geht davon aus') + ((1, 2), (1, 5), 'assumes', 'geht davon aus ,') + ((1, 3), (1, 6), 'assumes that', 'geht davon aus , dass') + ((1, 4), (1, 7), 'assumes that he', 'geht davon aus , dass er') + ((1, 9), (1, 10), 'assumes that he will stay in the house', 'geht davon aus , dass er im haus bleibt') + ((2, 3), (4, 6), 'that', ', dass') + ((2, 3), (5, 6), 'that', 'dass') + ((2, 4), (4, 7), 'that he', ', dass er') + ((2, 4), (5, 7), 'that he', 'dass er') + ((2, 9), (4, 10), 'that he will stay in the house', ', dass er im haus bleibt') + ((2, 9), (5, 10), 'that he will stay in the house', 'dass er im haus bleibt') + ((3, 4), (6, 7), 'he', 'er') + ((3, 9), (6, 10), 'he will stay in the house', 'er im haus bleibt') + ((4, 6), (9, 10), 'will stay', 'bleibt') + ((4, 9), (7, 10), 'will stay in the house', 'im haus bleibt') + ((6, 8), (7, 8), 'in the', 'im') + ((6, 9), (7, 9), 'in the house', 'im haus') + ((8, 9), (8, 9), 'house', 'haus') + + :type srctext: str + :param srctext: The sentence string from the source language. + :type trgtext: str + :param trgtext: The sentence string from the target language. + :type alignment: list(tuple) + :param alignment: The word alignment outputs as list of tuples, where + the first elements of tuples are the source words' indices and + second elements are the target words' indices. This is also the output + format of nltk.translate.ibm1 + :rtype: list(tuple) + :return: A list of tuples, each element in a list is a phrase and each + phrase is a tuple made up of (i) its source location, (ii) its target + location, (iii) the source phrase and (iii) the target phrase. The phrase + list of tuples represents all the possible phrases extracted from the + word alignments. + :type max_phrase_length: int + :param max_phrase_length: maximal phrase length, if 0 or not specified + it is set to a length of the longer sentence (srctext or trgtext). + """ + + srctext = srctext.split() # e + trgtext = trgtext.split() # f + srclen = len(srctext) # len(e) + trglen = len(trgtext) # len(f) + # Keeps an index of which source/target words that are aligned. + f_aligned = [j for _, j in alignment] + max_phrase_length = max_phrase_length or max(srclen, trglen) + + # set of phrase pairs BP + bp = set() + + for e_start in range(srclen): + max_idx = min(srclen, e_start + max_phrase_length) + for e_end in range(e_start, max_idx): + # // find the minimally matching foreign phrase + # (f start , f end ) = ( length(f), 0 ) + # f_start ∈ [0, len(f) - 1]; f_end ∈ [0, len(f) - 1] + f_start, f_end = trglen - 1, -1 # 0-based indexing + + for e, f in alignment: + if e_start <= e <= e_end: + f_start = min(f, f_start) + f_end = max(f, f_end) + # add extract (f start , f end , e start , e end ) to set BP + phrases = extract( + f_start, + f_end, + e_start, + e_end, + alignment, + f_aligned, + srctext, + trgtext, + srclen, + trglen, + max_phrase_length, + ) + if phrases: + bp.update(phrases) + return bp diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ribes_score.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ribes_score.py new file mode 100644 index 0000000..63ecaf1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/ribes_score.py @@ -0,0 +1,330 @@ +# Natural Language Toolkit: RIBES Score +# +# Copyright (C) 2001-2023 NLTK Project +# Contributors: Katsuhito Sudoh, Liling Tan, Kasramvd, J.F.Sebastian +# Mark Byers, ekhumoro, P. Ortiz +# URL: +# For license information, see LICENSE.TXT +""" RIBES score implementation """ + +import math +from itertools import islice + +from nltk.util import choose, ngrams + + +def sentence_ribes(references, hypothesis, alpha=0.25, beta=0.10): + """ + The RIBES (Rank-based Intuitive Bilingual Evaluation Score) from + Hideki Isozaki, Tsutomu Hirao, Kevin Duh, Katsuhito Sudoh and + Hajime Tsukada. 2010. "Automatic Evaluation of Translation Quality for + Distant Language Pairs". In Proceedings of EMNLP. + https://www.aclweb.org/anthology/D/D10/D10-1092.pdf + + The generic RIBES scores used in shared task, e.g. Workshop for + Asian Translation (WAT) uses the following RIBES calculations: + + RIBES = kendall_tau * (alpha**p1) * (beta**bp) + + Please note that this re-implementation differs from the official + RIBES implementation and though it emulates the results as describe + in the original paper, there are further optimization implemented + in the official RIBES script. + + Users are encouraged to use the official RIBES script instead of this + implementation when evaluating your machine translation system. Refer + to https://www.kecl.ntt.co.jp/icl/lirg/ribes/ for the official script. + + :param references: a list of reference sentences + :type references: list(list(str)) + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) + :param alpha: hyperparameter used as a prior for the unigram precision. + :type alpha: float + :param beta: hyperparameter used as a prior for the brevity penalty. + :type beta: float + :return: The best ribes score from one of the references. + :rtype: float + """ + best_ribes = -1.0 + # Calculates RIBES for each reference and returns the best score. + for reference in references: + # Collects the *worder* from the ranked correlation alignments. + worder = word_rank_alignment(reference, hypothesis) + nkt = kendall_tau(worder) + + # Calculates the brevity penalty + bp = min(1.0, math.exp(1.0 - len(reference) / len(hypothesis))) + + # Calculates the unigram precision, *p1* + p1 = len(worder) / len(hypothesis) + + _ribes = nkt * (p1**alpha) * (bp**beta) + + if _ribes > best_ribes: # Keeps the best score. + best_ribes = _ribes + + return best_ribes + + +def corpus_ribes(list_of_references, hypotheses, alpha=0.25, beta=0.10): + """ + This function "calculates RIBES for a system output (hypothesis) with + multiple references, and returns "best" score among multi-references and + individual scores. The scores are corpus-wise, i.e., averaged by the number + of sentences." (c.f. RIBES version 1.03.1 code). + + Different from BLEU's micro-average precision, RIBES calculates the + macro-average precision by averaging the best RIBES score for each pair of + hypothesis and its corresponding references + + >>> hyp1 = ['It', 'is', 'a', 'guide', 'to', 'action', 'which', + ... 'ensures', 'that', 'the', 'military', 'always', + ... 'obeys', 'the', 'commands', 'of', 'the', 'party'] + >>> ref1a = ['It', 'is', 'a', 'guide', 'to', 'action', 'that', + ... 'ensures', 'that', 'the', 'military', 'will', 'forever', + ... 'heed', 'Party', 'commands'] + >>> ref1b = ['It', 'is', 'the', 'guiding', 'principle', 'which', + ... 'guarantees', 'the', 'military', 'forces', 'always', + ... 'being', 'under', 'the', 'command', 'of', 'the', 'Party'] + >>> ref1c = ['It', 'is', 'the', 'practical', 'guide', 'for', 'the', + ... 'army', 'always', 'to', 'heed', 'the', 'directions', + ... 'of', 'the', 'party'] + + >>> hyp2 = ['he', 'read', 'the', 'book', 'because', 'he', 'was', + ... 'interested', 'in', 'world', 'history'] + >>> ref2a = ['he', 'was', 'interested', 'in', 'world', 'history', + ... 'because', 'he', 'read', 'the', 'book'] + + >>> list_of_references = [[ref1a, ref1b, ref1c], [ref2a]] + >>> hypotheses = [hyp1, hyp2] + >>> round(corpus_ribes(list_of_references, hypotheses),4) + 0.3597 + + :param references: a corpus of lists of reference sentences, w.r.t. hypotheses + :type references: list(list(list(str))) + :param hypotheses: a list of hypothesis sentences + :type hypotheses: list(list(str)) + :param alpha: hyperparameter used as a prior for the unigram precision. + :type alpha: float + :param beta: hyperparameter used as a prior for the brevity penalty. + :type beta: float + :return: The best ribes score from one of the references. + :rtype: float + """ + corpus_best_ribes = 0.0 + # Iterate through each hypothesis and their corresponding references. + for references, hypothesis in zip(list_of_references, hypotheses): + corpus_best_ribes += sentence_ribes(references, hypothesis, alpha, beta) + return corpus_best_ribes / len(hypotheses) + + +def position_of_ngram(ngram, sentence): + """ + This function returns the position of the first instance of the ngram + appearing in a sentence. + + Note that one could also use string as follows but the code is a little + convoluted with type casting back and forth: + + char_pos = ' '.join(sent)[:' '.join(sent).index(' '.join(ngram))] + word_pos = char_pos.count(' ') + + Another way to conceive this is: + + return next(i for i, ng in enumerate(ngrams(sentence, len(ngram))) + if ng == ngram) + + :param ngram: The ngram that needs to be searched + :type ngram: tuple + :param sentence: The list of tokens to search from. + :type sentence: list(str) + """ + # Iterates through the ngrams in sentence. + for i, sublist in enumerate(ngrams(sentence, len(ngram))): + # Returns the index of the word when ngram matches. + if ngram == sublist: + return i + + +def word_rank_alignment(reference, hypothesis, character_based=False): + """ + This is the word rank alignment algorithm described in the paper to produce + the *worder* list, i.e. a list of word indices of the hypothesis word orders + w.r.t. the list of reference words. + + Below is (H0, R0) example from the Isozaki et al. 2010 paper, + note the examples are indexed from 1 but the results here are indexed from 0: + + >>> ref = str('he was interested in world history because he ' + ... 'read the book').split() + >>> hyp = str('he read the book because he was interested in world ' + ... 'history').split() + >>> word_rank_alignment(ref, hyp) + [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5] + + The (H1, R1) example from the paper, note the 0th index: + + >>> ref = 'John hit Bob yesterday'.split() + >>> hyp = 'Bob hit John yesterday'.split() + >>> word_rank_alignment(ref, hyp) + [2, 1, 0, 3] + + Here is the (H2, R2) example from the paper, note the 0th index here too: + + >>> ref = 'the boy read the book'.split() + >>> hyp = 'the book was read by the boy'.split() + >>> word_rank_alignment(ref, hyp) + [3, 4, 2, 0, 1] + + :param reference: a reference sentence + :type reference: list(str) + :param hypothesis: a hypothesis sentence + :type hypothesis: list(str) + """ + worder = [] + hyp_len = len(hypothesis) + # Stores a list of possible ngrams from the reference sentence. + # This is used for matching context window later in the algorithm. + ref_ngrams = [] + hyp_ngrams = [] + for n in range(1, len(reference) + 1): + for ng in ngrams(reference, n): + ref_ngrams.append(ng) + for ng in ngrams(hypothesis, n): + hyp_ngrams.append(ng) + for i, h_word in enumerate(hypothesis): + # If word is not in the reference, continue. + if h_word not in reference: + continue + # If we can determine one-to-one word correspondence for unigrams that + # only appear once in both the reference and hypothesis. + elif hypothesis.count(h_word) == reference.count(h_word) == 1: + worder.append(reference.index(h_word)) + else: + max_window_size = max(i, hyp_len - i + 1) + for window in range(1, max_window_size): + if i + window < hyp_len: # If searching the right context is possible. + # Retrieve the right context window. + right_context_ngram = tuple(islice(hypothesis, i, i + window + 1)) + num_times_in_ref = ref_ngrams.count(right_context_ngram) + num_times_in_hyp = hyp_ngrams.count(right_context_ngram) + # If ngram appears only once in both ref and hyp. + if num_times_in_ref == num_times_in_hyp == 1: + # Find the position of ngram that matched the reference. + pos = position_of_ngram(right_context_ngram, reference) + worder.append(pos) # Add the positions of the ngram. + break + if window <= i: # If searching the left context is possible. + # Retrieve the left context window. + left_context_ngram = tuple(islice(hypothesis, i - window, i + 1)) + num_times_in_ref = ref_ngrams.count(left_context_ngram) + num_times_in_hyp = hyp_ngrams.count(left_context_ngram) + if num_times_in_ref == num_times_in_hyp == 1: + # Find the position of ngram that matched the reference. + pos = position_of_ngram(left_context_ngram, reference) + # Add the positions of the ngram. + worder.append(pos + len(left_context_ngram) - 1) + break + return worder + + +def find_increasing_sequences(worder): + """ + Given the *worder* list, this function groups monotonic +1 sequences. + + >>> worder = [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5] + >>> list(find_increasing_sequences(worder)) + [(7, 8, 9, 10), (0, 1, 2, 3, 4, 5)] + + :param worder: The worder list output from word_rank_alignment + :param type: list(int) + """ + items = iter(worder) + a, b = None, next(items, None) + result = [b] + while b is not None: + a, b = b, next(items, None) + if b is not None and a + 1 == b: + result.append(b) + else: + if len(result) > 1: + yield tuple(result) + result = [b] + + +def kendall_tau(worder, normalize=True): + """ + Calculates the Kendall's Tau correlation coefficient given the *worder* + list of word alignments from word_rank_alignment(), using the formula: + + tau = 2 * num_increasing_pairs / num_possible_pairs -1 + + Note that the no. of increasing pairs can be discontinuous in the *worder* + list and each each increasing sequence can be tabulated as choose(len(seq), 2) + no. of increasing pairs, e.g. + + >>> worder = [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5] + >>> number_possible_pairs = choose(len(worder), 2) + >>> round(kendall_tau(worder, normalize=False),3) + -0.236 + >>> round(kendall_tau(worder),3) + 0.382 + + :param worder: The worder list output from word_rank_alignment + :type worder: list(int) + :param normalize: Flag to indicate normalization to between 0.0 and 1.0. + :type normalize: boolean + :return: The Kendall's Tau correlation coefficient. + :rtype: float + """ + worder_len = len(worder) + # With worder_len < 2, `choose(worder_len, 2)` will be 0. + # As we divide by this, it will give a ZeroDivisionError. + # To avoid this, we can just return the lowest possible score. + if worder_len < 2: + tau = -1 + else: + # Extract the groups of increasing/monotonic sequences. + increasing_sequences = find_increasing_sequences(worder) + # Calculate no. of increasing_pairs in *worder* list. + num_increasing_pairs = sum(choose(len(seq), 2) for seq in increasing_sequences) + # Calculate no. of possible pairs. + num_possible_pairs = choose(worder_len, 2) + # Kendall's Tau computation. + tau = 2 * num_increasing_pairs / num_possible_pairs - 1 + if normalize: # If normalized, the tau output falls between 0.0 to 1.0 + return (tau + 1) / 2 + else: # Otherwise, the tau outputs falls between -1.0 to +1.0 + return tau + + +def spearman_rho(worder, normalize=True): + """ + Calculates the Spearman's Rho correlation coefficient given the *worder* + list of word alignment from word_rank_alignment(), using the formula: + + rho = 1 - sum(d**2) / choose(len(worder)+1, 3) + + Given that d is the sum of difference between the *worder* list of indices + and the original word indices from the reference sentence. + + Using the (H0,R0) and (H5, R5) example from the paper + + >>> worder = [7, 8, 9, 10, 6, 0, 1, 2, 3, 4, 5] + >>> round(spearman_rho(worder, normalize=False), 3) + -0.591 + >>> round(spearman_rho(worder), 3) + 0.205 + + :param worder: The worder list output from word_rank_alignment + :param type: list(int) + """ + worder_len = len(worder) + sum_d_square = sum((wi - i) ** 2 for wi, i in zip(worder, range(worder_len))) + rho = 1 - sum_d_square / choose(worder_len + 1, 3) + + if normalize: # If normalized, the rho output falls between 0.0 to 1.0 + return (rho + 1) / 2 + else: # Otherwise, the rho outputs falls between -1.0 to +1.0 + return rho diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/translate/stack_decoder.py b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/stack_decoder.py new file mode 100644 index 0000000..70bc82a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/translate/stack_decoder.py @@ -0,0 +1,515 @@ +# Natural Language Toolkit: Stack decoder +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Tah Wei Hoon +# URL: +# For license information, see LICENSE.TXT + +""" +A decoder that uses stacks to implement phrase-based translation. + +In phrase-based translation, the source sentence is segmented into +phrases of one or more words, and translations for those phrases are +used to build the target sentence. + +Hypothesis data structures are used to keep track of the source words +translated so far and the partial output. A hypothesis can be expanded +by selecting an untranslated phrase, looking up its translation in a +phrase table, and appending that translation to the partial output. +Translation is complete when a hypothesis covers all source words. + +The search space is huge because the source sentence can be segmented +in different ways, the source phrases can be selected in any order, +and there could be multiple translations for the same source phrase in +the phrase table. To make decoding tractable, stacks are used to limit +the number of candidate hypotheses by doing histogram and/or threshold +pruning. + +Hypotheses with the same number of words translated are placed in the +same stack. In histogram pruning, each stack has a size limit, and +the hypothesis with the lowest score is removed when the stack is full. +In threshold pruning, hypotheses that score below a certain threshold +of the best hypothesis in that stack are removed. + +Hypothesis scoring can include various factors such as phrase +translation probability, language model probability, length of +translation, cost of remaining words to be translated, and so on. + + +References: +Philipp Koehn. 2010. Statistical Machine Translation. +Cambridge University Press, New York. +""" + +import warnings +from collections import defaultdict +from math import log + + +class StackDecoder: + """ + Phrase-based stack decoder for machine translation + + >>> from nltk.translate import PhraseTable + >>> phrase_table = PhraseTable() + >>> phrase_table.add(('niemand',), ('nobody',), log(0.8)) + >>> phrase_table.add(('niemand',), ('no', 'one'), log(0.2)) + >>> phrase_table.add(('erwartet',), ('expects',), log(0.8)) + >>> phrase_table.add(('erwartet',), ('expecting',), log(0.2)) + >>> phrase_table.add(('niemand', 'erwartet'), ('one', 'does', 'not', 'expect'), log(0.1)) + >>> phrase_table.add(('die', 'spanische', 'inquisition'), ('the', 'spanish', 'inquisition'), log(0.8)) + >>> phrase_table.add(('!',), ('!',), log(0.8)) + + >>> # nltk.model should be used here once it is implemented + >>> from collections import defaultdict + >>> language_prob = defaultdict(lambda: -999.0) + >>> language_prob[('nobody',)] = log(0.5) + >>> language_prob[('expects',)] = log(0.4) + >>> language_prob[('the', 'spanish', 'inquisition')] = log(0.2) + >>> language_prob[('!',)] = log(0.1) + >>> language_model = type('',(object,),{'probability_change': lambda self, context, phrase: language_prob[phrase], 'probability': lambda self, phrase: language_prob[phrase]})() + + >>> stack_decoder = StackDecoder(phrase_table, language_model) + + >>> stack_decoder.translate(['niemand', 'erwartet', 'die', 'spanische', 'inquisition', '!']) + ['nobody', 'expects', 'the', 'spanish', 'inquisition', '!'] + + """ + + def __init__(self, phrase_table, language_model): + """ + :param phrase_table: Table of translations for source language + phrases and the log probabilities for those translations. + :type phrase_table: PhraseTable + + :param language_model: Target language model. Must define a + ``probability_change`` method that calculates the change in + log probability of a sentence, if a given string is appended + to it. + This interface is experimental and will likely be replaced + with nltk.model once it is implemented. + :type language_model: object + """ + self.phrase_table = phrase_table + self.language_model = language_model + + self.word_penalty = 0.0 + """ + float: Influences the translation length exponentially. + If positive, shorter translations are preferred. + If negative, longer translations are preferred. + If zero, no penalty is applied. + """ + + self.beam_threshold = 0.0 + """ + float: Hypotheses that score below this factor of the best + hypothesis in a stack are dropped from consideration. + Value between 0.0 and 1.0. + """ + + self.stack_size = 100 + """ + int: Maximum number of hypotheses to consider in a stack. + Higher values increase the likelihood of a good translation, + but increases processing time. + """ + + self.__distortion_factor = 0.5 + self.__compute_log_distortion() + + @property + def distortion_factor(self): + """ + float: Amount of reordering of source phrases. + Lower values favour monotone translation, suitable when + word order is similar for both source and target languages. + Value between 0.0 and 1.0. Default 0.5. + """ + return self.__distortion_factor + + @distortion_factor.setter + def distortion_factor(self, d): + self.__distortion_factor = d + self.__compute_log_distortion() + + def __compute_log_distortion(self): + # cache log(distortion_factor) so we don't have to recompute it + # when scoring hypotheses + if self.__distortion_factor == 0.0: + self.__log_distortion_factor = log(1e-9) # 1e-9 is almost zero + else: + self.__log_distortion_factor = log(self.__distortion_factor) + + def translate(self, src_sentence): + """ + :param src_sentence: Sentence to be translated + :type src_sentence: list(str) + + :return: Translated sentence + :rtype: list(str) + """ + sentence = tuple(src_sentence) # prevent accidental modification + sentence_length = len(sentence) + stacks = [ + _Stack(self.stack_size, self.beam_threshold) + for _ in range(0, sentence_length + 1) + ] + empty_hypothesis = _Hypothesis() + stacks[0].push(empty_hypothesis) + + all_phrases = self.find_all_src_phrases(sentence) + future_score_table = self.compute_future_scores(sentence) + for stack in stacks: + for hypothesis in stack: + possible_expansions = StackDecoder.valid_phrases( + all_phrases, hypothesis + ) + for src_phrase_span in possible_expansions: + src_phrase = sentence[src_phrase_span[0] : src_phrase_span[1]] + for translation_option in self.phrase_table.translations_for( + src_phrase + ): + raw_score = self.expansion_score( + hypothesis, translation_option, src_phrase_span + ) + new_hypothesis = _Hypothesis( + raw_score=raw_score, + src_phrase_span=src_phrase_span, + trg_phrase=translation_option.trg_phrase, + previous=hypothesis, + ) + new_hypothesis.future_score = self.future_score( + new_hypothesis, future_score_table, sentence_length + ) + total_words = new_hypothesis.total_translated_words() + stacks[total_words].push(new_hypothesis) + + if not stacks[sentence_length]: + warnings.warn( + "Unable to translate all words. " + "The source sentence contains words not in " + "the phrase table" + ) + # Instead of returning empty output, perhaps a partial + # translation could be returned + return [] + + best_hypothesis = stacks[sentence_length].best() + return best_hypothesis.translation_so_far() + + def find_all_src_phrases(self, src_sentence): + """ + Finds all subsequences in src_sentence that have a phrase + translation in the translation table + + :type src_sentence: tuple(str) + + :return: Subsequences that have a phrase translation, + represented as a table of lists of end positions. + For example, if result[2] is [5, 6, 9], then there are + three phrases starting from position 2 in ``src_sentence``, + ending at positions 5, 6, and 9 exclusive. The list of + ending positions are in ascending order. + :rtype: list(list(int)) + """ + sentence_length = len(src_sentence) + phrase_indices = [[] for _ in src_sentence] + for start in range(0, sentence_length): + for end in range(start + 1, sentence_length + 1): + potential_phrase = src_sentence[start:end] + if potential_phrase in self.phrase_table: + phrase_indices[start].append(end) + return phrase_indices + + def compute_future_scores(self, src_sentence): + """ + Determines the approximate scores for translating every + subsequence in ``src_sentence`` + + Future scores can be used a look-ahead to determine the + difficulty of translating the remaining parts of a src_sentence. + + :type src_sentence: tuple(str) + + :return: Scores of subsequences referenced by their start and + end positions. For example, result[2][5] is the score of the + subsequence covering positions 2, 3, and 4. + :rtype: dict(int: (dict(int): float)) + """ + scores = defaultdict(lambda: defaultdict(lambda: float("-inf"))) + for seq_length in range(1, len(src_sentence) + 1): + for start in range(0, len(src_sentence) - seq_length + 1): + end = start + seq_length + phrase = src_sentence[start:end] + if phrase in self.phrase_table: + score = self.phrase_table.translations_for(phrase)[ + 0 + ].log_prob # pick best (first) translation + # Warning: API of language_model is subject to change + score += self.language_model.probability(phrase) + scores[start][end] = score + + # check if a better score can be obtained by combining + # two child subsequences + for mid in range(start + 1, end): + combined_score = scores[start][mid] + scores[mid][end] + if combined_score > scores[start][end]: + scores[start][end] = combined_score + return scores + + def future_score(self, hypothesis, future_score_table, sentence_length): + """ + Determines the approximate score for translating the + untranslated words in ``hypothesis`` + """ + score = 0.0 + for span in hypothesis.untranslated_spans(sentence_length): + score += future_score_table[span[0]][span[1]] + return score + + def expansion_score(self, hypothesis, translation_option, src_phrase_span): + """ + Calculate the score of expanding ``hypothesis`` with + ``translation_option`` + + :param hypothesis: Hypothesis being expanded + :type hypothesis: _Hypothesis + + :param translation_option: Information about the proposed expansion + :type translation_option: PhraseTableEntry + + :param src_phrase_span: Word position span of the source phrase + :type src_phrase_span: tuple(int, int) + """ + score = hypothesis.raw_score + score += translation_option.log_prob + # The API of language_model is subject to change; it could accept + # a string, a list of words, and/or some other type + score += self.language_model.probability_change( + hypothesis, translation_option.trg_phrase + ) + score += self.distortion_score(hypothesis, src_phrase_span) + score -= self.word_penalty * len(translation_option.trg_phrase) + return score + + def distortion_score(self, hypothesis, next_src_phrase_span): + if not hypothesis.src_phrase_span: + return 0.0 + next_src_phrase_start = next_src_phrase_span[0] + prev_src_phrase_end = hypothesis.src_phrase_span[1] + distortion_distance = next_src_phrase_start - prev_src_phrase_end + return abs(distortion_distance) * self.__log_distortion_factor + + @staticmethod + def valid_phrases(all_phrases_from, hypothesis): + """ + Extract phrases from ``all_phrases_from`` that contains words + that have not been translated by ``hypothesis`` + + :param all_phrases_from: Phrases represented by their spans, in + the same format as the return value of + ``find_all_src_phrases`` + :type all_phrases_from: list(list(int)) + + :type hypothesis: _Hypothesis + + :return: A list of phrases, represented by their spans, that + cover untranslated positions. + :rtype: list(tuple(int, int)) + """ + untranslated_spans = hypothesis.untranslated_spans(len(all_phrases_from)) + valid_phrases = [] + for available_span in untranslated_spans: + start = available_span[0] + available_end = available_span[1] + while start < available_end: + for phrase_end in all_phrases_from[start]: + if phrase_end > available_end: + # Subsequent elements in all_phrases_from[start] + # will also be > available_end, since the + # elements are in ascending order + break + valid_phrases.append((start, phrase_end)) + start += 1 + return valid_phrases + + +class _Hypothesis: + """ + Partial solution to a translation. + + Records the word positions of the phrase being translated, its + translation, raw score, and the cost of the untranslated parts of + the sentence. When the next phrase is selected to build upon the + partial solution, a new _Hypothesis object is created, with a back + pointer to the previous hypothesis. + + To find out which words have been translated so far, look at the + ``src_phrase_span`` in the hypothesis chain. Similarly, the + translation output can be found by traversing up the chain. + """ + + def __init__( + self, + raw_score=0.0, + src_phrase_span=(), + trg_phrase=(), + previous=None, + future_score=0.0, + ): + """ + :param raw_score: Likelihood of hypothesis so far. + Higher is better. Does not account for untranslated words. + :type raw_score: float + + :param src_phrase_span: Span of word positions covered by the + source phrase in this hypothesis expansion. For example, + (2, 5) means that the phrase is from the second word up to, + but not including the fifth word in the source sentence. + :type src_phrase_span: tuple(int) + + :param trg_phrase: Translation of the source phrase in this + hypothesis expansion + :type trg_phrase: tuple(str) + + :param previous: Previous hypothesis before expansion to this one + :type previous: _Hypothesis + + :param future_score: Approximate score for translating the + remaining words not covered by this hypothesis. Higher means + that the remaining words are easier to translate. + :type future_score: float + """ + self.raw_score = raw_score + self.src_phrase_span = src_phrase_span + self.trg_phrase = trg_phrase + self.previous = previous + self.future_score = future_score + + def score(self): + """ + Overall score of hypothesis after accounting for local and + global features + """ + return self.raw_score + self.future_score + + def untranslated_spans(self, sentence_length): + """ + Starting from each untranslated word, find the longest + continuous span of untranslated positions + + :param sentence_length: Length of source sentence being + translated by the hypothesis + :type sentence_length: int + + :rtype: list(tuple(int, int)) + """ + translated_positions = self.translated_positions() + translated_positions.sort() + translated_positions.append(sentence_length) # add sentinel position + + untranslated_spans = [] + start = 0 + # each untranslated span must end in one of the translated_positions + for end in translated_positions: + if start < end: + untranslated_spans.append((start, end)) + start = end + 1 + + return untranslated_spans + + def translated_positions(self): + """ + List of positions in the source sentence of words already + translated. The list is not sorted. + + :rtype: list(int) + """ + translated_positions = [] + current_hypothesis = self + while current_hypothesis.previous is not None: + translated_span = current_hypothesis.src_phrase_span + translated_positions.extend(range(translated_span[0], translated_span[1])) + current_hypothesis = current_hypothesis.previous + return translated_positions + + def total_translated_words(self): + return len(self.translated_positions()) + + def translation_so_far(self): + translation = [] + self.__build_translation(self, translation) + return translation + + def __build_translation(self, hypothesis, output): + if hypothesis.previous is None: + return + self.__build_translation(hypothesis.previous, output) + output.extend(hypothesis.trg_phrase) + + +class _Stack: + """ + Collection of _Hypothesis objects + """ + + def __init__(self, max_size=100, beam_threshold=0.0): + """ + :param beam_threshold: Hypotheses that score less than this + factor of the best hypothesis are discarded from the stack. + Value must be between 0.0 and 1.0. + :type beam_threshold: float + """ + self.max_size = max_size + self.items = [] + + if beam_threshold == 0.0: + self.__log_beam_threshold = float("-inf") + else: + self.__log_beam_threshold = log(beam_threshold) + + def push(self, hypothesis): + """ + Add ``hypothesis`` to the stack. + Removes lowest scoring hypothesis if the stack is full. + After insertion, hypotheses that score less than + ``beam_threshold`` times the score of the best hypothesis + are removed. + """ + self.items.append(hypothesis) + self.items.sort(key=lambda h: h.score(), reverse=True) + while len(self.items) > self.max_size: + self.items.pop() + self.threshold_prune() + + def threshold_prune(self): + if not self.items: + return + # log(score * beam_threshold) = log(score) + log(beam_threshold) + threshold = self.items[0].score() + self.__log_beam_threshold + for hypothesis in reversed(self.items): + if hypothesis.score() < threshold: + self.items.pop() + else: + break + + def best(self): + """ + :return: Hypothesis with the highest score in the stack + :rtype: _Hypothesis + """ + if self.items: + return self.items[0] + return None + + def __iter__(self): + return iter(self.items) + + def __contains__(self, hypothesis): + return hypothesis in self.items + + def __bool__(self): + return len(self.items) != 0 + + __nonzero__ = __bool__ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__init__.py new file mode 100644 index 0000000..1c0fd3a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__init__.py @@ -0,0 +1,52 @@ +# Natural Language Toolkit: Machine Translation +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK Tree Package + +This package may be used for representing hierarchical language +structures, such as syntax trees and morphological trees. +""" + +# TODO: add LabelledTree (can be used for dependency trees) + +from nltk.tree.immutable import ( + ImmutableMultiParentedTree, + ImmutableParentedTree, + ImmutableProbabilisticTree, + ImmutableTree, +) +from nltk.tree.parented import MultiParentedTree, ParentedTree +from nltk.tree.parsing import bracket_parse, sinica_parse +from nltk.tree.prettyprinter import TreePrettyPrinter +from nltk.tree.probabilistic import ProbabilisticTree +from nltk.tree.transforms import ( + chomsky_normal_form, + collapse_unary, + un_chomsky_normal_form, +) +from nltk.tree.tree import Tree + +__all__ = [ + "ImmutableMultiParentedTree", + "ImmutableParentedTree", + "ImmutableProbabilisticTree", + "ImmutableTree", + "MultiParentedTree", + "ParentedTree", + "bracket_parse", + "sinica_parse", + "TreePrettyPrinter", + "ProbabilisticTree", + "chomsky_normal_form", + "collapse_unary", + "un_chomsky_normal_form", + "Tree", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..79b8bf0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/immutable.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/immutable.cpython-310.pyc new file mode 100644 index 0000000..e2e8a58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/immutable.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parented.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parented.cpython-310.pyc new file mode 100644 index 0000000..96e7a26 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parented.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parsing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parsing.cpython-310.pyc new file mode 100644 index 0000000..46370a5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/parsing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/prettyprinter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/prettyprinter.cpython-310.pyc new file mode 100644 index 0000000..46025c2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/prettyprinter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/probabilistic.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/probabilistic.cpython-310.pyc new file mode 100644 index 0000000..854ddbf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/probabilistic.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/transforms.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/transforms.cpython-310.pyc new file mode 100644 index 0000000..7e70377 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/transforms.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/tree.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/tree.cpython-310.pyc new file mode 100644 index 0000000..c1fe709 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/__pycache__/tree.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/immutable.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/immutable.py new file mode 100644 index 0000000..e0757e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/immutable.py @@ -0,0 +1,124 @@ +# Natural Language Toolkit: Text Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +from nltk.probability import ProbabilisticMixIn +from nltk.tree.parented import MultiParentedTree, ParentedTree +from nltk.tree.tree import Tree + + +class ImmutableTree(Tree): + def __init__(self, node, children=None): + super().__init__(node, children) + # Precompute our hash value. This ensures that we're really + # immutable. It also means we only have to calculate it once. + try: + self._hash = hash((self._label, tuple(self))) + except (TypeError, ValueError) as e: + raise ValueError( + "%s: node value and children " "must be immutable" % type(self).__name__ + ) from e + + def __setitem__(self, index, value): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __setslice__(self, i, j, value): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __delitem__(self, index): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __delslice__(self, i, j): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __iadd__(self, other): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __imul__(self, other): + raise ValueError("%s may not be modified" % type(self).__name__) + + def append(self, v): + raise ValueError("%s may not be modified" % type(self).__name__) + + def extend(self, v): + raise ValueError("%s may not be modified" % type(self).__name__) + + def pop(self, v=None): + raise ValueError("%s may not be modified" % type(self).__name__) + + def remove(self, v): + raise ValueError("%s may not be modified" % type(self).__name__) + + def reverse(self): + raise ValueError("%s may not be modified" % type(self).__name__) + + def sort(self): + raise ValueError("%s may not be modified" % type(self).__name__) + + def __hash__(self): + return self._hash + + def set_label(self, value): + """ + Set the node label. This will only succeed the first time the + node label is set, which should occur in ImmutableTree.__init__(). + """ + if hasattr(self, "_label"): + raise ValueError("%s may not be modified" % type(self).__name__) + self._label = value + + +class ImmutableProbabilisticTree(ImmutableTree, ProbabilisticMixIn): + def __init__(self, node, children=None, **prob_kwargs): + ImmutableTree.__init__(self, node, children) + ProbabilisticMixIn.__init__(self, **prob_kwargs) + self._hash = hash((self._label, tuple(self), self.prob())) + + # We have to patch up these methods to make them work right: + def _frozen_class(self): + return ImmutableProbabilisticTree + + def __repr__(self): + return f"{Tree.__repr__(self)} [{self.prob()}]" + + def __str__(self): + return f"{self.pformat(margin=60)} [{self.prob()}]" + + def copy(self, deep=False): + if not deep: + return type(self)(self._label, self, prob=self.prob()) + else: + return type(self).convert(self) + + @classmethod + def convert(cls, val): + if isinstance(val, Tree): + children = [cls.convert(child) for child in val] + if isinstance(val, ProbabilisticMixIn): + return cls(val._label, children, prob=val.prob()) + else: + return cls(val._label, children, prob=1.0) + else: + return val + + +class ImmutableParentedTree(ImmutableTree, ParentedTree): + pass + + +class ImmutableMultiParentedTree(ImmutableTree, MultiParentedTree): + pass + + +__all__ = [ + "ImmutableProbabilisticTree", + "ImmutableTree", + "ImmutableParentedTree", + "ImmutableMultiParentedTree", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parented.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parented.py new file mode 100644 index 0000000..a6a6f39 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parented.py @@ -0,0 +1,590 @@ +# Natural Language Toolkit: Text Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +import warnings +from abc import ABCMeta, abstractmethod + +from nltk.tree.tree import Tree +from nltk.util import slice_bounds + + +###################################################################### +## Parented trees +###################################################################### +class AbstractParentedTree(Tree, metaclass=ABCMeta): + """ + An abstract base class for a ``Tree`` that automatically maintains + pointers to parent nodes. These parent pointers are updated + whenever any change is made to a tree's structure. Two subclasses + are currently defined: + + - ``ParentedTree`` is used for tree structures where each subtree + has at most one parent. This class should be used in cases + where there is no"sharing" of subtrees. + + - ``MultiParentedTree`` is used for tree structures where a + subtree may have zero or more parents. This class should be + used in cases where subtrees may be shared. + + Subclassing + =========== + The ``AbstractParentedTree`` class redefines all operations that + modify a tree's structure to call two methods, which are used by + subclasses to update parent information: + + - ``_setparent()`` is called whenever a new child is added. + - ``_delparent()`` is called whenever a child is removed. + """ + + def __init__(self, node, children=None): + super().__init__(node, children) + # If children is None, the tree is read from node, and + # all parents will be set during parsing. + if children is not None: + # Otherwise we have to set the parent of the children. + # Iterate over self, and *not* children, because children + # might be an iterator. + for i, child in enumerate(self): + if isinstance(child, Tree): + self._setparent(child, i, dry_run=True) + for i, child in enumerate(self): + if isinstance(child, Tree): + self._setparent(child, i) + + # //////////////////////////////////////////////////////////// + # Parent management + # //////////////////////////////////////////////////////////// + @abstractmethod + def _setparent(self, child, index, dry_run=False): + """ + Update the parent pointer of ``child`` to point to ``self``. This + method is only called if the type of ``child`` is ``Tree``; + i.e., it is not called when adding a leaf to a tree. This method + is always called before the child is actually added to the + child list of ``self``. + + :type child: Tree + :type index: int + :param index: The index of ``child`` in ``self``. + :raise TypeError: If ``child`` is a tree with an impropriate + type. Typically, if ``child`` is a tree, then its type needs + to match the type of ``self``. This prevents mixing of + different tree types (single-parented, multi-parented, and + non-parented). + :param dry_run: If true, the don't actually set the child's + parent pointer; just check for any error conditions, and + raise an exception if one is found. + """ + + @abstractmethod + def _delparent(self, child, index): + """ + Update the parent pointer of ``child`` to not point to self. This + method is only called if the type of ``child`` is ``Tree``; i.e., it + is not called when removing a leaf from a tree. This method + is always called before the child is actually removed from the + child list of ``self``. + + :type child: Tree + :type index: int + :param index: The index of ``child`` in ``self``. + """ + + # //////////////////////////////////////////////////////////// + # Methods that add/remove children + # //////////////////////////////////////////////////////////// + # Every method that adds or removes a child must make + # appropriate calls to _setparent() and _delparent(). + + def __delitem__(self, index): + # del ptree[start:stop] + if isinstance(index, slice): + start, stop, step = slice_bounds(self, index, allow_step=True) + # Clear all the children pointers. + for i in range(start, stop, step): + if isinstance(self[i], Tree): + self._delparent(self[i], i) + # Delete the children from our child list. + super().__delitem__(index) + + # del ptree[i] + elif isinstance(index, int): + if index < 0: + index += len(self) + if index < 0: + raise IndexError("index out of range") + # Clear the child's parent pointer. + if isinstance(self[index], Tree): + self._delparent(self[index], index) + # Remove the child from our child list. + super().__delitem__(index) + + elif isinstance(index, (list, tuple)): + # del ptree[()] + if len(index) == 0: + raise IndexError("The tree position () may not be deleted.") + # del ptree[(i,)] + elif len(index) == 1: + del self[index[0]] + # del ptree[i1, i2, i3] + else: + del self[index[0]][index[1:]] + + else: + raise TypeError( + "%s indices must be integers, not %s" + % (type(self).__name__, type(index).__name__) + ) + + def __setitem__(self, index, value): + # ptree[start:stop] = value + if isinstance(index, slice): + start, stop, step = slice_bounds(self, index, allow_step=True) + # make a copy of value, in case it's an iterator + if not isinstance(value, (list, tuple)): + value = list(value) + # Check for any error conditions, so we can avoid ending + # up in an inconsistent state if an error does occur. + for i, child in enumerate(value): + if isinstance(child, Tree): + self._setparent(child, start + i * step, dry_run=True) + # clear the child pointers of all parents we're removing + for i in range(start, stop, step): + if isinstance(self[i], Tree): + self._delparent(self[i], i) + # set the child pointers of the new children. We do this + # after clearing *all* child pointers, in case we're e.g. + # reversing the elements in a tree. + for i, child in enumerate(value): + if isinstance(child, Tree): + self._setparent(child, start + i * step) + # finally, update the content of the child list itself. + super().__setitem__(index, value) + + # ptree[i] = value + elif isinstance(index, int): + if index < 0: + index += len(self) + if index < 0: + raise IndexError("index out of range") + # if the value is not changing, do nothing. + if value is self[index]: + return + # Set the new child's parent pointer. + if isinstance(value, Tree): + self._setparent(value, index) + # Remove the old child's parent pointer + if isinstance(self[index], Tree): + self._delparent(self[index], index) + # Update our child list. + super().__setitem__(index, value) + + elif isinstance(index, (list, tuple)): + # ptree[()] = value + if len(index) == 0: + raise IndexError("The tree position () may not be assigned to.") + # ptree[(i,)] = value + elif len(index) == 1: + self[index[0]] = value + # ptree[i1, i2, i3] = value + else: + self[index[0]][index[1:]] = value + + else: + raise TypeError( + "%s indices must be integers, not %s" + % (type(self).__name__, type(index).__name__) + ) + + def append(self, child): + if isinstance(child, Tree): + self._setparent(child, len(self)) + super().append(child) + + def extend(self, children): + for child in children: + if isinstance(child, Tree): + self._setparent(child, len(self)) + super().append(child) + + def insert(self, index, child): + # Handle negative indexes. Note that if index < -len(self), + # we do *not* raise an IndexError, unlike __getitem__. This + # is done for consistency with list.__getitem__ and list.index. + if index < 0: + index += len(self) + if index < 0: + index = 0 + # Set the child's parent, and update our child list. + if isinstance(child, Tree): + self._setparent(child, index) + super().insert(index, child) + + def pop(self, index=-1): + if index < 0: + index += len(self) + if index < 0: + raise IndexError("index out of range") + if isinstance(self[index], Tree): + self._delparent(self[index], index) + return super().pop(index) + + # n.b.: like `list`, this is done by equality, not identity! + # To remove a specific child, use del ptree[i]. + def remove(self, child): + index = self.index(child) + if isinstance(self[index], Tree): + self._delparent(self[index], index) + super().remove(child) + + # We need to implement __getslice__ and friends, even though + # they're deprecated, because otherwise list.__getslice__ will get + # called (since we're subclassing from list). Just delegate to + # __getitem__ etc., but use max(0, start) and max(0, stop) because + # because negative indices are already handled *before* + # __getslice__ is called; and we don't want to double-count them. + if hasattr(list, "__getslice__"): + + def __getslice__(self, start, stop): + return self.__getitem__(slice(max(0, start), max(0, stop))) + + def __delslice__(self, start, stop): + return self.__delitem__(slice(max(0, start), max(0, stop))) + + def __setslice__(self, start, stop, value): + return self.__setitem__(slice(max(0, start), max(0, stop)), value) + + def __getnewargs__(self): + """Method used by the pickle module when un-pickling. + This method provides the arguments passed to ``__new__`` + upon un-pickling. Without this method, ParentedTree instances + cannot be pickled and unpickled in Python 3.7+ onwards. + + :return: Tuple of arguments for ``__new__``, i.e. the label + and the children of this node. + :rtype: Tuple[Any, List[AbstractParentedTree]] + """ + return (self._label, list(self)) + + +class ParentedTree(AbstractParentedTree): + """ + A ``Tree`` that automatically maintains parent pointers for + single-parented trees. The following are methods for querying + the structure of a parented tree: ``parent``, ``parent_index``, + ``left_sibling``, ``right_sibling``, ``root``, ``treeposition``. + + Each ``ParentedTree`` may have at most one parent. In + particular, subtrees may not be shared. Any attempt to reuse a + single ``ParentedTree`` as a child of more than one parent (or + as multiple children of the same parent) will cause a + ``ValueError`` exception to be raised. + + ``ParentedTrees`` should never be used in the same tree as ``Trees`` + or ``MultiParentedTrees``. Mixing tree implementations may result + in incorrect parent pointers and in ``TypeError`` exceptions. + """ + + def __init__(self, node, children=None): + self._parent = None + """The parent of this Tree, or None if it has no parent.""" + super().__init__(node, children) + if children is None: + # If children is None, the tree is read from node. + # After parsing, the parent of the immediate children + # will point to an intermediate tree, not self. + # We fix this by brute force: + for i, child in enumerate(self): + if isinstance(child, Tree): + child._parent = None + self._setparent(child, i) + + def _frozen_class(self): + from nltk.tree.immutable import ImmutableParentedTree + + return ImmutableParentedTree + + def copy(self, deep=False): + if not deep: + warnings.warn( + f"{self.__class__.__name__} objects do not support shallow copies. Defaulting to a deep copy." + ) + return super().copy(deep=True) + + # ///////////////////////////////////////////////////////////////// + # Methods + # ///////////////////////////////////////////////////////////////// + + def parent(self): + """The parent of this tree, or None if it has no parent.""" + return self._parent + + def parent_index(self): + """ + The index of this tree in its parent. I.e., + ``ptree.parent()[ptree.parent_index()] is ptree``. Note that + ``ptree.parent_index()`` is not necessarily equal to + ``ptree.parent.index(ptree)``, since the ``index()`` method + returns the first child that is equal to its argument. + """ + if self._parent is None: + return None + for i, child in enumerate(self._parent): + if child is self: + return i + assert False, "expected to find self in self._parent!" + + def left_sibling(self): + """The left sibling of this tree, or None if it has none.""" + parent_index = self.parent_index() + if self._parent and parent_index > 0: + return self._parent[parent_index - 1] + return None # no left sibling + + def right_sibling(self): + """The right sibling of this tree, or None if it has none.""" + parent_index = self.parent_index() + if self._parent and parent_index < (len(self._parent) - 1): + return self._parent[parent_index + 1] + return None # no right sibling + + def root(self): + """ + The root of this tree. I.e., the unique ancestor of this tree + whose parent is None. If ``ptree.parent()`` is None, then + ``ptree`` is its own root. + """ + root = self + while root.parent() is not None: + root = root.parent() + return root + + def treeposition(self): + """ + The tree position of this tree, relative to the root of the + tree. I.e., ``ptree.root[ptree.treeposition] is ptree``. + """ + if self.parent() is None: + return () + else: + return self.parent().treeposition() + (self.parent_index(),) + + # ///////////////////////////////////////////////////////////////// + # Parent Management + # ///////////////////////////////////////////////////////////////// + + def _delparent(self, child, index): + # Sanity checks + assert isinstance(child, ParentedTree) + assert self[index] is child + assert child._parent is self + + # Delete child's parent pointer. + child._parent = None + + def _setparent(self, child, index, dry_run=False): + # If the child's type is incorrect, then complain. + if not isinstance(child, ParentedTree): + raise TypeError("Can not insert a non-ParentedTree into a ParentedTree") + + # If child already has a parent, then complain. + if hasattr(child, "_parent") and child._parent is not None: + raise ValueError("Can not insert a subtree that already has a parent.") + + # Set child's parent pointer & index. + if not dry_run: + child._parent = self + + +class MultiParentedTree(AbstractParentedTree): + """ + A ``Tree`` that automatically maintains parent pointers for + multi-parented trees. The following are methods for querying the + structure of a multi-parented tree: ``parents()``, ``parent_indices()``, + ``left_siblings()``, ``right_siblings()``, ``roots``, ``treepositions``. + + Each ``MultiParentedTree`` may have zero or more parents. In + particular, subtrees may be shared. If a single + ``MultiParentedTree`` is used as multiple children of the same + parent, then that parent will appear multiple times in its + ``parents()`` method. + + ``MultiParentedTrees`` should never be used in the same tree as + ``Trees`` or ``ParentedTrees``. Mixing tree implementations may + result in incorrect parent pointers and in ``TypeError`` exceptions. + """ + + def __init__(self, node, children=None): + self._parents = [] + """A list of this tree's parents. This list should not + contain duplicates, even if a parent contains this tree + multiple times.""" + super().__init__(node, children) + if children is None: + # If children is None, the tree is read from node. + # After parsing, the parent(s) of the immediate children + # will point to an intermediate tree, not self. + # We fix this by brute force: + for i, child in enumerate(self): + if isinstance(child, Tree): + child._parents = [] + self._setparent(child, i) + + def _frozen_class(self): + from nltk.tree.immutable import ImmutableMultiParentedTree + + return ImmutableMultiParentedTree + + # ///////////////////////////////////////////////////////////////// + # Methods + # ///////////////////////////////////////////////////////////////// + + def parents(self): + """ + The set of parents of this tree. If this tree has no parents, + then ``parents`` is the empty set. To check if a tree is used + as multiple children of the same parent, use the + ``parent_indices()`` method. + + :type: list(MultiParentedTree) + """ + return list(self._parents) + + def left_siblings(self): + """ + A list of all left siblings of this tree, in any of its parent + trees. A tree may be its own left sibling if it is used as + multiple contiguous children of the same parent. A tree may + appear multiple times in this list if it is the left sibling + of this tree with respect to multiple parents. + + :type: list(MultiParentedTree) + """ + return [ + parent[index - 1] + for (parent, index) in self._get_parent_indices() + if index > 0 + ] + + def right_siblings(self): + """ + A list of all right siblings of this tree, in any of its parent + trees. A tree may be its own right sibling if it is used as + multiple contiguous children of the same parent. A tree may + appear multiple times in this list if it is the right sibling + of this tree with respect to multiple parents. + + :type: list(MultiParentedTree) + """ + return [ + parent[index + 1] + for (parent, index) in self._get_parent_indices() + if index < (len(parent) - 1) + ] + + def _get_parent_indices(self): + return [ + (parent, index) + for parent in self._parents + for index, child in enumerate(parent) + if child is self + ] + + def roots(self): + """ + The set of all roots of this tree. This set is formed by + tracing all possible parent paths until trees with no parents + are found. + + :type: list(MultiParentedTree) + """ + return list(self._get_roots_helper({}).values()) + + def _get_roots_helper(self, result): + if self._parents: + for parent in self._parents: + parent._get_roots_helper(result) + else: + result[id(self)] = self + return result + + def parent_indices(self, parent): + """ + Return a list of the indices where this tree occurs as a child + of ``parent``. If this child does not occur as a child of + ``parent``, then the empty list is returned. The following is + always true:: + + for parent_index in ptree.parent_indices(parent): + parent[parent_index] is ptree + """ + if parent not in self._parents: + return [] + else: + return [index for (index, child) in enumerate(parent) if child is self] + + def treepositions(self, root): + """ + Return a list of all tree positions that can be used to reach + this multi-parented tree starting from ``root``. I.e., the + following is always true:: + + for treepos in ptree.treepositions(root): + root[treepos] is ptree + """ + if self is root: + return [()] + else: + return [ + treepos + (index,) + for parent in self._parents + for treepos in parent.treepositions(root) + for (index, child) in enumerate(parent) + if child is self + ] + + # ///////////////////////////////////////////////////////////////// + # Parent Management + # ///////////////////////////////////////////////////////////////// + + def _delparent(self, child, index): + # Sanity checks + assert isinstance(child, MultiParentedTree) + assert self[index] is child + assert len([p for p in child._parents if p is self]) == 1 + + # If the only copy of child in self is at index, then delete + # self from child's parent list. + for i, c in enumerate(self): + if c is child and i != index: + break + else: + child._parents.remove(self) + + def _setparent(self, child, index, dry_run=False): + # If the child's type is incorrect, then complain. + if not isinstance(child, MultiParentedTree): + raise TypeError( + "Can not insert a non-MultiParentedTree into a MultiParentedTree" + ) + + # Add self as a parent pointer if it's not already listed. + if not dry_run: + for parent in child._parents: + if parent is self: + break + else: + child._parents.append(self) + + +__all__ = [ + "ParentedTree", + "MultiParentedTree", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parsing.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parsing.py new file mode 100644 index 0000000..82c43a1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/parsing.py @@ -0,0 +1,66 @@ +# Natural Language Toolkit: Text Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + +import re + +from nltk.tree.tree import Tree + +###################################################################### +## Parsing +###################################################################### + + +def bracket_parse(s): + """ + Use Tree.read(s, remove_empty_top_bracketing=True) instead. + """ + raise NameError("Use Tree.read(s, remove_empty_top_bracketing=True) instead.") + + +def sinica_parse(s): + """ + Parse a Sinica Treebank string and return a tree. Trees are represented as nested brackettings, + as shown in the following example (X represents a Chinese character): + S(goal:NP(Head:Nep:XX)|theme:NP(Head:Nhaa:X)|quantity:Dab:X|Head:VL2:X)#0(PERIODCATEGORY) + + :return: A tree corresponding to the string representation. + :rtype: Tree + :param s: The string to be converted + :type s: str + """ + tokens = re.split(r"([()| ])", s) + for i in range(len(tokens)): + if tokens[i] == "(": + tokens[i - 1], tokens[i] = ( + tokens[i], + tokens[i - 1], + ) # pull nonterminal inside parens + elif ":" in tokens[i]: + fields = tokens[i].split(":") + if len(fields) == 2: # non-terminal + tokens[i] = fields[1] + else: + tokens[i] = "(" + fields[-2] + " " + fields[-1] + ")" + elif tokens[i] == "|": + tokens[i] = "" + + treebank_string = " ".join(tokens) + return Tree.fromstring(treebank_string, remove_empty_top_bracketing=True) + + +# s = re.sub(r'^#[^\s]*\s', '', s) # remove leading identifier +# s = re.sub(r'\w+:', '', s) # remove role tags + +# return s + +__all__ = [ + "bracket_parse", + "sinica_parse", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/prettyprinter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/prettyprinter.py new file mode 100644 index 0000000..099e5c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/prettyprinter.py @@ -0,0 +1,627 @@ +# Natural Language Toolkit: ASCII visualization of NLTK trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Andreas van Cranenburgh +# Peter Ljunglöf +# URL: +# For license information, see LICENSE.TXT + +""" +Pretty-printing of discontinuous trees. +Adapted from the disco-dop project, by Andreas van Cranenburgh. +https://github.com/andreasvc/disco-dop + +Interesting reference (not used for this code): +T. Eschbach et al., Orth. Hypergraph Drawing, Journal of +Graph Algorithms and Applications, 10(2) 141--157 (2006)149. +https://jgaa.info/accepted/2006/EschbachGuentherBecker2006.10.2.pdf +""" + +import re + +try: + from html import escape +except ImportError: + from cgi import escape + +from collections import defaultdict +from operator import itemgetter + +from nltk.tree.tree import Tree +from nltk.util import OrderedDict + +ANSICOLOR = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, +} + + +class TreePrettyPrinter: + """ + Pretty-print a tree in text format, either as ASCII or Unicode. + The tree can be a normal tree, or discontinuous. + + ``TreePrettyPrinter(tree, sentence=None, highlight=())`` + creates an object from which different visualizations can be created. + + :param tree: a Tree object. + :param sentence: a list of words (strings). If `sentence` is given, + `tree` must contain integers as leaves, which are taken as indices + in `sentence`. Using this you can display a discontinuous tree. + :param highlight: Optionally, a sequence of Tree objects in `tree` which + should be highlighted. Has the effect of only applying colors to nodes + in this sequence (nodes should be given as Tree objects, terminals as + indices). + + >>> from nltk.tree import Tree + >>> tree = Tree.fromstring('(S (NP Mary) (VP walks))') + >>> print(TreePrettyPrinter(tree).text()) + ... # doctest: +NORMALIZE_WHITESPACE + S + ____|____ + NP VP + | | + Mary walks + """ + + def __init__(self, tree, sentence=None, highlight=()): + if sentence is None: + leaves = tree.leaves() + if ( + leaves + and all(len(a) > 0 for a in tree.subtrees()) + and all(isinstance(a, int) for a in leaves) + ): + sentence = [str(a) for a in leaves] + else: + # this deals with empty nodes (frontier non-terminals) + # and multiple/mixed terminals under non-terminals. + tree = tree.copy(True) + sentence = [] + for a in tree.subtrees(): + if len(a) == 0: + a.append(len(sentence)) + sentence.append(None) + elif any(not isinstance(b, Tree) for b in a): + for n, b in enumerate(a): + if not isinstance(b, Tree): + a[n] = len(sentence) + if type(b) == tuple: + b = "/".join(b) + sentence.append("%s" % b) + self.nodes, self.coords, self.edges, self.highlight = self.nodecoords( + tree, sentence, highlight + ) + + def __str__(self): + return self.text() + + def __repr__(self): + return "" % len(self.nodes) + + @staticmethod + def nodecoords(tree, sentence, highlight): + """ + Produce coordinates of nodes on a grid. + + Objective: + + - Produce coordinates for a non-overlapping placement of nodes and + horizontal lines. + - Order edges so that crossing edges cross a minimal number of previous + horizontal lines (never vertical lines). + + Approach: + + - bottom up level order traversal (start at terminals) + - at each level, identify nodes which cannot be on the same row + - identify nodes which cannot be in the same column + - place nodes into a grid at (row, column) + - order child-parent edges with crossing edges last + + Coordinates are (row, column); the origin (0, 0) is at the top left; + the root node is on row 0. Coordinates do not consider the size of a + node (which depends on font, &c), so the width of a column of the grid + should be automatically determined by the element with the greatest + width in that column. Alternatively, the integer coordinates could be + converted to coordinates in which the distances between adjacent nodes + are non-uniform. + + Produces tuple (nodes, coords, edges, highlighted) where: + + - nodes[id]: Tree object for the node with this integer id + - coords[id]: (n, m) coordinate where to draw node with id in the grid + - edges[id]: parent id of node with this id (ordered dictionary) + - highlighted: set of ids that should be highlighted + """ + + def findcell(m, matrix, startoflevel, children): + """ + Find vacant row, column index for node ``m``. + Iterate over current rows for this level (try lowest first) + and look for cell between first and last child of this node, + add new row to level if no free row available. + """ + candidates = [a for _, a in children[m]] + minidx, maxidx = min(candidates), max(candidates) + leaves = tree[m].leaves() + center = scale * sum(leaves) // len(leaves) # center of gravity + if minidx < maxidx and not minidx < center < maxidx: + center = sum(candidates) // len(candidates) + if max(candidates) - min(candidates) > 2 * scale: + center -= center % scale # round to unscaled coordinate + if minidx < maxidx and not minidx < center < maxidx: + center += scale + if ids[m] == 0: + startoflevel = len(matrix) + for rowidx in range(startoflevel, len(matrix) + 1): + if rowidx == len(matrix): # need to add a new row + matrix.append( + [ + vertline if a not in (corner, None) else None + for a in matrix[-1] + ] + ) + row = matrix[rowidx] + if len(children[m]) == 1: # place unaries directly above child + return rowidx, next(iter(children[m]))[1] + elif all( + a is None or a == vertline + for a in row[min(candidates) : max(candidates) + 1] + ): + # find free column + for n in range(scale): + i = j = center + n + while j > minidx or i < maxidx: + if i < maxidx and ( + matrix[rowidx][i] is None or i in candidates + ): + return rowidx, i + elif j > minidx and ( + matrix[rowidx][j] is None or j in candidates + ): + return rowidx, j + i += scale + j -= scale + raise ValueError( + "could not find a free cell for:\n%s\n%s" + "min=%d; max=%d" % (tree[m], minidx, maxidx, dumpmatrix()) + ) + + def dumpmatrix(): + """Dump matrix contents for debugging purposes.""" + return "\n".join( + "%2d: %s" % (n, " ".join(("%2r" % i)[:2] for i in row)) + for n, row in enumerate(matrix) + ) + + leaves = tree.leaves() + if not all(isinstance(n, int) for n in leaves): + raise ValueError("All leaves must be integer indices.") + if len(leaves) != len(set(leaves)): + raise ValueError("Indices must occur at most once.") + if not all(0 <= n < len(sentence) for n in leaves): + raise ValueError( + "All leaves must be in the interval 0..n " + "with n=len(sentence)\ntokens: %d indices: " + "%r\nsentence: %s" % (len(sentence), tree.leaves(), sentence) + ) + vertline, corner = -1, -2 # constants + tree = tree.copy(True) + for a in tree.subtrees(): + a.sort(key=lambda n: min(n.leaves()) if isinstance(n, Tree) else n) + scale = 2 + crossed = set() + # internal nodes and lexical nodes (no frontiers) + positions = tree.treepositions() + maxdepth = max(map(len, positions)) + 1 + childcols = defaultdict(set) + matrix = [[None] * (len(sentence) * scale)] + nodes = {} + ids = {a: n for n, a in enumerate(positions)} + highlighted_nodes = { + n for a, n in ids.items() if not highlight or tree[a] in highlight + } + levels = {n: [] for n in range(maxdepth - 1)} + terminals = [] + for a in positions: + node = tree[a] + if isinstance(node, Tree): + levels[maxdepth - node.height()].append(a) + else: + terminals.append(a) + + for n in levels: + levels[n].sort(key=lambda n: max(tree[n].leaves()) - min(tree[n].leaves())) + terminals.sort() + positions = set(positions) + + for m in terminals: + i = int(tree[m]) * scale + assert matrix[0][i] is None, (matrix[0][i], m, i) + matrix[0][i] = ids[m] + nodes[ids[m]] = sentence[tree[m]] + if nodes[ids[m]] is None: + nodes[ids[m]] = "..." + highlighted_nodes.discard(ids[m]) + positions.remove(m) + childcols[m[:-1]].add((0, i)) + + # add other nodes centered on their children, + # if the center is already taken, back off + # to the left and right alternately, until an empty cell is found. + for n in sorted(levels, reverse=True): + nodesatdepth = levels[n] + startoflevel = len(matrix) + matrix.append( + [vertline if a not in (corner, None) else None for a in matrix[-1]] + ) + for m in nodesatdepth: # [::-1]: + if n < maxdepth - 1 and childcols[m]: + _, pivot = min(childcols[m], key=itemgetter(1)) + if { + a[:-1] + for row in matrix[:-1] + for a in row[:pivot] + if isinstance(a, tuple) + } & { + a[:-1] + for row in matrix[:-1] + for a in row[pivot:] + if isinstance(a, tuple) + }: + crossed.add(m) + + rowidx, i = findcell(m, matrix, startoflevel, childcols) + positions.remove(m) + + # block positions where children of this node branch out + for _, x in childcols[m]: + matrix[rowidx][x] = corner + # assert m == () or matrix[rowidx][i] in (None, corner), ( + # matrix[rowidx][i], m, str(tree), ' '.join(sentence)) + # node itself + matrix[rowidx][i] = ids[m] + nodes[ids[m]] = tree[m] + # add column to the set of children for its parent + if len(m) > 0: + childcols[m[:-1]].add((rowidx, i)) + assert len(positions) == 0 + + # remove unused columns, right to left + for m in range(scale * len(sentence) - 1, -1, -1): + if not any(isinstance(row[m], (Tree, int)) for row in matrix): + for row in matrix: + del row[m] + + # remove unused rows, reverse + matrix = [ + row + for row in reversed(matrix) + if not all(a is None or a == vertline for a in row) + ] + + # collect coordinates of nodes + coords = {} + for n, _ in enumerate(matrix): + for m, i in enumerate(matrix[n]): + if isinstance(i, int) and i >= 0: + coords[i] = n, m + + # move crossed edges last + positions = sorted( + (a for level in levels.values() for a in level), + key=lambda a: a[:-1] in crossed, + ) + + # collect edges from node to node + edges = OrderedDict() + for i in reversed(positions): + for j, _ in enumerate(tree[i]): + edges[ids[i + (j,)]] = ids[i] + + return nodes, coords, edges, highlighted_nodes + + def text( + self, + nodedist=1, + unicodelines=False, + html=False, + ansi=False, + nodecolor="blue", + leafcolor="red", + funccolor="green", + abbreviate=None, + maxwidth=16, + ): + """ + :return: ASCII art for a discontinuous tree. + + :param unicodelines: whether to use Unicode line drawing characters + instead of plain (7-bit) ASCII. + :param html: whether to wrap output in html code (default plain text). + :param ansi: whether to produce colors with ANSI escape sequences + (only effective when html==False). + :param leafcolor, nodecolor: specify colors of leaves and phrasal + nodes; effective when either html or ansi is True. + :param abbreviate: if True, abbreviate labels longer than 5 characters. + If integer, abbreviate labels longer than `abbr` characters. + :param maxwidth: maximum number of characters before a label starts to + wrap; pass None to disable. + """ + if abbreviate == True: + abbreviate = 5 + if unicodelines: + horzline = "\u2500" + leftcorner = "\u250c" + rightcorner = "\u2510" + vertline = " \u2502 " + tee = horzline + "\u252C" + horzline + bottom = horzline + "\u2534" + horzline + cross = horzline + "\u253c" + horzline + ellipsis = "\u2026" + else: + horzline = "_" + leftcorner = rightcorner = " " + vertline = " | " + tee = 3 * horzline + cross = bottom = "_|_" + ellipsis = "." + + def crosscell(cur, x=vertline): + """Overwrite center of this cell with a vertical branch.""" + splitl = len(cur) - len(cur) // 2 - len(x) // 2 - 1 + lst = list(cur) + lst[splitl : splitl + len(x)] = list(x) + return "".join(lst) + + result = [] + matrix = defaultdict(dict) + maxnodewith = defaultdict(lambda: 3) + maxnodeheight = defaultdict(lambda: 1) + maxcol = 0 + minchildcol = {} + maxchildcol = {} + childcols = defaultdict(set) + labels = {} + wrapre = re.compile( + "(.{%d,%d}\\b\\W*|.{%d})" % (maxwidth - 4, maxwidth, maxwidth) + ) + # collect labels and coordinates + for a in self.nodes: + row, column = self.coords[a] + matrix[row][column] = a + maxcol = max(maxcol, column) + label = ( + self.nodes[a].label() + if isinstance(self.nodes[a], Tree) + else self.nodes[a] + ) + if abbreviate and len(label) > abbreviate: + label = label[:abbreviate] + ellipsis + if maxwidth and len(label) > maxwidth: + label = wrapre.sub(r"\1\n", label).strip() + label = label.split("\n") + maxnodeheight[row] = max(maxnodeheight[row], len(label)) + maxnodewith[column] = max(maxnodewith[column], max(map(len, label))) + labels[a] = label + if a not in self.edges: + continue # e.g., root + parent = self.edges[a] + childcols[parent].add((row, column)) + minchildcol[parent] = min(minchildcol.get(parent, column), column) + maxchildcol[parent] = max(maxchildcol.get(parent, column), column) + # bottom up level order traversal + for row in sorted(matrix, reverse=True): + noderows = [ + ["".center(maxnodewith[col]) for col in range(maxcol + 1)] + for _ in range(maxnodeheight[row]) + ] + branchrow = ["".center(maxnodewith[col]) for col in range(maxcol + 1)] + for col in matrix[row]: + n = matrix[row][col] + node = self.nodes[n] + text = labels[n] + if isinstance(node, Tree): + # draw horizontal branch towards children for this node + if n in minchildcol and minchildcol[n] < maxchildcol[n]: + i, j = minchildcol[n], maxchildcol[n] + a, b = (maxnodewith[i] + 1) // 2 - 1, maxnodewith[j] // 2 + branchrow[i] = ((" " * a) + leftcorner).ljust( + maxnodewith[i], horzline + ) + branchrow[j] = (rightcorner + (" " * b)).rjust( + maxnodewith[j], horzline + ) + for i in range(minchildcol[n] + 1, maxchildcol[n]): + if i == col and any(a == i for _, a in childcols[n]): + line = cross + elif i == col: + line = bottom + elif any(a == i for _, a in childcols[n]): + line = tee + else: + line = horzline + branchrow[i] = line.center(maxnodewith[i], horzline) + else: # if n and n in minchildcol: + branchrow[col] = crosscell(branchrow[col]) + text = [a.center(maxnodewith[col]) for a in text] + color = nodecolor if isinstance(node, Tree) else leafcolor + if isinstance(node, Tree) and node.label().startswith("-"): + color = funccolor + if html: + text = [escape(a, quote=False) for a in text] + if n in self.highlight: + text = [f"{a}" for a in text] + elif ansi and n in self.highlight: + text = ["\x1b[%d;1m%s\x1b[0m" % (ANSICOLOR[color], a) for a in text] + for x in range(maxnodeheight[row]): + # draw vertical lines in partially filled multiline node + # labels, but only if it's not a frontier node. + noderows[x][col] = ( + text[x] + if x < len(text) + else (vertline if childcols[n] else " ").center( + maxnodewith[col], " " + ) + ) + # for each column, if there is a node below us which has a parent + # above us, draw a vertical branch in that column. + if row != max(matrix): + for n, (childrow, col) in self.coords.items(): + if n > 0 and self.coords[self.edges[n]][0] < row < childrow: + branchrow[col] = crosscell(branchrow[col]) + if col not in matrix[row]: + for noderow in noderows: + noderow[col] = crosscell(noderow[col]) + branchrow = [ + a + ((a[-1] if a[-1] != " " else b[0]) * nodedist) + for a, b in zip(branchrow, branchrow[1:] + [" "]) + ] + result.append("".join(branchrow)) + result.extend( + (" " * nodedist).join(noderow) for noderow in reversed(noderows) + ) + return "\n".join(reversed(result)) + "\n" + + def svg(self, nodecolor="blue", leafcolor="red", funccolor="green"): + """ + :return: SVG representation of a tree. + """ + fontsize = 12 + hscale = 40 + vscale = 25 + hstart = vstart = 20 + width = max(col for _, col in self.coords.values()) + height = max(row for row, _ in self.coords.values()) + result = [ + '' + % ( + width * 3, + height * 2.5, + -hstart, + -vstart, + width * hscale + 3 * hstart, + height * vscale + 3 * vstart, + ) + ] + + children = defaultdict(set) + for n in self.nodes: + if n: + children[self.edges[n]].add(n) + + # horizontal branches from nodes to children + for node in self.nodes: + if not children[node]: + continue + y, x = self.coords[node] + x *= hscale + y *= vscale + x += hstart + y += vstart + fontsize // 2 + childx = [self.coords[c][1] for c in children[node]] + xmin = hstart + hscale * min(childx) + xmax = hstart + hscale * max(childx) + result.append( + '\t' % (xmin, y, xmax, y) + ) + result.append( + '\t' % (x, y, x, y - fontsize // 3) + ) + + # vertical branches from children to parents + for child, parent in self.edges.items(): + y, _ = self.coords[parent] + y *= vscale + y += vstart + fontsize // 2 + childy, childx = self.coords[child] + childx *= hscale + childy *= vscale + childx += hstart + childy += vstart - fontsize + result += [ + '\t' % (childx, childy, childx, y + 5), + '\t' % (childx, childy, childx, y), + ] + + # write nodes with coordinates + for n, (row, column) in self.coords.items(): + node = self.nodes[n] + x = column * hscale + hstart + y = row * vscale + vstart + if n in self.highlight: + color = nodecolor if isinstance(node, Tree) else leafcolor + if isinstance(node, Tree) and node.label().startswith("-"): + color = funccolor + else: + color = "black" + result += [ + '\t%s' + % ( + color, + fontsize, + x, + y, + escape( + node.label() if isinstance(node, Tree) else node, quote=False + ), + ) + ] + + result += [""] + return "\n".join(result) + + +def test(): + """Do some tree drawing tests.""" + + def print_tree(n, tree, sentence=None, ansi=True, **xargs): + print() + print('{}: "{}"'.format(n, " ".join(sentence or tree.leaves()))) + print(tree) + print() + drawtree = TreePrettyPrinter(tree, sentence) + try: + print(drawtree.text(unicodelines=ansi, ansi=ansi, **xargs)) + except (UnicodeDecodeError, UnicodeEncodeError): + print(drawtree.text(unicodelines=False, ansi=False, **xargs)) + + from nltk.corpus import treebank + + for n in [0, 1440, 1591, 2771, 2170]: + tree = treebank.parsed_sents()[n] + print_tree(n, tree, nodedist=2, maxwidth=8) + print() + print("ASCII version:") + print(TreePrettyPrinter(tree).text(nodedist=2)) + + tree = Tree.fromstring( + "(top (punct 8) (smain (noun 0) (verb 1) (inf (verb 5) (inf (verb 6) " + "(conj (inf (pp (prep 2) (np (det 3) (noun 4))) (verb 7)) (inf (verb 9)) " + "(vg 10) (inf (verb 11)))))) (punct 12))", + read_leaf=int, + ) + sentence = ( + "Ze had met haar moeder kunnen gaan winkelen ," + " zwemmen of terrassen .".split() + ) + print_tree("Discontinuous tree", tree, sentence, nodedist=2) + + +__all__ = ["TreePrettyPrinter"] + +if __name__ == "__main__": + test() diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/probabilistic.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/probabilistic.py new file mode 100644 index 0000000..73dc401 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/probabilistic.py @@ -0,0 +1,74 @@ +# Natural Language Toolkit: Text Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Tom Aarsen <> +# URL: +# For license information, see LICENSE.TXT + + +from nltk.internals import raise_unorderable_types +from nltk.probability import ProbabilisticMixIn +from nltk.tree.immutable import ImmutableProbabilisticTree +from nltk.tree.tree import Tree + +###################################################################### +## Probabilistic trees +###################################################################### + + +class ProbabilisticTree(Tree, ProbabilisticMixIn): + def __init__(self, node, children=None, **prob_kwargs): + Tree.__init__(self, node, children) + ProbabilisticMixIn.__init__(self, **prob_kwargs) + + # We have to patch up these methods to make them work right: + def _frozen_class(self): + return ImmutableProbabilisticTree + + def __repr__(self): + return f"{Tree.__repr__(self)} (p={self.prob()!r})" + + def __str__(self): + return f"{self.pformat(margin=60)} (p={self.prob():.6g})" + + def copy(self, deep=False): + if not deep: + return type(self)(self._label, self, prob=self.prob()) + else: + return type(self).convert(self) + + @classmethod + def convert(cls, val): + if isinstance(val, Tree): + children = [cls.convert(child) for child in val] + if isinstance(val, ProbabilisticMixIn): + return cls(val._label, children, prob=val.prob()) + else: + return cls(val._label, children, prob=1.0) + else: + return val + + def __eq__(self, other): + return self.__class__ is other.__class__ and ( + self._label, + list(self), + self.prob(), + ) == (other._label, list(other), other.prob()) + + def __lt__(self, other): + if not isinstance(other, Tree): + raise_unorderable_types("<", self, other) + if self.__class__ is other.__class__: + return (self._label, list(self), self.prob()) < ( + other._label, + list(other), + other.prob(), + ) + else: + return self.__class__.__name__ < other.__class__.__name__ + + +__all__ = ["ProbabilisticTree"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/transforms.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/transforms.py new file mode 100644 index 0000000..2ebaa4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/transforms.py @@ -0,0 +1,338 @@ +# Natural Language Toolkit: Tree Transformations +# +# Copyright (C) 2005-2007 Oregon Graduate Institute +# Author: Nathan Bodenstab +# URL: +# For license information, see LICENSE.TXT + +r""" +A collection of methods for tree (grammar) transformations used +in parsing natural language. + +Although many of these methods are technically grammar transformations +(ie. Chomsky Norm Form), when working with treebanks it is much more +natural to visualize these modifications in a tree structure. Hence, +we will do all transformation directly to the tree itself. +Transforming the tree directly also allows us to do parent annotation. +A grammar can then be simply induced from the modified tree. + +The following is a short tutorial on the available transformations. + + 1. Chomsky Normal Form (binarization) + + It is well known that any grammar has a Chomsky Normal Form (CNF) + equivalent grammar where CNF is defined by every production having + either two non-terminals or one terminal on its right hand side. + When we have hierarchically structured data (ie. a treebank), it is + natural to view this in terms of productions where the root of every + subtree is the head (left hand side) of the production and all of + its children are the right hand side constituents. In order to + convert a tree into CNF, we simply need to ensure that every subtree + has either two subtrees as children (binarization), or one leaf node + (non-terminal). In order to binarize a subtree with more than two + children, we must introduce artificial nodes. + + There are two popular methods to convert a tree into CNF: left + factoring and right factoring. The following example demonstrates + the difference between them. Example:: + + Original Right-Factored Left-Factored + + A A A + / | \ / \ / \ + B C D ==> B A| OR A| D + / \ / \ + C D B C + + 2. Parent Annotation + + In addition to binarizing the tree, there are two standard + modifications to node labels we can do in the same traversal: parent + annotation and Markov order-N smoothing (or sibling smoothing). + + The purpose of parent annotation is to refine the probabilities of + productions by adding a small amount of context. With this simple + addition, a CYK (inside-outside, dynamic programming chart parse) + can improve from 74% to 79% accuracy. A natural generalization from + parent annotation is to grandparent annotation and beyond. The + tradeoff becomes accuracy gain vs. computational complexity. We + must also keep in mind data sparcity issues. Example:: + + Original Parent Annotation + + A A^ + / | \ / \ + B C D ==> B^
    A|^ where ? is the + / \ parent of A + C^ D^ + + + 3. Markov order-N smoothing + + Markov smoothing combats data sparcity issues as well as decreasing + computational requirements by limiting the number of children + included in artificial nodes. In practice, most people use an order + 2 grammar. Example:: + + Original No Smoothing Markov order 1 Markov order 2 etc. + + __A__ A A A + / /|\ \ / \ / \ / \ + B C D E F ==> B A| ==> B A| ==> B A| + / \ / \ / \ + C ... C ... C ... + + + + Annotation decisions can be thought about in the vertical direction + (parent, grandparent, etc) and the horizontal direction (number of + siblings to keep). Parameters to the following functions specify + these values. For more information see: + + Dan Klein and Chris Manning (2003) "Accurate Unlexicalized + Parsing", ACL-03. https://www.aclweb.org/anthology/P03-1054 + + 4. Unary Collapsing + + Collapse unary productions (ie. subtrees with a single child) into a + new non-terminal (Tree node). This is useful when working with + algorithms that do not allow unary productions, yet you do not wish + to lose the parent information. Example:: + + A + | + B ==> A+B + / \ / \ + C D C D + +""" + +from nltk.tree.tree import Tree + + +def chomsky_normal_form( + tree, factor="right", horzMarkov=None, vertMarkov=0, childChar="|", parentChar="^" +): + # assume all subtrees have homogeneous children + # assume all terminals have no siblings + + # A semi-hack to have elegant looking code below. As a result, + # any subtree with a branching factor greater than 999 will be incorrectly truncated. + if horzMarkov is None: + horzMarkov = 999 + + # Traverse the tree depth-first keeping a list of ancestor nodes to the root. + # I chose not to use the tree.treepositions() method since it requires + # two traversals of the tree (one to get the positions, one to iterate + # over them) and node access time is proportional to the height of the node. + # This method is 7x faster which helps when parsing 40,000 sentences. + + nodeList = [(tree, [tree.label()])] + while nodeList != []: + node, parent = nodeList.pop() + if isinstance(node, Tree): + + # parent annotation + parentString = "" + originalNode = node.label() + if vertMarkov != 0 and node != tree and isinstance(node[0], Tree): + parentString = "{}<{}>".format(parentChar, "-".join(parent)) + node.set_label(node.label() + parentString) + parent = [originalNode] + parent[: vertMarkov - 1] + + # add children to the agenda before we mess with them + for child in node: + nodeList.append((child, parent)) + + # chomsky normal form factorization + if len(node) > 2: + childNodes = [child.label() for child in node] + nodeCopy = node.copy() + node[0:] = [] # delete the children + + curNode = node + numChildren = len(nodeCopy) + for i in range(1, numChildren - 1): + if factor == "right": + newHead = "{}{}<{}>{}".format( + originalNode, + childChar, + "-".join( + childNodes[i : min([i + horzMarkov, numChildren])] + ), + parentString, + ) # create new head + newNode = Tree(newHead, []) + curNode[0:] = [nodeCopy.pop(0), newNode] + else: + newHead = "{}{}<{}>{}".format( + originalNode, + childChar, + "-".join( + childNodes[max([numChildren - i - horzMarkov, 0]) : -i] + ), + parentString, + ) + newNode = Tree(newHead, []) + curNode[0:] = [newNode, nodeCopy.pop()] + + curNode = newNode + + curNode[0:] = [child for child in nodeCopy] + + +def un_chomsky_normal_form( + tree, expandUnary=True, childChar="|", parentChar="^", unaryChar="+" +): + # Traverse the tree-depth first keeping a pointer to the parent for modification purposes. + nodeList = [(tree, [])] + while nodeList != []: + node, parent = nodeList.pop() + if isinstance(node, Tree): + # if the node contains the 'childChar' character it means that + # it is an artificial node and can be removed, although we still need + # to move its children to its parent + childIndex = node.label().find(childChar) + if childIndex != -1: + nodeIndex = parent.index(node) + parent.remove(parent[nodeIndex]) + # Generated node was on the left if the nodeIndex is 0 which + # means the grammar was left factored. We must insert the children + # at the beginning of the parent's children + if nodeIndex == 0: + parent.insert(0, node[0]) + parent.insert(1, node[1]) + else: + parent.extend([node[0], node[1]]) + + # parent is now the current node so the children of parent will be added to the agenda + node = parent + else: + parentIndex = node.label().find(parentChar) + if parentIndex != -1: + # strip the node name of the parent annotation + node.set_label(node.label()[:parentIndex]) + + # expand collapsed unary productions + if expandUnary == True: + unaryIndex = node.label().find(unaryChar) + if unaryIndex != -1: + newNode = Tree( + node.label()[unaryIndex + 1 :], [i for i in node] + ) + node.set_label(node.label()[:unaryIndex]) + node[0:] = [newNode] + + for child in node: + nodeList.append((child, node)) + + +def collapse_unary(tree, collapsePOS=False, collapseRoot=False, joinChar="+"): + """ + Collapse subtrees with a single child (ie. unary productions) + into a new non-terminal (Tree node) joined by 'joinChar'. + This is useful when working with algorithms that do not allow + unary productions, and completely removing the unary productions + would require loss of useful information. The Tree is modified + directly (since it is passed by reference) and no value is returned. + + :param tree: The Tree to be collapsed + :type tree: Tree + :param collapsePOS: 'False' (default) will not collapse the parent of leaf nodes (ie. + Part-of-Speech tags) since they are always unary productions + :type collapsePOS: bool + :param collapseRoot: 'False' (default) will not modify the root production + if it is unary. For the Penn WSJ treebank corpus, this corresponds + to the TOP -> productions. + :type collapseRoot: bool + :param joinChar: A string used to connect collapsed node values (default = "+") + :type joinChar: str + """ + + if collapseRoot == False and isinstance(tree, Tree) and len(tree) == 1: + nodeList = [tree[0]] + else: + nodeList = [tree] + + # depth-first traversal of tree + while nodeList != []: + node = nodeList.pop() + if isinstance(node, Tree): + if ( + len(node) == 1 + and isinstance(node[0], Tree) + and (collapsePOS == True or isinstance(node[0, 0], Tree)) + ): + node.set_label(node.label() + joinChar + node[0].label()) + node[0:] = [child for child in node[0]] + # since we assigned the child's children to the current node, + # evaluate the current node again + nodeList.append(node) + else: + for child in node: + nodeList.append(child) + + +################################################################# +# Demonstration +################################################################# + + +def demo(): + """ + A demonstration showing how each tree transform can be used. + """ + + from copy import deepcopy + + from nltk.draw.tree import draw_trees + from nltk.tree.tree import Tree + + # original tree from WSJ bracketed text + sentence = """(TOP + (S + (S + (VP + (VBN Turned) + (ADVP (RB loose)) + (PP + (IN in) + (NP + (NP (NNP Shane) (NNP Longman) (POS 's)) + (NN trading) + (NN room))))) + (, ,) + (NP (DT the) (NN yuppie) (NNS dealers)) + (VP (AUX do) (NP (NP (RB little)) (ADJP (RB right)))) + (. .)))""" + t = Tree.fromstring(sentence, remove_empty_top_bracketing=True) + + # collapse subtrees with only one child + collapsedTree = deepcopy(t) + collapse_unary(collapsedTree) + + # convert the tree to CNF + cnfTree = deepcopy(collapsedTree) + chomsky_normal_form(cnfTree) + + # convert the tree to CNF with parent annotation (one level) and horizontal smoothing of order two + parentTree = deepcopy(collapsedTree) + chomsky_normal_form(parentTree, horzMarkov=2, vertMarkov=1) + + # convert the tree back to its original form (used to make CYK results comparable) + original = deepcopy(parentTree) + un_chomsky_normal_form(original) + + # convert tree back to bracketed text + sentence2 = original.pprint() + print(sentence) + print(sentence2) + print("Sentences the same? ", sentence == sentence2) + + draw_trees(t, collapsedTree, cnfTree, parentTree, original) + + +if __name__ == "__main__": + demo() + +__all__ = ["chomsky_normal_form", "un_chomsky_normal_form", "collapse_unary"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/tree/tree.py b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/tree.py new file mode 100644 index 0000000..1373c5f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/tree/tree.py @@ -0,0 +1,982 @@ +# Natural Language Toolkit: Text Trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Edward Loper +# Steven Bird +# Peter Ljunglöf +# Nathan Bodenstab (tree transforms) +# Eric Kafe (Tree.fromlist()) +# Mohaned mashaly (Deprecating methods) +# URL: +# For license information, see LICENSE.TXT + +""" +Class for representing hierarchical language structures, such as +syntax trees and morphological trees. +""" + +import re + +from nltk.grammar import Nonterminal, Production +from nltk.internals import deprecated + +###################################################################### +## Trees +###################################################################### + + +class Tree(list): + r""" + A Tree represents a hierarchical grouping of leaves and subtrees. + For example, each constituent in a syntax tree is represented by a single Tree. + + A tree's children are encoded as a list of leaves and subtrees, + where a leaf is a basic (non-tree) value; and a subtree is a + nested Tree. + + >>> from nltk.tree import Tree + >>> print(Tree(1, [2, Tree(3, [4]), 5])) + (1 2 (3 4) 5) + >>> vp = Tree('VP', [Tree('V', ['saw']), + ... Tree('NP', ['him'])]) + >>> s = Tree('S', [Tree('NP', ['I']), vp]) + >>> print(s) + (S (NP I) (VP (V saw) (NP him))) + >>> print(s[1]) + (VP (V saw) (NP him)) + >>> print(s[1,1]) + (NP him) + >>> t = Tree.fromstring("(S (NP I) (VP (V saw) (NP him)))") + >>> s == t + True + >>> t[1][1].set_label('X') + >>> t[1][1].label() + 'X' + >>> print(t) + (S (NP I) (VP (V saw) (X him))) + >>> t[0], t[1,1] = t[1,1], t[0] + >>> print(t) + (S (X him) (VP (V saw) (NP I))) + + The length of a tree is the number of children it has. + + >>> len(t) + 2 + + The set_label() and label() methods allow individual constituents + to be labeled. For example, syntax trees use this label to specify + phrase tags, such as "NP" and "VP". + + Several Tree methods use "tree positions" to specify + children or descendants of a tree. Tree positions are defined as + follows: + + - The tree position *i* specifies a Tree's *i*\ th child. + - The tree position ``()`` specifies the Tree itself. + - If *p* is the tree position of descendant *d*, then + *p+i* specifies the *i*\ th child of *d*. + + I.e., every tree position is either a single index *i*, + specifying ``tree[i]``; or a sequence *i1, i2, ..., iN*, + specifying ``tree[i1][i2]...[iN]``. + + Construct a new tree. This constructor can be called in one + of two ways: + + - ``Tree(label, children)`` constructs a new tree with the + specified label and list of children. + + - ``Tree.fromstring(s)`` constructs a new tree by parsing the string ``s``. + """ + + def __init__(self, node, children=None): + if children is None: + raise TypeError( + "%s: Expected a node value and child list " % type(self).__name__ + ) + elif isinstance(children, str): + raise TypeError( + "%s() argument 2 should be a list, not a " + "string" % type(self).__name__ + ) + else: + list.__init__(self, children) + self._label = node + + # //////////////////////////////////////////////////////////// + # Comparison operators + # //////////////////////////////////////////////////////////// + + def __eq__(self, other): + return self.__class__ is other.__class__ and (self._label, list(self)) == ( + other._label, + list(other), + ) + + def __lt__(self, other): + if not isinstance(other, Tree): + # raise_unorderable_types("<", self, other) + # Sometimes children can be pure strings, + # so we need to be able to compare with non-trees: + return self.__class__.__name__ < other.__class__.__name__ + elif self.__class__ is other.__class__: + return (self._label, list(self)) < (other._label, list(other)) + else: + return self.__class__.__name__ < other.__class__.__name__ + + # @total_ordering doesn't work here, since the class inherits from a builtin class + __ne__ = lambda self, other: not self == other + __gt__ = lambda self, other: not (self < other or self == other) + __le__ = lambda self, other: self < other or self == other + __ge__ = lambda self, other: not self < other + + # //////////////////////////////////////////////////////////// + # Disabled list operations + # //////////////////////////////////////////////////////////// + + def __mul__(self, v): + raise TypeError("Tree does not support multiplication") + + def __rmul__(self, v): + raise TypeError("Tree does not support multiplication") + + def __add__(self, v): + raise TypeError("Tree does not support addition") + + def __radd__(self, v): + raise TypeError("Tree does not support addition") + + # //////////////////////////////////////////////////////////// + # Indexing (with support for tree positions) + # //////////////////////////////////////////////////////////// + + def __getitem__(self, index): + if isinstance(index, (int, slice)): + return list.__getitem__(self, index) + elif isinstance(index, (list, tuple)): + if len(index) == 0: + return self + elif len(index) == 1: + return self[index[0]] + else: + return self[index[0]][index[1:]] + else: + raise TypeError( + "%s indices must be integers, not %s" + % (type(self).__name__, type(index).__name__) + ) + + def __setitem__(self, index, value): + if isinstance(index, (int, slice)): + return list.__setitem__(self, index, value) + elif isinstance(index, (list, tuple)): + if len(index) == 0: + raise IndexError("The tree position () may not be " "assigned to.") + elif len(index) == 1: + self[index[0]] = value + else: + self[index[0]][index[1:]] = value + else: + raise TypeError( + "%s indices must be integers, not %s" + % (type(self).__name__, type(index).__name__) + ) + + def __delitem__(self, index): + if isinstance(index, (int, slice)): + return list.__delitem__(self, index) + elif isinstance(index, (list, tuple)): + if len(index) == 0: + raise IndexError("The tree position () may not be deleted.") + elif len(index) == 1: + del self[index[0]] + else: + del self[index[0]][index[1:]] + else: + raise TypeError( + "%s indices must be integers, not %s" + % (type(self).__name__, type(index).__name__) + ) + + # //////////////////////////////////////////////////////////// + # Basic tree operations + # //////////////////////////////////////////////////////////// + @deprecated("Use label() instead") + def _get_node(self): + """Outdated method to access the node value; use the label() method instead.""" + + @deprecated("Use set_label() instead") + def _set_node(self, value): + """Outdated method to set the node value; use the set_label() method instead.""" + + node = property(_get_node, _set_node) + + def label(self): + """ + Return the node label of the tree. + + >>> t = Tree.fromstring('(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))') + >>> t.label() + 'S' + + :return: the node label (typically a string) + :rtype: any + """ + return self._label + + def set_label(self, label): + """ + Set the node label of the tree. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.set_label("T") + >>> print(t) + (T (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat)))) + + :param label: the node label (typically a string) + :type label: any + """ + self._label = label + + def leaves(self): + """ + Return the leaves of the tree. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.leaves() + ['the', 'dog', 'chased', 'the', 'cat'] + + :return: a list containing this tree's leaves. + The order reflects the order of the + leaves in the tree's hierarchical structure. + :rtype: list + """ + leaves = [] + for child in self: + if isinstance(child, Tree): + leaves.extend(child.leaves()) + else: + leaves.append(child) + return leaves + + def flatten(self): + """ + Return a flat version of the tree, with all non-root non-terminals removed. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> print(t.flatten()) + (S the dog chased the cat) + + :return: a tree consisting of this tree's root connected directly to + its leaves, omitting all intervening non-terminal nodes. + :rtype: Tree + """ + return Tree(self.label(), self.leaves()) + + def height(self): + """ + Return the height of the tree. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.height() + 5 + >>> print(t[0,0]) + (D the) + >>> t[0,0].height() + 2 + + :return: The height of this tree. The height of a tree + containing no children is 1; the height of a tree + containing only leaves is 2; and the height of any other + tree is one plus the maximum of its children's + heights. + :rtype: int + """ + max_child_height = 0 + for child in self: + if isinstance(child, Tree): + max_child_height = max(max_child_height, child.height()) + else: + max_child_height = max(max_child_height, 1) + return 1 + max_child_height + + def treepositions(self, order="preorder"): + """ + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.treepositions() # doctest: +ELLIPSIS + [(), (0,), (0, 0), (0, 0, 0), (0, 1), (0, 1, 0), (1,), (1, 0), (1, 0, 0), ...] + >>> for pos in t.treepositions('leaves'): + ... t[pos] = t[pos][::-1].upper() + >>> print(t) + (S (NP (D EHT) (N GOD)) (VP (V DESAHC) (NP (D EHT) (N TAC)))) + + :param order: One of: ``preorder``, ``postorder``, ``bothorder``, + ``leaves``. + """ + positions = [] + if order in ("preorder", "bothorder"): + positions.append(()) + for i, child in enumerate(self): + if isinstance(child, Tree): + childpos = child.treepositions(order) + positions.extend((i,) + p for p in childpos) + else: + positions.append((i,)) + if order in ("postorder", "bothorder"): + positions.append(()) + return positions + + def subtrees(self, filter=None): + """ + Generate all the subtrees of this tree, optionally restricted + to trees matching the filter function. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> for s in t.subtrees(lambda t: t.height() == 2): + ... print(s) + (D the) + (N dog) + (V chased) + (D the) + (N cat) + + :type filter: function + :param filter: the function to filter all local trees + """ + if not filter or filter(self): + yield self + for child in self: + if isinstance(child, Tree): + yield from child.subtrees(filter) + + def productions(self): + """ + Generate the productions that correspond to the non-terminal nodes of the tree. + For each subtree of the form (P: C1 C2 ... Cn) this produces a production of the + form P -> C1 C2 ... Cn. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.productions() # doctest: +NORMALIZE_WHITESPACE + [S -> NP VP, NP -> D N, D -> 'the', N -> 'dog', VP -> V NP, V -> 'chased', + NP -> D N, D -> 'the', N -> 'cat'] + + :rtype: list(Production) + """ + + if not isinstance(self._label, str): + raise TypeError( + "Productions can only be generated from trees having node labels that are strings" + ) + + prods = [Production(Nonterminal(self._label), _child_names(self))] + for child in self: + if isinstance(child, Tree): + prods += child.productions() + return prods + + def pos(self): + """ + Return a sequence of pos-tagged words extracted from the tree. + + >>> t = Tree.fromstring("(S (NP (D the) (N dog)) (VP (V chased) (NP (D the) (N cat))))") + >>> t.pos() + [('the', 'D'), ('dog', 'N'), ('chased', 'V'), ('the', 'D'), ('cat', 'N')] + + :return: a list of tuples containing leaves and pre-terminals (part-of-speech tags). + The order reflects the order of the leaves in the tree's hierarchical structure. + :rtype: list(tuple) + """ + pos = [] + for child in self: + if isinstance(child, Tree): + pos.extend(child.pos()) + else: + pos.append((child, self._label)) + return pos + + def leaf_treeposition(self, index): + """ + :return: The tree position of the ``index``-th leaf in this + tree. I.e., if ``tp=self.leaf_treeposition(i)``, then + ``self[tp]==self.leaves()[i]``. + + :raise IndexError: If this tree contains fewer than ``index+1`` + leaves, or if ``index<0``. + """ + if index < 0: + raise IndexError("index must be non-negative") + + stack = [(self, ())] + while stack: + value, treepos = stack.pop() + if not isinstance(value, Tree): + if index == 0: + return treepos + else: + index -= 1 + else: + for i in range(len(value) - 1, -1, -1): + stack.append((value[i], treepos + (i,))) + + raise IndexError("index must be less than or equal to len(self)") + + def treeposition_spanning_leaves(self, start, end): + """ + :return: The tree position of the lowest descendant of this + tree that dominates ``self.leaves()[start:end]``. + :raise ValueError: if ``end <= start`` + """ + if end <= start: + raise ValueError("end must be greater than start") + # Find the tree positions of the start & end leaves, and + # take the longest common subsequence. + start_treepos = self.leaf_treeposition(start) + end_treepos = self.leaf_treeposition(end - 1) + # Find the first index where they mismatch: + for i in range(len(start_treepos)): + if i == len(end_treepos) or start_treepos[i] != end_treepos[i]: + return start_treepos[:i] + return start_treepos + + # //////////////////////////////////////////////////////////// + # Transforms + # //////////////////////////////////////////////////////////// + + def chomsky_normal_form( + self, + factor="right", + horzMarkov=None, + vertMarkov=0, + childChar="|", + parentChar="^", + ): + """ + This method can modify a tree in three ways: + + 1. Convert a tree into its Chomsky Normal Form (CNF) + equivalent -- Every subtree has either two non-terminals + or one terminal as its children. This process requires + the creation of more"artificial" non-terminal nodes. + 2. Markov (vertical) smoothing of children in new artificial + nodes + 3. Horizontal (parent) annotation of nodes + + :param factor: Right or left factoring method (default = "right") + :type factor: str = [left|right] + :param horzMarkov: Markov order for sibling smoothing in artificial nodes (None (default) = include all siblings) + :type horzMarkov: int | None + :param vertMarkov: Markov order for parent smoothing (0 (default) = no vertical annotation) + :type vertMarkov: int | None + :param childChar: A string used in construction of the artificial nodes, separating the head of the + original subtree from the child nodes that have yet to be expanded (default = "|") + :type childChar: str + :param parentChar: A string used to separate the node representation from its vertical annotation + :type parentChar: str + """ + from nltk.tree.transforms import chomsky_normal_form + + chomsky_normal_form(self, factor, horzMarkov, vertMarkov, childChar, parentChar) + + def un_chomsky_normal_form( + self, expandUnary=True, childChar="|", parentChar="^", unaryChar="+" + ): + """ + This method modifies the tree in three ways: + + 1. Transforms a tree in Chomsky Normal Form back to its + original structure (branching greater than two) + 2. Removes any parent annotation (if it exists) + 3. (optional) expands unary subtrees (if previously + collapsed with collapseUnary(...) ) + + :param expandUnary: Flag to expand unary or not (default = True) + :type expandUnary: bool + :param childChar: A string separating the head node from its children in an artificial node (default = "|") + :type childChar: str + :param parentChar: A string separating the node label from its parent annotation (default = "^") + :type parentChar: str + :param unaryChar: A string joining two non-terminals in a unary production (default = "+") + :type unaryChar: str + """ + from nltk.tree.transforms import un_chomsky_normal_form + + un_chomsky_normal_form(self, expandUnary, childChar, parentChar, unaryChar) + + def collapse_unary(self, collapsePOS=False, collapseRoot=False, joinChar="+"): + """ + Collapse subtrees with a single child (ie. unary productions) + into a new non-terminal (Tree node) joined by 'joinChar'. + This is useful when working with algorithms that do not allow + unary productions, and completely removing the unary productions + would require loss of useful information. The Tree is modified + directly (since it is passed by reference) and no value is returned. + + :param collapsePOS: 'False' (default) will not collapse the parent of leaf nodes (ie. + Part-of-Speech tags) since they are always unary productions + :type collapsePOS: bool + :param collapseRoot: 'False' (default) will not modify the root production + if it is unary. For the Penn WSJ treebank corpus, this corresponds + to the TOP -> productions. + :type collapseRoot: bool + :param joinChar: A string used to connect collapsed node values (default = "+") + :type joinChar: str + """ + from nltk.tree.transforms import collapse_unary + + collapse_unary(self, collapsePOS, collapseRoot, joinChar) + + # //////////////////////////////////////////////////////////// + # Convert, copy + # //////////////////////////////////////////////////////////// + + @classmethod + def convert(cls, tree): + """ + Convert a tree between different subtypes of Tree. ``cls`` determines + which class will be used to encode the new tree. + + :type tree: Tree + :param tree: The tree that should be converted. + :return: The new Tree. + """ + if isinstance(tree, Tree): + children = [cls.convert(child) for child in tree] + return cls(tree._label, children) + else: + return tree + + def __copy__(self): + return self.copy() + + def __deepcopy__(self, memo): + return self.copy(deep=True) + + def copy(self, deep=False): + if not deep: + return type(self)(self._label, self) + else: + return type(self).convert(self) + + def _frozen_class(self): + from nltk.tree.immutable import ImmutableTree + + return ImmutableTree + + def freeze(self, leaf_freezer=None): + frozen_class = self._frozen_class() + if leaf_freezer is None: + newcopy = frozen_class.convert(self) + else: + newcopy = self.copy(deep=True) + for pos in newcopy.treepositions("leaves"): + newcopy[pos] = leaf_freezer(newcopy[pos]) + newcopy = frozen_class.convert(newcopy) + hash(newcopy) # Make sure the leaves are hashable. + return newcopy + + # //////////////////////////////////////////////////////////// + # Parsing + # //////////////////////////////////////////////////////////// + + @classmethod + def fromstring( + cls, + s, + brackets="()", + read_node=None, + read_leaf=None, + node_pattern=None, + leaf_pattern=None, + remove_empty_top_bracketing=False, + ): + """ + Read a bracketed tree string and return the resulting tree. + Trees are represented as nested brackettings, such as:: + + (S (NP (NNP John)) (VP (V runs))) + + :type s: str + :param s: The string to read + + :type brackets: str (length=2) + :param brackets: The bracket characters used to mark the + beginning and end of trees and subtrees. + + :type read_node: function + :type read_leaf: function + :param read_node, read_leaf: If specified, these functions + are applied to the substrings of ``s`` corresponding to + nodes and leaves (respectively) to obtain the values for + those nodes and leaves. They should have the following + signature: + + read_node(str) -> value + + For example, these functions could be used to process nodes + and leaves whose values should be some type other than + string (such as ``FeatStruct``). + Note that by default, node strings and leaf strings are + delimited by whitespace and brackets; to override this + default, use the ``node_pattern`` and ``leaf_pattern`` + arguments. + + :type node_pattern: str + :type leaf_pattern: str + :param node_pattern, leaf_pattern: Regular expression patterns + used to find node and leaf substrings in ``s``. By + default, both nodes patterns are defined to match any + sequence of non-whitespace non-bracket characters. + + :type remove_empty_top_bracketing: bool + :param remove_empty_top_bracketing: If the resulting tree has + an empty node label, and is length one, then return its + single child instead. This is useful for treebank trees, + which sometimes contain an extra level of bracketing. + + :return: A tree corresponding to the string representation ``s``. + If this class method is called using a subclass of Tree, + then it will return a tree of that type. + :rtype: Tree + """ + if not isinstance(brackets, str) or len(brackets) != 2: + raise TypeError("brackets must be a length-2 string") + if re.search(r"\s", brackets): + raise TypeError("whitespace brackets not allowed") + # Construct a regexp that will tokenize the string. + open_b, close_b = brackets + open_pattern, close_pattern = (re.escape(open_b), re.escape(close_b)) + if node_pattern is None: + node_pattern = rf"[^\s{open_pattern}{close_pattern}]+" + if leaf_pattern is None: + leaf_pattern = rf"[^\s{open_pattern}{close_pattern}]+" + token_re = re.compile( + r"%s\s*(%s)?|%s|(%s)" + % (open_pattern, node_pattern, close_pattern, leaf_pattern) + ) + # Walk through each token, updating a stack of trees. + stack = [(None, [])] # list of (node, children) tuples + for match in token_re.finditer(s): + token = match.group() + # Beginning of a tree/subtree + if token[0] == open_b: + if len(stack) == 1 and len(stack[0][1]) > 0: + cls._parse_error(s, match, "end-of-string") + label = token[1:].lstrip() + if read_node is not None: + label = read_node(label) + stack.append((label, [])) + # End of a tree/subtree + elif token == close_b: + if len(stack) == 1: + if len(stack[0][1]) == 0: + cls._parse_error(s, match, open_b) + else: + cls._parse_error(s, match, "end-of-string") + label, children = stack.pop() + stack[-1][1].append(cls(label, children)) + # Leaf node + else: + if len(stack) == 1: + cls._parse_error(s, match, open_b) + if read_leaf is not None: + token = read_leaf(token) + stack[-1][1].append(token) + + # check that we got exactly one complete tree. + if len(stack) > 1: + cls._parse_error(s, "end-of-string", close_b) + elif len(stack[0][1]) == 0: + cls._parse_error(s, "end-of-string", open_b) + else: + assert stack[0][0] is None + assert len(stack[0][1]) == 1 + tree = stack[0][1][0] + + # If the tree has an extra level with node='', then get rid of + # it. E.g.: "((S (NP ...) (VP ...)))" + if remove_empty_top_bracketing and tree._label == "" and len(tree) == 1: + tree = tree[0] + # return the tree. + return tree + + @classmethod + def _parse_error(cls, s, match, expecting): + """ + Display a friendly error message when parsing a tree string fails. + :param s: The string we're parsing. + :param match: regexp match of the problem token. + :param expecting: what we expected to see instead. + """ + # Construct a basic error message + if match == "end-of-string": + pos, token = len(s), "end-of-string" + else: + pos, token = match.start(), match.group() + msg = "%s.read(): expected %r but got %r\n%sat index %d." % ( + cls.__name__, + expecting, + token, + " " * 12, + pos, + ) + # Add a display showing the error token itsels: + s = s.replace("\n", " ").replace("\t", " ") + offset = pos + if len(s) > pos + 10: + s = s[: pos + 10] + "..." + if pos > 10: + s = "..." + s[pos - 10 :] + offset = 13 + msg += '\n{}"{}"\n{}^'.format(" " * 16, s, " " * (17 + offset)) + raise ValueError(msg) + + @classmethod + def fromlist(cls, l): + """ + :type l: list + :param l: a tree represented as nested lists + + :return: A tree corresponding to the list representation ``l``. + :rtype: Tree + + Convert nested lists to a NLTK Tree + """ + if type(l) == list and len(l) > 0: + label = repr(l[0]) + if len(l) > 1: + return Tree(label, [cls.fromlist(child) for child in l[1:]]) + else: + return label + + # //////////////////////////////////////////////////////////// + # Visualization & String Representation + # //////////////////////////////////////////////////////////// + + def draw(self): + """ + Open a new window containing a graphical diagram of this tree. + """ + from nltk.draw.tree import draw_trees + + draw_trees(self) + + def pretty_print(self, sentence=None, highlight=(), stream=None, **kwargs): + """ + Pretty-print this tree as ASCII or Unicode art. + For explanation of the arguments, see the documentation for + `nltk.tree.prettyprinter.TreePrettyPrinter`. + """ + from nltk.tree.prettyprinter import TreePrettyPrinter + + print(TreePrettyPrinter(self, sentence, highlight).text(**kwargs), file=stream) + + def __repr__(self): + childstr = ", ".join(repr(c) for c in self) + return "{}({}, [{}])".format( + type(self).__name__, + repr(self._label), + childstr, + ) + + def _repr_svg_(self): + from svgling import draw_tree + + return draw_tree(self)._repr_svg_() + + def __str__(self): + return self.pformat() + + def pprint(self, **kwargs): + """ + Print a string representation of this Tree to 'stream' + """ + + if "stream" in kwargs: + stream = kwargs["stream"] + del kwargs["stream"] + else: + stream = None + print(self.pformat(**kwargs), file=stream) + + def pformat(self, margin=70, indent=0, nodesep="", parens="()", quotes=False): + """ + :return: A pretty-printed string representation of this tree. + :rtype: str + :param margin: The right margin at which to do line-wrapping. + :type margin: int + :param indent: The indentation level at which printing + begins. This number is used to decide how far to indent + subsequent lines. + :type indent: int + :param nodesep: A string that is used to separate the node + from the children. E.g., the default value ``':'`` gives + trees like ``(S: (NP: I) (VP: (V: saw) (NP: it)))``. + """ + + # Try writing it on one line. + s = self._pformat_flat(nodesep, parens, quotes) + if len(s) + indent < margin: + return s + + # If it doesn't fit on one line, then write it on multi-lines. + if isinstance(self._label, str): + s = f"{parens[0]}{self._label}{nodesep}" + else: + s = f"{parens[0]}{repr(self._label)}{nodesep}" + for child in self: + if isinstance(child, Tree): + s += ( + "\n" + + " " * (indent + 2) + + child.pformat(margin, indent + 2, nodesep, parens, quotes) + ) + elif isinstance(child, tuple): + s += "\n" + " " * (indent + 2) + "/".join(child) + elif isinstance(child, str) and not quotes: + s += "\n" + " " * (indent + 2) + "%s" % child + else: + s += "\n" + " " * (indent + 2) + repr(child) + return s + parens[1] + + def pformat_latex_qtree(self): + r""" + Returns a representation of the tree compatible with the + LaTeX qtree package. This consists of the string ``\Tree`` + followed by the tree represented in bracketed notation. + + For example, the following result was generated from a parse tree of + the sentence ``The announcement astounded us``:: + + \Tree [.I'' [.N'' [.D The ] [.N' [.N announcement ] ] ] + [.I' [.V'' [.V' [.V astounded ] [.N'' [.N' [.N us ] ] ] ] ] ] ] + + See https://www.ling.upenn.edu/advice/latex.html for the LaTeX + style file for the qtree package. + + :return: A latex qtree representation of this tree. + :rtype: str + """ + reserved_chars = re.compile(r"([#\$%&~_\{\}])") + + pformat = self.pformat(indent=6, nodesep="", parens=("[.", " ]")) + return r"\Tree " + re.sub(reserved_chars, r"\\\1", pformat) + + def _pformat_flat(self, nodesep, parens, quotes): + childstrs = [] + for child in self: + if isinstance(child, Tree): + childstrs.append(child._pformat_flat(nodesep, parens, quotes)) + elif isinstance(child, tuple): + childstrs.append("/".join(child)) + elif isinstance(child, str) and not quotes: + childstrs.append("%s" % child) + else: + childstrs.append(repr(child)) + if isinstance(self._label, str): + return "{}{}{} {}{}".format( + parens[0], + self._label, + nodesep, + " ".join(childstrs), + parens[1], + ) + else: + return "{}{}{} {}{}".format( + parens[0], + repr(self._label), + nodesep, + " ".join(childstrs), + parens[1], + ) + + +def _child_names(tree): + names = [] + for child in tree: + if isinstance(child, Tree): + names.append(Nonterminal(child._label)) + else: + names.append(child) + return names + + +###################################################################### +## Demonstration +###################################################################### + + +def demo(): + """ + A demonstration showing how Trees and Trees can be + used. This demonstration creates a Tree, and loads a + Tree from the Treebank corpus, + and shows the results of calling several of their methods. + """ + + from nltk import ProbabilisticTree, Tree + + # Demonstrate tree parsing. + s = "(S (NP (DT the) (NN cat)) (VP (VBD ate) (NP (DT a) (NN cookie))))" + t = Tree.fromstring(s) + print("Convert bracketed string into tree:") + print(t) + print(t.__repr__()) + + print("Display tree properties:") + print(t.label()) # tree's constituent type + print(t[0]) # tree's first child + print(t[1]) # tree's second child + print(t.height()) + print(t.leaves()) + print(t[1]) + print(t[1, 1]) + print(t[1, 1, 0]) + + # Demonstrate tree modification. + the_cat = t[0] + the_cat.insert(1, Tree.fromstring("(JJ big)")) + print("Tree modification:") + print(t) + t[1, 1, 1] = Tree.fromstring("(NN cake)") + print(t) + print() + + # Tree transforms + print("Collapse unary:") + t.collapse_unary() + print(t) + print("Chomsky normal form:") + t.chomsky_normal_form() + print(t) + print() + + # Demonstrate probabilistic trees. + pt = ProbabilisticTree("x", ["y", "z"], prob=0.5) + print("Probabilistic Tree:") + print(pt) + print() + + # Demonstrate parsing of treebank output format. + t = Tree.fromstring(t.pformat()) + print("Convert tree to bracketed string and back again:") + print(t) + print() + + # Demonstrate LaTeX output + print("LaTeX output:") + print(t.pformat_latex_qtree()) + print() + + # Demonstrate Productions + print("Production output:") + print(t.productions()) + print() + + # Demonstrate tree nodes containing objects other than strings + t.set_label(("test", 3)) + print(t) + + +__all__ = [ + "Tree", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/treeprettyprinter.py b/.pythonlibs/lib/python3.10/site-packages/nltk/treeprettyprinter.py new file mode 100644 index 0000000..63302f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/treeprettyprinter.py @@ -0,0 +1,28 @@ +# Natural Language Toolkit: ASCII visualization of NLTK trees +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Andreas van Cranenburgh +# Peter Ljunglöf +# URL: +# For license information, see LICENSE.TXT + +""" +Pretty-printing of discontinuous trees. +Adapted from the disco-dop project, by Andreas van Cranenburgh. +https://github.com/andreasvc/disco-dop + +Interesting reference (not used for this code): +T. Eschbach et al., Orth. Hypergraph Drawing, Journal of +Graph Algorithms and Applications, 10(2) 141--157 (2006)149. +https://jgaa.info/accepted/2006/EschbachGuentherBecker2006.10.2.pdf +""" + +from nltk.internals import Deprecated +from nltk.tree.prettyprinter import TreePrettyPrinter as TPP + + +class TreePrettyPrinter(Deprecated, TPP): + """Import `TreePrettyPrinter` using `from nltk.tree import TreePrettyPrinter` instead.""" + + +__all__ = ["TreePrettyPrinter"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/treetransforms.py b/.pythonlibs/lib/python3.10/site-packages/nltk/treetransforms.py new file mode 100644 index 0000000..d136061 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/treetransforms.py @@ -0,0 +1,126 @@ +# Natural Language Toolkit: Tree Transformations +# +# Copyright (C) 2005-2007 Oregon Graduate Institute +# Author: Nathan Bodenstab +# URL: +# For license information, see LICENSE.TXT + +r""" +A collection of methods for tree (grammar) transformations used +in parsing natural language. + +Although many of these methods are technically grammar transformations +(ie. Chomsky Norm Form), when working with treebanks it is much more +natural to visualize these modifications in a tree structure. Hence, +we will do all transformation directly to the tree itself. +Transforming the tree directly also allows us to do parent annotation. +A grammar can then be simply induced from the modified tree. + +The following is a short tutorial on the available transformations. + + 1. Chomsky Normal Form (binarization) + + It is well known that any grammar has a Chomsky Normal Form (CNF) + equivalent grammar where CNF is defined by every production having + either two non-terminals or one terminal on its right hand side. + When we have hierarchically structured data (ie. a treebank), it is + natural to view this in terms of productions where the root of every + subtree is the head (left hand side) of the production and all of + its children are the right hand side constituents. In order to + convert a tree into CNF, we simply need to ensure that every subtree + has either two subtrees as children (binarization), or one leaf node + (non-terminal). In order to binarize a subtree with more than two + children, we must introduce artificial nodes. + + There are two popular methods to convert a tree into CNF: left + factoring and right factoring. The following example demonstrates + the difference between them. Example:: + + Original Right-Factored Left-Factored + + A A A + / | \ / \ / \ + B C D ==> B A| OR A| D + / \ / \ + C D B C + + 2. Parent Annotation + + In addition to binarizing the tree, there are two standard + modifications to node labels we can do in the same traversal: parent + annotation and Markov order-N smoothing (or sibling smoothing). + + The purpose of parent annotation is to refine the probabilities of + productions by adding a small amount of context. With this simple + addition, a CYK (inside-outside, dynamic programming chart parse) + can improve from 74% to 79% accuracy. A natural generalization from + parent annotation is to grandparent annotation and beyond. The + tradeoff becomes accuracy gain vs. computational complexity. We + must also keep in mind data sparcity issues. Example:: + + Original Parent Annotation + + A A^ + / | \ / \ + B C D ==> B^ A|^ where ? is the + / \ parent of A + C^ D^ + + + 3. Markov order-N smoothing + + Markov smoothing combats data sparcity issues as well as decreasing + computational requirements by limiting the number of children + included in artificial nodes. In practice, most people use an order + 2 grammar. Example:: + + Original No Smoothing Markov order 1 Markov order 2 etc. + + __A__ A A A + / /|\ \ / \ / \ / \ + B C D E F ==> B A| ==> B A| ==> B A| + / \ / \ / \ + C ... C ... C ... + + + + Annotation decisions can be thought about in the vertical direction + (parent, grandparent, etc) and the horizontal direction (number of + siblings to keep). Parameters to the following functions specify + these values. For more information see: + + Dan Klein and Chris Manning (2003) "Accurate Unlexicalized + Parsing", ACL-03. https://www.aclweb.org/anthology/P03-1054 + + 4. Unary Collapsing + + Collapse unary productions (ie. subtrees with a single child) into a + new non-terminal (Tree node). This is useful when working with + algorithms that do not allow unary productions, yet you do not wish + to lose the parent information. Example:: + + A + | + B ==> A+B + / \ / \ + C D C D + +""" + +from nltk.internals import deprecated +from nltk.tree.transforms import chomsky_normal_form as cnf +from nltk.tree.transforms import collapse_unary as cu +from nltk.tree.transforms import un_chomsky_normal_form as ucnf + +chomsky_normal_form = deprecated( + "Import using `from nltk.tree import chomsky_normal_form` instead." +)(cnf) +un_chomsky_normal_form = deprecated( + "Import using `from nltk.tree import un_chomsky_normal_form` instead." +)(ucnf) +collapse_unary = deprecated( + "Import using `from nltk.tree import collapse_unary` instead." +)(cu) + + +__all__ = ["chomsky_normal_form", "un_chomsky_normal_form", "collapse_unary"] diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__init__.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__init__.py new file mode 100644 index 0000000..6a996ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__init__.py @@ -0,0 +1,35 @@ +# Natural Language Toolkit: Twitter +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# URL: +# For license information, see LICENSE.TXT + +""" +NLTK Twitter Package + +This package contains classes for retrieving Tweet documents using the +Twitter API. + +""" +try: + import twython +except ImportError: + import warnings + + warnings.warn( + "The twython library has not been installed. " + "Some functionality from the twitter package will not be available." + ) +else: + from nltk.twitter.util import Authenticate, credsfromfile + from nltk.twitter.twitterclient import ( + Streamer, + Query, + Twitter, + TweetViewer, + TweetWriter, + ) + + +from nltk.twitter.common import json2csv diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..49ca01e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..838848c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/common.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/common.cpython-310.pyc new file mode 100644 index 0000000..136e54a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/common.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitter_demo.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitter_demo.cpython-310.pyc new file mode 100644 index 0000000..b020999 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitter_demo.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitterclient.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitterclient.cpython-310.pyc new file mode 100644 index 0000000..d4a637f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/twitterclient.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..e21a4ad Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/api.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/api.py new file mode 100644 index 0000000..ad0b09d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/api.py @@ -0,0 +1,145 @@ +# Natural Language Toolkit: Twitter API +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + +""" +This module provides an interface for TweetHandlers, and support for timezone +handling. +""" + +import time as _time +from abc import ABCMeta, abstractmethod +from datetime import datetime, timedelta, timezone, tzinfo + + +class LocalTimezoneOffsetWithUTC(tzinfo): + """ + This is not intended to be a general purpose class for dealing with the + local timezone. In particular: + + * it assumes that the date passed has been created using + `datetime(..., tzinfo=Local)`, where `Local` is an instance of + the object `LocalTimezoneOffsetWithUTC`; + * for such an object, it returns the offset with UTC, used for date comparisons. + + Reference: https://docs.python.org/3/library/datetime.html + """ + + STDOFFSET = timedelta(seconds=-_time.timezone) + + if _time.daylight: + DSTOFFSET = timedelta(seconds=-_time.altzone) + else: + DSTOFFSET = STDOFFSET + + def utcoffset(self, dt): + """ + Access the relevant time offset. + """ + return self.DSTOFFSET + + +LOCAL = LocalTimezoneOffsetWithUTC() + + +class BasicTweetHandler(metaclass=ABCMeta): + """ + Minimal implementation of `TweetHandler`. + + Counts the number of Tweets and decides when the client should stop + fetching them. + """ + + def __init__(self, limit=20): + self.limit = limit + self.counter = 0 + + """ + A flag to indicate to the client whether to stop fetching data given + some condition (e.g., reaching a date limit). + """ + self.do_stop = False + + """ + Stores the id of the last fetched Tweet to handle pagination. + """ + self.max_id = None + + def do_continue(self): + """ + Returns `False` if the client should stop fetching Tweets. + """ + return self.counter < self.limit and not self.do_stop + + +class TweetHandlerI(BasicTweetHandler): + """ + Interface class whose subclasses should implement a handle method that + Twitter clients can delegate to. + """ + + def __init__(self, limit=20, upper_date_limit=None, lower_date_limit=None): + """ + :param int limit: The number of data items to process in the current\ + round of processing. + + :param tuple upper_date_limit: The date at which to stop collecting\ + new data. This should be entered as a tuple which can serve as the\ + argument to `datetime.datetime`.\ + E.g. `date_limit=(2015, 4, 1, 12, 40)` for 12:30 pm on April 1 2015. + + :param tuple lower_date_limit: The date at which to stop collecting\ + new data. See `upper_data_limit` for formatting. + """ + BasicTweetHandler.__init__(self, limit) + + self.upper_date_limit = None + self.lower_date_limit = None + if upper_date_limit: + self.upper_date_limit = datetime(*upper_date_limit, tzinfo=LOCAL) + if lower_date_limit: + self.lower_date_limit = datetime(*lower_date_limit, tzinfo=LOCAL) + + self.startingup = True + + @abstractmethod + def handle(self, data): + """ + Deal appropriately with data returned by the Twitter API + """ + + @abstractmethod + def on_finish(self): + """ + Actions when the tweet limit has been reached + """ + + def check_date_limit(self, data, verbose=False): + """ + Validate date limits. + """ + if self.upper_date_limit or self.lower_date_limit: + date_fmt = "%a %b %d %H:%M:%S +0000 %Y" + tweet_date = datetime.strptime(data["created_at"], date_fmt).replace( + tzinfo=timezone.utc + ) + if (self.upper_date_limit and tweet_date > self.upper_date_limit) or ( + self.lower_date_limit and tweet_date < self.lower_date_limit + ): + if self.upper_date_limit: + message = "earlier" + date_limit = self.upper_date_limit + else: + message = "later" + date_limit = self.lower_date_limit + if verbose: + print( + "Date limit {} is {} than date of current tweet {}".format( + date_limit, message, tweet_date + ) + ) + self.do_stop = True diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/common.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/common.py new file mode 100644 index 0000000..7440448 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/common.py @@ -0,0 +1,270 @@ +# Natural Language Toolkit: Twitter client +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + +""" +Utility functions for the `twitterclient` module which do not require +the `twython` library to have been installed. +""" +import csv +import gzip +import json + +from nltk.internals import deprecated + +HIER_SEPARATOR = "." + + +def extract_fields(tweet, fields): + """ + Extract field values from a full tweet and return them as a list + + :param json tweet: The tweet in JSON format + :param list fields: The fields to be extracted from the tweet + :rtype: list(str) + """ + out = [] + for field in fields: + try: + _add_field_to_out(tweet, field, out) + except TypeError as e: + raise RuntimeError( + "Fatal error when extracting fields. Cannot find field ", field + ) from e + return out + + +def _add_field_to_out(json, field, out): + if _is_composed_key(field): + key, value = _get_key_value_composed(field) + _add_field_to_out(json[key], value, out) + else: + out += [json[field]] + + +def _is_composed_key(field): + return HIER_SEPARATOR in field + + +def _get_key_value_composed(field): + out = field.split(HIER_SEPARATOR) + # there could be up to 3 levels + key = out[0] + value = HIER_SEPARATOR.join(out[1:]) + return key, value + + +def _get_entity_recursive(json, entity): + if not json: + return None + elif isinstance(json, dict): + for key, value in json.items(): + if key == entity: + return value + # 'entities' and 'extended_entities' are wrappers in Twitter json + # structure that contain other Twitter objects. See: + # https://dev.twitter.com/overview/api/entities-in-twitter-objects + + if key == "entities" or key == "extended_entities": + candidate = _get_entity_recursive(value, entity) + if candidate is not None: + return candidate + return None + elif isinstance(json, list): + for item in json: + candidate = _get_entity_recursive(item, entity) + if candidate is not None: + return candidate + return None + else: + return None + + +def json2csv( + fp, outfile, fields, encoding="utf8", errors="replace", gzip_compress=False +): + """ + Extract selected fields from a file of line-separated JSON tweets and + write to a file in CSV format. + + This utility function allows a file of full tweets to be easily converted + to a CSV file for easier processing. For example, just TweetIDs or + just the text content of the Tweets can be extracted. + + Additionally, the function allows combinations of fields of other Twitter + objects (mainly the users, see below). + + For Twitter entities (e.g. hashtags of a Tweet), and for geolocation, see + `json2csv_entities` + + :param str infile: The name of the file containing full tweets + + :param str outfile: The name of the text file where results should be\ + written + + :param list fields: The list of fields to be extracted. Useful examples\ + are 'id_str' for the tweetID and 'text' for the text of the tweet. See\ + for a full list of fields.\ + e. g.: ['id_str'], ['id', 'text', 'favorite_count', 'retweet_count']\ + Additionally, it allows IDs from other Twitter objects, e. g.,\ + ['id', 'text', 'user.id', 'user.followers_count', 'user.friends_count'] + + :param error: Behaviour for encoding errors, see\ + https://docs.python.org/3/library/codecs.html#codec-base-classes + + :param gzip_compress: if `True`, output files are compressed with gzip + """ + (writer, outf) = _outf_writer(outfile, encoding, errors, gzip_compress) + # write the list of fields as header + writer.writerow(fields) + # process the file + for line in fp: + tweet = json.loads(line) + row = extract_fields(tweet, fields) + writer.writerow(row) + outf.close() + + +@deprecated("Use open() and csv.writer() directly instead.") +def outf_writer_compat(outfile, encoding, errors, gzip_compress=False): + """Get a CSV writer with optional compression.""" + return _outf_writer(outfile, encoding, errors, gzip_compress) + + +def _outf_writer(outfile, encoding, errors, gzip_compress=False): + if gzip_compress: + outf = gzip.open(outfile, "wt", newline="", encoding=encoding, errors=errors) + else: + outf = open(outfile, "w", newline="", encoding=encoding, errors=errors) + writer = csv.writer(outf) + return (writer, outf) + + +def json2csv_entities( + tweets_file, + outfile, + main_fields, + entity_type, + entity_fields, + encoding="utf8", + errors="replace", + gzip_compress=False, +): + """ + Extract selected fields from a file of line-separated JSON tweets and + write to a file in CSV format. + + This utility function allows a file of full Tweets to be easily converted + to a CSV file for easier processing of Twitter entities. For example, the + hashtags or media elements of a tweet can be extracted. + + It returns one line per entity of a Tweet, e.g. if a tweet has two hashtags + there will be two lines in the output file, one per hashtag + + :param tweets_file: the file-like object containing full Tweets + + :param str outfile: The path of the text file where results should be\ + written + + :param list main_fields: The list of fields to be extracted from the main\ + object, usually the tweet. Useful examples: 'id_str' for the tweetID. See\ + for a full list of fields. + e. g.: ['id_str'], ['id', 'text', 'favorite_count', 'retweet_count'] + If `entity_type` is expressed with hierarchy, then it is the list of\ + fields of the object that corresponds to the key of the entity_type,\ + (e.g., for entity_type='user.urls', the fields in the main_fields list\ + belong to the user object; for entity_type='place.bounding_box', the\ + files in the main_field list belong to the place object of the tweet). + + :param list entity_type: The name of the entity: 'hashtags', 'media',\ + 'urls' and 'user_mentions' for the tweet object. For a user object,\ + this needs to be expressed with a hierarchy: `'user.urls'`. For the\ + bounding box of the Tweet location, use `'place.bounding_box'`. + + :param list entity_fields: The list of fields to be extracted from the\ + entity. E.g. `['text']` (of the Tweet) + + :param error: Behaviour for encoding errors, see\ + https://docs.python.org/3/library/codecs.html#codec-base-classes + + :param gzip_compress: if `True`, output files are compressed with gzip + """ + + (writer, outf) = _outf_writer(outfile, encoding, errors, gzip_compress) + header = get_header_field_list(main_fields, entity_type, entity_fields) + writer.writerow(header) + for line in tweets_file: + tweet = json.loads(line) + if _is_composed_key(entity_type): + key, value = _get_key_value_composed(entity_type) + object_json = _get_entity_recursive(tweet, key) + if not object_json: + # this can happen in the case of "place" + continue + object_fields = extract_fields(object_json, main_fields) + items = _get_entity_recursive(object_json, value) + _write_to_file(object_fields, items, entity_fields, writer) + else: + tweet_fields = extract_fields(tweet, main_fields) + items = _get_entity_recursive(tweet, entity_type) + _write_to_file(tweet_fields, items, entity_fields, writer) + outf.close() + + +def get_header_field_list(main_fields, entity_type, entity_fields): + if _is_composed_key(entity_type): + key, value = _get_key_value_composed(entity_type) + main_entity = key + sub_entity = value + else: + main_entity = None + sub_entity = entity_type + + if main_entity: + output1 = [HIER_SEPARATOR.join([main_entity, x]) for x in main_fields] + else: + output1 = main_fields + output2 = [HIER_SEPARATOR.join([sub_entity, x]) for x in entity_fields] + return output1 + output2 + + +def _write_to_file(object_fields, items, entity_fields, writer): + if not items: + # it could be that the entity is just not present for the tweet + # e.g. tweet hashtag is always present, even as [], however + # tweet media may not be present + return + if isinstance(items, dict): + # this happens e.g. for "place" of a tweet + row = object_fields + # there might be composed keys in de list of required fields + entity_field_values = [x for x in entity_fields if not _is_composed_key(x)] + entity_field_composed = [x for x in entity_fields if _is_composed_key(x)] + for field in entity_field_values: + value = items[field] + if isinstance(value, list): + row += value + else: + row += [value] + # now check required dictionaries + for d in entity_field_composed: + kd, vd = _get_key_value_composed(d) + json_dict = items[kd] + if not isinstance(json_dict, dict): + raise RuntimeError( + """Key {} does not contain a dictionary + in the json file""".format( + kd + ) + ) + row += [json_dict[vd]] + writer.writerow(row) + return + # in general it is a list + for item in items: + row = object_fields + extract_fields(item, entity_fields) + writer.writerow(row) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitter_demo.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitter_demo.py new file mode 100644 index 0000000..c74bfff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitter_demo.py @@ -0,0 +1,306 @@ +# Natural Language Toolkit: Twitter client +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + +""" +Examples to demo the :py:mod:`twitterclient` code. + +These demo functions should all run, with the following caveats: + +* You must have obtained API keys from Twitter, and installed them according to + the instructions in the `twitter HOWTO `_. + +* If you are on a slow network, some of the calls to the Twitter API may + timeout. + +* If you are being rate limited while searching, you will receive a 420 + error response. + +* Your terminal window / console must be able to display UTF-8 encoded characters. + +For documentation about the Twitter APIs, see `The Streaming APIs Overview +`_ and `The REST APIs Overview +`_. + +For error codes see Twitter's +`Error Codes and Responses ` +""" + +import datetime +import json +from functools import wraps +from io import StringIO + +from nltk.twitter import ( + Query, + Streamer, + TweetViewer, + TweetWriter, + Twitter, + credsfromfile, +) + +SPACER = "###################################" + + +def verbose(func): + """Decorator for demo functions""" + + @wraps(func) + def with_formatting(*args, **kwargs): + print() + print(SPACER) + print("Using %s" % (func.__name__)) + print(SPACER) + return func(*args, **kwargs) + + return with_formatting + + +def yesterday(): + """ + Get yesterday's datetime as a 5-tuple. + """ + date = datetime.datetime.now() + date -= datetime.timedelta(days=1) + date_tuple = date.timetuple()[:6] + return date_tuple + + +def setup(): + """ + Initialize global variables for the demos. + """ + global USERIDS, FIELDS + + USERIDS = ["759251", "612473", "15108702", "6017542", "2673523800"] + # UserIDs corresponding to\ + # @CNN, @BBCNews, @ReutersLive, @BreakingNews, @AJELive + FIELDS = ["id_str"] + + +@verbose +def twitterclass_demo(): + """ + Use the simplified :class:`Twitter` class to write some tweets to a file. + """ + tw = Twitter() + print("Track from the public stream\n") + tw.tweets(keywords="love, hate", limit=10) # public stream + print(SPACER) + print("Search past Tweets\n") + tw = Twitter() + tw.tweets(keywords="love, hate", stream=False, limit=10) # search past tweets + print(SPACER) + print( + "Follow two accounts in the public stream" + + " -- be prepared to wait a few minutes\n" + ) + tw = Twitter() + tw.tweets(follow=["759251", "6017542"], stream=True, limit=5) # public stream + + +@verbose +def sampletoscreen_demo(limit=20): + """ + Sample from the Streaming API and send output to terminal. + """ + oauth = credsfromfile() + client = Streamer(**oauth) + client.register(TweetViewer(limit=limit)) + client.sample() + + +@verbose +def tracktoscreen_demo(track="taylor swift", limit=10): + """ + Track keywords from the public Streaming API and send output to terminal. + """ + oauth = credsfromfile() + client = Streamer(**oauth) + client.register(TweetViewer(limit=limit)) + client.filter(track=track) + + +@verbose +def search_demo(keywords="nltk"): + """ + Use the REST API to search for past tweets containing a given keyword. + """ + oauth = credsfromfile() + client = Query(**oauth) + for tweet in client.search_tweets(keywords=keywords, limit=10): + print(tweet["text"]) + + +@verbose +def tweets_by_user_demo(user="NLTK_org", count=200): + """ + Use the REST API to search for past tweets by a given user. + """ + oauth = credsfromfile() + client = Query(**oauth) + client.register(TweetWriter()) + client.user_tweets(user, count) + + +@verbose +def lookup_by_userid_demo(): + """ + Use the REST API to convert a userID to a screen name. + """ + oauth = credsfromfile() + client = Query(**oauth) + user_info = client.user_info_from_id(USERIDS) + for info in user_info: + name = info["screen_name"] + followers = info["followers_count"] + following = info["friends_count"] + print(f"{name}, followers: {followers}, following: {following}") + + +@verbose +def followtoscreen_demo(limit=10): + """ + Using the Streaming API, select just the tweets from a specified list of + userIDs. + + This is will only give results in a reasonable time if the users in + question produce a high volume of tweets, and may even so show some delay. + """ + oauth = credsfromfile() + client = Streamer(**oauth) + client.register(TweetViewer(limit=limit)) + client.statuses.filter(follow=USERIDS) + + +@verbose +def streamtofile_demo(limit=20): + """ + Write 20 tweets sampled from the public Streaming API to a file. + """ + oauth = credsfromfile() + client = Streamer(**oauth) + client.register(TweetWriter(limit=limit, repeat=False)) + client.statuses.sample() + + +@verbose +def limit_by_time_demo(keywords="nltk"): + """ + Query the REST API for Tweets about NLTK since yesterday and send + the output to terminal. + + This example makes the assumption that there are sufficient Tweets since + yesterday for the date to be an effective cut-off. + """ + date = yesterday() + dt_date = datetime.datetime(*date) + oauth = credsfromfile() + client = Query(**oauth) + client.register(TweetViewer(limit=100, lower_date_limit=date)) + + print(f"Cutoff date: {dt_date}\n") + + for tweet in client.search_tweets(keywords=keywords): + print("{} ".format(tweet["created_at"]), end="") + client.handler.handle(tweet) + + +@verbose +def corpusreader_demo(): + """ + Use `TwitterCorpusReader` tp read a file of tweets, and print out + + * some full tweets in JSON format; + * some raw strings from the tweets (i.e., the value of the `text` field); and + * the result of tokenising the raw strings. + + """ + from nltk.corpus import twitter_samples as tweets + + print() + print("Complete tweet documents") + print(SPACER) + for tweet in tweets.docs("tweets.20150430-223406.json")[:1]: + print(json.dumps(tweet, indent=1, sort_keys=True)) + + print() + print("Raw tweet strings:") + print(SPACER) + for text in tweets.strings("tweets.20150430-223406.json")[:15]: + print(text) + + print() + print("Tokenized tweet strings:") + print(SPACER) + for toks in tweets.tokenized("tweets.20150430-223406.json")[:15]: + print(toks) + + +@verbose +def expand_tweetids_demo(): + """ + Given a file object containing a list of Tweet IDs, fetch the + corresponding full Tweets, if available. + + """ + ids_f = StringIO( + """\ + 588665495492124672 + 588665495487909888 + 588665495508766721 + 588665495513006080 + 588665495517200384 + 588665495487811584 + 588665495525588992 + 588665495487844352 + 588665495492014081 + 588665495512948737""" + ) + oauth = credsfromfile() + client = Query(**oauth) + hydrated = client.expand_tweetids(ids_f) + + for tweet in hydrated: + id_str = tweet["id_str"] + print(f"id: {id_str}") + text = tweet["text"] + if text.startswith("@null"): + text = "[Tweet not available]" + print(text + "\n") + + +ALL = [ + twitterclass_demo, + sampletoscreen_demo, + tracktoscreen_demo, + search_demo, + tweets_by_user_demo, + lookup_by_userid_demo, + followtoscreen_demo, + streamtofile_demo, + limit_by_time_demo, + corpusreader_demo, + expand_tweetids_demo, +] + +""" +Select demo functions to run. E.g. replace the following line with "DEMOS = +ALL[8:]" to execute only the final three demos. +""" +DEMOS = ALL[:] + +if __name__ == "__main__": + setup() + + for demo in DEMOS: + demo() + + print("\n" + SPACER) + print("All demos completed") + print(SPACER) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitterclient.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitterclient.py new file mode 100644 index 0000000..eb4d06b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/twitterclient.py @@ -0,0 +1,564 @@ +# Natural Language Toolkit: Twitter client +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + + +""" +NLTK Twitter client + +This module offers methods for collecting and processing Tweets. Most of the +functionality depends on access to the Twitter APIs, and this is handled via +the third party Twython library. + +If one of the methods below returns an integer, it is probably a `Twitter +error code `_. For +example, the response of '420' means that you have reached the limit of the +requests you can currently make to the Twitter API. Currently, `rate limits +for the search API `_ are +divided into 15 minute windows. +""" + +import datetime +import gzip +import itertools +import json +import os +import time + +import requests +from twython import Twython, TwythonStreamer +from twython.exceptions import TwythonError, TwythonRateLimitError + +from nltk.twitter.api import BasicTweetHandler, TweetHandlerI +from nltk.twitter.util import credsfromfile, guess_path + + +class Streamer(TwythonStreamer): + """ + Retrieve data from the Twitter Streaming API. + + The streaming API requires + `OAuth 1.0 `_ authentication. + """ + + def __init__(self, app_key, app_secret, oauth_token, oauth_token_secret): + + self.handler = None + self.do_continue = True + TwythonStreamer.__init__( + self, app_key, app_secret, oauth_token, oauth_token_secret + ) + + def register(self, handler): + """ + Register a method for handling Tweets. + + :param TweetHandlerI handler: method for viewing + """ + self.handler = handler + + def on_success(self, data): + """ + :param data: response from Twitter API + """ + if self.do_continue: + if self.handler is not None: + if "text" in data: + self.handler.counter += 1 + self.handler.handle(data) + self.do_continue = self.handler.do_continue() + else: + raise ValueError("No data handler has been registered.") + else: + self.disconnect() + self.handler.on_finish() + + def on_error(self, status_code, data): + """ + :param status_code: The status code returned by the Twitter API + :param data: The response from Twitter API + + """ + print(status_code) + + def sample(self): + """ + Wrapper for 'statuses / sample' API call + """ + while self.do_continue: + + # Stream in an endless loop until limit is reached. See twython + # issue 288: https://github.com/ryanmcgrath/twython/issues/288 + # colditzjb commented on 9 Dec 2014 + + try: + self.statuses.sample() + except requests.exceptions.ChunkedEncodingError as e: + if e is not None: + print(f"Error (stream will continue): {e}") + continue + + def filter(self, track="", follow="", lang="en"): + """ + Wrapper for 'statuses / filter' API call + """ + while self.do_continue: + # Stream in an endless loop until limit is reached + + try: + if track == "" and follow == "": + msg = "Please supply a value for 'track', 'follow'" + raise ValueError(msg) + self.statuses.filter(track=track, follow=follow, lang=lang) + except requests.exceptions.ChunkedEncodingError as e: + if e is not None: + print(f"Error (stream will continue): {e}") + continue + + +class Query(Twython): + """ + Retrieve data from the Twitter REST API. + """ + + def __init__(self, app_key, app_secret, oauth_token, oauth_token_secret): + """ + :param app_key: (optional) Your applications key + :param app_secret: (optional) Your applications secret key + :param oauth_token: (optional) When using **OAuth 1**, combined with + oauth_token_secret to make authenticated calls + :param oauth_token_secret: (optional) When using **OAuth 1** combined + with oauth_token to make authenticated calls + """ + self.handler = None + self.do_continue = True + Twython.__init__(self, app_key, app_secret, oauth_token, oauth_token_secret) + + def register(self, handler): + """ + Register a method for handling Tweets. + + :param TweetHandlerI handler: method for viewing or writing Tweets to a file. + """ + self.handler = handler + + def expand_tweetids(self, ids_f, verbose=True): + """ + Given a file object containing a list of Tweet IDs, fetch the + corresponding full Tweets from the Twitter API. + + The API call `statuses/lookup` will fail to retrieve a Tweet if the + user has deleted it. + + This call to the Twitter API is rate-limited. See + for details. + + :param ids_f: input file object consisting of Tweet IDs, one to a line + :return: iterable of Tweet objects in JSON format + """ + ids = [line.strip() for line in ids_f if line] + + if verbose: + print(f"Counted {len(ids)} Tweet IDs in {ids_f}.") + + # The Twitter endpoint takes lists of up to 100 ids, so we chunk the + # ids. + id_chunks = [ids[i : i + 100] for i in range(0, len(ids), 100)] + + chunked_tweets = (self.lookup_status(id=chunk) for chunk in id_chunks) + + return itertools.chain.from_iterable(chunked_tweets) + + def _search_tweets(self, keywords, limit=100, lang="en"): + """ + Assumes that the handler has been informed. Fetches Tweets from + search_tweets generator output and passses them to handler + + :param str keywords: A list of query terms to search for, written as\ + a comma-separated string. + :param int limit: Number of Tweets to process + :param str lang: language + """ + while True: + tweets = self.search_tweets( + keywords=keywords, limit=limit, lang=lang, max_id=self.handler.max_id + ) + for tweet in tweets: + self.handler.handle(tweet) + if not (self.handler.do_continue() and self.handler.repeat): + break + self.handler.on_finish() + + def search_tweets( + self, + keywords, + limit=100, + lang="en", + max_id=None, + retries_after_twython_exception=0, + ): + """ + Call the REST API ``'search/tweets'`` endpoint with some plausible + defaults. See `the Twitter search documentation + `_ for more information + about admissible search parameters. + + :param str keywords: A list of query terms to search for, written as\ + a comma-separated string + :param int limit: Number of Tweets to process + :param str lang: language + :param int max_id: id of the last tweet fetched + :param int retries_after_twython_exception: number of retries when\ + searching Tweets before raising an exception + :rtype: python generator + """ + if not self.handler: + # if no handler is provided, `BasicTweetHandler` provides minimum + # functionality for limiting the number of Tweets retrieved + self.handler = BasicTweetHandler(limit=limit) + + count_from_query = 0 + if max_id: + self.handler.max_id = max_id + else: + results = self.search( + q=keywords, count=min(100, limit), lang=lang, result_type="recent" + ) + count = len(results["statuses"]) + if count == 0: + print("No Tweets available through REST API for those keywords") + return + count_from_query = count + self.handler.max_id = results["statuses"][count - 1]["id"] - 1 + + for result in results["statuses"]: + yield result + self.handler.counter += 1 + if self.handler.do_continue() == False: + return + + # Pagination loop: keep fetching Tweets until the desired count is + # reached while dealing with Twitter rate limits. + retries = 0 + while count_from_query < limit: + try: + mcount = min(100, limit - count_from_query) + results = self.search( + q=keywords, + count=mcount, + lang=lang, + max_id=self.handler.max_id, + result_type="recent", + ) + except TwythonRateLimitError as e: + print(f"Waiting for 15 minutes -{e}") + time.sleep(15 * 60) # wait 15 minutes + continue + except TwythonError as e: + print(f"Fatal error in Twython request -{e}") + if retries_after_twython_exception == retries: + raise e + retries += 1 + + count = len(results["statuses"]) + if count == 0: + print("No more Tweets available through rest api") + return + count_from_query += count + # the max_id is also present in the Tweet metadata + # results['search_metadata']['next_results'], but as part of a + # query and difficult to fetch. This is doing the equivalent + # (last tweet id minus one) + self.handler.max_id = results["statuses"][count - 1]["id"] - 1 + + for result in results["statuses"]: + yield result + self.handler.counter += 1 + if self.handler.do_continue() == False: + return + + def user_info_from_id(self, userids): + """ + Convert a list of userIDs into a variety of information about the users. + + See . + + :param list userids: A list of integer strings corresponding to Twitter userIDs + :rtype: list(json) + """ + return [self.show_user(user_id=userid) for userid in userids] + + def user_tweets(self, screen_name, limit, include_rts="false"): + """ + Return a collection of the most recent Tweets posted by the user + + :param str user: The user's screen name; the initial '@' symbol\ + should be omitted + :param int limit: The number of Tweets to recover; 200 is the maximum allowed + :param str include_rts: Whether to include statuses which have been\ + retweeted by the user; possible values are 'true' and 'false' + """ + data = self.get_user_timeline( + screen_name=screen_name, count=limit, include_rts=include_rts + ) + for item in data: + self.handler.handle(item) + + +class Twitter: + """ + Wrapper class with restricted functionality and fewer options. + """ + + def __init__(self): + self._oauth = credsfromfile() + self.streamer = Streamer(**self._oauth) + self.query = Query(**self._oauth) + + def tweets( + self, + keywords="", + follow="", + to_screen=True, + stream=True, + limit=100, + date_limit=None, + lang="en", + repeat=False, + gzip_compress=False, + ): + """ + Process some Tweets in a simple manner. + + :param str keywords: Keywords to use for searching or filtering + :param list follow: UserIDs to use for filtering Tweets from the public stream + :param bool to_screen: If `True`, display the tweet texts on the screen,\ + otherwise print to a file + + :param bool stream: If `True`, use the live public stream,\ + otherwise search past public Tweets + + :param int limit: The number of data items to process in the current\ + round of processing. + + :param tuple date_limit: The date at which to stop collecting\ + new data. This should be entered as a tuple which can serve as the\ + argument to `datetime.datetime`.\ + E.g. `date_limit=(2015, 4, 1, 12, 40)` for 12:30 pm on April 1 2015. + Note that, in the case of streaming, this is the maximum date, i.e.\ + a date in the future; if not, it is the minimum date, i.e. a date\ + in the past + + :param str lang: language + + :param bool repeat: A flag to determine whether multiple files should\ + be written. If `True`, the length of each file will be set by the\ + value of `limit`. Use only if `to_screen` is `False`. See also + :py:func:`handle`. + + :param gzip_compress: if `True`, output files are compressed with gzip. + """ + if stream: + upper_date_limit = date_limit + lower_date_limit = None + else: + upper_date_limit = None + lower_date_limit = date_limit + + if to_screen: + handler = TweetViewer( + limit=limit, + upper_date_limit=upper_date_limit, + lower_date_limit=lower_date_limit, + ) + else: + handler = TweetWriter( + limit=limit, + upper_date_limit=upper_date_limit, + lower_date_limit=lower_date_limit, + repeat=repeat, + gzip_compress=gzip_compress, + ) + + if to_screen: + handler = TweetViewer(limit=limit) + else: + if stream: + upper_date_limit = date_limit + lower_date_limit = None + else: + upper_date_limit = None + lower_date_limit = date_limit + + handler = TweetWriter( + limit=limit, + upper_date_limit=upper_date_limit, + lower_date_limit=lower_date_limit, + repeat=repeat, + gzip_compress=gzip_compress, + ) + + if stream: + self.streamer.register(handler) + if keywords == "" and follow == "": + self.streamer.sample() + else: + self.streamer.filter(track=keywords, follow=follow, lang=lang) + else: + self.query.register(handler) + if keywords == "": + raise ValueError("Please supply at least one keyword to search for.") + else: + self.query._search_tweets(keywords, limit=limit, lang=lang) + + +class TweetViewer(TweetHandlerI): + """ + Handle data by sending it to the terminal. + """ + + def handle(self, data): + """ + Direct data to `sys.stdout` + + :return: return ``False`` if processing should cease, otherwise return ``True``. + :rtype: bool + :param data: Tweet object returned by Twitter API + """ + text = data["text"] + print(text) + + self.check_date_limit(data) + if self.do_stop: + return + + def on_finish(self): + print(f"Written {self.counter} Tweets") + + +class TweetWriter(TweetHandlerI): + """ + Handle data by writing it to a file. + """ + + def __init__( + self, + limit=2000, + upper_date_limit=None, + lower_date_limit=None, + fprefix="tweets", + subdir="twitter-files", + repeat=False, + gzip_compress=False, + ): + """ + The difference between the upper and lower date limits depends on + whether Tweets are coming in an ascending date order (i.e. when + streaming) or descending date order (i.e. when searching past Tweets). + + :param int limit: number of data items to process in the current\ + round of processing. + + :param tuple upper_date_limit: The date at which to stop collecting new\ + data. This should be entered as a tuple which can serve as the\ + argument to `datetime.datetime`. E.g. `upper_date_limit=(2015, 4, 1, 12,\ + 40)` for 12:30 pm on April 1 2015. + + :param tuple lower_date_limit: The date at which to stop collecting new\ + data. See `upper_data_limit` for formatting. + + :param str fprefix: The prefix to use in creating file names for Tweet\ + collections. + + :param str subdir: The name of the directory where Tweet collection\ + files should be stored. + + :param bool repeat: flag to determine whether multiple files should be\ + written. If `True`, the length of each file will be set by the value\ + of `limit`. See also :py:func:`handle`. + + :param gzip_compress: if `True`, output files are compressed with gzip. + """ + self.fprefix = fprefix + self.subdir = guess_path(subdir) + self.gzip_compress = gzip_compress + self.fname = self.timestamped_file() + self.repeat = repeat + self.output = None + TweetHandlerI.__init__(self, limit, upper_date_limit, lower_date_limit) + + def timestamped_file(self): + """ + :return: timestamped file name + :rtype: str + """ + subdir = self.subdir + fprefix = self.fprefix + if subdir: + if not os.path.exists(subdir): + os.mkdir(subdir) + + fname = os.path.join(subdir, fprefix) + fmt = "%Y%m%d-%H%M%S" + timestamp = datetime.datetime.now().strftime(fmt) + if self.gzip_compress: + suffix = ".gz" + else: + suffix = "" + outfile = f"{fname}.{timestamp}.json{suffix}" + return outfile + + def handle(self, data): + """ + Write Twitter data as line-delimited JSON into one or more files. + + :return: return `False` if processing should cease, otherwise return `True`. + :param data: tweet object returned by Twitter API + """ + if self.startingup: + if self.gzip_compress: + self.output = gzip.open(self.fname, "w") + else: + self.output = open(self.fname, "w") + print(f"Writing to {self.fname}") + + json_data = json.dumps(data) + if self.gzip_compress: + self.output.write((json_data + "\n").encode("utf-8")) + else: + self.output.write(json_data + "\n") + + self.check_date_limit(data) + if self.do_stop: + return + + self.startingup = False + + def on_finish(self): + print(f"Written {self.counter} Tweets") + if self.output: + self.output.close() + + def do_continue(self): + if self.repeat == False: + return TweetHandlerI.do_continue(self) + + if self.do_stop: + # stop for a functional cause (e.g. date limit) + return False + + if self.counter == self.limit: + # repeat is True, thus close output file and + # create a new one + self._restart_file() + return True + + def _restart_file(self): + self.on_finish() + self.fname = self.timestamped_file() + self.startingup = True + self.counter = 0 diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/util.py new file mode 100644 index 0000000..f6cc35b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/twitter/util.py @@ -0,0 +1,147 @@ +# Natural Language Toolkit: Twitter client +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Ewan Klein +# Lorenzo Rubio +# URL: +# For license information, see LICENSE.TXT + +""" +Authentication utilities to accompany `twitterclient`. +""" + +import os +import pprint + +from twython import Twython + + +def credsfromfile(creds_file=None, subdir=None, verbose=False): + """ + Convenience function for authentication + """ + return Authenticate().load_creds( + creds_file=creds_file, subdir=subdir, verbose=verbose + ) + + +class Authenticate: + """ + Methods for authenticating with Twitter. + """ + + def __init__(self): + self.creds_file = "credentials.txt" + self.creds_fullpath = None + + self.oauth = {} + try: + self.twitter_dir = os.environ["TWITTER"] + self.creds_subdir = self.twitter_dir + except KeyError: + self.twitter_dir = None + self.creds_subdir = None + + def load_creds(self, creds_file=None, subdir=None, verbose=False): + """ + Read OAuth credentials from a text file. + + File format for OAuth 1:: + + app_key=YOUR_APP_KEY + app_secret=YOUR_APP_SECRET + oauth_token=OAUTH_TOKEN + oauth_token_secret=OAUTH_TOKEN_SECRET + + + File format for OAuth 2:: + + app_key=YOUR_APP_KEY + app_secret=YOUR_APP_SECRET + access_token=ACCESS_TOKEN + + :param str file_name: File containing credentials. ``None`` (default) reads + data from `TWITTER/'credentials.txt'` + """ + if creds_file is not None: + self.creds_file = creds_file + + if subdir is None: + if self.creds_subdir is None: + msg = ( + "Supply a value to the 'subdir' parameter or" + + " set the TWITTER environment variable." + ) + raise ValueError(msg) + else: + self.creds_subdir = subdir + + self.creds_fullpath = os.path.normpath( + os.path.join(self.creds_subdir, self.creds_file) + ) + + if not os.path.isfile(self.creds_fullpath): + raise OSError(f"Cannot find file {self.creds_fullpath}") + + with open(self.creds_fullpath) as infile: + if verbose: + print(f"Reading credentials file {self.creds_fullpath}") + + for line in infile: + if "=" in line: + name, value = line.split("=", 1) + self.oauth[name.strip()] = value.strip() + + self._validate_creds_file(verbose=verbose) + + return self.oauth + + def _validate_creds_file(self, verbose=False): + """Check validity of a credentials file.""" + oauth1 = False + oauth1_keys = ["app_key", "app_secret", "oauth_token", "oauth_token_secret"] + oauth2 = False + oauth2_keys = ["app_key", "app_secret", "access_token"] + if all(k in self.oauth for k in oauth1_keys): + oauth1 = True + elif all(k in self.oauth for k in oauth2_keys): + oauth2 = True + + if not (oauth1 or oauth2): + msg = f"Missing or incorrect entries in {self.creds_file}\n" + msg += pprint.pformat(self.oauth) + raise ValueError(msg) + elif verbose: + print(f'Credentials file "{self.creds_file}" looks good') + + +def add_access_token(creds_file=None): + """ + For OAuth 2, retrieve an access token for an app and append it to a + credentials file. + """ + if creds_file is None: + path = os.path.dirname(__file__) + creds_file = os.path.join(path, "credentials2.txt") + oauth2 = credsfromfile(creds_file=creds_file) + app_key = oauth2["app_key"] + app_secret = oauth2["app_secret"] + + twitter = Twython(app_key, app_secret, oauth_version=2) + access_token = twitter.obtain_access_token() + tok = f"access_token={access_token}\n" + with open(creds_file, "a") as infile: + print(tok, file=infile) + + +def guess_path(pth): + """ + If the path is not absolute, guess that it is a subdirectory of the + user's home directory. + + :param str pth: The pathname of the directory where files of tweets should be written + """ + if os.path.isabs(pth): + return pth + else: + return os.path.expanduser(os.path.join("~", pth)) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/util.py b/.pythonlibs/lib/python3.10/site-packages/nltk/util.py new file mode 100644 index 0000000..b8e83e0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/util.py @@ -0,0 +1,1216 @@ +# Natural Language Toolkit: Utility functions +# +# Copyright (C) 2001-2023 NLTK Project +# Author: Steven Bird +# Eric Kafe (acyclic closures) +# URL: +# For license information, see LICENSE.TXT + +import inspect +import locale +import os +import pydoc +import re +import textwrap +import warnings +from collections import defaultdict, deque +from itertools import chain, combinations, islice, tee +from pprint import pprint +from urllib.request import ( + HTTPPasswordMgrWithDefaultRealm, + ProxyBasicAuthHandler, + ProxyDigestAuthHandler, + ProxyHandler, + build_opener, + getproxies, + install_opener, +) + +from nltk.collections import * +from nltk.internals import deprecated, raise_unorderable_types, slice_bounds + +###################################################################### +# Short usage message +###################################################################### + + +@deprecated("Use help(obj) instead.") +def usage(obj): + str(obj) # In case it's lazy, this will load it. + + if not isinstance(obj, type): + obj = obj.__class__ + + print(f"{obj.__name__} supports the following operations:") + for (name, method) in sorted(pydoc.allmethods(obj).items()): + if name.startswith("_"): + continue + if getattr(method, "__deprecated__", False): + continue + + try: + sig = str(inspect.signature(method)) + except ValueError as e: + # builtins sometimes don't support introspection + if "builtin" in str(e): + continue + else: + raise + + args = sig.lstrip("(").rstrip(")").split(", ") + meth = inspect.getattr_static(obj, name) + if isinstance(meth, (classmethod, staticmethod)): + name = f"cls.{name}" + elif args and args[0] == "self": + name = f"self.{name}" + args.pop(0) + print( + textwrap.fill( + f"{name}({', '.join(args)})", + initial_indent=" - ", + subsequent_indent=" " * (len(name) + 5), + ) + ) + + +########################################################################## +# IDLE +########################################################################## + + +def in_idle(): + """ + Return True if this function is run within idle. Tkinter + programs that are run in idle should never call ``Tk.mainloop``; so + this function should be used to gate all calls to ``Tk.mainloop``. + + :warning: This function works by checking ``sys.stdin``. If the + user has modified ``sys.stdin``, then it may return incorrect + results. + :rtype: bool + """ + import sys + + return sys.stdin.__class__.__name__ in ("PyShell", "RPCProxy") + + +########################################################################## +# PRETTY PRINTING +########################################################################## + + +def pr(data, start=0, end=None): + """ + Pretty print a sequence of data items + + :param data: the data stream to print + :type data: sequence or iter + :param start: the start position + :type start: int + :param end: the end position + :type end: int + """ + pprint(list(islice(data, start, end))) + + +def print_string(s, width=70): + """ + Pretty print a string, breaking lines on whitespace + + :param s: the string to print, consisting of words and spaces + :type s: str + :param width: the display width + :type width: int + """ + print("\n".join(textwrap.wrap(s, width=width))) + + +def tokenwrap(tokens, separator=" ", width=70): + """ + Pretty print a list of text tokens, breaking lines on whitespace + + :param tokens: the tokens to print + :type tokens: list + :param separator: the string to use to separate tokens + :type separator: str + :param width: the display width (default=70) + :type width: int + """ + return "\n".join(textwrap.wrap(separator.join(tokens), width=width)) + + +########################################################################## +# Indexing +########################################################################## + + +class Index(defaultdict): + def __init__(self, pairs): + defaultdict.__init__(self, list) + for key, value in pairs: + self[key].append(value) + + +###################################################################### +## Regexp display (thanks to David Mertz) +###################################################################### + + +def re_show(regexp, string, left="{", right="}"): + """ + Return a string with markers surrounding the matched substrings. + Search str for substrings matching ``regexp`` and wrap the matches + with braces. This is convenient for learning about regular expressions. + + :param regexp: The regular expression. + :type regexp: str + :param string: The string being matched. + :type string: str + :param left: The left delimiter (printed before the matched substring) + :type left: str + :param right: The right delimiter (printed after the matched substring) + :type right: str + :rtype: str + """ + print(re.compile(regexp, re.M).sub(left + r"\g<0>" + right, string.rstrip())) + + +########################################################################## +# READ FROM FILE OR STRING +########################################################################## + +# recipe from David Mertz +def filestring(f): + if hasattr(f, "read"): + return f.read() + elif isinstance(f, str): + with open(f) as infile: + return infile.read() + else: + raise ValueError("Must be called with a filename or file-like object") + + +########################################################################## +# Breadth-First Search +########################################################################## + + +def breadth_first(tree, children=iter, maxdepth=-1): + """Traverse the nodes of a tree in breadth-first order. + (No check for cycles.) + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + """ + queue = deque([(tree, 0)]) + + while queue: + node, depth = queue.popleft() + yield node + + if depth != maxdepth: + try: + queue.extend((c, depth + 1) for c in children(node)) + except TypeError: + pass + + +########################################################################## +# Graph Drawing +########################################################################## + + +def edge_closure(tree, children=iter, maxdepth=-1, verbose=False): + """Yield the edges of a graph in breadth-first order, + discarding eventual cycles. + The first argument should be the start node; + children should be a function taking as argument a graph node + and returning an iterator of the node's children. + + >>> from nltk.util import edge_closure + >>> print(list(edge_closure('A', lambda node:{'A':['B','C'], 'B':'C', 'C':'B'}[node]))) + [('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'B')] + """ + traversed = set() + edges = set() + queue = deque([(tree, 0)]) + while queue: + node, depth = queue.popleft() + traversed.add(node) + if depth != maxdepth: + try: + for child in children(node): + if child not in traversed: + queue.append((child, depth + 1)) + else: + if verbose: + warnings.warn( + f"Discarded redundant search for {child} at depth {depth + 1}", + stacklevel=2, + ) + edge = (node, child) + if edge not in edges: + yield edge + edges.add(edge) + except TypeError: + pass + + +def edges2dot(edges, shapes=None, attr=None): + """ + :param edges: the set (or list) of edges of a directed graph. + + :return dot_string: a representation of 'edges' as a string in the DOT + graph language, which can be converted to an image by the 'dot' program + from the Graphviz package, or nltk.parse.dependencygraph.dot2img(dot_string). + + :param shapes: dictionary of strings that trigger a specified shape. + :param attr: dictionary with global graph attributes + + >>> import nltk + >>> from nltk.util import edges2dot + >>> print(edges2dot([('A', 'B'), ('A', 'C'), ('B', 'C'), ('C', 'B')])) + digraph G { + "A" -> "B"; + "A" -> "C"; + "B" -> "C"; + "C" -> "B"; + } + + """ + if not shapes: + shapes = dict() + if not attr: + attr = dict() + + dot_string = "digraph G {\n" + + for pair in attr.items(): + dot_string += f"{pair[0]} = {pair[1]};\n" + + for edge in edges: + for shape in shapes.items(): + for node in range(2): + if shape[0] in repr(edge[node]): + dot_string += f'"{edge[node]}" [shape = {shape[1]}];\n' + dot_string += f'"{edge[0]}" -> "{edge[1]}";\n' + + dot_string += "}\n" + return dot_string + + +def unweighted_minimum_spanning_digraph(tree, children=iter, shapes=None, attr=None): + """ + + Build a Minimum Spanning Tree (MST) of an unweighted graph, + by traversing the nodes of a tree in breadth-first order, + discarding eventual cycles. + + Return a representation of this MST as a string in the DOT graph language, + which can be converted to an image by the 'dot' program from the Graphviz + package, or nltk.parse.dependencygraph.dot2img(dot_string). + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + + >>> import nltk + >>> wn=nltk.corpus.wordnet + >>> from nltk.util import unweighted_minimum_spanning_digraph as umsd + >>> print(umsd(wn.synset('bound.a.01'), lambda s:s.also_sees())) + digraph G { + "Synset('bound.a.01')" -> "Synset('unfree.a.02')"; + "Synset('unfree.a.02')" -> "Synset('confined.a.02')"; + "Synset('unfree.a.02')" -> "Synset('dependent.a.01')"; + "Synset('unfree.a.02')" -> "Synset('restricted.a.01')"; + "Synset('restricted.a.01')" -> "Synset('classified.a.02')"; + } + + """ + return edges2dot( + edge_closure( + tree, lambda node: unweighted_minimum_spanning_dict(tree, children)[node] + ), + shapes, + attr, + ) + + +########################################################################## +# Breadth-First / Depth-first Searches with Cycle Detection +########################################################################## + + +def acyclic_breadth_first(tree, children=iter, maxdepth=-1): + """Traverse the nodes of a tree in breadth-first order, + discarding eventual cycles. + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + """ + traversed = set() + queue = deque([(tree, 0)]) + while queue: + node, depth = queue.popleft() + yield node + traversed.add(node) + if depth != maxdepth: + try: + for child in children(node): + if child not in traversed: + queue.append((child, depth + 1)) + else: + warnings.warn( + "Discarded redundant search for {} at depth {}".format( + child, depth + 1 + ), + stacklevel=2, + ) + except TypeError: + pass + + +def acyclic_depth_first(tree, children=iter, depth=-1, cut_mark=None, traversed=None): + """Traverse the nodes of a tree in depth-first order, + discarding eventual cycles within any branch, + adding cut_mark (when specified) if cycles were truncated. + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + + Catches all cycles: + + >>> import nltk + >>> from nltk.util import acyclic_depth_first as acyclic_tree + >>> wn=nltk.corpus.wordnet + >>> from pprint import pprint + >>> pprint(acyclic_tree(wn.synset('dog.n.01'), lambda s:s.hypernyms(),cut_mark='...')) + [Synset('dog.n.01'), + [Synset('canine.n.02'), + [Synset('carnivore.n.01'), + [Synset('placental.n.01'), + [Synset('mammal.n.01'), + [Synset('vertebrate.n.01'), + [Synset('chordate.n.01'), + [Synset('animal.n.01'), + [Synset('organism.n.01'), + [Synset('living_thing.n.01'), + [Synset('whole.n.02'), + [Synset('object.n.01'), + [Synset('physical_entity.n.01'), + [Synset('entity.n.01')]]]]]]]]]]]]], + [Synset('domestic_animal.n.01'), "Cycle(Synset('animal.n.01'),-3,...)"]] + """ + if traversed is None: + traversed = {tree} + out_tree = [tree] + if depth != 0: + try: + for child in children(tree): + if child not in traversed: + # Recurse with a common "traversed" set for all children: + traversed.add(child) + out_tree += [ + acyclic_depth_first( + child, children, depth - 1, cut_mark, traversed + ) + ] + else: + warnings.warn( + "Discarded redundant search for {} at depth {}".format( + child, depth - 1 + ), + stacklevel=3, + ) + if cut_mark: + out_tree += [f"Cycle({child},{depth - 1},{cut_mark})"] + except TypeError: + pass + elif cut_mark: + out_tree += [cut_mark] + return out_tree + + +def acyclic_branches_depth_first( + tree, children=iter, depth=-1, cut_mark=None, traversed=None +): + """Traverse the nodes of a tree in depth-first order, + discarding eventual cycles within the same branch, + but keep duplicate paths in different branches. + Add cut_mark (when defined) if cycles were truncated. + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + + Catches only only cycles within the same branch, + but keeping cycles from different branches: + + >>> import nltk + >>> from nltk.util import acyclic_branches_depth_first as tree + >>> wn=nltk.corpus.wordnet + >>> from pprint import pprint + >>> pprint(tree(wn.synset('certified.a.01'), lambda s:s.also_sees(), cut_mark='...', depth=4)) + [Synset('certified.a.01'), + [Synset('authorized.a.01'), + [Synset('lawful.a.01'), + [Synset('legal.a.01'), + "Cycle(Synset('lawful.a.01'),0,...)", + [Synset('legitimate.a.01'), '...']], + [Synset('straight.a.06'), + [Synset('honest.a.01'), '...'], + "Cycle(Synset('lawful.a.01'),0,...)"]], + [Synset('legitimate.a.01'), + "Cycle(Synset('authorized.a.01'),1,...)", + [Synset('legal.a.01'), + [Synset('lawful.a.01'), '...'], + "Cycle(Synset('legitimate.a.01'),0,...)"], + [Synset('valid.a.01'), + "Cycle(Synset('legitimate.a.01'),0,...)", + [Synset('reasonable.a.01'), '...']]], + [Synset('official.a.01'), "Cycle(Synset('authorized.a.01'),1,...)"]], + [Synset('documented.a.01')]] + """ + if traversed is None: + traversed = {tree} + out_tree = [tree] + if depth != 0: + try: + for child in children(tree): + if child not in traversed: + # Recurse with a different "traversed" set for each child: + out_tree += [ + acyclic_branches_depth_first( + child, + children, + depth - 1, + cut_mark, + traversed.union({child}), + ) + ] + else: + warnings.warn( + "Discarded redundant search for {} at depth {}".format( + child, depth - 1 + ), + stacklevel=3, + ) + if cut_mark: + out_tree += [f"Cycle({child},{depth - 1},{cut_mark})"] + except TypeError: + pass + elif cut_mark: + out_tree += [cut_mark] + return out_tree + + +def acyclic_dic2tree(node, dic): + """Convert acyclic dictionary 'dic', where the keys are nodes, and the + values are lists of children, to output tree suitable for pprint(), + starting at root 'node', with subtrees as nested lists.""" + return [node] + [acyclic_dic2tree(child, dic) for child in dic[node]] + + +def unweighted_minimum_spanning_dict(tree, children=iter): + """ + Output a dictionary representing a Minimum Spanning Tree (MST) + of an unweighted graph, by traversing the nodes of a tree in + breadth-first order, discarding eventual cycles. + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + + >>> import nltk + >>> from nltk.corpus import wordnet as wn + >>> from nltk.util import unweighted_minimum_spanning_dict as umsd + >>> from pprint import pprint + >>> pprint(umsd(wn.synset('bound.a.01'), lambda s:s.also_sees())) + {Synset('bound.a.01'): [Synset('unfree.a.02')], + Synset('classified.a.02'): [], + Synset('confined.a.02'): [], + Synset('dependent.a.01'): [], + Synset('restricted.a.01'): [Synset('classified.a.02')], + Synset('unfree.a.02'): [Synset('confined.a.02'), + Synset('dependent.a.01'), + Synset('restricted.a.01')]} + + """ + traversed = set() # Empty set of traversed nodes + queue = deque([tree]) # Initialize queue + agenda = {tree} # Set of all nodes ever queued + mstdic = {} # Empty MST dictionary + while queue: + node = queue.popleft() # Node is not yet in the MST dictionary, + mstdic[node] = [] # so add it with an empty list of children + if node not in traversed: # Avoid cycles + traversed.add(node) + for child in children(node): + if child not in agenda: # Queue nodes only once + mstdic[node].append(child) # Add child to the MST + queue.append(child) # Add child to queue + agenda.add(child) + return mstdic + + +def unweighted_minimum_spanning_tree(tree, children=iter): + """ + Output a Minimum Spanning Tree (MST) of an unweighted graph, + by traversing the nodes of a tree in breadth-first order, + discarding eventual cycles. + + The first argument should be the tree root; + children should be a function taking as argument a tree node + and returning an iterator of the node's children. + + >>> import nltk + >>> from nltk.util import unweighted_minimum_spanning_tree as mst + >>> wn=nltk.corpus.wordnet + >>> from pprint import pprint + >>> pprint(mst(wn.synset('bound.a.01'), lambda s:s.also_sees())) + [Synset('bound.a.01'), + [Synset('unfree.a.02'), + [Synset('confined.a.02')], + [Synset('dependent.a.01')], + [Synset('restricted.a.01'), [Synset('classified.a.02')]]]] + """ + return acyclic_dic2tree(tree, unweighted_minimum_spanning_dict(tree, children)) + + +########################################################################## +# Guess Character Encoding +########################################################################## + +# adapted from io.py in the docutils extension module (https://docutils.sourceforge.io/) +# http://www.pyzine.com/Issue008/Section_Articles/article_Encodings.html + + +def guess_encoding(data): + """ + Given a byte string, attempt to decode it. + Tries the standard 'UTF8' and 'latin-1' encodings, + Plus several gathered from locale information. + + The calling program *must* first call:: + + locale.setlocale(locale.LC_ALL, '') + + If successful it returns ``(decoded_unicode, successful_encoding)``. + If unsuccessful it raises a ``UnicodeError``. + """ + successful_encoding = None + # we make 'utf-8' the first encoding + encodings = ["utf-8"] + # + # next we add anything we can learn from the locale + try: + encodings.append(locale.nl_langinfo(locale.CODESET)) + except AttributeError: + pass + try: + encodings.append(locale.getlocale()[1]) + except (AttributeError, IndexError): + pass + try: + encodings.append(locale.getdefaultlocale()[1]) + except (AttributeError, IndexError): + pass + # + # we try 'latin-1' last + encodings.append("latin-1") + for enc in encodings: + # some of the locale calls + # may have returned None + if not enc: + continue + try: + decoded = str(data, enc) + successful_encoding = enc + + except (UnicodeError, LookupError): + pass + else: + break + if not successful_encoding: + raise UnicodeError( + "Unable to decode input data. " + "Tried the following encodings: %s." + % ", ".join([repr(enc) for enc in encodings if enc]) + ) + else: + return (decoded, successful_encoding) + + +########################################################################## +# Remove repeated elements from a list deterministcally +########################################################################## + + +def unique_list(xs): + seen = set() + # not seen.add(x) here acts to make the code shorter without using if statements, seen.add(x) always returns None. + return [x for x in xs if x not in seen and not seen.add(x)] + + +########################################################################## +# Invert a dictionary +########################################################################## + + +def invert_dict(d): + inverted_dict = defaultdict(list) + for key in d: + if hasattr(d[key], "__iter__"): + for term in d[key]: + inverted_dict[term].append(key) + else: + inverted_dict[d[key]] = key + return inverted_dict + + +########################################################################## +# Utilities for directed graphs: transitive closure, and inversion +# The graph is represented as a dictionary of sets +########################################################################## + + +def transitive_closure(graph, reflexive=False): + """ + Calculate the transitive closure of a directed graph, + optionally the reflexive transitive closure. + + The algorithm is a slight modification of the "Marking Algorithm" of + Ioannidis & Ramakrishnan (1998) "Efficient Transitive Closure Algorithms". + + :param graph: the initial graph, represented as a dictionary of sets + :type graph: dict(set) + :param reflexive: if set, also make the closure reflexive + :type reflexive: bool + :rtype: dict(set) + """ + if reflexive: + base_set = lambda k: {k} + else: + base_set = lambda k: set() + # The graph U_i in the article: + agenda_graph = {k: graph[k].copy() for k in graph} + # The graph M_i in the article: + closure_graph = {k: base_set(k) for k in graph} + for i in graph: + agenda = agenda_graph[i] + closure = closure_graph[i] + while agenda: + j = agenda.pop() + closure.add(j) + closure |= closure_graph.setdefault(j, base_set(j)) + agenda |= agenda_graph.get(j, base_set(j)) + agenda -= closure + return closure_graph + + +def invert_graph(graph): + """ + Inverts a directed graph. + + :param graph: the graph, represented as a dictionary of sets + :type graph: dict(set) + :return: the inverted graph + :rtype: dict(set) + """ + inverted = {} + for key in graph: + for value in graph[key]: + inverted.setdefault(value, set()).add(key) + return inverted + + +########################################################################## +# HTML Cleaning +########################################################################## + + +def clean_html(html): + raise NotImplementedError( + "To remove HTML markup, use BeautifulSoup's get_text() function" + ) + + +def clean_url(url): + raise NotImplementedError( + "To remove HTML markup, use BeautifulSoup's get_text() function" + ) + + +########################################################################## +# FLATTEN LISTS +########################################################################## + + +def flatten(*args): + """ + Flatten a list. + + >>> from nltk.util import flatten + >>> flatten(1, 2, ['b', 'a' , ['c', 'd']], 3) + [1, 2, 'b', 'a', 'c', 'd', 3] + + :param args: items and lists to be combined into a single list + :rtype: list + """ + + x = [] + for l in args: + if not isinstance(l, (list, tuple)): + l = [l] + for item in l: + if isinstance(item, (list, tuple)): + x.extend(flatten(item)) + else: + x.append(item) + return x + + +########################################################################## +# Ngram iteration +########################################################################## + + +def pad_sequence( + sequence, + n, + pad_left=False, + pad_right=False, + left_pad_symbol=None, + right_pad_symbol=None, +): + """ + Returns a padded sequence of items before ngram extraction. + + >>> list(pad_sequence([1,2,3,4,5], 2, pad_left=True, pad_right=True, left_pad_symbol='', right_pad_symbol='')) + ['', 1, 2, 3, 4, 5, ''] + >>> list(pad_sequence([1,2,3,4,5], 2, pad_left=True, left_pad_symbol='')) + ['', 1, 2, 3, 4, 5] + >>> list(pad_sequence([1,2,3,4,5], 2, pad_right=True, right_pad_symbol='')) + [1, 2, 3, 4, 5, ''] + + :param sequence: the source data to be padded + :type sequence: sequence or iter + :param n: the degree of the ngrams + :type n: int + :param pad_left: whether the ngrams should be left-padded + :type pad_left: bool + :param pad_right: whether the ngrams should be right-padded + :type pad_right: bool + :param left_pad_symbol: the symbol to use for left padding (default is None) + :type left_pad_symbol: any + :param right_pad_symbol: the symbol to use for right padding (default is None) + :type right_pad_symbol: any + :rtype: sequence or iter + """ + sequence = iter(sequence) + if pad_left: + sequence = chain((left_pad_symbol,) * (n - 1), sequence) + if pad_right: + sequence = chain(sequence, (right_pad_symbol,) * (n - 1)) + return sequence + + +# add a flag to pad the sequence so we get peripheral ngrams? + + +def ngrams(sequence, n, **kwargs): + """ + Return the ngrams generated from a sequence of items, as an iterator. + For example: + + >>> from nltk.util import ngrams + >>> list(ngrams([1,2,3,4,5], 3)) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + Wrap with list for a list version of this function. Set pad_left + or pad_right to true in order to get additional ngrams: + + >>> list(ngrams([1,2,3,4,5], 2, pad_right=True)) + [(1, 2), (2, 3), (3, 4), (4, 5), (5, None)] + >>> list(ngrams([1,2,3,4,5], 2, pad_right=True, right_pad_symbol='
    ')) + [(1, 2), (2, 3), (3, 4), (4, 5), (5, '
    ')] + >>> list(ngrams([1,2,3,4,5], 2, pad_left=True, left_pad_symbol='')) + [('', 1), (1, 2), (2, 3), (3, 4), (4, 5)] + >>> list(ngrams([1,2,3,4,5], 2, pad_left=True, pad_right=True, left_pad_symbol='', right_pad_symbol='')) + [('', 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, '')] + + + :param sequence: the source data to be converted into ngrams + :type sequence: sequence or iter + :param n: the degree of the ngrams + :type n: int + :param pad_left: whether the ngrams should be left-padded + :type pad_left: bool + :param pad_right: whether the ngrams should be right-padded + :type pad_right: bool + :param left_pad_symbol: the symbol to use for left padding (default is None) + :type left_pad_symbol: any + :param right_pad_symbol: the symbol to use for right padding (default is None) + :type right_pad_symbol: any + :rtype: sequence or iter + """ + sequence = pad_sequence(sequence, n, **kwargs) + + # Creates the sliding window, of n no. of items. + # `iterables` is a tuple of iterables where each iterable is a window of n items. + iterables = tee(sequence, n) + + for i, sub_iterable in enumerate(iterables): # For each window, + for _ in range(i): # iterate through every order of ngrams + next(sub_iterable, None) # generate the ngrams within the window. + return zip(*iterables) # Unpack and flattens the iterables. + + +def bigrams(sequence, **kwargs): + """ + Return the bigrams generated from a sequence of items, as an iterator. + For example: + + >>> from nltk.util import bigrams + >>> list(bigrams([1,2,3,4,5])) + [(1, 2), (2, 3), (3, 4), (4, 5)] + + Use bigrams for a list version of this function. + + :param sequence: the source data to be converted into bigrams + :type sequence: sequence or iter + :rtype: iter(tuple) + """ + + yield from ngrams(sequence, 2, **kwargs) + + +def trigrams(sequence, **kwargs): + """ + Return the trigrams generated from a sequence of items, as an iterator. + For example: + + >>> from nltk.util import trigrams + >>> list(trigrams([1,2,3,4,5])) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + Use trigrams for a list version of this function. + + :param sequence: the source data to be converted into trigrams + :type sequence: sequence or iter + :rtype: iter(tuple) + """ + + yield from ngrams(sequence, 3, **kwargs) + + +def everygrams( + sequence, min_len=1, max_len=-1, pad_left=False, pad_right=False, **kwargs +): + """ + Returns all possible ngrams generated from a sequence of items, as an iterator. + + >>> sent = 'a b c'.split() + + New version outputs for everygrams. + >>> list(everygrams(sent)) + [('a',), ('a', 'b'), ('a', 'b', 'c'), ('b',), ('b', 'c'), ('c',)] + + Old version outputs for everygrams. + >>> sorted(everygrams(sent), key=len) + [('a',), ('b',), ('c',), ('a', 'b'), ('b', 'c'), ('a', 'b', 'c')] + + >>> list(everygrams(sent, max_len=2)) + [('a',), ('a', 'b'), ('b',), ('b', 'c'), ('c',)] + + :param sequence: the source data to be converted into ngrams. If max_len is + not provided, this sequence will be loaded into memory + :type sequence: sequence or iter + :param min_len: minimum length of the ngrams, aka. n-gram order/degree of ngram + :type min_len: int + :param max_len: maximum length of the ngrams (set to length of sequence by default) + :type max_len: int + :param pad_left: whether the ngrams should be left-padded + :type pad_left: bool + :param pad_right: whether the ngrams should be right-padded + :type pad_right: bool + :rtype: iter(tuple) + """ + + # Get max_len for padding. + if max_len == -1: + try: + max_len = len(sequence) + except TypeError: + sequence = list(sequence) + max_len = len(sequence) + + # Pad if indicated using max_len. + sequence = pad_sequence(sequence, max_len, pad_left, pad_right, **kwargs) + + # Sliding window to store grams. + history = list(islice(sequence, max_len)) + + # Yield ngrams from sequence. + while history: + for ngram_len in range(min_len, len(history) + 1): + yield tuple(history[:ngram_len]) + + # Append element to history if sequence has more items. + try: + history.append(next(sequence)) + except StopIteration: + pass + + del history[0] + + +def skipgrams(sequence, n, k, **kwargs): + """ + Returns all possible skipgrams generated from a sequence of items, as an iterator. + Skipgrams are ngrams that allows tokens to be skipped. + Refer to http://homepages.inf.ed.ac.uk/ballison/pdf/lrec_skipgrams.pdf + + >>> sent = "Insurgents killed in ongoing fighting".split() + >>> list(skipgrams(sent, 2, 2)) + [('Insurgents', 'killed'), ('Insurgents', 'in'), ('Insurgents', 'ongoing'), ('killed', 'in'), ('killed', 'ongoing'), ('killed', 'fighting'), ('in', 'ongoing'), ('in', 'fighting'), ('ongoing', 'fighting')] + >>> list(skipgrams(sent, 3, 2)) + [('Insurgents', 'killed', 'in'), ('Insurgents', 'killed', 'ongoing'), ('Insurgents', 'killed', 'fighting'), ('Insurgents', 'in', 'ongoing'), ('Insurgents', 'in', 'fighting'), ('Insurgents', 'ongoing', 'fighting'), ('killed', 'in', 'ongoing'), ('killed', 'in', 'fighting'), ('killed', 'ongoing', 'fighting'), ('in', 'ongoing', 'fighting')] + + :param sequence: the source data to be converted into trigrams + :type sequence: sequence or iter + :param n: the degree of the ngrams + :type n: int + :param k: the skip distance + :type k: int + :rtype: iter(tuple) + """ + + # Pads the sequence as desired by **kwargs. + if "pad_left" in kwargs or "pad_right" in kwargs: + sequence = pad_sequence(sequence, n, **kwargs) + + # Note when iterating through the ngrams, the pad_right here is not + # the **kwargs padding, it's for the algorithm to detect the SENTINEL + # object on the right pad to stop inner loop. + SENTINEL = object() + for ngram in ngrams(sequence, n + k, pad_right=True, right_pad_symbol=SENTINEL): + head = ngram[:1] + tail = ngram[1:] + for skip_tail in combinations(tail, n - 1): + if skip_tail[-1] is SENTINEL: + continue + yield head + skip_tail + + +###################################################################### +# Binary Search in a File +###################################################################### + +# inherited from pywordnet, by Oliver Steele +def binary_search_file(file, key, cache=None, cacheDepth=-1): + """ + Return the line from the file with first word key. + Searches through a sorted file using the binary search algorithm. + + :type file: file + :param file: the file to be searched through. + :type key: str + :param key: the identifier we are searching for. + """ + + key = key + " " + keylen = len(key) + start = 0 + currentDepth = 0 + + if hasattr(file, "name"): + end = os.stat(file.name).st_size - 1 + else: + file.seek(0, 2) + end = file.tell() - 1 + file.seek(0) + + if cache is None: + cache = {} + + while start < end: + lastState = start, end + middle = (start + end) // 2 + + if cache.get(middle): + offset, line = cache[middle] + + else: + line = "" + while True: + file.seek(max(0, middle - 1)) + if middle > 0: + file.discard_line() + offset = file.tell() + line = file.readline() + if line != "": + break + # at EOF; try to find start of the last line + middle = (start + middle) // 2 + if middle == end - 1: + return None + if currentDepth < cacheDepth: + cache[middle] = (offset, line) + + if offset > end: + assert end != middle - 1, "infinite loop" + end = middle - 1 + elif line[:keylen] == key: + return line + elif line > key: + assert end != middle - 1, "infinite loop" + end = middle - 1 + elif line < key: + start = offset + len(line) - 1 + + currentDepth += 1 + thisState = start, end + + if lastState == thisState: + # Detects the condition where we're searching past the end + # of the file, which is otherwise difficult to detect + return None + + return None + + +###################################################################### +# Proxy configuration +###################################################################### + + +def set_proxy(proxy, user=None, password=""): + """ + Set the HTTP proxy for Python to download through. + + If ``proxy`` is None then tries to set proxy from environment or system + settings. + + :param proxy: The HTTP proxy server to use. For example: + 'http://proxy.example.com:3128/' + :param user: The username to authenticate with. Use None to disable + authentication. + :param password: The password to authenticate with. + """ + if proxy is None: + # Try and find the system proxy settings + try: + proxy = getproxies()["http"] + except KeyError as e: + raise ValueError("Could not detect default proxy settings") from e + + # Set up the proxy handler + proxy_handler = ProxyHandler({"https": proxy, "http": proxy}) + opener = build_opener(proxy_handler) + + if user is not None: + # Set up basic proxy authentication if provided + password_manager = HTTPPasswordMgrWithDefaultRealm() + password_manager.add_password(realm=None, uri=proxy, user=user, passwd=password) + opener.add_handler(ProxyBasicAuthHandler(password_manager)) + opener.add_handler(ProxyDigestAuthHandler(password_manager)) + + # Override the existing url opener + install_opener(opener) + + +###################################################################### +# ElementTree pretty printing from https://www.effbot.org/zone/element-lib.htm +###################################################################### + + +def elementtree_indent(elem, level=0): + """ + Recursive function to indent an ElementTree._ElementInterface + used for pretty printing. Run indent on elem and then output + in the normal way. + + :param elem: element to be indented. will be modified. + :type elem: ElementTree._ElementInterface + :param level: level of indentation for this element + :type level: nonnegative integer + :rtype: ElementTree._ElementInterface + :return: Contents of elem indented to reflect its structure + """ + + i = "\n" + level * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + for elem in elem: + elementtree_indent(elem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + +###################################################################### +# Mathematical approximations +###################################################################### + + +def choose(n, k): + """ + This function is a fast way to calculate binomial coefficients, commonly + known as nCk, i.e. the number of combinations of n things taken k at a time. + (https://en.wikipedia.org/wiki/Binomial_coefficient). + + This is the *scipy.special.comb()* with long integer computation but this + approximation is faster, see https://github.com/nltk/nltk/issues/1181 + + >>> choose(4, 2) + 6 + >>> choose(6, 2) + 15 + + :param n: The number of things. + :type n: int + :param r: The number of times a thing is taken. + :type r: int + """ + if 0 <= k <= n: + ntok, ktok = 1, 1 + for t in range(1, min(k, n - k) + 1): + ntok *= n + ktok *= t + n -= 1 + return ntok // ktok + else: + return 0 + + +###################################################################### +# Iteration utilities +###################################################################### + + +def pairwise(iterable): + """s -> (s0,s1), (s1,s2), (s2, s3), ...""" + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + +###################################################################### +# Parallelization. +###################################################################### + + +def parallelize_preprocess(func, iterator, processes, progress_bar=False): + from joblib import Parallel, delayed + from tqdm import tqdm + + iterator = tqdm(iterator) if progress_bar else iterator + if processes <= 1: + return map(func, iterator) + return Parallel(n_jobs=processes)(delayed(func)(line) for line in iterator) diff --git a/.pythonlibs/lib/python3.10/site-packages/nltk/wsd.py b/.pythonlibs/lib/python3.10/site-packages/nltk/wsd.py new file mode 100644 index 0000000..7022edf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/nltk/wsd.py @@ -0,0 +1,51 @@ +# Natural Language Toolkit: Word Sense Disambiguation Algorithms +# +# Authors: Liling Tan , +# Dmitrijs Milajevs +# +# Copyright (C) 2001-2023 NLTK Project +# URL: +# For license information, see LICENSE.TXT + +from nltk.corpus import wordnet + + +def lesk(context_sentence, ambiguous_word, pos=None, synsets=None): + """Return a synset for an ambiguous word in a context. + + :param iter context_sentence: The context sentence where the ambiguous word + occurs, passed as an iterable of words. + :param str ambiguous_word: The ambiguous word that requires WSD. + :param str pos: A specified Part-of-Speech (POS). + :param iter synsets: Possible synsets of the ambiguous word. + :return: ``lesk_sense`` The Synset() object with the highest signature overlaps. + + This function is an implementation of the original Lesk algorithm (1986) [1]. + + Usage example:: + + >>> lesk(['I', 'went', 'to', 'the', 'bank', 'to', 'deposit', 'money', '.'], 'bank', 'n') + Synset('savings_bank.n.02') + + [1] Lesk, Michael. "Automatic sense disambiguation using machine + readable dictionaries: how to tell a pine cone from an ice cream + cone." Proceedings of the 5th Annual International Conference on + Systems Documentation. ACM, 1986. + https://dl.acm.org/citation.cfm?id=318728 + """ + + context = set(context_sentence) + if synsets is None: + synsets = wordnet.synsets(ambiguous_word) + + if pos: + synsets = [ss for ss in synsets if str(ss.pos()) == pos] + + if not synsets: + return None + + _, sense = max( + (len(context.intersection(ss.definition().split())), ss) for ss in synsets + ) + + return sense diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/LICENSE new file mode 100644 index 0000000..4f14854 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) OpenAI (https://openai.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/METADATA new file mode 100644 index 0000000..1dfdf20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/METADATA @@ -0,0 +1,361 @@ +Metadata-Version: 2.1 +Name: openai +Version: 0.28.0 +Summary: Python client library for the OpenAI API +Home-page: https://github.com/openai/openai-python +Author: OpenAI +Author-email: support@openai.com +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7.1 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: requests >=2.20 +Requires-Dist: tqdm +Requires-Dist: aiohttp +Requires-Dist: typing-extensions ; python_version < "3.8" +Provides-Extra: datalib +Requires-Dist: numpy ; extra == 'datalib' +Requires-Dist: pandas >=1.2.3 ; extra == 'datalib' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'datalib' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'datalib' +Provides-Extra: dev +Requires-Dist: black ~=21.6b0 ; extra == 'dev' +Requires-Dist: pytest ==6.* ; extra == 'dev' +Requires-Dist: pytest-asyncio ; extra == 'dev' +Requires-Dist: pytest-mock ; extra == 'dev' +Provides-Extra: embeddings +Requires-Dist: scikit-learn >=1.0.2 ; extra == 'embeddings' +Requires-Dist: tenacity >=8.0.1 ; extra == 'embeddings' +Requires-Dist: matplotlib ; extra == 'embeddings' +Requires-Dist: plotly ; extra == 'embeddings' +Requires-Dist: numpy ; extra == 'embeddings' +Requires-Dist: scipy ; extra == 'embeddings' +Requires-Dist: pandas >=1.2.3 ; extra == 'embeddings' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'embeddings' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'embeddings' +Provides-Extra: wandb +Requires-Dist: wandb ; extra == 'wandb' +Requires-Dist: numpy ; extra == 'wandb' +Requires-Dist: pandas >=1.2.3 ; extra == 'wandb' +Requires-Dist: pandas-stubs >=1.1.0.11 ; extra == 'wandb' +Requires-Dist: openpyxl >=3.0.7 ; extra == 'wandb' + +# OpenAI Python Library + +The OpenAI Python library provides convenient access to the OpenAI API +from applications written in the Python language. It includes a +pre-defined set of classes for API resources that initialize +themselves dynamically from API responses which makes it compatible +with a wide range of versions of the OpenAI API. + +You can find usage examples for the OpenAI Python library in our [API reference](https://platform.openai.com/docs/api-reference?lang=python) and the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). + +## Installation + +You don't need this source code unless you want to modify the package. If you just +want to use the package, just run: + +```sh +pip install --upgrade openai +``` + +Install from source with: + +```sh +python setup.py install +``` + +### Optional dependencies + +Install dependencies for [`openai.embeddings_utils`](openai/embeddings_utils.py): + +```sh +pip install openai[embeddings] +``` + +Install support for [Weights & Biases](https://wandb.me/openai-docs): + +``` +pip install openai[wandb] +``` + +Data libraries like `numpy` and `pandas` are not installed by default due to their size. They’re needed for some functionality of this library, but generally not for talking to the API. If you encounter a `MissingDependencyError`, install them with: + +```sh +pip install openai[datalib] +``` + +## Usage + +The library needs to be configured with your account's secret key which is available on the [website](https://platform.openai.com/account/api-keys). Either set it as the `OPENAI_API_KEY` environment variable before using the library: + +```bash +export OPENAI_API_KEY='sk-...' +``` + +Or set `openai.api_key` to its value: + +```python +import openai +openai.api_key = "sk-..." + +# list models +models = openai.Model.list() + +# print the first model's id +print(models.data[0].id) + +# create a chat completion +chat_completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +# print the chat completion +print(chat_completion.choices[0].message.content) +``` + +### Params + +All endpoints have a `.create` method that supports a `request_timeout` param. This param takes a `Union[float, Tuple[float, float]]` and will raise an `openai.error.Timeout` error if the request exceeds that time in seconds (See: https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts). + +### Microsoft Azure Endpoints + +In order to use the library with Microsoft Azure endpoints, you need to set the `api_type`, `api_base` and `api_version` in addition to the `api_key`. The `api_type` must be set to 'azure' and the others correspond to the properties of your endpoint. +In addition, the deployment name must be passed as the engine parameter. + +```python +import openai +openai.api_type = "azure" +openai.api_key = "..." +openai.api_base = "https://example-endpoint.openai.azure.com" +openai.api_version = "2023-05-15" + +# create a chat completion +chat_completion = openai.ChatCompletion.create(deployment_id="deployment-name", model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +# print the completion +print(chat_completion.choices[0].message.content) +``` + +Please note that for the moment, the Microsoft Azure endpoints can only be used for completion, embedding, and fine-tuning operations. +For a detailed example of how to use fine-tuning and other operations using Azure endpoints, please check out the following Jupyter notebooks: + +- [Using Azure completions](https://github.com/openai/openai-cookbook/tree/main/examples/azure/completions.ipynb) +- [Using Azure fine-tuning](https://github.com/openai/openai-cookbook/tree/main/examples/azure/finetuning.ipynb) +- [Using Azure embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/azure/embeddings.ipynb) + +### Microsoft Azure Active Directory Authentication + +In order to use Microsoft Active Directory to authenticate to your Azure endpoint, you need to set the `api_type` to "azure_ad" and pass the acquired credential token to `api_key`. The rest of the parameters need to be set as specified in the previous section. + +```python +from azure.identity import DefaultAzureCredential +import openai + +# Request credential +default_credential = DefaultAzureCredential() +token = default_credential.get_token("https://cognitiveservices.azure.com/.default") + +# Setup parameters +openai.api_type = "azure_ad" +openai.api_key = token.token +openai.api_base = "https://example-endpoint.openai.azure.com/" +openai.api_version = "2023-05-15" + +# ... +``` + +### Command-line interface + +This library additionally provides an `openai` command-line utility +which makes it easy to interact with the API from your terminal. Run +`openai api -h` for usage. + +```sh +# list models +openai api models.list + +# create a chat completion (gpt-3.5-turbo, gpt-4, etc.) +openai api chat_completions.create -m gpt-3.5-turbo -g user "Hello world" + +# create a completion (text-davinci-003, text-davinci-002, ada, babbage, curie, davinci, etc.) +openai api completions.create -m ada -p "Hello world" + +# generate images via DALL·E API +openai api image.create -p "two dogs playing chess, cartoon" -n 1 + +# using openai through a proxy +openai --proxy=http://proxy.com api models.list +``` + +## Example code + +Examples of how to use this Python library to accomplish various tasks can be found in the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). It contains code examples for: + +- Classification using fine-tuning +- Clustering +- Code search +- Customizing embeddings +- Question answering from a corpus of documents +- Recommendations +- Visualization of embeddings +- And more + +Prior to July 2022, this OpenAI Python library hosted code examples in its examples folder, but since then all examples have been migrated to the [OpenAI Cookbook](https://github.com/openai/openai-cookbook/). + +### Chat Completions + +Conversational models such as `gpt-3.5-turbo` can be called using the chat completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) +print(completion.choices[0].message.content) +``` + +### Completions + +Text models such as `babbage-002` or `davinci-002` (and our [legacy completions models](https://platform.openai.com/docs/deprecations/deprecation-history)) can be called using the completions endpoint. + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +completion = openai.Completion.create(model="davinci-002", prompt="Hello world") +print(completion.choices[0].text) +``` + +### Embeddings + +In the OpenAI Python library, an embedding represents a text string as a fixed-length vector of floating point numbers. Embeddings are designed to measure the similarity or relevance between text strings. + +To get an embedding for a text string, you can use the embeddings method as follows in Python: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +# choose text to embed +text_string = "sample text" + +# choose an embedding +model_id = "text-embedding-ada-002" + +# compute the embedding of the text +embedding = openai.Embedding.create(input=text_string, model=model_id)['data'][0]['embedding'] +``` + +An example of how to call the embeddings method is shown in this [embeddings guide](https://platform.openai.com/docs/guides/embeddings/embeddings). + +Examples of how to use embeddings are shared in the following Jupyter notebooks: + +- [Classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Classification_using_embeddings.ipynb) +- [Clustering using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb) +- [Code search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Code_search.ipynb) +- [Semantic text search using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Semantic_text_search_using_embeddings.ipynb) +- [User and product embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/User_and_product_embeddings.ipynb) +- [Zero-shot classification using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot_classification_with_embeddings.ipynb) +- [Recommendation using embeddings](https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation_using_embeddings.ipynb) + +For more information on embeddings and the types of embeddings OpenAI offers, read the [embeddings guide](https://platform.openai.com/docs/guides/embeddings) in the OpenAI documentation. + +### Fine-tuning + +Fine-tuning a model on training data can both improve the results (by giving the model more examples to learn from) and lower the cost/latency of API calls by reducing the need to include training examples in prompts. + +```python +# Create a fine-tuning job with an already uploaded file +openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo") + +# List 10 fine-tuning jobs +openai.FineTuningJob.list(limit=10) + +# Retrieve the state of a fine-tune +openai.FineTuningJob.retrieve("ft-abc123") + +# Cancel a job +openai.FineTuningJob.cancel("ft-abc123") + +# List up to 10 events from a fine-tuning job +openai.FineTuningJob.list_events(id="ft-abc123", limit=10) + +# Delete a fine-tuned model (must be an owner of the org the model was created in) +openai.Model.delete("ft-abc123") +``` + +For more information on fine-tuning, read the [fine-tuning guide](https://platform.openai.com/docs/guides/fine-tuning) in the OpenAI documentation. + +### Moderation + +OpenAI provides a Moderation endpoint that can be used to check whether content complies with the OpenAI [content policy](https://platform.openai.com/docs/usage-policies) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +moderation_resp = openai.Moderation.create(input="Here is some perfectly innocuous text that follows all OpenAI content policies.") +``` + +See the [moderation guide](https://platform.openai.com/docs/guides/moderation) for more details. + +## Image generation (DALL·E) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +image_resp = openai.Image.create(prompt="two dogs playing chess, oil painting", n=4, size="512x512") + +``` + +## Audio transcription (Whisper) + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose +f = open("path/to/file.mp3", "rb") +transcript = openai.Audio.transcribe("whisper-1", f) + +``` + +## Async API + +Async support is available in the API by prepending `a` to a network-bound method: + +```python +import openai +openai.api_key = "sk-..." # supply your API key however you choose + +async def create_chat_completion(): + chat_completion_resp = await openai.ChatCompletion.acreate(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello world"}]) + +``` + +To make async requests more efficient, you can pass in your own +`aiohttp.ClientSession`, but you must manually close the client session at the end +of your program/event loop: + +```python +import openai +from aiohttp import ClientSession + +openai.aiosession.set(ClientSession()) +# At the end of your program, close the http session +await openai.aiosession.get().close() +``` + +See the [usage guide](https://platform.openai.com/docs/guides/images) for more details. + +## Requirements + +- Python 3.7.1+ + +In general, we want to support the versions of Python that our +customers are using. If you run into problems with any version +issues, please let us know on our [support page](https://help.openai.com/en/). + +## Credit + +This library is forked from the [Stripe Python Library](https://github.com/stripe/stripe-python). diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/RECORD new file mode 100644 index 0000000..b0be7e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/RECORD @@ -0,0 +1,121 @@ +../../../bin/openai,sha256=sQKnGSCLtsjrSv0UJ4jY1VTh3jBLJygDhIfacK5LcKM,225 +openai-0.28.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +openai-0.28.0.dist-info/LICENSE,sha256=vLo94hSFHM5G7Vr0LWaYBEYW7qzoh8MjG8eiBHSrY54,1083 +openai-0.28.0.dist-info/METADATA,sha256=mJk3B792DlIXBfwjhtRUMq5kBMMWGM8aQbsV2bsLkDM,13509 +openai-0.28.0.dist-info/RECORD,, +openai-0.28.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai-0.28.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92 +openai-0.28.0.dist-info/entry_points.txt,sha256=q68fD1MsMkiejaG68n2mKmbuZEwWkq89oniOBBq9fo0,55 +openai-0.28.0.dist-info/top_level.txt,sha256=yroxRDiE4kOdI0tEpF1d7ffoQJMxe4i3z_pKoyou9wg,7 +openai-0.28.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +openai/__init__.py,sha256=rv_DUleSfW1ORJQ3P5F-s93jivhjUbNINjBOOEhEcL4,2799 +openai/__pycache__/__init__.cpython-310.pyc,, +openai/__pycache__/_openai_scripts.cpython-310.pyc,, +openai/__pycache__/api_requestor.cpython-310.pyc,, +openai/__pycache__/cli.cpython-310.pyc,, +openai/__pycache__/embeddings_utils.cpython-310.pyc,, +openai/__pycache__/error.cpython-310.pyc,, +openai/__pycache__/object_classes.cpython-310.pyc,, +openai/__pycache__/openai_object.cpython-310.pyc,, +openai/__pycache__/openai_response.cpython-310.pyc,, +openai/__pycache__/upload_progress.cpython-310.pyc,, +openai/__pycache__/util.cpython-310.pyc,, +openai/__pycache__/validators.cpython-310.pyc,, +openai/__pycache__/version.cpython-310.pyc,, +openai/__pycache__/wandb_logger.cpython-310.pyc,, +openai/_openai_scripts.py,sha256=KDA_8h9wIJqVpRBSM-_r3FOhi3S5Hh5b5zMzxIIoZ2s,2558 +openai/api_requestor.py,sha256=2q9qbzcIUBjI7DVG_TucEEK0nEh6qTQD9VkPIn7cm88,25865 +openai/api_resources/__init__.py,sha256=-ApRXceSmjK--aNhHMHFwEFYG3WpOOW-AuByshh3evk,980 +openai/api_resources/__pycache__/__init__.cpython-310.pyc,, +openai/api_resources/__pycache__/audio.cpython-310.pyc,, +openai/api_resources/__pycache__/chat_completion.cpython-310.pyc,, +openai/api_resources/__pycache__/completion.cpython-310.pyc,, +openai/api_resources/__pycache__/customer.cpython-310.pyc,, +openai/api_resources/__pycache__/deployment.cpython-310.pyc,, +openai/api_resources/__pycache__/edit.cpython-310.pyc,, +openai/api_resources/__pycache__/embedding.cpython-310.pyc,, +openai/api_resources/__pycache__/engine.cpython-310.pyc,, +openai/api_resources/__pycache__/error_object.cpython-310.pyc,, +openai/api_resources/__pycache__/file.cpython-310.pyc,, +openai/api_resources/__pycache__/fine_tune.cpython-310.pyc,, +openai/api_resources/__pycache__/fine_tuning.cpython-310.pyc,, +openai/api_resources/__pycache__/image.cpython-310.pyc,, +openai/api_resources/__pycache__/model.cpython-310.pyc,, +openai/api_resources/__pycache__/moderation.cpython-310.pyc,, +openai/api_resources/abstract/__init__.py,sha256=OJuM5gagjZktzrsUQIlv2wBQVLVltzwCZsNDEKO5XTo,639 +openai/api_resources/abstract/__pycache__/__init__.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/paginatable_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-310.pyc,, +openai/api_resources/abstract/api_resource.py,sha256=wzCww3boKgr0Ru33CIRi8lOCsMEtQJLKlz1HMHIpMkY,5443 +openai/api_resources/abstract/createable_api_resource.py,sha256=w6atKaf0aUDe3NuV-wLtbmCxCGPtCxXIkH98yk8s7xQ,2573 +openai/api_resources/abstract/deletable_api_resource.py,sha256=9jI4tt7RejbO0aIyLJwivyg3jclr7r55-l50mVFC7GI,1624 +openai/api_resources/abstract/engine_api_resource.py,sha256=GGj9NpJkPqKozgTT3E2meIS1VsRq8YnTg_A3Uzh-P00,10007 +openai/api_resources/abstract/listable_api_resource.py,sha256=-okp69Lu1OaA9aIkEK2nC-JXRW44vtCKFCN89w_4f60,2678 +openai/api_resources/abstract/nested_resource_class_methods.py,sha256=r_hcN2M61YgUSCe_dDEzXOiQ8naOoHHmhK6xPO-BckQ,5640 +openai/api_resources/abstract/paginatable_api_resource.py,sha256=RAwBGpU_DqkIPyAYq-fn_a85nUTfPFyioaAy_1r9OMY,3453 +openai/api_resources/abstract/updateable_api_resource.py,sha256=cQQd04fI85DTYpOqM_LFuZ9ldDKrkT-MRetkDG5FUU0,534 +openai/api_resources/audio.py,sha256=wpAQgGgQKv91tjLRgEHqrX03IVtvgaqAOw36_QZXKlY,7658 +openai/api_resources/chat_completion.py,sha256=TTXb7OuBEBjtoz5Rjxw3UTfXcdnL8z-mjFDykj9J23A,1563 +openai/api_resources/completion.py,sha256=EDgsMEJXwYyTqB5fGy2Vi6nQeproKZWvNmkdNve5Ykc,1610 +openai/api_resources/customer.py,sha256=vX1reXMqDHLssmT2aGev83TIE4NbnzvQ73yrQWyDpFk,539 +openai/api_resources/deployment.py,sha256=9K64JZyWxVcvutebf0qguYrSRPb0V_lMALwNsWRqNzs,4055 +openai/api_resources/edit.py,sha256=HlE11SFBBJH_ompIBHAYToNc4tIdUu01tZyym7f8LlE,1963 +openai/api_resources/embedding.py,sha256=iQBHiBszZPuh4lbiucL9mT_c9ed6DvPatHvyER4ye1Q,3438 +openai/api_resources/engine.py,sha256=_bzICgcNFmpdm08yL_W3n-vekkO0AGcCdPGl352HMT8,1665 +openai/api_resources/error_object.py,sha256=gKtXIo8mYZDPE2GiJUzQ8LDzLKKEPLqQoyYo_AAqOA4,892 +openai/api_resources/experimental/__init__.py,sha256=fLUruA1uO11wqsDNuAKxgx3qVhQyPYC79JoZNTCIq1A,104 +openai/api_resources/experimental/__pycache__/__init__.cpython-310.pyc,, +openai/api_resources/experimental/__pycache__/completion_config.cpython-310.pyc,, +openai/api_resources/experimental/completion_config.py,sha256=7vlxmFIkpleNCEUir50NBk31XxnqL0yBFZ5Ah1h9TpQ,274 +openai/api_resources/file.py,sha256=0_NHh62OTD926yibTqfevRQyagCBvQQZdv7WNc4c7vM,8435 +openai/api_resources/fine_tune.py,sha256=uotwurKyV8wAnyw4x01O2b2t3b--8i_EBSBVbV-2EhE,5263 +openai/api_resources/fine_tuning.py,sha256=3iciDEIAfGEXV-IDM1KRyE1OAYWFCntNyYc-aJNDcXk,2261 +openai/api_resources/image.py,sha256=cwwVjOFRblykuBC9cLyLsmDBKmfkppF3gwTz2hF7Wgs,8341 +openai/api_resources/model.py,sha256=BiXo7j4P6-rhZW8my4eAbD6aIdQ6sAf2Dw9e-U21b_Q,169 +openai/api_resources/moderation.py,sha256=XLEVJbP2A5AqcnelbIcXq7-PMqvfPPibOetOf2xiMTA,1376 +openai/cli.py,sha256=F65mKrvUgDKqY60ANP1nyAK3ub0vZOwjsdBFyask2g4,49069 +openai/datalib/__init__.py,sha256=cCLTzxxXF1mYb_iC2zX9WMr_VnJWnuWOPxgFvLZKIYE,651 +openai/datalib/__pycache__/__init__.cpython-310.pyc,, +openai/datalib/__pycache__/common.cpython-310.pyc,, +openai/datalib/__pycache__/numpy_helper.cpython-310.pyc,, +openai/datalib/__pycache__/pandas_helper.cpython-310.pyc,, +openai/datalib/common.py,sha256=YNnJ_IDOc-lF43LkwFJNftfF5h2n9mhdt6eZfAH976w,258 +openai/datalib/numpy_helper.py,sha256=p5o-NYEF0DX5jN9Dy_pRbYpD9M9C1B18konK-WZSOIQ,320 +openai/datalib/pandas_helper.py,sha256=WvA3nHCaTgeB5-VgSJlfqfQzVMdmi_hKFRQtamdLzAE,329 +openai/embeddings_utils.py,sha256=0LweljppFfBV5GjY_oipGTnVqpicnECqseKYJOX8tWM,8592 +openai/error.py,sha256=xJJSg_JWuEKL5WdASVg3wNx27J52tzXN9f86kPkCEII,4298 +openai/object_classes.py,sha256=YB1LxGA4lg9L1p1avXfniUw_5vZGsWjoroOOTEf0QYc,431 +openai/openai_object.py,sha256=AohBhBMUtdI96e0YBiiqMabG4ZbkcSYIN_q0LZVr_V8,10774 +openai/openai_response.py,sha256=GoWVZ5kKB0-p8hEN0_0v8TTOq4drthtNRivQc3VBrsk,836 +openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai/tests/__pycache__/__init__.cpython-310.pyc,, +openai/tests/__pycache__/test_api_requestor.cpython-310.pyc,, +openai/tests/__pycache__/test_endpoints.cpython-310.pyc,, +openai/tests/__pycache__/test_exceptions.cpython-310.pyc,, +openai/tests/__pycache__/test_file_cli.cpython-310.pyc,, +openai/tests/__pycache__/test_long_examples_validator.cpython-310.pyc,, +openai/tests/__pycache__/test_url_composition.cpython-310.pyc,, +openai/tests/__pycache__/test_util.cpython-310.pyc,, +openai/tests/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openai/tests/asyncio/__pycache__/__init__.cpython-310.pyc,, +openai/tests/asyncio/__pycache__/test_endpoints.cpython-310.pyc,, +openai/tests/asyncio/test_endpoints.py,sha256=ko3ra_FcwlmkCzxEoVwIyOTYdSNQn7VgSqUGQ7O8kro,2362 +openai/tests/test_api_requestor.py,sha256=rU9EJyDrtOqSzDmMaYxf-FKqdaBCFHkzJYGMCQS37EY,3582 +openai/tests/test_endpoints.py,sha256=RgYp2Gcz-nkM6AH35MJ_QvNKcpN5IUvIzMth7SzsJN4,2867 +openai/tests/test_exceptions.py,sha256=VbKHyi7QAoLi_t6IMuJWSdSkY4zJD4ngrNRjQ89GIjQ,1156 +openai/tests/test_file_cli.py,sha256=BIBCaDJ6SZsAtvNqERhEQLDutJjoejgV9GCP3tYcu1A,1418 +openai/tests/test_long_examples_validator.py,sha256=hlJSur4VkHxc3kMviBMeuZTATyEqwHCDdCTD0ok_v1g,2016 +openai/tests/test_url_composition.py,sha256=d3Jr-bmCFLedEYu3FTYN180WT6wBygRABW8g9R3ccsA,6506 +openai/tests/test_util.py,sha256=K268icrNV6XQBi25VSGqpBzOP2bLBoIuSj91NMV4nU0,1786 +openai/upload_progress.py,sha256=L723nchd5OQ_tfDFDKOru_rlHdG73uzesOh528wOOcM,1188 +openai/util.py,sha256=erOS0iwMOpjIUd1TH2clqXxdA-MU1ll0dC8cBP3DO9w,5366 +openai/validators.py,sha256=lqy0NTs1rZS7lt_DFKGtyOmhBVf5LwkmYchsiMKMkBA,34275 +openai/version.py,sha256=iMp3MO9cecOC2ODQBTEFiyBqct3uSff9TJ-wRgLInJs,19 +openai/wandb_logger.py,sha256=ZQpNHqwrru0sYXMyL9ywOOWS68OhKypoxko1Jko3CuQ,10388 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/REQUESTED b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/WHEEL new file mode 100644 index 0000000..7e68873 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/entry_points.txt new file mode 100644 index 0000000..7885109 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +openai = openai._openai_scripts:main diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/top_level.txt new file mode 100644 index 0000000..ec838c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/top_level.txt @@ -0,0 +1 @@ +openai diff --git a/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/zip-safe b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai-0.28.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/__init__.py new file mode 100644 index 0000000..b44e50f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/__init__.py @@ -0,0 +1,106 @@ +# OpenAI Python bindings. +# +# Originally forked from the MIT-licensed Stripe Python bindings. + +import os +import sys +from typing import TYPE_CHECKING, Optional, Union, Callable + +from contextvars import ContextVar + +if "pkg_resources" not in sys.modules: + # workaround for the following: + # https://github.com/benoitc/gunicorn/pull/2539 + sys.modules["pkg_resources"] = object() # type: ignore[assignment] + import aiohttp + + del sys.modules["pkg_resources"] + +from openai.api_resources import ( + Audio, + ChatCompletion, + Completion, + Customer, + Deployment, + Edit, + Embedding, + Engine, + ErrorObject, + File, + FineTune, + FineTuningJob, + Image, + Model, + Moderation, +) +from openai.error import APIError, InvalidRequestError, OpenAIError +from openai.version import VERSION + +if TYPE_CHECKING: + import requests + from aiohttp import ClientSession + +api_key = os.environ.get("OPENAI_API_KEY") +# Path of a file with an API key, whose contents can change. Supercedes +# `api_key` if set. The main use case is volume-mounted Kubernetes secrets, +# which are updated automatically. +api_key_path: Optional[str] = os.environ.get("OPENAI_API_KEY_PATH") + +organization = os.environ.get("OPENAI_ORGANIZATION") +api_base = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") +api_type = os.environ.get("OPENAI_API_TYPE", "open_ai") +api_version = os.environ.get( + "OPENAI_API_VERSION", + ("2023-05-15" if api_type in ("azure", "azure_ad", "azuread") else None), +) +verify_ssl_certs = True # No effect. Certificates are always verified. +proxy = None +app_info = None +enable_telemetry = False # Ignored; the telemetry feature was removed. +ca_bundle_path = None # No longer used, feature was removed +debug = False +log = None # Set to either 'debug' or 'info', controls console logging + +requestssession: Optional[ + Union["requests.Session", Callable[[], "requests.Session"]] +] = None # Provide a requests.Session or Session factory. + +aiosession: ContextVar[Optional["ClientSession"]] = ContextVar( + "aiohttp-session", default=None +) # Acts as a global aiohttp ClientSession that reuses connections. +# This is user-supplied; otherwise, a session is remade for each request. + +__version__ = VERSION +__all__ = [ + "APIError", + "Audio", + "ChatCompletion", + "Completion", + "Customer", + "Edit", + "Image", + "Deployment", + "Embedding", + "Engine", + "ErrorObject", + "File", + "FineTune", + "FineTuningJob", + "InvalidRequestError", + "Model", + "Moderation", + "OpenAIError", + "api_base", + "api_key", + "api_type", + "api_key_path", + "api_version", + "app_info", + "ca_bundle_path", + "debug", + "enable_telemetry", + "log", + "organization", + "proxy", + "verify_ssl_certs", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..760808b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/_openai_scripts.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/_openai_scripts.cpython-310.pyc new file mode 100644 index 0000000..42e6aa3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/_openai_scripts.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/api_requestor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/api_requestor.cpython-310.pyc new file mode 100644 index 0000000..fa75674 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/api_requestor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/cli.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/cli.cpython-310.pyc new file mode 100644 index 0000000..5bab55e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/cli.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/embeddings_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/embeddings_utils.cpython-310.pyc new file mode 100644 index 0000000..74437eb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/embeddings_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/error.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/error.cpython-310.pyc new file mode 100644 index 0000000..e185663 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/error.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/object_classes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/object_classes.cpython-310.pyc new file mode 100644 index 0000000..1396d74 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/object_classes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_object.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_object.cpython-310.pyc new file mode 100644 index 0000000..1e6ebcb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_object.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_response.cpython-310.pyc new file mode 100644 index 0000000..4e6b205 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/openai_response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/upload_progress.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/upload_progress.cpython-310.pyc new file mode 100644 index 0000000..ca8197e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/upload_progress.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..f5a70aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/validators.cpython-310.pyc new file mode 100644 index 0000000..dae679b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..895e77a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/wandb_logger.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/wandb_logger.cpython-310.pyc new file mode 100644 index 0000000..1a2b508 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/__pycache__/wandb_logger.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/_openai_scripts.py b/.pythonlibs/lib/python3.10/site-packages/openai/_openai_scripts.py new file mode 100644 index 0000000..f2aa3ce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/_openai_scripts.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +import argparse +import logging +import sys + +import openai +from openai import version +from openai.cli import api_register, display_error, tools_register, wandb_register + +logger = logging.getLogger() +formatter = logging.Formatter("[%(asctime)s] %(message)s") +handler = logging.StreamHandler(sys.stderr) +handler.setFormatter(formatter) +logger.addHandler(handler) + + +def main(): + parser = argparse.ArgumentParser(description=None) + parser.add_argument( + "-V", + "--version", + action="version", + version="%(prog)s " + version.VERSION, + ) + parser.add_argument( + "-v", + "--verbose", + action="count", + dest="verbosity", + default=0, + help="Set verbosity.", + ) + parser.add_argument("-b", "--api-base", help="What API base url to use.") + parser.add_argument("-k", "--api-key", help="What API key to use.") + parser.add_argument("-p", "--proxy", nargs='+', help="What proxy to use.") + parser.add_argument( + "-o", + "--organization", + help="Which organization to run as (will use your default organization if not specified)", + ) + + def help(args): + parser.print_help() + + parser.set_defaults(func=help) + + subparsers = parser.add_subparsers() + sub_api = subparsers.add_parser("api", help="Direct API calls") + sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") + sub_wandb = subparsers.add_parser("wandb", help="Logging with Weights & Biases") + + api_register(sub_api) + tools_register(sub_tools) + wandb_register(sub_wandb) + + args = parser.parse_args() + if args.verbosity == 1: + logger.setLevel(logging.INFO) + elif args.verbosity >= 2: + logger.setLevel(logging.DEBUG) + + openai.debug = True + if args.api_key is not None: + openai.api_key = args.api_key + if args.api_base is not None: + openai.api_base = args.api_base + if args.organization is not None: + openai.organization = args.organization + if args.proxy is not None: + openai.proxy = {} + for proxy in args.proxy: + if proxy.startswith('https'): + openai.proxy['https'] = proxy + elif proxy.startswith('http'): + openai.proxy['http'] = proxy + + try: + args.func(args) + except openai.error.OpenAIError as e: + display_error(e) + return 1 + except KeyboardInterrupt: + sys.stderr.write("\n") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_requestor.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_requestor.py new file mode 100644 index 0000000..504f7c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_requestor.py @@ -0,0 +1,778 @@ +import asyncio +import json +import time +import platform +import sys +import threading +import time +import warnings +from contextlib import asynccontextmanager +from json import JSONDecodeError +from typing import ( + AsyncGenerator, + AsyncIterator, + Callable, + Dict, + Iterator, + Optional, + Tuple, + Union, + overload, +) +from urllib.parse import urlencode, urlsplit, urlunsplit + +import aiohttp +import requests + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +import openai +from openai import error, util, version +from openai.openai_response import OpenAIResponse +from openai.util import ApiType + +TIMEOUT_SECS = 600 +MAX_SESSION_LIFETIME_SECS = 180 +MAX_CONNECTION_RETRIES = 2 + +# Has one attribute per thread, 'session'. +_thread_context = threading.local() + + +def _build_api_url(url, query): + scheme, netloc, path, base_query, fragment = urlsplit(url) + + if base_query: + query = "%s&%s" % (base_query, query) + + return urlunsplit((scheme, netloc, path, query, fragment)) + + +def _requests_proxies_arg(proxy) -> Optional[Dict[str, str]]: + """Returns a value suitable for the 'proxies' argument to 'requests.request.""" + if proxy is None: + return None + elif isinstance(proxy, str): + return {"http": proxy, "https": proxy} + elif isinstance(proxy, dict): + return proxy.copy() + else: + raise ValueError( + "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." + ) + + +def _aiohttp_proxies_arg(proxy) -> Optional[str]: + """Returns a value suitable for the 'proxies' argument to 'aiohttp.ClientSession.request.""" + if proxy is None: + return None + elif isinstance(proxy, str): + return proxy + elif isinstance(proxy, dict): + return proxy["https"] if "https" in proxy else proxy["http"] + else: + raise ValueError( + "'openai.proxy' must be specified as either a string URL or a dict with string URL under the https and/or http keys." + ) + + +def _make_session() -> requests.Session: + if openai.requestssession: + if isinstance(openai.requestssession, requests.Session): + return openai.requestssession + return openai.requestssession() + if not openai.verify_ssl_certs: + warnings.warn("verify_ssl_certs is ignored; openai always verifies.") + s = requests.Session() + proxies = _requests_proxies_arg(openai.proxy) + if proxies: + s.proxies = proxies + s.mount( + "https://", + requests.adapters.HTTPAdapter(max_retries=MAX_CONNECTION_RETRIES), + ) + return s + + +def parse_stream_helper(line: bytes) -> Optional[str]: + if line: + if line.strip() == b"data: [DONE]": + # return here will cause GeneratorExit exception in urllib3 + # and it will close http connection with TCP Reset + return None + if line.startswith(b"data: "): + line = line[len(b"data: "):] + return line.decode("utf-8") + else: + return None + return None + + +def parse_stream(rbody: Iterator[bytes]) -> Iterator[str]: + for line in rbody: + _line = parse_stream_helper(line) + if _line is not None: + yield _line + + +async def parse_stream_async(rbody: aiohttp.StreamReader): + async for line in rbody: + _line = parse_stream_helper(line) + if _line is not None: + yield _line + + +class APIRequestor: + def __init__( + self, + key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + self.api_base = api_base or openai.api_base + self.api_key = key or util.default_api_key() + self.api_type = ( + ApiType.from_str(api_type) + if api_type + else ApiType.from_str(openai.api_type) + ) + self.api_version = api_version or openai.api_version + self.organization = organization or openai.organization + + @classmethod + def format_app_info(cls, info): + str = info["name"] + if info["version"]: + str += "/%s" % (info["version"],) + if info["url"]: + str += " (%s)" % (info["url"],) + return str + + def _check_polling_response(self, response: OpenAIResponse, predicate: Callable[[OpenAIResponse], bool]): + if not predicate(response): + return + error_data = response.data['error'] + message = error_data.get('message', 'Operation failed') + code = error_data.get('code') + raise error.OpenAIError(message=message, code=code) + + def _poll( + self, + method, + url, + until, + failed, + params = None, + headers = None, + interval = None, + delay = None + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + if delay: + time.sleep(delay) + + response, b, api_key = self.request(method, url, params, headers) + self._check_polling_response(response, failed) + start_time = time.time() + while not until(response): + if time.time() - start_time > TIMEOUT_SECS: + raise error.Timeout("Operation polling timed out.") + + time.sleep(interval or response.retry_after or 10) + response, b, api_key = self.request(method, url, params, headers) + self._check_polling_response(response, failed) + + response.data = response.data['result'] + return response, b, api_key + + async def _apoll( + self, + method, + url, + until, + failed, + params = None, + headers = None, + interval = None, + delay = None + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + if delay: + await asyncio.sleep(delay) + + response, b, api_key = await self.arequest(method, url, params, headers) + self._check_polling_response(response, failed) + start_time = time.time() + while not until(response): + if time.time() - start_time > TIMEOUT_SECS: + raise error.Timeout("Operation polling timed out.") + + await asyncio.sleep(interval or response.retry_after or 10) + response, b, api_key = await self.arequest(method, url, params, headers) + self._check_polling_response(response, failed) + + response.data = response.data['result'] + return response, b, api_key + + @overload + def request( + self, + method, + url, + params, + headers, + files, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + *, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Iterator[OpenAIResponse], bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: Literal[False] = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[OpenAIResponse, bool, str]: + pass + + @overload + def request( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: bool = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: + pass + + def request( + self, + method, + url, + params=None, + headers=None, + files=None, + stream: bool = False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool, str]: + result = self.request_raw( + method.lower(), + url, + params=params, + supplied_headers=headers, + files=files, + stream=stream, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = self._interpret_response(result, stream) + return resp, got_stream, self.api_key + + @overload + async def arequest( + self, + method, + url, + params, + headers, + files, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + *, + stream: Literal[True], + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[AsyncGenerator[OpenAIResponse, None], bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: Literal[False] = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[OpenAIResponse, bool, str]: + pass + + @overload + async def arequest( + self, + method, + url, + params=..., + headers=..., + files=..., + stream: bool = ..., + request_id: Optional[str] = ..., + request_timeout: Optional[Union[float, Tuple[float, float]]] = ..., + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: + pass + + async def arequest( + self, + method, + url, + params=None, + headers=None, + files=None, + stream: bool = False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool, str]: + ctx = aiohttp_session() + session = await ctx.__aenter__() + try: + result = await self.arequest_raw( + method.lower(), + url, + session, + params=params, + supplied_headers=headers, + files=files, + request_id=request_id, + request_timeout=request_timeout, + ) + resp, got_stream = await self._interpret_async_response(result, stream) + except Exception: + await ctx.__aexit__(None, None, None) + raise + if got_stream: + + async def wrap_resp(): + assert isinstance(resp, AsyncGenerator) + try: + async for r in resp: + yield r + finally: + await ctx.__aexit__(None, None, None) + + return wrap_resp(), got_stream, self.api_key + else: + await ctx.__aexit__(None, None, None) + return resp, got_stream, self.api_key + + def handle_error_response(self, rbody, rcode, resp, rheaders, stream_error=False): + try: + error_data = resp["error"] + except (KeyError, TypeError): + raise error.APIError( + "Invalid response object from API: %r (HTTP response code " + "was %d)" % (rbody, rcode), + rbody, + rcode, + resp, + ) + + if "internal_message" in error_data: + error_data["message"] += "\n\n" + error_data["internal_message"] + + util.log_info( + "OpenAI API error received", + error_code=error_data.get("code"), + error_type=error_data.get("type"), + error_message=error_data.get("message"), + error_param=error_data.get("param"), + stream_error=stream_error, + ) + + # Rate limits were previously coded as 400's with code 'rate_limit' + if rcode == 429: + return error.RateLimitError( + error_data.get("message"), rbody, rcode, resp, rheaders + ) + elif rcode in [400, 404, 415]: + return error.InvalidRequestError( + error_data.get("message"), + error_data.get("param"), + error_data.get("code"), + rbody, + rcode, + resp, + rheaders, + ) + elif rcode == 401: + return error.AuthenticationError( + error_data.get("message"), rbody, rcode, resp, rheaders + ) + elif rcode == 403: + return error.PermissionError( + error_data.get("message"), rbody, rcode, resp, rheaders + ) + elif rcode == 409: + return error.TryAgain( + error_data.get("message"), rbody, rcode, resp, rheaders + ) + elif stream_error: + # TODO: we will soon attach status codes to stream errors + parts = [error_data.get("message"), "(Error occurred while streaming.)"] + message = " ".join([p for p in parts if p is not None]) + return error.APIError(message, rbody, rcode, resp, rheaders) + else: + return error.APIError( + f"{error_data.get('message')} {rbody} {rcode} {resp} {rheaders}", + rbody, + rcode, + resp, + rheaders, + ) + + def request_headers( + self, method: str, extra, request_id: Optional[str] + ) -> Dict[str, str]: + user_agent = "OpenAI/v1 PythonBindings/%s" % (version.VERSION,) + if openai.app_info: + user_agent += " " + self.format_app_info(openai.app_info) + + uname_without_node = " ".join( + v for k, v in platform.uname()._asdict().items() if k != "node" + ) + ua = { + "bindings_version": version.VERSION, + "httplib": "requests", + "lang": "python", + "lang_version": platform.python_version(), + "platform": platform.platform(), + "publisher": "openai", + "uname": uname_without_node, + } + if openai.app_info: + ua["application"] = openai.app_info + + headers = { + "X-OpenAI-Client-User-Agent": json.dumps(ua), + "User-Agent": user_agent, + } + + headers.update(util.api_key_to_header(self.api_type, self.api_key)) + + if self.organization: + headers["OpenAI-Organization"] = self.organization + + if self.api_version is not None and self.api_type == ApiType.OPEN_AI: + headers["OpenAI-Version"] = self.api_version + if request_id is not None: + headers["X-Request-Id"] = request_id + if openai.debug: + headers["OpenAI-Debug"] = "true" + headers.update(extra) + + return headers + + def _validate_headers( + self, supplied_headers: Optional[Dict[str, str]] + ) -> Dict[str, str]: + headers: Dict[str, str] = {} + if supplied_headers is None: + return headers + + if not isinstance(supplied_headers, dict): + raise TypeError("Headers must be a dictionary") + + for k, v in supplied_headers.items(): + if not isinstance(k, str): + raise TypeError("Header keys must be strings") + if not isinstance(v, str): + raise TypeError("Header values must be strings") + headers[k] = v + + # NOTE: It is possible to do more validation of the headers, but a request could always + # be made to the API manually with invalid headers, so we need to handle them server side. + + return headers + + def _prepare_request_raw( + self, + url, + supplied_headers, + method, + params, + files, + request_id: Optional[str], + ) -> Tuple[str, Dict[str, str], Optional[bytes]]: + abs_url = "%s%s" % (self.api_base, url) + headers = self._validate_headers(supplied_headers) + + data = None + if method == "get" or method == "delete": + if params: + encoded_params = urlencode( + [(k, v) for k, v in params.items() if v is not None] + ) + abs_url = _build_api_url(abs_url, encoded_params) + elif method in {"post", "put"}: + if params and files: + data = params + if params and not files: + data = json.dumps(params).encode() + headers["Content-Type"] = "application/json" + else: + raise error.APIConnectionError( + "Unrecognized HTTP method %r. This may indicate a bug in the " + "OpenAI bindings. Please contact us through our help center at help.openai.com for " + "assistance." % (method,) + ) + + headers = self.request_headers(method, headers, request_id) + + util.log_debug("Request to OpenAI API", method=method, path=abs_url) + util.log_debug("Post details", data=data, api_version=self.api_version) + + return abs_url, headers, data + + def request_raw( + self, + method, + url, + *, + params=None, + supplied_headers: Optional[Dict[str, str]] = None, + files=None, + stream: bool = False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> requests.Response: + abs_url, headers, data = self._prepare_request_raw( + url, supplied_headers, method, params, files, request_id + ) + + if not hasattr(_thread_context, "session"): + _thread_context.session = _make_session() + _thread_context.session_create_time = time.time() + elif ( + time.time() - getattr(_thread_context, "session_create_time", 0) + >= MAX_SESSION_LIFETIME_SECS + ): + _thread_context.session.close() + _thread_context.session = _make_session() + _thread_context.session_create_time = time.time() + try: + result = _thread_context.session.request( + method, + abs_url, + headers=headers, + data=data, + files=files, + stream=stream, + timeout=request_timeout if request_timeout else TIMEOUT_SECS, + proxies=_thread_context.session.proxies, + ) + except requests.exceptions.Timeout as e: + raise error.Timeout("Request timed out: {}".format(e)) from e + except requests.exceptions.RequestException as e: + raise error.APIConnectionError( + "Error communicating with OpenAI: {}".format(e) + ) from e + util.log_debug( + "OpenAI API response", + path=abs_url, + response_code=result.status_code, + processing_ms=result.headers.get("OpenAI-Processing-Ms"), + request_id=result.headers.get("X-Request-Id"), + ) + # Don't read the whole stream for debug logging unless necessary. + if openai.log == "debug": + util.log_debug( + "API response body", body=result.content, headers=result.headers + ) + return result + + async def arequest_raw( + self, + method, + url, + session, + *, + params=None, + supplied_headers: Optional[Dict[str, str]] = None, + files=None, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ) -> aiohttp.ClientResponse: + abs_url, headers, data = self._prepare_request_raw( + url, supplied_headers, method, params, files, request_id + ) + + if isinstance(request_timeout, tuple): + timeout = aiohttp.ClientTimeout( + connect=request_timeout[0], + total=request_timeout[1], + ) + else: + timeout = aiohttp.ClientTimeout( + total=request_timeout if request_timeout else TIMEOUT_SECS + ) + + if files: + # TODO: Use `aiohttp.MultipartWriter` to create the multipart form data here. + # For now we use the private `requests` method that is known to have worked so far. + data, content_type = requests.models.RequestEncodingMixin._encode_files( # type: ignore + files, data + ) + headers["Content-Type"] = content_type + request_kwargs = { + "method": method, + "url": abs_url, + "headers": headers, + "data": data, + "proxy": _aiohttp_proxies_arg(openai.proxy), + "timeout": timeout, + } + try: + result = await session.request(**request_kwargs) + util.log_info( + "OpenAI API response", + path=abs_url, + response_code=result.status, + processing_ms=result.headers.get("OpenAI-Processing-Ms"), + request_id=result.headers.get("X-Request-Id"), + ) + # Don't read the whole stream for debug logging unless necessary. + if openai.log == "debug": + util.log_debug( + "API response body", body=result.content, headers=result.headers + ) + return result + except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: + raise error.Timeout("Request timed out") from e + except aiohttp.ClientError as e: + raise error.APIConnectionError("Error communicating with OpenAI") from e + + def _interpret_response( + self, result: requests.Response, stream: bool + ) -> Tuple[Union[OpenAIResponse, Iterator[OpenAIResponse]], bool]: + """Returns the response(s) and a bool indicating whether it is a stream.""" + if stream and "text/event-stream" in result.headers.get("Content-Type", ""): + return ( + self._interpret_response_line( + line, result.status_code, result.headers, stream=True + ) + for line in parse_stream(result.iter_lines()) + ), True + else: + return ( + self._interpret_response_line( + result.content.decode("utf-8"), + result.status_code, + result.headers, + stream=False, + ), + False, + ) + + async def _interpret_async_response( + self, result: aiohttp.ClientResponse, stream: bool + ) -> Tuple[Union[OpenAIResponse, AsyncGenerator[OpenAIResponse, None]], bool]: + """Returns the response(s) and a bool indicating whether it is a stream.""" + if stream and "text/event-stream" in result.headers.get("Content-Type", ""): + return ( + self._interpret_response_line( + line, result.status, result.headers, stream=True + ) + async for line in parse_stream_async(result.content) + ), True + else: + try: + await result.read() + except (aiohttp.ServerTimeoutError, asyncio.TimeoutError) as e: + raise error.Timeout("Request timed out") from e + except aiohttp.ClientError as e: + util.log_warn(e, body=result.content) + return ( + self._interpret_response_line( + (await result.read()).decode("utf-8"), + result.status, + result.headers, + stream=False, + ), + False, + ) + + def _interpret_response_line( + self, rbody: str, rcode: int, rheaders, stream: bool + ) -> OpenAIResponse: + # HTTP 204 response code does not have any content in the body. + if rcode == 204: + return OpenAIResponse(None, rheaders) + + if rcode == 503: + raise error.ServiceUnavailableError( + "The server is overloaded or not ready yet.", + rbody, + rcode, + headers=rheaders, + ) + try: + if 'text/plain' in rheaders.get('Content-Type', ''): + data = rbody + else: + data = json.loads(rbody) + except (JSONDecodeError, UnicodeDecodeError) as e: + raise error.APIError( + f"HTTP code {rcode} from API ({rbody})", rbody, rcode, headers=rheaders + ) from e + resp = OpenAIResponse(data, rheaders) + # In the future, we might add a "status" parameter to errors + # to better handle the "error while streaming" case. + stream_error = stream and "error" in resp.data + if stream_error or not 200 <= rcode < 300: + raise self.handle_error_response( + rbody, rcode, resp.data, rheaders, stream_error=stream_error + ) + return resp + + +@asynccontextmanager +async def aiohttp_session() -> AsyncIterator[aiohttp.ClientSession]: + user_set_session = openai.aiosession.get() + if user_set_session: + yield user_set_session + else: + async with aiohttp.ClientSession() as session: + yield session diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__init__.py new file mode 100644 index 0000000..78bad1a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__init__.py @@ -0,0 +1,15 @@ +from openai.api_resources.audio import Audio # noqa: F401 +from openai.api_resources.chat_completion import ChatCompletion # noqa: F401 +from openai.api_resources.completion import Completion # noqa: F401 +from openai.api_resources.customer import Customer # noqa: F401 +from openai.api_resources.deployment import Deployment # noqa: F401 +from openai.api_resources.edit import Edit # noqa: F401 +from openai.api_resources.embedding import Embedding # noqa: F401 +from openai.api_resources.engine import Engine # noqa: F401 +from openai.api_resources.error_object import ErrorObject # noqa: F401 +from openai.api_resources.file import File # noqa: F401 +from openai.api_resources.fine_tune import FineTune # noqa: F401 +from openai.api_resources.fine_tuning import FineTuningJob # noqa: F401 +from openai.api_resources.image import Image # noqa: F401 +from openai.api_resources.model import Model # noqa: F401 +from openai.api_resources.moderation import Moderation # noqa: F401 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..506267e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/audio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/audio.cpython-310.pyc new file mode 100644 index 0000000..ab334ae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/audio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/chat_completion.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/chat_completion.cpython-310.pyc new file mode 100644 index 0000000..4665b75 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/chat_completion.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/completion.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/completion.cpython-310.pyc new file mode 100644 index 0000000..05fdb23 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/completion.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/customer.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/customer.cpython-310.pyc new file mode 100644 index 0000000..82277b9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/customer.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/deployment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/deployment.cpython-310.pyc new file mode 100644 index 0000000..6be2776 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/deployment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/edit.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/edit.cpython-310.pyc new file mode 100644 index 0000000..7056662 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/edit.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/embedding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/embedding.cpython-310.pyc new file mode 100644 index 0000000..a3ce3ba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/embedding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/engine.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/engine.cpython-310.pyc new file mode 100644 index 0000000..52d8d52 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/engine.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/error_object.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/error_object.cpython-310.pyc new file mode 100644 index 0000000..8144395 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/error_object.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/file.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/file.cpython-310.pyc new file mode 100644 index 0000000..6cd8fd8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/file.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tune.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tune.cpython-310.pyc new file mode 100644 index 0000000..005cc16 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tune.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tuning.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tuning.cpython-310.pyc new file mode 100644 index 0000000..d01e8c6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/fine_tuning.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/image.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/image.cpython-310.pyc new file mode 100644 index 0000000..b3f59ef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/image.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/model.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/model.cpython-310.pyc new file mode 100644 index 0000000..71e0b94 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/model.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/moderation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/moderation.cpython-310.pyc new file mode 100644 index 0000000..f24ca6f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/__pycache__/moderation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__init__.py new file mode 100644 index 0000000..48482bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__init__.py @@ -0,0 +1,13 @@ +# flake8: noqa + +from openai.api_resources.abstract.api_resource import APIResource +from openai.api_resources.abstract.createable_api_resource import CreateableAPIResource +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource +from openai.api_resources.abstract.listable_api_resource import ListableAPIResource +from openai.api_resources.abstract.nested_resource_class_methods import ( + nested_resource_class_methods, +) +from openai.api_resources.abstract.paginatable_api_resource import ( + PaginatableAPIResource, +) +from openai.api_resources.abstract.updateable_api_resource import UpdateableAPIResource diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..02bac62 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/api_resource.cpython-310.pyc new file mode 100644 index 0000000..dca6325 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-310.pyc new file mode 100644 index 0000000..413736b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/createable_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-310.pyc new file mode 100644 index 0000000..359db53 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/deletable_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-310.pyc new file mode 100644 index 0000000..20fa45c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/engine_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-310.pyc new file mode 100644 index 0000000..80e2ac7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/listable_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-310.pyc new file mode 100644 index 0000000..24450c9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/nested_resource_class_methods.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/paginatable_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/paginatable_api_resource.cpython-310.pyc new file mode 100644 index 0000000..a5cfef1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/paginatable_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-310.pyc new file mode 100644 index 0000000..6268e2f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/__pycache__/updateable_api_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/api_resource.py new file mode 100644 index 0000000..5d54bb9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/api_resource.py @@ -0,0 +1,172 @@ +from urllib.parse import quote_plus + +import openai +from openai import api_requestor, error, util +from openai.openai_object import OpenAIObject +from openai.util import ApiType +from typing import Optional + + +class APIResource(OpenAIObject): + api_prefix = "" + azure_api_prefix = "openai" + azure_deployments_prefix = "deployments" + + @classmethod + def retrieve( + cls, id, api_key=None, request_id=None, request_timeout=None, **params + ): + instance = cls(id=id, api_key=api_key, **params) + instance.refresh(request_id=request_id, request_timeout=request_timeout) + return instance + + @classmethod + def aretrieve( + cls, id, api_key=None, request_id=None, request_timeout=None, **params + ): + instance = cls(id=id, api_key=api_key, **params) + return instance.arefresh(request_id=request_id, request_timeout=request_timeout) + + def refresh(self, request_id=None, request_timeout=None): + self.refresh_from( + self.request( + "get", + self.instance_url(), + request_id=request_id, + request_timeout=request_timeout, + ) + ) + return self + + async def arefresh(self, request_id=None, request_timeout=None): + self.refresh_from( + await self.arequest( + "get", + self.instance_url(operation="refresh"), + request_id=request_id, + request_timeout=request_timeout, + ) + ) + return self + + @classmethod + def class_url(cls): + if cls == APIResource: + raise NotImplementedError( + "APIResource is an abstract class. You should perform actions on its subclasses." + ) + # Namespaces are separated in object names with periods (.) and in URLs + # with forward slashes (/), so replace the former with the latter. + base = cls.OBJECT_NAME.replace(".", "/") # type: ignore + if cls.api_prefix: + return "/%s/%s" % (cls.api_prefix, base) + return "/%s" % (base) + + def instance_url(self, operation=None): + id = self.get("id") + + if not isinstance(id, str): + raise error.InvalidRequestError( + "Could not determine which URL to request: %s instance " + "has invalid ID: %r, %s. ID should be of type `str` (or" + " `unicode`)" % (type(self).__name__, id, type(id)), + "id", + ) + api_version = self.api_version or openai.api_version + extn = quote_plus(id) + + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + if not api_version: + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) + + if not operation: + base = self.class_url() + return "/%s%s/%s?api-version=%s" % ( + self.azure_api_prefix, + base, + extn, + api_version, + ) + + return "/%s/%s/%s/%s?api-version=%s" % ( + self.azure_api_prefix, + self.azure_deployments_prefix, + extn, + operation, + api_version, + ) + + elif self.typed_api_type == ApiType.OPEN_AI: + base = self.class_url() + return "%s/%s" % (base, extn) + + else: + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) + + # The `method_` and `url_` arguments are suffixed with an underscore to + # avoid conflicting with actual request parameters in `params`. + @classmethod + def _static_request( + cls, + method_, + url_, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_version=api_version, + organization=organization, + api_base=api_base, + api_type=api_type, + ) + response, _, api_key = requestor.request( + method_, url_, params, request_id=request_id + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def _astatic_request( + cls, + method_, + url_, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_version=api_version, + organization=organization, + api_base=api_base, + api_type=api_type, + ) + response, _, api_key = await requestor.arequest( + method_, url_, params, request_id=request_id + ) + return response + + @classmethod + def _get_api_type_and_version( + cls, api_type: Optional[str] = None, api_version: Optional[str] = None + ): + typed_api_type = ( + ApiType.from_str(api_type) + if api_type + else ApiType.from_str(openai.api_type) + ) + typed_api_version = api_version or openai.api_version + return (typed_api_type, typed_api_version) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/createable_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/createable_api_resource.py new file mode 100644 index 0000000..1361c02 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/createable_api_resource.py @@ -0,0 +1,98 @@ +from openai import api_requestor, util, error +from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType + + +class CreateableAPIResource(APIResource): + plain_old_data = False + + @classmethod + def __prepare_create_requestor( + cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def create( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls.__prepare_create_requestor( + api_key, + api_base, + api_type, + api_version, + organization, + ) + + response, _, api_key = requestor.request( + "post", url, params, request_id=request_id + ) + + return util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + plain_old_data=cls.plain_old_data, + ) + + @classmethod + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls.__prepare_create_requestor( + api_key, + api_base, + api_type, + api_version, + organization, + ) + + response, _, api_key = await requestor.arequest( + "post", url, params, request_id=request_id + ) + + return util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + plain_old_data=cls.plain_old_data, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/deletable_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/deletable_api_resource.py new file mode 100644 index 0000000..a800ceb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/deletable_api_resource.py @@ -0,0 +1,48 @@ +from urllib.parse import quote_plus +from typing import Awaitable + +from openai import error +from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType + + +class DeletableAPIResource(APIResource): + @classmethod + def __prepare_delete(cls, sid, api_type=None, api_version=None): + if isinstance(cls, APIResource): + raise ValueError(".delete may only be called as a class method now.") + + base = cls.class_url() + extn = quote_plus(sid) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s?api-version=%s" % ( + cls.azure_api_prefix, + base, + extn, + api_version, + ) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s" % (base, extn) + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return url + + @classmethod + def delete(cls, sid, api_type=None, api_version=None, **params): + url = cls.__prepare_delete(sid, api_type, api_version) + + return cls._static_request( + "delete", url, api_type=api_type, api_version=api_version, **params + ) + + @classmethod + def adelete(cls, sid, api_type=None, api_version=None, **params) -> Awaitable: + url = cls.__prepare_delete(sid, api_type, api_version) + + return cls._astatic_request( + "delete", url, api_type=api_type, api_version=api_version, **params + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/engine_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/engine_api_resource.py new file mode 100644 index 0000000..1f172d8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/engine_api_resource.py @@ -0,0 +1,325 @@ +import time +from pydoc import apropos +from typing import Optional +from urllib.parse import quote_plus + +import openai +from openai import api_requestor, error, util +from openai.api_resources.abstract.api_resource import APIResource +from openai.openai_response import OpenAIResponse +from openai.util import ApiType + +MAX_TIMEOUT = 20 + + +class EngineAPIResource(APIResource): + plain_old_data = False + + def __init__(self, engine: Optional[str] = None, **kwargs): + super().__init__(engine=engine, **kwargs) + + @classmethod + def class_url( + cls, + engine: Optional[str] = None, + api_type: Optional[str] = None, + api_version: Optional[str] = None, + ): + # Namespaces are separated in object names with periods (.) and in URLs + # with forward slashes (/), so replace the former with the latter. + base = cls.OBJECT_NAME.replace(".", "/") # type: ignore + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + if not api_version: + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) + if engine is None: + raise error.InvalidRequestError( + "You must provide the deployment name in the 'engine' parameter to access the Azure OpenAI service" + ) + extn = quote_plus(engine) + return "/%s/%s/%s/%s?api-version=%s" % ( + cls.azure_api_prefix, + cls.azure_deployments_prefix, + extn, + base, + api_version, + ) + + elif typed_api_type == ApiType.OPEN_AI: + if engine is None: + return "/%s" % (base) + + extn = quote_plus(engine) + return "/engines/%s/%s" % (extn, base) + + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + @classmethod + def __prepare_create_request( + cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + deployment_id = params.pop("deployment_id", None) + engine = params.pop("engine", deployment_id) + model = params.get("model", None) + timeout = params.pop("timeout", None) + stream = params.get("stream", False) + headers = params.pop("headers", None) + request_timeout = params.pop("request_timeout", None) + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + if deployment_id is None and engine is None: + raise error.InvalidRequestError( + "Must provide an 'engine' or 'deployment_id' parameter to create a %s" + % cls, + "engine", + ) + else: + if model is None and engine is None: + raise error.InvalidRequestError( + "Must provide an 'engine' or 'model' parameter to create a %s" + % cls, + "engine", + ) + + if timeout is None: + # No special timeout handling + pass + elif timeout > 0: + # API only supports timeouts up to MAX_TIMEOUT + params["timeout"] = min(timeout, MAX_TIMEOUT) + timeout = (timeout - params["timeout"]) or None + elif timeout == 0: + params["timeout"] = MAX_TIMEOUT + + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + url = cls.class_url(engine, api_type, api_version) + return ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) + + @classmethod + def create( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) = cls.__prepare_create_request( + api_key, api_base, api_type, api_version, organization, **params + ) + + response, _, api_key = requestor.request( + "post", + url, + params=params, + headers=headers, + stream=stream, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + # must be an iterator + assert not isinstance(response, OpenAIResponse) + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + for line in response + ) + else: + obj = util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + + if timeout is not None: + obj.wait(timeout=timeout or None) + + return obj + + @classmethod + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + ( + deployment_id, + engine, + timeout, + stream, + headers, + request_timeout, + typed_api_type, + requestor, + url, + params, + ) = cls.__prepare_create_request( + api_key, api_base, api_type, api_version, organization, **params + ) + response, _, api_key = await requestor.arequest( + "post", + url, + params=params, + headers=headers, + stream=stream, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + # must be an iterator + assert not isinstance(response, OpenAIResponse) + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + async for line in response + ) + else: + obj = util.convert_to_openai_object( + response, + api_key, + api_version, + organization, + engine=engine, + plain_old_data=cls.plain_old_data, + ) + + if timeout is not None: + await obj.await_(timeout=timeout or None) + + return obj + + def instance_url(self): + id = self.get("id") + + if not isinstance(id, str): + raise error.InvalidRequestError( + f"Could not determine which URL to request: {type(self).__name__} instance has invalid ID: {id}, {type(id)}. ID should be of type str.", + "id", + ) + + extn = quote_plus(id) + params_connector = "?" + + if self.typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + api_version = self.api_version or openai.api_version + if not api_version: + raise error.InvalidRequestError( + "An API version is required for the Azure API type." + ) + base = self.OBJECT_NAME.replace(".", "/") + url = "/%s/%s/%s/%s/%s?api-version=%s" % ( + self.azure_api_prefix, + self.azure_deployments_prefix, + self.engine, + base, + extn, + api_version, + ) + params_connector = "&" + + elif self.typed_api_type == ApiType.OPEN_AI: + base = self.class_url(self.engine, self.api_type, self.api_version) + url = "%s/%s" % (base, extn) + + else: + raise error.InvalidAPIType("Unsupported API type %s" % self.api_type) + + timeout = self.get("timeout") + if timeout is not None: + timeout = quote_plus(str(timeout)) + url += params_connector + "timeout={}".format(timeout) + return url + + def wait(self, timeout=None): + start = time.time() + while self.status != "complete": + self.timeout = ( + min(timeout + start - time.time(), MAX_TIMEOUT) + if timeout is not None + else MAX_TIMEOUT + ) + if self.timeout < 0: + del self.timeout + break + self.refresh() + return self + + async def await_(self, timeout=None): + """Async version of `EngineApiResource.wait`""" + start = time.time() + while self.status != "complete": + self.timeout = ( + min(timeout + start - time.time(), MAX_TIMEOUT) + if timeout is not None + else MAX_TIMEOUT + ) + if self.timeout < 0: + del self.timeout + break + await self.arefresh() + return self diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/listable_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/listable_api_resource.py new file mode 100644 index 0000000..3e59979 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/listable_api_resource.py @@ -0,0 +1,95 @@ +from openai import api_requestor, util, error +from openai.api_resources.abstract.api_resource import APIResource +from openai.util import ApiType + + +class ListableAPIResource(APIResource): + @classmethod + def auto_paging_iter(cls, *args, **params): + return cls.list(*args, **params).auto_paging_iter() + + @classmethod + def __prepare_list_requestor( + cls, + api_key=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or cls.api_base(), + api_version=api_version, + api_type=api_type, + organization=organization, + ) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def list( + cls, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + response, _, api_key = requestor.request( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object + + @classmethod + async def alist( + cls, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + response, _, api_key = await requestor.arequest( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/nested_resource_class_methods.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/nested_resource_class_methods.py new file mode 100644 index 0000000..f171737 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/nested_resource_class_methods.py @@ -0,0 +1,167 @@ +from urllib.parse import quote_plus + +from openai import api_requestor, util + + +def _nested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, + async_=False, +): + if resource_plural is None: + resource_plural = "%ss" % resource + if path is None: + path = resource_plural + if operations is None: + raise ValueError("operations list required") + + def wrapper(cls): + def nested_resource_url(cls, id, nested_id=None): + url = "%s/%s/%s" % (cls.class_url(), quote_plus(id), quote_plus(path)) + if nested_id is not None: + url += "/%s" % quote_plus(nested_id) + return url + + resource_url_method = "%ss_url" % resource + setattr(cls, resource_url_method, classmethod(nested_resource_url)) + + def nested_resource_request( + cls, + method, + url, + api_key=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, api_version=api_version, organization=organization + ) + response, _, api_key = requestor.request( + method, url, params, request_id=request_id + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + async def anested_resource_request( + cls, + method, + url, + api_key=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, api_version=api_version, organization=organization + ) + response, _, api_key = await requestor.arequest( + method, url, params, request_id=request_id + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + resource_request_method = "%ss_request" % resource + setattr( + cls, + resource_request_method, + classmethod( + anested_resource_request if async_ else nested_resource_request + ), + ) + + for operation in operations: + if operation == "create": + + def create_nested_resource(cls, id, **params): + url = getattr(cls, resource_url_method)(id) + return getattr(cls, resource_request_method)("post", url, **params) + + create_method = "create_%s" % resource + setattr(cls, create_method, classmethod(create_nested_resource)) + + elif operation == "retrieve": + + def retrieve_nested_resource(cls, id, nested_id, **params): + url = getattr(cls, resource_url_method)(id, nested_id) + return getattr(cls, resource_request_method)("get", url, **params) + + retrieve_method = "retrieve_%s" % resource + setattr(cls, retrieve_method, classmethod(retrieve_nested_resource)) + + elif operation == "update": + + def modify_nested_resource(cls, id, nested_id, **params): + url = getattr(cls, resource_url_method)(id, nested_id) + return getattr(cls, resource_request_method)("post", url, **params) + + modify_method = "modify_%s" % resource + setattr(cls, modify_method, classmethod(modify_nested_resource)) + + elif operation == "delete": + + def delete_nested_resource(cls, id, nested_id, **params): + url = getattr(cls, resource_url_method)(id, nested_id) + return getattr(cls, resource_request_method)( + "delete", url, **params + ) + + delete_method = "delete_%s" % resource + setattr(cls, delete_method, classmethod(delete_nested_resource)) + + elif operation == "list": + + def list_nested_resources(cls, id, **params): + url = getattr(cls, resource_url_method)(id) + return getattr(cls, resource_request_method)("get", url, **params) + + list_method = "list_%s" % resource_plural + setattr(cls, list_method, classmethod(list_nested_resources)) + + elif operation == "paginated_list": + + def paginated_list_nested_resources( + cls, id, limit=None, after=None, **params + ): + url = getattr(cls, resource_url_method)(id) + return getattr(cls, resource_request_method)( + "get", url, limit=limit, after=after, **params + ) + + list_method = "list_%s" % resource_plural + setattr(cls, list_method, classmethod(paginated_list_nested_resources)) + + else: + raise ValueError("Unknown operation: %s" % operation) + + return cls + + return wrapper + + +def nested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, +): + return _nested_resource_class_methods( + resource, path, operations, resource_plural, async_=False + ) + + +def anested_resource_class_methods( + resource, + path=None, + operations=None, + resource_plural=None, +): + return _nested_resource_class_methods( + resource, path, operations, resource_plural, async_=True + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/paginatable_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/paginatable_api_resource.py new file mode 100644 index 0000000..2d75744 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/paginatable_api_resource.py @@ -0,0 +1,125 @@ +from openai import api_requestor, error, util +from openai.api_resources.abstract.listable_api_resource import ListableAPIResource +from openai.util import ApiType + + +class PaginatableAPIResource(ListableAPIResource): + @classmethod + def auto_paging_iter(cls, *args, **params): + next_cursor = None + has_more = True + if not params.get("limit"): + params["limit"] = 20 + while has_more: + if next_cursor: + params["after"] = next_cursor + response = cls.list(*args, **params) + + for item in response.data: + yield item + + if response.data: + next_cursor = response.data[-1].id + has_more = response.has_more + + @classmethod + def __prepare_list_requestor( + cls, + api_key=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or cls.api_base(), + api_version=api_version, + api_type=api_type, + organization=organization, + ) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + return requestor, url + + @classmethod + def list( + cls, + limit=None, + starting_after=None, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + params = { + **params, + "limit": limit, + "starting_after": starting_after, + } + + response, _, api_key = requestor.request( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object + + @classmethod + async def alist( + cls, + limit=None, + starting_after=None, + api_key=None, + request_id=None, + api_version=None, + organization=None, + api_base=None, + api_type=None, + **params, + ): + requestor, url = cls.__prepare_list_requestor( + api_key, + api_version, + organization, + api_base, + api_type, + ) + + params = { + **params, + "limit": limit, + "starting_after": starting_after, + } + + response, _, api_key = await requestor.arequest( + "get", url, params, request_id=request_id + ) + openai_object = util.convert_to_openai_object( + response, api_key, api_version, organization + ) + openai_object._retrieve_params = params + return openai_object diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/updateable_api_resource.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/updateable_api_resource.py new file mode 100644 index 0000000..245f9b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/abstract/updateable_api_resource.py @@ -0,0 +1,16 @@ +from urllib.parse import quote_plus +from typing import Awaitable + +from openai.api_resources.abstract.api_resource import APIResource + + +class UpdateableAPIResource(APIResource): + @classmethod + def modify(cls, sid, **params): + url = "%s/%s" % (cls.class_url(), quote_plus(sid)) + return cls._static_request("post", url, **params) + + @classmethod + def amodify(cls, sid, **params) -> Awaitable: + url = "%s/%s" % (cls.class_url(), quote_plus(sid)) + return cls._astatic_request("patch", url, **params) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/audio.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/audio.py new file mode 100644 index 0000000..d5d906e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/audio.py @@ -0,0 +1,285 @@ +from typing import Any, List + +import openai +from openai import api_requestor, util +from openai.api_resources.abstract import APIResource + + +class Audio(APIResource): + OBJECT_NAME = "audio" + + @classmethod + def _get_url(cls, action): + return cls.class_url() + f"/{action}" + + @classmethod + def _prepare_request( + cls, + file, + filename, + model, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + files: List[Any] = [] + data = { + "model": model, + **params, + } + files.append(("file", (filename, file, "application/octet-stream"))) + return requestor, files, data + + @classmethod + def transcribe( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("transcriptions") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def translate( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("translations") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def transcribe_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("transcriptions") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def translate_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("translations") + response, _, api_key = requestor.request("post", url, files=files, params=data) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranscribe( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("transcriptions") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranslate( + cls, + model, + file, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=file.name, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("translations") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranscribe_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("transcriptions") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def atranslate_raw( + cls, + model, + file, + filename, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor, files, data = cls._prepare_request( + file=file, + filename=filename, + model=model, + api_key=api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + **params, + ) + url = cls._get_url("translations") + response, _, api_key = await requestor.arequest( + "post", url, files=files, params=data + ) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/chat_completion.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/chat_completion.py new file mode 100644 index 0000000..7e55f9e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/chat_completion.py @@ -0,0 +1,50 @@ +import time + +from openai import util +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import TryAgain + + +class ChatCompletion(EngineAPIResource): + engine_required = False + OBJECT_NAME = "chat.completions" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new chat completion for the provided messages and parameters. + + See https://platform.openai.com/docs/api-reference/chat/create + for a list of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new chat completion for the provided messages and parameters. + + See https://platform.openai.com/docs/api-reference/chat/create + for a list of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/completion.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/completion.py new file mode 100644 index 0000000..7b9c44b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/completion.py @@ -0,0 +1,50 @@ +import time + +from openai import util +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import TryAgain + + +class Completion(EngineAPIResource): + OBJECT_NAME = "completions" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new completion for the provided prompt and parameters. + + See https://platform.openai.com/docs/api-reference/completions/create for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new completion for the provided prompt and parameters. + + See https://platform.openai.com/docs/api-reference/completions/create for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/customer.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/customer.py new file mode 100644 index 0000000..8690d07 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/customer.py @@ -0,0 +1,17 @@ +from openai.openai_object import OpenAIObject + + +class Customer(OpenAIObject): + @classmethod + def get_url(cls, customer, endpoint): + return f"/customer/{customer}/{endpoint}" + + @classmethod + def create(cls, customer, endpoint, **params): + instance = cls() + return instance.request("post", cls.get_url(customer, endpoint), params) + + @classmethod + def acreate(cls, customer, endpoint, **params): + instance = cls() + return instance.arequest("post", cls.get_url(customer, endpoint), params) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/deployment.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/deployment.py new file mode 100644 index 0000000..2f3fcd1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/deployment.py @@ -0,0 +1,119 @@ +from openai import util +from openai.api_resources.abstract import ( + DeletableAPIResource, + ListableAPIResource, + CreateableAPIResource, +) +from openai.error import InvalidRequestError, APIError + + +class Deployment(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): + OBJECT_NAME = "deployments" + + @classmethod + def _check_create(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None + ) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type." + ) + + if kwargs.get("model", None) is None: + raise InvalidRequestError( + "Must provide a 'model' parameter to create a Deployment.", + param="model", + ) + + scale_settings = kwargs.get("scale_settings", None) + if scale_settings is None: + raise InvalidRequestError( + "Must provide a 'scale_settings' parameter to create a Deployment.", + param="scale_settings", + ) + + if "scale_type" not in scale_settings or ( + scale_settings["scale_type"].lower() == "manual" + and "capacity" not in scale_settings + ): + raise InvalidRequestError( + "The 'scale_settings' parameter contains invalid or incomplete values.", + param="scale_settings", + ) + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new deployment for the provided prompt and parameters. + """ + cls._check_create(*args, **kwargs) + return super().create(*args, **kwargs) + + @classmethod + def acreate(cls, *args, **kwargs): + """ + Creates a new deployment for the provided prompt and parameters. + """ + cls._check_create(*args, **kwargs) + return super().acreate(*args, **kwargs) + + @classmethod + def _check_list(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None + ) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type." + ) + + @classmethod + def list(cls, *args, **kwargs): + cls._check_list(*args, **kwargs) + return super().list(*args, **kwargs) + + @classmethod + def alist(cls, *args, **kwargs): + cls._check_list(*args, **kwargs) + return super().alist(*args, **kwargs) + + @classmethod + def _check_delete(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None + ) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type." + ) + + @classmethod + def delete(cls, *args, **kwargs): + cls._check_delete(*args, **kwargs) + return super().delete(*args, **kwargs) + + @classmethod + def adelete(cls, *args, **kwargs): + cls._check_delete(*args, **kwargs) + return super().adelete(*args, **kwargs) + + @classmethod + def _check_retrieve(cls, *args, **kwargs): + typed_api_type, _ = cls._get_api_type_and_version( + kwargs.get("api_type", None), None + ) + if typed_api_type not in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise APIError( + "Deployment operations are only available for the Azure API type." + ) + + @classmethod + def retrieve(cls, *args, **kwargs): + cls._check_retrieve(*args, **kwargs) + return super().retrieve(*args, **kwargs) + + @classmethod + def aretrieve(cls, *args, **kwargs): + cls._check_retrieve(*args, **kwargs) + return super().aretrieve(*args, **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/edit.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/edit.py new file mode 100644 index 0000000..985f062 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/edit.py @@ -0,0 +1,57 @@ +import time + +from openai import util, error +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.error import TryAgain + + +class Edit(EngineAPIResource): + OBJECT_NAME = "edits" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new edit for the provided input, instruction, and parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + api_type = kwargs.pop("api_type", None) + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType( + "This operation is not supported by the Azure OpenAI API yet." + ) + + while True: + try: + return super().create(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new edit for the provided input, instruction, and parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + api_type = kwargs.pop("api_type", None) + typed_api_type = cls._get_api_type_and_version(api_type=api_type)[0] + if typed_api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType( + "This operation is not supported by the Azure OpenAI API yet." + ) + + while True: + try: + return await super().acreate(*args, **kwargs) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/embedding.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/embedding.py new file mode 100644 index 0000000..e937636 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/embedding.py @@ -0,0 +1,91 @@ +import base64 +import time + +from openai import util +from openai.api_resources.abstract.engine_api_resource import EngineAPIResource +from openai.datalib.numpy_helper import assert_has_numpy +from openai.datalib.numpy_helper import numpy as np +from openai.error import TryAgain + + +class Embedding(EngineAPIResource): + OBJECT_NAME = "embeddings" + + @classmethod + def create(cls, *args, **kwargs): + """ + Creates a new embedding for the provided input and parameters. + + See https://platform.openai.com/docs/api-reference/embeddings for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + user_provided_encoding_format = kwargs.get("encoding_format", None) + + # If encoding format was not explicitly specified, we opaquely use base64 for performance + if not user_provided_encoding_format: + kwargs["encoding_format"] = "base64" + + while True: + try: + response = super().create(*args, **kwargs) + + # If a user specifies base64, we'll just return the encoded string. + # This is only for the default case. + if not user_provided_encoding_format: + for data in response.data: + + # If an engine isn't using this optimization, don't do anything + if type(data["embedding"]) == str: + assert_has_numpy() + data["embedding"] = np.frombuffer( + base64.b64decode(data["embedding"]), dtype="float32" + ).tolist() + + return response + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + @classmethod + async def acreate(cls, *args, **kwargs): + """ + Creates a new embedding for the provided input and parameters. + + See https://platform.openai.com/docs/api-reference/embeddings for a list + of valid parameters. + """ + start = time.time() + timeout = kwargs.pop("timeout", None) + + user_provided_encoding_format = kwargs.get("encoding_format", None) + + # If encoding format was not explicitly specified, we opaquely use base64 for performance + if not user_provided_encoding_format: + kwargs["encoding_format"] = "base64" + + while True: + try: + response = await super().acreate(*args, **kwargs) + + # If a user specifies base64, we'll just return the encoded string. + # This is only for the default case. + if not user_provided_encoding_format: + for data in response.data: + + # If an engine isn't using this optimization, don't do anything + if type(data["embedding"]) == str: + data["embedding"] = np.frombuffer( + base64.b64decode(data["embedding"]), dtype="float32" + ).tolist() + + return response + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/engine.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/engine.py new file mode 100644 index 0000000..5a0c467 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/engine.py @@ -0,0 +1,50 @@ +import time +import warnings + +from openai import util +from openai.api_resources.abstract import ListableAPIResource, UpdateableAPIResource +from openai.error import TryAgain + + +class Engine(ListableAPIResource, UpdateableAPIResource): + OBJECT_NAME = "engines" + + def generate(self, timeout=None, **params): + start = time.time() + while True: + try: + return self.request( + "post", + self.instance_url() + "/generate", + params, + stream=params.get("stream"), + plain_old_data=True, + ) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + async def agenerate(self, timeout=None, **params): + start = time.time() + while True: + try: + return await self.arequest( + "post", + self.instance_url() + "/generate", + params, + stream=params.get("stream"), + plain_old_data=True, + ) + except TryAgain as e: + if timeout is not None and time.time() > start + timeout: + raise + + util.log_info("Waiting for model to warm up", error=e) + + def embeddings(self, **params): + warnings.warn( + "Engine.embeddings is deprecated, use Embedding.create", DeprecationWarning + ) + return self.request("post", self.instance_url() + "/embeddings", params) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/error_object.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/error_object.py new file mode 100644 index 0000000..555dc35 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/error_object.py @@ -0,0 +1,28 @@ +from typing import Optional + +from openai.openai_object import OpenAIObject +from openai.util import merge_dicts + + +class ErrorObject(OpenAIObject): + def refresh_from( + self, + values, + api_key=None, + api_version=None, + api_type=None, + organization=None, + response_ms: Optional[int] = None, + ): + # Unlike most other API resources, the API will omit attributes in + # error objects when they have a null value. We manually set default + # values here to facilitate generic error handling. + values = merge_dicts({"message": None, "type": None}, values) + return super(ErrorObject, self).refresh_from( + values=values, + api_key=api_key, + api_version=api_version, + api_type=api_type, + organization=organization, + response_ms=response_ms, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__init__.py new file mode 100644 index 0000000..d24c7b0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__init__.py @@ -0,0 +1,3 @@ +from openai.api_resources.experimental.completion_config import ( # noqa: F401 + CompletionConfig, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0986eb3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/completion_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/completion_config.cpython-310.pyc new file mode 100644 index 0000000..2650222 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/__pycache__/completion_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/completion_config.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/completion_config.py new file mode 100644 index 0000000..5d4feb4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/experimental/completion_config.py @@ -0,0 +1,11 @@ +from openai.api_resources.abstract import ( + CreateableAPIResource, + DeletableAPIResource, + ListableAPIResource, +) + + +class CompletionConfig( + CreateableAPIResource, ListableAPIResource, DeletableAPIResource +): + OBJECT_NAME = "experimental.completion_configs" diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/file.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/file.py new file mode 100644 index 0000000..dba2ee9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/file.py @@ -0,0 +1,279 @@ +import json +import os +from typing import cast +import time + +import openai +from openai import api_requestor, util, error +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource +from openai.util import ApiType + + +class File(ListableAPIResource, DeletableAPIResource): + OBJECT_NAME = "files" + + @classmethod + def __prepare_file_create( + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + user_provided_filename=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = "/%s%s?api-version=%s" % (cls.azure_api_prefix, base, api_version) + elif typed_api_type == ApiType.OPEN_AI: + url = cls.class_url() + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + # Set the filename on 'purpose' and 'model' to None so they are + # interpreted as form data. + files = [("purpose", (None, purpose))] + if model is not None: + files.append(("model", (None, model))) + if user_provided_filename is not None: + files.append( + ("file", (user_provided_filename, file, "application/octet-stream")) + ) + else: + files.append(("file", ("file", file, "application/octet-stream"))) + + return requestor, url, files + + @classmethod + def create( + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + user_provided_filename=None, + ): + requestor, url, files = cls.__prepare_file_create( + file, + purpose, + model, + api_key, + api_base, + api_type, + api_version, + organization, + user_provided_filename, + ) + response, _, api_key = requestor.request("post", url, files=files) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def acreate( + cls, + file, + purpose, + model=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + user_provided_filename=None, + ): + requestor, url, files = cls.__prepare_file_create( + file, + purpose, + model, + api_key, + api_base, + api_type, + api_version, + organization, + user_provided_filename, + ) + response, _, api_key = await requestor.arequest("post", url, files=files) + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def __prepare_file_download( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + base = cls.class_url() + url = f"/{cls.azure_api_prefix}{base}/{id}/content?api-version={api_version}" + elif typed_api_type == ApiType.OPEN_AI: + url = f"{cls.class_url()}/{id}/content" + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + return requestor, url + + @classmethod + def download( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + requestor, url = cls.__prepare_file_download( + id, api_key, api_base, api_type, api_version, organization + ) + + result = requestor.request_raw("get", url) + if not 200 <= result.status_code < 300: + raise requestor.handle_error_response( + result.content, + result.status_code, + json.loads(cast(bytes, result.content)), + result.headers, + stream_error=False, + ) + return result.content + + @classmethod + async def adownload( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + requestor, url = cls.__prepare_file_download( + id, api_key, api_base, api_type, api_version, organization + ) + + async with api_requestor.aiohttp_session() as session: + result = await requestor.arequest_raw("get", url, session) + if not 200 <= result.status < 300: + raise requestor.handle_error_response( + result.content, + result.status, + json.loads(cast(bytes, result.content)), + result.headers, + stream_error=False, + ) + return result.content + + @classmethod + def __find_matching_files(cls, name, bytes, all_files, purpose): + matching_files = [] + basename = os.path.basename(name) + for f in all_files: + if f["purpose"] != purpose: + continue + file_basename = os.path.basename(f["filename"]) + if file_basename != basename: + continue + if "bytes" in f and f["bytes"] != bytes: + continue + if "size" in f and int(f["size"]) != bytes: + continue + matching_files.append(f) + return matching_files + + @classmethod + def find_matching_files( + cls, + name, + bytes, + purpose, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + """Find already uploaded files with the same name, size, and purpose.""" + all_files = cls.list( + api_key=api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ).get("data", []) + return cls.__find_matching_files(name, bytes, all_files, purpose) + + @classmethod + async def afind_matching_files( + cls, + name, + bytes, + purpose, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + ): + """Find already uploaded files with the same name, size, and purpose.""" + all_files = ( + await cls.alist( + api_key=api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + ).get("data", []) + return cls.__find_matching_files(name, bytes, all_files, purpose) + + @classmethod + def wait_for_processing(cls, id, max_wait_seconds=30 * 60): + TERMINAL_STATES = ["processed", "error", "deleted"] + + start = time.time() + file = cls.retrieve(id=id) + while file.status not in TERMINAL_STATES: + file = cls.retrieve(id=id) + time.sleep(5.0) + if time.time() - start > max_wait_seconds: + raise openai.error.OpenAIError( + message="Giving up on waiting for file {id} to finish processing after {max_wait_seconds} seconds.".format( + id=id, max_wait_seconds=max_wait_seconds + ) + ) + return file.status diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tune.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tune.py new file mode 100644 index 0000000..45e3cf2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tune.py @@ -0,0 +1,204 @@ +from urllib.parse import quote_plus + +from openai import api_requestor, util, error +from openai.api_resources.abstract import ( + CreateableAPIResource, + ListableAPIResource, + nested_resource_class_methods, +) +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource +from openai.openai_response import OpenAIResponse +from openai.util import ApiType + + +@nested_resource_class_methods("event", operations=["list"]) +class FineTune(ListableAPIResource, CreateableAPIResource, DeletableAPIResource): + OBJECT_NAME = "fine-tunes" + + @classmethod + def _prepare_cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + base = cls.class_url() + extn = quote_plus(id) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/cancel?api-version=%s" % ( + cls.azure_api_prefix, + base, + extn, + api_version, + ) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/cancel" % (base, extn) + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + instance = cls(id, api_key, **params) + return instance, url + + @classmethod + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.request("post", url, request_id=request_id) + + @classmethod + def acancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.arequest("post", url, request_id=request_id) + + @classmethod + def _prepare_stream_events( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + base = cls.class_url() + extn = quote_plus(id) + + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/events?stream=true&api-version=%s" % ( + cls.azure_api_prefix, + base, + extn, + api_version, + ) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/events?stream=true" % (base, extn) + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + return requestor, url + + @classmethod + def stream_events( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls._prepare_stream_events( + id, + api_key, + api_base, + api_type, + request_id, + api_version, + organization, + **params, + ) + + response, _, api_key = requestor.request( + "get", url, params, stream=True, request_id=request_id + ) + + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + ) + for line in response + ) + + @classmethod + async def astream_events( + cls, + id, + api_key=None, + api_base=None, + api_type=None, + request_id=None, + api_version=None, + organization=None, + **params, + ): + requestor, url = cls._prepare_stream_events( + id, + api_key, + api_base, + api_type, + request_id, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest( + "get", url, params, stream=True, request_id=request_id + ) + + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + api_version, + organization, + ) + async for line in response + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tuning.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tuning.py new file mode 100644 index 0000000..f03be56 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/fine_tuning.py @@ -0,0 +1,88 @@ +from urllib.parse import quote_plus + +from openai import error +from openai.api_resources.abstract import ( + CreateableAPIResource, + PaginatableAPIResource, + nested_resource_class_methods, +) +from openai.api_resources.abstract.deletable_api_resource import DeletableAPIResource +from openai.util import ApiType + + +@nested_resource_class_methods("event", operations=["paginated_list"]) +class FineTuningJob( + PaginatableAPIResource, CreateableAPIResource, DeletableAPIResource +): + OBJECT_NAME = "fine_tuning.jobs" + + @classmethod + def _prepare_cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + base = cls.class_url() + extn = quote_plus(id) + + typed_api_type, api_version = cls._get_api_type_and_version( + api_type, api_version + ) + if typed_api_type in (ApiType.AZURE, ApiType.AZURE_AD): + url = "/%s%s/%s/cancel?api-version=%s" % ( + cls.azure_api_prefix, + base, + extn, + api_version, + ) + elif typed_api_type == ApiType.OPEN_AI: + url = "%s/%s/cancel" % (base, extn) + else: + raise error.InvalidAPIType("Unsupported API type %s" % api_type) + + instance = cls(id, api_key, **params) + return instance, url + + @classmethod + def cancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.request("post", url, request_id=request_id) + + @classmethod + def acancel( + cls, + id, + api_key=None, + api_type=None, + request_id=None, + api_version=None, + **params, + ): + instance, url = cls._prepare_cancel( + id, + api_key, + api_type, + request_id, + api_version, + **params, + ) + return instance.arequest("post", url, request_id=request_id) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/image.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/image.py new file mode 100644 index 0000000..1522923 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/image.py @@ -0,0 +1,273 @@ +# WARNING: This interface is considered experimental and may changed in the future without warning. +from typing import Any, List + +import openai +from openai import api_requestor, error, util +from openai.api_resources.abstract import APIResource + + +class Image(APIResource): + OBJECT_NAME = "images" + + @classmethod + def _get_url(cls, action, azure_action, api_type, api_version): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD) and azure_action is not None: + return f"/{cls.azure_api_prefix}{cls.class_url()}/{action}:{azure_action}?api-version={api_version}" + else: + return f"{cls.class_url()}/{action}" + + @classmethod + def create( + cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + response, _, api_key = requestor.request( + "post", cls._get_url("generations", azure_action="submit", api_type=api_type, api_version=api_version), params + ) + + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + requestor.api_base = "" # operation_location is a full url + response, _, api_key = requestor._poll( + "get", response.operation_location, + until=lambda response: response.data['status'] in [ 'succeeded' ], + failed=lambda response: response.data['status'] in [ 'failed' ] + ) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def acreate( + cls, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + response, _, api_key = await requestor.arequest( + "post", cls._get_url("generations", azure_action="submit", api_type=api_type, api_version=api_version), params + ) + + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + requestor.api_base = "" # operation_location is a full url + response, _, api_key = await requestor._apoll( + "get", response.operation_location, + until=lambda response: response.data['status'] in [ 'succeeded' ], + failed=lambda response: response.data['status'] in [ 'failed' ] + ) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def _prepare_create_variation( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + url = cls._get_url("variations", azure_action=None, api_type=api_type, api_version=api_version) + + files: List[Any] = [] + for key, value in params.items(): + files.append((key, (None, value))) + files.append(("image", ("image", image, "application/octet-stream"))) + return requestor, url, files + + @classmethod + def create_variation( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") + + requestor, url, files = cls._prepare_create_variation( + image, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = requestor.request("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def acreate_variation( + cls, + image, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Variations are not supported by the Azure OpenAI API yet.") + + requestor, url, files = cls._prepare_create_variation( + image, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + def _prepare_create_edit( + cls, + image, + mask=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + requestor = api_requestor.APIRequestor( + api_key, + api_base=api_base or openai.api_base, + api_type=api_type, + api_version=api_version, + organization=organization, + ) + api_type, api_version = cls._get_api_type_and_version(api_type, api_version) + + url = cls._get_url("edits", azure_action=None, api_type=api_type, api_version=api_version) + + files: List[Any] = [] + for key, value in params.items(): + files.append((key, (None, value))) + files.append(("image", ("image", image, "application/octet-stream"))) + if mask is not None: + files.append(("mask", ("mask", mask, "application/octet-stream"))) + return requestor, url, files + + @classmethod + def create_edit( + cls, + image, + mask=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") + + requestor, url, files = cls._prepare_create_edit( + image, + mask, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = requestor.request("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) + + @classmethod + async def acreate_edit( + cls, + image, + mask=None, + api_key=None, + api_base=None, + api_type=None, + api_version=None, + organization=None, + **params, + ): + if api_type in (util.ApiType.AZURE, util.ApiType.AZURE_AD): + raise error.InvalidAPIType("Edits are not supported by the Azure OpenAI API yet.") + + requestor, url, files = cls._prepare_create_edit( + image, + mask, + api_key, + api_base, + api_type, + api_version, + organization, + **params, + ) + + response, _, api_key = await requestor.arequest("post", url, files=files) + + return util.convert_to_openai_object( + response, api_key, api_version, organization + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/model.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/model.py new file mode 100644 index 0000000..9785e17 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/model.py @@ -0,0 +1,5 @@ +from openai.api_resources.abstract import DeletableAPIResource, ListableAPIResource + + +class Model(ListableAPIResource, DeletableAPIResource): + OBJECT_NAME = "models" diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/moderation.py b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/moderation.py new file mode 100644 index 0000000..bd19646 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/api_resources/moderation.py @@ -0,0 +1,45 @@ +from typing import List, Optional, Union + +from openai.openai_object import OpenAIObject + + +class Moderation(OpenAIObject): + VALID_MODEL_NAMES: List[str] = ["text-moderation-stable", "text-moderation-latest"] + + @classmethod + def get_url(cls): + return "/moderations" + + @classmethod + def _prepare_create(cls, input, model, api_key): + if model is not None and model not in cls.VALID_MODEL_NAMES: + raise ValueError( + f"The parameter model should be chosen from {cls.VALID_MODEL_NAMES} " + f"and it is default to be None." + ) + + instance = cls(api_key=api_key) + params = {"input": input} + if model is not None: + params["model"] = model + return instance, params + + @classmethod + def create( + cls, + input: Union[str, List[str]], + model: Optional[str] = None, + api_key: Optional[str] = None, + ): + instance, params = cls._prepare_create(input, model, api_key) + return instance.request("post", cls.get_url(), params) + + @classmethod + def acreate( + cls, + input: Union[str, List[str]], + model: Optional[str] = None, + api_key: Optional[str] = None, + ): + instance, params = cls._prepare_create(input, model, api_key) + return instance.arequest("post", cls.get_url(), params) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/cli.py b/.pythonlibs/lib/python3.10/site-packages/openai/cli.py new file mode 100644 index 0000000..99d171a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/cli.py @@ -0,0 +1,1410 @@ +import datetime +import os +import signal +import sys +import warnings +from typing import Optional + +import requests + +import openai +from openai.upload_progress import BufferReader +from openai.validators import ( + apply_necessary_remediation, + apply_validators, + get_validators, + read_any_format, + write_out_file, +) + + +class bcolors: + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + + +def organization_info(obj): + organization = getattr(obj, "organization", None) + if organization is not None: + return "[organization={}] ".format(organization) + else: + return "" + + +def display(obj): + sys.stderr.write(organization_info(obj)) + sys.stderr.flush() + print(obj) + + +def display_error(e): + extra = ( + " (HTTP status code: {})".format(e.http_status) + if e.http_status is not None + else "" + ) + sys.stderr.write( + "{}{}Error:{} {}{}\n".format( + organization_info(e), bcolors.FAIL, bcolors.ENDC, e, extra + ) + ) + + +class Engine: + @classmethod + def get(cls, args): + engine = openai.Engine.retrieve(id=args.id) + display(engine) + + @classmethod + def update(cls, args): + engine = openai.Engine.modify(args.id, replicas=args.replicas) + display(engine) + + @classmethod + def generate(cls, args): + warnings.warn( + "Engine.generate is deprecated, use Completion.create", DeprecationWarning + ) + if args.completions and args.completions > 1 and args.stream: + raise ValueError("Can't stream multiple completions with openai CLI") + + kwargs = {} + if args.model is not None: + kwargs["model"] = args.model + resp = openai.Engine(id=args.id).generate( + completions=args.completions, + context=args.context, + length=args.length, + stream=args.stream, + temperature=args.temperature, + top_p=args.top_p, + logprobs=args.logprobs, + stop=args.stop, + **kwargs, + ) + if not args.stream: + resp = [resp] + + for part in resp: + completions = len(part["data"]) + for c_idx, c in enumerate(part["data"]): + if completions > 1: + sys.stdout.write("===== Completion {} =====\n".format(c_idx)) + sys.stdout.write("".join(c["text"])) + if completions > 1: + sys.stdout.write("\n") + sys.stdout.flush() + + @classmethod + def list(cls, args): + engines = openai.Engine.list() + display(engines) + + +class ChatCompletion: + @classmethod + def create(cls, args): + if args.n is not None and args.n > 1 and args.stream: + raise ValueError( + "Can't stream chat completions with n>1 with the current CLI" + ) + + messages = [ + {"role": role, "content": content} for role, content in args.message + ] + + resp = openai.ChatCompletion.create( + # Required + model=args.model, + engine=args.engine, + messages=messages, + # Optional + n=args.n, + max_tokens=args.max_tokens, + temperature=args.temperature, + top_p=args.top_p, + stop=args.stop, + stream=args.stream, + ) + if not args.stream: + resp = [resp] + + for part in resp: + choices = part["choices"] + for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): + if len(choices) > 1: + sys.stdout.write("===== Chat Completion {} =====\n".format(c_idx)) + if args.stream: + delta = c["delta"] + if "content" in delta: + sys.stdout.write(delta["content"]) + else: + sys.stdout.write(c["message"]["content"]) + if len(choices) > 1: # not in streams + sys.stdout.write("\n") + sys.stdout.flush() + + +class Completion: + @classmethod + def create(cls, args): + if args.n is not None and args.n > 1 and args.stream: + raise ValueError("Can't stream completions with n>1 with the current CLI") + + if args.engine and args.model: + warnings.warn( + "In most cases, you should not be specifying both engine and model." + ) + + resp = openai.Completion.create( + engine=args.engine, + model=args.model, + n=args.n, + max_tokens=args.max_tokens, + logprobs=args.logprobs, + prompt=args.prompt, + stream=args.stream, + temperature=args.temperature, + top_p=args.top_p, + stop=args.stop, + echo=True, + ) + if not args.stream: + resp = [resp] + + for part in resp: + choices = part["choices"] + for c_idx, c in enumerate(sorted(choices, key=lambda s: s["index"])): + if len(choices) > 1: + sys.stdout.write("===== Completion {} =====\n".format(c_idx)) + sys.stdout.write(c["text"]) + if len(choices) > 1: + sys.stdout.write("\n") + sys.stdout.flush() + + +class Deployment: + @classmethod + def get(cls, args): + resp = openai.Deployment.retrieve(id=args.id) + print(resp) + + @classmethod + def delete(cls, args): + model = openai.Deployment.delete(args.id) + print(model) + + @classmethod + def list(cls, args): + models = openai.Deployment.list() + print(models) + + @classmethod + def create(cls, args): + models = openai.Deployment.create( + model=args.model, scale_settings={"scale_type": args.scale_type} + ) + print(models) + + +class Model: + @classmethod + def get(cls, args): + resp = openai.Model.retrieve(id=args.id) + print(resp) + + @classmethod + def delete(cls, args): + model = openai.Model.delete(args.id) + print(model) + + @classmethod + def list(cls, args): + models = openai.Model.list() + print(models) + + +class File: + @classmethod + def create(cls, args): + with open(args.file, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + resp = openai.File.create( + file=buffer_reader, + purpose=args.purpose, + user_provided_filename=args.file, + ) + print(resp) + + @classmethod + def get(cls, args): + resp = openai.File.retrieve(id=args.id) + print(resp) + + @classmethod + def delete(cls, args): + file = openai.File.delete(args.id) + print(file) + + @classmethod + def list(cls, args): + file = openai.File.list() + print(file) + + +class Image: + @classmethod + def create(cls, args): + resp = openai.Image.create( + prompt=args.prompt, + size=args.size, + n=args.num_images, + response_format=args.response_format, + ) + print(resp) + + @classmethod + def create_variation(cls, args): + with open(args.image, "rb") as file_reader: + buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") + resp = openai.Image.create_variation( + image=buffer_reader, + size=args.size, + n=args.num_images, + response_format=args.response_format, + ) + print(resp) + + @classmethod + def create_edit(cls, args): + with open(args.image, "rb") as file_reader: + image_reader = BufferReader(file_reader.read(), desc="Upload progress") + mask_reader = None + if args.mask is not None: + with open(args.mask, "rb") as file_reader: + mask_reader = BufferReader(file_reader.read(), desc="Upload progress") + resp = openai.Image.create_edit( + image=image_reader, + mask=mask_reader, + prompt=args.prompt, + size=args.size, + n=args.num_images, + response_format=args.response_format, + ) + print(resp) + + +class Audio: + @classmethod + def transcribe(cls, args): + with open(args.file, "rb") as r: + file_reader = BufferReader(r.read(), desc="Upload progress") + + resp = openai.Audio.transcribe_raw( + # Required + model=args.model, + file=file_reader, + filename=args.file, + # Optional + response_format=args.response_format, + language=args.language, + temperature=args.temperature, + prompt=args.prompt, + ) + print(resp) + + @classmethod + def translate(cls, args): + with open(args.file, "rb") as r: + file_reader = BufferReader(r.read(), desc="Upload progress") + resp = openai.Audio.translate_raw( + # Required + model=args.model, + file=file_reader, + filename=args.file, + # Optional + response_format=args.response_format, + language=args.language, + temperature=args.temperature, + prompt=args.prompt, + ) + print(resp) + + +class FineTune: + @classmethod + def list(cls, args): + resp = openai.FineTune.list() + print(resp) + + @classmethod + def _is_url(cls, file: str): + return file.lower().startswith("http") + + @classmethod + def _download_file_from_public_url(cls, url: str) -> Optional[bytes]: + resp = requests.get(url) + if resp.status_code == 200: + return resp.content + else: + return None + + @classmethod + def _maybe_upload_file( + cls, + file: Optional[str] = None, + content: Optional[bytes] = None, + user_provided_file: Optional[str] = None, + check_if_file_exists: bool = True, + ): + # Exactly one of `file` or `content` must be provided + if (file is None) == (content is None): + raise ValueError("Exactly one of `file` or `content` must be provided") + + if content is None: + assert file is not None + with open(file, "rb") as f: + content = f.read() + + if check_if_file_exists: + bytes = len(content) + matching_files = openai.File.find_matching_files( + name=user_provided_file or f.name, bytes=bytes, purpose="fine-tune" + ) + if len(matching_files) > 0: + file_ids = [f["id"] for f in matching_files] + sys.stdout.write( + "Found potentially duplicated files with name '{name}', purpose 'fine-tune' and size {size} bytes\n".format( + name=os.path.basename(matching_files[0]["filename"]), + size=matching_files[0]["bytes"] + if "bytes" in matching_files[0] + else matching_files[0]["size"], + ) + ) + sys.stdout.write("\n".join(file_ids)) + while True: + sys.stdout.write( + "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " + ) + inp = sys.stdin.readline().strip() + if inp in file_ids: + sys.stdout.write( + "Reusing already uploaded file: {id}\n".format(id=inp) + ) + return inp + elif inp == "": + break + else: + sys.stdout.write( + "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( + id=inp + ) + ) + + buffer_reader = BufferReader(content, desc="Upload progress") + resp = openai.File.create( + file=buffer_reader, + purpose="fine-tune", + user_provided_filename=user_provided_file or file, + ) + sys.stdout.write( + "Uploaded file from {file}: {id}\n".format( + file=user_provided_file or file, id=resp["id"] + ) + ) + return resp["id"] + + @classmethod + def _get_or_upload(cls, file, check_if_file_exists=True): + try: + # 1. If it's a valid file, use it + openai.File.retrieve(file) + return file + except openai.error.InvalidRequestError: + pass + if os.path.isfile(file): + # 2. If it's a file on the filesystem, upload it + return cls._maybe_upload_file( + file=file, check_if_file_exists=check_if_file_exists + ) + if cls._is_url(file): + # 3. If it's a URL, download it temporarily + content = cls._download_file_from_public_url(file) + if content is not None: + return cls._maybe_upload_file( + content=content, + check_if_file_exists=check_if_file_exists, + user_provided_file=file, + ) + return file + + @classmethod + def create(cls, args): + create_args = { + "training_file": cls._get_or_upload( + args.training_file, args.check_if_files_exist + ), + } + if args.validation_file: + create_args["validation_file"] = cls._get_or_upload( + args.validation_file, args.check_if_files_exist + ) + + for hparam in ( + "model", + "suffix", + "n_epochs", + "batch_size", + "learning_rate_multiplier", + "prompt_loss_weight", + "compute_classification_metrics", + "classification_n_classes", + "classification_positive_class", + "classification_betas", + ): + attr = getattr(args, hparam) + if attr is not None: + create_args[hparam] = attr + + resp = openai.FineTune.create(**create_args) + + if args.no_follow: + print(resp) + return + + sys.stdout.write( + "Created fine-tune: {job_id}\n" + "Streaming events until fine-tuning is complete...\n\n" + "(Ctrl-C will interrupt the stream, but not cancel the fine-tune)\n".format( + job_id=resp["id"] + ) + ) + cls._stream_events(resp["id"]) + + @classmethod + def get(cls, args): + resp = openai.FineTune.retrieve(id=args.id) + print(resp) + + @classmethod + def results(cls, args): + fine_tune = openai.FineTune.retrieve(id=args.id) + if "result_files" not in fine_tune or len(fine_tune["result_files"]) == 0: + raise openai.error.InvalidRequestError( + f"No results file available for fine-tune {args.id}", "id" + ) + result_file = openai.FineTune.retrieve(id=args.id)["result_files"][0] + resp = openai.File.download(id=result_file["id"]) + print(resp.decode("utf-8")) + + @classmethod + def events(cls, args): + if args.stream: + raise openai.error.OpenAIError( + message=( + "The --stream parameter is deprecated, use fine_tunes.follow " + "instead:\n\n" + " openai api fine_tunes.follow -i {id}\n".format(id=args.id) + ), + ) + + resp = openai.FineTune.list_events(id=args.id) # type: ignore + print(resp) + + @classmethod + def follow(cls, args): + cls._stream_events(args.id) + + @classmethod + def _stream_events(cls, job_id): + def signal_handler(sig, frame): + status = openai.FineTune.retrieve(job_id).status + sys.stdout.write( + "\nStream interrupted. Job is still {status}.\n" + "To resume the stream, run:\n\n" + " openai api fine_tunes.follow -i {job_id}\n\n" + "To cancel your job, run:\n\n" + " openai api fine_tunes.cancel -i {job_id}\n\n".format( + status=status, job_id=job_id + ) + ) + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + events = openai.FineTune.stream_events(job_id) + # TODO(rachel): Add a nifty spinner here. + try: + for event in events: + sys.stdout.write( + "[%s] %s" + % ( + datetime.datetime.fromtimestamp(event["created_at"]), + event["message"], + ) + ) + sys.stdout.write("\n") + sys.stdout.flush() + except Exception: + sys.stdout.write( + "\nStream interrupted (client disconnected).\n" + "To resume the stream, run:\n\n" + " openai api fine_tunes.follow -i {job_id}\n\n".format(job_id=job_id) + ) + return + + resp = openai.FineTune.retrieve(id=job_id) + status = resp["status"] + if status == "succeeded": + sys.stdout.write("\nJob complete! Status: succeeded 🎉") + sys.stdout.write( + "\nTry out your fine-tuned model:\n\n" + "openai api completions.create -m {model} -p ".format( + model=resp["fine_tuned_model"] + ) + ) + elif status == "failed": + sys.stdout.write( + "\nJob failed. Please contact us through our help center at help.openai.com if you need assistance." + ) + sys.stdout.write("\n") + + @classmethod + def cancel(cls, args): + resp = openai.FineTune.cancel(id=args.id) + print(resp) + + @classmethod + def delete(cls, args): + resp = openai.FineTune.delete(sid=args.id) + print(resp) + + @classmethod + def prepare_data(cls, args): + sys.stdout.write("Analyzing...\n") + fname = args.file + auto_accept = args.quiet + df, remediation = read_any_format(fname) + apply_necessary_remediation(None, remediation) + + validators = get_validators() + + apply_validators( + df, + fname, + remediation, + validators, + auto_accept, + write_out_file_func=write_out_file, + ) + + +class FineTuningJob: + @classmethod + def list(cls, args): + has_ft_jobs = False + for fine_tune_job in openai.FineTuningJob.auto_paging_iter(): + has_ft_jobs = True + print(fine_tune_job) + if not has_ft_jobs: + print("No fine-tuning jobs found.") + + @classmethod + def _is_url(cls, file: str): + return file.lower().startswith("http") + + @classmethod + def _download_file_from_public_url(cls, url: str) -> Optional[bytes]: + resp = requests.get(url) + if resp.status_code == 200: + return resp.content + else: + return None + + @classmethod + def _maybe_upload_file( + cls, + file: Optional[str] = None, + content: Optional[bytes] = None, + user_provided_file: Optional[str] = None, + check_if_file_exists: bool = True, + ): + # Exactly one of `file` or `content` must be provided + if (file is None) == (content is None): + raise ValueError("Exactly one of `file` or `content` must be provided") + + if content is None: + assert file is not None + with open(file, "rb") as f: + content = f.read() + + if check_if_file_exists: + bytes = len(content) + matching_files = openai.File.find_matching_files( + name=user_provided_file or f.name, + bytes=bytes, + purpose="fine-tune", + ) + if len(matching_files) > 0: + file_ids = [f["id"] for f in matching_files] + sys.stdout.write( + "Found potentially duplicated files with name '{name}', purpose 'fine-tune', and size {size} bytes\n".format( + name=os.path.basename(matching_files[0]["filename"]), + size=matching_files[0]["bytes"] + if "bytes" in matching_files[0] + else matching_files[0]["size"], + ) + ) + sys.stdout.write("\n".join(file_ids)) + while True: + sys.stdout.write( + "\nEnter file ID to reuse an already uploaded file, or an empty string to upload this file anyway: " + ) + inp = sys.stdin.readline().strip() + if inp in file_ids: + sys.stdout.write( + "Reusing already uploaded file: {id}\n".format(id=inp) + ) + return inp + elif inp == "": + break + else: + sys.stdout.write( + "File id '{id}' is not among the IDs of the potentially duplicated files\n".format( + id=inp + ) + ) + + buffer_reader = BufferReader(content, desc="Upload progress") + resp = openai.File.create( + file=buffer_reader, + purpose="fine-tune", + user_provided_filename=user_provided_file or file, + ) + sys.stdout.write( + "Uploaded file from {file}: {id}\n".format( + file=user_provided_file or file, id=resp["id"] + ) + ) + sys.stdout.write("Waiting for file to finish processing before proceeding..\n") + sys.stdout.flush() + status = openai.File.wait_for_processing(resp["id"]) + if status != "processed": + raise openai.error.OpenAIError( + "File {id} failed to process, status={status}.".format( + id=resp["id"], status=status + ) + ) + + sys.stdout.write( + "File {id} finished processing and is ready for use in fine-tuning".format( + id=resp["id"] + ) + ) + sys.stdout.flush() + return resp["id"] + + @classmethod + def _get_or_upload(cls, file, check_if_file_exists=True): + try: + # 1. If it's a valid file, use it + openai.File.retrieve(file) + return file + except openai.error.InvalidRequestError: + pass + if os.path.isfile(file): + # 2. If it's a file on the filesystem, upload it + return cls._maybe_upload_file( + file=file, check_if_file_exists=check_if_file_exists + ) + if cls._is_url(file): + # 3. If it's a URL, download it temporarily + content = cls._download_file_from_public_url(file) + if content is not None: + return cls._maybe_upload_file( + content=content, + check_if_file_exists=check_if_file_exists, + user_provided_file=file, + ) + return file + + @classmethod + def create(cls, args): + create_args = { + "training_file": cls._get_or_upload( + args.training_file, args.check_if_files_exist + ), + } + if args.validation_file: + create_args["validation_file"] = cls._get_or_upload( + args.validation_file, args.check_if_files_exist + ) + + for param in ("model", "suffix"): + attr = getattr(args, param) + if attr is not None: + create_args[param] = attr + + if getattr(args, "n_epochs"): + create_args["hyperparameters"] = { + "n_epochs": args.n_epochs, + } + + resp = openai.FineTuningJob.create(**create_args) + print(resp) + return + + @classmethod + def get(cls, args): + resp = openai.FineTuningJob.retrieve(id=args.id) + print(resp) + + @classmethod + def results(cls, args): + fine_tune = openai.FineTuningJob.retrieve(id=args.id) + if "result_files" not in fine_tune or len(fine_tune["result_files"]) == 0: + raise openai.error.InvalidRequestError( + f"No results file available for fine-tune {args.id}", "id" + ) + result_file = openai.FineTuningJob.retrieve(id=args.id)["result_files"][0] + resp = openai.File.download(id=result_file) + print(resp.decode("utf-8")) + + @classmethod + def events(cls, args): + seen, has_more, after = 0, True, None + while has_more: + resp = openai.FineTuningJob.list_events(id=args.id, after=after) # type: ignore + for event in resp["data"]: + print(event) + seen += 1 + if args.limit is not None and seen >= args.limit: + return + has_more = resp.get("has_more", False) + if resp["data"]: + after = resp["data"][-1]["id"] + + @classmethod + def follow(cls, args): + raise openai.error.OpenAIError( + message="Event streaming is not yet supported for `fine_tuning.job` events" + ) + + @classmethod + def cancel(cls, args): + resp = openai.FineTuningJob.cancel(id=args.id) + print(resp) + + +class WandbLogger: + @classmethod + def sync(cls, args): + import openai.wandb_logger + + resp = openai.wandb_logger.WandbLogger.sync( + id=args.id, + n_fine_tunes=args.n_fine_tunes, + project=args.project, + entity=args.entity, + force=args.force, + ) + print(resp) + + +def tools_register(parser): + subparsers = parser.add_subparsers( + title="Tools", help="Convenience client side tools" + ) + + def help(args): + parser.print_help() + + parser.set_defaults(func=help) + + sub = subparsers.add_parser("fine_tunes.prepare_data") + sub.add_argument( + "-f", + "--file", + required=True, + help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed." + "This should be the local file path.", + ) + sub.add_argument( + "-q", + "--quiet", + required=False, + action="store_true", + help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", + ) + sub.set_defaults(func=FineTune.prepare_data) + + +def api_register(parser): + # Engine management + subparsers = parser.add_subparsers(help="All API subcommands") + + def help(args): + parser.print_help() + + parser.set_defaults(func=help) + + sub = subparsers.add_parser("engines.list") + sub.set_defaults(func=Engine.list) + + sub = subparsers.add_parser("engines.get") + sub.add_argument("-i", "--id", required=True) + sub.set_defaults(func=Engine.get) + + sub = subparsers.add_parser("engines.update") + sub.add_argument("-i", "--id", required=True) + sub.add_argument("-r", "--replicas", type=int) + sub.set_defaults(func=Engine.update) + + sub = subparsers.add_parser("engines.generate") + sub.add_argument("-i", "--id", required=True) + sub.add_argument( + "--stream", help="Stream tokens as they're ready.", action="store_true" + ) + sub.add_argument("-c", "--context", help="An optional context to generate from") + sub.add_argument("-l", "--length", help="How many tokens to generate", type=int) + sub.add_argument( + "-t", + "--temperature", + help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. + +Mutually exclusive with `top_p`.""", + type=float, + ) + sub.add_argument( + "-p", + "--top_p", + help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. + + Mutually exclusive with `temperature`.""", + type=float, + ) + sub.add_argument( + "-n", + "--completions", + help="How many parallel completions to run on this context", + type=int, + ) + sub.add_argument( + "--logprobs", + help="Include the log probabilites on the `logprobs` most likely tokens. So for example, if `logprobs` is 10, the API will return a list of the 10 most likely tokens. If `logprobs` is supplied, the API will always return the logprob of the generated token, so there may be up to `logprobs+1` elements in the response.", + type=int, + ) + sub.add_argument( + "--stop", help="A stop sequence at which to stop generating tokens." + ) + sub.add_argument( + "-m", + "--model", + required=False, + help="A model (most commonly a model ID) to generate from. Defaults to the engine's default model.", + ) + sub.set_defaults(func=Engine.generate) + + # Chat Completions + sub = subparsers.add_parser("chat_completions.create") + + sub._action_groups.pop() + req = sub.add_argument_group("required arguments") + opt = sub.add_argument_group("optional arguments") + + req.add_argument( + "-g", + "--message", + action="append", + nargs=2, + metavar=("ROLE", "CONTENT"), + help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", + required=True, + ) + + group = opt.add_mutually_exclusive_group() + group.add_argument( + "-e", + "--engine", + help="The engine to use. See https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=programming-language-python for more about what engines are available.", + ) + group.add_argument( + "-m", + "--model", + help="The model to use.", + ) + + opt.add_argument( + "-n", + "--n", + help="How many completions to generate for the conversation.", + type=int, + ) + opt.add_argument( + "-M", "--max-tokens", help="The maximum number of tokens to generate.", type=int + ) + opt.add_argument( + "-t", + "--temperature", + help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. + +Mutually exclusive with `top_p`.""", + type=float, + ) + opt.add_argument( + "-P", + "--top_p", + help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. + + Mutually exclusive with `temperature`.""", + type=float, + ) + opt.add_argument( + "--stop", + help="A stop sequence at which to stop generating tokens for the message.", + ) + opt.add_argument( + "--stream", help="Stream messages as they're ready.", action="store_true" + ) + sub.set_defaults(func=ChatCompletion.create) + + # Completions + sub = subparsers.add_parser("completions.create") + sub.add_argument( + "-e", + "--engine", + help="The engine to use. See https://platform.openai.com/docs/engines for more about what engines are available.", + ) + sub.add_argument( + "-m", + "--model", + help="The model to use. At most one of `engine` or `model` should be specified.", + ) + sub.add_argument( + "--stream", help="Stream tokens as they're ready.", action="store_true" + ) + sub.add_argument("-p", "--prompt", help="An optional prompt to complete from") + sub.add_argument( + "-M", "--max-tokens", help="The maximum number of tokens to generate", type=int + ) + sub.add_argument( + "-t", + "--temperature", + help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. + +Mutually exclusive with `top_p`.""", + type=float, + ) + sub.add_argument( + "-P", + "--top_p", + help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. + + Mutually exclusive with `temperature`.""", + type=float, + ) + sub.add_argument( + "-n", + "--n", + help="How many sub-completions to generate for each prompt.", + type=int, + ) + sub.add_argument( + "--logprobs", + help="Include the log probabilites on the `logprobs` most likely tokens, as well the chosen tokens. So for example, if `logprobs` is 10, the API will return a list of the 10 most likely tokens. If `logprobs` is 0, only the chosen tokens will have logprobs returned.", + type=int, + ) + sub.add_argument( + "--stop", help="A stop sequence at which to stop generating tokens." + ) + sub.set_defaults(func=Completion.create) + + # Deployments + sub = subparsers.add_parser("deployments.list") + sub.set_defaults(func=Deployment.list) + + sub = subparsers.add_parser("deployments.get") + sub.add_argument("-i", "--id", required=True, help="The deployment ID") + sub.set_defaults(func=Deployment.get) + + sub = subparsers.add_parser("deployments.delete") + sub.add_argument("-i", "--id", required=True, help="The deployment ID") + sub.set_defaults(func=Deployment.delete) + + sub = subparsers.add_parser("deployments.create") + sub.add_argument("-m", "--model", required=True, help="The model ID") + sub.add_argument( + "-s", + "--scale_type", + required=True, + help="The scale type. Either 'manual' or 'standard'", + ) + sub.set_defaults(func=Deployment.create) + + # Models + sub = subparsers.add_parser("models.list") + sub.set_defaults(func=Model.list) + + sub = subparsers.add_parser("models.get") + sub.add_argument("-i", "--id", required=True, help="The model ID") + sub.set_defaults(func=Model.get) + + sub = subparsers.add_parser("models.delete") + sub.add_argument("-i", "--id", required=True, help="The model ID") + sub.set_defaults(func=Model.delete) + + # Files + sub = subparsers.add_parser("files.create") + + sub.add_argument( + "-f", + "--file", + required=True, + help="File to upload", + ) + sub.add_argument( + "-p", + "--purpose", + help="Why are you uploading this file? (see https://platform.openai.com/docs/api-reference/ for purposes)", + required=True, + ) + sub.set_defaults(func=File.create) + + sub = subparsers.add_parser("files.get") + sub.add_argument("-i", "--id", required=True, help="The files ID") + sub.set_defaults(func=File.get) + + sub = subparsers.add_parser("files.delete") + sub.add_argument("-i", "--id", required=True, help="The files ID") + sub.set_defaults(func=File.delete) + + sub = subparsers.add_parser("files.list") + sub.set_defaults(func=File.list) + + # Finetune + sub = subparsers.add_parser("fine_tunes.list") + sub.set_defaults(func=FineTune.list) + + sub = subparsers.add_parser("fine_tunes.create") + sub.add_argument( + "-t", + "--training_file", + required=True, + help="JSONL file containing prompt-completion examples for training. This can " + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "-v", + "--validation_file", + help="JSONL file containing prompt-completion examples for validation. This can " + "be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "--no_check_if_files_exist", + dest="check_if_files_exist", + action="store_false", + help="If this argument is set and training_file or validation_file are file paths, immediately upload them. If this argument is not set, check if they may be duplicates of already uploaded files before uploading, based on file name and file size.", + ) + sub.add_argument( + "-m", + "--model", + help="The model to start fine-tuning from", + ) + sub.add_argument( + "--suffix", + help="If set, this argument can be used to customize the generated fine-tuned model name." + "All punctuation and whitespace in `suffix` will be replaced with a " + "single dash, and the string will be lower cased. The max " + "length of `suffix` is 40 chars. " + "The generated name will match the form `{base_model}:ft-{org-title}:{suffix}-{timestamp}`. " + 'For example, `openai api fine_tunes.create -t test.jsonl -m ada --suffix "custom model name" ' + "could generate a model with the name " + "ada:ft-your-org:custom-model-name-2022-02-15-04-21-04", + ) + sub.add_argument( + "--no_follow", + action="store_true", + help="If set, returns immediately after creating the job. Otherwise, streams events and waits for the job to complete.", + ) + sub.add_argument( + "--n_epochs", + type=int, + help="The number of epochs to train the model for. An epoch refers to one " + "full cycle through the training dataset.", + ) + sub.add_argument( + "--batch_size", + type=int, + help="The batch size to use for training. The batch size is the number of " + "training examples used to train a single forward and backward pass.", + ) + sub.add_argument( + "--learning_rate_multiplier", + type=float, + help="The learning rate multiplier to use for training. The fine-tuning " + "learning rate is determined by the original learning rate used for " + "pretraining multiplied by this value.", + ) + sub.add_argument( + "--prompt_loss_weight", + type=float, + help="The weight to use for the prompt loss. The optimum value here depends " + "depends on your use case. This determines how much the model prioritizes " + "learning from prompt tokens vs learning from completion tokens.", + ) + sub.add_argument( + "--compute_classification_metrics", + action="store_true", + help="If set, we calculate classification-specific metrics such as accuracy " + "and F-1 score using the validation set at the end of every epoch.", + ) + sub.set_defaults(compute_classification_metrics=None) + sub.add_argument( + "--classification_n_classes", + type=int, + help="The number of classes in a classification task. This parameter is " + "required for multiclass classification.", + ) + sub.add_argument( + "--classification_positive_class", + help="The positive class in binary classification. This parameter is needed " + "to generate precision, recall and F-1 metrics when doing binary " + "classification.", + ) + sub.add_argument( + "--classification_betas", + type=float, + nargs="+", + help="If this is provided, we calculate F-beta scores at the specified beta " + "values. The F-beta score is a generalization of F-1 score. This is only " + "used for binary classification.", + ) + sub.set_defaults(func=FineTune.create) + + sub = subparsers.add_parser("fine_tunes.get") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.get) + + sub = subparsers.add_parser("fine_tunes.results") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.results) + + sub = subparsers.add_parser("fine_tunes.events") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + + # TODO(rachel): Remove this in 1.0 + sub.add_argument( + "-s", + "--stream", + action="store_true", + help="[DEPRECATED] If set, events will be streamed until the job is done. Otherwise, " + "displays the event history to date.", + ) + sub.set_defaults(func=FineTune.events) + + sub = subparsers.add_parser("fine_tunes.follow") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.follow) + + sub = subparsers.add_parser("fine_tunes.cancel") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.cancel) + + sub = subparsers.add_parser("fine_tunes.delete") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTune.delete) + + # Image + sub = subparsers.add_parser("image.create") + sub.add_argument("-p", "--prompt", type=str, required=True) + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument( + "-s", "--size", type=str, default="1024x1024", help="Size of the output image" + ) + sub.add_argument("--response-format", type=str, default="url") + sub.set_defaults(func=Image.create) + + sub = subparsers.add_parser("image.create_edit") + sub.add_argument("-p", "--prompt", type=str, required=True) + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument( + "-I", + "--image", + type=str, + required=True, + help="Image to modify. Should be a local path and a PNG encoded image.", + ) + sub.add_argument( + "-s", "--size", type=str, default="1024x1024", help="Size of the output image" + ) + sub.add_argument("--response-format", type=str, default="url") + sub.add_argument( + "-M", + "--mask", + type=str, + required=False, + help="Path to a mask image. It should be the same size as the image you're editing and a RGBA PNG image. The Alpha channel acts as the mask.", + ) + sub.set_defaults(func=Image.create_edit) + + sub = subparsers.add_parser("image.create_variation") + sub.add_argument("-n", "--num-images", type=int, default=1) + sub.add_argument( + "-I", + "--image", + type=str, + required=True, + help="Image to modify. Should be a local path and a PNG encoded image.", + ) + sub.add_argument( + "-s", "--size", type=str, default="1024x1024", help="Size of the output image" + ) + sub.add_argument("--response-format", type=str, default="url") + sub.set_defaults(func=Image.create_variation) + + # Audio + # transcriptions + sub = subparsers.add_parser("audio.transcribe") + # Required + sub.add_argument("-m", "--model", type=str, default="whisper-1") + sub.add_argument("-f", "--file", type=str, required=True) + # Optional + sub.add_argument("--response-format", type=str) + sub.add_argument("--language", type=str) + sub.add_argument("-t", "--temperature", type=float) + sub.add_argument("--prompt", type=str) + sub.set_defaults(func=Audio.transcribe) + # translations + sub = subparsers.add_parser("audio.translate") + # Required + sub.add_argument("-m", "--model", type=str, default="whisper-1") + sub.add_argument("-f", "--file", type=str, required=True) + # Optional + sub.add_argument("--response-format", type=str) + sub.add_argument("--language", type=str) + sub.add_argument("-t", "--temperature", type=float) + sub.add_argument("--prompt", type=str) + sub.set_defaults(func=Audio.translate) + + # FineTuning Jobs + sub = subparsers.add_parser("fine_tuning.job.list") + sub.set_defaults(func=FineTuningJob.list) + + sub = subparsers.add_parser("fine_tuning.job.create") + sub.add_argument( + "-t", + "--training_file", + required=True, + help="JSONL file containing either chat-completion or prompt-completion examples for training. " + "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "-v", + "--validation_file", + help="JSONL file containing either chat-completion or prompt-completion examples for validation. " + "This can be the ID of a file uploaded through the OpenAI API (e.g. file-abcde12345), " + 'a local file path, or a URL that starts with "http".', + ) + sub.add_argument( + "--no_check_if_files_exist", + dest="check_if_files_exist", + action="store_false", + help="If this argument is set and training_file or validation_file are file paths, immediately upload them. If this argument is not set, check if they may be duplicates of already uploaded files before uploading, based on file name and file size.", + ) + sub.add_argument( + "-m", + "--model", + help="The model to start fine-tuning from", + ) + sub.add_argument( + "--suffix", + help="If set, this argument can be used to customize the generated fine-tuned model name." + "All punctuation and whitespace in `suffix` will be replaced with a " + "single dash, and the string will be lower cased. The max " + "length of `suffix` is 18 chars. " + "The generated name will match the form `ft:{base_model}:{org-title}:{suffix}:{rstring}` where `rstring` " + "is a random string sortable as a timestamp. " + 'For example, `openai api fine_tuning.job.create -t test.jsonl -m gpt-3.5-turbo-0613 --suffix "first finetune!" ' + "could generate a model with the name " + "ft:gpt-3.5-turbo-0613:your-org:first-finetune:7p4PqAoY", + ) + sub.add_argument( + "--n_epochs", + type=int, + help="The number of epochs to train the model for. An epoch refers to one " + "full cycle through the training dataset.", + ) + sub.set_defaults(func=FineTuningJob.create) + + sub = subparsers.add_parser("fine_tuning.job.get") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.get) + + sub = subparsers.add_parser("fine_tuning.job.results") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.results) + + sub = subparsers.add_parser("fine_tuning.job.events") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.add_argument( + "--limit", + type=int, + required=False, + help="The number of events to return, starting from most recent. If not specified, all events will be returned.", + ) + sub.set_defaults(func=FineTuningJob.events) + + sub = subparsers.add_parser("fine_tuning.job.follow") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.follow) + + sub = subparsers.add_parser("fine_tuning.job.cancel") + sub.add_argument("-i", "--id", required=True, help="The id of the fine-tune job") + sub.set_defaults(func=FineTuningJob.cancel) + + +def wandb_register(parser): + subparsers = parser.add_subparsers( + title="wandb", help="Logging with Weights & Biases" + ) + + def help(args): + parser.print_help() + + parser.set_defaults(func=help) + + sub = subparsers.add_parser("sync") + sub.add_argument("-i", "--id", help="The id of the fine-tune job (optional)") + sub.add_argument( + "-n", + "--n_fine_tunes", + type=int, + default=None, + help="Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced.", + ) + sub.add_argument( + "--project", + default="GPT-3", + help="""Name of the project where you're sending runs. By default, it is "GPT-3".""", + ) + sub.add_argument( + "--entity", + help="Username or team name where you're sending runs. By default, your default entity is used, which is usually your username.", + ) + sub.add_argument( + "--force", + action="store_true", + help="Forces logging and overwrite existing wandb run of the same fine-tune.", + ) + sub.set_defaults(force=False) + sub.set_defaults(func=WandbLogger.sync) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__init__.py new file mode 100644 index 0000000..d02b49c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__init__.py @@ -0,0 +1,14 @@ +""" +This module helps make data libraries like `numpy` and `pandas` optional dependencies. + +The libraries add up to 130MB+, which makes it challenging to deploy applications +using this library in environments with code size constraints, like AWS Lambda. + +This module serves as an import proxy and provides a few utilities for dealing with the optionality. + +Since the primary use case of this library (talking to the OpenAI API) doesn't generally require data libraries, +it's safe to make them optional. The rare case when data libraries are needed in the client is handled through +assertions with instructive error messages. + +See also `setup.py`. +""" diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a314c9e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/common.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/common.cpython-310.pyc new file mode 100644 index 0000000..e67f40f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/common.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/numpy_helper.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/numpy_helper.cpython-310.pyc new file mode 100644 index 0000000..fed4215 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/numpy_helper.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/pandas_helper.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/pandas_helper.cpython-310.pyc new file mode 100644 index 0000000..f63b79a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/__pycache__/pandas_helper.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/common.py b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/common.py new file mode 100644 index 0000000..96f9908 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/common.py @@ -0,0 +1,17 @@ +INSTRUCTIONS = """ + +OpenAI error: + + missing `{library}` + +This feature requires additional dependencies: + + $ pip install openai[datalib] + +""" + +NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") + + +class MissingDependencyError(Exception): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/numpy_helper.py b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/numpy_helper.py new file mode 100644 index 0000000..fb80f2a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/numpy_helper.py @@ -0,0 +1,15 @@ +from openai.datalib.common import INSTRUCTIONS, MissingDependencyError + +try: + import numpy +except ImportError: + numpy = None + +HAS_NUMPY = bool(numpy) + +NUMPY_INSTRUCTIONS = INSTRUCTIONS.format(library="numpy") + + +def assert_has_numpy(): + if not HAS_NUMPY: + raise MissingDependencyError(NUMPY_INSTRUCTIONS) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/datalib/pandas_helper.py b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/pandas_helper.py new file mode 100644 index 0000000..4e86d7b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/datalib/pandas_helper.py @@ -0,0 +1,15 @@ +from openai.datalib.common import INSTRUCTIONS, MissingDependencyError + +try: + import pandas +except ImportError: + pandas = None + +HAS_PANDAS = bool(pandas) + +PANDAS_INSTRUCTIONS = INSTRUCTIONS.format(library="pandas") + + +def assert_has_pandas(): + if not HAS_PANDAS: + raise MissingDependencyError(PANDAS_INSTRUCTIONS) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/embeddings_utils.py b/.pythonlibs/lib/python3.10/site-packages/openai/embeddings_utils.py new file mode 100644 index 0000000..f1d438c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/embeddings_utils.py @@ -0,0 +1,252 @@ +import textwrap as tr +from typing import List, Optional + +import matplotlib.pyplot as plt +import plotly.express as px +from scipy import spatial +from sklearn.decomposition import PCA +from sklearn.manifold import TSNE +from sklearn.metrics import average_precision_score, precision_recall_curve +from tenacity import retry, stop_after_attempt, wait_random_exponential + +import openai +from openai.datalib.numpy_helper import numpy as np +from openai.datalib.pandas_helper import pandas as pd + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +def get_embedding(text: str, engine="text-similarity-davinci-001", **kwargs) -> List[float]: + + # replace newlines, which can negatively affect performance. + text = text.replace("\n", " ") + + return openai.Embedding.create(input=[text], engine=engine, **kwargs)["data"][0]["embedding"] + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +async def aget_embedding( + text: str, engine="text-similarity-davinci-001", **kwargs +) -> List[float]: + + # replace newlines, which can negatively affect performance. + text = text.replace("\n", " ") + + return (await openai.Embedding.acreate(input=[text], engine=engine, **kwargs))["data"][0][ + "embedding" + ] + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +def get_embeddings( + list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs +) -> List[List[float]]: + assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." + + # replace newlines, which can negatively affect performance. + list_of_text = [text.replace("\n", " ") for text in list_of_text] + + data = openai.Embedding.create(input=list_of_text, engine=engine, **kwargs).data + return [d["embedding"] for d in data] + + +@retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6)) +async def aget_embeddings( + list_of_text: List[str], engine="text-similarity-babbage-001", **kwargs +) -> List[List[float]]: + assert len(list_of_text) <= 2048, "The batch size should not be larger than 2048." + + # replace newlines, which can negatively affect performance. + list_of_text = [text.replace("\n", " ") for text in list_of_text] + + data = (await openai.Embedding.acreate(input=list_of_text, engine=engine, **kwargs)).data + return [d["embedding"] for d in data] + + +def cosine_similarity(a, b): + return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) + + +def plot_multiclass_precision_recall( + y_score, y_true_untransformed, class_list, classifier_name +): + """ + Precision-Recall plotting for a multiclass problem. It plots average precision-recall, per class precision recall and reference f1 contours. + + Code slightly modified, but heavily based on https://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html + """ + n_classes = len(class_list) + y_true = pd.concat( + [(y_true_untransformed == class_list[i]) for i in range(n_classes)], axis=1 + ).values + + # For each class + precision = dict() + recall = dict() + average_precision = dict() + for i in range(n_classes): + precision[i], recall[i], _ = precision_recall_curve(y_true[:, i], y_score[:, i]) + average_precision[i] = average_precision_score(y_true[:, i], y_score[:, i]) + + # A "micro-average": quantifying score on all classes jointly + precision_micro, recall_micro, _ = precision_recall_curve( + y_true.ravel(), y_score.ravel() + ) + average_precision_micro = average_precision_score(y_true, y_score, average="micro") + print( + str(classifier_name) + + " - Average precision score over all classes: {0:0.2f}".format( + average_precision_micro + ) + ) + + # setup plot details + plt.figure(figsize=(9, 10)) + f_scores = np.linspace(0.2, 0.8, num=4) + lines = [] + labels = [] + for f_score in f_scores: + x = np.linspace(0.01, 1) + y = f_score * x / (2 * x - f_score) + (l,) = plt.plot(x[y >= 0], y[y >= 0], color="gray", alpha=0.2) + plt.annotate("f1={0:0.1f}".format(f_score), xy=(0.9, y[45] + 0.02)) + + lines.append(l) + labels.append("iso-f1 curves") + (l,) = plt.plot(recall_micro, precision_micro, color="gold", lw=2) + lines.append(l) + labels.append( + "average Precision-recall (auprc = {0:0.2f})" "".format(average_precision_micro) + ) + + for i in range(n_classes): + (l,) = plt.plot(recall[i], precision[i], lw=2) + lines.append(l) + labels.append( + "Precision-recall for class `{0}` (auprc = {1:0.2f})" + "".format(class_list[i], average_precision[i]) + ) + + fig = plt.gcf() + fig.subplots_adjust(bottom=0.25) + plt.xlim([0.0, 1.0]) + plt.ylim([0.0, 1.05]) + plt.xlabel("Recall") + plt.ylabel("Precision") + plt.title(f"{classifier_name}: Precision-Recall curve for each class") + plt.legend(lines, labels) + + +def distances_from_embeddings( + query_embedding: List[float], + embeddings: List[List[float]], + distance_metric="cosine", +) -> List[List]: + """Return the distances between a query embedding and a list of embeddings.""" + distance_metrics = { + "cosine": spatial.distance.cosine, + "L1": spatial.distance.cityblock, + "L2": spatial.distance.euclidean, + "Linf": spatial.distance.chebyshev, + } + distances = [ + distance_metrics[distance_metric](query_embedding, embedding) + for embedding in embeddings + ] + return distances + + +def indices_of_nearest_neighbors_from_distances(distances) -> np.ndarray: + """Return a list of indices of nearest neighbors from a list of distances.""" + return np.argsort(distances) + + +def pca_components_from_embeddings( + embeddings: List[List[float]], n_components=2 +) -> np.ndarray: + """Return the PCA components of a list of embeddings.""" + pca = PCA(n_components=n_components) + array_of_embeddings = np.array(embeddings) + return pca.fit_transform(array_of_embeddings) + + +def tsne_components_from_embeddings( + embeddings: List[List[float]], n_components=2, **kwargs +) -> np.ndarray: + """Returns t-SNE components of a list of embeddings.""" + # use better defaults if not specified + if "init" not in kwargs.keys(): + kwargs["init"] = "pca" + if "learning_rate" not in kwargs.keys(): + kwargs["learning_rate"] = "auto" + tsne = TSNE(n_components=n_components, **kwargs) + array_of_embeddings = np.array(embeddings) + return tsne.fit_transform(array_of_embeddings) + + +def chart_from_components( + components: np.ndarray, + labels: Optional[List[str]] = None, + strings: Optional[List[str]] = None, + x_title="Component 0", + y_title="Component 1", + mark_size=5, + **kwargs, +): + """Return an interactive 2D chart of embedding components.""" + empty_list = ["" for _ in components] + data = pd.DataFrame( + { + x_title: components[:, 0], + y_title: components[:, 1], + "label": labels if labels else empty_list, + "string": ["
    ".join(tr.wrap(string, width=30)) for string in strings] + if strings + else empty_list, + } + ) + chart = px.scatter( + data, + x=x_title, + y=y_title, + color="label" if labels else None, + symbol="label" if labels else None, + hover_data=["string"] if strings else None, + **kwargs, + ).update_traces(marker=dict(size=mark_size)) + return chart + + +def chart_from_components_3D( + components: np.ndarray, + labels: Optional[List[str]] = None, + strings: Optional[List[str]] = None, + x_title: str = "Component 0", + y_title: str = "Component 1", + z_title: str = "Compontent 2", + mark_size: int = 5, + **kwargs, +): + """Return an interactive 3D chart of embedding components.""" + empty_list = ["" for _ in components] + data = pd.DataFrame( + { + x_title: components[:, 0], + y_title: components[:, 1], + z_title: components[:, 2], + "label": labels if labels else empty_list, + "string": ["
    ".join(tr.wrap(string, width=30)) for string in strings] + if strings + else empty_list, + } + ) + chart = px.scatter_3d( + data, + x=x_title, + y=y_title, + z=z_title, + color="label" if labels else None, + symbol="label" if labels else None, + hover_data=["string"] if strings else None, + **kwargs, + ).update_traces(marker=dict(size=mark_size)) + return chart diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/error.py b/.pythonlibs/lib/python3.10/site-packages/openai/error.py new file mode 100644 index 0000000..2928ef6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/error.py @@ -0,0 +1,169 @@ +import openai + + +class OpenAIError(Exception): + def __init__( + self, + message=None, + http_body=None, + http_status=None, + json_body=None, + headers=None, + code=None, + ): + super(OpenAIError, self).__init__(message) + + if http_body and hasattr(http_body, "decode"): + try: + http_body = http_body.decode("utf-8") + except BaseException: + http_body = ( + "" + ) + + self._message = message + self.http_body = http_body + self.http_status = http_status + self.json_body = json_body + self.headers = headers or {} + self.code = code + self.request_id = self.headers.get("request-id", None) + self.error = self.construct_error_object() + self.organization = self.headers.get("openai-organization", None) + + def __str__(self): + msg = self._message or "" + if self.request_id is not None: + return "Request {0}: {1}".format(self.request_id, msg) + else: + return msg + + # Returns the underlying `Exception` (base class) message, which is usually + # the raw message returned by OpenAI's API. This was previously available + # in python2 via `error.message`. Unlike `str(error)`, it omits "Request + # req_..." from the beginning of the string. + @property + def user_message(self): + return self._message + + def __repr__(self): + return "%s(message=%r, http_status=%r, request_id=%r)" % ( + self.__class__.__name__, + self._message, + self.http_status, + self.request_id, + ) + + def construct_error_object(self): + if ( + self.json_body is None + or not isinstance(self.json_body, dict) + or "error" not in self.json_body + or not isinstance(self.json_body["error"], dict) + ): + return None + + return openai.api_resources.error_object.ErrorObject.construct_from( + self.json_body["error"] + ) + + +class APIError(OpenAIError): + pass + + +class TryAgain(OpenAIError): + pass + + +class Timeout(OpenAIError): + pass + + +class APIConnectionError(OpenAIError): + def __init__( + self, + message, + http_body=None, + http_status=None, + json_body=None, + headers=None, + code=None, + should_retry=False, + ): + super(APIConnectionError, self).__init__( + message, http_body, http_status, json_body, headers, code + ) + self.should_retry = should_retry + + +class InvalidRequestError(OpenAIError): + def __init__( + self, + message, + param, + code=None, + http_body=None, + http_status=None, + json_body=None, + headers=None, + ): + super(InvalidRequestError, self).__init__( + message, http_body, http_status, json_body, headers, code + ) + self.param = param + + def __repr__(self): + return "%s(message=%r, param=%r, code=%r, http_status=%r, " "request_id=%r)" % ( + self.__class__.__name__, + self._message, + self.param, + self.code, + self.http_status, + self.request_id, + ) + + def __reduce__(self): + return type(self), ( + self._message, + self.param, + self.code, + self.http_body, + self.http_status, + self.json_body, + self.headers, + ) + + +class AuthenticationError(OpenAIError): + pass + + +class PermissionError(OpenAIError): + pass + + +class RateLimitError(OpenAIError): + pass + + +class ServiceUnavailableError(OpenAIError): + pass + + +class InvalidAPIType(OpenAIError): + pass + + +class SignatureVerificationError(OpenAIError): + def __init__(self, message, sig_header, http_body=None): + super(SignatureVerificationError, self).__init__(message, http_body) + self.sig_header = sig_header + + def __reduce__(self): + return type(self), ( + self._message, + self.sig_header, + self.http_body, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/object_classes.py b/.pythonlibs/lib/python3.10/site-packages/openai/object_classes.py new file mode 100644 index 0000000..0809365 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/object_classes.py @@ -0,0 +1,12 @@ +from openai import api_resources +from openai.api_resources.experimental.completion_config import CompletionConfig + +OBJECT_CLASSES = { + "engine": api_resources.Engine, + "experimental.completion_config": CompletionConfig, + "file": api_resources.File, + "fine-tune": api_resources.FineTune, + "model": api_resources.Model, + "deployment": api_resources.Deployment, + "fine_tuning.job": api_resources.FineTuningJob, +} diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/openai_object.py b/.pythonlibs/lib/python3.10/site-packages/openai/openai_object.py new file mode 100644 index 0000000..95f8829 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/openai_object.py @@ -0,0 +1,347 @@ +import json +from copy import deepcopy +from typing import Optional, Tuple, Union + +import openai +from openai import api_requestor, util +from openai.openai_response import OpenAIResponse +from openai.util import ApiType + + +class OpenAIObject(dict): + api_base_override = None + + def __init__( + self, + id=None, + api_key=None, + api_version=None, + api_type=None, + organization=None, + response_ms: Optional[int] = None, + api_base=None, + engine=None, + **params, + ): + super(OpenAIObject, self).__init__() + + if response_ms is not None and not isinstance(response_ms, int): + raise TypeError(f"response_ms is a {type(response_ms).__name__}.") + self._response_ms = response_ms + + self._retrieve_params = params + + object.__setattr__(self, "api_key", api_key) + object.__setattr__(self, "api_version", api_version) + object.__setattr__(self, "api_type", api_type) + object.__setattr__(self, "organization", organization) + object.__setattr__(self, "api_base_override", api_base) + object.__setattr__(self, "engine", engine) + + if id: + self["id"] = id + + @property + def response_ms(self) -> Optional[int]: + return self._response_ms + + def __setattr__(self, k, v): + if k[0] == "_" or k in self.__dict__: + return super(OpenAIObject, self).__setattr__(k, v) + + self[k] = v + return None + + def __getattr__(self, k): + if k[0] == "_": + raise AttributeError(k) + try: + return self[k] + except KeyError as err: + raise AttributeError(*err.args) + + def __delattr__(self, k): + if k[0] == "_" or k in self.__dict__: + return super(OpenAIObject, self).__delattr__(k) + else: + del self[k] + + def __setitem__(self, k, v): + if v == "": + raise ValueError( + "You cannot set %s to an empty string. " + "We interpret empty strings as None in requests." + "You may set %s.%s = None to delete the property" % (k, str(self), k) + ) + super(OpenAIObject, self).__setitem__(k, v) + + def __delitem__(self, k): + raise NotImplementedError("del is not supported") + + # Custom unpickling method that uses `update` to update the dictionary + # without calling __setitem__, which would fail if any value is an empty + # string + def __setstate__(self, state): + self.update(state) + + # Custom pickling method to ensure the instance is pickled as a custom + # class and not as a dict, otherwise __setstate__ would not be called when + # unpickling. + def __reduce__(self): + reduce_value = ( + type(self), # callable + ( # args + self.get("id", None), + self.api_key, + self.api_version, + self.api_type, + self.organization, + ), + dict(self), # state + ) + return reduce_value + + @classmethod + def construct_from( + cls, + values, + api_key: Optional[str] = None, + api_version=None, + organization=None, + engine=None, + response_ms: Optional[int] = None, + ): + instance = cls( + values.get("id"), + api_key=api_key, + api_version=api_version, + organization=organization, + engine=engine, + response_ms=response_ms, + ) + instance.refresh_from( + values, + api_key=api_key, + api_version=api_version, + organization=organization, + response_ms=response_ms, + ) + return instance + + def refresh_from( + self, + values, + api_key=None, + api_version=None, + api_type=None, + organization=None, + response_ms: Optional[int] = None, + ): + self.api_key = api_key or getattr(values, "api_key", None) + self.api_version = api_version or getattr(values, "api_version", None) + self.api_type = api_type or getattr(values, "api_type", None) + self.organization = organization or getattr(values, "organization", None) + self._response_ms = response_ms or getattr(values, "_response_ms", None) + + # Wipe old state before setting new. + self.clear() + for k, v in values.items(): + super(OpenAIObject, self).__setitem__( + k, util.convert_to_openai_object(v, api_key, api_version, organization) + ) + + self._previous = values + + @classmethod + def api_base(cls): + return None + + def request( + self, + method, + url, + params=None, + headers=None, + stream=False, + plain_old_data=False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ): + if params is None: + params = self._retrieve_params + requestor = api_requestor.APIRequestor( + key=self.api_key, + api_base=self.api_base_override or self.api_base(), + api_type=self.api_type, + api_version=self.api_version, + organization=self.organization, + ) + response, stream, api_key = requestor.request( + method, + url, + params=params, + stream=stream, + headers=headers, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + for line in response + ) + else: + return util.convert_to_openai_object( + response, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + + async def arequest( + self, + method, + url, + params=None, + headers=None, + stream=False, + plain_old_data=False, + request_id: Optional[str] = None, + request_timeout: Optional[Union[float, Tuple[float, float]]] = None, + ): + if params is None: + params = self._retrieve_params + requestor = api_requestor.APIRequestor( + key=self.api_key, + api_base=self.api_base_override or self.api_base(), + api_type=self.api_type, + api_version=self.api_version, + organization=self.organization, + ) + response, stream, api_key = await requestor.arequest( + method, + url, + params=params, + stream=stream, + headers=headers, + request_id=request_id, + request_timeout=request_timeout, + ) + + if stream: + assert not isinstance(response, OpenAIResponse) # must be an iterator + return ( + util.convert_to_openai_object( + line, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + for line in response + ) + else: + return util.convert_to_openai_object( + response, + api_key, + self.api_version, + self.organization, + plain_old_data=plain_old_data, + ) + + def __repr__(self): + ident_parts = [type(self).__name__] + + obj = self.get("object") + if isinstance(obj, str): + ident_parts.append(obj) + + if isinstance(self.get("id"), str): + ident_parts.append("id=%s" % (self.get("id"),)) + + unicode_repr = "<%s at %s> JSON: %s" % ( + " ".join(ident_parts), + hex(id(self)), + str(self), + ) + + return unicode_repr + + def __str__(self): + obj = self.to_dict_recursive() + return json.dumps(obj, indent=2) + + def to_dict(self): + return dict(self) + + def to_dict_recursive(self): + d = dict(self) + for k, v in d.items(): + if isinstance(v, OpenAIObject): + d[k] = v.to_dict_recursive() + elif isinstance(v, list): + d[k] = [ + e.to_dict_recursive() if isinstance(e, OpenAIObject) else e + for e in v + ] + return d + + @property + def openai_id(self): + return self.id + + @property + def typed_api_type(self): + return ( + ApiType.from_str(self.api_type) + if self.api_type + else ApiType.from_str(openai.api_type) + ) + + # This class overrides __setitem__ to throw exceptions on inputs that it + # doesn't like. This can cause problems when we try to copy an object + # wholesale because some data that's returned from the API may not be valid + # if it was set to be set manually. Here we override the class' copy + # arguments so that we can bypass these possible exceptions on __setitem__. + def __copy__(self): + copied = OpenAIObject( + self.get("id"), + self.api_key, + api_version=self.api_version, + api_type=self.api_type, + organization=self.organization, + ) + + copied._retrieve_params = self._retrieve_params + + for k, v in self.items(): + # Call parent's __setitem__ to avoid checks that we've added in the + # overridden version that can throw exceptions. + super(OpenAIObject, copied).__setitem__(k, v) + + return copied + + # This class overrides __setitem__ to throw exceptions on inputs that it + # doesn't like. This can cause problems when we try to copy an object + # wholesale because some data that's returned from the API may not be valid + # if it was set to be set manually. Here we override the class' copy + # arguments so that we can bypass these possible exceptions on __setitem__. + def __deepcopy__(self, memo): + copied = self.__copy__() + memo[id(self)] = copied + + for k, v in self.items(): + # Call parent's __setitem__ to avoid checks that we've added in the + # overridden version that can throw exceptions. + super(OpenAIObject, copied).__setitem__(k, deepcopy(v, memo)) + + return copied diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/openai_response.py b/.pythonlibs/lib/python3.10/site-packages/openai/openai_response.py new file mode 100644 index 0000000..d2230b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/openai_response.py @@ -0,0 +1,31 @@ +from typing import Optional + + +class OpenAIResponse: + def __init__(self, data, headers): + self._headers = headers + self.data = data + + @property + def request_id(self) -> Optional[str]: + return self._headers.get("request-id") + + @property + def retry_after(self) -> Optional[int]: + try: + return int(self._headers.get("retry-after")) + except TypeError: + return None + + @property + def operation_location(self) -> Optional[str]: + return self._headers.get("operation-location") + + @property + def organization(self) -> Optional[str]: + return self._headers.get("OpenAI-Organization") + + @property + def response_ms(self) -> Optional[int]: + h = self._headers.get("Openai-Processing-Ms") + return None if h is None else round(float(h)) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/py.typed b/.pythonlibs/lib/python3.10/site-packages/openai/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..906a424 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_api_requestor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_api_requestor.cpython-310.pyc new file mode 100644 index 0000000..c71e9fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_api_requestor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_endpoints.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_endpoints.cpython-310.pyc new file mode 100644 index 0000000..8a49166 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_endpoints.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_exceptions.cpython-310.pyc new file mode 100644 index 0000000..535ed44 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_file_cli.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_file_cli.cpython-310.pyc new file mode 100644 index 0000000..93b22da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_file_cli.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_long_examples_validator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_long_examples_validator.cpython-310.pyc new file mode 100644 index 0000000..5cec033 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_long_examples_validator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_url_composition.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_url_composition.cpython-310.pyc new file mode 100644 index 0000000..251054a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_url_composition.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_util.cpython-310.pyc new file mode 100644 index 0000000..5810261 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/__pycache__/test_util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__init__.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..757a244 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/test_endpoints.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/test_endpoints.cpython-310.pyc new file mode 100644 index 0000000..f27f293 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/__pycache__/test_endpoints.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/test_endpoints.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/test_endpoints.py new file mode 100644 index 0000000..1b146e6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/asyncio/test_endpoints.py @@ -0,0 +1,90 @@ +import io +import json + +import pytest +from aiohttp import ClientSession + +import openai +from openai import error + +pytestmark = [pytest.mark.asyncio] + + +# FILE TESTS +async def test_file_upload(): + result = await openai.File.acreate( + file=io.StringIO( + json.dumps({"prompt": "test file data", "completion": "tada"}) + ), + purpose="fine-tune", + ) + assert result.purpose == "fine-tune" + assert "id" in result + + result = await openai.File.aretrieve(id=result.id) + assert result.status == "uploaded" + + +# COMPLETION TESTS +async def test_completions(): + result = await openai.Completion.acreate( + prompt="This was a test", n=5, engine="ada" + ) + assert len(result.choices) == 5 + + +async def test_completions_multiple_prompts(): + result = await openai.Completion.acreate( + prompt=["This was a test", "This was another test"], n=5, engine="ada" + ) + assert len(result.choices) == 10 + + +async def test_completions_model(): + result = await openai.Completion.acreate(prompt="This was a test", n=5, model="ada") + assert len(result.choices) == 5 + assert result.model.startswith("ada") + + +async def test_timeout_raises_error(): + # A query that should take awhile to return + with pytest.raises(error.Timeout): + await openai.Completion.acreate( + prompt="test" * 1000, + n=10, + model="ada", + max_tokens=100, + request_timeout=0.01, + ) + + +async def test_timeout_does_not_error(): + # A query that should be fast + await openai.Completion.acreate( + prompt="test", + model="ada", + request_timeout=10, + ) + + +async def test_completions_stream_finishes_global_session(): + async with ClientSession() as session: + openai.aiosession.set(session) + + # A query that should be fast + parts = [] + async for part in await openai.Completion.acreate( + prompt="test", model="ada", request_timeout=3, stream=True + ): + parts.append(part) + assert len(parts) > 1 + + +async def test_completions_stream_finishes_local_session(): + # A query that should be fast + parts = [] + async for part in await openai.Completion.acreate( + prompt="test", model="ada", request_timeout=3, stream=True + ): + parts.append(part) + assert len(parts) > 1 diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_api_requestor.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_api_requestor.py new file mode 100644 index 0000000..56e8ec8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_api_requestor.py @@ -0,0 +1,101 @@ +import json + +import pytest +import requests +from pytest_mock import MockerFixture + +from openai import Model +from openai.api_requestor import APIRequestor + + +@pytest.mark.requestor +def test_requestor_sets_request_id(mocker: MockerFixture) -> None: + # Fake out 'requests' and confirm that the X-Request-Id header is set. + + got_headers = {} + + def fake_request(self, *args, **kwargs): + nonlocal got_headers + got_headers = kwargs["headers"] + r = requests.Response() + r.status_code = 200 + r.headers["content-type"] = "application/json" + r._content = json.dumps({}).encode("utf-8") + return r + + mocker.patch("requests.sessions.Session.request", fake_request) + fake_request_id = "1234" + Model.retrieve("xxx", request_id=fake_request_id) # arbitrary API resource + got_request_id = got_headers.get("X-Request-Id") + assert got_request_id == fake_request_id + + +@pytest.mark.requestor +def test_requestor_open_ai_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="open_ai") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) + assert "Test_Header" in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "Authorization" in headers + assert headers["Authorization"] == "Bearer test_key" + + +@pytest.mark.requestor +def test_requestor_azure_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="azure") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) + assert "Test_Header" in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "api-key" in headers + assert headers["api-key"] == "test_key" + + +@pytest.mark.requestor +def test_requestor_azure_ad_headers() -> None: + api_requestor = APIRequestor(key="test_key", api_type="azure_ad") + headers = {"Test_Header": "Unit_Test_Header"} + headers = api_requestor.request_headers( + method="get", extra=headers, request_id="test_id" + ) + assert "Test_Header" in headers + assert headers["Test_Header"] == "Unit_Test_Header" + assert "Authorization" in headers + assert headers["Authorization"] == "Bearer test_key" + + +@pytest.mark.requestor +def test_requestor_cycle_sessions(mocker: MockerFixture) -> None: + # HACK: we need to purge the _thread_context to not interfere + # with other tests + from openai.api_requestor import _thread_context + + delattr(_thread_context, "session") + + api_requestor = APIRequestor(key="test_key", api_type="azure_ad") + + mock_session = mocker.MagicMock() + mocker.patch("openai.api_requestor._make_session", lambda: mock_session) + + # We don't call `session.close()` if not enough time has elapsed + api_requestor.request_raw("get", "http://example.com") + mock_session.request.assert_called() + api_requestor.request_raw("get", "http://example.com") + mock_session.close.assert_not_called() + + mocker.patch("openai.api_requestor.MAX_SESSION_LIFETIME_SECS", 0) + + # Due to 0 lifetime, the original session will be closed before the next call + # and a new session will be created + mock_session_2 = mocker.MagicMock() + mocker.patch("openai.api_requestor._make_session", lambda: mock_session_2) + api_requestor.request_raw("get", "http://example.com") + mock_session.close.assert_called() + mock_session_2.request.assert_called() + + delattr(_thread_context, "session") diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_endpoints.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_endpoints.py new file mode 100644 index 0000000..958e07f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_endpoints.py @@ -0,0 +1,118 @@ +import io +import json + +import pytest +import requests + +import openai +from openai import error + + +# FILE TESTS +def test_file_upload(): + result = openai.File.create( + file=io.StringIO( + json.dumps({"prompt": "test file data", "completion": "tada"}) + ), + purpose="fine-tune", + ) + assert result.purpose == "fine-tune" + assert "id" in result + + result = openai.File.retrieve(id=result.id) + assert result.status == "uploaded" + + +# CHAT COMPLETION TESTS +def test_chat_completions(): + result = openai.ChatCompletion.create( + model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}] + ) + assert len(result.choices) == 1 + + +def test_chat_completions_multiple(): + result = openai.ChatCompletion.create( + model="gpt-3.5-turbo", messages=[{"role": "user", "content": "Hello!"}], n=5 + ) + assert len(result.choices) == 5 + + +def test_chat_completions_streaming(): + result = None + events = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "Hello!"}], + stream=True, + ) + for result in events: + assert len(result.choices) == 1 + + +# COMPLETION TESTS +def test_completions(): + result = openai.Completion.create(prompt="This was a test", n=5, engine="ada") + assert len(result.choices) == 5 + + +def test_completions_multiple_prompts(): + result = openai.Completion.create( + prompt=["This was a test", "This was another test"], n=5, engine="ada" + ) + assert len(result.choices) == 10 + + +def test_completions_model(): + result = openai.Completion.create(prompt="This was a test", n=5, model="ada") + assert len(result.choices) == 5 + assert result.model.startswith("ada") + + +def test_timeout_raises_error(): + # A query that should take awhile to return + with pytest.raises(error.Timeout): + openai.Completion.create( + prompt="test" * 1000, + n=10, + model="ada", + max_tokens=100, + request_timeout=0.01, + ) + + +def test_timeout_does_not_error(): + # A query that should be fast + openai.Completion.create( + prompt="test", + model="ada", + request_timeout=10, + ) + + +def test_user_session(): + with requests.Session() as session: + openai.requestssession = session + + completion = openai.Completion.create( + prompt="hello world", + model="ada", + ) + assert completion + + +def test_user_session_factory(): + def factory(): + session = requests.Session() + session.mount( + "https://", + requests.adapters.HTTPAdapter(max_retries=4), + ) + return session + + openai.requestssession = factory + + completion = openai.Completion.create( + prompt="hello world", + model="ada", + ) + assert completion diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_exceptions.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_exceptions.py new file mode 100644 index 0000000..7760cdc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_exceptions.py @@ -0,0 +1,40 @@ +import pickle + +import pytest + +import openai + +EXCEPTION_TEST_CASES = [ + openai.InvalidRequestError( + "message", + "param", + code=400, + http_body={"test": "test1"}, + http_status="fail", + json_body={"text": "iono some text"}, + headers={"request-id": "asasd"}, + ), + openai.error.AuthenticationError(), + openai.error.PermissionError(), + openai.error.RateLimitError(), + openai.error.ServiceUnavailableError(), + openai.error.SignatureVerificationError("message", "sig_header?"), + openai.error.APIConnectionError("message!", should_retry=True), + openai.error.TryAgain(), + openai.error.Timeout(), + openai.error.APIError( + message="message", + code=400, + http_body={"test": "test1"}, + http_status="fail", + json_body={"text": "iono some text"}, + headers={"request-id": "asasd"}, + ), + openai.error.OpenAIError(), +] + + +class TestExceptions: + @pytest.mark.parametrize("error", EXCEPTION_TEST_CASES) + def test_exceptions_are_pickleable(self, error) -> None: + assert error.__repr__() == pickle.loads(pickle.dumps(error)).__repr__() diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_file_cli.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_file_cli.py new file mode 100644 index 0000000..69ea29e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_file_cli.py @@ -0,0 +1,39 @@ +import json +import subprocess +import time +from tempfile import NamedTemporaryFile + +STILL_PROCESSING = "File is still processing. Check back later." + + +def test_file_cli() -> None: + contents = json.dumps({"prompt": "1 + 3 =", "completion": "4"}) + "\n" + with NamedTemporaryFile(suffix=".jsonl", mode="wb") as train_file: + train_file.write(contents.encode("utf-8")) + train_file.flush() + create_output = subprocess.check_output( + ["openai", "api", "files.create", "-f", train_file.name, "-p", "fine-tune"] + ) + file_obj = json.loads(create_output) + assert file_obj["bytes"] == len(contents) + file_id: str = file_obj["id"] + assert file_id.startswith("file-") + start_time = time.time() + while True: + delete_result = subprocess.run( + ["openai", "api", "files.delete", "-i", file_id], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf-8", + ) + if delete_result.returncode == 0: + break + elif STILL_PROCESSING in delete_result.stderr: + time.sleep(0.5) + if start_time + 60 < time.time(): + raise RuntimeError("timed out waiting for file to become available") + continue + else: + raise RuntimeError( + f"delete failed: stdout={delete_result.stdout} stderr={delete_result.stderr}" + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_long_examples_validator.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_long_examples_validator.py new file mode 100644 index 0000000..949a7cb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_long_examples_validator.py @@ -0,0 +1,54 @@ +import json +import subprocess +from tempfile import NamedTemporaryFile + +import pytest + +from openai.datalib.numpy_helper import HAS_NUMPY, NUMPY_INSTRUCTIONS +from openai.datalib.pandas_helper import HAS_PANDAS, PANDAS_INSTRUCTIONS + + +@pytest.mark.skipif(not HAS_PANDAS, reason=PANDAS_INSTRUCTIONS) +@pytest.mark.skipif(not HAS_NUMPY, reason=NUMPY_INSTRUCTIONS) +def test_long_examples_validator() -> None: + """ + Ensures that long_examples_validator() handles previously applied recommendations, + namely dropped duplicates, without resulting in a KeyError. + """ + + # data + short_prompt = "a prompt " + long_prompt = short_prompt * 500 + + short_completion = "a completion " + long_completion = short_completion * 500 + + # the order of these matters + unprepared_training_data = [ + {"prompt": long_prompt, "completion": long_completion}, # 1 of 2 duplicates + {"prompt": short_prompt, "completion": short_completion}, + {"prompt": long_prompt, "completion": long_completion}, # 2 of 2 duplicates + ] + + with NamedTemporaryFile(suffix=".jsonl", mode="w") as training_data: + print(training_data.name) + for prompt_completion_row in unprepared_training_data: + training_data.write(json.dumps(prompt_completion_row) + "\n") + training_data.flush() + + prepared_data_cmd_output = subprocess.run( + [f"openai tools fine_tunes.prepare_data -f {training_data.name}"], + stdout=subprocess.PIPE, + text=True, + input="y\ny\ny\ny\ny", # apply all recommendations, one at a time + stderr=subprocess.PIPE, + encoding="utf-8", + shell=True, + ) + + # validate data was prepared successfully + assert prepared_data_cmd_output.stderr == "" + # validate get_long_indexes() applied during optional_fn() call in long_examples_validator() + assert "indices of the long examples has changed" in prepared_data_cmd_output.stdout + + return prepared_data_cmd_output.stdout diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_url_composition.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_url_composition.py new file mode 100644 index 0000000..5034354 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_url_composition.py @@ -0,0 +1,209 @@ +from sys import api_version + +import pytest + +from openai import Completion, Engine +from openai.util import ApiType + + +@pytest.mark.url +def test_completions_url_composition_azure() -> None: + url = Completion.class_url("test_engine", "azure", "2021-11-01-preview") + assert ( + url + == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_completions_url_composition_azure_ad() -> None: + url = Completion.class_url("test_engine", "azure_ad", "2021-11-01-preview") + assert ( + url + == "/openai/deployments/test_engine/completions?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_completions_url_composition_default() -> None: + url = Completion.class_url("test_engine") + assert url == "/engines/test_engine/completions" + + +@pytest.mark.url +def test_completions_url_composition_open_ai() -> None: + url = Completion.class_url("test_engine", "open_ai") + assert url == "/engines/test_engine/completions" + + +@pytest.mark.url +def test_completions_url_composition_invalid_type() -> None: + with pytest.raises(Exception): + url = Completion.class_url("test_engine", "invalid") + + +@pytest.mark.url +def test_completions_url_composition_instance_url_azure() -> None: + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure", + api_version="2021-11-01-preview", + ) + url = completion.instance_url() + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_completions_url_composition_instance_url_azure_ad() -> None: + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure_ad", + api_version="2021-11-01-preview", + ) + url = completion.instance_url() + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_completions_url_composition_instance_url_azure_no_version() -> None: + completion = Completion( + id="test_id", engine="test_engine", api_type="azure", api_version=None + ) + with pytest.raises(Exception): + completion.instance_url() + + +@pytest.mark.url +def test_completions_url_composition_instance_url_default() -> None: + completion = Completion(id="test_id", engine="test_engine") + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id" + + +@pytest.mark.url +def test_completions_url_composition_instance_url_open_ai() -> None: + completion = Completion( + id="test_id", + engine="test_engine", + api_type="open_ai", + api_version="2021-11-01-preview", + ) + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id" + + +@pytest.mark.url +def test_completions_url_composition_instance_url_invalid() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="invalid") + with pytest.raises(Exception): + url = completion.instance_url() + + +@pytest.mark.url +def test_completions_url_composition_instance_url_timeout_azure() -> None: + completion = Completion( + id="test_id", + engine="test_engine", + api_type="azure", + api_version="2021-11-01-preview", + ) + completion["timeout"] = 12 + url = completion.instance_url() + assert ( + url + == "/openai/deployments/test_engine/completions/test_id?api-version=2021-11-01-preview&timeout=12" + ) + + +@pytest.mark.url +def test_completions_url_composition_instance_url_timeout_openai() -> None: + completion = Completion(id="test_id", engine="test_engine", api_type="open_ai") + completion["timeout"] = 12 + url = completion.instance_url() + assert url == "/engines/test_engine/completions/test_id?timeout=12" + + +@pytest.mark.url +def test_engine_search_url_composition_azure() -> None: + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + url = engine.instance_url("test_operation") + assert ( + url + == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_engine_search_url_composition_azure_ad() -> None: + engine = Engine(id="test_id", api_type="azure_ad", api_version="2021-11-01-preview") + assert engine.api_type == "azure_ad" + assert engine.typed_api_type == ApiType.AZURE_AD + url = engine.instance_url("test_operation") + assert ( + url + == "/openai/deployments/test_id/test_operation?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_engine_search_url_composition_azure_no_version() -> None: + engine = Engine(id="test_id", api_type="azure", api_version=None) + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + with pytest.raises(Exception): + engine.instance_url("test_operation") + + +@pytest.mark.url +def test_engine_search_url_composition_azure_no_operation() -> None: + engine = Engine(id="test_id", api_type="azure", api_version="2021-11-01-preview") + assert engine.api_type == "azure" + assert engine.typed_api_type == ApiType.AZURE + assert ( + engine.instance_url() + == "/openai/engines/test_id?api-version=2021-11-01-preview" + ) + + +@pytest.mark.url +def test_engine_search_url_composition_default() -> None: + engine = Engine(id="test_id") + assert engine.api_type == None + assert engine.typed_api_type == ApiType.OPEN_AI + url = engine.instance_url() + assert url == "/engines/test_id" + + +@pytest.mark.url +def test_engine_search_url_composition_open_ai() -> None: + engine = Engine(id="test_id", api_type="open_ai") + assert engine.api_type == "open_ai" + assert engine.typed_api_type == ApiType.OPEN_AI + url = engine.instance_url() + assert url == "/engines/test_id" + + +@pytest.mark.url +def test_engine_search_url_composition_invalid_type() -> None: + engine = Engine(id="test_id", api_type="invalid") + assert engine.api_type == "invalid" + with pytest.raises(Exception): + assert engine.typed_api_type == ApiType.OPEN_AI + + +@pytest.mark.url +def test_engine_search_url_composition_invalid_search() -> None: + engine = Engine(id="test_id", api_type="invalid") + assert engine.api_type == "invalid" + with pytest.raises(Exception): + engine.search() diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_util.py b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_util.py new file mode 100644 index 0000000..6220ccb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/tests/test_util.py @@ -0,0 +1,55 @@ +import json +from tempfile import NamedTemporaryFile + +import pytest + +import openai +from openai import util + + +@pytest.fixture(scope="function") +def api_key_file(): + saved_path = openai.api_key_path + try: + with NamedTemporaryFile(prefix="openai-api-key", mode="wt") as tmp: + openai.api_key_path = tmp.name + yield tmp + finally: + openai.api_key_path = saved_path + + +def test_openai_api_key_path(api_key_file) -> None: + print("sk-foo", file=api_key_file) + api_key_file.flush() + assert util.default_api_key() == "sk-foo" + + +def test_openai_api_key_path_with_malformed_key(api_key_file) -> None: + print("malformed-api-key", file=api_key_file) + api_key_file.flush() + with pytest.raises(ValueError, match="Malformed API key"): + util.default_api_key() + + +def test_key_order_openai_object_rendering() -> None: + sample_response = { + "id": "chatcmpl-7NaPEA6sgX7LnNPyKPbRlsyqLbr5V", + "object": "chat.completion", + "created": 1685855844, + "model": "gpt-3.5-turbo-0301", + "usage": {"prompt_tokens": 57, "completion_tokens": 40, "total_tokens": 97}, + "choices": [ + { + "message": { + "role": "assistant", + "content": "The 2020 World Series was played at Globe Life Field in Arlington, Texas. It was the first time that the World Series was played at a neutral site because of the COVID-19 pandemic.", + }, + "finish_reason": "stop", + "index": 0, + } + ], + } + + oai_object = util.convert_to_openai_object(sample_response) + # The `__str__` method was sorting while dumping to json + assert list(json.loads(str(oai_object)).keys()) == list(sample_response.keys()) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/upload_progress.py b/.pythonlibs/lib/python3.10/site-packages/openai/upload_progress.py new file mode 100644 index 0000000..e4da62a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/upload_progress.py @@ -0,0 +1,52 @@ +import io + + +class CancelledError(Exception): + def __init__(self, msg): + self.msg = msg + Exception.__init__(self, msg) + + def __str__(self): + return self.msg + + __repr__ = __str__ + + +class BufferReader(io.BytesIO): + def __init__(self, buf=b"", desc=None): + self._len = len(buf) + io.BytesIO.__init__(self, buf) + self._progress = 0 + self._callback = progress(len(buf), desc=desc) + + def __len__(self): + return self._len + + def read(self, n=-1): + chunk = io.BytesIO.read(self, n) + self._progress += len(chunk) + if self._callback: + try: + self._callback(self._progress) + except Exception as e: # catches exception from the callback + raise CancelledError("The upload was cancelled: {}".format(e)) + return chunk + + +def progress(total, desc): + import tqdm # type: ignore + + meter = tqdm.tqdm(total=total, unit_scale=True, desc=desc) + + def incr(progress): + meter.n = progress + if progress == total: + meter.close() + else: + meter.refresh() + + return incr + + +def MB(i): + return int(i // 1024**2) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/util.py b/.pythonlibs/lib/python3.10/site-packages/openai/util.py new file mode 100644 index 0000000..5501d5b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/util.py @@ -0,0 +1,188 @@ +import logging +import os +import re +import sys +from enum import Enum +from typing import Optional + +import openai + +OPENAI_LOG = os.environ.get("OPENAI_LOG") + +logger = logging.getLogger("openai") + +__all__ = [ + "log_info", + "log_debug", + "log_warn", + "logfmt", +] + +api_key_to_header = ( + lambda api, key: {"Authorization": f"Bearer {key}"} + if api in (ApiType.OPEN_AI, ApiType.AZURE_AD) + else {"api-key": f"{key}"} +) + + +class ApiType(Enum): + AZURE = 1 + OPEN_AI = 2 + AZURE_AD = 3 + + @staticmethod + def from_str(label): + if label.lower() == "azure": + return ApiType.AZURE + elif label.lower() in ("azure_ad", "azuread"): + return ApiType.AZURE_AD + elif label.lower() in ("open_ai", "openai"): + return ApiType.OPEN_AI + else: + raise openai.error.InvalidAPIType( + "The API type provided in invalid. Please select one of the supported API types: 'azure', 'azure_ad', 'open_ai'" + ) + + +def _console_log_level(): + if openai.log in ["debug", "info"]: + return openai.log + elif OPENAI_LOG in ["debug", "info"]: + return OPENAI_LOG + else: + return None + + +def log_debug(message, **params): + msg = logfmt(dict(message=message, **params)) + if _console_log_level() == "debug": + print(msg, file=sys.stderr) + logger.debug(msg) + + +def log_info(message, **params): + msg = logfmt(dict(message=message, **params)) + if _console_log_level() in ["debug", "info"]: + print(msg, file=sys.stderr) + logger.info(msg) + + +def log_warn(message, **params): + msg = logfmt(dict(message=message, **params)) + print(msg, file=sys.stderr) + logger.warn(msg) + + +def logfmt(props): + def fmt(key, val): + # Handle case where val is a bytes or bytesarray + if hasattr(val, "decode"): + val = val.decode("utf-8") + # Check if val is already a string to avoid re-encoding into ascii. + if not isinstance(val, str): + val = str(val) + if re.search(r"\s", val): + val = repr(val) + # key should already be a string + if re.search(r"\s", key): + key = repr(key) + return "{key}={val}".format(key=key, val=val) + + return " ".join([fmt(key, val) for key, val in sorted(props.items())]) + + +def get_object_classes(): + # This is here to avoid a circular dependency + from openai.object_classes import OBJECT_CLASSES + + return OBJECT_CLASSES + + +def convert_to_openai_object( + resp, + api_key=None, + api_version=None, + organization=None, + engine=None, + plain_old_data=False, +): + # If we get a OpenAIResponse, we'll want to return a OpenAIObject. + + response_ms: Optional[int] = None + if isinstance(resp, openai.openai_response.OpenAIResponse): + organization = resp.organization + response_ms = resp.response_ms + resp = resp.data + + if plain_old_data: + return resp + elif isinstance(resp, list): + return [ + convert_to_openai_object( + i, api_key, api_version, organization, engine=engine + ) + for i in resp + ] + elif isinstance(resp, dict) and not isinstance( + resp, openai.openai_object.OpenAIObject + ): + resp = resp.copy() + klass_name = resp.get("object") + if isinstance(klass_name, str): + klass = get_object_classes().get( + klass_name, openai.openai_object.OpenAIObject + ) + else: + klass = openai.openai_object.OpenAIObject + + return klass.construct_from( + resp, + api_key=api_key, + api_version=api_version, + organization=organization, + response_ms=response_ms, + engine=engine, + ) + else: + return resp + + +def convert_to_dict(obj): + """Converts a OpenAIObject back to a regular dict. + + Nested OpenAIObjects are also converted back to regular dicts. + + :param obj: The OpenAIObject to convert. + + :returns: The OpenAIObject as a dict. + """ + if isinstance(obj, list): + return [convert_to_dict(i) for i in obj] + # This works by virtue of the fact that OpenAIObjects _are_ dicts. The dict + # comprehension returns a regular dict and recursively applies the + # conversion to each value. + elif isinstance(obj, dict): + return {k: convert_to_dict(v) for k, v in obj.items()} + else: + return obj + + +def merge_dicts(x, y): + z = x.copy() + z.update(y) + return z + + +def default_api_key() -> str: + if openai.api_key_path: + with open(openai.api_key_path, "rt") as k: + api_key = k.read().strip() + if not api_key.startswith("sk-"): + raise ValueError(f"Malformed API key in {openai.api_key_path}.") + return api_key + elif openai.api_key is not None: + return openai.api_key + else: + raise openai.error.AuthenticationError( + "No API key provided. You can set your API key in code using 'openai.api_key = ', or you can set the environment variable OPENAI_API_KEY=). If your API key is stored in a file, you can point the openai module at it with 'openai.api_key_path = '. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details." + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/validators.py b/.pythonlibs/lib/python3.10/site-packages/openai/validators.py new file mode 100644 index 0000000..078179a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/validators.py @@ -0,0 +1,852 @@ +import os +import sys +from typing import Any, Callable, NamedTuple, Optional + +from openai.datalib.pandas_helper import assert_has_pandas +from openai.datalib.pandas_helper import pandas as pd + + +class Remediation(NamedTuple): + name: str + immediate_msg: Optional[str] = None + necessary_msg: Optional[str] = None + necessary_fn: Optional[Callable[[Any], Any]] = None + optional_msg: Optional[str] = None + optional_fn: Optional[Callable[[Any], Any]] = None + error_msg: Optional[str] = None + + +def num_examples_validator(df): + """ + This validator will only print out the number of examples and recommend to the user to increase the number of examples if less than 100. + """ + MIN_EXAMPLES = 100 + optional_suggestion = ( + "" + if len(df) >= MIN_EXAMPLES + else ". In general, we recommend having at least a few hundred examples. We've found that performance tends to linearly increase for every doubling of the number of examples" + ) + immediate_msg = ( + f"\n- Your file contains {len(df)} prompt-completion pairs{optional_suggestion}" + ) + return Remediation(name="num_examples", immediate_msg=immediate_msg) + + +def necessary_column_validator(df, necessary_column): + """ + This validator will ensure that the necessary column is present in the dataframe. + """ + + def lower_case_column(df, column): + cols = [c for c in df.columns if str(c).lower() == column] + df.rename(columns={cols[0]: column.lower()}, inplace=True) + return df + + immediate_msg = None + necessary_fn = None + necessary_msg = None + error_msg = None + + if necessary_column not in df.columns: + if necessary_column in [str(c).lower() for c in df.columns]: + + def lower_case_column_creator(df): + return lower_case_column(df, necessary_column) + + necessary_fn = lower_case_column_creator + immediate_msg = ( + f"\n- The `{necessary_column}` column/key should be lowercase" + ) + necessary_msg = f"Lower case column name to `{necessary_column}`" + else: + error_msg = f"`{necessary_column}` column/key is missing. Please make sure you name your columns/keys appropriately, then retry" + + return Remediation( + name="necessary_column", + immediate_msg=immediate_msg, + necessary_msg=necessary_msg, + necessary_fn=necessary_fn, + error_msg=error_msg, + ) + + +def additional_column_validator(df, fields=["prompt", "completion"]): + """ + This validator will remove additional columns from the dataframe. + """ + additional_columns = [] + necessary_msg = None + immediate_msg = None + necessary_fn = None + if len(df.columns) > 2: + additional_columns = [c for c in df.columns if c not in fields] + warn_message = "" + for ac in additional_columns: + dups = [c for c in additional_columns if ac in c] + if len(dups) > 0: + warn_message += f"\n WARNING: Some of the additional columns/keys contain `{ac}` in their name. These will be ignored, and the column/key `{ac}` will be used instead. This could also result from a duplicate column/key in the provided file." + immediate_msg = f"\n- The input file should contain exactly two columns/keys per row. Additional columns/keys present are: {additional_columns}{warn_message}" + necessary_msg = f"Remove additional columns/keys: {additional_columns}" + + def necessary_fn(x): + return x[fields] + + return Remediation( + name="additional_column", + immediate_msg=immediate_msg, + necessary_msg=necessary_msg, + necessary_fn=necessary_fn, + ) + + +def non_empty_field_validator(df, field="completion"): + """ + This validator will ensure that no completion is empty. + """ + necessary_msg = None + necessary_fn = None + immediate_msg = None + + if df[field].apply(lambda x: x == "").any() or df[field].isnull().any(): + empty_rows = (df[field] == "") | (df[field].isnull()) + empty_indexes = df.reset_index().index[empty_rows].tolist() + immediate_msg = f"\n- `{field}` column/key should not contain empty strings. These are rows: {empty_indexes}" + + def necessary_fn(x): + return x[x[field] != ""].dropna(subset=[field]) + + necessary_msg = f"Remove {len(empty_indexes)} rows with empty {field}s" + return Remediation( + name=f"empty_{field}", + immediate_msg=immediate_msg, + necessary_msg=necessary_msg, + necessary_fn=necessary_fn, + ) + + +def duplicated_rows_validator(df, fields=["prompt", "completion"]): + """ + This validator will suggest to the user to remove duplicate rows if they exist. + """ + duplicated_rows = df.duplicated(subset=fields) + duplicated_indexes = df.reset_index().index[duplicated_rows].tolist() + immediate_msg = None + optional_msg = None + optional_fn = None + + if len(duplicated_indexes) > 0: + immediate_msg = f"\n- There are {len(duplicated_indexes)} duplicated {'-'.join(fields)} sets. These are rows: {duplicated_indexes}" + optional_msg = f"Remove {len(duplicated_indexes)} duplicate rows" + + def optional_fn(x): + return x.drop_duplicates(subset=fields) + + return Remediation( + name="duplicated_rows", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + ) + + +def long_examples_validator(df): + """ + This validator will suggest to the user to remove examples that are too long. + """ + immediate_msg = None + optional_msg = None + optional_fn = None + + ft_type = infer_task_type(df) + if ft_type != "open-ended generation": + + def get_long_indexes(d): + long_examples = d.apply( + lambda x: len(x.prompt) + len(x.completion) > 10000, axis=1 + ) + return d.reset_index().index[long_examples].tolist() + + long_indexes = get_long_indexes(df) + + if len(long_indexes) > 0: + immediate_msg = f"\n- There are {len(long_indexes)} examples that are very long. These are rows: {long_indexes}\nFor conditional generation, and for classification the examples shouldn't be longer than 2048 tokens." + optional_msg = f"Remove {len(long_indexes)} long examples" + + def optional_fn(x): + + long_indexes_to_drop = get_long_indexes(x) + if long_indexes != long_indexes_to_drop: + sys.stdout.write( + f"The indices of the long examples has changed as a result of a previously applied recommendation.\nThe {len(long_indexes_to_drop)} long examples to be dropped are now at the following indices: {long_indexes_to_drop}\n" + ) + return x.drop(long_indexes_to_drop) + + return Remediation( + name="long_examples", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + ) + + +def common_prompt_suffix_validator(df): + """ + This validator will suggest to add a common suffix to the prompt if one doesn't already exist in case of classification or conditional generation. + """ + error_msg = None + immediate_msg = None + optional_msg = None + optional_fn = None + + # Find a suffix which is not contained within the prompt otherwise + suggested_suffix = "\n\n### =>\n\n" + suffix_options = [ + " ->", + "\n\n###\n\n", + "\n\n===\n\n", + "\n\n---\n\n", + "\n\n===>\n\n", + "\n\n--->\n\n", + ] + for suffix_option in suffix_options: + if suffix_option == " ->": + if df.prompt.str.contains("\n").any(): + continue + if df.prompt.str.contains(suffix_option, regex=False).any(): + continue + suggested_suffix = suffix_option + break + display_suggested_suffix = suggested_suffix.replace("\n", "\\n") + + ft_type = infer_task_type(df) + if ft_type == "open-ended generation": + return Remediation(name="common_suffix") + + def add_suffix(x, suffix): + x["prompt"] += suffix + return x + + common_suffix = get_common_xfix(df.prompt, xfix="suffix") + if (df.prompt == common_suffix).all(): + error_msg = f"All prompts are identical: `{common_suffix}`\nConsider leaving the prompts blank if you want to do open-ended generation, otherwise ensure prompts are different" + return Remediation(name="common_suffix", error_msg=error_msg) + + if common_suffix != "": + common_suffix_new_line_handled = common_suffix.replace("\n", "\\n") + immediate_msg = ( + f"\n- All prompts end with suffix `{common_suffix_new_line_handled}`" + ) + if len(common_suffix) > 10: + immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`" + if ( + df.prompt.str[: -len(common_suffix)] + .str.contains(common_suffix, regex=False) + .any() + ): + immediate_msg += f"\n WARNING: Some of your prompts contain the suffix `{common_suffix}` more than once. We strongly suggest that you review your prompts and add a unique suffix" + + else: + immediate_msg = "\n- Your data does not contain a common separator at the end of your prompts. Having a separator string appended to the end of the prompt makes it clearer to the fine-tuned model where the completion should begin. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples. If you intend to do open-ended generation, then you should leave the prompts empty" + + if common_suffix == "": + optional_msg = ( + f"Add a suffix separator `{display_suggested_suffix}` to all prompts" + ) + + def optional_fn(x): + return add_suffix(x, suggested_suffix) + + return Remediation( + name="common_completion_suffix", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + error_msg=error_msg, + ) + + +def common_prompt_prefix_validator(df): + """ + This validator will suggest to remove a common prefix from the prompt if a long one exist. + """ + MAX_PREFIX_LEN = 12 + + immediate_msg = None + optional_msg = None + optional_fn = None + + common_prefix = get_common_xfix(df.prompt, xfix="prefix") + if common_prefix == "": + return Remediation(name="common_prefix") + + def remove_common_prefix(x, prefix): + x["prompt"] = x["prompt"].str[len(prefix) :] + return x + + if (df.prompt == common_prefix).all(): + # already handled by common_suffix_validator + return Remediation(name="common_prefix") + + if common_prefix != "": + immediate_msg = f"\n- All prompts start with prefix `{common_prefix}`" + if MAX_PREFIX_LEN < len(common_prefix): + immediate_msg += ". Fine-tuning doesn't require the instruction specifying the task, or a few-shot example scenario. Most of the time you should only add the input data into the prompt, and the desired output into the completion" + optional_msg = f"Remove prefix `{common_prefix}` from all prompts" + + def optional_fn(x): + return remove_common_prefix(x, common_prefix) + + return Remediation( + name="common_prompt_prefix", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + ) + + +def common_completion_prefix_validator(df): + """ + This validator will suggest to remove a common prefix from the completion if a long one exist. + """ + MAX_PREFIX_LEN = 5 + + common_prefix = get_common_xfix(df.completion, xfix="prefix") + ws_prefix = len(common_prefix) > 0 and common_prefix[0] == " " + if len(common_prefix) < MAX_PREFIX_LEN: + return Remediation(name="common_prefix") + + def remove_common_prefix(x, prefix, ws_prefix): + x["completion"] = x["completion"].str[len(prefix) :] + if ws_prefix: + # keep the single whitespace as prefix + x["completion"] = " " + x["completion"] + return x + + if (df.completion == common_prefix).all(): + # already handled by common_suffix_validator + return Remediation(name="common_prefix") + + immediate_msg = f"\n- All completions start with prefix `{common_prefix}`. Most of the time you should only add the output data into the completion, without any prefix" + optional_msg = f"Remove prefix `{common_prefix}` from all completions" + + def optional_fn(x): + return remove_common_prefix(x, common_prefix, ws_prefix) + + return Remediation( + name="common_completion_prefix", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + ) + + +def common_completion_suffix_validator(df): + """ + This validator will suggest to add a common suffix to the completion if one doesn't already exist in case of classification or conditional generation. + """ + error_msg = None + immediate_msg = None + optional_msg = None + optional_fn = None + + ft_type = infer_task_type(df) + if ft_type == "open-ended generation" or ft_type == "classification": + return Remediation(name="common_suffix") + + common_suffix = get_common_xfix(df.completion, xfix="suffix") + if (df.completion == common_suffix).all(): + error_msg = f"All completions are identical: `{common_suffix}`\nEnsure completions are different, otherwise the model will just repeat `{common_suffix}`" + return Remediation(name="common_suffix", error_msg=error_msg) + + # Find a suffix which is not contained within the completion otherwise + suggested_suffix = " [END]" + suffix_options = [ + "\n", + ".", + " END", + "***", + "+++", + "&&&", + "$$$", + "@@@", + "%%%", + ] + for suffix_option in suffix_options: + if df.completion.str.contains(suffix_option, regex=False).any(): + continue + suggested_suffix = suffix_option + break + display_suggested_suffix = suggested_suffix.replace("\n", "\\n") + + def add_suffix(x, suffix): + x["completion"] += suffix + return x + + if common_suffix != "": + common_suffix_new_line_handled = common_suffix.replace("\n", "\\n") + immediate_msg = ( + f"\n- All completions end with suffix `{common_suffix_new_line_handled}`" + ) + if len(common_suffix) > 10: + immediate_msg += f". This suffix seems very long. Consider replacing with a shorter suffix, such as `{display_suggested_suffix}`" + if ( + df.completion.str[: -len(common_suffix)] + .str.contains(common_suffix, regex=False) + .any() + ): + immediate_msg += f"\n WARNING: Some of your completions contain the suffix `{common_suffix}` more than once. We suggest that you review your completions and add a unique ending" + + else: + immediate_msg = "\n- Your data does not contain a common ending at the end of your completions. Having a common ending string appended to the end of the completion makes it clearer to the fine-tuned model where the completion should end. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more detail and examples." + + if common_suffix == "": + optional_msg = ( + f"Add a suffix ending `{display_suggested_suffix}` to all completions" + ) + + def optional_fn(x): + return add_suffix(x, suggested_suffix) + + return Remediation( + name="common_completion_suffix", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + error_msg=error_msg, + ) + + +def completions_space_start_validator(df): + """ + This validator will suggest to add a space at the start of the completion if it doesn't already exist. This helps with tokenization. + """ + + def add_space_start(x): + x["completion"] = x["completion"].apply( + lambda x: ("" if x[0] == " " else " ") + x + ) + return x + + optional_msg = None + optional_fn = None + immediate_msg = None + + if df.completion.str[:1].nunique() != 1 or df.completion.values[0][0] != " ": + immediate_msg = "\n- The completion should start with a whitespace character (` `). This tends to produce better results due to the tokenization we use. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details" + optional_msg = "Add a whitespace character to the beginning of the completion" + optional_fn = add_space_start + return Remediation( + name="completion_space_start", + immediate_msg=immediate_msg, + optional_msg=optional_msg, + optional_fn=optional_fn, + ) + + +def lower_case_validator(df, column): + """ + This validator will suggest to lowercase the column values, if more than a third of letters are uppercase. + """ + + def lower_case(x): + x[column] = x[column].str.lower() + return x + + count_upper = ( + df[column] + .apply(lambda x: sum(1 for c in x if c.isalpha() and c.isupper())) + .sum() + ) + count_lower = ( + df[column] + .apply(lambda x: sum(1 for c in x if c.isalpha() and c.islower())) + .sum() + ) + + if count_upper * 2 > count_lower: + return Remediation( + name="lower_case", + immediate_msg=f"\n- More than a third of your `{column}` column/key is uppercase. Uppercase {column}s tends to perform worse than a mixture of case encountered in normal language. We recommend to lower case the data if that makes sense in your domain. See https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset for more details", + optional_msg=f"Lowercase all your data in column/key `{column}`", + optional_fn=lower_case, + ) + + +def read_any_format(fname, fields=["prompt", "completion"]): + """ + This function will read a file saved in .csv, .json, .txt, .xlsx or .tsv format using pandas. + - for .xlsx it will read the first sheet + - for .txt it will assume completions and split on newline + """ + assert_has_pandas() + remediation = None + necessary_msg = None + immediate_msg = None + error_msg = None + df = None + + if os.path.isfile(fname): + try: + if fname.lower().endswith(".csv") or fname.lower().endswith(".tsv"): + file_extension_str, separator = ( + ("CSV", ",") if fname.lower().endswith(".csv") else ("TSV", "\t") + ) + immediate_msg = f"\n- Based on your file extension, your file is formatted as a {file_extension_str} file" + necessary_msg = ( + f"Your format `{file_extension_str}` will be converted to `JSONL`" + ) + df = pd.read_csv(fname, sep=separator, dtype=str).fillna("") + elif fname.lower().endswith(".xlsx"): + immediate_msg = "\n- Based on your file extension, your file is formatted as an Excel file" + necessary_msg = "Your format `XLSX` will be converted to `JSONL`" + xls = pd.ExcelFile(fname) + sheets = xls.sheet_names + if len(sheets) > 1: + immediate_msg += "\n- Your Excel file contains more than one sheet. Please either save as csv or ensure all data is present in the first sheet. WARNING: Reading only the first sheet..." + df = pd.read_excel(fname, dtype=str).fillna("") + elif fname.lower().endswith(".txt"): + immediate_msg = ( + "\n- Based on your file extension, you provided a text file" + ) + necessary_msg = "Your format `TXT` will be converted to `JSONL`" + with open(fname, "r") as f: + content = f.read() + df = pd.DataFrame( + [["", line] for line in content.split("\n")], + columns=fields, + dtype=str, + ).fillna("") + elif fname.lower().endswith(".jsonl"): + df = pd.read_json(fname, lines=True, dtype=str).fillna("") + if len(df) == 1: + # this is NOT what we expect for a .jsonl file + immediate_msg = "\n- Your JSONL file appears to be in a JSON format. Your file will be converted to JSONL format" + necessary_msg = "Your format `JSON` will be converted to `JSONL`" + df = pd.read_json(fname, dtype=str).fillna("") + else: + pass # this is what we expect for a .jsonl file + elif fname.lower().endswith(".json"): + try: + # to handle case where .json file is actually a .jsonl file + df = pd.read_json(fname, lines=True, dtype=str).fillna("") + if len(df) == 1: + # this code path corresponds to a .json file that has one line + df = pd.read_json(fname, dtype=str).fillna("") + else: + # this is NOT what we expect for a .json file + immediate_msg = "\n- Your JSON file appears to be in a JSONL format. Your file will be converted to JSONL format" + necessary_msg = ( + "Your format `JSON` will be converted to `JSONL`" + ) + except ValueError: + # this code path corresponds to a .json file that has multiple lines (i.e. it is indented) + df = pd.read_json(fname, dtype=str).fillna("") + else: + error_msg = "Your file must have one of the following extensions: .CSV, .TSV, .XLSX, .TXT, .JSON or .JSONL" + if "." in fname: + error_msg += f" Your file `{fname}` ends with the extension `.{fname.split('.')[-1]}` which is not supported." + else: + error_msg += f" Your file `{fname}` is missing a file extension." + + except (ValueError, TypeError): + file_extension_str = fname.split(".")[-1].upper() + error_msg = f"Your file `{fname}` does not appear to be in valid {file_extension_str} format. Please ensure your file is formatted as a valid {file_extension_str} file." + + else: + error_msg = f"File {fname} does not exist." + + remediation = Remediation( + name="read_any_format", + necessary_msg=necessary_msg, + immediate_msg=immediate_msg, + error_msg=error_msg, + ) + return df, remediation + + +def format_inferrer_validator(df): + """ + This validator will infer the likely fine-tuning format of the data, and display it to the user if it is classification. + It will also suggest to use ada and explain train/validation split benefits. + """ + ft_type = infer_task_type(df) + immediate_msg = None + if ft_type == "classification": + immediate_msg = f"\n- Based on your data it seems like you're trying to fine-tune a model for {ft_type}\n- For classification, we recommend you try one of the faster and cheaper models, such as `ada`\n- For classification, you can estimate the expected model performance by keeping a held out dataset, which is not used for training" + return Remediation(name="num_examples", immediate_msg=immediate_msg) + + +def apply_necessary_remediation(df, remediation): + """ + This function will apply a necessary remediation to a dataframe, or print an error message if one exists. + """ + if remediation.error_msg is not None: + sys.stderr.write( + f"\n\nERROR in {remediation.name} validator: {remediation.error_msg}\n\nAborting..." + ) + sys.exit(1) + if remediation.immediate_msg is not None: + sys.stdout.write(remediation.immediate_msg) + if remediation.necessary_fn is not None: + df = remediation.necessary_fn(df) + return df + + +def accept_suggestion(input_text, auto_accept): + sys.stdout.write(input_text) + if auto_accept: + sys.stdout.write("Y\n") + return True + return input().lower() != "n" + + +def apply_optional_remediation(df, remediation, auto_accept): + """ + This function will apply an optional remediation to a dataframe, based on the user input. + """ + optional_applied = False + input_text = f"- [Recommended] {remediation.optional_msg} [Y/n]: " + if remediation.optional_msg is not None: + if accept_suggestion(input_text, auto_accept): + df = remediation.optional_fn(df) + optional_applied = True + if remediation.necessary_msg is not None: + sys.stdout.write(f"- [Necessary] {remediation.necessary_msg}\n") + return df, optional_applied + + +def estimate_fine_tuning_time(df): + """ + Estimate the time it'll take to fine-tune the dataset + """ + ft_format = infer_task_type(df) + expected_time = 1.0 + if ft_format == "classification": + num_examples = len(df) + expected_time = num_examples * 1.44 + else: + size = df.memory_usage(index=True).sum() + expected_time = size * 0.0515 + + def format_time(time): + if time < 60: + return f"{round(time, 2)} seconds" + elif time < 3600: + return f"{round(time / 60, 2)} minutes" + elif time < 86400: + return f"{round(time / 3600, 2)} hours" + else: + return f"{round(time / 86400, 2)} days" + + time_string = format_time(expected_time + 140) + sys.stdout.write( + f"Once your model starts training, it'll approximately take {time_string} to train a `curie` model, and less for `ada` and `babbage`. Queue will approximately take half an hour per job ahead of you.\n" + ) + + +def get_outfnames(fname, split): + suffixes = ["_train", "_valid"] if split else [""] + i = 0 + while True: + index_suffix = f" ({i})" if i > 0 else "" + candidate_fnames = [ + os.path.splitext(fname)[0] + "_prepared" + suffix + index_suffix + ".jsonl" + for suffix in suffixes + ] + if not any(os.path.isfile(f) for f in candidate_fnames): + return candidate_fnames + i += 1 + + +def get_classification_hyperparams(df): + n_classes = df.completion.nunique() + pos_class = None + if n_classes == 2: + pos_class = df.completion.value_counts().index[0] + return n_classes, pos_class + + +def write_out_file(df, fname, any_remediations, auto_accept): + """ + This function will write out a dataframe to a file, if the user would like to proceed, and also offer a fine-tuning command with the newly created file. + For classification it will optionally ask the user if they would like to split the data into train/valid files, and modify the suggested command to include the valid set. + """ + ft_format = infer_task_type(df) + common_prompt_suffix = get_common_xfix(df.prompt, xfix="suffix") + common_completion_suffix = get_common_xfix(df.completion, xfix="suffix") + + split = False + input_text = "- [Recommended] Would you like to split into training and validation set? [Y/n]: " + if ft_format == "classification": + if accept_suggestion(input_text, auto_accept): + split = True + + additional_params = "" + common_prompt_suffix_new_line_handled = common_prompt_suffix.replace("\n", "\\n") + common_completion_suffix_new_line_handled = common_completion_suffix.replace( + "\n", "\\n" + ) + optional_ending_string = ( + f' Make sure to include `stop=["{common_completion_suffix_new_line_handled}"]` so that the generated texts ends at the expected place.' + if len(common_completion_suffix_new_line_handled) > 0 + else "" + ) + + input_text = "\n\nYour data will be written to a new JSONL file. Proceed [Y/n]: " + + if not any_remediations and not split: + sys.stdout.write( + f'\nYou can use your file for fine-tuning:\n> openai api fine_tunes.create -t "{fname}"{additional_params}\n\nAfter you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `{common_prompt_suffix_new_line_handled}` for the model to start generating completions, rather than continuing with the prompt.{optional_ending_string}\n' + ) + estimate_fine_tuning_time(df) + + elif accept_suggestion(input_text, auto_accept): + fnames = get_outfnames(fname, split) + if split: + assert len(fnames) == 2 and "train" in fnames[0] and "valid" in fnames[1] + MAX_VALID_EXAMPLES = 1000 + n_train = max(len(df) - MAX_VALID_EXAMPLES, int(len(df) * 0.8)) + df_train = df.sample(n=n_train, random_state=42) + df_valid = df.drop(df_train.index) + df_train[["prompt", "completion"]].to_json( + fnames[0], lines=True, orient="records", force_ascii=False + ) + df_valid[["prompt", "completion"]].to_json( + fnames[1], lines=True, orient="records", force_ascii=False + ) + + n_classes, pos_class = get_classification_hyperparams(df) + additional_params += " --compute_classification_metrics" + if n_classes == 2: + additional_params += f' --classification_positive_class "{pos_class}"' + else: + additional_params += f" --classification_n_classes {n_classes}" + else: + assert len(fnames) == 1 + df[["prompt", "completion"]].to_json( + fnames[0], lines=True, orient="records", force_ascii=False + ) + + # Add -v VALID_FILE if we split the file into train / valid + files_string = ("s" if split else "") + " to `" + ("` and `".join(fnames)) + valid_string = f' -v "{fnames[1]}"' if split else "" + separator_reminder = ( + "" + if len(common_prompt_suffix_new_line_handled) == 0 + else f"After you’ve fine-tuned a model, remember that your prompt has to end with the indicator string `{common_prompt_suffix_new_line_handled}` for the model to start generating completions, rather than continuing with the prompt." + ) + sys.stdout.write( + f'\nWrote modified file{files_string}`\nFeel free to take a look!\n\nNow use that file when fine-tuning:\n> openai api fine_tunes.create -t "{fnames[0]}"{valid_string}{additional_params}\n\n{separator_reminder}{optional_ending_string}\n' + ) + estimate_fine_tuning_time(df) + else: + sys.stdout.write("Aborting... did not write the file\n") + + +def infer_task_type(df): + """ + Infer the likely fine-tuning task type from the data + """ + CLASSIFICATION_THRESHOLD = 3 # min_average instances of each class + if sum(df.prompt.str.len()) == 0: + return "open-ended generation" + + if len(df.completion.unique()) < len(df) / CLASSIFICATION_THRESHOLD: + return "classification" + + return "conditional generation" + + +def get_common_xfix(series, xfix="suffix"): + """ + Finds the longest common suffix or prefix of all the values in a series + """ + common_xfix = "" + while True: + common_xfixes = ( + series.str[-(len(common_xfix) + 1) :] + if xfix == "suffix" + else series.str[: len(common_xfix) + 1] + ) # first few or last few characters + if ( + common_xfixes.nunique() != 1 + ): # we found the character at which we don't have a unique xfix anymore + break + elif ( + common_xfix == common_xfixes.values[0] + ): # the entire first row is a prefix of every other row + break + else: # the first or last few characters are still common across all rows - let's try to add one more + common_xfix = common_xfixes.values[0] + return common_xfix + + +def get_validators(): + return [ + num_examples_validator, + lambda x: necessary_column_validator(x, "prompt"), + lambda x: necessary_column_validator(x, "completion"), + additional_column_validator, + non_empty_field_validator, + format_inferrer_validator, + duplicated_rows_validator, + long_examples_validator, + lambda x: lower_case_validator(x, "prompt"), + lambda x: lower_case_validator(x, "completion"), + common_prompt_suffix_validator, + common_prompt_prefix_validator, + common_completion_prefix_validator, + common_completion_suffix_validator, + completions_space_start_validator, + ] + + +def apply_validators( + df, + fname, + remediation, + validators, + auto_accept, + write_out_file_func, +): + optional_remediations = [] + if remediation is not None: + optional_remediations.append(remediation) + for validator in validators: + remediation = validator(df) + if remediation is not None: + optional_remediations.append(remediation) + df = apply_necessary_remediation(df, remediation) + + any_optional_or_necessary_remediations = any( + [ + remediation + for remediation in optional_remediations + if remediation.optional_msg is not None + or remediation.necessary_msg is not None + ] + ) + any_necessary_applied = any( + [ + remediation + for remediation in optional_remediations + if remediation.necessary_msg is not None + ] + ) + any_optional_applied = False + + if any_optional_or_necessary_remediations: + sys.stdout.write( + "\n\nBased on the analysis we will perform the following actions:\n" + ) + for remediation in optional_remediations: + df, optional_applied = apply_optional_remediation( + df, remediation, auto_accept + ) + any_optional_applied = any_optional_applied or optional_applied + else: + sys.stdout.write("\n\nNo remediations found.\n") + + any_optional_or_necessary_applied = any_optional_applied or any_necessary_applied + + write_out_file_func(df, fname, any_optional_or_necessary_applied, auto_accept) diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/version.py b/.pythonlibs/lib/python3.10/site-packages/openai/version.py new file mode 100644 index 0000000..f62542b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/version.py @@ -0,0 +1 @@ +VERSION = "0.28.0" diff --git a/.pythonlibs/lib/python3.10/site-packages/openai/wandb_logger.py b/.pythonlibs/lib/python3.10/site-packages/openai/wandb_logger.py new file mode 100644 index 0000000..fdd8c24 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/openai/wandb_logger.py @@ -0,0 +1,300 @@ +try: + import wandb + + WANDB_AVAILABLE = True +except: + WANDB_AVAILABLE = False + + +if WANDB_AVAILABLE: + import datetime + import io + import json + import re + from pathlib import Path + + from openai import File, FineTune + from openai.datalib.numpy_helper import numpy as np + from openai.datalib.pandas_helper import pandas as pd + + +class WandbLogger: + """ + Log fine-tunes to [Weights & Biases](https://wandb.me/openai-docs) + """ + + if not WANDB_AVAILABLE: + print("Logging requires wandb to be installed. Run `pip install wandb`.") + else: + _wandb_api = None + _logged_in = False + + @classmethod + def sync( + cls, + id=None, + n_fine_tunes=None, + project="GPT-3", + entity=None, + force=False, + **kwargs_wandb_init, + ): + """ + Sync fine-tunes to Weights & Biases. + :param id: The id of the fine-tune (optional) + :param n_fine_tunes: Number of most recent fine-tunes to log when an id is not provided. By default, every fine-tune is synced. + :param project: Name of the project where you're sending runs. By default, it is "GPT-3". + :param entity: Username or team name where you're sending runs. By default, your default entity is used, which is usually your username. + :param force: Forces logging and overwrite existing wandb run of the same fine-tune. + """ + + if not WANDB_AVAILABLE: + return + + if id: + fine_tune = FineTune.retrieve(id=id) + fine_tune.pop("events", None) + fine_tunes = [fine_tune] + + else: + # get list of fine_tune to log + fine_tunes = FineTune.list() + if not fine_tunes or fine_tunes.get("data") is None: + print("No fine-tune has been retrieved") + return + fine_tunes = fine_tunes["data"][ + -n_fine_tunes if n_fine_tunes is not None else None : + ] + + # log starting from oldest fine_tune + show_individual_warnings = ( + False if id is None and n_fine_tunes is None else True + ) + fine_tune_logged = [ + cls._log_fine_tune( + fine_tune, + project, + entity, + force, + show_individual_warnings, + **kwargs_wandb_init, + ) + for fine_tune in fine_tunes + ] + + if not show_individual_warnings and not any(fine_tune_logged): + print("No new successful fine-tunes were found") + + return "🎉 wandb sync completed successfully" + + @classmethod + def _log_fine_tune( + cls, + fine_tune, + project, + entity, + force, + show_individual_warnings, + **kwargs_wandb_init, + ): + fine_tune_id = fine_tune.get("id") + status = fine_tune.get("status") + + # check run completed successfully + if status != "succeeded": + if show_individual_warnings: + print( + f'Fine-tune {fine_tune_id} has the status "{status}" and will not be logged' + ) + return + + # check results are present + try: + results_id = fine_tune["result_files"][0]["id"] + results = File.download(id=results_id).decode("utf-8") + except: + if show_individual_warnings: + print(f"Fine-tune {fine_tune_id} has no results and will not be logged") + return + + # check run has not been logged already + run_path = f"{project}/{fine_tune_id}" + if entity is not None: + run_path = f"{entity}/{run_path}" + wandb_run = cls._get_wandb_run(run_path) + if wandb_run: + wandb_status = wandb_run.summary.get("status") + if show_individual_warnings: + if wandb_status == "succeeded": + print( + f"Fine-tune {fine_tune_id} has already been logged successfully at {wandb_run.url}" + ) + if not force: + print( + 'Use "--force" in the CLI or "force=True" in python if you want to overwrite previous run' + ) + else: + print( + f"A run for fine-tune {fine_tune_id} was previously created but didn't end successfully" + ) + if wandb_status != "succeeded" or force: + print( + f"A new wandb run will be created for fine-tune {fine_tune_id} and previous run will be overwritten" + ) + if wandb_status == "succeeded" and not force: + return + + # start a wandb run + wandb.init( + job_type="fine-tune", + config=cls._get_config(fine_tune), + project=project, + entity=entity, + name=fine_tune_id, + id=fine_tune_id, + **kwargs_wandb_init, + ) + + # log results + df_results = pd.read_csv(io.StringIO(results)) + for _, row in df_results.iterrows(): + metrics = {k: v for k, v in row.items() if not np.isnan(v)} + step = metrics.pop("step") + if step is not None: + step = int(step) + wandb.log(metrics, step=step) + fine_tuned_model = fine_tune.get("fine_tuned_model") + if fine_tuned_model is not None: + wandb.summary["fine_tuned_model"] = fine_tuned_model + + # training/validation files and fine-tune details + cls._log_artifacts(fine_tune, project, entity) + + # mark run as complete + wandb.summary["status"] = "succeeded" + + wandb.finish() + return True + + @classmethod + def _ensure_logged_in(cls): + if not cls._logged_in: + if wandb.login(): + cls._logged_in = True + else: + raise Exception("You need to log in to wandb") + + @classmethod + def _get_wandb_run(cls, run_path): + cls._ensure_logged_in() + try: + if cls._wandb_api is None: + cls._wandb_api = wandb.Api() + return cls._wandb_api.run(run_path) + except Exception: + return None + + @classmethod + def _get_wandb_artifact(cls, artifact_path): + cls._ensure_logged_in() + try: + if cls._wandb_api is None: + cls._wandb_api = wandb.Api() + return cls._wandb_api.artifact(artifact_path) + except Exception: + return None + + @classmethod + def _get_config(cls, fine_tune): + config = dict(fine_tune) + for key in ("training_files", "validation_files", "result_files"): + if config.get(key) and len(config[key]): + config[key] = config[key][0] + if config.get("created_at"): + config["created_at"] = datetime.datetime.fromtimestamp(config["created_at"]) + return config + + @classmethod + def _log_artifacts(cls, fine_tune, project, entity): + # training/validation files + training_file = ( + fine_tune["training_files"][0] + if fine_tune.get("training_files") and len(fine_tune["training_files"]) + else None + ) + validation_file = ( + fine_tune["validation_files"][0] + if fine_tune.get("validation_files") and len(fine_tune["validation_files"]) + else None + ) + for file, prefix, artifact_type in ( + (training_file, "train", "training_files"), + (validation_file, "valid", "validation_files"), + ): + if file is not None: + cls._log_artifact_inputs(file, prefix, artifact_type, project, entity) + + # fine-tune details + fine_tune_id = fine_tune.get("id") + artifact = wandb.Artifact( + "fine_tune_details", + type="fine_tune_details", + metadata=fine_tune, + ) + with artifact.new_file( + "fine_tune_details.json", mode="w", encoding="utf-8" + ) as f: + json.dump(fine_tune, f, indent=2) + wandb.run.log_artifact( + artifact, + aliases=["latest", fine_tune_id], + ) + + @classmethod + def _log_artifact_inputs(cls, file, prefix, artifact_type, project, entity): + file_id = file["id"] + filename = Path(file["filename"]).name + stem = Path(file["filename"]).stem + + # get input artifact + artifact_name = f"{prefix}-{filename}" + # sanitize name to valid wandb artifact name + artifact_name = re.sub(r"[^a-zA-Z0-9_\-.]", "_", artifact_name) + artifact_alias = file_id + artifact_path = f"{project}/{artifact_name}:{artifact_alias}" + if entity is not None: + artifact_path = f"{entity}/{artifact_path}" + artifact = cls._get_wandb_artifact(artifact_path) + + # create artifact if file not already logged previously + if artifact is None: + # get file content + try: + file_content = File.download(id=file_id).decode("utf-8") + except: + print( + f"File {file_id} could not be retrieved. Make sure you are allowed to download training/validation files" + ) + return + artifact = wandb.Artifact(artifact_name, type=artifact_type, metadata=file) + with artifact.new_file(filename, mode="w", encoding="utf-8") as f: + f.write(file_content) + + # create a Table + try: + table, n_items = cls._make_table(file_content) + artifact.add(table, stem) + wandb.config.update({f"n_{prefix}": n_items}) + artifact.metadata["items"] = n_items + except: + print(f"File {file_id} could not be read as a valid JSON file") + else: + # log number of items + wandb.config.update({f"n_{prefix}": artifact.metadata.get("items")}) + + wandb.run.use_artifact(artifact, aliases=["latest", artifact_alias]) + + @classmethod + def _make_table(cls, file_content): + df = pd.read_json(io.StringIO(file_content), orient="records", lines=True) + return wandb.Table(dataframe=df), len(df) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/METADATA new file mode 100644 index 0000000..b9daf19 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/METADATA @@ -0,0 +1,1077 @@ +Metadata-Version: 2.3 +Name: pydantic +Version: 2.7.1 +Summary: Data validation using Python type hints +Project-URL: Homepage, https://github.com/pydantic/pydantic +Project-URL: Documentation, https://docs.pydantic.dev +Project-URL: Funding, https://github.com/sponsors/samuelcolvin +Project-URL: Source, https://github.com/pydantic/pydantic +Project-URL: Changelog, https://docs.pydantic.dev/latest/changelog/ +Author-email: Samuel Colvin , Eric Jolibois , Hasan Ramezani , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Terrence Dorsey , David Montague , Serge Matveenko , Marcelo Trylesinski , Sydney Runkle , David Hewitt , Alex Hall +License-Expression: MIT +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Framework :: Hypothesis +Classifier: Framework :: Pydantic +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.8 +Requires-Dist: annotated-types>=0.4.0 +Requires-Dist: pydantic-core==2.18.2 +Requires-Dist: typing-extensions>=4.6.1 +Provides-Extra: email +Requires-Dist: email-validator>=2.0.0; extra == 'email' +Description-Content-Type: text/markdown + +# Pydantic + +[![CI](https://img.shields.io/github/actions/workflow/status/pydantic/pydantic/ci.yml?branch=main&logo=github&label=CI)](https://github.com/pydantic/pydantic/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) +[![Coverage](https://coverage-badge.samuelcolvin.workers.dev/pydantic/pydantic.svg)](https://coverage-badge.samuelcolvin.workers.dev/redirect/pydantic/pydantic) +[![pypi](https://img.shields.io/pypi/v/pydantic.svg)](https://pypi.python.org/pypi/pydantic) +[![CondaForge](https://img.shields.io/conda/v/conda-forge/pydantic.svg)](https://anaconda.org/conda-forge/pydantic) +[![downloads](https://static.pepy.tech/badge/pydantic/month)](https://pepy.tech/project/pydantic) +[![versions](https://img.shields.io/pypi/pyversions/pydantic.svg)](https://github.com/pydantic/pydantic) +[![license](https://img.shields.io/github/license/pydantic/pydantic.svg)](https://github.com/pydantic/pydantic/blob/main/LICENSE) +[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/main/docs/badge/v2.json)](https://docs.pydantic.dev/latest/contributing/#badges) + +Data validation using Python type hints. + +Fast and extensible, Pydantic plays nicely with your linters/IDE/brain. +Define how data should be in pure, canonical Python 3.8+; validate it with Pydantic. + +## Pydantic Company :rocket: + +We've started a company based on the principles that I believe have led to Pydantic's success. +Learn more from the [Company Announcement](https://blog.pydantic.dev/blog/2023/02/16/company-announcement--pydantic/). + +## Pydantic V1.10 vs. V2 + +Pydantic V2 is a ground-up rewrite that offers many new features, performance improvements, and some breaking changes compared to Pydantic V1. + +If you're using Pydantic V1 you may want to look at the +[pydantic V1.10 Documentation](https://docs.pydantic.dev/) or, +[`1.10.X-fixes` git branch](https://github.com/pydantic/pydantic/tree/1.10.X-fixes). Pydantic V2 also ships with the latest version of Pydantic V1 built in so that you can incrementally upgrade your code base and projects: `from pydantic import v1 as pydantic_v1`. + +## Help + +See [documentation](https://docs.pydantic.dev/) for more details. + +## Installation + +Install using `pip install -U pydantic` or `conda install pydantic -c conda-forge`. +For more installation options to make Pydantic even faster, +see the [Install](https://docs.pydantic.dev/install/) section in the documentation. + +## A Simple Example + +```py +from datetime import datetime +from typing import List, Optional +from pydantic import BaseModel + +class User(BaseModel): + id: int + name: str = 'John Doe' + signup_ts: Optional[datetime] = None + friends: List[int] = [] + +external_data = {'id': '123', 'signup_ts': '2017-06-01 12:22', 'friends': [1, '2', b'3']} +user = User(**external_data) +print(user) +#> User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3] +print(user.id) +#> 123 +``` + +## Contributing + +For guidance on setting up a development environment and how to make a +contribution to Pydantic, see +[Contributing to Pydantic](https://docs.pydantic.dev/contributing/). + +## Reporting a Security Vulnerability + +See our [security policy](https://github.com/pydantic/pydantic/security/policy). + +## Changelog + +## v2.7.1 (2024-04-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.1) + +### What's Changed + +#### Packaging + +* Bump `pydantic-core` to `v2.18.2` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9307](https://github.com/pydantic/pydantic/pull/9307) + +#### New Features + +* Ftp and Websocket connection strings support by [@CherrySuryp](https://github.com/CherrySuryp) in [#9205](https://github.com/pydantic/pydantic/pull/9205) + +#### Changes + +* Use field description for RootModel schema description when there is `…` by [@LouisGobert](https://github.com/LouisGobert) in [#9214](https://github.com/pydantic/pydantic/pull/9214) + +#### Fixes + +* Fix `validation_alias` behavior with `model_construct` for `AliasChoices` and `AliasPath` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9223](https://github.com/pydantic/pydantic/pull/9223) +* Revert `typing.Literal` and import it outside the TYPE_CHECKING block by [@frost-nzcr4](https://github.com/frost-nzcr4) in [#9232](https://github.com/pydantic/pydantic/pull/9232) +* Fix `Secret` serialization schema, applicable for unions by [@sydney-runkle](https://github.com/sydney-runkle) in [#9240](https://github.com/pydantic/pydantic/pull/9240) +* Fix `strict` application to `function-after` with `use_enum_values` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9279](https://github.com/pydantic/pydantic/pull/9279) +* Address case where `model_construct` on a class which defines `model_post_init` fails with `AttributeError` by [@babygrimes](https://github.com/babygrimes) in [#9168](https://github.com/pydantic/pydantic/pull/9168) +* Fix `model_json_schema` with config types by [@NeevCohen](https://github.com/NeevCohen) in [#9287](https://github.com/pydantic/pydantic/pull/9287) +* Support multiple zeros as an `int` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1269](https://github.com/pydantic/pydantic-core/pull/1269) +* Fix validation of `int`s with leading unary plus by [@cknv](https://github.com/cknv) in [pydantic/pydantic-core#1272](https://github.com/pydantic/pydantic-core/pull/1272) +* Fix interaction between `extra != 'ignore'` and `from_attributes=True` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1276](https://github.com/pydantic/pydantic-core/pull/1276) +* Handle error from `Enum`'s `missing` function as `ValidationError` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1274](https://github.com/pydantic/pydantic-core/pull/1754) +* Fix memory leak with `Iterable` validation by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1271](https://github.com/pydantic/pydantic-core/pull/1751) + +### New Contributors + +* [@zzstoatzz](https://github.com/zzstoatzz) made their first contribution in [#9219](https://github.com/pydantic/pydantic/pull/9219) +* [@frost-nzcr4](https://github.com/frost-nzcr4) made their first contribution in [#9232](https://github.com/pydantic/pydantic/pull/9232) +* [@CherrySuryp](https://github.com/CherrySuryp) made their first contribution in [#9205](https://github.com/pydantic/pydantic/pull/9205) +* [@vagenas](https://github.com/vagenas) made their first contribution in [#9268](https://github.com/pydantic/pydantic/pull/9268) +* [@ollz272](https://github.com/ollz272) made their first contribution in [#9262](https://github.com/pydantic/pydantic/pull/9262) +* [@babygrimes](https://github.com/babygrimes) made their first contribution in [#9168](https://github.com/pydantic/pydantic/pull/9168) +* [@swelborn](https://github.com/swelborn) made their first contribution in [#9296](https://github.com/pydantic/pydantic/pull/9296) +* [@kf-novi](https://github.com/kf-novi) made their first contribution in [#9236](https://github.com/pydantic/pydantic/pull/9236) +* [@lgeiger](https://github.com/lgeiger) made their first contribution in [#9288](https://github.com/pydantic/pydantic/pull/9288) + +## v2.7.0 (2024-04-11) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.0) + +The code released in v2.7.0 is practically identical to that of v2.7.0b1. + +### What's Changed + +#### Packaging + +* Reorganize `pyproject.toml` sections by [@Viicos](https://github.com/Viicos) in [#8899](https://github.com/pydantic/pydantic/pull/8899) +* Bump `pydantic-core` to `v2.18.1` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9211](https://github.com/pydantic/pydantic/pull/9211) +* Adopt `jiter` `v0.2.0` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1250](https://github.com/pydantic/pydantic-core/pull/1250) + +#### New Features + +* Extract attribute docstrings from `FieldInfo.description` by [@Viicos](https://github.com/Viicos) in [#6563](https://github.com/pydantic/pydantic/pull/6563) +* Add a `with_config` decorator to comply with typing spec by [@Viicos](https://github.com/Viicos) in [#8611](https://github.com/pydantic/pydantic/pull/8611) +* Allow an optional separator splitting the value and unit of the result of `ByteSize.human_readable` by [@jks15satoshi](https://github.com/jks15satoshi) in [#8706](https://github.com/pydantic/pydantic/pull/8706) +* Add generic `Secret` base type by [@conradogarciaberrotaran](https://github.com/conradogarciaberrotaran) in [#8519](https://github.com/pydantic/pydantic/pull/8519) +* Make use of `Sphinx` inventories for cross references in docs by [@Viicos](https://github.com/Viicos) in [#8682](https://github.com/pydantic/pydantic/pull/8682) +* Add environment variable to disable plugins by [@geospackle](https://github.com/geospackle) in [#8767](https://github.com/pydantic/pydantic/pull/8767) +* Add support for `deprecated` fields by [@Viicos](https://github.com/Viicos) in [#8237](https://github.com/pydantic/pydantic/pull/8237) +* Allow `field_serializer('*')` by [@ornariece](https://github.com/ornariece) in [#9001](https://github.com/pydantic/pydantic/pull/9001) +* Handle a case when `model_config` is defined as a model property by [@alexeyt101](https://github.com/alexeyt101) in [#9004](https://github.com/pydantic/pydantic/pull/9004) +* Update `create_model()` to support `typing.Annotated` as input by [@wannieman98](https://github.com/wannieman98) in [#8947](https://github.com/pydantic/pydantic/pull/8947) +* Add `ClickhouseDsn` support by [@solidguy7](https://github.com/solidguy7) in [#9062](https://github.com/pydantic/pydantic/pull/9062) +* Add support for `re.Pattern[str]` to `pattern` field by [@jag-k](https://github.com/jag-k) in [#9053](https://github.com/pydantic/pydantic/pull/9053) +* Support for `serialize_as_any` runtime setting by [@sydney-runkle](https://github.com/sydney-runkle) in [#8830](https://github.com/pydantic/pydantic/pull/8830) +* Add support for `typing.Self` by [@Youssefares](https://github.com/Youssefares) in [#9023](https://github.com/pydantic/pydantic/pull/9023) +* Ability to pass `context` to serialization by [@ornariece](https://github.com/ornariece) in [#8965](https://github.com/pydantic/pydantic/pull/8965) +* Add feedback widget to docs with flarelytics integration by [@sydney-runkle](https://github.com/sydney-runkle) in [#9129](https://github.com/pydantic/pydantic/pull/9129) +* Support for parsing partial JSON strings in Python by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/jiter#66](https://github.com/pydantic/jiter/pull/66) + +**Finalized in v2.7.0, rather than v2.7.0b1:** +* Add support for field level number to str coercion option by [@NeevCohen](https://github.com/NeevCohen) in [#9137](https://github.com/pydantic/pydantic/pull/9137) +* Update `warnings` parameter for serialization utilities to allow raising a warning by [@Lance-Drane](https://github.com/Lance-Drane) in [#9166](https://github.com/pydantic/pydantic/pull/9166) + +#### Changes + +* Correct docs, logic for `model_construct` behavior with `extra` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8807](https://github.com/pydantic/pydantic/pull/8807) +* Improve error message for improper `RootModel` subclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8857](https://github.com/pydantic/pydantic/pull/8857) +* Use `PEP570` syntax by [@Viicos](https://github.com/Viicos) in [#8940](https://github.com/pydantic/pydantic/pull/8940) +* Add `enum` and `type` to the JSON schema for single item literals by [@dmontagu](https://github.com/dmontagu) in [#8944](https://github.com/pydantic/pydantic/pull/8944) +* Deprecate `update_json_schema` internal function by [@sydney-runkle](https://github.com/sydney-runkle) in [#9125](https://github.com/pydantic/pydantic/pull/9125) +* Serialize duration to hour minute second, instead of just seconds by [@kakilangit](https://github.com/kakilangit) in [pydantic/speedate#50](https://github.com/pydantic/speedate/pull/50) +* Trimming str before parsing to int and float by [@hungtsetse](https://github.com/hungtsetse) in [pydantic/pydantic-core#1203](https://github.com/pydantic/pydantic-core/pull/1203) + +#### Performance + +* `enum` validator improvements by [@samuelcolvin](https://github.com/samuelcolvin) in [#9045](https://github.com/pydantic/pydantic/pull/9045) +* Move `enum` validation and serialization to Rust by [@samuelcolvin](https://github.com/samuelcolvin) in [#9064](https://github.com/pydantic/pydantic/pull/9064) +* Improve schema generation for nested dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#9114](https://github.com/pydantic/pydantic/pull/9114) +* Fast path for ASCII python string creation in JSON by [@samuelcolvin](https://github.com/samuelcolvin) in in [pydantic/jiter#72](https://github.com/pydantic/jiter/pull/72) +* SIMD integer and string JSON parsing on `aarch64`(**Note:** SIMD on x86 will be implemented in a future release) by [@samuelcolvin](https://github.com/samuelcolvin) in in [pydantic/jiter#65](https://github.com/pydantic/jiter/pull/65) +* Support JSON `Cow` from `jiter` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1231](https://github.com/pydantic/pydantic-core/pull/1231) +* MAJOR performance improvement: update to PyO3 0.21 final by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1248](https://github.com/pydantic/pydantic-core/pull/1248) +* cache Python strings by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1240](https://github.com/pydantic/pydantic-core/pull/1240) + +#### Fixes + +* Fix strict parsing for some `Sequence`s by [@sydney-runkle](https://github.com/sydney-runkle) in [#8614](https://github.com/pydantic/pydantic/pull/8614) +* Add a check on the existence of `__qualname__` by [@anci3ntr0ck](https://github.com/anci3ntr0ck) in [#8642](https://github.com/pydantic/pydantic/pull/8642) +* Handle `__pydantic_extra__` annotation being a string or inherited by [@alexmojaki](https://github.com/alexmojaki) in [#8659](https://github.com/pydantic/pydantic/pull/8659) +* Fix json validation for `NameEmail` by [@Holi0317](https://github.com/Holi0317) in [#8650](https://github.com/pydantic/pydantic/pull/8650) +* Fix type-safety of attribute access in `BaseModel` by [@bluenote10](https://github.com/bluenote10) in [#8651](https://github.com/pydantic/pydantic/pull/8651) +* Fix bug with `mypy` plugin and `no_strict_optional = True` by [@dmontagu](https://github.com/dmontagu) in [#8666](https://github.com/pydantic/pydantic/pull/8666) +* Fix `ByteSize` error `type` change by [@sydney-runkle](https://github.com/sydney-runkle) in [#8681](https://github.com/pydantic/pydantic/pull/8681) +* Fix inheriting annotations in dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8679](https://github.com/pydantic/pydantic/pull/8679) +* Fix regression in core schema generation for indirect definition references by [@dmontagu](https://github.com/dmontagu) in [#8702](https://github.com/pydantic/pydantic/pull/8702) +* Fix unsupported types bug with plain validator by [@sydney-runkle](https://github.com/sydney-runkle) in [#8710](https://github.com/pydantic/pydantic/pull/8710) +* Reverting problematic fix from 2.6 release, fixing schema building bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8718](https://github.com/pydantic/pydantic/pull/8718) +* fixes `__pydantic_config__` ignored for TypeDict by [@13sin](https://github.com/13sin) in [#8734](https://github.com/pydantic/pydantic/pull/8734) +* Fix test failures with `pytest v8.0.0` due to `pytest.warns()` starting to work inside `pytest.raises()` by [@mgorny](https://github.com/mgorny) in [#8678](https://github.com/pydantic/pydantic/pull/8678) +* Use `is_valid_field` from 1.x for `mypy` plugin by [@DanielNoord](https://github.com/DanielNoord) in [#8738](https://github.com/pydantic/pydantic/pull/8738) +* Better-support `mypy` strict equality flag by [@dmontagu](https://github.com/dmontagu) in [#8799](https://github.com/pydantic/pydantic/pull/8799) +* model_json_schema export with Annotated types misses 'required' parameters by [@LouisGobert](https://github.com/LouisGobert) in [#8793](https://github.com/pydantic/pydantic/pull/8793) +* Fix default inclusion in `FieldInfo.__repr_args__` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8801](https://github.com/pydantic/pydantic/pull/8801) +* Fix resolution of forward refs in dataclass base classes that are not present in the subclass module namespace by [@matsjoyce-refeyn](https://github.com/matsjoyce-refeyn) in [#8751](https://github.com/pydantic/pydantic/pull/8751) +* Fix `BaseModel` type annotations to be resolvable by `typing.get_type_hints` by [@devmonkey22](https://github.com/devmonkey22) in [#7680](https://github.com/pydantic/pydantic/pull/7680) +* Fix: allow empty string aliases with `AliasGenerator` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8810](https://github.com/pydantic/pydantic/pull/8810) +* Fix test along with `date` -> `datetime` timezone assumption fix by [@sydney-runkle](https://github.com/sydney-runkle) in [#8823](https://github.com/pydantic/pydantic/pull/8823) +* Fix deprecation warning with usage of `ast.Str` by [@Viicos](https://github.com/Viicos) in [#8837](https://github.com/pydantic/pydantic/pull/8837) +* Add missing `deprecated` decorators by [@Viicos](https://github.com/Viicos) in [#8877](https://github.com/pydantic/pydantic/pull/8877) +* Fix serialization of `NameEmail` if name includes an email address by [@NeevCohen](https://github.com/NeevCohen) in [#8860](https://github.com/pydantic/pydantic/pull/8860) +* Add information about class in error message of schema generation by [@Czaki](https://github.com/Czaki) in [#8917](https://github.com/pydantic/pydantic/pull/8917) +* Make `TypeAdapter`'s typing compatible with special forms by [@adriangb](https://github.com/adriangb) in [#8923](https://github.com/pydantic/pydantic/pull/8923) +* Fix issue with config behavior being baked into the ref schema for `enum`s by [@dmontagu](https://github.com/dmontagu) in [#8920](https://github.com/pydantic/pydantic/pull/8920) +* More helpful error re wrong `model_json_schema` usage by [@sydney-runkle](https://github.com/sydney-runkle) in [#8928](https://github.com/pydantic/pydantic/pull/8928) +* Fix nested discriminated union schema gen, pt 2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8932](https://github.com/pydantic/pydantic/pull/8932) +* Fix schema build for nested dataclasses / TypedDicts with discriminators by [@sydney-runkle](https://github.com/sydney-runkle) in [#8950](https://github.com/pydantic/pydantic/pull/8950) +* Remove unnecessary logic for definitions schema gen with discriminated unions by [@sydney-runkle](https://github.com/sydney-runkle) in [#8951](https://github.com/pydantic/pydantic/pull/8951) +* Fix handling of optionals in `mypy` plugin by [@dmontagu](https://github.com/dmontagu) in [#9008](https://github.com/pydantic/pydantic/pull/9008) +* Fix `PlainSerializer` usage with std type constructor by [@sydney-runkle](https://github.com/sydney-runkle) in [#9031](https://github.com/pydantic/pydantic/pull/9031) +* Remove unnecessary warning for config in plugin by [@dmontagu](https://github.com/dmontagu) in [#9039](https://github.com/pydantic/pydantic/pull/9039) +* Fix default value serializing by [@NeevCohen](https://github.com/NeevCohen) in [#9066](https://github.com/pydantic/pydantic/pull/9066) +* Fix extra fields check in `Model.__getattr__()` by [@NeevCohen](https://github.com/NeevCohen) in [#9082](https://github.com/pydantic/pydantic/pull/9082) +* Fix `ClassVar` forward ref inherited from parent class by [@alexmojaki](https://github.com/alexmojaki) in [#9097](https://github.com/pydantic/pydantic/pull/9097) +* fix sequence like validator with strict `True` by [@andresliszt](https://github.com/andresliszt) in [#8977](https://github.com/pydantic/pydantic/pull/8977) +* Improve warning message when a field name shadows a field in a parent model by [@chan-vince](https://github.com/chan-vince) in [#9105](https://github.com/pydantic/pydantic/pull/9105) +* Do not warn about shadowed fields if they are not redefined in a child class by [@chan-vince](https://github.com/chan-vince) in [#9111](https://github.com/pydantic/pydantic/pull/9111) +* Fix discriminated union bug with unsubstituted type var by [@sydney-runkle](https://github.com/sydney-runkle) in [#9124](https://github.com/pydantic/pydantic/pull/9124) +* Support serialization of `deque` when passed to `Sequence[blah blah blah]` by [@sydney-runkle](https://github.com/sydney-runkle) in [#9128](https://github.com/pydantic/pydantic/pull/9128) +* Init private attributes from super-types in `model_post_init` by [@Viicos](https://github.com/Viicos) in [#9134](https://github.com/pydantic/pydantic/pull/9134) +* fix `model_construct` with `validation_alias` by [@ornariece](https://github.com/ornariece) in [#9144](https://github.com/pydantic/pydantic/pull/9144) +* Ensure json-schema generator handles `Literal` `null` types by [@bruno-f-cruz](https://github.com/bruno-f-cruz) in [#9135](https://github.com/pydantic/pydantic/pull/9135) +* **Fixed in v2.7.0**: Fix allow extra generic by [@dmontagu](https://github.com/dmontagu) in [#9193](https://github.com/pydantic/pydantic/pull/9193) + +### New Contributors + +* [@hungtsetse](https://github.com/hungtsetse) made their first contribution in [#8546](https://github.com/pydantic/pydantic/pull/8546) +* [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) made their first contribution in [#8583](https://github.com/pydantic/pydantic/pull/8583) +* [@anci3ntr0ck](https://github.com/anci3ntr0ck) made their first contribution in [#8642](https://github.com/pydantic/pydantic/pull/8642) +* [@Holi0317](https://github.com/Holi0317) made their first contribution in [#8650](https://github.com/pydantic/pydantic/pull/8650) +* [@bluenote10](https://github.com/bluenote10) made their first contribution in [#8651](https://github.com/pydantic/pydantic/pull/8651) +* [@ADSteele916](https://github.com/ADSteele916) made their first contribution in [#8703](https://github.com/pydantic/pydantic/pull/8703) +* [@musicinmybrain](https://github.com/musicinmybrain) made their first contribution in [#8731](https://github.com/pydantic/pydantic/pull/8731) +* [@jks15satoshi](https://github.com/jks15satoshi) made their first contribution in [#8706](https://github.com/pydantic/pydantic/pull/8706) +* [@13sin](https://github.com/13sin) made their first contribution in [#8734](https://github.com/pydantic/pydantic/pull/8734) +* [@DanielNoord](https://github.com/DanielNoord) made their first contribution in [#8738](https://github.com/pydantic/pydantic/pull/8738) +* [@conradogarciaberrotaran](https://github.com/conradogarciaberrotaran) made their first contribution in [#8519](https://github.com/pydantic/pydantic/pull/8519) +* [@chris-griffin](https://github.com/chris-griffin) made their first contribution in [#8775](https://github.com/pydantic/pydantic/pull/8775) +* [@LouisGobert](https://github.com/LouisGobert) made their first contribution in [#8793](https://github.com/pydantic/pydantic/pull/8793) +* [@matsjoyce-refeyn](https://github.com/matsjoyce-refeyn) made their first contribution in [#8751](https://github.com/pydantic/pydantic/pull/8751) +* [@devmonkey22](https://github.com/devmonkey22) made their first contribution in [#7680](https://github.com/pydantic/pydantic/pull/7680) +* [@adamency](https://github.com/adamency) made their first contribution in [#8847](https://github.com/pydantic/pydantic/pull/8847) +* [@MamfTheKramf](https://github.com/MamfTheKramf) made their first contribution in [#8851](https://github.com/pydantic/pydantic/pull/8851) +* [@ornariece](https://github.com/ornariece) made their first contribution in [#9001](https://github.com/pydantic/pydantic/pull/9001) +* [@alexeyt101](https://github.com/alexeyt101) made their first contribution in [#9004](https://github.com/pydantic/pydantic/pull/9004) +* [@wannieman98](https://github.com/wannieman98) made their first contribution in [#8947](https://github.com/pydantic/pydantic/pull/8947) +* [@solidguy7](https://github.com/solidguy7) made their first contribution in [#9062](https://github.com/pydantic/pydantic/pull/9062) +* [@kloczek](https://github.com/kloczek) made their first contribution in [#9047](https://github.com/pydantic/pydantic/pull/9047) +* [@jag-k](https://github.com/jag-k) made their first contribution in [#9053](https://github.com/pydantic/pydantic/pull/9053) +* [@priya-gitTest](https://github.com/priya-gitTest) made their first contribution in [#9088](https://github.com/pydantic/pydantic/pull/9088) +* [@Youssefares](https://github.com/Youssefares) made their first contribution in [#9023](https://github.com/pydantic/pydantic/pull/9023) +* [@chan-vince](https://github.com/chan-vince) made their first contribution in [#9105](https://github.com/pydantic/pydantic/pull/9105) +* [@bruno-f-cruz](https://github.com/bruno-f-cruz) made their first contribution in [#9135](https://github.com/pydantic/pydantic/pull/9135) +* [@Lance-Drane](https://github.com/Lance-Drane) made their first contribution in [#9166](https://github.com/pydantic/pydantic/pull/9166) + +## v2.7.0b1 (2024-04-03) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.7.0b1) for details. + +## v2.6.4 (2024-03-12) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.4) + +### What's Changed + +#### Fixes + +* Fix usage of `AliasGenerator` with `computed_field` decorator by [@sydney-runkle](https://github.com/sydney-runkle) in [#8806](https://github.com/pydantic/pydantic/pull/8806) +* Fix nested discriminated union schema gen, pt 2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8932](https://github.com/pydantic/pydantic/pull/8932) +* Fix bug with no_strict_optional=True caused by API deferral by [@dmontagu](https://github.com/dmontagu) in [#8826](https://github.com/pydantic/pydantic/pull/8826) + + +## v2.6.3 (2024-02-27) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.3) + +### What's Changed + +#### Packaging + +* Update `pydantic-settings` version in the docs by [@hramezani](https://github.com/hramezani) in [#8906](https://github.com/pydantic/pydantic/pull/8906) + +#### Fixes + +* Fix discriminated union schema gen bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8904](https://github.com/pydantic/pydantic/pull/8904) + + +## v2.6.2 (2024-02-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.2) + +### What's Changed + +#### Packaging + +* Upgrade to `pydantic-core` 2.16.3 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8879](https://github.com/pydantic/pydantic/pull/8879) + +#### Fixes + +* 'YYYY-MM-DD' date string coerced to datetime shouldn't infer timezone by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1193](https://github.com/pydantic/pydantic-core/pull/1193) + + +## v2.6.1 (2024-02-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.1) + +### What's Changed + +#### Packaging + +* Upgrade to `pydantic-core` 2.16.2 by [@sydney-runkle](https://github.com/sydney-runkle) in [#8717](https://github.com/pydantic/pydantic/pull/8717) + +#### Fixes + +* Fix bug with `mypy` plugin and `no_strict_optional = True` by [@dmontagu](https://github.com/dmontagu) in [#8666](https://github.com/pydantic/pydantic/pull/8666) +* Fix `ByteSize` error `type` change by [@sydney-runkle](https://github.com/sydney-runkle) in [#8681](https://github.com/pydantic/pydantic/pull/8681) +* Fix inheriting `Field` annotations in dataclasses by [@sydney-runkle](https://github.com/sydney-runkle) in [#8679](https://github.com/pydantic/pydantic/pull/8679) +* Fix regression in core schema generation for indirect definition references by [@dmontagu](https://github.com/dmontagu) in [#8702](https://github.com/pydantic/pydantic/pull/8702) +* Fix unsupported types bug with `PlainValidator` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8710](https://github.com/pydantic/pydantic/pull/8710) +* Reverting problematic fix from 2.6 release, fixing schema building bug by [@sydney-runkle](https://github.com/sydney-runkle) in [#8718](https://github.com/pydantic/pydantic/pull/8718) +* Fix warning for tuple of wrong size in `Union` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1174](https://github.com/pydantic/pydantic-core/pull/1174) +* Fix `computed_field` JSON serializer `exclude_none` behavior by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1187](https://github.com/pydantic/pydantic-core/pull/1187) + + +## v2.6.0 (2024-01-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.0) + +The code released in v2.6.0 is practically identical to that of v2.6.0b1. + +### What's Changed + +#### Packaging + +* Check for `email-validator` version >= 2.0 by [@commonism](https://github.com/commonism) in [#6033](https://github.com/pydantic/pydantic/pull/6033) +* Upgrade `ruff`` target version to Python 3.8 by [@Elkiwa](https://github.com/Elkiwa) in [#8341](https://github.com/pydantic/pydantic/pull/8341) +* Update to `pydantic-extra-types==2.4.1` by [@yezz123](https://github.com/yezz123) in [#8478](https://github.com/pydantic/pydantic/pull/8478) +* Update to `pyright==1.1.345` by [@Viicos](https://github.com/Viicos) in [#8453](https://github.com/pydantic/pydantic/pull/8453) +* Update pydantic-core from 2.14.6 to 2.16.1, significant changes from these updates are described below, full changelog [here](https://github.com/pydantic/pydantic-core/compare/v2.14.6...v2.16.1) + +#### New Features + +* Add `NatsDsn` by [@ekeew](https://github.com/ekeew) in [#6874](https://github.com/pydantic/pydantic/pull/6874) +* Add `ConfigDict.ser_json_inf_nan` by [@davidhewitt](https://github.com/davidhewitt) in [#8159](https://github.com/pydantic/pydantic/pull/8159) +* Add `types.OnErrorOmit` by [@adriangb](https://github.com/adriangb) in [#8222](https://github.com/pydantic/pydantic/pull/8222) +* Support `AliasGenerator` usage by [@sydney-runkle](https://github.com/sydney-runkle) in [#8282](https://github.com/pydantic/pydantic/pull/8282) +* Add Pydantic People Page to docs by [@sydney-runkle](https://github.com/sydney-runkle) in [#8345](https://github.com/pydantic/pydantic/pull/8345) +* Support `yyyy-MM-DD` datetime parsing by [@sydney-runkle](https://github.com/sydney-runkle) in [#8404](https://github.com/pydantic/pydantic/pull/8404) +* Added bits conversions to the `ByteSize` class [#8415](https://github.com/pydantic/pydantic/issues/8415) by [@luca-matei](https://github.com/luca-matei) in [#8507](https://github.com/pydantic/pydantic/pull/8507) +* Enable json schema creation with type `ByteSize` by [@geospackle](https://github.com/geospackle) in [#8537](https://github.com/pydantic/pydantic/pull/8537) +* Add `eval_type_backport` to handle union operator and builtin generic subscripting in older Pythons by [@alexmojaki](https://github.com/alexmojaki) in [#8209](https://github.com/pydantic/pydantic/pull/8209) +* Add support for `dataclass` fields `init` by [@dmontagu](https://github.com/dmontagu) in [#8552](https://github.com/pydantic/pydantic/pull/8552) +* Implement pickling for `ValidationError` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1119](https://github.com/pydantic/pydantic-core/pull/1119) +* Add unified tuple validator that can handle "variadic" tuples via PEP-646 by [@dmontagu](https://github.com/dmontagu) in [pydantic/pydantic-core#865](https://github.com/pydantic/pydantic-core/pull/865) + +#### Changes + +* Drop Python3.7 support by [@hramezani](https://github.com/hramezani) in [#7188](https://github.com/pydantic/pydantic/pull/7188) +* Drop Python 3.7, and PyPy 3.7 and 3.8 by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1129](https://github.com/pydantic/pydantic-core/pull/1129) +* Use positional-only `self` in `BaseModel` constructor, so no field name can ever conflict with it by [@ariebovenberg](https://github.com/ariebovenberg) in [#8072](https://github.com/pydantic/pydantic/pull/8072) +* Make `@validate_call` return a function instead of a custom descriptor - fixes binding issue with inheritance and adds `self/cls` argument to validation errors by [@alexmojaki](https://github.com/alexmojaki) in [#8268](https://github.com/pydantic/pydantic/pull/8268) +* Exclude `BaseModel` docstring from JSON schema description by [@sydney-runkle](https://github.com/sydney-runkle) in [#8352](https://github.com/pydantic/pydantic/pull/8352) +* Introducing `classproperty` decorator for `model_computed_fields` by [@Jocelyn-Gas](https://github.com/Jocelyn-Gas) in [#8437](https://github.com/pydantic/pydantic/pull/8437) +* Explicitly raise an error if field names clashes with types by [@Viicos](https://github.com/Viicos) in [#8243](https://github.com/pydantic/pydantic/pull/8243) +* Use stricter serializer for unions of simple types by [@alexdrydew](https://github.com/alexdrydew) [pydantic/pydantic-core#1132](https://github.com/pydantic/pydantic-core/pull/1132) + +#### Performance + +* Add Codspeed profiling Actions workflow by [@lambertsbennett](https://github.com/lambertsbennett) in [#8054](https://github.com/pydantic/pydantic/pull/8054) +* Improve `int` extraction by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1155](https://github.com/pydantic/pydantic-core/pull/1155) +* Improve performance of recursion guard by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1156](https://github.com/pydantic/pydantic-core/pull/1156) +* `dataclass` serialization speedups by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1162](https://github.com/pydantic/pydantic-core/pull/1162) +* Avoid `HashMap` creation when looking up small JSON objects in `LazyIndexMaps` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/jiter#55](https://github.com/pydantic/jiter/pull/55) +* use hashbrown to speedup python string caching by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#51](https://github.com/pydantic/jiter/pull/51) +* Replace `Peak` with more efficient `Peek` by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/jiter#48](https://github.com/pydantic/jiter/pull/48) + +#### Fixes + +* Move `getattr` warning in deprecated `BaseConfig` by [@tlambert03](https://github.com/tlambert03) in [#7183](https://github.com/pydantic/pydantic/pull/7183) +* Only hash `model_fields`, not whole `__dict__` by [@alexmojaki](https://github.com/alexmojaki) in [#7786](https://github.com/pydantic/pydantic/pull/7786) +* Fix mishandling of unions while freezing types in the `mypy` plugin by [@dmontagu](https://github.com/dmontagu) in [#7411](https://github.com/pydantic/pydantic/pull/7411) +* Fix `mypy` error on untyped `ClassVar` by [@vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) in [#8138](https://github.com/pydantic/pydantic/pull/8138) +* Only compare pydantic fields in `BaseModel.__eq__` instead of whole `__dict__` by [@QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) in [#7825](https://github.com/pydantic/pydantic/pull/7825) +* Update `strict` docstring in `model_validate` method. by [@LukeTonin](https://github.com/LukeTonin) in [#8223](https://github.com/pydantic/pydantic/pull/8223) +* Fix overload position of `computed_field` by [@Viicos](https://github.com/Viicos) in [#8227](https://github.com/pydantic/pydantic/pull/8227) +* Fix custom type type casting used in multiple attributes by [@ianhfc](https://github.com/ianhfc) in [#8066](https://github.com/pydantic/pydantic/pull/8066) +* Fix issue not allowing `validate_call` decorator to be dynamically assigned to a class method by [@jusexton](https://github.com/jusexton) in [#8249](https://github.com/pydantic/pydantic/pull/8249) +* Fix issue `unittest.mock` deprecation warnings by [@ibleedicare](https://github.com/ibleedicare) in [#8262](https://github.com/pydantic/pydantic/pull/8262) +* Added tests for the case `JsonValue` contains subclassed primitive values by [@jusexton](https://github.com/jusexton) in [#8286](https://github.com/pydantic/pydantic/pull/8286) +* Fix `mypy` error on free before validator (classmethod) by [@sydney-runkle](https://github.com/sydney-runkle) in [#8285](https://github.com/pydantic/pydantic/pull/8285) +* Fix `to_snake` conversion by [@jevins09](https://github.com/jevins09) in [#8316](https://github.com/pydantic/pydantic/pull/8316) +* Fix type annotation of `ModelMetaclass.__prepare__` by [@slanzmich](https://github.com/slanzmich) in [#8305](https://github.com/pydantic/pydantic/pull/8305) +* Disallow `config` specification when initializing a `TypeAdapter` when the annotated type has config already by [@sydney-runkle](https://github.com/sydney-runkle) in [#8365](https://github.com/pydantic/pydantic/pull/8365) +* Fix a naming issue with JSON schema for generics parametrized by recursive type aliases by [@dmontagu](https://github.com/dmontagu) in [#8389](https://github.com/pydantic/pydantic/pull/8389) +* Fix type annotation in pydantic people script by [@shenxiangzhuang](https://github.com/shenxiangzhuang) in [#8402](https://github.com/pydantic/pydantic/pull/8402) +* Add support for field `alias` in `dataclass` signature by [@NeevCohen](https://github.com/NeevCohen) in [#8387](https://github.com/pydantic/pydantic/pull/8387) +* Fix bug with schema generation with `Field(...)` in a forward ref by [@dmontagu](https://github.com/dmontagu) in [#8494](https://github.com/pydantic/pydantic/pull/8494) +* Fix ordering of keys in `__dict__` with `model_construct` call by [@sydney-runkle](https://github.com/sydney-runkle) in [#8500](https://github.com/pydantic/pydantic/pull/8500) +* Fix module `path_type` creation when globals does not contain `__name__` by [@hramezani](https://github.com/hramezani) in [#8470](https://github.com/pydantic/pydantic/pull/8470) +* Fix for namespace issue with dataclasses with `from __future__ import annotations` by [@sydney-runkle](https://github.com/sydney-runkle) in [#8513](https://github.com/pydantic/pydantic/pull/8513) +* Fix: make function validator types positional-only by [@pmmmwh](https://github.com/pmmmwh) in [#8479](https://github.com/pydantic/pydantic/pull/8479) +* Fix usage of `@deprecated` by [@Viicos](https://github.com/Viicos) in [#8294](https://github.com/pydantic/pydantic/pull/8294) +* Add more support for private attributes in `model_construct` call by [@sydney-runkle](https://github.com/sydney-runkle) in [#8525](https://github.com/pydantic/pydantic/pull/8525) +* Use a stack for the types namespace by [@dmontagu](https://github.com/dmontagu) in [#8378](https://github.com/pydantic/pydantic/pull/8378) +* Fix schema-building bug with `TypeAliasType` for types with refs by [@dmontagu](https://github.com/dmontagu) in [#8526](https://github.com/pydantic/pydantic/pull/8526) +* Support `pydantic.Field(repr=False)` in dataclasses by [@tigeryy2](https://github.com/tigeryy2) in [#8511](https://github.com/pydantic/pydantic/pull/8511) +* Override `dataclass_transform` behavior for `RootModel` by [@Viicos](https://github.com/Viicos) in [#8163](https://github.com/pydantic/pydantic/pull/8163) +* Refactor signature generation for simplicity by [@sydney-runkle](https://github.com/sydney-runkle) in [#8572](https://github.com/pydantic/pydantic/pull/8572) +* Fix ordering bug of PlainValidator annotation by [@Anvil](https://github.com/Anvil) in [#8567](https://github.com/pydantic/pydantic/pull/8567) +* Fix `exclude_none` for json serialization of `computed_field`s by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1098](https://github.com/pydantic/pydantic-core/pull/1098) +* Support yyyy-MM-DD string for datetimes by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1124](https://github.com/pydantic/pydantic-core/pull/1124) +* Tweak ordering of definitions in generated schemas by [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) in [#8583](https://github.com/pydantic/pydantic/pull/8583) + + +### New Contributors + +#### `pydantic` +* [@ekeew](https://github.com/ekeew) made their first contribution in [#6874](https://github.com/pydantic/pydantic/pull/6874) +* [@lambertsbennett](https://github.com/lambertsbennett) made their first contribution in [#8054](https://github.com/pydantic/pydantic/pull/8054) +* [@vincent-hachin-wmx](https://github.com/vincent-hachin-wmx) made their first contribution in [#8138](https://github.com/pydantic/pydantic/pull/8138) +* [@QuentinSoubeyranAqemia](https://github.com/QuentinSoubeyranAqemia) made their first contribution in [#7825](https://github.com/pydantic/pydantic/pull/7825) +* [@ariebovenberg](https://github.com/ariebovenberg) made their first contribution in [#8072](https://github.com/pydantic/pydantic/pull/8072) +* [@LukeTonin](https://github.com/LukeTonin) made their first contribution in [#8223](https://github.com/pydantic/pydantic/pull/8223) +* [@denisart](https://github.com/denisart) made their first contribution in [#8231](https://github.com/pydantic/pydantic/pull/8231) +* [@ianhfc](https://github.com/ianhfc) made their first contribution in [#8066](https://github.com/pydantic/pydantic/pull/8066) +* [@eonu](https://github.com/eonu) made their first contribution in [#8255](https://github.com/pydantic/pydantic/pull/8255) +* [@amandahla](https://github.com/amandahla) made their first contribution in [#8263](https://github.com/pydantic/pydantic/pull/8263) +* [@ibleedicare](https://github.com/ibleedicare) made their first contribution in [#8262](https://github.com/pydantic/pydantic/pull/8262) +* [@jevins09](https://github.com/jevins09) made their first contribution in [#8316](https://github.com/pydantic/pydantic/pull/8316) +* [@cuu508](https://github.com/cuu508) made their first contribution in [#8322](https://github.com/pydantic/pydantic/pull/8322) +* [@slanzmich](https://github.com/slanzmich) made their first contribution in [#8305](https://github.com/pydantic/pydantic/pull/8305) +* [@jensenbox](https://github.com/jensenbox) made their first contribution in [#8331](https://github.com/pydantic/pydantic/pull/8331) +* [@szepeviktor](https://github.com/szepeviktor) made their first contribution in [#8356](https://github.com/pydantic/pydantic/pull/8356) +* [@Elkiwa](https://github.com/Elkiwa) made their first contribution in [#8341](https://github.com/pydantic/pydantic/pull/8341) +* [@parhamfh](https://github.com/parhamfh) made their first contribution in [#8395](https://github.com/pydantic/pydantic/pull/8395) +* [@shenxiangzhuang](https://github.com/shenxiangzhuang) made their first contribution in [#8402](https://github.com/pydantic/pydantic/pull/8402) +* [@NeevCohen](https://github.com/NeevCohen) made their first contribution in [#8387](https://github.com/pydantic/pydantic/pull/8387) +* [@zby](https://github.com/zby) made their first contribution in [#8497](https://github.com/pydantic/pydantic/pull/8497) +* [@patelnets](https://github.com/patelnets) made their first contribution in [#8491](https://github.com/pydantic/pydantic/pull/8491) +* [@edwardwli](https://github.com/edwardwli) made their first contribution in [#8503](https://github.com/pydantic/pydantic/pull/8503) +* [@luca-matei](https://github.com/luca-matei) made their first contribution in [#8507](https://github.com/pydantic/pydantic/pull/8507) +* [@Jocelyn-Gas](https://github.com/Jocelyn-Gas) made their first contribution in [#8437](https://github.com/pydantic/pydantic/pull/8437) +* [@bL34cHig0](https://github.com/bL34cHig0) made their first contribution in [#8501](https://github.com/pydantic/pydantic/pull/8501) +* [@tigeryy2](https://github.com/tigeryy2) made their first contribution in [#8511](https://github.com/pydantic/pydantic/pull/8511) +* [@geospackle](https://github.com/geospackle) made their first contribution in [#8537](https://github.com/pydantic/pydantic/pull/8537) +* [@Anvil](https://github.com/Anvil) made their first contribution in [#8567](https://github.com/pydantic/pydantic/pull/8567) +* [@hungtsetse](https://github.com/hungtsetse) made their first contribution in [#8546](https://github.com/pydantic/pydantic/pull/8546) +* [@StrawHatDrag0n](https://github.com/StrawHatDrag0n) made their first contribution in [#8583](https://github.com/pydantic/pydantic/pull/8583) + +#### `pydantic-core` +* [@mariuswinger](https://github.com/mariuswinger) made their first contribution in [pydantic/pydantic-core#1087](https://github.com/pydantic/pydantic-core/pull/1087) +* [@adamchainz](https://github.com/adamchainz) made their first contribution in [pydantic/pydantic-core#1090](https://github.com/pydantic/pydantic-core/pull/1090) +* [@akx](https://github.com/akx) made their first contribution in [pydantic/pydantic-core#1123](https://github.com/pydantic/pydantic-core/pull/1123) + +## v2.6.0b1 (2024-01-19) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.6.0b1) for details. + +## v2.5.3 (2023-12-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.3) + +### What's Changed + +#### Packaging + +* uprev `pydantic-core` to 2.14.6 + +#### Fixes + +* Fix memory leak with recursive definitions creating reference cycles by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1125](https://github.com/pydantic/pydantic-core/pull/1125) + +## v2.5.2 (2023-11-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.2) + +### What's Changed + +#### Packaging + +* uprev `pydantic-core` to 2.14.5 + +#### New Features + +* Add `ConfigDict.ser_json_inf_nan` by [@davidhewitt](https://github.com/davidhewitt) in [#8159](https://github.com/pydantic/pydantic/pull/8159) + +#### Fixes + +* Fix validation of `Literal` from JSON keys when used as `dict` key by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1075](https://github.com/pydantic/pydantic-core/pull/1075) +* Fix bug re `custom_init` on members of `Union` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1076](https://github.com/pydantic/pydantic-core/pull/1076) +* Fix `JsonValue` `bool` serialization by [@sydney-runkle](https://github.com/sydney-runkle) in [#8190](https://github.com/pydantic/pydantic/pull/8159) +* Fix handling of unhashable inputs with `Literal` in `Union`s by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1089](https://github.com/pydantic/pydantic-core/pull/1089) + +## v2.5.1 (2023-11-15) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.1) + +### What's Changed + +#### Packaging + +* uprev pydantic-core to 2.14.3 by [@samuelcolvin](https://github.com/samuelcolvin) in [#8120](https://github.com/pydantic/pydantic/pull/8120) + +#### Fixes + +* Fix package description limit by [@dmontagu](https://github.com/dmontagu) in [#8097](https://github.com/pydantic/pydantic/pull/8097) +* Fix `ValidateCallWrapper` error when creating a model which has a [@validate_call](https://github.com/validate_call) wrapped field annotation by [@sydney-runkle](https://github.com/sydney-runkle) in [#8110](https://github.com/pydantic/pydantic/pull/8110) + +## v2.5.0 (2023-11-13) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.0) + +The code released in v2.5.0 is functionally identical to that of v2.5.0b1. + +### What's Changed + +#### Packaging + +* Update pydantic-core from 2.10.1 to 2.14.1, significant changes from these updates are described below, full changelog [here](https://github.com/pydantic/pydantic-core/compare/v2.10.1...v2.14.1) +* Update to `pyright==1.1.335` by [@Viicos](https://github.com/Viicos) in [#8075](https://github.com/pydantic/pydantic/pull/8075) + +#### New Features + +* Allow plugins to catch non `ValidationError` errors by [@adriangb](https://github.com/adriangb) in [#7806](https://github.com/pydantic/pydantic/pull/7806) +* Support `__doc__` argument in `create_model()` by [@chris-spann](https://github.com/chris-spann) in [#7863](https://github.com/pydantic/pydantic/pull/7863) +* Expose `regex_engine` flag - meaning you can use with the Rust or Python regex libraries in constraints by [@utkini](https://github.com/utkini) in [#7768](https://github.com/pydantic/pydantic/pull/7768) +* Save return type generated from type annotation in `ComputedFieldInfo` by [@alexmojaki](https://github.com/alexmojaki) in [#7889](https://github.com/pydantic/pydantic/pull/7889) +* Adopting `ruff` formatter by [@Luca-Blight](https://github.com/Luca-Blight) in [#7930](https://github.com/pydantic/pydantic/pull/7930) +* Added `validation_error_cause` to config by [@zakstucke](https://github.com/zakstucke) in [#7626](https://github.com/pydantic/pydantic/pull/7626) +* Make path of the item to validate available in plugin by [@hramezani](https://github.com/hramezani) in [#7861](https://github.com/pydantic/pydantic/pull/7861) +* Add `CallableDiscriminator` and `Tag` by [@dmontagu](https://github.com/dmontagu) in [#7983](https://github.com/pydantic/pydantic/pull/7983) + * `CallableDiscriminator` renamed to `Discriminator` by [@dmontagu](https://github.com/dmontagu) in [#8047](https://github.com/pydantic/pydantic/pull/8047) +* Make union case tags affect union error messages by [@dmontagu](https://github.com/dmontagu) in [#8001](https://github.com/pydantic/pydantic/pull/8001) +* Add `examples` and `json_schema_extra` to `@computed_field` by [@alexmojaki](https://github.com/alexmojaki) in [#8013](https://github.com/pydantic/pydantic/pull/8013) +* Add `JsonValue` type by [@dmontagu](https://github.com/dmontagu) in [#7998](https://github.com/pydantic/pydantic/pull/7998) +* Allow `str` as argument to `Discriminator` by [@dmontagu](https://github.com/dmontagu) in [#8047](https://github.com/pydantic/pydantic/pull/8047) +* Add `SchemaSerializer.__reduce__` method to enable pickle serialization by [@edoakes](https://github.com/edoakes) in [pydantic/pydantic-core#1006](https://github.com/pydantic/pydantic-core/pull/1006) + +#### Changes + +* **Significant Change:** replace `ultra_strict` with new smart union implementation, the way unions are validated has changed significantly to improve performance and correctness, we have worked hard to absolutely minimise the number of cases where behaviour has changed, see the PR for details - by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#867](https://github.com/pydantic/pydantic-core/pull/867) +* Add support for instance method reassignment when `extra='allow'` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7683](https://github.com/pydantic/pydantic/pull/7683) +* Support JSON schema generation for `Enum` types with no cases by [@sydney-runkle](https://github.com/sydney-runkle) in [#7927](https://github.com/pydantic/pydantic/pull/7927) +* Warn if a class inherits from `Generic` before `BaseModel` by [@alexmojaki](https://github.com/alexmojaki) in [#7891](https://github.com/pydantic/pydantic/pull/7891) + +#### Performance + +* New custom JSON parser, `jiter` by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#974](https://github.com/pydantic/pydantic-core/pull/974) +* PGO build for MacOS M1 by [@samuelcolvin](https://github.com/samuelcolvin) in [pydantic/pydantic-core#1063](https://github.com/pydantic/pydantic-core/pull/1063) +* Use `__getattr__` for all package imports, improve import time by [@samuelcolvin](https://github.com/samuelcolvin) in [#7947](https://github.com/pydantic/pydantic/pull/7947) + +#### Fixes + +* Fix `mypy` issue with subclasses of `RootModel` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7677](https://github.com/pydantic/pydantic/pull/7677) +* Properly rebuild the `FieldInfo` when a forward ref gets evaluated by [@dmontagu](https://github.com/dmontagu) in [#7698](https://github.com/pydantic/pydantic/pull/7698) +* Fix failure to load `SecretStr` from JSON (regression in v2.4) by [@sydney-runkle](https://github.com/sydney-runkle) in [#7729](https://github.com/pydantic/pydantic/pull/7729) +* Fix `defer_build` behavior with `TypeAdapter` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7736](https://github.com/pydantic/pydantic/pull/7736) +* Improve compatibility with legacy `mypy` versions by [@dmontagu](https://github.com/dmontagu) in [#7742](https://github.com/pydantic/pydantic/pull/7742) +* Fix: update `TypeVar` handling when default is not set by [@pmmmwh](https://github.com/pmmmwh) in [#7719](https://github.com/pydantic/pydantic/pull/7719) +* Support specification of `strict` on `Enum` type fields by [@sydney-runkle](https://github.com/sydney-runkle) in [#7761](https://github.com/pydantic/pydantic/pull/7761) +* Wrap `weakref.ref` instead of subclassing to fix `cloudpickle` serialization by [@edoakes](https://github.com/edoakes) in [#7780](https://github.com/pydantic/pydantic/pull/7780) +* Keep values of private attributes set within `model_post_init` in subclasses by [@alexmojaki](https://github.com/alexmojaki) in [#7775](https://github.com/pydantic/pydantic/pull/7775) +* Add more specific type for non-callable `json_schema_extra` by [@alexmojaki](https://github.com/alexmojaki) in [#7803](https://github.com/pydantic/pydantic/pull/7803) +* Raise an error when deleting frozen (model) fields by [@alexmojaki](https://github.com/alexmojaki) in [#7800](https://github.com/pydantic/pydantic/pull/7800) +* Fix schema sorting bug with default values by [@sydney-runkle](https://github.com/sydney-runkle) in [#7817](https://github.com/pydantic/pydantic/pull/7817) +* Use generated alias for aliases that are not specified otherwise by [@alexmojaki](https://github.com/alexmojaki) in [#7802](https://github.com/pydantic/pydantic/pull/7802) +* Support `strict` specification for `UUID` types by [@sydney-runkle](https://github.com/sydney-runkle) in [#7865](https://github.com/pydantic/pydantic/pull/7865) +* JSON schema: fix extra parameter handling by [@me-and](https://github.com/me-and) in [#7810](https://github.com/pydantic/pydantic/pull/7810) +* Fix: support `pydantic.Field(kw_only=True)` with inherited dataclasses by [@PrettyWood](https://github.com/PrettyWood) in [#7827](https://github.com/pydantic/pydantic/pull/7827) +* Support `validate_call` decorator for methods in classes with `__slots__` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7883](https://github.com/pydantic/pydantic/pull/7883) +* Fix pydantic dataclass problem with `dataclasses.field` default by [@hramezani](https://github.com/hramezani) in [#7898](https://github.com/pydantic/pydantic/pull/7898) +* Fix schema generation for generics with union type bounds by [@sydney-runkle](https://github.com/sydney-runkle) in [#7899](https://github.com/pydantic/pydantic/pull/7899) +* Fix version for `importlib_metadata` on python 3.7 by [@sydney-runkle](https://github.com/sydney-runkle) in [#7904](https://github.com/pydantic/pydantic/pull/7904) +* Support `|` operator (Union) in PydanticRecursiveRef by [@alexmojaki](https://github.com/alexmojaki) in [#7892](https://github.com/pydantic/pydantic/pull/7892) +* Fix `display_as_type` for `TypeAliasType` in python 3.12 by [@dmontagu](https://github.com/dmontagu) in [#7929](https://github.com/pydantic/pydantic/pull/7929) +* Add support for `NotRequired` generics in `TypedDict` by [@sydney-runkle](https://github.com/sydney-runkle) in [#7932](https://github.com/pydantic/pydantic/pull/7932) +* Make generic `TypeAliasType` specifications produce different schema definitions by [@alexdrydew](https://github.com/alexdrydew) in [#7893](https://github.com/pydantic/pydantic/pull/7893) +* Added fix for signature of inherited dataclass by [@howsunjow](https://github.com/howsunjow) in [#7925](https://github.com/pydantic/pydantic/pull/7925) +* Make the model name generation more robust in JSON schema by [@joakimnordling](https://github.com/joakimnordling) in [#7881](https://github.com/pydantic/pydantic/pull/7881) +* Fix plurals in validation error messages (in tests) by [@Iipin](https://github.com/Iipin) in [#7972](https://github.com/pydantic/pydantic/pull/7972) +* `PrivateAttr` is passed from `Annotated` default position by [@tabassco](https://github.com/tabassco) in [#8004](https://github.com/pydantic/pydantic/pull/8004) +* Don't decode bytes (which may not be UTF8) when displaying SecretBytes by [@alexmojaki](https://github.com/alexmojaki) in [#8012](https://github.com/pydantic/pydantic/pull/8012) +* Use `classmethod` instead of `classmethod[Any, Any, Any]` by [@Mr-Pepe](https://github.com/Mr-Pepe) in [#7979](https://github.com/pydantic/pydantic/pull/7979) +* Clearer error on invalid Plugin by [@samuelcolvin](https://github.com/samuelcolvin) in [#8023](https://github.com/pydantic/pydantic/pull/8023) +* Correct pydantic dataclasses import by [@samuelcolvin](https://github.com/samuelcolvin) in [#8027](https://github.com/pydantic/pydantic/pull/8027) +* Fix misbehavior for models referencing redefined type aliases by [@dmontagu](https://github.com/dmontagu) in [#8050](https://github.com/pydantic/pydantic/pull/8050) +* Fix `Optional` field with `validate_default` only performing one field validation by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1002](https://github.com/pydantic/pydantic-core/pull/1002) +* Fix `definition-ref` bug with `Dict` keys by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1014](https://github.com/pydantic/pydantic-core/pull/1014) +* Fix bug allowing validation of `bool` types with `coerce_numbers_to_str=True` by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1017](https://github.com/pydantic/pydantic-core/pull/1017) +* Don't accept `NaN` in float and decimal constraints by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1037](https://github.com/pydantic/pydantic-core/pull/1037) +* Add `lax_str` and `lax_int` support for enum values not inherited from str/int by [@michaelhly](https://github.com/michaelhly) in [pydantic/pydantic-core#1015](https://github.com/pydantic/pydantic-core/pull/1015) +* Support subclasses in lists in `Union` of `List` types by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1039](https://github.com/pydantic/pydantic-core/pull/1039) +* Allow validation against `max_digits` and `decimals` to pass if normalized or non-normalized input is valid by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1049](https://github.com/pydantic/pydantic-core/pull/1049) +* Fix: proper pluralization in `ValidationError` messages by [@Iipin](https://github.com/Iipin) in [pydantic/pydantic-core#1050](https://github.com/pydantic/pydantic-core/pull/1050) +* Disallow the string `'-'` as `datetime` input by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/speedate#52](https://github.com/pydantic/speedate/pull/52) & [pydantic/pydantic-core#1060](https://github.com/pydantic/pydantic-core/pull/1060) +* Fix: NaN and Inf float serialization by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1062](https://github.com/pydantic/pydantic-core/pull/1062) +* Restore manylinux-compatible PGO builds by [@davidhewitt](https://github.com/davidhewitt) in [pydantic/pydantic-core#1068](https://github.com/pydantic/pydantic-core/pull/1068) + +### New Contributors + +#### `pydantic` +* [@schneebuzz](https://github.com/schneebuzz) made their first contribution in [#7699](https://github.com/pydantic/pydantic/pull/7699) +* [@edoakes](https://github.com/edoakes) made their first contribution in [#7780](https://github.com/pydantic/pydantic/pull/7780) +* [@alexmojaki](https://github.com/alexmojaki) made their first contribution in [#7775](https://github.com/pydantic/pydantic/pull/7775) +* [@NickG123](https://github.com/NickG123) made their first contribution in [#7751](https://github.com/pydantic/pydantic/pull/7751) +* [@gowthamgts](https://github.com/gowthamgts) made their first contribution in [#7830](https://github.com/pydantic/pydantic/pull/7830) +* [@jamesbraza](https://github.com/jamesbraza) made their first contribution in [#7848](https://github.com/pydantic/pydantic/pull/7848) +* [@laundmo](https://github.com/laundmo) made their first contribution in [#7850](https://github.com/pydantic/pydantic/pull/7850) +* [@rahmatnazali](https://github.com/rahmatnazali) made their first contribution in [#7870](https://github.com/pydantic/pydantic/pull/7870) +* [@waterfountain1996](https://github.com/waterfountain1996) made their first contribution in [#7878](https://github.com/pydantic/pydantic/pull/7878) +* [@chris-spann](https://github.com/chris-spann) made their first contribution in [#7863](https://github.com/pydantic/pydantic/pull/7863) +* [@me-and](https://github.com/me-and) made their first contribution in [#7810](https://github.com/pydantic/pydantic/pull/7810) +* [@utkini](https://github.com/utkini) made their first contribution in [#7768](https://github.com/pydantic/pydantic/pull/7768) +* [@bn-l](https://github.com/bn-l) made their first contribution in [#7744](https://github.com/pydantic/pydantic/pull/7744) +* [@alexdrydew](https://github.com/alexdrydew) made their first contribution in [#7893](https://github.com/pydantic/pydantic/pull/7893) +* [@Luca-Blight](https://github.com/Luca-Blight) made their first contribution in [#7930](https://github.com/pydantic/pydantic/pull/7930) +* [@howsunjow](https://github.com/howsunjow) made their first contribution in [#7925](https://github.com/pydantic/pydantic/pull/7925) +* [@joakimnordling](https://github.com/joakimnordling) made their first contribution in [#7881](https://github.com/pydantic/pydantic/pull/7881) +* [@icfly2](https://github.com/icfly2) made their first contribution in [#7976](https://github.com/pydantic/pydantic/pull/7976) +* [@Yummy-Yums](https://github.com/Yummy-Yums) made their first contribution in [#8003](https://github.com/pydantic/pydantic/pull/8003) +* [@Iipin](https://github.com/Iipin) made their first contribution in [#7972](https://github.com/pydantic/pydantic/pull/7972) +* [@tabassco](https://github.com/tabassco) made their first contribution in [#8004](https://github.com/pydantic/pydantic/pull/8004) +* [@Mr-Pepe](https://github.com/Mr-Pepe) made their first contribution in [#7979](https://github.com/pydantic/pydantic/pull/7979) +* [@0x00cl](https://github.com/0x00cl) made their first contribution in [#8010](https://github.com/pydantic/pydantic/pull/8010) +* [@barraponto](https://github.com/barraponto) made their first contribution in [#8032](https://github.com/pydantic/pydantic/pull/8032) + +#### `pydantic-core` +* [@sisp](https://github.com/sisp) made their first contribution in [pydantic/pydantic-core#995](https://github.com/pydantic/pydantic-core/pull/995) +* [@michaelhly](https://github.com/michaelhly) made their first contribution in [pydantic/pydantic-core#1015](https://github.com/pydantic/pydantic-core/pull/1015) + +## v2.5.0b1 (2023-11-09) + +Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.5.0b1) for details. + +## v2.4.2 (2023-09-27) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.2) + +### What's Changed + +#### Fixes + +* Fix bug with JSON schema for sequence of discriminated union by [@dmontagu](https://github.com/dmontagu) in [#7647](https://github.com/pydantic/pydantic/pull/7647) +* Fix schema references in discriminated unions by [@adriangb](https://github.com/adriangb) in [#7646](https://github.com/pydantic/pydantic/pull/7646) +* Fix json schema generation for recursive models by [@adriangb](https://github.com/adriangb) in [#7653](https://github.com/pydantic/pydantic/pull/7653) +* Fix `models_json_schema` for generic models by [@adriangb](https://github.com/adriangb) in [#7654](https://github.com/pydantic/pydantic/pull/7654) +* Fix xfailed test for generic model signatures by [@adriangb](https://github.com/adriangb) in [#7658](https://github.com/pydantic/pydantic/pull/7658) + +### New Contributors + +* [@austinorr](https://github.com/austinorr) made their first contribution in [#7657](https://github.com/pydantic/pydantic/pull/7657) +* [@peterHoburg](https://github.com/peterHoburg) made their first contribution in [#7670](https://github.com/pydantic/pydantic/pull/7670) + +## v2.4.1 (2023-09-26) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.1) + +### What's Changed + +#### Packaging + +* Update pydantic-core to 2.10.1 by [@davidhewitt](https://github.com/davidhewitt) in [#7633](https://github.com/pydantic/pydantic/pull/7633) + +#### Fixes + +* Serialize unsubstituted type vars as `Any` by [@adriangb](https://github.com/adriangb) in [#7606](https://github.com/pydantic/pydantic/pull/7606) +* Remove schema building caches by [@adriangb](https://github.com/adriangb) in [#7624](https://github.com/pydantic/pydantic/pull/7624) +* Fix an issue where JSON schema extras weren't JSON encoded by [@dmontagu](https://github.com/dmontagu) in [#7625](https://github.com/pydantic/pydantic/pull/7625) + +## v2.4.0 (2023-09-22) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.4.0) + +### What's Changed + +#### Packaging + +* Update pydantic-core to 2.10.0 by [@samuelcolvin](https://github.com/samuelcolvin) in [#7542](https://github.com/pydantic/pydantic/pull/7542) + +#### New Features + +* Add `Base64Url` types by [@dmontagu](https://github.com/dmontagu) in [#7286](https://github.com/pydantic/pydantic/pull/7286) +* Implement optional `number` to `str` coercion by [@lig](https://github.com/lig) in [#7508](https://github.com/pydantic/pydantic/pull/7508) +* Allow access to `field_name` and `data` in all validators if there is data and a field name by [@samuelcolvin](https://github.com/samuelcolvin) in [#7542](https://github.com/pydantic/pydantic/pull/7542) +* Add `BaseModel.model_validate_strings` and `TypeAdapter.validate_strings` by [@hramezani](https://github.com/hramezani) in [#7552](https://github.com/pydantic/pydantic/pull/7552) +* Add Pydantic `plugins` experimental implementation by [@lig](https://github.com/lig) [@samuelcolvin](https://github.com/samuelcolvin) and [@Kludex](https://github.com/Kludex) in [#6820](https://github.com/pydantic/pydantic/pull/6820) + +#### Changes + +* Do not override `model_post_init` in subclass with private attrs by [@Viicos](https://github.com/Viicos) in [#7302](https://github.com/pydantic/pydantic/pull/7302) +* Make fields with defaults not required in the serialization schema by default by [@dmontagu](https://github.com/dmontagu) in [#7275](https://github.com/pydantic/pydantic/pull/7275) +* Mark `Extra` as deprecated by [@disrupted](https://github.com/disrupted) in [#7299](https://github.com/pydantic/pydantic/pull/7299) +* Make `EncodedStr` a dataclass by [@Kludex](https://github.com/Kludex) in [#7396](https://github.com/pydantic/pydantic/pull/7396) +* Move `annotated_handlers` to be public by [@samuelcolvin](https://github.com/samuelcolvin) in [#7569](https://github.com/pydantic/pydantic/pull/7569) + +#### Performance + +* Simplify flattening and inlining of `CoreSchema` by [@adriangb](https://github.com/adriangb) in [#7523](https://github.com/pydantic/pydantic/pull/7523) +* Remove unused copies in `CoreSchema` walking by [@adriangb](https://github.com/adriangb) in [#7528](https://github.com/pydantic/pydantic/pull/7528) +* Add caches for collecting definitions and invalid schemas from a CoreSchema by [@adriangb](https://github.com/adriangb) in [#7527](https://github.com/pydantic/pydantic/pull/7527) +* Eagerly resolve discriminated unions and cache cases where we can't by [@adriangb](https://github.com/adriangb) in [#7529](https://github.com/pydantic/pydantic/pull/7529) +* Replace `dict.get` and `dict.setdefault` with more verbose versions in `CoreSchema` building hot paths by [@adriangb](https://github.com/adriangb) in [#7536](https://github.com/pydantic/pydantic/pull/7536) +* Cache invalid `CoreSchema` discovery by [@adriangb](https://github.com/adriangb) in [#7535](https://github.com/pydantic/pydantic/pull/7535) +* Allow disabling `CoreSchema` validation for faster startup times by [@adriangb](https://github.com/adriangb) in [#7565](https://github.com/pydantic/pydantic/pull/7565) + +#### Fixes + +* Fix config detection for `TypedDict` from grandparent classes by [@dmontagu](https://github.com/dmontagu) in [#7272](https://github.com/pydantic/pydantic/pull/7272) +* Fix hash function generation for frozen models with unusual MRO by [@dmontagu](https://github.com/dmontagu) in [#7274](https://github.com/pydantic/pydantic/pull/7274) +* Make `strict` config overridable in field for Path by [@hramezani](https://github.com/hramezani) in [#7281](https://github.com/pydantic/pydantic/pull/7281) +* Use `ser_json_` on default in `GenerateJsonSchema` by [@Kludex](https://github.com/Kludex) in [#7269](https://github.com/pydantic/pydantic/pull/7269) +* Adding a check that alias is validated as an identifier for Python by [@andree0](https://github.com/andree0) in [#7319](https://github.com/pydantic/pydantic/pull/7319) +* Raise an error when computed field overrides field by [@sydney-runkle](https://github.com/sydney-runkle) in [#7346](https://github.com/pydantic/pydantic/pull/7346) +* Fix applying `SkipValidation` to referenced schemas by [@adriangb](https://github.com/adriangb) in [#7381](https://github.com/pydantic/pydantic/pull/7381) +* Enforce behavior of private attributes having double leading underscore by [@lig](https://github.com/lig) in [#7265](https://github.com/pydantic/pydantic/pull/7265) +* Standardize `__get_pydantic_core_schema__` signature by [@hramezani](https://github.com/hramezani) in [#7415](https://github.com/pydantic/pydantic/pull/7415) +* Fix generic dataclass fields mutation bug (when using `TypeAdapter`) by [@sydney-runkle](https://github.com/sydney-runkle) in [#7435](https://github.com/pydantic/pydantic/pull/7435) +* Fix `TypeError` on `model_validator` in `wrap` mode by [@pmmmwh](https://github.com/pmmmwh) in [#7496](https://github.com/pydantic/pydantic/pull/7496) +* Improve enum error message by [@hramezani](https://github.com/hramezani) in [#7506](https://github.com/pydantic/pydantic/pull/7506) +* Make `repr` work for instances that failed initialization when handling `ValidationError`s by [@dmontagu](https://github.com/dmontagu) in [#7439](https://github.com/pydantic/pydantic/pull/7439) +* Fixed a regular expression denial of service issue by limiting whitespaces by [@prodigysml](https://github.com/prodigysml) in [#7360](https://github.com/pydantic/pydantic/pull/7360) +* Fix handling of `UUID` values having `UUID.version=None` by [@lig](https://github.com/lig) in [#7566](https://github.com/pydantic/pydantic/pull/7566) +* Fix `__iter__` returning private `cached_property` info by [@sydney-runkle](https://github.com/sydney-runkle) in [#7570](https://github.com/pydantic/pydantic/pull/7570) +* Improvements to version info message by [@samuelcolvin](https://github.com/samuelcolvin) in [#7594](https://github.com/pydantic/pydantic/pull/7594) + +### New Contributors +* [@15498th](https://github.com/15498th) made their first contribution in [#7238](https://github.com/pydantic/pydantic/pull/7238) +* [@GabrielCappelli](https://github.com/GabrielCappelli) made their first contribution in [#7213](https://github.com/pydantic/pydantic/pull/7213) +* [@tobni](https://github.com/tobni) made their first contribution in [#7184](https://github.com/pydantic/pydantic/pull/7184) +* [@redruin1](https://github.com/redruin1) made their first contribution in [#7282](https://github.com/pydantic/pydantic/pull/7282) +* [@FacerAin](https://github.com/FacerAin) made their first contribution in [#7288](https://github.com/pydantic/pydantic/pull/7288) +* [@acdha](https://github.com/acdha) made their first contribution in [#7297](https://github.com/pydantic/pydantic/pull/7297) +* [@andree0](https://github.com/andree0) made their first contribution in [#7319](https://github.com/pydantic/pydantic/pull/7319) +* [@gordonhart](https://github.com/gordonhart) made their first contribution in [#7375](https://github.com/pydantic/pydantic/pull/7375) +* [@pmmmwh](https://github.com/pmmmwh) made their first contribution in [#7496](https://github.com/pydantic/pydantic/pull/7496) +* [@disrupted](https://github.com/disrupted) made their first contribution in [#7299](https://github.com/pydantic/pydantic/pull/7299) +* [@prodigysml](https://github.com/prodigysml) made their first contribution in [#7360](https://github.com/pydantic/pydantic/pull/7360) + +## v2.3.0 (2023-08-23) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.3.0) + +* 🔥 Remove orphaned changes file from repo by [@lig](https://github.com/lig) in [#7168](https://github.com/pydantic/pydantic/pull/7168) +* Add copy button on documentation by [@Kludex](https://github.com/Kludex) in [#7190](https://github.com/pydantic/pydantic/pull/7190) +* Fix docs on JSON type by [@Kludex](https://github.com/Kludex) in [#7189](https://github.com/pydantic/pydantic/pull/7189) +* Update mypy 1.5.0 to 1.5.1 in CI by [@hramezani](https://github.com/hramezani) in [#7191](https://github.com/pydantic/pydantic/pull/7191) +* fix download links badge by [@samuelcolvin](https://github.com/samuelcolvin) in [#7200](https://github.com/pydantic/pydantic/pull/7200) +* add 2.2.1 to changelog by [@samuelcolvin](https://github.com/samuelcolvin) in [#7212](https://github.com/pydantic/pydantic/pull/7212) +* Make ModelWrapValidator protocols generic by [@dmontagu](https://github.com/dmontagu) in [#7154](https://github.com/pydantic/pydantic/pull/7154) +* Correct `Field(..., exclude: bool)` docs by [@samuelcolvin](https://github.com/samuelcolvin) in [#7214](https://github.com/pydantic/pydantic/pull/7214) +* Make shadowing attributes a warning instead of an error by [@adriangb](https://github.com/adriangb) in [#7193](https://github.com/pydantic/pydantic/pull/7193) +* Document `Base64Str` and `Base64Bytes` by [@Kludex](https://github.com/Kludex) in [#7192](https://github.com/pydantic/pydantic/pull/7192) +* Fix `config.defer_build` for serialization first cases by [@samuelcolvin](https://github.com/samuelcolvin) in [#7024](https://github.com/pydantic/pydantic/pull/7024) +* clean Model docstrings in JSON Schema by [@samuelcolvin](https://github.com/samuelcolvin) in [#7210](https://github.com/pydantic/pydantic/pull/7210) +* fix [#7228](https://github.com/pydantic/pydantic/pull/7228) (typo): docs in `validators.md` to correct `validate_default` kwarg by [@lmmx](https://github.com/lmmx) in [#7229](https://github.com/pydantic/pydantic/pull/7229) +* ✅ Implement `tzinfo.fromutc` method for `TzInfo` in `pydantic-core` by [@lig](https://github.com/lig) in [#7019](https://github.com/pydantic/pydantic/pull/7019) +* Support `__get_validators__` by [@hramezani](https://github.com/hramezani) in [#7197](https://github.com/pydantic/pydantic/pull/7197) + +## v2.2.1 (2023-08-18) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.2.1) + +* Make `xfail`ing test for root model extra stop `xfail`ing by [@dmontagu](https://github.com/dmontagu) in [#6937](https://github.com/pydantic/pydantic/pull/6937) +* Optimize recursion detection by stopping on the second visit for the same object by [@mciucu](https://github.com/mciucu) in [#7160](https://github.com/pydantic/pydantic/pull/7160) +* fix link in docs by [@tlambert03](https://github.com/tlambert03) in [#7166](https://github.com/pydantic/pydantic/pull/7166) +* Replace MiMalloc w/ default allocator by [@adriangb](https://github.com/adriangb) in [pydantic/pydantic-core#900](https://github.com/pydantic/pydantic-core/pull/900) +* Bump pydantic-core to 2.6.1 and prepare 2.2.1 release by [@adriangb](https://github.com/adriangb) in [#7176](https://github.com/pydantic/pydantic/pull/7176) + +## v2.2.0 (2023-08-17) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.2.0) + +* Split "pipx install" setup command into two commands on the documentation site by [@nomadmtb](https://github.com/nomadmtb) in [#6869](https://github.com/pydantic/pydantic/pull/6869) +* Deprecate `Field.include` by [@hramezani](https://github.com/hramezani) in [#6852](https://github.com/pydantic/pydantic/pull/6852) +* Fix typo in default factory error msg by [@hramezani](https://github.com/hramezani) in [#6880](https://github.com/pydantic/pydantic/pull/6880) +* Simplify handling of typing.Annotated in GenerateSchema by [@dmontagu](https://github.com/dmontagu) in [#6887](https://github.com/pydantic/pydantic/pull/6887) +* Re-enable fastapi tests in CI by [@dmontagu](https://github.com/dmontagu) in [#6883](https://github.com/pydantic/pydantic/pull/6883) +* Make it harder to hit collisions with json schema defrefs by [@dmontagu](https://github.com/dmontagu) in [#6566](https://github.com/pydantic/pydantic/pull/6566) +* Cleaner error for invalid input to `Path` fields by [@samuelcolvin](https://github.com/samuelcolvin) in [#6903](https://github.com/pydantic/pydantic/pull/6903) +* :memo: support Coordinate Type by [@yezz123](https://github.com/yezz123) in [#6906](https://github.com/pydantic/pydantic/pull/6906) +* Fix `ForwardRef` wrapper for py 3.10.0 (shim until bpo-45166) by [@randomir](https://github.com/randomir) in [#6919](https://github.com/pydantic/pydantic/pull/6919) +* Fix misbehavior related to copying of RootModel by [@dmontagu](https://github.com/dmontagu) in [#6918](https://github.com/pydantic/pydantic/pull/6918) +* Fix issue with recursion error caused by ParamSpec by [@dmontagu](https://github.com/dmontagu) in [#6923](https://github.com/pydantic/pydantic/pull/6923) +* Add section about Constrained classes to the Migration Guide by [@Kludex](https://github.com/Kludex) in [#6924](https://github.com/pydantic/pydantic/pull/6924) +* Use `main` branch for badge links by [@Viicos](https://github.com/Viicos) in [#6925](https://github.com/pydantic/pydantic/pull/6925) +* Add test for v1/v2 Annotated discrepancy by [@carlbordum](https://github.com/carlbordum) in [#6926](https://github.com/pydantic/pydantic/pull/6926) +* Make the v1 mypy plugin work with both v1 and v2 by [@dmontagu](https://github.com/dmontagu) in [#6921](https://github.com/pydantic/pydantic/pull/6921) +* Fix issue where generic models couldn't be parametrized with BaseModel by [@dmontagu](https://github.com/dmontagu) in [#6933](https://github.com/pydantic/pydantic/pull/6933) +* Remove xfail for discriminated union with alias by [@dmontagu](https://github.com/dmontagu) in [#6938](https://github.com/pydantic/pydantic/pull/6938) +* add field_serializer to computed_field by [@andresliszt](https://github.com/andresliszt) in [#6965](https://github.com/pydantic/pydantic/pull/6965) +* Use union_schema with Type[Union[...]] by [@JeanArhancet](https://github.com/JeanArhancet) in [#6952](https://github.com/pydantic/pydantic/pull/6952) +* Fix inherited typeddict attributes / config by [@adriangb](https://github.com/adriangb) in [#6981](https://github.com/pydantic/pydantic/pull/6981) +* fix dataclass annotated before validator called twice by [@davidhewitt](https://github.com/davidhewitt) in [#6998](https://github.com/pydantic/pydantic/pull/6998) +* Update test-fastapi deselected tests by [@hramezani](https://github.com/hramezani) in [#7014](https://github.com/pydantic/pydantic/pull/7014) +* Fix validator doc format by [@hramezani](https://github.com/hramezani) in [#7015](https://github.com/pydantic/pydantic/pull/7015) +* Fix typo in docstring of model_json_schema by [@AdamVinch-Federated](https://github.com/AdamVinch-Federated) in [#7032](https://github.com/pydantic/pydantic/pull/7032) +* remove unused "type ignores" with pyright by [@samuelcolvin](https://github.com/samuelcolvin) in [#7026](https://github.com/pydantic/pydantic/pull/7026) +* Add benchmark representing FastAPI startup time by [@adriangb](https://github.com/adriangb) in [#7030](https://github.com/pydantic/pydantic/pull/7030) +* Fix json_encoders for Enum subclasses by [@adriangb](https://github.com/adriangb) in [#7029](https://github.com/pydantic/pydantic/pull/7029) +* Update docstring of `ser_json_bytes` regarding base64 encoding by [@Viicos](https://github.com/Viicos) in [#7052](https://github.com/pydantic/pydantic/pull/7052) +* Allow `@validate_call` to work on async methods by [@adriangb](https://github.com/adriangb) in [#7046](https://github.com/pydantic/pydantic/pull/7046) +* Fix: mypy error with `Settings` and `SettingsConfigDict` by [@JeanArhancet](https://github.com/JeanArhancet) in [#7002](https://github.com/pydantic/pydantic/pull/7002) +* Fix some typos (repeated words and it's/its) by [@eumiro](https://github.com/eumiro) in [#7063](https://github.com/pydantic/pydantic/pull/7063) +* Fix the typo in docstring by [@harunyasar](https://github.com/harunyasar) in [#7062](https://github.com/pydantic/pydantic/pull/7062) +* Docs: Fix broken URL in the pydantic-settings package recommendation by [@swetjen](https://github.com/swetjen) in [#6995](https://github.com/pydantic/pydantic/pull/6995) +* Handle constraints being applied to schemas that don't accept it by [@adriangb](https://github.com/adriangb) in [#6951](https://github.com/pydantic/pydantic/pull/6951) +* Replace almost_equal_floats with math.isclose by [@eumiro](https://github.com/eumiro) in [#7082](https://github.com/pydantic/pydantic/pull/7082) +* bump pydantic-core to 2.5.0 by [@davidhewitt](https://github.com/davidhewitt) in [#7077](https://github.com/pydantic/pydantic/pull/7077) +* Add `short_version` and use it in links by [@hramezani](https://github.com/hramezani) in [#7115](https://github.com/pydantic/pydantic/pull/7115) +* 📝 Add usage link to `RootModel` by [@Kludex](https://github.com/Kludex) in [#7113](https://github.com/pydantic/pydantic/pull/7113) +* Revert "Fix default port for mongosrv DSNs (#6827)" by [@Kludex](https://github.com/Kludex) in [#7116](https://github.com/pydantic/pydantic/pull/7116) +* Clarify validate_default and _Unset handling in usage docs and migration guide by [@benbenbang](https://github.com/benbenbang) in [#6950](https://github.com/pydantic/pydantic/pull/6950) +* Tweak documentation of `Field.exclude` by [@Viicos](https://github.com/Viicos) in [#7086](https://github.com/pydantic/pydantic/pull/7086) +* Do not require `validate_assignment` to use `Field.frozen` by [@Viicos](https://github.com/Viicos) in [#7103](https://github.com/pydantic/pydantic/pull/7103) +* tweaks to `_core_utils` by [@samuelcolvin](https://github.com/samuelcolvin) in [#7040](https://github.com/pydantic/pydantic/pull/7040) +* Make DefaultDict working with set by [@hramezani](https://github.com/hramezani) in [#7126](https://github.com/pydantic/pydantic/pull/7126) +* Don't always require typing.Generic as a base for partially parametrized models by [@dmontagu](https://github.com/dmontagu) in [#7119](https://github.com/pydantic/pydantic/pull/7119) +* Fix issue with JSON schema incorrectly using parent class core schema by [@dmontagu](https://github.com/dmontagu) in [#7020](https://github.com/pydantic/pydantic/pull/7020) +* Fix xfailed test related to TypedDict and alias_generator by [@dmontagu](https://github.com/dmontagu) in [#6940](https://github.com/pydantic/pydantic/pull/6940) +* Improve error message for NameEmail by [@dmontagu](https://github.com/dmontagu) in [#6939](https://github.com/pydantic/pydantic/pull/6939) +* Fix generic computed fields by [@dmontagu](https://github.com/dmontagu) in [#6988](https://github.com/pydantic/pydantic/pull/6988) +* Reflect namedtuple default values during validation by [@dmontagu](https://github.com/dmontagu) in [#7144](https://github.com/pydantic/pydantic/pull/7144) +* Update dependencies, fix pydantic-core usage, fix CI issues by [@dmontagu](https://github.com/dmontagu) in [#7150](https://github.com/pydantic/pydantic/pull/7150) +* Add mypy 1.5.0 by [@hramezani](https://github.com/hramezani) in [#7118](https://github.com/pydantic/pydantic/pull/7118) +* Handle non-json native enum values by [@adriangb](https://github.com/adriangb) in [#7056](https://github.com/pydantic/pydantic/pull/7056) +* document `round_trip` in Json type documentation by [@jc-louis](https://github.com/jc-louis) in [#7137](https://github.com/pydantic/pydantic/pull/7137) +* Relax signature checks to better support builtins and C extension functions as validators by [@adriangb](https://github.com/adriangb) in [#7101](https://github.com/pydantic/pydantic/pull/7101) +* add union_mode='left_to_right' by [@davidhewitt](https://github.com/davidhewitt) in [#7151](https://github.com/pydantic/pydantic/pull/7151) +* Include an error message hint for inherited ordering by [@yvalencia91](https://github.com/yvalencia91) in [#7124](https://github.com/pydantic/pydantic/pull/7124) +* Fix one docs link and resolve some warnings for two others by [@dmontagu](https://github.com/dmontagu) in [#7153](https://github.com/pydantic/pydantic/pull/7153) +* Include Field extra keys name in warning by [@hramezani](https://github.com/hramezani) in [#7136](https://github.com/pydantic/pydantic/pull/7136) + +## v2.1.1 (2023-07-25) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.1.1) + +* Skip FieldInfo merging when unnecessary by [@dmontagu](https://github.com/dmontagu) in [#6862](https://github.com/pydantic/pydantic/pull/6862) + +## v2.1.0 (2023-07-25) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.1.0) + +* Add `StringConstraints` for use as Annotated metadata by [@adriangb](https://github.com/adriangb) in [#6605](https://github.com/pydantic/pydantic/pull/6605) +* Try to fix intermittently failing CI by [@adriangb](https://github.com/adriangb) in [#6683](https://github.com/pydantic/pydantic/pull/6683) +* Remove redundant example of optional vs default. by [@ehiggs-deliverect](https://github.com/ehiggs-deliverect) in [#6676](https://github.com/pydantic/pydantic/pull/6676) +* Docs update by [@samuelcolvin](https://github.com/samuelcolvin) in [#6692](https://github.com/pydantic/pydantic/pull/6692) +* Remove the Validate always section in validator docs by [@adriangb](https://github.com/adriangb) in [#6679](https://github.com/pydantic/pydantic/pull/6679) +* Fix recursion error in json schema generation by [@adriangb](https://github.com/adriangb) in [#6720](https://github.com/pydantic/pydantic/pull/6720) +* Fix incorrect subclass check for secretstr by [@AlexVndnblcke](https://github.com/AlexVndnblcke) in [#6730](https://github.com/pydantic/pydantic/pull/6730) +* update pdm / pdm lockfile to 2.8.0 by [@davidhewitt](https://github.com/davidhewitt) in [#6714](https://github.com/pydantic/pydantic/pull/6714) +* unpin pdm on more CI jobs by [@davidhewitt](https://github.com/davidhewitt) in [#6755](https://github.com/pydantic/pydantic/pull/6755) +* improve source locations for auxiliary packages in docs by [@davidhewitt](https://github.com/davidhewitt) in [#6749](https://github.com/pydantic/pydantic/pull/6749) +* Assume builtins don't accept an info argument by [@adriangb](https://github.com/adriangb) in [#6754](https://github.com/pydantic/pydantic/pull/6754) +* Fix bug where calling `help(BaseModelSubclass)` raises errors by [@hramezani](https://github.com/hramezani) in [#6758](https://github.com/pydantic/pydantic/pull/6758) +* Fix mypy plugin handling of `@model_validator(mode="after")` by [@ljodal](https://github.com/ljodal) in [#6753](https://github.com/pydantic/pydantic/pull/6753) +* update pydantic-core to 2.3.1 by [@davidhewitt](https://github.com/davidhewitt) in [#6756](https://github.com/pydantic/pydantic/pull/6756) +* Mypy plugin for settings by [@hramezani](https://github.com/hramezani) in [#6760](https://github.com/pydantic/pydantic/pull/6760) +* Use `contentSchema` keyword for JSON schema by [@dmontagu](https://github.com/dmontagu) in [#6715](https://github.com/pydantic/pydantic/pull/6715) +* fast-path checking finite decimals by [@davidhewitt](https://github.com/davidhewitt) in [#6769](https://github.com/pydantic/pydantic/pull/6769) +* Docs update by [@samuelcolvin](https://github.com/samuelcolvin) in [#6771](https://github.com/pydantic/pydantic/pull/6771) +* Improve json schema doc by [@hramezani](https://github.com/hramezani) in [#6772](https://github.com/pydantic/pydantic/pull/6772) +* Update validator docs by [@adriangb](https://github.com/adriangb) in [#6695](https://github.com/pydantic/pydantic/pull/6695) +* Fix typehint for wrap validator by [@dmontagu](https://github.com/dmontagu) in [#6788](https://github.com/pydantic/pydantic/pull/6788) +* 🐛 Fix validation warning for unions of Literal and other type by [@lig](https://github.com/lig) in [#6628](https://github.com/pydantic/pydantic/pull/6628) +* Update documentation for generics support in V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6685](https://github.com/pydantic/pydantic/pull/6685) +* add pydantic-core build info to `version_info()` by [@samuelcolvin](https://github.com/samuelcolvin) in [#6785](https://github.com/pydantic/pydantic/pull/6785) +* Fix pydantic dataclasses that use slots with default values by [@dmontagu](https://github.com/dmontagu) in [#6796](https://github.com/pydantic/pydantic/pull/6796) +* Fix inheritance of hash function for frozen models by [@dmontagu](https://github.com/dmontagu) in [#6789](https://github.com/pydantic/pydantic/pull/6789) +* ✨ Add `SkipJsonSchema` annotation by [@Kludex](https://github.com/Kludex) in [#6653](https://github.com/pydantic/pydantic/pull/6653) +* Error if an invalid field name is used with Field by [@dmontagu](https://github.com/dmontagu) in [#6797](https://github.com/pydantic/pydantic/pull/6797) +* Add `GenericModel` to `MOVED_IN_V2` by [@adriangb](https://github.com/adriangb) in [#6776](https://github.com/pydantic/pydantic/pull/6776) +* Remove unused code from `docs/usage/types/custom.md` by [@hramezani](https://github.com/hramezani) in [#6803](https://github.com/pydantic/pydantic/pull/6803) +* Fix `float` -> `Decimal` coercion precision loss by [@adriangb](https://github.com/adriangb) in [#6810](https://github.com/pydantic/pydantic/pull/6810) +* remove email validation from the north star benchmark by [@davidhewitt](https://github.com/davidhewitt) in [#6816](https://github.com/pydantic/pydantic/pull/6816) +* Fix link to mypy by [@progsmile](https://github.com/progsmile) in [#6824](https://github.com/pydantic/pydantic/pull/6824) +* Improve initialization hooks example by [@hramezani](https://github.com/hramezani) in [#6822](https://github.com/pydantic/pydantic/pull/6822) +* Fix default port for mongosrv DSNs by [@dmontagu](https://github.com/dmontagu) in [#6827](https://github.com/pydantic/pydantic/pull/6827) +* Improve API documentation, in particular more links between usage and API docs by [@samuelcolvin](https://github.com/samuelcolvin) in [#6780](https://github.com/pydantic/pydantic/pull/6780) +* update pydantic-core to 2.4.0 by [@davidhewitt](https://github.com/davidhewitt) in [#6831](https://github.com/pydantic/pydantic/pull/6831) +* Fix `annotated_types.MaxLen` validator for custom sequence types by [@ImogenBits](https://github.com/ImogenBits) in [#6809](https://github.com/pydantic/pydantic/pull/6809) +* Update V1 by [@hramezani](https://github.com/hramezani) in [#6833](https://github.com/pydantic/pydantic/pull/6833) +* Make it so callable JSON schema extra works by [@dmontagu](https://github.com/dmontagu) in [#6798](https://github.com/pydantic/pydantic/pull/6798) +* Fix serialization issue with `InstanceOf` by [@dmontagu](https://github.com/dmontagu) in [#6829](https://github.com/pydantic/pydantic/pull/6829) +* Add back support for `json_encoders` by [@adriangb](https://github.com/adriangb) in [#6811](https://github.com/pydantic/pydantic/pull/6811) +* Update field annotations when building the schema by [@dmontagu](https://github.com/dmontagu) in [#6838](https://github.com/pydantic/pydantic/pull/6838) +* Use `WeakValueDictionary` to fix generic memory leak by [@dmontagu](https://github.com/dmontagu) in [#6681](https://github.com/pydantic/pydantic/pull/6681) +* Add `config.defer_build` to optionally make model building lazy by [@samuelcolvin](https://github.com/samuelcolvin) in [#6823](https://github.com/pydantic/pydantic/pull/6823) +* delegate `UUID` serialization to pydantic-core by [@davidhewitt](https://github.com/davidhewitt) in [#6850](https://github.com/pydantic/pydantic/pull/6850) +* Update `json_encoders` docs by [@adriangb](https://github.com/adriangb) in [#6848](https://github.com/pydantic/pydantic/pull/6848) +* Fix error message for `staticmethod`/`classmethod` order with validate_call by [@dmontagu](https://github.com/dmontagu) in [#6686](https://github.com/pydantic/pydantic/pull/6686) +* Improve documentation for `Config` by [@samuelcolvin](https://github.com/samuelcolvin) in [#6847](https://github.com/pydantic/pydantic/pull/6847) +* Update serialization doc to mention `Field.exclude` takes priority over call-time `include/exclude` by [@hramezani](https://github.com/hramezani) in [#6851](https://github.com/pydantic/pydantic/pull/6851) +* Allow customizing core schema generation by making `GenerateSchema` public by [@adriangb](https://github.com/adriangb) in [#6737](https://github.com/pydantic/pydantic/pull/6737) + +## v2.0.3 (2023-07-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.3) + +* Mention PyObject (v1) moving to ImportString (v2) in migration doc by [@slafs](https://github.com/slafs) in [#6456](https://github.com/pydantic/pydantic/pull/6456) +* Fix release-tweet CI by [@Kludex](https://github.com/Kludex) in [#6461](https://github.com/pydantic/pydantic/pull/6461) +* Revise the section on required / optional / nullable fields. by [@ybressler](https://github.com/ybressler) in [#6468](https://github.com/pydantic/pydantic/pull/6468) +* Warn if a type hint is not in fact a type by [@adriangb](https://github.com/adriangb) in [#6479](https://github.com/pydantic/pydantic/pull/6479) +* Replace TransformSchema with GetPydanticSchema by [@dmontagu](https://github.com/dmontagu) in [#6484](https://github.com/pydantic/pydantic/pull/6484) +* Fix the un-hashability of various annotation types, for use in caching generic containers by [@dmontagu](https://github.com/dmontagu) in [#6480](https://github.com/pydantic/pydantic/pull/6480) +* PYD-164: Rework custom types docs by [@adriangb](https://github.com/adriangb) in [#6490](https://github.com/pydantic/pydantic/pull/6490) +* Fix ci by [@adriangb](https://github.com/adriangb) in [#6507](https://github.com/pydantic/pydantic/pull/6507) +* Fix forward ref in generic by [@adriangb](https://github.com/adriangb) in [#6511](https://github.com/pydantic/pydantic/pull/6511) +* Fix generation of serialization JSON schemas for core_schema.ChainSchema by [@dmontagu](https://github.com/dmontagu) in [#6515](https://github.com/pydantic/pydantic/pull/6515) +* Document the change in `Field.alias` behavior in Pydantic V2 by [@hramezani](https://github.com/hramezani) in [#6508](https://github.com/pydantic/pydantic/pull/6508) +* Give better error message attempting to compute the json schema of a model with undefined fields by [@dmontagu](https://github.com/dmontagu) in [#6519](https://github.com/pydantic/pydantic/pull/6519) +* Document `alias_priority` by [@tpdorsey](https://github.com/tpdorsey) in [#6520](https://github.com/pydantic/pydantic/pull/6520) +* Add redirect for types documentation by [@tpdorsey](https://github.com/tpdorsey) in [#6513](https://github.com/pydantic/pydantic/pull/6513) +* Allow updating docs without release by [@samuelcolvin](https://github.com/samuelcolvin) in [#6551](https://github.com/pydantic/pydantic/pull/6551) +* Ensure docs tests always run in the right folder by [@dmontagu](https://github.com/dmontagu) in [#6487](https://github.com/pydantic/pydantic/pull/6487) +* Defer evaluation of return type hints for serializer functions by [@dmontagu](https://github.com/dmontagu) in [#6516](https://github.com/pydantic/pydantic/pull/6516) +* Disable E501 from Ruff and rely on just Black by [@adriangb](https://github.com/adriangb) in [#6552](https://github.com/pydantic/pydantic/pull/6552) +* Update JSON Schema documentation for V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6492](https://github.com/pydantic/pydantic/pull/6492) +* Add documentation of cyclic reference handling by [@dmontagu](https://github.com/dmontagu) in [#6493](https://github.com/pydantic/pydantic/pull/6493) +* Remove the need for change files by [@samuelcolvin](https://github.com/samuelcolvin) in [#6556](https://github.com/pydantic/pydantic/pull/6556) +* add "north star" benchmark by [@davidhewitt](https://github.com/davidhewitt) in [#6547](https://github.com/pydantic/pydantic/pull/6547) +* Update Dataclasses docs by [@tpdorsey](https://github.com/tpdorsey) in [#6470](https://github.com/pydantic/pydantic/pull/6470) +* ♻️ Use different error message on v1 redirects by [@Kludex](https://github.com/Kludex) in [#6595](https://github.com/pydantic/pydantic/pull/6595) +* ⬆ Upgrade `pydantic-core` to v2.2.0 by [@lig](https://github.com/lig) in [#6589](https://github.com/pydantic/pydantic/pull/6589) +* Fix serialization for IPvAny by [@dmontagu](https://github.com/dmontagu) in [#6572](https://github.com/pydantic/pydantic/pull/6572) +* Improve CI by using PDM instead of pip to install typing-extensions by [@adriangb](https://github.com/adriangb) in [#6602](https://github.com/pydantic/pydantic/pull/6602) +* Add `enum` error type docs by [@lig](https://github.com/lig) in [#6603](https://github.com/pydantic/pydantic/pull/6603) +* 🐛 Fix `max_length` for unicode strings by [@lig](https://github.com/lig) in [#6559](https://github.com/pydantic/pydantic/pull/6559) +* Add documentation for accessing features via `pydantic.v1` by [@tpdorsey](https://github.com/tpdorsey) in [#6604](https://github.com/pydantic/pydantic/pull/6604) +* Include extra when iterating over a model by [@adriangb](https://github.com/adriangb) in [#6562](https://github.com/pydantic/pydantic/pull/6562) +* Fix typing of model_validator by [@adriangb](https://github.com/adriangb) in [#6514](https://github.com/pydantic/pydantic/pull/6514) +* Touch up Decimal validator by [@adriangb](https://github.com/adriangb) in [#6327](https://github.com/pydantic/pydantic/pull/6327) +* Fix various docstrings using fixed pytest-examples by [@dmontagu](https://github.com/dmontagu) in [#6607](https://github.com/pydantic/pydantic/pull/6607) +* Handle function validators in a discriminated union by [@dmontagu](https://github.com/dmontagu) in [#6570](https://github.com/pydantic/pydantic/pull/6570) +* Review json_schema.md by [@tpdorsey](https://github.com/tpdorsey) in [#6608](https://github.com/pydantic/pydantic/pull/6608) +* Make validate_call work on basemodel methods by [@dmontagu](https://github.com/dmontagu) in [#6569](https://github.com/pydantic/pydantic/pull/6569) +* add test for big int json serde by [@davidhewitt](https://github.com/davidhewitt) in [#6614](https://github.com/pydantic/pydantic/pull/6614) +* Fix pydantic dataclass problem with dataclasses.field default_factory by [@hramezani](https://github.com/hramezani) in [#6616](https://github.com/pydantic/pydantic/pull/6616) +* Fixed mypy type inference for TypeAdapter by [@zakstucke](https://github.com/zakstucke) in [#6617](https://github.com/pydantic/pydantic/pull/6617) +* Make it work to use None as a generic parameter by [@dmontagu](https://github.com/dmontagu) in [#6609](https://github.com/pydantic/pydantic/pull/6609) +* Make it work to use `$ref` as an alias by [@dmontagu](https://github.com/dmontagu) in [#6568](https://github.com/pydantic/pydantic/pull/6568) +* add note to migration guide about changes to `AnyUrl` etc by [@davidhewitt](https://github.com/davidhewitt) in [#6618](https://github.com/pydantic/pydantic/pull/6618) +* 🐛 Support defining `json_schema_extra` on `RootModel` using `Field` by [@lig](https://github.com/lig) in [#6622](https://github.com/pydantic/pydantic/pull/6622) +* Update pre-commit to prevent commits to main branch on accident by [@dmontagu](https://github.com/dmontagu) in [#6636](https://github.com/pydantic/pydantic/pull/6636) +* Fix PDM CI for python 3.7 on MacOS/windows by [@dmontagu](https://github.com/dmontagu) in [#6627](https://github.com/pydantic/pydantic/pull/6627) +* Produce more accurate signatures for pydantic dataclasses by [@dmontagu](https://github.com/dmontagu) in [#6633](https://github.com/pydantic/pydantic/pull/6633) +* Updates to Url types for Pydantic V2 by [@tpdorsey](https://github.com/tpdorsey) in [#6638](https://github.com/pydantic/pydantic/pull/6638) +* Fix list markdown in `transform` docstring by [@StefanBRas](https://github.com/StefanBRas) in [#6649](https://github.com/pydantic/pydantic/pull/6649) +* simplify slots_dataclass construction to appease mypy by [@davidhewitt](https://github.com/davidhewitt) in [#6639](https://github.com/pydantic/pydantic/pull/6639) +* Update TypedDict schema generation docstring by [@adriangb](https://github.com/adriangb) in [#6651](https://github.com/pydantic/pydantic/pull/6651) +* Detect and lint-error for prints by [@dmontagu](https://github.com/dmontagu) in [#6655](https://github.com/pydantic/pydantic/pull/6655) +* Add xfailing test for pydantic-core PR 766 by [@dmontagu](https://github.com/dmontagu) in [#6641](https://github.com/pydantic/pydantic/pull/6641) +* Ignore unrecognized fields from dataclasses metadata by [@dmontagu](https://github.com/dmontagu) in [#6634](https://github.com/pydantic/pydantic/pull/6634) +* Make non-existent class getattr a mypy error by [@dmontagu](https://github.com/dmontagu) in [#6658](https://github.com/pydantic/pydantic/pull/6658) +* Update pydantic-core to 2.3.0 by [@hramezani](https://github.com/hramezani) in [#6648](https://github.com/pydantic/pydantic/pull/6648) +* Use OrderedDict from typing_extensions by [@dmontagu](https://github.com/dmontagu) in [#6664](https://github.com/pydantic/pydantic/pull/6664) +* Fix typehint for JSON schema extra callable by [@dmontagu](https://github.com/dmontagu) in [#6659](https://github.com/pydantic/pydantic/pull/6659) + +## v2.0.2 (2023-07-05) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.2) + +* Fix bug where round-trip pickling/unpickling a `RootModel` would change the value of `__dict__`, [#6457](https://github.com/pydantic/pydantic/pull/6457) by [@dmontagu](https://github.com/dmontagu) +* Allow single-item discriminated unions, [#6405](https://github.com/pydantic/pydantic/pull/6405) by [@dmontagu](https://github.com/dmontagu) +* Fix issue with union parsing of enums, [#6440](https://github.com/pydantic/pydantic/pull/6440) by [@dmontagu](https://github.com/dmontagu) +* Docs: Fixed `constr` documentation, renamed old `regex` to new `pattern`, [#6452](https://github.com/pydantic/pydantic/pull/6452) by [@miili](https://github.com/miili) +* Change `GenerateJsonSchema.generate_definitions` signature, [#6436](https://github.com/pydantic/pydantic/pull/6436) by [@dmontagu](https://github.com/dmontagu) + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0.2) + +## v2.0.1 (2023-07-04) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0.1) + +First patch release of Pydantic V2 + +* Extra fields added via `setattr` (i.e. `m.some_extra_field = 'extra_value'`) + are added to `.model_extra` if `model_config` `extra='allowed'`. Fixed [#6333](https://github.com/pydantic/pydantic/pull/6333), [#6365](https://github.com/pydantic/pydantic/pull/6365) by [@aaraney](https://github.com/aaraney) +* Automatically unpack JSON schema '$ref' for custom types, [#6343](https://github.com/pydantic/pydantic/pull/6343) by [@adriangb](https://github.com/adriangb) +* Fix tagged unions multiple processing in submodels, [#6340](https://github.com/pydantic/pydantic/pull/6340) by [@suharnikov](https://github.com/suharnikov) + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0.1) + +## v2.0 (2023-06-30) + +[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.0) + +Pydantic V2 is here! :tada: + +See [this post](https://docs.pydantic.dev/2.0/blog/pydantic-v2-final/) for more details. + +## v2.0b3 (2023-06-16) + +Third beta pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b3) + +## v2.0b2 (2023-06-03) + +Add `from_attributes` runtime flag to `TypeAdapter.validate_python` and `BaseModel.model_validate`. + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b2) + +## v2.0b1 (2023-06-01) + +First beta pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0b1) + +## v2.0a4 (2023-05-05) + +Fourth pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a4) + +## v2.0a3 (2023-04-20) + +Third pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a3) + +## v2.0a2 (2023-04-12) + +Second pre-release of Pydantic V2 + +See the full changelog [here](https://github.com/pydantic/pydantic/releases/tag/v2.0a2) + +## v2.0a1 (2023-04-03) + +First pre-release of Pydantic V2! + +See [this post](https://docs.pydantic.dev/blog/pydantic-v2-alpha/) for more details. + + +... see [here](https://docs.pydantic.dev/changelog/#v0322-2019-08-17) for earlier changes. diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/RECORD new file mode 100644 index 0000000..6e0349c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/RECORD @@ -0,0 +1,205 @@ +pydantic-2.7.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pydantic-2.7.1.dist-info/METADATA,sha256=TdbA3g6cZ6igPWmjqjlrJItAzgEAZCQMLyMYyGQNl6Q,107323 +pydantic-2.7.1.dist-info/RECORD,, +pydantic-2.7.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87 +pydantic-2.7.1.dist-info/licenses/LICENSE,sha256=qeGG88oWte74QxjnpwFyE1GgDLe4rjpDlLZ7SeNSnvM,1129 +pydantic/__init__.py,sha256=Mrk6eFaVrhRyIY6HszHnWtkeyZ5lHeHSo73flEGSf20,13178 +pydantic/__pycache__/__init__.cpython-310.pyc,, +pydantic/__pycache__/_migration.cpython-310.pyc,, +pydantic/__pycache__/alias_generators.cpython-310.pyc,, +pydantic/__pycache__/aliases.cpython-310.pyc,, +pydantic/__pycache__/annotated_handlers.cpython-310.pyc,, +pydantic/__pycache__/class_validators.cpython-310.pyc,, +pydantic/__pycache__/color.cpython-310.pyc,, +pydantic/__pycache__/config.cpython-310.pyc,, +pydantic/__pycache__/dataclasses.cpython-310.pyc,, +pydantic/__pycache__/datetime_parse.cpython-310.pyc,, +pydantic/__pycache__/decorator.cpython-310.pyc,, +pydantic/__pycache__/env_settings.cpython-310.pyc,, +pydantic/__pycache__/error_wrappers.cpython-310.pyc,, +pydantic/__pycache__/errors.cpython-310.pyc,, +pydantic/__pycache__/fields.cpython-310.pyc,, +pydantic/__pycache__/functional_serializers.cpython-310.pyc,, +pydantic/__pycache__/functional_validators.cpython-310.pyc,, +pydantic/__pycache__/generics.cpython-310.pyc,, +pydantic/__pycache__/json.cpython-310.pyc,, +pydantic/__pycache__/json_schema.cpython-310.pyc,, +pydantic/__pycache__/main.cpython-310.pyc,, +pydantic/__pycache__/mypy.cpython-310.pyc,, +pydantic/__pycache__/networks.cpython-310.pyc,, +pydantic/__pycache__/parse.cpython-310.pyc,, +pydantic/__pycache__/root_model.cpython-310.pyc,, +pydantic/__pycache__/schema.cpython-310.pyc,, +pydantic/__pycache__/tools.cpython-310.pyc,, +pydantic/__pycache__/type_adapter.cpython-310.pyc,, +pydantic/__pycache__/types.cpython-310.pyc,, +pydantic/__pycache__/typing.cpython-310.pyc,, +pydantic/__pycache__/utils.cpython-310.pyc,, +pydantic/__pycache__/validate_call_decorator.cpython-310.pyc,, +pydantic/__pycache__/validators.cpython-310.pyc,, +pydantic/__pycache__/version.cpython-310.pyc,, +pydantic/__pycache__/warnings.cpython-310.pyc,, +pydantic/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pydantic/_internal/__pycache__/__init__.cpython-310.pyc,, +pydantic/_internal/__pycache__/_config.cpython-310.pyc,, +pydantic/_internal/__pycache__/_core_metadata.cpython-310.pyc,, +pydantic/_internal/__pycache__/_core_utils.cpython-310.pyc,, +pydantic/_internal/__pycache__/_dataclasses.cpython-310.pyc,, +pydantic/_internal/__pycache__/_decorators.cpython-310.pyc,, +pydantic/_internal/__pycache__/_decorators_v1.cpython-310.pyc,, +pydantic/_internal/__pycache__/_discriminated_union.cpython-310.pyc,, +pydantic/_internal/__pycache__/_docs_extraction.cpython-310.pyc,, +pydantic/_internal/__pycache__/_fields.cpython-310.pyc,, +pydantic/_internal/__pycache__/_forward_ref.cpython-310.pyc,, +pydantic/_internal/__pycache__/_generate_schema.cpython-310.pyc,, +pydantic/_internal/__pycache__/_generics.cpython-310.pyc,, +pydantic/_internal/__pycache__/_git.cpython-310.pyc,, +pydantic/_internal/__pycache__/_internal_dataclass.cpython-310.pyc,, +pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-310.pyc,, +pydantic/_internal/__pycache__/_mock_val_ser.cpython-310.pyc,, +pydantic/_internal/__pycache__/_model_construction.cpython-310.pyc,, +pydantic/_internal/__pycache__/_repr.cpython-310.pyc,, +pydantic/_internal/__pycache__/_schema_generation_shared.cpython-310.pyc,, +pydantic/_internal/__pycache__/_signature.cpython-310.pyc,, +pydantic/_internal/__pycache__/_std_types_schema.cpython-310.pyc,, +pydantic/_internal/__pycache__/_typing_extra.cpython-310.pyc,, +pydantic/_internal/__pycache__/_utils.cpython-310.pyc,, +pydantic/_internal/__pycache__/_validate_call.cpython-310.pyc,, +pydantic/_internal/__pycache__/_validators.cpython-310.pyc,, +pydantic/_internal/_config.py,sha256=Qx4Oz0mTEYFXGk5dWApTu_5BdJ14DiZIEKKTTzfa9Ik,12208 +pydantic/_internal/_core_metadata.py,sha256=Da-e0-DXK__dJvog0e8CZLQ4r_k9RpldG6KQTGrYlHg,3521 +pydantic/_internal/_core_utils.py,sha256=6aD8J26n4_tP2R-Sermk66DGwHpFpksOrwHdI0O22jg,24268 +pydantic/_internal/_dataclasses.py,sha256=i5kyUZhCH7gxxIAX7l0F8LR7pLgeCGsBH-3KLnRcoYw,8679 +pydantic/_internal/_decorators.py,sha256=h8mGrgWqnjdOEawkvBd1sDf_umOELsQuIzwGn1dmwbs,30623 +pydantic/_internal/_decorators_v1.py,sha256=_m9TskhZh9yPUn7Jmy3KbKa3UDREQWyMm5NXyOJM3R8,6266 +pydantic/_internal/_discriminated_union.py,sha256=XDzlfKc7LAkJJErQWAZ4EuP_G_CQXJSVFKtwuHRJEmg,26435 +pydantic/_internal/_docs_extraction.py,sha256=_E7w2_wnTXmglMBJIXy9X3BLPa90pCTWR7tcSB3vnbw,3790 +pydantic/_internal/_fields.py,sha256=Ucbc8WzZ2n9WhGLmNO3xmA1oKR3UtVCWexhf65yKZts,14184 +pydantic/_internal/_forward_ref.py,sha256=5n3Y7-3AKLn8_FS3Yc7KutLiPUhyXmAtkEZOaFnonwM,611 +pydantic/_internal/_generate_schema.py,sha256=AX9-pHItmgVMxLhCRk7qx-YAkSFp1ywPZMsShDbkG4M,101871 +pydantic/_internal/_generics.py,sha256=NvLBQkoZpYHud3ofzoKKf5EmPiI4nVQQRHUeDZb0-TQ,22218 +pydantic/_internal/_git.py,sha256=7SAsfMSM4PXvxEWmxzRt1iGwWMhAlKbKpqFbGxHzBro,783 +pydantic/_internal/_internal_dataclass.py,sha256=NswLpapJY_61NFHBAXYpgFdxMmIX_yE9ttx_pQt_Vp8,207 +pydantic/_internal/_known_annotated_metadata.py,sha256=z5JmcRyfMpmAM7VSSAZ4kaNU12ej9yNZLW0HqyHqkuw,17271 +pydantic/_internal/_mock_val_ser.py,sha256=5DqrtofFw4wUmZWNky6zaFwyCAbjTTD9XQc8FK2JzKc,5180 +pydantic/_internal/_model_construction.py,sha256=v6WPfyrbpNE7-Gx4B4Z1owS_Mmi44Ql-04SwqJUflfc,31789 +pydantic/_internal/_repr.py,sha256=7F_-smiOWEUPREAnVtmq0kNDIoVYXDgBrPpazmnzfG0,4568 +pydantic/_internal/_schema_generation_shared.py,sha256=vOhnCnpyWYv-_h8861ZUn8_5RtJWHMBfSYBMwfje6Hc,4852 +pydantic/_internal/_signature.py,sha256=gJvpQ9vklQuYfYKI4d0zvHOGhCR44Oa_xltdMUMg59g,6293 +pydantic/_internal/_std_types_schema.py,sha256=xPvG4vSxNY4I9nytK6On3nybRHd7MRxWjT_Unw1txbY,28696 +pydantic/_internal/_typing_extra.py,sha256=mBYOMtlPrWtcZYgJRKdSnGsT78NPgExns0pkoUzT4vQ,18863 +pydantic/_internal/_utils.py,sha256=oFdfNA3Q4vZqjFPlarcCE4eEPLv_tySvdhQvg3fhltk,12668 +pydantic/_internal/_validate_call.py,sha256=pePVGIJ_G2ysBFwOHxe3C1CbIIR5CmbanmNk5EhZkfE,3226 +pydantic/_internal/_validators.py,sha256=1WqylJK7FWaTZwoDgX82r4j-nCOcURLUcfaFr1PbPlU,10508 +pydantic/_migration.py,sha256=j6TbRpJofjAX8lr-k2nVnQcBR9RD2B91I7Ulcw_ZzEo,11913 +pydantic/alias_generators.py,sha256=Q-L6PE-67qI35Zq-Fx3sgGFIn4luDv_arYI7by35XZA,1600 +pydantic/aliases.py,sha256=WjU9EQjJWOkNQObX4mwGRHfy4GIAE6oJEbGBppIDhsU,4818 +pydantic/annotated_handlers.py,sha256=1LAjZImtk1cjyQyc1CJPuJTt0_uvqkfTgDTu1qqc518,4352 +pydantic/class_validators.py,sha256=iQz1Tw8FBliqEapmzB7iLkbwkJAeAx5314Vksb_Kj0g,147 +pydantic/color.py,sha256=Pq4DAe1HgmbhKlrZ5kbal23npquKd9c0RPwPPCS_OYM,21493 +pydantic/config.py,sha256=-jA6d8nWdHD2PewBdDfiHVnUbBjsWfgOfEfNradVFb0,32206 +pydantic/dataclasses.py,sha256=6GFbUp4sxSHoEkHjXcKoP9uzzbsmXveK64Nr7KbaQIE,13450 +pydantic/datetime_parse.py,sha256=5lJpo3-iBTAA9YmMuDLglP-5f2k8etayAXjEi6rfEN0,149 +pydantic/decorator.py,sha256=Qqx1UU19tpRVp05a2NIK5OdpLXN_a84HZPMjt_5BxdE,144 +pydantic/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pydantic/deprecated/__pycache__/__init__.cpython-310.pyc,, +pydantic/deprecated/__pycache__/class_validators.cpython-310.pyc,, +pydantic/deprecated/__pycache__/config.cpython-310.pyc,, +pydantic/deprecated/__pycache__/copy_internals.cpython-310.pyc,, +pydantic/deprecated/__pycache__/decorator.cpython-310.pyc,, +pydantic/deprecated/__pycache__/json.cpython-310.pyc,, +pydantic/deprecated/__pycache__/parse.cpython-310.pyc,, +pydantic/deprecated/__pycache__/tools.cpython-310.pyc,, +pydantic/deprecated/class_validators.py,sha256=6Psv1LmdOQUdRHXj49rvf_Mfa4zZsoE4OAmtHp5o5bE,10338 +pydantic/deprecated/config.py,sha256=eKhnG--ZQtJ4A7KA3xeF76E15-4pVau3B5T8D39ptFs,2663 +pydantic/deprecated/copy_internals.py,sha256=SoUj1MevXt3fnloqNg5wivSUHSDPnuSj_YydzkEMzu0,7595 +pydantic/deprecated/decorator.py,sha256=2LFdSWzXSVfpDzsM7V4LN3jwTkr8uM8DZF-SitgUV58,10778 +pydantic/deprecated/json.py,sha256=JDkKtGzCnRj9MQ_EkCGTU1u5gQQr2PPTEt_E3v9LhZQ,4580 +pydantic/deprecated/parse.py,sha256=Gzd6b_g8zJXcuE7QRq5adhx_EMJahXfcpXCF0RgrqqI,2511 +pydantic/deprecated/tools.py,sha256=XUoIW9W4sgOUWQ6Xzf-Z_NukUC1l_yUwz2_n0fE3MEI,3336 +pydantic/env_settings.py,sha256=quxt8c9TioRg-u74gTW-GrK6r5mFXmn-J5H8FAC9Prc,147 +pydantic/error_wrappers.py,sha256=u9Dz8RgawIw8-rx7G7WGZoRtGptHXyXhHxiN9PbQ58g,149 +pydantic/errors.py,sha256=cQO-I-JKRSsvNxDiXsdBWDkt8ENqp_H4pJVhK-lKC_U,4780 +pydantic/fields.py,sha256=GwVaS_dcu6Z-bvrh8veCzdqBkXg5WR8hFfXz1E0t11U,49922 +pydantic/functional_serializers.py,sha256=K34I1vlHQchjUWCyE2Dm8QfDv0glgboD8OLpHcGmyMg,14635 +pydantic/functional_validators.py,sha256=e8ms_ug89MurgyBnP_sd4xuAuUt3HmX34cn7gk9PESk,24322 +pydantic/generics.py,sha256=T1UIBvpgur_28EIcR9Dc_Wo2r9yntzqdcR-NbnOLXB8,143 +pydantic/json.py,sha256=qk9fHVGWKNrvE-v2WxWLEm66t81JKttbySd9zjy0dnc,139 +pydantic/json_schema.py,sha256=Plr85cBW7LF4da5SQiqvvUraGQbIOCPb9d0c_9Ix0tQ,105243 +pydantic/main.py,sha256=cSbM9n5yNd0e2XKJexNV4J2mbdsTDOdHRhBaTd6uTJg,69938 +pydantic/mypy.py,sha256=Qz7jHt81JqPHO6B4xxgdhIa_EXbDQ9YyJzYBI_EF9zw,55574 +pydantic/networks.py,sha256=MGRk73D3zKpR_e8rIxIYPq3NjuMdIXLSH5jBljpkkjM,22461 +pydantic/parse.py,sha256=BNo_W_gp1xR7kohYdHjF2m_5UNYFQxUt487-NR0RiK8,140 +pydantic/plugin/__init__.py,sha256=-7bMvAKCbNYQI6-inaTvSHmKmnNFCTgxlvqWXzeZ0oM,6115 +pydantic/plugin/__pycache__/__init__.cpython-310.pyc,, +pydantic/plugin/__pycache__/_loader.cpython-310.pyc,, +pydantic/plugin/__pycache__/_schema_validator.cpython-310.pyc,, +pydantic/plugin/_loader.py,sha256=rmLbIwThDmVR1JwFVi_XvrLH7b1A5teMED-O3pr6Gk4,2140 +pydantic/plugin/_schema_validator.py,sha256=3eVp5-4IsIHEQrsCsh34oPf2bNyMJTVh3nGAH7IRC1M,5228 +pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pydantic/root_model.py,sha256=htGyAIBVbx_VS9AbHeEe_Vu0wNQEd03n9yooCYdiu8g,6188 +pydantic/schema.py,sha256=EkbomWuaAdv7C3V8h6xxoT4uJKy3Mwvkg064tOUbvxg,141 +pydantic/tools.py,sha256=YB4vzOx4g7reKUM_s5oTXIGxC5LGBnGsXdVICSRuh7g,140 +pydantic/type_adapter.py,sha256=9JqyeNs3FaW4Nr_Ec6HizmFMcEAbDrsTKQ8q7OWekd0,18834 +pydantic/types.py,sha256=3S0z-SPHYatgMQKdBh51XvEnH07avyv0msR3TrgGzTg,94034 +pydantic/typing.py,sha256=sPkx0hi_RX7qSV3BB0zzHd8ZuAKbRRI37XJI4av_HzQ,137 +pydantic/utils.py,sha256=twRV5SqiguiCrOA9GvrKvOG-TThfWYb7mEXDVXFZp2s,140 +pydantic/v1/__init__.py,sha256=iTu8CwWWvn6zM_zYJtqhie24PImW25zokitz_06kDYw,2771 +pydantic/v1/__pycache__/__init__.cpython-310.pyc,, +pydantic/v1/__pycache__/_hypothesis_plugin.cpython-310.pyc,, +pydantic/v1/__pycache__/annotated_types.cpython-310.pyc,, +pydantic/v1/__pycache__/class_validators.cpython-310.pyc,, +pydantic/v1/__pycache__/color.cpython-310.pyc,, +pydantic/v1/__pycache__/config.cpython-310.pyc,, +pydantic/v1/__pycache__/dataclasses.cpython-310.pyc,, +pydantic/v1/__pycache__/datetime_parse.cpython-310.pyc,, +pydantic/v1/__pycache__/decorator.cpython-310.pyc,, +pydantic/v1/__pycache__/env_settings.cpython-310.pyc,, +pydantic/v1/__pycache__/error_wrappers.cpython-310.pyc,, +pydantic/v1/__pycache__/errors.cpython-310.pyc,, +pydantic/v1/__pycache__/fields.cpython-310.pyc,, +pydantic/v1/__pycache__/generics.cpython-310.pyc,, +pydantic/v1/__pycache__/json.cpython-310.pyc,, +pydantic/v1/__pycache__/main.cpython-310.pyc,, +pydantic/v1/__pycache__/mypy.cpython-310.pyc,, +pydantic/v1/__pycache__/networks.cpython-310.pyc,, +pydantic/v1/__pycache__/parse.cpython-310.pyc,, +pydantic/v1/__pycache__/schema.cpython-310.pyc,, +pydantic/v1/__pycache__/tools.cpython-310.pyc,, +pydantic/v1/__pycache__/types.cpython-310.pyc,, +pydantic/v1/__pycache__/typing.cpython-310.pyc,, +pydantic/v1/__pycache__/utils.cpython-310.pyc,, +pydantic/v1/__pycache__/v1.cpython-310.pyc,, +pydantic/v1/__pycache__/validators.cpython-310.pyc,, +pydantic/v1/__pycache__/version.cpython-310.pyc,, +pydantic/v1/_hypothesis_plugin.py,sha256=gILcyAEfZ3u9YfKxtDxkReLpakjMou1VWC3FEcXmJgQ,14844 +pydantic/v1/annotated_types.py,sha256=dJTDUyPj4QJj4rDcNkt9xDUMGEkAnuWzDeGE2q7Wxrc,3124 +pydantic/v1/class_validators.py,sha256=0BZx0Ft19cREVHEOaA6wf_E3A0bTL4wQIGzeOinVatg,14595 +pydantic/v1/color.py,sha256=cGzck7kSD5beBkOMhda4bfTICput6dMx8GGpEU5SK5Y,16811 +pydantic/v1/config.py,sha256=h5ceeZ9HzDjUv0IZNYQoza0aNGFVo22iszY-6s0a3eM,6477 +pydantic/v1/dataclasses.py,sha256=dO6Slbjc6_PZKi2ZFhDkcmCL1VSPoX44sV-2FBIajRQ,18073 +pydantic/v1/datetime_parse.py,sha256=DhGfkbG4Vs5Oyxq3u8jM-7gFrbuUKsn-4aG2DJDJbHw,7714 +pydantic/v1/decorator.py,sha256=wzuIuKKHVjaiE97YBctCU0Vho0VRlUO-aVu1IUEczFE,10263 +pydantic/v1/env_settings.py,sha256=4PWxPYeK5jt59JJ4QGb90qU8pfC7qgGX44UESTmXdpE,14039 +pydantic/v1/error_wrappers.py,sha256=xm128iOK5r0-P-oP0XWq2EqMEfEyjhQlj2aBpECytNc,5141 +pydantic/v1/errors.py,sha256=f93z30S4s5bJEl8JXh-zFCAtLDCko9ze2hKTkOimaa8,17693 +pydantic/v1/fields.py,sha256=vNZItUcuW6kBhQNQW7wA4LgiZiza4dT2Q6Dx4QvoSgE,50485 +pydantic/v1/generics.py,sha256=n5TTgh3EHkG1Xw3eY9A143bUN11_4m57Db5u49hkGJ8,17805 +pydantic/v1/json.py,sha256=B0gJ2WmPqw-6fsvPmgu-rwhhOy4E0JpbbYjC8HR01Ho,3346 +pydantic/v1/main.py,sha256=5dcT68MxbI3WguS194pyQYUGXb3a3Ukxnq2jzoi_SXg,44376 +pydantic/v1/mypy.py,sha256=E2GLG18nXJQth9UIZhrrDDyfEkgJdVUjJsdNhqa29Xo,38737 +pydantic/v1/networks.py,sha256=TeV9FvCYg4ALk8j7dU1q6Ntze7yaUrCHQFEDJDnq1NI,22059 +pydantic/v1/parse.py,sha256=rrVhaWLK8t03rT3oxvC6uRLuTF5iZ2NKGvGqs4iQEM0,1810 +pydantic/v1/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pydantic/v1/schema.py,sha256=hj0y0U-ooqNnB6kBWg_ybZXOBIjAhI4PgucQYU0MVIc,47614 +pydantic/v1/tools.py,sha256=ELC66w6UaU_HzAGfJBSIP47Aq9ZGkGiWPMLkkTs6VrI,2826 +pydantic/v1/types.py,sha256=cjZCEd0AjkJcwmDKOP6DlAjQn4aGyBVZHBVWBKt0jI0,35379 +pydantic/v1/typing.py,sha256=5_C_fiUvWiAzW3MBJaHeuy2s3Hi52rFMxTfNPHv9_os,18996 +pydantic/v1/utils.py,sha256=5w7Q3N_Fqg5H9__JQDaumw9N3EFdlc7galEsCGxEDN0,25809 +pydantic/v1/v1.py,sha256=nw16mjPbyYfGLLdYDQ8DPG-nZJBs7GZF2ez2L9KVeO4,2299 +pydantic/v1/validators.py,sha256=T-t9y9L_68El9p4PYkEVGEjpetNV6luav8Iwu9iTLkM,21887 +pydantic/v1/version.py,sha256=rN-YMSHoE40ee3x7xyKta3ZSMlIp8Shu3_KTXTTWM8U,1039 +pydantic/validate_call_decorator.py,sha256=VV0MmEYBRfTVZJ4WATN9wl7LDuM3sS_zFB45h2M7vOw,2054 +pydantic/validators.py,sha256=3oPhHojp9UD3PdEZpMYMkxeLGUAabRm__zera8_T92w,145 +pydantic/version.py,sha256=SxlFfFpVhlr3hYZJRqFFEujX7jMmMMlIwxto06x3L-8,2441 +pydantic/warnings.py,sha256=lGHr0AuWA7bHRw68uV79fmRS1UxxIu-b6hNI2iyK-xU,2322 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/WHEEL new file mode 100644 index 0000000..516596c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.24.2 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/licenses/LICENSE b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000..488c626 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic-2.7.1.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 to present Pydantic Services Inc. and individual contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/__init__.py new file mode 100644 index 0000000..8880b4f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/__init__.py @@ -0,0 +1,400 @@ +import typing + +from ._migration import getattr_migration +from .version import VERSION + +if typing.TYPE_CHECKING: + # import of virtually everything is supported via `__getattr__` below, + # but we need them here for type checking and IDE support + import pydantic_core + from pydantic_core.core_schema import ( + FieldSerializationInfo, + SerializationInfo, + SerializerFunctionWrapHandler, + ValidationInfo, + ValidatorFunctionWrapHandler, + ) + + from . import dataclasses + from ._internal._generate_schema import GenerateSchema as GenerateSchema + from .aliases import AliasChoices, AliasGenerator, AliasPath + from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler + from .config import ConfigDict, with_config + from .errors import * + from .fields import Field, PrivateAttr, computed_field + from .functional_serializers import ( + PlainSerializer, + SerializeAsAny, + WrapSerializer, + field_serializer, + model_serializer, + ) + from .functional_validators import ( + AfterValidator, + BeforeValidator, + InstanceOf, + PlainValidator, + SkipValidation, + WrapValidator, + field_validator, + model_validator, + ) + from .json_schema import WithJsonSchema + from .main import * + from .networks import * + from .type_adapter import TypeAdapter + from .types import * + from .validate_call_decorator import validate_call + from .warnings import PydanticDeprecatedSince20, PydanticDeprecatedSince26, PydanticDeprecationWarning + + # this encourages pycharm to import `ValidationError` from here, not pydantic_core + ValidationError = pydantic_core.ValidationError + from .deprecated.class_validators import root_validator, validator + from .deprecated.config import BaseConfig, Extra + from .deprecated.tools import * + from .root_model import RootModel + +__version__ = VERSION +__all__ = ( + # dataclasses + 'dataclasses', + # functional validators + 'field_validator', + 'model_validator', + 'AfterValidator', + 'BeforeValidator', + 'PlainValidator', + 'WrapValidator', + 'SkipValidation', + 'InstanceOf', + # JSON Schema + 'WithJsonSchema', + # deprecated V1 functional validators, these are imported via `__getattr__` below + 'root_validator', + 'validator', + # functional serializers + 'field_serializer', + 'model_serializer', + 'PlainSerializer', + 'SerializeAsAny', + 'WrapSerializer', + # config + 'ConfigDict', + 'with_config', + # deprecated V1 config, these are imported via `__getattr__` below + 'BaseConfig', + 'Extra', + # validate_call + 'validate_call', + # errors + 'PydanticErrorCodes', + 'PydanticUserError', + 'PydanticSchemaGenerationError', + 'PydanticImportError', + 'PydanticUndefinedAnnotation', + 'PydanticInvalidForJsonSchema', + # fields + 'Field', + 'computed_field', + 'PrivateAttr', + # alias + 'AliasChoices', + 'AliasGenerator', + 'AliasPath', + # main + 'BaseModel', + 'create_model', + # network + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'HttpUrl', + 'FtpUrl', + 'WebsocketUrl', + 'AnyWebsocketUrl', + 'UrlConstraints', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'NatsDsn', + 'MySQLDsn', + 'MariaDBDsn', + 'ClickHouseDsn', + 'validate_email', + # root_model + 'RootModel', + # deprecated tools, these are imported via `__getattr__` below + 'parse_obj_as', + 'schema_of', + 'schema_json_of', + # types + 'Strict', + 'StrictStr', + 'conbytes', + 'conlist', + 'conset', + 'confrozenset', + 'constr', + 'StringConstraints', + 'ImportString', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'condecimal', + 'condate', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'NewPath', + 'Json', + 'Secret', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'PastDatetime', + 'FutureDatetime', + 'AwareDatetime', + 'NaiveDatetime', + 'AllowInfNan', + 'EncoderProtocol', + 'EncodedBytes', + 'EncodedStr', + 'Base64Encoder', + 'Base64Bytes', + 'Base64Str', + 'Base64UrlBytes', + 'Base64UrlStr', + 'GetPydanticSchema', + 'Tag', + 'Discriminator', + 'JsonValue', + # type_adapter + 'TypeAdapter', + # version + '__version__', + 'VERSION', + # warnings + 'PydanticDeprecatedSince20', + 'PydanticDeprecatedSince26', + 'PydanticDeprecationWarning', + # annotated handlers + 'GetCoreSchemaHandler', + 'GetJsonSchemaHandler', + # generate schema from ._internal + 'GenerateSchema', + # pydantic_core + 'ValidationError', + 'ValidationInfo', + 'SerializationInfo', + 'ValidatorFunctionWrapHandler', + 'FieldSerializationInfo', + 'SerializerFunctionWrapHandler', + 'OnErrorOmit', +) + +# A mapping of {: (package, )} defining dynamic imports +_dynamic_imports: 'dict[str, tuple[str, str]]' = { + 'dataclasses': (__package__, '__module__'), + # functional validators + 'field_validator': (__package__, '.functional_validators'), + 'model_validator': (__package__, '.functional_validators'), + 'AfterValidator': (__package__, '.functional_validators'), + 'BeforeValidator': (__package__, '.functional_validators'), + 'PlainValidator': (__package__, '.functional_validators'), + 'WrapValidator': (__package__, '.functional_validators'), + 'SkipValidation': (__package__, '.functional_validators'), + 'InstanceOf': (__package__, '.functional_validators'), + # JSON Schema + 'WithJsonSchema': (__package__, '.json_schema'), + # functional serializers + 'field_serializer': (__package__, '.functional_serializers'), + 'model_serializer': (__package__, '.functional_serializers'), + 'PlainSerializer': (__package__, '.functional_serializers'), + 'SerializeAsAny': (__package__, '.functional_serializers'), + 'WrapSerializer': (__package__, '.functional_serializers'), + # config + 'ConfigDict': (__package__, '.config'), + 'with_config': (__package__, '.config'), + # validate call + 'validate_call': (__package__, '.validate_call_decorator'), + # errors + 'PydanticErrorCodes': (__package__, '.errors'), + 'PydanticUserError': (__package__, '.errors'), + 'PydanticSchemaGenerationError': (__package__, '.errors'), + 'PydanticImportError': (__package__, '.errors'), + 'PydanticUndefinedAnnotation': (__package__, '.errors'), + 'PydanticInvalidForJsonSchema': (__package__, '.errors'), + # fields + 'Field': (__package__, '.fields'), + 'computed_field': (__package__, '.fields'), + 'PrivateAttr': (__package__, '.fields'), + # alias + 'AliasChoices': (__package__, '.aliases'), + 'AliasGenerator': (__package__, '.aliases'), + 'AliasPath': (__package__, '.aliases'), + # main + 'BaseModel': (__package__, '.main'), + 'create_model': (__package__, '.main'), + # network + 'AnyUrl': (__package__, '.networks'), + 'AnyHttpUrl': (__package__, '.networks'), + 'FileUrl': (__package__, '.networks'), + 'HttpUrl': (__package__, '.networks'), + 'FtpUrl': (__package__, '.networks'), + 'WebsocketUrl': (__package__, '.networks'), + 'AnyWebsocketUrl': (__package__, '.networks'), + 'UrlConstraints': (__package__, '.networks'), + 'EmailStr': (__package__, '.networks'), + 'NameEmail': (__package__, '.networks'), + 'IPvAnyAddress': (__package__, '.networks'), + 'IPvAnyInterface': (__package__, '.networks'), + 'IPvAnyNetwork': (__package__, '.networks'), + 'PostgresDsn': (__package__, '.networks'), + 'CockroachDsn': (__package__, '.networks'), + 'AmqpDsn': (__package__, '.networks'), + 'RedisDsn': (__package__, '.networks'), + 'MongoDsn': (__package__, '.networks'), + 'KafkaDsn': (__package__, '.networks'), + 'NatsDsn': (__package__, '.networks'), + 'MySQLDsn': (__package__, '.networks'), + 'MariaDBDsn': (__package__, '.networks'), + 'ClickHouseDsn': (__package__, '.networks'), + 'validate_email': (__package__, '.networks'), + # root_model + 'RootModel': (__package__, '.root_model'), + # types + 'Strict': (__package__, '.types'), + 'StrictStr': (__package__, '.types'), + 'conbytes': (__package__, '.types'), + 'conlist': (__package__, '.types'), + 'conset': (__package__, '.types'), + 'confrozenset': (__package__, '.types'), + 'constr': (__package__, '.types'), + 'StringConstraints': (__package__, '.types'), + 'ImportString': (__package__, '.types'), + 'conint': (__package__, '.types'), + 'PositiveInt': (__package__, '.types'), + 'NegativeInt': (__package__, '.types'), + 'NonNegativeInt': (__package__, '.types'), + 'NonPositiveInt': (__package__, '.types'), + 'confloat': (__package__, '.types'), + 'PositiveFloat': (__package__, '.types'), + 'NegativeFloat': (__package__, '.types'), + 'NonNegativeFloat': (__package__, '.types'), + 'NonPositiveFloat': (__package__, '.types'), + 'FiniteFloat': (__package__, '.types'), + 'condecimal': (__package__, '.types'), + 'condate': (__package__, '.types'), + 'UUID1': (__package__, '.types'), + 'UUID3': (__package__, '.types'), + 'UUID4': (__package__, '.types'), + 'UUID5': (__package__, '.types'), + 'FilePath': (__package__, '.types'), + 'DirectoryPath': (__package__, '.types'), + 'NewPath': (__package__, '.types'), + 'Json': (__package__, '.types'), + 'Secret': (__package__, '.types'), + 'SecretStr': (__package__, '.types'), + 'SecretBytes': (__package__, '.types'), + 'StrictBool': (__package__, '.types'), + 'StrictBytes': (__package__, '.types'), + 'StrictInt': (__package__, '.types'), + 'StrictFloat': (__package__, '.types'), + 'PaymentCardNumber': (__package__, '.types'), + 'ByteSize': (__package__, '.types'), + 'PastDate': (__package__, '.types'), + 'FutureDate': (__package__, '.types'), + 'PastDatetime': (__package__, '.types'), + 'FutureDatetime': (__package__, '.types'), + 'AwareDatetime': (__package__, '.types'), + 'NaiveDatetime': (__package__, '.types'), + 'AllowInfNan': (__package__, '.types'), + 'EncoderProtocol': (__package__, '.types'), + 'EncodedBytes': (__package__, '.types'), + 'EncodedStr': (__package__, '.types'), + 'Base64Encoder': (__package__, '.types'), + 'Base64Bytes': (__package__, '.types'), + 'Base64Str': (__package__, '.types'), + 'Base64UrlBytes': (__package__, '.types'), + 'Base64UrlStr': (__package__, '.types'), + 'GetPydanticSchema': (__package__, '.types'), + 'Tag': (__package__, '.types'), + 'Discriminator': (__package__, '.types'), + 'JsonValue': (__package__, '.types'), + 'OnErrorOmit': (__package__, '.types'), + # type_adapter + 'TypeAdapter': (__package__, '.type_adapter'), + # warnings + 'PydanticDeprecatedSince20': (__package__, '.warnings'), + 'PydanticDeprecatedSince26': (__package__, '.warnings'), + 'PydanticDeprecationWarning': (__package__, '.warnings'), + # annotated handlers + 'GetCoreSchemaHandler': (__package__, '.annotated_handlers'), + 'GetJsonSchemaHandler': (__package__, '.annotated_handlers'), + # generate schema from ._internal + 'GenerateSchema': (__package__, '._internal._generate_schema'), + # pydantic_core stuff + 'ValidationError': ('pydantic_core', '.'), + 'ValidationInfo': ('pydantic_core', '.core_schema'), + 'SerializationInfo': ('pydantic_core', '.core_schema'), + 'ValidatorFunctionWrapHandler': ('pydantic_core', '.core_schema'), + 'FieldSerializationInfo': ('pydantic_core', '.core_schema'), + 'SerializerFunctionWrapHandler': ('pydantic_core', '.core_schema'), + # deprecated, mostly not included in __all__ + 'root_validator': (__package__, '.deprecated.class_validators'), + 'validator': (__package__, '.deprecated.class_validators'), + 'BaseConfig': (__package__, '.deprecated.config'), + 'Extra': (__package__, '.deprecated.config'), + 'parse_obj_as': (__package__, '.deprecated.tools'), + 'schema_of': (__package__, '.deprecated.tools'), + 'schema_json_of': (__package__, '.deprecated.tools'), + 'FieldValidationInfo': ('pydantic_core', '.core_schema'), +} + +_getattr_migration = getattr_migration(__name__) + + +def __getattr__(attr_name: str) -> object: + dynamic_attr = _dynamic_imports.get(attr_name) + if dynamic_attr is None: + return _getattr_migration(attr_name) + + package, module_name = dynamic_attr + + from importlib import import_module + + if module_name == '__module__': + return import_module(f'.{attr_name}', package=package) + else: + module = import_module(module_name, package=package) + return getattr(module, attr_name) + + +def __dir__() -> 'list[str]': + return list(__all__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3702c6c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/_migration.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/_migration.cpython-310.pyc new file mode 100644 index 0000000..4d9521d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/_migration.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/alias_generators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/alias_generators.cpython-310.pyc new file mode 100644 index 0000000..554e506 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/alias_generators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/aliases.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/aliases.cpython-310.pyc new file mode 100644 index 0000000..5a347b8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/aliases.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/annotated_handlers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/annotated_handlers.cpython-310.pyc new file mode 100644 index 0000000..18aaf4e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/annotated_handlers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/class_validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/class_validators.cpython-310.pyc new file mode 100644 index 0000000..3ea9293 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/class_validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/color.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/color.cpython-310.pyc new file mode 100644 index 0000000..54c6919 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/color.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..8053fc4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/dataclasses.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/dataclasses.cpython-310.pyc new file mode 100644 index 0000000..8810c0b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/dataclasses.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/datetime_parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/datetime_parse.cpython-310.pyc new file mode 100644 index 0000000..7d9336d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/datetime_parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/decorator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/decorator.cpython-310.pyc new file mode 100644 index 0000000..5cf6e68 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/decorator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/env_settings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/env_settings.cpython-310.pyc new file mode 100644 index 0000000..10444fe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/env_settings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/error_wrappers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/error_wrappers.cpython-310.pyc new file mode 100644 index 0000000..2de8ec2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/error_wrappers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/errors.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/errors.cpython-310.pyc new file mode 100644 index 0000000..3547edf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/errors.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/fields.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/fields.cpython-310.pyc new file mode 100644 index 0000000..f5697dd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/fields.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_serializers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_serializers.cpython-310.pyc new file mode 100644 index 0000000..42ffc59 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_serializers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_validators.cpython-310.pyc new file mode 100644 index 0000000..e020b68 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/functional_validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/generics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/generics.cpython-310.pyc new file mode 100644 index 0000000..17f1fbb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/generics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json.cpython-310.pyc new file mode 100644 index 0000000..8216efc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json_schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json_schema.cpython-310.pyc new file mode 100644 index 0000000..057241b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/json_schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/main.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000..8926f8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/main.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/mypy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/mypy.cpython-310.pyc new file mode 100644 index 0000000..8a49907 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/mypy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/networks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/networks.cpython-310.pyc new file mode 100644 index 0000000..842ad9e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/networks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/parse.cpython-310.pyc new file mode 100644 index 0000000..6d9d94c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/root_model.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/root_model.cpython-310.pyc new file mode 100644 index 0000000..62f502f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/root_model.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/schema.cpython-310.pyc new file mode 100644 index 0000000..9ec3b2d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/tools.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/tools.cpython-310.pyc new file mode 100644 index 0000000..f2e9c14 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/tools.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/type_adapter.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/type_adapter.cpython-310.pyc new file mode 100644 index 0000000..d528aa9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/type_adapter.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000..5f0a8b6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/typing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/typing.cpython-310.pyc new file mode 100644 index 0000000..a2eeae5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/typing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..c24014a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validate_call_decorator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validate_call_decorator.cpython-310.pyc new file mode 100644 index 0000000..79238f4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validate_call_decorator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validators.cpython-310.pyc new file mode 100644 index 0000000..b273b0b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..22589e9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/warnings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/warnings.cpython-310.pyc new file mode 100644 index 0000000..35047b9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/__pycache__/warnings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c42b09c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_config.cpython-310.pyc new file mode 100644 index 0000000..a5808b7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_metadata.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_metadata.cpython-310.pyc new file mode 100644 index 0000000..299138f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_metadata.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_utils.cpython-310.pyc new file mode 100644 index 0000000..3f6fa19 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_core_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_dataclasses.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_dataclasses.cpython-310.pyc new file mode 100644 index 0000000..9800f92 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_dataclasses.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators.cpython-310.pyc new file mode 100644 index 0000000..7fa796c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators_v1.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators_v1.cpython-310.pyc new file mode 100644 index 0000000..02dfd08 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_decorators_v1.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_discriminated_union.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_discriminated_union.cpython-310.pyc new file mode 100644 index 0000000..cc2ec7c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_discriminated_union.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_docs_extraction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_docs_extraction.cpython-310.pyc new file mode 100644 index 0000000..8e136cb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_docs_extraction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_fields.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_fields.cpython-310.pyc new file mode 100644 index 0000000..03937df Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_fields.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_forward_ref.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_forward_ref.cpython-310.pyc new file mode 100644 index 0000000..2a7f77f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_forward_ref.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generate_schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generate_schema.cpython-310.pyc new file mode 100644 index 0000000..c11dfbd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generate_schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generics.cpython-310.pyc new file mode 100644 index 0000000..c41dab7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_generics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_git.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_git.cpython-310.pyc new file mode 100644 index 0000000..3074be0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_git.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_internal_dataclass.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_internal_dataclass.cpython-310.pyc new file mode 100644 index 0000000..7c06a76 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_internal_dataclass.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-310.pyc new file mode 100644 index 0000000..853a332 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_known_annotated_metadata.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_mock_val_ser.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_mock_val_ser.cpython-310.pyc new file mode 100644 index 0000000..4278388 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_mock_val_ser.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_model_construction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_model_construction.cpython-310.pyc new file mode 100644 index 0000000..397ded4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_model_construction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_repr.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_repr.cpython-310.pyc new file mode 100644 index 0000000..e75fb17 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_repr.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_schema_generation_shared.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_schema_generation_shared.cpython-310.pyc new file mode 100644 index 0000000..6e3b5da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_schema_generation_shared.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_signature.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_signature.cpython-310.pyc new file mode 100644 index 0000000..c0aea0b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_signature.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_std_types_schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_std_types_schema.cpython-310.pyc new file mode 100644 index 0000000..68e9d5d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_std_types_schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_typing_extra.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_typing_extra.cpython-310.pyc new file mode 100644 index 0000000..e62c2c8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_typing_extra.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000..fe532cc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validate_call.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validate_call.cpython-310.pyc new file mode 100644 index 0000000..b71f9e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validate_call.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validators.cpython-310.pyc new file mode 100644 index 0000000..fe6ef81 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/__pycache__/_validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_config.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_config.py new file mode 100644 index 0000000..ec00b38 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_config.py @@ -0,0 +1,334 @@ +from __future__ import annotations as _annotations + +import warnings +from contextlib import contextmanager +from typing import ( + TYPE_CHECKING, + Any, + Callable, + cast, +) + +from pydantic_core import core_schema +from typing_extensions import ( + Literal, + Self, +) + +from ..aliases import AliasGenerator +from ..config import ConfigDict, ExtraValues, JsonDict, JsonEncoder, JsonSchemaExtraCallable +from ..errors import PydanticUserError +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +if TYPE_CHECKING: + from .._internal._schema_generation_shared import GenerateSchema + +DEPRECATION_MESSAGE = 'Support for class-based `config` is deprecated, use ConfigDict instead.' + + +class ConfigWrapper: + """Internal wrapper for Config which exposes ConfigDict items as attributes.""" + + __slots__ = ('config_dict',) + + config_dict: ConfigDict + + # all annotations are copied directly from ConfigDict, and should be kept up to date, a test will fail if they + # stop matching + title: str | None + str_to_lower: bool + str_to_upper: bool + str_strip_whitespace: bool + str_min_length: int + str_max_length: int | None + extra: ExtraValues | None + frozen: bool + populate_by_name: bool + use_enum_values: bool + validate_assignment: bool + arbitrary_types_allowed: bool + from_attributes: bool + # whether to use the actual key provided in the data (e.g. alias or first alias for "field required" errors) instead of field_names + # to construct error `loc`s, default `True` + loc_by_alias: bool + alias_generator: Callable[[str], str] | AliasGenerator | None + ignored_types: tuple[type, ...] + allow_inf_nan: bool + json_schema_extra: JsonDict | JsonSchemaExtraCallable | None + json_encoders: dict[type[object], JsonEncoder] | None + + # new in V2 + strict: bool + # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' + revalidate_instances: Literal['always', 'never', 'subclass-instances'] + ser_json_timedelta: Literal['iso8601', 'float'] + ser_json_bytes: Literal['utf8', 'base64'] + ser_json_inf_nan: Literal['null', 'constants'] + # whether to validate default values during validation, default False + validate_default: bool + validate_return: bool + protected_namespaces: tuple[str, ...] + hide_input_in_errors: bool + defer_build: bool + plugin_settings: dict[str, object] | None + schema_generator: type[GenerateSchema] | None + json_schema_serialization_defaults_required: bool + json_schema_mode_override: Literal['validation', 'serialization', None] + coerce_numbers_to_str: bool + regex_engine: Literal['rust-regex', 'python-re'] + validation_error_cause: bool + use_attribute_docstrings: bool + cache_strings: bool | Literal['all', 'keys', 'none'] + + def __init__(self, config: ConfigDict | dict[str, Any] | type[Any] | None, *, check: bool = True): + if check: + self.config_dict = prepare_config(config) + else: + self.config_dict = cast(ConfigDict, config) + + @classmethod + def for_model(cls, bases: tuple[type[Any], ...], namespace: dict[str, Any], kwargs: dict[str, Any]) -> Self: + """Build a new `ConfigWrapper` instance for a `BaseModel`. + + The config wrapper built based on (in descending order of priority): + - options from `kwargs` + - options from the `namespace` + - options from the base classes (`bases`) + + Args: + bases: A tuple of base classes. + namespace: The namespace of the class being created. + kwargs: The kwargs passed to the class being created. + + Returns: + A `ConfigWrapper` instance for `BaseModel`. + """ + config_new = ConfigDict() + for base in bases: + config = getattr(base, 'model_config', None) + if config: + config_new.update(config.copy()) + + config_class_from_namespace = namespace.get('Config') + config_dict_from_namespace = namespace.get('model_config') + + raw_annotations = namespace.get('__annotations__', {}) + if raw_annotations.get('model_config') and not config_dict_from_namespace: + raise PydanticUserError( + '`model_config` cannot be used as a model field name. Use `model_config` for model configuration.', + code='model-config-invalid-field-name', + ) + + if config_class_from_namespace and config_dict_from_namespace: + raise PydanticUserError('"Config" and "model_config" cannot be used together', code='config-both') + + config_from_namespace = config_dict_from_namespace or prepare_config(config_class_from_namespace) + + config_new.update(config_from_namespace) + + for k in list(kwargs.keys()): + if k in config_keys: + config_new[k] = kwargs.pop(k) + + return cls(config_new) + + # we don't show `__getattr__` to type checkers so missing attributes cause errors + if not TYPE_CHECKING: # pragma: no branch + + def __getattr__(self, name: str) -> Any: + try: + return self.config_dict[name] + except KeyError: + try: + return config_defaults[name] + except KeyError: + raise AttributeError(f'Config has no attribute {name!r}') from None + + def core_config(self, obj: Any) -> core_schema.CoreConfig: + """Create a pydantic-core config, `obj` is just used to populate `title` if not set in config. + + Pass `obj=None` if you do not want to attempt to infer the `title`. + + We don't use getattr here since we don't want to populate with defaults. + + Args: + obj: An object used to populate `title` if not set in config. + + Returns: + A `CoreConfig` object created from config. + """ + + def dict_not_none(**kwargs: Any) -> Any: + return {k: v for k, v in kwargs.items() if v is not None} + + core_config = core_schema.CoreConfig( + **dict_not_none( + title=self.config_dict.get('title') or (obj and obj.__name__), + extra_fields_behavior=self.config_dict.get('extra'), + allow_inf_nan=self.config_dict.get('allow_inf_nan'), + populate_by_name=self.config_dict.get('populate_by_name'), + str_strip_whitespace=self.config_dict.get('str_strip_whitespace'), + str_to_lower=self.config_dict.get('str_to_lower'), + str_to_upper=self.config_dict.get('str_to_upper'), + strict=self.config_dict.get('strict'), + ser_json_timedelta=self.config_dict.get('ser_json_timedelta'), + ser_json_bytes=self.config_dict.get('ser_json_bytes'), + ser_json_inf_nan=self.config_dict.get('ser_json_inf_nan'), + from_attributes=self.config_dict.get('from_attributes'), + loc_by_alias=self.config_dict.get('loc_by_alias'), + revalidate_instances=self.config_dict.get('revalidate_instances'), + validate_default=self.config_dict.get('validate_default'), + str_max_length=self.config_dict.get('str_max_length'), + str_min_length=self.config_dict.get('str_min_length'), + hide_input_in_errors=self.config_dict.get('hide_input_in_errors'), + coerce_numbers_to_str=self.config_dict.get('coerce_numbers_to_str'), + regex_engine=self.config_dict.get('regex_engine'), + validation_error_cause=self.config_dict.get('validation_error_cause'), + cache_strings=self.config_dict.get('cache_strings'), + ) + ) + return core_config + + def __repr__(self): + c = ', '.join(f'{k}={v!r}' for k, v in self.config_dict.items()) + return f'ConfigWrapper({c})' + + +class ConfigWrapperStack: + """A stack of `ConfigWrapper` instances.""" + + def __init__(self, config_wrapper: ConfigWrapper): + self._config_wrapper_stack: list[ConfigWrapper] = [config_wrapper] + + @property + def tail(self) -> ConfigWrapper: + return self._config_wrapper_stack[-1] + + @contextmanager + def push(self, config_wrapper: ConfigWrapper | ConfigDict | None): + if config_wrapper is None: + yield + return + + if not isinstance(config_wrapper, ConfigWrapper): + config_wrapper = ConfigWrapper(config_wrapper, check=False) + + self._config_wrapper_stack.append(config_wrapper) + try: + yield + finally: + self._config_wrapper_stack.pop() + + +config_defaults = ConfigDict( + title=None, + str_to_lower=False, + str_to_upper=False, + str_strip_whitespace=False, + str_min_length=0, + str_max_length=None, + # let the model / dataclass decide how to handle it + extra=None, + frozen=False, + populate_by_name=False, + use_enum_values=False, + validate_assignment=False, + arbitrary_types_allowed=False, + from_attributes=False, + loc_by_alias=True, + alias_generator=None, + ignored_types=(), + allow_inf_nan=True, + json_schema_extra=None, + strict=False, + revalidate_instances='never', + ser_json_timedelta='iso8601', + ser_json_bytes='utf8', + ser_json_inf_nan='null', + validate_default=False, + validate_return=False, + protected_namespaces=('model_',), + hide_input_in_errors=False, + json_encoders=None, + defer_build=False, + plugin_settings=None, + schema_generator=None, + json_schema_serialization_defaults_required=False, + json_schema_mode_override=None, + coerce_numbers_to_str=False, + regex_engine='rust-regex', + validation_error_cause=False, + use_attribute_docstrings=False, + cache_strings=True, +) + + +def prepare_config(config: ConfigDict | dict[str, Any] | type[Any] | None) -> ConfigDict: + """Create a `ConfigDict` instance from an existing dict, a class (e.g. old class-based config) or None. + + Args: + config: The input config. + + Returns: + A ConfigDict object created from config. + """ + if config is None: + return ConfigDict() + + if not isinstance(config, dict): + warnings.warn(DEPRECATION_MESSAGE, DeprecationWarning) + config = {k: getattr(config, k) for k in dir(config) if not k.startswith('__')} + + config_dict = cast(ConfigDict, config) + check_deprecated(config_dict) + return config_dict + + +config_keys = set(ConfigDict.__annotations__.keys()) + + +V2_REMOVED_KEYS = { + 'allow_mutation', + 'error_msg_templates', + 'fields', + 'getter_dict', + 'smart_union', + 'underscore_attrs_are_private', + 'json_loads', + 'json_dumps', + 'copy_on_model_validation', + 'post_init_call', +} +V2_RENAMED_KEYS = { + 'allow_population_by_field_name': 'populate_by_name', + 'anystr_lower': 'str_to_lower', + 'anystr_strip_whitespace': 'str_strip_whitespace', + 'anystr_upper': 'str_to_upper', + 'keep_untouched': 'ignored_types', + 'max_anystr_length': 'str_max_length', + 'min_anystr_length': 'str_min_length', + 'orm_mode': 'from_attributes', + 'schema_extra': 'json_schema_extra', + 'validate_all': 'validate_default', +} + + +def check_deprecated(config_dict: ConfigDict) -> None: + """Check for deprecated config keys and warn the user. + + Args: + config_dict: The input config. + """ + deprecated_removed_keys = V2_REMOVED_KEYS & config_dict.keys() + deprecated_renamed_keys = V2_RENAMED_KEYS.keys() & config_dict.keys() + if deprecated_removed_keys or deprecated_renamed_keys: + renamings = {k: V2_RENAMED_KEYS[k] for k in sorted(deprecated_renamed_keys)} + renamed_bullets = [f'* {k!r} has been renamed to {v!r}' for k, v in renamings.items()] + removed_bullets = [f'* {k!r} has been removed' for k in sorted(deprecated_removed_keys)] + message = '\n'.join(['Valid config keys have changed in V2:'] + renamed_bullets + removed_bullets) + warnings.warn(message, UserWarning) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_metadata.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_metadata.py new file mode 100644 index 0000000..296d49f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_metadata.py @@ -0,0 +1,92 @@ +from __future__ import annotations as _annotations + +import typing +from typing import Any + +import typing_extensions + +if typing.TYPE_CHECKING: + from ._schema_generation_shared import ( + CoreSchemaOrField as CoreSchemaOrField, + ) + from ._schema_generation_shared import ( + GetJsonSchemaFunction, + ) + + +class CoreMetadata(typing_extensions.TypedDict, total=False): + """A `TypedDict` for holding the metadata dict of the schema. + + Attributes: + pydantic_js_functions: List of JSON schema functions. + pydantic_js_prefer_positional_arguments: Whether JSON schema generator will + prefer positional over keyword arguments for an 'arguments' schema. + """ + + pydantic_js_functions: list[GetJsonSchemaFunction] + pydantic_js_annotation_functions: list[GetJsonSchemaFunction] + + # If `pydantic_js_prefer_positional_arguments` is True, the JSON schema generator will + # prefer positional over keyword arguments for an 'arguments' schema. + pydantic_js_prefer_positional_arguments: bool | None + + pydantic_typed_dict_cls: type[Any] | None # TODO: Consider moving this into the pydantic-core TypedDictSchema + + +class CoreMetadataHandler: + """Because the metadata field in pydantic_core is of type `Any`, we can't assume much about its contents. + + This class is used to interact with the metadata field on a CoreSchema object in a consistent + way throughout pydantic. + """ + + __slots__ = ('_schema',) + + def __init__(self, schema: CoreSchemaOrField): + self._schema = schema + + metadata = schema.get('metadata') + if metadata is None: + schema['metadata'] = CoreMetadata() + elif not isinstance(metadata, dict): + raise TypeError(f'CoreSchema metadata should be a dict; got {metadata!r}.') + + @property + def metadata(self) -> CoreMetadata: + """Retrieves the metadata dict from the schema, initializing it to a dict if it is None + and raises an error if it is not a dict. + """ + metadata = self._schema.get('metadata') + if metadata is None: + self._schema['metadata'] = metadata = CoreMetadata() + if not isinstance(metadata, dict): + raise TypeError(f'CoreSchema metadata should be a dict; got {metadata!r}.') + return metadata + + +def build_metadata_dict( + *, # force keyword arguments to make it easier to modify this signature in a backwards-compatible way + js_functions: list[GetJsonSchemaFunction] | None = None, + js_annotation_functions: list[GetJsonSchemaFunction] | None = None, + js_prefer_positional_arguments: bool | None = None, + typed_dict_cls: type[Any] | None = None, + initial_metadata: Any | None = None, +) -> Any: + """Builds a dict to use as the metadata field of a CoreSchema object in a manner that is consistent + with the CoreMetadataHandler class. + """ + if initial_metadata is not None and not isinstance(initial_metadata, dict): + raise TypeError(f'CoreSchema metadata should be a dict; got {initial_metadata!r}.') + + metadata = CoreMetadata( + pydantic_js_functions=js_functions or [], + pydantic_js_annotation_functions=js_annotation_functions or [], + pydantic_js_prefer_positional_arguments=js_prefer_positional_arguments, + pydantic_typed_dict_cls=typed_dict_cls, + ) + metadata = {k: v for k, v in metadata.items() if v is not None} + + if initial_metadata is not None: + metadata = {**initial_metadata, **metadata} + + return metadata diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_utils.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_utils.py new file mode 100644 index 0000000..baec706 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_core_utils.py @@ -0,0 +1,568 @@ +from __future__ import annotations + +import os +from collections import defaultdict +from typing import ( + Any, + Callable, + Hashable, + TypeVar, + Union, +) + +from pydantic_core import CoreSchema, core_schema +from pydantic_core import validate_core_schema as _validate_core_schema +from typing_extensions import TypeAliasType, TypeGuard, get_args, get_origin + +from . import _repr +from ._typing_extra import is_generic_alias + +AnyFunctionSchema = Union[ + core_schema.AfterValidatorFunctionSchema, + core_schema.BeforeValidatorFunctionSchema, + core_schema.WrapValidatorFunctionSchema, + core_schema.PlainValidatorFunctionSchema, +] + + +FunctionSchemaWithInnerSchema = Union[ + core_schema.AfterValidatorFunctionSchema, + core_schema.BeforeValidatorFunctionSchema, + core_schema.WrapValidatorFunctionSchema, +] + +CoreSchemaField = Union[ + core_schema.ModelField, core_schema.DataclassField, core_schema.TypedDictField, core_schema.ComputedField +] +CoreSchemaOrField = Union[core_schema.CoreSchema, CoreSchemaField] + +_CORE_SCHEMA_FIELD_TYPES = {'typed-dict-field', 'dataclass-field', 'model-field', 'computed-field'} +_FUNCTION_WITH_INNER_SCHEMA_TYPES = {'function-before', 'function-after', 'function-wrap'} +_LIST_LIKE_SCHEMA_WITH_ITEMS_TYPES = {'list', 'set', 'frozenset'} + +TAGGED_UNION_TAG_KEY = 'pydantic.internal.tagged_union_tag' +""" +Used in a `Tag` schema to specify the tag used for a discriminated union. +""" +HAS_INVALID_SCHEMAS_METADATA_KEY = 'pydantic.internal.invalid' +"""Used to mark a schema that is invalid because it refers to a definition that was not yet defined when the +schema was first encountered. +""" + + +def is_core_schema( + schema: CoreSchemaOrField, +) -> TypeGuard[CoreSchema]: + return schema['type'] not in _CORE_SCHEMA_FIELD_TYPES + + +def is_core_schema_field( + schema: CoreSchemaOrField, +) -> TypeGuard[CoreSchemaField]: + return schema['type'] in _CORE_SCHEMA_FIELD_TYPES + + +def is_function_with_inner_schema( + schema: CoreSchemaOrField, +) -> TypeGuard[FunctionSchemaWithInnerSchema]: + return schema['type'] in _FUNCTION_WITH_INNER_SCHEMA_TYPES + + +def is_list_like_schema_with_items_schema( + schema: CoreSchema, +) -> TypeGuard[core_schema.ListSchema | core_schema.SetSchema | core_schema.FrozenSetSchema]: + return schema['type'] in _LIST_LIKE_SCHEMA_WITH_ITEMS_TYPES + + +def get_type_ref(type_: type[Any], args_override: tuple[type[Any], ...] | None = None) -> str: + """Produces the ref to be used for this type by pydantic_core's core schemas. + + This `args_override` argument was added for the purpose of creating valid recursive references + when creating generic models without needing to create a concrete class. + """ + origin = get_origin(type_) or type_ + + args = get_args(type_) if is_generic_alias(type_) else (args_override or ()) + generic_metadata = getattr(type_, '__pydantic_generic_metadata__', None) + if generic_metadata: + origin = generic_metadata['origin'] or origin + args = generic_metadata['args'] or args + + module_name = getattr(origin, '__module__', '') + if isinstance(origin, TypeAliasType): + type_ref = f'{module_name}.{origin.__name__}:{id(origin)}' + else: + try: + qualname = getattr(origin, '__qualname__', f'') + except Exception: + qualname = getattr(origin, '__qualname__', '') + type_ref = f'{module_name}.{qualname}:{id(origin)}' + + arg_refs: list[str] = [] + for arg in args: + if isinstance(arg, str): + # Handle string literals as a special case; we may be able to remove this special handling if we + # wrap them in a ForwardRef at some point. + arg_ref = f'{arg}:str-{id(arg)}' + else: + arg_ref = f'{_repr.display_as_type(arg)}:{id(arg)}' + arg_refs.append(arg_ref) + if arg_refs: + type_ref = f'{type_ref}[{",".join(arg_refs)}]' + return type_ref + + +def get_ref(s: core_schema.CoreSchema) -> None | str: + """Get the ref from the schema if it has one. + This exists just for type checking to work correctly. + """ + return s.get('ref', None) + + +def collect_definitions(schema: core_schema.CoreSchema) -> dict[str, core_schema.CoreSchema]: + defs: dict[str, CoreSchema] = {} + + def _record_valid_refs(s: core_schema.CoreSchema, recurse: Recurse) -> core_schema.CoreSchema: + ref = get_ref(s) + if ref: + defs[ref] = s + return recurse(s, _record_valid_refs) + + walk_core_schema(schema, _record_valid_refs) + + return defs + + +def define_expected_missing_refs( + schema: core_schema.CoreSchema, allowed_missing_refs: set[str] +) -> core_schema.CoreSchema | None: + if not allowed_missing_refs: + # in this case, there are no missing refs to potentially substitute, so there's no need to walk the schema + # this is a common case (will be hit for all non-generic models), so it's worth optimizing for + return None + + refs = collect_definitions(schema).keys() + + expected_missing_refs = allowed_missing_refs.difference(refs) + if expected_missing_refs: + definitions: list[core_schema.CoreSchema] = [ + # TODO: Replace this with a (new) CoreSchema that, if present at any level, makes validation fail + # Issue: https://github.com/pydantic/pydantic-core/issues/619 + core_schema.none_schema(ref=ref, metadata={HAS_INVALID_SCHEMAS_METADATA_KEY: True}) + for ref in expected_missing_refs + ] + return core_schema.definitions_schema(schema, definitions) + return None + + +def collect_invalid_schemas(schema: core_schema.CoreSchema) -> bool: + invalid = False + + def _is_schema_valid(s: core_schema.CoreSchema, recurse: Recurse) -> core_schema.CoreSchema: + nonlocal invalid + if 'metadata' in s: + metadata = s['metadata'] + if HAS_INVALID_SCHEMAS_METADATA_KEY in metadata: + invalid = metadata[HAS_INVALID_SCHEMAS_METADATA_KEY] + return s + return recurse(s, _is_schema_valid) + + walk_core_schema(schema, _is_schema_valid) + return invalid + + +T = TypeVar('T') + + +Recurse = Callable[[core_schema.CoreSchema, 'Walk'], core_schema.CoreSchema] +Walk = Callable[[core_schema.CoreSchema, Recurse], core_schema.CoreSchema] + +# TODO: Should we move _WalkCoreSchema into pydantic_core proper? +# Issue: https://github.com/pydantic/pydantic-core/issues/615 + + +class _WalkCoreSchema: + def __init__(self): + self._schema_type_to_method = self._build_schema_type_to_method() + + def _build_schema_type_to_method(self) -> dict[core_schema.CoreSchemaType, Recurse]: + mapping: dict[core_schema.CoreSchemaType, Recurse] = {} + key: core_schema.CoreSchemaType + for key in get_args(core_schema.CoreSchemaType): + method_name = f"handle_{key.replace('-', '_')}_schema" + mapping[key] = getattr(self, method_name, self._handle_other_schemas) + return mapping + + def walk(self, schema: core_schema.CoreSchema, f: Walk) -> core_schema.CoreSchema: + return f(schema, self._walk) + + def _walk(self, schema: core_schema.CoreSchema, f: Walk) -> core_schema.CoreSchema: + schema = self._schema_type_to_method[schema['type']](schema.copy(), f) + ser_schema: core_schema.SerSchema | None = schema.get('serialization') # type: ignore + if ser_schema: + schema['serialization'] = self._handle_ser_schemas(ser_schema, f) + return schema + + def _handle_other_schemas(self, schema: core_schema.CoreSchema, f: Walk) -> core_schema.CoreSchema: + sub_schema = schema.get('schema', None) + if sub_schema is not None: + schema['schema'] = self.walk(sub_schema, f) # type: ignore + return schema + + def _handle_ser_schemas(self, ser_schema: core_schema.SerSchema, f: Walk) -> core_schema.SerSchema: + schema: core_schema.CoreSchema | None = ser_schema.get('schema', None) + if schema is not None: + ser_schema['schema'] = self.walk(schema, f) # type: ignore + return_schema: core_schema.CoreSchema | None = ser_schema.get('return_schema', None) + if return_schema is not None: + ser_schema['return_schema'] = self.walk(return_schema, f) # type: ignore + return ser_schema + + def handle_definitions_schema(self, schema: core_schema.DefinitionsSchema, f: Walk) -> core_schema.CoreSchema: + new_definitions: list[core_schema.CoreSchema] = [] + for definition in schema['definitions']: + if 'schema_ref' in definition and 'ref' in definition: + # This indicates a purposely indirect reference + # We want to keep such references around for implications related to JSON schema, etc.: + new_definitions.append(definition) + # However, we still need to walk the referenced definition: + self.walk(definition, f) + continue + + updated_definition = self.walk(definition, f) + if 'ref' in updated_definition: + # If the updated definition schema doesn't have a 'ref', it shouldn't go in the definitions + # This is most likely to happen due to replacing something with a definition reference, in + # which case it should certainly not go in the definitions list + new_definitions.append(updated_definition) + new_inner_schema = self.walk(schema['schema'], f) + + if not new_definitions and len(schema) == 3: + # This means we'd be returning a "trivial" definitions schema that just wrapped the inner schema + return new_inner_schema + + new_schema = schema.copy() + new_schema['schema'] = new_inner_schema + new_schema['definitions'] = new_definitions + return new_schema + + def handle_list_schema(self, schema: core_schema.ListSchema, f: Walk) -> core_schema.CoreSchema: + items_schema = schema.get('items_schema') + if items_schema is not None: + schema['items_schema'] = self.walk(items_schema, f) + return schema + + def handle_set_schema(self, schema: core_schema.SetSchema, f: Walk) -> core_schema.CoreSchema: + items_schema = schema.get('items_schema') + if items_schema is not None: + schema['items_schema'] = self.walk(items_schema, f) + return schema + + def handle_frozenset_schema(self, schema: core_schema.FrozenSetSchema, f: Walk) -> core_schema.CoreSchema: + items_schema = schema.get('items_schema') + if items_schema is not None: + schema['items_schema'] = self.walk(items_schema, f) + return schema + + def handle_generator_schema(self, schema: core_schema.GeneratorSchema, f: Walk) -> core_schema.CoreSchema: + items_schema = schema.get('items_schema') + if items_schema is not None: + schema['items_schema'] = self.walk(items_schema, f) + return schema + + def handle_tuple_schema(self, schema: core_schema.TupleSchema, f: Walk) -> core_schema.CoreSchema: + schema['items_schema'] = [self.walk(v, f) for v in schema['items_schema']] + return schema + + def handle_dict_schema(self, schema: core_schema.DictSchema, f: Walk) -> core_schema.CoreSchema: + keys_schema = schema.get('keys_schema') + if keys_schema is not None: + schema['keys_schema'] = self.walk(keys_schema, f) + values_schema = schema.get('values_schema') + if values_schema: + schema['values_schema'] = self.walk(values_schema, f) + return schema + + def handle_function_schema(self, schema: AnyFunctionSchema, f: Walk) -> core_schema.CoreSchema: + if not is_function_with_inner_schema(schema): + return schema + schema['schema'] = self.walk(schema['schema'], f) + return schema + + def handle_union_schema(self, schema: core_schema.UnionSchema, f: Walk) -> core_schema.CoreSchema: + new_choices: list[CoreSchema | tuple[CoreSchema, str]] = [] + for v in schema['choices']: + if isinstance(v, tuple): + new_choices.append((self.walk(v[0], f), v[1])) + else: + new_choices.append(self.walk(v, f)) + schema['choices'] = new_choices + return schema + + def handle_tagged_union_schema(self, schema: core_schema.TaggedUnionSchema, f: Walk) -> core_schema.CoreSchema: + new_choices: dict[Hashable, core_schema.CoreSchema] = {} + for k, v in schema['choices'].items(): + new_choices[k] = v if isinstance(v, (str, int)) else self.walk(v, f) + schema['choices'] = new_choices + return schema + + def handle_chain_schema(self, schema: core_schema.ChainSchema, f: Walk) -> core_schema.CoreSchema: + schema['steps'] = [self.walk(v, f) for v in schema['steps']] + return schema + + def handle_lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema, f: Walk) -> core_schema.CoreSchema: + schema['lax_schema'] = self.walk(schema['lax_schema'], f) + schema['strict_schema'] = self.walk(schema['strict_schema'], f) + return schema + + def handle_json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema, f: Walk) -> core_schema.CoreSchema: + schema['json_schema'] = self.walk(schema['json_schema'], f) + schema['python_schema'] = self.walk(schema['python_schema'], f) + return schema + + def handle_model_fields_schema(self, schema: core_schema.ModelFieldsSchema, f: Walk) -> core_schema.CoreSchema: + extras_schema = schema.get('extras_schema') + if extras_schema is not None: + schema['extras_schema'] = self.walk(extras_schema, f) + replaced_fields: dict[str, core_schema.ModelField] = {} + replaced_computed_fields: list[core_schema.ComputedField] = [] + for computed_field in schema.get('computed_fields', ()): + replaced_field = computed_field.copy() + replaced_field['return_schema'] = self.walk(computed_field['return_schema'], f) + replaced_computed_fields.append(replaced_field) + if replaced_computed_fields: + schema['computed_fields'] = replaced_computed_fields + for k, v in schema['fields'].items(): + replaced_field = v.copy() + replaced_field['schema'] = self.walk(v['schema'], f) + replaced_fields[k] = replaced_field + schema['fields'] = replaced_fields + return schema + + def handle_typed_dict_schema(self, schema: core_schema.TypedDictSchema, f: Walk) -> core_schema.CoreSchema: + extras_schema = schema.get('extras_schema') + if extras_schema is not None: + schema['extras_schema'] = self.walk(extras_schema, f) + replaced_computed_fields: list[core_schema.ComputedField] = [] + for computed_field in schema.get('computed_fields', ()): + replaced_field = computed_field.copy() + replaced_field['return_schema'] = self.walk(computed_field['return_schema'], f) + replaced_computed_fields.append(replaced_field) + if replaced_computed_fields: + schema['computed_fields'] = replaced_computed_fields + replaced_fields: dict[str, core_schema.TypedDictField] = {} + for k, v in schema['fields'].items(): + replaced_field = v.copy() + replaced_field['schema'] = self.walk(v['schema'], f) + replaced_fields[k] = replaced_field + schema['fields'] = replaced_fields + return schema + + def handle_dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema, f: Walk) -> core_schema.CoreSchema: + replaced_fields: list[core_schema.DataclassField] = [] + replaced_computed_fields: list[core_schema.ComputedField] = [] + for computed_field in schema.get('computed_fields', ()): + replaced_field = computed_field.copy() + replaced_field['return_schema'] = self.walk(computed_field['return_schema'], f) + replaced_computed_fields.append(replaced_field) + if replaced_computed_fields: + schema['computed_fields'] = replaced_computed_fields + for field in schema['fields']: + replaced_field = field.copy() + replaced_field['schema'] = self.walk(field['schema'], f) + replaced_fields.append(replaced_field) + schema['fields'] = replaced_fields + return schema + + def handle_arguments_schema(self, schema: core_schema.ArgumentsSchema, f: Walk) -> core_schema.CoreSchema: + replaced_arguments_schema: list[core_schema.ArgumentsParameter] = [] + for param in schema['arguments_schema']: + replaced_param = param.copy() + replaced_param['schema'] = self.walk(param['schema'], f) + replaced_arguments_schema.append(replaced_param) + schema['arguments_schema'] = replaced_arguments_schema + if 'var_args_schema' in schema: + schema['var_args_schema'] = self.walk(schema['var_args_schema'], f) + if 'var_kwargs_schema' in schema: + schema['var_kwargs_schema'] = self.walk(schema['var_kwargs_schema'], f) + return schema + + def handle_call_schema(self, schema: core_schema.CallSchema, f: Walk) -> core_schema.CoreSchema: + schema['arguments_schema'] = self.walk(schema['arguments_schema'], f) + if 'return_schema' in schema: + schema['return_schema'] = self.walk(schema['return_schema'], f) + return schema + + +_dispatch = _WalkCoreSchema().walk + + +def walk_core_schema(schema: core_schema.CoreSchema, f: Walk) -> core_schema.CoreSchema: + """Recursively traverse a CoreSchema. + + Args: + schema (core_schema.CoreSchema): The CoreSchema to process, it will not be modified. + f (Walk): A function to apply. This function takes two arguments: + 1. The current CoreSchema that is being processed + (not the same one you passed into this function, one level down). + 2. The "next" `f` to call. This lets you for example use `f=functools.partial(some_method, some_context)` + to pass data down the recursive calls without using globals or other mutable state. + + Returns: + core_schema.CoreSchema: A processed CoreSchema. + """ + return f(schema.copy(), _dispatch) + + +def simplify_schema_references(schema: core_schema.CoreSchema) -> core_schema.CoreSchema: # noqa: C901 + definitions: dict[str, core_schema.CoreSchema] = {} + ref_counts: dict[str, int] = defaultdict(int) + involved_in_recursion: dict[str, bool] = {} + current_recursion_ref_count: dict[str, int] = defaultdict(int) + + def collect_refs(s: core_schema.CoreSchema, recurse: Recurse) -> core_schema.CoreSchema: + if s['type'] == 'definitions': + for definition in s['definitions']: + ref = get_ref(definition) + assert ref is not None + if ref not in definitions: + definitions[ref] = definition + recurse(definition, collect_refs) + return recurse(s['schema'], collect_refs) + else: + ref = get_ref(s) + if ref is not None: + new = recurse(s, collect_refs) + new_ref = get_ref(new) + if new_ref: + definitions[new_ref] = new + return core_schema.definition_reference_schema(schema_ref=ref) + else: + return recurse(s, collect_refs) + + schema = walk_core_schema(schema, collect_refs) + + def count_refs(s: core_schema.CoreSchema, recurse: Recurse) -> core_schema.CoreSchema: + if s['type'] != 'definition-ref': + return recurse(s, count_refs) + ref = s['schema_ref'] + ref_counts[ref] += 1 + + if ref_counts[ref] >= 2: + # If this model is involved in a recursion this should be detected + # on its second encounter, we can safely stop the walk here. + if current_recursion_ref_count[ref] != 0: + involved_in_recursion[ref] = True + return s + + current_recursion_ref_count[ref] += 1 + recurse(definitions[ref], count_refs) + current_recursion_ref_count[ref] -= 1 + return s + + schema = walk_core_schema(schema, count_refs) + + assert all(c == 0 for c in current_recursion_ref_count.values()), 'this is a bug! please report it' + + def can_be_inlined(s: core_schema.DefinitionReferenceSchema, ref: str) -> bool: + if ref_counts[ref] > 1: + return False + if involved_in_recursion.get(ref, False): + return False + if 'serialization' in s: + return False + if 'metadata' in s: + metadata = s['metadata'] + for k in ( + 'pydantic_js_functions', + 'pydantic_js_annotation_functions', + 'pydantic.internal.union_discriminator', + ): + if k in metadata: + # we need to keep this as a ref + return False + return True + + def inline_refs(s: core_schema.CoreSchema, recurse: Recurse) -> core_schema.CoreSchema: + if s['type'] == 'definition-ref': + ref = s['schema_ref'] + # Check if the reference is only used once, not involved in recursion and does not have + # any extra keys (like 'serialization') + if can_be_inlined(s, ref): + # Inline the reference by replacing the reference with the actual schema + new = definitions.pop(ref) + ref_counts[ref] -= 1 # because we just replaced it! + # put all other keys that were on the def-ref schema into the inlined version + # in particular this is needed for `serialization` + if 'serialization' in s: + new['serialization'] = s['serialization'] + s = recurse(new, inline_refs) + return s + else: + return recurse(s, inline_refs) + else: + return recurse(s, inline_refs) + + schema = walk_core_schema(schema, inline_refs) + + def_values = [v for v in definitions.values() if ref_counts[v['ref']] > 0] # type: ignore + + if def_values: + schema = core_schema.definitions_schema(schema=schema, definitions=def_values) + return schema + + +def _strip_metadata(schema: CoreSchema) -> CoreSchema: + def strip_metadata(s: CoreSchema, recurse: Recurse) -> CoreSchema: + s = s.copy() + s.pop('metadata', None) + if s['type'] == 'model-fields': + s = s.copy() + s['fields'] = {k: v.copy() for k, v in s['fields'].items()} + for field_name, field_schema in s['fields'].items(): + field_schema.pop('metadata', None) + s['fields'][field_name] = field_schema + computed_fields = s.get('computed_fields', None) + if computed_fields: + s['computed_fields'] = [cf.copy() for cf in computed_fields] + for cf in computed_fields: + cf.pop('metadata', None) + else: + s.pop('computed_fields', None) + elif s['type'] == 'model': + # remove some defaults + if s.get('custom_init', True) is False: + s.pop('custom_init') + if s.get('root_model', True) is False: + s.pop('root_model') + if {'title'}.issuperset(s.get('config', {}).keys()): + s.pop('config', None) + + return recurse(s, strip_metadata) + + return walk_core_schema(schema, strip_metadata) + + +def pretty_print_core_schema( + schema: CoreSchema, + include_metadata: bool = False, +) -> None: + """Pretty print a CoreSchema using rich. + This is intended for debugging purposes. + + Args: + schema: The CoreSchema to print. + include_metadata: Whether to include metadata in the output. Defaults to `False`. + """ + from rich import print # type: ignore # install it manually in your dev env + + if not include_metadata: + schema = _strip_metadata(schema) + + return print(schema) + + +def validate_core_schema(schema: CoreSchema) -> CoreSchema: + if 'PYDANTIC_SKIP_VALIDATING_CORE_SCHEMAS' in os.environ: + return schema + return _validate_core_schema(schema) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_dataclasses.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_dataclasses.py new file mode 100644 index 0000000..db200f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_dataclasses.py @@ -0,0 +1,230 @@ +"""Private logic for creating pydantic dataclasses.""" +from __future__ import annotations as _annotations + +import dataclasses +import typing +import warnings +from functools import partial, wraps +from typing import Any, Callable, ClassVar + +from pydantic_core import ( + ArgsKwargs, + SchemaSerializer, + SchemaValidator, + core_schema, +) +from typing_extensions import TypeGuard + +from ..errors import PydanticUndefinedAnnotation +from ..fields import FieldInfo +from ..plugin._schema_validator import create_schema_validator +from ..warnings import PydanticDeprecatedSince20 +from . import _config, _decorators, _typing_extra +from ._fields import collect_dataclass_fields +from ._generate_schema import GenerateSchema +from ._generics import get_standard_typevars_map +from ._mock_val_ser import set_dataclass_mocks +from ._schema_generation_shared import CallbackGetCoreSchemaHandler +from ._signature import generate_pydantic_signature + +if typing.TYPE_CHECKING: + from ..config import ConfigDict + + class StandardDataclass(typing.Protocol): + __dataclass_fields__: ClassVar[dict[str, Any]] + __dataclass_params__: ClassVar[Any] # in reality `dataclasses._DataclassParams` + __post_init__: ClassVar[Callable[..., None]] + + def __init__(self, *args: object, **kwargs: object) -> None: + pass + + class PydanticDataclass(StandardDataclass, typing.Protocol): + """A protocol containing attributes only available once a class has been decorated as a Pydantic dataclass. + + Attributes: + __pydantic_config__: Pydantic-specific configuration settings for the dataclass. + __pydantic_complete__: Whether dataclass building is completed, or if there are still undefined fields. + __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer. + __pydantic_decorators__: Metadata containing the decorators defined on the dataclass. + __pydantic_fields__: Metadata about the fields defined on the dataclass. + __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the dataclass. + __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the dataclass. + """ + + __pydantic_config__: ClassVar[ConfigDict] + __pydantic_complete__: ClassVar[bool] + __pydantic_core_schema__: ClassVar[core_schema.CoreSchema] + __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] + __pydantic_fields__: ClassVar[dict[str, FieldInfo]] + __pydantic_serializer__: ClassVar[SchemaSerializer] + __pydantic_validator__: ClassVar[SchemaValidator] + +else: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + + +def set_dataclass_fields( + cls: type[StandardDataclass], + types_namespace: dict[str, Any] | None = None, + config_wrapper: _config.ConfigWrapper | None = None, +) -> None: + """Collect and set `cls.__pydantic_fields__`. + + Args: + cls: The class. + types_namespace: The types namespace, defaults to `None`. + config_wrapper: The config wrapper instance, defaults to `None`. + """ + typevars_map = get_standard_typevars_map(cls) + fields = collect_dataclass_fields(cls, types_namespace, typevars_map=typevars_map, config_wrapper=config_wrapper) + + cls.__pydantic_fields__ = fields # type: ignore + + +def complete_dataclass( + cls: type[Any], + config_wrapper: _config.ConfigWrapper, + *, + raise_errors: bool = True, + types_namespace: dict[str, Any] | None, +) -> bool: + """Finish building a pydantic dataclass. + + This logic is called on a class which has already been wrapped in `dataclasses.dataclass()`. + + This is somewhat analogous to `pydantic._internal._model_construction.complete_model_class`. + + Args: + cls: The class. + config_wrapper: The config wrapper instance. + raise_errors: Whether to raise errors, defaults to `True`. + types_namespace: The types namespace. + + Returns: + `True` if building a pydantic dataclass is successfully completed, `False` otherwise. + + Raises: + PydanticUndefinedAnnotation: If `raise_error` is `True` and there is an undefined annotations. + """ + if hasattr(cls, '__post_init_post_parse__'): + warnings.warn( + 'Support for `__post_init_post_parse__` has been dropped, the method will not be called', DeprecationWarning + ) + + if types_namespace is None: + types_namespace = _typing_extra.get_cls_types_namespace(cls) + + set_dataclass_fields(cls, types_namespace, config_wrapper=config_wrapper) + + typevars_map = get_standard_typevars_map(cls) + gen_schema = GenerateSchema( + config_wrapper, + types_namespace, + typevars_map, + ) + + # This needs to be called before we change the __init__ + sig = generate_pydantic_signature( + init=cls.__init__, + fields=cls.__pydantic_fields__, # type: ignore + config_wrapper=config_wrapper, + is_dataclass=True, + ) + + # dataclass.__init__ must be defined here so its `__qualname__` can be changed since functions can't be copied. + def __init__(__dataclass_self__: PydanticDataclass, *args: Any, **kwargs: Any) -> None: + __tracebackhide__ = True + s = __dataclass_self__ + s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s) + + __init__.__qualname__ = f'{cls.__qualname__}.__init__' + + cls.__init__ = __init__ # type: ignore + cls.__pydantic_config__ = config_wrapper.config_dict # type: ignore + cls.__signature__ = sig # type: ignore + get_core_schema = getattr(cls, '__get_pydantic_core_schema__', None) + try: + if get_core_schema: + schema = get_core_schema( + cls, + CallbackGetCoreSchemaHandler( + partial(gen_schema.generate_schema, from_dunder_get_core_schema=False), + gen_schema, + ref_mode='unpack', + ), + ) + else: + schema = gen_schema.generate_schema(cls, from_dunder_get_core_schema=False) + except PydanticUndefinedAnnotation as e: + if raise_errors: + raise + set_dataclass_mocks(cls, cls.__name__, f'`{e.name}`') + return False + + core_config = config_wrapper.core_config(cls) + + try: + schema = gen_schema.clean_schema(schema) + except gen_schema.CollectedInvalid: + set_dataclass_mocks(cls, cls.__name__, 'all referenced types') + return False + + # We are about to set all the remaining required properties expected for this cast; + # __pydantic_decorators__ and __pydantic_fields__ should already be set + cls = typing.cast('type[PydanticDataclass]', cls) + # debug(schema) + + cls.__pydantic_core_schema__ = schema + cls.__pydantic_validator__ = validator = create_schema_validator( + schema, cls, cls.__module__, cls.__qualname__, 'dataclass', core_config, config_wrapper.plugin_settings + ) + cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config) + + if config_wrapper.validate_assignment: + + @wraps(cls.__setattr__) + def validated_setattr(instance: Any, field: str, value: str, /) -> None: + validator.validate_assignment(instance, field, value) + + cls.__setattr__ = validated_setattr.__get__(None, cls) # type: ignore + + return True + + +def is_builtin_dataclass(_cls: type[Any]) -> TypeGuard[type[StandardDataclass]]: + """Returns True if a class is a stdlib dataclass and *not* a pydantic dataclass. + + We check that + - `_cls` is a dataclass + - `_cls` does not inherit from a processed pydantic dataclass (and thus have a `__pydantic_validator__`) + - `_cls` does not have any annotations that are not dataclass fields + e.g. + ```py + import dataclasses + + import pydantic.dataclasses + + @dataclasses.dataclass + class A: + x: int + + @pydantic.dataclasses.dataclass + class B(A): + y: int + ``` + In this case, when we first check `B`, we make an extra check and look at the annotations ('y'), + which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x') + + Args: + cls: The class. + + Returns: + `True` if the class is a stdlib dataclass, `False` otherwise. + """ + return ( + dataclasses.is_dataclass(_cls) + and not hasattr(_cls, '__pydantic_validator__') + and set(_cls.__dataclass_fields__).issuperset(set(getattr(_cls, '__annotations__', {}))) + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators.py new file mode 100644 index 0000000..8507e4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators.py @@ -0,0 +1,791 @@ +"""Logic related to validators applied to models etc. via the `@field_validator` and `@model_validator` decorators.""" +from __future__ import annotations as _annotations + +from collections import deque +from dataclasses import dataclass, field +from functools import cached_property, partial, partialmethod +from inspect import Parameter, Signature, isdatadescriptor, ismethoddescriptor, signature +from itertools import islice +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Iterable, TypeVar, Union + +from pydantic_core import PydanticUndefined, core_schema +from typing_extensions import Literal, TypeAlias, is_typeddict + +from ..errors import PydanticUserError +from ._core_utils import get_type_ref +from ._internal_dataclass import slots_true +from ._typing_extra import get_function_type_hints + +if TYPE_CHECKING: + from ..fields import ComputedFieldInfo + from ..functional_validators import FieldValidatorModes + + +@dataclass(**slots_true) +class ValidatorDecoratorInfo: + """A container for data from `@validator` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@validator'. + fields: A tuple of field names the validator should be called on. + mode: The proposed validator mode. + each_item: For complex objects (sets, lists etc.) whether to validate individual + elements rather than the whole object. + always: Whether this method and other validators should be called even if the value is missing. + check_fields: Whether to check that the fields actually exist on the model. + """ + + decorator_repr: ClassVar[str] = '@validator' + + fields: tuple[str, ...] + mode: Literal['before', 'after'] + each_item: bool + always: bool + check_fields: bool | None + + +@dataclass(**slots_true) +class FieldValidatorDecoratorInfo: + """A container for data from `@field_validator` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@field_validator'. + fields: A tuple of field names the validator should be called on. + mode: The proposed validator mode. + check_fields: Whether to check that the fields actually exist on the model. + """ + + decorator_repr: ClassVar[str] = '@field_validator' + + fields: tuple[str, ...] + mode: FieldValidatorModes + check_fields: bool | None + + +@dataclass(**slots_true) +class RootValidatorDecoratorInfo: + """A container for data from `@root_validator` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@root_validator'. + mode: The proposed validator mode. + """ + + decorator_repr: ClassVar[str] = '@root_validator' + mode: Literal['before', 'after'] + + +@dataclass(**slots_true) +class FieldSerializerDecoratorInfo: + """A container for data from `@field_serializer` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@field_serializer'. + fields: A tuple of field names the serializer should be called on. + mode: The proposed serializer mode. + return_type: The type of the serializer's return value. + when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`, + and `'json-unless-none'`. + check_fields: Whether to check that the fields actually exist on the model. + """ + + decorator_repr: ClassVar[str] = '@field_serializer' + fields: tuple[str, ...] + mode: Literal['plain', 'wrap'] + return_type: Any + when_used: core_schema.WhenUsed + check_fields: bool | None + + +@dataclass(**slots_true) +class ModelSerializerDecoratorInfo: + """A container for data from `@model_serializer` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@model_serializer'. + mode: The proposed serializer mode. + return_type: The type of the serializer's return value. + when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`, + and `'json-unless-none'`. + """ + + decorator_repr: ClassVar[str] = '@model_serializer' + mode: Literal['plain', 'wrap'] + return_type: Any + when_used: core_schema.WhenUsed + + +@dataclass(**slots_true) +class ModelValidatorDecoratorInfo: + """A container for data from `@model_validator` so that we can access it + while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@model_serializer'. + mode: The proposed serializer mode. + """ + + decorator_repr: ClassVar[str] = '@model_validator' + mode: Literal['wrap', 'before', 'after'] + + +DecoratorInfo: TypeAlias = """Union[ + ValidatorDecoratorInfo, + FieldValidatorDecoratorInfo, + RootValidatorDecoratorInfo, + FieldSerializerDecoratorInfo, + ModelSerializerDecoratorInfo, + ModelValidatorDecoratorInfo, + ComputedFieldInfo, +]""" + +ReturnType = TypeVar('ReturnType') +DecoratedType: TypeAlias = ( + 'Union[classmethod[Any, Any, ReturnType], staticmethod[Any, ReturnType], Callable[..., ReturnType], property]' +) + + +@dataclass # can't use slots here since we set attributes on `__post_init__` +class PydanticDescriptorProxy(Generic[ReturnType]): + """Wrap a classmethod, staticmethod, property or unbound function + and act as a descriptor that allows us to detect decorated items + from the class' attributes. + + This class' __get__ returns the wrapped item's __get__ result, + which makes it transparent for classmethods and staticmethods. + + Attributes: + wrapped: The decorator that has to be wrapped. + decorator_info: The decorator info. + shim: A wrapper function to wrap V1 style function. + """ + + wrapped: DecoratedType[ReturnType] + decorator_info: DecoratorInfo + shim: Callable[[Callable[..., Any]], Callable[..., Any]] | None = None + + def __post_init__(self): + for attr in 'setter', 'deleter': + if hasattr(self.wrapped, attr): + f = partial(self._call_wrapped_attr, name=attr) + setattr(self, attr, f) + + def _call_wrapped_attr(self, func: Callable[[Any], None], *, name: str) -> PydanticDescriptorProxy[ReturnType]: + self.wrapped = getattr(self.wrapped, name)(func) + return self + + def __get__(self, obj: object | None, obj_type: type[object] | None = None) -> PydanticDescriptorProxy[ReturnType]: + try: + return self.wrapped.__get__(obj, obj_type) + except AttributeError: + # not a descriptor, e.g. a partial object + return self.wrapped # type: ignore[return-value] + + def __set_name__(self, instance: Any, name: str) -> None: + if hasattr(self.wrapped, '__set_name__'): + self.wrapped.__set_name__(instance, name) # pyright: ignore[reportFunctionMemberAccess] + + def __getattr__(self, __name: str) -> Any: + """Forward checks for __isabstractmethod__ and such.""" + return getattr(self.wrapped, __name) + + +DecoratorInfoType = TypeVar('DecoratorInfoType', bound=DecoratorInfo) + + +@dataclass(**slots_true) +class Decorator(Generic[DecoratorInfoType]): + """A generic container class to join together the decorator metadata + (metadata from decorator itself, which we have when the + decorator is called but not when we are building the core-schema) + and the bound function (which we have after the class itself is created). + + Attributes: + cls_ref: The class ref. + cls_var_name: The decorated function name. + func: The decorated function. + shim: A wrapper function to wrap V1 style function. + info: The decorator info. + """ + + cls_ref: str + cls_var_name: str + func: Callable[..., Any] + shim: Callable[[Any], Any] | None + info: DecoratorInfoType + + @staticmethod + def build( + cls_: Any, + *, + cls_var_name: str, + shim: Callable[[Any], Any] | None, + info: DecoratorInfoType, + ) -> Decorator[DecoratorInfoType]: + """Build a new decorator. + + Args: + cls_: The class. + cls_var_name: The decorated function name. + shim: A wrapper function to wrap V1 style function. + info: The decorator info. + + Returns: + The new decorator instance. + """ + func = get_attribute_from_bases(cls_, cls_var_name) + if shim is not None: + func = shim(func) + func = unwrap_wrapped_function(func, unwrap_partial=False) + if not callable(func): + # This branch will get hit for classmethod properties + attribute = get_attribute_from_base_dicts(cls_, cls_var_name) # prevents the binding call to `__get__` + if isinstance(attribute, PydanticDescriptorProxy): + func = unwrap_wrapped_function(attribute.wrapped) + return Decorator( + cls_ref=get_type_ref(cls_), + cls_var_name=cls_var_name, + func=func, + shim=shim, + info=info, + ) + + def bind_to_cls(self, cls: Any) -> Decorator[DecoratorInfoType]: + """Bind the decorator to a class. + + Args: + cls: the class. + + Returns: + The new decorator instance. + """ + return self.build( + cls, + cls_var_name=self.cls_var_name, + shim=self.shim, + info=self.info, + ) + + +def get_bases(tp: type[Any]) -> tuple[type[Any], ...]: + """Get the base classes of a class or typeddict. + + Args: + tp: The type or class to get the bases. + + Returns: + The base classes. + """ + if is_typeddict(tp): + return tp.__orig_bases__ # type: ignore + try: + return tp.__bases__ + except AttributeError: + return () + + +def mro(tp: type[Any]) -> tuple[type[Any], ...]: + """Calculate the Method Resolution Order of bases using the C3 algorithm. + + See https://www.python.org/download/releases/2.3/mro/ + """ + # try to use the existing mro, for performance mainly + # but also because it helps verify the implementation below + if not is_typeddict(tp): + try: + return tp.__mro__ + except AttributeError: + # GenericAlias and some other cases + pass + + bases = get_bases(tp) + return (tp,) + mro_for_bases(bases) + + +def mro_for_bases(bases: tuple[type[Any], ...]) -> tuple[type[Any], ...]: + def merge_seqs(seqs: list[deque[type[Any]]]) -> Iterable[type[Any]]: + while True: + non_empty = [seq for seq in seqs if seq] + if not non_empty: + # Nothing left to process, we're done. + return + candidate: type[Any] | None = None + for seq in non_empty: # Find merge candidates among seq heads. + candidate = seq[0] + not_head = [s for s in non_empty if candidate in islice(s, 1, None)] + if not_head: + # Reject the candidate. + candidate = None + else: + break + if not candidate: + raise TypeError('Inconsistent hierarchy, no C3 MRO is possible') + yield candidate + for seq in non_empty: + # Remove candidate. + if seq[0] == candidate: + seq.popleft() + + seqs = [deque(mro(base)) for base in bases] + [deque(bases)] + return tuple(merge_seqs(seqs)) + + +_sentinel = object() + + +def get_attribute_from_bases(tp: type[Any] | tuple[type[Any], ...], name: str) -> Any: + """Get the attribute from the next class in the MRO that has it, + aiming to simulate calling the method on the actual class. + + The reason for iterating over the mro instead of just getting + the attribute (which would do that for us) is to support TypedDict, + which lacks a real __mro__, but can have a virtual one constructed + from its bases (as done here). + + Args: + tp: The type or class to search for the attribute. If a tuple, this is treated as a set of base classes. + name: The name of the attribute to retrieve. + + Returns: + Any: The attribute value, if found. + + Raises: + AttributeError: If the attribute is not found in any class in the MRO. + """ + if isinstance(tp, tuple): + for base in mro_for_bases(tp): + attribute = base.__dict__.get(name, _sentinel) + if attribute is not _sentinel: + attribute_get = getattr(attribute, '__get__', None) + if attribute_get is not None: + return attribute_get(None, tp) + return attribute + raise AttributeError(f'{name} not found in {tp}') + else: + try: + return getattr(tp, name) + except AttributeError: + return get_attribute_from_bases(mro(tp), name) + + +def get_attribute_from_base_dicts(tp: type[Any], name: str) -> Any: + """Get an attribute out of the `__dict__` following the MRO. + This prevents the call to `__get__` on the descriptor, and allows + us to get the original function for classmethod properties. + + Args: + tp: The type or class to search for the attribute. + name: The name of the attribute to retrieve. + + Returns: + Any: The attribute value, if found. + + Raises: + KeyError: If the attribute is not found in any class's `__dict__` in the MRO. + """ + for base in reversed(mro(tp)): + if name in base.__dict__: + return base.__dict__[name] + return tp.__dict__[name] # raise the error + + +@dataclass(**slots_true) +class DecoratorInfos: + """Mapping of name in the class namespace to decorator info. + + note that the name in the class namespace is the function or attribute name + not the field name! + """ + + validators: dict[str, Decorator[ValidatorDecoratorInfo]] = field(default_factory=dict) + field_validators: dict[str, Decorator[FieldValidatorDecoratorInfo]] = field(default_factory=dict) + root_validators: dict[str, Decorator[RootValidatorDecoratorInfo]] = field(default_factory=dict) + field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]] = field(default_factory=dict) + model_serializers: dict[str, Decorator[ModelSerializerDecoratorInfo]] = field(default_factory=dict) + model_validators: dict[str, Decorator[ModelValidatorDecoratorInfo]] = field(default_factory=dict) + computed_fields: dict[str, Decorator[ComputedFieldInfo]] = field(default_factory=dict) + + @staticmethod + def build(model_dc: type[Any]) -> DecoratorInfos: # noqa: C901 (ignore complexity) + """We want to collect all DecFunc instances that exist as + attributes in the namespace of the class (a BaseModel or dataclass) + that called us + But we want to collect these in the order of the bases + So instead of getting them all from the leaf class (the class that called us), + we traverse the bases from root (the oldest ancestor class) to leaf + and collect all of the instances as we go, taking care to replace + any duplicate ones with the last one we see to mimic how function overriding + works with inheritance. + If we do replace any functions we put the replacement into the position + the replaced function was in; that is, we maintain the order. + """ + # reminder: dicts are ordered and replacement does not alter the order + res = DecoratorInfos() + for base in reversed(mro(model_dc)[1:]): + existing: DecoratorInfos | None = base.__dict__.get('__pydantic_decorators__') + if existing is None: + existing = DecoratorInfos.build(base) + res.validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.validators.items()}) + res.field_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_validators.items()}) + res.root_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.root_validators.items()}) + res.field_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.field_serializers.items()}) + res.model_serializers.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_serializers.items()}) + res.model_validators.update({k: v.bind_to_cls(model_dc) for k, v in existing.model_validators.items()}) + res.computed_fields.update({k: v.bind_to_cls(model_dc) for k, v in existing.computed_fields.items()}) + + to_replace: list[tuple[str, Any]] = [] + + for var_name, var_value in vars(model_dc).items(): + if isinstance(var_value, PydanticDescriptorProxy): + info = var_value.decorator_info + if isinstance(info, ValidatorDecoratorInfo): + res.validators[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + elif isinstance(info, FieldValidatorDecoratorInfo): + res.field_validators[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + elif isinstance(info, RootValidatorDecoratorInfo): + res.root_validators[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + elif isinstance(info, FieldSerializerDecoratorInfo): + # check whether a serializer function is already registered for fields + for field_serializer_decorator in res.field_serializers.values(): + # check that each field has at most one serializer function. + # serializer functions for the same field in subclasses are allowed, + # and are treated as overrides + if field_serializer_decorator.cls_var_name == var_name: + continue + for f in info.fields: + if f in field_serializer_decorator.info.fields: + raise PydanticUserError( + 'Multiple field serializer functions were defined ' + f'for field {f!r}, this is not allowed.', + code='multiple-field-serializers', + ) + res.field_serializers[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + elif isinstance(info, ModelValidatorDecoratorInfo): + res.model_validators[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + elif isinstance(info, ModelSerializerDecoratorInfo): + res.model_serializers[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=var_value.shim, info=info + ) + else: + from ..fields import ComputedFieldInfo + + isinstance(var_value, ComputedFieldInfo) + res.computed_fields[var_name] = Decorator.build( + model_dc, cls_var_name=var_name, shim=None, info=info + ) + to_replace.append((var_name, var_value.wrapped)) + if to_replace: + # If we can save `__pydantic_decorators__` on the class we'll be able to check for it above + # so then we don't need to re-process the type, which means we can discard our descriptor wrappers + # and replace them with the thing they are wrapping (see the other setattr call below) + # which allows validator class methods to also function as regular class methods + setattr(model_dc, '__pydantic_decorators__', res) + for name, value in to_replace: + setattr(model_dc, name, value) + return res + + +def inspect_validator(validator: Callable[..., Any], mode: FieldValidatorModes) -> bool: + """Look at a field or model validator function and determine whether it takes an info argument. + + An error is raised if the function has an invalid signature. + + Args: + validator: The validator function to inspect. + mode: The proposed validator mode. + + Returns: + Whether the validator takes an info argument. + """ + try: + sig = signature(validator) + except ValueError: + # builtins and some C extensions don't have signatures + # assume that they don't take an info argument and only take a single argument + # e.g. `str.strip` or `datetime.datetime` + return False + n_positional = count_positional_params(sig) + if mode == 'wrap': + if n_positional == 3: + return True + elif n_positional == 2: + return False + else: + assert mode in {'before', 'after', 'plain'}, f"invalid mode: {mode!r}, expected 'before', 'after' or 'plain" + if n_positional == 2: + return True + elif n_positional == 1: + return False + + raise PydanticUserError( + f'Unrecognized field_validator function signature for {validator} with `mode={mode}`:{sig}', + code='validator-signature', + ) + + +def inspect_field_serializer( + serializer: Callable[..., Any], mode: Literal['plain', 'wrap'], computed_field: bool = False +) -> tuple[bool, bool]: + """Look at a field serializer function and determine if it is a field serializer, + and whether it takes an info argument. + + An error is raised if the function has an invalid signature. + + Args: + serializer: The serializer function to inspect. + mode: The serializer mode, either 'plain' or 'wrap'. + computed_field: When serializer is applied on computed_field. It doesn't require + info signature. + + Returns: + Tuple of (is_field_serializer, info_arg). + """ + sig = signature(serializer) + + first = next(iter(sig.parameters.values()), None) + is_field_serializer = first is not None and first.name == 'self' + + n_positional = count_positional_params(sig) + if is_field_serializer: + # -1 to correct for self parameter + info_arg = _serializer_info_arg(mode, n_positional - 1) + else: + info_arg = _serializer_info_arg(mode, n_positional) + + if info_arg is None: + raise PydanticUserError( + f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}', + code='field-serializer-signature', + ) + if info_arg and computed_field: + raise PydanticUserError( + 'field_serializer on computed_field does not use info signature', code='field-serializer-signature' + ) + + else: + return is_field_serializer, info_arg + + +def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: + """Look at a serializer function used via `Annotated` and determine whether it takes an info argument. + + An error is raised if the function has an invalid signature. + + Args: + serializer: The serializer function to check. + mode: The serializer mode, either 'plain' or 'wrap'. + + Returns: + info_arg + """ + sig = signature(serializer) + info_arg = _serializer_info_arg(mode, count_positional_params(sig)) + if info_arg is None: + raise PydanticUserError( + f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}', + code='field-serializer-signature', + ) + else: + return info_arg + + +def inspect_model_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool: + """Look at a model serializer function and determine whether it takes an info argument. + + An error is raised if the function has an invalid signature. + + Args: + serializer: The serializer function to check. + mode: The serializer mode, either 'plain' or 'wrap'. + + Returns: + `info_arg` - whether the function expects an info argument. + """ + if isinstance(serializer, (staticmethod, classmethod)) or not is_instance_method_from_sig(serializer): + raise PydanticUserError( + '`@model_serializer` must be applied to instance methods', code='model-serializer-instance-method' + ) + + sig = signature(serializer) + info_arg = _serializer_info_arg(mode, count_positional_params(sig)) + if info_arg is None: + raise PydanticUserError( + f'Unrecognized model_serializer function signature for {serializer} with `mode={mode}`:{sig}', + code='model-serializer-signature', + ) + else: + return info_arg + + +def _serializer_info_arg(mode: Literal['plain', 'wrap'], n_positional: int) -> bool | None: + if mode == 'plain': + if n_positional == 1: + # (input_value: Any, /) -> Any + return False + elif n_positional == 2: + # (model: Any, input_value: Any, /) -> Any + return True + else: + assert mode == 'wrap', f"invalid mode: {mode!r}, expected 'plain' or 'wrap'" + if n_positional == 2: + # (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any + return False + elif n_positional == 3: + # (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any + return True + + return None + + +AnyDecoratorCallable: TypeAlias = ( + 'Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any], Callable[..., Any]]' +) + + +def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool: + """Whether the function is an instance method. + + It will consider a function as instance method if the first parameter of + function is `self`. + + Args: + function: The function to check. + + Returns: + `True` if the function is an instance method, `False` otherwise. + """ + sig = signature(unwrap_wrapped_function(function)) + first = next(iter(sig.parameters.values()), None) + if first and first.name == 'self': + return True + return False + + +def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any: + """Apply the `@classmethod` decorator on the function. + + Args: + function: The function to apply the decorator on. + + Return: + The `@classmethod` decorator applied function. + """ + if not isinstance( + unwrap_wrapped_function(function, unwrap_class_static_method=False), classmethod + ) and _is_classmethod_from_sig(function): + return classmethod(function) # type: ignore[arg-type] + return function + + +def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool: + sig = signature(unwrap_wrapped_function(function)) + first = next(iter(sig.parameters.values()), None) + if first and first.name == 'cls': + return True + return False + + +def unwrap_wrapped_function( + func: Any, + *, + unwrap_partial: bool = True, + unwrap_class_static_method: bool = True, +) -> Any: + """Recursively unwraps a wrapped function until the underlying function is reached. + This handles property, functools.partial, functools.partialmethod, staticmethod and classmethod. + + Args: + func: The function to unwrap. + unwrap_partial: If True (default), unwrap partial and partialmethod decorators, otherwise don't. + decorators. + unwrap_class_static_method: If True (default), also unwrap classmethod and staticmethod + decorators. If False, only unwrap partial and partialmethod decorators. + + Returns: + The underlying function of the wrapped function. + """ + all: set[Any] = {property, cached_property} + + if unwrap_partial: + all.update({partial, partialmethod}) + + if unwrap_class_static_method: + all.update({staticmethod, classmethod}) + + while isinstance(func, tuple(all)): + if unwrap_class_static_method and isinstance(func, (classmethod, staticmethod)): + func = func.__func__ + elif isinstance(func, (partial, partialmethod)): + func = func.func + elif isinstance(func, property): + func = func.fget # arbitrary choice, convenient for computed fields + else: + # Make coverage happy as it can only get here in the last possible case + assert isinstance(func, cached_property) + func = func.func # type: ignore + + return func + + +def get_function_return_type( + func: Any, explicit_return_type: Any, types_namespace: dict[str, Any] | None = None +) -> Any: + """Get the function return type. + + It gets the return type from the type annotation if `explicit_return_type` is `None`. + Otherwise, it returns `explicit_return_type`. + + Args: + func: The function to get its return type. + explicit_return_type: The explicit return type. + types_namespace: The types namespace, defaults to `None`. + + Returns: + The function return type. + """ + if explicit_return_type is PydanticUndefined: + # try to get it from the type annotation + hints = get_function_type_hints( + unwrap_wrapped_function(func), include_keys={'return'}, types_namespace=types_namespace + ) + return hints.get('return', PydanticUndefined) + else: + return explicit_return_type + + +def count_positional_params(sig: Signature) -> int: + return sum(1 for param in sig.parameters.values() if can_be_positional(param)) + + +def can_be_positional(param: Parameter) -> bool: + return param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) + + +def ensure_property(f: Any) -> Any: + """Ensure that a function is a `property` or `cached_property`, or is a valid descriptor. + + Args: + f: The function to check. + + Returns: + The function, or a `property` or `cached_property` instance wrapping the function. + """ + if ismethoddescriptor(f) or isdatadescriptor(f): + return f + else: + return property(f) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators_v1.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators_v1.py new file mode 100644 index 0000000..4f81e6d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_decorators_v1.py @@ -0,0 +1,181 @@ +"""Logic for V1 validators, e.g. `@validator` and `@root_validator`.""" +from __future__ import annotations as _annotations + +from inspect import Parameter, signature +from typing import Any, Dict, Tuple, Union, cast + +from pydantic_core import core_schema +from typing_extensions import Protocol + +from ..errors import PydanticUserError +from ._decorators import can_be_positional + + +class V1OnlyValueValidator(Protocol): + """A simple validator, supported for V1 validators and V2 validators.""" + + def __call__(self, __value: Any) -> Any: + ... + + +class V1ValidatorWithValues(Protocol): + """A validator with `values` argument, supported for V1 validators and V2 validators.""" + + def __call__(self, __value: Any, values: dict[str, Any]) -> Any: + ... + + +class V1ValidatorWithValuesKwOnly(Protocol): + """A validator with keyword only `values` argument, supported for V1 validators and V2 validators.""" + + def __call__(self, __value: Any, *, values: dict[str, Any]) -> Any: + ... + + +class V1ValidatorWithKwargs(Protocol): + """A validator with `kwargs` argument, supported for V1 validators and V2 validators.""" + + def __call__(self, __value: Any, **kwargs: Any) -> Any: + ... + + +class V1ValidatorWithValuesAndKwargs(Protocol): + """A validator with `values` and `kwargs` arguments, supported for V1 validators and V2 validators.""" + + def __call__(self, __value: Any, values: dict[str, Any], **kwargs: Any) -> Any: + ... + + +V1Validator = Union[ + V1ValidatorWithValues, V1ValidatorWithValuesKwOnly, V1ValidatorWithKwargs, V1ValidatorWithValuesAndKwargs +] + + +def can_be_keyword(param: Parameter) -> bool: + return param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) + + +def make_generic_v1_field_validator(validator: V1Validator) -> core_schema.WithInfoValidatorFunction: + """Wrap a V1 style field validator for V2 compatibility. + + Args: + validator: The V1 style field validator. + + Returns: + A wrapped V2 style field validator. + + Raises: + PydanticUserError: If the signature is not supported or the parameters are + not available in Pydantic V2. + """ + sig = signature(validator) + + needs_values_kw = False + + for param_num, (param_name, parameter) in enumerate(sig.parameters.items()): + if can_be_keyword(parameter) and param_name in ('field', 'config'): + raise PydanticUserError( + 'The `field` and `config` parameters are not available in Pydantic V2, ' + 'please use the `info` parameter instead.', + code='validator-field-config-info', + ) + if parameter.kind is Parameter.VAR_KEYWORD: + needs_values_kw = True + elif can_be_keyword(parameter) and param_name == 'values': + needs_values_kw = True + elif can_be_positional(parameter) and param_num == 0: + # value + continue + elif parameter.default is Parameter.empty: # ignore params with defaults e.g. bound by functools.partial + raise PydanticUserError( + f'Unsupported signature for V1 style validator {validator}: {sig} is not supported.', + code='validator-v1-signature', + ) + + if needs_values_kw: + # (v, **kwargs), (v, values, **kwargs), (v, *, values, **kwargs) or (v, *, values) + val1 = cast(V1ValidatorWithValues, validator) + + def wrapper1(value: Any, info: core_schema.ValidationInfo) -> Any: + return val1(value, values=info.data) + + return wrapper1 + else: + val2 = cast(V1OnlyValueValidator, validator) + + def wrapper2(value: Any, _: core_schema.ValidationInfo) -> Any: + return val2(value) + + return wrapper2 + + +RootValidatorValues = Dict[str, Any] +# technically tuple[model_dict, model_extra, fields_set] | tuple[dataclass_dict, init_vars] +RootValidatorFieldsTuple = Tuple[Any, ...] + + +class V1RootValidatorFunction(Protocol): + """A simple root validator, supported for V1 validators and V2 validators.""" + + def __call__(self, __values: RootValidatorValues) -> RootValidatorValues: + ... + + +class V2CoreBeforeRootValidator(Protocol): + """V2 validator with mode='before'.""" + + def __call__(self, __values: RootValidatorValues, __info: core_schema.ValidationInfo) -> RootValidatorValues: + ... + + +class V2CoreAfterRootValidator(Protocol): + """V2 validator with mode='after'.""" + + def __call__( + self, __fields_tuple: RootValidatorFieldsTuple, __info: core_schema.ValidationInfo + ) -> RootValidatorFieldsTuple: + ... + + +def make_v1_generic_root_validator( + validator: V1RootValidatorFunction, pre: bool +) -> V2CoreBeforeRootValidator | V2CoreAfterRootValidator: + """Wrap a V1 style root validator for V2 compatibility. + + Args: + validator: The V1 style field validator. + pre: Whether the validator is a pre validator. + + Returns: + A wrapped V2 style validator. + """ + if pre is True: + # mode='before' for pydantic-core + def _wrapper1(values: RootValidatorValues, _: core_schema.ValidationInfo) -> RootValidatorValues: + return validator(values) + + return _wrapper1 + + # mode='after' for pydantic-core + def _wrapper2(fields_tuple: RootValidatorFieldsTuple, _: core_schema.ValidationInfo) -> RootValidatorFieldsTuple: + if len(fields_tuple) == 2: + # dataclass, this is easy + values, init_vars = fields_tuple + values = validator(values) + return values, init_vars + else: + # ugly hack: to match v1 behaviour, we merge values and model_extra, then split them up based on fields + # afterwards + model_dict, model_extra, fields_set = fields_tuple + if model_extra: + fields = set(model_dict.keys()) + model_dict.update(model_extra) + model_dict_new = validator(model_dict) + for k in list(model_dict_new.keys()): + if k not in fields: + model_extra[k] = model_dict_new.pop(k) + else: + model_dict_new = validator(model_dict) + return model_dict_new, model_extra, fields_set + + return _wrapper2 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_discriminated_union.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_discriminated_union.py new file mode 100644 index 0000000..a090c36 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_discriminated_union.py @@ -0,0 +1,503 @@ +from __future__ import annotations as _annotations + +from typing import TYPE_CHECKING, Any, Hashable, Sequence + +from pydantic_core import CoreSchema, core_schema + +from ..errors import PydanticUserError +from . import _core_utils +from ._core_utils import ( + CoreSchemaField, + collect_definitions, +) + +if TYPE_CHECKING: + from ..types import Discriminator + +CORE_SCHEMA_METADATA_DISCRIMINATOR_PLACEHOLDER_KEY = 'pydantic.internal.union_discriminator' + + +class MissingDefinitionForUnionRef(Exception): + """Raised when applying a discriminated union discriminator to a schema + requires a definition that is not yet defined + """ + + def __init__(self, ref: str) -> None: + self.ref = ref + super().__init__(f'Missing definition for ref {self.ref!r}') + + +def set_discriminator_in_metadata(schema: CoreSchema, discriminator: Any) -> None: + schema.setdefault('metadata', {}) + metadata = schema.get('metadata') + assert metadata is not None + metadata[CORE_SCHEMA_METADATA_DISCRIMINATOR_PLACEHOLDER_KEY] = discriminator + + +def apply_discriminators(schema: core_schema.CoreSchema) -> core_schema.CoreSchema: + # We recursively walk through the `schema` passed to `apply_discriminators`, applying discriminators + # where necessary at each level. During this recursion, we allow references to be resolved from the definitions + # that are originally present on the original, outermost `schema`. Before `apply_discriminators` is called, + # `simplify_schema_references` is called on the schema (in the `clean_schema` function), + # which often puts the definitions in the outermost schema. + global_definitions: dict[str, CoreSchema] = collect_definitions(schema) + + def inner(s: core_schema.CoreSchema, recurse: _core_utils.Recurse) -> core_schema.CoreSchema: + nonlocal global_definitions + + s = recurse(s, inner) + if s['type'] == 'tagged-union': + return s + + metadata = s.get('metadata', {}) + discriminator = metadata.pop(CORE_SCHEMA_METADATA_DISCRIMINATOR_PLACEHOLDER_KEY, None) + if discriminator is not None: + s = apply_discriminator(s, discriminator, global_definitions) + return s + + return _core_utils.walk_core_schema(schema, inner) + + +def apply_discriminator( + schema: core_schema.CoreSchema, + discriminator: str | Discriminator, + definitions: dict[str, core_schema.CoreSchema] | None = None, +) -> core_schema.CoreSchema: + """Applies the discriminator and returns a new core schema. + + Args: + schema: The input schema. + discriminator: The name of the field which will serve as the discriminator. + definitions: A mapping of schema ref to schema. + + Returns: + The new core schema. + + Raises: + TypeError: + - If `discriminator` is used with invalid union variant. + - If `discriminator` is used with `Union` type with one variant. + - If `discriminator` value mapped to multiple choices. + MissingDefinitionForUnionRef: + If the definition for ref is missing. + PydanticUserError: + - If a model in union doesn't have a discriminator field. + - If discriminator field has a non-string alias. + - If discriminator fields have different aliases. + - If discriminator field not of type `Literal`. + """ + from ..types import Discriminator + + if isinstance(discriminator, Discriminator): + if isinstance(discriminator.discriminator, str): + discriminator = discriminator.discriminator + else: + return discriminator._convert_schema(schema) + + return _ApplyInferredDiscriminator(discriminator, definitions or {}).apply(schema) + + +class _ApplyInferredDiscriminator: + """This class is used to convert an input schema containing a union schema into one where that union is + replaced with a tagged-union, with all the associated debugging and performance benefits. + + This is done by: + * Validating that the input schema is compatible with the provided discriminator + * Introspecting the schema to determine which discriminator values should map to which union choices + * Handling various edge cases such as 'definitions', 'default', 'nullable' schemas, and more + + I have chosen to implement the conversion algorithm in this class, rather than a function, + to make it easier to maintain state while recursively walking the provided CoreSchema. + """ + + def __init__(self, discriminator: str, definitions: dict[str, core_schema.CoreSchema]): + # `discriminator` should be the name of the field which will serve as the discriminator. + # It must be the python name of the field, and *not* the field's alias. Note that as of now, + # all members of a discriminated union _must_ use a field with the same name as the discriminator. + # This may change if/when we expose a way to manually specify the TaggedUnionSchema's choices. + self.discriminator = discriminator + + # `definitions` should contain a mapping of schema ref to schema for all schemas which might + # be referenced by some choice + self.definitions = definitions + + # `_discriminator_alias` will hold the value, if present, of the alias for the discriminator + # + # Note: following the v1 implementation, we currently disallow the use of different aliases + # for different choices. This is not a limitation of pydantic_core, but if we try to handle + # this, the inference logic gets complicated very quickly, and could result in confusing + # debugging challenges for users making subtle mistakes. + # + # Rather than trying to do the most powerful inference possible, I think we should eventually + # expose a way to more-manually control the way the TaggedUnionSchema is constructed through + # the use of a new type which would be placed as an Annotation on the Union type. This would + # provide the full flexibility/power of pydantic_core's TaggedUnionSchema where necessary for + # more complex cases, without over-complicating the inference logic for the common cases. + self._discriminator_alias: str | None = None + + # `_should_be_nullable` indicates whether the converted union has `None` as an allowed value. + # If `None` is an acceptable value of the (possibly-wrapped) union, we ignore it while + # constructing the TaggedUnionSchema, but set the `_should_be_nullable` attribute to True. + # Once we have constructed the TaggedUnionSchema, if `_should_be_nullable` is True, we ensure + # that the final schema gets wrapped as a NullableSchema. This has the same semantics on the + # python side, but resolves the issue that `None` cannot correspond to any discriminator values. + self._should_be_nullable = False + + # `_is_nullable` is used to track if the final produced schema will definitely be nullable; + # we set it to True if the input schema is wrapped in a nullable schema that we know will be preserved + # as an indication that, even if None is discovered as one of the union choices, we will not need to wrap + # the final value in another nullable schema. + # + # This is more complicated than just checking for the final outermost schema having type 'nullable' thanks + # to the possible presence of other wrapper schemas such as DefinitionsSchema, WithDefaultSchema, etc. + self._is_nullable = False + + # `_choices_to_handle` serves as a stack of choices to add to the tagged union. Initially, choices + # from the union in the wrapped schema will be appended to this list, and the recursive choice-handling + # algorithm may add more choices to this stack as (nested) unions are encountered. + self._choices_to_handle: list[core_schema.CoreSchema] = [] + + # `_tagged_union_choices` is built during the call to `apply`, and will hold the choices to be included + # in the output TaggedUnionSchema that will replace the union from the input schema + self._tagged_union_choices: dict[Hashable, core_schema.CoreSchema] = {} + + # `_used` is changed to True after applying the discriminator to prevent accidental re-use + self._used = False + + def apply(self, schema: core_schema.CoreSchema) -> core_schema.CoreSchema: + """Return a new CoreSchema based on `schema` that uses a tagged-union with the discriminator provided + to this class. + + Args: + schema: The input schema. + + Returns: + The new core schema. + + Raises: + TypeError: + - If `discriminator` is used with invalid union variant. + - If `discriminator` is used with `Union` type with one variant. + - If `discriminator` value mapped to multiple choices. + ValueError: + If the definition for ref is missing. + PydanticUserError: + - If a model in union doesn't have a discriminator field. + - If discriminator field has a non-string alias. + - If discriminator fields have different aliases. + - If discriminator field not of type `Literal`. + """ + assert not self._used + schema = self._apply_to_root(schema) + if self._should_be_nullable and not self._is_nullable: + schema = core_schema.nullable_schema(schema) + self._used = True + return schema + + def _apply_to_root(self, schema: core_schema.CoreSchema) -> core_schema.CoreSchema: + """This method handles the outer-most stage of recursion over the input schema: + unwrapping nullable or definitions schemas, and calling the `_handle_choice` + method iteratively on the choices extracted (recursively) from the possibly-wrapped union. + """ + if schema['type'] == 'nullable': + self._is_nullable = True + wrapped = self._apply_to_root(schema['schema']) + nullable_wrapper = schema.copy() + nullable_wrapper['schema'] = wrapped + return nullable_wrapper + + if schema['type'] == 'definitions': + wrapped = self._apply_to_root(schema['schema']) + definitions_wrapper = schema.copy() + definitions_wrapper['schema'] = wrapped + return definitions_wrapper + + if schema['type'] != 'union': + # If the schema is not a union, it probably means it just had a single member and + # was flattened by pydantic_core. + # However, it still may make sense to apply the discriminator to this schema, + # as a way to get discriminated-union-style error messages, so we allow this here. + schema = core_schema.union_schema([schema]) + + # Reverse the choices list before extending the stack so that they get handled in the order they occur + choices_schemas = [v[0] if isinstance(v, tuple) else v for v in schema['choices'][::-1]] + self._choices_to_handle.extend(choices_schemas) + while self._choices_to_handle: + choice = self._choices_to_handle.pop() + self._handle_choice(choice) + + if self._discriminator_alias is not None and self._discriminator_alias != self.discriminator: + # * We need to annotate `discriminator` as a union here to handle both branches of this conditional + # * We need to annotate `discriminator` as list[list[str | int]] and not list[list[str]] due to the + # invariance of list, and because list[list[str | int]] is the type of the discriminator argument + # to tagged_union_schema below + # * See the docstring of pydantic_core.core_schema.tagged_union_schema for more details about how to + # interpret the value of the discriminator argument to tagged_union_schema. (The list[list[str]] here + # is the appropriate way to provide a list of fallback attributes to check for a discriminator value.) + discriminator: str | list[list[str | int]] = [[self.discriminator], [self._discriminator_alias]] + else: + discriminator = self.discriminator + return core_schema.tagged_union_schema( + choices=self._tagged_union_choices, + discriminator=discriminator, + custom_error_type=schema.get('custom_error_type'), + custom_error_message=schema.get('custom_error_message'), + custom_error_context=schema.get('custom_error_context'), + strict=False, + from_attributes=True, + ref=schema.get('ref'), + metadata=schema.get('metadata'), + serialization=schema.get('serialization'), + ) + + def _handle_choice(self, choice: core_schema.CoreSchema) -> None: + """This method handles the "middle" stage of recursion over the input schema. + Specifically, it is responsible for handling each choice of the outermost union + (and any "coalesced" choices obtained from inner unions). + + Here, "handling" entails: + * Coalescing nested unions and compatible tagged-unions + * Tracking the presence of 'none' and 'nullable' schemas occurring as choices + * Validating that each allowed discriminator value maps to a unique choice + * Updating the _tagged_union_choices mapping that will ultimately be used to build the TaggedUnionSchema. + """ + if choice['type'] == 'definition-ref': + if choice['schema_ref'] not in self.definitions: + raise MissingDefinitionForUnionRef(choice['schema_ref']) + + if choice['type'] == 'none': + self._should_be_nullable = True + elif choice['type'] == 'definitions': + self._handle_choice(choice['schema']) + elif choice['type'] == 'nullable': + self._should_be_nullable = True + self._handle_choice(choice['schema']) # unwrap the nullable schema + elif choice['type'] == 'union': + # Reverse the choices list before extending the stack so that they get handled in the order they occur + choices_schemas = [v[0] if isinstance(v, tuple) else v for v in choice['choices'][::-1]] + self._choices_to_handle.extend(choices_schemas) + elif choice['type'] not in { + 'model', + 'typed-dict', + 'tagged-union', + 'lax-or-strict', + 'dataclass', + 'dataclass-args', + 'definition-ref', + } and not _core_utils.is_function_with_inner_schema(choice): + # We should eventually handle 'definition-ref' as well + raise TypeError( + f'{choice["type"]!r} is not a valid discriminated union variant;' + ' should be a `BaseModel` or `dataclass`' + ) + else: + if choice['type'] == 'tagged-union' and self._is_discriminator_shared(choice): + # In this case, this inner tagged-union is compatible with the outer tagged-union, + # and its choices can be coalesced into the outer TaggedUnionSchema. + subchoices = [x for x in choice['choices'].values() if not isinstance(x, (str, int))] + # Reverse the choices list before extending the stack so that they get handled in the order they occur + self._choices_to_handle.extend(subchoices[::-1]) + return + + inferred_discriminator_values = self._infer_discriminator_values_for_choice(choice, source_name=None) + self._set_unique_choice_for_values(choice, inferred_discriminator_values) + + def _is_discriminator_shared(self, choice: core_schema.TaggedUnionSchema) -> bool: + """This method returns a boolean indicating whether the discriminator for the `choice` + is the same as that being used for the outermost tagged union. This is used to + determine whether this TaggedUnionSchema choice should be "coalesced" into the top level, + or whether it should be treated as a separate (nested) choice. + """ + inner_discriminator = choice['discriminator'] + return inner_discriminator == self.discriminator or ( + isinstance(inner_discriminator, list) + and (self.discriminator in inner_discriminator or [self.discriminator] in inner_discriminator) + ) + + def _infer_discriminator_values_for_choice( # noqa C901 + self, choice: core_schema.CoreSchema, source_name: str | None + ) -> list[str | int]: + """This function recurses over `choice`, extracting all discriminator values that should map to this choice. + + `model_name` is accepted for the purpose of producing useful error messages. + """ + if choice['type'] == 'definitions': + return self._infer_discriminator_values_for_choice(choice['schema'], source_name=source_name) + elif choice['type'] == 'function-plain': + raise TypeError( + f'{choice["type"]!r} is not a valid discriminated union variant;' + ' should be a `BaseModel` or `dataclass`' + ) + elif _core_utils.is_function_with_inner_schema(choice): + return self._infer_discriminator_values_for_choice(choice['schema'], source_name=source_name) + elif choice['type'] == 'lax-or-strict': + return sorted( + set( + self._infer_discriminator_values_for_choice(choice['lax_schema'], source_name=None) + + self._infer_discriminator_values_for_choice(choice['strict_schema'], source_name=None) + ) + ) + + elif choice['type'] == 'tagged-union': + values: list[str | int] = [] + # Ignore str/int "choices" since these are just references to other choices + subchoices = [x for x in choice['choices'].values() if not isinstance(x, (str, int))] + for subchoice in subchoices: + subchoice_values = self._infer_discriminator_values_for_choice(subchoice, source_name=None) + values.extend(subchoice_values) + return values + + elif choice['type'] == 'union': + values = [] + for subchoice in choice['choices']: + subchoice_schema = subchoice[0] if isinstance(subchoice, tuple) else subchoice + subchoice_values = self._infer_discriminator_values_for_choice(subchoice_schema, source_name=None) + values.extend(subchoice_values) + return values + + elif choice['type'] == 'nullable': + self._should_be_nullable = True + return self._infer_discriminator_values_for_choice(choice['schema'], source_name=None) + + elif choice['type'] == 'model': + return self._infer_discriminator_values_for_choice(choice['schema'], source_name=choice['cls'].__name__) + + elif choice['type'] == 'dataclass': + return self._infer_discriminator_values_for_choice(choice['schema'], source_name=choice['cls'].__name__) + + elif choice['type'] == 'model-fields': + return self._infer_discriminator_values_for_model_choice(choice, source_name=source_name) + + elif choice['type'] == 'dataclass-args': + return self._infer_discriminator_values_for_dataclass_choice(choice, source_name=source_name) + + elif choice['type'] == 'typed-dict': + return self._infer_discriminator_values_for_typed_dict_choice(choice, source_name=source_name) + + elif choice['type'] == 'definition-ref': + schema_ref = choice['schema_ref'] + if schema_ref not in self.definitions: + raise MissingDefinitionForUnionRef(schema_ref) + return self._infer_discriminator_values_for_choice(self.definitions[schema_ref], source_name=source_name) + else: + raise TypeError( + f'{choice["type"]!r} is not a valid discriminated union variant;' + ' should be a `BaseModel` or `dataclass`' + ) + + def _infer_discriminator_values_for_typed_dict_choice( + self, choice: core_schema.TypedDictSchema, source_name: str | None = None + ) -> list[str | int]: + """This method just extracts the _infer_discriminator_values_for_choice logic specific to TypedDictSchema + for the sake of readability. + """ + source = 'TypedDict' if source_name is None else f'TypedDict {source_name!r}' + field = choice['fields'].get(self.discriminator) + if field is None: + raise PydanticUserError( + f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field' + ) + return self._infer_discriminator_values_for_field(field, source) + + def _infer_discriminator_values_for_model_choice( + self, choice: core_schema.ModelFieldsSchema, source_name: str | None = None + ) -> list[str | int]: + source = 'ModelFields' if source_name is None else f'Model {source_name!r}' + field = choice['fields'].get(self.discriminator) + if field is None: + raise PydanticUserError( + f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field' + ) + return self._infer_discriminator_values_for_field(field, source) + + def _infer_discriminator_values_for_dataclass_choice( + self, choice: core_schema.DataclassArgsSchema, source_name: str | None = None + ) -> list[str | int]: + source = 'DataclassArgs' if source_name is None else f'Dataclass {source_name!r}' + for field in choice['fields']: + if field['name'] == self.discriminator: + break + else: + raise PydanticUserError( + f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field' + ) + return self._infer_discriminator_values_for_field(field, source) + + def _infer_discriminator_values_for_field(self, field: CoreSchemaField, source: str) -> list[str | int]: + if field['type'] == 'computed-field': + # This should never occur as a discriminator, as it is only relevant to serialization + return [] + alias = field.get('validation_alias', self.discriminator) + if not isinstance(alias, str): + raise PydanticUserError( + f'Alias {alias!r} is not supported in a discriminated union', code='discriminator-alias-type' + ) + if self._discriminator_alias is None: + self._discriminator_alias = alias + elif self._discriminator_alias != alias: + raise PydanticUserError( + f'Aliases for discriminator {self.discriminator!r} must be the same ' + f'(got {alias}, {self._discriminator_alias})', + code='discriminator-alias', + ) + return self._infer_discriminator_values_for_inner_schema(field['schema'], source) + + def _infer_discriminator_values_for_inner_schema( + self, schema: core_schema.CoreSchema, source: str + ) -> list[str | int]: + """When inferring discriminator values for a field, we typically extract the expected values from a literal + schema. This function does that, but also handles nested unions and defaults. + """ + if schema['type'] == 'literal': + return schema['expected'] + + elif schema['type'] == 'union': + # Generally when multiple values are allowed they should be placed in a single `Literal`, but + # we add this case to handle the situation where a field is annotated as a `Union` of `Literal`s. + # For example, this lets us handle `Union[Literal['key'], Union[Literal['Key'], Literal['KEY']]]` + values: list[Any] = [] + for choice in schema['choices']: + choice_schema = choice[0] if isinstance(choice, tuple) else choice + choice_values = self._infer_discriminator_values_for_inner_schema(choice_schema, source) + values.extend(choice_values) + return values + + elif schema['type'] == 'default': + # This will happen if the field has a default value; we ignore it while extracting the discriminator values + return self._infer_discriminator_values_for_inner_schema(schema['schema'], source) + + elif schema['type'] == 'function-after': + # After validators don't affect the discriminator values + return self._infer_discriminator_values_for_inner_schema(schema['schema'], source) + + elif schema['type'] in {'function-before', 'function-wrap', 'function-plain'}: + validator_type = repr(schema['type'].split('-')[1]) + raise PydanticUserError( + f'Cannot use a mode={validator_type} validator in the' + f' discriminator field {self.discriminator!r} of {source}', + code='discriminator-validator', + ) + + else: + raise PydanticUserError( + f'{source} needs field {self.discriminator!r} to be of type `Literal`', + code='discriminator-needs-literal', + ) + + def _set_unique_choice_for_values(self, choice: core_schema.CoreSchema, values: Sequence[str | int]) -> None: + """This method updates `self.tagged_union_choices` so that all provided (discriminator) `values` map to the + provided `choice`, validating that none of these values already map to another (different) choice. + """ + for discriminator_value in values: + if discriminator_value in self._tagged_union_choices: + # It is okay if `value` is already in tagged_union_choices as long as it maps to the same value. + # Because tagged_union_choices may map values to other values, we need to walk the choices dict + # until we get to a "real" choice, and confirm that is equal to the one assigned. + existing_choice = self._tagged_union_choices[discriminator_value] + if existing_choice != choice: + raise TypeError( + f'Value {discriminator_value!r} for discriminator ' + f'{self.discriminator!r} mapped to multiple choices' + ) + else: + self._tagged_union_choices[discriminator_value] = choice diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_docs_extraction.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_docs_extraction.py new file mode 100644 index 0000000..261aec2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_docs_extraction.py @@ -0,0 +1,107 @@ +"""Utilities related to attribute docstring extraction.""" +from __future__ import annotations + +import ast +import inspect +import textwrap +from typing import Any + + +class DocstringVisitor(ast.NodeVisitor): + def __init__(self) -> None: + super().__init__() + + self.target: str | None = None + self.attrs: dict[str, str] = {} + self.previous_node_type: type[ast.AST] | None = None + + def visit(self, node: ast.AST) -> Any: + node_result = super().visit(node) + self.previous_node_type = type(node) + return node_result + + def visit_AnnAssign(self, node: ast.AnnAssign) -> Any: + if isinstance(node.target, ast.Name): + self.target = node.target.id + + def visit_Expr(self, node: ast.Expr) -> Any: + if ( + isinstance(node.value, ast.Constant) + and isinstance(node.value.value, str) + and self.previous_node_type is ast.AnnAssign + ): + docstring = inspect.cleandoc(node.value.value) + if self.target: + self.attrs[self.target] = docstring + self.target = None + + +def _dedent_source_lines(source: list[str]) -> str: + # Required for nested class definitions, e.g. in a function block + dedent_source = textwrap.dedent(''.join(source)) + if dedent_source.startswith((' ', '\t')): + # We are in the case where there's a dedented (usually multiline) string + # at a lower indentation level than the class itself. We wrap our class + # in a function as a workaround. + dedent_source = f'def dedent_workaround():\n{dedent_source}' + return dedent_source + + +def _extract_source_from_frame(cls: type[Any]) -> list[str] | None: + frame = inspect.currentframe() + + while frame: + if inspect.getmodule(frame) is inspect.getmodule(cls): + lnum = frame.f_lineno + try: + lines, _ = inspect.findsource(frame) + except OSError: + # Source can't be retrieved (maybe because running in an interactive terminal), + # we don't want to error here. + pass + else: + block_lines = inspect.getblock(lines[lnum - 1 :]) + dedent_source = _dedent_source_lines(block_lines) + try: + block_tree = ast.parse(dedent_source) + except SyntaxError: + pass + else: + stmt = block_tree.body[0] + if isinstance(stmt, ast.FunctionDef) and stmt.name == 'dedent_workaround': + # `_dedent_source_lines` wrapped the class around the workaround function + stmt = stmt.body[0] + if isinstance(stmt, ast.ClassDef) and stmt.name == cls.__name__: + return block_lines + + frame = frame.f_back + + +def extract_docstrings_from_cls(cls: type[Any], use_inspect: bool = False) -> dict[str, str]: + """Map model attributes and their corresponding docstring. + + Args: + cls: The class of the Pydantic model to inspect. + use_inspect: Whether to skip usage of frames to find the object and use + the `inspect` module instead. + + Returns: + A mapping containing attribute names and their corresponding docstring. + """ + if use_inspect: + # Might not work as expected if two classes have the same name in the same source file. + try: + source, _ = inspect.getsourcelines(cls) + except OSError: + return {} + else: + source = _extract_source_from_frame(cls) + + if not source: + return {} + + dedent_source = _dedent_source_lines(source) + + visitor = DocstringVisitor() + visitor.visit(ast.parse(dedent_source)) + return visitor.attrs diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_fields.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_fields.py new file mode 100644 index 0000000..6e5e933 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_fields.py @@ -0,0 +1,344 @@ +"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`.""" +from __future__ import annotations as _annotations + +import dataclasses +import sys +import warnings +from copy import copy +from functools import lru_cache +from typing import TYPE_CHECKING, Any + +from pydantic_core import PydanticUndefined + +from pydantic.errors import PydanticUserError + +from . import _typing_extra +from ._config import ConfigWrapper +from ._docs_extraction import extract_docstrings_from_cls +from ._repr import Representation +from ._typing_extra import get_cls_type_hints_lenient, get_type_hints, is_classvar, is_finalvar + +if TYPE_CHECKING: + from annotated_types import BaseMetadata + + from ..fields import FieldInfo + from ..main import BaseModel + from ._dataclasses import StandardDataclass + from ._decorators import DecoratorInfos + + +def get_type_hints_infer_globalns( + obj: Any, + localns: dict[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + """Gets type hints for an object by inferring the global namespace. + + It uses the `typing.get_type_hints`, The only thing that we do here is fetching + global namespace from `obj.__module__` if it is not `None`. + + Args: + obj: The object to get its type hints. + localns: The local namespaces. + include_extras: Whether to recursively include annotation metadata. + + Returns: + The object type hints. + """ + module_name = getattr(obj, '__module__', None) + globalns: dict[str, Any] | None = None + if module_name: + try: + globalns = sys.modules[module_name].__dict__ + except KeyError: + # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 + pass + return get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) + + +class PydanticMetadata(Representation): + """Base class for annotation markers like `Strict`.""" + + __slots__ = () + + +def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: + """Create a new `_PydanticGeneralMetadata` class with the given metadata. + + Args: + **metadata: The metadata to add. + + Returns: + The new `_PydanticGeneralMetadata` class. + """ + return _general_metadata_cls()(metadata) # type: ignore + + +@lru_cache(maxsize=None) +def _general_metadata_cls() -> type[BaseMetadata]: + """Do it this way to avoid importing `annotated_types` at import time.""" + from annotated_types import BaseMetadata + + class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): + """Pydantic general metadata like `max_digits`.""" + + def __init__(self, metadata: Any): + self.__dict__ = metadata + + return _PydanticGeneralMetadata # type: ignore + + +def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], config_wrapper: ConfigWrapper) -> None: + if config_wrapper.use_attribute_docstrings: + fields_docs = extract_docstrings_from_cls(cls) + for ann_name, field_info in fields.items(): + if field_info.description is None and ann_name in fields_docs: + field_info.description = fields_docs[ann_name] + + +def collect_model_fields( # noqa: C901 + cls: type[BaseModel], + bases: tuple[type[Any], ...], + config_wrapper: ConfigWrapper, + types_namespace: dict[str, Any] | None, + *, + typevars_map: dict[Any, Any] | None = None, +) -> tuple[dict[str, FieldInfo], set[str]]: + """Collect the fields of a nascent pydantic model. + + Also collect the names of any ClassVars present in the type hints. + + The returned value is a tuple of two items: the fields dict, and the set of ClassVar names. + + Args: + cls: BaseModel or dataclass. + bases: Parents of the class, generally `cls.__bases__`. + config_wrapper: The config wrapper instance. + types_namespace: Optional extra namespace to look for types in. + typevars_map: A dictionary mapping type variables to their concrete types. + + Returns: + A tuple contains fields and class variables. + + Raises: + NameError: + - If there is a conflict between a field name and protected namespaces. + - If there is a field other than `root` in `RootModel`. + - If a field shadows an attribute in the parent model. + """ + from ..fields import FieldInfo + + type_hints = get_cls_type_hints_lenient(cls, types_namespace) + + # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older + # annotations is only used for finding fields in parent classes + annotations = cls.__dict__.get('__annotations__', {}) + fields: dict[str, FieldInfo] = {} + + class_vars: set[str] = set() + for ann_name, ann_type in type_hints.items(): + if ann_name == 'model_config': + # We never want to treat `model_config` as a field + # Note: we may need to change this logic if/when we introduce a `BareModel` class with no + # protected namespaces (where `model_config` might be allowed as a field name) + continue + for protected_namespace in config_wrapper.protected_namespaces: + if ann_name.startswith(protected_namespace): + for b in bases: + if hasattr(b, ann_name): + from ..main import BaseModel + + if not (issubclass(b, BaseModel) and ann_name in b.model_fields): + raise NameError( + f'Field "{ann_name}" conflicts with member {getattr(b, ann_name)}' + f' of protected namespace "{protected_namespace}".' + ) + else: + valid_namespaces = tuple( + x for x in config_wrapper.protected_namespaces if not ann_name.startswith(x) + ) + warnings.warn( + f'Field "{ann_name}" has conflict with protected namespace "{protected_namespace}".' + '\n\nYou may be able to resolve this warning by setting' + f" `model_config['protected_namespaces'] = {valid_namespaces}`.", + UserWarning, + ) + if is_classvar(ann_type): + class_vars.add(ann_name) + continue + if _is_finalvar_with_default_val(ann_type, getattr(cls, ann_name, PydanticUndefined)): + class_vars.add(ann_name) + continue + if not is_valid_field_name(ann_name): + continue + if cls.__pydantic_root_model__ and ann_name != 'root': + raise NameError( + f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`" + ) + + # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get + # "... shadows an attribute" warnings + generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') + for base in bases: + dataclass_fields = { + field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ()) + } + if hasattr(base, ann_name): + if base is generic_origin: + # Don't warn when "shadowing" of attributes in parametrized generics + continue + + if ann_name in dataclass_fields: + # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set + # on the class instance. + continue + + if ann_name not in annotations: + # Don't warn when a field exists in a parent class but has not been defined in the current class + continue + + warnings.warn( + f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent ' + f'"{base.__qualname__}"', + UserWarning, + ) + + try: + default = getattr(cls, ann_name, PydanticUndefined) + if default is PydanticUndefined: + raise AttributeError + except AttributeError: + if ann_name in annotations: + field_info = FieldInfo.from_annotation(ann_type) + else: + # if field has no default value and is not in __annotations__ this means that it is + # defined in a base class and we can take it from there + model_fields_lookup: dict[str, FieldInfo] = {} + for x in cls.__bases__[::-1]: + model_fields_lookup.update(getattr(x, 'model_fields', {})) + if ann_name in model_fields_lookup: + # The field was present on one of the (possibly multiple) base classes + # copy the field to make sure typevar substitutions don't cause issues with the base classes + field_info = copy(model_fields_lookup[ann_name]) + else: + # The field was not found on any base classes; this seems to be caused by fields not getting + # generated thanks to models not being fully defined while initializing recursive models. + # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this. + field_info = FieldInfo.from_annotation(ann_type) + else: + field_info = FieldInfo.from_annotated_attribute(ann_type, default) + # attributes which are fields are removed from the class namespace: + # 1. To match the behaviour of annotation-only fields + # 2. To avoid false positives in the NameError check above + try: + delattr(cls, ann_name) + except AttributeError: + pass # indicates the attribute was on a parent class + + # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__ + # to make sure the decorators have already been built for this exact class + decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] + if ann_name in decorators.computed_fields: + raise ValueError("you can't override a field with a computed field") + fields[ann_name] = field_info + + if typevars_map: + for field in fields.values(): + field.apply_typevars_map(typevars_map, types_namespace) + + _update_fields_from_docstrings(cls, fields, config_wrapper) + + return fields, class_vars + + +def _is_finalvar_with_default_val(type_: type[Any], val: Any) -> bool: + from ..fields import FieldInfo + + if not is_finalvar(type_): + return False + elif val is PydanticUndefined: + return False + elif isinstance(val, FieldInfo) and (val.default is PydanticUndefined and val.default_factory is None): + return False + else: + return True + + +def collect_dataclass_fields( + cls: type[StandardDataclass], + types_namespace: dict[str, Any] | None, + *, + typevars_map: dict[Any, Any] | None = None, + config_wrapper: ConfigWrapper | None = None, +) -> dict[str, FieldInfo]: + """Collect the fields of a dataclass. + + Args: + cls: dataclass. + types_namespace: Optional extra namespace to look for types in. + typevars_map: A dictionary mapping type variables to their concrete types. + config_wrapper: The config wrapper instance. + + Returns: + The dataclass fields. + """ + from ..fields import FieldInfo + + fields: dict[str, FieldInfo] = {} + dataclass_fields: dict[str, dataclasses.Field] = cls.__dataclass_fields__ + cls_localns = dict(vars(cls)) # this matches get_cls_type_hints_lenient, but all tests pass with `= None` instead + + source_module = sys.modules.get(cls.__module__) + if source_module is not None: + types_namespace = {**source_module.__dict__, **(types_namespace or {})} + + for ann_name, dataclass_field in dataclass_fields.items(): + ann_type = _typing_extra.eval_type_lenient(dataclass_field.type, types_namespace, cls_localns) + if is_classvar(ann_type): + continue + + if ( + not dataclass_field.init + and dataclass_field.default == dataclasses.MISSING + and dataclass_field.default_factory == dataclasses.MISSING + ): + # TODO: We should probably do something with this so that validate_assignment behaves properly + # Issue: https://github.com/pydantic/pydantic/issues/5470 + continue + + if isinstance(dataclass_field.default, FieldInfo): + if dataclass_field.default.init_var: + if dataclass_field.default.init is False: + raise PydanticUserError( + f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.', + code='clashing-init-and-init-var', + ) + + # TODO: same note as above re validate_assignment + continue + field_info = FieldInfo.from_annotated_attribute(ann_type, dataclass_field.default) + else: + field_info = FieldInfo.from_annotated_attribute(ann_type, dataclass_field) + + fields[ann_name] = field_info + + if field_info.default is not PydanticUndefined and isinstance(getattr(cls, ann_name, field_info), FieldInfo): + # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo + setattr(cls, ann_name, field_info.default) + + if typevars_map: + for field in fields.values(): + field.apply_typevars_map(typevars_map, types_namespace) + + if config_wrapper is not None: + _update_fields_from_docstrings(cls, fields, config_wrapper) + + return fields + + +def is_valid_field_name(name: str) -> bool: + return not name.startswith('_') + + +def is_valid_privateattr_name(name: str) -> bool: + return name.startswith('_') and not name.startswith('__') diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_forward_ref.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_forward_ref.py new file mode 100644 index 0000000..231f81d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_forward_ref.py @@ -0,0 +1,23 @@ +from __future__ import annotations as _annotations + +from dataclasses import dataclass +from typing import Union + + +@dataclass +class PydanticRecursiveRef: + type_ref: str + + __name__ = 'PydanticRecursiveRef' + __hash__ = object.__hash__ + + def __call__(self) -> None: + """Defining __call__ is necessary for the `typing` module to let you use an instance of + this class as the result of resolving a standard ForwardRef. + """ + + def __or__(self, other): + return Union[self, other] # type: ignore + + def __ror__(self, other): + return Union[other, self] # type: ignore diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py new file mode 100644 index 0000000..b88dd8b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py @@ -0,0 +1,2306 @@ +"""Convert python types to pydantic-core schema.""" +from __future__ import annotations as _annotations + +import collections.abc +import dataclasses +import inspect +import re +import sys +import typing +import warnings +from contextlib import ExitStack, contextmanager +from copy import copy, deepcopy +from enum import Enum +from functools import partial +from inspect import Parameter, _ParameterKind, signature +from itertools import chain +from operator import attrgetter +from types import FunctionType, LambdaType, MethodType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Final, + ForwardRef, + Iterable, + Iterator, + Mapping, + Type, + TypeVar, + Union, + cast, + overload, +) +from warnings import warn + +from pydantic_core import CoreSchema, PydanticUndefined, core_schema, to_jsonable_python +from typing_extensions import Annotated, Literal, TypeAliasType, TypedDict, get_args, get_origin, is_typeddict + +from ..aliases import AliasGenerator +from ..annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler +from ..config import ConfigDict, JsonDict, JsonEncoder +from ..errors import PydanticSchemaGenerationError, PydanticUndefinedAnnotation, PydanticUserError +from ..json_schema import JsonSchemaValue +from ..version import version_short +from ..warnings import PydanticDeprecatedSince20 +from . import _core_utils, _decorators, _discriminated_union, _known_annotated_metadata, _typing_extra +from ._config import ConfigWrapper, ConfigWrapperStack +from ._core_metadata import CoreMetadataHandler, build_metadata_dict +from ._core_utils import ( + CoreSchemaOrField, + collect_invalid_schemas, + define_expected_missing_refs, + get_ref, + get_type_ref, + is_function_with_inner_schema, + is_list_like_schema_with_items_schema, + simplify_schema_references, + validate_core_schema, +) +from ._decorators import ( + Decorator, + DecoratorInfos, + FieldSerializerDecoratorInfo, + FieldValidatorDecoratorInfo, + ModelSerializerDecoratorInfo, + ModelValidatorDecoratorInfo, + RootValidatorDecoratorInfo, + ValidatorDecoratorInfo, + get_attribute_from_bases, + inspect_field_serializer, + inspect_model_serializer, + inspect_validator, +) +from ._docs_extraction import extract_docstrings_from_cls +from ._fields import collect_dataclass_fields, get_type_hints_infer_globalns +from ._forward_ref import PydanticRecursiveRef +from ._generics import get_standard_typevars_map, has_instance_in_type, recursively_defined_type_refs, replace_types +from ._schema_generation_shared import CallbackGetCoreSchemaHandler +from ._typing_extra import is_finalvar, is_self_type +from ._utils import lenient_issubclass + +if TYPE_CHECKING: + from ..fields import ComputedFieldInfo, FieldInfo + from ..main import BaseModel + from ..types import Discriminator + from ..validators import FieldValidatorModes + from ._dataclasses import StandardDataclass + from ._schema_generation_shared import GetJsonSchemaFunction + +_SUPPORTS_TYPEDDICT = sys.version_info >= (3, 12) +_AnnotatedType = type(Annotated[int, 123]) + +FieldDecoratorInfo = Union[ValidatorDecoratorInfo, FieldValidatorDecoratorInfo, FieldSerializerDecoratorInfo] +FieldDecoratorInfoType = TypeVar('FieldDecoratorInfoType', bound=FieldDecoratorInfo) +AnyFieldDecorator = Union[ + Decorator[ValidatorDecoratorInfo], + Decorator[FieldValidatorDecoratorInfo], + Decorator[FieldSerializerDecoratorInfo], +] + +ModifyCoreSchemaWrapHandler = GetCoreSchemaHandler +GetCoreSchemaFunction = Callable[[Any, ModifyCoreSchemaWrapHandler], core_schema.CoreSchema] + + +TUPLE_TYPES: list[type] = [tuple, typing.Tuple] +LIST_TYPES: list[type] = [list, typing.List, collections.abc.MutableSequence] +SET_TYPES: list[type] = [set, typing.Set, collections.abc.MutableSet] +FROZEN_SET_TYPES: list[type] = [frozenset, typing.FrozenSet, collections.abc.Set] +DICT_TYPES: list[type] = [dict, typing.Dict, collections.abc.MutableMapping, collections.abc.Mapping] + + +def check_validator_fields_against_field_name( + info: FieldDecoratorInfo, + field: str, +) -> bool: + """Check if field name is in validator fields. + + Args: + info: The field info. + field: The field name to check. + + Returns: + `True` if field name is in validator fields, `False` otherwise. + """ + if '*' in info.fields: + return True + for v_field_name in info.fields: + if v_field_name == field: + return True + return False + + +def check_decorator_fields_exist(decorators: Iterable[AnyFieldDecorator], fields: Iterable[str]) -> None: + """Check if the defined fields in decorators exist in `fields` param. + + It ignores the check for a decorator if the decorator has `*` as field or `check_fields=False`. + + Args: + decorators: An iterable of decorators. + fields: An iterable of fields name. + + Raises: + PydanticUserError: If one of the field names does not exist in `fields` param. + """ + fields = set(fields) + for dec in decorators: + if '*' in dec.info.fields: + continue + if dec.info.check_fields is False: + continue + for field in dec.info.fields: + if field not in fields: + raise PydanticUserError( + f'Decorators defined with incorrect fields: {dec.cls_ref}.{dec.cls_var_name}' + " (use check_fields=False if you're inheriting from the model and intended this)", + code='decorator-missing-field', + ) + + +def filter_field_decorator_info_by_field( + validator_functions: Iterable[Decorator[FieldDecoratorInfoType]], field: str +) -> list[Decorator[FieldDecoratorInfoType]]: + return [dec for dec in validator_functions if check_validator_fields_against_field_name(dec.info, field)] + + +def apply_each_item_validators( + schema: core_schema.CoreSchema, + each_item_validators: list[Decorator[ValidatorDecoratorInfo]], + field_name: str | None, +) -> core_schema.CoreSchema: + # This V1 compatibility shim should eventually be removed + + # push down any `each_item=True` validators + # note that this won't work for any Annotated types that get wrapped by a function validator + # but that's okay because that didn't exist in V1 + if schema['type'] == 'nullable': + schema['schema'] = apply_each_item_validators(schema['schema'], each_item_validators, field_name) + return schema + elif schema['type'] == 'tuple': + if (variadic_item_index := schema.get('variadic_item_index')) is not None: + schema['items_schema'][variadic_item_index] = apply_validators( + schema['items_schema'][variadic_item_index], each_item_validators, field_name + ) + elif is_list_like_schema_with_items_schema(schema): + inner_schema = schema.get('items_schema', None) + if inner_schema is None: + inner_schema = core_schema.any_schema() + schema['items_schema'] = apply_validators(inner_schema, each_item_validators, field_name) + elif schema['type'] == 'dict': + # push down any `each_item=True` validators onto dict _values_ + # this is super arbitrary but it's the V1 behavior + inner_schema = schema.get('values_schema', None) + if inner_schema is None: + inner_schema = core_schema.any_schema() + schema['values_schema'] = apply_validators(inner_schema, each_item_validators, field_name) + elif each_item_validators: + raise TypeError( + f"`@validator(..., each_item=True)` cannot be applied to fields with a schema of {schema['type']}" + ) + return schema + + +def modify_model_json_schema( + schema_or_field: CoreSchemaOrField, handler: GetJsonSchemaHandler, *, cls: Any +) -> JsonSchemaValue: + """Add title and description for model-like classes' JSON schema. + + Args: + schema_or_field: The schema data to generate a JSON schema from. + handler: The `GetCoreSchemaHandler` instance. + cls: The model-like class. + + Returns: + JsonSchemaValue: The updated JSON schema. + """ + from ..main import BaseModel + from ..root_model import RootModel + + json_schema = handler(schema_or_field) + original_schema = handler.resolve_ref_schema(json_schema) + # Preserve the fact that definitions schemas should never have sibling keys: + if '$ref' in original_schema: + ref = original_schema['$ref'] + original_schema.clear() + original_schema['allOf'] = [{'$ref': ref}] + if 'title' not in original_schema: + original_schema['title'] = cls.__name__ + # BaseModel; don't use cls.__doc__ as it will contain the verbose class signature by default + docstring = None if cls is BaseModel else cls.__doc__ + if docstring and 'description' not in original_schema: + original_schema['description'] = inspect.cleandoc(docstring) + elif issubclass(cls, RootModel) and cls.model_fields['root'].description: + original_schema['description'] = cls.model_fields['root'].description + return json_schema + + +JsonEncoders = Dict[Type[Any], JsonEncoder] + + +def _add_custom_serialization_from_json_encoders( + json_encoders: JsonEncoders | None, tp: Any, schema: CoreSchema +) -> CoreSchema: + """Iterate over the json_encoders and add the first matching encoder to the schema. + + Args: + json_encoders: A dictionary of types and their encoder functions. + tp: The type to check for a matching encoder. + schema: The schema to add the encoder to. + """ + if not json_encoders: + return schema + if 'serialization' in schema: + return schema + # Check the class type and its superclasses for a matching encoder + # Decimal.__class__.__mro__ (and probably other cases) doesn't include Decimal itself + # if the type is a GenericAlias (e.g. from list[int]) we need to use __class__ instead of .__mro__ + for base in (tp, *getattr(tp, '__mro__', tp.__class__.__mro__)[:-1]): + encoder = json_encoders.get(base) + if encoder is None: + continue + + warnings.warn( + f'`json_encoders` is deprecated. See https://docs.pydantic.dev/{version_short()}/concepts/serialization/#custom-serializers for alternatives', + PydanticDeprecatedSince20, + ) + + # TODO: in theory we should check that the schema accepts a serialization key + schema['serialization'] = core_schema.plain_serializer_function_ser_schema(encoder, when_used='json') + return schema + + return schema + + +TypesNamespace = Union[Dict[str, Any], None] + + +class TypesNamespaceStack: + """A stack of types namespaces.""" + + def __init__(self, types_namespace: TypesNamespace): + self._types_namespace_stack: list[TypesNamespace] = [types_namespace] + + @property + def tail(self) -> TypesNamespace: + return self._types_namespace_stack[-1] + + @contextmanager + def push(self, for_type: type[Any]): + types_namespace = {**_typing_extra.get_cls_types_namespace(for_type), **(self.tail or {})} + self._types_namespace_stack.append(types_namespace) + try: + yield + finally: + self._types_namespace_stack.pop() + + +def _get_first_non_null(a: Any, b: Any) -> Any: + """Return the first argument if it is not None, otherwise return the second argument. + + Use case: serialization_alias (argument a) and alias (argument b) are both defined, and serialization_alias is ''. + This function will return serialization_alias, which is the first argument, even though it is an empty string. + """ + return a if a is not None else b + + +class GenerateSchema: + """Generate core schema for a Pydantic model, dataclass and types like `str`, `datetime`, ... .""" + + __slots__ = ( + '_config_wrapper_stack', + '_types_namespace_stack', + '_typevars_map', + 'field_name_stack', + 'model_type_stack', + 'defs', + ) + + def __init__( + self, + config_wrapper: ConfigWrapper, + types_namespace: dict[str, Any] | None, + typevars_map: dict[Any, Any] | None = None, + ) -> None: + # we need a stack for recursing into child models + self._config_wrapper_stack = ConfigWrapperStack(config_wrapper) + self._types_namespace_stack = TypesNamespaceStack(types_namespace) + self._typevars_map = typevars_map + self.field_name_stack = _FieldNameStack() + self.model_type_stack = _ModelTypeStack() + self.defs = _Definitions() + + @classmethod + def __from_parent( + cls, + config_wrapper_stack: ConfigWrapperStack, + types_namespace_stack: TypesNamespaceStack, + model_type_stack: _ModelTypeStack, + typevars_map: dict[Any, Any] | None, + defs: _Definitions, + ) -> GenerateSchema: + obj = cls.__new__(cls) + obj._config_wrapper_stack = config_wrapper_stack + obj._types_namespace_stack = types_namespace_stack + obj.model_type_stack = model_type_stack + obj._typevars_map = typevars_map + obj.field_name_stack = _FieldNameStack() + obj.defs = defs + return obj + + @property + def _config_wrapper(self) -> ConfigWrapper: + return self._config_wrapper_stack.tail + + @property + def _types_namespace(self) -> dict[str, Any] | None: + return self._types_namespace_stack.tail + + @property + def _current_generate_schema(self) -> GenerateSchema: + cls = self._config_wrapper.schema_generator or GenerateSchema + return cls.__from_parent( + self._config_wrapper_stack, + self._types_namespace_stack, + self.model_type_stack, + self._typevars_map, + self.defs, + ) + + @property + def _arbitrary_types(self) -> bool: + return self._config_wrapper.arbitrary_types_allowed + + def str_schema(self) -> CoreSchema: + """Generate a CoreSchema for `str`""" + return core_schema.str_schema() + + # the following methods can be overridden but should be considered + # unstable / private APIs + def _list_schema(self, tp: Any, items_type: Any) -> CoreSchema: + return core_schema.list_schema(self.generate_schema(items_type)) + + def _dict_schema(self, tp: Any, keys_type: Any, values_type: Any) -> CoreSchema: + return core_schema.dict_schema(self.generate_schema(keys_type), self.generate_schema(values_type)) + + def _set_schema(self, tp: Any, items_type: Any) -> CoreSchema: + return core_schema.set_schema(self.generate_schema(items_type)) + + def _frozenset_schema(self, tp: Any, items_type: Any) -> CoreSchema: + return core_schema.frozenset_schema(self.generate_schema(items_type)) + + def _arbitrary_type_schema(self, tp: Any) -> CoreSchema: + if not isinstance(tp, type): + warn( + f'{tp!r} is not a Python type (it may be an instance of an object),' + ' Pydantic will allow any object with no validation since we cannot even' + ' enforce that the input is an instance of the given type.' + ' To get rid of this error wrap the type with `pydantic.SkipValidation`.', + UserWarning, + ) + return core_schema.any_schema() + return core_schema.is_instance_schema(tp) + + def _unknown_type_schema(self, obj: Any) -> CoreSchema: + raise PydanticSchemaGenerationError( + f'Unable to generate pydantic-core schema for {obj!r}. ' + 'Set `arbitrary_types_allowed=True` in the model_config to ignore this error' + ' or implement `__get_pydantic_core_schema__` on your type to fully support it.' + '\n\nIf you got this error by calling handler() within' + ' `__get_pydantic_core_schema__` then you likely need to call' + ' `handler.generate_schema()` since we do not call' + ' `__get_pydantic_core_schema__` on `` otherwise to avoid infinite recursion.' + ) + + def _apply_discriminator_to_union( + self, schema: CoreSchema, discriminator: str | Discriminator | None + ) -> CoreSchema: + if discriminator is None: + return schema + try: + return _discriminated_union.apply_discriminator( + schema, + discriminator, + ) + except _discriminated_union.MissingDefinitionForUnionRef: + # defer until defs are resolved + _discriminated_union.set_discriminator_in_metadata( + schema, + discriminator, + ) + return schema + + class CollectedInvalid(Exception): + pass + + def clean_schema(self, schema: CoreSchema) -> CoreSchema: + schema = self.collect_definitions(schema) + schema = simplify_schema_references(schema) + if collect_invalid_schemas(schema): + raise self.CollectedInvalid() + schema = _discriminated_union.apply_discriminators(schema) + schema = validate_core_schema(schema) + return schema + + def collect_definitions(self, schema: CoreSchema) -> CoreSchema: + ref = cast('str | None', schema.get('ref', None)) + if ref: + self.defs.definitions[ref] = schema + if 'ref' in schema: + schema = core_schema.definition_reference_schema(schema['ref']) + return core_schema.definitions_schema( + schema, + list(self.defs.definitions.values()), + ) + + def _add_js_function(self, metadata_schema: CoreSchema, js_function: Callable[..., Any]) -> None: + metadata = CoreMetadataHandler(metadata_schema).metadata + pydantic_js_functions = metadata.setdefault('pydantic_js_functions', []) + # because of how we generate core schemas for nested generic models + # we can end up adding `BaseModel.__get_pydantic_json_schema__` multiple times + # this check may fail to catch duplicates if the function is a `functools.partial` + # or something like that + # but if it does it'll fail by inserting the duplicate + if js_function not in pydantic_js_functions: + pydantic_js_functions.append(js_function) + + def generate_schema( + self, + obj: Any, + from_dunder_get_core_schema: bool = True, + ) -> core_schema.CoreSchema: + """Generate core schema. + + Args: + obj: The object to generate core schema for. + from_dunder_get_core_schema: Whether to generate schema from either the + `__get_pydantic_core_schema__` function or `__pydantic_core_schema__` property. + + Returns: + The generated core schema. + + Raises: + PydanticUndefinedAnnotation: + If it is not possible to evaluate forward reference. + PydanticSchemaGenerationError: + If it is not possible to generate pydantic-core schema. + TypeError: + - If `alias_generator` returns a disallowed type (must be str, AliasPath or AliasChoices). + - If V1 style validator with `each_item=True` applied on a wrong field. + PydanticUserError: + - If `typing.TypedDict` is used instead of `typing_extensions.TypedDict` on Python < 3.12. + - If `__modify_schema__` method is used instead of `__get_pydantic_json_schema__`. + """ + schema: CoreSchema | None = None + + if from_dunder_get_core_schema: + from_property = self._generate_schema_from_property(obj, obj) + if from_property is not None: + schema = from_property + + if schema is None: + schema = self._generate_schema_inner(obj) + + metadata_js_function = _extract_get_pydantic_json_schema(obj, schema) + if metadata_js_function is not None: + metadata_schema = resolve_original_schema(schema, self.defs.definitions) + if metadata_schema: + self._add_js_function(metadata_schema, metadata_js_function) + + schema = _add_custom_serialization_from_json_encoders(self._config_wrapper.json_encoders, obj, schema) + + return schema + + def _model_schema(self, cls: type[BaseModel]) -> core_schema.CoreSchema: + """Generate schema for a Pydantic model.""" + with self.defs.get_schema_or_ref(cls) as (model_ref, maybe_schema): + if maybe_schema is not None: + return maybe_schema + + fields = cls.model_fields + decorators = cls.__pydantic_decorators__ + computed_fields = decorators.computed_fields + check_decorator_fields_exist( + chain( + decorators.field_validators.values(), + decorators.field_serializers.values(), + decorators.validators.values(), + ), + {*fields.keys(), *computed_fields.keys()}, + ) + config_wrapper = ConfigWrapper(cls.model_config, check=False) + core_config = config_wrapper.core_config(cls) + metadata = build_metadata_dict(js_functions=[partial(modify_model_json_schema, cls=cls)]) + + model_validators = decorators.model_validators.values() + + extras_schema = None + if core_config.get('extra_fields_behavior') == 'allow': + assert cls.__mro__[0] is cls + assert cls.__mro__[-1] is object + for candidate_cls in cls.__mro__[:-1]: + extras_annotation = getattr(candidate_cls, '__annotations__', {}).get('__pydantic_extra__', None) + if extras_annotation is not None: + if isinstance(extras_annotation, str): + extras_annotation = _typing_extra.eval_type_backport( + _typing_extra._make_forward_ref(extras_annotation, is_argument=False, is_class=True), + self._types_namespace, + ) + tp = get_origin(extras_annotation) + if tp not in (Dict, dict): + raise PydanticSchemaGenerationError( + 'The type annotation for `__pydantic_extra__` must be `Dict[str, ...]`' + ) + extra_items_type = self._get_args_resolving_forward_refs( + extras_annotation, + required=True, + )[1] + if extra_items_type is not Any: + extras_schema = self.generate_schema(extra_items_type) + break + + with self._config_wrapper_stack.push(config_wrapper), self._types_namespace_stack.push(cls): + self = self._current_generate_schema + if cls.__pydantic_root_model__: + root_field = self._common_field_schema('root', fields['root'], decorators) + inner_schema = root_field['schema'] + inner_schema = apply_model_validators(inner_schema, model_validators, 'inner') + model_schema = core_schema.model_schema( + cls, + inner_schema, + custom_init=getattr(cls, '__pydantic_custom_init__', None), + root_model=True, + post_init=getattr(cls, '__pydantic_post_init__', None), + config=core_config, + ref=model_ref, + metadata=metadata, + ) + else: + fields_schema: core_schema.CoreSchema = core_schema.model_fields_schema( + {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()}, + computed_fields=[ + self._computed_field_schema(d, decorators.field_serializers) + for d in computed_fields.values() + ], + extras_schema=extras_schema, + model_name=cls.__name__, + ) + inner_schema = apply_validators(fields_schema, decorators.root_validators.values(), None) + new_inner_schema = define_expected_missing_refs(inner_schema, recursively_defined_type_refs()) + if new_inner_schema is not None: + inner_schema = new_inner_schema + inner_schema = apply_model_validators(inner_schema, model_validators, 'inner') + + model_schema = core_schema.model_schema( + cls, + inner_schema, + custom_init=getattr(cls, '__pydantic_custom_init__', None), + root_model=False, + post_init=getattr(cls, '__pydantic_post_init__', None), + config=core_config, + ref=model_ref, + metadata=metadata, + ) + + schema = self._apply_model_serializers(model_schema, decorators.model_serializers.values()) + schema = apply_model_validators(schema, model_validators, 'outer') + self.defs.definitions[model_ref] = schema + return core_schema.definition_reference_schema(model_ref) + + def _unpack_refs_defs(self, schema: CoreSchema) -> CoreSchema: + """Unpack all 'definitions' schemas into `GenerateSchema.defs.definitions` + and return the inner schema. + """ + + def get_ref(s: CoreSchema) -> str: + return s['ref'] # type: ignore + + if schema['type'] == 'definitions': + self.defs.definitions.update({get_ref(s): s for s in schema['definitions']}) + schema = schema['schema'] + return schema + + def _generate_schema_from_property(self, obj: Any, source: Any) -> core_schema.CoreSchema | None: + """Try to generate schema from either the `__get_pydantic_core_schema__` function or + `__pydantic_core_schema__` property. + + Note: `__get_pydantic_core_schema__` takes priority so it can + decide whether to use a `__pydantic_core_schema__` attribute, or generate a fresh schema. + """ + # avoid calling `__get_pydantic_core_schema__` if we've already visited this object + if is_self_type(obj): + obj = self.model_type_stack.get() + with self.defs.get_schema_or_ref(obj) as (_, maybe_schema): + if maybe_schema is not None: + return maybe_schema + if obj is source: + ref_mode = 'unpack' + else: + ref_mode = 'to-def' + + schema: CoreSchema + + if (get_schema := getattr(obj, '__get_pydantic_core_schema__', None)) is not None: + if len(inspect.signature(get_schema).parameters) == 1: + # (source) -> CoreSchema + schema = get_schema(source) + else: + schema = get_schema( + source, CallbackGetCoreSchemaHandler(self._generate_schema_inner, self, ref_mode=ref_mode) + ) + # fmt: off + elif (existing_schema := getattr(obj, '__pydantic_core_schema__', None)) is not None and existing_schema.get( + 'cls', None + ) == obj: + schema = existing_schema + # fmt: on + elif (validators := getattr(obj, '__get_validators__', None)) is not None: + warn( + '`__get_validators__` is deprecated and will be removed, use `__get_pydantic_core_schema__` instead.', + PydanticDeprecatedSince20, + ) + schema = core_schema.chain_schema([core_schema.with_info_plain_validator_function(v) for v in validators()]) + else: + # we have no existing schema information on the property, exit early so that we can go generate a schema + return None + + schema = self._unpack_refs_defs(schema) + + if is_function_with_inner_schema(schema): + ref = schema['schema'].pop('ref', None) # pyright: ignore[reportGeneralTypeIssues] + if ref: + schema['ref'] = ref + else: + ref = get_ref(schema) + + if ref: + self.defs.definitions[ref] = schema + return core_schema.definition_reference_schema(ref) + + return schema + + def _resolve_forward_ref(self, obj: Any) -> Any: + # we assume that types_namespace has the target of forward references in its scope, + # but this could fail, for example, if calling Validator on an imported type which contains + # forward references to other types only defined in the module from which it was imported + # `Validator(SomeImportedTypeAliasWithAForwardReference)` + # or the equivalent for BaseModel + # class Model(BaseModel): + # x: SomeImportedTypeAliasWithAForwardReference + try: + obj = _typing_extra.eval_type_backport(obj, globalns=self._types_namespace) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + + # if obj is still a ForwardRef, it means we can't evaluate it, raise PydanticUndefinedAnnotation + if isinstance(obj, ForwardRef): + raise PydanticUndefinedAnnotation(obj.__forward_arg__, f'Unable to evaluate forward reference {obj}') + + if self._typevars_map: + obj = replace_types(obj, self._typevars_map) + + return obj + + @overload + def _get_args_resolving_forward_refs(self, obj: Any, required: Literal[True]) -> tuple[Any, ...]: + ... + + @overload + def _get_args_resolving_forward_refs(self, obj: Any) -> tuple[Any, ...] | None: + ... + + def _get_args_resolving_forward_refs(self, obj: Any, required: bool = False) -> tuple[Any, ...] | None: + args = get_args(obj) + if args: + args = tuple([self._resolve_forward_ref(a) if isinstance(a, ForwardRef) else a for a in args]) + elif required: # pragma: no cover + raise TypeError(f'Expected {obj} to have generic parameters but it had none') + return args + + def _get_first_arg_or_any(self, obj: Any) -> Any: + args = self._get_args_resolving_forward_refs(obj) + if not args: + return Any + return args[0] + + def _get_first_two_args_or_any(self, obj: Any) -> tuple[Any, Any]: + args = self._get_args_resolving_forward_refs(obj) + if not args: + return (Any, Any) + if len(args) < 2: + origin = get_origin(obj) + raise TypeError(f'Expected two type arguments for {origin}, got 1') + return args[0], args[1] + + def _generate_schema_inner(self, obj: Any) -> core_schema.CoreSchema: + if isinstance(obj, _AnnotatedType): + return self._annotated_schema(obj) + + if isinstance(obj, dict): + # we assume this is already a valid schema + return obj # type: ignore[return-value] + + if isinstance(obj, str): + obj = ForwardRef(obj) + + if isinstance(obj, ForwardRef): + return self.generate_schema(self._resolve_forward_ref(obj)) + + from ..main import BaseModel + + if lenient_issubclass(obj, BaseModel): + with self.model_type_stack.push(obj): + return self._model_schema(obj) + + if isinstance(obj, PydanticRecursiveRef): + return core_schema.definition_reference_schema(schema_ref=obj.type_ref) + + return self.match_type(obj) + + def match_type(self, obj: Any) -> core_schema.CoreSchema: # noqa: C901 + """Main mapping of types to schemas. + + The general structure is a series of if statements starting with the simple cases + (non-generic primitive types) and then handling generics and other more complex cases. + + Each case either generates a schema directly, calls into a public user-overridable method + (like `GenerateSchema.tuple_variable_schema`) or calls into a private method that handles some + boilerplate before calling into the user-facing method (e.g. `GenerateSchema._tuple_schema`). + + The idea is that we'll evolve this into adding more and more user facing methods over time + as they get requested and we figure out what the right API for them is. + """ + if obj is str: + return self.str_schema() + elif obj is bytes: + return core_schema.bytes_schema() + elif obj is int: + return core_schema.int_schema() + elif obj is float: + return core_schema.float_schema() + elif obj is bool: + return core_schema.bool_schema() + elif obj is Any or obj is object: + return core_schema.any_schema() + elif obj is None or obj is _typing_extra.NoneType: + return core_schema.none_schema() + elif obj in TUPLE_TYPES: + return self._tuple_schema(obj) + elif obj in LIST_TYPES: + return self._list_schema(obj, self._get_first_arg_or_any(obj)) + elif obj in SET_TYPES: + return self._set_schema(obj, self._get_first_arg_or_any(obj)) + elif obj in FROZEN_SET_TYPES: + return self._frozenset_schema(obj, self._get_first_arg_or_any(obj)) + elif obj in DICT_TYPES: + return self._dict_schema(obj, *self._get_first_two_args_or_any(obj)) + elif isinstance(obj, TypeAliasType): + return self._type_alias_type_schema(obj) + elif obj == type: + return self._type_schema() + elif _typing_extra.is_callable_type(obj): + return core_schema.callable_schema() + elif _typing_extra.is_literal_type(obj): + return self._literal_schema(obj) + elif is_typeddict(obj): + return self._typed_dict_schema(obj, None) + elif _typing_extra.is_namedtuple(obj): + return self._namedtuple_schema(obj, None) + elif _typing_extra.is_new_type(obj): + # NewType, can't use isinstance because it fails <3.10 + return self.generate_schema(obj.__supertype__) + elif obj == re.Pattern: + return self._pattern_schema(obj) + elif obj is collections.abc.Hashable or obj is typing.Hashable: + return self._hashable_schema() + elif isinstance(obj, typing.TypeVar): + return self._unsubstituted_typevar_schema(obj) + elif is_finalvar(obj): + if obj is Final: + return core_schema.any_schema() + return self.generate_schema( + self._get_first_arg_or_any(obj), + ) + elif isinstance(obj, (FunctionType, LambdaType, MethodType, partial)): + return self._callable_schema(obj) + elif inspect.isclass(obj) and issubclass(obj, Enum): + from ._std_types_schema import get_enum_core_schema + + return get_enum_core_schema(obj, self._config_wrapper.config_dict) + + if _typing_extra.is_dataclass(obj): + return self._dataclass_schema(obj, None) + res = self._get_prepare_pydantic_annotations_for_known_type(obj, ()) + if res is not None: + source_type, annotations = res + return self._apply_annotations(source_type, annotations) + + origin = get_origin(obj) + if origin is not None: + return self._match_generic_type(obj, origin) + + if self._arbitrary_types: + return self._arbitrary_type_schema(obj) + return self._unknown_type_schema(obj) + + def _match_generic_type(self, obj: Any, origin: Any) -> CoreSchema: # noqa: C901 + if isinstance(origin, TypeAliasType): + return self._type_alias_type_schema(obj) + + # Need to handle generic dataclasses before looking for the schema properties because attribute accesses + # on _GenericAlias delegate to the origin type, so lose the information about the concrete parametrization + # As a result, currently, there is no way to cache the schema for generic dataclasses. This may be possible + # to resolve by modifying the value returned by `Generic.__class_getitem__`, but that is a dangerous game. + if _typing_extra.is_dataclass(origin): + return self._dataclass_schema(obj, origin) + if _typing_extra.is_namedtuple(origin): + return self._namedtuple_schema(obj, origin) + + from_property = self._generate_schema_from_property(origin, obj) + if from_property is not None: + return from_property + + if _typing_extra.origin_is_union(origin): + return self._union_schema(obj) + elif origin in TUPLE_TYPES: + return self._tuple_schema(obj) + elif origin in LIST_TYPES: + return self._list_schema(obj, self._get_first_arg_or_any(obj)) + elif origin in SET_TYPES: + return self._set_schema(obj, self._get_first_arg_or_any(obj)) + elif origin in FROZEN_SET_TYPES: + return self._frozenset_schema(obj, self._get_first_arg_or_any(obj)) + elif origin in DICT_TYPES: + return self._dict_schema(obj, *self._get_first_two_args_or_any(obj)) + elif is_typeddict(origin): + return self._typed_dict_schema(obj, origin) + elif origin in (typing.Type, type): + return self._subclass_schema(obj) + elif origin in {typing.Sequence, collections.abc.Sequence}: + return self._sequence_schema(obj) + elif origin in {typing.Iterable, collections.abc.Iterable, typing.Generator, collections.abc.Generator}: + return self._iterable_schema(obj) + elif origin in (re.Pattern, typing.Pattern): + return self._pattern_schema(obj) + + if self._arbitrary_types: + return self._arbitrary_type_schema(origin) + return self._unknown_type_schema(obj) + + def _generate_td_field_schema( + self, + name: str, + field_info: FieldInfo, + decorators: DecoratorInfos, + *, + required: bool = True, + ) -> core_schema.TypedDictField: + """Prepare a TypedDictField to represent a model or typeddict field.""" + common_field = self._common_field_schema(name, field_info, decorators) + return core_schema.typed_dict_field( + common_field['schema'], + required=False if not field_info.is_required() else required, + serialization_exclude=common_field['serialization_exclude'], + validation_alias=common_field['validation_alias'], + serialization_alias=common_field['serialization_alias'], + metadata=common_field['metadata'], + ) + + def _generate_md_field_schema( + self, + name: str, + field_info: FieldInfo, + decorators: DecoratorInfos, + ) -> core_schema.ModelField: + """Prepare a ModelField to represent a model field.""" + common_field = self._common_field_schema(name, field_info, decorators) + return core_schema.model_field( + common_field['schema'], + serialization_exclude=common_field['serialization_exclude'], + validation_alias=common_field['validation_alias'], + serialization_alias=common_field['serialization_alias'], + frozen=common_field['frozen'], + metadata=common_field['metadata'], + ) + + def _generate_dc_field_schema( + self, + name: str, + field_info: FieldInfo, + decorators: DecoratorInfos, + ) -> core_schema.DataclassField: + """Prepare a DataclassField to represent the parameter/field, of a dataclass.""" + common_field = self._common_field_schema(name, field_info, decorators) + return core_schema.dataclass_field( + name, + common_field['schema'], + init=field_info.init, + init_only=field_info.init_var or None, + kw_only=None if field_info.kw_only else False, + serialization_exclude=common_field['serialization_exclude'], + validation_alias=common_field['validation_alias'], + serialization_alias=common_field['serialization_alias'], + frozen=common_field['frozen'], + metadata=common_field['metadata'], + ) + + @staticmethod + def _apply_alias_generator_to_field_info( + alias_generator: Callable[[str], str] | AliasGenerator, field_info: FieldInfo, field_name: str + ) -> None: + """Apply an alias_generator to aliases on a FieldInfo instance if appropriate. + + Args: + alias_generator: A callable that takes a string and returns a string, or an AliasGenerator instance. + field_info: The FieldInfo instance to which the alias_generator is (maybe) applied. + field_name: The name of the field from which to generate the alias. + """ + # Apply an alias_generator if + # 1. An alias is not specified + # 2. An alias is specified, but the priority is <= 1 + if ( + field_info.alias_priority is None + or field_info.alias_priority <= 1 + or field_info.alias is None + or field_info.validation_alias is None + or field_info.serialization_alias is None + ): + alias, validation_alias, serialization_alias = None, None, None + + if isinstance(alias_generator, AliasGenerator): + alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) + elif isinstance(alias_generator, Callable): + alias = alias_generator(field_name) + if not isinstance(alias, str): + raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') + + # if priority is not set, we set to 1 + # which supports the case where the alias_generator from a child class is used + # to generate an alias for a field in a parent class + if field_info.alias_priority is None or field_info.alias_priority <= 1: + field_info.alias_priority = 1 + + # if the priority is 1, then we set the aliases to the generated alias + if field_info.alias_priority == 1: + field_info.serialization_alias = _get_first_non_null(serialization_alias, alias) + field_info.validation_alias = _get_first_non_null(validation_alias, alias) + field_info.alias = alias + + # if any of the aliases are not set, then we set them to the corresponding generated alias + if field_info.alias is None: + field_info.alias = alias + if field_info.serialization_alias is None: + field_info.serialization_alias = _get_first_non_null(serialization_alias, alias) + if field_info.validation_alias is None: + field_info.validation_alias = _get_first_non_null(validation_alias, alias) + + @staticmethod + def _apply_alias_generator_to_computed_field_info( + alias_generator: Callable[[str], str] | AliasGenerator, + computed_field_info: ComputedFieldInfo, + computed_field_name: str, + ): + """Apply an alias_generator to alias on a ComputedFieldInfo instance if appropriate. + + Args: + alias_generator: A callable that takes a string and returns a string, or an AliasGenerator instance. + computed_field_info: The ComputedFieldInfo instance to which the alias_generator is (maybe) applied. + computed_field_name: The name of the computed field from which to generate the alias. + """ + # Apply an alias_generator if + # 1. An alias is not specified + # 2. An alias is specified, but the priority is <= 1 + + if ( + computed_field_info.alias_priority is None + or computed_field_info.alias_priority <= 1 + or computed_field_info.alias is None + ): + alias, validation_alias, serialization_alias = None, None, None + + if isinstance(alias_generator, AliasGenerator): + alias, validation_alias, serialization_alias = alias_generator.generate_aliases(computed_field_name) + elif isinstance(alias_generator, Callable): + alias = alias_generator(computed_field_name) + if not isinstance(alias, str): + raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') + + # if priority is not set, we set to 1 + # which supports the case where the alias_generator from a child class is used + # to generate an alias for a field in a parent class + if computed_field_info.alias_priority is None or computed_field_info.alias_priority <= 1: + computed_field_info.alias_priority = 1 + + # if the priority is 1, then we set the aliases to the generated alias + # note that we use the serialization_alias with priority over alias, as computed_field + # aliases are used for serialization only (not validation) + if computed_field_info.alias_priority == 1: + computed_field_info.alias = _get_first_non_null(serialization_alias, alias) + + def _common_field_schema( # C901 + self, name: str, field_info: FieldInfo, decorators: DecoratorInfos + ) -> _CommonField: + # Update FieldInfo annotation if appropriate: + from .. import AliasChoices, AliasPath + from ..fields import FieldInfo + + if has_instance_in_type(field_info.annotation, (ForwardRef, str)): + types_namespace = self._types_namespace + if self._typevars_map: + types_namespace = (types_namespace or {}).copy() + # Ensure that typevars get mapped to their concrete types: + types_namespace.update({k.__name__: v for k, v in self._typevars_map.items()}) + + evaluated = _typing_extra.eval_type_lenient(field_info.annotation, types_namespace) + if evaluated is not field_info.annotation and not has_instance_in_type(evaluated, PydanticRecursiveRef): + new_field_info = FieldInfo.from_annotation(evaluated) + field_info.annotation = new_field_info.annotation + + # Handle any field info attributes that may have been obtained from now-resolved annotations + for k, v in new_field_info._attributes_set.items(): + # If an attribute is already set, it means it was set by assigning to a call to Field (or just a + # default value), and that should take the highest priority. So don't overwrite existing attributes. + # We skip over "attributes" that are present in the metadata_lookup dict because these won't + # actually end up as attributes of the `FieldInfo` instance. + if k not in field_info._attributes_set and k not in field_info.metadata_lookup: + setattr(field_info, k, v) + + # Finally, ensure the field info also reflects all the `_attributes_set` that are actually metadata. + field_info.metadata = [*new_field_info.metadata, *field_info.metadata] + + source_type, annotations = field_info.annotation, field_info.metadata + + def set_discriminator(schema: CoreSchema) -> CoreSchema: + schema = self._apply_discriminator_to_union(schema, field_info.discriminator) + return schema + + with self.field_name_stack.push(name): + if field_info.discriminator is not None: + schema = self._apply_annotations(source_type, annotations, transform_inner_schema=set_discriminator) + else: + schema = self._apply_annotations( + source_type, + annotations, + ) + + # This V1 compatibility shim should eventually be removed + # push down any `each_item=True` validators + # note that this won't work for any Annotated types that get wrapped by a function validator + # but that's okay because that didn't exist in V1 + this_field_validators = filter_field_decorator_info_by_field(decorators.validators.values(), name) + if _validators_require_validate_default(this_field_validators): + field_info.validate_default = True + each_item_validators = [v for v in this_field_validators if v.info.each_item is True] + this_field_validators = [v for v in this_field_validators if v not in each_item_validators] + schema = apply_each_item_validators(schema, each_item_validators, name) + + schema = apply_validators(schema, filter_field_decorator_info_by_field(this_field_validators, name), name) + schema = apply_validators( + schema, filter_field_decorator_info_by_field(decorators.field_validators.values(), name), name + ) + + # the default validator needs to go outside of any other validators + # so that it is the topmost validator for the field validator + # which uses it to check if the field has a default value or not + if not field_info.is_required(): + schema = wrap_default(field_info, schema) + + schema = self._apply_field_serializers( + schema, filter_field_decorator_info_by_field(decorators.field_serializers.values(), name) + ) + json_schema_updates = { + 'title': field_info.title, + 'description': field_info.description, + 'deprecated': bool(field_info.deprecated) or field_info.deprecated == '' or None, + 'examples': to_jsonable_python(field_info.examples), + } + json_schema_updates = {k: v for k, v in json_schema_updates.items() if v is not None} + + json_schema_extra = field_info.json_schema_extra + + metadata = build_metadata_dict( + js_annotation_functions=[get_json_schema_update_func(json_schema_updates, json_schema_extra)] + ) + + alias_generator = self._config_wrapper.alias_generator + if alias_generator is not None: + self._apply_alias_generator_to_field_info(alias_generator, field_info, name) + + if isinstance(field_info.validation_alias, (AliasChoices, AliasPath)): + validation_alias = field_info.validation_alias.convert_to_aliases() + else: + validation_alias = field_info.validation_alias + + return _common_field( + schema, + serialization_exclude=True if field_info.exclude else None, + validation_alias=validation_alias, + serialization_alias=field_info.serialization_alias, + frozen=field_info.frozen, + metadata=metadata, + ) + + def _union_schema(self, union_type: Any) -> core_schema.CoreSchema: + """Generate schema for a Union.""" + args = self._get_args_resolving_forward_refs(union_type, required=True) + choices: list[CoreSchema] = [] + nullable = False + for arg in args: + if arg is None or arg is _typing_extra.NoneType: + nullable = True + else: + choices.append(self.generate_schema(arg)) + + if len(choices) == 1: + s = choices[0] + else: + choices_with_tags: list[CoreSchema | tuple[CoreSchema, str]] = [] + for choice in choices: + tag = choice.get('metadata', {}).get(_core_utils.TAGGED_UNION_TAG_KEY) + if tag is not None: + choices_with_tags.append((choice, tag)) + else: + choices_with_tags.append(choice) + s = core_schema.union_schema(choices_with_tags) + + if nullable: + s = core_schema.nullable_schema(s) + return s + + def _type_alias_type_schema( + self, + obj: Any, # TypeAliasType + ) -> CoreSchema: + with self.defs.get_schema_or_ref(obj) as (ref, maybe_schema): + if maybe_schema is not None: + return maybe_schema + + origin = get_origin(obj) or obj + + annotation = origin.__value__ + typevars_map = get_standard_typevars_map(obj) + + with self._types_namespace_stack.push(origin): + annotation = _typing_extra.eval_type_lenient(annotation, self._types_namespace) + annotation = replace_types(annotation, typevars_map) + schema = self.generate_schema(annotation) + assert schema['type'] != 'definitions' + schema['ref'] = ref # type: ignore + self.defs.definitions[ref] = schema + return core_schema.definition_reference_schema(ref) + + def _literal_schema(self, literal_type: Any) -> CoreSchema: + """Generate schema for a Literal.""" + expected = _typing_extra.all_literal_values(literal_type) + assert expected, f'literal "expected" cannot be empty, obj={literal_type}' + return core_schema.literal_schema(expected) + + def _typed_dict_schema(self, typed_dict_cls: Any, origin: Any) -> core_schema.CoreSchema: + """Generate schema for a TypedDict. + + It is not possible to track required/optional keys in TypedDict without __required_keys__ + since TypedDict.__new__ erases the base classes (it replaces them with just `dict`) + and thus we can track usage of total=True/False + __required_keys__ was added in Python 3.9 + (https://github.com/miss-islington/cpython/blob/1e9939657dd1f8eb9f596f77c1084d2d351172fc/Doc/library/typing.rst?plain=1#L1546-L1548) + however it is buggy + (https://github.com/python/typing_extensions/blob/ac52ac5f2cb0e00e7988bae1e2a1b8257ac88d6d/src/typing_extensions.py#L657-L666). + + On 3.11 but < 3.12 TypedDict does not preserve inheritance information. + + Hence to avoid creating validators that do not do what users expect we only + support typing.TypedDict on Python >= 3.12 or typing_extension.TypedDict on all versions + """ + from ..fields import FieldInfo + + with self.model_type_stack.push(typed_dict_cls), self.defs.get_schema_or_ref(typed_dict_cls) as ( + typed_dict_ref, + maybe_schema, + ): + if maybe_schema is not None: + return maybe_schema + + typevars_map = get_standard_typevars_map(typed_dict_cls) + if origin is not None: + typed_dict_cls = origin + + if not _SUPPORTS_TYPEDDICT and type(typed_dict_cls).__module__ == 'typing': + raise PydanticUserError( + 'Please use `typing_extensions.TypedDict` instead of `typing.TypedDict` on Python < 3.12.', + code='typed-dict-version', + ) + + try: + config: ConfigDict | None = get_attribute_from_bases(typed_dict_cls, '__pydantic_config__') + except AttributeError: + config = None + + with self._config_wrapper_stack.push(config), self._types_namespace_stack.push(typed_dict_cls): + core_config = self._config_wrapper.core_config(typed_dict_cls) + + self = self._current_generate_schema + + required_keys: frozenset[str] = typed_dict_cls.__required_keys__ + + fields: dict[str, core_schema.TypedDictField] = {} + + decorators = DecoratorInfos.build(typed_dict_cls) + + if self._config_wrapper.use_attribute_docstrings: + field_docstrings = extract_docstrings_from_cls(typed_dict_cls, use_inspect=True) + else: + field_docstrings = None + + for field_name, annotation in get_type_hints_infer_globalns( + typed_dict_cls, localns=self._types_namespace, include_extras=True + ).items(): + annotation = replace_types(annotation, typevars_map) + required = field_name in required_keys + + if get_origin(annotation) == _typing_extra.Required: + required = True + annotation = self._get_args_resolving_forward_refs( + annotation, + required=True, + )[0] + elif get_origin(annotation) == _typing_extra.NotRequired: + required = False + annotation = self._get_args_resolving_forward_refs( + annotation, + required=True, + )[0] + + field_info = FieldInfo.from_annotation(annotation) + if ( + field_docstrings is not None + and field_info.description is None + and field_name in field_docstrings + ): + field_info.description = field_docstrings[field_name] + fields[field_name] = self._generate_td_field_schema( + field_name, field_info, decorators, required=required + ) + + metadata = build_metadata_dict( + js_functions=[partial(modify_model_json_schema, cls=typed_dict_cls)], typed_dict_cls=typed_dict_cls + ) + + td_schema = core_schema.typed_dict_schema( + fields, + computed_fields=[ + self._computed_field_schema(d, decorators.field_serializers) + for d in decorators.computed_fields.values() + ], + ref=typed_dict_ref, + metadata=metadata, + config=core_config, + ) + + schema = self._apply_model_serializers(td_schema, decorators.model_serializers.values()) + schema = apply_model_validators(schema, decorators.model_validators.values(), 'all') + self.defs.definitions[typed_dict_ref] = schema + return core_schema.definition_reference_schema(typed_dict_ref) + + def _namedtuple_schema(self, namedtuple_cls: Any, origin: Any) -> core_schema.CoreSchema: + """Generate schema for a NamedTuple.""" + with self.model_type_stack.push(namedtuple_cls), self.defs.get_schema_or_ref(namedtuple_cls) as ( + namedtuple_ref, + maybe_schema, + ): + if maybe_schema is not None: + return maybe_schema + typevars_map = get_standard_typevars_map(namedtuple_cls) + if origin is not None: + namedtuple_cls = origin + + annotations: dict[str, Any] = get_type_hints_infer_globalns( + namedtuple_cls, include_extras=True, localns=self._types_namespace + ) + if not annotations: + # annotations is empty, happens if namedtuple_cls defined via collections.namedtuple(...) + annotations = {k: Any for k in namedtuple_cls._fields} + + if typevars_map: + annotations = { + field_name: replace_types(annotation, typevars_map) + for field_name, annotation in annotations.items() + } + + arguments_schema = core_schema.arguments_schema( + [ + self._generate_parameter_schema( + field_name, annotation, default=namedtuple_cls._field_defaults.get(field_name, Parameter.empty) + ) + for field_name, annotation in annotations.items() + ], + metadata=build_metadata_dict(js_prefer_positional_arguments=True), + ) + return core_schema.call_schema(arguments_schema, namedtuple_cls, ref=namedtuple_ref) + + def _generate_parameter_schema( + self, + name: str, + annotation: type[Any], + default: Any = Parameter.empty, + mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] | None = None, + ) -> core_schema.ArgumentsParameter: + """Prepare a ArgumentsParameter to represent a field in a namedtuple or function signature.""" + from ..fields import FieldInfo + + if default is Parameter.empty: + field = FieldInfo.from_annotation(annotation) + else: + field = FieldInfo.from_annotated_attribute(annotation, default) + assert field.annotation is not None, 'field.annotation should not be None when generating a schema' + source_type, annotations = field.annotation, field.metadata + with self.field_name_stack.push(name): + schema = self._apply_annotations(source_type, annotations) + + if not field.is_required(): + schema = wrap_default(field, schema) + + parameter_schema = core_schema.arguments_parameter(name, schema) + if mode is not None: + parameter_schema['mode'] = mode + if field.alias is not None: + parameter_schema['alias'] = field.alias + else: + alias_generator = self._config_wrapper.alias_generator + if isinstance(alias_generator, AliasGenerator) and alias_generator.alias is not None: + parameter_schema['alias'] = alias_generator.alias(name) + elif isinstance(alias_generator, Callable): + parameter_schema['alias'] = alias_generator(name) + return parameter_schema + + def _tuple_schema(self, tuple_type: Any) -> core_schema.CoreSchema: + """Generate schema for a Tuple, e.g. `tuple[int, str]` or `tuple[int, ...]`.""" + # TODO: do we really need to resolve type vars here? + typevars_map = get_standard_typevars_map(tuple_type) + params = self._get_args_resolving_forward_refs(tuple_type) + + if typevars_map and params: + params = tuple(replace_types(param, typevars_map) for param in params) + + # NOTE: subtle difference: `tuple[()]` gives `params=()`, whereas `typing.Tuple[()]` gives `params=((),)` + # This is only true for <3.11, on Python 3.11+ `typing.Tuple[()]` gives `params=()` + if not params: + if tuple_type in TUPLE_TYPES: + return core_schema.tuple_schema([core_schema.any_schema()], variadic_item_index=0) + else: + # special case for `tuple[()]` which means `tuple[]` - an empty tuple + return core_schema.tuple_schema([]) + elif params[-1] is Ellipsis: + if len(params) == 2: + return core_schema.tuple_schema([self.generate_schema(params[0])], variadic_item_index=0) + else: + # TODO: something like https://github.com/pydantic/pydantic/issues/5952 + raise ValueError('Variable tuples can only have one type') + elif len(params) == 1 and params[0] == (): + # special case for `Tuple[()]` which means `Tuple[]` - an empty tuple + # NOTE: This conditional can be removed when we drop support for Python 3.10. + return core_schema.tuple_schema([]) + else: + return core_schema.tuple_schema([self.generate_schema(param) for param in params]) + + def _type_schema(self) -> core_schema.CoreSchema: + return core_schema.custom_error_schema( + core_schema.is_instance_schema(type), + custom_error_type='is_type', + custom_error_message='Input should be a type', + ) + + def _union_is_subclass_schema(self, union_type: Any) -> core_schema.CoreSchema: + """Generate schema for `Type[Union[X, ...]]`.""" + args = self._get_args_resolving_forward_refs(union_type, required=True) + return core_schema.union_schema([self.generate_schema(typing.Type[args]) for args in args]) + + def _subclass_schema(self, type_: Any) -> core_schema.CoreSchema: + """Generate schema for a Type, e.g. `Type[int]`.""" + type_param = self._get_first_arg_or_any(type_) + if type_param == Any: + return self._type_schema() + elif isinstance(type_param, typing.TypeVar): + if type_param.__bound__: + if _typing_extra.origin_is_union(get_origin(type_param.__bound__)): + return self._union_is_subclass_schema(type_param.__bound__) + return core_schema.is_subclass_schema(type_param.__bound__) + elif type_param.__constraints__: + return core_schema.union_schema( + [self.generate_schema(typing.Type[c]) for c in type_param.__constraints__] + ) + else: + return self._type_schema() + elif _typing_extra.origin_is_union(get_origin(type_param)): + return self._union_is_subclass_schema(type_param) + else: + return core_schema.is_subclass_schema(type_param) + + def _sequence_schema(self, sequence_type: Any) -> core_schema.CoreSchema: + """Generate schema for a Sequence, e.g. `Sequence[int]`.""" + from ._std_types_schema import serialize_sequence_via_list + + item_type = self._get_first_arg_or_any(sequence_type) + item_type_schema = self.generate_schema(item_type) + list_schema = core_schema.list_schema(item_type_schema) + + python_schema = core_schema.is_instance_schema(typing.Sequence, cls_repr='Sequence') + if item_type != Any: + from ._validators import sequence_validator + + python_schema = core_schema.chain_schema( + [python_schema, core_schema.no_info_wrap_validator_function(sequence_validator, list_schema)], + ) + + serialization = core_schema.wrap_serializer_function_ser_schema( + serialize_sequence_via_list, schema=item_type_schema, info_arg=True + ) + return core_schema.json_or_python_schema( + json_schema=list_schema, python_schema=python_schema, serialization=serialization + ) + + def _iterable_schema(self, type_: Any) -> core_schema.GeneratorSchema: + """Generate a schema for an `Iterable`.""" + item_type = self._get_first_arg_or_any(type_) + + return core_schema.generator_schema(self.generate_schema(item_type)) + + def _pattern_schema(self, pattern_type: Any) -> core_schema.CoreSchema: + from . import _validators + + metadata = build_metadata_dict(js_functions=[lambda _1, _2: {'type': 'string', 'format': 'regex'}]) + ser = core_schema.plain_serializer_function_ser_schema( + attrgetter('pattern'), when_used='json', return_schema=core_schema.str_schema() + ) + if pattern_type == typing.Pattern or pattern_type == re.Pattern: + # bare type + return core_schema.no_info_plain_validator_function( + _validators.pattern_either_validator, serialization=ser, metadata=metadata + ) + + param = self._get_args_resolving_forward_refs( + pattern_type, + required=True, + )[0] + if param == str: + return core_schema.no_info_plain_validator_function( + _validators.pattern_str_validator, serialization=ser, metadata=metadata + ) + elif param == bytes: + return core_schema.no_info_plain_validator_function( + _validators.pattern_bytes_validator, serialization=ser, metadata=metadata + ) + else: + raise PydanticSchemaGenerationError(f'Unable to generate pydantic-core schema for {pattern_type!r}.') + + def _hashable_schema(self) -> core_schema.CoreSchema: + return core_schema.custom_error_schema( + core_schema.is_instance_schema(collections.abc.Hashable), + custom_error_type='is_hashable', + custom_error_message='Input should be hashable', + ) + + def _dataclass_schema( + self, dataclass: type[StandardDataclass], origin: type[StandardDataclass] | None + ) -> core_schema.CoreSchema: + """Generate schema for a dataclass.""" + with self.model_type_stack.push(dataclass), self.defs.get_schema_or_ref(dataclass) as ( + dataclass_ref, + maybe_schema, + ): + if maybe_schema is not None: + return maybe_schema + + typevars_map = get_standard_typevars_map(dataclass) + if origin is not None: + dataclass = origin + + with ExitStack() as dataclass_bases_stack: + # Pushing a namespace prioritises items already in the stack, so iterate though the MRO forwards + for dataclass_base in dataclass.__mro__: + if dataclasses.is_dataclass(dataclass_base): + dataclass_bases_stack.enter_context(self._types_namespace_stack.push(dataclass_base)) + + # Pushing a config overwrites the previous config, so iterate though the MRO backwards + for dataclass_base in reversed(dataclass.__mro__): + if dataclasses.is_dataclass(dataclass_base): + config = getattr(dataclass_base, '__pydantic_config__', None) + dataclass_bases_stack.enter_context(self._config_wrapper_stack.push(config)) + + core_config = self._config_wrapper.core_config(dataclass) + + self = self._current_generate_schema + + from ..dataclasses import is_pydantic_dataclass + + if is_pydantic_dataclass(dataclass): + fields = deepcopy(dataclass.__pydantic_fields__) + if typevars_map: + for field in fields.values(): + field.apply_typevars_map(typevars_map, self._types_namespace) + else: + fields = collect_dataclass_fields( + dataclass, + self._types_namespace, + typevars_map=typevars_map, + ) + + # disallow combination of init=False on a dataclass field and extra='allow' on a dataclass + if self._config_wrapper_stack.tail.extra == 'allow': + # disallow combination of init=False on a dataclass field and extra='allow' on a dataclass + for field_name, field in fields.items(): + if field.init is False: + raise PydanticUserError( + f'Field {field_name} has `init=False` and dataclass has config setting `extra="allow"`. ' + f'This combination is not allowed.', + code='dataclass-init-false-extra-allow', + ) + + decorators = dataclass.__dict__.get('__pydantic_decorators__') or DecoratorInfos.build(dataclass) + # Move kw_only=False args to the start of the list, as this is how vanilla dataclasses work. + # Note that when kw_only is missing or None, it is treated as equivalent to kw_only=True + args = sorted( + (self._generate_dc_field_schema(k, v, decorators) for k, v in fields.items()), + key=lambda a: a.get('kw_only') is not False, + ) + has_post_init = hasattr(dataclass, '__post_init__') + has_slots = hasattr(dataclass, '__slots__') + + args_schema = core_schema.dataclass_args_schema( + dataclass.__name__, + args, + computed_fields=[ + self._computed_field_schema(d, decorators.field_serializers) + for d in decorators.computed_fields.values() + ], + collect_init_only=has_post_init, + ) + + inner_schema = apply_validators(args_schema, decorators.root_validators.values(), None) + + model_validators = decorators.model_validators.values() + inner_schema = apply_model_validators(inner_schema, model_validators, 'inner') + + dc_schema = core_schema.dataclass_schema( + dataclass, + inner_schema, + post_init=has_post_init, + ref=dataclass_ref, + fields=[field.name for field in dataclasses.fields(dataclass)], + slots=has_slots, + config=core_config, + ) + schema = self._apply_model_serializers(dc_schema, decorators.model_serializers.values()) + schema = apply_model_validators(schema, model_validators, 'outer') + self.defs.definitions[dataclass_ref] = schema + return core_schema.definition_reference_schema(dataclass_ref) + + # Type checkers seem to assume ExitStack may suppress exceptions and therefore + # control flow can exit the `with` block without returning. + assert False, 'Unreachable' + + def _callable_schema(self, function: Callable[..., Any]) -> core_schema.CallSchema: + """Generate schema for a Callable. + + TODO support functional validators once we support them in Config + """ + sig = signature(function) + + type_hints = _typing_extra.get_function_type_hints(function) + + mode_lookup: dict[_ParameterKind, Literal['positional_only', 'positional_or_keyword', 'keyword_only']] = { + Parameter.POSITIONAL_ONLY: 'positional_only', + Parameter.POSITIONAL_OR_KEYWORD: 'positional_or_keyword', + Parameter.KEYWORD_ONLY: 'keyword_only', + } + + arguments_list: list[core_schema.ArgumentsParameter] = [] + var_args_schema: core_schema.CoreSchema | None = None + var_kwargs_schema: core_schema.CoreSchema | None = None + + for name, p in sig.parameters.items(): + if p.annotation is sig.empty: + annotation = Any + else: + annotation = type_hints[name] + + parameter_mode = mode_lookup.get(p.kind) + if parameter_mode is not None: + arg_schema = self._generate_parameter_schema(name, annotation, p.default, parameter_mode) + arguments_list.append(arg_schema) + elif p.kind == Parameter.VAR_POSITIONAL: + var_args_schema = self.generate_schema(annotation) + else: + assert p.kind == Parameter.VAR_KEYWORD, p.kind + var_kwargs_schema = self.generate_schema(annotation) + + return_schema: core_schema.CoreSchema | None = None + config_wrapper = self._config_wrapper + if config_wrapper.validate_return: + return_hint = type_hints.get('return') + if return_hint is not None: + return_schema = self.generate_schema(return_hint) + + return core_schema.call_schema( + core_schema.arguments_schema( + arguments_list, + var_args_schema=var_args_schema, + var_kwargs_schema=var_kwargs_schema, + populate_by_name=config_wrapper.populate_by_name, + ), + function, + return_schema=return_schema, + ) + + def _unsubstituted_typevar_schema(self, typevar: typing.TypeVar) -> core_schema.CoreSchema: + assert isinstance(typevar, typing.TypeVar) + + bound = typevar.__bound__ + constraints = typevar.__constraints__ + default = getattr(typevar, '__default__', None) + + if (bound is not None) + (len(constraints) != 0) + (default is not None) > 1: + raise NotImplementedError( + 'Pydantic does not support mixing more than one of TypeVar bounds, constraints and defaults' + ) + + if default is not None: + return self.generate_schema(default) + elif constraints: + return self._union_schema(typing.Union[constraints]) # type: ignore + elif bound: + schema = self.generate_schema(bound) + schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + lambda x, h: h(x), schema=core_schema.any_schema() + ) + return schema + else: + return core_schema.any_schema() + + def _computed_field_schema( + self, + d: Decorator[ComputedFieldInfo], + field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]], + ) -> core_schema.ComputedField: + try: + return_type = _decorators.get_function_return_type(d.func, d.info.return_type, self._types_namespace) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + if return_type is PydanticUndefined: + raise PydanticUserError( + 'Computed field is missing return type annotation or specifying `return_type`' + ' to the `@computed_field` decorator (e.g. `@computed_field(return_type=int|str)`)', + code='model-field-missing-annotation', + ) + + return_type = replace_types(return_type, self._typevars_map) + # Create a new ComputedFieldInfo so that different type parametrizations of the same + # generic model's computed field can have different return types. + d.info = dataclasses.replace(d.info, return_type=return_type) + return_type_schema = self.generate_schema(return_type) + # Apply serializers to computed field if there exist + return_type_schema = self._apply_field_serializers( + return_type_schema, + filter_field_decorator_info_by_field(field_serializers.values(), d.cls_var_name), + computed_field=True, + ) + + alias_generator = self._config_wrapper.alias_generator + if alias_generator is not None: + self._apply_alias_generator_to_computed_field_info( + alias_generator=alias_generator, computed_field_info=d.info, computed_field_name=d.cls_var_name + ) + + def set_computed_field_metadata(schema: CoreSchemaOrField, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(schema) + + json_schema['readOnly'] = True + + title = d.info.title + if title is not None: + json_schema['title'] = title + + description = d.info.description + if description is not None: + json_schema['description'] = description + + if d.info.deprecated or d.info.deprecated == '': + json_schema['deprecated'] = True + + examples = d.info.examples + if examples is not None: + json_schema['examples'] = to_jsonable_python(examples) + + json_schema_extra = d.info.json_schema_extra + if json_schema_extra is not None: + add_json_schema_extra(json_schema, json_schema_extra) + + return json_schema + + metadata = build_metadata_dict(js_annotation_functions=[set_computed_field_metadata]) + return core_schema.computed_field( + d.cls_var_name, return_schema=return_type_schema, alias=d.info.alias, metadata=metadata + ) + + def _annotated_schema(self, annotated_type: Any) -> core_schema.CoreSchema: + """Generate schema for an Annotated type, e.g. `Annotated[int, Field(...)]` or `Annotated[int, Gt(0)]`.""" + from ..fields import FieldInfo + + source_type, *annotations = self._get_args_resolving_forward_refs( + annotated_type, + required=True, + ) + schema = self._apply_annotations(source_type, annotations) + # put the default validator last so that TypeAdapter.get_default_value() works + # even if there are function validators involved + for annotation in annotations: + if isinstance(annotation, FieldInfo): + schema = wrap_default(annotation, schema) + return schema + + def _get_prepare_pydantic_annotations_for_known_type( + self, obj: Any, annotations: tuple[Any, ...] + ) -> tuple[Any, list[Any]] | None: + from ._std_types_schema import PREPARE_METHODS + + # Check for hashability + try: + hash(obj) + except TypeError: + # obj is definitely not a known type if this fails + return None + + for gen in PREPARE_METHODS: + res = gen(obj, annotations, self._config_wrapper.config_dict) + if res is not None: + return res + + return None + + def _apply_annotations( + self, + source_type: Any, + annotations: list[Any], + transform_inner_schema: Callable[[CoreSchema], CoreSchema] = lambda x: x, + ) -> CoreSchema: + """Apply arguments from `Annotated` or from `FieldInfo` to a schema. + + This gets called by `GenerateSchema._annotated_schema` but differs from it in that it does + not expect `source_type` to be an `Annotated` object, it expects it to be the first argument of that + (in other words, `GenerateSchema._annotated_schema` just unpacks `Annotated`, this process it). + """ + annotations = list(_known_annotated_metadata.expand_grouped_metadata(annotations)) + res = self._get_prepare_pydantic_annotations_for_known_type(source_type, tuple(annotations)) + if res is not None: + source_type, annotations = res + + pydantic_js_annotation_functions: list[GetJsonSchemaFunction] = [] + + def inner_handler(obj: Any) -> CoreSchema: + from_property = self._generate_schema_from_property(obj, obj) + if from_property is None: + schema = self._generate_schema_inner(obj) + else: + schema = from_property + metadata_js_function = _extract_get_pydantic_json_schema(obj, schema) + if metadata_js_function is not None: + metadata_schema = resolve_original_schema(schema, self.defs.definitions) + if metadata_schema is not None: + self._add_js_function(metadata_schema, metadata_js_function) + return transform_inner_schema(schema) + + get_inner_schema = CallbackGetCoreSchemaHandler(inner_handler, self) + + for annotation in annotations: + if annotation is None: + continue + get_inner_schema = self._get_wrapped_inner_schema( + get_inner_schema, annotation, pydantic_js_annotation_functions + ) + + schema = get_inner_schema(source_type) + if pydantic_js_annotation_functions: + metadata = CoreMetadataHandler(schema).metadata + metadata.setdefault('pydantic_js_annotation_functions', []).extend(pydantic_js_annotation_functions) + return _add_custom_serialization_from_json_encoders(self._config_wrapper.json_encoders, source_type, schema) + + def _apply_single_annotation(self, schema: core_schema.CoreSchema, metadata: Any) -> core_schema.CoreSchema: + from ..fields import FieldInfo + + if isinstance(metadata, FieldInfo): + for field_metadata in metadata.metadata: + schema = self._apply_single_annotation(schema, field_metadata) + + if metadata.discriminator is not None: + schema = self._apply_discriminator_to_union(schema, metadata.discriminator) + return schema + + if schema['type'] == 'nullable': + # for nullable schemas, metadata is automatically applied to the inner schema + inner = schema.get('schema', core_schema.any_schema()) + inner = self._apply_single_annotation(inner, metadata) + if inner: + schema['schema'] = inner + return schema + + original_schema = schema + ref = schema.get('ref', None) + if ref is not None: + schema = schema.copy() + new_ref = ref + f'_{repr(metadata)}' + if new_ref in self.defs.definitions: + return self.defs.definitions[new_ref] + schema['ref'] = new_ref # type: ignore + elif schema['type'] == 'definition-ref': + ref = schema['schema_ref'] + if ref in self.defs.definitions: + schema = self.defs.definitions[ref].copy() + new_ref = ref + f'_{repr(metadata)}' + if new_ref in self.defs.definitions: + return self.defs.definitions[new_ref] + schema['ref'] = new_ref # type: ignore + + maybe_updated_schema = _known_annotated_metadata.apply_known_metadata(metadata, schema.copy()) + + if maybe_updated_schema is not None: + return maybe_updated_schema + return original_schema + + def _apply_single_annotation_json_schema( + self, schema: core_schema.CoreSchema, metadata: Any + ) -> core_schema.CoreSchema: + from ..fields import FieldInfo + + if isinstance(metadata, FieldInfo): + for field_metadata in metadata.metadata: + schema = self._apply_single_annotation_json_schema(schema, field_metadata) + json_schema_update: JsonSchemaValue = {} + if metadata.title: + json_schema_update['title'] = metadata.title + if metadata.description: + json_schema_update['description'] = metadata.description + if metadata.examples: + json_schema_update['examples'] = to_jsonable_python(metadata.examples) + + json_schema_extra = metadata.json_schema_extra + if json_schema_update or json_schema_extra: + CoreMetadataHandler(schema).metadata.setdefault('pydantic_js_annotation_functions', []).append( + get_json_schema_update_func(json_schema_update, json_schema_extra) + ) + return schema + + def _get_wrapped_inner_schema( + self, + get_inner_schema: GetCoreSchemaHandler, + annotation: Any, + pydantic_js_annotation_functions: list[GetJsonSchemaFunction], + ) -> CallbackGetCoreSchemaHandler: + metadata_get_schema: GetCoreSchemaFunction = getattr(annotation, '__get_pydantic_core_schema__', None) or ( + lambda source, handler: handler(source) + ) + + def new_handler(source: Any) -> core_schema.CoreSchema: + schema = metadata_get_schema(source, get_inner_schema) + schema = self._apply_single_annotation(schema, annotation) + schema = self._apply_single_annotation_json_schema(schema, annotation) + + metadata_js_function = _extract_get_pydantic_json_schema(annotation, schema) + if metadata_js_function is not None: + pydantic_js_annotation_functions.append(metadata_js_function) + return schema + + return CallbackGetCoreSchemaHandler(new_handler, self) + + def _apply_field_serializers( + self, + schema: core_schema.CoreSchema, + serializers: list[Decorator[FieldSerializerDecoratorInfo]], + computed_field: bool = False, + ) -> core_schema.CoreSchema: + """Apply field serializers to a schema.""" + if serializers: + schema = copy(schema) + if schema['type'] == 'definitions': + inner_schema = schema['schema'] + schema['schema'] = self._apply_field_serializers(inner_schema, serializers) + return schema + else: + ref = typing.cast('str|None', schema.get('ref', None)) + if ref is not None: + schema = core_schema.definition_reference_schema(ref) + + # use the last serializer to make it easy to override a serializer set on a parent model + serializer = serializers[-1] + is_field_serializer, info_arg = inspect_field_serializer( + serializer.func, serializer.info.mode, computed_field=computed_field + ) + + try: + return_type = _decorators.get_function_return_type( + serializer.func, serializer.info.return_type, self._types_namespace + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + + if return_type is PydanticUndefined: + return_schema = None + else: + return_schema = self.generate_schema(return_type) + + if serializer.info.mode == 'wrap': + schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + serializer.func, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + return_schema=return_schema, + when_used=serializer.info.when_used, + ) + else: + assert serializer.info.mode == 'plain' + schema['serialization'] = core_schema.plain_serializer_function_ser_schema( + serializer.func, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + return_schema=return_schema, + when_used=serializer.info.when_used, + ) + return schema + + def _apply_model_serializers( + self, schema: core_schema.CoreSchema, serializers: Iterable[Decorator[ModelSerializerDecoratorInfo]] + ) -> core_schema.CoreSchema: + """Apply model serializers to a schema.""" + ref: str | None = schema.pop('ref', None) # type: ignore + if serializers: + serializer = list(serializers)[-1] + info_arg = inspect_model_serializer(serializer.func, serializer.info.mode) + + try: + return_type = _decorators.get_function_return_type( + serializer.func, serializer.info.return_type, self._types_namespace + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + if return_type is PydanticUndefined: + return_schema = None + else: + return_schema = self.generate_schema(return_type) + + if serializer.info.mode == 'wrap': + ser_schema: core_schema.SerSchema = core_schema.wrap_serializer_function_ser_schema( + serializer.func, + info_arg=info_arg, + return_schema=return_schema, + when_used=serializer.info.when_used, + ) + else: + # plain + ser_schema = core_schema.plain_serializer_function_ser_schema( + serializer.func, + info_arg=info_arg, + return_schema=return_schema, + when_used=serializer.info.when_used, + ) + schema['serialization'] = ser_schema + if ref: + schema['ref'] = ref # type: ignore + return schema + + +_VALIDATOR_F_MATCH: Mapping[ + tuple[FieldValidatorModes, Literal['no-info', 'with-info']], + Callable[[Callable[..., Any], core_schema.CoreSchema, str | None], core_schema.CoreSchema], +] = { + ('before', 'no-info'): lambda f, schema, _: core_schema.no_info_before_validator_function(f, schema), + ('after', 'no-info'): lambda f, schema, _: core_schema.no_info_after_validator_function(f, schema), + ('plain', 'no-info'): lambda f, _1, _2: core_schema.no_info_plain_validator_function(f), + ('wrap', 'no-info'): lambda f, schema, _: core_schema.no_info_wrap_validator_function(f, schema), + ('before', 'with-info'): lambda f, schema, field_name: core_schema.with_info_before_validator_function( + f, schema, field_name=field_name + ), + ('after', 'with-info'): lambda f, schema, field_name: core_schema.with_info_after_validator_function( + f, schema, field_name=field_name + ), + ('plain', 'with-info'): lambda f, _, field_name: core_schema.with_info_plain_validator_function( + f, field_name=field_name + ), + ('wrap', 'with-info'): lambda f, schema, field_name: core_schema.with_info_wrap_validator_function( + f, schema, field_name=field_name + ), +} + + +def apply_validators( + schema: core_schema.CoreSchema, + validators: Iterable[Decorator[RootValidatorDecoratorInfo]] + | Iterable[Decorator[ValidatorDecoratorInfo]] + | Iterable[Decorator[FieldValidatorDecoratorInfo]], + field_name: str | None, +) -> core_schema.CoreSchema: + """Apply validators to a schema. + + Args: + schema: The schema to apply validators on. + validators: An iterable of validators. + field_name: The name of the field if validators are being applied to a model field. + + Returns: + The updated schema. + """ + for validator in validators: + info_arg = inspect_validator(validator.func, validator.info.mode) + val_type = 'with-info' if info_arg else 'no-info' + + schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, schema, field_name) + return schema + + +def _validators_require_validate_default(validators: Iterable[Decorator[ValidatorDecoratorInfo]]) -> bool: + """In v1, if any of the validators for a field had `always=True`, the default value would be validated. + + This serves as an auxiliary function for re-implementing that logic, by looping over a provided + collection of (v1-style) ValidatorDecoratorInfo's and checking if any of them have `always=True`. + + We should be able to drop this function and the associated logic calling it once we drop support + for v1-style validator decorators. (Or we can extend it and keep it if we add something equivalent + to the v1-validator `always` kwarg to `field_validator`.) + """ + for validator in validators: + if validator.info.always: + return True + return False + + +def apply_model_validators( + schema: core_schema.CoreSchema, + validators: Iterable[Decorator[ModelValidatorDecoratorInfo]], + mode: Literal['inner', 'outer', 'all'], +) -> core_schema.CoreSchema: + """Apply model validators to a schema. + + If mode == 'inner', only "before" validators are applied + If mode == 'outer', validators other than "before" are applied + If mode == 'all', all validators are applied + + Args: + schema: The schema to apply validators on. + validators: An iterable of validators. + mode: The validator mode. + + Returns: + The updated schema. + """ + ref: str | None = schema.pop('ref', None) # type: ignore + for validator in validators: + if mode == 'inner' and validator.info.mode != 'before': + continue + if mode == 'outer' and validator.info.mode == 'before': + continue + info_arg = inspect_validator(validator.func, validator.info.mode) + if validator.info.mode == 'wrap': + if info_arg: + schema = core_schema.with_info_wrap_validator_function(function=validator.func, schema=schema) + else: + schema = core_schema.no_info_wrap_validator_function(function=validator.func, schema=schema) + elif validator.info.mode == 'before': + if info_arg: + schema = core_schema.with_info_before_validator_function(function=validator.func, schema=schema) + else: + schema = core_schema.no_info_before_validator_function(function=validator.func, schema=schema) + else: + assert validator.info.mode == 'after' + if info_arg: + schema = core_schema.with_info_after_validator_function(function=validator.func, schema=schema) + else: + schema = core_schema.no_info_after_validator_function(function=validator.func, schema=schema) + if ref: + schema['ref'] = ref # type: ignore + return schema + + +def wrap_default(field_info: FieldInfo, schema: core_schema.CoreSchema) -> core_schema.CoreSchema: + """Wrap schema with default schema if default value or `default_factory` are available. + + Args: + field_info: The field info object. + schema: The schema to apply default on. + + Returns: + Updated schema by default value or `default_factory`. + """ + if field_info.default_factory: + return core_schema.with_default_schema( + schema, default_factory=field_info.default_factory, validate_default=field_info.validate_default + ) + elif field_info.default is not PydanticUndefined: + return core_schema.with_default_schema( + schema, default=field_info.default, validate_default=field_info.validate_default + ) + else: + return schema + + +def _extract_get_pydantic_json_schema(tp: Any, schema: CoreSchema) -> GetJsonSchemaFunction | None: + """Extract `__get_pydantic_json_schema__` from a type, handling the deprecated `__modify_schema__`.""" + js_modify_function = getattr(tp, '__get_pydantic_json_schema__', None) + + if hasattr(tp, '__modify_schema__'): + from pydantic import BaseModel # circular reference + + has_custom_v2_modify_js_func = ( + js_modify_function is not None + and BaseModel.__get_pydantic_json_schema__.__func__ # type: ignore + not in (js_modify_function, getattr(js_modify_function, '__func__', None)) + ) + + if not has_custom_v2_modify_js_func: + cls_name = getattr(tp, '__name__', None) + raise PydanticUserError( + f'The `__modify_schema__` method is not supported in Pydantic v2. ' + f'Use `__get_pydantic_json_schema__` instead{f" in class `{cls_name}`" if cls_name else ""}.', + code='custom-json-schema', + ) + + # handle GenericAlias' but ignore Annotated which "lies" about its origin (in this case it would be `int`) + if hasattr(tp, '__origin__') and not isinstance(tp, type(Annotated[int, 'placeholder'])): + return _extract_get_pydantic_json_schema(tp.__origin__, schema) + + if js_modify_function is None: + return None + + return js_modify_function + + +def get_json_schema_update_func( + json_schema_update: JsonSchemaValue, json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None +) -> GetJsonSchemaFunction: + def json_schema_update_func( + core_schema_or_field: CoreSchemaOrField, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + json_schema = {**handler(core_schema_or_field), **json_schema_update} + add_json_schema_extra(json_schema, json_schema_extra) + return json_schema + + return json_schema_update_func + + +def add_json_schema_extra( + json_schema: JsonSchemaValue, json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None +): + if isinstance(json_schema_extra, dict): + json_schema.update(to_jsonable_python(json_schema_extra)) + elif callable(json_schema_extra): + json_schema_extra(json_schema) + + +class _CommonField(TypedDict): + schema: core_schema.CoreSchema + validation_alias: str | list[str | int] | list[list[str | int]] | None + serialization_alias: str | None + serialization_exclude: bool | None + frozen: bool | None + metadata: dict[str, Any] + + +def _common_field( + schema: core_schema.CoreSchema, + *, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + frozen: bool | None = None, + metadata: Any = None, +) -> _CommonField: + return { + 'schema': schema, + 'validation_alias': validation_alias, + 'serialization_alias': serialization_alias, + 'serialization_exclude': serialization_exclude, + 'frozen': frozen, + 'metadata': metadata, + } + + +class _Definitions: + """Keeps track of references and definitions.""" + + def __init__(self) -> None: + self.seen: set[str] = set() + self.definitions: dict[str, core_schema.CoreSchema] = {} + + @contextmanager + def get_schema_or_ref(self, tp: Any) -> Iterator[tuple[str, None] | tuple[str, CoreSchema]]: + """Get a definition for `tp` if one exists. + + If a definition exists, a tuple of `(ref_string, CoreSchema)` is returned. + If no definition exists yet, a tuple of `(ref_string, None)` is returned. + + Note that the returned `CoreSchema` will always be a `DefinitionReferenceSchema`, + not the actual definition itself. + + This should be called for any type that can be identified by reference. + This includes any recursive types. + + At present the following types can be named/recursive: + + - BaseModel + - Dataclasses + - TypedDict + - TypeAliasType + """ + ref = get_type_ref(tp) + # return the reference if we're either (1) in a cycle or (2) it was already defined + if ref in self.seen or ref in self.definitions: + yield (ref, core_schema.definition_reference_schema(ref)) + else: + self.seen.add(ref) + try: + yield (ref, None) + finally: + self.seen.discard(ref) + + +def resolve_original_schema(schema: CoreSchema, definitions: dict[str, CoreSchema]) -> CoreSchema | None: + if schema['type'] == 'definition-ref': + return definitions.get(schema['schema_ref'], None) + elif schema['type'] == 'definitions': + return schema['schema'] + else: + return schema + + +class _FieldNameStack: + __slots__ = ('_stack',) + + def __init__(self) -> None: + self._stack: list[str] = [] + + @contextmanager + def push(self, field_name: str) -> Iterator[None]: + self._stack.append(field_name) + yield + self._stack.pop() + + def get(self) -> str | None: + if self._stack: + return self._stack[-1] + else: + return None + + +class _ModelTypeStack: + __slots__ = ('_stack',) + + def __init__(self) -> None: + self._stack: list[type] = [] + + @contextmanager + def push(self, type_obj: type) -> Iterator[None]: + self._stack.append(type_obj) + yield + self._stack.pop() + + def get(self) -> type | None: + if self._stack: + return self._stack[-1] + else: + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generics.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generics.py new file mode 100644 index 0000000..a24763a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_generics.py @@ -0,0 +1,517 @@ +from __future__ import annotations + +import sys +import types +import typing +from collections import ChainMap +from contextlib import contextmanager +from contextvars import ContextVar +from types import prepare_class +from typing import TYPE_CHECKING, Any, Iterator, List, Mapping, MutableMapping, Tuple, TypeVar +from weakref import WeakValueDictionary + +import typing_extensions + +from ._core_utils import get_type_ref +from ._forward_ref import PydanticRecursiveRef +from ._typing_extra import TypeVarType, typing_base +from ._utils import all_identical, is_model_class + +if sys.version_info >= (3, 10): + from typing import _UnionGenericAlias # type: ignore[attr-defined] + +if TYPE_CHECKING: + from ..main import BaseModel + +GenericTypesCacheKey = Tuple[Any, Any, Tuple[Any, ...]] + +# Note: We want to remove LimitedDict, but to do this, we'd need to improve the handling of generics caching. +# Right now, to handle recursive generics, we some types must remain cached for brief periods without references. +# By chaining the WeakValuesDict with a LimitedDict, we have a way to retain caching for all types with references, +# while also retaining a limited number of types even without references. This is generally enough to build +# specific recursive generic models without losing required items out of the cache. + +KT = TypeVar('KT') +VT = TypeVar('VT') +_LIMITED_DICT_SIZE = 100 +if TYPE_CHECKING: + + class LimitedDict(dict, MutableMapping[KT, VT]): + def __init__(self, size_limit: int = _LIMITED_DICT_SIZE): + ... + +else: + + class LimitedDict(dict): + """Limit the size/length of a dict used for caching to avoid unlimited increase in memory usage. + + Since the dict is ordered, and we always remove elements from the beginning, this is effectively a FIFO cache. + """ + + def __init__(self, size_limit: int = _LIMITED_DICT_SIZE): + self.size_limit = size_limit + super().__init__() + + def __setitem__(self, key: Any, value: Any, /) -> None: + super().__setitem__(key, value) + if len(self) > self.size_limit: + excess = len(self) - self.size_limit + self.size_limit // 10 + to_remove = list(self.keys())[:excess] + for k in to_remove: + del self[k] + + +# weak dictionaries allow the dynamically created parametrized versions of generic models to get collected +# once they are no longer referenced by the caller. +if sys.version_info >= (3, 9): # Typing for weak dictionaries available at 3.9 + GenericTypesCache = WeakValueDictionary[GenericTypesCacheKey, 'type[BaseModel]'] +else: + GenericTypesCache = WeakValueDictionary + +if TYPE_CHECKING: + + class DeepChainMap(ChainMap[KT, VT]): # type: ignore + ... + +else: + + class DeepChainMap(ChainMap): + """Variant of ChainMap that allows direct updates to inner scopes. + + Taken from https://docs.python.org/3/library/collections.html#collections.ChainMap, + with some light modifications for this use case. + """ + + def clear(self) -> None: + for mapping in self.maps: + mapping.clear() + + def __setitem__(self, key: KT, value: VT) -> None: + for mapping in self.maps: + mapping[key] = value + + def __delitem__(self, key: KT) -> None: + hit = False + for mapping in self.maps: + if key in mapping: + del mapping[key] + hit = True + if not hit: + raise KeyError(key) + + +# Despite the fact that LimitedDict _seems_ no longer necessary, I'm very nervous to actually remove it +# and discover later on that we need to re-add all this infrastructure... +# _GENERIC_TYPES_CACHE = DeepChainMap(GenericTypesCache(), LimitedDict()) + +_GENERIC_TYPES_CACHE = GenericTypesCache() + + +class PydanticGenericMetadata(typing_extensions.TypedDict): + origin: type[BaseModel] | None # analogous to typing._GenericAlias.__origin__ + args: tuple[Any, ...] # analogous to typing._GenericAlias.__args__ + parameters: tuple[type[Any], ...] # analogous to typing.Generic.__parameters__ + + +def create_generic_submodel( + model_name: str, origin: type[BaseModel], args: tuple[Any, ...], params: tuple[Any, ...] +) -> type[BaseModel]: + """Dynamically create a submodel of a provided (generic) BaseModel. + + This is used when producing concrete parametrizations of generic models. This function + only *creates* the new subclass; the schema/validators/serialization must be updated to + reflect a concrete parametrization elsewhere. + + Args: + model_name: The name of the newly created model. + origin: The base class for the new model to inherit from. + args: A tuple of generic metadata arguments. + params: A tuple of generic metadata parameters. + + Returns: + The created submodel. + """ + namespace: dict[str, Any] = {'__module__': origin.__module__} + bases = (origin,) + meta, ns, kwds = prepare_class(model_name, bases) + namespace.update(ns) + created_model = meta( + model_name, + bases, + namespace, + __pydantic_generic_metadata__={ + 'origin': origin, + 'args': args, + 'parameters': params, + }, + __pydantic_reset_parent_namespace__=False, + **kwds, + ) + + model_module, called_globally = _get_caller_frame_info(depth=3) + if called_globally: # create global reference and therefore allow pickling + object_by_reference = None + reference_name = model_name + reference_module_globals = sys.modules[created_model.__module__].__dict__ + while object_by_reference is not created_model: + object_by_reference = reference_module_globals.setdefault(reference_name, created_model) + reference_name += '_' + + return created_model + + +def _get_caller_frame_info(depth: int = 2) -> tuple[str | None, bool]: + """Used inside a function to check whether it was called globally. + + Args: + depth: The depth to get the frame. + + Returns: + A tuple contains `module_name` and `called_globally`. + + Raises: + RuntimeError: If the function is not called inside a function. + """ + try: + previous_caller_frame = sys._getframe(depth) + except ValueError as e: + raise RuntimeError('This function must be used inside another function') from e + except AttributeError: # sys module does not have _getframe function, so there's nothing we can do about it + return None, False + frame_globals = previous_caller_frame.f_globals + return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals + + +DictValues: type[Any] = {}.values().__class__ + + +def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]: + """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found. + + This is inspired as an alternative to directly accessing the `__parameters__` attribute of a GenericAlias, + since __parameters__ of (nested) generic BaseModel subclasses won't show up in that list. + """ + if isinstance(v, TypeVar): + yield v + elif is_model_class(v): + yield from v.__pydantic_generic_metadata__['parameters'] + elif isinstance(v, (DictValues, list)): + for var in v: + yield from iter_contained_typevars(var) + else: + args = get_args(v) + for arg in args: + yield from iter_contained_typevars(arg) + + +def get_args(v: Any) -> Any: + pydantic_generic_metadata: PydanticGenericMetadata | None = getattr(v, '__pydantic_generic_metadata__', None) + if pydantic_generic_metadata: + return pydantic_generic_metadata.get('args') + return typing_extensions.get_args(v) + + +def get_origin(v: Any) -> Any: + pydantic_generic_metadata: PydanticGenericMetadata | None = getattr(v, '__pydantic_generic_metadata__', None) + if pydantic_generic_metadata: + return pydantic_generic_metadata.get('origin') + return typing_extensions.get_origin(v) + + +def get_standard_typevars_map(cls: type[Any]) -> dict[TypeVarType, Any] | None: + """Package a generic type's typevars and parametrization (if present) into a dictionary compatible with the + `replace_types` function. Specifically, this works with standard typing generics and typing._GenericAlias. + """ + origin = get_origin(cls) + if origin is None: + return None + if not hasattr(origin, '__parameters__'): + return None + + # In this case, we know that cls is a _GenericAlias, and origin is the generic type + # So it is safe to access cls.__args__ and origin.__parameters__ + args: tuple[Any, ...] = cls.__args__ # type: ignore + parameters: tuple[TypeVarType, ...] = origin.__parameters__ + return dict(zip(parameters, args)) + + +def get_model_typevars_map(cls: type[BaseModel]) -> dict[TypeVarType, Any] | None: + """Package a generic BaseModel's typevars and concrete parametrization (if present) into a dictionary compatible + with the `replace_types` function. + + Since BaseModel.__class_getitem__ does not produce a typing._GenericAlias, and the BaseModel generic info is + stored in the __pydantic_generic_metadata__ attribute, we need special handling here. + """ + # TODO: This could be unified with `get_standard_typevars_map` if we stored the generic metadata + # in the __origin__, __args__, and __parameters__ attributes of the model. + generic_metadata = cls.__pydantic_generic_metadata__ + origin = generic_metadata['origin'] + args = generic_metadata['args'] + return dict(zip(iter_contained_typevars(origin), args)) + + +def replace_types(type_: Any, type_map: Mapping[Any, Any] | None) -> Any: + """Return type with all occurrences of `type_map` keys recursively replaced with their values. + + Args: + type_: The class or generic alias. + type_map: Mapping from `TypeVar` instance to concrete types. + + Returns: + A new type representing the basic structure of `type_` with all + `typevar_map` keys recursively replaced. + + Example: + ```py + from typing import List, Tuple, Union + + from pydantic._internal._generics import replace_types + + replace_types(Tuple[str, Union[List[str], float]], {str: int}) + #> Tuple[int, Union[List[int], float]] + ``` + """ + if not type_map: + return type_ + + type_args = get_args(type_) + origin_type = get_origin(type_) + + if origin_type is typing_extensions.Annotated: + annotated_type, *annotations = type_args + annotated = replace_types(annotated_type, type_map) + for annotation in annotations: + annotated = typing_extensions.Annotated[annotated, annotation] + return annotated + + # Having type args is a good indicator that this is a typing module + # class instantiation or a generic alias of some sort. + if type_args: + resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args) + if all_identical(type_args, resolved_type_args): + # If all arguments are the same, there is no need to modify the + # type or create a new object at all + return type_ + if ( + origin_type is not None + and isinstance(type_, typing_base) + and not isinstance(origin_type, typing_base) + and getattr(type_, '_name', None) is not None + ): + # In python < 3.9 generic aliases don't exist so any of these like `list`, + # `type` or `collections.abc.Callable` need to be translated. + # See: https://www.python.org/dev/peps/pep-0585 + origin_type = getattr(typing, type_._name) + assert origin_type is not None + # PEP-604 syntax (Ex.: list | str) is represented with a types.UnionType object that does not have __getitem__. + # We also cannot use isinstance() since we have to compare types. + if sys.version_info >= (3, 10) and origin_type is types.UnionType: + return _UnionGenericAlias(origin_type, resolved_type_args) + # NotRequired[T] and Required[T] don't support tuple type resolved_type_args, hence the condition below + return origin_type[resolved_type_args[0] if len(resolved_type_args) == 1 else resolved_type_args] + + # We handle pydantic generic models separately as they don't have the same + # semantics as "typing" classes or generic aliases + + if not origin_type and is_model_class(type_): + parameters = type_.__pydantic_generic_metadata__['parameters'] + if not parameters: + return type_ + resolved_type_args = tuple(replace_types(t, type_map) for t in parameters) + if all_identical(parameters, resolved_type_args): + return type_ + return type_[resolved_type_args] + + # Handle special case for typehints that can have lists as arguments. + # `typing.Callable[[int, str], int]` is an example for this. + if isinstance(type_, (List, list)): + resolved_list = list(replace_types(element, type_map) for element in type_) + if all_identical(type_, resolved_list): + return type_ + return resolved_list + + # If all else fails, we try to resolve the type directly and otherwise just + # return the input with no modifications. + return type_map.get(type_, type_) + + +def has_instance_in_type(type_: Any, isinstance_target: Any) -> bool: + """Checks if the type, or any of its arbitrary nested args, satisfy + `isinstance(, isinstance_target)`. + """ + if isinstance(type_, isinstance_target): + return True + + type_args = get_args(type_) + origin_type = get_origin(type_) + + if origin_type is typing_extensions.Annotated: + annotated_type, *annotations = type_args + return has_instance_in_type(annotated_type, isinstance_target) + + # Having type args is a good indicator that this is a typing module + # class instantiation or a generic alias of some sort. + if any(has_instance_in_type(a, isinstance_target) for a in type_args): + return True + + # Handle special case for typehints that can have lists as arguments. + # `typing.Callable[[int, str], int]` is an example for this. + if isinstance(type_, (List, list)) and not isinstance(type_, typing_extensions.ParamSpec): + if any(has_instance_in_type(element, isinstance_target) for element in type_): + return True + + return False + + +def check_parameters_count(cls: type[BaseModel], parameters: tuple[Any, ...]) -> None: + """Check the generic model parameters count is equal. + + Args: + cls: The generic model. + parameters: A tuple of passed parameters to the generic model. + + Raises: + TypeError: If the passed parameters count is not equal to generic model parameters count. + """ + actual = len(parameters) + expected = len(cls.__pydantic_generic_metadata__['parameters']) + if actual != expected: + description = 'many' if actual > expected else 'few' + raise TypeError(f'Too {description} parameters for {cls}; actual {actual}, expected {expected}') + + +_generic_recursion_cache: ContextVar[set[str] | None] = ContextVar('_generic_recursion_cache', default=None) + + +@contextmanager +def generic_recursion_self_type( + origin: type[BaseModel], args: tuple[Any, ...] +) -> Iterator[PydanticRecursiveRef | None]: + """This contextmanager should be placed around the recursive calls used to build a generic type, + and accept as arguments the generic origin type and the type arguments being passed to it. + + If the same origin and arguments are observed twice, it implies that a self-reference placeholder + can be used while building the core schema, and will produce a schema_ref that will be valid in the + final parent schema. + """ + previously_seen_type_refs = _generic_recursion_cache.get() + if previously_seen_type_refs is None: + previously_seen_type_refs = set() + token = _generic_recursion_cache.set(previously_seen_type_refs) + else: + token = None + + try: + type_ref = get_type_ref(origin, args_override=args) + if type_ref in previously_seen_type_refs: + self_type = PydanticRecursiveRef(type_ref=type_ref) + yield self_type + else: + previously_seen_type_refs.add(type_ref) + yield None + finally: + if token: + _generic_recursion_cache.reset(token) + + +def recursively_defined_type_refs() -> set[str]: + visited = _generic_recursion_cache.get() + if not visited: + return set() # not in a generic recursion, so there are no types + + return visited.copy() # don't allow modifications + + +def get_cached_generic_type_early(parent: type[BaseModel], typevar_values: Any) -> type[BaseModel] | None: + """The use of a two-stage cache lookup approach was necessary to have the highest performance possible for + repeated calls to `__class_getitem__` on generic types (which may happen in tighter loops during runtime), + while still ensuring that certain alternative parametrizations ultimately resolve to the same type. + + As a concrete example, this approach was necessary to make Model[List[T]][int] equal to Model[List[int]]. + The approach could be modified to not use two different cache keys at different points, but the + _early_cache_key is optimized to be as quick to compute as possible (for repeated-access speed), and the + _late_cache_key is optimized to be as "correct" as possible, so that two types that will ultimately be the + same after resolving the type arguments will always produce cache hits. + + If we wanted to move to only using a single cache key per type, we would either need to always use the + slower/more computationally intensive logic associated with _late_cache_key, or would need to accept + that Model[List[T]][int] is a different type than Model[List[T]][int]. Because we rely on subclass relationships + during validation, I think it is worthwhile to ensure that types that are functionally equivalent are actually + equal. + """ + return _GENERIC_TYPES_CACHE.get(_early_cache_key(parent, typevar_values)) + + +def get_cached_generic_type_late( + parent: type[BaseModel], typevar_values: Any, origin: type[BaseModel], args: tuple[Any, ...] +) -> type[BaseModel] | None: + """See the docstring of `get_cached_generic_type_early` for more information about the two-stage cache lookup.""" + cached = _GENERIC_TYPES_CACHE.get(_late_cache_key(origin, args, typevar_values)) + if cached is not None: + set_cached_generic_type(parent, typevar_values, cached, origin, args) + return cached + + +def set_cached_generic_type( + parent: type[BaseModel], + typevar_values: tuple[Any, ...], + type_: type[BaseModel], + origin: type[BaseModel] | None = None, + args: tuple[Any, ...] | None = None, +) -> None: + """See the docstring of `get_cached_generic_type_early` for more information about why items are cached with + two different keys. + """ + _GENERIC_TYPES_CACHE[_early_cache_key(parent, typevar_values)] = type_ + if len(typevar_values) == 1: + _GENERIC_TYPES_CACHE[_early_cache_key(parent, typevar_values[0])] = type_ + if origin and args: + _GENERIC_TYPES_CACHE[_late_cache_key(origin, args, typevar_values)] = type_ + + +def _union_orderings_key(typevar_values: Any) -> Any: + """This is intended to help differentiate between Union types with the same arguments in different order. + + Thanks to caching internal to the `typing` module, it is not possible to distinguish between + List[Union[int, float]] and List[Union[float, int]] (and similarly for other "parent" origins besides List) + because `typing` considers Union[int, float] to be equal to Union[float, int]. + + However, you _can_ distinguish between (top-level) Union[int, float] vs. Union[float, int]. + Because we parse items as the first Union type that is successful, we get slightly more consistent behavior + if we make an effort to distinguish the ordering of items in a union. It would be best if we could _always_ + get the exact-correct order of items in the union, but that would require a change to the `typing` module itself. + (See https://github.com/python/cpython/issues/86483 for reference.) + """ + if isinstance(typevar_values, tuple): + args_data = [] + for value in typevar_values: + args_data.append(_union_orderings_key(value)) + return tuple(args_data) + elif typing_extensions.get_origin(typevar_values) is typing.Union: + return get_args(typevar_values) + else: + return () + + +def _early_cache_key(cls: type[BaseModel], typevar_values: Any) -> GenericTypesCacheKey: + """This is intended for minimal computational overhead during lookups of cached types. + + Note that this is overly simplistic, and it's possible that two different cls/typevar_values + inputs would ultimately result in the same type being created in BaseModel.__class_getitem__. + To handle this, we have a fallback _late_cache_key that is checked later if the _early_cache_key + lookup fails, and should result in a cache hit _precisely_ when the inputs to __class_getitem__ + would result in the same type. + """ + return cls, typevar_values, _union_orderings_key(typevar_values) + + +def _late_cache_key(origin: type[BaseModel], args: tuple[Any, ...], typevar_values: Any) -> GenericTypesCacheKey: + """This is intended for use later in the process of creating a new type, when we have more information + about the exact args that will be passed. If it turns out that a different set of inputs to + __class_getitem__ resulted in the same inputs to the generic type creation process, we can still + return the cached type, and update the cache with the _early_cache_key as well. + """ + # The _union_orderings_key is placed at the start here to ensure there cannot be a collision with an + # _early_cache_key, as that function will always produce a BaseModel subclass as the first item in the key, + # whereas this function will always produce a tuple as the first item in the key. + return _union_orderings_key(typevar_values), origin, args diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_git.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_git.py new file mode 100644 index 0000000..9de7aaf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_git.py @@ -0,0 +1,26 @@ +"""Git utilities, adopted from mypy's git utilities (https://github.com/python/mypy/blob/master/mypy/git.py).""" +from __future__ import annotations + +import os +import subprocess + + +def is_git_repo(dir: str) -> bool: + """Is the given directory version-controlled with git?""" + return os.path.exists(os.path.join(dir, '.git')) + + +def have_git() -> bool: + """Can we run the git executable?""" + try: + subprocess.check_output(['git', '--help']) + return True + except subprocess.CalledProcessError: + return False + except OSError: + return False + + +def git_revision(dir: str) -> str: + """Get the SHA-1 of the HEAD of a git repository.""" + return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=dir).decode('utf-8').strip() diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_internal_dataclass.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_internal_dataclass.py new file mode 100644 index 0000000..317a3d9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_internal_dataclass.py @@ -0,0 +1,10 @@ +import sys +from typing import Any, Dict + +dataclass_kwargs: Dict[str, Any] + +# `slots` is available on Python >= 3.10 +if sys.version_info >= (3, 10): + slots_true = {'slots': True} +else: + slots_true = {} diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_known_annotated_metadata.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_known_annotated_metadata.py new file mode 100644 index 0000000..83d85b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_known_annotated_metadata.py @@ -0,0 +1,434 @@ +from __future__ import annotations + +from collections import defaultdict +from copy import copy +from functools import partial +from typing import TYPE_CHECKING, Any, Callable, Iterable + +from pydantic_core import CoreSchema, PydanticCustomError, to_jsonable_python +from pydantic_core import core_schema as cs + +from ._fields import PydanticMetadata + +if TYPE_CHECKING: + from ..annotated_handlers import GetJsonSchemaHandler + + +STRICT = {'strict'} +SEQUENCE_CONSTRAINTS = {'min_length', 'max_length'} +INEQUALITY = {'le', 'ge', 'lt', 'gt'} +NUMERIC_CONSTRAINTS = {'multiple_of', 'allow_inf_nan', *INEQUALITY} + +STR_CONSTRAINTS = { + *SEQUENCE_CONSTRAINTS, + *STRICT, + 'strip_whitespace', + 'to_lower', + 'to_upper', + 'pattern', + 'coerce_numbers_to_str', +} +BYTES_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} + +LIST_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} +TUPLE_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} +SET_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} +DICT_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} +GENERATOR_CONSTRAINTS = {*SEQUENCE_CONSTRAINTS, *STRICT} + +FLOAT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT} +INT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT} +BOOL_CONSTRAINTS = STRICT +UUID_CONSTRAINTS = STRICT + +DATE_TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT} +TIMEDELTA_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT} +TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT} +LAX_OR_STRICT_CONSTRAINTS = STRICT +ENUM_CONSTRAINTS = STRICT + +UNION_CONSTRAINTS = {'union_mode'} +URL_CONSTRAINTS = { + 'max_length', + 'allowed_schemes', + 'host_required', + 'default_host', + 'default_port', + 'default_path', +} + +TEXT_SCHEMA_TYPES = ('str', 'bytes', 'url', 'multi-host-url') +SEQUENCE_SCHEMA_TYPES = ('list', 'tuple', 'set', 'frozenset', 'generator', *TEXT_SCHEMA_TYPES) +NUMERIC_SCHEMA_TYPES = ('float', 'int', 'date', 'time', 'timedelta', 'datetime') + +CONSTRAINTS_TO_ALLOWED_SCHEMAS: dict[str, set[str]] = defaultdict(set) +for constraint in STR_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(TEXT_SCHEMA_TYPES) +for constraint in BYTES_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('bytes',)) +for constraint in LIST_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('list',)) +for constraint in TUPLE_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('tuple',)) +for constraint in SET_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('set', 'frozenset')) +for constraint in DICT_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('dict',)) +for constraint in GENERATOR_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('generator',)) +for constraint in FLOAT_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('float',)) +for constraint in INT_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('int',)) +for constraint in DATE_TIME_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('date', 'time', 'datetime')) +for constraint in TIMEDELTA_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('timedelta',)) +for constraint in TIME_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('time',)) +for schema_type in (*TEXT_SCHEMA_TYPES, *SEQUENCE_SCHEMA_TYPES, *NUMERIC_SCHEMA_TYPES, 'typed-dict', 'model'): + CONSTRAINTS_TO_ALLOWED_SCHEMAS['strict'].add(schema_type) +for constraint in UNION_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('union',)) +for constraint in URL_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('url', 'multi-host-url')) +for constraint in BOOL_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('bool',)) +for constraint in UUID_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('uuid',)) +for constraint in LAX_OR_STRICT_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('lax-or-strict',)) +for constraint in ENUM_CONSTRAINTS: + CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint].update(('enum',)) + + +def add_js_update_schema(s: cs.CoreSchema, f: Callable[[], dict[str, Any]]) -> None: + def update_js_schema(s: cs.CoreSchema, handler: GetJsonSchemaHandler) -> dict[str, Any]: + js_schema = handler(s) + js_schema.update(f()) + return js_schema + + if 'metadata' in s: + metadata = s['metadata'] + if 'pydantic_js_functions' in s: + metadata['pydantic_js_functions'].append(update_js_schema) + else: + metadata['pydantic_js_functions'] = [update_js_schema] + else: + s['metadata'] = {'pydantic_js_functions': [update_js_schema]} + + +def as_jsonable_value(v: Any) -> Any: + if type(v) not in (int, str, float, bytes, bool, type(None)): + return to_jsonable_python(v) + return v + + +def expand_grouped_metadata(annotations: Iterable[Any]) -> Iterable[Any]: + """Expand the annotations. + + Args: + annotations: An iterable of annotations. + + Returns: + An iterable of expanded annotations. + + Example: + ```py + from annotated_types import Ge, Len + + from pydantic._internal._known_annotated_metadata import expand_grouped_metadata + + print(list(expand_grouped_metadata([Ge(4), Len(5)]))) + #> [Ge(ge=4), MinLen(min_length=5)] + ``` + """ + import annotated_types as at + + from pydantic.fields import FieldInfo # circular import + + for annotation in annotations: + if isinstance(annotation, at.GroupedMetadata): + yield from annotation + elif isinstance(annotation, FieldInfo): + yield from annotation.metadata + # this is a bit problematic in that it results in duplicate metadata + # all of our "consumers" can handle it, but it is not ideal + # we probably should split up FieldInfo into: + # - annotated types metadata + # - individual metadata known only to Pydantic + annotation = copy(annotation) + annotation.metadata = [] + yield annotation + else: + yield annotation + + +def apply_known_metadata(annotation: Any, schema: CoreSchema) -> CoreSchema | None: # noqa: C901 + """Apply `annotation` to `schema` if it is an annotation we know about (Gt, Le, etc.). + Otherwise return `None`. + + This does not handle all known annotations. If / when it does, it can always + return a CoreSchema and return the unmodified schema if the annotation should be ignored. + + Assumes that GroupedMetadata has already been expanded via `expand_grouped_metadata`. + + Args: + annotation: The annotation. + schema: The schema. + + Returns: + An updated schema with annotation if it is an annotation we know about, `None` otherwise. + + Raises: + PydanticCustomError: If `Predicate` fails. + """ + import annotated_types as at + + from . import _validators + + schema = schema.copy() + schema_update, other_metadata = collect_known_metadata([annotation]) + schema_type = schema['type'] + for constraint, value in schema_update.items(): + if constraint not in CONSTRAINTS_TO_ALLOWED_SCHEMAS: + raise ValueError(f'Unknown constraint {constraint}') + allowed_schemas = CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint] + + # if it becomes necessary to handle more than one constraint + # in this recursive case with function-after or function-wrap, we should refactor + if schema_type in {'function-before', 'function-wrap', 'function-after'} and constraint == 'strict': + schema['schema'] = apply_known_metadata(annotation, schema['schema']) # type: ignore # schema is function-after schema + return schema + + if schema_type in allowed_schemas: + if constraint == 'union_mode' and schema_type == 'union': + schema['mode'] = value # type: ignore # schema is UnionSchema + else: + schema[constraint] = value + continue + + if constraint == 'allow_inf_nan' and value is False: + return cs.no_info_after_validator_function( + _validators.forbid_inf_nan_check, + schema, + ) + elif constraint == 'pattern': + # insert a str schema to make sure the regex engine matches + return cs.chain_schema( + [ + schema, + cs.str_schema(pattern=value), + ] + ) + elif constraint == 'gt': + s = cs.no_info_after_validator_function( + partial(_validators.greater_than_validator, gt=value), + schema, + ) + add_js_update_schema(s, lambda: {'gt': as_jsonable_value(value)}) + return s + elif constraint == 'ge': + return cs.no_info_after_validator_function( + partial(_validators.greater_than_or_equal_validator, ge=value), + schema, + ) + elif constraint == 'lt': + return cs.no_info_after_validator_function( + partial(_validators.less_than_validator, lt=value), + schema, + ) + elif constraint == 'le': + return cs.no_info_after_validator_function( + partial(_validators.less_than_or_equal_validator, le=value), + schema, + ) + elif constraint == 'multiple_of': + return cs.no_info_after_validator_function( + partial(_validators.multiple_of_validator, multiple_of=value), + schema, + ) + elif constraint == 'min_length': + s = cs.no_info_after_validator_function( + partial(_validators.min_length_validator, min_length=value), + schema, + ) + add_js_update_schema(s, lambda: {'minLength': (as_jsonable_value(value))}) + return s + elif constraint == 'max_length': + s = cs.no_info_after_validator_function( + partial(_validators.max_length_validator, max_length=value), + schema, + ) + add_js_update_schema(s, lambda: {'maxLength': (as_jsonable_value(value))}) + return s + elif constraint == 'strip_whitespace': + return cs.chain_schema( + [ + schema, + cs.str_schema(strip_whitespace=True), + ] + ) + elif constraint == 'to_lower': + return cs.chain_schema( + [ + schema, + cs.str_schema(to_lower=True), + ] + ) + elif constraint == 'to_upper': + return cs.chain_schema( + [ + schema, + cs.str_schema(to_upper=True), + ] + ) + elif constraint == 'min_length': + return cs.no_info_after_validator_function( + partial(_validators.min_length_validator, min_length=annotation.min_length), + schema, + ) + elif constraint == 'max_length': + return cs.no_info_after_validator_function( + partial(_validators.max_length_validator, max_length=annotation.max_length), + schema, + ) + elif constraint == 'coerce_numbers_to_str': + return cs.chain_schema( + [ + schema, + cs.str_schema(coerce_numbers_to_str=True), # type: ignore + ] + ) + else: + raise RuntimeError(f'Unable to apply constraint {constraint} to schema {schema_type}') + + for annotation in other_metadata: + if isinstance(annotation, at.Gt): + return cs.no_info_after_validator_function( + partial(_validators.greater_than_validator, gt=annotation.gt), + schema, + ) + elif isinstance(annotation, at.Ge): + return cs.no_info_after_validator_function( + partial(_validators.greater_than_or_equal_validator, ge=annotation.ge), + schema, + ) + elif isinstance(annotation, at.Lt): + return cs.no_info_after_validator_function( + partial(_validators.less_than_validator, lt=annotation.lt), + schema, + ) + elif isinstance(annotation, at.Le): + return cs.no_info_after_validator_function( + partial(_validators.less_than_or_equal_validator, le=annotation.le), + schema, + ) + elif isinstance(annotation, at.MultipleOf): + return cs.no_info_after_validator_function( + partial(_validators.multiple_of_validator, multiple_of=annotation.multiple_of), + schema, + ) + elif isinstance(annotation, at.MinLen): + return cs.no_info_after_validator_function( + partial(_validators.min_length_validator, min_length=annotation.min_length), + schema, + ) + elif isinstance(annotation, at.MaxLen): + return cs.no_info_after_validator_function( + partial(_validators.max_length_validator, max_length=annotation.max_length), + schema, + ) + elif isinstance(annotation, at.Predicate): + predicate_name = f'{annotation.func.__qualname__} ' if hasattr(annotation.func, '__qualname__') else '' + + def val_func(v: Any) -> Any: + # annotation.func may also raise an exception, let it pass through + if not annotation.func(v): + raise PydanticCustomError( + 'predicate_failed', + f'Predicate {predicate_name}failed', # type: ignore + ) + return v + + return cs.no_info_after_validator_function(val_func, schema) + # ignore any other unknown metadata + return None + + return schema + + +def collect_known_metadata(annotations: Iterable[Any]) -> tuple[dict[str, Any], list[Any]]: + """Split `annotations` into known metadata and unknown annotations. + + Args: + annotations: An iterable of annotations. + + Returns: + A tuple contains a dict of known metadata and a list of unknown annotations. + + Example: + ```py + from annotated_types import Gt, Len + + from pydantic._internal._known_annotated_metadata import collect_known_metadata + + print(collect_known_metadata([Gt(1), Len(42), ...])) + #> ({'gt': 1, 'min_length': 42}, [Ellipsis]) + ``` + """ + import annotated_types as at + + annotations = expand_grouped_metadata(annotations) + + res: dict[str, Any] = {} + remaining: list[Any] = [] + for annotation in annotations: + # isinstance(annotation, PydanticMetadata) also covers ._fields:_PydanticGeneralMetadata + if isinstance(annotation, PydanticMetadata): + res.update(annotation.__dict__) + # we don't use dataclasses.asdict because that recursively calls asdict on the field values + elif isinstance(annotation, at.MinLen): + res.update({'min_length': annotation.min_length}) + elif isinstance(annotation, at.MaxLen): + res.update({'max_length': annotation.max_length}) + elif isinstance(annotation, at.Gt): + res.update({'gt': annotation.gt}) + elif isinstance(annotation, at.Ge): + res.update({'ge': annotation.ge}) + elif isinstance(annotation, at.Lt): + res.update({'lt': annotation.lt}) + elif isinstance(annotation, at.Le): + res.update({'le': annotation.le}) + elif isinstance(annotation, at.MultipleOf): + res.update({'multiple_of': annotation.multiple_of}) + elif isinstance(annotation, type) and issubclass(annotation, PydanticMetadata): + # also support PydanticMetadata classes being used without initialisation, + # e.g. `Annotated[int, Strict]` as well as `Annotated[int, Strict()]` + res.update({k: v for k, v in vars(annotation).items() if not k.startswith('_')}) + else: + remaining.append(annotation) + # Nones can sneak in but pydantic-core will reject them + # it'd be nice to clean things up so we don't put in None (we probably don't _need_ to, it was just easier) + # but this is simple enough to kick that can down the road + res = {k: v for k, v in res.items() if v is not None} + return res, remaining + + +def check_metadata(metadata: dict[str, Any], allowed: Iterable[str], source_type: Any) -> None: + """A small utility function to validate that the given metadata can be applied to the target. + More than saving lines of code, this gives us a consistent error message for all of our internal implementations. + + Args: + metadata: A dict of metadata. + allowed: An iterable of allowed metadata. + source_type: The source type. + + Raises: + TypeError: If there is metadatas that can't be applied on source type. + """ + unknown = metadata.keys() - set(allowed) + if unknown: + raise TypeError( + f'The following constraints cannot be applied to {source_type!r}: {", ".join([f"{k!r}" for k in unknown])}' + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_mock_val_ser.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_mock_val_ser.py new file mode 100644 index 0000000..b303fed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_mock_val_ser.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Callable, Generic, TypeVar + +from pydantic_core import SchemaSerializer, SchemaValidator +from typing_extensions import Literal + +from ..errors import PydanticErrorCodes, PydanticUserError + +if TYPE_CHECKING: + from ..dataclasses import PydanticDataclass + from ..main import BaseModel + + +ValSer = TypeVar('ValSer', SchemaValidator, SchemaSerializer) + + +class MockValSer(Generic[ValSer]): + """Mocker for `pydantic_core.SchemaValidator` or `pydantic_core.SchemaSerializer` which optionally attempts to + rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails. + """ + + __slots__ = '_error_message', '_code', '_val_or_ser', '_attempt_rebuild' + + def __init__( + self, + error_message: str, + *, + code: PydanticErrorCodes, + val_or_ser: Literal['validator', 'serializer'], + attempt_rebuild: Callable[[], ValSer | None] | None = None, + ) -> None: + self._error_message = error_message + self._val_or_ser = SchemaValidator if val_or_ser == 'validator' else SchemaSerializer + self._code: PydanticErrorCodes = code + self._attempt_rebuild = attempt_rebuild + + def __getattr__(self, item: str) -> None: + __tracebackhide__ = True + if self._attempt_rebuild: + val_ser = self._attempt_rebuild() + if val_ser is not None: + return getattr(val_ser, item) + + # raise an AttributeError if `item` doesn't exist + getattr(self._val_or_ser, item) + raise PydanticUserError(self._error_message, code=self._code) + + def rebuild(self) -> ValSer | None: + if self._attempt_rebuild: + val_ser = self._attempt_rebuild() + if val_ser is not None: + return val_ser + else: + raise PydanticUserError(self._error_message, code=self._code) + return None + + +def set_model_mocks(cls: type[BaseModel], cls_name: str, undefined_name: str = 'all referenced types') -> None: + """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a model. + + Args: + cls: The model class to set the mocks on + cls_name: Name of the model class, used in error messages + undefined_name: Name of the undefined thing, used in error messages + """ + undefined_type_error_message = ( + f'`{cls_name}` is not fully defined; you should define {undefined_name},' + f' then call `{cls_name}.model_rebuild()`.' + ) + + def attempt_rebuild_validator() -> SchemaValidator | None: + if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False: + return cls.__pydantic_validator__ + else: + return None + + cls.__pydantic_validator__ = MockValSer( # type: ignore[assignment] + undefined_type_error_message, + code='class-not-fully-defined', + val_or_ser='validator', + attempt_rebuild=attempt_rebuild_validator, + ) + + def attempt_rebuild_serializer() -> SchemaSerializer | None: + if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False: + return cls.__pydantic_serializer__ + else: + return None + + cls.__pydantic_serializer__ = MockValSer( # type: ignore[assignment] + undefined_type_error_message, + code='class-not-fully-defined', + val_or_ser='serializer', + attempt_rebuild=attempt_rebuild_serializer, + ) + + +def set_dataclass_mocks( + cls: type[PydanticDataclass], cls_name: str, undefined_name: str = 'all referenced types' +) -> None: + """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a dataclass. + + Args: + cls: The model class to set the mocks on + cls_name: Name of the model class, used in error messages + undefined_name: Name of the undefined thing, used in error messages + """ + from ..dataclasses import rebuild_dataclass + + undefined_type_error_message = ( + f'`{cls_name}` is not fully defined; you should define {undefined_name},' + f' then call `pydantic.dataclasses.rebuild_dataclass({cls_name})`.' + ) + + def attempt_rebuild_validator() -> SchemaValidator | None: + if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False: + return cls.__pydantic_validator__ + else: + return None + + cls.__pydantic_validator__ = MockValSer( # type: ignore[assignment] + undefined_type_error_message, + code='class-not-fully-defined', + val_or_ser='validator', + attempt_rebuild=attempt_rebuild_validator, + ) + + def attempt_rebuild_serializer() -> SchemaSerializer | None: + if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False: + return cls.__pydantic_serializer__ + else: + return None + + cls.__pydantic_serializer__ = MockValSer( # type: ignore[assignment] + undefined_type_error_message, + code='class-not-fully-defined', + val_or_ser='validator', + attempt_rebuild=attempt_rebuild_serializer, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_model_construction.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_model_construction.py new file mode 100644 index 0000000..7476629 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_model_construction.py @@ -0,0 +1,713 @@ +"""Private logic for creating models.""" +from __future__ import annotations as _annotations + +import builtins +import operator +import typing +import warnings +import weakref +from abc import ABCMeta +from functools import partial +from types import FunctionType +from typing import Any, Callable, Generic, NoReturn + +import typing_extensions +from pydantic_core import PydanticUndefined, SchemaSerializer +from typing_extensions import dataclass_transform, deprecated + +from ..errors import PydanticUndefinedAnnotation, PydanticUserError +from ..plugin._schema_validator import create_schema_validator +from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20 +from ._config import ConfigWrapper +from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function +from ._fields import collect_model_fields, is_valid_field_name, is_valid_privateattr_name +from ._generate_schema import GenerateSchema +from ._generics import PydanticGenericMetadata, get_model_typevars_map +from ._mock_val_ser import MockValSer, set_model_mocks +from ._schema_generation_shared import CallbackGetCoreSchemaHandler +from ._signature import generate_pydantic_signature +from ._typing_extra import get_cls_types_namespace, is_annotated, is_classvar, parent_frame_namespace +from ._utils import ClassAttribute, SafeGetItemProxy +from ._validate_call import ValidateCallWrapper + +if typing.TYPE_CHECKING: + from ..fields import Field as PydanticModelField + from ..fields import FieldInfo, ModelPrivateAttr + from ..main import BaseModel +else: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + PydanticModelField = object() + +object_setattr = object.__setattr__ + + +class _ModelNamespaceDict(dict): + """A dictionary subclass that intercepts attribute setting on model classes and + warns about overriding of decorators. + """ + + def __setitem__(self, k: str, v: object) -> None: + existing: Any = self.get(k, None) + if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy): + warnings.warn(f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator') + + return super().__setitem__(k, v) + + +@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField,)) +class ModelMetaclass(ABCMeta): + def __new__( + mcs, + cls_name: str, + bases: tuple[type[Any], ...], + namespace: dict[str, Any], + __pydantic_generic_metadata__: PydanticGenericMetadata | None = None, + __pydantic_reset_parent_namespace__: bool = True, + _create_model_module: str | None = None, + **kwargs: Any, + ) -> type: + """Metaclass for creating Pydantic models. + + Args: + cls_name: The name of the class to be created. + bases: The base classes of the class to be created. + namespace: The attribute dictionary of the class to be created. + __pydantic_generic_metadata__: Metadata for generic models. + __pydantic_reset_parent_namespace__: Reset parent namespace. + _create_model_module: The module of the class to be created, if created by `create_model`. + **kwargs: Catch-all for any other keyword arguments. + + Returns: + The new class created by the metaclass. + """ + # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact + # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__` + # call we're in the middle of is for the `BaseModel` class. + if bases: + base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases) + + config_wrapper = ConfigWrapper.for_model(bases, namespace, kwargs) + namespace['model_config'] = config_wrapper.config_dict + private_attributes = inspect_namespace( + namespace, config_wrapper.ignored_types, class_vars, base_field_names + ) + if private_attributes or base_private_attributes: + original_model_post_init = get_model_post_init(namespace, bases) + if original_model_post_init is not None: + # if there are private_attributes and a model_post_init function, we handle both + + def wrapped_model_post_init(self: BaseModel, __context: Any) -> None: + """We need to both initialize private attributes and call the user-defined model_post_init + method. + """ + init_private_attributes(self, __context) + original_model_post_init(self, __context) + + namespace['model_post_init'] = wrapped_model_post_init + else: + namespace['model_post_init'] = init_private_attributes + + namespace['__class_vars__'] = class_vars + namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes} + + cls: type[BaseModel] = super().__new__(mcs, cls_name, bases, namespace, **kwargs) # type: ignore + + from ..main import BaseModel + + mro = cls.__mro__ + if Generic in mro and mro.index(Generic) < mro.index(BaseModel): + warnings.warn( + GenericBeforeBaseModelWarning( + 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) ' + 'for pydantic generics to work properly.' + ), + stacklevel=2, + ) + + cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False) + cls.__pydantic_post_init__ = None if cls.model_post_init is BaseModel.model_post_init else 'model_post_init' + + cls.__pydantic_decorators__ = DecoratorInfos.build(cls) + + # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class + if __pydantic_generic_metadata__: + cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__ + else: + parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ()) + parameters = getattr(cls, '__parameters__', None) or parent_parameters + if parameters and parent_parameters and not all(x in parameters for x in parent_parameters): + from ..root_model import RootModelRootType + + missing_parameters = tuple(x for x in parameters if x not in parent_parameters) + if RootModelRootType in parent_parameters and RootModelRootType not in parameters: + # This is a special case where the user has subclassed `RootModel`, but has not parametrized + # RootModel with the generic type identifiers being used. Ex: + # class MyModel(RootModel, Generic[T]): + # root: T + # Should instead just be: + # class MyModel(RootModel[T]): + # root: T + parameters_str = ', '.join([x.__name__ for x in missing_parameters]) + error_message = ( + f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) ' + f'{parameters_str} in its parameters. ' + f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.' + ) + else: + combined_parameters = parent_parameters + missing_parameters + parameters_str = ', '.join([str(x) for x in combined_parameters]) + generic_type_label = f'typing.Generic[{parameters_str}]' + error_message = ( + f'All parameters must be present on typing.Generic;' + f' you should inherit from {generic_type_label}.' + ) + if Generic not in bases: # pragma: no cover + # We raise an error here not because it is desirable, but because some cases are mishandled. + # It would be nice to remove this error and still have things behave as expected, it's just + # challenging because we are using a custom `__class_getitem__` to parametrize generic models, + # and not returning a typing._GenericAlias from it. + bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label]) + error_message += ( + f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)' + ) + raise TypeError(error_message) + + cls.__pydantic_generic_metadata__ = { + 'origin': None, + 'args': (), + 'parameters': parameters, + } + + cls.__pydantic_complete__ = False # Ensure this specific class gets completed + + # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487 + # for attributes not in `new_namespace` (e.g. private attributes) + for name, obj in private_attributes.items(): + obj.__set_name__(cls, name) + + if __pydantic_reset_parent_namespace__: + cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace()) + parent_namespace = getattr(cls, '__pydantic_parent_namespace__', None) + if isinstance(parent_namespace, dict): + parent_namespace = unpack_lenient_weakvaluedict(parent_namespace) + + types_namespace = get_cls_types_namespace(cls, parent_namespace) + set_model_fields(cls, bases, config_wrapper, types_namespace) + + if config_wrapper.frozen and '__hash__' not in namespace: + set_default_hash_func(cls, bases) + + complete_model_class( + cls, + cls_name, + config_wrapper, + raise_errors=False, + types_namespace=types_namespace, + create_model_module=_create_model_module, + ) + + # If this is placed before the complete_model_class call above, + # the generic computed fields return type is set to PydanticUndefined + cls.model_computed_fields = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()} + + set_deprecated_descriptors(cls) + + # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__ + # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is + # only hit for _proper_ subclasses of BaseModel + super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc] + return cls + else: + # this is the BaseModel class itself being created, no logic required + return super().__new__(mcs, cls_name, bases, namespace, **kwargs) + + if not typing.TYPE_CHECKING: # pragma: no branch + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + + def __getattr__(self, item: str) -> Any: + """This is necessary to keep attribute access working for class attribute access.""" + private_attributes = self.__dict__.get('__private_attributes__') + if private_attributes and item in private_attributes: + return private_attributes[item] + if item == '__pydantic_core_schema__': + # This means the class didn't get a schema generated for it, likely because there was an undefined reference + maybe_mock_validator = getattr(self, '__pydantic_validator__', None) + if isinstance(maybe_mock_validator, MockValSer): + rebuilt_validator = maybe_mock_validator.rebuild() + if rebuilt_validator is not None: + # In this case, a validator was built, and so `__pydantic_core_schema__` should now be set + return getattr(self, '__pydantic_core_schema__') + raise AttributeError(item) + + @classmethod + def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]: + return _ModelNamespaceDict() + + def __instancecheck__(self, instance: Any) -> bool: + """Avoid calling ABC _abc_subclasscheck unless we're pretty sure. + + See #3829 and python/cpython#92810 + """ + return hasattr(instance, '__pydantic_validator__') and super().__instancecheck__(instance) + + @staticmethod + def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]: + from ..main import BaseModel + + field_names: set[str] = set() + class_vars: set[str] = set() + private_attributes: dict[str, ModelPrivateAttr] = {} + for base in bases: + if issubclass(base, BaseModel) and base is not BaseModel: + # model_fields might not be defined yet in the case of generics, so we use getattr here: + field_names.update(getattr(base, 'model_fields', {}).keys()) + class_vars.update(base.__class_vars__) + private_attributes.update(base.__private_attributes__) + return field_names, class_vars, private_attributes + + @property + @deprecated('The `__fields__` attribute is deprecated, use `model_fields` instead.', category=None) + def __fields__(self) -> dict[str, FieldInfo]: + warnings.warn( + 'The `__fields__` attribute is deprecated, use `model_fields` instead.', PydanticDeprecatedSince20 + ) + return self.model_fields # type: ignore + + def __dir__(self) -> list[str]: + attributes = list(super().__dir__()) + if '__fields__' in attributes: + attributes.remove('__fields__') + return attributes + + +def init_private_attributes(self: BaseModel, __context: Any) -> None: + """This function is meant to behave like a BaseModel method to initialise private attributes. + + It takes context as an argument since that's what pydantic-core passes when calling it. + + Args: + self: The BaseModel instance. + __context: The context. + """ + if getattr(self, '__pydantic_private__', None) is None: + pydantic_private = {} + for name, private_attr in self.__private_attributes__.items(): + default = private_attr.get_default() + if default is not PydanticUndefined: + pydantic_private[name] = default + object_setattr(self, '__pydantic_private__', pydantic_private) + + +def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None: + """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined.""" + if 'model_post_init' in namespace: + return namespace['model_post_init'] + + from ..main import BaseModel + + model_post_init = get_attribute_from_bases(bases, 'model_post_init') + if model_post_init is not BaseModel.model_post_init: + return model_post_init + + +def inspect_namespace( # noqa C901 + namespace: dict[str, Any], + ignored_types: tuple[type[Any], ...], + base_class_vars: set[str], + base_class_fields: set[str], +) -> dict[str, ModelPrivateAttr]: + """Iterate over the namespace and: + * gather private attributes + * check for items which look like fields but are not (e.g. have no annotation) and warn. + + Args: + namespace: The attribute dictionary of the class to be created. + ignored_types: A tuple of ignore types. + base_class_vars: A set of base class class variables. + base_class_fields: A set of base class fields. + + Returns: + A dict contains private attributes info. + + Raises: + TypeError: If there is a `__root__` field in model. + NameError: If private attribute name is invalid. + PydanticUserError: + - If a field does not have a type annotation. + - If a field on base class was overridden by a non-annotated attribute. + """ + from ..fields import FieldInfo, ModelPrivateAttr, PrivateAttr + + all_ignored_types = ignored_types + default_ignored_types() + + private_attributes: dict[str, ModelPrivateAttr] = {} + raw_annotations = namespace.get('__annotations__', {}) + + if '__root__' in raw_annotations or '__root__' in namespace: + raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'") + + ignored_names: set[str] = set() + for var_name, value in list(namespace.items()): + if var_name == 'model_config': + continue + elif ( + isinstance(value, type) + and value.__module__ == namespace['__module__'] + and '__qualname__' in namespace + and value.__qualname__.startswith(namespace['__qualname__']) + ): + # `value` is a nested type defined in this namespace; don't error + continue + elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools': + ignored_names.add(var_name) + continue + elif isinstance(value, ModelPrivateAttr): + if var_name.startswith('__'): + raise NameError( + 'Private attributes must not use dunder names;' + f' use a single underscore prefix instead of {var_name!r}.' + ) + elif is_valid_field_name(var_name): + raise NameError( + 'Private attributes must not use valid field names;' + f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.' + ) + private_attributes[var_name] = value + del namespace[var_name] + elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name): + suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name + raise NameError( + f'Fields must not use names with leading underscores;' + f' e.g., use {suggested_name!r} instead of {var_name!r}.' + ) + + elif var_name.startswith('__'): + continue + elif is_valid_privateattr_name(var_name): + if var_name not in raw_annotations or not is_classvar(raw_annotations[var_name]): + private_attributes[var_name] = PrivateAttr(default=value) + del namespace[var_name] + elif var_name in base_class_vars: + continue + elif var_name not in raw_annotations: + if var_name in base_class_fields: + raise PydanticUserError( + f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. ' + f'All field definitions, including overrides, require a type annotation.', + code='model-field-overridden', + ) + elif isinstance(value, FieldInfo): + raise PydanticUserError( + f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation' + ) + else: + raise PydanticUserError( + f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a ' + f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this ' + f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.", + code='model-field-missing-annotation', + ) + + for ann_name, ann_type in raw_annotations.items(): + if ( + is_valid_privateattr_name(ann_name) + and ann_name not in private_attributes + and ann_name not in ignored_names + and not is_classvar(ann_type) + and ann_type not in all_ignored_types + and getattr(ann_type, '__module__', None) != 'functools' + ): + if is_annotated(ann_type): + _, *metadata = typing_extensions.get_args(ann_type) + private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None) + if private_attr is not None: + private_attributes[ann_name] = private_attr + continue + private_attributes[ann_name] = PrivateAttr() + + return private_attributes + + +def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None: + base_hash_func = get_attribute_from_bases(bases, '__hash__') + new_hash_func = make_hash_func(cls) + if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__: + # If `__hash__` is some default, we generate a hash function. + # It will be `None` if not overridden from BaseModel. + # It may be `object.__hash__` if there is another + # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`). + # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model. + # In the last case we still need a new hash function to account for new `model_fields`. + cls.__hash__ = new_hash_func + + +def make_hash_func(cls: type[BaseModel]) -> Any: + getter = operator.itemgetter(*cls.model_fields.keys()) if cls.model_fields else lambda _: 0 + + def hash_func(self: Any) -> int: + try: + return hash(getter(self.__dict__)) + except KeyError: + # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain + # all model fields, which is how we can get here. + # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys, + # and wrapping it in a `try` doesn't slow things down much in the common case. + return hash(getter(SafeGetItemProxy(self.__dict__))) + + return hash_func + + +def set_model_fields( + cls: type[BaseModel], bases: tuple[type[Any], ...], config_wrapper: ConfigWrapper, types_namespace: dict[str, Any] +) -> None: + """Collect and set `cls.model_fields` and `cls.__class_vars__`. + + Args: + cls: BaseModel or dataclass. + bases: Parents of the class, generally `cls.__bases__`. + config_wrapper: The config wrapper instance. + types_namespace: Optional extra namespace to look for types in. + """ + typevars_map = get_model_typevars_map(cls) + fields, class_vars = collect_model_fields(cls, bases, config_wrapper, types_namespace, typevars_map=typevars_map) + + cls.model_fields = fields + cls.__class_vars__.update(class_vars) + + for k in class_vars: + # Class vars should not be private attributes + # We remove them _here_ and not earlier because we rely on inspecting the class to determine its classvars, + # but private attributes are determined by inspecting the namespace _prior_ to class creation. + # In the case that a classvar with a leading-'_' is defined via a ForwardRef (e.g., when using + # `__future__.annotations`), we want to remove the private attribute which was detected _before_ we knew it + # evaluated to a classvar + + value = cls.__private_attributes__.pop(k, None) + if value is not None and value.default is not PydanticUndefined: + setattr(cls, k, value.default) + + +def complete_model_class( + cls: type[BaseModel], + cls_name: str, + config_wrapper: ConfigWrapper, + *, + raise_errors: bool = True, + types_namespace: dict[str, Any] | None, + create_model_module: str | None = None, +) -> bool: + """Finish building a model class. + + This logic must be called after class has been created since validation functions must be bound + and `get_type_hints` requires a class object. + + Args: + cls: BaseModel or dataclass. + cls_name: The model or dataclass name. + config_wrapper: The config wrapper instance. + raise_errors: Whether to raise errors. + types_namespace: Optional extra namespace to look for types in. + create_model_module: The module of the class to be created, if created by `create_model`. + + Returns: + `True` if the model is successfully completed, else `False`. + + Raises: + PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs in`__get_pydantic_core_schema__` + and `raise_errors=True`. + """ + typevars_map = get_model_typevars_map(cls) + gen_schema = GenerateSchema( + config_wrapper, + types_namespace, + typevars_map, + ) + + handler = CallbackGetCoreSchemaHandler( + partial(gen_schema.generate_schema, from_dunder_get_core_schema=False), + gen_schema, + ref_mode='unpack', + ) + + if config_wrapper.defer_build: + set_model_mocks(cls, cls_name) + return False + + try: + schema = cls.__get_pydantic_core_schema__(cls, handler) + except PydanticUndefinedAnnotation as e: + if raise_errors: + raise + set_model_mocks(cls, cls_name, f'`{e.name}`') + return False + + core_config = config_wrapper.core_config(cls) + + try: + schema = gen_schema.clean_schema(schema) + except gen_schema.CollectedInvalid: + set_model_mocks(cls, cls_name) + return False + + # debug(schema) + cls.__pydantic_core_schema__ = schema + + cls.__pydantic_validator__ = create_schema_validator( + schema, + cls, + create_model_module or cls.__module__, + cls.__qualname__, + 'create_model' if create_model_module else 'BaseModel', + core_config, + config_wrapper.plugin_settings, + ) + cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config) + cls.__pydantic_complete__ = True + + # set __signature__ attr only for model class, but not for its instances + cls.__signature__ = ClassAttribute( + '__signature__', + generate_pydantic_signature(init=cls.__init__, fields=cls.model_fields, config_wrapper=config_wrapper), + ) + return True + + +def set_deprecated_descriptors(cls: type[BaseModel]) -> None: + """Set data descriptors on the class for deprecated fields.""" + for field, field_info in cls.model_fields.items(): + if (msg := field_info.deprecation_message) is not None: + desc = _DeprecatedFieldDescriptor(msg) + desc.__set_name__(cls, field) + setattr(cls, field, desc) + + for field, computed_field_info in cls.model_computed_fields.items(): + if ( + (msg := computed_field_info.deprecation_message) is not None + # Avoid having two warnings emitted: + and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__') + ): + desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property) + desc.__set_name__(cls, field) + setattr(cls, field, desc) + + +class _DeprecatedFieldDescriptor: + """Data descriptor used to emit a runtime deprecation warning before accessing a deprecated field. + + Attributes: + msg: The deprecation message to be emitted. + wrapped_property: The property instance if the deprecated field is a computed field, or `None`. + field_name: The name of the field being deprecated. + """ + + field_name: str + + def __init__(self, msg: str, wrapped_property: property | None = None) -> None: + self.msg = msg + self.wrapped_property = wrapped_property + + def __set_name__(self, cls: type[BaseModel], name: str) -> None: + self.field_name = name + + def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any: + if obj is None: + raise AttributeError(self.field_name) + + warnings.warn(self.msg, builtins.DeprecationWarning, stacklevel=2) + + if self.wrapped_property is not None: + return self.wrapped_property.__get__(obj, obj_type) + return obj.__dict__[self.field_name] + + # Defined to take precedence over the instance's dictionary + # Note that it will not be called when setting a value on a model instance + # as `BaseModel.__setattr__` is defined and takes priority. + def __set__(self, obj: Any, value: Any) -> NoReturn: + raise AttributeError(self.field_name) + + +class _PydanticWeakRef: + """Wrapper for `weakref.ref` that enables `pickle` serialization. + + Cloudpickle fails to serialize `weakref.ref` objects due to an arcane error related + to abstract base classes (`abc.ABC`). This class works around the issue by wrapping + `weakref.ref` instead of subclassing it. + + See https://github.com/pydantic/pydantic/issues/6763 for context. + + Semantics: + - If not pickled, behaves the same as a `weakref.ref`. + - If pickled along with the referenced object, the same `weakref.ref` behavior + will be maintained between them after unpickling. + - If pickled without the referenced object, after unpickling the underlying + reference will be cleared (`__call__` will always return `None`). + """ + + def __init__(self, obj: Any): + if obj is None: + # The object will be `None` upon deserialization if the serialized weakref + # had lost its underlying object. + self._wr = None + else: + self._wr = weakref.ref(obj) + + def __call__(self) -> Any: + if self._wr is None: + return None + else: + return self._wr() + + def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]: + return _PydanticWeakRef, (self(),) + + +def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None: + """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs. + + We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values + in a WeakValueDictionary. + + The `unpack_lenient_weakvaluedict` function can be used to reverse this operation. + """ + if d is None: + return None + result = {} + for k, v in d.items(): + try: + proxy = _PydanticWeakRef(v) + except TypeError: + proxy = v + result[k] = proxy + return result + + +def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None: + """Inverts the transform performed by `build_lenient_weakvaluedict`.""" + if d is None: + return None + + result = {} + for k, v in d.items(): + if isinstance(v, _PydanticWeakRef): + v = v() + if v is not None: + result[k] = v + else: + result[k] = v + return result + + +def default_ignored_types() -> tuple[type[Any], ...]: + from ..fields import ComputedFieldInfo + + return ( + FunctionType, + property, + classmethod, + staticmethod, + PydanticDescriptorProxy, + ComputedFieldInfo, + ValidateCallWrapper, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_repr.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_repr.py new file mode 100644 index 0000000..479b447 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_repr.py @@ -0,0 +1,117 @@ +"""Tools to provide pretty/human-readable display of objects.""" +from __future__ import annotations as _annotations + +import types +import typing +from typing import Any + +import typing_extensions + +from . import _typing_extra + +if typing.TYPE_CHECKING: + ReprArgs: typing_extensions.TypeAlias = 'typing.Iterable[tuple[str | None, Any]]' + RichReprResult: typing_extensions.TypeAlias = ( + 'typing.Iterable[Any | tuple[Any] | tuple[str, Any] | tuple[str, Any, Any]]' + ) + + +class PlainRepr(str): + """String class where repr doesn't include quotes. Useful with Representation when you want to return a string + representation of something that is valid (or pseudo-valid) python. + """ + + def __repr__(self) -> str: + return str(self) + + +class Representation: + # Mixin to provide `__str__`, `__repr__`, and `__pretty__` and `__rich_repr__` methods. + # `__pretty__` is used by [devtools](https://python-devtools.helpmanual.io/). + # `__rich_repr__` is used by [rich](https://rich.readthedocs.io/en/stable/pretty.html). + # (this is not a docstring to avoid adding a docstring to classes which inherit from Representation) + + # we don't want to use a type annotation here as it can break get_type_hints + __slots__ = tuple() # type: typing.Collection[str] + + def __repr_args__(self) -> ReprArgs: + """Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden. + + Can either return: + * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]` + * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]` + """ + attrs_names = self.__slots__ + if not attrs_names and hasattr(self, '__dict__'): + attrs_names = self.__dict__.keys() + attrs = ((s, getattr(self, s)) for s in attrs_names) + return [(a, v) for a, v in attrs if v is not None] + + def __repr_name__(self) -> str: + """Name of the instance's class, used in __repr__.""" + return self.__class__.__name__ + + def __repr_str__(self, join_str: str) -> str: + return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__()) + + def __pretty__(self, fmt: typing.Callable[[Any], Any], **kwargs: Any) -> typing.Generator[Any, None, None]: + """Used by devtools (https://python-devtools.helpmanual.io/) to pretty print objects.""" + yield self.__repr_name__() + '(' + yield 1 + for name, value in self.__repr_args__(): + if name is not None: + yield name + '=' + yield fmt(value) + yield ',' + yield 0 + yield -1 + yield ')' + + def __rich_repr__(self) -> RichReprResult: + """Used by Rich (https://rich.readthedocs.io/en/stable/pretty.html) to pretty print objects.""" + for name, field_repr in self.__repr_args__(): + if name is None: + yield field_repr + else: + yield name, field_repr + + def __str__(self) -> str: + return self.__repr_str__(' ') + + def __repr__(self) -> str: + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' + + +def display_as_type(obj: Any) -> str: + """Pretty representation of a type, should be as close as possible to the original type definition string. + + Takes some logic from `typing._type_repr`. + """ + if isinstance(obj, types.FunctionType): + return obj.__name__ + elif obj is ...: + return '...' + elif isinstance(obj, Representation): + return repr(obj) + elif isinstance(obj, typing_extensions.TypeAliasType): + return str(obj) + + if not isinstance(obj, (_typing_extra.typing_base, _typing_extra.WithArgsTypes, type)): + obj = obj.__class__ + + if _typing_extra.origin_is_union(typing_extensions.get_origin(obj)): + args = ', '.join(map(display_as_type, typing_extensions.get_args(obj))) + return f'Union[{args}]' + elif isinstance(obj, _typing_extra.WithArgsTypes): + if typing_extensions.get_origin(obj) == typing_extensions.Literal: + args = ', '.join(map(repr, typing_extensions.get_args(obj))) + else: + args = ', '.join(map(display_as_type, typing_extensions.get_args(obj))) + try: + return f'{obj.__qualname__}[{args}]' + except AttributeError: + return str(obj) # handles TypeAliasType in 3.12 + elif isinstance(obj, type): + return obj.__qualname__ + else: + return repr(obj).replace('typing.', '').replace('typing_extensions.', '') diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py new file mode 100644 index 0000000..2cf01c6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py @@ -0,0 +1,124 @@ +"""Types and utility functions used by various other internal tools.""" +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable + +from pydantic_core import core_schema +from typing_extensions import Literal + +from ..annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler + +if TYPE_CHECKING: + from ..json_schema import GenerateJsonSchema, JsonSchemaValue + from ._core_utils import CoreSchemaOrField + from ._generate_schema import GenerateSchema + + GetJsonSchemaFunction = Callable[[CoreSchemaOrField, GetJsonSchemaHandler], JsonSchemaValue] + HandlerOverride = Callable[[CoreSchemaOrField], JsonSchemaValue] + + +class GenerateJsonSchemaHandler(GetJsonSchemaHandler): + """JsonSchemaHandler implementation that doesn't do ref unwrapping by default. + + This is used for any Annotated metadata so that we don't end up with conflicting + modifications to the definition schema. + + Used internally by Pydantic, please do not rely on this implementation. + See `GetJsonSchemaHandler` for the handler API. + """ + + def __init__(self, generate_json_schema: GenerateJsonSchema, handler_override: HandlerOverride | None) -> None: + self.generate_json_schema = generate_json_schema + self.handler = handler_override or generate_json_schema.generate_inner + self.mode = generate_json_schema.mode + + def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue: + return self.handler(core_schema) + + def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue) -> JsonSchemaValue: + """Resolves `$ref` in the json schema. + + This returns the input json schema if there is no `$ref` in json schema. + + Args: + maybe_ref_json_schema: The input json schema that may contains `$ref`. + + Returns: + Resolved json schema. + + Raises: + LookupError: If it can't find the definition for `$ref`. + """ + if '$ref' not in maybe_ref_json_schema: + return maybe_ref_json_schema + ref = maybe_ref_json_schema['$ref'] + json_schema = self.generate_json_schema.get_schema_from_definitions(ref) + if json_schema is None: + raise LookupError( + f'Could not find a ref for {ref}.' + ' Maybe you tried to call resolve_ref_schema from within a recursive model?' + ) + return json_schema + + +class CallbackGetCoreSchemaHandler(GetCoreSchemaHandler): + """Wrapper to use an arbitrary function as a `GetCoreSchemaHandler`. + + Used internally by Pydantic, please do not rely on this implementation. + See `GetCoreSchemaHandler` for the handler API. + """ + + def __init__( + self, + handler: Callable[[Any], core_schema.CoreSchema], + generate_schema: GenerateSchema, + ref_mode: Literal['to-def', 'unpack'] = 'to-def', + ) -> None: + self._handler = handler + self._generate_schema = generate_schema + self._ref_mode = ref_mode + + def __call__(self, source_type: Any, /) -> core_schema.CoreSchema: + schema = self._handler(source_type) + ref = schema.get('ref') + if self._ref_mode == 'to-def': + if ref is not None: + self._generate_schema.defs.definitions[ref] = schema + return core_schema.definition_reference_schema(ref) + return schema + else: # ref_mode = 'unpack + return self.resolve_ref_schema(schema) + + def _get_types_namespace(self) -> dict[str, Any] | None: + return self._generate_schema._types_namespace + + def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema: + return self._generate_schema.generate_schema(source_type) + + @property + def field_name(self) -> str | None: + return self._generate_schema.field_name_stack.get() + + def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema) -> core_schema.CoreSchema: + """Resolves reference in the core schema. + + Args: + maybe_ref_schema: The input core schema that may contains reference. + + Returns: + Resolved core schema. + + Raises: + LookupError: If it can't find the definition for reference. + """ + if maybe_ref_schema['type'] == 'definition-ref': + ref = maybe_ref_schema['schema_ref'] + if ref not in self._generate_schema.defs.definitions: + raise LookupError( + f'Could not find a ref for {ref}.' + ' Maybe you tried to call resolve_ref_schema from within a recursive model?' + ) + return self._generate_schema.defs.definitions[ref] + elif maybe_ref_schema['type'] == 'definitions': + return self.resolve_ref_schema(maybe_ref_schema['schema']) + return maybe_ref_schema diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_signature.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_signature.py new file mode 100644 index 0000000..816a165 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_signature.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +import dataclasses +from inspect import Parameter, Signature, signature +from typing import TYPE_CHECKING, Any, Callable + +from pydantic_core import PydanticUndefined + +from ._config import ConfigWrapper +from ._utils import is_valid_identifier + +if TYPE_CHECKING: + from ..fields import FieldInfo + + +def _field_name_for_signature(field_name: str, field_info: FieldInfo) -> str: + """Extract the correct name to use for the field when generating a signature. + + Assuming the field has a valid alias, this will return the alias. Otherwise, it will return the field name. + First priority is given to the validation_alias, then the alias, then the field name. + + Args: + field_name: The name of the field + field_info: The corresponding FieldInfo object. + + Returns: + The correct name to use when generating a signature. + """ + + def _alias_if_valid(x: Any) -> str | None: + """Return the alias if it is a valid alias and identifier, else None.""" + return x if isinstance(x, str) and is_valid_identifier(x) else None + + return _alias_if_valid(field_info.alias) or _alias_if_valid(field_info.validation_alias) or field_name + + +def _process_param_defaults(param: Parameter) -> Parameter: + """Modify the signature for a parameter in a dataclass where the default value is a FieldInfo instance. + + Args: + param (Parameter): The parameter + + Returns: + Parameter: The custom processed parameter + """ + from ..fields import FieldInfo + + param_default = param.default + if isinstance(param_default, FieldInfo): + annotation = param.annotation + # Replace the annotation if appropriate + # inspect does "clever" things to show annotations as strings because we have + # `from __future__ import annotations` in main, we don't want that + if annotation == 'Any': + annotation = Any + + # Replace the field default + default = param_default.default + if default is PydanticUndefined: + if param_default.default_factory is PydanticUndefined: + default = Signature.empty + else: + # this is used by dataclasses to indicate a factory exists: + default = dataclasses._HAS_DEFAULT_FACTORY # type: ignore + return param.replace( + annotation=annotation, name=_field_name_for_signature(param.name, param_default), default=default + ) + return param + + +def _generate_signature_parameters( # noqa: C901 (ignore complexity, could use a refactor) + init: Callable[..., None], + fields: dict[str, FieldInfo], + config_wrapper: ConfigWrapper, +) -> dict[str, Parameter]: + """Generate a mapping of parameter names to Parameter objects for a pydantic BaseModel or dataclass.""" + from itertools import islice + + present_params = signature(init).parameters.values() + merged_params: dict[str, Parameter] = {} + var_kw = None + use_var_kw = False + + for param in islice(present_params, 1, None): # skip self arg + # inspect does "clever" things to show annotations as strings because we have + # `from __future__ import annotations` in main, we don't want that + if fields.get(param.name): + # exclude params with init=False + if getattr(fields[param.name], 'init', True) is False: + continue + param = param.replace(name=_field_name_for_signature(param.name, fields[param.name])) + if param.annotation == 'Any': + param = param.replace(annotation=Any) + if param.kind is param.VAR_KEYWORD: + var_kw = param + continue + merged_params[param.name] = param + + if var_kw: # if custom init has no var_kw, fields which are not declared in it cannot be passed through + allow_names = config_wrapper.populate_by_name + for field_name, field in fields.items(): + # when alias is a str it should be used for signature generation + param_name = _field_name_for_signature(field_name, field) + + if field_name in merged_params or param_name in merged_params: + continue + + if not is_valid_identifier(param_name): + if allow_names: + param_name = field_name + else: + use_var_kw = True + continue + + kwargs = {} if field.is_required() else {'default': field.get_default(call_default_factory=False)} + merged_params[param_name] = Parameter( + param_name, Parameter.KEYWORD_ONLY, annotation=field.rebuild_annotation(), **kwargs + ) + + if config_wrapper.extra == 'allow': + use_var_kw = True + + if var_kw and use_var_kw: + # Make sure the parameter for extra kwargs + # does not have the same name as a field + default_model_signature = [ + ('self', Parameter.POSITIONAL_ONLY), + ('data', Parameter.VAR_KEYWORD), + ] + if [(p.name, p.kind) for p in present_params] == default_model_signature: + # if this is the standard model signature, use extra_data as the extra args name + var_kw_name = 'extra_data' + else: + # else start from var_kw + var_kw_name = var_kw.name + + # generate a name that's definitely unique + while var_kw_name in fields: + var_kw_name += '_' + merged_params[var_kw_name] = var_kw.replace(name=var_kw_name) + + return merged_params + + +def generate_pydantic_signature( + init: Callable[..., None], fields: dict[str, FieldInfo], config_wrapper: ConfigWrapper, is_dataclass: bool = False +) -> Signature: + """Generate signature for a pydantic BaseModel or dataclass. + + Args: + init: The class init. + fields: The model fields. + config_wrapper: The config wrapper instance. + is_dataclass: Whether the model is a dataclass. + + Returns: + The dataclass/BaseModel subclass signature. + """ + merged_params = _generate_signature_parameters(init, fields, config_wrapper) + + if is_dataclass: + merged_params = {k: _process_param_defaults(v) for k, v in merged_params.items()} + + return Signature(parameters=list(merged_params.values()), return_annotation=None) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_std_types_schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_std_types_schema.py new file mode 100644 index 0000000..0b65a89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_std_types_schema.py @@ -0,0 +1,715 @@ +"""Logic for generating pydantic-core schemas for standard library types. + +Import of this module is deferred since it contains imports of many standard library modules. +""" +from __future__ import annotations as _annotations + +import collections +import collections.abc +import dataclasses +import decimal +import inspect +import os +import typing +from enum import Enum +from functools import partial +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from operator import attrgetter +from typing import Any, Callable, Iterable, Literal, TypeVar + +import typing_extensions +from pydantic_core import ( + CoreSchema, + MultiHostUrl, + PydanticCustomError, + PydanticOmit, + Url, + core_schema, +) +from typing_extensions import get_args, get_origin + +from pydantic.errors import PydanticSchemaGenerationError +from pydantic.fields import FieldInfo +from pydantic.types import Strict + +from ..config import ConfigDict +from ..json_schema import JsonSchemaValue +from . import _known_annotated_metadata, _typing_extra, _validators +from ._core_utils import get_type_ref +from ._internal_dataclass import slots_true +from ._schema_generation_shared import GetCoreSchemaHandler, GetJsonSchemaHandler + +if typing.TYPE_CHECKING: + from ._generate_schema import GenerateSchema + + StdSchemaFunction = Callable[[GenerateSchema, type[Any]], core_schema.CoreSchema] + + +@dataclasses.dataclass(**slots_true) +class SchemaTransformer: + get_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] + get_json_schema: Callable[[CoreSchema, GetJsonSchemaHandler], JsonSchemaValue] + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + return self.get_core_schema(source_type, handler) + + def __get_pydantic_json_schema__(self, schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + return self.get_json_schema(schema, handler) + + +def get_enum_core_schema(enum_type: type[Enum], config: ConfigDict) -> CoreSchema: + cases: list[Any] = list(enum_type.__members__.values()) + + enum_ref = get_type_ref(enum_type) + description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) + if description == 'An enumeration.': # This is the default value provided by enum.EnumMeta.__new__; don't use it + description = None + js_updates = {'title': enum_type.__name__, 'description': description} + js_updates = {k: v for k, v in js_updates.items() if v is not None} + + sub_type: Literal['str', 'int', 'float'] | None = None + if issubclass(enum_type, int): + sub_type = 'int' + value_ser_type: core_schema.SerSchema = core_schema.simple_ser_schema('int') + elif issubclass(enum_type, str): + # this handles `StrEnum` (3.11 only), and also `Foobar(str, Enum)` + sub_type = 'str' + value_ser_type = core_schema.simple_ser_schema('str') + elif issubclass(enum_type, float): + sub_type = 'float' + value_ser_type = core_schema.simple_ser_schema('float') + else: + # TODO this is an ugly hack, how do we trigger an Any schema for serialization? + value_ser_type = core_schema.plain_serializer_function_ser_schema(lambda x: x) + + if cases: + + def get_json_schema(schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(schema) + original_schema = handler.resolve_ref_schema(json_schema) + original_schema.update(js_updates) + return json_schema + + # we don't want to add the missing to the schema if it's the default one + default_missing = getattr(enum_type._missing_, '__func__', None) == Enum._missing_.__func__ # type: ignore + enum_schema = core_schema.enum_schema( + enum_type, + cases, + sub_type=sub_type, + missing=None if default_missing else enum_type._missing_, + ref=enum_ref, + metadata={'pydantic_js_functions': [get_json_schema]}, + ) + + if config.get('use_enum_values', False): + enum_schema = core_schema.no_info_after_validator_function( + attrgetter('value'), enum_schema, serialization=value_ser_type + ) + + return enum_schema + + else: + + def get_json_schema_no_cases(_, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(core_schema.enum_schema(enum_type, cases, sub_type=sub_type, ref=enum_ref)) + original_schema = handler.resolve_ref_schema(json_schema) + original_schema.update(js_updates) + return json_schema + + # Use an isinstance check for enums with no cases. + # The most important use case for this is creating TypeVar bounds for generics that should + # be restricted to enums. This is more consistent than it might seem at first, since you can only + # subclass enum.Enum (or subclasses of enum.Enum) if all parent classes have no cases. + # We use the get_json_schema function when an Enum subclass has been declared with no cases + # so that we can still generate a valid json schema. + return core_schema.is_instance_schema( + enum_type, + metadata={'pydantic_js_functions': [get_json_schema_no_cases]}, + ) + + +@dataclasses.dataclass(**slots_true) +class InnerSchemaValidator: + """Use a fixed CoreSchema, avoiding interference from outward annotations.""" + + core_schema: CoreSchema + js_schema: JsonSchemaValue | None = None + js_core_schema: CoreSchema | None = None + js_schema_update: JsonSchemaValue | None = None + + def __get_pydantic_json_schema__(self, _schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + if self.js_schema is not None: + return self.js_schema + js_schema = handler(self.js_core_schema or self.core_schema) + if self.js_schema_update is not None: + js_schema.update(self.js_schema_update) + return js_schema + + def __get_pydantic_core_schema__(self, _source_type: Any, _handler: GetCoreSchemaHandler) -> CoreSchema: + return self.core_schema + + +def decimal_prepare_pydantic_annotations( + source: Any, annotations: Iterable[Any], config: ConfigDict +) -> tuple[Any, list[Any]] | None: + if source is not decimal.Decimal: + return None + + metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) + + config_allow_inf_nan = config.get('allow_inf_nan') + if config_allow_inf_nan is not None: + metadata.setdefault('allow_inf_nan', config_allow_inf_nan) + + _known_annotated_metadata.check_metadata( + metadata, {*_known_annotated_metadata.FLOAT_CONSTRAINTS, 'max_digits', 'decimal_places'}, decimal.Decimal + ) + return source, [InnerSchemaValidator(core_schema.decimal_schema(**metadata)), *remaining_annotations] + + +def datetime_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + import datetime + + metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) + if source_type is datetime.date: + sv = InnerSchemaValidator(core_schema.date_schema(**metadata)) + elif source_type is datetime.datetime: + sv = InnerSchemaValidator(core_schema.datetime_schema(**metadata)) + elif source_type is datetime.time: + sv = InnerSchemaValidator(core_schema.time_schema(**metadata)) + elif source_type is datetime.timedelta: + sv = InnerSchemaValidator(core_schema.timedelta_schema(**metadata)) + else: + return None + # check now that we know the source type is correct + _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.DATE_TIME_CONSTRAINTS, source_type) + return (source_type, [sv, *remaining_annotations]) + + +def uuid_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + # UUIDs have no constraints - they are fixed length, constructing a UUID instance checks the length + + from uuid import UUID + + if source_type is not UUID: + return None + + return (source_type, [InnerSchemaValidator(core_schema.uuid_schema()), *annotations]) + + +def path_schema_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + import pathlib + + if source_type not in { + os.PathLike, + pathlib.Path, + pathlib.PurePath, + pathlib.PosixPath, + pathlib.PurePosixPath, + pathlib.PureWindowsPath, + }: + return None + + metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) + _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.STR_CONSTRAINTS, source_type) + + construct_path = pathlib.PurePath if source_type is os.PathLike else source_type + + def path_validator(input_value: str) -> os.PathLike[Any]: + try: + return construct_path(input_value) + except TypeError as e: + raise PydanticCustomError('path_type', 'Input is not a valid path') from e + + constrained_str_schema = core_schema.str_schema(**metadata) + + instance_schema = core_schema.json_or_python_schema( + json_schema=core_schema.no_info_after_validator_function(path_validator, constrained_str_schema), + python_schema=core_schema.is_instance_schema(source_type), + ) + + strict: bool | None = None + for annotation in annotations: + if isinstance(annotation, Strict): + strict = annotation.strict + + schema = core_schema.lax_or_strict_schema( + lax_schema=core_schema.union_schema( + [ + instance_schema, + core_schema.no_info_after_validator_function(path_validator, constrained_str_schema), + ], + custom_error_type='path_type', + custom_error_message='Input is not a valid path', + strict=True, + ), + strict_schema=instance_schema, + serialization=core_schema.to_string_ser_schema(), + strict=strict, + ) + + return ( + source_type, + [ + InnerSchemaValidator(schema, js_core_schema=constrained_str_schema, js_schema_update={'format': 'path'}), + *remaining_annotations, + ], + ) + + +def dequeue_validator( + input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, maxlen: None | int +) -> collections.deque[Any]: + if isinstance(input_value, collections.deque): + maxlens = [v for v in (input_value.maxlen, maxlen) if v is not None] + if maxlens: + maxlen = min(maxlens) + return collections.deque(handler(input_value), maxlen=maxlen) + else: + return collections.deque(handler(input_value), maxlen=maxlen) + + +def serialize_sequence_via_list( + v: Any, handler: core_schema.SerializerFunctionWrapHandler, info: core_schema.SerializationInfo +) -> Any: + items: list[Any] = [] + + mapped_origin = SEQUENCE_ORIGIN_MAP.get(type(v), None) + if mapped_origin is None: + # we shouldn't hit this branch, should probably add a serialization error or something + return v + + for index, item in enumerate(v): + try: + v = handler(item, index) + except PydanticOmit: + pass + else: + items.append(v) + + if info.mode_is_json(): + return items + else: + return mapped_origin(items) + + +@dataclasses.dataclass(**slots_true) +class SequenceValidator: + mapped_origin: type[Any] + item_source_type: type[Any] + min_length: int | None = None + max_length: int | None = None + strict: bool | None = None + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + if self.item_source_type is Any: + items_schema = None + else: + items_schema = handler.generate_schema(self.item_source_type) + + metadata = {'min_length': self.min_length, 'max_length': self.max_length, 'strict': self.strict} + + if self.mapped_origin in (list, set, frozenset): + if self.mapped_origin is list: + constrained_schema = core_schema.list_schema(items_schema, **metadata) + elif self.mapped_origin is set: + constrained_schema = core_schema.set_schema(items_schema, **metadata) + else: + assert self.mapped_origin is frozenset # safety check in case we forget to add a case + constrained_schema = core_schema.frozenset_schema(items_schema, **metadata) + + schema = constrained_schema + else: + # safety check in case we forget to add a case + assert self.mapped_origin in (collections.deque, collections.Counter) + + if self.mapped_origin is collections.deque: + # if we have a MaxLen annotation might as well set that as the default maxlen on the deque + # this lets us re-use existing metadata annotations to let users set the maxlen on a dequeue + # that e.g. comes from JSON + coerce_instance_wrap = partial( + core_schema.no_info_wrap_validator_function, + partial(dequeue_validator, maxlen=metadata.get('max_length', None)), + ) + else: + coerce_instance_wrap = partial(core_schema.no_info_after_validator_function, self.mapped_origin) + + # we have to use a lax list schema here, because we need to validate the deque's + # items via a list schema, but it's ok if the deque itself is not a list (same for Counter) + metadata_with_strict_override = {**metadata, 'strict': False} + constrained_schema = core_schema.list_schema(items_schema, **metadata_with_strict_override) + + check_instance = core_schema.json_or_python_schema( + json_schema=core_schema.list_schema(), + python_schema=core_schema.is_instance_schema(self.mapped_origin), + ) + + serialization = core_schema.wrap_serializer_function_ser_schema( + serialize_sequence_via_list, schema=items_schema or core_schema.any_schema(), info_arg=True + ) + + strict = core_schema.chain_schema([check_instance, coerce_instance_wrap(constrained_schema)]) + + if metadata.get('strict', False): + schema = strict + else: + lax = coerce_instance_wrap(constrained_schema) + schema = core_schema.lax_or_strict_schema(lax_schema=lax, strict_schema=strict) + schema['serialization'] = serialization + + return schema + + +SEQUENCE_ORIGIN_MAP: dict[Any, Any] = { + typing.Deque: collections.deque, + collections.deque: collections.deque, + list: list, + typing.List: list, + set: set, + typing.AbstractSet: set, + typing.Set: set, + frozenset: frozenset, + typing.FrozenSet: frozenset, + typing.Sequence: list, + typing.MutableSequence: list, + typing.MutableSet: set, + # this doesn't handle subclasses of these + # parametrized typing.Set creates one of these + collections.abc.MutableSet: set, + collections.abc.Set: frozenset, +} + + +def identity(s: CoreSchema) -> CoreSchema: + return s + + +def sequence_like_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + origin: Any = get_origin(source_type) + + mapped_origin = SEQUENCE_ORIGIN_MAP.get(origin, None) if origin else SEQUENCE_ORIGIN_MAP.get(source_type, None) + if mapped_origin is None: + return None + + args = get_args(source_type) + + if not args: + args = (Any,) + elif len(args) != 1: + raise ValueError('Expected sequence to have exactly 1 generic parameter') + + item_source_type = args[0] + + metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) + _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.SEQUENCE_CONSTRAINTS, source_type) + + return (source_type, [SequenceValidator(mapped_origin, item_source_type, **metadata), *remaining_annotations]) + + +MAPPING_ORIGIN_MAP: dict[Any, Any] = { + typing.DefaultDict: collections.defaultdict, + collections.defaultdict: collections.defaultdict, + collections.OrderedDict: collections.OrderedDict, + typing_extensions.OrderedDict: collections.OrderedDict, + dict: dict, + typing.Dict: dict, + collections.Counter: collections.Counter, + typing.Counter: collections.Counter, + # this doesn't handle subclasses of these + typing.Mapping: dict, + typing.MutableMapping: dict, + # parametrized typing.{Mutable}Mapping creates one of these + collections.abc.MutableMapping: dict, + collections.abc.Mapping: dict, +} + + +def defaultdict_validator( + input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, default_default_factory: Callable[[], Any] +) -> collections.defaultdict[Any, Any]: + if isinstance(input_value, collections.defaultdict): + default_factory = input_value.default_factory + return collections.defaultdict(default_factory, handler(input_value)) + else: + return collections.defaultdict(default_default_factory, handler(input_value)) + + +def get_defaultdict_default_default_factory(values_source_type: Any) -> Callable[[], Any]: + def infer_default() -> Callable[[], Any]: + allowed_default_types: dict[Any, Any] = { + typing.Tuple: tuple, + tuple: tuple, + collections.abc.Sequence: tuple, + collections.abc.MutableSequence: list, + typing.List: list, + list: list, + typing.Sequence: list, + typing.Set: set, + set: set, + typing.MutableSet: set, + collections.abc.MutableSet: set, + collections.abc.Set: frozenset, + typing.MutableMapping: dict, + typing.Mapping: dict, + collections.abc.Mapping: dict, + collections.abc.MutableMapping: dict, + float: float, + int: int, + str: str, + bool: bool, + } + values_type_origin = get_origin(values_source_type) or values_source_type + instructions = 'set using `DefaultDict[..., Annotated[..., Field(default_factory=...)]]`' + if isinstance(values_type_origin, TypeVar): + + def type_var_default_factory() -> None: + raise RuntimeError( + 'Generic defaultdict cannot be used without a concrete value type or an' + ' explicit default factory, ' + instructions + ) + + return type_var_default_factory + elif values_type_origin not in allowed_default_types: + # a somewhat subjective set of types that have reasonable default values + allowed_msg = ', '.join([t.__name__ for t in set(allowed_default_types.values())]) + raise PydanticSchemaGenerationError( + f'Unable to infer a default factory for keys of type {values_source_type}.' + f' Only {allowed_msg} are supported, other types require an explicit default factory' + ' ' + instructions + ) + return allowed_default_types[values_type_origin] + + # Assume Annotated[..., Field(...)] + if _typing_extra.is_annotated(values_source_type): + field_info = next((v for v in get_args(values_source_type) if isinstance(v, FieldInfo)), None) + else: + field_info = None + if field_info and field_info.default_factory: + default_default_factory = field_info.default_factory + else: + default_default_factory = infer_default() + return default_default_factory + + +@dataclasses.dataclass(**slots_true) +class MappingValidator: + mapped_origin: type[Any] + keys_source_type: type[Any] + values_source_type: type[Any] + min_length: int | None = None + max_length: int | None = None + strict: bool = False + + def serialize_mapping_via_dict(self, v: Any, handler: core_schema.SerializerFunctionWrapHandler) -> Any: + return handler(v) + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + if self.keys_source_type is Any: + keys_schema = None + else: + keys_schema = handler.generate_schema(self.keys_source_type) + if self.values_source_type is Any: + values_schema = None + else: + values_schema = handler.generate_schema(self.values_source_type) + + metadata = {'min_length': self.min_length, 'max_length': self.max_length, 'strict': self.strict} + + if self.mapped_origin is dict: + schema = core_schema.dict_schema(keys_schema, values_schema, **metadata) + else: + constrained_schema = core_schema.dict_schema(keys_schema, values_schema, **metadata) + check_instance = core_schema.json_or_python_schema( + json_schema=core_schema.dict_schema(), + python_schema=core_schema.is_instance_schema(self.mapped_origin), + ) + + if self.mapped_origin is collections.defaultdict: + default_default_factory = get_defaultdict_default_default_factory(self.values_source_type) + coerce_instance_wrap = partial( + core_schema.no_info_wrap_validator_function, + partial(defaultdict_validator, default_default_factory=default_default_factory), + ) + else: + coerce_instance_wrap = partial(core_schema.no_info_after_validator_function, self.mapped_origin) + + serialization = core_schema.wrap_serializer_function_ser_schema( + self.serialize_mapping_via_dict, + schema=core_schema.dict_schema( + keys_schema or core_schema.any_schema(), values_schema or core_schema.any_schema() + ), + info_arg=False, + ) + + strict = core_schema.chain_schema([check_instance, coerce_instance_wrap(constrained_schema)]) + + if metadata.get('strict', False): + schema = strict + else: + lax = coerce_instance_wrap(constrained_schema) + schema = core_schema.lax_or_strict_schema(lax_schema=lax, strict_schema=strict) + schema['serialization'] = serialization + + return schema + + +def mapping_like_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + origin: Any = get_origin(source_type) + + mapped_origin = MAPPING_ORIGIN_MAP.get(origin, None) if origin else MAPPING_ORIGIN_MAP.get(source_type, None) + if mapped_origin is None: + return None + + args = get_args(source_type) + + if not args: + args = (Any, Any) + elif mapped_origin is collections.Counter: + # a single generic + if len(args) != 1: + raise ValueError('Expected Counter to have exactly 1 generic parameter') + args = (args[0], int) # keys are always an int + elif len(args) != 2: + raise ValueError('Expected mapping to have exactly 2 generic parameters') + + keys_source_type, values_source_type = args + + metadata, remaining_annotations = _known_annotated_metadata.collect_known_metadata(annotations) + _known_annotated_metadata.check_metadata(metadata, _known_annotated_metadata.SEQUENCE_CONSTRAINTS, source_type) + + return ( + source_type, + [ + MappingValidator(mapped_origin, keys_source_type, values_source_type, **metadata), + *remaining_annotations, + ], + ) + + +def ip_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + def make_strict_ip_schema(tp: type[Any]) -> CoreSchema: + return core_schema.json_or_python_schema( + json_schema=core_schema.no_info_after_validator_function(tp, core_schema.str_schema()), + python_schema=core_schema.is_instance_schema(tp), + ) + + if source_type is IPv4Address: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_address_validator), + strict_schema=make_strict_ip_schema(IPv4Address), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv4'}, + ), + *annotations, + ] + if source_type is IPv4Network: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_network_validator), + strict_schema=make_strict_ip_schema(IPv4Network), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv4network'}, + ), + *annotations, + ] + if source_type is IPv4Interface: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v4_interface_validator), + strict_schema=make_strict_ip_schema(IPv4Interface), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv4interface'}, + ), + *annotations, + ] + + if source_type is IPv6Address: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_address_validator), + strict_schema=make_strict_ip_schema(IPv6Address), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv6'}, + ), + *annotations, + ] + if source_type is IPv6Network: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_network_validator), + strict_schema=make_strict_ip_schema(IPv6Network), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv6network'}, + ), + *annotations, + ] + if source_type is IPv6Interface: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.lax_or_strict_schema( + lax_schema=core_schema.no_info_plain_validator_function(_validators.ip_v6_interface_validator), + strict_schema=make_strict_ip_schema(IPv6Interface), + serialization=core_schema.to_string_ser_schema(), + ), + lambda _1, _2: {'type': 'string', 'format': 'ipv6interface'}, + ), + *annotations, + ] + + return None + + +def url_prepare_pydantic_annotations( + source_type: Any, annotations: Iterable[Any], _config: ConfigDict +) -> tuple[Any, list[Any]] | None: + if source_type is Url: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.url_schema(), + lambda cs, handler: handler(cs), + ), + *annotations, + ] + if source_type is MultiHostUrl: + return source_type, [ + SchemaTransformer( + lambda _1, _2: core_schema.multi_host_url_schema(), + lambda cs, handler: handler(cs), + ), + *annotations, + ] + + +PREPARE_METHODS: tuple[Callable[[Any, Iterable[Any], ConfigDict], tuple[Any, list[Any]] | None], ...] = ( + decimal_prepare_pydantic_annotations, + sequence_like_prepare_pydantic_annotations, + datetime_prepare_pydantic_annotations, + uuid_prepare_pydantic_annotations, + path_schema_prepare_pydantic_annotations, + mapping_like_prepare_pydantic_annotations, + ip_prepare_pydantic_annotations, + url_prepare_pydantic_annotations, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_typing_extra.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_typing_extra.py new file mode 100644 index 0000000..0873537 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_typing_extra.py @@ -0,0 +1,494 @@ +"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap python's typing module.""" +from __future__ import annotations as _annotations + +import dataclasses +import re +import sys +import types +import typing +import warnings +from collections.abc import Callable +from functools import partial +from types import GetSetDescriptorType +from typing import TYPE_CHECKING, Any, Final + +from typing_extensions import Annotated, Literal, TypeAliasType, TypeGuard, deprecated, get_args, get_origin + +if TYPE_CHECKING: + from ._dataclasses import StandardDataclass + +try: + from typing import _TypingBase # type: ignore[attr-defined] +except ImportError: + from typing import _Final as _TypingBase # type: ignore[attr-defined] + +typing_base = _TypingBase + + +if sys.version_info < (3, 9): + # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) + TypingGenericAlias = () +else: + from typing import GenericAlias as TypingGenericAlias # type: ignore + + +if sys.version_info < (3, 11): + from typing_extensions import NotRequired, Required +else: + from typing import NotRequired, Required # noqa: F401 + + +if sys.version_info < (3, 10): + + def origin_is_union(tp: type[Any] | None) -> bool: + return tp is typing.Union + + WithArgsTypes = (TypingGenericAlias,) + +else: + + def origin_is_union(tp: type[Any] | None) -> bool: + return tp is typing.Union or tp is types.UnionType + + WithArgsTypes = typing._GenericAlias, types.GenericAlias, types.UnionType # type: ignore[attr-defined] + + +if sys.version_info < (3, 10): + NoneType = type(None) + EllipsisType = type(Ellipsis) +else: + from types import NoneType as NoneType + + +LITERAL_TYPES: set[Any] = {Literal} +if hasattr(typing, 'Literal'): + LITERAL_TYPES.add(typing.Literal) # type: ignore + +# Check if `deprecated` is a type to prevent errors when using typing_extensions < 4.9.0 +DEPRECATED_TYPES: tuple[Any, ...] = (deprecated,) if isinstance(deprecated, type) else () +if hasattr(warnings, 'deprecated'): + DEPRECATED_TYPES = (*DEPRECATED_TYPES, warnings.deprecated) # type: ignore + +NONE_TYPES: tuple[Any, ...] = (None, NoneType, *(tp[None] for tp in LITERAL_TYPES)) + + +TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type + + +def is_none_type(type_: Any) -> bool: + return type_ in NONE_TYPES + + +def is_callable_type(type_: type[Any]) -> bool: + return type_ is Callable or get_origin(type_) is Callable + + +def is_literal_type(type_: type[Any]) -> bool: + return Literal is not None and get_origin(type_) in LITERAL_TYPES + + +def is_deprecated_instance(instance: Any) -> TypeGuard[deprecated]: + return isinstance(instance, DEPRECATED_TYPES) + + +def literal_values(type_: type[Any]) -> tuple[Any, ...]: + return get_args(type_) + + +def all_literal_values(type_: type[Any]) -> list[Any]: + """This method is used to retrieve all Literal values as + Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) + e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]`. + """ + if not is_literal_type(type_): + return [type_] + + values = literal_values(type_) + return list(x for value in values for x in all_literal_values(value)) + + +def is_annotated(ann_type: Any) -> bool: + from ._utils import lenient_issubclass + + origin = get_origin(ann_type) + return origin is not None and lenient_issubclass(origin, Annotated) + + +def is_namedtuple(type_: type[Any]) -> bool: + """Check if a given class is a named tuple. + It can be either a `typing.NamedTuple` or `collections.namedtuple`. + """ + from ._utils import lenient_issubclass + + return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') + + +test_new_type = typing.NewType('test_new_type', str) + + +def is_new_type(type_: type[Any]) -> bool: + """Check whether type_ was created using typing.NewType. + + Can't use isinstance because it fails <3.10. + """ + return isinstance(type_, test_new_type.__class__) and hasattr(type_, '__supertype__') # type: ignore[arg-type] + + +def _check_classvar(v: type[Any] | None) -> bool: + if v is None: + return False + + return v.__class__ == typing.ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' + + +def is_classvar(ann_type: type[Any]) -> bool: + if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): + return True + + # this is an ugly workaround for class vars that contain forward references and are therefore themselves + # forward references, see #3679 + if ann_type.__class__ == typing.ForwardRef and re.match( + r'(\w+\.)?ClassVar\[', + ann_type.__forward_arg__, # type: ignore + ): + return True + + return False + + +def _check_finalvar(v: type[Any] | None) -> bool: + """Check if a given type is a `typing.Final` type.""" + if v is None: + return False + + return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') + + +def is_finalvar(ann_type: Any) -> bool: + return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) + + +def parent_frame_namespace(*, parent_depth: int = 2) -> dict[str, Any] | None: + """We allow use of items in parent namespace to get around the issue with `get_type_hints` only looking in the + global module namespace. See https://github.com/pydantic/pydantic/issues/2678#issuecomment-1008139014 -> Scope + and suggestion at the end of the next comment by @gvanrossum. + + WARNING 1: it matters exactly where this is called. By default, this function will build a namespace from the + parent of where it is called. + + WARNING 2: this only looks in the parent namespace, not other parents since (AFAIK) there's no way to collect a + dict of exactly what's in scope. Using `f_back` would work sometimes but would be very wrong and confusing in many + other cases. See https://discuss.python.org/t/is-there-a-way-to-access-parent-nested-namespaces/20659. + """ + frame = sys._getframe(parent_depth) + # if f_back is None, it's the global module namespace and we don't need to include it here + if frame.f_back is None: + return None + else: + return frame.f_locals + + +def add_module_globals(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: + module_name = getattr(obj, '__module__', None) + if module_name: + try: + module_globalns = sys.modules[module_name].__dict__ + except KeyError: + # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 + pass + else: + if globalns: + return {**module_globalns, **globalns} + else: + # copy module globals to make sure it can't be updated later + return module_globalns.copy() + + return globalns or {} + + +def get_cls_types_namespace(cls: type[Any], parent_namespace: dict[str, Any] | None = None) -> dict[str, Any]: + ns = add_module_globals(cls, parent_namespace) + ns[cls.__name__] = cls + return ns + + +def get_cls_type_hints_lenient(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: + """Collect annotations from a class, including those from parent classes. + + Unlike `typing.get_type_hints`, this function will not error if a forward reference is not resolvable. + """ + hints = {} + for base in reversed(obj.__mro__): + ann = base.__dict__.get('__annotations__') + localns = dict(vars(base)) + if ann is not None and ann is not GetSetDescriptorType: + for name, value in ann.items(): + hints[name] = eval_type_lenient(value, globalns, localns) + return hints + + +def eval_type_lenient(value: Any, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None) -> Any: + """Behaves like typing._eval_type, except it won't raise an error if a forward reference can't be resolved.""" + if value is None: + value = NoneType + elif isinstance(value, str): + value = _make_forward_ref(value, is_argument=False, is_class=True) + + try: + return eval_type_backport(value, globalns, localns) + except NameError: + # the point of this function is to be tolerant to this case + return value + + +def eval_type_backport( + value: Any, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None +) -> Any: + """Like `typing._eval_type`, but falls back to the `eval_type_backport` package if it's + installed to let older Python versions use newer typing features. + Specifically, this transforms `X | Y` into `typing.Union[X, Y]` + and `list[X]` into `typing.List[X]` etc. (for all the types made generic in PEP 585) + if the original syntax is not supported in the current Python version. + """ + try: + return typing._eval_type( # type: ignore + value, globalns, localns + ) + except TypeError as e: + if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)): + raise + try: + from eval_type_backport import eval_type_backport + except ImportError: + raise TypeError( + f'You have a type annotation {value.__forward_arg__!r} ' + f'which makes use of newer typing features than are supported in your version of Python. ' + f'To handle this error, you should either remove the use of new syntax ' + f'or install the `eval_type_backport` package.' + ) from e + + return eval_type_backport(value, globalns, localns, try_default=False) + + +def is_backport_fixable_error(e: TypeError) -> bool: + msg = str(e) + return msg.startswith('unsupported operand type(s) for |: ') or "' object is not subscriptable" in msg + + +def get_function_type_hints( + function: Callable[..., Any], *, include_keys: set[str] | None = None, types_namespace: dict[str, Any] | None = None +) -> dict[str, Any]: + """Like `typing.get_type_hints`, but doesn't convert `X` to `Optional[X]` if the default value is `None`, also + copes with `partial`. + """ + try: + if isinstance(function, partial): + annotations = function.func.__annotations__ + else: + annotations = function.__annotations__ + except AttributeError: + type_hints = get_type_hints(function) + if isinstance(function, type): + type_hints.setdefault('return', type) + return type_hints + + globalns = add_module_globals(function) + type_hints = {} + for name, value in annotations.items(): + if include_keys is not None and name not in include_keys: + continue + if value is None: + value = NoneType + elif isinstance(value, str): + value = _make_forward_ref(value) + + type_hints[name] = eval_type_backport(value, globalns, types_namespace) + + return type_hints + + +if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): + + def _make_forward_ref( + arg: Any, + is_argument: bool = True, + *, + is_class: bool = False, + ) -> typing.ForwardRef: + """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions. + The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below. + + See https://github.com/python/cpython/pull/28560 for some background. + The backport happened on 3.9.8, see: + https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458, + and on 3.10.1 for the 3.10 branch, see: + https://github.com/pydantic/pydantic/issues/6912 + + Implemented as EAFP with memory. + """ + return typing.ForwardRef(arg, is_argument) + +else: + _make_forward_ref = typing.ForwardRef + + +if sys.version_info >= (3, 10): + get_type_hints = typing.get_type_hints + +else: + """ + For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to + the implementation in CPython 3.10.8. + """ + + @typing.no_type_check + def get_type_hints( # noqa: C901 + obj: Any, + globalns: dict[str, Any] | None = None, + localns: dict[str, Any] | None = None, + include_extras: bool = False, + ) -> dict[str, Any]: # pragma: no cover + """Taken verbatim from python 3.10.8 unchanged, except: + * type annotations of the function definition above. + * prefixing `typing.` where appropriate + * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument. + + https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875 + + DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY. + ====================================================== + + Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. For classes, the search + order is globals first then locals. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if getattr(obj, '__no_type_check__', None): + return {} + # Classes require a special treatment. + if isinstance(obj, type): + hints = {} + for base in reversed(obj.__mro__): + if globalns is None: + base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {}) + else: + base_globals = globalns + ann = base.__dict__.get('__annotations__', {}) + if isinstance(ann, types.GetSetDescriptorType): + ann = {} + base_locals = dict(vars(base)) if localns is None else localns + if localns is None and globalns is None: + # This is surprising, but required. Before Python 3.10, + # get_type_hints only evaluated the globalns of + # a class. To maintain backwards compatibility, we reverse + # the globalns and localns order so that eval() looks into + # *base_globals* first rather than *base_locals*. + # This only affects ForwardRefs. + base_globals, base_locals = base_locals, base_globals + for name, value in ann.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _make_forward_ref(value, is_argument=False, is_class=True) + + value = eval_type_backport(value, base_globals, base_locals) + hints[name] = value + if not include_extras and hasattr(typing, '_strip_annotations'): + return { + k: typing._strip_annotations(t) # type: ignore + for k, t in hints.items() + } + else: + return hints + + if globalns is None: + if isinstance(obj, types.ModuleType): + globalns = obj.__dict__ + else: + nsobj = obj + # Find globalns for the unwrapped object. + while hasattr(nsobj, '__wrapped__'): + nsobj = nsobj.__wrapped__ + globalns = getattr(nsobj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns + hints = getattr(obj, '__annotations__', None) + if hints is None: + # Return empty annotations for something that _could_ have them. + if isinstance(obj, typing._allowed_types): # type: ignore + return {} + else: + raise TypeError(f'{obj!r} is not a module, class, method, ' 'or function.') + defaults = typing._get_defaults(obj) # type: ignore + hints = dict(hints) + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + # class-level forward refs were handled above, this must be either + # a module-level annotation or a function argument annotation + + value = _make_forward_ref( + value, + is_argument=not isinstance(obj, types.ModuleType), + is_class=False, + ) + value = eval_type_backport(value, globalns, localns) + if name in defaults and defaults[name] is None: + value = typing.Optional[value] + hints[name] = value + return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore + + +def is_dataclass(_cls: type[Any]) -> TypeGuard[type[StandardDataclass]]: + # The dataclasses.is_dataclass function doesn't seem to provide TypeGuard functionality, + # so I created this convenience function + return dataclasses.is_dataclass(_cls) + + +def origin_is_type_alias_type(origin: Any) -> TypeGuard[TypeAliasType]: + return isinstance(origin, TypeAliasType) + + +if sys.version_info >= (3, 10): + + def is_generic_alias(type_: type[Any]) -> bool: + return isinstance(type_, (types.GenericAlias, typing._GenericAlias)) # type: ignore[attr-defined] + +else: + + def is_generic_alias(type_: type[Any]) -> bool: + return isinstance(type_, typing._GenericAlias) # type: ignore + + +def is_self_type(tp: Any) -> bool: + """Check if a given class is a Self type (from `typing` or `typing_extensions`)""" + return isinstance(tp, typing_base) and getattr(tp, '_name', None) == 'Self' diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_utils.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_utils.py new file mode 100644 index 0000000..ef8b32d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_utils.py @@ -0,0 +1,362 @@ +"""Bucket of reusable internal utilities. + +This should be reduced as much as possible with functions only used in one place, moved to that place. +""" +from __future__ import annotations as _annotations + +import dataclasses +import keyword +import typing +import weakref +from collections import OrderedDict, defaultdict, deque +from copy import deepcopy +from itertools import zip_longest +from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType +from typing import Any, Mapping, TypeVar + +from typing_extensions import TypeAlias, TypeGuard + +from . import _repr, _typing_extra + +if typing.TYPE_CHECKING: + MappingIntStrAny: TypeAlias = 'typing.Mapping[int, Any] | typing.Mapping[str, Any]' + AbstractSetIntStr: TypeAlias = 'typing.AbstractSet[int] | typing.AbstractSet[str]' + from ..main import BaseModel + + +# these are types that are returned unchanged by deepcopy +IMMUTABLE_NON_COLLECTIONS_TYPES: set[type[Any]] = { + int, + float, + complex, + str, + bool, + bytes, + type, + _typing_extra.NoneType, + FunctionType, + BuiltinFunctionType, + LambdaType, + weakref.ref, + CodeType, + # note: including ModuleType will differ from behaviour of deepcopy by not producing error. + # It might be not a good idea in general, but considering that this function used only internally + # against default values of fields, this will allow to actually have a field with module as default value + ModuleType, + NotImplemented.__class__, + Ellipsis.__class__, +} + +# these are types that if empty, might be copied with simple copy() instead of deepcopy() +BUILTIN_COLLECTIONS: set[type[Any]] = { + list, + set, + tuple, + frozenset, + dict, + OrderedDict, + defaultdict, + deque, +} + + +def sequence_like(v: Any) -> bool: + return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) + + +def lenient_isinstance(o: Any, class_or_tuple: type[Any] | tuple[type[Any], ...] | None) -> bool: # pragma: no cover + try: + return isinstance(o, class_or_tuple) # type: ignore[arg-type] + except TypeError: + return False + + +def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cover + try: + return isinstance(cls, type) and issubclass(cls, class_or_tuple) + except TypeError: + if isinstance(cls, _typing_extra.WithArgsTypes): + return False + raise # pragma: no cover + + +def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]: + """Returns true if cls is a _proper_ subclass of BaseModel, and provides proper type-checking, + unlike raw calls to lenient_issubclass. + """ + from ..main import BaseModel + + return lenient_issubclass(cls, BaseModel) and cls is not BaseModel + + +def is_valid_identifier(identifier: str) -> bool: + """Checks that a string is a valid identifier and not a Python keyword. + :param identifier: The identifier to test. + :return: True if the identifier is valid. + """ + return identifier.isidentifier() and not keyword.iskeyword(identifier) + + +KeyType = TypeVar('KeyType') + + +def deep_update(mapping: dict[KeyType, Any], *updating_mappings: dict[KeyType, Any]) -> dict[KeyType, Any]: + updated_mapping = mapping.copy() + for updating_mapping in updating_mappings: + for k, v in updating_mapping.items(): + if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict): + updated_mapping[k] = deep_update(updated_mapping[k], v) + else: + updated_mapping[k] = v + return updated_mapping + + +def update_not_none(mapping: dict[Any, Any], **update: Any) -> None: + mapping.update({k: v for k, v in update.items() if v is not None}) + + +T = TypeVar('T') + + +def unique_list( + input_list: list[T] | tuple[T, ...], + *, + name_factory: typing.Callable[[T], str] = str, +) -> list[T]: + """Make a list unique while maintaining order. + We update the list if another one with the same name is set + (e.g. model validator overridden in subclass). + """ + result: list[T] = [] + result_names: list[str] = [] + for v in input_list: + v_name = name_factory(v) + if v_name not in result_names: + result_names.append(v_name) + result.append(v) + else: + result[result_names.index(v_name)] = v + + return result + + +class ValueItems(_repr.Representation): + """Class for more convenient calculation of excluded or included fields on values.""" + + __slots__ = ('_items', '_type') + + def __init__(self, value: Any, items: AbstractSetIntStr | MappingIntStrAny) -> None: + items = self._coerce_items(items) + + if isinstance(value, (list, tuple)): + items = self._normalize_indexes(items, len(value)) # type: ignore + + self._items: MappingIntStrAny = items # type: ignore + + def is_excluded(self, item: Any) -> bool: + """Check if item is fully excluded. + + :param item: key or index of a value + """ + return self.is_true(self._items.get(item)) + + def is_included(self, item: Any) -> bool: + """Check if value is contained in self._items. + + :param item: key or index of value + """ + return item in self._items + + def for_element(self, e: int | str) -> AbstractSetIntStr | MappingIntStrAny | None: + """:param e: key or index of element on value + :return: raw values for element if self._items is dict and contain needed element + """ + item = self._items.get(e) # type: ignore + return item if not self.is_true(item) else None + + def _normalize_indexes(self, items: MappingIntStrAny, v_length: int) -> dict[int | str, Any]: + """:param items: dict or set of indexes which will be normalized + :param v_length: length of sequence indexes of which will be + + >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4) + {0: True, 2: True, 3: True} + >>> self._normalize_indexes({'__all__': True}, 4) + {0: True, 1: True, 2: True, 3: True} + """ + normalized_items: dict[int | str, Any] = {} + all_items = None + for i, v in items.items(): + if not (isinstance(v, typing.Mapping) or isinstance(v, typing.AbstractSet) or self.is_true(v)): + raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}') + if i == '__all__': + all_items = self._coerce_value(v) + continue + if not isinstance(i, int): + raise TypeError( + 'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: ' + 'expected integer keys or keyword "__all__"' + ) + normalized_i = v_length + i if i < 0 else i + normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i)) + + if not all_items: + return normalized_items + if self.is_true(all_items): + for i in range(v_length): + normalized_items.setdefault(i, ...) + return normalized_items + for i in range(v_length): + normalized_item = normalized_items.setdefault(i, {}) + if not self.is_true(normalized_item): + normalized_items[i] = self.merge(all_items, normalized_item) + return normalized_items + + @classmethod + def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any: + """Merge a `base` item with an `override` item. + + Both `base` and `override` are converted to dictionaries if possible. + Sets are converted to dictionaries with the sets entries as keys and + Ellipsis as values. + + Each key-value pair existing in `base` is merged with `override`, + while the rest of the key-value pairs are updated recursively with this function. + + Merging takes place based on the "union" of keys if `intersect` is + set to `False` (default) and on the intersection of keys if + `intersect` is set to `True`. + """ + override = cls._coerce_value(override) + base = cls._coerce_value(base) + if override is None: + return base + if cls.is_true(base) or base is None: + return override + if cls.is_true(override): + return base if intersect else override + + # intersection or union of keys while preserving ordering: + if intersect: + merge_keys = [k for k in base if k in override] + [k for k in override if k in base] + else: + merge_keys = list(base) + [k for k in override if k not in base] + + merged: dict[int | str, Any] = {} + for k in merge_keys: + merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect) + if merged_item is not None: + merged[k] = merged_item + + return merged + + @staticmethod + def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny: + if isinstance(items, typing.Mapping): + pass + elif isinstance(items, typing.AbstractSet): + items = dict.fromkeys(items, ...) # type: ignore + else: + class_name = getattr(items, '__class__', '???') + raise TypeError(f'Unexpected type of exclude value {class_name}') + return items # type: ignore + + @classmethod + def _coerce_value(cls, value: Any) -> Any: + if value is None or cls.is_true(value): + return value + return cls._coerce_items(value) + + @staticmethod + def is_true(v: Any) -> bool: + return v is True or v is ... + + def __repr_args__(self) -> _repr.ReprArgs: + return [(None, self._items)] + + +if typing.TYPE_CHECKING: + + def ClassAttribute(name: str, value: T) -> T: + ... + +else: + + class ClassAttribute: + """Hide class attribute from its instances.""" + + __slots__ = 'name', 'value' + + def __init__(self, name: str, value: Any) -> None: + self.name = name + self.value = value + + def __get__(self, instance: Any, owner: type[Any]) -> None: + if instance is None: + return self.value + raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only') + + +Obj = TypeVar('Obj') + + +def smart_deepcopy(obj: Obj) -> Obj: + """Return type as is for immutable built-in types + Use obj.copy() for built-in empty collections + Use copy.deepcopy() for non-empty collections and unknown objects. + """ + obj_type = obj.__class__ + if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES: + return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway + try: + if not obj and obj_type in BUILTIN_COLLECTIONS: + # faster way for empty collections, no need to copy its members + return obj if obj_type is tuple else obj.copy() # tuple doesn't have copy method # type: ignore + except (TypeError, ValueError, RuntimeError): + # do we really dare to catch ALL errors? Seems a bit risky + pass + + return deepcopy(obj) # slowest way when we actually might need a deepcopy + + +_SENTINEL = object() + + +def all_identical(left: typing.Iterable[Any], right: typing.Iterable[Any]) -> bool: + """Check that the items of `left` are the same objects as those in `right`. + + >>> a, b = object(), object() + >>> all_identical([a, b, a], [a, b, a]) + True + >>> all_identical([a, b, [a]], [a, b, [a]]) # new list object, while "equal" is not "identical" + False + """ + for left_item, right_item in zip_longest(left, right, fillvalue=_SENTINEL): + if left_item is not right_item: + return False + return True + + +@dataclasses.dataclass(frozen=True) +class SafeGetItemProxy: + """Wrapper redirecting `__getitem__` to `get` with a sentinel value as default + + This makes is safe to use in `operator.itemgetter` when some keys may be missing + """ + + # Define __slots__manually for performances + # @dataclasses.dataclass() only support slots=True in python>=3.10 + __slots__ = ('wrapped',) + + wrapped: Mapping[str, Any] + + def __getitem__(self, key: str, /) -> Any: + return self.wrapped.get(key, _SENTINEL) + + # required to pass the object to operator.itemgetter() instances due to a quirk of typeshed + # https://github.com/python/mypy/issues/13713 + # https://github.com/python/typeshed/pull/8785 + # Since this is typing-only, hide it in a typing.TYPE_CHECKING block + if typing.TYPE_CHECKING: + + def __contains__(self, key: str, /) -> bool: + return self.wrapped.__contains__(key) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validate_call.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validate_call.py new file mode 100644 index 0000000..664c063 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validate_call.py @@ -0,0 +1,84 @@ +from __future__ import annotations as _annotations + +import inspect +from functools import partial +from typing import Any, Awaitable, Callable + +import pydantic_core + +from ..config import ConfigDict +from ..plugin._schema_validator import create_schema_validator +from . import _generate_schema, _typing_extra +from ._config import ConfigWrapper + + +class ValidateCallWrapper: + """This is a wrapper around a function that validates the arguments passed to it, and optionally the return value.""" + + __slots__ = ( + '__pydantic_validator__', + '__name__', + '__qualname__', + '__annotations__', + '__dict__', # required for __module__ + ) + + def __init__(self, function: Callable[..., Any], config: ConfigDict | None, validate_return: bool): + if isinstance(function, partial): + func = function.func + schema_type = func + self.__name__ = f'partial({func.__name__})' + self.__qualname__ = f'partial({func.__qualname__})' + self.__module__ = func.__module__ + else: + schema_type = function + self.__name__ = function.__name__ + self.__qualname__ = function.__qualname__ + self.__module__ = function.__module__ + + namespace = _typing_extra.add_module_globals(function, None) + config_wrapper = ConfigWrapper(config) + gen_schema = _generate_schema.GenerateSchema(config_wrapper, namespace) + schema = gen_schema.clean_schema(gen_schema.generate_schema(function)) + core_config = config_wrapper.core_config(self) + + self.__pydantic_validator__ = create_schema_validator( + schema, + schema_type, + self.__module__, + self.__qualname__, + 'validate_call', + core_config, + config_wrapper.plugin_settings, + ) + + if validate_return: + signature = inspect.signature(function) + return_type = signature.return_annotation if signature.return_annotation is not signature.empty else Any + gen_schema = _generate_schema.GenerateSchema(config_wrapper, namespace) + schema = gen_schema.clean_schema(gen_schema.generate_schema(return_type)) + validator = create_schema_validator( + schema, + schema_type, + self.__module__, + self.__qualname__, + 'validate_call', + core_config, + config_wrapper.plugin_settings, + ) + if inspect.iscoroutinefunction(function): + + async def return_val_wrapper(aw: Awaitable[Any]) -> None: + return validator.validate_python(await aw) + + self.__return_pydantic_validator__ = return_val_wrapper + else: + self.__return_pydantic_validator__ = validator.validate_python + else: + self.__return_pydantic_validator__ = None + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs)) + if self.__return_pydantic_validator__: + return self.__return_pydantic_validator__(res) + return res diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validators.py new file mode 100644 index 0000000..6eb9953 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_internal/_validators.py @@ -0,0 +1,289 @@ +"""Validator functions for standard library types. + +Import of this module is deferred since it contains imports of many standard library modules. +""" + +from __future__ import annotations as _annotations + +import math +import re +import typing +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from typing import Any + +from pydantic_core import PydanticCustomError, core_schema +from pydantic_core._pydantic_core import PydanticKnownError + + +def sequence_validator( + input_value: typing.Sequence[Any], + /, + validator: core_schema.ValidatorFunctionWrapHandler, +) -> typing.Sequence[Any]: + """Validator for `Sequence` types, isinstance(v, Sequence) has already been called.""" + value_type = type(input_value) + + # We don't accept any plain string as a sequence + # Relevant issue: https://github.com/pydantic/pydantic/issues/5595 + if issubclass(value_type, (str, bytes)): + raise PydanticCustomError( + 'sequence_str', + "'{type_name}' instances are not allowed as a Sequence value", + {'type_name': value_type.__name__}, + ) + + # TODO: refactor sequence validation to validate with either a list or a tuple + # schema, depending on the type of the value. + # Additionally, we should be able to remove one of either this validator or the + # SequenceValidator in _std_types_schema.py (preferably this one, while porting over some logic). + # Effectively, a refactor for sequence validation is needed. + if value_type == tuple: + input_value = list(input_value) + + v_list = validator(input_value) + + # the rest of the logic is just re-creating the original type from `v_list` + if value_type == list: + return v_list + elif issubclass(value_type, range): + # return the list as we probably can't re-create the range + return v_list + elif value_type == tuple: + return tuple(v_list) + else: + # best guess at how to re-create the original type, more custom construction logic might be required + return value_type(v_list) # type: ignore[call-arg] + + +def import_string(value: Any) -> Any: + if isinstance(value, str): + try: + return _import_string_logic(value) + except ImportError as e: + raise PydanticCustomError('import_error', 'Invalid python path: {error}', {'error': str(e)}) from e + else: + # otherwise we just return the value and let the next validator do the rest of the work + return value + + +def _import_string_logic(dotted_path: str) -> Any: + """Inspired by uvicorn — dotted paths should include a colon before the final item if that item is not a module. + (This is necessary to distinguish between a submodule and an attribute when there is a conflict.). + + If the dotted path does not include a colon and the final item is not a valid module, importing as an attribute + rather than a submodule will be attempted automatically. + + So, for example, the following values of `dotted_path` result in the following returned values: + * 'collections': + * 'collections.abc': + * 'collections.abc:Mapping': + * `collections.abc.Mapping`: (though this is a bit slower than the previous line) + + An error will be raised under any of the following scenarios: + * `dotted_path` contains more than one colon (e.g., 'collections:abc:Mapping') + * the substring of `dotted_path` before the colon is not a valid module in the environment (e.g., '123:Mapping') + * the substring of `dotted_path` after the colon is not an attribute of the module (e.g., 'collections:abc123') + """ + from importlib import import_module + + components = dotted_path.strip().split(':') + if len(components) > 2: + raise ImportError(f"Import strings should have at most one ':'; received {dotted_path!r}") + + module_path = components[0] + if not module_path: + raise ImportError(f'Import strings should have a nonempty module name; received {dotted_path!r}') + + try: + module = import_module(module_path) + except ModuleNotFoundError as e: + if '.' in module_path: + # Check if it would be valid if the final item was separated from its module with a `:` + maybe_module_path, maybe_attribute = dotted_path.strip().rsplit('.', 1) + try: + return _import_string_logic(f'{maybe_module_path}:{maybe_attribute}') + except ImportError: + pass + raise ImportError(f'No module named {module_path!r}') from e + raise e + + if len(components) > 1: + attribute = components[1] + try: + return getattr(module, attribute) + except AttributeError as e: + raise ImportError(f'cannot import name {attribute!r} from {module_path!r}') from e + else: + return module + + +def pattern_either_validator(input_value: Any, /) -> typing.Pattern[Any]: + if isinstance(input_value, typing.Pattern): + return input_value + elif isinstance(input_value, (str, bytes)): + # todo strict mode + return compile_pattern(input_value) # type: ignore + else: + raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') + + +def pattern_str_validator(input_value: Any, /) -> typing.Pattern[str]: + if isinstance(input_value, typing.Pattern): + if isinstance(input_value.pattern, str): + return input_value + else: + raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') + elif isinstance(input_value, str): + return compile_pattern(input_value) + elif isinstance(input_value, bytes): + raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern') + else: + raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') + + +def pattern_bytes_validator(input_value: Any, /) -> typing.Pattern[bytes]: + if isinstance(input_value, typing.Pattern): + if isinstance(input_value.pattern, bytes): + return input_value + else: + raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') + elif isinstance(input_value, bytes): + return compile_pattern(input_value) + elif isinstance(input_value, str): + raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern') + else: + raise PydanticCustomError('pattern_type', 'Input should be a valid pattern') + + +PatternType = typing.TypeVar('PatternType', str, bytes) + + +def compile_pattern(pattern: PatternType) -> typing.Pattern[PatternType]: + try: + return re.compile(pattern) + except re.error: + raise PydanticCustomError('pattern_regex', 'Input should be a valid regular expression') + + +def ip_v4_address_validator(input_value: Any, /) -> IPv4Address: + if isinstance(input_value, IPv4Address): + return input_value + + try: + return IPv4Address(input_value) + except ValueError: + raise PydanticCustomError('ip_v4_address', 'Input is not a valid IPv4 address') + + +def ip_v6_address_validator(input_value: Any, /) -> IPv6Address: + if isinstance(input_value, IPv6Address): + return input_value + + try: + return IPv6Address(input_value) + except ValueError: + raise PydanticCustomError('ip_v6_address', 'Input is not a valid IPv6 address') + + +def ip_v4_network_validator(input_value: Any, /) -> IPv4Network: + """Assume IPv4Network initialised with a default `strict` argument. + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network + """ + if isinstance(input_value, IPv4Network): + return input_value + + try: + return IPv4Network(input_value) + except ValueError: + raise PydanticCustomError('ip_v4_network', 'Input is not a valid IPv4 network') + + +def ip_v6_network_validator(input_value: Any, /) -> IPv6Network: + """Assume IPv6Network initialised with a default `strict` argument. + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network + """ + if isinstance(input_value, IPv6Network): + return input_value + + try: + return IPv6Network(input_value) + except ValueError: + raise PydanticCustomError('ip_v6_network', 'Input is not a valid IPv6 network') + + +def ip_v4_interface_validator(input_value: Any, /) -> IPv4Interface: + if isinstance(input_value, IPv4Interface): + return input_value + + try: + return IPv4Interface(input_value) + except ValueError: + raise PydanticCustomError('ip_v4_interface', 'Input is not a valid IPv4 interface') + + +def ip_v6_interface_validator(input_value: Any, /) -> IPv6Interface: + if isinstance(input_value, IPv6Interface): + return input_value + + try: + return IPv6Interface(input_value) + except ValueError: + raise PydanticCustomError('ip_v6_interface', 'Input is not a valid IPv6 interface') + + +def greater_than_validator(x: Any, gt: Any) -> Any: + if not (x > gt): + raise PydanticKnownError('greater_than', {'gt': gt}) + return x + + +def greater_than_or_equal_validator(x: Any, ge: Any) -> Any: + if not (x >= ge): + raise PydanticKnownError('greater_than_equal', {'ge': ge}) + return x + + +def less_than_validator(x: Any, lt: Any) -> Any: + if not (x < lt): + raise PydanticKnownError('less_than', {'lt': lt}) + return x + + +def less_than_or_equal_validator(x: Any, le: Any) -> Any: + if not (x <= le): + raise PydanticKnownError('less_than_equal', {'le': le}) + return x + + +def multiple_of_validator(x: Any, multiple_of: Any) -> Any: + if not (x % multiple_of == 0): + raise PydanticKnownError('multiple_of', {'multiple_of': multiple_of}) + return x + + +def min_length_validator(x: Any, min_length: Any) -> Any: + if not (len(x) >= min_length): + raise PydanticKnownError( + 'too_short', + {'field_type': 'Value', 'min_length': min_length, 'actual_length': len(x)}, + ) + return x + + +def max_length_validator(x: Any, max_length: Any) -> Any: + if len(x) > max_length: + raise PydanticKnownError( + 'too_long', + {'field_type': 'Value', 'max_length': max_length, 'actual_length': len(x)}, + ) + return x + + +def forbid_inf_nan_check(x: Any) -> Any: + if not math.isfinite(x): + raise PydanticKnownError('finite_number') + return x diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/_migration.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/_migration.py new file mode 100644 index 0000000..c8478a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/_migration.py @@ -0,0 +1,308 @@ +import sys +from typing import Any, Callable, Dict + +from .version import version_short + +MOVED_IN_V2 = { + 'pydantic.utils:version_info': 'pydantic.version:version_info', + 'pydantic.error_wrappers:ValidationError': 'pydantic:ValidationError', + 'pydantic.utils:to_camel': 'pydantic.alias_generators:to_pascal', + 'pydantic.utils:to_lower_camel': 'pydantic.alias_generators:to_camel', + 'pydantic:PyObject': 'pydantic.types:ImportString', + 'pydantic.types:PyObject': 'pydantic.types:ImportString', + 'pydantic.generics:GenericModel': 'pydantic.BaseModel', +} + +DEPRECATED_MOVED_IN_V2 = { + 'pydantic.tools:schema_of': 'pydantic.deprecated.tools:schema_of', + 'pydantic.tools:parse_obj_as': 'pydantic.deprecated.tools:parse_obj_as', + 'pydantic.tools:schema_json_of': 'pydantic.deprecated.tools:schema_json_of', + 'pydantic.json:pydantic_encoder': 'pydantic.deprecated.json:pydantic_encoder', + 'pydantic:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments', + 'pydantic.json:custom_pydantic_encoder': 'pydantic.deprecated.json:custom_pydantic_encoder', + 'pydantic.json:timedelta_isoformat': 'pydantic.deprecated.json:timedelta_isoformat', + 'pydantic.decorator:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments', + 'pydantic.class_validators:validator': 'pydantic.deprecated.class_validators:validator', + 'pydantic.class_validators:root_validator': 'pydantic.deprecated.class_validators:root_validator', + 'pydantic.config:BaseConfig': 'pydantic.deprecated.config:BaseConfig', + 'pydantic.config:Extra': 'pydantic.deprecated.config:Extra', +} + +REDIRECT_TO_V1 = { + f'pydantic.utils:{obj}': f'pydantic.v1.utils:{obj}' + for obj in ( + 'deep_update', + 'GetterDict', + 'lenient_issubclass', + 'lenient_isinstance', + 'is_valid_field', + 'update_not_none', + 'import_string', + 'Representation', + 'ROOT_KEY', + 'smart_deepcopy', + 'sequence_like', + ) +} + + +REMOVED_IN_V2 = { + 'pydantic:ConstrainedBytes', + 'pydantic:ConstrainedDate', + 'pydantic:ConstrainedDecimal', + 'pydantic:ConstrainedFloat', + 'pydantic:ConstrainedFrozenSet', + 'pydantic:ConstrainedInt', + 'pydantic:ConstrainedList', + 'pydantic:ConstrainedSet', + 'pydantic:ConstrainedStr', + 'pydantic:JsonWrapper', + 'pydantic:NoneBytes', + 'pydantic:NoneStr', + 'pydantic:NoneStrBytes', + 'pydantic:Protocol', + 'pydantic:Required', + 'pydantic:StrBytes', + 'pydantic:compiled', + 'pydantic.config:get_config', + 'pydantic.config:inherit_config', + 'pydantic.config:prepare_config', + 'pydantic:create_model_from_namedtuple', + 'pydantic:create_model_from_typeddict', + 'pydantic.dataclasses:create_pydantic_model_from_dataclass', + 'pydantic.dataclasses:make_dataclass_validator', + 'pydantic.dataclasses:set_validation', + 'pydantic.datetime_parse:parse_date', + 'pydantic.datetime_parse:parse_time', + 'pydantic.datetime_parse:parse_datetime', + 'pydantic.datetime_parse:parse_duration', + 'pydantic.error_wrappers:ErrorWrapper', + 'pydantic.errors:AnyStrMaxLengthError', + 'pydantic.errors:AnyStrMinLengthError', + 'pydantic.errors:ArbitraryTypeError', + 'pydantic.errors:BoolError', + 'pydantic.errors:BytesError', + 'pydantic.errors:CallableError', + 'pydantic.errors:ClassError', + 'pydantic.errors:ColorError', + 'pydantic.errors:ConfigError', + 'pydantic.errors:DataclassTypeError', + 'pydantic.errors:DateError', + 'pydantic.errors:DateNotInTheFutureError', + 'pydantic.errors:DateNotInThePastError', + 'pydantic.errors:DateTimeError', + 'pydantic.errors:DecimalError', + 'pydantic.errors:DecimalIsNotFiniteError', + 'pydantic.errors:DecimalMaxDigitsError', + 'pydantic.errors:DecimalMaxPlacesError', + 'pydantic.errors:DecimalWholeDigitsError', + 'pydantic.errors:DictError', + 'pydantic.errors:DurationError', + 'pydantic.errors:EmailError', + 'pydantic.errors:EnumError', + 'pydantic.errors:EnumMemberError', + 'pydantic.errors:ExtraError', + 'pydantic.errors:FloatError', + 'pydantic.errors:FrozenSetError', + 'pydantic.errors:FrozenSetMaxLengthError', + 'pydantic.errors:FrozenSetMinLengthError', + 'pydantic.errors:HashableError', + 'pydantic.errors:IPv4AddressError', + 'pydantic.errors:IPv4InterfaceError', + 'pydantic.errors:IPv4NetworkError', + 'pydantic.errors:IPv6AddressError', + 'pydantic.errors:IPv6InterfaceError', + 'pydantic.errors:IPv6NetworkError', + 'pydantic.errors:IPvAnyAddressError', + 'pydantic.errors:IPvAnyInterfaceError', + 'pydantic.errors:IPvAnyNetworkError', + 'pydantic.errors:IntEnumError', + 'pydantic.errors:IntegerError', + 'pydantic.errors:InvalidByteSize', + 'pydantic.errors:InvalidByteSizeUnit', + 'pydantic.errors:InvalidDiscriminator', + 'pydantic.errors:InvalidLengthForBrand', + 'pydantic.errors:JsonError', + 'pydantic.errors:JsonTypeError', + 'pydantic.errors:ListError', + 'pydantic.errors:ListMaxLengthError', + 'pydantic.errors:ListMinLengthError', + 'pydantic.errors:ListUniqueItemsError', + 'pydantic.errors:LuhnValidationError', + 'pydantic.errors:MissingDiscriminator', + 'pydantic.errors:MissingError', + 'pydantic.errors:NoneIsAllowedError', + 'pydantic.errors:NoneIsNotAllowedError', + 'pydantic.errors:NotDigitError', + 'pydantic.errors:NotNoneError', + 'pydantic.errors:NumberNotGeError', + 'pydantic.errors:NumberNotGtError', + 'pydantic.errors:NumberNotLeError', + 'pydantic.errors:NumberNotLtError', + 'pydantic.errors:NumberNotMultipleError', + 'pydantic.errors:PathError', + 'pydantic.errors:PathNotADirectoryError', + 'pydantic.errors:PathNotAFileError', + 'pydantic.errors:PathNotExistsError', + 'pydantic.errors:PatternError', + 'pydantic.errors:PyObjectError', + 'pydantic.errors:PydanticTypeError', + 'pydantic.errors:PydanticValueError', + 'pydantic.errors:SequenceError', + 'pydantic.errors:SetError', + 'pydantic.errors:SetMaxLengthError', + 'pydantic.errors:SetMinLengthError', + 'pydantic.errors:StrError', + 'pydantic.errors:StrRegexError', + 'pydantic.errors:StrictBoolError', + 'pydantic.errors:SubclassError', + 'pydantic.errors:TimeError', + 'pydantic.errors:TupleError', + 'pydantic.errors:TupleLengthError', + 'pydantic.errors:UUIDError', + 'pydantic.errors:UUIDVersionError', + 'pydantic.errors:UrlError', + 'pydantic.errors:UrlExtraError', + 'pydantic.errors:UrlHostError', + 'pydantic.errors:UrlHostTldError', + 'pydantic.errors:UrlPortError', + 'pydantic.errors:UrlSchemeError', + 'pydantic.errors:UrlSchemePermittedError', + 'pydantic.errors:UrlUserInfoError', + 'pydantic.errors:WrongConstantError', + 'pydantic.main:validate_model', + 'pydantic.networks:stricturl', + 'pydantic:parse_file_as', + 'pydantic:parse_raw_as', + 'pydantic:stricturl', + 'pydantic.tools:parse_file_as', + 'pydantic.tools:parse_raw_as', + 'pydantic.types:ConstrainedBytes', + 'pydantic.types:ConstrainedDate', + 'pydantic.types:ConstrainedDecimal', + 'pydantic.types:ConstrainedFloat', + 'pydantic.types:ConstrainedFrozenSet', + 'pydantic.types:ConstrainedInt', + 'pydantic.types:ConstrainedList', + 'pydantic.types:ConstrainedSet', + 'pydantic.types:ConstrainedStr', + 'pydantic.types:JsonWrapper', + 'pydantic.types:NoneBytes', + 'pydantic.types:NoneStr', + 'pydantic.types:NoneStrBytes', + 'pydantic.types:StrBytes', + 'pydantic.typing:evaluate_forwardref', + 'pydantic.typing:AbstractSetIntStr', + 'pydantic.typing:AnyCallable', + 'pydantic.typing:AnyClassMethod', + 'pydantic.typing:CallableGenerator', + 'pydantic.typing:DictAny', + 'pydantic.typing:DictIntStrAny', + 'pydantic.typing:DictStrAny', + 'pydantic.typing:IntStr', + 'pydantic.typing:ListStr', + 'pydantic.typing:MappingIntStrAny', + 'pydantic.typing:NoArgAnyCallable', + 'pydantic.typing:NoneType', + 'pydantic.typing:ReprArgs', + 'pydantic.typing:SetStr', + 'pydantic.typing:StrPath', + 'pydantic.typing:TupleGenerator', + 'pydantic.typing:WithArgsTypes', + 'pydantic.typing:all_literal_values', + 'pydantic.typing:display_as_type', + 'pydantic.typing:get_all_type_hints', + 'pydantic.typing:get_args', + 'pydantic.typing:get_origin', + 'pydantic.typing:get_sub_types', + 'pydantic.typing:is_callable_type', + 'pydantic.typing:is_classvar', + 'pydantic.typing:is_finalvar', + 'pydantic.typing:is_literal_type', + 'pydantic.typing:is_namedtuple', + 'pydantic.typing:is_new_type', + 'pydantic.typing:is_none_type', + 'pydantic.typing:is_typeddict', + 'pydantic.typing:is_typeddict_special', + 'pydantic.typing:is_union', + 'pydantic.typing:new_type_supertype', + 'pydantic.typing:resolve_annotations', + 'pydantic.typing:typing_base', + 'pydantic.typing:update_field_forward_refs', + 'pydantic.typing:update_model_forward_refs', + 'pydantic.utils:ClassAttribute', + 'pydantic.utils:DUNDER_ATTRIBUTES', + 'pydantic.utils:PyObjectStr', + 'pydantic.utils:ValueItems', + 'pydantic.utils:almost_equal_floats', + 'pydantic.utils:get_discriminator_alias_and_values', + 'pydantic.utils:get_model', + 'pydantic.utils:get_unique_discriminator_alias', + 'pydantic.utils:in_ipython', + 'pydantic.utils:is_valid_identifier', + 'pydantic.utils:path_type', + 'pydantic.utils:validate_field_name', + 'pydantic:validate_model', +} + + +def getattr_migration(module: str) -> Callable[[str], Any]: + """Implement PEP 562 for objects that were either moved or removed on the migration + to V2. + + Args: + module: The module name. + + Returns: + A callable that will raise an error if the object is not found. + """ + # This avoids circular import with errors.py. + from .errors import PydanticImportError + + def wrapper(name: str) -> object: + """Raise an error if the object is not found, or warn if it was moved. + + In case it was moved, it still returns the object. + + Args: + name: The object name. + + Returns: + The object. + """ + if name == '__path__': + raise AttributeError(f'module {module!r} has no attribute {name!r}') + + import warnings + + from ._internal._validators import import_string + + import_path = f'{module}:{name}' + if import_path in MOVED_IN_V2.keys(): + new_location = MOVED_IN_V2[import_path] + warnings.warn(f'`{import_path}` has been moved to `{new_location}`.') + return import_string(MOVED_IN_V2[import_path]) + if import_path in DEPRECATED_MOVED_IN_V2: + # skip the warning here because a deprecation warning will be raised elsewhere + return import_string(DEPRECATED_MOVED_IN_V2[import_path]) + if import_path in REDIRECT_TO_V1: + new_location = REDIRECT_TO_V1[import_path] + warnings.warn( + f'`{import_path}` has been removed. We are importing from `{new_location}` instead.' + 'See the migration guide for more details: https://docs.pydantic.dev/latest/migration/' + ) + return import_string(REDIRECT_TO_V1[import_path]) + if import_path == 'pydantic:BaseSettings': + raise PydanticImportError( + '`BaseSettings` has been moved to the `pydantic-settings` package. ' + f'See https://docs.pydantic.dev/{version_short()}/migration/#basesettings-has-moved-to-pydantic-settings ' + 'for more details.' + ) + if import_path in REMOVED_IN_V2: + raise PydanticImportError(f'`{import_path}` has been removed in V2.') + globals: Dict[str, Any] = sys.modules[module].__dict__ + if name in globals: + return globals[name] + raise AttributeError(f'module {module!r} has no attribute {name!r}') + + return wrapper diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/alias_generators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/alias_generators.py new file mode 100644 index 0000000..155e66e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/alias_generators.py @@ -0,0 +1,50 @@ +"""Alias generators for converting between different capitalization conventions.""" +import re + +__all__ = ('to_pascal', 'to_camel', 'to_snake') + + +def to_pascal(snake: str) -> str: + """Convert a snake_case string to PascalCase. + + Args: + snake: The string to convert. + + Returns: + The PascalCase string. + """ + camel = snake.title() + return re.sub('([0-9A-Za-z])_(?=[0-9A-Z])', lambda m: m.group(1), camel) + + +def to_camel(snake: str) -> str: + """Convert a snake_case string to camelCase. + + Args: + snake: The string to convert. + + Returns: + The converted camelCase string. + """ + camel = to_pascal(snake) + return re.sub('(^_*[A-Z])', lambda m: m.group(1).lower(), camel) + + +def to_snake(camel: str) -> str: + """Convert a PascalCase or camelCase string to snake_case. + + Args: + camel: The string to convert. + + Returns: + The converted string in snake_case. + """ + # Handle the sequence of uppercase letters followed by a lowercase letter + snake = re.sub(r'([A-Z]+)([A-Z][a-z])', lambda m: f'{m.group(1)}_{m.group(2)}', camel) + # Insert an underscore between a lowercase letter and an uppercase letter + snake = re.sub(r'([a-z])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + # Insert an underscore between a digit and an uppercase letter + snake = re.sub(r'([0-9])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + # Insert an underscore between a lowercase letter and a digit + snake = re.sub(r'([a-z])([0-9])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + return snake.lower() diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/aliases.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/aliases.py new file mode 100644 index 0000000..a4659be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/aliases.py @@ -0,0 +1,131 @@ +"""Support for alias configurations.""" +from __future__ import annotations + +import dataclasses +from typing import Any, Callable, Literal + +from pydantic_core import PydanticUndefined + +from ._internal import _internal_dataclass + +__all__ = ('AliasGenerator', 'AliasPath', 'AliasChoices') + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasPath: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/alias#aliaspath-and-aliaschoices + + A data class used by `validation_alias` as a convenience to create aliases. + + Attributes: + path: A list of string or integer aliases. + """ + + path: list[int | str] + + def __init__(self, first_arg: str, *args: str | int) -> None: + self.path = [first_arg] + list(args) + + def convert_to_aliases(self) -> list[str | int]: + """Converts arguments to a list of string or integer aliases. + + Returns: + The list of aliases. + """ + return self.path + + def search_dict_for_path(self, d: dict) -> Any: + """Searches a dictionary for the path specified by the alias. + + Returns: + The value at the specified path, or `PydanticUndefined` if the path is not found. + """ + v = d + for k in self.path: + if isinstance(v, str): + # disallow indexing into a str, like for AliasPath('x', 0) and x='abc' + return PydanticUndefined + try: + v = v[k] + except (KeyError, IndexError, TypeError): + return PydanticUndefined + return v + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasChoices: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/alias#aliaspath-and-aliaschoices + + A data class used by `validation_alias` as a convenience to create aliases. + + Attributes: + choices: A list containing a string or `AliasPath`. + """ + + choices: list[str | AliasPath] + + def __init__(self, first_choice: str | AliasPath, *choices: str | AliasPath) -> None: + self.choices = [first_choice] + list(choices) + + def convert_to_aliases(self) -> list[list[str | int]]: + """Converts arguments to a list of lists containing string or integer aliases. + + Returns: + The list of aliases. + """ + aliases: list[list[str | int]] = [] + for c in self.choices: + if isinstance(c, AliasPath): + aliases.append(c.convert_to_aliases()) + else: + aliases.append([c]) + return aliases + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasGenerator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/alias#using-an-aliasgenerator + + A data class used by `alias_generator` as a convenience to create various aliases. + + Attributes: + alias: A callable that takes a field name and returns an alias for it. + validation_alias: A callable that takes a field name and returns a validation alias for it. + serialization_alias: A callable that takes a field name and returns a serialization alias for it. + """ + + alias: Callable[[str], str] | None = None + validation_alias: Callable[[str], str | AliasPath | AliasChoices] | None = None + serialization_alias: Callable[[str], str] | None = None + + def _generate_alias( + self, + alias_kind: Literal['alias', 'validation_alias', 'serialization_alias'], + allowed_types: tuple[type[str] | type[AliasPath] | type[AliasChoices], ...], + field_name: str, + ) -> str | AliasPath | AliasChoices | None: + """Generate an alias of the specified kind. Returns None if the alias generator is None. + + Raises: + TypeError: If the alias generator produces an invalid type. + """ + alias = None + if alias_generator := getattr(self, alias_kind): + alias = alias_generator(field_name) + if alias and not isinstance(alias, allowed_types): + raise TypeError( + f'Invalid `{alias_kind}` type. `{alias_kind}` generator must produce one of `{allowed_types}`' + ) + return alias + + def generate_aliases(self, field_name: str) -> tuple[str | None, str | AliasPath | AliasChoices | None, str | None]: + """Generate `alias`, `validation_alias`, and `serialization_alias` for a field. + + Returns: + A tuple of three aliases - validation, alias, and serialization. + """ + alias = self._generate_alias('alias', (str,), field_name) + validation_alias = self._generate_alias('validation_alias', (str, AliasChoices, AliasPath), field_name) + serialization_alias = self._generate_alias('serialization_alias', (str,), field_name) + + return alias, validation_alias, serialization_alias # type: ignore diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/annotated_handlers.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/annotated_handlers.py new file mode 100644 index 0000000..b1a4487 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/annotated_handlers.py @@ -0,0 +1,120 @@ +"""Type annotations to use with `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__`.""" +from __future__ import annotations as _annotations + +from typing import TYPE_CHECKING, Any, Union + +from pydantic_core import core_schema + +if TYPE_CHECKING: + from .json_schema import JsonSchemaMode, JsonSchemaValue + + CoreSchemaOrField = Union[ + core_schema.CoreSchema, + core_schema.ModelField, + core_schema.DataclassField, + core_schema.TypedDictField, + core_schema.ComputedField, + ] + +__all__ = 'GetJsonSchemaHandler', 'GetCoreSchemaHandler' + + +class GetJsonSchemaHandler: + """Handler to call into the next JSON schema generation function. + + Attributes: + mode: Json schema mode, can be `validation` or `serialization`. + """ + + mode: JsonSchemaMode + + def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue: + """Call the inner handler and get the JsonSchemaValue it returns. + This will call the next JSON schema modifying function up until it calls + into `pydantic.json_schema.GenerateJsonSchema`, which will raise a + `pydantic.errors.PydanticInvalidForJsonSchema` error if it cannot generate + a JSON schema. + + Args: + core_schema: A `pydantic_core.core_schema.CoreSchema`. + + Returns: + JsonSchemaValue: The JSON schema generated by the inner JSON schema modify + functions. + """ + raise NotImplementedError + + def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue, /) -> JsonSchemaValue: + """Get the real schema for a `{"$ref": ...}` schema. + If the schema given is not a `$ref` schema, it will be returned as is. + This means you don't have to check before calling this function. + + Args: + maybe_ref_json_schema: A JsonSchemaValue which may be a `$ref` schema. + + Raises: + LookupError: If the ref is not found. + + Returns: + JsonSchemaValue: A JsonSchemaValue that has no `$ref`. + """ + raise NotImplementedError + + +class GetCoreSchemaHandler: + """Handler to call into the next CoreSchema schema generation function.""" + + def __call__(self, source_type: Any, /) -> core_schema.CoreSchema: + """Call the inner handler and get the CoreSchema it returns. + This will call the next CoreSchema modifying function up until it calls + into Pydantic's internal schema generation machinery, which will raise a + `pydantic.errors.PydanticSchemaGenerationError` error if it cannot generate + a CoreSchema for the given source type. + + Args: + source_type: The input type. + + Returns: + CoreSchema: The `pydantic-core` CoreSchema generated. + """ + raise NotImplementedError + + def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema: + """Generate a schema unrelated to the current context. + Use this function if e.g. you are handling schema generation for a sequence + and want to generate a schema for its items. + Otherwise, you may end up doing something like applying a `min_length` constraint + that was intended for the sequence itself to its items! + + Args: + source_type: The input type. + + Returns: + CoreSchema: The `pydantic-core` CoreSchema generated. + """ + raise NotImplementedError + + def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema, /) -> core_schema.CoreSchema: + """Get the real schema for a `definition-ref` schema. + If the schema given is not a `definition-ref` schema, it will be returned as is. + This means you don't have to check before calling this function. + + Args: + maybe_ref_schema: A `CoreSchema`, `ref`-based or not. + + Raises: + LookupError: If the `ref` is not found. + + Returns: + A concrete `CoreSchema`. + """ + raise NotImplementedError + + @property + def field_name(self) -> str | None: + """Get the name of the closest field to this validator.""" + raise NotImplementedError + + def _get_types_namespace(self) -> dict[str, Any] | None: + """Internal method used during type resolution for serializer annotations.""" + raise NotImplementedError diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/class_validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/class_validators.py new file mode 100644 index 0000000..2ff72ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/class_validators.py @@ -0,0 +1,4 @@ +"""`class_validators` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/color.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/color.py new file mode 100644 index 0000000..108bb8f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/color.py @@ -0,0 +1,603 @@ +"""Color definitions are used as per the CSS3 +[CSS Color Module Level 3](http://www.w3.org/TR/css3-color/#svg-color) specification. + +A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`. + +In these cases the _last_ color when sorted alphabetically takes preferences, +eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua". + +Warning: Deprecated + The `Color` class is deprecated, use `pydantic_extra_types` instead. + See [`pydantic-extra-types.Color`](../usage/types/extra_types/color_types.md) + for more information. +""" +import math +import re +from colorsys import hls_to_rgb, rgb_to_hls +from typing import Any, Callable, Optional, Tuple, Type, Union, cast + +from pydantic_core import CoreSchema, PydanticCustomError, core_schema +from typing_extensions import deprecated + +from ._internal import _repr +from ._internal._schema_generation_shared import GetJsonSchemaHandler as _GetJsonSchemaHandler +from .json_schema import JsonSchemaValue +from .warnings import PydanticDeprecatedSince20 + +ColorTuple = Union[Tuple[int, int, int], Tuple[int, int, int, float]] +ColorType = Union[ColorTuple, str] +HslColorTuple = Union[Tuple[float, float, float], Tuple[float, float, float, float]] + + +class RGBA: + """Internal use only as a representation of a color.""" + + __slots__ = 'r', 'g', 'b', 'alpha', '_tuple' + + def __init__(self, r: float, g: float, b: float, alpha: Optional[float]): + self.r = r + self.g = g + self.b = b + self.alpha = alpha + + self._tuple: Tuple[float, float, float, Optional[float]] = (r, g, b, alpha) + + def __getitem__(self, item: Any) -> Any: + return self._tuple[item] + + +# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached +_r_255 = r'(\d{1,3}(?:\.\d+)?)' +_r_comma = r'\s*,\s*' +_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)' +_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?' +_r_sl = r'(\d{1,3}(?:\.\d+)?)%' +r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*' +r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*' +# CSS3 RGB examples: rgb(0, 0, 0), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 50%) +r_rgb = rf'\s*rgba?\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}(?:{_r_comma}{_r_alpha})?\s*\)\s*' +# CSS3 HSL examples: hsl(270, 60%, 50%), hsla(270, 60%, 50%, 0.5), hsla(270, 60%, 50%, 50%) +r_hsl = rf'\s*hsla?\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}(?:{_r_comma}{_r_alpha})?\s*\)\s*' +# CSS4 RGB examples: rgb(0 0 0), rgb(0 0 0 / 0.5), rgb(0 0 0 / 50%), rgba(0 0 0 / 50%) +r_rgb_v4_style = rf'\s*rgba?\(\s*{_r_255}\s+{_r_255}\s+{_r_255}(?:\s*/\s*{_r_alpha})?\s*\)\s*' +# CSS4 HSL examples: hsl(270 60% 50%), hsl(270 60% 50% / 0.5), hsl(270 60% 50% / 50%), hsla(270 60% 50% / 50%) +r_hsl_v4_style = rf'\s*hsla?\(\s*{_r_h}\s+{_r_sl}\s+{_r_sl}(?:\s*/\s*{_r_alpha})?\s*\)\s*' + +# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used +repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'} +rads = 2 * math.pi + + +@deprecated( + 'The `Color` class is deprecated, use `pydantic_extra_types` instead. ' + 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/.', + category=PydanticDeprecatedSince20, +) +class Color(_repr.Representation): + """Represents a color.""" + + __slots__ = '_original', '_rgba' + + def __init__(self, value: ColorType) -> None: + self._rgba: RGBA + self._original: ColorType + if isinstance(value, (tuple, list)): + self._rgba = parse_tuple(value) + elif isinstance(value, str): + self._rgba = parse_str(value) + elif isinstance(value, Color): + self._rgba = value._rgba + value = value._original + else: + raise PydanticCustomError( + 'color_error', 'value is not a valid color: value must be a tuple, list or string' + ) + + # if we've got here value must be a valid color + self._original = value + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='color') + return field_schema + + def original(self) -> ColorType: + """Original value passed to `Color`.""" + return self._original + + def as_named(self, *, fallback: bool = False) -> str: + """Returns the name of the color if it can be found in `COLORS_BY_VALUE` dictionary, + otherwise returns the hexadecimal representation of the color or raises `ValueError`. + + Args: + fallback: If True, falls back to returning the hexadecimal representation of + the color instead of raising a ValueError when no named color is found. + + Returns: + The name of the color, or the hexadecimal representation of the color. + + Raises: + ValueError: When no named color is found and fallback is `False`. + """ + if self._rgba.alpha is None: + rgb = cast(Tuple[int, int, int], self.as_rgb_tuple()) + try: + return COLORS_BY_VALUE[rgb] + except KeyError as e: + if fallback: + return self.as_hex() + else: + raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e + else: + return self.as_hex() + + def as_hex(self) -> str: + """Returns the hexadecimal representation of the color. + + Hex string representing the color can be 3, 4, 6, or 8 characters depending on whether the string + a "short" representation of the color is possible and whether there's an alpha channel. + + Returns: + The hexadecimal representation of the color. + """ + values = [float_to_255(c) for c in self._rgba[:3]] + if self._rgba.alpha is not None: + values.append(float_to_255(self._rgba.alpha)) + + as_hex = ''.join(f'{v:02x}' for v in values) + if all(c in repeat_colors for c in values): + as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2)) + return '#' + as_hex + + def as_rgb(self) -> str: + """Color as an `rgb(, , )` or `rgba(, , ,
    )` string.""" + if self._rgba.alpha is None: + return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})' + else: + return ( + f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, ' + f'{round(self._alpha_float(), 2)})' + ) + + def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple: + """Returns the color as an RGB or RGBA tuple. + + Args: + alpha: Whether to include the alpha channel. There are three options for this input: + + - `None` (default): Include alpha only if it's set. (e.g. not `None`) + - `True`: Always include alpha. + - `False`: Always omit alpha. + + Returns: + A tuple that contains the values of the red, green, and blue channels in the range 0 to 255. + If alpha is included, it is in the range 0 to 1. + """ + r, g, b = (float_to_255(c) for c in self._rgba[:3]) + if alpha is None: + if self._rgba.alpha is None: + return r, g, b + else: + return r, g, b, self._alpha_float() + elif alpha: + return r, g, b, self._alpha_float() + else: + # alpha is False + return r, g, b + + def as_hsl(self) -> str: + """Color as an `hsl(, , )` or `hsl(, , , )` string.""" + if self._rgba.alpha is None: + h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})' + else: + h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})' + + def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple: + """Returns the color as an HSL or HSLA tuple. + + Args: + alpha: Whether to include the alpha channel. + + - `None` (default): Include the alpha channel only if it's set (e.g. not `None`). + - `True`: Always include alpha. + - `False`: Always omit alpha. + + Returns: + The color as a tuple of hue, saturation, lightness, and alpha (if included). + All elements are in the range 0 to 1. + + Note: + This is HSL as used in HTML and most other places, not HLS as used in Python's `colorsys`. + """ + h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) # noqa: E741 + if alpha is None: + if self._rgba.alpha is None: + return h, s, l + else: + return h, s, l, self._alpha_float() + if alpha: + return h, s, l, self._alpha_float() + else: + # alpha is False + return h, s, l + + def _alpha_float(self) -> float: + return 1 if self._rgba.alpha is None else self._rgba.alpha + + @classmethod + def __get_pydantic_core_schema__( + cls, source: Type[Any], handler: Callable[[Any], CoreSchema] + ) -> core_schema.CoreSchema: + return core_schema.with_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, __input_value: Any, _: Any) -> 'Color': + return cls(__input_value) + + def __str__(self) -> str: + return self.as_named(fallback=True) + + def __repr_args__(self) -> '_repr.ReprArgs': + return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())] + + def __eq__(self, other: Any) -> bool: + return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple() + + def __hash__(self) -> int: + return hash(self.as_rgb_tuple()) + + +def parse_tuple(value: Tuple[Any, ...]) -> RGBA: + """Parse a tuple or list to get RGBA values. + + Args: + value: A tuple or list. + + Returns: + An `RGBA` tuple parsed from the input tuple. + + Raises: + PydanticCustomError: If tuple is not valid. + """ + if len(value) == 3: + r, g, b = (parse_color_value(v) for v in value) + return RGBA(r, g, b, None) + elif len(value) == 4: + r, g, b = (parse_color_value(v) for v in value[:3]) + return RGBA(r, g, b, parse_float_alpha(value[3])) + else: + raise PydanticCustomError('color_error', 'value is not a valid color: tuples must have length 3 or 4') + + +def parse_str(value: str) -> RGBA: + """Parse a string representing a color to an RGBA tuple. + + Possible formats for the input string include: + + * named color, see `COLORS_BY_NAME` + * hex short eg. `fff` (prefix can be `#`, `0x` or nothing) + * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing) + * `rgb(, , )` + * `rgba(, , , )` + + Args: + value: A string representing a color. + + Returns: + An `RGBA` tuple parsed from the input string. + + Raises: + ValueError: If the input string cannot be parsed to an RGBA tuple. + """ + value_lower = value.lower() + try: + r, g, b = COLORS_BY_NAME[value_lower] + except KeyError: + pass + else: + return ints_to_rgba(r, g, b, None) + + m = re.fullmatch(r_hex_short, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v * 2, 16) for v in rgb) + if a: + alpha: Optional[float] = int(a * 2, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_hex_long, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v, 16) for v in rgb) + if a: + alpha = int(a, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_rgb, value_lower) or re.fullmatch(r_rgb_v4_style, value_lower) + if m: + return ints_to_rgba(*m.groups()) # type: ignore + + m = re.fullmatch(r_hsl, value_lower) or re.fullmatch(r_hsl_v4_style, value_lower) + if m: + return parse_hsl(*m.groups()) # type: ignore + + raise PydanticCustomError('color_error', 'value is not a valid color: string not recognised as a valid color') + + +def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float] = None) -> RGBA: + """Converts integer or string values for RGB color and an optional alpha value to an `RGBA` object. + + Args: + r: An integer or string representing the red color value. + g: An integer or string representing the green color value. + b: An integer or string representing the blue color value. + alpha: A float representing the alpha value. Defaults to None. + + Returns: + An instance of the `RGBA` class with the corresponding color and alpha values. + """ + return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha)) + + +def parse_color_value(value: Union[int, str], max_val: int = 255) -> float: + """Parse the color value provided and return a number between 0 and 1. + + Args: + value: An integer or string color value. + max_val: Maximum range value. Defaults to 255. + + Raises: + PydanticCustomError: If the value is not a valid color. + + Returns: + A number between 0 and 1. + """ + try: + color = float(value) + except ValueError: + raise PydanticCustomError('color_error', 'value is not a valid color: color values must be a valid number') + if 0 <= color <= max_val: + return color / max_val + else: + raise PydanticCustomError( + 'color_error', + 'value is not a valid color: color values must be in the range 0 to {max_val}', + {'max_val': max_val}, + ) + + +def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]: + """Parse an alpha value checking it's a valid float in the range 0 to 1. + + Args: + value: The input value to parse. + + Returns: + The parsed value as a float, or `None` if the value was None or equal 1. + + Raises: + PydanticCustomError: If the input value cannot be successfully parsed as a float in the expected range. + """ + if value is None: + return None + try: + if isinstance(value, str) and value.endswith('%'): + alpha = float(value[:-1]) / 100 + else: + alpha = float(value) + except ValueError: + raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be a valid float') + + if math.isclose(alpha, 1): + return None + elif 0 <= alpha <= 1: + return alpha + else: + raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be in the range 0 to 1') + + +def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA: + """Parse raw hue, saturation, lightness, and alpha values and convert to RGBA. + + Args: + h: The hue value. + h_units: The unit for hue value. + sat: The saturation value. + light: The lightness value. + alpha: Alpha value. + + Returns: + An instance of `RGBA`. + """ + s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100) + + h_value = float(h) + if h_units in {None, 'deg'}: + h_value = h_value % 360 / 360 + elif h_units == 'rad': + h_value = h_value % rads / rads + else: + # turns + h_value = h_value % 1 + + r, g, b = hls_to_rgb(h_value, l_value, s_value) + return RGBA(r, g, b, parse_float_alpha(alpha)) + + +def float_to_255(c: float) -> int: + """Converts a float value between 0 and 1 (inclusive) to an integer between 0 and 255 (inclusive). + + Args: + c: The float value to be converted. Must be between 0 and 1 (inclusive). + + Returns: + The integer equivalent of the given float value rounded to the nearest whole number. + + Raises: + ValueError: If the given float value is outside the acceptable range of 0 to 1 (inclusive). + """ + return int(round(c * 255)) + + +COLORS_BY_NAME = { + 'aliceblue': (240, 248, 255), + 'antiquewhite': (250, 235, 215), + 'aqua': (0, 255, 255), + 'aquamarine': (127, 255, 212), + 'azure': (240, 255, 255), + 'beige': (245, 245, 220), + 'bisque': (255, 228, 196), + 'black': (0, 0, 0), + 'blanchedalmond': (255, 235, 205), + 'blue': (0, 0, 255), + 'blueviolet': (138, 43, 226), + 'brown': (165, 42, 42), + 'burlywood': (222, 184, 135), + 'cadetblue': (95, 158, 160), + 'chartreuse': (127, 255, 0), + 'chocolate': (210, 105, 30), + 'coral': (255, 127, 80), + 'cornflowerblue': (100, 149, 237), + 'cornsilk': (255, 248, 220), + 'crimson': (220, 20, 60), + 'cyan': (0, 255, 255), + 'darkblue': (0, 0, 139), + 'darkcyan': (0, 139, 139), + 'darkgoldenrod': (184, 134, 11), + 'darkgray': (169, 169, 169), + 'darkgreen': (0, 100, 0), + 'darkgrey': (169, 169, 169), + 'darkkhaki': (189, 183, 107), + 'darkmagenta': (139, 0, 139), + 'darkolivegreen': (85, 107, 47), + 'darkorange': (255, 140, 0), + 'darkorchid': (153, 50, 204), + 'darkred': (139, 0, 0), + 'darksalmon': (233, 150, 122), + 'darkseagreen': (143, 188, 143), + 'darkslateblue': (72, 61, 139), + 'darkslategray': (47, 79, 79), + 'darkslategrey': (47, 79, 79), + 'darkturquoise': (0, 206, 209), + 'darkviolet': (148, 0, 211), + 'deeppink': (255, 20, 147), + 'deepskyblue': (0, 191, 255), + 'dimgray': (105, 105, 105), + 'dimgrey': (105, 105, 105), + 'dodgerblue': (30, 144, 255), + 'firebrick': (178, 34, 34), + 'floralwhite': (255, 250, 240), + 'forestgreen': (34, 139, 34), + 'fuchsia': (255, 0, 255), + 'gainsboro': (220, 220, 220), + 'ghostwhite': (248, 248, 255), + 'gold': (255, 215, 0), + 'goldenrod': (218, 165, 32), + 'gray': (128, 128, 128), + 'green': (0, 128, 0), + 'greenyellow': (173, 255, 47), + 'grey': (128, 128, 128), + 'honeydew': (240, 255, 240), + 'hotpink': (255, 105, 180), + 'indianred': (205, 92, 92), + 'indigo': (75, 0, 130), + 'ivory': (255, 255, 240), + 'khaki': (240, 230, 140), + 'lavender': (230, 230, 250), + 'lavenderblush': (255, 240, 245), + 'lawngreen': (124, 252, 0), + 'lemonchiffon': (255, 250, 205), + 'lightblue': (173, 216, 230), + 'lightcoral': (240, 128, 128), + 'lightcyan': (224, 255, 255), + 'lightgoldenrodyellow': (250, 250, 210), + 'lightgray': (211, 211, 211), + 'lightgreen': (144, 238, 144), + 'lightgrey': (211, 211, 211), + 'lightpink': (255, 182, 193), + 'lightsalmon': (255, 160, 122), + 'lightseagreen': (32, 178, 170), + 'lightskyblue': (135, 206, 250), + 'lightslategray': (119, 136, 153), + 'lightslategrey': (119, 136, 153), + 'lightsteelblue': (176, 196, 222), + 'lightyellow': (255, 255, 224), + 'lime': (0, 255, 0), + 'limegreen': (50, 205, 50), + 'linen': (250, 240, 230), + 'magenta': (255, 0, 255), + 'maroon': (128, 0, 0), + 'mediumaquamarine': (102, 205, 170), + 'mediumblue': (0, 0, 205), + 'mediumorchid': (186, 85, 211), + 'mediumpurple': (147, 112, 219), + 'mediumseagreen': (60, 179, 113), + 'mediumslateblue': (123, 104, 238), + 'mediumspringgreen': (0, 250, 154), + 'mediumturquoise': (72, 209, 204), + 'mediumvioletred': (199, 21, 133), + 'midnightblue': (25, 25, 112), + 'mintcream': (245, 255, 250), + 'mistyrose': (255, 228, 225), + 'moccasin': (255, 228, 181), + 'navajowhite': (255, 222, 173), + 'navy': (0, 0, 128), + 'oldlace': (253, 245, 230), + 'olive': (128, 128, 0), + 'olivedrab': (107, 142, 35), + 'orange': (255, 165, 0), + 'orangered': (255, 69, 0), + 'orchid': (218, 112, 214), + 'palegoldenrod': (238, 232, 170), + 'palegreen': (152, 251, 152), + 'paleturquoise': (175, 238, 238), + 'palevioletred': (219, 112, 147), + 'papayawhip': (255, 239, 213), + 'peachpuff': (255, 218, 185), + 'peru': (205, 133, 63), + 'pink': (255, 192, 203), + 'plum': (221, 160, 221), + 'powderblue': (176, 224, 230), + 'purple': (128, 0, 128), + 'red': (255, 0, 0), + 'rosybrown': (188, 143, 143), + 'royalblue': (65, 105, 225), + 'saddlebrown': (139, 69, 19), + 'salmon': (250, 128, 114), + 'sandybrown': (244, 164, 96), + 'seagreen': (46, 139, 87), + 'seashell': (255, 245, 238), + 'sienna': (160, 82, 45), + 'silver': (192, 192, 192), + 'skyblue': (135, 206, 235), + 'slateblue': (106, 90, 205), + 'slategray': (112, 128, 144), + 'slategrey': (112, 128, 144), + 'snow': (255, 250, 250), + 'springgreen': (0, 255, 127), + 'steelblue': (70, 130, 180), + 'tan': (210, 180, 140), + 'teal': (0, 128, 128), + 'thistle': (216, 191, 216), + 'tomato': (255, 99, 71), + 'turquoise': (64, 224, 208), + 'violet': (238, 130, 238), + 'wheat': (245, 222, 179), + 'white': (255, 255, 255), + 'whitesmoke': (245, 245, 245), + 'yellow': (255, 255, 0), + 'yellowgreen': (154, 205, 50), +} + +COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()} diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/config.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/config.py new file mode 100644 index 0000000..1841a33 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/config.py @@ -0,0 +1,1003 @@ +"""Configuration for Pydantic models.""" +from __future__ import annotations as _annotations + +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Type, TypeVar, Union + +from typing_extensions import Literal, TypeAlias, TypedDict + +from ._migration import getattr_migration +from .aliases import AliasGenerator + +if TYPE_CHECKING: + from ._internal._generate_schema import GenerateSchema as _GenerateSchema + +__all__ = ('ConfigDict', 'with_config') + + +JsonValue: TypeAlias = Union[int, float, str, bool, None, List['JsonValue'], 'JsonDict'] +JsonDict: TypeAlias = Dict[str, JsonValue] + +JsonEncoder = Callable[[Any], Any] + +JsonSchemaExtraCallable: TypeAlias = Union[ + Callable[[JsonDict], None], + Callable[[JsonDict, Type[Any]], None], +] + +ExtraValues = Literal['allow', 'ignore', 'forbid'] + + +class ConfigDict(TypedDict, total=False): + """A TypedDict for configuring Pydantic behaviour.""" + + title: str | None + """The title for the generated JSON schema, defaults to the model's name""" + + str_to_lower: bool + """Whether to convert all characters to lowercase for str types. Defaults to `False`.""" + + str_to_upper: bool + """Whether to convert all characters to uppercase for str types. Defaults to `False`.""" + str_strip_whitespace: bool + """Whether to strip leading and trailing whitespace for str types.""" + + str_min_length: int + """The minimum length for str types. Defaults to `None`.""" + + str_max_length: int | None + """The maximum length for str types. Defaults to `None`.""" + + extra: ExtraValues | None + """ + Whether to ignore, allow, or forbid extra attributes during model initialization. Defaults to `'ignore'`. + + You can configure how pydantic handles the attributes that are not defined in the model: + + * `allow` - Allow any extra attributes. + * `forbid` - Forbid any extra attributes. + * `ignore` - Ignore any extra attributes. + + ```py + from pydantic import BaseModel, ConfigDict + + + class User(BaseModel): + model_config = ConfigDict(extra='ignore') # (1)! + + name: str + + + user = User(name='John Doe', age=20) # (2)! + print(user) + #> name='John Doe' + ``` + + 1. This is the default behaviour. + 2. The `age` argument is ignored. + + Instead, with `extra='allow'`, the `age` argument is included: + + ```py + from pydantic import BaseModel, ConfigDict + + + class User(BaseModel): + model_config = ConfigDict(extra='allow') + + name: str + + + user = User(name='John Doe', age=20) # (1)! + print(user) + #> name='John Doe' age=20 + ``` + + 1. The `age` argument is included. + + With `extra='forbid'`, an error is raised: + + ```py + from pydantic import BaseModel, ConfigDict, ValidationError + + + class User(BaseModel): + model_config = ConfigDict(extra='forbid') + + name: str + + + try: + User(name='John Doe', age=20) + except ValidationError as e: + print(e) + ''' + 1 validation error for User + age + Extra inputs are not permitted [type=extra_forbidden, input_value=20, input_type=int] + ''' + ``` + """ + + frozen: bool + """ + Whether models are faux-immutable, i.e. whether `__setattr__` is allowed, and also generates + a `__hash__()` method for the model. This makes instances of the model potentially hashable if all the + attributes are hashable. Defaults to `False`. + + Note: + On V1, the inverse of this setting was called `allow_mutation`, and was `True` by default. + """ + + populate_by_name: bool + """ + Whether an aliased field may be populated by its name as given by the model + attribute, as well as the alias. Defaults to `False`. + + Note: + The name of this configuration setting was changed in **v2.0** from + `allow_population_by_field_name` to `populate_by_name`. + + ```py + from pydantic import BaseModel, ConfigDict, Field + + + class User(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + name: str = Field(alias='full_name') # (1)! + age: int + + + user = User(full_name='John Doe', age=20) # (2)! + print(user) + #> name='John Doe' age=20 + user = User(name='John Doe', age=20) # (3)! + print(user) + #> name='John Doe' age=20 + ``` + + 1. The field `'name'` has an alias `'full_name'`. + 2. The model is populated by the alias `'full_name'`. + 3. The model is populated by the field name `'name'`. + """ + + use_enum_values: bool + """ + Whether to populate models with the `value` property of enums, rather than the raw enum. + This may be useful if you want to serialize `model.model_dump()` later. Defaults to `False`. + + !!! note + If you have an `Optional[Enum]` value that you set a default for, you need to use `validate_default=True` + for said Field to ensure that the `use_enum_values` flag takes effect on the default, as extracting an + enum's value occurs during validation, not serialization. + + ```py + from enum import Enum + from typing import Optional + + from pydantic import BaseModel, ConfigDict, Field + + + class SomeEnum(Enum): + FOO = 'foo' + BAR = 'bar' + BAZ = 'baz' + + + class SomeModel(BaseModel): + model_config = ConfigDict(use_enum_values=True) + + some_enum: SomeEnum + another_enum: Optional[SomeEnum] = Field(default=SomeEnum.FOO, validate_default=True) + + + model1 = SomeModel(some_enum=SomeEnum.BAR) + print(model1.model_dump()) + # {'some_enum': 'bar', 'another_enum': 'foo'} + + model2 = SomeModel(some_enum=SomeEnum.BAR, another_enum=SomeEnum.BAZ) + print(model2.model_dump()) + #> {'some_enum': 'bar', 'another_enum': 'baz'} + ``` + """ + + validate_assignment: bool + """ + Whether to validate the data when the model is changed. Defaults to `False`. + + The default behavior of Pydantic is to validate the data when the model is created. + + In case the user changes the data after the model is created, the model is _not_ revalidated. + + ```py + from pydantic import BaseModel + + class User(BaseModel): + name: str + + user = User(name='John Doe') # (1)! + print(user) + #> name='John Doe' + user.name = 123 # (1)! + print(user) + #> name=123 + ``` + + 1. The validation happens only when the model is created. + 2. The validation does not happen when the data is changed. + + In case you want to revalidate the model when the data is changed, you can use `validate_assignment=True`: + + ```py + from pydantic import BaseModel, ValidationError + + class User(BaseModel, validate_assignment=True): # (1)! + name: str + + user = User(name='John Doe') # (2)! + print(user) + #> name='John Doe' + try: + user.name = 123 # (3)! + except ValidationError as e: + print(e) + ''' + 1 validation error for User + name + Input should be a valid string [type=string_type, input_value=123, input_type=int] + ''' + ``` + + 1. You can either use class keyword arguments, or `model_config` to set `validate_assignment=True`. + 2. The validation happens when the model is created. + 3. The validation _also_ happens when the data is changed. + """ + + arbitrary_types_allowed: bool + """ + Whether arbitrary types are allowed for field types. Defaults to `False`. + + ```py + from pydantic import BaseModel, ConfigDict, ValidationError + + # This is not a pydantic model, it's an arbitrary class + class Pet: + def __init__(self, name: str): + self.name = name + + class Model(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + + pet: Pet + owner: str + + pet = Pet(name='Hedwig') + # A simple check of instance type is used to validate the data + model = Model(owner='Harry', pet=pet) + print(model) + #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry' + print(model.pet) + #> <__main__.Pet object at 0x0123456789ab> + print(model.pet.name) + #> Hedwig + print(type(model.pet)) + #> + try: + # If the value is not an instance of the type, it's invalid + Model(owner='Harry', pet='Hedwig') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + pet + Input should be an instance of Pet [type=is_instance_of, input_value='Hedwig', input_type=str] + ''' + + # Nothing in the instance of the arbitrary type is checked + # Here name probably should have been a str, but it's not validated + pet2 = Pet(name=42) + model2 = Model(owner='Harry', pet=pet2) + print(model2) + #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry' + print(model2.pet) + #> <__main__.Pet object at 0x0123456789ab> + print(model2.pet.name) + #> 42 + print(type(model2.pet)) + #> + ``` + """ + + from_attributes: bool + """ + Whether to build models and look up discriminators of tagged unions using python object attributes. + """ + + loc_by_alias: bool + """Whether to use the actual key provided in the data (e.g. alias) for error `loc`s rather than the field's name. Defaults to `True`.""" + + alias_generator: Callable[[str], str] | AliasGenerator | None + """ + A callable that takes a field name and returns an alias for it + or an instance of [`AliasGenerator`][pydantic.aliases.AliasGenerator]. Defaults to `None`. + + When using a callable, the alias generator is used for both validation and serialization. + If you want to use different alias generators for validation and serialization, you can use + [`AliasGenerator`][pydantic.aliases.AliasGenerator] instead. + + If data source field names do not match your code style (e. g. CamelCase fields), + you can automatically generate aliases using `alias_generator`. Here's an example with + a basic callable: + + ```py + from pydantic import BaseModel, ConfigDict + from pydantic.alias_generators import to_pascal + + class Voice(BaseModel): + model_config = ConfigDict(alias_generator=to_pascal) + + name: str + language_code: str + + voice = Voice(Name='Filiz', LanguageCode='tr-TR') + print(voice.language_code) + #> tr-TR + print(voice.model_dump(by_alias=True)) + #> {'Name': 'Filiz', 'LanguageCode': 'tr-TR'} + ``` + + If you want to use different alias generators for validation and serialization, you can use + [`AliasGenerator`][pydantic.aliases.AliasGenerator]. + + ```py + from pydantic import AliasGenerator, BaseModel, ConfigDict + from pydantic.alias_generators import to_camel, to_pascal + + class Athlete(BaseModel): + first_name: str + last_name: str + sport: str + + model_config = ConfigDict( + alias_generator=AliasGenerator( + validation_alias=to_camel, + serialization_alias=to_pascal, + ) + ) + + athlete = Athlete(firstName='John', lastName='Doe', sport='track') + print(athlete.model_dump(by_alias=True)) + #> {'FirstName': 'John', 'LastName': 'Doe', 'Sport': 'track'} + ``` + + Note: + Pydantic offers three built-in alias generators: [`to_pascal`][pydantic.alias_generators.to_pascal], + [`to_camel`][pydantic.alias_generators.to_camel], and [`to_snake`][pydantic.alias_generators.to_snake]. + """ + + ignored_types: tuple[type, ...] + """A tuple of types that may occur as values of class attributes without annotations. This is + typically used for custom descriptors (classes that behave like `property`). If an attribute is set on a + class without an annotation and has a type that is not in this tuple (or otherwise recognized by + _pydantic_), an error will be raised. Defaults to `()`. + """ + + allow_inf_nan: bool + """Whether to allow infinity (`+inf` an `-inf`) and NaN values to float fields. Defaults to `True`.""" + + json_schema_extra: JsonDict | JsonSchemaExtraCallable | None + """A dict or callable to provide extra JSON schema properties. Defaults to `None`.""" + + json_encoders: dict[type[object], JsonEncoder] | None + """ + A `dict` of custom JSON encoders for specific types. Defaults to `None`. + + !!! warning "Deprecated" + This config option is a carryover from v1. + We originally planned to remove it in v2 but didn't have a 1:1 replacement so we are keeping it for now. + It is still deprecated and will likely be removed in the future. + """ + + # new in V2 + strict: bool + """ + _(new in V2)_ If `True`, strict validation is applied to all fields on the model. + + By default, Pydantic attempts to coerce values to the correct type, when possible. + + There are situations in which you may want to disable this behavior, and instead raise an error if a value's type + does not match the field's type annotation. + + To configure strict mode for all fields on a model, you can set `strict=True` on the model. + + ```py + from pydantic import BaseModel, ConfigDict + + class Model(BaseModel): + model_config = ConfigDict(strict=True) + + name: str + age: int + ``` + + See [Strict Mode](../concepts/strict_mode.md) for more details. + + See the [Conversion Table](../concepts/conversion_table.md) for more details on how Pydantic converts data in both + strict and lax modes. + """ + # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' + revalidate_instances: Literal['always', 'never', 'subclass-instances'] + """ + When and how to revalidate models and dataclasses during validation. Accepts the string + values of `'never'`, `'always'` and `'subclass-instances'`. Defaults to `'never'`. + + - `'never'` will not revalidate models and dataclasses during validation + - `'always'` will revalidate models and dataclasses during validation + - `'subclass-instances'` will revalidate models and dataclasses during validation if the instance is a + subclass of the model or dataclass + + By default, model and dataclass instances are not revalidated during validation. + + ```py + from typing import List + + from pydantic import BaseModel + + class User(BaseModel, revalidate_instances='never'): # (1)! + hobbies: List[str] + + class SubUser(User): + sins: List[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] # (2)! + t = Transaction(user=my_user) # (3)! + print(t) + #> user=User(hobbies=[1]) + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) + #> user=SubUser(hobbies=['scuba diving'], sins=['lying']) + ``` + + 1. `revalidate_instances` is set to `'never'` by **default. + 2. The assignment is not validated, unless you set `validate_assignment` to `True` in the model's config. + 3. Since `revalidate_instances` is set to `never`, this is not revalidated. + + If you want to revalidate instances during validation, you can set `revalidate_instances` to `'always'` + in the model's config. + + ```py + from typing import List + + from pydantic import BaseModel, ValidationError + + class User(BaseModel, revalidate_instances='always'): # (1)! + hobbies: List[str] + + class SubUser(User): + sins: List[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] + try: + t = Transaction(user=my_user) # (2)! + except ValidationError as e: + print(e) + ''' + 1 validation error for Transaction + user.hobbies.0 + Input should be a valid string [type=string_type, input_value=1, input_type=int] + ''' + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) # (3)! + #> user=User(hobbies=['scuba diving']) + ``` + + 1. `revalidate_instances` is set to `'always'`. + 2. The model is revalidated, since `revalidate_instances` is set to `'always'`. + 3. Using `'never'` we would have gotten `user=SubUser(hobbies=['scuba diving'], sins=['lying'])`. + + It's also possible to set `revalidate_instances` to `'subclass-instances'` to only revalidate instances + of subclasses of the model. + + ```py + from typing import List + + from pydantic import BaseModel + + class User(BaseModel, revalidate_instances='subclass-instances'): # (1)! + hobbies: List[str] + + class SubUser(User): + sins: List[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] + t = Transaction(user=my_user) # (2)! + print(t) + #> user=User(hobbies=[1]) + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) # (3)! + #> user=User(hobbies=['scuba diving']) + ``` + + 1. `revalidate_instances` is set to `'subclass-instances'`. + 2. This is not revalidated, since `my_user` is not a subclass of `User`. + 3. Using `'never'` we would have gotten `user=SubUser(hobbies=['scuba diving'], sins=['lying'])`. + """ + + ser_json_timedelta: Literal['iso8601', 'float'] + """ + The format of JSON serialized timedeltas. Accepts the string values of `'iso8601'` and + `'float'`. Defaults to `'iso8601'`. + + - `'iso8601'` will serialize timedeltas to ISO 8601 durations. + - `'float'` will serialize timedeltas to the total number of seconds. + """ + + ser_json_bytes: Literal['utf8', 'base64'] + """ + The encoding of JSON serialized bytes. Accepts the string values of `'utf8'` and `'base64'`. + Defaults to `'utf8'`. + + - `'utf8'` will serialize bytes to UTF-8 strings. + - `'base64'` will serialize bytes to URL safe base64 strings. + """ + + ser_json_inf_nan: Literal['null', 'constants'] + """ + The encoding of JSON serialized infinity and NaN float values. Accepts the string values of `'null'` and `'constants'`. + Defaults to `'null'`. + + - `'null'` will serialize infinity and NaN values as `null`. + - `'constants'` will serialize infinity and NaN values as `Infinity` and `NaN`. + """ + + # whether to validate default values during validation, default False + validate_default: bool + """Whether to validate default values during validation. Defaults to `False`.""" + + validate_return: bool + """whether to validate the return value from call validators. Defaults to `False`.""" + + protected_namespaces: tuple[str, ...] + """ + A `tuple` of strings that prevent model to have field which conflict with them. + Defaults to `('model_', )`). + + Pydantic prevents collisions between model attributes and `BaseModel`'s own methods by + namespacing them with the prefix `model_`. + + ```py + import warnings + + from pydantic import BaseModel + + warnings.filterwarnings('error') # Raise warnings as errors + + try: + + class Model(BaseModel): + model_prefixed_field: str + + except UserWarning as e: + print(e) + ''' + Field "model_prefixed_field" has conflict with protected namespace "model_". + + You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ()`. + ''' + ``` + + You can customize this behavior using the `protected_namespaces` setting: + + ```py + import warnings + + from pydantic import BaseModel, ConfigDict + + warnings.filterwarnings('error') # Raise warnings as errors + + try: + + class Model(BaseModel): + model_prefixed_field: str + also_protect_field: str + + model_config = ConfigDict( + protected_namespaces=('protect_me_', 'also_protect_') + ) + + except UserWarning as e: + print(e) + ''' + Field "also_protect_field" has conflict with protected namespace "also_protect_". + + You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ('protect_me_',)`. + ''' + ``` + + While Pydantic will only emit a warning when an item is in a protected namespace but does not actually have a collision, + an error _is_ raised if there is an actual collision with an existing attribute: + + ```py + from pydantic import BaseModel + + try: + + class Model(BaseModel): + model_validate: str + + except NameError as e: + print(e) + ''' + Field "model_validate" conflicts with member > of protected namespace "model_". + ''' + ``` + """ + + hide_input_in_errors: bool + """ + Whether to hide inputs when printing errors. Defaults to `False`. + + Pydantic shows the input value and type when it raises `ValidationError` during the validation. + + ```py + from pydantic import BaseModel, ValidationError + + class Model(BaseModel): + a: str + + try: + Model(a=123) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + a + Input should be a valid string [type=string_type, input_value=123, input_type=int] + ''' + ``` + + You can hide the input value and type by setting the `hide_input_in_errors` config to `True`. + + ```py + from pydantic import BaseModel, ConfigDict, ValidationError + + class Model(BaseModel): + a: str + model_config = ConfigDict(hide_input_in_errors=True) + + try: + Model(a=123) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + a + Input should be a valid string [type=string_type] + ''' + ``` + """ + + defer_build: bool + """ + Whether to defer model validator and serializer construction until the first model validation. + + This can be useful to avoid the overhead of building models which are only + used nested within other models, or when you want to manually define type namespace via + [`Model.model_rebuild(_types_namespace=...)`][pydantic.BaseModel.model_rebuild]. Defaults to False. + """ + + plugin_settings: dict[str, object] | None + """A `dict` of settings for plugins. Defaults to `None`. + + See [Pydantic Plugins](../concepts/plugins.md) for details. + """ + + schema_generator: type[_GenerateSchema] | None + """ + A custom core schema generator class to use when generating JSON schemas. + Useful if you want to change the way types are validated across an entire model/schema. Defaults to `None`. + + The `GenerateSchema` interface is subject to change, currently only the `string_schema` method is public. + + See [#6737](https://github.com/pydantic/pydantic/pull/6737) for details. + """ + + json_schema_serialization_defaults_required: bool + """ + Whether fields with default values should be marked as required in the serialization schema. Defaults to `False`. + + This ensures that the serialization schema will reflect the fact a field with a default will always be present + when serializing the model, even though it is not required for validation. + + However, there are scenarios where this may be undesirable — in particular, if you want to share the schema + between validation and serialization, and don't mind fields with defaults being marked as not required during + serialization. See [#7209](https://github.com/pydantic/pydantic/issues/7209) for more details. + + ```py + from pydantic import BaseModel, ConfigDict + + class Model(BaseModel): + a: str = 'a' + + model_config = ConfigDict(json_schema_serialization_defaults_required=True) + + print(Model.model_json_schema(mode='validation')) + ''' + { + 'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}}, + 'title': 'Model', + 'type': 'object', + } + ''' + print(Model.model_json_schema(mode='serialization')) + ''' + { + 'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}}, + 'required': ['a'], + 'title': 'Model', + 'type': 'object', + } + ''' + ``` + """ + + json_schema_mode_override: Literal['validation', 'serialization', None] + """ + If not `None`, the specified mode will be used to generate the JSON schema regardless of what `mode` was passed to + the function call. Defaults to `None`. + + This provides a way to force the JSON schema generation to reflect a specific mode, e.g., to always use the + validation schema. + + It can be useful when using frameworks (such as FastAPI) that may generate different schemas for validation + and serialization that must both be referenced from the same schema; when this happens, we automatically append + `-Input` to the definition reference for the validation schema and `-Output` to the definition reference for the + serialization schema. By specifying a `json_schema_mode_override` though, this prevents the conflict between + the validation and serialization schemas (since both will use the specified schema), and so prevents the suffixes + from being added to the definition references. + + ```py + from pydantic import BaseModel, ConfigDict, Json + + class Model(BaseModel): + a: Json[int] # requires a string to validate, but will dump an int + + print(Model.model_json_schema(mode='serialization')) + ''' + { + 'properties': {'a': {'title': 'A', 'type': 'integer'}}, + 'required': ['a'], + 'title': 'Model', + 'type': 'object', + } + ''' + + class ForceInputModel(Model): + # the following ensures that even with mode='serialization', we + # will get the schema that would be generated for validation. + model_config = ConfigDict(json_schema_mode_override='validation') + + print(ForceInputModel.model_json_schema(mode='serialization')) + ''' + { + 'properties': { + 'a': { + 'contentMediaType': 'application/json', + 'contentSchema': {'type': 'integer'}, + 'title': 'A', + 'type': 'string', + } + }, + 'required': ['a'], + 'title': 'ForceInputModel', + 'type': 'object', + } + ''' + ``` + """ + + coerce_numbers_to_str: bool + """ + If `True`, enables automatic coercion of any `Number` type to `str` in "lax" (non-strict) mode. Defaults to `False`. + + Pydantic doesn't allow number types (`int`, `float`, `Decimal`) to be coerced as type `str` by default. + + ```py + from decimal import Decimal + + from pydantic import BaseModel, ConfigDict, ValidationError + + class Model(BaseModel): + value: str + + try: + print(Model(value=42)) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + value + Input should be a valid string [type=string_type, input_value=42, input_type=int] + ''' + + class Model(BaseModel): + model_config = ConfigDict(coerce_numbers_to_str=True) + + value: str + + repr(Model(value=42).value) + #> "42" + repr(Model(value=42.13).value) + #> "42.13" + repr(Model(value=Decimal('42.13')).value) + #> "42.13" + ``` + """ + + regex_engine: Literal['rust-regex', 'python-re'] + """ + The regex engine to be used for pattern validation. + Defaults to `'rust-regex'`. + + - `rust-regex` uses the [`regex`](https://docs.rs/regex) Rust crate, + which is non-backtracking and therefore more DDoS resistant, but does not support all regex features. + - `python-re` use the [`re`](https://docs.python.org/3/library/re.html) module, + which supports all regex features, but may be slower. + + ```py + from pydantic import BaseModel, ConfigDict, Field, ValidationError + + class Model(BaseModel): + model_config = ConfigDict(regex_engine='python-re') + + value: str = Field(pattern=r'^abc(?=def)') + + print(Model(value='abcdef').value) + #> abcdef + + try: + print(Model(value='abxyzcdef')) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + value + String should match pattern '^abc(?=def)' [type=string_pattern_mismatch, input_value='abxyzcdef', input_type=str] + ''' + ``` + """ + + validation_error_cause: bool + """ + If `True`, Python exceptions that were part of a validation failure will be shown as an exception group as a cause. Can be useful for debugging. Defaults to `False`. + + Note: + Python 3.10 and older don't support exception groups natively. <=3.10, backport must be installed: `pip install exceptiongroup`. + + Note: + The structure of validation errors are likely to change in future Pydantic versions. Pydantic offers no guarantees about their structure. Should be used for visual traceback debugging only. + """ + + use_attribute_docstrings: bool + ''' + Whether docstrings of attributes (bare string literals immediately following the attribute declaration) + should be used for field descriptions. Defaults to `False`. + + ```py + from pydantic import BaseModel, ConfigDict, Field + + + class Model(BaseModel): + model_config = ConfigDict(use_attribute_docstrings=True) + + x: str + """ + Example of an attribute docstring + """ + + y: int = Field(description="Description in Field") + """ + Description in Field overrides attribute docstring + """ + + + print(Model.model_fields["x"].description) + # > Example of an attribute docstring + print(Model.model_fields["y"].description) + # > Description in Field + ``` + This requires the source code of the class to be available at runtime. + + !!! warning "Usage with `TypedDict`" + Due to current limitations, attribute docstrings detection may not work as expected when using `TypedDict` + (in particular when multiple `TypedDict` classes have the same name in the same source file). The behavior + can be different depending on the Python version used. + ''' + + cache_strings: bool | Literal['all', 'keys', 'none'] + """ + Whether to cache strings to avoid constructing new Python objects. Defaults to True. + + Enabling this setting should significantly improve validation performance while increasing memory usage slightly. + + - `True` or `'all'` (the default): cache all strings + - `'keys'`: cache only dictionary keys + - `False` or `'none'`: no caching + + !!! note + `True` or `'all'` is required to cache strings during general validation because + validators don't know if they're in a key or a value. + + !!! tip + If repeated strings are rare, it's recommended to use `'keys'` or `'none'` to reduce memory usage, + as the performance difference is minimal if repeated strings are rare. + """ + + +_TypeT = TypeVar('_TypeT', bound=type) + + +def with_config(config: ConfigDict) -> Callable[[_TypeT], _TypeT]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/config/#configuration-with-dataclass-from-the-standard-library-or-typeddict + + A convenience decorator to set a [Pydantic configuration](config.md) on a `TypedDict` or a `dataclass` from the standard library. + + Although the configuration can be set using the `__pydantic_config__` attribute, it does not play well with type checkers, + especially with `TypedDict`. + + !!! example "Usage" + + ```py + from typing_extensions import TypedDict + + from pydantic import ConfigDict, TypeAdapter, with_config + + @with_config(ConfigDict(str_to_lower=True)) + class Model(TypedDict): + x: str + + ta = TypeAdapter(Model) + + print(ta.validate_python({'x': 'ABC'})) + #> {'x': 'abc'} + ``` + """ + + def inner(TypedDictClass: _TypeT, /) -> _TypeT: + TypedDictClass.__pydantic_config__ = config + return TypedDictClass + + return inner + + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/dataclasses.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/dataclasses.py new file mode 100644 index 0000000..c00de32 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/dataclasses.py @@ -0,0 +1,327 @@ +"""Provide an enhanced dataclass that performs validation.""" +from __future__ import annotations as _annotations + +import dataclasses +import sys +import types +from typing import TYPE_CHECKING, Any, Callable, Generic, NoReturn, TypeVar, overload + +from typing_extensions import Literal, TypeGuard, dataclass_transform + +from ._internal import _config, _decorators, _typing_extra +from ._internal import _dataclasses as _pydantic_dataclasses +from ._migration import getattr_migration +from .config import ConfigDict +from .fields import Field, FieldInfo + +if TYPE_CHECKING: + from ._internal._dataclasses import PydanticDataclass + +__all__ = 'dataclass', 'rebuild_dataclass' + +_T = TypeVar('_T') + +if sys.version_info >= (3, 10): + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = ..., + slots: bool = ..., + ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + _cls: type[_T], # type: ignore + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = ..., + slots: bool = ..., + ) -> type[PydanticDataclass]: + ... + +else: + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + _cls: type[_T], # type: ignore + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + ) -> type[PydanticDataclass]: + ... + + +@dataclass_transform(field_specifiers=(dataclasses.field, Field)) +def dataclass( # noqa: C901 + _cls: type[_T] | None = None, + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = False, + slots: bool = False, +) -> Callable[[type[_T]], type[PydanticDataclass]] | type[PydanticDataclass]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/dataclasses/ + + A decorator used to create a Pydantic-enhanced dataclass, similar to the standard Python `dataclass`, + but with added validation. + + This function should be used similarly to `dataclasses.dataclass`. + + Args: + _cls: The target `dataclass`. + init: Included for signature compatibility with `dataclasses.dataclass`, and is passed through to + `dataclasses.dataclass` when appropriate. If specified, must be set to `False`, as pydantic inserts its + own `__init__` function. + repr: A boolean indicating whether to include the field in the `__repr__` output. + eq: Determines if a `__eq__` method should be generated for the class. + order: Determines if comparison magic methods should be generated, such as `__lt__`, but not `__eq__`. + unsafe_hash: Determines if a `__hash__` method should be included in the class, as in `dataclasses.dataclass`. + frozen: Determines if the generated class should be a 'frozen' `dataclass`, which does not allow its + attributes to be modified after it has been initialized. + config: The Pydantic config to use for the `dataclass`. + validate_on_init: A deprecated parameter included for backwards compatibility; in V2, all Pydantic dataclasses + are validated on init. + kw_only: Determines if `__init__` method parameters must be specified by keyword only. Defaults to `False`. + slots: Determines if the generated class should be a 'slots' `dataclass`, which does not allow the addition of + new attributes after instantiation. + + Returns: + A decorator that accepts a class as its argument and returns a Pydantic `dataclass`. + + Raises: + AssertionError: Raised if `init` is not `False` or `validate_on_init` is `False`. + """ + assert init is False, 'pydantic.dataclasses.dataclass only supports init=False' + assert validate_on_init is not False, 'validate_on_init=False is no longer supported' + + if sys.version_info >= (3, 10): + kwargs = dict(kw_only=kw_only, slots=slots) + else: + kwargs = {} + + def make_pydantic_fields_compatible(cls: type[Any]) -> None: + """Make sure that stdlib `dataclasses` understands `Field` kwargs like `kw_only` + To do that, we simply change + `x: int = pydantic.Field(..., kw_only=True)` + into + `x: int = dataclasses.field(default=pydantic.Field(..., kw_only=True), kw_only=True)` + """ + for annotation_cls in cls.__mro__: + # In Python < 3.9, `__annotations__` might not be present if there are no fields. + # we therefore need to use `getattr` to avoid an `AttributeError`. + annotations = getattr(annotation_cls, '__annotations__', []) + for field_name in annotations: + field_value = getattr(cls, field_name, None) + # Process only if this is an instance of `FieldInfo`. + if not isinstance(field_value, FieldInfo): + continue + + # Initialize arguments for the standard `dataclasses.field`. + field_args: dict = {'default': field_value} + + # Handle `kw_only` for Python 3.10+ + if sys.version_info >= (3, 10) and field_value.kw_only: + field_args['kw_only'] = True + + # Set `repr` attribute if it's explicitly specified to be not `True`. + if field_value.repr is not True: + field_args['repr'] = field_value.repr + + setattr(cls, field_name, dataclasses.field(**field_args)) + # In Python 3.8, dataclasses checks cls.__dict__['__annotations__'] for annotations, + # so we must make sure it's initialized before we add to it. + if cls.__dict__.get('__annotations__') is None: + cls.__annotations__ = {} + cls.__annotations__[field_name] = annotations[field_name] + + def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]: + """Create a Pydantic dataclass from a regular dataclass. + + Args: + cls: The class to create the Pydantic dataclass from. + + Returns: + A Pydantic dataclass. + """ + original_cls = cls + + config_dict = config + if config_dict is None: + # if not explicitly provided, read from the type + cls_config = getattr(cls, '__pydantic_config__', None) + if cls_config is not None: + config_dict = cls_config + config_wrapper = _config.ConfigWrapper(config_dict) + decorators = _decorators.DecoratorInfos.build(cls) + + # Keep track of the original __doc__ so that we can restore it after applying the dataclasses decorator + # Otherwise, classes with no __doc__ will have their signature added into the JSON schema description, + # since dataclasses.dataclass will set this as the __doc__ + original_doc = cls.__doc__ + + if _pydantic_dataclasses.is_builtin_dataclass(cls): + # Don't preserve the docstring for vanilla dataclasses, as it may include the signature + # This matches v1 behavior, and there was an explicit test for it + original_doc = None + + # We don't want to add validation to the existing std lib dataclass, so we will subclass it + # If the class is generic, we need to make sure the subclass also inherits from Generic + # with all the same parameters. + bases = (cls,) + if issubclass(cls, Generic): + generic_base = Generic[cls.__parameters__] # type: ignore + bases = bases + (generic_base,) + cls = types.new_class(cls.__name__, bases) + + make_pydantic_fields_compatible(cls) + + cls = dataclasses.dataclass( # type: ignore[call-overload] + cls, + # the value of init here doesn't affect anything except that it makes it easier to generate a signature + init=True, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen, + **kwargs, + ) + + cls.__pydantic_decorators__ = decorators # type: ignore + cls.__doc__ = original_doc + cls.__module__ = original_cls.__module__ + cls.__qualname__ = original_cls.__qualname__ + pydantic_complete = _pydantic_dataclasses.complete_dataclass( + cls, config_wrapper, raise_errors=False, types_namespace=None + ) + cls.__pydantic_complete__ = pydantic_complete # type: ignore + return cls + + if _cls is None: + return create_dataclass + + return create_dataclass(_cls) + + +__getattr__ = getattr_migration(__name__) + +if (3, 8) <= sys.version_info < (3, 11): + # Monkeypatch dataclasses.InitVar so that typing doesn't error if it occurs as a type when evaluating type hints + # Starting in 3.11, typing.get_type_hints will not raise an error if the retrieved type hints are not callable. + + def _call_initvar(*args: Any, **kwargs: Any) -> NoReturn: + """This function does nothing but raise an error that is as similar as possible to what you'd get + if you were to try calling `InitVar[int]()` without this monkeypatch. The whole purpose is just + to ensure typing._type_check does not error if the type hint evaluates to `InitVar[]`. + """ + raise TypeError("'InitVar' object is not callable") + + dataclasses.InitVar.__call__ = _call_initvar + + +def rebuild_dataclass( + cls: type[PydanticDataclass], + *, + force: bool = False, + raise_errors: bool = True, + _parent_namespace_depth: int = 2, + _types_namespace: dict[str, Any] | None = None, +) -> bool | None: + """Try to rebuild the pydantic-core schema for the dataclass. + + This may be necessary when one of the annotations is a ForwardRef which could not be resolved during + the initial attempt to build the schema, and automatic rebuilding fails. + + This is analogous to `BaseModel.model_rebuild`. + + Args: + cls: The class to rebuild the pydantic-core schema for. + force: Whether to force the rebuilding of the schema, defaults to `False`. + raise_errors: Whether to raise errors, defaults to `True`. + _parent_namespace_depth: The depth level of the parent namespace, defaults to 2. + _types_namespace: The types namespace, defaults to `None`. + + Returns: + Returns `None` if the schema is already "complete" and rebuilding was not required. + If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`. + """ + if not force and cls.__pydantic_complete__: + return None + else: + if _types_namespace is not None: + types_namespace: dict[str, Any] | None = _types_namespace.copy() + else: + if _parent_namespace_depth > 0: + frame_parent_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth) or {} + # Note: we may need to add something similar to cls.__pydantic_parent_namespace__ from BaseModel + # here when implementing handling of recursive generics. See BaseModel.model_rebuild for reference. + types_namespace = frame_parent_ns + else: + types_namespace = {} + + types_namespace = _typing_extra.get_cls_types_namespace(cls, types_namespace) + return _pydantic_dataclasses.complete_dataclass( + cls, + _config.ConfigWrapper(cls.__pydantic_config__, check=False), + raise_errors=raise_errors, + types_namespace=types_namespace, + ) + + +def is_pydantic_dataclass(__cls: type[Any]) -> TypeGuard[type[PydanticDataclass]]: + """Whether a class is a pydantic dataclass. + + Args: + __cls: The class. + + Returns: + `True` if the class is a pydantic dataclass, `False` otherwise. + """ + return dataclasses.is_dataclass(__cls) and '__pydantic_validator__' in __cls.__dict__ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/datetime_parse.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/datetime_parse.py new file mode 100644 index 0000000..902219d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/datetime_parse.py @@ -0,0 +1,4 @@ +"""The `datetime_parse` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/decorator.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/decorator.py new file mode 100644 index 0000000..c364346 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/decorator.py @@ -0,0 +1,4 @@ +"""The `decorator` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6cb4ff7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/class_validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/class_validators.cpython-310.pyc new file mode 100644 index 0000000..31c98e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/class_validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..1a7e059 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/copy_internals.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/copy_internals.cpython-310.pyc new file mode 100644 index 0000000..8eb190b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/copy_internals.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/decorator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/decorator.cpython-310.pyc new file mode 100644 index 0000000..2cd64f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/decorator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/json.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/json.cpython-310.pyc new file mode 100644 index 0000000..646d651 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/json.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/parse.cpython-310.pyc new file mode 100644 index 0000000..2c31843 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/tools.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/tools.cpython-310.pyc new file mode 100644 index 0000000..d0c24e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/__pycache__/tools.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/class_validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/class_validators.py new file mode 100644 index 0000000..540e40e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/class_validators.py @@ -0,0 +1,265 @@ +"""Old `@validator` and `@root_validator` function validators from V1.""" + +from __future__ import annotations as _annotations + +from functools import partial, partialmethod +from types import FunctionType +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, overload +from warnings import warn + +from typing_extensions import Literal, Protocol, TypeAlias, deprecated + +from .._internal import _decorators, _decorators_v1 +from ..errors import PydanticUserError +from ..warnings import PydanticDeprecatedSince20 + +_ALLOW_REUSE_WARNING_MESSAGE = '`allow_reuse` is deprecated and will be ignored; it should no longer be necessary' + + +if TYPE_CHECKING: + + class _OnlyValueValidatorClsMethod(Protocol): + def __call__(self, __cls: Any, __value: Any) -> Any: + ... + + class _V1ValidatorWithValuesClsMethod(Protocol): + def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any: + ... + + class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol): + def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any: + ... + + class _V1ValidatorWithKwargsClsMethod(Protocol): + def __call__(self, __cls: Any, **kwargs: Any) -> Any: + ... + + class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol): + def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any: + ... + + class _V1RootValidatorClsMethod(Protocol): + def __call__( + self, __cls: Any, __values: _decorators_v1.RootValidatorValues + ) -> _decorators_v1.RootValidatorValues: + ... + + V1Validator = Union[ + _OnlyValueValidatorClsMethod, + _V1ValidatorWithValuesClsMethod, + _V1ValidatorWithValuesKwOnlyClsMethod, + _V1ValidatorWithKwargsClsMethod, + _V1ValidatorWithValuesAndKwargsClsMethod, + _decorators_v1.V1ValidatorWithValues, + _decorators_v1.V1ValidatorWithValuesKwOnly, + _decorators_v1.V1ValidatorWithKwargs, + _decorators_v1.V1ValidatorWithValuesAndKwargs, + ] + + V1RootValidator = Union[ + _V1RootValidatorClsMethod, + _decorators_v1.V1RootValidatorFunction, + ] + + _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]] + + # Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator + # We lie to type checkers and say we return the same thing we get + # but in reality we return a proxy object that _mostly_ behaves like the wrapped thing + _V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod) + _V1RootValidatorFunctionType = TypeVar( + '_V1RootValidatorFunctionType', + _decorators_v1.V1RootValidatorFunction, + _V1RootValidatorClsMethod, + _PartialClsOrStaticMethod, + ) +else: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + + +@deprecated( + 'Pydantic V1 style `@validator` validators are deprecated.' + ' You should migrate to Pydantic V2 style `@field_validator` validators,' + ' see the migration guide for more details', + category=None, +) +def validator( + __field: str, + *fields: str, + pre: bool = False, + each_item: bool = False, + always: bool = False, + check_fields: bool | None = None, + allow_reuse: bool = False, +) -> Callable[[_V1ValidatorType], _V1ValidatorType]: + """Decorate methods on the class indicating that they should be used to validate fields. + + Args: + __field (str): The first field the validator should be called on; this is separate + from `fields` to ensure an error is raised if you don't pass at least one. + *fields (str): Additional field(s) the validator should be called on. + pre (bool, optional): Whether this validator should be called before the standard + validators (else after). Defaults to False. + each_item (bool, optional): For complex objects (sets, lists etc.) whether to validate + individual elements rather than the whole object. Defaults to False. + always (bool, optional): Whether this method and other validators should be called even if + the value is missing. Defaults to False. + check_fields (bool | None, optional): Whether to check that the fields actually exist on the model. + Defaults to None. + allow_reuse (bool, optional): Whether to track and raise an error if another validator refers to + the decorated function. Defaults to False. + + Returns: + Callable: A decorator that can be used to decorate a + function to be used as a validator. + """ + warn( + 'Pydantic V1 style `@validator` validators are deprecated.' + ' You should migrate to Pydantic V2 style `@field_validator` validators,' + ' see the migration guide for more details', + DeprecationWarning, + stacklevel=2, + ) + + if allow_reuse is True: # pragma: no cover + warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning) + fields = tuple((__field, *fields)) + if isinstance(fields[0], FunctionType): + raise PydanticUserError( + '`@validator` should be used with fields and keyword arguments, not bare. ' + "E.g. usage should be `@validator('', ...)`", + code='validator-no-fields', + ) + elif not all(isinstance(field, str) for field in fields): + raise PydanticUserError( + '`@validator` fields should be passed as separate string args. ' + "E.g. usage should be `@validator('', '', ...)`", + code='validator-invalid-fields', + ) + + mode: Literal['before', 'after'] = 'before' if pre is True else 'after' + + def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]: + if _decorators.is_instance_method_from_sig(f): + raise PydanticUserError( + '`@validator` cannot be applied to instance methods', code='validator-instance-method' + ) + # auto apply the @classmethod decorator + f = _decorators.ensure_classmethod_based_on_signature(f) + wrap = _decorators_v1.make_generic_v1_field_validator + validator_wrapper_info = _decorators.ValidatorDecoratorInfo( + fields=fields, + mode=mode, + each_item=each_item, + always=always, + check_fields=check_fields, + ) + return _decorators.PydanticDescriptorProxy(f, validator_wrapper_info, shim=wrap) + + return dec # type: ignore[return-value] + + +@overload +def root_validator( + *, + # if you don't specify `pre` the default is `pre=False` + # which means you need to specify `skip_on_failure=True` + skip_on_failure: Literal[True], + allow_reuse: bool = ..., +) -> Callable[ + [_V1RootValidatorFunctionType], + _V1RootValidatorFunctionType, +]: + ... + + +@overload +def root_validator( + *, + # if you specify `pre=True` then you don't need to specify + # `skip_on_failure`, in fact it is not allowed as an argument! + pre: Literal[True], + allow_reuse: bool = ..., +) -> Callable[ + [_V1RootValidatorFunctionType], + _V1RootValidatorFunctionType, +]: + ... + + +@overload +def root_validator( + *, + # if you explicitly specify `pre=False` then you + # MUST specify `skip_on_failure=True` + pre: Literal[False], + skip_on_failure: Literal[True], + allow_reuse: bool = ..., +) -> Callable[ + [_V1RootValidatorFunctionType], + _V1RootValidatorFunctionType, +]: + ... + + +@deprecated( + 'Pydantic V1 style `@root_validator` validators are deprecated.' + ' You should migrate to Pydantic V2 style `@model_validator` validators,' + ' see the migration guide for more details', + category=None, +) +def root_validator( + *__args, + pre: bool = False, + skip_on_failure: bool = False, + allow_reuse: bool = False, +) -> Any: + """Decorate methods on a model indicating that they should be used to validate (and perhaps + modify) data either before or after standard model parsing/validation is performed. + + Args: + pre (bool, optional): Whether this validator should be called before the standard + validators (else after). Defaults to False. + skip_on_failure (bool, optional): Whether to stop validation and return as soon as a + failure is encountered. Defaults to False. + allow_reuse (bool, optional): Whether to track and raise an error if another validator + refers to the decorated function. Defaults to False. + + Returns: + Any: A decorator that can be used to decorate a function to be used as a root_validator. + """ + warn( + 'Pydantic V1 style `@root_validator` validators are deprecated.' + ' You should migrate to Pydantic V2 style `@model_validator` validators,' + ' see the migration guide for more details', + DeprecationWarning, + stacklevel=2, + ) + + if __args: + # Ensure a nice error is raised if someone attempts to use the bare decorator + return root_validator()(*__args) # type: ignore + + if allow_reuse is True: # pragma: no cover + warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning) + mode: Literal['before', 'after'] = 'before' if pre is True else 'after' + if pre is False and skip_on_failure is not True: + raise PydanticUserError( + 'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.' + ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.', + code='root-validator-pre-skip', + ) + + wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre) + + def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any: + if _decorators.is_instance_method_from_sig(f): + raise TypeError('`@root_validator` cannot be applied to instance methods') + # auto apply the @classmethod decorator + res = _decorators.ensure_classmethod_based_on_signature(f) + dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode) + return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap) + + return dec diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/config.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/config.py new file mode 100644 index 0000000..45400c6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/config.py @@ -0,0 +1,72 @@ +from __future__ import annotations as _annotations + +import warnings +from typing import TYPE_CHECKING, Any + +from typing_extensions import Literal, deprecated + +from .._internal import _config +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = 'BaseConfig', 'Extra' + + +class _ConfigMetaclass(type): + def __getattr__(self, item: str) -> Any: + try: + obj = _config.config_defaults[item] + warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning) + return obj + except KeyError as exc: + raise AttributeError(f"type object '{self.__name__}' has no attribute {exc}") from exc + + +@deprecated('BaseConfig is deprecated. Use the `pydantic.ConfigDict` instead.', category=PydanticDeprecatedSince20) +class BaseConfig(metaclass=_ConfigMetaclass): + """This class is only retained for backwards compatibility. + + !!! Warning "Deprecated" + BaseConfig is deprecated. Use the [`pydantic.ConfigDict`][pydantic.ConfigDict] instead. + """ + + def __getattr__(self, item: str) -> Any: + try: + obj = super().__getattribute__(item) + warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning) + return obj + except AttributeError as exc: + try: + return getattr(type(self), item) + except AttributeError: + # re-raising changes the displayed text to reflect that `self` is not a type + raise AttributeError(str(exc)) from exc + + def __init_subclass__(cls, **kwargs: Any) -> None: + warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning) + return super().__init_subclass__(**kwargs) + + +class _ExtraMeta(type): + def __getattribute__(self, __name: str) -> Any: + # The @deprecated decorator accesses other attributes, so we only emit a warning for the expected ones + if __name in {'allow', 'ignore', 'forbid'}: + warnings.warn( + "`pydantic.config.Extra` is deprecated, use literal values instead (e.g. `extra='allow'`)", + DeprecationWarning, + stacklevel=2, + ) + return super().__getattribute__(__name) + + +@deprecated( + "Extra is deprecated. Use literal values instead (e.g. `extra='allow'`)", category=PydanticDeprecatedSince20 +) +class Extra(metaclass=_ExtraMeta): + allow: Literal['allow'] = 'allow' + ignore: Literal['ignore'] = 'ignore' + forbid: Literal['forbid'] = 'forbid' diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/copy_internals.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/copy_internals.py new file mode 100644 index 0000000..efe5de2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/copy_internals.py @@ -0,0 +1,224 @@ +from __future__ import annotations as _annotations + +import typing +from copy import deepcopy +from enum import Enum +from typing import Any, Tuple + +import typing_extensions + +from .._internal import ( + _model_construction, + _typing_extra, + _utils, +) + +if typing.TYPE_CHECKING: + from .. import BaseModel + from .._internal._utils import AbstractSetIntStr, MappingIntStrAny + + AnyClassMethod = classmethod[Any, Any, Any] + TupleGenerator = typing.Generator[Tuple[str, Any], None, None] + Model = typing.TypeVar('Model', bound='BaseModel') + # should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope + IncEx: typing_extensions.TypeAlias = 'set[int] | set[str] | dict[int, Any] | dict[str, Any] | None' + +_object_setattr = _model_construction.object_setattr + + +def _iter( + self: BaseModel, + to_dict: bool = False, + by_alias: bool = False, + include: AbstractSetIntStr | MappingIntStrAny | None = None, + exclude: AbstractSetIntStr | MappingIntStrAny | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, +) -> TupleGenerator: + # Merge field set excludes with explicit exclude parameter with explicit overriding field set options. + # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case. + if exclude is not None: + exclude = _utils.ValueItems.merge( + {k: v.exclude for k, v in self.model_fields.items() if v.exclude is not None}, exclude + ) + + if include is not None: + include = _utils.ValueItems.merge({k: True for k in self.model_fields}, include, intersect=True) + + allowed_keys = _calculate_keys(self, include=include, exclude=exclude, exclude_unset=exclude_unset) # type: ignore + if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none): + # huge boost for plain _iter() + yield from self.__dict__.items() + if self.__pydantic_extra__: + yield from self.__pydantic_extra__.items() + return + + value_exclude = _utils.ValueItems(self, exclude) if exclude is not None else None + value_include = _utils.ValueItems(self, include) if include is not None else None + + if self.__pydantic_extra__ is None: + items = self.__dict__.items() + else: + items = list(self.__dict__.items()) + list(self.__pydantic_extra__.items()) + + for field_key, v in items: + if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None): + continue + + if exclude_defaults: + try: + field = self.model_fields[field_key] + except KeyError: + pass + else: + if not field.is_required() and field.default == v: + continue + + if by_alias and field_key in self.model_fields: + dict_key = self.model_fields[field_key].alias or field_key + else: + dict_key = field_key + + if to_dict or value_include or value_exclude: + v = _get_value( + type(self), + v, + to_dict=to_dict, + by_alias=by_alias, + include=value_include and value_include.for_element(field_key), + exclude=value_exclude and value_exclude.for_element(field_key), + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + yield dict_key, v + + +def _copy_and_set_values( + self: Model, + values: dict[str, Any], + fields_set: set[str], + extra: dict[str, Any] | None = None, + private: dict[str, Any] | None = None, + *, + deep: bool, # UP006 +) -> Model: + if deep: + # chances of having empty dict here are quite low for using smart_deepcopy + values = deepcopy(values) + extra = deepcopy(extra) + private = deepcopy(private) + + cls = self.__class__ + m = cls.__new__(cls) + _object_setattr(m, '__dict__', values) + _object_setattr(m, '__pydantic_extra__', extra) + _object_setattr(m, '__pydantic_fields_set__', fields_set) + _object_setattr(m, '__pydantic_private__', private) + + return m + + +@typing.no_type_check +def _get_value( + cls: type[BaseModel], + v: Any, + to_dict: bool, + by_alias: bool, + include: AbstractSetIntStr | MappingIntStrAny | None, + exclude: AbstractSetIntStr | MappingIntStrAny | None, + exclude_unset: bool, + exclude_defaults: bool, + exclude_none: bool, +) -> Any: + from .. import BaseModel + + if isinstance(v, BaseModel): + if to_dict: + return v.model_dump( + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=include, # type: ignore + exclude=exclude, # type: ignore + exclude_none=exclude_none, + ) + else: + return v.copy(include=include, exclude=exclude) + + value_exclude = _utils.ValueItems(v, exclude) if exclude else None + value_include = _utils.ValueItems(v, include) if include else None + + if isinstance(v, dict): + return { + k_: _get_value( + cls, + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(k_), + exclude=value_exclude and value_exclude.for_element(k_), + exclude_none=exclude_none, + ) + for k_, v_ in v.items() + if (not value_exclude or not value_exclude.is_excluded(k_)) + and (not value_include or value_include.is_included(k_)) + } + + elif _utils.sequence_like(v): + seq_args = ( + _get_value( + cls, + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(i), + exclude=value_exclude and value_exclude.for_element(i), + exclude_none=exclude_none, + ) + for i, v_ in enumerate(v) + if (not value_exclude or not value_exclude.is_excluded(i)) + and (not value_include or value_include.is_included(i)) + ) + + return v.__class__(*seq_args) if _typing_extra.is_namedtuple(v.__class__) else v.__class__(seq_args) + + elif isinstance(v, Enum) and getattr(cls.model_config, 'use_enum_values', False): + return v.value + + else: + return v + + +def _calculate_keys( + self: BaseModel, + include: MappingIntStrAny | None, + exclude: MappingIntStrAny | None, + exclude_unset: bool, + update: typing.Dict[str, Any] | None = None, # noqa UP006 +) -> typing.AbstractSet[str] | None: + if include is None and exclude is None and exclude_unset is False: + return None + + keys: typing.AbstractSet[str] + if exclude_unset: + keys = self.__pydantic_fields_set__.copy() + else: + keys = set(self.__dict__.keys()) + keys = keys | (self.__pydantic_extra__ or {}).keys() + + if include is not None: + keys &= include.keys() + + if update: + keys -= update.keys() + + if exclude: + keys -= {k for k, v in exclude.items() if _utils.ValueItems.is_true(v)} + + return keys diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/decorator.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/decorator.py new file mode 100644 index 0000000..36bd069 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/decorator.py @@ -0,0 +1,279 @@ +import warnings +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload + +from typing_extensions import deprecated + +from .._internal import _config, _typing_extra +from ..alias_generators import to_pascal +from ..errors import PydanticUserError +from ..functional_validators import field_validator +from ..main import BaseModel, create_model +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = ('validate_arguments',) + +if TYPE_CHECKING: + AnyCallable = Callable[..., Any] + + AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable) + ConfigType = Union[None, Type[Any], Dict[str, Any]] + + +@overload +def validate_arguments(func: None = None, *, config: 'ConfigType' = None) -> Callable[['AnyCallableT'], 'AnyCallableT']: + ... + + +@overload +def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT': + ... + + +@deprecated( + 'The `validate_arguments` method is deprecated; use `validate_call` instead.', + category=None, +) +def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any: + """Decorator to validate the arguments passed to a function.""" + warnings.warn( + 'The `validate_arguments` method is deprecated; use `validate_call` instead.', + PydanticDeprecatedSince20, + stacklevel=2, + ) + + def validate(_func: 'AnyCallable') -> 'AnyCallable': + vd = ValidatedFunction(_func, config) + + @wraps(_func) + def wrapper_function(*args: Any, **kwargs: Any) -> Any: + return vd.call(*args, **kwargs) + + wrapper_function.vd = vd # type: ignore + wrapper_function.validate = vd.init_model_instance # type: ignore + wrapper_function.raw_function = vd.raw_function # type: ignore + wrapper_function.model = vd.model # type: ignore + return wrapper_function + + if func: + return validate(func) + else: + return validate + + +ALT_V_ARGS = 'v__args' +ALT_V_KWARGS = 'v__kwargs' +V_POSITIONAL_ONLY_NAME = 'v__positional_only' +V_DUPLICATE_KWARGS = 'v__duplicate_kwargs' + + +class ValidatedFunction: + def __init__(self, function: 'AnyCallable', config: 'ConfigType'): + from inspect import Parameter, signature + + parameters: Mapping[str, Parameter] = signature(function).parameters + + if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}: + raise PydanticUserError( + f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" ' + f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator', + code=None, + ) + + self.raw_function = function + self.arg_mapping: Dict[int, str] = {} + self.positional_only_args: set[str] = set() + self.v_args_name = 'args' + self.v_kwargs_name = 'kwargs' + + type_hints = _typing_extra.get_type_hints(function, include_extras=True) + takes_args = False + takes_kwargs = False + fields: Dict[str, Tuple[Any, Any]] = {} + for i, (name, p) in enumerate(parameters.items()): + if p.annotation is p.empty: + annotation = Any + else: + annotation = type_hints[name] + + default = ... if p.default is p.empty else p.default + if p.kind == Parameter.POSITIONAL_ONLY: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_POSITIONAL_ONLY_NAME] = List[str], None + self.positional_only_args.add(name) + elif p.kind == Parameter.POSITIONAL_OR_KEYWORD: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_DUPLICATE_KWARGS] = List[str], None + elif p.kind == Parameter.KEYWORD_ONLY: + fields[name] = annotation, default + elif p.kind == Parameter.VAR_POSITIONAL: + self.v_args_name = name + fields[name] = Tuple[annotation, ...], None + takes_args = True + else: + assert p.kind == Parameter.VAR_KEYWORD, p.kind + self.v_kwargs_name = name + fields[name] = Dict[str, annotation], None + takes_kwargs = True + + # these checks avoid a clash between "args" and a field with that name + if not takes_args and self.v_args_name in fields: + self.v_args_name = ALT_V_ARGS + + # same with "kwargs" + if not takes_kwargs and self.v_kwargs_name in fields: + self.v_kwargs_name = ALT_V_KWARGS + + if not takes_args: + # we add the field so validation below can raise the correct exception + fields[self.v_args_name] = List[Any], None + + if not takes_kwargs: + # same with kwargs + fields[self.v_kwargs_name] = Dict[Any, Any], None + + self.create_model(fields, takes_args, takes_kwargs, config) + + def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel: + values = self.build_values(args, kwargs) + return self.model(**values) + + def call(self, *args: Any, **kwargs: Any) -> Any: + m = self.init_model_instance(*args, **kwargs) + return self.execute(m) + + def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[str, Any]: + values: Dict[str, Any] = {} + if args: + arg_iter = enumerate(args) + while True: + try: + i, a = next(arg_iter) + except StopIteration: + break + arg_name = self.arg_mapping.get(i) + if arg_name is not None: + values[arg_name] = a + else: + values[self.v_args_name] = [a] + [a for _, a in arg_iter] + break + + var_kwargs: Dict[str, Any] = {} + wrong_positional_args = [] + duplicate_kwargs = [] + fields_alias = [ + field.alias + for name, field in self.model.model_fields.items() + if name not in (self.v_args_name, self.v_kwargs_name) + ] + non_var_fields = set(self.model.model_fields) - {self.v_args_name, self.v_kwargs_name} + for k, v in kwargs.items(): + if k in non_var_fields or k in fields_alias: + if k in self.positional_only_args: + wrong_positional_args.append(k) + if k in values: + duplicate_kwargs.append(k) + values[k] = v + else: + var_kwargs[k] = v + + if var_kwargs: + values[self.v_kwargs_name] = var_kwargs + if wrong_positional_args: + values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args + if duplicate_kwargs: + values[V_DUPLICATE_KWARGS] = duplicate_kwargs + return values + + def execute(self, m: BaseModel) -> Any: + d = {k: v for k, v in m.__dict__.items() if k in m.__pydantic_fields_set__ or m.model_fields[k].default_factory} + var_kwargs = d.pop(self.v_kwargs_name, {}) + + if self.v_args_name in d: + args_: List[Any] = [] + in_kwargs = False + kwargs = {} + for name, value in d.items(): + if in_kwargs: + kwargs[name] = value + elif name == self.v_args_name: + args_ += value + in_kwargs = True + else: + args_.append(value) + return self.raw_function(*args_, **kwargs, **var_kwargs) + elif self.positional_only_args: + args_ = [] + kwargs = {} + for name, value in d.items(): + if name in self.positional_only_args: + args_.append(value) + else: + kwargs[name] = value + return self.raw_function(*args_, **kwargs, **var_kwargs) + else: + return self.raw_function(**d, **var_kwargs) + + def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None: + pos_args = len(self.arg_mapping) + + config_wrapper = _config.ConfigWrapper(config) + + if config_wrapper.alias_generator: + raise PydanticUserError( + 'Setting the "alias_generator" property on custom Config for ' + '@validate_arguments is not yet supported, please remove.', + code=None, + ) + if config_wrapper.extra is None: + config_wrapper.config_dict['extra'] = 'forbid' + + class DecoratorBaseModel(BaseModel): + @field_validator(self.v_args_name, check_fields=False) + @classmethod + def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]: + if takes_args or v is None: + return v + + raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given') + + @field_validator(self.v_kwargs_name, check_fields=False) + @classmethod + def check_kwargs(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: + if takes_kwargs or v is None: + return v + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v.keys())) + raise TypeError(f'unexpected keyword argument{plural}: {keys}') + + @field_validator(V_POSITIONAL_ONLY_NAME, check_fields=False) + @classmethod + def check_positional_only(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}') + + @field_validator(V_DUPLICATE_KWARGS, check_fields=False) + @classmethod + def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'multiple values for argument{plural}: {keys}') + + model_config = config_wrapper.config_dict + + self.model = create_model(to_pascal(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/json.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/json.py new file mode 100644 index 0000000..79e2f44 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/json.py @@ -0,0 +1,140 @@ +import datetime +import warnings +from collections import deque +from decimal import Decimal +from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from re import Pattern +from types import GeneratorType +from typing import TYPE_CHECKING, Any, Callable, Dict, Type, Union +from uuid import UUID + +from typing_extensions import deprecated + +from ..color import Color +from ..networks import NameEmail +from ..types import SecretBytes, SecretStr +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat' + + +def isoformat(o: Union[datetime.date, datetime.time]) -> str: + return o.isoformat() + + +def decimal_encoder(dec_value: Decimal) -> Union[int, float]: + """Encodes a Decimal as int of there's no exponent, otherwise float. + + This is useful when we use ConstrainedDecimal to represent Numeric(x,0) + where a integer (but not int typed) is used. Encoding this as a float + results in failed round-tripping between encode and parse. + Our Id type is a prime example of this. + + >>> decimal_encoder(Decimal("1.0")) + 1.0 + + >>> decimal_encoder(Decimal("1")) + 1 + """ + exponent = dec_value.as_tuple().exponent + if isinstance(exponent, int) and exponent >= 0: + return int(dec_value) + else: + return float(dec_value) + + +ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { + bytes: lambda o: o.decode(), + Color: str, + datetime.date: isoformat, + datetime.datetime: isoformat, + datetime.time: isoformat, + datetime.timedelta: lambda td: td.total_seconds(), + Decimal: decimal_encoder, + Enum: lambda o: o.value, + frozenset: list, + deque: list, + GeneratorType: list, + IPv4Address: str, + IPv4Interface: str, + IPv4Network: str, + IPv6Address: str, + IPv6Interface: str, + IPv6Network: str, + NameEmail: str, + Path: str, + Pattern: lambda o: o.pattern, + SecretBytes: str, + SecretStr: str, + set: list, + UUID: str, +} + + +@deprecated( + '`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.', + category=None, +) +def pydantic_encoder(obj: Any) -> Any: + warnings.warn( + '`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from dataclasses import asdict, is_dataclass + + from ..main import BaseModel + + if isinstance(obj, BaseModel): + return obj.model_dump() + elif is_dataclass(obj): + return asdict(obj) + + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = ENCODERS_BY_TYPE[base] + except KeyError: + continue + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable") + + +# TODO: Add a suggested migration path once there is a way to use custom encoders +@deprecated( + '`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.', + category=None, +) +def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any: + warnings.warn( + '`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = type_encoders[base] + except KeyError: + continue + + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + return pydantic_encoder(obj) + + +@deprecated('`timedelta_isoformat` is deprecated.', category=None) +def timedelta_isoformat(td: datetime.timedelta) -> str: + """ISO 8601 encoding for Python timedelta object.""" + warnings.warn('`timedelta_isoformat` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2) + minutes, seconds = divmod(td.seconds, 60) + hours, minutes = divmod(minutes, 60) + return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S' diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/parse.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/parse.py new file mode 100644 index 0000000..2a92e62 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/parse.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import json +import pickle +import warnings +from enum import Enum +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable + +from typing_extensions import deprecated + +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + + +class Protocol(str, Enum): + json = 'json' + pickle = 'pickle' + + +@deprecated('`load_str_bytes` is deprecated.', category=None) +def load_str_bytes( + b: str | bytes, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: Protocol | None = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, +) -> Any: + warnings.warn('`load_str_bytes` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2) + if proto is None and content_type: + if content_type.endswith(('json', 'javascript')): + pass + elif allow_pickle and content_type.endswith('pickle'): + proto = Protocol.pickle + else: + raise TypeError(f'Unknown content-type: {content_type}') + + proto = proto or Protocol.json + + if proto == Protocol.json: + if isinstance(b, bytes): + b = b.decode(encoding) + return json_loads(b) # type: ignore + elif proto == Protocol.pickle: + if not allow_pickle: + raise RuntimeError('Trying to decode with pickle with allow_pickle=False') + bb = b if isinstance(b, bytes) else b.encode() # type: ignore + return pickle.loads(bb) + else: + raise TypeError(f'Unknown protocol: {proto}') + + +@deprecated('`load_file` is deprecated.', category=None) +def load_file( + path: str | Path, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: Protocol | None = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, +) -> Any: + warnings.warn('`load_file` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2) + path = Path(path) + b = path.read_bytes() + if content_type is None: + if path.suffix in ('.js', '.json'): + proto = Protocol.json + elif path.suffix == '.pkl': + proto = Protocol.pickle + + return load_str_bytes( + b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/tools.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/tools.py new file mode 100644 index 0000000..b04eae4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/deprecated/tools.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import json +import warnings +from typing import TYPE_CHECKING, Any, Callable, Type, TypeVar, Union + +from typing_extensions import deprecated + +from ..json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema +from ..type_adapter import TypeAdapter +from ..warnings import PydanticDeprecatedSince20 + +if not TYPE_CHECKING: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = 'parse_obj_as', 'schema_of', 'schema_json_of' + +NameFactory = Union[str, Callable[[Type[Any]], str]] + + +T = TypeVar('T') + + +@deprecated( + '`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.', + category=None, +) +def parse_obj_as(type_: type[T], obj: Any, type_name: NameFactory | None = None) -> T: + warnings.warn( + '`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + if type_name is not None: # pragma: no cover + warnings.warn( + 'The type_name parameter is deprecated. parse_obj_as no longer creates temporary models', + DeprecationWarning, + stacklevel=2, + ) + return TypeAdapter(type_).validate_python(obj) + + +@deprecated( + '`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + category=None, +) +def schema_of( + type_: Any, + *, + title: NameFactory | None = None, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, +) -> dict[str, Any]: + """Generate a JSON schema (as dict) for the passed model or dynamically generated one.""" + warnings.warn( + '`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + res = TypeAdapter(type_).json_schema( + by_alias=by_alias, + schema_generator=schema_generator, + ref_template=ref_template, + ) + if title is not None: + if isinstance(title, str): + res['title'] = title + else: + warnings.warn( + 'Passing a callable for the `title` parameter is deprecated and no longer supported', + DeprecationWarning, + stacklevel=2, + ) + res['title'] = title(type_) + return res + + +@deprecated( + '`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + category=None, +) +def schema_json_of( + type_: Any, + *, + title: NameFactory | None = None, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + **dumps_kwargs: Any, +) -> str: + """Generate a JSON schema (as JSON) for the passed model or dynamically generated one.""" + warnings.warn( + '`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return json.dumps( + schema_of(type_, title=title, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator), + **dumps_kwargs, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/env_settings.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/env_settings.py new file mode 100644 index 0000000..662f590 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/env_settings.py @@ -0,0 +1,4 @@ +"""The `env_settings` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/error_wrappers.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/error_wrappers.py new file mode 100644 index 0000000..5144eee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/error_wrappers.py @@ -0,0 +1,4 @@ +"""The `error_wrappers` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/errors.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/errors.py new file mode 100644 index 0000000..e4fadd8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/errors.py @@ -0,0 +1,153 @@ +"""Pydantic-specific errors.""" +from __future__ import annotations as _annotations + +import re + +from typing_extensions import Literal, Self + +from ._migration import getattr_migration +from .version import version_short + +__all__ = ( + 'PydanticUserError', + 'PydanticUndefinedAnnotation', + 'PydanticImportError', + 'PydanticSchemaGenerationError', + 'PydanticInvalidForJsonSchema', + 'PydanticErrorCodes', +) + +# We use this URL to allow for future flexibility about how we host the docs, while allowing for Pydantic +# code in the while with "old" URLs to still work. +# 'u' refers to "user errors" - e.g. errors caused by developers using pydantic, as opposed to validation errors. +DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/' +PydanticErrorCodes = Literal[ + 'class-not-fully-defined', + 'custom-json-schema', + 'decorator-missing-field', + 'discriminator-no-field', + 'discriminator-alias-type', + 'discriminator-needs-literal', + 'discriminator-alias', + 'discriminator-validator', + 'callable-discriminator-no-tag', + 'typed-dict-version', + 'model-field-overridden', + 'model-field-missing-annotation', + 'config-both', + 'removed-kwargs', + 'invalid-for-json-schema', + 'json-schema-already-used', + 'base-model-instantiated', + 'undefined-annotation', + 'schema-for-unknown-type', + 'import-error', + 'create-model-field-definitions', + 'create-model-config-base', + 'validator-no-fields', + 'validator-invalid-fields', + 'validator-instance-method', + 'root-validator-pre-skip', + 'model-serializer-instance-method', + 'validator-field-config-info', + 'validator-v1-signature', + 'validator-signature', + 'field-serializer-signature', + 'model-serializer-signature', + 'multiple-field-serializers', + 'invalid_annotated_type', + 'type-adapter-config-unused', + 'root-model-extra', + 'unevaluable-type-annotation', + 'dataclass-init-false-extra-allow', + 'clashing-init-and-init-var', + 'model-config-invalid-field-name', +] + + +class PydanticErrorMixin: + """A mixin class for common functionality shared by all Pydantic-specific errors. + + Attributes: + message: A message describing the error. + code: An optional error code from PydanticErrorCodes enum. + """ + + def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None: + self.message = message + self.code = code + + def __str__(self) -> str: + if self.code is None: + return self.message + else: + return f'{self.message}\n\nFor further information visit {DEV_ERROR_DOCS_URL}{self.code}' + + +class PydanticUserError(PydanticErrorMixin, TypeError): + """An error raised due to incorrect use of Pydantic.""" + + +class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError): + """A subclass of `NameError` raised when handling undefined annotations during `CoreSchema` generation. + + Attributes: + name: Name of the error. + message: Description of the error. + """ + + def __init__(self, name: str, message: str) -> None: + self.name = name + super().__init__(message=message, code='undefined-annotation') + + @classmethod + def from_name_error(cls, name_error: NameError) -> Self: + """Convert a `NameError` to a `PydanticUndefinedAnnotation` error. + + Args: + name_error: `NameError` to be converted. + + Returns: + Converted `PydanticUndefinedAnnotation` error. + """ + try: + name = name_error.name # type: ignore # python > 3.10 + except AttributeError: + name = re.search(r".*'(.+?)'", str(name_error)).group(1) # type: ignore[union-attr] + return cls(name=name, message=str(name_error)) + + +class PydanticImportError(PydanticErrorMixin, ImportError): + """An error raised when an import fails due to module changes between V1 and V2. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='import-error') + + +class PydanticSchemaGenerationError(PydanticUserError): + """An error raised during failures to generate a `CoreSchema` for some type. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='schema-for-unknown-type') + + +class PydanticInvalidForJsonSchema(PydanticUserError): + """An error raised during failures to generate a JSON schema for some `CoreSchema`. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='invalid-for-json-schema') + + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/fields.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/fields.py new file mode 100644 index 0000000..312bd92 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/fields.py @@ -0,0 +1,1237 @@ +"""Defining fields on models.""" +from __future__ import annotations as _annotations + +import dataclasses +import inspect +import sys +import typing +from copy import copy +from dataclasses import Field as DataclassField +from functools import cached_property +from typing import Any, ClassVar +from warnings import warn + +import annotated_types +import typing_extensions +from pydantic_core import PydanticUndefined +from typing_extensions import Literal, TypeAlias, Unpack, deprecated + +from . import types +from ._internal import _decorators, _fields, _generics, _internal_dataclass, _repr, _typing_extra, _utils +from .aliases import AliasChoices, AliasPath +from .config import JsonDict +from .errors import PydanticUserError +from .warnings import PydanticDeprecatedSince20 + +if typing.TYPE_CHECKING: + from ._internal._repr import ReprArgs +else: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = 'Field', 'PrivateAttr', 'computed_field' + + +_Unset: Any = PydanticUndefined + +if sys.version_info >= (3, 13): + import warnings + + Deprecated: TypeAlias = warnings.deprecated | deprecated +else: + Deprecated: TypeAlias = deprecated + + +class _FromFieldInfoInputs(typing_extensions.TypedDict, total=False): + """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.from_field`.""" + + annotation: type[Any] | None + default_factory: typing.Callable[[], Any] | None + alias: str | None + alias_priority: int | None + validation_alias: str | AliasPath | AliasChoices | None + serialization_alias: str | None + title: str | None + description: str | None + examples: list[Any] | None + exclude: bool | None + gt: float | None + ge: float | None + lt: float | None + le: float | None + multiple_of: float | None + strict: bool | None + min_length: int | None + max_length: int | None + pattern: str | typing.Pattern[str] | None + allow_inf_nan: bool | None + max_digits: int | None + decimal_places: int | None + union_mode: Literal['smart', 'left_to_right'] | None + discriminator: str | types.Discriminator | None + deprecated: Deprecated | str | bool | None + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None + frozen: bool | None + validate_default: bool | None + repr: bool + init: bool | None + init_var: bool | None + kw_only: bool | None + coerce_numbers_to_str: bool | None + + +class _FieldInfoInputs(_FromFieldInfoInputs, total=False): + """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.__init__`.""" + + default: Any + + +class FieldInfo(_repr.Representation): + """This class holds information about a field. + + `FieldInfo` is used for any field definition regardless of whether the [`Field()`][pydantic.fields.Field] + function is explicitly used. + + !!! warning + You generally shouldn't be creating `FieldInfo` directly, you'll only need to use it when accessing + [`BaseModel`][pydantic.main.BaseModel] `.model_fields` internals. + + Attributes: + annotation: The type annotation of the field. + default: The default value of the field. + default_factory: The factory function used to construct the default for the field. + alias: The alias name of the field. + alias_priority: The priority of the field's alias. + validation_alias: The validation alias of the field. + serialization_alias: The serialization alias of the field. + title: The title of the field. + description: The description of the field. + examples: List of examples of the field. + exclude: Whether to exclude the field from the model serialization. + discriminator: Field name or Discriminator for discriminating the type in a tagged union. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + frozen: Whether the field is frozen. + validate_default: Whether to validate the default value of the field. + repr: Whether to include the field in representation of the model. + init: Whether the field should be included in the constructor of the dataclass. + init_var: Whether the field should _only_ be included in the constructor of the dataclass, and not stored. + kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass. + metadata: List of metadata constraints. + """ + + annotation: type[Any] | None + default: Any + default_factory: typing.Callable[[], Any] | None + alias: str | None + alias_priority: int | None + validation_alias: str | AliasPath | AliasChoices | None + serialization_alias: str | None + title: str | None + description: str | None + examples: list[Any] | None + exclude: bool | None + discriminator: str | types.Discriminator | None + deprecated: Deprecated | str | bool | None + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None + frozen: bool | None + validate_default: bool | None + repr: bool + init: bool | None + init_var: bool | None + kw_only: bool | None + metadata: list[Any] + + __slots__ = ( + 'annotation', + 'default', + 'default_factory', + 'alias', + 'alias_priority', + 'validation_alias', + 'serialization_alias', + 'title', + 'description', + 'examples', + 'exclude', + 'discriminator', + 'deprecated', + 'json_schema_extra', + 'frozen', + 'validate_default', + 'repr', + 'init', + 'init_var', + 'kw_only', + 'metadata', + '_attributes_set', + ) + + # used to convert kwargs to metadata/constraints, + # None has a special meaning - these items are collected into a `PydanticGeneralMetadata` + metadata_lookup: ClassVar[dict[str, typing.Callable[[Any], Any] | None]] = { + 'strict': types.Strict, + 'gt': annotated_types.Gt, + 'ge': annotated_types.Ge, + 'lt': annotated_types.Lt, + 'le': annotated_types.Le, + 'multiple_of': annotated_types.MultipleOf, + 'min_length': annotated_types.MinLen, + 'max_length': annotated_types.MaxLen, + 'pattern': None, + 'allow_inf_nan': None, + 'max_digits': None, + 'decimal_places': None, + 'union_mode': None, + 'coerce_numbers_to_str': None, + } + + def __init__(self, **kwargs: Unpack[_FieldInfoInputs]) -> None: + """This class should generally not be initialized directly; instead, use the `pydantic.fields.Field` function + or one of the constructor classmethods. + + See the signature of `pydantic.fields.Field` for more details about the expected arguments. + """ + self._attributes_set = {k: v for k, v in kwargs.items() if v is not _Unset} + kwargs = {k: _DefaultValues.get(k) if v is _Unset else v for k, v in kwargs.items()} # type: ignore + self.annotation, annotation_metadata = self._extract_metadata(kwargs.get('annotation')) + + default = kwargs.pop('default', PydanticUndefined) + if default is Ellipsis: + self.default = PydanticUndefined + else: + self.default = default + + self.default_factory = kwargs.pop('default_factory', None) + + if self.default is not PydanticUndefined and self.default_factory is not None: + raise TypeError('cannot specify both default and default_factory') + + self.title = kwargs.pop('title', None) + self.alias = kwargs.pop('alias', None) + self.validation_alias = kwargs.pop('validation_alias', None) + self.serialization_alias = kwargs.pop('serialization_alias', None) + alias_is_set = any(alias is not None for alias in (self.alias, self.validation_alias, self.serialization_alias)) + self.alias_priority = kwargs.pop('alias_priority', None) or 2 if alias_is_set else None + self.description = kwargs.pop('description', None) + self.examples = kwargs.pop('examples', None) + self.exclude = kwargs.pop('exclude', None) + self.discriminator = kwargs.pop('discriminator', None) + # For compatibility with FastAPI<=0.110.0, we preserve the existing value if it is not overridden + self.deprecated = kwargs.pop('deprecated', getattr(self, 'deprecated', None)) + self.repr = kwargs.pop('repr', True) + self.json_schema_extra = kwargs.pop('json_schema_extra', None) + self.validate_default = kwargs.pop('validate_default', None) + self.frozen = kwargs.pop('frozen', None) + # currently only used on dataclasses + self.init = kwargs.pop('init', None) + self.init_var = kwargs.pop('init_var', None) + self.kw_only = kwargs.pop('kw_only', None) + + self.metadata = self._collect_metadata(kwargs) + annotation_metadata # type: ignore + + @staticmethod + def from_field(default: Any = PydanticUndefined, **kwargs: Unpack[_FromFieldInfoInputs]) -> FieldInfo: + """Create a new `FieldInfo` object with the `Field` function. + + Args: + default: The default value for the field. Defaults to Undefined. + **kwargs: Additional arguments dictionary. + + Raises: + TypeError: If 'annotation' is passed as a keyword argument. + + Returns: + A new FieldInfo object with the given parameters. + + Example: + This is how you can create a field with default value like this: + + ```python + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int = pydantic.Field(4) + ``` + """ + if 'annotation' in kwargs: + raise TypeError('"annotation" is not permitted as a Field keyword argument') + return FieldInfo(default=default, **kwargs) + + @staticmethod + def from_annotation(annotation: type[Any]) -> FieldInfo: + """Creates a `FieldInfo` instance from a bare annotation. + + This function is used internally to create a `FieldInfo` from a bare annotation like this: + + ```python + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int # <-- like this + ``` + + We also account for the case where the annotation can be an instance of `Annotated` and where + one of the (not first) arguments in `Annotated` is an instance of `FieldInfo`, e.g.: + + ```python + import annotated_types + from typing_extensions import Annotated + + import pydantic + + class MyModel(pydantic.BaseModel): + foo: Annotated[int, annotated_types.Gt(42)] + bar: Annotated[int, pydantic.Field(gt=42)] + ``` + + Args: + annotation: An annotation object. + + Returns: + An instance of the field metadata. + """ + final = False + if _typing_extra.is_finalvar(annotation): + final = True + if annotation is not typing_extensions.Final: + annotation = typing_extensions.get_args(annotation)[0] + + if _typing_extra.is_annotated(annotation): + first_arg, *extra_args = typing_extensions.get_args(annotation) + if _typing_extra.is_finalvar(first_arg): + final = True + field_info_annotations = [a for a in extra_args if isinstance(a, FieldInfo)] + field_info = FieldInfo.merge_field_infos(*field_info_annotations, annotation=first_arg) + if field_info: + new_field_info = copy(field_info) + new_field_info.annotation = first_arg + new_field_info.frozen = final or field_info.frozen + metadata: list[Any] = [] + for a in extra_args: + if _typing_extra.is_deprecated_instance(a): + new_field_info.deprecated = a.message + elif not isinstance(a, FieldInfo): + metadata.append(a) + else: + metadata.extend(a.metadata) + new_field_info.metadata = metadata + return new_field_info + + return FieldInfo(annotation=annotation, frozen=final or None) + + @staticmethod + def from_annotated_attribute(annotation: type[Any], default: Any) -> FieldInfo: + """Create `FieldInfo` from an annotation with a default value. + + This is used in cases like the following: + + ```python + import annotated_types + from typing_extensions import Annotated + + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int = 4 # <-- like this + bar: Annotated[int, annotated_types.Gt(4)] = 4 # <-- or this + spam: Annotated[int, pydantic.Field(gt=4)] = 4 # <-- or this + ``` + + Args: + annotation: The type annotation of the field. + default: The default value of the field. + + Returns: + A field object with the passed values. + """ + if annotation is default: + raise PydanticUserError( + 'Error when building FieldInfo from annotated attribute. ' + "Make sure you don't have any field name clashing with a type annotation ", + code='unevaluable-type-annotation', + ) + + final = False + if _typing_extra.is_finalvar(annotation): + final = True + if annotation is not typing_extensions.Final: + annotation = typing_extensions.get_args(annotation)[0] + + if isinstance(default, FieldInfo): + default.annotation, annotation_metadata = FieldInfo._extract_metadata(annotation) + default.metadata += annotation_metadata + default = default.merge_field_infos( + *[x for x in annotation_metadata if isinstance(x, FieldInfo)], default, annotation=default.annotation + ) + default.frozen = final or default.frozen + return default + elif isinstance(default, dataclasses.Field): + init_var = False + if annotation is dataclasses.InitVar: + init_var = True + annotation = Any + elif isinstance(annotation, dataclasses.InitVar): + init_var = True + annotation = annotation.type + pydantic_field = FieldInfo._from_dataclass_field(default) + pydantic_field.annotation, annotation_metadata = FieldInfo._extract_metadata(annotation) + pydantic_field.metadata += annotation_metadata + pydantic_field = pydantic_field.merge_field_infos( + *[x for x in annotation_metadata if isinstance(x, FieldInfo)], + pydantic_field, + annotation=pydantic_field.annotation, + ) + pydantic_field.frozen = final or pydantic_field.frozen + pydantic_field.init_var = init_var + pydantic_field.init = getattr(default, 'init', None) + pydantic_field.kw_only = getattr(default, 'kw_only', None) + return pydantic_field + else: + if _typing_extra.is_annotated(annotation): + first_arg, *extra_args = typing_extensions.get_args(annotation) + field_infos = [a for a in extra_args if isinstance(a, FieldInfo)] + field_info = FieldInfo.merge_field_infos(*field_infos, annotation=first_arg, default=default) + metadata: list[Any] = [] + for a in extra_args: + if _typing_extra.is_deprecated_instance(a): + field_info.deprecated = a.message + elif not isinstance(a, FieldInfo): + metadata.append(a) + else: + metadata.extend(a.metadata) + field_info.metadata = metadata + return field_info + + return FieldInfo(annotation=annotation, default=default, frozen=final or None) + + @staticmethod + def merge_field_infos(*field_infos: FieldInfo, **overrides: Any) -> FieldInfo: + """Merge `FieldInfo` instances keeping only explicitly set attributes. + + Later `FieldInfo` instances override earlier ones. + + Returns: + FieldInfo: A merged FieldInfo instance. + """ + flattened_field_infos: list[FieldInfo] = [] + for field_info in field_infos: + flattened_field_infos.extend(x for x in field_info.metadata if isinstance(x, FieldInfo)) + flattened_field_infos.append(field_info) + field_infos = tuple(flattened_field_infos) + if len(field_infos) == 1: + # No merging necessary, but we still need to make a copy and apply the overrides + field_info = copy(field_infos[0]) + field_info._attributes_set.update(overrides) + + default_override = overrides.pop('default', PydanticUndefined) + if default_override is Ellipsis: + default_override = PydanticUndefined + if default_override is not PydanticUndefined: + field_info.default = default_override + + for k, v in overrides.items(): + setattr(field_info, k, v) + return field_info # type: ignore + + new_kwargs: dict[str, Any] = {} + metadata = {} + for field_info in field_infos: + new_kwargs.update(field_info._attributes_set) + for x in field_info.metadata: + if not isinstance(x, FieldInfo): + metadata[type(x)] = x + new_kwargs.update(overrides) + field_info = FieldInfo(**new_kwargs) + field_info.metadata = list(metadata.values()) + return field_info + + @staticmethod + def _from_dataclass_field(dc_field: DataclassField[Any]) -> FieldInfo: + """Return a new `FieldInfo` instance from a `dataclasses.Field` instance. + + Args: + dc_field: The `dataclasses.Field` instance to convert. + + Returns: + The corresponding `FieldInfo` instance. + + Raises: + TypeError: If any of the `FieldInfo` kwargs does not match the `dataclass.Field` kwargs. + """ + default = dc_field.default + if default is dataclasses.MISSING: + default = PydanticUndefined + + if dc_field.default_factory is dataclasses.MISSING: + default_factory: typing.Callable[[], Any] | None = None + else: + default_factory = dc_field.default_factory + + # use the `Field` function so in correct kwargs raise the correct `TypeError` + dc_field_metadata = {k: v for k, v in dc_field.metadata.items() if k in _FIELD_ARG_NAMES} + return Field(default=default, default_factory=default_factory, repr=dc_field.repr, **dc_field_metadata) + + @staticmethod + def _extract_metadata(annotation: type[Any] | None) -> tuple[type[Any] | None, list[Any]]: + """Tries to extract metadata/constraints from an annotation if it uses `Annotated`. + + Args: + annotation: The type hint annotation for which metadata has to be extracted. + + Returns: + A tuple containing the extracted metadata type and the list of extra arguments. + """ + if annotation is not None: + if _typing_extra.is_annotated(annotation): + first_arg, *extra_args = typing_extensions.get_args(annotation) + return first_arg, list(extra_args) + + return annotation, [] + + @staticmethod + def _collect_metadata(kwargs: dict[str, Any]) -> list[Any]: + """Collect annotations from kwargs. + + Args: + kwargs: Keyword arguments passed to the function. + + Returns: + A list of metadata objects - a combination of `annotated_types.BaseMetadata` and + `PydanticMetadata`. + """ + metadata: list[Any] = [] + general_metadata = {} + for key, value in list(kwargs.items()): + try: + marker = FieldInfo.metadata_lookup[key] + except KeyError: + continue + + del kwargs[key] + if value is not None: + if marker is None: + general_metadata[key] = value + else: + metadata.append(marker(value)) + if general_metadata: + metadata.append(_fields.pydantic_general_metadata(**general_metadata)) + return metadata + + @property + def deprecation_message(self) -> str | None: + """The deprecation message to be emitted, or `None` if not set.""" + if self.deprecated is None: + return None + if isinstance(self.deprecated, bool): + return 'deprecated' if self.deprecated else None + return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message + + def get_default(self, *, call_default_factory: bool = False) -> Any: + """Get the default value. + + We expose an option for whether to call the default_factory (if present), as calling it may + result in side effects that we want to avoid. However, there are times when it really should + be called (namely, when instantiating a model via `model_construct`). + + Args: + call_default_factory: Whether to call the default_factory or not. Defaults to `False`. + + Returns: + The default value, calling the default factory if requested or `None` if not set. + """ + if self.default_factory is None: + return _utils.smart_deepcopy(self.default) + elif call_default_factory: + return self.default_factory() + else: + return None + + def is_required(self) -> bool: + """Check if the field is required (i.e., does not have a default value or factory). + + Returns: + `True` if the field is required, `False` otherwise. + """ + return self.default is PydanticUndefined and self.default_factory is None + + def rebuild_annotation(self) -> Any: + """Attempts to rebuild the original annotation for use in function signatures. + + If metadata is present, it adds it to the original annotation using + `Annotated`. Otherwise, it returns the original annotation as-is. + + Note that because the metadata has been flattened, the original annotation + may not be reconstructed exactly as originally provided, e.g. if the original + type had unrecognized annotations, or was annotated with a call to `pydantic.Field`. + + Returns: + The rebuilt annotation. + """ + if not self.metadata: + return self.annotation + else: + # Annotated arguments must be a tuple + return typing_extensions.Annotated[(self.annotation, *self.metadata)] # type: ignore + + def apply_typevars_map(self, typevars_map: dict[Any, Any] | None, types_namespace: dict[str, Any] | None) -> None: + """Apply a `typevars_map` to the annotation. + + This method is used when analyzing parametrized generic types to replace typevars with their concrete types. + + This method applies the `typevars_map` to the annotation in place. + + Args: + typevars_map: A dictionary mapping type variables to their concrete types. + types_namespace (dict | None): A dictionary containing related types to the annotated type. + + See Also: + pydantic._internal._generics.replace_types is used for replacing the typevars with + their concrete types. + """ + annotation = _typing_extra.eval_type_lenient(self.annotation, types_namespace) + self.annotation = _generics.replace_types(annotation, typevars_map) + + def __repr_args__(self) -> ReprArgs: + yield 'annotation', _repr.PlainRepr(_repr.display_as_type(self.annotation)) + yield 'required', self.is_required() + + for s in self.__slots__: + if s == '_attributes_set': + continue + if s == 'annotation': + continue + elif s == 'metadata' and not self.metadata: + continue + elif s == 'repr' and self.repr is True: + continue + if s == 'frozen' and self.frozen is False: + continue + if s == 'validation_alias' and self.validation_alias == self.alias: + continue + if s == 'serialization_alias' and self.serialization_alias == self.alias: + continue + if s == 'default' and self.default is not PydanticUndefined: + yield 'default', self.default + elif s == 'default_factory' and self.default_factory is not None: + yield 'default_factory', _repr.PlainRepr(_repr.display_as_type(self.default_factory)) + else: + value = getattr(self, s) + if value is not None and value is not PydanticUndefined: + yield s, value + + +class _EmptyKwargs(typing_extensions.TypedDict): + """This class exists solely to ensure that type checking warns about passing `**extra` in `Field`.""" + + +_DefaultValues = dict( + default=..., + default_factory=None, + alias=None, + alias_priority=None, + validation_alias=None, + serialization_alias=None, + title=None, + description=None, + examples=None, + exclude=None, + discriminator=None, + json_schema_extra=None, + frozen=None, + validate_default=None, + repr=True, + init=None, + init_var=None, + kw_only=None, + pattern=None, + strict=None, + gt=None, + ge=None, + lt=None, + le=None, + multiple_of=None, + allow_inf_nan=None, + max_digits=None, + decimal_places=None, + min_length=None, + max_length=None, + coerce_numbers_to_str=None, +) + + +def Field( # noqa: C901 + default: Any = PydanticUndefined, + *, + default_factory: typing.Callable[[], Any] | None = _Unset, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: bool | None = _Unset, + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | typing.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: float | None = _Unset, + ge: float | None = _Unset, + lt: float | None = _Unset, + le: float | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/fields + + Create a field for objects that can be configured. + + Used to provide extra information about a field, either for the model schema or complex validation. Some arguments + apply only to number fields (`int`, `float`, `Decimal`) and some apply only to `str`. + + Note: + - Any `_Unset` objects will be replaced by the corresponding value defined in the `_DefaultValues` dictionary. If a key for the `_Unset` object is not found in the `_DefaultValues` dictionary, it will default to `None` + + Args: + default: Default value if the field is not set. + default_factory: A callable to generate the default value, such as :func:`~datetime.utcnow`. + alias: The name to use for the attribute when validating or serializing by alias. + This is often used for things like converting between snake and camel case. + alias_priority: Priority of the alias. This affects whether an alias generator is used. + validation_alias: Like `alias`, but only affects validation, not serialization. + serialization_alias: Like `alias`, but only affects serialization, not validation. + title: Human-readable title. + description: Human-readable description. + examples: Example values for this field. + exclude: Whether to exclude the field from the model serialization. + discriminator: Field name or Discriminator for discriminating the type in a tagged union. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + frozen: Whether the field is frozen. If true, attempts to change the value on an instance will raise an error. + validate_default: If `True`, apply validation to the default value every time you create an instance. + Otherwise, for performance reasons, the default value of the field is trusted and not validated. + repr: A boolean indicating whether to include the field in the `__repr__` output. + init: Whether the field should be included in the constructor of the dataclass. + (Only applies to dataclasses.) + init_var: Whether the field should _only_ be included in the constructor of the dataclass. + (Only applies to dataclasses.) + kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass. + (Only applies to dataclasses.) + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + strict: If `True`, strict validation is applied to the field. + See [Strict Mode](../concepts/strict_mode.md) for details. + gt: Greater than. If set, value must be greater than this. Only applicable to numbers. + ge: Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers. + lt: Less than. If set, value must be less than this. Only applicable to numbers. + le: Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers. + multiple_of: Value must be a multiple of this. Only applicable to numbers. + min_length: Minimum length for iterables. + max_length: Maximum length for iterables. + pattern: Pattern for strings (a regular expression). + allow_inf_nan: Allow `inf`, `-inf`, `nan`. Only applicable to numbers. + max_digits: Maximum number of allow digits for strings. + decimal_places: Maximum number of decimal places allowed for numbers. + union_mode: The strategy to apply when validating a union. Can be `smart` (the default), or `left_to_right`. + See [Union Mode](standard_library_types.md#union-mode) for details. + extra: (Deprecated) Extra fields that will be included in the JSON schema. + + !!! warning Deprecated + The `extra` kwargs is deprecated. Use `json_schema_extra` instead. + + Returns: + A new [`FieldInfo`][pydantic.fields.FieldInfo]. The return annotation is `Any` so `Field` can be used on + type-annotated fields without causing a type error. + """ + # Check deprecated and removed params from V1. This logic should eventually be removed. + const = extra.pop('const', None) # type: ignore + if const is not None: + raise PydanticUserError('`const` is removed, use `Literal` instead', code='removed-kwargs') + + min_items = extra.pop('min_items', None) # type: ignore + if min_items is not None: + warn('`min_items` is deprecated and will be removed, use `min_length` instead', DeprecationWarning) + if min_length in (None, _Unset): + min_length = min_items # type: ignore + + max_items = extra.pop('max_items', None) # type: ignore + if max_items is not None: + warn('`max_items` is deprecated and will be removed, use `max_length` instead', DeprecationWarning) + if max_length in (None, _Unset): + max_length = max_items # type: ignore + + unique_items = extra.pop('unique_items', None) # type: ignore + if unique_items is not None: + raise PydanticUserError( + ( + '`unique_items` is removed, use `Set` instead' + '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)' + ), + code='removed-kwargs', + ) + + allow_mutation = extra.pop('allow_mutation', None) # type: ignore + if allow_mutation is not None: + warn('`allow_mutation` is deprecated and will be removed. use `frozen` instead', DeprecationWarning) + if allow_mutation is False: + frozen = True + + regex = extra.pop('regex', None) # type: ignore + if regex is not None: + raise PydanticUserError('`regex` is removed. use `pattern` instead', code='removed-kwargs') + + if isinstance(pattern, typing.Pattern): + pattern = pattern.pattern + + if extra: + warn( + 'Using extra keyword arguments on `Field` is deprecated and will be removed.' + ' Use `json_schema_extra` instead.' + f' (Extra keys: {", ".join(k.__repr__() for k in extra.keys())})', + DeprecationWarning, + ) + if not json_schema_extra or json_schema_extra is _Unset: + json_schema_extra = extra # type: ignore + + if ( + validation_alias + and validation_alias is not _Unset + and not isinstance(validation_alias, (str, AliasChoices, AliasPath)) + ): + raise TypeError('Invalid `validation_alias` type. it should be `str`, `AliasChoices`, or `AliasPath`') + + if serialization_alias in (_Unset, None) and isinstance(alias, str): + serialization_alias = alias + + if validation_alias in (_Unset, None): + validation_alias = alias + + include = extra.pop('include', None) # type: ignore + if include is not None: + warn('`include` is deprecated and does nothing. It will be removed, use `exclude` instead', DeprecationWarning) + + return FieldInfo.from_field( + default, + default_factory=default_factory, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + description=description, + examples=examples, + exclude=exclude, + discriminator=discriminator, + deprecated=deprecated, + json_schema_extra=json_schema_extra, + frozen=frozen, + pattern=pattern, + validate_default=validate_default, + repr=repr, + init=init, + init_var=init_var, + kw_only=kw_only, + coerce_numbers_to_str=coerce_numbers_to_str, + strict=strict, + gt=gt, + ge=ge, + lt=lt, + le=le, + multiple_of=multiple_of, + min_length=min_length, + max_length=max_length, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + union_mode=union_mode, + ) + + +_FIELD_ARG_NAMES = set(inspect.signature(Field).parameters) +_FIELD_ARG_NAMES.remove('extra') # do not include the varkwargs parameter + + +class ModelPrivateAttr(_repr.Representation): + """A descriptor for private attributes in class models. + + !!! warning + You generally shouldn't be creating `ModelPrivateAttr` instances directly, instead use + `pydantic.fields.PrivateAttr`. (This is similar to `FieldInfo` vs. `Field`.) + + Attributes: + default: The default value of the attribute if not provided. + default_factory: A callable function that generates the default value of the + attribute if not provided. + """ + + __slots__ = 'default', 'default_factory' + + def __init__( + self, default: Any = PydanticUndefined, *, default_factory: typing.Callable[[], Any] | None = None + ) -> None: + self.default = default + self.default_factory = default_factory + + if not typing.TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + + def __getattr__(self, item: str) -> Any: + """This function improves compatibility with custom descriptors by ensuring delegation happens + as expected when the default value of a private attribute is a descriptor. + """ + if item in {'__get__', '__set__', '__delete__'}: + if hasattr(self.default, item): + return getattr(self.default, item) + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + def __set_name__(self, cls: type[Any], name: str) -> None: + """Preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487.""" + if self.default is PydanticUndefined: + return + if not hasattr(self.default, '__set_name__'): + return + set_name = self.default.__set_name__ + if callable(set_name): + set_name(cls, name) + + def get_default(self) -> Any: + """Retrieve the default value of the object. + + If `self.default_factory` is `None`, the method will return a deep copy of the `self.default` object. + + If `self.default_factory` is not `None`, it will call `self.default_factory` and return the value returned. + + Returns: + The default value of the object. + """ + return _utils.smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and (self.default, self.default_factory) == ( + other.default, + other.default_factory, + ) + + +def PrivateAttr( + default: Any = PydanticUndefined, + *, + default_factory: typing.Callable[[], Any] | None = None, +) -> Any: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/models/#private-model-attributes + + Indicates that an attribute is intended for private use and not handled during normal validation/serialization. + + Private attributes are not validated by Pydantic, so it's up to you to ensure they are used in a type-safe manner. + + Private attributes are stored in `__private_attributes__` on the model. + + Args: + default: The attribute's default value. Defaults to Undefined. + default_factory: Callable that will be + called when a default value is needed for this attribute. + If both `default` and `default_factory` are set, an error will be raised. + + Returns: + An instance of [`ModelPrivateAttr`][pydantic.fields.ModelPrivateAttr] class. + + Raises: + ValueError: If both `default` and `default_factory` are set. + """ + if default is not PydanticUndefined and default_factory is not None: + raise TypeError('cannot specify both default and default_factory') + + return ModelPrivateAttr( + default, + default_factory=default_factory, + ) + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class ComputedFieldInfo: + """A container for data from `@computed_field` so that we can access it while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@computed_field'. + wrapped_property: The wrapped computed field property. + return_type: The type of the computed field property's return value. + alias: The alias of the property to be used during serialization. + alias_priority: The priority of the alias. This affects whether an alias generator is used. + title: Title of the computed field to include in the serialization JSON schema. + description: Description of the computed field to include in the serialization JSON schema. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + examples: Example values of the computed field to include in the serialization JSON schema. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + repr: A boolean indicating whether to include the field in the __repr__ output. + """ + + decorator_repr: ClassVar[str] = '@computed_field' + wrapped_property: property + return_type: Any + alias: str | None + alias_priority: int | None + title: str | None + description: str | None + deprecated: Deprecated | str | bool | None + examples: list[Any] | None + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None + repr: bool + + @property + def deprecation_message(self) -> str | None: + """The deprecation message to be emitted, or `None` if not set.""" + if self.deprecated is None: + return None + if isinstance(self.deprecated, bool): + return 'deprecated' if self.deprecated else None + return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message + + +def _wrapped_property_is_private(property_: cached_property | property) -> bool: # type: ignore + """Returns true if provided property is private, False otherwise.""" + wrapped_name: str = '' + + if isinstance(property_, property): + wrapped_name = getattr(property_.fget, '__name__', '') + elif isinstance(property_, cached_property): # type: ignore + wrapped_name = getattr(property_.func, '__name__', '') # type: ignore + + return wrapped_name.startswith('_') and not wrapped_name.startswith('__') + + +# this should really be `property[T], cached_property[T]` but property is not generic unlike cached_property +# See https://github.com/python/typing/issues/985 and linked issues +PropertyT = typing.TypeVar('PropertyT') + + +@typing.overload +def computed_field( + *, + alias: str | None = None, + alias_priority: int | None = None, + title: str | None = None, + description: str | None = None, + deprecated: Deprecated | str | bool | None = None, + examples: list[Any] | None = None, + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None = None, + repr: bool = True, + return_type: Any = PydanticUndefined, +) -> typing.Callable[[PropertyT], PropertyT]: + ... + + +@typing.overload +def computed_field(__func: PropertyT) -> PropertyT: + ... + + +def computed_field( + func: PropertyT | None = None, + /, + *, + alias: str | None = None, + alias_priority: int | None = None, + title: str | None = None, + description: str | None = None, + deprecated: Deprecated | str | bool | None = None, + examples: list[Any] | None = None, + json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None = None, + repr: bool | None = None, + return_type: Any = PydanticUndefined, +) -> PropertyT | typing.Callable[[PropertyT], PropertyT]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/fields#the-computed_field-decorator + + Decorator to include `property` and `cached_property` when serializing models or dataclasses. + + This is useful for fields that are computed from other fields, or for fields that are expensive to compute and should be cached. + + ```py + from pydantic import BaseModel, computed_field + + class Rectangle(BaseModel): + width: int + length: int + + @computed_field + @property + def area(self) -> int: + return self.width * self.length + + print(Rectangle(width=3, length=2).model_dump()) + #> {'width': 3, 'length': 2, 'area': 6} + ``` + + If applied to functions not yet decorated with `@property` or `@cached_property`, the function is + automatically wrapped with `property`. Although this is more concise, you will lose IntelliSense in your IDE, + and confuse static type checkers, thus explicit use of `@property` is recommended. + + !!! warning "Mypy Warning" + Even with the `@property` or `@cached_property` applied to your function before `@computed_field`, + mypy may throw a `Decorated property not supported` error. + See [mypy issue #1362](https://github.com/python/mypy/issues/1362), for more information. + To avoid this error message, add `# type: ignore[misc]` to the `@computed_field` line. + + [pyright](https://github.com/microsoft/pyright) supports `@computed_field` without error. + + ```py + import random + + from pydantic import BaseModel, computed_field + + class Square(BaseModel): + width: float + + @computed_field + def area(self) -> float: # converted to a `property` by `computed_field` + return round(self.width**2, 2) + + @area.setter + def area(self, new_area: float) -> None: + self.width = new_area**0.5 + + @computed_field(alias='the magic number', repr=False) + def random_number(self) -> int: + return random.randint(0, 1_000) + + square = Square(width=1.3) + + # `random_number` does not appear in representation + print(repr(square)) + #> Square(width=1.3, area=1.69) + + print(square.random_number) + #> 3 + + square.area = 4 + + print(square.model_dump_json(by_alias=True)) + #> {"width":2.0,"area":4.0,"the magic number":3} + ``` + + !!! warning "Overriding with `computed_field`" + You can't override a field from a parent class with a `computed_field` in the child class. + `mypy` complains about this behavior if allowed, and `dataclasses` doesn't allow this pattern either. + See the example below: + + ```py + from pydantic import BaseModel, computed_field + + class Parent(BaseModel): + a: str + + try: + + class Child(Parent): + @computed_field + @property + def a(self) -> str: + return 'new a' + + except ValueError as e: + print(repr(e)) + #> ValueError("you can't override a field with a computed field") + ``` + + Private properties decorated with `@computed_field` have `repr=False` by default. + + ```py + from functools import cached_property + + from pydantic import BaseModel, computed_field + + class Model(BaseModel): + foo: int + + @computed_field + @cached_property + def _private_cached_property(self) -> int: + return -self.foo + + @computed_field + @property + def _private_property(self) -> int: + return -self.foo + + m = Model(foo=1) + print(repr(m)) + #> M(foo=1) + ``` + + Args: + func: the function to wrap. + alias: alias to use when serializing this computed field, only used when `by_alias=True` + alias_priority: priority of the alias. This affects whether an alias generator is used + title: Title to use when including this computed field in JSON Schema + description: Description to use when including this computed field in JSON Schema, defaults to the function's + docstring + deprecated: A deprecation message (or an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport). + to be emitted when accessing the field. Or a boolean. This will automatically be set if the property is decorated with the + `deprecated` decorator. + examples: Example values to use when including this computed field in JSON Schema + json_schema_extra: A dict or callable to provide extra JSON schema properties. + repr: whether to include this computed field in model repr. + Default is `False` for private properties and `True` for public properties. + return_type: optional return for serialization logic to expect when serializing to JSON, if included + this must be correct, otherwise a `TypeError` is raised. + If you don't include a return type Any is used, which does runtime introspection to handle arbitrary + objects. + + Returns: + A proxy wrapper for the property. + """ + + def dec(f: Any) -> Any: + nonlocal description, deprecated, return_type, alias_priority + unwrapped = _decorators.unwrap_wrapped_function(f) + + if description is None and unwrapped.__doc__: + description = inspect.cleandoc(unwrapped.__doc__) + + if deprecated is None and hasattr(unwrapped, '__deprecated__'): + deprecated = unwrapped.__deprecated__ + + # if the function isn't already decorated with `@property` (or another descriptor), then we wrap it now + f = _decorators.ensure_property(f) + alias_priority = (alias_priority or 2) if alias is not None else None + + if repr is None: + repr_: bool = not _wrapped_property_is_private(property_=f) + else: + repr_ = repr + + dec_info = ComputedFieldInfo( + f, + return_type, + alias, + alias_priority, + title, + description, + deprecated, + examples, + json_schema_extra, + repr_, + ) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + if func is None: + return dec + else: + return dec(func) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_serializers.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_serializers.py new file mode 100644 index 0000000..1a9e556 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_serializers.py @@ -0,0 +1,399 @@ +"""This module contains related classes and functions for serialization.""" +from __future__ import annotations + +import dataclasses +from functools import partialmethod +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, overload + +from pydantic_core import PydanticUndefined, core_schema +from pydantic_core import core_schema as _core_schema +from typing_extensions import Annotated, Literal, TypeAlias + +from . import PydanticUndefinedAnnotation +from ._internal import _decorators, _internal_dataclass +from .annotated_handlers import GetCoreSchemaHandler + + +@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class PlainSerializer: + """Plain serializers use a function to modify the output of serialization. + + This is particularly helpful when you want to customize the serialization for annotated types. + Consider an input of `list`, which will be serialized into a space-delimited string. + + ```python + from typing import List + + from typing_extensions import Annotated + + from pydantic import BaseModel, PlainSerializer + + CustomStr = Annotated[ + List, PlainSerializer(lambda x: ' '.join(x), return_type=str) + ] + + class StudentModel(BaseModel): + courses: CustomStr + + student = StudentModel(courses=['Math', 'Chemistry', 'English']) + print(student.model_dump()) + #> {'courses': 'Math Chemistry English'} + ``` + + Attributes: + func: The serializer function. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + when_used: Determines when this serializer should be used. Accepts a string with values `'always'`, + `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'. + """ + + func: core_schema.SerializerFunction + return_type: Any = PydanticUndefined + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = 'always' + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + """Gets the Pydantic core schema. + + Args: + source_type: The source type. + handler: The `GetCoreSchemaHandler` instance. + + Returns: + The Pydantic core schema. + """ + schema = handler(source_type) + try: + return_type = _decorators.get_function_return_type( + self.func, self.return_type, handler._get_types_namespace() + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type) + schema['serialization'] = core_schema.plain_serializer_function_ser_schema( + function=self.func, + info_arg=_decorators.inspect_annotated_serializer(self.func, 'plain'), + return_schema=return_schema, + when_used=self.when_used, + ) + return schema + + +@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class WrapSerializer: + """Wrap serializers receive the raw inputs along with a handler function that applies the standard serialization + logic, and can modify the resulting value before returning it as the final output of serialization. + + For example, here's a scenario in which a wrap serializer transforms timezones to UTC **and** utilizes the existing `datetime` serialization logic. + + ```python + from datetime import datetime, timezone + from typing import Any, Dict + + from typing_extensions import Annotated + + from pydantic import BaseModel, WrapSerializer + + class EventDatetime(BaseModel): + start: datetime + end: datetime + + def convert_to_utc(value: Any, handler, info) -> Dict[str, datetime]: + # Note that `helper` can actually help serialize the `value` for further custom serialization in case it's a subclass. + partial_result = handler(value, info) + if info.mode == 'json': + return { + k: datetime.fromisoformat(v).astimezone(timezone.utc) + for k, v in partial_result.items() + } + return {k: v.astimezone(timezone.utc) for k, v in partial_result.items()} + + UTCEventDatetime = Annotated[EventDatetime, WrapSerializer(convert_to_utc)] + + class EventModel(BaseModel): + event_datetime: UTCEventDatetime + + dt = EventDatetime( + start='2024-01-01T07:00:00-08:00', end='2024-01-03T20:00:00+06:00' + ) + event = EventModel(event_datetime=dt) + print(event.model_dump()) + ''' + { + 'event_datetime': { + 'start': datetime.datetime( + 2024, 1, 1, 15, 0, tzinfo=datetime.timezone.utc + ), + 'end': datetime.datetime( + 2024, 1, 3, 14, 0, tzinfo=datetime.timezone.utc + ), + } + } + ''' + + print(event.model_dump_json()) + ''' + {"event_datetime":{"start":"2024-01-01T15:00:00Z","end":"2024-01-03T14:00:00Z"}} + ''' + ``` + + Attributes: + func: The serializer function to be wrapped. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + when_used: Determines when this serializer should be used. Accepts a string with values `'always'`, + `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'. + """ + + func: core_schema.WrapSerializerFunction + return_type: Any = PydanticUndefined + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = 'always' + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + """This method is used to get the Pydantic core schema of the class. + + Args: + source_type: Source type. + handler: Core schema handler. + + Returns: + The generated core schema of the class. + """ + schema = handler(source_type) + try: + return_type = _decorators.get_function_return_type( + self.func, self.return_type, handler._get_types_namespace() + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type) + schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + function=self.func, + info_arg=_decorators.inspect_annotated_serializer(self.func, 'wrap'), + return_schema=return_schema, + when_used=self.when_used, + ) + return schema + + +if TYPE_CHECKING: + _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]] + _PlainSerializationFunction = Union[_core_schema.SerializerFunction, _PartialClsOrStaticMethod] + _WrapSerializationFunction = Union[_core_schema.WrapSerializerFunction, _PartialClsOrStaticMethod] + _PlainSerializeMethodType = TypeVar('_PlainSerializeMethodType', bound=_PlainSerializationFunction) + _WrapSerializeMethodType = TypeVar('_WrapSerializeMethodType', bound=_WrapSerializationFunction) + + +@overload +def field_serializer( + field: str, + /, + *fields: str, + return_type: Any = ..., + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = ..., + check_fields: bool | None = ..., +) -> Callable[[_PlainSerializeMethodType], _PlainSerializeMethodType]: + ... + + +@overload +def field_serializer( + field: str, + /, + *fields: str, + mode: Literal['plain'], + return_type: Any = ..., + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = ..., + check_fields: bool | None = ..., +) -> Callable[[_PlainSerializeMethodType], _PlainSerializeMethodType]: + ... + + +@overload +def field_serializer( + field: str, + /, + *fields: str, + mode: Literal['wrap'], + return_type: Any = ..., + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = ..., + check_fields: bool | None = ..., +) -> Callable[[_WrapSerializeMethodType], _WrapSerializeMethodType]: + ... + + +def field_serializer( + *fields: str, + mode: Literal['plain', 'wrap'] = 'plain', + return_type: Any = PydanticUndefined, + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = 'always', + check_fields: bool | None = None, +) -> Callable[[Any], Any]: + """Decorator that enables custom field serialization. + + In the below example, a field of type `set` is used to mitigate duplication. A `field_serializer` is used to serialize the data as a sorted list. + + ```python + from typing import Set + + from pydantic import BaseModel, field_serializer + + class StudentModel(BaseModel): + name: str = 'Jane' + courses: Set[str] + + @field_serializer('courses', when_used='json') + def serialize_courses_in_order(courses: Set[str]): + return sorted(courses) + + student = StudentModel(courses={'Math', 'Chemistry', 'English'}) + print(student.model_dump_json()) + #> {"name":"Jane","courses":["Chemistry","English","Math"]} + ``` + + See [Custom serializers](../concepts/serialization.md#custom-serializers) for more information. + + Four signatures are supported: + + - `(self, value: Any, info: FieldSerializationInfo)` + - `(self, value: Any, nxt: SerializerFunctionWrapHandler, info: FieldSerializationInfo)` + - `(value: Any, info: SerializationInfo)` + - `(value: Any, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)` + + Args: + fields: Which field(s) the method should be called on. + mode: The serialization mode. + + - `plain` means the function will be called instead of the default serialization logic, + - `wrap` means the function will be called with an argument to optionally call the + default serialization logic. + return_type: Optional return type for the function, if omitted it will be inferred from the type annotation. + when_used: Determines the serializer will be used for serialization. + check_fields: Whether to check that the fields actually exist on the model. + + Returns: + The decorator function. + """ + + def dec( + f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any], + ) -> _decorators.PydanticDescriptorProxy[Any]: + dec_info = _decorators.FieldSerializerDecoratorInfo( + fields=fields, + mode=mode, + return_type=return_type, + when_used=when_used, + check_fields=check_fields, + ) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + return dec + + +FuncType = TypeVar('FuncType', bound=Callable[..., Any]) + + +@overload +def model_serializer(__f: FuncType) -> FuncType: + ... + + +@overload +def model_serializer( + *, + mode: Literal['plain', 'wrap'] = ..., + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = 'always', + return_type: Any = ..., +) -> Callable[[FuncType], FuncType]: + ... + + +def model_serializer( + f: Callable[..., Any] | None = None, + /, + *, + mode: Literal['plain', 'wrap'] = 'plain', + when_used: Literal['always', 'unless-none', 'json', 'json-unless-none'] = 'always', + return_type: Any = PydanticUndefined, +) -> Callable[[Any], Any]: + """Decorator that enables custom model serialization. + + This is useful when a model need to be serialized in a customized manner, allowing for flexibility beyond just specific fields. + + An example would be to serialize temperature to the same temperature scale, such as degrees Celsius. + + ```python + from typing import Literal + + from pydantic import BaseModel, model_serializer + + class TemperatureModel(BaseModel): + unit: Literal['C', 'F'] + value: int + + @model_serializer() + def serialize_model(self): + if self.unit == 'F': + return {'unit': 'C', 'value': int((self.value - 32) / 1.8)} + return {'unit': self.unit, 'value': self.value} + + temperature = TemperatureModel(unit='F', value=212) + print(temperature.model_dump()) + #> {'unit': 'C', 'value': 100} + ``` + + See [Custom serializers](../concepts/serialization.md#custom-serializers) for more information. + + Args: + f: The function to be decorated. + mode: The serialization mode. + + - `'plain'` means the function will be called instead of the default serialization logic + - `'wrap'` means the function will be called with an argument to optionally call the default + serialization logic. + when_used: Determines when this serializer should be used. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + + Returns: + The decorator function. + """ + + def dec(f: Callable[..., Any]) -> _decorators.PydanticDescriptorProxy[Any]: + dec_info = _decorators.ModelSerializerDecoratorInfo(mode=mode, return_type=return_type, when_used=when_used) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + if f is None: + return dec + else: + return dec(f) # type: ignore + + +AnyType = TypeVar('AnyType') + + +if TYPE_CHECKING: + SerializeAsAny = Annotated[AnyType, ...] # SerializeAsAny[list[str]] will be treated by type checkers as list[str] + """Force serialization to ignore whatever is defined in the schema and instead ask the object + itself how it should be serialized. + In particular, this means that when model subclasses are serialized, fields present in the subclass + but not in the original schema will be included. + """ +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SerializeAsAny: # noqa: D101 + def __class_getitem__(cls, item: Any) -> Any: + return Annotated[item, SerializeAsAny()] + + def __get_pydantic_core_schema__( + self, source_type: Any, handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + schema = handler(source_type) + schema_to_update = schema + while schema_to_update['type'] == 'definitions': + schema_to_update = schema_to_update.copy() + schema_to_update = schema_to_update['schema'] + schema_to_update['serialization'] = core_schema.wrap_serializer_function_ser_schema( + lambda x, h: h(x), schema=core_schema.any_schema() + ) + return schema + + __hash__ = object.__hash__ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_validators.py new file mode 100644 index 0000000..fc3ad71 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/functional_validators.py @@ -0,0 +1,709 @@ +"""This module contains related classes and functions for validation.""" + +from __future__ import annotations as _annotations + +import dataclasses +import sys +from functools import partialmethod +from types import FunctionType +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, cast, overload + +from pydantic_core import core_schema +from pydantic_core import core_schema as _core_schema +from typing_extensions import Annotated, Literal, TypeAlias + +from . import GetCoreSchemaHandler as _GetCoreSchemaHandler +from ._internal import _core_metadata, _decorators, _generics, _internal_dataclass +from .annotated_handlers import GetCoreSchemaHandler +from .errors import PydanticUserError + +if sys.version_info < (3, 11): + from typing_extensions import Protocol +else: + from typing import Protocol + +_inspect_validator = _decorators.inspect_validator + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class AfterValidator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#annotated-validators + + A metadata class that indicates that a validation should be applied **after** the inner validation logic. + + Attributes: + func: The validator function. + + Example: + ```py + from typing_extensions import Annotated + + from pydantic import AfterValidator, BaseModel, ValidationError + + MyInt = Annotated[int, AfterValidator(lambda v: v + 1)] + + class Model(BaseModel): + a: MyInt + + print(Model(a=1).a) + #> 2 + + try: + Model(a='a') + except ValidationError as e: + print(e.json(indent=2)) + ''' + [ + { + "type": "int_parsing", + "loc": [ + "a" + ], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "a", + "url": "https://errors.pydantic.dev/2/v/int_parsing" + } + ] + ''' + ``` + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + + def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + info_arg = _inspect_validator(self.func, 'after') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_after_validator_function(func, schema=schema, field_name=handler.field_name) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_after_validator_function(func, schema=schema) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class BeforeValidator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#annotated-validators + + A metadata class that indicates that a validation should be applied **before** the inner validation logic. + + Attributes: + func: The validator function. + + Example: + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, BeforeValidator + + MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)] + + class Model(BaseModel): + a: MyInt + + print(Model(a=1).a) + #> 2 + + try: + Model(a='a') + except TypeError as e: + print(e) + #> can only concatenate str (not "int") to str + ``` + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + + def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + info_arg = _inspect_validator(self.func, 'before') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_before_validator_function(func, schema=schema, field_name=handler.field_name) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_before_validator_function(func, schema=schema) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class PlainValidator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#annotated-validators + + A metadata class that indicates that a validation should be applied **instead** of the inner validation logic. + + Attributes: + func: The validator function. + + Example: + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, PlainValidator + + MyInt = Annotated[int, PlainValidator(lambda v: int(v) + 1)] + + class Model(BaseModel): + a: MyInt + + print(Model(a='1').a) + #> 2 + ``` + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + + def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema: + # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the + # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper + # serialization schema. To work around this for use cases that will not involve serialization, we simply + # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema + # and abort any attempts to handle special serialization. + from pydantic import PydanticSchemaGenerationError + + try: + schema = handler(source_type) + serialization = core_schema.wrap_serializer_function_ser_schema(function=lambda v, h: h(v), schema=schema) + except PydanticSchemaGenerationError: + serialization = None + + info_arg = _inspect_validator(self.func, 'plain') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_plain_validator_function( + func, field_name=handler.field_name, serialization=serialization + ) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_plain_validator_function(func, serialization=serialization) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class WrapValidator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#annotated-validators + + A metadata class that indicates that a validation should be applied **around** the inner validation logic. + + Attributes: + func: The validator function. + + ```py + from datetime import datetime + + from typing_extensions import Annotated + + from pydantic import BaseModel, ValidationError, WrapValidator + + def validate_timestamp(v, handler): + if v == 'now': + # we don't want to bother with further validation, just return the new value + return datetime.now() + try: + return handler(v) + except ValidationError: + # validation failed, in this case we want to return a default value + return datetime(2000, 1, 1) + + MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)] + + class Model(BaseModel): + a: MyTimestamp + + print(Model(a='now').a) + #> 2032-01-02 03:04:05.000006 + print(Model(a='invalid').a) + #> 2000-01-01 00:00:00 + ``` + """ + + func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction + + def __get_pydantic_core_schema__(self, source_type: Any, handler: _GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + info_arg = _inspect_validator(self.func, 'wrap') + if info_arg: + func = cast(core_schema.WithInfoWrapValidatorFunction, self.func) + return core_schema.with_info_wrap_validator_function(func, schema=schema, field_name=handler.field_name) + else: + func = cast(core_schema.NoInfoWrapValidatorFunction, self.func) + return core_schema.no_info_wrap_validator_function(func, schema=schema) + + +if TYPE_CHECKING: + + class _OnlyValueValidatorClsMethod(Protocol): + def __call__(self, cls: Any, value: Any, /) -> Any: + ... + + class _V2ValidatorClsMethod(Protocol): + def __call__(self, cls: Any, value: Any, info: _core_schema.ValidationInfo, /) -> Any: + ... + + class _V2WrapValidatorClsMethod(Protocol): + def __call__( + self, + cls: Any, + value: Any, + handler: _core_schema.ValidatorFunctionWrapHandler, + info: _core_schema.ValidationInfo, + /, + ) -> Any: + ... + + _V2Validator = Union[ + _V2ValidatorClsMethod, + _core_schema.WithInfoValidatorFunction, + _OnlyValueValidatorClsMethod, + _core_schema.NoInfoValidatorFunction, + ] + + _V2WrapValidator = Union[ + _V2WrapValidatorClsMethod, + _core_schema.WithInfoWrapValidatorFunction, + ] + + _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]] + + _V2BeforeAfterOrPlainValidatorType = TypeVar( + '_V2BeforeAfterOrPlainValidatorType', + _V2Validator, + _PartialClsOrStaticMethod, + ) + _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', _V2WrapValidator, _PartialClsOrStaticMethod) + + +@overload +def field_validator( + field: str, + /, + *fields: str, + mode: Literal['before', 'after', 'plain'] = ..., + check_fields: bool | None = ..., +) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: + ... + + +@overload +def field_validator( + field: str, + /, + *fields: str, + mode: Literal['wrap'], + check_fields: bool | None = ..., +) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: + ... + + +FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain'] + + +def field_validator( + field: str, + /, + *fields: str, + mode: FieldValidatorModes = 'after', + check_fields: bool | None = None, +) -> Callable[[Any], Any]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#field-validators + + Decorate methods on the class indicating that they should be used to validate fields. + + Example usage: + ```py + from typing import Any + + from pydantic import ( + BaseModel, + ValidationError, + field_validator, + ) + + class Model(BaseModel): + a: str + + @field_validator('a') + @classmethod + def ensure_foobar(cls, v: Any): + if 'foobar' not in v: + raise ValueError('"foobar" not found in a') + return v + + print(repr(Model(a='this is foobar good'))) + #> Model(a='this is foobar good') + + try: + Model(a='snap') + except ValidationError as exc_info: + print(exc_info) + ''' + 1 validation error for Model + a + Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str] + ''' + ``` + + For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators). + + Args: + field: The first field the `field_validator` should be called on; this is separate + from `fields` to ensure an error is raised if you don't pass at least one. + *fields: Additional field(s) the `field_validator` should be called on. + mode: Specifies whether to validate the fields before or after validation. + check_fields: Whether to check that the fields actually exist on the model. + + Returns: + A decorator that can be used to decorate a function to be used as a field_validator. + + Raises: + PydanticUserError: + - If `@field_validator` is used bare (with no fields). + - If the args passed to `@field_validator` as fields are not strings. + - If `@field_validator` applied to instance methods. + """ + if isinstance(field, FunctionType): + raise PydanticUserError( + '`@field_validator` should be used with fields and keyword arguments, not bare. ' + "E.g. usage should be `@validator('', ...)`", + code='validator-no-fields', + ) + fields = field, *fields + if not all(isinstance(field, str) for field in fields): + raise PydanticUserError( + '`@field_validator` fields should be passed as separate string args. ' + "E.g. usage should be `@validator('', '', ...)`", + code='validator-invalid-fields', + ) + + def dec( + f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any], + ) -> _decorators.PydanticDescriptorProxy[Any]: + if _decorators.is_instance_method_from_sig(f): + raise PydanticUserError( + '`@field_validator` cannot be applied to instance methods', code='validator-instance-method' + ) + + # auto apply the @classmethod decorator + f = _decorators.ensure_classmethod_based_on_signature(f) + + dec_info = _decorators.FieldValidatorDecoratorInfo(fields=fields, mode=mode, check_fields=check_fields) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + return dec + + +_ModelType = TypeVar('_ModelType') +_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True) + + +class ModelWrapValidatorHandler(_core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]): + """@model_validator decorated function handler argument type. This is used when `mode='wrap'`.""" + + def __call__( # noqa: D102 + self, + value: Any, + outer_location: str | int | None = None, + /, + ) -> _ModelTypeCo: # pragma: no cover + ... + + +class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]): + """A @model_validator decorated function signature. + This is used when `mode='wrap'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + cls: type[_ModelType], + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + handler: ModelWrapValidatorHandler[_ModelType], + /, + ) -> _ModelType: + ... + + +class ModelWrapValidator(Protocol[_ModelType]): + """A @model_validator decorated function signature. This is used when `mode='wrap'`.""" + + def __call__( # noqa: D102 + self, + cls: type[_ModelType], + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + handler: ModelWrapValidatorHandler[_ModelType], + info: _core_schema.ValidationInfo, + /, + ) -> _ModelType: + ... + + +class FreeModelBeforeValidatorWithoutInfo(Protocol): + """A @model_validator decorated function signature. + This is used when `mode='before'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + /, + ) -> Any: + ... + + +class ModelBeforeValidatorWithoutInfo(Protocol): + """A @model_validator decorated function signature. + This is used when `mode='before'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + cls: Any, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + /, + ) -> Any: + ... + + +class FreeModelBeforeValidator(Protocol): + """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" + + def __call__( # noqa: D102 + self, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + info: _core_schema.ValidationInfo, + /, + ) -> Any: + ... + + +class ModelBeforeValidator(Protocol): + """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" + + def __call__( # noqa: D102 + self, + cls: Any, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + info: _core_schema.ValidationInfo, + /, + ) -> Any: + ... + + +ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType] +"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not +have info argument. +""" + +ModelAfterValidator = Callable[[_ModelType, _core_schema.ValidationInfo], _ModelType] +"""A `@model_validator` decorated function signature. This is used when `mode='after'`.""" + +_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]] +_AnyModeBeforeValidator = Union[ + FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo +] +_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]] + + +@overload +def model_validator( + *, + mode: Literal['wrap'], +) -> Callable[ + [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] +]: + ... + + +@overload +def model_validator( + *, + mode: Literal['before'], +) -> Callable[[_AnyModeBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]]: + ... + + +@overload +def model_validator( + *, + mode: Literal['after'], +) -> Callable[ + [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] +]: + ... + + +def model_validator( + *, + mode: Literal['wrap', 'before', 'after'], +) -> Any: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#model-validators + + Decorate model methods for validation purposes. + + Example usage: + ```py + from typing_extensions import Self + + from pydantic import BaseModel, ValidationError, model_validator + + class Square(BaseModel): + width: float + height: float + + @model_validator(mode='after') + def verify_square(self) -> Self: + if self.width != self.height: + raise ValueError('width and height do not match') + return self + + s = Square(width=1, height=1) + print(repr(s)) + #> Square(width=1.0, height=1.0) + + try: + Square(width=1, height=2) + except ValidationError as e: + print(e) + ''' + 1 validation error for Square + Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict] + ''' + ``` + + For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators). + + Args: + mode: A required string literal that specifies the validation mode. + It can be one of the following: 'wrap', 'before', or 'after'. + + Returns: + A decorator that can be used to decorate a function to be used as a model validator. + """ + + def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]: + # auto apply the @classmethod decorator + f = _decorators.ensure_classmethod_based_on_signature(f) + dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + return dec + + +AnyType = TypeVar('AnyType') + + +if TYPE_CHECKING: + # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this + InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence` + +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class InstanceOf: + '''Generic type for annotating a type that is an instance of a given class. + + Example: + ```py + from pydantic import BaseModel, InstanceOf + + class Foo: + ... + + class Bar(BaseModel): + foo: InstanceOf[Foo] + + Bar(foo=Foo()) + try: + Bar(foo=42) + except ValidationError as e: + print(e) + """ + [ + │ { + │ │ 'type': 'is_instance_of', + │ │ 'loc': ('foo',), + │ │ 'msg': 'Input should be an instance of Foo', + │ │ 'input': 42, + │ │ 'ctx': {'class': 'Foo'}, + │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of' + │ } + ] + """ + ``` + ''' + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + from pydantic import PydanticSchemaGenerationError + + # use the generic _origin_ as the second argument to isinstance when appropriate + instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source) + + try: + # Try to generate the "standard" schema, which will be used when loading from JSON + original_schema = handler(source) + except PydanticSchemaGenerationError: + # If that fails, just produce a schema that can validate from python + return instance_of_schema + else: + # Use the "original" approach to serialization + instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + function=lambda v, h: h(v), schema=original_schema + ) + return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema) + + __hash__ = object.__hash__ + + +if TYPE_CHECKING: + SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str] +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SkipValidation: + """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be + skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`. + + This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes, + and know that it is safe to skip validation for one or more of the fields. + + Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations + may not have the expected effects. Therefore, when used, this annotation should generally be the final + annotation applied to a type. + """ + + def __class_getitem__(cls, item: Any) -> Any: + return Annotated[item, SkipValidation()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + original_schema = handler(source) + metadata = _core_metadata.build_metadata_dict(js_annotation_functions=[lambda _c, h: h(original_schema)]) + return core_schema.any_schema( + metadata=metadata, + serialization=core_schema.wrap_serializer_function_ser_schema( + function=lambda v, h: h(v), schema=original_schema + ), + ) + + __hash__ = object.__hash__ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/generics.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/generics.py new file mode 100644 index 0000000..5f6f7f7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/generics.py @@ -0,0 +1,4 @@ +"""The `generics` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/json.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/json.py new file mode 100644 index 0000000..020fb6d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/json.py @@ -0,0 +1,4 @@ +"""The `json` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/json_schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/json_schema.py new file mode 100644 index 0000000..4b0e013 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/json_schema.py @@ -0,0 +1,2493 @@ +""" +Usage docs: https://docs.pydantic.dev/2.5/concepts/json_schema/ + +The `json_schema` module contains classes and functions to allow the way [JSON Schema](https://json-schema.org/) +is generated to be customized. + +In general you shouldn't need to use this module directly; instead, you can use +[`BaseModel.model_json_schema`][pydantic.BaseModel.model_json_schema] and +[`TypeAdapter.json_schema`][pydantic.TypeAdapter.json_schema]. +""" +from __future__ import annotations as _annotations + +import dataclasses +import inspect +import math +import re +import warnings +from collections import defaultdict +from copy import deepcopy +from dataclasses import is_dataclass +from enum import Enum +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Counter, + Dict, + Hashable, + Iterable, + NewType, + Sequence, + Tuple, + TypeVar, + Union, + cast, +) + +import pydantic_core +from pydantic_core import CoreSchema, PydanticOmit, core_schema, to_jsonable_python +from pydantic_core.core_schema import ComputedField +from typing_extensions import Annotated, Literal, TypeAlias, assert_never, deprecated, final + +from pydantic.warnings import PydanticDeprecatedSince26 + +from ._internal import ( + _config, + _core_metadata, + _core_utils, + _decorators, + _internal_dataclass, + _mock_val_ser, + _schema_generation_shared, + _typing_extra, +) +from .annotated_handlers import GetJsonSchemaHandler +from .config import JsonDict, JsonSchemaExtraCallable, JsonValue +from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError + +if TYPE_CHECKING: + from . import ConfigDict + from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField + from ._internal._dataclasses import PydanticDataclass + from ._internal._schema_generation_shared import GetJsonSchemaFunction + from .main import BaseModel + + +CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType] +""" +A type alias for defined schema types that represents a union of +`core_schema.CoreSchemaType` and +`core_schema.CoreSchemaFieldType`. +""" + +JsonSchemaValue = Dict[str, Any] +""" +A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values. +""" + +JsonSchemaMode = Literal['validation', 'serialization'] +""" +A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'. + +For some types, the inputs to validation differ from the outputs of serialization. For example, +computed fields will only be present when serializing, and should not be provided when +validating. This flag provides a way to indicate whether you want the JSON schema required +for validation inputs, or that will be matched by serialization outputs. +""" + +_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'} + + +@deprecated( + '`update_json_schema` is deprecated, use a simple `my_dict.update(update_dict)` call instead.', + category=None, +) +def update_json_schema(schema: JsonSchemaValue, updates: dict[str, Any]) -> JsonSchemaValue: + """Update a JSON schema in-place by providing a dictionary of updates. + + This function sets the provided key-value pairs in the schema and returns the updated schema. + + Args: + schema: The JSON schema to update. + updates: A dictionary of key-value pairs to set in the schema. + + Returns: + The updated JSON schema. + """ + schema.update(updates) + return schema + + +JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default'] +""" +A type alias representing the kinds of warnings that can be emitted during JSON schema generation. + +See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] +for more details. +""" + + +class PydanticJsonSchemaWarning(UserWarning): + """This class is used to emit warnings produced during JSON schema generation. + See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and + [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] + methods for more details; these can be overridden to control warning behavior. + """ + + +# ##### JSON Schema Generation ##### +DEFAULT_REF_TEMPLATE = '#/$defs/{model}' +"""The default format string used to generate reference names.""" + +# There are three types of references relevant to building JSON schemas: +# 1. core_schema "ref" values; these are not exposed as part of the JSON schema +# * these might look like the fully qualified path of a model, its id, or something similar +CoreRef = NewType('CoreRef', str) +# 2. keys of the "definitions" object that will eventually go into the JSON schema +# * by default, these look like "MyModel", though may change in the presence of collisions +# * eventually, we may want to make it easier to modify the way these names are generated +DefsRef = NewType('DefsRef', str) +# 3. the values corresponding to the "$ref" key in the schema +# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"} +JsonRef = NewType('JsonRef', str) + +CoreModeRef = Tuple[CoreRef, JsonSchemaMode] +JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class _DefinitionsRemapping: + defs_remapping: dict[DefsRef, DefsRef] + json_remapping: dict[JsonRef, JsonRef] + + @staticmethod + def from_prioritized_choices( + prioritized_choices: dict[DefsRef, list[DefsRef]], + defs_to_json: dict[DefsRef, JsonRef], + definitions: dict[DefsRef, JsonSchemaValue], + ) -> _DefinitionsRemapping: + """ + This function should produce a remapping that replaces complex DefsRef with the simpler ones from the + prioritized_choices such that applying the name remapping would result in an equivalent JSON schema. + """ + # We need to iteratively simplify the definitions until we reach a fixed point. + # The reason for this is that outer definitions may reference inner definitions that get simplified + # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified + # the inner definitions. + copied_definitions = deepcopy(definitions) + definitions_schema = {'$defs': copied_definitions} + for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough + # For every possible remapped DefsRef, collect all schemas that that DefsRef might be used for: + schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) + for defs_ref in copied_definitions: + alternatives = prioritized_choices[defs_ref] + for alternative in alternatives: + schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) + + # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef + # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef. + for defs_ref, schemas in schemas_for_alternatives.items(): + schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) + + # Build the remapping + defs_remapping: dict[DefsRef, DefsRef] = {} + json_remapping: dict[JsonRef, JsonRef] = {} + for original_defs_ref in definitions: + alternatives = prioritized_choices[original_defs_ref] + # Pick the first alternative that has only one schema, since that means there is no collision + remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) + defs_remapping[original_defs_ref] = remapped_defs_ref + json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] + remapping = _DefinitionsRemapping(defs_remapping, json_remapping) + new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) + if definitions_schema == new_definitions_schema: + # We've reached the fixed point + return remapping + definitions_schema = new_definitions_schema + + raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions') + + def remap_defs_ref(self, ref: DefsRef) -> DefsRef: + return self.defs_remapping.get(ref, ref) + + def remap_json_ref(self, ref: JsonRef) -> JsonRef: + return self.json_remapping.get(ref, ref) + + def remap_json_schema(self, schema: Any) -> Any: + """ + Recursively update the JSON schema replacing all $refs + """ + if isinstance(schema, str): + # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings + return self.remap_json_ref(JsonRef(schema)) + elif isinstance(schema, list): + return [self.remap_json_schema(item) for item in schema] + elif isinstance(schema, dict): + for key, value in schema.items(): + if key == '$ref' and isinstance(value, str): + schema['$ref'] = self.remap_json_ref(JsonRef(value)) + elif key == '$defs': + schema['$defs'] = { + self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value) + for key, value in schema['$defs'].items() + } + else: + schema[key] = self.remap_json_schema(value) + return schema + + +class GenerateJsonSchema: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json_schema/#customizing-the-json-schema-generation-process + + A class for generating JSON schemas. + + This class generates JSON schemas based on configured parameters. The default schema dialect + is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema). + The class uses `by_alias` to configure how fields with + multiple names are handled and `ref_template` to format reference names. + + Attributes: + schema_dialect: The JSON schema dialect used to generate the schema. See + [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4) + in the JSON Schema documentation for more information about dialects. + ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will + do nothing if its argument `kind` is in `ignored_warning_kinds`; + this value can be modified on subclasses to easily control which warnings are emitted. + by_alias: Whether to use field aliases when generating the schema. + ref_template: The format string used when generating reference names. + core_to_json_refs: A mapping of core refs to JSON refs. + core_to_defs_refs: A mapping of core refs to definition refs. + defs_to_core_refs: A mapping of definition refs to core refs. + json_to_defs_refs: A mapping of JSON refs to definition refs. + definitions: Definitions in the schema. + + Args: + by_alias: Whether to use field aliases in the generated schemas. + ref_template: The format string to use when generating reference names. + + Raises: + JsonSchemaError: If the instance of the class is inadvertently re-used after generating a schema. + """ + + schema_dialect = 'https://json-schema.org/draft/2020-12/schema' + + # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`; + # this value can be modified on subclasses to easily control which warnings are emitted + ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} + + def __init__(self, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE): + self.by_alias = by_alias + self.ref_template = ref_template + + self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} + self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} + self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} + self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} + + self.definitions: dict[DefsRef, JsonSchemaValue] = {} + self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) + + self._mode: JsonSchemaMode = 'validation' + + # The following includes a mapping of a fully-unique defs ref choice to a list of preferred + # alternatives, which are generally simpler, such as only including the class name. + # At the end of schema generation, we use these to produce a JSON schema with more human-readable + # definitions, which would also work better in a generated OpenAPI client, etc. + self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} + self._collision_counter: dict[str, int] = defaultdict(int) + self._collision_index: dict[str, int] = {} + + self._schema_type_to_method = self.build_schema_type_to_method() + + # When we encounter definitions we need to try to build them immediately + # so that they are available schemas that reference them + # But it's possible that CoreSchema was never going to be used + # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing + # the reference) so instead of failing altogether if we can't build a definition we + # store the error raised and re-throw it if we end up needing that def + self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} + + # This changes to True after generating a schema, to prevent issues caused by accidental re-use + # of a single instance of a schema generator + self._used = False + + @property + def _config(self) -> _config.ConfigWrapper: + return self._config_wrapper_stack.tail + + @property + def mode(self) -> JsonSchemaMode: + if self._config.json_schema_mode_override is not None: + return self._config.json_schema_mode_override + else: + return self._mode + + def build_schema_type_to_method( + self, + ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]: + """Builds a dictionary mapping fields to methods for generating JSON schemas. + + Returns: + A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method. + + Raises: + TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type. + """ + mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} + core_schema_types: list[CoreSchemaOrFieldType] = _typing_extra.all_literal_values( + CoreSchemaOrFieldType # type: ignore + ) + for key in core_schema_types: + method_name = f"{key.replace('-', '_')}_schema" + try: + mapping[key] = getattr(self, method_name) + except AttributeError as e: # pragma: no cover + raise TypeError( + f'No method for generating JsonSchema for core_schema.type={key!r} ' + f'(expected: {type(self).__name__}.{method_name})' + ) from e + return mapping + + def generate_definitions( + self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]] + ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]: + """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a + mapping that links the input keys to the definition references. + + Args: + inputs: A sequence of tuples, where: + + - The first element is a JSON schema key type. + - The second element is the JSON mode: either 'validation' or 'serialization'. + - The third element is a core schema. + + Returns: + A tuple where: + + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a dictionary whose keys are definition references for the JSON schemas + from the first returned element, and whose values are the actual JSON schema definitions. + + Raises: + PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema. + """ + if self._used: + raise PydanticUserError( + 'This JSON schema generator has already been used to generate a JSON schema. ' + f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', + code='json-schema-already-used', + ) + + for key, mode, schema in inputs: + self._mode = mode + self.generate_inner(schema) + + definitions_remapping = self._build_definitions_remapping() + + json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} + for key, mode, schema in inputs: + self._mode = mode + json_schema = self.generate_inner(schema) + json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) + + json_schema = {'$defs': self.definitions} + json_schema = definitions_remapping.remap_json_schema(json_schema) + self._used = True + return json_schemas_map, _sort_json_schema(json_schema['$defs']) # type: ignore + + def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: + """Generates a JSON schema for a specified schema in a specified mode. + + Args: + schema: A Pydantic model. + mode: The mode in which to generate the schema. Defaults to 'validation'. + + Returns: + A JSON schema representing the specified schema. + + Raises: + PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema. + """ + self._mode = mode + if self._used: + raise PydanticUserError( + 'This JSON schema generator has already been used to generate a JSON schema. ' + f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', + code='json-schema-already-used', + ) + + json_schema: JsonSchemaValue = self.generate_inner(schema) + json_ref_counts = self.get_json_ref_counts(json_schema) + + # Remove the top-level $ref if present; note that the _generate method already ensures there are no sibling keys + ref = cast(JsonRef, json_schema.get('$ref')) + while ref is not None: # may need to unpack multiple levels + ref_json_schema = self.get_schema_from_definitions(ref) + if json_ref_counts[ref] > 1 or ref_json_schema is None: + # Keep the ref, but use an allOf to remove the top level $ref + json_schema = {'allOf': [{'$ref': ref}]} + else: + # "Unpack" the ref since this is the only reference + json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference + json_ref_counts[ref] -= 1 + ref = cast(JsonRef, json_schema.get('$ref')) + + self._garbage_collect_definitions(json_schema) + definitions_remapping = self._build_definitions_remapping() + + if self.definitions: + json_schema['$defs'] = self.definitions + + json_schema = definitions_remapping.remap_json_schema(json_schema) + + # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding + # this method and adding the following line after a call to super().generate(schema): + # json_schema['$schema'] = self.schema_dialect + + self._used = True + return _sort_json_schema(json_schema) + + def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 + """Generates a JSON schema for a given core schema. + + Args: + schema: The given core schema. + + Returns: + The generated JSON schema. + """ + # If a schema with the same CoreRef has been handled, just return a reference to it + # Note that this assumes that it will _never_ be the case that the same CoreRef is used + # on types that should have different JSON schemas + if 'ref' in schema: + core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] + core_mode_ref = (core_ref, self.mode) + if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: + return {'$ref': self.core_to_json_refs[core_mode_ref]} + + # Generate the JSON schema, accounting for the json_schema_override and core_schema_override + metadata_handler = _core_metadata.CoreMetadataHandler(schema) + + def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: + if 'ref' in core_schema: + core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] + defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) + json_ref = JsonRef(ref_json_schema['$ref']) + self.json_to_defs_refs[json_ref] = defs_ref + # Replace the schema if it's not a reference to itself + # What we want to avoid is having the def be just a ref to itself + # which is what would happen if we blindly assigned any + if json_schema.get('$ref', None) != json_ref: + self.definitions[defs_ref] = json_schema + self._core_defs_invalid_for_json_schema.pop(defs_ref, None) + json_schema = ref_json_schema + return json_schema + + def convert_to_all_of(json_schema: JsonSchemaValue) -> JsonSchemaValue: + if '$ref' in json_schema and len(json_schema.keys()) > 1: + # technically you can't have any other keys next to a "$ref" + # but it's an easy mistake to make and not hard to correct automatically here + json_schema = json_schema.copy() + ref = json_schema.pop('$ref') + json_schema = {'allOf': [{'$ref': ref}], **json_schema} + return json_schema + + def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: + """Generate a JSON schema based on the input schema. + + Args: + schema_or_field: The core schema to generate a JSON schema from. + + Returns: + The generated JSON schema. + + Raises: + TypeError: If an unexpected schema type is encountered. + """ + # Generate the core-schema-type-specific bits of the schema generation: + json_schema: JsonSchemaValue | None = None + if self.mode == 'serialization' and 'serialization' in schema_or_field: + ser_schema = schema_or_field['serialization'] # type: ignore + json_schema = self.ser_schema(ser_schema) + if json_schema is None: + if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): + generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] + json_schema = generate_for_schema_type(schema_or_field) + else: + raise TypeError(f'Unexpected schema type: schema={schema_or_field}') + if _core_utils.is_core_schema(schema_or_field): + json_schema = populate_defs(schema_or_field, json_schema) + json_schema = convert_to_all_of(json_schema) + return json_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) + + for js_modify_function in metadata_handler.metadata.get('pydantic_js_functions', ()): + + def new_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + js_modify_function: GetJsonSchemaFunction = js_modify_function, + ) -> JsonSchemaValue: + json_schema = js_modify_function(schema_or_field, current_handler) + if _core_utils.is_core_schema(schema_or_field): + json_schema = populate_defs(schema_or_field, json_schema) + original_schema = current_handler.resolve_ref_schema(json_schema) + ref = json_schema.pop('$ref', None) + if ref and json_schema: + original_schema.update(json_schema) + return original_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) + + for js_modify_function in metadata_handler.metadata.get('pydantic_js_annotation_functions', ()): + + def new_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + js_modify_function: GetJsonSchemaFunction = js_modify_function, + ) -> JsonSchemaValue: + json_schema = js_modify_function(schema_or_field, current_handler) + if _core_utils.is_core_schema(schema_or_field): + json_schema = populate_defs(schema_or_field, json_schema) + json_schema = convert_to_all_of(json_schema) + return json_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) + + json_schema = current_handler(schema) + if _core_utils.is_core_schema(schema): + json_schema = populate_defs(schema, json_schema) + json_schema = convert_to_all_of(json_schema) + return json_schema + + # ### Schema generation methods + def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue: + """Generates a JSON schema that matches any value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {} + + def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches `None`. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'null'} + + def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a bool value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'boolean'} + + def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches an int value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: dict[str, Any] = {'type': 'integer'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) + json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} + return json_schema + + def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a float value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: dict[str, Any] = {'type': 'number'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) + json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} + return json_schema + + def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a decimal value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = self.str_schema(core_schema.str_schema()) + if self.mode == 'validation': + multiple_of = schema.get('multiple_of') + le = schema.get('le') + ge = schema.get('ge') + lt = schema.get('lt') + gt = schema.get('gt') + json_schema = { + 'anyOf': [ + self.float_schema( + core_schema.float_schema( + allow_inf_nan=schema.get('allow_inf_nan'), + multiple_of=None if multiple_of is None else float(multiple_of), + le=None if le is None else float(le), + ge=None if ge is None else float(ge), + lt=None if lt is None else float(lt), + gt=None if gt is None else float(gt), + ) + ), + json_schema, + ], + } + return json_schema + + def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a string value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + return json_schema + + def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a bytes value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) + return json_schema + + def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a date value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string', 'format': 'date'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.date) + return json_schema + + def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a time value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'time'} + + def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a datetime value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'date-time'} + + def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a timedelta value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + if self._config.ser_json_timedelta == 'float': + return {'type': 'number'} + return {'type': 'string', 'format': 'duration'} + + def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a literal value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + expected = [v.value if isinstance(v, Enum) else v for v in schema['expected']] + # jsonify the expected values + expected = [to_jsonable_python(v) for v in expected] + + result: dict[str, Any] = {'enum': expected} + if len(expected) == 1: + result['const'] = expected[0] + + types = {type(e) for e in expected} + if types == {str}: + result['type'] = 'string' + elif types == {int}: + result['type'] = 'integer' + elif types == {float}: + result['type'] = 'numeric' + elif types == {bool}: + result['type'] = 'boolean' + elif types == {list}: + result['type'] = 'array' + elif types == {type(None)}: + result['type'] = 'null' + return result + + def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches an Enum value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + enum_type = schema['cls'] + description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) + if ( + description == 'An enumeration.' + ): # This is the default value provided by enum.EnumMeta.__new__; don't use it + description = None + result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} + result = {k: v for k, v in result.items() if v is not None} + + expected = [to_jsonable_python(v.value) for v in schema['members']] + + result['enum'] = expected + if len(expected) == 1: + result['const'] = expected[0] + + types = {type(e) for e in expected} + if isinstance(enum_type, str) or types == {str}: + result['type'] = 'string' + elif isinstance(enum_type, int) or types == {int}: + result['type'] = 'integer' + elif isinstance(enum_type, float) or types == {float}: + result['type'] = 'numeric' + elif types == {bool}: + result['type'] = 'boolean' + elif types == {list}: + result['type'] = 'array' + + return result + + def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: + """Handles JSON schema generation for a core schema that checks if a value is an instance of a class. + + Unless overridden in a subclass, this raises an error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') + + def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: + """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class. + + For backwards compatibility with v1, this does not raise an error, but can be overridden to change this. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # Note: This is for compatibility with V1; you can override if you want different behavior. + return {} + + def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a callable value. + + Unless overridden in a subclass, this raises an error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') + + def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: + """Returns a schema that matches a list schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) + @final + def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Replaced by `tuple_schema`.""" + warnings.warn( + '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', + PydanticDeprecatedSince26, + stacklevel=2, + ) + return self.tuple_schema(schema) + + @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) + @final + def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Replaced by `tuple_schema`.""" + warnings.warn( + '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', + PydanticDeprecatedSince26, + stacklevel=2, + ) + return self.tuple_schema(schema) + + def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a tuple schema e.g. `Tuple[int, + str, bool]` or `Tuple[int, ...]`. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: JsonSchemaValue = {'type': 'array'} + if 'variadic_item_index' in schema: + variadic_item_index = schema['variadic_item_index'] + if variadic_item_index > 0: + json_schema['minItems'] = variadic_item_index + json_schema['prefixItems'] = [ + self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index] + ] + if variadic_item_index + 1 == len(schema['items_schema']): + # if the variadic item is the last item, then represent it faithfully + json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) + else: + # otherwise, 'items' represents the schema for the variadic + # item plus the suffix, so just allow anything for simplicity + # for now + json_schema['items'] = True + else: + prefixItems = [self.generate_inner(item) for item in schema['items_schema']] + if prefixItems: + json_schema['prefixItems'] = prefixItems + json_schema['minItems'] = len(prefixItems) + json_schema['maxItems'] = len(prefixItems) + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a set schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._common_set_schema(schema) + + def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a frozenset schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._common_set_schema(schema) + + def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: + """Returns a JSON schema that represents the provided GeneratorSchema. + + Args: + schema: The schema. + + Returns: + The generated JSON schema. + """ + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a dict schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: JsonSchemaValue = {'type': 'object'} + + keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} + keys_pattern = keys_schema.pop('pattern', None) + + values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} + values_schema.pop('title', None) # don't give a title to the additionalProperties + if values_schema or keys_pattern is not None: # don't add additionalProperties if it's empty + if keys_pattern is None: + json_schema['additionalProperties'] = values_schema + else: + json_schema['patternProperties'] = {keys_pattern: values_schema} + + self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) + return json_schema + + def _function_schema( + self, + schema: _core_utils.AnyFunctionSchema, + ) -> JsonSchemaValue: + if _core_utils.is_function_with_inner_schema(schema): + # This could be wrong if the function's mode is 'before', but in practice will often be right, and when it + # isn't, I think it would be hard to automatically infer what the desired schema should be. + return self.generate_inner(schema['schema']) + + # function-plain + return self.handle_invalid_for_json_schema( + schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})' + ) + + def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-before schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._function_schema(schema) + + def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-after schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._function_schema(schema) + + def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-plain schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._function_schema(schema) + + def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-wrap schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._function_schema(schema) + + def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema with a default value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = self.generate_inner(schema['schema']) + + if 'default' not in schema: + return json_schema + default = schema['default'] + # Note: if you want to include the value returned by the default_factory, + # override this method and replace the code above with: + # if 'default' in schema: + # default = schema['default'] + # elif 'default_factory' in schema: + # default = schema['default_factory']() + # else: + # return json_schema + + try: + encoded_default = self.encode_default(default) + except pydantic_core.PydanticSerializationError: + self.emit_warning( + 'non-serializable-default', + f'Default value {default} is not JSON serializable; excluding default from JSON schema', + ) + # Return the inner schema, as though there was no default + return json_schema + + if '$ref' in json_schema: + # Since reference schemas do not support child keys, we wrap the reference schema in a single-case allOf: + return {'allOf': [json_schema], 'default': encoded_default} + else: + json_schema['default'] = encoded_default + return json_schema + + def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows null values. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + null_schema = {'type': 'null'} + inner_json_schema = self.generate_inner(schema['schema']) + + if inner_json_schema == null_schema: + return null_schema + else: + # Thanks to the equality check against `null_schema` above, I think 'oneOf' would also be valid here; + # I'll use 'anyOf' for now, but it could be changed it if it would work better with some external tooling + return self.get_flattened_anyof([inner_json_schema, null_schema]) + + def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching any of the given schemas. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + generated: list[JsonSchemaValue] = [] + + choices = schema['choices'] + for choice in choices: + # choice will be a tuple if an explicit label was provided + choice_schema = choice[0] if isinstance(choice, tuple) else choice + try: + generated.append(self.generate_inner(choice_schema)) + except PydanticOmit: + continue + except PydanticInvalidForJsonSchema as exc: + self.emit_warning('skipped-choice', exc.message) + if len(generated) == 1: + return generated[0] + return self.get_flattened_anyof(generated) + + def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where + the schemas are tagged with a discriminator field that indicates which schema should be used to validate + the value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + generated: dict[str, JsonSchemaValue] = {} + for k, v in schema['choices'].items(): + if isinstance(k, Enum): + k = k.value + try: + # Use str(k) since keys must be strings for json; while not technically correct, + # it's the closest that can be represented in valid JSON + generated[str(k)] = self.generate_inner(v).copy() + except PydanticOmit: + continue + except PydanticInvalidForJsonSchema as exc: + self.emit_warning('skipped-choice', exc.message) + + one_of_choices = _deduplicate_schemas(generated.values()) + json_schema: JsonSchemaValue = {'oneOf': one_of_choices} + + # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema + openapi_discriminator = self._extract_discriminator(schema, one_of_choices) + if openapi_discriminator is not None: + json_schema['discriminator'] = { + 'propertyName': openapi_discriminator, + 'mapping': {k: v.get('$ref', v) for k, v in generated.items()}, + } + + return json_schema + + def _extract_discriminator( + self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict] + ) -> str | None: + """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final + schema.""" + openapi_discriminator: str | None = None + + if isinstance(schema['discriminator'], str): + return schema['discriminator'] + + if isinstance(schema['discriminator'], list): + # If the discriminator is a single item list containing a string, that is equivalent to the string case + if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): + return schema['discriminator'][0] + # When an alias is used that is different from the field name, the discriminator will be a list of single + # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is + # more than one possible attribute, and looks for whether a single alias choice is present as a documented + # property on all choices. If so, that property will be used as the OpenAPI discriminator. + for alias_path in schema['discriminator']: + if not isinstance(alias_path, list): + break # this means that the discriminator is not a list of alias paths + if len(alias_path) != 1: + continue # this means that the "alias" does not represent a single field + alias = alias_path[0] + if not isinstance(alias, str): + continue # this means that the "alias" does not represent a field + alias_is_present_on_all_choices = True + for choice in one_of_choices: + while '$ref' in choice: + assert isinstance(choice['$ref'], str) + choice = self.get_schema_from_definitions(JsonRef(choice['$ref'])) or {} + properties = choice.get('properties', {}) + if not isinstance(properties, dict) or alias not in properties: + alias_is_present_on_all_choices = False + break + if alias_is_present_on_all_choices: + openapi_discriminator = alias + break + return openapi_discriminator + + def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a core_schema.ChainSchema. + + When generating a schema for validation, we return the validation JSON schema for the first step in the chain. + For serialization, we return the serialization JSON schema for the last step in the chain. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization + return self.generate_inner(schema['steps'][step_index]) + + def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the + strict schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # TODO: Need to read the default value off of model config or whatever + use_strict = schema.get('strict', False) # TODO: replace this default False + # If your JSON schema fails to generate it is probably + # because one of the following two branches failed. + if use_strict: + return self.generate_inner(schema['strict_schema']) + else: + return self.generate_inner(schema['lax_schema']) + + def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the + Python schema. + + The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override + this method. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['json_schema']) + + def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a typed dict. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + total = schema.get('total', True) + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (name, self.field_is_required(field, total), field) + for name, field in schema['fields'].items() + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + cls = _get_typed_dict_cls(schema) + config = _get_typed_dict_config(cls) + with self._config_wrapper_stack.push(config): + json_schema = self._named_required_fields_schema(named_required_fields) + + json_schema_extra = config.get('json_schema_extra') + extra = schema.get('extra_behavior') + if extra is None: + extra = config.get('extra', 'ignore') + + if cls is not None: + title = config.get('title') or cls.__name__ + json_schema = self._update_class_schema(json_schema, title, extra, cls, json_schema_extra) + else: + if extra == 'forbid': + json_schema['additionalProperties'] = False + elif extra == 'allow': + json_schema['additionalProperties'] = True + + return json_schema + + @staticmethod + def _name_required_computed_fields( + computed_fields: list[ComputedField], + ) -> list[tuple[str, bool, core_schema.ComputedField]]: + return [(field['property_name'], True, field) for field in computed_fields] + + def _named_required_fields_schema( + self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]] + ) -> JsonSchemaValue: + properties: dict[str, JsonSchemaValue] = {} + required_fields: list[str] = [] + for name, required, field in named_required_fields: + if self.by_alias: + name = self._get_alias_name(field, name) + try: + field_json_schema = self.generate_inner(field).copy() + except PydanticOmit: + continue + if 'title' not in field_json_schema and self.field_title_should_be_set(field): + title = self.get_title_from_name(name) + field_json_schema['title'] = title + field_json_schema = self.handle_ref_overrides(field_json_schema) + properties[name] = field_json_schema + if required: + required_fields.append(name) + + json_schema = {'type': 'object', 'properties': properties} + if required_fields: + json_schema['required'] = required_fields + return json_schema + + def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: + if field['type'] == 'computed-field': + alias: Any = field.get('alias', name) + elif self.mode == 'validation': + alias = field.get('validation_alias', name) + else: + alias = field.get('serialization_alias', name) + if isinstance(alias, str): + name = alias + elif isinstance(alias, list): + alias = cast('list[str] | str', alias) + for path in alias: + if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): + # Use the first valid single-item string path; the code that constructs the alias array + # should ensure the first such item is what belongs in the JSON schema + name = path[0] + break + else: + assert_never(alias) + return name + + def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a typed dict field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a computed field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['return_schema']) + + def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # We do not use schema['model'].model_json_schema() here + # because it could lead to inconsistent refs handling, etc. + cls = cast('type[BaseModel]', schema['cls']) + config = cls.model_config + title = config.get('title') + + with self._config_wrapper_stack.push(config): + json_schema = self.generate_inner(schema['schema']) + + json_schema_extra = config.get('json_schema_extra') + if cls.__pydantic_root_model__: + root_json_schema_extra = cls.model_fields['root'].json_schema_extra + if json_schema_extra and root_json_schema_extra: + raise ValueError( + '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"' + ' field must not be set simultaneously' + ) + if root_json_schema_extra: + json_schema_extra = root_json_schema_extra + + json_schema = self._update_class_schema(json_schema, title, config.get('extra', None), cls, json_schema_extra) + + return json_schema + + def _update_class_schema( + self, + json_schema: JsonSchemaValue, + title: str | None, + extra: Literal['allow', 'ignore', 'forbid'] | None, + cls: type[Any], + json_schema_extra: JsonDict | JsonSchemaExtraCallable | None, + ) -> JsonSchemaValue: + if '$ref' in json_schema: + schema_to_update = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) or json_schema + else: + schema_to_update = json_schema + + if title is not None: + # referenced_schema['title'] = title + schema_to_update.setdefault('title', title) + + if 'additionalProperties' not in schema_to_update: + if extra == 'allow': + schema_to_update['additionalProperties'] = True + elif extra == 'forbid': + schema_to_update['additionalProperties'] = False + + if isinstance(json_schema_extra, (staticmethod, classmethod)): + # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable + json_schema_extra = json_schema_extra.__get__(cls) + + if isinstance(json_schema_extra, dict): + schema_to_update.update(json_schema_extra) + elif callable(json_schema_extra): + if len(inspect.signature(json_schema_extra).parameters) > 1: + json_schema_extra(schema_to_update, cls) # type: ignore + else: + json_schema_extra(schema_to_update) # type: ignore + elif json_schema_extra is not None: + raise ValueError( + f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None" + ) + + return json_schema + + def resolve_schema_to_update(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: + """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema. + + Args: + json_schema: The schema to resolve. + + Returns: + The resolved schema. + """ + if '$ref' in json_schema: + schema_to_update = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) + if schema_to_update is None: + raise RuntimeError(f'Cannot update undefined schema for $ref={json_schema["$ref"]}') + return self.resolve_schema_to_update(schema_to_update) + else: + schema_to_update = json_schema + return schema_to_update + + def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model's fields. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (name, self.field_is_required(field, total=True), field) + for name, field in schema['fields'].items() + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + json_schema = self._named_required_fields_schema(named_required_fields) + extras_schema = schema.get('extras_schema', None) + if extras_schema is not None: + schema_to_update = self.resolve_schema_to_update(json_schema) + schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) + return json_schema + + def field_is_present(self, field: CoreSchemaField) -> bool: + """Whether the field should be included in the generated JSON schema. + + Args: + field: The schema for the field itself. + + Returns: + `True` if the field should be included in the generated JSON schema, `False` otherwise. + """ + if self.mode == 'serialization': + # If you still want to include the field in the generated JSON schema, + # override this method and return True + return not field.get('serialization_exclude') + elif self.mode == 'validation': + return True + else: + assert_never(self.mode) + + def field_is_required( + self, + field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField, + total: bool, + ) -> bool: + """Whether the field should be marked as required in the generated JSON schema. + (Note that this is irrelevant if the field is not present in the JSON schema.). + + Args: + field: The schema for the field itself. + total: Only applies to `TypedDictField`s. + Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't + explicitly specify `required=False` are required. + + Returns: + `True` if the field should be marked as required in the generated JSON schema, `False` otherwise. + """ + if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: + return not field.get('serialization_exclude') + else: + if field['type'] == 'typed-dict-field': + return field.get('required', total) + else: + return field['schema']['type'] != 'default' + + def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (field['name'], self.field_is_required(field, total=True), field) + for field in schema['fields'] + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + return self._named_required_fields_schema(named_required_fields) + + def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + cls = schema['cls'] + config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) + title = config.get('title') or cls.__name__ + + with self._config_wrapper_stack.push(config): + json_schema = self.generate_inner(schema['schema']).copy() + + json_schema_extra = config.get('json_schema_extra') + json_schema = self._update_class_schema(json_schema, title, config.get('extra', None), cls, json_schema_extra) + + # Dataclass-specific handling of description + if is_dataclass(cls) and not hasattr(cls, '__pydantic_validator__'): + # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default + description = None + else: + description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) + if description: + json_schema['description'] = description + + return json_schema + + def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's arguments. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + metadata = _core_metadata.CoreMetadataHandler(schema).metadata + prefer_positional = metadata.get('pydantic_js_prefer_positional_arguments') + + arguments = schema['arguments_schema'] + kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] + kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] + p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] + var_args_schema = schema.get('var_args_schema') + var_kwargs_schema = schema.get('var_kwargs_schema') + + if prefer_positional: + positional_possible = not kw_only_arguments and not var_kwargs_schema + if positional_possible: + return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) + + keyword_possible = not p_only_arguments and not var_args_schema + if keyword_possible: + return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) + + if not prefer_positional: + positional_possible = not kw_only_arguments and not var_kwargs_schema + if positional_possible: + return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) + + raise PydanticInvalidForJsonSchema( + 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments' + ) + + def kw_arguments_schema( + self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None + ) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's keyword arguments. + + Args: + arguments: The core schema. + + Returns: + The generated JSON schema. + """ + properties: dict[str, JsonSchemaValue] = {} + required: list[str] = [] + for argument in arguments: + name = self.get_argument_name(argument) + argument_schema = self.generate_inner(argument['schema']).copy() + argument_schema['title'] = self.get_title_from_name(name) + properties[name] = argument_schema + + if argument['schema']['type'] != 'default': + # This assumes that if the argument has a default value, + # the inner schema must be of type WithDefaultSchema. + # I believe this is true, but I am not 100% sure + required.append(name) + + json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} + if required: + json_schema['required'] = required + + if var_kwargs_schema: + additional_properties_schema = self.generate_inner(var_kwargs_schema) + if additional_properties_schema: + json_schema['additionalProperties'] = additional_properties_schema + else: + json_schema['additionalProperties'] = False + return json_schema + + def p_arguments_schema( + self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None + ) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's positional arguments. + + Args: + arguments: The core schema. + + Returns: + The generated JSON schema. + """ + prefix_items: list[JsonSchemaValue] = [] + min_items = 0 + + for argument in arguments: + name = self.get_argument_name(argument) + + argument_schema = self.generate_inner(argument['schema']).copy() + argument_schema['title'] = self.get_title_from_name(name) + prefix_items.append(argument_schema) + + if argument['schema']['type'] != 'default': + # This assumes that if the argument has a default value, + # the inner schema must be of type WithDefaultSchema. + # I believe this is true, but I am not 100% sure + min_items += 1 + + json_schema: JsonSchemaValue = {'type': 'array', 'prefixItems': prefix_items} + if min_items: + json_schema['minItems'] = min_items + + if var_args_schema: + items_schema = self.generate_inner(var_args_schema) + if items_schema: + json_schema['items'] = items_schema + else: + json_schema['maxItems'] = len(prefix_items) + + return json_schema + + def get_argument_name(self, argument: core_schema.ArgumentsParameter) -> str: + """Retrieves the name of an argument. + + Args: + argument: The core schema. + + Returns: + The name of the argument. + """ + name = argument['name'] + if self.by_alias: + alias = argument.get('alias') + if isinstance(alias, str): + name = alias + else: + pass # might want to do something else? + return name + + def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function call. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['arguments_schema']) + + def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a custom error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a JSON object. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + content_core_schema = schema.get('schema') or core_schema.any_schema() + content_json_schema = self.generate_inner(content_core_schema) + if self.mode == 'validation': + return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} + else: + # self.mode == 'serialization' + return content_json_schema + + def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a URL. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + return json_schema + + def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec + json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + return json_schema + + def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a UUID. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'uuid'} + + def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a JSON object with definitions. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + for definition in schema['definitions']: + try: + self.generate_inner(definition) + except PydanticInvalidForJsonSchema as e: + core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore + self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e + continue + return self.generate_inner(schema['schema']) + + def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that references a definition. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + core_ref = CoreRef(schema['schema_ref']) + _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) + return ref_json_schema + + def ser_schema( + self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema + ) -> JsonSchemaValue | None: + """Generates a JSON schema that matches a schema that defines a serialized object. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + schema_type = schema['type'] + if schema_type == 'function-plain' or schema_type == 'function-wrap': + # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema + return_schema = schema.get('return_schema') + if return_schema is not None: + return self.generate_inner(return_schema) + elif schema_type == 'format' or schema_type == 'to-string': + # FormatSerSchema or ToStringSerSchema + return self.str_schema(core_schema.str_schema()) + elif schema['type'] == 'model': + # ModelSerSchema + return self.generate_inner(schema['schema']) + return None + + # ### Utility methods + + def get_title_from_name(self, name: str) -> str: + """Retrieves a title from a name. + + Args: + name: The name to retrieve a title from. + + Returns: + The title. + """ + return name.title().replace('_', ' ') + + def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: + """Returns true if a field with the given schema should have a title set based on the field name. + + Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title + (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses). + + Args: + schema: The schema to check. + + Returns: + `True` if the field should have a title set, `False` otherwise. + """ + if _core_utils.is_core_schema_field(schema): + if schema['type'] == 'computed-field': + field_schema = schema['return_schema'] + else: + field_schema = schema['schema'] + return self.field_title_should_be_set(field_schema) + + elif _core_utils.is_core_schema(schema): + if schema.get('ref'): # things with refs, such as models and enums, should not have titles set + return False + if schema['type'] in {'default', 'nullable', 'definitions'}: + return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] + if _core_utils.is_function_with_inner_schema(schema): + return self.field_title_should_be_set(schema['schema']) + if schema['type'] == 'definition-ref': + # Referenced schemas should not have titles set for the same reason + # schemas with refs should not + return False + return True # anything else should have title set + + else: + raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover + + def normalize_name(self, name: str) -> str: + """Normalizes a name to be used as a key in a dictionary. + + Args: + name: The name to normalize. + + Returns: + The normalized name. + """ + return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') + + def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: + """Override this method to change the way that definitions keys are generated from a core reference. + + Args: + core_mode_ref: The core reference. + + Returns: + The definitions key. + """ + # Split the core ref into "components"; generic origins and arguments are each separate components + core_ref, mode = core_mode_ref + components = re.split(r'([\][,])', core_ref) + # Remove IDs from each component + components = [x.rsplit(':', 1)[0] for x in components] + core_ref_no_id = ''.join(components) + # Remove everything before the last period from each "component" + components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] + short_ref = ''.join(components) + + mode_title = _MODE_TITLE_MAPPING[mode] + + # It is important that the generated defs_ref values be such that at least one choice will not + # be generated for any other core_ref. Currently, this should be the case because we include + # the id of the source type in the core_ref + name = DefsRef(self.normalize_name(short_ref)) + name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') + module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) + module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') + module_qualname_id = DefsRef(self.normalize_name(core_ref)) + occurrence_index = self._collision_index.get(module_qualname_id) + if occurrence_index is None: + self._collision_counter[module_qualname] += 1 + occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] + + module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') + module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') + + self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ + name, + name_mode, + module_qualname, + module_qualname_mode, + module_qualname_occurrence, + module_qualname_occurrence_mode, + ] + + return module_qualname_occurrence_mode + + def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: + """This method wraps the get_defs_ref method with some cache-lookup/population logic, + and returns both the produced defs_ref and the JSON schema that will refer to the right definition. + + Args: + core_ref: The core reference to get the definitions reference for. + + Returns: + A tuple of the definitions reference and the JSON schema that will refer to it. + """ + core_mode_ref = (core_ref, self.mode) + maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) + if maybe_defs_ref is not None: + json_ref = self.core_to_json_refs[core_mode_ref] + return maybe_defs_ref, {'$ref': json_ref} + + defs_ref = self.get_defs_ref(core_mode_ref) + + # populate the ref translation mappings + self.core_to_defs_refs[core_mode_ref] = defs_ref + self.defs_to_core_refs[defs_ref] = core_mode_ref + + json_ref = JsonRef(self.ref_template.format(model=defs_ref)) + self.core_to_json_refs[core_mode_ref] = json_ref + self.json_to_defs_refs[json_ref] = defs_ref + ref_json_schema = {'$ref': json_ref} + return defs_ref, ref_json_schema + + def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: + """It is not valid for a schema with a top-level $ref to have sibling keys. + + During our own schema generation, we treat sibling keys as overrides to the referenced schema, + but this is not how the official JSON schema spec works. + + Because of this, we first remove any sibling keys that are redundant with the referenced schema, then if + any remain, we transform the schema from a top-level '$ref' to use allOf to move the $ref out of the top level. + (See bottom of https://swagger.io/docs/specification/using-ref/ for a reference about this behavior) + """ + if '$ref' in json_schema: + # prevent modifications to the input; this copy may be safe to drop if there is significant overhead + json_schema = json_schema.copy() + + referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) + if referenced_json_schema is None: + # This can happen when building schemas for models with not-yet-defined references. + # It may be a good idea to do a recursive pass at the end of the generation to remove + # any redundant override keys. + if len(json_schema) > 1: + # Make it an allOf to at least resolve the sibling keys issue + json_schema = json_schema.copy() + json_schema.setdefault('allOf', []) + json_schema['allOf'].append({'$ref': json_schema['$ref']}) + del json_schema['$ref'] + + return json_schema + for k, v in list(json_schema.items()): + if k == '$ref': + continue + if k in referenced_json_schema and referenced_json_schema[k] == v: + del json_schema[k] # redundant key + if len(json_schema) > 1: + # There is a remaining "override" key, so we need to move $ref out of the top level + json_ref = JsonRef(json_schema['$ref']) + del json_schema['$ref'] + assert 'allOf' not in json_schema # this should never happen, but just in case + json_schema['allOf'] = [{'$ref': json_ref}] + + return json_schema + + def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: + def_ref = self.json_to_defs_refs[json_ref] + if def_ref in self._core_defs_invalid_for_json_schema: + raise self._core_defs_invalid_for_json_schema[def_ref] + return self.definitions.get(def_ref, None) + + def encode_default(self, dft: Any) -> Any: + """Encode a default value to a JSON-serializable value. + + This is used to encode default values for fields in the generated JSON schema. + + Args: + dft: The default value to encode. + + Returns: + The encoded default value. + """ + from .type_adapter import TypeAdapter, _type_has_config + + config = self._config + try: + default = ( + dft + if _type_has_config(type(dft)) + else TypeAdapter(type(dft), config=config.config_dict).dump_python(dft, mode='json') + ) + except PydanticSchemaGenerationError: + raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') + + return pydantic_core.to_jsonable_python( + default, + timedelta_mode=config.ser_json_timedelta, + bytes_mode=config.ser_json_bytes, + ) + + def update_with_validations( + self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str] + ) -> None: + """Update the json_schema with the corresponding validations specified in the core_schema, + using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema. + + Args: + json_schema: The JSON schema to update. + core_schema: The core schema to get the validations from. + mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names. + """ + for core_key, json_schema_key in mapping.items(): + if core_key in core_schema: + json_schema[json_schema_key] = core_schema[core_key] + + class ValidationsMapping: + """This class just contains mappings from core_schema attribute names to the corresponding + JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in + principle override this class in a subclass of GenerateJsonSchema (by inheriting from + GenerateJsonSchema.ValidationsMapping) to change these mappings. + """ + + numeric = { + 'multiple_of': 'multipleOf', + 'le': 'maximum', + 'ge': 'minimum', + 'lt': 'exclusiveMaximum', + 'gt': 'exclusiveMinimum', + } + bytes = { + 'min_length': 'minLength', + 'max_length': 'maxLength', + } + string = { + 'min_length': 'minLength', + 'max_length': 'maxLength', + 'pattern': 'pattern', + } + array = { + 'min_length': 'minItems', + 'max_length': 'maxItems', + } + object = { + 'min_length': 'minProperties', + 'max_length': 'maxProperties', + } + date = { + 'le': 'maximum', + 'ge': 'minimum', + 'lt': 'exclusiveMaximum', + 'gt': 'exclusiveMinimum', + } + + def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: + members = [] + for schema in schemas: + if len(schema) == 1 and 'anyOf' in schema: + members.extend(schema['anyOf']) + else: + members.append(schema) + members = _deduplicate_schemas(members) + if len(members) == 1: + return members[0] + return {'anyOf': members} + + def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: + """Get all values corresponding to the key '$ref' anywhere in the json_schema.""" + json_refs: dict[JsonRef, int] = Counter() + + def _add_json_refs(schema: Any) -> None: + if isinstance(schema, dict): + if '$ref' in schema: + json_ref = JsonRef(schema['$ref']) + if not isinstance(json_ref, str): + return # in this case, '$ref' might have been the name of a property + already_visited = json_ref in json_refs + json_refs[json_ref] += 1 + if already_visited: + return # prevent recursion on a definition that was already visited + defs_ref = self.json_to_defs_refs[json_ref] + if defs_ref in self._core_defs_invalid_for_json_schema: + raise self._core_defs_invalid_for_json_schema[defs_ref] + _add_json_refs(self.definitions[defs_ref]) + + for v in schema.values(): + _add_json_refs(v) + elif isinstance(schema, list): + for v in schema: + _add_json_refs(v) + + _add_json_refs(json_schema) + return json_refs + + def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: + raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') + + def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: + """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method.""" + message = self.render_warning_message(kind, detail) + if message is not None: + warnings.warn(message, PydanticJsonSchemaWarning) + + def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: + """This method is responsible for ignoring warnings as desired, and for formatting the warning messages. + + You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema + to modify what warnings are generated. If you want more control, you can override this method; + just return None in situations where you don't want warnings to be emitted. + + Args: + kind: The kind of warning to render. It can be one of the following: + + - 'skipped-choice': A choice field was skipped because it had no valid choices. + - 'non-serializable-default': A default value was skipped because it was not JSON-serializable. + detail: A string with additional details about the warning. + + Returns: + The formatted warning message, or `None` if no warning should be emitted. + """ + if kind in self.ignored_warning_kinds: + return None + return f'{detail} [{kind}]' + + def _build_definitions_remapping(self) -> _DefinitionsRemapping: + defs_to_json: dict[DefsRef, JsonRef] = {} + for defs_refs in self._prioritized_defsref_choices.values(): + for defs_ref in defs_refs: + json_ref = JsonRef(self.ref_template.format(model=defs_ref)) + defs_to_json[defs_ref] = json_ref + + return _DefinitionsRemapping.from_prioritized_choices( + self._prioritized_defsref_choices, defs_to_json, self.definitions + ) + + def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: + visited_defs_refs: set[DefsRef] = set() + unvisited_json_refs = _get_all_json_refs(schema) + while unvisited_json_refs: + next_json_ref = unvisited_json_refs.pop() + next_defs_ref = self.json_to_defs_refs[next_json_ref] + if next_defs_ref in visited_defs_refs: + continue + visited_defs_refs.add(next_defs_ref) + unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) + + self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} + + +# ##### Start JSON Schema Generation Functions ##### + + +def model_json_schema( + cls: type[BaseModel] | type[PydanticDataclass], + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', +) -> dict[str, Any]: + """Utility function to generate a JSON Schema for a model. + + Args: + cls: The model class to generate a JSON Schema for. + by_alias: If `True` (the default), fields will be serialized according to their alias. + If `False`, fields will be serialized according to their attribute name. + ref_template: The template to use for generating JSON Schema references. + schema_generator: The class to use for generating the JSON Schema. + mode: The mode to use for generating the JSON Schema. It can be one of the following: + + - 'validation': Generate a JSON Schema for validating data. + - 'serialization': Generate a JSON Schema for serializing data. + + Returns: + The generated JSON Schema. + """ + from .main import BaseModel + + schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) + if isinstance(cls.__pydantic_validator__, _mock_val_ser.MockValSer): + cls.__pydantic_validator__.rebuild() + + if cls is BaseModel: + raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.') + assert '__pydantic_core_schema__' in cls.__dict__, 'this is a bug! please report it' + return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) + + +def models_json_schema( + models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]], + *, + by_alias: bool = True, + title: str | None = None, + description: str | None = None, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, +) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: + """Utility function to generate a JSON Schema for multiple models. + + Args: + models: A sequence of tuples of the form (model, mode). + by_alias: Whether field aliases should be used as keys in the generated JSON Schema. + title: The title of the generated JSON Schema. + description: The description of the generated JSON Schema. + ref_template: The reference template to use for generating JSON Schema references. + schema_generator: The schema generator to use for generating the JSON Schema. + + Returns: + A tuple where: + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a JSON schema containing all definitions referenced in the first returned + element, along with the optional title and description keys. + """ + for cls, _ in models: + if isinstance(cls.__pydantic_validator__, _mock_val_ser.MockValSer): + cls.__pydantic_validator__.rebuild() + + instance = schema_generator(by_alias=by_alias, ref_template=ref_template) + inputs = [(m, mode, m.__pydantic_core_schema__) for m, mode in models] + json_schemas_map, definitions = instance.generate_definitions(inputs) + + json_schema: dict[str, Any] = {} + if definitions: + json_schema['$defs'] = definitions + if title: + json_schema['title'] = title + if description: + json_schema['description'] = description + + return json_schemas_map, json_schema + + +# ##### End JSON Schema Generation Functions ##### + + +_HashableJsonValue: TypeAlias = Union[ + int, float, str, bool, None, Tuple['_HashableJsonValue', ...], Tuple[Tuple[str, '_HashableJsonValue'], ...] +] + + +def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: + return list({_make_json_hashable(schema): schema for schema in schemas}.values()) + + +def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: + if isinstance(value, dict): + return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) + elif isinstance(value, list): + return tuple(_make_json_hashable(v) for v in value) + else: + return value + + +def _sort_json_schema(value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: + if isinstance(value, dict): + sorted_dict: dict[str, JsonSchemaValue] = {} + keys = value.keys() + if (parent_key != 'properties') and (parent_key != 'default'): + keys = sorted(keys) + for key in keys: + sorted_dict[key] = _sort_json_schema(value[key], parent_key=key) + return sorted_dict + elif isinstance(value, list): + sorted_list: list[JsonSchemaValue] = [] + for item in value: # type: ignore + sorted_list.append(_sort_json_schema(item, parent_key)) + return sorted_list # type: ignore + else: + return value + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class WithJsonSchema: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json_schema/#withjsonschema-annotation + + Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field. + This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema, + such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a + custom subclass of pydantic.json_schema.GenerateJsonSchema. + Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields) + will still be performed. + + If `mode` is set this will only apply to that schema generation mode, allowing you + to set different json schemas for validation and serialization. + """ + + json_schema: JsonSchemaValue | None + mode: Literal['validation', 'serialization'] | None = None + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + mode = self.mode or handler.mode + if mode != handler.mode: + return handler(core_schema) + if self.json_schema is None: + # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema + raise PydanticOmit + else: + return self.json_schema + + def __hash__(self) -> int: + return hash(type(self.mode)) + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class Examples: + """Add examples to a JSON schema. + + Examples should be a map of example names (strings) + to example values (any valid JSON). + + If `mode` is set this will only apply to that schema generation mode, + allowing you to add different examples for validation and serialization. + """ + + examples: dict[str, Any] + mode: Literal['validation', 'serialization'] | None = None + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + mode = self.mode or handler.mode + json_schema = handler(core_schema) + if mode != handler.mode: + return json_schema + examples = json_schema.get('examples', {}) + examples.update(to_jsonable_python(self.examples)) + json_schema['examples'] = examples + return json_schema + + def __hash__(self) -> int: + return hash(type(self.mode)) + + +def _get_all_json_refs(item: Any) -> set[JsonRef]: + """Get all the definitions references from a JSON schema.""" + refs: set[JsonRef] = set() + if isinstance(item, dict): + for key, value in item.items(): + if key == '$ref' and isinstance(value, str): + # the isinstance check ensures that '$ref' isn't the name of a property, etc. + refs.add(JsonRef(value)) + elif isinstance(value, dict): + refs.update(_get_all_json_refs(value)) + elif isinstance(value, list): + for item in value: + refs.update(_get_all_json_refs(item)) + elif isinstance(item, list): + for item in item: + refs.update(_get_all_json_refs(item)) + return refs + + +AnyType = TypeVar('AnyType') + +if TYPE_CHECKING: + SkipJsonSchema = Annotated[AnyType, ...] +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SkipJsonSchema: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json_schema/#skipjsonschema-annotation + + Add this as an annotation on a field to skip generating a JSON schema for that field. + + Example: + ```py + from typing import Union + + from pydantic import BaseModel + from pydantic.json_schema import SkipJsonSchema + + from pprint import pprint + + + class Model(BaseModel): + a: Union[int, None] = None # (1)! + b: Union[int, SkipJsonSchema[None]] = None # (2)! + c: SkipJsonSchema[Union[int, None]] = None # (3)! + + + pprint(Model.model_json_schema()) + ''' + { + 'properties': { + 'a': { + 'anyOf': [ + {'type': 'integer'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'A' + }, + 'b': { + 'default': None, + 'title': 'B', + 'type': 'integer' + } + }, + 'title': 'Model', + 'type': 'object' + } + ''' + ``` + + 1. The integer and null types are both included in the schema for `a`. + 2. The integer type is the only type included in the schema for `b`. + 3. The entirety of the `c` field is omitted from the schema. + """ + + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + def __get_pydantic_json_schema__( + self, core_schema: CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + raise PydanticOmit + + def __hash__(self) -> int: + return hash(type(self)) + + +def _get_typed_dict_cls(schema: core_schema.TypedDictSchema) -> type[Any] | None: + metadata = _core_metadata.CoreMetadataHandler(schema).metadata + cls = metadata.get('pydantic_typed_dict_cls') + return cls + + +def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: + if cls is not None: + try: + return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') + except AttributeError: + pass + return {} diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/main.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/main.py new file mode 100644 index 0000000..5d87f7c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/main.py @@ -0,0 +1,1560 @@ +"""Logic for creating models.""" +from __future__ import annotations as _annotations + +import operator +import sys +import types +import typing +import warnings +from copy import copy, deepcopy +from typing import Any, ClassVar, Dict, Generator, Literal, Set, Tuple, TypeVar, Union + +import pydantic_core +import typing_extensions +from pydantic_core import PydanticUndefined +from typing_extensions import TypeAlias + +from ._internal import ( + _config, + _decorators, + _fields, + _forward_ref, + _generics, + _mock_val_ser, + _model_construction, + _repr, + _typing_extra, + _utils, +) +from ._migration import getattr_migration +from .aliases import AliasChoices, AliasPath +from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler +from .config import ConfigDict +from .errors import PydanticUndefinedAnnotation, PydanticUserError +from .json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema, JsonSchemaMode, JsonSchemaValue, model_json_schema +from .warnings import PydanticDeprecatedSince20 + +# Always define certain types that are needed to resolve method type hints/annotations +# (even when not type checking) via typing.get_type_hints. +Model = TypeVar('Model', bound='BaseModel') +TupleGenerator = Generator[Tuple[str, Any], None, None] +# should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope +IncEx: TypeAlias = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any], None] + + +if typing.TYPE_CHECKING: + from inspect import Signature + from pathlib import Path + + from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator + from typing_extensions import Unpack + + from ._internal._utils import AbstractSetIntStr, MappingIntStrAny + from .deprecated.parse import Protocol as DeprecatedParseProtocol + from .fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr + from .fields import Field as _Field +else: + # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915 + # and https://youtrack.jetbrains.com/issue/PY-51428 + DeprecationWarning = PydanticDeprecatedSince20 + +__all__ = 'BaseModel', 'create_model' + +_object_setattr = _model_construction.object_setattr + + +class BaseModel(metaclass=_model_construction.ModelMetaclass): + """Usage docs: https://docs.pydantic.dev/2.7/concepts/models/ + + A base class for creating Pydantic models. + + Attributes: + __class_vars__: The names of classvars defined on the model. + __private_attributes__: Metadata about the private attributes of the model. + __signature__: The signature for instantiating the model. + + __pydantic_complete__: Whether model building is completed, or if there are still undefined fields. + __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer. + __pydantic_custom_init__: Whether the model has a custom `__init__` function. + __pydantic_decorators__: Metadata containing the decorators defined on the model. + This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1. + __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to + __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these. + __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models. + __pydantic_post_init__: The name of the post-init method for the model, if defined. + __pydantic_root_model__: Whether the model is a `RootModel`. + __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the model. + __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the model. + + __pydantic_extra__: An instance attribute with the values of extra fields from validation when + `model_config['extra'] == 'allow'`. + __pydantic_fields_set__: An instance attribute with the names of fields explicitly set. + __pydantic_private__: Instance attribute with the values of private attributes set on the model instance. + """ + + if typing.TYPE_CHECKING: + # Here we provide annotations for the attributes of BaseModel. + # Many of these are populated by the metaclass, which is why this section is in a `TYPE_CHECKING` block. + # However, for the sake of easy review, we have included type annotations of all class and instance attributes + # of `BaseModel` here: + + # Class attributes + model_config: ClassVar[ConfigDict] + """ + Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict]. + """ + + model_fields: ClassVar[dict[str, FieldInfo]] + """ + Metadata about the fields defined on the model, + mapping of field names to [`FieldInfo`][pydantic.fields.FieldInfo]. + + This replaces `Model.__fields__` from Pydantic V1. + """ + + model_computed_fields: ClassVar[dict[str, ComputedFieldInfo]] + """A dictionary of computed field names and their corresponding `ComputedFieldInfo` objects.""" + + __class_vars__: ClassVar[set[str]] + __private_attributes__: ClassVar[dict[str, ModelPrivateAttr]] + __signature__: ClassVar[Signature] + + __pydantic_complete__: ClassVar[bool] + __pydantic_core_schema__: ClassVar[CoreSchema] + __pydantic_custom_init__: ClassVar[bool] + __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] + __pydantic_generic_metadata__: ClassVar[_generics.PydanticGenericMetadata] + __pydantic_parent_namespace__: ClassVar[dict[str, Any] | None] + __pydantic_post_init__: ClassVar[None | Literal['model_post_init']] + __pydantic_root_model__: ClassVar[bool] + __pydantic_serializer__: ClassVar[SchemaSerializer] + __pydantic_validator__: ClassVar[SchemaValidator] + + # Instance attributes + # Note: we use the non-existent kwarg `init=False` in pydantic.fields.Field below so that @dataclass_transform + # doesn't think these are valid as keyword arguments to the class initializer. + __pydantic_extra__: dict[str, Any] | None = _Field(init=False) # type: ignore + __pydantic_fields_set__: set[str] = _Field(init=False) # type: ignore + __pydantic_private__: dict[str, Any] | None = _Field(init=False) # type: ignore + + else: + # `model_fields` and `__pydantic_decorators__` must be set for + # pydantic._internal._generate_schema.GenerateSchema.model_schema to work for a plain BaseModel annotation + model_fields = {} + model_computed_fields = {} + + __pydantic_decorators__ = _decorators.DecoratorInfos() + __pydantic_parent_namespace__ = None + # Prevent `BaseModel` from being instantiated directly: + __pydantic_validator__ = _mock_val_ser.MockValSer( + 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly', + val_or_ser='validator', + code='base-model-instantiated', + ) + __pydantic_serializer__ = _mock_val_ser.MockValSer( + 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly', + val_or_ser='serializer', + code='base-model-instantiated', + ) + + __slots__ = '__dict__', '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__' + + model_config = ConfigDict() + __pydantic_complete__ = False + __pydantic_root_model__ = False + + def __init__(self, /, **data: Any) -> None: # type: ignore + """Create a new model by parsing and validating input data from keyword arguments. + + Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be + validated to form a valid model. + + `self` is explicitly positional-only to allow `self` as a field name. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + self.__pydantic_validator__.validate_python(data, self_instance=self) + + # The following line sets a flag that we use to determine when `__init__` gets overridden by the user + __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess] + + @property + def model_extra(self) -> dict[str, Any] | None: + """Get extra fields set during validation. + + Returns: + A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. + """ + return self.__pydantic_extra__ + + @property + def model_fields_set(self) -> set[str]: + """Returns the set of fields that have been explicitly set on this model instance. + + Returns: + A set of strings representing the fields that have been set, + i.e. that were not filled from defaults. + """ + return self.__pydantic_fields_set__ + + @classmethod + def model_construct(cls: type[Model], _fields_set: set[str] | None = None, **values: Any) -> Model: # noqa: C901 + """Creates a new instance of the `Model` class with validated data. + + Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data. + Default values are respected, but no other validation is performed. + + !!! note + `model_construct()` generally respects the `model_config.extra` setting on the provided model. + That is, if `model_config.extra == 'allow'`, then all extra passed values are added to the model instance's `__dict__` + and `__pydantic_extra__` fields. If `model_config.extra == 'ignore'` (the default), then all extra passed values are ignored. + Because no validation is performed with a call to `model_construct()`, having `model_config.extra == 'forbid'` does not result in + an error if extra values are passed, but they will be ignored. + + Args: + _fields_set: The set of field names accepted for the Model instance. + values: Trusted or pre-validated data dictionary. + + Returns: + A new instance of the `Model` class with validated data. + """ + m = cls.__new__(cls) + fields_values: dict[str, Any] = {} + fields_set = set() + + for name, field in cls.model_fields.items(): + if field.alias is not None and field.alias in values: + fields_values[name] = values.pop(field.alias) + fields_set.add(name) + + if (name not in fields_set) and (field.validation_alias is not None): + validation_aliases: list[str | AliasPath] = ( + field.validation_alias.choices + if isinstance(field.validation_alias, AliasChoices) + else [field.validation_alias] + ) + + for alias in validation_aliases: + if isinstance(alias, str) and alias in values: + fields_values[name] = values.pop(alias) + fields_set.add(name) + break + elif isinstance(alias, AliasPath): + value = alias.search_dict_for_path(values) + if value is not PydanticUndefined: + fields_values[name] = value + fields_set.add(name) + break + + if name not in fields_set: + if name in values: + fields_values[name] = values.pop(name) + fields_set.add(name) + elif not field.is_required(): + fields_values[name] = field.get_default(call_default_factory=True) + if _fields_set is None: + _fields_set = fields_set + + _extra: dict[str, Any] | None = ( + {k: v for k, v in values.items()} if cls.model_config.get('extra') == 'allow' else None + ) + _object_setattr(m, '__dict__', fields_values) + _object_setattr(m, '__pydantic_fields_set__', _fields_set) + if not cls.__pydantic_root_model__: + _object_setattr(m, '__pydantic_extra__', _extra) + + if cls.__pydantic_post_init__: + m.model_post_init(None) + # update private attributes with values set + if hasattr(m, '__pydantic_private__') and m.__pydantic_private__ is not None: + for k, v in values.items(): + if k in m.__private_attributes__: + m.__pydantic_private__[k] = v + + elif not cls.__pydantic_root_model__: + # Note: if there are any private attributes, cls.__pydantic_post_init__ would exist + # Since it doesn't, that means that `__pydantic_private__` should be set to None + _object_setattr(m, '__pydantic_private__', None) + + return m + + def model_copy(self: Model, *, update: dict[str, Any] | None = None, deep: bool = False) -> Model: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/serialization/#model_copy + + Returns a copy of the model. + + Args: + update: Values to change/add in the new model. Note: the data is not validated + before creating the new model. You should trust this data. + deep: Set to `True` to make a deep copy of the model. + + Returns: + New model instance. + """ + copied = self.__deepcopy__() if deep else self.__copy__() + if update: + if self.model_config.get('extra') == 'allow': + for k, v in update.items(): + if k in self.model_fields: + copied.__dict__[k] = v + else: + if copied.__pydantic_extra__ is None: + copied.__pydantic_extra__ = {} + copied.__pydantic_extra__[k] = v + else: + copied.__dict__.update(update) + copied.__pydantic_fields_set__.update(update.keys()) + return copied + + def model_dump( + self, + *, + mode: Literal['json', 'python'] | str = 'python', + include: IncEx = None, + exclude: IncEx = None, + context: dict[str, Any] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A dictionary representation of the model. + """ + return self.__pydantic_serializer__.to_python( + self, + mode=mode, + by_alias=by_alias, + include=include, + exclude=exclude, + context=context, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + round_trip=round_trip, + warnings=warnings, + serialize_as_any=serialize_as_any, + ) + + def model_dump_json( + self, + *, + indent: int | None = None, + include: IncEx = None, + exclude: IncEx = None, + context: dict[str, Any] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. + exclude: Field(s) to exclude from the JSON output. + context: Additional context to pass to the serializer. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A JSON string representation of the model. + """ + return self.__pydantic_serializer__.to_json( + self, + indent=indent, + include=include, + exclude=exclude, + context=context, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + round_trip=round_trip, + warnings=warnings, + serialize_as_any=serialize_as_any, + ).decode() + + @classmethod + def model_json_schema( + cls, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', + ) -> dict[str, Any]: + """Generates a JSON schema for a model class. + + Args: + by_alias: Whether to use attribute aliases or not. + ref_template: The reference template. + schema_generator: To override the logic used to generate the JSON schema, as a subclass of + `GenerateJsonSchema` with your desired modifications + mode: The mode in which to generate the schema. + + Returns: + The JSON schema for the given model class. + """ + return model_json_schema( + cls, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator, mode=mode + ) + + @classmethod + def model_parametrized_name(cls, params: tuple[type[Any], ...]) -> str: + """Compute the class name for parametrizations of generic classes. + + This method can be overridden to achieve a custom naming scheme for generic BaseModels. + + Args: + params: Tuple of types of the class. Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + + Returns: + String representing the new class where `params` are passed to `cls` as type variables. + + Raises: + TypeError: Raised when trying to generate concrete names for non-generic models. + """ + if not issubclass(cls, typing.Generic): + raise TypeError('Concrete names should only be generated for generic models.') + + # Any strings received should represent forward references, so we handle them specially below. + # If we eventually move toward wrapping them in a ForwardRef in __class_getitem__ in the future, + # we may be able to remove this special case. + param_names = [param if isinstance(param, str) else _repr.display_as_type(param) for param in params] + params_component = ', '.join(param_names) + return f'{cls.__name__}[{params_component}]' + + def model_post_init(self, __context: Any) -> None: + """Override this method to perform additional initialization after `__init__` and `model_construct`. + This is useful if you want to do some validation that requires the entire model to be initialized. + """ + pass + + @classmethod + def model_rebuild( + cls, + *, + force: bool = False, + raise_errors: bool = True, + _parent_namespace_depth: int = 2, + _types_namespace: dict[str, Any] | None = None, + ) -> bool | None: + """Try to rebuild the pydantic-core schema for the model. + + This may be necessary when one of the annotations is a ForwardRef which could not be resolved during + the initial attempt to build the schema, and automatic rebuilding fails. + + Args: + force: Whether to force the rebuilding of the model schema, defaults to `False`. + raise_errors: Whether to raise errors, defaults to `True`. + _parent_namespace_depth: The depth level of the parent namespace, defaults to 2. + _types_namespace: The types namespace, defaults to `None`. + + Returns: + Returns `None` if the schema is already "complete" and rebuilding was not required. + If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`. + """ + if not force and cls.__pydantic_complete__: + return None + else: + if '__pydantic_core_schema__' in cls.__dict__: + delattr(cls, '__pydantic_core_schema__') # delete cached value to ensure full rebuild happens + if _types_namespace is not None: + types_namespace: dict[str, Any] | None = _types_namespace.copy() + else: + if _parent_namespace_depth > 0: + frame_parent_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth) or {} + cls_parent_ns = ( + _model_construction.unpack_lenient_weakvaluedict(cls.__pydantic_parent_namespace__) or {} + ) + types_namespace = {**cls_parent_ns, **frame_parent_ns} + cls.__pydantic_parent_namespace__ = _model_construction.build_lenient_weakvaluedict(types_namespace) + else: + types_namespace = _model_construction.unpack_lenient_weakvaluedict( + cls.__pydantic_parent_namespace__ + ) + + types_namespace = _typing_extra.get_cls_types_namespace(cls, types_namespace) + + # manually override defer_build so complete_model_class doesn't skip building the model again + config = {**cls.model_config, 'defer_build': False} + return _model_construction.complete_model_class( + cls, + cls.__name__, + _config.ConfigWrapper(config, check=False), + raise_errors=raise_errors, + types_namespace=types_namespace, + ) + + @classmethod + def model_validate( + cls: type[Model], + obj: Any, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + ) -> Model: + """Validate a pydantic model instance. + + Args: + obj: The object to validate. + strict: Whether to enforce types strictly. + from_attributes: Whether to extract data from object attributes. + context: Additional context to pass to the validator. + + Raises: + ValidationError: If the object could not be validated. + + Returns: + The validated model instance. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + return cls.__pydantic_validator__.validate_python( + obj, strict=strict, from_attributes=from_attributes, context=context + ) + + @classmethod + def model_validate_json( + cls: type[Model], + json_data: str | bytes | bytearray, + *, + strict: bool | None = None, + context: dict[str, Any] | None = None, + ) -> Model: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json/#json-parsing + + Validate the given JSON data against the Pydantic model. + + Args: + json_data: The JSON data to validate. + strict: Whether to enforce types strictly. + context: Extra variables to pass to the validator. + + Returns: + The validated Pydantic model. + + Raises: + ValueError: If `json_data` is not a JSON string. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + return cls.__pydantic_validator__.validate_json(json_data, strict=strict, context=context) + + @classmethod + def model_validate_strings( + cls: type[Model], + obj: Any, + *, + strict: bool | None = None, + context: dict[str, Any] | None = None, + ) -> Model: + """Validate the given object contains string data against the Pydantic model. + + Args: + obj: The object contains string data to validate. + strict: Whether to enforce types strictly. + context: Extra variables to pass to the validator. + + Returns: + The validated Pydantic model. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + return cls.__pydantic_validator__.validate_strings(obj, strict=strict, context=context) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreSchemaHandler, /) -> CoreSchema: + """Hook into generating the model's CoreSchema. + + Args: + source: The class we are generating a schema for. + This will generally be the same as the `cls` argument if this is a classmethod. + handler: Call into Pydantic's internal JSON schema generation. + A callable that calls into Pydantic's internal CoreSchema generation logic. + + Returns: + A `pydantic-core` `CoreSchema`. + """ + # Only use the cached value from this _exact_ class; we don't want one from a parent class + # This is why we check `cls.__dict__` and don't use `cls.__pydantic_core_schema__` or similar. + if '__pydantic_core_schema__' in cls.__dict__: + # Due to the way generic classes are built, it's possible that an invalid schema may be temporarily + # set on generic classes. I think we could resolve this to ensure that we get proper schema caching + # for generics, but for simplicity for now, we just always rebuild if the class has a generic origin. + if not cls.__pydantic_generic_metadata__['origin']: + return cls.__pydantic_core_schema__ + + return handler(source) + + @classmethod + def __get_pydantic_json_schema__( + cls, + core_schema: CoreSchema, + handler: GetJsonSchemaHandler, + /, + ) -> JsonSchemaValue: + """Hook into generating the model's JSON schema. + + Args: + core_schema: A `pydantic-core` CoreSchema. + You can ignore this argument and call the handler with a new CoreSchema, + wrap this CoreSchema (`{'type': 'nullable', 'schema': current_schema}`), + or just call the handler with the original schema. + handler: Call into Pydantic's internal JSON schema generation. + This will raise a `pydantic.errors.PydanticInvalidForJsonSchema` if JSON schema + generation fails. + Since this gets called by `BaseModel.model_json_schema` you can override the + `schema_generator` argument to that function to change JSON schema generation globally + for a type. + + Returns: + A JSON schema, as a Python object. + """ + return handler(core_schema) + + @classmethod + def __pydantic_init_subclass__(cls, **kwargs: Any) -> None: + """This is intended to behave just like `__init_subclass__`, but is called by `ModelMetaclass` + only after the class is actually fully initialized. In particular, attributes like `model_fields` will + be present when this is called. + + This is necessary because `__init_subclass__` will always be called by `type.__new__`, + and it would require a prohibitively large refactor to the `ModelMetaclass` to ensure that + `type.__new__` was called in such a manner that the class would already be sufficiently initialized. + + This will receive the same `kwargs` that would be passed to the standard `__init_subclass__`, namely, + any kwargs passed to the class definition that aren't used internally by pydantic. + + Args: + **kwargs: Any keyword arguments passed to the class definition that aren't used internally + by pydantic. + """ + pass + + def __class_getitem__( + cls, typevar_values: type[Any] | tuple[type[Any], ...] + ) -> type[BaseModel] | _forward_ref.PydanticRecursiveRef: + cached = _generics.get_cached_generic_type_early(cls, typevar_values) + if cached is not None: + return cached + + if cls is BaseModel: + raise TypeError('Type parameters should be placed on typing.Generic, not BaseModel') + if not hasattr(cls, '__parameters__'): + raise TypeError(f'{cls} cannot be parametrized because it does not inherit from typing.Generic') + if not cls.__pydantic_generic_metadata__['parameters'] and typing.Generic not in cls.__bases__: + raise TypeError(f'{cls} is not a generic class') + + if not isinstance(typevar_values, tuple): + typevar_values = (typevar_values,) + _generics.check_parameters_count(cls, typevar_values) + + # Build map from generic typevars to passed params + typevars_map: dict[_typing_extra.TypeVarType, type[Any]] = dict( + zip(cls.__pydantic_generic_metadata__['parameters'], typevar_values) + ) + + if _utils.all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map: + submodel = cls # if arguments are equal to parameters it's the same object + _generics.set_cached_generic_type(cls, typevar_values, submodel) + else: + parent_args = cls.__pydantic_generic_metadata__['args'] + if not parent_args: + args = typevar_values + else: + args = tuple(_generics.replace_types(arg, typevars_map) for arg in parent_args) + + origin = cls.__pydantic_generic_metadata__['origin'] or cls + model_name = origin.model_parametrized_name(args) + params = tuple( + {param: None for param in _generics.iter_contained_typevars(typevars_map.values())} + ) # use dict as ordered set + + with _generics.generic_recursion_self_type(origin, args) as maybe_self_type: + if maybe_self_type is not None: + return maybe_self_type + + cached = _generics.get_cached_generic_type_late(cls, typevar_values, origin, args) + if cached is not None: + return cached + + # Attempt to rebuild the origin in case new types have been defined + try: + # depth 3 gets you above this __class_getitem__ call + origin.model_rebuild(_parent_namespace_depth=3) + except PydanticUndefinedAnnotation: + # It's okay if it fails, it just means there are still undefined types + # that could be evaluated later. + # TODO: Make sure validation fails if there are still undefined types, perhaps using MockValidator + pass + + submodel = _generics.create_generic_submodel(model_name, origin, args, params) + + # Update cache + _generics.set_cached_generic_type(cls, typevar_values, submodel, origin, args) + + return submodel + + def __copy__(self: Model) -> Model: + """Returns a shallow copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', copy(self.__dict__)) + _object_setattr(m, '__pydantic_extra__', copy(self.__pydantic_extra__)) + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + + if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None: + _object_setattr(m, '__pydantic_private__', None) + else: + _object_setattr( + m, + '__pydantic_private__', + {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, + ) + + return m + + def __deepcopy__(self: Model, memo: dict[int, Any] | None = None) -> Model: + """Returns a deep copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo)) + _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo)) + # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str], + # and attempting a deepcopy would be marginally slower. + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + + if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None: + _object_setattr(m, '__pydantic_private__', None) + else: + _object_setattr( + m, + '__pydantic_private__', + deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo), + ) + + return m + + if not typing.TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + # The same goes for __setattr__ and __delattr__, see: https://github.com/pydantic/pydantic/issues/8643 + + def __getattr__(self, item: str) -> Any: + private_attributes = object.__getattribute__(self, '__private_attributes__') + if item in private_attributes: + attribute = private_attributes[item] + if hasattr(attribute, '__get__'): + return attribute.__get__(self, type(self)) # type: ignore + + try: + # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items + return self.__pydantic_private__[item] # type: ignore + except KeyError as exc: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc + else: + # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized. + # See `BaseModel.__repr_args__` for more details + try: + pydantic_extra = object.__getattribute__(self, '__pydantic_extra__') + except AttributeError: + pydantic_extra = None + + if pydantic_extra: + try: + return pydantic_extra[item] + except KeyError as exc: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc + else: + if hasattr(self.__class__, item): + return super().__getattribute__(item) # Raises AttributeError if appropriate + else: + # this is the current error + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + def __setattr__(self, name: str, value: Any) -> None: + if name in self.__class_vars__: + raise AttributeError( + f'{name!r} is a ClassVar of `{self.__class__.__name__}` and cannot be set on an instance. ' + f'If you want to set a value on the class, use `{self.__class__.__name__}.{name} = value`.' + ) + elif not _fields.is_valid_field_name(name): + if self.__pydantic_private__ is None or name not in self.__private_attributes__: + _object_setattr(self, name, value) + else: + attribute = self.__private_attributes__[name] + if hasattr(attribute, '__set__'): + attribute.__set__(self, value) # type: ignore + else: + self.__pydantic_private__[name] = value + return + + self._check_frozen(name, value) + + attr = getattr(self.__class__, name, None) + if isinstance(attr, property): + attr.__set__(self, value) + elif self.model_config.get('validate_assignment', None): + self.__pydantic_validator__.validate_assignment(self, name, value) + elif self.model_config.get('extra') != 'allow' and name not in self.model_fields: + # TODO - matching error + raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"') + elif self.model_config.get('extra') == 'allow' and name not in self.model_fields: + if self.model_extra and name in self.model_extra: + self.__pydantic_extra__[name] = value # type: ignore + else: + try: + getattr(self, name) + except AttributeError: + # attribute does not already exist on instance, so put it in extra + self.__pydantic_extra__[name] = value # type: ignore + else: + # attribute _does_ already exist on instance, and was not in extra, so update it + _object_setattr(self, name, value) + else: + self.__dict__[name] = value + self.__pydantic_fields_set__.add(name) + + def __delattr__(self, item: str) -> Any: + if item in self.__private_attributes__: + attribute = self.__private_attributes__[item] + if hasattr(attribute, '__delete__'): + attribute.__delete__(self) # type: ignore + return + + try: + # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items + del self.__pydantic_private__[item] # type: ignore + return + except KeyError as exc: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc + + self._check_frozen(item, None) + + if item in self.model_fields: + object.__delattr__(self, item) + elif self.__pydantic_extra__ is not None and item in self.__pydantic_extra__: + del self.__pydantic_extra__[item] + else: + try: + object.__delattr__(self, item) + except AttributeError: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + def _check_frozen(self, name: str, value: Any) -> None: + if self.model_config.get('frozen', None): + typ = 'frozen_instance' + elif getattr(self.model_fields.get(name), 'frozen', False): + typ = 'frozen_field' + else: + return + error: pydantic_core.InitErrorDetails = { + 'type': typ, + 'loc': (name,), + 'input': value, + } + raise pydantic_core.ValidationError.from_exception_data(self.__class__.__name__, [error]) + + def __getstate__(self) -> dict[Any, Any]: + private = self.__pydantic_private__ + if private: + private = {k: v for k, v in private.items() if v is not PydanticUndefined} + return { + '__dict__': self.__dict__, + '__pydantic_extra__': self.__pydantic_extra__, + '__pydantic_fields_set__': self.__pydantic_fields_set__, + '__pydantic_private__': private, + } + + def __setstate__(self, state: dict[Any, Any]) -> None: + _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__']) + _object_setattr(self, '__pydantic_extra__', state['__pydantic_extra__']) + _object_setattr(self, '__pydantic_private__', state['__pydantic_private__']) + _object_setattr(self, '__dict__', state['__dict__']) + + if not typing.TYPE_CHECKING: + + def __eq__(self, other: Any) -> bool: + if isinstance(other, BaseModel): + # When comparing instances of generic types for equality, as long as all field values are equal, + # only require their generic origin types to be equal, rather than exact type equality. + # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1). + self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__ + other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__ + + # Perform common checks first + if not ( + self_type == other_type + and getattr(self, '__pydantic_private__', None) == getattr(other, '__pydantic_private__', None) + and self.__pydantic_extra__ == other.__pydantic_extra__ + ): + return False + + # We only want to compare pydantic fields but ignoring fields is costly. + # We'll perform a fast check first, and fallback only when needed + # See GH-7444 and GH-7825 for rationale and a performance benchmark + + # First, do the fast (and sometimes faulty) __dict__ comparison + if self.__dict__ == other.__dict__: + # If the check above passes, then pydantic fields are equal, we can return early + return True + + # We don't want to trigger unnecessary costly filtering of __dict__ on all unequal objects, so we return + # early if there are no keys to ignore (we would just return False later on anyway) + model_fields = type(self).model_fields.keys() + if self.__dict__.keys() <= model_fields and other.__dict__.keys() <= model_fields: + return False + + # If we reach here, there are non-pydantic-fields keys, mapped to unequal values, that we need to ignore + # Resort to costly filtering of the __dict__ objects + # We use operator.itemgetter because it is much faster than dict comprehensions + # NOTE: Contrary to standard python class and instances, when the Model class has a default value for an + # attribute and the model instance doesn't have a corresponding attribute, accessing the missing attribute + # raises an error in BaseModel.__getattr__ instead of returning the class attribute + # So we can use operator.itemgetter() instead of operator.attrgetter() + getter = operator.itemgetter(*model_fields) if model_fields else lambda _: _utils._SENTINEL + try: + return getter(self.__dict__) == getter(other.__dict__) + except KeyError: + # In rare cases (such as when using the deprecated BaseModel.copy() method), + # the __dict__ may not contain all model fields, which is how we can get here. + # getter(self.__dict__) is much faster than any 'safe' method that accounts + # for missing keys, and wrapping it in a `try` doesn't slow things down much + # in the common case. + self_fields_proxy = _utils.SafeGetItemProxy(self.__dict__) + other_fields_proxy = _utils.SafeGetItemProxy(other.__dict__) + return getter(self_fields_proxy) == getter(other_fields_proxy) + + # other instance is not a BaseModel + else: + return NotImplemented # delegate to the other item in the comparison + + if typing.TYPE_CHECKING: + # We put `__init_subclass__` in a TYPE_CHECKING block because, even though we want the type-checking benefits + # described in the signature of `__init_subclass__` below, we don't want to modify the default behavior of + # subclass initialization. + + def __init_subclass__(cls, **kwargs: Unpack[ConfigDict]): + """This signature is included purely to help type-checkers check arguments to class declaration, which + provides a way to conveniently set model_config key/value pairs. + + ```py + from pydantic import BaseModel + + class MyModel(BaseModel, extra='allow'): + ... + ``` + + However, this may be deceiving, since the _actual_ calls to `__init_subclass__` will not receive any + of the config arguments, and will only receive any keyword arguments passed during class initialization + that are _not_ expected keys in ConfigDict. (This is due to the way `ModelMetaclass.__new__` works.) + + Args: + **kwargs: Keyword arguments passed to the class definition, which set model_config + + Note: + You may want to override `__pydantic_init_subclass__` instead, which behaves similarly but is called + *after* the class is fully initialized. + """ + + def __iter__(self) -> TupleGenerator: + """So `dict(model)` works.""" + yield from [(k, v) for (k, v) in self.__dict__.items() if not k.startswith('_')] + extra = self.__pydantic_extra__ + if extra: + yield from extra.items() + + def __repr__(self) -> str: + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' + + def __repr_args__(self) -> _repr.ReprArgs: + for k, v in self.__dict__.items(): + field = self.model_fields.get(k) + if field and field.repr: + yield k, v + + # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized. + # This can happen if a `ValidationError` is raised during initialization and the instance's + # repr is generated as part of the exception handling. Therefore, we use `getattr` here + # with a fallback, even though the type hints indicate the attribute will always be present. + try: + pydantic_extra = object.__getattribute__(self, '__pydantic_extra__') + except AttributeError: + pydantic_extra = None + + if pydantic_extra is not None: + yield from ((k, v) for k, v in pydantic_extra.items()) + yield from ((k, getattr(self, k)) for k, v in self.model_computed_fields.items() if v.repr) + + # take logic from `_repr.Representation` without the side effects of inheritance, see #5740 + __repr_name__ = _repr.Representation.__repr_name__ + __repr_str__ = _repr.Representation.__repr_str__ + __pretty__ = _repr.Representation.__pretty__ + __rich_repr__ = _repr.Representation.__rich_repr__ + + def __str__(self) -> str: + return self.__repr_str__(' ') + + # ##### Deprecated methods from v1 ##### + @property + @typing_extensions.deprecated( + 'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=None + ) + def __fields__(self) -> dict[str, FieldInfo]: + warnings.warn( + 'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=PydanticDeprecatedSince20 + ) + return self.model_fields + + @property + @typing_extensions.deprecated( + 'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.', + category=None, + ) + def __fields_set__(self) -> set[str]: + warnings.warn( + 'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.', + category=PydanticDeprecatedSince20, + ) + return self.__pydantic_fields_set__ + + @typing_extensions.deprecated('The `dict` method is deprecated; use `model_dump` instead.', category=None) + def dict( # noqa: D102 + self, + *, + include: IncEx = None, + exclude: IncEx = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> typing.Dict[str, Any]: # noqa UP006 + warnings.warn('The `dict` method is deprecated; use `model_dump` instead.', category=PydanticDeprecatedSince20) + return self.model_dump( + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + @typing_extensions.deprecated('The `json` method is deprecated; use `model_dump_json` instead.', category=None) + def json( # noqa: D102 + self, + *, + include: IncEx = None, + exclude: IncEx = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + encoder: typing.Callable[[Any], Any] | None = PydanticUndefined, # type: ignore[assignment] + models_as_dict: bool = PydanticUndefined, # type: ignore[assignment] + **dumps_kwargs: Any, + ) -> str: + warnings.warn( + 'The `json` method is deprecated; use `model_dump_json` instead.', category=PydanticDeprecatedSince20 + ) + if encoder is not PydanticUndefined: + raise TypeError('The `encoder` argument is no longer supported; use field serializers instead.') + if models_as_dict is not PydanticUndefined: + raise TypeError('The `models_as_dict` argument is no longer supported; use a model serializer instead.') + if dumps_kwargs: + raise TypeError('`dumps_kwargs` keyword arguments are no longer supported.') + return self.model_dump_json( + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + @classmethod + @typing_extensions.deprecated('The `parse_obj` method is deprecated; use `model_validate` instead.', category=None) + def parse_obj(cls: type[Model], obj: Any) -> Model: # noqa: D102 + warnings.warn( + 'The `parse_obj` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20 + ) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, ' + 'otherwise load the data then use `model_validate` instead.', + category=None, + ) + def parse_raw( # noqa: D102 + cls: type[Model], + b: str | bytes, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: DeprecatedParseProtocol | None = None, + allow_pickle: bool = False, + ) -> Model: # pragma: no cover + warnings.warn( + 'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, ' + 'otherwise load the data then use `model_validate` instead.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import parse + + try: + obj = parse.load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + ) + except (ValueError, TypeError) as exc: + import json + + # try to match V1 + if isinstance(exc, UnicodeDecodeError): + type_str = 'value_error.unicodedecode' + elif isinstance(exc, json.JSONDecodeError): + type_str = 'value_error.jsondecode' + elif isinstance(exc, ValueError): + type_str = 'value_error' + else: + type_str = 'type_error' + + # ctx is missing here, but since we've added `input` to the error, we're not pretending it's the same + error: pydantic_core.InitErrorDetails = { + # The type: ignore on the next line is to ignore the requirement of LiteralString + 'type': pydantic_core.PydanticCustomError(type_str, str(exc)), # type: ignore + 'loc': ('__root__',), + 'input': b, + } + raise pydantic_core.ValidationError.from_exception_data(cls.__name__, [error]) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `parse_file` method is deprecated; load the data from file, then if your data is JSON ' + 'use `model_validate_json`, otherwise `model_validate` instead.', + category=None, + ) + def parse_file( # noqa: D102 + cls: type[Model], + path: str | Path, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: DeprecatedParseProtocol | None = None, + allow_pickle: bool = False, + ) -> Model: + warnings.warn( + 'The `parse_file` method is deprecated; load the data from file, then if your data is JSON ' + 'use `model_validate_json`, otherwise `model_validate` instead.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import parse + + obj = parse.load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + ) + return cls.parse_obj(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `from_orm` method is deprecated; set ' + "`model_config['from_attributes']=True` and use `model_validate` instead.", + category=None, + ) + def from_orm(cls: type[Model], obj: Any) -> Model: # noqa: D102 + warnings.warn( + 'The `from_orm` method is deprecated; set ' + "`model_config['from_attributes']=True` and use `model_validate` instead.", + category=PydanticDeprecatedSince20, + ) + if not cls.model_config.get('from_attributes', None): + raise PydanticUserError( + 'You must set the config attribute `from_attributes=True` to use from_orm', code=None + ) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated('The `construct` method is deprecated; use `model_construct` instead.', category=None) + def construct(cls: type[Model], _fields_set: set[str] | None = None, **values: Any) -> Model: # noqa: D102 + warnings.warn( + 'The `construct` method is deprecated; use `model_construct` instead.', category=PydanticDeprecatedSince20 + ) + return cls.model_construct(_fields_set=_fields_set, **values) + + @typing_extensions.deprecated( + 'The `copy` method is deprecated; use `model_copy` instead. ' + 'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.', + category=None, + ) + def copy( + self: Model, + *, + include: AbstractSetIntStr | MappingIntStrAny | None = None, + exclude: AbstractSetIntStr | MappingIntStrAny | None = None, + update: typing.Dict[str, Any] | None = None, # noqa UP006 + deep: bool = False, + ) -> Model: # pragma: no cover + """Returns a copy of the model. + + !!! warning "Deprecated" + This method is now deprecated; use `model_copy` instead. + + If you need `include` or `exclude`, use: + + ```py + data = self.model_dump(include=include, exclude=exclude, round_trip=True) + data = {**data, **(update or {})} + copied = self.model_validate(data) + ``` + + Args: + include: Optional set or mapping specifying which fields to include in the copied model. + exclude: Optional set or mapping specifying which fields to exclude in the copied model. + update: Optional dictionary of field-value pairs to override field values in the copied model. + deep: If True, the values of fields that are Pydantic models will be deep-copied. + + Returns: + A copy of the model with included, excluded and updated fields as specified. + """ + warnings.warn( + 'The `copy` method is deprecated; use `model_copy` instead. ' + 'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import copy_internals + + values = dict( + copy_internals._iter( + self, to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False + ), + **(update or {}), + ) + if self.__pydantic_private__ is None: + private = None + else: + private = {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined} + + if self.__pydantic_extra__ is None: + extra: dict[str, Any] | None = None + else: + extra = self.__pydantic_extra__.copy() + for k in list(self.__pydantic_extra__): + if k not in values: # k was in the exclude + extra.pop(k) + for k in list(values): + if k in self.__pydantic_extra__: # k must have come from extra + extra[k] = values.pop(k) + + # new `__pydantic_fields_set__` can have unset optional fields with a set value in `update` kwarg + if update: + fields_set = self.__pydantic_fields_set__ | update.keys() + else: + fields_set = set(self.__pydantic_fields_set__) + + # removing excluded fields from `__pydantic_fields_set__` + if exclude: + fields_set -= set(exclude) + + return copy_internals._copy_and_set_values(self, values, fields_set, extra, private, deep=deep) + + @classmethod + @typing_extensions.deprecated('The `schema` method is deprecated; use `model_json_schema` instead.', category=None) + def schema( # noqa: D102 + cls, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE + ) -> typing.Dict[str, Any]: # noqa UP006 + warnings.warn( + 'The `schema` method is deprecated; use `model_json_schema` instead.', category=PydanticDeprecatedSince20 + ) + return cls.model_json_schema(by_alias=by_alias, ref_template=ref_template) + + @classmethod + @typing_extensions.deprecated( + 'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.', + category=None, + ) + def schema_json( # noqa: D102 + cls, *, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE, **dumps_kwargs: Any + ) -> str: # pragma: no cover + warnings.warn( + 'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.', + category=PydanticDeprecatedSince20, + ) + import json + + from .deprecated.json import pydantic_encoder + + return json.dumps( + cls.model_json_schema(by_alias=by_alias, ref_template=ref_template), + default=pydantic_encoder, + **dumps_kwargs, + ) + + @classmethod + @typing_extensions.deprecated('The `validate` method is deprecated; use `model_validate` instead.', category=None) + def validate(cls: type[Model], value: Any) -> Model: # noqa: D102 + warnings.warn( + 'The `validate` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20 + ) + return cls.model_validate(value) + + @classmethod + @typing_extensions.deprecated( + 'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.', + category=None, + ) + def update_forward_refs(cls, **localns: Any) -> None: # noqa: D102 + warnings.warn( + 'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.', + category=PydanticDeprecatedSince20, + ) + if localns: # pragma: no cover + raise TypeError('`localns` arguments are not longer accepted.') + cls.model_rebuild(force=True) + + @typing_extensions.deprecated( + 'The private method `_iter` will be removed and should no longer be used.', category=None + ) + def _iter(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_iter` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import copy_internals + + return copy_internals._iter(self, *args, **kwargs) + + @typing_extensions.deprecated( + 'The private method `_copy_and_set_values` will be removed and should no longer be used.', + category=None, + ) + def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_copy_and_set_values` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import copy_internals + + return copy_internals._copy_and_set_values(self, *args, **kwargs) + + @classmethod + @typing_extensions.deprecated( + 'The private method `_get_value` will be removed and should no longer be used.', + category=None, + ) + def _get_value(cls, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_get_value` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import copy_internals + + return copy_internals._get_value(cls, *args, **kwargs) + + @typing_extensions.deprecated( + 'The private method `_calculate_keys` will be removed and should no longer be used.', + category=None, + ) + def _calculate_keys(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_calculate_keys` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + ) + from .deprecated import copy_internals + + return copy_internals._calculate_keys(self, *args, **kwargs) + + +@typing.overload +def create_model( + __model_name: str, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: None = None, + __module__: str = __name__, + __validators__: dict[str, classmethod] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + **field_definitions: Any, +) -> type[BaseModel]: + ... + + +@typing.overload +def create_model( + __model_name: str, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: type[Model] | tuple[type[Model], ...], + __module__: str = __name__, + __validators__: dict[str, classmethod] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + **field_definitions: Any, +) -> type[Model]: + ... + + +def create_model( # noqa: C901 + __model_name: str, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: type[Model] | tuple[type[Model], ...] | None = None, + __module__: str | None = None, + __validators__: dict[str, classmethod] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + __slots__: tuple[str, ...] | None = None, + **field_definitions: Any, +) -> type[Model]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/models/#dynamic-model-creation + + Dynamically creates and returns a new Pydantic model, in other words, `create_model` dynamically creates a + subclass of [`BaseModel`][pydantic.BaseModel]. + + Args: + __model_name: The name of the newly created model. + __config__: The configuration of the new model. + __doc__: The docstring of the new model. + __base__: The base class or classes for the new model. + __module__: The name of the module that the model belongs to; + if `None`, the value is taken from `sys._getframe(1)` + __validators__: A dictionary of methods that validate fields. + __cls_kwargs__: A dictionary of keyword arguments for class creation, such as `metaclass`. + __slots__: Deprecated. Should not be passed to `create_model`. + **field_definitions: Attributes of the new model. They should be passed in the format: + `=(, )`, `=(, )`, or `typing.Annotated[, ]`. + Any additional metadata in `typing.Annotated[, , ...]` will be ignored. + + Returns: + The new [model][pydantic.BaseModel]. + + Raises: + PydanticUserError: If `__base__` and `__config__` are both passed. + """ + if __slots__ is not None: + # __slots__ will be ignored from here on + warnings.warn('__slots__ should not be passed to create_model', RuntimeWarning) + + if __base__ is not None: + if __config__ is not None: + raise PydanticUserError( + 'to avoid confusion `__config__` and `__base__` cannot be used together', + code='create-model-config-base', + ) + if not isinstance(__base__, tuple): + __base__ = (__base__,) + else: + __base__ = (typing.cast(typing.Type['Model'], BaseModel),) + + __cls_kwargs__ = __cls_kwargs__ or {} + + fields = {} + annotations = {} + + for f_name, f_def in field_definitions.items(): + if not _fields.is_valid_field_name(f_name): + warnings.warn(f'fields may not start with an underscore, ignoring "{f_name}"', RuntimeWarning) + if isinstance(f_def, tuple): + f_def = typing.cast('tuple[str, Any]', f_def) + try: + f_annotation, f_value = f_def + except ValueError as e: + raise PydanticUserError( + 'Field definitions should be a `(, )`.', + code='create-model-field-definitions', + ) from e + + elif _typing_extra.is_annotated(f_def): + (f_annotation, f_value, *_) = typing_extensions.get_args( + f_def + ) # first two input are expected from Annotated, refer to https://docs.python.org/3/library/typing.html#typing.Annotated + from .fields import FieldInfo + + if not isinstance(f_value, FieldInfo): + raise PydanticUserError( + 'Field definitions should be a Annotated[, ]', + code='create-model-field-definitions', + ) + + else: + f_annotation, f_value = None, f_def + + if f_annotation: + annotations[f_name] = f_annotation + fields[f_name] = f_value + + if __module__ is None: + f = sys._getframe(1) + __module__ = f.f_globals['__name__'] + + namespace: dict[str, Any] = {'__annotations__': annotations, '__module__': __module__} + if __doc__: + namespace.update({'__doc__': __doc__}) + if __validators__: + namespace.update(__validators__) + namespace.update(fields) + if __config__: + namespace['model_config'] = _config.ConfigWrapper(__config__).config_dict + resolved_bases = types.resolve_bases(__base__) + meta, ns, kwds = types.prepare_class(__model_name, resolved_bases, kwds=__cls_kwargs__) + if resolved_bases is not __base__: + ns['__orig_bases__'] = __base__ + namespace.update(ns) + + return meta( + __model_name, + resolved_bases, + namespace, + __pydantic_reset_parent_namespace__=False, + _create_model_module=__module__, + **kwds, + ) + + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/mypy.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/mypy.py new file mode 100644 index 0000000..1a71bfe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/mypy.py @@ -0,0 +1,1284 @@ +"""This module includes classes and functions designed specifically for use with the mypy plugin.""" + +from __future__ import annotations + +import sys +from configparser import ConfigParser +from typing import Any, Callable, Iterator + +from mypy.errorcodes import ErrorCode +from mypy.expandtype import expand_type, expand_type_by_instance +from mypy.nodes import ( + ARG_NAMED, + ARG_NAMED_OPT, + ARG_OPT, + ARG_POS, + ARG_STAR2, + MDEF, + Argument, + AssignmentStmt, + Block, + CallExpr, + ClassDef, + Context, + Decorator, + DictExpr, + EllipsisExpr, + Expression, + FuncDef, + IfStmt, + JsonDict, + MemberExpr, + NameExpr, + PassStmt, + PlaceholderNode, + RefExpr, + Statement, + StrExpr, + SymbolTableNode, + TempNode, + TypeAlias, + TypeInfo, + Var, +) +from mypy.options import Options +from mypy.plugin import ( + CheckerPluginInterface, + ClassDefContext, + FunctionContext, + MethodContext, + Plugin, + ReportConfigContext, + SemanticAnalyzerPluginInterface, +) +from mypy.plugins import dataclasses +from mypy.plugins.common import ( + deserialize_and_fixup_type, +) +from mypy.semanal import set_callable_name +from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state +from mypy.typeops import map_type_from_supertype +from mypy.types import ( + AnyType, + CallableType, + Instance, + NoneType, + Overloaded, + Type, + TypeOfAny, + TypeType, + TypeVarType, + UnionType, + get_proper_type, +) +from mypy.typevars import fill_typevars +from mypy.util import get_unique_redefinition_name +from mypy.version import __version__ as mypy_version + +from pydantic._internal import _fields +from pydantic.version import parse_mypy_version + +try: + from mypy.types import TypeVarDef # type: ignore[attr-defined] +except ImportError: # pragma: no cover + # Backward-compatible with TypeVarDef from Mypy 0.930. + from mypy.types import TypeVarType as TypeVarDef + +CONFIGFILE_KEY = 'pydantic-mypy' +METADATA_KEY = 'pydantic-mypy-metadata' +BASEMODEL_FULLNAME = 'pydantic.main.BaseModel' +BASESETTINGS_FULLNAME = 'pydantic_settings.main.BaseSettings' +ROOT_MODEL_FULLNAME = 'pydantic.root_model.RootModel' +MODEL_METACLASS_FULLNAME = 'pydantic._internal._model_construction.ModelMetaclass' +FIELD_FULLNAME = 'pydantic.fields.Field' +DATACLASS_FULLNAME = 'pydantic.dataclasses.dataclass' +MODEL_VALIDATOR_FULLNAME = 'pydantic.functional_validators.model_validator' +DECORATOR_FULLNAMES = { + 'pydantic.functional_validators.field_validator', + 'pydantic.functional_validators.model_validator', + 'pydantic.functional_serializers.serializer', + 'pydantic.functional_serializers.model_serializer', + 'pydantic.deprecated.class_validators.validator', + 'pydantic.deprecated.class_validators.root_validator', +} + + +MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version) +BUILTINS_NAME = 'builtins' if MYPY_VERSION_TUPLE >= (0, 930) else '__builtins__' + +# Increment version if plugin changes and mypy caches should be invalidated +__version__ = 2 + + +def plugin(version: str) -> type[Plugin]: + """`version` is the mypy version string. + + We might want to use this to print a warning if the mypy version being used is + newer, or especially older, than we expect (or need). + + Args: + version: The mypy version string. + + Return: + The Pydantic mypy plugin type. + """ + return PydanticPlugin + + +class PydanticPlugin(Plugin): + """The Pydantic mypy plugin.""" + + def __init__(self, options: Options) -> None: + self.plugin_config = PydanticPluginConfig(options) + self._plugin_data = self.plugin_config.to_data() + super().__init__(options) + + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], bool] | None: + """Update Pydantic model class.""" + sym = self.lookup_fully_qualified(fullname) + if sym and isinstance(sym.node, TypeInfo): # pragma: no branch + # No branching may occur if the mypy cache has not been cleared + if any(base.fullname == BASEMODEL_FULLNAME for base in sym.node.mro): + return self._pydantic_model_class_maker_callback + return None + + def get_metaclass_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: + """Update Pydantic `ModelMetaclass` definition.""" + if fullname == MODEL_METACLASS_FULLNAME: + return self._pydantic_model_metaclass_marker_callback + return None + + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + """Adjust the return type of the `Field` function.""" + sym = self.lookup_fully_qualified(fullname) + if sym and sym.fullname == FIELD_FULLNAME: + return self._pydantic_field_callback + return None + + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + """Adjust return type of `from_orm` method call.""" + if fullname.endswith('.from_orm'): + return from_attributes_callback + return None + + def get_class_decorator_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: + """Mark pydantic.dataclasses as dataclass. + + Mypy version 1.1.1 added support for `@dataclass_transform` decorator. + """ + if fullname == DATACLASS_FULLNAME and MYPY_VERSION_TUPLE < (1, 1): + return dataclasses.dataclass_class_maker_callback # type: ignore[return-value] + return None + + def report_config_data(self, ctx: ReportConfigContext) -> dict[str, Any]: + """Return all plugin config data. + + Used by mypy to determine if cache needs to be discarded. + """ + return self._plugin_data + + def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> bool: + transformer = PydanticModelTransformer(ctx.cls, ctx.reason, ctx.api, self.plugin_config) + return transformer.transform() + + def _pydantic_model_metaclass_marker_callback(self, ctx: ClassDefContext) -> None: + """Reset dataclass_transform_spec attribute of ModelMetaclass. + + Let the plugin handle it. This behavior can be disabled + if 'debug_dataclass_transform' is set to True', for testing purposes. + """ + if self.plugin_config.debug_dataclass_transform: + return + info_metaclass = ctx.cls.info.declared_metaclass + assert info_metaclass, "callback not passed from 'get_metaclass_hook'" + if getattr(info_metaclass.type, 'dataclass_transform_spec', None): + info_metaclass.type.dataclass_transform_spec = None + + def _pydantic_field_callback(self, ctx: FunctionContext) -> Type: + """Extract the type of the `default` argument from the Field function, and use it as the return type. + + In particular: + * Check whether the default and default_factory argument is specified. + * Output an error if both are specified. + * Retrieve the type of the argument which is specified, and use it as return type for the function. + """ + default_any_type = ctx.default_return_type + + assert ctx.callee_arg_names[0] == 'default', '"default" is no longer first argument in Field()' + assert ctx.callee_arg_names[1] == 'default_factory', '"default_factory" is no longer second argument in Field()' + default_args = ctx.args[0] + default_factory_args = ctx.args[1] + + if default_args and default_factory_args: + error_default_and_default_factory_specified(ctx.api, ctx.context) + return default_any_type + + if default_args: + default_type = ctx.arg_types[0][0] + default_arg = default_args[0] + + # Fallback to default Any type if the field is required + if not isinstance(default_arg, EllipsisExpr): + return default_type + + elif default_factory_args: + default_factory_type = ctx.arg_types[1][0] + + # Functions which use `ParamSpec` can be overloaded, exposing the callable's types as a parameter + # Pydantic calls the default factory without any argument, so we retrieve the first item + if isinstance(default_factory_type, Overloaded): + default_factory_type = default_factory_type.items[0] + + if isinstance(default_factory_type, CallableType): + ret_type = default_factory_type.ret_type + # mypy doesn't think `ret_type` has `args`, you'd think mypy should know, + # add this check in case it varies by version + args = getattr(ret_type, 'args', None) + if args: + if all(isinstance(arg, TypeVarType) for arg in args): + # Looks like the default factory is a type like `list` or `dict`, replace all args with `Any` + ret_type.args = tuple(default_any_type for _ in args) # type: ignore[attr-defined] + return ret_type + + return default_any_type + + +class PydanticPluginConfig: + """A Pydantic mypy plugin config holder. + + Attributes: + init_forbid_extra: Whether to add a `**kwargs` at the end of the generated `__init__` signature. + init_typed: Whether to annotate fields in the generated `__init__`. + warn_required_dynamic_aliases: Whether to raise required dynamic aliases error. + debug_dataclass_transform: Whether to not reset `dataclass_transform_spec` attribute + of `ModelMetaclass` for testing purposes. + """ + + __slots__ = ( + 'init_forbid_extra', + 'init_typed', + 'warn_required_dynamic_aliases', + 'debug_dataclass_transform', + ) + init_forbid_extra: bool + init_typed: bool + warn_required_dynamic_aliases: bool + debug_dataclass_transform: bool # undocumented + + def __init__(self, options: Options) -> None: + if options.config_file is None: # pragma: no cover + return + + toml_config = parse_toml(options.config_file) + if toml_config is not None: + config = toml_config.get('tool', {}).get('pydantic-mypy', {}) + for key in self.__slots__: + setting = config.get(key, False) + if not isinstance(setting, bool): + raise ValueError(f'Configuration value must be a boolean for key: {key}') + setattr(self, key, setting) + else: + plugin_config = ConfigParser() + plugin_config.read(options.config_file) + for key in self.__slots__: + setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False) + setattr(self, key, setting) + + def to_data(self) -> dict[str, Any]: + """Returns a dict of config names to their values.""" + return {key: getattr(self, key) for key in self.__slots__} + + +def from_attributes_callback(ctx: MethodContext) -> Type: + """Raise an error if from_attributes is not enabled.""" + model_type: Instance + ctx_type = ctx.type + if isinstance(ctx_type, TypeType): + ctx_type = ctx_type.item + if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance): + model_type = ctx_type.ret_type # called on the class + elif isinstance(ctx_type, Instance): + model_type = ctx_type # called on an instance (unusual, but still valid) + else: # pragma: no cover + detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})' + error_unexpected_behavior(detail, ctx.api, ctx.context) + return ctx.default_return_type + pydantic_metadata = model_type.type.metadata.get(METADATA_KEY) + if pydantic_metadata is None: + return ctx.default_return_type + from_attributes = pydantic_metadata.get('config', {}).get('from_attributes') + if from_attributes is not True: + error_from_attributes(model_type.type.name, ctx.api, ctx.context) + return ctx.default_return_type + + +class PydanticModelField: + """Based on mypy.plugins.dataclasses.DataclassAttribute.""" + + def __init__( + self, + name: str, + alias: str | None, + has_dynamic_alias: bool, + has_default: bool, + line: int, + column: int, + type: Type | None, + info: TypeInfo, + ): + self.name = name + self.alias = alias + self.has_dynamic_alias = has_dynamic_alias + self.has_default = has_default + self.line = line + self.column = column + self.type = type + self.info = info + + def to_argument( + self, + current_info: TypeInfo, + typed: bool, + force_optional: bool, + use_alias: bool, + api: SemanticAnalyzerPluginInterface, + ) -> Argument: + """Based on mypy.plugins.dataclasses.DataclassAttribute.to_argument.""" + variable = self.to_var(current_info, api, use_alias) + type_annotation = self.expand_type(current_info, api) if typed else AnyType(TypeOfAny.explicit) + return Argument( + variable=variable, + type_annotation=type_annotation, + initializer=None, + kind=ARG_NAMED_OPT if force_optional or self.has_default else ARG_NAMED, + ) + + def expand_type(self, current_info: TypeInfo, api: SemanticAnalyzerPluginInterface) -> Type | None: + """Based on mypy.plugins.dataclasses.DataclassAttribute.expand_type.""" + # The getattr in the next line is used to prevent errors in legacy versions of mypy without this attribute + if self.type is not None and getattr(self.info, 'self_type', None) is not None: + # In general, it is not safe to call `expand_type()` during semantic analyzis, + # however this plugin is called very late, so all types should be fully ready. + # Also, it is tricky to avoid eager expansion of Self types here (e.g. because + # we serialize attributes). + with state.strict_optional_set(api.options.strict_optional): + return expand_type(self.type, {self.info.self_type.id: fill_typevars(current_info)}) + return self.type + + def to_var(self, current_info: TypeInfo, api: SemanticAnalyzerPluginInterface, use_alias: bool) -> Var: + """Based on mypy.plugins.dataclasses.DataclassAttribute.to_var.""" + if use_alias and self.alias is not None: + name = self.alias + else: + name = self.name + + return Var(name, self.expand_type(current_info, api)) + + def serialize(self) -> JsonDict: + """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize.""" + assert self.type + return { + 'name': self.name, + 'alias': self.alias, + 'has_dynamic_alias': self.has_dynamic_alias, + 'has_default': self.has_default, + 'line': self.line, + 'column': self.column, + 'type': self.type.serialize(), + } + + @classmethod + def deserialize(cls, info: TypeInfo, data: JsonDict, api: SemanticAnalyzerPluginInterface) -> PydanticModelField: + """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize.""" + data = data.copy() + typ = deserialize_and_fixup_type(data.pop('type'), api) + return cls(type=typ, info=info, **data) + + def expand_typevar_from_subtype(self, sub_type: TypeInfo, api: SemanticAnalyzerPluginInterface) -> None: + """Expands type vars in the context of a subtype when an attribute is inherited + from a generic super type. + """ + if self.type is not None: + with state.strict_optional_set(api.options.strict_optional): + self.type = map_type_from_supertype(self.type, sub_type, self.info) + + +class PydanticModelClassVar: + """Based on mypy.plugins.dataclasses.DataclassAttribute. + + ClassVars are ignored by subclasses. + + Attributes: + name: the ClassVar name + """ + + def __init__(self, name): + self.name = name + + @classmethod + def deserialize(cls, data: JsonDict) -> PydanticModelClassVar: + """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize.""" + data = data.copy() + return cls(**data) + + def serialize(self) -> JsonDict: + """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize.""" + return { + 'name': self.name, + } + + +class PydanticModelTransformer: + """Transform the BaseModel subclass according to the plugin settings. + + Attributes: + tracked_config_fields: A set of field configs that the plugin has to track their value. + """ + + tracked_config_fields: set[str] = { + 'extra', + 'frozen', + 'from_attributes', + 'populate_by_name', + 'alias_generator', + } + + def __init__( + self, + cls: ClassDef, + reason: Expression | Statement, + api: SemanticAnalyzerPluginInterface, + plugin_config: PydanticPluginConfig, + ) -> None: + self._cls = cls + self._reason = reason + self._api = api + + self.plugin_config = plugin_config + + def transform(self) -> bool: + """Configures the BaseModel subclass according to the plugin settings. + + In particular: + + * determines the model config and fields, + * adds a fields-aware signature for the initializer and construct methods + * freezes the class if frozen = True + * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses + """ + info = self._cls.info + is_root_model = any(ROOT_MODEL_FULLNAME in base.fullname for base in info.mro[:-1]) + config = self.collect_config() + fields, class_vars = self.collect_fields_and_class_vars(config, is_root_model) + if fields is None or class_vars is None: + # Some definitions are not ready. We need another pass. + return False + for field in fields: + if field.type is None: + return False + + is_settings = any(base.fullname == BASESETTINGS_FULLNAME for base in info.mro[:-1]) + self.add_initializer(fields, config, is_settings, is_root_model) + self.add_model_construct_method(fields, config, is_settings) + self.set_frozen(fields, self._api, frozen=config.frozen is True) + + self.adjust_decorator_signatures() + + info.metadata[METADATA_KEY] = { + 'fields': {field.name: field.serialize() for field in fields}, + 'class_vars': {class_var.name: class_var.serialize() for class_var in class_vars}, + 'config': config.get_values_dict(), + } + + return True + + def adjust_decorator_signatures(self) -> None: + """When we decorate a function `f` with `pydantic.validator(...)`, `pydantic.field_validator` + or `pydantic.serializer(...)`, mypy sees `f` as a regular method taking a `self` instance, + even though pydantic internally wraps `f` with `classmethod` if necessary. + + Teach mypy this by marking any function whose outermost decorator is a `validator()`, + `field_validator()` or `serializer()` call as a `classmethod`. + """ + for name, sym in self._cls.info.names.items(): + if isinstance(sym.node, Decorator): + first_dec = sym.node.original_decorators[0] + if ( + isinstance(first_dec, CallExpr) + and isinstance(first_dec.callee, NameExpr) + and first_dec.callee.fullname in DECORATOR_FULLNAMES + # @model_validator(mode="after") is an exception, it expects a regular method + and not ( + first_dec.callee.fullname == MODEL_VALIDATOR_FULLNAME + and any( + first_dec.arg_names[i] == 'mode' and isinstance(arg, StrExpr) and arg.value == 'after' + for i, arg in enumerate(first_dec.args) + ) + ) + ): + # TODO: Only do this if the first argument of the decorated function is `cls` + sym.node.func.is_class = True + + def collect_config(self) -> ModelConfigData: # noqa: C901 (ignore complexity) + """Collects the values of the config attributes that are used by the plugin, accounting for parent classes.""" + cls = self._cls + config = ModelConfigData() + + has_config_kwargs = False + has_config_from_namespace = False + + # Handle `class MyModel(BaseModel, =, ...):` + for name, expr in cls.keywords.items(): + config_data = self.get_config_update(name, expr) + if config_data: + has_config_kwargs = True + config.update(config_data) + + # Handle `model_config` + stmt: Statement | None = None + for stmt in cls.defs.body: + if not isinstance(stmt, (AssignmentStmt, ClassDef)): + continue + + if isinstance(stmt, AssignmentStmt): + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or lhs.name != 'model_config': + continue + + if isinstance(stmt.rvalue, CallExpr): # calls to `dict` or `ConfigDict` + for arg_name, arg in zip(stmt.rvalue.arg_names, stmt.rvalue.args): + if arg_name is None: + continue + config.update(self.get_config_update(arg_name, arg, lax_extra=True)) + elif isinstance(stmt.rvalue, DictExpr): # dict literals + for key_expr, value_expr in stmt.rvalue.items: + if not isinstance(key_expr, StrExpr): + continue + config.update(self.get_config_update(key_expr.value, value_expr)) + + elif isinstance(stmt, ClassDef): + if stmt.name != 'Config': # 'deprecated' Config-class + continue + for substmt in stmt.defs.body: + if not isinstance(substmt, AssignmentStmt): + continue + lhs = substmt.lvalues[0] + if not isinstance(lhs, NameExpr): + continue + config.update(self.get_config_update(lhs.name, substmt.rvalue)) + + if has_config_kwargs: + self._api.fail( + 'Specifying config in two places is ambiguous, use either Config attribute or class kwargs', + cls, + ) + break + + has_config_from_namespace = True + + if has_config_kwargs or has_config_from_namespace: + if ( + stmt + and config.has_alias_generator + and not config.populate_by_name + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(self._api, stmt) + + for info in cls.info.mro[1:]: # 0 is the current class + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of fields in its ancestors + self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname)) + for name, value in info.metadata[METADATA_KEY]['config'].items(): + config.setdefault(name, value) + return config + + def collect_fields_and_class_vars( + self, model_config: ModelConfigData, is_root_model: bool + ) -> tuple[list[PydanticModelField] | None, list[PydanticModelClassVar] | None]: + """Collects the fields for the model, accounting for parent classes.""" + cls = self._cls + + # First, collect fields and ClassVars belonging to any class in the MRO, ignoring duplicates. + # + # We iterate through the MRO in reverse because attrs defined in the parent must appear + # earlier in the attributes list than attrs defined in the child. See: + # https://docs.python.org/3/library/dataclasses.html#inheritance + # + # However, we also want fields defined in the subtype to override ones defined + # in the parent. We can implement this via a dict without disrupting the attr order + # because dicts preserve insertion order in Python 3.7+. + found_fields: dict[str, PydanticModelField] = {} + found_class_vars: dict[str, PydanticModelClassVar] = {} + for info in reversed(cls.info.mro[1:-1]): # 0 is the current class, -2 is BaseModel, -1 is object + # if BASEMODEL_METADATA_TAG_KEY in info.metadata and BASEMODEL_METADATA_KEY not in info.metadata: + # # We haven't processed the base class yet. Need another pass. + # return None, None + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of attributes in its dataclass ancestors. + self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname)) + + for name, data in info.metadata[METADATA_KEY]['fields'].items(): + field = PydanticModelField.deserialize(info, data, self._api) + # (The following comment comes directly from the dataclasses plugin) + # TODO: We shouldn't be performing type operations during the main + # semantic analysis pass, since some TypeInfo attributes might + # still be in flux. This should be performed in a later phase. + field.expand_typevar_from_subtype(cls.info, self._api) + found_fields[name] = field + + sym_node = cls.info.names.get(name) + if sym_node and sym_node.node and not isinstance(sym_node.node, Var): + self._api.fail( + 'BaseModel field may only be overridden by another field', + sym_node.node, + ) + # Collect ClassVars + for name, data in info.metadata[METADATA_KEY]['class_vars'].items(): + found_class_vars[name] = PydanticModelClassVar.deserialize(data) + + # Second, collect fields and ClassVars belonging to the current class. + current_field_names: set[str] = set() + current_class_vars_names: set[str] = set() + for stmt in self._get_assignment_statements_from_block(cls.defs): + maybe_field = self.collect_field_or_class_var_from_stmt(stmt, model_config, found_class_vars) + if isinstance(maybe_field, PydanticModelField): + lhs = stmt.lvalues[0] + if is_root_model and lhs.name != 'root': + error_extra_fields_on_root_model(self._api, stmt) + else: + current_field_names.add(lhs.name) + found_fields[lhs.name] = maybe_field + elif isinstance(maybe_field, PydanticModelClassVar): + lhs = stmt.lvalues[0] + current_class_vars_names.add(lhs.name) + found_class_vars[lhs.name] = maybe_field + + return list(found_fields.values()), list(found_class_vars.values()) + + def _get_assignment_statements_from_if_statement(self, stmt: IfStmt) -> Iterator[AssignmentStmt]: + for body in stmt.body: + if not body.is_unreachable: + yield from self._get_assignment_statements_from_block(body) + if stmt.else_body is not None and not stmt.else_body.is_unreachable: + yield from self._get_assignment_statements_from_block(stmt.else_body) + + def _get_assignment_statements_from_block(self, block: Block) -> Iterator[AssignmentStmt]: + for stmt in block.body: + if isinstance(stmt, AssignmentStmt): + yield stmt + elif isinstance(stmt, IfStmt): + yield from self._get_assignment_statements_from_if_statement(stmt) + + def collect_field_or_class_var_from_stmt( # noqa C901 + self, stmt: AssignmentStmt, model_config: ModelConfigData, class_vars: dict[str, PydanticModelClassVar] + ) -> PydanticModelField | PydanticModelClassVar | None: + """Get pydantic model field from statement. + + Args: + stmt: The statement. + model_config: Configuration settings for the model. + class_vars: ClassVars already known to be defined on the model. + + Returns: + A pydantic model field if it could find the field in statement. Otherwise, `None`. + """ + cls = self._cls + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config': + return None + + if not stmt.new_syntax: + if ( + isinstance(stmt.rvalue, CallExpr) + and isinstance(stmt.rvalue.callee, CallExpr) + and isinstance(stmt.rvalue.callee.callee, NameExpr) + and stmt.rvalue.callee.callee.fullname in DECORATOR_FULLNAMES + ): + # This is a (possibly-reused) validator or serializer, not a field + # In particular, it looks something like: my_validator = validator('my_field')(f) + # Eventually, we may want to attempt to respect model_config['ignored_types'] + return None + + if lhs.name in class_vars: + # Class vars are not fields and are not required to be annotated + return None + + # The assignment does not have an annotation, and it's not anything else we recognize + error_untyped_fields(self._api, stmt) + return None + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr): + return None + + if not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config': + return None + + sym = cls.info.names.get(lhs.name) + if sym is None: # pragma: no cover + # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation) + # This is the same logic used in the dataclasses plugin + return None + + node = sym.node + if isinstance(node, PlaceholderNode): # pragma: no cover + # See the PlaceholderNode docstring for more detail about how this can occur + # Basically, it is an edge case when dealing with complex import logic + + # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does.. + return None + + if isinstance(node, TypeAlias): + self._api.fail( + 'Type aliases inside BaseModel definitions are not supported at runtime', + node, + ) + # Skip processing this node. This doesn't match the runtime behaviour, + # but the only alternative would be to modify the SymbolTable, + # and it's a little hairy to do that in a plugin. + return None + + if not isinstance(node, Var): # pragma: no cover + # Don't know if this edge case still happens with the `is_valid_field` check above + # but better safe than sorry + + # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does.. + return None + + # x: ClassVar[int] is not a field + if node.is_classvar: + return PydanticModelClassVar(lhs.name) + + # x: InitVar[int] is not supported in BaseModel + node_type = get_proper_type(node.type) + if isinstance(node_type, Instance) and node_type.type.fullname == 'dataclasses.InitVar': + self._api.fail( + 'InitVar is not supported in BaseModel', + node, + ) + + has_default = self.get_has_default(stmt) + + if sym.type is None and node.is_final and node.is_inferred: + # This follows the logic from the dataclasses plugin. The following comment is taken verbatim: + # + # This is a special case, assignment like x: Final = 42 is classified + # annotated above, but mypy strips the `Final` turning it into x = 42. + # We do not support inferred types in dataclasses, so we can try inferring + # type for simple literals, and otherwise require an explicit type + # argument for Final[...]. + typ = self._api.analyze_simple_literal_type(stmt.rvalue, is_final=True) + if typ: + node.type = typ + else: + self._api.fail( + 'Need type argument for Final[...] with non-literal default in BaseModel', + stmt, + ) + node.type = AnyType(TypeOfAny.from_error) + + alias, has_dynamic_alias = self.get_alias_info(stmt) + if has_dynamic_alias and not model_config.populate_by_name and self.plugin_config.warn_required_dynamic_aliases: + error_required_dynamic_aliases(self._api, stmt) + + init_type = self._infer_dataclass_attr_init_type(sym, lhs.name, stmt) + return PydanticModelField( + name=lhs.name, + has_dynamic_alias=has_dynamic_alias, + has_default=has_default, + alias=alias, + line=stmt.line, + column=stmt.column, + type=init_type, + info=cls.info, + ) + + def _infer_dataclass_attr_init_type(self, sym: SymbolTableNode, name: str, context: Context) -> Type | None: + """Infer __init__ argument type for an attribute. + + In particular, possibly use the signature of __set__. + """ + default = sym.type + if sym.implicit: + return default + t = get_proper_type(sym.type) + + # Perform a simple-minded inference from the signature of __set__, if present. + # We can't use mypy.checkmember here, since this plugin runs before type checking. + # We only support some basic scanerios here, which is hopefully sufficient for + # the vast majority of use cases. + if not isinstance(t, Instance): + return default + setter = t.type.get('__set__') + if setter: + if isinstance(setter.node, FuncDef): + super_info = t.type.get_containing_type_info('__set__') + assert super_info + if setter.type: + setter_type = get_proper_type(map_type_from_supertype(setter.type, t.type, super_info)) + else: + return AnyType(TypeOfAny.unannotated) + if isinstance(setter_type, CallableType) and setter_type.arg_kinds == [ + ARG_POS, + ARG_POS, + ARG_POS, + ]: + return expand_type_by_instance(setter_type.arg_types[2], t) + else: + self._api.fail(f'Unsupported signature for "__set__" in "{t.type.name}"', context) + else: + self._api.fail(f'Unsupported "__set__" in "{t.type.name}"', context) + + return default + + def add_initializer( + self, fields: list[PydanticModelField], config: ModelConfigData, is_settings: bool, is_root_model: bool + ) -> None: + """Adds a fields-aware `__init__` method to the class. + + The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings. + """ + if '__init__' in self._cls.info.names and not self._cls.info.names['__init__'].plugin_generated: + return # Don't generate an __init__ if one already exists + + typed = self.plugin_config.init_typed + use_alias = config.populate_by_name is not True + requires_dynamic_aliases = bool(config.has_alias_generator and not config.populate_by_name) + args = self.get_field_arguments( + fields, + typed=typed, + requires_dynamic_aliases=requires_dynamic_aliases, + use_alias=use_alias, + is_settings=is_settings, + ) + + if is_root_model and MYPY_VERSION_TUPLE <= (1, 0, 1): + # convert root argument to positional argument + # This is needed because mypy support for `dataclass_transform` isn't complete on 1.0.1 + args[0].kind = ARG_POS if args[0].kind == ARG_NAMED else ARG_OPT + + if is_settings: + base_settings_node = self._api.lookup_fully_qualified(BASESETTINGS_FULLNAME).node + if '__init__' in base_settings_node.names: + base_settings_init_node = base_settings_node.names['__init__'].node + if base_settings_init_node is not None and base_settings_init_node.type is not None: + func_type = base_settings_init_node.type + for arg_idx, arg_name in enumerate(func_type.arg_names): + if arg_name.startswith('__') or not arg_name.startswith('_'): + continue + analyzed_variable_type = self._api.anal_type(func_type.arg_types[arg_idx]) + variable = Var(arg_name, analyzed_variable_type) + args.append(Argument(variable, analyzed_variable_type, None, ARG_OPT)) + + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + add_method(self._api, self._cls, '__init__', args=args, return_type=NoneType()) + + def add_model_construct_method( + self, fields: list[PydanticModelField], config: ModelConfigData, is_settings: bool + ) -> None: + """Adds a fully typed `model_construct` classmethod to the class. + + Similar to the fields-aware __init__ method, but always uses the field names (not aliases), + and does not treat settings fields as optional. + """ + set_str = self._api.named_type(f'{BUILTINS_NAME}.set', [self._api.named_type(f'{BUILTINS_NAME}.str')]) + optional_set_str = UnionType([set_str, NoneType()]) + fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT) + with state.strict_optional_set(self._api.options.strict_optional): + args = self.get_field_arguments( + fields, typed=True, requires_dynamic_aliases=False, use_alias=False, is_settings=is_settings + ) + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + args = [fields_set_argument] + args + + add_method( + self._api, + self._cls, + 'model_construct', + args=args, + return_type=fill_typevars(self._cls.info), + is_classmethod=True, + ) + + def set_frozen(self, fields: list[PydanticModelField], api: SemanticAnalyzerPluginInterface, frozen: bool) -> None: + """Marks all fields as properties so that attempts to set them trigger mypy errors. + + This is the same approach used by the attrs and dataclasses plugins. + """ + info = self._cls.info + for field in fields: + sym_node = info.names.get(field.name) + if sym_node is not None: + var = sym_node.node + if isinstance(var, Var): + var.is_property = frozen + elif isinstance(var, PlaceholderNode) and not self._api.final_iteration: + # See https://github.com/pydantic/pydantic/issues/5191 to hit this branch for test coverage + self._api.defer() + else: # pragma: no cover + # I don't know whether it's possible to hit this branch, but I've added it for safety + try: + var_str = str(var) + except TypeError: + # This happens for PlaceholderNode; perhaps it will happen for other types in the future.. + var_str = repr(var) + detail = f'sym_node.node: {var_str} (of type {var.__class__})' + error_unexpected_behavior(detail, self._api, self._cls) + else: + var = field.to_var(info, api, use_alias=False) + var.info = info + var.is_property = frozen + var._fullname = info.fullname + '.' + var.name + info.names[var.name] = SymbolTableNode(MDEF, var) + + def get_config_update(self, name: str, arg: Expression, lax_extra: bool = False) -> ModelConfigData | None: + """Determines the config update due to a single kwarg in the ConfigDict definition. + + Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int) + """ + if name not in self.tracked_config_fields: + return None + if name == 'extra': + if isinstance(arg, StrExpr): + forbid_extra = arg.value == 'forbid' + elif isinstance(arg, MemberExpr): + forbid_extra = arg.name == 'forbid' + else: + if not lax_extra: + # Only emit an error for other types of `arg` (e.g., `NameExpr`, `ConditionalExpr`, etc.) when + # reading from a config class, etc. If a ConfigDict is used, then we don't want to emit an error + # because you'll get type checking from the ConfigDict itself. + # + # It would be nice if we could introspect the types better otherwise, but I don't know what the API + # is to evaluate an expr into its type and then check if that type is compatible with the expected + # type. Note that you can still get proper type checking via: `model_config = ConfigDict(...)`, just + # if you don't use an explicit string, the plugin won't be able to infer whether extra is forbidden. + error_invalid_config_value(name, self._api, arg) + return None + return ModelConfigData(forbid_extra=forbid_extra) + if name == 'alias_generator': + has_alias_generator = True + if isinstance(arg, NameExpr) and arg.fullname == 'builtins.None': + has_alias_generator = False + return ModelConfigData(has_alias_generator=has_alias_generator) + if isinstance(arg, NameExpr) and arg.fullname in ('builtins.True', 'builtins.False'): + return ModelConfigData(**{name: arg.fullname == 'builtins.True'}) + error_invalid_config_value(name, self._api, arg) + return None + + @staticmethod + def get_has_default(stmt: AssignmentStmt) -> bool: + """Returns a boolean indicating whether the field defined in `stmt` is a required field.""" + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only, so has no default + return False + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME: + # The "default value" is a call to `Field`; at this point, the field has a default if and only if: + # * there is a positional argument that is not `...` + # * there is a keyword argument named "default" that is not `...` + # * there is a "default_factory" that is not `None` + for arg, name in zip(expr.args, expr.arg_names): + # If name is None, then this arg is the default because it is the only positional argument. + if name is None or name == 'default': + return arg.__class__ is not EllipsisExpr + if name == 'default_factory': + return not (isinstance(arg, NameExpr) and arg.fullname == 'builtins.None') + return False + # Has no default if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`) + return not isinstance(expr, EllipsisExpr) + + @staticmethod + def get_alias_info(stmt: AssignmentStmt) -> tuple[str | None, bool]: + """Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`. + + `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal. + If `has_dynamic_alias` is True, `alias` will be None. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only + return None, False + + if not ( + isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME + ): + # Assigned value is not a call to pydantic.fields.Field + return None, False + + for i, arg_name in enumerate(expr.arg_names): + if arg_name != 'alias': + continue + arg = expr.args[i] + if isinstance(arg, StrExpr): + return arg.value, False + else: + return None, True + return None, False + + def get_field_arguments( + self, + fields: list[PydanticModelField], + typed: bool, + use_alias: bool, + requires_dynamic_aliases: bool, + is_settings: bool, + ) -> list[Argument]: + """Helper function used during the construction of the `__init__` and `model_construct` method signatures. + + Returns a list of mypy Argument instances for use in the generated signatures. + """ + info = self._cls.info + arguments = [ + field.to_argument( + info, + typed=typed, + force_optional=requires_dynamic_aliases or is_settings, + use_alias=use_alias, + api=self._api, + ) + for field in fields + if not (use_alias and field.has_dynamic_alias) + ] + return arguments + + def should_init_forbid_extra(self, fields: list[PydanticModelField], config: ModelConfigData) -> bool: + """Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature. + + We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to, + *unless* a required dynamic alias is present (since then we can't determine a valid signature). + """ + if not config.populate_by_name: + if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)): + return False + if config.forbid_extra: + return True + return self.plugin_config.init_forbid_extra + + @staticmethod + def is_dynamic_alias_present(fields: list[PydanticModelField], has_alias_generator: bool) -> bool: + """Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be + determined during static analysis. + """ + for field in fields: + if field.has_dynamic_alias: + return True + if has_alias_generator: + for field in fields: + if field.alias is None: + return True + return False + + +class ModelConfigData: + """Pydantic mypy plugin model config class.""" + + def __init__( + self, + forbid_extra: bool | None = None, + frozen: bool | None = None, + from_attributes: bool | None = None, + populate_by_name: bool | None = None, + has_alias_generator: bool | None = None, + ): + self.forbid_extra = forbid_extra + self.frozen = frozen + self.from_attributes = from_attributes + self.populate_by_name = populate_by_name + self.has_alias_generator = has_alias_generator + + def get_values_dict(self) -> dict[str, Any]: + """Returns a dict of Pydantic model config names to their values. + + It includes the config if config value is not `None`. + """ + return {k: v for k, v in self.__dict__.items() if v is not None} + + def update(self, config: ModelConfigData | None) -> None: + """Update Pydantic model config values.""" + if config is None: + return + for k, v in config.get_values_dict().items(): + setattr(self, k, v) + + def setdefault(self, key: str, value: Any) -> None: + """Set default value for Pydantic model config if config value is `None`.""" + if getattr(self, key) is None: + setattr(self, key, value) + + +ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_attributes call', 'Pydantic') +ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic') +ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic') +ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic') +ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic') +ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic') +ERROR_EXTRA_FIELD_ROOT_MODEL = ErrorCode('pydantic-field', 'Extra field on RootModel subclass', 'Pydantic') + + +def error_from_attributes(model_name: str, api: CheckerPluginInterface, context: Context) -> None: + """Emits an error when the model does not have `from_attributes=True`.""" + api.fail(f'"{model_name}" does not have from_attributes=True', context, code=ERROR_ORM) + + +def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits an error when the config value is invalid.""" + api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG) + + +def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits required dynamic aliases error. + + This will be called when `warn_required_dynamic_aliases=True`. + """ + api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS) + + +def error_unexpected_behavior( + detail: str, api: CheckerPluginInterface | SemanticAnalyzerPluginInterface, context: Context +) -> None: # pragma: no cover + """Emits unexpected behavior error.""" + # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path + link = 'https://github.com/pydantic/pydantic/issues/new/choose' + full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n' + full_message += f'Please consider reporting this bug at {link} so we can try to fix it!' + api.fail(full_message, context, code=ERROR_UNEXPECTED) + + +def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits an error when there is an untyped field in the model.""" + api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED) + + +def error_extra_fields_on_root_model(api: CheckerPluginInterface, context: Context) -> None: + """Emits an error when there is more than just a root field defined for a subclass of RootModel.""" + api.fail('Only `root` is allowed as a field of a `RootModel`', context, code=ERROR_EXTRA_FIELD_ROOT_MODEL) + + +def error_default_and_default_factory_specified(api: CheckerPluginInterface, context: Context) -> None: + """Emits an error when `Field` has both `default` and `default_factory` together.""" + api.fail('Field default and default_factory cannot be specified together', context, code=ERROR_FIELD_DEFAULTS) + + +def add_method( + api: SemanticAnalyzerPluginInterface | CheckerPluginInterface, + cls: ClassDef, + name: str, + args: list[Argument], + return_type: Type, + self_type: Type | None = None, + tvar_def: TypeVarDef | None = None, + is_classmethod: bool = False, +) -> None: + """Very closely related to `mypy.plugins.common.add_method_to_class`, with a few pydantic-specific changes.""" + info = cls.info + + # First remove any previously generated methods with the same name + # to avoid clashes and problems in the semantic analyzer. + if name in info.names: + sym = info.names[name] + if sym.plugin_generated and isinstance(sym.node, FuncDef): + cls.defs.body.remove(sym.node) # pragma: no cover + + if isinstance(api, SemanticAnalyzerPluginInterface): + function_type = api.named_type('builtins.function') + else: + function_type = api.named_generic_type('builtins.function', []) + + if is_classmethod: + self_type = self_type or TypeType(fill_typevars(info)) + first = [Argument(Var('_cls'), self_type, None, ARG_POS, True)] + else: + self_type = self_type or fill_typevars(info) + # `self` is positional *ONLY* here, but this can't be expressed + # fully in the mypy internal API. ARG_POS is the closest we can get. + # Using ARG_POS will, however, give mypy errors if a `self` field + # is present on a model: + # + # Name "self" already defined (possibly by an import) [no-redef] + # + # As a workaround, we give this argument a name that will + # never conflict. By its positional nature, this name will not + # be used or exposed to users. + first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] + args = first + args + + arg_types, arg_names, arg_kinds = [], [], [] + for arg in args: + assert arg.type_annotation, 'All arguments must be fully typed.' + arg_types.append(arg.type_annotation) + arg_names.append(arg.variable.name) + arg_kinds.append(arg.kind) + + signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) + if tvar_def: + signature.variables = [tvar_def] + + func = FuncDef(name, args, Block([PassStmt()])) + func.info = info + func.type = set_callable_name(signature, func) + func.is_class = is_classmethod + func._fullname = info.fullname + '.' + name + func.line = info.line + + # NOTE: we would like the plugin generated node to dominate, but we still + # need to keep any existing definitions so they get semantically analyzed. + if name in info.names: + # Get a nice unique name instead. + r_name = get_unique_redefinition_name(name, info.names) + info.names[r_name] = info.names[name] + + # Add decorator for is_classmethod + # The dataclasses plugin claims this is unnecessary for classmethods, but not including it results in a + # signature incompatible with the superclass, which causes mypy errors to occur for every subclass of BaseModel. + if is_classmethod: + func.is_decorated = True + v = Var(name, func.type) + v.info = info + v._fullname = func._fullname + v.is_classmethod = True + dec = Decorator(func, [NameExpr('classmethod')], v) + dec.line = info.line + sym = SymbolTableNode(MDEF, dec) + else: + sym = SymbolTableNode(MDEF, func) + sym.plugin_generated = True + info.names[name] = sym + + info.defn.defs.body.append(func) + + +def parse_toml(config_file: str) -> dict[str, Any] | None: + """Returns a dict of config keys to values. + + It reads configs from toml file and returns `None` if the file is not a toml file. + """ + if not config_file.endswith('.toml'): + return None + + if sys.version_info >= (3, 11): + import tomllib as toml_ + else: + try: + import tomli as toml_ + except ImportError: # pragma: no cover + import warnings + + warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.') + return None + + with open(config_file, 'rb') as rf: + return toml_.load(rf) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/networks.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/networks.py new file mode 100644 index 0000000..d037a72 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/networks.py @@ -0,0 +1,754 @@ +"""The networks module contains types for common network-related fields.""" +from __future__ import annotations as _annotations + +import dataclasses as _dataclasses +import re +from importlib.metadata import version +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from typing import TYPE_CHECKING, Any + +from pydantic_core import MultiHostUrl, PydanticCustomError, Url, core_schema +from typing_extensions import Annotated, Self, TypeAlias + +from ._internal import _fields, _repr, _schema_generation_shared +from ._migration import getattr_migration +from .annotated_handlers import GetCoreSchemaHandler +from .json_schema import JsonSchemaValue + +if TYPE_CHECKING: + import email_validator + + NetworkType: TypeAlias = 'str | bytes | int | tuple[str | bytes | int, str | int]' + +else: + email_validator = None + + +__all__ = [ + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'FtpUrl', + 'HttpUrl', + 'WebsocketUrl', + 'AnyWebsocketUrl', + 'UrlConstraints', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'NatsDsn', + 'validate_email', + 'MySQLDsn', + 'MariaDBDsn', + 'ClickHouseDsn', +] + + +@_dataclasses.dataclass +class UrlConstraints(_fields.PydanticMetadata): + """Url constraints. + + Attributes: + max_length: The maximum length of the url. Defaults to `None`. + allowed_schemes: The allowed schemes. Defaults to `None`. + host_required: Whether the host is required. Defaults to `None`. + default_host: The default host. Defaults to `None`. + default_port: The default port. Defaults to `None`. + default_path: The default path. Defaults to `None`. + """ + + max_length: int | None = None + allowed_schemes: list[str] | None = None + host_required: bool | None = None + default_host: str | None = None + default_port: int | None = None + default_path: str | None = None + + def __hash__(self) -> int: + return hash( + ( + self.max_length, + tuple(self.allowed_schemes) if self.allowed_schemes is not None else None, + self.host_required, + self.default_host, + self.default_port, + self.default_path, + ) + ) + + +AnyUrl = Url +"""Base type for all URLs. + +* Any scheme allowed +* Top-level domain (TLD) not required +* Host required + +Assuming an input URL of `http://samuel:pass@example.com:8000/the/path/?query=here#fragment=is;this=bit`, +the types export the following properties: + +- `scheme`: the URL scheme (`http`), always set. +- `host`: the URL host (`example.com`), always set. +- `username`: optional username if included (`samuel`). +- `password`: optional password if included (`pass`). +- `port`: optional port (`8000`). +- `path`: optional path (`/the/path/`). +- `query`: optional URL query (for example, `GET` arguments or "search string", such as `query=here`). +- `fragment`: optional fragment (`fragment=is;this=bit`). +""" +AnyHttpUrl = Annotated[Url, UrlConstraints(allowed_schemes=['http', 'https'])] +"""A type that will accept any http or https URL. + +* TLD not required +* Host required +""" +HttpUrl = Annotated[Url, UrlConstraints(max_length=2083, allowed_schemes=['http', 'https'])] +"""A type that will accept any http or https URL. + +* TLD required +* Host required +* Max length 2083 + +```py +from pydantic import BaseModel, HttpUrl, ValidationError + +class MyModel(BaseModel): + url: HttpUrl + +m = MyModel(url='http://www.example.com') # (1)! +print(m.url) +#> http://www.example.com/ + +try: + MyModel(url='ftp://invalid.url') +except ValidationError as e: + print(e) + ''' + 1 validation error for MyModel + url + URL scheme should be 'http' or 'https' [type=url_scheme, input_value='ftp://invalid.url', input_type=str] + ''' + +try: + MyModel(url='not a url') +except ValidationError as e: + print(e) + ''' + 1 validation error for MyModel + url + Input should be a valid URL, relative URL without a base [type=url_parsing, input_value='not a url', input_type=str] + ''' +``` + +1. Note: mypy would prefer `m = MyModel(url=HttpUrl('http://www.example.com'))`, but Pydantic will convert the string to an HttpUrl instance anyway. + +"International domains" (e.g. a URL where the host or TLD includes non-ascii characters) will be encoded via +[punycode](https://en.wikipedia.org/wiki/Punycode) (see +[this article](https://www.xudongz.com/blog/2017/idn-phishing/) for a good description of why this is important): + +```py +from pydantic import BaseModel, HttpUrl + +class MyModel(BaseModel): + url: HttpUrl + +m1 = MyModel(url='http://puny£code.com') +print(m1.url) +#> http://xn--punycode-eja.com/ +m2 = MyModel(url='https://www.аррӏе.com/') +print(m2.url) +#> https://www.xn--80ak6aa92e.com/ +m3 = MyModel(url='https://www.example.珠宝/') +print(m3.url) +#> https://www.example.xn--pbt977c/ +``` + + +!!! warning "Underscores in Hostnames" + In Pydantic, underscores are allowed in all parts of a domain except the TLD. + Technically this might be wrong - in theory the hostname cannot have underscores, but subdomains can. + + To explain this; consider the following two cases: + + - `exam_ple.co.uk`: the hostname is `exam_ple`, which should not be allowed since it contains an underscore. + - `foo_bar.example.com` the hostname is `example`, which should be allowed since the underscore is in the subdomain. + + Without having an exhaustive list of TLDs, it would be impossible to differentiate between these two. Therefore + underscores are allowed, but you can always do further validation in a validator if desired. + + Also, Chrome, Firefox, and Safari all currently accept `http://exam_ple.com` as a URL, so we're in good + (or at least big) company. +""" +AnyWebsocketUrl = Annotated[Url, UrlConstraints(allowed_schemes=['ws', 'wss'])] +"""A type that will accept any ws or wss URL. + +* TLD not required +* Host required +""" +WebsocketUrl = Annotated[Url, UrlConstraints(max_length=2083, allowed_schemes=['ws', 'wss'])] +"""A type that will accept any ws or wss URL. + +* TLD required +* Host required +* Max length 2083 +""" +FileUrl = Annotated[Url, UrlConstraints(allowed_schemes=['file'])] +"""A type that will accept any file URL. + +* Host not required +""" +FtpUrl = Annotated[Url, UrlConstraints(allowed_schemes=['ftp'])] +"""A type that will accept ftp URL. + +* TLD not required +* Host required +""" +PostgresDsn = Annotated[ + MultiHostUrl, + UrlConstraints( + host_required=True, + allowed_schemes=[ + 'postgres', + 'postgresql', + 'postgresql+asyncpg', + 'postgresql+pg8000', + 'postgresql+psycopg', + 'postgresql+psycopg2', + 'postgresql+psycopg2cffi', + 'postgresql+py-postgresql', + 'postgresql+pygresql', + ], + ), +] +"""A type that will accept any Postgres DSN. + +* User info required +* TLD not required +* Host required +* Supports multiple hosts + +If further validation is required, these properties can be used by validators to enforce specific behaviour: + +```py +from pydantic import ( + BaseModel, + HttpUrl, + PostgresDsn, + ValidationError, + field_validator, +) + +class MyModel(BaseModel): + url: HttpUrl + +m = MyModel(url='http://www.example.com') + +# the repr() method for a url will display all properties of the url +print(repr(m.url)) +#> Url('http://www.example.com/') +print(m.url.scheme) +#> http +print(m.url.host) +#> www.example.com +print(m.url.port) +#> 80 + +class MyDatabaseModel(BaseModel): + db: PostgresDsn + + @field_validator('db') + def check_db_name(cls, v): + assert v.path and len(v.path) > 1, 'database must be provided' + return v + +m = MyDatabaseModel(db='postgres://user:pass@localhost:5432/foobar') +print(m.db) +#> postgres://user:pass@localhost:5432/foobar + +try: + MyDatabaseModel(db='postgres://user:pass@localhost:5432') +except ValidationError as e: + print(e) + ''' + 1 validation error for MyDatabaseModel + db + Assertion failed, database must be provided + assert (None) + + where None = MultiHostUrl('postgres://user:pass@localhost:5432').path [type=assertion_error, input_value='postgres://user:pass@localhost:5432', input_type=str] + ''' +``` +""" + +CockroachDsn = Annotated[ + Url, + UrlConstraints( + host_required=True, + allowed_schemes=[ + 'cockroachdb', + 'cockroachdb+psycopg2', + 'cockroachdb+asyncpg', + ], + ), +] +"""A type that will accept any Cockroach DSN. + +* User info required +* TLD not required +* Host required +""" +AmqpDsn = Annotated[Url, UrlConstraints(allowed_schemes=['amqp', 'amqps'])] +"""A type that will accept any AMQP DSN. + +* User info required +* TLD not required +* Host required +""" +RedisDsn = Annotated[ + Url, + UrlConstraints(allowed_schemes=['redis', 'rediss'], default_host='localhost', default_port=6379, default_path='/0'), +] +"""A type that will accept any Redis DSN. + +* User info required +* TLD not required +* Host required (e.g., `rediss://:pass@localhost`) +""" +MongoDsn = Annotated[MultiHostUrl, UrlConstraints(allowed_schemes=['mongodb', 'mongodb+srv'], default_port=27017)] +"""A type that will accept any MongoDB DSN. + +* User info not required +* Database name not required +* Port not required +* User info may be passed without user part (e.g., `mongodb://mongodb0.example.com:27017`). +""" +KafkaDsn = Annotated[Url, UrlConstraints(allowed_schemes=['kafka'], default_host='localhost', default_port=9092)] +"""A type that will accept any Kafka DSN. + +* User info required +* TLD not required +* Host required +""" +NatsDsn = Annotated[ + MultiHostUrl, UrlConstraints(allowed_schemes=['nats', 'tls', 'ws'], default_host='localhost', default_port=4222) +] +"""A type that will accept any NATS DSN. + +NATS is a connective technology built for the ever increasingly hyper-connected world. +It is a single technology that enables applications to securely communicate across +any combination of cloud vendors, on-premise, edge, web and mobile, and devices. +More: https://nats.io +""" +MySQLDsn = Annotated[ + Url, + UrlConstraints( + allowed_schemes=[ + 'mysql', + 'mysql+mysqlconnector', + 'mysql+aiomysql', + 'mysql+asyncmy', + 'mysql+mysqldb', + 'mysql+pymysql', + 'mysql+cymysql', + 'mysql+pyodbc', + ], + default_port=3306, + ), +] +"""A type that will accept any MySQL DSN. + +* User info required +* TLD not required +* Host required +""" +MariaDBDsn = Annotated[ + Url, + UrlConstraints( + allowed_schemes=['mariadb', 'mariadb+mariadbconnector', 'mariadb+pymysql'], + default_port=3306, + ), +] +"""A type that will accept any MariaDB DSN. + +* User info required +* TLD not required +* Host required +""" +ClickHouseDsn = Annotated[ + Url, + UrlConstraints( + allowed_schemes=['clickhouse+native', 'clickhouse+asynch'], + default_host='localhost', + default_port=9000, + ), +] +"""A type that will accept any ClickHouse DSN. + +* User info required +* TLD not required +* Host required +""" + + +def import_email_validator() -> None: + global email_validator + try: + import email_validator + except ImportError as e: + raise ImportError('email-validator is not installed, run `pip install pydantic[email]`') from e + if not version('email-validator').partition('.')[0] == '2': + raise ImportError('email-validator version >= 2.0 required, run pip install -U email-validator') + + +if TYPE_CHECKING: + EmailStr = Annotated[str, ...] +else: + + class EmailStr: + """ + Info: + To use this type, you need to install the optional + [`email-validator`](https://github.com/JoshData/python-email-validator) package: + + ```bash + pip install email-validator + ``` + + Validate email addresses. + + ```py + from pydantic import BaseModel, EmailStr + + class Model(BaseModel): + email: EmailStr + + print(Model(email='contact@mail.com')) + #> email='contact@mail.com' + ``` + """ # noqa: D212 + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + import_email_validator() + return core_schema.no_info_after_validator_function(cls._validate, core_schema.str_schema()) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format='email') + return field_schema + + @classmethod + def _validate(cls, input_value: str, /) -> str: + return validate_email(input_value)[1] + + +class NameEmail(_repr.Representation): + """ + Info: + To use this type, you need to install the optional + [`email-validator`](https://github.com/JoshData/python-email-validator) package: + + ```bash + pip install email-validator + ``` + + Validate a name and email address combination, as specified by + [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4). + + The `NameEmail` has two properties: `name` and `email`. + In case the `name` is not provided, it's inferred from the email address. + + ```py + from pydantic import BaseModel, NameEmail + + class User(BaseModel): + email: NameEmail + + user = User(email='Fred Bloggs ') + print(user.email) + #> Fred Bloggs + print(user.email.name) + #> Fred Bloggs + + user = User(email='fred.bloggs@example.com') + print(user.email) + #> fred.bloggs + print(user.email.name) + #> fred.bloggs + ``` + """ # noqa: D212 + + __slots__ = 'name', 'email' + + def __init__(self, name: str, email: str): + self.name = name + self.email = email + + def __eq__(self, other: Any) -> bool: + return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format='name-email') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + import_email_validator() + + return core_schema.no_info_after_validator_function( + cls._validate, + core_schema.json_or_python_schema( + json_schema=core_schema.str_schema(), + python_schema=core_schema.union_schema( + [core_schema.is_instance_schema(cls), core_schema.str_schema()], + custom_error_type='name_email_type', + custom_error_message='Input is not a valid NameEmail', + ), + serialization=core_schema.to_string_ser_schema(), + ), + ) + + @classmethod + def _validate(cls, input_value: Self | str, /) -> Self: + if isinstance(input_value, cls): + return input_value + else: + name, email = validate_email(input_value) + return cls(name, email) + + def __str__(self) -> str: + if '@' in self.name: + return f'"{self.name}" <{self.email}>' + + return f'{self.name} <{self.email}>' + + +class IPvAnyAddress: + """Validate an IPv4 or IPv6 address. + + ```py + from pydantic import BaseModel + from pydantic.networks import IPvAnyAddress + + class IpModel(BaseModel): + ip: IPvAnyAddress + + print(IpModel(ip='127.0.0.1')) + #> ip=IPv4Address('127.0.0.1') + + try: + IpModel(ip='http://www.example.com') + except ValueError as e: + print(e.errors()) + ''' + [ + { + 'type': 'ip_any_address', + 'loc': ('ip',), + 'msg': 'value is not a valid IPv4 or IPv6 address', + 'input': 'http://www.example.com', + } + ] + ''' + ``` + """ + + __slots__ = () + + def __new__(cls, value: Any) -> IPv4Address | IPv6Address: + """Validate an IPv4 or IPv6 address.""" + try: + return IPv4Address(value) + except ValueError: + pass + + try: + return IPv6Address(value) + except ValueError: + raise PydanticCustomError('ip_any_address', 'value is not a valid IPv4 or IPv6 address') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanyaddress') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: Any, /) -> IPv4Address | IPv6Address: + return cls(input_value) # type: ignore[return-value] + + +class IPvAnyInterface: + """Validate an IPv4 or IPv6 interface.""" + + __slots__ = () + + def __new__(cls, value: NetworkType) -> IPv4Interface | IPv6Interface: + """Validate an IPv4 or IPv6 interface.""" + try: + return IPv4Interface(value) + except ValueError: + pass + + try: + return IPv6Interface(value) + except ValueError: + raise PydanticCustomError('ip_any_interface', 'value is not a valid IPv4 or IPv6 interface') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanyinterface') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: NetworkType, /) -> IPv4Interface | IPv6Interface: + return cls(input_value) # type: ignore[return-value] + + +class IPvAnyNetwork: + """Validate an IPv4 or IPv6 network.""" + + __slots__ = () + + def __new__(cls, value: NetworkType) -> IPv4Network | IPv6Network: + """Validate an IPv4 or IPv6 network.""" + # Assume IP Network is defined with a default value for `strict` argument. + # Define your own class if you want to specify network address check strictness. + try: + return IPv4Network(value) + except ValueError: + pass + + try: + return IPv6Network(value) + except ValueError: + raise PydanticCustomError('ip_any_network', 'value is not a valid IPv4 or IPv6 network') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanynetwork') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: NetworkType, /) -> IPv4Network | IPv6Network: + return cls(input_value) # type: ignore[return-value] + + +def _build_pretty_email_regex() -> re.Pattern[str]: + name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]' + unquoted_name_group = rf'((?:{name_chars}+\s+)*{name_chars}+)' + quoted_name_group = r'"((?:[^"]|\")+)"' + email_group = r'<\s*(.+)\s*>' + return re.compile(rf'\s*(?:{unquoted_name_group}|{quoted_name_group})?\s*{email_group}\s*') + + +pretty_email_regex = _build_pretty_email_regex() + +MAX_EMAIL_LENGTH = 2048 +"""Maximum length for an email. +A somewhat arbitrary but very generous number compared to what is allowed by most implementations. +""" + + +def validate_email(value: str) -> tuple[str, str]: + """Email address validation using [email-validator](https://pypi.org/project/email-validator/). + + Note: + Note that: + + * Raw IP address (literal) domain parts are not allowed. + * `"John Doe "` style "pretty" email addresses are processed. + * Spaces are striped from the beginning and end of addresses, but no error is raised. + """ + if email_validator is None: + import_email_validator() + + if len(value) > MAX_EMAIL_LENGTH: + raise PydanticCustomError( + 'value_error', + 'value is not a valid email address: {reason}', + {'reason': f'Length must not exceed {MAX_EMAIL_LENGTH} characters'}, + ) + + m = pretty_email_regex.fullmatch(value) + name: str | None = None + if m: + unquoted_name, quoted_name, value = m.groups() + name = unquoted_name or quoted_name + + email = value.strip() + + try: + parts = email_validator.validate_email(email, check_deliverability=False) + except email_validator.EmailNotValidError as e: + raise PydanticCustomError( + 'value_error', 'value is not a valid email address: {reason}', {'reason': str(e.args[0])} + ) from e + + email = parts.normalized + assert email is not None + name = name or parts.local_part + return name, email + + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/parse.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/parse.py new file mode 100644 index 0000000..ceee634 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/parse.py @@ -0,0 +1,4 @@ +"""The `parse` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__init__.py new file mode 100644 index 0000000..6a9b1eb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__init__.py @@ -0,0 +1,170 @@ +"""Usage docs: https://docs.pydantic.dev/2.7/concepts/plugins#build-a-plugin + +Plugin interface for Pydantic plugins, and related types. +""" +from __future__ import annotations + +from typing import Any, Callable, NamedTuple + +from pydantic_core import CoreConfig, CoreSchema, ValidationError +from typing_extensions import Literal, Protocol, TypeAlias + +__all__ = ( + 'PydanticPluginProtocol', + 'BaseValidateHandlerProtocol', + 'ValidatePythonHandlerProtocol', + 'ValidateJsonHandlerProtocol', + 'ValidateStringsHandlerProtocol', + 'NewSchemaReturns', + 'SchemaTypePath', + 'SchemaKind', +) + +NewSchemaReturns: TypeAlias = 'tuple[ValidatePythonHandlerProtocol | None, ValidateJsonHandlerProtocol | None, ValidateStringsHandlerProtocol | None]' + + +class SchemaTypePath(NamedTuple): + """Path defining where `schema_type` was defined, or where `TypeAdapter` was called.""" + + module: str + name: str + + +SchemaKind: TypeAlias = Literal['BaseModel', 'TypeAdapter', 'dataclass', 'create_model', 'validate_call'] + + +class PydanticPluginProtocol(Protocol): + """Protocol defining the interface for Pydantic plugins.""" + + def new_schema_validator( + self, + schema: CoreSchema, + schema_type: Any, + schema_type_path: SchemaTypePath, + schema_kind: SchemaKind, + config: CoreConfig | None, + plugin_settings: dict[str, object], + ) -> tuple[ + ValidatePythonHandlerProtocol | None, ValidateJsonHandlerProtocol | None, ValidateStringsHandlerProtocol | None + ]: + """This method is called for each plugin every time a new [`SchemaValidator`][pydantic_core.SchemaValidator] + is created. + + It should return an event handler for each of the three validation methods, or `None` if the plugin does not + implement that method. + + Args: + schema: The schema to validate against. + schema_type: The original type which the schema was created from, e.g. the model class. + schema_type_path: Path defining where `schema_type` was defined, or where `TypeAdapter` was called. + schema_kind: The kind of schema to validate against. + config: The config to use for validation. + plugin_settings: Any plugin settings. + + Returns: + A tuple of optional event handlers for each of the three validation methods - + `validate_python`, `validate_json`, `validate_strings`. + """ + raise NotImplementedError('Pydantic plugins should implement `new_schema_validator`.') + + +class BaseValidateHandlerProtocol(Protocol): + """Base class for plugin callbacks protocols. + + You shouldn't implement this protocol directly, instead use one of the subclasses with adds the correctly + typed `on_error` method. + """ + + on_enter: Callable[..., None] + """`on_enter` is changed to be more specific on all subclasses""" + + def on_success(self, result: Any) -> None: + """Callback to be notified of successful validation. + + Args: + result: The result of the validation. + """ + return + + def on_error(self, error: ValidationError) -> None: + """Callback to be notified of validation errors. + + Args: + error: The validation error. + """ + return + + def on_exception(self, exception: Exception) -> None: + """Callback to be notified of validation exceptions. + + Args: + exception: The exception raised during validation. + """ + return + + +class ValidatePythonHandlerProtocol(BaseValidateHandlerProtocol, Protocol): + """Event handler for `SchemaValidator.validate_python`.""" + + def on_enter( + self, + input: Any, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + self_instance: Any | None = None, + ) -> None: + """Callback to be notified of validation start, and create an instance of the event handler. + + Args: + input: The input to be validated. + strict: Whether to validate the object in strict mode. + from_attributes: Whether to validate objects as inputs by extracting attributes. + context: The context to use for validation, this is passed to functional validators. + self_instance: An instance of a model to set attributes on from validation, this is used when running + validation from the `__init__` method of a model. + """ + pass + + +class ValidateJsonHandlerProtocol(BaseValidateHandlerProtocol, Protocol): + """Event handler for `SchemaValidator.validate_json`.""" + + def on_enter( + self, + input: str | bytes | bytearray, + *, + strict: bool | None = None, + context: dict[str, Any] | None = None, + self_instance: Any | None = None, + ) -> None: + """Callback to be notified of validation start, and create an instance of the event handler. + + Args: + input: The JSON data to be validated. + strict: Whether to validate the object in strict mode. + context: The context to use for validation, this is passed to functional validators. + self_instance: An instance of a model to set attributes on from validation, this is used when running + validation from the `__init__` method of a model. + """ + pass + + +StringInput: TypeAlias = 'dict[str, StringInput]' + + +class ValidateStringsHandlerProtocol(BaseValidateHandlerProtocol, Protocol): + """Event handler for `SchemaValidator.validate_strings`.""" + + def on_enter( + self, input: StringInput, *, strict: bool | None = None, context: dict[str, Any] | None = None + ) -> None: + """Callback to be notified of validation start, and create an instance of the event handler. + + Args: + input: The string data to be validated. + strict: Whether to validate the object in strict mode. + context: The context to use for validation, this is passed to functional validators. + """ + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4b2d138 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_loader.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_loader.cpython-310.pyc new file mode 100644 index 0000000..4bb6bd3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_loader.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_schema_validator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_schema_validator.cpython-310.pyc new file mode 100644 index 0000000..26ea500 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/__pycache__/_schema_validator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_loader.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_loader.py new file mode 100644 index 0000000..2f90dc5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_loader.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import importlib.metadata as importlib_metadata +import os +import warnings +from typing import TYPE_CHECKING, Final, Iterable + +if TYPE_CHECKING: + from . import PydanticPluginProtocol + + +PYDANTIC_ENTRY_POINT_GROUP: Final[str] = 'pydantic' + +# cache of plugins +_plugins: dict[str, PydanticPluginProtocol] | None = None +# return no plugins while loading plugins to avoid recursion and errors while import plugins +# this means that if plugins use pydantic +_loading_plugins: bool = False + + +def get_plugins() -> Iterable[PydanticPluginProtocol]: + """Load plugins for Pydantic. + + Inspired by: https://github.com/pytest-dev/pluggy/blob/1.3.0/src/pluggy/_manager.py#L376-L402 + """ + disabled_plugins = os.getenv('PYDANTIC_DISABLE_PLUGINS') + global _plugins, _loading_plugins + if _loading_plugins: + # this happens when plugins themselves use pydantic, we return no plugins + return () + elif disabled_plugins in ('__all__', '1', 'true'): + return () + elif _plugins is None: + _plugins = {} + # set _loading_plugins so any plugins that use pydantic don't themselves use plugins + _loading_plugins = True + try: + for dist in importlib_metadata.distributions(): + for entry_point in dist.entry_points: + if entry_point.group != PYDANTIC_ENTRY_POINT_GROUP: + continue + if entry_point.value in _plugins: + continue + if disabled_plugins is not None and entry_point.name in disabled_plugins.split(','): + continue + try: + _plugins[entry_point.value] = entry_point.load() + except (ImportError, AttributeError) as e: + warnings.warn( + f'{e.__class__.__name__} while loading the `{entry_point.name}` Pydantic plugin, ' + f'this plugin will not be installed.\n\n{e!r}' + ) + finally: + _loading_plugins = False + + return _plugins.values() diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_schema_validator.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_schema_validator.py new file mode 100644 index 0000000..7186ece --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/plugin/_schema_validator.py @@ -0,0 +1,138 @@ +"""Pluggable schema validator for pydantic.""" +from __future__ import annotations + +import functools +from typing import TYPE_CHECKING, Any, Callable, Iterable, TypeVar + +from pydantic_core import CoreConfig, CoreSchema, SchemaValidator, ValidationError +from typing_extensions import Literal, ParamSpec + +if TYPE_CHECKING: + from . import BaseValidateHandlerProtocol, PydanticPluginProtocol, SchemaKind, SchemaTypePath + + +P = ParamSpec('P') +R = TypeVar('R') +Event = Literal['on_validate_python', 'on_validate_json', 'on_validate_strings'] +events: list[Event] = list(Event.__args__) # type: ignore + + +def create_schema_validator( + schema: CoreSchema, + schema_type: Any, + schema_type_module: str, + schema_type_name: str, + schema_kind: SchemaKind, + config: CoreConfig | None = None, + plugin_settings: dict[str, Any] | None = None, +) -> SchemaValidator: + """Create a `SchemaValidator` or `PluggableSchemaValidator` if plugins are installed. + + Returns: + If plugins are installed then return `PluggableSchemaValidator`, otherwise return `SchemaValidator`. + """ + from . import SchemaTypePath + from ._loader import get_plugins + + plugins = get_plugins() + if plugins: + return PluggableSchemaValidator( + schema, + schema_type, + SchemaTypePath(schema_type_module, schema_type_name), + schema_kind, + config, + plugins, + plugin_settings or {}, + ) # type: ignore + else: + return SchemaValidator(schema, config) + + +class PluggableSchemaValidator: + """Pluggable schema validator.""" + + __slots__ = '_schema_validator', 'validate_json', 'validate_python', 'validate_strings' + + def __init__( + self, + schema: CoreSchema, + schema_type: Any, + schema_type_path: SchemaTypePath, + schema_kind: SchemaKind, + config: CoreConfig | None, + plugins: Iterable[PydanticPluginProtocol], + plugin_settings: dict[str, Any], + ) -> None: + self._schema_validator = SchemaValidator(schema, config) + + python_event_handlers: list[BaseValidateHandlerProtocol] = [] + json_event_handlers: list[BaseValidateHandlerProtocol] = [] + strings_event_handlers: list[BaseValidateHandlerProtocol] = [] + for plugin in plugins: + try: + p, j, s = plugin.new_schema_validator( + schema, schema_type, schema_type_path, schema_kind, config, plugin_settings + ) + except TypeError as e: # pragma: no cover + raise TypeError(f'Error using plugin `{plugin.__module__}:{plugin.__class__.__name__}`: {e}') from e + if p is not None: + python_event_handlers.append(p) + if j is not None: + json_event_handlers.append(j) + if s is not None: + strings_event_handlers.append(s) + + self.validate_python = build_wrapper(self._schema_validator.validate_python, python_event_handlers) + self.validate_json = build_wrapper(self._schema_validator.validate_json, json_event_handlers) + self.validate_strings = build_wrapper(self._schema_validator.validate_strings, strings_event_handlers) + + def __getattr__(self, name: str) -> Any: + return getattr(self._schema_validator, name) + + +def build_wrapper(func: Callable[P, R], event_handlers: list[BaseValidateHandlerProtocol]) -> Callable[P, R]: + if not event_handlers: + return func + else: + on_enters = tuple(h.on_enter for h in event_handlers if filter_handlers(h, 'on_enter')) + on_successes = tuple(h.on_success for h in event_handlers if filter_handlers(h, 'on_success')) + on_errors = tuple(h.on_error for h in event_handlers if filter_handlers(h, 'on_error')) + on_exceptions = tuple(h.on_exception for h in event_handlers if filter_handlers(h, 'on_exception')) + + @functools.wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + for on_enter_handler in on_enters: + on_enter_handler(*args, **kwargs) + + try: + result = func(*args, **kwargs) + except ValidationError as error: + for on_error_handler in on_errors: + on_error_handler(error) + raise + except Exception as exception: + for on_exception_handler in on_exceptions: + on_exception_handler(exception) + raise + else: + for on_success_handler in on_successes: + on_success_handler(result) + return result + + return wrapper + + +def filter_handlers(handler_cls: BaseValidateHandlerProtocol, method_name: str) -> bool: + """Filter out handler methods which are not implemented by the plugin directly - e.g. are missing + or are inherited from the protocol. + """ + handler = getattr(handler_cls, method_name, None) + if handler is None: + return False + elif handler.__module__ == 'pydantic.plugin': + # this is the original handler, from the protocol due to runtime inheritance + # we don't want to call it + return False + else: + return True diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/py.typed b/.pythonlibs/lib/python3.10/site-packages/pydantic/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/root_model.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/root_model.py new file mode 100644 index 0000000..c579642 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/root_model.py @@ -0,0 +1,155 @@ +"""RootModel class and type definitions.""" + +from __future__ import annotations as _annotations + +import typing +from copy import copy, deepcopy + +from pydantic_core import PydanticUndefined + +from . import PydanticUserError +from ._internal import _model_construction, _repr +from .main import BaseModel, _object_setattr + +if typing.TYPE_CHECKING: + from typing import Any + + from typing_extensions import Literal, dataclass_transform + + from .fields import Field as PydanticModelField + + # dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform + # takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform + # on a new metaclass. + @dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField,)) + class _RootModelMetaclass(_model_construction.ModelMetaclass): + ... +else: + _RootModelMetaclass = _model_construction.ModelMetaclass + +__all__ = ('RootModel',) + +Model = typing.TypeVar('Model', bound='BaseModel') +RootModelRootType = typing.TypeVar('RootModelRootType') + + +class RootModel(BaseModel, typing.Generic[RootModelRootType], metaclass=_RootModelMetaclass): + """Usage docs: https://docs.pydantic.dev/2.7/concepts/models/#rootmodel-and-custom-root-types + + A Pydantic `BaseModel` for the root object of the model. + + Attributes: + root: The root object of the model. + __pydantic_root_model__: Whether the model is a RootModel. + __pydantic_private__: Private fields in the model. + __pydantic_extra__: Extra fields in the model. + + """ + + __pydantic_root_model__ = True + __pydantic_private__ = None + __pydantic_extra__ = None + + root: RootModelRootType + + def __init_subclass__(cls, **kwargs): + extra = cls.model_config.get('extra') + if extra is not None: + raise PydanticUserError( + "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra' + ) + super().__init_subclass__(**kwargs) + + def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore + __tracebackhide__ = True + if data: + if root is not PydanticUndefined: + raise ValueError( + '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments' + ) + root = data # type: ignore + self.__pydantic_validator__.validate_python(root, self_instance=self) + + __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess] + + @classmethod + def model_construct(cls: type[Model], root: RootModelRootType, _fields_set: set[str] | None = None) -> Model: # type: ignore + """Create a new model using the provided root object and update fields set. + + Args: + root: The root object of the model. + _fields_set: The set of fields to be updated. + + Returns: + The new model. + + Raises: + NotImplemented: If the model is not a subclass of `RootModel`. + """ + return super().model_construct(root=root, _fields_set=_fields_set) + + def __getstate__(self) -> dict[Any, Any]: + return { + '__dict__': self.__dict__, + '__pydantic_fields_set__': self.__pydantic_fields_set__, + } + + def __setstate__(self, state: dict[Any, Any]) -> None: + _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__']) + _object_setattr(self, '__dict__', state['__dict__']) + + def __copy__(self: Model) -> Model: + """Returns a shallow copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', copy(self.__dict__)) + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + return m + + def __deepcopy__(self: Model, memo: dict[int, Any] | None = None) -> Model: + """Returns a deep copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo)) + # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str], + # and attempting a deepcopy would be marginally slower. + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + return m + + if typing.TYPE_CHECKING: + + def model_dump( # type: ignore + self, + *, + mode: Literal['json', 'python'] | str = 'python', + include: Any = None, + exclude: Any = None, + context: dict[str, Any] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + serialize_as_any: bool = False, + ) -> Any: + """This method is included just to get a more accurate return type for type checkers. + It is included in this `if TYPE_CHECKING:` block since no override is actually necessary. + + See the documentation of `BaseModel.model_dump` for more details about the arguments. + + Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is + not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return + type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could + even be something different, in the case of a custom serializer. + Thus, `Any` is used here to catch all of these cases. + """ + ... + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, RootModel): + return NotImplemented + return self.model_fields['root'].annotation == other.model_fields['root'].annotation and super().__eq__(other) + + def __repr_args__(self) -> _repr.ReprArgs: + yield 'root', self.root diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/schema.py new file mode 100644 index 0000000..e290aed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/schema.py @@ -0,0 +1,4 @@ +"""The `schema` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/tools.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/tools.py new file mode 100644 index 0000000..8e317c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/tools.py @@ -0,0 +1,4 @@ +"""The `tools` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/type_adapter.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/type_adapter.py new file mode 100644 index 0000000..e055a38 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/type_adapter.py @@ -0,0 +1,473 @@ +"""Type adapter specification.""" +from __future__ import annotations as _annotations + +import sys +from dataclasses import is_dataclass +from typing import TYPE_CHECKING, Any, Dict, Generic, Iterable, Set, TypeVar, Union, cast, final, overload + +from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator, Some +from typing_extensions import Literal, get_args, is_typeddict + +from pydantic.errors import PydanticUserError +from pydantic.main import BaseModel + +from ._internal import _config, _generate_schema, _typing_extra +from .config import ConfigDict +from .json_schema import ( + DEFAULT_REF_TEMPLATE, + GenerateJsonSchema, + JsonSchemaKeyT, + JsonSchemaMode, + JsonSchemaValue, +) +from .plugin._schema_validator import create_schema_validator + +T = TypeVar('T') + + +if TYPE_CHECKING: + # should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope + IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]] + + +def _get_schema(type_: Any, config_wrapper: _config.ConfigWrapper, parent_depth: int) -> CoreSchema: + """`BaseModel` uses its own `__module__` to find out where it was defined + and then looks for symbols to resolve forward references in those globals. + On the other hand this function can be called with arbitrary objects, + including type aliases, where `__module__` (always `typing.py`) is not useful. + So instead we look at the globals in our parent stack frame. + + This works for the case where this function is called in a module that + has the target of forward references in its scope, but + does not always work for more complex cases. + + For example, take the following: + + a.py + ```python + from typing import Dict, List + + IntList = List[int] + OuterDict = Dict[str, 'IntList'] + ``` + + b.py + ```python test="skip" + from a import OuterDict + + from pydantic import TypeAdapter + + IntList = int # replaces the symbol the forward reference is looking for + v = TypeAdapter(OuterDict) + v({'x': 1}) # should fail but doesn't + ``` + + If `OuterDict` were a `BaseModel`, this would work because it would resolve + the forward reference within the `a.py` namespace. + But `TypeAdapter(OuterDict)` can't determine what module `OuterDict` came from. + + In other words, the assumption that _all_ forward references exist in the + module we are being called from is not technically always true. + Although most of the time it is and it works fine for recursive models and such, + `BaseModel`'s behavior isn't perfect either and _can_ break in similar ways, + so there is no right or wrong between the two. + + But at the very least this behavior is _subtly_ different from `BaseModel`'s. + """ + local_ns = _typing_extra.parent_frame_namespace(parent_depth=parent_depth) + global_ns = sys._getframe(max(parent_depth - 1, 1)).f_globals.copy() + global_ns.update(local_ns or {}) + gen = _generate_schema.GenerateSchema(config_wrapper, types_namespace=global_ns, typevars_map={}) + schema = gen.generate_schema(type_) + schema = gen.clean_schema(schema) + return schema + + +def _getattr_no_parents(obj: Any, attribute: str) -> Any: + """Returns the attribute value without attempting to look up attributes from parent types.""" + if hasattr(obj, '__dict__'): + try: + return obj.__dict__[attribute] + except KeyError: + pass + + slots = getattr(obj, '__slots__', None) + if slots is not None and attribute in slots: + return getattr(obj, attribute) + else: + raise AttributeError(attribute) + + +def _type_has_config(type_: Any) -> bool: + """Returns whether the type has config.""" + try: + return issubclass(type_, BaseModel) or is_dataclass(type_) or is_typeddict(type_) + except TypeError: + # type is not a class + return False + + +@final +class TypeAdapter(Generic[T]): + """Usage docs: https://docs.pydantic.dev/2.7/concepts/type_adapter/ + + Type adapters provide a flexible way to perform validation and serialization based on a Python type. + + A `TypeAdapter` instance exposes some of the functionality from `BaseModel` instance methods + for types that do not have such methods (such as dataclasses, primitive types, and more). + + **Note:** `TypeAdapter` instances are not types, and cannot be used as type annotations for fields. + + Attributes: + core_schema: The core schema for the type. + validator (SchemaValidator): The schema validator for the type. + serializer: The schema serializer for the type. + """ + + @overload + def __init__( + self: TypeAdapter[T], + type: type[T], + *, + config: ConfigDict | None = ..., + _parent_depth: int = ..., + module: str | None = ..., + ) -> None: + ... + + # This second overload is for unsupported special forms (such as Annotated, Union, etc.) + # Currently there is no way to type this correctly + # See https://github.com/python/typing/pull/1618 + @overload + def __init__( + self: TypeAdapter[Any], + type: Any, + *, + config: ConfigDict | None = ..., + _parent_depth: int = ..., + module: str | None = ..., + ) -> None: + ... + + def __init__( + self, + type: Any, + *, + config: ConfigDict | None = None, + _parent_depth: int = 2, + module: str | None = None, + ) -> None: + """Initializes the TypeAdapter object. + + Args: + type: The type associated with the `TypeAdapter`. + config: Configuration for the `TypeAdapter`, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict]. + _parent_depth: depth at which to search the parent namespace to construct the local namespace. + module: The module that passes to plugin if provided. + + !!! note + You cannot use the `config` argument when instantiating a `TypeAdapter` if the type you're using has its own + config that cannot be overridden (ex: `BaseModel`, `TypedDict`, and `dataclass`). A + [`type-adapter-config-unused`](../errors/usage_errors.md#type-adapter-config-unused) error will be raised in this case. + + !!! note + The `_parent_depth` argument is named with an underscore to suggest its private nature and discourage use. + It may be deprecated in a minor version, so we only recommend using it if you're + comfortable with potential change in behavior / support. + + ??? tip "Compatibility with `mypy`" + Depending on the type used, `mypy` might raise an error when instantiating a `TypeAdapter`. As a workaround, you can explicitly + annotate your variable: + + ```py + from typing import Union + + from pydantic import TypeAdapter + + ta: TypeAdapter[Union[str, int]] = TypeAdapter(Union[str, int]) # type: ignore[arg-type] + ``` + + Returns: + A type adapter configured for the specified `type`. + """ + type_is_annotated: bool = _typing_extra.is_annotated(type) + annotated_type: Any = get_args(type)[0] if type_is_annotated else None + type_has_config: bool = _type_has_config(annotated_type if type_is_annotated else type) + + if type_has_config and config is not None: + raise PydanticUserError( + 'Cannot use `config` when the type is a BaseModel, dataclass or TypedDict.' + ' These types can have their own config and setting the config via the `config`' + ' parameter to TypeAdapter will not override it, thus the `config` you passed to' + ' TypeAdapter becomes meaningless, which is probably not what you want.', + code='type-adapter-config-unused', + ) + + config_wrapper = _config.ConfigWrapper(config) + + core_schema: CoreSchema + try: + core_schema = _getattr_no_parents(type, '__pydantic_core_schema__') + except AttributeError: + core_schema = _get_schema(type, config_wrapper, parent_depth=_parent_depth + 1) + + core_config = config_wrapper.core_config(None) + validator: SchemaValidator + try: + validator = _getattr_no_parents(type, '__pydantic_validator__') + except AttributeError: + if module is None: + f = sys._getframe(1) + module = cast(str, f.f_globals.get('__name__', '')) + validator = create_schema_validator( + core_schema, type, module, str(type), 'TypeAdapter', core_config, config_wrapper.plugin_settings + ) # type: ignore + + serializer: SchemaSerializer + try: + serializer = _getattr_no_parents(type, '__pydantic_serializer__') + except AttributeError: + serializer = SchemaSerializer(core_schema, core_config) + + self.core_schema = core_schema + self.validator = validator + self.serializer = serializer + + def validate_python( + self, + object: Any, + /, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + ) -> T: + """Validate a Python object against the model. + + Args: + object: The Python object to validate against the model. + strict: Whether to strictly check types. + from_attributes: Whether to extract data from object attributes. + context: Additional context to pass to the validator. + + !!! note + When using `TypeAdapter` with a Pydantic `dataclass`, the use of the `from_attributes` + argument is not supported. + + Returns: + The validated object. + """ + return self.validator.validate_python(object, strict=strict, from_attributes=from_attributes, context=context) + + def validate_json( + self, data: str | bytes, /, *, strict: bool | None = None, context: dict[str, Any] | None = None + ) -> T: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json/#json-parsing + + Validate a JSON string or bytes against the model. + + Args: + data: The JSON data to validate against the model. + strict: Whether to strictly check types. + context: Additional context to use during validation. + + Returns: + The validated object. + """ + return self.validator.validate_json(data, strict=strict, context=context) + + def validate_strings(self, obj: Any, /, *, strict: bool | None = None, context: dict[str, Any] | None = None) -> T: + """Validate object contains string data against the model. + + Args: + obj: The object contains string data to validate. + strict: Whether to strictly check types. + context: Additional context to use during validation. + + Returns: + The validated object. + """ + return self.validator.validate_strings(obj, strict=strict, context=context) + + def get_default_value(self, *, strict: bool | None = None, context: dict[str, Any] | None = None) -> Some[T] | None: + """Get the default value for the wrapped type. + + Args: + strict: Whether to strictly check types. + context: Additional context to pass to the validator. + + Returns: + The default value wrapped in a `Some` if there is one or None if not. + """ + return self.validator.get_default_value(strict=strict, context=context) + + def dump_python( + self, + instance: T, + /, + *, + mode: Literal['json', 'python'] = 'python', + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + serialize_as_any: bool = False, + ) -> Any: + """Dump an instance of the adapted type to a Python object. + + Args: + instance: The Python object to serialize. + mode: The output format. + include: Fields to include in the output. + exclude: Fields to exclude from the output. + by_alias: Whether to use alias names for field names. + exclude_unset: Whether to exclude unset fields. + exclude_defaults: Whether to exclude fields with default values. + exclude_none: Whether to exclude fields with None values. + round_trip: Whether to output the serialized data in a way that is compatible with deserialization. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + The serialized object. + """ + return self.serializer.to_python( + instance, + mode=mode, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + round_trip=round_trip, + warnings=warnings, + serialize_as_any=serialize_as_any, + ) + + def dump_json( + self, + instance: T, + /, + *, + indent: int | None = None, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + serialize_as_any: bool = False, + ) -> bytes: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/json/#json-serialization + + Serialize an instance of the adapted type to JSON. + + Args: + instance: The instance to be serialized. + indent: Number of spaces for JSON indentation. + include: Fields to include. + exclude: Fields to exclude. + by_alias: Whether to use alias names for field names. + exclude_unset: Whether to exclude unset fields. + exclude_defaults: Whether to exclude fields with default values. + exclude_none: Whether to exclude fields with a value of `None`. + round_trip: Whether to serialize and deserialize the instance to ensure round-tripping. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + The JSON representation of the given instance as bytes. + """ + return self.serializer.to_json( + instance, + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + round_trip=round_trip, + warnings=warnings, + serialize_as_any=serialize_as_any, + ) + + def json_schema( + self, + *, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', + ) -> dict[str, Any]: + """Generate a JSON schema for the adapted type. + + Args: + by_alias: Whether to use alias names for field names. + ref_template: The format string used for generating $ref strings. + schema_generator: The generator class used for creating the schema. + mode: The mode to use for schema generation. + + Returns: + The JSON schema for the model as a dictionary. + """ + schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) + return schema_generator_instance.generate(self.core_schema, mode=mode) + + @staticmethod + def json_schemas( + inputs: Iterable[tuple[JsonSchemaKeyT, JsonSchemaMode, TypeAdapter[Any]]], + /, + *, + by_alias: bool = True, + title: str | None = None, + description: str | None = None, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: + """Generate a JSON schema including definitions from multiple type adapters. + + Args: + inputs: Inputs to schema generation. The first two items will form the keys of the (first) + output mapping; the type adapters will provide the core schemas that get converted into + definitions in the output JSON schema. + by_alias: Whether to use alias names. + title: The title for the schema. + description: The description for the schema. + ref_template: The format string used for generating $ref strings. + schema_generator: The generator class used for creating the schema. + + Returns: + A tuple where: + + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a JSON schema containing all definitions referenced in the first returned + element, along with the optional title and description keys. + + """ + schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) + + inputs_ = [(key, mode, adapter.core_schema) for key, mode, adapter in inputs] + + json_schemas_map, definitions = schema_generator_instance.generate_definitions(inputs_) + + json_schema: dict[str, Any] = {} + if definitions: + json_schema['$defs'] = definitions + if title: + json_schema['title'] = title + if description: + json_schema['description'] = description + + return json_schemas_map, json_schema diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/types.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/types.py new file mode 100644 index 0000000..f1025a5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/types.py @@ -0,0 +1,3004 @@ +"""The types module contains custom types used by pydantic.""" +from __future__ import annotations as _annotations + +import base64 +import dataclasses as _dataclasses +import re +from datetime import date, datetime +from decimal import Decimal +from enum import Enum +from pathlib import Path +from types import ModuleType +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + FrozenSet, + Generic, + Hashable, + Iterator, + List, + Pattern, + Set, + TypeVar, + Union, + cast, + get_args, + get_origin, +) +from uuid import UUID + +import annotated_types +from annotated_types import BaseMetadata, MaxLen, MinLen +from pydantic_core import CoreSchema, PydanticCustomError, core_schema +from typing_extensions import Annotated, Literal, Protocol, TypeAlias, TypeAliasType, deprecated + +from ._internal import ( + _core_utils, + _fields, + _internal_dataclass, + _typing_extra, + _utils, + _validators, +) +from ._migration import getattr_migration +from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler +from .errors import PydanticUserError +from .json_schema import JsonSchemaValue +from .warnings import PydanticDeprecatedSince20 + +__all__ = ( + 'Strict', + 'StrictStr', + 'conbytes', + 'conlist', + 'conset', + 'confrozenset', + 'constr', + 'ImportString', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'condecimal', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'NewPath', + 'Json', + 'Secret', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'PastDatetime', + 'FutureDatetime', + 'condate', + 'AwareDatetime', + 'NaiveDatetime', + 'AllowInfNan', + 'EncoderProtocol', + 'EncodedBytes', + 'EncodedStr', + 'Base64Encoder', + 'Base64Bytes', + 'Base64Str', + 'Base64UrlBytes', + 'Base64UrlStr', + 'GetPydanticSchema', + 'StringConstraints', + 'Tag', + 'Discriminator', + 'JsonValue', + 'OnErrorOmit', +) + + +T = TypeVar('T') + + +@_dataclasses.dataclass +class Strict(_fields.PydanticMetadata, BaseMetadata): + """Usage docs: https://docs.pydantic.dev/2.7/concepts/strict_mode/#strict-mode-with-annotated-strict + + A field metadata class to indicate that a field should be validated in strict mode. + + Attributes: + strict: Whether to validate the field in strict mode. + + Example: + ```python + from typing_extensions import Annotated + + from pydantic.types import Strict + + StrictBool = Annotated[bool, Strict()] + ``` + """ + + strict: bool = True + + def __hash__(self) -> int: + return hash(self.strict) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +StrictBool = Annotated[bool, Strict()] +"""A boolean that must be either ``True`` or ``False``.""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def conint( + *, + strict: bool | None = None, + gt: int | None = None, + ge: int | None = None, + lt: int | None = None, + le: int | None = None, + multiple_of: int | None = None, +) -> type[int]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `conint` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```py + from pydantic import BaseModel, conint + + class Foo(BaseModel): + bar: conint(strict=True, gt=0) + ``` + + === ":white_check_mark: Do this" + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[int, Field(strict=True, gt=0)] + ``` + + A wrapper around `int` that allows for additional constraints. + + Args: + strict: Whether to validate the integer in strict mode. Defaults to `None`. + gt: The value must be greater than this. + ge: The value must be greater than or equal to this. + lt: The value must be less than this. + le: The value must be less than or equal to this. + multiple_of: The value must be a multiple of this. + + Returns: + The wrapped integer type. + + ```py + from pydantic import BaseModel, ValidationError, conint + + class ConstrainedExample(BaseModel): + constrained_int: conint(gt=1) + + m = ConstrainedExample(constrained_int=2) + print(repr(m)) + #> ConstrainedExample(constrained_int=2) + + try: + ConstrainedExample(constrained_int=0) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_int',), + 'msg': 'Input should be greater than 1', + 'input': 0, + 'ctx': {'gt': 1}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + + """ # noqa: D212 + return Annotated[ + int, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + ] + + +PositiveInt = Annotated[int, annotated_types.Gt(0)] +"""An integer that must be greater than zero. + +```py +from pydantic import BaseModel, PositiveInt, ValidationError + +class Model(BaseModel): + positive_int: PositiveInt + +m = Model(positive_int=1) +print(repr(m)) +#> Model(positive_int=1) + +try: + Model(positive_int=-1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('positive_int',), + 'msg': 'Input should be greater than 0', + 'input': -1, + 'ctx': {'gt': 0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' +``` +""" +NegativeInt = Annotated[int, annotated_types.Lt(0)] +"""An integer that must be less than zero. + +```py +from pydantic import BaseModel, NegativeInt, ValidationError + +class Model(BaseModel): + negative_int: NegativeInt + +m = Model(negative_int=-1) +print(repr(m)) +#> Model(negative_int=-1) + +try: + Model(negative_int=1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than', + 'loc': ('negative_int',), + 'msg': 'Input should be less than 0', + 'input': 1, + 'ctx': {'lt': 0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than', + } + ] + ''' +``` +""" +NonPositiveInt = Annotated[int, annotated_types.Le(0)] +"""An integer that must be less than or equal to zero. + +```py +from pydantic import BaseModel, NonPositiveInt, ValidationError + +class Model(BaseModel): + non_positive_int: NonPositiveInt + +m = Model(non_positive_int=0) +print(repr(m)) +#> Model(non_positive_int=0) + +try: + Model(non_positive_int=1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than_equal', + 'loc': ('non_positive_int',), + 'msg': 'Input should be less than or equal to 0', + 'input': 1, + 'ctx': {'le': 0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than_equal', + } + ] + ''' +``` +""" +NonNegativeInt = Annotated[int, annotated_types.Ge(0)] +"""An integer that must be greater than or equal to zero. + +```py +from pydantic import BaseModel, NonNegativeInt, ValidationError + +class Model(BaseModel): + non_negative_int: NonNegativeInt + +m = Model(non_negative_int=0) +print(repr(m)) +#> Model(non_negative_int=0) + +try: + Model(non_negative_int=-1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than_equal', + 'loc': ('non_negative_int',), + 'msg': 'Input should be greater than or equal to 0', + 'input': -1, + 'ctx': {'ge': 0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal', + } + ] + ''' +``` +""" +StrictInt = Annotated[int, Strict()] +"""An integer that must be validated in strict mode. + +```py +from pydantic import BaseModel, StrictInt, ValidationError + +class StrictIntModel(BaseModel): + strict_int: StrictInt + +try: + StrictIntModel(strict_int=3.14159) +except ValidationError as e: + print(e) + ''' + 1 validation error for StrictIntModel + strict_int + Input should be a valid integer [type=int_type, input_value=3.14159, input_type=float] + ''' +``` +""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass +class AllowInfNan(_fields.PydanticMetadata): + """A field metadata class to indicate that a field should allow ``-inf``, ``inf``, and ``nan``.""" + + allow_inf_nan: bool = True + + def __hash__(self) -> int: + return hash(self.allow_inf_nan) + + +def confloat( + *, + strict: bool | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + multiple_of: float | None = None, + allow_inf_nan: bool | None = None, +) -> type[float]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `confloat` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```py + from pydantic import BaseModel, confloat + + class Foo(BaseModel): + bar: confloat(strict=True, gt=0) + ``` + + === ":white_check_mark: Do this" + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[float, Field(strict=True, gt=0)] + ``` + + A wrapper around `float` that allows for additional constraints. + + Args: + strict: Whether to validate the float in strict mode. + gt: The value must be greater than this. + ge: The value must be greater than or equal to this. + lt: The value must be less than this. + le: The value must be less than or equal to this. + multiple_of: The value must be a multiple of this. + allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. + + Returns: + The wrapped float type. + + ```py + from pydantic import BaseModel, ValidationError, confloat + + class ConstrainedExample(BaseModel): + constrained_float: confloat(gt=1.0) + + m = ConstrainedExample(constrained_float=1.1) + print(repr(m)) + #> ConstrainedExample(constrained_float=1.1) + + try: + ConstrainedExample(constrained_float=0.9) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_float',), + 'msg': 'Input should be greater than 1', + 'input': 0.9, + 'ctx': {'gt': 1.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + """ # noqa: D212 + return Annotated[ + float, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None, + ] + + +PositiveFloat = Annotated[float, annotated_types.Gt(0)] +"""A float that must be greater than zero. + +```py +from pydantic import BaseModel, PositiveFloat, ValidationError + +class Model(BaseModel): + positive_float: PositiveFloat + +m = Model(positive_float=1.0) +print(repr(m)) +#> Model(positive_float=1.0) + +try: + Model(positive_float=-1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('positive_float',), + 'msg': 'Input should be greater than 0', + 'input': -1.0, + 'ctx': {'gt': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' +``` +""" +NegativeFloat = Annotated[float, annotated_types.Lt(0)] +"""A float that must be less than zero. + +```py +from pydantic import BaseModel, NegativeFloat, ValidationError + +class Model(BaseModel): + negative_float: NegativeFloat + +m = Model(negative_float=-1.0) +print(repr(m)) +#> Model(negative_float=-1.0) + +try: + Model(negative_float=1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than', + 'loc': ('negative_float',), + 'msg': 'Input should be less than 0', + 'input': 1.0, + 'ctx': {'lt': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than', + } + ] + ''' +``` +""" +NonPositiveFloat = Annotated[float, annotated_types.Le(0)] +"""A float that must be less than or equal to zero. + +```py +from pydantic import BaseModel, NonPositiveFloat, ValidationError + +class Model(BaseModel): + non_positive_float: NonPositiveFloat + +m = Model(non_positive_float=0.0) +print(repr(m)) +#> Model(non_positive_float=0.0) + +try: + Model(non_positive_float=1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than_equal', + 'loc': ('non_positive_float',), + 'msg': 'Input should be less than or equal to 0', + 'input': 1.0, + 'ctx': {'le': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than_equal', + } + ] + ''' +``` +""" +NonNegativeFloat = Annotated[float, annotated_types.Ge(0)] +"""A float that must be greater than or equal to zero. + +```py +from pydantic import BaseModel, NonNegativeFloat, ValidationError + +class Model(BaseModel): + non_negative_float: NonNegativeFloat + +m = Model(non_negative_float=0.0) +print(repr(m)) +#> Model(non_negative_float=0.0) + +try: + Model(non_negative_float=-1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than_equal', + 'loc': ('non_negative_float',), + 'msg': 'Input should be greater than or equal to 0', + 'input': -1.0, + 'ctx': {'ge': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal', + } + ] + ''' +``` +""" +StrictFloat = Annotated[float, Strict(True)] +"""A float that must be validated in strict mode. + +```py +from pydantic import BaseModel, StrictFloat, ValidationError + +class StrictFloatModel(BaseModel): + strict_float: StrictFloat + +try: + StrictFloatModel(strict_float='1.0') +except ValidationError as e: + print(e) + ''' + 1 validation error for StrictFloatModel + strict_float + Input should be a valid number [type=float_type, input_value='1.0', input_type=str] + ''' +``` +""" +FiniteFloat = Annotated[float, AllowInfNan(False)] +"""A float that must be finite (not ``-inf``, ``inf``, or ``nan``). + +```py +from pydantic import BaseModel, FiniteFloat + +class Model(BaseModel): + finite: FiniteFloat + +m = Model(finite=1.0) +print(m) +#> finite=1.0 +``` +""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def conbytes( + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, +) -> type[bytes]: + """A wrapper around `bytes` that allows for additional constraints. + + Args: + min_length: The minimum length of the bytes. + max_length: The maximum length of the bytes. + strict: Whether to validate the bytes in strict mode. + + Returns: + The wrapped bytes type. + """ + return Annotated[ + bytes, + Strict(strict) if strict is not None else None, + annotated_types.Len(min_length or 0, max_length), + ] + + +StrictBytes = Annotated[bytes, Strict()] +"""A bytes that must be validated in strict mode.""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass(frozen=True) +class StringConstraints(annotated_types.GroupedMetadata): + """Usage docs: https://docs.pydantic.dev/2.7/concepts/fields/#string-constraints + + Apply constraints to `str` types. + + Attributes: + strip_whitespace: Whether to strip whitespace from the string. + to_upper: Whether to convert the string to uppercase. + to_lower: Whether to convert the string to lowercase. + strict: Whether to validate the string in strict mode. + min_length: The minimum length of the string. + max_length: The maximum length of the string. + pattern: A regex pattern that the string must match. + """ + + strip_whitespace: bool | None = None + to_upper: bool | None = None + to_lower: bool | None = None + strict: bool | None = None + min_length: int | None = None + max_length: int | None = None + pattern: str | Pattern[str] | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + if self.min_length is not None: + yield MinLen(self.min_length) + if self.max_length is not None: + yield MaxLen(self.max_length) + if self.strict is not None: + yield Strict() + if ( + self.strip_whitespace is not None + or self.pattern is not None + or self.to_lower is not None + or self.to_upper is not None + ): + yield _fields.pydantic_general_metadata( + strip_whitespace=self.strip_whitespace, + to_upper=self.to_upper, + to_lower=self.to_lower, + pattern=self.pattern.pattern if isinstance(self.pattern, Pattern) else self.pattern, + ) + + +def constr( + *, + strip_whitespace: bool | None = None, + to_upper: bool | None = None, + to_lower: bool | None = None, + strict: bool | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | Pattern[str] | None = None, +) -> type[str]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`StringConstraints`][pydantic.types.StringConstraints] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `constr` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```py + from pydantic import BaseModel, constr + + class Foo(BaseModel): + bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$') + ``` + + === ":white_check_mark: Do this" + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, StringConstraints + + class Foo(BaseModel): + bar: Annotated[str, StringConstraints(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$')] + ``` + + A wrapper around `str` that allows for additional constraints. + + ```py + from pydantic import BaseModel, constr + + class Foo(BaseModel): + bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$') + + + foo = Foo(bar=' hello ') + print(foo) + #> bar='HELLO' + ``` + + Args: + strip_whitespace: Whether to remove leading and trailing whitespace. + to_upper: Whether to turn all characters to uppercase. + to_lower: Whether to turn all characters to lowercase. + strict: Whether to validate the string in strict mode. + min_length: The minimum length of the string. + max_length: The maximum length of the string. + pattern: A regex pattern to validate the string against. + + Returns: + The wrapped string type. + """ # noqa: D212 + return Annotated[ + str, + StringConstraints( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + strict=strict, + min_length=min_length, + max_length=max_length, + pattern=pattern, + ), + ] + + +StrictStr = Annotated[str, Strict()] +"""A string that must be validated in strict mode.""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLLECTION TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +HashableItemType = TypeVar('HashableItemType', bound=Hashable) + + +def conset( + item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None +) -> type[set[HashableItemType]]: + """A wrapper around `typing.Set` that allows for additional constraints. + + Args: + item_type: The type of the items in the set. + min_length: The minimum length of the set. + max_length: The maximum length of the set. + + Returns: + The wrapped set type. + """ + return Annotated[Set[item_type], annotated_types.Len(min_length or 0, max_length)] + + +def confrozenset( + item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None +) -> type[frozenset[HashableItemType]]: + """A wrapper around `typing.FrozenSet` that allows for additional constraints. + + Args: + item_type: The type of the items in the frozenset. + min_length: The minimum length of the frozenset. + max_length: The maximum length of the frozenset. + + Returns: + The wrapped frozenset type. + """ + return Annotated[FrozenSet[item_type], annotated_types.Len(min_length or 0, max_length)] + + +AnyItemType = TypeVar('AnyItemType') + + +def conlist( + item_type: type[AnyItemType], + *, + min_length: int | None = None, + max_length: int | None = None, + unique_items: bool | None = None, +) -> type[list[AnyItemType]]: + """A wrapper around typing.List that adds validation. + + Args: + item_type: The type of the items in the list. + min_length: The minimum length of the list. Defaults to None. + max_length: The maximum length of the list. Defaults to None. + unique_items: Whether the items in the list must be unique. Defaults to None. + !!! warning Deprecated + The `unique_items` parameter is deprecated, use `Set` instead. + See [this issue](https://github.com/pydantic/pydantic-core/issues/296) for more details. + + Returns: + The wrapped list type. + """ + if unique_items is not None: + raise PydanticUserError( + ( + '`unique_items` is removed, use `Set` instead' + '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)' + ), + code='removed-kwargs', + ) + return Annotated[List[item_type], annotated_types.Len(min_length or 0, max_length)] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT STRING TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AnyType = TypeVar('AnyType') +if TYPE_CHECKING: + ImportString = Annotated[AnyType, ...] +else: + + class ImportString: + """A type that can be used to import a type from a string. + + `ImportString` expects a string and loads the Python object importable at that dotted path. + Attributes of modules may be separated from the module by `:` or `.`, e.g. if `'math:cos'` was provided, + the resulting field value would be the function`cos`. If a `.` is used and both an attribute and submodule + are present at the same path, the module will be preferred. + + On model instantiation, pointers will be evaluated and imported. There is + some nuance to this behavior, demonstrated in the examples below. + + **Good behavior:** + ```py + from math import cos + + from pydantic import BaseModel, Field, ImportString, ValidationError + + + class ImportThings(BaseModel): + obj: ImportString + + + # A string value will cause an automatic import + my_cos = ImportThings(obj='math.cos') + + # You can use the imported function as you would expect + cos_of_0 = my_cos.obj(0) + assert cos_of_0 == 1 + + + # A string whose value cannot be imported will raise an error + try: + ImportThings(obj='foo.bar') + except ValidationError as e: + print(e) + ''' + 1 validation error for ImportThings + obj + Invalid python path: No module named 'foo.bar' [type=import_error, input_value='foo.bar', input_type=str] + ''' + + + # Actual python objects can be assigned as well + my_cos = ImportThings(obj=cos) + my_cos_2 = ImportThings(obj='math.cos') + my_cos_3 = ImportThings(obj='math:cos') + assert my_cos == my_cos_2 == my_cos_3 + + + # You can set default field value either as Python object: + class ImportThingsDefaultPyObj(BaseModel): + obj: ImportString = math.cos + + + # or as a string value (but only if used with `validate_default=True`) + class ImportThingsDefaultString(BaseModel): + obj: ImportString = Field(default='math.cos', validate_default=True) + + + my_cos_default1 = ImportThingsDefaultPyObj() + my_cos_default2 = ImportThingsDefaultString() + assert my_cos_default1.obj == my_cos_default2.obj == math.cos + + + # note: this will not work! + class ImportThingsMissingValidateDefault(BaseModel): + obj: ImportString = 'math.cos' + + my_cos_default3 = ImportThingsMissingValidateDefault() + assert my_cos_default3.obj == 'math.cos' # just string, not evaluated + ``` + + Serializing an `ImportString` type to json is also possible. + + ```py + from pydantic import BaseModel, ImportString + + + class ImportThings(BaseModel): + obj: ImportString + + + # Create an instance + m = ImportThings(obj='math.cos') + print(m) + #> obj= + print(m.model_dump_json()) + #> {"obj":"math.cos"} + ``` + """ + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + serializer = core_schema.plain_serializer_function_ser_schema(cls._serialize, when_used='json') + if cls is source: + # Treat bare usage of ImportString (`schema is None`) as the same as ImportString[Any] + return core_schema.no_info_plain_validator_function( + function=_validators.import_string, serialization=serializer + ) + else: + return core_schema.no_info_before_validator_function( + function=_validators.import_string, schema=handler(source), serialization=serializer + ) + + @staticmethod + def _serialize(v: Any) -> str: + if isinstance(v, ModuleType): + return v.__name__ + elif hasattr(v, '__module__') and hasattr(v, '__name__'): + return f'{v.__module__}.{v.__name__}' + else: + return v + + def __repr__(self) -> str: + return 'ImportString' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def condecimal( + *, + strict: bool | None = None, + gt: int | Decimal | None = None, + ge: int | Decimal | None = None, + lt: int | Decimal | None = None, + le: int | Decimal | None = None, + multiple_of: int | Decimal | None = None, + max_digits: int | None = None, + decimal_places: int | None = None, + allow_inf_nan: bool | None = None, +) -> type[Decimal]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `condecimal` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```py + from pydantic import BaseModel, condecimal + + class Foo(BaseModel): + bar: condecimal(strict=True, allow_inf_nan=True) + ``` + + === ":white_check_mark: Do this" + ```py + from decimal import Decimal + + from typing_extensions import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[Decimal, Field(strict=True, allow_inf_nan=True)] + ``` + + A wrapper around Decimal that adds validation. + + Args: + strict: Whether to validate the value in strict mode. Defaults to `None`. + gt: The value must be greater than this. Defaults to `None`. + ge: The value must be greater than or equal to this. Defaults to `None`. + lt: The value must be less than this. Defaults to `None`. + le: The value must be less than or equal to this. Defaults to `None`. + multiple_of: The value must be a multiple of this. Defaults to `None`. + max_digits: The maximum number of digits. Defaults to `None`. + decimal_places: The number of decimal places. Defaults to `None`. + allow_inf_nan: Whether to allow infinity and NaN. Defaults to `None`. + + ```py + from decimal import Decimal + + from pydantic import BaseModel, ValidationError, condecimal + + class ConstrainedExample(BaseModel): + constrained_decimal: condecimal(gt=Decimal('1.0')) + + m = ConstrainedExample(constrained_decimal=Decimal('1.1')) + print(repr(m)) + #> ConstrainedExample(constrained_decimal=Decimal('1.1')) + + try: + ConstrainedExample(constrained_decimal=Decimal('0.9')) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_decimal',), + 'msg': 'Input should be greater than 1.0', + 'input': Decimal('0.9'), + 'ctx': {'gt': Decimal('1.0')}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + """ # noqa: D212 + return Annotated[ + Decimal, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + _fields.pydantic_general_metadata(max_digits=max_digits, decimal_places=decimal_places), + AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None, + ] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class UuidVersion: + """A field metadata class to indicate a [UUID](https://docs.python.org/3/library/uuid.html) version.""" + + uuid_version: Literal[1, 3, 4, 5] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.pop('anyOf', None) # remove the bytes/str union + field_schema.update(type='string', format=f'uuid{self.uuid_version}') + return field_schema + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + if isinstance(self, source): + # used directly as a type + return core_schema.uuid_schema(version=self.uuid_version) + else: + # update existing schema with self.uuid_version + schema = handler(source) + _check_annotated_type(schema['type'], 'uuid', self.__class__.__name__) + schema['version'] = self.uuid_version # type: ignore + return schema + + def __hash__(self) -> int: + return hash(type(self.uuid_version)) + + +UUID1 = Annotated[UUID, UuidVersion(1)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 1. + +```py +import uuid + +from pydantic import UUID1, BaseModel + +class Model(BaseModel): + uuid1: UUID1 + +Model(uuid1=uuid.uuid1()) +``` +""" +UUID3 = Annotated[UUID, UuidVersion(3)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 3. + +```py +import uuid + +from pydantic import UUID3, BaseModel + +class Model(BaseModel): + uuid3: UUID3 + +Model(uuid3=uuid.uuid3(uuid.NAMESPACE_DNS, 'pydantic.org')) +``` +""" +UUID4 = Annotated[UUID, UuidVersion(4)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 4. + +```py +import uuid + +from pydantic import UUID4, BaseModel + +class Model(BaseModel): + uuid4: UUID4 + +Model(uuid4=uuid.uuid4()) +``` +""" +UUID5 = Annotated[UUID, UuidVersion(5)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 5. + +```py +import uuid + +from pydantic import UUID5, BaseModel + +class Model(BaseModel): + uuid5: UUID5 + +Model(uuid5=uuid.uuid5(uuid.NAMESPACE_DNS, 'pydantic.org')) +``` +""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass +class PathType: + path_type: Literal['file', 'dir', 'new'] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + format_conversion = {'file': 'file-path', 'dir': 'directory-path'} + field_schema.update(format=format_conversion.get(self.path_type, 'path'), type='string') + return field_schema + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + function_lookup = { + 'file': cast(core_schema.WithInfoValidatorFunction, self.validate_file), + 'dir': cast(core_schema.WithInfoValidatorFunction, self.validate_directory), + 'new': cast(core_schema.WithInfoValidatorFunction, self.validate_new), + } + + return core_schema.with_info_after_validator_function( + function_lookup[self.path_type], + handler(source), + ) + + @staticmethod + def validate_file(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.is_file(): + return path + else: + raise PydanticCustomError('path_not_file', 'Path does not point to a file') + + @staticmethod + def validate_directory(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.is_dir(): + return path + else: + raise PydanticCustomError('path_not_directory', 'Path does not point to a directory') + + @staticmethod + def validate_new(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.exists(): + raise PydanticCustomError('path_exists', 'Path already exists') + elif not path.parent.exists(): + raise PydanticCustomError('parent_does_not_exist', 'Parent directory does not exist') + else: + return path + + def __hash__(self) -> int: + return hash(type(self.path_type)) + + +FilePath = Annotated[Path, PathType('file')] +"""A path that must point to a file. + +```py +from pathlib import Path + +from pydantic import BaseModel, FilePath, ValidationError + +class Model(BaseModel): + f: FilePath + +path = Path('text.txt') +path.touch() +m = Model(f='text.txt') +print(m.model_dump()) +#> {'f': PosixPath('text.txt')} +path.unlink() + +path = Path('directory') +path.mkdir(exist_ok=True) +try: + Model(f='directory') # directory +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a file [type=path_not_file, input_value='directory', input_type=str] + ''' +path.rmdir() + +try: + Model(f='not-exists-file') +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a file [type=path_not_file, input_value='not-exists-file', input_type=str] + ''' +``` +""" +DirectoryPath = Annotated[Path, PathType('dir')] +"""A path that must point to a directory. + +```py +from pathlib import Path + +from pydantic import BaseModel, DirectoryPath, ValidationError + +class Model(BaseModel): + f: DirectoryPath + +path = Path('directory/') +path.mkdir() +m = Model(f='directory/') +print(m.model_dump()) +#> {'f': PosixPath('directory')} +path.rmdir() + +path = Path('file.txt') +path.touch() +try: + Model(f='file.txt') # file +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a directory [type=path_not_directory, input_value='file.txt', input_type=str] + ''' +path.unlink() + +try: + Model(f='not-exists-directory') +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a directory [type=path_not_directory, input_value='not-exists-directory', input_type=str] + ''' +``` +""" +NewPath = Annotated[Path, PathType('new')] +"""A path for a new file or directory that must not already exist.""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + # Json[list[str]] will be recognized by type checkers as list[str] + Json = Annotated[AnyType, ...] + +else: + + class Json: + """A special type wrapper which loads JSON before parsing. + + You can use the `Json` data type to make Pydantic first load a raw JSON string before + validating the loaded data into the parametrized type: + + ```py + from typing import Any, List + + from pydantic import BaseModel, Json, ValidationError + + + class AnyJsonModel(BaseModel): + json_obj: Json[Any] + + + class ConstrainedJsonModel(BaseModel): + json_obj: Json[List[int]] + + + print(AnyJsonModel(json_obj='{"b": 1}')) + #> json_obj={'b': 1} + print(ConstrainedJsonModel(json_obj='[1, 2, 3]')) + #> json_obj=[1, 2, 3] + + try: + ConstrainedJsonModel(json_obj=12) + except ValidationError as e: + print(e) + ''' + 1 validation error for ConstrainedJsonModel + json_obj + JSON input should be string, bytes or bytearray [type=json_type, input_value=12, input_type=int] + ''' + + try: + ConstrainedJsonModel(json_obj='[a, b]') + except ValidationError as e: + print(e) + ''' + 1 validation error for ConstrainedJsonModel + json_obj + Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value='[a, b]', input_type=str] + ''' + + try: + ConstrainedJsonModel(json_obj='["a", "b"]') + except ValidationError as e: + print(e) + ''' + 2 validation errors for ConstrainedJsonModel + json_obj.0 + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] + json_obj.1 + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str] + ''' + ``` + + When you dump the model using `model_dump` or `model_dump_json`, the dumped value will be the result of validation, + not the original JSON string. However, you can use the argument `round_trip=True` to get the original JSON string back: + + ```py + from typing import List + + from pydantic import BaseModel, Json + + + class ConstrainedJsonModel(BaseModel): + json_obj: Json[List[int]] + + + print(ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json()) + #> {"json_obj":[1,2,3]} + print( + ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json(round_trip=True) + ) + #> {"json_obj":"[1,2,3]"} + ``` + """ + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + if cls is source: + return core_schema.json_schema(None) + else: + return core_schema.json_schema(handler(source)) + + def __repr__(self) -> str: + return 'Json' + + def __hash__(self) -> int: + return hash(type(self)) + + def __eq__(self, other: Any) -> bool: + return type(other) == type(self) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SecretType = TypeVar('SecretType') + + +class _SecretBase(Generic[SecretType]): + def __init__(self, secret_value: SecretType) -> None: + self._secret_value: SecretType = secret_value + + def get_secret_value(self) -> SecretType: + """Get the secret value. + + Returns: + The secret value. + """ + return self._secret_value + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() + + def __hash__(self) -> int: + return hash(self.get_secret_value()) + + def __str__(self) -> str: + return str(self._display()) + + def __repr__(self) -> str: + return f'{self.__class__.__name__}({self._display()!r})' + + def _display(self) -> str | bytes: + raise NotImplementedError + + +class Secret(_SecretBase[SecretType]): + """A generic base class used for defining a field with sensitive information that you do not want to be visible in logging or tracebacks. + + You may either directly parametrize `Secret` with a type, or subclass from `Secret` with a parametrized type. The benefit of subclassing + is that you can define a custom `_display` method, which will be used for `repr()` and `str()` methods. The examples below demonstrate both + ways of using `Secret` to create a new secret type. + + 1. Directly parametrizing `Secret` with a type: + + ```py + from pydantic import BaseModel, Secret + + SecretBool = Secret[bool] + + class Model(BaseModel): + secret_bool: SecretBool + + m = Model(secret_bool=True) + print(m.model_dump()) + #> {'secret_bool': Secret('**********')} + + print(m.model_dump_json()) + #> {"secret_bool":"**********"} + + print(m.secret_bool.get_secret_value()) + #> True + ``` + + 2. Subclassing from parametrized `Secret`: + + ```py + from datetime import date + + from pydantic import BaseModel, Secret + + class SecretDate(Secret[date]): + def _display(self) -> str: + return '****/**/**' + + class Model(BaseModel): + secret_date: SecretDate + + m = Model(secret_date=date(2022, 1, 1)) + print(m.model_dump()) + #> {'secret_date': SecretDate('****/**/**')} + + print(m.model_dump_json()) + #> {"secret_date":"****/**/**"} + + print(m.secret_date.get_secret_value()) + #> 2022-01-01 + ``` + + The value returned by the `_display` method will be used for `repr()` and `str()`. + """ + + def _display(self) -> str | bytes: + return '**********' if self.get_secret_value() else '' + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + inner_type = None + # if origin_type is Secret, then cls is a GenericAlias, and we can extract the inner type directly + origin_type = get_origin(source) + if origin_type is not None: + inner_type = get_args(source)[0] + # otherwise, we need to get the inner type from the base class + else: + bases = getattr(cls, '__orig_bases__', getattr(cls, '__bases__', [])) + for base in bases: + if get_origin(base) is Secret: + inner_type = get_args(base)[0] + if bases == [] or inner_type is None: + raise TypeError( + f"Can't get secret type from {cls.__name__}. " + 'Please use Secret[], or subclass from Secret[] instead.' + ) + + inner_schema = handler.generate_schema(inner_type) # type: ignore + + def validate_secret_value(value, handler) -> Secret[SecretType]: + if isinstance(value, Secret): + value = value.get_secret_value() + validated_inner = handler(value) + return cls(validated_inner) + + def serialize(value: Secret[SecretType], info: core_schema.SerializationInfo) -> str | Secret[SecretType]: + if info.mode == 'json': + return str(value) + else: + return value + + return core_schema.json_or_python_schema( + python_schema=core_schema.no_info_wrap_validator_function( + validate_secret_value, + inner_schema, + ), + json_schema=core_schema.no_info_after_validator_function(lambda x: cls(x), inner_schema), + serialization=core_schema.plain_serializer_function_ser_schema( + serialize, + info_arg=True, + when_used='always', + ), + ) + + +def _secret_display(value: SecretType) -> str: # type: ignore + return '**********' if value else '' + + +class _SecretField(_SecretBase[SecretType]): + _inner_schema: ClassVar[CoreSchema] + _error_kind: ClassVar[str] + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + def serialize( + value: _SecretField[SecretType], info: core_schema.SerializationInfo + ) -> str | _SecretField[SecretType]: + if info.mode == 'json': + # we want the output to always be string without the `b'` prefix for bytes, + # hence we just use `secret_display` + return _secret_display(value.get_secret_value()) + else: + return value + + def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(cls._inner_schema) + _utils.update_not_none( + json_schema, + type='string', + writeOnly=True, + format='password', + ) + return json_schema + + json_schema = core_schema.no_info_after_validator_function( + source, # construct the type + cls._inner_schema, + ) + + def get_secret_schema(strict: bool) -> CoreSchema: + return core_schema.json_or_python_schema( + python_schema=core_schema.union_schema( + [ + core_schema.is_instance_schema(source), + json_schema, + ], + custom_error_type=cls._error_kind, + strict=strict, + ), + json_schema=json_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + serialize, + info_arg=True, + return_schema=core_schema.str_schema(), + when_used='json', + ), + ) + + return core_schema.lax_or_strict_schema( + lax_schema=get_secret_schema(strict=False), + strict_schema=get_secret_schema(strict=True), + metadata={'pydantic_js_functions': [get_json_schema]}, + ) + + +class SecretStr(_SecretField[str]): + """A string used for storing sensitive information that you do not want to be visible in logging or tracebacks. + + When the secret value is nonempty, it is displayed as `'**********'` instead of the underlying value in + calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `''`. + + ```py + from pydantic import BaseModel, SecretStr + + class User(BaseModel): + username: str + password: SecretStr + + user = User(username='scolvin', password='password1') + + print(user) + #> username='scolvin' password=SecretStr('**********') + print(user.password.get_secret_value()) + #> password1 + print((SecretStr('password'), SecretStr(''))) + #> (SecretStr('**********'), SecretStr('')) + ``` + """ + + _inner_schema: ClassVar[CoreSchema] = core_schema.str_schema() + _error_kind: ClassVar[str] = 'string_type' + + def __len__(self) -> int: + return len(self._secret_value) + + def _display(self) -> str: + return _secret_display(self._secret_value) + + +class SecretBytes(_SecretField[bytes]): + """A bytes used for storing sensitive information that you do not want to be visible in logging or tracebacks. + + It displays `b'**********'` instead of the string value on `repr()` and `str()` calls. + When the secret value is nonempty, it is displayed as `b'**********'` instead of the underlying value in + calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `b''`. + + ```py + from pydantic import BaseModel, SecretBytes + + class User(BaseModel): + username: str + password: SecretBytes + + user = User(username='scolvin', password=b'password1') + #> username='scolvin' password=SecretBytes(b'**********') + print(user.password.get_secret_value()) + #> b'password1' + print((SecretBytes(b'password'), SecretBytes(b''))) + #> (SecretBytes(b'**********'), SecretBytes(b'')) + ``` + """ + + _inner_schema: ClassVar[CoreSchema] = core_schema.bytes_schema() + _error_kind: ClassVar[str] = 'bytes_type' + + def __len__(self) -> int: + return len(self._secret_value) + + def _display(self) -> bytes: + return _secret_display(self._secret_value).encode() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class PaymentCardBrand(str, Enum): + amex = 'American Express' + mastercard = 'Mastercard' + visa = 'Visa' + other = 'other' + + def __str__(self) -> str: + return self.value + + +@deprecated( + 'The `PaymentCardNumber` class is deprecated, use `pydantic_extra_types` instead. ' + 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_payment/#pydantic_extra_types.payment.PaymentCardNumber.', + category=PydanticDeprecatedSince20, +) +class PaymentCardNumber(str): + """Based on: https://en.wikipedia.org/wiki/Payment_card_number.""" + + strip_whitespace: ClassVar[bool] = True + min_length: ClassVar[int] = 12 + max_length: ClassVar[int] = 19 + bin: str + last4: str + brand: PaymentCardBrand + + def __init__(self, card_number: str): + self.validate_digits(card_number) + + card_number = self.validate_luhn_check_digit(card_number) + + self.bin = card_number[:6] + self.last4 = card_number[-4:] + self.brand = self.validate_brand(card_number) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + cls.validate, + core_schema.str_schema( + min_length=cls.min_length, max_length=cls.max_length, strip_whitespace=cls.strip_whitespace + ), + ) + + @classmethod + def validate(cls, input_value: str, /, _: core_schema.ValidationInfo) -> PaymentCardNumber: + """Validate the card number and return a `PaymentCardNumber` instance.""" + return cls(input_value) + + @property + def masked(self) -> str: + """Mask all but the last 4 digits of the card number. + + Returns: + A masked card number string. + """ + num_masked = len(self) - 10 # len(bin) + len(last4) == 10 + return f'{self.bin}{"*" * num_masked}{self.last4}' + + @classmethod + def validate_digits(cls, card_number: str) -> None: + """Validate that the card number is all digits.""" + if not card_number.isdigit(): + raise PydanticCustomError('payment_card_number_digits', 'Card number is not all digits') + + @classmethod + def validate_luhn_check_digit(cls, card_number: str) -> str: + """Based on: https://en.wikipedia.org/wiki/Luhn_algorithm.""" + sum_ = int(card_number[-1]) + length = len(card_number) + parity = length % 2 + for i in range(length - 1): + digit = int(card_number[i]) + if i % 2 == parity: + digit *= 2 + if digit > 9: + digit -= 9 + sum_ += digit + valid = sum_ % 10 == 0 + if not valid: + raise PydanticCustomError('payment_card_number_luhn', 'Card number is not luhn valid') + return card_number + + @staticmethod + def validate_brand(card_number: str) -> PaymentCardBrand: + """Validate length based on BIN for major brands: + https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN). + """ + if card_number[0] == '4': + brand = PaymentCardBrand.visa + elif 51 <= int(card_number[:2]) <= 55: + brand = PaymentCardBrand.mastercard + elif card_number[:2] in {'34', '37'}: + brand = PaymentCardBrand.amex + else: + brand = PaymentCardBrand.other + + required_length: None | int | str = None + if brand in PaymentCardBrand.mastercard: + required_length = 16 + valid = len(card_number) == required_length + elif brand == PaymentCardBrand.visa: + required_length = '13, 16 or 19' + valid = len(card_number) in {13, 16, 19} + elif brand == PaymentCardBrand.amex: + required_length = 15 + valid = len(card_number) == required_length + else: + valid = True + + if not valid: + raise PydanticCustomError( + 'payment_card_number_brand', + 'Length for a {brand} card must be {required_length}', + {'brand': brand, 'required_length': required_length}, + ) + return brand + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ByteSize(int): + """Converts a string representing a number of bytes with units (such as `'1KB'` or `'11.5MiB'`) into an integer. + + You can use the `ByteSize` data type to (case-insensitively) convert a string representation of a number of bytes into + an integer, and also to print out human-readable strings representing a number of bytes. + + In conformance with [IEC 80000-13 Standard](https://en.wikipedia.org/wiki/ISO/IEC_80000) we interpret `'1KB'` to mean 1000 bytes, + and `'1KiB'` to mean 1024 bytes. In general, including a middle `'i'` will cause the unit to be interpreted as a power of 2, + rather than a power of 10 (so, for example, `'1 MB'` is treated as `1_000_000` bytes, whereas `'1 MiB'` is treated as `1_048_576` bytes). + + !!! info + Note that `1b` will be parsed as "1 byte" and not "1 bit". + + ```py + from pydantic import BaseModel, ByteSize + + class MyModel(BaseModel): + size: ByteSize + + print(MyModel(size=52000).size) + #> 52000 + print(MyModel(size='3000 KiB').size) + #> 3072000 + + m = MyModel(size='50 PB') + print(m.size.human_readable()) + #> 44.4PiB + print(m.size.human_readable(decimal=True)) + #> 50.0PB + print(m.size.human_readable(separator=' ')) + #> 44.4 PiB + + print(m.size.to('TiB')) + #> 45474.73508864641 + ``` + """ + + byte_sizes = { + 'b': 1, + 'kb': 10**3, + 'mb': 10**6, + 'gb': 10**9, + 'tb': 10**12, + 'pb': 10**15, + 'eb': 10**18, + 'kib': 2**10, + 'mib': 2**20, + 'gib': 2**30, + 'tib': 2**40, + 'pib': 2**50, + 'eib': 2**60, + 'bit': 1 / 8, + 'kbit': 10**3 / 8, + 'mbit': 10**6 / 8, + 'gbit': 10**9 / 8, + 'tbit': 10**12 / 8, + 'pbit': 10**15 / 8, + 'ebit': 10**18 / 8, + 'kibit': 2**10 / 8, + 'mibit': 2**20 / 8, + 'gibit': 2**30 / 8, + 'tibit': 2**40 / 8, + 'pibit': 2**50 / 8, + 'eibit': 2**60 / 8, + } + byte_sizes.update({k.lower()[0]: v for k, v in byte_sizes.items() if 'i' not in k}) + + byte_string_pattern = r'^\s*(\d*\.?\d+)\s*(\w+)?' + byte_string_re = re.compile(byte_string_pattern, re.IGNORECASE) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + function=cls._validate, + schema=core_schema.union_schema( + [ + core_schema.str_schema(pattern=cls.byte_string_pattern), + core_schema.int_schema(ge=0), + ], + custom_error_type='byte_size', + custom_error_message='could not parse value and unit from byte string', + ), + serialization=core_schema.plain_serializer_function_ser_schema( + int, return_schema=core_schema.int_schema(ge=0) + ), + ) + + @classmethod + def _validate(cls, input_value: Any, /, _: core_schema.ValidationInfo) -> ByteSize: + try: + return cls(int(input_value)) + except ValueError: + pass + + str_match = cls.byte_string_re.match(str(input_value)) + if str_match is None: + raise PydanticCustomError('byte_size', 'could not parse value and unit from byte string') + + scalar, unit = str_match.groups() + if unit is None: + unit = 'b' + + try: + unit_mult = cls.byte_sizes[unit.lower()] + except KeyError: + raise PydanticCustomError('byte_size_unit', 'could not interpret byte unit: {unit}', {'unit': unit}) + + return cls(int(float(scalar) * unit_mult)) + + def human_readable(self, decimal: bool = False, separator: str = '') -> str: + """Converts a byte size to a human readable string. + + Args: + decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units + (e.g. 1024 bytes per KiB). + separator: A string used to split the value and unit. Defaults to an empty string (''). + + Returns: + A human readable string representation of the byte size. + """ + if decimal: + divisor = 1000 + units = 'B', 'KB', 'MB', 'GB', 'TB', 'PB' + final_unit = 'EB' + else: + divisor = 1024 + units = 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB' + final_unit = 'EiB' + + num = float(self) + for unit in units: + if abs(num) < divisor: + if unit == 'B': + return f'{num:0.0f}{separator}{unit}' + else: + return f'{num:0.1f}{separator}{unit}' + num /= divisor + + return f'{num:0.1f}{separator}{final_unit}' + + def to(self, unit: str) -> float: + """Converts a byte size to another unit, including both byte and bit units. + + Args: + unit: The unit to convert to. Must be one of the following: B, KB, MB, GB, TB, PB, EB, + KiB, MiB, GiB, TiB, PiB, EiB (byte units) and + bit, kbit, mbit, gbit, tbit, pbit, ebit, + kibit, mibit, gibit, tibit, pibit, eibit (bit units). + + Returns: + The byte size in the new unit. + """ + try: + unit_div = self.byte_sizes[unit.lower()] + except KeyError: + raise PydanticCustomError('byte_size_unit', 'Could not interpret byte unit: {unit}', {'unit': unit}) + + return self / unit_div + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def _check_annotated_type(annotated_type: str, expected_type: str, annotation: str) -> None: + if annotated_type != expected_type: + raise PydanticUserError(f"'{annotation}' cannot annotate '{annotated_type}'.", code='invalid_annotated_type') + + +if TYPE_CHECKING: + PastDate = Annotated[date, ...] + FutureDate = Annotated[date, ...] +else: + + class PastDate: + """A date in the past.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.date_schema(now_op='past') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'date', cls.__name__) + schema['now_op'] = 'past' + return schema + + def __repr__(self) -> str: + return 'PastDate' + + class FutureDate: + """A date in the future.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.date_schema(now_op='future') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'date', cls.__name__) + schema['now_op'] = 'future' + return schema + + def __repr__(self) -> str: + return 'FutureDate' + + +def condate( + *, + strict: bool | None = None, + gt: date | None = None, + ge: date | None = None, + lt: date | None = None, + le: date | None = None, +) -> type[date]: + """A wrapper for date that adds constraints. + + Args: + strict: Whether to validate the date value in strict mode. Defaults to `None`. + gt: The value must be greater than this. Defaults to `None`. + ge: The value must be greater than or equal to this. Defaults to `None`. + lt: The value must be less than this. Defaults to `None`. + le: The value must be less than or equal to this. Defaults to `None`. + + Returns: + A date type with the specified constraints. + """ + return Annotated[ + date, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + ] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATETIME TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + AwareDatetime = Annotated[datetime, ...] + NaiveDatetime = Annotated[datetime, ...] + PastDatetime = Annotated[datetime, ...] + FutureDatetime = Annotated[datetime, ...] + +else: + + class AwareDatetime: + """A datetime that requires timezone info.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(tz_constraint='aware') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['tz_constraint'] = 'aware' + return schema + + def __repr__(self) -> str: + return 'AwareDatetime' + + class NaiveDatetime: + """A datetime that doesn't require timezone info.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(tz_constraint='naive') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['tz_constraint'] = 'naive' + return schema + + def __repr__(self) -> str: + return 'NaiveDatetime' + + class PastDatetime: + """A datetime that must be in the past.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(now_op='past') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['now_op'] = 'past' + return schema + + def __repr__(self) -> str: + return 'PastDatetime' + + class FutureDatetime: + """A datetime that must be in the future.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(now_op='future') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['now_op'] = 'future' + return schema + + def __repr__(self) -> str: + return 'FutureDatetime' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Encoded TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class EncoderProtocol(Protocol): + """Protocol for encoding and decoding data to and from bytes.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data using the encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + ... + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data using the encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + ... + + @classmethod + def get_json_format(cls) -> str: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + ... + + +class Base64Encoder(EncoderProtocol): + """Standard (non-URL-safe) Base64 encoder.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data from base64 encoded bytes to original bytes data. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + try: + return base64.decodebytes(data) + except ValueError as e: + raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data from bytes to a base64 encoded bytes. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return base64.encodebytes(value) + + @classmethod + def get_json_format(cls) -> Literal['base64']: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + return 'base64' + + +class Base64UrlEncoder(EncoderProtocol): + """URL-safe Base64 encoder.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data from base64 encoded bytes to original bytes data. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + try: + return base64.urlsafe_b64decode(data) + except ValueError as e: + raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data from bytes to a base64 encoded bytes. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return base64.urlsafe_b64encode(value) + + @classmethod + def get_json_format(cls) -> Literal['base64url']: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + return 'base64url' + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class EncodedBytes: + """A bytes type that is encoded and decoded using the specified encoder. + + `EncodedBytes` needs an encoder that implements `EncoderProtocol` to operate. + + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, EncodedBytes, EncoderProtocol, ValidationError + + class MyEncoder(EncoderProtocol): + @classmethod + def decode(cls, data: bytes) -> bytes: + if data == b'**undecodable**': + raise ValueError('Cannot decode data') + return data[13:] + + @classmethod + def encode(cls, value: bytes) -> bytes: + return b'**encoded**: ' + value + + @classmethod + def get_json_format(cls) -> str: + return 'my-encoder' + + MyEncodedBytes = Annotated[bytes, EncodedBytes(encoder=MyEncoder)] + + class Model(BaseModel): + my_encoded_bytes: MyEncodedBytes + + # Initialize the model with encoded data + m = Model(my_encoded_bytes=b'**encoded**: some bytes') + + # Access decoded value + print(m.my_encoded_bytes) + #> b'some bytes' + + # Serialize into the encoded form + print(m.model_dump()) + #> {'my_encoded_bytes': b'**encoded**: some bytes'} + + # Validate encoded data + try: + Model(my_encoded_bytes=b'**undecodable**') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + my_encoded_bytes + Value error, Cannot decode data [type=value_error, input_value=b'**undecodable**', input_type=bytes] + ''' + ``` + """ + + encoder: type[EncoderProtocol] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format=self.encoder.get_json_format()) + return field_schema + + def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + function=self.decode, + schema=core_schema.bytes_schema(), + serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode), + ) + + def decode(self, data: bytes, _: core_schema.ValidationInfo) -> bytes: + """Decode the data using the specified encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + return self.encoder.decode(data) + + def encode(self, value: bytes) -> bytes: + """Encode the data using the specified encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return self.encoder.encode(value) + + def __hash__(self) -> int: + return hash(self.encoder) + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class EncodedStr(EncodedBytes): + """A str type that is encoded and decoded using the specified encoder. + + `EncodedStr` needs an encoder that implements `EncoderProtocol` to operate. + + ```py + from typing_extensions import Annotated + + from pydantic import BaseModel, EncodedStr, EncoderProtocol, ValidationError + + class MyEncoder(EncoderProtocol): + @classmethod + def decode(cls, data: bytes) -> bytes: + if data == b'**undecodable**': + raise ValueError('Cannot decode data') + return data[13:] + + @classmethod + def encode(cls, value: bytes) -> bytes: + return b'**encoded**: ' + value + + @classmethod + def get_json_format(cls) -> str: + return 'my-encoder' + + MyEncodedStr = Annotated[str, EncodedStr(encoder=MyEncoder)] + + class Model(BaseModel): + my_encoded_str: MyEncodedStr + + # Initialize the model with encoded data + m = Model(my_encoded_str='**encoded**: some str') + + # Access decoded value + print(m.my_encoded_str) + #> some str + + # Serialize into the encoded form + print(m.model_dump()) + #> {'my_encoded_str': '**encoded**: some str'} + + # Validate encoded data + try: + Model(my_encoded_str='**undecodable**') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + my_encoded_str + Value error, Cannot decode data [type=value_error, input_value='**undecodable**', input_type=str] + ''' + ``` + """ + + def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + function=self.decode_str, + schema=super(EncodedStr, self).__get_pydantic_core_schema__(source=source, handler=handler), # noqa: UP008 + serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str), + ) + + def decode_str(self, data: bytes, _: core_schema.ValidationInfo) -> str: + """Decode the data using the specified encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + return data.decode() + + def encode_str(self, value: str) -> str: + """Encode the data using the specified encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return super(EncodedStr, self).encode(value=value.encode()).decode() # noqa: UP008 + + def __hash__(self) -> int: + return hash(self.encoder) + + +Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)] +"""A bytes type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. + +Note: + Under the hood, `Base64Bytes` use standard library `base64.encodebytes` and `base64.decodebytes` functions. + + As a result, attempting to decode url-safe base64 data using the `Base64Bytes` type may fail or produce an incorrect + decoding. + +```py +from pydantic import Base64Bytes, BaseModel, ValidationError + +class Model(BaseModel): + base64_bytes: Base64Bytes + +# Initialize the model with base64 data +m = Model(base64_bytes=b'VGhpcyBpcyB0aGUgd2F5') + +# Access decoded value +print(m.base64_bytes) +#> b'This is the way' + +# Serialize into the base64 form +print(m.model_dump()) +#> {'base64_bytes': b'VGhpcyBpcyB0aGUgd2F5\n'} + +# Validate base64 data +try: + print(Model(base64_bytes=b'undecodable').base64_bytes) +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + base64_bytes + Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value=b'undecodable', input_type=bytes] + ''' +``` +""" +Base64Str = Annotated[str, EncodedStr(encoder=Base64Encoder)] +"""A str type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. + +Note: + Under the hood, `Base64Bytes` use standard library `base64.encodebytes` and `base64.decodebytes` functions. + + As a result, attempting to decode url-safe base64 data using the `Base64Str` type may fail or produce an incorrect + decoding. + +```py +from pydantic import Base64Str, BaseModel, ValidationError + +class Model(BaseModel): + base64_str: Base64Str + +# Initialize the model with base64 data +m = Model(base64_str='VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y') + +# Access decoded value +print(m.base64_str) +#> These aren't the droids you're looking for + +# Serialize into the base64 form +print(m.model_dump()) +#> {'base64_str': 'VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y\n'} + +# Validate base64 data +try: + print(Model(base64_str='undecodable').base64_str) +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + base64_str + Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value='undecodable', input_type=str] + ''' +``` +""" +Base64UrlBytes = Annotated[bytes, EncodedBytes(encoder=Base64UrlEncoder)] +"""A bytes type that is encoded and decoded using the URL-safe base64 encoder. + +Note: + Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` + functions. + + As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data + (using `'+'` and `'/'`). + +```py +from pydantic import Base64UrlBytes, BaseModel + +class Model(BaseModel): + base64url_bytes: Base64UrlBytes + +# Initialize the model with base64 data +m = Model(base64url_bytes=b'SHc_dHc-TXc==') +print(m) +#> base64url_bytes=b'Hw?tw>Mw' +``` +""" +Base64UrlStr = Annotated[str, EncodedStr(encoder=Base64UrlEncoder)] +"""A str type that is encoded and decoded using the URL-safe base64 encoder. + +Note: + Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` + functions. + + As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`). + +```py +from pydantic import Base64UrlStr, BaseModel + +class Model(BaseModel): + base64url_str: Base64UrlStr + +# Initialize the model with base64 data +m = Model(base64url_str='SHc_dHc-TXc==') +print(m) +#> base64url_str='Hw?tw>Mw' +``` +""" + + +__getattr__ = getattr_migration(__name__) + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class GetPydanticSchema: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/types/#using-getpydanticschema-to-reduce-boilerplate + + A convenience class for creating an annotation that provides pydantic custom type hooks. + + This class is intended to eliminate the need to create a custom "marker" which defines the + `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` custom hook methods. + + For example, to have a field treated by type checkers as `int`, but by pydantic as `Any`, you can do: + ```python + from typing import Any + + from typing_extensions import Annotated + + from pydantic import BaseModel, GetPydanticSchema + + HandleAsAny = GetPydanticSchema(lambda _s, h: h(Any)) + + class Model(BaseModel): + x: Annotated[int, HandleAsAny] # pydantic sees `x: Any` + + print(repr(Model(x='abc').x)) + #> 'abc' + ``` + """ + + get_pydantic_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] | None = None + get_pydantic_json_schema: Callable[[Any, GetJsonSchemaHandler], JsonSchemaValue] | None = None + + # Note: we may want to consider adding a convenience staticmethod `def for_type(type_: Any) -> GetPydanticSchema:` + # which returns `GetPydanticSchema(lambda _s, h: h(type_))` + + if not TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + + def __getattr__(self, item: str) -> Any: + """Use this rather than defining `__get_pydantic_core_schema__` etc. to reduce the number of nested calls.""" + if item == '__get_pydantic_core_schema__' and self.get_pydantic_core_schema: + return self.get_pydantic_core_schema + elif item == '__get_pydantic_json_schema__' and self.get_pydantic_json_schema: + return self.get_pydantic_json_schema + else: + return object.__getattribute__(self, item) + + __hash__ = object.__hash__ + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class Tag: + """Provides a way to specify the expected tag to use for a case of a (callable) discriminated union. + + Also provides a way to label a union case in error messages. + + When using a callable `Discriminator`, attach a `Tag` to each case in the `Union` to specify the tag that + should be used to identify that case. For example, in the below example, the `Tag` is used to specify that + if `get_discriminator_value` returns `'apple'`, the input should be validated as an `ApplePie`, and if it + returns `'pumpkin'`, the input should be validated as a `PumpkinPie`. + + The primary role of the `Tag` here is to map the return value from the callable `Discriminator` function to + the appropriate member of the `Union` in question. + + ```py + from typing import Any, Union + + from typing_extensions import Annotated, Literal + + from pydantic import BaseModel, Discriminator, Tag + + class Pie(BaseModel): + time_to_cook: int + num_ingredients: int + + class ApplePie(Pie): + fruit: Literal['apple'] = 'apple' + + class PumpkinPie(Pie): + filling: Literal['pumpkin'] = 'pumpkin' + + def get_discriminator_value(v: Any) -> str: + if isinstance(v, dict): + return v.get('fruit', v.get('filling')) + return getattr(v, 'fruit', getattr(v, 'filling', None)) + + class ThanksgivingDinner(BaseModel): + dessert: Annotated[ + Union[ + Annotated[ApplePie, Tag('apple')], + Annotated[PumpkinPie, Tag('pumpkin')], + ], + Discriminator(get_discriminator_value), + ] + + apple_variation = ThanksgivingDinner.model_validate( + {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} + ) + print(repr(apple_variation)) + ''' + ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) + ''' + + pumpkin_variation = ThanksgivingDinner.model_validate( + { + 'dessert': { + 'filling': 'pumpkin', + 'time_to_cook': 40, + 'num_ingredients': 6, + } + } + ) + print(repr(pumpkin_variation)) + ''' + ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) + ''' + ``` + + !!! note + You must specify a `Tag` for every case in a `Tag` that is associated with a + callable `Discriminator`. Failing to do so will result in a `PydanticUserError` with code + [`callable-discriminator-no-tag`](../errors/usage_errors.md#callable-discriminator-no-tag). + + See the [Discriminated Unions] concepts docs for more details on how to use `Tag`s. + + [Discriminated Unions]: ../concepts/unions.md#discriminated-unions + """ + + tag: str + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + schema = handler(source_type) + metadata = schema.setdefault('metadata', {}) + assert isinstance(metadata, dict) + metadata[_core_utils.TAGGED_UNION_TAG_KEY] = self.tag + return schema + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class Discriminator: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/unions/#discriminated-unions-with-callable-discriminator + + Provides a way to use a custom callable as the way to extract the value of a union discriminator. + + This allows you to get validation behavior like you'd get from `Field(discriminator=)`, + but without needing to have a single shared field across all the union choices. This also makes it + possible to handle unions of models and primitive types with discriminated-union-style validation errors. + Finally, this allows you to use a custom callable as the way to identify which member of a union a value + belongs to, while still seeing all the performance benefits of a discriminated union. + + Consider this example, which is much more performant with the use of `Discriminator` and thus a `TaggedUnion` + than it would be as a normal `Union`. + + ```py + from typing import Any, Union + + from typing_extensions import Annotated, Literal + + from pydantic import BaseModel, Discriminator, Tag + + class Pie(BaseModel): + time_to_cook: int + num_ingredients: int + + class ApplePie(Pie): + fruit: Literal['apple'] = 'apple' + + class PumpkinPie(Pie): + filling: Literal['pumpkin'] = 'pumpkin' + + def get_discriminator_value(v: Any) -> str: + if isinstance(v, dict): + return v.get('fruit', v.get('filling')) + return getattr(v, 'fruit', getattr(v, 'filling', None)) + + class ThanksgivingDinner(BaseModel): + dessert: Annotated[ + Union[ + Annotated[ApplePie, Tag('apple')], + Annotated[PumpkinPie, Tag('pumpkin')], + ], + Discriminator(get_discriminator_value), + ] + + apple_variation = ThanksgivingDinner.model_validate( + {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} + ) + print(repr(apple_variation)) + ''' + ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) + ''' + + pumpkin_variation = ThanksgivingDinner.model_validate( + { + 'dessert': { + 'filling': 'pumpkin', + 'time_to_cook': 40, + 'num_ingredients': 6, + } + } + ) + print(repr(pumpkin_variation)) + ''' + ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) + ''' + ``` + + See the [Discriminated Unions] concepts docs for more details on how to use `Discriminator`s. + + [Discriminated Unions]: ../concepts/unions.md#discriminated-unions + """ + + discriminator: str | Callable[[Any], Hashable] + """The callable or field name for discriminating the type in a tagged union. + + A `Callable` discriminator must extract the value of the discriminator from the input. + A `str` discriminator must be the name of a field to discriminate against. + """ + custom_error_type: str | None = None + """Type to use in [custom errors](../errors/errors.md#custom-errors) replacing the standard discriminated union + validation errors. + """ + custom_error_message: str | None = None + """Message to use in custom errors.""" + custom_error_context: dict[str, int | str | float] | None = None + """Context to use in custom errors.""" + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + origin = _typing_extra.get_origin(source_type) + if not origin or not _typing_extra.origin_is_union(origin): + raise TypeError(f'{type(self).__name__} must be used with a Union type, not {source_type}') + + if isinstance(self.discriminator, str): + from pydantic import Field + + return handler(Annotated[source_type, Field(discriminator=self.discriminator)]) + else: + original_schema = handler(source_type) + return self._convert_schema(original_schema) + + def _convert_schema(self, original_schema: core_schema.CoreSchema) -> core_schema.TaggedUnionSchema: + if original_schema['type'] != 'union': + # This likely indicates that the schema was a single-item union that was simplified. + # In this case, we do the same thing we do in + # `pydantic._internal._discriminated_union._ApplyInferredDiscriminator._apply_to_root`, namely, + # package the generated schema back into a single-item union. + original_schema = core_schema.union_schema([original_schema]) + + tagged_union_choices = {} + for i, choice in enumerate(original_schema['choices']): + tag = None + if isinstance(choice, tuple): + choice, tag = choice + metadata = choice.get('metadata') + if metadata is not None: + metadata_tag = metadata.get(_core_utils.TAGGED_UNION_TAG_KEY) + if metadata_tag is not None: + tag = metadata_tag + if tag is None: + raise PydanticUserError( + f'`Tag` not provided for choice {choice} used with `Discriminator`', + code='callable-discriminator-no-tag', + ) + tagged_union_choices[tag] = choice + + # Have to do these verbose checks to ensure falsy values ('' and {}) don't get ignored + custom_error_type = self.custom_error_type + if custom_error_type is None: + custom_error_type = original_schema.get('custom_error_type') + + custom_error_message = self.custom_error_message + if custom_error_message is None: + custom_error_message = original_schema.get('custom_error_message') + + custom_error_context = self.custom_error_context + if custom_error_context is None: + custom_error_context = original_schema.get('custom_error_context') + + custom_error_type = original_schema.get('custom_error_type') if custom_error_type is None else custom_error_type + return core_schema.tagged_union_schema( + tagged_union_choices, + self.discriminator, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + strict=original_schema.get('strict'), + ref=original_schema.get('ref'), + metadata=original_schema.get('metadata'), + serialization=original_schema.get('serialization'), + ) + + +_JSON_TYPES = {int, float, str, bool, list, dict, type(None)} + + +def _get_type_name(x: Any) -> str: + type_ = type(x) + if type_ in _JSON_TYPES: + return type_.__name__ + + # Handle proper subclasses; note we don't need to handle None or bool here + if isinstance(x, int): + return 'int' + if isinstance(x, float): + return 'float' + if isinstance(x, str): + return 'str' + if isinstance(x, list): + return 'list' + if isinstance(x, dict): + return 'dict' + + # Fail by returning the type's actual name + return getattr(type_, '__name__', '') + + +class _AllowAnyJson: + @classmethod + def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + python_schema = handler(source_type) + return core_schema.json_or_python_schema(json_schema=core_schema.any_schema(), python_schema=python_schema) + + +if TYPE_CHECKING: + # This seems to only be necessary for mypy + JsonValue: TypeAlias = Union[ + List['JsonValue'], + Dict[str, 'JsonValue'], + str, + bool, + int, + float, + None, + ] + """A `JsonValue` is used to represent a value that can be serialized to JSON. + + It may be one of: + + * `List['JsonValue']` + * `Dict[str, 'JsonValue']` + * `str` + * `bool` + * `int` + * `float` + * `None` + + The following example demonstrates how to use `JsonValue` to validate JSON data, + and what kind of errors to expect when input data is not json serializable. + + ```py + import json + + from pydantic import BaseModel, JsonValue, ValidationError + + class Model(BaseModel): + j: JsonValue + + valid_json_data = {'j': {'a': {'b': {'c': 1, 'd': [2, None]}}}} + invalid_json_data = {'j': {'a': {'b': ...}}} + + print(repr(Model.model_validate(valid_json_data))) + #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) + print(repr(Model.model_validate_json(json.dumps(valid_json_data)))) + #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) + + try: + Model.model_validate(invalid_json_data) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + j.dict.a.dict.b + input was not a valid JSON value [type=invalid-json-value, input_value=Ellipsis, input_type=ellipsis] + ''' + ``` + """ + +else: + JsonValue = TypeAliasType( + 'JsonValue', + Annotated[ + Union[ + Annotated[List['JsonValue'], Tag('list')], + Annotated[Dict[str, 'JsonValue'], Tag('dict')], + Annotated[str, Tag('str')], + Annotated[bool, Tag('bool')], + Annotated[int, Tag('int')], + Annotated[float, Tag('float')], + Annotated[None, Tag('NoneType')], + ], + Discriminator( + _get_type_name, + custom_error_type='invalid-json-value', + custom_error_message='input was not a valid JSON value', + ), + _AllowAnyJson, + ], + ) + + +class _OnErrorOmit: + @classmethod + def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + # there is no actual default value here but we use with_default_schema since it already has the on_error + # behavior implemented and it would be no more efficient to implement it on every other validator + # or as a standalone validator + return core_schema.with_default_schema(schema=handler(source_type), on_error='omit') + + +OnErrorOmit = Annotated[T, _OnErrorOmit] +""" +When used as an item in a list, the key type in a dict, optional values of a TypedDict, etc. +this annotation omits the item from the iteration if there is any error validating it. +That is, instead of a [`ValidationError`][pydantic_core.ValidationError] being propagated up and the entire iterable being discarded +any invalid items are discarded and the valid ones are returned. +""" diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/typing.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/typing.py new file mode 100644 index 0000000..f1b32ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/typing.py @@ -0,0 +1,4 @@ +"""`typing` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/utils.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/utils.py new file mode 100644 index 0000000..1619d1d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/utils.py @@ -0,0 +1,4 @@ +"""The `utils` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__init__.py new file mode 100644 index 0000000..3bf1418 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__init__.py @@ -0,0 +1,131 @@ +# flake8: noqa +from . import dataclasses +from .annotated_types import create_model_from_namedtuple, create_model_from_typeddict +from .class_validators import root_validator, validator +from .config import BaseConfig, ConfigDict, Extra +from .decorator import validate_arguments +from .env_settings import BaseSettings +from .error_wrappers import ValidationError +from .errors import * +from .fields import Field, PrivateAttr, Required +from .main import * +from .networks import * +from .parse import Protocol +from .tools import * +from .types import * +from .version import VERSION, compiled + +__version__ = VERSION + +# WARNING __all__ from .errors is not included here, it will be removed as an export here in v2 +# please use "from pydantic.errors import ..." instead +__all__ = [ + # annotated types utils + 'create_model_from_namedtuple', + 'create_model_from_typeddict', + # dataclasses + 'dataclasses', + # class_validators + 'root_validator', + 'validator', + # config + 'BaseConfig', + 'ConfigDict', + 'Extra', + # decorator + 'validate_arguments', + # env_settings + 'BaseSettings', + # error_wrappers + 'ValidationError', + # fields + 'Field', + 'Required', + # main + 'BaseModel', + 'create_model', + 'validate_model', + # network + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'HttpUrl', + 'stricturl', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'validate_email', + # parse + 'Protocol', + # tools + 'parse_file_as', + 'parse_obj_as', + 'parse_raw_as', + 'schema_of', + 'schema_json_of', + # types + 'NoneStr', + 'NoneBytes', + 'StrBytes', + 'NoneStrBytes', + 'StrictStr', + 'ConstrainedBytes', + 'conbytes', + 'ConstrainedList', + 'conlist', + 'ConstrainedSet', + 'conset', + 'ConstrainedFrozenSet', + 'confrozenset', + 'ConstrainedStr', + 'constr', + 'PyObject', + 'ConstrainedInt', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'ConstrainedFloat', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'ConstrainedDecimal', + 'condecimal', + 'ConstrainedDate', + 'condate', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'Json', + 'JsonWrapper', + 'SecretField', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'PrivateAttr', + 'ByteSize', + 'PastDate', + 'FutureDate', + # version + 'compiled', + 'VERSION', +] diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3fdaa32 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/_hypothesis_plugin.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/_hypothesis_plugin.cpython-310.pyc new file mode 100644 index 0000000..3970366 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/_hypothesis_plugin.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/annotated_types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/annotated_types.cpython-310.pyc new file mode 100644 index 0000000..72465a9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/annotated_types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/class_validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/class_validators.cpython-310.pyc new file mode 100644 index 0000000..ca590a4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/class_validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/color.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/color.cpython-310.pyc new file mode 100644 index 0000000..5b33208 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/color.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..2952a62 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/dataclasses.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/dataclasses.cpython-310.pyc new file mode 100644 index 0000000..b73b239 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/dataclasses.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/datetime_parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/datetime_parse.cpython-310.pyc new file mode 100644 index 0000000..0d717c9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/datetime_parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/decorator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/decorator.cpython-310.pyc new file mode 100644 index 0000000..5abb482 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/decorator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/env_settings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/env_settings.cpython-310.pyc new file mode 100644 index 0000000..a031e44 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/env_settings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/error_wrappers.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/error_wrappers.cpython-310.pyc new file mode 100644 index 0000000..6c3d251 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/error_wrappers.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/errors.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/errors.cpython-310.pyc new file mode 100644 index 0000000..66c41a4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/errors.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/fields.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/fields.cpython-310.pyc new file mode 100644 index 0000000..fecdfc2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/fields.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/generics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/generics.cpython-310.pyc new file mode 100644 index 0000000..05c7c08 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/generics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/json.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/json.cpython-310.pyc new file mode 100644 index 0000000..9fe6a49 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/json.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/main.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000..1ecdea1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/main.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/mypy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/mypy.cpython-310.pyc new file mode 100644 index 0000000..826c47d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/mypy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/networks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/networks.cpython-310.pyc new file mode 100644 index 0000000..27b9cfb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/networks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/parse.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/parse.cpython-310.pyc new file mode 100644 index 0000000..85c7ed4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/parse.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/schema.cpython-310.pyc new file mode 100644 index 0000000..825b432 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/tools.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/tools.cpython-310.pyc new file mode 100644 index 0000000..ff5db58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/tools.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/types.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000..7e7e15c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/types.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/typing.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/typing.cpython-310.pyc new file mode 100644 index 0000000..f3a91a0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/typing.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..4e74aa8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/v1.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/v1.cpython-310.pyc new file mode 100644 index 0000000..c899e59 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/v1.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/validators.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/validators.cpython-310.pyc new file mode 100644 index 0000000..d1fe27d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/validators.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..6d5b880 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/_hypothesis_plugin.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/_hypothesis_plugin.py new file mode 100644 index 0000000..0c52962 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/_hypothesis_plugin.py @@ -0,0 +1,391 @@ +""" +Register Hypothesis strategies for Pydantic custom types. + +This enables fully-automatic generation of test data for most Pydantic classes. + +Note that this module has *no* runtime impact on Pydantic itself; instead it +is registered as a setuptools entry point and Hypothesis will import it if +Pydantic is installed. See also: + +https://hypothesis.readthedocs.io/en/latest/strategies.html#registering-strategies-via-setuptools-entry-points +https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.register_type_strategy +https://hypothesis.readthedocs.io/en/latest/strategies.html#interaction-with-pytest-cov +https://docs.pydantic.dev/usage/types/#pydantic-types + +Note that because our motivation is to *improve user experience*, the strategies +are always sound (never generate invalid data) but sacrifice completeness for +maintainability (ie may be unable to generate some tricky but valid data). + +Finally, this module makes liberal use of `# type: ignore[]` pragmas. +This is because Hypothesis annotates `register_type_strategy()` with +`(T, SearchStrategy[T])`, but in most cases we register e.g. `ConstrainedInt` +to generate instances of the builtin `int` type which match the constraints. +""" + +import contextlib +import datetime +import ipaddress +import json +import math +from fractions import Fraction +from typing import Callable, Dict, Type, Union, cast, overload + +import hypothesis.strategies as st + +import pydantic +import pydantic.color +import pydantic.types +from pydantic.utils import lenient_issubclass + +# FilePath and DirectoryPath are explicitly unsupported, as we'd have to create +# them on-disk, and that's unsafe in general without being told *where* to do so. +# +# URLs are unsupported because it's easy for users to define their own strategy for +# "normal" URLs, and hard for us to define a general strategy which includes "weird" +# URLs but doesn't also have unpredictable performance problems. +# +# conlist() and conset() are unsupported for now, because the workarounds for +# Cython and Hypothesis to handle parametrized generic types are incompatible. +# We are rethinking Hypothesis compatibility in Pydantic v2. + +# Emails +try: + import email_validator +except ImportError: # pragma: no cover + pass +else: + + def is_valid_email(s: str) -> bool: + # Hypothesis' st.emails() occasionally generates emails like 0@A0--0.ac + # that are invalid according to email-validator, so we filter those out. + try: + email_validator.validate_email(s, check_deliverability=False) + return True + except email_validator.EmailNotValidError: # pragma: no cover + return False + + # Note that these strategies deliberately stay away from any tricky Unicode + # or other encoding issues; we're just trying to generate *something* valid. + st.register_type_strategy(pydantic.EmailStr, st.emails().filter(is_valid_email)) # type: ignore[arg-type] + st.register_type_strategy( + pydantic.NameEmail, + st.builds( + '{} <{}>'.format, # type: ignore[arg-type] + st.from_regex('[A-Za-z0-9_]+( [A-Za-z0-9_]+){0,5}', fullmatch=True), + st.emails().filter(is_valid_email), + ), + ) + +# PyObject - dotted names, in this case taken from the math module. +st.register_type_strategy( + pydantic.PyObject, # type: ignore[arg-type] + st.sampled_from( + [cast(pydantic.PyObject, f'math.{name}') for name in sorted(vars(math)) if not name.startswith('_')] + ), +) + +# CSS3 Colors; as name, hex, rgb(a) tuples or strings, or hsl strings +_color_regexes = ( + '|'.join( + ( + pydantic.color.r_hex_short, + pydantic.color.r_hex_long, + pydantic.color.r_rgb, + pydantic.color.r_rgba, + pydantic.color.r_hsl, + pydantic.color.r_hsla, + ) + ) + # Use more precise regex patterns to avoid value-out-of-range errors + .replace(pydantic.color._r_sl, r'(?:(\d\d?(?:\.\d+)?|100(?:\.0+)?)%)') + .replace(pydantic.color._r_alpha, r'(?:(0(?:\.\d+)?|1(?:\.0+)?|\.\d+|\d{1,2}%))') + .replace(pydantic.color._r_255, r'(?:((?:\d|\d\d|[01]\d\d|2[0-4]\d|25[0-4])(?:\.\d+)?|255(?:\.0+)?))') +) +st.register_type_strategy( + pydantic.color.Color, + st.one_of( + st.sampled_from(sorted(pydantic.color.COLORS_BY_NAME)), + st.tuples( + st.integers(0, 255), + st.integers(0, 255), + st.integers(0, 255), + st.none() | st.floats(0, 1) | st.floats(0, 100).map('{}%'.format), + ), + st.from_regex(_color_regexes, fullmatch=True), + ), +) + + +# Card numbers, valid according to the Luhn algorithm + + +def add_luhn_digit(card_number: str) -> str: + # See https://en.wikipedia.org/wiki/Luhn_algorithm + for digit in '0123456789': + with contextlib.suppress(Exception): + pydantic.PaymentCardNumber.validate_luhn_check_digit(card_number + digit) + return card_number + digit + raise AssertionError('Unreachable') # pragma: no cover + + +card_patterns = ( + # Note that these patterns omit the Luhn check digit; that's added by the function above + '4[0-9]{14}', # Visa + '5[12345][0-9]{13}', # Mastercard + '3[47][0-9]{12}', # American Express + '[0-26-9][0-9]{10,17}', # other (incomplete to avoid overlap) +) +st.register_type_strategy( + pydantic.PaymentCardNumber, + st.from_regex('|'.join(card_patterns), fullmatch=True).map(add_luhn_digit), # type: ignore[arg-type] +) + +# UUIDs +st.register_type_strategy(pydantic.UUID1, st.uuids(version=1)) +st.register_type_strategy(pydantic.UUID3, st.uuids(version=3)) +st.register_type_strategy(pydantic.UUID4, st.uuids(version=4)) +st.register_type_strategy(pydantic.UUID5, st.uuids(version=5)) + +# Secrets +st.register_type_strategy(pydantic.SecretBytes, st.binary().map(pydantic.SecretBytes)) +st.register_type_strategy(pydantic.SecretStr, st.text().map(pydantic.SecretStr)) + +# IP addresses, networks, and interfaces +st.register_type_strategy(pydantic.IPvAnyAddress, st.ip_addresses()) # type: ignore[arg-type] +st.register_type_strategy( + pydantic.IPvAnyInterface, + st.from_type(ipaddress.IPv4Interface) | st.from_type(ipaddress.IPv6Interface), # type: ignore[arg-type] +) +st.register_type_strategy( + pydantic.IPvAnyNetwork, + st.from_type(ipaddress.IPv4Network) | st.from_type(ipaddress.IPv6Network), # type: ignore[arg-type] +) + +# We hook into the con***() functions and the ConstrainedNumberMeta metaclass, +# so here we only have to register subclasses for other constrained types which +# don't go via those mechanisms. Then there are the registration hooks below. +st.register_type_strategy(pydantic.StrictBool, st.booleans()) +st.register_type_strategy(pydantic.StrictStr, st.text()) + + +# FutureDate, PastDate +st.register_type_strategy(pydantic.FutureDate, st.dates(min_value=datetime.date.today() + datetime.timedelta(days=1))) +st.register_type_strategy(pydantic.PastDate, st.dates(max_value=datetime.date.today() - datetime.timedelta(days=1))) + + +# Constrained-type resolver functions +# +# For these ones, we actually want to inspect the type in order to work out a +# satisfying strategy. First up, the machinery for tracking resolver functions: + +RESOLVERS: Dict[type, Callable[[type], st.SearchStrategy]] = {} # type: ignore[type-arg] + + +@overload +def _registered(typ: Type[pydantic.types.T]) -> Type[pydantic.types.T]: + pass + + +@overload +def _registered(typ: pydantic.types.ConstrainedNumberMeta) -> pydantic.types.ConstrainedNumberMeta: + pass + + +def _registered( + typ: Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta] +) -> Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta]: + # This function replaces the version in `pydantic.types`, in order to + # effect the registration of new constrained types so that Hypothesis + # can generate valid examples. + pydantic.types._DEFINED_TYPES.add(typ) + for supertype, resolver in RESOLVERS.items(): + if issubclass(typ, supertype): + st.register_type_strategy(typ, resolver(typ)) # type: ignore + return typ + raise NotImplementedError(f'Unknown type {typ!r} has no resolver to register') # pragma: no cover + + +def resolves( + typ: Union[type, pydantic.types.ConstrainedNumberMeta] +) -> Callable[[Callable[..., st.SearchStrategy]], Callable[..., st.SearchStrategy]]: # type: ignore[type-arg] + def inner(f): # type: ignore + assert f not in RESOLVERS + RESOLVERS[typ] = f + return f + + return inner + + +# Type-to-strategy resolver functions + + +@resolves(pydantic.JsonWrapper) +def resolve_json(cls): # type: ignore[no-untyped-def] + try: + inner = st.none() if cls.inner_type is None else st.from_type(cls.inner_type) + except Exception: # pragma: no cover + finite = st.floats(allow_infinity=False, allow_nan=False) + inner = st.recursive( + base=st.one_of(st.none(), st.booleans(), st.integers(), finite, st.text()), + extend=lambda x: st.lists(x) | st.dictionaries(st.text(), x), # type: ignore + ) + inner_type = getattr(cls, 'inner_type', None) + return st.builds( + cls.inner_type.json if lenient_issubclass(inner_type, pydantic.BaseModel) else json.dumps, + inner, + ensure_ascii=st.booleans(), + indent=st.none() | st.integers(0, 16), + sort_keys=st.booleans(), + ) + + +@resolves(pydantic.ConstrainedBytes) +def resolve_conbytes(cls): # type: ignore[no-untyped-def] # pragma: no cover + min_size = cls.min_length or 0 + max_size = cls.max_length + if not cls.strip_whitespace: + return st.binary(min_size=min_size, max_size=max_size) + # Fun with regex to ensure we neither start nor end with whitespace + repeats = '{{{},{}}}'.format( + min_size - 2 if min_size > 2 else 0, + max_size - 2 if (max_size or 0) > 2 else '', + ) + if min_size >= 2: + pattern = rf'\W.{repeats}\W' + elif min_size == 1: + pattern = rf'\W(.{repeats}\W)?' + else: + assert min_size == 0 + pattern = rf'(\W(.{repeats}\W)?)?' + return st.from_regex(pattern.encode(), fullmatch=True) + + +@resolves(pydantic.ConstrainedDecimal) +def resolve_condecimal(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt + s = st.decimals(min_value, max_value, allow_nan=False, places=cls.decimal_places) + if cls.lt is not None: + s = s.filter(lambda d: d < cls.lt) + if cls.gt is not None: + s = s.filter(lambda d: cls.gt < d) + return s + + +@resolves(pydantic.ConstrainedFloat) +def resolve_confloat(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + exclude_min = False + exclude_max = False + + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + exclude_min = True + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt + exclude_max = True + + if cls.multiple_of is None: + return st.floats(min_value, max_value, exclude_min=exclude_min, exclude_max=exclude_max, allow_nan=False) + + if min_value is not None: + min_value = math.ceil(min_value / cls.multiple_of) + if exclude_min: + min_value = min_value + 1 + if max_value is not None: + assert max_value >= cls.multiple_of, 'Cannot build model with max value smaller than multiple of' + max_value = math.floor(max_value / cls.multiple_of) + if exclude_max: + max_value = max_value - 1 + + return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of) + + +@resolves(pydantic.ConstrainedInt) +def resolve_conint(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + 1 + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt - 1 + + if cls.multiple_of is None or cls.multiple_of == 1: + return st.integers(min_value, max_value) + + # These adjustments and the .map handle integer-valued multiples, while the + # .filter handles trickier cases as for confloat. + if min_value is not None: + min_value = math.ceil(Fraction(min_value) / Fraction(cls.multiple_of)) + if max_value is not None: + max_value = math.floor(Fraction(max_value) / Fraction(cls.multiple_of)) + return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of) + + +@resolves(pydantic.ConstrainedDate) +def resolve_condate(cls): # type: ignore[no-untyped-def] + if cls.ge is not None: + assert cls.gt is None, 'Set `gt` or `ge`, but not both' + min_value = cls.ge + elif cls.gt is not None: + min_value = cls.gt + datetime.timedelta(days=1) + else: + min_value = datetime.date.min + if cls.le is not None: + assert cls.lt is None, 'Set `lt` or `le`, but not both' + max_value = cls.le + elif cls.lt is not None: + max_value = cls.lt - datetime.timedelta(days=1) + else: + max_value = datetime.date.max + return st.dates(min_value, max_value) + + +@resolves(pydantic.ConstrainedStr) +def resolve_constr(cls): # type: ignore[no-untyped-def] # pragma: no cover + min_size = cls.min_length or 0 + max_size = cls.max_length + + if cls.regex is None and not cls.strip_whitespace: + return st.text(min_size=min_size, max_size=max_size) + + if cls.regex is not None: + strategy = st.from_regex(cls.regex) + if cls.strip_whitespace: + strategy = strategy.filter(lambda s: s == s.strip()) + elif cls.strip_whitespace: + repeats = '{{{},{}}}'.format( + min_size - 2 if min_size > 2 else 0, + max_size - 2 if (max_size or 0) > 2 else '', + ) + if min_size >= 2: + strategy = st.from_regex(rf'\W.{repeats}\W') + elif min_size == 1: + strategy = st.from_regex(rf'\W(.{repeats}\W)?') + else: + assert min_size == 0 + strategy = st.from_regex(rf'(\W(.{repeats}\W)?)?') + + if min_size == 0 and max_size is None: + return strategy + elif max_size is None: + return strategy.filter(lambda s: min_size <= len(s)) + return strategy.filter(lambda s: min_size <= len(s) <= max_size) + + +# Finally, register all previously-defined types, and patch in our new function +for typ in list(pydantic.types._DEFINED_TYPES): + _registered(typ) +pydantic.types._registered = _registered +st.register_type_strategy(pydantic.Json, resolve_json) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/annotated_types.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/annotated_types.py new file mode 100644 index 0000000..d333457 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/annotated_types.py @@ -0,0 +1,72 @@ +import sys +from typing import TYPE_CHECKING, Any, Dict, FrozenSet, NamedTuple, Type + +from .fields import Required +from .main import BaseModel, create_model +from .typing import is_typeddict, is_typeddict_special + +if TYPE_CHECKING: + from typing_extensions import TypedDict + +if sys.version_info < (3, 11): + + def is_legacy_typeddict(typeddict_cls: Type['TypedDict']) -> bool: # type: ignore[valid-type] + return is_typeddict(typeddict_cls) and type(typeddict_cls).__module__ == 'typing' + +else: + + def is_legacy_typeddict(_: Any) -> Any: + return False + + +def create_model_from_typeddict( + # Mypy bug: `Type[TypedDict]` is resolved as `Any` https://github.com/python/mypy/issues/11030 + typeddict_cls: Type['TypedDict'], # type: ignore[valid-type] + **kwargs: Any, +) -> Type['BaseModel']: + """ + Create a `BaseModel` based on the fields of a `TypedDict`. + Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys, + we raise an error if this happens (see https://bugs.python.org/issue38834). + """ + field_definitions: Dict[str, Any] + + # Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions` + if not hasattr(typeddict_cls, '__required_keys__'): + raise TypeError( + 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.9.2. ' + 'Without it, there is no way to differentiate required and optional fields when subclassed.' + ) + + if is_legacy_typeddict(typeddict_cls) and any( + is_typeddict_special(t) for t in typeddict_cls.__annotations__.values() + ): + raise TypeError( + 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. ' + 'Without it, there is no way to reflect Required/NotRequired keys.' + ) + + required_keys: FrozenSet[str] = typeddict_cls.__required_keys__ # type: ignore[attr-defined] + field_definitions = { + field_name: (field_type, Required if field_name in required_keys else None) + for field_name, field_type in typeddict_cls.__annotations__.items() + } + + return create_model(typeddict_cls.__name__, **kwargs, **field_definitions) + + +def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']: + """ + Create a `BaseModel` based on the fields of a named tuple. + A named tuple can be created with `typing.NamedTuple` and declared annotations + but also with `collections.namedtuple`, in this case we consider all fields + to have type `Any`. + """ + # With python 3.10+, `__annotations__` always exists but can be empty hence the `getattr... or...` logic + namedtuple_annotations: Dict[str, Type[Any]] = getattr(namedtuple_cls, '__annotations__', None) or { + k: Any for k in namedtuple_cls._fields + } + field_definitions: Dict[str, Any] = { + field_name: (field_type, Required) for field_name, field_type in namedtuple_annotations.items() + } + return create_model(namedtuple_cls.__name__, **kwargs, **field_definitions) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/class_validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/class_validators.py new file mode 100644 index 0000000..71e6650 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/class_validators.py @@ -0,0 +1,361 @@ +import warnings +from collections import ChainMap +from functools import partial, partialmethod, wraps +from itertools import chain +from types import FunctionType +from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, Union, overload + +from .errors import ConfigError +from .typing import AnyCallable +from .utils import ROOT_KEY, in_ipython + +if TYPE_CHECKING: + from .typing import AnyClassMethod + + +class Validator: + __slots__ = 'func', 'pre', 'each_item', 'always', 'check_fields', 'skip_on_failure' + + def __init__( + self, + func: AnyCallable, + pre: bool = False, + each_item: bool = False, + always: bool = False, + check_fields: bool = False, + skip_on_failure: bool = False, + ): + self.func = func + self.pre = pre + self.each_item = each_item + self.always = always + self.check_fields = check_fields + self.skip_on_failure = skip_on_failure + + +if TYPE_CHECKING: + from inspect import Signature + + from .config import BaseConfig + from .fields import ModelField + from .types import ModelOrDc + + ValidatorCallable = Callable[[Optional[ModelOrDc], Any, Dict[str, Any], ModelField, Type[BaseConfig]], Any] + ValidatorsList = List[ValidatorCallable] + ValidatorListDict = Dict[str, List[Validator]] + +_FUNCS: Set[str] = set() +VALIDATOR_CONFIG_KEY = '__validator_config__' +ROOT_VALIDATOR_CONFIG_KEY = '__root_validator_config__' + + +def validator( + *fields: str, + pre: bool = False, + each_item: bool = False, + always: bool = False, + check_fields: bool = True, + whole: Optional[bool] = None, + allow_reuse: bool = False, +) -> Callable[[AnyCallable], 'AnyClassMethod']: + """ + Decorate methods on the class indicating that they should be used to validate fields + :param fields: which field(s) the method should be called on + :param pre: whether or not this validator should be called before the standard validators (else after) + :param each_item: for complex objects (sets, lists etc.) whether to validate individual elements rather than the + whole object + :param always: whether this method and other validators should be called even if the value is missing + :param check_fields: whether to check that the fields actually exist on the model + :param allow_reuse: whether to track and raise an error if another validator refers to the decorated function + """ + if not fields: + raise ConfigError('validator with no fields specified') + elif isinstance(fields[0], FunctionType): + raise ConfigError( + "validators should be used with fields and keyword arguments, not bare. " # noqa: Q000 + "E.g. usage should be `@validator('', ...)`" + ) + elif not all(isinstance(field, str) for field in fields): + raise ConfigError( + "validator fields should be passed as separate string args. " # noqa: Q000 + "E.g. usage should be `@validator('', '', ...)`" + ) + + if whole is not None: + warnings.warn( + 'The "whole" keyword argument is deprecated, use "each_item" (inverse meaning, default False) instead', + DeprecationWarning, + ) + assert each_item is False, '"each_item" and "whole" conflict, remove "whole"' + each_item = not whole + + def dec(f: AnyCallable) -> 'AnyClassMethod': + f_cls = _prepare_validator(f, allow_reuse) + setattr( + f_cls, + VALIDATOR_CONFIG_KEY, + ( + fields, + Validator(func=f_cls.__func__, pre=pre, each_item=each_item, always=always, check_fields=check_fields), + ), + ) + return f_cls + + return dec + + +@overload +def root_validator(_func: AnyCallable) -> 'AnyClassMethod': + ... + + +@overload +def root_validator( + *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False +) -> Callable[[AnyCallable], 'AnyClassMethod']: + ... + + +def root_validator( + _func: Optional[AnyCallable] = None, *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False +) -> Union['AnyClassMethod', Callable[[AnyCallable], 'AnyClassMethod']]: + """ + Decorate methods on a model indicating that they should be used to validate (and perhaps modify) data either + before or after standard model parsing/validation is performed. + """ + if _func: + f_cls = _prepare_validator(_func, allow_reuse) + setattr( + f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure) + ) + return f_cls + + def dec(f: AnyCallable) -> 'AnyClassMethod': + f_cls = _prepare_validator(f, allow_reuse) + setattr( + f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure) + ) + return f_cls + + return dec + + +def _prepare_validator(function: AnyCallable, allow_reuse: bool) -> 'AnyClassMethod': + """ + Avoid validators with duplicated names since without this, validators can be overwritten silently + which generally isn't the intended behaviour, don't run in ipython (see #312) or if allow_reuse is False. + """ + f_cls = function if isinstance(function, classmethod) else classmethod(function) + if not in_ipython() and not allow_reuse: + ref = ( + getattr(f_cls.__func__, '__module__', '') + + '.' + + getattr(f_cls.__func__, '__qualname__', f'') + ) + if ref in _FUNCS: + raise ConfigError(f'duplicate validator function "{ref}"; if this is intended, set `allow_reuse=True`') + _FUNCS.add(ref) + return f_cls + + +class ValidatorGroup: + def __init__(self, validators: 'ValidatorListDict') -> None: + self.validators = validators + self.used_validators = {'*'} + + def get_validators(self, name: str) -> Optional[Dict[str, Validator]]: + self.used_validators.add(name) + validators = self.validators.get(name, []) + if name != ROOT_KEY: + validators += self.validators.get('*', []) + if validators: + return {getattr(v.func, '__name__', f''): v for v in validators} + else: + return None + + def check_for_unused(self) -> None: + unused_validators = set( + chain.from_iterable( + ( + getattr(v.func, '__name__', f'') + for v in self.validators[f] + if v.check_fields + ) + for f in (self.validators.keys() - self.used_validators) + ) + ) + if unused_validators: + fn = ', '.join(unused_validators) + raise ConfigError( + f"Validators defined with incorrect fields: {fn} " # noqa: Q000 + f"(use check_fields=False if you're inheriting from the model and intended this)" + ) + + +def extract_validators(namespace: Dict[str, Any]) -> Dict[str, List[Validator]]: + validators: Dict[str, List[Validator]] = {} + for var_name, value in namespace.items(): + validator_config = getattr(value, VALIDATOR_CONFIG_KEY, None) + if validator_config: + fields, v = validator_config + for field in fields: + if field in validators: + validators[field].append(v) + else: + validators[field] = [v] + return validators + + +def extract_root_validators(namespace: Dict[str, Any]) -> Tuple[List[AnyCallable], List[Tuple[bool, AnyCallable]]]: + from inspect import signature + + pre_validators: List[AnyCallable] = [] + post_validators: List[Tuple[bool, AnyCallable]] = [] + for name, value in namespace.items(): + validator_config: Optional[Validator] = getattr(value, ROOT_VALIDATOR_CONFIG_KEY, None) + if validator_config: + sig = signature(validator_config.func) + args = list(sig.parameters.keys()) + if args[0] == 'self': + raise ConfigError( + f'Invalid signature for root validator {name}: {sig}, "self" not permitted as first argument, ' + f'should be: (cls, values).' + ) + if len(args) != 2: + raise ConfigError(f'Invalid signature for root validator {name}: {sig}, should be: (cls, values).') + # check function signature + if validator_config.pre: + pre_validators.append(validator_config.func) + else: + post_validators.append((validator_config.skip_on_failure, validator_config.func)) + return pre_validators, post_validators + + +def inherit_validators(base_validators: 'ValidatorListDict', validators: 'ValidatorListDict') -> 'ValidatorListDict': + for field, field_validators in base_validators.items(): + if field not in validators: + validators[field] = [] + validators[field] += field_validators + return validators + + +def make_generic_validator(validator: AnyCallable) -> 'ValidatorCallable': + """ + Make a generic function which calls a validator with the right arguments. + + Unfortunately other approaches (eg. return a partial of a function that builds the arguments) is slow, + hence this laborious way of doing things. + + It's done like this so validators don't all need **kwargs in their signature, eg. any combination of + the arguments "values", "fields" and/or "config" are permitted. + """ + from inspect import signature + + if not isinstance(validator, (partial, partialmethod)): + # This should be the default case, so overhead is reduced + sig = signature(validator) + args = list(sig.parameters.keys()) + else: + # Fix the generated argument lists of partial methods + sig = signature(validator.func) + args = [ + k + for k in signature(validator.func).parameters.keys() + if k not in validator.args | validator.keywords.keys() + ] + + first_arg = args.pop(0) + if first_arg == 'self': + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, "self" not permitted as first argument, ' + f'should be: (cls, value, values, config, field), "values", "config" and "field" are all optional.' + ) + elif first_arg == 'cls': + # assume the second argument is value + return wraps(validator)(_generic_validator_cls(validator, sig, set(args[1:]))) + else: + # assume the first argument was value which has already been removed + return wraps(validator)(_generic_validator_basic(validator, sig, set(args))) + + +def prep_validators(v_funcs: Iterable[AnyCallable]) -> 'ValidatorsList': + return [make_generic_validator(f) for f in v_funcs if f] + + +all_kwargs = {'values', 'field', 'config'} + + +def _generic_validator_cls(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable': + # assume the first argument is value + has_kwargs = False + if 'kwargs' in args: + has_kwargs = True + args -= {'kwargs'} + + if not args.issubset(all_kwargs): + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, should be: ' + f'(cls, value, values, config, field), "values", "config" and "field" are all optional.' + ) + + if has_kwargs: + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config) + elif args == set(): + return lambda cls, v, values, field, config: validator(cls, v) + elif args == {'values'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values) + elif args == {'field'}: + return lambda cls, v, values, field, config: validator(cls, v, field=field) + elif args == {'config'}: + return lambda cls, v, values, field, config: validator(cls, v, config=config) + elif args == {'values', 'field'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field) + elif args == {'values', 'config'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values, config=config) + elif args == {'field', 'config'}: + return lambda cls, v, values, field, config: validator(cls, v, field=field, config=config) + else: + # args == {'values', 'field', 'config'} + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config) + + +def _generic_validator_basic(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable': + has_kwargs = False + if 'kwargs' in args: + has_kwargs = True + args -= {'kwargs'} + + if not args.issubset(all_kwargs): + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, should be: ' + f'(value, values, config, field), "values", "config" and "field" are all optional.' + ) + + if has_kwargs: + return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config) + elif args == set(): + return lambda cls, v, values, field, config: validator(v) + elif args == {'values'}: + return lambda cls, v, values, field, config: validator(v, values=values) + elif args == {'field'}: + return lambda cls, v, values, field, config: validator(v, field=field) + elif args == {'config'}: + return lambda cls, v, values, field, config: validator(v, config=config) + elif args == {'values', 'field'}: + return lambda cls, v, values, field, config: validator(v, values=values, field=field) + elif args == {'values', 'config'}: + return lambda cls, v, values, field, config: validator(v, values=values, config=config) + elif args == {'field', 'config'}: + return lambda cls, v, values, field, config: validator(v, field=field, config=config) + else: + # args == {'values', 'field', 'config'} + return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config) + + +def gather_all_validators(type_: 'ModelOrDc') -> Dict[str, 'AnyClassMethod']: + all_attributes = ChainMap(*[cls.__dict__ for cls in type_.__mro__]) # type: ignore[arg-type,var-annotated] + return { + k: v + for k, v in all_attributes.items() + if hasattr(v, VALIDATOR_CONFIG_KEY) or hasattr(v, ROOT_VALIDATOR_CONFIG_KEY) + } diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/color.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/color.py new file mode 100644 index 0000000..6fdc9fb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/color.py @@ -0,0 +1,494 @@ +""" +Color definitions are used as per CSS3 specification: +http://www.w3.org/TR/css3-color/#svg-color + +A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`. + +In these cases the LAST color when sorted alphabetically takes preferences, +eg. Color((0, 255, 255)).as_named() == 'cyan' because "cyan" comes after "aqua". +""" +import math +import re +from colorsys import hls_to_rgb, rgb_to_hls +from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union, cast + +from .errors import ColorError +from .utils import Representation, almost_equal_floats + +if TYPE_CHECKING: + from .typing import CallableGenerator, ReprArgs + +ColorTuple = Union[Tuple[int, int, int], Tuple[int, int, int, float]] +ColorType = Union[ColorTuple, str] +HslColorTuple = Union[Tuple[float, float, float], Tuple[float, float, float, float]] + + +class RGBA: + """ + Internal use only as a representation of a color. + """ + + __slots__ = 'r', 'g', 'b', 'alpha', '_tuple' + + def __init__(self, r: float, g: float, b: float, alpha: Optional[float]): + self.r = r + self.g = g + self.b = b + self.alpha = alpha + + self._tuple: Tuple[float, float, float, Optional[float]] = (r, g, b, alpha) + + def __getitem__(self, item: Any) -> Any: + return self._tuple[item] + + +# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached +r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*' +r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*' +_r_255 = r'(\d{1,3}(?:\.\d+)?)' +_r_comma = r'\s*,\s*' +r_rgb = fr'\s*rgb\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}\)\s*' +_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)' +r_rgba = fr'\s*rgba\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_alpha}\s*\)\s*' +_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?' +_r_sl = r'(\d{1,3}(?:\.\d+)?)%' +r_hsl = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}\s*\)\s*' +r_hsla = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}{_r_comma}{_r_alpha}\s*\)\s*' + +# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used +repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'} +rads = 2 * math.pi + + +class Color(Representation): + __slots__ = '_original', '_rgba' + + def __init__(self, value: ColorType) -> None: + self._rgba: RGBA + self._original: ColorType + if isinstance(value, (tuple, list)): + self._rgba = parse_tuple(value) + elif isinstance(value, str): + self._rgba = parse_str(value) + elif isinstance(value, Color): + self._rgba = value._rgba + value = value._original + else: + raise ColorError(reason='value must be a tuple, list or string') + + # if we've got here value must be a valid color + self._original = value + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='color') + + def original(self) -> ColorType: + """ + Original value passed to Color + """ + return self._original + + def as_named(self, *, fallback: bool = False) -> str: + if self._rgba.alpha is None: + rgb = cast(Tuple[int, int, int], self.as_rgb_tuple()) + try: + return COLORS_BY_VALUE[rgb] + except KeyError as e: + if fallback: + return self.as_hex() + else: + raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e + else: + return self.as_hex() + + def as_hex(self) -> str: + """ + Hex string representing the color can be 3, 4, 6 or 8 characters depending on whether the string + a "short" representation of the color is possible and whether there's an alpha channel. + """ + values = [float_to_255(c) for c in self._rgba[:3]] + if self._rgba.alpha is not None: + values.append(float_to_255(self._rgba.alpha)) + + as_hex = ''.join(f'{v:02x}' for v in values) + if all(c in repeat_colors for c in values): + as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2)) + return '#' + as_hex + + def as_rgb(self) -> str: + """ + Color as an rgb(, , ) or rgba(, , , ) string. + """ + if self._rgba.alpha is None: + return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})' + else: + return ( + f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, ' + f'{round(self._alpha_float(), 2)})' + ) + + def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple: + """ + Color as an RGB or RGBA tuple; red, green and blue are in the range 0 to 255, alpha if included is + in the range 0 to 1. + + :param alpha: whether to include the alpha channel, options are + None - (default) include alpha only if it's set (e.g. not None) + True - always include alpha, + False - always omit alpha, + """ + r, g, b = (float_to_255(c) for c in self._rgba[:3]) + if alpha is None: + if self._rgba.alpha is None: + return r, g, b + else: + return r, g, b, self._alpha_float() + elif alpha: + return r, g, b, self._alpha_float() + else: + # alpha is False + return r, g, b + + def as_hsl(self) -> str: + """ + Color as an hsl(, , ) or hsl(, , , ) string. + """ + if self._rgba.alpha is None: + h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})' + else: + h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})' + + def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple: + """ + Color as an HSL or HSLA tuple, e.g. hue, saturation, lightness and optionally alpha; all elements are in + the range 0 to 1. + + NOTE: this is HSL as used in HTML and most other places, not HLS as used in python's colorsys. + + :param alpha: whether to include the alpha channel, options are + None - (default) include alpha only if it's set (e.g. not None) + True - always include alpha, + False - always omit alpha, + """ + h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) + if alpha is None: + if self._rgba.alpha is None: + return h, s, l + else: + return h, s, l, self._alpha_float() + if alpha: + return h, s, l, self._alpha_float() + else: + # alpha is False + return h, s, l + + def _alpha_float(self) -> float: + return 1 if self._rgba.alpha is None else self._rgba.alpha + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls + + def __str__(self) -> str: + return self.as_named(fallback=True) + + def __repr_args__(self) -> 'ReprArgs': + return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())] # type: ignore + + def __eq__(self, other: Any) -> bool: + return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple() + + def __hash__(self) -> int: + return hash(self.as_rgb_tuple()) + + +def parse_tuple(value: Tuple[Any, ...]) -> RGBA: + """ + Parse a tuple or list as a color. + """ + if len(value) == 3: + r, g, b = (parse_color_value(v) for v in value) + return RGBA(r, g, b, None) + elif len(value) == 4: + r, g, b = (parse_color_value(v) for v in value[:3]) + return RGBA(r, g, b, parse_float_alpha(value[3])) + else: + raise ColorError(reason='tuples must have length 3 or 4') + + +def parse_str(value: str) -> RGBA: + """ + Parse a string to an RGBA tuple, trying the following formats (in this order): + * named color, see COLORS_BY_NAME below + * hex short eg. `fff` (prefix can be `#`, `0x` or nothing) + * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing) + * `rgb(, , ) ` + * `rgba(, , , )` + """ + value_lower = value.lower() + try: + r, g, b = COLORS_BY_NAME[value_lower] + except KeyError: + pass + else: + return ints_to_rgba(r, g, b, None) + + m = re.fullmatch(r_hex_short, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v * 2, 16) for v in rgb) + if a: + alpha: Optional[float] = int(a * 2, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_hex_long, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v, 16) for v in rgb) + if a: + alpha = int(a, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_rgb, value_lower) + if m: + return ints_to_rgba(*m.groups(), None) # type: ignore + + m = re.fullmatch(r_rgba, value_lower) + if m: + return ints_to_rgba(*m.groups()) # type: ignore + + m = re.fullmatch(r_hsl, value_lower) + if m: + h, h_units, s, l_ = m.groups() + return parse_hsl(h, h_units, s, l_) + + m = re.fullmatch(r_hsla, value_lower) + if m: + h, h_units, s, l_, a = m.groups() + return parse_hsl(h, h_units, s, l_, parse_float_alpha(a)) + + raise ColorError(reason='string not recognised as a valid color') + + +def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float]) -> RGBA: + return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha)) + + +def parse_color_value(value: Union[int, str], max_val: int = 255) -> float: + """ + Parse a value checking it's a valid int in the range 0 to max_val and divide by max_val to give a number + in the range 0 to 1 + """ + try: + color = float(value) + except ValueError: + raise ColorError(reason='color values must be a valid number') + if 0 <= color <= max_val: + return color / max_val + else: + raise ColorError(reason=f'color values must be in the range 0 to {max_val}') + + +def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]: + """ + Parse a value checking it's a valid float in the range 0 to 1 + """ + if value is None: + return None + try: + if isinstance(value, str) and value.endswith('%'): + alpha = float(value[:-1]) / 100 + else: + alpha = float(value) + except ValueError: + raise ColorError(reason='alpha values must be a valid float') + + if almost_equal_floats(alpha, 1): + return None + elif 0 <= alpha <= 1: + return alpha + else: + raise ColorError(reason='alpha values must be in the range 0 to 1') + + +def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA: + """ + Parse raw hue, saturation, lightness and alpha values and convert to RGBA. + """ + s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100) + + h_value = float(h) + if h_units in {None, 'deg'}: + h_value = h_value % 360 / 360 + elif h_units == 'rad': + h_value = h_value % rads / rads + else: + # turns + h_value = h_value % 1 + + r, g, b = hls_to_rgb(h_value, l_value, s_value) + return RGBA(r, g, b, alpha) + + +def float_to_255(c: float) -> int: + return int(round(c * 255)) + + +COLORS_BY_NAME = { + 'aliceblue': (240, 248, 255), + 'antiquewhite': (250, 235, 215), + 'aqua': (0, 255, 255), + 'aquamarine': (127, 255, 212), + 'azure': (240, 255, 255), + 'beige': (245, 245, 220), + 'bisque': (255, 228, 196), + 'black': (0, 0, 0), + 'blanchedalmond': (255, 235, 205), + 'blue': (0, 0, 255), + 'blueviolet': (138, 43, 226), + 'brown': (165, 42, 42), + 'burlywood': (222, 184, 135), + 'cadetblue': (95, 158, 160), + 'chartreuse': (127, 255, 0), + 'chocolate': (210, 105, 30), + 'coral': (255, 127, 80), + 'cornflowerblue': (100, 149, 237), + 'cornsilk': (255, 248, 220), + 'crimson': (220, 20, 60), + 'cyan': (0, 255, 255), + 'darkblue': (0, 0, 139), + 'darkcyan': (0, 139, 139), + 'darkgoldenrod': (184, 134, 11), + 'darkgray': (169, 169, 169), + 'darkgreen': (0, 100, 0), + 'darkgrey': (169, 169, 169), + 'darkkhaki': (189, 183, 107), + 'darkmagenta': (139, 0, 139), + 'darkolivegreen': (85, 107, 47), + 'darkorange': (255, 140, 0), + 'darkorchid': (153, 50, 204), + 'darkred': (139, 0, 0), + 'darksalmon': (233, 150, 122), + 'darkseagreen': (143, 188, 143), + 'darkslateblue': (72, 61, 139), + 'darkslategray': (47, 79, 79), + 'darkslategrey': (47, 79, 79), + 'darkturquoise': (0, 206, 209), + 'darkviolet': (148, 0, 211), + 'deeppink': (255, 20, 147), + 'deepskyblue': (0, 191, 255), + 'dimgray': (105, 105, 105), + 'dimgrey': (105, 105, 105), + 'dodgerblue': (30, 144, 255), + 'firebrick': (178, 34, 34), + 'floralwhite': (255, 250, 240), + 'forestgreen': (34, 139, 34), + 'fuchsia': (255, 0, 255), + 'gainsboro': (220, 220, 220), + 'ghostwhite': (248, 248, 255), + 'gold': (255, 215, 0), + 'goldenrod': (218, 165, 32), + 'gray': (128, 128, 128), + 'green': (0, 128, 0), + 'greenyellow': (173, 255, 47), + 'grey': (128, 128, 128), + 'honeydew': (240, 255, 240), + 'hotpink': (255, 105, 180), + 'indianred': (205, 92, 92), + 'indigo': (75, 0, 130), + 'ivory': (255, 255, 240), + 'khaki': (240, 230, 140), + 'lavender': (230, 230, 250), + 'lavenderblush': (255, 240, 245), + 'lawngreen': (124, 252, 0), + 'lemonchiffon': (255, 250, 205), + 'lightblue': (173, 216, 230), + 'lightcoral': (240, 128, 128), + 'lightcyan': (224, 255, 255), + 'lightgoldenrodyellow': (250, 250, 210), + 'lightgray': (211, 211, 211), + 'lightgreen': (144, 238, 144), + 'lightgrey': (211, 211, 211), + 'lightpink': (255, 182, 193), + 'lightsalmon': (255, 160, 122), + 'lightseagreen': (32, 178, 170), + 'lightskyblue': (135, 206, 250), + 'lightslategray': (119, 136, 153), + 'lightslategrey': (119, 136, 153), + 'lightsteelblue': (176, 196, 222), + 'lightyellow': (255, 255, 224), + 'lime': (0, 255, 0), + 'limegreen': (50, 205, 50), + 'linen': (250, 240, 230), + 'magenta': (255, 0, 255), + 'maroon': (128, 0, 0), + 'mediumaquamarine': (102, 205, 170), + 'mediumblue': (0, 0, 205), + 'mediumorchid': (186, 85, 211), + 'mediumpurple': (147, 112, 219), + 'mediumseagreen': (60, 179, 113), + 'mediumslateblue': (123, 104, 238), + 'mediumspringgreen': (0, 250, 154), + 'mediumturquoise': (72, 209, 204), + 'mediumvioletred': (199, 21, 133), + 'midnightblue': (25, 25, 112), + 'mintcream': (245, 255, 250), + 'mistyrose': (255, 228, 225), + 'moccasin': (255, 228, 181), + 'navajowhite': (255, 222, 173), + 'navy': (0, 0, 128), + 'oldlace': (253, 245, 230), + 'olive': (128, 128, 0), + 'olivedrab': (107, 142, 35), + 'orange': (255, 165, 0), + 'orangered': (255, 69, 0), + 'orchid': (218, 112, 214), + 'palegoldenrod': (238, 232, 170), + 'palegreen': (152, 251, 152), + 'paleturquoise': (175, 238, 238), + 'palevioletred': (219, 112, 147), + 'papayawhip': (255, 239, 213), + 'peachpuff': (255, 218, 185), + 'peru': (205, 133, 63), + 'pink': (255, 192, 203), + 'plum': (221, 160, 221), + 'powderblue': (176, 224, 230), + 'purple': (128, 0, 128), + 'red': (255, 0, 0), + 'rosybrown': (188, 143, 143), + 'royalblue': (65, 105, 225), + 'saddlebrown': (139, 69, 19), + 'salmon': (250, 128, 114), + 'sandybrown': (244, 164, 96), + 'seagreen': (46, 139, 87), + 'seashell': (255, 245, 238), + 'sienna': (160, 82, 45), + 'silver': (192, 192, 192), + 'skyblue': (135, 206, 235), + 'slateblue': (106, 90, 205), + 'slategray': (112, 128, 144), + 'slategrey': (112, 128, 144), + 'snow': (255, 250, 250), + 'springgreen': (0, 255, 127), + 'steelblue': (70, 130, 180), + 'tan': (210, 180, 140), + 'teal': (0, 128, 128), + 'thistle': (216, 191, 216), + 'tomato': (255, 99, 71), + 'turquoise': (64, 224, 208), + 'violet': (238, 130, 238), + 'wheat': (245, 222, 179), + 'white': (255, 255, 255), + 'whitesmoke': (245, 245, 245), + 'yellow': (255, 255, 0), + 'yellowgreen': (154, 205, 50), +} + +COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()} diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/config.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/config.py new file mode 100644 index 0000000..a25973a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/config.py @@ -0,0 +1,191 @@ +import json +from enum import Enum +from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Optional, Tuple, Type, Union + +from typing_extensions import Literal, Protocol + +from .typing import AnyArgTCallable, AnyCallable +from .utils import GetterDict +from .version import compiled + +if TYPE_CHECKING: + from typing import overload + + from .fields import ModelField + from .main import BaseModel + + ConfigType = Type['BaseConfig'] + + class SchemaExtraCallable(Protocol): + @overload + def __call__(self, schema: Dict[str, Any]) -> None: + pass + + @overload + def __call__(self, schema: Dict[str, Any], model_class: Type[BaseModel]) -> None: + pass + +else: + SchemaExtraCallable = Callable[..., None] + +__all__ = 'BaseConfig', 'ConfigDict', 'get_config', 'Extra', 'inherit_config', 'prepare_config' + + +class Extra(str, Enum): + allow = 'allow' + ignore = 'ignore' + forbid = 'forbid' + + +# https://github.com/cython/cython/issues/4003 +# Fixed in Cython 3 and Pydantic v1 won't support Cython 3. +# Pydantic v2 doesn't depend on Cython at all. +if not compiled: + from typing_extensions import TypedDict + + class ConfigDict(TypedDict, total=False): + title: Optional[str] + anystr_lower: bool + anystr_strip_whitespace: bool + min_anystr_length: int + max_anystr_length: Optional[int] + validate_all: bool + extra: Extra + allow_mutation: bool + frozen: bool + allow_population_by_field_name: bool + use_enum_values: bool + fields: Dict[str, Union[str, Dict[str, str]]] + validate_assignment: bool + error_msg_templates: Dict[str, str] + arbitrary_types_allowed: bool + orm_mode: bool + getter_dict: Type[GetterDict] + alias_generator: Optional[Callable[[str], str]] + keep_untouched: Tuple[type, ...] + schema_extra: Union[Dict[str, object], 'SchemaExtraCallable'] + json_loads: Callable[[str], object] + json_dumps: AnyArgTCallable[str] + json_encoders: Dict[Type[object], AnyCallable] + underscore_attrs_are_private: bool + allow_inf_nan: bool + copy_on_model_validation: Literal['none', 'deep', 'shallow'] + # whether dataclass `__post_init__` should be run after validation + post_init_call: Literal['before_validation', 'after_validation'] + +else: + ConfigDict = dict # type: ignore + + +class BaseConfig: + title: Optional[str] = None + anystr_lower: bool = False + anystr_upper: bool = False + anystr_strip_whitespace: bool = False + min_anystr_length: int = 0 + max_anystr_length: Optional[int] = None + validate_all: bool = False + extra: Extra = Extra.ignore + allow_mutation: bool = True + frozen: bool = False + allow_population_by_field_name: bool = False + use_enum_values: bool = False + fields: Dict[str, Union[str, Dict[str, str]]] = {} + validate_assignment: bool = False + error_msg_templates: Dict[str, str] = {} + arbitrary_types_allowed: bool = False + orm_mode: bool = False + getter_dict: Type[GetterDict] = GetterDict + alias_generator: Optional[Callable[[str], str]] = None + keep_untouched: Tuple[type, ...] = () + schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {} + json_loads: Callable[[str], Any] = json.loads + json_dumps: Callable[..., str] = json.dumps + json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {} + underscore_attrs_are_private: bool = False + allow_inf_nan: bool = True + + # whether inherited models as fields should be reconstructed as base model, + # and whether such a copy should be shallow or deep + copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow' + + # whether `Union` should check all allowed types before even trying to coerce + smart_union: bool = False + # whether dataclass `__post_init__` should be run before or after validation + post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation' + + @classmethod + def get_field_info(cls, name: str) -> Dict[str, Any]: + """ + Get properties of FieldInfo from the `fields` property of the config class. + """ + + fields_value = cls.fields.get(name) + + if isinstance(fields_value, str): + field_info: Dict[str, Any] = {'alias': fields_value} + elif isinstance(fields_value, dict): + field_info = fields_value + else: + field_info = {} + + if 'alias' in field_info: + field_info.setdefault('alias_priority', 2) + + if field_info.get('alias_priority', 0) <= 1 and cls.alias_generator: + alias = cls.alias_generator(name) + if not isinstance(alias, str): + raise TypeError(f'Config.alias_generator must return str, not {alias.__class__}') + field_info.update(alias=alias, alias_priority=1) + return field_info + + @classmethod + def prepare_field(cls, field: 'ModelField') -> None: + """ + Optional hook to check or modify fields during model creation. + """ + pass + + +def get_config(config: Union[ConfigDict, Type[object], None]) -> Type[BaseConfig]: + if config is None: + return BaseConfig + + else: + config_dict = ( + config + if isinstance(config, dict) + else {k: getattr(config, k) for k in dir(config) if not k.startswith('__')} + ) + + class Config(BaseConfig): + ... + + for k, v in config_dict.items(): + setattr(Config, k, v) + return Config + + +def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType': + if not self_config: + base_classes: Tuple['ConfigType', ...] = (parent_config,) + elif self_config == parent_config: + base_classes = (self_config,) + else: + base_classes = self_config, parent_config + + namespace['json_encoders'] = { + **getattr(parent_config, 'json_encoders', {}), + **getattr(self_config, 'json_encoders', {}), + **namespace.get('json_encoders', {}), + } + + return type('Config', base_classes, namespace) + + +def prepare_config(config: Type[BaseConfig], cls_name: str) -> None: + if not isinstance(config.extra, Extra): + try: + config.extra = Extra(config.extra) + except ValueError: + raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"') diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/dataclasses.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/dataclasses.py new file mode 100644 index 0000000..2df3987 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/dataclasses.py @@ -0,0 +1,500 @@ +""" +The main purpose is to enhance stdlib dataclasses by adding validation +A pydantic dataclass can be generated from scratch or from a stdlib one. + +Behind the scene, a pydantic dataclass is just like a regular one on which we attach +a `BaseModel` and magic methods to trigger the validation of the data. +`__init__` and `__post_init__` are hence overridden and have extra logic to be +able to validate input data. + +When a pydantic dataclass is generated from scratch, it's just a plain dataclass +with validation triggered at initialization + +The tricky part if for stdlib dataclasses that are converted after into pydantic ones e.g. + +```py +@dataclasses.dataclass +class M: + x: int + +ValidatedM = pydantic.dataclasses.dataclass(M) +``` + +We indeed still want to support equality, hashing, repr, ... as if it was the stdlib one! + +```py +assert isinstance(ValidatedM(x=1), M) +assert ValidatedM(x=1) == M(x=1) +``` + +This means we **don't want to create a new dataclass that inherits from it** +The trick is to create a wrapper around `M` that will act as a proxy to trigger +validation without altering default `M` behaviour. +""" +import copy +import dataclasses +import sys +from contextlib import contextmanager +from functools import wraps + +try: + from functools import cached_property +except ImportError: + # cached_property available only for python3.8+ + pass + +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Generator, Optional, Type, TypeVar, Union, overload + +from typing_extensions import dataclass_transform + +from .class_validators import gather_all_validators +from .config import BaseConfig, ConfigDict, Extra, get_config +from .error_wrappers import ValidationError +from .errors import DataclassTypeError +from .fields import Field, FieldInfo, Required, Undefined +from .main import create_model, validate_model +from .utils import ClassAttribute + +if TYPE_CHECKING: + from .main import BaseModel + from .typing import CallableGenerator, NoArgAnyCallable + + DataclassT = TypeVar('DataclassT', bound='Dataclass') + + DataclassClassOrWrapper = Union[Type['Dataclass'], 'DataclassProxy'] + + class Dataclass: + # stdlib attributes + __dataclass_fields__: ClassVar[Dict[str, Any]] + __dataclass_params__: ClassVar[Any] # in reality `dataclasses._DataclassParams` + __post_init__: ClassVar[Callable[..., None]] + + # Added by pydantic + __pydantic_run_validation__: ClassVar[bool] + __post_init_post_parse__: ClassVar[Callable[..., None]] + __pydantic_initialised__: ClassVar[bool] + __pydantic_model__: ClassVar[Type[BaseModel]] + __pydantic_validate_values__: ClassVar[Callable[['Dataclass'], None]] + __pydantic_has_field_info_default__: ClassVar[bool] # whether a `pydantic.Field` is used as default value + + def __init__(self, *args: object, **kwargs: object) -> None: + pass + + @classmethod + def __get_validators__(cls: Type['Dataclass']) -> 'CallableGenerator': + pass + + @classmethod + def __validate__(cls: Type['DataclassT'], v: Any) -> 'DataclassT': + pass + + +__all__ = [ + 'dataclass', + 'set_validation', + 'create_pydantic_model_from_dataclass', + 'is_builtin_dataclass', + 'make_dataclass_validator', +] + +_T = TypeVar('_T') + +if sys.version_info >= (3, 10): + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, + kw_only: bool = ..., + ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + _cls: Type[_T], + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, + kw_only: bool = ..., + ) -> 'DataclassClassOrWrapper': + ... + +else: + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, + ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field)) + @overload + def dataclass( + _cls: Type[_T], + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, + ) -> 'DataclassClassOrWrapper': + ... + + +@dataclass_transform(field_specifiers=(dataclasses.field, Field)) +def dataclass( + _cls: Optional[Type[_T]] = None, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + use_proxy: Optional[bool] = None, + kw_only: bool = False, +) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']: + """ + Like the python standard lib dataclasses but with type validation. + The result is either a pydantic dataclass that will validate input data + or a wrapper that will trigger validation around a stdlib dataclass + to avoid modifying it directly + """ + the_config = get_config(config) + + def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper': + should_use_proxy = ( + use_proxy + if use_proxy is not None + else ( + is_builtin_dataclass(cls) + and (cls.__bases__[0] is object or set(dir(cls)) == set(dir(cls.__bases__[0]))) + ) + ) + if should_use_proxy: + dc_cls_doc = '' + dc_cls = DataclassProxy(cls) + default_validate_on_init = False + else: + dc_cls_doc = cls.__doc__ or '' # needs to be done before generating dataclass + if sys.version_info >= (3, 10): + dc_cls = dataclasses.dataclass( + cls, + init=init, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen, + kw_only=kw_only, + ) + else: + dc_cls = dataclasses.dataclass( # type: ignore + cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen + ) + default_validate_on_init = True + + should_validate_on_init = default_validate_on_init if validate_on_init is None else validate_on_init + _add_pydantic_validation_attributes(cls, the_config, should_validate_on_init, dc_cls_doc) + dc_cls.__pydantic_model__.__try_update_forward_refs__(**{cls.__name__: cls}) + return dc_cls + + if _cls is None: + return wrap + + return wrap(_cls) + + +@contextmanager +def set_validation(cls: Type['DataclassT'], value: bool) -> Generator[Type['DataclassT'], None, None]: + original_run_validation = cls.__pydantic_run_validation__ + try: + cls.__pydantic_run_validation__ = value + yield cls + finally: + cls.__pydantic_run_validation__ = original_run_validation + + +class DataclassProxy: + __slots__ = '__dataclass__' + + def __init__(self, dc_cls: Type['Dataclass']) -> None: + object.__setattr__(self, '__dataclass__', dc_cls) + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + with set_validation(self.__dataclass__, True): + return self.__dataclass__(*args, **kwargs) + + def __getattr__(self, name: str) -> Any: + return getattr(self.__dataclass__, name) + + def __setattr__(self, __name: str, __value: Any) -> None: + return setattr(self.__dataclass__, __name, __value) + + def __instancecheck__(self, instance: Any) -> bool: + return isinstance(instance, self.__dataclass__) + + def __copy__(self) -> 'DataclassProxy': + return DataclassProxy(copy.copy(self.__dataclass__)) + + def __deepcopy__(self, memo: Any) -> 'DataclassProxy': + return DataclassProxy(copy.deepcopy(self.__dataclass__, memo)) + + +def _add_pydantic_validation_attributes( # noqa: C901 (ignore complexity) + dc_cls: Type['Dataclass'], + config: Type[BaseConfig], + validate_on_init: bool, + dc_cls_doc: str, +) -> None: + """ + We need to replace the right method. If no `__post_init__` has been set in the stdlib dataclass + it won't even exist (code is generated on the fly by `dataclasses`) + By default, we run validation after `__init__` or `__post_init__` if defined + """ + init = dc_cls.__init__ + + @wraps(init) + def handle_extra_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + if config.extra == Extra.ignore: + init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__}) + + elif config.extra == Extra.allow: + for k, v in kwargs.items(): + self.__dict__.setdefault(k, v) + init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__}) + + else: + init(self, *args, **kwargs) + + if hasattr(dc_cls, '__post_init__'): + try: + post_init = dc_cls.__post_init__.__wrapped__ # type: ignore[attr-defined] + except AttributeError: + post_init = dc_cls.__post_init__ + + @wraps(post_init) + def new_post_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + if config.post_init_call == 'before_validation': + post_init(self, *args, **kwargs) + + if self.__class__.__pydantic_run_validation__: + self.__pydantic_validate_values__() + if hasattr(self, '__post_init_post_parse__'): + self.__post_init_post_parse__(*args, **kwargs) + + if config.post_init_call == 'after_validation': + post_init(self, *args, **kwargs) + + setattr(dc_cls, '__init__', handle_extra_init) + setattr(dc_cls, '__post_init__', new_post_init) + + else: + + @wraps(init) + def new_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + handle_extra_init(self, *args, **kwargs) + + if self.__class__.__pydantic_run_validation__: + self.__pydantic_validate_values__() + + if hasattr(self, '__post_init_post_parse__'): + # We need to find again the initvars. To do that we use `__dataclass_fields__` instead of + # public method `dataclasses.fields` + + # get all initvars and their default values + initvars_and_values: Dict[str, Any] = {} + for i, f in enumerate(self.__class__.__dataclass_fields__.values()): + if f._field_type is dataclasses._FIELD_INITVAR: # type: ignore[attr-defined] + try: + # set arg value by default + initvars_and_values[f.name] = args[i] + except IndexError: + initvars_and_values[f.name] = kwargs.get(f.name, f.default) + + self.__post_init_post_parse__(**initvars_and_values) + + setattr(dc_cls, '__init__', new_init) + + setattr(dc_cls, '__pydantic_run_validation__', ClassAttribute('__pydantic_run_validation__', validate_on_init)) + setattr(dc_cls, '__pydantic_initialised__', False) + setattr(dc_cls, '__pydantic_model__', create_pydantic_model_from_dataclass(dc_cls, config, dc_cls_doc)) + setattr(dc_cls, '__pydantic_validate_values__', _dataclass_validate_values) + setattr(dc_cls, '__validate__', classmethod(_validate_dataclass)) + setattr(dc_cls, '__get_validators__', classmethod(_get_validators)) + + if dc_cls.__pydantic_model__.__config__.validate_assignment and not dc_cls.__dataclass_params__.frozen: + setattr(dc_cls, '__setattr__', _dataclass_validate_assignment_setattr) + + +def _get_validators(cls: 'DataclassClassOrWrapper') -> 'CallableGenerator': + yield cls.__validate__ + + +def _validate_dataclass(cls: Type['DataclassT'], v: Any) -> 'DataclassT': + with set_validation(cls, True): + if isinstance(v, cls): + v.__pydantic_validate_values__() + return v + elif isinstance(v, (list, tuple)): + return cls(*v) + elif isinstance(v, dict): + return cls(**v) + else: + raise DataclassTypeError(class_name=cls.__name__) + + +def create_pydantic_model_from_dataclass( + dc_cls: Type['Dataclass'], + config: Type[Any] = BaseConfig, + dc_cls_doc: Optional[str] = None, +) -> Type['BaseModel']: + field_definitions: Dict[str, Any] = {} + for field in dataclasses.fields(dc_cls): + default: Any = Undefined + default_factory: Optional['NoArgAnyCallable'] = None + field_info: FieldInfo + + if field.default is not dataclasses.MISSING: + default = field.default + elif field.default_factory is not dataclasses.MISSING: + default_factory = field.default_factory + else: + default = Required + + if isinstance(default, FieldInfo): + field_info = default + dc_cls.__pydantic_has_field_info_default__ = True + else: + field_info = Field(default=default, default_factory=default_factory, **field.metadata) + + field_definitions[field.name] = (field.type, field_info) + + validators = gather_all_validators(dc_cls) + model: Type['BaseModel'] = create_model( + dc_cls.__name__, + __config__=config, + __module__=dc_cls.__module__, + __validators__=validators, + __cls_kwargs__={'__resolve_forward_refs__': False}, + **field_definitions, + ) + model.__doc__ = dc_cls_doc if dc_cls_doc is not None else dc_cls.__doc__ or '' + return model + + +if sys.version_info >= (3, 8): + + def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool: + return isinstance(getattr(type(obj), k, None), cached_property) + +else: + + def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool: + return False + + +def _dataclass_validate_values(self: 'Dataclass') -> None: + # validation errors can occur if this function is called twice on an already initialised dataclass. + # for example if Extra.forbid is enabled, it would consider __pydantic_initialised__ an invalid extra property + if getattr(self, '__pydantic_initialised__'): + return + if getattr(self, '__pydantic_has_field_info_default__', False): + # We need to remove `FieldInfo` values since they are not valid as input + # It's ok to do that because they are obviously the default values! + input_data = { + k: v + for k, v in self.__dict__.items() + if not (isinstance(v, FieldInfo) or _is_field_cached_property(self, k)) + } + else: + input_data = {k: v for k, v in self.__dict__.items() if not _is_field_cached_property(self, k)} + d, _, validation_error = validate_model(self.__pydantic_model__, input_data, cls=self.__class__) + if validation_error: + raise validation_error + self.__dict__.update(d) + object.__setattr__(self, '__pydantic_initialised__', True) + + +def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value: Any) -> None: + if self.__pydantic_initialised__: + d = dict(self.__dict__) + d.pop(name, None) + known_field = self.__pydantic_model__.__fields__.get(name, None) + if known_field: + value, error_ = known_field.validate(value, d, loc=name, cls=self.__class__) + if error_: + raise ValidationError([error_], self.__class__) + + object.__setattr__(self, name, value) + + +def is_builtin_dataclass(_cls: Type[Any]) -> bool: + """ + Whether a class is a stdlib dataclass + (useful to discriminated a pydantic dataclass that is actually a wrapper around a stdlib dataclass) + + we check that + - `_cls` is a dataclass + - `_cls` is not a processed pydantic dataclass (with a basemodel attached) + - `_cls` is not a pydantic dataclass inheriting directly from a stdlib dataclass + e.g. + ``` + @dataclasses.dataclass + class A: + x: int + + @pydantic.dataclasses.dataclass + class B(A): + y: int + ``` + In this case, when we first check `B`, we make an extra check and look at the annotations ('y'), + which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x') + """ + return ( + dataclasses.is_dataclass(_cls) + and not hasattr(_cls, '__pydantic_model__') + and set(_cls.__dataclass_fields__).issuperset(set(getattr(_cls, '__annotations__', {}))) + ) + + +def make_dataclass_validator(dc_cls: Type['Dataclass'], config: Type[BaseConfig]) -> 'CallableGenerator': + """ + Create a pydantic.dataclass from a builtin dataclass to add type validation + and yield the validators + It retrieves the parameters of the dataclass and forwards them to the newly created dataclass + """ + yield from _get_validators(dataclass(dc_cls, config=config, use_proxy=True)) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/datetime_parse.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/datetime_parse.py new file mode 100644 index 0000000..cfd5459 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/datetime_parse.py @@ -0,0 +1,248 @@ +""" +Functions to parse datetime objects. + +We're using regular expressions rather than time.strptime because: +- They provide both validation and parsing. +- They're more flexible for datetimes. +- The date/datetime/time constructors produce friendlier error messages. + +Stolen from https://raw.githubusercontent.com/django/django/main/django/utils/dateparse.py at +9718fa2e8abe430c3526a9278dd976443d4ae3c6 + +Changed to: +* use standard python datetime types not django.utils.timezone +* raise ValueError when regex doesn't match rather than returning None +* support parsing unix timestamps for dates and datetimes +""" +import re +from datetime import date, datetime, time, timedelta, timezone +from typing import Dict, Optional, Type, Union + +from . import errors + +date_expr = r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})' +time_expr = ( + r'(?P\d{1,2}):(?P\d{1,2})' + r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?' + r'(?PZ|[+-]\d{2}(?::?\d{2})?)?$' +) + +date_re = re.compile(f'{date_expr}$') +time_re = re.compile(time_expr) +datetime_re = re.compile(f'{date_expr}[T ]{time_expr}') + +standard_duration_re = re.compile( + r'^' + r'(?:(?P-?\d+) (days?, )?)?' + r'((?:(?P-?\d+):)(?=\d+:\d+))?' + r'(?:(?P-?\d+):)?' + r'(?P-?\d+)' + r'(?:\.(?P\d{1,6})\d{0,6})?' + r'$' +) + +# Support the sections of ISO 8601 date representation that are accepted by timedelta +iso8601_duration_re = re.compile( + r'^(?P[-+]?)' + r'P' + r'(?:(?P\d+(.\d+)?)D)?' + r'(?:T' + r'(?:(?P\d+(.\d+)?)H)?' + r'(?:(?P\d+(.\d+)?)M)?' + r'(?:(?P\d+(.\d+)?)S)?' + r')?' + r'$' +) + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) +StrBytesIntFloat = Union[str, bytes, int, float] + + +def get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f'invalid type; expected {native_expected_type}, string, bytes, int or float') + + +def from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str], error: Type[Exception]) -> Union[None, int, timezone]: + if value == 'Z': + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == '-': + offset = -offset + try: + return timezone(timedelta(minutes=offset)) + except ValueError: + raise error() + else: + return None + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = get_numeric(value, 'date') + if number is not None: + return from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + match = date_re.match(value) # type: ignore + if match is None: + raise errors.DateError() + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise errors.DateError() + + +def parse_time(value: Union[time, StrBytesIntFloat]) -> time: + """ + Parse a time/string and return a datetime.time. + + Raise ValueError if the input is well formatted but not a valid time. + Raise ValueError if the input isn't well formatted, in particular if it contains an offset. + """ + if isinstance(value, time): + return value + + number = get_numeric(value, 'time') + if number is not None: + if number >= 86400: + # doesn't make sense since the time time loop back around to 0 + raise errors.TimeError() + return (datetime.min + timedelta(seconds=number)).time() + + if isinstance(value, bytes): + value = value.decode() + + match = time_re.match(value) # type: ignore + if match is None: + raise errors.TimeError() + + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + + tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.TimeError) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_['tzinfo'] = tzinfo + + try: + return time(**kw_) # type: ignore + except ValueError: + raise errors.TimeError() + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = get_numeric(value, 'datetime') + if number is not None: + return from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + match = datetime_re.match(value) # type: ignore + if match is None: + raise errors.DateTimeError() + + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + + tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.DateTimeError) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_['tzinfo'] = tzinfo + + try: + return datetime(**kw_) # type: ignore + except ValueError: + raise errors.DateTimeError() + + +def parse_duration(value: StrBytesIntFloat) -> timedelta: + """ + Parse a duration int/float/string and return a datetime.timedelta. + + The preferred format for durations in Django is '%d %H:%M:%S.%f'. + + Also supports ISO 8601 representation. + """ + if isinstance(value, timedelta): + return value + + if isinstance(value, (int, float)): + # below code requires a string + value = f'{value:f}' + elif isinstance(value, bytes): + value = value.decode() + + try: + match = standard_duration_re.match(value) or iso8601_duration_re.match(value) + except TypeError: + raise TypeError('invalid type; expected timedelta, string, bytes, int or float') + + if not match: + raise errors.DurationError() + + kw = match.groupdict() + sign = -1 if kw.pop('sign', '+') == '-' else 1 + if kw.get('microseconds'): + kw['microseconds'] = kw['microseconds'].ljust(6, '0') + + if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'): + kw['microseconds'] = '-' + kw['microseconds'] + + kw_ = {k: float(v) for k, v in kw.items() if v is not None} + + return sign * timedelta(**kw_) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/decorator.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/decorator.py new file mode 100644 index 0000000..089aab6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/decorator.py @@ -0,0 +1,264 @@ +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload + +from . import validator +from .config import Extra +from .errors import ConfigError +from .main import BaseModel, create_model +from .typing import get_all_type_hints +from .utils import to_camel + +__all__ = ('validate_arguments',) + +if TYPE_CHECKING: + from .typing import AnyCallable + + AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable) + ConfigType = Union[None, Type[Any], Dict[str, Any]] + + +@overload +def validate_arguments(func: None = None, *, config: 'ConfigType' = None) -> Callable[['AnyCallableT'], 'AnyCallableT']: + ... + + +@overload +def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT': + ... + + +def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any: + """ + Decorator to validate the arguments passed to a function. + """ + + def validate(_func: 'AnyCallable') -> 'AnyCallable': + vd = ValidatedFunction(_func, config) + + @wraps(_func) + def wrapper_function(*args: Any, **kwargs: Any) -> Any: + return vd.call(*args, **kwargs) + + wrapper_function.vd = vd # type: ignore + wrapper_function.validate = vd.init_model_instance # type: ignore + wrapper_function.raw_function = vd.raw_function # type: ignore + wrapper_function.model = vd.model # type: ignore + return wrapper_function + + if func: + return validate(func) + else: + return validate + + +ALT_V_ARGS = 'v__args' +ALT_V_KWARGS = 'v__kwargs' +V_POSITIONAL_ONLY_NAME = 'v__positional_only' +V_DUPLICATE_KWARGS = 'v__duplicate_kwargs' + + +class ValidatedFunction: + def __init__(self, function: 'AnyCallableT', config: 'ConfigType'): # noqa C901 + from inspect import Parameter, signature + + parameters: Mapping[str, Parameter] = signature(function).parameters + + if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}: + raise ConfigError( + f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" ' + f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator' + ) + + self.raw_function = function + self.arg_mapping: Dict[int, str] = {} + self.positional_only_args = set() + self.v_args_name = 'args' + self.v_kwargs_name = 'kwargs' + + type_hints = get_all_type_hints(function) + takes_args = False + takes_kwargs = False + fields: Dict[str, Tuple[Any, Any]] = {} + for i, (name, p) in enumerate(parameters.items()): + if p.annotation is p.empty: + annotation = Any + else: + annotation = type_hints[name] + + default = ... if p.default is p.empty else p.default + if p.kind == Parameter.POSITIONAL_ONLY: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_POSITIONAL_ONLY_NAME] = List[str], None + self.positional_only_args.add(name) + elif p.kind == Parameter.POSITIONAL_OR_KEYWORD: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_DUPLICATE_KWARGS] = List[str], None + elif p.kind == Parameter.KEYWORD_ONLY: + fields[name] = annotation, default + elif p.kind == Parameter.VAR_POSITIONAL: + self.v_args_name = name + fields[name] = Tuple[annotation, ...], None + takes_args = True + else: + assert p.kind == Parameter.VAR_KEYWORD, p.kind + self.v_kwargs_name = name + fields[name] = Dict[str, annotation], None # type: ignore + takes_kwargs = True + + # these checks avoid a clash between "args" and a field with that name + if not takes_args and self.v_args_name in fields: + self.v_args_name = ALT_V_ARGS + + # same with "kwargs" + if not takes_kwargs and self.v_kwargs_name in fields: + self.v_kwargs_name = ALT_V_KWARGS + + if not takes_args: + # we add the field so validation below can raise the correct exception + fields[self.v_args_name] = List[Any], None + + if not takes_kwargs: + # same with kwargs + fields[self.v_kwargs_name] = Dict[Any, Any], None + + self.create_model(fields, takes_args, takes_kwargs, config) + + def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel: + values = self.build_values(args, kwargs) + return self.model(**values) + + def call(self, *args: Any, **kwargs: Any) -> Any: + m = self.init_model_instance(*args, **kwargs) + return self.execute(m) + + def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[str, Any]: + values: Dict[str, Any] = {} + if args: + arg_iter = enumerate(args) + while True: + try: + i, a = next(arg_iter) + except StopIteration: + break + arg_name = self.arg_mapping.get(i) + if arg_name is not None: + values[arg_name] = a + else: + values[self.v_args_name] = [a] + [a for _, a in arg_iter] + break + + var_kwargs: Dict[str, Any] = {} + wrong_positional_args = [] + duplicate_kwargs = [] + fields_alias = [ + field.alias + for name, field in self.model.__fields__.items() + if name not in (self.v_args_name, self.v_kwargs_name) + ] + non_var_fields = set(self.model.__fields__) - {self.v_args_name, self.v_kwargs_name} + for k, v in kwargs.items(): + if k in non_var_fields or k in fields_alias: + if k in self.positional_only_args: + wrong_positional_args.append(k) + if k in values: + duplicate_kwargs.append(k) + values[k] = v + else: + var_kwargs[k] = v + + if var_kwargs: + values[self.v_kwargs_name] = var_kwargs + if wrong_positional_args: + values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args + if duplicate_kwargs: + values[V_DUPLICATE_KWARGS] = duplicate_kwargs + return values + + def execute(self, m: BaseModel) -> Any: + d = {k: v for k, v in m._iter() if k in m.__fields_set__ or m.__fields__[k].default_factory} + var_kwargs = d.pop(self.v_kwargs_name, {}) + + if self.v_args_name in d: + args_: List[Any] = [] + in_kwargs = False + kwargs = {} + for name, value in d.items(): + if in_kwargs: + kwargs[name] = value + elif name == self.v_args_name: + args_ += value + in_kwargs = True + else: + args_.append(value) + return self.raw_function(*args_, **kwargs, **var_kwargs) + elif self.positional_only_args: + args_ = [] + kwargs = {} + for name, value in d.items(): + if name in self.positional_only_args: + args_.append(value) + else: + kwargs[name] = value + return self.raw_function(*args_, **kwargs, **var_kwargs) + else: + return self.raw_function(**d, **var_kwargs) + + def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None: + pos_args = len(self.arg_mapping) + + class CustomConfig: + pass + + if not TYPE_CHECKING: # pragma: no branch + if isinstance(config, dict): + CustomConfig = type('Config', (), config) # noqa: F811 + elif config is not None: + CustomConfig = config # noqa: F811 + + if hasattr(CustomConfig, 'fields') or hasattr(CustomConfig, 'alias_generator'): + raise ConfigError( + 'Setting the "fields" and "alias_generator" property on custom Config for ' + '@validate_arguments is not yet supported, please remove.' + ) + + class DecoratorBaseModel(BaseModel): + @validator(self.v_args_name, check_fields=False, allow_reuse=True) + def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]: + if takes_args or v is None: + return v + + raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given') + + @validator(self.v_kwargs_name, check_fields=False, allow_reuse=True) + def check_kwargs(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: + if takes_kwargs or v is None: + return v + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v.keys())) + raise TypeError(f'unexpected keyword argument{plural}: {keys}') + + @validator(V_POSITIONAL_ONLY_NAME, check_fields=False, allow_reuse=True) + def check_positional_only(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}') + + @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True) + def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'multiple values for argument{plural}: {keys}') + + class Config(CustomConfig): + extra = getattr(CustomConfig, 'extra', Extra.forbid) + + self.model = create_model(to_camel(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/env_settings.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/env_settings.py new file mode 100644 index 0000000..6c446e5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/env_settings.py @@ -0,0 +1,350 @@ +import os +import warnings +from pathlib import Path +from typing import AbstractSet, Any, Callable, ClassVar, Dict, List, Mapping, Optional, Tuple, Type, Union + +from .config import BaseConfig, Extra +from .fields import ModelField +from .main import BaseModel +from .types import JsonWrapper +from .typing import StrPath, display_as_type, get_origin, is_union +from .utils import deep_update, lenient_issubclass, path_type, sequence_like + +env_file_sentinel = str(object()) + +SettingsSourceCallable = Callable[['BaseSettings'], Dict[str, Any]] +DotenvType = Union[StrPath, List[StrPath], Tuple[StrPath, ...]] + + +class SettingsError(ValueError): + pass + + +class BaseSettings(BaseModel): + """ + Base class for settings, allowing values to be overridden by environment variables. + + This is useful in production for secrets you do not wish to save in code, it plays nicely with docker(-compose), + Heroku and any 12 factor app design. + """ + + def __init__( + __pydantic_self__, + _env_file: Optional[DotenvType] = env_file_sentinel, + _env_file_encoding: Optional[str] = None, + _env_nested_delimiter: Optional[str] = None, + _secrets_dir: Optional[StrPath] = None, + **values: Any, + ) -> None: + # Uses something other than `self` the first arg to allow "self" as a settable attribute + super().__init__( + **__pydantic_self__._build_values( + values, + _env_file=_env_file, + _env_file_encoding=_env_file_encoding, + _env_nested_delimiter=_env_nested_delimiter, + _secrets_dir=_secrets_dir, + ) + ) + + def _build_values( + self, + init_kwargs: Dict[str, Any], + _env_file: Optional[DotenvType] = None, + _env_file_encoding: Optional[str] = None, + _env_nested_delimiter: Optional[str] = None, + _secrets_dir: Optional[StrPath] = None, + ) -> Dict[str, Any]: + # Configure built-in sources + init_settings = InitSettingsSource(init_kwargs=init_kwargs) + env_settings = EnvSettingsSource( + env_file=(_env_file if _env_file != env_file_sentinel else self.__config__.env_file), + env_file_encoding=( + _env_file_encoding if _env_file_encoding is not None else self.__config__.env_file_encoding + ), + env_nested_delimiter=( + _env_nested_delimiter if _env_nested_delimiter is not None else self.__config__.env_nested_delimiter + ), + env_prefix_len=len(self.__config__.env_prefix), + ) + file_secret_settings = SecretsSettingsSource(secrets_dir=_secrets_dir or self.__config__.secrets_dir) + # Provide a hook to set built-in sources priority and add / remove sources + sources = self.__config__.customise_sources( + init_settings=init_settings, env_settings=env_settings, file_secret_settings=file_secret_settings + ) + if sources: + return deep_update(*reversed([source(self) for source in sources])) + else: + # no one should mean to do this, but I think returning an empty dict is marginally preferable + # to an informative error and much better than a confusing error + return {} + + class Config(BaseConfig): + env_prefix: str = '' + env_file: Optional[DotenvType] = None + env_file_encoding: Optional[str] = None + env_nested_delimiter: Optional[str] = None + secrets_dir: Optional[StrPath] = None + validate_all: bool = True + extra: Extra = Extra.forbid + arbitrary_types_allowed: bool = True + case_sensitive: bool = False + + @classmethod + def prepare_field(cls, field: ModelField) -> None: + env_names: Union[List[str], AbstractSet[str]] + field_info_from_config = cls.get_field_info(field.name) + + env = field_info_from_config.get('env') or field.field_info.extra.get('env') + if env is None: + if field.has_alias: + warnings.warn( + 'aliases are no longer used by BaseSettings to define which environment variables to read. ' + 'Instead use the "env" field setting. ' + 'See https://pydantic-docs.helpmanual.io/usage/settings/#environment-variable-names', + FutureWarning, + ) + env_names = {cls.env_prefix + field.name} + elif isinstance(env, str): + env_names = {env} + elif isinstance(env, (set, frozenset)): + env_names = env + elif sequence_like(env): + env_names = list(env) + else: + raise TypeError(f'invalid field env: {env!r} ({display_as_type(env)}); should be string, list or set') + + if not cls.case_sensitive: + env_names = env_names.__class__(n.lower() for n in env_names) + field.field_info.extra['env_names'] = env_names + + @classmethod + def customise_sources( + cls, + init_settings: SettingsSourceCallable, + env_settings: SettingsSourceCallable, + file_secret_settings: SettingsSourceCallable, + ) -> Tuple[SettingsSourceCallable, ...]: + return init_settings, env_settings, file_secret_settings + + @classmethod + def parse_env_var(cls, field_name: str, raw_val: str) -> Any: + return cls.json_loads(raw_val) + + # populated by the metaclass using the Config class defined above, annotated here to help IDEs only + __config__: ClassVar[Type[Config]] + + +class InitSettingsSource: + __slots__ = ('init_kwargs',) + + def __init__(self, init_kwargs: Dict[str, Any]): + self.init_kwargs = init_kwargs + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: + return self.init_kwargs + + def __repr__(self) -> str: + return f'InitSettingsSource(init_kwargs={self.init_kwargs!r})' + + +class EnvSettingsSource: + __slots__ = ('env_file', 'env_file_encoding', 'env_nested_delimiter', 'env_prefix_len') + + def __init__( + self, + env_file: Optional[DotenvType], + env_file_encoding: Optional[str], + env_nested_delimiter: Optional[str] = None, + env_prefix_len: int = 0, + ): + self.env_file: Optional[DotenvType] = env_file + self.env_file_encoding: Optional[str] = env_file_encoding + self.env_nested_delimiter: Optional[str] = env_nested_delimiter + self.env_prefix_len: int = env_prefix_len + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: # noqa C901 + """ + Build environment variables suitable for passing to the Model. + """ + d: Dict[str, Any] = {} + + if settings.__config__.case_sensitive: + env_vars: Mapping[str, Optional[str]] = os.environ + else: + env_vars = {k.lower(): v for k, v in os.environ.items()} + + dotenv_vars = self._read_env_files(settings.__config__.case_sensitive) + if dotenv_vars: + env_vars = {**dotenv_vars, **env_vars} + + for field in settings.__fields__.values(): + env_val: Optional[str] = None + for env_name in field.field_info.extra['env_names']: + env_val = env_vars.get(env_name) + if env_val is not None: + break + + is_complex, allow_parse_failure = self.field_is_complex(field) + if is_complex: + if env_val is None: + # field is complex but no value found so far, try explode_env_vars + env_val_built = self.explode_env_vars(field, env_vars) + if env_val_built: + d[field.alias] = env_val_built + else: + # field is complex and there's a value, decode that as JSON, then add explode_env_vars + try: + env_val = settings.__config__.parse_env_var(field.name, env_val) + except ValueError as e: + if not allow_parse_failure: + raise SettingsError(f'error parsing env var "{env_name}"') from e + + if isinstance(env_val, dict): + d[field.alias] = deep_update(env_val, self.explode_env_vars(field, env_vars)) + else: + d[field.alias] = env_val + elif env_val is not None: + # simplest case, field is not complex, we only need to add the value if it was found + d[field.alias] = env_val + + return d + + def _read_env_files(self, case_sensitive: bool) -> Dict[str, Optional[str]]: + env_files = self.env_file + if env_files is None: + return {} + + if isinstance(env_files, (str, os.PathLike)): + env_files = [env_files] + + dotenv_vars = {} + for env_file in env_files: + env_path = Path(env_file).expanduser() + if env_path.is_file(): + dotenv_vars.update( + read_env_file(env_path, encoding=self.env_file_encoding, case_sensitive=case_sensitive) + ) + + return dotenv_vars + + def field_is_complex(self, field: ModelField) -> Tuple[bool, bool]: + """ + Find out if a field is complex, and if so whether JSON errors should be ignored + """ + if lenient_issubclass(field.annotation, JsonWrapper): + return False, False + + if field.is_complex(): + allow_parse_failure = False + elif is_union(get_origin(field.type_)) and field.sub_fields and any(f.is_complex() for f in field.sub_fields): + allow_parse_failure = True + else: + return False, False + + return True, allow_parse_failure + + def explode_env_vars(self, field: ModelField, env_vars: Mapping[str, Optional[str]]) -> Dict[str, Any]: + """ + Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries. + + This is applied to a single field, hence filtering by env_var prefix. + """ + prefixes = [f'{env_name}{self.env_nested_delimiter}' for env_name in field.field_info.extra['env_names']] + result: Dict[str, Any] = {} + for env_name, env_val in env_vars.items(): + if not any(env_name.startswith(prefix) for prefix in prefixes): + continue + # we remove the prefix before splitting in case the prefix has characters in common with the delimiter + env_name_without_prefix = env_name[self.env_prefix_len :] + _, *keys, last_key = env_name_without_prefix.split(self.env_nested_delimiter) + env_var = result + for key in keys: + env_var = env_var.setdefault(key, {}) + env_var[last_key] = env_val + + return result + + def __repr__(self) -> str: + return ( + f'EnvSettingsSource(env_file={self.env_file!r}, env_file_encoding={self.env_file_encoding!r}, ' + f'env_nested_delimiter={self.env_nested_delimiter!r})' + ) + + +class SecretsSettingsSource: + __slots__ = ('secrets_dir',) + + def __init__(self, secrets_dir: Optional[StrPath]): + self.secrets_dir: Optional[StrPath] = secrets_dir + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: + """ + Build fields from "secrets" files. + """ + secrets: Dict[str, Optional[str]] = {} + + if self.secrets_dir is None: + return secrets + + secrets_path = Path(self.secrets_dir).expanduser() + + if not secrets_path.exists(): + warnings.warn(f'directory "{secrets_path}" does not exist') + return secrets + + if not secrets_path.is_dir(): + raise SettingsError(f'secrets_dir must reference a directory, not a {path_type(secrets_path)}') + + for field in settings.__fields__.values(): + for env_name in field.field_info.extra['env_names']: + path = find_case_path(secrets_path, env_name, settings.__config__.case_sensitive) + if not path: + # path does not exist, we currently don't return a warning for this + continue + + if path.is_file(): + secret_value = path.read_text().strip() + if field.is_complex(): + try: + secret_value = settings.__config__.parse_env_var(field.name, secret_value) + except ValueError as e: + raise SettingsError(f'error parsing env var "{env_name}"') from e + + secrets[field.alias] = secret_value + else: + warnings.warn( + f'attempted to load secret file "{path}" but found a {path_type(path)} instead.', + stacklevel=4, + ) + return secrets + + def __repr__(self) -> str: + return f'SecretsSettingsSource(secrets_dir={self.secrets_dir!r})' + + +def read_env_file( + file_path: StrPath, *, encoding: str = None, case_sensitive: bool = False +) -> Dict[str, Optional[str]]: + try: + from dotenv import dotenv_values + except ImportError as e: + raise ImportError('python-dotenv is not installed, run `pip install pydantic[dotenv]`') from e + + file_vars: Dict[str, Optional[str]] = dotenv_values(file_path, encoding=encoding or 'utf8') + if not case_sensitive: + return {k.lower(): v for k, v in file_vars.items()} + else: + return file_vars + + +def find_case_path(dir_path: Path, file_name: str, case_sensitive: bool) -> Optional[Path]: + """ + Find a file within path's directory matching filename, optionally ignoring case. + """ + for f in dir_path.iterdir(): + if f.name == file_name: + return f + elif not case_sensitive and f.name.lower() == file_name.lower(): + return f + return None diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/error_wrappers.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/error_wrappers.py new file mode 100644 index 0000000..d89a500 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/error_wrappers.py @@ -0,0 +1,161 @@ +import json +from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Sequence, Tuple, Type, Union + +from .json import pydantic_encoder +from .utils import Representation + +if TYPE_CHECKING: + from typing_extensions import TypedDict + + from .config import BaseConfig + from .types import ModelOrDc + from .typing import ReprArgs + + Loc = Tuple[Union[int, str], ...] + + class _ErrorDictRequired(TypedDict): + loc: Loc + msg: str + type: str + + class ErrorDict(_ErrorDictRequired, total=False): + ctx: Dict[str, Any] + + +__all__ = 'ErrorWrapper', 'ValidationError' + + +class ErrorWrapper(Representation): + __slots__ = 'exc', '_loc' + + def __init__(self, exc: Exception, loc: Union[str, 'Loc']) -> None: + self.exc = exc + self._loc = loc + + def loc_tuple(self) -> 'Loc': + if isinstance(self._loc, tuple): + return self._loc + else: + return (self._loc,) + + def __repr_args__(self) -> 'ReprArgs': + return [('exc', self.exc), ('loc', self.loc_tuple())] + + +# ErrorList is something like Union[List[Union[List[ErrorWrapper], ErrorWrapper]], ErrorWrapper] +# but recursive, therefore just use: +ErrorList = Union[Sequence[Any], ErrorWrapper] + + +class ValidationError(Representation, ValueError): + __slots__ = 'raw_errors', 'model', '_error_cache' + + def __init__(self, errors: Sequence[ErrorList], model: 'ModelOrDc') -> None: + self.raw_errors = errors + self.model = model + self._error_cache: Optional[List['ErrorDict']] = None + + def errors(self) -> List['ErrorDict']: + if self._error_cache is None: + try: + config = self.model.__config__ # type: ignore + except AttributeError: + config = self.model.__pydantic_model__.__config__ # type: ignore + self._error_cache = list(flatten_errors(self.raw_errors, config)) + return self._error_cache + + def json(self, *, indent: Union[None, int, str] = 2) -> str: + return json.dumps(self.errors(), indent=indent, default=pydantic_encoder) + + def __str__(self) -> str: + errors = self.errors() + no_errors = len(errors) + return ( + f'{no_errors} validation error{"" if no_errors == 1 else "s"} for {self.model.__name__}\n' + f'{display_errors(errors)}' + ) + + def __repr_args__(self) -> 'ReprArgs': + return [('model', self.model.__name__), ('errors', self.errors())] + + +def display_errors(errors: List['ErrorDict']) -> str: + return '\n'.join(f'{_display_error_loc(e)}\n {e["msg"]} ({_display_error_type_and_ctx(e)})' for e in errors) + + +def _display_error_loc(error: 'ErrorDict') -> str: + return ' -> '.join(str(e) for e in error['loc']) + + +def _display_error_type_and_ctx(error: 'ErrorDict') -> str: + t = 'type=' + error['type'] + ctx = error.get('ctx') + if ctx: + return t + ''.join(f'; {k}={v}' for k, v in ctx.items()) + else: + return t + + +def flatten_errors( + errors: Sequence[Any], config: Type['BaseConfig'], loc: Optional['Loc'] = None +) -> Generator['ErrorDict', None, None]: + for error in errors: + if isinstance(error, ErrorWrapper): + if loc: + error_loc = loc + error.loc_tuple() + else: + error_loc = error.loc_tuple() + + if isinstance(error.exc, ValidationError): + yield from flatten_errors(error.exc.raw_errors, config, error_loc) + else: + yield error_dict(error.exc, config, error_loc) + elif isinstance(error, list): + yield from flatten_errors(error, config, loc=loc) + else: + raise RuntimeError(f'Unknown error object: {error}') + + +def error_dict(exc: Exception, config: Type['BaseConfig'], loc: 'Loc') -> 'ErrorDict': + type_ = get_exc_type(exc.__class__) + msg_template = config.error_msg_templates.get(type_) or getattr(exc, 'msg_template', None) + ctx = exc.__dict__ + if msg_template: + msg = msg_template.format(**ctx) + else: + msg = str(exc) + + d: 'ErrorDict' = {'loc': loc, 'msg': msg, 'type': type_} + + if ctx: + d['ctx'] = ctx + + return d + + +_EXC_TYPE_CACHE: Dict[Type[Exception], str] = {} + + +def get_exc_type(cls: Type[Exception]) -> str: + # slightly more efficient than using lru_cache since we don't need to worry about the cache filling up + try: + return _EXC_TYPE_CACHE[cls] + except KeyError: + r = _get_exc_type(cls) + _EXC_TYPE_CACHE[cls] = r + return r + + +def _get_exc_type(cls: Type[Exception]) -> str: + if issubclass(cls, AssertionError): + return 'assertion_error' + + base_name = 'type_error' if issubclass(cls, TypeError) else 'value_error' + if cls in (TypeError, ValueError): + # just TypeError or ValueError, no extra code + return base_name + + # if it's not a TypeError or ValueError, we just take the lowercase of the exception name + # no chaining or snake case logic, use "code" for more complex error types. + code = getattr(cls, 'code', None) or cls.__name__.replace('Error', '').lower() + return base_name + '.' + code diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/errors.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/errors.py new file mode 100644 index 0000000..7bdafdd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/errors.py @@ -0,0 +1,646 @@ +from decimal import Decimal +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable, Sequence, Set, Tuple, Type, Union + +from .typing import display_as_type + +if TYPE_CHECKING: + from .typing import DictStrAny + +# explicitly state exports to avoid "from .errors import *" also importing Decimal, Path etc. +__all__ = ( + 'PydanticTypeError', + 'PydanticValueError', + 'ConfigError', + 'MissingError', + 'ExtraError', + 'NoneIsNotAllowedError', + 'NoneIsAllowedError', + 'WrongConstantError', + 'NotNoneError', + 'BoolError', + 'BytesError', + 'DictError', + 'EmailError', + 'UrlError', + 'UrlSchemeError', + 'UrlSchemePermittedError', + 'UrlUserInfoError', + 'UrlHostError', + 'UrlHostTldError', + 'UrlPortError', + 'UrlExtraError', + 'EnumError', + 'IntEnumError', + 'EnumMemberError', + 'IntegerError', + 'FloatError', + 'PathError', + 'PathNotExistsError', + 'PathNotAFileError', + 'PathNotADirectoryError', + 'PyObjectError', + 'SequenceError', + 'ListError', + 'SetError', + 'FrozenSetError', + 'TupleError', + 'TupleLengthError', + 'ListMinLengthError', + 'ListMaxLengthError', + 'ListUniqueItemsError', + 'SetMinLengthError', + 'SetMaxLengthError', + 'FrozenSetMinLengthError', + 'FrozenSetMaxLengthError', + 'AnyStrMinLengthError', + 'AnyStrMaxLengthError', + 'StrError', + 'StrRegexError', + 'NumberNotGtError', + 'NumberNotGeError', + 'NumberNotLtError', + 'NumberNotLeError', + 'NumberNotMultipleError', + 'DecimalError', + 'DecimalIsNotFiniteError', + 'DecimalMaxDigitsError', + 'DecimalMaxPlacesError', + 'DecimalWholeDigitsError', + 'DateTimeError', + 'DateError', + 'DateNotInThePastError', + 'DateNotInTheFutureError', + 'TimeError', + 'DurationError', + 'HashableError', + 'UUIDError', + 'UUIDVersionError', + 'ArbitraryTypeError', + 'ClassError', + 'SubclassError', + 'JsonError', + 'JsonTypeError', + 'PatternError', + 'DataclassTypeError', + 'CallableError', + 'IPvAnyAddressError', + 'IPvAnyInterfaceError', + 'IPvAnyNetworkError', + 'IPv4AddressError', + 'IPv6AddressError', + 'IPv4NetworkError', + 'IPv6NetworkError', + 'IPv4InterfaceError', + 'IPv6InterfaceError', + 'ColorError', + 'StrictBoolError', + 'NotDigitError', + 'LuhnValidationError', + 'InvalidLengthForBrand', + 'InvalidByteSize', + 'InvalidByteSizeUnit', + 'MissingDiscriminator', + 'InvalidDiscriminator', +) + + +def cls_kwargs(cls: Type['PydanticErrorMixin'], ctx: 'DictStrAny') -> 'PydanticErrorMixin': + """ + For built-in exceptions like ValueError or TypeError, we need to implement + __reduce__ to override the default behaviour (instead of __getstate__/__setstate__) + By default pickle protocol 2 calls `cls.__new__(cls, *args)`. + Since we only use kwargs, we need a little constructor to change that. + Note: the callable can't be a lambda as pickle looks in the namespace to find it + """ + return cls(**ctx) + + +class PydanticErrorMixin: + code: str + msg_template: str + + def __init__(self, **ctx: Any) -> None: + self.__dict__ = ctx + + def __str__(self) -> str: + return self.msg_template.format(**self.__dict__) + + def __reduce__(self) -> Tuple[Callable[..., 'PydanticErrorMixin'], Tuple[Type['PydanticErrorMixin'], 'DictStrAny']]: + return cls_kwargs, (self.__class__, self.__dict__) + + +class PydanticTypeError(PydanticErrorMixin, TypeError): + pass + + +class PydanticValueError(PydanticErrorMixin, ValueError): + pass + + +class ConfigError(RuntimeError): + pass + + +class MissingError(PydanticValueError): + msg_template = 'field required' + + +class ExtraError(PydanticValueError): + msg_template = 'extra fields not permitted' + + +class NoneIsNotAllowedError(PydanticTypeError): + code = 'none.not_allowed' + msg_template = 'none is not an allowed value' + + +class NoneIsAllowedError(PydanticTypeError): + code = 'none.allowed' + msg_template = 'value is not none' + + +class WrongConstantError(PydanticValueError): + code = 'const' + + def __str__(self) -> str: + permitted = ', '.join(repr(v) for v in self.permitted) # type: ignore + return f'unexpected value; permitted: {permitted}' + + +class NotNoneError(PydanticTypeError): + code = 'not_none' + msg_template = 'value is not None' + + +class BoolError(PydanticTypeError): + msg_template = 'value could not be parsed to a boolean' + + +class BytesError(PydanticTypeError): + msg_template = 'byte type expected' + + +class DictError(PydanticTypeError): + msg_template = 'value is not a valid dict' + + +class EmailError(PydanticValueError): + msg_template = 'value is not a valid email address' + + +class UrlError(PydanticValueError): + code = 'url' + + +class UrlSchemeError(UrlError): + code = 'url.scheme' + msg_template = 'invalid or missing URL scheme' + + +class UrlSchemePermittedError(UrlError): + code = 'url.scheme' + msg_template = 'URL scheme not permitted' + + def __init__(self, allowed_schemes: Set[str]): + super().__init__(allowed_schemes=allowed_schemes) + + +class UrlUserInfoError(UrlError): + code = 'url.userinfo' + msg_template = 'userinfo required in URL but missing' + + +class UrlHostError(UrlError): + code = 'url.host' + msg_template = 'URL host invalid' + + +class UrlHostTldError(UrlError): + code = 'url.host' + msg_template = 'URL host invalid, top level domain required' + + +class UrlPortError(UrlError): + code = 'url.port' + msg_template = 'URL port invalid, port cannot exceed 65535' + + +class UrlExtraError(UrlError): + code = 'url.extra' + msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}' + + +class EnumMemberError(PydanticTypeError): + code = 'enum' + + def __str__(self) -> str: + permitted = ', '.join(repr(v.value) for v in self.enum_values) # type: ignore + return f'value is not a valid enumeration member; permitted: {permitted}' + + +class IntegerError(PydanticTypeError): + msg_template = 'value is not a valid integer' + + +class FloatError(PydanticTypeError): + msg_template = 'value is not a valid float' + + +class PathError(PydanticTypeError): + msg_template = 'value is not a valid path' + + +class _PathValueError(PydanticValueError): + def __init__(self, *, path: Path) -> None: + super().__init__(path=str(path)) + + +class PathNotExistsError(_PathValueError): + code = 'path.not_exists' + msg_template = 'file or directory at path "{path}" does not exist' + + +class PathNotAFileError(_PathValueError): + code = 'path.not_a_file' + msg_template = 'path "{path}" does not point to a file' + + +class PathNotADirectoryError(_PathValueError): + code = 'path.not_a_directory' + msg_template = 'path "{path}" does not point to a directory' + + +class PyObjectError(PydanticTypeError): + msg_template = 'ensure this value contains valid import path or valid callable: {error_message}' + + +class SequenceError(PydanticTypeError): + msg_template = 'value is not a valid sequence' + + +class IterableError(PydanticTypeError): + msg_template = 'value is not a valid iterable' + + +class ListError(PydanticTypeError): + msg_template = 'value is not a valid list' + + +class SetError(PydanticTypeError): + msg_template = 'value is not a valid set' + + +class FrozenSetError(PydanticTypeError): + msg_template = 'value is not a valid frozenset' + + +class DequeError(PydanticTypeError): + msg_template = 'value is not a valid deque' + + +class TupleError(PydanticTypeError): + msg_template = 'value is not a valid tuple' + + +class TupleLengthError(PydanticValueError): + code = 'tuple.length' + msg_template = 'wrong tuple length {actual_length}, expected {expected_length}' + + def __init__(self, *, actual_length: int, expected_length: int) -> None: + super().__init__(actual_length=actual_length, expected_length=expected_length) + + +class ListMinLengthError(PydanticValueError): + code = 'list.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class ListMaxLengthError(PydanticValueError): + code = 'list.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class ListUniqueItemsError(PydanticValueError): + code = 'list.unique_items' + msg_template = 'the list has duplicated items' + + +class SetMinLengthError(PydanticValueError): + code = 'set.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class SetMaxLengthError(PydanticValueError): + code = 'set.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class FrozenSetMinLengthError(PydanticValueError): + code = 'frozenset.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class FrozenSetMaxLengthError(PydanticValueError): + code = 'frozenset.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class AnyStrMinLengthError(PydanticValueError): + code = 'any_str.min_length' + msg_template = 'ensure this value has at least {limit_value} characters' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class AnyStrMaxLengthError(PydanticValueError): + code = 'any_str.max_length' + msg_template = 'ensure this value has at most {limit_value} characters' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class StrError(PydanticTypeError): + msg_template = 'str type expected' + + +class StrRegexError(PydanticValueError): + code = 'str.regex' + msg_template = 'string does not match regex "{pattern}"' + + def __init__(self, *, pattern: str) -> None: + super().__init__(pattern=pattern) + + +class _NumberBoundError(PydanticValueError): + def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None: + super().__init__(limit_value=limit_value) + + +class NumberNotGtError(_NumberBoundError): + code = 'number.not_gt' + msg_template = 'ensure this value is greater than {limit_value}' + + +class NumberNotGeError(_NumberBoundError): + code = 'number.not_ge' + msg_template = 'ensure this value is greater than or equal to {limit_value}' + + +class NumberNotLtError(_NumberBoundError): + code = 'number.not_lt' + msg_template = 'ensure this value is less than {limit_value}' + + +class NumberNotLeError(_NumberBoundError): + code = 'number.not_le' + msg_template = 'ensure this value is less than or equal to {limit_value}' + + +class NumberNotFiniteError(PydanticValueError): + code = 'number.not_finite_number' + msg_template = 'ensure this value is a finite number' + + +class NumberNotMultipleError(PydanticValueError): + code = 'number.not_multiple' + msg_template = 'ensure this value is a multiple of {multiple_of}' + + def __init__(self, *, multiple_of: Union[int, float, Decimal]) -> None: + super().__init__(multiple_of=multiple_of) + + +class DecimalError(PydanticTypeError): + msg_template = 'value is not a valid decimal' + + +class DecimalIsNotFiniteError(PydanticValueError): + code = 'decimal.not_finite' + msg_template = 'value is not a valid decimal' + + +class DecimalMaxDigitsError(PydanticValueError): + code = 'decimal.max_digits' + msg_template = 'ensure that there are no more than {max_digits} digits in total' + + def __init__(self, *, max_digits: int) -> None: + super().__init__(max_digits=max_digits) + + +class DecimalMaxPlacesError(PydanticValueError): + code = 'decimal.max_places' + msg_template = 'ensure that there are no more than {decimal_places} decimal places' + + def __init__(self, *, decimal_places: int) -> None: + super().__init__(decimal_places=decimal_places) + + +class DecimalWholeDigitsError(PydanticValueError): + code = 'decimal.whole_digits' + msg_template = 'ensure that there are no more than {whole_digits} digits before the decimal point' + + def __init__(self, *, whole_digits: int) -> None: + super().__init__(whole_digits=whole_digits) + + +class DateTimeError(PydanticValueError): + msg_template = 'invalid datetime format' + + +class DateError(PydanticValueError): + msg_template = 'invalid date format' + + +class DateNotInThePastError(PydanticValueError): + code = 'date.not_in_the_past' + msg_template = 'date is not in the past' + + +class DateNotInTheFutureError(PydanticValueError): + code = 'date.not_in_the_future' + msg_template = 'date is not in the future' + + +class TimeError(PydanticValueError): + msg_template = 'invalid time format' + + +class DurationError(PydanticValueError): + msg_template = 'invalid duration format' + + +class HashableError(PydanticTypeError): + msg_template = 'value is not a valid hashable' + + +class UUIDError(PydanticTypeError): + msg_template = 'value is not a valid uuid' + + +class UUIDVersionError(PydanticValueError): + code = 'uuid.version' + msg_template = 'uuid version {required_version} expected' + + def __init__(self, *, required_version: int) -> None: + super().__init__(required_version=required_version) + + +class ArbitraryTypeError(PydanticTypeError): + code = 'arbitrary_type' + msg_template = 'instance of {expected_arbitrary_type} expected' + + def __init__(self, *, expected_arbitrary_type: Type[Any]) -> None: + super().__init__(expected_arbitrary_type=display_as_type(expected_arbitrary_type)) + + +class ClassError(PydanticTypeError): + code = 'class' + msg_template = 'a class is expected' + + +class SubclassError(PydanticTypeError): + code = 'subclass' + msg_template = 'subclass of {expected_class} expected' + + def __init__(self, *, expected_class: Type[Any]) -> None: + super().__init__(expected_class=display_as_type(expected_class)) + + +class JsonError(PydanticValueError): + msg_template = 'Invalid JSON' + + +class JsonTypeError(PydanticTypeError): + code = 'json' + msg_template = 'JSON object must be str, bytes or bytearray' + + +class PatternError(PydanticValueError): + code = 'regex_pattern' + msg_template = 'Invalid regular expression' + + +class DataclassTypeError(PydanticTypeError): + code = 'dataclass' + msg_template = 'instance of {class_name}, tuple or dict expected' + + +class CallableError(PydanticTypeError): + msg_template = '{value} is not callable' + + +class EnumError(PydanticTypeError): + code = 'enum_instance' + msg_template = '{value} is not a valid Enum instance' + + +class IntEnumError(PydanticTypeError): + code = 'int_enum_instance' + msg_template = '{value} is not a valid IntEnum instance' + + +class IPvAnyAddressError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 address' + + +class IPvAnyInterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 interface' + + +class IPvAnyNetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 network' + + +class IPv4AddressError(PydanticValueError): + msg_template = 'value is not a valid IPv4 address' + + +class IPv6AddressError(PydanticValueError): + msg_template = 'value is not a valid IPv6 address' + + +class IPv4NetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv4 network' + + +class IPv6NetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv6 network' + + +class IPv4InterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv4 interface' + + +class IPv6InterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv6 interface' + + +class ColorError(PydanticValueError): + msg_template = 'value is not a valid color: {reason}' + + +class StrictBoolError(PydanticValueError): + msg_template = 'value is not a valid boolean' + + +class NotDigitError(PydanticValueError): + code = 'payment_card_number.digits' + msg_template = 'card number is not all digits' + + +class LuhnValidationError(PydanticValueError): + code = 'payment_card_number.luhn_check' + msg_template = 'card number is not luhn valid' + + +class InvalidLengthForBrand(PydanticValueError): + code = 'payment_card_number.invalid_length_for_brand' + msg_template = 'Length for a {brand} card must be {required_length}' + + +class InvalidByteSize(PydanticValueError): + msg_template = 'could not parse value and unit from byte string' + + +class InvalidByteSizeUnit(PydanticValueError): + msg_template = 'could not interpret byte unit: {unit}' + + +class MissingDiscriminator(PydanticValueError): + code = 'discriminated_union.missing_discriminator' + msg_template = 'Discriminator {discriminator_key!r} is missing in value' + + +class InvalidDiscriminator(PydanticValueError): + code = 'discriminated_union.invalid_discriminator' + msg_template = ( + 'No match for discriminator {discriminator_key!r} and value {discriminator_value!r} ' + '(allowed values: {allowed_values})' + ) + + def __init__(self, *, discriminator_key: str, discriminator_value: Any, allowed_values: Sequence[Any]) -> None: + super().__init__( + discriminator_key=discriminator_key, + discriminator_value=discriminator_value, + allowed_values=', '.join(map(repr, allowed_values)), + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/fields.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/fields.py new file mode 100644 index 0000000..60d260e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/fields.py @@ -0,0 +1,1253 @@ +import copy +import re +from collections import Counter as CollectionCounter, defaultdict, deque +from collections.abc import Callable, Hashable as CollectionsHashable, Iterable as CollectionsIterable +from typing import ( + TYPE_CHECKING, + Any, + Counter, + DefaultDict, + Deque, + Dict, + ForwardRef, + FrozenSet, + Generator, + Iterable, + Iterator, + List, + Mapping, + Optional, + Pattern, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from typing_extensions import Annotated, Final + +from . import errors as errors_ +from .class_validators import Validator, make_generic_validator, prep_validators +from .error_wrappers import ErrorWrapper +from .errors import ConfigError, InvalidDiscriminator, MissingDiscriminator, NoneIsNotAllowedError +from .types import Json, JsonWrapper +from .typing import ( + NoArgAnyCallable, + convert_generics, + display_as_type, + get_args, + get_origin, + is_finalvar, + is_literal_type, + is_new_type, + is_none_type, + is_typeddict, + is_typeddict_special, + is_union, + new_type_supertype, +) +from .utils import ( + PyObjectStr, + Representation, + ValueItems, + get_discriminator_alias_and_values, + get_unique_discriminator_alias, + lenient_isinstance, + lenient_issubclass, + sequence_like, + smart_deepcopy, +) +from .validators import constant_validator, dict_validator, find_validators, validate_json + +Required: Any = Ellipsis + +T = TypeVar('T') + + +class UndefinedType: + def __repr__(self) -> str: + return 'PydanticUndefined' + + def __copy__(self: T) -> T: + return self + + def __reduce__(self) -> str: + return 'Undefined' + + def __deepcopy__(self: T, _: Any) -> T: + return self + + +Undefined = UndefinedType() + +if TYPE_CHECKING: + from .class_validators import ValidatorsList + from .config import BaseConfig + from .error_wrappers import ErrorList + from .types import ModelOrDc + from .typing import AbstractSetIntStr, MappingIntStrAny, ReprArgs + + ValidateReturn = Tuple[Optional[Any], Optional[ErrorList]] + LocStr = Union[Tuple[Union[int, str], ...], str] + BoolUndefined = Union[bool, UndefinedType] + + +class FieldInfo(Representation): + """ + Captures extra information about a field. + """ + + __slots__ = ( + 'default', + 'default_factory', + 'alias', + 'alias_priority', + 'title', + 'description', + 'exclude', + 'include', + 'const', + 'gt', + 'ge', + 'lt', + 'le', + 'multiple_of', + 'allow_inf_nan', + 'max_digits', + 'decimal_places', + 'min_items', + 'max_items', + 'unique_items', + 'min_length', + 'max_length', + 'allow_mutation', + 'repr', + 'regex', + 'discriminator', + 'extra', + ) + + # field constraints with the default value, it's also used in update_from_config below + __field_constraints__ = { + 'min_length': None, + 'max_length': None, + 'regex': None, + 'gt': None, + 'lt': None, + 'ge': None, + 'le': None, + 'multiple_of': None, + 'allow_inf_nan': None, + 'max_digits': None, + 'decimal_places': None, + 'min_items': None, + 'max_items': None, + 'unique_items': None, + 'allow_mutation': True, + } + + def __init__(self, default: Any = Undefined, **kwargs: Any) -> None: + self.default = default + self.default_factory = kwargs.pop('default_factory', None) + self.alias = kwargs.pop('alias', None) + self.alias_priority = kwargs.pop('alias_priority', 2 if self.alias is not None else None) + self.title = kwargs.pop('title', None) + self.description = kwargs.pop('description', None) + self.exclude = kwargs.pop('exclude', None) + self.include = kwargs.pop('include', None) + self.const = kwargs.pop('const', None) + self.gt = kwargs.pop('gt', None) + self.ge = kwargs.pop('ge', None) + self.lt = kwargs.pop('lt', None) + self.le = kwargs.pop('le', None) + self.multiple_of = kwargs.pop('multiple_of', None) + self.allow_inf_nan = kwargs.pop('allow_inf_nan', None) + self.max_digits = kwargs.pop('max_digits', None) + self.decimal_places = kwargs.pop('decimal_places', None) + self.min_items = kwargs.pop('min_items', None) + self.max_items = kwargs.pop('max_items', None) + self.unique_items = kwargs.pop('unique_items', None) + self.min_length = kwargs.pop('min_length', None) + self.max_length = kwargs.pop('max_length', None) + self.allow_mutation = kwargs.pop('allow_mutation', True) + self.regex = kwargs.pop('regex', None) + self.discriminator = kwargs.pop('discriminator', None) + self.repr = kwargs.pop('repr', True) + self.extra = kwargs + + def __repr_args__(self) -> 'ReprArgs': + field_defaults_to_hide: Dict[str, Any] = { + 'repr': True, + **self.__field_constraints__, + } + + attrs = ((s, getattr(self, s)) for s in self.__slots__) + return [(a, v) for a, v in attrs if v != field_defaults_to_hide.get(a, None)] + + def get_constraints(self) -> Set[str]: + """ + Gets the constraints set on the field by comparing the constraint value with its default value + + :return: the constraints set on field_info + """ + return {attr for attr, default in self.__field_constraints__.items() if getattr(self, attr) != default} + + def update_from_config(self, from_config: Dict[str, Any]) -> None: + """ + Update this FieldInfo based on a dict from get_field_info, only fields which have not been set are dated. + """ + for attr_name, value in from_config.items(): + try: + current_value = getattr(self, attr_name) + except AttributeError: + # attr_name is not an attribute of FieldInfo, it should therefore be added to extra + # (except if extra already has this value!) + self.extra.setdefault(attr_name, value) + else: + if current_value is self.__field_constraints__.get(attr_name, None): + setattr(self, attr_name, value) + elif attr_name == 'exclude': + self.exclude = ValueItems.merge(value, current_value) + elif attr_name == 'include': + self.include = ValueItems.merge(value, current_value, intersect=True) + + def _validate(self) -> None: + if self.default is not Undefined and self.default_factory is not None: + raise ValueError('cannot specify both default and default_factory') + + +def Field( + default: Any = Undefined, + *, + default_factory: Optional[NoArgAnyCallable] = None, + alias: Optional[str] = None, + title: Optional[str] = None, + description: Optional[str] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny', Any]] = None, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny', Any]] = None, + const: Optional[bool] = None, + gt: Optional[float] = None, + ge: Optional[float] = None, + lt: Optional[float] = None, + le: Optional[float] = None, + multiple_of: Optional[float] = None, + allow_inf_nan: Optional[bool] = None, + max_digits: Optional[int] = None, + decimal_places: Optional[int] = None, + min_items: Optional[int] = None, + max_items: Optional[int] = None, + unique_items: Optional[bool] = None, + min_length: Optional[int] = None, + max_length: Optional[int] = None, + allow_mutation: bool = True, + regex: Optional[str] = None, + discriminator: Optional[str] = None, + repr: bool = True, + **extra: Any, +) -> Any: + """ + Used to provide extra information about a field, either for the model schema or complex validation. Some arguments + apply only to number fields (``int``, ``float``, ``Decimal``) and some apply only to ``str``. + + :param default: since this is replacing the field’s default, its first argument is used + to set the default, use ellipsis (``...``) to indicate the field is required + :param default_factory: callable that will be called when a default value is needed for this field + If both `default` and `default_factory` are set, an error is raised. + :param alias: the public name of the field + :param title: can be any string, used in the schema + :param description: can be any string, used in the schema + :param exclude: exclude this field while dumping. + Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method. + :param include: include this field while dumping. + Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method. + :param const: this field is required and *must* take it's default value + :param gt: only applies to numbers, requires the field to be "greater than". The schema + will have an ``exclusiveMinimum`` validation keyword + :param ge: only applies to numbers, requires the field to be "greater than or equal to". The + schema will have a ``minimum`` validation keyword + :param lt: only applies to numbers, requires the field to be "less than". The schema + will have an ``exclusiveMaximum`` validation keyword + :param le: only applies to numbers, requires the field to be "less than or equal to". The + schema will have a ``maximum`` validation keyword + :param multiple_of: only applies to numbers, requires the field to be "a multiple of". The + schema will have a ``multipleOf`` validation keyword + :param allow_inf_nan: only applies to numbers, allows the field to be NaN or infinity (+inf or -inf), + which is a valid Python float. Default True, set to False for compatibility with JSON. + :param max_digits: only applies to Decimals, requires the field to have a maximum number + of digits within the decimal. It does not include a zero before the decimal point or trailing decimal zeroes. + :param decimal_places: only applies to Decimals, requires the field to have at most a number of decimal places + allowed. It does not include trailing decimal zeroes. + :param min_items: only applies to lists, requires the field to have a minimum number of + elements. The schema will have a ``minItems`` validation keyword + :param max_items: only applies to lists, requires the field to have a maximum number of + elements. The schema will have a ``maxItems`` validation keyword + :param unique_items: only applies to lists, requires the field not to have duplicated + elements. The schema will have a ``uniqueItems`` validation keyword + :param min_length: only applies to strings, requires the field to have a minimum length. The + schema will have a ``minLength`` validation keyword + :param max_length: only applies to strings, requires the field to have a maximum length. The + schema will have a ``maxLength`` validation keyword + :param allow_mutation: a boolean which defaults to True. When False, the field raises a TypeError if the field is + assigned on an instance. The BaseModel Config must set validate_assignment to True + :param regex: only applies to strings, requires the field match against a regular expression + pattern string. The schema will have a ``pattern`` validation keyword + :param discriminator: only useful with a (discriminated a.k.a. tagged) `Union` of sub models with a common field. + The `discriminator` is the name of this common field to shorten validation and improve generated schema + :param repr: show this field in the representation + :param **extra: any additional keyword arguments will be added as is to the schema + """ + field_info = FieldInfo( + default, + default_factory=default_factory, + alias=alias, + title=title, + description=description, + exclude=exclude, + include=include, + const=const, + gt=gt, + ge=ge, + lt=lt, + le=le, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + min_items=min_items, + max_items=max_items, + unique_items=unique_items, + min_length=min_length, + max_length=max_length, + allow_mutation=allow_mutation, + regex=regex, + discriminator=discriminator, + repr=repr, + **extra, + ) + field_info._validate() + return field_info + + +# used to be an enum but changed to int's for small performance improvement as less access overhead +SHAPE_SINGLETON = 1 +SHAPE_LIST = 2 +SHAPE_SET = 3 +SHAPE_MAPPING = 4 +SHAPE_TUPLE = 5 +SHAPE_TUPLE_ELLIPSIS = 6 +SHAPE_SEQUENCE = 7 +SHAPE_FROZENSET = 8 +SHAPE_ITERABLE = 9 +SHAPE_GENERIC = 10 +SHAPE_DEQUE = 11 +SHAPE_DICT = 12 +SHAPE_DEFAULTDICT = 13 +SHAPE_COUNTER = 14 +SHAPE_NAME_LOOKUP = { + SHAPE_LIST: 'List[{}]', + SHAPE_SET: 'Set[{}]', + SHAPE_TUPLE_ELLIPSIS: 'Tuple[{}, ...]', + SHAPE_SEQUENCE: 'Sequence[{}]', + SHAPE_FROZENSET: 'FrozenSet[{}]', + SHAPE_ITERABLE: 'Iterable[{}]', + SHAPE_DEQUE: 'Deque[{}]', + SHAPE_DICT: 'Dict[{}]', + SHAPE_DEFAULTDICT: 'DefaultDict[{}]', + SHAPE_COUNTER: 'Counter[{}]', +} + +MAPPING_LIKE_SHAPES: Set[int] = {SHAPE_DEFAULTDICT, SHAPE_DICT, SHAPE_MAPPING, SHAPE_COUNTER} + + +class ModelField(Representation): + __slots__ = ( + 'type_', + 'outer_type_', + 'annotation', + 'sub_fields', + 'sub_fields_mapping', + 'key_field', + 'validators', + 'pre_validators', + 'post_validators', + 'default', + 'default_factory', + 'required', + 'final', + 'model_config', + 'name', + 'alias', + 'has_alias', + 'field_info', + 'discriminator_key', + 'discriminator_alias', + 'validate_always', + 'allow_none', + 'shape', + 'class_validators', + 'parse_json', + ) + + def __init__( + self, + *, + name: str, + type_: Type[Any], + class_validators: Optional[Dict[str, Validator]], + model_config: Type['BaseConfig'], + default: Any = None, + default_factory: Optional[NoArgAnyCallable] = None, + required: 'BoolUndefined' = Undefined, + final: bool = False, + alias: Optional[str] = None, + field_info: Optional[FieldInfo] = None, + ) -> None: + self.name: str = name + self.has_alias: bool = alias is not None + self.alias: str = alias if alias is not None else name + self.annotation = type_ + self.type_: Any = convert_generics(type_) + self.outer_type_: Any = type_ + self.class_validators = class_validators or {} + self.default: Any = default + self.default_factory: Optional[NoArgAnyCallable] = default_factory + self.required: 'BoolUndefined' = required + self.final: bool = final + self.model_config = model_config + self.field_info: FieldInfo = field_info or FieldInfo(default) + self.discriminator_key: Optional[str] = self.field_info.discriminator + self.discriminator_alias: Optional[str] = self.discriminator_key + + self.allow_none: bool = False + self.validate_always: bool = False + self.sub_fields: Optional[List[ModelField]] = None + self.sub_fields_mapping: Optional[Dict[str, 'ModelField']] = None # used for discriminated union + self.key_field: Optional[ModelField] = None + self.validators: 'ValidatorsList' = [] + self.pre_validators: Optional['ValidatorsList'] = None + self.post_validators: Optional['ValidatorsList'] = None + self.parse_json: bool = False + self.shape: int = SHAPE_SINGLETON + self.model_config.prepare_field(self) + self.prepare() + + def get_default(self) -> Any: + return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + @staticmethod + def _get_field_info( + field_name: str, annotation: Any, value: Any, config: Type['BaseConfig'] + ) -> Tuple[FieldInfo, Any]: + """ + Get a FieldInfo from a root typing.Annotated annotation, value, or config default. + + The FieldInfo may be set in typing.Annotated or the value, but not both. If neither contain + a FieldInfo, a new one will be created using the config. + + :param field_name: name of the field for use in error messages + :param annotation: a type hint such as `str` or `Annotated[str, Field(..., min_length=5)]` + :param value: the field's assigned value + :param config: the model's config object + :return: the FieldInfo contained in the `annotation`, the value, or a new one from the config. + """ + field_info_from_config = config.get_field_info(field_name) + + field_info = None + if get_origin(annotation) is Annotated: + field_infos = [arg for arg in get_args(annotation)[1:] if isinstance(arg, FieldInfo)] + if len(field_infos) > 1: + raise ValueError(f'cannot specify multiple `Annotated` `Field`s for {field_name!r}') + field_info = next(iter(field_infos), None) + if field_info is not None: + field_info = copy.copy(field_info) + field_info.update_from_config(field_info_from_config) + if field_info.default not in (Undefined, Required): + raise ValueError(f'`Field` default cannot be set in `Annotated` for {field_name!r}') + if value is not Undefined and value is not Required: + # check also `Required` because of `validate_arguments` that sets `...` as default value + field_info.default = value + + if isinstance(value, FieldInfo): + if field_info is not None: + raise ValueError(f'cannot specify `Annotated` and value `Field`s together for {field_name!r}') + field_info = value + field_info.update_from_config(field_info_from_config) + elif field_info is None: + field_info = FieldInfo(value, **field_info_from_config) + value = None if field_info.default_factory is not None else field_info.default + field_info._validate() + return field_info, value + + @classmethod + def infer( + cls, + *, + name: str, + value: Any, + annotation: Any, + class_validators: Optional[Dict[str, Validator]], + config: Type['BaseConfig'], + ) -> 'ModelField': + from .schema import get_annotation_from_field_info + + field_info, value = cls._get_field_info(name, annotation, value, config) + required: 'BoolUndefined' = Undefined + if value is Required: + required = True + value = None + elif value is not Undefined: + required = False + annotation = get_annotation_from_field_info(annotation, field_info, name, config.validate_assignment) + + return cls( + name=name, + type_=annotation, + alias=field_info.alias, + class_validators=class_validators, + default=value, + default_factory=field_info.default_factory, + required=required, + model_config=config, + field_info=field_info, + ) + + def set_config(self, config: Type['BaseConfig']) -> None: + self.model_config = config + info_from_config = config.get_field_info(self.name) + config.prepare_field(self) + new_alias = info_from_config.get('alias') + new_alias_priority = info_from_config.get('alias_priority') or 0 + if new_alias and new_alias_priority >= (self.field_info.alias_priority or 0): + self.field_info.alias = new_alias + self.field_info.alias_priority = new_alias_priority + self.alias = new_alias + new_exclude = info_from_config.get('exclude') + if new_exclude is not None: + self.field_info.exclude = ValueItems.merge(self.field_info.exclude, new_exclude) + new_include = info_from_config.get('include') + if new_include is not None: + self.field_info.include = ValueItems.merge(self.field_info.include, new_include, intersect=True) + + @property + def alt_alias(self) -> bool: + return self.name != self.alias + + def prepare(self) -> None: + """ + Prepare the field but inspecting self.default, self.type_ etc. + + Note: this method is **not** idempotent (because _type_analysis is not idempotent), + e.g. calling it it multiple times may modify the field and configure it incorrectly. + """ + self._set_default_and_type() + if self.type_.__class__ is ForwardRef or self.type_.__class__ is DeferredType: + # self.type_ is currently a ForwardRef and there's nothing we can do now, + # user will need to call model.update_forward_refs() + return + + self._type_analysis() + if self.required is Undefined: + self.required = True + if self.default is Undefined and self.default_factory is None: + self.default = None + self.populate_validators() + + def _set_default_and_type(self) -> None: + """ + Set the default value, infer the type if needed and check if `None` value is valid. + """ + if self.default_factory is not None: + if self.type_ is Undefined: + raise errors_.ConfigError( + f'you need to set the type of field {self.name!r} when using `default_factory`' + ) + return + + default_value = self.get_default() + + if default_value is not None and self.type_ is Undefined: + self.type_ = default_value.__class__ + self.outer_type_ = self.type_ + self.annotation = self.type_ + + if self.type_ is Undefined: + raise errors_.ConfigError(f'unable to infer type for attribute "{self.name}"') + + if self.required is False and default_value is None: + self.allow_none = True + + def _type_analysis(self) -> None: # noqa: C901 (ignore complexity) + # typing interface is horrible, we have to do some ugly checks + if lenient_issubclass(self.type_, JsonWrapper): + self.type_ = self.type_.inner_type + self.parse_json = True + elif lenient_issubclass(self.type_, Json): + self.type_ = Any + self.parse_json = True + elif isinstance(self.type_, TypeVar): + if self.type_.__bound__: + self.type_ = self.type_.__bound__ + elif self.type_.__constraints__: + self.type_ = Union[self.type_.__constraints__] + else: + self.type_ = Any + elif is_new_type(self.type_): + self.type_ = new_type_supertype(self.type_) + + if self.type_ is Any or self.type_ is object: + if self.required is Undefined: + self.required = False + self.allow_none = True + return + elif self.type_ is Pattern or self.type_ is re.Pattern: + # python 3.7 only, Pattern is a typing object but without sub fields + return + elif is_literal_type(self.type_): + return + elif is_typeddict(self.type_): + return + + if is_finalvar(self.type_): + self.final = True + + if self.type_ is Final: + self.type_ = Any + else: + self.type_ = get_args(self.type_)[0] + + self._type_analysis() + return + + origin = get_origin(self.type_) + + if origin is Annotated or is_typeddict_special(origin): + self.type_ = get_args(self.type_)[0] + self._type_analysis() + return + + if self.discriminator_key is not None and not is_union(origin): + raise TypeError('`discriminator` can only be used with `Union` type with more than one variant') + + # add extra check for `collections.abc.Hashable` for python 3.10+ where origin is not `None` + if origin is None or origin is CollectionsHashable: + # field is not "typing" object eg. Union, Dict, List etc. + # allow None for virtual superclasses of NoneType, e.g. Hashable + if isinstance(self.type_, type) and isinstance(None, self.type_): + self.allow_none = True + return + elif origin is Callable: + return + elif is_union(origin): + types_ = [] + for type_ in get_args(self.type_): + if is_none_type(type_) or type_ is Any or type_ is object: + if self.required is Undefined: + self.required = False + self.allow_none = True + if is_none_type(type_): + continue + types_.append(type_) + + if len(types_) == 1: + # Optional[] + self.type_ = types_[0] + # this is the one case where the "outer type" isn't just the original type + self.outer_type_ = self.type_ + # re-run to correctly interpret the new self.type_ + self._type_analysis() + else: + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{display_as_type(t)}') for t in types_] + + if self.discriminator_key is not None: + self.prepare_discriminated_union_sub_fields() + return + elif issubclass(origin, Tuple): # type: ignore + # origin == Tuple without item type + args = get_args(self.type_) + if not args: # plain tuple + self.type_ = Any + self.shape = SHAPE_TUPLE_ELLIPSIS + elif len(args) == 2 and args[1] is Ellipsis: # e.g. Tuple[int, ...] + self.type_ = args[0] + self.shape = SHAPE_TUPLE_ELLIPSIS + self.sub_fields = [self._create_sub_type(args[0], f'{self.name}_0')] + elif args == ((),): # Tuple[()] means empty tuple + self.shape = SHAPE_TUPLE + self.type_ = Any + self.sub_fields = [] + else: + self.shape = SHAPE_TUPLE + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(args)] + return + elif issubclass(origin, List): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'list_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_LIST + elif issubclass(origin, Set): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'set_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_SET + elif issubclass(origin, FrozenSet): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'frozenset_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_FROZENSET + elif issubclass(origin, Deque): + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_DEQUE + elif issubclass(origin, Sequence): + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_SEQUENCE + # priority to most common mapping: dict + elif origin is dict or origin is Dict: + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_DICT + elif issubclass(origin, DefaultDict): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_DEFAULTDICT + elif issubclass(origin, Counter): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = int + self.shape = SHAPE_COUNTER + elif issubclass(origin, Mapping): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_MAPPING + # Equality check as almost everything inherits form Iterable, including str + # check for Iterable and CollectionsIterable, as it could receive one even when declared with the other + elif origin in {Iterable, CollectionsIterable}: + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_ITERABLE + self.sub_fields = [self._create_sub_type(self.type_, f'{self.name}_type')] + elif issubclass(origin, Type): # type: ignore + return + elif hasattr(origin, '__get_validators__') or self.model_config.arbitrary_types_allowed: + # Is a Pydantic-compatible generic that handles itself + # or we have arbitrary_types_allowed = True + self.shape = SHAPE_GENERIC + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(get_args(self.type_))] + self.type_ = origin + return + else: + raise TypeError(f'Fields of type "{origin}" are not supported.') + + # type_ has been refined eg. as the type of a List and sub_fields needs to be populated + self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)] + + def prepare_discriminated_union_sub_fields(self) -> None: + """ + Prepare the mapping -> and update `sub_fields` + Note that this process can be aborted if a `ForwardRef` is encountered + """ + assert self.discriminator_key is not None + + if self.type_.__class__ is DeferredType: + return + + assert self.sub_fields is not None + sub_fields_mapping: Dict[str, 'ModelField'] = {} + all_aliases: Set[str] = set() + + for sub_field in self.sub_fields: + t = sub_field.type_ + if t.__class__ is ForwardRef: + # Stopping everything...will need to call `update_forward_refs` + return + + alias, discriminator_values = get_discriminator_alias_and_values(t, self.discriminator_key) + all_aliases.add(alias) + for discriminator_value in discriminator_values: + sub_fields_mapping[discriminator_value] = sub_field + + self.sub_fields_mapping = sub_fields_mapping + self.discriminator_alias = get_unique_discriminator_alias(all_aliases, self.discriminator_key) + + def _create_sub_type(self, type_: Type[Any], name: str, *, for_keys: bool = False) -> 'ModelField': + if for_keys: + class_validators = None + else: + # validators for sub items should not have `each_item` as we want to check only the first sublevel + class_validators = { + k: Validator( + func=v.func, + pre=v.pre, + each_item=False, + always=v.always, + check_fields=v.check_fields, + skip_on_failure=v.skip_on_failure, + ) + for k, v in self.class_validators.items() + if v.each_item + } + + field_info, _ = self._get_field_info(name, type_, None, self.model_config) + + return self.__class__( + type_=type_, + name=name, + class_validators=class_validators, + model_config=self.model_config, + field_info=field_info, + ) + + def populate_validators(self) -> None: + """ + Prepare self.pre_validators, self.validators, and self.post_validators based on self.type_'s __get_validators__ + and class validators. This method should be idempotent, e.g. it should be safe to call multiple times + without mis-configuring the field. + """ + self.validate_always = getattr(self.type_, 'validate_always', False) or any( + v.always for v in self.class_validators.values() + ) + + class_validators_ = self.class_validators.values() + if not self.sub_fields or self.shape == SHAPE_GENERIC: + get_validators = getattr(self.type_, '__get_validators__', None) + v_funcs = ( + *[v.func for v in class_validators_ if v.each_item and v.pre], + *(get_validators() if get_validators else list(find_validators(self.type_, self.model_config))), + *[v.func for v in class_validators_ if v.each_item and not v.pre], + ) + self.validators = prep_validators(v_funcs) + + self.pre_validators = [] + self.post_validators = [] + + if self.field_info and self.field_info.const: + self.post_validators.append(make_generic_validator(constant_validator)) + + if class_validators_: + self.pre_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and v.pre) + self.post_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and not v.pre) + + if self.parse_json: + self.pre_validators.append(make_generic_validator(validate_json)) + + self.pre_validators = self.pre_validators or None + self.post_validators = self.post_validators or None + + def validate( + self, v: Any, values: Dict[str, Any], *, loc: 'LocStr', cls: Optional['ModelOrDc'] = None + ) -> 'ValidateReturn': + assert self.type_.__class__ is not DeferredType + + if self.type_.__class__ is ForwardRef: + assert cls is not None + raise ConfigError( + f'field "{self.name}" not yet prepared so type is still a ForwardRef, ' + f'you might need to call {cls.__name__}.update_forward_refs().' + ) + + errors: Optional['ErrorList'] + if self.pre_validators: + v, errors = self._apply_validators(v, values, loc, cls, self.pre_validators) + if errors: + return v, errors + + if v is None: + if is_none_type(self.type_): + # keep validating + pass + elif self.allow_none: + if self.post_validators: + return self._apply_validators(v, values, loc, cls, self.post_validators) + else: + return None, None + else: + return v, ErrorWrapper(NoneIsNotAllowedError(), loc) + + if self.shape == SHAPE_SINGLETON: + v, errors = self._validate_singleton(v, values, loc, cls) + elif self.shape in MAPPING_LIKE_SHAPES: + v, errors = self._validate_mapping_like(v, values, loc, cls) + elif self.shape == SHAPE_TUPLE: + v, errors = self._validate_tuple(v, values, loc, cls) + elif self.shape == SHAPE_ITERABLE: + v, errors = self._validate_iterable(v, values, loc, cls) + elif self.shape == SHAPE_GENERIC: + v, errors = self._apply_validators(v, values, loc, cls, self.validators) + else: + # sequence, list, set, generator, tuple with ellipsis, frozen set + v, errors = self._validate_sequence_like(v, values, loc, cls) + + if not errors and self.post_validators: + v, errors = self._apply_validators(v, values, loc, cls, self.post_validators) + return v, errors + + def _validate_sequence_like( # noqa: C901 (ignore complexity) + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + """ + Validate sequence-like containers: lists, tuples, sets and generators + Note that large if-else blocks are necessary to enable Cython + optimization, which is why we disable the complexity check above. + """ + if not sequence_like(v): + e: errors_.PydanticTypeError + if self.shape == SHAPE_LIST: + e = errors_.ListError() + elif self.shape in (SHAPE_TUPLE, SHAPE_TUPLE_ELLIPSIS): + e = errors_.TupleError() + elif self.shape == SHAPE_SET: + e = errors_.SetError() + elif self.shape == SHAPE_FROZENSET: + e = errors_.FrozenSetError() + else: + e = errors_.SequenceError() + return v, ErrorWrapper(e, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result = [] + errors: List[ErrorList] = [] + for i, v_ in enumerate(v): + v_loc = *loc, i + r, ee = self._validate_singleton(v_, values, v_loc, cls) + if ee: + errors.append(ee) + else: + result.append(r) + + if errors: + return v, errors + + converted: Union[List[Any], Set[Any], FrozenSet[Any], Tuple[Any, ...], Iterator[Any], Deque[Any]] = result + + if self.shape == SHAPE_SET: + converted = set(result) + elif self.shape == SHAPE_FROZENSET: + converted = frozenset(result) + elif self.shape == SHAPE_TUPLE_ELLIPSIS: + converted = tuple(result) + elif self.shape == SHAPE_DEQUE: + converted = deque(result, maxlen=getattr(v, 'maxlen', None)) + elif self.shape == SHAPE_SEQUENCE: + if isinstance(v, tuple): + converted = tuple(result) + elif isinstance(v, set): + converted = set(result) + elif isinstance(v, Generator): + converted = iter(result) + elif isinstance(v, deque): + converted = deque(result, maxlen=getattr(v, 'maxlen', None)) + return converted, None + + def _validate_iterable( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + """ + Validate Iterables. + + This intentionally doesn't validate values to allow infinite generators. + """ + + try: + iterable = iter(v) + except TypeError: + return v, ErrorWrapper(errors_.IterableError(), loc) + return iterable, None + + def _validate_tuple( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + e: Optional[Exception] = None + if not sequence_like(v): + e = errors_.TupleError() + else: + actual_length, expected_length = len(v), len(self.sub_fields) # type: ignore + if actual_length != expected_length: + e = errors_.TupleLengthError(actual_length=actual_length, expected_length=expected_length) + + if e: + return v, ErrorWrapper(e, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result = [] + errors: List[ErrorList] = [] + for i, (v_, field) in enumerate(zip(v, self.sub_fields)): # type: ignore + v_loc = *loc, i + r, ee = field.validate(v_, values, loc=v_loc, cls=cls) + if ee: + errors.append(ee) + else: + result.append(r) + + if errors: + return v, errors + else: + return tuple(result), None + + def _validate_mapping_like( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + try: + v_iter = dict_validator(v) + except TypeError as exc: + return v, ErrorWrapper(exc, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result, errors = {}, [] + for k, v_ in v_iter.items(): + v_loc = *loc, '__key__' + key_result, key_errors = self.key_field.validate(k, values, loc=v_loc, cls=cls) # type: ignore + if key_errors: + errors.append(key_errors) + continue + + v_loc = *loc, k + value_result, value_errors = self._validate_singleton(v_, values, v_loc, cls) + if value_errors: + errors.append(value_errors) + continue + + result[key_result] = value_result + if errors: + return v, errors + elif self.shape == SHAPE_DICT: + return result, None + elif self.shape == SHAPE_DEFAULTDICT: + return defaultdict(self.type_, result), None + elif self.shape == SHAPE_COUNTER: + return CollectionCounter(result), None + else: + return self._get_mapping_value(v, result), None + + def _get_mapping_value(self, original: T, converted: Dict[Any, Any]) -> Union[T, Dict[Any, Any]]: + """ + When type is `Mapping[KT, KV]` (or another unsupported mapping), we try to avoid + coercing to `dict` unwillingly. + """ + original_cls = original.__class__ + + if original_cls == dict or original_cls == Dict: + return converted + elif original_cls in {defaultdict, DefaultDict}: + return defaultdict(self.type_, converted) + else: + try: + # Counter, OrderedDict, UserDict, ... + return original_cls(converted) # type: ignore + except TypeError: + raise RuntimeError(f'Could not convert dictionary to {original_cls.__name__!r}') from None + + def _validate_singleton( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + if self.sub_fields: + if self.discriminator_key is not None: + return self._validate_discriminated_union(v, values, loc, cls) + + errors = [] + + if self.model_config.smart_union and is_union(get_origin(self.type_)): + # 1st pass: check if the value is an exact instance of one of the Union types + # (e.g. to avoid coercing a bool into an int) + for field in self.sub_fields: + if v.__class__ is field.outer_type_: + return v, None + + # 2nd pass: check if the value is an instance of any subclass of the Union types + for field in self.sub_fields: + # This whole logic will be improved later on to support more complex `isinstance` checks + # It will probably be done once a strict mode is added and be something like: + # ``` + # value, error = field.validate(v, values, strict=True) + # if error is None: + # return value, None + # ``` + try: + if isinstance(v, field.outer_type_): + return v, None + except TypeError: + # compound type + if lenient_isinstance(v, get_origin(field.outer_type_)): + value, error = field.validate(v, values, loc=loc, cls=cls) + if not error: + return value, None + + # 1st pass by default or 3rd pass with `smart_union` enabled: + # check if the value can be coerced into one of the Union types + for field in self.sub_fields: + value, error = field.validate(v, values, loc=loc, cls=cls) + if error: + errors.append(error) + else: + return value, None + return v, errors + else: + return self._apply_validators(v, values, loc, cls, self.validators) + + def _validate_discriminated_union( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + assert self.discriminator_key is not None + assert self.discriminator_alias is not None + + try: + try: + discriminator_value = v[self.discriminator_alias] + except KeyError: + if self.model_config.allow_population_by_field_name: + discriminator_value = v[self.discriminator_key] + else: + raise + except KeyError: + return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc) + except TypeError: + try: + # BaseModel or dataclass + discriminator_value = getattr(v, self.discriminator_key) + except (AttributeError, TypeError): + return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc) + + if self.sub_fields_mapping is None: + assert cls is not None + raise ConfigError( + f'field "{self.name}" not yet prepared so type is still a ForwardRef, ' + f'you might need to call {cls.__name__}.update_forward_refs().' + ) + + try: + sub_field = self.sub_fields_mapping[discriminator_value] + except (KeyError, TypeError): + # KeyError: `discriminator_value` is not in the dictionary. + # TypeError: `discriminator_value` is unhashable. + assert self.sub_fields_mapping is not None + return v, ErrorWrapper( + InvalidDiscriminator( + discriminator_key=self.discriminator_key, + discriminator_value=discriminator_value, + allowed_values=list(self.sub_fields_mapping), + ), + loc, + ) + else: + if not isinstance(loc, tuple): + loc = (loc,) + return sub_field.validate(v, values, loc=(*loc, display_as_type(sub_field.type_)), cls=cls) + + def _apply_validators( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'], validators: 'ValidatorsList' + ) -> 'ValidateReturn': + for validator in validators: + try: + v = validator(cls, v, values, self, self.model_config) + except (ValueError, TypeError, AssertionError) as exc: + return v, ErrorWrapper(exc, loc) + return v, None + + def is_complex(self) -> bool: + """ + Whether the field is "complex" eg. env variables should be parsed as JSON. + """ + from .main import BaseModel + + return ( + self.shape != SHAPE_SINGLETON + or hasattr(self.type_, '__pydantic_model__') + or lenient_issubclass(self.type_, (BaseModel, list, set, frozenset, dict)) + ) + + def _type_display(self) -> PyObjectStr: + t = display_as_type(self.type_) + + if self.shape in MAPPING_LIKE_SHAPES: + t = f'Mapping[{display_as_type(self.key_field.type_)}, {t}]' # type: ignore + elif self.shape == SHAPE_TUPLE: + t = 'Tuple[{}]'.format(', '.join(display_as_type(f.type_) for f in self.sub_fields)) # type: ignore + elif self.shape == SHAPE_GENERIC: + assert self.sub_fields + t = '{}[{}]'.format( + display_as_type(self.type_), ', '.join(display_as_type(f.type_) for f in self.sub_fields) + ) + elif self.shape != SHAPE_SINGLETON: + t = SHAPE_NAME_LOOKUP[self.shape].format(t) + + if self.allow_none and (self.shape != SHAPE_SINGLETON or not self.sub_fields): + t = f'Optional[{t}]' + return PyObjectStr(t) + + def __repr_args__(self) -> 'ReprArgs': + args = [('name', self.name), ('type', self._type_display()), ('required', self.required)] + + if not self.required: + if self.default_factory is not None: + args.append(('default_factory', f'')) + else: + args.append(('default', self.default)) + + if self.alt_alias: + args.append(('alias', self.alias)) + return args + + +class ModelPrivateAttr(Representation): + __slots__ = ('default', 'default_factory') + + def __init__(self, default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None) -> None: + self.default = default + self.default_factory = default_factory + + def get_default(self) -> Any: + return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and (self.default, self.default_factory) == ( + other.default, + other.default_factory, + ) + + +def PrivateAttr( + default: Any = Undefined, + *, + default_factory: Optional[NoArgAnyCallable] = None, +) -> Any: + """ + Indicates that attribute is only used internally and never mixed with regular fields. + + Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant. + + Private attrs are stored in model __slots__. + + :param default: the attribute’s default value + :param default_factory: callable that will be called when a default value is needed for this attribute + If both `default` and `default_factory` are set, an error is raised. + """ + if default is not Undefined and default_factory is not None: + raise ValueError('cannot specify both default and default_factory') + + return ModelPrivateAttr( + default, + default_factory=default_factory, + ) + + +class DeferredType: + """ + Used to postpone field preparation, while creating recursive generic models. + """ + + +def is_finalvar_with_default_val(type_: Type[Any], val: Any) -> bool: + return is_finalvar(type_) and val is not Undefined and not isinstance(val, FieldInfo) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/generics.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/generics.py new file mode 100644 index 0000000..a75b6b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/generics.py @@ -0,0 +1,400 @@ +import sys +import types +import typing +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + ForwardRef, + Generic, + Iterator, + List, + Mapping, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, +) +from weakref import WeakKeyDictionary, WeakValueDictionary + +from typing_extensions import Annotated, Literal as ExtLiteral + +from .class_validators import gather_all_validators +from .fields import DeferredType +from .main import BaseModel, create_model +from .types import JsonWrapper +from .typing import display_as_type, get_all_type_hints, get_args, get_origin, typing_base +from .utils import all_identical, lenient_issubclass + +if sys.version_info >= (3, 10): + from typing import _UnionGenericAlias +if sys.version_info >= (3, 8): + from typing import Literal + +GenericModelT = TypeVar('GenericModelT', bound='GenericModel') +TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type + +CacheKey = Tuple[Type[Any], Any, Tuple[Any, ...]] +Parametrization = Mapping[TypeVarType, Type[Any]] + +# weak dictionaries allow the dynamically created parametrized versions of generic models to get collected +# once they are no longer referenced by the caller. +if sys.version_info >= (3, 9): # Typing for weak dictionaries available at 3.9 + GenericTypesCache = WeakValueDictionary[CacheKey, Type[BaseModel]] + AssignedParameters = WeakKeyDictionary[Type[BaseModel], Parametrization] +else: + GenericTypesCache = WeakValueDictionary + AssignedParameters = WeakKeyDictionary + +# _generic_types_cache is a Mapping from __class_getitem__ arguments to the parametrized version of generic models. +# This ensures multiple calls of e.g. A[B] return always the same class. +_generic_types_cache = GenericTypesCache() + +# _assigned_parameters is a Mapping from parametrized version of generic models to assigned types of parametrizations +# as captured during construction of the class (not instances). +# E.g., for generic model `Model[A, B]`, when parametrized model `Model[int, str]` is created, +# `Model[int, str]`: {A: int, B: str}` will be stored in `_assigned_parameters`. +# (This information is only otherwise available after creation from the class name string). +_assigned_parameters = AssignedParameters() + + +class GenericModel(BaseModel): + __slots__ = () + __concrete__: ClassVar[bool] = False + + if TYPE_CHECKING: + # Putting this in a TYPE_CHECKING block allows us to replace `if Generic not in cls.__bases__` with + # `not hasattr(cls, "__parameters__")`. This means we don't need to force non-concrete subclasses of + # `GenericModel` to also inherit from `Generic`, which would require changes to the use of `create_model` below. + __parameters__: ClassVar[Tuple[TypeVarType, ...]] + + # Setting the return type as Type[Any] instead of Type[BaseModel] prevents PyCharm warnings + def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[Type[Any], ...]]) -> Type[Any]: + """Instantiates a new class from a generic class `cls` and type variables `params`. + + :param params: Tuple of types the class . Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + :return: New model class inheriting from `cls` with instantiated + types described by `params`. If no parameters are given, `cls` is + returned as is. + + """ + + def _cache_key(_params: Any) -> CacheKey: + args = get_args(_params) + # python returns a list for Callables, which is not hashable + if len(args) == 2 and isinstance(args[0], list): + args = (tuple(args[0]), args[1]) + return cls, _params, args + + cached = _generic_types_cache.get(_cache_key(params)) + if cached is not None: + return cached + if cls.__concrete__ and Generic not in cls.__bases__: + raise TypeError('Cannot parameterize a concrete instantiation of a generic model') + if not isinstance(params, tuple): + params = (params,) + if cls is GenericModel and any(isinstance(param, TypeVar) for param in params): + raise TypeError('Type parameters should be placed on typing.Generic, not GenericModel') + if not hasattr(cls, '__parameters__'): + raise TypeError(f'Type {cls.__name__} must inherit from typing.Generic before being parameterized') + + check_parameters_count(cls, params) + # Build map from generic typevars to passed params + typevars_map: Dict[TypeVarType, Type[Any]] = dict(zip(cls.__parameters__, params)) + if all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map: + return cls # if arguments are equal to parameters it's the same object + + # Create new model with original model as parent inserting fields with DeferredType. + model_name = cls.__concrete_name__(params) + validators = gather_all_validators(cls) + + type_hints = get_all_type_hints(cls).items() + instance_type_hints = {k: v for k, v in type_hints if get_origin(v) is not ClassVar} + + fields = {k: (DeferredType(), cls.__fields__[k].field_info) for k in instance_type_hints if k in cls.__fields__} + + model_module, called_globally = get_caller_frame_info() + created_model = cast( + Type[GenericModel], # casting ensures mypy is aware of the __concrete__ and __parameters__ attributes + create_model( + model_name, + __module__=model_module or cls.__module__, + __base__=(cls,) + tuple(cls.__parameterized_bases__(typevars_map)), + __config__=None, + __validators__=validators, + __cls_kwargs__=None, + **fields, + ), + ) + + _assigned_parameters[created_model] = typevars_map + + if called_globally: # create global reference and therefore allow pickling + object_by_reference = None + reference_name = model_name + reference_module_globals = sys.modules[created_model.__module__].__dict__ + while object_by_reference is not created_model: + object_by_reference = reference_module_globals.setdefault(reference_name, created_model) + reference_name += '_' + + created_model.Config = cls.Config + + # Find any typevars that are still present in the model. + # If none are left, the model is fully "concrete", otherwise the new + # class is a generic class as well taking the found typevars as + # parameters. + new_params = tuple( + {param: None for param in iter_contained_typevars(typevars_map.values())} + ) # use dict as ordered set + created_model.__concrete__ = not new_params + if new_params: + created_model.__parameters__ = new_params + + # Save created model in cache so we don't end up creating duplicate + # models that should be identical. + _generic_types_cache[_cache_key(params)] = created_model + if len(params) == 1: + _generic_types_cache[_cache_key(params[0])] = created_model + + # Recursively walk class type hints and replace generic typevars + # with concrete types that were passed. + _prepare_model_fields(created_model, fields, instance_type_hints, typevars_map) + + return created_model + + @classmethod + def __concrete_name__(cls: Type[Any], params: Tuple[Type[Any], ...]) -> str: + """Compute class name for child classes. + + :param params: Tuple of types the class . Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + :return: String representing a the new class where `params` are + passed to `cls` as type variables. + + This method can be overridden to achieve a custom naming scheme for GenericModels. + """ + param_names = [display_as_type(param) for param in params] + params_component = ', '.join(param_names) + return f'{cls.__name__}[{params_component}]' + + @classmethod + def __parameterized_bases__(cls, typevars_map: Parametrization) -> Iterator[Type[Any]]: + """ + Returns unbound bases of cls parameterised to given type variables + + :param typevars_map: Dictionary of type applications for binding subclasses. + Given a generic class `Model` with 2 type variables [S, T] + and a concrete model `Model[str, int]`, + the value `{S: str, T: int}` would be passed to `typevars_map`. + :return: an iterator of generic sub classes, parameterised by `typevars_map` + and other assigned parameters of `cls` + + e.g.: + ``` + class A(GenericModel, Generic[T]): + ... + + class B(A[V], Generic[V]): + ... + + assert A[int] in B.__parameterized_bases__({V: int}) + ``` + """ + + def build_base_model( + base_model: Type[GenericModel], mapped_types: Parametrization + ) -> Iterator[Type[GenericModel]]: + base_parameters = tuple(mapped_types[param] for param in base_model.__parameters__) + parameterized_base = base_model.__class_getitem__(base_parameters) + if parameterized_base is base_model or parameterized_base is cls: + # Avoid duplication in MRO + return + yield parameterized_base + + for base_model in cls.__bases__: + if not issubclass(base_model, GenericModel): + # not a class that can be meaningfully parameterized + continue + elif not getattr(base_model, '__parameters__', None): + # base_model is "GenericModel" (and has no __parameters__) + # or + # base_model is already concrete, and will be included transitively via cls. + continue + elif cls in _assigned_parameters: + if base_model in _assigned_parameters: + # cls is partially parameterised but not from base_model + # e.g. cls = B[S], base_model = A[S] + # B[S][int] should subclass A[int], (and will be transitively via B[int]) + # but it's not viable to consistently subclass types with arbitrary construction + # So don't attempt to include A[S][int] + continue + else: # base_model not in _assigned_parameters: + # cls is partially parameterized, base_model is original generic + # e.g. cls = B[str, T], base_model = B[S, T] + # Need to determine the mapping for the base_model parameters + mapped_types: Parametrization = { + key: typevars_map.get(value, value) for key, value in _assigned_parameters[cls].items() + } + yield from build_base_model(base_model, mapped_types) + else: + # cls is base generic, so base_class has a distinct base + # can construct the Parameterised base model using typevars_map directly + yield from build_base_model(base_model, typevars_map) + + +def replace_types(type_: Any, type_map: Mapping[Any, Any]) -> Any: + """Return type with all occurrences of `type_map` keys recursively replaced with their values. + + :param type_: Any type, class or generic alias + :param type_map: Mapping from `TypeVar` instance to concrete types. + :return: New type representing the basic structure of `type_` with all + `typevar_map` keys recursively replaced. + + >>> replace_types(Tuple[str, Union[List[str], float]], {str: int}) + Tuple[int, Union[List[int], float]] + + """ + if not type_map: + return type_ + + type_args = get_args(type_) + origin_type = get_origin(type_) + + if origin_type is Annotated: + annotated_type, *annotations = type_args + return Annotated[replace_types(annotated_type, type_map), tuple(annotations)] + + if (origin_type is ExtLiteral) or (sys.version_info >= (3, 8) and origin_type is Literal): + return type_map.get(type_, type_) + # Having type args is a good indicator that this is a typing module + # class instantiation or a generic alias of some sort. + if type_args: + resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args) + if all_identical(type_args, resolved_type_args): + # If all arguments are the same, there is no need to modify the + # type or create a new object at all + return type_ + if ( + origin_type is not None + and isinstance(type_, typing_base) + and not isinstance(origin_type, typing_base) + and getattr(type_, '_name', None) is not None + ): + # In python < 3.9 generic aliases don't exist so any of these like `list`, + # `type` or `collections.abc.Callable` need to be translated. + # See: https://www.python.org/dev/peps/pep-0585 + origin_type = getattr(typing, type_._name) + assert origin_type is not None + # PEP-604 syntax (Ex.: list | str) is represented with a types.UnionType object that does not have __getitem__. + # We also cannot use isinstance() since we have to compare types. + if sys.version_info >= (3, 10) and origin_type is types.UnionType: # noqa: E721 + return _UnionGenericAlias(origin_type, resolved_type_args) + return origin_type[resolved_type_args] + + # We handle pydantic generic models separately as they don't have the same + # semantics as "typing" classes or generic aliases + if not origin_type and lenient_issubclass(type_, GenericModel) and not type_.__concrete__: + type_args = type_.__parameters__ + resolved_type_args = tuple(replace_types(t, type_map) for t in type_args) + if all_identical(type_args, resolved_type_args): + return type_ + return type_[resolved_type_args] + + # Handle special case for typehints that can have lists as arguments. + # `typing.Callable[[int, str], int]` is an example for this. + if isinstance(type_, (List, list)): + resolved_list = list(replace_types(element, type_map) for element in type_) + if all_identical(type_, resolved_list): + return type_ + return resolved_list + + # For JsonWrapperValue, need to handle its inner type to allow correct parsing + # of generic Json arguments like Json[T] + if not origin_type and lenient_issubclass(type_, JsonWrapper): + type_.inner_type = replace_types(type_.inner_type, type_map) + return type_ + + # If all else fails, we try to resolve the type directly and otherwise just + # return the input with no modifications. + new_type = type_map.get(type_, type_) + # Convert string to ForwardRef + if isinstance(new_type, str): + return ForwardRef(new_type) + else: + return new_type + + +def check_parameters_count(cls: Type[GenericModel], parameters: Tuple[Any, ...]) -> None: + actual = len(parameters) + expected = len(cls.__parameters__) + if actual != expected: + description = 'many' if actual > expected else 'few' + raise TypeError(f'Too {description} parameters for {cls.__name__}; actual {actual}, expected {expected}') + + +DictValues: Type[Any] = {}.values().__class__ + + +def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]: + """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found.""" + if isinstance(v, TypeVar): + yield v + elif hasattr(v, '__parameters__') and not get_origin(v) and lenient_issubclass(v, GenericModel): + yield from v.__parameters__ + elif isinstance(v, (DictValues, list)): + for var in v: + yield from iter_contained_typevars(var) + else: + args = get_args(v) + for arg in args: + yield from iter_contained_typevars(arg) + + +def get_caller_frame_info() -> Tuple[Optional[str], bool]: + """ + Used inside a function to check whether it was called globally + + Will only work against non-compiled code, therefore used only in pydantic.generics + + :returns Tuple[module_name, called_globally] + """ + try: + previous_caller_frame = sys._getframe(2) + except ValueError as e: + raise RuntimeError('This function must be used inside another function') from e + except AttributeError: # sys module does not have _getframe function, so there's nothing we can do about it + return None, False + frame_globals = previous_caller_frame.f_globals + return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals + + +def _prepare_model_fields( + created_model: Type[GenericModel], + fields: Mapping[str, Any], + instance_type_hints: Mapping[str, type], + typevars_map: Mapping[Any, type], +) -> None: + """ + Replace DeferredType fields with concrete type hints and prepare them. + """ + + for key, field in created_model.__fields__.items(): + if key not in fields: + assert field.type_.__class__ is not DeferredType + # https://github.com/nedbat/coveragepy/issues/198 + continue # pragma: no cover + + assert field.type_.__class__ is DeferredType, field.type_.__class__ + + field_type_hint = instance_type_hints[key] + concrete_type = replace_types(field_type_hint, typevars_map) + field.type_ = concrete_type + field.outer_type_ = concrete_type + field.prepare() + created_model.__annotations__[key] = concrete_type diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/json.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/json.py new file mode 100644 index 0000000..b358b85 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/json.py @@ -0,0 +1,112 @@ +import datetime +from collections import deque +from decimal import Decimal +from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from re import Pattern +from types import GeneratorType +from typing import Any, Callable, Dict, Type, Union +from uuid import UUID + +from .color import Color +from .networks import NameEmail +from .types import SecretBytes, SecretStr + +__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat' + + +def isoformat(o: Union[datetime.date, datetime.time]) -> str: + return o.isoformat() + + +def decimal_encoder(dec_value: Decimal) -> Union[int, float]: + """ + Encodes a Decimal as int of there's no exponent, otherwise float + + This is useful when we use ConstrainedDecimal to represent Numeric(x,0) + where a integer (but not int typed) is used. Encoding this as a float + results in failed round-tripping between encode and parse. + Our Id type is a prime example of this. + + >>> decimal_encoder(Decimal("1.0")) + 1.0 + + >>> decimal_encoder(Decimal("1")) + 1 + """ + if dec_value.as_tuple().exponent >= 0: + return int(dec_value) + else: + return float(dec_value) + + +ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { + bytes: lambda o: o.decode(), + Color: str, + datetime.date: isoformat, + datetime.datetime: isoformat, + datetime.time: isoformat, + datetime.timedelta: lambda td: td.total_seconds(), + Decimal: decimal_encoder, + Enum: lambda o: o.value, + frozenset: list, + deque: list, + GeneratorType: list, + IPv4Address: str, + IPv4Interface: str, + IPv4Network: str, + IPv6Address: str, + IPv6Interface: str, + IPv6Network: str, + NameEmail: str, + Path: str, + Pattern: lambda o: o.pattern, + SecretBytes: str, + SecretStr: str, + set: list, + UUID: str, +} + + +def pydantic_encoder(obj: Any) -> Any: + from dataclasses import asdict, is_dataclass + + from .main import BaseModel + + if isinstance(obj, BaseModel): + return obj.dict() + elif is_dataclass(obj): + return asdict(obj) + + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = ENCODERS_BY_TYPE[base] + except KeyError: + continue + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable") + + +def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any: + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = type_encoders[base] + except KeyError: + continue + + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + return pydantic_encoder(obj) + + +def timedelta_isoformat(td: datetime.timedelta) -> str: + """ + ISO 8601 encoding for Python timedelta object. + """ + minutes, seconds = divmod(td.seconds, 60) + hours, minutes = divmod(minutes, 60) + return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S' diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/main.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/main.py new file mode 100644 index 0000000..08b8af5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/main.py @@ -0,0 +1,1107 @@ +import warnings +from abc import ABCMeta +from copy import deepcopy +from enum import Enum +from functools import partial +from pathlib import Path +from types import FunctionType, prepare_class, resolve_bases +from typing import ( + TYPE_CHECKING, + AbstractSet, + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, + no_type_check, + overload, +) + +from typing_extensions import dataclass_transform + +from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators +from .config import BaseConfig, Extra, inherit_config, prepare_config +from .error_wrappers import ErrorWrapper, ValidationError +from .errors import ConfigError, DictError, ExtraError, MissingError +from .fields import ( + MAPPING_LIKE_SHAPES, + Field, + ModelField, + ModelPrivateAttr, + PrivateAttr, + Undefined, + is_finalvar_with_default_val, +) +from .json import custom_pydantic_encoder, pydantic_encoder +from .parse import Protocol, load_file, load_str_bytes +from .schema import default_ref_template, model_schema +from .types import PyObject, StrBytes +from .typing import ( + AnyCallable, + get_args, + get_origin, + is_classvar, + is_namedtuple, + is_union, + resolve_annotations, + update_model_forward_refs, +) +from .utils import ( + DUNDER_ATTRIBUTES, + ROOT_KEY, + ClassAttribute, + GetterDict, + Representation, + ValueItems, + generate_model_signature, + is_valid_field, + is_valid_private_name, + lenient_issubclass, + sequence_like, + smart_deepcopy, + unique_list, + validate_field_name, +) + +if TYPE_CHECKING: + from inspect import Signature + + from .class_validators import ValidatorListDict + from .types import ModelOrDc + from .typing import ( + AbstractSetIntStr, + AnyClassMethod, + CallableGenerator, + DictAny, + DictStrAny, + MappingIntStrAny, + ReprArgs, + SetStr, + TupleGenerator, + ) + + Model = TypeVar('Model', bound='BaseModel') + +__all__ = 'BaseModel', 'create_model', 'validate_model' + +_T = TypeVar('_T') + + +def validate_custom_root_type(fields: Dict[str, ModelField]) -> None: + if len(fields) > 1: + raise ValueError(f'{ROOT_KEY} cannot be mixed with other fields') + + +def generate_hash_function(frozen: bool) -> Optional[Callable[[Any], int]]: + def hash_function(self_: Any) -> int: + return hash(self_.__class__) + hash(tuple(self_.__dict__.values())) + + return hash_function if frozen else None + + +# If a field is of type `Callable`, its default value should be a function and cannot to ignored. +ANNOTATED_FIELD_UNTOUCHED_TYPES: Tuple[Any, ...] = (property, type, classmethod, staticmethod) +# When creating a `BaseModel` instance, we bypass all the methods, properties... added to the model +UNTOUCHED_TYPES: Tuple[Any, ...] = (FunctionType,) + ANNOTATED_FIELD_UNTOUCHED_TYPES +# Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we need to add this extra +# (somewhat hacky) boolean to keep track of whether we've created the `BaseModel` class yet, and therefore whether it's +# safe to refer to it. If it *hasn't* been created, we assume that the `__new__` call we're in the middle of is for +# the `BaseModel` class, since that's defined immediately after the metaclass. +_is_base_model_class_defined = False + + +@dataclass_transform(kw_only_default=True, field_specifiers=(Field,)) +class ModelMetaclass(ABCMeta): + @no_type_check # noqa C901 + def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901 + fields: Dict[str, ModelField] = {} + config = BaseConfig + validators: 'ValidatorListDict' = {} + + pre_root_validators, post_root_validators = [], [] + private_attributes: Dict[str, ModelPrivateAttr] = {} + base_private_attributes: Dict[str, ModelPrivateAttr] = {} + slots: SetStr = namespace.get('__slots__', ()) + slots = {slots} if isinstance(slots, str) else set(slots) + class_vars: SetStr = set() + hash_func: Optional[Callable[[Any], int]] = None + + for base in reversed(bases): + if _is_base_model_class_defined and issubclass(base, BaseModel) and base != BaseModel: + fields.update(smart_deepcopy(base.__fields__)) + config = inherit_config(base.__config__, config) + validators = inherit_validators(base.__validators__, validators) + pre_root_validators += base.__pre_root_validators__ + post_root_validators += base.__post_root_validators__ + base_private_attributes.update(base.__private_attributes__) + class_vars.update(base.__class_vars__) + hash_func = base.__hash__ + + resolve_forward_refs = kwargs.pop('__resolve_forward_refs__', True) + allowed_config_kwargs: SetStr = { + key + for key in dir(config) + if not (key.startswith('__') and key.endswith('__')) # skip dunder methods and attributes + } + config_kwargs = {key: kwargs.pop(key) for key in kwargs.keys() & allowed_config_kwargs} + config_from_namespace = namespace.get('Config') + if config_kwargs and config_from_namespace: + raise TypeError('Specifying config in two places is ambiguous, use either Config attribute or class kwargs') + config = inherit_config(config_from_namespace, config, **config_kwargs) + + validators = inherit_validators(extract_validators(namespace), validators) + vg = ValidatorGroup(validators) + + for f in fields.values(): + f.set_config(config) + extra_validators = vg.get_validators(f.name) + if extra_validators: + f.class_validators.update(extra_validators) + # re-run prepare to add extra validators + f.populate_validators() + + prepare_config(config, name) + + untouched_types = ANNOTATED_FIELD_UNTOUCHED_TYPES + + def is_untouched(v: Any) -> bool: + return isinstance(v, untouched_types) or v.__class__.__name__ == 'cython_function_or_method' + + if (namespace.get('__module__'), namespace.get('__qualname__')) != ('pydantic.main', 'BaseModel'): + annotations = resolve_annotations(namespace.get('__annotations__', {}), namespace.get('__module__', None)) + # annotation only fields need to come first in fields + for ann_name, ann_type in annotations.items(): + if is_classvar(ann_type): + class_vars.add(ann_name) + elif is_finalvar_with_default_val(ann_type, namespace.get(ann_name, Undefined)): + class_vars.add(ann_name) + elif is_valid_field(ann_name): + validate_field_name(bases, ann_name) + value = namespace.get(ann_name, Undefined) + allowed_types = get_args(ann_type) if is_union(get_origin(ann_type)) else (ann_type,) + if ( + is_untouched(value) + and ann_type != PyObject + and not any( + lenient_issubclass(get_origin(allowed_type), Type) for allowed_type in allowed_types + ) + ): + continue + fields[ann_name] = ModelField.infer( + name=ann_name, + value=value, + annotation=ann_type, + class_validators=vg.get_validators(ann_name), + config=config, + ) + elif ann_name not in namespace and config.underscore_attrs_are_private: + private_attributes[ann_name] = PrivateAttr() + + untouched_types = UNTOUCHED_TYPES + config.keep_untouched + for var_name, value in namespace.items(): + can_be_changed = var_name not in class_vars and not is_untouched(value) + if isinstance(value, ModelPrivateAttr): + if not is_valid_private_name(var_name): + raise NameError( + f'Private attributes "{var_name}" must not be a valid field name; ' + f'Use sunder or dunder names, e. g. "_{var_name}" or "__{var_name}__"' + ) + private_attributes[var_name] = value + elif config.underscore_attrs_are_private and is_valid_private_name(var_name) and can_be_changed: + private_attributes[var_name] = PrivateAttr(default=value) + elif is_valid_field(var_name) and var_name not in annotations and can_be_changed: + validate_field_name(bases, var_name) + inferred = ModelField.infer( + name=var_name, + value=value, + annotation=annotations.get(var_name, Undefined), + class_validators=vg.get_validators(var_name), + config=config, + ) + if var_name in fields: + if lenient_issubclass(inferred.type_, fields[var_name].type_): + inferred.type_ = fields[var_name].type_ + else: + raise TypeError( + f'The type of {name}.{var_name} differs from the new default value; ' + f'if you wish to change the type of this field, please use a type annotation' + ) + fields[var_name] = inferred + + _custom_root_type = ROOT_KEY in fields + if _custom_root_type: + validate_custom_root_type(fields) + vg.check_for_unused() + if config.json_encoders: + json_encoder = partial(custom_pydantic_encoder, config.json_encoders) + else: + json_encoder = pydantic_encoder + pre_rv_new, post_rv_new = extract_root_validators(namespace) + + if hash_func is None: + hash_func = generate_hash_function(config.frozen) + + exclude_from_namespace = fields | private_attributes.keys() | {'__slots__'} + new_namespace = { + '__config__': config, + '__fields__': fields, + '__exclude_fields__': { + name: field.field_info.exclude for name, field in fields.items() if field.field_info.exclude is not None + } + or None, + '__include_fields__': { + name: field.field_info.include for name, field in fields.items() if field.field_info.include is not None + } + or None, + '__validators__': vg.validators, + '__pre_root_validators__': unique_list( + pre_root_validators + pre_rv_new, + name_factory=lambda v: v.__name__, + ), + '__post_root_validators__': unique_list( + post_root_validators + post_rv_new, + name_factory=lambda skip_on_failure_and_v: skip_on_failure_and_v[1].__name__, + ), + '__schema_cache__': {}, + '__json_encoder__': staticmethod(json_encoder), + '__custom_root_type__': _custom_root_type, + '__private_attributes__': {**base_private_attributes, **private_attributes}, + '__slots__': slots | private_attributes.keys(), + '__hash__': hash_func, + '__class_vars__': class_vars, + **{n: v for n, v in namespace.items() if n not in exclude_from_namespace}, + } + + cls = super().__new__(mcs, name, bases, new_namespace, **kwargs) + # set __signature__ attr only for model class, but not for its instances + cls.__signature__ = ClassAttribute('__signature__', generate_model_signature(cls.__init__, fields, config)) + if resolve_forward_refs: + cls.__try_update_forward_refs__() + + # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487 + # for attributes not in `new_namespace` (e.g. private attributes) + for name, obj in namespace.items(): + if name not in new_namespace: + set_name = getattr(obj, '__set_name__', None) + if callable(set_name): + set_name(cls, name) + + return cls + + def __instancecheck__(self, instance: Any) -> bool: + """ + Avoid calling ABC _abc_subclasscheck unless we're pretty sure. + + See #3829 and python/cpython#92810 + """ + return hasattr(instance, '__fields__') and super().__instancecheck__(instance) + + +object_setattr = object.__setattr__ + + +class BaseModel(Representation, metaclass=ModelMetaclass): + if TYPE_CHECKING: + # populated by the metaclass, defined here to help IDEs only + __fields__: ClassVar[Dict[str, ModelField]] = {} + __include_fields__: ClassVar[Optional[Mapping[str, Any]]] = None + __exclude_fields__: ClassVar[Optional[Mapping[str, Any]]] = None + __validators__: ClassVar[Dict[str, AnyCallable]] = {} + __pre_root_validators__: ClassVar[List[AnyCallable]] + __post_root_validators__: ClassVar[List[Tuple[bool, AnyCallable]]] + __config__: ClassVar[Type[BaseConfig]] = BaseConfig + __json_encoder__: ClassVar[Callable[[Any], Any]] = lambda x: x + __schema_cache__: ClassVar['DictAny'] = {} + __custom_root_type__: ClassVar[bool] = False + __signature__: ClassVar['Signature'] + __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]] + __class_vars__: ClassVar[SetStr] + __fields_set__: ClassVar[SetStr] = set() + + Config = BaseConfig + __slots__ = ('__dict__', '__fields_set__') + __doc__ = '' # Null out the Representation docstring + + def __init__(__pydantic_self__, **data: Any) -> None: + """ + Create a new model by parsing and validating input data from keyword arguments. + + Raises ValidationError if the input data cannot be parsed to form a valid model. + """ + # Uses something other than `self` the first arg to allow "self" as a settable attribute + values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data) + if validation_error: + raise validation_error + try: + object_setattr(__pydantic_self__, '__dict__', values) + except TypeError as e: + raise TypeError( + 'Model values must be a dict; you may not have returned a dictionary from a root validator' + ) from e + object_setattr(__pydantic_self__, '__fields_set__', fields_set) + __pydantic_self__._init_private_attributes() + + @no_type_check + def __setattr__(self, name, value): # noqa: C901 (ignore complexity) + if name in self.__private_attributes__ or name in DUNDER_ATTRIBUTES: + return object_setattr(self, name, value) + + if self.__config__.extra is not Extra.allow and name not in self.__fields__: + raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"') + elif not self.__config__.allow_mutation or self.__config__.frozen: + raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment') + elif name in self.__fields__ and self.__fields__[name].final: + raise TypeError( + f'"{self.__class__.__name__}" object "{name}" field is final and does not support reassignment' + ) + elif self.__config__.validate_assignment: + new_values = {**self.__dict__, name: value} + + for validator in self.__pre_root_validators__: + try: + new_values = validator(self.__class__, new_values) + except (ValueError, TypeError, AssertionError) as exc: + raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], self.__class__) + + known_field = self.__fields__.get(name, None) + if known_field: + # We want to + # - make sure validators are called without the current value for this field inside `values` + # - keep other values (e.g. submodels) untouched (using `BaseModel.dict()` will change them into dicts) + # - keep the order of the fields + if not known_field.field_info.allow_mutation: + raise TypeError(f'"{known_field.name}" has allow_mutation set to False and cannot be assigned') + dict_without_original_value = {k: v for k, v in self.__dict__.items() if k != name} + value, error_ = known_field.validate(value, dict_without_original_value, loc=name, cls=self.__class__) + if error_: + raise ValidationError([error_], self.__class__) + else: + new_values[name] = value + + errors = [] + for skip_on_failure, validator in self.__post_root_validators__: + if skip_on_failure and errors: + continue + try: + new_values = validator(self.__class__, new_values) + except (ValueError, TypeError, AssertionError) as exc: + errors.append(ErrorWrapper(exc, loc=ROOT_KEY)) + if errors: + raise ValidationError(errors, self.__class__) + + # update the whole __dict__ as other values than just `value` + # may be changed (e.g. with `root_validator`) + object_setattr(self, '__dict__', new_values) + else: + self.__dict__[name] = value + + self.__fields_set__.add(name) + + def __getstate__(self) -> 'DictAny': + private_attrs = ((k, getattr(self, k, Undefined)) for k in self.__private_attributes__) + return { + '__dict__': self.__dict__, + '__fields_set__': self.__fields_set__, + '__private_attribute_values__': {k: v for k, v in private_attrs if v is not Undefined}, + } + + def __setstate__(self, state: 'DictAny') -> None: + object_setattr(self, '__dict__', state['__dict__']) + object_setattr(self, '__fields_set__', state['__fields_set__']) + for name, value in state.get('__private_attribute_values__', {}).items(): + object_setattr(self, name, value) + + def _init_private_attributes(self) -> None: + for name, private_attr in self.__private_attributes__.items(): + default = private_attr.get_default() + if default is not Undefined: + object_setattr(self, name, default) + + def dict( + self, + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + by_alias: bool = False, + skip_defaults: Optional[bool] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> 'DictStrAny': + """ + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + """ + if skip_defaults is not None: + warnings.warn( + f'{self.__class__.__name__}.dict(): "skip_defaults" is deprecated and replaced by "exclude_unset"', + DeprecationWarning, + ) + exclude_unset = skip_defaults + + return dict( + self._iter( + to_dict=True, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + ) + + def json( + self, + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + by_alias: bool = False, + skip_defaults: Optional[bool] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + encoder: Optional[Callable[[Any], Any]] = None, + models_as_dict: bool = True, + **dumps_kwargs: Any, + ) -> str: + """ + Generate a JSON representation of the model, `include` and `exclude` arguments as per `dict()`. + + `encoder` is an optional function to supply as `default` to json.dumps(), other arguments as per `json.dumps()`. + """ + if skip_defaults is not None: + warnings.warn( + f'{self.__class__.__name__}.json(): "skip_defaults" is deprecated and replaced by "exclude_unset"', + DeprecationWarning, + ) + exclude_unset = skip_defaults + encoder = cast(Callable[[Any], Any], encoder or self.__json_encoder__) + + # We don't directly call `self.dict()`, which does exactly this with `to_dict=True` + # because we want to be able to keep raw `BaseModel` instances and not as `dict`. + # This allows users to write custom JSON encoders for given `BaseModel` classes. + data = dict( + self._iter( + to_dict=models_as_dict, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + ) + if self.__custom_root_type__: + data = data[ROOT_KEY] + return self.__config__.json_dumps(data, default=encoder, **dumps_kwargs) + + @classmethod + def _enforce_dict_if_root(cls, obj: Any) -> Any: + if cls.__custom_root_type__ and ( + not (isinstance(obj, dict) and obj.keys() == {ROOT_KEY}) + and not (isinstance(obj, BaseModel) and obj.__fields__.keys() == {ROOT_KEY}) + or cls.__fields__[ROOT_KEY].shape in MAPPING_LIKE_SHAPES + ): + return {ROOT_KEY: obj} + else: + return obj + + @classmethod + def parse_obj(cls: Type['Model'], obj: Any) -> 'Model': + obj = cls._enforce_dict_if_root(obj) + if not isinstance(obj, dict): + try: + obj = dict(obj) + except (TypeError, ValueError) as e: + exc = TypeError(f'{cls.__name__} expected dict not {obj.__class__.__name__}') + raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls) from e + return cls(**obj) + + @classmethod + def parse_raw( + cls: Type['Model'], + b: StrBytes, + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + ) -> 'Model': + try: + obj = load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=cls.__config__.json_loads, + ) + except (ValueError, TypeError, UnicodeDecodeError) as e: + raise ValidationError([ErrorWrapper(e, loc=ROOT_KEY)], cls) + return cls.parse_obj(obj) + + @classmethod + def parse_file( + cls: Type['Model'], + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + ) -> 'Model': + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=cls.__config__.json_loads, + ) + return cls.parse_obj(obj) + + @classmethod + def from_orm(cls: Type['Model'], obj: Any) -> 'Model': + if not cls.__config__.orm_mode: + raise ConfigError('You must have the config attribute orm_mode=True to use from_orm') + obj = {ROOT_KEY: obj} if cls.__custom_root_type__ else cls._decompose_class(obj) + m = cls.__new__(cls) + values, fields_set, validation_error = validate_model(cls, obj) + if validation_error: + raise validation_error + object_setattr(m, '__dict__', values) + object_setattr(m, '__fields_set__', fields_set) + m._init_private_attributes() + return m + + @classmethod + def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **values: Any) -> 'Model': + """ + Creates a new model setting __dict__ and __fields_set__ from trusted or pre-validated data. + Default values are respected, but no other validation is performed. + Behaves as if `Config.extra = 'allow'` was set since it adds all passed values + """ + m = cls.__new__(cls) + fields_values: Dict[str, Any] = {} + for name, field in cls.__fields__.items(): + if field.alt_alias and field.alias in values: + fields_values[name] = values[field.alias] + elif name in values: + fields_values[name] = values[name] + elif not field.required: + fields_values[name] = field.get_default() + fields_values.update(values) + object_setattr(m, '__dict__', fields_values) + if _fields_set is None: + _fields_set = set(values.keys()) + object_setattr(m, '__fields_set__', _fields_set) + m._init_private_attributes() + return m + + def _copy_and_set_values(self: 'Model', values: 'DictStrAny', fields_set: 'SetStr', *, deep: bool) -> 'Model': + if deep: + # chances of having empty dict here are quite low for using smart_deepcopy + values = deepcopy(values) + + cls = self.__class__ + m = cls.__new__(cls) + object_setattr(m, '__dict__', values) + object_setattr(m, '__fields_set__', fields_set) + for name in self.__private_attributes__: + value = getattr(self, name, Undefined) + if value is not Undefined: + if deep: + value = deepcopy(value) + object_setattr(m, name, value) + + return m + + def copy( + self: 'Model', + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + update: Optional['DictStrAny'] = None, + deep: bool = False, + ) -> 'Model': + """ + Duplicate a model, optionally choose which fields to include, exclude and change. + + :param include: fields to include in new model + :param exclude: fields to exclude from new model, as with values this takes precedence over include + :param update: values to change/add in the new model. Note: the data is not validated before creating + the new model: you should trust this data + :param deep: set to `True` to make a deep copy of the model + :return: new model instance + """ + + values = dict( + self._iter(to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False), + **(update or {}), + ) + + # new `__fields_set__` can have unset optional fields with a set value in `update` kwarg + if update: + fields_set = self.__fields_set__ | update.keys() + else: + fields_set = set(self.__fields_set__) + + return self._copy_and_set_values(values, fields_set, deep=deep) + + @classmethod + def schema(cls, by_alias: bool = True, ref_template: str = default_ref_template) -> 'DictStrAny': + cached = cls.__schema_cache__.get((by_alias, ref_template)) + if cached is not None: + return cached + s = model_schema(cls, by_alias=by_alias, ref_template=ref_template) + cls.__schema_cache__[(by_alias, ref_template)] = s + return s + + @classmethod + def schema_json( + cls, *, by_alias: bool = True, ref_template: str = default_ref_template, **dumps_kwargs: Any + ) -> str: + from .json import pydantic_encoder + + return cls.__config__.json_dumps( + cls.schema(by_alias=by_alias, ref_template=ref_template), default=pydantic_encoder, **dumps_kwargs + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls: Type['Model'], value: Any) -> 'Model': + if isinstance(value, cls): + copy_on_model_validation = cls.__config__.copy_on_model_validation + # whether to deep or shallow copy the model on validation, None means do not copy + deep_copy: Optional[bool] = None + if copy_on_model_validation not in {'deep', 'shallow', 'none'}: + # Warn about deprecated behavior + warnings.warn( + "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", DeprecationWarning + ) + if copy_on_model_validation: + deep_copy = False + + if copy_on_model_validation == 'shallow': + # shallow copy + deep_copy = False + elif copy_on_model_validation == 'deep': + # deep copy + deep_copy = True + + if deep_copy is None: + return value + else: + return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=deep_copy) + + value = cls._enforce_dict_if_root(value) + + if isinstance(value, dict): + return cls(**value) + elif cls.__config__.orm_mode: + return cls.from_orm(value) + else: + try: + value_as_dict = dict(value) + except (TypeError, ValueError) as e: + raise DictError() from e + return cls(**value_as_dict) + + @classmethod + def _decompose_class(cls: Type['Model'], obj: Any) -> GetterDict: + if isinstance(obj, GetterDict): + return obj + return cls.__config__.getter_dict(obj) + + @classmethod + @no_type_check + def _get_value( + cls, + v: Any, + to_dict: bool, + by_alias: bool, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']], + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']], + exclude_unset: bool, + exclude_defaults: bool, + exclude_none: bool, + ) -> Any: + if isinstance(v, BaseModel): + if to_dict: + v_dict = v.dict( + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=include, + exclude=exclude, + exclude_none=exclude_none, + ) + if ROOT_KEY in v_dict: + return v_dict[ROOT_KEY] + return v_dict + else: + return v.copy(include=include, exclude=exclude) + + value_exclude = ValueItems(v, exclude) if exclude else None + value_include = ValueItems(v, include) if include else None + + if isinstance(v, dict): + return { + k_: cls._get_value( + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(k_), + exclude=value_exclude and value_exclude.for_element(k_), + exclude_none=exclude_none, + ) + for k_, v_ in v.items() + if (not value_exclude or not value_exclude.is_excluded(k_)) + and (not value_include or value_include.is_included(k_)) + } + + elif sequence_like(v): + seq_args = ( + cls._get_value( + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(i), + exclude=value_exclude and value_exclude.for_element(i), + exclude_none=exclude_none, + ) + for i, v_ in enumerate(v) + if (not value_exclude or not value_exclude.is_excluded(i)) + and (not value_include or value_include.is_included(i)) + ) + + return v.__class__(*seq_args) if is_namedtuple(v.__class__) else v.__class__(seq_args) + + elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False): + return v.value + + else: + return v + + @classmethod + def __try_update_forward_refs__(cls, **localns: Any) -> None: + """ + Same as update_forward_refs but will not raise exception + when forward references are not defined. + """ + update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,)) + + @classmethod + def update_forward_refs(cls, **localns: Any) -> None: + """ + Try to update ForwardRefs on fields based on this Model, globalns and localns. + """ + update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns) + + def __iter__(self) -> 'TupleGenerator': + """ + so `dict(model)` works + """ + yield from self.__dict__.items() + + def _iter( + self, + to_dict: bool = False, + by_alias: bool = False, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> 'TupleGenerator': + # Merge field set excludes with explicit exclude parameter with explicit overriding field set options. + # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case. + if exclude is not None or self.__exclude_fields__ is not None: + exclude = ValueItems.merge(self.__exclude_fields__, exclude) + + if include is not None or self.__include_fields__ is not None: + include = ValueItems.merge(self.__include_fields__, include, intersect=True) + + allowed_keys = self._calculate_keys( + include=include, exclude=exclude, exclude_unset=exclude_unset # type: ignore + ) + if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none): + # huge boost for plain _iter() + yield from self.__dict__.items() + return + + value_exclude = ValueItems(self, exclude) if exclude is not None else None + value_include = ValueItems(self, include) if include is not None else None + + for field_key, v in self.__dict__.items(): + if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None): + continue + + if exclude_defaults: + model_field = self.__fields__.get(field_key) + if not getattr(model_field, 'required', True) and getattr(model_field, 'default', _missing) == v: + continue + + if by_alias and field_key in self.__fields__: + dict_key = self.__fields__[field_key].alias + else: + dict_key = field_key + + if to_dict or value_include or value_exclude: + v = self._get_value( + v, + to_dict=to_dict, + by_alias=by_alias, + include=value_include and value_include.for_element(field_key), + exclude=value_exclude and value_exclude.for_element(field_key), + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + yield dict_key, v + + def _calculate_keys( + self, + include: Optional['MappingIntStrAny'], + exclude: Optional['MappingIntStrAny'], + exclude_unset: bool, + update: Optional['DictStrAny'] = None, + ) -> Optional[AbstractSet[str]]: + if include is None and exclude is None and exclude_unset is False: + return None + + keys: AbstractSet[str] + if exclude_unset: + keys = self.__fields_set__.copy() + else: + keys = self.__dict__.keys() + + if include is not None: + keys &= include.keys() + + if update: + keys -= update.keys() + + if exclude: + keys -= {k for k, v in exclude.items() if ValueItems.is_true(v)} + + return keys + + def __eq__(self, other: Any) -> bool: + if isinstance(other, BaseModel): + return self.dict() == other.dict() + else: + return self.dict() == other + + def __repr_args__(self) -> 'ReprArgs': + return [ + (k, v) + for k, v in self.__dict__.items() + if k not in DUNDER_ATTRIBUTES and (k not in self.__fields__ or self.__fields__[k].field_info.repr) + ] + + +_is_base_model_class_defined = True + + +@overload +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: None = None, + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + **field_definitions: Any, +) -> Type['BaseModel']: + ... + + +@overload +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: Union[Type['Model'], Tuple[Type['Model'], ...]], + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + **field_definitions: Any, +) -> Type['Model']: + ... + + +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: Union[None, Type['Model'], Tuple[Type['Model'], ...]] = None, + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + __slots__: Optional[Tuple[str, ...]] = None, + **field_definitions: Any, +) -> Type['Model']: + """ + Dynamically create a model. + :param __model_name: name of the created model + :param __config__: config class to use for the new model + :param __base__: base class for the new model to inherit from + :param __module__: module of the created model + :param __validators__: a dict of method names and @validator class methods + :param __cls_kwargs__: a dict for class creation + :param __slots__: Deprecated, `__slots__` should not be passed to `create_model` + :param field_definitions: fields of the model (or extra fields if a base is supplied) + in the format `=(, )` or `=, e.g. + `foobar=(str, ...)` or `foobar=123`, or, for complex use-cases, in the format + `=` or `=(, )`, e.g. + `foo=Field(datetime, default_factory=datetime.utcnow, alias='bar')` or + `foo=(str, FieldInfo(title='Foo'))` + """ + if __slots__ is not None: + # __slots__ will be ignored from here on + warnings.warn('__slots__ should not be passed to create_model', RuntimeWarning) + + if __base__ is not None: + if __config__ is not None: + raise ConfigError('to avoid confusion __config__ and __base__ cannot be used together') + if not isinstance(__base__, tuple): + __base__ = (__base__,) + else: + __base__ = (cast(Type['Model'], BaseModel),) + + __cls_kwargs__ = __cls_kwargs__ or {} + + fields = {} + annotations = {} + + for f_name, f_def in field_definitions.items(): + if not is_valid_field(f_name): + warnings.warn(f'fields may not start with an underscore, ignoring "{f_name}"', RuntimeWarning) + if isinstance(f_def, tuple): + try: + f_annotation, f_value = f_def + except ValueError as e: + raise ConfigError( + 'field definitions should either be a tuple of (, ) or just a ' + 'default value, unfortunately this means tuples as ' + 'default values are not allowed' + ) from e + else: + f_annotation, f_value = None, f_def + + if f_annotation: + annotations[f_name] = f_annotation + fields[f_name] = f_value + + namespace: 'DictStrAny' = {'__annotations__': annotations, '__module__': __module__} + if __validators__: + namespace.update(__validators__) + namespace.update(fields) + if __config__: + namespace['Config'] = inherit_config(__config__, BaseConfig) + resolved_bases = resolve_bases(__base__) + meta, ns, kwds = prepare_class(__model_name, resolved_bases, kwds=__cls_kwargs__) + if resolved_bases is not __base__: + ns['__orig_bases__'] = __base__ + namespace.update(ns) + return meta(__model_name, resolved_bases, namespace, **kwds) + + +_missing = object() + + +def validate_model( # noqa: C901 (ignore complexity) + model: Type[BaseModel], input_data: 'DictStrAny', cls: 'ModelOrDc' = None +) -> Tuple['DictStrAny', 'SetStr', Optional[ValidationError]]: + """ + validate data against a model. + """ + values = {} + errors = [] + # input_data names, possibly alias + names_used = set() + # field names, never aliases + fields_set = set() + config = model.__config__ + check_extra = config.extra is not Extra.ignore + cls_ = cls or model + + for validator in model.__pre_root_validators__: + try: + input_data = validator(cls_, input_data) + except (ValueError, TypeError, AssertionError) as exc: + return {}, set(), ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls_) + + for name, field in model.__fields__.items(): + value = input_data.get(field.alias, _missing) + using_name = False + if value is _missing and config.allow_population_by_field_name and field.alt_alias: + value = input_data.get(field.name, _missing) + using_name = True + + if value is _missing: + if field.required: + errors.append(ErrorWrapper(MissingError(), loc=field.alias)) + continue + + value = field.get_default() + + if not config.validate_all and not field.validate_always: + values[name] = value + continue + else: + fields_set.add(name) + if check_extra: + names_used.add(field.name if using_name else field.alias) + + v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls_) + if isinstance(errors_, ErrorWrapper): + errors.append(errors_) + elif isinstance(errors_, list): + errors.extend(errors_) + else: + values[name] = v_ + + if check_extra: + if isinstance(input_data, GetterDict): + extra = input_data.extra_keys() - names_used + else: + extra = input_data.keys() - names_used + if extra: + fields_set |= extra + if config.extra is Extra.allow: + for f in extra: + values[f] = input_data[f] + else: + for f in sorted(extra): + errors.append(ErrorWrapper(ExtraError(), loc=f)) + + for skip_on_failure, validator in model.__post_root_validators__: + if skip_on_failure and errors: + continue + try: + values = validator(cls_, values) + except (ValueError, TypeError, AssertionError) as exc: + errors.append(ErrorWrapper(exc, loc=ROOT_KEY)) + + if errors: + return values, fields_set, ValidationError(errors, cls_) + else: + return values, fields_set, None diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/mypy.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/mypy.py new file mode 100644 index 0000000..0262120 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/mypy.py @@ -0,0 +1,944 @@ +import sys +from configparser import ConfigParser +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType, Union + +from mypy.errorcodes import ErrorCode +from mypy.nodes import ( + ARG_NAMED, + ARG_NAMED_OPT, + ARG_OPT, + ARG_POS, + ARG_STAR2, + MDEF, + Argument, + AssignmentStmt, + Block, + CallExpr, + ClassDef, + Context, + Decorator, + EllipsisExpr, + FuncBase, + FuncDef, + JsonDict, + MemberExpr, + NameExpr, + PassStmt, + PlaceholderNode, + RefExpr, + StrExpr, + SymbolNode, + SymbolTableNode, + TempNode, + TypeInfo, + TypeVarExpr, + Var, +) +from mypy.options import Options +from mypy.plugin import ( + CheckerPluginInterface, + ClassDefContext, + FunctionContext, + MethodContext, + Plugin, + ReportConfigContext, + SemanticAnalyzerPluginInterface, +) +from mypy.plugins import dataclasses +from mypy.semanal import set_callable_name # type: ignore +from mypy.server.trigger import make_wildcard_trigger +from mypy.types import ( + AnyType, + CallableType, + Instance, + NoneType, + Overloaded, + ProperType, + Type, + TypeOfAny, + TypeType, + TypeVarType, + UnionType, + get_proper_type, +) +from mypy.typevars import fill_typevars +from mypy.util import get_unique_redefinition_name +from mypy.version import __version__ as mypy_version + +from .utils import is_valid_field + +try: + from mypy.types import TypeVarDef # type: ignore[attr-defined] +except ImportError: # pragma: no cover + # Backward-compatible with TypeVarDef from Mypy 0.910. + from mypy.types import TypeVarType as TypeVarDef + +CONFIGFILE_KEY = 'pydantic-mypy' +METADATA_KEY = 'pydantic-mypy-metadata' +_NAMESPACE = __name__[:-5] # 'pydantic' in 1.10.X, 'pydantic.v1' in v2.X +BASEMODEL_FULLNAME = f'{_NAMESPACE}.main.BaseModel' +BASESETTINGS_FULLNAME = f'{_NAMESPACE}.env_settings.BaseSettings' +MODEL_METACLASS_FULLNAME = f'{_NAMESPACE}.main.ModelMetaclass' +FIELD_FULLNAME = f'{_NAMESPACE}.fields.Field' +DATACLASS_FULLNAME = f'{_NAMESPACE}.dataclasses.dataclass' + + +def parse_mypy_version(version: str) -> Tuple[int, ...]: + return tuple(map(int, version.partition('+')[0].split('.'))) + + +MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version) +BUILTINS_NAME = 'builtins' if MYPY_VERSION_TUPLE >= (0, 930) else '__builtins__' + +# Increment version if plugin changes and mypy caches should be invalidated +__version__ = 2 + + +def plugin(version: str) -> 'TypingType[Plugin]': + """ + `version` is the mypy version string + + We might want to use this to print a warning if the mypy version being used is + newer, or especially older, than we expect (or need). + """ + return PydanticPlugin + + +class PydanticPlugin(Plugin): + def __init__(self, options: Options) -> None: + self.plugin_config = PydanticPluginConfig(options) + self._plugin_data = self.plugin_config.to_data() + super().__init__(options) + + def get_base_class_hook(self, fullname: str) -> 'Optional[Callable[[ClassDefContext], None]]': + sym = self.lookup_fully_qualified(fullname) + if sym and isinstance(sym.node, TypeInfo): # pragma: no branch + # No branching may occur if the mypy cache has not been cleared + if any(get_fullname(base) == BASEMODEL_FULLNAME for base in sym.node.mro): + return self._pydantic_model_class_maker_callback + return None + + def get_metaclass_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]: + if fullname == MODEL_METACLASS_FULLNAME: + return self._pydantic_model_metaclass_marker_callback + return None + + def get_function_hook(self, fullname: str) -> 'Optional[Callable[[FunctionContext], Type]]': + sym = self.lookup_fully_qualified(fullname) + if sym and sym.fullname == FIELD_FULLNAME: + return self._pydantic_field_callback + return None + + def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]: + if fullname.endswith('.from_orm'): + return from_orm_callback + return None + + def get_class_decorator_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]: + """Mark pydantic.dataclasses as dataclass. + + Mypy version 1.1.1 added support for `@dataclass_transform` decorator. + """ + if fullname == DATACLASS_FULLNAME and MYPY_VERSION_TUPLE < (1, 1): + return dataclasses.dataclass_class_maker_callback # type: ignore[return-value] + return None + + def report_config_data(self, ctx: ReportConfigContext) -> Dict[str, Any]: + """Return all plugin config data. + + Used by mypy to determine if cache needs to be discarded. + """ + return self._plugin_data + + def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> None: + transformer = PydanticModelTransformer(ctx, self.plugin_config) + transformer.transform() + + def _pydantic_model_metaclass_marker_callback(self, ctx: ClassDefContext) -> None: + """Reset dataclass_transform_spec attribute of ModelMetaclass. + + Let the plugin handle it. This behavior can be disabled + if 'debug_dataclass_transform' is set to True', for testing purposes. + """ + if self.plugin_config.debug_dataclass_transform: + return + info_metaclass = ctx.cls.info.declared_metaclass + assert info_metaclass, "callback not passed from 'get_metaclass_hook'" + if getattr(info_metaclass.type, 'dataclass_transform_spec', None): + info_metaclass.type.dataclass_transform_spec = None # type: ignore[attr-defined] + + def _pydantic_field_callback(self, ctx: FunctionContext) -> 'Type': + """ + Extract the type of the `default` argument from the Field function, and use it as the return type. + + In particular: + * Check whether the default and default_factory argument is specified. + * Output an error if both are specified. + * Retrieve the type of the argument which is specified, and use it as return type for the function. + """ + default_any_type = ctx.default_return_type + + assert ctx.callee_arg_names[0] == 'default', '"default" is no longer first argument in Field()' + assert ctx.callee_arg_names[1] == 'default_factory', '"default_factory" is no longer second argument in Field()' + default_args = ctx.args[0] + default_factory_args = ctx.args[1] + + if default_args and default_factory_args: + error_default_and_default_factory_specified(ctx.api, ctx.context) + return default_any_type + + if default_args: + default_type = ctx.arg_types[0][0] + default_arg = default_args[0] + + # Fallback to default Any type if the field is required + if not isinstance(default_arg, EllipsisExpr): + return default_type + + elif default_factory_args: + default_factory_type = ctx.arg_types[1][0] + + # Functions which use `ParamSpec` can be overloaded, exposing the callable's types as a parameter + # Pydantic calls the default factory without any argument, so we retrieve the first item + if isinstance(default_factory_type, Overloaded): + if MYPY_VERSION_TUPLE > (0, 910): + default_factory_type = default_factory_type.items[0] + else: + # Mypy0.910 exposes the items of overloaded types in a function + default_factory_type = default_factory_type.items()[0] # type: ignore[operator] + + if isinstance(default_factory_type, CallableType): + ret_type = default_factory_type.ret_type + # mypy doesn't think `ret_type` has `args`, you'd think mypy should know, + # add this check in case it varies by version + args = getattr(ret_type, 'args', None) + if args: + if all(isinstance(arg, TypeVarType) for arg in args): + # Looks like the default factory is a type like `list` or `dict`, replace all args with `Any` + ret_type.args = tuple(default_any_type for _ in args) # type: ignore[attr-defined] + return ret_type + + return default_any_type + + +class PydanticPluginConfig: + __slots__ = ( + 'init_forbid_extra', + 'init_typed', + 'warn_required_dynamic_aliases', + 'warn_untyped_fields', + 'debug_dataclass_transform', + ) + init_forbid_extra: bool + init_typed: bool + warn_required_dynamic_aliases: bool + warn_untyped_fields: bool + debug_dataclass_transform: bool # undocumented + + def __init__(self, options: Options) -> None: + if options.config_file is None: # pragma: no cover + return + + toml_config = parse_toml(options.config_file) + if toml_config is not None: + config = toml_config.get('tool', {}).get('pydantic-mypy', {}) + for key in self.__slots__: + setting = config.get(key, False) + if not isinstance(setting, bool): + raise ValueError(f'Configuration value must be a boolean for key: {key}') + setattr(self, key, setting) + else: + plugin_config = ConfigParser() + plugin_config.read(options.config_file) + for key in self.__slots__: + setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False) + setattr(self, key, setting) + + def to_data(self) -> Dict[str, Any]: + return {key: getattr(self, key) for key in self.__slots__} + + +def from_orm_callback(ctx: MethodContext) -> Type: + """ + Raise an error if orm_mode is not enabled + """ + model_type: Instance + ctx_type = ctx.type + if isinstance(ctx_type, TypeType): + ctx_type = ctx_type.item + if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance): + model_type = ctx_type.ret_type # called on the class + elif isinstance(ctx_type, Instance): + model_type = ctx_type # called on an instance (unusual, but still valid) + else: # pragma: no cover + detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})' + error_unexpected_behavior(detail, ctx.api, ctx.context) + return ctx.default_return_type + pydantic_metadata = model_type.type.metadata.get(METADATA_KEY) + if pydantic_metadata is None: + return ctx.default_return_type + orm_mode = pydantic_metadata.get('config', {}).get('orm_mode') + if orm_mode is not True: + error_from_orm(get_name(model_type.type), ctx.api, ctx.context) + return ctx.default_return_type + + +class PydanticModelTransformer: + tracked_config_fields: Set[str] = { + 'extra', + 'allow_mutation', + 'frozen', + 'orm_mode', + 'allow_population_by_field_name', + 'alias_generator', + } + + def __init__(self, ctx: ClassDefContext, plugin_config: PydanticPluginConfig) -> None: + self._ctx = ctx + self.plugin_config = plugin_config + + def transform(self) -> None: + """ + Configures the BaseModel subclass according to the plugin settings. + + In particular: + * determines the model config and fields, + * adds a fields-aware signature for the initializer and construct methods + * freezes the class if allow_mutation = False or frozen = True + * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses + """ + ctx = self._ctx + info = ctx.cls.info + + self.adjust_validator_signatures() + config = self.collect_config() + fields = self.collect_fields(config) + is_settings = any(get_fullname(base) == BASESETTINGS_FULLNAME for base in info.mro[:-1]) + self.add_initializer(fields, config, is_settings) + self.add_construct_method(fields) + self.set_frozen(fields, frozen=config.allow_mutation is False or config.frozen is True) + info.metadata[METADATA_KEY] = { + 'fields': {field.name: field.serialize() for field in fields}, + 'config': config.set_values_dict(), + } + + def adjust_validator_signatures(self) -> None: + """When we decorate a function `f` with `pydantic.validator(...), mypy sees + `f` as a regular method taking a `self` instance, even though pydantic + internally wraps `f` with `classmethod` if necessary. + + Teach mypy this by marking any function whose outermost decorator is a + `validator()` call as a classmethod. + """ + for name, sym in self._ctx.cls.info.names.items(): + if isinstance(sym.node, Decorator): + first_dec = sym.node.original_decorators[0] + if ( + isinstance(first_dec, CallExpr) + and isinstance(first_dec.callee, NameExpr) + and first_dec.callee.fullname == f'{_NAMESPACE}.class_validators.validator' + ): + sym.node.func.is_class = True + + def collect_config(self) -> 'ModelConfigData': + """ + Collects the values of the config attributes that are used by the plugin, accounting for parent classes. + """ + ctx = self._ctx + cls = ctx.cls + config = ModelConfigData() + for stmt in cls.defs.body: + if not isinstance(stmt, ClassDef): + continue + if stmt.name == 'Config': + for substmt in stmt.defs.body: + if not isinstance(substmt, AssignmentStmt): + continue + config.update(self.get_config_update(substmt)) + if ( + config.has_alias_generator + and not config.allow_population_by_field_name + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(ctx.api, stmt) + for info in cls.info.mro[1:]: # 0 is the current class + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of fields in its ancestors + ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info))) + for name, value in info.metadata[METADATA_KEY]['config'].items(): + config.setdefault(name, value) + return config + + def collect_fields(self, model_config: 'ModelConfigData') -> List['PydanticModelField']: + """ + Collects the fields for the model, accounting for parent classes + """ + # First, collect fields belonging to the current class. + ctx = self._ctx + cls = self._ctx.cls + fields = [] # type: List[PydanticModelField] + known_fields = set() # type: Set[str] + for stmt in cls.defs.body: + if not isinstance(stmt, AssignmentStmt): # `and stmt.new_syntax` to require annotation + continue + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or not is_valid_field(lhs.name): + continue + + if not stmt.new_syntax and self.plugin_config.warn_untyped_fields: + error_untyped_fields(ctx.api, stmt) + + # if lhs.name == '__config__': # BaseConfig not well handled; I'm not sure why yet + # continue + + sym = cls.info.names.get(lhs.name) + if sym is None: # pragma: no cover + # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation) + # This is the same logic used in the dataclasses plugin + continue + + node = sym.node + if isinstance(node, PlaceholderNode): # pragma: no cover + # See the PlaceholderNode docstring for more detail about how this can occur + # Basically, it is an edge case when dealing with complex import logic + # This is the same logic used in the dataclasses plugin + continue + if not isinstance(node, Var): # pragma: no cover + # Don't know if this edge case still happens with the `is_valid_field` check above + # but better safe than sorry + continue + + # x: ClassVar[int] is ignored by dataclasses. + if node.is_classvar: + continue + + is_required = self.get_is_required(cls, stmt, lhs) + alias, has_dynamic_alias = self.get_alias_info(stmt) + if ( + has_dynamic_alias + and not model_config.allow_population_by_field_name + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(ctx.api, stmt) + fields.append( + PydanticModelField( + name=lhs.name, + is_required=is_required, + alias=alias, + has_dynamic_alias=has_dynamic_alias, + line=stmt.line, + column=stmt.column, + ) + ) + known_fields.add(lhs.name) + all_fields = fields.copy() + for info in cls.info.mro[1:]: # 0 is the current class, -2 is BaseModel, -1 is object + if METADATA_KEY not in info.metadata: + continue + + superclass_fields = [] + # Each class depends on the set of fields in its ancestors + ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info))) + + for name, data in info.metadata[METADATA_KEY]['fields'].items(): + if name not in known_fields: + field = PydanticModelField.deserialize(info, data) + known_fields.add(name) + superclass_fields.append(field) + else: + (field,) = (a for a in all_fields if a.name == name) + all_fields.remove(field) + superclass_fields.append(field) + all_fields = superclass_fields + all_fields + return all_fields + + def add_initializer(self, fields: List['PydanticModelField'], config: 'ModelConfigData', is_settings: bool) -> None: + """ + Adds a fields-aware `__init__` method to the class. + + The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings. + """ + ctx = self._ctx + typed = self.plugin_config.init_typed + use_alias = config.allow_population_by_field_name is not True + force_all_optional = is_settings or bool( + config.has_alias_generator and not config.allow_population_by_field_name + ) + init_arguments = self.get_field_arguments( + fields, typed=typed, force_all_optional=force_all_optional, use_alias=use_alias + ) + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + init_arguments.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + if '__init__' not in ctx.cls.info.names: + add_method(ctx, '__init__', init_arguments, NoneType()) + + def add_construct_method(self, fields: List['PydanticModelField']) -> None: + """ + Adds a fully typed `construct` classmethod to the class. + + Similar to the fields-aware __init__ method, but always uses the field names (not aliases), + and does not treat settings fields as optional. + """ + ctx = self._ctx + set_str = ctx.api.named_type(f'{BUILTINS_NAME}.set', [ctx.api.named_type(f'{BUILTINS_NAME}.str')]) + optional_set_str = UnionType([set_str, NoneType()]) + fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT) + construct_arguments = self.get_field_arguments(fields, typed=True, force_all_optional=False, use_alias=False) + construct_arguments = [fields_set_argument] + construct_arguments + + obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object') + self_tvar_name = '_PydanticBaseModel' # Make sure it does not conflict with other names in the class + tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name + if MYPY_VERSION_TUPLE >= (1, 4): + tvd = TypeVarType( + self_tvar_name, + tvar_fullname, + -1, + [], + obj_type, + AnyType(TypeOfAny.from_omitted_generics), # type: ignore[arg-type] + ) + self_tvar_expr = TypeVarExpr( + self_tvar_name, + tvar_fullname, + [], + obj_type, + AnyType(TypeOfAny.from_omitted_generics), # type: ignore[arg-type] + ) + else: + tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type) + self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type) + ctx.cls.info.names[self_tvar_name] = SymbolTableNode(MDEF, self_tvar_expr) + + # Backward-compatible with TypeVarDef from Mypy 0.910. + if isinstance(tvd, TypeVarType): + self_type = tvd + else: + self_type = TypeVarType(tvd) + + add_method( + ctx, + 'construct', + construct_arguments, + return_type=self_type, + self_type=self_type, + tvar_def=tvd, + is_classmethod=True, + ) + + def set_frozen(self, fields: List['PydanticModelField'], frozen: bool) -> None: + """ + Marks all fields as properties so that attempts to set them trigger mypy errors. + + This is the same approach used by the attrs and dataclasses plugins. + """ + ctx = self._ctx + info = ctx.cls.info + for field in fields: + sym_node = info.names.get(field.name) + if sym_node is not None: + var = sym_node.node + if isinstance(var, Var): + var.is_property = frozen + elif isinstance(var, PlaceholderNode) and not ctx.api.final_iteration: + # See https://github.com/pydantic/pydantic/issues/5191 to hit this branch for test coverage + ctx.api.defer() + else: # pragma: no cover + # I don't know whether it's possible to hit this branch, but I've added it for safety + try: + var_str = str(var) + except TypeError: + # This happens for PlaceholderNode; perhaps it will happen for other types in the future.. + var_str = repr(var) + detail = f'sym_node.node: {var_str} (of type {var.__class__})' + error_unexpected_behavior(detail, ctx.api, ctx.cls) + else: + var = field.to_var(info, use_alias=False) + var.info = info + var.is_property = frozen + var._fullname = get_fullname(info) + '.' + get_name(var) + info.names[get_name(var)] = SymbolTableNode(MDEF, var) + + def get_config_update(self, substmt: AssignmentStmt) -> Optional['ModelConfigData']: + """ + Determines the config update due to a single statement in the Config class definition. + + Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int) + """ + lhs = substmt.lvalues[0] + if not (isinstance(lhs, NameExpr) and lhs.name in self.tracked_config_fields): + return None + if lhs.name == 'extra': + if isinstance(substmt.rvalue, StrExpr): + forbid_extra = substmt.rvalue.value == 'forbid' + elif isinstance(substmt.rvalue, MemberExpr): + forbid_extra = substmt.rvalue.name == 'forbid' + else: + error_invalid_config_value(lhs.name, self._ctx.api, substmt) + return None + return ModelConfigData(forbid_extra=forbid_extra) + if lhs.name == 'alias_generator': + has_alias_generator = True + if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname == 'builtins.None': + has_alias_generator = False + return ModelConfigData(has_alias_generator=has_alias_generator) + if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname in ('builtins.True', 'builtins.False'): + return ModelConfigData(**{lhs.name: substmt.rvalue.fullname == 'builtins.True'}) + error_invalid_config_value(lhs.name, self._ctx.api, substmt) + return None + + @staticmethod + def get_is_required(cls: ClassDef, stmt: AssignmentStmt, lhs: NameExpr) -> bool: + """ + Returns a boolean indicating whether the field defined in `stmt` is a required field. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only, so only non-required if Optional + value_type = get_proper_type(cls.info[lhs.name].type) + return not PydanticModelTransformer.type_has_implicit_default(value_type) + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME: + # The "default value" is a call to `Field`; at this point, the field is + # only required if default is Ellipsis (i.e., `field_name: Annotation = Field(...)`) or if default_factory + # is specified. + for arg, name in zip(expr.args, expr.arg_names): + # If name is None, then this arg is the default because it is the only positional argument. + if name is None or name == 'default': + return arg.__class__ is EllipsisExpr + if name == 'default_factory': + return False + # In this case, default and default_factory are not specified, so we need to look at the annotation + value_type = get_proper_type(cls.info[lhs.name].type) + return not PydanticModelTransformer.type_has_implicit_default(value_type) + # Only required if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`) + return isinstance(expr, EllipsisExpr) + + @staticmethod + def type_has_implicit_default(type_: Optional[ProperType]) -> bool: + """ + Returns True if the passed type will be given an implicit default value. + + In pydantic v1, this is the case for Optional types and Any (with default value None). + """ + if isinstance(type_, AnyType): + # Annotated as Any + return True + if isinstance(type_, UnionType) and any( + isinstance(item, NoneType) or isinstance(item, AnyType) for item in type_.items + ): + # Annotated as Optional, or otherwise having NoneType or AnyType in the union + return True + return False + + @staticmethod + def get_alias_info(stmt: AssignmentStmt) -> Tuple[Optional[str], bool]: + """ + Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`. + + `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal. + If `has_dynamic_alias` is True, `alias` will be None. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only + return None, False + + if not ( + isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME + ): + # Assigned value is not a call to pydantic.fields.Field + return None, False + + for i, arg_name in enumerate(expr.arg_names): + if arg_name != 'alias': + continue + arg = expr.args[i] + if isinstance(arg, StrExpr): + return arg.value, False + else: + return None, True + return None, False + + def get_field_arguments( + self, fields: List['PydanticModelField'], typed: bool, force_all_optional: bool, use_alias: bool + ) -> List[Argument]: + """ + Helper function used during the construction of the `__init__` and `construct` method signatures. + + Returns a list of mypy Argument instances for use in the generated signatures. + """ + info = self._ctx.cls.info + arguments = [ + field.to_argument(info, typed=typed, force_optional=force_all_optional, use_alias=use_alias) + for field in fields + if not (use_alias and field.has_dynamic_alias) + ] + return arguments + + def should_init_forbid_extra(self, fields: List['PydanticModelField'], config: 'ModelConfigData') -> bool: + """ + Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature + + We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to, + *unless* a required dynamic alias is present (since then we can't determine a valid signature). + """ + if not config.allow_population_by_field_name: + if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)): + return False + if config.forbid_extra: + return True + return self.plugin_config.init_forbid_extra + + @staticmethod + def is_dynamic_alias_present(fields: List['PydanticModelField'], has_alias_generator: bool) -> bool: + """ + Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be + determined during static analysis. + """ + for field in fields: + if field.has_dynamic_alias: + return True + if has_alias_generator: + for field in fields: + if field.alias is None: + return True + return False + + +class PydanticModelField: + def __init__( + self, name: str, is_required: bool, alias: Optional[str], has_dynamic_alias: bool, line: int, column: int + ): + self.name = name + self.is_required = is_required + self.alias = alias + self.has_dynamic_alias = has_dynamic_alias + self.line = line + self.column = column + + def to_var(self, info: TypeInfo, use_alias: bool) -> Var: + name = self.name + if use_alias and self.alias is not None: + name = self.alias + return Var(name, info[self.name].type) + + def to_argument(self, info: TypeInfo, typed: bool, force_optional: bool, use_alias: bool) -> Argument: + if typed and info[self.name].type is not None: + type_annotation = info[self.name].type + else: + type_annotation = AnyType(TypeOfAny.explicit) + return Argument( + variable=self.to_var(info, use_alias), + type_annotation=type_annotation, + initializer=None, + kind=ARG_NAMED_OPT if force_optional or not self.is_required else ARG_NAMED, + ) + + def serialize(self) -> JsonDict: + return self.__dict__ + + @classmethod + def deserialize(cls, info: TypeInfo, data: JsonDict) -> 'PydanticModelField': + return cls(**data) + + +class ModelConfigData: + def __init__( + self, + forbid_extra: Optional[bool] = None, + allow_mutation: Optional[bool] = None, + frozen: Optional[bool] = None, + orm_mode: Optional[bool] = None, + allow_population_by_field_name: Optional[bool] = None, + has_alias_generator: Optional[bool] = None, + ): + self.forbid_extra = forbid_extra + self.allow_mutation = allow_mutation + self.frozen = frozen + self.orm_mode = orm_mode + self.allow_population_by_field_name = allow_population_by_field_name + self.has_alias_generator = has_alias_generator + + def set_values_dict(self) -> Dict[str, Any]: + return {k: v for k, v in self.__dict__.items() if v is not None} + + def update(self, config: Optional['ModelConfigData']) -> None: + if config is None: + return + for k, v in config.set_values_dict().items(): + setattr(self, k, v) + + def setdefault(self, key: str, value: Any) -> None: + if getattr(self, key) is None: + setattr(self, key, value) + + +ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_orm call', 'Pydantic') +ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic') +ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic') +ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic') +ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic') +ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic') + + +def error_from_orm(model_name: str, api: CheckerPluginInterface, context: Context) -> None: + api.fail(f'"{model_name}" does not have orm_mode=True', context, code=ERROR_ORM) + + +def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG) + + +def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS) + + +def error_unexpected_behavior( + detail: str, api: Union[CheckerPluginInterface, SemanticAnalyzerPluginInterface], context: Context +) -> None: # pragma: no cover + # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path + link = 'https://github.com/pydantic/pydantic/issues/new/choose' + full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n' + full_message += f'Please consider reporting this bug at {link} so we can try to fix it!' + api.fail(full_message, context, code=ERROR_UNEXPECTED) + + +def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED) + + +def error_default_and_default_factory_specified(api: CheckerPluginInterface, context: Context) -> None: + api.fail('Field default and default_factory cannot be specified together', context, code=ERROR_FIELD_DEFAULTS) + + +def add_method( + ctx: ClassDefContext, + name: str, + args: List[Argument], + return_type: Type, + self_type: Optional[Type] = None, + tvar_def: Optional[TypeVarDef] = None, + is_classmethod: bool = False, + is_new: bool = False, + # is_staticmethod: bool = False, +) -> None: + """ + Adds a new method to a class. + + This can be dropped if/when https://github.com/python/mypy/issues/7301 is merged + """ + info = ctx.cls.info + + # First remove any previously generated methods with the same name + # to avoid clashes and problems in the semantic analyzer. + if name in info.names: + sym = info.names[name] + if sym.plugin_generated and isinstance(sym.node, FuncDef): + ctx.cls.defs.body.remove(sym.node) # pragma: no cover + + self_type = self_type or fill_typevars(info) + if is_classmethod or is_new: + first = [Argument(Var('_cls'), TypeType.make_normalized(self_type), None, ARG_POS)] + # elif is_staticmethod: + # first = [] + else: + self_type = self_type or fill_typevars(info) + first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] + args = first + args + arg_types, arg_names, arg_kinds = [], [], [] + for arg in args: + assert arg.type_annotation, 'All arguments must be fully typed.' + arg_types.append(arg.type_annotation) + arg_names.append(get_name(arg.variable)) + arg_kinds.append(arg.kind) + + function_type = ctx.api.named_type(f'{BUILTINS_NAME}.function') + signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) + if tvar_def: + signature.variables = [tvar_def] + + func = FuncDef(name, args, Block([PassStmt()])) + func.info = info + func.type = set_callable_name(signature, func) + func.is_class = is_classmethod + # func.is_static = is_staticmethod + func._fullname = get_fullname(info) + '.' + name + func.line = info.line + + # NOTE: we would like the plugin generated node to dominate, but we still + # need to keep any existing definitions so they get semantically analyzed. + if name in info.names: + # Get a nice unique name instead. + r_name = get_unique_redefinition_name(name, info.names) + info.names[r_name] = info.names[name] + + if is_classmethod: # or is_staticmethod: + func.is_decorated = True + v = Var(name, func.type) + v.info = info + v._fullname = func._fullname + # if is_classmethod: + v.is_classmethod = True + dec = Decorator(func, [NameExpr('classmethod')], v) + # else: + # v.is_staticmethod = True + # dec = Decorator(func, [NameExpr('staticmethod')], v) + + dec.line = info.line + sym = SymbolTableNode(MDEF, dec) + else: + sym = SymbolTableNode(MDEF, func) + sym.plugin_generated = True + + info.names[name] = sym + info.defn.defs.body.append(func) + + +def get_fullname(x: Union[FuncBase, SymbolNode]) -> str: + """ + Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped. + """ + fn = x.fullname + if callable(fn): # pragma: no cover + return fn() + return fn + + +def get_name(x: Union[FuncBase, SymbolNode]) -> str: + """ + Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped. + """ + fn = x.name + if callable(fn): # pragma: no cover + return fn() + return fn + + +def parse_toml(config_file: str) -> Optional[Dict[str, Any]]: + if not config_file.endswith('.toml'): + return None + + read_mode = 'rb' + if sys.version_info >= (3, 11): + import tomllib as toml_ + else: + try: + import tomli as toml_ + except ImportError: + # older versions of mypy have toml as a dependency, not tomli + read_mode = 'r' + try: + import toml as toml_ # type: ignore[no-redef] + except ImportError: # pragma: no cover + import warnings + + warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.') + return None + + with open(config_file, read_mode) as rf: + return toml_.load(rf) # type: ignore[arg-type] diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/networks.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/networks.py new file mode 100644 index 0000000..cfebe58 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/networks.py @@ -0,0 +1,747 @@ +import re +from ipaddress import ( + IPv4Address, + IPv4Interface, + IPv4Network, + IPv6Address, + IPv6Interface, + IPv6Network, + _BaseAddress, + _BaseNetwork, +) +from typing import ( + TYPE_CHECKING, + Any, + Collection, + Dict, + Generator, + List, + Match, + Optional, + Pattern, + Set, + Tuple, + Type, + Union, + cast, + no_type_check, +) + +from . import errors +from .utils import Representation, update_not_none +from .validators import constr_length_validator, str_validator + +if TYPE_CHECKING: + import email_validator + from typing_extensions import TypedDict + + from .config import BaseConfig + from .fields import ModelField + from .typing import AnyCallable + + CallableGenerator = Generator[AnyCallable, None, None] + + class Parts(TypedDict, total=False): + scheme: str + user: Optional[str] + password: Optional[str] + ipv4: Optional[str] + ipv6: Optional[str] + domain: Optional[str] + port: Optional[str] + path: Optional[str] + query: Optional[str] + fragment: Optional[str] + + class HostParts(TypedDict, total=False): + host: str + tld: Optional[str] + host_type: Optional[str] + port: Optional[str] + rebuild: bool + +else: + email_validator = None + + class Parts(dict): + pass + + +NetworkType = Union[str, bytes, int, Tuple[Union[str, bytes, int], Union[str, int]]] + +__all__ = [ + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'HttpUrl', + 'stricturl', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'validate_email', +] + +_url_regex_cache = None +_multi_host_url_regex_cache = None +_ascii_domain_regex_cache = None +_int_domain_regex_cache = None +_host_regex_cache = None + +_host_regex = ( + r'(?:' + r'(?P(?:\d{1,3}\.){3}\d{1,3})(?=$|[/:#?])|' # ipv4 + r'(?P\[[A-F0-9]*:[A-F0-9:]+\])(?=$|[/:#?])|' # ipv6 + r'(?P[^\s/:?#]+)' # domain, validation occurs later + r')?' + r'(?::(?P\d+))?' # port +) +_scheme_regex = r'(?:(?P[a-z][a-z0-9+\-.]+)://)?' # scheme https://tools.ietf.org/html/rfc3986#appendix-A +_user_info_regex = r'(?:(?P[^\s:/]*)(?::(?P[^\s/]*))?@)?' +_path_regex = r'(?P/[^\s?#]*)?' +_query_regex = r'(?:\?(?P[^\s#]*))?' +_fragment_regex = r'(?:#(?P[^\s#]*))?' + + +def url_regex() -> Pattern[str]: + global _url_regex_cache + if _url_regex_cache is None: + _url_regex_cache = re.compile( + rf'{_scheme_regex}{_user_info_regex}{_host_regex}{_path_regex}{_query_regex}{_fragment_regex}', + re.IGNORECASE, + ) + return _url_regex_cache + + +def multi_host_url_regex() -> Pattern[str]: + """ + Compiled multi host url regex. + + Additionally to `url_regex` it allows to match multiple hosts. + E.g. host1.db.net,host2.db.net + """ + global _multi_host_url_regex_cache + if _multi_host_url_regex_cache is None: + _multi_host_url_regex_cache = re.compile( + rf'{_scheme_regex}{_user_info_regex}' + r'(?P([^/]*))' # validation occurs later + rf'{_path_regex}{_query_regex}{_fragment_regex}', + re.IGNORECASE, + ) + return _multi_host_url_regex_cache + + +def ascii_domain_regex() -> Pattern[str]: + global _ascii_domain_regex_cache + if _ascii_domain_regex_cache is None: + ascii_chunk = r'[_0-9a-z](?:[-_0-9a-z]{0,61}[_0-9a-z])?' + ascii_domain_ending = r'(?P\.[a-z]{2,63})?\.?' + _ascii_domain_regex_cache = re.compile( + fr'(?:{ascii_chunk}\.)*?{ascii_chunk}{ascii_domain_ending}', re.IGNORECASE + ) + return _ascii_domain_regex_cache + + +def int_domain_regex() -> Pattern[str]: + global _int_domain_regex_cache + if _int_domain_regex_cache is None: + int_chunk = r'[_0-9a-\U00040000](?:[-_0-9a-\U00040000]{0,61}[_0-9a-\U00040000])?' + int_domain_ending = r'(?P(\.[^\W\d_]{2,63})|(\.(?:xn--)[_0-9a-z-]{2,63}))?\.?' + _int_domain_regex_cache = re.compile(fr'(?:{int_chunk}\.)*?{int_chunk}{int_domain_ending}', re.IGNORECASE) + return _int_domain_regex_cache + + +def host_regex() -> Pattern[str]: + global _host_regex_cache + if _host_regex_cache is None: + _host_regex_cache = re.compile( + _host_regex, + re.IGNORECASE, + ) + return _host_regex_cache + + +class AnyUrl(str): + strip_whitespace = True + min_length = 1 + max_length = 2**16 + allowed_schemes: Optional[Collection[str]] = None + tld_required: bool = False + user_required: bool = False + host_required: bool = True + hidden_parts: Set[str] = set() + + __slots__ = ('scheme', 'user', 'password', 'host', 'tld', 'host_type', 'port', 'path', 'query', 'fragment') + + @no_type_check + def __new__(cls, url: Optional[str], **kwargs) -> object: + return str.__new__(cls, cls.build(**kwargs) if url is None else url) + + def __init__( + self, + url: str, + *, + scheme: str, + user: Optional[str] = None, + password: Optional[str] = None, + host: Optional[str] = None, + tld: Optional[str] = None, + host_type: str = 'domain', + port: Optional[str] = None, + path: Optional[str] = None, + query: Optional[str] = None, + fragment: Optional[str] = None, + ) -> None: + str.__init__(url) + self.scheme = scheme + self.user = user + self.password = password + self.host = host + self.tld = tld + self.host_type = host_type + self.port = port + self.path = path + self.query = query + self.fragment = fragment + + @classmethod + def build( + cls, + *, + scheme: str, + user: Optional[str] = None, + password: Optional[str] = None, + host: str, + port: Optional[str] = None, + path: Optional[str] = None, + query: Optional[str] = None, + fragment: Optional[str] = None, + **_kwargs: str, + ) -> str: + parts = Parts( + scheme=scheme, + user=user, + password=password, + host=host, + port=port, + path=path, + query=query, + fragment=fragment, + **_kwargs, # type: ignore[misc] + ) + + url = scheme + '://' + if user: + url += user + if password: + url += ':' + password + if user or password: + url += '@' + url += host + if port and ('port' not in cls.hidden_parts or cls.get_default_parts(parts).get('port') != port): + url += ':' + port + if path: + url += path + if query: + url += '?' + query + if fragment: + url += '#' + fragment + return url + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length, format='uri') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'AnyUrl': + if value.__class__ == cls: + return value + value = str_validator(value) + if cls.strip_whitespace: + value = value.strip() + url: str = cast(str, constr_length_validator(value, field, config)) + + m = cls._match_url(url) + # the regex should always match, if it doesn't please report with details of the URL tried + assert m, 'URL regex failed unexpectedly' + + original_parts = cast('Parts', m.groupdict()) + parts = cls.apply_default_parts(original_parts) + parts = cls.validate_parts(parts) + + if m.end() != len(url): + raise errors.UrlExtraError(extra=url[m.end() :]) + + return cls._build_url(m, url, parts) + + @classmethod + def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'AnyUrl': + """ + Validate hosts and build the AnyUrl object. Split from `validate` so this method + can be altered in `MultiHostDsn`. + """ + host, tld, host_type, rebuild = cls.validate_host(parts) + + return cls( + None if rebuild else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + host=host, + tld=tld, + host_type=host_type, + port=parts['port'], + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + ) + + @staticmethod + def _match_url(url: str) -> Optional[Match[str]]: + return url_regex().match(url) + + @staticmethod + def _validate_port(port: Optional[str]) -> None: + if port is not None and int(port) > 65_535: + raise errors.UrlPortError() + + @classmethod + def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts': + """ + A method used to validate parts of a URL. + Could be overridden to set default values for parts if missing + """ + scheme = parts['scheme'] + if scheme is None: + raise errors.UrlSchemeError() + + if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes: + raise errors.UrlSchemePermittedError(set(cls.allowed_schemes)) + + if validate_port: + cls._validate_port(parts['port']) + + user = parts['user'] + if cls.user_required and user is None: + raise errors.UrlUserInfoError() + + return parts + + @classmethod + def validate_host(cls, parts: 'Parts') -> Tuple[str, Optional[str], str, bool]: + tld, host_type, rebuild = None, None, False + for f in ('domain', 'ipv4', 'ipv6'): + host = parts[f] # type: ignore[literal-required] + if host: + host_type = f + break + + if host is None: + if cls.host_required: + raise errors.UrlHostError() + elif host_type == 'domain': + is_international = False + d = ascii_domain_regex().fullmatch(host) + if d is None: + d = int_domain_regex().fullmatch(host) + if d is None: + raise errors.UrlHostError() + is_international = True + + tld = d.group('tld') + if tld is None and not is_international: + d = int_domain_regex().fullmatch(host) + assert d is not None + tld = d.group('tld') + is_international = True + + if tld is not None: + tld = tld[1:] + elif cls.tld_required: + raise errors.UrlHostTldError() + + if is_international: + host_type = 'int_domain' + rebuild = True + host = host.encode('idna').decode('ascii') + if tld is not None: + tld = tld.encode('idna').decode('ascii') + + return host, tld, host_type, rebuild # type: ignore + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return {} + + @classmethod + def apply_default_parts(cls, parts: 'Parts') -> 'Parts': + for key, value in cls.get_default_parts(parts).items(): + if not parts[key]: # type: ignore[literal-required] + parts[key] = value # type: ignore[literal-required] + return parts + + def __repr__(self) -> str: + extra = ', '.join(f'{n}={getattr(self, n)!r}' for n in self.__slots__ if getattr(self, n) is not None) + return f'{self.__class__.__name__}({super().__repr__()}, {extra})' + + +class AnyHttpUrl(AnyUrl): + allowed_schemes = {'http', 'https'} + + __slots__ = () + + +class HttpUrl(AnyHttpUrl): + tld_required = True + # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + max_length = 2083 + hidden_parts = {'port'} + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return {'port': '80' if parts['scheme'] == 'http' else '443'} + + +class FileUrl(AnyUrl): + allowed_schemes = {'file'} + host_required = False + + __slots__ = () + + +class MultiHostDsn(AnyUrl): + __slots__ = AnyUrl.__slots__ + ('hosts',) + + def __init__(self, *args: Any, hosts: Optional[List['HostParts']] = None, **kwargs: Any): + super().__init__(*args, **kwargs) + self.hosts = hosts + + @staticmethod + def _match_url(url: str) -> Optional[Match[str]]: + return multi_host_url_regex().match(url) + + @classmethod + def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts': + return super().validate_parts(parts, validate_port=False) + + @classmethod + def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'MultiHostDsn': + hosts_parts: List['HostParts'] = [] + host_re = host_regex() + for host in m.groupdict()['hosts'].split(','): + d: Parts = host_re.match(host).groupdict() # type: ignore + host, tld, host_type, rebuild = cls.validate_host(d) + port = d.get('port') + cls._validate_port(port) + hosts_parts.append( + { + 'host': host, + 'host_type': host_type, + 'tld': tld, + 'rebuild': rebuild, + 'port': port, + } + ) + + if len(hosts_parts) > 1: + return cls( + None if any([hp['rebuild'] for hp in hosts_parts]) else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + host_type=None, + hosts=hosts_parts, + ) + else: + # backwards compatibility with single host + host_part = hosts_parts[0] + return cls( + None if host_part['rebuild'] else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + host=host_part['host'], + tld=host_part['tld'], + host_type=host_part['host_type'], + port=host_part.get('port'), + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + ) + + +class PostgresDsn(MultiHostDsn): + allowed_schemes = { + 'postgres', + 'postgresql', + 'postgresql+asyncpg', + 'postgresql+pg8000', + 'postgresql+psycopg', + 'postgresql+psycopg2', + 'postgresql+psycopg2cffi', + 'postgresql+py-postgresql', + 'postgresql+pygresql', + } + user_required = True + + __slots__ = () + + +class CockroachDsn(AnyUrl): + allowed_schemes = { + 'cockroachdb', + 'cockroachdb+psycopg2', + 'cockroachdb+asyncpg', + } + user_required = True + + +class AmqpDsn(AnyUrl): + allowed_schemes = {'amqp', 'amqps'} + host_required = False + + +class RedisDsn(AnyUrl): + __slots__ = () + allowed_schemes = {'redis', 'rediss'} + host_required = False + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'domain': 'localhost' if not (parts['ipv4'] or parts['ipv6']) else '', + 'port': '6379', + 'path': '/0', + } + + +class MongoDsn(AnyUrl): + allowed_schemes = {'mongodb'} + + # TODO: Needed to generic "Parts" for "Replica Set", "Sharded Cluster", and other mongodb deployment modes + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'port': '27017', + } + + +class KafkaDsn(AnyUrl): + allowed_schemes = {'kafka'} + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'domain': 'localhost', + 'port': '9092', + } + + +def stricturl( + *, + strip_whitespace: bool = True, + min_length: int = 1, + max_length: int = 2**16, + tld_required: bool = True, + host_required: bool = True, + allowed_schemes: Optional[Collection[str]] = None, +) -> Type[AnyUrl]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + min_length=min_length, + max_length=max_length, + tld_required=tld_required, + host_required=host_required, + allowed_schemes=allowed_schemes, + ) + return type('UrlValue', (AnyUrl,), namespace) + + +def import_email_validator() -> None: + global email_validator + try: + import email_validator + except ImportError as e: + raise ImportError('email-validator is not installed, run `pip install pydantic[email]`') from e + + +class EmailStr(str): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='email') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + # included here and below so the error happens straight away + import_email_validator() + + yield str_validator + yield cls.validate + + @classmethod + def validate(cls, value: Union[str]) -> str: + return validate_email(value)[1] + + +class NameEmail(Representation): + __slots__ = 'name', 'email' + + def __init__(self, name: str, email: str): + self.name = name + self.email = email + + def __eq__(self, other: Any) -> bool: + return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email) + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='name-email') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + import_email_validator() + + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> 'NameEmail': + if value.__class__ == cls: + return value + value = str_validator(value) + return cls(*validate_email(value)) + + def __str__(self) -> str: + return f'{self.name} <{self.email}>' + + +class IPvAnyAddress(_BaseAddress): + __slots__ = () + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanyaddress') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Union[str, bytes, int]) -> Union[IPv4Address, IPv6Address]: + try: + return IPv4Address(value) + except ValueError: + pass + + try: + return IPv6Address(value) + except ValueError: + raise errors.IPvAnyAddressError() + + +class IPvAnyInterface(_BaseAddress): + __slots__ = () + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanyinterface') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: NetworkType) -> Union[IPv4Interface, IPv6Interface]: + try: + return IPv4Interface(value) + except ValueError: + pass + + try: + return IPv6Interface(value) + except ValueError: + raise errors.IPvAnyInterfaceError() + + +class IPvAnyNetwork(_BaseNetwork): # type: ignore + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanynetwork') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: NetworkType) -> Union[IPv4Network, IPv6Network]: + # Assume IP Network is defined with a default value for ``strict`` argument. + # Define your own class if you want to specify network address check strictness. + try: + return IPv4Network(value) + except ValueError: + pass + + try: + return IPv6Network(value) + except ValueError: + raise errors.IPvAnyNetworkError() + + +pretty_email_regex = re.compile(r'([\w ]*?) *<(.*)> *') +MAX_EMAIL_LENGTH = 2048 +"""Maximum length for an email. +A somewhat arbitrary but very generous number compared to what is allowed by most implementations. +""" + + +def validate_email(value: Union[str]) -> Tuple[str, str]: + """ + Email address validation using https://pypi.org/project/email-validator/ + Notes: + * raw ip address (literal) domain parts are not allowed. + * "John Doe " style "pretty" email addresses are processed + * spaces are striped from the beginning and end of addresses but no error is raised + """ + if email_validator is None: + import_email_validator() + + if len(value) > MAX_EMAIL_LENGTH: + raise errors.EmailError() + + m = pretty_email_regex.fullmatch(value) + name: Union[str, None] = None + if m: + name, value = m.groups() + email = value.strip() + try: + parts = email_validator.validate_email(email, check_deliverability=False) + except email_validator.EmailNotValidError as e: + raise errors.EmailError from e + + if hasattr(parts, 'normalized'): + # email-validator >= 2 + email = parts.normalized + assert email is not None + name = name or parts.local_part + return name, email + else: + # email-validator >1, <2 + at_index = email.index('@') + local_part = email[:at_index] # RFC 5321, local part must be case-sensitive. + global_part = email[at_index:].lower() + + return name or local_part, local_part + global_part diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/parse.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/parse.py new file mode 100644 index 0000000..7ac330c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/parse.py @@ -0,0 +1,66 @@ +import json +import pickle +from enum import Enum +from pathlib import Path +from typing import Any, Callable, Union + +from .types import StrBytes + + +class Protocol(str, Enum): + json = 'json' + pickle = 'pickle' + + +def load_str_bytes( + b: StrBytes, + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, +) -> Any: + if proto is None and content_type: + if content_type.endswith(('json', 'javascript')): + pass + elif allow_pickle and content_type.endswith('pickle'): + proto = Protocol.pickle + else: + raise TypeError(f'Unknown content-type: {content_type}') + + proto = proto or Protocol.json + + if proto == Protocol.json: + if isinstance(b, bytes): + b = b.decode(encoding) + return json_loads(b) + elif proto == Protocol.pickle: + if not allow_pickle: + raise RuntimeError('Trying to decode with pickle with allow_pickle=False') + bb = b if isinstance(b, bytes) else b.encode() + return pickle.loads(bb) + else: + raise TypeError(f'Unknown protocol: {proto}') + + +def load_file( + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, +) -> Any: + path = Path(path) + b = path.read_bytes() + if content_type is None: + if path.suffix in ('.js', '.json'): + proto = Protocol.json + elif path.suffix == '.pkl': + proto = Protocol.pickle + + return load_str_bytes( + b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/py.typed b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/schema.py new file mode 100644 index 0000000..ea16a72 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/schema.py @@ -0,0 +1,1163 @@ +import re +import warnings +from collections import defaultdict +from dataclasses import is_dataclass +from datetime import date, datetime, time, timedelta +from decimal import Decimal +from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + ForwardRef, + FrozenSet, + Generic, + Iterable, + List, + Optional, + Pattern, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) +from uuid import UUID + +from typing_extensions import Annotated, Literal + +from .fields import ( + MAPPING_LIKE_SHAPES, + SHAPE_DEQUE, + SHAPE_FROZENSET, + SHAPE_GENERIC, + SHAPE_ITERABLE, + SHAPE_LIST, + SHAPE_SEQUENCE, + SHAPE_SET, + SHAPE_SINGLETON, + SHAPE_TUPLE, + SHAPE_TUPLE_ELLIPSIS, + FieldInfo, + ModelField, +) +from .json import pydantic_encoder +from .networks import AnyUrl, EmailStr +from .types import ( + ConstrainedDecimal, + ConstrainedFloat, + ConstrainedFrozenSet, + ConstrainedInt, + ConstrainedList, + ConstrainedSet, + ConstrainedStr, + SecretBytes, + SecretStr, + StrictBytes, + StrictStr, + conbytes, + condecimal, + confloat, + confrozenset, + conint, + conlist, + conset, + constr, +) +from .typing import ( + all_literal_values, + get_args, + get_origin, + get_sub_types, + is_callable_type, + is_literal_type, + is_namedtuple, + is_none_type, + is_union, +) +from .utils import ROOT_KEY, get_model, lenient_issubclass + +if TYPE_CHECKING: + from .dataclasses import Dataclass + from .main import BaseModel + +default_prefix = '#/definitions/' +default_ref_template = '#/definitions/{model}' + +TypeModelOrEnum = Union[Type['BaseModel'], Type[Enum]] +TypeModelSet = Set[TypeModelOrEnum] + + +def _apply_modify_schema( + modify_schema: Callable[..., None], field: Optional[ModelField], field_schema: Dict[str, Any] +) -> None: + from inspect import signature + + sig = signature(modify_schema) + args = set(sig.parameters.keys()) + if 'field' in args or 'kwargs' in args: + modify_schema(field_schema, field=field) + else: + modify_schema(field_schema) + + +def schema( + models: Sequence[Union[Type['BaseModel'], Type['Dataclass']]], + *, + by_alias: bool = True, + title: Optional[str] = None, + description: Optional[str] = None, + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, +) -> Dict[str, Any]: + """ + Process a list of models and generate a single JSON Schema with all of them defined in the ``definitions`` + top-level JSON key, including their sub-models. + + :param models: a list of models to include in the generated JSON Schema + :param by_alias: generate the schemas using the aliases defined, if any + :param title: title for the generated schema that includes the definitions + :param description: description for the generated schema + :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the + default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere + else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the + top-level key ``definitions``, so you can extract them from there. But all the references will have the set + prefix. + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful + for references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For + a sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :return: dict with the JSON Schema with a ``definitions`` top-level key including the schema definitions for + the models and sub-models passed in ``models``. + """ + clean_models = [get_model(model) for model in models] + flat_models = get_flat_models_from_models(clean_models) + model_name_map = get_model_name_map(flat_models) + definitions = {} + output_schema: Dict[str, Any] = {} + if title: + output_schema['title'] = title + if description: + output_schema['description'] = description + for model in clean_models: + m_schema, m_definitions, m_nested_models = model_process_schema( + model, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + ) + definitions.update(m_definitions) + model_name = model_name_map[model] + definitions[model_name] = m_schema + if definitions: + output_schema['definitions'] = definitions + return output_schema + + +def model_schema( + model: Union[Type['BaseModel'], Type['Dataclass']], + by_alias: bool = True, + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, +) -> Dict[str, Any]: + """ + Generate a JSON Schema for one model. With all the sub-models defined in the ``definitions`` top-level + JSON key. + + :param model: a Pydantic model (a class that inherits from BaseModel) + :param by_alias: generate the schemas using the aliases defined, if any + :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the + default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere + else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the + top-level key ``definitions``, so you can extract them from there. But all the references will have the set + prefix. + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for + references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a + sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :return: dict with the JSON Schema for the passed ``model`` + """ + model = get_model(model) + flat_models = get_flat_models_from_model(model) + model_name_map = get_model_name_map(flat_models) + model_name = model_name_map[model] + m_schema, m_definitions, nested_models = model_process_schema( + model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix, ref_template=ref_template + ) + if model_name in nested_models: + # model_name is in Nested models, it has circular references + m_definitions[model_name] = m_schema + m_schema = get_schema_ref(model_name, ref_prefix, ref_template, False) + if m_definitions: + m_schema.update({'definitions': m_definitions}) + return m_schema + + +def get_field_info_schema(field: ModelField, schema_overrides: bool = False) -> Tuple[Dict[str, Any], bool]: + # If no title is explicitly set, we don't set title in the schema for enums. + # The behaviour is the same as `BaseModel` reference, where the default title + # is in the definitions part of the schema. + schema_: Dict[str, Any] = {} + if field.field_info.title or not lenient_issubclass(field.type_, Enum): + schema_['title'] = field.field_info.title or field.alias.title().replace('_', ' ') + + if field.field_info.title: + schema_overrides = True + + if field.field_info.description: + schema_['description'] = field.field_info.description + schema_overrides = True + + if not field.required and field.default is not None and not is_callable_type(field.outer_type_): + schema_['default'] = encode_default(field.default) + schema_overrides = True + + return schema_, schema_overrides + + +def field_schema( + field: ModelField, + *, + by_alias: bool = True, + model_name_map: Dict[TypeModelOrEnum, str], + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, + known_models: Optional[TypeModelSet] = None, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Process a Pydantic field and return a tuple with a JSON Schema for it as the first item. + Also return a dictionary of definitions with models as keys and their schemas as values. If the passed field + is a model and has sub-models, and those sub-models don't have overrides (as ``title``, ``default``, etc), they + will be included in the definitions and referenced in the schema instead of included recursively. + + :param field: a Pydantic ``ModelField`` + :param by_alias: use the defined alias (if any) in the returned schema + :param model_name_map: used to generate the JSON Schema references to other models included in the definitions + :param ref_prefix: the JSON Pointer prefix to use for references to other schemas, if None, the default of + #/definitions/ will be used + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for + references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a + sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :param known_models: used to solve circular references + :return: tuple of the schema for this field and additional definitions + """ + s, schema_overrides = get_field_info_schema(field) + + validation_schema = get_field_schema_validations(field) + if validation_schema: + s.update(validation_schema) + schema_overrides = True + + f_schema, f_definitions, f_nested_models = field_type_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models or set(), + ) + + # $ref will only be returned when there are no schema_overrides + if '$ref' in f_schema: + return f_schema, f_definitions, f_nested_models + else: + s.update(f_schema) + return s, f_definitions, f_nested_models + + +numeric_types = (int, float, Decimal) +_str_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = ( + ('max_length', numeric_types, 'maxLength'), + ('min_length', numeric_types, 'minLength'), + ('regex', str, 'pattern'), +) + +_numeric_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = ( + ('gt', numeric_types, 'exclusiveMinimum'), + ('lt', numeric_types, 'exclusiveMaximum'), + ('ge', numeric_types, 'minimum'), + ('le', numeric_types, 'maximum'), + ('multiple_of', numeric_types, 'multipleOf'), +) + + +def get_field_schema_validations(field: ModelField) -> Dict[str, Any]: + """ + Get the JSON Schema validation keywords for a ``field`` with an annotation of + a Pydantic ``FieldInfo`` with validation arguments. + """ + f_schema: Dict[str, Any] = {} + + if lenient_issubclass(field.type_, Enum): + # schema is already updated by `enum_process_schema`; just update with field extra + if field.field_info.extra: + f_schema.update(field.field_info.extra) + return f_schema + + if lenient_issubclass(field.type_, (str, bytes)): + for attr_name, t, keyword in _str_types_attrs: + attr = getattr(field.field_info, attr_name, None) + if isinstance(attr, t): + f_schema[keyword] = attr + if lenient_issubclass(field.type_, numeric_types) and not issubclass(field.type_, bool): + for attr_name, t, keyword in _numeric_types_attrs: + attr = getattr(field.field_info, attr_name, None) + if isinstance(attr, t): + f_schema[keyword] = attr + if field.field_info is not None and field.field_info.const: + f_schema['const'] = field.default + if field.field_info.extra: + f_schema.update(field.field_info.extra) + modify_schema = getattr(field.outer_type_, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + return f_schema + + +def get_model_name_map(unique_models: TypeModelSet) -> Dict[TypeModelOrEnum, str]: + """ + Process a set of models and generate unique names for them to be used as keys in the JSON Schema + definitions. By default the names are the same as the class name. But if two models in different Python + modules have the same name (e.g. "users.Model" and "items.Model"), the generated names will be + based on the Python module path for those conflicting models to prevent name collisions. + + :param unique_models: a Python set of models + :return: dict mapping models to names + """ + name_model_map = {} + conflicting_names: Set[str] = set() + for model in unique_models: + model_name = normalize_name(model.__name__) + if model_name in conflicting_names: + model_name = get_long_model_name(model) + name_model_map[model_name] = model + elif model_name in name_model_map: + conflicting_names.add(model_name) + conflicting_model = name_model_map.pop(model_name) + name_model_map[get_long_model_name(conflicting_model)] = conflicting_model + name_model_map[get_long_model_name(model)] = model + else: + name_model_map[model_name] = model + return {v: k for k, v in name_model_map.items()} + + +def get_flat_models_from_model(model: Type['BaseModel'], known_models: Optional[TypeModelSet] = None) -> TypeModelSet: + """ + Take a single ``model`` and generate a set with itself and all the sub-models in the tree. I.e. if you pass + model ``Foo`` (subclass of Pydantic ``BaseModel``) as ``model``, and it has a field of type ``Bar`` (also + subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also subclass of ``BaseModel``), + the return value will be ``set([Foo, Bar, Baz])``. + + :param model: a Pydantic ``BaseModel`` subclass + :param known_models: used to solve circular references + :return: a set with the initial model and all its sub-models + """ + known_models = known_models or set() + flat_models: TypeModelSet = set() + flat_models.add(model) + known_models |= flat_models + fields = cast(Sequence[ModelField], model.__fields__.values()) + flat_models |= get_flat_models_from_fields(fields, known_models=known_models) + return flat_models + + +def get_flat_models_from_field(field: ModelField, known_models: TypeModelSet) -> TypeModelSet: + """ + Take a single Pydantic ``ModelField`` (from a model) that could have been declared as a subclass of BaseModel + (so, it could be a submodel), and generate a set with its model and all the sub-models in the tree. + I.e. if you pass a field that was declared to be of type ``Foo`` (subclass of BaseModel) as ``field``, and that + model ``Foo`` has a field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of + type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + + :param field: a Pydantic ``ModelField`` + :param known_models: used to solve circular references + :return: a set with the model used in the declaration for this field, if any, and all its sub-models + """ + from .main import BaseModel + + flat_models: TypeModelSet = set() + + field_type = field.type_ + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): + field_type = field_type.__pydantic_model__ + + if field.sub_fields and not lenient_issubclass(field_type, BaseModel): + flat_models |= get_flat_models_from_fields(field.sub_fields, known_models=known_models) + elif lenient_issubclass(field_type, BaseModel) and field_type not in known_models: + flat_models |= get_flat_models_from_model(field_type, known_models=known_models) + elif lenient_issubclass(field_type, Enum): + flat_models.add(field_type) + return flat_models + + +def get_flat_models_from_fields(fields: Sequence[ModelField], known_models: TypeModelSet) -> TypeModelSet: + """ + Take a list of Pydantic ``ModelField``s (from a model) that could have been declared as subclasses of ``BaseModel`` + (so, any of them could be a submodel), and generate a set with their models and all the sub-models in the tree. + I.e. if you pass a the fields of a model ``Foo`` (subclass of ``BaseModel``) as ``fields``, and on of them has a + field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also + subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + + :param fields: a list of Pydantic ``ModelField``s + :param known_models: used to solve circular references + :return: a set with any model declared in the fields, and all their sub-models + """ + flat_models: TypeModelSet = set() + for field in fields: + flat_models |= get_flat_models_from_field(field, known_models=known_models) + return flat_models + + +def get_flat_models_from_models(models: Sequence[Type['BaseModel']]) -> TypeModelSet: + """ + Take a list of ``models`` and generate a set with them and all their sub-models in their trees. I.e. if you pass + a list of two models, ``Foo`` and ``Bar``, both subclasses of Pydantic ``BaseModel`` as models, and ``Bar`` has + a field of type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + """ + flat_models: TypeModelSet = set() + for model in models: + flat_models |= get_flat_models_from_model(model) + return flat_models + + +def get_long_model_name(model: TypeModelOrEnum) -> str: + return f'{model.__module__}__{model.__qualname__}'.replace('.', '__') + + +def field_type_schema( + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Used by ``field_schema()``, you probably should be using that function. + + Take a single ``field`` and generate the schema for its type only, not including additional + information as title, etc. Also return additional schema definitions, from sub-models. + """ + from .main import BaseModel # noqa: F811 + + definitions = {} + nested_models: Set[str] = set() + f_schema: Dict[str, Any] + if field.shape in { + SHAPE_LIST, + SHAPE_TUPLE_ELLIPSIS, + SHAPE_SEQUENCE, + SHAPE_SET, + SHAPE_FROZENSET, + SHAPE_ITERABLE, + SHAPE_DEQUE, + }: + items_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + f_schema = {'type': 'array', 'items': items_schema} + if field.shape in {SHAPE_SET, SHAPE_FROZENSET}: + f_schema['uniqueItems'] = True + + elif field.shape in MAPPING_LIKE_SHAPES: + f_schema = {'type': 'object'} + key_field = cast(ModelField, field.key_field) + regex = getattr(key_field.type_, 'regex', None) + items_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + if regex: + # Dict keys have a regex pattern + # items_schema might be a schema or empty dict, add it either way + f_schema['patternProperties'] = {ConstrainedStr._get_pattern(regex): items_schema} + if items_schema: + # The dict values are not simply Any, so they need a schema + f_schema['additionalProperties'] = items_schema + elif field.shape == SHAPE_TUPLE or (field.shape == SHAPE_GENERIC and not issubclass(field.type_, BaseModel)): + sub_schema = [] + sub_fields = cast(List[ModelField], field.sub_fields) + for sf in sub_fields: + sf_schema, sf_definitions, sf_nested_models = field_type_schema( + sf, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(sf_definitions) + nested_models.update(sf_nested_models) + sub_schema.append(sf_schema) + + sub_fields_len = len(sub_fields) + if field.shape == SHAPE_GENERIC: + all_of_schemas = sub_schema[0] if sub_fields_len == 1 else {'type': 'array', 'items': sub_schema} + f_schema = {'allOf': [all_of_schemas]} + else: + f_schema = { + 'type': 'array', + 'minItems': sub_fields_len, + 'maxItems': sub_fields_len, + } + if sub_fields_len >= 1: + f_schema['items'] = sub_schema + else: + assert field.shape in {SHAPE_SINGLETON, SHAPE_GENERIC}, field.shape + f_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + + # check field type to avoid repeated calls to the same __modify_schema__ method + if field.type_ != field.outer_type_: + if field.shape == SHAPE_GENERIC: + field_type = field.type_ + else: + field_type = field.outer_type_ + modify_schema = getattr(field_type, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + return f_schema, definitions, nested_models + + +def model_process_schema( + model: TypeModelOrEnum, + *, + by_alias: bool = True, + model_name_map: Dict[TypeModelOrEnum, str], + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, + known_models: Optional[TypeModelSet] = None, + field: Optional[ModelField] = None, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Used by ``model_schema()``, you probably should be using that function. + + Take a single ``model`` and generate its schema. Also return additional schema definitions, from sub-models. The + sub-models of the returned schema will be referenced, but their definitions will not be included in the schema. All + the definitions are returned as the second value. + """ + from inspect import getdoc, signature + + known_models = known_models or set() + if lenient_issubclass(model, Enum): + model = cast(Type[Enum], model) + s = enum_process_schema(model, field=field) + return s, {}, set() + model = cast(Type['BaseModel'], model) + s = {'title': model.__config__.title or model.__name__} + doc = getdoc(model) + if doc: + s['description'] = doc + known_models.add(model) + m_schema, m_definitions, nested_models = model_type_schema( + model, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + s.update(m_schema) + schema_extra = model.__config__.schema_extra + if callable(schema_extra): + if len(signature(schema_extra).parameters) == 1: + schema_extra(s) + else: + schema_extra(s, model) + else: + s.update(schema_extra) + return s, m_definitions, nested_models + + +def model_type_schema( + model: Type['BaseModel'], + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + You probably should be using ``model_schema()``, this function is indirectly used by that function. + + Take a single ``model`` and generate the schema for its type only, not including additional + information as title, etc. Also return additional schema definitions, from sub-models. + """ + properties = {} + required = [] + definitions: Dict[str, Any] = {} + nested_models: Set[str] = set() + for k, f in model.__fields__.items(): + try: + f_schema, f_definitions, f_nested_models = field_schema( + f, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + except SkipField as skip: + warnings.warn(skip.message, UserWarning) + continue + definitions.update(f_definitions) + nested_models.update(f_nested_models) + if by_alias: + properties[f.alias] = f_schema + if f.required: + required.append(f.alias) + else: + properties[k] = f_schema + if f.required: + required.append(k) + if ROOT_KEY in properties: + out_schema = properties[ROOT_KEY] + out_schema['title'] = model.__config__.title or model.__name__ + else: + out_schema = {'type': 'object', 'properties': properties} + if required: + out_schema['required'] = required + if model.__config__.extra == 'forbid': + out_schema['additionalProperties'] = False + return out_schema, definitions, nested_models + + +def enum_process_schema(enum: Type[Enum], *, field: Optional[ModelField] = None) -> Dict[str, Any]: + """ + Take a single `enum` and generate its schema. + + This is similar to the `model_process_schema` function, but applies to ``Enum`` objects. + """ + import inspect + + schema_: Dict[str, Any] = { + 'title': enum.__name__, + # Python assigns all enums a default docstring value of 'An enumeration', so + # all enums will have a description field even if not explicitly provided. + 'description': inspect.cleandoc(enum.__doc__ or 'An enumeration.'), + # Add enum values and the enum field type to the schema. + 'enum': [item.value for item in cast(Iterable[Enum], enum)], + } + + add_field_type_to_schema(enum, schema_) + + modify_schema = getattr(enum, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, schema_) + + return schema_ + + +def field_singleton_sub_fields_schema( + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + This function is indirectly used by ``field_schema()``, you probably should be using that function. + + Take a list of Pydantic ``ModelField`` from the declaration of a type with parameters, and generate their + schema. I.e., fields used as "type parameters", like ``str`` and ``int`` in ``Tuple[str, int]``. + """ + sub_fields = cast(List[ModelField], field.sub_fields) + definitions = {} + nested_models: Set[str] = set() + if len(sub_fields) == 1: + return field_type_schema( + sub_fields[0], + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + else: + s: Dict[str, Any] = {} + # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminator-object + field_has_discriminator: bool = field.discriminator_key is not None + if field_has_discriminator: + assert field.sub_fields_mapping is not None + + discriminator_models_refs: Dict[str, Union[str, Dict[str, Any]]] = {} + + for discriminator_value, sub_field in field.sub_fields_mapping.items(): + if isinstance(discriminator_value, Enum): + discriminator_value = str(discriminator_value.value) + # sub_field is either a `BaseModel` or directly an `Annotated` `Union` of many + if is_union(get_origin(sub_field.type_)): + sub_models = get_sub_types(sub_field.type_) + discriminator_models_refs[discriminator_value] = { + model_name_map[sub_model]: get_schema_ref( + model_name_map[sub_model], ref_prefix, ref_template, False + ) + for sub_model in sub_models + } + else: + sub_field_type = sub_field.type_ + if hasattr(sub_field_type, '__pydantic_model__'): + sub_field_type = sub_field_type.__pydantic_model__ + + discriminator_model_name = model_name_map[sub_field_type] + discriminator_model_ref = get_schema_ref(discriminator_model_name, ref_prefix, ref_template, False) + discriminator_models_refs[discriminator_value] = discriminator_model_ref['$ref'] + + s['discriminator'] = { + 'propertyName': field.discriminator_alias, + 'mapping': discriminator_models_refs, + } + + sub_field_schemas = [] + for sf in sub_fields: + sub_schema, sub_definitions, sub_nested_models = field_type_schema( + sf, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(sub_definitions) + if schema_overrides and 'allOf' in sub_schema: + # if the sub_field is a referenced schema we only need the referenced + # object. Otherwise we will end up with several allOf inside anyOf/oneOf. + # See https://github.com/pydantic/pydantic/issues/1209 + sub_schema = sub_schema['allOf'][0] + + if sub_schema.keys() == {'discriminator', 'oneOf'}: + # we don't want discriminator information inside oneOf choices, this is dealt with elsewhere + sub_schema.pop('discriminator') + sub_field_schemas.append(sub_schema) + nested_models.update(sub_nested_models) + s['oneOf' if field_has_discriminator else 'anyOf'] = sub_field_schemas + return s, definitions, nested_models + + +# Order is important, e.g. subclasses of str must go before str +# this is used only for standard library types, custom types should use __modify_schema__ instead +field_class_to_schema: Tuple[Tuple[Any, Dict[str, Any]], ...] = ( + (Path, {'type': 'string', 'format': 'path'}), + (datetime, {'type': 'string', 'format': 'date-time'}), + (date, {'type': 'string', 'format': 'date'}), + (time, {'type': 'string', 'format': 'time'}), + (timedelta, {'type': 'number', 'format': 'time-delta'}), + (IPv4Network, {'type': 'string', 'format': 'ipv4network'}), + (IPv6Network, {'type': 'string', 'format': 'ipv6network'}), + (IPv4Interface, {'type': 'string', 'format': 'ipv4interface'}), + (IPv6Interface, {'type': 'string', 'format': 'ipv6interface'}), + (IPv4Address, {'type': 'string', 'format': 'ipv4'}), + (IPv6Address, {'type': 'string', 'format': 'ipv6'}), + (Pattern, {'type': 'string', 'format': 'regex'}), + (str, {'type': 'string'}), + (bytes, {'type': 'string', 'format': 'binary'}), + (bool, {'type': 'boolean'}), + (int, {'type': 'integer'}), + (float, {'type': 'number'}), + (Decimal, {'type': 'number'}), + (UUID, {'type': 'string', 'format': 'uuid'}), + (dict, {'type': 'object'}), + (list, {'type': 'array', 'items': {}}), + (tuple, {'type': 'array', 'items': {}}), + (set, {'type': 'array', 'items': {}, 'uniqueItems': True}), + (frozenset, {'type': 'array', 'items': {}, 'uniqueItems': True}), +) + +json_scheme = {'type': 'string', 'format': 'json-string'} + + +def add_field_type_to_schema(field_type: Any, schema_: Dict[str, Any]) -> None: + """ + Update the given `schema` with the type-specific metadata for the given `field_type`. + + This function looks through `field_class_to_schema` for a class that matches the given `field_type`, + and then modifies the given `schema` with the information from that type. + """ + for type_, t_schema in field_class_to_schema: + # Fallback for `typing.Pattern` and `re.Pattern` as they are not a valid class + if lenient_issubclass(field_type, type_) or field_type is type_ is Pattern: + schema_.update(t_schema) + break + + +def get_schema_ref(name: str, ref_prefix: Optional[str], ref_template: str, schema_overrides: bool) -> Dict[str, Any]: + if ref_prefix: + schema_ref = {'$ref': ref_prefix + name} + else: + schema_ref = {'$ref': ref_template.format(model=name)} + return {'allOf': [schema_ref]} if schema_overrides else schema_ref + + +def field_singleton_schema( # noqa: C901 (ignore complexity) + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + This function is indirectly used by ``field_schema()``, you should probably be using that function. + + Take a single Pydantic ``ModelField``, and return its schema and any additional definitions from sub-models. + """ + from .main import BaseModel + + definitions: Dict[str, Any] = {} + nested_models: Set[str] = set() + field_type = field.type_ + + # Recurse into this field if it contains sub_fields and is NOT a + # BaseModel OR that BaseModel is a const + if field.sub_fields and ( + (field.field_info and field.field_info.const) or not lenient_issubclass(field_type, BaseModel) + ): + return field_singleton_sub_fields_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + if field_type is Any or field_type is object or field_type.__class__ == TypeVar or get_origin(field_type) is type: + return {}, definitions, nested_models # no restrictions + if is_none_type(field_type): + return {'type': 'null'}, definitions, nested_models + if is_callable_type(field_type): + raise SkipField(f'Callable {field.name} was excluded from schema since JSON schema has no equivalent type.') + f_schema: Dict[str, Any] = {} + if field.field_info is not None and field.field_info.const: + f_schema['const'] = field.default + + if is_literal_type(field_type): + values = tuple(x.value if isinstance(x, Enum) else x for x in all_literal_values(field_type)) + + if len({v.__class__ for v in values}) > 1: + return field_schema( + multitypes_literal_field_for_schema(values, field), + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + + # All values have the same type + field_type = values[0].__class__ + f_schema['enum'] = list(values) + add_field_type_to_schema(field_type, f_schema) + elif lenient_issubclass(field_type, Enum): + enum_name = model_name_map[field_type] + f_schema, schema_overrides = get_field_info_schema(field, schema_overrides) + f_schema.update(get_schema_ref(enum_name, ref_prefix, ref_template, schema_overrides)) + definitions[enum_name] = enum_process_schema(field_type, field=field) + elif is_namedtuple(field_type): + sub_schema, *_ = model_process_schema( + field_type.__pydantic_model__, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + field=field, + ) + items_schemas = list(sub_schema['properties'].values()) + f_schema.update( + { + 'type': 'array', + 'items': items_schemas, + 'minItems': len(items_schemas), + 'maxItems': len(items_schemas), + } + ) + elif not hasattr(field_type, '__pydantic_model__'): + add_field_type_to_schema(field_type, f_schema) + + modify_schema = getattr(field_type, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + + if f_schema: + return f_schema, definitions, nested_models + + # Handle dataclass-based models + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): + field_type = field_type.__pydantic_model__ + + if issubclass(field_type, BaseModel): + model_name = model_name_map[field_type] + if field_type not in known_models: + sub_schema, sub_definitions, sub_nested_models = model_process_schema( + field_type, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + field=field, + ) + definitions.update(sub_definitions) + definitions[model_name] = sub_schema + nested_models.update(sub_nested_models) + else: + nested_models.add(model_name) + schema_ref = get_schema_ref(model_name, ref_prefix, ref_template, schema_overrides) + return schema_ref, definitions, nested_models + + # For generics with no args + args = get_args(field_type) + if args is not None and not args and Generic in field_type.__bases__: + return f_schema, definitions, nested_models + + raise ValueError(f'Value not declarable with JSON Schema, field: {field}') + + +def multitypes_literal_field_for_schema(values: Tuple[Any, ...], field: ModelField) -> ModelField: + """ + To support `Literal` with values of different types, we split it into multiple `Literal` with same type + e.g. `Literal['qwe', 'asd', 1, 2]` becomes `Union[Literal['qwe', 'asd'], Literal[1, 2]]` + """ + literal_distinct_types = defaultdict(list) + for v in values: + literal_distinct_types[v.__class__].append(v) + distinct_literals = (Literal[tuple(same_type_values)] for same_type_values in literal_distinct_types.values()) + + return ModelField( + name=field.name, + type_=Union[tuple(distinct_literals)], # type: ignore + class_validators=field.class_validators, + model_config=field.model_config, + default=field.default, + required=field.required, + alias=field.alias, + field_info=field.field_info, + ) + + +def encode_default(dft: Any) -> Any: + from .main import BaseModel + + if isinstance(dft, BaseModel) or is_dataclass(dft): + dft = cast('dict[str, Any]', pydantic_encoder(dft)) + + if isinstance(dft, dict): + return {encode_default(k): encode_default(v) for k, v in dft.items()} + elif isinstance(dft, Enum): + return dft.value + elif isinstance(dft, (int, float, str)): + return dft + elif isinstance(dft, (list, tuple)): + t = dft.__class__ + seq_args = (encode_default(v) for v in dft) + return t(*seq_args) if is_namedtuple(t) else t(seq_args) + elif dft is None: + return None + else: + return pydantic_encoder(dft) + + +_map_types_constraint: Dict[Any, Callable[..., type]] = {int: conint, float: confloat, Decimal: condecimal} + + +def get_annotation_from_field_info( + annotation: Any, field_info: FieldInfo, field_name: str, validate_assignment: bool = False +) -> Type[Any]: + """ + Get an annotation with validation implemented for numbers and strings based on the field_info. + :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr`` + :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema + :param field_name: name of the field for use in error messages + :param validate_assignment: default False, flag for BaseModel Config value of validate_assignment + :return: the same ``annotation`` if unmodified or a new annotation with validation in place + """ + constraints = field_info.get_constraints() + used_constraints: Set[str] = set() + if constraints: + annotation, used_constraints = get_annotation_with_constraints(annotation, field_info) + if validate_assignment: + used_constraints.add('allow_mutation') + + unused_constraints = constraints - used_constraints + if unused_constraints: + raise ValueError( + f'On field "{field_name}" the following field constraints are set but not enforced: ' + f'{", ".join(unused_constraints)}. ' + f'\nFor more details see https://docs.pydantic.dev/usage/schema/#unenforced-field-constraints' + ) + + return annotation + + +def get_annotation_with_constraints(annotation: Any, field_info: FieldInfo) -> Tuple[Type[Any], Set[str]]: # noqa: C901 + """ + Get an annotation with used constraints implemented for numbers and strings based on the field_info. + + :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr`` + :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema + :return: the same ``annotation`` if unmodified or a new annotation along with the used constraints. + """ + used_constraints: Set[str] = set() + + def go(type_: Any) -> Type[Any]: + if ( + is_literal_type(type_) + or isinstance(type_, ForwardRef) + or lenient_issubclass(type_, (ConstrainedList, ConstrainedSet, ConstrainedFrozenSet)) + ): + return type_ + origin = get_origin(type_) + if origin is not None: + args: Tuple[Any, ...] = get_args(type_) + if any(isinstance(a, ForwardRef) for a in args): + # forward refs cause infinite recursion below + return type_ + + if origin is Annotated: + return go(args[0]) + if is_union(origin): + return Union[tuple(go(a) for a in args)] # type: ignore + + if issubclass(origin, List) and ( + field_info.min_items is not None + or field_info.max_items is not None + or field_info.unique_items is not None + ): + used_constraints.update({'min_items', 'max_items', 'unique_items'}) + return conlist( + go(args[0]), + min_items=field_info.min_items, + max_items=field_info.max_items, + unique_items=field_info.unique_items, + ) + + if issubclass(origin, Set) and (field_info.min_items is not None or field_info.max_items is not None): + used_constraints.update({'min_items', 'max_items'}) + return conset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items) + + if issubclass(origin, FrozenSet) and (field_info.min_items is not None or field_info.max_items is not None): + used_constraints.update({'min_items', 'max_items'}) + return confrozenset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items) + + for t in (Tuple, List, Set, FrozenSet, Sequence): + if issubclass(origin, t): # type: ignore + return t[tuple(go(a) for a in args)] # type: ignore + + if issubclass(origin, Dict): + return Dict[args[0], go(args[1])] # type: ignore + + attrs: Optional[Tuple[str, ...]] = None + constraint_func: Optional[Callable[..., type]] = None + if isinstance(type_, type): + if issubclass(type_, (SecretStr, SecretBytes)): + attrs = ('max_length', 'min_length') + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + elif issubclass(type_, str) and not issubclass(type_, (EmailStr, AnyUrl)): + attrs = ('max_length', 'min_length', 'regex') + if issubclass(type_, StrictStr): + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + else: + constraint_func = constr + elif issubclass(type_, bytes): + attrs = ('max_length', 'min_length', 'regex') + if issubclass(type_, StrictBytes): + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + else: + constraint_func = conbytes + elif issubclass(type_, numeric_types) and not issubclass( + type_, + ( + ConstrainedInt, + ConstrainedFloat, + ConstrainedDecimal, + ConstrainedList, + ConstrainedSet, + ConstrainedFrozenSet, + bool, + ), + ): + # Is numeric type + attrs = ('gt', 'lt', 'ge', 'le', 'multiple_of') + if issubclass(type_, float): + attrs += ('allow_inf_nan',) + if issubclass(type_, Decimal): + attrs += ('max_digits', 'decimal_places') + numeric_type = next(t for t in numeric_types if issubclass(type_, t)) # pragma: no branch + constraint_func = _map_types_constraint[numeric_type] + + if attrs: + used_constraints.update(set(attrs)) + kwargs = { + attr_name: attr + for attr_name, attr in ((attr_name, getattr(field_info, attr_name)) for attr_name in attrs) + if attr is not None + } + if kwargs: + constraint_func = cast(Callable[..., type], constraint_func) + return constraint_func(**kwargs) + return type_ + + return go(annotation), used_constraints + + +def normalize_name(name: str) -> str: + """ + Normalizes the given name. This can be applied to either a model *or* enum. + """ + return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name) + + +class SkipField(Exception): + """ + Utility exception used to exclude fields from schema. + """ + + def __init__(self, message: str) -> None: + self.message = message diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/tools.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/tools.py new file mode 100644 index 0000000..45be277 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/tools.py @@ -0,0 +1,92 @@ +import json +from functools import lru_cache +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable, Optional, Type, TypeVar, Union + +from .parse import Protocol, load_file, load_str_bytes +from .types import StrBytes +from .typing import display_as_type + +__all__ = ('parse_file_as', 'parse_obj_as', 'parse_raw_as', 'schema_of', 'schema_json_of') + +NameFactory = Union[str, Callable[[Type[Any]], str]] + +if TYPE_CHECKING: + from .typing import DictStrAny + + +def _generate_parsing_type_name(type_: Any) -> str: + return f'ParsingModel[{display_as_type(type_)}]' + + +@lru_cache(maxsize=2048) +def _get_parsing_type(type_: Any, *, type_name: Optional[NameFactory] = None) -> Any: + from .main import create_model + + if type_name is None: + type_name = _generate_parsing_type_name + if not isinstance(type_name, str): + type_name = type_name(type_) + return create_model(type_name, __root__=(type_, ...)) + + +T = TypeVar('T') + + +def parse_obj_as(type_: Type[T], obj: Any, *, type_name: Optional[NameFactory] = None) -> T: + model_type = _get_parsing_type(type_, type_name=type_name) # type: ignore[arg-type] + return model_type(__root__=obj).__root__ + + +def parse_file_as( + type_: Type[T], + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, + type_name: Optional[NameFactory] = None, +) -> T: + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=json_loads, + ) + return parse_obj_as(type_, obj, type_name=type_name) + + +def parse_raw_as( + type_: Type[T], + b: StrBytes, + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, + type_name: Optional[NameFactory] = None, +) -> T: + obj = load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=json_loads, + ) + return parse_obj_as(type_, obj, type_name=type_name) + + +def schema_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_kwargs: Any) -> 'DictStrAny': + """Generate a JSON schema (as dict) for the passed model or dynamically generated one""" + return _get_parsing_type(type_, type_name=title).schema(**schema_kwargs) + + +def schema_json_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_json_kwargs: Any) -> str: + """Generate a JSON schema (as JSON) for the passed model or dynamically generated one""" + return _get_parsing_type(type_, type_name=title).schema_json(**schema_json_kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/types.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/types.py new file mode 100644 index 0000000..754e58f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/types.py @@ -0,0 +1,1205 @@ +import abc +import math +import re +import warnings +from datetime import date +from decimal import Decimal, InvalidOperation +from enum import Enum +from pathlib import Path +from types import new_class +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + FrozenSet, + List, + Optional, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) +from uuid import UUID +from weakref import WeakSet + +from . import errors +from .datetime_parse import parse_date +from .utils import import_string, update_not_none +from .validators import ( + bytes_validator, + constr_length_validator, + constr_lower, + constr_strip_whitespace, + constr_upper, + decimal_validator, + float_finite_validator, + float_validator, + frozenset_validator, + int_validator, + list_validator, + number_multiple_validator, + number_size_validator, + path_exists_validator, + path_validator, + set_validator, + str_validator, + strict_bytes_validator, + strict_float_validator, + strict_int_validator, + strict_str_validator, +) + +__all__ = [ + 'NoneStr', + 'NoneBytes', + 'StrBytes', + 'NoneStrBytes', + 'StrictStr', + 'ConstrainedBytes', + 'conbytes', + 'ConstrainedList', + 'conlist', + 'ConstrainedSet', + 'conset', + 'ConstrainedFrozenSet', + 'confrozenset', + 'ConstrainedStr', + 'constr', + 'PyObject', + 'ConstrainedInt', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'ConstrainedFloat', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'ConstrainedDecimal', + 'condecimal', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'Json', + 'JsonWrapper', + 'SecretField', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'ConstrainedDate', + 'condate', +] + +NoneStr = Optional[str] +NoneBytes = Optional[bytes] +StrBytes = Union[str, bytes] +NoneStrBytes = Optional[StrBytes] +OptionalInt = Optional[int] +OptionalIntFloat = Union[OptionalInt, float] +OptionalIntFloatDecimal = Union[OptionalIntFloat, Decimal] +OptionalDate = Optional[date] +StrIntFloat = Union[str, int, float] + +if TYPE_CHECKING: + from typing_extensions import Annotated + + from .dataclasses import Dataclass + from .main import BaseModel + from .typing import CallableGenerator + + ModelOrDc = Type[Union[BaseModel, Dataclass]] + +T = TypeVar('T') +_DEFINED_TYPES: 'WeakSet[type]' = WeakSet() + + +@overload +def _registered(typ: Type[T]) -> Type[T]: + pass + + +@overload +def _registered(typ: 'ConstrainedNumberMeta') -> 'ConstrainedNumberMeta': + pass + + +def _registered(typ: Union[Type[T], 'ConstrainedNumberMeta']) -> Union[Type[T], 'ConstrainedNumberMeta']: + # In order to generate valid examples of constrained types, Hypothesis needs + # to inspect the type object - so we keep a weakref to each contype object + # until it can be registered. When (or if) our Hypothesis plugin is loaded, + # it monkeypatches this function. + # If Hypothesis is never used, the total effect is to keep a weak reference + # which has minimal memory usage and doesn't even affect garbage collection. + _DEFINED_TYPES.add(typ) + return typ + + +class ConstrainedNumberMeta(type): + def __new__(cls, name: str, bases: Any, dct: Dict[str, Any]) -> 'ConstrainedInt': # type: ignore + new_cls = cast('ConstrainedInt', type.__new__(cls, name, bases, dct)) + + if new_cls.gt is not None and new_cls.ge is not None: + raise errors.ConfigError('bounds gt and ge cannot be specified at the same time') + if new_cls.lt is not None and new_cls.le is not None: + raise errors.ConfigError('bounds lt and le cannot be specified at the same time') + + return _registered(new_cls) # type: ignore + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + StrictBool = bool +else: + + class StrictBool(int): + """ + StrictBool to allow for bools which are not type-coerced. + """ + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='boolean') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> bool: + """ + Ensure that we only allow bools. + """ + if isinstance(value, bool): + return value + + raise errors.StrictBoolError() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedInt(int, metaclass=ConstrainedNumberMeta): + strict: bool = False + gt: OptionalInt = None + ge: OptionalInt = None + lt: OptionalInt = None + le: OptionalInt = None + multiple_of: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_int_validator if cls.strict else int_validator + yield number_size_validator + yield number_multiple_validator + + +def conint( + *, + strict: bool = False, + gt: Optional[int] = None, + ge: Optional[int] = None, + lt: Optional[int] = None, + le: Optional[int] = None, + multiple_of: Optional[int] = None, +) -> Type[int]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of) + return type('ConstrainedIntValue', (ConstrainedInt,), namespace) + + +if TYPE_CHECKING: + PositiveInt = int + NegativeInt = int + NonPositiveInt = int + NonNegativeInt = int + StrictInt = int +else: + + class PositiveInt(ConstrainedInt): + gt = 0 + + class NegativeInt(ConstrainedInt): + lt = 0 + + class NonPositiveInt(ConstrainedInt): + le = 0 + + class NonNegativeInt(ConstrainedInt): + ge = 0 + + class StrictInt(ConstrainedInt): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedFloat(float, metaclass=ConstrainedNumberMeta): + strict: bool = False + gt: OptionalIntFloat = None + ge: OptionalIntFloat = None + lt: OptionalIntFloat = None + le: OptionalIntFloat = None + multiple_of: OptionalIntFloat = None + allow_inf_nan: Optional[bool] = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + # Modify constraints to account for differences between IEEE floats and JSON + if field_schema.get('exclusiveMinimum') == -math.inf: + del field_schema['exclusiveMinimum'] + if field_schema.get('minimum') == -math.inf: + del field_schema['minimum'] + if field_schema.get('exclusiveMaximum') == math.inf: + del field_schema['exclusiveMaximum'] + if field_schema.get('maximum') == math.inf: + del field_schema['maximum'] + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_float_validator if cls.strict else float_validator + yield number_size_validator + yield number_multiple_validator + yield float_finite_validator + + +def confloat( + *, + strict: bool = False, + gt: float = None, + ge: float = None, + lt: float = None, + le: float = None, + multiple_of: float = None, + allow_inf_nan: Optional[bool] = None, +) -> Type[float]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of, allow_inf_nan=allow_inf_nan) + return type('ConstrainedFloatValue', (ConstrainedFloat,), namespace) + + +if TYPE_CHECKING: + PositiveFloat = float + NegativeFloat = float + NonPositiveFloat = float + NonNegativeFloat = float + StrictFloat = float + FiniteFloat = float +else: + + class PositiveFloat(ConstrainedFloat): + gt = 0 + + class NegativeFloat(ConstrainedFloat): + lt = 0 + + class NonPositiveFloat(ConstrainedFloat): + le = 0 + + class NonNegativeFloat(ConstrainedFloat): + ge = 0 + + class StrictFloat(ConstrainedFloat): + strict = True + + class FiniteFloat(ConstrainedFloat): + allow_inf_nan = False + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedBytes(bytes): + strip_whitespace = False + to_upper = False + to_lower = False + min_length: OptionalInt = None + max_length: OptionalInt = None + strict: bool = False + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_bytes_validator if cls.strict else bytes_validator + yield constr_strip_whitespace + yield constr_upper + yield constr_lower + yield constr_length_validator + + +def conbytes( + *, + strip_whitespace: bool = False, + to_upper: bool = False, + to_lower: bool = False, + min_length: Optional[int] = None, + max_length: Optional[int] = None, + strict: bool = False, +) -> Type[bytes]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + min_length=min_length, + max_length=max_length, + strict=strict, + ) + return _registered(type('ConstrainedBytesValue', (ConstrainedBytes,), namespace)) + + +if TYPE_CHECKING: + StrictBytes = bytes +else: + + class StrictBytes(ConstrainedBytes): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedStr(str): + strip_whitespace = False + to_upper = False + to_lower = False + min_length: OptionalInt = None + max_length: OptionalInt = None + curtail_length: OptionalInt = None + regex: Optional[Union[str, Pattern[str]]] = None + strict = False + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + minLength=cls.min_length, + maxLength=cls.max_length, + pattern=cls.regex and cls._get_pattern(cls.regex), + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_str_validator if cls.strict else str_validator + yield constr_strip_whitespace + yield constr_upper + yield constr_lower + yield constr_length_validator + yield cls.validate + + @classmethod + def validate(cls, value: Union[str]) -> Union[str]: + if cls.curtail_length and len(value) > cls.curtail_length: + value = value[: cls.curtail_length] + + if cls.regex: + if not re.match(cls.regex, value): + raise errors.StrRegexError(pattern=cls._get_pattern(cls.regex)) + + return value + + @staticmethod + def _get_pattern(regex: Union[str, Pattern[str]]) -> str: + return regex if isinstance(regex, str) else regex.pattern + + +def constr( + *, + strip_whitespace: bool = False, + to_upper: bool = False, + to_lower: bool = False, + strict: bool = False, + min_length: Optional[int] = None, + max_length: Optional[int] = None, + curtail_length: Optional[int] = None, + regex: Optional[str] = None, +) -> Type[str]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + strict=strict, + min_length=min_length, + max_length=max_length, + curtail_length=curtail_length, + regex=regex and re.compile(regex), + ) + return _registered(type('ConstrainedStrValue', (ConstrainedStr,), namespace)) + + +if TYPE_CHECKING: + StrictStr = str +else: + + class StrictStr(ConstrainedStr): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# This types superclass should be Set[T], but cython chokes on that... +class ConstrainedSet(set): # type: ignore + # Needed for pydantic to detect that this is a set + __origin__ = set + __args__: Set[Type[T]] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.set_length_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items) + + @classmethod + def set_length_validator(cls, v: 'Optional[Set[T]]') -> 'Optional[Set[T]]': + if v is None: + return None + + v = set_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.SetMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.SetMaxLengthError(limit_value=cls.max_items) + + return v + + +def conset(item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None) -> Type[Set[T]]: + # __args__ is needed to conform to typing generics api + namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]} + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedSetValue', (ConstrainedSet,), {}, lambda ns: ns.update(namespace)) + + +# This types superclass should be FrozenSet[T], but cython chokes on that... +class ConstrainedFrozenSet(frozenset): # type: ignore + # Needed for pydantic to detect that this is a set + __origin__ = frozenset + __args__: FrozenSet[Type[T]] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.frozenset_length_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items) + + @classmethod + def frozenset_length_validator(cls, v: 'Optional[FrozenSet[T]]') -> 'Optional[FrozenSet[T]]': + if v is None: + return None + + v = frozenset_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.FrozenSetMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.FrozenSetMaxLengthError(limit_value=cls.max_items) + + return v + + +def confrozenset( + item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None +) -> Type[FrozenSet[T]]: + # __args__ is needed to conform to typing generics api + namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]} + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedFrozenSetValue', (ConstrainedFrozenSet,), {}, lambda ns: ns.update(namespace)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LIST TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# This types superclass should be List[T], but cython chokes on that... +class ConstrainedList(list): # type: ignore + # Needed for pydantic to detect that this is a list + __origin__ = list + __args__: Tuple[Type[T], ...] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + unique_items: Optional[bool] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.list_length_validator + if cls.unique_items: + yield cls.unique_items_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items, uniqueItems=cls.unique_items) + + @classmethod + def list_length_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]': + if v is None: + return None + + v = list_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.ListMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.ListMaxLengthError(limit_value=cls.max_items) + + return v + + @classmethod + def unique_items_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]': + if v is None: + return None + + for i, value in enumerate(v, start=1): + if value in v[i:]: + raise errors.ListUniqueItemsError() + + return v + + +def conlist( + item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None, unique_items: bool = None +) -> Type[List[T]]: + # __args__ is needed to conform to typing generics api + namespace = dict( + min_items=min_items, max_items=max_items, unique_items=unique_items, item_type=item_type, __args__=(item_type,) + ) + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedListValue', (ConstrainedList,), {}, lambda ns: ns.update(namespace)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PYOBJECT TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +if TYPE_CHECKING: + PyObject = Callable[..., Any] +else: + + class PyObject: + validate_always = True + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> Any: + if isinstance(value, Callable): + return value + + try: + value = str_validator(value) + except errors.StrError: + raise errors.PyObjectError(error_message='value is neither a valid import path not a valid callable') + + try: + return import_string(value) + except ImportError as e: + raise errors.PyObjectError(error_message=str(e)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedDecimal(Decimal, metaclass=ConstrainedNumberMeta): + gt: OptionalIntFloatDecimal = None + ge: OptionalIntFloatDecimal = None + lt: OptionalIntFloatDecimal = None + le: OptionalIntFloatDecimal = None + max_digits: OptionalInt = None + decimal_places: OptionalInt = None + multiple_of: OptionalIntFloatDecimal = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield decimal_validator + yield number_size_validator + yield number_multiple_validator + yield cls.validate + + @classmethod + def validate(cls, value: Decimal) -> Decimal: + try: + normalized_value = value.normalize() + except InvalidOperation: + normalized_value = value + digit_tuple, exponent = normalized_value.as_tuple()[1:] + if exponent in {'F', 'n', 'N'}: + raise errors.DecimalIsNotFiniteError() + + if exponent >= 0: + # A positive exponent adds that many trailing zeros. + digits = len(digit_tuple) + exponent + decimals = 0 + else: + # If the absolute value of the negative exponent is larger than the + # number of digits, then it's the same as the number of digits, + # because it'll consume all of the digits in digit_tuple and then + # add abs(exponent) - len(digit_tuple) leading zeros after the + # decimal point. + if abs(exponent) > len(digit_tuple): + digits = decimals = abs(exponent) + else: + digits = len(digit_tuple) + decimals = abs(exponent) + whole_digits = digits - decimals + + if cls.max_digits is not None and digits > cls.max_digits: + raise errors.DecimalMaxDigitsError(max_digits=cls.max_digits) + + if cls.decimal_places is not None and decimals > cls.decimal_places: + raise errors.DecimalMaxPlacesError(decimal_places=cls.decimal_places) + + if cls.max_digits is not None and cls.decimal_places is not None: + expected = cls.max_digits - cls.decimal_places + if whole_digits > expected: + raise errors.DecimalWholeDigitsError(whole_digits=expected) + + return value + + +def condecimal( + *, + gt: Decimal = None, + ge: Decimal = None, + lt: Decimal = None, + le: Decimal = None, + max_digits: Optional[int] = None, + decimal_places: Optional[int] = None, + multiple_of: Decimal = None, +) -> Type[Decimal]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + gt=gt, ge=ge, lt=lt, le=le, max_digits=max_digits, decimal_places=decimal_places, multiple_of=multiple_of + ) + return type('ConstrainedDecimalValue', (ConstrainedDecimal,), namespace) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + UUID1 = UUID + UUID3 = UUID + UUID4 = UUID + UUID5 = UUID +else: + + class UUID1(UUID): + _required_version = 1 + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format=f'uuid{cls._required_version}') + + class UUID3(UUID1): + _required_version = 3 + + class UUID4(UUID1): + _required_version = 4 + + class UUID5(UUID1): + _required_version = 5 + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + FilePath = Path + DirectoryPath = Path +else: + + class FilePath(Path): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(format='file-path') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield path_validator + yield path_exists_validator + yield cls.validate + + @classmethod + def validate(cls, value: Path) -> Path: + if not value.is_file(): + raise errors.PathNotAFileError(path=value) + + return value + + class DirectoryPath(Path): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(format='directory-path') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield path_validator + yield path_exists_validator + yield cls.validate + + @classmethod + def validate(cls, value: Path) -> Path: + if not value.is_dir(): + raise errors.PathNotADirectoryError(path=value) + + return value + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class JsonWrapper: + pass + + +class JsonMeta(type): + def __getitem__(self, t: Type[Any]) -> Type[JsonWrapper]: + if t is Any: + return Json # allow Json[Any] to replecate plain Json + return _registered(type('JsonWrapperValue', (JsonWrapper,), {'inner_type': t})) + + +if TYPE_CHECKING: + Json = Annotated[T, ...] # Json[list[str]] will be recognized by type checkers as list[str] + +else: + + class Json(metaclass=JsonMeta): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='json-string') + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class SecretField(abc.ABC): + """ + Note: this should be implemented as a generic like `SecretField(ABC, Generic[T])`, + the `__init__()` should be part of the abstract class and the + `get_secret_value()` method should use the generic `T` type. + + However Cython doesn't support very well generics at the moment and + the generated code fails to be imported (see + https://github.com/cython/cython/issues/2753). + """ + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() + + def __str__(self) -> str: + return '**********' if self.get_secret_value() else '' + + def __hash__(self) -> int: + return hash(self.get_secret_value()) + + @abc.abstractmethod + def get_secret_value(self) -> Any: # pragma: no cover + ... + + +class SecretStr(SecretField): + min_length: OptionalInt = None + max_length: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + type='string', + writeOnly=True, + format='password', + minLength=cls.min_length, + maxLength=cls.max_length, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + yield constr_length_validator + + @classmethod + def validate(cls, value: Any) -> 'SecretStr': + if isinstance(value, cls): + return value + value = str_validator(value) + return cls(value) + + def __init__(self, value: str): + self._secret_value = value + + def __repr__(self) -> str: + return f"SecretStr('{self}')" + + def __len__(self) -> int: + return len(self._secret_value) + + def display(self) -> str: + warnings.warn('`secret_str.display()` is deprecated, use `str(secret_str)` instead', DeprecationWarning) + return str(self) + + def get_secret_value(self) -> str: + return self._secret_value + + +class SecretBytes(SecretField): + min_length: OptionalInt = None + max_length: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + type='string', + writeOnly=True, + format='password', + minLength=cls.min_length, + maxLength=cls.max_length, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + yield constr_length_validator + + @classmethod + def validate(cls, value: Any) -> 'SecretBytes': + if isinstance(value, cls): + return value + value = bytes_validator(value) + return cls(value) + + def __init__(self, value: bytes): + self._secret_value = value + + def __repr__(self) -> str: + return f"SecretBytes(b'{self}')" + + def __len__(self) -> int: + return len(self._secret_value) + + def display(self) -> str: + warnings.warn('`secret_bytes.display()` is deprecated, use `str(secret_bytes)` instead', DeprecationWarning) + return str(self) + + def get_secret_value(self) -> bytes: + return self._secret_value + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class PaymentCardBrand(str, Enum): + # If you add another card type, please also add it to the + # Hypothesis strategy in `pydantic._hypothesis_plugin`. + amex = 'American Express' + mastercard = 'Mastercard' + visa = 'Visa' + other = 'other' + + def __str__(self) -> str: + return self.value + + +class PaymentCardNumber(str): + """ + Based on: https://en.wikipedia.org/wiki/Payment_card_number + """ + + strip_whitespace: ClassVar[bool] = True + min_length: ClassVar[int] = 12 + max_length: ClassVar[int] = 19 + bin: str + last4: str + brand: PaymentCardBrand + + def __init__(self, card_number: str): + self.bin = card_number[:6] + self.last4 = card_number[-4:] + self.brand = self._get_brand(card_number) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield str_validator + yield constr_strip_whitespace + yield constr_length_validator + yield cls.validate_digits + yield cls.validate_luhn_check_digit + yield cls + yield cls.validate_length_for_brand + + @property + def masked(self) -> str: + num_masked = len(self) - 10 # len(bin) + len(last4) == 10 + return f'{self.bin}{"*" * num_masked}{self.last4}' + + @classmethod + def validate_digits(cls, card_number: str) -> str: + if not card_number.isdigit(): + raise errors.NotDigitError + return card_number + + @classmethod + def validate_luhn_check_digit(cls, card_number: str) -> str: + """ + Based on: https://en.wikipedia.org/wiki/Luhn_algorithm + """ + sum_ = int(card_number[-1]) + length = len(card_number) + parity = length % 2 + for i in range(length - 1): + digit = int(card_number[i]) + if i % 2 == parity: + digit *= 2 + if digit > 9: + digit -= 9 + sum_ += digit + valid = sum_ % 10 == 0 + if not valid: + raise errors.LuhnValidationError + return card_number + + @classmethod + def validate_length_for_brand(cls, card_number: 'PaymentCardNumber') -> 'PaymentCardNumber': + """ + Validate length based on BIN for major brands: + https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN) + """ + required_length: Union[None, int, str] = None + if card_number.brand in PaymentCardBrand.mastercard: + required_length = 16 + valid = len(card_number) == required_length + elif card_number.brand == PaymentCardBrand.visa: + required_length = '13, 16 or 19' + valid = len(card_number) in {13, 16, 19} + elif card_number.brand == PaymentCardBrand.amex: + required_length = 15 + valid = len(card_number) == required_length + else: + valid = True + if not valid: + raise errors.InvalidLengthForBrand(brand=card_number.brand, required_length=required_length) + return card_number + + @staticmethod + def _get_brand(card_number: str) -> PaymentCardBrand: + if card_number[0] == '4': + brand = PaymentCardBrand.visa + elif 51 <= int(card_number[:2]) <= 55: + brand = PaymentCardBrand.mastercard + elif card_number[:2] in {'34', '37'}: + brand = PaymentCardBrand.amex + else: + brand = PaymentCardBrand.other + return brand + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +BYTE_SIZES = { + 'b': 1, + 'kb': 10**3, + 'mb': 10**6, + 'gb': 10**9, + 'tb': 10**12, + 'pb': 10**15, + 'eb': 10**18, + 'kib': 2**10, + 'mib': 2**20, + 'gib': 2**30, + 'tib': 2**40, + 'pib': 2**50, + 'eib': 2**60, +} +BYTE_SIZES.update({k.lower()[0]: v for k, v in BYTE_SIZES.items() if 'i' not in k}) +byte_string_re = re.compile(r'^\s*(\d*\.?\d+)\s*(\w+)?', re.IGNORECASE) + + +class ByteSize(int): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, v: StrIntFloat) -> 'ByteSize': + try: + return cls(int(v)) + except ValueError: + pass + + str_match = byte_string_re.match(str(v)) + if str_match is None: + raise errors.InvalidByteSize() + + scalar, unit = str_match.groups() + if unit is None: + unit = 'b' + + try: + unit_mult = BYTE_SIZES[unit.lower()] + except KeyError: + raise errors.InvalidByteSizeUnit(unit=unit) + + return cls(int(float(scalar) * unit_mult)) + + def human_readable(self, decimal: bool = False) -> str: + if decimal: + divisor = 1000 + units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + final_unit = 'EB' + else: + divisor = 1024 + units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] + final_unit = 'EiB' + + num = float(self) + for unit in units: + if abs(num) < divisor: + return f'{num:0.1f}{unit}' + num /= divisor + + return f'{num:0.1f}{final_unit}' + + def to(self, unit: str) -> float: + try: + unit_div = BYTE_SIZES[unit.lower()] + except KeyError: + raise errors.InvalidByteSizeUnit(unit=unit) + + return self / unit_div + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + PastDate = date + FutureDate = date +else: + + class PastDate(date): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield cls.validate + + @classmethod + def validate(cls, value: date) -> date: + if value >= date.today(): + raise errors.DateNotInThePastError() + + return value + + class FutureDate(date): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield cls.validate + + @classmethod + def validate(cls, value: date) -> date: + if value <= date.today(): + raise errors.DateNotInTheFutureError() + + return value + + +class ConstrainedDate(date, metaclass=ConstrainedNumberMeta): + gt: OptionalDate = None + ge: OptionalDate = None + lt: OptionalDate = None + le: OptionalDate = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, exclusiveMinimum=cls.gt, exclusiveMaximum=cls.lt, minimum=cls.ge, maximum=cls.le) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield number_size_validator + + +def condate( + *, + gt: date = None, + ge: date = None, + lt: date = None, + le: date = None, +) -> Type[date]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(gt=gt, ge=ge, lt=lt, le=le) + return type('ConstrainedDateValue', (ConstrainedDate,), namespace) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/typing.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/typing.py new file mode 100644 index 0000000..a690a05 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/typing.py @@ -0,0 +1,603 @@ +import sys +import typing +from collections.abc import Callable +from os import PathLike +from typing import ( # type: ignore + TYPE_CHECKING, + AbstractSet, + Any, + Callable as TypingCallable, + ClassVar, + Dict, + ForwardRef, + Generator, + Iterable, + List, + Mapping, + NewType, + Optional, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, + _eval_type, + cast, + get_type_hints, +) + +from typing_extensions import ( + Annotated, + Final, + Literal, + NotRequired as TypedDictNotRequired, + Required as TypedDictRequired, +) + +try: + from typing import _TypingBase as typing_base # type: ignore +except ImportError: + from typing import _Final as typing_base # type: ignore + +try: + from typing import GenericAlias as TypingGenericAlias # type: ignore +except ImportError: + # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) + TypingGenericAlias = () + +try: + from types import UnionType as TypesUnionType # type: ignore +except ImportError: + # python < 3.10 does not have UnionType (str | int, byte | bool and so on) + TypesUnionType = () + + +if sys.version_info < (3, 9): + + def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: + return type_._evaluate(globalns, localns) + +else: + + def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: + # Even though it is the right signature for python 3.9, mypy complains with + # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... + return cast(Any, type_)._evaluate(globalns, localns, set()) + + +if sys.version_info < (3, 9): + # Ensure we always get all the whole `Annotated` hint, not just the annotated type. + # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, + # so it already returns the full annotation + get_all_type_hints = get_type_hints + +else: + + def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any: + return get_type_hints(obj, globalns, localns, include_extras=True) + + +_T = TypeVar('_T') + +AnyCallable = TypingCallable[..., Any] +NoArgAnyCallable = TypingCallable[[], Any] + +# workaround for https://github.com/python/mypy/issues/9496 +AnyArgTCallable = TypingCallable[..., _T] + + +# Annotated[...] is implemented by returning an instance of one of these classes, depending on +# python/typing_extensions version. +AnnotatedTypeNames = {'AnnotatedMeta', '_AnnotatedAlias'} + + +LITERAL_TYPES: Set[Any] = {Literal} +if hasattr(typing, 'Literal'): + LITERAL_TYPES.add(typing.Literal) + + +if sys.version_info < (3, 8): + + def get_origin(t: Type[Any]) -> Optional[Type[Any]]: + if type(t).__name__ in AnnotatedTypeNames: + # weirdly this is a runtime requirement, as well as for mypy + return cast(Type[Any], Annotated) + return getattr(t, '__origin__', None) + +else: + from typing import get_origin as _typing_get_origin + + def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: + """ + We can't directly use `typing.get_origin` since we need a fallback to support + custom generic classes like `ConstrainedList` + It should be useless once https://github.com/cython/cython/issues/3537 is + solved and https://github.com/pydantic/pydantic/pull/1753 is merged. + """ + if type(tp).__name__ in AnnotatedTypeNames: + return cast(Type[Any], Annotated) # mypy complains about _SpecialForm + return _typing_get_origin(tp) or getattr(tp, '__origin__', None) + + +if sys.version_info < (3, 8): + from typing import _GenericAlias + + def get_args(t: Type[Any]) -> Tuple[Any, ...]: + """Compatibility version of get_args for python 3.7. + + Mostly compatible with the python 3.8 `typing` module version + and able to handle almost all use cases. + """ + if type(t).__name__ in AnnotatedTypeNames: + return t.__args__ + t.__metadata__ + if isinstance(t, _GenericAlias): + res = t.__args__ + if t.__origin__ is Callable and res and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return getattr(t, '__args__', ()) + +else: + from typing import get_args as _typing_get_args + + def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """ + In python 3.9, `typing.Dict`, `typing.List`, ... + do have an empty `__args__` by default (instead of the generic ~T for example). + In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, + we retrieve the `_nparams` value that tells us how many parameters it needs. + """ + if hasattr(tp, '_nparams'): + return (Any,) * tp._nparams + # Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple` + # in python 3.10- but now returns () for `tuple` and `Tuple`. + # This will probably be clarified in pydantic v2 + try: + if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]: # type: ignore[misc] + return ((),) + # there is a TypeError when compiled with cython + except TypeError: # pragma: no cover + pass + return () + + def get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if type(tp).__name__ in AnnotatedTypeNames: + return tp.__args__ + tp.__metadata__ + # the fallback is needed for the same reasons as `get_origin` (see above) + return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp) + + +if sys.version_info < (3, 9): + + def convert_generics(tp: Type[Any]) -> Type[Any]: + """Python 3.9 and older only supports generics from `typing` module. + They convert strings to ForwardRef automatically. + + Examples:: + typing.List['Hero'] == typing.List[ForwardRef('Hero')] + """ + return tp + +else: + from typing import _UnionGenericAlias # type: ignore + + from typing_extensions import _AnnotatedAlias + + def convert_generics(tp: Type[Any]) -> Type[Any]: + """ + Recursively searches for `str` type hints and replaces them with ForwardRef. + + Examples:: + convert_generics(list['Hero']) == list[ForwardRef('Hero')] + convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')] + convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')] + convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int + """ + origin = get_origin(tp) + if not origin or not hasattr(tp, '__args__'): + return tp + + args = get_args(tp) + + # typing.Annotated needs special treatment + if origin is Annotated: + return _AnnotatedAlias(convert_generics(args[0]), args[1:]) + + # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)` + converted = tuple( + ForwardRef(arg) if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) else convert_generics(arg) + for arg in args + ) + + if converted == args: + return tp + elif isinstance(tp, TypingGenericAlias): + return TypingGenericAlias(origin, converted) + elif isinstance(tp, TypesUnionType): + # recreate types.UnionType (PEP604, Python >= 3.10) + return _UnionGenericAlias(origin, converted) + else: + try: + setattr(tp, '__args__', converted) + except AttributeError: + pass + return tp + + +if sys.version_info < (3, 10): + + def is_union(tp: Optional[Type[Any]]) -> bool: + return tp is Union + + WithArgsTypes = (TypingGenericAlias,) + +else: + import types + import typing + + def is_union(tp: Optional[Type[Any]]) -> bool: + return tp is Union or tp is types.UnionType # noqa: E721 + + WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) + + +StrPath = Union[str, PathLike] + + +if TYPE_CHECKING: + from .fields import ModelField + + TupleGenerator = Generator[Tuple[str, Any], None, None] + DictStrAny = Dict[str, Any] + DictAny = Dict[Any, Any] + SetStr = Set[str] + ListStr = List[str] + IntStr = Union[int, str] + AbstractSetIntStr = AbstractSet[IntStr] + DictIntStrAny = Dict[IntStr, Any] + MappingIntStrAny = Mapping[IntStr, Any] + CallableGenerator = Generator[AnyCallable, None, None] + ReprArgs = Sequence[Tuple[Optional[str], Any]] + + MYPY = False + if MYPY: + AnyClassMethod = classmethod[Any] + else: + # classmethod[TargetType, CallableParamSpecType, CallableReturnType] + AnyClassMethod = classmethod[Any, Any, Any] + +__all__ = ( + 'AnyCallable', + 'NoArgAnyCallable', + 'NoneType', + 'is_none_type', + 'display_as_type', + 'resolve_annotations', + 'is_callable_type', + 'is_literal_type', + 'all_literal_values', + 'is_namedtuple', + 'is_typeddict', + 'is_typeddict_special', + 'is_new_type', + 'new_type_supertype', + 'is_classvar', + 'is_finalvar', + 'update_field_forward_refs', + 'update_model_forward_refs', + 'TupleGenerator', + 'DictStrAny', + 'DictAny', + 'SetStr', + 'ListStr', + 'IntStr', + 'AbstractSetIntStr', + 'DictIntStrAny', + 'CallableGenerator', + 'ReprArgs', + 'AnyClassMethod', + 'CallableGenerator', + 'WithArgsTypes', + 'get_args', + 'get_origin', + 'get_sub_types', + 'typing_base', + 'get_all_type_hints', + 'is_union', + 'StrPath', + 'MappingIntStrAny', +) + + +NoneType = None.__class__ + + +NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None]) + + +if sys.version_info < (3, 8): + # Even though this implementation is slower, we need it for python 3.7: + # In python 3.7 "Literal" is not a builtin type and uses a different + # mechanism. + # for this reason `Literal[None] is Literal[None]` evaluates to `False`, + # breaking the faster implementation used for the other python versions. + + def is_none_type(type_: Any) -> bool: + return type_ in NONE_TYPES + +elif sys.version_info[:2] == (3, 8): + + def is_none_type(type_: Any) -> bool: + for none_type in NONE_TYPES: + if type_ is none_type: + return True + # With python 3.8, specifically 3.8.10, Literal "is" check sare very flakey + # can change on very subtle changes like use of types in other modules, + # hopefully this check avoids that issue. + if is_literal_type(type_): # pragma: no cover + return all_literal_values(type_) == (None,) + return False + +else: + + def is_none_type(type_: Any) -> bool: + return type_ in NONE_TYPES + + +def display_as_type(v: Type[Any]) -> str: + if not isinstance(v, typing_base) and not isinstance(v, WithArgsTypes) and not isinstance(v, type): + v = v.__class__ + + if is_union(get_origin(v)): + return f'Union[{", ".join(map(display_as_type, get_args(v)))}]' + + if isinstance(v, WithArgsTypes): + # Generic alias are constructs like `list[int]` + return str(v).replace('typing.', '') + + try: + return v.__name__ + except AttributeError: + # happens with typing objects + return str(v).replace('typing.', '') + + +def resolve_annotations(raw_annotations: Dict[str, Type[Any]], module_name: Optional[str]) -> Dict[str, Type[Any]]: + """ + Partially taken from typing.get_type_hints. + + Resolve string or ForwardRef annotations into type objects if possible. + """ + base_globals: Optional[Dict[str, Any]] = None + if module_name: + try: + module = sys.modules[module_name] + except KeyError: + # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 + pass + else: + base_globals = module.__dict__ + + annotations = {} + for name, value in raw_annotations.items(): + if isinstance(value, str): + if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= (3, 10, 1): + value = ForwardRef(value, is_argument=False, is_class=True) + else: + value = ForwardRef(value, is_argument=False) + try: + value = _eval_type(value, base_globals, None) + except NameError: + # this is ok, it can be fixed with update_forward_refs + pass + annotations[name] = value + return annotations + + +def is_callable_type(type_: Type[Any]) -> bool: + return type_ is Callable or get_origin(type_) is Callable + + +def is_literal_type(type_: Type[Any]) -> bool: + return Literal is not None and get_origin(type_) in LITERAL_TYPES + + +def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + return get_args(type_) + + +def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + """ + This method is used to retrieve all Literal values as + Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) + e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]` + """ + if not is_literal_type(type_): + return (type_,) + + values = literal_values(type_) + return tuple(x for value in values for x in all_literal_values(value)) + + +def is_namedtuple(type_: Type[Any]) -> bool: + """ + Check if a given class is a named tuple. + It can be either a `typing.NamedTuple` or `collections.namedtuple` + """ + from .utils import lenient_issubclass + + return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') + + +def is_typeddict(type_: Type[Any]) -> bool: + """ + Check if a given class is a typed dict (from `typing` or `typing_extensions`) + In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict) + """ + from .utils import lenient_issubclass + + return lenient_issubclass(type_, dict) and hasattr(type_, '__total__') + + +def _check_typeddict_special(type_: Any) -> bool: + return type_ is TypedDictRequired or type_ is TypedDictNotRequired + + +def is_typeddict_special(type_: Any) -> bool: + """ + Check if type is a TypedDict special form (Required or NotRequired). + """ + return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_)) + + +test_type = NewType('test_type', str) + + +def is_new_type(type_: Type[Any]) -> bool: + """ + Check whether type_ was created using typing.NewType + """ + return isinstance(type_, test_type.__class__) and hasattr(type_, '__supertype__') # type: ignore + + +def new_type_supertype(type_: Type[Any]) -> Type[Any]: + while hasattr(type_, '__supertype__'): + type_ = type_.__supertype__ + return type_ + + +def _check_classvar(v: Optional[Type[Any]]) -> bool: + if v is None: + return False + + return v.__class__ == ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' + + +def _check_finalvar(v: Optional[Type[Any]]) -> bool: + """ + Check if a given type is a `typing.Final` type. + """ + if v is None: + return False + + return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') + + +def is_classvar(ann_type: Type[Any]) -> bool: + if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): + return True + + # this is an ugly workaround for class vars that contain forward references and are therefore themselves + # forward references, see #3679 + if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith('ClassVar['): + return True + + return False + + +def is_finalvar(ann_type: Type[Any]) -> bool: + return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) + + +def update_field_forward_refs(field: 'ModelField', globalns: Any, localns: Any) -> None: + """ + Try to update ForwardRefs on fields based on this ModelField, globalns and localns. + """ + prepare = False + if field.type_.__class__ == ForwardRef: + prepare = True + field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) + if field.outer_type_.__class__ == ForwardRef: + prepare = True + field.outer_type_ = evaluate_forwardref(field.outer_type_, globalns, localns or None) + if prepare: + field.prepare() + + if field.sub_fields: + for sub_f in field.sub_fields: + update_field_forward_refs(sub_f, globalns=globalns, localns=localns) + + if field.discriminator_key is not None: + field.prepare_discriminated_union_sub_fields() + + +def update_model_forward_refs( + model: Type[Any], + fields: Iterable['ModelField'], + json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable], + localns: 'DictStrAny', + exc_to_suppress: Tuple[Type[BaseException], ...] = (), +) -> None: + """ + Try to update model fields ForwardRefs based on model and localns. + """ + if model.__module__ in sys.modules: + globalns = sys.modules[model.__module__].__dict__.copy() + else: + globalns = {} + + globalns.setdefault(model.__name__, model) + + for f in fields: + try: + update_field_forward_refs(f, globalns=globalns, localns=localns) + except exc_to_suppress: + pass + + for key in set(json_encoders.keys()): + if isinstance(key, str): + fr: ForwardRef = ForwardRef(key) + elif isinstance(key, ForwardRef): + fr = key + else: + continue + + try: + new_key = evaluate_forwardref(fr, globalns, localns or None) + except exc_to_suppress: # pragma: no cover + continue + + json_encoders[new_key] = json_encoders.pop(key) + + +def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]: + """ + Tries to get the class of a Type[T] annotation. Returns True if Type is used + without brackets. Otherwise returns None. + """ + if type_ is type: + return True + + if get_origin(type_) is None: + return None + + args = get_args(type_) + if not args or not isinstance(args[0], type): + return True + else: + return args[0] + + +def get_sub_types(tp: Any) -> List[Any]: + """ + Return all the types that are allowed by type `tp` + `tp` can be a `Union` of allowed types or an `Annotated` type + """ + origin = get_origin(tp) + if origin is Annotated: + return get_sub_types(get_args(tp)[0]) + elif is_union(origin): + return [x for t in get_args(tp) for x in get_sub_types(t)] + else: + return [tp] diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/utils.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/utils.py new file mode 100644 index 0000000..4d0f68e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/utils.py @@ -0,0 +1,803 @@ +import keyword +import warnings +import weakref +from collections import OrderedDict, defaultdict, deque +from copy import deepcopy +from itertools import islice, zip_longest +from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType +from typing import ( + TYPE_CHECKING, + AbstractSet, + Any, + Callable, + Collection, + Dict, + Generator, + Iterable, + Iterator, + List, + Mapping, + NoReturn, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from typing_extensions import Annotated + +from .errors import ConfigError +from .typing import ( + NoneType, + WithArgsTypes, + all_literal_values, + display_as_type, + get_args, + get_origin, + is_literal_type, + is_union, +) +from .version import version_info + +if TYPE_CHECKING: + from inspect import Signature + from pathlib import Path + + from .config import BaseConfig + from .dataclasses import Dataclass + from .fields import ModelField + from .main import BaseModel + from .typing import AbstractSetIntStr, DictIntStrAny, IntStr, MappingIntStrAny, ReprArgs + + RichReprResult = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]] + +__all__ = ( + 'import_string', + 'sequence_like', + 'validate_field_name', + 'lenient_isinstance', + 'lenient_issubclass', + 'in_ipython', + 'is_valid_identifier', + 'deep_update', + 'update_not_none', + 'almost_equal_floats', + 'get_model', + 'to_camel', + 'is_valid_field', + 'smart_deepcopy', + 'PyObjectStr', + 'Representation', + 'GetterDict', + 'ValueItems', + 'version_info', # required here to match behaviour in v1.3 + 'ClassAttribute', + 'path_type', + 'ROOT_KEY', + 'get_unique_discriminator_alias', + 'get_discriminator_alias_and_values', + 'DUNDER_ATTRIBUTES', +) + +ROOT_KEY = '__root__' +# these are types that are returned unchanged by deepcopy +IMMUTABLE_NON_COLLECTIONS_TYPES: Set[Type[Any]] = { + int, + float, + complex, + str, + bool, + bytes, + type, + NoneType, + FunctionType, + BuiltinFunctionType, + LambdaType, + weakref.ref, + CodeType, + # note: including ModuleType will differ from behaviour of deepcopy by not producing error. + # It might be not a good idea in general, but considering that this function used only internally + # against default values of fields, this will allow to actually have a field with module as default value + ModuleType, + NotImplemented.__class__, + Ellipsis.__class__, +} + +# these are types that if empty, might be copied with simple copy() instead of deepcopy() +BUILTIN_COLLECTIONS: Set[Type[Any]] = { + list, + set, + tuple, + frozenset, + dict, + OrderedDict, + defaultdict, + deque, +} + + +def import_string(dotted_path: str) -> Any: + """ + Stolen approximately from django. Import a dotted module path and return the attribute/class designated by the + last name in the path. Raise ImportError if the import fails. + """ + from importlib import import_module + + try: + module_path, class_name = dotted_path.strip(' ').rsplit('.', 1) + except ValueError as e: + raise ImportError(f'"{dotted_path}" doesn\'t look like a module path') from e + + module = import_module(module_path) + try: + return getattr(module, class_name) + except AttributeError as e: + raise ImportError(f'Module "{module_path}" does not define a "{class_name}" attribute') from e + + +def truncate(v: Union[str], *, max_len: int = 80) -> str: + """ + Truncate a value and add a unicode ellipsis (three dots) to the end if it was too long + """ + warnings.warn('`truncate` is no-longer used by pydantic and is deprecated', DeprecationWarning) + if isinstance(v, str) and len(v) > (max_len - 2): + # -3 so quote + string + … + quote has correct length + return (v[: (max_len - 3)] + '…').__repr__() + try: + v = v.__repr__() + except TypeError: + v = v.__class__.__repr__(v) # in case v is a type + if len(v) > max_len: + v = v[: max_len - 1] + '…' + return v + + +def sequence_like(v: Any) -> bool: + return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) + + +def validate_field_name(bases: List[Type['BaseModel']], field_name: str) -> None: + """ + Ensure that the field's name does not shadow an existing attribute of the model. + """ + for base in bases: + if getattr(base, field_name, None): + raise NameError( + f'Field name "{field_name}" shadows a BaseModel attribute; ' + f'use a different field name with "alias=\'{field_name}\'".' + ) + + +def lenient_isinstance(o: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool: + try: + return isinstance(o, class_or_tuple) # type: ignore[arg-type] + except TypeError: + return False + + +def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool: + try: + return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type] + except TypeError: + if isinstance(cls, WithArgsTypes): + return False + raise # pragma: no cover + + +def in_ipython() -> bool: + """ + Check whether we're in an ipython environment, including jupyter notebooks. + """ + try: + eval('__IPYTHON__') + except NameError: + return False + else: # pragma: no cover + return True + + +def is_valid_identifier(identifier: str) -> bool: + """ + Checks that a string is a valid identifier and not a Python keyword. + :param identifier: The identifier to test. + :return: True if the identifier is valid. + """ + return identifier.isidentifier() and not keyword.iskeyword(identifier) + + +KeyType = TypeVar('KeyType') + + +def deep_update(mapping: Dict[KeyType, Any], *updating_mappings: Dict[KeyType, Any]) -> Dict[KeyType, Any]: + updated_mapping = mapping.copy() + for updating_mapping in updating_mappings: + for k, v in updating_mapping.items(): + if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict): + updated_mapping[k] = deep_update(updated_mapping[k], v) + else: + updated_mapping[k] = v + return updated_mapping + + +def update_not_none(mapping: Dict[Any, Any], **update: Any) -> None: + mapping.update({k: v for k, v in update.items() if v is not None}) + + +def almost_equal_floats(value_1: float, value_2: float, *, delta: float = 1e-8) -> bool: + """ + Return True if two floats are almost equal + """ + return abs(value_1 - value_2) <= delta + + +def generate_model_signature( + init: Callable[..., None], fields: Dict[str, 'ModelField'], config: Type['BaseConfig'] +) -> 'Signature': + """ + Generate signature for model based on its fields + """ + from inspect import Parameter, Signature, signature + + from .config import Extra + + present_params = signature(init).parameters.values() + merged_params: Dict[str, Parameter] = {} + var_kw = None + use_var_kw = False + + for param in islice(present_params, 1, None): # skip self arg + if param.kind is param.VAR_KEYWORD: + var_kw = param + continue + merged_params[param.name] = param + + if var_kw: # if custom init has no var_kw, fields which are not declared in it cannot be passed through + allow_names = config.allow_population_by_field_name + for field_name, field in fields.items(): + param_name = field.alias + if field_name in merged_params or param_name in merged_params: + continue + elif not is_valid_identifier(param_name): + if allow_names and is_valid_identifier(field_name): + param_name = field_name + else: + use_var_kw = True + continue + + # TODO: replace annotation with actual expected types once #1055 solved + kwargs = {'default': field.default} if not field.required else {} + merged_params[param_name] = Parameter( + param_name, Parameter.KEYWORD_ONLY, annotation=field.annotation, **kwargs + ) + + if config.extra is Extra.allow: + use_var_kw = True + + if var_kw and use_var_kw: + # Make sure the parameter for extra kwargs + # does not have the same name as a field + default_model_signature = [ + ('__pydantic_self__', Parameter.POSITIONAL_OR_KEYWORD), + ('data', Parameter.VAR_KEYWORD), + ] + if [(p.name, p.kind) for p in present_params] == default_model_signature: + # if this is the standard model signature, use extra_data as the extra args name + var_kw_name = 'extra_data' + else: + # else start from var_kw + var_kw_name = var_kw.name + + # generate a name that's definitely unique + while var_kw_name in fields: + var_kw_name += '_' + merged_params[var_kw_name] = var_kw.replace(name=var_kw_name) + + return Signature(parameters=list(merged_params.values()), return_annotation=None) + + +def get_model(obj: Union[Type['BaseModel'], Type['Dataclass']]) -> Type['BaseModel']: + from .main import BaseModel + + try: + model_cls = obj.__pydantic_model__ # type: ignore + except AttributeError: + model_cls = obj + + if not issubclass(model_cls, BaseModel): + raise TypeError('Unsupported type, must be either BaseModel or dataclass') + return model_cls + + +def to_camel(string: str) -> str: + return ''.join(word.capitalize() for word in string.split('_')) + + +def to_lower_camel(string: str) -> str: + if len(string) >= 1: + pascal_string = to_camel(string) + return pascal_string[0].lower() + pascal_string[1:] + return string.lower() + + +T = TypeVar('T') + + +def unique_list( + input_list: Union[List[T], Tuple[T, ...]], + *, + name_factory: Callable[[T], str] = str, +) -> List[T]: + """ + Make a list unique while maintaining order. + We update the list if another one with the same name is set + (e.g. root validator overridden in subclass) + """ + result: List[T] = [] + result_names: List[str] = [] + for v in input_list: + v_name = name_factory(v) + if v_name not in result_names: + result_names.append(v_name) + result.append(v) + else: + result[result_names.index(v_name)] = v + + return result + + +class PyObjectStr(str): + """ + String class where repr doesn't include quotes. Useful with Representation when you want to return a string + representation of something that valid (or pseudo-valid) python. + """ + + def __repr__(self) -> str: + return str(self) + + +class Representation: + """ + Mixin to provide __str__, __repr__, and __pretty__ methods. See #884 for more details. + + __pretty__ is used by [devtools](https://python-devtools.helpmanual.io/) to provide human readable representations + of objects. + """ + + __slots__: Tuple[str, ...] = tuple() + + def __repr_args__(self) -> 'ReprArgs': + """ + Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden. + + Can either return: + * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]` + * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]` + """ + attrs = ((s, getattr(self, s)) for s in self.__slots__) + return [(a, v) for a, v in attrs if v is not None] + + def __repr_name__(self) -> str: + """ + Name of the instance's class, used in __repr__. + """ + return self.__class__.__name__ + + def __repr_str__(self, join_str: str) -> str: + return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__()) + + def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any, None, None]: + """ + Used by devtools (https://python-devtools.helpmanual.io/) to provide a human readable representations of objects + """ + yield self.__repr_name__() + '(' + yield 1 + for name, value in self.__repr_args__(): + if name is not None: + yield name + '=' + yield fmt(value) + yield ',' + yield 0 + yield -1 + yield ')' + + def __str__(self) -> str: + return self.__repr_str__(' ') + + def __repr__(self) -> str: + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' + + def __rich_repr__(self) -> 'RichReprResult': + """Get fields for Rich library""" + for name, field_repr in self.__repr_args__(): + if name is None: + yield field_repr + else: + yield name, field_repr + + +class GetterDict(Representation): + """ + Hack to make object's smell just enough like dicts for validate_model. + + We can't inherit from Mapping[str, Any] because it upsets cython so we have to implement all methods ourselves. + """ + + __slots__ = ('_obj',) + + def __init__(self, obj: Any): + self._obj = obj + + def __getitem__(self, key: str) -> Any: + try: + return getattr(self._obj, key) + except AttributeError as e: + raise KeyError(key) from e + + def get(self, key: Any, default: Any = None) -> Any: + return getattr(self._obj, key, default) + + def extra_keys(self) -> Set[Any]: + """ + We don't want to get any other attributes of obj if the model didn't explicitly ask for them + """ + return set() + + def keys(self) -> List[Any]: + """ + Keys of the pseudo dictionary, uses a list not set so order information can be maintained like python + dictionaries. + """ + return list(self) + + def values(self) -> List[Any]: + return [self[k] for k in self] + + def items(self) -> Iterator[Tuple[str, Any]]: + for k in self: + yield k, self.get(k) + + def __iter__(self) -> Iterator[str]: + for name in dir(self._obj): + if not name.startswith('_'): + yield name + + def __len__(self) -> int: + return sum(1 for _ in self) + + def __contains__(self, item: Any) -> bool: + return item in self.keys() + + def __eq__(self, other: Any) -> bool: + return dict(self) == dict(other.items()) + + def __repr_args__(self) -> 'ReprArgs': + return [(None, dict(self))] + + def __repr_name__(self) -> str: + return f'GetterDict[{display_as_type(self._obj)}]' + + +class ValueItems(Representation): + """ + Class for more convenient calculation of excluded or included fields on values. + """ + + __slots__ = ('_items', '_type') + + def __init__(self, value: Any, items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> None: + items = self._coerce_items(items) + + if isinstance(value, (list, tuple)): + items = self._normalize_indexes(items, len(value)) + + self._items: 'MappingIntStrAny' = items + + def is_excluded(self, item: Any) -> bool: + """ + Check if item is fully excluded. + + :param item: key or index of a value + """ + return self.is_true(self._items.get(item)) + + def is_included(self, item: Any) -> bool: + """ + Check if value is contained in self._items + + :param item: key or index of value + """ + return item in self._items + + def for_element(self, e: 'IntStr') -> Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']]: + """ + :param e: key or index of element on value + :return: raw values for element if self._items is dict and contain needed element + """ + + item = self._items.get(e) + return item if not self.is_true(item) else None + + def _normalize_indexes(self, items: 'MappingIntStrAny', v_length: int) -> 'DictIntStrAny': + """ + :param items: dict or set of indexes which will be normalized + :param v_length: length of sequence indexes of which will be + + >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4) + {0: True, 2: True, 3: True} + >>> self._normalize_indexes({'__all__': True}, 4) + {0: True, 1: True, 2: True, 3: True} + """ + + normalized_items: 'DictIntStrAny' = {} + all_items = None + for i, v in items.items(): + if not (isinstance(v, Mapping) or isinstance(v, AbstractSet) or self.is_true(v)): + raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}') + if i == '__all__': + all_items = self._coerce_value(v) + continue + if not isinstance(i, int): + raise TypeError( + 'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: ' + 'expected integer keys or keyword "__all__"' + ) + normalized_i = v_length + i if i < 0 else i + normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i)) + + if not all_items: + return normalized_items + if self.is_true(all_items): + for i in range(v_length): + normalized_items.setdefault(i, ...) + return normalized_items + for i in range(v_length): + normalized_item = normalized_items.setdefault(i, {}) + if not self.is_true(normalized_item): + normalized_items[i] = self.merge(all_items, normalized_item) + return normalized_items + + @classmethod + def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any: + """ + Merge a ``base`` item with an ``override`` item. + + Both ``base`` and ``override`` are converted to dictionaries if possible. + Sets are converted to dictionaries with the sets entries as keys and + Ellipsis as values. + + Each key-value pair existing in ``base`` is merged with ``override``, + while the rest of the key-value pairs are updated recursively with this function. + + Merging takes place based on the "union" of keys if ``intersect`` is + set to ``False`` (default) and on the intersection of keys if + ``intersect`` is set to ``True``. + """ + override = cls._coerce_value(override) + base = cls._coerce_value(base) + if override is None: + return base + if cls.is_true(base) or base is None: + return override + if cls.is_true(override): + return base if intersect else override + + # intersection or union of keys while preserving ordering: + if intersect: + merge_keys = [k for k in base if k in override] + [k for k in override if k in base] + else: + merge_keys = list(base) + [k for k in override if k not in base] + + merged: 'DictIntStrAny' = {} + for k in merge_keys: + merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect) + if merged_item is not None: + merged[k] = merged_item + + return merged + + @staticmethod + def _coerce_items(items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> 'MappingIntStrAny': + if isinstance(items, Mapping): + pass + elif isinstance(items, AbstractSet): + items = dict.fromkeys(items, ...) + else: + class_name = getattr(items, '__class__', '???') + assert_never( + items, + f'Unexpected type of exclude value {class_name}', + ) + return items + + @classmethod + def _coerce_value(cls, value: Any) -> Any: + if value is None or cls.is_true(value): + return value + return cls._coerce_items(value) + + @staticmethod + def is_true(v: Any) -> bool: + return v is True or v is ... + + def __repr_args__(self) -> 'ReprArgs': + return [(None, self._items)] + + +class ClassAttribute: + """ + Hide class attribute from its instances + """ + + __slots__ = ( + 'name', + 'value', + ) + + def __init__(self, name: str, value: Any) -> None: + self.name = name + self.value = value + + def __get__(self, instance: Any, owner: Type[Any]) -> None: + if instance is None: + return self.value + raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only') + + +path_types = { + 'is_dir': 'directory', + 'is_file': 'file', + 'is_mount': 'mount point', + 'is_symlink': 'symlink', + 'is_block_device': 'block device', + 'is_char_device': 'char device', + 'is_fifo': 'FIFO', + 'is_socket': 'socket', +} + + +def path_type(p: 'Path') -> str: + """ + Find out what sort of thing a path is. + """ + assert p.exists(), 'path does not exist' + for method, name in path_types.items(): + if getattr(p, method)(): + return name + + return 'unknown' + + +Obj = TypeVar('Obj') + + +def smart_deepcopy(obj: Obj) -> Obj: + """ + Return type as is for immutable built-in types + Use obj.copy() for built-in empty collections + Use copy.deepcopy() for non-empty collections and unknown objects + """ + + obj_type = obj.__class__ + if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES: + return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway + try: + if not obj and obj_type in BUILTIN_COLLECTIONS: + # faster way for empty collections, no need to copy its members + return obj if obj_type is tuple else obj.copy() # type: ignore # tuple doesn't have copy method + except (TypeError, ValueError, RuntimeError): + # do we really dare to catch ALL errors? Seems a bit risky + pass + + return deepcopy(obj) # slowest way when we actually might need a deepcopy + + +def is_valid_field(name: str) -> bool: + if not name.startswith('_'): + return True + return ROOT_KEY == name + + +DUNDER_ATTRIBUTES = { + '__annotations__', + '__classcell__', + '__doc__', + '__module__', + '__orig_bases__', + '__orig_class__', + '__qualname__', +} + + +def is_valid_private_name(name: str) -> bool: + return not is_valid_field(name) and name not in DUNDER_ATTRIBUTES + + +_EMPTY = object() + + +def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool: + """ + Check that the items of `left` are the same objects as those in `right`. + + >>> a, b = object(), object() + >>> all_identical([a, b, a], [a, b, a]) + True + >>> all_identical([a, b, [a]], [a, b, [a]]) # new list object, while "equal" is not "identical" + False + """ + for left_item, right_item in zip_longest(left, right, fillvalue=_EMPTY): + if left_item is not right_item: + return False + return True + + +def assert_never(obj: NoReturn, msg: str) -> NoReturn: + """ + Helper to make sure that we have covered all possible types. + + This is mostly useful for ``mypy``, docs: + https://mypy.readthedocs.io/en/latest/literal_types.html#exhaustive-checks + """ + raise TypeError(msg) + + +def get_unique_discriminator_alias(all_aliases: Collection[str], discriminator_key: str) -> str: + """Validate that all aliases are the same and if that's the case return the alias""" + unique_aliases = set(all_aliases) + if len(unique_aliases) > 1: + raise ConfigError( + f'Aliases for discriminator {discriminator_key!r} must be the same (got {", ".join(sorted(all_aliases))})' + ) + return unique_aliases.pop() + + +def get_discriminator_alias_and_values(tp: Any, discriminator_key: str) -> Tuple[str, Tuple[str, ...]]: + """ + Get alias and all valid values in the `Literal` type of the discriminator field + `tp` can be a `BaseModel` class or directly an `Annotated` `Union` of many. + """ + is_root_model = getattr(tp, '__custom_root_type__', False) + + if get_origin(tp) is Annotated: + tp = get_args(tp)[0] + + if hasattr(tp, '__pydantic_model__'): + tp = tp.__pydantic_model__ + + if is_union(get_origin(tp)): + alias, all_values = _get_union_alias_and_all_values(tp, discriminator_key) + return alias, tuple(v for values in all_values for v in values) + elif is_root_model: + union_type = tp.__fields__[ROOT_KEY].type_ + alias, all_values = _get_union_alias_and_all_values(union_type, discriminator_key) + + if len(set(all_values)) > 1: + raise ConfigError( + f'Field {discriminator_key!r} is not the same for all submodels of {display_as_type(tp)!r}' + ) + + return alias, all_values[0] + + else: + try: + t_discriminator_type = tp.__fields__[discriminator_key].type_ + except AttributeError as e: + raise TypeError(f'Type {tp.__name__!r} is not a valid `BaseModel` or `dataclass`') from e + except KeyError as e: + raise ConfigError(f'Model {tp.__name__!r} needs a discriminator field for key {discriminator_key!r}') from e + + if not is_literal_type(t_discriminator_type): + raise ConfigError(f'Field {discriminator_key!r} of model {tp.__name__!r} needs to be a `Literal`') + + return tp.__fields__[discriminator_key].alias, all_literal_values(t_discriminator_type) + + +def _get_union_alias_and_all_values( + union_type: Type[Any], discriminator_key: str +) -> Tuple[str, Tuple[Tuple[str, ...], ...]]: + zipped_aliases_values = [get_discriminator_alias_and_values(t, discriminator_key) for t in get_args(union_type)] + # unzip: [('alias_a',('v1', 'v2)), ('alias_b', ('v3',))] => [('alias_a', 'alias_b'), (('v1', 'v2'), ('v3',))] + all_aliases, all_values = zip(*zipped_aliases_values) + return get_unique_discriminator_alias(all_aliases, discriminator_key), all_values diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/v1.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/v1.py new file mode 100644 index 0000000..7e7895c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/v1.py @@ -0,0 +1,116 @@ +# NOTE This file aliases the pydantic namespace as pydantic.v1 for smoother v1 -> v2 transition +# flake8: noqa +from pydantic import * + +# WARNING __all__ from .errors is not included here, it will be removed as an export here in v2 +# please use "from pydantic.errors import ..." instead +__all__ = [ + # annotated types utils + "create_model_from_namedtuple", + "create_model_from_typeddict", + # dataclasses + "dataclasses", + # class_validators + "root_validator", + "validator", + # config + "BaseConfig", + "ConfigDict", + "Extra", + # decorator + "validate_arguments", + # env_settings + "BaseSettings", + # error_wrappers + "ValidationError", + # fields + "Field", + "Required", + # main + "BaseModel", + "create_model", + "validate_model", + # network + "AnyUrl", + "AnyHttpUrl", + "FileUrl", + "HttpUrl", + "stricturl", + "EmailStr", + "NameEmail", + "IPvAnyAddress", + "IPvAnyInterface", + "IPvAnyNetwork", + "PostgresDsn", + "CockroachDsn", + "AmqpDsn", + "RedisDsn", + "MongoDsn", + "KafkaDsn", + "validate_email", + # parse + "Protocol", + # tools + "parse_file_as", + "parse_obj_as", + "parse_raw_as", + "schema_of", + "schema_json_of", + # types + "NoneStr", + "NoneBytes", + "StrBytes", + "NoneStrBytes", + "StrictStr", + "ConstrainedBytes", + "conbytes", + "ConstrainedList", + "conlist", + "ConstrainedSet", + "conset", + "ConstrainedFrozenSet", + "confrozenset", + "ConstrainedStr", + "constr", + "PyObject", + "ConstrainedInt", + "conint", + "PositiveInt", + "NegativeInt", + "NonNegativeInt", + "NonPositiveInt", + "ConstrainedFloat", + "confloat", + "PositiveFloat", + "NegativeFloat", + "NonNegativeFloat", + "NonPositiveFloat", + "FiniteFloat", + "ConstrainedDecimal", + "condecimal", + "ConstrainedDate", + "condate", + "UUID1", + "UUID3", + "UUID4", + "UUID5", + "FilePath", + "DirectoryPath", + "Json", + "JsonWrapper", + "SecretField", + "SecretStr", + "SecretBytes", + "StrictBool", + "StrictBytes", + "StrictInt", + "StrictFloat", + "PaymentCardNumber", + "PrivateAttr", + "ByteSize", + "PastDate", + "FutureDate", + # version + "compiled", + "VERSION", +] diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/validators.py new file mode 100644 index 0000000..549a235 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/validators.py @@ -0,0 +1,765 @@ +import math +import re +from collections import OrderedDict, deque +from collections.abc import Hashable as CollectionsHashable +from datetime import date, datetime, time, timedelta +from decimal import Decimal, DecimalException +from enum import Enum, IntEnum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Deque, + Dict, + ForwardRef, + FrozenSet, + Generator, + Hashable, + List, + NamedTuple, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, +) +from uuid import UUID + +from . import errors +from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time +from .typing import ( + AnyCallable, + all_literal_values, + display_as_type, + get_class, + is_callable_type, + is_literal_type, + is_namedtuple, + is_none_type, + is_typeddict, +) +from .utils import almost_equal_floats, lenient_issubclass, sequence_like + +if TYPE_CHECKING: + from typing_extensions import Literal, TypedDict + + from .config import BaseConfig + from .fields import ModelField + from .types import ConstrainedDecimal, ConstrainedFloat, ConstrainedInt + + ConstrainedNumber = Union[ConstrainedDecimal, ConstrainedFloat, ConstrainedInt] + AnyOrderedDict = OrderedDict[Any, Any] + Number = Union[int, float, Decimal] + StrBytes = Union[str, bytes] + + +def str_validator(v: Any) -> Union[str]: + if isinstance(v, str): + if isinstance(v, Enum): + return v.value + else: + return v + elif isinstance(v, (float, int, Decimal)): + # is there anything else we want to add here? If you think so, create an issue. + return str(v) + elif isinstance(v, (bytes, bytearray)): + return v.decode() + else: + raise errors.StrError() + + +def strict_str_validator(v: Any) -> Union[str]: + if isinstance(v, str) and not isinstance(v, Enum): + return v + raise errors.StrError() + + +def bytes_validator(v: Any) -> Union[bytes]: + if isinstance(v, bytes): + return v + elif isinstance(v, bytearray): + return bytes(v) + elif isinstance(v, str): + return v.encode() + elif isinstance(v, (float, int, Decimal)): + return str(v).encode() + else: + raise errors.BytesError() + + +def strict_bytes_validator(v: Any) -> Union[bytes]: + if isinstance(v, bytes): + return v + elif isinstance(v, bytearray): + return bytes(v) + else: + raise errors.BytesError() + + +BOOL_FALSE = {0, '0', 'off', 'f', 'false', 'n', 'no'} +BOOL_TRUE = {1, '1', 'on', 't', 'true', 'y', 'yes'} + + +def bool_validator(v: Any) -> bool: + if v is True or v is False: + return v + if isinstance(v, bytes): + v = v.decode() + if isinstance(v, str): + v = v.lower() + try: + if v in BOOL_TRUE: + return True + if v in BOOL_FALSE: + return False + except TypeError: + raise errors.BoolError() + raise errors.BoolError() + + +# matches the default limit cpython, see https://github.com/python/cpython/pull/96500 +max_str_int = 4_300 + + +def int_validator(v: Any) -> int: + if isinstance(v, int) and not (v is True or v is False): + return v + + # see https://github.com/pydantic/pydantic/issues/1477 and in turn, https://github.com/python/cpython/issues/95778 + # this check should be unnecessary once patch releases are out for 3.7, 3.8, 3.9 and 3.10 + # but better to check here until then. + # NOTICE: this does not fully protect user from the DOS risk since the standard library JSON implementation + # (and other std lib modules like xml) use `int()` and are likely called before this, the best workaround is to + # 1. update to the latest patch release of python once released, 2. use a different JSON library like ujson + if isinstance(v, (str, bytes, bytearray)) and len(v) > max_str_int: + raise errors.IntegerError() + + try: + return int(v) + except (TypeError, ValueError, OverflowError): + raise errors.IntegerError() + + +def strict_int_validator(v: Any) -> int: + if isinstance(v, int) and not (v is True or v is False): + return v + raise errors.IntegerError() + + +def float_validator(v: Any) -> float: + if isinstance(v, float): + return v + + try: + return float(v) + except (TypeError, ValueError): + raise errors.FloatError() + + +def strict_float_validator(v: Any) -> float: + if isinstance(v, float): + return v + raise errors.FloatError() + + +def float_finite_validator(v: 'Number', field: 'ModelField', config: 'BaseConfig') -> 'Number': + allow_inf_nan = getattr(field.type_, 'allow_inf_nan', None) + if allow_inf_nan is None: + allow_inf_nan = config.allow_inf_nan + + if allow_inf_nan is False and (math.isnan(v) or math.isinf(v)): + raise errors.NumberNotFiniteError() + return v + + +def number_multiple_validator(v: 'Number', field: 'ModelField') -> 'Number': + field_type: ConstrainedNumber = field.type_ + if field_type.multiple_of is not None: + mod = float(v) / float(field_type.multiple_of) % 1 + if not almost_equal_floats(mod, 0.0) and not almost_equal_floats(mod, 1.0): + raise errors.NumberNotMultipleError(multiple_of=field_type.multiple_of) + return v + + +def number_size_validator(v: 'Number', field: 'ModelField') -> 'Number': + field_type: ConstrainedNumber = field.type_ + if field_type.gt is not None and not v > field_type.gt: + raise errors.NumberNotGtError(limit_value=field_type.gt) + elif field_type.ge is not None and not v >= field_type.ge: + raise errors.NumberNotGeError(limit_value=field_type.ge) + + if field_type.lt is not None and not v < field_type.lt: + raise errors.NumberNotLtError(limit_value=field_type.lt) + if field_type.le is not None and not v <= field_type.le: + raise errors.NumberNotLeError(limit_value=field_type.le) + + return v + + +def constant_validator(v: 'Any', field: 'ModelField') -> 'Any': + """Validate ``const`` fields. + + The value provided for a ``const`` field must be equal to the default value + of the field. This is to support the keyword of the same name in JSON + Schema. + """ + if v != field.default: + raise errors.WrongConstantError(given=v, permitted=[field.default]) + + return v + + +def anystr_length_validator(v: 'StrBytes', config: 'BaseConfig') -> 'StrBytes': + v_len = len(v) + + min_length = config.min_anystr_length + if v_len < min_length: + raise errors.AnyStrMinLengthError(limit_value=min_length) + + max_length = config.max_anystr_length + if max_length is not None and v_len > max_length: + raise errors.AnyStrMaxLengthError(limit_value=max_length) + + return v + + +def anystr_strip_whitespace(v: 'StrBytes') -> 'StrBytes': + return v.strip() + + +def anystr_upper(v: 'StrBytes') -> 'StrBytes': + return v.upper() + + +def anystr_lower(v: 'StrBytes') -> 'StrBytes': + return v.lower() + + +def ordered_dict_validator(v: Any) -> 'AnyOrderedDict': + if isinstance(v, OrderedDict): + return v + + try: + return OrderedDict(v) + except (TypeError, ValueError): + raise errors.DictError() + + +def dict_validator(v: Any) -> Dict[Any, Any]: + if isinstance(v, dict): + return v + + try: + return dict(v) + except (TypeError, ValueError): + raise errors.DictError() + + +def list_validator(v: Any) -> List[Any]: + if isinstance(v, list): + return v + elif sequence_like(v): + return list(v) + else: + raise errors.ListError() + + +def tuple_validator(v: Any) -> Tuple[Any, ...]: + if isinstance(v, tuple): + return v + elif sequence_like(v): + return tuple(v) + else: + raise errors.TupleError() + + +def set_validator(v: Any) -> Set[Any]: + if isinstance(v, set): + return v + elif sequence_like(v): + return set(v) + else: + raise errors.SetError() + + +def frozenset_validator(v: Any) -> FrozenSet[Any]: + if isinstance(v, frozenset): + return v + elif sequence_like(v): + return frozenset(v) + else: + raise errors.FrozenSetError() + + +def deque_validator(v: Any) -> Deque[Any]: + if isinstance(v, deque): + return v + elif sequence_like(v): + return deque(v) + else: + raise errors.DequeError() + + +def enum_member_validator(v: Any, field: 'ModelField', config: 'BaseConfig') -> Enum: + try: + enum_v = field.type_(v) + except ValueError: + # field.type_ should be an enum, so will be iterable + raise errors.EnumMemberError(enum_values=list(field.type_)) + return enum_v.value if config.use_enum_values else enum_v + + +def uuid_validator(v: Any, field: 'ModelField') -> UUID: + try: + if isinstance(v, str): + v = UUID(v) + elif isinstance(v, (bytes, bytearray)): + try: + v = UUID(v.decode()) + except ValueError: + # 16 bytes in big-endian order as the bytes argument fail + # the above check + v = UUID(bytes=v) + except ValueError: + raise errors.UUIDError() + + if not isinstance(v, UUID): + raise errors.UUIDError() + + required_version = getattr(field.type_, '_required_version', None) + if required_version and v.version != required_version: + raise errors.UUIDVersionError(required_version=required_version) + + return v + + +def decimal_validator(v: Any) -> Decimal: + if isinstance(v, Decimal): + return v + elif isinstance(v, (bytes, bytearray)): + v = v.decode() + + v = str(v).strip() + + try: + v = Decimal(v) + except DecimalException: + raise errors.DecimalError() + + if not v.is_finite(): + raise errors.DecimalIsNotFiniteError() + + return v + + +def hashable_validator(v: Any) -> Hashable: + if isinstance(v, Hashable): + return v + + raise errors.HashableError() + + +def ip_v4_address_validator(v: Any) -> IPv4Address: + if isinstance(v, IPv4Address): + return v + + try: + return IPv4Address(v) + except ValueError: + raise errors.IPv4AddressError() + + +def ip_v6_address_validator(v: Any) -> IPv6Address: + if isinstance(v, IPv6Address): + return v + + try: + return IPv6Address(v) + except ValueError: + raise errors.IPv6AddressError() + + +def ip_v4_network_validator(v: Any) -> IPv4Network: + """ + Assume IPv4Network initialised with a default ``strict`` argument + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network + """ + if isinstance(v, IPv4Network): + return v + + try: + return IPv4Network(v) + except ValueError: + raise errors.IPv4NetworkError() + + +def ip_v6_network_validator(v: Any) -> IPv6Network: + """ + Assume IPv6Network initialised with a default ``strict`` argument + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network + """ + if isinstance(v, IPv6Network): + return v + + try: + return IPv6Network(v) + except ValueError: + raise errors.IPv6NetworkError() + + +def ip_v4_interface_validator(v: Any) -> IPv4Interface: + if isinstance(v, IPv4Interface): + return v + + try: + return IPv4Interface(v) + except ValueError: + raise errors.IPv4InterfaceError() + + +def ip_v6_interface_validator(v: Any) -> IPv6Interface: + if isinstance(v, IPv6Interface): + return v + + try: + return IPv6Interface(v) + except ValueError: + raise errors.IPv6InterfaceError() + + +def path_validator(v: Any) -> Path: + if isinstance(v, Path): + return v + + try: + return Path(v) + except TypeError: + raise errors.PathError() + + +def path_exists_validator(v: Any) -> Path: + if not v.exists(): + raise errors.PathNotExistsError(path=v) + + return v + + +def callable_validator(v: Any) -> AnyCallable: + """ + Perform a simple check if the value is callable. + + Note: complete matching of argument type hints and return types is not performed + """ + if callable(v): + return v + + raise errors.CallableError(value=v) + + +def enum_validator(v: Any) -> Enum: + if isinstance(v, Enum): + return v + + raise errors.EnumError(value=v) + + +def int_enum_validator(v: Any) -> IntEnum: + if isinstance(v, IntEnum): + return v + + raise errors.IntEnumError(value=v) + + +def make_literal_validator(type_: Any) -> Callable[[Any], Any]: + permitted_choices = all_literal_values(type_) + + # To have a O(1) complexity and still return one of the values set inside the `Literal`, + # we create a dict with the set values (a set causes some problems with the way intersection works). + # In some cases the set value and checked value can indeed be different (see `test_literal_validator_str_enum`) + allowed_choices = {v: v for v in permitted_choices} + + def literal_validator(v: Any) -> Any: + try: + return allowed_choices[v] + except (KeyError, TypeError): + raise errors.WrongConstantError(given=v, permitted=permitted_choices) + + return literal_validator + + +def constr_length_validator(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + v_len = len(v) + + min_length = field.type_.min_length if field.type_.min_length is not None else config.min_anystr_length + if v_len < min_length: + raise errors.AnyStrMinLengthError(limit_value=min_length) + + max_length = field.type_.max_length if field.type_.max_length is not None else config.max_anystr_length + if max_length is not None and v_len > max_length: + raise errors.AnyStrMaxLengthError(limit_value=max_length) + + return v + + +def constr_strip_whitespace(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + strip_whitespace = field.type_.strip_whitespace or config.anystr_strip_whitespace + if strip_whitespace: + v = v.strip() + + return v + + +def constr_upper(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + upper = field.type_.to_upper or config.anystr_upper + if upper: + v = v.upper() + + return v + + +def constr_lower(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + lower = field.type_.to_lower or config.anystr_lower + if lower: + v = v.lower() + return v + + +def validate_json(v: Any, config: 'BaseConfig') -> Any: + if v is None: + # pass None through to other validators + return v + try: + return config.json_loads(v) # type: ignore + except ValueError: + raise errors.JsonError() + except TypeError: + raise errors.JsonTypeError() + + +T = TypeVar('T') + + +def make_arbitrary_type_validator(type_: Type[T]) -> Callable[[T], T]: + def arbitrary_type_validator(v: Any) -> T: + if isinstance(v, type_): + return v + raise errors.ArbitraryTypeError(expected_arbitrary_type=type_) + + return arbitrary_type_validator + + +def make_class_validator(type_: Type[T]) -> Callable[[Any], Type[T]]: + def class_validator(v: Any) -> Type[T]: + if lenient_issubclass(v, type_): + return v + raise errors.SubclassError(expected_class=type_) + + return class_validator + + +def any_class_validator(v: Any) -> Type[T]: + if isinstance(v, type): + return v + raise errors.ClassError() + + +def none_validator(v: Any) -> 'Literal[None]': + if v is None: + return v + raise errors.NotNoneError() + + +def pattern_validator(v: Any) -> Pattern[str]: + if isinstance(v, Pattern): + return v + + str_value = str_validator(v) + + try: + return re.compile(str_value) + except re.error: + raise errors.PatternError() + + +NamedTupleT = TypeVar('NamedTupleT', bound=NamedTuple) + + +def make_namedtuple_validator( + namedtuple_cls: Type[NamedTupleT], config: Type['BaseConfig'] +) -> Callable[[Tuple[Any, ...]], NamedTupleT]: + from .annotated_types import create_model_from_namedtuple + + NamedTupleModel = create_model_from_namedtuple( + namedtuple_cls, + __config__=config, + __module__=namedtuple_cls.__module__, + ) + namedtuple_cls.__pydantic_model__ = NamedTupleModel # type: ignore[attr-defined] + + def namedtuple_validator(values: Tuple[Any, ...]) -> NamedTupleT: + annotations = NamedTupleModel.__annotations__ + + if len(values) > len(annotations): + raise errors.ListMaxLengthError(limit_value=len(annotations)) + + dict_values: Dict[str, Any] = dict(zip(annotations, values)) + validated_dict_values: Dict[str, Any] = dict(NamedTupleModel(**dict_values)) + return namedtuple_cls(**validated_dict_values) + + return namedtuple_validator + + +def make_typeddict_validator( + typeddict_cls: Type['TypedDict'], config: Type['BaseConfig'] # type: ignore[valid-type] +) -> Callable[[Any], Dict[str, Any]]: + from .annotated_types import create_model_from_typeddict + + TypedDictModel = create_model_from_typeddict( + typeddict_cls, + __config__=config, + __module__=typeddict_cls.__module__, + ) + typeddict_cls.__pydantic_model__ = TypedDictModel # type: ignore[attr-defined] + + def typeddict_validator(values: 'TypedDict') -> Dict[str, Any]: # type: ignore[valid-type] + return TypedDictModel.parse_obj(values).dict(exclude_unset=True) + + return typeddict_validator + + +class IfConfig: + def __init__(self, validator: AnyCallable, *config_attr_names: str, ignored_value: Any = False) -> None: + self.validator = validator + self.config_attr_names = config_attr_names + self.ignored_value = ignored_value + + def check(self, config: Type['BaseConfig']) -> bool: + return any(getattr(config, name) not in {None, self.ignored_value} for name in self.config_attr_names) + + +# order is important here, for example: bool is a subclass of int so has to come first, datetime before date same, +# IPv4Interface before IPv4Address, etc +_VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [ + (IntEnum, [int_validator, enum_member_validator]), + (Enum, [enum_member_validator]), + ( + str, + [ + str_validator, + IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'), + IfConfig(anystr_upper, 'anystr_upper'), + IfConfig(anystr_lower, 'anystr_lower'), + IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'), + ], + ), + ( + bytes, + [ + bytes_validator, + IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'), + IfConfig(anystr_upper, 'anystr_upper'), + IfConfig(anystr_lower, 'anystr_lower'), + IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'), + ], + ), + (bool, [bool_validator]), + (int, [int_validator]), + (float, [float_validator, IfConfig(float_finite_validator, 'allow_inf_nan', ignored_value=True)]), + (Path, [path_validator]), + (datetime, [parse_datetime]), + (date, [parse_date]), + (time, [parse_time]), + (timedelta, [parse_duration]), + (OrderedDict, [ordered_dict_validator]), + (dict, [dict_validator]), + (list, [list_validator]), + (tuple, [tuple_validator]), + (set, [set_validator]), + (frozenset, [frozenset_validator]), + (deque, [deque_validator]), + (UUID, [uuid_validator]), + (Decimal, [decimal_validator]), + (IPv4Interface, [ip_v4_interface_validator]), + (IPv6Interface, [ip_v6_interface_validator]), + (IPv4Address, [ip_v4_address_validator]), + (IPv6Address, [ip_v6_address_validator]), + (IPv4Network, [ip_v4_network_validator]), + (IPv6Network, [ip_v6_network_validator]), +] + + +def find_validators( # noqa: C901 (ignore complexity) + type_: Type[Any], config: Type['BaseConfig'] +) -> Generator[AnyCallable, None, None]: + from .dataclasses import is_builtin_dataclass, make_dataclass_validator + + if type_ is Any or type_ is object: + return + type_type = type_.__class__ + if type_type == ForwardRef or type_type == TypeVar: + return + + if is_none_type(type_): + yield none_validator + return + if type_ is Pattern or type_ is re.Pattern: + yield pattern_validator + return + if type_ is Hashable or type_ is CollectionsHashable: + yield hashable_validator + return + if is_callable_type(type_): + yield callable_validator + return + if is_literal_type(type_): + yield make_literal_validator(type_) + return + if is_builtin_dataclass(type_): + yield from make_dataclass_validator(type_, config) + return + if type_ is Enum: + yield enum_validator + return + if type_ is IntEnum: + yield int_enum_validator + return + if is_namedtuple(type_): + yield tuple_validator + yield make_namedtuple_validator(type_, config) + return + if is_typeddict(type_): + yield make_typeddict_validator(type_, config) + return + + class_ = get_class(type_) + if class_ is not None: + if class_ is not Any and isinstance(class_, type): + yield make_class_validator(class_) + else: + yield any_class_validator + return + + for val_type, validators in _VALIDATORS: + try: + if issubclass(type_, val_type): + for v in validators: + if isinstance(v, IfConfig): + if v.check(config): + yield v.validator + else: + yield v + return + except TypeError: + raise RuntimeError(f'error checking inheritance of {type_!r} (type: {display_as_type(type_)})') + + if config.arbitrary_types_allowed: + yield make_arbitrary_type_validator(type_) + else: + raise RuntimeError(f'no validator found for {type_}, see `arbitrary_types_allowed` in Config') diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/version.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/version.py new file mode 100644 index 0000000..b1c5547 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/v1/version.py @@ -0,0 +1,38 @@ +__all__ = 'compiled', 'VERSION', 'version_info' + +VERSION = '1.10.15' + +try: + import cython # type: ignore +except ImportError: + compiled: bool = False +else: # pragma: no cover + try: + compiled = cython.compiled + except AttributeError: + compiled = False + + +def version_info() -> str: + import platform + import sys + from importlib import import_module + from pathlib import Path + + optional_deps = [] + for p in ('devtools', 'dotenv', 'email-validator', 'typing-extensions'): + try: + import_module(p.replace('-', '_')) + except ImportError: + continue + optional_deps.append(p) + + info = { + 'pydantic version': VERSION, + 'pydantic compiled': compiled, + 'install path': Path(__file__).resolve().parent, + 'python version': sys.version, + 'platform': platform.platform(), + 'optional deps. installed': optional_deps, + } + return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items()) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/validate_call_decorator.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/validate_call_decorator.py new file mode 100644 index 0000000..5b82e19 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/validate_call_decorator.py @@ -0,0 +1,68 @@ +"""Decorator for validating function calls.""" +from __future__ import annotations as _annotations + +import functools +from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload + +from ._internal import _validate_call + +__all__ = ('validate_call',) + +if TYPE_CHECKING: + from .config import ConfigDict + + AnyCallableT = TypeVar('AnyCallableT', bound=Callable[..., Any]) + + +@overload +def validate_call( + *, config: ConfigDict | None = None, validate_return: bool = False +) -> Callable[[AnyCallableT], AnyCallableT]: + ... + + +@overload +def validate_call(func: AnyCallableT, /) -> AnyCallableT: + ... + + +def validate_call( + func: AnyCallableT | None = None, + /, + *, + config: ConfigDict | None = None, + validate_return: bool = False, +) -> AnyCallableT | Callable[[AnyCallableT], AnyCallableT]: + """Usage docs: https://docs.pydantic.dev/2.7/concepts/validation_decorator/ + + Returns a decorated wrapper around the function that validates the arguments and, optionally, the return value. + + Usage may be either as a plain decorator `@validate_call` or with arguments `@validate_call(...)`. + + Args: + func: The function to be decorated. + config: The configuration dictionary. + validate_return: Whether to validate the return value. + + Returns: + The decorated function. + """ + + def validate(function: AnyCallableT) -> AnyCallableT: + if isinstance(function, (classmethod, staticmethod)): + name = type(function).__name__ + raise TypeError(f'The `@{name}` decorator should be applied after `@validate_call` (put `@{name}` on top)') + validate_call_wrapper = _validate_call.ValidateCallWrapper(function, config, validate_return) + + @functools.wraps(function) + def wrapper_function(*args, **kwargs): + return validate_call_wrapper(*args, **kwargs) + + wrapper_function.raw_function = function # type: ignore + + return wrapper_function # type: ignore + + if func: + return validate(func) + else: + return validate diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/validators.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/validators.py new file mode 100644 index 0000000..55b0339 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/validators.py @@ -0,0 +1,4 @@ +"""The `validators` module is a backport module from V1.""" +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/version.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/version.py new file mode 100644 index 0000000..b7b87a2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/version.py @@ -0,0 +1,77 @@ +"""The `version` module holds the version information for Pydantic.""" +from __future__ import annotations as _annotations + +__all__ = 'VERSION', 'version_info' + +VERSION = '2.7.1' +"""The version of Pydantic.""" + + +def version_short() -> str: + """Return the `major.minor` part of Pydantic version. + + It returns '2.1' if Pydantic version is '2.1.1'. + """ + return '.'.join(VERSION.split('.')[:2]) + + +def version_info() -> str: + """Return complete version information for Pydantic and its dependencies.""" + import importlib.metadata as importlib_metadata + import os + import platform + import sys + from pathlib import Path + + import pydantic_core._pydantic_core as pdc + + from ._internal import _git as git + + # get data about packages that are closely related to pydantic, use pydantic or often conflict with pydantic + package_names = { + 'email-validator', + 'fastapi', + 'mypy', + 'pydantic-extra-types', + 'pydantic-settings', + 'pyright', + 'typing_extensions', + } + related_packages = [] + + for dist in importlib_metadata.distributions(): + name = dist.metadata['Name'] + if name in package_names: + related_packages.append(f'{name}-{dist.version}') + + pydantic_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + most_recent_commit = ( + git.git_revision(pydantic_dir) if git.is_git_repo(pydantic_dir) and git.have_git() else 'unknown' + ) + + info = { + 'pydantic version': VERSION, + 'pydantic-core version': pdc.__version__, + 'pydantic-core build': getattr(pdc, 'build_info', None) or pdc.build_profile, + 'install path': Path(__file__).resolve().parent, + 'python version': sys.version, + 'platform': platform.platform(), + 'related packages': ' '.join(related_packages), + 'commit': most_recent_commit, + } + return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items()) + + +def parse_mypy_version(version: str) -> tuple[int, ...]: + """Parse mypy string version to tuple of ints. + + It parses normal version like `0.930` and extra info followed by a `+` sign + like `0.940+dev.04cac4b5d911c4f9529e6ce86a27b44f28846f5d.dirty`. + + Args: + version: The mypy version string. + + Returns: + A tuple of ints. e.g. (0, 930). + """ + return tuple(map(int, version.partition('+')[0].split('.'))) diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic/warnings.py b/.pythonlibs/lib/python3.10/site-packages/pydantic/warnings.py new file mode 100644 index 0000000..aedd4fb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic/warnings.py @@ -0,0 +1,58 @@ +"""Pydantic-specific warnings.""" +from __future__ import annotations as _annotations + +from .version import version_short + +__all__ = 'PydanticDeprecatedSince20', 'PydanticDeprecationWarning' + + +class PydanticDeprecationWarning(DeprecationWarning): + """A Pydantic specific deprecation warning. + + This warning is raised when using deprecated functionality in Pydantic. It provides information on when the + deprecation was introduced and the expected version in which the corresponding functionality will be removed. + + Attributes: + message: Description of the warning. + since: Pydantic version in what the deprecation was introduced. + expected_removal: Pydantic version in what the corresponding functionality expected to be removed. + """ + + message: str + since: tuple[int, int] + expected_removal: tuple[int, int] + + def __init__( + self, message: str, *args: object, since: tuple[int, int], expected_removal: tuple[int, int] | None = None + ) -> None: + super().__init__(message, *args) + self.message = message.rstrip('.') + self.since = since + self.expected_removal = expected_removal if expected_removal is not None else (since[0] + 1, 0) + + def __str__(self) -> str: + message = ( + f'{self.message}. Deprecated in Pydantic V{self.since[0]}.{self.since[1]}' + f' to be removed in V{self.expected_removal[0]}.{self.expected_removal[1]}.' + ) + if self.since == (2, 0): + message += f' See Pydantic V2 Migration Guide at https://errors.pydantic.dev/{version_short()}/migration/' + return message + + +class PydanticDeprecatedSince20(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.0.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 0), expected_removal=(3, 0)) + + +class PydanticDeprecatedSince26(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.6.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 0), expected_removal=(3, 0)) + + +class GenericBeforeBaseModelWarning(Warning): + pass diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/METADATA new file mode 100644 index 0000000..cc725e8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/METADATA @@ -0,0 +1,160 @@ +Metadata-Version: 2.3 +Name: pydantic_core +Version: 2.18.2 +Classifier: Development Status :: 3 - Alpha +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Rust +Classifier: Framework :: Pydantic +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS +Classifier: Typing :: Typed +Requires-Dist: typing-extensions >=4.6.0, !=4.7.0 +License-File: LICENSE +Summary: Core functionality for Pydantic validation and serialization +Home-Page: https://github.com/pydantic/pydantic-core +Author-email: Samuel Colvin +License: MIT +Requires-Python: >=3.8 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM +Project-URL: Homepage, https://github.com/pydantic/pydantic-core +Project-URL: Funding, https://github.com/sponsors/samuelcolvin +Project-URL: Source, https://github.com/pydantic/pydantic-core + +# pydantic-core + +[![CI](https://github.com/pydantic/pydantic-core/workflows/ci/badge.svg?event=push)](https://github.com/pydantic/pydantic-core/actions?query=event%3Apush+branch%3Amain+workflow%3Aci) +[![Coverage](https://codecov.io/gh/pydantic/pydantic-core/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/pydantic-core) +[![pypi](https://img.shields.io/pypi/v/pydantic-core.svg)](https://pypi.python.org/pypi/pydantic-core) +[![versions](https://img.shields.io/pypi/pyversions/pydantic-core.svg)](https://github.com/pydantic/pydantic-core) +[![license](https://img.shields.io/github/license/pydantic/pydantic-core.svg)](https://github.com/pydantic/pydantic-core/blob/main/LICENSE) + +This package provides the core functionality for [pydantic](https://docs.pydantic.dev) validation and serialization. + +Pydantic-core is currently around 17x faster than pydantic V1. +See [`tests/benchmarks/`](./tests/benchmarks/) for details. + +## Example of direct usage + +_NOTE: You should not need to use pydantic-core directly; instead, use pydantic, which in turn uses pydantic-core._ + +```py +from pydantic_core import SchemaValidator, ValidationError + + +v = SchemaValidator( + { + 'type': 'typed-dict', + 'fields': { + 'name': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'str', + }, + }, + 'age': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'int', + 'ge': 18, + }, + }, + 'is_developer': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'default', + 'schema': {'type': 'bool'}, + 'default': True, + }, + }, + }, + } +) + +r1 = v.validate_python({'name': 'Samuel', 'age': 35}) +assert r1 == {'name': 'Samuel', 'age': 35, 'is_developer': True} + +# pydantic-core can also validate JSON directly +r2 = v.validate_json('{"name": "Samuel", "age": 35}') +assert r1 == r2 + +try: + v.validate_python({'name': 'Samuel', 'age': 11}) +except ValidationError as e: + print(e) + """ + 1 validation error for model + age + Input should be greater than or equal to 18 + [type=greater_than_equal, context={ge: 18}, input_value=11, input_type=int] + """ +``` + +## Getting Started + +You'll need rust stable [installed](https://rustup.rs/), or rust nightly if you want to generate accurate coverage. + +With rust and python 3.8+ installed, compiling pydantic-core should be possible with roughly the following: + +```bash +# clone this repo or your fork +git clone git@github.com:pydantic/pydantic-core.git +cd pydantic-core +# create a new virtual env +python3 -m venv env +source env/bin/activate +# install dependencies and install pydantic-core +make install +``` + +That should be it, the example shown above should now run. + +You might find it useful to look at [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) and +[`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) for more information on the python API, +beyond that, [`tests/`](./tests) provide a large number of examples of usage. + +If you want to contribute to pydantic-core, you'll want to use some other make commands: +* `make build-dev` to build the package during development +* `make build-prod` to perform an optimised build for benchmarking +* `make test` to run the tests +* `make testcov` to run the tests and generate a coverage report +* `make lint` to run the linter +* `make format` to format python and rust code +* `make` to run `format build-dev lint test` + +## Profiling + +It's possible to profile the code using the [`flamegraph` utility from `flamegraph-rs`](https://github.com/flamegraph-rs/flamegraph). (Tested on Linux.) You can install this with `cargo install flamegraph`. + +Run `make build-profiling` to install a release build with debugging symbols included (needed for profiling). + +Once that is built, you can profile pytest benchmarks with (e.g.): + +```bash +flamegraph -- pytest tests/benchmarks/test_micro_benchmarks.py -k test_list_of_ints_core_py --benchmark-enable +``` +The `flamegraph` command will produce an interactive SVG at `flamegraph.svg`. + +## Releasing + +1. Bump package version locally. Do not just edit `Cargo.toml` on Github, you need both `Cargo.toml` and `Cargo.lock` to be updated. +2. Make a PR for the version bump and merge it. +3. Go to https://github.com/pydantic/pydantic-core/releases and click "Draft a new release" +4. In the "Choose a tag" dropdown enter the new tag `v` and select "Create new tag on publish" when the option appears. +5. Enter the release title in the form "v " +6. Click Generate release notes button +7. Click Publish release +8. Go to https://github.com/pydantic/pydantic-core/actions and ensure that all build for release are done successfully. +9. Go to https://pypi.org/project/pydantic-core/ and ensure that the latest release is published. +10. Done 🎉 + diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/RECORD new file mode 100644 index 0000000..30a58e7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/RECORD @@ -0,0 +1,12 @@ +pydantic_core-2.18.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pydantic_core-2.18.2.dist-info/METADATA,sha256=wVXxopnMYbbll_eqxvSpj614NnRY-07ViGh8InwnF54,6534 +pydantic_core-2.18.2.dist-info/RECORD,, +pydantic_core-2.18.2.dist-info/WHEEL,sha256=JL8sd1C0RQ2f7cmwbAn1Jp257v_vSS2r0VvTBpJeZwA,129 +pydantic_core-2.18.2.dist-info/license_files/LICENSE,sha256=Kv3TDVS01itvSIprzBVG6E7FBh8T9CCcA9ASNIeDeVo,1080 +pydantic_core/__init__.py,sha256=H-OvjcLVZnP4v4DQ6CAXIGqXN_2bK3PIEIv1lcH5HaQ,4197 +pydantic_core/__pycache__/__init__.cpython-310.pyc,, +pydantic_core/__pycache__/core_schema.cpython-310.pyc,, +pydantic_core/_pydantic_core.cpython-310-x86_64-linux-gnu.so,sha256=tqxRCNFNQWUuRIPN-EM1aKasIzWRvcX9V98pTt4aucI,5026584 +pydantic_core/_pydantic_core.pyi,sha256=JAnx595t_KJyl_UZT0Bgu7GFg8CzpIdVrpMUYdwUFGY,35226 +pydantic_core/core_schema.py,sha256=_Jdm8GaUtrBxxsbffEwpwrRfqQLtptWw0c-mNsarcTg,137407 +pydantic_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/WHEEL new file mode 100644 index 0000000..efc3fe1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: maturin (1.5.1) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64 diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/license_files/LICENSE b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/license_files/LICENSE new file mode 100644 index 0000000..0716871 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core-2.18.2.dist-info/license_files/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Samuel Colvin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__init__.py b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__init__.py new file mode 100644 index 0000000..5b2655c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__init__.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +import sys as _sys +from typing import Any as _Any + +from ._pydantic_core import ( + ArgsKwargs, + MultiHostUrl, + PydanticCustomError, + PydanticKnownError, + PydanticOmit, + PydanticSerializationError, + PydanticSerializationUnexpectedValue, + PydanticUndefined, + PydanticUndefinedType, + PydanticUseDefault, + SchemaError, + SchemaSerializer, + SchemaValidator, + Some, + TzInfo, + Url, + ValidationError, + __version__, + from_json, + to_json, + to_jsonable_python, + validate_core_schema, +) +from .core_schema import CoreConfig, CoreSchema, CoreSchemaType, ErrorType + +if _sys.version_info < (3, 11): + from typing_extensions import NotRequired as _NotRequired +else: + from typing import NotRequired as _NotRequired + +if _sys.version_info < (3, 9): + from typing_extensions import TypedDict as _TypedDict +else: + from typing import TypedDict as _TypedDict + +__all__ = [ + '__version__', + 'CoreConfig', + 'CoreSchema', + 'CoreSchemaType', + 'SchemaValidator', + 'SchemaSerializer', + 'Some', + 'Url', + 'MultiHostUrl', + 'ArgsKwargs', + 'PydanticUndefined', + 'PydanticUndefinedType', + 'SchemaError', + 'ErrorDetails', + 'InitErrorDetails', + 'ValidationError', + 'PydanticCustomError', + 'PydanticKnownError', + 'PydanticOmit', + 'PydanticUseDefault', + 'PydanticSerializationError', + 'PydanticSerializationUnexpectedValue', + 'TzInfo', + 'to_json', + 'from_json', + 'to_jsonable_python', + 'validate_core_schema', +] + + +class ErrorDetails(_TypedDict): + type: str + """ + The type of error that occurred, this is an identifier designed for + programmatic use that will change rarely or never. + + `type` is unique for each error message, and can hence be used as an identifier to build custom error messages. + """ + loc: tuple[int | str, ...] + """Tuple of strings and ints identifying where in the schema the error occurred.""" + msg: str + """A human readable error message.""" + input: _Any + """The input data at this `loc` that caused the error.""" + ctx: _NotRequired[dict[str, _Any]] + """ + Values which are required to render the error message, and could hence be useful in rendering custom error messages. + Also useful for passing custom error data forward. + """ + + +class InitErrorDetails(_TypedDict): + type: str | PydanticCustomError + """The type of error that occurred, this should a "slug" identifier that changes rarely or never.""" + loc: _NotRequired[tuple[int | str, ...]] + """Tuple of strings and ints identifying where in the schema the error occurred.""" + input: _Any + """The input data at this `loc` that caused the error.""" + ctx: _NotRequired[dict[str, _Any]] + """ + Values which are required to render the error message, and could hence be useful in rendering custom error messages. + Also useful for passing custom error data forward. + """ + + +class ErrorTypeInfo(_TypedDict): + """ + Gives information about errors. + """ + + type: ErrorType + """The type of error that occurred, this should a "slug" identifier that changes rarely or never.""" + message_template_python: str + """String template to render a human readable error message from using context, when the input is Python.""" + example_message_python: str + """Example of a human readable error message, when the input is Python.""" + message_template_json: _NotRequired[str] + """String template to render a human readable error message from using context, when the input is JSON data.""" + example_message_json: _NotRequired[str] + """Example of a human readable error message, when the input is JSON data.""" + example_context: dict[str, _Any] | None + """Example of context values.""" + + +class MultiHostHost(_TypedDict): + """ + A host part of a multi-host URL. + """ + + username: str | None + """The username part of this host, or `None`.""" + password: str | None + """The password part of this host, or `None`.""" + host: str | None + """The host part of this host, or `None`.""" + port: int | None + """The port part of this host, or `None`.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2f16b19 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/core_schema.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/core_schema.cpython-310.pyc new file mode 100644 index 0000000..a72a627 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/__pycache__/core_schema.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..ecb04cd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.pyi b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.pyi new file mode 100644 index 0000000..b86b7aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/_pydantic_core.pyi @@ -0,0 +1,901 @@ +import datetime +from typing import Any, Callable, Generic, Literal, TypeVar, final + +from _typeshed import SupportsAllComparisons +from typing_extensions import LiteralString, Self, TypeAlias + +from pydantic_core import ErrorDetails, ErrorTypeInfo, InitErrorDetails, MultiHostHost +from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorType + +__all__ = [ + '__version__', + 'build_profile', + 'build_info', + '_recursion_limit', + 'ArgsKwargs', + 'SchemaValidator', + 'SchemaSerializer', + 'Url', + 'MultiHostUrl', + 'SchemaError', + 'ValidationError', + 'PydanticCustomError', + 'PydanticKnownError', + 'PydanticOmit', + 'PydanticUseDefault', + 'PydanticSerializationError', + 'PydanticSerializationUnexpectedValue', + 'PydanticUndefined', + 'PydanticUndefinedType', + 'Some', + 'to_json', + 'from_json', + 'to_jsonable_python', + 'list_all_errors', + 'TzInfo', + 'validate_core_schema', +] +__version__: str +build_profile: str +build_info: str +_recursion_limit: int + +_T = TypeVar('_T', default=Any, covariant=True) + +_StringInput: TypeAlias = 'dict[str, _StringInput]' + +@final +class Some(Generic[_T]): + """ + Similar to Rust's [`Option::Some`](https://doc.rust-lang.org/std/option/enum.Option.html) type, this + identifies a value as being present, and provides a way to access it. + + Generally used in a union with `None` to different between "some value which could be None" and no value. + """ + + __match_args__ = ('value',) + + @property + def value(self) -> _T: + """ + Returns the value wrapped by `Some`. + """ + @classmethod + def __class_getitem__(cls, item: Any, /) -> type[Self]: ... + +@final +class SchemaValidator: + """ + `SchemaValidator` is the Python wrapper for `pydantic-core`'s Rust validation logic, internally it owns one + `CombinedValidator` which may in turn own more `CombinedValidator`s which make up the full schema validator. + """ + + def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None) -> Self: + """ + Create a new SchemaValidator. + + Arguments: + schema: The [`CoreSchema`][pydantic_core.core_schema.CoreSchema] to use for validation. + config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to configure validation. + """ + @property + def title(self) -> str: + """ + The title of the schema, as used in the heading of [`ValidationError.__str__()`][pydantic_core.ValidationError]. + """ + def validate_python( + self, + input: Any, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + self_instance: Any | None = None, + ) -> Any: + """ + Validate a Python object against the schema and return the validated object. + + Arguments: + input: The Python object to validate. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + from_attributes: Whether to validate objects as inputs to models by extracting attributes. + If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + self_instance: An instance of a model set attributes on from validation, this is used when running + validation from the `__init__` method of a model. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated object. + """ + def isinstance_python( + self, + input: Any, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + self_instance: Any | None = None, + ) -> bool: + """ + Similar to [`validate_python()`][pydantic_core.SchemaValidator.validate_python] but returns a boolean. + + Arguments match `validate_python()`. This method will not raise `ValidationError`s but will raise internal + errors. + + Returns: + `True` if validation succeeds, `False` if validation fails. + """ + def validate_json( + self, + input: str | bytes | bytearray, + *, + strict: bool | None = None, + context: dict[str, Any] | None = None, + self_instance: Any | None = None, + ) -> Any: + """ + Validate JSON data directly against the schema and return the validated Python object. + + This method should be significantly faster than `validate_python(json.loads(json_data))` as it avoids the + need to create intermediate Python objects + + It also handles constructing the correct Python type even in strict mode, where + `validate_python(json.loads(json_data))` would fail validation. + + Arguments: + input: The JSON data to validate. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + self_instance: An instance of a model set attributes on from validation. + + Raises: + ValidationError: If validation fails or if the JSON data is invalid. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated Python object. + """ + def validate_strings( + self, input: _StringInput, *, strict: bool | None = None, context: dict[str, Any] | None = None + ) -> Any: + """ + Validate a string against the schema and return the validated Python object. + + This is similar to `validate_json` but applies to scenarios where the input will be a string but not + JSON data, e.g. URL fragments, query parameters, etc. + + Arguments: + input: The input as a string, or bytes/bytearray if `strict=False`. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + + Raises: + ValidationError: If validation fails or if the JSON data is invalid. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated Python object. + """ + def validate_assignment( + self, + obj: Any, + field_name: str, + field_value: Any, + *, + strict: bool | None = None, + from_attributes: bool | None = None, + context: dict[str, Any] | None = None, + ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any] | None, set[str]]: + """ + Validate an assignment to a field on a model. + + Arguments: + obj: The model instance being assigned to. + field_name: The name of the field to validate assignment for. + field_value: The value to assign to the field. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + from_attributes: Whether to validate objects as inputs to models by extracting attributes. + If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + Either the model dict or a tuple of `(model_data, model_extra, fields_set)` + """ + def get_default_value(self, *, strict: bool | None = None, context: Any = None) -> Some | None: + """ + Get the default value for the schema, including running default value validation. + + Arguments: + strict: Whether to validate the default value in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + `None` if the schema has no default value, otherwise a [`Some`][pydantic_core.Some] containing the default. + """ + +_IncEx: TypeAlias = set[int] | set[str] | dict[int, _IncEx] | dict[str, _IncEx] | None + +@final +class SchemaSerializer: + """ + `SchemaSerializer` is the Python wrapper for `pydantic-core`'s Rust serialization logic, internally it owns one + `CombinedSerializer` which may in turn own more `CombinedSerializer`s which make up the full schema serializer. + """ + + def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None) -> Self: + """ + Create a new SchemaSerializer. + + Arguments: + schema: The [`CoreSchema`][pydantic_core.core_schema.CoreSchema] to use for serialization. + config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to to configure serialization. + """ + def to_python( + self, + value: Any, + *, + mode: str | None = None, + include: _IncEx = None, + exclude: _IncEx = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + context: dict[str, Any] | None = None, + ) -> Any: + """ + Serialize/marshal a Python object to a Python object including transforming and filtering data. + + Arguments: + value: The Python object to serialize. + mode: The serialization mode to use, either `'python'` or `'json'`, defaults to `'python'`. In JSON mode, + all values are converted to JSON compatible types, e.g. `None`, `int`, `float`, `str`, `list`, `dict`. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_unset: Whether to exclude fields that are not set, + e.g. are not included in `__pydantic_fields_set__`. + exclude_defaults: Whether to exclude fields that are equal to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + The serialized Python object. + """ + def to_json( + self, + value: Any, + *, + indent: int | None = None, + include: _IncEx = None, + exclude: _IncEx = None, + by_alias: bool = True, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + context: dict[str, Any] | None = None, + ) -> bytes: + """ + Serialize a Python object to JSON including transforming and filtering data. + + Arguments: + value: The Python object to serialize. + indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_unset: Whether to exclude fields that are not set, + e.g. are not included in `__pydantic_fields_set__`. + exclude_defaults: Whether to exclude fields that are equal to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + JSON bytes. + """ + +def to_json( + value: Any, + *, + indent: int | None = None, + include: _IncEx = None, + exclude: _IncEx = None, + by_alias: bool = True, + exclude_none: bool = False, + round_trip: bool = False, + timedelta_mode: Literal['iso8601', 'float'] = 'iso8601', + bytes_mode: Literal['utf8', 'base64'] = 'utf8', + inf_nan_mode: Literal['null', 'constants'] = 'constants', + serialize_unknown: bool = False, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + context: dict[str, Any] | None = None, +) -> bytes: + """ + Serialize a Python object to JSON including transforming and filtering data. + + This is effectively a standalone version of [`SchemaSerializer.to_json`][pydantic_core.SchemaSerializer.to_json]. + + Arguments: + value: The Python object to serialize. + indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`. + bytes_mode: How to serialize `bytes` objects, either `'utf8'` or `'base64'`. + inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'` or `'constants'`. + serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails + `""` will be used. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + JSON bytes. + """ + +def from_json( + data: str | bytes | bytearray, + *, + allow_inf_nan: bool = True, + cache_strings: bool | Literal['all', 'keys', 'none'] = True, + allow_partial: bool = False, +) -> Any: + """ + Deserialize JSON data to a Python object. + + This is effectively a faster version of `json.loads()`, with some extra functionality. + + Arguments: + data: The JSON data to deserialize. + allow_inf_nan: Whether to allow `Infinity`, `-Infinity` and `NaN` values as `json.loads()` does by default. + cache_strings: Whether to cache strings to avoid constructing new Python objects, + this should have a significant impact on performance while increasing memory usage slightly, + `all/True` means cache all strings, `keys` means cache only dict keys, `none/False` means no caching. + allow_partial: Whether to allow partial deserialization, if `True` JSON data is returned if the end of the + input is reached before the full object is deserialized, e.g. `["aa", "bb", "c` would return `['aa', 'bb']`. + + Raises: + ValueError: If deserialization fails. + + Returns: + The deserialized Python object. + """ + +def to_jsonable_python( + value: Any, + *, + include: _IncEx = None, + exclude: _IncEx = None, + by_alias: bool = True, + exclude_none: bool = False, + round_trip: bool = False, + timedelta_mode: Literal['iso8601', 'float'] = 'iso8601', + bytes_mode: Literal['utf8', 'base64'] = 'utf8', + inf_nan_mode: Literal['null', 'constants'] = 'constants', + serialize_unknown: bool = False, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + context: dict[str, Any] | None = None, +) -> Any: + """ + Serialize/marshal a Python object to a JSON-serializable Python object including transforming and filtering data. + + This is effectively a standalone version of + [`SchemaSerializer.to_python(mode='json')`][pydantic_core.SchemaSerializer.to_python]. + + Args: + value: The Python object to serialize. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`. + bytes_mode: How to serialize `bytes` objects, either `'utf8'` or `'base64'`. + inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'` or `'constants'`. + serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails + `""` will be used. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + The serialized Python object. + """ + +class Url(SupportsAllComparisons): + """ + A URL type, internal logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed + by Mozilla. + """ + + def __new__(cls, url: str) -> Self: + """ + Create a new `Url` instance. + + Args: + url: String representation of a URL. + + Returns: + A new `Url` instance. + + Raises: + ValidationError: If the URL is invalid. + """ + @property + def scheme(self) -> str: + """ + The scheme part of the URL. + + e.g. `https` in `https://user:pass@host:port/path?query#fragment` + """ + @property + def username(self) -> str | None: + """ + The username part of the URL, or `None`. + + e.g. `user` in `https://user:pass@host:port/path?query#fragment` + """ + @property + def password(self) -> str | None: + """ + The password part of the URL, or `None`. + + e.g. `pass` in `https://user:pass@host:port/path?query#fragment` + """ + @property + def host(self) -> str | None: + """ + The host part of the URL, or `None`. + + If the URL must be punycode encoded, this is the encoded host, e.g if the input URL is `https://£££.com`, + `host` will be `xn--9aaa.com` + """ + def unicode_host(self) -> str | None: + """ + The host part of the URL as a unicode string, or `None`. + + e.g. `host` in `https://user:pass@host:port/path?query#fragment` + + If the URL must be punycode encoded, this is the decoded host, e.g if the input URL is `https://£££.com`, + `unicode_host()` will be `£££.com` + """ + @property + def port(self) -> int | None: + """ + The port part of the URL, or `None`. + + e.g. `port` in `https://user:pass@host:port/path?query#fragment` + """ + @property + def path(self) -> str | None: + """ + The path part of the URL, or `None`. + + e.g. `/path` in `https://user:pass@host:port/path?query#fragment` + """ + @property + def query(self) -> str | None: + """ + The query part of the URL, or `None`. + + e.g. `query` in `https://user:pass@host:port/path?query#fragment` + """ + def query_params(self) -> list[tuple[str, str]]: + """ + The query part of the URL as a list of key-value pairs. + + e.g. `[('foo', 'bar')]` in `https://user:pass@host:port/path?foo=bar#fragment` + """ + @property + def fragment(self) -> str | None: + """ + The fragment part of the URL, or `None`. + + e.g. `fragment` in `https://user:pass@host:port/path?query#fragment` + """ + def unicode_string(self) -> str: + """ + The URL as a unicode string, unlike `__str__()` this will not punycode encode the host. + + If the URL must be punycode encoded, this is the decoded string, e.g if the input URL is `https://£££.com`, + `unicode_string()` will be `https://£££.com` + """ + def __repr__(self) -> str: ... + def __str__(self) -> str: + """ + The URL as a string, this will punycode encode the host if required. + """ + def __deepcopy__(self, memo: dict) -> str: ... + @classmethod + def build( + cls, + *, + scheme: str, + username: str | None = None, + password: str | None = None, + host: str, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: + """ + Build a new `Url` instance from its component parts. + + Args: + scheme: The scheme part of the URL. + username: The username part of the URL, or omit for no username. + password: The password part of the URL, or omit for no password. + host: The host part of the URL. + port: The port part of the URL, or omit for no port. + path: The path part of the URL, or omit for no path. + query: The query part of the URL, or omit for no query. + fragment: The fragment part of the URL, or omit for no fragment. + + Returns: + An instance of URL + """ + +class MultiHostUrl(SupportsAllComparisons): + """ + A URL type with support for multiple hosts, as used by some databases for DSNs, e.g. `https://foo.com,bar.com/path`. + + Internal URL logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed + by Mozilla. + """ + + def __new__(cls, url: str) -> Self: + """ + Create a new `MultiHostUrl` instance. + + Args: + url: String representation of a URL. + + Returns: + A new `MultiHostUrl` instance. + + Raises: + ValidationError: If the URL is invalid. + """ + @property + def scheme(self) -> str: + """ + The scheme part of the URL. + + e.g. `https` in `https://foo.com,bar.com/path?query#fragment` + """ + @property + def path(self) -> str | None: + """ + The path part of the URL, or `None`. + + e.g. `/path` in `https://foo.com,bar.com/path?query#fragment` + """ + @property + def query(self) -> str | None: + """ + The query part of the URL, or `None`. + + e.g. `query` in `https://foo.com,bar.com/path?query#fragment` + """ + def query_params(self) -> list[tuple[str, str]]: + """ + The query part of the URL as a list of key-value pairs. + + e.g. `[('foo', 'bar')]` in `https://foo.com,bar.com/path?query#fragment` + """ + @property + def fragment(self) -> str | None: + """ + The fragment part of the URL, or `None`. + + e.g. `fragment` in `https://foo.com,bar.com/path?query#fragment` + """ + def hosts(self) -> list[MultiHostHost]: + ''' + + The hosts of the `MultiHostUrl` as [`MultiHostHost`][pydantic_core.MultiHostHost] typed dicts. + + ```py + from pydantic_core import MultiHostUrl + + mhu = MultiHostUrl('https://foo.com:123,foo:bar@bar.com/path') + print(mhu.hosts()) + """ + [ + {'username': None, 'password': None, 'host': 'foo.com', 'port': 123}, + {'username': 'foo', 'password': 'bar', 'host': 'bar.com', 'port': 443} + ] + ``` + Returns: + A list of dicts, each representing a host. + ''' + def unicode_string(self) -> str: + """ + The URL as a unicode string, unlike `__str__()` this will not punycode encode the hosts. + """ + def __repr__(self) -> str: ... + def __str__(self) -> str: + """ + The URL as a string, this will punycode encode the hosts if required. + """ + def __deepcopy__(self, memo: dict) -> Self: ... + @classmethod + def build( + cls, + *, + scheme: str, + hosts: list[MultiHostHost] | None = None, + username: str | None = None, + password: str | None = None, + host: str | None = None, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: + """ + Build a new `MultiHostUrl` instance from its component parts. + + This method takes either `hosts` - a list of `MultiHostHost` typed dicts, or the individual components + `username`, `password`, `host` and `port`. + + Args: + scheme: The scheme part of the URL. + hosts: Multiple hosts to build the URL from. + username: The username part of the URL. + password: The password part of the URL. + host: The host part of the URL. + port: The port part of the URL. + path: The path part of the URL. + query: The query part of the URL, or omit for no query. + fragment: The fragment part of the URL, or omit for no fragment. + + Returns: + An instance of `MultiHostUrl` + """ + +@final +class SchemaError(Exception): + """ + Information about errors that occur while building a [`SchemaValidator`][pydantic_core.SchemaValidator] + or [`SchemaSerializer`][pydantic_core.SchemaSerializer]. + """ + + def error_count(self) -> int: + """ + Returns: + The number of errors in the schema. + """ + def errors(self) -> list[ErrorDetails]: + """ + Returns: + A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the schema. + """ + +@final +class ValidationError(ValueError): + """ + `ValidationError` is the exception raised by `pydantic-core` when validation fails, it contains a list of errors + which detail why validation failed. + """ + + @staticmethod + def from_exception_data( + title: str, + line_errors: list[InitErrorDetails], + input_type: Literal['python', 'json'] = 'python', + hide_input: bool = False, + ) -> ValidationError: + """ + Python constructor for a Validation Error. + + The API for constructing validation errors will probably change in the future, + hence the static method rather than `__init__`. + + Arguments: + title: The title of the error, as used in the heading of `str(validation_error)` + line_errors: A list of [`InitErrorDetails`][pydantic_core.InitErrorDetails] which contain information + about errors that occurred during validation. + input_type: Whether the error is for a Python object or JSON. + hide_input: Whether to hide the input value in the error message. + """ + @property + def title(self) -> str: + """ + The title of the error, as used in the heading of `str(validation_error)`. + """ + def error_count(self) -> int: + """ + Returns: + The number of errors in the validation error. + """ + def errors( + self, *, include_url: bool = True, include_context: bool = True, include_input: bool = True + ) -> list[ErrorDetails]: + """ + Details about each error in the validation error. + + Args: + include_url: Whether to include a URL to documentation on the error each error. + include_context: Whether to include the context of each error. + include_input: Whether to include the input value of each error. + + Returns: + A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the validation error. + """ + def json( + self, + *, + indent: int | None = None, + include_url: bool = True, + include_context: bool = True, + include_input: bool = True, + ) -> str: + """ + Same as [`errors()`][pydantic_core.ValidationError.errors] but returns a JSON string. + + Args: + indent: The number of spaces to indent the JSON by, or `None` for no indentation - compact JSON. + include_url: Whether to include a URL to documentation on the error each error. + include_context: Whether to include the context of each error. + include_input: Whether to include the input value of each error. + + Returns: + a JSON string. + """ + + def __repr__(self) -> str: + """ + A string representation of the validation error. + + Whether or not documentation URLs are included in the repr is controlled by the + environment variable `PYDANTIC_ERRORS_INCLUDE_URL` being set to `1` or + `true`; by default, URLs are shown. + + Due to implementation details, this environment variable can only be set once, + before the first validation error is created. + """ + +@final +class PydanticCustomError(ValueError): + def __new__( + cls, error_type: LiteralString, message_template: LiteralString, context: dict[str, Any] | None = None + ) -> Self: ... + @property + def context(self) -> dict[str, Any] | None: ... + @property + def type(self) -> str: ... + @property + def message_template(self) -> str: ... + def message(self) -> str: ... + +@final +class PydanticKnownError(ValueError): + def __new__(cls, error_type: ErrorType, context: dict[str, Any] | None = None) -> Self: ... + @property + def context(self) -> dict[str, Any] | None: ... + @property + def type(self) -> ErrorType: ... + @property + def message_template(self) -> str: ... + def message(self) -> str: ... + +@final +class PydanticOmit(Exception): + def __new__(cls) -> Self: ... + +@final +class PydanticUseDefault(Exception): + def __new__(cls) -> Self: ... + +@final +class PydanticSerializationError(ValueError): + def __new__(cls, message: str) -> Self: ... + +@final +class PydanticSerializationUnexpectedValue(ValueError): + def __new__(cls, message: str | None = None) -> Self: ... + +@final +class ArgsKwargs: + def __new__(cls, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> Self: ... + @property + def args(self) -> tuple[Any, ...]: ... + @property + def kwargs(self) -> dict[str, Any] | None: ... + +@final +class PydanticUndefinedType: + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any) -> Self: ... + +PydanticUndefined: PydanticUndefinedType + +def list_all_errors() -> list[ErrorTypeInfo]: + """ + Get information about all built-in errors. + + Returns: + A list of `ErrorTypeInfo` typed dicts. + """ +@final +class TzInfo(datetime.tzinfo): + def tzname(self, _dt: datetime.datetime | None) -> str | None: ... + def utcoffset(self, _dt: datetime.datetime | None) -> datetime.timedelta: ... + def dst(self, _dt: datetime.datetime | None) -> datetime.timedelta: ... + def fromutc(self, dt: datetime.datetime) -> datetime.datetime: ... + def __deepcopy__(self, _memo: dict[Any, Any]) -> TzInfo: ... + +def validate_core_schema(schema: CoreSchema, *, strict: bool | None = None) -> CoreSchema: + """Validate a CoreSchema + This currently uses lax mode for validation (i.e. will coerce strings to dates and such) + but may use strict mode in the future. + We may also remove this function altogether, do not rely on it being present if you are + using pydantic-core directly. + """ diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/core_schema.py b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/core_schema.py new file mode 100644 index 0000000..2cb875b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/core_schema.py @@ -0,0 +1,4045 @@ +""" +This module contains definitions to build schemas which `pydantic_core` can +validate and serialize. +""" + +from __future__ import annotations as _annotations + +import sys +import warnings +from collections.abc import Mapping +from datetime import date, datetime, time, timedelta +from decimal import Decimal +from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Set, Tuple, Type, Union + +from typing_extensions import deprecated + +if sys.version_info < (3, 12): + from typing_extensions import TypedDict +else: + from typing import TypedDict + +if sys.version_info < (3, 11): + from typing_extensions import Protocol, Required, TypeAlias +else: + from typing import Protocol, Required, TypeAlias + +if sys.version_info < (3, 9): + from typing_extensions import Literal +else: + from typing import Literal + +if TYPE_CHECKING: + from pydantic_core import PydanticUndefined +else: + # The initial build of pydantic_core requires PydanticUndefined to generate + # the core schema; so we need to conditionally skip it. mypy doesn't like + # this at all, hence the TYPE_CHECKING branch above. + try: + from pydantic_core import PydanticUndefined + except ImportError: + PydanticUndefined = object() + + +ExtraBehavior = Literal['allow', 'forbid', 'ignore'] + + +class CoreConfig(TypedDict, total=False): + """ + Base class for schema configuration options. + + Attributes: + title: The name of the configuration. + strict: Whether the configuration should strictly adhere to specified rules. + extra_fields_behavior: The behavior for handling extra fields. + typed_dict_total: Whether the TypedDict should be considered total. Default is `True`. + from_attributes: Whether to use attributes for models, dataclasses, and tagged union keys. + loc_by_alias: Whether to use the used alias (or first alias for "field required" errors) instead of + `field_names` to construct error `loc`s. Default is `True`. + revalidate_instances: Whether instances of models and dataclasses should re-validate. Default is 'never'. + validate_default: Whether to validate default values during validation. Default is `False`. + populate_by_name: Whether an aliased field may be populated by its name as given by the model attribute, + as well as the alias. (Replaces 'allow_population_by_field_name' in Pydantic v1.) Default is `False`. + str_max_length: The maximum length for string fields. + str_min_length: The minimum length for string fields. + str_strip_whitespace: Whether to strip whitespace from string fields. + str_to_lower: Whether to convert string fields to lowercase. + str_to_upper: Whether to convert string fields to uppercase. + allow_inf_nan: Whether to allow infinity and NaN values for float fields. Default is `True`. + ser_json_timedelta: The serialization option for `timedelta` values. Default is 'iso8601'. + ser_json_bytes: The serialization option for `bytes` values. Default is 'utf8'. + ser_json_inf_nan: The serialization option for infinity and NaN values + in float fields. Default is 'null'. + hide_input_in_errors: Whether to hide input data from `ValidationError` representation. + validation_error_cause: Whether to add user-python excs to the __cause__ of a ValidationError. + Requires exceptiongroup backport pre Python 3.11. + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + regex_engine: The regex engine to use for regex pattern validation. Default is 'rust-regex'. See `StringSchema`. + cache_strings: Whether to cache strings. Default is `True`, `True` or `'all'` is required to cache strings + during general validation since validators don't know if they're in a key or a value. + """ + + title: str + strict: bool + # settings related to typed dicts, model fields, dataclass fields + extra_fields_behavior: ExtraBehavior + typed_dict_total: bool # default: True + # used for models, dataclasses, and tagged union keys + from_attributes: bool + # whether to use the used alias (or first alias for "field required" errors) instead of field_names + # to construct error `loc`s, default True + loc_by_alias: bool + # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' + revalidate_instances: Literal['always', 'never', 'subclass-instances'] + # whether to validate default values during validation, default False + validate_default: bool + # used on typed-dicts and arguments + populate_by_name: bool # replaces `allow_population_by_field_name` in pydantic v1 + # fields related to string fields only + str_max_length: int + str_min_length: int + str_strip_whitespace: bool + str_to_lower: bool + str_to_upper: bool + # fields related to float fields only + allow_inf_nan: bool # default: True + # the config options are used to customise serialization to JSON + ser_json_timedelta: Literal['iso8601', 'float'] # default: 'iso8601' + ser_json_bytes: Literal['utf8', 'base64', 'hex'] # default: 'utf8' + ser_json_inf_nan: Literal['null', 'constants'] # default: 'null' + # used to hide input data from ValidationError repr + hide_input_in_errors: bool + validation_error_cause: bool # default: False + coerce_numbers_to_str: bool # default: False + regex_engine: Literal['rust-regex', 'python-re'] # default: 'rust-regex' + cache_strings: Union[bool, Literal['all', 'keys', 'none']] # default: 'True' + + +IncExCall: TypeAlias = 'set[int | str] | dict[int | str, IncExCall] | None' + + +class SerializationInfo(Protocol): + @property + def include(self) -> IncExCall: ... + + @property + def exclude(self) -> IncExCall: ... + + @property + def context(self) -> Any | None: + """Current serialization context.""" + + @property + def mode(self) -> str: ... + + @property + def by_alias(self) -> bool: ... + + @property + def exclude_unset(self) -> bool: ... + + @property + def exclude_defaults(self) -> bool: ... + + @property + def exclude_none(self) -> bool: ... + + @property + def serialize_as_any(self) -> bool: ... + + def round_trip(self) -> bool: ... + + def mode_is_json(self) -> bool: ... + + def __str__(self) -> str: ... + + def __repr__(self) -> str: ... + + +class FieldSerializationInfo(SerializationInfo, Protocol): + @property + def field_name(self) -> str: ... + + +class ValidationInfo(Protocol): + """ + Argument passed to validation functions. + """ + + @property + def context(self) -> Any | None: + """Current validation context.""" + ... + + @property + def config(self) -> CoreConfig | None: + """The CoreConfig that applies to this validation.""" + ... + + @property + def mode(self) -> Literal['python', 'json']: + """The type of input data we are currently validating""" + ... + + @property + def data(self) -> Dict[str, Any]: + """The data being validated for this model.""" + ... + + @property + def field_name(self) -> str | None: + """ + The name of the current field being validated if this validator is + attached to a model field. + """ + ... + + +ExpectedSerializationTypes = Literal[ + 'none', + 'int', + 'bool', + 'float', + 'str', + 'bytes', + 'bytearray', + 'list', + 'tuple', + 'set', + 'frozenset', + 'generator', + 'dict', + 'datetime', + 'date', + 'time', + 'timedelta', + 'url', + 'multi-host-url', + 'json', + 'uuid', +] + + +class SimpleSerSchema(TypedDict, total=False): + type: Required[ExpectedSerializationTypes] + + +def simple_ser_schema(type: ExpectedSerializationTypes) -> SimpleSerSchema: + """ + Returns a schema for serialization with a custom type. + + Args: + type: The type to use for serialization + """ + return SimpleSerSchema(type=type) + + +# (input_value: Any, /) -> Any +GeneralPlainNoInfoSerializerFunction = Callable[[Any], Any] +# (input_value: Any, info: FieldSerializationInfo, /) -> Any +GeneralPlainInfoSerializerFunction = Callable[[Any, SerializationInfo], Any] +# (model: Any, input_value: Any, /) -> Any +FieldPlainNoInfoSerializerFunction = Callable[[Any, Any], Any] +# (model: Any, input_value: Any, info: FieldSerializationInfo, /) -> Any +FieldPlainInfoSerializerFunction = Callable[[Any, Any, FieldSerializationInfo], Any] +SerializerFunction = Union[ + GeneralPlainNoInfoSerializerFunction, + GeneralPlainInfoSerializerFunction, + FieldPlainNoInfoSerializerFunction, + FieldPlainInfoSerializerFunction, +] + +WhenUsed = Literal['always', 'unless-none', 'json', 'json-unless-none'] +""" +Values have the following meanings: + +* `'always'` means always use +* `'unless-none'` means use unless the value is `None` +* `'json'` means use when serializing to JSON +* `'json-unless-none'` means use when serializing to JSON and the value is not `None` +""" + + +class PlainSerializerFunctionSerSchema(TypedDict, total=False): + type: Required[Literal['function-plain']] + function: Required[SerializerFunction] + is_field_serializer: bool # default False + info_arg: bool # default False + return_schema: CoreSchema # if omitted, AnySchema is used + when_used: WhenUsed # default: 'always' + + +def plain_serializer_function_ser_schema( + function: SerializerFunction, + *, + is_field_serializer: bool | None = None, + info_arg: bool | None = None, + return_schema: CoreSchema | None = None, + when_used: WhenUsed = 'always', +) -> PlainSerializerFunctionSerSchema: + """ + Returns a schema for serialization with a function, can be either a "general" or "field" function. + + Args: + function: The function to use for serialization + is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument, + and `info` includes `field_name` + info_arg: Whether the function takes an `info` argument + return_schema: Schema to use for serializing return value + when_used: When the function should be called + """ + if when_used == 'always': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none( + type='function-plain', + function=function, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + return_schema=return_schema, + when_used=when_used, + ) + + +class SerializerFunctionWrapHandler(Protocol): # pragma: no cover + def __call__(self, input_value: Any, index_key: int | str | None = None, /) -> Any: ... + + +# (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any +GeneralWrapNoInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler], Any] +# (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any +GeneralWrapInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo], Any] +# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any +FieldWrapNoInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler], Any] +# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, info: FieldSerializationInfo, /) -> Any +FieldWrapInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler, FieldSerializationInfo], Any] +WrapSerializerFunction = Union[ + GeneralWrapNoInfoSerializerFunction, + GeneralWrapInfoSerializerFunction, + FieldWrapNoInfoSerializerFunction, + FieldWrapInfoSerializerFunction, +] + + +class WrapSerializerFunctionSerSchema(TypedDict, total=False): + type: Required[Literal['function-wrap']] + function: Required[WrapSerializerFunction] + is_field_serializer: bool # default False + info_arg: bool # default False + schema: CoreSchema # if omitted, the schema on which this serializer is defined is used + return_schema: CoreSchema # if omitted, AnySchema is used + when_used: WhenUsed # default: 'always' + + +def wrap_serializer_function_ser_schema( + function: WrapSerializerFunction, + *, + is_field_serializer: bool | None = None, + info_arg: bool | None = None, + schema: CoreSchema | None = None, + return_schema: CoreSchema | None = None, + when_used: WhenUsed = 'always', +) -> WrapSerializerFunctionSerSchema: + """ + Returns a schema for serialization with a wrap function, can be either a "general" or "field" function. + + Args: + function: The function to use for serialization + is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument, + and `info` includes `field_name` + info_arg: Whether the function takes an `info` argument + schema: The schema to use for the inner serialization + return_schema: Schema to use for serializing return value + when_used: When the function should be called + """ + if when_used == 'always': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none( + type='function-wrap', + function=function, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + schema=schema, + return_schema=return_schema, + when_used=when_used, + ) + + +class FormatSerSchema(TypedDict, total=False): + type: Required[Literal['format']] + formatting_string: Required[str] + when_used: WhenUsed # default: 'json-unless-none' + + +def format_ser_schema(formatting_string: str, *, when_used: WhenUsed = 'json-unless-none') -> FormatSerSchema: + """ + Returns a schema for serialization using python's `format` method. + + Args: + formatting_string: String defining the format to use + when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default + """ + if when_used == 'json-unless-none': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none(type='format', formatting_string=formatting_string, when_used=when_used) + + +class ToStringSerSchema(TypedDict, total=False): + type: Required[Literal['to-string']] + when_used: WhenUsed # default: 'json-unless-none' + + +def to_string_ser_schema(*, when_used: WhenUsed = 'json-unless-none') -> ToStringSerSchema: + """ + Returns a schema for serialization using python's `str()` / `__str__` method. + + Args: + when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default + """ + s = dict(type='to-string') + if when_used != 'json-unless-none': + # just to avoid extra elements in schema, and to use the actual default defined in rust + s['when_used'] = when_used + return s # type: ignore + + +class ModelSerSchema(TypedDict, total=False): + type: Required[Literal['model']] + cls: Required[Type[Any]] + schema: Required[CoreSchema] + + +def model_ser_schema(cls: Type[Any], schema: CoreSchema) -> ModelSerSchema: + """ + Returns a schema for serialization using a model. + + Args: + cls: The expected class type, used to generate warnings if the wrong type is passed + schema: Internal schema to use to serialize the model dict + """ + return ModelSerSchema(type='model', cls=cls, schema=schema) + + +SerSchema = Union[ + SimpleSerSchema, + PlainSerializerFunctionSerSchema, + WrapSerializerFunctionSerSchema, + FormatSerSchema, + ToStringSerSchema, + ModelSerSchema, +] + + +class ComputedField(TypedDict, total=False): + type: Required[Literal['computed-field']] + property_name: Required[str] + return_schema: Required[CoreSchema] + alias: str + metadata: Any + + +def computed_field( + property_name: str, return_schema: CoreSchema, *, alias: str | None = None, metadata: Any = None +) -> ComputedField: + """ + ComputedFields are properties of a model or dataclass that are included in serialization. + + Args: + property_name: The name of the property on the model or dataclass + return_schema: The schema used for the type returned by the computed field + alias: The name to use in the serialized output + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='computed-field', property_name=property_name, return_schema=return_schema, alias=alias, metadata=metadata + ) + + +class AnySchema(TypedDict, total=False): + type: Required[Literal['any']] + ref: str + metadata: Any + serialization: SerSchema + + +def any_schema(*, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None) -> AnySchema: + """ + Returns a schema that matches any value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.any_schema() + v = SchemaValidator(schema) + assert v.validate_python(1) == 1 + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='any', ref=ref, metadata=metadata, serialization=serialization) + + +class NoneSchema(TypedDict, total=False): + type: Required[Literal['none']] + ref: str + metadata: Any + serialization: SerSchema + + +def none_schema(*, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None) -> NoneSchema: + """ + Returns a schema that matches a None value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.none_schema() + v = SchemaValidator(schema) + assert v.validate_python(None) is None + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='none', ref=ref, metadata=metadata, serialization=serialization) + + +class BoolSchema(TypedDict, total=False): + type: Required[Literal['bool']] + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def bool_schema( + strict: bool | None = None, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None +) -> BoolSchema: + """ + Returns a schema that matches a bool value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.bool_schema() + v = SchemaValidator(schema) + assert v.validate_python('True') is True + ``` + + Args: + strict: Whether the value should be a bool or a value that can be converted to a bool + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='bool', strict=strict, ref=ref, metadata=metadata, serialization=serialization) + + +class IntSchema(TypedDict, total=False): + type: Required[Literal['int']] + multiple_of: int + le: int + ge: int + lt: int + gt: int + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def int_schema( + *, + multiple_of: int | None = None, + le: int | None = None, + ge: int | None = None, + lt: int | None = None, + gt: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> IntSchema: + """ + Returns a schema that matches a int value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.int_schema(multiple_of=2, le=6, ge=2) + v = SchemaValidator(schema) + assert v.validate_python('4') == 4 + ``` + + Args: + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + strict: Whether the value should be a int or a value that can be converted to a int + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='int', + multiple_of=multiple_of, + le=le, + ge=ge, + lt=lt, + gt=gt, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class FloatSchema(TypedDict, total=False): + type: Required[Literal['float']] + allow_inf_nan: bool # whether 'NaN', '+inf', '-inf' should be forbidden. default: True + multiple_of: float + le: float + ge: float + lt: float + gt: float + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def float_schema( + *, + allow_inf_nan: bool | None = None, + multiple_of: float | None = None, + le: float | None = None, + ge: float | None = None, + lt: float | None = None, + gt: float | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> FloatSchema: + """ + Returns a schema that matches a float value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.float_schema(le=0.8, ge=0.2) + v = SchemaValidator(schema) + assert v.validate_python('0.5') == 0.5 + ``` + + Args: + allow_inf_nan: Whether to allow inf and nan values + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + strict: Whether the value should be a float or a value that can be converted to a float + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='float', + allow_inf_nan=allow_inf_nan, + multiple_of=multiple_of, + le=le, + ge=ge, + lt=lt, + gt=gt, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DecimalSchema(TypedDict, total=False): + type: Required[Literal['decimal']] + allow_inf_nan: bool # whether 'NaN', '+inf', '-inf' should be forbidden. default: False + multiple_of: Decimal + le: Decimal + ge: Decimal + lt: Decimal + gt: Decimal + max_digits: int + decimal_places: int + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def decimal_schema( + *, + allow_inf_nan: bool = None, + multiple_of: Decimal | None = None, + le: Decimal | None = None, + ge: Decimal | None = None, + lt: Decimal | None = None, + gt: Decimal | None = None, + max_digits: int | None = None, + decimal_places: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> DecimalSchema: + """ + Returns a schema that matches a decimal value, e.g.: + + ```py + from decimal import Decimal + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.decimal_schema(le=0.8, ge=0.2) + v = SchemaValidator(schema) + assert v.validate_python('0.5') == Decimal('0.5') + ``` + + Args: + allow_inf_nan: Whether to allow inf and nan values + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + max_digits: The maximum number of decimal digits allowed + decimal_places: The maximum number of decimal places allowed + strict: Whether the value should be a float or a value that can be converted to a float + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='decimal', + gt=gt, + ge=ge, + lt=lt, + le=le, + max_digits=max_digits, + decimal_places=decimal_places, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class StringSchema(TypedDict, total=False): + type: Required[Literal['str']] + pattern: str + max_length: int + min_length: int + strip_whitespace: bool + to_lower: bool + to_upper: bool + regex_engine: Literal['rust-regex', 'python-re'] # default: 'rust-regex' + strict: bool + coerce_numbers_to_str: bool + ref: str + metadata: Any + serialization: SerSchema + + +def str_schema( + *, + pattern: str | None = None, + max_length: int | None = None, + min_length: int | None = None, + strip_whitespace: bool | None = None, + to_lower: bool | None = None, + to_upper: bool | None = None, + regex_engine: Literal['rust-regex', 'python-re'] | None = None, + strict: bool | None = None, + coerce_numbers_to_str: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> StringSchema: + """ + Returns a schema that matches a string value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.str_schema(max_length=10, min_length=2) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + ``` + + Args: + pattern: A regex pattern that the value must match + max_length: The value must be at most this length + min_length: The value must be at least this length + strip_whitespace: Whether to strip whitespace from the value + to_lower: Whether to convert the value to lowercase + to_upper: Whether to convert the value to uppercase + regex_engine: The regex engine to use for pattern validation. Default is 'rust-regex'. + - `rust-regex` uses the [`regex`](https://docs.rs/regex) Rust + crate, which is non-backtracking and therefore more DDoS + resistant, but does not support all regex features. + - `python-re` use the [`re`](https://docs.python.org/3/library/re.html) module, + which supports all regex features, but may be slower. + strict: Whether the value should be a string or a value that can be converted to a string + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='str', + pattern=pattern, + max_length=max_length, + min_length=min_length, + strip_whitespace=strip_whitespace, + to_lower=to_lower, + to_upper=to_upper, + regex_engine=regex_engine, + strict=strict, + coerce_numbers_to_str=coerce_numbers_to_str, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class BytesSchema(TypedDict, total=False): + type: Required[Literal['bytes']] + max_length: int + min_length: int + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def bytes_schema( + *, + max_length: int | None = None, + min_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> BytesSchema: + """ + Returns a schema that matches a bytes value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.bytes_schema(max_length=10, min_length=2) + v = SchemaValidator(schema) + assert v.validate_python(b'hello') == b'hello' + ``` + + Args: + max_length: The value must be at most this length + min_length: The value must be at least this length + strict: Whether the value should be a bytes or a value that can be converted to a bytes + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='bytes', + max_length=max_length, + min_length=min_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DateSchema(TypedDict, total=False): + type: Required[Literal['date']] + strict: bool + le: date + ge: date + lt: date + gt: date + now_op: Literal['past', 'future'] + # defaults to current local utc offset from `time.localtime().tm_gmtoff` + # value is restricted to -86_400 < offset < 86_400 by bounds in generate_self_schema.py + now_utc_offset: int + ref: str + metadata: Any + serialization: SerSchema + + +def date_schema( + *, + strict: bool | None = None, + le: date | None = None, + ge: date | None = None, + lt: date | None = None, + gt: date | None = None, + now_op: Literal['past', 'future'] | None = None, + now_utc_offset: int | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> DateSchema: + """ + Returns a schema that matches a date value, e.g.: + + ```py + from datetime import date + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.date_schema(le=date(2020, 1, 1), ge=date(2019, 1, 1)) + v = SchemaValidator(schema) + assert v.validate_python(date(2019, 6, 1)) == date(2019, 6, 1) + ``` + + Args: + strict: Whether the value should be a date or a value that can be converted to a date + le: The value must be less than or equal to this date + ge: The value must be greater than or equal to this date + lt: The value must be strictly less than this date + gt: The value must be strictly greater than this date + now_op: The value must be in the past or future relative to the current date + now_utc_offset: The value must be in the past or future relative to the current date with this utc offset + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='date', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + now_op=now_op, + now_utc_offset=now_utc_offset, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TimeSchema(TypedDict, total=False): + type: Required[Literal['time']] + strict: bool + le: time + ge: time + lt: time + gt: time + tz_constraint: Union[Literal['aware', 'naive'], int] + microseconds_precision: Literal['truncate', 'error'] + ref: str + metadata: Any + serialization: SerSchema + + +def time_schema( + *, + strict: bool | None = None, + le: time | None = None, + ge: time | None = None, + lt: time | None = None, + gt: time | None = None, + tz_constraint: Literal['aware', 'naive'] | int | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> TimeSchema: + """ + Returns a schema that matches a time value, e.g.: + + ```py + from datetime import time + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.time_schema(le=time(12, 0, 0), ge=time(6, 0, 0)) + v = SchemaValidator(schema) + assert v.validate_python(time(9, 0, 0)) == time(9, 0, 0) + ``` + + Args: + strict: Whether the value should be a time or a value that can be converted to a time + le: The value must be less than or equal to this time + ge: The value must be greater than or equal to this time + lt: The value must be strictly less than this time + gt: The value must be strictly greater than this time + tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='time', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + tz_constraint=tz_constraint, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DatetimeSchema(TypedDict, total=False): + type: Required[Literal['datetime']] + strict: bool + le: datetime + ge: datetime + lt: datetime + gt: datetime + now_op: Literal['past', 'future'] + tz_constraint: Union[Literal['aware', 'naive'], int] + # defaults to current local utc offset from `time.localtime().tm_gmtoff` + # value is restricted to -86_400 < offset < 86_400 by bounds in generate_self_schema.py + now_utc_offset: int + microseconds_precision: Literal['truncate', 'error'] # default: 'truncate' + ref: str + metadata: Any + serialization: SerSchema + + +def datetime_schema( + *, + strict: bool | None = None, + le: datetime | None = None, + ge: datetime | None = None, + lt: datetime | None = None, + gt: datetime | None = None, + now_op: Literal['past', 'future'] | None = None, + tz_constraint: Literal['aware', 'naive'] | int | None = None, + now_utc_offset: int | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> DatetimeSchema: + """ + Returns a schema that matches a datetime value, e.g.: + + ```py + from datetime import datetime + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.datetime_schema() + v = SchemaValidator(schema) + now = datetime.now() + assert v.validate_python(str(now)) == now + ``` + + Args: + strict: Whether the value should be a datetime or a value that can be converted to a datetime + le: The value must be less than or equal to this datetime + ge: The value must be greater than or equal to this datetime + lt: The value must be strictly less than this datetime + gt: The value must be strictly greater than this datetime + now_op: The value must be in the past or future relative to the current datetime + tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset + TODO: use of a tzinfo where offset changes based on the datetime is not yet supported + now_utc_offset: The value must be in the past or future relative to the current datetime with this utc offset + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='datetime', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + now_op=now_op, + tz_constraint=tz_constraint, + now_utc_offset=now_utc_offset, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TimedeltaSchema(TypedDict, total=False): + type: Required[Literal['timedelta']] + strict: bool + le: timedelta + ge: timedelta + lt: timedelta + gt: timedelta + microseconds_precision: Literal['truncate', 'error'] + ref: str + metadata: Any + serialization: SerSchema + + +def timedelta_schema( + *, + strict: bool | None = None, + le: timedelta | None = None, + ge: timedelta | None = None, + lt: timedelta | None = None, + gt: timedelta | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> TimedeltaSchema: + """ + Returns a schema that matches a timedelta value, e.g.: + + ```py + from datetime import timedelta + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.timedelta_schema(le=timedelta(days=1), ge=timedelta(days=0)) + v = SchemaValidator(schema) + assert v.validate_python(timedelta(hours=12)) == timedelta(hours=12) + ``` + + Args: + strict: Whether the value should be a timedelta or a value that can be converted to a timedelta + le: The value must be less than or equal to this timedelta + ge: The value must be greater than or equal to this timedelta + lt: The value must be strictly less than this timedelta + gt: The value must be strictly greater than this timedelta + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='timedelta', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class LiteralSchema(TypedDict, total=False): + type: Required[Literal['literal']] + expected: Required[List[Any]] + ref: str + metadata: Any + serialization: SerSchema + + +def literal_schema( + expected: list[Any], *, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None +) -> LiteralSchema: + """ + Returns a schema that matches a literal value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.literal_schema(['hello', 'world']) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + ``` + + Args: + expected: The value must be one of these values + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='literal', expected=expected, ref=ref, metadata=metadata, serialization=serialization) + + +class EnumSchema(TypedDict, total=False): + type: Required[Literal['enum']] + cls: Required[Any] + members: Required[List[Any]] + sub_type: Literal['str', 'int', 'float'] + missing: Callable[[Any], Any] + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def enum_schema( + cls: Any, + members: list[Any], + *, + sub_type: Literal['str', 'int', 'float'] | None = None, + missing: Callable[[Any], Any] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> EnumSchema: + """ + Returns a schema that matches an enum value, e.g.: + + ```py + from enum import Enum + from pydantic_core import SchemaValidator, core_schema + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + schema = core_schema.enum_schema(Color, list(Color.__members__.values())) + v = SchemaValidator(schema) + assert v.validate_python(2) is Color.GREEN + ``` + + Args: + cls: The enum class + members: The members of the enum, generally `list(MyEnum.__members__.values())` + sub_type: The type of the enum, either 'str' or 'int' or None for plain enums + missing: A function to use when the value is not found in the enum, from `_missing_` + strict: Whether to use strict mode, defaults to False + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='enum', + cls=cls, + members=members, + sub_type=sub_type, + missing=missing, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# must match input/parse_json.rs::JsonType::try_from +JsonType = Literal['null', 'bool', 'int', 'float', 'str', 'list', 'dict'] + + +class IsInstanceSchema(TypedDict, total=False): + type: Required[Literal['is-instance']] + cls: Required[Any] + cls_repr: str + ref: str + metadata: Any + serialization: SerSchema + + +def is_instance_schema( + cls: Any, + *, + cls_repr: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> IsInstanceSchema: + """ + Returns a schema that checks if a value is an instance of a class, equivalent to python's `isinstance` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + class A: + pass + + schema = core_schema.is_instance_schema(cls=A) + v = SchemaValidator(schema) + v.validate_python(A()) + ``` + + Args: + cls: The value must be an instance of this class + cls_repr: If provided this string is used in the validator name instead of `repr(cls)` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='is-instance', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization + ) + + +class IsSubclassSchema(TypedDict, total=False): + type: Required[Literal['is-subclass']] + cls: Required[Type[Any]] + cls_repr: str + ref: str + metadata: Any + serialization: SerSchema + + +def is_subclass_schema( + cls: Type[Any], + *, + cls_repr: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> IsInstanceSchema: + """ + Returns a schema that checks if a value is a subtype of a class, equivalent to python's `issubclass` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + class A: + pass + + class B(A): + pass + + schema = core_schema.is_subclass_schema(cls=A) + v = SchemaValidator(schema) + v.validate_python(B) + ``` + + Args: + cls: The value must be a subclass of this class + cls_repr: If provided this string is used in the validator name instead of `repr(cls)` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='is-subclass', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization + ) + + +class CallableSchema(TypedDict, total=False): + type: Required[Literal['callable']] + ref: str + metadata: Any + serialization: SerSchema + + +def callable_schema( + *, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None +) -> CallableSchema: + """ + Returns a schema that checks if a value is callable, equivalent to python's `callable` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.callable_schema() + v = SchemaValidator(schema) + v.validate_python(min) + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='callable', ref=ref, metadata=metadata, serialization=serialization) + + +class UuidSchema(TypedDict, total=False): + type: Required[Literal['uuid']] + version: Literal[1, 3, 4, 5] + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def uuid_schema( + *, + version: Literal[1, 3, 4, 5] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> UuidSchema: + return _dict_not_none( + type='uuid', version=version, strict=strict, ref=ref, metadata=metadata, serialization=serialization + ) + + +class IncExSeqSerSchema(TypedDict, total=False): + type: Required[Literal['include-exclude-sequence']] + include: Set[int] + exclude: Set[int] + + +def filter_seq_schema(*, include: Set[int] | None = None, exclude: Set[int] | None = None) -> IncExSeqSerSchema: + return _dict_not_none(type='include-exclude-sequence', include=include, exclude=exclude) + + +IncExSeqOrElseSerSchema = Union[IncExSeqSerSchema, SerSchema] + + +class ListSchema(TypedDict, total=False): + type: Required[Literal['list']] + items_schema: CoreSchema + min_length: int + max_length: int + strict: bool + ref: str + metadata: Any + serialization: IncExSeqOrElseSerSchema + + +def list_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> ListSchema: + """ + Returns a schema that matches a list value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.list_schema(core_schema.int_schema(), min_length=0, max_length=10) + v = SchemaValidator(schema) + assert v.validate_python(['4']) == [4] + ``` + + Args: + items_schema: The value must be a list of items that match this schema + min_length: The value must be a list with at least this many items + max_length: The value must be a list with at most this many items + strict: The value must be a list with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='list', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# @deprecated('tuple_positional_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.') +def tuple_positional_schema( + items_schema: list[CoreSchema], + *, + extras_schema: CoreSchema | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of schemas, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_positional_schema( + [core_schema.int_schema(), core_schema.str_schema()] + ) + v = SchemaValidator(schema) + assert v.validate_python((1, 'hello')) == (1, 'hello') + ``` + + Args: + items_schema: The value must be a tuple with items that match these schemas + extras_schema: The value must be a tuple with items that match this schema + This was inspired by JSON schema's `prefixItems` and `items` fields. + In python's `typing.Tuple`, you can't specify a type for "extra" items -- they must all be the same type + if the length is variable. So this field won't be set from a `typing.Tuple` annotation on a pydantic model. + strict: The value must be a tuple with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if extras_schema is not None: + variadic_item_index = len(items_schema) + items_schema = items_schema + [extras_schema] + else: + variadic_item_index = None + return tuple_schema( + items_schema=items_schema, + variadic_item_index=variadic_item_index, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# @deprecated('tuple_variable_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.') +def tuple_variable_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_variable_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python(('1', 2, 3)) == (1, 2, 3) + ``` + + Args: + items_schema: The value must be a tuple with items that match this schema + min_length: The value must be a tuple with at least this many items + max_length: The value must be a tuple with at most this many items + strict: The value must be a tuple with exactly this many items + ref: Optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return tuple_schema( + items_schema=[items_schema or any_schema()], + variadic_item_index=0, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TupleSchema(TypedDict, total=False): + type: Required[Literal['tuple']] + items_schema: Required[List[CoreSchema]] + variadic_item_index: int + min_length: int + max_length: int + strict: bool + ref: str + metadata: Any + serialization: IncExSeqOrElseSerSchema + + +def tuple_schema( + items_schema: list[CoreSchema], + *, + variadic_item_index: int | None = None, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of schemas, with an optional variadic item, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_schema( + [core_schema.int_schema(), core_schema.str_schema(), core_schema.float_schema()], + variadic_item_index=1, + ) + v = SchemaValidator(schema) + assert v.validate_python((1, 'hello', 'world', 1.5)) == (1, 'hello', 'world', 1.5) + ``` + + Args: + items_schema: The value must be a tuple with items that match these schemas + variadic_item_index: The index of the schema in `items_schema` to be treated as variadic (following PEP 646) + min_length: The value must be a tuple with at least this many items + max_length: The value must be a tuple with at most this many items + strict: The value must be a tuple with exactly this many items + ref: Optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='tuple', + items_schema=items_schema, + variadic_item_index=variadic_item_index, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class SetSchema(TypedDict, total=False): + type: Required[Literal['set']] + items_schema: CoreSchema + min_length: int + max_length: int + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def set_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> SetSchema: + """ + Returns a schema that matches a set of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.set_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python({1, '2', 3}) == {1, 2, 3} + ``` + + Args: + items_schema: The value must be a set with items that match this schema + min_length: The value must be a set with at least this many items + max_length: The value must be a set with at most this many items + strict: The value must be a set with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='set', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class FrozenSetSchema(TypedDict, total=False): + type: Required[Literal['frozenset']] + items_schema: CoreSchema + min_length: int + max_length: int + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def frozenset_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> FrozenSetSchema: + """ + Returns a schema that matches a frozenset of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.frozenset_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python(frozenset(range(3))) == frozenset({0, 1, 2}) + ``` + + Args: + items_schema: The value must be a frozenset with items that match this schema + min_length: The value must be a frozenset with at least this many items + max_length: The value must be a frozenset with at most this many items + strict: The value must be a frozenset with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='frozenset', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class GeneratorSchema(TypedDict, total=False): + type: Required[Literal['generator']] + items_schema: CoreSchema + min_length: int + max_length: int + ref: str + metadata: Any + serialization: IncExSeqOrElseSerSchema + + +def generator_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> GeneratorSchema: + """ + Returns a schema that matches a generator value, e.g.: + + ```py + from typing import Iterator + from pydantic_core import SchemaValidator, core_schema + + def gen() -> Iterator[int]: + yield 1 + + schema = core_schema.generator_schema(items_schema=core_schema.int_schema()) + v = SchemaValidator(schema) + v.validate_python(gen()) + ``` + + Unlike other types, validated generators do not raise ValidationErrors eagerly, + but instead will raise a ValidationError when a violating value is actually read from the generator. + This is to ensure that "validated" generators retain the benefit of lazy evaluation. + + Args: + items_schema: The value must be a generator with items that match this schema + min_length: The value must be a generator that yields at least this many items + max_length: The value must be a generator that yields at most this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='generator', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +IncExDict = Set[Union[int, str]] + + +class IncExDictSerSchema(TypedDict, total=False): + type: Required[Literal['include-exclude-dict']] + include: IncExDict + exclude: IncExDict + + +def filter_dict_schema(*, include: IncExDict | None = None, exclude: IncExDict | None = None) -> IncExDictSerSchema: + return _dict_not_none(type='include-exclude-dict', include=include, exclude=exclude) + + +IncExDictOrElseSerSchema = Union[IncExDictSerSchema, SerSchema] + + +class DictSchema(TypedDict, total=False): + type: Required[Literal['dict']] + keys_schema: CoreSchema # default: AnySchema + values_schema: CoreSchema # default: AnySchema + min_length: int + max_length: int + strict: bool + ref: str + metadata: Any + serialization: IncExDictOrElseSerSchema + + +def dict_schema( + keys_schema: CoreSchema | None = None, + values_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> DictSchema: + """ + Returns a schema that matches a dict value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.dict_schema( + keys_schema=core_schema.str_schema(), values_schema=core_schema.int_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2} + ``` + + Args: + keys_schema: The value must be a dict with keys that match this schema + values_schema: The value must be a dict with values that match this schema + min_length: The value must be a dict with at least this many items + max_length: The value must be a dict with at most this many items + strict: Whether the keys and values should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='dict', + keys_schema=keys_schema, + values_schema=values_schema, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# (input_value: Any, /) -> Any +NoInfoValidatorFunction = Callable[[Any], Any] + + +class NoInfoValidatorFunctionSchema(TypedDict): + type: Literal['no-info'] + function: NoInfoValidatorFunction + + +# (input_value: Any, info: ValidationInfo, /) -> Any +WithInfoValidatorFunction = Callable[[Any, ValidationInfo], Any] + + +class WithInfoValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['with-info']] + function: Required[WithInfoValidatorFunction] + field_name: str + + +ValidationFunction = Union[NoInfoValidatorFunctionSchema, WithInfoValidatorFunctionSchema] + + +class _ValidatorFunctionSchema(TypedDict, total=False): + function: Required[ValidationFunction] + schema: Required[CoreSchema] + ref: str + metadata: Any + serialization: SerSchema + + +class BeforeValidatorFunctionSchema(_ValidatorFunctionSchema, total=False): + type: Required[Literal['function-before']] + + +def no_info_before_validator_function( + function: NoInfoValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> BeforeValidatorFunctionSchema: + """ + Returns a schema that calls a validator function before validating, no `info` argument is provided, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: bytes) -> str: + return v.decode() + 'world' + + func_schema = core_schema.no_info_before_validator_function( + function=fn, schema=core_schema.str_schema() + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-before', + function={'type': 'no-info', 'function': function}, + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_before_validator_function( + function: WithInfoValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> BeforeValidatorFunctionSchema: + """ + Returns a schema that calls a validator function before validation, the function is called with + an `info` argument, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: bytes, info: core_schema.ValidationInfo) -> str: + assert info.data is not None + assert info.field_name is not None + return v.decode() + 'world' + + func_schema = core_schema.with_info_before_validator_function( + function=fn, schema=core_schema.str_schema(), field_name='a' + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call + field_name: The name of the field + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-before', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class AfterValidatorFunctionSchema(_ValidatorFunctionSchema, total=False): + type: Required[Literal['function-after']] + + +def no_info_after_validator_function( + function: NoInfoValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> AfterValidatorFunctionSchema: + """ + Returns a schema that calls a validator function after validating, no `info` argument is provided, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str) -> str: + return v + 'world' + + func_schema = core_schema.no_info_after_validator_function(fn, core_schema.str_schema()) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call after the schema is validated + schema: The schema to validate before the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-after', + function={'type': 'no-info', 'function': function}, + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_after_validator_function( + function: WithInfoValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> AfterValidatorFunctionSchema: + """ + Returns a schema that calls a validator function after validation, the function is called with + an `info` argument, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert info.data is not None + assert info.field_name is not None + return v + 'world' + + func_schema = core_schema.with_info_after_validator_function( + function=fn, schema=core_schema.str_schema(), field_name='a' + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call after the schema is validated + schema: The schema to validate before the validator function + field_name: The name of the field this validators is applied to, if any + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-after', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ValidatorFunctionWrapHandler(Protocol): + def __call__(self, input_value: Any, outer_location: str | int | None = None) -> Any: # pragma: no cover + ... + + +# (input_value: Any, validator: ValidatorFunctionWrapHandler, /) -> Any +NoInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler], Any] + + +class NoInfoWrapValidatorFunctionSchema(TypedDict): + type: Literal['no-info'] + function: NoInfoWrapValidatorFunction + + +# (input_value: Any, validator: ValidatorFunctionWrapHandler, info: ValidationInfo, /) -> Any +WithInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler, ValidationInfo], Any] + + +class WithInfoWrapValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['with-info']] + function: Required[WithInfoWrapValidatorFunction] + field_name: str + + +WrapValidatorFunction = Union[NoInfoWrapValidatorFunctionSchema, WithInfoWrapValidatorFunctionSchema] + + +class WrapValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['function-wrap']] + function: Required[WrapValidatorFunction] + schema: Required[CoreSchema] + ref: str + metadata: Any + serialization: SerSchema + + +def no_info_wrap_validator_function( + function: NoInfoWrapValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> WrapValidatorFunctionSchema: + """ + Returns a schema which calls a function with a `validator` callable argument which can + optionally be used to call inner validation with the function logic, this is much like the + "onion" implementation of middleware in many popular web frameworks, no `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn( + v: str, + validator: core_schema.ValidatorFunctionWrapHandler, + ) -> str: + return validator(input_value=v) + 'world' + + schema = core_schema.no_info_wrap_validator_function( + function=fn, schema=core_schema.str_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-wrap', + function={'type': 'no-info', 'function': function}, + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_wrap_validator_function( + function: WithInfoWrapValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> WrapValidatorFunctionSchema: + """ + Returns a schema which calls a function with a `validator` callable argument which can + optionally be used to call inner validation with the function logic, this is much like the + "onion" implementation of middleware in many popular web frameworks, an `info` argument is also passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn( + v: str, + validator: core_schema.ValidatorFunctionWrapHandler, + info: core_schema.ValidationInfo, + ) -> str: + return validator(input_value=v) + 'world' + + schema = core_schema.with_info_wrap_validator_function( + function=fn, schema=core_schema.str_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + field_name: The name of the field this validators is applied to, if any + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-wrap', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class PlainValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['function-plain']] + function: Required[ValidationFunction] + ref: str + metadata: Any + serialization: SerSchema + + +def no_info_plain_validator_function( + function: NoInfoValidatorFunction, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> PlainValidatorFunctionSchema: + """ + Returns a schema that uses the provided function for validation, no `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str) -> str: + assert 'hello' in v + return v + 'world' + + schema = core_schema.no_info_plain_validator_function(function=fn) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-plain', + function={'type': 'no-info', 'function': function}, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_plain_validator_function( + function: WithInfoValidatorFunction, + *, + field_name: str | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> PlainValidatorFunctionSchema: + """ + Returns a schema that uses the provided function for validation, an `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + 'world' + + schema = core_schema.with_info_plain_validator_function(function=fn) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + field_name: The name of the field this validators is applied to, if any + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-plain', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class WithDefaultSchema(TypedDict, total=False): + type: Required[Literal['default']] + schema: Required[CoreSchema] + default: Any + default_factory: Callable[[], Any] + on_error: Literal['raise', 'omit', 'default'] # default: 'raise' + validate_default: bool # default: False + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def with_default_schema( + schema: CoreSchema, + *, + default: Any = PydanticUndefined, + default_factory: Callable[[], Any] | None = None, + on_error: Literal['raise', 'omit', 'default'] | None = None, + validate_default: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> WithDefaultSchema: + """ + Returns a schema that adds a default value to the given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.with_default_schema(core_schema.str_schema(), default='hello') + wrapper_schema = core_schema.typed_dict_schema( + {'a': core_schema.typed_dict_field(schema)} + ) + v = SchemaValidator(wrapper_schema) + assert v.validate_python({}) == v.validate_python({'a': 'hello'}) + ``` + + Args: + schema: The schema to add a default value to + default: The default value to use + default_factory: A function that returns the default value to use + on_error: What to do if the schema validation fails. One of 'raise', 'omit', 'default' + validate_default: Whether the default value should be validated + strict: Whether the underlying schema should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + s = _dict_not_none( + type='default', + schema=schema, + default_factory=default_factory, + on_error=on_error, + validate_default=validate_default, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + if default is not PydanticUndefined: + s['default'] = default + return s + + +class NullableSchema(TypedDict, total=False): + type: Required[Literal['nullable']] + schema: Required[CoreSchema] + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def nullable_schema( + schema: CoreSchema, + *, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> NullableSchema: + """ + Returns a schema that matches a nullable value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.nullable_schema(core_schema.str_schema()) + v = SchemaValidator(schema) + assert v.validate_python(None) is None + ``` + + Args: + schema: The schema to wrap + strict: Whether the underlying schema should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='nullable', schema=schema, strict=strict, ref=ref, metadata=metadata, serialization=serialization + ) + + +class UnionSchema(TypedDict, total=False): + type: Required[Literal['union']] + choices: Required[List[Union[CoreSchema, Tuple[CoreSchema, str]]]] + # default true, whether to automatically collapse unions with one element to the inner validator + auto_collapse: bool + custom_error_type: str + custom_error_message: str + custom_error_context: Dict[str, Union[str, int, float]] + mode: Literal['smart', 'left_to_right'] # default: 'smart' + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def union_schema( + choices: list[CoreSchema | tuple[CoreSchema, str]], + *, + auto_collapse: bool | None = None, + custom_error_type: str | None = None, + custom_error_message: str | None = None, + custom_error_context: dict[str, str | int] | None = None, + mode: Literal['smart', 'left_to_right'] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> UnionSchema: + """ + Returns a schema that matches a union value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.union_schema([core_schema.str_schema(), core_schema.int_schema()]) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + assert v.validate_python(1) == 1 + ``` + + Args: + choices: The schemas to match. If a tuple, the second item is used as the label for the case. + auto_collapse: whether to automatically collapse unions with one element to the inner validator, default true + custom_error_type: The custom error type to use if the validation fails + custom_error_message: The custom error message to use if the validation fails + custom_error_context: The custom error context to use if the validation fails + mode: How to select which choice to return + * `smart` (default) will try to return the choice which is the closest match to the input value + * `left_to_right` will return the first choice in `choices` which succeeds validation + strict: Whether the underlying schemas should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='union', + choices=choices, + auto_collapse=auto_collapse, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + mode=mode, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TaggedUnionSchema(TypedDict, total=False): + type: Required[Literal['tagged-union']] + choices: Required[Dict[Hashable, CoreSchema]] + discriminator: Required[Union[str, List[Union[str, int]], List[List[Union[str, int]]], Callable[[Any], Hashable]]] + custom_error_type: str + custom_error_message: str + custom_error_context: Dict[str, Union[str, int, float]] + strict: bool + from_attributes: bool # default: True + ref: str + metadata: Any + serialization: SerSchema + + +def tagged_union_schema( + choices: Dict[Hashable, CoreSchema], + discriminator: str | list[str | int] | list[list[str | int]] | Callable[[Any], Hashable], + *, + custom_error_type: str | None = None, + custom_error_message: str | None = None, + custom_error_context: dict[str, int | str | float] | None = None, + strict: bool | None = None, + from_attributes: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> TaggedUnionSchema: + """ + Returns a schema that matches a tagged union value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + apple_schema = core_schema.typed_dict_schema( + { + 'foo': core_schema.typed_dict_field(core_schema.str_schema()), + 'bar': core_schema.typed_dict_field(core_schema.int_schema()), + } + ) + banana_schema = core_schema.typed_dict_schema( + { + 'foo': core_schema.typed_dict_field(core_schema.str_schema()), + 'spam': core_schema.typed_dict_field( + core_schema.list_schema(items_schema=core_schema.int_schema()) + ), + } + ) + schema = core_schema.tagged_union_schema( + choices={ + 'apple': apple_schema, + 'banana': banana_schema, + }, + discriminator='foo', + ) + v = SchemaValidator(schema) + assert v.validate_python({'foo': 'apple', 'bar': '123'}) == {'foo': 'apple', 'bar': 123} + assert v.validate_python({'foo': 'banana', 'spam': [1, 2, 3]}) == { + 'foo': 'banana', + 'spam': [1, 2, 3], + } + ``` + + Args: + choices: The schemas to match + When retrieving a schema from `choices` using the discriminator value, if the value is a str, + it should be fed back into the `choices` map until a schema is obtained + (This approach is to prevent multiple ownership of a single schema in Rust) + discriminator: The discriminator to use to determine the schema to use + * If `discriminator` is a str, it is the name of the attribute to use as the discriminator + * If `discriminator` is a list of int/str, it should be used as a "path" to access the discriminator + * If `discriminator` is a list of lists, each inner list is a path, and the first path that exists is used + * If `discriminator` is a callable, it should return the discriminator when called on the value to validate; + the callable can return `None` to indicate that there is no matching discriminator present on the input + custom_error_type: The custom error type to use if the validation fails + custom_error_message: The custom error message to use if the validation fails + custom_error_context: The custom error context to use if the validation fails + strict: Whether the underlying schemas should be validated with strict mode + from_attributes: Whether to use the attributes of the object to retrieve the discriminator value + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='tagged-union', + choices=choices, + discriminator=discriminator, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + strict=strict, + from_attributes=from_attributes, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ChainSchema(TypedDict, total=False): + type: Required[Literal['chain']] + steps: Required[List[CoreSchema]] + ref: str + metadata: Any + serialization: SerSchema + + +def chain_schema( + steps: list[CoreSchema], *, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None +) -> ChainSchema: + """ + Returns a schema that chains the provided validation schemas, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + ' world' + + fn_schema = core_schema.with_info_plain_validator_function(function=fn) + schema = core_schema.chain_schema( + [fn_schema, fn_schema, fn_schema, core_schema.str_schema()] + ) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello world world world' + ``` + + Args: + steps: The schemas to chain + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='chain', steps=steps, ref=ref, metadata=metadata, serialization=serialization) + + +class LaxOrStrictSchema(TypedDict, total=False): + type: Required[Literal['lax-or-strict']] + lax_schema: Required[CoreSchema] + strict_schema: Required[CoreSchema] + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def lax_or_strict_schema( + lax_schema: CoreSchema, + strict_schema: CoreSchema, + *, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> LaxOrStrictSchema: + """ + Returns a schema that uses the lax or strict schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + ' world' + + lax_schema = core_schema.int_schema(strict=False) + strict_schema = core_schema.int_schema(strict=True) + + schema = core_schema.lax_or_strict_schema( + lax_schema=lax_schema, strict_schema=strict_schema, strict=True + ) + v = SchemaValidator(schema) + assert v.validate_python(123) == 123 + + schema = core_schema.lax_or_strict_schema( + lax_schema=lax_schema, strict_schema=strict_schema, strict=False + ) + v = SchemaValidator(schema) + assert v.validate_python('123') == 123 + ``` + + Args: + lax_schema: The lax schema to use + strict_schema: The strict schema to use + strict: Whether the strict schema should be used + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='lax-or-strict', + lax_schema=lax_schema, + strict_schema=strict_schema, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class JsonOrPythonSchema(TypedDict, total=False): + type: Required[Literal['json-or-python']] + json_schema: Required[CoreSchema] + python_schema: Required[CoreSchema] + ref: str + metadata: Any + serialization: SerSchema + + +def json_or_python_schema( + json_schema: CoreSchema, + python_schema: CoreSchema, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> JsonOrPythonSchema: + """ + Returns a schema that uses the Json or Python schema depending on the input: + + ```py + from pydantic_core import SchemaValidator, ValidationError, core_schema + + v = SchemaValidator( + core_schema.json_or_python_schema( + json_schema=core_schema.int_schema(), + python_schema=core_schema.int_schema(strict=True), + ) + ) + + assert v.validate_json('"123"') == 123 + + try: + v.validate_python('123') + except ValidationError: + pass + else: + raise AssertionError('Validation should have failed') + ``` + + Args: + json_schema: The schema to use for Json inputs + python_schema: The schema to use for Python inputs + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='json-or-python', + json_schema=json_schema, + python_schema=python_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TypedDictField(TypedDict, total=False): + type: Required[Literal['typed-dict-field']] + schema: Required[CoreSchema] + required: bool + validation_alias: Union[str, List[Union[str, int]], List[List[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + metadata: Any + + +def typed_dict_field( + schema: CoreSchema, + *, + required: bool | None = None, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + metadata: Any = None, +) -> TypedDictField: + """ + Returns a schema that matches a typed dict field, e.g.: + + ```py + from pydantic_core import core_schema + + field = core_schema.typed_dict_field(schema=core_schema.int_schema(), required=True) + ``` + + Args: + schema: The schema to use for the field + required: Whether the field is required + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='typed-dict-field', + schema=schema, + required=required, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + metadata=metadata, + ) + + +class TypedDictSchema(TypedDict, total=False): + type: Required[Literal['typed-dict']] + fields: Required[Dict[str, TypedDictField]] + computed_fields: List[ComputedField] + strict: bool + extras_schema: CoreSchema + # all these values can be set via config, equivalent fields have `typed_dict_` prefix + extra_behavior: ExtraBehavior + total: bool # default: True + populate_by_name: bool # replaces `allow_population_by_field_name` in pydantic v1 + ref: str + metadata: Any + serialization: SerSchema + config: CoreConfig + + +def typed_dict_schema( + fields: Dict[str, TypedDictField], + *, + computed_fields: list[ComputedField] | None = None, + strict: bool | None = None, + extras_schema: CoreSchema | None = None, + extra_behavior: ExtraBehavior | None = None, + total: bool | None = None, + populate_by_name: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, + config: CoreConfig | None = None, +) -> TypedDictSchema: + """ + Returns a schema that matches a typed dict, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + wrapper_schema = core_schema.typed_dict_schema( + {'a': core_schema.typed_dict_field(core_schema.str_schema())} + ) + v = SchemaValidator(wrapper_schema) + assert v.validate_python({'a': 'hello'}) == {'a': 'hello'} + ``` + + Args: + fields: The fields to use for the typed dict + computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model + strict: Whether the typed dict is strict + extras_schema: The extra validator to use for the typed dict + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + extra_behavior: The extra behavior to use for the typed dict + total: Whether the typed dict is total + populate_by_name: Whether the typed dict should populate by name + serialization: Custom serialization schema + """ + return _dict_not_none( + type='typed-dict', + fields=fields, + computed_fields=computed_fields, + strict=strict, + extras_schema=extras_schema, + extra_behavior=extra_behavior, + total=total, + populate_by_name=populate_by_name, + ref=ref, + metadata=metadata, + serialization=serialization, + config=config, + ) + + +class ModelField(TypedDict, total=False): + type: Required[Literal['model-field']] + schema: Required[CoreSchema] + validation_alias: Union[str, List[Union[str, int]], List[List[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + frozen: bool + metadata: Any + + +def model_field( + schema: CoreSchema, + *, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + frozen: bool | None = None, + metadata: Any = None, +) -> ModelField: + """ + Returns a schema for a model field, e.g.: + + ```py + from pydantic_core import core_schema + + field = core_schema.model_field(schema=core_schema.int_schema()) + ``` + + Args: + schema: The schema to use for the field + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + frozen: Whether the field is frozen + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='model-field', + schema=schema, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + frozen=frozen, + metadata=metadata, + ) + + +class ModelFieldsSchema(TypedDict, total=False): + type: Required[Literal['model-fields']] + fields: Required[Dict[str, ModelField]] + model_name: str + computed_fields: List[ComputedField] + strict: bool + extras_schema: CoreSchema + # all these values can be set via config, equivalent fields have `typed_dict_` prefix + extra_behavior: ExtraBehavior + populate_by_name: bool # replaces `allow_population_by_field_name` in pydantic v1 + from_attributes: bool + ref: str + metadata: Any + serialization: SerSchema + + +def model_fields_schema( + fields: Dict[str, ModelField], + *, + model_name: str | None = None, + computed_fields: list[ComputedField] | None = None, + strict: bool | None = None, + extras_schema: CoreSchema | None = None, + extra_behavior: ExtraBehavior | None = None, + populate_by_name: bool | None = None, + from_attributes: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> ModelFieldsSchema: + """ + Returns a schema that matches a typed dict, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + wrapper_schema = core_schema.model_fields_schema( + {'a': core_schema.model_field(core_schema.str_schema())} + ) + v = SchemaValidator(wrapper_schema) + print(v.validate_python({'a': 'hello'})) + #> ({'a': 'hello'}, None, {'a'}) + ``` + + Args: + fields: The fields to use for the typed dict + model_name: The name of the model, used for error messages, defaults to "Model" + computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model + strict: Whether the typed dict is strict + extras_schema: The extra validator to use for the typed dict + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + extra_behavior: The extra behavior to use for the typed dict + populate_by_name: Whether the typed dict should populate by name + from_attributes: Whether the typed dict should be populated from attributes + serialization: Custom serialization schema + """ + return _dict_not_none( + type='model-fields', + fields=fields, + model_name=model_name, + computed_fields=computed_fields, + strict=strict, + extras_schema=extras_schema, + extra_behavior=extra_behavior, + populate_by_name=populate_by_name, + from_attributes=from_attributes, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ModelSchema(TypedDict, total=False): + type: Required[Literal['model']] + cls: Required[Type[Any]] + schema: Required[CoreSchema] + custom_init: bool + root_model: bool + post_init: str + revalidate_instances: Literal['always', 'never', 'subclass-instances'] # default: 'never' + strict: bool + frozen: bool + extra_behavior: ExtraBehavior + config: CoreConfig + ref: str + metadata: Any + serialization: SerSchema + + +def model_schema( + cls: Type[Any], + schema: CoreSchema, + *, + custom_init: bool | None = None, + root_model: bool | None = None, + post_init: str | None = None, + revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None, + strict: bool | None = None, + frozen: bool | None = None, + extra_behavior: ExtraBehavior | None = None, + config: CoreConfig | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> ModelSchema: + """ + A model schema generally contains a typed-dict schema. + It will run the typed dict validator, then create a new class + and set the dict and fields set returned from the typed dict validator + to `__dict__` and `__pydantic_fields_set__` respectively. + + Example: + + ```py + from pydantic_core import CoreConfig, SchemaValidator, core_schema + + class MyModel: + __slots__ = ( + '__dict__', + '__pydantic_fields_set__', + '__pydantic_extra__', + '__pydantic_private__', + ) + + schema = core_schema.model_schema( + cls=MyModel, + config=CoreConfig(str_max_length=5), + schema=core_schema.model_fields_schema( + fields={'a': core_schema.model_field(core_schema.str_schema())}, + ), + ) + v = SchemaValidator(schema) + assert v.isinstance_python({'a': 'hello'}) is True + assert v.isinstance_python({'a': 'too long'}) is False + ``` + + Args: + cls: The class to use for the model + schema: The schema to use for the model + custom_init: Whether the model has a custom init method + root_model: Whether the model is a `RootModel` + post_init: The call after init to use for the model + revalidate_instances: whether instances of models and dataclasses (including subclass instances) + should re-validate defaults to config.revalidate_instances, else 'never' + strict: Whether the model is strict + frozen: Whether the model is frozen + extra_behavior: The extra behavior to use for the model, used in serialization + config: The config to use for the model + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='model', + cls=cls, + schema=schema, + custom_init=custom_init, + root_model=root_model, + post_init=post_init, + revalidate_instances=revalidate_instances, + strict=strict, + frozen=frozen, + extra_behavior=extra_behavior, + config=config, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DataclassField(TypedDict, total=False): + type: Required[Literal['dataclass-field']] + name: Required[str] + schema: Required[CoreSchema] + kw_only: bool # default: True + init: bool # default: True + init_only: bool # default: False + frozen: bool # default: False + validation_alias: Union[str, List[Union[str, int]], List[List[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + metadata: Any + + +def dataclass_field( + name: str, + schema: CoreSchema, + *, + kw_only: bool | None = None, + init: bool | None = None, + init_only: bool | None = None, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + metadata: Any = None, + frozen: bool | None = None, +) -> DataclassField: + """ + Returns a schema for a dataclass field, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + field = core_schema.dataclass_field( + name='a', schema=core_schema.str_schema(), kw_only=False + ) + schema = core_schema.dataclass_args_schema('Foobar', [field]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hello'}) == ({'a': 'hello'}, None) + ``` + + Args: + name: The name to use for the argument parameter + schema: The schema to use for the argument parameter + kw_only: Whether the field can be set with a positional argument as well as a keyword argument + init: Whether the field should be validated during initialization + init_only: Whether the field should be omitted from `__dict__` and passed to `__post_init__` + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + metadata: Any other information you want to include with the schema, not used by pydantic-core + frozen: Whether the field is frozen + """ + return _dict_not_none( + type='dataclass-field', + name=name, + schema=schema, + kw_only=kw_only, + init=init, + init_only=init_only, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + metadata=metadata, + frozen=frozen, + ) + + +class DataclassArgsSchema(TypedDict, total=False): + type: Required[Literal['dataclass-args']] + dataclass_name: Required[str] + fields: Required[List[DataclassField]] + computed_fields: List[ComputedField] + populate_by_name: bool # default: False + collect_init_only: bool # default: False + ref: str + metadata: Any + serialization: SerSchema + extra_behavior: ExtraBehavior + + +def dataclass_args_schema( + dataclass_name: str, + fields: list[DataclassField], + *, + computed_fields: List[ComputedField] | None = None, + populate_by_name: bool | None = None, + collect_init_only: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, + extra_behavior: ExtraBehavior | None = None, +) -> DataclassArgsSchema: + """ + Returns a schema for validating dataclass arguments, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + field_a = core_schema.dataclass_field( + name='a', schema=core_schema.str_schema(), kw_only=False + ) + field_b = core_schema.dataclass_field( + name='b', schema=core_schema.bool_schema(), kw_only=False + ) + schema = core_schema.dataclass_args_schema('Foobar', [field_a, field_b]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hello', 'b': True}) == ({'a': 'hello', 'b': True}, None) + ``` + + Args: + dataclass_name: The name of the dataclass being validated + fields: The fields to use for the dataclass + computed_fields: Computed fields to use when serializing the dataclass + populate_by_name: Whether to populate by name + collect_init_only: Whether to collect init only fields into a dict to pass to `__post_init__` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + extra_behavior: How to handle extra fields + """ + return _dict_not_none( + type='dataclass-args', + dataclass_name=dataclass_name, + fields=fields, + computed_fields=computed_fields, + populate_by_name=populate_by_name, + collect_init_only=collect_init_only, + ref=ref, + metadata=metadata, + serialization=serialization, + extra_behavior=extra_behavior, + ) + + +class DataclassSchema(TypedDict, total=False): + type: Required[Literal['dataclass']] + cls: Required[Type[Any]] + schema: Required[CoreSchema] + fields: Required[List[str]] + cls_name: str + post_init: bool # default: False + revalidate_instances: Literal['always', 'never', 'subclass-instances'] # default: 'never' + strict: bool # default: False + frozen: bool # default False + ref: str + metadata: Any + serialization: SerSchema + slots: bool + config: CoreConfig + + +def dataclass_schema( + cls: Type[Any], + schema: CoreSchema, + fields: List[str], + *, + cls_name: str | None = None, + post_init: bool | None = None, + revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, + frozen: bool | None = None, + slots: bool | None = None, + config: CoreConfig | None = None, +) -> DataclassSchema: + """ + Returns a schema for a dataclass. As with `ModelSchema`, this schema can only be used as a field within + another schema, not as the root type. + + Args: + cls: The dataclass type, used to perform subclass checks + schema: The schema to use for the dataclass fields + fields: Fields of the dataclass, this is used in serialization and in validation during re-validation + and while validating assignment + cls_name: The name to use in error locs, etc; this is useful for generics (default: `cls.__name__`) + post_init: Whether to call `__post_init__` after validation + revalidate_instances: whether instances of models and dataclasses (including subclass instances) + should re-validate defaults to config.revalidate_instances, else 'never' + strict: Whether to require an exact instance of `cls` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + frozen: Whether the dataclass is frozen + slots: Whether `slots=True` on the dataclass, means each field is assigned independently, rather than + simply setting `__dict__`, default false + """ + return _dict_not_none( + type='dataclass', + cls=cls, + fields=fields, + cls_name=cls_name, + schema=schema, + post_init=post_init, + revalidate_instances=revalidate_instances, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + frozen=frozen, + slots=slots, + config=config, + ) + + +class ArgumentsParameter(TypedDict, total=False): + name: Required[str] + schema: Required[CoreSchema] + mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] # default positional_or_keyword + alias: Union[str, List[Union[str, int]], List[List[Union[str, int]]]] + + +def arguments_parameter( + name: str, + schema: CoreSchema, + *, + mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] | None = None, + alias: str | list[str | int] | list[list[str | int]] | None = None, +) -> ArgumentsParameter: + """ + Returns a schema that matches an argument parameter, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + schema = core_schema.arguments_schema([param]) + v = SchemaValidator(schema) + assert v.validate_python(('hello',)) == (('hello',), {}) + ``` + + Args: + name: The name to use for the argument parameter + schema: The schema to use for the argument parameter + mode: The mode to use for the argument parameter + alias: The alias to use for the argument parameter + """ + return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias) + + +class ArgumentsSchema(TypedDict, total=False): + type: Required[Literal['arguments']] + arguments_schema: Required[List[ArgumentsParameter]] + populate_by_name: bool + var_args_schema: CoreSchema + var_kwargs_schema: CoreSchema + ref: str + metadata: Any + serialization: SerSchema + + +def arguments_schema( + arguments: list[ArgumentsParameter], + *, + populate_by_name: bool | None = None, + var_args_schema: CoreSchema | None = None, + var_kwargs_schema: CoreSchema | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> ArgumentsSchema: + """ + Returns a schema that matches an arguments schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param_a = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + param_b = core_schema.arguments_parameter( + name='b', schema=core_schema.bool_schema(), mode='positional_only' + ) + schema = core_schema.arguments_schema([param_a, param_b]) + v = SchemaValidator(schema) + assert v.validate_python(('hello', True)) == (('hello', True), {}) + ``` + + Args: + arguments: The arguments to use for the arguments schema + populate_by_name: Whether to populate by name + var_args_schema: The variable args schema to use for the arguments schema + var_kwargs_schema: The variable kwargs schema to use for the arguments schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='arguments', + arguments_schema=arguments, + populate_by_name=populate_by_name, + var_args_schema=var_args_schema, + var_kwargs_schema=var_kwargs_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class CallSchema(TypedDict, total=False): + type: Required[Literal['call']] + arguments_schema: Required[CoreSchema] + function: Required[Callable[..., Any]] + function_name: str # default function.__name__ + return_schema: CoreSchema + ref: str + metadata: Any + serialization: SerSchema + + +def call_schema( + arguments: CoreSchema, + function: Callable[..., Any], + *, + function_name: str | None = None, + return_schema: CoreSchema | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> CallSchema: + """ + Returns a schema that matches an arguments schema, then calls a function, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param_a = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + param_b = core_schema.arguments_parameter( + name='b', schema=core_schema.bool_schema(), mode='positional_only' + ) + args_schema = core_schema.arguments_schema([param_a, param_b]) + + schema = core_schema.call_schema( + arguments=args_schema, + function=lambda a, b: a + str(not b), + return_schema=core_schema.str_schema(), + ) + v = SchemaValidator(schema) + assert v.validate_python((('hello', True))) == 'helloFalse' + ``` + + Args: + arguments: The arguments to use for the arguments schema + function: The function to use for the call schema + function_name: The function name to use for the call schema, if not provided `function.__name__` is used + return_schema: The return schema to use for the call schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='call', + arguments_schema=arguments, + function=function, + function_name=function_name, + return_schema=return_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class CustomErrorSchema(TypedDict, total=False): + type: Required[Literal['custom-error']] + schema: Required[CoreSchema] + custom_error_type: Required[str] + custom_error_message: str + custom_error_context: Dict[str, Union[str, int, float]] + ref: str + metadata: Any + serialization: SerSchema + + +def custom_error_schema( + schema: CoreSchema, + custom_error_type: str, + *, + custom_error_message: str | None = None, + custom_error_context: dict[str, Any] | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> CustomErrorSchema: + """ + Returns a schema that matches a custom error value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.custom_error_schema( + schema=core_schema.int_schema(), + custom_error_type='MyError', + custom_error_message='Error msg', + ) + v = SchemaValidator(schema) + v.validate_python(1) + ``` + + Args: + schema: The schema to use for the custom error schema + custom_error_type: The custom error type to use for the custom error schema + custom_error_message: The custom error message to use for the custom error schema + custom_error_context: The custom error context to use for the custom error schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='custom-error', + schema=schema, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class JsonSchema(TypedDict, total=False): + type: Required[Literal['json']] + schema: CoreSchema + ref: str + metadata: Any + serialization: SerSchema + + +def json_schema( + schema: CoreSchema | None = None, + *, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> JsonSchema: + """ + Returns a schema that matches a JSON value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + dict_schema = core_schema.model_fields_schema( + { + 'field_a': core_schema.model_field(core_schema.str_schema()), + 'field_b': core_schema.model_field(core_schema.bool_schema()), + }, + ) + + class MyModel: + __slots__ = ( + '__dict__', + '__pydantic_fields_set__', + '__pydantic_extra__', + '__pydantic_private__', + ) + field_a: str + field_b: bool + + json_schema = core_schema.json_schema(schema=dict_schema) + schema = core_schema.model_schema(cls=MyModel, schema=json_schema) + v = SchemaValidator(schema) + m = v.validate_python('{"field_a": "hello", "field_b": true}') + assert isinstance(m, MyModel) + ``` + + Args: + schema: The schema to use for the JSON schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='json', schema=schema, ref=ref, metadata=metadata, serialization=serialization) + + +class UrlSchema(TypedDict, total=False): + type: Required[Literal['url']] + max_length: int + allowed_schemes: List[str] + host_required: bool # default False + default_host: str + default_port: int + default_path: str + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def url_schema( + *, + max_length: int | None = None, + allowed_schemes: list[str] | None = None, + host_required: bool | None = None, + default_host: str | None = None, + default_port: int | None = None, + default_path: str | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> UrlSchema: + """ + Returns a schema that matches a URL value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.url_schema() + v = SchemaValidator(schema) + print(v.validate_python('https://example.com')) + #> https://example.com/ + ``` + + Args: + max_length: The maximum length of the URL + allowed_schemes: The allowed URL schemes + host_required: Whether the URL must have a host + default_host: The default host to use if the URL does not have a host + default_port: The default port to use if the URL does not have a port + default_path: The default path to use if the URL does not have a path + strict: Whether to use strict URL parsing + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='url', + max_length=max_length, + allowed_schemes=allowed_schemes, + host_required=host_required, + default_host=default_host, + default_port=default_port, + default_path=default_path, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class MultiHostUrlSchema(TypedDict, total=False): + type: Required[Literal['multi-host-url']] + max_length: int + allowed_schemes: List[str] + host_required: bool # default False + default_host: str + default_port: int + default_path: str + strict: bool + ref: str + metadata: Any + serialization: SerSchema + + +def multi_host_url_schema( + *, + max_length: int | None = None, + allowed_schemes: list[str] | None = None, + host_required: bool | None = None, + default_host: str | None = None, + default_port: int | None = None, + default_path: str | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: Any = None, + serialization: SerSchema | None = None, +) -> MultiHostUrlSchema: + """ + Returns a schema that matches a URL value with possibly multiple hosts, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.multi_host_url_schema() + v = SchemaValidator(schema) + print(v.validate_python('redis://localhost,0.0.0.0,127.0.0.1')) + #> redis://localhost,0.0.0.0,127.0.0.1 + ``` + + Args: + max_length: The maximum length of the URL + allowed_schemes: The allowed URL schemes + host_required: Whether the URL must have a host + default_host: The default host to use if the URL does not have a host + default_port: The default port to use if the URL does not have a port + default_path: The default path to use if the URL does not have a path + strict: Whether to use strict URL parsing + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='multi-host-url', + max_length=max_length, + allowed_schemes=allowed_schemes, + host_required=host_required, + default_host=default_host, + default_port=default_port, + default_path=default_path, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DefinitionsSchema(TypedDict, total=False): + type: Required[Literal['definitions']] + schema: Required[CoreSchema] + definitions: Required[List[CoreSchema]] + metadata: Any + serialization: SerSchema + + +def definitions_schema(schema: CoreSchema, definitions: list[CoreSchema]) -> DefinitionsSchema: + """ + Build a schema that contains both an inner schema and a list of definitions which can be used + within the inner schema. + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.definitions_schema( + core_schema.list_schema(core_schema.definition_reference_schema('foobar')), + [core_schema.int_schema(ref='foobar')], + ) + v = SchemaValidator(schema) + assert v.validate_python([1, 2, '3']) == [1, 2, 3] + ``` + + Args: + schema: The inner schema + definitions: List of definitions which can be referenced within inner schema + """ + return DefinitionsSchema(type='definitions', schema=schema, definitions=definitions) + + +class DefinitionReferenceSchema(TypedDict, total=False): + type: Required[Literal['definition-ref']] + schema_ref: Required[str] + ref: str + metadata: Any + serialization: SerSchema + + +def definition_reference_schema( + schema_ref: str, ref: str | None = None, metadata: Any = None, serialization: SerSchema | None = None +) -> DefinitionReferenceSchema: + """ + Returns a schema that points to a schema stored in "definitions", this is useful for nested recursive + models and also when you want to define validators separately from the main schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema_definition = core_schema.definition_reference_schema('list-schema') + schema = core_schema.definitions_schema( + schema=schema_definition, + definitions=[ + core_schema.list_schema(items_schema=schema_definition, ref='list-schema'), + ], + ) + v = SchemaValidator(schema) + assert v.validate_python([()]) == [[]] + ``` + + Args: + schema_ref: The schema ref to use for the definition reference schema + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='definition-ref', schema_ref=schema_ref, ref=ref, metadata=metadata, serialization=serialization + ) + + +MYPY = False +# See https://github.com/python/mypy/issues/14034 for details, in summary mypy is extremely slow to process this +# union which kills performance not just for pydantic, but even for code using pydantic +if not MYPY: + CoreSchema = Union[ + AnySchema, + NoneSchema, + BoolSchema, + IntSchema, + FloatSchema, + DecimalSchema, + StringSchema, + BytesSchema, + DateSchema, + TimeSchema, + DatetimeSchema, + TimedeltaSchema, + LiteralSchema, + EnumSchema, + IsInstanceSchema, + IsSubclassSchema, + CallableSchema, + ListSchema, + TupleSchema, + SetSchema, + FrozenSetSchema, + GeneratorSchema, + DictSchema, + AfterValidatorFunctionSchema, + BeforeValidatorFunctionSchema, + WrapValidatorFunctionSchema, + PlainValidatorFunctionSchema, + WithDefaultSchema, + NullableSchema, + UnionSchema, + TaggedUnionSchema, + ChainSchema, + LaxOrStrictSchema, + JsonOrPythonSchema, + TypedDictSchema, + ModelFieldsSchema, + ModelSchema, + DataclassArgsSchema, + DataclassSchema, + ArgumentsSchema, + CallSchema, + CustomErrorSchema, + JsonSchema, + UrlSchema, + MultiHostUrlSchema, + DefinitionsSchema, + DefinitionReferenceSchema, + UuidSchema, + ] +elif False: + CoreSchema: TypeAlias = Mapping[str, Any] + + +# to update this, call `pytest -k test_core_schema_type_literal` and copy the output +CoreSchemaType = Literal[ + 'any', + 'none', + 'bool', + 'int', + 'float', + 'decimal', + 'str', + 'bytes', + 'date', + 'time', + 'datetime', + 'timedelta', + 'literal', + 'enum', + 'is-instance', + 'is-subclass', + 'callable', + 'list', + 'tuple', + 'set', + 'frozenset', + 'generator', + 'dict', + 'function-after', + 'function-before', + 'function-wrap', + 'function-plain', + 'default', + 'nullable', + 'union', + 'tagged-union', + 'chain', + 'lax-or-strict', + 'json-or-python', + 'typed-dict', + 'model-fields', + 'model', + 'dataclass-args', + 'dataclass', + 'arguments', + 'call', + 'custom-error', + 'json', + 'url', + 'multi-host-url', + 'definitions', + 'definition-ref', + 'uuid', +] + +CoreSchemaFieldType = Literal['model-field', 'dataclass-field', 'typed-dict-field', 'computed-field'] + + +# used in _pydantic_core.pyi::PydanticKnownError +# to update this, call `pytest -k test_all_errors` and copy the output +ErrorType = Literal[ + 'no_such_attribute', + 'json_invalid', + 'json_type', + 'recursion_loop', + 'missing', + 'frozen_field', + 'frozen_instance', + 'extra_forbidden', + 'invalid_key', + 'get_attribute_error', + 'model_type', + 'model_attributes_type', + 'dataclass_type', + 'dataclass_exact_type', + 'none_required', + 'greater_than', + 'greater_than_equal', + 'less_than', + 'less_than_equal', + 'multiple_of', + 'finite_number', + 'too_short', + 'too_long', + 'iterable_type', + 'iteration_error', + 'string_type', + 'string_sub_type', + 'string_unicode', + 'string_too_short', + 'string_too_long', + 'string_pattern_mismatch', + 'enum', + 'dict_type', + 'mapping_type', + 'list_type', + 'tuple_type', + 'set_type', + 'bool_type', + 'bool_parsing', + 'int_type', + 'int_parsing', + 'int_parsing_size', + 'int_from_float', + 'float_type', + 'float_parsing', + 'bytes_type', + 'bytes_too_short', + 'bytes_too_long', + 'value_error', + 'assertion_error', + 'literal_error', + 'date_type', + 'date_parsing', + 'date_from_datetime_parsing', + 'date_from_datetime_inexact', + 'date_past', + 'date_future', + 'time_type', + 'time_parsing', + 'datetime_type', + 'datetime_parsing', + 'datetime_object_invalid', + 'datetime_from_date_parsing', + 'datetime_past', + 'datetime_future', + 'timezone_naive', + 'timezone_aware', + 'timezone_offset', + 'time_delta_type', + 'time_delta_parsing', + 'frozen_set_type', + 'is_instance_of', + 'is_subclass_of', + 'callable_type', + 'union_tag_invalid', + 'union_tag_not_found', + 'arguments_type', + 'missing_argument', + 'unexpected_keyword_argument', + 'missing_keyword_only_argument', + 'unexpected_positional_argument', + 'missing_positional_only_argument', + 'multiple_argument_values', + 'url_type', + 'url_parsing', + 'url_syntax_violation', + 'url_too_long', + 'url_scheme', + 'uuid_type', + 'uuid_parsing', + 'uuid_version', + 'decimal_type', + 'decimal_parsing', + 'decimal_max_digits', + 'decimal_max_places', + 'decimal_whole_digits', +] + + +def _dict_not_none(**kwargs: Any) -> Any: + return {k: v for k, v in kwargs.items() if v is not None} + + +############################################################################### +# All this stuff is deprecated by #980 and will be removed eventually +# They're kept because some code external code will be using them + + +@deprecated('`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.') +def field_before_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs): + warnings.warn( + '`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.', + DeprecationWarning, + ) + return with_info_before_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.') +def general_before_validator_function(*args, **kwargs): + warnings.warn( + '`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.', + DeprecationWarning, + ) + return with_info_before_validator_function(*args, **kwargs) + + +@deprecated('`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.') +def field_after_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs): + warnings.warn( + '`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.', + DeprecationWarning, + ) + return with_info_after_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.') +def general_after_validator_function(*args, **kwargs): + warnings.warn( + '`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.', + DeprecationWarning, + ) + return with_info_after_validator_function(*args, **kwargs) + + +@deprecated('`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.') +def field_wrap_validator_function( + function: WithInfoWrapValidatorFunction, field_name: str, schema: CoreSchema, **kwargs +): + warnings.warn( + '`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.', + DeprecationWarning, + ) + return with_info_wrap_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.') +def general_wrap_validator_function(*args, **kwargs): + warnings.warn( + '`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.', + DeprecationWarning, + ) + return with_info_wrap_validator_function(*args, **kwargs) + + +@deprecated('`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.') +def field_plain_validator_function(function: WithInfoValidatorFunction, field_name: str, **kwargs): + warnings.warn( + '`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.', + DeprecationWarning, + ) + return with_info_plain_validator_function(function, field_name=field_name, **kwargs) + + +@deprecated('`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.') +def general_plain_validator_function(*args, **kwargs): + warnings.warn( + '`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.', + DeprecationWarning, + ) + return with_info_plain_validator_function(*args, **kwargs) + + +_deprecated_import_lookup = { + 'FieldValidationInfo': ValidationInfo, + 'FieldValidatorFunction': WithInfoValidatorFunction, + 'GeneralValidatorFunction': WithInfoValidatorFunction, + 'FieldWrapValidatorFunction': WithInfoWrapValidatorFunction, +} + +if TYPE_CHECKING: + FieldValidationInfo = ValidationInfo + + +def __getattr__(attr_name: str) -> object: + new_attr = _deprecated_import_lookup.get(attr_name) + if new_attr is None: + raise AttributeError(f"module 'pydantic_core' has no attribute '{attr_name}'") + else: + import warnings + + msg = f'`{attr_name}` is deprecated, use `{new_attr.__name__}` instead.' + warnings.warn(msg, DeprecationWarning, stacklevel=1) + return new_attr diff --git a/.pythonlibs/lib/python3.10/site-packages/pydantic_core/py.typed b/.pythonlibs/lib/python3.10/site-packages/pydantic_core/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/LICENSE.txt new file mode 100644 index 0000000..99c19cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/LICENSE.txt @@ -0,0 +1,208 @@ +This work was derived from the 're' module of CPython 2.6 and CPython 3.1, +copyright (c) 1998-2001 by Secret Labs AB and licensed under CNRI's Python 1.6 +license. + +All additions and alterations are licensed under the Apache 2.0 License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Matthew Barnett + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/METADATA new file mode 100644 index 0000000..78a8563 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/METADATA @@ -0,0 +1,1076 @@ +Metadata-Version: 2.1 +Name: regex +Version: 2024.4.16 +Summary: Alternative regular expression module, to replace re. +Home-page: https://github.com/mrabarnett/mrab-regex +Author: Matthew Barnett +Author-email: regex@mrabarnett.plus.com +License: Apache Software License +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: General +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt + +Introduction +------------ + +This regex implementation is backwards-compatible with the standard 're' module, but offers additional functionality. + +Note +---- + +The re module's behaviour with zero-width matches changed in Python 3.7, and this module follows that behaviour when compiled for Python 3.7. + +Python 2 +-------- + +Python 2 is no longer supported. The last release that supported Python 2 was 2021.11.10. + +PyPy +---- + +This module is targeted at CPython. It expects that all codepoints are the same width, so it won't behave properly with PyPy outside U+0000..U+007F because PyPy stores strings as UTF-8. + +Multithreading +-------------- + +The regex module releases the GIL during matching on instances of the built-in (immutable) string classes, enabling other Python threads to run concurrently. It is also possible to force the regex module to release the GIL during matching by calling the matching methods with the keyword argument ``concurrent=True``. The behaviour is undefined if the string changes during matching, so use it *only* when it is guaranteed that that won't happen. + +Unicode +------- + +This module supports Unicode 15.1.0. Full Unicode case-folding is supported. + +Flags +----- + +There are 2 kinds of flag: scoped and global. Scoped flags can apply to only part of a pattern and can be turned on or off; global flags apply to the entire pattern and can only be turned on. + +The scoped flags are: ``ASCII (?a)``, ``FULLCASE (?f)``, ``IGNORECASE (?i)``, ``LOCALE (?L)``, ``MULTILINE (?m)``, ``DOTALL (?s)``, ``UNICODE (?u)``, ``VERBOSE (?x)``, ``WORD (?w)``. + +The global flags are: ``BESTMATCH (?b)``, ``ENHANCEMATCH (?e)``, ``POSIX (?p)``, ``REVERSE (?r)``, ``VERSION0 (?V0)``, ``VERSION1 (?V1)``. + +If neither the ``ASCII``, ``LOCALE`` nor ``UNICODE`` flag is specified, it will default to ``UNICODE`` if the regex pattern is a Unicode string and ``ASCII`` if it's a bytestring. + +The ``ENHANCEMATCH`` flag makes fuzzy matching attempt to improve the fit of the next match that it finds. + +The ``BESTMATCH`` flag makes fuzzy matching search for the best match instead of the next match. + +Old vs new behaviour +-------------------- + +In order to be compatible with the re module, this module has 2 behaviours: + +* **Version 0** behaviour (old behaviour, compatible with the re module): + + Please note that the re module's behaviour may change over time, and I'll endeavour to match that behaviour in version 0. + + * Indicated by the ``VERSION0`` flag. + + * Zero-width matches are not handled correctly in the re module before Python 3.7. The behaviour in those earlier versions is: + + * ``.split`` won't split a string at a zero-width match. + + * ``.sub`` will advance by one character after a zero-width match. + + * Inline flags apply to the entire pattern, and they can't be turned off. + + * Only simple sets are supported. + + * Case-insensitive matches in Unicode use simple case-folding by default. + +* **Version 1** behaviour (new behaviour, possibly different from the re module): + + * Indicated by the ``VERSION1`` flag. + + * Zero-width matches are handled correctly. + + * Inline flags apply to the end of the group or pattern, and they can be turned off. + + * Nested sets and set operations are supported. + + * Case-insensitive matches in Unicode use full case-folding by default. + +If no version is specified, the regex module will default to ``regex.DEFAULT_VERSION``. + +Case-insensitive matches in Unicode +----------------------------------- + +The regex module supports both simple and full case-folding for case-insensitive matches in Unicode. Use of full case-folding can be turned on using the ``FULLCASE`` flag. Please note that this flag affects how the ``IGNORECASE`` flag works; the ``FULLCASE`` flag itself does not turn on case-insensitive matching. + +Version 0 behaviour: the flag is off by default. + +Version 1 behaviour: the flag is on by default. + +Nested sets and set operations +------------------------------ + +It's not possible to support both simple sets, as used in the re module, and nested sets at the same time because of a difference in the meaning of an unescaped ``"["`` in a set. + +For example, the pattern ``[[a-z]--[aeiou]]`` is treated in the version 0 behaviour (simple sets, compatible with the re module) as: + +* Set containing "[" and the letters "a" to "z" + +* Literal "--" + +* Set containing letters "a", "e", "i", "o", "u" + +* Literal "]" + +but in the version 1 behaviour (nested sets, enhanced behaviour) as: + +* Set which is: + + * Set containing the letters "a" to "z" + +* but excluding: + + * Set containing the letters "a", "e", "i", "o", "u" + +Version 0 behaviour: only simple sets are supported. + +Version 1 behaviour: nested sets and set operations are supported. + +Notes on named groups +--------------------- + +All groups have a group number, starting from 1. + +Groups with the same group name will have the same group number, and groups with a different group name will have a different group number. + +The same name can be used by more than one group, with later captures 'overwriting' earlier captures. All the captures of the group will be available from the ``captures`` method of the match object. + +Group numbers will be reused across different branches of a branch reset, eg. ``(?|(first)|(second))`` has only group 1. If groups have different group names then they will, of course, have different group numbers, eg. ``(?|(?Pfirst)|(?Psecond))`` has group 1 ("foo") and group 2 ("bar"). + +In the regex ``(\s+)(?|(?P[A-Z]+)|(\w+) (?P[0-9]+)`` there are 2 groups: + +* ``(\s+)`` is group 1. + +* ``(?P[A-Z]+)`` is group 2, also called "foo". + +* ``(\w+)`` is group 2 because of the branch reset. + +* ``(?P[0-9]+)`` is group 2 because it's called "foo". + +If you want to prevent ``(\w+)`` from being group 2, you need to name it (different name, different group number). + +Additional features +------------------- + +The issue numbers relate to the Python bug tracker, except where listed otherwise. + +Added ``\p{Horiz_Space}`` and ``\p{Vert_Space}`` (`GitHub issue 477 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{Horiz_Space}`` or ``\p{H}`` matches horizontal whitespace and ``\p{Vert_Space}`` or ``\p{V}`` matches vertical whitespace. + +Added support for lookaround in conditional pattern (`Hg issue 163 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The test of a conditional pattern can be a lookaround. + +.. sourcecode:: python + + >>> regex.match(r'(?(?=\d)\d+|\w+)', '123abc') + + >>> regex.match(r'(?(?=\d)\d+|\w+)', 'abc123') + + +This is not quite the same as putting a lookaround in the first branch of a pair of alternatives. + +.. sourcecode:: python + + >>> print(regex.match(r'(?:(?=\d)\d+\b|\w+)', '123abc')) + + >>> print(regex.match(r'(?(?=\d)\d+\b|\w+)', '123abc')) + None + +In the first example, the lookaround matched, but the remainder of the first branch failed to match, and so the second branch was attempted, whereas in the second example, the lookaround matched, and the first branch failed to match, but the second branch was **not** attempted. + +Added POSIX matching (leftmost longest) (`Hg issue 150 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The POSIX standard for regex is to return the leftmost longest match. This can be turned on using the ``POSIX`` flag. + +.. sourcecode:: python + + >>> # Normal matching. + >>> regex.search(r'Mr|Mrs', 'Mrs') + + >>> regex.search(r'one(self)?(selfsufficient)?', 'oneselfsufficient') + + >>> # POSIX matching. + >>> regex.search(r'(?p)Mr|Mrs', 'Mrs') + + >>> regex.search(r'(?p)one(self)?(selfsufficient)?', 'oneselfsufficient') + + +Note that it will take longer to find matches because when it finds a match at a certain position, it won't return that immediately, but will keep looking to see if there's another longer match there. + +Added ``(?(DEFINE)...)`` (`Hg issue 152 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If there's no group called "DEFINE", then ... will be ignored except that any groups defined within it can be called and that the normal rules for numbering groups still apply. + +.. sourcecode:: python + + >>> regex.search(r'(?(DEFINE)(?P\d+)(?P\w+))(?&quant) (?&item)', '5 elephants') + + +Added ``(*PRUNE)``, ``(*SKIP)`` and ``(*FAIL)`` (`Hg issue 153 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(*PRUNE)`` discards the backtracking info up to that point. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*SKIP)`` is similar to ``(*PRUNE)``, except that it also sets where in the text the next attempt to match will start. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*FAIL)`` causes immediate backtracking. ``(*F)`` is a permitted abbreviation. + +Added ``\K`` (`Hg issue 151 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Keeps the part of the entire match after the position where ``\K`` occurred; the part before it is discarded. + +It does not affect what groups return. + +.. sourcecode:: python + + >>> m = regex.search(r'(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'cde' + >>> m[1] + 'abcde' + >>> + >>> m = regex.search(r'(?r)(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'bc' + >>> m[1] + 'bcdef' + +Added capture subscripting for ``expandf`` and ``subf``/``subfn`` (`Hg issue 133 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can use subscripting to get the captures of a repeated group. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w)+", "abc") + >>> m.expandf("{1}") + 'c' + >>> m.expandf("{1[0]} {1[1]} {1[2]}") + 'a b c' + >>> m.expandf("{1[-1]} {1[-2]} {1[-3]}") + 'c b a' + >>> + >>> m = regex.match(r"(?P\w)+", "abc") + >>> m.expandf("{letter}") + 'c' + >>> m.expandf("{letter[0]} {letter[1]} {letter[2]}") + 'a b c' + >>> m.expandf("{letter[-1]} {letter[-2]} {letter[-3]}") + 'c b a' + +Added support for referring to a group by number using ``(?P=...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is in addition to the existing ``\g<...>``. + +Fixed the handling of locale-sensitive regexes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``LOCALE`` flag is intended for legacy code and has limited support. You're still recommended to use Unicode instead. + +Added partial matches (`Hg issue 102 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A partial match is one that matches up to the end of string, but that string has been truncated and you want to know whether a complete match could be possible if the string had not been truncated. + +Partial matches are supported by ``match``, ``search``, ``fullmatch`` and ``finditer`` with the ``partial`` keyword argument. + +Match objects have a ``partial`` attribute, which is ``True`` if it's a partial match. + +For example, if you wanted a user to enter a 4-digit number and check it character by character as it was being entered: + +.. sourcecode:: python + + >>> pattern = regex.compile(r'\d{4}') + + >>> # Initially, nothing has been entered: + >>> print(pattern.fullmatch('', partial=True)) + + + >>> # An empty string is OK, but it's only a partial match. + >>> # The user enters a letter: + >>> print(pattern.fullmatch('a', partial=True)) + None + >>> # It'll never match. + + >>> # The user deletes that and enters a digit: + >>> print(pattern.fullmatch('1', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters 2 more digits: + >>> print(pattern.fullmatch('123', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters another digit: + >>> print(pattern.fullmatch('1234', partial=True)) + + >>> # It's a complete match. + + >>> # If the user enters another digit: + >>> print(pattern.fullmatch('12345', partial=True)) + None + >>> # It's no longer a match. + + >>> # This is a partial match: + >>> pattern.match('123', partial=True).partial + True + + >>> # This is a complete match: + >>> pattern.match('1233', partial=True).partial + False + +``*`` operator not working correctly with sub() (`Hg issue 106 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it's not clear how zero-width matches should be handled. For example, should ``.*`` match 0 characters directly after matching >0 characters? + +.. sourcecode:: python + + # Python 3.7 and later + >>> regex.sub('.*', 'x', 'test') + 'xx' + >>> regex.sub('.*?', '|', 'test') + '|||||||||' + + # Python 3.6 and earlier + >>> regex.sub('(?V0).*', 'x', 'test') + 'x' + >>> regex.sub('(?V1).*', 'x', 'test') + 'xx' + >>> regex.sub('(?V0).*?', '|', 'test') + '|t|e|s|t|' + >>> regex.sub('(?V1).*?', '|', 'test') + '|||||||||' + +Added ``capturesdict`` (`Hg issue 86 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``capturesdict`` is a combination of ``groupdict`` and ``captures``: + +``groupdict`` returns a dict of the named groups and the last capture of those groups. + +``captures`` returns a list of all the captures of a group + +``capturesdict`` returns a dict of the named groups and lists of all the captures of those groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.groupdict() + {'word': 'three', 'digits': '3'} + >>> m.captures("word") + ['one', 'two', 'three'] + >>> m.captures("digits") + ['1', '2', '3'] + >>> m.capturesdict() + {'word': ['one', 'two', 'three'], 'digits': ['1', '2', '3']} + +Added ``allcaptures`` and ``allspans`` (`Git issue 474 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``allcaptures`` returns a list of all the captures of all the groups. + +``allspans`` returns a list of all the spans of the all captures of all the groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.allcaptures() + (['one 1\ntwo 2\nthree 3\n'], ['one', 'two', 'three'], ['1', '2', '3']) + >>> m.allspans() + ([(0, 20)], [(0, 3), (6, 9), (12, 17)], [(4, 5), (10, 11), (18, 19)]) + +Allow duplicate names of groups (`Hg issue 87 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group names can be duplicated. + +.. sourcecode:: python + + >>> # With optional groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Only the second group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['second'] + >>> # Only the first group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or ") + >>> m.group("item") + 'first' + >>> m.captures("item") + ['first'] + >>> + >>> # With mandatory groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['', 'second'] + >>> # And yet again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", "first or ") + >>> m.group("item") + '' + >>> m.captures("item") + ['first', ''] + +Added ``fullmatch`` (`issue #16203 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``fullmatch`` behaves like ``match``, except that it must match all of the string. + +.. sourcecode:: python + + >>> print(regex.fullmatch(r"abc", "abc").span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "abcx")) + None + >>> print(regex.fullmatch(r"abc", "abcx", endpos=3).span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "xabcy", pos=1, endpos=4).span()) + (1, 4) + >>> + >>> regex.match(r"a.*?", "abcd").group(0) + 'a' + >>> regex.fullmatch(r"a.*?", "abcd").group(0) + 'abcd' + +Added ``subf`` and ``subfn`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``subf`` and ``subfn`` are alternatives to ``sub`` and ``subn`` respectively. When passed a replacement string, they treat it as a format string. + +.. sourcecode:: python + + >>> regex.subf(r"(\w+) (\w+)", "{0} => {2} {1}", "foo bar") + 'foo bar => bar foo' + >>> regex.subf(r"(?P\w+) (?P\w+)", "{word2} {word1}", "foo bar") + 'bar foo' + +Added ``expandf`` to match object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``expandf`` is an alternative to ``expand``. When passed a replacement string, it treats it as a format string. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w+) (\w+)", "foo bar") + >>> m.expandf("{0} => {2} {1}") + 'foo bar => bar foo' + >>> + >>> m = regex.match(r"(?P\w+) (?P\w+)", "foo bar") + >>> m.expandf("{word2} {word1}") + 'bar foo' + +Detach searched string +^^^^^^^^^^^^^^^^^^^^^^ + +A match object contains a reference to the string that was searched, via its ``string`` attribute. The ``detach_string`` method will 'detach' that string, making it available for garbage collection, which might save valuable memory if that string is very large. + +.. sourcecode:: python + + >>> m = regex.search(r"\w+", "Hello world") + >>> print(m.group()) + Hello + >>> print(m.string) + Hello world + >>> m.detach_string() + >>> print(m.group()) + Hello + >>> print(m.string) + None + +Recursive patterns (`Hg issue 27 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Recursive and repeated patterns are supported. + +``(?R)`` or ``(?0)`` tries to match the entire regex recursively. ``(?1)``, ``(?2)``, etc, try to match the relevant group. + +``(?&name)`` tries to match the named group. + +.. sourcecode:: python + + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Tarzan loves Jane").groups() + ('Tarzan',) + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Jane loves Tarzan").groups() + ('Jane',) + + >>> m = regex.search(r"(\w)(?:(?R)|(\w?))\1", "kayak") + >>> m.group(0, 1, 2) + ('kayak', 'k', None) + +The first two examples show how the subpattern within the group is reused, but is _not_ itself a group. In other words, ``"(Tarzan|Jane) loves (?1)"`` is equivalent to ``"(Tarzan|Jane) loves (?:Tarzan|Jane)"``. + +It's possible to backtrack into a recursed or repeated group. + +You can't call a group if there is more than one group with that group name or group number (``"ambiguous group reference"``). + +The alternative forms ``(?P>name)`` and ``(?P&name)`` are also supported. + +Full Unicode case-folding is supported +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In version 1 behaviour, the regex module uses full case-folding when performing case-insensitive matches in Unicode. + +.. sourcecode:: python + + >>> regex.match(r"(?iV1)strasse", "stra\N{LATIN SMALL LETTER SHARP S}e").span() + (0, 6) + >>> regex.match(r"(?iV1)stra\N{LATIN SMALL LETTER SHARP S}e", "STRASSE").span() + (0, 7) + +In version 0 behaviour, it uses simple case-folding for backward compatibility with the re module. + +Approximate "fuzzy" matching (`Hg issue 12 `_, `Hg issue 41 `_, `Hg issue 109 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Regex usually attempts an exact match, but sometimes an approximate, or "fuzzy", match is needed, for those cases where the text being searched may contain errors in the form of inserted, deleted or substituted characters. + +A fuzzy regex specifies which types of errors are permitted, and, optionally, either the minimum and maximum or only the maximum permitted number of each type. (You cannot specify only a minimum.) + +The 3 types of error are: + +* Insertion, indicated by "i" + +* Deletion, indicated by "d" + +* Substitution, indicated by "s" + +In addition, "e" indicates any type of error. + +The fuzziness of a regex item is specified between "{" and "}" after the item. + +Examples: + +* ``foo`` match "foo" exactly + +* ``(?:foo){i}`` match "foo", permitting insertions + +* ``(?:foo){d}`` match "foo", permitting deletions + +* ``(?:foo){s}`` match "foo", permitting substitutions + +* ``(?:foo){i,s}`` match "foo", permitting insertions and substitutions + +* ``(?:foo){e}`` match "foo", permitting errors + +If a certain type of error is specified, then any type not specified will **not** be permitted. + +In the following examples I'll omit the item and write only the fuzziness: + +* ``{d<=3}`` permit at most 3 deletions, but no other types + +* ``{i<=1,s<=2}`` permit at most 1 insertion and at most 2 substitutions, but no deletions + +* ``{1<=e<=3}`` permit at least 1 and at most 3 errors + +* ``{i<=2,d<=2,e<=3}`` permit at most 2 insertions, at most 2 deletions, at most 3 errors in total, but no substitutions + +It's also possible to state the costs of each type of error and the maximum permitted total cost. + +Examples: + +* ``{2i+2d+1s<=4}`` each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +* ``{i<=1,d<=1,s<=1,2i+2d+1s<=4}`` at most 1 insertion, at most 1 deletion, at most 1 substitution; each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +You can also use "<" instead of "<=" if you want an exclusive minimum or maximum. + +You can add a test to perform on a character that's substituted or inserted. + +Examples: + +* ``{s<=2:[a-z]}`` at most 2 substitutions, which must be in the character set ``[a-z]``. + +* ``{s<=2,i<=3:\d}`` at most 2 substitutions, at most 3 insertions, which must be digits. + +By default, fuzzy matching searches for the first match that meets the given constraints. The ``ENHANCEMATCH`` flag will cause it to attempt to improve the fit (i.e. reduce the number of errors) of the match that it has found. + +The ``BESTMATCH`` flag will make it search for the best match instead. + +Further examples to note: + +* ``regex.search("(dog){e}", "cat and dog")[1]`` returns ``"cat"`` because that matches ``"dog"`` with 3 errors (an unlimited number of errors is permitted). + +* ``regex.search("(dog){e<=1}", "cat and dog")[1]`` returns ``" dog"`` (with a leading space) because that matches ``"dog"`` with 1 error, which is within the limit. + +* ``regex.search("(?e)(dog){e<=1}", "cat and dog")[1]`` returns ``"dog"`` (without a leading space) because the fuzzy search matches ``" dog"`` with 1 error, which is within the limit, and the ``(?e)`` then it attempts a better fit. + +In the first two examples there are perfect matches later in the string, but in neither case is it the first possible match. + +The match object has an attribute ``fuzzy_counts`` which gives the total number of substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> # A 'raw' fuzzy match: + >>> regex.fullmatch(r"(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 1) + >>> # 0 substitutions, 0 insertions, 1 deletion. + + >>> # A better match might be possible if the ENHANCEMATCH flag used: + >>> regex.fullmatch(r"(?e)(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 0) + >>> # 0 substitutions, 0 insertions, 0 deletions. + +The match object also has an attribute ``fuzzy_changes`` which gives a tuple of the positions of the substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> m = regex.search('(fuu){i<=2,d<=2,e<=5}', 'anaconda foo bar') + >>> m + + >>> m.fuzzy_changes + ([], [7, 8], [10, 11]) + +What this means is that if the matched part of the string had been: + +.. sourcecode:: python + + 'anacondfuuoo bar' + +it would've been an exact match. + +However, there were insertions at positions 7 and 8: + +.. sourcecode:: python + + 'anaconda fuuoo bar' + ^^ + +and deletions at positions 10 and 11: + +.. sourcecode:: python + + 'anaconda f~~oo bar' + ^^ + +So the actual string was: + +.. sourcecode:: python + + 'anaconda foo bar' + +Named lists ``\L`` (`Hg issue 11 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are occasions where you may want to include a list (actually, a set) of options in a regex. + +One way is to build the pattern like this: + +.. sourcecode:: python + + >>> p = regex.compile(r"first|second|third|fourth|fifth") + +but if the list is large, parsing the resulting regex can take considerable time, and care must also be taken that the strings are properly escaped and properly ordered, for example, "cats" before "cat". + +The new alternative is to use a named list: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set) + +The order of the items is irrelevant, they are treated as a set. The named lists are available as the ``.named_lists`` attribute of the pattern object : + +.. sourcecode:: python + + >>> print(p.named_lists) + {'options': frozenset({'third', 'first', 'fifth', 'fourth', 'second'})} + +If there are any unused keyword arguments, ``ValueError`` will be raised unless you tell it otherwise: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set, other_options=[]) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=True) + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=False) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> + +Start and end of word +^^^^^^^^^^^^^^^^^^^^^ + +``\m`` matches at the start of a word. + +``\M`` matches at the end of a word. + +Compare with ``\b``, which matches at the start or end of a word. + +Unicode line separators +^^^^^^^^^^^^^^^^^^^^^^^ + +Normally the only line separator is ``\n`` (``\x0A``), but if the ``WORD`` flag is turned on then the line separators are ``\x0D\x0A``, ``\x0A``, ``\x0B``, ``\x0C`` and ``\x0D``, plus ``\x85``, ``\u2028`` and ``\u2029`` when working with Unicode. + +This affects the regex dot ``"."``, which, with the ``DOTALL`` flag turned off, matches any character except a line separator. It also affects the line anchors ``^`` and ``$`` (in multiline mode). + +Set operators +^^^^^^^^^^^^^ + +**Version 1 behaviour only** + +Set operators have been added, and a set ``[...]`` can include nested sets. + +The operators, in order of increasing precedence, are: + +* ``||`` for union ("x||y" means "x or y") + +* ``~~`` (double tilde) for symmetric difference ("x~~y" means "x or y, but not both") + +* ``&&`` for intersection ("x&&y" means "x and y") + +* ``--`` (double dash) for difference ("x--y" means "x but not y") + +Implicit union, ie, simple juxtaposition like in ``[ab]``, has the highest precedence. Thus, ``[ab&&cd]`` is the same as ``[[a||b]&&[c||d]]``. + +Examples: + +* ``[ab]`` # Set containing 'a' and 'b' + +* ``[a-z]`` # Set containing 'a' .. 'z' + +* ``[[a-z]--[qw]]`` # Set containing 'a' .. 'z', but not 'q' or 'w' + +* ``[a-z--qw]`` # Same as above + +* ``[\p{L}--QW]`` # Set containing all letters except 'Q' and 'W' + +* ``[\p{N}--[0-9]]`` # Set containing all numbers except '0' .. '9' + +* ``[\p{ASCII}&&\p{Letter}]`` # Set containing all characters which are ASCII and letter + +regex.escape (`issue #2650 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``special_only``. When True, only 'special' regex characters, such as '?', are escaped. + +.. sourcecode:: python + + >>> regex.escape("foo!?", special_only=False) + 'foo\\!\\?' + >>> regex.escape("foo!?", special_only=True) + 'foo!\\?' + +regex.escape (`Hg issue 249 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``literal_spaces``. When True, spaces are not escaped. + +.. sourcecode:: python + + >>> regex.escape("foo bar!?", literal_spaces=False) + 'foo\\ bar!\\?' + >>> regex.escape("foo bar!?", literal_spaces=True) + 'foo bar!\\?' + +Repeated captures (`issue #7132 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object has additional methods which return information on all the successful matches of a repeated group. These methods are: + +* ``matchobject.captures([group1, ...])`` + + * Returns a list of the strings matched in a group or groups. Compare with ``matchobject.group([group1, ...])``. + +* ``matchobject.starts([group])`` + + * Returns a list of the start positions. Compare with ``matchobject.start([group])``. + +* ``matchobject.ends([group])`` + + * Returns a list of the end positions. Compare with ``matchobject.end([group])``. + +* ``matchobject.spans([group])`` + + * Returns a list of the spans. Compare with ``matchobject.span([group])``. + +.. sourcecode:: python + + >>> m = regex.search(r"(\w{3})+", "123456789") + >>> m.group(1) + '789' + >>> m.captures(1) + ['123', '456', '789'] + >>> m.start(1) + 6 + >>> m.starts(1) + [0, 3, 6] + >>> m.end(1) + 9 + >>> m.ends(1) + [3, 6, 9] + >>> m.span(1) + (6, 9) + >>> m.spans(1) + [(0, 3), (3, 6), (6, 9)] + +Atomic grouping ``(?>...)`` (`issue #433030 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the following pattern subsequently fails, then the subpattern as a whole will fail. + +Possessive quantifiers +^^^^^^^^^^^^^^^^^^^^^^ + +``(?:...)?+`` ; ``(?:...)*+`` ; ``(?:...)++`` ; ``(?:...){min,max}+`` + +The subpattern is matched up to 'max' times. If the following pattern subsequently fails, then all the repeated subpatterns will fail as a whole. For example, ``(?:...)++`` is equivalent to ``(?>(?:...)+)``. + +Scoped flags (`issue #433028 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(?flags-flags:...)`` + +The flags will apply only to the subpattern. Flags can be turned on or off. + +Definition of 'word' character (`issue #1693050 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The definition of a 'word' character has been expanded for Unicode. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Variable-length lookbehind +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A lookbehind can match a variable-length string. + +Flags argument for regex.split, regex.sub and regex.subn (`issue #3482 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.split``, ``regex.sub`` and ``regex.subn`` support a 'flags' argument. + +Pos and endpos arguments for regex.sub and regex.subn +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.sub`` and ``regex.subn`` support 'pos' and 'endpos' arguments. + +'Overlapped' argument for regex.findall and regex.finditer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.findall`` and ``regex.finditer`` support an 'overlapped' flag which permits overlapped matches. + +Splititer +^^^^^^^^^ + +``regex.splititer`` has been added. It's a generator equivalent of ``regex.split``. + +Subscripting match objects for groups +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object accepts access to the groups via subscripting and slicing: + +.. sourcecode:: python + + >>> m = regex.search(r"(?P.*?)(?P\d+)(?P.*)", "pqr123stu") + >>> print(m["before"]) + pqr + >>> print(len(m)) + 4 + >>> print(m[:]) + ('pqr123stu', 'pqr', '123', 'stu') + +Named groups +^^^^^^^^^^^^ + +Groups can be named with ``(?...)`` as well as the existing ``(?P...)``. + +Group references +^^^^^^^^^^^^^^^^ + +Groups can be referenced within a pattern with ``\g``. This also allows there to be more than 99 groups. + +Named characters ``\N{name}`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Named characters are supported. Note that only those known by Python's Unicode database will be recognised. + +Unicode codepoint properties, including scripts and blocks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{property=value}``; ``\P{property=value}``; ``\p{value}`` ; ``\P{value}`` + +Many Unicode properties are supported, including blocks and scripts. ``\p{property=value}`` or ``\p{property:value}`` matches a character whose property ``property`` has value ``value``. The inverse of ``\p{property=value}`` is ``\P{property=value}`` or ``\p{^property=value}``. + +If the short form ``\p{value}`` is used, the properties are checked in the order: ``General_Category``, ``Script``, ``Block``, binary property: + +* ``Latin``, the 'Latin' script (``Script=Latin``). + +* ``BasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +* ``Alphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``Is`` indicates a script or binary property: + +* ``IsLatin``, the 'Latin' script (``Script=Latin``). + +* ``IsAlphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``In`` indicates a block property: + +* ``InBasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +POSIX character classes +^^^^^^^^^^^^^^^^^^^^^^^ + +``[[:alpha:]]``; ``[[:^alpha:]]`` + +POSIX character classes are supported. These are normally treated as an alternative form of ``\p{...}``. + +The exceptions are ``alnum``, ``digit``, ``punct`` and ``xdigit``, whose definitions are different from those of Unicode. + +``[[:alnum:]]`` is equivalent to ``\p{posix_alnum}``. + +``[[:digit:]]`` is equivalent to ``\p{posix_digit}``. + +``[[:punct:]]`` is equivalent to ``\p{posix_punct}``. + +``[[:xdigit:]]`` is equivalent to ``\p{posix_xdigit}``. + +Search anchor ``\G`` +^^^^^^^^^^^^^^^^^^^^ + +A search anchor has been added. It matches at the position where each search started/continued and can be used for contiguous matches or in negative variable-length lookbehinds to limit how far back the lookbehind goes: + +.. sourcecode:: python + + >>> regex.findall(r"\w{2}", "abcd ef") + ['ab', 'cd', 'ef'] + >>> regex.findall(r"\G\w{2}", "abcd ef") + ['ab', 'cd'] + +* The search starts at position 0 and matches 'ab'. + +* The search continues at position 2 and matches 'cd'. + +* The search continues at position 4 and fails to match any letters. + +* The anchor stops the search start position from being advanced, so there are no more results. + +Reverse searching +^^^^^^^^^^^^^^^^^ + +Searches can also work backwards: + +.. sourcecode:: python + + >>> regex.findall(r".", "abc") + ['a', 'b', 'c'] + >>> regex.findall(r"(?r).", "abc") + ['c', 'b', 'a'] + +Note that the result of a reverse search is not necessarily the reverse of a forward search: + +.. sourcecode:: python + + >>> regex.findall(r"..", "abcde") + ['ab', 'cd'] + >>> regex.findall(r"(?r)..", "abcde") + ['de', 'bc'] + +Matching a single grapheme ``\X`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The grapheme matcher is supported. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Branch reset ``(?|...|...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group numbers will be reused across the alternatives, but groups with different names will have different group numbers. + +.. sourcecode:: python + + >>> regex.match(r"(?|(first)|(second))", "first").groups() + ('first',) + >>> regex.match(r"(?|(first)|(second))", "second").groups() + ('second',) + +Note that there is only one group. + +Default Unicode word boundary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``WORD`` flag changes the definition of a 'word boundary' to that of a default Unicode word boundary. This applies to ``\b`` and ``\B``. + +Timeout +^^^^^^^ + +The matching methods and functions support timeouts. The timeout (in seconds) applies to the entire operation: + +.. sourcecode:: python + + >>> from time import sleep + >>> + >>> def fast_replace(m): + ... return 'X' + ... + >>> def slow_replace(m): + ... sleep(0.5) + ... return 'X' + ... + >>> regex.sub(r'[a-z]', fast_replace, 'abcde', timeout=2) + 'XXXXX' + >>> regex.sub(r'[a-z]', slow_replace, 'abcde', timeout=2) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 278, in sub + return pat.sub(repl, string, count, pos, endpos, concurrent, timeout) + TimeoutError: regex timed out diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/RECORD new file mode 100644 index 0000000..3598fa8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/RECORD @@ -0,0 +1,15 @@ +regex-2024.4.16.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +regex-2024.4.16.dist-info/LICENSE.txt,sha256=v_Ve9M3MjBTOJZ-OirYOJkQYRA1jNfTcE4Jz-9UGFE0,11584 +regex-2024.4.16.dist-info/METADATA,sha256=WlT-7eS0fPAvXemL05dNYWE0jPINGPTvkmKI7KCgRzo,40896 +regex-2024.4.16.dist-info/RECORD,, +regex-2024.4.16.dist-info/WHEEL,sha256=CzQQWV-lNyM92gr3iaBk8dvO35YDHRxgzkZ-dxumUIM,152 +regex-2024.4.16.dist-info/top_level.txt,sha256=aQmiDMhNTF26cCK4_7D-qaVvhbxClG0wyCTnEhkzYBs,6 +regex/__init__.py,sha256=9slNQEb4SCZ9LncNzcQvqmkyxXlcOAF7QwAwigxWjsw,65 +regex/__pycache__/__init__.cpython-310.pyc,, +regex/__pycache__/_regex_core.cpython-310.pyc,, +regex/__pycache__/regex.cpython-310.pyc,, +regex/__pycache__/test_regex.cpython-310.pyc,, +regex/_regex.cpython-310-x86_64-linux-gnu.so,sha256=eDasy28Zqt08KlBmrPsvhvzf9RC7bT77ODLqPybkzBM,2503320 +regex/_regex_core.py,sha256=SJeeGyKV-M_BjGU6-HksYUUjgjj7qeHnIjnLp2tbp3Q,140971 +regex/regex.py,sha256=XWrQocuQ6i5spKu0DCr4cV8jucGoF6TwXJLvkHeyEqo,32683 +regex/test_regex.py,sha256=Xh3CMiZE9Q6MV45wv0HNyNMLkU3Q5sGOPqJxErqjE0c,219821 diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/WHEEL new file mode 100644 index 0000000..9bb86cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/top_level.txt new file mode 100644 index 0000000..4f9256d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex-2024.4.16.dist-info/top_level.txt @@ -0,0 +1 @@ +regex diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/__init__.py b/.pythonlibs/lib/python3.10/site-packages/regex/__init__.py new file mode 100644 index 0000000..eb06564 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex/__init__.py @@ -0,0 +1,3 @@ +from .regex import * +from . import regex +__all__ = regex.__all__ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..be52002 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/_regex_core.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/_regex_core.cpython-310.pyc new file mode 100644 index 0000000..f8f9bb7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/_regex_core.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/regex.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/regex.cpython-310.pyc new file mode 100644 index 0000000..f129c84 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/regex.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/test_regex.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/test_regex.cpython-310.pyc new file mode 100644 index 0000000..8d62844 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/regex/__pycache__/test_regex.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/_regex.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/regex/_regex.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..1ea40d4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/regex/_regex.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/_regex_core.py b/.pythonlibs/lib/python3.10/site-packages/regex/_regex_core.py new file mode 100644 index 0000000..550e922 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex/_regex_core.py @@ -0,0 +1,4495 @@ +# +# Secret Labs' Regular Expression Engine core module +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# +# 2010-01-16 mrab Python front-end re-written and extended + +import enum +import string +import unicodedata +from collections import defaultdict + +import regex._regex as _regex + +__all__ = ["A", "ASCII", "B", "BESTMATCH", "D", "DEBUG", "E", "ENHANCEMATCH", + "F", "FULLCASE", "I", "IGNORECASE", "L", "LOCALE", "M", "MULTILINE", "P", + "POSIX", "R", "REVERSE", "S", "DOTALL", "T", "TEMPLATE", "U", "UNICODE", + "V0", "VERSION0", "V1", "VERSION1", "W", "WORD", "X", "VERBOSE", "error", + "Scanner", "RegexFlag"] + +# The regex exception. +class error(Exception): + """Exception raised for invalid regular expressions. + + Attributes: + + msg: The unformatted error message + pattern: The regular expression pattern + pos: The position in the pattern where compilation failed, or None + lineno: The line number where compilation failed, unless pos is None + colno: The column number where compilation failed, unless pos is None + """ + + def __init__(self, message, pattern=None, pos=None): + newline = '\n' if isinstance(pattern, str) else b'\n' + self.msg = message + self.pattern = pattern + self.pos = pos + if pattern is not None and pos is not None: + self.lineno = pattern.count(newline, 0, pos) + 1 + self.colno = pos - pattern.rfind(newline, 0, pos) + + message = "{} at position {}".format(message, pos) + + if newline in pattern: + message += " (line {}, column {})".format(self.lineno, + self.colno) + + Exception.__init__(self, message) + +# The exception for when a positional flag has been turned on in the old +# behaviour. +class _UnscopedFlagSet(Exception): + pass + +# The exception for when parsing fails and we want to try something else. +class ParseError(Exception): + pass + +# The exception for when there isn't a valid first set. +class _FirstSetError(Exception): + pass + +# Flags. +class RegexFlag(enum.IntFlag): + A = ASCII = 0x80 # Assume ASCII locale. + B = BESTMATCH = 0x1000 # Best fuzzy match. + D = DEBUG = 0x200 # Print parsed pattern. + E = ENHANCEMATCH = 0x8000 # Attempt to improve the fit after finding the first + # fuzzy match. + F = FULLCASE = 0x4000 # Unicode full case-folding. + I = IGNORECASE = 0x2 # Ignore case. + L = LOCALE = 0x4 # Assume current 8-bit locale. + M = MULTILINE = 0x8 # Make anchors look for newline. + P = POSIX = 0x10000 # POSIX-style matching (leftmost longest). + R = REVERSE = 0x400 # Search backwards. + S = DOTALL = 0x10 # Make dot match newline. + U = UNICODE = 0x20 # Assume Unicode locale. + V0 = VERSION0 = 0x2000 # Old legacy behaviour. + V1 = VERSION1 = 0x100 # New enhanced behaviour. + W = WORD = 0x800 # Default Unicode word breaks. + X = VERBOSE = 0x40 # Ignore whitespace and comments. + T = TEMPLATE = 0x1 # Template (present because re module has it). + + def __repr__(self): + if self._name_ is not None: + return 'regex.%s' % self._name_ + + value = self._value_ + members = [] + negative = value < 0 + + if negative: + value = ~value + + for m in self.__class__: + if value & m._value_: + value &= ~m._value_ + members.append('regex.%s' % m._name_) + + if value: + members.append(hex(value)) + + res = '|'.join(members) + + if negative: + if len(members) > 1: + res = '~(%s)' % res + else: + res = '~%s' % res + + return res + + __str__ = object.__str__ + +globals().update(RegexFlag.__members__) + +DEFAULT_VERSION = VERSION1 + +_ALL_VERSIONS = VERSION0 | VERSION1 +_ALL_ENCODINGS = ASCII | LOCALE | UNICODE + +# The default flags for the various versions. +DEFAULT_FLAGS = {VERSION0: 0, VERSION1: FULLCASE} + +# The mask for the flags. +GLOBAL_FLAGS = (_ALL_VERSIONS | BESTMATCH | DEBUG | ENHANCEMATCH | POSIX | + REVERSE) +SCOPED_FLAGS = (FULLCASE | IGNORECASE | MULTILINE | DOTALL | WORD | VERBOSE | + _ALL_ENCODINGS) + +ALPHA = frozenset(string.ascii_letters) +DIGITS = frozenset(string.digits) +ALNUM = ALPHA | DIGITS +OCT_DIGITS = frozenset(string.octdigits) +HEX_DIGITS = frozenset(string.hexdigits) +SPECIAL_CHARS = frozenset("()|?*+{^$.[\\#") | frozenset([""]) +NAMED_CHAR_PART = ALNUM | frozenset(" -") +PROPERTY_NAME_PART = ALNUM | frozenset(" &_-.") +SET_OPS = ("||", "~~", "&&", "--") + +# The width of the code words inside the regex engine. +BYTES_PER_CODE = _regex.get_code_size() +BITS_PER_CODE = BYTES_PER_CODE * 8 + +# The repeat count which represents infinity. +UNLIMITED = (1 << BITS_PER_CODE) - 1 + +# The regular expression flags. +REGEX_FLAGS = {"a": ASCII, "b": BESTMATCH, "e": ENHANCEMATCH, "f": FULLCASE, + "i": IGNORECASE, "L": LOCALE, "m": MULTILINE, "p": POSIX, "r": REVERSE, + "s": DOTALL, "u": UNICODE, "V0": VERSION0, "V1": VERSION1, "w": WORD, "x": + VERBOSE} + +# The case flags. +CASE_FLAGS = FULLCASE | IGNORECASE +NOCASE = 0 +FULLIGNORECASE = FULLCASE | IGNORECASE + +FULL_CASE_FOLDING = UNICODE | FULLIGNORECASE + +CASE_FLAGS_COMBINATIONS = {0: 0, FULLCASE: 0, IGNORECASE: IGNORECASE, + FULLIGNORECASE: FULLIGNORECASE} + +# The number of digits in hexadecimal escapes. +HEX_ESCAPES = {"x": 2, "u": 4, "U": 8} + +# The names of the opcodes. +OPCODES = """ +FAILURE +SUCCESS +ANY +ANY_ALL +ANY_ALL_REV +ANY_REV +ANY_U +ANY_U_REV +ATOMIC +BOUNDARY +BRANCH +CALL_REF +CHARACTER +CHARACTER_IGN +CHARACTER_IGN_REV +CHARACTER_REV +CONDITIONAL +DEFAULT_BOUNDARY +DEFAULT_END_OF_WORD +DEFAULT_START_OF_WORD +END +END_OF_LINE +END_OF_LINE_U +END_OF_STRING +END_OF_STRING_LINE +END_OF_STRING_LINE_U +END_OF_WORD +FUZZY +GRAPHEME_BOUNDARY +GREEDY_REPEAT +GROUP +GROUP_CALL +GROUP_EXISTS +KEEP +LAZY_REPEAT +LOOKAROUND +NEXT +PROPERTY +PROPERTY_IGN +PROPERTY_IGN_REV +PROPERTY_REV +PRUNE +RANGE +RANGE_IGN +RANGE_IGN_REV +RANGE_REV +REF_GROUP +REF_GROUP_FLD +REF_GROUP_FLD_REV +REF_GROUP_IGN +REF_GROUP_IGN_REV +REF_GROUP_REV +SEARCH_ANCHOR +SET_DIFF +SET_DIFF_IGN +SET_DIFF_IGN_REV +SET_DIFF_REV +SET_INTER +SET_INTER_IGN +SET_INTER_IGN_REV +SET_INTER_REV +SET_SYM_DIFF +SET_SYM_DIFF_IGN +SET_SYM_DIFF_IGN_REV +SET_SYM_DIFF_REV +SET_UNION +SET_UNION_IGN +SET_UNION_IGN_REV +SET_UNION_REV +SKIP +START_OF_LINE +START_OF_LINE_U +START_OF_STRING +START_OF_WORD +STRING +STRING_FLD +STRING_FLD_REV +STRING_IGN +STRING_IGN_REV +STRING_REV +FUZZY_EXT +""" + +# Define the opcodes in a namespace. +class Namespace: + pass + +OP = Namespace() +for i, op in enumerate(OPCODES.split()): + setattr(OP, op, i) + +def _shrink_cache(cache_dict, args_dict, locale_sensitive, max_length, divisor=5): + """Make room in the given cache. + + Args: + cache_dict: The cache dictionary to modify. + args_dict: The dictionary of named list args used by patterns. + max_length: Maximum # of entries in cache_dict before it is shrunk. + divisor: Cache will shrink to max_length - 1/divisor*max_length items. + """ + # Toss out a fraction of the entries at random to make room for new ones. + # A random algorithm was chosen as opposed to simply cache_dict.popitem() + # as popitem could penalize the same regular expression repeatedly based + # on its internal hash value. Being random should spread the cache miss + # love around. + cache_keys = tuple(cache_dict.keys()) + overage = len(cache_keys) - max_length + if overage < 0: + # Cache is already within limits. Normally this should not happen + # but it could due to multithreading. + return + + number_to_toss = max_length // divisor + overage + + # The import is done here to avoid a circular dependency. + import random + if not hasattr(random, 'sample'): + # Do nothing while resolving the circular dependency: + # re->random->warnings->tokenize->string->re + return + + for doomed_key in random.sample(cache_keys, number_to_toss): + try: + del cache_dict[doomed_key] + except KeyError: + # Ignore problems if the cache changed from another thread. + pass + + # Rebuild the arguments and locale-sensitivity dictionaries. + args_dict.clear() + sensitivity_dict = {} + for pattern, pattern_type, flags, args, default_version, locale in tuple(cache_dict): + args_dict[pattern, pattern_type, flags, default_version, locale] = args + try: + sensitivity_dict[pattern_type, pattern] = locale_sensitive[pattern_type, pattern] + except KeyError: + pass + + locale_sensitive.clear() + locale_sensitive.update(sensitivity_dict) + +def _fold_case(info, string): + "Folds the case of a string." + flags = info.flags + if (flags & _ALL_ENCODINGS) == 0: + flags |= info.guess_encoding + + return _regex.fold_case(flags, string) + +def is_cased_i(info, char): + "Checks whether a character is cased." + return len(_regex.get_all_cases(info.flags, char)) > 1 + +def is_cased_f(flags, char): + "Checks whether a character is cased." + return len(_regex.get_all_cases(flags, char)) > 1 + +def _compile_firstset(info, fs): + "Compiles the firstset for the pattern." + reverse = bool(info.flags & REVERSE) + fs = _check_firstset(info, reverse, fs) + if not fs: + return [] + + # Compile the firstset. + return fs.compile(reverse) + +def _check_firstset(info, reverse, fs): + "Checks the firstset for the pattern." + if not fs or None in fs: + return None + + # If we ignore the case, for simplicity we won't build a firstset. + members = set() + case_flags = NOCASE + for i in fs: + if isinstance(i, Character) and not i.positive: + return None + +# if i.case_flags: +# if isinstance(i, Character): +# if is_cased_i(info, i.value): +# return [] +# elif isinstance(i, SetBase): +# return [] + case_flags |= i.case_flags + members.add(i.with_flags(case_flags=NOCASE)) + + if case_flags == (FULLCASE | IGNORECASE): + return None + + # Build the firstset. + fs = SetUnion(info, list(members), case_flags=case_flags & ~FULLCASE, + zerowidth=True) + fs = fs.optimise(info, reverse, in_set=True) + + return fs + +def _flatten_code(code): + "Flattens the code from a list of tuples." + flat_code = [] + for c in code: + flat_code.extend(c) + + return flat_code + +def make_case_flags(info): + "Makes the case flags." + flags = info.flags & CASE_FLAGS + + # Turn off FULLCASE if ASCII is turned on. + if info.flags & ASCII: + flags &= ~FULLCASE + + return flags + +def make_character(info, value, in_set=False): + "Makes a character literal." + if in_set: + # A character set is built case-sensitively. + return Character(value) + + return Character(value, case_flags=make_case_flags(info)) + +def make_ref_group(info, name, position): + "Makes a group reference." + return RefGroup(info, name, position, case_flags=make_case_flags(info)) + +def make_string_set(info, name): + "Makes a string set." + return StringSet(info, name, case_flags=make_case_flags(info)) + +def make_property(info, prop, in_set): + "Makes a property." + if in_set: + return prop + + return prop.with_flags(case_flags=make_case_flags(info)) + +def _parse_pattern(source, info): + "Parses a pattern, eg. 'a|b|c'." + branches = [parse_sequence(source, info)] + while source.match("|"): + branches.append(parse_sequence(source, info)) + + if len(branches) == 1: + return branches[0] + return Branch(branches) + +def parse_sequence(source, info): + "Parses a sequence, eg. 'abc'." + sequence = [None] + case_flags = make_case_flags(info) + while True: + saved_pos = source.pos + ch = source.get() + if ch in SPECIAL_CHARS: + if ch in ")|": + # The end of a sequence. At the end of the pattern ch is "". + source.pos = saved_pos + break + elif ch == "\\": + # An escape sequence outside a set. + sequence.append(parse_escape(source, info, False)) + elif ch == "(": + # A parenthesised subpattern or a flag. + element = parse_paren(source, info) + if element is None: + case_flags = make_case_flags(info) + else: + sequence.append(element) + elif ch == ".": + # Any character. + if info.flags & DOTALL: + sequence.append(AnyAll()) + elif info.flags & WORD: + sequence.append(AnyU()) + else: + sequence.append(Any()) + elif ch == "[": + # A character set. + sequence.append(parse_set(source, info)) + elif ch == "^": + # The start of a line or the string. + if info.flags & MULTILINE: + if info.flags & WORD: + sequence.append(StartOfLineU()) + else: + sequence.append(StartOfLine()) + else: + sequence.append(StartOfString()) + elif ch == "$": + # The end of a line or the string. + if info.flags & MULTILINE: + if info.flags & WORD: + sequence.append(EndOfLineU()) + else: + sequence.append(EndOfLine()) + else: + if info.flags & WORD: + sequence.append(EndOfStringLineU()) + else: + sequence.append(EndOfStringLine()) + elif ch in "?*+{": + # Looks like a quantifier. + counts = parse_quantifier(source, info, ch) + if counts: + # It _is_ a quantifier. + apply_quantifier(source, info, counts, case_flags, ch, + saved_pos, sequence) + sequence.append(None) + else: + # It's not a quantifier. Maybe it's a fuzzy constraint. + constraints = parse_fuzzy(source, info, ch, case_flags) + if constraints: + # It _is_ a fuzzy constraint. + apply_constraint(source, info, constraints, case_flags, + saved_pos, sequence) + sequence.append(None) + else: + # The element was just a literal. + sequence.append(Character(ord(ch), + case_flags=case_flags)) + else: + # A literal. + sequence.append(Character(ord(ch), case_flags=case_flags)) + else: + # A literal. + sequence.append(Character(ord(ch), case_flags=case_flags)) + + sequence = [item for item in sequence if item is not None] + return Sequence(sequence) + +def apply_quantifier(source, info, counts, case_flags, ch, saved_pos, + sequence): + element = sequence.pop() + if element is None: + if sequence: + raise error("multiple repeat", source.string, saved_pos) + raise error("nothing to repeat", source.string, saved_pos) + + if isinstance(element, (GreedyRepeat, LazyRepeat, PossessiveRepeat)): + raise error("multiple repeat", source.string, saved_pos) + + min_count, max_count = counts + saved_pos = source.pos + ch = source.get() + if ch == "?": + # The "?" suffix that means it's a lazy repeat. + repeated = LazyRepeat + elif ch == "+": + # The "+" suffix that means it's a possessive repeat. + repeated = PossessiveRepeat + else: + # No suffix means that it's a greedy repeat. + source.pos = saved_pos + repeated = GreedyRepeat + + # Ignore the quantifier if it applies to a zero-width item or the number of + # repeats is fixed at 1. + if not element.is_empty() and (min_count != 1 or max_count != 1): + element = repeated(element, min_count, max_count) + + sequence.append(element) + +def apply_constraint(source, info, constraints, case_flags, saved_pos, + sequence): + element = sequence.pop() + if element is None: + raise error("nothing for fuzzy constraint", source.string, saved_pos) + + # If a group is marked as fuzzy then put all of the fuzzy part in the + # group. + if isinstance(element, Group): + element.subpattern = Fuzzy(element.subpattern, constraints) + sequence.append(element) + else: + sequence.append(Fuzzy(element, constraints)) + +_QUANTIFIERS = {"?": (0, 1), "*": (0, None), "+": (1, None)} + +def parse_quantifier(source, info, ch): + "Parses a quantifier." + q = _QUANTIFIERS.get(ch) + if q: + # It's a quantifier. + return q + + if ch == "{": + # Looks like a limited repeated element, eg. 'a{2,3}'. + counts = parse_limited_quantifier(source) + if counts: + return counts + + return None + +def is_above_limit(count): + "Checks whether a count is above the maximum." + return count is not None and count >= UNLIMITED + +def parse_limited_quantifier(source): + "Parses a limited quantifier." + saved_pos = source.pos + min_count = parse_count(source) + if source.match(","): + max_count = parse_count(source) + + # No minimum means 0 and no maximum means unlimited. + min_count = int(min_count or 0) + max_count = int(max_count) if max_count else None + else: + if not min_count: + source.pos = saved_pos + return None + + min_count = max_count = int(min_count) + + if not source.match ("}"): + source.pos = saved_pos + return None + + if is_above_limit(min_count) or is_above_limit(max_count): + raise error("repeat count too big", source.string, saved_pos) + + if max_count is not None and min_count > max_count: + raise error("min repeat greater than max repeat", source.string, + saved_pos) + + return min_count, max_count + +def parse_fuzzy(source, info, ch, case_flags): + "Parses a fuzzy setting, if present." + saved_pos = source.pos + + if ch != "{": + return None + + constraints = {} + try: + parse_fuzzy_item(source, constraints) + while source.match(","): + parse_fuzzy_item(source, constraints) + except ParseError: + source.pos = saved_pos + return None + + if source.match(":"): + constraints["test"] = parse_fuzzy_test(source, info, case_flags) + + if not source.match("}"): + raise error("expected }", source.string, source.pos) + + return constraints + +def parse_fuzzy_item(source, constraints): + "Parses a fuzzy setting item." + saved_pos = source.pos + try: + parse_cost_constraint(source, constraints) + except ParseError: + source.pos = saved_pos + + parse_cost_equation(source, constraints) + +def parse_cost_constraint(source, constraints): + "Parses a cost constraint." + saved_pos = source.pos + ch = source.get() + if ch in ALPHA: + # Syntax: constraint [("<=" | "<") cost] + constraint = parse_constraint(source, constraints, ch) + + max_inc = parse_fuzzy_compare(source) + + if max_inc is None: + # No maximum cost. + constraints[constraint] = 0, None + else: + # There's a maximum cost. + cost_pos = source.pos + max_cost = parse_cost_limit(source) + + # Inclusive or exclusive limit? + if not max_inc: + max_cost -= 1 + + if max_cost < 0: + raise error("bad fuzzy cost limit", source.string, cost_pos) + + constraints[constraint] = 0, max_cost + elif ch in DIGITS: + # Syntax: cost ("<=" | "<") constraint ("<=" | "<") cost + source.pos = saved_pos + + # Minimum cost. + cost_pos = source.pos + min_cost = parse_cost_limit(source) + + min_inc = parse_fuzzy_compare(source) + if min_inc is None: + raise ParseError() + + constraint = parse_constraint(source, constraints, source.get()) + + max_inc = parse_fuzzy_compare(source) + if max_inc is None: + raise ParseError() + + # Maximum cost. + cost_pos = source.pos + max_cost = parse_cost_limit(source) + + # Inclusive or exclusive limits? + if not min_inc: + min_cost += 1 + if not max_inc: + max_cost -= 1 + + if not 0 <= min_cost <= max_cost: + raise error("bad fuzzy cost limit", source.string, cost_pos) + + constraints[constraint] = min_cost, max_cost + else: + raise ParseError() + +def parse_cost_limit(source): + "Parses a cost limit." + cost_pos = source.pos + digits = parse_count(source) + + try: + return int(digits) + except ValueError: + pass + + raise error("bad fuzzy cost limit", source.string, cost_pos) + +def parse_constraint(source, constraints, ch): + "Parses a constraint." + if ch not in "deis": + raise ParseError() + + if ch in constraints: + raise ParseError() + + return ch + +def parse_fuzzy_compare(source): + "Parses a cost comparator." + if source.match("<="): + return True + elif source.match("<"): + return False + else: + return None + +def parse_cost_equation(source, constraints): + "Parses a cost equation." + if "cost" in constraints: + raise error("more than one cost equation", source.string, source.pos) + + cost = {} + + parse_cost_term(source, cost) + while source.match("+"): + parse_cost_term(source, cost) + + max_inc = parse_fuzzy_compare(source) + if max_inc is None: + raise ParseError() + + max_cost = int(parse_count(source)) + + if not max_inc: + max_cost -= 1 + + if max_cost < 0: + raise error("bad fuzzy cost limit", source.string, source.pos) + + cost["max"] = max_cost + + constraints["cost"] = cost + +def parse_cost_term(source, cost): + "Parses a cost equation term." + coeff = parse_count(source) + ch = source.get() + if ch not in "dis": + raise ParseError() + + if ch in cost: + raise error("repeated fuzzy cost", source.string, source.pos) + + cost[ch] = int(coeff or 1) + +def parse_fuzzy_test(source, info, case_flags): + saved_pos = source.pos + ch = source.get() + if ch in SPECIAL_CHARS: + if ch == "\\": + # An escape sequence outside a set. + return parse_escape(source, info, False) + elif ch == ".": + # Any character. + if info.flags & DOTALL: + return AnyAll() + elif info.flags & WORD: + return AnyU() + else: + return Any() + elif ch == "[": + # A character set. + return parse_set(source, info) + else: + raise error("expected character set", source.string, saved_pos) + elif ch: + # A literal. + return Character(ord(ch), case_flags=case_flags) + else: + raise error("expected character set", source.string, saved_pos) + +def parse_count(source): + "Parses a quantifier's count, which can be empty." + return source.get_while(DIGITS) + +def parse_paren(source, info): + """Parses a parenthesised subpattern or a flag. Returns FLAGS if it's an + inline flag. + """ + saved_pos = source.pos + ch = source.get(True) + if ch == "?": + # (?... + saved_pos_2 = source.pos + ch = source.get(True) + if ch == "<": + # (?<... + saved_pos_3 = source.pos + ch = source.get() + if ch in ("=", "!"): + # (?<=... or (?") + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + return Group(info, group, subpattern) + if ch in ("=", "!"): + # (?=... or (?!...: lookahead. + return parse_lookaround(source, info, False, ch == "=") + if ch == "P": + # (?P...: a Python extension. + return parse_extension(source, info) + if ch == "#": + # (?#...: a comment. + return parse_comment(source) + if ch == "(": + # (?(...: a conditional subpattern. + return parse_conditional(source, info) + if ch == ">": + # (?>...: an atomic subpattern. + return parse_atomic(source, info) + if ch == "|": + # (?|...: a common/reset groups branch. + return parse_common(source, info) + if ch == "R" or "0" <= ch <= "9": + # (?R...: probably a call to a group. + return parse_call_group(source, info, ch, saved_pos_2) + if ch == "&": + # (?&...: a call to a named group. + return parse_call_named_group(source, info, saved_pos_2) + + # (?...: probably a flags subpattern. + source.pos = saved_pos_2 + return parse_flags_subpattern(source, info) + + if ch == "*": + # (*... + saved_pos_2 = source.pos + word = source.get_while(set(")>"), include=False) + if word[ : 1].isalpha(): + verb = VERBS.get(word) + if not verb: + raise error("unknown verb", source.string, saved_pos_2) + + source.expect(")") + + return verb + + # (...: an unnamed capture group. + source.pos = saved_pos + group = info.open_group() + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + + return Group(info, group, subpattern) + +def parse_extension(source, info): + "Parses a Python extension." + saved_pos = source.pos + ch = source.get() + if ch == "<": + # (?P<...: a named capture group. + name = parse_name(source) + group = info.open_group(name) + source.expect(">") + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + info.close_group() + + return Group(info, group, subpattern) + if ch == "=": + # (?P=...: a named group reference. + name = parse_name(source, allow_numeric=True) + source.expect(")") + if info.is_open_group(name): + raise error("cannot refer to an open group", source.string, + saved_pos) + + return make_ref_group(info, name, saved_pos) + if ch == ">" or ch == "&": + # (?P>...: a call to a group. + return parse_call_named_group(source, info, saved_pos) + + source.pos = saved_pos + raise error("unknown extension", source.string, saved_pos) + +def parse_comment(source): + "Parses a comment." + while True: + saved_pos = source.pos + c = source.get(True) + + if not c or c == ")": + break + + if c == "\\": + c = source.get(True) + + source.pos = saved_pos + source.expect(")") + + return None + +def parse_lookaround(source, info, behind, positive): + "Parses a lookaround." + saved_flags = info.flags + try: + subpattern = _parse_pattern(source, info) + source.expect(")") + finally: + info.flags = saved_flags + source.ignore_space = bool(info.flags & VERBOSE) + + return LookAround(behind, positive, subpattern) + +def parse_conditional(source, info): + "Parses a conditional subpattern." + saved_flags = info.flags + saved_pos = source.pos + ch = source.get() + if ch == "?": + # (?(?... + ch = source.get() + if ch in ("=", "!"): + # (?(?=... or (?(?!...: lookahead conditional. + return parse_lookaround_conditional(source, info, False, ch == "=") + if ch == "<": + # (?(?<... + ch = source.get() + if ch in ("=", "!"): + # (?(?<=... or (?(?"), include=False) + + if not name: + raise error("missing group name", source.string, source.pos) + + if name.isdigit(): + min_group = 0 if allow_group_0 else 1 + if not allow_numeric or int(name) < min_group: + raise error("bad character in group name", source.string, + source.pos) + else: + if not name.isidentifier(): + raise error("bad character in group name", source.string, + source.pos) + + return name + +def is_octal(string): + "Checks whether a string is octal." + return all(ch in OCT_DIGITS for ch in string) + +def is_decimal(string): + "Checks whether a string is decimal." + return all(ch in DIGITS for ch in string) + +def is_hexadecimal(string): + "Checks whether a string is hexadecimal." + return all(ch in HEX_DIGITS for ch in string) + +def parse_escape(source, info, in_set): + "Parses an escape sequence." + saved_ignore = source.ignore_space + source.ignore_space = False + ch = source.get() + source.ignore_space = saved_ignore + if not ch: + # A backslash at the end of the pattern. + raise error("bad escape (end of pattern)", source.string, source.pos) + if ch in HEX_ESCAPES: + # A hexadecimal escape sequence. + return parse_hex_escape(source, info, ch, HEX_ESCAPES[ch], in_set, ch) + elif ch == "g" and not in_set: + # A group reference. + saved_pos = source.pos + try: + return parse_group_ref(source, info) + except error: + # Invalid as a group reference, so assume it's a literal. + source.pos = saved_pos + + return make_character(info, ord(ch), in_set) + elif ch == "G" and not in_set: + # A search anchor. + return SearchAnchor() + elif ch == "L" and not in_set: + # A string set. + return parse_string_set(source, info) + elif ch == "N": + # A named codepoint. + return parse_named_char(source, info, in_set) + elif ch in "pP": + # A Unicode property, positive or negative. + return parse_property(source, info, ch == "p", in_set) + elif ch == "R" and not in_set: + # A line ending. + charset = [0x0A, 0x0B, 0x0C, 0x0D] + if info.guess_encoding == UNICODE: + charset.extend([0x85, 0x2028, 0x2029]) + + return Atomic(Branch([String([0x0D, 0x0A]), SetUnion(info, [Character(c) + for c in charset])])) + elif ch == "X" and not in_set: + # A grapheme cluster. + return Grapheme() + elif ch in ALPHA: + # An alphabetic escape sequence. + # Positional escapes aren't allowed inside a character set. + if not in_set: + if info.flags & WORD: + value = WORD_POSITION_ESCAPES.get(ch) + else: + value = POSITION_ESCAPES.get(ch) + + if value: + return value + + value = CHARSET_ESCAPES.get(ch) + if value: + return value + + value = CHARACTER_ESCAPES.get(ch) + if value: + return Character(ord(value)) + + raise error("bad escape \\%s" % ch, source.string, source.pos) + elif ch in DIGITS: + # A numeric escape sequence. + return parse_numeric_escape(source, info, ch, in_set) + else: + # A literal. + return make_character(info, ord(ch), in_set) + +def parse_numeric_escape(source, info, ch, in_set): + "Parses a numeric escape sequence." + if in_set or ch == "0": + # Octal escape sequence, max 3 digits. + return parse_octal_escape(source, info, [ch], in_set) + + # At least 1 digit, so either octal escape or group. + digits = ch + saved_pos = source.pos + ch = source.get() + if ch in DIGITS: + # At least 2 digits, so either octal escape or group. + digits += ch + saved_pos = source.pos + ch = source.get() + if is_octal(digits) and ch in OCT_DIGITS: + # 3 octal digits, so octal escape sequence. + encoding = info.flags & _ALL_ENCODINGS + if encoding == ASCII or encoding == LOCALE: + octal_mask = 0xFF + else: + octal_mask = 0x1FF + + value = int(digits + ch, 8) & octal_mask + return make_character(info, value) + + # Group reference. + source.pos = saved_pos + if info.is_open_group(digits): + raise error("cannot refer to an open group", source.string, source.pos) + + return make_ref_group(info, digits, source.pos) + +def parse_octal_escape(source, info, digits, in_set): + "Parses an octal escape sequence." + saved_pos = source.pos + ch = source.get() + while len(digits) < 3 and ch in OCT_DIGITS: + digits.append(ch) + saved_pos = source.pos + ch = source.get() + + source.pos = saved_pos + try: + value = int("".join(digits), 8) + return make_character(info, value, in_set) + except ValueError: + if digits[0] in OCT_DIGITS: + raise error("incomplete escape \\%s" % ''.join(digits), + source.string, source.pos) + else: + raise error("bad escape \\%s" % digits[0], source.string, + source.pos) + +def parse_hex_escape(source, info, esc, expected_len, in_set, type): + "Parses a hex escape sequence." + saved_pos = source.pos + digits = [] + for i in range(expected_len): + ch = source.get() + if ch not in HEX_DIGITS: + raise error("incomplete escape \\%s%s" % (type, ''.join(digits)), + source.string, saved_pos) + digits.append(ch) + + try: + value = int("".join(digits), 16) + except ValueError: + pass + else: + if value < 0x110000: + return make_character(info, value, in_set) + + # Bad hex escape. + raise error("bad hex escape \\%s%s" % (esc, ''.join(digits)), + source.string, saved_pos) + +def parse_group_ref(source, info): + "Parses a group reference." + source.expect("<") + saved_pos = source.pos + name = parse_name(source, True) + source.expect(">") + if info.is_open_group(name): + raise error("cannot refer to an open group", source.string, source.pos) + + return make_ref_group(info, name, saved_pos) + +def parse_string_set(source, info): + "Parses a string set reference." + source.expect("<") + name = parse_name(source, True) + source.expect(">") + if name is None or name not in info.kwargs: + raise error("undefined named list", source.string, source.pos) + + return make_string_set(info, name) + +def parse_named_char(source, info, in_set): + "Parses a named character." + saved_pos = source.pos + if source.match("{"): + name = source.get_while(NAMED_CHAR_PART) + if source.match("}"): + try: + value = unicodedata.lookup(name) + return make_character(info, ord(value), in_set) + except KeyError: + raise error("undefined character name", source.string, + source.pos) + + source.pos = saved_pos + return make_character(info, ord("N"), in_set) + +def parse_property(source, info, positive, in_set): + "Parses a Unicode property." + saved_pos = source.pos + ch = source.get() + if ch == "{": + negate = source.match("^") + prop_name, name = parse_property_name(source) + if source.match("}"): + # It's correctly delimited. + prop = lookup_property(prop_name, name, positive != negate, source) + return make_property(info, prop, in_set) + elif ch and ch in "CLMNPSZ": + # An abbreviated property, eg \pL. + prop = lookup_property(None, ch, positive, source) + return make_property(info, prop, in_set) + + # Not a property, so treat as a literal "p" or "P". + source.pos = saved_pos + ch = "p" if positive else "P" + return make_character(info, ord(ch), in_set) + +def parse_property_name(source): + "Parses a property name, which may be qualified." + name = source.get_while(PROPERTY_NAME_PART) + saved_pos = source.pos + + ch = source.get() + if ch and ch in ":=": + prop_name = name + name = source.get_while(ALNUM | set(" &_-./")).strip() + + if name: + # Name after the ":" or "=", so it's a qualified name. + saved_pos = source.pos + else: + # No name after the ":" or "=", so assume it's an unqualified name. + prop_name, name = None, prop_name + else: + prop_name = None + + source.pos = saved_pos + return prop_name, name + +def parse_set(source, info): + "Parses a character set." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + saved_ignore = source.ignore_space + source.ignore_space = False + # Negative set? + negate = source.match("^") + try: + if version == VERSION0: + item = parse_set_imp_union(source, info) + else: + item = parse_set_union(source, info) + + if not source.match("]"): + raise error("missing ]", source.string, source.pos) + finally: + source.ignore_space = saved_ignore + + if negate: + item = item.with_flags(positive=not item.positive) + + item = item.with_flags(case_flags=make_case_flags(info)) + + return item + +def parse_set_union(source, info): + "Parses a set union ([x||y])." + items = [parse_set_symm_diff(source, info)] + while source.match("||"): + items.append(parse_set_symm_diff(source, info)) + + if len(items) == 1: + return items[0] + return SetUnion(info, items) + +def parse_set_symm_diff(source, info): + "Parses a set symmetric difference ([x~~y])." + items = [parse_set_inter(source, info)] + while source.match("~~"): + items.append(parse_set_inter(source, info)) + + if len(items) == 1: + return items[0] + return SetSymDiff(info, items) + +def parse_set_inter(source, info): + "Parses a set intersection ([x&&y])." + items = [parse_set_diff(source, info)] + while source.match("&&"): + items.append(parse_set_diff(source, info)) + + if len(items) == 1: + return items[0] + return SetInter(info, items) + +def parse_set_diff(source, info): + "Parses a set difference ([x--y])." + items = [parse_set_imp_union(source, info)] + while source.match("--"): + items.append(parse_set_imp_union(source, info)) + + if len(items) == 1: + return items[0] + return SetDiff(info, items) + +def parse_set_imp_union(source, info): + "Parses a set implicit union ([xy])." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + items = [parse_set_member(source, info)] + while True: + saved_pos = source.pos + if source.match("]"): + # End of the set. + source.pos = saved_pos + break + + if version == VERSION1 and any(source.match(op) for op in SET_OPS): + # The new behaviour has set operators. + source.pos = saved_pos + break + + items.append(parse_set_member(source, info)) + + if len(items) == 1: + return items[0] + return SetUnion(info, items) + +def parse_set_member(source, info): + "Parses a member in a character set." + # Parse a set item. + start = parse_set_item(source, info) + saved_pos1 = source.pos + if (not isinstance(start, Character) or not start.positive or not + source.match("-")): + # It's not the start of a range. + return start + + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + # It looks like the start of a range of characters. + saved_pos2 = source.pos + if version == VERSION1 and source.match("-"): + # It's actually the set difference operator '--', so return the + # character. + source.pos = saved_pos1 + return start + + if source.match("]"): + # We've reached the end of the set, so return both the character and + # hyphen. + source.pos = saved_pos2 + return SetUnion(info, [start, Character(ord("-"))]) + + # Parse a set item. + end = parse_set_item(source, info) + if not isinstance(end, Character) or not end.positive: + # It's not a range, so return the character, hyphen and property. + return SetUnion(info, [start, Character(ord("-")), end]) + + # It _is_ a range. + if start.value > end.value: + raise error("bad character range", source.string, source.pos) + + if start.value == end.value: + return start + + return Range(start.value, end.value) + +def parse_set_item(source, info): + "Parses an item in a character set." + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + + if source.match("\\"): + # An escape sequence in a set. + return parse_escape(source, info, True) + + saved_pos = source.pos + if source.match("[:"): + # Looks like a POSIX character class. + try: + return parse_posix_class(source, info) + except ParseError: + # Not a POSIX character class. + source.pos = saved_pos + + if version == VERSION1 and source.match("["): + # It's the start of a nested set. + + # Negative set? + negate = source.match("^") + item = parse_set_union(source, info) + + if not source.match("]"): + raise error("missing ]", source.string, source.pos) + + if negate: + item = item.with_flags(positive=not item.positive) + + return item + + ch = source.get() + if not ch: + raise error("unterminated character set", source.string, source.pos) + + return Character(ord(ch)) + +def parse_posix_class(source, info): + "Parses a POSIX character class." + negate = source.match("^") + prop_name, name = parse_property_name(source) + if not source.match(":]"): + raise ParseError() + + return lookup_property(prop_name, name, not negate, source, posix=True) + +def float_to_rational(flt): + "Converts a float to a rational pair." + int_part = int(flt) + error = flt - int_part + if abs(error) < 0.0001: + return int_part, 1 + + den, num = float_to_rational(1.0 / error) + + return int_part * den + num, den + +def numeric_to_rational(numeric): + "Converts a numeric string to a rational string, if possible." + if numeric[ : 1] == "-": + sign, numeric = numeric[0], numeric[1 : ] + else: + sign = "" + + parts = numeric.split("/") + if len(parts) == 2: + num, den = float_to_rational(float(parts[0]) / float(parts[1])) + elif len(parts) == 1: + num, den = float_to_rational(float(parts[0])) + else: + raise ValueError() + + result = "{}{}/{}".format(sign, num, den) + if result.endswith("/1"): + return result[ : -2] + + return result + +def standardise_name(name): + "Standardises a property or value name." + try: + return numeric_to_rational("".join(name)) + except (ValueError, ZeroDivisionError): + return "".join(ch for ch in name if ch not in "_- ").upper() + +_POSIX_CLASSES = set('ALNUM DIGIT PUNCT XDIGIT'.split()) + +_BINARY_VALUES = set('YES Y NO N TRUE T FALSE F'.split()) + +def lookup_property(property, value, positive, source=None, posix=False): + "Looks up a property." + # Normalise the names (which may still be lists). + property = standardise_name(property) if property else None + value = standardise_name(value) + + if (property, value) == ("GENERALCATEGORY", "ASSIGNED"): + property, value, positive = "GENERALCATEGORY", "UNASSIGNED", not positive + + if posix and not property and value.upper() in _POSIX_CLASSES: + value = 'POSIX' + value + + if property: + # Both the property and the value are provided. + prop = PROPERTIES.get(property) + if not prop: + if not source: + raise error("unknown property") + + raise error("unknown property", source.string, source.pos) + + prop_id, value_dict = prop + val_id = value_dict.get(value) + if val_id is None: + if not source: + raise error("unknown property value") + + raise error("unknown property value", source.string, source.pos) + + return Property((prop_id << 16) | val_id, positive) + + # Only the value is provided. + # It might be the name of a GC, script or block value. + for property in ("GC", "SCRIPT", "BLOCK"): + prop_id, value_dict = PROPERTIES.get(property) + val_id = value_dict.get(value) + if val_id is not None: + return Property((prop_id << 16) | val_id, positive) + + # It might be the name of a binary property. + prop = PROPERTIES.get(value) + if prop: + prop_id, value_dict = prop + if set(value_dict) == _BINARY_VALUES: + return Property((prop_id << 16) | 1, positive) + + return Property(prop_id << 16, not positive) + + # It might be the name of a binary property starting with a prefix. + if value.startswith("IS"): + prop = PROPERTIES.get(value[2 : ]) + if prop: + prop_id, value_dict = prop + if "YES" in value_dict: + return Property((prop_id << 16) | 1, positive) + + # It might be the name of a script or block starting with a prefix. + for prefix, property in (("IS", "SCRIPT"), ("IN", "BLOCK")): + if value.startswith(prefix): + prop_id, value_dict = PROPERTIES.get(property) + val_id = value_dict.get(value[2 : ]) + if val_id is not None: + return Property((prop_id << 16) | val_id, positive) + + # Unknown property. + if not source: + raise error("unknown property") + + raise error("unknown property", source.string, source.pos) + +def _compile_replacement(source, pattern, is_unicode): + "Compiles a replacement template escape sequence." + ch = source.get() + if ch in ALPHA: + # An alphabetic escape sequence. + value = CHARACTER_ESCAPES.get(ch) + if value: + return False, [ord(value)] + + if ch in HEX_ESCAPES and (ch == "x" or is_unicode): + # A hexadecimal escape sequence. + return False, [parse_repl_hex_escape(source, HEX_ESCAPES[ch], ch)] + + if ch == "g": + # A group preference. + return True, [compile_repl_group(source, pattern)] + + if ch == "N" and is_unicode: + # A named character. + value = parse_repl_named_char(source) + if value is not None: + return False, [value] + + raise error("bad escape \\%s" % ch, source.string, source.pos) + + if isinstance(source.sep, bytes): + octal_mask = 0xFF + else: + octal_mask = 0x1FF + + if ch == "0": + # An octal escape sequence. + digits = ch + while len(digits) < 3: + saved_pos = source.pos + ch = source.get() + if ch not in OCT_DIGITS: + source.pos = saved_pos + break + digits += ch + + return False, [int(digits, 8) & octal_mask] + + if ch in DIGITS: + # Either an octal escape sequence (3 digits) or a group reference (max + # 2 digits). + digits = ch + saved_pos = source.pos + ch = source.get() + if ch in DIGITS: + digits += ch + saved_pos = source.pos + ch = source.get() + if ch and is_octal(digits + ch): + # An octal escape sequence. + return False, [int(digits + ch, 8) & octal_mask] + + # A group reference. + source.pos = saved_pos + return True, [int(digits)] + + if ch == "\\": + # An escaped backslash is a backslash. + return False, [ord("\\")] + + if not ch: + # A trailing backslash. + raise error("bad escape (end of pattern)", source.string, source.pos) + + # An escaped non-backslash is a backslash followed by the literal. + return False, [ord("\\"), ord(ch)] + +def parse_repl_hex_escape(source, expected_len, type): + "Parses a hex escape sequence in a replacement string." + digits = [] + for i in range(expected_len): + ch = source.get() + if ch not in HEX_DIGITS: + raise error("incomplete escape \\%s%s" % (type, ''.join(digits)), + source.string, source.pos) + digits.append(ch) + + return int("".join(digits), 16) + +def parse_repl_named_char(source): + "Parses a named character in a replacement string." + saved_pos = source.pos + if source.match("{"): + name = source.get_while(ALPHA | set(" ")) + + if source.match("}"): + try: + value = unicodedata.lookup(name) + return ord(value) + except KeyError: + raise error("undefined character name", source.string, + source.pos) + + source.pos = saved_pos + return None + +def compile_repl_group(source, pattern): + "Compiles a replacement template group reference." + source.expect("<") + name = parse_name(source, True, True) + + source.expect(">") + if name.isdigit(): + index = int(name) + if not 0 <= index <= pattern.groups: + raise error("invalid group reference", source.string, source.pos) + + return index + + try: + return pattern.groupindex[name] + except KeyError: + raise IndexError("unknown group") + +# The regular expression is parsed into a syntax tree. The different types of +# node are defined below. + +INDENT = " " +POSITIVE_OP = 0x1 +ZEROWIDTH_OP = 0x2 +FUZZY_OP = 0x4 +REVERSE_OP = 0x8 +REQUIRED_OP = 0x10 + +POS_TEXT = {False: "NON-MATCH", True: "MATCH"} +CASE_TEXT = {NOCASE: "", IGNORECASE: " SIMPLE_IGNORE_CASE", FULLCASE: "", + FULLIGNORECASE: " FULL_IGNORE_CASE"} + +def make_sequence(items): + if len(items) == 1: + return items[0] + return Sequence(items) + +# Common base class for all nodes. +class RegexBase: + def __init__(self): + self._key = self.__class__ + + def with_flags(self, positive=None, case_flags=None, zerowidth=None): + if positive is None: + positive = self.positive + else: + positive = bool(positive) + if case_flags is None: + case_flags = self.case_flags + else: + case_flags = CASE_FLAGS_COMBINATIONS[case_flags & CASE_FLAGS] + if zerowidth is None: + zerowidth = self.zerowidth + else: + zerowidth = bool(zerowidth) + + if (positive == self.positive and case_flags == self.case_flags and + zerowidth == self.zerowidth): + return self + + return self.rebuild(positive, case_flags, zerowidth) + + def fix_groups(self, pattern, reverse, fuzzy): + pass + + def optimise(self, info, reverse): + return self + + def pack_characters(self, info): + return self + + def remove_captures(self): + return self + + def is_atomic(self): + return True + + def can_be_affix(self): + return True + + def contains_group(self): + return False + + def get_firstset(self, reverse): + raise _FirstSetError() + + def has_simple_start(self): + return False + + def compile(self, reverse=False, fuzzy=False): + return self._compile(reverse, fuzzy) + + def is_empty(self): + return False + + def __hash__(self): + return hash(self._key) + + def __eq__(self, other): + return type(self) is type(other) and self._key == other._key + + def __ne__(self, other): + return not self.__eq__(other) + + def get_required_string(self, reverse): + return self.max_width(), None + +# Base class for zero-width nodes. +class ZeroWidthBase(RegexBase): + def __init__(self, positive=True): + RegexBase.__init__(self) + self.positive = bool(positive) + + self._key = self.__class__, self.positive + + def get_firstset(self, reverse): + return set([None]) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if fuzzy: + flags |= FUZZY_OP + if reverse: + flags |= REVERSE_OP + return [(self._opcode, flags)] + + def dump(self, indent, reverse): + print("{}{} {}".format(INDENT * indent, self._op_name, + POS_TEXT[self.positive])) + + def max_width(self): + return 0 + +class Any(RegexBase): + _opcode = {False: OP.ANY, True: OP.ANY_REV} + _op_name = "ANY" + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[reverse], flags)] + + def dump(self, indent, reverse): + print("{}{}".format(INDENT * indent, self._op_name)) + + def max_width(self): + return 1 + +class AnyAll(Any): + _opcode = {False: OP.ANY_ALL, True: OP.ANY_ALL_REV} + _op_name = "ANY_ALL" + +class AnyU(Any): + _opcode = {False: OP.ANY_U, True: OP.ANY_U_REV} + _op_name = "ANY_U" + +class Atomic(RegexBase): + def __init__(self, subpattern): + RegexBase.__init__(self) + self.subpattern = subpattern + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + self.subpattern = self.subpattern.optimise(info, reverse) + + if self.subpattern.is_empty(): + return self.subpattern + return self + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def can_be_affix(self): + return self.subpattern.can_be_affix() + + def contains_group(self): + return self.subpattern.contains_group() + + def get_firstset(self, reverse): + return self.subpattern.get_firstset(reverse) + + def has_simple_start(self): + return self.subpattern.has_simple_start() + + def _compile(self, reverse, fuzzy): + return ([(OP.ATOMIC, )] + self.subpattern.compile(reverse, fuzzy) + + [(OP.END, )]) + + def dump(self, indent, reverse): + print("{}ATOMIC".format(INDENT * indent)) + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return (type(self) is type(other) and self.subpattern == + other.subpattern) + + def max_width(self): + return self.subpattern.max_width() + + def get_required_string(self, reverse): + return self.subpattern.get_required_string(reverse) + +class Boundary(ZeroWidthBase): + _opcode = OP.BOUNDARY + _op_name = "BOUNDARY" + +class Branch(RegexBase): + def __init__(self, branches): + RegexBase.__init__(self) + self.branches = branches + + def fix_groups(self, pattern, reverse, fuzzy): + for b in self.branches: + b.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + if not self.branches: + return Sequence([]) + + # Flatten branches within branches. + branches = Branch._flatten_branches(info, reverse, self.branches) + + # Move any common prefix or suffix out of the branches. + if reverse: + suffix, branches = Branch._split_common_suffix(info, branches) + prefix = [] + else: + prefix, branches = Branch._split_common_prefix(info, branches) + suffix = [] + + # Try to reduce adjacent single-character branches to sets. + branches = Branch._reduce_to_set(info, reverse, branches) + + if len(branches) > 1: + sequence = [Branch(branches)] + + if not prefix or not suffix: + # We might be able to add a quick precheck before the branches. + firstset = self._add_precheck(info, reverse, branches) + + if firstset: + if reverse: + sequence.append(firstset) + else: + sequence.insert(0, firstset) + else: + sequence = branches + + return make_sequence(prefix + sequence + suffix) + + def _add_precheck(self, info, reverse, branches): + charset = set() + pos = -1 if reverse else 0 + + for branch in branches: + if type(branch) is Literal and branch.case_flags == NOCASE: + charset.add(branch.characters[pos]) + else: + return + + if not charset: + return None + + return _check_firstset(info, reverse, [Character(c) for c in charset]) + + def pack_characters(self, info): + self.branches = [b.pack_characters(info) for b in self.branches] + return self + + def remove_captures(self): + self.branches = [b.remove_captures() for b in self.branches] + return self + + def is_atomic(self): + return all(b.is_atomic() for b in self.branches) + + def can_be_affix(self): + return all(b.can_be_affix() for b in self.branches) + + def contains_group(self): + return any(b.contains_group() for b in self.branches) + + def get_firstset(self, reverse): + fs = set() + for b in self.branches: + fs |= b.get_firstset(reverse) + + return fs or set([None]) + + def _compile(self, reverse, fuzzy): + if not self.branches: + return [] + + code = [(OP.BRANCH, )] + for b in self.branches: + code.extend(b.compile(reverse, fuzzy)) + code.append((OP.NEXT, )) + + code[-1] = (OP.END, ) + + return code + + def dump(self, indent, reverse): + print("{}BRANCH".format(INDENT * indent)) + self.branches[0].dump(indent + 1, reverse) + for b in self.branches[1 : ]: + print("{}OR".format(INDENT * indent)) + b.dump(indent + 1, reverse) + + @staticmethod + def _flatten_branches(info, reverse, branches): + # Flatten the branches so that there aren't branches of branches. + new_branches = [] + for b in branches: + b = b.optimise(info, reverse) + if isinstance(b, Branch): + new_branches.extend(b.branches) + else: + new_branches.append(b) + + return new_branches + + @staticmethod + def _split_common_prefix(info, branches): + # Common leading items can be moved out of the branches. + # Get the items in the branches. + alternatives = [] + for b in branches: + if isinstance(b, Sequence): + alternatives.append(b.items) + else: + alternatives.append([b]) + + # What is the maximum possible length of the prefix? + max_count = min(len(a) for a in alternatives) + + # What is the longest common prefix? + prefix = alternatives[0] + pos = 0 + end_pos = max_count + while pos < end_pos and prefix[pos].can_be_affix() and all(a[pos] == + prefix[pos] for a in alternatives): + pos += 1 + count = pos + + if info.flags & UNICODE: + # We need to check that we're not splitting a sequence of + # characters which could form part of full case-folding. + count = pos + while count > 0 and not all(Branch._can_split(a, count) for a in + alternatives): + count -= 1 + + # No common prefix is possible. + if count == 0: + return [], branches + + # Rebuild the branches. + new_branches = [] + for a in alternatives: + new_branches.append(make_sequence(a[count : ])) + + return prefix[ : count], new_branches + + @staticmethod + def _split_common_suffix(info, branches): + # Common trailing items can be moved out of the branches. + # Get the items in the branches. + alternatives = [] + for b in branches: + if isinstance(b, Sequence): + alternatives.append(b.items) + else: + alternatives.append([b]) + + # What is the maximum possible length of the suffix? + max_count = min(len(a) for a in alternatives) + + # What is the longest common suffix? + suffix = alternatives[0] + pos = -1 + end_pos = -1 - max_count + while pos > end_pos and suffix[pos].can_be_affix() and all(a[pos] == + suffix[pos] for a in alternatives): + pos -= 1 + count = -1 - pos + + if info.flags & UNICODE: + # We need to check that we're not splitting a sequence of + # characters which could form part of full case-folding. + while count > 0 and not all(Branch._can_split_rev(a, count) for a + in alternatives): + count -= 1 + + # No common suffix is possible. + if count == 0: + return [], branches + + # Rebuild the branches. + new_branches = [] + for a in alternatives: + new_branches.append(make_sequence(a[ : -count])) + + return suffix[-count : ], new_branches + + @staticmethod + def _can_split(items, count): + # Check the characters either side of the proposed split. + if not Branch._is_full_case(items, count - 1): + return True + + if not Branch._is_full_case(items, count): + return True + + # Check whether a 1-1 split would be OK. + if Branch._is_folded(items[count - 1 : count + 1]): + return False + + # Check whether a 1-2 split would be OK. + if (Branch._is_full_case(items, count + 2) and + Branch._is_folded(items[count - 1 : count + 2])): + return False + + # Check whether a 2-1 split would be OK. + if (Branch._is_full_case(items, count - 2) and + Branch._is_folded(items[count - 2 : count + 1])): + return False + + return True + + @staticmethod + def _can_split_rev(items, count): + end = len(items) + + # Check the characters either side of the proposed split. + if not Branch._is_full_case(items, end - count): + return True + + if not Branch._is_full_case(items, end - count - 1): + return True + + # Check whether a 1-1 split would be OK. + if Branch._is_folded(items[end - count - 1 : end - count + 1]): + return False + + # Check whether a 1-2 split would be OK. + if (Branch._is_full_case(items, end - count + 2) and + Branch._is_folded(items[end - count - 1 : end - count + 2])): + return False + + # Check whether a 2-1 split would be OK. + if (Branch._is_full_case(items, end - count - 2) and + Branch._is_folded(items[end - count - 2 : end - count + 1])): + return False + + return True + + @staticmethod + def _merge_common_prefixes(info, reverse, branches): + # Branches with the same case-sensitive character prefix can be grouped + # together if they are separated only by other branches with a + # character prefix. + prefixed = defaultdict(list) + order = {} + new_branches = [] + for b in branches: + if Branch._is_simple_character(b): + # Branch starts with a simple character. + prefixed[b.value].append([b]) + order.setdefault(b.value, len(order)) + elif (isinstance(b, Sequence) and b.items and + Branch._is_simple_character(b.items[0])): + # Branch starts with a simple character. + prefixed[b.items[0].value].append(b.items) + order.setdefault(b.items[0].value, len(order)) + else: + Branch._flush_char_prefix(info, reverse, prefixed, order, + new_branches) + + new_branches.append(b) + + Branch._flush_char_prefix(info, prefixed, order, new_branches) + + return new_branches + + @staticmethod + def _is_simple_character(c): + return isinstance(c, Character) and c.positive and not c.case_flags + + @staticmethod + def _reduce_to_set(info, reverse, branches): + # Can the branches be reduced to a set? + new_branches = [] + items = set() + case_flags = NOCASE + for b in branches: + if isinstance(b, (Character, Property, SetBase)): + # Branch starts with a single character. + if b.case_flags != case_flags: + # Different case sensitivity, so flush. + Branch._flush_set_members(info, reverse, items, case_flags, + new_branches) + + case_flags = b.case_flags + + items.add(b.with_flags(case_flags=NOCASE)) + else: + Branch._flush_set_members(info, reverse, items, case_flags, + new_branches) + + new_branches.append(b) + + Branch._flush_set_members(info, reverse, items, case_flags, + new_branches) + + return new_branches + + @staticmethod + def _flush_char_prefix(info, reverse, prefixed, order, new_branches): + # Flush the prefixed branches. + if not prefixed: + return + + for value, branches in sorted(prefixed.items(), key=lambda pair: + order[pair[0]]): + if len(branches) == 1: + new_branches.append(make_sequence(branches[0])) + else: + subbranches = [] + optional = False + for b in branches: + if len(b) > 1: + subbranches.append(make_sequence(b[1 : ])) + elif not optional: + subbranches.append(Sequence()) + optional = True + + sequence = Sequence([Character(value), Branch(subbranches)]) + new_branches.append(sequence.optimise(info, reverse)) + + prefixed.clear() + order.clear() + + @staticmethod + def _flush_set_members(info, reverse, items, case_flags, new_branches): + # Flush the set members. + if not items: + return + + if len(items) == 1: + item = list(items)[0] + else: + item = SetUnion(info, list(items)).optimise(info, reverse) + + new_branches.append(item.with_flags(case_flags=case_flags)) + + items.clear() + + @staticmethod + def _is_full_case(items, i): + if not 0 <= i < len(items): + return False + + item = items[i] + return (isinstance(item, Character) and item.positive and + (item.case_flags & FULLIGNORECASE) == FULLIGNORECASE) + + @staticmethod + def _is_folded(items): + if len(items) < 2: + return False + + for i in items: + if (not isinstance(i, Character) or not i.positive or not + i.case_flags): + return False + + folded = "".join(chr(i.value) for i in items) + folded = _regex.fold_case(FULL_CASE_FOLDING, folded) + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + for c in expanding_chars: + if folded == _regex.fold_case(FULL_CASE_FOLDING, c): + return True + + return False + + def is_empty(self): + return all(b.is_empty() for b in self.branches) + + def __eq__(self, other): + return type(self) is type(other) and self.branches == other.branches + + def max_width(self): + return max(b.max_width() for b in self.branches) + +class CallGroup(RegexBase): + def __init__(self, info, group, position): + RegexBase.__init__(self) + self.info = info + self.group = group + self.position = position + + self._key = self.__class__, self.group + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + raise error("invalid group reference", pattern, self.position) + + if not 0 <= self.group <= self.info.group_count: + raise error("unknown group", pattern, self.position) + + if self.group > 0 and self.info.open_group_count[self.group] > 1: + raise error("ambiguous group reference", pattern, self.position) + + self.info.group_calls.append((self, reverse, fuzzy)) + + self._key = self.__class__, self.group + + def remove_captures(self): + raise error("group reference not allowed", pattern, self.position) + + def _compile(self, reverse, fuzzy): + return [(OP.GROUP_CALL, self.call_ref)] + + def dump(self, indent, reverse): + print("{}GROUP_CALL {}".format(INDENT * indent, self.group)) + + def __eq__(self, other): + return type(self) is type(other) and self.group == other.group + + def max_width(self): + return UNLIMITED + + def __del__(self): + self.info = None + +class CallRef(RegexBase): + def __init__(self, ref, parsed): + self.ref = ref + self.parsed = parsed + + def _compile(self, reverse, fuzzy): + return ([(OP.CALL_REF, self.ref)] + self.parsed._compile(reverse, + fuzzy) + [(OP.END, )]) + +class Character(RegexBase): + _opcode = {(NOCASE, False): OP.CHARACTER, (IGNORECASE, False): + OP.CHARACTER_IGN, (FULLCASE, False): OP.CHARACTER, (FULLIGNORECASE, + False): OP.CHARACTER_IGN, (NOCASE, True): OP.CHARACTER_REV, (IGNORECASE, + True): OP.CHARACTER_IGN_REV, (FULLCASE, True): OP.CHARACTER_REV, + (FULLIGNORECASE, True): OP.CHARACTER_IGN_REV} + + def __init__(self, value, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.value = value + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + if (self.positive and (self.case_flags & FULLIGNORECASE) == + FULLIGNORECASE): + self.folded = _regex.fold_case(FULL_CASE_FOLDING, chr(self.value)) + else: + self.folded = chr(self.value) + + self._key = (self.__class__, self.value, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Character(self.value, positive, case_flags, zerowidth) + + def optimise(self, info, reverse, in_set=False): + return self + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + + code = PrecompiledCode([self._opcode[self.case_flags, reverse], flags, + self.value]) + + if len(self.folded) > 1: + # The character expands on full case-folding. + code = Branch([code, String([ord(c) for c in self.folded], + case_flags=self.case_flags)]) + + return code.compile(reverse, fuzzy) + + def dump(self, indent, reverse): + display = ascii(chr(self.value)).lstrip("bu") + print("{}CHARACTER {} {}{}".format(INDENT * indent, + POS_TEXT[self.positive], display, CASE_TEXT[self.case_flags])) + + def matches(self, ch): + return (ch == self.value) == self.positive + + def max_width(self): + return len(self.folded) + + def get_required_string(self, reverse): + if not self.positive: + return 1, None + + self.folded_characters = tuple(ord(c) for c in self.folded) + + return 0, self + +class Conditional(RegexBase): + def __init__(self, info, group, yes_item, no_item, position): + RegexBase.__init__(self) + self.info = info + self.group = group + self.yes_item = yes_item + self.no_item = no_item + self.position = position + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + if self.group == 'DEFINE': + # 'DEFINE' is a special name unless there's a group with + # that name. + self.group = 0 + else: + raise error("unknown group", pattern, self.position) + + if not 0 <= self.group <= self.info.group_count: + raise error("invalid group reference", pattern, self.position) + + self.yes_item.fix_groups(pattern, reverse, fuzzy) + self.no_item.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + yes_item = self.yes_item.optimise(info, reverse) + no_item = self.no_item.optimise(info, reverse) + + return Conditional(info, self.group, yes_item, no_item, self.position) + + def pack_characters(self, info): + self.yes_item = self.yes_item.pack_characters(info) + self.no_item = self.no_item.pack_characters(info) + return self + + def remove_captures(self): + self.yes_item = self.yes_item.remove_captures() + self.no_item = self.no_item.remove_captures() + + def is_atomic(self): + return self.yes_item.is_atomic() and self.no_item.is_atomic() + + def can_be_affix(self): + return self.yes_item.can_be_affix() and self.no_item.can_be_affix() + + def contains_group(self): + return self.yes_item.contains_group() or self.no_item.contains_group() + + def get_firstset(self, reverse): + return (self.yes_item.get_firstset(reverse) | + self.no_item.get_firstset(reverse)) + + def _compile(self, reverse, fuzzy): + code = [(OP.GROUP_EXISTS, self.group)] + code.extend(self.yes_item.compile(reverse, fuzzy)) + add_code = self.no_item.compile(reverse, fuzzy) + if add_code: + code.append((OP.NEXT, )) + code.extend(add_code) + + code.append((OP.END, )) + + return code + + def dump(self, indent, reverse): + print("{}GROUP_EXISTS {}".format(INDENT * indent, self.group)) + self.yes_item.dump(indent + 1, reverse) + if not self.no_item.is_empty(): + print("{}OR".format(INDENT * indent)) + self.no_item.dump(indent + 1, reverse) + + def is_empty(self): + return self.yes_item.is_empty() and self.no_item.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.group, self.yes_item, + self.no_item) == (other.group, other.yes_item, other.no_item) + + def max_width(self): + return max(self.yes_item.max_width(), self.no_item.max_width()) + + def __del__(self): + self.info = None + +class DefaultBoundary(ZeroWidthBase): + _opcode = OP.DEFAULT_BOUNDARY + _op_name = "DEFAULT_BOUNDARY" + +class DefaultEndOfWord(ZeroWidthBase): + _opcode = OP.DEFAULT_END_OF_WORD + _op_name = "DEFAULT_END_OF_WORD" + +class DefaultStartOfWord(ZeroWidthBase): + _opcode = OP.DEFAULT_START_OF_WORD + _op_name = "DEFAULT_START_OF_WORD" + +class EndOfLine(ZeroWidthBase): + _opcode = OP.END_OF_LINE + _op_name = "END_OF_LINE" + +class EndOfLineU(EndOfLine): + _opcode = OP.END_OF_LINE_U + _op_name = "END_OF_LINE_U" + +class EndOfString(ZeroWidthBase): + _opcode = OP.END_OF_STRING + _op_name = "END_OF_STRING" + +class EndOfStringLine(ZeroWidthBase): + _opcode = OP.END_OF_STRING_LINE + _op_name = "END_OF_STRING_LINE" + +class EndOfStringLineU(EndOfStringLine): + _opcode = OP.END_OF_STRING_LINE_U + _op_name = "END_OF_STRING_LINE_U" + +class EndOfWord(ZeroWidthBase): + _opcode = OP.END_OF_WORD + _op_name = "END_OF_WORD" + +class Failure(ZeroWidthBase): + _op_name = "FAILURE" + + def _compile(self, reverse, fuzzy): + return [(OP.FAILURE, )] + +class Fuzzy(RegexBase): + def __init__(self, subpattern, constraints=None): + RegexBase.__init__(self) + if constraints is None: + constraints = {} + self.subpattern = subpattern + self.constraints = constraints + + # If an error type is mentioned in the cost equation, then its maximum + # defaults to unlimited. + if "cost" in constraints: + for e in "dis": + if e in constraints["cost"]: + constraints.setdefault(e, (0, None)) + + # If any error type is mentioned, then all the error maxima default to + # 0, otherwise they default to unlimited. + if set(constraints) & set("dis"): + for e in "dis": + constraints.setdefault(e, (0, 0)) + else: + for e in "dis": + constraints.setdefault(e, (0, None)) + + # The maximum of the generic error type defaults to unlimited. + constraints.setdefault("e", (0, None)) + + # The cost equation defaults to equal costs. Also, the cost of any + # error type not mentioned in the cost equation defaults to 0. + if "cost" in constraints: + for e in "dis": + constraints["cost"].setdefault(e, 0) + else: + constraints["cost"] = {"d": 1, "i": 1, "s": 1, "max": + constraints["e"][1]} + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, True) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def is_atomic(self): + return self.subpattern.is_atomic() + + def contains_group(self): + return self.subpattern.contains_group() + + def _compile(self, reverse, fuzzy): + # The individual limits. + arguments = [] + for e in "dise": + v = self.constraints[e] + arguments.append(v[0]) + arguments.append(UNLIMITED if v[1] is None else v[1]) + + # The coeffs of the cost equation. + for e in "dis": + arguments.append(self.constraints["cost"][e]) + + # The maximum of the cost equation. + v = self.constraints["cost"]["max"] + arguments.append(UNLIMITED if v is None else v) + + flags = 0 + if reverse: + flags |= REVERSE_OP + + test = self.constraints.get("test") + + if test: + return ([(OP.FUZZY_EXT, flags) + tuple(arguments)] + + test.compile(reverse, True) + [(OP.NEXT,)] + + self.subpattern.compile(reverse, True) + [(OP.END,)]) + + return ([(OP.FUZZY, flags) + tuple(arguments)] + + self.subpattern.compile(reverse, True) + [(OP.END,)]) + + def dump(self, indent, reverse): + constraints = self._constraints_to_string() + if constraints: + constraints = " " + constraints + print("{}FUZZY{}".format(INDENT * indent, constraints)) + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return (type(self) is type(other) and self.subpattern == + other.subpattern and self.constraints == other.constraints) + + def max_width(self): + return UNLIMITED + + def _constraints_to_string(self): + constraints = [] + + for name in "ids": + min, max = self.constraints[name] + if max == 0: + continue + + con = "" + + if min > 0: + con = "{}<=".format(min) + + con += name + + if max is not None: + con += "<={}".format(max) + + constraints.append(con) + + cost = [] + for name in "ids": + coeff = self.constraints["cost"][name] + if coeff > 0: + cost.append("{}{}".format(coeff, name)) + + limit = self.constraints["cost"]["max"] + if limit is not None and limit > 0: + cost = "{}<={}".format("+".join(cost), limit) + constraints.append(cost) + + return ",".join(constraints) + +class Grapheme(RegexBase): + def _compile(self, reverse, fuzzy): + # Match at least 1 character until a grapheme boundary is reached. Note + # that this is the same whether matching forwards or backwards. + grapheme_matcher = Atomic(Sequence([LazyRepeat(AnyAll(), 1, None), + GraphemeBoundary()])) + + return grapheme_matcher.compile(reverse, fuzzy) + + def dump(self, indent, reverse): + print("{}GRAPHEME".format(INDENT * indent)) + + def max_width(self): + return UNLIMITED + +class GraphemeBoundary: + def compile(self, reverse, fuzzy): + return [(OP.GRAPHEME_BOUNDARY, 1)] + +class GreedyRepeat(RegexBase): + _opcode = OP.GREEDY_REPEAT + _op_name = "GREEDY_REPEAT" + + def __init__(self, subpattern, min_count, max_count): + RegexBase.__init__(self) + self.subpattern = subpattern + self.min_count = min_count + self.max_count = max_count + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + subpattern = self.subpattern.optimise(info, reverse) + + return type(self)(subpattern, self.min_count, self.max_count) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + return self + + def is_atomic(self): + return self.min_count == self.max_count and self.subpattern.is_atomic() + + def can_be_affix(self): + return False + + def contains_group(self): + return self.subpattern.contains_group() + + def get_firstset(self, reverse): + fs = self.subpattern.get_firstset(reverse) + if self.min_count == 0: + fs.add(None) + + return fs + + def _compile(self, reverse, fuzzy): + repeat = [self._opcode, self.min_count] + if self.max_count is None: + repeat.append(UNLIMITED) + else: + repeat.append(self.max_count) + + subpattern = self.subpattern.compile(reverse, fuzzy) + if not subpattern: + return [] + + return ([tuple(repeat)] + subpattern + [(OP.END, )]) + + def dump(self, indent, reverse): + if self.max_count is None: + limit = "INF" + else: + limit = self.max_count + print("{}{} {} {}".format(INDENT * indent, self._op_name, + self.min_count, limit)) + + self.subpattern.dump(indent + 1, reverse) + + def is_empty(self): + return self.subpattern.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.subpattern, self.min_count, + self.max_count) == (other.subpattern, other.min_count, + other.max_count) + + def max_width(self): + if self.max_count is None: + return UNLIMITED + + return self.subpattern.max_width() * self.max_count + + def get_required_string(self, reverse): + max_count = UNLIMITED if self.max_count is None else self.max_count + if self.min_count == 0: + w = self.subpattern.max_width() * max_count + return min(w, UNLIMITED), None + + ofs, req = self.subpattern.get_required_string(reverse) + if req: + return ofs, req + + w = self.subpattern.max_width() * max_count + return min(w, UNLIMITED), None + +class PossessiveRepeat(GreedyRepeat): + def is_atomic(self): + return True + + def _compile(self, reverse, fuzzy): + subpattern = self.subpattern.compile(reverse, fuzzy) + if not subpattern: + return [] + + repeat = [self._opcode, self.min_count] + if self.max_count is None: + repeat.append(UNLIMITED) + else: + repeat.append(self.max_count) + + return ([(OP.ATOMIC, ), tuple(repeat)] + subpattern + [(OP.END, ), + (OP.END, )]) + + def dump(self, indent, reverse): + print("{}ATOMIC".format(INDENT * indent)) + + if self.max_count is None: + limit = "INF" + else: + limit = self.max_count + print("{}{} {} {}".format(INDENT * (indent + 1), self._op_name, + self.min_count, limit)) + + self.subpattern.dump(indent + 2, reverse) + +class Group(RegexBase): + def __init__(self, info, group, subpattern): + RegexBase.__init__(self) + self.info = info + self.group = group + self.subpattern = subpattern + + self.call_ref = None + + def fix_groups(self, pattern, reverse, fuzzy): + self.info.defined_groups[self.group] = (self, reverse, fuzzy) + self.subpattern.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + subpattern = self.subpattern.optimise(info, reverse) + + return Group(self.info, self.group, subpattern) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + return self.subpattern.remove_captures() + + def is_atomic(self): + return self.subpattern.is_atomic() + + def can_be_affix(self): + return False + + def contains_group(self): + return True + + def get_firstset(self, reverse): + return self.subpattern.get_firstset(reverse) + + def has_simple_start(self): + return self.subpattern.has_simple_start() + + def _compile(self, reverse, fuzzy): + code = [] + + public_group = private_group = self.group + if private_group < 0: + public_group = self.info.private_groups[private_group] + private_group = self.info.group_count - private_group + + key = self.group, reverse, fuzzy + ref = self.info.call_refs.get(key) + if ref is not None: + code += [(OP.CALL_REF, ref)] + + code += [(OP.GROUP, int(not reverse), private_group, public_group)] + code += self.subpattern.compile(reverse, fuzzy) + code += [(OP.END, )] + + if ref is not None: + code += [(OP.END, )] + + return code + + def dump(self, indent, reverse): + group = self.group + if group < 0: + group = private_groups[group] + print("{}GROUP {}".format(INDENT * indent, group)) + self.subpattern.dump(indent + 1, reverse) + + def __eq__(self, other): + return (type(self) is type(other) and (self.group, self.subpattern) == + (other.group, other.subpattern)) + + def max_width(self): + return self.subpattern.max_width() + + def get_required_string(self, reverse): + return self.subpattern.get_required_string(reverse) + + def __del__(self): + self.info = None + +class Keep(ZeroWidthBase): + _opcode = OP.KEEP + _op_name = "KEEP" + +class LazyRepeat(GreedyRepeat): + _opcode = OP.LAZY_REPEAT + _op_name = "LAZY_REPEAT" + +class LookAround(RegexBase): + _dir_text = {False: "AHEAD", True: "BEHIND"} + + def __init__(self, behind, positive, subpattern): + RegexBase.__init__(self) + self.behind = bool(behind) + self.positive = bool(positive) + self.subpattern = subpattern + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, self.behind, fuzzy) + + def optimise(self, info, reverse): + subpattern = self.subpattern.optimise(info, self.behind) + if self.positive and subpattern.is_empty(): + return subpattern + + return LookAround(self.behind, self.positive, subpattern) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + return self + + def remove_captures(self): + return self.subpattern.remove_captures() + + def is_atomic(self): + return self.subpattern.is_atomic() + + def can_be_affix(self): + return self.subpattern.can_be_affix() + + def contains_group(self): + return self.subpattern.contains_group() + + def get_firstset(self, reverse): + if self.positive and self.behind == reverse: + return self.subpattern.get_firstset(reverse) + + return set([None]) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if fuzzy: + flags |= FUZZY_OP + if reverse: + flags |= REVERSE_OP + + return ([(OP.LOOKAROUND, flags, int(not self.behind))] + + self.subpattern.compile(self.behind) + [(OP.END, )]) + + def dump(self, indent, reverse): + print("{}LOOK{} {}".format(INDENT * indent, + self._dir_text[self.behind], POS_TEXT[self.positive])) + self.subpattern.dump(indent + 1, self.behind) + + def is_empty(self): + return self.positive and self.subpattern.is_empty() + + def __eq__(self, other): + return type(self) is type(other) and (self.behind, self.positive, + self.subpattern) == (other.behind, other.positive, other.subpattern) + + def max_width(self): + return 0 + +class LookAroundConditional(RegexBase): + _dir_text = {False: "AHEAD", True: "BEHIND"} + + def __init__(self, behind, positive, subpattern, yes_item, no_item): + RegexBase.__init__(self) + self.behind = bool(behind) + self.positive = bool(positive) + self.subpattern = subpattern + self.yes_item = yes_item + self.no_item = no_item + + def fix_groups(self, pattern, reverse, fuzzy): + self.subpattern.fix_groups(pattern, reverse, fuzzy) + self.yes_item.fix_groups(pattern, reverse, fuzzy) + self.no_item.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + subpattern = self.subpattern.optimise(info, self.behind) + yes_item = self.yes_item.optimise(info, self.behind) + no_item = self.no_item.optimise(info, self.behind) + + return LookAroundConditional(self.behind, self.positive, subpattern, + yes_item, no_item) + + def pack_characters(self, info): + self.subpattern = self.subpattern.pack_characters(info) + self.yes_item = self.yes_item.pack_characters(info) + self.no_item = self.no_item.pack_characters(info) + return self + + def remove_captures(self): + self.subpattern = self.subpattern.remove_captures() + self.yes_item = self.yes_item.remove_captures() + self.no_item = self.no_item.remove_captures() + + def is_atomic(self): + return (self.subpattern.is_atomic() and self.yes_item.is_atomic() and + self.no_item.is_atomic()) + + def can_be_affix(self): + return (self.subpattern.can_be_affix() and self.yes_item.can_be_affix() + and self.no_item.can_be_affix()) + + def contains_group(self): + return (self.subpattern.contains_group() or + self.yes_item.contains_group() or self.no_item.contains_group()) + + def _compile(self, reverse, fuzzy): + code = [(OP.CONDITIONAL, int(self.positive), int(not self.behind))] + code.extend(self.subpattern.compile(self.behind, fuzzy)) + code.append((OP.NEXT, )) + code.extend(self.yes_item.compile(reverse, fuzzy)) + add_code = self.no_item.compile(reverse, fuzzy) + if add_code: + code.append((OP.NEXT, )) + code.extend(add_code) + + code.append((OP.END, )) + + return code + + def dump(self, indent, reverse): + print("{}CONDITIONAL {} {}".format(INDENT * indent, + self._dir_text[self.behind], POS_TEXT[self.positive])) + self.subpattern.dump(indent + 1, self.behind) + print("{}EITHER".format(INDENT * indent)) + self.yes_item.dump(indent + 1, reverse) + if not self.no_item.is_empty(): + print("{}OR".format(INDENT * indent)) + self.no_item.dump(indent + 1, reverse) + + def is_empty(self): + return (self.subpattern.is_empty() and self.yes_item.is_empty() or + self.no_item.is_empty()) + + def __eq__(self, other): + return type(self) is type(other) and (self.subpattern, self.yes_item, + self.no_item) == (other.subpattern, other.yes_item, other.no_item) + + def max_width(self): + return max(self.yes_item.max_width(), self.no_item.max_width()) + + def get_required_string(self, reverse): + return self.max_width(), None + +class PrecompiledCode(RegexBase): + def __init__(self, code): + self.code = code + + def _compile(self, reverse, fuzzy): + return [tuple(self.code)] + +class Property(RegexBase): + _opcode = {(NOCASE, False): OP.PROPERTY, (IGNORECASE, False): + OP.PROPERTY_IGN, (FULLCASE, False): OP.PROPERTY, (FULLIGNORECASE, False): + OP.PROPERTY_IGN, (NOCASE, True): OP.PROPERTY_REV, (IGNORECASE, True): + OP.PROPERTY_IGN_REV, (FULLCASE, True): OP.PROPERTY_REV, (FULLIGNORECASE, + True): OP.PROPERTY_IGN_REV} + + def __init__(self, value, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.value = value + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self._key = (self.__class__, self.value, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Property(self.value, positive, case_flags, zerowidth) + + def optimise(self, info, reverse, in_set=False): + return self + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.value)] + + def dump(self, indent, reverse): + prop = PROPERTY_NAMES[self.value >> 16] + name, value = prop[0], prop[1][self.value & 0xFFFF] + print("{}PROPERTY {} {}:{}{}".format(INDENT * indent, + POS_TEXT[self.positive], name, value, CASE_TEXT[self.case_flags])) + + def matches(self, ch): + return _regex.has_property_value(self.value, ch) == self.positive + + def max_width(self): + return 1 + +class Prune(ZeroWidthBase): + _op_name = "PRUNE" + + def _compile(self, reverse, fuzzy): + return [(OP.PRUNE, )] + +class Range(RegexBase): + _opcode = {(NOCASE, False): OP.RANGE, (IGNORECASE, False): OP.RANGE_IGN, + (FULLCASE, False): OP.RANGE, (FULLIGNORECASE, False): OP.RANGE_IGN, + (NOCASE, True): OP.RANGE_REV, (IGNORECASE, True): OP.RANGE_IGN_REV, + (FULLCASE, True): OP.RANGE_REV, (FULLIGNORECASE, True): OP.RANGE_IGN_REV} + _op_name = "RANGE" + + def __init__(self, lower, upper, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.lower = lower + self.upper = upper + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self._key = (self.__class__, self.lower, self.upper, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return Range(self.lower, self.upper, positive, case_flags, zerowidth) + + def optimise(self, info, reverse, in_set=False): + # Is the range case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE) or in_set: + return self + + # Is full case-folding possible? + if (not (info.flags & UNICODE) or (self.case_flags & FULLIGNORECASE) != + FULLIGNORECASE): + return self + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the range. + items = [] + for ch in expanding_chars: + if self.lower <= ord(ch) <= self.upper: + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + items.append(String([ord(c) for c in folded], + case_flags=self.case_flags)) + + if not items: + # We can fall back to simple case-folding. + return self + + if len(items) < self.upper - self.lower + 1: + # Not all the characters are covered by the full case-folding. + items.insert(0, self) + + return Branch(items) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.lower, + self.upper)] + + def dump(self, indent, reverse): + display_lower = ascii(chr(self.lower)).lstrip("bu") + display_upper = ascii(chr(self.upper)).lstrip("bu") + print("{}RANGE {} {} {}{}".format(INDENT * indent, + POS_TEXT[self.positive], display_lower, display_upper, + CASE_TEXT[self.case_flags])) + + def matches(self, ch): + return (self.lower <= ch <= self.upper) == self.positive + + def max_width(self): + return 1 + +class RefGroup(RegexBase): + _opcode = {(NOCASE, False): OP.REF_GROUP, (IGNORECASE, False): + OP.REF_GROUP_IGN, (FULLCASE, False): OP.REF_GROUP, (FULLIGNORECASE, + False): OP.REF_GROUP_FLD, (NOCASE, True): OP.REF_GROUP_REV, (IGNORECASE, + True): OP.REF_GROUP_IGN_REV, (FULLCASE, True): OP.REF_GROUP_REV, + (FULLIGNORECASE, True): OP.REF_GROUP_FLD_REV} + + def __init__(self, info, group, position, case_flags=NOCASE): + RegexBase.__init__(self) + self.info = info + self.group = group + self.position = position + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + self._key = self.__class__, self.group, self.case_flags + + def fix_groups(self, pattern, reverse, fuzzy): + try: + self.group = int(self.group) + except ValueError: + try: + self.group = self.info.group_index[self.group] + except KeyError: + raise error("unknown group", pattern, self.position) + + if not 1 <= self.group <= self.info.group_count: + raise error("invalid group reference", pattern, self.position) + + self._key = self.__class__, self.group, self.case_flags + + def remove_captures(self): + raise error("group reference not allowed", pattern, self.position) + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + return [(self._opcode[self.case_flags, reverse], flags, self.group)] + + def dump(self, indent, reverse): + print("{}REF_GROUP {}{}".format(INDENT * indent, self.group, + CASE_TEXT[self.case_flags])) + + def max_width(self): + return UNLIMITED + + def __del__(self): + self.info = None + +class SearchAnchor(ZeroWidthBase): + _opcode = OP.SEARCH_ANCHOR + _op_name = "SEARCH_ANCHOR" + +class Sequence(RegexBase): + def __init__(self, items=None): + RegexBase.__init__(self) + if items is None: + items = [] + + self.items = items + + def fix_groups(self, pattern, reverse, fuzzy): + for s in self.items: + s.fix_groups(pattern, reverse, fuzzy) + + def optimise(self, info, reverse): + # Flatten the sequences. + items = [] + for s in self.items: + s = s.optimise(info, reverse) + if isinstance(s, Sequence): + items.extend(s.items) + else: + items.append(s) + + return make_sequence(items) + + def pack_characters(self, info): + "Packs sequences of characters into strings." + items = [] + characters = [] + case_flags = NOCASE + for s in self.items: + if type(s) is Character and s.positive and not s.zerowidth: + if s.case_flags != case_flags: + # Different case sensitivity, so flush, unless neither the + # previous nor the new character are cased. + if s.case_flags or is_cased_i(info, s.value): + Sequence._flush_characters(info, characters, + case_flags, items) + + case_flags = s.case_flags + + characters.append(s.value) + elif type(s) is String or type(s) is Literal: + if s.case_flags != case_flags: + # Different case sensitivity, so flush, unless the neither + # the previous nor the new string are cased. + if s.case_flags or any(is_cased_i(info, c) for c in + characters): + Sequence._flush_characters(info, characters, + case_flags, items) + + case_flags = s.case_flags + + characters.extend(s.characters) + else: + Sequence._flush_characters(info, characters, case_flags, items) + + items.append(s.pack_characters(info)) + + Sequence._flush_characters(info, characters, case_flags, items) + + return make_sequence(items) + + def remove_captures(self): + self.items = [s.remove_captures() for s in self.items] + return self + + def is_atomic(self): + return all(s.is_atomic() for s in self.items) + + def can_be_affix(self): + return False + + def contains_group(self): + return any(s.contains_group() for s in self.items) + + def get_firstset(self, reverse): + fs = set() + items = self.items + if reverse: + items.reverse() + for s in items: + fs |= s.get_firstset(reverse) + if None not in fs: + return fs + fs.discard(None) + + return fs | set([None]) + + def has_simple_start(self): + return bool(self.items) and self.items[0].has_simple_start() + + def _compile(self, reverse, fuzzy): + seq = self.items + if reverse: + seq = seq[::-1] + + code = [] + for s in seq: + code.extend(s.compile(reverse, fuzzy)) + + return code + + def dump(self, indent, reverse): + for s in self.items: + s.dump(indent, reverse) + + @staticmethod + def _flush_characters(info, characters, case_flags, items): + if not characters: + return + + # Disregard case_flags if all of the characters are case-less. + if case_flags & IGNORECASE: + if not any(is_cased_i(info, c) for c in characters): + case_flags = NOCASE + + if (case_flags & FULLIGNORECASE) == FULLIGNORECASE: + literals = Sequence._fix_full_casefold(characters) + + for item in literals: + chars = item.characters + + if len(chars) == 1: + items.append(Character(chars[0], case_flags=item.case_flags)) + else: + items.append(String(chars, case_flags=item.case_flags)) + else: + if len(characters) == 1: + items.append(Character(characters[0], case_flags=case_flags)) + else: + items.append(String(characters, case_flags=case_flags)) + + characters[:] = [] + + @staticmethod + def _fix_full_casefold(characters): + # Split a literal needing full case-folding into chunks that need it + # and chunks that can use simple case-folding, which is faster. + expanded = [_regex.fold_case(FULL_CASE_FOLDING, c) for c in + _regex.get_expand_on_folding()] + string = _regex.fold_case(FULL_CASE_FOLDING, ''.join(chr(c) + for c in characters)).lower() + chunks = [] + + for e in expanded: + found = string.find(e) + + while found >= 0: + chunks.append((found, found + len(e))) + found = string.find(e, found + 1) + + pos = 0 + literals = [] + + for start, end in Sequence._merge_chunks(chunks): + if pos < start: + literals.append(Literal(characters[pos : start], + case_flags=IGNORECASE)) + + literals.append(Literal(characters[start : end], + case_flags=FULLIGNORECASE)) + pos = end + + if pos < len(characters): + literals.append(Literal(characters[pos : ], case_flags=IGNORECASE)) + + return literals + + @staticmethod + def _merge_chunks(chunks): + if len(chunks) < 2: + return chunks + + chunks.sort() + + start, end = chunks[0] + new_chunks = [] + + for s, e in chunks[1 : ]: + if s <= end: + end = max(end, e) + else: + new_chunks.append((start, end)) + start, end = s, e + + new_chunks.append((start, end)) + + return new_chunks + + def is_empty(self): + return all(i.is_empty() for i in self.items) + + def __eq__(self, other): + return type(self) is type(other) and self.items == other.items + + def max_width(self): + return sum(s.max_width() for s in self.items) + + def get_required_string(self, reverse): + seq = self.items + if reverse: + seq = seq[::-1] + + offset = 0 + + for s in seq: + ofs, req = s.get_required_string(reverse) + offset += ofs + if req: + return offset, req + + return offset, None + +class SetBase(RegexBase): + def __init__(self, info, items, positive=True, case_flags=NOCASE, + zerowidth=False): + RegexBase.__init__(self) + self.info = info + self.items = tuple(items) + self.positive = bool(positive) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + self.zerowidth = bool(zerowidth) + + self.char_width = 1 + + self._key = (self.__class__, self.items, self.positive, + self.case_flags, self.zerowidth) + + def rebuild(self, positive, case_flags, zerowidth): + return type(self)(self.info, self.items, positive, case_flags, + zerowidth).optimise(self.info, False) + + def get_firstset(self, reverse): + return set([self]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + code = [(self._opcode[self.case_flags, reverse], flags)] + for m in self.items: + code.extend(m.compile()) + + code.append((OP.END, )) + + return code + + def dump(self, indent, reverse): + print("{}{} {}{}".format(INDENT * indent, self._op_name, + POS_TEXT[self.positive], CASE_TEXT[self.case_flags])) + for i in self.items: + i.dump(indent + 1, reverse) + + def _handle_case_folding(self, info, in_set): + # Is the set case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE) or in_set: + return self + + # Is full case-folding possible? + if (not (self.info.flags & UNICODE) or (self.case_flags & + FULLIGNORECASE) != FULLIGNORECASE): + return self + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the set. + items = [] + seen = set() + for ch in expanding_chars: + if self.matches(ord(ch)): + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + if folded not in seen: + items.append(String([ord(c) for c in folded], + case_flags=self.case_flags)) + seen.add(folded) + + if not items: + # We can fall back to simple case-folding. + return self + + return Branch([self] + items) + + def max_width(self): + # Is the set case-sensitive? + if not self.positive or not (self.case_flags & IGNORECASE): + return 1 + + # Is full case-folding possible? + if (not (self.info.flags & UNICODE) or (self.case_flags & + FULLIGNORECASE) != FULLIGNORECASE): + return 1 + + # Get the characters which expand to multiple codepoints on folding. + expanding_chars = _regex.get_expand_on_folding() + + # Get the folded characters in the set. + seen = set() + for ch in expanding_chars: + if self.matches(ord(ch)): + folded = _regex.fold_case(FULL_CASE_FOLDING, ch) + seen.add(folded) + + if not seen: + return 1 + + return max(len(folded) for folded in seen) + + def __del__(self): + self.info = None + +class SetDiff(SetBase): + _opcode = {(NOCASE, False): OP.SET_DIFF, (IGNORECASE, False): + OP.SET_DIFF_IGN, (FULLCASE, False): OP.SET_DIFF, (FULLIGNORECASE, False): + OP.SET_DIFF_IGN, (NOCASE, True): OP.SET_DIFF_REV, (IGNORECASE, True): + OP.SET_DIFF_IGN_REV, (FULLCASE, True): OP.SET_DIFF_REV, (FULLIGNORECASE, + True): OP.SET_DIFF_IGN_REV} + _op_name = "SET_DIFF" + + def optimise(self, info, reverse, in_set=False): + items = self.items + if len(items) > 2: + items = [items[0], SetUnion(info, items[1 : ])] + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, reverse, in_set) + + self.items = tuple(m.optimise(info, reverse, in_set=True) for m in + items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = self.items[0].matches(ch) and not self.items[1].matches(ch) + return m == self.positive + +class SetInter(SetBase): + _opcode = {(NOCASE, False): OP.SET_INTER, (IGNORECASE, False): + OP.SET_INTER_IGN, (FULLCASE, False): OP.SET_INTER, (FULLIGNORECASE, + False): OP.SET_INTER_IGN, (NOCASE, True): OP.SET_INTER_REV, (IGNORECASE, + True): OP.SET_INTER_IGN_REV, (FULLCASE, True): OP.SET_INTER_REV, + (FULLIGNORECASE, True): OP.SET_INTER_IGN_REV} + _op_name = "SET_INTER" + + def optimise(self, info, reverse, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, reverse, in_set=True) + if isinstance(m, SetInter) and m.positive: + # Intersection in intersection. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, reverse, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = all(i.matches(ch) for i in self.items) + return m == self.positive + +class SetSymDiff(SetBase): + _opcode = {(NOCASE, False): OP.SET_SYM_DIFF, (IGNORECASE, False): + OP.SET_SYM_DIFF_IGN, (FULLCASE, False): OP.SET_SYM_DIFF, (FULLIGNORECASE, + False): OP.SET_SYM_DIFF_IGN, (NOCASE, True): OP.SET_SYM_DIFF_REV, + (IGNORECASE, True): OP.SET_SYM_DIFF_IGN_REV, (FULLCASE, True): + OP.SET_SYM_DIFF_REV, (FULLIGNORECASE, True): OP.SET_SYM_DIFF_IGN_REV} + _op_name = "SET_SYM_DIFF" + + def optimise(self, info, reverse, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, reverse, in_set=True) + if isinstance(m, SetSymDiff) and m.positive: + # Symmetric difference in symmetric difference. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + return items[0].with_flags(case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, reverse, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def matches(self, ch): + m = False + for i in self.items: + m = m != i.matches(ch) + + return m == self.positive + +class SetUnion(SetBase): + _opcode = {(NOCASE, False): OP.SET_UNION, (IGNORECASE, False): + OP.SET_UNION_IGN, (FULLCASE, False): OP.SET_UNION, (FULLIGNORECASE, + False): OP.SET_UNION_IGN, (NOCASE, True): OP.SET_UNION_REV, (IGNORECASE, + True): OP.SET_UNION_IGN_REV, (FULLCASE, True): OP.SET_UNION_REV, + (FULLIGNORECASE, True): OP.SET_UNION_IGN_REV} + _op_name = "SET_UNION" + + def optimise(self, info, reverse, in_set=False): + items = [] + for m in self.items: + m = m.optimise(info, reverse, in_set=True) + if isinstance(m, SetUnion) and m.positive: + # Union in union. + items.extend(m.items) + else: + items.append(m) + + if len(items) == 1: + i = items[0] + return i.with_flags(positive=i.positive == self.positive, + case_flags=self.case_flags, + zerowidth=self.zerowidth).optimise(info, reverse, in_set) + + self.items = tuple(items) + + return self._handle_case_folding(info, in_set) + + def _compile(self, reverse, fuzzy): + flags = 0 + if self.positive: + flags |= POSITIVE_OP + if self.zerowidth: + flags |= ZEROWIDTH_OP + if fuzzy: + flags |= FUZZY_OP + + characters, others = defaultdict(list), [] + for m in self.items: + if isinstance(m, Character): + characters[m.positive].append(m.value) + else: + others.append(m) + + code = [(self._opcode[self.case_flags, reverse], flags)] + + for positive, values in characters.items(): + flags = 0 + if positive: + flags |= POSITIVE_OP + if len(values) == 1: + code.append((OP.CHARACTER, flags, values[0])) + else: + code.append((OP.STRING, flags, len(values)) + tuple(values)) + + for m in others: + code.extend(m.compile()) + + code.append((OP.END, )) + + return code + + def matches(self, ch): + m = any(i.matches(ch) for i in self.items) + return m == self.positive + +class Skip(ZeroWidthBase): + _op_name = "SKIP" + _opcode = OP.SKIP + +class StartOfLine(ZeroWidthBase): + _opcode = OP.START_OF_LINE + _op_name = "START_OF_LINE" + +class StartOfLineU(StartOfLine): + _opcode = OP.START_OF_LINE_U + _op_name = "START_OF_LINE_U" + +class StartOfString(ZeroWidthBase): + _opcode = OP.START_OF_STRING + _op_name = "START_OF_STRING" + +class StartOfWord(ZeroWidthBase): + _opcode = OP.START_OF_WORD + _op_name = "START_OF_WORD" + +class String(RegexBase): + _opcode = {(NOCASE, False): OP.STRING, (IGNORECASE, False): OP.STRING_IGN, + (FULLCASE, False): OP.STRING, (FULLIGNORECASE, False): OP.STRING_FLD, + (NOCASE, True): OP.STRING_REV, (IGNORECASE, True): OP.STRING_IGN_REV, + (FULLCASE, True): OP.STRING_REV, (FULLIGNORECASE, True): + OP.STRING_FLD_REV} + + def __init__(self, characters, case_flags=NOCASE): + self.characters = tuple(characters) + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + if (self.case_flags & FULLIGNORECASE) == FULLIGNORECASE: + folded_characters = [] + for char in self.characters: + folded = _regex.fold_case(FULL_CASE_FOLDING, chr(char)) + folded_characters.extend(ord(c) for c in folded) + else: + folded_characters = self.characters + + self.folded_characters = tuple(folded_characters) + self.required = False + + self._key = self.__class__, self.characters, self.case_flags + + def get_firstset(self, reverse): + if reverse: + pos = -1 + else: + pos = 0 + return set([Character(self.characters[pos], + case_flags=self.case_flags)]) + + def has_simple_start(self): + return True + + def _compile(self, reverse, fuzzy): + flags = 0 + if fuzzy: + flags |= FUZZY_OP + if self.required: + flags |= REQUIRED_OP + return [(self._opcode[self.case_flags, reverse], flags, + len(self.folded_characters)) + self.folded_characters] + + def dump(self, indent, reverse): + display = ascii("".join(chr(c) for c in self.characters)).lstrip("bu") + print("{}STRING {}{}".format(INDENT * indent, display, + CASE_TEXT[self.case_flags])) + + def max_width(self): + return len(self.folded_characters) + + def get_required_string(self, reverse): + return 0, self + +class Literal(String): + def dump(self, indent, reverse): + literal = ''.join(chr(c) for c in self.characters) + display = ascii(literal).lstrip("bu") + print("{}LITERAL MATCH {}{}".format(INDENT * indent, display, + CASE_TEXT[self.case_flags])) + +class StringSet(Branch): + def __init__(self, info, name, case_flags=NOCASE): + self.info = info + self.name = name + self.case_flags = CASE_FLAGS_COMBINATIONS[case_flags] + + self._key = self.__class__, self.name, self.case_flags + + self.set_key = (name, self.case_flags) + if self.set_key not in info.named_lists_used: + info.named_lists_used[self.set_key] = len(info.named_lists_used) + + index = self.info.named_lists_used[self.set_key] + items = self.info.kwargs[self.name] + + case_flags = self.case_flags + + encoding = self.info.flags & _ALL_ENCODINGS + fold_flags = encoding | case_flags + + choices = [] + + for string in items: + if isinstance(string, str): + string = [ord(c) for c in string] + + choices.append([Character(c, case_flags=case_flags) for c in + string]) + + # Sort from longest to shortest. + choices.sort(key=len, reverse=True) + + self.branches = [Sequence(choice) for choice in choices] + + def dump(self, indent, reverse): + print("{}STRING_SET {}{}".format(INDENT * indent, self.name, + CASE_TEXT[self.case_flags])) + + def __del__(self): + self.info = None + +class Source: + "Scanner for the regular expression source string." + def __init__(self, string): + if isinstance(string, str): + self.string = string + self.char_type = chr + else: + self.string = string.decode("latin-1") + self.char_type = lambda c: bytes([c]) + + self.pos = 0 + self.ignore_space = False + self.sep = string[ : 0] + + def get(self, override_ignore=False): + string = self.string + pos = self.pos + + try: + if self.ignore_space and not override_ignore: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + ch = string[pos] + self.pos = pos + 1 + return ch + except IndexError: + # We've reached the end of the string. + self.pos = pos + return string[ : 0] + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + return string[ : 0] + + def get_many(self, count=1): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + substring = [] + + while len(substring) < count: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + substring.append(string[pos]) + pos += 1 + + substring = "".join(substring) + else: + substring = string[pos : pos + count] + pos += len(substring) + + self.pos = pos + return substring + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + return "".join(substring) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + return "".join(substring) + + def get_while(self, test_set, include=True): + string = self.string + pos = self.pos + + if self.ignore_space: + try: + substring = [] + + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + elif (string[pos] in test_set) == include: + substring.append(string[pos]) + pos += 1 + else: + break + + self.pos = pos + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + + return "".join(substring) + else: + try: + while (string[pos] in test_set) == include: + pos += 1 + + substring = string[self.pos : pos] + + self.pos = pos + + return substring + except IndexError: + # We've reached the end of the string. + substring = string[self.pos : pos] + + self.pos = pos + + return substring + + def skip_while(self, test_set, include=True): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + elif (string[pos] in test_set) == include: + pos += 1 + else: + break + else: + while (string[pos] in test_set) == include: + pos += 1 + + self.pos = pos + except IndexError: + # We've reached the end of the string. + self.pos = len(string) + except ValueError: + # The comment extended to the end of the string. + self.pos = len(string) + + def match(self, substring): + string = self.string + pos = self.pos + + if self.ignore_space: + try: + for c in substring: + while True: + if string[pos].isspace(): + # Skip over the whitespace. + pos += 1 + elif string[pos] == "#": + # Skip over the comment to the end of the line. + pos = string.index("\n", pos) + else: + break + + if string[pos] != c: + return False + + pos += 1 + + self.pos = pos + + return True + except IndexError: + # We've reached the end of the string. + return False + except ValueError: + # The comment extended to the end of the string. + return False + else: + if not string.startswith(substring, pos): + return False + + self.pos = pos + len(substring) + + return True + + def expect(self, substring): + if not self.match(substring): + raise error("missing {}".format(substring), self.string, self.pos) + + def at_end(self): + string = self.string + pos = self.pos + + try: + if self.ignore_space: + while True: + if string[pos].isspace(): + pos += 1 + elif string[pos] == "#": + pos = string.index("\n", pos) + else: + break + + return pos >= len(string) + except IndexError: + # We've reached the end of the string. + return True + except ValueError: + # The comment extended to the end of the string. + return True + +class Info: + "Info about the regular expression." + + def __init__(self, flags=0, char_type=None, kwargs={}): + flags |= DEFAULT_FLAGS[(flags & _ALL_VERSIONS) or DEFAULT_VERSION] + self.flags = flags + self.global_flags = flags + self.inline_locale = False + + self.kwargs = kwargs + + self.group_count = 0 + self.group_index = {} + self.group_name = {} + self.char_type = char_type + self.named_lists_used = {} + self.open_groups = [] + self.open_group_count = {} + self.defined_groups = {} + self.group_calls = [] + self.private_groups = {} + + def open_group(self, name=None): + group = self.group_index.get(name) + if group is None: + while True: + self.group_count += 1 + if name is None or self.group_count not in self.group_name: + break + + group = self.group_count + if name: + self.group_index[name] = group + self.group_name[group] = name + + if group in self.open_groups: + # We have a nested named group. We'll assign it a private group + # number, initially negative until we can assign a proper + # (positive) number. + group_alias = -(len(self.private_groups) + 1) + self.private_groups[group_alias] = group + group = group_alias + + self.open_groups.append(group) + self.open_group_count[group] = self.open_group_count.get(group, 0) + 1 + + return group + + def close_group(self): + self.open_groups.pop() + + def is_open_group(self, name): + # In version 1, a group reference can refer to an open group. We'll + # just pretend the group isn't open. + version = (self.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version == VERSION1: + return False + + if name.isdigit(): + group = int(name) + else: + group = self.group_index.get(name) + + return group in self.open_groups + +def _check_group_features(info, parsed): + """Checks whether the reverse and fuzzy features of the group calls match + the groups which they call. + """ + call_refs = {} + additional_groups = [] + for call, reverse, fuzzy in info.group_calls: + # Look up the reference of this group call. + key = (call.group, reverse, fuzzy) + ref = call_refs.get(key) + if ref is None: + # This group doesn't have a reference yet, so look up its features. + if call.group == 0: + # Calling the pattern as a whole. + rev = bool(info.flags & REVERSE) + fuz = isinstance(parsed, Fuzzy) + if (rev, fuz) != (reverse, fuzzy): + # The pattern as a whole doesn't have the features we want, + # so we'll need to make a copy of it with the desired + # features. + additional_groups.append((CallRef(len(call_refs), parsed), + reverse, fuzzy)) + else: + # Calling a capture group. + def_info = info.defined_groups[call.group] + group = def_info[0] + if def_info[1 : ] != (reverse, fuzzy): + # The group doesn't have the features we want, so we'll + # need to make a copy of it with the desired features. + additional_groups.append((group, reverse, fuzzy)) + + ref = len(call_refs) + call_refs[key] = ref + + call.call_ref = ref + + info.call_refs = call_refs + info.additional_groups = additional_groups + +def _get_required_string(parsed, flags): + "Gets the required string and related info of a parsed pattern." + + req_offset, required = parsed.get_required_string(bool(flags & REVERSE)) + if required: + required.required = True + if req_offset >= UNLIMITED: + req_offset = -1 + + req_flags = required.case_flags + if not (flags & UNICODE): + req_flags &= ~UNICODE + + req_chars = required.folded_characters + else: + req_offset = 0 + req_chars = () + req_flags = 0 + + return req_offset, req_chars, req_flags + +class Scanner: + def __init__(self, lexicon, flags=0): + self.lexicon = lexicon + + # Combine phrases into a compound pattern. + patterns = [] + for phrase, action in lexicon: + # Parse the regular expression. + source = Source(phrase) + info = Info(flags, source.char_type) + source.ignore_space = bool(info.flags & VERBOSE) + parsed = _parse_pattern(source, info) + if not source.at_end(): + raise error("unbalanced parenthesis", source.string, + source.pos) + + # We want to forbid capture groups within each phrase. + patterns.append(parsed.remove_captures()) + + # Combine all the subpatterns into one pattern. + info = Info(flags) + patterns = [Group(info, g + 1, p) for g, p in enumerate(patterns)] + parsed = Branch(patterns) + + # Optimise the compound pattern. + reverse = bool(info.flags & REVERSE) + parsed = parsed.optimise(info, reverse) + parsed = parsed.pack_characters(info) + + # Get the required string. + req_offset, req_chars, req_flags = _get_required_string(parsed, + info.flags) + + # Check the features of the groups. + _check_group_features(info, parsed) + + # Complain if there are any group calls. They are not supported by the + # Scanner class. + if info.call_refs: + raise error("recursive regex not supported by Scanner", + source.string, source.pos) + + reverse = bool(info.flags & REVERSE) + + # Compile the compound pattern. The result is a list of tuples. + code = parsed.compile(reverse) + [(OP.SUCCESS, )] + + # Flatten the code into a list of ints. + code = _flatten_code(code) + + if not parsed.has_simple_start(): + # Get the first set, if possible. + try: + fs_code = _compile_firstset(info, parsed.get_firstset(reverse)) + fs_code = _flatten_code(fs_code) + code = fs_code + code + except _FirstSetError: + pass + + # Check the global flags for conflicts. + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version not in (0, VERSION0, VERSION1): + raise ValueError("VERSION0 and VERSION1 flags are mutually incompatible") + + # Create the PatternObject. + # + # Local flags like IGNORECASE affect the code generation, but aren't + # needed by the PatternObject itself. Conversely, global flags like + # LOCALE _don't_ affect the code generation but _are_ needed by the + # PatternObject. + self.scanner = _regex.compile(None, (flags & GLOBAL_FLAGS) | version, + code, {}, {}, {}, [], req_offset, req_chars, req_flags, + len(patterns)) + + def scan(self, string): + result = [] + append = result.append + match = self.scanner.scanner(string).match + i = 0 + while True: + m = match() + if not m: + break + j = m.end() + if i == j: + break + action = self.lexicon[m.lastindex - 1][1] + if hasattr(action, '__call__'): + self.match = m + action = action(self, m.group()) + if action is not None: + append(action) + i = j + + return result, string[i : ] + +# Get the known properties dict. +PROPERTIES = _regex.get_properties() + +# Build the inverse of the properties dict. +PROPERTY_NAMES = {} +for prop_name, (prop_id, values) in PROPERTIES.items(): + name, prop_values = PROPERTY_NAMES.get(prop_id, ("", {})) + name = max(name, prop_name, key=len) + PROPERTY_NAMES[prop_id] = name, prop_values + + for val_name, val_id in values.items(): + prop_values[val_id] = max(prop_values.get(val_id, ""), val_name, + key=len) + +# Character escape sequences. +CHARACTER_ESCAPES = { + "a": "\a", + "b": "\b", + "f": "\f", + "n": "\n", + "r": "\r", + "t": "\t", + "v": "\v", +} + +# Predefined character set escape sequences. +CHARSET_ESCAPES = { + "d": lookup_property(None, "Digit", True), + "D": lookup_property(None, "Digit", False), + "h": lookup_property(None, "Blank", True), + "s": lookup_property(None, "Space", True), + "S": lookup_property(None, "Space", False), + "w": lookup_property(None, "Word", True), + "W": lookup_property(None, "Word", False), +} + +# Positional escape sequences. +POSITION_ESCAPES = { + "A": StartOfString(), + "b": Boundary(), + "B": Boundary(False), + "K": Keep(), + "m": StartOfWord(), + "M": EndOfWord(), + "Z": EndOfString(), +} + +# Positional escape sequences when WORD flag set. +WORD_POSITION_ESCAPES = dict(POSITION_ESCAPES) +WORD_POSITION_ESCAPES.update({ + "b": DefaultBoundary(), + "B": DefaultBoundary(False), + "m": DefaultStartOfWord(), + "M": DefaultEndOfWord(), +}) + +# Regex control verbs. +VERBS = { + "FAIL": Failure(), + "F": Failure(), + "PRUNE": Prune(), + "SKIP": Skip(), +} diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/regex.py b/.pythonlibs/lib/python3.10/site-packages/regex/regex.py new file mode 100644 index 0000000..e467685 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex/regex.py @@ -0,0 +1,746 @@ +# +# Secret Labs' Regular Expression Engine +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# +# 2010-01-16 mrab Python front-end re-written and extended + +r"""Support for regular expressions (RE). + +This module provides regular expression matching operations similar to those +found in Perl. It supports both 8-bit and Unicode strings; both the pattern and +the strings being processed can contain null bytes and characters outside the +US ASCII range. + +Regular expressions can contain both special and ordinary characters. Most +ordinary characters, like "A", "a", or "0", are the simplest regular +expressions; they simply match themselves. You can concatenate ordinary +characters, so last matches the string 'last'. + +There are a few differences between the old (legacy) behaviour and the new +(enhanced) behaviour, which are indicated by VERSION0 or VERSION1. + +The special characters are: + "." Matches any character except a newline. + "^" Matches the start of the string. + "$" Matches the end of the string or just before the + newline at the end of the string. + "*" Matches 0 or more (greedy) repetitions of the preceding + RE. Greedy means that it will match as many repetitions + as possible. + "+" Matches 1 or more (greedy) repetitions of the preceding + RE. + "?" Matches 0 or 1 (greedy) of the preceding RE. + *?,+?,?? Non-greedy versions of the previous three special + characters. + *+,++,?+ Possessive versions of the previous three special + characters. + {m,n} Matches from m to n repetitions of the preceding RE. + {m,n}? Non-greedy version of the above. + {m,n}+ Possessive version of the above. + {...} Fuzzy matching constraints. + "\\" Either escapes special characters or signals a special + sequence. + [...] Indicates a set of characters. A "^" as the first + character indicates a complementing set. + "|" A|B, creates an RE that will match either A or B. + (...) Matches the RE inside the parentheses. The contents are + captured and can be retrieved or matched later in the + string. + (?flags-flags) VERSION1: Sets/clears the flags for the remainder of + the group or pattern; VERSION0: Sets the flags for the + entire pattern. + (?:...) Non-capturing version of regular parentheses. + (?>...) Atomic non-capturing version of regular parentheses. + (?flags-flags:...) Non-capturing version of regular parentheses with local + flags. + (?P...) The substring matched by the group is accessible by + name. + (?...) The substring matched by the group is accessible by + name. + (?P=name) Matches the text matched earlier by the group named + name. + (?#...) A comment; ignored. + (?=...) Matches if ... matches next, but doesn't consume the + string. + (?!...) Matches if ... doesn't match next. + (?<=...) Matches if preceded by .... + (? Matches the text matched by the group named name. + \G Matches the empty string, but only at the position where + the search started. + \h Matches horizontal whitespace. + \K Keeps only what follows for the entire match. + \L Named list. The list is provided as a keyword argument. + \m Matches the empty string, but only at the start of a word. + \M Matches the empty string, but only at the end of a word. + \n Matches the newline character. + \N{name} Matches the named character. + \p{name=value} Matches the character if its property has the specified + value. + \P{name=value} Matches the character if its property hasn't the specified + value. + \r Matches the carriage-return character. + \s Matches any whitespace character; equivalent to + [ \t\n\r\f\v]. + \S Matches any non-whitespace character; equivalent to [^\s]. + \t Matches the tab character. + \uXXXX Matches the Unicode codepoint with 4-digit hex code XXXX. + \UXXXXXXXX Matches the Unicode codepoint with 8-digit hex code + XXXXXXXX. + \v Matches the vertical tab character. + \w Matches any alphanumeric character; equivalent to + [a-zA-Z0-9_] when matching a bytestring or a Unicode string + with the ASCII flag, or the whole range of Unicode + alphanumeric characters (letters plus digits plus + underscore) when matching a Unicode string. With LOCALE, it + will match the set [0-9_] plus characters defined as + letters for the current locale. + \W Matches the complement of \w; equivalent to [^\w]. + \xXX Matches the character with 2-digit hex code XX. + \X Matches a grapheme. + \Z Matches only at the end of the string. + \\ Matches a literal backslash. + +This module exports the following functions: + match Match a regular expression pattern at the beginning of a string. + fullmatch Match a regular expression pattern against all of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string using a + template string. + subf Substitute occurrences of a pattern found in a string using a + format string. + subn Same as sub, but also return the number of substitutions made. + subfn Same as subf, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. VERSION1: will + split at zero-width match; VERSION0: won't split at zero-width + match. + splititer Return an iterator yielding the parts of a split string. + findall Find all occurrences of a pattern in a string. + finditer Return an iterator yielding a match object for each match. + compile Compile a pattern into a Pattern object. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics or special characters in a + string. + +Most of the functions support a concurrent parameter: if True, the GIL will be +released during matching, allowing other Python threads to run concurrently. If +the string changes during matching, the behaviour is undefined. This parameter +is not needed when working on the builtin (immutable) string classes. + +Some of the functions in this module take flags as optional parameters. Most of +these flags can also be set within an RE: + A a ASCII Make \w, \W, \b, \B, \d, and \D match the + corresponding ASCII character categories. Default + when matching a bytestring. + B b BESTMATCH Find the best fuzzy match (default is first). + D DEBUG Print the parsed pattern. + E e ENHANCEMATCH Attempt to improve the fit after finding the first + fuzzy match. + F f FULLCASE Use full case-folding when performing + case-insensitive matching in Unicode. + I i IGNORECASE Perform case-insensitive matching. + L L LOCALE Make \w, \W, \b, \B, \d, and \D dependent on the + current locale. (One byte per character only.) + M m MULTILINE "^" matches the beginning of lines (after a newline) + as well as the string. "$" matches the end of lines + (before a newline) as well as the end of the string. + P p POSIX Perform POSIX-standard matching (leftmost longest). + R r REVERSE Searches backwards. + S s DOTALL "." matches any character at all, including the + newline. + U u UNICODE Make \w, \W, \b, \B, \d, and \D dependent on the + Unicode locale. Default when matching a Unicode + string. + V0 V0 VERSION0 Turn on the old legacy behaviour. + V1 V1 VERSION1 Turn on the new enhanced behaviour. This flag + includes the FULLCASE flag. + W w WORD Make \b and \B work with default Unicode word breaks + and make ".", "^" and "$" work with Unicode line + breaks. + X x VERBOSE Ignore whitespace and comments for nicer looking REs. + +This module also defines an exception 'error'. + +""" + +# Public symbols. +__all__ = ["cache_all", "compile", "DEFAULT_VERSION", "escape", "findall", + "finditer", "fullmatch", "match", "purge", "search", "split", "splititer", + "sub", "subf", "subfn", "subn", "template", "Scanner", "A", "ASCII", "B", + "BESTMATCH", "D", "DEBUG", "E", "ENHANCEMATCH", "S", "DOTALL", "F", + "FULLCASE", "I", "IGNORECASE", "L", "LOCALE", "M", "MULTILINE", "P", "POSIX", + "R", "REVERSE", "T", "TEMPLATE", "U", "UNICODE", "V0", "VERSION0", "V1", + "VERSION1", "X", "VERBOSE", "W", "WORD", "error", "Regex", "__version__", + "__doc__", "RegexFlag"] + +__version__ = "2.5.141" + +# -------------------------------------------------------------------- +# Public interface. + +def match(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Try to apply the pattern at the start of the string, returning a match + object, or None if no match was found.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.match(string, pos, endpos, concurrent, partial, timeout) + +def fullmatch(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Try to apply the pattern against all of the string, returning a match + object, or None if no match was found.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.fullmatch(string, pos, endpos, concurrent, partial, timeout) + +def search(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Search through string looking for a match to the pattern, returning a + match object, or None if no match was found.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.search(string, pos, endpos, concurrent, partial, timeout) + +def sub(pattern, repl, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return the string obtained by replacing the leftmost (or rightmost with a + reverse pattern) non-overlapping occurrences of the pattern in string by the + replacement repl. repl can be either a string or a callable; if a string, + backslash escapes in it are processed; if a callable, it's passed the match + object and must return a replacement string to be used.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.sub(repl, string, count, pos, endpos, concurrent, timeout) + +def subf(pattern, format, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return the string obtained by replacing the leftmost (or rightmost with a + reverse pattern) non-overlapping occurrences of the pattern in string by the + replacement format. format can be either a string or a callable; if a string, + it's treated as a format string; if a callable, it's passed the match object + and must return a replacement string to be used.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.subf(format, string, count, pos, endpos, concurrent, timeout) + +def subn(pattern, repl, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return a 2-tuple containing (new_string, number). new_string is the string + obtained by replacing the leftmost (or rightmost with a reverse pattern) + non-overlapping occurrences of the pattern in the source string by the + replacement repl. number is the number of substitutions that were made. repl + can be either a string or a callable; if a string, backslash escapes in it + are processed; if a callable, it's passed the match object and must return a + replacement string to be used.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.subn(repl, string, count, pos, endpos, concurrent, timeout) + +def subfn(pattern, format, string, count=0, flags=0, pos=None, endpos=None, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return a 2-tuple containing (new_string, number). new_string is the string + obtained by replacing the leftmost (or rightmost with a reverse pattern) + non-overlapping occurrences of the pattern in the source string by the + replacement format. number is the number of substitutions that were made. format + can be either a string or a callable; if a string, it's treated as a format + string; if a callable, it's passed the match object and must return a + replacement string to be used.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.subfn(format, string, count, pos, endpos, concurrent, timeout) + +def split(pattern, string, maxsplit=0, flags=0, concurrent=None, timeout=None, + ignore_unused=False, **kwargs): + """Split the source string by the occurrences of the pattern, returning a + list containing the resulting substrings. If capturing parentheses are used + in pattern, then the text of all groups in the pattern are also returned as + part of the resulting list. If maxsplit is nonzero, at most maxsplit splits + occur, and the remainder of the string is returned as the final element of + the list.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.split(string, maxsplit, concurrent, timeout) + +def splititer(pattern, string, maxsplit=0, flags=0, concurrent=None, + timeout=None, ignore_unused=False, **kwargs): + "Return an iterator yielding the parts of a split string." + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.splititer(string, maxsplit, concurrent, timeout) + +def findall(pattern, string, flags=0, pos=None, endpos=None, overlapped=False, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return a list of all matches in the string. The matches may be overlapped + if overlapped is True. If one or more groups are present in the pattern, + return a list of groups; this will be a list of tuples if the pattern has + more than one group. Empty matches are included in the result.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.findall(string, pos, endpos, overlapped, concurrent, timeout) + +def finditer(pattern, string, flags=0, pos=None, endpos=None, overlapped=False, + partial=False, concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Return an iterator over all matches in the string. The matches may be + overlapped if overlapped is True. For each match, the iterator returns a + match object. Empty matches are included in the result.""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.finditer(string, pos, endpos, overlapped, concurrent, partial, + timeout) + +def compile(pattern, flags=0, ignore_unused=False, cache_pattern=None, **kwargs): + "Compile a regular expression pattern, returning a pattern object." + if cache_pattern is None: + cache_pattern = _cache_all + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + +def purge(): + "Clear the regular expression cache" + _cache.clear() + _locale_sensitive.clear() + +# Whether to cache all patterns. +_cache_all = True + +def cache_all(value=True): + """Sets whether to cache all patterns, even those are compiled explicitly. + Passing None has no effect, but returns the current setting.""" + global _cache_all + + if value is None: + return _cache_all + + _cache_all = value + +def template(pattern, flags=0): + "Compile a template pattern, returning a pattern object." + return _compile(pattern, flags | TEMPLATE, False, {}, False) + +def escape(pattern, special_only=True, literal_spaces=False): + """Escape a string for use as a literal in a pattern. If special_only is + True, escape only special characters, else escape all non-alphanumeric + characters. If literal_spaces is True, don't escape spaces.""" + # Convert it to Unicode. + if isinstance(pattern, bytes): + p = pattern.decode("latin-1") + else: + p = pattern + + s = [] + if special_only: + for c in p: + if c == " " and literal_spaces: + s.append(c) + elif c in _METACHARS or c.isspace(): + s.append("\\") + s.append(c) + else: + s.append(c) + else: + for c in p: + if c == " " and literal_spaces: + s.append(c) + elif c in _ALNUM: + s.append(c) + else: + s.append("\\") + s.append(c) + + r = "".join(s) + # Convert it back to bytes if necessary. + if isinstance(pattern, bytes): + r = r.encode("latin-1") + + return r + +# -------------------------------------------------------------------- +# Internals. + +import regex._regex_core as _regex_core +import regex._regex as _regex +from threading import RLock as _RLock +from locale import getpreferredencoding as _getpreferredencoding +from regex._regex_core import * +from regex._regex_core import (_ALL_VERSIONS, _ALL_ENCODINGS, _FirstSetError, + _UnscopedFlagSet, _check_group_features, _compile_firstset, + _compile_replacement, _flatten_code, _fold_case, _get_required_string, + _parse_pattern, _shrink_cache) +from regex._regex_core import (ALNUM as _ALNUM, Info as _Info, OP as _OP, Source + as _Source, Fuzzy as _Fuzzy) + +# Version 0 is the old behaviour, compatible with the original 're' module. +# Version 1 is the new behaviour, which differs slightly. + +DEFAULT_VERSION = VERSION0 + +_METACHARS = frozenset("()[]{}?*+|^$\\.-#&~") + +_regex_core.DEFAULT_VERSION = DEFAULT_VERSION + +# Caches for the patterns and replacements. +_cache = {} +_cache_lock = _RLock() +_named_args = {} +_replacement_cache = {} +_locale_sensitive = {} + +# Maximum size of the cache. +_MAXCACHE = 500 +_MAXREPCACHE = 500 + +def _compile(pattern, flags, ignore_unused, kwargs, cache_it): + "Compiles a regular expression to a PatternObject." + + global DEFAULT_VERSION + try: + from regex import DEFAULT_VERSION + except ImportError: + pass + + # We won't bother to cache the pattern if we're debugging. + if (flags & DEBUG) != 0: + cache_it = False + + # What locale is this pattern using? + locale_key = (type(pattern), pattern) + if _locale_sensitive.get(locale_key, True) or (flags & LOCALE) != 0: + # This pattern is, or might be, locale-sensitive. + pattern_locale = _getpreferredencoding() + else: + # This pattern is definitely not locale-sensitive. + pattern_locale = None + + def complain_unused_args(): + if ignore_unused: + return + + # Complain about any unused keyword arguments, possibly resulting from a typo. + unused_kwargs = set(kwargs) - {k for k, v in args_needed} + if unused_kwargs: + any_one = next(iter(unused_kwargs)) + raise ValueError('unused keyword argument {!a}'.format(any_one)) + + if cache_it: + try: + # Do we know what keyword arguments are needed? + args_key = pattern, type(pattern), flags + args_needed = _named_args[args_key] + + # Are we being provided with its required keyword arguments? + args_supplied = set() + if args_needed: + for k, v in args_needed: + try: + args_supplied.add((k, frozenset(kwargs[k]))) + except KeyError: + raise error("missing named list: {!r}".format(k)) + + complain_unused_args() + + args_supplied = frozenset(args_supplied) + + # Have we already seen this regular expression and named list? + pattern_key = (pattern, type(pattern), flags, args_supplied, + DEFAULT_VERSION, pattern_locale) + return _cache[pattern_key] + except KeyError: + # It's a new pattern, or new named list for a known pattern. + pass + + # Guess the encoding from the class of the pattern string. + if isinstance(pattern, str): + guess_encoding = UNICODE + elif isinstance(pattern, bytes): + guess_encoding = ASCII + elif isinstance(pattern, Pattern): + if flags: + raise ValueError("cannot process flags argument with a compiled pattern") + + return pattern + else: + raise TypeError("first argument must be a string or compiled pattern") + + # Set the default version in the core code in case it has been changed. + _regex_core.DEFAULT_VERSION = DEFAULT_VERSION + + global_flags = flags + + while True: + caught_exception = None + try: + source = _Source(pattern) + info = _Info(global_flags, source.char_type, kwargs) + info.guess_encoding = guess_encoding + source.ignore_space = bool(info.flags & VERBOSE) + parsed = _parse_pattern(source, info) + break + except _UnscopedFlagSet: + # Remember the global flags for the next attempt. + global_flags = info.global_flags + except error as e: + caught_exception = e + + if caught_exception: + raise error(caught_exception.msg, caught_exception.pattern, + caught_exception.pos) + + if not source.at_end(): + raise error("unbalanced parenthesis", pattern, source.pos) + + # Check the global flags for conflicts. + version = (info.flags & _ALL_VERSIONS) or DEFAULT_VERSION + if version not in (0, VERSION0, VERSION1): + raise ValueError("VERSION0 and VERSION1 flags are mutually incompatible") + + if (info.flags & _ALL_ENCODINGS) not in (0, ASCII, LOCALE, UNICODE): + raise ValueError("ASCII, LOCALE and UNICODE flags are mutually incompatible") + + if isinstance(pattern, bytes) and (info.flags & UNICODE): + raise ValueError("cannot use UNICODE flag with a bytes pattern") + + if not (info.flags & _ALL_ENCODINGS): + if isinstance(pattern, str): + info.flags |= UNICODE + else: + info.flags |= ASCII + + reverse = bool(info.flags & REVERSE) + fuzzy = isinstance(parsed, _Fuzzy) + + # Remember whether this pattern as an inline locale flag. + _locale_sensitive[locale_key] = info.inline_locale + + # Fix the group references. + caught_exception = None + try: + parsed.fix_groups(pattern, reverse, False) + except error as e: + caught_exception = e + + if caught_exception: + raise error(caught_exception.msg, caught_exception.pattern, + caught_exception.pos) + + # Should we print the parsed pattern? + if flags & DEBUG: + parsed.dump(indent=0, reverse=reverse) + + # Optimise the parsed pattern. + parsed = parsed.optimise(info, reverse) + parsed = parsed.pack_characters(info) + + # Get the required string. + req_offset, req_chars, req_flags = _get_required_string(parsed, info.flags) + + # Build the named lists. + named_lists = {} + named_list_indexes = [None] * len(info.named_lists_used) + args_needed = set() + for key, index in info.named_lists_used.items(): + name, case_flags = key + values = frozenset(kwargs[name]) + if case_flags: + items = frozenset(_fold_case(info, v) for v in values) + else: + items = values + named_lists[name] = values + named_list_indexes[index] = items + args_needed.add((name, values)) + + complain_unused_args() + + # Check the features of the groups. + _check_group_features(info, parsed) + + # Compile the parsed pattern. The result is a list of tuples. + code = parsed.compile(reverse) + + # Is there a group call to the pattern as a whole? + key = (0, reverse, fuzzy) + ref = info.call_refs.get(key) + if ref is not None: + code = [(_OP.CALL_REF, ref)] + code + [(_OP.END, )] + + # Add the final 'success' opcode. + code += [(_OP.SUCCESS, )] + + # Compile the additional copies of the groups that we need. + for group, rev, fuz in info.additional_groups: + code += group.compile(rev, fuz) + + # Flatten the code into a list of ints. + code = _flatten_code(code) + + if not parsed.has_simple_start(): + # Get the first set, if possible. + try: + fs_code = _compile_firstset(info, parsed.get_firstset(reverse)) + fs_code = _flatten_code(fs_code) + code = fs_code + code + except _FirstSetError: + pass + + # The named capture groups. + index_group = dict((v, n) for n, v in info.group_index.items()) + + # Create the PatternObject. + # + # Local flags like IGNORECASE affect the code generation, but aren't needed + # by the PatternObject itself. Conversely, global flags like LOCALE _don't_ + # affect the code generation but _are_ needed by the PatternObject. + compiled_pattern = _regex.compile(pattern, info.flags | version, code, + info.group_index, index_group, named_lists, named_list_indexes, + req_offset, req_chars, req_flags, info.group_count) + + # Do we need to reduce the size of the cache? + if len(_cache) >= _MAXCACHE: + with _cache_lock: + _shrink_cache(_cache, _named_args, _locale_sensitive, _MAXCACHE) + + if cache_it: + if (info.flags & LOCALE) == 0: + pattern_locale = None + + args_needed = frozenset(args_needed) + + # Store this regular expression and named list. + pattern_key = (pattern, type(pattern), flags, args_needed, + DEFAULT_VERSION, pattern_locale) + _cache[pattern_key] = compiled_pattern + + # Store what keyword arguments are needed. + _named_args[args_key] = args_needed + + return compiled_pattern + +def _compile_replacement_helper(pattern, template): + "Compiles a replacement template." + # This function is called by the _regex module. + + # Have we seen this before? + key = pattern.pattern, pattern.flags, template + compiled = _replacement_cache.get(key) + if compiled is not None: + return compiled + + if len(_replacement_cache) >= _MAXREPCACHE: + _replacement_cache.clear() + + is_unicode = isinstance(template, str) + source = _Source(template) + if is_unicode: + def make_string(char_codes): + return "".join(chr(c) for c in char_codes) + else: + def make_string(char_codes): + return bytes(char_codes) + + compiled = [] + literal = [] + while True: + ch = source.get() + if not ch: + break + if ch == "\\": + # '_compile_replacement' will return either an int group reference + # or a string literal. It returns items (plural) in order to handle + # a 2-character literal (an invalid escape sequence). + is_group, items = _compile_replacement(source, pattern, is_unicode) + if is_group: + # It's a group, so first flush the literal. + if literal: + compiled.append(make_string(literal)) + literal = [] + compiled.extend(items) + else: + literal.extend(items) + else: + literal.append(ord(ch)) + + # Flush the literal. + if literal: + compiled.append(make_string(literal)) + + _replacement_cache[key] = compiled + + return compiled + +# We define Pattern here after all the support objects have been defined. +_pat = _compile('', 0, False, {}, False) +Pattern = type(_pat) +Match = type(_pat.match('')) +del _pat + +# Make Pattern public for typing annotations. +__all__.append("Pattern") +__all__.append("Match") + +# We'll define an alias for the 'compile' function so that the repr of a +# pattern object is eval-able. +Regex = compile + +# Register myself for pickling. +import copyreg as _copy_reg + +def _pickle(pattern): + return _regex.compile, pattern._pickled_data + +_copy_reg.pickle(Pattern, _pickle) diff --git a/.pythonlibs/lib/python3.10/site-packages/regex/test_regex.py b/.pythonlibs/lib/python3.10/site-packages/regex/test_regex.py new file mode 100644 index 0000000..9f902c0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/regex/test_regex.py @@ -0,0 +1,4461 @@ +from weakref import proxy +import copy +import pickle +import regex +import string +import sys +import unittest + +# String subclasses for issue 18468. +class StrSubclass(str): + def __getitem__(self, index): + return StrSubclass(super().__getitem__(index)) + +class BytesSubclass(bytes): + def __getitem__(self, index): + return BytesSubclass(super().__getitem__(index)) + +class RegexTests(unittest.TestCase): + PATTERN_CLASS = "" + FLAGS_WITH_COMPILED_PAT = "cannot process flags argument with a compiled pattern" + INVALID_GROUP_REF = "invalid group reference" + MISSING_GT = "missing >" + BAD_GROUP_NAME = "bad character in group name" + MISSING_GROUP_NAME = "missing group name" + MISSING_LT = "missing <" + UNKNOWN_GROUP_I = "unknown group" + UNKNOWN_GROUP = "unknown group" + BAD_ESCAPE = r"bad escape \(end of pattern\)" + BAD_OCTAL_ESCAPE = r"bad escape \\" + BAD_SET = "unterminated character set" + STR_PAT_ON_BYTES = "cannot use a string pattern on a bytes-like object" + BYTES_PAT_ON_STR = "cannot use a bytes pattern on a string-like object" + STR_PAT_BYTES_TEMPL = "expected str instance, bytes found" + BYTES_PAT_STR_TEMPL = "expected a bytes-like object, str found" + BYTES_PAT_UNI_FLAG = "cannot use UNICODE flag with a bytes pattern" + MIXED_FLAGS = "ASCII, LOCALE and UNICODE flags are mutually incompatible" + MISSING_RPAREN = "missing \\)" + TRAILING_CHARS = "unbalanced parenthesis" + BAD_CHAR_RANGE = "bad character range" + NOTHING_TO_REPEAT = "nothing to repeat" + MULTIPLE_REPEAT = "multiple repeat" + OPEN_GROUP = "cannot refer to an open group" + DUPLICATE_GROUP = "duplicate group" + CANT_TURN_OFF = "bad inline flags: cannot turn flags off" + UNDEF_CHAR_NAME = "undefined character name" + + def assertTypedEqual(self, actual, expect, msg=None): + self.assertEqual(actual, expect, msg) + + def recurse(actual, expect): + if isinstance(expect, (tuple, list)): + for x, y in zip(actual, expect): + recurse(x, y) + else: + self.assertIs(type(actual), type(expect), msg) + + recurse(actual, expect) + + def test_weakref(self): + s = 'QabbbcR' + x = regex.compile('ab+c') + y = proxy(x) + if x.findall('QabbbcR') != y.findall('QabbbcR'): + self.fail() + + def test_search_star_plus(self): + self.assertEqual(regex.search('a*', 'xxx').span(0), (0, 0)) + self.assertEqual(regex.search('x*', 'axx').span(), (0, 0)) + self.assertEqual(regex.search('x+', 'axx').span(0), (1, 3)) + self.assertEqual(regex.search('x+', 'axx').span(), (1, 3)) + self.assertEqual(regex.search('x', 'aaa'), None) + self.assertEqual(regex.match('a*', 'xxx').span(0), (0, 0)) + self.assertEqual(regex.match('a*', 'xxx').span(), (0, 0)) + self.assertEqual(regex.match('x*', 'xxxa').span(0), (0, 3)) + self.assertEqual(regex.match('x*', 'xxxa').span(), (0, 3)) + self.assertEqual(regex.match('a+', 'xxx'), None) + + def bump_num(self, matchobj): + int_value = int(matchobj[0]) + return str(int_value + 1) + + def test_basic_regex_sub(self): + self.assertEqual(regex.sub("(?i)b+", "x", "bbbb BBBB"), 'x x') + self.assertEqual(regex.sub(r'\d+', self.bump_num, '08.2 -2 23x99y'), + '9.3 -3 24x100y') + self.assertEqual(regex.sub(r'\d+', self.bump_num, '08.2 -2 23x99y', 3), + '9.3 -3 23x99y') + + self.assertEqual(regex.sub('.', lambda m: r"\n", 'x'), "\\n") + self.assertEqual(regex.sub('.', r"\n", 'x'), "\n") + + self.assertEqual(regex.sub('(?Px)', r'\g\g', 'xx'), 'xxxx') + self.assertEqual(regex.sub('(?Px)', r'\g\g<1>', 'xx'), 'xxxx') + self.assertEqual(regex.sub('(?Px)', r'\g\g', 'xx'), + 'xxxx') + self.assertEqual(regex.sub('(?Px)', r'\g<1>\g<1>', 'xx'), 'xxxx') + + self.assertEqual(regex.sub('a', r'\t\n\v\r\f\a\b', 'a'), "\t\n\v\r\f\a\b") + self.assertEqual(regex.sub('a', '\t\n\v\r\f\a', 'a'), "\t\n\v\r\f\a") + self.assertEqual(regex.sub('a', '\t\n\v\r\f\a', 'a'), chr(9) + chr(10) + + chr(11) + chr(13) + chr(12) + chr(7)) + + self.assertEqual(regex.sub(r'^\s*', 'X', 'test'), 'Xtest') + + self.assertEqual(regex.sub(r"x", r"\x0A", "x"), "\n") + self.assertEqual(regex.sub(r"x", r"\u000A", "x"), "\n") + self.assertEqual(regex.sub(r"x", r"\U0000000A", "x"), "\n") + self.assertEqual(regex.sub(r"x", r"\N{LATIN CAPITAL LETTER A}", + "x"), "A") + + self.assertEqual(regex.sub(br"x", br"\x0A", b"x"), b"\n") + + def test_bug_449964(self): + # Fails for group followed by other escape. + self.assertEqual(regex.sub(r'(?Px)', r'\g<1>\g<1>\b', 'xx'), + "xx\bxx\b") + + def test_bug_449000(self): + # Test for sub() on escaped characters. + self.assertEqual(regex.sub(r'\r\n', r'\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub('\r\n', r'\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub(r'\r\n', '\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + self.assertEqual(regex.sub('\r\n', '\n', 'abc\r\ndef\r\n'), + "abc\ndef\n") + + def test_bug_1661(self): + # Verify that flags do not get silently ignored with compiled patterns + pattern = regex.compile('.') + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.match(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.search(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.findall(pattern, 'A', regex.I)) + self.assertRaisesRegex(ValueError, self.FLAGS_WITH_COMPILED_PAT, + lambda: regex.compile(pattern, regex.I)) + + def test_bug_3629(self): + # A regex that triggered a bug in the sre-code validator + self.assertEqual(repr(type(regex.compile("(?P)(?(quote))"))), + self.PATTERN_CLASS) + + def test_sub_template_numeric_escape(self): + # Bug 776311 and friends. + self.assertEqual(regex.sub('x', r'\0', 'x'), "\0") + self.assertEqual(regex.sub('x', r'\000', 'x'), "\000") + self.assertEqual(regex.sub('x', r'\001', 'x'), "\001") + self.assertEqual(regex.sub('x', r'\008', 'x'), "\0" + "8") + self.assertEqual(regex.sub('x', r'\009', 'x'), "\0" + "9") + self.assertEqual(regex.sub('x', r'\111', 'x'), "\111") + self.assertEqual(regex.sub('x', r'\117', 'x'), "\117") + + self.assertEqual(regex.sub('x', r'\1111', 'x'), "\1111") + self.assertEqual(regex.sub('x', r'\1111', 'x'), "\111" + "1") + + self.assertEqual(regex.sub('x', r'\00', 'x'), '\x00') + self.assertEqual(regex.sub('x', r'\07', 'x'), '\x07') + self.assertEqual(regex.sub('x', r'\08', 'x'), "\0" + "8") + self.assertEqual(regex.sub('x', r'\09', 'x'), "\0" + "9") + self.assertEqual(regex.sub('x', r'\0a', 'x'), "\0" + "a") + + self.assertEqual(regex.sub('x', r'\400', 'x'), "\u0100") + self.assertEqual(regex.sub('x', r'\777', 'x'), "\u01FF") + self.assertEqual(regex.sub(b'x', br'\400', b'x'), b"\x00") + self.assertEqual(regex.sub(b'x', br'\777', b'x'), b"\xFF") + + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\1', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\8', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\9', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\11', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\18', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\1a', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\90', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\99', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\118', 'x')) # r'\11' + '8' + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\11a', 'x')) + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\181', 'x')) # r'\18' + '1' + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.sub('x', r'\800', 'x')) # r'\80' + '0' + + # In Python 2.3 (etc), these loop endlessly in sre_parser.py. + self.assertEqual(regex.sub('(((((((((((x)))))))))))', r'\11', 'x'), + 'x') + self.assertEqual(regex.sub('((((((((((y))))))))))(.)', r'\118', 'xyz'), + 'xz8') + self.assertEqual(regex.sub('((((((((((y))))))))))(.)', r'\11a', 'xyz'), + 'xza') + + def test_qualified_re_sub(self): + self.assertEqual(regex.sub('a', 'b', 'aaaaa'), 'bbbbb') + self.assertEqual(regex.sub('a', 'b', 'aaaaa', 1), 'baaaa') + + def test_bug_114660(self): + self.assertEqual(regex.sub(r'(\S)\s+(\S)', r'\1 \2', 'hello there'), + 'hello there') + + def test_bug_462270(self): + # Test for empty sub() behaviour, see SF bug #462270 + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.sub('(?V0)x*', '-', 'abxd'), '-a-b--d-') + else: + self.assertEqual(regex.sub('(?V0)x*', '-', 'abxd'), '-a-b-d-') + self.assertEqual(regex.sub('(?V1)x*', '-', 'abxd'), '-a-b--d-') + self.assertEqual(regex.sub('x+', '-', 'abxd'), 'ab-d') + + def test_bug_14462(self): + # chr(255) is a valid identifier in Python 3. + group_name = '\xFF' + self.assertEqual(regex.search(r'(?P<' + group_name + '>a)', + 'abc').group(group_name), 'a') + + def test_symbolic_refs(self): + self.assertRaisesRegex(regex.error, self.MISSING_GT, lambda: + regex.sub('(?Px)', r'\gx)', r'\g<', 'xx')) + self.assertRaisesRegex(regex.error, self.MISSING_LT, lambda: + regex.sub('(?Px)', r'\g', 'xx')) + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?Px)', r'\g', 'xx')) + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?Px)', r'\g<1a1>', 'xx')) + self.assertRaisesRegex(IndexError, self.UNKNOWN_GROUP_I, lambda: + regex.sub('(?Px)', r'\g', 'xx')) + + # The new behaviour of unmatched but valid groups is to treat them like + # empty matches in the replacement template, like in Perl. + self.assertEqual(regex.sub('(?Px)|(?Py)', r'\g', 'xx'), '') + self.assertEqual(regex.sub('(?Px)|(?Py)', r'\2', 'xx'), '') + + # The old behaviour was to raise it as an IndexError. + self.assertRaisesRegex(regex.error, self.BAD_GROUP_NAME, lambda: + regex.sub('(?Px)', r'\g<-1>', 'xx')) + + def test_re_subn(self): + self.assertEqual(regex.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) + self.assertEqual(regex.subn("b+", "x", "bbbb BBBB"), ('x BBBB', 1)) + self.assertEqual(regex.subn("b+", "x", "xyz"), ('xyz', 0)) + self.assertEqual(regex.subn("b*", "x", "xyz"), ('xxxyxzx', 4)) + self.assertEqual(regex.subn("b*", "x", "xyz", 2), ('xxxyz', 2)) + + def test_re_split(self): + self.assertEqual(regex.split(":", ":a:b::c"), ['', 'a', 'b', '', 'c']) + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split(":*", ":a:b::c"), ['', '', 'a', '', + 'b', '', 'c', '']) + self.assertEqual(regex.split("(:*)", ":a:b::c"), ['', ':', '', '', + 'a', ':', '', '', 'b', '::', '', '', 'c', '', '']) + self.assertEqual(regex.split("(?::*)", ":a:b::c"), ['', '', 'a', + '', 'b', '', 'c', '']) + self.assertEqual(regex.split("(:)*", ":a:b::c"), ['', ':', '', + None, 'a', ':', '', None, 'b', ':', '', None, 'c', None, '']) + else: + self.assertEqual(regex.split(":*", ":a:b::c"), ['', 'a', 'b', 'c']) + self.assertEqual(regex.split("(:*)", ":a:b::c"), ['', ':', 'a', + ':', 'b', '::', 'c']) + self.assertEqual(regex.split("(?::*)", ":a:b::c"), ['', 'a', 'b', + 'c']) + self.assertEqual(regex.split("(:)*", ":a:b::c"), ['', ':', 'a', + ':', 'b', ':', 'c']) + self.assertEqual(regex.split("([b:]+)", ":a:b::c"), ['', ':', 'a', + ':b::', 'c']) + self.assertEqual(regex.split("(b)|(:+)", ":a:b::c"), ['', None, ':', + 'a', None, ':', '', 'b', None, '', None, '::', 'c']) + self.assertEqual(regex.split("(?:b)|(?::+)", ":a:b::c"), ['', 'a', '', + '', 'c']) + + self.assertEqual(regex.split("x", "xaxbxc"), ['', 'a', 'b', 'c']) + self.assertEqual([m for m in regex.splititer("x", "xaxbxc")], ['', 'a', + 'b', 'c']) + + self.assertEqual(regex.split("(?r)x", "xaxbxc"), ['c', 'b', 'a', '']) + self.assertEqual([m for m in regex.splititer("(?r)x", "xaxbxc")], ['c', + 'b', 'a', '']) + + self.assertEqual(regex.split("(x)|(y)", "xaxbxc"), ['', 'x', None, 'a', + 'x', None, 'b', 'x', None, 'c']) + self.assertEqual([m for m in regex.splititer("(x)|(y)", "xaxbxc")], + ['', 'x', None, 'a', 'x', None, 'b', 'x', None, 'c']) + + self.assertEqual(regex.split("(?r)(x)|(y)", "xaxbxc"), ['c', 'x', None, + 'b', 'x', None, 'a', 'x', None, '']) + self.assertEqual([m for m in regex.splititer("(?r)(x)|(y)", "xaxbxc")], + ['c', 'x', None, 'b', 'x', None, 'a', 'x', None, '']) + + self.assertEqual(regex.split(r"(?V1)\b", "a b c"), ['', 'a', ' ', 'b', + ' ', 'c', '']) + self.assertEqual(regex.split(r"(?V1)\m", "a b c"), ['', 'a ', 'b ', + 'c']) + self.assertEqual(regex.split(r"(?V1)\M", "a b c"), ['a', ' b', ' c', + '']) + + def test_qualified_re_split(self): + self.assertEqual(regex.split(":", ":a:b::c", 2), ['', 'a', 'b::c']) + self.assertEqual(regex.split(':', 'a:b:c:d', 2), ['a', 'b', 'c:d']) + self.assertEqual(regex.split("(:)", ":a:b::c", 2), ['', ':', 'a', ':', + 'b::c']) + + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split("(:*)", ":a:b::c", 2), ['', ':', '', + '', 'a:b::c']) + else: + self.assertEqual(regex.split("(:*)", ":a:b::c", 2), ['', ':', 'a', + ':', 'b::c']) + + def test_re_findall(self): + self.assertEqual(regex.findall(":+", "abc"), []) + self.assertEqual(regex.findall(":+", "a:b::c:::d"), [':', '::', ':::']) + self.assertEqual(regex.findall("(:+)", "a:b::c:::d"), [':', '::', + ':::']) + self.assertEqual(regex.findall("(:)(:*)", "a:b::c:::d"), [(':', ''), + (':', ':'), (':', '::')]) + + self.assertEqual(regex.findall(r"\((?P.{0,5}?TEST)\)", + "(MY TEST)"), ["MY TEST"]) + self.assertEqual(regex.findall(r"\((?P.{0,3}?TEST)\)", + "(MY TEST)"), ["MY TEST"]) + self.assertEqual(regex.findall(r"\((?P.{0,3}?T)\)", "(MY T)"), + ["MY T"]) + + self.assertEqual(regex.findall(r"[^a]{2}[A-Z]", "\n S"), [' S']) + self.assertEqual(regex.findall(r"[^a]{2,3}[A-Z]", "\n S"), ['\n S']) + self.assertEqual(regex.findall(r"[^a]{2,3}[A-Z]", "\n S"), [' S']) + + self.assertEqual(regex.findall(r"X(Y[^Y]+?){1,2}( |Q)+DEF", + "XYABCYPPQ\nQ DEF"), [('YPPQ\n', ' ')]) + + self.assertEqual(regex.findall(r"(\nTest(\n+.+?){0,2}?)?\n+End", + "\nTest\nxyz\nxyz\nEnd"), [('\nTest\nxyz\nxyz', '\nxyz')]) + + def test_bug_117612(self): + self.assertEqual(regex.findall(r"(a|(b))", "aba"), [('a', ''), ('b', + 'b'), ('a', '')]) + + def test_re_match(self): + self.assertEqual(regex.match('a', 'a')[:], ('a',)) + self.assertEqual(regex.match('(a)', 'a')[:], ('a', 'a')) + self.assertEqual(regex.match(r'(a)', 'a')[0], 'a') + self.assertEqual(regex.match(r'(a)', 'a')[1], 'a') + self.assertEqual(regex.match(r'(a)', 'a').group(1, 1), ('a', 'a')) + + pat = regex.compile('((a)|(b))(c)?') + self.assertEqual(pat.match('a')[:], ('a', 'a', 'a', None, None)) + self.assertEqual(pat.match('b')[:], ('b', 'b', None, 'b', None)) + self.assertEqual(pat.match('ac')[:], ('ac', 'a', 'a', None, 'c')) + self.assertEqual(pat.match('bc')[:], ('bc', 'b', None, 'b', 'c')) + self.assertEqual(pat.match('bc')[:], ('bc', 'b', None, 'b', 'c')) + + # A single group. + m = regex.match('(a)', 'a') + self.assertEqual(m.group(), 'a') + self.assertEqual(m.group(0), 'a') + self.assertEqual(m.group(1), 'a') + self.assertEqual(m.group(1, 1), ('a', 'a')) + + pat = regex.compile('(?:(?Pa)|(?Pb))(?Pc)?') + self.assertEqual(pat.match('a').group(1, 2, 3), ('a', None, None)) + self.assertEqual(pat.match('b').group('a1', 'b2', 'c3'), (None, 'b', + None)) + self.assertEqual(pat.match('ac').group(1, 'b2', 3), ('a', None, 'c')) + + def test_re_groupref_exists(self): + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', '(a)')[:], + ('(a)', '(', 'a')) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', 'a')[:], ('a', + None, 'a')) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', 'a)'), None) + self.assertEqual(regex.match(r'^(\()?([^()]+)(?(1)\))$', '(a'), None) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)b|d))$', 'ab')[:], ('ab', + 'a', 'b')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)b|d))$', 'cd')[:], ('cd', + None, 'd')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)|d))$', 'cd')[:], ('cd', + None, 'd')) + self.assertEqual(regex.match('^(?:(a)|c)((?(1)|d))$', 'a')[:], ('a', + 'a', '')) + + # Tests for bug #1177831: exercise groups other than the first group. + p = regex.compile('(?Pa)(?Pb)?((?(g2)c|d))') + self.assertEqual(p.match('abc')[:], ('abc', 'a', 'b', 'c')) + self.assertEqual(p.match('ad')[:], ('ad', 'a', None, 'd')) + self.assertEqual(p.match('abd'), None) + self.assertEqual(p.match('ac'), None) + + def test_re_groupref(self): + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', '|a|')[:], ('|a|', + '|', 'a')) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1?$', 'a')[:], ('a', + None, 'a')) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', 'a|'), None) + self.assertEqual(regex.match(r'^(\|)?([^()]+)\1$', '|a'), None) + self.assertEqual(regex.match(r'^(?:(a)|c)(\1)$', 'aa')[:], ('aa', 'a', + 'a')) + self.assertEqual(regex.match(r'^(?:(a)|c)(\1)?$', 'c')[:], ('c', None, + None)) + + self.assertEqual(regex.findall(r"(?i)(.{1,40}?),(.{1,40}?)(?:;)+(.{1,80}).{1,40}?\3(\ |;)+(.{1,80}?)\1", + "TEST, BEST; LEST ; Lest 123 Test, Best"), [('TEST', ' BEST', + ' LEST', ' ', '123 ')]) + + def test_groupdict(self): + self.assertEqual(regex.match('(?Pfirst) (?Psecond)', + 'first second').groupdict(), {'first': 'first', 'second': 'second'}) + + def test_expand(self): + self.assertEqual(regex.match("(?Pfirst) (?Psecond)", + "first second").expand(r"\2 \1 \g \g"), + 'second first second first') + + def test_repeat_minmax(self): + self.assertEqual(regex.match(r"^(\w){1}$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1}?$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1,2}$", "abc"), None) + self.assertEqual(regex.match(r"^(\w){1,2}?$", "abc"), None) + + self.assertEqual(regex.match(r"^(\w){3}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,3}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,4}$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3,4}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,3}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){1,4}?$", "abc")[1], 'c') + self.assertEqual(regex.match(r"^(\w){3,4}?$", "abc")[1], 'c') + + self.assertEqual(regex.match("^x{1}$", "xxx"), None) + self.assertEqual(regex.match("^x{1}?$", "xxx"), None) + self.assertEqual(regex.match("^x{1,2}$", "xxx"), None) + self.assertEqual(regex.match("^x{1,2}?$", "xxx"), None) + + self.assertEqual(regex.match("^x{1}", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{1}?", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{0,1}", "xxx")[0], 'x') + self.assertEqual(regex.match("^x{0,1}?", "xxx")[0], '') + + self.assertEqual(bool(regex.match("^x{3}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,3}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,4}$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3,4}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,3}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{1,4}?$", "xxx")), True) + self.assertEqual(bool(regex.match("^x{3,4}?$", "xxx")), True) + + self.assertEqual(regex.match("^x{}$", "xxx"), None) + self.assertEqual(bool(regex.match("^x{}$", "x{}")), True) + + def test_getattr(self): + self.assertEqual(regex.compile("(?i)(a)(b)").pattern, '(?i)(a)(b)') + self.assertEqual(regex.compile("(?i)(a)(b)").flags, regex.I | regex.U | + regex.DEFAULT_VERSION) + self.assertEqual(regex.compile(b"(?i)(a)(b)").flags, regex.A | regex.I + | regex.DEFAULT_VERSION) + self.assertEqual(regex.compile("(?i)(a)(b)").groups, 2) + self.assertEqual(regex.compile("(?i)(a)(b)").groupindex, {}) + + self.assertEqual(regex.compile("(?i)(?Pa)(?Pb)").groupindex, + {'first': 1, 'other': 2}) + + self.assertEqual(regex.match("(a)", "a").pos, 0) + self.assertEqual(regex.match("(a)", "a").endpos, 1) + + self.assertEqual(regex.search("b(c)", "abcdef").pos, 0) + self.assertEqual(regex.search("b(c)", "abcdef").endpos, 6) + self.assertEqual(regex.search("b(c)", "abcdef").span(), (1, 3)) + self.assertEqual(regex.search("b(c)", "abcdef").span(1), (2, 3)) + + self.assertEqual(regex.match("(a)", "a").string, 'a') + self.assertEqual(regex.match("(a)", "a").regs, ((0, 1), (0, 1))) + self.assertEqual(repr(type(regex.match("(a)", "a").re)), + self.PATTERN_CLASS) + + # Issue 14260. + p = regex.compile(r'abc(?Pdef)') + p.groupindex["n"] = 0 + self.assertEqual(p.groupindex["n"], 1) + + def test_special_escapes(self): + self.assertEqual(regex.search(r"\b(b.)\b", "abcd abc bcd bx")[1], 'bx') + self.assertEqual(regex.search(r"\B(b.)\B", "abc bcd bc abxd")[1], 'bx') + self.assertEqual(regex.search(br"\b(b.)\b", b"abcd abc bcd bx", + regex.LOCALE)[1], b'bx') + self.assertEqual(regex.search(br"\B(b.)\B", b"abc bcd bc abxd", + regex.LOCALE)[1], b'bx') + self.assertEqual(regex.search(r"\b(b.)\b", "abcd abc bcd bx", + regex.UNICODE)[1], 'bx') + self.assertEqual(regex.search(r"\B(b.)\B", "abc bcd bc abxd", + regex.UNICODE)[1], 'bx') + + self.assertEqual(regex.search(r"^abc$", "\nabc\n", regex.M)[0], 'abc') + self.assertEqual(regex.search(r"^\Aabc\Z$", "abc", regex.M)[0], 'abc') + self.assertEqual(regex.search(r"^\Aabc\Z$", "\nabc\n", regex.M), None) + + self.assertEqual(regex.search(br"\b(b.)\b", b"abcd abc bcd bx")[1], + b'bx') + self.assertEqual(regex.search(br"\B(b.)\B", b"abc bcd bc abxd")[1], + b'bx') + self.assertEqual(regex.search(br"^abc$", b"\nabc\n", regex.M)[0], + b'abc') + self.assertEqual(regex.search(br"^\Aabc\Z$", b"abc", regex.M)[0], + b'abc') + self.assertEqual(regex.search(br"^\Aabc\Z$", b"\nabc\n", regex.M), + None) + + self.assertEqual(regex.search(r"\d\D\w\W\s\S", "1aa! a")[0], '1aa! a') + self.assertEqual(regex.search(br"\d\D\w\W\s\S", b"1aa! a", + regex.LOCALE)[0], b'1aa! a') + self.assertEqual(regex.search(r"\d\D\w\W\s\S", "1aa! a", + regex.UNICODE)[0], '1aa! a') + + def test_bigcharset(self): + self.assertEqual(regex.match(r"([\u2222\u2223])", "\u2222")[1], + '\u2222') + self.assertEqual(regex.match(r"([\u2222\u2223])", "\u2222", + regex.UNICODE)[1], '\u2222') + self.assertEqual("".join(regex.findall(".", + "e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + 'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + self.assertEqual("".join(regex.findall(r"[e\xe8\xe9\xea\xeb\u0113\u011b\u0117]", + "e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + 'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + self.assertEqual("".join(regex.findall(r"e|\xe8|\xe9|\xea|\xeb|\u0113|\u011b|\u0117", + "e\xe8\xe9\xea\xeb\u0113\u011b\u0117", flags=regex.UNICODE)), + 'e\xe8\xe9\xea\xeb\u0113\u011b\u0117') + + def test_anyall(self): + self.assertEqual(regex.match("a.b", "a\nb", regex.DOTALL)[0], "a\nb") + self.assertEqual(regex.match("a.*b", "a\n\nb", regex.DOTALL)[0], + "a\n\nb") + + def test_non_consuming(self): + self.assertEqual(regex.match(r"(a(?=\s[^a]))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[^a]*))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[abc]))", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a(?=\s[abc]*))", "a bc")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s\1)", "a a")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s\1*)", "a aa")[1], 'a') + self.assertEqual(regex.match(r"(a)(?=\s(abc|a))", "a a")[1], 'a') + + self.assertEqual(regex.match(r"(a(?!\s[^a]))", "a a")[1], 'a') + self.assertEqual(regex.match(r"(a(?!\s[abc]))", "a d")[1], 'a') + self.assertEqual(regex.match(r"(a)(?!\s\1)", "a b")[1], 'a') + self.assertEqual(regex.match(r"(a)(?!\s(abc|a))", "a b")[1], 'a') + + def test_ignore_case(self): + self.assertEqual(regex.match("abc", "ABC", regex.I)[0], 'ABC') + self.assertEqual(regex.match(b"abc", b"ABC", regex.I)[0], b'ABC') + + self.assertEqual(regex.match(r"(a\s[^a]*)", "a bb", regex.I)[1], + 'a bb') + self.assertEqual(regex.match(r"(a\s[abc])", "a b", regex.I)[1], 'a b') + self.assertEqual(regex.match(r"(a\s[abc]*)", "a bb", regex.I)[1], + 'a bb') + self.assertEqual(regex.match(r"((a)\s\2)", "a a", regex.I)[1], 'a a') + self.assertEqual(regex.match(r"((a)\s\2*)", "a aa", regex.I)[1], + 'a aa') + self.assertEqual(regex.match(r"((a)\s(abc|a))", "a a", regex.I)[1], + 'a a') + self.assertEqual(regex.match(r"((a)\s(abc|a)*)", "a aa", regex.I)[1], + 'a aa') + + # Issue 3511. + self.assertEqual(regex.match(r"[Z-a]", "_").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[Z-a]", "_").span(), (0, 1)) + + self.assertEqual(bool(regex.match(r"(?i)nao", "nAo")), True) + self.assertEqual(bool(regex.match(r"(?i)n\xE3o", "n\xC3o")), True) + self.assertEqual(bool(regex.match(r"(?i)n\xE3o", "N\xC3O")), True) + self.assertEqual(bool(regex.match(r"(?i)s", "\u017F")), True) + + def test_case_folding(self): + self.assertEqual(regex.search(r"(?fi)ss", "SS").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)SS", "ss").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)SS", + "\N{LATIN SMALL LETTER SHARP S}").span(), (0, 1)) + self.assertEqual(regex.search(r"(?fi)\N{LATIN SMALL LETTER SHARP S}", + "SS").span(), (0, 2)) + + self.assertEqual(regex.search(r"(?fi)\N{LATIN SMALL LIGATURE ST}", + "ST").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)ST", + "\N{LATIN SMALL LIGATURE ST}").span(), (0, 1)) + self.assertEqual(regex.search(r"(?fi)ST", + "\N{LATIN SMALL LIGATURE LONG S T}").span(), (0, 1)) + + self.assertEqual(regex.search(r"(?fi)SST", + "\N{LATIN SMALL LETTER SHARP S}t").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)SST", + "s\N{LATIN SMALL LIGATURE LONG S T}").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)SST", + "s\N{LATIN SMALL LIGATURE ST}").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)\N{LATIN SMALL LIGATURE ST}", + "SST").span(), (1, 3)) + self.assertEqual(regex.search(r"(?fi)SST", + "s\N{LATIN SMALL LIGATURE ST}").span(), (0, 2)) + + self.assertEqual(regex.search(r"(?fi)FFI", + "\N{LATIN SMALL LIGATURE FFI}").span(), (0, 1)) + self.assertEqual(regex.search(r"(?fi)FFI", + "\N{LATIN SMALL LIGATURE FF}i").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)FFI", + "f\N{LATIN SMALL LIGATURE FI}").span(), (0, 2)) + self.assertEqual(regex.search(r"(?fi)\N{LATIN SMALL LIGATURE FFI}", + "FFI").span(), (0, 3)) + self.assertEqual(regex.search(r"(?fi)\N{LATIN SMALL LIGATURE FF}i", + "FFI").span(), (0, 3)) + self.assertEqual(regex.search(r"(?fi)f\N{LATIN SMALL LIGATURE FI}", + "FFI").span(), (0, 3)) + + sigma = "\u03A3\u03C3\u03C2" + for ch1 in sigma: + for ch2 in sigma: + if not regex.match(r"(?fi)" + ch1, ch2): + self.fail() + + self.assertEqual(bool(regex.search(r"(?iV1)ff", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)ff", "\uFB01\uFB00")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)fi", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)fi", "\uFB01\uFB00")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)fffi", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)f\uFB03", + "\uFB00\uFB01")), True) + self.assertEqual(bool(regex.search(r"(?iV1)ff", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)fi", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)fffi", "\uFB00\uFB01")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)f\uFB03", + "\uFB00\uFB01")), True) + self.assertEqual(bool(regex.search(r"(?iV1)f\uFB01", "\uFB00i")), + True) + self.assertEqual(bool(regex.search(r"(?iV1)f\uFB01", "\uFB00i")), + True) + + self.assertEqual(regex.findall(r"(?iV0)\m(?:word){e<=3}\M(?ne", "affine", + options=["\N{LATIN SMALL LIGATURE FFI}"]).span(), (0, 6)) + self.assertEqual(regex.search(r"(?fi)a\Lne", + "a\N{LATIN SMALL LIGATURE FFI}ne", options=["ffi"]).span(), (0, 4)) + + def test_category(self): + self.assertEqual(regex.match(r"(\s)", " ")[1], ' ') + + def test_not_literal(self): + self.assertEqual(regex.search(r"\s([^a])", " b")[1], 'b') + self.assertEqual(regex.search(r"\s([^a]*)", " bb")[1], 'bb') + + def test_search_coverage(self): + self.assertEqual(regex.search(r"\s(b)", " b")[1], 'b') + self.assertEqual(regex.search(r"a\s", "a ")[0], 'a ') + + def test_re_escape(self): + p = "" + self.assertEqual(regex.escape(p), p) + for i in range(0, 256): + p += chr(i) + self.assertEqual(bool(regex.match(regex.escape(chr(i)), chr(i))), + True) + self.assertEqual(regex.match(regex.escape(chr(i)), chr(i)).span(), + (0, 1)) + + pat = regex.compile(regex.escape(p)) + self.assertEqual(pat.match(p).span(), (0, 256)) + + def test_re_escape_byte(self): + p = b"" + self.assertEqual(regex.escape(p), p) + for i in range(0, 256): + b = bytes([i]) + p += b + self.assertEqual(bool(regex.match(regex.escape(b), b)), True) + self.assertEqual(regex.match(regex.escape(b), b).span(), (0, 1)) + + pat = regex.compile(regex.escape(p)) + self.assertEqual(pat.match(p).span(), (0, 256)) + + def test_constants(self): + if regex.I != regex.IGNORECASE: + self.fail() + if regex.L != regex.LOCALE: + self.fail() + if regex.M != regex.MULTILINE: + self.fail() + if regex.S != regex.DOTALL: + self.fail() + if regex.X != regex.VERBOSE: + self.fail() + + def test_flags(self): + for flag in [regex.I, regex.M, regex.X, regex.S, regex.L]: + self.assertEqual(repr(type(regex.compile('^pattern$', flag))), + self.PATTERN_CLASS) + + def test_sre_character_literals(self): + for i in [0, 8, 16, 32, 64, 127, 128, 255]: + self.assertEqual(bool(regex.match(r"\%03o" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"\%03o0" % i, chr(i) + "0")), + True) + self.assertEqual(bool(regex.match(r"\%03o8" % i, chr(i) + "8")), + True) + self.assertEqual(bool(regex.match(r"\x%02x" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"\x%02x0" % i, chr(i) + "0")), + True) + self.assertEqual(bool(regex.match(r"\x%02xz" % i, chr(i) + "z")), + True) + + self.assertRaisesRegex(regex.error, self.INVALID_GROUP_REF, lambda: + regex.match(r"\911", "")) + + def test_sre_character_class_literals(self): + for i in [0, 8, 16, 32, 64, 127, 128, 255]: + self.assertEqual(bool(regex.match(r"[\%03o]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\%03o0]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\%03o8]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02x]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02x0]" % i, chr(i))), True) + self.assertEqual(bool(regex.match(r"[\x%02xz]" % i, chr(i))), True) + + self.assertRaisesRegex(regex.error, self.BAD_OCTAL_ESCAPE, lambda: + regex.match(r"[\911]", "")) + + def test_bug_113254(self): + self.assertEqual(regex.match(r'(a)|(b)', 'b').start(1), -1) + self.assertEqual(regex.match(r'(a)|(b)', 'b').end(1), -1) + self.assertEqual(regex.match(r'(a)|(b)', 'b').span(1), (-1, -1)) + + def test_bug_527371(self): + # Bug described in patches 527371/672491. + self.assertEqual(regex.match(r'(a)?a','a').lastindex, None) + self.assertEqual(regex.match(r'(a)(b)?b','ab').lastindex, 1) + self.assertEqual(regex.match(r'(?Pa)(?Pb)?b','ab').lastgroup, + 'a') + self.assertEqual(regex.match("(?Pa(b))", "ab").lastgroup, 'a') + self.assertEqual(regex.match("((a))", "a").lastindex, 1) + + def test_bug_545855(self): + # Bug 545855 -- This pattern failed to cause a compile error as it + # should, instead provoking a TypeError. + self.assertRaisesRegex(regex.error, self.BAD_SET, lambda: + regex.compile('foo[a-')) + + def test_bug_418626(self): + # Bugs 418626 at al. -- Testing Greg Chapman's addition of op code + # SRE_OP_MIN_REPEAT_ONE for eliminating recursion on simple uses of + # pattern '*?' on a long string. + self.assertEqual(regex.match('.*?c', 10000 * 'ab' + 'cd').end(0), + 20001) + self.assertEqual(regex.match('.*?cd', 5000 * 'ab' + 'c' + 5000 * 'ab' + + 'cde').end(0), 20003) + self.assertEqual(regex.match('.*?cd', 20000 * 'abc' + 'de').end(0), + 60001) + # Non-simple '*?' still used to hit the recursion limit, before the + # non-recursive scheme was implemented. + self.assertEqual(regex.search('(a|b)*?c', 10000 * 'ab' + 'cd').end(0), + 20001) + + def test_bug_612074(self): + pat = "[" + regex.escape("\u2039") + "]" + self.assertEqual(regex.compile(pat) and 1, 1) + + def test_stack_overflow(self): + # Nasty cases that used to overflow the straightforward recursive + # implementation of repeated groups. + self.assertEqual(regex.match('(x)*', 50000 * 'x')[1], 'x') + self.assertEqual(regex.match('(x)*y', 50000 * 'x' + 'y')[1], 'x') + self.assertEqual(regex.match('(x)*?y', 50000 * 'x' + 'y')[1], 'x') + + def test_scanner(self): + def s_ident(scanner, token): return token + def s_operator(scanner, token): return "op%s" % token + def s_float(scanner, token): return float(token) + def s_int(scanner, token): return int(token) + + scanner = regex.Scanner([(r"[a-zA-Z_]\w*", s_ident), (r"\d+\.\d*", + s_float), (r"\d+", s_int), (r"=|\+|-|\*|/", s_operator), (r"\s+", + None), ]) + + self.assertEqual(repr(type(scanner.scanner.scanner("").pattern)), + self.PATTERN_CLASS) + + self.assertEqual(scanner.scan("sum = 3*foo + 312.50 + bar"), (['sum', + 'op=', 3, 'op*', 'foo', 'op+', 312.5, 'op+', 'bar'], '')) + + def test_bug_448951(self): + # Bug 448951 (similar to 429357, but with single char match). + # (Also test greedy matches.) + for op in '', '?', '*': + self.assertEqual(regex.match(r'((.%s):)?z' % op, 'z')[:], ('z', + None, None)) + self.assertEqual(regex.match(r'((.%s):)?z' % op, 'a:z')[:], ('a:z', + 'a:', 'a')) + + def test_bug_725106(self): + # Capturing groups in alternatives in repeats. + self.assertEqual(regex.match('^((a)|b)*', 'abc')[:], ('ab', 'b', 'a')) + self.assertEqual(regex.match('^(([ab])|c)*', 'abc')[:], ('abc', 'c', + 'b')) + self.assertEqual(regex.match('^((d)|[ab])*', 'abc')[:], ('ab', 'b', + None)) + self.assertEqual(regex.match('^((a)c|[ab])*', 'abc')[:], ('ab', 'b', + None)) + self.assertEqual(regex.match('^((a)|b)*?c', 'abc')[:], ('abc', 'b', + 'a')) + self.assertEqual(regex.match('^(([ab])|c)*?d', 'abcd')[:], ('abcd', + 'c', 'b')) + self.assertEqual(regex.match('^((d)|[ab])*?c', 'abc')[:], ('abc', 'b', + None)) + self.assertEqual(regex.match('^((a)c|[ab])*?c', 'abc')[:], ('abc', 'b', + None)) + + def test_bug_725149(self): + # Mark_stack_base restoring before restoring marks. + self.assertEqual(regex.match('(a)(?:(?=(b)*)c)*', 'abb')[:], ('a', 'a', + None)) + self.assertEqual(regex.match('(a)((?!(b)*))*', 'abb')[:], ('a', 'a', + None, None)) + + def test_bug_764548(self): + # Bug 764548, regex.compile() barfs on str/unicode subclasses. + class my_unicode(str): pass + pat = regex.compile(my_unicode("abc")) + self.assertEqual(pat.match("xyz"), None) + + def test_finditer(self): + it = regex.finditer(r":+", "a:b::c:::d") + self.assertEqual([item[0] for item in it], [':', '::', ':::']) + + def test_bug_926075(self): + if regex.compile('bug_926075') is regex.compile(b'bug_926075'): + self.fail() + + def test_bug_931848(self): + pattern = "[\u002E\u3002\uFF0E\uFF61]" + self.assertEqual(regex.compile(pattern).split("a.b.c"), ['a', 'b', + 'c']) + + def test_bug_581080(self): + it = regex.finditer(r"\s", "a b") + self.assertEqual(next(it).span(), (1, 2)) + self.assertRaises(StopIteration, lambda: next(it)) + + scanner = regex.compile(r"\s").scanner("a b") + self.assertEqual(scanner.search().span(), (1, 2)) + self.assertEqual(scanner.search(), None) + + def test_bug_817234(self): + it = regex.finditer(r".*", "asdf") + self.assertEqual(next(it).span(), (0, 4)) + self.assertEqual(next(it).span(), (4, 4)) + self.assertRaises(StopIteration, lambda: next(it)) + + def test_empty_array(self): + # SF buf 1647541. + import array + for typecode in 'bBuhHiIlLfd': + a = array.array(typecode) + self.assertEqual(regex.compile(b"bla").match(a), None) + self.assertEqual(regex.compile(b"").match(a)[1 : ], ()) + + def test_inline_flags(self): + # Bug #1700. + upper_char = chr(0x1ea0) # Latin Capital Letter A with Dot Below + lower_char = chr(0x1ea1) # Latin Small Letter A with Dot Below + + p = regex.compile(upper_char, regex.I | regex.U) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile(lower_char, regex.I | regex.U) + self.assertEqual(bool(p.match(upper_char)), True) + + p = regex.compile('(?i)' + upper_char, regex.U) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile('(?i)' + lower_char, regex.U) + self.assertEqual(bool(p.match(upper_char)), True) + + p = regex.compile('(?iu)' + upper_char) + self.assertEqual(bool(p.match(lower_char)), True) + + p = regex.compile('(?iu)' + lower_char) + self.assertEqual(bool(p.match(upper_char)), True) + + # Changed to positional flags in regex 2023.12.23. + self.assertEqual(bool(regex.match(r"(?i)a", "A")), True) + self.assertEqual(regex.match(r"a(?i)", "A"), None) + + def test_dollar_matches_twice(self): + # $ matches the end of string, and just before the terminating \n. + pattern = regex.compile('$') + self.assertEqual(pattern.sub('#', 'a\nb\n'), 'a\nb#\n#') + self.assertEqual(pattern.sub('#', 'a\nb\nc'), 'a\nb\nc#') + self.assertEqual(pattern.sub('#', '\n'), '#\n#') + + pattern = regex.compile('$', regex.MULTILINE) + self.assertEqual(pattern.sub('#', 'a\nb\n' ), 'a#\nb#\n#') + self.assertEqual(pattern.sub('#', 'a\nb\nc'), 'a#\nb#\nc#') + self.assertEqual(pattern.sub('#', '\n'), '#\n#') + + def test_bytes_str_mixing(self): + # Mixing str and bytes is disallowed. + pat = regex.compile('.') + bpat = regex.compile(b'.') + self.assertRaisesRegex(TypeError, self.STR_PAT_ON_BYTES, lambda: + pat.match(b'b')) + self.assertRaisesRegex(TypeError, self.BYTES_PAT_ON_STR, lambda: + bpat.match('b')) + self.assertRaisesRegex(TypeError, self.STR_PAT_BYTES_TEMPL, lambda: + pat.sub(b'b', 'c')) + self.assertRaisesRegex(TypeError, self.STR_PAT_ON_BYTES, lambda: + pat.sub('b', b'c')) + self.assertRaisesRegex(TypeError, self.STR_PAT_ON_BYTES, lambda: + pat.sub(b'b', b'c')) + self.assertRaisesRegex(TypeError, self.BYTES_PAT_ON_STR, lambda: + bpat.sub(b'b', 'c')) + self.assertRaisesRegex(TypeError, self.BYTES_PAT_STR_TEMPL, lambda: + bpat.sub('b', b'c')) + self.assertRaisesRegex(TypeError, self.BYTES_PAT_ON_STR, lambda: + bpat.sub('b', 'c')) + + self.assertRaisesRegex(ValueError, self.BYTES_PAT_UNI_FLAG, lambda: + regex.compile(br'\w', regex.UNICODE)) + self.assertRaisesRegex(ValueError, self.BYTES_PAT_UNI_FLAG, lambda: + regex.compile(br'(?u)\w')) + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile(r'\w', regex.UNICODE | regex.ASCII)) + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile(r'(?u)\w', regex.ASCII)) + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile(r'(?a)\w', regex.UNICODE)) + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile(r'(?au)\w')) + + def test_ascii_and_unicode_flag(self): + # String patterns. + for flags in (0, regex.UNICODE): + pat = regex.compile('\xc0', flags | regex.IGNORECASE) + self.assertEqual(bool(pat.match('\xe0')), True) + pat = regex.compile(r'\w', flags) + self.assertEqual(bool(pat.match('\xe0')), True) + + pat = regex.compile('\xc0', regex.ASCII | regex.IGNORECASE) + self.assertEqual(pat.match('\xe0'), None) + pat = regex.compile('(?a)\xc0', regex.IGNORECASE) + self.assertEqual(pat.match('\xe0'), None) + pat = regex.compile(r'\w', regex.ASCII) + self.assertEqual(pat.match('\xe0'), None) + pat = regex.compile(r'(?a)\w') + self.assertEqual(pat.match('\xe0'), None) + + # Bytes patterns. + for flags in (0, regex.ASCII): + pat = regex.compile(b'\xc0', flags | regex.IGNORECASE) + self.assertEqual(pat.match(b'\xe0'), None) + pat = regex.compile(br'\w') + self.assertEqual(pat.match(b'\xe0'), None) + + self.assertRaisesRegex(ValueError, self.MIXED_FLAGS, lambda: + regex.compile(r'(?au)\w')) + + def test_subscripting_match(self): + m = regex.match(r'(?\w)', 'xy') + if not m: + self.fail("Failed: expected match but returned None") + elif not m or m[0] != m.group(0) or m[1] != m.group(1): + self.fail("Failed") + if not m: + self.fail("Failed: expected match but returned None") + elif m[:] != ('x', 'x'): + self.fail("Failed: expected \"('x', 'x')\" but got {} instead".format(ascii(m[:]))) + + def test_new_named_groups(self): + m0 = regex.match(r'(?P\w)', 'x') + m1 = regex.match(r'(?\w)', 'x') + if not (m0 and m1 and m0[:] == m1[:]): + self.fail("Failed") + + def test_properties(self): + self.assertEqual(regex.match(b'(?ai)\xC0', b'\xE0'), None) + self.assertEqual(regex.match(br'(?ai)\xC0', b'\xE0'), None) + self.assertEqual(regex.match(br'(?a)\w', b'\xE0'), None) + self.assertEqual(bool(regex.match(r'\w', '\xE0')), True) + + # Dropped the following test. It's not possible to determine what the + # correct result should be in the general case. +# self.assertEqual(bool(regex.match(br'(?L)\w', b'\xE0')), +# b'\xE0'.isalnum()) + + self.assertEqual(bool(regex.match(br'(?L)\d', b'0')), True) + self.assertEqual(bool(regex.match(br'(?L)\s', b' ')), True) + self.assertEqual(bool(regex.match(br'(?L)\w', b'a')), True) + self.assertEqual(regex.match(br'(?L)\d', b'?'), None) + self.assertEqual(regex.match(br'(?L)\s', b'?'), None) + self.assertEqual(regex.match(br'(?L)\w', b'?'), None) + + self.assertEqual(regex.match(br'(?L)\D', b'0'), None) + self.assertEqual(regex.match(br'(?L)\S', b' '), None) + self.assertEqual(regex.match(br'(?L)\W', b'a'), None) + self.assertEqual(bool(regex.match(br'(?L)\D', b'?')), True) + self.assertEqual(bool(regex.match(br'(?L)\S', b'?')), True) + self.assertEqual(bool(regex.match(br'(?L)\W', b'?')), True) + + self.assertEqual(bool(regex.match(r'\p{Cyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'(?i)\p{Cyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{IsCyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{Script=Cyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{InCyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{Block=Cyrillic}', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:Cyrillic:]]', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:IsCyrillic:]]', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:Script=Cyrillic:]]', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:InCyrillic:]]', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:Block=Cyrillic:]]', + '\N{CYRILLIC CAPITAL LETTER A}')), True) + + self.assertEqual(bool(regex.match(r'\P{Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\P{IsCyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\P{Script=Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\P{InCyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\P{Block=Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{^Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{^IsCyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{^Script=Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{^InCyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'\p{^Block=Cyrillic}', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:^Cyrillic:]]', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:^IsCyrillic:]]', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:^Script=Cyrillic:]]', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:^InCyrillic:]]', + '\N{LATIN CAPITAL LETTER A}')), True) + self.assertEqual(bool(regex.match(r'[[:^Block=Cyrillic:]]', + '\N{LATIN CAPITAL LETTER A}')), True) + + self.assertEqual(bool(regex.match(r'\d', '0')), True) + self.assertEqual(bool(regex.match(r'\s', ' ')), True) + self.assertEqual(bool(regex.match(r'\w', 'A')), True) + self.assertEqual(regex.match(r"\d", "?"), None) + self.assertEqual(regex.match(r"\s", "?"), None) + self.assertEqual(regex.match(r"\w", "?"), None) + self.assertEqual(regex.match(r"\D", "0"), None) + self.assertEqual(regex.match(r"\S", " "), None) + self.assertEqual(regex.match(r"\W", "A"), None) + self.assertEqual(bool(regex.match(r'\D', '?')), True) + self.assertEqual(bool(regex.match(r'\S', '?')), True) + self.assertEqual(bool(regex.match(r'\W', '?')), True) + + self.assertEqual(bool(regex.match(r'\p{L}', 'A')), True) + self.assertEqual(bool(regex.match(r'\p{L}', 'a')), True) + self.assertEqual(bool(regex.match(r'\p{Lu}', 'A')), True) + self.assertEqual(bool(regex.match(r'\p{Ll}', 'a')), True) + + self.assertEqual(bool(regex.match(r'(?i)a', 'a')), True) + self.assertEqual(bool(regex.match(r'(?i)a', 'A')), True) + + self.assertEqual(bool(regex.match(r'\w', '0')), True) + self.assertEqual(bool(regex.match(r'\w', 'a')), True) + self.assertEqual(bool(regex.match(r'\w', '_')), True) + + self.assertEqual(regex.match(r"\X", "\xE0").span(), (0, 1)) + self.assertEqual(regex.match(r"\X", "a\u0300").span(), (0, 2)) + self.assertEqual(regex.findall(r"\X", + "a\xE0a\u0300e\xE9e\u0301"), ['a', '\xe0', 'a\u0300', 'e', + '\xe9', 'e\u0301']) + self.assertEqual(regex.findall(r"\X{3}", + "a\xE0a\u0300e\xE9e\u0301"), ['a\xe0a\u0300', 'e\xe9e\u0301']) + self.assertEqual(regex.findall(r"\X", "\r\r\n\u0301A\u0301"), + ['\r', '\r\n', '\u0301', 'A\u0301']) + + self.assertEqual(bool(regex.match(r'\p{Ll}', 'a')), True) + + chars_u = "-09AZaz_\u0393\u03b3" + chars_b = b"-09AZaz_" + word_set = set("Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc".split()) + + tests = [ + (r"\w", chars_u, "09AZaz_\u0393\u03b3"), + (r"[[:word:]]", chars_u, "09AZaz_\u0393\u03b3"), + (r"\W", chars_u, "-"), + (r"[[:^word:]]", chars_u, "-"), + (r"\d", chars_u, "09"), + (r"[[:digit:]]", chars_u, "09"), + (r"\D", chars_u, "-AZaz_\u0393\u03b3"), + (r"[[:^digit:]]", chars_u, "-AZaz_\u0393\u03b3"), + (r"[[:alpha:]]", chars_u, "AZaz\u0393\u03b3"), + (r"[[:^alpha:]]", chars_u, "-09_"), + (r"[[:alnum:]]", chars_u, "09AZaz\u0393\u03b3"), + (r"[[:^alnum:]]", chars_u, "-_"), + (r"[[:xdigit:]]", chars_u, "09Aa"), + (r"[[:^xdigit:]]", chars_u, "-Zz_\u0393\u03b3"), + (r"\p{InBasicLatin}", "a\xE1", "a"), + (r"\P{InBasicLatin}", "a\xE1", "\xE1"), + (r"(?i)\p{InBasicLatin}", "a\xE1", "a"), + (r"(?i)\P{InBasicLatin}", "a\xE1", "\xE1"), + + (br"(?L)\w", chars_b, b"09AZaz_"), + (br"(?L)[[:word:]]", chars_b, b"09AZaz_"), + (br"(?L)\W", chars_b, b"-"), + (br"(?L)[[:^word:]]", chars_b, b"-"), + (br"(?L)\d", chars_b, b"09"), + (br"(?L)[[:digit:]]", chars_b, b"09"), + (br"(?L)\D", chars_b, b"-AZaz_"), + (br"(?L)[[:^digit:]]", chars_b, b"-AZaz_"), + (br"(?L)[[:alpha:]]", chars_b, b"AZaz"), + (br"(?L)[[:^alpha:]]", chars_b, b"-09_"), + (br"(?L)[[:alnum:]]", chars_b, b"09AZaz"), + (br"(?L)[[:^alnum:]]", chars_b, b"-_"), + (br"(?L)[[:xdigit:]]", chars_b, b"09Aa"), + (br"(?L)[[:^xdigit:]]", chars_b, b"-Zz_"), + + (br"(?a)\w", chars_b, b"09AZaz_"), + (br"(?a)[[:word:]]", chars_b, b"09AZaz_"), + (br"(?a)\W", chars_b, b"-"), + (br"(?a)[[:^word:]]", chars_b, b"-"), + (br"(?a)\d", chars_b, b"09"), + (br"(?a)[[:digit:]]", chars_b, b"09"), + (br"(?a)\D", chars_b, b"-AZaz_"), + (br"(?a)[[:^digit:]]", chars_b, b"-AZaz_"), + (br"(?a)[[:alpha:]]", chars_b, b"AZaz"), + (br"(?a)[[:^alpha:]]", chars_b, b"-09_"), + (br"(?a)[[:alnum:]]", chars_b, b"09AZaz"), + (br"(?a)[[:^alnum:]]", chars_b, b"-_"), + (br"(?a)[[:xdigit:]]", chars_b, b"09Aa"), + (br"(?a)[[:^xdigit:]]", chars_b, b"-Zz_"), + ] + for pattern, chars, expected in tests: + try: + if chars[ : 0].join(regex.findall(pattern, chars)) != expected: + self.fail("Failed: {}".format(pattern)) + except Exception as e: + self.fail("Failed: {} raised {}".format(pattern, ascii(e))) + + self.assertEqual(bool(regex.match(r"\p{NumericValue=0}", "0")), + True) + self.assertEqual(bool(regex.match(r"\p{NumericValue=1/2}", + "\N{VULGAR FRACTION ONE HALF}")), True) + self.assertEqual(bool(regex.match(r"\p{NumericValue=0.5}", + "\N{VULGAR FRACTION ONE HALF}")), True) + + def test_word_class(self): + self.assertEqual(regex.findall(r"\w+", + " \u0939\u093f\u0928\u094d\u0926\u0940,"), + ['\u0939\u093f\u0928\u094d\u0926\u0940']) + self.assertEqual(regex.findall(r"\W+", + " \u0939\u093f\u0928\u094d\u0926\u0940,"), [' ', ',']) + self.assertEqual(regex.split(r"(?V1)\b", + " \u0939\u093f\u0928\u094d\u0926\u0940,"), [' ', + '\u0939\u093f\u0928\u094d\u0926\u0940', ',']) + self.assertEqual(regex.split(r"(?V1)\B", + " \u0939\u093f\u0928\u094d\u0926\u0940,"), ['', ' \u0939', + '\u093f', '\u0928', '\u094d', '\u0926', '\u0940,', '']) + + def test_search_anchor(self): + self.assertEqual(regex.findall(r"\G\w{2}", "abcd ef"), ['ab', 'cd']) + + def test_search_reverse(self): + self.assertEqual(regex.findall(r"(?r).", "abc"), ['c', 'b', 'a']) + self.assertEqual(regex.findall(r"(?r).", "abc", overlapped=True), ['c', + 'b', 'a']) + self.assertEqual(regex.findall(r"(?r)..", "abcde"), ['de', 'bc']) + self.assertEqual(regex.findall(r"(?r)..", "abcde", overlapped=True), + ['de', 'cd', 'bc', 'ab']) + self.assertEqual(regex.findall(r"(?r)(.)(-)(.)", "a-b-c", + overlapped=True), [("b", "-", "c"), ("a", "-", "b")]) + + self.assertEqual([m[0] for m in regex.finditer(r"(?r).", "abc")], ['c', + 'b', 'a']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r).", "abc")], ['c', + 'b', 'a']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + + self.assertEqual(regex.findall(r"^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual(regex.findall(r"(?V1)^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual(regex.findall(r"(?r)^|\w+", "foo bar"), ['bar', 'foo', + '']) + self.assertEqual(regex.findall(r"(?rV1)^|\w+", "foo bar"), ['bar', + 'foo', '']) + + self.assertEqual([m[0] for m in regex.finditer(r"^|\w+", "foo bar")], + ['', 'foo', 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?V1)^|\w+", + "foo bar")], ['', 'foo', 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)^|\w+", + "foo bar")], ['bar', 'foo', '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?rV1)^|\w+", + "foo bar")], ['bar', 'foo', '']) + + self.assertEqual(regex.findall(r"\G\w{2}", "abcd ef"), ['ab', 'cd']) + self.assertEqual(regex.findall(r".{2}(?<=\G.*)", "abcd"), ['ab', 'cd']) + self.assertEqual(regex.findall(r"(?r)\G\w{2}", "abcd ef"), []) + self.assertEqual(regex.findall(r"(?r)\w{2}\G", "abcd ef"), ['ef']) + + self.assertEqual(regex.findall(r"q*", "qqwe"), ['qq', '', '', '']) + self.assertEqual(regex.findall(r"(?V1)q*", "qqwe"), ['qq', '', '', '']) + self.assertEqual(regex.findall(r"(?r)q*", "qqwe"), ['', '', 'qq', '']) + self.assertEqual(regex.findall(r"(?rV1)q*", "qqwe"), ['', '', 'qq', + '']) + + self.assertEqual(regex.findall(".", "abcd", pos=1, endpos=3), ['b', + 'c']) + self.assertEqual(regex.findall(".", "abcd", pos=1, endpos=-1), ['b', + 'c']) + self.assertEqual([m[0] for m in regex.finditer(".", "abcd", pos=1, + endpos=3)], ['b', 'c']) + self.assertEqual([m[0] for m in regex.finditer(".", "abcd", pos=1, + endpos=-1)], ['b', 'c']) + + self.assertEqual([m[0] for m in regex.finditer("(?r).", "abcd", pos=1, + endpos=3)], ['c', 'b']) + self.assertEqual([m[0] for m in regex.finditer("(?r).", "abcd", pos=1, + endpos=-1)], ['c', 'b']) + self.assertEqual(regex.findall("(?r).", "abcd", pos=1, endpos=3), ['c', + 'b']) + self.assertEqual(regex.findall("(?r).", "abcd", pos=1, endpos=-1), + ['c', 'b']) + + self.assertEqual(regex.findall(r"[ab]", "aB", regex.I), ['a', 'B']) + self.assertEqual(regex.findall(r"(?r)[ab]", "aB", regex.I), ['B', 'a']) + + self.assertEqual(regex.findall(r"(?r).{2}", "abc"), ['bc']) + self.assertEqual(regex.findall(r"(?r).{2}", "abc", overlapped=True), + ['bc', 'ab']) + self.assertEqual(regex.findall(r"(\w+) (\w+)", + "first second third fourth fifth"), [('first', 'second'), ('third', + 'fourth')]) + self.assertEqual(regex.findall(r"(?r)(\w+) (\w+)", + "first second third fourth fifth"), [('fourth', 'fifth'), ('second', + 'third')]) + + self.assertEqual([m[0] for m in regex.finditer(r"(?r).{2}", "abc")], + ['bc']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r).{2}", "abc", + overlapped=True)], ['bc', 'ab']) + self.assertEqual([m[0] for m in regex.finditer(r"(\w+) (\w+)", + "first second third fourth fifth")], ['first second', + 'third fourth']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)(\w+) (\w+)", + "first second third fourth fifth")], ['fourth fifth', + 'second third']) + + self.assertEqual(regex.search("abcdef", "abcdef").span(), (0, 6)) + self.assertEqual(regex.search("(?r)abcdef", "abcdef").span(), (0, 6)) + self.assertEqual(regex.search("(?i)abcdef", "ABCDEF").span(), (0, 6)) + self.assertEqual(regex.search("(?ir)abcdef", "ABCDEF").span(), (0, 6)) + + self.assertEqual(regex.sub(r"(.)", r"\1", "abc"), 'abc') + self.assertEqual(regex.sub(r"(?r)(.)", r"\1", "abc"), 'abc') + + def test_atomic(self): + # Issue 433030. + self.assertEqual(regex.search(r"(?>a*)a", "aa"), None) + + def test_possessive(self): + # Single-character non-possessive. + self.assertEqual(regex.search(r"a?a", "a").span(), (0, 1)) + self.assertEqual(regex.search(r"a*a", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"a+a", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"a{1,3}a", "aaa").span(), (0, 3)) + + # Multiple-character non-possessive. + self.assertEqual(regex.search(r"(?:ab)?ab", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"(?:ab)*ab", "ababab").span(), (0, 6)) + self.assertEqual(regex.search(r"(?:ab)+ab", "ababab").span(), (0, 6)) + self.assertEqual(regex.search(r"(?:ab){1,3}ab", "ababab").span(), (0, + 6)) + + # Single-character possessive. + self.assertEqual(regex.search(r"a?+a", "a"), None) + self.assertEqual(regex.search(r"a*+a", "aaa"), None) + self.assertEqual(regex.search(r"a++a", "aaa"), None) + self.assertEqual(regex.search(r"a{1,3}+a", "aaa"), None) + + # Multiple-character possessive. + self.assertEqual(regex.search(r"(?:ab)?+ab", "ab"), None) + self.assertEqual(regex.search(r"(?:ab)*+ab", "ababab"), None) + self.assertEqual(regex.search(r"(?:ab)++ab", "ababab"), None) + self.assertEqual(regex.search(r"(?:ab){1,3}+ab", "ababab"), None) + + def test_zerowidth(self): + # Issue 3262. + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split(r"\b", "a b"), ['', 'a', ' ', 'b', + '']) + else: + self.assertEqual(regex.split(r"\b", "a b"), ['a b']) + self.assertEqual(regex.split(r"(?V1)\b", "a b"), ['', 'a', ' ', 'b', + '']) + + # Issue 1647489. + self.assertEqual(regex.findall(r"^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"^|\w+", "foo bar")], + ['', 'foo', 'bar']) + self.assertEqual(regex.findall(r"(?r)^|\w+", "foo bar"), ['bar', + 'foo', '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)^|\w+", + "foo bar")], ['bar', 'foo', '']) + self.assertEqual(regex.findall(r"(?V1)^|\w+", "foo bar"), ['', 'foo', + 'bar']) + self.assertEqual([m[0] for m in regex.finditer(r"(?V1)^|\w+", + "foo bar")], ['', 'foo', 'bar']) + self.assertEqual(regex.findall(r"(?rV1)^|\w+", "foo bar"), ['bar', + 'foo', '']) + self.assertEqual([m[0] for m in regex.finditer(r"(?rV1)^|\w+", + "foo bar")], ['bar', 'foo', '']) + + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split("", "xaxbxc"), ['', 'x', 'a', 'x', + 'b', 'x', 'c', '']) + self.assertEqual([m for m in regex.splititer("", "xaxbxc")], ['', + 'x', 'a', 'x', 'b', 'x', 'c', '']) + else: + self.assertEqual(regex.split("", "xaxbxc"), ['xaxbxc']) + self.assertEqual([m for m in regex.splititer("", "xaxbxc")], + ['xaxbxc']) + + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split("(?r)", "xaxbxc"), ['', 'c', 'x', 'b', + 'x', 'a', 'x', '']) + self.assertEqual([m for m in regex.splititer("(?r)", "xaxbxc")], + ['', 'c', 'x', 'b', 'x', 'a', 'x', '']) + else: + self.assertEqual(regex.split("(?r)", "xaxbxc"), ['xaxbxc']) + self.assertEqual([m for m in regex.splititer("(?r)", "xaxbxc")], + ['xaxbxc']) + + self.assertEqual(regex.split("(?V1)", "xaxbxc"), ['', 'x', 'a', 'x', + 'b', 'x', 'c', '']) + self.assertEqual([m for m in regex.splititer("(?V1)", "xaxbxc")], ['', + 'x', 'a', 'x', 'b', 'x', 'c', '']) + + self.assertEqual(regex.split("(?rV1)", "xaxbxc"), ['', 'c', 'x', 'b', + 'x', 'a', 'x', '']) + self.assertEqual([m for m in regex.splititer("(?rV1)", "xaxbxc")], ['', + 'c', 'x', 'b', 'x', 'a', 'x', '']) + + def test_scoped_and_inline_flags(self): + # Issues 433028, 433024, 433027. + self.assertEqual(regex.search(r"(?i)Ab", "ab").span(), (0, 2)) + self.assertEqual(regex.search(r"(?i:A)b", "ab").span(), (0, 2)) + # Changed to positional flags in regex 2023.12.23. + self.assertEqual(regex.search(r"A(?i)b", "ab"), None) + + self.assertEqual(regex.search(r"(?V0)Ab", "ab"), None) + self.assertEqual(regex.search(r"(?V1)Ab", "ab"), None) + self.assertEqual(regex.search(r"(?-i)Ab", "ab", flags=regex.I), None) + self.assertEqual(regex.search(r"(?-i:A)b", "ab", flags=regex.I), None) + self.assertEqual(regex.search(r"A(?-i)b", "ab", flags=regex.I).span(), + (0, 2)) + + def test_repeated_repeats(self): + # Issue 2537. + self.assertEqual(regex.search(r"(?:a+)+", "aaa").span(), (0, 3)) + self.assertEqual(regex.search(r"(?:(?:ab)+c)+", "abcabc").span(), (0, + 6)) + + # Hg issue 286. + self.assertEqual(regex.search(r"(?:a+){2,}", "aaa").span(), (0, 3)) + + def test_lookbehind(self): + self.assertEqual(regex.search(r"123(?<=a\d+)", "a123").span(), (1, 4)) + self.assertEqual(regex.search(r"123(?<=a\d+)", "b123"), None) + self.assertEqual(regex.search(r"123(?= (3, 7, 0): + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "xy"), + 'y-x-') + else: + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "xy"), + 'y-x') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "xy"), 'y-x-') + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "x"), '-x-') + else: + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "x"), '-x') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "x"), '-x-') + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "y"), 'y--') + else: + self.assertEqual(regex.sub(r"(?V0)(x)?(y)?", r"\2-\1", "y"), 'y-') + self.assertEqual(regex.sub(r"(?V1)(x)?(y)?", r"\2-\1", "y"), 'y--') + + def test_bug_10328 (self): + # Issue 10328. + pat = regex.compile(r'(?mV0)(?P[ \t]+\r*$)|(?P(?<=[^\n])\Z)') + if sys.version_info >= (3, 7, 0): + self.assertEqual(pat.subn(lambda m: '<' + m.lastgroup + '>', + 'foobar '), ('foobar', 2)) + else: + self.assertEqual(pat.subn(lambda m: '<' + m.lastgroup + '>', + 'foobar '), ('foobar', 1)) + self.assertEqual([m.group() for m in pat.finditer('foobar ')], [' ', + '']) + pat = regex.compile(r'(?mV1)(?P[ \t]+\r*$)|(?P(?<=[^\n])\Z)') + self.assertEqual(pat.subn(lambda m: '<' + m.lastgroup + '>', + 'foobar '), ('foobar', 2)) + self.assertEqual([m.group() for m in pat.finditer('foobar ')], [' ', + '']) + + def test_overlapped(self): + self.assertEqual(regex.findall(r"..", "abcde"), ['ab', 'cd']) + self.assertEqual(regex.findall(r"..", "abcde", overlapped=True), ['ab', + 'bc', 'cd', 'de']) + self.assertEqual(regex.findall(r"(?r)..", "abcde"), ['de', 'bc']) + self.assertEqual(regex.findall(r"(?r)..", "abcde", overlapped=True), + ['de', 'cd', 'bc', 'ab']) + self.assertEqual(regex.findall(r"(.)(-)(.)", "a-b-c", overlapped=True), + [("a", "-", "b"), ("b", "-", "c")]) + + self.assertEqual([m[0] for m in regex.finditer(r"..", "abcde")], ['ab', + 'cd']) + self.assertEqual([m[0] for m in regex.finditer(r"..", "abcde", + overlapped=True)], ['ab', 'bc', 'cd', 'de']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde")], + ['de', 'bc']) + self.assertEqual([m[0] for m in regex.finditer(r"(?r)..", "abcde", + overlapped=True)], ['de', 'cd', 'bc', 'ab']) + + self.assertEqual([m.groups() for m in regex.finditer(r"(.)(-)(.)", + "a-b-c", overlapped=True)], [("a", "-", "b"), ("b", "-", "c")]) + self.assertEqual([m.groups() for m in regex.finditer(r"(?r)(.)(-)(.)", + "a-b-c", overlapped=True)], [("b", "-", "c"), ("a", "-", "b")]) + + def test_splititer(self): + self.assertEqual(regex.split(r",", "a,b,,c,"), ['a', 'b', '', 'c', '']) + self.assertEqual([m for m in regex.splititer(r",", "a,b,,c,")], ['a', + 'b', '', 'c', '']) + + def test_grapheme(self): + self.assertEqual(regex.match(r"\X", "\xE0").span(), (0, 1)) + self.assertEqual(regex.match(r"\X", "a\u0300").span(), (0, 2)) + + self.assertEqual(regex.findall(r"\X", + "a\xE0a\u0300e\xE9e\u0301"), ['a', '\xe0', 'a\u0300', 'e', + '\xe9', 'e\u0301']) + self.assertEqual(regex.findall(r"\X{3}", + "a\xE0a\u0300e\xE9e\u0301"), ['a\xe0a\u0300', 'e\xe9e\u0301']) + self.assertEqual(regex.findall(r"\X", "\r\r\n\u0301A\u0301"), + ['\r', '\r\n', '\u0301', 'A\u0301']) + + def test_word_boundary(self): + text = 'The quick ("brown") fox can\'t jump 32.3 feet, right?' + self.assertEqual(regex.split(r'(?V1)\b', text), ['', 'The', ' ', + 'quick', ' ("', 'brown', '") ', 'fox', ' ', 'can', "'", 't', + ' ', 'jump', ' ', '32', '.', '3', ' ', 'feet', ', ', + 'right', '?']) + self.assertEqual(regex.split(r'(?V1w)\b', text), ['', 'The', ' ', + 'quick', ' ', '(', '"', 'brown', '"', ')', ' ', 'fox', ' ', + "can't", ' ', 'jump', ' ', '32.3', ' ', 'feet', ',', ' ', + 'right', '?', '']) + + text = "The fox" + self.assertEqual(regex.split(r'(?V1)\b', text), ['', 'The', ' ', + 'fox', '']) + self.assertEqual(regex.split(r'(?V1w)\b', text), ['', 'The', ' ', + 'fox', '']) + + text = "can't aujourd'hui l'objectif" + self.assertEqual(regex.split(r'(?V1)\b', text), ['', 'can', "'", + 't', ' ', 'aujourd', "'", 'hui', ' ', 'l', "'", 'objectif', + '']) + self.assertEqual(regex.split(r'(?V1w)\b', text), ['', "can't", ' ', + "aujourd'hui", ' ', "l'objectif", '']) + + def test_line_boundary(self): + self.assertEqual(regex.findall(r".+", "Line 1\nLine 2\n"), ["Line 1", + "Line 2"]) + self.assertEqual(regex.findall(r".+", "Line 1\rLine 2\r"), + ["Line 1\rLine 2\r"]) + self.assertEqual(regex.findall(r".+", "Line 1\r\nLine 2\r\n"), + ["Line 1\r", "Line 2\r"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\nLine 2\n"), + ["Line 1", "Line 2"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\rLine 2\r"), + ["Line 1", "Line 2"]) + self.assertEqual(regex.findall(r"(?w).+", "Line 1\r\nLine 2\r\n"), + ["Line 1", "Line 2"]) + + self.assertEqual(regex.search(r"^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"^abc", "\nabc"), None) + self.assertEqual(regex.search(r"^abc", "\rabc"), None) + self.assertEqual(regex.search(r"(?w)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?w)^abc", "\nabc"), None) + self.assertEqual(regex.search(r"(?w)^abc", "\rabc"), None) + + self.assertEqual(regex.search(r"abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"abc$", "abc\r"), None) + self.assertEqual(regex.search(r"(?w)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?w)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?w)abc$", "abc\r").start(), 0) + + self.assertEqual(regex.search(r"(?m)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?m)^abc", "\nabc").start(), 1) + self.assertEqual(regex.search(r"(?m)^abc", "\rabc"), None) + self.assertEqual(regex.search(r"(?mw)^abc", "abc").start(), 0) + self.assertEqual(regex.search(r"(?mw)^abc", "\nabc").start(), 1) + self.assertEqual(regex.search(r"(?mw)^abc", "\rabc").start(), 1) + + self.assertEqual(regex.search(r"(?m)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?m)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?m)abc$", "abc\r"), None) + self.assertEqual(regex.search(r"(?mw)abc$", "abc").start(), 0) + self.assertEqual(regex.search(r"(?mw)abc$", "abc\n").start(), 0) + self.assertEqual(regex.search(r"(?mw)abc$", "abc\r").start(), 0) + + def test_branch_reset(self): + self.assertEqual(regex.match(r"(?:(a)|(b))(c)", "ac").groups(), ('a', + None, 'c')) + self.assertEqual(regex.match(r"(?:(a)|(b))(c)", "bc").groups(), (None, + 'b', 'c')) + self.assertEqual(regex.match(r"(?:(?a)|(?b))(?c)", + "ac").groups(), ('a', None, 'c')) + self.assertEqual(regex.match(r"(?:(?a)|(?b))(?c)", + "bc").groups(), (None, 'b', 'c')) + + self.assertEqual(regex.match(r"(?a)(?:(?b)|(?c))(?d)", + "abd").groups(), ('a', 'b', None, 'd')) + self.assertEqual(regex.match(r"(?a)(?:(?b)|(?c))(?d)", + "acd").groups(), ('a', None, 'c', 'd')) + self.assertEqual(regex.match(r"(a)(?:(b)|(c))(d)", "abd").groups(), + ('a', 'b', None, 'd')) + + self.assertEqual(regex.match(r"(a)(?:(b)|(c))(d)", "acd").groups(), + ('a', None, 'c', 'd')) + self.assertEqual(regex.match(r"(a)(?|(b)|(b))(d)", "abd").groups(), + ('a', 'b', 'd')) + self.assertEqual(regex.match(r"(?|(?a)|(?b))(c)", "ac").groups(), + ('a', None, 'c')) + self.assertEqual(regex.match(r"(?|(?a)|(?b))(c)", "bc").groups(), + (None, 'b', 'c')) + self.assertEqual(regex.match(r"(?|(?a)|(?b))(c)", "ac").groups(), + ('a', 'c')) + + self.assertEqual(regex.match(r"(?|(?a)|(?b))(c)", "bc").groups(), + ('b', 'c')) + + self.assertEqual(regex.match(r"(?|(?a)(?b)|(?c)(?d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(?c)(?d))(e)", + "cde").groups(), ('d', 'c', 'e')) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(?c)(d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(?c)(d))(e)", + "cde").groups(), ('d', 'c', 'e')) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(d))(e)", + "abe").groups(), ('a', 'b', 'e')) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(d))(e)", + "cde").groups(), ('c', 'd', 'e')) + + # Hg issue 87: Allow duplicate names of groups + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(?d))(e)", + "abe").groups(), ("a", "b", "e")) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(?d))(e)", + "abe").capturesdict(), {"a": ["a"], "b": ["b"]}) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(?d))(e)", + "cde").groups(), ("d", None, "e")) + self.assertEqual(regex.match(r"(?|(?a)(?b)|(c)(?d))(e)", + "cde").capturesdict(), {"a": ["c", "d"], "b": []}) + + def test_set(self): + self.assertEqual(regex.match(r"[a]", "a").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[a]", "A").span(), (0, 1)) + self.assertEqual(regex.match(r"[a-b]", r"a").span(), (0, 1)) + self.assertEqual(regex.match(r"(?i)[a-b]", r"A").span(), (0, 1)) + + self.assertEqual(regex.sub(r"(?V0)([][])", r"-", "a[b]c"), "a-b-c") + + self.assertEqual(regex.findall(r"[\p{Alpha}]", "a0"), ["a"]) + self.assertEqual(regex.findall(r"(?i)[\p{Alpha}]", "A0"), ["A"]) + + self.assertEqual(regex.findall(r"[a\p{Alpha}]", "ab0"), ["a", "b"]) + self.assertEqual(regex.findall(r"[a\P{Alpha}]", "ab0"), ["a", "0"]) + self.assertEqual(regex.findall(r"(?i)[a\p{Alpha}]", "ab0"), ["a", + "b"]) + self.assertEqual(regex.findall(r"(?i)[a\P{Alpha}]", "ab0"), ["a", + "0"]) + + self.assertEqual(regex.findall(r"[a-b\p{Alpha}]", "abC0"), ["a", + "b", "C"]) + self.assertEqual(regex.findall(r"(?i)[a-b\p{Alpha}]", "AbC0"), ["A", + "b", "C"]) + + self.assertEqual(regex.findall(r"[\p{Alpha}]", "a0"), ["a"]) + self.assertEqual(regex.findall(r"[\P{Alpha}]", "a0"), ["0"]) + self.assertEqual(regex.findall(r"[^\p{Alpha}]", "a0"), ["0"]) + self.assertEqual(regex.findall(r"[^\P{Alpha}]", "a0"), ["a"]) + + self.assertEqual("".join(regex.findall(r"[^\d-h]", "a^b12c-h")), + 'a^bc') + self.assertEqual("".join(regex.findall(r"[^\dh]", "a^b12c-h")), + 'a^bc-') + self.assertEqual("".join(regex.findall(r"[^h\s\db]", "a^b 12c-h")), + 'a^c-') + self.assertEqual("".join(regex.findall(r"[^b\w]", "a b")), ' ') + self.assertEqual("".join(regex.findall(r"[^b\S]", "a b")), ' ') + self.assertEqual("".join(regex.findall(r"[^8\d]", "a 1b2")), 'a b') + + all_chars = "".join(chr(c) for c in range(0x100)) + self.assertEqual(len(regex.findall(r"\p{ASCII}", all_chars)), 128) + self.assertEqual(len(regex.findall(r"\p{Letter}", all_chars)), + 117) + self.assertEqual(len(regex.findall(r"\p{Digit}", all_chars)), 10) + + # Set operators + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}&&\p{Letter}]", + all_chars)), 52) + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}&&\p{Alnum}&&\p{Letter}]", + all_chars)), 52) + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}&&\p{Alnum}&&\p{Digit}]", + all_chars)), 10) + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}&&\p{Cc}]", + all_chars)), 33) + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}&&\p{Graph}]", + all_chars)), 94) + self.assertEqual(len(regex.findall(r"(?V1)[\p{ASCII}--\p{Cc}]", + all_chars)), 95) + self.assertEqual(len(regex.findall(r"[\p{Letter}\p{Digit}]", + all_chars)), 127) + self.assertEqual(len(regex.findall(r"(?V1)[\p{Letter}||\p{Digit}]", + all_chars)), 127) + self.assertEqual(len(regex.findall(r"\p{HexDigit}", all_chars)), + 22) + self.assertEqual(len(regex.findall(r"(?V1)[\p{HexDigit}~~\p{Digit}]", + all_chars)), 12) + self.assertEqual(len(regex.findall(r"(?V1)[\p{Digit}~~\p{HexDigit}]", + all_chars)), 12) + + self.assertEqual(repr(type(regex.compile(r"(?V0)([][-])"))), + self.PATTERN_CLASS) + self.assertEqual(regex.findall(r"(?V1)[[a-z]--[aei]]", "abc"), ["b", + "c"]) + self.assertEqual(regex.findall(r"(?iV1)[[a-z]--[aei]]", "abc"), ["b", + "c"]) + self.assertEqual(regex.findall(r"(?V1)[\w--a]","abc"), ["b", "c"]) + self.assertEqual(regex.findall(r"(?iV1)[\w--a]","abc"), ["b", "c"]) + + def test_various(self): + tests = [ + # Test ?P< and ?P= extensions. + ('(?Pa)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with a digit. + ('(?Pa)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with an illegal char. + ('(?Pa)', '', '', regex.error, self.BAD_GROUP_NAME), # Begins with an illegal char. + + # Same tests, for the ?P= form. + ('(?Pa)(?P=foo_123', 'aa', '', regex.error, + self.MISSING_RPAREN), + ('(?Pa)(?P=1)', 'aa', '1', ascii('a')), + ('(?Pa)(?P=0)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?Pa)(?P=-1)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?Pa)(?P=!)', 'aa', '', regex.error, + self.BAD_GROUP_NAME), + ('(?Pa)(?P=foo_124)', 'aa', '', regex.error, + self.UNKNOWN_GROUP), # Backref to undefined group. + + ('(?Pa)', 'a', '1', ascii('a')), + ('(?Pa)(?P=foo_123)', 'aa', '1', ascii('a')), + + # Mal-formed \g in pattern treated as literal for compatibility. + (r'(?a)\ga)\g<1>', 'aa', '1', ascii('a')), + (r'(?a)\g', 'aa', '', ascii(None)), + (r'(?a)\g', 'aa', '', regex.error, + self.UNKNOWN_GROUP), # Backref to undefined group. + + ('(?a)', 'a', '1', ascii('a')), + (r'(?a)\g', 'aa', '1', ascii('a')), + + # Test octal escapes. + ('\\1', 'a', '', regex.error, self.INVALID_GROUP_REF), # Backreference. + ('[\\1]', '\1', '0', "'\\x01'"), # Character. + ('\\09', chr(0) + '9', '0', ascii(chr(0) + '9')), + ('\\141', 'a', '0', ascii('a')), + ('(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\119', 'abcdefghijklk9', + '0,11', ascii(('abcdefghijklk9', 'k'))), + + # Test \0 is handled everywhere. + (r'\0', '\0', '0', ascii('\0')), + (r'[\0a]', '\0', '0', ascii('\0')), + (r'[a\0]', '\0', '0', ascii('\0')), + (r'[^a\0]', '\0', '', ascii(None)), + + # Test various letter escapes. + (r'\a[\b]\f\n\r\t\v', '\a\b\f\n\r\t\v', '0', + ascii('\a\b\f\n\r\t\v')), + (r'[\a][\b][\f][\n][\r][\t][\v]', '\a\b\f\n\r\t\v', '0', + ascii('\a\b\f\n\r\t\v')), + (r'\xff', '\377', '0', ascii(chr(255))), + + # New \x semantics. + (r'\x00ffffffffffffff', '\377', '', ascii(None)), + (r'\x00f', '\017', '', ascii(None)), + (r'\x00fe', '\376', '', ascii(None)), + + (r'\x00ff', '\377', '', ascii(None)), + (r'\t\n\v\r\f\a\g', '\t\n\v\r\f\ag', '0', ascii('\t\n\v\r\f\ag')), + ('\t\n\v\r\f\a\\g', '\t\n\v\r\f\ag', '0', ascii('\t\n\v\r\f\ag')), + (r'\t\n\v\r\f\a', '\t\n\v\r\f\a', '0', ascii(chr(9) + chr(10) + + chr(11) + chr(13) + chr(12) + chr(7))), + (r'[\t][\n][\v][\r][\f][\b]', '\t\n\v\r\f\b', '0', + ascii('\t\n\v\r\f\b')), + + (r"^\w+=(\\[\000-\277]|[^\n\\])*", + "SRC=eval.c g.c blah blah blah \\\\\n\tapes.c", '0', + ascii("SRC=eval.c g.c blah blah blah \\\\")), + + # Test that . only matches \n in DOTALL mode. + ('a.b', 'acb', '0', ascii('acb')), + ('a.b', 'a\nb', '', ascii(None)), + ('a.*b', 'acc\nccb', '', ascii(None)), + ('a.{4,5}b', 'acc\nccb', '', ascii(None)), + ('a.b', 'a\rb', '0', ascii('a\rb')), + # Changed to positional flags in regex 2023.12.23. + ('a.b(?s)', 'a\nb', '', ascii(None)), + ('(?s)a.b', 'a\nb', '0', ascii('a\nb')), + ('a.*(?s)b', 'acc\nccb', '', ascii(None)), + ('(?s)a.*b', 'acc\nccb', '0', ascii('acc\nccb')), + ('(?s)a.{4,5}b', 'acc\nccb', '0', ascii('acc\nccb')), + + (')', '', '', regex.error, self.TRAILING_CHARS), # Unmatched right bracket. + ('', '', '0', "''"), # Empty pattern. + ('abc', 'abc', '0', ascii('abc')), + ('abc', 'xbc', '', ascii(None)), + ('abc', 'axc', '', ascii(None)), + ('abc', 'abx', '', ascii(None)), + ('abc', 'xabcy', '0', ascii('abc')), + ('abc', 'ababc', '0', ascii('abc')), + ('ab*c', 'abc', '0', ascii('abc')), + ('ab*bc', 'abc', '0', ascii('abc')), + + ('ab*bc', 'abbc', '0', ascii('abbc')), + ('ab*bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab+bc', 'abbc', '0', ascii('abbc')), + ('ab+bc', 'abc', '', ascii(None)), + ('ab+bc', 'abq', '', ascii(None)), + ('ab+bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab?bc', 'abbc', '0', ascii('abbc')), + ('ab?bc', 'abc', '0', ascii('abc')), + ('ab?bc', 'abbbbc', '', ascii(None)), + ('ab?c', 'abc', '0', ascii('abc')), + + ('^abc$', 'abc', '0', ascii('abc')), + ('^abc$', 'abcc', '', ascii(None)), + ('^abc', 'abcc', '0', ascii('abc')), + ('^abc$', 'aabc', '', ascii(None)), + ('abc$', 'aabc', '0', ascii('abc')), + ('^', 'abc', '0', ascii('')), + ('$', 'abc', '0', ascii('')), + ('a.c', 'abc', '0', ascii('abc')), + ('a.c', 'axc', '0', ascii('axc')), + ('a.*c', 'axyzc', '0', ascii('axyzc')), + + ('a.*c', 'axyzd', '', ascii(None)), + ('a[bc]d', 'abc', '', ascii(None)), + ('a[bc]d', 'abd', '0', ascii('abd')), + ('a[b-d]e', 'abd', '', ascii(None)), + ('a[b-d]e', 'ace', '0', ascii('ace')), + ('a[b-d]', 'aac', '0', ascii('ac')), + ('a[-b]', 'a-', '0', ascii('a-')), + ('a[\\-b]', 'a-', '0', ascii('a-')), + ('a[b-]', 'a-', '0', ascii('a-')), + ('a[]b', '-', '', regex.error, self.BAD_SET), + + ('a[', '-', '', regex.error, self.BAD_SET), + ('a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('a]', 'a]', '0', ascii('a]')), + ('a[]]b', 'a]b', '0', ascii('a]b')), + ('a[]]b', 'a]b', '0', ascii('a]b')), + ('a[^bc]d', 'aed', '0', ascii('aed')), + ('a[^bc]d', 'abd', '', ascii(None)), + ('a[^-b]c', 'adc', '0', ascii('adc')), + + ('a[^-b]c', 'a-c', '', ascii(None)), + ('a[^]b]c', 'a]c', '', ascii(None)), + ('a[^]b]c', 'adc', '0', ascii('adc')), + ('\\ba\\b', 'a-', '0', ascii('a')), + ('\\ba\\b', '-a', '0', ascii('a')), + ('\\ba\\b', '-a-', '0', ascii('a')), + ('\\by\\b', 'xy', '', ascii(None)), + ('\\by\\b', 'yz', '', ascii(None)), + ('\\by\\b', 'xyz', '', ascii(None)), + ('x\\b', 'xyz', '', ascii(None)), + + ('x\\B', 'xyz', '0', ascii('x')), + ('\\Bz', 'xyz', '0', ascii('z')), + ('z\\B', 'xyz', '', ascii(None)), + ('\\Bx', 'xyz', '', ascii(None)), + ('\\Ba\\B', 'a-', '', ascii(None)), + ('\\Ba\\B', '-a', '', ascii(None)), + ('\\Ba\\B', '-a-', '', ascii(None)), + ('\\By\\B', 'xy', '', ascii(None)), + ('\\By\\B', 'yz', '', ascii(None)), + ('\\By\\b', 'xy', '0', ascii('y')), + + ('\\by\\B', 'yz', '0', ascii('y')), + ('\\By\\B', 'xyz', '0', ascii('y')), + ('ab|cd', 'abc', '0', ascii('ab')), + ('ab|cd', 'abcd', '0', ascii('ab')), + ('()ef', 'def', '0,1', ascii(('ef', ''))), + ('$b', 'b', '', ascii(None)), + ('a\\(b', 'a(b', '', ascii(('a(b',))), + ('a\\(*b', 'ab', '0', ascii('ab')), + ('a\\(*b', 'a((b', '0', ascii('a((b')), + ('a\\\\b', 'a\\b', '0', ascii('a\\b')), + + ('((a))', 'abc', '0,1,2', ascii(('a', 'a', 'a'))), + ('(a)b(c)', 'abc', '0,1,2', ascii(('abc', 'a', 'c'))), + ('a+b+c', 'aabbabc', '0', ascii('abc')), + ('(a+|b)*', 'ab', '0,1', ascii(('ab', 'b'))), + ('(a+|b)+', 'ab', '0,1', ascii(('ab', 'b'))), + ('(a+|b)?', 'ab', '0,1', ascii(('a', 'a'))), + (')(', '-', '', regex.error, self.TRAILING_CHARS), + ('[^ab]*', 'cde', '0', ascii('cde')), + ('abc', '', '', ascii(None)), + ('a*', '', '0', ascii('')), + + ('a|b|c|d|e', 'e', '0', ascii('e')), + ('(a|b|c|d|e)f', 'ef', '0,1', ascii(('ef', 'e'))), + ('abcd*efg', 'abcdefg', '0', ascii('abcdefg')), + ('ab*', 'xabyabbbz', '0', ascii('ab')), + ('ab*', 'xayabbbz', '0', ascii('a')), + ('(ab|cd)e', 'abcde', '0,1', ascii(('cde', 'cd'))), + ('[abhgefdc]ij', 'hij', '0', ascii('hij')), + ('^(ab|cd)e', 'abcde', '', ascii(None)), + ('(abc|)ef', 'abcdef', '0,1', ascii(('ef', ''))), + ('(a|b)c*d', 'abcd', '0,1', ascii(('bcd', 'b'))), + + ('(ab|ab*)bc', 'abc', '0,1', ascii(('abc', 'a'))), + ('a([bc]*)c*', 'abc', '0,1', ascii(('abc', 'bc'))), + ('a([bc]*)(c*d)', 'abcd', '0,1,2', ascii(('abcd', 'bc', 'd'))), + ('a([bc]+)(c*d)', 'abcd', '0,1,2', ascii(('abcd', 'bc', 'd'))), + ('a([bc]*)(c+d)', 'abcd', '0,1,2', ascii(('abcd', 'b', 'cd'))), + ('a[bcd]*dcdcde', 'adcdcde', '0', ascii('adcdcde')), + ('a[bcd]+dcdcde', 'adcdcde', '', ascii(None)), + ('(ab|a)b*c', 'abc', '0,1', ascii(('abc', 'ab'))), + ('((a)(b)c)(d)', 'abcd', '1,2,3,4', ascii(('abc', 'a', 'b', 'd'))), + ('[a-zA-Z_][a-zA-Z0-9_]*', 'alpha', '0', ascii('alpha')), + + ('^a(bc+|b[eh])g|.h$', 'abh', '0,1', ascii(('bh', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', '0,1,2', ascii(('effgz', + 'effgz', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'ij', '0,1,2', ascii(('ij', 'ij', + 'j'))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effg', '', ascii(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', '', ascii(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', '0,1,2', ascii(('effgz', + 'effgz', None))), + ('(((((((((a)))))))))', 'a', '0', ascii('a')), + ('multiple words of text', 'uh-uh', '', ascii(None)), + ('multiple words', 'multiple words, yeah', '0', + ascii('multiple words')), + ('(.*)c(.*)', 'abcde', '0,1,2', ascii(('abcde', 'ab', 'de'))), + + ('\\((.*), (.*)\\)', '(a, b)', '2,1', ascii(('b', 'a'))), + ('[k]', 'ab', '', ascii(None)), + ('a[-]?c', 'ac', '0', ascii('ac')), + ('(abc)\\1', 'abcabc', '1', ascii('abc')), + ('([a-c]*)\\1', 'abcabc', '1', ascii('abc')), + ('^(.+)?B', 'AB', '1', ascii('A')), + ('(a+).\\1$', 'aaaaa', '0,1', ascii(('aaaaa', 'aa'))), + ('^(a+).\\1$', 'aaaa', '', ascii(None)), + ('(abc)\\1', 'abcabc', '0,1', ascii(('abcabc', 'abc'))), + ('([a-c]+)\\1', 'abcabc', '0,1', ascii(('abcabc', 'abc'))), + + ('(a)\\1', 'aa', '0,1', ascii(('aa', 'a'))), + ('(a+)\\1', 'aa', '0,1', ascii(('aa', 'a'))), + ('(a+)+\\1', 'aa', '0,1', ascii(('aa', 'a'))), + ('(a).+\\1', 'aba', '0,1', ascii(('aba', 'a'))), + ('(a)ba*\\1', 'aba', '0,1', ascii(('aba', 'a'))), + ('(aa|a)a\\1$', 'aaa', '0,1', ascii(('aaa', 'a'))), + ('(a|aa)a\\1$', 'aaa', '0,1', ascii(('aaa', 'a'))), + ('(a+)a\\1$', 'aaa', '0,1', ascii(('aaa', 'a'))), + ('([abc]*)\\1', 'abcabc', '0,1', ascii(('abcabc', 'abc'))), + ('(a)(b)c|ab', 'ab', '0,1,2', ascii(('ab', None, None))), + + ('(a)+x', 'aaax', '0,1', ascii(('aaax', 'a'))), + ('([ac])+x', 'aacx', '0,1', ascii(('aacx', 'c'))), + ('([^/]*/)*sub1/', 'd:msgs/tdir/sub1/trial/away.cpp', '0,1', + ascii(('d:msgs/tdir/sub1/', 'tdir/'))), + ('([^.]*)\\.([^:]*):[T ]+(.*)', 'track1.title:TBlah blah blah', + '0,1,2,3', ascii(('track1.title:TBlah blah blah', 'track1', + 'title', 'Blah blah blah'))), + ('([^N]*N)+', 'abNNxyzN', '0,1', ascii(('abNNxyzN', 'xyzN'))), + ('([^N]*N)+', 'abNNxyz', '0,1', ascii(('abNN', 'N'))), + ('([abc]*)x', 'abcx', '0,1', ascii(('abcx', 'abc'))), + ('([abc]*)x', 'abc', '', ascii(None)), + ('([xyz]*)x', 'abcx', '0,1', ascii(('x', ''))), + ('(a)+b|aac', 'aac', '0,1', ascii(('aac', None))), + + # Test symbolic groups. + ('(?Paaa)a', 'aaaa', '', regex.error, self.BAD_GROUP_NAME), + ('(?Paaa)a', 'aaaa', '0,id', ascii(('aaaa', 'aaa'))), + ('(?Paa)(?P=id)', 'aaaa', '0,id', ascii(('aaaa', 'aa'))), + ('(?Paa)(?P=xd)', 'aaaa', '', regex.error, self.UNKNOWN_GROUP), + + # Character properties. + (r"\g", "g", '0', ascii('g')), + (r"\g<1>", "g", '', regex.error, self.INVALID_GROUP_REF), + (r"(.)\g<1>", "gg", '0', ascii('gg')), + (r"(.)\g<1>", "gg", '', ascii(('gg', 'g'))), + (r"\N", "N", '0', ascii('N')), + (r"\N{LATIN SMALL LETTER A}", "a", '0', ascii('a')), + (r"\p", "p", '0', ascii('p')), + (r"\p{Ll}", "a", '0', ascii('a')), + (r"\P", "P", '0', ascii('P')), + (r"\P{Lu}", "p", '0', ascii('p')), + + # All tests from Perl. + ('abc', 'abc', '0', ascii('abc')), + ('abc', 'xbc', '', ascii(None)), + ('abc', 'axc', '', ascii(None)), + ('abc', 'abx', '', ascii(None)), + ('abc', 'xabcy', '0', ascii('abc')), + ('abc', 'ababc', '0', ascii('abc')), + + ('ab*c', 'abc', '0', ascii('abc')), + ('ab*bc', 'abc', '0', ascii('abc')), + ('ab*bc', 'abbc', '0', ascii('abbc')), + ('ab*bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab{0,}bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab+bc', 'abbc', '0', ascii('abbc')), + ('ab+bc', 'abc', '', ascii(None)), + ('ab+bc', 'abq', '', ascii(None)), + ('ab{1,}bc', 'abq', '', ascii(None)), + ('ab+bc', 'abbbbc', '0', ascii('abbbbc')), + + ('ab{1,}bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab{1,3}bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab{3,4}bc', 'abbbbc', '0', ascii('abbbbc')), + ('ab{4,5}bc', 'abbbbc', '', ascii(None)), + ('ab?bc', 'abbc', '0', ascii('abbc')), + ('ab?bc', 'abc', '0', ascii('abc')), + ('ab{0,1}bc', 'abc', '0', ascii('abc')), + ('ab?bc', 'abbbbc', '', ascii(None)), + ('ab?c', 'abc', '0', ascii('abc')), + ('ab{0,1}c', 'abc', '0', ascii('abc')), + + ('^abc$', 'abc', '0', ascii('abc')), + ('^abc$', 'abcc', '', ascii(None)), + ('^abc', 'abcc', '0', ascii('abc')), + ('^abc$', 'aabc', '', ascii(None)), + ('abc$', 'aabc', '0', ascii('abc')), + ('^', 'abc', '0', ascii('')), + ('$', 'abc', '0', ascii('')), + ('a.c', 'abc', '0', ascii('abc')), + ('a.c', 'axc', '0', ascii('axc')), + ('a.*c', 'axyzc', '0', ascii('axyzc')), + + ('a.*c', 'axyzd', '', ascii(None)), + ('a[bc]d', 'abc', '', ascii(None)), + ('a[bc]d', 'abd', '0', ascii('abd')), + ('a[b-d]e', 'abd', '', ascii(None)), + ('a[b-d]e', 'ace', '0', ascii('ace')), + ('a[b-d]', 'aac', '0', ascii('ac')), + ('a[-b]', 'a-', '0', ascii('a-')), + ('a[b-]', 'a-', '0', ascii('a-')), + ('a[b-a]', '-', '', regex.error, self.BAD_CHAR_RANGE), + ('a[]b', '-', '', regex.error, self.BAD_SET), + + ('a[', '-', '', regex.error, self.BAD_SET), + ('a]', 'a]', '0', ascii('a]')), + ('a[]]b', 'a]b', '0', ascii('a]b')), + ('a[^bc]d', 'aed', '0', ascii('aed')), + ('a[^bc]d', 'abd', '', ascii(None)), + ('a[^-b]c', 'adc', '0', ascii('adc')), + ('a[^-b]c', 'a-c', '', ascii(None)), + ('a[^]b]c', 'a]c', '', ascii(None)), + ('a[^]b]c', 'adc', '0', ascii('adc')), + ('ab|cd', 'abc', '0', ascii('ab')), + + ('ab|cd', 'abcd', '0', ascii('ab')), + ('()ef', 'def', '0,1', ascii(('ef', ''))), + ('*a', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(*)b', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('$b', 'b', '', ascii(None)), + ('a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('a\\(b', 'a(b', '', ascii(('a(b',))), + ('a\\(*b', 'ab', '0', ascii('ab')), + ('a\\(*b', 'a((b', '0', ascii('a((b')), + ('a\\\\b', 'a\\b', '0', ascii('a\\b')), + + ('abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('((a))', 'abc', '0,1,2', ascii(('a', 'a', 'a'))), + ('(a)b(c)', 'abc', '0,1,2', ascii(('abc', 'a', 'c'))), + ('a+b+c', 'aabbabc', '0', ascii('abc')), + ('a{1,}b{1,}c', 'aabbabc', '0', ascii('abc')), + ('a**', '-', '', regex.error, self.MULTIPLE_REPEAT), + ('a.+?c', 'abcabc', '0', ascii('abc')), + ('(a+|b)*', 'ab', '0,1', ascii(('ab', 'b'))), + ('(a+|b){0,}', 'ab', '0,1', ascii(('ab', 'b'))), + + ('(a+|b)+', 'ab', '0,1', ascii(('ab', 'b'))), + ('(a+|b){1,}', 'ab', '0,1', ascii(('ab', 'b'))), + ('(a+|b)?', 'ab', '0,1', ascii(('a', 'a'))), + ('(a+|b){0,1}', 'ab', '0,1', ascii(('a', 'a'))), + (')(', '-', '', regex.error, self.TRAILING_CHARS), + ('[^ab]*', 'cde', '0', ascii('cde')), + ('abc', '', '', ascii(None)), + ('a*', '', '0', ascii('')), + ('([abc])*d', 'abbbcd', '0,1', ascii(('abbbcd', 'c'))), + ('([abc])*bcd', 'abcd', '0,1', ascii(('abcd', 'a'))), + + ('a|b|c|d|e', 'e', '0', ascii('e')), + ('(a|b|c|d|e)f', 'ef', '0,1', ascii(('ef', 'e'))), + ('abcd*efg', 'abcdefg', '0', ascii('abcdefg')), + ('ab*', 'xabyabbbz', '0', ascii('ab')), + ('ab*', 'xayabbbz', '0', ascii('a')), + ('(ab|cd)e', 'abcde', '0,1', ascii(('cde', 'cd'))), + ('[abhgefdc]ij', 'hij', '0', ascii('hij')), + ('^(ab|cd)e', 'abcde', '', ascii(None)), + ('(abc|)ef', 'abcdef', '0,1', ascii(('ef', ''))), + ('(a|b)c*d', 'abcd', '0,1', ascii(('bcd', 'b'))), + + ('(ab|ab*)bc', 'abc', '0,1', ascii(('abc', 'a'))), + ('a([bc]*)c*', 'abc', '0,1', ascii(('abc', 'bc'))), + ('a([bc]*)(c*d)', 'abcd', '0,1,2', ascii(('abcd', 'bc', 'd'))), + ('a([bc]+)(c*d)', 'abcd', '0,1,2', ascii(('abcd', 'bc', 'd'))), + ('a([bc]*)(c+d)', 'abcd', '0,1,2', ascii(('abcd', 'b', 'cd'))), + ('a[bcd]*dcdcde', 'adcdcde', '0', ascii('adcdcde')), + ('a[bcd]+dcdcde', 'adcdcde', '', ascii(None)), + ('(ab|a)b*c', 'abc', '0,1', ascii(('abc', 'ab'))), + ('((a)(b)c)(d)', 'abcd', '1,2,3,4', ascii(('abc', 'a', 'b', 'd'))), + ('[a-zA-Z_][a-zA-Z0-9_]*', 'alpha', '0', ascii('alpha')), + + ('^a(bc+|b[eh])g|.h$', 'abh', '0,1', ascii(('bh', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effgz', '0,1,2', ascii(('effgz', + 'effgz', None))), + ('(bc+d$|ef*g.|h?i(j|k))', 'ij', '0,1,2', ascii(('ij', 'ij', + 'j'))), + ('(bc+d$|ef*g.|h?i(j|k))', 'effg', '', ascii(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'bcdd', '', ascii(None)), + ('(bc+d$|ef*g.|h?i(j|k))', 'reffgz', '0,1,2', ascii(('effgz', + 'effgz', None))), + ('((((((((((a))))))))))', 'a', '10', ascii('a')), + ('((((((((((a))))))))))\\10', 'aa', '0', ascii('aa')), + + # Python does not have the same rules for \\41 so this is a syntax error + # ('((((((((((a))))))))))\\41', 'aa', '', ascii(None)), + # ('((((((((((a))))))))))\\41', 'a!', '0', ascii('a!')), + ('((((((((((a))))))))))\\41', '', '', regex.error, + self.INVALID_GROUP_REF), + ('(?i)((((((((((a))))))))))\\41', '', '', regex.error, + self.INVALID_GROUP_REF), + + ('(((((((((a)))))))))', 'a', '0', ascii('a')), + ('multiple words of text', 'uh-uh', '', ascii(None)), + ('multiple words', 'multiple words, yeah', '0', + ascii('multiple words')), + ('(.*)c(.*)', 'abcde', '0,1,2', ascii(('abcde', 'ab', 'de'))), + ('\\((.*), (.*)\\)', '(a, b)', '2,1', ascii(('b', 'a'))), + ('[k]', 'ab', '', ascii(None)), + ('a[-]?c', 'ac', '0', ascii('ac')), + ('(abc)\\1', 'abcabc', '1', ascii('abc')), + ('([a-c]*)\\1', 'abcabc', '1', ascii('abc')), + ('(?i)abc', 'ABC', '0', ascii('ABC')), + + ('(?i)abc', 'XBC', '', ascii(None)), + ('(?i)abc', 'AXC', '', ascii(None)), + ('(?i)abc', 'ABX', '', ascii(None)), + ('(?i)abc', 'XABCY', '0', ascii('ABC')), + ('(?i)abc', 'ABABC', '0', ascii('ABC')), + ('(?i)ab*c', 'ABC', '0', ascii('ABC')), + ('(?i)ab*bc', 'ABC', '0', ascii('ABC')), + ('(?i)ab*bc', 'ABBC', '0', ascii('ABBC')), + ('(?i)ab*?bc', 'ABBBBC', '0', ascii('ABBBBC')), + ('(?i)ab{0,}?bc', 'ABBBBC', '0', ascii('ABBBBC')), + + ('(?i)ab+?bc', 'ABBC', '0', ascii('ABBC')), + ('(?i)ab+bc', 'ABC', '', ascii(None)), + ('(?i)ab+bc', 'ABQ', '', ascii(None)), + ('(?i)ab{1,}bc', 'ABQ', '', ascii(None)), + ('(?i)ab+bc', 'ABBBBC', '0', ascii('ABBBBC')), + ('(?i)ab{1,}?bc', 'ABBBBC', '0', ascii('ABBBBC')), + ('(?i)ab{1,3}?bc', 'ABBBBC', '0', ascii('ABBBBC')), + ('(?i)ab{3,4}?bc', 'ABBBBC', '0', ascii('ABBBBC')), + ('(?i)ab{4,5}?bc', 'ABBBBC', '', ascii(None)), + ('(?i)ab??bc', 'ABBC', '0', ascii('ABBC')), + + ('(?i)ab??bc', 'ABC', '0', ascii('ABC')), + ('(?i)ab{0,1}?bc', 'ABC', '0', ascii('ABC')), + ('(?i)ab??bc', 'ABBBBC', '', ascii(None)), + ('(?i)ab??c', 'ABC', '0', ascii('ABC')), + ('(?i)ab{0,1}?c', 'ABC', '0', ascii('ABC')), + ('(?i)^abc$', 'ABC', '0', ascii('ABC')), + ('(?i)^abc$', 'ABCC', '', ascii(None)), + ('(?i)^abc', 'ABCC', '0', ascii('ABC')), + ('(?i)^abc$', 'AABC', '', ascii(None)), + ('(?i)abc$', 'AABC', '0', ascii('ABC')), + + ('(?i)^', 'ABC', '0', ascii('')), + ('(?i)$', 'ABC', '0', ascii('')), + ('(?i)a.c', 'ABC', '0', ascii('ABC')), + ('(?i)a.c', 'AXC', '0', ascii('AXC')), + ('(?i)a.*?c', 'AXYZC', '0', ascii('AXYZC')), + ('(?i)a.*c', 'AXYZD', '', ascii(None)), + ('(?i)a[bc]d', 'ABC', '', ascii(None)), + ('(?i)a[bc]d', 'ABD', '0', ascii('ABD')), + ('(?i)a[b-d]e', 'ABD', '', ascii(None)), + ('(?i)a[b-d]e', 'ACE', '0', ascii('ACE')), + + ('(?i)a[b-d]', 'AAC', '0', ascii('AC')), + ('(?i)a[-b]', 'A-', '0', ascii('A-')), + ('(?i)a[b-]', 'A-', '0', ascii('A-')), + ('(?i)a[b-a]', '-', '', regex.error, self.BAD_CHAR_RANGE), + ('(?i)a[]b', '-', '', regex.error, self.BAD_SET), + ('(?i)a[', '-', '', regex.error, self.BAD_SET), + ('(?i)a]', 'A]', '0', ascii('A]')), + ('(?i)a[]]b', 'A]B', '0', ascii('A]B')), + ('(?i)a[^bc]d', 'AED', '0', ascii('AED')), + ('(?i)a[^bc]d', 'ABD', '', ascii(None)), + + ('(?i)a[^-b]c', 'ADC', '0', ascii('ADC')), + ('(?i)a[^-b]c', 'A-C', '', ascii(None)), + ('(?i)a[^]b]c', 'A]C', '', ascii(None)), + ('(?i)a[^]b]c', 'ADC', '0', ascii('ADC')), + ('(?i)ab|cd', 'ABC', '0', ascii('AB')), + ('(?i)ab|cd', 'ABCD', '0', ascii('AB')), + ('(?i)()ef', 'DEF', '0,1', ascii(('EF', ''))), + ('(?i)*a', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(?i)(*)b', '-', '', regex.error, self.NOTHING_TO_REPEAT), + ('(?i)$b', 'B', '', ascii(None)), + + ('(?i)a\\', '-', '', regex.error, self.BAD_ESCAPE), + ('(?i)a\\(b', 'A(B', '', ascii(('A(B',))), + ('(?i)a\\(*b', 'AB', '0', ascii('AB')), + ('(?i)a\\(*b', 'A((B', '0', ascii('A((B')), + ('(?i)a\\\\b', 'A\\B', '0', ascii('A\\B')), + ('(?i)abc)', '-', '', regex.error, self.TRAILING_CHARS), + ('(?i)(abc', '-', '', regex.error, self.MISSING_RPAREN), + ('(?i)((a))', 'ABC', '0,1,2', ascii(('A', 'A', 'A'))), + ('(?i)(a)b(c)', 'ABC', '0,1,2', ascii(('ABC', 'A', 'C'))), + ('(?i)a+b+c', 'AABBABC', '0', ascii('ABC')), + + ('(?i)a{1,}b{1,}c', 'AABBABC', '0', ascii('ABC')), + ('(?i)a**', '-', '', regex.error, self.MULTIPLE_REPEAT), + ('(?i)a.+?c', 'ABCABC', '0', ascii('ABC')), + ('(?i)a.*?c', 'ABCABC', '0', ascii('ABC')), + ('(?i)a.{0,5}?c', 'ABCABC', '0', ascii('ABC')), + ('(?i)(a+|b)*', 'AB', '0,1', ascii(('AB', 'B'))), + ('(?i)(a+|b){0,}', 'AB', '0,1', ascii(('AB', 'B'))), + ('(?i)(a+|b)+', 'AB', '0,1', ascii(('AB', 'B'))), + ('(?i)(a+|b){1,}', 'AB', '0,1', ascii(('AB', 'B'))), + ('(?i)(a+|b)?', 'AB', '0,1', ascii(('A', 'A'))), + + ('(?i)(a+|b){0,1}', 'AB', '0,1', ascii(('A', 'A'))), + ('(?i)(a+|b){0,1}?', 'AB', '0,1', ascii(('', None))), + ('(?i))(', '-', '', regex.error, self.TRAILING_CHARS), + ('(?i)[^ab]*', 'CDE', '0', ascii('CDE')), + ('(?i)abc', '', '', ascii(None)), + ('(?i)a*', '', '0', ascii('')), + ('(?i)([abc])*d', 'ABBBCD', '0,1', ascii(('ABBBCD', 'C'))), + ('(?i)([abc])*bcd', 'ABCD', '0,1', ascii(('ABCD', 'A'))), + ('(?i)a|b|c|d|e', 'E', '0', ascii('E')), + ('(?i)(a|b|c|d|e)f', 'EF', '0,1', ascii(('EF', 'E'))), + + ('(?i)abcd*efg', 'ABCDEFG', '0', ascii('ABCDEFG')), + ('(?i)ab*', 'XABYABBBZ', '0', ascii('AB')), + ('(?i)ab*', 'XAYABBBZ', '0', ascii('A')), + ('(?i)(ab|cd)e', 'ABCDE', '0,1', ascii(('CDE', 'CD'))), + ('(?i)[abhgefdc]ij', 'HIJ', '0', ascii('HIJ')), + ('(?i)^(ab|cd)e', 'ABCDE', '', ascii(None)), + ('(?i)(abc|)ef', 'ABCDEF', '0,1', ascii(('EF', ''))), + ('(?i)(a|b)c*d', 'ABCD', '0,1', ascii(('BCD', 'B'))), + ('(?i)(ab|ab*)bc', 'ABC', '0,1', ascii(('ABC', 'A'))), + ('(?i)a([bc]*)c*', 'ABC', '0,1', ascii(('ABC', 'BC'))), + + ('(?i)a([bc]*)(c*d)', 'ABCD', '0,1,2', ascii(('ABCD', 'BC', 'D'))), + ('(?i)a([bc]+)(c*d)', 'ABCD', '0,1,2', ascii(('ABCD', 'BC', 'D'))), + ('(?i)a([bc]*)(c+d)', 'ABCD', '0,1,2', ascii(('ABCD', 'B', 'CD'))), + ('(?i)a[bcd]*dcdcde', 'ADCDCDE', '0', ascii('ADCDCDE')), + ('(?i)a[bcd]+dcdcde', 'ADCDCDE', '', ascii(None)), + ('(?i)(ab|a)b*c', 'ABC', '0,1', ascii(('ABC', 'AB'))), + ('(?i)((a)(b)c)(d)', 'ABCD', '1,2,3,4', ascii(('ABC', 'A', 'B', + 'D'))), + ('(?i)[a-zA-Z_][a-zA-Z0-9_]*', 'ALPHA', '0', ascii('ALPHA')), + ('(?i)^a(bc+|b[eh])g|.h$', 'ABH', '0,1', ascii(('BH', None))), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'EFFGZ', '0,1,2', ascii(('EFFGZ', + 'EFFGZ', None))), + + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'IJ', '0,1,2', ascii(('IJ', 'IJ', + 'J'))), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'EFFG', '', ascii(None)), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'BCDD', '', ascii(None)), + ('(?i)(bc+d$|ef*g.|h?i(j|k))', 'REFFGZ', '0,1,2', ascii(('EFFGZ', + 'EFFGZ', None))), + ('(?i)((((((((((a))))))))))', 'A', '10', ascii('A')), + ('(?i)((((((((((a))))))))))\\10', 'AA', '0', ascii('AA')), + #('(?i)((((((((((a))))))))))\\41', 'AA', '', ascii(None)), + #('(?i)((((((((((a))))))))))\\41', 'A!', '0', ascii('A!')), + ('(?i)(((((((((a)))))))))', 'A', '0', ascii('A')), + ('(?i)(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))', 'A', '1', + ascii('A')), + ('(?i)(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))', 'C', '1', + ascii('C')), + ('(?i)multiple words of text', 'UH-UH', '', ascii(None)), + + ('(?i)multiple words', 'MULTIPLE WORDS, YEAH', '0', + ascii('MULTIPLE WORDS')), + ('(?i)(.*)c(.*)', 'ABCDE', '0,1,2', ascii(('ABCDE', 'AB', 'DE'))), + ('(?i)\\((.*), (.*)\\)', '(A, B)', '2,1', ascii(('B', 'A'))), + ('(?i)[k]', 'AB', '', ascii(None)), + # ('(?i)abcd', 'ABCD', SUCCEED, 'found+"-"+\\found+"-"+\\\\found', ascii(ABCD-$&-\\ABCD)), + # ('(?i)a(bc)d', 'ABCD', SUCCEED, 'g1+"-"+\\g1+"-"+\\\\g1', ascii(BC-$1-\\BC)), + ('(?i)a[-]?c', 'AC', '0', ascii('AC')), + ('(?i)(abc)\\1', 'ABCABC', '1', ascii('ABC')), + ('(?i)([a-c]*)\\1', 'ABCABC', '1', ascii('ABC')), + ('a(?!b).', 'abad', '0', ascii('ad')), + ('a(?=d).', 'abad', '0', ascii('ad')), + ('a(?=c|d).', 'abad', '0', ascii('ad')), + + ('a(?:b|c|d)(.)', 'ace', '1', ascii('e')), + ('a(?:b|c|d)*(.)', 'ace', '1', ascii('e')), + ('a(?:b|c|d)+?(.)', 'ace', '1', ascii('e')), + ('a(?:b|(c|e){1,2}?|d)+?(.)', 'ace', '1,2', ascii(('c', 'e'))), + + # Lookbehind: split by : but not if it is escaped by -. + ('(?]*?b', 'a>b', '', ascii(None)), + # Bug 490573: minimizing repeat problem. + (r'^a*?$', 'foo', '', ascii(None)), + # Bug 470582: nested groups problem. + (r'^((a)c)?(ab)$', 'ab', '1,2,3', ascii((None, None, 'ab'))), + # Another minimizing repeat problem (capturing groups in assertions). + ('^([ab]*?)(?=(b)?)c', 'abc', '1,2', ascii(('ab', None))), + ('^([ab]*?)(?!(b))c', 'abc', '1,2', ascii(('ab', None))), + ('^([ab]*?)(?(.){0,2})d", "abcd").captures(1), + ['b', 'c']) + self.assertEqual(regex.search(r"(.)+", "a").captures(1), ['a']) + + def test_guards(self): + m = regex.search(r"(X.*?Y\s*){3}(X\s*)+AB:", + "XY\nX Y\nX Y\nXY\nXX AB:") + self.assertEqual(m.span(0, 1, 2), ((3, 21), (12, 15), (16, 18))) + + m = regex.search(r"(X.*?Y\s*){3,}(X\s*)+AB:", + "XY\nX Y\nX Y\nXY\nXX AB:") + self.assertEqual(m.span(0, 1, 2), ((0, 21), (12, 15), (16, 18))) + + m = regex.search(r'\d{4}(\s*\w)?\W*((?!\d)\w){2}', "9999XX") + self.assertEqual(m.span(0, 1, 2), ((0, 6), (-1, -1), (5, 6))) + + m = regex.search(r'A\s*?.*?(\n+.*?\s*?){0,2}\(X', 'A\n1\nS\n1 (X') + self.assertEqual(m.span(0, 1), ((0, 10), (5, 8))) + + m = regex.search(r'Derde\s*:', 'aaaaaa:\nDerde:') + self.assertEqual(m.span(), (8, 14)) + m = regex.search(r'Derde\s*:', 'aaaaa:\nDerde:') + self.assertEqual(m.span(), (7, 13)) + + def test_turkic(self): + # Turkish has dotted and dotless I/i. + pairs = "I=i;I=\u0131;i=\u0130" + + all_chars = set() + matching = set() + for pair in pairs.split(";"): + ch1, ch2 = pair.split("=") + all_chars.update((ch1, ch2)) + matching.add((ch1, ch1)) + matching.add((ch1, ch2)) + matching.add((ch2, ch1)) + matching.add((ch2, ch2)) + + for ch1 in all_chars: + for ch2 in all_chars: + m = regex.match(r"(?i)\A" + ch1 + r"\Z", ch2) + if m: + if (ch1, ch2) not in matching: + self.fail("{} matching {}".format(ascii(ch1), + ascii(ch2))) + else: + if (ch1, ch2) in matching: + self.fail("{} not matching {}".format(ascii(ch1), + ascii(ch2))) + + def test_named_lists(self): + options = ["one", "two", "three"] + self.assertEqual(regex.match(r"333\L444", "333one444", + bar=options).group(), "333one444") + self.assertEqual(regex.match(r"(?i)333\L444", "333TWO444", + bar=options).group(), "333TWO444") + self.assertEqual(regex.match(r"333\L444", "333four444", + bar=options), None) + + options = [b"one", b"two", b"three"] + self.assertEqual(regex.match(br"333\L444", b"333one444", + bar=options).group(), b"333one444") + self.assertEqual(regex.match(br"(?i)333\L444", b"333TWO444", + bar=options).group(), b"333TWO444") + self.assertEqual(regex.match(br"333\L444", b"333four444", + bar=options), None) + + self.assertEqual(repr(type(regex.compile(r"3\L4\L+5", + bar=["one", "two", "three"]))), self.PATTERN_CLASS) + + self.assertEqual(regex.findall(r"^\L", "solid QWERT", + options=set(['good', 'brilliant', '+s\\ol[i}d'])), []) + self.assertEqual(regex.findall(r"^\L", "+solid QWERT", + options=set(['good', 'brilliant', '+solid'])), ['+solid']) + + options = ["STRASSE"] + self.assertEqual(regex.match(r"(?fi)\L", + "stra\N{LATIN SMALL LETTER SHARP S}e", words=options).span(), (0, + 6)) + + options = ["STRASSE", "stress"] + self.assertEqual(regex.match(r"(?fi)\L", + "stra\N{LATIN SMALL LETTER SHARP S}e", words=options).span(), (0, + 6)) + + options = ["stra\N{LATIN SMALL LETTER SHARP S}e"] + self.assertEqual(regex.match(r"(?fi)\L", "STRASSE", + words=options).span(), (0, 7)) + + options = ["kit"] + self.assertEqual(regex.search(r"(?i)\L", "SKITS", + words=options).span(), (1, 4)) + self.assertEqual(regex.search(r"(?i)\L", + "SK\N{LATIN CAPITAL LETTER I WITH DOT ABOVE}TS", + words=options).span(), (1, 4)) + + self.assertEqual(regex.search(r"(?fi)\b(\w+) +\1\b", + " stra\N{LATIN SMALL LETTER SHARP S}e STRASSE ").span(), (1, 15)) + self.assertEqual(regex.search(r"(?fi)\b(\w+) +\1\b", + " STRASSE stra\N{LATIN SMALL LETTER SHARP S}e ").span(), (1, 15)) + + self.assertEqual(regex.search(r"^\L$", "", options=[]).span(), + (0, 0)) + + def test_fuzzy(self): + # Some tests borrowed from TRE library tests. + self.assertEqual(repr(type(regex.compile('(fou){s,e<=1}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(fuu){s}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(fuu){s,e}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){1i+1d<1,s<=1}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){1i+1d<1,s<=1,e<=10}'))), + self.PATTERN_CLASS) + self.assertEqual(repr(type(regex.compile('(anaconda){s<=1,e<=1,1i+1d<1}'))), + self.PATTERN_CLASS) + + text = 'molasses anaconda foo bar baz smith anderson ' + self.assertEqual(regex.search('(znacnda){s<=1,e<=3,1i+1d<1}', text), + None) + self.assertEqual(regex.search('(znacnda){s<=1,e<=3,1i+1d<2}', + text).span(0, 1), ((9, 17), (9, 17))) + self.assertEqual(regex.search('(ananda){1i+1d<2}', text), None) + self.assertEqual(regex.search(r"(?:\bznacnda){e<=2}", text)[0], + "anaconda") + self.assertEqual(regex.search(r"(?:\bnacnda){e<=2}", text)[0], + "anaconda") + + text = 'anaconda foo bar baz smith anderson' + self.assertEqual(regex.search('(fuu){i<=3,d<=3,e<=5}', text).span(0, + 1), ((0, 0), (0, 0))) + self.assertEqual(regex.search('(?b)(fuu){i<=3,d<=3,e<=5}', + text).span(0, 1), ((9, 10), (9, 10))) + self.assertEqual(regex.search('(fuu){i<=2,d<=2,e<=5}', text).span(0, + 1), ((7, 10), (7, 10))) + self.assertEqual(regex.search('(?e)(fuu){i<=2,d<=2,e<=5}', + text).span(0, 1), ((9, 10), (9, 10))) + self.assertEqual(regex.search('(fuu){i<=3,d<=3,e}', text).span(0, 1), + ((0, 0), (0, 0))) + self.assertEqual(regex.search('(?b)(fuu){i<=3,d<=3,e}', text).span(0, + 1), ((9, 10), (9, 10))) + + self.assertEqual(repr(type(regex.compile('(approximate){s<=3,1i+1d<3}'))), + self.PATTERN_CLASS) + + # No cost limit. + self.assertEqual(regex.search('(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((0, 6), (0, 6))) + self.assertEqual(regex.search('(?e)(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((0, 3), (0, 3))) + self.assertEqual(regex.search('(?b)(foobar){e}', + 'xirefoabralfobarxie').span(0, 1), ((11, 16), (11, 16))) + + # At most two errors. + self.assertEqual(regex.search('(foobar){e<=2}', + 'xirefoabrzlfd').span(0, 1), ((4, 9), (4, 9))) + self.assertEqual(regex.search('(foobar){e<=2}', 'xirefoabzlfd'), None) + + # At most two inserts or substitutions and max two errors total. + self.assertEqual(regex.search('(foobar){i<=2,s<=2,e<=2}', + 'oobargoobaploowap').span(0, 1), ((5, 11), (5, 11))) + + # Find best whole word match for "foobar". + self.assertEqual(regex.search('\\b(foobar){e}\\b', 'zfoobarz').span(0, + 1), ((0, 8), (0, 8))) + self.assertEqual(regex.search('\\b(foobar){e}\\b', + 'boing zfoobarz goobar woop').span(0, 1), ((0, 6), (0, 6))) + self.assertEqual(regex.search('(?b)\\b(foobar){e}\\b', + 'boing zfoobarz goobar woop').span(0, 1), ((15, 21), (15, 21))) + + # Match whole string, allow only 1 error. + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoobar').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobarx').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fooxbar').span(0, + 1), ((0, 7), (0, 7))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foxbar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xoobar').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobax').span(0, 1), + ((0, 6), (0, 6))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'oobar').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fobar').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'fooba').span(0, 1), + ((0, 5), (0, 5))) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoobarx'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foobarxx'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xxfoobar'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'xfoxbar'), None) + self.assertEqual(regex.search('^(foobar){e<=1}$', 'foxbarx'), None) + + # At most one insert, two deletes, and three substitutions. + # Additionally, deletes cost two and substitutes one, and total + # cost must be less than 4. + self.assertEqual(regex.search('(foobar){i<=1,d<=2,s<=3,2d+1s<4}', + '3oifaowefbaoraofuiebofasebfaobfaorfeoaro').span(0, 1), ((6, 13), (6, + 13))) + self.assertEqual(regex.search('(?b)(foobar){i<=1,d<=2,s<=3,2d+1s<4}', + '3oifaowefbaoraofuiebofasebfaobfaorfeoaro').span(0, 1), ((34, 39), + (34, 39))) + + # Partially fuzzy matches. + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'foobarzap').span(0, + 1), ((0, 9), (3, 6))) + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'fobarzap'), None) + self.assertEqual(regex.search('foo(bar){e<=1}zap', 'foobrzap').span(0, + 1), ((0, 8), (3, 5))) + + text = ('www.cnn.com 64.236.16.20\nwww.slashdot.org 66.35.250.150\n' + 'For useful information, use www.slashdot.org\nthis is demo data!\n') + self.assertEqual(regex.search(r'(?s)^.*(dot.org){e}.*$', text).span(0, + 1), ((0, 120), (120, 120))) + self.assertEqual(regex.search(r'(?es)^.*(dot.org){e}.*$', text).span(0, + 1), ((0, 120), (93, 100))) + self.assertEqual(regex.search(r'^.*(dot.org){e}.*$', text).span(0, 1), + ((0, 119), (24, 101))) + + # Behaviour is unexpected, but arguably not wrong. It first finds the + # best match, then the best in what follows, etc. + self.assertEqual(regex.findall(r"\b\L{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["cot", "dog"]) + self.assertEqual(regex.findall(r"\b\L{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), [" dog", "cot"]) + self.assertEqual(regex.findall(r"(?e)\b\L{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), ["dog", "cot"]) + self.assertEqual(regex.findall(r"(?r)\b\L{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["dog ", "cot"]) + self.assertEqual(regex.findall(r"(?er)\b\L{e<=1}\b", + " book cot dog desk ", words="cat dog".split()), ["dog", "cot"]) + self.assertEqual(regex.findall(r"(?r)\b\L{e<=1}\b", + " book dog cot desk ", words="cat dog".split()), ["cot", "dog"]) + self.assertEqual(regex.findall(br"\b\L{e<=1}\b", + b" book cot dog desk ", words=b"cat dog".split()), [b"cot", b"dog"]) + self.assertEqual(regex.findall(br"\b\L{e<=1}\b", + b" book dog cot desk ", words=b"cat dog".split()), [b" dog", b"cot"]) + self.assertEqual(regex.findall(br"(?e)\b\L{e<=1}\b", + b" book dog cot desk ", words=b"cat dog".split()), [b"dog", b"cot"]) + self.assertEqual(regex.findall(br"(?r)\b\L{e<=1}\b", + b" book cot dog desk ", words=b"cat dog".split()), [b"dog ", b"cot"]) + self.assertEqual(regex.findall(br"(?er)\b\L{e<=1}\b", + b" book cot dog desk ", words=b"cat dog".split()), [b"dog", b"cot"]) + self.assertEqual(regex.findall(br"(?r)\b\L{e<=1}\b", + b" book dog cot desk ", words=b"cat dog".split()), [b"cot", b"dog"]) + + self.assertEqual(regex.search(r"(\w+) (\1{e<=1})", "foo fou").groups(), + ("foo", "fou")) + self.assertEqual(regex.search(r"(?r)(\2{e<=1}) (\w+)", + "foo fou").groups(), ("foo", "fou")) + self.assertEqual(regex.search(br"(\w+) (\1{e<=1})", + b"foo fou").groups(), (b"foo", b"fou")) + + self.assertEqual(regex.findall(r"(?:(?:QR)+){e}", "abcde"), ["abcde", + ""]) + self.assertEqual(regex.findall(r"(?:Q+){e}", "abc"), ["abc", ""]) + + # Hg issue 41: = for fuzzy matches + self.assertEqual(regex.match(r"(?:service detection){0[^()]+)|(?R))*\)", "(ab(cd)ef)")[ + : ], ("(ab(cd)ef)", "ef")) + self.assertEqual(regex.search(r"\(((?>[^()]+)|(?R))*\)", + "(ab(cd)ef)").captures(1), ["ab", "cd", "(cd)", "ef"]) + + self.assertEqual(regex.search(r"(?r)\(((?R)|(?>[^()]+))*\)", + "(ab(cd)ef)")[ : ], ("(ab(cd)ef)", "ab")) + self.assertEqual(regex.search(r"(?r)\(((?R)|(?>[^()]+))*\)", + "(ab(cd)ef)").captures(1), ["ef", "cd", "(cd)", "ab"]) + + self.assertEqual(regex.search(r"\(([^()]+|(?R))*\)", + "some text (a(b(c)d)e) more text")[ : ], ("(a(b(c)d)e)", "e")) + + self.assertEqual(regex.search(r"(?r)\(((?R)|[^()]+)*\)", + "some text (a(b(c)d)e) more text")[ : ], ("(a(b(c)d)e)", "a")) + + self.assertEqual(regex.search(r"(foo(\(((?:(?>[^()]+)|(?2))*)\)))", + "foo(bar(baz)+baz(bop))")[ : ], ("foo(bar(baz)+baz(bop))", + "foo(bar(baz)+baz(bop))", "(bar(baz)+baz(bop))", + "bar(baz)+baz(bop)")) + + self.assertEqual(regex.search(r"(?r)(foo(\(((?:(?2)|(?>[^()]+))*)\)))", + "foo(bar(baz)+baz(bop))")[ : ], ("foo(bar(baz)+baz(bop))", + "foo(bar(baz)+baz(bop))", "(bar(baz)+baz(bop))", + "bar(baz)+baz(bop)")) + + rgx = regex.compile(r"""^\s*(<\s*([a-zA-Z:]+)(?:\s*[a-zA-Z:]*\s*=\s*(?:'[^']*'|"[^"]*"))*\s*(/\s*)?>(?:[^<>]*|(?1))*(?(3)|<\s*/\s*\2\s*>))\s*$""") + self.assertEqual(bool(rgx.search('')), True) + self.assertEqual(bool(rgx.search('')), False) + self.assertEqual(bool(rgx.search('')), True) + self.assertEqual(bool(rgx.search('')), False) + self.assertEqual(bool(rgx.search('')), False) + + self.assertEqual(bool(rgx.search('')), False) + self.assertEqual(bool(rgx.search('')), True) + self.assertEqual(bool(rgx.search('< fooo / >')), True) + # The next regex should and does match. Perl 5.14 agrees. + #self.assertEqual(bool(rgx.search('foo')), False) + self.assertEqual(bool(rgx.search('foo')), False) + + self.assertEqual(bool(rgx.search('foo')), True) + self.assertEqual(bool(rgx.search('foo')), True) + self.assertEqual(bool(rgx.search('')), True) + + def test_copy(self): + # PatternObjects are immutable, therefore there's no need to clone them. + r = regex.compile("a") + self.assertTrue(copy.copy(r) is r) + self.assertTrue(copy.deepcopy(r) is r) + + # MatchObjects are normally mutable because the target string can be + # detached. However, after the target string has been detached, a + # MatchObject becomes immutable, so there's no need to clone it. + m = r.match("a") + self.assertTrue(copy.copy(m) is not m) + self.assertTrue(copy.deepcopy(m) is not m) + + self.assertTrue(m.string is not None) + m2 = copy.copy(m) + m2.detach_string() + self.assertTrue(m.string is not None) + self.assertTrue(m2.string is None) + + # The following behaviour matches that of the re module. + it = regex.finditer(".", "ab") + it2 = copy.copy(it) + self.assertEqual(next(it).group(), "a") + self.assertEqual(next(it2).group(), "b") + + # The following behaviour matches that of the re module. + it = regex.finditer(".", "ab") + it2 = copy.deepcopy(it) + self.assertEqual(next(it).group(), "a") + self.assertEqual(next(it2).group(), "b") + + # The following behaviour is designed to match that of copying 'finditer'. + it = regex.splititer(" ", "a b") + it2 = copy.copy(it) + self.assertEqual(next(it), "a") + self.assertEqual(next(it2), "b") + + # The following behaviour is designed to match that of copying 'finditer'. + it = regex.splititer(" ", "a b") + it2 = copy.deepcopy(it) + self.assertEqual(next(it), "a") + self.assertEqual(next(it2), "b") + + def test_format(self): + self.assertEqual(regex.subf(r"(\w+) (\w+)", "{0} => {2} {1}", + "foo bar"), "foo bar => bar foo") + self.assertEqual(regex.subf(r"(?\w+) (?\w+)", + "{word2} {word1}", "foo bar"), "bar foo") + + self.assertEqual(regex.subfn(r"(\w+) (\w+)", "{0} => {2} {1}", + "foo bar"), ("foo bar => bar foo", 1)) + self.assertEqual(regex.subfn(r"(?\w+) (?\w+)", + "{word2} {word1}", "foo bar"), ("bar foo", 1)) + + self.assertEqual(regex.match(r"(\w+) (\w+)", + "foo bar").expandf("{0} => {2} {1}"), "foo bar => bar foo") + + def test_fullmatch(self): + self.assertEqual(bool(regex.fullmatch(r"abc", "abc")), True) + self.assertEqual(bool(regex.fullmatch(r"abc", "abcx")), False) + self.assertEqual(bool(regex.fullmatch(r"abc", "abcx", endpos=3)), True) + + self.assertEqual(bool(regex.fullmatch(r"abc", "xabc", pos=1)), True) + self.assertEqual(bool(regex.fullmatch(r"abc", "xabcy", pos=1)), False) + self.assertEqual(bool(regex.fullmatch(r"abc", "xabcy", pos=1, + endpos=4)), True) + + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abc")), True) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abcx")), False) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "abcx", endpos=3)), + True) + + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabc", pos=1)), + True) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabcy", pos=1)), + False) + self.assertEqual(bool(regex.fullmatch(r"(?r)abc", "xabcy", pos=1, + endpos=4)), True) + + def test_issue_18468(self): + self.assertTypedEqual(regex.sub('y', 'a', 'xyz'), 'xaz') + self.assertTypedEqual(regex.sub('y', StrSubclass('a'), + StrSubclass('xyz')), 'xaz') + self.assertTypedEqual(regex.sub(b'y', b'a', b'xyz'), b'xaz') + self.assertTypedEqual(regex.sub(b'y', BytesSubclass(b'a'), + BytesSubclass(b'xyz')), b'xaz') + self.assertTypedEqual(regex.sub(b'y', bytearray(b'a'), + bytearray(b'xyz')), b'xaz') + self.assertTypedEqual(regex.sub(b'y', memoryview(b'a'), + memoryview(b'xyz')), b'xaz') + + for string in ":a:b::c", StrSubclass(":a:b::c"): + self.assertTypedEqual(regex.split(":", string), ['', 'a', 'b', '', + 'c']) + if sys.version_info >= (3, 7, 0): + self.assertTypedEqual(regex.split(":*", string), ['', '', 'a', + '', 'b', '', 'c', '']) + self.assertTypedEqual(regex.split("(:*)", string), ['', ':', + '', '', 'a', ':', '', '', 'b', '::', '', '', 'c', '', '']) + else: + self.assertTypedEqual(regex.split(":*", string), ['', 'a', 'b', + 'c']) + self.assertTypedEqual(regex.split("(:*)", string), ['', ':', + 'a', ':', 'b', '::', 'c']) + + for string in (b":a:b::c", BytesSubclass(b":a:b::c"), + bytearray(b":a:b::c"), memoryview(b":a:b::c")): + self.assertTypedEqual(regex.split(b":", string), [b'', b'a', b'b', + b'', b'c']) + if sys.version_info >= (3, 7, 0): + self.assertTypedEqual(regex.split(b":*", string), [b'', b'', + b'a', b'', b'b', b'', b'c', b'']) + self.assertTypedEqual(regex.split(b"(:*)", string), [b'', b':', + b'', b'', b'a', b':', b'', b'', b'b', b'::', b'', b'', b'c', + b'', b'']) + else: + self.assertTypedEqual(regex.split(b":*", string), [b'', b'a', + b'b', b'c']) + self.assertTypedEqual(regex.split(b"(:*)", string), [b'', b':', + b'a', b':', b'b', b'::', b'c']) + + for string in "a:b::c:::d", StrSubclass("a:b::c:::d"): + self.assertTypedEqual(regex.findall(":+", string), [":", "::", + ":::"]) + self.assertTypedEqual(regex.findall("(:+)", string), [":", "::", + ":::"]) + self.assertTypedEqual(regex.findall("(:)(:*)", string), [(":", ""), + (":", ":"), (":", "::")]) + + for string in (b"a:b::c:::d", BytesSubclass(b"a:b::c:::d"), + bytearray(b"a:b::c:::d"), memoryview(b"a:b::c:::d")): + self.assertTypedEqual(regex.findall(b":+", string), [b":", b"::", + b":::"]) + self.assertTypedEqual(regex.findall(b"(:+)", string), [b":", b"::", + b":::"]) + self.assertTypedEqual(regex.findall(b"(:)(:*)", string), [(b":", + b""), (b":", b":"), (b":", b"::")]) + + for string in 'a', StrSubclass('a'): + self.assertEqual(regex.match('a', string).groups(), ()) + self.assertEqual(regex.match('(a)', string).groups(), ('a',)) + self.assertEqual(regex.match('(a)', string).group(0), 'a') + self.assertEqual(regex.match('(a)', string).group(1), 'a') + self.assertEqual(regex.match('(a)', string).group(1, 1), ('a', + 'a')) + + for string in (b'a', BytesSubclass(b'a'), bytearray(b'a'), + memoryview(b'a')): + self.assertEqual(regex.match(b'a', string).groups(), ()) + self.assertEqual(regex.match(b'(a)', string).groups(), (b'a',)) + self.assertEqual(regex.match(b'(a)', string).group(0), b'a') + self.assertEqual(regex.match(b'(a)', string).group(1), b'a') + self.assertEqual(regex.match(b'(a)', string).group(1, 1), (b'a', + b'a')) + + def test_partial(self): + self.assertEqual(regex.match('ab', 'a', partial=True).partial, True) + self.assertEqual(regex.match('ab', 'a', partial=True).span(), (0, 1)) + self.assertEqual(regex.match(r'cats', 'cat', partial=True).partial, + True) + self.assertEqual(regex.match(r'cats', 'cat', partial=True).span(), (0, + 3)) + self.assertEqual(regex.match(r'cats', 'catch', partial=True), None) + self.assertEqual(regex.match(r'abc\w{3}', 'abcdef', + partial=True).partial, False) + self.assertEqual(regex.match(r'abc\w{3}', 'abcdef', + partial=True).span(), (0, 6)) + self.assertEqual(regex.match(r'abc\w{3}', 'abcde', + partial=True).partial, True) + self.assertEqual(regex.match(r'abc\w{3}', 'abcde', + partial=True).span(), (0, 5)) + + self.assertEqual(regex.match(r'\d{4}$', '1234', partial=True).partial, + False) + + self.assertEqual(regex.match(r'\L', 'post', partial=True, + words=['post']).partial, False) + self.assertEqual(regex.match(r'\L', 'post', partial=True, + words=['post']).span(), (0, 4)) + self.assertEqual(regex.match(r'\L', 'pos', partial=True, + words=['post']).partial, True) + self.assertEqual(regex.match(r'\L', 'pos', partial=True, + words=['post']).span(), (0, 3)) + + self.assertEqual(regex.match(r'(?fi)\L', 'POST', partial=True, + words=['po\uFB06']).partial, False) + self.assertEqual(regex.match(r'(?fi)\L', 'POST', partial=True, + words=['po\uFB06']).span(), (0, 4)) + self.assertEqual(regex.match(r'(?fi)\L', 'POS', partial=True, + words=['po\uFB06']).partial, True) + self.assertEqual(regex.match(r'(?fi)\L', 'POS', partial=True, + words=['po\uFB06']).span(), (0, 3)) + self.assertEqual(regex.match(r'(?fi)\L', 'po\uFB06', + partial=True, words=['POS']), None) + + self.assertEqual(regex.match(r'[a-z]*4R$', 'a', partial=True).span(), + (0, 1)) + self.assertEqual(regex.match(r'[a-z]*4R$', 'ab', partial=True).span(), + (0, 2)) + self.assertEqual(regex.match(r'[a-z]*4R$', 'ab4', partial=True).span(), + (0, 3)) + self.assertEqual(regex.match(r'[a-z]*4R$', 'a4', partial=True).span(), + (0, 2)) + self.assertEqual(regex.match(r'[a-z]*4R$', 'a4R', partial=True).span(), + (0, 3)) + self.assertEqual(regex.match(r'[a-z]*4R$', '4a', partial=True), None) + self.assertEqual(regex.match(r'[a-z]*4R$', 'a44', partial=True), None) + + def test_hg_bugs(self): + # Hg issue 28: regex.compile("(?>b)") causes "TypeError: 'Character' + # object is not subscriptable" + self.assertEqual(bool(regex.compile("(?>b)", flags=regex.V1)), True) + + # Hg issue 29: regex.compile("^((?>\w+)|(?>\s+))*$") causes + # "TypeError: 'GreedyRepeat' object is not iterable" + self.assertEqual(bool(regex.compile(r"^((?>\w+)|(?>\s+))*$", + flags=regex.V1)), True) + + # Hg issue 31: atomic and normal groups in recursive patterns + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(bcd(e)f)g(h)"), ['(bcd(e)f)', '(h)']) + self.assertEqual(regex.findall(r"\((?:(?:[^()]+)|(?R))*\)", + "a(bcd(e)f)g(h)"), ['(bcd(e)f)', '(h)']) + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(b(cd)e)f)g)h"), ['(b(cd)e)']) + self.assertEqual(regex.findall(r"\((?:(?>[^()]+)|(?R))*\)", + "a(bc(d(e)f)gh"), ['(d(e)f)']) + self.assertEqual(regex.findall(r"(?r)\((?:(?>[^()]+)|(?R))*\)", + "a(bc(d(e)f)gh"), ['(d(e)f)']) + self.assertEqual([m.group() for m in + regex.finditer(r"\((?:[^()]*+|(?0))*\)", "a(b(c(de)fg)h")], + ['(c(de)fg)']) + + # Hg issue 32: regex.search("a(bc)d", "abcd", regex.I|regex.V1) returns + # None + self.assertEqual(regex.search("a(bc)d", "abcd", regex.I | + regex.V1).group(0), "abcd") + + # Hg issue 33: regex.search("([\da-f:]+)$", "E", regex.I|regex.V1) + # returns None + self.assertEqual(regex.search(r"([\da-f:]+)$", "E", regex.I | + regex.V1).group(0), "E") + self.assertEqual(regex.search(r"([\da-f:]+)$", "e", regex.I | + regex.V1).group(0), "e") + + # Hg issue 34: regex.search("^(?=ab(de))(abd)(e)", "abde").groups() + # returns (None, 'abd', 'e') instead of ('de', 'abd', 'e') + self.assertEqual(regex.search("^(?=ab(de))(abd)(e)", "abde").groups(), + ('de', 'abd', 'e')) + + # Hg issue 35: regex.compile("\ ", regex.X) causes "_regex_core.error: + # bad escape" + self.assertEqual(bool(regex.match(r"\ ", " ", flags=regex.X)), True) + + # Hg issue 36: regex.search("^(a|)\1{2}b", "b") returns None + self.assertEqual(regex.search(r"^(a|)\1{2}b", "b").group(0, 1), ('b', + '')) + + # Hg issue 37: regex.search("^(a){0,0}", "abc").group(0,1) returns + # ('a', 'a') instead of ('', None) + self.assertEqual(regex.search("^(a){0,0}", "abc").group(0, 1), ('', + None)) + + # Hg issue 38: regex.search("(?>.*/)b", "a/b") returns None + self.assertEqual(regex.search("(?>.*/)b", "a/b").group(0), "a/b") + + # Hg issue 39: regex.search("((?i)blah)\\s+\\1", "blah BLAH") doesn't + # return None + # Changed to positional flags in regex 2023.12.23. + self.assertEqual(regex.search(r"((?i)blah)\s+\1", "blah BLAH"), None) + + # Hg issue 40: regex.search("(\()?[^()]+(?(1)\)|)", "(abcd").group(0) + # returns "bcd" instead of "abcd" + self.assertEqual(regex.search(r"(\()?[^()]+(?(1)\)|)", + "(abcd").group(0), "abcd") + + # Hg issue 42: regex.search("(a*)*", "a", flags=regex.V1).span(1) + # returns (0, 1) instead of (1, 1) + self.assertEqual(regex.search("(a*)*", "a").span(1), (1, 1)) + self.assertEqual(regex.search("(a*)*", "aa").span(1), (2, 2)) + self.assertEqual(regex.search("(a*)*", "aaa").span(1), (3, 3)) + + # Hg issue 43: regex.compile("a(?#xxx)*") causes "_regex_core.error: + # nothing to repeat" + self.assertEqual(regex.search("a(?#xxx)*", "aaa").group(), "aaa") + + # Hg issue 44: regex.compile("(?=abc){3}abc") causes + # "_regex_core.error: nothing to repeat" + self.assertEqual(regex.search("(?=abc){3}abc", "abcabcabc").span(), (0, + 3)) + + # Hg issue 45: regex.compile("^(?:a(?:(?:))+)+") causes + # "_regex_core.error: nothing to repeat" + self.assertEqual(regex.search("^(?:a(?:(?:))+)+", "a").span(), (0, 1)) + self.assertEqual(regex.search("^(?:a(?:(?:))+)+", "aa").span(), (0, 2)) + + # Hg issue 46: regex.compile("a(?x: b c )d") causes + # "_regex_core.error: missing )" + self.assertEqual(regex.search("a(?x: b c )d", "abcd").group(0), "abcd") + + # Hg issue 47: regex.compile("a#comment\n*", flags=regex.X) causes + # "_regex_core.error: nothing to repeat" + self.assertEqual(regex.search("a#comment\n*", "aaa", + flags=regex.X).group(0), "aaa") + + # Hg issue 48: regex.search("(a(?(1)\\1)){4}", "a"*10, + # flags=regex.V1).group(0,1) returns ('aaaaa', 'a') instead of ('aaaaaaaaaa', 'aaaa') + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){1}", + "aaaaaaaaaa").span(0, 1), ((0, 1), (0, 1))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){2}", + "aaaaaaaaaa").span(0, 1), ((0, 3), (1, 3))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){3}", + "aaaaaaaaaa").span(0, 1), ((0, 6), (3, 6))) + self.assertEqual(regex.search(r"(?V1)(a(?(1)\1)){4}", + "aaaaaaaaaa").span(0, 1), ((0, 10), (6, 10))) + + # Hg issue 49: regex.search("(a)(?<=b(?1))", "baz", regex.V1) returns + # None incorrectly + self.assertEqual(regex.search("(?V1)(a)(?<=b(?1))", "baz").group(0), + "a") + + # Hg issue 50: not all keywords are found by named list with + # overlapping keywords when full Unicode casefolding is required + self.assertEqual(regex.findall(r'(?fi)\L', + 'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05', + keywords=['post','pos']), ['POST', 'Post', 'post', 'po\u017Ft', + 'po\uFB06', 'po\uFB05']) + self.assertEqual(regex.findall(r'(?fi)pos|post', + 'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), ['POS', + 'Pos', 'pos', 'po\u017F', 'po\uFB06', 'po\uFB05']) + self.assertEqual(regex.findall(r'(?fi)post|pos', + 'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), ['POST', + 'Post', 'post', 'po\u017Ft', 'po\uFB06', 'po\uFB05']) + self.assertEqual(regex.findall(r'(?fi)post|another', + 'POST, Post, post, po\u017Ft, po\uFB06, and po\uFB05'), ['POST', + 'Post', 'post', 'po\u017Ft', 'po\uFB06', 'po\uFB05']) + + # Hg issue 51: regex.search("((a)(?1)|(?2))", "a", flags=regex.V1) + # returns None incorrectly + self.assertEqual(regex.search("(?V1)((a)(?1)|(?2))", "a").group(0, 1, + 2), ('a', 'a', None)) + + # Hg issue 52: regex.search("(\\1xx|){6}", "xx", + # flags=regex.V1).span(0,1) returns incorrect value + self.assertEqual(regex.search(r"(?V1)(\1xx|){6}", "xx").span(0, 1), + ((0, 2), (2, 2))) + + # Hg issue 53: regex.search("(a|)+", "a") causes MemoryError + self.assertEqual(regex.search("(a|)+", "a").group(0, 1), ("a", "")) + + # Hg issue 54: regex.search("(a|)*\\d", "a"*80) causes MemoryError + self.assertEqual(regex.search(r"(a|)*\d", "a" * 80), None) + + # Hg issue 55: regex.search("^(?:a?b?)*$", "ac") take a very long time. + self.assertEqual(regex.search("^(?:a?b?)*$", "ac"), None) + + # Hg issue 58: bad named character escape sequences like "\\N{1}" + # treats as "N" + self.assertRaisesRegex(regex.error, self.UNDEF_CHAR_NAME, lambda: + regex.compile("\\N{1}")) + + # Hg issue 59: regex.search("\\Z", "a\na\n") returns None incorrectly + self.assertEqual(regex.search("\\Z", "a\na\n").span(0), (4, 4)) + + # Hg issue 60: regex.search("(q1|.)*(q2|.)*(x(a|bc)*y){2,}", "xayxay") + # returns None incorrectly + self.assertEqual(regex.search("(q1|.)*(q2|.)*(x(a|bc)*y){2,}", + "xayxay").group(0), "xayxay") + + # Hg issue 61: regex.search("[^a]", "A", regex.I).group(0) returns '' + # incorrectly + self.assertEqual(regex.search("(?i)[^a]", "A"), None) + + # Hg issue 63: regex.search("[[:ascii:]]", "\N{KELVIN SIGN}", + # flags=regex.I|regex.V1) doesn't return None + self.assertEqual(regex.search("(?i)[[:ascii:]]", "\N{KELVIN SIGN}"), + None) + + # Hg issue 66: regex.search("((a|b(?1)c){3,5})", "baaaaca", + # flags=regex.V1).groups() returns ('baaaac', 'baaaac') instead of ('aaaa', 'a') + self.assertEqual(regex.search("((a|b(?1)c){3,5})", "baaaaca").group(0, + 1, 2), ('aaaa', 'aaaa', 'a')) + + # Hg issue 71: non-greedy quantifier in lookbehind + self.assertEqual(regex.findall(r"(?<=:\S+ )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S* )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S+? )\w+", ":9 abc :10 def"), + ['abc', 'def']) + self.assertEqual(regex.findall(r"(?<=:\S*? )\w+", ":9 abc :10 def"), + ['abc', 'def']) + + # Hg issue 73: conditional patterns + self.assertEqual(regex.search(r"(?:fe)?male", "female").group(), + "female") + self.assertEqual([m.group() for m in + regex.finditer(r"(fe)?male: h(?(1)(er)|(is)) (\w+)", + "female: her dog; male: his cat. asdsasda")], ['female: her dog', + 'male: his cat']) + + # Hg issue 78: "Captures" doesn't work for recursive calls + self.assertEqual(regex.search(r'(?\((?:[^()]++|(?&rec))*\))', + 'aaa(((1+0)+1)+1)bbb').captures('rec'), ['(1+0)', '((1+0)+1)', + '(((1+0)+1)+1)']) + + # Hg issue 80: Escape characters throws an exception + self.assertRaisesRegex(regex.error, self.BAD_ESCAPE, lambda: + regex.sub('x', '\\', 'x'), ) + + # Hg issue 82: error range does not work + fz = "(CAGCCTCCCATTTCAGAATATACATCC){1a(?b))', "ab").spans("x"), [(1, + 2), (0, 2)]) + + # Hg issue 91: match.expand is extremely slow + # Check that the replacement cache works. + self.assertEqual(regex.sub(r'(-)', lambda m: m.expand(r'x'), 'a-b-c'), + 'axbxc') + + # Hg issue 94: Python crashes when executing regex updates + # pattern.findall + rx = regex.compile(r'\bt(est){i<2}', flags=regex.V1) + self.assertEqual(rx.search("Some text"), None) + self.assertEqual(rx.findall("Some text"), []) + + # Hg issue 95: 'pos' for regex.error + self.assertRaisesRegex(regex.error, self.MULTIPLE_REPEAT, lambda: + regex.compile(r'.???')) + + # Hg issue 97: behaviour of regex.escape's special_only is wrong + # + # Hg issue 244: Make `special_only=True` the default in + # `regex.escape()` + self.assertEqual(regex.escape('foo!?', special_only=False), 'foo\\!\\?') + self.assertEqual(regex.escape('foo!?', special_only=True), 'foo!\\?') + self.assertEqual(regex.escape('foo!?'), 'foo!\\?') + + self.assertEqual(regex.escape(b'foo!?', special_only=False), b'foo\\!\\?') + self.assertEqual(regex.escape(b'foo!?', special_only=True), + b'foo!\\?') + self.assertEqual(regex.escape(b'foo!?'), b'foo!\\?') + + # Hg issue 100: strange results from regex.search + self.assertEqual(regex.search('^([^z]*(?:WWWi|W))?$', + 'WWWi').groups(), ('WWWi', )) + self.assertEqual(regex.search('^([^z]*(?:WWWi|w))?$', + 'WWWi').groups(), ('WWWi', )) + self.assertEqual(regex.search('^([^z]*?(?:WWWi|W))?$', + 'WWWi').groups(), ('WWWi', )) + + # Hg issue 101: findall() broken (seems like memory corruption) + pat = regex.compile(r'xxx', flags=regex.FULLCASE | regex.UNICODE) + self.assertEqual([x.group() for x in pat.finditer('yxxx')], ['xxx']) + self.assertEqual(pat.findall('yxxx'), ['xxx']) + + raw = 'yxxx' + self.assertEqual([x.group() for x in pat.finditer(raw)], ['xxx']) + self.assertEqual(pat.findall(raw), ['xxx']) + + pat = regex.compile(r'xxx', flags=regex.FULLCASE | regex.IGNORECASE | + regex.UNICODE) + self.assertEqual([x.group() for x in pat.finditer('yxxx')], ['xxx']) + self.assertEqual(pat.findall('yxxx'), ['xxx']) + + raw = 'yxxx' + self.assertEqual([x.group() for x in pat.finditer(raw)], ['xxx']) + self.assertEqual(pat.findall(raw), ['xxx']) + + # Hg issue 106: * operator not working correctly with sub() + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.sub('(?V0).*', 'x', 'test'), 'xx') + else: + self.assertEqual(regex.sub('(?V0).*', 'x', 'test'), 'x') + self.assertEqual(regex.sub('(?V1).*', 'x', 'test'), 'xx') + + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.sub('(?V0).*?', '|', 'test'), '|||||||||') + else: + self.assertEqual(regex.sub('(?V0).*?', '|', 'test'), '|t|e|s|t|') + self.assertEqual(regex.sub('(?V1).*?', '|', 'test'), '|||||||||') + + # Hg issue 112: re: OK, but regex: SystemError + self.assertEqual(regex.sub(r'^(@)\n(?!.*?@)(.*)', + r'\1\n==========\n\2', '@\n', flags=regex.DOTALL), '@\n==========\n') + + # Hg issue 109: Edit distance of fuzzy match + self.assertEqual(regex.match(r'(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats|cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + + self.assertEqual(regex.match(r'(?:cat){e<=1}', 'caz').fuzzy_counts, + (1, 0, 0)) + self.assertEqual(regex.match(r'(?e)(?:cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match(r'(?b)(?:cat){e<=1}', + 'caz').fuzzy_counts, (1, 0, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=2}', 'c ats').fuzzy_counts, + (1, 1, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=2}', + 'c ats').fuzzy_counts, (0, 1, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=2}', + 'c ats').fuzzy_counts, (0, 1, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=2}', + 'c a ts').fuzzy_counts, (0, 2, 0)) + + self.assertEqual(regex.match(r'(?:cats){e<=1}', 'c ats').fuzzy_counts, + (0, 1, 0)) + self.assertEqual(regex.match(r'(?e)(?:cats){e<=1}', + 'c ats').fuzzy_counts, (0, 1, 0)) + self.assertEqual(regex.match(r'(?b)(?:cats){e<=1}', + 'c ats').fuzzy_counts, (0, 1, 0)) + + # Hg issue 115: Infinite loop when processing backreferences + self.assertEqual(regex.findall(r'\bof ([a-z]+) of \1\b', + 'To make use of one of these modules'), []) + + # Hg issue 125: Reference to entire match (\g<0>) in + # Pattern.sub() doesn't work as of 2014.09.22 release. + self.assertEqual(regex.sub(r'x', r'\g<0>', 'x'), 'x') + + # Unreported issue: no such builtin as 'ascii' in Python 2. + self.assertEqual(bool(regex.match(r'a', 'a', regex.DEBUG)), True) + + # Hg issue 131: nested sets behaviour + self.assertEqual(regex.findall(r'(?V1)[[b-e]--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[b-e--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[[bcde]--cd]', 'abcdef'), ['b', + 'e']) + self.assertEqual(regex.findall(r'(?V1)[bcde--cd]', 'abcdef'), ['b', + 'e']) + + # Hg issue 132: index out of range on null property \p{} + self.assertRaisesRegex(regex.error, '^unknown property at position 4$', + lambda: regex.compile(r'\p{}')) + + # Issue 23692. + self.assertEqual(regex.match('(?:()|(?(1)()|z)){2}(?(2)a|z)', + 'a').group(0, 1, 2), ('a', '', '')) + self.assertEqual(regex.match('(?:()|(?(1)()|z)){0,2}(?(2)a|z)', + 'a').group(0, 1, 2), ('a', '', '')) + + # Hg issue 137: Posix character class :punct: does not seem to be + # supported. + + # Posix compatibility as recommended here: + # http://www.unicode.org/reports/tr18/#Compatibility_Properties + + # Posix in Unicode. + chars = ''.join(chr(c) for c in range(0x10000)) + + self.assertEqual(ascii(''.join(regex.findall(r'''[[:alnum:]]+''', + chars))), ascii(''.join(regex.findall(r'''[\p{Alpha}\p{PosixDigit}]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:alpha:]]+''', + chars))), ascii(''.join(regex.findall(r'''\p{Alpha}+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:ascii:]]+''', + chars))), ascii(''.join(regex.findall(r'''[\p{InBasicLatin}]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:blank:]]+''', + chars))), ascii(''.join(regex.findall(r'''[\p{gc=Space_Separator}\t]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:cntrl:]]+''', + chars))), ascii(''.join(regex.findall(r'''\p{gc=Control}+''', chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:digit:]]+''', + chars))), ascii(''.join(regex.findall(r'''[0-9]+''', chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:graph:]]+''', + chars))), ascii(''.join(regex.findall(r'''[^\p{Space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:lower:]]+''', + chars))), ascii(''.join(regex.findall(r'''\p{Lower}+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:print:]]+''', + chars))), ascii(''.join(regex.findall(r'''(?V1)[\p{Graph}\p{Blank}--\p{Cntrl}]+''', chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:punct:]]+''', + chars))), + ascii(''.join(regex.findall(r'''(?V1)[\p{gc=Punctuation}\p{gc=Symbol}--\p{Alpha}]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:space:]]+''', + chars))), ascii(''.join(regex.findall(r'''\p{Whitespace}+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:upper:]]+''', + chars))), ascii(''.join(regex.findall(r'''\p{Upper}+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:word:]]+''', + chars))), ascii(''.join(regex.findall(r'''[\p{Alpha}\p{gc=Mark}\p{Digit}\p{gc=Connector_Punctuation}\p{Join_Control}]+''', + chars)))) + self.assertEqual(ascii(''.join(regex.findall(r'''[[:xdigit:]]+''', + chars))), ascii(''.join(regex.findall(r'''[0-9A-Fa-f]+''', + chars)))) + + # Posix in ASCII. + chars = bytes(range(0x100)) + + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:alnum:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[\p{Alpha}\p{PosixDigit}]+''', + chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:alpha:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)\p{Alpha}+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:ascii:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[\x00-\x7F]+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:blank:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[\p{gc=Space_Separator}\t]+''', + chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:cntrl:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)\p{gc=Control}+''', + chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:digit:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[0-9]+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:graph:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[^\p{Space}\p{gc=Control}\p{gc=Surrogate}\p{gc=Unassigned}]+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:lower:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)\p{Lower}+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:print:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?aV1)[\p{Graph}\p{Blank}--\p{Cntrl}]+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:punct:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?aV1)[\p{gc=Punctuation}\p{gc=Symbol}--\p{Alpha}]+''', + chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:space:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)\p{Whitespace}+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:upper:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)\p{Upper}+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:word:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[\p{Alpha}\p{gc=Mark}\p{Digit}\p{gc=Connector_Punctuation}\p{Join_Control}]+''', chars)))) + self.assertEqual(ascii(b''.join(regex.findall(br'''(?a)[[:xdigit:]]+''', + chars))), ascii(b''.join(regex.findall(br'''(?a)[0-9A-Fa-f]+''', chars)))) + + # Hg issue 138: grapheme anchored search not working properly. + self.assertEqual(ascii(regex.search(r'\X$', 'ab\u2103').group()), + ascii('\u2103')) + + # Hg issue 139: Regular expression with multiple wildcards where first + # should match empty string does not always work. + self.assertEqual(regex.search("([^L]*)([^R]*R)", "LtR").groups(), ('', + 'LtR')) + + # Hg issue 140: Replace with REVERSE and groups has unexpected + # behavior. + self.assertEqual(regex.sub(r'(.)', r'x\1y', 'ab'), 'xayxby') + self.assertEqual(regex.sub(r'(?r)(.)', r'x\1y', 'ab'), 'xayxby') + self.assertEqual(regex.subf(r'(.)', 'x{1}y', 'ab'), 'xayxby') + self.assertEqual(regex.subf(r'(?r)(.)', 'x{1}y', 'ab'), 'xayxby') + + # Hg issue 141: Crash on a certain partial match. + self.assertEqual(regex.fullmatch('(a)*abc', 'ab', + partial=True).span(), (0, 2)) + self.assertEqual(regex.fullmatch('(a)*abc', 'ab', + partial=True).partial, True) + + # Hg issue 143: Partial matches have incorrect span if prefix is '.' + # wildcard. + self.assertEqual(regex.search('OXRG', 'OOGOX', partial=True).span(), + (3, 5)) + self.assertEqual(regex.search('.XRG', 'OOGOX', partial=True).span(), + (3, 5)) + self.assertEqual(regex.search('.{1,3}XRG', 'OOGOX', + partial=True).span(), (1, 5)) + + # Hg issue 144: Latest version problem with matching 'R|R'. + self.assertEqual(regex.match('R|R', 'R').span(), (0, 1)) + + # Hg issue 146: Forced-fail (?!) works improperly in conditional. + self.assertEqual(regex.match(r'(.)(?(1)(?!))', 'xy'), None) + + # Groups cleared after failure. + self.assertEqual(regex.findall(r'(y)?(\d)(?(1)\b\B)', 'ax1y2z3b'), + [('', '1'), ('', '2'), ('', '3')]) + self.assertEqual(regex.findall(r'(y)?+(\d)(?(1)\b\B)', 'ax1y2z3b'), + [('', '1'), ('', '2'), ('', '3')]) + + # Hg issue 147: Fuzzy match can return match points beyond buffer end. + self.assertEqual([m.span() for m in regex.finditer(r'(?i)(?:error){e}', + 'regex failure')], [(0, 5), (5, 10), (10, 13), (13, 13)]) + self.assertEqual([m.span() for m in + regex.finditer(r'(?fi)(?:error){e}', 'regex failure')], [(0, 5), (5, + 10), (10, 13), (13, 13)]) + + # Hg issue 150: Have an option for POSIX-compatible longest match of + # alternates. + self.assertEqual(regex.search(r'(?p)\d+(\w(\d*)?|[eE]([+-]\d+))', + '10b12')[0], '10b12') + self.assertEqual(regex.search(r'(?p)\d+(\w(\d*)?|[eE]([+-]\d+))', + '10E+12')[0], '10E+12') + + self.assertEqual(regex.search(r'(?p)(\w|ae|oe|ue|ss)', 'ae')[0], 'ae') + self.assertEqual(regex.search(r'(?p)one(self)?(selfsufficient)?', + 'oneselfsufficient')[0], 'oneselfsufficient') + + # Hg issue 151: Request: \K. + self.assertEqual(regex.search(r'(ab\Kcd)', 'abcd').group(0, 1), ('cd', + 'abcd')) + self.assertEqual(regex.findall(r'\w\w\K\w\w', 'abcdefgh'), ['cd', + 'gh']) + self.assertEqual(regex.findall(r'(\w\w\K\w\w)', 'abcdefgh'), ['abcd', + 'efgh']) + + self.assertEqual(regex.search(r'(?r)(ab\Kcd)', 'abcd').group(0, 1), + ('ab', 'abcd')) + self.assertEqual(regex.findall(r'(?r)\w\w\K\w\w', 'abcdefgh'), ['ef', + 'ab']) + self.assertEqual(regex.findall(r'(?r)(\w\w\K\w\w)', 'abcdefgh'), + ['efgh', 'abcd']) + + # Hg issue 152: Request: Request: (?(DEFINE)...). + self.assertEqual(regex.search(r'(?(DEFINE)(?\d+)(?\w+))(?&quant) (?&item)', + '5 elephants')[0], '5 elephants') + + self.assertEqual(regex.search(r'(?&routine)(?(DEFINE)(?.))', 'a').group('routine'), None) + self.assertEqual(regex.search(r'(?&routine)(?(DEFINE)(?.))', 'a').captures('routine'), ['a']) + + # Hg issue 153: Request: (*SKIP). + self.assertEqual(regex.search(r'12(*FAIL)|3', '123')[0], '3') + self.assertEqual(regex.search(r'(?r)12(*FAIL)|3', '123')[0], '3') + + self.assertEqual(regex.search(r'\d+(*PRUNE)\d', '123'), None) + self.assertEqual(regex.search(r'\d+(?=(*PRUNE))\d', '123')[0], '123') + self.assertEqual(regex.search(r'\d+(*PRUNE)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'\d+(*PRUNE)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d+?(*PRUNE)bcd|[3d]', '123bcd')[0], + '3bcd') + self.assertEqual(regex.search(r'\d+?(*PRUNE)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d++(?<=3(*PRUNE))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'\d++(?<=3(*PRUNE))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=(*PRUNE)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=2(*PRUNE)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'(?r)\d(*PRUNE)\d+', '123'), None) + self.assertEqual(regex.search(r'(?r)\d(?<=(*PRUNE))\d+', '123')[0], + '123') + self.assertEqual(regex.search(r'(?r)\d+(*PRUNE)bcd|[3d]', + '123bcd')[0], '123bcd') + self.assertEqual(regex.search(r'(?r)\d+(*PRUNE)bcd|[3d]', + '123zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*PRUNE))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*PRUNE))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=(*PRUNE)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=2(*PRUNE)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'\d+(*SKIP)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'\d+(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d+?(*SKIP)bcd|[3d]', '123bcd')[0], + '3bcd') + self.assertEqual(regex.search(r'\d+?(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'\d++(?<=3(*SKIP))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'\d++(?<=3(*SKIP))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=(*SKIP)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'\d++(?<=2(*SKIP)3)zzd|[3d]$', + '124zzd')[0], 'd') + + self.assertEqual(regex.search(r'(?r)\d+(*SKIP)bcd|[3d]', '123bcd')[0], + '123bcd') + self.assertEqual(regex.search(r'(?r)\d+(*SKIP)bcd|[3d]', '123zzd')[0], + 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*SKIP))zzd|[4d]$', + '123zzd')[0], '123zzd') + self.assertEqual(regex.search(r'(?r)\d++(?<=3(*SKIP))zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=(*SKIP)3)zzd|[4d]$', + '124zzd')[0], 'd') + self.assertEqual(regex.search(r'(?r)\d++(?<=2(*SKIP)3)zzd|[3d]$', + '124zzd')[0], 'd') + + # Hg issue 154: Segmentation fault 11 when working with an atomic group + text = """June 30, December 31, 2013 2012 +some words follow: +more words and numbers 1,234,567 9,876,542 +more words and numbers 1,234,567 9,876,542""" + self.assertEqual(len(regex.findall(r'(?2014|2013 ?2012)', text)), 1) + + # Hg issue 156: regression on atomic grouping + self.assertEqual(regex.match('1(?>2)', '12').span(), (0, 2)) + + # Hg issue 157: regression: segfault on complex lookaround + self.assertEqual(regex.match(r'(?V1w)(?=(?=[^A-Z]*+[A-Z])(?=[^a-z]*+[a-z]))(?=\D*+\d)(?=\p{Alphanumeric}*+\P{Alphanumeric})\A(?s:.){8,255}+\Z', + 'AAaa11!!')[0], 'AAaa11!!') + + # Hg issue 158: Group issue with (?(DEFINE)...) + TEST_REGEX = regex.compile(r'''(?smx) +(?(DEFINE) + (? + ^,[^,]+, + ) +) + +# Group 2 is defined on this line +^,([^,]+), + +(?:(?!(?&subcat)[\r\n]+(?&subcat)).)+ +''') + + TEST_DATA = ''' +,Cat 1, +,Brand 1, +some +thing +,Brand 2, +other +things +,Cat 2, +,Brand, +Some +thing +''' + + self.assertEqual([m.span(1, 2) for m in + TEST_REGEX.finditer(TEST_DATA)], [((-1, -1), (2, 7)), ((-1, -1), (54, + 59))]) + + # Hg issue 161: Unexpected fuzzy match results + self.assertEqual(regex.search('(abcdefgh){e}', + '******abcdefghijklmnopqrtuvwxyz', regex.BESTMATCH).span(), (6, 14)) + self.assertEqual(regex.search('(abcdefghi){e}', + '******abcdefghijklmnopqrtuvwxyz', regex.BESTMATCH).span(), (6, 15)) + + # Hg issue 163: allow lookarounds in conditionals. + self.assertEqual(regex.match(r'(?:(?=\d)\d+\b|\w+)', '123abc').span(), + (0, 6)) + self.assertEqual(regex.match(r'(?(?=\d)\d+\b|\w+)', '123abc'), None) + self.assertEqual(regex.search(r'(?(?<=love\s)you|(?<=hate\s)her)', + "I love you").span(), (7, 10)) + self.assertEqual(regex.findall(r'(?(?<=love\s)you|(?<=hate\s)her)', + "I love you but I don't hate her either"), ['you', 'her']) + + # Hg issue 180: bug of POSIX matching. + self.assertEqual(regex.search(r'(?p)a*(.*?)', 'aaabbb').group(0, 1), + ('aaabbb', 'bbb')) + self.assertEqual(regex.search(r'(?p)a*(.*)', 'aaabbb').group(0, 1), + ('aaabbb', 'bbb')) + self.assertEqual(regex.sub(r'(?p)a*(.*?)', r'\1', 'aaabbb'), 'bbb') + self.assertEqual(regex.sub(r'(?p)a*(.*)', r'\1', 'aaabbb'), 'bbb') + + # Hg issue 192: Named lists reverse matching doesn't work with + # IGNORECASE and V1 + self.assertEqual(regex.match(r'(?irV0)\L', '21', kw=['1']).span(), + (1, 2)) + self.assertEqual(regex.match(r'(?irV1)\L', '21', kw=['1']).span(), + (1, 2)) + + # Hg issue 193: Alternation and .REVERSE flag. + self.assertEqual(regex.search('a|b', '111a222').span(), (3, 4)) + self.assertEqual(regex.search('(?r)a|b', '111a222').span(), (3, 4)) + + # Hg issue 194: .FULLCASE and Backreference + self.assertEqual(regex.search(r'(?if)<(CLI)><\1>', + '').span(), (0, 10)) + self.assertEqual(regex.search(r'(?if)<(CLI)><\1>', + '').span(), (0, 10)) + self.assertEqual(regex.search(r'(?ifr)<\1><(CLI)>', + '').span(), (0, 10)) + + # Hg issue 195: Pickle (or otherwise serial) the compiled regex + r = regex.compile(r'\L', options=['foo', 'bar']) + p = pickle.dumps(r) + r = pickle.loads(p) + self.assertEqual(r.match('foo').span(), (0, 3)) + + # Hg issue 196: Fuzzy matching on repeated regex not working as + # expected + self.assertEqual(regex.match('(x{6}){e<=1}', 'xxxxxx', + flags=regex.BESTMATCH).span(), (0, 6)) + self.assertEqual(regex.match('(x{6}){e<=1}', 'xxxxx', + flags=regex.BESTMATCH).span(), (0, 5)) + self.assertEqual(regex.match('(x{6}){e<=1}', 'x', + flags=regex.BESTMATCH), None) + self.assertEqual(regex.match('(?r)(x{6}){e<=1}', 'xxxxxx', + flags=regex.BESTMATCH).span(), (0, 6)) + self.assertEqual(regex.match('(?r)(x{6}){e<=1}', 'xxxxx', + flags=regex.BESTMATCH).span(), (0, 5)) + self.assertEqual(regex.match('(?r)(x{6}){e<=1}', 'x', + flags=regex.BESTMATCH), None) + + # Hg issue 197: ValueError in regex.compile + self.assertRaises(regex.error, lambda: + regex.compile(b'00000\\0\\00\\^\50\\00\\U05000000')) + + # Hg issue 198: ValueError in regex.compile + self.assertRaises(regex.error, lambda: regex.compile(b"{e', '22', aa=['121', + '22'])), True) + self.assertEqual(bool(regex.search(r'(?ri)\L', '22', aa=['121', + '22'])), True) + self.assertEqual(bool(regex.search(r'(?fi)\L', '22', aa=['121', + '22'])), True) + self.assertEqual(bool(regex.search(r'(?fri)\L', '22', aa=['121', + '22'])), True) + + # Hg issue 208: Named list, (?ri) flags, Backreference + self.assertEqual(regex.search(r'(?r)\1dog..(?<=(\L))$', 'ccdogcc', + aa=['bcb', 'cc']). span(), (0, 7)) + self.assertEqual(regex.search(r'(?ir)\1dog..(?<=(\L))$', + 'ccdogcc', aa=['bcb', 'cc']). span(), (0, 7)) + + # Hg issue 210: Fuzzy matching and Backreference + self.assertEqual(regex.search(r'(2)(?:\1{5}){e<=1}', + '3222212').span(), (1, 7)) + self.assertEqual(regex.search(r'(\d)(?:\1{5}){e<=1}', + '3222212').span(), (1, 7)) + + # Hg issue 211: Segmentation fault with recursive matches and atomic + # groups + self.assertEqual(regex.match(r'''\A(?P(?>\((?&whole)\)|[+\-]))\Z''', + '((-))').span(), (0, 5)) + self.assertEqual(regex.match(r'''\A(?P(?>\((?&whole)\)|[+\-]))\Z''', + '((-)+)'), None) + + # Hg issue 212: Unexpected matching difference with .*? between re and + # regex + self.assertEqual(regex.match(r"x.*? (.).*\1(.*)\1", + 'x |y| z|').span(), (0, 9)) + self.assertEqual(regex.match(r"\.sr (.*?) (.)(.*)\2(.*)\2(.*)", + r'.sr h |||').span(), (0, 35)) + + # Hg issue 213: Segmentation Fault + a = '"\\xF9\\x80\\xAEqdz\\x95L\\xA7\\x89[\\xFE \\x91)\\xF9]\\xDB\'\\x99\\x09=\\x00\\xFD\\x98\\x22\\xDD\\xF1\\xB6\\xC3 Z\\xB6gv\\xA5x\\x93P\\xE1r\\x14\\x8Cv\\x0C\\xC0w\\x15r\\xFFc%" ' + py_regex_pattern = r'''(?P((?>(?"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``)))) (?P((?>(?"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))))''' + self.assertEqual(bool(regex.search(py_regex_pattern, a)), False) + + # Hg Issue 216: Invalid match when using negative lookbehind and pipe + self.assertEqual(bool(regex.match('foo(?<=foo)', 'foo')), True) + self.assertEqual(bool(regex.match('foo(?.*\!\w*\:.*)|(?P.*))', + '!')), False) + + # Hg issue 220: Misbehavior of group capture with OR operand + self.assertEqual(regex.match(r'\w*(ea)\w*|\w*e(?!a)\w*', + 'easier').groups(), ('ea', )) + + # Hg issue 225: BESTMATCH in fuzzy match not working + self.assertEqual(regex.search('(^1234$){i,d}', '12234', + regex.BESTMATCH).span(), (0, 5)) + self.assertEqual(regex.search('(^1234$){i,d}', '12234', + regex.BESTMATCH).fuzzy_counts, (0, 1, 0)) + + self.assertEqual(regex.search('(^1234$){s,i,d}', '12234', + regex.BESTMATCH).span(), (0, 5)) + self.assertEqual(regex.search('(^1234$){s,i,d}', '12234', + regex.BESTMATCH).fuzzy_counts, (0, 1, 0)) + + # Hg issue 226: Error matching at start of string + self.assertEqual(regex.search('(^123$){s,i,d}', 'xxxxxxxx123', + regex.BESTMATCH).span(), (0, 11)) + self.assertEqual(regex.search('(^123$){s,i,d}', 'xxxxxxxx123', + regex.BESTMATCH).fuzzy_counts, (0, 8, 0)) + + # Hg issue 227: Incorrect behavior for ? operator with UNICODE + + # IGNORECASE + self.assertEqual(regex.search(r'a?yz', 'xxxxyz', flags=regex.FULLCASE | + regex.IGNORECASE).span(), (4, 6)) + + # Hg issue 230: Is it a bug of (?(DEFINE)...) + self.assertEqual(regex.findall(r'(?:(?![a-d]).)+', 'abcdefgh'), + ['efgh']) + self.assertEqual(regex.findall(r'''(?(DEFINE)(?P(?:(?![a-d]).)))(?&mydef)+''', + 'abcdefgh'), ['efgh']) + + # Hg issue 238: Not fully re backward compatible + self.assertEqual(regex.findall(r'((\w{1,3})(\.{2,10})){1,3}', + '"Erm....yes. T..T...Thank you for that."'), [('Erm....', 'Erm', + '....'), ('T...', 'T', '...')]) + self.assertEqual(regex.findall(r'((\w{1,3})(\.{2,10})){3}', + '"Erm....yes. T..T...Thank you for that."'), []) + self.assertEqual(regex.findall(r'((\w{1,3})(\.{2,10})){2}', + '"Erm....yes. T..T...Thank you for that."'), [('T...', 'T', '...')]) + self.assertEqual(regex.findall(r'((\w{1,3})(\.{2,10})){1}', + '"Erm....yes. T..T...Thank you for that."'), [('Erm....', 'Erm', + '....'), ('T..', 'T', '..'), ('T...', 'T', '...')]) + + # Hg issue 247: Unexpected result with fuzzy matching and lookahead + # expression + self.assertEqual(regex.search(r'(?:ESTONIA(?!\w)){e<=1}', + 'ESTONIAN WORKERS').group(), 'ESTONIAN') + self.assertEqual(regex.search(r'(?:ESTONIA(?=\W)){e<=1}', + 'ESTONIAN WORKERS').group(), 'ESTONIAN') + + self.assertEqual(regex.search(r'(?:(?.))(?&func)', + 'abc').groups(), (None, )) + self.assertEqual(regex.search(r'(?(DEFINE)(?.))(?&func)', + 'abc').groupdict(), {'func': None}) + self.assertEqual(regex.search(r'(?(DEFINE)(?.))(?&func)', + 'abc').capturesdict(), {'func': ['a']}) + + self.assertEqual(regex.search(r'(?(DEFINE)(?.))(?=(?&func))', + 'abc').groups(), (None, )) + self.assertEqual(regex.search(r'(?(DEFINE)(?.))(?=(?&func))', + 'abc').groupdict(), {'func': None}) + self.assertEqual(regex.search(r'(?(DEFINE)(?.))(?=(?&func))', + 'abc').capturesdict(), {'func': ['a']}) + + self.assertEqual(regex.search(r'(?(DEFINE)(?.)).(?<=(?&func))', + 'abc').groups(), (None, )) + self.assertEqual(regex.search(r'(?(DEFINE)(?.)).(?<=(?&func))', + 'abc').groupdict(), {'func': None}) + self.assertEqual(regex.search(r'(?(DEFINE)(?.)).(?<=(?&func))', + 'abc').capturesdict(), {'func': ['a']}) + + # Hg issue 271: Comment logic different between Re and Regex + self.assertEqual(bool(regex.match(r'ab(?#comment\))cd', 'abcd')), True) + + # Hg issue 276: Partial Matches yield incorrect matches and bounds + self.assertEqual(regex.search(r'[a-z]+ [a-z]*?:', 'foo bar', + partial=True).span(), (0, 7)) + self.assertEqual(regex.search(r'(?r):[a-z]*? [a-z]+', 'foo bar', + partial=True).span(), (0, 7)) + + # Hg issue 291: Include Script Extensions as a supported Unicode property + self.assertEqual(bool(regex.match(r'(?u)\p{Script:Beng}', + '\u09EF')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{Script:Bengali}', + '\u09EF')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{Script_Extensions:Bengali}', + '\u09EF')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{Script_Extensions:Beng}', + '\u09EF')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{Script_Extensions:Cakm}', + '\u09EF')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{Script_Extensions:Sylo}', + '\u09EF')), True) + + # Hg issue #293: scx (Script Extensions) property currently matches + # incorrectly + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Latin}', 'P')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Ahom}', 'P')), False) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Common}', '4')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Caucasian_Albanian}', '4')), + False) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Arabic}', '\u062A')), True) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Balinese}', '\u062A')), + False) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Devanagari}', '\u091C')), + True) + self.assertEqual(bool(regex.match(r'(?u)\p{scx:Batak}', '\u091C')), False) + + # Hg issue 296: Group references are not taken into account when group is reporting the last match + self.assertEqual(regex.fullmatch('(?P.)*(?&x)', 'abc').captures('x'), + ['a', 'b', 'c']) + self.assertEqual(regex.fullmatch('(?P.)*(?&x)', 'abc').group('x'), + 'b') + + self.assertEqual(regex.fullmatch('(?P.)(?P.)(?P.)', + 'abc').captures('x'), ['a', 'b', 'c']) + self.assertEqual(regex.fullmatch('(?P.)(?P.)(?P.)', + 'abc').group('x'), 'c') + + # Hg issue 299: Partial gives misleading results with "open ended" regexp + self.assertEqual(regex.match('(?:ab)*', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)*', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)*?', '', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)*+', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)*+', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)+', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)+', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)+?', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)++', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?:ab)++', 'abab', partial=True).partial, + False) + + self.assertEqual(regex.match('(?r)(?:ab)*', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)*', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)*?', '', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)*+', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)*+', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)+', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)+', 'abab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)+?', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)++', 'ab', partial=True).partial, + False) + self.assertEqual(regex.match('(?r)(?:ab)++', 'abab', partial=True).partial, + False) + + self.assertEqual(regex.match('a*', '', partial=True).partial, False) + self.assertEqual(regex.match('a*?', '', partial=True).partial, False) + self.assertEqual(regex.match('a*+', '', partial=True).partial, False) + self.assertEqual(regex.match('a+', '', partial=True).partial, True) + self.assertEqual(regex.match('a+?', '', partial=True).partial, True) + self.assertEqual(regex.match('a++', '', partial=True).partial, True) + self.assertEqual(regex.match('a+', 'a', partial=True).partial, False) + self.assertEqual(regex.match('a+?', 'a', partial=True).partial, False) + self.assertEqual(regex.match('a++', 'a', partial=True).partial, False) + + self.assertEqual(regex.match('(?r)a*', '', partial=True).partial, False) + self.assertEqual(regex.match('(?r)a*?', '', partial=True).partial, False) + self.assertEqual(regex.match('(?r)a*+', '', partial=True).partial, False) + self.assertEqual(regex.match('(?r)a+', '', partial=True).partial, True) + self.assertEqual(regex.match('(?r)a+?', '', partial=True).partial, True) + self.assertEqual(regex.match('(?r)a++', '', partial=True).partial, True) + self.assertEqual(regex.match('(?r)a+', 'a', partial=True).partial, False) + self.assertEqual(regex.match('(?r)a+?', 'a', partial=True).partial, False) + self.assertEqual(regex.match('(?r)a++', 'a', partial=True).partial, False) + + self.assertEqual(regex.match(r"(?:\s*\w+'*)+", 'whatever', partial=True).partial, + False) + + # Hg issue 300: segmentation fault + pattern = ('(?PGGCGTCACACTTTGCTATGCCATAGCAT[AG]TTTATCCATAAGA' + 'TTAGCGGATCCTACCTGACGCTTTTTATCGCAACTCTCTACTGTTTCTCCATAACAGAACATATTGA' + 'CTATCCGGTATTACCCGGCATGACAGGAGTAAAA){e<=1}' + '(?P[ACGT]{1059}){e<=2}' + '(?PTAATCGTCTTGTTTGATACACAAGGGTCGCATCTGCGGCCCTTTTGCTTTTTTAAG' + 'TTGTAAGGATATGCCATTCTAGA){e<=0}' + '(?P[ACGT]{18}){e<=0}' + '(?PAGATCGG[CT]AGAGCGTCGTGTAGGGAAAGAGTGTGG){e<=1}') + + text = ('GCACGGCGTCACACTTTGCTATGCCATAGCATATTTATCCATAAGATTAGCGGATCCTACC' + 'TGACGCTTTTTATCGCAACTCTCTACTGTTTCTCCATAACAGAACATATTGACTATCCGGTATTACC' + 'CGGCATGACAGGAGTAAAAATGGCTATCGACGAAAACAAACAGAAAGCGTTGGCGGCAGCACTGGGC' + 'CAGATTGAGAAACAATTTGGTAAAGGCTCCATCATGCGCCTGGGTGAAGACCGTTCCATGGATGTGG' + 'AAACCATCTCTACCGGTTCGCTTTCACTGGATATCGCGCTTGGGGCAGGTGGTCTGCCGATGGGCCG' + 'TATCGTCGAAATCTACGGACCGGAATCTTCCGGTAAAACCACGCTGACGCTGCAGGTGATCGCCGCA' + 'GCGCAGCGTGAAGGTAAAACCTGTGCGTTTATCGATGCTGAACACGCGCTGGACCCAATCTACGCAC' + 'GTAAACTGGGCGTCGATATCGACAACCTGCTGTGCTCCCAGCCGGACACCGGCGAGCAGGCACTGGA' + 'AATCTGTGACGCCCTGGCGCGTTCTGGCGCAGTAGACGTTATCGTCGTTGACTCCGTGGCGGCACTG' + 'ACGCCGAAAGCGGAAATCGAAGGCGAAATCGGCGACTCTCATATGGGCCTTGCGGCACGTATGATGA' + 'GCCAGGCGATGCGTAAGCTGGCGGGTAACCTGAAGCAGTCCAACACGCTGCTGATCTTCATCAACCC' + 'CATCCGTATGAAAATTGGTGTGATGTTCGGCAACCCGGAAACCACTTACCGGTGGTAACGCGCTGAA' + 'ATTCTACGCCTCTGTTCGTCTCGACATCCGTTAAATCGGCGCGGTGAAAGAGGGCGAAAACGTGGTG' + 'GGTAGCGAAACCCGCGTGAAAGTGGTGAAGAACAAAATCGCTGCGCCGTTTAAACAGGCTGAATTCC' + 'AGATCCTCTACGGCGAAGGTATCAACTTCTACCCCGAACTGGTTGACCTGGGCGTAAAAGAGAAGCT' + 'GATCGAGAAAGCAGGCGCGTGGTACAGCTACAAAGGTGAGAAGATCGGTCAGGGTAAAGCGAATGCG' + 'ACTGCCTGGCTGAAATTTAACCCGGAAACCGCGAAAGAGATCGAGTGAAAAGTACGTGAGTTGCTGC' + 'TGAGCAACCCGAACTCAACGCCGGATTTCTCTGTAGATGATAGCGAAGGCGTAGCAGAAACTAACGA' + 'AGATTTTTAATCGTCTTGTTTGATACACAAGGGTCGCATCTGCGGCCCTTTTGCTTTTTTAAGTTGT' + 'AAGGATATGCCATTCTAGACAGTTAACACACCAACAAAGATCGGTAGAGCGTCGTGTAGGGAAAGAG' + 'TGTGGTACC') + + m = regex.search(pattern, text, flags=regex.BESTMATCH) + self.assertEqual(m.fuzzy_counts, (0, 1, 0)) + self.assertEqual(m.fuzzy_changes, ([], [1206], [])) + + # Hg issue 306: Fuzzy match parameters not respecting quantifier scope + self.assertEqual(regex.search(r'(?e)(dogf(((oo){e<1})|((00){e<1}))d){e<2}', + 'dogfood').fuzzy_counts, (0, 0, 0)) + self.assertEqual(regex.search(r'(?e)(dogf(((oo){e<1})|((00){e<1}))d){e<2}', + 'dogfoot').fuzzy_counts, (1, 0, 0)) + + # Hg issue 312: \X not matching graphemes with zero-width-joins + self.assertEqual(regex.findall(r'\X', + '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'), + ['\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466']) + + # Hg issue 320: Abnormal performance + self.assertEqual(bool(regex.search(r'(?=a)a', 'a')), True) + self.assertEqual(bool(regex.search(r'(?!b)a', 'a')), True) + + # Hg issue 327: .fullmatch() causes MemoryError + self.assertEqual(regex.fullmatch(r'((\d)*?)*?', '123').span(), (0, 3)) + + # Hg issue 329: Wrong group matches when question mark quantifier is used within a look behind + self.assertEqual(regex.search(r'''(?(DEFINE)(?(?THIS_SHOULD_NOT_MATCHx?)|(?right))).*(?<=(?&mydef).*)''', + 'x right').capturesdict(), {'mydef': ['right'], 'wrong': [], 'right': + ['right']}) + + # Hg issue 338: specifying allowed characters when fuzzy-matching + self.assertEqual(bool(regex.match(r'(?:cat){e<=1:[u]}', 'cut')), True) + self.assertEqual(bool(regex.match(r'(?:cat){e<=1:u}', 'cut')), True) + + # Hg issue 353: fuzzy changes negative indexes + self.assertEqual(regex.search(r'(?be)(AGTGTTCCCCGCGCCAGCGGGGATAAACCG){s<=5,i<=5,d<=5,s+i+d<=10}', + 'TTCCCCGCGCCAGCGGGGATAAACCG').fuzzy_changes, ([], [], [0, 1, 3, 5])) + + # Git issue 364: Contradictory values in fuzzy_counts and fuzzy_changes + self.assertEqual(regex.match(r'(?:bc){e}', 'c').fuzzy_counts, (1, 0, + 1)) + self.assertEqual(regex.match(r'(?:bc){e}', 'c').fuzzy_changes, ([0], + [], [1])) + self.assertEqual(regex.match(r'(?e)(?:bc){e}', 'c').fuzzy_counts, (0, + 0, 1)) + self.assertEqual(regex.match(r'(?e)(?:bc){e}', 'c').fuzzy_changes, + ([], [], [0])) + self.assertEqual(regex.match(r'(?b)(?:bc){e}', 'c').fuzzy_counts, (0, + 0, 1)) + self.assertEqual(regex.match(r'(?b)(?:bc){e}', 'c').fuzzy_changes, + ([], [], [0])) + + # Git issue 370: Confusions about Fuzzy matching behavior + self.assertEqual(regex.match('(?e)(?:^(\\$ )?\\d{1,3}(,\\d{3})*(\\.\\d{2})$){e}', + '$ 10,112.111.12').fuzzy_counts, (6, 0, 5)) + self.assertEqual(regex.match('(?e)(?:^(\\$ )?\\d{1,3}(,\\d{3})*(\\.\\d{2})$){s<=1}', + '$ 10,112.111.12').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match('(?e)(?:^(\\$ )?\\d{1,3}(,\\d{3})*(\\.\\d{2})$){s<=1,i<=1,d<=1}', + '$ 10,112.111.12').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.match('(?e)(?:^(\\$ )?\\d{1,3}(,\\d{3})*(\\.\\d{2})$){s<=3}', + '$ 10,1a2.111.12').fuzzy_counts, (2, 0, 0)) + self.assertEqual(regex.match('(?e)(?:^(\\$ )?\\d{1,3}(,\\d{3})*(\\.\\d{2})$){s<=2}', + '$ 10,1a2.111.12').fuzzy_counts, (2, 0, 0)) + + self.assertEqual(regex.fullmatch(r'(?e)(?:0?,0(?:,0)?){s<=1,d<=1}', + ',0;0').fuzzy_counts, (1, 0, 0)) + self.assertEqual(regex.fullmatch(r'(?e)(?:0??,0(?:,0)?){s<=1,d<=1}', + ',0;0').fuzzy_counts, (1, 0, 0)) + + # Git issue 371: Specifying character set when fuzzy-matching allows characters not in the set + self.assertEqual(regex.search(r"\b(?e)(?:\d{6,20}){i<=5:[\-\\\/]}\b", + "cat dog starting at 00:01132.000. hello world"), None) + + # Git issue 385: Comments in expressions + self.assertEqual(bool(regex.compile('(?#)')), True) + self.assertEqual(bool(regex.compile('(?x)(?#)')), True) + + # Git issue 394: Unexpected behaviour in fuzzy matching with limited character set with IGNORECASE flag + self.assertEqual(regex.findall(r'(\d+){i<=2:[ab]}', '123X4Y5'), + ['123', '4', '5']) + self.assertEqual(regex.findall(r'(?i)(\d+){i<=2:[ab]}', '123X4Y5'), + ['123', '4', '5']) + + # Git issue 403: Fuzzy matching with wrong distance (unnecessary substitutions) + self.assertEqual(regex.match(r'^(test){e<=5}$', 'terstin', + flags=regex.B).fuzzy_counts, (0, 3, 0)) + + # Git issue 408: regex fails with a quantified backreference but succeeds with repeated backref + self.assertEqual(bool(regex.match(r"(?:(x*)\1\1\1)*x$", "x" * 5)), True) + self.assertEqual(bool(regex.match(r"(?:(x*)\1{3})*x$", "x" * 5)), True) + + # Git issue 415: Fuzzy character restrictions don't apply to insertions at "right edge" + self.assertEqual(regex.match(r't(?:es){s<=1:\d}t', 'te5t').group(), + 'te5t') + self.assertEqual(regex.match(r't(?:es){s<=1:\d}t', 'tezt'), None) + self.assertEqual(regex.match(r't(?:es){i<=1:\d}t', 'tes5t').group(), + 'tes5t') + self.assertEqual(regex.match(r't(?:es){i<=1:\d}t', 'teszt'), None) + self.assertEqual(regex.match(r't(?:es){i<=1:\d}t', + 'tes5t').fuzzy_changes, ([], [3], [])) + self.assertEqual(regex.match(r't(es){i<=1,0.*)(?PCTTCC){e<=1}(?P([ACGT]){4,6})(?PCAATACCGACTCCTCACTGTGT){e<=2}(?P([ACGT]){0,6}$)' + + m = regex.match(pattern, sequence, flags=regex.BESTMATCH) + self.assertEqual(m.span(), (0, 50)) + self.assertEqual(m.groupdict(), {'insert': 'TTCAGACGTGTGCT', 'anchor': 'CTTCC', 'umi': 'GATCT', 'sid': 'CAATACCGACTCCTCACTGTGT', 'end': 'GTCT'}) + + m = regex.match(pattern, sequence, flags=regex.ENHANCEMATCH) + self.assertEqual(m.span(), (0, 50)) + self.assertEqual(m.groupdict(), {'insert': 'TTCAGACGTGTGCT', 'anchor': 'CTTCC', 'umi': 'GATCT', 'sid': 'CAATACCGACTCCTCACTGTGT', 'end': 'GTCT'}) + + # Git issue 433: Disagreement between fuzzy_counts and fuzzy_changes + pattern = r'(?P.*)(?PAACACTGG){e<=1}(?P([AT][CG]){5}){e<=2}(?PGTAACCGAAG){e<=2}(?P([ACGT]){0,6}$)' + + sequence = 'GGAAAACACTGGTCTCAGTCTCGTAACCGAAGTGGTCG' + m = regex.match(pattern, sequence, flags=regex.BESTMATCH) + self.assertEqual(m.fuzzy_counts, (0, 0, 0)) + self.assertEqual(m.fuzzy_changes, ([], [], [])) + + sequence = 'GGAAAACACTGGTCTCAGTCTCGTCCCCGAAGTGGTCG' + m = regex.match(pattern, sequence, flags=regex.BESTMATCH) + self.assertEqual(m.fuzzy_counts, (2, 0, 0)) + self.assertEqual(m.fuzzy_changes, ([24, 25], [], [])) + + # Git issue 439: Unmatched groups: sub vs subf + self.assertEqual(regex.sub(r'(test1)|(test2)', r'matched: \1\2', 'test1'), 'matched: test1') + self.assertEqual(regex.subf(r'(test1)|(test2)', r'matched: {1}{2}', 'test1'), 'matched: test1') + self.assertEqual(regex.search(r'(test1)|(test2)', 'matched: test1').expand(r'matched: \1\2'), 'matched: test1'), + self.assertEqual(regex.search(r'(test1)|(test2)', 'matched: test1').expandf(r'matched: {1}{2}'), 'matched: test1') + + # Git issue 442: Fuzzy regex matching doesn't seem to test insertions correctly + self.assertEqual(regex.search(r"(?:\bha\b){i:[ ]}", "having"), None) + self.assertEqual(regex.search(r"(?:\bha\b){i:[ ]}", "having", flags=regex.I), None) + + # Git issue 467: Scoped inline flags 'a', 'u' and 'L' affect global flags + self.assertEqual(regex.match(r'(?a:\w)\w', 'd\N{CYRILLIC SMALL LETTER ZHE}').span(), (0, 2)) + self.assertEqual(regex.match(r'(?a:\w)(?u:\w)', 'd\N{CYRILLIC SMALL LETTER ZHE}').span(), (0, 2)) + + # Git issue 473: Emoji classified as letter + self.assertEqual(regex.match(r'^\p{LC}+$', '\N{SMILING CAT FACE WITH OPEN MOUTH}'), None) + self.assertEqual(regex.match(r'^\p{So}+$', '\N{SMILING CAT FACE WITH OPEN MOUTH}').span(), (0, 1)) + + # Git issue 474: regex has no equivalent to `re.Match.groups()` for captures + self.assertEqual(regex.match(r'(.)+', 'abc').allcaptures(), (['abc'], ['a', 'b', 'c'])) + self.assertEqual(regex.match(r'(.)+', 'abc').allspans(), ([(0, 3)], [(0, 1), (1, 2), (2, 3)])) + + # Git issue 477: \v for vertical spacing + self.assertEqual(bool(regex.fullmatch(r'\p{HorizSpace}+', '\t \xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000')), True) + self.assertEqual(bool(regex.fullmatch(r'\p{VertSpace}+', '\n\v\f\r\x85\u2028\u2029')), True) + + # Git issue 479: Segmentation fault when using conditional pattern + self.assertEqual(regex.match(r'(?(?<=A)|(?(?![^B])C|D))', 'A'), None) + self.assertEqual(regex.search(r'(?(?<=A)|(?(?![^B])C|D))', 'A').span(), (1, 1)) + + # Git issue 494: Backtracking failure matching regex ^a?(a?)b?c\1$ against string abca + self.assertEqual(regex.search(r"^a?(a?)b?c\1$", "abca").span(), (0, 4)) + + # Git issue 498: Conditional negative lookahead inside positive lookahead fails to match + self.assertEqual(regex.match(r'(?(?=a).|..)', 'ab').span(), (0, 1)) + self.assertEqual(regex.match(r'(?(?=b).|..)', 'ab').span(), (0, 2)) + self.assertEqual(regex.match(r'(?(?!a).|..)', 'ab').span(), (0, 2)) + self.assertEqual(regex.match(r'(?(?!b).|..)', 'ab').span(), (0, 1)) + + # Git issue 525: segfault when fuzzy matching empty list + self.assertEqual(regex.match(r"(\L){e<=5}", "blah", foo=[]).span(), (0, 0)) + + def test_fuzzy_ext(self): + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:a){e<=1:[a-z]}', 'e')), + True) + self.assertEqual(bool(regex.fullmatch(r'(?:a){e<=1:[a-z]}', 'e')), + True) + self.assertEqual(bool(regex.fullmatch(r'(?:a){e<=1:[a-z]}', '-')), + False) + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:a){e<=1:[a-z]}', '-')), + False) + + self.assertEqual(bool(regex.fullmatch(r'(?:a){e<=1:[a-z]}', 'ae')), + True) + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:a){e<=1:[a-z]}', + 'ae')), True) + self.assertEqual(bool(regex.fullmatch(r'(?:a){e<=1:[a-z]}', 'a-')), + False) + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:a){e<=1:[a-z]}', + 'a-')), False) + + self.assertEqual(bool(regex.fullmatch(r'(?:ab){e<=1:[a-z]}', 'ae')), + True) + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:ab){e<=1:[a-z]}', + 'ae')), True) + self.assertEqual(bool(regex.fullmatch(r'(?:ab){e<=1:[a-z]}', 'a-')), + False) + self.assertEqual(bool(regex.fullmatch(r'(?r)(?:ab){e<=1:[a-z]}', + 'a-')), False) + + self.assertEqual(bool(regex.fullmatch(r'(a)\1{e<=1:[a-z]}', 'ae')), + True) + self.assertEqual(bool(regex.fullmatch(r'(?r)\1{e<=1:[a-z]}(a)', + 'ea')), True) + self.assertEqual(bool(regex.fullmatch(r'(a)\1{e<=1:[a-z]}', 'a-')), + False) + self.assertEqual(bool(regex.fullmatch(r'(?r)\1{e<=1:[a-z]}(a)', + '-a')), False) + + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 'ts')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 'st')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 'st')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 'ts')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + '-s')), False) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 's-')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + 's-')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(?:\N{LATIN SMALL LETTER SHARP S}){e<=1:[a-z]}', + '-s')), False) + + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + 'ssst')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + 'ssts')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)\1{e<=1:[a-z]}(\N{LATIN SMALL LETTER SHARP S})', + 'stss')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)\1{e<=1:[a-z]}(\N{LATIN SMALL LETTER SHARP S})', + 'tsss')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + 'ss-s')), False) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + 'sss-')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + '-s')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(\N{LATIN SMALL LETTER SHARP S})\1{e<=1:[a-z]}', + 's-')), False) + + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(ss)\1{e<=1:[a-z]}', + '\N{LATIN SMALL LETTER SHARP S}ts')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(ss)\1{e<=1:[a-z]}', + '\N{LATIN SMALL LETTER SHARP S}st')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)\1{e<=1:[a-z]}(ss)', + 'st\N{LATIN SMALL LETTER SHARP S}')), True) + self.assertEqual(bool(regex.fullmatch(r'(?firu)\1{e<=1:[a-z]}(ss)', + 'ts\N{LATIN SMALL LETTER SHARP S}')), True) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(ss)\1{e<=1:[a-z]}', + '\N{LATIN SMALL LETTER SHARP S}-s')), False) + self.assertEqual(bool(regex.fullmatch(r'(?fiu)(ss)\1{e<=1:[a-z]}', + '\N{LATIN SMALL LETTER SHARP S}s-')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(ss)\1{e<=1:[a-z]}', + 's-\N{LATIN SMALL LETTER SHARP S}')), False) + self.assertEqual(bool(regex.fullmatch(r'(?firu)(ss)\1{e<=1:[a-z]}', + '-s\N{LATIN SMALL LETTER SHARP S}')), False) + + def test_subscripted_captures(self): + self.assertEqual(regex.match(r'(?P.)+', + 'abc').expandf('{0} {0[0]} {0[-1]}'), 'abc abc abc') + self.assertEqual(regex.match(r'(?P.)+', + 'abc').expandf('{1} {1[0]} {1[1]} {1[2]} {1[-1]} {1[-2]} {1[-3]}'), + 'c a b c c b a') + self.assertEqual(regex.match(r'(?P.)+', + 'abc').expandf('{x} {x[0]} {x[1]} {x[2]} {x[-1]} {x[-2]} {x[-3]}'), + 'c a b c c b a') + + self.assertEqual(regex.subf(r'(?P.)+', r'{0} {0[0]} {0[-1]}', + 'abc'), 'abc abc abc') + self.assertEqual(regex.subf(r'(?P.)+', + '{1} {1[0]} {1[1]} {1[2]} {1[-1]} {1[-2]} {1[-3]}', 'abc'), + 'c a b c c b a') + self.assertEqual(regex.subf(r'(?P.)+', + '{x} {x[0]} {x[1]} {x[2]} {x[-1]} {x[-2]} {x[-3]}', 'abc'), + 'c a b c c b a') + + def test_more_zerowidth(self): + if sys.version_info >= (3, 7, 0): + self.assertEqual(regex.split(r'\b|:+', 'a::bc'), ['', 'a', '', '', + 'bc', '']) + self.assertEqual(regex.sub(r'\b|:+', '-', 'a::bc'), '-a---bc-') + self.assertEqual(regex.findall(r'\b|:+', 'a::bc'), ['', '', '::', + '', '']) + self.assertEqual([m.span() for m in regex.finditer(r'\b|:+', + 'a::bc')], [(0, 0), (1, 1), (1, 3), (3, 3), (5, 5)]) + self.assertEqual([m.span() for m in regex.finditer(r'(?m)^\s*?$', + 'foo\n\n\nbar')], [(4, 4), (4, 5), (5, 5)]) + + def test_line_ending(self): + self.assertEqual(regex.findall(r'\R', '\r\n\n\x0B\f\r\x85\u2028\u2029'), + ['\r\n', '\n', '\x0B', '\f', '\r', '\x85', '\u2028', '\u2029']) + self.assertEqual(regex.findall(br'\R', b'\r\n\n\x0B\f\r\x85'), [b'\r\n', + b'\n', b'\x0B', b'\f', b'\r']) + +def test_main(): + unittest.main(verbosity=2) + +if __name__ == "__main__": + test_main() diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA new file mode 100644 index 0000000..05779fa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/METADATA @@ -0,0 +1,122 @@ +Metadata-Version: 2.1 +Name: requests +Version: 2.31.0 +Summary: Python HTTP for Humans. +Home-page: https://requests.readthedocs.io +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +License: Apache 2.0 +Project-URL: Documentation, https://requests.readthedocs.io +Project-URL: Source, https://github.com/psf/requests +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: charset-normalizer (<4,>=2) +Requires-Dist: idna (<4,>=2.5) +Requires-Dist: urllib3 (<3,>=1.21.1) +Requires-Dist: certifi (>=2017.4.17) +Provides-Extra: security +Provides-Extra: socks +Requires-Dist: PySocks (!=1.5.7,>=1.5.6) ; extra == 'socks' +Provides-Extra: use_chardet_on_py3 +Requires-Dist: chardet (<6,>=3.0.2) ; extra == 'use_chardet_on_py3' + +# Requests + +**Requests** is a simple, yet elegant, HTTP library. + +```python +>>> import requests +>>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) +>>> r.status_code +200 +>>> r.headers['content-type'] +'application/json; charset=utf8' +>>> r.encoding +'utf-8' +>>> r.text +'{"authenticated": true, ...' +>>> r.json() +{'authenticated': True, ...} +``` + +Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! + +Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. + +[![Downloads](https://pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) +[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) +[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) + +## Installing Requests and Supported Versions + +Requests is available on PyPI: + +```console +$ python -m pip install requests +``` + +Requests officially supports Python 3.7+. + +## Supported Features & Best–Practices + +Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. + +- Keep-Alive & Connection Pooling +- International Domains and URLs +- Sessions with Cookie Persistence +- Browser-style TLS/SSL Verification +- Basic & Digest Authentication +- Familiar `dict`–like Cookies +- Automatic Content Decompression and Decoding +- Multi-part File Uploads +- SOCKS Proxy Support +- Connection Timeouts +- Streaming Downloads +- Automatic honoring of `.netrc` +- Chunked HTTP Requests + +## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) + +[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) + +## Cloning the repository + +When cloning the Requests repository, you may need to add the `-c +fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see +[this issue](https://github.com/psf/requests/issues/2690) for more background): + +```shell +git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git +``` + +You can also apply this setting to your global Git config: + +```shell +git config --global fetch.fsck.badTimezone ignore +``` + +--- + +[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) + + diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/RECORD new file mode 100644 index 0000000..9705810 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/RECORD @@ -0,0 +1,42 @@ +requests-2.31.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +requests-2.31.0.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142 +requests-2.31.0.dist-info/METADATA,sha256=eCPokOnbb0FROLrfl0R5EpDvdufsb9CaN4noJH__54I,4634 +requests-2.31.0.dist-info/RECORD,, +requests-2.31.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +requests-2.31.0.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9 +requests/__init__.py,sha256=LvmKhjIz8mHaKXthC2Mv5ykZ1d92voyf3oJpd-VuAig,4963 +requests/__pycache__/__init__.cpython-310.pyc,, +requests/__pycache__/__version__.cpython-310.pyc,, +requests/__pycache__/_internal_utils.cpython-310.pyc,, +requests/__pycache__/adapters.cpython-310.pyc,, +requests/__pycache__/api.cpython-310.pyc,, +requests/__pycache__/auth.cpython-310.pyc,, +requests/__pycache__/certs.cpython-310.pyc,, +requests/__pycache__/compat.cpython-310.pyc,, +requests/__pycache__/cookies.cpython-310.pyc,, +requests/__pycache__/exceptions.cpython-310.pyc,, +requests/__pycache__/help.cpython-310.pyc,, +requests/__pycache__/hooks.cpython-310.pyc,, +requests/__pycache__/models.cpython-310.pyc,, +requests/__pycache__/packages.cpython-310.pyc,, +requests/__pycache__/sessions.cpython-310.pyc,, +requests/__pycache__/status_codes.cpython-310.pyc,, +requests/__pycache__/structures.cpython-310.pyc,, +requests/__pycache__/utils.cpython-310.pyc,, +requests/__version__.py,sha256=ssI3Ezt7PaxgkOW45GhtwPUclo_SO_ygtIm4A74IOfw,435 +requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 +requests/adapters.py,sha256=v_FmjU5KZ76k-YttShZYB5RprIzhhL8Y3zgW9p4eBQ8,19553 +requests/api.py,sha256=q61xcXq4tmiImrvcSVLTbFyCiD2F-L_-hWKGbz4y8vg,6449 +requests/auth.py,sha256=h-HLlVx9j8rKV5hfSAycP2ApOSglTz77R0tz7qCbbEE,10187 +requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429 +requests/compat.py,sha256=yxntVOSEHGMrn7FNr_32EEam1ZNAdPRdSE13_yaHzTk,1451 +requests/cookies.py,sha256=kD3kNEcCj-mxbtf5fJsSaT86eGoEYpD3X0CSgpzl7BM,18560 +requests/exceptions.py,sha256=DhveFBclVjTRxhRduVpO-GbMYMID2gmjdLfNEqNpI_U,3811 +requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875 +requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 +requests/models.py,sha256=-DlKi0or8gFAM6VzutobXvvBW_2wrJuOF5NfndTIddA,35223 +requests/packages.py,sha256=DXgv-FJIczZITmv0vEBAhWj4W-5CGCIN_ksvgR17Dvs,957 +requests/sessions.py,sha256=-LvTzrPtetSTrR3buxu4XhdgMrJFLB1q5D7P--L2Xhw,30373 +requests/status_codes.py,sha256=FvHmT5uH-_uimtRz5hH9VCbt7VV-Nei2J9upbej6j8g,4235 +requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 +requests/utils.py,sha256=6sx2X3cIVA8BgWOg8odxFy-_lbWDFETU8HI4fU4Rmqw,33448 diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/WHEEL new file mode 100644 index 0000000..1f37c02 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.40.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/top_level.txt new file mode 100644 index 0000000..f229360 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests-2.31.0.dist-info/top_level.txt @@ -0,0 +1 @@ +requests diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__init__.py b/.pythonlibs/lib/python3.10/site-packages/requests/__init__.py new file mode 100644 index 0000000..300a16c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/__init__.py @@ -0,0 +1,180 @@ +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / + +""" +Requests HTTP Library +~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. +Basic GET usage: + + >>> import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> b'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('https://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key1": "value1", + "key2": "value2" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at . + +:copyright: (c) 2017 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +import warnings + +import urllib3 + +from .exceptions import RequestsDependencyWarning + +try: + from charset_normalizer import __version__ as charset_normalizer_version +except ImportError: + charset_normalizer_version = None + +try: + from chardet import __version__ as chardet_version +except ImportError: + chardet_version = None + + +def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): + urllib3_version = urllib3_version.split(".") + assert urllib3_version != ["dev"] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append("0") + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1 + assert major >= 1 + if major == 1: + assert minor >= 21 + + # Check charset_normalizer for compatibility. + if chardet_version: + major, minor, patch = chardet_version.split(".")[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet_version >= 3.0.2, < 6.0.0 + assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0) + elif charset_normalizer_version: + major, minor, patch = charset_normalizer_version.split(".")[:3] + major, minor, patch = int(major), int(minor), int(patch) + # charset_normalizer >= 2.0.0 < 4.0.0 + assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) + else: + raise Exception("You need either charset_normalizer or chardet installed") + + +def _check_cryptography(cryptography_version): + # cryptography < 1.3.4 + try: + cryptography_version = list(map(int, cryptography_version.split("."))) + except ValueError: + return + + if cryptography_version < [1, 3, 4]: + warning = "Old version of cryptography ({}) may cause slowdown.".format( + cryptography_version + ) + warnings.warn(warning, RequestsDependencyWarning) + + +# Check imported dependencies for compatibility. +try: + check_compatibility( + urllib3.__version__, chardet_version, charset_normalizer_version + ) +except (AssertionError, ValueError): + warnings.warn( + "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " + "version!".format( + urllib3.__version__, chardet_version, charset_normalizer_version + ), + RequestsDependencyWarning, + ) + +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. +try: + try: + import ssl + except ImportError: + ssl = None + + if not getattr(ssl, "HAS_SNI", False): + from urllib3.contrib import pyopenssl + + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + + _check_cryptography(cryptography_version) +except ImportError: + pass + +# urllib3's DependencyWarnings should be silenced. +from urllib3.exceptions import DependencyWarning + +warnings.simplefilter("ignore", DependencyWarning) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +from . import packages, utils +from .__version__ import ( + __author__, + __author_email__, + __build__, + __cake__, + __copyright__, + __description__, + __license__, + __title__, + __url__, + __version__, +) +from .api import delete, get, head, options, patch, post, put, request +from .exceptions import ( + ConnectionError, + ConnectTimeout, + FileModeWarning, + HTTPError, + JSONDecodeError, + ReadTimeout, + RequestException, + Timeout, + TooManyRedirects, + URLRequired, +) +from .models import PreparedRequest, Request, Response +from .sessions import Session, session +from .status_codes import codes + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter("default", FileModeWarning, append=True) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..20e717d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__version__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__version__.cpython-310.pyc new file mode 100644 index 0000000..6be3484 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/__version__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/_internal_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/_internal_utils.cpython-310.pyc new file mode 100644 index 0000000..1ac362b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/_internal_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/adapters.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/adapters.cpython-310.pyc new file mode 100644 index 0000000..651bdcf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/adapters.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/api.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..e026770 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/api.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/auth.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/auth.cpython-310.pyc new file mode 100644 index 0000000..8e55238 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/auth.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/certs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/certs.cpython-310.pyc new file mode 100644 index 0000000..76df28f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/certs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/compat.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/compat.cpython-310.pyc new file mode 100644 index 0000000..636cff6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/compat.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/cookies.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/cookies.cpython-310.pyc new file mode 100644 index 0000000..b8a3a49 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/cookies.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..9007f42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/help.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/help.cpython-310.pyc new file mode 100644 index 0000000..fe13829 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/help.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/hooks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/hooks.cpython-310.pyc new file mode 100644 index 0000000..57b6bc2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/hooks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/models.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000..68b1b51 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/models.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/packages.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/packages.cpython-310.pyc new file mode 100644 index 0000000..e5aab0d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/packages.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/sessions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/sessions.cpython-310.pyc new file mode 100644 index 0000000..bdde0c7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/sessions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/status_codes.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/status_codes.cpython-310.pyc new file mode 100644 index 0000000..304ff7b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/status_codes.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/structures.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/structures.cpython-310.pyc new file mode 100644 index 0000000..4de4a0f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/structures.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..76e41d4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/requests/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/__version__.py b/.pythonlibs/lib/python3.10/site-packages/requests/__version__.py new file mode 100644 index 0000000..5063c3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/__version__.py @@ -0,0 +1,14 @@ +# .-. .-. .-. . . .-. .-. .-. .-. +# |( |- |.| | | |- `-. | `-. +# ' ' `-' `-`.`-' `-' `-' ' `-' + +__title__ = "requests" +__description__ = "Python HTTP for Humans." +__url__ = "https://requests.readthedocs.io" +__version__ = "2.31.0" +__build__ = 0x023100 +__author__ = "Kenneth Reitz" +__author_email__ = "me@kennethreitz.org" +__license__ = "Apache 2.0" +__copyright__ = "Copyright Kenneth Reitz" +__cake__ = "\u2728 \U0001f370 \u2728" diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/_internal_utils.py b/.pythonlibs/lib/python3.10/site-packages/requests/_internal_utils.py new file mode 100644 index 0000000..f2cf635 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/_internal_utils.py @@ -0,0 +1,50 @@ +""" +requests._internal_utils +~~~~~~~~~~~~~~ + +Provides utility functions that are consumed internally by Requests +which depend on extremely few external helpers (such as compat) +""" +import re + +from .compat import builtin_str + +_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$") +_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$") +_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$") +_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$") + +_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR) +_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE) +HEADER_VALIDATORS = { + bytes: _HEADER_VALIDATORS_BYTE, + str: _HEADER_VALIDATORS_STR, +} + + +def to_native_string(string, encoding="ascii"): + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + out = string.decode(encoding) + + return out + + +def unicode_is_ascii(u_string): + """Determine if unicode string only contains ASCII characters. + + :param str u_string: unicode string to check. Must be unicode + and not Python 2 `str`. + :rtype: bool + """ + assert isinstance(u_string, str) + try: + u_string.encode("ascii") + return True + except UnicodeEncodeError: + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/adapters.py b/.pythonlibs/lib/python3.10/site-packages/requests/adapters.py new file mode 100644 index 0000000..78e3bb6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/adapters.py @@ -0,0 +1,538 @@ +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +import os.path +import socket # noqa: F401 + +from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError +from urllib3.exceptions import HTTPError as _HTTPError +from urllib3.exceptions import InvalidHeader as _InvalidHeader +from urllib3.exceptions import ( + LocationValueError, + MaxRetryError, + NewConnectionError, + ProtocolError, +) +from urllib3.exceptions import ProxyError as _ProxyError +from urllib3.exceptions import ReadTimeoutError, ResponseError +from urllib3.exceptions import SSLError as _SSLError +from urllib3.poolmanager import PoolManager, proxy_from_url +from urllib3.util import Timeout as TimeoutSauce +from urllib3.util import parse_url +from urllib3.util.retry import Retry + +from .auth import _basic_auth_str +from .compat import basestring, urlparse +from .cookies import extract_cookies_to_jar +from .exceptions import ( + ConnectionError, + ConnectTimeout, + InvalidHeader, + InvalidProxyURL, + InvalidSchema, + InvalidURL, + ProxyError, + ReadTimeout, + RetryError, + SSLError, +) +from .models import Response +from .structures import CaseInsensitiveDict +from .utils import ( + DEFAULT_CA_BUNDLE_PATH, + extract_zipped_paths, + get_auth_from_url, + get_encoding_from_headers, + prepend_scheme_if_needed, + select_proxy, + urldefragauth, +) + +try: + from urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +class BaseAdapter: + """The Base Transport Adapter""" + + def __init__(self): + super().__init__() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self): + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session ` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + + __attrs__ = [ + "max_retries", + "config", + "_pool_connections", + "_pool_maxsize", + "_pool_block", + ] + + def __init__( + self, + pool_connections=DEFAULT_POOLSIZE, + pool_maxsize=DEFAULT_POOLSIZE, + max_retries=DEFAULT_RETRIES, + pool_block=DEFAULT_POOLBLOCK, + ): + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super().__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self): + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager( + self._pool_connections, self._pool_maxsize, block=self._pool_block + ) + + def init_poolmanager( + self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs + ): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager( + num_pools=connections, + maxsize=maxsize, + block=block, + **pool_kwargs, + ) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith("socks"): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs, + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs, + ) + + return manager + + def cert_verify(self, conn, url, verify, cert): + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith("https") and verify: + + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + + if not cert_loc or not os.path.exists(cert_loc): + raise OSError( + f"Could not find a suitable TLS CA certificate bundle, " + f"invalid path: {cert_loc}" + ) + + conn.cert_reqs = "CERT_REQUIRED" + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = "CERT_NONE" + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + conn.key_file = None + if conn.cert_file and not os.path.exists(conn.cert_file): + raise OSError( + f"Could not find the TLS certificate file, " + f"invalid path: {conn.cert_file}" + ) + if conn.key_file and not os.path.exists(conn.key_file): + raise OSError( + f"Could not find the TLS key file, invalid path: {conn.key_file}" + ) + + def build_response(self, req, resp): + """Builds a :class:`Response ` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter ` + + :param req: The :class:`PreparedRequest ` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, "status", None) + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, "headers", {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode("utf-8") + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: urllib3.ConnectionPool + """ + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, "http") + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL( + "Please check proxy URL. It is malformed " + "and could be missing the host." + ) + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self): + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url(self, request, proxies): + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = proxy and scheme != "https" + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith("socks") + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request, **kwargs): + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter `. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy): + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The url of the proxy being used for this request. + :rtype: dict + """ + headers = {} + username, password = get_auth_from_url(proxy) + + if username: + headers["Proxy-Authorization"] = _basic_auth_str(username, password) + + return headers + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection(request.url, proxies) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + elif isinstance(e, _InvalidHeader): + raise InvalidHeader(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/api.py b/.pythonlibs/lib/python3.10/site-packages/requests/api.py new file mode 100644 index 0000000..cd0b3ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/api.py @@ -0,0 +1,157 @@ +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from . import sessions + + +def request(method, url, **kwargs): + """Constructs and sends a :class:`Request `. + + :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response ` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'https://httpbin.org/get') + >>> req + + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get(url, params=None, **kwargs): + r"""Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("get", url, params=params, **kwargs) + + +def options(url, **kwargs): + r"""Sends an OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("options", url, **kwargs) + + +def head(url, **kwargs): + r"""Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. If + `allow_redirects` is not provided, it will be set to `False` (as + opposed to the default :meth:`request` behavior). + :return: :class:`Response ` object + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", False) + return request("head", url, **kwargs) + + +def post(url, data=None, json=None, **kwargs): + r"""Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("post", url, data=data, json=json, **kwargs) + + +def put(url, data=None, **kwargs): + r"""Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("put", url, data=data, **kwargs) + + +def patch(url, data=None, **kwargs): + r"""Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("patch", url, data=data, **kwargs) + + +def delete(url, **kwargs): + r"""Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request("delete", url, **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/auth.py b/.pythonlibs/lib/python3.10/site-packages/requests/auth.py new file mode 100644 index 0000000..9733686 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/auth.py @@ -0,0 +1,315 @@ +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +import hashlib +import os +import re +import threading +import time +import warnings +from base64 import b64encode + +from ._internal_utils import to_native_string +from .compat import basestring, str, urlparse +from .cookies import extract_cookies_to_jar +from .utils import parse_dict_header + +CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded" +CONTENT_TYPE_MULTI_PART = "multipart/form-data" + + +def _basic_auth_str(username, password): + """Returns a Basic Auth string.""" + + # "I want us to put a big-ol' comment on top of it that + # says that this behaviour is dumb but we need to preserve + # it because people are relying on it." + # - Lukasa + # + # These are here solely to maintain backwards compatibility + # for things like ints. This will be removed in 3.0.0. + if not isinstance(username, basestring): + warnings.warn( + "Non-string usernames will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(username), + category=DeprecationWarning, + ) + username = str(username) + + if not isinstance(password, basestring): + warnings.warn( + "Non-string passwords will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(type(password)), + category=DeprecationWarning, + ) + password = str(password) + # -- End Removal -- + + if isinstance(username, str): + username = username.encode("latin1") + + if isinstance(password, str): + password = password.encode("latin1") + + authstr = "Basic " + to_native_string( + b64encode(b":".join((username, password))).strip() + ) + + return authstr + + +class AuthBase: + """Base class that all auth implementations derive from""" + + def __call__(self, r): + raise NotImplementedError("Auth hooks must be callable.") + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + + def __eq__(self, other): + return all( + [ + self.username == getattr(other, "username", None), + self.password == getattr(other, "password", None), + ] + ) + + def __ne__(self, other): + return not self == other + + def __call__(self, r): + r.headers["Authorization"] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r): + r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, "init"): + self._thread_local.init = True + self._thread_local.last_nonce = "" + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method, url): + """ + :rtype: str + """ + + realm = self._thread_local.chal["realm"] + nonce = self._thread_local.chal["nonce"] + qop = self._thread_local.chal.get("qop") + algorithm = self._thread_local.chal.get("algorithm") + opaque = self._thread_local.chal.get("opaque") + hash_utf8 = None + + if algorithm is None: + _algorithm = "MD5" + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == "MD5" or _algorithm == "MD5-SESS": + + def md5_utf8(x): + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.md5(x).hexdigest() + + hash_utf8 = md5_utf8 + elif _algorithm == "SHA": + + def sha_utf8(x): + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha1(x).hexdigest() + + hash_utf8 = sha_utf8 + elif _algorithm == "SHA-256": + + def sha256_utf8(x): + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha256(x).hexdigest() + + hash_utf8 = sha256_utf8 + elif _algorithm == "SHA-512": + + def sha512_utf8(x): + if isinstance(x, str): + x = x.encode("utf-8") + return hashlib.sha512(x).hexdigest() + + hash_utf8 = sha512_utf8 + + KD = lambda s, d: hash_utf8(f"{s}:{d}") # noqa:E731 + + if hash_utf8 is None: + return None + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += f"?{p_parsed.query}" + + A1 = f"{self.username}:{realm}:{self.password}" + A2 = f"{method}:{path}" + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = f"{self._thread_local.nonce_count:08x}" + s = str(self._thread_local.nonce_count).encode("utf-8") + s += nonce.encode("utf-8") + s += time.ctime().encode("utf-8") + s += os.urandom(8) + + cnonce = hashlib.sha1(s).hexdigest()[:16] + if _algorithm == "MD5-SESS": + HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}") + + if not qop: + respdig = KD(HA1, f"{nonce}:{HA2}") + elif qop == "auth" or "auth" in qop.split(","): + noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}" + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = ( + f'username="{self.username}", realm="{realm}", nonce="{nonce}", ' + f'uri="{path}", response="{respdig}"' + ) + if opaque: + base += f', opaque="{opaque}"' + if algorithm: + base += f', algorithm="{algorithm}"' + if entdig: + base += f', digest="{entdig}"' + if qop: + base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"' + + return f"Digest {base}" + + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r, **kwargs): + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + # If response is not 4xx, do not auth + # See https://github.com/psf/requests/issues/3772 + if not 400 <= r.status_code < 500: + self._thread_local.num_401_calls = 1 + return r + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self._thread_local.pos) + s_auth = r.headers.get("www-authenticate", "") + + if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2: + + self._thread_local.num_401_calls += 1 + pat = re.compile(r"digest ", flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) + + prep.headers["Authorization"] = self.build_digest_header( + prep.method, prep.url + ) + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + r.headers["Authorization"] = self.build_digest_header(r.method, r.url) + try: + self._thread_local.pos = r.body.tell() + except AttributeError: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook("response", self.handle_401) + r.register_hook("response", self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other): + return all( + [ + self.username == getattr(other, "username", None), + self.password == getattr(other, "password", None), + ] + ) + + def __ne__(self, other): + return not self == other diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/certs.py b/.pythonlibs/lib/python3.10/site-packages/requests/certs.py new file mode 100644 index 0000000..be422c3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/certs.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. There is +only one — the one from the certifi package. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" +from certifi import where + +if __name__ == "__main__": + print(where()) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/compat.py b/.pythonlibs/lib/python3.10/site-packages/requests/compat.py new file mode 100644 index 0000000..6776163 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/compat.py @@ -0,0 +1,79 @@ +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module previously handled import compatibility issues +between Python 2 and Python 3. It remains for backwards +compatibility until the next major version. +""" + +try: + import chardet +except ImportError: + import charset_normalizer as chardet + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = _ver[0] == 2 + +#: Python 3.x? +is_py3 = _ver[0] == 3 + +# json/simplejson module import resolution +has_simplejson = False +try: + import simplejson as json + + has_simplejson = True +except ImportError: + import json + +if has_simplejson: + from simplejson import JSONDecodeError +else: + from json import JSONDecodeError + +# Keep OrderedDict for backwards compatibility. +from collections import OrderedDict +from collections.abc import Callable, Mapping, MutableMapping +from http import cookiejar as cookielib +from http.cookies import Morsel +from io import StringIO + +# -------------- +# Legacy Imports +# -------------- +from urllib.parse import ( + quote, + quote_plus, + unquote, + unquote_plus, + urldefrag, + urlencode, + urljoin, + urlparse, + urlsplit, + urlunparse, +) +from urllib.request import ( + getproxies, + getproxies_environment, + parse_http_list, + proxy_bypass, + proxy_bypass_environment, +) + +builtin_str = str +str = str +bytes = bytes +basestring = (str, bytes) +numeric_types = (int, float) +integer_types = (int,) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/cookies.py b/.pythonlibs/lib/python3.10/site-packages/requests/cookies.py new file mode 100644 index 0000000..bf54ab2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/cookies.py @@ -0,0 +1,561 @@ +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `cookielib.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +import calendar +import copy +import time + +from ._internal_utils import to_native_string +from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse + +try: + import threading +except ImportError: + import dummy_threading as threading + + +class MockRequest: + """Wraps a `requests.Request` to mimic a `urllib2.Request`. + + The code in `cookielib.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + def __init__(self, request): + self._r = request + self._new_headers = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self): + return self.type + + def get_host(self): + return urlparse(self._r.url).netloc + + def get_origin_req_host(self): + return self.get_host() + + def get_full_url(self): + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get("Host"): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = to_native_string(self._r.headers["Host"], encoding="utf-8") + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse( + [ + parsed.scheme, + host, + parsed.path, + parsed.params, + parsed.query, + parsed.fragment, + ] + ) + + def is_unverifiable(self): + return True + + def has_header(self, name): + return name in self._r.headers or name in self._new_headers + + def get_header(self, name, default=None): + return self._r.headers.get(name, self._new_headers.get(name, default)) + + def add_header(self, key, val): + """cookielib has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError( + "Cookie headers should be added with add_unredirected_header()" + ) + + def add_unredirected_header(self, name, value): + self._new_headers[name] = value + + def get_new_headers(self): + return self._new_headers + + @property + def unverifiable(self): + return self.is_unverifiable() + + @property + def origin_req_host(self): + return self.get_origin_req_host() + + @property + def host(self): + return self.get_host() + + +class MockResponse: + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `cookielib` expects to see them. + """ + + def __init__(self, headers): + """Make a MockResponse for `cookielib` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self): + return self._headers + + def getheaders(self, name): + self._headers.getheaders(name) + + +def extract_cookies_to_jar(jar, request, response): + """Extract the cookies from the response into a CookieJar. + + :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, "_original_response") and response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) + + +def get_cookie_header(jar, request): + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) + return r.get_new_headers().get("Cookie") + + +def remove_cookie_by_name(cookiejar, name, domain=None, path=None): + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(cookielib.CookieJar, MutableMapping): + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + def get(self, name, default=None, domain=None, path=None): + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set(self, name, value, **kwargs): + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name( + self, name, domain=kwargs.get("domain"), path=kwargs.get("path") + ) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self): + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self): + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self): + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self): + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self): + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self): + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self): + """Utility method to list all the domains in the jar.""" + domains = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self): + """Utility method to list all the paths in the jar.""" + paths = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self): + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict(self, domain=None, path=None): + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary = {} + for cookie in iter(self): + if (domain is None or cookie.domain == domain) and ( + path is None or cookie.path == path + ): + dictionary[cookie.name] = cookie.value + return dictionary + + def __contains__(self, name): + try: + return super().__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name): + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__(self, name, value): + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name): + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie, *args, **kwargs): + if ( + hasattr(cookie.value, "startswith") + and cookie.value.startswith('"') + and cookie.value.endswith('"') + ): + cookie.value = cookie.value.replace('\\"', "") + return super().set_cookie(cookie, *args, **kwargs) + + def update(self, other): + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super().update(other) + + def _find(self, name, domain=None, path=None): + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") + + def _find_no_duplicates(self, name, domain=None, path=None): + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: + # if there are multiple cookies that meet passed in criteria + raise CookieConflictError( + f"There are multiple cookies with name, {name!r}" + ) + # we will eventually return this as long as no cookie conflict + toReturn = cookie.value + + if toReturn: + return toReturn + raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") + + def __getstate__(self): + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop("_cookies_lock") + return state + + def __setstate__(self, state): + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if "_cookies_lock" not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self): + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.set_policy(self.get_policy()) + new_cj.update(self) + return new_cj + + def get_policy(self): + """Return the CookiePolicy instance used.""" + return self._policy + + +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, "copy"): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name, value, **kwargs): + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result = { + "version": 0, + "name": name, + "value": value, + "port": None, + "domain": "", + "path": "/", + "secure": False, + "expires": None, + "discard": True, + "comment": None, + "comment_url": None, + "rest": {"HttpOnly": None}, + "rfc2109": False, + } + + badargs = set(kwargs) - set(result) + if badargs: + raise TypeError( + f"create_cookie() got unexpected keyword arguments: {list(badargs)}" + ) + + result.update(kwargs) + result["port_specified"] = bool(result["port"]) + result["domain_specified"] = bool(result["domain"]) + result["domain_initial_dot"] = result["domain"].startswith(".") + result["path_specified"] = bool(result["path"]) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel): + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires = None + if morsel["max-age"]: + try: + expires = int(time.time() + int(morsel["max-age"])) + except ValueError: + raise TypeError(f"max-age: {morsel['max-age']} must be integer") + elif morsel["expires"]: + time_template = "%a, %d-%b-%Y %H:%M:%S GMT" + expires = calendar.timegm(time.strptime(morsel["expires"], time_template)) + return create_cookie( + comment=morsel["comment"], + comment_url=bool(morsel["comment"]), + discard=False, + domain=morsel["domain"], + expires=expires, + name=morsel.key, + path=morsel["path"], + port=None, + rest={"HttpOnly": morsel["httponly"]}, + rfc2109=False, + secure=bool(morsel["secure"]), + value=morsel.value, + version=morsel["version"] or 0, + ) + + +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + :rtype: CookieJar + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies(cookiejar, cookies): + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + :rtype: CookieJar + """ + if not isinstance(cookiejar, cookielib.CookieJar): + raise ValueError("You can only merge into CookieJar") + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + try: + cookiejar.update(cookies) + except AttributeError: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/requests/exceptions.py new file mode 100644 index 0000000..e1cedf8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/exceptions.py @@ -0,0 +1,141 @@ +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" +from urllib3.exceptions import HTTPError as BaseHTTPError + +from .compat import JSONDecodeError as CompatJSONDecodeError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + def __init__(self, *args, **kwargs): + """Initialize RequestException with `request` and `response` objects.""" + response = kwargs.pop("response", None) + self.response = response + self.request = kwargs.pop("request", None) + if response is not None and not self.request and hasattr(response, "request"): + self.request = self.response.request + super().__init__(*args, **kwargs) + + +class InvalidJSONError(RequestException): + """A JSON error occurred.""" + + +class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): + """Couldn't decode the text into json""" + + def __init__(self, *args, **kwargs): + """ + Construct the JSONDecodeError instance first with all + args. Then use it's args to construct the IOError so that + the json specific args aren't used as IOError specific args + and the error message from JSONDecodeError is preserved. + """ + CompatJSONDecodeError.__init__(self, *args) + InvalidJSONError.__init__(self, *self.args, **kwargs) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL scheme (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """The URL scheme provided is either invalid or unsupported.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content.""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed.""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +class UnrewindableBodyError(RequestException): + """Requests encountered an error when trying to rewind a body.""" + + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/help.py b/.pythonlibs/lib/python3.10/site-packages/requests/help.py new file mode 100644 index 0000000..8fbcd65 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/help.py @@ -0,0 +1,134 @@ +"""Module containing bug report helper(s).""" + +import json +import platform +import ssl +import sys + +import idna +import urllib3 + +from . import __version__ as requests_version + +try: + import charset_normalizer +except ImportError: + charset_normalizer = None + +try: + import chardet +except ImportError: + chardet = None + +try: + from urllib3.contrib import pyopenssl +except ImportError: + pyopenssl = None + OpenSSL = None + cryptography = None +else: + import cryptography + import OpenSSL + + +def _implementation(): + """Return a dict with the Python implementation and version. + + Provide both the name and the version of the Python implementation + currently running. For example, on CPython 3.10.3 it will return + {'name': 'CPython', 'version': '3.10.3'}. + + This function works best on CPython and PyPy: in particular, it probably + doesn't work for Jython or IronPython. Future investigation should be done + to work out the correct shape of the code for those platforms. + """ + implementation = platform.python_implementation() + + if implementation == "CPython": + implementation_version = platform.python_version() + elif implementation == "PyPy": + implementation_version = "{}.{}.{}".format( + sys.pypy_version_info.major, + sys.pypy_version_info.minor, + sys.pypy_version_info.micro, + ) + if sys.pypy_version_info.releaselevel != "final": + implementation_version = "".join( + [implementation_version, sys.pypy_version_info.releaselevel] + ) + elif implementation == "Jython": + implementation_version = platform.python_version() # Complete Guess + elif implementation == "IronPython": + implementation_version = platform.python_version() # Complete Guess + else: + implementation_version = "Unknown" + + return {"name": implementation, "version": implementation_version} + + +def info(): + """Generate information for a bug report.""" + try: + platform_info = { + "system": platform.system(), + "release": platform.release(), + } + except OSError: + platform_info = { + "system": "Unknown", + "release": "Unknown", + } + + implementation_info = _implementation() + urllib3_info = {"version": urllib3.__version__} + charset_normalizer_info = {"version": None} + chardet_info = {"version": None} + if charset_normalizer: + charset_normalizer_info = {"version": charset_normalizer.__version__} + if chardet: + chardet_info = {"version": chardet.__version__} + + pyopenssl_info = { + "version": None, + "openssl_version": "", + } + if OpenSSL: + pyopenssl_info = { + "version": OpenSSL.__version__, + "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}", + } + cryptography_info = { + "version": getattr(cryptography, "__version__", ""), + } + idna_info = { + "version": getattr(idna, "__version__", ""), + } + + system_ssl = ssl.OPENSSL_VERSION_NUMBER + system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} + + return { + "platform": platform_info, + "implementation": implementation_info, + "system_ssl": system_ssl_info, + "using_pyopenssl": pyopenssl is not None, + "using_charset_normalizer": chardet is None, + "pyOpenSSL": pyopenssl_info, + "urllib3": urllib3_info, + "chardet": chardet_info, + "charset_normalizer": charset_normalizer_info, + "cryptography": cryptography_info, + "idna": idna_info, + "requests": { + "version": requests_version, + }, + } + + +def main(): + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/hooks.py b/.pythonlibs/lib/python3.10/site-packages/requests/hooks.py new file mode 100644 index 0000000..d181ba2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/hooks.py @@ -0,0 +1,33 @@ +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" +HOOKS = ["response"] + + +def default_hooks(): + return {event: [] for event in HOOKS} + + +# TODO: response is the only one + + +def dispatch_hook(key, hooks, hook_data, **kwargs): + """Dispatches a hook dictionary on a given piece of data.""" + hooks = hooks or {} + hooks = hooks.get(key) + if hooks: + if hasattr(hooks, "__call__"): + hooks = [hooks] + for hook in hooks: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/models.py b/.pythonlibs/lib/python3.10/site-packages/requests/models.py new file mode 100644 index 0000000..617a413 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/models.py @@ -0,0 +1,1034 @@ +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +import datetime + +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP, +# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. +import encodings.idna # noqa: F401 +from io import UnsupportedOperation + +from urllib3.exceptions import ( + DecodeError, + LocationParseError, + ProtocolError, + ReadTimeoutError, + SSLError, +) +from urllib3.fields import RequestField +from urllib3.filepost import encode_multipart_formdata +from urllib3.util import parse_url + +from ._internal_utils import to_native_string, unicode_is_ascii +from .auth import HTTPBasicAuth +from .compat import ( + Callable, + JSONDecodeError, + Mapping, + basestring, + builtin_str, + chardet, + cookielib, +) +from .compat import json as complexjson +from .compat import urlencode, urlsplit, urlunparse +from .cookies import _copy_cookie_jar, cookiejar_from_dict, get_cookie_header +from .exceptions import ( + ChunkedEncodingError, + ConnectionError, + ContentDecodingError, + HTTPError, + InvalidJSONError, + InvalidURL, +) +from .exceptions import JSONDecodeError as RequestsJSONDecodeError +from .exceptions import MissingSchema +from .exceptions import SSLError as RequestsSSLError +from .exceptions import StreamConsumedError +from .hooks import default_hooks +from .status_codes import codes +from .structures import CaseInsensitiveDict +from .utils import ( + check_header_validity, + get_auth_from_url, + guess_filename, + guess_json_utf, + iter_slices, + parse_header_links, + requote_uri, + stream_decode_response_unicode, + super_len, + to_key_val_list, +) + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI = ( + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT = 30 +CONTENT_CHUNK_SIZE = 10 * 1024 +ITER_CHUNK_SIZE = 512 + + +class RequestEncodingMixin: + @property + def path_url(self): + """Build the path URL to use.""" + + url = [] + + p = urlsplit(self.url) + + path = p.path + if not path: + path = "/" + + url.append(path) + + query = p.query + if query: + url.append("?") + url.append(query) + + return "".join(url) + + @staticmethod + def _encode_params(data): + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, "read"): + return data + elif hasattr(data, "__iter__"): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, "__iter__"): + vs = [vs] + for v in vs: + if v is not None: + result.append( + ( + k.encode("utf-8") if isinstance(k, str) else k, + v.encode("utf-8") if isinstance(v, str) else v, + ) + ) + return urlencode(result, doseq=True) + else: + return data + + @staticmethod + def _encode_files(files, data): + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if not files: + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, "__iter__"): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + ( + field.decode("utf-8") + if isinstance(field, bytes) + else field, + v.encode("utf-8") if isinstance(v, str) else v, + ) + ) + + for (k, v) in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + elif hasattr(fp, "read"): + fdata = fp.read() + elif fp is None: + continue + else: + fdata = fp + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin: + def register_hook(self, event, hook): + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError(f'Unsupported event specified, with event name "{event}"') + + if isinstance(hook, Callable): + self.hooks[event].append(hook) + elif hasattr(hook, "__iter__"): + self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) + + def deregister_hook(self, event, hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request ` object. + + Used to prepare a :class:`PreparedRequest `, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: URL parameters to append to the URL. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> req.prepare() + + """ + + def __init__( + self, + method=None, + url=None, + headers=None, + files=None, + data=None, + params=None, + auth=None, + cookies=None, + hooks=None, + json=None, + ): + + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for (k, v) in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self): + return f"" + + def prepare(self): + """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest ` object, + containing the exact bytes that will be sent to the server. + + Instances are generated from a :class:`Request ` object, and + should not be instantiated manually; doing so may produce undesirable + effects. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> r = req.prepare() + >>> r + + + >>> s = requests.Session() + >>> s.send(r) + + """ + + def __init__(self): + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + #: integer denoting starting position of a readable file-like body. + self._body_position = None + + def prepare( + self, + method=None, + url=None, + headers=None, + files=None, + data=None, + params=None, + auth=None, + cookies=None, + hooks=None, + json=None, + ): + """Prepares the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self): + return f"" + + def copy(self): + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + p._body_position = self._body_position + return p + + def prepare_method(self, method): + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + @staticmethod + def _get_idna_encoded_host(host): + import idna + + try: + host = idna.encode(host, uts46=True).decode("utf-8") + except idna.IDNAError: + raise UnicodeError + return host + + def prepare_url(self, url, params): + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/psf/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode("utf8") + else: + url = str(url) + + # Remove leading whitespaces from url + url = url.lstrip() + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ":" in url and not url.lower().startswith("http"): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + raise MissingSchema( + f"Invalid URL {url!r}: No scheme supplied. " + f"Perhaps you meant https://{url}?" + ) + + if not host: + raise InvalidURL(f"Invalid URL {url!r}: No host supplied") + + # In general, we want to try IDNA encoding the hostname if the string contains + # non-ASCII characters. This allows users to automatically get the correct IDNA + # behaviour. For strings containing only ASCII characters, we need to also verify + # it doesn't start with a wildcard (*), before allowing the unencoded hostname. + if not unicode_is_ascii(host): + try: + host = self._get_idna_encoded_host(host) + except UnicodeError: + raise InvalidURL("URL has an invalid label.") + elif host.startswith(("*", ".")): + raise InvalidURL("URL has an invalid label.") + + # Carefully reconstruct the network location + netloc = auth or "" + if netloc: + netloc += "@" + netloc += host + if port: + netloc += f":{port}" + + # Bare domains aren't valid URLs. + if not path: + path = "/" + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + enc_params = self._encode_params(params) + if enc_params: + if query: + query = f"{query}&{enc_params}" + else: + query = enc_params + + url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) + self.url = url + + def prepare_headers(self, headers): + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body(self, data, files, json=None): + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = "application/json" + + try: + body = complexjson.dumps(json, allow_nan=False) + except ValueError as ve: + raise InvalidJSONError(ve, request=self) + + if not isinstance(body, bytes): + body = body.encode("utf-8") + + is_stream = all( + [ + hasattr(data, "__iter__"), + not isinstance(data, (basestring, list, tuple, Mapping)), + ] + ) + + if is_stream: + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + body = data + + if getattr(body, "tell", None) is not None: + # Record the current file position before reading. + # This will allow us to rewind a file in the event + # of a redirect. + try: + self._body_position = body.tell() + except OSError: + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body + self._body_position = object() + + if files: + raise NotImplementedError( + "Streamed bodies and files are mutually exclusive." + ) + + if length: + self.headers["Content-Length"] = builtin_str(length) + else: + self.headers["Transfer-Encoding"] = "chunked" + else: + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, data) + else: + if data: + body = self._encode_params(data) + if isinstance(data, basestring) or hasattr(data, "read"): + content_type = None + else: + content_type = "application/x-www-form-urlencoded" + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ("content-type" not in self.headers): + self.headers["Content-Type"] = content_type + + self.body = body + + def prepare_content_length(self, body): + """Prepare Content-Length header based on request method and body""" + if body is not None: + length = super_len(body) + if length: + # If length exists, set it. Otherwise, we fallback + # to Transfer-Encoding: chunked. + self.headers["Content-Length"] = builtin_str(length) + elif ( + self.method not in ("GET", "HEAD") + and self.headers.get("Content-Length") is None + ): + # Set Content-Length to 0 for methods that can have a body + # but don't provide one. (i.e. not GET or HEAD) + self.headers["Content-Length"] = "0" + + def prepare_auth(self, auth, url=""): + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(self.url) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: + # special-case basic HTTP auth + auth = HTTPBasicAuth(*auth) + + # Allow auth to make its changes. + r = auth(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies(self, cookies): + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest ` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookie_header = get_cookie_header(self._cookies, self) + if cookie_header is not None: + self.headers["Cookie"] = cookie_header + + def prepare_hooks(self, hooks): + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response: + """The :class:`Response ` object, which contains a + server's response to an HTTP request. + """ + + __attrs__ = [ + "_content", + "status_code", + "headers", + "url", + "history", + "encoding", + "reason", + "cookies", + "elapsed", + "request", + ] + + def __init__(self): + self._content = False + self._content_consumed = False + self._next = None + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + #: This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response ` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest ` object to which this + #: is a response. + self.request = None + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def __getstate__(self): + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, "_content_consumed", True) + setattr(self, "raw", None) + + def __repr__(self): + return f"" + + def __bool__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __nonzero__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __iter__(self): + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self): + """Returns True if :attr:`status_code` is less than 400, False if not. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self): + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return "location" in self.headers and self.status_code in REDIRECT_STATI + + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect.""" + return "location" in self.headers and self.status_code in ( + codes.moved_permanently, + codes.permanent_redirect, + ) + + @property + def next(self): + """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" + return self._next + + @property + def apparent_encoding(self): + """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" + return chardet.detect(self.content)["encoding"] + + def iter_content(self, chunk_size=1, decode_unicode=False): + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using the best + available encoding based on the response. + """ + + def generate(): + # Special case for urllib3. + if hasattr(self.raw, "stream"): + try: + yield from self.raw.stream(chunk_size, decode_content=True) + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + except SSLError as e: + raise RequestsSSLError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): + raise TypeError( + f"chunk_size must be an int, it is instead a {type(chunk_size)}." + ) + # simulate reading small chunks of the content + reused_chunks = iter_slices(self._content, chunk_size) + + stream_chunks = generate() + + chunks = reused_chunks if self._content_consumed else stream_chunks + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + def iter_lines( + self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None + ): + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + .. note:: This method is not reentrant safe. + """ + + pending = None + + for chunk in self.iter_content( + chunk_size=chunk_size, decode_unicode=decode_unicode + ): + + if pending is not None: + chunk = pending + chunk + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + yield from lines + + if pending is not None: + yield pending + + @property + def content(self): + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + if self._content_consumed: + raise RuntimeError("The content for this response was already consumed") + + if self.status_code == 0 or self.raw is None: + self._content = None + else: + self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content + + @property + def text(self): + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``charset_normalizer`` or ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return "" + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding, errors="replace") + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors="replace") + + return content + + def json(self, **kwargs): + r"""Returns the json-encoded content of a response, if any. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + :raises requests.exceptions.JSONDecodeError: If the response body does not + contain valid json. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using charset_normalizer to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads(self.content.decode(encoding), **kwargs) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + except JSONDecodeError as e: + raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) + + try: + return complexjson.loads(self.text, **kwargs) + except JSONDecodeError as e: + # Catch JSON-related errors and raise as requests.JSONDecodeError + # This aliases json.JSONDecodeError and simplejson.JSONDecodeError + raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) + + @property + def links(self): + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get("link") + + resolved_links = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get("rel") or link.get("url") + resolved_links[key] = link + + return resolved_links + + def raise_for_status(self): + """Raises :class:`HTTPError`, if one occurred.""" + + http_error_msg = "" + if isinstance(self.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. (See PR #3538) + try: + reason = self.reason.decode("utf-8") + except UnicodeDecodeError: + reason = self.reason.decode("iso-8859-1") + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = ( + f"{self.status_code} Client Error: {reason} for url: {self.url}" + ) + + elif 500 <= self.status_code < 600: + http_error_msg = ( + f"{self.status_code} Server Error: {reason} for url: {self.url}" + ) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self): + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + release_conn = getattr(self.raw, "release_conn", None) + if release_conn is not None: + release_conn() diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/packages.py b/.pythonlibs/lib/python3.10/site-packages/requests/packages.py new file mode 100644 index 0000000..77c45c9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/packages.py @@ -0,0 +1,28 @@ +import sys + +try: + import chardet +except ImportError: + import warnings + + import charset_normalizer as chardet + + warnings.filterwarnings("ignore", "Trying to detect", module="charset_normalizer") + +# This code exists for backwards compatibility reasons. +# I don't like it either. Just look the other way. :) + +for package in ("urllib3", "idna"): + locals()[package] = __import__(package) + # This traversal is apparently necessary such that the identities are + # preserved (requests.packages.urllib3.* is urllib3.*) + for mod in list(sys.modules): + if mod == package or mod.startswith(f"{package}."): + sys.modules[f"requests.packages.{mod}"] = sys.modules[mod] + +target = chardet.__name__ +for mod in list(sys.modules): + if mod == target or mod.startswith(f"{target}."): + target = target.replace(target, "chardet") + sys.modules[f"requests.packages.{target}"] = sys.modules[mod] +# Kinda cool, though, right? diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/sessions.py b/.pythonlibs/lib/python3.10/site-packages/requests/sessions.py new file mode 100644 index 0000000..dbcf2a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/sessions.py @@ -0,0 +1,833 @@ +""" +requests.sessions +~~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" +import os +import sys +import time +from collections import OrderedDict +from datetime import timedelta + +from ._internal_utils import to_native_string +from .adapters import HTTPAdapter +from .auth import _basic_auth_str +from .compat import Mapping, cookielib, urljoin, urlparse +from .cookies import ( + RequestsCookieJar, + cookiejar_from_dict, + extract_cookies_to_jar, + merge_cookies, +) +from .exceptions import ( + ChunkedEncodingError, + ContentDecodingError, + InvalidSchema, + TooManyRedirects, +) +from .hooks import default_hooks, dispatch_hook + +# formerly defined here, reexposed here for backward compatibility +from .models import ( # noqa: F401 + DEFAULT_REDIRECT_LIMIT, + REDIRECT_STATI, + PreparedRequest, + Request, +) +from .status_codes import codes +from .structures import CaseInsensitiveDict +from .utils import ( # noqa: F401 + DEFAULT_PORTS, + default_headers, + get_auth_from_url, + get_environ_proxies, + get_netrc_auth, + requote_uri, + resolve_proxies, + rewind_body, + should_bypass_proxies, + to_key_val_list, +) + +# Preferred clock, based on which one is more accurate on a given system. +if sys.platform == "win32": + preferred_clock = time.perf_counter +else: + preferred_clock = time.time + + +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get("response") == []: + return request_hooks + + if request_hooks is None or request_hooks.get("response") == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin: + def get_redirect_target(self, resp): + """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. + if resp.is_redirect: + location = resp.headers["location"] + # Currently the underlying http module on py3 decode headers + # in latin1, but empirical evidence suggests that latin1 is very + # rarely used with non-ASCII characters in HTTP headers. + # It is more likely to get UTF8 header rather than latin1. + # This causes incorrect handling of UTF8 encoded location headers. + # To solve this, we re-encode the location in latin1. + location = location.encode("latin1") + return to_native_string(location, "utf8") + return None + + def should_strip_auth(self, old_url, new_url): + """Decide whether Authorization header should be removed when redirecting""" + old_parsed = urlparse(old_url) + new_parsed = urlparse(new_url) + if old_parsed.hostname != new_parsed.hostname: + return True + # Special case: allow http -> https redirect when using the standard + # ports. This isn't specified by RFC 7235, but is kept to avoid + # breaking backwards compatibility with older versions of requests + # that allowed any redirects on the same host. + if ( + old_parsed.scheme == "http" + and old_parsed.port in (80, None) + and new_parsed.scheme == "https" + and new_parsed.port in (443, None) + ): + return False + + # Handle default port usage corresponding to scheme. + changed_port = old_parsed.port != new_parsed.port + changed_scheme = old_parsed.scheme != new_parsed.scheme + default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) + if ( + not changed_scheme + and old_parsed.port in default_port + and new_parsed.port in default_port + ): + return False + + # Standard case: root URI must match + return changed_port or changed_scheme + + def resolve_redirects( + self, + resp, + req, + stream=False, + timeout=None, + verify=True, + cert=None, + proxies=None, + yield_requests=False, + **adapter_kwargs, + ): + """Receives a Response. Returns a generator of Responses or Requests.""" + + hist = [] # keep track of history + + url = self.get_redirect_target(resp) + previous_fragment = urlparse(req.url).fragment + while url: + prepared_request = req.copy() + + # Update history and keep track of redirects. + # resp.history must ignore the original request in this loop + hist.append(resp) + resp.history = hist[1:] + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if len(resp.history) >= self.max_redirects: + raise TooManyRedirects( + f"Exceeded {self.max_redirects} redirects.", response=resp + ) + + # Release the connection back into the pool. + resp.close() + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith("//"): + parsed_rurl = urlparse(resp.url) + url = ":".join([to_native_string(parsed_rurl.scheme), url]) + + # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) + parsed = urlparse(url) + if parsed.fragment == "" and previous_fragment: + parsed = parsed._replace(fragment=previous_fragment) + elif parsed.fragment: + previous_fragment = parsed.fragment + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + + self.rebuild_method(prepared_request, resp) + + # https://github.com/psf/requests/issues/1084 + if resp.status_code not in ( + codes.temporary_redirect, + codes.permanent_redirect, + ): + # https://github.com/psf/requests/issues/3490 + purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding") + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + headers.pop("Cookie", None) + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) + merge_cookies(prepared_request._cookies, self.cookies) + prepared_request.prepare_cookies(prepared_request._cookies) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # A failed tell() sets `_body_position` to `object()`. This non-None + # value ensures `rewindable` will be True, allowing us to raise an + # UnrewindableBodyError, instead of hanging the connection. + rewindable = prepared_request._body_position is not None and ( + "Content-Length" in headers or "Transfer-Encoding" in headers + ) + + # Attempt to rewind consumed file-like object. + if rewindable: + rewind_body(prepared_request) + + # Override the original request. + req = prepared_request + + if yield_requests: + yield req + else: + + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs, + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + # extract redirect url, if any, for the next loop + url = self.get_redirect_target(resp) + yield resp + + def rebuild_auth(self, prepared_request, response): + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + headers = prepared_request.headers + url = prepared_request.url + + if "Authorization" in headers and self.should_strip_auth( + response.request.url, url + ): + # If we get redirected to a new host, we should strip out any + # authentication headers. + del headers["Authorization"] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + def rebuild_proxies(self, prepared_request, proxies): + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + headers = prepared_request.headers + scheme = urlparse(prepared_request.url).scheme + new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env) + + if "Proxy-Authorization" in headers: + del headers["Proxy-Authorization"] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + # urllib3 handles proxy authorization for us in the standard adapter. + # Avoid appending this to TLS tunneled requests where it may be leaked. + if not scheme.startswith('https') and username and password: + headers["Proxy-Authorization"] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != "HEAD": + method = "GET" + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != "HEAD": + method = "GET" + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == "POST": + method = "GET" + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('https://httpbin.org/get') + + + Or as a context manager:: + + >>> with requests.Session() as s: + ... s.get('https://httpbin.org/get') + + """ + + __attrs__ = [ + "headers", + "cookies", + "auth", + "proxies", + "hooks", + "params", + "verify", + "cert", + "adapters", + "stream", + "trust_env", + "max_redirects", + ] + + def __init__(self): + + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request ` sent from this + #: :class:`Session `. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request `. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request `. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request `. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + #: Defaults to `True`, requiring requests to verify the TLS certificate at the + #: remote end. + #: If verify is set to `False`, requests will accept any TLS certificate + #: presented by the server, and will ignore hostname mismatches and/or + #: expired certificates, which will make your application vulnerable to + #: man-in-the-middle (MitM) attacks. + #: Only set this to `False` for testing. + self.verify = True + + #: SSL client certificate default, if String, path to ssl client + #: cert file (.pem). If Tuple, ('cert', 'key') pair. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar `, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount("https://", HTTPAdapter()) + self.mount("http://", HTTPAdapter()) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest ` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request ` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies + ) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting( + request.headers, self.headers, dict_class=CaseInsensitiveDict + ), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request( + self, + method, + url, + params=None, + data=None, + headers=None, + cookies=None, + files=None, + auth=None, + timeout=None, + allow_redirects=True, + proxies=None, + hooks=None, + stream=None, + verify=None, + cert=None, + json=None, + ): + """Constructs a :class:`Request `, prepares it and sends it. + Returns :class:`Response ` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. When set to + ``False``, requests will accept any TLS certificate presented by + the server, and will ignore hostname mismatches and/or expired + certificates, which will make your application vulnerable to + man-in-the-middle (MitM) attacks. Setting verify to ``False`` + may be useful during local development or testing. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + # Create the Request. + req = Request( + method=method.upper(), + url=url, + headers=headers, + files=files, + data=data or {}, + json=json, + params=params or {}, + auth=auth, + cookies=cookies, + hooks=hooks, + ) + prep = self.prepare_request(req) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + "timeout": timeout, + "allow_redirects": allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get(self, url, **kwargs): + r"""Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", True) + return self.request("GET", url, **kwargs) + + def options(self, url, **kwargs): + r"""Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", True) + return self.request("OPTIONS", url, **kwargs) + + def head(self, url, **kwargs): + r"""Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault("allow_redirects", False) + return self.request("HEAD", url, **kwargs) + + def post(self, url, data=None, json=None, **kwargs): + r"""Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("POST", url, data=data, json=json, **kwargs) + + def put(self, url, data=None, **kwargs): + r"""Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("PUT", url, data=data, **kwargs) + + def patch(self, url, data=None, **kwargs): + r"""Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("PATCH", url, data=data, **kwargs) + + def delete(self, url, **kwargs): + r"""Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request("DELETE", url, **kwargs) + + def send(self, request, **kwargs): + """Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault("stream", self.stream) + kwargs.setdefault("verify", self.verify) + kwargs.setdefault("cert", self.cert) + if "proxies" not in kwargs: + kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError("You can only send PreparedRequests.") + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop("allow_redirects", True) + stream = kwargs.get("stream") + hooks = request.hooks + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = preferred_clock() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + elapsed = preferred_clock() - start + r.elapsed = timedelta(seconds=elapsed) + + # Response manipulation hooks + r = dispatch_hook("response", hooks, r, **kwargs) + + # Persist cookies + if r.history: + + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Resolve redirects if allowed. + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + # If redirects aren't being followed, store the response on the Request for Response.next(). + if not allow_redirects: + try: + r._next = next( + self.resolve_redirects(r, request, yield_requests=True, **kwargs) + ) + except StopIteration: + pass + + if not stream: + r.content + + return r + + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + no_proxy = proxies.get("no_proxy") if proxies is not None else None + env_proxies = get_environ_proxies(url, no_proxy=no_proxy) + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration + # and be compatible with cURL. + if verify is True or verify is None: + verify = ( + os.environ.get("REQUESTS_CA_BUNDLE") + or os.environ.get("CURL_CA_BUNDLE") + or verify + ) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert} + + def get_adapter(self, url): + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for (prefix, adapter) in self.adapters.items(): + + if url.lower().startswith(prefix.lower()): + return adapter + + # Nothing matches :-/ + raise InvalidSchema(f"No connection adapters were found for {url!r}") + + def close(self): + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix, adapter): + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by prefix length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self): + state = {attr: getattr(self, attr, None) for attr in self.__attrs__} + return state + + def __setstate__(self, state): + for attr, value in state.items(): + setattr(self, attr, value) + + +def session(): + """ + Returns a :class:`Session` for context-management. + + .. deprecated:: 1.0.0 + + This method has been deprecated since version 1.0.0 and is only kept for + backwards compatibility. New code should use :class:`~requests.sessions.Session` + to create a session. This may be removed at a future date. + + :rtype: Session + """ + return Session() diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/status_codes.py b/.pythonlibs/lib/python3.10/site-packages/requests/status_codes.py new file mode 100644 index 0000000..4bd072b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/status_codes.py @@ -0,0 +1,128 @@ +r""" +The ``codes`` object defines a mapping from common names for HTTP statuses +to their numerical codes, accessible either as attributes or as dictionary +items. + +Example:: + + >>> import requests + >>> requests.codes['temporary_redirect'] + 307 + >>> requests.codes.teapot + 418 + >>> requests.codes['\o/'] + 200 + +Some codes have multiple names, and both upper- and lower-case versions of +the names are allowed. For example, ``codes.ok``, ``codes.OK``, and +``codes.okay`` all correspond to the HTTP status code 200. +""" + +from .structures import LookupDict + +_codes = { + # Informational. + 100: ("continue",), + 101: ("switching_protocols",), + 102: ("processing",), + 103: ("checkpoint",), + 122: ("uri_too_long", "request_uri_too_long"), + 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), + 201: ("created",), + 202: ("accepted",), + 203: ("non_authoritative_info", "non_authoritative_information"), + 204: ("no_content",), + 205: ("reset_content", "reset"), + 206: ("partial_content", "partial"), + 207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"), + 208: ("already_reported",), + 226: ("im_used",), + # Redirection. + 300: ("multiple_choices",), + 301: ("moved_permanently", "moved", "\\o-"), + 302: ("found",), + 303: ("see_other", "other"), + 304: ("not_modified",), + 305: ("use_proxy",), + 306: ("switch_proxy",), + 307: ("temporary_redirect", "temporary_moved", "temporary"), + 308: ( + "permanent_redirect", + "resume_incomplete", + "resume", + ), # "resume" and "resume_incomplete" to be removed in 3.0 + # Client Error. + 400: ("bad_request", "bad"), + 401: ("unauthorized",), + 402: ("payment_required", "payment"), + 403: ("forbidden",), + 404: ("not_found", "-o-"), + 405: ("method_not_allowed", "not_allowed"), + 406: ("not_acceptable",), + 407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"), + 408: ("request_timeout", "timeout"), + 409: ("conflict",), + 410: ("gone",), + 411: ("length_required",), + 412: ("precondition_failed", "precondition"), + 413: ("request_entity_too_large",), + 414: ("request_uri_too_large",), + 415: ("unsupported_media_type", "unsupported_media", "media_type"), + 416: ( + "requested_range_not_satisfiable", + "requested_range", + "range_not_satisfiable", + ), + 417: ("expectation_failed",), + 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), + 421: ("misdirected_request",), + 422: ("unprocessable_entity", "unprocessable"), + 423: ("locked",), + 424: ("failed_dependency", "dependency"), + 425: ("unordered_collection", "unordered"), + 426: ("upgrade_required", "upgrade"), + 428: ("precondition_required", "precondition"), + 429: ("too_many_requests", "too_many"), + 431: ("header_fields_too_large", "fields_too_large"), + 444: ("no_response", "none"), + 449: ("retry_with", "retry"), + 450: ("blocked_by_windows_parental_controls", "parental_controls"), + 451: ("unavailable_for_legal_reasons", "legal_reasons"), + 499: ("client_closed_request",), + # Server Error. + 500: ("internal_server_error", "server_error", "/o\\", "✗"), + 501: ("not_implemented",), + 502: ("bad_gateway",), + 503: ("service_unavailable", "unavailable"), + 504: ("gateway_timeout",), + 505: ("http_version_not_supported", "http_version"), + 506: ("variant_also_negotiates",), + 507: ("insufficient_storage",), + 509: ("bandwidth_limit_exceeded", "bandwidth"), + 510: ("not_extended",), + 511: ("network_authentication_required", "network_auth", "network_authentication"), +} + +codes = LookupDict(name="status_codes") + + +def _init(): + for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith(("\\", "/")): + setattr(codes, title.upper(), code) + + def doc(code): + names = ", ".join(f"``{n}``" for n in _codes[code]) + return "* %d: %s" % (code, names) + + global __doc__ + __doc__ = ( + __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes)) + if __doc__ is not None + else None + ) + + +_init() diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/structures.py b/.pythonlibs/lib/python3.10/site-packages/requests/structures.py new file mode 100644 index 0000000..188e13e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/structures.py @@ -0,0 +1,99 @@ +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +from collections import OrderedDict + +from .compat import Mapping, MutableMapping + + +class CaseInsensitiveDict(MutableMapping): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + def __init__(self, data=None, **kwargs): + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key, value): + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key): + return self._store[key.lower()][1] + + def __delitem__(self, key): + del self._store[key.lower()] + + def __iter__(self): + return (casedkey for casedkey, mappedvalue in self._store.values()) + + def __len__(self): + return len(self._store) + + def lower_items(self): + """Like iteritems(), but with all lowercase keys.""" + return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()) + + def __eq__(self, other): + if isinstance(other, Mapping): + other = CaseInsensitiveDict(other) + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other.lower_items()) + + # Copy is required + def copy(self): + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self): + return str(dict(self.items())) + + +class LookupDict(dict): + """Dictionary lookup object.""" + + def __init__(self, name=None): + self.name = name + super().__init__() + + def __repr__(self): + return f"" + + def __getitem__(self, key): + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + def get(self, key, default=None): + return self.__dict__.get(key, default) diff --git a/.pythonlibs/lib/python3.10/site-packages/requests/utils.py b/.pythonlibs/lib/python3.10/site-packages/requests/utils.py new file mode 100644 index 0000000..a367417 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/requests/utils.py @@ -0,0 +1,1094 @@ +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +import codecs +import contextlib +import io +import os +import re +import socket +import struct +import sys +import tempfile +import warnings +import zipfile +from collections import OrderedDict + +from urllib3.util import make_headers, parse_url + +from . import certs +from .__version__ import __version__ + +# to_native_string is unused here, but imported here for backwards compatibility +from ._internal_utils import ( # noqa: F401 + _HEADER_VALIDATORS_BYTE, + _HEADER_VALIDATORS_STR, + HEADER_VALIDATORS, + to_native_string, +) +from .compat import ( + Mapping, + basestring, + bytes, + getproxies, + getproxies_environment, + integer_types, +) +from .compat import parse_http_list as _parse_list_header +from .compat import ( + proxy_bypass, + proxy_bypass_environment, + quote, + str, + unquote, + urlparse, + urlunparse, +) +from .cookies import cookiejar_from_dict +from .exceptions import ( + FileModeWarning, + InvalidHeader, + InvalidURL, + UnrewindableBodyError, +) +from .structures import CaseInsensitiveDict + +NETRC_FILES = (".netrc", "_netrc") + +DEFAULT_CA_BUNDLE_PATH = certs.where() + +DEFAULT_PORTS = {"http": 80, "https": 443} + +# Ensure that ', ' is used to preserve previous delimiter behavior. +DEFAULT_ACCEPT_ENCODING = ", ".join( + re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) +) + + +if sys.platform == "win32": + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host): + try: + import winreg + except ImportError: + return False + + try: + internetSettings = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", + ) + # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it + proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0]) + # ProxyOverride is almost always a string + proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0] + except (OSError, ValueError): + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(";") + # now check if we match one of the registry values. + for test in proxyOverride: + if test == "": + if "." not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host): # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + +def dict_to_sequence(d): + """Returns an internal sequence dictionary update.""" + + if hasattr(d, "items"): + d = d.items() + + return d + + +def super_len(o): + total_length = None + current_position = 0 + + if hasattr(o, "__len__"): + total_length = len(o) + + elif hasattr(o, "len"): + total_length = o.len + + elif hasattr(o, "fileno"): + try: + fileno = o.fileno() + except (io.UnsupportedOperation, AttributeError): + # AttributeError is a surprising exception, seeing as how we've just checked + # that `hasattr(o, 'fileno')`. It happens for objects obtained via + # `Tarfile.extractfile()`, per issue 5229. + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if "b" not in o.mode: + warnings.warn( + ( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode." + ), + FileModeWarning, + ) + + if hasattr(o, "tell"): + try: + current_position = o.tell() + except OSError: + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + if total_length is not None: + current_position = total_length + else: + if hasattr(o, "seek") and total_length is None: + # StringIO and BytesIO have seek but no usable fileno + try: + # seek to end of file + o.seek(0, 2) + total_length = o.tell() + + # seek back to current position to support + # partially read file-like objects + o.seek(current_position or 0) + except OSError: + total_length = 0 + + if total_length is None: + total_length = 0 + + return max(0, total_length - current_position) + + +def get_netrc_auth(url, raise_errors=False): + """Returns the Requests tuple auth for a given url from netrc.""" + + netrc_file = os.environ.get("NETRC") + if netrc_file is not None: + netrc_locations = (netrc_file,) + else: + netrc_locations = (f"~/{f}" for f in NETRC_FILES) + + try: + from netrc import NetrcParseError, netrc + + netrc_path = None + + for f in netrc_locations: + try: + loc = os.path.expanduser(f) + except KeyError: + # os.path.expanduser can fail when $HOME is undefined and + # getpwuid fails. See https://bugs.python.org/issue20164 & + # https://github.com/psf/requests/issues/1846 + return + + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b":" + if isinstance(url, str): + splitstr = splitstr.decode("ascii") + host = ri.netloc.split(splitstr)[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = 0 if _netrc[0] else 1 + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, OSError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # App Engine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj): + """Tries to guess the filename of the given object.""" + name = getattr(obj, "name", None) + if name and isinstance(name, basestring) and name[0] != "<" and name[-1] != ">": + return os.path.basename(name) + + +def extract_zipped_paths(path): + """Replace nonexistent paths that look like they refer to a member of a zip + archive with the location of an extracted copy of the target, or else + just return the provided path unchanged. + """ + if os.path.exists(path): + # this is already a valid path, no need to do anything further + return path + + # find the first valid part of the provided path and treat that as a zip archive + # assume the rest of the path is the name of a member in the archive + archive, member = os.path.split(path) + while archive and not os.path.exists(archive): + archive, prefix = os.path.split(archive) + if not prefix: + # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split), + # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users + break + member = "/".join([prefix, member]) + + if not zipfile.is_zipfile(archive): + return path + + zip_file = zipfile.ZipFile(archive) + if member not in zip_file.namelist(): + return path + + # we have a valid zip archive and a valid member of that archive + tmp = tempfile.gettempdir() + extracted_path = os.path.join(tmp, member.split("/")[-1]) + if not os.path.exists(extracted_path): + # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition + with atomic_open(extracted_path) as file_handler: + file_handler.write(zip_file.read(member)) + return extracted_path + + +@contextlib.contextmanager +def atomic_open(filename): + """Write a file to the disk in an atomic fashion""" + tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) + try: + with os.fdopen(tmp_descriptor, "wb") as tmp_handler: + yield tmp_handler + os.replace(tmp_name, filename) + except BaseException: + os.remove(tmp_name) + raise + + +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError("cannot encode objects that are not 2-tuples") + + return OrderedDict(value) + + +def to_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError("cannot encode objects that are not 2-tuples") + + if isinstance(value, Mapping): + value = value.items() + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value): + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value): + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result = {} + for item in _parse_list_header(value): + if "=" not in item: + result[item] = None + continue + name, value = item.split("=", 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value, is_filename=False): + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != "\\\\": + return value.replace("\\\\", "\\").replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj): + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {} + + for cookie in cj: + cookie_dict[cookie.name] = cookie.value + + return cookie_dict + + +def add_dict_to_cookiejar(cj, cookie_dict): + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + return cookiejar_from_dict(cookie_dict, cj) + + +def get_encodings_from_content(content): + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn( + ( + "In requests 3.0, get_encodings_from_content will be removed. For " + "more information, please see the discussion on issue #2266. (This" + " warning should only appear once.)" + ), + DeprecationWarning, + ) + + charset_re = re.compile(r']', flags=re.I) + pragma_re = re.compile(r']', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return ( + charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content) + ) + + +def _parse_content_type_header(header): + """Returns content type and parameters from given header + + :param header: string + :return: tuple containing content type and dictionary of + parameters + """ + + tokens = header.split(";") + content_type, params = tokens[0].strip(), tokens[1:] + params_dict = {} + items_to_strip = "\"' " + + for param in params: + param = param.strip() + if param: + key, value = param, True + index_of_equals = param.find("=") + if index_of_equals != -1: + key = param[:index_of_equals].strip(items_to_strip) + value = param[index_of_equals + 1 :].strip(items_to_strip) + params_dict[key.lower()] = value + return content_type, params_dict + + +def get_encoding_from_headers(headers): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get("content-type") + + if not content_type: + return None + + content_type, params = _parse_content_type_header(content_type) + + if "charset" in params: + return params["charset"].strip("'\"") + + if "text" in content_type: + return "ISO-8859-1" + + if "application/json" in content_type: + # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset + return "utf-8" + + +def stream_decode_response_unicode(iterator, r): + """Stream decodes an iterator.""" + + if r.encoding is None: + yield from iterator + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace") + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b"", final=True) + if rv: + yield rv + + +def iter_slices(string, slice_length): + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos : pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r): + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn( + ( + "In requests 3.0, get_unicode_from_response will be removed. For " + "more information, please see the discussion on issue #2266. (This" + " warning should only appear once.)" + ), + DeprecationWarning, + ) + + tried_encodings = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding, errors="replace") + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~" +) + + +def unquote_unreserved(uri): + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split("%") + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL(f"Invalid percent-escape sequence: '{h}'") + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = f"%{parts[i]}" + else: + parts[i] = f"%{parts[i]}" + return "".join(parts) + + +def requote_uri(uri): + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip, net): + """This function allows you to check if an IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0] + netaddr, bits = net.split("/") + netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask): + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack(">I", bits)) + + +def is_ipv4_address(string_ip): + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except OSError: + return False + return True + + +def is_valid_cidr(string_network): + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count("/") == 1: + try: + mask = int(string_network.split("/")[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split("/")[0]) + except OSError: + return False + else: + return False + return True + + +@contextlib.contextmanager +def set_environ(env_name, value): + """Set the environment variable 'env_name' to 'value' + + Save previous value, yield, and then restore the previous value stored in + the environment variable 'env_name'. + + If 'value' is None, do nothing""" + value_changed = value is not None + if value_changed: + old_value = os.environ.get(env_name) + os.environ[env_name] = value + try: + yield + finally: + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value + + +def should_bypass_proxies(url, no_proxy): + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + # Prioritize lowercase environment variables over uppercase + # to keep a consistent behaviour with other http projects (curl, wget). + def get_proxy(key): + return os.environ.get(key) or os.environ.get(key.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy_arg = no_proxy + if no_proxy is None: + no_proxy = get_proxy("no_proxy") + parsed = urlparse(url) + + if parsed.hostname is None: + # URLs don't always have hostnames, e.g. file:/// urls. + return True + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the hostname, both with and without the port. + no_proxy = (host for host in no_proxy.replace(" ", "").split(",") if host) + + if is_ipv4_address(parsed.hostname): + for proxy_ip in no_proxy: + if is_valid_cidr(proxy_ip): + if address_in_network(parsed.hostname, proxy_ip): + return True + elif parsed.hostname == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + host_with_port = parsed.hostname + if parsed.port: + host_with_port += f":{parsed.port}" + + for host in no_proxy: + if parsed.hostname.endswith(host) or host_with_port.endswith(host): + # The URL does match something in no_proxy, so we don't want + # to apply the proxies on this URL. + return True + + with set_environ("no_proxy", no_proxy_arg): + # parsed.hostname can be `None` in cases such as a file URI. + try: + bypass = proxy_bypass(parsed.hostname) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url, no_proxy=None): + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url, no_proxy=no_proxy): + return {} + else: + return getproxies() + + +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get(urlparts.scheme, proxies.get("all")) + + proxy_keys = [ + urlparts.scheme + "://" + urlparts.hostname, + urlparts.scheme, + "all://" + urlparts.hostname, + "all", + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def resolve_proxies(request, proxies, trust_env=True): + """This method takes proxy information from a request and configuration + input to resolve a mapping of target proxies. This will consider settings + such a NO_PROXY to strip proxy configurations. + + :param request: Request or PreparedRequest + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + :param trust_env: Boolean declaring whether to trust environment configs + + :rtype: dict + """ + proxies = proxies if proxies is not None else {} + url = request.url + scheme = urlparse(url).scheme + no_proxy = proxies.get("no_proxy") + new_proxies = proxies.copy() + + if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy): + environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) + + proxy = environ_proxies.get(scheme, environ_proxies.get("all")) + + if proxy: + new_proxies.setdefault(scheme, proxy) + return new_proxies + + +def default_user_agent(name="python-requests"): + """ + Return a string representing the default user agent. + + :rtype: str + """ + return f"{name}/{__version__}" + + +def default_headers(): + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict( + { + "User-Agent": default_user_agent(), + "Accept-Encoding": DEFAULT_ACCEPT_ENCODING, + "Accept": "*/*", + "Connection": "keep-alive", + } + ) + + +def parse_header_links(value): + """Return a list of parsed link headers proxies. + + i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" + + :rtype: list + """ + + links = [] + + replace_chars = " '\"" + + value = value.strip(replace_chars) + if not value: + return links + + for val in re.split(", *<", value): + try: + url, params = val.split(";", 1) + except ValueError: + url, params = val, "" + + link = {"url": url.strip("<> '\"")} + + for param in params.split(";"): + try: + key, value = param.split("=") + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = "\x00".encode("ascii") # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data): + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): + return "utf-32" # BOM included + if sample[:3] == codecs.BOM_UTF8: + return "utf-8-sig" # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return "utf-16" # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return "utf-8" + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return "utf-16-be" + if sample[1::2] == _null2: # 2nd and 4th are null + return "utf-16-le" + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return "utf-32-be" + if sample[1:] == _null3: + return "utf-32-le" + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url, new_scheme): + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + parsed = parse_url(url) + scheme, auth, host, port, path, query, fragment = parsed + + # A defect in urlparse determines that there isn't a netloc present in some + # urls. We previously assumed parsing was overly cautious, and swapped the + # netloc and path. Due to a lack of tests on the original defect, this is + # maintained with parse_url for backwards compatibility. + netloc = parsed.netloc + if not netloc: + netloc, path = path, netloc + + if auth: + # parse_url doesn't provide the netloc with auth + # so we'll add it ourselves. + netloc = "@".join([auth, netloc]) + if scheme is None: + scheme = new_scheme + if path is None: + path = "" + + return urlunparse((scheme, netloc, path, "", query, fragment)) + + +def get_auth_from_url(url): + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + auth = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + auth = ("", "") + + return auth + + +def check_header_validity(header): + """Verifies that header parts don't contain leading whitespace + reserved characters, or return characters. + + :param header: tuple, in the format (name, value). + """ + name, value = header + _validate_header_part(header, name, 0) + _validate_header_part(header, value, 1) + + +def _validate_header_part(header, header_part, header_validator_index): + if isinstance(header_part, str): + validator = _HEADER_VALIDATORS_STR[header_validator_index] + elif isinstance(header_part, bytes): + validator = _HEADER_VALIDATORS_BYTE[header_validator_index] + else: + raise InvalidHeader( + f"Header part ({header_part!r}) from {header} " + f"must be of type str or bytes, not {type(header_part)}" + ) + + if not validator.match(header_part): + header_kind = "name" if header_validator_index == 0 else "value" + raise InvalidHeader( + f"Invalid leading whitespace, reserved character(s), or return" + f"character(s) in header {header_kind}: {header_part!r}" + ) + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit("@", 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, "")) + + +def rewind_body(prepared_request): + """Move file pointer back to its recorded starting position + so it can be read again on redirect. + """ + body_seek = getattr(prepared_request.body, "seek", None) + if body_seek is not None and isinstance( + prepared_request._body_position, integer_types + ): + try: + body_seek(prepared_request._body_position) + except OSError: + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect." + ) + else: + raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE new file mode 100644 index 0000000..51f3442 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the +licenses found in LICENSE.APACHE2 or LICENSE.MIT. Contributions to are +made under the terms of *both* these licenses. diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT new file mode 100644 index 0000000..b8bb971 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/METADATA new file mode 100644 index 0000000..88968ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/METADATA @@ -0,0 +1,104 @@ +Metadata-Version: 2.1 +Name: sniffio +Version: 1.3.1 +Summary: Sniff out which async library your code is running under +Author-email: "Nathaniel J. Smith" +License: MIT OR Apache-2.0 +Project-URL: Homepage, https://github.com/python-trio/sniffio +Project-URL: Documentation, https://sniffio.readthedocs.io/ +Project-URL: Changelog, https://sniffio.readthedocs.io/en/latest/history.html +Keywords: async,trio,asyncio +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Framework :: Trio +Classifier: Framework :: AsyncIO +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Intended Audience :: Developers +Classifier: Development Status :: 5 - Production/Stable +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: LICENSE.APACHE2 +License-File: LICENSE.MIT + +.. image:: https://img.shields.io/badge/chat-join%20now-blue.svg + :target: https://gitter.im/python-trio/general + :alt: Join chatroom + +.. image:: https://img.shields.io/badge/docs-read%20now-blue.svg + :target: https://sniffio.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/pypi/v/sniffio.svg + :target: https://pypi.org/project/sniffio + :alt: Latest PyPi version + +.. image:: https://img.shields.io/conda/vn/conda-forge/sniffio.svg + :target: https://anaconda.org/conda-forge/sniffio + :alt: Latest conda-forge version + +.. image:: https://travis-ci.org/python-trio/sniffio.svg?branch=master + :target: https://travis-ci.org/python-trio/sniffio + :alt: Automated test status + +.. image:: https://codecov.io/gh/python-trio/sniffio/branch/master/graph/badge.svg + :target: https://codecov.io/gh/python-trio/sniffio + :alt: Test coverage + +================================================================= +sniffio: Sniff out which async library your code is running under +================================================================= + +You're writing a library. You've decided to be ambitious, and support +multiple async I/O packages, like `Trio +`__, and `asyncio +`__, and ... You've +written a bunch of clever code to handle all the differences. But... +how do you know *which* piece of clever code to run? + +This is a tiny package whose only purpose is to let you detect which +async library your code is running under. + +* Documentation: https://sniffio.readthedocs.io + +* Bug tracker and source code: https://github.com/python-trio/sniffio + +* License: MIT or Apache License 2.0, your choice + +* Contributor guide: https://trio.readthedocs.io/en/latest/contributing.html + +* Code of conduct: Contributors are requested to follow our `code of + conduct + `_ + in all project spaces. + +This library is maintained by the Trio project, as a service to the +async Python community as a whole. + + +Quickstart +---------- + +.. code-block:: python3 + + from sniffio import current_async_library + import trio + import asyncio + + async def print_library(): + library = current_async_library() + print("This is:", library) + + # Prints "This is trio" + trio.run(print_library) + + # Prints "This is asyncio" + asyncio.run(print_library()) + +For more details, including how to add support to new async libraries, +`please peruse our fine manual `__. diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/RECORD new file mode 100644 index 0000000..6ebf07b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/RECORD @@ -0,0 +1,19 @@ +sniffio-1.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +sniffio-1.3.1.dist-info/LICENSE,sha256=ZSyHhIjRRWNh4Iw_hgf9e6WYkqFBA9Fczk_5PIW1zIs,185 +sniffio-1.3.1.dist-info/LICENSE.APACHE2,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +sniffio-1.3.1.dist-info/LICENSE.MIT,sha256=Pm2uVV65J4f8gtHUg1Vnf0VMf2Wus40_nnK_mj2vA0s,1046 +sniffio-1.3.1.dist-info/METADATA,sha256=CzGLVwmO3sz1heYKiJprantcQIbzqapi7_dqHTzuEtk,3875 +sniffio-1.3.1.dist-info/RECORD,, +sniffio-1.3.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +sniffio-1.3.1.dist-info/top_level.txt,sha256=v9UJXGs5CyddCVeAqXkQiWOrpp6Wtx6GeRrPt9-jjHg,8 +sniffio/__init__.py,sha256=9WJEJlXu7yluP0YtI5SQ9M9OTQfbNHkadarK1vXGDPM,335 +sniffio/__pycache__/__init__.cpython-310.pyc,, +sniffio/__pycache__/_impl.cpython-310.pyc,, +sniffio/__pycache__/_version.cpython-310.pyc,, +sniffio/_impl.py,sha256=UmUFMZpiuOrcjnuHhuYiYMxeCNWfqu9kBlaPf0xk6X8,2843 +sniffio/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sniffio/_tests/__pycache__/__init__.cpython-310.pyc,, +sniffio/_tests/__pycache__/test_sniffio.cpython-310.pyc,, +sniffio/_tests/test_sniffio.py,sha256=MMJZZJjQrUi95RANNM-a_55BZquA_gv4rHU1pevcTCM,2058 +sniffio/_version.py,sha256=iVes5xwsHeRzQDexBaAhyx_taNt2ucfA7CWAo4QDt6Q,89 +sniffio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/top_level.txt new file mode 100644 index 0000000..01c6502 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio-1.3.1.dist-info/top_level.txt @@ -0,0 +1 @@ +sniffio diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/__init__.py b/.pythonlibs/lib/python3.10/site-packages/sniffio/__init__.py new file mode 100644 index 0000000..63f2f19 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio/__init__.py @@ -0,0 +1,17 @@ +"""Top-level package for sniffio.""" + +__all__ = [ + "current_async_library", + "AsyncLibraryNotFoundError", + "current_async_library_cvar", + "thread_local", +] + +from ._version import __version__ + +from ._impl import ( + current_async_library, + AsyncLibraryNotFoundError, + current_async_library_cvar, + thread_local, +) diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c92f13c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_impl.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_impl.cpython-310.pyc new file mode 100644 index 0000000..4ff0030 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_impl.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_version.cpython-310.pyc new file mode 100644 index 0000000..50465f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/sniffio/__pycache__/_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_impl.py b/.pythonlibs/lib/python3.10/site-packages/sniffio/_impl.py new file mode 100644 index 0000000..c1a7bbf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio/_impl.py @@ -0,0 +1,95 @@ +from contextvars import ContextVar +from typing import Optional +import sys +import threading + +current_async_library_cvar = ContextVar( + "current_async_library_cvar", default=None +) # type: ContextVar[Optional[str]] + + +class _ThreadLocal(threading.local): + # Since threading.local provides no explicit mechanism is for setting + # a default for a value, a custom class with a class attribute is used + # instead. + name = None # type: Optional[str] + + +thread_local = _ThreadLocal() + + +class AsyncLibraryNotFoundError(RuntimeError): + pass + + +def current_async_library() -> str: + """Detect which async library is currently running. + + The following libraries are currently supported: + + ================ =========== ============================ + Library Requires Magic string + ================ =========== ============================ + **Trio** Trio v0.6+ ``"trio"`` + **Curio** - ``"curio"`` + **asyncio** ``"asyncio"`` + **Trio-asyncio** v0.8.2+ ``"trio"`` or ``"asyncio"``, + depending on current mode + ================ =========== ============================ + + Returns: + A string like ``"trio"``. + + Raises: + AsyncLibraryNotFoundError: if called from synchronous context, + or if the current async library was not recognized. + + Examples: + + .. code-block:: python3 + + from sniffio import current_async_library + + async def generic_sleep(seconds): + library = current_async_library() + if library == "trio": + import trio + await trio.sleep(seconds) + elif library == "asyncio": + import asyncio + await asyncio.sleep(seconds) + # ... and so on ... + else: + raise RuntimeError(f"Unsupported library {library!r}") + + """ + value = thread_local.name + if value is not None: + return value + + value = current_async_library_cvar.get() + if value is not None: + return value + + # Need to sniff for asyncio + if "asyncio" in sys.modules: + import asyncio + try: + current_task = asyncio.current_task # type: ignore[attr-defined] + except AttributeError: + current_task = asyncio.Task.current_task # type: ignore[attr-defined] + try: + if current_task() is not None: + return "asyncio" + except RuntimeError: + pass + + # Sniff for curio (for now) + if 'curio' in sys.modules: + from curio.meta import curio_running + if curio_running(): + return 'curio' + + raise AsyncLibraryNotFoundError( + "unknown async library, or not in async context" + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__init__.py b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..850181e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/test_sniffio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/test_sniffio.cpython-310.pyc new file mode 100644 index 0000000..f1f98b1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/__pycache__/test_sniffio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/test_sniffio.py b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/test_sniffio.py new file mode 100644 index 0000000..02945a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio/_tests/test_sniffio.py @@ -0,0 +1,84 @@ +import os +import sys + +import pytest + +from .. import ( + current_async_library, AsyncLibraryNotFoundError, + current_async_library_cvar, thread_local +) + + +def test_basics_cvar(): + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + token = current_async_library_cvar.set("generic-lib") + try: + assert current_async_library() == "generic-lib" + finally: + current_async_library_cvar.reset(token) + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + +def test_basics_tlocal(): + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + old_name, thread_local.name = thread_local.name, "generic-lib" + try: + assert current_async_library() == "generic-lib" + finally: + thread_local.name = old_name + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + +def test_asyncio(): + import asyncio + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + ran = [] + + async def this_is_asyncio(): + assert current_async_library() == "asyncio" + # Call it a second time to exercise the caching logic + assert current_async_library() == "asyncio" + ran.append(True) + + asyncio.run(this_is_asyncio()) + assert ran == [True] + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + +@pytest.mark.skipif( + sys.version_info >= (3, 12), + reason= + "curio broken on 3.12 (https://github.com/python-trio/sniffio/pull/42)", +) +def test_curio(): + import curio + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() + + ran = [] + + async def this_is_curio(): + assert current_async_library() == "curio" + # Call it a second time to exercise the caching logic + assert current_async_library() == "curio" + ran.append(True) + + curio.run(this_is_curio) + assert ran == [True] + + with pytest.raises(AsyncLibraryNotFoundError): + current_async_library() diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/_version.py b/.pythonlibs/lib/python3.10/site-packages/sniffio/_version.py new file mode 100644 index 0000000..0495d10 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/sniffio/_version.py @@ -0,0 +1,3 @@ +# This file is imported from __init__.py and exec'd from setup.py + +__version__ = "1.3.1" diff --git a/.pythonlibs/lib/python3.10/site-packages/sniffio/py.typed b/.pythonlibs/lib/python3.10/site-packages/sniffio/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/LICENCE b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/LICENCE new file mode 100644 index 0000000..a8922b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/LICENCE @@ -0,0 +1,49 @@ +`tqdm` is a product of collaborative work. +Unless otherwise stated, all authors (see commit logs) retain copyright +for their respective work, and release the work under the MIT licence +(text below). + +Exceptions or notable authors are listed below +in reverse chronological order: + +* files: * + MPL-2.0 2015-2024 (c) Casper da Costa-Luis + [casperdcl](https://github.com/casperdcl). +* files: tqdm/_tqdm.py + MIT 2016 (c) [PR #96] on behalf of Google Inc. +* files: tqdm/_tqdm.py README.rst .gitignore + MIT 2013 (c) Noam Yorav-Raphael, original author. + +[PR #96]: https://github.com/tqdm/tqdm/pull/96 + + +Mozilla Public Licence (MPL) v. 2.0 - Exhibit A +----------------------------------------------- + +This Source Code Form is subject to the terms of the +Mozilla Public License, v. 2.0. +If a copy of the MPL was not distributed with this project, +You can obtain one at https://mozilla.org/MPL/2.0/. + + +MIT License (MIT) +----------------- + +Copyright (c) 2013 noamraph + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/METADATA new file mode 100644 index 0000000..3dd9013 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/METADATA @@ -0,0 +1,1590 @@ +Metadata-Version: 2.1 +Name: tqdm +Version: 4.66.2 +Summary: Fast, Extensible Progress Meter +Maintainer-email: tqdm developers +License: MPL-2.0 AND MIT +Project-URL: homepage, https://tqdm.github.io +Project-URL: repository, https://github.com/tqdm/tqdm +Project-URL: changelog, https://tqdm.github.io/releases +Project-URL: wiki, https://github.com/tqdm/tqdm/wiki +Keywords: progressbar,progressmeter,progress,bar,meter,rate,eta,console,terminal,time +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Environment :: MacOS X +Classifier: Environment :: Other Environment +Classifier: Environment :: Win32 (MS Windows) +Classifier: Environment :: X11 Applications +Classifier: Framework :: IPython +Classifier: Framework :: Jupyter +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: Other Audience +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Operating System :: MacOS +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft +Classifier: Operating System :: Microsoft :: MS-DOS +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: SunOS/Solaris +Classifier: Operating System :: Unix +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation +Classifier: Programming Language :: Python :: Implementation :: IronPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Unix Shell +Classifier: Topic :: Desktop Environment +Classifier: Topic :: Education :: Computer Aided Instruction (CAI) +Classifier: Topic :: Education :: Testing +Classifier: Topic :: Office/Business +Classifier: Topic :: Other/Nonlisted Topic +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Pre-processors +Classifier: Topic :: Software Development :: User Interfaces +Classifier: Topic :: System :: Installation/Setup +Classifier: Topic :: System :: Logging +Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Shells +Classifier: Topic :: Terminals +Classifier: Topic :: Utilities +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENCE +Requires-Dist: colorama ; platform_system == "Windows" +Provides-Extra: dev +Requires-Dist: pytest >=6 ; extra == 'dev' +Requires-Dist: pytest-cov ; extra == 'dev' +Requires-Dist: pytest-timeout ; extra == 'dev' +Requires-Dist: pytest-xdist ; extra == 'dev' +Provides-Extra: notebook +Requires-Dist: ipywidgets >=6 ; extra == 'notebook' +Provides-Extra: slack +Requires-Dist: slack-sdk ; extra == 'slack' +Provides-Extra: telegram +Requires-Dist: requests ; extra == 'telegram' + +|Logo| + +tqdm +==== + +|Py-Versions| |Versions| |Conda-Forge-Status| |Docker| |Snapcraft| + +|Build-Status| |Coverage-Status| |Branch-Coverage-Status| |Codacy-Grade| |Libraries-Rank| |PyPI-Downloads| + +|LICENCE| |OpenHub-Status| |binder-demo| |awesome-python| + +``tqdm`` derives from the Arabic word *taqaddum* (تقدّم) which can mean "progress," +and is an abbreviation for "I love you so much" in Spanish (*te quiero demasiado*). + +Instantly make your loops show a smart progress meter - just wrap any +iterable with ``tqdm(iterable)``, and you're done! + +.. code:: python + + from tqdm import tqdm + for i in tqdm(range(10000)): + ... + +``76%|████████████████████████        | 7568/10000 [00:33<00:10, 229.00it/s]`` + +``trange(N)`` can be also used as a convenient shortcut for +``tqdm(range(N))``. + +|Screenshot| + |Video| |Slides| |Merch| + +It can also be executed as a module with pipes: + +.. code:: sh + + $ seq 9999999 | tqdm --bytes | wc -l + 75.2MB [00:00, 217MB/s] + 9999999 + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 32%|██████████▍ | 8.89G/27.9G [00:42<01:31, 223MB/s] + +Overhead is low -- about 60ns per iteration (80ns with ``tqdm.gui``), and is +unit tested against performance regression. +By comparison, the well-established +`ProgressBar `__ has +an 800ns/iter overhead. + +In addition to its low overhead, ``tqdm`` uses smart algorithms to predict +the remaining time and to skip unnecessary iteration displays, which allows +for a negligible overhead in most cases. + +``tqdm`` works on any platform +(Linux, Windows, Mac, FreeBSD, NetBSD, Solaris/SunOS), +in any console or in a GUI, and is also friendly with IPython/Jupyter notebooks. + +``tqdm`` does not require any dependencies (not even ``curses``!), just +Python and an environment supporting ``carriage return \r`` and +``line feed \n`` control characters. + +------------------------------------------ + +.. contents:: Table of contents + :backlinks: top + :local: + + +Installation +------------ + +Latest PyPI stable release +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|Versions| |PyPI-Downloads| |Libraries-Dependents| + +.. code:: sh + + pip install tqdm + +Latest development release on GitHub +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|GitHub-Status| |GitHub-Stars| |GitHub-Commits| |GitHub-Forks| |GitHub-Updated| + +Pull and install pre-release ``devel`` branch: + +.. code:: sh + + pip install "git+https://github.com/tqdm/tqdm.git@devel#egg=tqdm" + +Latest Conda release +~~~~~~~~~~~~~~~~~~~~ + +|Conda-Forge-Status| + +.. code:: sh + + conda install -c conda-forge tqdm + +Latest Snapcraft release +~~~~~~~~~~~~~~~~~~~~~~~~ + +|Snapcraft| + +There are 3 channels to choose from: + +.. code:: sh + + snap install tqdm # implies --stable, i.e. latest tagged release + snap install tqdm --candidate # master branch + snap install tqdm --edge # devel branch + +Note that ``snap`` binaries are purely for CLI use (not ``import``-able), and +automatically set up ``bash`` tab-completion. + +Latest Docker release +~~~~~~~~~~~~~~~~~~~~~ + +|Docker| + +.. code:: sh + + docker pull tqdm/tqdm + docker run -i --rm tqdm/tqdm --help + +Other +~~~~~ + +There are other (unofficial) places where ``tqdm`` may be downloaded, particularly for CLI use: + +|Repology| + +.. |Repology| image:: https://repology.org/badge/tiny-repos/python:tqdm.svg + :target: https://repology.org/project/python:tqdm/versions + +Changelog +--------- + +The list of all changes is available either on GitHub's Releases: +|GitHub-Status|, on the +`wiki `__, or on the +`website `__. + + +Usage +----- + +``tqdm`` is very versatile and can be used in a number of ways. +The three main ones are given below. + +Iterable-based +~~~~~~~~~~~~~~ + +Wrap ``tqdm()`` around any iterable: + +.. code:: python + + from tqdm import tqdm + from time import sleep + + text = "" + for char in tqdm(["a", "b", "c", "d"]): + sleep(0.25) + text = text + char + +``trange(i)`` is a special optimised instance of ``tqdm(range(i))``: + +.. code:: python + + from tqdm import trange + + for i in trange(100): + sleep(0.01) + +Instantiation outside of the loop allows for manual control over ``tqdm()``: + +.. code:: python + + pbar = tqdm(["a", "b", "c", "d"]) + for char in pbar: + sleep(0.25) + pbar.set_description("Processing %s" % char) + +Manual +~~~~~~ + +Manual control of ``tqdm()`` updates using a ``with`` statement: + +.. code:: python + + with tqdm(total=100) as pbar: + for i in range(10): + sleep(0.1) + pbar.update(10) + +If the optional variable ``total`` (or an iterable with ``len()``) is +provided, predictive stats are displayed. + +``with`` is also optional (you can just assign ``tqdm()`` to a variable, +but in this case don't forget to ``del`` or ``close()`` at the end: + +.. code:: python + + pbar = tqdm(total=100) + for i in range(10): + sleep(0.1) + pbar.update(10) + pbar.close() + +Module +~~~~~~ + +Perhaps the most wonderful use of ``tqdm`` is in a script or on the command +line. Simply inserting ``tqdm`` (or ``python -m tqdm``) between pipes will pass +through all ``stdin`` to ``stdout`` while printing progress to ``stderr``. + +The example below demonstrate counting the number of lines in all Python files +in the current directory, with timing information included. + +.. code:: sh + + $ time find . -name '*.py' -type f -exec cat \{} \; | wc -l + 857365 + + real 0m3.458s + user 0m0.274s + sys 0m3.325s + + $ time find . -name '*.py' -type f -exec cat \{} \; | tqdm | wc -l + 857366it [00:03, 246471.31it/s] + 857365 + + real 0m3.585s + user 0m0.862s + sys 0m3.358s + +Note that the usual arguments for ``tqdm`` can also be specified. + +.. code:: sh + + $ find . -name '*.py' -type f -exec cat \{} \; | + tqdm --unit loc --unit_scale --total 857366 >> /dev/null + 100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s] + +Backing up a large directory? + +.. code:: sh + + $ tar -zcf - docs/ | tqdm --bytes --total `du -sb docs/ | cut -f1` \ + > backup.tgz + 44%|██████████████▊ | 153M/352M [00:14<00:18, 11.0MB/s] + +This can be beautified further: + +.. code:: sh + + $ BYTES=$(du -sb docs/ | cut -f1) + $ tar -cf - docs/ \ + | tqdm --bytes --total "$BYTES" --desc Processing | gzip \ + | tqdm --bytes --total "$BYTES" --desc Compressed --position 1 \ + > ~/backup.tgz + Processing: 100%|██████████████████████| 352M/352M [00:14<00:00, 30.2MB/s] + Compressed: 42%|█████████▎ | 148M/352M [00:14<00:19, 10.9MB/s] + +Or done on a file level using 7-zip: + +.. code:: sh + + $ 7z a -bd -r backup.7z docs/ | grep Compressing \ + | tqdm --total $(find docs/ -type f | wc -l) --unit files \ + | grep -v Compressing + 100%|██████████████████████████▉| 15327/15327 [01:00<00:00, 712.96files/s] + +Pre-existing CLI programs already outputting basic progress information will +benefit from ``tqdm``'s ``--update`` and ``--update_to`` flags: + +.. code:: sh + + $ seq 3 0.1 5 | tqdm --total 5 --update_to --null + 100%|████████████████████████████████████| 5.0/5 [00:00<00:00, 9673.21it/s] + $ seq 10 | tqdm --update --null # 1 + 2 + ... + 10 = 55 iterations + 55it [00:00, 90006.52it/s] + +FAQ and Known Issues +-------------------- + +|GitHub-Issues| + +The most common issues relate to excessive output on multiple lines, instead +of a neat one-line progress bar. + +- Consoles in general: require support for carriage return (``CR``, ``\r``). + + * Some cloud logging consoles which don't support ``\r`` properly + (`cloudwatch `__, + `K8s `__) may benefit from + ``export TQDM_POSITION=-1``. + +- Nested progress bars: + + * Consoles in general: require support for moving cursors up to the + previous line. For example, + `IDLE `__, + `ConEmu `__ and + `PyCharm `__ (also + `here `__, + `here `__, and + `here `__) + lack full support. + * Windows: additionally may require the Python module ``colorama`` + to ensure nested bars stay within their respective lines. + +- Unicode: + + * Environments which report that they support unicode will have solid smooth + progressbars. The fallback is an ``ascii``-only bar. + * Windows consoles often only partially support unicode and thus + `often require explicit ascii=True `__ + (also `here `__). This is due to + either normal-width unicode characters being incorrectly displayed as + "wide", or some unicode characters not rendering. + +- Wrapping generators: + + * Generator wrapper functions tend to hide the length of iterables. + ``tqdm`` does not. + * Replace ``tqdm(enumerate(...))`` with ``enumerate(tqdm(...))`` or + ``tqdm(enumerate(x), total=len(x), ...)``. + The same applies to ``numpy.ndenumerate``. + * Replace ``tqdm(zip(a, b))`` with ``zip(tqdm(a), b)`` or even + ``zip(tqdm(a), tqdm(b))``. + * The same applies to ``itertools``. + * Some useful convenience functions can be found under ``tqdm.contrib``. + +- `No intermediate output in docker-compose `__: + use ``docker-compose run`` instead of ``docker-compose up`` and ``tty: true``. + +- Overriding defaults via environment variables: + e.g. in CI/cloud jobs, ``export TQDM_MININTERVAL=5`` to avoid log spam. + This override logic is handled by the ``tqdm.utils.envwrap`` decorator + (useful independent of ``tqdm``). + +If you come across any other difficulties, browse and file |GitHub-Issues|. + +Documentation +------------- + +|Py-Versions| |README-Hits| (Since 19 May 2016) + +.. code:: python + + class tqdm(): + """ + Decorate an iterable object, returning an iterator which acts exactly + like the original iterable, but prints a dynamically updating + progressbar every time a value is requested. + """ + + @envwrap("TQDM_") # override defaults via env vars + def __init__(self, iterable=None, desc=None, total=None, leave=True, + file=None, ncols=None, mininterval=0.1, + maxinterval=10.0, miniters=None, ascii=None, disable=False, + unit='it', unit_scale=False, dynamic_ncols=False, + smoothing=0.3, bar_format=None, initial=0, position=None, + postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0): + +Parameters +~~~~~~~~~~ + +* iterable : iterable, optional + Iterable to decorate with a progressbar. + Leave blank to manually manage the updates. +* desc : str, optional + Prefix for the progressbar. +* total : int or float, optional + The number of expected iterations. If unspecified, + len(iterable) is used if possible. If float("inf") or as a last + resort, only basic progress statistics are displayed + (no ETA, no progressbar). + If ``gui`` is True and this parameter needs subsequent updating, + specify an initial arbitrary large positive number, + e.g. 9e9. +* leave : bool, optional + If [default: True], keeps all traces of the progressbar + upon termination of iteration. + If ``None``, will leave only if ``position`` is ``0``. +* file : ``io.TextIOWrapper`` or ``io.StringIO``, optional + Specifies where to output the progress messages + (default: sys.stderr). Uses ``file.write(str)`` and ``file.flush()`` + methods. For encoding, see ``write_bytes``. +* ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes the progressbar to stay within this bound. + If unspecified, attempts to use environment width. The + fallback is a meter width of 10 and no limit for the counter and + statistics. If 0, will not print any meter (only stats). +* mininterval : float, optional + Minimum progress display update interval [default: 0.1] seconds. +* maxinterval : float, optional + Maximum progress display update interval [default: 10] seconds. + Automatically adjusts ``miniters`` to correspond to ``mininterval`` + after long display update lag. Only works if ``dynamic_miniters`` + or monitor thread is enabled. +* miniters : int or float, optional + Minimum progress display update interval, in iterations. + If 0 and ``dynamic_miniters``, will automatically adjust to equal + ``mininterval`` (more CPU efficient, good for tight loops). + If > 0, will skip display of specified number of iterations. + Tweak this and ``mininterval`` to get very efficient loops. + If your progress is erratic with both fast and slow iterations + (network, skipping items, etc) you should set miniters=1. +* ascii : bool or str, optional + If unspecified or False, use unicode (smooth blocks) to fill + the meter. The fallback is to use ASCII characters " 123456789#". +* disable : bool, optional + Whether to disable the entire progressbar wrapper + [default: False]. If set to None, disable on non-TTY. +* unit : str, optional + String that will be used to define the unit of each iteration + [default: it]. +* unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled + automatically and a metric prefix following the + International System of Units standard will be added + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale ``total`` and ``n``. +* dynamic_ncols : bool, optional + If set, constantly alters ``ncols`` and ``nrows`` to the + environment (allowing for window resizes) [default: False]. +* smoothing : float, optional + Exponential moving average smoothing factor for speed estimates + (ignored in GUI mode). Ranges from 0 (average speed) to 1 + (current/instantaneous speed) [default: 0.3]. +* bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. +* initial : int or float, optional + The initial counter value. Useful when restarting a progress + bar [default: 0]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. +* position : int, optional + Specify the line offset to print this bar (starting from 0) + Automatic if unspecified. + Useful to manage multiple bars at once (eg, from threads). +* postfix : dict or ``*``, optional + Specify additional stats to display at the end of the bar. + Calls ``set_postfix(**postfix)`` if possible (dict). +* unit_divisor : float, optional + [default: 1000], ignored unless ``unit_scale`` is True. +* write_bytes : bool, optional + Whether to write bytes. If (default: False) will write unicode. +* lock_args : tuple, optional + Passed to ``refresh`` for intermediate output + (initialisation, iterating, and updating). +* nrows : int, optional + The screen height. If specified, hides nested bars outside this + bound. If unspecified, attempts to use environment height. + The fallback is 20. +* colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). +* delay : float, optional + Don't display until [default: 0] seconds have elapsed. + +Extra CLI Options +~~~~~~~~~~~~~~~~~ + +* delim : chr, optional + Delimiting character [default: '\n']. Use '\0' for null. + N.B.: on Windows systems, Python converts '\n' to '\r\n'. +* buf_size : int, optional + String buffer size in bytes [default: 256] + used when ``delim`` is specified. +* bytes : bool, optional + If true, will count bytes, ignore ``delim``, and default + ``unit_scale`` to True, ``unit_divisor`` to 1024, and ``unit`` to 'B'. +* tee : bool, optional + If true, passes ``stdin`` to both ``stderr`` and ``stdout``. +* update : bool, optional + If true, will treat input as newly elapsed iterations, + i.e. numbers to pass to ``update()``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* update_to : bool, optional + If true, will treat input as total elapsed iterations, + i.e. numbers to assign to ``self.n``. Note that this is slow + (~2e5 it/s) since every input must be decoded as a number. +* null : bool, optional + If true, will discard input (no stdout). +* manpath : str, optional + Directory in which to install tqdm man pages. +* comppath : str, optional + Directory in which to place tqdm completion. +* log : str, optional + CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET. + +Returns +~~~~~~~ + +* out : decorated iterator. + +.. code:: python + + class tqdm(): + def update(self, n=1): + """ + Manually update the progress bar, useful for streams + such as reading files. + E.g.: + >>> t = tqdm(total=filesize) # Initialise + >>> for current_buffer in stream: + ... ... + ... t.update(len(current_buffer)) + >>> t.close() + The last line is highly recommended, but possibly not necessary if + ``t.update()`` will be called in such a way that ``filesize`` will be + exactly reached and printed. + + Parameters + ---------- + n : int or float, optional + Increment to add to the internal counter of iterations + [default: 1]. If using float, consider specifying ``{n:.3f}`` + or similar in ``bar_format``, or specifying ``unit_scale``. + + Returns + ------- + out : bool or None + True if a ``display()`` was triggered. + """ + + def close(self): + """Cleanup and (if leave=False) close the progressbar.""" + + def clear(self, nomove=False): + """Clear current bar display.""" + + def refresh(self): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + If ``True``, does not lock. + If [default: ``False``]: calls ``acquire()`` on internal lock. + lock_args : tuple, optional + Passed to internal lock's ``acquire()``. + If specified, will only ``display()`` if ``acquire()`` returns ``True``. + """ + + def unpause(self): + """Restart tqdm timer from last print time.""" + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Consider combining with ``leave=True``. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + + def set_description(self, desc=None, refresh=True): + """ + Set/modify description of the progress bar. + + Parameters + ---------- + desc : str, optional + refresh : bool, optional + Forces refresh [default: True]. + """ + + def set_postfix(self, ordered_dict=None, refresh=True, **tqdm_kwargs): + """ + Set/modify postfix (additional stats) + with automatic formatting based on datatype. + + Parameters + ---------- + ordered_dict : dict or OrderedDict, optional + refresh : bool, optional + Forces refresh [default: True]. + kwargs : dict, optional + """ + + @classmethod + def write(cls, s, file=sys.stdout, end="\n"): + """Print a message via tqdm (without overlap with bars).""" + + @property + def format_dict(self): + """Public API for read-only member access.""" + + def display(self, msg=None, pos=None): + """ + Use ``self.sp`` to display ``msg`` in the specified ``pos``. + + Consider overloading this function when inheriting to use e.g.: + ``self.some_frontend(**self.format_dict)`` instead of ``self.sp``. + + Parameters + ---------- + msg : str, optional. What to display (default: ``repr(self)``). + pos : int, optional. Position to ``moveto`` + (default: ``abs(self.pos)``). + """ + + @classmethod + @contextmanager + def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs): + """ + stream : file-like object. + method : str, "read" or "write". The result of ``read()`` and + the first argument of ``write()`` should have a ``len()``. + + >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj: + ... while True: + ... chunk = fobj.read(chunk_size) + ... if not chunk: + ... break + """ + + @classmethod + def pandas(cls, *targs, **tqdm_kwargs): + """Registers the current `tqdm` class with `pandas`.""" + + def trange(*args, **tqdm_kwargs): + """Shortcut for `tqdm(range(*args), **tqdm_kwargs)`.""" + +Convenience Functions +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + def tqdm.contrib.tenumerate(iterable, start=0, total=None, + tqdm_class=tqdm.auto.tqdm, **tqdm_kwargs): + """Equivalent of `numpy.ndenumerate` or builtin `enumerate`.""" + + def tqdm.contrib.tzip(iter1, *iter2plus, **tqdm_kwargs): + """Equivalent of builtin `zip`.""" + + def tqdm.contrib.tmap(function, *sequences, **tqdm_kwargs): + """Equivalent of builtin `map`.""" + +Submodules +~~~~~~~~~~ + +.. code:: python + + class tqdm.notebook.tqdm(tqdm.tqdm): + """IPython/Jupyter Notebook widget.""" + + class tqdm.auto.tqdm(tqdm.tqdm): + """Automatically chooses beween `tqdm.notebook` and `tqdm.tqdm`.""" + + class tqdm.asyncio.tqdm(tqdm.tqdm): + """Asynchronous version.""" + @classmethod + def as_completed(cls, fs, *, loop=None, timeout=None, total=None, + **tqdm_kwargs): + """Wrapper for `asyncio.as_completed`.""" + + class tqdm.gui.tqdm(tqdm.tqdm): + """Matplotlib GUI version.""" + + class tqdm.tk.tqdm(tqdm.tqdm): + """Tkinter GUI version.""" + + class tqdm.rich.tqdm(tqdm.tqdm): + """`rich.progress` version.""" + + class tqdm.keras.TqdmCallback(keras.callbacks.Callback): + """Keras callback for epoch and batch progress.""" + + class tqdm.dask.TqdmCallback(dask.callbacks.Callback): + """Dask callback for task progress.""" + + +``contrib`` ++++++++++++ + +The ``tqdm.contrib`` package also contains experimental modules: + +- ``tqdm.contrib.itertools``: Thin wrappers around ``itertools`` +- ``tqdm.contrib.concurrent``: Thin wrappers around ``concurrent.futures`` +- ``tqdm.contrib.slack``: Posts to `Slack `__ bots +- ``tqdm.contrib.discord``: Posts to `Discord `__ bots +- ``tqdm.contrib.telegram``: Posts to `Telegram `__ bots +- ``tqdm.contrib.bells``: Automagically enables all optional features + + * ``auto``, ``pandas``, ``slack``, ``discord``, ``telegram`` + +Examples and Advanced Usage +--------------------------- + +- See the `examples `__ + folder; +- import the module and run ``help()``; +- consult the `wiki `__; + + * this has an + `excellent article `__ + on how to make a **great** progressbar; + +- check out the `slides from PyData London `__, or +- run the |binder-demo|. + +Description and additional stats +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Custom information can be displayed and updated dynamically on ``tqdm`` bars +with the ``desc`` and ``postfix`` arguments: + +.. code:: python + + from tqdm import tqdm, trange + from random import random, randint + from time import sleep + + with trange(10) as t: + for i in t: + # Description will be displayed on the left + t.set_description('GEN %i' % i) + # Postfix will be displayed on the right, + # formatted automatically based on argument's datatype + t.set_postfix(loss=random(), gen=randint(1,999), str='h', + lst=[1, 2]) + sleep(0.1) + + with tqdm(total=10, bar_format="{postfix[0]} {postfix[1][value]:>8.2g}", + postfix=["Batch", {"value": 0}]) as t: + for i in range(10): + sleep(0.1) + t.postfix[1]["value"] = i / 2 + t.update() + +Points to remember when using ``{postfix[...]}`` in the ``bar_format`` string: + +- ``postfix`` also needs to be passed as an initial argument in a compatible + format, and +- ``postfix`` will be auto-converted to a string if it is a ``dict``-like + object. To prevent this behaviour, insert an extra item into the dictionary + where the key is not a string. + +Additional ``bar_format`` parameters may also be defined by overriding +``format_dict``, and the bar itself may be modified using ``ascii``: + +.. code:: python + + from tqdm import tqdm + class TqdmExtraFormat(tqdm): + """Provides a `total_time` format parameter""" + @property + def format_dict(self): + d = super(TqdmExtraFormat, self).format_dict + total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) + d.update(total_time=self.format_interval(total_time) + " in total") + return d + + for i in TqdmExtraFormat( + range(9), ascii=" .oO0", + bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"): + if i == 4: + break + +.. code:: + + 00:00 in total: 44%|0000. | 4/9 [00:00<00:00, 962.93it/s] + +Note that ``{bar}`` also supports a format specifier ``[width][type]``. + +- ``width`` + + * unspecified (default): automatic to fill ``ncols`` + * ``int >= 0``: fixed width overriding ``ncols`` logic + * ``int < 0``: subtract from the automatic default + +- ``type`` + + * ``a``: ascii (``ascii=True`` override) + * ``u``: unicode (``ascii=False`` override) + * ``b``: blank (``ascii=" "`` override) + +This means a fixed bar with right-justified text may be created by using: +``bar_format="{l_bar}{bar:10}|{bar:-10b}right-justified"`` + +Nested progress bars +~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` supports nested progress bars. Here's an example: + +.. code:: python + + from tqdm.auto import trange + from time import sleep + + for i in trange(4, desc='1st loop'): + for j in trange(5, desc='2nd loop'): + for k in trange(50, desc='3rd loop', leave=False): + sleep(0.01) + +For manual control over positioning (e.g. for multi-processing use), +you may specify ``position=n`` where ``n=0`` for the outermost bar, +``n=1`` for the next, and so on. +However, it's best to check if ``tqdm`` can work without manual ``position`` +first. + +.. code:: python + + from time import sleep + from tqdm import trange, tqdm + from multiprocessing import Pool, RLock, freeze_support + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text, position=n): + sleep(interval) + + if __name__ == '__main__': + freeze_support() # for Windows support + tqdm.set_lock(RLock()) # for managing output contention + p = Pool(initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) + p.map(progresser, L) + +Note that in Python 3, ``tqdm.write`` is thread-safe: + +.. code:: python + + from time import sleep + from tqdm import tqdm, trange + from concurrent.futures import ThreadPoolExecutor + + L = list(range(9)) + + def progresser(n): + interval = 0.001 / (n + 2) + total = 5000 + text = f"#{n}, est. {interval * total:<04.2}s" + for _ in trange(total, desc=text): + sleep(interval) + if n == 6: + tqdm.write("n == 6 completed.") + tqdm.write("`tqdm.write()` is thread-safe in py3!") + + if __name__ == '__main__': + with ThreadPoolExecutor() as p: + p.map(progresser, L) + +Hooks and callbacks +~~~~~~~~~~~~~~~~~~~ + +``tqdm`` can easily support callbacks/hooks and manual updates. +Here's an example with ``urllib``: + +**``urllib.urlretrieve`` documentation** + + | [...] + | If present, the hook function will be called once + | on establishment of the network connection and once after each block read + | thereafter. The hook will be passed three arguments; a count of blocks + | transferred so far, a block size in bytes, and the total size of the file. + | [...] + +.. code:: python + + import urllib, os + from tqdm import tqdm + urllib = getattr(urllib, 'request', urllib) + + class TqdmUpTo(tqdm): + """Provides `update_to(n)` which uses `tqdm.update(delta_n)`.""" + def update_to(self, b=1, bsize=1, tsize=None): + """ + b : int, optional + Number of blocks transferred so far [default: 1]. + bsize : int, optional + Size of each block (in tqdm units) [default: 1]. + tsize : int, optional + Total size (in tqdm units). If [default: None] remains unchanged. + """ + if tsize is not None: + self.total = tsize + return self.update(b * bsize - self.n) # also sets self.n = b * bsize + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + with TqdmUpTo(unit='B', unit_scale=True, unit_divisor=1024, miniters=1, + desc=eg_link.split('/')[-1]) as t: # all optional kwargs + urllib.urlretrieve(eg_link, filename=os.devnull, + reporthook=t.update_to, data=None) + t.total = t.n + +Inspired by `twine#242 `__. +Functional alternative in +`examples/tqdm_wget.py `__. + +It is recommend to use ``miniters=1`` whenever there is potentially +large differences in iteration speed (e.g. downloading a file over +a patchy connection). + +**Wrapping read/write methods** + +To measure throughput through a file-like object's ``read`` or ``write`` +methods, use ``CallbackIOWrapper``: + +.. code:: python + + from tqdm.auto import tqdm + from tqdm.utils import CallbackIOWrapper + + with tqdm(total=file_obj.size, + unit='B', unit_scale=True, unit_divisor=1024) as t: + fobj = CallbackIOWrapper(t.update, file_obj, "read") + while True: + chunk = fobj.read(chunk_size) + if not chunk: + break + t.reset() + # ... continue to use `t` for something else + +Alternatively, use the even simpler ``wrapattr`` convenience function, +which would condense both the ``urllib`` and ``CallbackIOWrapper`` examples +down to: + +.. code:: python + + import urllib, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = getattr(urllib, 'request', urllib).urlopen(eg_link) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=getattr(response, 'length', None)) as fout: + for chunk in response: + fout.write(chunk) + +The ``requests`` equivalent is nearly identical: + +.. code:: python + + import requests, os + from tqdm import tqdm + + eg_link = "https://caspersci.uk.to/matryoshka.zip" + response = requests.get(eg_link, stream=True) + with tqdm.wrapattr(open(os.devnull, "wb"), "write", + miniters=1, desc=eg_link.split('/')[-1], + total=int(response.headers.get('content-length', 0))) as fout: + for chunk in response.iter_content(chunk_size=4096): + fout.write(chunk) + +**Custom callback** + +``tqdm`` is known for intelligently skipping unnecessary displays. To make a +custom callback take advantage of this, simply use the return value of +``update()``. This is set to ``True`` if a ``display()`` was triggered. + +.. code:: python + + from tqdm.auto import tqdm as std_tqdm + + def external_callback(*args, **kwargs): + ... + + class TqdmExt(std_tqdm): + def update(self, n=1): + displayed = super(TqdmExt, self).update(n) + if displayed: + external_callback(**self.format_dict) + return displayed + +``asyncio`` +~~~~~~~~~~~ + +Note that ``break`` isn't currently caught by asynchronous iterators. +This means that ``tqdm`` cannot clean up after itself in this case: + +.. code:: python + + from tqdm.asyncio import tqdm + + async for i in tqdm(range(9)): + if i == 2: + break + +Instead, either call ``pbar.close()`` manually or use the context manager syntax: + +.. code:: python + + from tqdm.asyncio import tqdm + + with tqdm(range(9)) as pbar: + async for i in pbar: + if i == 2: + break + +Pandas Integration +~~~~~~~~~~~~~~~~~~ + +Due to popular demand we've added support for ``pandas`` -- here's an example +for ``DataFrame.progress_apply`` and ``DataFrameGroupBy.progress_apply``: + +.. code:: python + + import pandas as pd + import numpy as np + from tqdm import tqdm + + df = pd.DataFrame(np.random.randint(0, 100, (100000, 6))) + + # Register `pandas.progress_apply` and `pandas.Series.map_apply` with `tqdm` + # (can use `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, optional kwargs, etc.) + tqdm.pandas(desc="my bar!") + + # Now you can use `progress_apply` instead of `apply` + # and `progress_map` instead of `map` + df.progress_apply(lambda x: x**2) + # can also groupby: + # df.groupby(0).progress_apply(lambda x: x**2) + +In case you're interested in how this works (and how to modify it for your +own callbacks), see the +`examples `__ +folder or import the module and run ``help()``. + +Keras Integration +~~~~~~~~~~~~~~~~~ + +A ``keras`` callback is also available: + +.. code:: python + + from tqdm.keras import TqdmCallback + + ... + + model.fit(..., verbose=0, callbacks=[TqdmCallback()]) + +Dask Integration +~~~~~~~~~~~~~~~~ + +A ``dask`` callback is also available: + +.. code:: python + + from tqdm.dask import TqdmCallback + + with TqdmCallback(desc="compute"): + ... + arr.compute() + + # or use callback globally + cb = TqdmCallback(desc="global") + cb.register() + arr.compute() + +IPython/Jupyter Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +IPython/Jupyter is supported via the ``tqdm.notebook`` submodule: + +.. code:: python + + from tqdm.notebook import trange, tqdm + from time import sleep + + for i in trange(3, desc='1st loop'): + for j in tqdm(range(100), desc='2nd loop'): + sleep(0.01) + +In addition to ``tqdm`` features, the submodule provides a native Jupyter +widget (compatible with IPython v1-v4 and Jupyter), fully working nested bars +and colour hints (blue: normal, green: completed, red: error/interrupt, +light blue: no ETA); as demonstrated below. + +|Screenshot-Jupyter1| +|Screenshot-Jupyter2| +|Screenshot-Jupyter3| + +The ``notebook`` version supports percentage or pixels for overall width +(e.g.: ``ncols='100%'`` or ``ncols='480px'``). + +It is also possible to let ``tqdm`` automatically choose between +console or notebook versions by using the ``autonotebook`` submodule: + +.. code:: python + + from tqdm.autonotebook import tqdm + tqdm.pandas() + +Note that this will issue a ``TqdmExperimentalWarning`` if run in a notebook +since it is not meant to be possible to distinguish between ``jupyter notebook`` +and ``jupyter console``. Use ``auto`` instead of ``autonotebook`` to suppress +this warning. + +Note that notebooks will display the bar in the cell where it was created. +This may be a different cell from the one where it is used. +If this is not desired, either + +- delay the creation of the bar to the cell where it must be displayed, or +- create the bar with ``display=False``, and in a later cell call + ``display(bar.container)``: + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm(..., display=False) + +.. code:: python + + # different cell + display(pbar.container) + +The ``keras`` callback has a ``display()`` method which can be used likewise: + +.. code:: python + + from tqdm.keras import TqdmCallback + cbk = TqdmCallback(display=False) + +.. code:: python + + # different cell + cbk.display() + model.fit(..., verbose=0, callbacks=[cbk]) + +Another possibility is to have a single bar (near the top of the notebook) +which is constantly re-used (using ``reset()`` rather than ``close()``). +For this reason, the notebook version (unlike the CLI version) does not +automatically call ``close()`` upon ``Exception``. + +.. code:: python + + from tqdm.notebook import tqdm + pbar = tqdm() + +.. code:: python + + # different cell + iterable = range(100) + pbar.reset(total=len(iterable)) # initialise with new `total` + for i in iterable: + pbar.update() + pbar.refresh() # force print final status but don't `close()` + +Custom Integration +~~~~~~~~~~~~~~~~~~ + +To change the default arguments (such as making ``dynamic_ncols=True``), +simply use built-in Python magic: + +.. code:: python + + from functools import partial + from tqdm import tqdm as std_tqdm + tqdm = partial(std_tqdm, dynamic_ncols=True) + +For further customisation, +``tqdm`` may be inherited from to create custom callbacks (as with the +``TqdmUpTo`` example `above <#hooks-and-callbacks>`__) or for custom frontends +(e.g. GUIs such as notebook or plotting packages). In the latter case: + +1. ``def __init__()`` to call ``super().__init__(..., gui=True)`` to disable + terminal ``status_printer`` creation. +2. Redefine: ``close()``, ``clear()``, ``display()``. + +Consider overloading ``display()`` to use e.g. +``self.frontend(**self.format_dict)`` instead of ``self.sp(repr(self))``. + +Some submodule examples of inheritance: + +- `tqdm/notebook.py `__ +- `tqdm/gui.py `__ +- `tqdm/tk.py `__ +- `tqdm/contrib/slack.py `__ +- `tqdm/contrib/discord.py `__ +- `tqdm/contrib/telegram.py `__ + +Dynamic Monitor/Meter +~~~~~~~~~~~~~~~~~~~~~ + +You can use a ``tqdm`` as a meter which is not monotonically increasing. +This could be because ``n`` decreases (e.g. a CPU usage monitor) or ``total`` +changes. + +One example would be recursively searching for files. The ``total`` is the +number of objects found so far, while ``n`` is the number of those objects which +are files (rather than folders): + +.. code:: python + + from tqdm import tqdm + import os.path + + def find_files_recursively(path, show_progress=True): + files = [] + # total=1 assumes `path` is a file + t = tqdm(total=1, unit="file", disable=not show_progress) + if not os.path.exists(path): + raise IOError("Cannot find:" + path) + + def append_found_file(f): + files.append(f) + t.update() + + def list_found_dir(path): + """returns os.listdir(path) assuming os.path.isdir(path)""" + listing = os.listdir(path) + # subtract 1 since a "file" we found was actually this directory + t.total += len(listing) - 1 + # fancy way to give info without forcing a refresh + t.set_postfix(dir=path[-10:], refresh=False) + t.update(0) # may trigger a refresh + return listing + + def recursively_search(path): + if os.path.isdir(path): + for f in list_found_dir(path): + recursively_search(os.path.join(path, f)) + else: + append_found_file(path) + + recursively_search(path) + t.set_postfix(dir=path) + t.close() + return files + +Using ``update(0)`` is a handy way to let ``tqdm`` decide when to trigger a +display refresh to avoid console spamming. + +Writing messages +~~~~~~~~~~~~~~~~ + +This is a work in progress (see +`#737 `__). + +Since ``tqdm`` uses a simple printing mechanism to display progress bars, +you should not write any message in the terminal using ``print()`` while +a progressbar is open. + +To write messages in the terminal without any collision with ``tqdm`` bar +display, a ``.write()`` method is provided: + +.. code:: python + + from tqdm.auto import tqdm, trange + from time import sleep + + bar = trange(10) + for i in bar: + # Print using tqdm class method .write() + sleep(0.1) + if not (i % 3): + tqdm.write("Done task %i" % i) + # Can also use bar.write() + +By default, this will print to standard output ``sys.stdout``. but you can +specify any file-like object using the ``file`` argument. For example, this +can be used to redirect the messages writing to a log file or class. + +Redirecting writing +~~~~~~~~~~~~~~~~~~~ + +If using a library that can print messages to the console, editing the library +by replacing ``print()`` with ``tqdm.write()`` may not be desirable. +In that case, redirecting ``sys.stdout`` to ``tqdm.write()`` is an option. + +To redirect ``sys.stdout``, create a file-like class that will write +any input string to ``tqdm.write()``, and supply the arguments +``file=sys.stdout, dynamic_ncols=True``. + +A reusable canonical example is given below: + +.. code:: python + + from time import sleep + import contextlib + import sys + from tqdm import tqdm + from tqdm.contrib import DummyTqdmFile + + + @contextlib.contextmanager + def std_out_err_redirect_tqdm(): + orig_out_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = map(DummyTqdmFile, orig_out_err) + yield orig_out_err[0] + # Relay exceptions + except Exception as exc: + raise exc + # Always restore sys.stdout/err if necessary + finally: + sys.stdout, sys.stderr = orig_out_err + + def some_fun(i): + print("Fee, fi, fo,".split()[i]) + + # Redirect stdout to tqdm.write() (don't forget the `as save_stdout`) + with std_out_err_redirect_tqdm() as orig_stdout: + # tqdm needs the original stdout + # and dynamic_ncols=True to autodetect console width + for i in tqdm(range(3), file=orig_stdout, dynamic_ncols=True): + sleep(.5) + some_fun(i) + + # After the `with`, printing is restored + print("Done!") + +Redirecting ``logging`` +~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to ``sys.stdout``/``sys.stderr`` as detailed above, console ``logging`` +may also be redirected to ``tqdm.write()``. + +Warning: if also redirecting ``sys.stdout``/``sys.stderr``, make sure to +redirect ``logging`` first if needed. + +Helper methods are available in ``tqdm.contrib.logging``. For example: + +.. code:: python + + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + +Monitoring thread, intervals and miniters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``tqdm`` implements a few tricks to increase efficiency and reduce overhead. + +- Avoid unnecessary frequent bar refreshing: ``mininterval`` defines how long + to wait between each refresh. ``tqdm`` always gets updated in the background, + but it will display only every ``mininterval``. +- Reduce number of calls to check system clock/time. +- ``mininterval`` is more intuitive to configure than ``miniters``. + A clever adjustment system ``dynamic_miniters`` will automatically adjust + ``miniters`` to the amount of iterations that fit into time ``mininterval``. + Essentially, ``tqdm`` will check if it's time to print without actually + checking time. This behaviour can be still be bypassed by manually setting + ``miniters``. + +However, consider a case with a combination of fast and slow iterations. +After a few fast iterations, ``dynamic_miniters`` will set ``miniters`` to a +large number. When iteration rate subsequently slows, ``miniters`` will +remain large and thus reduce display update frequency. To address this: + +- ``maxinterval`` defines the maximum time between display refreshes. + A concurrent monitoring thread checks for overdue updates and forces one + where necessary. + +The monitoring thread should not have a noticeable overhead, and guarantees +updates at least every 10 seconds by default. +This value can be directly changed by setting the ``monitor_interval`` of +any ``tqdm`` instance (i.e. ``t = tqdm.tqdm(...); t.monitor_interval = 2``). +The monitor thread may be disabled application-wide by setting +``tqdm.tqdm.monitor_interval = 0`` before instantiation of any ``tqdm`` bar. + + +Merch +----- + +You can buy `tqdm branded merch `__ now! + +Contributions +------------- + +|GitHub-Commits| |GitHub-Issues| |GitHub-PRs| |OpenHub-Status| |GitHub-Contributions| |CII Best Practices| + +All source code is hosted on `GitHub `__. +Contributions are welcome. + +See the +`CONTRIBUTING `__ +file for more information. + +Developers who have made significant contributions, ranked by *SLoC* +(surviving lines of code, +`git fame `__ ``-wMC --excl '\.(png|gif|jpg)$'``), +are: + +==================== ======================================================== ==== ================================ +Name ID SLoC Notes +==================== ======================================================== ==== ================================ +Casper da Costa-Luis `casperdcl `__ ~80% primary maintainer |Gift-Casper| +Stephen Larroque `lrq3000 `__ ~9% team member +Martin Zugnoni `martinzugnoni `__ ~3% +Daniel Ecer `de-code `__ ~2% +Richard Sheridan `richardsheridan `__ ~1% +Guangshuo Chen `chengs `__ ~1% +Helio Machado `0x2b3bfa0 `__ ~1% +Kyle Altendorf `altendky `__ <1% +Noam Yorav-Raphael `noamraph `__ <1% original author +Matthew Stevens `mjstevens777 `__ <1% +Hadrien Mary `hadim `__ <1% team member +Mikhail Korobov `kmike `__ <1% team member +==================== ======================================================== ==== ================================ + +Ports to Other Languages +~~~~~~~~~~~~~~~~~~~~~~~~ + +A list is available on +`this wiki page `__. + + +LICENCE +------- + +Open Source (OSI approved): |LICENCE| + +Citation information: |DOI| + +|README-Hits| (Since 19 May 2016) + +.. |Logo| image:: https://tqdm.github.io/img/logo.gif +.. |Screenshot| image:: https://tqdm.github.io/img/tqdm.gif +.. |Video| image:: https://tqdm.github.io/img/video.jpg + :target: https://tqdm.github.io/video +.. |Slides| image:: https://tqdm.github.io/img/slides.jpg + :target: https://tqdm.github.io/PyData2019/slides.html +.. |Merch| image:: https://tqdm.github.io/img/merch.jpg + :target: https://tqdm.github.io/merch +.. |Build-Status| image:: https://img.shields.io/github/actions/workflow/status/tqdm/tqdm/test.yml?branch=master&label=tqdm&logo=GitHub + :target: https://github.com/tqdm/tqdm/actions/workflows/test.yml +.. |Coverage-Status| image:: https://img.shields.io/coveralls/github/tqdm/tqdm/master?logo=coveralls + :target: https://coveralls.io/github/tqdm/tqdm +.. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg + :target: https://codecov.io/gh/tqdm/tqdm +.. |Codacy-Grade| image:: https://app.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 + :target: https://www.codacy.com/gh/tqdm/tqdm/dashboard +.. |CII Best Practices| image:: https://bestpractices.coreinfrastructure.org/projects/3264/badge + :target: https://bestpractices.coreinfrastructure.org/projects/3264 +.. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/releases +.. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/network +.. |GitHub-Stars| image:: https://img.shields.io/github/stars/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/stargazers +.. |GitHub-Commits| image:: https://img.shields.io/github/commit-activity/y/tqdm/tqdm.svg?logo=git&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/commit-activity +.. |GitHub-Issues| image:: https://img.shields.io/github/issues-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/issues?q= +.. |GitHub-PRs| image:: https://img.shields.io/github/issues-pr-closed/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/pulls +.. |GitHub-Contributions| image:: https://img.shields.io/github/contributors/tqdm/tqdm.svg?logo=github&logoColor=white + :target: https://github.com/tqdm/tqdm/graphs/contributors +.. |GitHub-Updated| image:: https://img.shields.io/github/last-commit/tqdm/tqdm/master.svg?logo=github&logoColor=white&label=pushed + :target: https://github.com/tqdm/tqdm/pulse +.. |Gift-Casper| image:: https://img.shields.io/badge/dynamic/json.svg?color=ff69b4&label=gifts%20received&prefix=%C2%A3&query=%24..sum&url=https%3A%2F%2Fcaspersci.uk.to%2Fgifts.json + :target: https://cdcl.ml/sponsor +.. |Versions| image:: https://img.shields.io/pypi/v/tqdm.svg + :target: https://tqdm.github.io/releases +.. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/tqdm.svg?label=pypi%20downloads&logo=PyPI&logoColor=white + :target: https://pepy.tech/project/tqdm +.. |Py-Versions| image:: https://img.shields.io/pypi/pyversions/tqdm.svg?logo=python&logoColor=white + :target: https://pypi.org/project/tqdm +.. |Conda-Forge-Status| image:: https://img.shields.io/conda/v/conda-forge/tqdm.svg?label=conda-forge&logo=conda-forge + :target: https://anaconda.org/conda-forge/tqdm +.. |Snapcraft| image:: https://img.shields.io/badge/snap-install-82BEA0.svg?logo=snapcraft + :target: https://snapcraft.io/tqdm +.. |Docker| image:: https://img.shields.io/badge/docker-pull-blue.svg?logo=docker&logoColor=white + :target: https://hub.docker.com/r/tqdm/tqdm +.. |Libraries-Rank| image:: https://img.shields.io/librariesio/sourcerank/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://libraries.io/pypi/tqdm +.. |Libraries-Dependents| image:: https://img.shields.io/librariesio/dependent-repos/pypi/tqdm.svg?logo=koding&logoColor=white + :target: https://github.com/tqdm/tqdm/network/dependents +.. |OpenHub-Status| image:: https://www.openhub.net/p/tqdm/widgets/project_thin_badge?format=gif + :target: https://www.openhub.net/p/tqdm?ref=Thin+badge +.. |awesome-python| image:: https://awesome.re/mentioned-badge.svg + :target: https://github.com/vinta/awesome-python +.. |LICENCE| image:: https://img.shields.io/pypi/l/tqdm.svg + :target: https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE +.. |DOI| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg + :target: https://doi.org/10.5281/zenodo.595120 +.. |binder-demo| image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/tqdm/tqdm/master?filepath=DEMO.ipynb +.. |Screenshot-Jupyter1| image:: https://tqdm.github.io/img/jupyter-1.gif +.. |Screenshot-Jupyter2| image:: https://tqdm.github.io/img/jupyter-2.gif +.. |Screenshot-Jupyter3| image:: https://tqdm.github.io/img/jupyter-3.gif +.. |README-Hits| image:: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&style=social&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif + :target: https://caspersci.uk.to/cgi-bin/hits.cgi?q=tqdm&a=plot&r=https://github.com/tqdm/tqdm&l=https://tqdm.github.io/img/favicon.png&f=https://tqdm.github.io/img/logo.gif&style=social diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/RECORD new file mode 100644 index 0000000..ac5dad3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/RECORD @@ -0,0 +1,74 @@ +../../../bin/tqdm,sha256=2FNX16FWNYUwAYDUbkfKckh3xgew88tC6RyO7q3yLIA,211 +tqdm-4.66.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +tqdm-4.66.2.dist-info/LICENCE,sha256=3DMlLoKQFeOxUAhvubOkD2rW-zLC9GEM6BL6Z301mGo,1985 +tqdm-4.66.2.dist-info/METADATA,sha256=hqX0to0oQdiQK_4I-kKeZTMZVBuSLqQlR-stAlZWUe4,57587 +tqdm-4.66.2.dist-info/RECORD,, +tqdm-4.66.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +tqdm-4.66.2.dist-info/entry_points.txt,sha256=ReJCH7Ui3Zyh6M16E4OhsZ1oU7WtMXCfbtoyBhGO29Y,39 +tqdm-4.66.2.dist-info/top_level.txt,sha256=NLiUJNfmc9At15s7JURiwvqMEjUi9G5PMGRrmMYzNSM,5 +tqdm/__init__.py,sha256=9mQNYSSqP99JasubEC1POJLMmhkkBH6cJZxPIR5G2pQ,1572 +tqdm/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +tqdm/__pycache__/__init__.cpython-310.pyc,, +tqdm/__pycache__/__main__.cpython-310.pyc,, +tqdm/__pycache__/_dist_ver.cpython-310.pyc,, +tqdm/__pycache__/_main.cpython-310.pyc,, +tqdm/__pycache__/_monitor.cpython-310.pyc,, +tqdm/__pycache__/_tqdm.cpython-310.pyc,, +tqdm/__pycache__/_tqdm_gui.cpython-310.pyc,, +tqdm/__pycache__/_tqdm_notebook.cpython-310.pyc,, +tqdm/__pycache__/_tqdm_pandas.cpython-310.pyc,, +tqdm/__pycache__/_utils.cpython-310.pyc,, +tqdm/__pycache__/asyncio.cpython-310.pyc,, +tqdm/__pycache__/auto.cpython-310.pyc,, +tqdm/__pycache__/autonotebook.cpython-310.pyc,, +tqdm/__pycache__/cli.cpython-310.pyc,, +tqdm/__pycache__/dask.cpython-310.pyc,, +tqdm/__pycache__/gui.cpython-310.pyc,, +tqdm/__pycache__/keras.cpython-310.pyc,, +tqdm/__pycache__/notebook.cpython-310.pyc,, +tqdm/__pycache__/rich.cpython-310.pyc,, +tqdm/__pycache__/std.cpython-310.pyc,, +tqdm/__pycache__/tk.cpython-310.pyc,, +tqdm/__pycache__/utils.cpython-310.pyc,, +tqdm/__pycache__/version.cpython-310.pyc,, +tqdm/_dist_ver.py,sha256=WtMQthlgvWIN7EQ42ET8BE1M6YXdXKafzqbKD3PLmGk,23 +tqdm/_main.py,sha256=9ySvgmi_2Sw4CAo5UDW0Q2dxfTryboEWGHohfCJz0sA,283 +tqdm/_monitor.py,sha256=Uku-DPWgzJ7dO5CK08xKJK-E_F6qQ-JB3ksuXczSYR0,3699 +tqdm/_tqdm.py,sha256=LfLCuJ6bpsVo9xilmtBXyEm1vGnUCFrliW85j3J-nD4,283 +tqdm/_tqdm_gui.py,sha256=03Hc8KayxJveieI5-0-2NGiDpLvw9jZekofJUV7CCwk,287 +tqdm/_tqdm_notebook.py,sha256=BuHiLuxu6uEfZFaPJW3RPpPaxaVctEQA3kdSJSDL1hw,307 +tqdm/_tqdm_pandas.py,sha256=c9jptUgigN6axRDhRd4Rif98Tmxeopc1nFNFhIpbFUE,888 +tqdm/_utils.py,sha256=_4E73bfDj4f1s3sM42NLHNrZDOkijZoWq-n6xWLkdZ8,553 +tqdm/asyncio.py,sha256=WcWbVEjc1-GxqnN0BVntDuwYR31JN9SV7ERZhEz8kKo,2775 +tqdm/auto.py,sha256=nDZflj6p2zKkjBCNBourrhS81zYfZy1_dQvbckrdW8o,871 +tqdm/autonotebook.py,sha256=Yb9F5uaiBPhfbDDFpbtoG8I2YUw3uQJ89rUDLbfR6ws,956 +tqdm/cli.py,sha256=RZh5sKVclaNp_7tZ3iCXWJB3-V6KfeQNNw7mZ818gMc,10594 +tqdm/completion.sh,sha256=j79KbSmpIj_E11jfTfBXrGnUTzKXVpQ1vGVQvsyDRl4,946 +tqdm/contrib/__init__.py,sha256=cNNaRURdcPjbQpkxPGe4iaShyQr_Dx8h6NQJubPhq7g,2513 +tqdm/contrib/__pycache__/__init__.cpython-310.pyc,, +tqdm/contrib/__pycache__/bells.cpython-310.pyc,, +tqdm/contrib/__pycache__/concurrent.cpython-310.pyc,, +tqdm/contrib/__pycache__/discord.cpython-310.pyc,, +tqdm/contrib/__pycache__/itertools.cpython-310.pyc,, +tqdm/contrib/__pycache__/logging.cpython-310.pyc,, +tqdm/contrib/__pycache__/slack.cpython-310.pyc,, +tqdm/contrib/__pycache__/telegram.cpython-310.pyc,, +tqdm/contrib/__pycache__/utils_worker.cpython-310.pyc,, +tqdm/contrib/bells.py,sha256=Yx1HqGCmHrESCAO700j5wE__JCleNODJxedh1ijPLD0,837 +tqdm/contrib/concurrent.py,sha256=K1yjloKS5WRNFyjLRth0DmU5PAnDbF0A-GD27N-J4a8,3986 +tqdm/contrib/discord.py,sha256=hhbOL1VGTWXQ4z1RUsIybhga7oUMYH5CoAkNWTt7t70,3962 +tqdm/contrib/itertools.py,sha256=WdKKQU5eSzsqHu29SN_oH12huYZo0Jihqoi9-nVhwz4,774 +tqdm/contrib/logging.py,sha256=aUvbBPGm0jetH22p1H5bobkdzewwYpXkdO8C_6nmAJk,3785 +tqdm/contrib/slack.py,sha256=CYIzKBgbk0azM19F4kSA8Ccod9I8hNfuscbcc9kjGYU,4068 +tqdm/contrib/telegram.py,sha256=ICWNBIb-l-quSgjKw4PhRdrH2_bKk7umy6BfEsp9EuU,5100 +tqdm/contrib/utils_worker.py,sha256=HJP5Mz1S1xyzEke2JaqJ2sYLHXADYoo2epT5AzQ38eA,1207 +tqdm/dask.py,sha256=RWR3rz9s36zNceeESTm-F3H1F7dxuBuqS1bhifOuvEY,1337 +tqdm/gui.py,sha256=LhGizt5G1w4f17OjFRBWza8MEmQZjrFgTx9Y9Lc7-gA,5809 +tqdm/keras.py,sha256=op9sBkb6q6c6dw2wJ0SD2ZwpPK7yM1Vbg4l1Qiy3MIo,4373 +tqdm/notebook.py,sha256=A6waQCPa0Ud3OrByzZwj91STnhwoJ1Ui3THrmQBZjqE,11023 +tqdm/rich.py,sha256=8SamiW-LlUKEMQwzubomHCXkX6awaz-7Tq-gnMkJJ5A,5018 +tqdm/std.py,sha256=AuM80Fy023Ex6N7J8TP8XaJehJLo8OnjIfLk9r7j7T4,57478 +tqdm/tk.py,sha256=Hp9QwXTsihURoxt5aXbIe8Cu1qjLBZzkhfl_nAgD418,6727 +tqdm/tqdm.1,sha256=aILyUPk2S4OPe_uWy2P4AMjUf0oQ6PUW0nLYXB-BWwI,7889 +tqdm/utils.py,sha256=abBTZZ38isuYMR38AgZ-nQzC-KvRn8M-mFCuzFpgsuA,11883 +tqdm/version.py,sha256=-1yWjfu3P0eghVsysHH07fbzdiADNRdzRtYPqOaqR2A,333 diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/entry_points.txt b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/entry_points.txt new file mode 100644 index 0000000..540e60f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +tqdm = tqdm.cli:main diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/top_level.txt new file mode 100644 index 0000000..78620c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm-4.66.2.dist-info/top_level.txt @@ -0,0 +1 @@ +tqdm diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__init__.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/__init__.py new file mode 100644 index 0000000..8081f77 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/__init__.py @@ -0,0 +1,38 @@ +from ._monitor import TMonitor, TqdmSynchronisationWarning +from ._tqdm_pandas import tqdm_pandas +from .cli import main # TODO: remove in v5.0.0 +from .gui import tqdm as tqdm_gui # TODO: remove in v5.0.0 +from .gui import trange as tgrange # TODO: remove in v5.0.0 +from .std import ( + TqdmDeprecationWarning, TqdmExperimentalWarning, TqdmKeyError, TqdmMonitorWarning, + TqdmTypeError, TqdmWarning, tqdm, trange) +from .version import __version__ + +__all__ = ['tqdm', 'tqdm_gui', 'trange', 'tgrange', 'tqdm_pandas', + 'tqdm_notebook', 'tnrange', 'main', 'TMonitor', + 'TqdmTypeError', 'TqdmKeyError', + 'TqdmWarning', 'TqdmDeprecationWarning', + 'TqdmExperimentalWarning', + 'TqdmMonitorWarning', 'TqdmSynchronisationWarning', + '__version__'] + + +def tqdm_notebook(*args, **kwargs): # pragma: no cover + """See tqdm.notebook.tqdm for full documentation""" + from warnings import warn + + from .notebook import tqdm as _tqdm_notebook + warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`", + TqdmDeprecationWarning, stacklevel=2) + return _tqdm_notebook(*args, **kwargs) + + +def tnrange(*args, **kwargs): # pragma: no cover + """Shortcut for `tqdm.notebook.tqdm(range(*args), **kwargs)`.""" + from warnings import warn + + from .notebook import trange as _tnrange + warn("Please use `tqdm.notebook.trange` instead of `tqdm.tnrange`", + TqdmDeprecationWarning, stacklevel=2) + return _tnrange(*args, **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__main__.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..66641c8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__main__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000..e7917f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/__main__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_dist_ver.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_dist_ver.cpython-310.pyc new file mode 100644 index 0000000..27630b8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_dist_ver.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_main.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_main.cpython-310.pyc new file mode 100644 index 0000000..711bb8a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_main.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_monitor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_monitor.cpython-310.pyc new file mode 100644 index 0000000..602385a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_monitor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm.cpython-310.pyc new file mode 100644 index 0000000..763987b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_gui.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_gui.cpython-310.pyc new file mode 100644 index 0000000..fae42ac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_gui.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_notebook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_notebook.cpython-310.pyc new file mode 100644 index 0000000..75f142b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_notebook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_pandas.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_pandas.cpython-310.pyc new file mode 100644 index 0000000..6b05c35 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_tqdm_pandas.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000..b08f292 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/_utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/asyncio.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/asyncio.cpython-310.pyc new file mode 100644 index 0000000..e7462ee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/asyncio.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/auto.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/auto.cpython-310.pyc new file mode 100644 index 0000000..cf91872 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/auto.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/autonotebook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/autonotebook.cpython-310.pyc new file mode 100644 index 0000000..49dbdf2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/autonotebook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/cli.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/cli.cpython-310.pyc new file mode 100644 index 0000000..b60593d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/cli.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/dask.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/dask.cpython-310.pyc new file mode 100644 index 0000000..b39d21a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/dask.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/gui.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/gui.cpython-310.pyc new file mode 100644 index 0000000..be10029 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/gui.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/keras.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/keras.cpython-310.pyc new file mode 100644 index 0000000..6ad51dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/keras.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/notebook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/notebook.cpython-310.pyc new file mode 100644 index 0000000..cdd557c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/notebook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/rich.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/rich.cpython-310.pyc new file mode 100644 index 0000000..109070f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/rich.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/std.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/std.cpython-310.pyc new file mode 100644 index 0000000..88d506b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/std.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/tk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/tk.cpython-310.pyc new file mode 100644 index 0000000..71def32 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/tk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/utils.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..48ed01c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/utils.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..71536ad Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_dist_ver.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_dist_ver.py new file mode 100644 index 0000000..63c9d0a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_dist_ver.py @@ -0,0 +1 @@ +__version__ = '4.66.2' diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_main.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_main.py new file mode 100644 index 0000000..04fdeef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_main.py @@ -0,0 +1,9 @@ +from warnings import warn + +from .cli import * # NOQA +from .cli import __all__ # NOQA +from .std import TqdmDeprecationWarning + +warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.cli.*` instead of `tqdm._main.*`", + TqdmDeprecationWarning, stacklevel=2) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_monitor.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_monitor.py new file mode 100644 index 0000000..f71aa56 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_monitor.py @@ -0,0 +1,95 @@ +import atexit +from threading import Event, Thread, current_thread +from time import time +from warnings import warn + +__all__ = ["TMonitor", "TqdmSynchronisationWarning"] + + +class TqdmSynchronisationWarning(RuntimeWarning): + """tqdm multi-thread/-process errors which may cause incorrect nesting + but otherwise no adverse effects""" + pass + + +class TMonitor(Thread): + """ + Monitoring thread for tqdm bars. + Monitors if tqdm bars are taking too much time to display + and readjusts miniters automatically if necessary. + + Parameters + ---------- + tqdm_cls : class + tqdm class to use (can be core tqdm or a submodule). + sleep_interval : float + Time to sleep between monitoring checks. + """ + _test = {} # internal vars for unit testing + + def __init__(self, tqdm_cls, sleep_interval): + Thread.__init__(self) + self.daemon = True # kill thread when main killed (KeyboardInterrupt) + self.woken = 0 # last time woken up, to sync with monitor + self.tqdm_cls = tqdm_cls + self.sleep_interval = sleep_interval + self._time = self._test.get("time", time) + self.was_killed = self._test.get("Event", Event)() + atexit.register(self.exit) + self.start() + + def exit(self): + self.was_killed.set() + if self is not current_thread(): + self.join() + return self.report() + + def get_instances(self): + # returns a copy of started `tqdm_cls` instances + return [i for i in self.tqdm_cls._instances.copy() + # Avoid race by checking that the instance started + if hasattr(i, 'start_t')] + + def run(self): + cur_t = self._time() + while True: + # After processing and before sleeping, notify that we woke + # Need to be done just before sleeping + self.woken = cur_t + # Sleep some time... + self.was_killed.wait(self.sleep_interval) + # Quit if killed + if self.was_killed.is_set(): + return + # Then monitor! + # Acquire lock (to access _instances) + with self.tqdm_cls.get_lock(): + cur_t = self._time() + # Check tqdm instances are waiting too long to print + instances = self.get_instances() + for instance in instances: + # Check event in loop to reduce blocking time on exit + if self.was_killed.is_set(): + return + # Only if mininterval > 1 (else iterations are just slow) + # and last refresh exceeded maxinterval + if ( + instance.miniters > 1 + and (cur_t - instance.last_print_t) >= instance.maxinterval + ): + # force bypassing miniters on next iteration + # (dynamic_miniters adjusts mininterval automatically) + instance.miniters = 1 + # Refresh now! (works only for manual tqdm) + instance.refresh(nolock=True) + # Remove accidental long-lived strong reference + del instance + if instances != self.get_instances(): # pragma: nocover + warn("Set changed size during iteration" + + " (see https://github.com/tqdm/tqdm/issues/481)", + TqdmSynchronisationWarning, stacklevel=2) + # Remove accidental long-lived strong references + del instances + + def report(self): + return not self.was_killed.is_set() diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm.py new file mode 100644 index 0000000..7fc4962 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm.py @@ -0,0 +1,9 @@ +from warnings import warn + +from .std import * # NOQA +from .std import __all__ # NOQA +from .std import TqdmDeprecationWarning + +warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.std.*` instead of `tqdm._tqdm.*`", + TqdmDeprecationWarning, stacklevel=2) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_gui.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_gui.py new file mode 100644 index 0000000..f32aa89 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_gui.py @@ -0,0 +1,9 @@ +from warnings import warn + +from .gui import * # NOQA +from .gui import __all__ # NOQA +from .std import TqdmDeprecationWarning + +warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.gui.*` instead of `tqdm._tqdm_gui.*`", + TqdmDeprecationWarning, stacklevel=2) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_notebook.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_notebook.py new file mode 100644 index 0000000..f225fbf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_notebook.py @@ -0,0 +1,9 @@ +from warnings import warn + +from .notebook import * # NOQA +from .notebook import __all__ # NOQA +from .std import TqdmDeprecationWarning + +warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.notebook.*` instead of `tqdm._tqdm_notebook.*`", + TqdmDeprecationWarning, stacklevel=2) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_pandas.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_pandas.py new file mode 100644 index 0000000..c4fe6ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_tqdm_pandas.py @@ -0,0 +1,24 @@ +import sys + +__author__ = "github.com/casperdcl" +__all__ = ['tqdm_pandas'] + + +def tqdm_pandas(tclass, **tqdm_kwargs): + """ + Registers the given `tqdm` instance with + `pandas.core.groupby.DataFrameGroupBy.progress_apply`. + """ + from tqdm import TqdmDeprecationWarning + + if isinstance(tclass, type) or (getattr(tclass, '__name__', '').startswith( + 'tqdm_')): # delayed adapter case + TqdmDeprecationWarning( + "Please use `tqdm.pandas(...)` instead of `tqdm_pandas(tqdm, ...)`.", + fp_write=getattr(tqdm_kwargs.get('file', None), 'write', sys.stderr.write)) + tclass.pandas(**tqdm_kwargs) + else: + TqdmDeprecationWarning( + "Please use `tqdm.pandas(...)` instead of `tqdm_pandas(tqdm(...))`.", + fp_write=getattr(tclass.fp, 'write', sys.stderr.write)) + type(tclass).pandas(deprecated_t=tclass) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/_utils.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/_utils.py new file mode 100644 index 0000000..385e849 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/_utils.py @@ -0,0 +1,11 @@ +from warnings import warn + +from .std import TqdmDeprecationWarning +from .utils import ( # NOQA, pylint: disable=unused-import + CUR_OS, IS_NIX, IS_WIN, RE_ANSI, Comparable, FormatReplace, SimpleTextIOWrapper, + _environ_cols_wrapper, _is_ascii, _is_utf, _screen_shape_linux, _screen_shape_tput, + _screen_shape_windows, _screen_shape_wrapper, _supports_unicode, _term_move_up, colorama) + +warn("This function will be removed in tqdm==5.0.0\n" + "Please use `tqdm.utils.*` instead of `tqdm._utils.*`", + TqdmDeprecationWarning, stacklevel=2) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/asyncio.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/asyncio.py new file mode 100644 index 0000000..ddc89b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/asyncio.py @@ -0,0 +1,93 @@ +""" +Asynchronous progressbar decorator for iterators. +Includes a default `range` iterator printing to `stderr`. + +Usage: +>>> from tqdm.asyncio import trange, tqdm +>>> async for i in trange(10): +... ... +""" +import asyncio +from sys import version_info + +from .std import tqdm as std_tqdm + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['tqdm_asyncio', 'tarange', 'tqdm', 'trange'] + + +class tqdm_asyncio(std_tqdm): + """ + Asynchronous-friendly version of tqdm. + """ + def __init__(self, iterable=None, *args, **kwargs): + super(tqdm_asyncio, self).__init__(iterable, *args, **kwargs) + self.iterable_awaitable = False + if iterable is not None: + if hasattr(iterable, "__anext__"): + self.iterable_next = iterable.__anext__ + self.iterable_awaitable = True + elif hasattr(iterable, "__next__"): + self.iterable_next = iterable.__next__ + else: + self.iterable_iterator = iter(iterable) + self.iterable_next = self.iterable_iterator.__next__ + + def __aiter__(self): + return self + + async def __anext__(self): + try: + if self.iterable_awaitable: + res = await self.iterable_next() + else: + res = self.iterable_next() + self.update() + return res + except StopIteration: + self.close() + raise StopAsyncIteration + except BaseException: + self.close() + raise + + def send(self, *args, **kwargs): + return self.iterable.send(*args, **kwargs) + + @classmethod + def as_completed(cls, fs, *, loop=None, timeout=None, total=None, **tqdm_kwargs): + """ + Wrapper for `asyncio.as_completed`. + """ + if total is None: + total = len(fs) + kwargs = {} + if version_info[:2] < (3, 10): + kwargs['loop'] = loop + yield from cls(asyncio.as_completed(fs, timeout=timeout, **kwargs), + total=total, **tqdm_kwargs) + + @classmethod + async def gather(cls, *fs, loop=None, timeout=None, total=None, **tqdm_kwargs): + """ + Wrapper for `asyncio.gather`. + """ + async def wrap_awaitable(i, f): + return i, await f + + ifs = [wrap_awaitable(i, f) for i, f in enumerate(fs)] + res = [await f for f in cls.as_completed(ifs, loop=loop, timeout=timeout, + total=total, **tqdm_kwargs)] + return [i for _, i in sorted(res)] + + +def tarange(*args, **kwargs): + """ + A shortcut for `tqdm.asyncio.tqdm(range(*args), **kwargs)`. + """ + return tqdm_asyncio(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_asyncio +trange = tarange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/auto.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/auto.py new file mode 100644 index 0000000..206c440 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/auto.py @@ -0,0 +1,40 @@ +""" +Enables multiple commonly used features. + +Method resolution order: + +- `tqdm.autonotebook` without import warnings +- `tqdm.asyncio` +- `tqdm.std` base class + +Usage: +>>> from tqdm.auto import trange, tqdm +>>> for i in trange(10): +... ... +""" +import warnings + +from .std import TqdmExperimentalWarning + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=TqdmExperimentalWarning) + from .autonotebook import tqdm as notebook_tqdm + +from .asyncio import tqdm as asyncio_tqdm +from .std import tqdm as std_tqdm + +if notebook_tqdm != std_tqdm: + class tqdm(notebook_tqdm, asyncio_tqdm): # pylint: disable=inconsistent-mro + pass +else: + tqdm = asyncio_tqdm + + +def trange(*args, **kwargs): + """ + A shortcut for `tqdm.auto.tqdm(range(*args), **kwargs)`. + """ + return tqdm(range(*args), **kwargs) + + +__all__ = ["tqdm", "trange"] diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/autonotebook.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/autonotebook.py new file mode 100644 index 0000000..a09f2ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/autonotebook.py @@ -0,0 +1,29 @@ +""" +Automatically choose between `tqdm.notebook` and `tqdm.std`. + +Usage: +>>> from tqdm.autonotebook import trange, tqdm +>>> for i in trange(10): +... ... +""" +import sys +from warnings import warn + +try: + get_ipython = sys.modules['IPython'].get_ipython + if 'IPKernelApp' not in get_ipython().config: # pragma: no cover + raise ImportError("console") + from .notebook import WARN_NOIPYW, IProgress + if IProgress is None: + from .std import TqdmWarning + warn(WARN_NOIPYW, TqdmWarning, stacklevel=2) + raise ImportError('ipywidgets') +except Exception: + from .std import tqdm, trange +else: # pragma: no cover + from .notebook import tqdm, trange + from .std import TqdmExperimentalWarning + warn("Using `tqdm.autonotebook.tqdm` in notebook mode." + " Use `tqdm.tqdm` instead to force console mode" + " (e.g. in jupyter console)", TqdmExperimentalWarning, stacklevel=2) +__all__ = ["tqdm", "trange"] diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/cli.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/cli.py new file mode 100644 index 0000000..1223d49 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/cli.py @@ -0,0 +1,311 @@ +""" +Module version for monitoring CLI pipes (`... | python -m tqdm | ...`). +""" +import logging +import re +import sys +from ast import literal_eval as numeric + +from .std import TqdmKeyError, TqdmTypeError, tqdm +from .version import __version__ + +__all__ = ["main"] +log = logging.getLogger(__name__) + + +def cast(val, typ): + log.debug((val, typ)) + if " or " in typ: + for t in typ.split(" or "): + try: + return cast(val, t) + except TqdmTypeError: + pass + raise TqdmTypeError(val + ' : ' + typ) + + # sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n') + if typ == 'bool': + if (val == 'True') or (val == ''): + return True + elif val == 'False': + return False + else: + raise TqdmTypeError(val + ' : ' + typ) + try: + return eval(typ + '("' + val + '")') + except Exception: + if typ == 'chr': + return chr(ord(eval('"' + val + '"'))).encode() + else: + raise TqdmTypeError(val + ' : ' + typ) + + +def posix_pipe(fin, fout, delim=b'\\n', buf_size=256, + callback=lambda float: None, callback_len=True): + """ + Params + ------ + fin : binary file with `read(buf_size : int)` method + fout : binary file with `write` (and optionally `flush`) methods. + callback : function(float), e.g.: `tqdm.update` + callback_len : If (default: True) do `callback(len(buffer))`. + Otherwise, do `callback(data) for data in buffer.split(delim)`. + """ + fp_write = fout.write + + if not delim: + while True: + tmp = fin.read(buf_size) + + # flush at EOF + if not tmp: + getattr(fout, 'flush', lambda: None)() + return + + fp_write(tmp) + callback(len(tmp)) + # return + + buf = b'' + len_delim = len(delim) + # n = 0 + while True: + tmp = fin.read(buf_size) + + # flush at EOF + if not tmp: + if buf: + fp_write(buf) + if callback_len: + # n += 1 + buf.count(delim) + callback(1 + buf.count(delim)) + else: + for i in buf.split(delim): + callback(i) + getattr(fout, 'flush', lambda: None)() + return # n + + while True: + i = tmp.find(delim) + if i < 0: + buf += tmp + break + fp_write(buf + tmp[:i + len(delim)]) + # n += 1 + callback(1 if callback_len else (buf + tmp[:i])) + buf = b'' + tmp = tmp[i + len_delim:] + + +# ((opt, type), ... ) +RE_OPTS = re.compile(r'\n {4}(\S+)\s{2,}:\s*([^,]+)') +# better split method assuming no positional args +RE_SHLEX = re.compile(r'\s*(? : \2', d) + split = RE_OPTS.split(d) + opt_types_desc = zip(split[1::3], split[2::3], split[3::3]) + d = ''.join(('\n --{0} : {2}{3}' if otd[1] == 'bool' else + '\n --{0}=<{1}> : {2}{3}').format( + otd[0].replace('_', '-'), otd[0], *otd[1:]) + for otd in opt_types_desc if otd[0] not in UNSUPPORTED_OPTS) + + help_short = "Usage:\n tqdm [--help | options]\n" + d = help_short + """ +Options: + -h, --help Print this help and exit. + -v, --version Print version and exit. +""" + d.strip('\n') + '\n' + + # opts = docopt(d, version=__version__) + if any(v in argv for v in ('-v', '--version')): + sys.stdout.write(__version__ + '\n') + sys.exit(0) + elif any(v in argv for v in ('-h', '--help')): + sys.stdout.write(d + '\n') + sys.exit(0) + elif argv and argv[0][:2] != '--': + sys.stderr.write(f"Error:Unknown argument:{argv[0]}\n{help_short}") + + argv = RE_SHLEX.split(' '.join(["tqdm"] + argv)) + opts = dict(zip(argv[1::3], argv[3::3])) + + log.debug(opts) + opts.pop('log', True) + + tqdm_args = {'file': fp} + try: + for (o, v) in opts.items(): + o = o.replace('-', '_') + try: + tqdm_args[o] = cast(v, opt_types[o]) + except KeyError as e: + raise TqdmKeyError(str(e)) + log.debug('args:' + str(tqdm_args)) + + delim_per_char = tqdm_args.pop('bytes', False) + update = tqdm_args.pop('update', False) + update_to = tqdm_args.pop('update_to', False) + if sum((delim_per_char, update, update_to)) > 1: + raise TqdmKeyError("Can only have one of --bytes --update --update_to") + except Exception: + fp.write("\nError:\n" + help_short) + stdin, stdout_write = sys.stdin, sys.stdout.write + for i in stdin: + stdout_write(i) + raise + else: + buf_size = tqdm_args.pop('buf_size', 256) + delim = tqdm_args.pop('delim', b'\\n') + tee = tqdm_args.pop('tee', False) + manpath = tqdm_args.pop('manpath', None) + comppath = tqdm_args.pop('comppath', None) + if tqdm_args.pop('null', False): + class stdout(object): + @staticmethod + def write(_): + pass + else: + stdout = sys.stdout + stdout = getattr(stdout, 'buffer', stdout) + stdin = getattr(sys.stdin, 'buffer', sys.stdin) + if manpath or comppath: + from importlib import resources + from os import path + from shutil import copyfile + + def cp(name, dst): + """copy resource `name` to `dst`""" + if hasattr(resources, 'files'): + copyfile(str(resources.files('tqdm') / name), dst) + else: # py<3.9 + with resources.path('tqdm', name) as src: + copyfile(str(src), dst) + log.info("written:%s", dst) + if manpath is not None: + cp('tqdm.1', path.join(manpath, 'tqdm.1')) + if comppath is not None: + cp('completion.sh', path.join(comppath, 'tqdm_completion.sh')) + sys.exit(0) + if tee: + stdout_write = stdout.write + fp_write = getattr(fp, 'buffer', fp).write + + class stdout(object): # pylint: disable=function-redefined + @staticmethod + def write(x): + with tqdm.external_write_mode(file=fp): + fp_write(x) + stdout_write(x) + if delim_per_char: + tqdm_args.setdefault('unit', 'B') + tqdm_args.setdefault('unit_scale', True) + tqdm_args.setdefault('unit_divisor', 1024) + log.debug(tqdm_args) + with tqdm(**tqdm_args) as t: + posix_pipe(stdin, stdout, '', buf_size, t.update) + elif delim == b'\\n': + log.debug(tqdm_args) + write = stdout.write + if update or update_to: + with tqdm(**tqdm_args) as t: + if update: + def callback(i): + t.update(numeric(i.decode())) + else: # update_to + def callback(i): + t.update(numeric(i.decode()) - t.n) + for i in stdin: + write(i) + callback(i) + else: + for i in tqdm(stdin, **tqdm_args): + write(i) + else: + log.debug(tqdm_args) + with tqdm(**tqdm_args) as t: + callback_len = False + if update: + def callback(i): + t.update(numeric(i.decode())) + elif update_to: + def callback(i): + t.update(numeric(i.decode()) - t.n) + else: + callback = t.update + callback_len = True + posix_pipe(stdin, stdout, delim, buf_size, callback, callback_len) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/completion.sh b/.pythonlibs/lib/python3.10/site-packages/tqdm/completion.sh new file mode 100644 index 0000000..9f61c7f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/completion.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +_tqdm(){ + local cur prv + cur="${COMP_WORDS[COMP_CWORD]}" + prv="${COMP_WORDS[COMP_CWORD - 1]}" + + case ${prv} in + --bar_format|--buf_size|--colour|--comppath|--delay|--delim|--desc|--initial|--lock_args|--manpath|--maxinterval|--mininterval|--miniters|--ncols|--nrows|--position|--postfix|--smoothing|--total|--unit|--unit_divisor) + # await user input + ;; + "--log") + COMPREPLY=($(compgen -W 'CRITICAL FATAL ERROR WARN WARNING INFO DEBUG NOTSET' -- ${cur})) + ;; + *) + COMPREPLY=($(compgen -W '--ascii --bar_format --buf_size --bytes --colour --comppath --delay --delim --desc --disable --dynamic_ncols --help --initial --leave --lock_args --log --manpath --maxinterval --mininterval --miniters --ncols --nrows --null --position --postfix --smoothing --tee --total --unit --unit_divisor --unit_scale --update --update_to --version --write_bytes -h -v' -- ${cur})) + ;; + esac +} +complete -F _tqdm tqdm diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__init__.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__init__.py new file mode 100644 index 0000000..7338c96 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__init__.py @@ -0,0 +1,92 @@ +""" +Thin wrappers around common functions. + +Subpackages contain potentially unstable extensions. +""" +from warnings import warn + +from ..auto import tqdm as tqdm_auto +from ..std import TqdmDeprecationWarning, tqdm +from ..utils import ObjectWrapper + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['tenumerate', 'tzip', 'tmap'] + + +class DummyTqdmFile(ObjectWrapper): + """Dummy file-like that will write to tqdm""" + + def __init__(self, wrapped): + super(DummyTqdmFile, self).__init__(wrapped) + self._buf = [] + + def write(self, x, nolock=False): + nl = b"\n" if isinstance(x, bytes) else "\n" + pre, sep, post = x.rpartition(nl) + if sep: + blank = type(nl)() + tqdm.write(blank.join(self._buf + [pre, sep]), + end=blank, file=self._wrapped, nolock=nolock) + self._buf = [post] + else: + self._buf.append(x) + + def __del__(self): + if self._buf: + blank = type(self._buf[0])() + try: + tqdm.write(blank.join(self._buf), end=blank, file=self._wrapped) + except (OSError, ValueError): + pass + + +def builtin_iterable(func): + """Returns `func`""" + warn("This function has no effect, and will be removed in tqdm==5.0.0", + TqdmDeprecationWarning, stacklevel=2) + return func + + +def tenumerate(iterable, start=0, total=None, tqdm_class=tqdm_auto, **tqdm_kwargs): + """ + Equivalent of `numpy.ndenumerate` or builtin `enumerate`. + + Parameters + ---------- + tqdm_class : [default: tqdm.auto.tqdm]. + """ + try: + import numpy as np + except ImportError: + pass + else: + if isinstance(iterable, np.ndarray): + return tqdm_class(np.ndenumerate(iterable), total=total or iterable.size, + **tqdm_kwargs) + return enumerate(tqdm_class(iterable, total=total, **tqdm_kwargs), start) + + +def tzip(iter1, *iter2plus, **tqdm_kwargs): + """ + Equivalent of builtin `zip`. + + Parameters + ---------- + tqdm_class : [default: tqdm.auto.tqdm]. + """ + kwargs = tqdm_kwargs.copy() + tqdm_class = kwargs.pop("tqdm_class", tqdm_auto) + for i in zip(tqdm_class(iter1, **kwargs), *iter2plus): + yield i + + +def tmap(function, *sequences, **tqdm_kwargs): + """ + Equivalent of builtin `map`. + + Parameters + ---------- + tqdm_class : [default: tqdm.auto.tqdm]. + """ + for i in tzip(*sequences, **tqdm_kwargs): + yield function(*i) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..aa815ba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/bells.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/bells.cpython-310.pyc new file mode 100644 index 0000000..c5e5579 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/bells.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/concurrent.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/concurrent.cpython-310.pyc new file mode 100644 index 0000000..48e1404 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/concurrent.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/discord.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/discord.cpython-310.pyc new file mode 100644 index 0000000..256b654 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/discord.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/itertools.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/itertools.cpython-310.pyc new file mode 100644 index 0000000..a4f83e7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/itertools.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/logging.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/logging.cpython-310.pyc new file mode 100644 index 0000000..85a8190 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/logging.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/slack.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/slack.cpython-310.pyc new file mode 100644 index 0000000..dceb8b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/slack.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/telegram.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/telegram.cpython-310.pyc new file mode 100644 index 0000000..23f50d6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/telegram.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/utils_worker.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/utils_worker.cpython-310.pyc new file mode 100644 index 0000000..6dcad0d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/__pycache__/utils_worker.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/bells.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/bells.py new file mode 100644 index 0000000..5b8f4b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/bells.py @@ -0,0 +1,26 @@ +""" +Even more features than `tqdm.auto` (all the bells & whistles): + +- `tqdm.auto` +- `tqdm.tqdm.pandas` +- `tqdm.contrib.telegram` + + uses `${TQDM_TELEGRAM_TOKEN}` and `${TQDM_TELEGRAM_CHAT_ID}` +- `tqdm.contrib.discord` + + uses `${TQDM_DISCORD_TOKEN}` and `${TQDM_DISCORD_CHANNEL_ID}` +""" +__all__ = ['tqdm', 'trange'] +import warnings +from os import getenv + +if getenv("TQDM_SLACK_TOKEN") and getenv("TQDM_SLACK_CHANNEL"): + from .slack import tqdm, trange +elif getenv("TQDM_TELEGRAM_TOKEN") and getenv("TQDM_TELEGRAM_CHAT_ID"): + from .telegram import tqdm, trange +elif getenv("TQDM_DISCORD_TOKEN") and getenv("TQDM_DISCORD_CHANNEL_ID"): + from .discord import tqdm, trange +else: + from ..auto import tqdm, trange + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=FutureWarning) + tqdm.pandas() diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/concurrent.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/concurrent.py new file mode 100644 index 0000000..cd81d62 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/concurrent.py @@ -0,0 +1,105 @@ +""" +Thin wrappers around `concurrent.futures`. +""" +from contextlib import contextmanager +from operator import length_hint +from os import cpu_count + +from ..auto import tqdm as tqdm_auto +from ..std import TqdmWarning + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['thread_map', 'process_map'] + + +@contextmanager +def ensure_lock(tqdm_class, lock_name=""): + """get (create if necessary) and then restore `tqdm_class`'s lock""" + old_lock = getattr(tqdm_class, '_lock', None) # don't create a new lock + lock = old_lock or tqdm_class.get_lock() # maybe create a new lock + lock = getattr(lock, lock_name, lock) # maybe subtype + tqdm_class.set_lock(lock) + yield lock + if old_lock is None: + del tqdm_class._lock + else: + tqdm_class.set_lock(old_lock) + + +def _executor_map(PoolExecutor, fn, *iterables, **tqdm_kwargs): + """ + Implementation of `thread_map` and `process_map`. + + Parameters + ---------- + tqdm_class : [default: tqdm.auto.tqdm]. + max_workers : [default: min(32, cpu_count() + 4)]. + chunksize : [default: 1]. + lock_name : [default: "":str]. + """ + kwargs = tqdm_kwargs.copy() + if "total" not in kwargs: + kwargs["total"] = length_hint(iterables[0]) + tqdm_class = kwargs.pop("tqdm_class", tqdm_auto) + max_workers = kwargs.pop("max_workers", min(32, cpu_count() + 4)) + chunksize = kwargs.pop("chunksize", 1) + lock_name = kwargs.pop("lock_name", "") + with ensure_lock(tqdm_class, lock_name=lock_name) as lk: + # share lock in case workers are already using `tqdm` + with PoolExecutor(max_workers=max_workers, initializer=tqdm_class.set_lock, + initargs=(lk,)) as ex: + return list(tqdm_class(ex.map(fn, *iterables, chunksize=chunksize), **kwargs)) + + +def thread_map(fn, *iterables, **tqdm_kwargs): + """ + Equivalent of `list(map(fn, *iterables))` + driven by `concurrent.futures.ThreadPoolExecutor`. + + Parameters + ---------- + tqdm_class : optional + `tqdm` class to use for bars [default: tqdm.auto.tqdm]. + max_workers : int, optional + Maximum number of workers to spawn; passed to + `concurrent.futures.ThreadPoolExecutor.__init__`. + [default: max(32, cpu_count() + 4)]. + """ + from concurrent.futures import ThreadPoolExecutor + return _executor_map(ThreadPoolExecutor, fn, *iterables, **tqdm_kwargs) + + +def process_map(fn, *iterables, **tqdm_kwargs): + """ + Equivalent of `list(map(fn, *iterables))` + driven by `concurrent.futures.ProcessPoolExecutor`. + + Parameters + ---------- + tqdm_class : optional + `tqdm` class to use for bars [default: tqdm.auto.tqdm]. + max_workers : int, optional + Maximum number of workers to spawn; passed to + `concurrent.futures.ProcessPoolExecutor.__init__`. + [default: min(32, cpu_count() + 4)]. + chunksize : int, optional + Size of chunks sent to worker processes; passed to + `concurrent.futures.ProcessPoolExecutor.map`. [default: 1]. + lock_name : str, optional + Member of `tqdm_class.get_lock()` to use [default: mp_lock]. + """ + from concurrent.futures import ProcessPoolExecutor + if iterables and "chunksize" not in tqdm_kwargs: + # default `chunksize=1` has poor performance for large iterables + # (most time spent dispatching items to workers). + longest_iterable_len = max(map(length_hint, iterables)) + if longest_iterable_len > 1000: + from warnings import warn + warn("Iterable length %d > 1000 but `chunksize` is not set." + " This may seriously degrade multiprocess performance." + " Set `chunksize=1` or more." % longest_iterable_len, + TqdmWarning, stacklevel=2) + if "lock_name" not in tqdm_kwargs: + tqdm_kwargs = tqdm_kwargs.copy() + tqdm_kwargs["lock_name"] = "mp_lock" + return _executor_map(ProcessPoolExecutor, fn, *iterables, **tqdm_kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/discord.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/discord.py new file mode 100644 index 0000000..1e41308 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/discord.py @@ -0,0 +1,119 @@ +""" +Sends updates to a Discord bot. + +Usage: +>>> from tqdm.contrib.discord import tqdm, trange +>>> for i in trange(10, token='{token}', channel_id='{channel_id}'): +... ... + +![screenshot](https://tqdm.github.io/img/screenshot-discord.png) +""" +import logging +from os import getenv + +try: + from disco.client import Client, ClientConfig +except ImportError: + raise ImportError("Please `pip install disco-py`") + +from ..auto import tqdm as tqdm_auto +from .utils_worker import MonoWorker + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['DiscordIO', 'tqdm_discord', 'tdrange', 'tqdm', 'trange'] + + +class DiscordIO(MonoWorker): + """Non-blocking file-like IO using a Discord Bot.""" + def __init__(self, token, channel_id): + """Creates a new message in the given `channel_id`.""" + super(DiscordIO, self).__init__() + config = ClientConfig() + config.token = token + client = Client(config) + self.text = self.__class__.__name__ + try: + self.message = client.api.channels_messages_create(channel_id, self.text) + except Exception as e: + tqdm_auto.write(str(e)) + self.message = None + + def write(self, s): + """Replaces internal `message`'s text with `s`.""" + if not s: + s = "..." + s = s.replace('\r', '').strip() + if s == self.text: + return # skip duplicate message + message = self.message + if message is None: + return + self.text = s + try: + future = self.submit(message.edit, '`' + s + '`') + except Exception as e: + tqdm_auto.write(str(e)) + else: + return future + + +class tqdm_discord(tqdm_auto): + """ + Standard `tqdm.auto.tqdm` but also sends updates to a Discord Bot. + May take a few seconds to create (`__init__`). + + - create a discord bot (not public, no requirement of OAuth2 code + grant, only send message permissions) & invite it to a channel: + + - copy the bot `{token}` & `{channel_id}` and paste below + + >>> from tqdm.contrib.discord import tqdm, trange + >>> for i in tqdm(iterable, token='{token}', channel_id='{channel_id}'): + ... ... + """ + def __init__(self, *args, **kwargs): + """ + Parameters + ---------- + token : str, required. Discord token + [default: ${TQDM_DISCORD_TOKEN}]. + channel_id : int, required. Discord channel ID + [default: ${TQDM_DISCORD_CHANNEL_ID}]. + mininterval : float, optional. + Minimum of [default: 1.5] to avoid rate limit. + + See `tqdm.auto.tqdm.__init__` for other parameters. + """ + if not kwargs.get('disable'): + kwargs = kwargs.copy() + logging.getLogger("HTTPClient").setLevel(logging.WARNING) + self.dio = DiscordIO( + kwargs.pop('token', getenv("TQDM_DISCORD_TOKEN")), + kwargs.pop('channel_id', getenv("TQDM_DISCORD_CHANNEL_ID"))) + kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5)) + super(tqdm_discord, self).__init__(*args, **kwargs) + + def display(self, **kwargs): + super(tqdm_discord, self).display(**kwargs) + fmt = self.format_dict + if fmt.get('bar_format', None): + fmt['bar_format'] = fmt['bar_format'].replace( + '', '{bar:10u}').replace('{bar}', '{bar:10u}') + else: + fmt['bar_format'] = '{l_bar}{bar:10u}{r_bar}' + self.dio.write(self.format_meter(**fmt)) + + def clear(self, *args, **kwargs): + super(tqdm_discord, self).clear(*args, **kwargs) + if not self.disable: + self.dio.write("") + + +def tdrange(*args, **kwargs): + """Shortcut for `tqdm.contrib.discord.tqdm(range(*args), **kwargs)`.""" + return tqdm_discord(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_discord +trange = tdrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/itertools.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/itertools.py new file mode 100644 index 0000000..e67651a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/itertools.py @@ -0,0 +1,35 @@ +""" +Thin wrappers around `itertools`. +""" +import itertools + +from ..auto import tqdm as tqdm_auto + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['product'] + + +def product(*iterables, **tqdm_kwargs): + """ + Equivalent of `itertools.product`. + + Parameters + ---------- + tqdm_class : [default: tqdm.auto.tqdm]. + """ + kwargs = tqdm_kwargs.copy() + tqdm_class = kwargs.pop("tqdm_class", tqdm_auto) + try: + lens = list(map(len, iterables)) + except TypeError: + total = None + else: + total = 1 + for i in lens: + total *= i + kwargs.setdefault("total", total) + with tqdm_class(**kwargs) as t: + it = itertools.product(*iterables) + for i in it: + yield i + t.update() diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/logging.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/logging.py new file mode 100644 index 0000000..b8eaec5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/logging.py @@ -0,0 +1,126 @@ +""" +Helper functionality for interoperability with stdlib `logging`. +""" +import logging +import sys +from contextlib import contextmanager + +try: + from typing import Iterator, List, Optional, Type # noqa: F401 +except ImportError: + pass + +from ..std import tqdm as std_tqdm + + +class _TqdmLoggingHandler(logging.StreamHandler): + def __init__( + self, + tqdm_class=std_tqdm # type: Type[std_tqdm] + ): + super(_TqdmLoggingHandler, self).__init__() + self.tqdm_class = tqdm_class + + def emit(self, record): + try: + msg = self.format(record) + self.tqdm_class.write(msg, file=self.stream) + self.flush() + except (KeyboardInterrupt, SystemExit): + raise + except: # noqa pylint: disable=bare-except + self.handleError(record) + + +def _is_console_logging_handler(handler): + return (isinstance(handler, logging.StreamHandler) + and handler.stream in {sys.stdout, sys.stderr}) + + +def _get_first_found_console_logging_handler(handlers): + for handler in handlers: + if _is_console_logging_handler(handler): + return handler + + +@contextmanager +def logging_redirect_tqdm( + loggers=None, # type: Optional[List[logging.Logger]], + tqdm_class=std_tqdm # type: Type[std_tqdm] +): + # type: (...) -> Iterator[None] + """ + Context manager redirecting console logging to `tqdm.write()`, leaving + other logging handlers (e.g. log files) unaffected. + + Parameters + ---------- + loggers : list, optional + Which handlers to redirect (default: [logging.root]). + tqdm_class : optional + + Example + ------- + ```python + import logging + from tqdm import trange + from tqdm.contrib.logging import logging_redirect_tqdm + + LOG = logging.getLogger(__name__) + + if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + with logging_redirect_tqdm(): + for i in trange(9): + if i == 4: + LOG.info("console logging redirected to `tqdm.write()`") + # logging restored + ``` + """ + if loggers is None: + loggers = [logging.root] + original_handlers_list = [logger.handlers for logger in loggers] + try: + for logger in loggers: + tqdm_handler = _TqdmLoggingHandler(tqdm_class) + orig_handler = _get_first_found_console_logging_handler(logger.handlers) + if orig_handler is not None: + tqdm_handler.setFormatter(orig_handler.formatter) + tqdm_handler.stream = orig_handler.stream + logger.handlers = [ + handler for handler in logger.handlers + if not _is_console_logging_handler(handler)] + [tqdm_handler] + yield + finally: + for logger, original_handlers in zip(loggers, original_handlers_list): + logger.handlers = original_handlers + + +@contextmanager +def tqdm_logging_redirect( + *args, + # loggers=None, # type: Optional[List[logging.Logger]] + # tqdm=None, # type: Optional[Type[tqdm.tqdm]] + **kwargs +): + # type: (...) -> Iterator[None] + """ + Convenience shortcut for: + ```python + with tqdm_class(*args, **tqdm_kwargs) as pbar: + with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class): + yield pbar + ``` + + Parameters + ---------- + tqdm_class : optional, (default: tqdm.std.tqdm). + loggers : optional, list. + **tqdm_kwargs : passed to `tqdm_class`. + """ + tqdm_kwargs = kwargs.copy() + loggers = tqdm_kwargs.pop('loggers', None) + tqdm_class = tqdm_kwargs.pop('tqdm_class', std_tqdm) + with tqdm_class(*args, **tqdm_kwargs) as pbar: + with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class): + yield pbar diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/slack.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/slack.py new file mode 100644 index 0000000..d4c850c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/slack.py @@ -0,0 +1,120 @@ +""" +Sends updates to a Slack app. + +Usage: +>>> from tqdm.contrib.slack import tqdm, trange +>>> for i in trange(10, token='{token}', channel='{channel}'): +... ... + +![screenshot](https://tqdm.github.io/img/screenshot-slack.png) +""" +import logging +from os import getenv + +try: + from slack_sdk import WebClient +except ImportError: + raise ImportError("Please `pip install slack-sdk`") + +from ..auto import tqdm as tqdm_auto +from .utils_worker import MonoWorker + +__author__ = {"github.com/": ["0x2b3bfa0", "casperdcl"]} +__all__ = ['SlackIO', 'tqdm_slack', 'tsrange', 'tqdm', 'trange'] + + +class SlackIO(MonoWorker): + """Non-blocking file-like IO using a Slack app.""" + def __init__(self, token, channel): + """Creates a new message in the given `channel`.""" + super(SlackIO, self).__init__() + self.client = WebClient(token=token) + self.text = self.__class__.__name__ + try: + self.message = self.client.chat_postMessage(channel=channel, text=self.text) + except Exception as e: + tqdm_auto.write(str(e)) + self.message = None + + def write(self, s): + """Replaces internal `message`'s text with `s`.""" + if not s: + s = "..." + s = s.replace('\r', '').strip() + if s == self.text: + return # skip duplicate message + message = self.message + if message is None: + return + self.text = s + try: + future = self.submit(self.client.chat_update, channel=message['channel'], + ts=message['ts'], text='`' + s + '`') + except Exception as e: + tqdm_auto.write(str(e)) + else: + return future + + +class tqdm_slack(tqdm_auto): + """ + Standard `tqdm.auto.tqdm` but also sends updates to a Slack app. + May take a few seconds to create (`__init__`). + + - create a Slack app with the `chat:write` scope & invite it to a + channel: + - copy the bot `{token}` & `{channel}` and paste below + >>> from tqdm.contrib.slack import tqdm, trange + >>> for i in tqdm(iterable, token='{token}', channel='{channel}'): + ... ... + """ + def __init__(self, *args, **kwargs): + """ + Parameters + ---------- + token : str, required. Slack token + [default: ${TQDM_SLACK_TOKEN}]. + channel : int, required. Slack channel + [default: ${TQDM_SLACK_CHANNEL}]. + mininterval : float, optional. + Minimum of [default: 1.5] to avoid rate limit. + + See `tqdm.auto.tqdm.__init__` for other parameters. + """ + if not kwargs.get('disable'): + kwargs = kwargs.copy() + logging.getLogger("HTTPClient").setLevel(logging.WARNING) + self.sio = SlackIO( + kwargs.pop('token', getenv("TQDM_SLACK_TOKEN")), + kwargs.pop('channel', getenv("TQDM_SLACK_CHANNEL"))) + kwargs['mininterval'] = max(1.5, kwargs.get('mininterval', 1.5)) + super(tqdm_slack, self).__init__(*args, **kwargs) + + def display(self, **kwargs): + super(tqdm_slack, self).display(**kwargs) + fmt = self.format_dict + if fmt.get('bar_format', None): + fmt['bar_format'] = fmt['bar_format'].replace( + '', '`{bar:10}`').replace('{bar}', '`{bar:10u}`') + else: + fmt['bar_format'] = '{l_bar}`{bar:10}`{r_bar}' + if fmt['ascii'] is False: + fmt['ascii'] = [":black_square:", ":small_blue_diamond:", ":large_blue_diamond:", + ":large_blue_square:"] + fmt['ncols'] = 336 + self.sio.write(self.format_meter(**fmt)) + + def clear(self, *args, **kwargs): + super(tqdm_slack, self).clear(*args, **kwargs) + if not self.disable: + self.sio.write("") + + +def tsrange(*args, **kwargs): + """Shortcut for `tqdm.contrib.slack.tqdm(range(*args), **kwargs)`.""" + return tqdm_slack(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_slack +trange = tsrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/telegram.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/telegram.py new file mode 100644 index 0000000..cbeadf2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/telegram.py @@ -0,0 +1,153 @@ +""" +Sends updates to a Telegram bot. + +Usage: +>>> from tqdm.contrib.telegram import tqdm, trange +>>> for i in trange(10, token='{token}', chat_id='{chat_id}'): +... ... + +![screenshot](https://tqdm.github.io/img/screenshot-telegram.gif) +""" +from os import getenv +from warnings import warn + +from requests import Session + +from ..auto import tqdm as tqdm_auto +from ..std import TqdmWarning +from .utils_worker import MonoWorker + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['TelegramIO', 'tqdm_telegram', 'ttgrange', 'tqdm', 'trange'] + + +class TelegramIO(MonoWorker): + """Non-blocking file-like IO using a Telegram Bot.""" + API = 'https://api.telegram.org/bot' + + def __init__(self, token, chat_id): + """Creates a new message in the given `chat_id`.""" + super(TelegramIO, self).__init__() + self.token = token + self.chat_id = chat_id + self.session = Session() + self.text = self.__class__.__name__ + self.message_id + + @property + def message_id(self): + if hasattr(self, '_message_id'): + return self._message_id + try: + res = self.session.post( + self.API + '%s/sendMessage' % self.token, + data={'text': '`' + self.text + '`', 'chat_id': self.chat_id, + 'parse_mode': 'MarkdownV2'}).json() + except Exception as e: + tqdm_auto.write(str(e)) + else: + if res.get('error_code') == 429: + warn("Creation rate limit: try increasing `mininterval`.", + TqdmWarning, stacklevel=2) + else: + self._message_id = res['result']['message_id'] + return self._message_id + + def write(self, s): + """Replaces internal `message_id`'s text with `s`.""" + if not s: + s = "..." + s = s.replace('\r', '').strip() + if s == self.text: + return # avoid duplicate message Bot error + message_id = self.message_id + if message_id is None: + return + self.text = s + try: + future = self.submit( + self.session.post, self.API + '%s/editMessageText' % self.token, + data={'text': '`' + s + '`', 'chat_id': self.chat_id, + 'message_id': message_id, 'parse_mode': 'MarkdownV2'}) + except Exception as e: + tqdm_auto.write(str(e)) + else: + return future + + def delete(self): + """Deletes internal `message_id`.""" + try: + future = self.submit( + self.session.post, self.API + '%s/deleteMessage' % self.token, + data={'chat_id': self.chat_id, 'message_id': self.message_id}) + except Exception as e: + tqdm_auto.write(str(e)) + else: + return future + + +class tqdm_telegram(tqdm_auto): + """ + Standard `tqdm.auto.tqdm` but also sends updates to a Telegram Bot. + May take a few seconds to create (`__init__`). + + - create a bot + - copy its `{token}` + - add the bot to a chat and send it a message such as `/start` + - go to to find out + the `{chat_id}` + - paste the `{token}` & `{chat_id}` below + + >>> from tqdm.contrib.telegram import tqdm, trange + >>> for i in tqdm(iterable, token='{token}', chat_id='{chat_id}'): + ... ... + """ + def __init__(self, *args, **kwargs): + """ + Parameters + ---------- + token : str, required. Telegram token + [default: ${TQDM_TELEGRAM_TOKEN}]. + chat_id : str, required. Telegram chat ID + [default: ${TQDM_TELEGRAM_CHAT_ID}]. + + See `tqdm.auto.tqdm.__init__` for other parameters. + """ + if not kwargs.get('disable'): + kwargs = kwargs.copy() + self.tgio = TelegramIO( + kwargs.pop('token', getenv('TQDM_TELEGRAM_TOKEN')), + kwargs.pop('chat_id', getenv('TQDM_TELEGRAM_CHAT_ID'))) + super(tqdm_telegram, self).__init__(*args, **kwargs) + + def display(self, **kwargs): + super(tqdm_telegram, self).display(**kwargs) + fmt = self.format_dict + if fmt.get('bar_format', None): + fmt['bar_format'] = fmt['bar_format'].replace( + '', '{bar:10u}').replace('{bar}', '{bar:10u}') + else: + fmt['bar_format'] = '{l_bar}{bar:10u}{r_bar}' + self.tgio.write(self.format_meter(**fmt)) + + def clear(self, *args, **kwargs): + super(tqdm_telegram, self).clear(*args, **kwargs) + if not self.disable: + self.tgio.write("") + + def close(self): + if self.disable: + return + super(tqdm_telegram, self).close() + if not (self.leave or (self.leave is None and self.pos == 0)): + self.tgio.delete() + + +def ttgrange(*args, **kwargs): + """Shortcut for `tqdm.contrib.telegram.tqdm(range(*args), **kwargs)`.""" + return tqdm_telegram(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_telegram +trange = ttgrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/utils_worker.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/utils_worker.py new file mode 100644 index 0000000..2a03a2a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/contrib/utils_worker.py @@ -0,0 +1,38 @@ +""" +IO/concurrency helpers for `tqdm.contrib`. +""" +from collections import deque +from concurrent.futures import ThreadPoolExecutor + +from ..auto import tqdm as tqdm_auto + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['MonoWorker'] + + +class MonoWorker(object): + """ + Supports one running task and one waiting task. + The waiting task is the most recent submitted (others are discarded). + """ + def __init__(self): + self.pool = ThreadPoolExecutor(max_workers=1) + self.futures = deque([], 2) + + def submit(self, func, *args, **kwargs): + """`func(*args, **kwargs)` may replace currently waiting task.""" + futures = self.futures + if len(futures) == futures.maxlen: + running = futures.popleft() + if not running.done(): + if len(futures): # clear waiting + waiting = futures.pop() + waiting.cancel() + futures.appendleft(running) # re-insert running + try: + waiting = self.pool.submit(func, *args, **kwargs) + except Exception as e: + tqdm_auto.write(str(e)) + else: + futures.append(waiting) + return waiting diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/dask.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/dask.py new file mode 100644 index 0000000..af9926a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/dask.py @@ -0,0 +1,44 @@ +from functools import partial + +from dask.callbacks import Callback + +from .auto import tqdm as tqdm_auto + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['TqdmCallback'] + + +class TqdmCallback(Callback): + """Dask callback for task progress.""" + def __init__(self, start=None, pretask=None, tqdm_class=tqdm_auto, + **tqdm_kwargs): + """ + Parameters + ---------- + tqdm_class : optional + `tqdm` class to use for bars [default: `tqdm.auto.tqdm`]. + tqdm_kwargs : optional + Any other arguments used for all bars. + """ + super(TqdmCallback, self).__init__(start=start, pretask=pretask) + if tqdm_kwargs: + tqdm_class = partial(tqdm_class, **tqdm_kwargs) + self.tqdm_class = tqdm_class + + def _start_state(self, _, state): + self.pbar = self.tqdm_class(total=sum( + len(state[k]) for k in ['ready', 'waiting', 'running', 'finished'])) + + def _posttask(self, *_, **__): + self.pbar.update() + + def _finish(self, *_, **__): + self.pbar.close() + + def display(self): + """Displays in the current cell in Notebooks.""" + container = getattr(self.bar, 'container', None) + if container is None: + return + from .notebook import display + display(container) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/gui.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/gui.py new file mode 100644 index 0000000..8bab6ac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/gui.py @@ -0,0 +1,186 @@ +""" +Matplotlib GUI progressbar decorator for iterators. + +Usage: +>>> from tqdm.gui import trange, tqdm +>>> for i in trange(10): +... ... +""" +# future division is important to divide integers and get as +# a result precise floating numbers (instead of truncated int) +import re +from warnings import warn + +# to inherit from the tqdm class +from .std import TqdmExperimentalWarning +from .std import tqdm as std_tqdm + +# import compatibility functions and utilities + +__author__ = {"github.com/": ["casperdcl", "lrq3000"]} +__all__ = ['tqdm_gui', 'tgrange', 'tqdm', 'trange'] + + +class tqdm_gui(std_tqdm): # pragma: no cover + """Experimental Matplotlib GUI version of tqdm!""" + # TODO: @classmethod: write() on GUI? + def __init__(self, *args, **kwargs): + from collections import deque + + import matplotlib as mpl + import matplotlib.pyplot as plt + kwargs = kwargs.copy() + kwargs['gui'] = True + colour = kwargs.pop('colour', 'g') + super(tqdm_gui, self).__init__(*args, **kwargs) + + if self.disable: + return + + warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2) + self.mpl = mpl + self.plt = plt + + # Remember if external environment uses toolbars + self.toolbar = self.mpl.rcParams['toolbar'] + self.mpl.rcParams['toolbar'] = 'None' + + self.mininterval = max(self.mininterval, 0.5) + self.fig, ax = plt.subplots(figsize=(9, 2.2)) + # self.fig.subplots_adjust(bottom=0.2) + total = self.__len__() # avoids TypeError on None #971 + if total is not None: + self.xdata = [] + self.ydata = [] + self.zdata = [] + else: + self.xdata = deque([]) + self.ydata = deque([]) + self.zdata = deque([]) + self.line1, = ax.plot(self.xdata, self.ydata, color='b') + self.line2, = ax.plot(self.xdata, self.zdata, color='k') + ax.set_ylim(0, 0.001) + if total is not None: + ax.set_xlim(0, 100) + ax.set_xlabel("percent") + self.fig.legend((self.line1, self.line2), ("cur", "est"), + loc='center right') + # progressbar + self.hspan = plt.axhspan(0, 0.001, xmin=0, xmax=0, color=colour) + else: + # ax.set_xlim(-60, 0) + ax.set_xlim(0, 60) + ax.invert_xaxis() + ax.set_xlabel("seconds") + ax.legend(("cur", "est"), loc='lower left') + ax.grid() + # ax.set_xlabel('seconds') + ax.set_ylabel((self.unit if self.unit else "it") + "/s") + if self.unit_scale: + plt.ticklabel_format(style='sci', axis='y', scilimits=(0, 0)) + ax.yaxis.get_offset_text().set_x(-0.15) + + # Remember if external environment is interactive + self.wasion = plt.isinteractive() + plt.ion() + self.ax = ax + + def close(self): + if self.disable: + return + + self.disable = True + + with self.get_lock(): + self._instances.remove(self) + + # Restore toolbars + self.mpl.rcParams['toolbar'] = self.toolbar + # Return to non-interactive mode + if not self.wasion: + self.plt.ioff() + if self.leave: + self.display() + else: + self.plt.close(self.fig) + + def clear(self, *_, **__): + pass + + def display(self, *_, **__): + n = self.n + cur_t = self._time() + elapsed = cur_t - self.start_t + delta_it = n - self.last_print_n + delta_t = cur_t - self.last_print_t + + # Inline due to multiple calls + total = self.total + xdata = self.xdata + ydata = self.ydata + zdata = self.zdata + ax = self.ax + line1 = self.line1 + line2 = self.line2 + # instantaneous rate + y = delta_it / delta_t + # overall rate + z = n / elapsed + # update line data + xdata.append(n * 100.0 / total if total else cur_t) + ydata.append(y) + zdata.append(z) + + # Discard old values + # xmin, xmax = ax.get_xlim() + # if (not total) and elapsed > xmin * 1.1: + if (not total) and elapsed > 66: + xdata.popleft() + ydata.popleft() + zdata.popleft() + + ymin, ymax = ax.get_ylim() + if y > ymax or z > ymax: + ymax = 1.1 * y + ax.set_ylim(ymin, ymax) + ax.figure.canvas.draw() + + if total: + line1.set_data(xdata, ydata) + line2.set_data(xdata, zdata) + try: + poly_lims = self.hspan.get_xy() + except AttributeError: + self.hspan = self.plt.axhspan(0, 0.001, xmin=0, xmax=0, color='g') + poly_lims = self.hspan.get_xy() + poly_lims[0, 1] = ymin + poly_lims[1, 1] = ymax + poly_lims[2] = [n / total, ymax] + poly_lims[3] = [poly_lims[2, 0], ymin] + if len(poly_lims) > 4: + poly_lims[4, 1] = ymin + self.hspan.set_xy(poly_lims) + else: + t_ago = [cur_t - i for i in xdata] + line1.set_data(t_ago, ydata) + line2.set_data(t_ago, zdata) + + d = self.format_dict + # remove {bar} + d['bar_format'] = (d['bar_format'] or "{l_bar}{r_bar}").replace( + "{bar}", "") + msg = self.format_meter(**d) + if '' in msg: + msg = "".join(re.split(r'\|?\|?', msg, maxsplit=1)) + ax.set_title(msg, fontname="DejaVu Sans Mono", fontsize=11) + self.plt.pause(1e-9) + + +def tgrange(*args, **kwargs): + """Shortcut for `tqdm.gui.tqdm(range(*args), **kwargs)`.""" + return tqdm_gui(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_gui +trange = tgrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/keras.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/keras.py new file mode 100644 index 0000000..cce9467 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/keras.py @@ -0,0 +1,122 @@ +from copy import copy +from functools import partial + +from .auto import tqdm as tqdm_auto + +try: + import keras +except (ImportError, AttributeError) as e: + try: + from tensorflow import keras + except ImportError: + raise e +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['TqdmCallback'] + + +class TqdmCallback(keras.callbacks.Callback): + """Keras callback for epoch and batch progress.""" + @staticmethod + def bar2callback(bar, pop=None, delta=(lambda logs: 1)): + def callback(_, logs=None): + n = delta(logs) + if logs: + if pop: + logs = copy(logs) + [logs.pop(i, 0) for i in pop] + bar.set_postfix(logs, refresh=False) + bar.update(n) + + return callback + + def __init__(self, epochs=None, data_size=None, batch_size=None, verbose=1, + tqdm_class=tqdm_auto, **tqdm_kwargs): + """ + Parameters + ---------- + epochs : int, optional + data_size : int, optional + Number of training pairs. + batch_size : int, optional + Number of training pairs per batch. + verbose : int + 0: epoch, 1: batch (transient), 2: batch. [default: 1]. + Will be set to `0` unless both `data_size` and `batch_size` + are given. + tqdm_class : optional + `tqdm` class to use for bars [default: `tqdm.auto.tqdm`]. + tqdm_kwargs : optional + Any other arguments used for all bars. + """ + if tqdm_kwargs: + tqdm_class = partial(tqdm_class, **tqdm_kwargs) + self.tqdm_class = tqdm_class + self.epoch_bar = tqdm_class(total=epochs, unit='epoch') + self.on_epoch_end = self.bar2callback(self.epoch_bar) + if data_size and batch_size: + self.batches = batches = (data_size + batch_size - 1) // batch_size + else: + self.batches = batches = None + self.verbose = verbose + if verbose == 1: + self.batch_bar = tqdm_class(total=batches, unit='batch', leave=False) + self.on_batch_end = self.bar2callback( + self.batch_bar, pop=['batch', 'size'], + delta=lambda logs: logs.get('size', 1)) + + def on_train_begin(self, *_, **__): + params = self.params.get + auto_total = params('epochs', params('nb_epoch', None)) + if auto_total is not None and auto_total != self.epoch_bar.total: + self.epoch_bar.reset(total=auto_total) + + def on_epoch_begin(self, epoch, *_, **__): + if self.epoch_bar.n < epoch: + ebar = self.epoch_bar + ebar.n = ebar.last_print_n = ebar.initial = epoch + if self.verbose: + params = self.params.get + total = params('samples', params( + 'nb_sample', params('steps', None))) or self.batches + if self.verbose == 2: + if hasattr(self, 'batch_bar'): + self.batch_bar.close() + self.batch_bar = self.tqdm_class( + total=total, unit='batch', leave=True, + unit_scale=1 / (params('batch_size', 1) or 1)) + self.on_batch_end = self.bar2callback( + self.batch_bar, pop=['batch', 'size'], + delta=lambda logs: logs.get('size', 1)) + elif self.verbose == 1: + self.batch_bar.unit_scale = 1 / (params('batch_size', 1) or 1) + self.batch_bar.reset(total=total) + else: + raise KeyError('Unknown verbosity') + + def on_train_end(self, *_, **__): + if hasattr(self, 'batch_bar'): + self.batch_bar.close() + self.epoch_bar.close() + + def display(self): + """Displays in the current cell in Notebooks.""" + container = getattr(self.epoch_bar, 'container', None) + if container is None: + return + from .notebook import display + display(container) + batch_bar = getattr(self, 'batch_bar', None) + if batch_bar is not None: + display(batch_bar.container) + + @staticmethod + def _implements_train_batch_hooks(): + return True + + @staticmethod + def _implements_test_batch_hooks(): + return True + + @staticmethod + def _implements_predict_batch_hooks(): + return True diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/notebook.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/notebook.py new file mode 100644 index 0000000..6ee43a6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/notebook.py @@ -0,0 +1,317 @@ +""" +IPython/Jupyter Notebook progressbar decorator for iterators. +Includes a default `range` iterator printing to `stderr`. + +Usage: +>>> from tqdm.notebook import trange, tqdm +>>> for i in trange(10): +... ... +""" +# import compatibility functions and utilities +import re +import sys +from html import escape +from weakref import proxy + +# to inherit from the tqdm class +from .std import tqdm as std_tqdm + +if True: # pragma: no cover + # import IPython/Jupyter base widget and display utilities + IPY = 0 + try: # IPython 4.x + import ipywidgets + IPY = 4 + except ImportError: # IPython 3.x / 2.x + IPY = 32 + import warnings + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', message=".*The `IPython.html` package has been deprecated.*") + try: + import IPython.html.widgets as ipywidgets # NOQA: F401 + except ImportError: + pass + + try: # IPython 4.x / 3.x + if IPY == 32: + from IPython.html.widgets import HTML + from IPython.html.widgets import FloatProgress as IProgress + from IPython.html.widgets import HBox + IPY = 3 + else: + from ipywidgets import HTML + from ipywidgets import FloatProgress as IProgress + from ipywidgets import HBox + except ImportError: + try: # IPython 2.x + from IPython.html.widgets import HTML + from IPython.html.widgets import ContainerWidget as HBox + from IPython.html.widgets import FloatProgressWidget as IProgress + IPY = 2 + except ImportError: + IPY = 0 + IProgress = None + HBox = object + + try: + from IPython.display import display # , clear_output + except ImportError: + pass + +__author__ = {"github.com/": ["lrq3000", "casperdcl", "alexanderkuk"]} +__all__ = ['tqdm_notebook', 'tnrange', 'tqdm', 'trange'] +WARN_NOIPYW = ("IProgress not found. Please update jupyter and ipywidgets." + " See https://ipywidgets.readthedocs.io/en/stable" + "/user_install.html") + + +class TqdmHBox(HBox): + """`ipywidgets.HBox` with a pretty representation""" + def _json_(self, pretty=None): + pbar = getattr(self, 'pbar', None) + if pbar is None: + return {} + d = pbar.format_dict + if pretty is not None: + d["ascii"] = not pretty + return d + + def __repr__(self, pretty=False): + pbar = getattr(self, 'pbar', None) + if pbar is None: + return super(TqdmHBox, self).__repr__() + return pbar.format_meter(**self._json_(pretty)) + + def _repr_pretty_(self, pp, *_, **__): + pp.text(self.__repr__(True)) + + +class tqdm_notebook(std_tqdm): + """ + Experimental IPython/Jupyter Notebook widget using tqdm! + """ + @staticmethod + def status_printer(_, total=None, desc=None, ncols=None): + """ + Manage the printing of an IPython/Jupyter Notebook progress bar widget. + """ + # Fallback to text bar if there's no total + # DEPRECATED: replaced with an 'info' style bar + # if not total: + # return super(tqdm_notebook, tqdm_notebook).status_printer(file) + + # fp = file + + # Prepare IPython progress bar + if IProgress is None: # #187 #451 #558 #872 + raise ImportError(WARN_NOIPYW) + if total: + pbar = IProgress(min=0, max=total) + else: # No total? Show info style bar with no progress tqdm status + pbar = IProgress(min=0, max=1) + pbar.value = 1 + pbar.bar_style = 'info' + if ncols is None: + pbar.layout.width = "20px" + + ltext = HTML() + rtext = HTML() + if desc: + ltext.value = desc + container = TqdmHBox(children=[ltext, pbar, rtext]) + # Prepare layout + if ncols is not None: # use default style of ipywidgets + # ncols could be 100, "100px", "100%" + ncols = str(ncols) # ipywidgets only accepts string + try: + if int(ncols) > 0: # isnumeric and positive + ncols += 'px' + except ValueError: + pass + pbar.layout.flex = '2' + container.layout.width = ncols + container.layout.display = 'inline-flex' + container.layout.flex_flow = 'row wrap' + + return container + + def display(self, msg=None, pos=None, + # additional signals + close=False, bar_style=None, check_delay=True): + # Note: contrary to native tqdm, msg='' does NOT clear bar + # goal is to keep all infos if error happens so user knows + # at which iteration the loop failed. + + # Clear previous output (really necessary?) + # clear_output(wait=1) + + if not msg and not close: + d = self.format_dict + # remove {bar} + d['bar_format'] = (d['bar_format'] or "{l_bar}{r_bar}").replace( + "{bar}", "") + msg = self.format_meter(**d) + + ltext, pbar, rtext = self.container.children + pbar.value = self.n + + if msg: + msg = msg.replace(' ', u'\u2007') # fix html space padding + # html escape special characters (like '&') + if '' in msg: + left, right = map(escape, re.split(r'\|?\|?', msg, maxsplit=1)) + else: + left, right = '', escape(msg) + + # Update description + ltext.value = left + # never clear the bar (signal: msg='') + if right: + rtext.value = right + + # Change bar style + if bar_style: + # Hack-ish way to avoid the danger bar_style being overridden by + # success because the bar gets closed after the error... + if pbar.bar_style != 'danger' or bar_style != 'success': + pbar.bar_style = bar_style + + # Special signal to close the bar + if close and pbar.bar_style != 'danger': # hide only if no error + try: + self.container.close() + except AttributeError: + self.container.visible = False + self.container.layout.visibility = 'hidden' # IPYW>=8 + + if check_delay and self.delay > 0 and not self.displayed: + display(self.container) + self.displayed = True + + @property + def colour(self): + if hasattr(self, 'container'): + return self.container.children[-2].style.bar_color + + @colour.setter + def colour(self, bar_color): + if hasattr(self, 'container'): + self.container.children[-2].style.bar_color = bar_color + + def __init__(self, *args, **kwargs): + """ + Supports the usual `tqdm.tqdm` parameters as well as those listed below. + + Parameters + ---------- + display : Whether to call `display(self.container)` immediately + [default: True]. + """ + kwargs = kwargs.copy() + # Setup default output + file_kwarg = kwargs.get('file', sys.stderr) + if file_kwarg is sys.stderr or file_kwarg is None: + kwargs['file'] = sys.stdout # avoid the red block in IPython + + # Initialize parent class + avoid printing by using gui=True + kwargs['gui'] = True + # convert disable = None to False + kwargs['disable'] = bool(kwargs.get('disable', False)) + colour = kwargs.pop('colour', None) + display_here = kwargs.pop('display', True) + super(tqdm_notebook, self).__init__(*args, **kwargs) + if self.disable or not kwargs['gui']: + self.disp = lambda *_, **__: None + return + + # Get bar width + self.ncols = '100%' if self.dynamic_ncols else kwargs.get("ncols", None) + + # Replace with IPython progress bar display (with correct total) + unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1 + total = self.total * unit_scale if self.total else self.total + self.container = self.status_printer(self.fp, total, self.desc, self.ncols) + self.container.pbar = proxy(self) + self.displayed = False + if display_here and self.delay <= 0: + display(self.container) + self.displayed = True + self.disp = self.display + self.colour = colour + + # Print initial bar state + if not self.disable: + self.display(check_delay=False) + + def __iter__(self): + try: + it = super(tqdm_notebook, self).__iter__() + for obj in it: + # return super(tqdm...) will not catch exception + yield obj + # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt + except: # NOQA + self.disp(bar_style='danger') + raise + # NB: don't `finally: close()` + # since this could be a shared bar which the user will `reset()` + + def update(self, n=1): + try: + return super(tqdm_notebook, self).update(n=n) + # NB: except ... [ as ...] breaks IPython async KeyboardInterrupt + except: # NOQA + # cannot catch KeyboardInterrupt when using manual tqdm + # as the interrupt will most likely happen on another statement + self.disp(bar_style='danger') + raise + # NB: don't `finally: close()` + # since this could be a shared bar which the user will `reset()` + + def close(self): + if self.disable: + return + super(tqdm_notebook, self).close() + # Try to detect if there was an error or KeyboardInterrupt + # in manual mode: if n < total, things probably got wrong + if self.total and self.n < self.total: + self.disp(bar_style='danger', check_delay=False) + else: + if self.leave: + self.disp(bar_style='success', check_delay=False) + else: + self.disp(close=True, check_delay=False) + + def clear(self, *_, **__): + pass + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Consider combining with `leave=True`. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + if self.disable: + return super(tqdm_notebook, self).reset(total=total) + _, pbar, _ = self.container.children + pbar.bar_style = '' + if total is not None: + pbar.max = total + if not self.total and self.ncols is None: # no longer unknown total + pbar.layout.width = None # reset width + return super(tqdm_notebook, self).reset(total=total) + + +def tnrange(*args, **kwargs): + """Shortcut for `tqdm.notebook.tqdm(range(*args), **kwargs)`.""" + return tqdm_notebook(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_notebook +trange = tnrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/rich.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/rich.py new file mode 100644 index 0000000..00e1ddf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/rich.py @@ -0,0 +1,150 @@ +""" +`rich.progress` decorator for iterators. + +Usage: +>>> from tqdm.rich import trange, tqdm +>>> for i in trange(10): +... ... +""" +from warnings import warn + +from rich.progress import ( + BarColumn, Progress, ProgressColumn, Text, TimeElapsedColumn, TimeRemainingColumn, filesize) + +from .std import TqdmExperimentalWarning +from .std import tqdm as std_tqdm + +__author__ = {"github.com/": ["casperdcl"]} +__all__ = ['tqdm_rich', 'trrange', 'tqdm', 'trange'] + + +class FractionColumn(ProgressColumn): + """Renders completed/total, e.g. '0.5/2.3 G'.""" + def __init__(self, unit_scale=False, unit_divisor=1000): + self.unit_scale = unit_scale + self.unit_divisor = unit_divisor + super().__init__() + + def render(self, task): + """Calculate common unit for completed and total.""" + completed = int(task.completed) + total = int(task.total) + if self.unit_scale: + unit, suffix = filesize.pick_unit_and_suffix( + total, + ["", "K", "M", "G", "T", "P", "E", "Z", "Y"], + self.unit_divisor, + ) + else: + unit, suffix = filesize.pick_unit_and_suffix(total, [""], 1) + precision = 0 if unit == 1 else 1 + return Text( + f"{completed/unit:,.{precision}f}/{total/unit:,.{precision}f} {suffix}", + style="progress.download") + + +class RateColumn(ProgressColumn): + """Renders human readable transfer speed.""" + def __init__(self, unit="", unit_scale=False, unit_divisor=1000): + self.unit = unit + self.unit_scale = unit_scale + self.unit_divisor = unit_divisor + super().__init__() + + def render(self, task): + """Show data transfer speed.""" + speed = task.speed + if speed is None: + return Text(f"? {self.unit}/s", style="progress.data.speed") + if self.unit_scale: + unit, suffix = filesize.pick_unit_and_suffix( + speed, + ["", "K", "M", "G", "T", "P", "E", "Z", "Y"], + self.unit_divisor, + ) + else: + unit, suffix = filesize.pick_unit_and_suffix(speed, [""], 1) + precision = 0 if unit == 1 else 1 + return Text(f"{speed/unit:,.{precision}f} {suffix}{self.unit}/s", + style="progress.data.speed") + + +class tqdm_rich(std_tqdm): # pragma: no cover + """Experimental rich.progress GUI version of tqdm!""" + # TODO: @classmethod: write()? + def __init__(self, *args, **kwargs): + """ + This class accepts the following parameters *in addition* to + the parameters accepted by `tqdm`. + + Parameters + ---------- + progress : tuple, optional + arguments for `rich.progress.Progress()`. + options : dict, optional + keyword arguments for `rich.progress.Progress()`. + """ + kwargs = kwargs.copy() + kwargs['gui'] = True + # convert disable = None to False + kwargs['disable'] = bool(kwargs.get('disable', False)) + progress = kwargs.pop('progress', None) + options = kwargs.pop('options', {}).copy() + super(tqdm_rich, self).__init__(*args, **kwargs) + + if self.disable: + return + + warn("rich is experimental/alpha", TqdmExperimentalWarning, stacklevel=2) + d = self.format_dict + if progress is None: + progress = ( + "[progress.description]{task.description}" + "[progress.percentage]{task.percentage:>4.0f}%", + BarColumn(bar_width=None), + FractionColumn( + unit_scale=d['unit_scale'], unit_divisor=d['unit_divisor']), + "[", TimeElapsedColumn(), "<", TimeRemainingColumn(), + ",", RateColumn(unit=d['unit'], unit_scale=d['unit_scale'], + unit_divisor=d['unit_divisor']), "]" + ) + options.setdefault('transient', not self.leave) + self._prog = Progress(*progress, **options) + self._prog.__enter__() + self._task_id = self._prog.add_task(self.desc or "", **d) + + def close(self): + if self.disable: + return + super(tqdm_rich, self).close() + self._prog.__exit__(None, None, None) + + def clear(self, *_, **__): + pass + + def display(self, *_, **__): + if not hasattr(self, '_prog'): + return + self._prog.update(self._task_id, completed=self.n, description=self.desc) + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + if hasattr(self, '_prog'): + self._prog.reset(total=total) + super(tqdm_rich, self).reset(total=total) + + +def trrange(*args, **kwargs): + """Shortcut for `tqdm.rich.tqdm(range(*args), **kwargs)`.""" + return tqdm_rich(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_rich +trange = trrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/std.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/std.py new file mode 100644 index 0000000..e58fdca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/std.py @@ -0,0 +1,1524 @@ +""" +Customisable progressbar decorator for iterators. +Includes a default `range` iterator printing to `stderr`. + +Usage: +>>> from tqdm import trange, tqdm +>>> for i in trange(10): +... ... +""" +import sys +from collections import OrderedDict, defaultdict +from contextlib import contextmanager +from datetime import datetime, timedelta, timezone +from numbers import Number +from time import time +from warnings import warn +from weakref import WeakSet + +from ._monitor import TMonitor +from .utils import ( + CallbackIOWrapper, Comparable, DisableOnWriteError, FormatReplace, SimpleTextIOWrapper, + _is_ascii, _screen_shape_wrapper, _supports_unicode, _term_move_up, disp_len, disp_trim, + envwrap) + +__author__ = "https://github.com/tqdm/tqdm#contributions" +__all__ = ['tqdm', 'trange', + 'TqdmTypeError', 'TqdmKeyError', 'TqdmWarning', + 'TqdmExperimentalWarning', 'TqdmDeprecationWarning', + 'TqdmMonitorWarning'] + + +class TqdmTypeError(TypeError): + pass + + +class TqdmKeyError(KeyError): + pass + + +class TqdmWarning(Warning): + """base class for all tqdm warnings. + + Used for non-external-code-breaking errors, such as garbled printing. + """ + def __init__(self, msg, fp_write=None, *a, **k): + if fp_write is not None: + fp_write("\n" + self.__class__.__name__ + ": " + str(msg).rstrip() + '\n') + else: + super(TqdmWarning, self).__init__(msg, *a, **k) + + +class TqdmExperimentalWarning(TqdmWarning, FutureWarning): + """beta feature, unstable API and behaviour""" + pass + + +class TqdmDeprecationWarning(TqdmWarning, DeprecationWarning): + # not suppressed if raised + pass + + +class TqdmMonitorWarning(TqdmWarning, RuntimeWarning): + """tqdm monitor errors which do not affect external functionality""" + pass + + +def TRLock(*args, **kwargs): + """threading RLock""" + try: + from threading import RLock + return RLock(*args, **kwargs) + except (ImportError, OSError): # pragma: no cover + pass + + +class TqdmDefaultWriteLock(object): + """ + Provide a default write lock for thread and multiprocessing safety. + Works only on platforms supporting `fork` (so Windows is excluded). + You must initialise a `tqdm` or `TqdmDefaultWriteLock` instance + before forking in order for the write lock to work. + On Windows, you need to supply the lock from the parent to the children as + an argument to joblib or the parallelism lib you use. + """ + # global thread lock so no setup required for multithreading. + # NB: Do not create multiprocessing lock as it sets the multiprocessing + # context, disallowing `spawn()`/`forkserver()` + th_lock = TRLock() + + def __init__(self): + # Create global parallelism locks to avoid racing issues with parallel + # bars works only if fork available (Linux/MacOSX, but not Windows) + cls = type(self) + root_lock = cls.th_lock + if root_lock is not None: + root_lock.acquire() + cls.create_mp_lock() + self.locks = [lk for lk in [cls.mp_lock, cls.th_lock] if lk is not None] + if root_lock is not None: + root_lock.release() + + def acquire(self, *a, **k): + for lock in self.locks: + lock.acquire(*a, **k) + + def release(self): + for lock in self.locks[::-1]: # Release in inverse order of acquisition + lock.release() + + def __enter__(self): + self.acquire() + + def __exit__(self, *exc): + self.release() + + @classmethod + def create_mp_lock(cls): + if not hasattr(cls, 'mp_lock'): + try: + from multiprocessing import RLock + cls.mp_lock = RLock() + except (ImportError, OSError): # pragma: no cover + cls.mp_lock = None + + @classmethod + def create_th_lock(cls): + assert hasattr(cls, 'th_lock') + warn("create_th_lock not needed anymore", TqdmDeprecationWarning, stacklevel=2) + + +class Bar(object): + """ + `str.format`-able bar with format specifiers: `[width][type]` + + - `width` + + unspecified (default): use `self.default_len` + + `int >= 0`: overrides `self.default_len` + + `int < 0`: subtract from `self.default_len` + - `type` + + `a`: ascii (`charset=self.ASCII` override) + + `u`: unicode (`charset=self.UTF` override) + + `b`: blank (`charset=" "` override) + """ + ASCII = " 123456789#" + UTF = u" " + u''.join(map(chr, range(0x258F, 0x2587, -1))) + BLANK = " " + COLOUR_RESET = '\x1b[0m' + COLOUR_RGB = '\x1b[38;2;%d;%d;%dm' + COLOURS = {'BLACK': '\x1b[30m', 'RED': '\x1b[31m', 'GREEN': '\x1b[32m', + 'YELLOW': '\x1b[33m', 'BLUE': '\x1b[34m', 'MAGENTA': '\x1b[35m', + 'CYAN': '\x1b[36m', 'WHITE': '\x1b[37m'} + + def __init__(self, frac, default_len=10, charset=UTF, colour=None): + if not 0 <= frac <= 1: + warn("clamping frac to range [0, 1]", TqdmWarning, stacklevel=2) + frac = max(0, min(1, frac)) + assert default_len > 0 + self.frac = frac + self.default_len = default_len + self.charset = charset + self.colour = colour + + @property + def colour(self): + return self._colour + + @colour.setter + def colour(self, value): + if not value: + self._colour = None + return + try: + if value.upper() in self.COLOURS: + self._colour = self.COLOURS[value.upper()] + elif value[0] == '#' and len(value) == 7: + self._colour = self.COLOUR_RGB % tuple( + int(i, 16) for i in (value[1:3], value[3:5], value[5:7])) + else: + raise KeyError + except (KeyError, AttributeError): + warn("Unknown colour (%s); valid choices: [hex (#00ff00), %s]" % ( + value, ", ".join(self.COLOURS)), + TqdmWarning, stacklevel=2) + self._colour = None + + def __format__(self, format_spec): + if format_spec: + _type = format_spec[-1].lower() + try: + charset = {'a': self.ASCII, 'u': self.UTF, 'b': self.BLANK}[_type] + except KeyError: + charset = self.charset + else: + format_spec = format_spec[:-1] + if format_spec: + N_BARS = int(format_spec) + if N_BARS < 0: + N_BARS += self.default_len + else: + N_BARS = self.default_len + else: + charset = self.charset + N_BARS = self.default_len + + nsyms = len(charset) - 1 + bar_length, frac_bar_length = divmod(int(self.frac * N_BARS * nsyms), nsyms) + + res = charset[-1] * bar_length + if bar_length < N_BARS: # whitespace padding + res = res + charset[frac_bar_length] + charset[0] * (N_BARS - bar_length - 1) + return self.colour + res + self.COLOUR_RESET if self.colour else res + + +class EMA(object): + """ + Exponential moving average: smoothing to give progressively lower + weights to older values. + + Parameters + ---------- + smoothing : float, optional + Smoothing factor in range [0, 1], [default: 0.3]. + Increase to give more weight to recent values. + Ranges from 0 (yields old value) to 1 (yields new value). + """ + def __init__(self, smoothing=0.3): + self.alpha = smoothing + self.last = 0 + self.calls = 0 + + def __call__(self, x=None): + """ + Parameters + ---------- + x : float + New value to include in EMA. + """ + beta = 1 - self.alpha + if x is not None: + self.last = self.alpha * x + beta * self.last + self.calls += 1 + return self.last / (1 - beta ** self.calls) if self.calls else self.last + + +class tqdm(Comparable): + """ + Decorate an iterable object, returning an iterator which acts exactly + like the original iterable, but prints a dynamically updating + progressbar every time a value is requested. + + Parameters + ---------- + iterable : iterable, optional + Iterable to decorate with a progressbar. + Leave blank to manually manage the updates. + desc : str, optional + Prefix for the progressbar. + total : int or float, optional + The number of expected iterations. If unspecified, + len(iterable) is used if possible. If float("inf") or as a last + resort, only basic progress statistics are displayed + (no ETA, no progressbar). + If `gui` is True and this parameter needs subsequent updating, + specify an initial arbitrary large positive number, + e.g. 9e9. + leave : bool, optional + If [default: True], keeps all traces of the progressbar + upon termination of iteration. + If `None`, will leave only if `position` is `0`. + file : `io.TextIOWrapper` or `io.StringIO`, optional + Specifies where to output the progress messages + (default: sys.stderr). Uses `file.write(str)` and `file.flush()` + methods. For encoding, see `write_bytes`. + ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes the progressbar to stay within this bound. + If unspecified, attempts to use environment width. The + fallback is a meter width of 10 and no limit for the counter and + statistics. If 0, will not print any meter (only stats). + mininterval : float, optional + Minimum progress display update interval [default: 0.1] seconds. + maxinterval : float, optional + Maximum progress display update interval [default: 10] seconds. + Automatically adjusts `miniters` to correspond to `mininterval` + after long display update lag. Only works if `dynamic_miniters` + or monitor thread is enabled. + miniters : int or float, optional + Minimum progress display update interval, in iterations. + If 0 and `dynamic_miniters`, will automatically adjust to equal + `mininterval` (more CPU efficient, good for tight loops). + If > 0, will skip display of specified number of iterations. + Tweak this and `mininterval` to get very efficient loops. + If your progress is erratic with both fast and slow iterations + (network, skipping items, etc) you should set miniters=1. + ascii : bool or str, optional + If unspecified or False, use unicode (smooth blocks) to fill + the meter. The fallback is to use ASCII characters " 123456789#". + disable : bool, optional + Whether to disable the entire progressbar wrapper + [default: False]. If set to None, disable on non-TTY. + unit : str, optional + String that will be used to define the unit of each iteration + [default: it]. + unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled + automatically and a metric prefix following the + International System of Units standard will be added + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale `total` and `n`. + dynamic_ncols : bool, optional + If set, constantly alters `ncols` and `nrows` to the + environment (allowing for window resizes) [default: False]. + smoothing : float, optional + Exponential moving average smoothing factor for speed estimates + (ignored in GUI mode). Ranges from 0 (average speed) to 1 + (current/instantaneous speed) [default: 0.3]. + bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. + initial : int or float, optional + The initial counter value. Useful when restarting a progress + bar [default: 0]. If using float, consider specifying `{n:.3f}` + or similar in `bar_format`, or specifying `unit_scale`. + position : int, optional + Specify the line offset to print this bar (starting from 0) + Automatic if unspecified. + Useful to manage multiple bars at once (eg, from threads). + postfix : dict or *, optional + Specify additional stats to display at the end of the bar. + Calls `set_postfix(**postfix)` if possible (dict). + unit_divisor : float, optional + [default: 1000], ignored unless `unit_scale` is True. + write_bytes : bool, optional + Whether to write bytes. If (default: False) will write unicode. + lock_args : tuple, optional + Passed to `refresh` for intermediate output + (initialisation, iterating, and updating). + nrows : int, optional + The screen height. If specified, hides nested bars outside this + bound. If unspecified, attempts to use environment height. + The fallback is 20. + colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). + delay : float, optional + Don't display until [default: 0] seconds have elapsed. + gui : bool, optional + WARNING: internal parameter - do not use. + Use tqdm.gui.tqdm(...) instead. If set, will attempt to use + matplotlib animations for a graphical output [default: False]. + + Returns + ------- + out : decorated iterator. + """ + + monitor_interval = 10 # set to 0 to disable the thread + monitor = None + _instances = WeakSet() + + @staticmethod + def format_sizeof(num, suffix='', divisor=1000): + """ + Formats a number (greater than unity) with SI Order of Magnitude + prefixes. + + Parameters + ---------- + num : float + Number ( >= 1) to format. + suffix : str, optional + Post-postfix [default: '']. + divisor : float, optional + Divisor between prefixes [default: 1000]. + + Returns + ------- + out : str + Number with Order of Magnitude SI unit postfix. + """ + for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']: + if abs(num) < 999.5: + if abs(num) < 99.95: + if abs(num) < 9.995: + return f'{num:1.2f}{unit}{suffix}' + return f'{num:2.1f}{unit}{suffix}' + return f'{num:3.0f}{unit}{suffix}' + num /= divisor + return f'{num:3.1f}Y{suffix}' + + @staticmethod + def format_interval(t): + """ + Formats a number of seconds as a clock time, [H:]MM:SS + + Parameters + ---------- + t : int + Number of seconds. + + Returns + ------- + out : str + [H:]MM:SS + """ + mins, s = divmod(int(t), 60) + h, m = divmod(mins, 60) + return f'{h:d}:{m:02d}:{s:02d}' if h else f'{m:02d}:{s:02d}' + + @staticmethod + def format_num(n): + """ + Intelligent scientific notation (.3g). + + Parameters + ---------- + n : int or float or Numeric + A Number. + + Returns + ------- + out : str + Formatted number. + """ + f = f'{n:.3g}'.replace('e+0', 'e+').replace('e-0', 'e-') + n = str(n) + return f if len(f) < len(n) else n + + @staticmethod + def status_printer(file): + """ + Manage the printing and in-place updating of a line of characters. + Note that if the string is longer than a line, then in-place + updating may not work (it will print a new line at each refresh). + """ + fp = file + fp_flush = getattr(fp, 'flush', lambda: None) # pragma: no cover + if fp in (sys.stderr, sys.stdout): + getattr(sys.stderr, 'flush', lambda: None)() + getattr(sys.stdout, 'flush', lambda: None)() + + def fp_write(s): + fp.write(str(s)) + fp_flush() + + last_len = [0] + + def print_status(s): + len_s = disp_len(s) + fp_write('\r' + s + (' ' * max(last_len[0] - len_s, 0))) + last_len[0] = len_s + + return print_status + + @staticmethod + def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, unit='it', + unit_scale=False, rate=None, bar_format=None, postfix=None, + unit_divisor=1000, initial=0, colour=None, **extra_kwargs): + """ + Return a string-based progress bar given some parameters + + Parameters + ---------- + n : int or float + Number of finished iterations. + total : int or float + The expected total number of iterations. If meaningless (None), + only basic progress statistics are displayed (no ETA). + elapsed : float + Number of seconds passed since start. + ncols : int, optional + The width of the entire output message. If specified, + dynamically resizes `{bar}` to stay within this bound + [default: None]. If `0`, will not print any bar (only stats). + The fallback is `{bar:10}`. + prefix : str, optional + Prefix message (included in total width) [default: '']. + Use as {desc} in bar_format string. + ascii : bool, optional or str, optional + If not set, use unicode (smooth blocks) to fill the meter + [default: False]. The fallback is to use ASCII characters + " 123456789#". + unit : str, optional + The iteration unit [default: 'it']. + unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be printed with an + appropriate SI metric prefix (k = 10^3, M = 10^6, etc.) + [default: False]. If any other non-zero number, will scale + `total` and `n`. + rate : float, optional + Manual override for iteration rate. + If [default: None], uses n/elapsed. + bar_format : str, optional + Specify a custom bar string formatting. May impact performance. + [default: '{l_bar}{bar}{r_bar}'], where + l_bar='{desc}: {percentage:3.0f}%|' and + r_bar='| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, ' + '{rate_fmt}{postfix}]' + Possible vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, + percentage, elapsed, elapsed_s, ncols, nrows, desc, unit, + rate, rate_fmt, rate_noinv, rate_noinv_fmt, + rate_inv, rate_inv_fmt, postfix, unit_divisor, + remaining, remaining_s, eta. + Note that a trailing ": " is automatically removed after {desc} + if the latter is empty. + postfix : *, optional + Similar to `prefix`, but placed at the end + (e.g. for additional stats). + Note: postfix is usually a string (not a dict) for this method, + and will if possible be set to postfix = ', ' + postfix. + However other types are supported (#382). + unit_divisor : float, optional + [default: 1000], ignored unless `unit_scale` is True. + initial : int or float, optional + The initial counter value [default: 0]. + colour : str, optional + Bar colour (e.g. 'green', '#00ff00'). + + Returns + ------- + out : Formatted meter and stats, ready to display. + """ + + # sanity check: total + if total and n >= (total + 0.5): # allow float imprecision (#849) + total = None + + # apply custom scale if necessary + if unit_scale and unit_scale not in (True, 1): + if total: + total *= unit_scale + n *= unit_scale + if rate: + rate *= unit_scale # by default rate = self.avg_dn / self.avg_dt + unit_scale = False + + elapsed_str = tqdm.format_interval(elapsed) + + # if unspecified, attempt to use rate = average speed + # (we allow manual override since predicting time is an arcane art) + if rate is None and elapsed: + rate = (n - initial) / elapsed + inv_rate = 1 / rate if rate else None + format_sizeof = tqdm.format_sizeof + rate_noinv_fmt = ((format_sizeof(rate) if unit_scale else f'{rate:5.2f}') + if rate else '?') + unit + '/s' + rate_inv_fmt = ( + (format_sizeof(inv_rate) if unit_scale else f'{inv_rate:5.2f}') + if inv_rate else '?') + 's/' + unit + rate_fmt = rate_inv_fmt if inv_rate and inv_rate > 1 else rate_noinv_fmt + + if unit_scale: + n_fmt = format_sizeof(n, divisor=unit_divisor) + total_fmt = format_sizeof(total, divisor=unit_divisor) if total is not None else '?' + else: + n_fmt = str(n) + total_fmt = str(total) if total is not None else '?' + + try: + postfix = ', ' + postfix if postfix else '' + except TypeError: + pass + + remaining = (total - n) / rate if rate and total else 0 + remaining_str = tqdm.format_interval(remaining) if rate else '?' + try: + eta_dt = (datetime.now() + timedelta(seconds=remaining) + if rate and total else datetime.fromtimestamp(0, timezone.utc)) + except OverflowError: + eta_dt = datetime.max + + # format the stats displayed to the left and right sides of the bar + if prefix: + # old prefix setup work around + bool_prefix_colon_already = (prefix[-2:] == ": ") + l_bar = prefix if bool_prefix_colon_already else prefix + ": " + else: + l_bar = '' + + r_bar = f'| {n_fmt}/{total_fmt} [{elapsed_str}<{remaining_str}, {rate_fmt}{postfix}]' + + # Custom bar formatting + # Populate a dict with all available progress indicators + format_dict = { + # slight extension of self.format_dict + 'n': n, 'n_fmt': n_fmt, 'total': total, 'total_fmt': total_fmt, + 'elapsed': elapsed_str, 'elapsed_s': elapsed, + 'ncols': ncols, 'desc': prefix or '', 'unit': unit, + 'rate': inv_rate if inv_rate and inv_rate > 1 else rate, + 'rate_fmt': rate_fmt, 'rate_noinv': rate, + 'rate_noinv_fmt': rate_noinv_fmt, 'rate_inv': inv_rate, + 'rate_inv_fmt': rate_inv_fmt, + 'postfix': postfix, 'unit_divisor': unit_divisor, + 'colour': colour, + # plus more useful definitions + 'remaining': remaining_str, 'remaining_s': remaining, + 'l_bar': l_bar, 'r_bar': r_bar, 'eta': eta_dt, + **extra_kwargs} + + # total is known: we can predict some stats + if total: + # fractional and percentage progress + frac = n / total + percentage = frac * 100 + + l_bar += f'{percentage:3.0f}%|' + + if ncols == 0: + return l_bar[:-1] + r_bar[1:] + + format_dict.update(l_bar=l_bar) + if bar_format: + format_dict.update(percentage=percentage) + + # auto-remove colon for empty `{desc}` + if not prefix: + bar_format = bar_format.replace("{desc}: ", '') + else: + bar_format = "{l_bar}{bar}{r_bar}" + + full_bar = FormatReplace() + nobar = bar_format.format(bar=full_bar, **format_dict) + if not full_bar.format_called: + return nobar # no `{bar}`; nothing else to do + + # Formatting progress bar space available for bar's display + full_bar = Bar(frac, + max(1, ncols - disp_len(nobar)) if ncols else 10, + charset=Bar.ASCII if ascii is True else ascii or Bar.UTF, + colour=colour) + if not _is_ascii(full_bar.charset) and _is_ascii(bar_format): + bar_format = str(bar_format) + res = bar_format.format(bar=full_bar, **format_dict) + return disp_trim(res, ncols) if ncols else res + + elif bar_format: + # user-specified bar_format but no total + l_bar += '|' + format_dict.update(l_bar=l_bar, percentage=0) + full_bar = FormatReplace() + nobar = bar_format.format(bar=full_bar, **format_dict) + if not full_bar.format_called: + return nobar + full_bar = Bar(0, + max(1, ncols - disp_len(nobar)) if ncols else 10, + charset=Bar.BLANK, colour=colour) + res = bar_format.format(bar=full_bar, **format_dict) + return disp_trim(res, ncols) if ncols else res + else: + # no total: no progressbar, ETA, just progress stats + return (f'{(prefix + ": ") if prefix else ""}' + f'{n_fmt}{unit} [{elapsed_str}, {rate_fmt}{postfix}]') + + def __new__(cls, *_, **__): + instance = object.__new__(cls) + with cls.get_lock(): # also constructs lock if non-existent + cls._instances.add(instance) + # create monitoring thread + if cls.monitor_interval and (cls.monitor is None + or not cls.monitor.report()): + try: + cls.monitor = TMonitor(cls, cls.monitor_interval) + except Exception as e: # pragma: nocover + warn("tqdm:disabling monitor support" + " (monitor_interval = 0) due to:\n" + str(e), + TqdmMonitorWarning, stacklevel=2) + cls.monitor_interval = 0 + return instance + + @classmethod + def _get_free_pos(cls, instance=None): + """Skips specified instance.""" + positions = {abs(inst.pos) for inst in cls._instances + if inst is not instance and hasattr(inst, "pos")} + return min(set(range(len(positions) + 1)).difference(positions)) + + @classmethod + def _decr_instances(cls, instance): + """ + Remove from list and reposition another unfixed bar + to fill the new gap. + + This means that by default (where all nested bars are unfixed), + order is not maintained but screen flicker/blank space is minimised. + (tqdm<=4.44.1 moved ALL subsequent unfixed bars up.) + """ + with cls._lock: + try: + cls._instances.remove(instance) + except KeyError: + # if not instance.gui: # pragma: no cover + # raise + pass # py2: maybe magically removed already + # else: + if not instance.gui: + last = (instance.nrows or 20) - 1 + # find unfixed (`pos >= 0`) overflow (`pos >= nrows - 1`) + instances = list(filter( + lambda i: hasattr(i, "pos") and last <= i.pos, + cls._instances)) + # set first found to current `pos` + if instances: + inst = min(instances, key=lambda i: i.pos) + inst.clear(nolock=True) + inst.pos = abs(instance.pos) + + @classmethod + def write(cls, s, file=None, end="\n", nolock=False): + """Print a message via tqdm (without overlap with bars).""" + fp = file if file is not None else sys.stdout + with cls.external_write_mode(file=file, nolock=nolock): + # Write the message + fp.write(s) + fp.write(end) + + @classmethod + @contextmanager + def external_write_mode(cls, file=None, nolock=False): + """ + Disable tqdm within context and refresh tqdm when exits. + Useful when writing to standard output stream + """ + fp = file if file is not None else sys.stdout + + try: + if not nolock: + cls.get_lock().acquire() + # Clear all bars + inst_cleared = [] + for inst in getattr(cls, '_instances', []): + # Clear instance if in the target output file + # or if write output + tqdm output are both either + # sys.stdout or sys.stderr (because both are mixed in terminal) + if hasattr(inst, "start_t") and (inst.fp == fp or all( + f in (sys.stdout, sys.stderr) for f in (fp, inst.fp))): + inst.clear(nolock=True) + inst_cleared.append(inst) + yield + # Force refresh display of bars we cleared + for inst in inst_cleared: + inst.refresh(nolock=True) + finally: + if not nolock: + cls._lock.release() + + @classmethod + def set_lock(cls, lock): + """Set the global lock.""" + cls._lock = lock + + @classmethod + def get_lock(cls): + """Get the global lock. Construct it if it does not exist.""" + if not hasattr(cls, '_lock'): + cls._lock = TqdmDefaultWriteLock() + return cls._lock + + @classmethod + def pandas(cls, **tqdm_kwargs): + """ + Registers the current `tqdm` class with + pandas.core. + ( frame.DataFrame + | series.Series + | groupby.(generic.)DataFrameGroupBy + | groupby.(generic.)SeriesGroupBy + ).progress_apply + + A new instance will be created every time `progress_apply` is called, + and each instance will automatically `close()` upon completion. + + Parameters + ---------- + tqdm_kwargs : arguments for the tqdm instance + + Examples + -------- + >>> import pandas as pd + >>> import numpy as np + >>> from tqdm import tqdm + >>> from tqdm.gui import tqdm as tqdm_gui + >>> + >>> df = pd.DataFrame(np.random.randint(0, 100, (100000, 6))) + >>> tqdm.pandas(ncols=50) # can use tqdm_gui, optional kwargs, etc + >>> # Now you can use `progress_apply` instead of `apply` + >>> df.groupby(0).progress_apply(lambda x: x**2) + + References + ---------- + + """ + from warnings import catch_warnings, simplefilter + + from pandas.core.frame import DataFrame + from pandas.core.series import Series + try: + with catch_warnings(): + simplefilter("ignore", category=FutureWarning) + from pandas import Panel + except ImportError: # pandas>=1.2.0 + Panel = None + Rolling, Expanding = None, None + try: # pandas>=1.0.0 + from pandas.core.window.rolling import _Rolling_and_Expanding + except ImportError: + try: # pandas>=0.18.0 + from pandas.core.window import _Rolling_and_Expanding + except ImportError: # pandas>=1.2.0 + try: # pandas>=1.2.0 + from pandas.core.window.expanding import Expanding + from pandas.core.window.rolling import Rolling + _Rolling_and_Expanding = Rolling, Expanding + except ImportError: # pragma: no cover + _Rolling_and_Expanding = None + try: # pandas>=0.25.0 + from pandas.core.groupby.generic import SeriesGroupBy # , NDFrameGroupBy + from pandas.core.groupby.generic import DataFrameGroupBy + except ImportError: # pragma: no cover + try: # pandas>=0.23.0 + from pandas.core.groupby.groupby import DataFrameGroupBy, SeriesGroupBy + except ImportError: + from pandas.core.groupby import DataFrameGroupBy, SeriesGroupBy + try: # pandas>=0.23.0 + from pandas.core.groupby.groupby import GroupBy + except ImportError: # pragma: no cover + from pandas.core.groupby import GroupBy + + try: # pandas>=0.23.0 + from pandas.core.groupby.groupby import PanelGroupBy + except ImportError: + try: + from pandas.core.groupby import PanelGroupBy + except ImportError: # pandas>=0.25.0 + PanelGroupBy = None + + tqdm_kwargs = tqdm_kwargs.copy() + deprecated_t = [tqdm_kwargs.pop('deprecated_t', None)] + + def inner_generator(df_function='apply'): + def inner(df, func, *args, **kwargs): + """ + Parameters + ---------- + df : (DataFrame|Series)[GroupBy] + Data (may be grouped). + func : function + To be applied on the (grouped) data. + **kwargs : optional + Transmitted to `df.apply()`. + """ + + # Precompute total iterations + total = tqdm_kwargs.pop("total", getattr(df, 'ngroups', None)) + if total is None: # not grouped + if df_function == 'applymap': + total = df.size + elif isinstance(df, Series): + total = len(df) + elif (_Rolling_and_Expanding is None or + not isinstance(df, _Rolling_and_Expanding)): + # DataFrame or Panel + axis = kwargs.get('axis', 0) + if axis == 'index': + axis = 0 + elif axis == 'columns': + axis = 1 + # when axis=0, total is shape[axis1] + total = df.size // df.shape[axis] + + # Init bar + if deprecated_t[0] is not None: + t = deprecated_t[0] + deprecated_t[0] = None + else: + t = cls(total=total, **tqdm_kwargs) + + if len(args) > 0: + # *args intentionally not supported (see #244, #299) + TqdmDeprecationWarning( + "Except func, normal arguments are intentionally" + + " not supported by" + + " `(DataFrame|Series|GroupBy).progress_apply`." + + " Use keyword arguments instead.", + fp_write=getattr(t.fp, 'write', sys.stderr.write)) + + try: # pandas>=1.3.0 + from pandas.core.common import is_builtin_func + except ImportError: + is_builtin_func = df._is_builtin_func + try: + func = is_builtin_func(func) + except TypeError: + pass + + # Define bar updating wrapper + def wrapper(*args, **kwargs): + # update tbar correctly + # it seems `pandas apply` calls `func` twice + # on the first column/row to decide whether it can + # take a fast or slow code path; so stop when t.total==t.n + t.update(n=1 if not t.total or t.n < t.total else 0) + return func(*args, **kwargs) + + # Apply the provided function (in **kwargs) + # on the df using our wrapper (which provides bar updating) + try: + return getattr(df, df_function)(wrapper, **kwargs) + finally: + t.close() + + return inner + + # Monkeypatch pandas to provide easy methods + # Enable custom tqdm progress in pandas! + Series.progress_apply = inner_generator() + SeriesGroupBy.progress_apply = inner_generator() + Series.progress_map = inner_generator('map') + SeriesGroupBy.progress_map = inner_generator('map') + + DataFrame.progress_apply = inner_generator() + DataFrameGroupBy.progress_apply = inner_generator() + DataFrame.progress_applymap = inner_generator('applymap') + DataFrame.progress_map = inner_generator('map') + DataFrameGroupBy.progress_map = inner_generator('map') + + if Panel is not None: + Panel.progress_apply = inner_generator() + if PanelGroupBy is not None: + PanelGroupBy.progress_apply = inner_generator() + + GroupBy.progress_apply = inner_generator() + GroupBy.progress_aggregate = inner_generator('aggregate') + GroupBy.progress_transform = inner_generator('transform') + + if Rolling is not None and Expanding is not None: + Rolling.progress_apply = inner_generator() + Expanding.progress_apply = inner_generator() + elif _Rolling_and_Expanding is not None: + _Rolling_and_Expanding.progress_apply = inner_generator() + + # override defaults via env vars + @envwrap("TQDM_", is_method=True, types={'total': float, 'ncols': int, 'miniters': float, + 'position': int, 'nrows': int}) + def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None, + ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None, + ascii=None, disable=False, unit='it', unit_scale=False, + dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0, + position=None, postfix=None, unit_divisor=1000, write_bytes=False, + lock_args=None, nrows=None, colour=None, delay=0.0, gui=False, + **kwargs): + """see tqdm.tqdm for arguments""" + if file is None: + file = sys.stderr + + if write_bytes: + # Despite coercing unicode into bytes, py2 sys.std* streams + # should have bytes written to them. + file = SimpleTextIOWrapper( + file, encoding=getattr(file, 'encoding', None) or 'utf-8') + + file = DisableOnWriteError(file, tqdm_instance=self) + + if disable is None and hasattr(file, "isatty") and not file.isatty(): + disable = True + + if total is None and iterable is not None: + try: + total = len(iterable) + except (TypeError, AttributeError): + total = None + if total == float("inf"): + # Infinite iterations, behave same as unknown + total = None + + if disable: + self.iterable = iterable + self.disable = disable + with self._lock: + self.pos = self._get_free_pos(self) + self._instances.remove(self) + self.n = initial + self.total = total + self.leave = leave + return + + if kwargs: + self.disable = True + with self._lock: + self.pos = self._get_free_pos(self) + self._instances.remove(self) + raise ( + TqdmDeprecationWarning( + "`nested` is deprecated and automated.\n" + "Use `position` instead for manual control.\n", + fp_write=getattr(file, 'write', sys.stderr.write)) + if "nested" in kwargs else + TqdmKeyError("Unknown argument(s): " + str(kwargs))) + + # Preprocess the arguments + if ( + (ncols is None or nrows is None) and (file in (sys.stderr, sys.stdout)) + ) or dynamic_ncols: # pragma: no cover + if dynamic_ncols: + dynamic_ncols = _screen_shape_wrapper() + if dynamic_ncols: + ncols, nrows = dynamic_ncols(file) + else: + _dynamic_ncols = _screen_shape_wrapper() + if _dynamic_ncols: + _ncols, _nrows = _dynamic_ncols(file) + if ncols is None: + ncols = _ncols + if nrows is None: + nrows = _nrows + + if miniters is None: + miniters = 0 + dynamic_miniters = True + else: + dynamic_miniters = False + + if mininterval is None: + mininterval = 0 + + if maxinterval is None: + maxinterval = 0 + + if ascii is None: + ascii = not _supports_unicode(file) + + if bar_format and ascii is not True and not _is_ascii(ascii): + # Convert bar format into unicode since terminal uses unicode + bar_format = str(bar_format) + + if smoothing is None: + smoothing = 0 + + # Store the arguments + self.iterable = iterable + self.desc = desc or '' + self.total = total + self.leave = leave + self.fp = file + self.ncols = ncols + self.nrows = nrows + self.mininterval = mininterval + self.maxinterval = maxinterval + self.miniters = miniters + self.dynamic_miniters = dynamic_miniters + self.ascii = ascii + self.disable = disable + self.unit = unit + self.unit_scale = unit_scale + self.unit_divisor = unit_divisor + self.initial = initial + self.lock_args = lock_args + self.delay = delay + self.gui = gui + self.dynamic_ncols = dynamic_ncols + self.smoothing = smoothing + self._ema_dn = EMA(smoothing) + self._ema_dt = EMA(smoothing) + self._ema_miniters = EMA(smoothing) + self.bar_format = bar_format + self.postfix = None + self.colour = colour + self._time = time + if postfix: + try: + self.set_postfix(refresh=False, **postfix) + except TypeError: + self.postfix = postfix + + # Init the iterations counters + self.last_print_n = initial + self.n = initial + + # if nested, at initial sp() call we replace '\r' by '\n' to + # not overwrite the outer progress bar + with self._lock: + # mark fixed positions as negative + self.pos = self._get_free_pos(self) if position is None else -position + + if not gui: + # Initialize the screen printer + self.sp = self.status_printer(self.fp) + if delay <= 0: + self.refresh(lock_args=self.lock_args) + + # Init the time counter + self.last_print_t = self._time() + # NB: Avoid race conditions by setting start_t at the very end of init + self.start_t = self.last_print_t + + def __bool__(self): + if self.total is not None: + return self.total > 0 + if self.iterable is None: + raise TypeError('bool() undefined when iterable == total == None') + return bool(self.iterable) + + def __len__(self): + return ( + self.total if self.iterable is None + else self.iterable.shape[0] if hasattr(self.iterable, "shape") + else len(self.iterable) if hasattr(self.iterable, "__len__") + else self.iterable.__length_hint__() if hasattr(self.iterable, "__length_hint__") + else getattr(self, "total", None)) + + def __reversed__(self): + try: + orig = self.iterable + except AttributeError: + raise TypeError("'tqdm' object is not reversible") + else: + self.iterable = reversed(self.iterable) + return self.__iter__() + finally: + self.iterable = orig + + def __contains__(self, item): + contains = getattr(self.iterable, '__contains__', None) + return contains(item) if contains is not None else item in self.__iter__() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + try: + self.close() + except AttributeError: + # maybe eager thread cleanup upon external error + if (exc_type, exc_value, traceback) == (None, None, None): + raise + warn("AttributeError ignored", TqdmWarning, stacklevel=2) + + def __del__(self): + self.close() + + def __str__(self): + return self.format_meter(**self.format_dict) + + @property + def _comparable(self): + return abs(getattr(self, "pos", 1 << 31)) + + def __hash__(self): + return id(self) + + def __iter__(self): + """Backward-compatibility to use: for x in tqdm(iterable)""" + + # Inlining instance variables as locals (speed optimisation) + iterable = self.iterable + + # If the bar is disabled, then just walk the iterable + # (note: keep this check outside the loop for performance) + if self.disable: + for obj in iterable: + yield obj + return + + mininterval = self.mininterval + last_print_t = self.last_print_t + last_print_n = self.last_print_n + min_start_t = self.start_t + self.delay + n = self.n + time = self._time + + try: + for obj in iterable: + yield obj + # Update and possibly print the progressbar. + # Note: does not call self.update(1) for speed optimisation. + n += 1 + + if n - last_print_n >= self.miniters: + cur_t = time() + dt = cur_t - last_print_t + if dt >= mininterval and cur_t >= min_start_t: + self.update(n - last_print_n) + last_print_n = self.last_print_n + last_print_t = self.last_print_t + finally: + self.n = n + self.close() + + def update(self, n=1): + """ + Manually update the progress bar, useful for streams + such as reading files. + E.g.: + >>> t = tqdm(total=filesize) # Initialise + >>> for current_buffer in stream: + ... ... + ... t.update(len(current_buffer)) + >>> t.close() + The last line is highly recommended, but possibly not necessary if + `t.update()` will be called in such a way that `filesize` will be + exactly reached and printed. + + Parameters + ---------- + n : int or float, optional + Increment to add to the internal counter of iterations + [default: 1]. If using float, consider specifying `{n:.3f}` + or similar in `bar_format`, or specifying `unit_scale`. + + Returns + ------- + out : bool or None + True if a `display()` was triggered. + """ + if self.disable: + return + + if n < 0: + self.last_print_n += n # for auto-refresh logic to work + self.n += n + + # check counter first to reduce calls to time() + if self.n - self.last_print_n >= self.miniters: + cur_t = self._time() + dt = cur_t - self.last_print_t + if dt >= self.mininterval and cur_t >= self.start_t + self.delay: + cur_t = self._time() + dn = self.n - self.last_print_n # >= n + if self.smoothing and dt and dn: + # EMA (not just overall average) + self._ema_dn(dn) + self._ema_dt(dt) + self.refresh(lock_args=self.lock_args) + if self.dynamic_miniters: + # If no `miniters` was specified, adjust automatically to the + # maximum iteration rate seen so far between two prints. + # e.g.: After running `tqdm.update(5)`, subsequent + # calls to `tqdm.update()` will only cause an update after + # at least 5 more iterations. + if self.maxinterval and dt >= self.maxinterval: + self.miniters = dn * (self.mininterval or self.maxinterval) / dt + elif self.smoothing: + # EMA miniters update + self.miniters = self._ema_miniters( + dn * (self.mininterval / dt if self.mininterval and dt + else 1)) + else: + # max iters between two prints + self.miniters = max(self.miniters, dn) + + # Store old values for next call + self.last_print_n = self.n + self.last_print_t = cur_t + return True + + def close(self): + """Cleanup and (if leave=False) close the progressbar.""" + if self.disable: + return + + # Prevent multiple closures + self.disable = True + + # decrement instance pos and remove from internal set + pos = abs(self.pos) + self._decr_instances(self) + + if self.last_print_t < self.start_t + self.delay: + # haven't ever displayed; nothing to clear + return + + # GUI mode + if getattr(self, 'sp', None) is None: + return + + # annoyingly, _supports_unicode isn't good enough + def fp_write(s): + self.fp.write(str(s)) + + try: + fp_write('') + except ValueError as e: + if 'closed' in str(e): + return + raise # pragma: no cover + + leave = pos == 0 if self.leave is None else self.leave + + with self._lock: + if leave: + # stats for overall rate (no weighted average) + self._ema_dt = lambda: None + self.display(pos=0) + fp_write('\n') + else: + # clear previous display + if self.display(msg='', pos=pos) and not pos: + fp_write('\r') + + def clear(self, nolock=False): + """Clear current bar display.""" + if self.disable: + return + + if not nolock: + self._lock.acquire() + pos = abs(self.pos) + if pos < (self.nrows or 20): + self.moveto(pos) + self.sp('') + self.fp.write('\r') # place cursor back at the beginning of line + self.moveto(-pos) + if not nolock: + self._lock.release() + + def refresh(self, nolock=False, lock_args=None): + """ + Force refresh the display of this bar. + + Parameters + ---------- + nolock : bool, optional + If `True`, does not lock. + If [default: `False`]: calls `acquire()` on internal lock. + lock_args : tuple, optional + Passed to internal lock's `acquire()`. + If specified, will only `display()` if `acquire()` returns `True`. + """ + if self.disable: + return + + if not nolock: + if lock_args: + if not self._lock.acquire(*lock_args): + return False + else: + self._lock.acquire() + self.display() + if not nolock: + self._lock.release() + return True + + def unpause(self): + """Restart tqdm timer from last print time.""" + if self.disable: + return + cur_t = self._time() + self.start_t += cur_t - self.last_print_t + self.last_print_t = cur_t + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Consider combining with `leave=True`. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + self.n = 0 + if total is not None: + self.total = total + if self.disable: + return + self.last_print_n = 0 + self.last_print_t = self.start_t = self._time() + self._ema_dn = EMA(self.smoothing) + self._ema_dt = EMA(self.smoothing) + self._ema_miniters = EMA(self.smoothing) + self.refresh() + + def set_description(self, desc=None, refresh=True): + """ + Set/modify description of the progress bar. + + Parameters + ---------- + desc : str, optional + refresh : bool, optional + Forces refresh [default: True]. + """ + self.desc = desc + ': ' if desc else '' + if refresh: + self.refresh() + + def set_description_str(self, desc=None, refresh=True): + """Set/modify description without ': ' appended.""" + self.desc = desc or '' + if refresh: + self.refresh() + + def set_postfix(self, ordered_dict=None, refresh=True, **kwargs): + """ + Set/modify postfix (additional stats) + with automatic formatting based on datatype. + + Parameters + ---------- + ordered_dict : dict or OrderedDict, optional + refresh : bool, optional + Forces refresh [default: True]. + kwargs : dict, optional + """ + # Sort in alphabetical order to be more deterministic + postfix = OrderedDict([] if ordered_dict is None else ordered_dict) + for key in sorted(kwargs.keys()): + postfix[key] = kwargs[key] + # Preprocess stats according to datatype + for key in postfix.keys(): + # Number: limit the length of the string + if isinstance(postfix[key], Number): + postfix[key] = self.format_num(postfix[key]) + # Else for any other type, try to get the string conversion + elif not isinstance(postfix[key], str): + postfix[key] = str(postfix[key]) + # Else if it's a string, don't need to preprocess anything + # Stitch together to get the final postfix + self.postfix = ', '.join(key + '=' + postfix[key].strip() + for key in postfix.keys()) + if refresh: + self.refresh() + + def set_postfix_str(self, s='', refresh=True): + """ + Postfix without dictionary expansion, similar to prefix handling. + """ + self.postfix = str(s) + if refresh: + self.refresh() + + def moveto(self, n): + # TODO: private method + self.fp.write('\n' * n + _term_move_up() * -n) + getattr(self.fp, 'flush', lambda: None)() + + @property + def format_dict(self): + """Public API for read-only member access.""" + if self.disable and not hasattr(self, 'unit'): + return defaultdict(lambda: None, { + 'n': self.n, 'total': self.total, 'elapsed': 0, 'unit': 'it'}) + if self.dynamic_ncols: + self.ncols, self.nrows = self.dynamic_ncols(self.fp) + return { + 'n': self.n, 'total': self.total, + 'elapsed': self._time() - self.start_t if hasattr(self, 'start_t') else 0, + 'ncols': self.ncols, 'nrows': self.nrows, 'prefix': self.desc, + 'ascii': self.ascii, 'unit': self.unit, 'unit_scale': self.unit_scale, + 'rate': self._ema_dn() / self._ema_dt() if self._ema_dt() else None, + 'bar_format': self.bar_format, 'postfix': self.postfix, + 'unit_divisor': self.unit_divisor, 'initial': self.initial, + 'colour': self.colour} + + def display(self, msg=None, pos=None): + """ + Use `self.sp` to display `msg` in the specified `pos`. + + Consider overloading this function when inheriting to use e.g.: + `self.some_frontend(**self.format_dict)` instead of `self.sp`. + + Parameters + ---------- + msg : str, optional. What to display (default: `repr(self)`). + pos : int, optional. Position to `moveto` + (default: `abs(self.pos)`). + """ + if pos is None: + pos = abs(self.pos) + + nrows = self.nrows or 20 + if pos >= nrows - 1: + if pos >= nrows: + return False + if msg or msg is None: # override at `nrows - 1` + msg = " ... (more hidden) ..." + + if not hasattr(self, "sp"): + raise TqdmDeprecationWarning( + "Please use `tqdm.gui.tqdm(...)`" + " instead of `tqdm(..., gui=True)`\n", + fp_write=getattr(self.fp, 'write', sys.stderr.write)) + + if pos: + self.moveto(pos) + self.sp(self.__str__() if msg is None else msg) + if pos: + self.moveto(-pos) + return True + + @classmethod + @contextmanager + def wrapattr(cls, stream, method, total=None, bytes=True, **tqdm_kwargs): + """ + stream : file-like object. + method : str, "read" or "write". The result of `read()` and + the first argument of `write()` should have a `len()`. + + >>> with tqdm.wrapattr(file_obj, "read", total=file_obj.size) as fobj: + ... while True: + ... chunk = fobj.read(chunk_size) + ... if not chunk: + ... break + """ + with cls(total=total, **tqdm_kwargs) as t: + if bytes: + t.unit = "B" + t.unit_scale = True + t.unit_divisor = 1024 + yield CallbackIOWrapper(t.update, stream, method) + + +def trange(*args, **kwargs): + """Shortcut for tqdm(range(*args), **kwargs).""" + return tqdm(range(*args), **kwargs) diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/tk.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/tk.py new file mode 100644 index 0000000..dfebf5c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/tk.py @@ -0,0 +1,196 @@ +""" +Tkinter GUI progressbar decorator for iterators. + +Usage: +>>> from tqdm.tk import trange, tqdm +>>> for i in trange(10): +... ... +""" +import re +import sys +import tkinter +import tkinter.ttk as ttk +from warnings import warn + +from .std import TqdmExperimentalWarning, TqdmWarning +from .std import tqdm as std_tqdm + +__author__ = {"github.com/": ["richardsheridan", "casperdcl"]} +__all__ = ['tqdm_tk', 'ttkrange', 'tqdm', 'trange'] + + +class tqdm_tk(std_tqdm): # pragma: no cover + """ + Experimental Tkinter GUI version of tqdm! + + Note: Window interactivity suffers if `tqdm_tk` is not running within + a Tkinter mainloop and values are generated infrequently. In this case, + consider calling `tqdm_tk.refresh()` frequently in the Tk thread. + """ + + # TODO: @classmethod: write()? + + def __init__(self, *args, **kwargs): + """ + This class accepts the following parameters *in addition* to + the parameters accepted by `tqdm`. + + Parameters + ---------- + grab : bool, optional + Grab the input across all windows of the process. + tk_parent : `tkinter.Wm`, optional + Parent Tk window. + cancel_callback : Callable, optional + Create a cancel button and set `cancel_callback` to be called + when the cancel or window close button is clicked. + """ + kwargs = kwargs.copy() + kwargs['gui'] = True + # convert disable = None to False + kwargs['disable'] = bool(kwargs.get('disable', False)) + self._warn_leave = 'leave' in kwargs + grab = kwargs.pop('grab', False) + tk_parent = kwargs.pop('tk_parent', None) + self._cancel_callback = kwargs.pop('cancel_callback', None) + super(tqdm_tk, self).__init__(*args, **kwargs) + + if self.disable: + return + + if tk_parent is None: # Discover parent widget + try: + tk_parent = tkinter._default_root + except AttributeError: + raise AttributeError( + "`tk_parent` required when using `tkinter.NoDefaultRoot()`") + if tk_parent is None: # use new default root window as display + self._tk_window = tkinter.Tk() + else: # some other windows already exist + self._tk_window = tkinter.Toplevel() + else: + self._tk_window = tkinter.Toplevel(tk_parent) + + warn("GUI is experimental/alpha", TqdmExperimentalWarning, stacklevel=2) + self._tk_dispatching = self._tk_dispatching_helper() + + self._tk_window.protocol("WM_DELETE_WINDOW", self.cancel) + self._tk_window.wm_title(self.desc) + self._tk_window.wm_attributes("-topmost", 1) + self._tk_window.after(0, lambda: self._tk_window.wm_attributes("-topmost", 0)) + self._tk_n_var = tkinter.DoubleVar(self._tk_window, value=0) + self._tk_text_var = tkinter.StringVar(self._tk_window) + pbar_frame = ttk.Frame(self._tk_window, padding=5) + pbar_frame.pack() + _tk_label = ttk.Label(pbar_frame, textvariable=self._tk_text_var, + wraplength=600, anchor="center", justify="center") + _tk_label.pack() + self._tk_pbar = ttk.Progressbar( + pbar_frame, variable=self._tk_n_var, length=450) + if self.total is not None: + self._tk_pbar.configure(maximum=self.total) + else: + self._tk_pbar.configure(mode="indeterminate") + self._tk_pbar.pack() + if self._cancel_callback is not None: + _tk_button = ttk.Button(pbar_frame, text="Cancel", command=self.cancel) + _tk_button.pack() + if grab: + self._tk_window.grab_set() + + def close(self): + if self.disable: + return + + self.disable = True + + with self.get_lock(): + self._instances.remove(self) + + def _close(): + self._tk_window.after('idle', self._tk_window.destroy) + if not self._tk_dispatching: + self._tk_window.update() + + self._tk_window.protocol("WM_DELETE_WINDOW", _close) + + # if leave is set but we are self-dispatching, the left window is + # totally unresponsive unless the user manually dispatches + if not self.leave: + _close() + elif not self._tk_dispatching: + if self._warn_leave: + warn("leave flag ignored if not in tkinter mainloop", + TqdmWarning, stacklevel=2) + _close() + + def clear(self, *_, **__): + pass + + def display(self, *_, **__): + self._tk_n_var.set(self.n) + d = self.format_dict + # remove {bar} + d['bar_format'] = (d['bar_format'] or "{l_bar}{r_bar}").replace( + "{bar}", "") + msg = self.format_meter(**d) + if '' in msg: + msg = "".join(re.split(r'\|?\|?', msg, maxsplit=1)) + self._tk_text_var.set(msg) + if not self._tk_dispatching: + self._tk_window.update() + + def set_description(self, desc=None, refresh=True): + self.set_description_str(desc, refresh) + + def set_description_str(self, desc=None, refresh=True): + self.desc = desc + if not self.disable: + self._tk_window.wm_title(desc) + if refresh and not self._tk_dispatching: + self._tk_window.update() + + def cancel(self): + """ + `cancel_callback()` followed by `close()` + when close/cancel buttons clicked. + """ + if self._cancel_callback is not None: + self._cancel_callback() + self.close() + + def reset(self, total=None): + """ + Resets to 0 iterations for repeated use. + + Parameters + ---------- + total : int or float, optional. Total to use for the new bar. + """ + if hasattr(self, '_tk_pbar'): + if total is None: + self._tk_pbar.configure(maximum=100, mode="indeterminate") + else: + self._tk_pbar.configure(maximum=total, mode="determinate") + super(tqdm_tk, self).reset(total=total) + + @staticmethod + def _tk_dispatching_helper(): + """determine if Tkinter mainloop is dispatching events""" + codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} + for frame in sys._current_frames().values(): + while frame: + if frame.f_code in codes: + return True + frame = frame.f_back + return False + + +def ttkrange(*args, **kwargs): + """Shortcut for `tqdm.tk.tqdm(range(*args), **kwargs)`.""" + return tqdm_tk(range(*args), **kwargs) + + +# Aliases +tqdm = tqdm_tk +trange = ttkrange diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/tqdm.1 b/.pythonlibs/lib/python3.10/site-packages/tqdm/tqdm.1 new file mode 100644 index 0000000..b90ab4b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/tqdm.1 @@ -0,0 +1,314 @@ +.\" Automatically generated by Pandoc 1.19.2 +.\" +.TH "TQDM" "1" "2015\-2021" "tqdm User Manuals" "" +.hy +.SH NAME +.PP +tqdm \- fast, extensible progress bar for Python and CLI +.SH SYNOPSIS +.PP +tqdm [\f[I]options\f[]] +.SH DESCRIPTION +.PP +See . +Can be used as a pipe: +.IP +.nf +\f[C] +$\ #\ count\ lines\ of\ code +$\ cat\ *.py\ |\ tqdm\ |\ wc\ \-l +327it\ [00:00,\ 981773.38it/s] +327 + +$\ #\ find\ all\ files +$\ find\ .\ \-name\ "*.py"\ |\ tqdm\ |\ wc\ \-l +432it\ [00:00,\ 833842.30it/s] +432 + +#\ ...\ and\ more\ info +$\ find\ .\ \-name\ \[aq]*.py\[aq]\ \-exec\ wc\ \-l\ \\{}\ \\;\ \\ +\ \ |\ tqdm\ \-\-total\ 432\ \-\-unit\ files\ \-\-desc\ counting\ \\ +\ \ |\ awk\ \[aq]{\ sum\ +=\ $1\ };\ END\ {\ print\ sum\ }\[aq] +counting:\ 100%|█████████|\ 432/432\ [00:00<00:00,\ 794361.83files/s] +131998 +\f[] +.fi +.SH OPTIONS +.TP +.B \-h, \-\-help +Print this help and exit. +.RS +.RE +.TP +.B \-v, \-\-version +Print version and exit. +.RS +.RE +.TP +.B \-\-desc=\f[I]desc\f[] +str, optional. +Prefix for the progressbar. +.RS +.RE +.TP +.B \-\-total=\f[I]total\f[] +int or float, optional. +The number of expected iterations. +If unspecified, len(iterable) is used if possible. +If float("inf") or as a last resort, only basic progress statistics are +displayed (no ETA, no progressbar). +If \f[C]gui\f[] is True and this parameter needs subsequent updating, +specify an initial arbitrary large positive number, e.g. +9e9. +.RS +.RE +.TP +.B \-\-leave +bool, optional. +If [default: True], keeps all traces of the progressbar upon termination +of iteration. +If \f[C]None\f[], will leave only if \f[C]position\f[] is \f[C]0\f[]. +.RS +.RE +.TP +.B \-\-ncols=\f[I]ncols\f[] +int, optional. +The width of the entire output message. +If specified, dynamically resizes the progressbar to stay within this +bound. +If unspecified, attempts to use environment width. +The fallback is a meter width of 10 and no limit for the counter and +statistics. +If 0, will not print any meter (only stats). +.RS +.RE +.TP +.B \-\-mininterval=\f[I]mininterval\f[] +float, optional. +Minimum progress display update interval [default: 0.1] seconds. +.RS +.RE +.TP +.B \-\-maxinterval=\f[I]maxinterval\f[] +float, optional. +Maximum progress display update interval [default: 10] seconds. +Automatically adjusts \f[C]miniters\f[] to correspond to +\f[C]mininterval\f[] after long display update lag. +Only works if \f[C]dynamic_miniters\f[] or monitor thread is enabled. +.RS +.RE +.TP +.B \-\-miniters=\f[I]miniters\f[] +int or float, optional. +Minimum progress display update interval, in iterations. +If 0 and \f[C]dynamic_miniters\f[], will automatically adjust to equal +\f[C]mininterval\f[] (more CPU efficient, good for tight loops). +If > 0, will skip display of specified number of iterations. +Tweak this and \f[C]mininterval\f[] to get very efficient loops. +If your progress is erratic with both fast and slow iterations (network, +skipping items, etc) you should set miniters=1. +.RS +.RE +.TP +.B \-\-ascii=\f[I]ascii\f[] +bool or str, optional. +If unspecified or False, use unicode (smooth blocks) to fill the meter. +The fallback is to use ASCII characters " 123456789#". +.RS +.RE +.TP +.B \-\-disable +bool, optional. +Whether to disable the entire progressbar wrapper [default: False]. +If set to None, disable on non\-TTY. +.RS +.RE +.TP +.B \-\-unit=\f[I]unit\f[] +str, optional. +String that will be used to define the unit of each iteration [default: +it]. +.RS +.RE +.TP +.B \-\-unit\-scale=\f[I]unit_scale\f[] +bool or int or float, optional. +If 1 or True, the number of iterations will be reduced/scaled +automatically and a metric prefix following the International System of +Units standard will be added (kilo, mega, etc.) [default: False]. +If any other non\-zero number, will scale \f[C]total\f[] and \f[C]n\f[]. +.RS +.RE +.TP +.B \-\-dynamic\-ncols +bool, optional. +If set, constantly alters \f[C]ncols\f[] and \f[C]nrows\f[] to the +environment (allowing for window resizes) [default: False]. +.RS +.RE +.TP +.B \-\-smoothing=\f[I]smoothing\f[] +float, optional. +Exponential moving average smoothing factor for speed estimates (ignored +in GUI mode). +Ranges from 0 (average speed) to 1 (current/instantaneous speed) +[default: 0.3]. +.RS +.RE +.TP +.B \-\-bar\-format=\f[I]bar_format\f[] +str, optional. +Specify a custom bar string formatting. +May impact performance. +[default: \[aq]{l_bar}{bar}{r_bar}\[aq]], where l_bar=\[aq]{desc}: +{percentage:3.0f}%|\[aq] and r_bar=\[aq]| {n_fmt}/{total_fmt} +[{elapsed}<{remaining}, \[aq] \[aq]{rate_fmt}{postfix}]\[aq] Possible +vars: l_bar, bar, r_bar, n, n_fmt, total, total_fmt, percentage, +elapsed, elapsed_s, ncols, nrows, desc, unit, rate, rate_fmt, +rate_noinv, rate_noinv_fmt, rate_inv, rate_inv_fmt, postfix, +unit_divisor, remaining, remaining_s, eta. +Note that a trailing ": " is automatically removed after {desc} if the +latter is empty. +.RS +.RE +.TP +.B \-\-initial=\f[I]initial\f[] +int or float, optional. +The initial counter value. +Useful when restarting a progress bar [default: 0]. +If using float, consider specifying \f[C]{n:.3f}\f[] or similar in +\f[C]bar_format\f[], or specifying \f[C]unit_scale\f[]. +.RS +.RE +.TP +.B \-\-position=\f[I]position\f[] +int, optional. +Specify the line offset to print this bar (starting from 0) Automatic if +unspecified. +Useful to manage multiple bars at once (eg, from threads). +.RS +.RE +.TP +.B \-\-postfix=\f[I]postfix\f[] +dict or *, optional. +Specify additional stats to display at the end of the bar. +Calls \f[C]set_postfix(**postfix)\f[] if possible (dict). +.RS +.RE +.TP +.B \-\-unit\-divisor=\f[I]unit_divisor\f[] +float, optional. +[default: 1000], ignored unless \f[C]unit_scale\f[] is True. +.RS +.RE +.TP +.B \-\-write\-bytes +bool, optional. +Whether to write bytes. +If (default: False) will write unicode. +.RS +.RE +.TP +.B \-\-lock\-args=\f[I]lock_args\f[] +tuple, optional. +Passed to \f[C]refresh\f[] for intermediate output (initialisation, +iterating, and updating). +.RS +.RE +.TP +.B \-\-nrows=\f[I]nrows\f[] +int, optional. +The screen height. +If specified, hides nested bars outside this bound. +If unspecified, attempts to use environment height. +The fallback is 20. +.RS +.RE +.TP +.B \-\-colour=\f[I]colour\f[] +str, optional. +Bar colour (e.g. +\[aq]green\[aq], \[aq]#00ff00\[aq]). +.RS +.RE +.TP +.B \-\-delay=\f[I]delay\f[] +float, optional. +Don\[aq]t display until [default: 0] seconds have elapsed. +.RS +.RE +.TP +.B \-\-delim=\f[I]delim\f[] +chr, optional. +Delimiting character [default: \[aq]\\n\[aq]]. +Use \[aq]\\0\[aq] for null. +N.B.: on Windows systems, Python converts \[aq]\\n\[aq] to +\[aq]\\r\\n\[aq]. +.RS +.RE +.TP +.B \-\-buf\-size=\f[I]buf_size\f[] +int, optional. +String buffer size in bytes [default: 256] used when \f[C]delim\f[] is +specified. +.RS +.RE +.TP +.B \-\-bytes +bool, optional. +If true, will count bytes, ignore \f[C]delim\f[], and default +\f[C]unit_scale\f[] to True, \f[C]unit_divisor\f[] to 1024, and +\f[C]unit\f[] to \[aq]B\[aq]. +.RS +.RE +.TP +.B \-\-tee +bool, optional. +If true, passes \f[C]stdin\f[] to both \f[C]stderr\f[] and +\f[C]stdout\f[]. +.RS +.RE +.TP +.B \-\-update +bool, optional. +If true, will treat input as newly elapsed iterations, i.e. +numbers to pass to \f[C]update()\f[]. +Note that this is slow (~2e5 it/s) since every input must be decoded as +a number. +.RS +.RE +.TP +.B \-\-update\-to +bool, optional. +If true, will treat input as total elapsed iterations, i.e. +numbers to assign to \f[C]self.n\f[]. +Note that this is slow (~2e5 it/s) since every input must be decoded as +a number. +.RS +.RE +.TP +.B \-\-null +bool, optional. +If true, will discard input (no stdout). +.RS +.RE +.TP +.B \-\-manpath=\f[I]manpath\f[] +str, optional. +Directory in which to install tqdm man pages. +.RS +.RE +.TP +.B \-\-comppath=\f[I]comppath\f[] +str, optional. +Directory in which to place tqdm completion. +.RS +.RE +.TP +.B \-\-log=\f[I]log\f[] +str, optional. +CRITICAL|FATAL|ERROR|WARN(ING)|[default: \[aq]INFO\[aq]]|DEBUG|NOTSET. +.RS +.RE +.SH AUTHORS +tqdm developers . diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/utils.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/utils.py new file mode 100644 index 0000000..9883fda --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/utils.py @@ -0,0 +1,399 @@ +""" +General helpers required for `tqdm.std`. +""" +import os +import re +import sys +from functools import partial, partialmethod, wraps +from inspect import signature +# TODO consider using wcswidth third-party package for 0-width characters +from unicodedata import east_asian_width +from warnings import warn +from weakref import proxy + +_range, _unich, _unicode, _basestring = range, chr, str, str +CUR_OS = sys.platform +IS_WIN = any(CUR_OS.startswith(i) for i in ['win32', 'cygwin']) +IS_NIX = any(CUR_OS.startswith(i) for i in ['aix', 'linux', 'darwin']) +RE_ANSI = re.compile(r"\x1b\[[;\d]*[A-Za-z]") + +try: + if IS_WIN: + import colorama + else: + raise ImportError +except ImportError: + colorama = None +else: + try: + colorama.init(strip=False) + except TypeError: + colorama.init() + + +def envwrap(prefix, types=None, is_method=False): + """ + Override parameter defaults via `os.environ[prefix + param_name]`. + Maps UPPER_CASE env vars map to lower_case param names. + camelCase isn't supported (because Windows ignores case). + + Precedence (highest first): + + - call (`foo(a=3)`) + - environ (`FOO_A=2`) + - signature (`def foo(a=1)`) + + Parameters + ---------- + prefix : str + Env var prefix, e.g. "FOO_" + types : dict, optional + Fallback mappings `{'param_name': type, ...}` if types cannot be + inferred from function signature. + Consider using `types=collections.defaultdict(lambda: ast.literal_eval)`. + is_method : bool, optional + Whether to use `functools.partialmethod`. If (default: False) use `functools.partial`. + + Examples + -------- + ``` + $ cat foo.py + from tqdm.utils import envwrap + @envwrap("FOO_") + def test(a=1, b=2, c=3): + print(f"received: a={a}, b={b}, c={c}") + + $ FOO_A=42 FOO_C=1337 python -c 'import foo; foo.test(c=99)' + received: a=42, b=2, c=99 + ``` + """ + if types is None: + types = {} + i = len(prefix) + env_overrides = {k[i:].lower(): v for k, v in os.environ.items() if k.startswith(prefix)} + part = partialmethod if is_method else partial + + def wrap(func): + params = signature(func).parameters + # ignore unknown env vars + overrides = {k: v for k, v in env_overrides.items() if k in params} + # infer overrides' `type`s + for k in overrides: + param = params[k] + if param.annotation is not param.empty: # typehints + for typ in getattr(param.annotation, '__args__', (param.annotation,)): + try: + overrides[k] = typ(overrides[k]) + except Exception: + pass + else: + break + elif param.default is not None: # type of default value + overrides[k] = type(param.default)(overrides[k]) + else: + try: # `types` fallback + overrides[k] = types[k](overrides[k]) + except KeyError: # keep unconverted (`str`) + pass + return part(func, **overrides) + return wrap + + +class FormatReplace(object): + """ + >>> a = FormatReplace('something') + >>> f"{a:5d}" + 'something' + """ # NOQA: P102 + def __init__(self, replace=''): + self.replace = replace + self.format_called = 0 + + def __format__(self, _): + self.format_called += 1 + return self.replace + + +class Comparable(object): + """Assumes child has self._comparable attr/@property""" + def __lt__(self, other): + return self._comparable < other._comparable + + def __le__(self, other): + return (self < other) or (self == other) + + def __eq__(self, other): + return self._comparable == other._comparable + + def __ne__(self, other): + return not self == other + + def __gt__(self, other): + return not self <= other + + def __ge__(self, other): + return not self < other + + +class ObjectWrapper(object): + def __getattr__(self, name): + return getattr(self._wrapped, name) + + def __setattr__(self, name, value): + return setattr(self._wrapped, name, value) + + def wrapper_getattr(self, name): + """Actual `self.getattr` rather than self._wrapped.getattr""" + try: + return object.__getattr__(self, name) + except AttributeError: # py2 + return getattr(self, name) + + def wrapper_setattr(self, name, value): + """Actual `self.setattr` rather than self._wrapped.setattr""" + return object.__setattr__(self, name, value) + + def __init__(self, wrapped): + """ + Thin wrapper around a given object + """ + self.wrapper_setattr('_wrapped', wrapped) + + +class SimpleTextIOWrapper(ObjectWrapper): + """ + Change only `.write()` of the wrapped object by encoding the passed + value and passing the result to the wrapped object's `.write()` method. + """ + # pylint: disable=too-few-public-methods + def __init__(self, wrapped, encoding): + super(SimpleTextIOWrapper, self).__init__(wrapped) + self.wrapper_setattr('encoding', encoding) + + def write(self, s): + """ + Encode `s` and pass to the wrapped object's `.write()` method. + """ + return self._wrapped.write(s.encode(self.wrapper_getattr('encoding'))) + + def __eq__(self, other): + return self._wrapped == getattr(other, '_wrapped', other) + + +class DisableOnWriteError(ObjectWrapper): + """ + Disable the given `tqdm_instance` upon `write()` or `flush()` errors. + """ + @staticmethod + def disable_on_exception(tqdm_instance, func): + """ + Quietly set `tqdm_instance.miniters=inf` if `func` raises `errno=5`. + """ + tqdm_instance = proxy(tqdm_instance) + + def inner(*args, **kwargs): + try: + return func(*args, **kwargs) + except OSError as e: + if e.errno != 5: + raise + try: + tqdm_instance.miniters = float('inf') + except ReferenceError: + pass + except ValueError as e: + if 'closed' not in str(e): + raise + try: + tqdm_instance.miniters = float('inf') + except ReferenceError: + pass + return inner + + def __init__(self, wrapped, tqdm_instance): + super(DisableOnWriteError, self).__init__(wrapped) + if hasattr(wrapped, 'write'): + self.wrapper_setattr( + 'write', self.disable_on_exception(tqdm_instance, wrapped.write)) + if hasattr(wrapped, 'flush'): + self.wrapper_setattr( + 'flush', self.disable_on_exception(tqdm_instance, wrapped.flush)) + + def __eq__(self, other): + return self._wrapped == getattr(other, '_wrapped', other) + + +class CallbackIOWrapper(ObjectWrapper): + def __init__(self, callback, stream, method="read"): + """ + Wrap a given `file`-like object's `read()` or `write()` to report + lengths to the given `callback` + """ + super(CallbackIOWrapper, self).__init__(stream) + func = getattr(stream, method) + if method == "write": + @wraps(func) + def write(data, *args, **kwargs): + res = func(data, *args, **kwargs) + callback(len(data)) + return res + self.wrapper_setattr('write', write) + elif method == "read": + @wraps(func) + def read(*args, **kwargs): + data = func(*args, **kwargs) + callback(len(data)) + return data + self.wrapper_setattr('read', read) + else: + raise KeyError("Can only wrap read/write methods") + + +def _is_utf(encoding): + try: + u'\u2588\u2589'.encode(encoding) + except UnicodeEncodeError: + return False + except Exception: + try: + return encoding.lower().startswith('utf-') or ('U8' == encoding) + except Exception: + return False + else: + return True + + +def _supports_unicode(fp): + try: + return _is_utf(fp.encoding) + except AttributeError: + return False + + +def _is_ascii(s): + if isinstance(s, str): + for c in s: + if ord(c) > 255: + return False + return True + return _supports_unicode(s) + + +def _screen_shape_wrapper(): # pragma: no cover + """ + Return a function which returns console dimensions (width, height). + Supported: linux, osx, windows, cygwin. + """ + _screen_shape = None + if IS_WIN: + _screen_shape = _screen_shape_windows + if _screen_shape is None: + _screen_shape = _screen_shape_tput + if IS_NIX: + _screen_shape = _screen_shape_linux + return _screen_shape + + +def _screen_shape_windows(fp): # pragma: no cover + try: + import struct + from ctypes import create_string_buffer, windll + from sys import stdin, stdout + + io_handle = -12 # assume stderr + if fp == stdin: + io_handle = -10 + elif fp == stdout: + io_handle = -11 + + h = windll.kernel32.GetStdHandle(io_handle) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + if res: + (_bufx, _bufy, _curx, _cury, _wattr, left, top, right, bottom, + _maxx, _maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + return right - left, bottom - top # +1 + except Exception: # nosec + pass + return None, None + + +def _screen_shape_tput(*_): # pragma: no cover + """cygwin xterm (windows)""" + try: + import shlex + from subprocess import check_call # nosec + return [int(check_call(shlex.split('tput ' + i))) - 1 + for i in ('cols', 'lines')] + except Exception: # nosec + pass + return None, None + + +def _screen_shape_linux(fp): # pragma: no cover + + try: + from array import array + from fcntl import ioctl + from termios import TIOCGWINSZ + except ImportError: + return None, None + else: + try: + rows, cols = array('h', ioctl(fp, TIOCGWINSZ, '\0' * 8))[:2] + return cols, rows + except Exception: + try: + return [int(os.environ[i]) - 1 for i in ("COLUMNS", "LINES")] + except (KeyError, ValueError): + return None, None + + +def _environ_cols_wrapper(): # pragma: no cover + """ + Return a function which returns console width. + Supported: linux, osx, windows, cygwin. + """ + warn("Use `_screen_shape_wrapper()(file)[0]` instead of" + " `_environ_cols_wrapper()(file)`", DeprecationWarning, stacklevel=2) + shape = _screen_shape_wrapper() + if not shape: + return None + + @wraps(shape) + def inner(fp): + return shape(fp)[0] + + return inner + + +def _term_move_up(): # pragma: no cover + return '' if (os.name == 'nt') and (colorama is None) else '\x1b[A' + + +def _text_width(s): + return sum(2 if east_asian_width(ch) in 'FW' else 1 for ch in str(s)) + + +def disp_len(data): + """ + Returns the real on-screen length of a string which may contain + ANSI control codes and wide chars. + """ + return _text_width(RE_ANSI.sub('', data)) + + +def disp_trim(data, length): + """ + Trim a string which may contain ANSI control characters. + """ + if len(data) == disp_len(data): + return data[:length] + + ansi_present = bool(RE_ANSI.search(data)) + while disp_len(data) > length: # carefully delete one char at a time + data = data[:-1] + if ansi_present and bool(RE_ANSI.search(data)): + # assume ANSI reset is required + return data if data.endswith("\033[0m") else data + "\033[0m" + return data diff --git a/.pythonlibs/lib/python3.10/site-packages/tqdm/version.py b/.pythonlibs/lib/python3.10/site-packages/tqdm/version.py new file mode 100644 index 0000000..11cbaea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/tqdm/version.py @@ -0,0 +1,9 @@ +"""`tqdm` version detector. Precedence: installed dist, git, 'UNKNOWN'.""" +try: + from ._dist_ver import __version__ +except ImportError: + try: + from setuptools_scm import get_version + __version__ = get_version(root='..', relative_to=__file__) + except (ImportError, LookupError): + __version__ = "UNKNOWN" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/AUTHORS.md b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/AUTHORS.md new file mode 100644 index 0000000..58eabd0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/AUTHORS.md @@ -0,0 +1,41 @@ +# Authors + +We'd like to thank the following people who have contributed to the +`twilio-python` repository. + +- Adam Ballai +- Alex Brinsmead +- Alex Chan +- Andrew Benton +- Chad Selph +- Comrade DOS +- Dan Yang +- Dennis Pilarinos +- Doug Black +- Evan Fossier +- Fabian Topfstedt +- Florian Le Goff +- Frank Tobia +- Frederik De Bleser +- Guillaume BINET +- Hunter Blanks +- Joël Franusic +- Justin Van Koten +- Kenneth Reitz +- Kevin Burke +- Kyle Conroy +- Michael Parker +- Moses Palmér +- Ryan Horn +- Sam Kimbrel +- Skylar Saveland +- Tiberiu Ana +- Zachary Voase +- aes +- dnathe4th +- isbo +- negeorge +- Evan Cooke +- tysonholub +- Brodan +- Kyle Jones \ No newline at end of file diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/LICENSE new file mode 100644 index 0000000..6485c1f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2023, Twilio, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/METADATA new file mode 100644 index 0000000..b9d8286 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/METADATA @@ -0,0 +1,325 @@ +Metadata-Version: 2.1 +Name: twilio +Version: 9.0.5 +Summary: Twilio API client and TwiML generator +Home-page: https://github.com/twilio/twilio-python/ +Author: Twilio +Keywords: twilio,twiml +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Communications :: Telephony +Requires-Python: >=3.7.0 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS.md +Requires-Dist: requests >=2.0.0 +Requires-Dist: PyJWT <3.0.0,>=2.0.0 +Requires-Dist: aiohttp >=3.8.4 +Requires-Dist: aiohttp-retry >=2.8.3 + +# twilio-python + +[![Tests](https://github.com/twilio/twilio-python/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/twilio/twilio-python/actions/workflows/test-and-deploy.yml) +[![PyPI](https://img.shields.io/pypi/v/twilio.svg)](https://pypi.python.org/pypi/twilio) +[![PyPI](https://img.shields.io/pypi/pyversions/twilio.svg)](https://pypi.python.org/pypi/twilio) +[![Learn OSS Contribution in TwilioQuest](https://img.shields.io/static/v1?label=TwilioQuest&message=Learn%20to%20contribute%21&color=F22F46&labelColor=1f243c&style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAASFBMVEUAAAAZGRkcHBwjIyMoKCgAAABgYGBoaGiAgICMjIyzs7PJycnMzMzNzc3UoBfd3d3m5ubqrhfrMEDu7u739/f4vSb/3AD///9tbdyEAAAABXRSTlMAAAAAAMJrBrEAAAKoSURBVHgB7ZrRcuI6EESdyxXGYoNFvMD//+l2bSszRgyUYpFAsXOeiJGmj4NkuWx1Qeh+Ekl9DgEXOBwOx+Px5xyQhDykfgq4wG63MxxaR4ddIkg6Ul3g84vCIcjPBA5gmUMeXESrlukuoK33+33uID8TWeLAdOWsKpJYzwVMB7bOzYSGOciyUlXSn0/ABXTosJ1M1SbypZ4O4MbZuIDMU02PMbauhhHMHXbmebmALIiEbbbbbUrpF1gwE9kFfRNAJaP+FQEXCCTGyJ4ngDrjOFo3jEL5JdqjF/pueR4cCeCGgAtwmuRS6gDwaRiGvu+DMFwSBLTE3+jF8JyuV1okPZ+AC4hDFhCHyHQjdjPHUKFDlHSJkHQXMB3KpSwXNGJPcwwTdZiXlRN0gSp0zpWxNtM0beYE0nRH6QIbO7rawwXaBYz0j78gxjokDuv12gVeUuBD0MDi0OQCLvDaAho4juP1Q/jkAncXqIcCfd+7gAu4QLMACCLxpRsSuQh0igu0C9Svhi7weAGZg50L3IE3cai4IfkNZAC8dfdhsUD3CgKBVC9JE5ABAFzg4QL/taYPAAWrHdYcgfLaIgAXWJ7OV38n1LEF8tt2TH29E+QAoDoO5Ve/LtCQDmKM9kPbvCEBApK+IXzbcSJ0cIGF6e8gpcRhUDogWZ8JnaWjPXc/fNnBBUKRngiHgTUSivSzDRDgHZQOLvBQgf8rRt+VdBUUhwkU6VpJ+xcOwQUqZr+mR0kvBUgv6cB4+37hQAkXqE8PwGisGhJtN4xAHMzrsgvI7rccXqSvKh6jltGlrOHA3Xk1At3LC4QiPdX9/0ndHpGVvTjR4bZA1ypAKgVcwE5vx74ulwIugDt8e/X7JgfkucBMIAr26ndnB4UCLnDOqvteQsHlgX9N4A+c4cW3DXSPbwAAAABJRU5ErkJggg==)](https://twil.io/learn-open-source) + +## Documentation + +The documentation for the Twilio API can be found [here][apidocs]. + +The Python library documentation can be found [here][libdocs]. + +## Versions + +`twilio-python` uses a modified version of [Semantic Versioning](https://semver.org) for all changes. [See this document](VERSIONS.md) for details. + +### Supported Python Versions + +This library supports the following Python implementations: + +- Python 3.7 +- Python 3.8 +- Python 3.9 +- Python 3.10 +- Python 3.11 + +## Installation + +Install from PyPi using [pip](https://pip.pypa.io/en/latest/), a +package manager for Python. + +```shell +pip3 install twilio +``` + +If pip install fails on Windows, check the path length of the directory. If it is greater 260 characters then enable [Long Paths](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) or choose other shorter location. + +Don't have pip installed? Try installing it, by running this from the command +line: + +```shell +curl https://bootstrap.pypa.io/get-pip.py | python +``` + +Or, you can [download the source code +(ZIP)](https://github.com/twilio/twilio-python/zipball/main 'twilio-python +source code') for `twilio-python`, and then run: + +```shell +python3 setup.py install +``` + +> **Info** +> If the command line gives you an error message that says Permission Denied, try running the above commands with `sudo` (e.g., `sudo pip3 install twilio`). + +### Test your installation + +Try sending yourself an SMS message. Save the following code sample to your computer with a text editor. Be sure to update the `account_sid`, `auth_token`, and `from_` phone number with values from your [Twilio account](https://console.twilio.com). The `to` phone number will be your own mobile phone. + +```python +from twilio.rest import Client + +# Your Account SID and Auth Token from console.twilio.com +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" + +client = Client(account_sid, auth_token) + +message = client.messages.create( + to="+15558675309", + from_="+15017250604", + body="Hello from Python!") + +print(message.sid) +``` + +Save the file as `send_sms.py`. In the terminal, `cd` to the directory containing the file you just saved then run: + +```shell +python3 send_sms.py +``` + +After a brief delay, you will receive the text message on your phone. + +> **Warning** +> It's okay to hardcode your credentials when testing locally, but you should use environment variables to keep them secret before committing any code or deploying to production. Check out [How to Set Environment Variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) for more information. + +## Use the helper library + +### API Credentials + +The `Twilio` client needs your Twilio credentials. You can either pass these directly to the constructor (see the code below) or via environment variables. + +Authenticating with Account SID and Auth Token: + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) +``` + +Authenticating with API Key and API Secret: + +```python +from twilio.rest import Client + +api_key = "XXXXXXXXXXXXXXXXX" +api_secret = "YYYYYYYYYYYYYYYYYY" +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +client = Client(api_key, api_secret, account_sid) +``` + +Alternatively, a `Client` constructor without these parameters will +look for `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` variables inside the +current environment. + +We suggest storing your credentials as environment variables. Why? You'll never +have to worry about committing your credentials and accidentally posting them +somewhere public. + +```python +from twilio.rest import Client +client = Client() +``` + +### Specify Region and/or Edge + +To take advantage of Twilio's [Global Infrastructure](https://www.twilio.com/docs/global-infrastructure), specify the target Region and/or Edge for the client: + +```python +from twilio.rest import Client + +client = Client(region='au1', edge='sydney') +``` + +A `Client` constructor without these parameters will also look for `TWILIO_REGION` and `TWILIO_EDGE` variables inside the current environment. + +Alternatively, you may specify the edge and/or region after constructing the Twilio client: + +```python +from twilio.rest import Client + +client = Client() +client.region = 'au1' +client.edge = 'sydney' +``` + +This will result in the `hostname` transforming from `api.twilio.com` to `api.sydney.au1.twilio.com`. + +### Make a Call + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +call = client.calls.create(to="9991231234", + from_="9991231234", + url="http://twimlets.com/holdmusic?Bucket=com.twilio.music.ambient") +print(call.sid) +``` + +### Get data about an existing call + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +call = client.calls.get("CA42ed11f93dc08b952027ffbc406d0868") +print(call.to) +``` + +### Iterate through records + +The library automatically handles paging for you. Collections, such as `calls` and `messages`, have `list` and `stream` methods that page under the hood. With both `list` and `stream`, you can specify the number of records you want to receive (`limit`) and the maximum size you want each page fetch to be (`page_size`). The library will then handle the task for you. + +`list` eagerly fetches all records and returns them as a list, whereas `stream` returns an iterator and lazily retrieves pages of records as you iterate over the collection. You can also page manually using the `page` method. + +#### Use the `list` method + +```python +from twilio.rest import Client + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +for sms in client.messages.list(): + print(sms.to) +``` + +### Asynchronous API Requests + +By default, the Twilio Client will make synchronous requests to the Twilio API. To allow for asynchronous, non-blocking requests, we've included an optional asynchronous HTTP client. When used with the Client and the accompanying `*_async` methods, requests made to the Twilio API will be performed asynchronously. + +```python +from twilio.http.async_http_client import AsyncTwilioHttpClient +from twilio.rest import Client + +async def main(): + account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + auth_token = "your_auth_token" + http_client = AsyncTwilioHttpClient() + client = Client(account_sid, auth_token, http_client=http_client) + + message = await client.messages.create_async(to="+12316851234", from_="+15555555555", + body="Hello there!") + +asyncio.run(main()) +``` + +### Enable Debug Logging + +Log the API request and response data to the console: + +```python +import logging + +client = Client(account_sid, auth_token) +logging.basicConfig() +client.http_client.logger.setLevel(logging.INFO) +``` + +Log the API request and response data to a file: + +```python +import logging + +client = Client(account_sid, auth_token) +logging.basicConfig(filename='./log.txt') +client.http_client.logger.setLevel(logging.INFO) +``` + +### Handling Exceptions + +Version 8.x of `twilio-python` exports an exception class to help you handle exceptions that are specific to Twilio methods. To use it, import `TwilioRestException` and catch exceptions as follows: + +```python +from twilio.rest import Client +from twilio.base.exceptions import TwilioRestException + +account_sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +auth_token = "your_auth_token" +client = Client(account_sid, auth_token) + +try: + message = client.messages.create(to="+12316851234", from_="+15555555555", + body="Hello there!") +except TwilioRestException as e: + print(e) +``` + +### Generating TwiML + +To control phone calls, your application needs to output [TwiML][twiml]. + +Use `twilio.twiml.Response` to easily create such responses. + +```python +from twilio.twiml.voice_response import VoiceResponse + +r = VoiceResponse() +r.say("Welcome to twilio!") +print(str(r)) +``` + +```xml + +Welcome to twilio! +``` + +### Other advanced examples + +- [Learn how to create your own custom HTTP client](./advanced-examples/custom-http-client.md) + +### Docker Image + +The `Dockerfile` present in this repository and its respective `twilio/twilio-python` Docker image are currently used by Twilio for testing purposes only. + +### Getting help + +If you need help installing or using the library, please check the [Twilio Support Help Center](https://support.twilio.com) first, and [file a support ticket](https://twilio.com/help/contact) if you don't find an answer to your question. + +If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo! + +[apidocs]: https://www.twilio.com/docs/api +[twiml]: https://www.twilio.com/docs/api/twiml +[libdocs]: https://twilio.github.io/twilio-python diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/RECORD new file mode 100644 index 0000000..aeb1d58 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/RECORD @@ -0,0 +1,1258 @@ +twilio-9.0.5.dist-info/AUTHORS.md,sha256=o3292UoaDN3jrjNs-qzN7yDnhvH6TqV7EzSfJdnCf4Q,1387 +twilio-9.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +twilio-9.0.5.dist-info/LICENSE,sha256=8UK19n5LbVBeSP2SuveHtbmQ2BMV-Dat89xZ8As-YZ0,1108 +twilio-9.0.5.dist-info/METADATA,sha256=QpXZb6at1YLBgL4o0YE9iCT19uzflCM9kx_0HfCq9Jg,12044 +twilio-9.0.5.dist-info/RECORD,, +twilio-9.0.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +twilio-9.0.5.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110 +twilio-9.0.5.dist-info/top_level.txt,sha256=fAdnbTJw2Q-bY1dyTpCPQR9_kIISEW1tNn4jYb-g3Ic,7 +twilio/__init__.py,sha256=-tgZeemAHL6veJdHI9SpVAqC2OGX3oS6R92LS2Dn94s,76 +twilio/__pycache__/__init__.cpython-310.pyc,, +twilio/__pycache__/request_validator.cpython-310.pyc,, +twilio/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +twilio/base/__pycache__/__init__.cpython-310.pyc,, +twilio/base/__pycache__/client_base.cpython-310.pyc,, +twilio/base/__pycache__/deserialize.cpython-310.pyc,, +twilio/base/__pycache__/domain.cpython-310.pyc,, +twilio/base/__pycache__/exceptions.cpython-310.pyc,, +twilio/base/__pycache__/instance_context.cpython-310.pyc,, +twilio/base/__pycache__/instance_resource.cpython-310.pyc,, +twilio/base/__pycache__/list_resource.cpython-310.pyc,, +twilio/base/__pycache__/obsolete.cpython-310.pyc,, +twilio/base/__pycache__/page.cpython-310.pyc,, +twilio/base/__pycache__/serialize.cpython-310.pyc,, +twilio/base/__pycache__/values.cpython-310.pyc,, +twilio/base/__pycache__/version.cpython-310.pyc,, +twilio/base/client_base.py,sha256=UCiMRbxIJ6V6pjdVn8ISjtOMOQ8kiKWVws1Tujabtw0,8153 +twilio/base/deserialize.py,sha256=ro6hPYvNK1c7WrpWeS3PgM_p0ppjPwJxM2iYVhgCvDk,2005 +twilio/base/domain.py,sha256=z0ESHLf2LEQxfi1JJ2xEQk8tQheW0mU1Bt_r3ko41U4,2978 +twilio/base/exceptions.py,sha256=qYou_kqs04_0ybcVzcfpRqN88RYf41iINB4B8egok7Q,2890 +twilio/base/instance_context.py,sha256=ln0XUHSb6ByjV6oHx0sa7TknQ0ahe8yuk-mhmOM5fMU,147 +twilio/base/instance_resource.py,sha256=84FA89HN55_rSWUQ2rwRFGaABXRYoudcLaYvBQDbQmo,148 +twilio/base/list_resource.py,sha256=WLDg_ByCMR9P7YOHLuQxNjkZvToteF7A3k-ZCb853Xc,144 +twilio/base/obsolete.py,sha256=A5EqW8Nu4aLatse83CIxdrZXxXic8yhaXiQC16RyvVg,1417 +twilio/base/page.py,sha256=1Lmhs3an6YswMcqXoWOCiQweXL6l36b8wyHgxbnDZxU,5181 +twilio/base/serialize.py,sha256=TlIJtVfXArDLLmH8WyfGZmczffxWZNuUtOckd4d9aJ0,2316 +twilio/base/values.py,sha256=okdxSj7zc0tg_IfScuZYQKOX-fHu99ZXQcFh_XuBAuE,280 +twilio/base/version.py,sha256=rLeF8uFxzEbSBw9DfRjHEUDu_CPeRtM2rGaciUKLRSI,14614 +twilio/http/__init__.py,sha256=aklR5-Xl8fnZNgV3jxylkbCcJCBEKQM3nt024ttm-Gs,3434 +twilio/http/__pycache__/__init__.cpython-310.pyc,, +twilio/http/__pycache__/async_http_client.cpython-310.pyc,, +twilio/http/__pycache__/http_client.cpython-310.pyc,, +twilio/http/__pycache__/request.cpython-310.pyc,, +twilio/http/__pycache__/response.cpython-310.pyc,, +twilio/http/__pycache__/validation_client.cpython-310.pyc,, +twilio/http/async_http_client.py,sha256=Ei7OXkbp26n3MAtWsX-0mTVZcEOgBui4Y4y8T20l1x0,4503 +twilio/http/http_client.py,sha256=XNSATlIh4LUO-YLh6Vshr8e_JKOIWsLFLqbJ3EeaMbk,3851 +twilio/http/request.py,sha256=kVHsIN0kJQXRySE2VRVQwCPmp9ZycMxJ9zG7FnvArhM,2655 +twilio/http/response.py,sha256=BmDMxrJFDhgAxXQzoLwzO5pZSuw-y9KjEA-i3lz3yY4,518 +twilio/http/validation_client.py,sha256=ndVZrpcpG9i7db10mbrCjmU3ks8rqYu4EDWJeWnKFUM,4818 +twilio/jwt/__init__.py,sha256=MScUNrJ0oRY16IYehs1wYQVGlV5QKxLY0msD9xmjRpo,5085 +twilio/jwt/__pycache__/__init__.cpython-310.pyc,, +twilio/jwt/access_token/__init__.py,sha256=RJOJBVlO4xm3hgiltdFNKOgInJVId3G8CYVkhx6vP54,2415 +twilio/jwt/access_token/__pycache__/__init__.cpython-310.pyc,, +twilio/jwt/access_token/__pycache__/grants.cpython-310.pyc,, +twilio/jwt/access_token/grants.py,sha256=CVuyFTkyRC9nCc8Z70KpBpyYPygYqku08l8hoXO6BOc,4902 +twilio/jwt/client/__init__.py,sha256=GCWbuOKucutdnMIMtTesx0qvjEs25ieB_0upacFT604,3982 +twilio/jwt/client/__pycache__/__init__.cpython-310.pyc,, +twilio/jwt/taskrouter/__init__.py,sha256=2LIrC_6D4qvW278bKvqKM-VDBYLhwXxzwKfuri6LQ9I,5726 +twilio/jwt/taskrouter/__pycache__/__init__.cpython-310.pyc,, +twilio/jwt/taskrouter/__pycache__/capabilities.cpython-310.pyc,, +twilio/jwt/taskrouter/capabilities.py,sha256=TGE-YHo60LL-ubtu4JJitO0sviweqNPQjsRzBjWBzxo,4309 +twilio/jwt/validation/__init__.py,sha256=NSTRTHrEp5aoI-jbcc7KrKCRYIPMK5SITRMKj4y_sKQ,3156 +twilio/jwt/validation/__pycache__/__init__.cpython-310.pyc,, +twilio/request_validator.py,sha256=ZeoQ7BbnPrDGTy-M_RzcLq0l8Hir64Il5VbiqyOJNdc,4100 +twilio/rest/__init__.py,sha256=0ZhtpOQ1yn75DDMIfahnUwPY9AG0hTLe57i1rkI3B-I,21083 +twilio/rest/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/accounts/AccountsBase.py,sha256=AqLmd6aIbV985gG5b473bkD2DqejaY4S3lvZaiNwcwg,1211 +twilio/rest/accounts/__init__.py,sha256=Oj-WM5ZDQWaF-2jstKhWRRPpkuCLs5AlDBpQlGDfZ28,1168 +twilio/rest/accounts/__pycache__/AccountsBase.cpython-310.pyc,, +twilio/rest/accounts/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/accounts/v1/__init__.py,sha256=rip8lWcPYeog8rhbjiJYS4vOnLdUFASQ9qEx4ol4mNk,2383 +twilio/rest/accounts/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/accounts/v1/__pycache__/auth_token_promotion.cpython-310.pyc,, +twilio/rest/accounts/v1/__pycache__/safelist.cpython-310.pyc,, +twilio/rest/accounts/v1/__pycache__/secondary_auth_token.cpython-310.pyc,, +twilio/rest/accounts/v1/auth_token_promotion.py,sha256=r9bclKl-HJcC96qsqC8mtvxWcXIBItEw8h6EvAdCXuU,5547 +twilio/rest/accounts/v1/credential/__init__.py,sha256=NsXgoL4scQZKaq3Q1qIpWSGGbjPWF2KzOHfL6iwkLPo,1766 +twilio/rest/accounts/v1/credential/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/accounts/v1/credential/__pycache__/aws.cpython-310.pyc,, +twilio/rest/accounts/v1/credential/__pycache__/public_key.cpython-310.pyc,, +twilio/rest/accounts/v1/credential/aws.py,sha256=ZmDc2Ik-jTK4ZzMaERlIUs-yYhmvoRZSYhTGUzIaI-8,19492 +twilio/rest/accounts/v1/credential/public_key.py,sha256=JAMWYhf0snkukETBpy4zZsXauxZUdi-j2M_Uf1rWJyY,20010 +twilio/rest/accounts/v1/safelist.py,sha256=EPia2I5IOEkWC1zURWVMBf8my5iDlgwMSycB3DXaQwU,5664 +twilio/rest/accounts/v1/secondary_auth_token.py,sha256=39oj0AWcZPFSOouzcDB9Ii5X55tc-sO1WjsYIzkk1T8,6551 +twilio/rest/api/ApiBase.py,sha256=1118YkG0wU9g7mMByeOtL4HfpUGvePUq4fK3_rpnsKU,1209 +twilio/rest/api/__init__.py,sha256=IF3HTJJA-JYCkkGZ_Mr6Js5AE3DVR7kVVfWZIbabN5Q,8255 +twilio/rest/api/__pycache__/ApiBase.cpython-310.pyc,, +twilio/rest/api/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/__init__.py,sha256=G3bZ5wo8gF9DrYNnYOVztO-T2-vPg_mBvHNXBVAaiSM,1806 +twilio/rest/api/v2010/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/__init__.py,sha256=A_EJVOtYwxrhzwMmRkZxDX_18Dqk-P4tzCTBweEL3Bg,36211 +twilio/rest/api/v2010/account/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/application.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/authorized_connect_app.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/balance.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/connect_app.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/key.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/new_key.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/new_signing_key.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/notification.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/outgoing_caller_id.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/short_code.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/signing_key.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/token.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/transcription.cpython-310.pyc,, +twilio/rest/api/v2010/account/__pycache__/validation_request.cpython-310.pyc,, +twilio/rest/api/v2010/account/address/__init__.py,sha256=b5oq96MHZYc4YwmW6-qBwb6OgCPre_3AUTvtiYRlv3M,36104 +twilio/rest/api/v2010/account/address/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/address/__pycache__/dependent_phone_number.cpython-310.pyc,, +twilio/rest/api/v2010/account/address/dependent_phone_number.py,sha256=gS7v_SOnej8eU_g5W4rWe69kUxssfA5w1TbaTXVbJTQ,17046 +twilio/rest/api/v2010/account/application.py,sha256=YQ_msHa6GraQ14TOta4FyLHcECfhVY0oT6jyrRRnH-8,48103 +twilio/rest/api/v2010/account/authorized_connect_app.py,sha256=nPAQYb8X0PKcMnAjrUNltvEJzr06VEMDgS668OsVHkE,16677 +twilio/rest/api/v2010/account/available_phone_number_country/__init__.py,sha256=Kffje6-TzTP8OBqs-FHeEAHF7TZEo8LmiG-4fJbuoVk,21428 +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/local.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/machine_to_machine.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/mobile.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/national.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/shared_cost.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/toll_free.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/voip.cpython-310.pyc,, +twilio/rest/api/v2010/account/available_phone_number_country/local.py,sha256=m_Ay-X7aX8iidyR-BE31c4bLEPi3Kle4pJUZLMibvJ8,46942 +twilio/rest/api/v2010/account/available_phone_number_country/machine_to_machine.py,sha256=lKbsF-REiwbLJT2qgwlmTrzYQSCQAtxfDR3QjgAVPr4,46913 +twilio/rest/api/v2010/account/available_phone_number_country/mobile.py,sha256=yP9DXXq_vDyUsx7rZ2Bx5ZLED2AxLUgWxDwRzb0meUk,46563 +twilio/rest/api/v2010/account/available_phone_number_country/national.py,sha256=yLLnxyqG1azDXksIK_r28VzI4ruIOkftOlA2uzPgrD8,46633 +twilio/rest/api/v2010/account/available_phone_number_country/shared_cost.py,sha256=CC4FhENqKAchXTu2bfFEYMC0cxZrImb_Pa3y4dAdW1E,46703 +twilio/rest/api/v2010/account/available_phone_number_country/toll_free.py,sha256=ztnsHGVr_-LMF1r1_BgXJYOd2l_E7yp40fJ6sFhzpus,46633 +twilio/rest/api/v2010/account/available_phone_number_country/voip.py,sha256=xJi1DNl1n15AdUVH6vY4iw70e6nWSCqMqUU-axr2RoI,46493 +twilio/rest/api/v2010/account/balance.py,sha256=HCvo10P--l1-Cq-k8gKRXD-eUm0xKq9nTP4eEJAW44c,3189 +twilio/rest/api/v2010/account/call/__init__.py,sha256=lZFrT7fRbDf2z0KyCAeWdeQhuDXXDVE8qX5G0pPuT7w,91321 +twilio/rest/api/v2010/account/call/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/event.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/notification.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/payment.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/recording.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/siprec.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/stream.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/user_defined_message.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/__pycache__/user_defined_message_subscription.cpython-310.pyc,, +twilio/rest/api/v2010/account/call/event.py,sha256=zjjX2-ohWF1FgHM7V9fbBjbauxIPWGK-lhaaLootKLE,10658 +twilio/rest/api/v2010/account/call/notification.py,sha256=FPOW_sp4iIHQk3_LYZcbEdTseznXdiUSR-7ToD_wrMg,28364 +twilio/rest/api/v2010/account/call/payment.py,sha256=stfyVkiIy4z82WmEvOnjb3GXShqAUo1jTtLE_4LtR9U,23634 +twilio/rest/api/v2010/account/call/recording.py,sha256=4O7LZKgcmoxMKsz2m2F5fegux7b3nt6-h9ajux3lGaM,38500 +twilio/rest/api/v2010/account/call/siprec.py,sha256=4zrUZOBANhouutqt2Wyt_xegVwt996hzSQlG8tY7wqA,77230 +twilio/rest/api/v2010/account/call/stream.py,sha256=VsUEkmILhjcdjjwxKE_oDCj7vH0olq7XRb0Wzhjf08U,77253 +twilio/rest/api/v2010/account/call/user_defined_message.py,sha256=3eBuRK-nDJLOna8usad2JKIoc-zahgZLztTUdxAe86o,5279 +twilio/rest/api/v2010/account/call/user_defined_message_subscription.py,sha256=xWgyOu2kw3wZ9OxJQQPtIUHWhe990j0gCiLGbLAsIug,10900 +twilio/rest/api/v2010/account/conference/__init__.py,sha256=tEsCR_ckGjBIPowBWTRur6iwUf60p0HeQKeA1i6mFQY,47201 +twilio/rest/api/v2010/account/conference/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/conference/__pycache__/participant.cpython-310.pyc,, +twilio/rest/api/v2010/account/conference/__pycache__/recording.cpython-310.pyc,, +twilio/rest/api/v2010/account/conference/participant.py,sha256=n-yhQWIyfR0yIDQSLbghhnzVeW3r3wDbTiPVpG4WJcQ,77270 +twilio/rest/api/v2010/account/conference/recording.py,sha256=nZTwd73aFHfyLNBZlMcTTD5c5iIZ54CBbgPQdURO-4U,32732 +twilio/rest/api/v2010/account/connect_app.py,sha256=IU1wV8xiJGxJvkgntjKgayFVhAsNJvjp2Yu7bJajM6g,26542 +twilio/rest/api/v2010/account/incoming_phone_number/__init__.py,sha256=6k9JxVD3dfLHzn7cJXODzmUaqU62cylOGcAS6NegjbI,73716 +twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/local.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/mobile.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/toll_free.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__init__.py,sha256=LQ5KbS953m-h-N01zFGzj3wS9l2-BB2JFAyRdnEcRl4,20753 +twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/assigned_add_on_extension.cpython-310.pyc,, +twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/assigned_add_on_extension.py,sha256=gP4SivH3yFQQ1MGAymC-D-wQDCD1TlMvJAuiBbxGP7w,18342 +twilio/rest/api/v2010/account/incoming_phone_number/local.py,sha256=RqRn10msAgtBuniWKy721IuF0iuK7Y2douYTKRYonEY,37146 +twilio/rest/api/v2010/account/incoming_phone_number/mobile.py,sha256=-xAqlJez2ZGbyseD5LL3CV9LErqwE7ad_AEHikaJH2A,37230 +twilio/rest/api/v2010/account/incoming_phone_number/toll_free.py,sha256=6rjH4reRn1ddfxsmP3IghGsj4fiTBWgzDNsdRYtKwFg,37316 +twilio/rest/api/v2010/account/key.py,sha256=sfYOgFk8tAE8zSQA6ItYehw1WFKCZNHmMPy6dnJeWQY,17990 +twilio/rest/api/v2010/account/message/__init__.py,sha256=FHLhV5c3rE27HpWwApXuyG4-b7KRD-hEsFcmJabGBZo,56916 +twilio/rest/api/v2010/account/message/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/message/__pycache__/feedback.cpython-310.pyc,, +twilio/rest/api/v2010/account/message/__pycache__/media.cpython-310.pyc,, +twilio/rest/api/v2010/account/message/feedback.py,sha256=D69B5awG14SqbWSZaX-BY5erFg2uMUtXEAnAbqKD9Tw,5198 +twilio/rest/api/v2010/account/message/media.py,sha256=FqTBziAUlWK1qDRfueU3TUFWnwmO5mo1YKiHTPsnJc0,27544 +twilio/rest/api/v2010/account/new_key.py,sha256=yuj9gpBwFCLhlD-ive0nDreIyj-rKCAKletX175re44,4786 +twilio/rest/api/v2010/account/new_signing_key.py,sha256=Wam_6ixQ2DE_Ngoi4EEU_aOhgsseaws9-P9dIZo1oks,4814 +twilio/rest/api/v2010/account/notification.py,sha256=e2Gp-cBbVp6ssGU-NKqK5kVXtvwuxNG_oj2jNaWky7Y,27506 +twilio/rest/api/v2010/account/outgoing_caller_id.py,sha256=drmx3k3-Cz9cwKeZf2Y138r7ZllqQqw7LarktyhRLhU,22200 +twilio/rest/api/v2010/account/queue/__init__.py,sha256=vSpABzXC5iYqqII6DCDaA5jwOWwQVr2hHsiOgcHNzi0,22431 +twilio/rest/api/v2010/account/queue/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/queue/__pycache__/member.cpython-310.pyc,, +twilio/rest/api/v2010/account/queue/member.py,sha256=XDs3sJBgI6-zY7iZGk4G21VPBPgBnLEwRVDo4AWRUtU,19132 +twilio/rest/api/v2010/account/recording/__init__.py,sha256=BM7Pu7wEYYH7A6EhmKFxmrC51_4UNFKpLV0H2ZfCGVY,37888 +twilio/rest/api/v2010/account/recording/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/recording/__pycache__/transcription.cpython-310.pyc,, +twilio/rest/api/v2010/account/recording/add_on_result/__init__.py,sha256=CuAkbW25Ag7cB-nXBLpX5-EdqarkcpIR-XbFYzVgL1g,19242 +twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/payload.cpython-310.pyc,, +twilio/rest/api/v2010/account/recording/add_on_result/payload.py,sha256=gp_US5ZbE06awl_gySRJNJogRL6XRARDPcmb_R-ogOU,19234 +twilio/rest/api/v2010/account/recording/transcription.py,sha256=7_PIVP2Sij07ccTOj_oE6qRjDOKC-PFrS2mZEOZbRn4,19011 +twilio/rest/api/v2010/account/short_code.py,sha256=YuxRyWhGlNijXKl-HLEmvJtdtFRsf2WhpxnmUVfuqkg,26407 +twilio/rest/api/v2010/account/signing_key.py,sha256=UQB0p7ptT-YQP7pIFukVxKPqLxxESS3H_HeOR_uTLFs,17377 +twilio/rest/api/v2010/account/sip/__init__.py,sha256=UNiLJ1eMTojjpoUrhgnV4Pux6F4udK3U7eVOMOHp9ws,2871 +twilio/rest/api/v2010/account/sip/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/credential_list/__init__.py,sha256=CNANdVGs20UF4QNa_8znwJJ88NhGT7xhs0X8VrK9FSY,21281 +twilio/rest/api/v2010/account/sip/credential_list/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/credential_list/__pycache__/credential.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/credential_list/credential.py,sha256=CKl57ntFl4rRFIsM_TozEofmNid3zQqROTc7cMi8jq8,22760 +twilio/rest/api/v2010/account/sip/domain/__init__.py,sha256=rnwu9Lbi_65gc2TTVjBaxnvR8FqCPska4akgqRFaxhM,45630 +twilio/rest/api/v2010/account/sip/domain/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/__pycache__/credential_list_mapping.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/__pycache__/ip_access_control_list_mapping.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/__init__.py,sha256=9e9q4NNr8KnUwHdYNbh2fpuaAVUuZ2OeQCuRQc-gsco,2758 +twilio/rest/api/v2010/account/sip/domain/auth_types/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__init__.py,sha256=nolfbRsWWIcMstPOS1VXOH9gDrNceSCUQYfcM4OSIXo,3332 +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_credential_list_mapping.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_ip_access_control_list_mapping.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_credential_list_mapping.py,sha256=VPd4QQUYyZDUOTjRa0WF_qwjYVJTKUrgylqvna2YyKc,20443 +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_ip_access_control_list_mapping.py,sha256=sCpGsH3ALRkDqYZvL7RmcAqP8HN5kqgASFQMU9h1cj4,20984 +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__init__.py,sha256=L0TAMoyPSClaHsol6TCehYLaBbNTf9EXMHSM2ipyDKA,2473 +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/auth_registrations_credential_list_mapping.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/auth_registrations_credential_list_mapping.py,sha256=C5QaJZcTQyVpaMTrrb4OpqLqkNrVBqSRPGnidG-z3fE,21051 +twilio/rest/api/v2010/account/sip/domain/credential_list_mapping.py,sha256=I46x2Z_Ku3sl2tf8Yr12MVVIrNH2ODxzsyFCrxDraQA,19896 +twilio/rest/api/v2010/account/sip/domain/ip_access_control_list_mapping.py,sha256=YIdlE2IJ1xmUdW5K9YKXwWeeRUYyg8FjLBXmX3RebJc,20167 +twilio/rest/api/v2010/account/sip/ip_access_control_list/__init__.py,sha256=FNhxYIcbhrqeK8Z5EGS_Eq9G6fbWYQaFMaMSgiW1340,21811 +twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/ip_address.cpython-310.pyc,, +twilio/rest/api/v2010/account/sip/ip_access_control_list/ip_address.py,sha256=Na5tO0rwYxFR9__iJ828IswF9sg6OjrxGXa3lGYBd0s,26241 +twilio/rest/api/v2010/account/token.py,sha256=PSHvIFNuPDZY1rhHkapTo5Z_ojoStyQF9SenuTAekus,4824 +twilio/rest/api/v2010/account/transcription.py,sha256=kULeFuW-iIsITGGw5CZm5GZOaTEHsi7XU7AUsm294Ck,18037 +twilio/rest/api/v2010/account/usage/__init__.py,sha256=uuxXF4PSniarhk76bNQxiOXKpBGhFXDVJhNiO-T69qI,2220 +twilio/rest/api/v2010/account/usage/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/__pycache__/trigger.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__init__.py,sha256=FAn0rzPiklwPqW3mEIWXM82tE2De1UbwpfA92hMRJfA,41987 +twilio/rest/api/v2010/account/usage/record/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/all_time.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/daily.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/last_month.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/monthly.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/this_month.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/today.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/yearly.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/__pycache__/yesterday.cpython-310.pyc,, +twilio/rest/api/v2010/account/usage/record/all_time.py,sha256=L2QXmqQR3OxlA41ZhI1jv3hgyiryWFRZ1ycgC1UY4ko,38631 +twilio/rest/api/v2010/account/usage/record/daily.py,sha256=2k5oCRpSB-BVuDjVq0oN6Rp8ayNpm60nMRgbyE_0Jow,38539 +twilio/rest/api/v2010/account/usage/record/last_month.py,sha256=VVlK1JKhagNDzGHGwNbS9ZUnJTyb2_YWEqS1Gc3gNJY,38723 +twilio/rest/api/v2010/account/usage/record/monthly.py,sha256=R2fv_31TBIzEuI1cfPZvqd5V8m5M5ngHczqOLPQDC7E,38631 +twilio/rest/api/v2010/account/usage/record/this_month.py,sha256=9SgQv9fDRvYz6B_zpjvsPnpIm-x672W4h1xH0WhpUzQ,38723 +twilio/rest/api/v2010/account/usage/record/today.py,sha256=j_eLjR154JSKx8KlA8LHV4pg-P0iBCxBMYVs7F2pcP4,38539 +twilio/rest/api/v2010/account/usage/record/yearly.py,sha256=kgLHCVSdIuIOcgESpLRQTTByahRFbXakINd4Z9UE5MY,38585 +twilio/rest/api/v2010/account/usage/record/yesterday.py,sha256=Pa1bXJ08bJki7NgjVdR6dY9CsjsszqJLrKu52wzAoyU,38723 +twilio/rest/api/v2010/account/usage/trigger.py,sha256=dQMp_t0IUHxFwEA10jkbcXakf0mK8HKz4-h2vH3LweM,49482 +twilio/rest/api/v2010/account/validation_request.py,sha256=JMtXDVZu1uCgupOhYgi415SIgqsn_4C71Mz3XZJ5hf8,7398 +twilio/rest/autopilot/__init__.py,sha256=GDd0N8sgBwO_w5kzJYw6VICpka2t0UVf608sJR7lNho,778 +twilio/rest/autopilot/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/bulkexports/BulkexportsBase.py,sha256=zZVbqHt-GFEwknpCSTIkMnUiy0psGTVr707I65ppqRA,1232 +twilio/rest/bulkexports/__init__.py,sha256=W70NE5bxC2jLDmKtWWL5WUe_hYQeywBIlqfRrQQxsnU,792 +twilio/rest/bulkexports/__pycache__/BulkexportsBase.cpython-310.pyc,, +twilio/rest/bulkexports/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/bulkexports/v1/__init__.py,sha256=17bpWiEa9ctnhyCFjXOlYLQNcrqMILJ28XTUY5MSeXc,1693 +twilio/rest/bulkexports/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/bulkexports/v1/__pycache__/export_configuration.cpython-310.pyc,, +twilio/rest/bulkexports/v1/export/__init__.py,sha256=EcjDP-0JaPk9UX7VNwJFJxsIKA4Nca-VgLBh2ZvSA0w,7083 +twilio/rest/bulkexports/v1/export/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/bulkexports/v1/export/__pycache__/day.cpython-310.pyc,, +twilio/rest/bulkexports/v1/export/__pycache__/export_custom_job.cpython-310.pyc,, +twilio/rest/bulkexports/v1/export/__pycache__/job.cpython-310.pyc,, +twilio/rest/bulkexports/v1/export/day.py,sha256=o18PN9-7SbjeEALfwuMR-f-y5JjbZ-TbIiSwq_SBKJs,14522 +twilio/rest/bulkexports/v1/export/export_custom_job.py,sha256=NTkSPBVCzFXjDmH02xSm6z5ubaBgoywDIZsTs_0U1RY,17007 +twilio/rest/bulkexports/v1/export/job.py,sha256=oDakamRykzeDgkZcsF5hnuRm-bgtPnvJ_uvLq4M6vww,8224 +twilio/rest/bulkexports/v1/export_configuration.py,sha256=Ad_fpegWxD1KK-Fih_8O1NzE4D4i89_Pa0TG6jcVrS8,10880 +twilio/rest/chat/ChatBase.py,sha256=KD32OSHBJlk3FnRUTh5rYBgVCX-8Wd3FxMK-sMbwfCU,1701 +twilio/rest/chat/__init__.py,sha256=yqheEsHshStD2dZwRJIab6Vd7pJ8l99lx6I1D-SDLaA,970 +twilio/rest/chat/__pycache__/ChatBase.cpython-310.pyc,, +twilio/rest/chat/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v1/__init__.py,sha256=pIPnbTMhwrC_TrSuEkGC1FMRoI9P0fl7auFCScLZs5g,1570 +twilio/rest/chat/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v1/__pycache__/credential.cpython-310.pyc,, +twilio/rest/chat/v1/credential.py,sha256=-ncWW256d0Dbo2oCIlFg-s1SgkWPgQin5_4O8kGRXhw,27988 +twilio/rest/chat/v1/service/__init__.py,sha256=D3591TzAgls8SPaClwaXrsrwMXYdvIFfhaQkWtrSiUU,93405 +twilio/rest/chat/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v1/service/__pycache__/role.cpython-310.pyc,, +twilio/rest/chat/v1/service/channel/__init__.py,sha256=jo3TvNml1Eac-BoNbDl7EFhY7Mv4r1mscxpON8Bb-QU,28972 +twilio/rest/chat/v1/service/channel/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v1/service/channel/__pycache__/invite.cpython-310.pyc,, +twilio/rest/chat/v1/service/channel/__pycache__/member.cpython-310.pyc,, +twilio/rest/chat/v1/service/channel/__pycache__/message.cpython-310.pyc,, +twilio/rest/chat/v1/service/channel/invite.py,sha256=QTV6Dbcuc_BxhxS_LsS6RMyXbbGj0mc00i2Fq-ywMaQ,22315 +twilio/rest/chat/v1/service/channel/member.py,sha256=etLn0mRd8dILgTBMs484Nms23WR8fZCEXMQoXtlinsg,27714 +twilio/rest/chat/v1/service/channel/message.py,sha256=HW2o__9y6o2YSemLDvul9HPHSyl1R5laQYXlMenOaoA,26361 +twilio/rest/chat/v1/service/role.py,sha256=8bAlaNHDSJRJRm7-gKNn9-1phuE-nblWXDrbcjlgvdQ,21836 +twilio/rest/chat/v1/service/user/__init__.py,sha256=xlruSf-ARHWFcAcUeOmZTR2yT389maIWH7dbxSPhn20,26320 +twilio/rest/chat/v1/service/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v1/service/user/__pycache__/user_channel.cpython-310.pyc,, +twilio/rest/chat/v1/service/user/user_channel.py,sha256=8Tb8mH5nW8uqwgmlvHr-Rth6rATLGoJbNQjnizWCaLg,12927 +twilio/rest/chat/v2/__init__.py,sha256=2MmAILBGDLB1r-m1D9B3OuCk0N-b8XosRDGwinn32C0,1570 +twilio/rest/chat/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v2/__pycache__/credential.cpython-310.pyc,, +twilio/rest/chat/v2/credential.py,sha256=H_NnINGnUINQCwPUAtOICqkUxuvlTXjgTdB3tK8gmFw,27618 +twilio/rest/chat/v2/service/__init__.py,sha256=GWlWhNIbl9LHrlvKBaDKfIB0dY1VL5R7bONPdwdqOgI,67569 +twilio/rest/chat/v2/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v2/service/__pycache__/binding.cpython-310.pyc,, +twilio/rest/chat/v2/service/__pycache__/role.cpython-310.pyc,, +twilio/rest/chat/v2/service/binding.py,sha256=kudZEdVbDOCAW2hKkfn4MNvk-TjIlKFE6L2mdGqDAl0,22121 +twilio/rest/chat/v2/service/channel/__init__.py,sha256=BcPEUUvOij6pq85y76IkyYA4sikxe6tp-uZYKqmyCDs,39130 +twilio/rest/chat/v2/service/channel/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v2/service/channel/__pycache__/invite.cpython-310.pyc,, +twilio/rest/chat/v2/service/channel/__pycache__/member.cpython-310.pyc,, +twilio/rest/chat/v2/service/channel/__pycache__/message.cpython-310.pyc,, +twilio/rest/chat/v2/service/channel/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/chat/v2/service/channel/invite.py,sha256=G9nAYA7zikj_7AH0jVxE2GqBqX7YI1jkXlnc5jjm4z8,22295 +twilio/rest/chat/v2/service/channel/member.py,sha256=zEFoijQADw_a0GNApbZrDlKv4Uh3GUtwPgG8bKbQiDQ,41118 +twilio/rest/chat/v2/service/channel/message.py,sha256=S1P7k5F0prdxE7Thuqdyazh1CPMq6TRTGtdHoIOH3n8,37552 +twilio/rest/chat/v2/service/channel/webhook.py,sha256=oQQbl59Gnxial2nPpIKIJGC9CZBq4AgT_QXP2STsdvg,33775 +twilio/rest/chat/v2/service/role.py,sha256=0yyqg96r9SI3MUNTJSxuXV3Gxifcw9nhIBXxO-LUlNY,22237 +twilio/rest/chat/v2/service/user/__init__.py,sha256=aiSIwz42435gI97NOZ79w7xigxo45trH199hEoRWMiA,29042 +twilio/rest/chat/v2/service/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v2/service/user/__pycache__/user_binding.cpython-310.pyc,, +twilio/rest/chat/v2/service/user/__pycache__/user_channel.cpython-310.pyc,, +twilio/rest/chat/v2/service/user/user_binding.py,sha256=yxgOnCjybHhB4fIR4VUUk7nH-UB11rQwfjhaeLUuxZ0,21912 +twilio/rest/chat/v2/service/user/user_channel.py,sha256=ltPBCLV2QqW-nTLcIuF0YeKMd1LhQcUBOQf2dBTzjGQ,27055 +twilio/rest/chat/v3/__init__.py,sha256=iNQ57HwS3IRB3wny2tHXgjL_qW8eN_c37SAGYYMRpAU,1269 +twilio/rest/chat/v3/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/chat/v3/__pycache__/channel.cpython-310.pyc,, +twilio/rest/chat/v3/channel.py,sha256=OHzAG7U71Skskia98okTwiydPWf7gIU9geSXi2vrj-I,11639 +twilio/rest/content/ContentBase.py,sha256=qVK1PprgPiQQ8G7Aa_Imgp8-j0q-qMUMm-sdaoeFwlE,1204 +twilio/rest/content/__init__.py,sha256=Q9Dc5LDsvNo1f1FttcKGP8uwMr2DDOoyrzdVHjgb5YA,1110 +twilio/rest/content/__pycache__/ContentBase.cpython-310.pyc,, +twilio/rest/content/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/content/v1/__init__.py,sha256=OJ_jzAzh2Q0NcAQUb3l5ohrBR08wfwDYfLMNwoQZ4No,2025 +twilio/rest/content/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/content/v1/__pycache__/content_and_approvals.cpython-310.pyc,, +twilio/rest/content/v1/__pycache__/legacy_content.cpython-310.pyc,, +twilio/rest/content/v1/content/__init__.py,sha256=SlVcvWVZ1_upj-8p8z38N_SMqN_geyMhkQM9kbTbUR4,33351 +twilio/rest/content/v1/content/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/content/v1/content/__pycache__/approval_create.cpython-310.pyc,, +twilio/rest/content/v1/content/__pycache__/approval_fetch.cpython-310.pyc,, +twilio/rest/content/v1/content/approval_create.py,sha256=lu2S4mSWFsFTRQIG2Y6f2n0Gj2GRzBkXSz4EBm8M1MA,4372 +twilio/rest/content/v1/content/approval_fetch.py,sha256=8dlqXoOkIs0cjR8H8zhqesTwQzlSLFD1H3zPYoj_4qw,6059 +twilio/rest/content/v1/content_and_approvals.py,sha256=5BfUOeosXJSicWDLBJ2a6w_H2936vopM2amBg80oDno,11924 +twilio/rest/content/v1/legacy_content.py,sha256=BAZAnpuMLxN6CLSjw7nR4zFfaoM81XCspEZGyZP9pGQ,12123 +twilio/rest/conversations/ConversationsBase.py,sha256=_xLX2JZyT_NNtyDsHn9NVE3LT3rh9x_gSlqFNE5zxyE,1246 +twilio/rest/conversations/__init__.py,sha256=wrtnbo04IMJ0dAyAXcBU2FPCV3fRFfDjjvQdGtNqbiA,2760 +twilio/rest/conversations/__pycache__/ConversationsBase.cpython-310.pyc,, +twilio/rest/conversations/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/__init__.py,sha256=pW61TJ1PNmI38mMoX53CNFtnGUIKfLFeyo4cgZ3JB8k,3660 +twilio/rest/conversations/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/__pycache__/address_configuration.cpython-310.pyc,, +twilio/rest/conversations/v1/__pycache__/credential.cpython-310.pyc,, +twilio/rest/conversations/v1/__pycache__/participant_conversation.cpython-310.pyc,, +twilio/rest/conversations/v1/__pycache__/role.cpython-310.pyc,, +twilio/rest/conversations/v1/address_configuration.py,sha256=Q1nZbF3BHhBCfQQjD5m5LzgHjfwwDts7FDpgyehvf00,37916 +twilio/rest/conversations/v1/configuration/__init__.py,sha256=Oi38rO9QWn9mBnsZ1OZ7Ire6DxFz2MnbhM9xdbYj-hA,12567 +twilio/rest/conversations/v1/configuration/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/configuration/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/conversations/v1/configuration/webhook.py,sha256=BhSfPoc2IjCSJK6EtYWCj17lHsHzTvqPHjlArDPYdoM,11085 +twilio/rest/conversations/v1/conversation/__init__.py,sha256=2csSkIKUVgiAXIjAmconK-Is90WSWRGUYaMMpMcgaoM,49916 +twilio/rest/conversations/v1/conversation/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/conversation/__pycache__/participant.cpython-310.pyc,, +twilio/rest/conversations/v1/conversation/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/conversations/v1/conversation/message/__init__.py,sha256=b21wpvx4unsYGysP3i5DAtQ_skhFJmMeDhzrmAH1OVM,36008 +twilio/rest/conversations/v1/conversation/message/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc,, +twilio/rest/conversations/v1/conversation/message/delivery_receipt.py,sha256=zqnP3CDkZWJh-cAPOi3Ln5iA3k1adze6YhkPSFsx35s,18066 +twilio/rest/conversations/v1/conversation/participant.py,sha256=jvqVyZ4Vn2qaFfTZ-DZ223RlBAdeZF5U1o8fMfNFUzw,40123 +twilio/rest/conversations/v1/conversation/webhook.py,sha256=DsDCchbxcf_D94JbGqDklCnjLLDFAvr3opLydghoMco,27241 +twilio/rest/conversations/v1/credential.py,sha256=YttyouYUsGYiIxDIrDK5C56V--Mq9ALXJb3ngCiXqYc,28067 +twilio/rest/conversations/v1/participant_conversation.py,sha256=yPjacKbHlvF7CcpPN_hbXwHNzWE-I9lQuy3A8LbUQE0,18747 +twilio/rest/conversations/v1/role.py,sha256=fe6HdUKJ_pXygtYrOHQLdy2ZR-Ax5YUimrxOtZl1myQ,20948 +twilio/rest/conversations/v1/service/__init__.py,sha256=YmPFxEQmn6b2OfkgwErBkmUNK1RL_axxlk7vfxCP5no,20028 +twilio/rest/conversations/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/service/__pycache__/binding.cpython-310.pyc,, +twilio/rest/conversations/v1/service/__pycache__/participant_conversation.cpython-310.pyc,, +twilio/rest/conversations/v1/service/__pycache__/role.cpython-310.pyc,, +twilio/rest/conversations/v1/service/binding.py,sha256=7SLRtFf8_O7PkdxP2b-JMN6pyd9FkjYpHpBZ3sQ5yOE,22292 +twilio/rest/conversations/v1/service/configuration/__init__.py,sha256=pruGyBiQ5FWEiMmLJ2Qy6ilB_jPArGGupDvNs0f0a3E,16163 +twilio/rest/conversations/v1/service/configuration/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/service/configuration/__pycache__/notification.cpython-310.pyc,, +twilio/rest/conversations/v1/service/configuration/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/conversations/v1/service/configuration/notification.py,sha256=MZ5zzbhshDYWznslFOf8NOJRi0RTzcZTpFCXULNjNcE,25073 +twilio/rest/conversations/v1/service/configuration/webhook.py,sha256=nDx96T2cmOLl33Yu-5X6sRV7knJIew-Y-iSvoIdKkBw,13883 +twilio/rest/conversations/v1/service/conversation/__init__.py,sha256=gbCcPEKKbWEP8Hv24Qb57QELAuC5X21YFF7adrNfbdk,51767 +twilio/rest/conversations/v1/service/conversation/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/service/conversation/__pycache__/participant.cpython-310.pyc,, +twilio/rest/conversations/v1/service/conversation/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/conversations/v1/service/conversation/message/__init__.py,sha256=mpVwG0txh9GbW2dc65sZxwdzsyHXR8A-lO7KxwNYUvk,37665 +twilio/rest/conversations/v1/service/conversation/message/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/service/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc,, +twilio/rest/conversations/v1/service/conversation/message/delivery_receipt.py,sha256=8q812CbaR5Bz5dkvq78_drMgCzX4ceQT5AIW15UC4fM,19401 +twilio/rest/conversations/v1/service/conversation/participant.py,sha256=aflGIhXRm9yZ1LYBXCPo0QacjlYW2WiXJmlzvL5e9po,42100 +twilio/rest/conversations/v1/service/conversation/webhook.py,sha256=rwsS_YMzgbfLvOIRcjU_Sx4UHBsjYunUeXAyCw-KpEo,28931 +twilio/rest/conversations/v1/service/participant_conversation.py,sha256=8vdtqyABU5e0AUHczV8KfGDP0FnylE2JvJ3kb7Sjgm0,19538 +twilio/rest/conversations/v1/service/role.py,sha256=FoErqRNA_zqCBLGkNBlfEoJHC50rlgIpMgBEhHpUpuM,22539 +twilio/rest/conversations/v1/service/user/__init__.py,sha256=Qm5xyom6d4_7_bWUowQxGKC8ZfcXePr1ReCkfZjCMAc,29689 +twilio/rest/conversations/v1/service/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/service/user/__pycache__/user_conversation.cpython-310.pyc,, +twilio/rest/conversations/v1/service/user/user_conversation.py,sha256=kiO53C20xcOq49XZ8WcOR_waj01Y4R_4rXtf6mh0Ri4,26515 +twilio/rest/conversations/v1/user/__init__.py,sha256=CMGJlEbpKWJZlrHCIgiJtcDdCZszkoCxOvBHzvuMlAk,28023 +twilio/rest/conversations/v1/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/conversations/v1/user/__pycache__/user_conversation.cpython-310.pyc,, +twilio/rest/conversations/v1/user/user_conversation.py,sha256=w6q8O3Ds6Kc5n-LQSU99iD6nBtuqIH9Ol9D-Q_pwlLE,25220 +twilio/rest/events/EventsBase.py,sha256=VmnvvHtsOqJHgao-2Db4suiqZbpRmzlbR_0mkXyg-yg,1197 +twilio/rest/events/__init__.py,sha256=0c7O0oWx1KIrwS_tzCy8c34OkPJfqXCK_9lULiGi_7s,1277 +twilio/rest/events/__pycache__/EventsBase.cpython-310.pyc,, +twilio/rest/events/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/events/v1/__init__.py,sha256=gxoinjjx5rqW0zUxOp6Ild8m1mNG8zayV5-nnlbCR-Q,2134 +twilio/rest/events/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/events/v1/__pycache__/event_type.cpython-310.pyc,, +twilio/rest/events/v1/event_type.py,sha256=majLBARs63YYiLd2MKI1EpuhcEYiLN4Fs4LIEekWBu4,15318 +twilio/rest/events/v1/schema/__init__.py,sha256=vRodC03nVmolviyEmr5b1rqDEWX83g4niunqTVCcRQg,6419 +twilio/rest/events/v1/schema/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/events/v1/schema/__pycache__/schema_version.cpython-310.pyc,, +twilio/rest/events/v1/schema/schema_version.py,sha256=CSgzrnj8jwtWBe9e7FMWJGP0MRNh5eEEMdYiXT67TlA,14921 +twilio/rest/events/v1/sink/__init__.py,sha256=_CJmX414j9LeJfW9TicB3f0GZmAG3UEAvCNIMwE4Uxo,22935 +twilio/rest/events/v1/sink/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/events/v1/sink/__pycache__/sink_test.cpython-310.pyc,, +twilio/rest/events/v1/sink/__pycache__/sink_validate.cpython-310.pyc,, +twilio/rest/events/v1/sink/sink_test.py,sha256=sgKePE_6D6WgtkiH0GvbaQm5s3QG0Sk8e1SPtTv20_4,2767 +twilio/rest/events/v1/sink/sink_validate.py,sha256=kf3ec7vuNVmNKFgKBMoxkBiJvPQ8Yk978a7tnnNd6ec,3359 +twilio/rest/events/v1/subscription/__init__.py,sha256=yhDhRFlZaFSZhpMnzIeRen2OiNZVmbv0Q0GI_uq1ZNE,22612 +twilio/rest/events/v1/subscription/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/events/v1/subscription/__pycache__/subscribed_event.cpython-310.pyc,, +twilio/rest/events/v1/subscription/subscribed_event.py,sha256=cR1heFAYi5pMlQ5JLvvX72vg8tlinsR7OKusgaG4uMU,20285 +twilio/rest/flex_api/FlexApiBase.py,sha256=uej-QbJtfmiUEUj54Hs5nXUoUmeWUnebYCqlHZ__Nck,1472 +twilio/rest/flex_api/__init__.py,sha256=95CohrSn9QGa5NEMqi1FfZ0ojxuqK5rVvszrR0NQd9Q,6332 +twilio/rest/flex_api/__pycache__/FlexApiBase.cpython-310.pyc,, +twilio/rest/flex_api/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/__init__.py,sha256=obnvo_MQ0rmSwmTbgQk7DEM9fxHwuMxmkJeL3_4BP30,10023 +twilio/rest/flex_api/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/assessments.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/channel.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/configuration.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/flex_flow.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_assessments_comment.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_conversations.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_questionnaires.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_category.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_question.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_segments.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_session.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_settings_answer_sets.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_settings_comment.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/insights_user_roles.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/plugin_archive.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/plugin_configuration_archive.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/plugin_release.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/plugin_version_archive.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/provisioning_status.cpython-310.pyc,, +twilio/rest/flex_api/v1/__pycache__/web_channel.cpython-310.pyc,, +twilio/rest/flex_api/v1/assessments.py,sha256=bXZh0BkKq2CcUvy7Ifizwua8YNz5rbUWwuXckeotBIM,23444 +twilio/rest/flex_api/v1/channel.py,sha256=ew2BSG7KEZ7zHHZXtfjUxTkhj9NO0t8NG7Oiv43Y_nQ,19865 +twilio/rest/flex_api/v1/configuration.py,sha256=rEpUloBjI-OZuN61b0eSsFCebXqux2AK4EavRQJU3WU,16094 +twilio/rest/flex_api/v1/flex_flow.py,sha256=qdhRcx5sTIswGWSjlGZSk_7NeYCTZvUOGedD8TIZ_8o,47494 +twilio/rest/flex_api/v1/insights_assessments_comment.py,sha256=HPG8TUH07yiHVQLgu6A-6I_e_Yx5LJF7f3iXu3-l6SE,17365 +twilio/rest/flex_api/v1/insights_conversations.py,sha256=10VEzcoe5VEQ2260-RUTT58RfvXi6BOWUBLZmKkaKvI,12881 +twilio/rest/flex_api/v1/insights_questionnaires.py,sha256=hYv7I2L3EwPWhy2Hoc0TQCPZVbmK8UkUiYPYhSdAOAg,28144 +twilio/rest/flex_api/v1/insights_questionnaires_category.py,sha256=oRReu4oO2j_BEtlQ7-BS2wLrFJtT1ZitzzvF8yJluHk,21096 +twilio/rest/flex_api/v1/insights_questionnaires_question.py,sha256=e9GqDkvzH2Xw0OOOfaHKoRV0CNY1PVriZ61ZOCButSY,26893 +twilio/rest/flex_api/v1/insights_segments.py,sha256=gXEYH4Dt441T7QzCHrSosrxI1pbg4bW3yk4lIEbSuZ4,15944 +twilio/rest/flex_api/v1/insights_session.py,sha256=bc4_hAx5exIgh0at-hmnAECjAh3ur33MguxMi4JjIRE,5688 +twilio/rest/flex_api/v1/insights_settings_answer_sets.py,sha256=Fx-uZvuSr4gvCeHIiMVhnHQUFXV21U2hxff7ngVnjYo,3753 +twilio/rest/flex_api/v1/insights_settings_comment.py,sha256=Cz8DZorupKWKsnQ6V1K7g9mmpgRG3Eb73pepwUTJIO4,3334 +twilio/rest/flex_api/v1/insights_user_roles.py,sha256=_VgFElI82jvgczFB94pxTINcHJUNf119NbNJtgTx6IU,5365 +twilio/rest/flex_api/v1/interaction/__init__.py,sha256=9WhNZU-Prdp9FqFjvNn0EAFQSUCaeCAjullD2KSD-DM,9253 +twilio/rest/flex_api/v1/interaction/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/interaction/interaction_channel/__init__.py,sha256=oQE2S68NjMymOISN3NqEQG_59Q7hNUPJ4BHSZSjU_dA,21581 +twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_invite.cpython-310.pyc,, +twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_participant.cpython-310.pyc,, +twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_invite.py,sha256=NHsMsaRmrKtCX8iGRJvasqcvJz4yJm_3J8ik3qe2BYA,13863 +twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_participant.py,sha256=D6Yv7UddLAU1nxoUuAJ2x_uTTQO3ImKxDS_Bh0kMQOA,20538 +twilio/rest/flex_api/v1/plugin/__init__.py,sha256=Hx5njxAsw5CMrqD9t6_xBRZC7XMW-X0cEeIyRLh6HoM,23281 +twilio/rest/flex_api/v1/plugin/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/plugin/__pycache__/plugin_versions.cpython-310.pyc,, +twilio/rest/flex_api/v1/plugin/plugin_versions.py,sha256=0UU1cTcbVspryPRImxVnkfgcPgRe_CNC06gwNhWZKdE,21468 +twilio/rest/flex_api/v1/plugin_archive.py,sha256=YeCsZRTVZPacCRnAN0Ha2M-Fm_t1DeXG1Rsdpb3gsOE,7631 +twilio/rest/flex_api/v1/plugin_configuration/__init__.py,sha256=9F_LT5inStInaqxed3fiGLDDtU0aJJK2W0xhVlBTu08,20121 +twilio/rest/flex_api/v1/plugin_configuration/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v1/plugin_configuration/__pycache__/configured_plugin.cpython-310.pyc,, +twilio/rest/flex_api/v1/plugin_configuration/configured_plugin.py,sha256=QcDmRl0bhi5ogjW7vpsQzniaD6VQop9DEcv208CQOsU,19509 +twilio/rest/flex_api/v1/plugin_configuration_archive.py,sha256=zhCe6KhIAFpHNKUwyXRLffFBpeirbhaGqW5hmnrmNIo,7774 +twilio/rest/flex_api/v1/plugin_release.py,sha256=NOEaBMXT2Cvw_WK3vCzA8JRIuSNtf3pfghOA2XqwFik,17816 +twilio/rest/flex_api/v1/plugin_version_archive.py,sha256=FluUO8XyW69kiftW9a-cCRyUS2z4Yjaoe5-S3Kt1JgQ,8781 +twilio/rest/flex_api/v1/provisioning_status.py,sha256=HRddgxi2LrB8qikoOiiBdSxBWdgfqgGMRiJuqFI2sj0,4739 +twilio/rest/flex_api/v1/web_channel.py,sha256=_7k5qrj0EfE7VfkwzSk5u44R5pybJdYaJUyz1HzYUGY,20826 +twilio/rest/flex_api/v2/__init__.py,sha256=WMe1F3S9rix25wT-TMn9o4BdKxc65EeMjC1VyDUWgyE,1610 +twilio/rest/flex_api/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/flex_api/v2/__pycache__/flex_user.cpython-310.pyc,, +twilio/rest/flex_api/v2/__pycache__/web_channels.cpython-310.pyc,, +twilio/rest/flex_api/v2/flex_user.py,sha256=HgELUzmJeyGnJf3u7NopsyA8ocZgDXetoXuFzbkgl7I,8100 +twilio/rest/flex_api/v2/web_channels.py,sha256=gulmFc8ksd2aalVsB412jvvJwRqJNtiALPh1grg-ChI,6020 +twilio/rest/frontline_api/FrontlineApiBase.py,sha256=LxpGZeNOYqlqYNh8VxMXZoDR_z13_Nh36r-vRQYUDRE,1241 +twilio/rest/frontline_api/__init__.py,sha256=dY4Pp2907z2kTJJ3jaSt4pX9-qP3t36LRK1Zs8Yniog,410 +twilio/rest/frontline_api/__pycache__/FrontlineApiBase.cpython-310.pyc,, +twilio/rest/frontline_api/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/frontline_api/v1/__init__.py,sha256=AcNIDcakdTFSUXD3AF55ot77IsixzC-rJAPP9Cs6A4I,1278 +twilio/rest/frontline_api/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/frontline_api/v1/__pycache__/user.cpython-310.pyc,, +twilio/rest/frontline_api/v1/user.py,sha256=w4yKEGoxSsUnc0UUtAjhNNHAvL26GhDT5S9SwfJ4xZ8,10893 +twilio/rest/insights/InsightsBase.py,sha256=E0d9cswKhgPZByiFy51V7AGE-TiQ7lZOB1hbGOczY7Q,1211 +twilio/rest/insights/__init__.py,sha256=quAZLguZrWAk1irDvVn1ChI9LgdMewLt9SO_YgfGkzQ,1579 +twilio/rest/insights/__pycache__/InsightsBase.cpython-310.pyc,, +twilio/rest/insights/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/insights/v1/__init__.py,sha256=CQgywgxUWHdv3N_p5I_-SpoczEh6-Q2TvrQE7BZAJoQ,2420 +twilio/rest/insights/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/insights/v1/__pycache__/call_summaries.cpython-310.pyc,, +twilio/rest/insights/v1/__pycache__/setting.cpython-310.pyc,, +twilio/rest/insights/v1/call/__init__.py,sha256=D5ciUU6G1tlaz_8JRkYtOnWguoct-4Bc5vIy2XK_TTw,6981 +twilio/rest/insights/v1/call/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/insights/v1/call/__pycache__/annotation.cpython-310.pyc,, +twilio/rest/insights/v1/call/__pycache__/call_summary.cpython-310.pyc,, +twilio/rest/insights/v1/call/__pycache__/event.cpython-310.pyc,, +twilio/rest/insights/v1/call/__pycache__/metric.cpython-310.pyc,, +twilio/rest/insights/v1/call/annotation.py,sha256=1wyUMjZvucqWplCjQsUEm1pqzFdKDn8EylFN1P6ZQ5A,17620 +twilio/rest/insights/v1/call/call_summary.py,sha256=-x8SgpGoj2EPuWkZHziSX8azD_QnXRI8WxC-SNnD5vM,11170 +twilio/rest/insights/v1/call/event.py,sha256=kgygB6yju72Zt5AZgKhWiVemISeyHGU4nV-QG-zi-CQ,13652 +twilio/rest/insights/v1/call/metric.py,sha256=gcVyUBClJT0YNXnuMedtFDYCKUX0uVOb8AuxcR5dRLQ,14824 +twilio/rest/insights/v1/call_summaries.py,sha256=4K97sNJ7v2v4nzqC0J-KQZB_BYME_HQ3vKO2hQHu_XI,49306 +twilio/rest/insights/v1/conference/__init__.py,sha256=7MauXKv7Y3EbhqgqVCsgo842Dnjzp-1nC0mD_XsbgKo,31379 +twilio/rest/insights/v1/conference/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/insights/v1/conference/__pycache__/conference_participant.cpython-310.pyc,, +twilio/rest/insights/v1/conference/conference_participant.py,sha256=a2-8U0IQ8ahnnbjxF2_lJVO23-Xo36NLTjg419fp0TY,25261 +twilio/rest/insights/v1/room/__init__.py,sha256=LqLq7aqmq0WLhWZJtPWbKT6HDDciy2zrNQVHLw6L9No,25246 +twilio/rest/insights/v1/room/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/insights/v1/room/__pycache__/participant.cpython-310.pyc,, +twilio/rest/insights/v1/room/participant.py,sha256=lQBz7Yxgddavb6ARjKouQPvSWG_Totp9HU5IG06L8dg,17902 +twilio/rest/insights/v1/setting.py,sha256=jY7iggruCKkzN2vopi0wl9cs50h1JdCGW62Bv1v2XBs,9164 +twilio/rest/intelligence/IntelligenceBase.py,sha256=IKwaNvPV9xaBmfpigqsK4zwhtfYDnV5pIf9mmonGVhs,1239 +twilio/rest/intelligence/__init__.py,sha256=F98_JVdUwFkjg-k58c8j6A45f_5HGYyW7mEoCziqMUk,417 +twilio/rest/intelligence/__pycache__/IntelligenceBase.cpython-310.pyc,, +twilio/rest/intelligence/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/intelligence/v2/__init__.py,sha256=yYQYcGatG9SEvnPs4HyiTyXXBPIs1v9_FMtBkyKH-a8,1618 +twilio/rest/intelligence/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/intelligence/v2/__pycache__/service.cpython-310.pyc,, +twilio/rest/intelligence/v2/service.py,sha256=Nwn6zsP4mI4C9oZxg6n2PS3klvIkAjxkBDrh5-sDPfo,33894 +twilio/rest/intelligence/v2/transcript/__init__.py,sha256=w24kL5bYrlbCLFKUOclpQ_6DX95HcdBN3L8Qlqsz7Xw,29041 +twilio/rest/intelligence/v2/transcript/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/intelligence/v2/transcript/__pycache__/media.cpython-310.pyc,, +twilio/rest/intelligence/v2/transcript/__pycache__/operator_result.cpython-310.pyc,, +twilio/rest/intelligence/v2/transcript/__pycache__/sentence.cpython-310.pyc,, +twilio/rest/intelligence/v2/transcript/media.py,sha256=Dxgdh8etpr1mB5XtCsbULD_mC96cMpyW4rAmoKCISc0,6512 +twilio/rest/intelligence/v2/transcript/operator_result.py,sha256=GHFywOaFBoC9neT0Dpd2MTS_hWllHLVpmCqMGXTHRdY,20410 +twilio/rest/intelligence/v2/transcript/sentence.py,sha256=x34QdSywxVR_IJdreFqeC68FjgYFT-1DTT7kFy7mub0,12818 +twilio/rest/ip_messaging/IpMessagingBase.py,sha256=Auoz1W9XNlV8F6owgPNOKpYY93GZnvtpYzUAzYQXyws,1508 +twilio/rest/ip_messaging/__init__.py,sha256=9mxOrxP0A0GodgNQW2cfokhI3-Ejo3MiX9EcPzBrY9s,738 +twilio/rest/ip_messaging/__pycache__/IpMessagingBase.cpython-310.pyc,, +twilio/rest/ip_messaging/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/__init__.py,sha256=NppQLF43kYaFcWw_ipWY9LDyWN1DH2b_lLbNm2H6okA,1616 +twilio/rest/ip_messaging/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/__pycache__/credential.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/credential.py,sha256=v_OQiFmmBq7ocq4aeMBMDyC1XvVGoQ4zRLrzbWZ9EmU,21764 +twilio/rest/ip_messaging/v1/service/__init__.py,sha256=TClaTCJCAAZNMildtWiyTFIp3jCdiCYNnnYQUCbfsCg,63691 +twilio/rest/ip_messaging/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/__pycache__/role.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/channel/__init__.py,sha256=WHrKe_jqSmoMvKjCwxJJulPsRwQLI8ux0VUzo4_d5mw,24069 +twilio/rest/ip_messaging/v1/service/channel/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/channel/__pycache__/invite.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/channel/__pycache__/member.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/channel/__pycache__/message.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/channel/invite.py,sha256=bUB6v9TP2arOnURioVvn5hDmzhbqcEIk7b54DPpOJDg,18544 +twilio/rest/ip_messaging/v1/service/channel/member.py,sha256=vCi_zFjb06BAnyiiX6djcWqLrX_CBui9Pc3wmdZElqs,21730 +twilio/rest/ip_messaging/v1/service/channel/message.py,sha256=vplYDd_WZNrjH-4VCKm4C70vzykskY7vkRyRxnvxiyk,21823 +twilio/rest/ip_messaging/v1/service/role.py,sha256=pQ2Hz443-v2eGGzatMwbYVsOvVtGyf_gCVqGGaEX0B8,18860 +twilio/rest/ip_messaging/v1/service/user/__init__.py,sha256=cmCgK7IsWNTzWymIQzGq36PKGVaGLc7A25VvAx31SMw,21368 +twilio/rest/ip_messaging/v1/service/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/user/__pycache__/user_channel.cpython-310.pyc,, +twilio/rest/ip_messaging/v1/service/user/user_channel.py,sha256=KWy8A6qSQ-pxPSUNSJjdmBrYYYzOxpurXFL7dpmFySs,11550 +twilio/rest/ip_messaging/v2/__init__.py,sha256=7jGoUJ93kos9yv2hkMzA5vflhMYUYzVY92S9Z9B6NlU,1616 +twilio/rest/ip_messaging/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/__pycache__/credential.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/credential.py,sha256=PggFZsPlWe05afCkW7_UyqidDEKz0joK1e3aSTboFRs,21764 +twilio/rest/ip_messaging/v2/service/__init__.py,sha256=Bbew_TrrK8OUv2_IruELtyoGnh8tlF85XcaL1pwex6w,46389 +twilio/rest/ip_messaging/v2/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/__pycache__/binding.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/__pycache__/role.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/binding.py,sha256=eSTOaYoHD_I_LGjQ_5xDZiqENNYQgwuSBRK__MgLaNA,17910 +twilio/rest/ip_messaging/v2/service/channel/__init__.py,sha256=k7v5g05sTk812_pJuoNi746B7glBuf4i57VkjvdqDaQ,30575 +twilio/rest/ip_messaging/v2/service/channel/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/channel/__pycache__/invite.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/channel/__pycache__/member.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/channel/__pycache__/message.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/channel/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/channel/invite.py,sha256=mxIpCp5cWGVejWsGDWPysxRwMnRcZWgzfM4JUV--mA4,18544 +twilio/rest/ip_messaging/v2/service/channel/member.py,sha256=cHdVm6HGi6453xYplLXSezf-e9q4NrKHJZdeCkja1q8,29529 +twilio/rest/ip_messaging/v2/service/channel/message.py,sha256=MAM00QUf1EeYDerZWGfn9McaegDrKWFpWN3i8kHEPW0,28805 +twilio/rest/ip_messaging/v2/service/channel/webhook.py,sha256=9WKQydetsZ6CW13JiF8BTDbhl-IuvmS7OB0w2bxJ7_c,26180 +twilio/rest/ip_messaging/v2/service/role.py,sha256=DM5BzOxrq_8p9dZ1zOAdbKYBMXr4aOBTiHs8b-Z1neg,18860 +twilio/rest/ip_messaging/v2/service/user/__init__.py,sha256=9XD9xdQeGKP6dSNijwsUQf4FB3seaDCVocg2qFqL8Bo,24058 +twilio/rest/ip_messaging/v2/service/user/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/user/__pycache__/user_binding.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/user/__pycache__/user_channel.cpython-310.pyc,, +twilio/rest/ip_messaging/v2/service/user/user_binding.py,sha256=ZW5ZK97CLzmFc8m8NQuXM-wrqupwpJzRU-MBUksDJhA,18195 +twilio/rest/ip_messaging/v2/service/user/user_channel.py,sha256=mTJJWFLVJu8LBKseQLEboDGJ6hpZ0L5vH2E6Em5ixvo,21481 +twilio/rest/lookups/LookupsBase.py,sha256=mH77GksjdX4_ynK3OmtPAjMIboayKFZBTcx8fpQk544,1469 +twilio/rest/lookups/__init__.py,sha256=LDDJTWTOQ1TyhW8ZWS-XfJfF3zO93E5PPyaTzR_6j1g,432 +twilio/rest/lookups/__pycache__/LookupsBase.cpython-310.pyc,, +twilio/rest/lookups/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/lookups/v1/__init__.py,sha256=2wsVTUfxrQaxpH18BvIJTnU-00nq-Y3bCOTymJjfTZk,1330 +twilio/rest/lookups/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/lookups/v1/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/lookups/v1/phone_number.py,sha256=XUz0styT-3rmQrFoEnVshRKx6C_MDJS6cyO4-5j4QjM,13250 +twilio/rest/lookups/v2/__init__.py,sha256=ngFacNsEH1hxQOpRWj4067Te6fxzhLYeQLr_gr0pBDU,1330 +twilio/rest/lookups/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/lookups/v2/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/lookups/v2/phone_number.py,sha256=5hM0eJDSHtOzNHtyq0Xfa8xgJ1sYmb_x820Fvxf-yV4,25093 +twilio/rest/media/__init__.py,sha256=L-OCk9PUahe2sC1y7IOWbaQJ3HlwE8ldoTYTanL-NEo,1107 +twilio/rest/media/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/messaging/MessagingBase.py,sha256=4olKMXglwwdEzGf7cl4sMkxQ1ni7WjwUxMzh_cNeQ98,1218 +twilio/rest/messaging/__init__.py,sha256=b_aOOaeRTDPGh-xajYYKry13McI3bmeF050u6j2UXa8,3308 +twilio/rest/messaging/__pycache__/MessagingBase.cpython-310.pyc,, +twilio/rest/messaging/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/messaging/v1/__init__.py,sha256=VqsV3A_i_Iex1o7dDZER-x8vL5o9elbXZA_O4TGO67c,5613 +twilio/rest/messaging/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/deactivations.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/domain_certs.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/domain_config.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/domain_config_messaging_service.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/external_campaign.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service_domain_association.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/tollfree_verification.cpython-310.pyc,, +twilio/rest/messaging/v1/__pycache__/usecase.cpython-310.pyc,, +twilio/rest/messaging/v1/brand_registration/__init__.py,sha256=X_85xaD9wx3Vw-SVczvZFUk_qR-kdPmXNcA5wwPx4qM,24690 +twilio/rest/messaging/v1/brand_registration/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/messaging/v1/brand_registration/__pycache__/brand_registration_otp.cpython-310.pyc,, +twilio/rest/messaging/v1/brand_registration/__pycache__/brand_vetting.cpython-310.pyc,, +twilio/rest/messaging/v1/brand_registration/brand_registration_otp.py,sha256=9kir-P3JHO1cEAFovZlkO9WAMoMBJS2FTQ9L1CWPz9A,3582 +twilio/rest/messaging/v1/brand_registration/brand_vetting.py,sha256=x5jpl8ZzMH52bZq1RqeTloUrEqPAyLFF1nfkYaWSdKY,19921 +twilio/rest/messaging/v1/deactivations.py,sha256=DnzBMB25tB6ZffsXRPPY5qbquqaT056zFR2xqjKkgEo,5796 +twilio/rest/messaging/v1/domain_certs.py,sha256=Ab6iz5A8AXmcqCWKNX8t1iRJlOWZnvJIpSFncsU2M-c,10495 +twilio/rest/messaging/v1/domain_config.py,sha256=LS2EbRMJcGUu-_7jN-VPyt--jlXEHH1aiurR4yarOp0,14002 +twilio/rest/messaging/v1/domain_config_messaging_service.py,sha256=y9myfaDMMGf-g2rMScnBk5TmS_nSyuNA3xrgWphZ5ZI,7990 +twilio/rest/messaging/v1/external_campaign.py,sha256=vHCDE4oKVLqHVG1rb7tBN05AaF70C-t4NuV1S5wZM8Y,4385 +twilio/rest/messaging/v1/linkshortening_messaging_service.py,sha256=5PiZfzRrca9OJJ-uKHaMOLbJFifxreZ8cOqBX-0b0x4,9216 +twilio/rest/messaging/v1/linkshortening_messaging_service_domain_association.py,sha256=S9rLc1yLgZMcdf1JEkcDbM5jy2t-e75B07fAtYy5fdg,7383 +twilio/rest/messaging/v1/service/__init__.py,sha256=OhorP7s7r8A6cCXxBxIS-13M_XJeXTncQQFtVCR8BAY,57063 +twilio/rest/messaging/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/alpha_sender.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/channel_sender.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/short_code.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/us_app_to_person.cpython-310.pyc,, +twilio/rest/messaging/v1/service/__pycache__/us_app_to_person_usecase.cpython-310.pyc,, +twilio/rest/messaging/v1/service/alpha_sender.py,sha256=wDDysHj4RvD8TVKiJNBgamhagnI3r4rUlvDzTtc7HPE,18351 +twilio/rest/messaging/v1/service/channel_sender.py,sha256=PuMNXwusTAIvj4lE00suTtoOTBz5R5ZbzUhY-MePZrk,16434 +twilio/rest/messaging/v1/service/phone_number.py,sha256=vq_J2vCws9YhRAOdlaahfYQ1uxDwScmtE54NcEEiEgg,18416 +twilio/rest/messaging/v1/service/short_code.py,sha256=0IVyEVcwA4gYqQseZAE9Qp9RNuztzP1U3efHsvBZuw4,18138 +twilio/rest/messaging/v1/service/us_app_to_person.py,sha256=LPGKwkBz7Ye3E14ZjA03T_9OPz5LVktXrhrQldFwoY4,44053 +twilio/rest/messaging/v1/service/us_app_to_person_usecase.py,sha256=PAwlu7FQto_MjqRNqkO1m5gnzy3zw5ROKkRao4Mv-AE,4199 +twilio/rest/messaging/v1/tollfree_verification.py,sha256=GfqE5Be0Dt6IsNXnnBtdCwpslOMh9p9aA_Nmz5ASpks,59064 +twilio/rest/messaging/v1/usecase.py,sha256=yTf3ssmAnZUrgQWBAx1swzBMeks1hGib9rLn-0axZbk,2336 +twilio/rest/microvisor/MicrovisorBase.py,sha256=qAQUxPKy5T4sjQxS1QLzNwZUuHSVNA1R6ANInXmLo1Q,1225 +twilio/rest/microvisor/__init__.py,sha256=VvCLDiEovqv3pvNtlafvwWWGupHDSOqLg0fDFLg-jrQ,1321 +twilio/rest/microvisor/__pycache__/MicrovisorBase.cpython-310.pyc,, +twilio/rest/microvisor/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/microvisor/v1/__init__.py,sha256=FpDCQcXn-wXd9-pDgvYRweUvclcQmcw7q9scbHbiYJU,2212 +twilio/rest/microvisor/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/microvisor/v1/__pycache__/account_config.cpython-310.pyc,, +twilio/rest/microvisor/v1/__pycache__/account_secret.cpython-310.pyc,, +twilio/rest/microvisor/v1/account_config.py,sha256=Zo9tNyoWH-Fsxu6xMzUV5f0RDIElvXYpNSBoYSU0OvU,17841 +twilio/rest/microvisor/v1/account_secret.py,sha256=FB72qGO5qCfIKXlQ75dYQBPgUFS-On7eai-6yid0EU0,17726 +twilio/rest/microvisor/v1/app/__init__.py,sha256=BZSWLyOb7EWGuSBWuwsImP7cuYQge_eKW_y_hx4RHtQ,15665 +twilio/rest/microvisor/v1/app/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/microvisor/v1/app/__pycache__/app_manifest.cpython-310.pyc,, +twilio/rest/microvisor/v1/app/app_manifest.py,sha256=wY1An2fmTX8GqiKCTrfQ8JtZ-UYPc5ZomqPnJuW3IUE,5534 +twilio/rest/microvisor/v1/device/__init__.py,sha256=9EFW5ob3cqnA6GE2_NVf_-AekqS3dNqHhXNBPYhRx6I,20979 +twilio/rest/microvisor/v1/device/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/microvisor/v1/device/__pycache__/device_config.cpython-310.pyc,, +twilio/rest/microvisor/v1/device/__pycache__/device_secret.cpython-310.pyc,, +twilio/rest/microvisor/v1/device/device_config.py,sha256=qYzRKhmqeUQj5EeBBS521Ni0zXKelzf0xrsQfVwJPBE,19148 +twilio/rest/microvisor/v1/device/device_secret.py,sha256=YSj6DJEVzbtdCf8FDRBMx_ljO_UznhQ1ilUVCf9ZL3k,19033 +twilio/rest/monitor/MonitorBase.py,sha256=ZUBQwu6h9-c-QsjqzqWOjAgKW0CLbhSzWkCaA6T9_I8,1204 +twilio/rest/monitor/__init__.py,sha256=qSQaNLR3BLW7RbTgo4-K5hyUs8WjFpQkejqdEmA1dgo,658 +twilio/rest/monitor/__pycache__/MonitorBase.cpython-310.pyc,, +twilio/rest/monitor/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/monitor/v1/__init__.py,sha256=jKGc7sS5H9TlP4M_SYtRAlTBGuVTG_NHrsmMqKxLaKg,1518 +twilio/rest/monitor/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/monitor/v1/__pycache__/alert.cpython-310.pyc,, +twilio/rest/monitor/v1/__pycache__/event.cpython-310.pyc,, +twilio/rest/monitor/v1/alert.py,sha256=5hhzU3go_GnSu3oYCN9W3d0dEZaW4CchD7MflxroSlQ,22724 +twilio/rest/monitor/v1/event.py,sha256=uEKNSr68MO8hIeRgLE-_GVKXpydZ1ZCcd9CLdpvm5k8,25438 +twilio/rest/notify/NotifyBase.py,sha256=63iUENxBxbeJ-AfquhzrOnfHxjA1Oh_ZF4JzKXFjrZQ,1197 +twilio/rest/notify/__init__.py,sha256=fqpFtdtT21ai83xGyuIHVTFFp04n0XS3J2N20Ebn4rc,700 +twilio/rest/notify/__pycache__/NotifyBase.cpython-310.pyc,, +twilio/rest/notify/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/notify/v1/__init__.py,sha256=w5j4Nxwc7PAmhImW90TEa38frEH3mjwUgAxT2Wn2iXA,1582 +twilio/rest/notify/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/notify/v1/__pycache__/credential.cpython-310.pyc,, +twilio/rest/notify/v1/credential.py,sha256=WD5nqgBSQZEU86-2WOr1L9OD2FMe1Pof9aAO66oLd1I,28093 +twilio/rest/notify/v1/service/__init__.py,sha256=_q6L9j0C1hnupot5cbgb2JwHHU-SXpReFqBQanAMuWU,47437 +twilio/rest/notify/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/notify/v1/service/__pycache__/binding.cpython-310.pyc,, +twilio/rest/notify/v1/service/__pycache__/notification.cpython-310.pyc,, +twilio/rest/notify/v1/service/binding.py,sha256=-JTc9-tiOCnRLg1t1odTxdqyc2FJOjWctR4zyWTm1yw,30277 +twilio/rest/notify/v1/service/notification.py,sha256=Ewmh6kn0ZY_REotpTiUNmqX_TLDcUzF5tOCki25W8n0,25487 +twilio/rest/numbers/NumbersBase.py,sha256=4QlX-Tsp953-WAZAJSdbn4b3hxx67pL_KI4N5rEbq0E,1469 +twilio/rest/numbers/__init__.py,sha256=wIjsaoiSWgzPPa5leWNXsOwttzjCbOg7NhVG0eGV2c8,491 +twilio/rest/numbers/__pycache__/NumbersBase.cpython-310.pyc,, +twilio/rest/numbers/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v1/__init__.py,sha256=Q9Nd9FKmfSjwJZu1evM2bZrUX4p8mQmNSXqhRF4A64c,3774 +twilio/rest/numbers/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/bulk_eligibility.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/eligibility.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/porting_bulk_portability.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/porting_port_in.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/porting_port_in_fetch.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/porting_port_in_phone_number.cpython-310.pyc,, +twilio/rest/numbers/v1/__pycache__/porting_portability.cpython-310.pyc,, +twilio/rest/numbers/v1/bulk_eligibility.py,sha256=r-3bBI7PoBpHz-PR8GfAr1B3Sm6xTEgVrpDHg0tLax0,8249 +twilio/rest/numbers/v1/eligibility.py,sha256=iOt5aeOdiUdHs1Io1kZiZaBdVgAkiiC6cWqogk0jFF8,3426 +twilio/rest/numbers/v1/porting_bulk_portability.py,sha256=QNFh_T5OzL5Xb3WFsk-voXoluJgf3vQ9rxl5WH86sfg,8781 +twilio/rest/numbers/v1/porting_port_in.py,sha256=rhhNhTNddlZCZ0lkLcYbsEAZmCO7OELnTjUc38Su9LA,6577 +twilio/rest/numbers/v1/porting_port_in_fetch.py,sha256=HdvwiJG5Dqh2-l-WWCCSQ-Dub-VZoJ0pTEaQUv-pocs,8163 +twilio/rest/numbers/v1/porting_port_in_phone_number.py,sha256=xs_Awf-AATFyzMKOfPy7D_Q96Ujn0Px98ewR0DXMceA,4197 +twilio/rest/numbers/v1/porting_portability.py,sha256=4BBioMEBS0XDuEC6gLWv4B6yxbmLKTLEO5WI7-DP8uc,8816 +twilio/rest/numbers/v2/__init__.py,sha256=7yY6txsbqfO1Ol3YT2cMMQFAUmu7SNWs2JgLEM4P9bA,2653 +twilio/rest/numbers/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v2/__pycache__/bulk_hosted_number_order.cpython-310.pyc,, +twilio/rest/numbers/v2/__pycache__/hosted_number_order.cpython-310.pyc,, +twilio/rest/numbers/v2/authorization_document/__init__.py,sha256=bHcHU1TU9Iufowp2vDnDGF5_JeeXi_uvTNpM1kXxaxA,25375 +twilio/rest/numbers/v2/authorization_document/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v2/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc,, +twilio/rest/numbers/v2/authorization_document/dependent_hosted_number_order.py,sha256=ohKyn34OBbFRrtppBpNMUuZNP11D5-GBuTtu8h_YzUM,22250 +twilio/rest/numbers/v2/bulk_hosted_number_order.py,sha256=SdT1TjJI_Kjxkv68AdXuEtgy4DfB2Tt5oQxnFud3OYQ,10792 +twilio/rest/numbers/v2/hosted_number_order.py,sha256=eoe0HQArYxY6FdLn_DjIJBzHnV4jeUazqhbeUObR9_Q,35453 +twilio/rest/numbers/v2/regulatory_compliance/__init__.py,sha256=om1Yd8YuZ5T0YX0OPdwl__lmeV3rf1uQI9a6VXnlPFo,3690 +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user_type.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/regulation.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document_type.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/__init__.py,sha256=NEYpfKnNskMJnOnQue-8XBeO-R96BCRghbR2MHXIYjg,47565 +twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/bundle_copy.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/evaluation.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/item_assignment.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/replace_items.cpython-310.pyc,, +twilio/rest/numbers/v2/regulatory_compliance/bundle/bundle_copy.py,sha256=eYUrNRgTIAAn6F7b4cJ9ff403AOt97gFBCpMv86-Jmk,13884 +twilio/rest/numbers/v2/regulatory_compliance/bundle/evaluation.py,sha256=xW2c-TyfNhZWlLQrybaX-kjrUtEBPqhn9M3K7LHjQcg,16200 +twilio/rest/numbers/v2/regulatory_compliance/bundle/item_assignment.py,sha256=IvHTKh1W8cMm6GaekXKeUwN4PYuvbKIY3gYUqfEVBeo,17836 +twilio/rest/numbers/v2/regulatory_compliance/bundle/replace_items.py,sha256=YY4SYS9mP-1Pyy-mO53x1dHGRgYtKxa-M_AS4-aaJZg,5668 +twilio/rest/numbers/v2/regulatory_compliance/end_user.py,sha256=lO-6xtWRK0fEuRC20ttLqU_ZQpSJVcqf93UWAbzuPYI,20505 +twilio/rest/numbers/v2/regulatory_compliance/end_user_type.py,sha256=w3jMd2XHOeK3vZPR-LrFYbGDJAHyLdrRWWiaK4Az3wc,14152 +twilio/rest/numbers/v2/regulatory_compliance/regulation.py,sha256=4WLu0-NbHzSY0XjqxJ3OWG33gSQHJRQjaek6Smxzoek,18539 +twilio/rest/numbers/v2/regulatory_compliance/supporting_document.py,sha256=z6Nw1fwUXgC8t0Ody64hphdw_Tnh59_OVOTs3RIWW6g,22412 +twilio/rest/numbers/v2/regulatory_compliance/supporting_document_type.py,sha256=vh2xP4m3RY1WKqCqR9eXsy7z75KngLLRNuWuxrig5HI,14800 +twilio/rest/oauth/OauthBase.py,sha256=osnf61tFtaKzvSRec80nclNAGL2hjobh7cCzzM7yNnA,1190 +twilio/rest/oauth/__init__.py,sha256=rEEQw885yV4hMbPvh3UCnaq2dvTLzP7LN70rAH_MzTI,1578 +twilio/rest/oauth/__pycache__/OauthBase.cpython-310.pyc,, +twilio/rest/oauth/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/oauth/v1/__init__.py,sha256=_O7z9N65ZEXjX5DAiMDvj-Sw4rNHP4JvxFhx1_yhRCg,1536 +twilio/rest/oauth/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/oauth/v1/__pycache__/authorize.cpython-310.pyc,, +twilio/rest/oauth/v1/__pycache__/token.cpython-310.pyc,, +twilio/rest/oauth/v1/authorize.py,sha256=nqPEDHn9octzkb66eWR8O_yKXMu6BIrnv1cervwhNqQ,4011 +twilio/rest/oauth/v1/token.py,sha256=XribWKiIbLJbNx2rVrL42c8lZ1NRW8uQEafmzp7GYZ0,5656 +twilio/rest/preview/PreviewBase.py,sha256=4YnmGU2_ZKfDYcmZcCVK9_PgzEy0RSwCxLbaerN2hd0,2729 +twilio/rest/preview/__init__.py,sha256=ZbwcHlKZZjcVhOUmxnaRniyOV-m4eeGXj_53Pls_wRA,3203 +twilio/rest/preview/__pycache__/PreviewBase.cpython-310.pyc,, +twilio/rest/preview/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/__init__.py,sha256=seknAQpzbWNXceJyolz23lrf7g1Ewmuot0d6M_zzQoU,1330 +twilio/rest/preview/deployed_devices/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/__init__.py,sha256=LGMq3G6xiZy7q7YB-nKXAv0gR86ct8f2RUX9j2Efs78,22445 +twilio/rest/preview/deployed_devices/fleet/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/__pycache__/certificate.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/__pycache__/deployment.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/__pycache__/device.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/__pycache__/key.cpython-310.pyc,, +twilio/rest/preview/deployed_devices/fleet/certificate.py,sha256=cMwV9lAARQGz7W0Umlgd95y-1wYy_wkubuCN-s5oa8M,24121 +twilio/rest/preview/deployed_devices/fleet/deployment.py,sha256=vp8ectC6BfKkQWEZzsqHMlKVg1GHizIR5P-aebGUJGw,22166 +twilio/rest/preview/deployed_devices/fleet/device.py,sha256=HS7hZ5PDhX7jNcMO6pp5mjtymdj4VCi47l3sjrqwXbU,26786 +twilio/rest/preview/deployed_devices/fleet/key.py,sha256=1Lqod_0IIKRCmDmAuqOcI6kmOzBMejcmzEmpjRryuAY,22729 +twilio/rest/preview/hosted_numbers/__init__.py,sha256=Q0CYZWP0LWjme7cyzlNZFMMI6BDqN0Fa5uvNtGXcVq8,1893 +twilio/rest/preview/hosted_numbers/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/hosted_numbers/__pycache__/hosted_number_order.cpython-310.pyc,, +twilio/rest/preview/hosted_numbers/authorization_document/__init__.py,sha256=CLCumAt5tzTJcRuUSPeuUZJwZ651sthXGLhfMZ3F-68,32383 +twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc,, +twilio/rest/preview/hosted_numbers/authorization_document/dependent_hosted_number_order.py,sha256=jVBMUPpoQmOUgaCwxCq3D10se0drdZuPe4Sd0Y1LcEI,25081 +twilio/rest/preview/hosted_numbers/hosted_number_order.py,sha256=ytnILnq-JCsSB6-pACvCFpr_7u9H_0RPKcjpQGQl4lI,48458 +twilio/rest/preview/marketplace/__init__.py,sha256=Z1ULxHOw4TLnAX6wXweELqxekmXLlhysuzjz34rOc9A,1776 +twilio/rest/preview/marketplace/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/marketplace/available_add_on/__init__.py,sha256=6unqqABf_6p_hEEi3p-q_kxK4rlxIcZ2GcyH7EPTN40,15068 +twilio/rest/preview/marketplace/available_add_on/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/marketplace/available_add_on/__pycache__/available_add_on_extension.cpython-310.pyc,, +twilio/rest/preview/marketplace/available_add_on/available_add_on_extension.py,sha256=CdtaebewLC4zZNRPiQZMveoCNkDQ8QQHl_5npW0ligA,16164 +twilio/rest/preview/marketplace/installed_add_on/__init__.py,sha256=2kRRAp68IO4HHFdahOESsRqEleADkpaO3JLYiHZSmfM,23430 +twilio/rest/preview/marketplace/installed_add_on/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/marketplace/installed_add_on/__pycache__/installed_add_on_extension.cpython-310.pyc,, +twilio/rest/preview/marketplace/installed_add_on/installed_add_on_extension.py,sha256=srUtaZ3_TJYxyElOJcByJvGDDp39ATFs1WUyjZ3fJ1c,18612 +twilio/rest/preview/sync/__init__.py,sha256=VlsQ7eL1CDUyVPRSJ3KtHXewVJ3EYe7XsblMubGPbyA,1294 +twilio/rest/preview/sync/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/sync/service/__init__.py,sha256=awqdSyxc_QnRX3OufaSpozpoP4mGP1w7W20ZzZ-PZGI,22668 +twilio/rest/preview/sync/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/sync/service/document/__init__.py,sha256=UN2trZCE9HRoyz27FKLBTPNEGnHh7RYfViF51EgHm54,20550 +twilio/rest/preview/sync/service/document/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/sync/service/document/__pycache__/document_permission.cpython-310.pyc,, +twilio/rest/preview/sync/service/document/document_permission.py,sha256=0PDU3Z7bJZJiTB-v4Gxy6BlEHZseQUyKrhNEShpcBeo,21641 +twilio/rest/preview/sync/service/sync_list/__init__.py,sha256=MxzscD9KvYtT4Li6jWqytrKepVxWf8pLgux8cIJJLX4,18342 +twilio/rest/preview/sync/service/sync_list/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_list/sync_list_item.py,sha256=GI_WR8AEhb_YbULpZu0PY_Z4-JGRZOB7i9AGeQ4mBRU,24179 +twilio/rest/preview/sync/service/sync_list/sync_list_permission.py,sha256=_xh0gxh6khE5TwrSERH5EFnAYLb1sXGmIJEjkHIiorg,21583 +twilio/rest/preview/sync/service/sync_map/__init__.py,sha256=3gf9q4AHAkXueVIcB9azNnlKlvtWAZ1zT5jZYbEpdiA,18220 +twilio/rest/preview/sync/service/sync_map/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc,, +twilio/rest/preview/sync/service/sync_map/sync_map_item.py,sha256=dVJ2vNUzLQD9k0cx6HGWO5WfNY0i-jzJ5umSYhKR1Ys,24068 +twilio/rest/preview/sync/service/sync_map/sync_map_permission.py,sha256=LI6aTLiD7L7rnlt2ZmWLlT4cUUWZuAdt20cAdcenfaU,21436 +twilio/rest/preview/wireless/__init__.py,sha256=ZlKINNHwHiqAhehhMb7g_jGDFUNK6XWGzlhA8XdGfZY,1850 +twilio/rest/preview/wireless/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/wireless/__pycache__/command.cpython-310.pyc,, +twilio/rest/preview/wireless/__pycache__/rate_plan.cpython-310.pyc,, +twilio/rest/preview/wireless/command.py,sha256=0aHoU-GoG5cT1aLriny-Ctk9Jy02rv7Urh6SBrl-77E,18914 +twilio/rest/preview/wireless/rate_plan.py,sha256=-4si-tyYrXJZCOa6LTKypp1Ah91Xs_ZXmz4BSXS848s,22050 +twilio/rest/preview/wireless/sim/__init__.py,sha256=J5WI7AXFLQyMsLTH3JdcpjRlzlR2yEeRWJAbUfnMeoY,28868 +twilio/rest/preview/wireless/sim/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview/wireless/sim/__pycache__/usage.cpython-310.pyc,, +twilio/rest/preview/wireless/sim/usage.py,sha256=Zv1hKeWqU3GsL7wyKQpUrxb1i-dB8minG0ziu5_LVnY,6592 +twilio/rest/preview_messaging/PreviewMessagingBase.py,sha256=QGixbwl0mUAo1YqyS4alymmBFBOgciyQ7tk7LWxHp-c,1269 +twilio/rest/preview_messaging/__init__.py,sha256=vJxmm2jelILjRRIY9WmstB_qa0OwBgqx-9ZBfGPac1o,442 +twilio/rest/preview_messaging/__pycache__/PreviewMessagingBase.cpython-310.pyc,, +twilio/rest/preview_messaging/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview_messaging/v1/__init__.py,sha256=VkKUy7A2FGVjR09MYaDBsuHKtI9eBRVpSkp0dSisDyA,1800 +twilio/rest/preview_messaging/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/preview_messaging/v1/__pycache__/broadcast.cpython-310.pyc,, +twilio/rest/preview_messaging/v1/__pycache__/message.cpython-310.pyc,, +twilio/rest/preview_messaging/v1/broadcast.py,sha256=ppRFaTUuK39_lUTeVa34jRXogdbF7iNoHrOohgPV_HI,4238 +twilio/rest/preview_messaging/v1/message.py,sha256=ZDiml6yrilCB2CidphVughKFrO9POcVsSMDicOMD0pk,13154 +twilio/rest/pricing/PricingBase.py,sha256=NLCOp1P4eV2ZlQikfRsLHd1zUZyqms5HRsl4Y54QtZ8,1469 +twilio/rest/pricing/__init__.py,sha256=ge3TqYJHEMFblpBnfC9X4FW0d0SsO7I5b4qKGY-jkVU,1569 +twilio/rest/pricing/__pycache__/PricingBase.cpython-310.pyc,, +twilio/rest/pricing/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v1/__init__.py,sha256=CurnOGdO6y0wIWCGSGxgxt0YdszryeMAmdRbj7Ooeog,1868 +twilio/rest/pricing/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v1/messaging/__init__.py,sha256=rSOhH2QMvljfAGkFDYwomtLmOqPZwBjbOkcUfRbqXPA,1443 +twilio/rest/pricing/v1/messaging/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v1/messaging/__pycache__/country.cpython-310.pyc,, +twilio/rest/pricing/v1/messaging/country.py,sha256=egPvBWpdwsjv3nfh1XrpwpQT6qAoYxlcrG3E-7s6DV8,14531 +twilio/rest/pricing/v1/phone_number/__init__.py,sha256=Y5i7NuFEchDtGXMSoTkFMonR8iyMVBzZM8Ud8xXW5ik,1455 +twilio/rest/pricing/v1/phone_number/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v1/phone_number/__pycache__/country.cpython-310.pyc,, +twilio/rest/pricing/v1/phone_number/country.py,sha256=-jNu0xMNWMzfr5wxWxhyUXmCftcEwJRKwzu4Vezx76w,14124 +twilio/rest/pricing/v1/voice/__init__.py,sha256=I_2ql8yNyelIDbO6xLw_YZOtvdVS4YsJ1_xA5inn4dI,1753 +twilio/rest/pricing/v1/voice/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v1/voice/__pycache__/country.cpython-310.pyc,, +twilio/rest/pricing/v1/voice/__pycache__/number.cpython-310.pyc,, +twilio/rest/pricing/v1/voice/country.py,sha256=0Osj6JDCL6AFvpfBWSWvlH0xEnh6LU3S-VkXdgOoV5k,14417 +twilio/rest/pricing/v1/voice/number.py,sha256=eO4DCFFjjHpy0ahnhSNFWvJXex5Q5CUb366M-BhoDYU,5701 +twilio/rest/pricing/v2/__init__.py,sha256=lt22Da-7bXFid4KEGWNLc5_R4u5Is6fDuYCoVa1wjj0,1802 +twilio/rest/pricing/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v2/__pycache__/country.cpython-310.pyc,, +twilio/rest/pricing/v2/__pycache__/number.cpython-310.pyc,, +twilio/rest/pricing/v2/country.py,sha256=eXm1AbUtLO3kFNPyXFHKqbEDiJDEMHI5kDes-iljniU,14471 +twilio/rest/pricing/v2/number.py,sha256=vqQHJjE9x_vOGY7GdfhmLYi_fxlv59jpM9zVTKFIzDw,8966 +twilio/rest/pricing/v2/voice/__init__.py,sha256=EV0LqxLaOB0PgH7E182h0InT6g9wmJsoPSBC5e6WSVE,1753 +twilio/rest/pricing/v2/voice/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/pricing/v2/voice/__pycache__/country.cpython-310.pyc,, +twilio/rest/pricing/v2/voice/__pycache__/number.cpython-310.pyc,, +twilio/rest/pricing/v2/voice/country.py,sha256=3FhNrc4NXkI-VaRbth-yZSOJTaBo6EwRf0d4PuVr6Yk,14447 +twilio/rest/pricing/v2/voice/number.py,sha256=rsD4p8SN3n7nRLXnm4xh1T1L21s4kDE44MYol_HLwe8,9040 +twilio/rest/proxy/ProxyBase.py,sha256=U3nXl8igDx1PLplE9GdldvQ7-_1ZofMO8kPMoptnTPY,1190 +twilio/rest/proxy/__init__.py,sha256=t3suaVnvsgCHvrHOkPBshzKqaK0xZ0OAfoBvPsgS_MA,387 +twilio/rest/proxy/__pycache__/ProxyBase.cpython-310.pyc,, +twilio/rest/proxy/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/proxy/v1/__init__.py,sha256=K_YLKgkAp-_QiAKKVb_RKJF1Bk2ge_6Z16uFrOqdq1Q,1274 +twilio/rest/proxy/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/proxy/v1/service/__init__.py,sha256=SDLy4Ge-oXenM3V0Vp_TWIQePjqpFcSA-0fkwLAJ52Y,38168 +twilio/rest/proxy/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/proxy/v1/service/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/proxy/v1/service/__pycache__/short_code.cpython-310.pyc,, +twilio/rest/proxy/v1/service/phone_number.py,sha256=FT_BtysGu7KKTb-foqJCxE66eowFHseINypbQnZ7Tjg,23884 +twilio/rest/proxy/v1/service/session/__init__.py,sha256=J0CErLKemcptJjbGa30GAUcYCd7go9-n-Zc3VT8D-VU,27772 +twilio/rest/proxy/v1/service/session/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/proxy/v1/service/session/__pycache__/interaction.cpython-310.pyc,, +twilio/rest/proxy/v1/service/session/interaction.py,sha256=89yjRacFODPKNyYmmViUztyLOv7zEHO3xTqv7INPmrM,21124 +twilio/rest/proxy/v1/service/session/participant/__init__.py,sha256=r_RymYxfqZ08Lvk1-uDAcUcxMjPj4iH_16hJHDBpJTE,22768 +twilio/rest/proxy/v1/service/session/participant/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/proxy/v1/service/session/participant/__pycache__/message_interaction.cpython-310.pyc,, +twilio/rest/proxy/v1/service/session/participant/message_interaction.py,sha256=nHVWNAPctVhXxE-55cAnBF8HbKhRIWOJHe_tiEtPJyw,23247 +twilio/rest/proxy/v1/service/short_code.py,sha256=FRbNg55q88GuJWLhWF29epqnuGEM5u5cIf_5gBFZJE4,21439 +twilio/rest/routes/RoutesBase.py,sha256=Wd7ZCiv13Jkec4cmQTcAEWgftoIU1m57bZ-qkzoriXg,1197 +twilio/rest/routes/__init__.py,sha256=2c-YFuPc4-lbnbloGWO0HBglvgusSEin0PqD7MhDKi4,1003 +twilio/rest/routes/__pycache__/RoutesBase.cpython-310.pyc,, +twilio/rest/routes/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/routes/v2/__init__.py,sha256=yOGIyQHTtHeDgd-yu4LSv5AcLa2KT__TcSmWUYploSo,1877 +twilio/rest/routes/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/routes/v2/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/routes/v2/__pycache__/sip_domain.cpython-310.pyc,, +twilio/rest/routes/v2/__pycache__/trunk.cpython-310.pyc,, +twilio/rest/routes/v2/phone_number.py,sha256=hytmXeYSg6HOyJoZ_leNyA6eTz2JHlSNGhIy4k24Nzs,9727 +twilio/rest/routes/v2/sip_domain.py,sha256=o_tv-jUTjrDugm6Ws-Ho-jehhzF5YwuKVM5dn5BFg60,8319 +twilio/rest/routes/v2/trunk.py,sha256=-YVsQFvvUkxL2dU8Ek7AS_JFfW2UD5UdtkPIo4-TxVA,9536 +twilio/rest/serverless/ServerlessBase.py,sha256=tkHMV_J34VboZ9KpnPZGKoWqoRvQmi7jUKNgGbQif_Y,1225 +twilio/rest/serverless/__init__.py,sha256=RSch0EvaZC7vInV-DXpUwtaitRhC1dpLXllzK-keHIQ,417 +twilio/rest/serverless/__pycache__/ServerlessBase.cpython-310.pyc,, +twilio/rest/serverless/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/__init__.py,sha256=FE26OpLcLc2D4bdUE9-madIxREcrRBOk8jPT-H99_zw,1299 +twilio/rest/serverless/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/__init__.py,sha256=VHlTZcEjsuYQkRz7ENvljoWCchmi7fFHTI7eRvfrNvA,25756 +twilio/rest/serverless/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/asset/__init__.py,sha256=m6B72n6Mc4hz9NxCS7vtNSdTwCZgyQhFpx04w33GF7Q,20585 +twilio/rest/serverless/v1/service/asset/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/asset/__pycache__/asset_version.cpython-310.pyc,, +twilio/rest/serverless/v1/service/asset/asset_version.py,sha256=gCi9geTML0Chu7gfz-Ir_Mr_uo8pwJ-nT8o6YC4WaUI,16717 +twilio/rest/serverless/v1/service/build/__init__.py,sha256=GO56UbLoKp9iwkZqm0HygR77Yn1VU2X_Z4V9w8q-Fi4,20312 +twilio/rest/serverless/v1/service/build/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/build/__pycache__/build_status.cpython-310.pyc,, +twilio/rest/serverless/v1/service/build/build_status.py,sha256=mXWm4c-ierr3KGfjo7pTQiVv4tO3Qy_J1iXj6k2IZXE,6407 +twilio/rest/serverless/v1/service/environment/__init__.py,sha256=U3R_jqr6uYdYQKSGOUY-FqjDctc155NVH5Jpy2YnjBc,21012 +twilio/rest/serverless/v1/service/environment/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/environment/__pycache__/deployment.cpython-310.pyc,, +twilio/rest/serverless/v1/service/environment/__pycache__/log.cpython-310.pyc,, +twilio/rest/serverless/v1/service/environment/__pycache__/variable.cpython-310.pyc,, +twilio/rest/serverless/v1/service/environment/deployment.py,sha256=4tP5qBsZsflCrQYKK2dMBjNa9gzzYhmeGHwvghMW2rU,18052 +twilio/rest/serverless/v1/service/environment/log.py,sha256=Z6Y3Mmaqmm6G6CD-1IXGkec2jWcRoeHAerq0jz6Mm5E,20929 +twilio/rest/serverless/v1/service/environment/variable.py,sha256=EYhwKqbGLBgvQApa2UPQSN-W5QjqH1LA4hAInNazVrA,22601 +twilio/rest/serverless/v1/service/function/__init__.py,sha256=OMuTPn84ke6FJE04AUFpdFhc6KLZl_Tt4td9DhzmjqE,20931 +twilio/rest/serverless/v1/service/function/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/function/function_version/__init__.py,sha256=kg3sGMdmtlcQnUv7NBtwZzdR2NUu_eIttgCPSEvY2CM,18096 +twilio/rest/serverless/v1/service/function/function_version/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/serverless/v1/service/function/function_version/__pycache__/function_version_content.cpython-310.pyc,, +twilio/rest/serverless/v1/service/function/function_version/function_version_content.py,sha256=_oSZXdMuTzE00KjjHQiwsLKFvLJUh3Rpl9Y07aAK3go,7736 +twilio/rest/studio/StudioBase.py,sha256=Ir8Bf4wI_qF2HPpL59i94svqiX_FB0g6Y3Tw02ZA4lQ,1460 +twilio/rest/studio/__init__.py,sha256=VF97OQEqJW5-ycSz4oqnGlxyvDEo-Hzxf0GZQzgv3ts,694 +twilio/rest/studio/__pycache__/StudioBase.cpython-310.pyc,, +twilio/rest/studio/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/__init__.py,sha256=x63ZfUydLZEcampmW0D3vaSqnpCc2mMGH5zg4uckLTA,1249 +twilio/rest/studio/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/__init__.py,sha256=x-yqdcvcpb3ktdewwszMtO7jOAj23UqCGlF_jSSg4tQ,16407 +twilio/rest/studio/v1/flow/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/engagement/__init__.py,sha256=qOWONDAGsQdT_MoMJ-a0D-dxl7dNJH8QUM70ORXo9c4,21371 +twilio/rest/studio/v1/flow/engagement/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/engagement/__pycache__/engagement_context.cpython-310.pyc,, +twilio/rest/studio/v1/flow/engagement/engagement_context.py,sha256=wT-fzrDvSh2XOW8BOFGxE3PUiR3jex_Y7vB3I67mf7g,6720 +twilio/rest/studio/v1/flow/engagement/step/__init__.py,sha256=A7TDaVwIHFtI7xfMpzjoYBGSxUtBJRlijei0Q0bJT0w,17220 +twilio/rest/studio/v1/flow/engagement/step/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/engagement/step/__pycache__/step_context.cpython-310.pyc,, +twilio/rest/studio/v1/flow/engagement/step/step_context.py,sha256=XM0KFy4FeSGPBtIXdAjKPfgpye9_dIdE1dezUYkhsUA,7395 +twilio/rest/studio/v1/flow/execution/__init__.py,sha256=Ewvc_3la4HpnauDsbBfjyqIuGN0EjOx6Dz9wDiwB9Wk,27275 +twilio/rest/studio/v1/flow/execution/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/execution/__pycache__/execution_context.cpython-310.pyc,, +twilio/rest/studio/v1/flow/execution/execution_context.py,sha256=H9HsfEzk50UijaRbs8e64gB1UgM_9upHR0V9-3ofQTY,6877 +twilio/rest/studio/v1/flow/execution/execution_step/__init__.py,sha256=paeGMr7qyGkOO1RlswgM7VZbHEuSnjYpP4cn2DLARzw,17889 +twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc,, +twilio/rest/studio/v1/flow/execution/execution_step/execution_step_context.py,sha256=MWCNHKvIg3heolUteYm1LnQ_N8jYOo5Wi4NYBOcKdR0,7716 +twilio/rest/studio/v2/__init__.py,sha256=W2t8U-5spMVgebzQZzLAkN8fOhceKLAPVTfJ3EWCXCo,1573 +twilio/rest/studio/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v2/__pycache__/flow_validate.cpython-310.pyc,, +twilio/rest/studio/v2/flow/__init__.py,sha256=GDcihMJf9c8_fTxsElwcj3ESmh-TfssYBZphE2ClWMY,23685 +twilio/rest/studio/v2/flow/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v2/flow/__pycache__/flow_revision.cpython-310.pyc,, +twilio/rest/studio/v2/flow/__pycache__/flow_test_user.cpython-310.pyc,, +twilio/rest/studio/v2/flow/execution/__init__.py,sha256=8fU7JbbKKh-IMPbf2cEvHB5paGphacjpAMLplauldRs,27159 +twilio/rest/studio/v2/flow/execution/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v2/flow/execution/__pycache__/execution_context.cpython-310.pyc,, +twilio/rest/studio/v2/flow/execution/execution_context.py,sha256=LjUAGnJAT9cQfj88HYK4SjjXY7NZA6QaM-6FKLFzUe4,6877 +twilio/rest/studio/v2/flow/execution/execution_step/__init__.py,sha256=FAO5t7fpidMd9CR__edborSIPxaHuiccQonDHx1v36o,17889 +twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc,, +twilio/rest/studio/v2/flow/execution/execution_step/execution_step_context.py,sha256=U2ETU9YRpLNwPQP3CvShwXFdpZMEobO456FLLvgMlFg,7716 +twilio/rest/studio/v2/flow/flow_revision.py,sha256=phGrDgaMO9M4UE8WrPb64MiZQBc6zLxmnem30AtDZKU,16073 +twilio/rest/studio/v2/flow/flow_test_user.py,sha256=6itldEfrpj1gsdUo-KcZtrWqJvFzb3hR2aA_muS-aPU,7422 +twilio/rest/studio/v2/flow_validate.py,sha256=fBcvxsYO2IbNSdYogiMdwkSEZKMTXe5eF3Ue10deEX4,3895 +twilio/rest/supersim/SupersimBase.py,sha256=tzDOW6QQb0M_HEBpS5SmrK9AN0FWPkMKxNRgN9bGoxQ,1211 +twilio/rest/supersim/__init__.py,sha256=dRLKZs5p-3LCF1VlFz1_jV9r338yrTYW7kzraj64vuc,2908 +twilio/rest/supersim/__pycache__/SupersimBase.cpython-310.pyc,, +twilio/rest/supersim/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/supersim/v1/__init__.py,sha256=ExvMot1Mt7LpQfp5mlZgaOTf-yVEfZo9hL-lIgPc03Y,3801 +twilio/rest/supersim/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/esim_profile.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/fleet.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/ip_command.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/network.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/settings_update.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/sms_command.cpython-310.pyc,, +twilio/rest/supersim/v1/__pycache__/usage_record.cpython-310.pyc,, +twilio/rest/supersim/v1/esim_profile.py,sha256=icddbemFkq05B_45eb2OhoxaSXixSwNEdYk1zp9HLD8,23725 +twilio/rest/supersim/v1/fleet.py,sha256=JuHZmT2zEpUvlJqH8KYAEtI3YA_K6xjmmS8MvjbWSgY,35353 +twilio/rest/supersim/v1/ip_command.py,sha256=OWKW60Mth9GczQuSYxWJbDZ0N3levapnOmCgFc3YhEg,26669 +twilio/rest/supersim/v1/network.py,sha256=aA07YMhdFuCAPLLLkyjmvPw36aQVyCOg8cekd7v0kf4,17594 +twilio/rest/supersim/v1/network_access_profile/__init__.py,sha256=Ss75Sq1C6w3xCkwNaxyfbWHgxAwfjgaB9oQlXlq2TIM,20074 +twilio/rest/supersim/v1/network_access_profile/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/supersim/v1/network_access_profile/__pycache__/network_access_profile_network.cpython-310.pyc,, +twilio/rest/supersim/v1/network_access_profile/network_access_profile_network.py,sha256=fYqMoNlZxThs8Mf0qVwcnK3BbMjo0JHka9eagLhIYCo,19373 +twilio/rest/supersim/v1/settings_update.py,sha256=8AN7a9dzvIK9_yyzLJNxCLdMJPP3X7li15f4cC3XfTw,14219 +twilio/rest/supersim/v1/sim/__init__.py,sha256=POfFv7b7Mu2Gou__7Hm1kBGXpnsdJNJmTNZNF14Rpbs,28737 +twilio/rest/supersim/v1/sim/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/supersim/v1/sim/__pycache__/billing_period.cpython-310.pyc,, +twilio/rest/supersim/v1/sim/__pycache__/sim_ip_address.cpython-310.pyc,, +twilio/rest/supersim/v1/sim/billing_period.py,sha256=2xj_kMMcXcoN8YsC8vrqFK-fkGZBkGk7PCd6EVOxFuw,12032 +twilio/rest/supersim/v1/sim/sim_ip_address.py,sha256=o3dE3v8BvubybgUtCP2oQysUwe1J1X_ZWBODCMHwhA4,10629 +twilio/rest/supersim/v1/sms_command.py,sha256=C82j1OZ3x45bzC3toks6zndweQoncvm8F7TrO8GqXoM,23006 +twilio/rest/supersim/v1/usage_record.py,sha256=K5yDwBXK7NVFKidkBJ8B1KcwPsV6c8ftjdD6tSOju34,26843 +twilio/rest/sync/SyncBase.py,sha256=qfnPzbBdwYLKn45ZV51XawiTF0J9LVVp7WoiWeNNuF8,1183 +twilio/rest/sync/__init__.py,sha256=IV4mK6fA37ohGhjUmJXQMoBrLYhbKu4rExsU7ZhBemI,381 +twilio/rest/sync/__pycache__/SyncBase.cpython-310.pyc,, +twilio/rest/sync/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/__init__.py,sha256=lo8inAzFDENbjugimex6jV8cKsaekYSSdhfnVb-jK08,1269 +twilio/rest/sync/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/__init__.py,sha256=lFV4bTiXI0EaOwn6yuRkdYN_hcANU5B2MnOwnU6d4QQ,37557 +twilio/rest/sync/v1/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/document/__init__.py,sha256=kZtCua5-4tfRaASu0wYd6Oe7N4S72IPhca-W7WIg0BU,25376 +twilio/rest/sync/v1/service/document/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/document/__pycache__/document_permission.cpython-310.pyc,, +twilio/rest/sync/v1/service/document/document_permission.py,sha256=CbNha-Pwp2rAcXpyinvbVR-xd68dWqBACqAlZBydXdc,22008 +twilio/rest/sync/v1/service/sync_list/__init__.py,sha256=SYs6aH6NeTfUE3v4Jf-pnN17XzQsLaThxWxjv6ep8P0,25201 +twilio/rest/sync/v1/service/sync_list/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_list/sync_list_item.py,sha256=6nNYjiCpxd0LowhYZmz58s5ICA62G1gicGBWwfkRTlw,36676 +twilio/rest/sync/v1/service/sync_list/sync_list_permission.py,sha256=Q2ST0JmhyVlQ3IgRYJbTkx7y4dsDsixFNnvXka_nU9Q,22012 +twilio/rest/sync/v1/service/sync_map/__init__.py,sha256=GJqqwRijhoXTdQq3Wr5m_PYKYcgCCNjNzRZDY0VhIt4,24889 +twilio/rest/sync/v1/service/sync_map/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_map/sync_map_item.py,sha256=R-NgZ1hGvv8QN-_cAYpebg-swXECWhIrW8ueokJZ2b0,37235 +twilio/rest/sync/v1/service/sync_map/sync_map_permission.py,sha256=vJOysSkZtOXoMhWj0-gKeccNN4fCzaA3HuM22oajSGY,21940 +twilio/rest/sync/v1/service/sync_stream/__init__.py,sha256=WV_Dh9MVyugAGqtU-07kUmFaSYwdsPrS5nn6NKrMFvA,22902 +twilio/rest/sync/v1/service/sync_stream/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_stream/__pycache__/stream_message.cpython-310.pyc,, +twilio/rest/sync/v1/service/sync_stream/stream_message.py,sha256=910eu_OT1S5xYJUpoEyCHgrSElFbbsQ6PpgMZEiTg5o,4316 +twilio/rest/taskrouter/TaskrouterBase.py,sha256=0pduWi9gHRMDnWejrHNLhL1UFtnTbFTD7K4_uBvk698,1225 +twilio/rest/taskrouter/__init__.py,sha256=LsvpBhg8Cg-tYPzdacb1bKJ9fEAwRBd6sdH41_s-CGA,431 +twilio/rest/taskrouter/__pycache__/TaskrouterBase.cpython-310.pyc,, +twilio/rest/taskrouter/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/__init__.py,sha256=dgBlDHuEdkXOOBusvaS3DPEdV6KpgNdnRKPxRPxmvsc,1319 +twilio/rest/taskrouter/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__init__.py,sha256=fJwP1JyyT3eEqXAqNtgIaN4GHNpSgd42tl51Z6p3Fw8,44586 +twilio/rest/taskrouter/v1/workspace/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/activity.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/event.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/task_channel.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_cumulative_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_real_time_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/activity.py,sha256=msvMo3blVM7MS2FW7UgOif23eFYEgA8gsRQwQvzHqKA,26058 +twilio/rest/taskrouter/v1/workspace/event.py,sha256=QOfAADWepoB2XW8trGUGAZrPQvk16Su0-g-4a2Utd_M,32777 +twilio/rest/taskrouter/v1/workspace/task/__init__.py,sha256=jo8dhhdHznwY8e7Zz0q0zonfvfF0Y5LU2Baq1mRDzMg,54579 +twilio/rest/taskrouter/v1/workspace/task/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task/__pycache__/reservation.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task/reservation.py,sha256=TyQVgVewDh6wiREiKkufjSxhW8-PKK_Iv-ZDZkWjcE8,81514 +twilio/rest/taskrouter/v1/workspace/task_channel.py,sha256=yKJTCBhyoE-289bhx_TBiQnhnPRPOhfd9A3W-udUqVg,23752 +twilio/rest/taskrouter/v1/workspace/task_queue/__init__.py,sha256=5xWsla8BAoHzxnKxStztSObfYbHLor_ObdxiAVgKPRg,39929 +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_bulk_real_time_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_cumulative_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_real_time_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queues_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_bulk_real_time_statistics.py,sha256=pYT3096-XHBW-sUeYF8WBJUAaw6EWKYRseXoQYPPu5M,5273 +twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_cumulative_statistics.py,sha256=ihJp6RrCIjgtwLjxU1g-GQK6kWL4BEvnAip1LnMaxNg,18320 +twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_real_time_statistics.py,sha256=Uzc7jopjonr0TrJUQO-vHu82l-nln0NEAaaxwcRuC08,10994 +twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_statistics.py,sha256=VtAbQ8ifnsrfI3wrihonwmC3selh5bWE1M1It7RMF2o,13380 +twilio/rest/taskrouter/v1/workspace/task_queue/task_queues_statistics.py,sha256=dAOQmLwwaBI0kZSxD8taho2SPKsGlE6qVUqmHQFeP9s,21315 +twilio/rest/taskrouter/v1/workspace/worker/__init__.py,sha256=Gj-heFKK4voc_xdLTkCaaYQ0rb-cmDx6YDkLpL5Nc-k,44060 +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/reservation.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_channel.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_cumulative_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_real_time_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/worker/reservation.py,sha256=qyuSORLzjjz20RJD1jiE4GD8xVk9IGRlG8CEJqO_EZM,79296 +twilio/rest/taskrouter/v1/workspace/worker/worker_channel.py,sha256=drxG9k7jJ2gijyaoIpnvhPnGd2UWxZeNWy8IzcDUbQo,22161 +twilio/rest/taskrouter/v1/workspace/worker/worker_statistics.py,sha256=dioWeEH1gHuDYLFEw9f41KVxu0_T8G68553htpXvGvs,11730 +twilio/rest/taskrouter/v1/workspace/worker/workers_cumulative_statistics.py,sha256=vwtCN1fpF8Qcye8t1JxIV1j9-Uv88YDEkkvongCTYrU,13252 +twilio/rest/taskrouter/v1/workspace/worker/workers_real_time_statistics.py,sha256=560tzJSqkhQ9YNTTBvFYkh221lxrJ7eOhaLKbMulVP4,7895 +twilio/rest/taskrouter/v1/workspace/worker/workers_statistics.py,sha256=jusUn4HHb51uwjwQf56RXikmCpFIt4T0FzWKi-vEmn8,13545 +twilio/rest/taskrouter/v1/workspace/workflow/__init__.py,sha256=g79oKzEDbTm36Rbjf2eQdK6OBcLT7xJWR4Sn04R2vS8,34582 +twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_cumulative_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_real_time_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_statistics.cpython-310.pyc,, +twilio/rest/taskrouter/v1/workspace/workflow/workflow_cumulative_statistics.py,sha256=9qhtmo-aJaPbXXSpFwNA-KVhRrrNPqFKVgVsCStQkSQ,19228 +twilio/rest/taskrouter/v1/workspace/workflow/workflow_real_time_statistics.py,sha256=zYvuSxymmwBYM-j2gxbDawghi2mQI34xNOIT6AfGtX4,9689 +twilio/rest/taskrouter/v1/workspace/workflow/workflow_statistics.py,sha256=9QfrU41hNIuj0qQdvGiP3nZElQygzsCzkBpuxrUZzcQ,14701 +twilio/rest/taskrouter/v1/workspace/workspace_cumulative_statistics.py,sha256=wxjupS0WMq0GuzSDBKfzIIl8cNrx-YKHDFLZ8zxRsZI,18165 +twilio/rest/taskrouter/v1/workspace/workspace_real_time_statistics.py,sha256=S5nARCNmxgMBuDgbIMQ46GAjrVSYP4mxJAwBPqexIaM,9038 +twilio/rest/taskrouter/v1/workspace/workspace_statistics.py,sha256=Pv19ZX_2NXrhikZVGpHhKIl02QH1XPqm3KcS5ZxXShk,13564 +twilio/rest/trunking/TrunkingBase.py,sha256=KEpBtt8vyNLrV0C6xgPB0EJy5eEylY48LePvQKhEi4U,1211 +twilio/rest/trunking/__init__.py,sha256=VX22mT-YnOfya6CQBiGMPyk7rHQmYGnJF5oiGiU7Jlg,391 +twilio/rest/trunking/__pycache__/TrunkingBase.cpython-310.pyc,, +twilio/rest/trunking/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trunking/v1/__init__.py,sha256=qbIMhWiC4TsBoUrQJNtWBduHzJSNW7w0usKKrT1vWXE,1269 +twilio/rest/trunking/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__init__.py,sha256=8Sk7IivEiaJZffh0ajsBL7JMwdtrYdazOR_VC35DLt4,40226 +twilio/rest/trunking/v1/trunk/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__pycache__/credential_list.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__pycache__/ip_access_control_list.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__pycache__/origination_url.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__pycache__/phone_number.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/__pycache__/recording.cpython-310.pyc,, +twilio/rest/trunking/v1/trunk/credential_list.py,sha256=11KUNZ9zhoEyMM4Kja7FVxvpPrYBv0i3BZd3foUgC1E,18353 +twilio/rest/trunking/v1/trunk/ip_access_control_list.py,sha256=YAB1IrPBQj2PY2j3neQ9L8grOSmV_RfhB_OaqRRhblw,18723 +twilio/rest/trunking/v1/trunk/origination_url.py,sha256=HFI8OnB98GOEZc7VlioCg-hCJ0yetx0lrfWPg8vH5s8,27869 +twilio/rest/trunking/v1/trunk/phone_number.py,sha256=CkR3bLxqbsa-c7gLRFUCMrwH-wio1KTHcZkiU2vT-zU,22464 +twilio/rest/trunking/v1/trunk/recording.py,sha256=Ew7oDDjxog_XDH5UKK0dT16MBZP-uEfVkiFQ9zNOSfk,8183 +twilio/rest/trusthub/TrusthubBase.py,sha256=LSqCTPqT2RLzvjNzVcTXdvmVqwmyGa46Mvp8NmhsSjA,1211 +twilio/rest/trusthub/__init__.py,sha256=HF6EMXEDfJnPqcSZRYnkzrwMEARiFkTxX57LIuhl_uA,2483 +twilio/rest/trusthub/__pycache__/TrusthubBase.cpython-310.pyc,, +twilio/rest/trusthub/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trusthub/v1/__init__.py,sha256=ylVjXc_ydfpG1fHfAe_zPUENSRXRiFgYw4VlVQmvFcE,4926 +twilio/rest/trusthub/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/compliance_inquiries.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/compliance_registration_inquiries.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/compliance_tollfree_inquiries.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/end_user.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/end_user_type.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/policies.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/supporting_document.cpython-310.pyc,, +twilio/rest/trusthub/v1/__pycache__/supporting_document_type.cpython-310.pyc,, +twilio/rest/trusthub/v1/compliance_inquiries.py,sha256=EqllP0ZgjkUi5lYyaGyx3p4kg1ydT2M7rqcHyD7w4k0,10026 +twilio/rest/trusthub/v1/compliance_registration_inquiries.py,sha256=xsTPMhsJZOYSp1HOzANmsK_-p-AWDZW0AfBYNVwI2Ik,27642 +twilio/rest/trusthub/v1/compliance_tollfree_inquiries.py,sha256=a9df3sPMZoRKpD3TeNnDKQ7swAohNeqOTKYSR5AM96k,13514 +twilio/rest/trusthub/v1/customer_profiles/__init__.py,sha256=8XSqCyal6Cl-95ObvRZWrfpZcAwhgK9AGeuxa5BVxxc,30766 +twilio/rest/trusthub/v1/customer_profiles/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_channel_endpoint_assignment.cpython-310.pyc,, +twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_entity_assignments.cpython-310.pyc,, +twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_evaluations.cpython-310.pyc,, +twilio/rest/trusthub/v1/customer_profiles/customer_profiles_channel_endpoint_assignment.py,sha256=p21HM6PyC92wEq7ltLW2BXhASZtI88kXTQ5StqTQfj0,23440 +twilio/rest/trusthub/v1/customer_profiles/customer_profiles_entity_assignments.py,sha256=GlUCg0XhgMG5VqitfsrX2jNzUIEkH4FHOVYQ0Qq10Qs,21885 +twilio/rest/trusthub/v1/customer_profiles/customer_profiles_evaluations.py,sha256=sdpYjPQwn_Xr4PffoQuRIrMFHviV-W1Zy9LG25NyOyo,18454 +twilio/rest/trusthub/v1/end_user.py,sha256=nPI6h2ptD3uNAKL-HqH4qwHmrNpb2jbsJGdYrp67R0A,20563 +twilio/rest/trusthub/v1/end_user_type.py,sha256=1zuFjdN8VxZ_XqO3rEl3cl9i3aMh6E4N-hq8Khy34Ts,14115 +twilio/rest/trusthub/v1/policies.py,sha256=IcOiK1_BzE070_iuifUzceWQz4hE7bGzQ2fjzDOUJGU,13543 +twilio/rest/trusthub/v1/supporting_document.py,sha256=pXeRyfwcmoU6Q3TVnBuOarJ6KfxdIqB2j3DeQQLPSCA,22196 +twilio/rest/trusthub/v1/supporting_document_type.py,sha256=jKYlRLcyACV7J98sX4BUlCcLwMKUjG4JJIiGz18YpbY,14741 +twilio/rest/trusthub/v1/trust_products/__init__.py,sha256=B9HXTW1CVk3woey4H4jqM59PP5_Xl05b5xxJcgYUTZ8,30129 +twilio/rest/trusthub/v1/trust_products/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_channel_endpoint_assignment.cpython-310.pyc,, +twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_entity_assignments.cpython-310.pyc,, +twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_evaluations.cpython-310.pyc,, +twilio/rest/trusthub/v1/trust_products/trust_products_channel_endpoint_assignment.py,sha256=3RVHMSnIwuzKxMEXfbfRslFiw2tptkH5i9gEnFyADTo,23134 +twilio/rest/trusthub/v1/trust_products/trust_products_entity_assignments.py,sha256=DU5yRBpBOIE7SgzVMi7hdDl1vgaPZpAHCD3PhH5RfII,21456 +twilio/rest/trusthub/v1/trust_products/trust_products_evaluations.py,sha256=BY73-vtkV0jtMjwHK4djYBg13hWZp0Q-iVAwQuNMXro,18073 +twilio/rest/verify/VerifyBase.py,sha256=slTn-f0x41HIoCytOvLLAYdWVdgh0Vfdno0Abcx0LC8,1197 +twilio/rest/verify/__init__.py,sha256=64lABdnh-2wHRS-ODTfyTpa3DHZlfAsSxsvLP5O9TK8,2056 +twilio/rest/verify/__pycache__/VerifyBase.cpython-310.pyc,, +twilio/rest/verify/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/__init__.py,sha256=CRuH0It_FUkZwbFGIhF4HmqAvwABDjm1dMUApkLqlFI,2993 +twilio/rest/verify/v2/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/__pycache__/form.cpython-310.pyc,, +twilio/rest/verify/v2/__pycache__/safelist.cpython-310.pyc,, +twilio/rest/verify/v2/__pycache__/template.cpython-310.pyc,, +twilio/rest/verify/v2/__pycache__/verification_attempt.cpython-310.pyc,, +twilio/rest/verify/v2/__pycache__/verification_attempts_summary.cpython-310.pyc,, +twilio/rest/verify/v2/form.py,sha256=vIMHXxXZCjpNcHKuQZzshOeyLSGlCsvCHaXesLpoTi4,5697 +twilio/rest/verify/v2/safelist.py,sha256=4fQLQwghLg9s5RRwVUorSGVxjwl9UCHjfc6Qx6Y5OXQ,8038 +twilio/rest/verify/v2/service/__init__.py,sha256=4heYsbmfIu2Zl7cLmT8s-WCmq_PeWj8d7kXpEMUSHTM,62442 +twilio/rest/verify/v2/service/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/service/__pycache__/access_token.cpython-310.pyc,, +twilio/rest/verify/v2/service/__pycache__/messaging_configuration.cpython-310.pyc,, +twilio/rest/verify/v2/service/__pycache__/verification.cpython-310.pyc,, +twilio/rest/verify/v2/service/__pycache__/verification_check.cpython-310.pyc,, +twilio/rest/verify/v2/service/__pycache__/webhook.cpython-310.pyc,, +twilio/rest/verify/v2/service/access_token.py,sha256=EQ26wzLa2Z3sjV6kMBtn8SEgidVCEL6y1pNVSa6qTBY,10271 +twilio/rest/verify/v2/service/entity/__init__.py,sha256=fhJsIJI7STD22vGhJnVtiQyP-wleiPqkawL0MG-isWY,20671 +twilio/rest/verify/v2/service/entity/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/service/entity/__pycache__/factor.cpython-310.pyc,, +twilio/rest/verify/v2/service/entity/__pycache__/new_factor.cpython-310.pyc,, +twilio/rest/verify/v2/service/entity/challenge/__init__.py,sha256=s0OjXUvz0mfQNrjI2X0GJvG6std-MzZKyqvYOhUWQes,34465 +twilio/rest/verify/v2/service/entity/challenge/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/service/entity/challenge/__pycache__/notification.cpython-310.pyc,, +twilio/rest/verify/v2/service/entity/challenge/notification.py,sha256=304fmFfAccK6w7894yeVkw-5jel93ff-rdZ89WY5QKM,6558 +twilio/rest/verify/v2/service/entity/factor.py,sha256=odGp278gnIEmFeZy8urYJTh31Dj7U4-NNVvCIasKtyw,29596 +twilio/rest/verify/v2/service/entity/new_factor.py,sha256=W-l1B7ig8IYj_CVm170e__hB5j5tUx3wfXOJr8hNLZQ,16167 +twilio/rest/verify/v2/service/messaging_configuration.py,sha256=nmBpY9VjR8zryw1yrj-BQZvfif4l3u0E2G5v6IN1NU0,23302 +twilio/rest/verify/v2/service/rate_limit/__init__.py,sha256=YWTxYakoSMt4MHjLbPb4Gz8Oyd9keU3LZJuyHXjb2oY,21510 +twilio/rest/verify/v2/service/rate_limit/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/verify/v2/service/rate_limit/__pycache__/bucket.cpython-310.pyc,, +twilio/rest/verify/v2/service/rate_limit/bucket.py,sha256=xXHNygyhVufq3oUMmZ8XlVY2oREssxcAh1vGHI0gP-c,22245 +twilio/rest/verify/v2/service/verification.py,sha256=KaZSWNhSdyMj7_pR7Z-duemYEsykcxqJhtjC_Y9x-C0,22420 +twilio/rest/verify/v2/service/verification_check.py,sha256=7QFcTGlwStF1AX2AydmBfrMVRRcNKo4NXgRnl4KBDBs,8377 +twilio/rest/verify/v2/service/webhook.py,sha256=WVPcjTdppzrECzfKZjjhMDa6oFmJvqY-KFdz9aYAuWQ,25394 +twilio/rest/verify/v2/template.py,sha256=0G34e6qHk7OXCoNmFlKyNmOcStZJQhBbhDC9Ck_VhGw,11731 +twilio/rest/verify/v2/verification_attempt.py,sha256=I8pRoI-c0lRG37tWsMXRsxHZ8LXX3Uu37KLT_JouMMY,30389 +twilio/rest/verify/v2/verification_attempts_summary.py,sha256=F7HnyfRxOG2EfMeADqmTaHZIbG0iIRx59JwR8Ob-_rI,13411 +twilio/rest/video/VideoBase.py,sha256=qDVCaYofOZ9GyWTLVAkIr-z20L7qJzK6UVkaoCSix7E,1190 +twilio/rest/video/__init__.py,sha256=n0lfmIKLLW72CaH5QxWw4PzNz9xs-sS7lcn3IVFNZZc,2050 +twilio/rest/video/__pycache__/VideoBase.cpython-310.pyc,, +twilio/rest/video/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/video/v1/__init__.py,sha256=dkp1TkZI0FLmxCCZ3nLx3YOtDwdzlm940z3vnPQSwXg,2972 +twilio/rest/video/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/video/v1/__pycache__/composition.cpython-310.pyc,, +twilio/rest/video/v1/__pycache__/composition_hook.cpython-310.pyc,, +twilio/rest/video/v1/__pycache__/composition_settings.cpython-310.pyc,, +twilio/rest/video/v1/__pycache__/recording.cpython-310.pyc,, +twilio/rest/video/v1/__pycache__/recording_settings.cpython-310.pyc,, +twilio/rest/video/v1/composition.py,sha256=A6ujboVZJ-KUuu4GfGtbDA42ppwneLc9PHf71DZnM3c,35040 +twilio/rest/video/v1/composition_hook.py,sha256=8jnxaxzsphoMF-d0pCl4VPLt0kwe3xydgU_4neUocd4,55002 +twilio/rest/video/v1/composition_settings.py,sha256=-x-JttoFAOE4cnFyrwPOWYhkoqyZM99mbRp8wPhz6Fs,14151 +twilio/rest/video/v1/recording.py,sha256=l_Y1AczEP4IloadTRaSP9enjAU_1JRWIgoeWTVU2aq4,27316 +twilio/rest/video/v1/recording_settings.py,sha256=WZ4oqurRsS_uoJb9LJiVLAudjA5RvCDkBe6e4M4s7qU,13982 +twilio/rest/video/v1/room/__init__.py,sha256=Ef6ZSPpFGs4w9RFxfg7C2OU5vshjujknbGDdSBwyJ2o,38354 +twilio/rest/video/v1/room/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/video/v1/room/__pycache__/recording_rules.cpython-310.pyc,, +twilio/rest/video/v1/room/__pycache__/room_recording.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/__init__.py,sha256=z_2_FnENojW_ED1FP4ymVTCbcVlnHlWKHfUHIJ1mtoc,28701 +twilio/rest/video/v1/room/participant/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/__pycache__/anonymize.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/__pycache__/published_track.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/__pycache__/subscribe_rules.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/__pycache__/subscribed_track.cpython-310.pyc,, +twilio/rest/video/v1/room/participant/anonymize.py,sha256=ZFnIAtEneGA67iGNQywZheD0iIl0yxadSOmXIAWnDKI,7907 +twilio/rest/video/v1/room/participant/published_track.py,sha256=F5DaEHCPBhmEC6BuZI31s27YCL-jWrfCLybnNz5cC0c,16890 +twilio/rest/video/v1/room/participant/subscribe_rules.py,sha256=6rHOZKMb1LD0I5Out2q-J7gXjYrzrw4W1ZUX0_F91EY,6396 +twilio/rest/video/v1/room/participant/subscribed_track.py,sha256=xSNCo9K0Rxb3O2uZpOsIy-jlAdfEdnVSdgs1gAu3K-E,17082 +twilio/rest/video/v1/room/recording_rules.py,sha256=3K0nQvwJqawde6gI5wwHGcYJuFToJUW7omKgKblB4T4,5092 +twilio/rest/video/v1/room/room_recording.py,sha256=psFI0lX8CF7ycImuuTON4o3yPmZwrhGLkSp02RdyOBk,24749 +twilio/rest/voice/VoiceBase.py,sha256=vx3BwrZ7ey2vqPrenZfgzIkoMV1txQR_vJlPGgMQLn4,1190 +twilio/rest/voice/__init__.py,sha256=mW4XcGaW44Uoa7eyNAKz4Nn4paB0Ck0Mkoub3XQYwwE,2099 +twilio/rest/voice/__pycache__/VoiceBase.cpython-310.pyc,, +twilio/rest/voice/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/voice/v1/__init__.py,sha256=dEg-rToBdmoNw7zta9H53Krf2WCRRe43XEWTXXkf3Hg,3036 +twilio/rest/voice/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/voice/v1/__pycache__/archived_call.cpython-310.pyc,, +twilio/rest/voice/v1/__pycache__/byoc_trunk.cpython-310.pyc,, +twilio/rest/voice/v1/__pycache__/ip_record.cpython-310.pyc,, +twilio/rest/voice/v1/__pycache__/source_ip_mapping.cpython-310.pyc,, +twilio/rest/voice/v1/archived_call.py,sha256=S6IGmSuhi_uFXQuJ_6KnQ84CDokmKpw3ypG0g4RPFyc,3317 +twilio/rest/voice/v1/byoc_trunk.py,sha256=clq_IKGQgIm1BGKI64T7YxPwoCM6l9Olaw9hott-cbI,37489 +twilio/rest/voice/v1/connection_policy/__init__.py,sha256=JH9PNS_D2B0GKaJhzwGneJMowpFimW-gtDYA2tPQKRE,20487 +twilio/rest/voice/v1/connection_policy/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/voice/v1/connection_policy/__pycache__/connection_policy_target.cpython-310.pyc,, +twilio/rest/voice/v1/connection_policy/connection_policy_target.py,sha256=S7UbMcg21ahnETMg_bCi-E0BLeZ1F3j7z3lsZzBuiqQ,29300 +twilio/rest/voice/v1/dialing_permissions/__init__.py,sha256=SJbMDsLFYUD-mkvu8YVDhjUbloobBrKuQwEtRC-Yvgw,2327 +twilio/rest/voice/v1/dialing_permissions/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/voice/v1/dialing_permissions/__pycache__/bulk_country_update.cpython-310.pyc,, +twilio/rest/voice/v1/dialing_permissions/__pycache__/settings.cpython-310.pyc,, +twilio/rest/voice/v1/dialing_permissions/bulk_country_update.py,sha256=xUdm-LgKbXk45UUrWP8hQWOT0q9y50Mvh2QGIoabWck,3838 +twilio/rest/voice/v1/dialing_permissions/country/__init__.py,sha256=mmC0bnM9P8-9-mvoxHi5I76Rr9z4ukOyowuczi83bn0,27348 +twilio/rest/voice/v1/dialing_permissions/country/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/voice/v1/dialing_permissions/country/__pycache__/highrisk_special_prefix.cpython-310.pyc,, +twilio/rest/voice/v1/dialing_permissions/country/highrisk_special_prefix.py,sha256=NUKeAjHIfhEtbHztu-wfYraO_9Oy9GYGk4xg4_5DlKk,11128 +twilio/rest/voice/v1/dialing_permissions/settings.py,sha256=ik381anRxHRqq4s-4QfBlS_LwkMvtn69NVTyWrOcHS0,7140 +twilio/rest/voice/v1/ip_record.py,sha256=JGNRyrSFU4BNTZoZ3I3bao9ywEb64jLd_FJIgFEPN9I,20416 +twilio/rest/voice/v1/source_ip_mapping.py,sha256=bEbhaho9kes5mk_ieFa5OX8urJLqgp3KCqx2S1wE9nY,19432 +twilio/rest/wireless/WirelessBase.py,sha256=VqCO0nP3Ox8WIkFHHdjMj-6bQeIWIBbW3_YnLYiyBMc,1211 +twilio/rest/wireless/__init__.py,sha256=37apinMOZa6Gr3Dl5SUnt5sy-fQE20i4bUBONAnhdGY,1261 +twilio/rest/wireless/__pycache__/WirelessBase.cpython-310.pyc,, +twilio/rest/wireless/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/wireless/v1/__init__.py,sha256=V2ghA15n9ZpyofyFP0pUiQtAkoDX4-u1a4-PqluNojM,2136 +twilio/rest/wireless/v1/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/wireless/v1/__pycache__/command.cpython-310.pyc,, +twilio/rest/wireless/v1/__pycache__/rate_plan.cpython-310.pyc,, +twilio/rest/wireless/v1/__pycache__/usage_record.cpython-310.pyc,, +twilio/rest/wireless/v1/command.py,sha256=8yB48dGwRlCk_7-SzW0cCIeAJh8Zu_geaLHZlw1vwng,27547 +twilio/rest/wireless/v1/rate_plan.py,sha256=7tmqoceoveWJftkzeX-DMbwiNCyk3ZNVsdhnJwuPyhw,29959 +twilio/rest/wireless/v1/sim/__init__.py,sha256=GeebfUqW6laJQb3rD7nypGxf1I5iWzs90IHuX5iczFw,46224 +twilio/rest/wireless/v1/sim/__pycache__/__init__.cpython-310.pyc,, +twilio/rest/wireless/v1/sim/__pycache__/data_session.cpython-310.pyc,, +twilio/rest/wireless/v1/sim/__pycache__/usage_record.cpython-310.pyc,, +twilio/rest/wireless/v1/sim/data_session.py,sha256=5SCWQJCCLfk5_c7mVAT0U40pJtTBbew-D2d7RtTG_vM,14323 +twilio/rest/wireless/v1/sim/usage_record.py,sha256=juLpPC7eG0Ggb4Gu4dr3IPNAulC6l5jirlQjNhVnfUM,17481 +twilio/rest/wireless/v1/usage_record.py,sha256=s20sRT_-2q_5WCDc_1FeU90ZN2Km-2V8FopFfGPuvic,16069 +twilio/twiml/__init__.py,sha256=2RyXWXsT4Nv38P4Gc51drBxMNMVQzyp7Wzmny5G5QjI,3471 +twilio/twiml/__pycache__/__init__.cpython-310.pyc,, +twilio/twiml/__pycache__/fax_response.cpython-310.pyc,, +twilio/twiml/__pycache__/messaging_response.cpython-310.pyc,, +twilio/twiml/__pycache__/voice_response.cpython-310.pyc,, +twilio/twiml/fax_response.py,sha256=tIz38pGfUL6kj2CHGUxyP1CKQtdjJySVFiedm64exKI,1435 +twilio/twiml/messaging_response.py,sha256=R3SFGVbZpkQHKf4v5FOm7S_sj97OyrUfjH2z4X53DXk,3017 +twilio/twiml/voice_response.py,sha256=PpmoBN7C7THNLBuizIqPY2-5pI2AX37ArmDCluyqLoA,83800 diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/REQUESTED b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/WHEEL new file mode 100644 index 0000000..832be11 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/top_level.txt new file mode 100644 index 0000000..105a686 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio-9.0.5.dist-info/top_level.txt @@ -0,0 +1 @@ +twilio diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/__init__.py new file mode 100644 index 0000000..a5d6bc7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/__init__.py @@ -0,0 +1,2 @@ +__version_info__ = ("9", "0", "5") +__version__ = ".".join(__version_info__) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..999c4ed Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/request_validator.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/request_validator.cpython-310.pyc new file mode 100644 index 0000000..6c87657 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/__pycache__/request_validator.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..677cdcd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/client_base.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/client_base.cpython-310.pyc new file mode 100644 index 0000000..d3bd796 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/client_base.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/deserialize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/deserialize.cpython-310.pyc new file mode 100644 index 0000000..85a260a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/deserialize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/domain.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/domain.cpython-310.pyc new file mode 100644 index 0000000..1ee8795 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/domain.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..d365fd3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_context.cpython-310.pyc new file mode 100644 index 0000000..06371f6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_resource.cpython-310.pyc new file mode 100644 index 0000000..37a23b0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/instance_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/list_resource.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/list_resource.cpython-310.pyc new file mode 100644 index 0000000..235bee6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/list_resource.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/obsolete.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/obsolete.cpython-310.pyc new file mode 100644 index 0000000..1e1a26b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/obsolete.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/page.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/page.cpython-310.pyc new file mode 100644 index 0000000..0734753 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/page.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/serialize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/serialize.cpython-310.pyc new file mode 100644 index 0000000..404cb66 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/serialize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/values.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/values.cpython-310.pyc new file mode 100644 index 0000000..4e8581b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/values.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..c5734ca Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/base/__pycache__/version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/client_base.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/client_base.py new file mode 100644 index 0000000..c9e0f9c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/client_base.py @@ -0,0 +1,234 @@ +import os +import platform +from typing import Dict, List, MutableMapping, Optional, Tuple +from urllib.parse import urlparse, urlunparse + +from twilio import __version__ +from twilio.base.exceptions import TwilioException +from twilio.http import HttpClient +from twilio.http.http_client import TwilioHttpClient +from twilio.http.response import Response + + +class ClientBase(object): + """A client for accessing the Twilio API.""" + + def __init__( + self, + username: Optional[str] = None, + password: Optional[str] = None, + account_sid: Optional[str] = None, + region: Optional[str] = None, + http_client: Optional[HttpClient] = None, + environment: Optional[MutableMapping[str, str]] = None, + edge: Optional[str] = None, + user_agent_extensions: Optional[List[str]] = None, + ): + """ + Initializes the Twilio Client + + :param username: Username to authenticate with + :param password: Password to authenticate with + :param account_sid: Account SID, defaults to Username + :param region: Twilio Region to make requests to, defaults to 'us1' if an edge is provided + :param http_client: HttpClient, defaults to TwilioHttpClient + :param environment: Environment to look for auth details, defaults to os.environ + :param edge: Twilio Edge to make requests to, defaults to None + :param user_agent_extensions: Additions to the user agent string + """ + environment = environment or os.environ + + self.username = username or environment.get("TWILIO_ACCOUNT_SID") + """ :type : str """ + self.password = password or environment.get("TWILIO_AUTH_TOKEN") + """ :type : str """ + self.edge = edge or environment.get("TWILIO_EDGE") + """ :type : str """ + self.region = region or environment.get("TWILIO_REGION") + """ :type : str """ + self.user_agent_extensions = user_agent_extensions or [] + """ :type : list[str] """ + + if not self.username or not self.password: + raise TwilioException("Credentials are required to create a TwilioClient") + + self.account_sid = account_sid or self.username + """ :type : str """ + self.auth = (self.username, self.password) + """ :type : tuple(str, str) """ + self.http_client: HttpClient = http_client or TwilioHttpClient() + """ :type : HttpClient """ + + def request( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Makes a request to the Twilio API using the configured http client + Authentication information is automatically added if none is provided + + :param method: HTTP Method + :param uri: Fully qualified url + :param params: Query string parameters + :param data: POST body data + :param headers: HTTP Headers + :param auth: Authentication + :param timeout: Timeout in seconds + :param allow_redirects: Should the client follow redirects + + :returns: Response from the Twilio API + """ + auth = self.get_auth(auth) + headers = self.get_headers(method, headers) + uri = self.get_hostname(uri) + + return self.http_client.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + async def request_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Asynchronously makes a request to the Twilio API using the configured http client + The configured http client must be an asynchronous http client + Authentication information is automatically added if none is provided + + :param method: HTTP Method + :param uri: Fully qualified url + :param params: Query string parameters + :param data: POST body data + :param headers: HTTP Headers + :param auth: Authentication + :param timeout: Timeout in seconds + :param allow_redirects: Should the client follow redirects + + :returns: Response from the Twilio API + """ + if not self.http_client.is_async: + raise RuntimeError( + "http_client must be asynchronous to support async API requests" + ) + + auth = self.get_auth(auth) + headers = self.get_headers(method, headers) + uri = self.get_hostname(uri) + + return await self.http_client.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + def get_auth(self, auth: Optional[Tuple[str, str]]) -> Tuple[str, str]: + """ + Get the request authentication object + :param auth: Authentication (username, password) + :returns: The authentication object + """ + return auth or self.auth + + def get_headers( + self, method: str, headers: Optional[Dict[str, str]] + ) -> Dict[str, str]: + """ + Get the request headers including user-agent, extensions, encoding, content-type, MIME type + :param method: HTTP method + :param headers: HTTP headers + :returns: HTTP headers + """ + headers = headers or {} + + # Set User-Agent + pkg_version = __version__ + os_name = platform.system() + os_arch = platform.machine() + python_version = platform.python_version() + headers["User-Agent"] = "twilio-python/{} ({} {}) Python/{}".format( + pkg_version, + os_name, + os_arch, + python_version, + ) + # Extensions + for extension in self.user_agent_extensions: + headers["User-Agent"] += " {}".format(extension) + headers["X-Twilio-Client"] = "python-{}".format(__version__) + + # Types, encodings, etc. + headers["Accept-Charset"] = "utf-8" + if method == "POST" and "Content-Type" not in headers: + headers["Content-Type"] = "application/x-www-form-urlencoded" + if "Accept" not in headers: + headers["Accept"] = "application/json" + + return headers + + def get_hostname(self, uri: str) -> str: + """ + Determines the proper hostname given edge and region preferences + via client configuration or uri. + + :param uri: Fully qualified url + + :returns: The final uri used to make the request + """ + if not self.edge and not self.region: + return uri + + parsed_url = urlparse(uri) + pieces = parsed_url.netloc.split(".") + prefix = pieces[0] + suffix = ".".join(pieces[-2:]) + region = None + edge = None + if len(pieces) == 4: + # product.region.twilio.com + region = pieces[1] + elif len(pieces) == 5: + # product.edge.region.twilio.com + edge = pieces[1] + region = pieces[2] + + edge = self.edge or edge + region = self.region or region or (edge and "us1") + + parsed_url = parsed_url._replace( + netloc=".".join([part for part in [prefix, edge, region, suffix] if part]) + ) + return str(urlunparse(parsed_url)) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "".format(self.account_sid) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/deserialize.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/deserialize.py new file mode 100644 index 0000000..71226c0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/deserialize.py @@ -0,0 +1,75 @@ +import datetime +from decimal import BasicContext, Decimal +from email.utils import parsedate +from typing import Optional, Union + +ISO8601_DATE_FORMAT = "%Y-%m-%d" +ISO8601_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + + +def iso8601_date(s: str) -> Union[datetime.date, str]: + """ + Parses an ISO 8601 date string and returns a UTC date object or the string + if the parsing failed. + :param s: ISO 8601-formatted date string (2015-01-25) + :return: + """ + try: + return ( + datetime.datetime.strptime(s, ISO8601_DATE_FORMAT) + .replace(tzinfo=datetime.timezone.utc) + .date() + ) + except (TypeError, ValueError): + return s + + +def iso8601_datetime( + s: str, +) -> Union[datetime.datetime, str]: + """ + Parses an ISO 8601 datetime string and returns a UTC datetime object, + or the string if parsing failed. + :param s: ISO 8601-formatted datetime string (2015-01-25T12:34:56Z) + """ + try: + return datetime.datetime.strptime(s, ISO8601_DATETIME_FORMAT).replace( + tzinfo=datetime.timezone.utc + ) + except (TypeError, ValueError): + return s + + +def rfc2822_datetime(s: str) -> Optional[datetime.datetime]: + """ + Parses an RFC 2822 date string and returns a UTC datetime object, + or the string if parsing failed. + :param s: RFC 2822-formatted string date + :return: datetime or str + """ + date_tuple = parsedate(s) + if date_tuple is None: + return None + return datetime.datetime(*date_tuple[:6]).replace(tzinfo=datetime.timezone.utc) + + +def decimal(d: Optional[str]) -> Union[Decimal, str]: + """ + Parses a decimal string into a Decimal + :param d: decimal string + """ + if not d: + return d + return Decimal(d, BasicContext) + + +def integer(i: str) -> Union[int, str]: + """ + Parses an integer string into an int + :param i: integer string + :return: int + """ + try: + return int(i) + except (TypeError, ValueError): + return i diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/domain.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/domain.py new file mode 100644 index 0000000..4f8395d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/domain.py @@ -0,0 +1,93 @@ +from typing import Dict, Optional, Tuple +from twilio.http.response import Response +from twilio.rest import Client + + +class Domain(object): + """ + This represents at Twilio API subdomain. + + Like, `api.twilio.com` or `lookups.twilio.com'. + """ + + def __init__(self, twilio: Client, base_url: str): + self.twilio = twilio + self.base_url = base_url + + def absolute_url(self, uri: str) -> str: + """ + Converts a relative `uri` to an absolute url. + :param string uri: The relative uri to make absolute. + :return: An absolute url (based off this domain) + """ + return "{}/{}".format(self.base_url.strip("/"), uri.strip("/")) + + def request( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Makes an HTTP request to this domain. + :param method: The HTTP method. + :param uri: The HTTP uri. + :param params: Query parameters. + :param data: The request body. + :param headers: The HTTP headers. + :param auth: Basic auth tuple of (username, password) + :param timeout: The request timeout. + :param allow_redirects: True if the client should follow HTTP + redirects. + """ + url = self.absolute_url(uri) + return self.twilio.request( + method, + url, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + async def request_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Makes an asynchronous HTTP request to this domain. + :param method: The HTTP method. + :param uri: The HTTP uri. + :param params: Query parameters. + :param data: The request body. + :param headers: The HTTP headers. + :param auth: Basic auth tuple of (username, password) + :param timeout: The request timeout. + :param allow_redirects: True if the client should follow HTTP + redirects. + """ + url = self.absolute_url(uri) + return await self.twilio.request_async( + method, + url, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/exceptions.py new file mode 100644 index 0000000..8f3b7cc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/exceptions.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +import sys +from typing import Optional + + +class TwilioException(Exception): + pass + + +class TwilioRestException(TwilioException): + """A generic 400 or 500 level exception from the Twilio API + + :param int status: the HTTP status that was returned for the exception + :param str uri: The URI that caused the exception + :param str msg: A human-readable message for the error + :param int|None code: A Twilio-specific error code for the error. This is + not available for all errors. + :param method: The HTTP method used to make the request + :param details: Additional error details returned for the exception + """ + + def __init__( + self, + status: int, + uri: str, + msg: str = "", + code: Optional[int] = None, + method: str = "GET", + details: Optional[object] = None, + ): + self.uri = uri + self.status = status + self.msg = msg + self.code = code + self.method = method + self.details = details + + def __str__(self) -> str: + """Try to pretty-print the exception, if this is going on screen.""" + + def red(words: str) -> str: + return "\033[31m\033[49m%s\033[0m" % words + + def white(words: str) -> str: + return "\033[37m\033[49m%s\033[0m" % words + + def blue(words: str) -> str: + return "\033[34m\033[49m%s\033[0m" % words + + def teal(words: str) -> str: + return "\033[36m\033[49m%s\033[0m" % words + + def get_uri(code: int) -> str: + return "https://www.twilio.com/docs/errors/{0}".format(code) + + # If it makes sense to print a human readable error message, try to + # do it. The one problem is that someone might catch this error and + # try to display the message from it to an end user. + if hasattr(sys.stderr, "isatty") and sys.stderr.isatty(): + msg = ( + "\n{red_error} {request_was}\n\n{http_line}" + "\n\n{twilio_returned}\n\n{message}\n".format( + red_error=red("HTTP Error"), + request_was=white("Your request was:"), + http_line=teal("%s %s" % (self.method, self.uri)), + twilio_returned=white("Twilio returned the following information:"), + message=blue(str(self.msg)), + ) + ) + if self.code: + msg = "".join( + [ + msg, + "\n{more_info}\n\n{uri}\n\n".format( + more_info=white("More information may be available here:"), + uri=blue(get_uri(self.code)), + ), + ] + ) + return msg + else: + return "HTTP {0} error: {1}".format(self.status, self.msg) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_context.py new file mode 100644 index 0000000..44ff9a3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_context.py @@ -0,0 +1,6 @@ +from twilio.base.version import Version + + +class InstanceContext(object): + def __init__(self, version: Version): + self._version = version diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_resource.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_resource.py new file mode 100644 index 0000000..a05aac3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/instance_resource.py @@ -0,0 +1,6 @@ +from twilio.base.version import Version + + +class InstanceResource(object): + def __init__(self, version: Version): + self._version = version diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/list_resource.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/list_resource.py new file mode 100644 index 0000000..e3eb176 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/list_resource.py @@ -0,0 +1,6 @@ +from twilio.base.version import Version + + +class ListResource(object): + def __init__(self, version: Version): + self._version = version diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/obsolete.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/obsolete.py new file mode 100644 index 0000000..e0f4a03 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/obsolete.py @@ -0,0 +1,47 @@ +import warnings +import functools + + +class ObsoleteException(Exception): + """Base class for warnings about obsolete features.""" + + +def obsolete_client(func): + """This is a decorator which can be used to mark Client classes as + obsolete. It will result in an error being emitted when the class is + instantiated.""" + + @functools.wraps(func) + def new_func(*args, **kwargs): + raise ObsoleteException( + "{} has been removed from this version of the library. " + "Please refer to current documentation for guidance.".format(func.__name__) + ) + + return new_func + + +def deprecated_method(new_func=None): + """ + This is a decorator which can be used to mark deprecated methods. + It will report in a DeprecationWarning being emitted to stderr when the deprecated method is used. + """ + + def deprecated_method_wrapper(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + msg = "Function method .{}() is deprecated".format(func.__name__) + msg += ( + " in favor of .{}()".format(new_func) + if isinstance(new_func, str) + else "" + ) + warnings.warn(msg, DeprecationWarning) + return func(*args, **kwargs) + + return wrapper + + if callable(new_func): + return deprecated_method_wrapper(new_func) + + return deprecated_method_wrapper diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/page.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/page.py new file mode 100644 index 0000000..2238528 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/page.py @@ -0,0 +1,171 @@ +import json +from typing import Any, Dict, Optional + +from twilio.base.exceptions import TwilioException +from twilio.http.response import Response + + +class Page(object): + """ + Represents a page of records in a collection. + + A `Page` lets you iterate over its records and fetch the next and previous + pages in the collection. + """ + + META_KEYS = { + "end", + "first_page_uri", + "next_page_uri", + "last_page_uri", + "page", + "page_size", + "previous_page_uri", + "total", + "num_pages", + "start", + "uri", + } + + def __init__(self, version, response: Response, solution={}): + payload = self.process_response(response) + + self._version = version + self._payload = payload + self._solution = solution + self._records = iter(self.load_page(payload)) + + def __iter__(self): + """ + A `Page` is a valid iterator. + """ + return self + + def __next__(self): + return self.next() + + def next(self): + """ + Returns the next record in the `Page`. + """ + return self.get_instance(next(self._records)) + + @classmethod + def process_response(cls, response: Response) -> Any: + """ + Load a JSON response. + + :param response: The HTTP response. + :return The JSON-loaded content. + """ + if response.status_code != 200: + raise TwilioException("Unable to fetch page", response) + + return json.loads(response.text) + + def load_page(self, payload: Dict[str, Any]): + """ + Parses the collection of records out of a list payload. + + :param payload: The JSON-loaded content. + :return list: The list of records. + """ + if "meta" in payload and "key" in payload["meta"]: + return payload[payload["meta"]["key"]] + else: + keys = set(payload.keys()) + key = keys - self.META_KEYS + if len(key) == 1: + return payload[key.pop()] + + raise TwilioException("Page Records can not be deserialized") + + @property + def previous_page_url(self) -> Optional[str]: + """ + :return str: Returns a link to the previous_page_url or None if doesn't exist. + """ + if "meta" in self._payload and "previous_page_url" in self._payload["meta"]: + return self._payload["meta"]["previous_page_url"] + elif ( + "previous_page_uri" in self._payload and self._payload["previous_page_uri"] + ): + return self._version.domain.absolute_url(self._payload["previous_page_uri"]) + + return None + + @property + def next_page_url(self) -> Optional[str]: + """ + :return str: Returns a link to the next_page_url or None if doesn't exist. + """ + if "meta" in self._payload and "next_page_url" in self._payload["meta"]: + return self._payload["meta"]["next_page_url"] + elif "next_page_uri" in self._payload and self._payload["next_page_uri"]: + return self._version.domain.absolute_url(self._payload["next_page_uri"]) + + return None + + def get_instance(self, payload: Dict[str, Any]) -> Any: + """ + :param dict payload: A JSON-loaded representation of an instance record. + :return: A rich, resource-dependent object. + """ + raise TwilioException( + "Page.get_instance() must be implemented in the derived class" + ) + + def next_page(self) -> Optional["Page"]: + """ + Return the `Page` after this one. + :return The next page. + """ + if not self.next_page_url: + return None + + response = self._version.domain.twilio.request("GET", self.next_page_url) + cls = type(self) + return cls(self._version, response, self._solution) + + async def next_page_async(self) -> Optional["Page"]: + """ + Asynchronously return the `Page` after this one. + :return The next page. + """ + if not self.next_page_url: + return None + + response = await self._version.domain.twilio.request_async( + "GET", self.next_page_url + ) + cls = type(self) + return cls(self._version, response, self._solution) + + def previous_page(self) -> Optional["Page"]: + """ + Return the `Page` before this one. + :return The previous page. + """ + if not self.previous_page_url: + return None + + response = self._version.domain.twilio.request("GET", self.previous_page_url) + cls = type(self) + return cls(self._version, response, self._solution) + + async def previous_page_async(self) -> Optional["Page"]: + """ + Asynchronously return the `Page` before this one. + :return The previous page. + """ + if not self.previous_page_url: + return None + + response = await self._version.domain.twilio.request_async( + "GET", self.previous_page_url + ) + cls = type(self) + return cls(self._version, response, self._solution) + + def __repr__(self) -> str: + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/serialize.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/serialize.py new file mode 100644 index 0000000..cea91b0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/serialize.py @@ -0,0 +1,93 @@ +import datetime +import json + +from twilio.base import values + + +def iso8601_date(d): + """ + Return a string representation of a date that the Twilio API understands + Format is YYYY-MM-DD. Returns None if d is not a string, datetime, or date + """ + if d == values.unset: + return d + elif isinstance(d, datetime.datetime): + return str(d.date()) + elif isinstance(d, datetime.date): + return str(d) + elif isinstance(d, str): + return d + + +def iso8601_datetime(d): + """ + Return a string representation of a date that the Twilio API understands + Format is YYYY-MM-DD. Returns None if d is not a string, datetime, or date + """ + if d == values.unset: + return d + elif isinstance(d, datetime.datetime) or isinstance(d, datetime.date): + return d.strftime("%Y-%m-%dT%H:%M:%SZ") + elif isinstance(d, str): + return d + + +def prefixed_collapsible_map(m, prefix): + """ + Return a dict of params corresponding to those in m with the added prefix + """ + if m == values.unset: + return {} + + def flatten_dict(d, result=None, prv_keys=None): + if result is None: + result = {} + + if prv_keys is None: + prv_keys = [] + + for k, v in d.items(): + if isinstance(v, dict): + flatten_dict(v, result, prv_keys + [k]) + else: + result[".".join(prv_keys + [k])] = v + + return result + + if isinstance(m, dict): + flattened = flatten_dict(m) + return {"{}.{}".format(prefix, k): v for k, v in flattened.items()} + + return {} + + +def boolean_to_string(bool_or_str): + if bool_or_str == values.unset: + return bool_or_str + + if bool_or_str is None: + return bool_or_str + + if isinstance(bool_or_str, str): + return bool_or_str.lower() + + return "true" if bool_or_str else "false" + + +def object(obj): + """ + Return a jsonified string represenation of obj if obj is jsonifiable else + return obj untouched + """ + if isinstance(obj, dict) or isinstance(obj, list): + return json.dumps(obj) + return obj + + +def map(lst, serialize_func): + """ + Applies serialize_func to every element in lst + """ + if not isinstance(lst, list): + return lst + return [serialize_func(e) for e in lst] diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/values.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/values.py new file mode 100644 index 0000000..16032b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/values.py @@ -0,0 +1,13 @@ +from typing import Dict + +unset = object() + + +def of(d: Dict[str, object]) -> Dict[str, object]: + """ + Remove unset values from a dict. + + :param d: A dict to strip. + :return A dict with unset values removed. + """ + return {k: v for k, v in d.items() if v != unset} diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/base/version.py b/.pythonlibs/lib/python3.10/site-packages/twilio/base/version.py new file mode 100644 index 0000000..64cc601 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/base/version.py @@ -0,0 +1,492 @@ +import json +from typing import Any, AsyncIterator, Dict, Iterator, Optional, Tuple + +from twilio.base import values +from twilio.base.domain import Domain +from twilio.base.exceptions import TwilioRestException +from twilio.base.page import Page +from twilio.http.response import Response + + +class Version(object): + """ + Represents an API version. + """ + + def __init__(self, domain: Domain, version: str): + self.domain = domain + self.version = version + + def absolute_url(self, uri: str) -> str: + """ + Turns a relative uri into an absolute url. + """ + return self.domain.absolute_url(self.relative_uri(uri)) + + def relative_uri(self, uri: str) -> str: + """ + Turns a relative uri into a versioned relative uri. + """ + return "{}/{}".format(self.version.strip("/"), uri.strip("/")) + + def request( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Make an HTTP request. + """ + url = self.relative_uri(uri) + return self.domain.request( + method, + url, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + async def request_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Make an asynchronous HTTP request + """ + url = self.relative_uri(uri) + return await self.domain.request_async( + method, + url, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + @classmethod + def exception( + cls, method: str, uri: str, response: Response, message: str + ) -> TwilioRestException: + """ + Wraps an exceptional response in a `TwilioRestException`. + """ + # noinspection PyBroadException + try: + error_payload = json.loads(response.text) + if "message" in error_payload: + message = "{}: {}".format(message, error_payload["message"]) + details = error_payload.get("details") + code = error_payload.get("code", response.status_code) + return TwilioRestException( + response.status_code, uri, message, code, method, details + ) + except Exception: + return TwilioRestException( + response.status_code, uri, message, response.status_code, method + ) + + def _parse_fetch(self, method: str, uri: str, response: Response) -> Any: + """ + Parses fetch response JSON + """ + # Note that 3XX response codes are allowed for fetches. + if response.status_code < 200 or response.status_code >= 400: + raise self.exception(method, uri, response, "Unable to fetch record") + + return json.loads(response.text) + + def fetch( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Fetch a resource instance. + """ + response = self.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_fetch(method, uri, response) + + async def fetch_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Asynchronously fetch a resource instance. + """ + response = await self.request_async( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_fetch(method, uri, response) + + def _parse_update(self, method: str, uri: str, response: Response) -> Any: + """ + Parses update response JSON + """ + if response.status_code < 200 or response.status_code >= 300: + raise self.exception(method, uri, response, "Unable to update record") + + return json.loads(response.text) + + def update( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Update a resource instance. + """ + response = self.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_update(method, uri, response) + + async def update_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Asynchronously update a resource instance. + """ + response = await self.request_async( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_update(method, uri, response) + + def _parse_delete(self, method: str, uri: str, response: Response) -> bool: + """ + Parses delete response JSON + """ + if response.status_code < 200 or response.status_code >= 300: + raise self.exception(method, uri, response, "Unable to delete record") + + return response.status_code == 204 + + def delete( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> bool: + """ + Delete a resource. + """ + response = self.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_delete(method, uri, response) + + async def delete_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> bool: + """ + Asynchronously delete a resource. + """ + response = await self.request_async( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_delete(method, uri, response) + + def read_limits( + self, limit: Optional[int] = None, page_size: Optional[int] = None + ) -> Dict[str, object]: + """ + Takes a limit on the max number of records to read and a max page_size + and calculates the max number of pages to read. + + :param limit: Max number of records to read. + :param page_size: Max page size. + :return A dictionary of paging limits. + """ + if limit is not None and page_size is None: + page_size = limit + + return { + "limit": limit or values.unset, + "page_size": page_size or values.unset, + } + + def page( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Makes an HTTP request. + """ + return self.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + async def page_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Makes an asynchronous HTTP request. + """ + return await self.request_async( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + def stream( + self, + page: Optional[Page], + limit: Optional[int] = None, + page_limit: Optional[int] = None, + ) -> Iterator[Any]: + """ + Generates records one a time from a page, stopping at prescribed limits. + + :param page: The page to stream. + :param limit: The max number of records to read. + :param page_limit: The max number of pages to read. + """ + current_record = 1 + current_page = 1 + + while page is not None: + for record in page: + yield record + current_record += 1 + if limit and limit is not values.unset and limit < current_record: + return + + current_page += 1 + if ( + page_limit + and page_limit is not values.unset + and page_limit < current_page + ): + return + + page = page.next_page() + + async def stream_async( + self, + page: Optional[Page], + limit: Optional[int] = None, + page_limit: Optional[int] = None, + ) -> AsyncIterator[Any]: + """ + Generates records one a time from a page, stopping at prescribed limits. + + :param page: The page to stream. + :param limit: The max number of records to read. + :param page_limit: The max number of pages to read. + """ + current_record = 1 + current_page = 1 + + while page is not None: + for record in page: + yield record + current_record += 1 + if limit and limit is not values.unset and limit < current_record: + return + + current_page += 1 + if ( + page_limit + and page_limit is not values.unset + and page_limit < current_page + ): + return + + page = await page.next_page_async() + + def _parse_create(self, method: str, uri: str, response: Response) -> Any: + """ + Parse create response JSON + """ + if response.status_code < 200 or response.status_code >= 300: + raise self.exception(method, uri, response, "Unable to create record") + + return json.loads(response.text) + + def create( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Create a resource instance. + """ + response = self.request( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_create(method, uri, response) + + async def create_async( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Any: + """ + Asynchronously create a resource instance. + """ + response = await self.request_async( + method, + uri, + params=params, + data=data, + headers=headers, + auth=auth, + timeout=timeout, + allow_redirects=allow_redirects, + ) + + return self._parse_create(method, uri, response) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__init__.py new file mode 100644 index 0000000..3e24827 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__init__.py @@ -0,0 +1,104 @@ +from logging import Logger +from typing import Any, Dict, Optional, Tuple +from urllib.parse import urlencode + +from requests import Response + +from twilio.base.exceptions import TwilioException +from twilio.http.request import Request as TwilioRequest +from twilio.http.response import Response as TwilioResponse + + +class HttpClient(object): + def __init__(self, logger: Logger, is_async: bool, timeout: Optional[float] = None): + """ + Constructor for the abstract HTTP client + + :param logger + :param is_async: Whether the client supports async request calls. + :param timeout: Timeout for the requests. + Timeout should never be zero (0) or less. + """ + self.logger = logger + self.is_async = is_async + + if timeout is not None and timeout <= 0: + raise ValueError(timeout) + self.timeout = timeout + + self._test_only_last_request: Optional[TwilioRequest] = None + self._test_only_last_response: Optional[TwilioResponse] = None + + """ + An abstract class representing an HTTP client. + """ + + def request( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> TwilioResponse: + """ + Make an HTTP request. + """ + raise TwilioException("HttpClient is an abstract class") + + def log_request(self, kwargs: Dict[str, Any]) -> None: + """ + Logs the HTTP request + """ + self.logger.info("-- BEGIN Twilio API Request --") + + if kwargs["params"]: + self.logger.info( + "{} Request: {}?{}".format( + kwargs["method"], kwargs["url"], urlencode(kwargs["params"]) + ) + ) + self.logger.info("Query Params: {}".format(kwargs["params"])) + else: + self.logger.info("{} Request: {}".format(kwargs["method"], kwargs["url"])) + + if kwargs["headers"]: + self.logger.info("Headers:") + for key, value in kwargs["headers"].items(): + # Do not log authorization headers + if "authorization" not in key.lower(): + self.logger.info("{} : {}".format(key, value)) + + self.logger.info("-- END Twilio API Request --") + + def log_response(self, status_code: int, response: Response) -> None: + """ + Logs the HTTP response + """ + self.logger.info("Response Status Code: {}".format(status_code)) + self.logger.info("Response Headers: {}".format(response.headers)) + + +class AsyncHttpClient(HttpClient): + """ + An abstract class representing an asynchronous HTTP client. + """ + + async def request( + self, + method: str, + uri: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> TwilioResponse: + """ + Make an asynchronous HTTP request. + """ + raise TwilioException("AsyncHttpClient is an abstract class") diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d32f023 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/async_http_client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/async_http_client.cpython-310.pyc new file mode 100644 index 0000000..f854f01 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/async_http_client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/http_client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/http_client.cpython-310.pyc new file mode 100644 index 0000000..dd03070 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/http_client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/request.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/request.cpython-310.pyc new file mode 100644 index 0000000..8b9c041 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/request.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000..7458aaa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/validation_client.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/validation_client.cpython-310.pyc new file mode 100644 index 0000000..07792d8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/http/__pycache__/validation_client.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/async_http_client.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/async_http_client.py new file mode 100644 index 0000000..ecd5d4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/async_http_client.py @@ -0,0 +1,135 @@ +import logging +from typing import Dict, Optional, Tuple + +from aiohttp import BasicAuth, ClientSession +from aiohttp_retry import ExponentialRetry, RetryClient + +from twilio.http import AsyncHttpClient +from twilio.http.request import Request as TwilioRequest +from twilio.http.response import Response + +_logger = logging.getLogger("twilio.async_http_client") + + +class AsyncTwilioHttpClient(AsyncHttpClient): + """ + General purpose asynchronous HTTP Client for interacting with the Twilio API + """ + + def __init__( + self, + pool_connections: bool = True, + trace_configs=None, + timeout: Optional[float] = None, + logger: logging.Logger = _logger, + proxy_url: Optional[str] = None, + max_retries: Optional[int] = None, + ): + """ + Constructor for the AsyncTwilioHttpClient + + :param pool_connections: Creates a client session for making requests from. + :param trace_configs: Configuration used to trace request lifecycle events. See aiohttp library TraceConfig + documentation for more info. + :param timeout: Timeout for the requests (seconds) + :param logger + :param proxy_url: Proxy URL + :param max_retries: Maximum number of retries each request should attempt + """ + super().__init__(logger, True, timeout) + self.proxy_url = proxy_url + self.trace_configs = trace_configs + self.session = ( + ClientSession(trace_configs=self.trace_configs) + if pool_connections + else None + ) + if max_retries is not None: + retry_options = ExponentialRetry(attempts=max_retries) + self.session = RetryClient( + client_session=self.session, retry_options=retry_options + ) + + async def request( + self, + method: str, + url: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Make an asynchronous HTTP Request with parameters provided. + + :param method: The HTTP method to use + :param url: The URL to request + :param params: Query parameters to append to the URL + :param data: Parameters to go in the body of the HTTP request + :param headers: HTTP Headers to send with the request + :param auth: Basic Auth arguments (username, password entries) + :param timeout: Socket/Read timeout for the request. Overrides the timeout if set on the client. + :param allow_redirects: Whether or not to allow redirects + See the requests documentation for explanation of all these parameters + + :return: An http response + """ + if timeout is not None and timeout <= 0: + raise ValueError(timeout) + + basic_auth = None + if auth is not None: + basic_auth = BasicAuth(login=auth[0], password=auth[1]) + + kwargs = { + "method": method.upper(), + "url": url, + "params": params, + "data": data, + "headers": headers, + "auth": basic_auth, + "timeout": timeout, + "allow_redirects": allow_redirects, + } + + self.log_request(kwargs) + self._test_only_last_response = None + + temp = False + session = None + if self.session: + session = self.session + else: + session = ClientSession() + temp = True + self._test_only_last_request = TwilioRequest(**kwargs) + response = await session.request(**kwargs) + self.log_response(response.status, response) + self._test_only_last_response = Response( + response.status, await response.text(), response.headers + ) + if temp: + await session.close() + return self._test_only_last_response + + async def close(self): + """ + Closes the HTTP client session + """ + if self.session: + await self.session.close() + + async def __aenter__(self): + """ + Async context manager setup + """ + return self + + async def __aexit__(self, *excinfo): + """ + Async context manager exit + """ + if self.session: + await self.session.close() diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/http_client.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/http_client.py new file mode 100644 index 0000000..7a1715a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/http_client.py @@ -0,0 +1,115 @@ +import logging +from typing import Dict, Optional, Tuple + +from requests import Request, Session, hooks +from requests.adapters import HTTPAdapter + +from twilio.http import HttpClient +from twilio.http.request import Request as TwilioRequest +from twilio.http.response import Response + +_logger = logging.getLogger("twilio.http_client") + + +class TwilioHttpClient(HttpClient): + """ + General purpose HTTP Client for interacting with the Twilio API + """ + + def __init__( + self, + pool_connections: bool = True, + request_hooks: Optional[Dict[str, object]] = None, + timeout: Optional[float] = None, + logger: logging.Logger = _logger, + proxy: Optional[Dict[str, str]] = None, + max_retries: Optional[int] = None, + ): + """ + Constructor for the TwilioHttpClient + :param pool_connections + :param request_hooks + :param timeout: Timeout for the requests. + Timeout should never be zero (0) or less + :param logger + :param proxy: Http proxy for the requests session + :param max_retries: Maximum number of retries each request should attempt + """ + super().__init__(logger, False, timeout) + self.session = Session() if pool_connections else None + if self.session and max_retries is not None: + self.session.mount("https://", HTTPAdapter(max_retries=max_retries)) + + self.request_hooks = request_hooks or hooks.default_hooks() + self.proxy = proxy if proxy else {} + + def request( + self, + method: str, + url: str, + params: Optional[Dict[str, object]] = None, + data: Optional[Dict[str, object]] = None, + headers: Optional[Dict[str, str]] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + allow_redirects: bool = False, + ) -> Response: + """ + Make an HTTP Request with parameters provided. + + :param method: The HTTP method to use + :param url: The URL to request + :param params: Query parameters to append to the URL + :param data: Parameters to go in the body of the HTTP request + :param headers: HTTP Headers to send with the request + :param auth: Basic Auth arguments + :param timeout: Socket/Read timeout for the request + :param allow_redirects: Whether to allow redirects + See the requests documentation for explanation of all these parameters + + :return: An HTTP response + """ + if timeout is None: + timeout = self.timeout + elif timeout <= 0: + raise ValueError(timeout) + + kwargs = { + "method": method.upper(), + "url": url, + "params": params, + "headers": headers, + "auth": auth, + "hooks": self.request_hooks, + } + if headers and headers.get("Content-Type") == "application/json": + kwargs["json"] = data + else: + kwargs["data"] = data + self.log_request(kwargs) + + self._test_only_last_response = None + session = self.session or Session() + request = Request(**kwargs) + self._test_only_last_request = TwilioRequest(**kwargs) + + prepped_request = session.prepare_request(request) + + settings = session.merge_environment_settings( + prepped_request.url, self.proxy, None, None, None + ) + + response = session.send( + prepped_request, + allow_redirects=allow_redirects, + timeout=timeout, + **settings + ) + + self.log_response(response.status_code, response) + + self._test_only_last_response = Response( + int(response.status_code), response.text, response.headers + ) + + return self._test_only_last_response diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/request.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/request.py new file mode 100644 index 0000000..e75cf12 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/request.py @@ -0,0 +1,91 @@ +from enum import Enum +from typing import Any, Dict, Tuple, Union +from urllib.parse import urlencode + + +class Match(Enum): + ANY = "*" + + +class Request(object): + """ + An HTTP request. + """ + + def __init__( + self, + method: Union[str, Match] = Match.ANY, + url: Union[str, Match] = Match.ANY, + auth: Union[Tuple[str, str], Match] = Match.ANY, + params: Union[Dict[str, str], Match] = Match.ANY, + data: Union[Dict[str, str], Match] = Match.ANY, + headers: Union[Dict[str, str], Match] = Match.ANY, + **kwargs: Any + ): + self.method = method + if method and method is not Match.ANY: + self.method = method.upper() + self.url = url + self.auth = auth + self.params = params + self.data = data + self.headers = headers + + @classmethod + def attribute_equal(cls, lhs, rhs) -> bool: + if lhs == Match.ANY or rhs == Match.ANY: + # ANY matches everything + return True + + lhs = lhs or None + rhs = rhs or None + + return lhs == rhs + + def __eq__(self, other) -> bool: + if not isinstance(other, Request): + return False + + return ( + self.attribute_equal(self.method, other.method) + and self.attribute_equal(self.url, other.url) + and self.attribute_equal(self.auth, other.auth) + and self.attribute_equal(self.params, other.params) + and self.attribute_equal(self.data, other.data) + and self.attribute_equal(self.headers, other.headers) + ) + + def __str__(self) -> str: + auth = "" + if self.auth and self.auth != Match.ANY: + auth = "{} ".format(self.auth) + + params = "" + if self.params and self.params != Match.ANY: + params = "?{}".format(urlencode(self.params, doseq=True)) + + data = "" + if self.data and self.data != Match.ANY: + if self.method == "GET": + data = "\n -G" + data += "\n{}".format( + "\n".join(' -d "{}={}"'.format(k, v) for k, v in self.data.items()) + ) + + headers = "" + if self.headers and self.headers != Match.ANY: + headers = "\n{}".format( + "\n".join(' -H "{}: {}"'.format(k, v) for k, v in self.headers.items()) + ) + + return "{auth}{method} {url}{params}{data}{headers}".format( + auth=auth, + method=self.method, + url=self.url, + params=params, + data=data, + headers=headers, + ) + + def __repr__(self) -> str: + return str(self) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/response.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/response.py new file mode 100644 index 0000000..af5a3b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/response.py @@ -0,0 +1,22 @@ +from typing import Any, Optional + + +class Response(object): + def __init__( + self, + status_code: int, + text: str, + headers: Optional[Any] = None, + ): + self.content = text + self.headers = headers + self.cached = False + self.status_code = status_code + self.ok = self.status_code < 400 + + @property + def text(self) -> str: + return self.content + + def __repr__(self) -> str: + return "HTTP {} {}".format(self.status_code, self.content) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/http/validation_client.py b/.pythonlibs/lib/python3.10/site-packages/twilio/http/validation_client.py new file mode 100644 index 0000000..1a4a83f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/http/validation_client.py @@ -0,0 +1,138 @@ +from collections import namedtuple + +from requests import Request, Session + +from twilio.base.exceptions import TwilioRestException +from urllib.parse import urlparse +from twilio.http import HttpClient +from twilio.http.response import Response +from twilio.jwt.validation import ClientValidationJwt + + +ValidationPayload = namedtuple( + "ValidationPayload", + ["method", "path", "query_string", "all_headers", "signed_headers", "body"], +) + + +class ValidationClient(HttpClient): + __SIGNED_HEADERS = ["authorization", "host"] + + def __init__( + self, + account_sid, + api_key_sid, + credential_sid, + private_key, + pool_connections=True, + ): + """ + Build a ValidationClient which signs requests with private_key and allows Twilio to + validate request has not been tampered with. + + :param str account_sid: A Twilio Account Sid starting with 'AC' + :param str api_key_sid: A Twilio API Key Sid starting with 'SK' + :param str credential_sid: A Credential Sid starting with 'CR', + corresponds to public key Twilio will use to verify the JWT. + :param str private_key: The private key used to sign the Client Validation JWT. + """ + self.account_sid = account_sid + self.credential_sid = credential_sid + self.api_key_sid = api_key_sid + self.private_key = private_key + self.session = Session() if pool_connections else None + + def request( + self, + method, + url, + params=None, + data=None, + headers=None, + auth=None, + timeout=None, + allow_redirects=False, + ): + """ + Make a signed HTTP Request + + :param str method: The HTTP method to use + :param str url: The URL to request + :param dict params: Query parameters to append to the URL + :param dict data: Parameters to go in the body of the HTTP request + :param dict headers: HTTP Headers to send with the request + :param tuple auth: Basic Auth arguments + :param float timeout: Socket/Read timeout for the request + :param boolean allow_redirects: Whether or not to allow redirects + See the requests documentation for explanation of all these parameters + + :return: An http response + :rtype: A :class:`Response ` object + """ + session = self.session or Session() + request = Request( + method.upper(), url, params=params, data=data, headers=headers, auth=auth + ) + prepared_request = session.prepare_request(request) + + if ( + "Host" not in prepared_request.headers + and "host" not in prepared_request.headers + ): + prepared_request.headers["Host"] = self._get_host(prepared_request) + + validation_payload = self._build_validation_payload(prepared_request) + jwt = ClientValidationJwt( + self.account_sid, + self.api_key_sid, + self.credential_sid, + self.private_key, + validation_payload, + ) + prepared_request.headers["Twilio-Client-Validation"] = jwt.to_jwt() + + response = session.send( + prepared_request, + allow_redirects=allow_redirects, + timeout=timeout, + ) + + return Response(int(response.status_code), response.text) + + def _build_validation_payload(self, request): + """ + Extract relevant information from request to build a ClientValidationJWT + :param PreparedRequest request: request we will extract information from. + :return: ValidationPayload + """ + parsed = urlparse(request.url) + path = parsed.path + query_string = parsed.query or "" + + return ValidationPayload( + method=request.method, + path=path, + query_string=query_string, + all_headers=request.headers, + signed_headers=ValidationClient.__SIGNED_HEADERS, + body=request.body or "", + ) + + def _get_host(self, request): + """Pull the Host out of the request""" + parsed = urlparse(request.url) + return str(parsed.netloc) + + def validate_ssl_certificate(self, client): + """ + Validate that a request to the new SSL certificate is successful + :return: null on success, raise TwilioRestException if the request fails + """ + response = client.request("GET", "https://tls-test.twilio.com:443") + + if response.status_code < 200 or response.status_code >= 300: + raise TwilioRestException( + response.status_code, + "https://tls-test.twilio.com:443", + "Failed to validate SSL certificate", + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__init__.py new file mode 100644 index 0000000..7a51ea7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__init__.py @@ -0,0 +1,161 @@ +import jwt as jwt_lib +import time + + +__all__ = ["Jwt", "JwtDecodeError"] + + +class JwtDecodeError(Exception): + pass + + +class Jwt(object): + """Base class for building a Json Web Token""" + + GENERATE = object() + ALGORITHM = "HS256" + + def __init__( + self, + secret_key, + issuer, + subject=None, + algorithm=None, + nbf=GENERATE, + ttl=3600, + valid_until=None, + ): + self.secret_key = secret_key + """:type str: The secret used to encode the JWT""" + self.issuer = issuer + """:type str: The issuer of this JWT""" + self.subject = subject + """:type str: The subject of this JWT, omitted from payload by default""" + self.algorithm = algorithm or self.ALGORITHM + """:type str: The algorithm used to encode the JWT, defaults to 'HS256'""" + self.nbf = nbf + """:type int: Time in secs since epoch before which this JWT is invalid. Defaults to now.""" + self.ttl = ttl + """:type int: Time to live of the JWT in seconds, defaults to 1 hour""" + self.valid_until = valid_until + """:type int: Time in secs since epoch this JWT is valid for. Overrides ttl if provided.""" + + self.__decoded_payload = None + self.__decoded_headers = None + + def _generate_payload(self): + """:rtype: dict the payload of the JWT to send""" + raise NotImplementedError("Subclass must provide a payload.") + + def _generate_headers(self): + """:rtype dict: Additional headers to include in the JWT, defaults to an empty dict""" + return {} + + @classmethod + def _from_jwt(cls, headers, payload, key=None): + """ + Class specific implementation of from_jwt which should take jwt components and return + and instance of this Class with jwt information loaded. + :return: Jwt object containing the headers, payload and key + """ + jwt = Jwt( + secret_key=key, + issuer=payload.get("iss", None), + subject=payload.get("sub", None), + algorithm=headers.get("alg", None), + valid_until=payload.get("exp", None), + nbf=payload.get("nbf", None), + ) + jwt.__decoded_payload = payload + jwt.__decoded_headers = headers + return jwt + + @property + def payload(self): + if self.__decoded_payload: + return self.__decoded_payload + + payload = self._generate_payload().copy() + payload["iss"] = self.issuer + payload["exp"] = int(time.time()) + self.ttl + if self.nbf is not None: + if self.nbf == self.GENERATE: + payload["nbf"] = int(time.time()) + else: + payload["nbf"] = self.nbf + if self.valid_until: + payload["exp"] = self.valid_until + if self.subject: + payload["sub"] = self.subject + + return payload + + @property + def headers(self): + if self.__decoded_headers: + return self.__decoded_headers + + headers = self._generate_headers().copy() + headers["typ"] = "JWT" + headers["alg"] = self.algorithm + return headers + + def to_jwt(self, ttl=None): + """ + Encode this JWT object into a JWT string + :param int ttl: override the ttl configured in the constructor + :rtype: str The JWT string + """ + + if not self.secret_key: + raise ValueError("JWT does not have a signing key configured.") + + headers = self.headers.copy() + + payload = self.payload.copy() + if ttl: + payload["exp"] = int(time.time()) + ttl + + return jwt_lib.encode( + payload, self.secret_key, algorithm=self.algorithm, headers=headers + ) + + @classmethod + def from_jwt(cls, jwt, key=""): + """ + Decode a JWT string into a Jwt object + :param str jwt: JWT string + :param Optional[str] key: key used to verify JWT signature, if not provided then validation + is skipped. + :raises JwtDecodeError if decoding JWT fails for any reason. + :return: A DecodedJwt object containing the jwt information. + """ + verify = True if key else False + + try: + headers = jwt_lib.get_unverified_header(jwt) + + alg = headers.get("alg") + if alg != cls.ALGORITHM: + raise ValueError( + f"Incorrect decoding algorithm {alg}, " + f"expecting {cls.ALGORITHM}." + ) + + payload = jwt_lib.decode( + jwt, + key, + algorithms=[cls.ALGORITHM], + options={ + "verify_signature": verify, + "verify_exp": True, + "verify_nbf": True, + }, + ) + except Exception as e: + raise JwtDecodeError(getattr(e, "message", str(e))) + + return cls._from_jwt(headers, payload, key) + + def __str__(self): + return "".format(self.to_jwt()) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..11c45c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__init__.py new file mode 100644 index 0000000..764b2a0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__init__.py @@ -0,0 +1,81 @@ +import time + +from twilio.jwt import Jwt + + +class AccessTokenGrant(object): + """A Grant giving access to a Twilio Resource""" + + @property + def key(self): + """:rtype str Grant's twilio specific key""" + raise NotImplementedError("Grant must have a key property.") + + def to_payload(self): + """:return: dict something""" + raise NotImplementedError("Grant must implement to_payload.") + + def __str__(self): + return "<{} {}>".format(self.__class__.__name__, self.to_payload()) + + +class AccessToken(Jwt): + """Access Token containing one or more AccessTokenGrants used to access Twilio Resources""" + + ALGORITHM = "HS256" + + def __init__( + self, + account_sid, + signing_key_sid, + secret, + grants=None, + identity=None, + nbf=Jwt.GENERATE, + ttl=3600, + valid_until=None, + region=None, + ): + grants = grants or [] + if any(not isinstance(g, AccessTokenGrant) for g in grants): + raise ValueError("Grants must be instances of AccessTokenGrant.") + + self.account_sid = account_sid + self.signing_key_sid = signing_key_sid + self.identity = identity + self.region = region + self.grants = grants + super(AccessToken, self).__init__( + secret_key=secret, + algorithm=self.ALGORITHM, + issuer=signing_key_sid, + subject=self.account_sid, + nbf=nbf, + ttl=ttl, + valid_until=valid_until, + ) + + def add_grant(self, grant): + """Add a grant to this AccessToken""" + if not isinstance(grant, AccessTokenGrant): + raise ValueError("Grant must be an instance of AccessTokenGrant.") + self.grants.append(grant) + + def _generate_headers(self): + headers = {"cty": "twilio-fpa;v=1"} + if self.region and isinstance(self.region, str): + headers["twr"] = self.region + return headers + + def _generate_payload(self): + now = int(time.time()) + payload = { + "jti": "{}-{}".format(self.signing_key_sid, now), + "grants": {grant.key: grant.to_payload() for grant in self.grants}, + } + if self.identity: + payload["grants"]["identity"] = self.identity + return payload + + def __str__(self): + return "<{} {}>".format(self.__class__.__name__, self.to_jwt()) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..411b3a2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/grants.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/grants.cpython-310.pyc new file mode 100644 index 0000000..bbc8433 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/__pycache__/grants.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/grants.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/grants.py new file mode 100644 index 0000000..16d19aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/access_token/grants.py @@ -0,0 +1,183 @@ +from twilio.jwt.access_token import AccessTokenGrant +import warnings +import functools + + +def deprecated(func): + """This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used.""" + + @functools.wraps(func) + def new_func(*args, **kwargs): + warnings.simplefilter("always", DeprecationWarning) + warnings.warn( + "Call to deprecated function {}.".format(func.__name__), + category=DeprecationWarning, + stacklevel=2, + ) + warnings.simplefilter("default", DeprecationWarning) + return func(*args, **kwargs) + + return new_func + + +class ChatGrant(AccessTokenGrant): + """Grant to access Twilio Chat""" + + def __init__( + self, + service_sid=None, + endpoint_id=None, + deployment_role_sid=None, + push_credential_sid=None, + ): + self.service_sid = service_sid + self.endpoint_id = endpoint_id + self.deployment_role_sid = deployment_role_sid + self.push_credential_sid = push_credential_sid + + @property + def key(self): + return "chat" + + def to_payload(self): + grant = {} + if self.service_sid: + grant["service_sid"] = self.service_sid + if self.endpoint_id: + grant["endpoint_id"] = self.endpoint_id + if self.deployment_role_sid: + grant["deployment_role_sid"] = self.deployment_role_sid + if self.push_credential_sid: + grant["push_credential_sid"] = self.push_credential_sid + + return grant + + +class SyncGrant(AccessTokenGrant): + """Grant to access Twilio Sync""" + + def __init__(self, service_sid=None, endpoint_id=None): + self.service_sid = service_sid + self.endpoint_id = endpoint_id + + @property + def key(self): + return "data_sync" + + def to_payload(self): + grant = {} + if self.service_sid: + grant["service_sid"] = self.service_sid + if self.endpoint_id: + grant["endpoint_id"] = self.endpoint_id + + return grant + + +class VoiceGrant(AccessTokenGrant): + """Grant to access Twilio Programmable Voice""" + + def __init__( + self, + incoming_allow=None, + outgoing_application_sid=None, + outgoing_application_params=None, + push_credential_sid=None, + endpoint_id=None, + ): + self.incoming_allow = incoming_allow + """ :type : bool """ + self.outgoing_application_sid = outgoing_application_sid + """ :type : str """ + self.outgoing_application_params = outgoing_application_params + """ :type : dict """ + self.push_credential_sid = push_credential_sid + """ :type : str """ + self.endpoint_id = endpoint_id + """ :type : str """ + + @property + def key(self): + return "voice" + + def to_payload(self): + grant = {} + if self.incoming_allow is True: + grant["incoming"] = {} + grant["incoming"]["allow"] = True + + if self.outgoing_application_sid: + grant["outgoing"] = {} + grant["outgoing"]["application_sid"] = self.outgoing_application_sid + + if self.outgoing_application_params: + grant["outgoing"]["params"] = self.outgoing_application_params + + if self.push_credential_sid: + grant["push_credential_sid"] = self.push_credential_sid + + if self.endpoint_id: + grant["endpoint_id"] = self.endpoint_id + + return grant + + +class VideoGrant(AccessTokenGrant): + """Grant to access Twilio Video""" + + def __init__(self, room=None): + self.room = room + + @property + def key(self): + return "video" + + def to_payload(self): + grant = {} + if self.room: + grant["room"] = self.room + + return grant + + +class TaskRouterGrant(AccessTokenGrant): + """Grant to access Twilio TaskRouter""" + + def __init__(self, workspace_sid=None, worker_sid=None, role=None): + self.workspace_sid = workspace_sid + self.worker_sid = worker_sid + self.role = role + + @property + def key(self): + return "task_router" + + def to_payload(self): + grant = {} + if self.workspace_sid: + grant["workspace_sid"] = self.workspace_sid + if self.worker_sid: + grant["worker_sid"] = self.worker_sid + if self.role: + grant["role"] = self.role + + return grant + + +class PlaybackGrant(AccessTokenGrant): + """Grant to access Twilio Live stream""" + + def __init__(self, grant=None): + """Initialize a PlaybackGrant with a grant retrieved from the Twilio API.""" + self.grant = grant + + @property + def key(self): + """Return the grant's key.""" + return "player" + + def to_payload(self): + """Return the grant.""" + return self.grant diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__init__.py new file mode 100644 index 0000000..d5c38c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__init__.py @@ -0,0 +1,117 @@ +from twilio.jwt import Jwt + +from urllib.parse import urlencode + + +class ClientCapabilityToken(Jwt): + """A token to control permissions with Twilio Client""" + + ALGORITHM = "HS256" + + def __init__( + self, + account_sid, + auth_token, + nbf=Jwt.GENERATE, + ttl=3600, + valid_until=None, + **kwargs + ): + """ + :param str account_sid: The account sid to which this token is granted access. + :param str auth_token: The secret key used to sign the token. Note, this auth token is not + visible to the user of the token. + :param int nbf: Time in secs from epic before which this token is considered invalid. + :param int ttl: the amount of time in seconds from generation that this token is valid for. + :param kwargs: + + + :returns: A new CapabilityToken with zero permissions + """ + super(ClientCapabilityToken, self).__init__( + algorithm=self.ALGORITHM, + secret_key=auth_token, + issuer=account_sid, + nbf=nbf, + ttl=ttl, + valid_until=None, + ) + + self.account_sid = account_sid + self.auth_token = auth_token + self.client_name = None + self.capabilities = {} + + if "allow_client_outgoing" in kwargs: + self.allow_client_outgoing(**kwargs["allow_client_outgoing"]) + if "allow_client_incoming" in kwargs: + self.allow_client_incoming(**kwargs["allow_client_incoming"]) + if "allow_event_stream" in kwargs: + self.allow_event_stream(**kwargs["allow_event_stream"]) + + def allow_client_outgoing(self, application_sid, **kwargs): + """ + Allow the user of this token to make outgoing connections. Keyword arguments are passed + to the application. + + :param str application_sid: Application to contact + """ + scope = ScopeURI("client", "outgoing", {"appSid": application_sid}) + if kwargs: + scope.add_param("appParams", urlencode(kwargs, doseq=True)) + + self.capabilities["outgoing"] = scope + + def allow_client_incoming(self, client_name): + """ + Allow the user of this token to accept incoming connections. + + :param str client_name: Client name to accept calls from + """ + self.client_name = client_name + self.capabilities["incoming"] = ScopeURI( + "client", "incoming", {"clientName": client_name} + ) + + def allow_event_stream(self, **kwargs): + """ + Allow the user of this token to access their event stream. + """ + scope = ScopeURI("stream", "subscribe", {"path": "/2010-04-01/Events"}) + if kwargs: + scope.add_param("params", urlencode(kwargs, doseq=True)) + + self.capabilities["events"] = scope + + def _generate_payload(self): + if "outgoing" in self.capabilities and self.client_name is not None: + self.capabilities["outgoing"].add_param("clientName", self.client_name) + + scope_uris = [ + scope_uri.to_payload() for scope_uri in self.capabilities.values() + ] + return {"scope": " ".join(scope_uris)} + + +class ScopeURI(object): + """A single capability granted to Twilio Client and scoped to a service""" + + def __init__(self, service, privilege, params=None): + self.service = service + self.privilege = privilege + self.params = params or {} + + def add_param(self, key, value): + self.params[key] = value + + def to_payload(self): + if self.params: + sorted_params = sorted([(k, v) for k, v in self.params.items()]) + encoded_params = urlencode(sorted_params) + param_string = "?{}".format(encoded_params) + else: + param_string = "" + return "scope:{}:{}{}".format(self.service, self.privilege, param_string) + + def __str__(self): + return "".format(self.to_payload()) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..895802d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/client/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__init__.py new file mode 100644 index 0000000..5a01560 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__init__.py @@ -0,0 +1,142 @@ +from twilio.jwt import Jwt + + +class TaskRouterCapabilityToken(Jwt): + VERSION = "v1" + DOMAIN = "https://taskrouter.twilio.com" + EVENTS_BASE_URL = "https://event-bridge.twilio.com/v1/wschannels" + ALGORITHM = "HS256" + + def __init__(self, account_sid, auth_token, workspace_sid, channel_id, **kwargs): + """ + :param str account_sid: Twilio account sid + :param str auth_token: Twilio auth token used to sign the JWT + :param str workspace_sid: TaskRouter workspace sid + :param str channel_id: TaskRouter channel sid + :param kwargs: + :param bool allow_web_sockets: shortcut to calling allow_web_sockets, defaults to True + :param bool allow_fetch_self: shortcut to calling allow_fetch_self, defaults to True + :param bool allow_update_self: shortcut to calling allow_update_self, defaults to False + :param bool allow_delete_self: shortcut to calling allow_delete_self, defaults to False + :param bool allow_fetch_subresources: shortcut to calling allow_fetch_subresources, + defaults to False + :param bool allow_update_subresources: shortcut to calling allow_update_subresources, + defaults to False + :param bool allow_delete_subresources: shortcut to calling allow_delete_subresources, + defaults to False + :returns a new TaskRouterCapabilityToken with capabilities set depending on kwargs. + """ + super(TaskRouterCapabilityToken, self).__init__( + secret_key=auth_token, + issuer=account_sid, + algorithm=self.ALGORITHM, + nbf=kwargs.get("nbf", Jwt.GENERATE), + ttl=kwargs.get("ttl", 3600), + valid_until=kwargs.get("valid_until", None), + ) + + self._validate_inputs(account_sid, workspace_sid, channel_id) + + self.account_sid = account_sid + self.auth_token = auth_token + self.workspace_sid = workspace_sid + self.channel_id = channel_id + self.policies = [] + + if kwargs.get("allow_web_sockets", True): + self.allow_web_sockets() + if kwargs.get("allow_fetch_self", True): + self.allow_fetch_self() + if kwargs.get("allow_update_self", False): + self.allow_update_self() + if kwargs.get("allow_delete_self", False): + self.allow_delete_self() + if kwargs.get("allow_fetch_subresources", False): + self.allow_fetch_subresources() + if kwargs.get("allow_delete_subresources", False): + self.allow_delete_subresources() + if kwargs.get("allow_update_subresources", False): + self.allow_update_subresources() + + @property + def workspace_url(self): + return "{}/{}/Workspaces/{}".format( + self.DOMAIN, self.VERSION, self.workspace_sid + ) + + @property + def resource_url(self): + raise NotImplementedError("Subclass must set its specific resource_url.") + + @property + def channel_prefix(self): + raise NotImplementedError( + "Subclass must set its specific channel_id sid prefix." + ) + + def allow_fetch_self(self): + self._make_policy(self.resource_url, "GET", True) + + def allow_update_self(self): + self._make_policy(self.resource_url, "POST", True) + + def allow_delete_self(self): + self._make_policy(self.resource_url, "DELETE", True) + + def allow_fetch_subresources(self): + self._make_policy(self.resource_url + "/**", "GET", True) + + def allow_update_subresources(self): + self._make_policy(self.resource_url + "/**", "POST", True) + + def allow_delete_subresources(self): + self._make_policy(self.resource_url + "/**", "DELETE", True) + + def allow_web_sockets(self, channel_id=None): + channel_id = channel_id or self.channel_id + web_socket_url = "{}/{}/{}".format( + self.EVENTS_BASE_URL, self.account_sid, channel_id + ) + self._make_policy(web_socket_url, "GET", True) + self._make_policy(web_socket_url, "POST", True) + + def _generate_payload(self): + payload = { + "account_sid": self.account_sid, + "workspace_sid": self.workspace_sid, + "channel": self.channel_id, + "version": self.VERSION, + "friendly_name": self.channel_id, + "policies": self.policies, + } + + if self.channel_id.startswith("WK"): + payload["worker_sid"] = self.channel_id + elif self.channel_id.startswith("WQ"): + payload["taskqueue_sid"] = self.channel_id + + return payload + + def _make_policy(self, url, method, allowed, query_filter=None, post_filter=None): + self.policies.append( + { + "url": url, + "method": method.upper(), + "allow": allowed, + "query_filter": query_filter or {}, + "post_filter": post_filter or {}, + } + ) + + def _validate_inputs(self, account_sid, workspace_sid, channel_id): + if not account_sid or not account_sid.startswith("AC"): + raise ValueError("Invalid account sid provided {}".format(account_sid)) + + if not workspace_sid or not workspace_sid.startswith("WS"): + raise ValueError("Invalid workspace sid provided {}".format(workspace_sid)) + + if not channel_id or not channel_id.startswith(self.channel_prefix): + raise ValueError("Invalid channel id provided {}".format(channel_id)) + + def __str__(self): + return "".format(self.to_jwt()) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1ea8cc3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/capabilities.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/capabilities.cpython-310.pyc new file mode 100644 index 0000000..73e2342 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/__pycache__/capabilities.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/capabilities.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/capabilities.py new file mode 100644 index 0000000..468a027 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/taskrouter/capabilities.py @@ -0,0 +1,116 @@ +from twilio.jwt.taskrouter import TaskRouterCapabilityToken + + +class WorkerCapabilityToken(TaskRouterCapabilityToken): + def __init__( + self, account_sid, auth_token, workspace_sid, worker_sid, ttl=3600, **kwargs + ): + """ + :param kwargs: + All kwarg parameters supported by TaskRouterCapabilityToken + :param bool allow_fetch_activities: shortcut to calling allow_fetch_activities, + defaults to True + :param bool allow_fetch_reservations: shortcut to calling allow_fetch_reservations, + defaults to True + :param bool allow_fetch_worker_reservations: shortcut to calling allow_fetch_worker_reservations, + defaults to True + :param bool allow_update_activities: shortcut to calling allow_update_activities, + defaults to False + :param bool allow_update_reservations: shortcut to calling allow_update_reservations, + defaults to False + """ + super(WorkerCapabilityToken, self).__init__( + account_sid=account_sid, + auth_token=auth_token, + workspace_sid=workspace_sid, + channel_id=worker_sid, + ttl=ttl, + **kwargs + ) + + if kwargs.get("allow_fetch_activities", True): + self.allow_fetch_activities() + if kwargs.get("allow_fetch_reservations", True): + self.allow_fetch_reservations() + if kwargs.get("allow_fetch_worker_reservations", True): + self.allow_fetch_worker_reservations() + if kwargs.get("allow_update_activities", False): + self.allow_update_activities() + if kwargs.get("allow_update_reservations", False): + self.allow_update_reservations() + + @property + def resource_url(self): + return "{}/Workers/{}".format(self.workspace_url, self.channel_id) + + @property + def channel_prefix(self): + return "WK" + + def allow_fetch_activities(self): + self._make_policy(self.workspace_url + "/Activities", "GET", True) + + def allow_fetch_reservations(self): + self._make_policy(self.workspace_url + "/Tasks/**", "GET", True) + + def allow_fetch_worker_reservations(self): + self._make_policy(self.resource_url + "/Reservations/**", "GET", True) + + def allow_update_activities(self): + post_filter = {"ActivitySid": {"required": True}} + self._make_policy(self.resource_url, "POST", True, post_filter=post_filter) + + def allow_update_reservations(self): + self._make_policy(self.workspace_url + "/Tasks/**", "POST", True) + self._make_policy(self.resource_url + "/Reservations/**", "POST", True) + + def __str__(self): + return "".format(self.to_jwt()) + + +class TaskQueueCapabilityToken(TaskRouterCapabilityToken): + def __init__( + self, account_sid, auth_token, workspace_sid, task_queue_sid, ttl=3600, **kwargs + ): + super(TaskQueueCapabilityToken, self).__init__( + account_sid=account_sid, + auth_token=auth_token, + workspace_sid=workspace_sid, + channel_id=task_queue_sid, + ttl=ttl, + **kwargs + ) + + @property + def resource_url(self): + return "{}/TaskQueues/{}".format(self.workspace_url, self.channel_id) + + @property + def channel_prefix(self): + return "WQ" + + def __str__(self): + return "".format(self.to_jwt()) + + +class WorkspaceCapabilityToken(TaskRouterCapabilityToken): + def __init__(self, account_sid, auth_token, workspace_sid, ttl=3600, **kwargs): + super(WorkspaceCapabilityToken, self).__init__( + account_sid=account_sid, + auth_token=auth_token, + workspace_sid=workspace_sid, + channel_id=workspace_sid, + ttl=ttl, + **kwargs + ) + + @property + def resource_url(self): + return self.workspace_url + + @property + def channel_prefix(self): + return "WS" + + def __str__(self): + return "".format(self.to_jwt()) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__init__.py new file mode 100644 index 0000000..837d619 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__init__.py @@ -0,0 +1,92 @@ +from hashlib import sha256 + +from twilio.jwt import Jwt + + +class ClientValidationJwt(Jwt): + """A JWT included on requests so that Twilio can verify request authenticity""" + + __CTY = "twilio-pkrv;v=1" + ALGORITHM = "RS256" + + def __init__( + self, account_sid, api_key_sid, credential_sid, private_key, validation_payload + ): + """ + Create a new ClientValidationJwt + :param str account_sid: A Twilio Account Sid starting with 'AC' + :param str api_key_sid: A Twilio API Key Sid starting with 'SK' + :param str credential_sid: A Credential Sid starting with 'CR', + public key Twilio will use to verify the JWT. + :param str private_key: The private key used to sign the JWT. + :param ValidationPayload validation_payload: information from the request to sign + """ + super(ClientValidationJwt, self).__init__( + secret_key=private_key, + issuer=api_key_sid, + subject=account_sid, + algorithm=self.ALGORITHM, + ttl=300, # 5 minute ttl + ) + self.credential_sid = credential_sid + self.validation_payload = validation_payload + + def _generate_headers(self): + return {"cty": ClientValidationJwt.__CTY, "kid": self.credential_sid} + + def _generate_payload(self): + # Lowercase header keys, combine and sort headers with list values + all_headers = { + k.lower(): self._sort_and_join(v, ",") + for k, v in self.validation_payload.all_headers.items() + } + # Names of headers we are signing in the jwt + signed_headers = sorted(self.validation_payload.signed_headers) + + # Stringify headers, only include headers in signed_headers + headers_str = [ + "{}:{}".format(h, all_headers[h]) + for h in signed_headers + if h in all_headers + ] + headers_str = "\n".join(headers_str) + + # Sort query string parameters + query_string = self.validation_payload.query_string.split("&") + query_string = self._sort_and_join(query_string, "&") + + req_body_hash = self._hash(self.validation_payload.body) or "" + + signed_headers_str = ";".join(signed_headers) + + signed_payload = [ + self.validation_payload.method, + self.validation_payload.path, + query_string, + ] + + if headers_str: + signed_payload.append(headers_str) + signed_payload.append("") + signed_payload.append(signed_headers_str) + signed_payload.append(req_body_hash) + + signed_payload = "\n".join(signed_payload) + + return {"hrh": signed_headers_str, "rqh": self._hash(signed_payload)} + + @classmethod + def _sort_and_join(cls, values, joiner): + if isinstance(values, str): + return values + return joiner.join(sorted(values)) + + @classmethod + def _hash(cls, input_str): + if not input_str: + return input_str + + if not isinstance(input_str, bytes): + input_str = input_str.encode("utf-8") + + return sha256(input_str).hexdigest() diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1085507 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/jwt/validation/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/request_validator.py b/.pythonlibs/lib/python3.10/site-packages/twilio/request_validator.py new file mode 100644 index 0000000..f7a0db3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/request_validator.py @@ -0,0 +1,137 @@ +import base64 +import hmac +from hashlib import sha1, sha256 + +from urllib.parse import urlparse, parse_qs + + +def compare(string1, string2): + """Compare two strings while protecting against timing attacks + + :param str string1: the first string + :param str string2: the second string + + :returns: True if the strings are equal, False if not + :rtype: :obj:`bool` + """ + if len(string1) != len(string2): + return False + result = True + for c1, c2 in zip(string1, string2): + result &= c1 == c2 + + return result + + +def remove_port(uri): + """Remove the port number from a URI + + :param uri: parsed URI that Twilio requested on your server + + :returns: full URI without a port number + :rtype: str + """ + if not uri.port: + return uri.geturl() + + new_netloc = uri.netloc.split(":")[0] + new_uri = uri._replace(netloc=new_netloc) + + return new_uri.geturl() + + +def add_port(uri): + """Add the port number to a URI + + :param uri: parsed URI that Twilio requested on your server + + :returns: full URI with a port number + :rtype: str + """ + if uri.port: + return uri.geturl() + + port = 443 if uri.scheme == "https" else 80 + new_netloc = uri.netloc + ":" + str(port) + new_uri = uri._replace(netloc=new_netloc) + + return new_uri.geturl() + + +class RequestValidator(object): + def __init__(self, token): + self.token = token.encode("utf-8") + + def compute_signature(self, uri, params): + """Compute the signature for a given request + + :param uri: full URI that Twilio requested on your server + :param params: post vars that Twilio sent with the request + + :returns: The computed signature + """ + s = uri + if params: + for param_name in sorted(set(params)): + values = self.get_values(params, param_name) + + for value in sorted(set(values)): + s += param_name + value + + # compute signature and compare signatures + mac = hmac.new(self.token, s.encode("utf-8"), sha1) + computed = base64.b64encode(mac.digest()) + computed = computed.decode("utf-8") + + return computed.strip() + + def get_values(self, param_dict, param_name): + try: + # Support MultiDict used by Flask. + return param_dict.getall(param_name) + except AttributeError: + try: + # Support QueryDict used by Django. + return param_dict.getlist(param_name) + except AttributeError: + # Fallback to a standard dict. + return [param_dict[param_name]] + + def compute_hash(self, body): + computed = sha256(body.encode("utf-8")).hexdigest() + + return computed.strip() + + def validate(self, uri, params, signature): + """Validate a request from Twilio + + :param uri: full URI that Twilio requested on your server + :param params: dictionary of POST variables or string of POST body for JSON requests + :param signature: expected signature in HTTP X-Twilio-Signature header + + :returns: True if the request passes validation, False if not + """ + if params is None: + params = {} + + parsed_uri = urlparse(uri) + uri_with_port = add_port(parsed_uri) + uri_without_port = remove_port(parsed_uri) + + valid_body_hash = True # May not receive body hash, so default succeed + + query = parse_qs(parsed_uri.query) + if "bodySHA256" in query and isinstance(params, str): + valid_body_hash = compare(self.compute_hash(params), query["bodySHA256"][0]) + params = {} + + # check signature of uri with and without port, + # since sig generation on back end is inconsistent + valid_signature = compare( + self.compute_signature(uri_without_port, params), signature + ) + valid_signature_with_port = compare( + self.compute_signature(uri_with_port, params), signature + ) + + return valid_body_hash and (valid_signature or valid_signature_with_port) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__init__.py new file mode 100644 index 0000000..c7f7226 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__init__.py @@ -0,0 +1,710 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import TYPE_CHECKING, Optional + +from twilio.base.client_base import ClientBase + +if TYPE_CHECKING: + from twilio.rest.accounts import Accounts + from twilio.rest.api import Api + from twilio.rest.bulkexports import Bulkexports + from twilio.rest.chat import Chat + from twilio.rest.content import Content + from twilio.rest.conversations import Conversations + from twilio.rest.events import Events + from twilio.rest.flex_api import FlexApi + from twilio.rest.frontline_api import FrontlineApi + from twilio.rest.insights import Insights + from twilio.rest.intelligence import Intelligence + from twilio.rest.ip_messaging import IpMessaging + from twilio.rest.lookups import Lookups + from twilio.rest.preview_messaging import PreviewMessaging + from twilio.rest.messaging import Messaging + from twilio.rest.microvisor import Microvisor + from twilio.rest.monitor import Monitor + from twilio.rest.notify import Notify + from twilio.rest.numbers import Numbers + from twilio.rest.oauth import Oauth + from twilio.rest.preview import Preview + from twilio.rest.pricing import Pricing + from twilio.rest.proxy import Proxy + from twilio.rest.routes import Routes + from twilio.rest.serverless import Serverless + from twilio.rest.studio import Studio + from twilio.rest.supersim import Supersim + from twilio.rest.sync import Sync + from twilio.rest.taskrouter import Taskrouter + from twilio.rest.trunking import Trunking + from twilio.rest.trusthub import Trusthub + from twilio.rest.verify import Verify + from twilio.rest.video import Video + from twilio.rest.voice import Voice + from twilio.rest.wireless import Wireless + from twilio.rest.api.v2010.account.address import AddressList + from twilio.rest.api.v2010.account.application import ApplicationList + from twilio.rest.api.v2010.account.authorized_connect_app import ( + AuthorizedConnectAppList, + ) + from twilio.rest.api.v2010.account.available_phone_number_country import ( + AvailablePhoneNumberCountryList, + ) + from twilio.rest.api.v2010.account.balance import BalanceList + from twilio.rest.api.v2010.account.call import CallList + from twilio.rest.api.v2010.account.conference import ConferenceList + from twilio.rest.api.v2010.account.connect_app import ConnectAppList + from twilio.rest.api.v2010.account.incoming_phone_number import ( + IncomingPhoneNumberList, + ) + from twilio.rest.api.v2010.account.key import KeyList + from twilio.rest.api.v2010.account.new_key import NewKeyList + from twilio.rest.api.v2010.account.message import MessageList + from twilio.rest.api.v2010.account.signing_key import SigningKeyList + from twilio.rest.api.v2010.account.new_signing_key import NewSigningKeyList + from twilio.rest.api.v2010.account.notification import NotificationList + from twilio.rest.api.v2010.account.outgoing_caller_id import OutgoingCallerIdList + from twilio.rest.api.v2010.account.validation_request import ValidationRequestList + from twilio.rest.api.v2010.account.queue import QueueList + from twilio.rest.api.v2010.account.recording import RecordingList + from twilio.rest.api.v2010.account.short_code import ShortCodeList + from twilio.rest.api.v2010.account.sip import SipList + from twilio.rest.api.v2010.account.token import TokenList + from twilio.rest.api.v2010.account.transcription import TranscriptionList + from twilio.rest.api.v2010.account.usage import UsageList + + +class Client(ClientBase): + """A client for accessing the Twilio API.""" + + def __init__( + self, + username=None, + password=None, + account_sid=None, + region=None, + http_client=None, + environment=None, + edge=None, + user_agent_extensions=None, + ): + """ + Initializes the Twilio Client + + :param str username: Username to authenticate with, either account_sid or api_key + :param str password: Password to authenticate with, auth_token (if using account_sid) or api_secret (if using api_key) + :param str account_sid: Account SID, required if using api_key to authenticate. + :param str region: Twilio Region to make requests to, defaults to 'us1' if an edge is provided + :param HttpClient http_client: HttpClient, defaults to TwilioHttpClient + :param dict environment: Environment to look for auth details, defaults to os.environ + :param str edge: Twilio Edge to make requests to, defaults to None + :param list[str] user_agent_extensions: Additions to the user agent string + + :returns: Twilio Client + :rtype: twilio.rest.Client + """ + super().__init__( + username, + password, + account_sid, + region, + http_client, + environment, + edge, + user_agent_extensions, + ) + + # Domains + self._accounts: Optional["Accounts"] = None + self._api: Optional["Api"] = None + self._bulkexports: Optional["Bulkexports"] = None + self._chat: Optional["Chat"] = None + self._content: Optional["Content"] = None + self._conversations: Optional["Conversations"] = None + self._events: Optional["Events"] = None + self._flex_api: Optional["FlexApi"] = None + self._frontline_api: Optional["FrontlineApi"] = None + self._insights: Optional["Insights"] = None + self._intelligence: Optional["Intelligence"] = None + self._ip_messaging: Optional["IpMessaging"] = None + self._lookups: Optional["Lookups"] = None + self._preview_messaging: Optional["PreviewMessaging"] = None + self._messaging: Optional["Messaging"] = None + self._microvisor: Optional["Microvisor"] = None + self._monitor: Optional["Monitor"] = None + self._notify: Optional["Notify"] = None + self._numbers: Optional["Numbers"] = None + self._oauth: Optional["Oauth"] = None + self._preview: Optional["Preview"] = None + self._pricing: Optional["Pricing"] = None + self._proxy: Optional["Proxy"] = None + self._routes: Optional["Routes"] = None + self._serverless: Optional["Serverless"] = None + self._studio: Optional["Studio"] = None + self._supersim: Optional["Supersim"] = None + self._sync: Optional["Sync"] = None + self._taskrouter: Optional["Taskrouter"] = None + self._trunking: Optional["Trunking"] = None + self._trusthub: Optional["Trusthub"] = None + self._verify: Optional["Verify"] = None + self._video: Optional["Video"] = None + self._voice: Optional["Voice"] = None + self._wireless: Optional["Wireless"] = None + + @property + def accounts(self) -> "Accounts": + """ + Access the Accounts Twilio Domain + + :returns: Accounts Twilio Domain + """ + if self._accounts is None: + from twilio.rest.accounts import Accounts + + self._accounts = Accounts(self) + return self._accounts + + @property + def api(self) -> "Api": + """ + Access the Api Twilio Domain + + :returns: Api Twilio Domain + """ + if self._api is None: + from twilio.rest.api import Api + + self._api = Api(self) + return self._api + + @property + def bulkexports(self) -> "Bulkexports": + """ + Access the Bulkexports Twilio Domain + + :returns: Bulkexports Twilio Domain + """ + if self._bulkexports is None: + from twilio.rest.bulkexports import Bulkexports + + self._bulkexports = Bulkexports(self) + return self._bulkexports + + @property + def chat(self) -> "Chat": + """ + Access the Chat Twilio Domain + + :returns: Chat Twilio Domain + """ + if self._chat is None: + from twilio.rest.chat import Chat + + self._chat = Chat(self) + return self._chat + + @property + def content(self) -> "Content": + """ + Access the Content Twilio Domain + + :returns: Content Twilio Domain + """ + if self._content is None: + from twilio.rest.content import Content + + self._content = Content(self) + return self._content + + @property + def conversations(self) -> "Conversations": + """ + Access the Conversations Twilio Domain + + :returns: Conversations Twilio Domain + """ + if self._conversations is None: + from twilio.rest.conversations import Conversations + + self._conversations = Conversations(self) + return self._conversations + + @property + def events(self) -> "Events": + """ + Access the Events Twilio Domain + + :returns: Events Twilio Domain + """ + if self._events is None: + from twilio.rest.events import Events + + self._events = Events(self) + return self._events + + @property + def flex_api(self) -> "FlexApi": + """ + Access the FlexApi Twilio Domain + + :returns: FlexApi Twilio Domain + """ + if self._flex_api is None: + from twilio.rest.flex_api import FlexApi + + self._flex_api = FlexApi(self) + return self._flex_api + + @property + def frontline_api(self) -> "FrontlineApi": + """ + Access the FrontlineApi Twilio Domain + + :returns: FrontlineApi Twilio Domain + """ + if self._frontline_api is None: + from twilio.rest.frontline_api import FrontlineApi + + self._frontline_api = FrontlineApi(self) + return self._frontline_api + + @property + def insights(self) -> "Insights": + """ + Access the Insights Twilio Domain + + :returns: Insights Twilio Domain + """ + if self._insights is None: + from twilio.rest.insights import Insights + + self._insights = Insights(self) + return self._insights + + @property + def intelligence(self) -> "Intelligence": + """ + Access the Intelligence Twilio Domain + + :returns: Intelligence Twilio Domain + """ + if self._intelligence is None: + from twilio.rest.intelligence import Intelligence + + self._intelligence = Intelligence(self) + return self._intelligence + + @property + def ip_messaging(self) -> "IpMessaging": + """ + Access the IpMessaging Twilio Domain + + :returns: IpMessaging Twilio Domain + """ + if self._ip_messaging is None: + from twilio.rest.ip_messaging import IpMessaging + + self._ip_messaging = IpMessaging(self) + return self._ip_messaging + + @property + def lookups(self) -> "Lookups": + """ + Access the Lookups Twilio Domain + + :returns: Lookups Twilio Domain + """ + if self._lookups is None: + from twilio.rest.lookups import Lookups + + self._lookups = Lookups(self) + return self._lookups + + @property + def preview_messaging(self) -> "PreviewMessaging": + """ + Access the PreviewMessaging Twilio Domain + + :returns: PreviewMessaging Twilio Domain + """ + if self._preview_messaging is None: + from twilio.rest.preview_messaging import PreviewMessaging + + self._preview_messaging = PreviewMessaging(self) + return self._preview_messaging + + @property + def messaging(self) -> "Messaging": + """ + Access the Messaging Twilio Domain + + :returns: Messaging Twilio Domain + """ + if self._messaging is None: + from twilio.rest.messaging import Messaging + + self._messaging = Messaging(self) + return self._messaging + + @property + def microvisor(self) -> "Microvisor": + """ + Access the Microvisor Twilio Domain + + :returns: Microvisor Twilio Domain + """ + if self._microvisor is None: + from twilio.rest.microvisor import Microvisor + + self._microvisor = Microvisor(self) + return self._microvisor + + @property + def monitor(self) -> "Monitor": + """ + Access the Monitor Twilio Domain + + :returns: Monitor Twilio Domain + """ + if self._monitor is None: + from twilio.rest.monitor import Monitor + + self._monitor = Monitor(self) + return self._monitor + + @property + def notify(self) -> "Notify": + """ + Access the Notify Twilio Domain + + :returns: Notify Twilio Domain + """ + if self._notify is None: + from twilio.rest.notify import Notify + + self._notify = Notify(self) + return self._notify + + @property + def numbers(self) -> "Numbers": + """ + Access the Numbers Twilio Domain + + :returns: Numbers Twilio Domain + """ + if self._numbers is None: + from twilio.rest.numbers import Numbers + + self._numbers = Numbers(self) + return self._numbers + + @property + def oauth(self) -> "Oauth": + """ + Access the Oauth Twilio Domain + + :returns: Oauth Twilio Domain + """ + if self._oauth is None: + from twilio.rest.oauth import Oauth + + self._oauth = Oauth(self) + return self._oauth + + @property + def preview(self) -> "Preview": + """ + Access the Preview Twilio Domain + + :returns: Preview Twilio Domain + """ + if self._preview is None: + from twilio.rest.preview import Preview + + self._preview = Preview(self) + return self._preview + + @property + def pricing(self) -> "Pricing": + """ + Access the Pricing Twilio Domain + + :returns: Pricing Twilio Domain + """ + if self._pricing is None: + from twilio.rest.pricing import Pricing + + self._pricing = Pricing(self) + return self._pricing + + @property + def proxy(self) -> "Proxy": + """ + Access the Proxy Twilio Domain + + :returns: Proxy Twilio Domain + """ + if self._proxy is None: + from twilio.rest.proxy import Proxy + + self._proxy = Proxy(self) + return self._proxy + + @property + def routes(self) -> "Routes": + """ + Access the Routes Twilio Domain + + :returns: Routes Twilio Domain + """ + if self._routes is None: + from twilio.rest.routes import Routes + + self._routes = Routes(self) + return self._routes + + @property + def serverless(self) -> "Serverless": + """ + Access the Serverless Twilio Domain + + :returns: Serverless Twilio Domain + """ + if self._serverless is None: + from twilio.rest.serverless import Serverless + + self._serverless = Serverless(self) + return self._serverless + + @property + def studio(self) -> "Studio": + """ + Access the Studio Twilio Domain + + :returns: Studio Twilio Domain + """ + if self._studio is None: + from twilio.rest.studio import Studio + + self._studio = Studio(self) + return self._studio + + @property + def supersim(self) -> "Supersim": + """ + Access the Supersim Twilio Domain + + :returns: Supersim Twilio Domain + """ + if self._supersim is None: + from twilio.rest.supersim import Supersim + + self._supersim = Supersim(self) + return self._supersim + + @property + def sync(self) -> "Sync": + """ + Access the Sync Twilio Domain + + :returns: Sync Twilio Domain + """ + if self._sync is None: + from twilio.rest.sync import Sync + + self._sync = Sync(self) + return self._sync + + @property + def taskrouter(self) -> "Taskrouter": + """ + Access the Taskrouter Twilio Domain + + :returns: Taskrouter Twilio Domain + """ + if self._taskrouter is None: + from twilio.rest.taskrouter import Taskrouter + + self._taskrouter = Taskrouter(self) + return self._taskrouter + + @property + def trunking(self) -> "Trunking": + """ + Access the Trunking Twilio Domain + + :returns: Trunking Twilio Domain + """ + if self._trunking is None: + from twilio.rest.trunking import Trunking + + self._trunking = Trunking(self) + return self._trunking + + @property + def trusthub(self) -> "Trusthub": + """ + Access the Trusthub Twilio Domain + + :returns: Trusthub Twilio Domain + """ + if self._trusthub is None: + from twilio.rest.trusthub import Trusthub + + self._trusthub = Trusthub(self) + return self._trusthub + + @property + def verify(self) -> "Verify": + """ + Access the Verify Twilio Domain + + :returns: Verify Twilio Domain + """ + if self._verify is None: + from twilio.rest.verify import Verify + + self._verify = Verify(self) + return self._verify + + @property + def video(self) -> "Video": + """ + Access the Video Twilio Domain + + :returns: Video Twilio Domain + """ + if self._video is None: + from twilio.rest.video import Video + + self._video = Video(self) + return self._video + + @property + def voice(self) -> "Voice": + """ + Access the Voice Twilio Domain + + :returns: Voice Twilio Domain + """ + if self._voice is None: + from twilio.rest.voice import Voice + + self._voice = Voice(self) + return self._voice + + @property + def wireless(self) -> "Wireless": + """ + Access the Wireless Twilio Domain + + :returns: Wireless Twilio Domain + """ + if self._wireless is None: + from twilio.rest.wireless import Wireless + + self._wireless = Wireless(self) + return self._wireless + + @property + def addresses(self) -> "AddressList": + return self.api.account.addresses + + @property + def applications(self) -> "ApplicationList": + return self.api.account.applications + + @property + def authorized_connect_apps(self) -> "AuthorizedConnectAppList": + return self.api.account.authorized_connect_apps + + @property + def available_phone_numbers(self) -> "AvailablePhoneNumberCountryList": + return self.api.account.available_phone_numbers + + @property + def balance(self) -> "BalanceList": + return self.api.account.balance + + @property + def calls(self) -> "CallList": + return self.api.account.calls + + @property + def conferences(self) -> "ConferenceList": + return self.api.account.conferences + + @property + def connect_apps(self) -> "ConnectAppList": + return self.api.account.connect_apps + + @property + def incoming_phone_numbers(self) -> "IncomingPhoneNumberList": + return self.api.account.incoming_phone_numbers + + @property + def keys(self) -> "KeyList": + return self.api.account.keys + + @property + def new_keys(self) -> "NewKeyList": + return self.api.account.new_keys + + @property + def messages(self) -> "MessageList": + return self.api.account.messages + + @property + def signing_keys(self) -> "SigningKeyList": + return self.api.account.signing_keys + + @property + def new_signing_keys(self) -> "NewSigningKeyList": + return self.api.account.new_signing_keys + + @property + def notifications(self) -> "NotificationList": + return self.api.account.notifications + + @property + def outgoing_caller_ids(self) -> "OutgoingCallerIdList": + return self.api.account.outgoing_caller_ids + + @property + def validation_requests(self) -> "ValidationRequestList": + return self.api.account.validation_requests + + @property + def queues(self) -> "QueueList": + return self.api.account.queues + + @property + def recordings(self) -> "RecordingList": + return self.api.account.recordings + + @property + def short_codes(self) -> "ShortCodeList": + return self.api.account.short_codes + + @property + def sip(self) -> "SipList": + return self.api.account.sip + + @property + def tokens(self) -> "TokenList": + return self.api.account.tokens + + @property + def transcriptions(self) -> "TranscriptionList": + return self.api.account.transcriptions + + @property + def usage(self) -> "UsageList": + return self.api.account.usage diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d74e8fa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/AccountsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/AccountsBase.py new file mode 100644 index 0000000..e9ac0d5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/AccountsBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.accounts.v1 import V1 + + +class AccountsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Accounts Domain + + :returns: Domain for Accounts + """ + super().__init__(twilio, "https://accounts.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Accounts + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__init__.py new file mode 100644 index 0000000..e2275ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__init__.py @@ -0,0 +1,35 @@ +from warnings import warn + +from twilio.rest.accounts.AccountsBase import AccountsBase +from twilio.rest.accounts.v1.auth_token_promotion import AuthTokenPromotionList +from twilio.rest.accounts.v1.credential import CredentialList +from twilio.rest.accounts.v1.secondary_auth_token import SecondaryAuthTokenList + + +class Accounts(AccountsBase): + @property + def auth_token_promotion(self) -> AuthTokenPromotionList: + warn( + "auth_token_promotion is deprecated. Use v1.auth_token_promotion instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.auth_token_promotion + + @property + def credentials(self) -> CredentialList: + warn( + "credentials is deprecated. Use v1.credentials instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.credentials + + @property + def secondary_auth_token(self) -> SecondaryAuthTokenList: + warn( + "secondary_auth_token is deprecated. Use v1.secondary_auth_token instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.secondary_auth_token diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/AccountsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/AccountsBase.cpython-310.pyc new file mode 100644 index 0000000..79f823a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/AccountsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..cb61baa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__init__.py new file mode 100644 index 0000000..142d5c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__init__.py @@ -0,0 +1,67 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.accounts.v1.auth_token_promotion import AuthTokenPromotionList +from twilio.rest.accounts.v1.credential import CredentialList +from twilio.rest.accounts.v1.safelist import SafelistList +from twilio.rest.accounts.v1.secondary_auth_token import SecondaryAuthTokenList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Accounts + + :param domain: The Twilio.accounts domain + """ + super().__init__(domain, "v1") + self._auth_token_promotion: Optional[AuthTokenPromotionList] = None + self._credentials: Optional[CredentialList] = None + self._safelist: Optional[SafelistList] = None + self._secondary_auth_token: Optional[SecondaryAuthTokenList] = None + + @property + def auth_token_promotion(self) -> AuthTokenPromotionList: + if self._auth_token_promotion is None: + self._auth_token_promotion = AuthTokenPromotionList(self) + return self._auth_token_promotion + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def safelist(self) -> SafelistList: + if self._safelist is None: + self._safelist = SafelistList(self) + return self._safelist + + @property + def secondary_auth_token(self) -> SecondaryAuthTokenList: + if self._secondary_auth_token is None: + self._secondary_auth_token = SecondaryAuthTokenList(self) + return self._secondary_auth_token + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6d33a8a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/auth_token_promotion.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/auth_token_promotion.cpython-310.pyc new file mode 100644 index 0000000..b8ee32c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/auth_token_promotion.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/safelist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/safelist.cpython-310.pyc new file mode 100644 index 0000000..899d07b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/safelist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/secondary_auth_token.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/secondary_auth_token.cpython-310.pyc new file mode 100644 index 0000000..d0201b8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/__pycache__/secondary_auth_token.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/auth_token_promotion.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/auth_token_promotion.py new file mode 100644 index 0000000..7ab6c3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/auth_token_promotion.py @@ -0,0 +1,177 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AuthTokenPromotionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the secondary Auth Token was created for. + :ivar auth_token: The promoted Auth Token that must be used to authenticate future API requests. + :ivar date_created: The date and time in UTC when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The URI for this resource, relative to `https://accounts.twilio.com` + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.auth_token: Optional[str] = payload.get("auth_token") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._context: Optional[AuthTokenPromotionContext] = None + + @property + def _proxy(self) -> "AuthTokenPromotionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthTokenPromotionContext for this AuthTokenPromotionInstance + """ + if self._context is None: + self._context = AuthTokenPromotionContext( + self._version, + ) + return self._context + + def update(self) -> "AuthTokenPromotionInstance": + """ + Update the AuthTokenPromotionInstance + + + :returns: The updated AuthTokenPromotionInstance + """ + return self._proxy.update() + + async def update_async(self) -> "AuthTokenPromotionInstance": + """ + Asynchronous coroutine to update the AuthTokenPromotionInstance + + + :returns: The updated AuthTokenPromotionInstance + """ + return await self._proxy.update_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class AuthTokenPromotionContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the AuthTokenPromotionContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/AuthTokens/Promote" + + def update(self) -> AuthTokenPromotionInstance: + """ + Update the AuthTokenPromotionInstance + + + :returns: The updated AuthTokenPromotionInstance + """ + data = values.of({}) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthTokenPromotionInstance(self._version, payload) + + async def update_async(self) -> AuthTokenPromotionInstance: + """ + Asynchronous coroutine to update the AuthTokenPromotionInstance + + + :returns: The updated AuthTokenPromotionInstance + """ + data = values.of({}) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthTokenPromotionInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class AuthTokenPromotionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AuthTokenPromotionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> AuthTokenPromotionContext: + """ + Constructs a AuthTokenPromotionContext + + """ + return AuthTokenPromotionContext(self._version) + + def __call__(self) -> AuthTokenPromotionContext: + """ + Constructs a AuthTokenPromotionContext + + """ + return AuthTokenPromotionContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__init__.py new file mode 100644 index 0000000..e9c4653 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__init__.py @@ -0,0 +1,65 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.accounts.v1.credential.aws import AwsList +from twilio.rest.accounts.v1.credential.public_key import PublicKeyList + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + self._aws: Optional[AwsList] = None + self._public_key: Optional[PublicKeyList] = None + + @property + def aws(self) -> AwsList: + """ + Access the aws + """ + if self._aws is None: + self._aws = AwsList(self._version) + return self._aws + + @property + def public_key(self) -> PublicKeyList: + """ + Access the public_key + """ + if self._public_key is None: + self._public_key = PublicKeyList(self._version) + return self._public_key + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a198c03 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/aws.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/aws.cpython-310.pyc new file mode 100644 index 0000000..9ff354a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/aws.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/public_key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/public_key.cpython-310.pyc new file mode 100644 index 0000000..5b4c64f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/__pycache__/public_key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/aws.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/aws.py new file mode 100644 index 0000000..ab75620 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/aws.py @@ -0,0 +1,579 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AwsInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the AWS resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AWS resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URI for this resource, relative to `https://accounts.twilio.com` + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AwsContext] = None + + @property + def _proxy(self) -> "AwsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AwsContext for this AwsInstance + """ + if self._context is None: + self._context = AwsContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AwsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AwsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AwsInstance": + """ + Fetch the AwsInstance + + + :returns: The fetched AwsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AwsInstance": + """ + Asynchronous coroutine to fetch the AwsInstance + + + :returns: The fetched AwsInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: Union[str, object] = values.unset) -> "AwsInstance": + """ + Update the AwsInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated AwsInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "AwsInstance": + """ + Asynchronous coroutine to update the AwsInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated AwsInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AwsContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AwsContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the AWS resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/AWS/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the AwsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AwsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AwsInstance: + """ + Fetch the AwsInstance + + + :returns: The fetched AwsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AwsInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AwsInstance: + """ + Asynchronous coroutine to fetch the AwsInstance + + + :returns: The fetched AwsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AwsInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, friendly_name: Union[str, object] = values.unset) -> AwsInstance: + """ + Update the AwsInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated AwsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AwsInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> AwsInstance: + """ + Asynchronous coroutine to update the AwsInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated AwsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AwsInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AwsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AwsInstance: + """ + Build an instance of AwsInstance + + :param payload: Payload response from the API + """ + return AwsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AwsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AwsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials/AWS" + + def create( + self, + credentials: str, + friendly_name: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> AwsInstance: + """ + Create the AwsInstance + + :param credentials: A string that contains the AWS access credentials in the format `:`. For example, `AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param account_sid: The SID of the Subaccount that this Credential should be associated with. Must be a valid Subaccount of the account issuing the request. + + :returns: The created AwsInstance + """ + + data = values.of( + { + "Credentials": credentials, + "FriendlyName": friendly_name, + "AccountSid": account_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AwsInstance(self._version, payload) + + async def create_async( + self, + credentials: str, + friendly_name: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> AwsInstance: + """ + Asynchronously create the AwsInstance + + :param credentials: A string that contains the AWS access credentials in the format `:`. For example, `AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param account_sid: The SID of the Subaccount that this Credential should be associated with. Must be a valid Subaccount of the account issuing the request. + + :returns: The created AwsInstance + """ + + data = values.of( + { + "Credentials": credentials, + "FriendlyName": friendly_name, + "AccountSid": account_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AwsInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AwsInstance]: + """ + Streams AwsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AwsInstance]: + """ + Asynchronously streams AwsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AwsInstance]: + """ + Lists AwsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AwsInstance]: + """ + Asynchronously lists AwsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AwsPage: + """ + Retrieve a single page of AwsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AwsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AwsPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AwsPage: + """ + Asynchronously retrieve a single page of AwsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AwsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AwsPage(self._version, response) + + def get_page(self, target_url: str) -> AwsPage: + """ + Retrieve a specific page of AwsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AwsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AwsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AwsPage: + """ + Asynchronously retrieve a specific page of AwsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AwsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AwsPage(self._version, response) + + def get(self, sid: str) -> AwsContext: + """ + Constructs a AwsContext + + :param sid: The Twilio-provided string that uniquely identifies the AWS resource to update. + """ + return AwsContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AwsContext: + """ + Constructs a AwsContext + + :param sid: The Twilio-provided string that uniquely identifies the AWS resource to update. + """ + return AwsContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/public_key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/public_key.py new file mode 100644 index 0000000..09018d1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/credential/public_key.py @@ -0,0 +1,583 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PublicKeyInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the PublicKey resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Credential that the PublicKey resource belongs to. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URI for this resource, relative to `https://accounts.twilio.com` + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PublicKeyContext] = None + + @property + def _proxy(self) -> "PublicKeyContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PublicKeyContext for this PublicKeyInstance + """ + if self._context is None: + self._context = PublicKeyContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PublicKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PublicKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "PublicKeyInstance": + """ + Fetch the PublicKeyInstance + + + :returns: The fetched PublicKeyInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PublicKeyInstance": + """ + Asynchronous coroutine to fetch the PublicKeyInstance + + + :returns: The fetched PublicKeyInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "PublicKeyInstance": + """ + Update the PublicKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated PublicKeyInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "PublicKeyInstance": + """ + Asynchronous coroutine to update the PublicKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated PublicKeyInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PublicKeyContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PublicKeyContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the PublicKey resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/PublicKeys/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the PublicKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PublicKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> PublicKeyInstance: + """ + Fetch the PublicKeyInstance + + + :returns: The fetched PublicKeyInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PublicKeyInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PublicKeyInstance: + """ + Asynchronous coroutine to fetch the PublicKeyInstance + + + :returns: The fetched PublicKeyInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PublicKeyInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> PublicKeyInstance: + """ + Update the PublicKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated PublicKeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return PublicKeyInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> PublicKeyInstance: + """ + Asynchronous coroutine to update the PublicKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated PublicKeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PublicKeyInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PublicKeyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PublicKeyInstance: + """ + Build an instance of PublicKeyInstance + + :param payload: Payload response from the API + """ + return PublicKeyInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PublicKeyList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PublicKeyList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials/PublicKeys" + + def create( + self, + public_key: str, + friendly_name: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> PublicKeyInstance: + """ + Create the PublicKeyInstance + + :param public_key: A URL encoded representation of the public key. For example, `-----BEGIN PUBLIC KEY-----MIIBIjANB.pa9xQIDAQAB-----END PUBLIC KEY-----` + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param account_sid: The SID of the Subaccount that this Credential should be associated with. Must be a valid Subaccount of the account issuing the request + + :returns: The created PublicKeyInstance + """ + + data = values.of( + { + "PublicKey": public_key, + "FriendlyName": friendly_name, + "AccountSid": account_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PublicKeyInstance(self._version, payload) + + async def create_async( + self, + public_key: str, + friendly_name: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> PublicKeyInstance: + """ + Asynchronously create the PublicKeyInstance + + :param public_key: A URL encoded representation of the public key. For example, `-----BEGIN PUBLIC KEY-----MIIBIjANB.pa9xQIDAQAB-----END PUBLIC KEY-----` + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param account_sid: The SID of the Subaccount that this Credential should be associated with. Must be a valid Subaccount of the account issuing the request + + :returns: The created PublicKeyInstance + """ + + data = values.of( + { + "PublicKey": public_key, + "FriendlyName": friendly_name, + "AccountSid": account_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PublicKeyInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PublicKeyInstance]: + """ + Streams PublicKeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PublicKeyInstance]: + """ + Asynchronously streams PublicKeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PublicKeyInstance]: + """ + Lists PublicKeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PublicKeyInstance]: + """ + Asynchronously lists PublicKeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PublicKeyPage: + """ + Retrieve a single page of PublicKeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PublicKeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PublicKeyPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PublicKeyPage: + """ + Asynchronously retrieve a single page of PublicKeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PublicKeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PublicKeyPage(self._version, response) + + def get_page(self, target_url: str) -> PublicKeyPage: + """ + Retrieve a specific page of PublicKeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PublicKeyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PublicKeyPage(self._version, response) + + async def get_page_async(self, target_url: str) -> PublicKeyPage: + """ + Asynchronously retrieve a specific page of PublicKeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PublicKeyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PublicKeyPage(self._version, response) + + def get(self, sid: str) -> PublicKeyContext: + """ + Constructs a PublicKeyContext + + :param sid: The Twilio-provided string that uniquely identifies the PublicKey resource to update. + """ + return PublicKeyContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PublicKeyContext: + """ + Constructs a PublicKeyContext + + :param sid: The Twilio-provided string that uniquely identifies the PublicKey resource to update. + """ + return PublicKeyContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/safelist.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/safelist.py new file mode 100644 index 0000000..ce5672c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/safelist.py @@ -0,0 +1,186 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SafelistInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the SafeList resource. + :ivar phone_number: The phone number in SafeList. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.phone_number: Optional[str] = payload.get("phone_number") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SafelistList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SafelistList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SafeList/Numbers" + + def create(self, phone_number: str) -> SafelistInstance: + """ + Create the SafelistInstance + + :param phone_number: The phone number to be added in SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + + :returns: The created SafelistInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SafelistInstance(self._version, payload) + + async def create_async(self, phone_number: str) -> SafelistInstance: + """ + Asynchronously create the SafelistInstance + + :param phone_number: The phone number to be added in SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + + :returns: The created SafelistInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SafelistInstance(self._version, payload) + + def delete(self, phone_number: Union[str, object] = values.unset) -> bool: + """ + Asynchronously delete the SafelistInstance + + :param phone_number: The phone number to be removed from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :returns: True if delete succeeds, False otherwise + """ + + params = values.of( + { + "PhoneNumber": phone_number, + } + ) + return self._version.delete(method="DELETE", uri=self._uri, params=params) + + async def delete_async( + self, phone_number: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronously delete the SafelistInstance + + :param phone_number: The phone number to be removed from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :returns: True if delete succeeds, False otherwise + """ + + params = values.of( + { + "PhoneNumber": phone_number, + } + ) + return await self._version.delete_async( + method="DELETE", uri=self._uri, params=params + ) + + def fetch( + self, phone_number: Union[str, object] = values.unset + ) -> SafelistInstance: + """ + Asynchronously fetch the SafelistInstance + + :param phone_number: The phone number to be fetched from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :returns: The fetched SafelistInstance + """ + + params = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=params) + + return SafelistInstance(self._version, payload) + + async def fetch_async( + self, phone_number: Union[str, object] = values.unset + ) -> SafelistInstance: + """ + Asynchronously fetch the SafelistInstance + + :param phone_number: The phone number to be fetched from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :returns: The fetched SafelistInstance + """ + + params = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=params + ) + + return SafelistInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/secondary_auth_token.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/secondary_auth_token.py new file mode 100644 index 0000000..3735dd8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/accounts/v1/secondary_auth_token.py @@ -0,0 +1,213 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Accounts + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SecondaryAuthTokenInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the secondary Auth Token was created for. + :ivar date_created: The date and time in UTC when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in UTC when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar secondary_auth_token: The generated secondary Auth Token that can be used to authenticate future API requests. + :ivar url: The URI for this resource, relative to `https://accounts.twilio.com` + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.secondary_auth_token: Optional[str] = payload.get("secondary_auth_token") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[SecondaryAuthTokenContext] = None + + @property + def _proxy(self) -> "SecondaryAuthTokenContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SecondaryAuthTokenContext for this SecondaryAuthTokenInstance + """ + if self._context is None: + self._context = SecondaryAuthTokenContext( + self._version, + ) + return self._context + + def create(self) -> "SecondaryAuthTokenInstance": + """ + Create the SecondaryAuthTokenInstance + + + :returns: The created SecondaryAuthTokenInstance + """ + return self._proxy.create() + + async def create_async(self) -> "SecondaryAuthTokenInstance": + """ + Asynchronous coroutine to create the SecondaryAuthTokenInstance + + + :returns: The created SecondaryAuthTokenInstance + """ + return await self._proxy.create_async() + + def delete(self) -> bool: + """ + Deletes the SecondaryAuthTokenInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SecondaryAuthTokenInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SecondaryAuthTokenContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the SecondaryAuthTokenContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/AuthTokens/Secondary" + + def create(self) -> SecondaryAuthTokenInstance: + """ + Create the SecondaryAuthTokenInstance + + + :returns: The created SecondaryAuthTokenInstance + """ + data = values.of({}) + + payload = self._version.create(method="POST", uri=self._uri, data=data) + + return SecondaryAuthTokenInstance(self._version, payload) + + async def create_async(self) -> SecondaryAuthTokenInstance: + """ + Asynchronous coroutine to create the SecondaryAuthTokenInstance + + + :returns: The created SecondaryAuthTokenInstance + """ + data = values.of({}) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data + ) + + return SecondaryAuthTokenInstance(self._version, payload) + + def delete(self) -> bool: + """ + Deletes the SecondaryAuthTokenInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SecondaryAuthTokenInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SecondaryAuthTokenList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SecondaryAuthTokenList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> SecondaryAuthTokenContext: + """ + Constructs a SecondaryAuthTokenContext + + """ + return SecondaryAuthTokenContext(self._version) + + def __call__(self) -> SecondaryAuthTokenContext: + """ + Constructs a SecondaryAuthTokenContext + + """ + return SecondaryAuthTokenContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/ApiBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/ApiBase.py new file mode 100644 index 0000000..e53535a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/ApiBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.api.v2010 import V2010 + + +class ApiBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Api Domain + + :returns: Domain for Api + """ + super().__init__(twilio, "https://api.twilio.com") + self._v2010: Optional[V2010] = None + + @property + def v2010(self) -> V2010: + """ + :returns: Versions v2010 of Api + """ + if self._v2010 is None: + self._v2010 = V2010(self) + return self._v2010 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__init__.py new file mode 100644 index 0000000..08d5885 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__init__.py @@ -0,0 +1,258 @@ +from warnings import warn + +from twilio.rest.api.ApiBase import ApiBase +from twilio.rest.api.v2010.account import AccountContext, AccountList +from twilio.rest.api.v2010.account.address import AddressList +from twilio.rest.api.v2010.account.application import ApplicationList +from twilio.rest.api.v2010.account.authorized_connect_app import ( + AuthorizedConnectAppList, +) +from twilio.rest.api.v2010.account.available_phone_number_country import ( + AvailablePhoneNumberCountryList, +) +from twilio.rest.api.v2010.account.balance import BalanceList +from twilio.rest.api.v2010.account.call import CallList +from twilio.rest.api.v2010.account.conference import ConferenceList +from twilio.rest.api.v2010.account.connect_app import ConnectAppList +from twilio.rest.api.v2010.account.incoming_phone_number import IncomingPhoneNumberList +from twilio.rest.api.v2010.account.key import KeyList +from twilio.rest.api.v2010.account.message import MessageList +from twilio.rest.api.v2010.account.new_key import NewKeyList +from twilio.rest.api.v2010.account.new_signing_key import NewSigningKeyList +from twilio.rest.api.v2010.account.notification import NotificationList +from twilio.rest.api.v2010.account.outgoing_caller_id import OutgoingCallerIdList +from twilio.rest.api.v2010.account.queue import QueueList +from twilio.rest.api.v2010.account.recording import RecordingList +from twilio.rest.api.v2010.account.short_code import ShortCodeList +from twilio.rest.api.v2010.account.signing_key import SigningKeyList +from twilio.rest.api.v2010.account.sip import SipList +from twilio.rest.api.v2010.account.token import TokenList +from twilio.rest.api.v2010.account.transcription import TranscriptionList +from twilio.rest.api.v2010.account.usage import UsageList +from twilio.rest.api.v2010.account.validation_request import ValidationRequestList + + +class Api(ApiBase): + @property + def account(self) -> AccountContext: + return self.v2010.account + + @property + def accounts(self) -> AccountList: + return self.v2010.accounts + + @property + def addresses(self) -> AddressList: + warn( + "addresses is deprecated. Use account.addresses instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.addresses + + @property + def applications(self) -> ApplicationList: + warn( + "applications is deprecated. Use account.applications instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.applications + + @property + def authorized_connect_apps(self) -> AuthorizedConnectAppList: + warn( + "authorized_connect_apps is deprecated. Use account.authorized_connect_apps instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.authorized_connect_apps + + @property + def available_phone_numbers(self) -> AvailablePhoneNumberCountryList: + warn( + "available_phone_numbers is deprecated. Use account.available_phone_numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.available_phone_numbers + + @property + def balance(self) -> BalanceList: + warn( + "balance is deprecated. Use account.balance instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.balance + + @property + def calls(self) -> CallList: + warn( + "calls is deprecated. Use account.calls instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.calls + + @property + def conferences(self) -> ConferenceList: + warn( + "conferences is deprecated. Use account.conferences instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.conferences + + @property + def connect_apps(self) -> ConnectAppList: + warn( + "connect_apps is deprecated. Use account.connect_apps instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.connect_apps + + @property + def incoming_phone_numbers(self) -> IncomingPhoneNumberList: + warn( + "incoming_phone_numbers is deprecated. Use account.incoming_phone_numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.incoming_phone_numbers + + @property + def keys(self) -> KeyList: + warn( + "keys is deprecated. Use account.keys instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.keys + + @property + def messages(self) -> MessageList: + warn( + "messages is deprecated. Use account.messages instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.messages + + @property + def new_keys(self) -> NewKeyList: + warn( + "new_keys is deprecated. Use account.new_keys instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.new_keys + + @property + def new_signing_keys(self) -> NewSigningKeyList: + warn( + "new_signing_keys is deprecated. Use account.new_signing_keys instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.new_signing_keys + + @property + def notifications(self) -> NotificationList: + warn( + "notifications is deprecated. Use account.notifications instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.notifications + + @property + def outgoing_caller_ids(self) -> OutgoingCallerIdList: + warn( + "outgoing_caller_ids is deprecated. Use account.outgoing_caller_ids instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.outgoing_caller_ids + + @property + def queues(self) -> QueueList: + warn( + "queues is deprecated. Use account.queues instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.queues + + @property + def recordings(self) -> RecordingList: + warn( + "recordings is deprecated. Use account.recordings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.recordings + + @property + def signing_keys(self) -> SigningKeyList: + warn( + "signing_keys is deprecated. Use account.signing_keys instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.signing_keys + + @property + def sip(self) -> SipList: + warn( + "sip is deprecated. Use account.sip instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.sip + + @property + def short_codes(self) -> ShortCodeList: + warn( + "short_codes is deprecated. Use account.short_codes instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.short_codes + + @property + def tokens(self) -> TokenList: + warn( + "tokens is deprecated. Use account.tokens instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.tokens + + @property + def transcriptions(self) -> TranscriptionList: + warn( + "transcriptions is deprecated. Use account.transcriptions instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.transcriptions + + @property + def usage(self) -> UsageList: + warn( + "usage is deprecated. Use account.usage instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.usage + + @property + def validation_requests(self) -> ValidationRequestList: + warn( + "validation_requests is deprecated. Use account.validation_requests instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.account.validation_requests diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/ApiBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/ApiBase.cpython-310.pyc new file mode 100644 index 0000000..c86d92d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/ApiBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..dc112d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__init__.py new file mode 100644 index 0000000..2efb4ff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.api.v2010.account import AccountList +from twilio.rest.api.v2010.account import AccountContext + + +class V2010(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2010 version of Api + + :param domain: The Twilio.api domain + """ + super().__init__(domain, "2010-04-01") + self._accounts: Optional[AccountList] = None + self._account: Optional[AccountContext] = None + + @property + def accounts(self) -> AccountList: + if self._accounts is None: + self._accounts = AccountList(self) + return self._accounts + + @property + def account(self) -> AccountContext: + if self._account is None: + self._account = AccountContext(self, self.domain.twilio.account_sid) + return self._account + + @account.setter + def account(self, value: AccountContext) -> None: + """ + Setter to override account + :param value: value to use as account + """ + self._account = value + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0867868 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__init__.py new file mode 100644 index 0000000..adf2458 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__init__.py @@ -0,0 +1,1108 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.address import AddressList +from twilio.rest.api.v2010.account.application import ApplicationList +from twilio.rest.api.v2010.account.authorized_connect_app import ( + AuthorizedConnectAppList, +) +from twilio.rest.api.v2010.account.available_phone_number_country import ( + AvailablePhoneNumberCountryList, +) +from twilio.rest.api.v2010.account.balance import BalanceList +from twilio.rest.api.v2010.account.call import CallList +from twilio.rest.api.v2010.account.conference import ConferenceList +from twilio.rest.api.v2010.account.connect_app import ConnectAppList +from twilio.rest.api.v2010.account.incoming_phone_number import IncomingPhoneNumberList +from twilio.rest.api.v2010.account.key import KeyList +from twilio.rest.api.v2010.account.message import MessageList +from twilio.rest.api.v2010.account.new_key import NewKeyList +from twilio.rest.api.v2010.account.new_signing_key import NewSigningKeyList +from twilio.rest.api.v2010.account.notification import NotificationList +from twilio.rest.api.v2010.account.outgoing_caller_id import OutgoingCallerIdList +from twilio.rest.api.v2010.account.queue import QueueList +from twilio.rest.api.v2010.account.recording import RecordingList +from twilio.rest.api.v2010.account.short_code import ShortCodeList +from twilio.rest.api.v2010.account.signing_key import SigningKeyList +from twilio.rest.api.v2010.account.sip import SipList +from twilio.rest.api.v2010.account.token import TokenList +from twilio.rest.api.v2010.account.transcription import TranscriptionList +from twilio.rest.api.v2010.account.usage import UsageList +from twilio.rest.api.v2010.account.validation_request import ValidationRequestList + + +class AccountInstance(InstanceResource): + + class Status(object): + ACTIVE = "active" + SUSPENDED = "suspended" + CLOSED = "closed" + + class Type(object): + TRIAL = "Trial" + FULL = "Full" + + """ + :ivar auth_token: The authorization token for this account. This token should be kept a secret, so no sharing. + :ivar date_created: The date that this account was created, in GMT in RFC 2822 format + :ivar date_updated: The date that this account was last updated, in GMT in RFC 2822 format. + :ivar friendly_name: A human readable description of this account, up to 64 characters long. By default the FriendlyName is your email address. + :ivar owner_account_sid: The unique 34 character id that represents the parent of this account. The OwnerAccountSid of a parent account is it's own sid. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar status: + :ivar subresource_uris: A Map of various subresources available for the given Account Instance + :ivar type: + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.auth_token: Optional[str] = payload.get("auth_token") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.owner_account_sid: Optional[str] = payload.get("owner_account_sid") + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["AccountInstance.Status"] = payload.get("status") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.type: Optional["AccountInstance.Type"] = payload.get("type") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AccountContext] = None + + @property + def _proxy(self) -> "AccountContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AccountContext for this AccountInstance + """ + if self._context is None: + self._context = AccountContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AccountInstance": + """ + Fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AccountInstance": + """ + Asynchronous coroutine to fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + ) -> "AccountInstance": + """ + Update the AccountInstance + + :param friendly_name: Update the human-readable description of this Account + :param status: + + :returns: The updated AccountInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + status=status, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + ) -> "AccountInstance": + """ + Asynchronous coroutine to update the AccountInstance + + :param friendly_name: Update the human-readable description of this Account + :param status: + + :returns: The updated AccountInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + status=status, + ) + + @property + def addresses(self) -> AddressList: + """ + Access the addresses + """ + return self._proxy.addresses + + @property + def applications(self) -> ApplicationList: + """ + Access the applications + """ + return self._proxy.applications + + @property + def authorized_connect_apps(self) -> AuthorizedConnectAppList: + """ + Access the authorized_connect_apps + """ + return self._proxy.authorized_connect_apps + + @property + def available_phone_numbers(self) -> AvailablePhoneNumberCountryList: + """ + Access the available_phone_numbers + """ + return self._proxy.available_phone_numbers + + @property + def balance(self) -> BalanceList: + """ + Access the balance + """ + return self._proxy.balance + + @property + def calls(self) -> CallList: + """ + Access the calls + """ + return self._proxy.calls + + @property + def conferences(self) -> ConferenceList: + """ + Access the conferences + """ + return self._proxy.conferences + + @property + def connect_apps(self) -> ConnectAppList: + """ + Access the connect_apps + """ + return self._proxy.connect_apps + + @property + def incoming_phone_numbers(self) -> IncomingPhoneNumberList: + """ + Access the incoming_phone_numbers + """ + return self._proxy.incoming_phone_numbers + + @property + def keys(self) -> KeyList: + """ + Access the keys + """ + return self._proxy.keys + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + @property + def new_keys(self) -> NewKeyList: + """ + Access the new_keys + """ + return self._proxy.new_keys + + @property + def new_signing_keys(self) -> NewSigningKeyList: + """ + Access the new_signing_keys + """ + return self._proxy.new_signing_keys + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + return self._proxy.notifications + + @property + def outgoing_caller_ids(self) -> OutgoingCallerIdList: + """ + Access the outgoing_caller_ids + """ + return self._proxy.outgoing_caller_ids + + @property + def queues(self) -> QueueList: + """ + Access the queues + """ + return self._proxy.queues + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + return self._proxy.recordings + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + return self._proxy.short_codes + + @property + def signing_keys(self) -> SigningKeyList: + """ + Access the signing_keys + """ + return self._proxy.signing_keys + + @property + def sip(self) -> SipList: + """ + Access the sip + """ + return self._proxy.sip + + @property + def tokens(self) -> TokenList: + """ + Access the tokens + """ + return self._proxy.tokens + + @property + def transcriptions(self) -> TranscriptionList: + """ + Access the transcriptions + """ + return self._proxy.transcriptions + + @property + def usage(self) -> UsageList: + """ + Access the usage + """ + return self._proxy.usage + + @property + def validation_requests(self) -> ValidationRequestList: + """ + Access the validation_requests + """ + return self._proxy.validation_requests + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AccountContext + + :param version: Version that contains the resource + :param sid: The Account Sid that uniquely identifies the account to update + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Accounts/{sid}.json".format(**self._solution) + + self._addresses: Optional[AddressList] = None + self._applications: Optional[ApplicationList] = None + self._authorized_connect_apps: Optional[AuthorizedConnectAppList] = None + self._available_phone_numbers: Optional[AvailablePhoneNumberCountryList] = None + self._balance: Optional[BalanceList] = None + self._calls: Optional[CallList] = None + self._conferences: Optional[ConferenceList] = None + self._connect_apps: Optional[ConnectAppList] = None + self._incoming_phone_numbers: Optional[IncomingPhoneNumberList] = None + self._keys: Optional[KeyList] = None + self._messages: Optional[MessageList] = None + self._new_keys: Optional[NewKeyList] = None + self._new_signing_keys: Optional[NewSigningKeyList] = None + self._notifications: Optional[NotificationList] = None + self._outgoing_caller_ids: Optional[OutgoingCallerIdList] = None + self._queues: Optional[QueueList] = None + self._recordings: Optional[RecordingList] = None + self._short_codes: Optional[ShortCodeList] = None + self._signing_keys: Optional[SigningKeyList] = None + self._sip: Optional[SipList] = None + self._tokens: Optional[TokenList] = None + self._transcriptions: Optional[TranscriptionList] = None + self._usage: Optional[UsageList] = None + self._validation_requests: Optional[ValidationRequestList] = None + + def fetch(self) -> AccountInstance: + """ + Fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AccountInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AccountInstance: + """ + Asynchronous coroutine to fetch the AccountInstance + + + :returns: The fetched AccountInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AccountInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + ) -> AccountInstance: + """ + Update the AccountInstance + + :param friendly_name: Update the human-readable description of this Account + :param status: + + :returns: The updated AccountInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + ) -> AccountInstance: + """ + Asynchronous coroutine to update the AccountInstance + + :param friendly_name: Update the human-readable description of this Account + :param status: + + :returns: The updated AccountInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def addresses(self) -> AddressList: + """ + Access the addresses + """ + if self._addresses is None: + self._addresses = AddressList( + self._version, + self._solution["sid"], + ) + return self._addresses + + @property + def applications(self) -> ApplicationList: + """ + Access the applications + """ + if self._applications is None: + self._applications = ApplicationList( + self._version, + self._solution["sid"], + ) + return self._applications + + @property + def authorized_connect_apps(self) -> AuthorizedConnectAppList: + """ + Access the authorized_connect_apps + """ + if self._authorized_connect_apps is None: + self._authorized_connect_apps = AuthorizedConnectAppList( + self._version, + self._solution["sid"], + ) + return self._authorized_connect_apps + + @property + def available_phone_numbers(self) -> AvailablePhoneNumberCountryList: + """ + Access the available_phone_numbers + """ + if self._available_phone_numbers is None: + self._available_phone_numbers = AvailablePhoneNumberCountryList( + self._version, + self._solution["sid"], + ) + return self._available_phone_numbers + + @property + def balance(self) -> BalanceList: + """ + Access the balance + """ + if self._balance is None: + self._balance = BalanceList( + self._version, + self._solution["sid"], + ) + return self._balance + + @property + def calls(self) -> CallList: + """ + Access the calls + """ + if self._calls is None: + self._calls = CallList( + self._version, + self._solution["sid"], + ) + return self._calls + + @property + def conferences(self) -> ConferenceList: + """ + Access the conferences + """ + if self._conferences is None: + self._conferences = ConferenceList( + self._version, + self._solution["sid"], + ) + return self._conferences + + @property + def connect_apps(self) -> ConnectAppList: + """ + Access the connect_apps + """ + if self._connect_apps is None: + self._connect_apps = ConnectAppList( + self._version, + self._solution["sid"], + ) + return self._connect_apps + + @property + def incoming_phone_numbers(self) -> IncomingPhoneNumberList: + """ + Access the incoming_phone_numbers + """ + if self._incoming_phone_numbers is None: + self._incoming_phone_numbers = IncomingPhoneNumberList( + self._version, + self._solution["sid"], + ) + return self._incoming_phone_numbers + + @property + def keys(self) -> KeyList: + """ + Access the keys + """ + if self._keys is None: + self._keys = KeyList( + self._version, + self._solution["sid"], + ) + return self._keys + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["sid"], + ) + return self._messages + + @property + def new_keys(self) -> NewKeyList: + """ + Access the new_keys + """ + if self._new_keys is None: + self._new_keys = NewKeyList( + self._version, + self._solution["sid"], + ) + return self._new_keys + + @property + def new_signing_keys(self) -> NewSigningKeyList: + """ + Access the new_signing_keys + """ + if self._new_signing_keys is None: + self._new_signing_keys = NewSigningKeyList( + self._version, + self._solution["sid"], + ) + return self._new_signing_keys + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + if self._notifications is None: + self._notifications = NotificationList( + self._version, + self._solution["sid"], + ) + return self._notifications + + @property + def outgoing_caller_ids(self) -> OutgoingCallerIdList: + """ + Access the outgoing_caller_ids + """ + if self._outgoing_caller_ids is None: + self._outgoing_caller_ids = OutgoingCallerIdList( + self._version, + self._solution["sid"], + ) + return self._outgoing_caller_ids + + @property + def queues(self) -> QueueList: + """ + Access the queues + """ + if self._queues is None: + self._queues = QueueList( + self._version, + self._solution["sid"], + ) + return self._queues + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + if self._recordings is None: + self._recordings = RecordingList( + self._version, + self._solution["sid"], + ) + return self._recordings + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + if self._short_codes is None: + self._short_codes = ShortCodeList( + self._version, + self._solution["sid"], + ) + return self._short_codes + + @property + def signing_keys(self) -> SigningKeyList: + """ + Access the signing_keys + """ + if self._signing_keys is None: + self._signing_keys = SigningKeyList( + self._version, + self._solution["sid"], + ) + return self._signing_keys + + @property + def sip(self) -> SipList: + """ + Access the sip + """ + if self._sip is None: + self._sip = SipList( + self._version, + self._solution["sid"], + ) + return self._sip + + @property + def tokens(self) -> TokenList: + """ + Access the tokens + """ + if self._tokens is None: + self._tokens = TokenList( + self._version, + self._solution["sid"], + ) + return self._tokens + + @property + def transcriptions(self) -> TranscriptionList: + """ + Access the transcriptions + """ + if self._transcriptions is None: + self._transcriptions = TranscriptionList( + self._version, + self._solution["sid"], + ) + return self._transcriptions + + @property + def usage(self) -> UsageList: + """ + Access the usage + """ + if self._usage is None: + self._usage = UsageList( + self._version, + self._solution["sid"], + ) + return self._usage + + @property + def validation_requests(self) -> ValidationRequestList: + """ + Access the validation_requests + """ + if self._validation_requests is None: + self._validation_requests = ValidationRequestList( + self._version, + self._solution["sid"], + ) + return self._validation_requests + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AccountInstance: + """ + Build an instance of AccountInstance + + :param payload: Payload response from the API + """ + return AccountInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AccountList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AccountList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Accounts.json" + + def create( + self, friendly_name: Union[str, object] = values.unset + ) -> AccountInstance: + """ + Create the AccountInstance + + :param friendly_name: A human readable description of the account to create, defaults to `SubAccount Created at {YYYY-MM-DD HH:MM meridian}` + + :returns: The created AccountInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountInstance(self._version, payload) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> AccountInstance: + """ + Asynchronously create the AccountInstance + + :param friendly_name: A human readable description of the account to create, defaults to `SubAccount Created at {YYYY-MM-DD HH:MM meridian}` + + :returns: The created AccountInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountInstance(self._version, payload) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AccountInstance]: + """ + Streams AccountInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param "AccountInstance.Status" status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + friendly_name=friendly_name, status=status, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AccountInstance]: + """ + Asynchronously streams AccountInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param "AccountInstance.Status" status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountInstance]: + """ + Lists AccountInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param "AccountInstance.Status" status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountInstance]: + """ + Asynchronously lists AccountInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param "AccountInstance.Status" status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountPage: + """ + Retrieve a single page of AccountInstance records from the API. + Request is executed immediately + + :param friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AccountPage(self._version, response) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + status: Union["AccountInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountPage: + """ + Asynchronously retrieve a single page of AccountInstance records from the API. + Request is executed immediately + + :param friendly_name: Only return the Account resources with friendly names that exactly match this name. + :param status: Only return Account resources with the given status. Can be `closed`, `suspended` or `active`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AccountPage(self._version, response) + + def get_page(self, target_url: str) -> AccountPage: + """ + Retrieve a specific page of AccountInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AccountPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AccountPage: + """ + Asynchronously retrieve a specific page of AccountInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AccountPage(self._version, response) + + def get(self, sid: str) -> AccountContext: + """ + Constructs a AccountContext + + :param sid: The Account Sid that uniquely identifies the account to update + """ + return AccountContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AccountContext: + """ + Constructs a AccountContext + + :param sid: The Account Sid that uniquely identifies the account to update + """ + return AccountContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7b9c5da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/application.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/application.cpython-310.pyc new file mode 100644 index 0000000..d585603 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/application.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/authorized_connect_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/authorized_connect_app.cpython-310.pyc new file mode 100644 index 0000000..fdc1c11 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/authorized_connect_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/balance.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/balance.cpython-310.pyc new file mode 100644 index 0000000..82590ee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/balance.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/connect_app.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/connect_app.cpython-310.pyc new file mode 100644 index 0000000..d5dec67 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/connect_app.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/key.cpython-310.pyc new file mode 100644 index 0000000..9a2c5f5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_key.cpython-310.pyc new file mode 100644 index 0000000..a2348b8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_signing_key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_signing_key.cpython-310.pyc new file mode 100644 index 0000000..7097d8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/new_signing_key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/notification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/notification.cpython-310.pyc new file mode 100644 index 0000000..9c2eeef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/notification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/outgoing_caller_id.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/outgoing_caller_id.cpython-310.pyc new file mode 100644 index 0000000..bba5d21 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/outgoing_caller_id.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/short_code.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/short_code.cpython-310.pyc new file mode 100644 index 0000000..da9b52b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/short_code.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/signing_key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/signing_key.cpython-310.pyc new file mode 100644 index 0000000..03b8658 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/signing_key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/token.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/token.cpython-310.pyc new file mode 100644 index 0000000..6bde2cf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/token.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/transcription.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/transcription.cpython-310.pyc new file mode 100644 index 0000000..a7b3e82 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/transcription.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/validation_request.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/validation_request.cpython-310.pyc new file mode 100644 index 0000000..9878993 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/__pycache__/validation_request.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__init__.py new file mode 100644 index 0000000..fd77747 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__init__.py @@ -0,0 +1,865 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.address.dependent_phone_number import ( + DependentPhoneNumberList, +) + + +class AddressInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that is responsible for the Address resource. + :ivar city: The city in which the address is located. + :ivar customer_name: The name associated with the address.This property has a maximum length of 16 4-byte characters, or 21 3-byte characters. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar iso_country: The ISO country code of the address. + :ivar postal_code: The postal code of the address. + :ivar region: The state or region of the address. + :ivar sid: The unique string that that we created to identify the Address resource. + :ivar street: The number and street address of the address. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar emergency_enabled: Whether emergency calling has been enabled on this number. + :ivar validated: Whether the address has been validated to comply with local regulation. In countries that require valid addresses, an invalid address will not be accepted. `true` indicates the Address has been validated. `false` indicate the country doesn't require validation or the Address is not valid. + :ivar verified: Whether the address has been verified to comply with regulation. In countries that require valid addresses, an invalid address will not be accepted. `true` indicates the Address has been verified. `false` indicate the country doesn't require verified or the Address is not valid. + :ivar street_secondary: The additional number and street address of the address. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.city: Optional[str] = payload.get("city") + self.customer_name: Optional[str] = payload.get("customer_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iso_country: Optional[str] = payload.get("iso_country") + self.postal_code: Optional[str] = payload.get("postal_code") + self.region: Optional[str] = payload.get("region") + self.sid: Optional[str] = payload.get("sid") + self.street: Optional[str] = payload.get("street") + self.uri: Optional[str] = payload.get("uri") + self.emergency_enabled: Optional[bool] = payload.get("emergency_enabled") + self.validated: Optional[bool] = payload.get("validated") + self.verified: Optional[bool] = payload.get("verified") + self.street_secondary: Optional[str] = payload.get("street_secondary") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[AddressContext] = None + + @property + def _proxy(self) -> "AddressContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AddressContext for this AddressInstance + """ + if self._context is None: + self._context = AddressContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AddressInstance": + """ + Fetch the AddressInstance + + + :returns: The fetched AddressInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AddressInstance": + """ + Asynchronous coroutine to fetch the AddressInstance + + + :returns: The fetched AddressInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + customer_name: Union[str, object] = values.unset, + street: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> "AddressInstance": + """ + Update the AddressInstance + + :param friendly_name: A descriptive string that you create to describe the address. It can be up to 64 characters long. + :param customer_name: The name to associate with the address. + :param street: The number and street address of the address. + :param city: The city of the address. + :param region: The state or region of the address. + :param postal_code: The postal code of the address. + :param emergency_enabled: Whether to enable emergency calling on the address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The updated AddressInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + customer_name=customer_name, + street=street, + city=city, + region=region, + postal_code=postal_code, + emergency_enabled=emergency_enabled, + auto_correct_address=auto_correct_address, + street_secondary=street_secondary, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + customer_name: Union[str, object] = values.unset, + street: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> "AddressInstance": + """ + Asynchronous coroutine to update the AddressInstance + + :param friendly_name: A descriptive string that you create to describe the address. It can be up to 64 characters long. + :param customer_name: The name to associate with the address. + :param street: The number and street address of the address. + :param city: The city of the address. + :param region: The state or region of the address. + :param postal_code: The postal code of the address. + :param emergency_enabled: Whether to enable emergency calling on the address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The updated AddressInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + customer_name=customer_name, + street=street, + city=city, + region=region, + postal_code=postal_code, + emergency_enabled=emergency_enabled, + auto_correct_address=auto_correct_address, + street_secondary=street_secondary, + ) + + @property + def dependent_phone_numbers(self) -> DependentPhoneNumberList: + """ + Access the dependent_phone_numbers + """ + return self._proxy.dependent_phone_numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AddressContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the AddressContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that is responsible for the Address resource to update. + :param sid: The Twilio-provided string that uniquely identifies the Address resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Addresses/{sid}.json".format( + **self._solution + ) + + self._dependent_phone_numbers: Optional[DependentPhoneNumberList] = None + + def delete(self) -> bool: + """ + Deletes the AddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AddressInstance: + """ + Fetch the AddressInstance + + + :returns: The fetched AddressInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AddressInstance: + """ + Asynchronous coroutine to fetch the AddressInstance + + + :returns: The fetched AddressInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + customer_name: Union[str, object] = values.unset, + street: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> AddressInstance: + """ + Update the AddressInstance + + :param friendly_name: A descriptive string that you create to describe the address. It can be up to 64 characters long. + :param customer_name: The name to associate with the address. + :param street: The number and street address of the address. + :param city: The city of the address. + :param region: The state or region of the address. + :param postal_code: The postal code of the address. + :param emergency_enabled: Whether to enable emergency calling on the address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The updated AddressInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "CustomerName": customer_name, + "Street": street, + "City": city, + "Region": region, + "PostalCode": postal_code, + "EmergencyEnabled": serialize.boolean_to_string(emergency_enabled), + "AutoCorrectAddress": serialize.boolean_to_string(auto_correct_address), + "StreetSecondary": street_secondary, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + customer_name: Union[str, object] = values.unset, + street: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> AddressInstance: + """ + Asynchronous coroutine to update the AddressInstance + + :param friendly_name: A descriptive string that you create to describe the address. It can be up to 64 characters long. + :param customer_name: The name to associate with the address. + :param street: The number and street address of the address. + :param city: The city of the address. + :param region: The state or region of the address. + :param postal_code: The postal code of the address. + :param emergency_enabled: Whether to enable emergency calling on the address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The updated AddressInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "CustomerName": customer_name, + "Street": street, + "City": city, + "Region": region, + "PostalCode": postal_code, + "EmergencyEnabled": serialize.boolean_to_string(emergency_enabled), + "AutoCorrectAddress": serialize.boolean_to_string(auto_correct_address), + "StreetSecondary": street_secondary, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def dependent_phone_numbers(self) -> DependentPhoneNumberList: + """ + Access the dependent_phone_numbers + """ + if self._dependent_phone_numbers is None: + self._dependent_phone_numbers = DependentPhoneNumberList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._dependent_phone_numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AddressPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AddressInstance: + """ + Build an instance of AddressInstance + + :param payload: Payload response from the API + """ + return AddressInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AddressList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the AddressList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that is responsible for the Address resource to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Addresses.json".format(**self._solution) + + def create( + self, + customer_name: str, + street: str, + city: str, + region: str, + postal_code: str, + iso_country: str, + friendly_name: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> AddressInstance: + """ + Create the AddressInstance + + :param customer_name: The name to associate with the new address. + :param street: The number and street address of the new address. + :param city: The city of the new address. + :param region: The state or region of the new address. + :param postal_code: The postal code of the new address. + :param iso_country: The ISO country code of the new address. + :param friendly_name: A descriptive string that you create to describe the new address. It can be up to 64 characters long. + :param emergency_enabled: Whether to enable emergency calling on the new address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The created AddressInstance + """ + + data = values.of( + { + "CustomerName": customer_name, + "Street": street, + "City": city, + "Region": region, + "PostalCode": postal_code, + "IsoCountry": iso_country, + "FriendlyName": friendly_name, + "EmergencyEnabled": serialize.boolean_to_string(emergency_enabled), + "AutoCorrectAddress": serialize.boolean_to_string(auto_correct_address), + "StreetSecondary": street_secondary, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + customer_name: str, + street: str, + city: str, + region: str, + postal_code: str, + iso_country: str, + friendly_name: Union[str, object] = values.unset, + emergency_enabled: Union[bool, object] = values.unset, + auto_correct_address: Union[bool, object] = values.unset, + street_secondary: Union[str, object] = values.unset, + ) -> AddressInstance: + """ + Asynchronously create the AddressInstance + + :param customer_name: The name to associate with the new address. + :param street: The number and street address of the new address. + :param city: The city of the new address. + :param region: The state or region of the new address. + :param postal_code: The postal code of the new address. + :param iso_country: The ISO country code of the new address. + :param friendly_name: A descriptive string that you create to describe the new address. It can be up to 64 characters long. + :param emergency_enabled: Whether to enable emergency calling on the new address. Can be: `true` or `false`. + :param auto_correct_address: Whether we should automatically correct the address. Can be: `true` or `false` and the default is `true`. If empty or `true`, we will correct the address you provide if necessary. If `false`, we won't alter the address you provide. + :param street_secondary: The additional number and street address of the address. + + :returns: The created AddressInstance + """ + + data = values.of( + { + "CustomerName": customer_name, + "Street": street, + "City": city, + "Region": region, + "PostalCode": postal_code, + "IsoCountry": iso_country, + "FriendlyName": friendly_name, + "EmergencyEnabled": serialize.boolean_to_string(emergency_enabled), + "AutoCorrectAddress": serialize.boolean_to_string(auto_correct_address), + "StreetSecondary": street_secondary, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AddressInstance]: + """ + Streams AddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str customer_name: The `customer_name` of the Address resources to read. + :param str friendly_name: The string that identifies the Address resources to read. + :param str iso_country: The ISO country code of the Address resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + customer_name=customer_name, + friendly_name=friendly_name, + iso_country=iso_country, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AddressInstance]: + """ + Asynchronously streams AddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str customer_name: The `customer_name` of the Address resources to read. + :param str friendly_name: The string that identifies the Address resources to read. + :param str iso_country: The ISO country code of the Address resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + customer_name=customer_name, + friendly_name=friendly_name, + iso_country=iso_country, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddressInstance]: + """ + Lists AddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str customer_name: The `customer_name` of the Address resources to read. + :param str friendly_name: The string that identifies the Address resources to read. + :param str iso_country: The ISO country code of the Address resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + customer_name=customer_name, + friendly_name=friendly_name, + iso_country=iso_country, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddressInstance]: + """ + Asynchronously lists AddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str customer_name: The `customer_name` of the Address resources to read. + :param str friendly_name: The string that identifies the Address resources to read. + :param str iso_country: The ISO country code of the Address resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + customer_name=customer_name, + friendly_name=friendly_name, + iso_country=iso_country, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddressPage: + """ + Retrieve a single page of AddressInstance records from the API. + Request is executed immediately + + :param customer_name: The `customer_name` of the Address resources to read. + :param friendly_name: The string that identifies the Address resources to read. + :param iso_country: The ISO country code of the Address resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddressInstance + """ + data = values.of( + { + "CustomerName": customer_name, + "FriendlyName": friendly_name, + "IsoCountry": iso_country, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AddressPage(self._version, response, self._solution) + + async def page_async( + self, + customer_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddressPage: + """ + Asynchronously retrieve a single page of AddressInstance records from the API. + Request is executed immediately + + :param customer_name: The `customer_name` of the Address resources to read. + :param friendly_name: The string that identifies the Address resources to read. + :param iso_country: The ISO country code of the Address resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddressInstance + """ + data = values.of( + { + "CustomerName": customer_name, + "FriendlyName": friendly_name, + "IsoCountry": iso_country, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AddressPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AddressPage: + """ + Retrieve a specific page of AddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddressInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AddressPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AddressPage: + """ + Asynchronously retrieve a specific page of AddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddressInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AddressPage(self._version, response, self._solution) + + def get(self, sid: str) -> AddressContext: + """ + Constructs a AddressContext + + :param sid: The Twilio-provided string that uniquely identifies the Address resource to update. + """ + return AddressContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> AddressContext: + """ + Constructs a AddressContext + + :param sid: The Twilio-provided string that uniquely identifies the Address resource to update. + """ + return AddressContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..bc072b1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/dependent_phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/dependent_phone_number.cpython-310.pyc new file mode 100644 index 0000000..8ae41ef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/__pycache__/dependent_phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/dependent_phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/dependent_phone_number.py new file mode 100644 index 0000000..738f9ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/address/dependent_phone_number.py @@ -0,0 +1,364 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DependentPhoneNumberInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + class EmergencyStatus(object): + ACTIVE = "Active" + INACTIVE = "Inactive" + + """ + :ivar sid: The unique string that that we created to identify the DependentPhoneNumber resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the DependentPhoneNumber resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar voice_url: The URL we call when the phone number receives a call. The `voice_url` will not be used if a `voice_application_sid` or a `trunk_sid` is set. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database. Can be: `true` or `false`. Caller ID lookups can cost $0.01 each. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar address_requirements: + :ivar capabilities: The set of Boolean properties that indicates whether a phone number can receive calls or messages. Capabilities are `Voice`, `SMS`, and `MMS` and each capability can be: `true` or `false`. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar api_version: The API version used to start a new TwiML session. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar emergency_status: + :ivar emergency_address_sid: The SID of the emergency address configuration that we use for emergency calling from the phone number. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + address_sid: str, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.voice_url: Optional[str] = payload.get("voice_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.address_requirements: Optional[ + "DependentPhoneNumberInstance.AddressRequirement" + ] = payload.get("address_requirements") + self.capabilities: Optional[Dict[str, object]] = payload.get("capabilities") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.api_version: Optional[str] = payload.get("api_version") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.emergency_status: Optional[ + "DependentPhoneNumberInstance.EmergencyStatus" + ] = payload.get("emergency_status") + self.emergency_address_sid: Optional[str] = payload.get("emergency_address_sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "address_sid": address_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DependentPhoneNumberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DependentPhoneNumberInstance: + """ + Build an instance of DependentPhoneNumberInstance + + :param payload: Payload response from the API + """ + return DependentPhoneNumberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + address_sid=self._solution["address_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DependentPhoneNumberList(ListResource): + + def __init__(self, version: Version, account_sid: str, address_sid: str): + """ + Initialize the DependentPhoneNumberList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the DependentPhoneNumber resources to read. + :param address_sid: The SID of the Address resource associated with the phone number. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "address_sid": address_sid, + } + self._uri = "/Accounts/{account_sid}/Addresses/{address_sid}/DependentPhoneNumbers.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DependentPhoneNumberInstance]: + """ + Streams DependentPhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DependentPhoneNumberInstance]: + """ + Asynchronously streams DependentPhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentPhoneNumberInstance]: + """ + Lists DependentPhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentPhoneNumberInstance]: + """ + Asynchronously lists DependentPhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentPhoneNumberPage: + """ + Retrieve a single page of DependentPhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentPhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DependentPhoneNumberPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentPhoneNumberPage: + """ + Asynchronously retrieve a single page of DependentPhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentPhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DependentPhoneNumberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DependentPhoneNumberPage: + """ + Retrieve a specific page of DependentPhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentPhoneNumberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DependentPhoneNumberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DependentPhoneNumberPage: + """ + Asynchronously retrieve a specific page of DependentPhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentPhoneNumberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DependentPhoneNumberPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/application.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/application.py new file mode 100644 index 0000000..a3b11f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/application.py @@ -0,0 +1,954 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ApplicationInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Application resource. + :ivar api_version: The API version used to start a new TwiML session. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar message_status_callback: The URL we call using a POST method to send message status information to your application. + :ivar sid: The unique string that that we created to identify the Application resource. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_status_callback: The URL we call using a POST method to send status information to your application about SMS messages that refer to the application. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call when the phone number assigned to this application receives a call. + :ivar public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.message_status_callback: Optional[str] = payload.get( + "message_status_callback" + ) + self.sid: Optional[str] = payload.get("sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_status_callback: Optional[str] = payload.get("sms_status_callback") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.uri: Optional[str] = payload.get("uri") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.public_application_connect_enabled: Optional[bool] = payload.get( + "public_application_connect_enabled" + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[ApplicationContext] = None + + @property + def _proxy(self) -> "ApplicationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ApplicationContext for this ApplicationInstance + """ + if self._context is None: + self._context = ApplicationContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ApplicationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ApplicationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ApplicationInstance": + """ + Fetch the ApplicationInstance + + + :returns: The fetched ApplicationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ApplicationInstance": + """ + Asynchronous coroutine to fetch the ApplicationInstance + + + :returns: The fetched ApplicationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> "ApplicationInstance": + """ + Update the ApplicationInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is your account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: Same as message_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. Deprecated, included for backwards compatibility. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The updated ApplicationInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + api_version=api_version, + voice_url=voice_url, + voice_method=voice_method, + voice_fallback_url=voice_fallback_url, + voice_fallback_method=voice_fallback_method, + status_callback=status_callback, + status_callback_method=status_callback_method, + voice_caller_id_lookup=voice_caller_id_lookup, + sms_url=sms_url, + sms_method=sms_method, + sms_fallback_url=sms_fallback_url, + sms_fallback_method=sms_fallback_method, + sms_status_callback=sms_status_callback, + message_status_callback=message_status_callback, + public_application_connect_enabled=public_application_connect_enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> "ApplicationInstance": + """ + Asynchronous coroutine to update the ApplicationInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is your account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: Same as message_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. Deprecated, included for backwards compatibility. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The updated ApplicationInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + api_version=api_version, + voice_url=voice_url, + voice_method=voice_method, + voice_fallback_url=voice_fallback_url, + voice_fallback_method=voice_fallback_method, + status_callback=status_callback, + status_callback_method=status_callback_method, + voice_caller_id_lookup=voice_caller_id_lookup, + sms_url=sms_url, + sms_method=sms_method, + sms_fallback_url=sms_fallback_url, + sms_fallback_method=sms_fallback_method, + sms_status_callback=sms_status_callback, + message_status_callback=message_status_callback, + public_application_connect_enabled=public_application_connect_enabled, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ApplicationContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the ApplicationContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Application resources to update. + :param sid: The Twilio-provided string that uniquely identifies the Application resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Applications/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the ApplicationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ApplicationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ApplicationInstance: + """ + Fetch the ApplicationInstance + + + :returns: The fetched ApplicationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ApplicationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ApplicationInstance: + """ + Asynchronous coroutine to fetch the ApplicationInstance + + + :returns: The fetched ApplicationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ApplicationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> ApplicationInstance: + """ + Update the ApplicationInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is your account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: Same as message_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. Deprecated, included for backwards compatibility. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The updated ApplicationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApiVersion": api_version, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsStatusCallback": sms_status_callback, + "MessageStatusCallback": message_status_callback, + "PublicApplicationConnectEnabled": serialize.boolean_to_string( + public_application_connect_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ApplicationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> ApplicationInstance: + """ + Asynchronous coroutine to update the ApplicationInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is your account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: Same as message_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. Deprecated, included for backwards compatibility. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The updated ApplicationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApiVersion": api_version, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsStatusCallback": sms_status_callback, + "MessageStatusCallback": message_status_callback, + "PublicApplicationConnectEnabled": serialize.boolean_to_string( + public_application_connect_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ApplicationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ApplicationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ApplicationInstance: + """ + Build an instance of ApplicationInstance + + :param payload: Payload response from the API + """ + return ApplicationInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ApplicationList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ApplicationList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Application resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Applications.json".format(**self._solution) + + def create( + self, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> ApplicationInstance: + """ + Create the ApplicationInstance + + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is the account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param friendly_name: A descriptive string that you create to describe the new application. It can be up to 64 characters long. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The created ApplicationInstance + """ + + data = values.of( + { + "ApiVersion": api_version, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsStatusCallback": sms_status_callback, + "MessageStatusCallback": message_status_callback, + "FriendlyName": friendly_name, + "PublicApplicationConnectEnabled": serialize.boolean_to_string( + public_application_connect_enabled + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ApplicationInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + api_version: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_status_callback: Union[str, object] = values.unset, + message_status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + public_application_connect_enabled: Union[bool, object] = values.unset, + ) -> ApplicationInstance: + """ + Asynchronously create the ApplicationInstance + + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. The default value is the account's default API version. + :param voice_url: The URL we should call when the phone number assigned to this application receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`. + :param voice_caller_id_lookup: Whether we should look up the caller's caller-ID name from the CNAM database (additional charges apply). Can be: `true` or `false`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :param sms_status_callback: The URL we should call using a POST method to send status information about SMS messages sent by the application. + :param message_status_callback: The URL we should call using a POST method to send message status information to your application. + :param friendly_name: A descriptive string that you create to describe the new application. It can be up to 64 characters long. + :param public_application_connect_enabled: Whether to allow other Twilio accounts to dial this applicaton using Dial verb. Can be: `true` or `false`. + + :returns: The created ApplicationInstance + """ + + data = values.of( + { + "ApiVersion": api_version, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsStatusCallback": sms_status_callback, + "MessageStatusCallback": message_status_callback, + "FriendlyName": friendly_name, + "PublicApplicationConnectEnabled": serialize.boolean_to_string( + public_application_connect_enabled + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ApplicationInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ApplicationInstance]: + """ + Streams ApplicationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the Application resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ApplicationInstance]: + """ + Asynchronously streams ApplicationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the Application resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ApplicationInstance]: + """ + Lists ApplicationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the Application resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ApplicationInstance]: + """ + Asynchronously lists ApplicationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the Application resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ApplicationPage: + """ + Retrieve a single page of ApplicationInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the Application resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ApplicationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ApplicationPage(self._version, response, self._solution) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ApplicationPage: + """ + Asynchronously retrieve a single page of ApplicationInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the Application resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ApplicationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ApplicationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ApplicationPage: + """ + Retrieve a specific page of ApplicationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ApplicationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ApplicationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ApplicationPage: + """ + Asynchronously retrieve a specific page of ApplicationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ApplicationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ApplicationPage(self._version, response, self._solution) + + def get(self, sid: str) -> ApplicationContext: + """ + Constructs a ApplicationContext + + :param sid: The Twilio-provided string that uniquely identifies the Application resource to update. + """ + return ApplicationContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ApplicationContext: + """ + Constructs a ApplicationContext + + :param sid: The Twilio-provided string that uniquely identifies the Application resource to update. + """ + return ApplicationContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/authorized_connect_app.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/authorized_connect_app.py new file mode 100644 index 0000000..9359545 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/authorized_connect_app.py @@ -0,0 +1,444 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AuthorizedConnectAppInstance(InstanceResource): + + class Permission(object): + GET_ALL = "get-all" + POST_ALL = "post-all" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AuthorizedConnectApp resource. + :ivar connect_app_company_name: The company name set for the Connect App. + :ivar connect_app_description: A detailed description of the Connect App. + :ivar connect_app_friendly_name: The name of the Connect App. + :ivar connect_app_homepage_url: The public URL for the Connect App. + :ivar connect_app_sid: The SID that we assigned to the Connect App. + :ivar permissions: The set of permissions that you authorized for the Connect App. Can be: `get-all` or `post-all`. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + connect_app_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.connect_app_company_name: Optional[str] = payload.get( + "connect_app_company_name" + ) + self.connect_app_description: Optional[str] = payload.get( + "connect_app_description" + ) + self.connect_app_friendly_name: Optional[str] = payload.get( + "connect_app_friendly_name" + ) + self.connect_app_homepage_url: Optional[str] = payload.get( + "connect_app_homepage_url" + ) + self.connect_app_sid: Optional[str] = payload.get("connect_app_sid") + self.permissions: Optional[List["AuthorizedConnectAppInstance.Permission"]] = ( + payload.get("permissions") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "connect_app_sid": connect_app_sid or self.connect_app_sid, + } + self._context: Optional[AuthorizedConnectAppContext] = None + + @property + def _proxy(self) -> "AuthorizedConnectAppContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthorizedConnectAppContext for this AuthorizedConnectAppInstance + """ + if self._context is None: + self._context = AuthorizedConnectAppContext( + self._version, + account_sid=self._solution["account_sid"], + connect_app_sid=self._solution["connect_app_sid"], + ) + return self._context + + def fetch(self) -> "AuthorizedConnectAppInstance": + """ + Fetch the AuthorizedConnectAppInstance + + + :returns: The fetched AuthorizedConnectAppInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthorizedConnectAppInstance": + """ + Asynchronous coroutine to fetch the AuthorizedConnectAppInstance + + + :returns: The fetched AuthorizedConnectAppInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AuthorizedConnectAppContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, connect_app_sid: str): + """ + Initialize the AuthorizedConnectAppContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AuthorizedConnectApp resource to fetch. + :param connect_app_sid: The SID of the Connect App to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "connect_app_sid": connect_app_sid, + } + self._uri = "/Accounts/{account_sid}/AuthorizedConnectApps/{connect_app_sid}.json".format( + **self._solution + ) + + def fetch(self) -> AuthorizedConnectAppInstance: + """ + Fetch the AuthorizedConnectAppInstance + + + :returns: The fetched AuthorizedConnectAppInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthorizedConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + connect_app_sid=self._solution["connect_app_sid"], + ) + + async def fetch_async(self) -> AuthorizedConnectAppInstance: + """ + Asynchronous coroutine to fetch the AuthorizedConnectAppInstance + + + :returns: The fetched AuthorizedConnectAppInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthorizedConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + connect_app_sid=self._solution["connect_app_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AuthorizedConnectAppPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AuthorizedConnectAppInstance: + """ + Build an instance of AuthorizedConnectAppInstance + + :param payload: Payload response from the API + """ + return AuthorizedConnectAppInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthorizedConnectAppList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the AuthorizedConnectAppList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AuthorizedConnectApp resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/AuthorizedConnectApps.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthorizedConnectAppInstance]: + """ + Streams AuthorizedConnectAppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthorizedConnectAppInstance]: + """ + Asynchronously streams AuthorizedConnectAppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizedConnectAppInstance]: + """ + Lists AuthorizedConnectAppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizedConnectAppInstance]: + """ + Asynchronously lists AuthorizedConnectAppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizedConnectAppPage: + """ + Retrieve a single page of AuthorizedConnectAppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizedConnectAppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthorizedConnectAppPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizedConnectAppPage: + """ + Asynchronously retrieve a single page of AuthorizedConnectAppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizedConnectAppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthorizedConnectAppPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AuthorizedConnectAppPage: + """ + Retrieve a specific page of AuthorizedConnectAppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizedConnectAppInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthorizedConnectAppPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AuthorizedConnectAppPage: + """ + Asynchronously retrieve a specific page of AuthorizedConnectAppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizedConnectAppInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthorizedConnectAppPage(self._version, response, self._solution) + + def get(self, connect_app_sid: str) -> AuthorizedConnectAppContext: + """ + Constructs a AuthorizedConnectAppContext + + :param connect_app_sid: The SID of the Connect App to fetch. + """ + return AuthorizedConnectAppContext( + self._version, + account_sid=self._solution["account_sid"], + connect_app_sid=connect_app_sid, + ) + + def __call__(self, connect_app_sid: str) -> AuthorizedConnectAppContext: + """ + Constructs a AuthorizedConnectAppContext + + :param connect_app_sid: The SID of the Connect App to fetch. + """ + return AuthorizedConnectAppContext( + self._version, + account_sid=self._solution["account_sid"], + connect_app_sid=connect_app_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__init__.py new file mode 100644 index 0000000..14fcfc2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__init__.py @@ -0,0 +1,598 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.available_phone_number_country.local import LocalList +from twilio.rest.api.v2010.account.available_phone_number_country.machine_to_machine import ( + MachineToMachineList, +) +from twilio.rest.api.v2010.account.available_phone_number_country.mobile import ( + MobileList, +) +from twilio.rest.api.v2010.account.available_phone_number_country.national import ( + NationalList, +) +from twilio.rest.api.v2010.account.available_phone_number_country.shared_cost import ( + SharedCostList, +) +from twilio.rest.api.v2010.account.available_phone_number_country.toll_free import ( + TollFreeList, +) +from twilio.rest.api.v2010.account.available_phone_number_country.voip import VoipList + + +class AvailablePhoneNumberCountryInstance(InstanceResource): + """ + :ivar country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country. + :ivar country: The name of the country. + :ivar uri: The URI of the Country resource, relative to `https://api.twilio.com`. + :ivar beta: Whether all phone numbers available in the country are new to the Twilio platform. `true` if they are and `false` if all numbers are not in the Twilio Phone Number Beta program. + :ivar subresource_uris: A list of related AvailablePhoneNumber resources identified by their URIs relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: Optional[str] = None, + ): + super().__init__(version) + + self.country_code: Optional[str] = payload.get("country_code") + self.country: Optional[str] = payload.get("country") + self.uri: Optional[str] = payload.get("uri") + self.beta: Optional[bool] = payload.get("beta") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "country_code": country_code or self.country_code, + } + self._context: Optional[AvailablePhoneNumberCountryContext] = None + + @property + def _proxy(self) -> "AvailablePhoneNumberCountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AvailablePhoneNumberCountryContext for this AvailablePhoneNumberCountryInstance + """ + if self._context is None: + self._context = AvailablePhoneNumberCountryContext( + self._version, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + return self._context + + def fetch(self) -> "AvailablePhoneNumberCountryInstance": + """ + Fetch the AvailablePhoneNumberCountryInstance + + + :returns: The fetched AvailablePhoneNumberCountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AvailablePhoneNumberCountryInstance": + """ + Asynchronous coroutine to fetch the AvailablePhoneNumberCountryInstance + + + :returns: The fetched AvailablePhoneNumberCountryInstance + """ + return await self._proxy.fetch_async() + + @property + def local(self) -> LocalList: + """ + Access the local + """ + return self._proxy.local + + @property + def machine_to_machine(self) -> MachineToMachineList: + """ + Access the machine_to_machine + """ + return self._proxy.machine_to_machine + + @property + def mobile(self) -> MobileList: + """ + Access the mobile + """ + return self._proxy.mobile + + @property + def national(self) -> NationalList: + """ + Access the national + """ + return self._proxy.national + + @property + def shared_cost(self) -> SharedCostList: + """ + Access the shared_cost + """ + return self._proxy.shared_cost + + @property + def toll_free(self) -> TollFreeList: + """ + Access the toll_free + """ + return self._proxy.toll_free + + @property + def voip(self) -> VoipList: + """ + Access the voip + """ + return self._proxy.voip + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AvailablePhoneNumberCountryContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the AvailablePhoneNumberCountryContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the available phone number Country resource. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country to fetch available phone number information about. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = ( + "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}.json".format( + **self._solution + ) + ) + + self._local: Optional[LocalList] = None + self._machine_to_machine: Optional[MachineToMachineList] = None + self._mobile: Optional[MobileList] = None + self._national: Optional[NationalList] = None + self._shared_cost: Optional[SharedCostList] = None + self._toll_free: Optional[TollFreeList] = None + self._voip: Optional[VoipList] = None + + def fetch(self) -> AvailablePhoneNumberCountryInstance: + """ + Fetch the AvailablePhoneNumberCountryInstance + + + :returns: The fetched AvailablePhoneNumberCountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AvailablePhoneNumberCountryInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + async def fetch_async(self) -> AvailablePhoneNumberCountryInstance: + """ + Asynchronous coroutine to fetch the AvailablePhoneNumberCountryInstance + + + :returns: The fetched AvailablePhoneNumberCountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AvailablePhoneNumberCountryInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + @property + def local(self) -> LocalList: + """ + Access the local + """ + if self._local is None: + self._local = LocalList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._local + + @property + def machine_to_machine(self) -> MachineToMachineList: + """ + Access the machine_to_machine + """ + if self._machine_to_machine is None: + self._machine_to_machine = MachineToMachineList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._machine_to_machine + + @property + def mobile(self) -> MobileList: + """ + Access the mobile + """ + if self._mobile is None: + self._mobile = MobileList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._mobile + + @property + def national(self) -> NationalList: + """ + Access the national + """ + if self._national is None: + self._national = NationalList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._national + + @property + def shared_cost(self) -> SharedCostList: + """ + Access the shared_cost + """ + if self._shared_cost is None: + self._shared_cost = SharedCostList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._shared_cost + + @property + def toll_free(self) -> TollFreeList: + """ + Access the toll_free + """ + if self._toll_free is None: + self._toll_free = TollFreeList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._toll_free + + @property + def voip(self) -> VoipList: + """ + Access the voip + """ + if self._voip is None: + self._voip = VoipList( + self._version, + self._solution["account_sid"], + self._solution["country_code"], + ) + return self._voip + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AvailablePhoneNumberCountryPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> AvailablePhoneNumberCountryInstance: + """ + Build an instance of AvailablePhoneNumberCountryInstance + + :param payload: Payload response from the API + """ + return AvailablePhoneNumberCountryInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AvailablePhoneNumberCountryList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the AvailablePhoneNumberCountryList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the available phone number Country resources. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AvailablePhoneNumberCountryInstance]: + """ + Streams AvailablePhoneNumberCountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AvailablePhoneNumberCountryInstance]: + """ + Asynchronously streams AvailablePhoneNumberCountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailablePhoneNumberCountryInstance]: + """ + Lists AvailablePhoneNumberCountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailablePhoneNumberCountryInstance]: + """ + Asynchronously lists AvailablePhoneNumberCountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailablePhoneNumberCountryPage: + """ + Retrieve a single page of AvailablePhoneNumberCountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailablePhoneNumberCountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AvailablePhoneNumberCountryPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailablePhoneNumberCountryPage: + """ + Asynchronously retrieve a single page of AvailablePhoneNumberCountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailablePhoneNumberCountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AvailablePhoneNumberCountryPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AvailablePhoneNumberCountryPage: + """ + Retrieve a specific page of AvailablePhoneNumberCountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailablePhoneNumberCountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AvailablePhoneNumberCountryPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AvailablePhoneNumberCountryPage: + """ + Asynchronously retrieve a specific page of AvailablePhoneNumberCountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailablePhoneNumberCountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AvailablePhoneNumberCountryPage(self._version, response, self._solution) + + def get(self, country_code: str) -> AvailablePhoneNumberCountryContext: + """ + Constructs a AvailablePhoneNumberCountryContext + + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country to fetch available phone number information about. + """ + return AvailablePhoneNumberCountryContext( + self._version, + account_sid=self._solution["account_sid"], + country_code=country_code, + ) + + def __call__(self, country_code: str) -> AvailablePhoneNumberCountryContext: + """ + Constructs a AvailablePhoneNumberCountryContext + + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country to fetch available phone number information about. + """ + return AvailablePhoneNumberCountryContext( + self._version, + account_sid=self._solution["account_sid"], + country_code=country_code, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d18279c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/local.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/local.cpython-310.pyc new file mode 100644 index 0000000..b31a6be Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/local.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/machine_to_machine.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/machine_to_machine.cpython-310.pyc new file mode 100644 index 0000000..72dd47d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/machine_to_machine.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/mobile.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/mobile.cpython-310.pyc new file mode 100644 index 0000000..098d5ec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/mobile.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/national.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/national.cpython-310.pyc new file mode 100644 index 0000000..4885bea Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/national.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/shared_cost.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/shared_cost.cpython-310.pyc new file mode 100644 index 0000000..1eefb8f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/shared_cost.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/toll_free.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/toll_free.cpython-310.pyc new file mode 100644 index 0000000..c913f4f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/toll_free.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/voip.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/voip.cpython-310.pyc new file mode 100644 index 0000000..73e193d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/__pycache__/voip.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/local.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/local.py new file mode 100644 index 0000000..43118d9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/local.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class LocalInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class LocalPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> LocalInstance: + """ + Build an instance of LocalInstance + + :param payload: Payload response from the API + """ + return LocalInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class LocalList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the LocalList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/Local.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[LocalInstance]: + """ + Streams LocalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[LocalInstance]: + """ + Asynchronously streams LocalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LocalInstance]: + """ + Lists LocalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LocalInstance]: + """ + Asynchronously lists LocalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LocalPage: + """ + Retrieve a single page of LocalInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LocalInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return LocalPage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LocalPage: + """ + Asynchronously retrieve a single page of LocalInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-number-pattern) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumberlocal-resource?code-sample=code-find-phone-numbers-by-character-pattern). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LocalInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return LocalPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> LocalPage: + """ + Retrieve a specific page of LocalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LocalInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return LocalPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> LocalPage: + """ + Asynchronously retrieve a specific page of LocalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LocalInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return LocalPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/machine_to_machine.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/machine_to_machine.py new file mode 100644 index 0000000..ee94aa9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/machine_to_machine.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MachineToMachineInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MachineToMachinePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MachineToMachineInstance: + """ + Build an instance of MachineToMachineInstance + + :param payload: Payload response from the API + """ + return MachineToMachineInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MachineToMachineList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the MachineToMachineList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/MachineToMachine.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MachineToMachineInstance]: + """ + Streams MachineToMachineInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MachineToMachineInstance]: + """ + Asynchronously streams MachineToMachineInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MachineToMachineInstance]: + """ + Lists MachineToMachineInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MachineToMachineInstance]: + """ + Asynchronously lists MachineToMachineInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MachineToMachinePage: + """ + Retrieve a single page of MachineToMachineInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MachineToMachineInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MachineToMachinePage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MachineToMachinePage: + """ + Asynchronously retrieve a single page of MachineToMachineInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MachineToMachineInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MachineToMachinePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MachineToMachinePage: + """ + Retrieve a specific page of MachineToMachineInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MachineToMachineInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MachineToMachinePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MachineToMachinePage: + """ + Asynchronously retrieve a specific page of MachineToMachineInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MachineToMachineInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MachineToMachinePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/mobile.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/mobile.py new file mode 100644 index 0000000..622d2b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/mobile.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MobileInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MobilePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MobileInstance: + """ + Build an instance of MobileInstance + + :param payload: Payload response from the API + """ + return MobileInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MobileList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the MobileList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/Mobile.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MobileInstance]: + """ + Streams MobileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MobileInstance]: + """ + Asynchronously streams MobileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MobileInstance]: + """ + Lists MobileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MobileInstance]: + """ + Asynchronously lists MobileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MobilePage: + """ + Retrieve a single page of MobileInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MobileInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MobilePage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MobilePage: + """ + Asynchronously retrieve a single page of MobileInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MobileInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MobilePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MobilePage: + """ + Retrieve a specific page of MobileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MobileInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MobilePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MobilePage: + """ + Asynchronously retrieve a specific page of MobileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MobileInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MobilePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/national.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/national.py new file mode 100644 index 0000000..d0f3a63 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/national.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class NationalInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NationalPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> NationalInstance: + """ + Build an instance of NationalInstance + + :param payload: Payload response from the API + """ + return NationalInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NationalList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the NationalList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/National.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NationalInstance]: + """ + Streams NationalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NationalInstance]: + """ + Asynchronously streams NationalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NationalInstance]: + """ + Lists NationalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NationalInstance]: + """ + Asynchronously lists NationalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NationalPage: + """ + Retrieve a single page of NationalInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NationalInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NationalPage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NationalPage: + """ + Asynchronously retrieve a single page of NationalInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NationalInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NationalPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> NationalPage: + """ + Retrieve a specific page of NationalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NationalInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NationalPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> NationalPage: + """ + Asynchronously retrieve a specific page of NationalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NationalInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NationalPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/shared_cost.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/shared_cost.py new file mode 100644 index 0000000..7b5b9b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/shared_cost.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SharedCostInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SharedCostPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SharedCostInstance: + """ + Build an instance of SharedCostInstance + + :param payload: Payload response from the API + """ + return SharedCostInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SharedCostList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the SharedCostList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/SharedCost.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SharedCostInstance]: + """ + Streams SharedCostInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SharedCostInstance]: + """ + Asynchronously streams SharedCostInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SharedCostInstance]: + """ + Lists SharedCostInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SharedCostInstance]: + """ + Asynchronously lists SharedCostInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SharedCostPage: + """ + Retrieve a single page of SharedCostInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SharedCostInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SharedCostPage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SharedCostPage: + """ + Asynchronously retrieve a single page of SharedCostInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SharedCostInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SharedCostPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SharedCostPage: + """ + Retrieve a specific page of SharedCostInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SharedCostInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SharedCostPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SharedCostPage: + """ + Asynchronously retrieve a specific page of SharedCostInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SharedCostInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SharedCostPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/toll_free.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/toll_free.py new file mode 100644 index 0000000..4368018 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/toll_free.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TollFreeInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TollFreePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TollFreeInstance: + """ + Build an instance of TollFreeInstance + + :param payload: Payload response from the API + """ + return TollFreeInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TollFreeList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the TollFreeList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/TollFree.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TollFreeInstance]: + """ + Streams TollFreeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TollFreeInstance]: + """ + Asynchronously streams TollFreeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollFreeInstance]: + """ + Lists TollFreeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollFreeInstance]: + """ + Asynchronously lists TollFreeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollFreePage: + """ + Retrieve a single page of TollFreeInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollFreeInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TollFreePage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollFreePage: + """ + Asynchronously retrieve a single page of TollFreeInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollFreeInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TollFreePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TollFreePage: + """ + Retrieve a specific page of TollFreeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollFreeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TollFreePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TollFreePage: + """ + Asynchronously retrieve a specific page of TollFreeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollFreeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TollFreePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/voip.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/voip.py new file mode 100644 index 0000000..e0615d5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/available_phone_number_country/voip.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class VoipInstance(InstanceResource): + """ + :ivar friendly_name: A formatted version of the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar lata: The [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) of this phone number. Available for only phone numbers from the US and Canada. + :ivar locality: The locality or city of this phone number's location. + :ivar rate_center: The [rate center](https://en.wikipedia.org/wiki/Telephone_exchange) of this phone number. Available for only phone numbers from the US and Canada. + :ivar latitude: The latitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar longitude: The longitude of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar region: The two-letter state or province abbreviation of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar postal_code: The postal or ZIP code of this phone number's location. Available for only phone numbers from the US and Canada. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of this phone number. + :ivar address_requirements: The type of [Address](https://www.twilio.com/docs/usage/api/address) resource the phone number requires. Can be: `none`, `any`, `local`, or `foreign`. `none` means no address is required. `any` means an address is required, but it can be anywhere in the world. `local` means an address in the phone number's country is required. `foreign` means an address outside of the phone number's country is required. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + country_code: str, + ): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.lata: Optional[str] = payload.get("lata") + self.locality: Optional[str] = payload.get("locality") + self.rate_center: Optional[str] = payload.get("rate_center") + self.latitude: Optional[float] = deserialize.decimal(payload.get("latitude")) + self.longitude: Optional[float] = deserialize.decimal(payload.get("longitude")) + self.region: Optional[str] = payload.get("region") + self.postal_code: Optional[str] = payload.get("postal_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.address_requirements: Optional[str] = payload.get("address_requirements") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VoipPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> VoipInstance: + """ + Build an instance of VoipInstance + + :param payload: Payload response from the API + """ + return VoipInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + country_code=self._solution["country_code"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class VoipList(ListResource): + + def __init__(self, version: Version, account_sid: str, country_code: str): + """ + Initialize the VoipList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) requesting the AvailablePhoneNumber resources. + :param country_code: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country from which to read phone numbers. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "country_code": country_code, + } + self._uri = "/Accounts/{account_sid}/AvailablePhoneNumbers/{country_code}/Voip.json".format( + **self._solution + ) + + def stream( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[VoipInstance]: + """ + Streams VoipInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[VoipInstance]: + """ + Asynchronously streams VoipInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VoipInstance]: + """ + Lists VoipInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VoipInstance]: + """ + Asynchronously lists VoipInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param str contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param bool sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param bool mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param bool voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param bool exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param bool beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param str near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param int distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param str in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param str in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param str in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param str in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param str in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param bool fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + area_code=area_code, + contains=contains, + sms_enabled=sms_enabled, + mms_enabled=mms_enabled, + voice_enabled=voice_enabled, + exclude_all_address_required=exclude_all_address_required, + exclude_local_address_required=exclude_local_address_required, + exclude_foreign_address_required=exclude_foreign_address_required, + beta=beta, + near_number=near_number, + near_lat_long=near_lat_long, + distance=distance, + in_postal_code=in_postal_code, + in_region=in_region, + in_rate_center=in_rate_center, + in_lata=in_lata, + in_locality=in_locality, + fax_enabled=fax_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VoipPage: + """ + Retrieve a single page of VoipInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VoipInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return VoipPage(self._version, response, self._solution) + + async def page_async( + self, + area_code: Union[int, object] = values.unset, + contains: Union[str, object] = values.unset, + sms_enabled: Union[bool, object] = values.unset, + mms_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + exclude_all_address_required: Union[bool, object] = values.unset, + exclude_local_address_required: Union[bool, object] = values.unset, + exclude_foreign_address_required: Union[bool, object] = values.unset, + beta: Union[bool, object] = values.unset, + near_number: Union[str, object] = values.unset, + near_lat_long: Union[str, object] = values.unset, + distance: Union[int, object] = values.unset, + in_postal_code: Union[str, object] = values.unset, + in_region: Union[str, object] = values.unset, + in_rate_center: Union[str, object] = values.unset, + in_lata: Union[str, object] = values.unset, + in_locality: Union[str, object] = values.unset, + fax_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VoipPage: + """ + Asynchronously retrieve a single page of VoipInstance records from the API. + Request is executed immediately + + :param area_code: The area code of the phone numbers to read. Applies to only phone numbers in the US and Canada. + :param contains: The pattern on which to match phone numbers. Valid characters are `*`, `0-9`, `a-z`, and `A-Z`. The `*` character matches any single digit. For examples, see [Example 2](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-2) and [Example 3](https://www.twilio.com/docs/phone-numbers/api/availablephonenumber-resource#local-get-basic-example-3). If specified, this value must have at least two characters. + :param sms_enabled: Whether the phone numbers can receive text messages. Can be: `true` or `false`. + :param mms_enabled: Whether the phone numbers can receive MMS messages. Can be: `true` or `false`. + :param voice_enabled: Whether the phone numbers can receive calls. Can be: `true` or `false`. + :param exclude_all_address_required: Whether to exclude phone numbers that require an [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_local_address_required: Whether to exclude phone numbers that require a local [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param exclude_foreign_address_required: Whether to exclude phone numbers that require a foreign [Address](https://www.twilio.com/docs/usage/api/address). Can be: `true` or `false` and the default is `false`. + :param beta: Whether to read phone numbers that are new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param near_number: Given a phone number, find a geographically close number within `distance` miles. Distance defaults to 25 miles. Applies to only phone numbers in the US and Canada. + :param near_lat_long: Given a latitude/longitude pair `lat,long` find geographically close numbers within `distance` miles. Applies to only phone numbers in the US and Canada. + :param distance: The search radius, in miles, for a `near_` query. Can be up to `500` and the default is `25`. Applies to only phone numbers in the US and Canada. + :param in_postal_code: Limit results to a particular postal code. Given a phone number, search within the same postal code as that number. Applies to only phone numbers in the US and Canada. + :param in_region: Limit results to a particular region, state, or province. Given a phone number, search within the same region as that number. Applies to only phone numbers in the US and Canada. + :param in_rate_center: Limit results to a specific rate center, or given a phone number search within the same rate center as that number. Requires `in_lata` to be set as well. Applies to only phone numbers in the US and Canada. + :param in_lata: Limit results to a specific local access and transport area ([LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area)). Given a phone number, search within the same [LATA](https://en.wikipedia.org/wiki/Local_access_and_transport_area) as that number. Applies to only phone numbers in the US and Canada. + :param in_locality: Limit results to a particular locality or city. Given a phone number, search within the same Locality as that number. + :param fax_enabled: Whether the phone numbers can receive faxes. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VoipInstance + """ + data = values.of( + { + "AreaCode": area_code, + "Contains": contains, + "SmsEnabled": serialize.boolean_to_string(sms_enabled), + "MmsEnabled": serialize.boolean_to_string(mms_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "ExcludeAllAddressRequired": serialize.boolean_to_string( + exclude_all_address_required + ), + "ExcludeLocalAddressRequired": serialize.boolean_to_string( + exclude_local_address_required + ), + "ExcludeForeignAddressRequired": serialize.boolean_to_string( + exclude_foreign_address_required + ), + "Beta": serialize.boolean_to_string(beta), + "NearNumber": near_number, + "NearLatLong": near_lat_long, + "Distance": distance, + "InPostalCode": in_postal_code, + "InRegion": in_region, + "InRateCenter": in_rate_center, + "InLata": in_lata, + "InLocality": in_locality, + "FaxEnabled": serialize.boolean_to_string(fax_enabled), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return VoipPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> VoipPage: + """ + Retrieve a specific page of VoipInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VoipInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return VoipPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> VoipPage: + """ + Asynchronously retrieve a specific page of VoipInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VoipInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return VoipPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/balance.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/balance.py new file mode 100644 index 0000000..0e90cda --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/balance.py @@ -0,0 +1,102 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BalanceInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar balance: The balance of the Account, in units specified by the unit parameter. Balance changes may not be reflected immediately. Child accounts do not contain balance information + :ivar currency: The units of currency for the account balance + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.balance: Optional[str] = payload.get("balance") + self.currency: Optional[str] = payload.get("currency") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BalanceList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the BalanceList + + :param version: Version that contains the resource + :param account_sid: The unique SID identifier of the Account. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Balance.json".format(**self._solution) + + def fetch(self) -> BalanceInstance: + """ + Asynchronously fetch the BalanceInstance + + + :returns: The fetched BalanceInstance + """ + + payload = self._version.fetch(method="GET", uri=self._uri) + + return BalanceInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def fetch_async(self) -> BalanceInstance: + """ + Asynchronously fetch the BalanceInstance + + + :returns: The fetched BalanceInstance + """ + + payload = await self._version.fetch_async(method="GET", uri=self._uri) + + return BalanceInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__init__.py new file mode 100644 index 0000000..2ba343f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__init__.py @@ -0,0 +1,1348 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.call.event import EventList +from twilio.rest.api.v2010.account.call.notification import NotificationList +from twilio.rest.api.v2010.account.call.payment import PaymentList +from twilio.rest.api.v2010.account.call.recording import RecordingList +from twilio.rest.api.v2010.account.call.siprec import SiprecList +from twilio.rest.api.v2010.account.call.stream import StreamList +from twilio.rest.api.v2010.account.call.user_defined_message import ( + UserDefinedMessageList, +) +from twilio.rest.api.v2010.account.call.user_defined_message_subscription import ( + UserDefinedMessageSubscriptionList, +) + + +class CallInstance(InstanceResource): + + class Status(object): + QUEUED = "queued" + RINGING = "ringing" + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + BUSY = "busy" + FAILED = "failed" + NO_ANSWER = "no-answer" + CANCELED = "canceled" + + class UpdateStatus(object): + CANCELED = "canceled" + COMPLETED = "completed" + + """ + :ivar sid: The unique string that we created to identify this Call resource. + :ivar date_created: The date and time in UTC that this resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in UTC that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar parent_call_sid: The SID that identifies the call that created this leg. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Call resource. + :ivar to: The phone number, SIP address, Client identifier or SIM SID that received this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). SIP addresses are formatted as `name@company.com`. Client identifiers are formatted `client:name`. SIM SIDs are formatted as `sim:sid`. + :ivar to_formatted: The phone number, SIP address or Client identifier that received this call. Formatted for display. Non-North American phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +442071838750). + :ivar _from: The phone number, SIP address, Client identifier or SIM SID that made this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). SIP addresses are formatted as `name@company.com`. Client identifiers are formatted `client:name`. SIM SIDs are formatted as `sim:sid`. + :ivar from_formatted: The calling phone number, SIP address, or Client identifier formatted for display. Non-North American phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +442071838750). + :ivar phone_number_sid: If the call was inbound, this is the SID of the IncomingPhoneNumber resource that received the call. If the call was outbound, it is the SID of the OutgoingCallerId resource from which the call was placed. + :ivar status: + :ivar start_time: The start time of the call, given as UTC in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. Empty if the call has not yet been dialed. + :ivar end_time: The time the call ended, given as UTC in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. Empty if the call did not complete successfully. + :ivar duration: The length of the call in seconds. This value is empty for busy, failed, unanswered, or ongoing calls. + :ivar price: The charge for this call, in the currency associated with the account. Populated after the call is completed. May not be immediately available. + :ivar price_unit: The currency in which `Price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g., `USD`, `EUR`, `JPY`). Always capitalized for calls. + :ivar direction: A string describing the direction of the call. Can be: `inbound` for inbound calls, `outbound-api` for calls initiated via the REST API or `outbound-dial` for calls initiated by a `` verb. Using [Elastic SIP Trunking](https://www.twilio.com/docs/sip-trunking), the values can be [`trunking-terminating`](https://www.twilio.com/docs/sip-trunking#termination) for outgoing calls from your communications infrastructure to the PSTN or [`trunking-originating`](https://www.twilio.com/docs/sip-trunking#origination) for incoming calls to your communications infrastructure from the PSTN. + :ivar answered_by: Either `human` or `machine` if this call was initiated with answering machine detection. Empty otherwise. + :ivar api_version: The API version used to create the call. + :ivar forwarded_from: The forwarding phone number if this call was an incoming call forwarded from another number (depends on carrier supporting forwarding). Otherwise, empty. + :ivar group_sid: The Group SID associated with this call. If no Group is associated with the call, the field is empty. + :ivar caller_name: The caller's name if this call was an incoming call to a phone number with caller ID Lookup enabled. Otherwise, empty. + :ivar queue_time: The wait time in milliseconds before the call is placed. + :ivar trunk_sid: The unique identifier of the trunk resource that was used for this call. The field is empty if the call was not made using a SIP trunk or if the call is not terminated. + :ivar uri: The URI of this resource, relative to `https://api.twilio.com`. + :ivar subresource_uris: A list of subresources available to this call, identified by their URIs relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.parent_call_sid: Optional[str] = payload.get("parent_call_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.to: Optional[str] = payload.get("to") + self.to_formatted: Optional[str] = payload.get("to_formatted") + self._from: Optional[str] = payload.get("from") + self.from_formatted: Optional[str] = payload.get("from_formatted") + self.phone_number_sid: Optional[str] = payload.get("phone_number_sid") + self.status: Optional["CallInstance.Status"] = payload.get("status") + self.start_time: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("end_time") + ) + self.duration: Optional[str] = payload.get("duration") + self.price: Optional[str] = payload.get("price") + self.price_unit: Optional[str] = payload.get("price_unit") + self.direction: Optional[str] = payload.get("direction") + self.answered_by: Optional[str] = payload.get("answered_by") + self.api_version: Optional[str] = payload.get("api_version") + self.forwarded_from: Optional[str] = payload.get("forwarded_from") + self.group_sid: Optional[str] = payload.get("group_sid") + self.caller_name: Optional[str] = payload.get("caller_name") + self.queue_time: Optional[str] = payload.get("queue_time") + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.uri: Optional[str] = payload.get("uri") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[CallContext] = None + + @property + def _proxy(self) -> "CallContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CallContext for this CallInstance + """ + if self._context is None: + self._context = CallContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CallInstance": + """ + Fetch the CallInstance + + + :returns: The fetched CallInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CallInstance": + """ + Asynchronous coroutine to fetch the CallInstance + + + :returns: The fetched CallInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + url: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + status: Union["CallInstance.UpdateStatus", object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + ) -> "CallInstance": + """ + Update the CallInstance + + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param method: The HTTP method we should use when calling the `url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status: + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_method: The HTTP method we should use when requesting the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url. Twiml and url parameters are mutually exclusive + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + + :returns: The updated CallInstance + """ + return self._proxy.update( + url=url, + method=method, + status=status, + fallback_url=fallback_url, + fallback_method=fallback_method, + status_callback=status_callback, + status_callback_method=status_callback_method, + twiml=twiml, + time_limit=time_limit, + ) + + async def update_async( + self, + url: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + status: Union["CallInstance.UpdateStatus", object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + ) -> "CallInstance": + """ + Asynchronous coroutine to update the CallInstance + + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param method: The HTTP method we should use when calling the `url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status: + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_method: The HTTP method we should use when requesting the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url. Twiml and url parameters are mutually exclusive + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + + :returns: The updated CallInstance + """ + return await self._proxy.update_async( + url=url, + method=method, + status=status, + fallback_url=fallback_url, + fallback_method=fallback_method, + status_callback=status_callback, + status_callback_method=status_callback_method, + twiml=twiml, + time_limit=time_limit, + ) + + @property + def events(self) -> EventList: + """ + Access the events + """ + return self._proxy.events + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + return self._proxy.notifications + + @property + def payments(self) -> PaymentList: + """ + Access the payments + """ + return self._proxy.payments + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + return self._proxy.recordings + + @property + def siprec(self) -> SiprecList: + """ + Access the siprec + """ + return self._proxy.siprec + + @property + def streams(self) -> StreamList: + """ + Access the streams + """ + return self._proxy.streams + + @property + def user_defined_messages(self) -> UserDefinedMessageList: + """ + Access the user_defined_messages + """ + return self._proxy.user_defined_messages + + @property + def user_defined_message_subscriptions(self) -> UserDefinedMessageSubscriptionList: + """ + Access the user_defined_message_subscriptions + """ + return self._proxy.user_defined_message_subscriptions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the CallContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Call resource(s) to update. + :param sid: The Twilio-provided string that uniquely identifies the Call resource to update + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{sid}.json".format(**self._solution) + + self._events: Optional[EventList] = None + self._notifications: Optional[NotificationList] = None + self._payments: Optional[PaymentList] = None + self._recordings: Optional[RecordingList] = None + self._siprec: Optional[SiprecList] = None + self._streams: Optional[StreamList] = None + self._user_defined_messages: Optional[UserDefinedMessageList] = None + self._user_defined_message_subscriptions: Optional[ + UserDefinedMessageSubscriptionList + ] = None + + def delete(self) -> bool: + """ + Deletes the CallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CallInstance: + """ + Fetch the CallInstance + + + :returns: The fetched CallInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CallInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CallInstance: + """ + Asynchronous coroutine to fetch the CallInstance + + + :returns: The fetched CallInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CallInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + url: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + status: Union["CallInstance.UpdateStatus", object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + ) -> CallInstance: + """ + Update the CallInstance + + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param method: The HTTP method we should use when calling the `url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status: + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_method: The HTTP method we should use when requesting the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url. Twiml and url parameters are mutually exclusive + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + + :returns: The updated CallInstance + """ + data = values.of( + { + "Url": url, + "Method": method, + "Status": status, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Twiml": twiml, + "TimeLimit": time_limit, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CallInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + url: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + status: Union["CallInstance.UpdateStatus", object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + ) -> CallInstance: + """ + Asynchronous coroutine to update the CallInstance + + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param method: The HTTP method we should use when calling the `url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status: + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_method: The HTTP method we should use when requesting the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url. Twiml and url parameters are mutually exclusive + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + + :returns: The updated CallInstance + """ + data = values.of( + { + "Url": url, + "Method": method, + "Status": status, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Twiml": twiml, + "TimeLimit": time_limit, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CallInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def events(self) -> EventList: + """ + Access the events + """ + if self._events is None: + self._events = EventList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._events + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + if self._notifications is None: + self._notifications = NotificationList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._notifications + + @property + def payments(self) -> PaymentList: + """ + Access the payments + """ + if self._payments is None: + self._payments = PaymentList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._payments + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + if self._recordings is None: + self._recordings = RecordingList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._recordings + + @property + def siprec(self) -> SiprecList: + """ + Access the siprec + """ + if self._siprec is None: + self._siprec = SiprecList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._siprec + + @property + def streams(self) -> StreamList: + """ + Access the streams + """ + if self._streams is None: + self._streams = StreamList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._streams + + @property + def user_defined_messages(self) -> UserDefinedMessageList: + """ + Access the user_defined_messages + """ + if self._user_defined_messages is None: + self._user_defined_messages = UserDefinedMessageList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._user_defined_messages + + @property + def user_defined_message_subscriptions(self) -> UserDefinedMessageSubscriptionList: + """ + Access the user_defined_message_subscriptions + """ + if self._user_defined_message_subscriptions is None: + self._user_defined_message_subscriptions = ( + UserDefinedMessageSubscriptionList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + ) + return self._user_defined_message_subscriptions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CallInstance: + """ + Build an instance of CallInstance + + :param payload: Payload response from the API + """ + return CallInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CallList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the CallList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Call resource(s) to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Calls.json".format(**self._solution) + + def create( + self, + to: str, + from_: str, + method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_event: Union[List[str], object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + send_digits: Union[str, object] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + machine_detection: Union[str, object] = values.unset, + machine_detection_timeout: Union[int, object] = values.unset, + recording_status_callback_event: Union[List[str], object] = values.unset, + trim: Union[str, object] = values.unset, + caller_id: Union[str, object] = values.unset, + machine_detection_speech_threshold: Union[int, object] = values.unset, + machine_detection_speech_end_threshold: Union[int, object] = values.unset, + machine_detection_silence_timeout: Union[int, object] = values.unset, + async_amd: Union[str, object] = values.unset, + async_amd_status_callback: Union[str, object] = values.unset, + async_amd_status_callback_method: Union[str, object] = values.unset, + byoc: Union[str, object] = values.unset, + call_reason: Union[str, object] = values.unset, + call_token: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + url: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + application_sid: Union[str, object] = values.unset, + ) -> CallInstance: + """ + Create the CallInstance + + :param to: The phone number, SIP address, or client identifier to call. + :param from_: The phone number or client identifier to use as the caller id. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `From` must also be a phone number. + :param method: The HTTP method we should use when calling the `url` parameter's value. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_event: The call progress events that we will send to the `status_callback` URL. Can be: `initiated`, `ringing`, `answered`, and `completed`. If no event is specified, we send the `completed` status. If you want to receive multiple events, specify each one in a separate `status_callback_event` parameter. See the code sample for [monitoring call progress](https://www.twilio.com/docs/voice/api/call-resource?code-sample=code-create-a-call-resource-and-specify-a-statuscallbackevent&code-sdk-version=json). If an `application_sid` is present, this parameter is ignored. + :param status_callback_method: The HTTP method we should use when calling the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param send_digits: A string of keys to dial after connecting to the number, maximum of 32 digits. Valid digits in the string include: any digit (`0`-`9`), '`#`', '`*`' and '`w`', to insert a half second pause. For example, if you connected to a company phone number and wanted to pause for one second, and then dial extension 1234 followed by the pound key, the value of this parameter would be `ww1234#`. Remember to URL-encode this string, since the '`#`' character has special meaning in a URL. If both `SendDigits` and `MachineDetection` parameters are provided, then `MachineDetection` will be ignored. + :param timeout: The integer number of seconds that we should allow the phone to ring before assuming there is no answer. The default is `60` seconds and the maximum is `600` seconds. For some call flows, we will add a 5-second buffer to the timeout value you provide. For this reason, a timeout value of 10 seconds could result in an actual timeout closer to 15 seconds. You can set this to a short time, such as `15` seconds, to hang up before reaching an answering machine or voicemail. + :param record: Whether to record the call. Can be `true` to record the phone call, or `false` to not. The default is `false`. The `recording_url` is sent to the `status_callback` URL. + :param recording_channels: The number of channels in the final recording. Can be: `mono` or `dual`. The default is `mono`. `mono` records both legs of the call in a single channel of the recording file. `dual` records each leg to a separate channel of the recording file. The first channel of a dual-channel recording contains the parent call and the second channel contains the child call. + :param recording_status_callback: The URL that we call when the recording is available to be accessed. + :param recording_status_callback_method: The HTTP method we should use when calling the `recording_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param sip_auth_username: The username used to authenticate the caller making a SIP call. + :param sip_auth_password: The password required to authenticate the user account specified in `sip_auth_username`. + :param machine_detection: Whether to detect if a human, answering machine, or fax has picked up the call. Can be: `Enable` or `DetectMessageEnd`. Use `Enable` if you would like us to return `AnsweredBy` as soon as the called party is identified. Use `DetectMessageEnd`, if you would like to leave a message on an answering machine. If `send_digits` is provided, this parameter is ignored. For more information, see [Answering Machine Detection](https://www.twilio.com/docs/voice/answering-machine-detection). + :param machine_detection_timeout: The number of seconds that we should attempt to detect an answering machine before timing out and sending a voice request with `AnsweredBy` of `unknown`. The default timeout is 30 seconds. + :param recording_status_callback_event: The recording status events that will trigger calls to the URL specified in `recording_status_callback`. Can be: `in-progress`, `completed` and `absent`. Defaults to `completed`. Separate multiple values with a space. + :param trim: Whether to trim any leading and trailing silence from the recording. Can be: `trim-silence` or `do-not-trim` and the default is `trim-silence`. + :param caller_id: The phone number, SIP address, or Client identifier that made this call. Phone numbers are in [E.164 format](https://wwnw.twilio.com/docs/glossary/what-e164) (e.g., +16175551212). SIP addresses are formatted as `name@company.com`. + :param machine_detection_speech_threshold: The number of milliseconds that is used as the measuring stick for the length of the speech activity, where durations lower than this value will be interpreted as a human and longer than this value as a machine. Possible Values: 1000-6000. Default: 2400. + :param machine_detection_speech_end_threshold: The number of milliseconds of silence after speech activity at which point the speech activity is considered complete. Possible Values: 500-5000. Default: 1200. + :param machine_detection_silence_timeout: The number of milliseconds of initial silence after which an `unknown` AnsweredBy result will be returned. Possible Values: 2000-10000. Default: 5000. + :param async_amd: Select whether to perform answering machine detection in the background. Default, blocks the execution of the call until Answering Machine Detection is completed. Can be: `true` or `false`. + :param async_amd_status_callback: The URL that we should call using the `async_amd_status_callback_method` to notify customer application whether the call was answered by human, machine or fax. + :param async_amd_status_callback_method: The HTTP method we should use when calling the `async_amd_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param byoc: The SID of a BYOC (Bring Your Own Carrier) trunk to route this call with. Note that `byoc` is only meaningful when `to` is a phone number; it will otherwise be ignored. (Beta) + :param call_reason: The Reason for the outgoing call. Use it to specify the purpose of the call that is presented on the called party's phone. (Branded Calls Beta) + :param call_token: A token string needed to invoke a forwarded call. A call_token is generated when an incoming call is received on a Twilio number. Pass an incoming call's call_token value to a forwarded call via the call_token parameter when creating a new call. A forwarded call should bear the same CallerID of the original incoming call. + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is generated from Twilio. `both` records the audio that is received and generated by Twilio. + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url parameter. If both `twiml` and `url` are provided then `twiml` parameter will be ignored. Max 4000 characters. + :param application_sid: The SID of the Application resource that will handle the call, if the call will be handled by an application. + + :returns: The created CallInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Method": method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "StatusCallbackMethod": status_callback_method, + "SendDigits": send_digits, + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "MachineDetection": machine_detection, + "MachineDetectionTimeout": machine_detection_timeout, + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "Trim": trim, + "CallerId": caller_id, + "MachineDetectionSpeechThreshold": machine_detection_speech_threshold, + "MachineDetectionSpeechEndThreshold": machine_detection_speech_end_threshold, + "MachineDetectionSilenceTimeout": machine_detection_silence_timeout, + "AsyncAmd": async_amd, + "AsyncAmdStatusCallback": async_amd_status_callback, + "AsyncAmdStatusCallbackMethod": async_amd_status_callback_method, + "Byoc": byoc, + "CallReason": call_reason, + "CallToken": call_token, + "RecordingTrack": recording_track, + "TimeLimit": time_limit, + "Url": url, + "Twiml": twiml, + "ApplicationSid": application_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CallInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + to: str, + from_: str, + method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_event: Union[List[str], object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + send_digits: Union[str, object] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + machine_detection: Union[str, object] = values.unset, + machine_detection_timeout: Union[int, object] = values.unset, + recording_status_callback_event: Union[List[str], object] = values.unset, + trim: Union[str, object] = values.unset, + caller_id: Union[str, object] = values.unset, + machine_detection_speech_threshold: Union[int, object] = values.unset, + machine_detection_speech_end_threshold: Union[int, object] = values.unset, + machine_detection_silence_timeout: Union[int, object] = values.unset, + async_amd: Union[str, object] = values.unset, + async_amd_status_callback: Union[str, object] = values.unset, + async_amd_status_callback_method: Union[str, object] = values.unset, + byoc: Union[str, object] = values.unset, + call_reason: Union[str, object] = values.unset, + call_token: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + url: Union[str, object] = values.unset, + twiml: Union[str, object] = values.unset, + application_sid: Union[str, object] = values.unset, + ) -> CallInstance: + """ + Asynchronously create the CallInstance + + :param to: The phone number, SIP address, or client identifier to call. + :param from_: The phone number or client identifier to use as the caller id. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `From` must also be a phone number. + :param method: The HTTP method we should use when calling the `url` parameter's value. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_url: The URL that we call using the `fallback_method` if an error occurs when requesting or executing the TwiML at `url`. If an `application_sid` parameter is present, this parameter is ignored. + :param fallback_method: The HTTP method that we should use to request the `fallback_url`. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. If no `status_callback_event` is specified, we will send the `completed` status. If an `application_sid` parameter is present, this parameter is ignored. URLs must contain a valid hostname (underscores are not permitted). + :param status_callback_event: The call progress events that we will send to the `status_callback` URL. Can be: `initiated`, `ringing`, `answered`, and `completed`. If no event is specified, we send the `completed` status. If you want to receive multiple events, specify each one in a separate `status_callback_event` parameter. See the code sample for [monitoring call progress](https://www.twilio.com/docs/voice/api/call-resource?code-sample=code-create-a-call-resource-and-specify-a-statuscallbackevent&code-sdk-version=json). If an `application_sid` is present, this parameter is ignored. + :param status_callback_method: The HTTP method we should use when calling the `status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. If an `application_sid` parameter is present, this parameter is ignored. + :param send_digits: A string of keys to dial after connecting to the number, maximum of 32 digits. Valid digits in the string include: any digit (`0`-`9`), '`#`', '`*`' and '`w`', to insert a half second pause. For example, if you connected to a company phone number and wanted to pause for one second, and then dial extension 1234 followed by the pound key, the value of this parameter would be `ww1234#`. Remember to URL-encode this string, since the '`#`' character has special meaning in a URL. If both `SendDigits` and `MachineDetection` parameters are provided, then `MachineDetection` will be ignored. + :param timeout: The integer number of seconds that we should allow the phone to ring before assuming there is no answer. The default is `60` seconds and the maximum is `600` seconds. For some call flows, we will add a 5-second buffer to the timeout value you provide. For this reason, a timeout value of 10 seconds could result in an actual timeout closer to 15 seconds. You can set this to a short time, such as `15` seconds, to hang up before reaching an answering machine or voicemail. + :param record: Whether to record the call. Can be `true` to record the phone call, or `false` to not. The default is `false`. The `recording_url` is sent to the `status_callback` URL. + :param recording_channels: The number of channels in the final recording. Can be: `mono` or `dual`. The default is `mono`. `mono` records both legs of the call in a single channel of the recording file. `dual` records each leg to a separate channel of the recording file. The first channel of a dual-channel recording contains the parent call and the second channel contains the child call. + :param recording_status_callback: The URL that we call when the recording is available to be accessed. + :param recording_status_callback_method: The HTTP method we should use when calling the `recording_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param sip_auth_username: The username used to authenticate the caller making a SIP call. + :param sip_auth_password: The password required to authenticate the user account specified in `sip_auth_username`. + :param machine_detection: Whether to detect if a human, answering machine, or fax has picked up the call. Can be: `Enable` or `DetectMessageEnd`. Use `Enable` if you would like us to return `AnsweredBy` as soon as the called party is identified. Use `DetectMessageEnd`, if you would like to leave a message on an answering machine. If `send_digits` is provided, this parameter is ignored. For more information, see [Answering Machine Detection](https://www.twilio.com/docs/voice/answering-machine-detection). + :param machine_detection_timeout: The number of seconds that we should attempt to detect an answering machine before timing out and sending a voice request with `AnsweredBy` of `unknown`. The default timeout is 30 seconds. + :param recording_status_callback_event: The recording status events that will trigger calls to the URL specified in `recording_status_callback`. Can be: `in-progress`, `completed` and `absent`. Defaults to `completed`. Separate multiple values with a space. + :param trim: Whether to trim any leading and trailing silence from the recording. Can be: `trim-silence` or `do-not-trim` and the default is `trim-silence`. + :param caller_id: The phone number, SIP address, or Client identifier that made this call. Phone numbers are in [E.164 format](https://wwnw.twilio.com/docs/glossary/what-e164) (e.g., +16175551212). SIP addresses are formatted as `name@company.com`. + :param machine_detection_speech_threshold: The number of milliseconds that is used as the measuring stick for the length of the speech activity, where durations lower than this value will be interpreted as a human and longer than this value as a machine. Possible Values: 1000-6000. Default: 2400. + :param machine_detection_speech_end_threshold: The number of milliseconds of silence after speech activity at which point the speech activity is considered complete. Possible Values: 500-5000. Default: 1200. + :param machine_detection_silence_timeout: The number of milliseconds of initial silence after which an `unknown` AnsweredBy result will be returned. Possible Values: 2000-10000. Default: 5000. + :param async_amd: Select whether to perform answering machine detection in the background. Default, blocks the execution of the call until Answering Machine Detection is completed. Can be: `true` or `false`. + :param async_amd_status_callback: The URL that we should call using the `async_amd_status_callback_method` to notify customer application whether the call was answered by human, machine or fax. + :param async_amd_status_callback_method: The HTTP method we should use when calling the `async_amd_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param byoc: The SID of a BYOC (Bring Your Own Carrier) trunk to route this call with. Note that `byoc` is only meaningful when `to` is a phone number; it will otherwise be ignored. (Beta) + :param call_reason: The Reason for the outgoing call. Use it to specify the purpose of the call that is presented on the called party's phone. (Branded Calls Beta) + :param call_token: A token string needed to invoke a forwarded call. A call_token is generated when an incoming call is received on a Twilio number. Pass an incoming call's call_token value to a forwarded call via the call_token parameter when creating a new call. A forwarded call should bear the same CallerID of the original incoming call. + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is generated from Twilio. `both` records the audio that is received and generated by Twilio. + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + :param url: The absolute URL that returns the TwiML instructions for the call. We will call this URL using the `method` when the call connects. For more information, see the [Url Parameter](https://www.twilio.com/docs/voice/make-calls#specify-a-url-parameter) section in [Making Calls](https://www.twilio.com/docs/voice/make-calls). + :param twiml: TwiML instructions for the call Twilio will use without fetching Twiml from url parameter. If both `twiml` and `url` are provided then `twiml` parameter will be ignored. Max 4000 characters. + :param application_sid: The SID of the Application resource that will handle the call, if the call will be handled by an application. + + :returns: The created CallInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Method": method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "StatusCallbackMethod": status_callback_method, + "SendDigits": send_digits, + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "MachineDetection": machine_detection, + "MachineDetectionTimeout": machine_detection_timeout, + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "Trim": trim, + "CallerId": caller_id, + "MachineDetectionSpeechThreshold": machine_detection_speech_threshold, + "MachineDetectionSpeechEndThreshold": machine_detection_speech_end_threshold, + "MachineDetectionSilenceTimeout": machine_detection_silence_timeout, + "AsyncAmd": async_amd, + "AsyncAmdStatusCallback": async_amd_status_callback, + "AsyncAmdStatusCallbackMethod": async_amd_status_callback_method, + "Byoc": byoc, + "CallReason": call_reason, + "CallToken": call_token, + "RecordingTrack": recording_track, + "TimeLimit": time_limit, + "Url": url, + "Twiml": twiml, + "ApplicationSid": application_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CallInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CallInstance]: + """ + Streams CallInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param str from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param str parent_call_sid: Only include calls spawned by calls with this SID. + :param "CallInstance.Status" status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param datetime start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + to=to, + from_=from_, + parent_call_sid=parent_call_sid, + status=status, + start_time=start_time, + start_time_before=start_time_before, + start_time_after=start_time_after, + end_time=end_time, + end_time_before=end_time_before, + end_time_after=end_time_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CallInstance]: + """ + Asynchronously streams CallInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param str from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param str parent_call_sid: Only include calls spawned by calls with this SID. + :param "CallInstance.Status" status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param datetime start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + to=to, + from_=from_, + parent_call_sid=parent_call_sid, + status=status, + start_time=start_time, + start_time_before=start_time_before, + start_time_after=start_time_after, + end_time=end_time, + end_time_before=end_time_before, + end_time_after=end_time_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CallInstance]: + """ + Lists CallInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param str from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param str parent_call_sid: Only include calls spawned by calls with this SID. + :param "CallInstance.Status" status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param datetime start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + to=to, + from_=from_, + parent_call_sid=parent_call_sid, + status=status, + start_time=start_time, + start_time_before=start_time_before, + start_time_after=start_time_after, + end_time=end_time, + end_time_before=end_time_before, + end_time_after=end_time_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CallInstance]: + """ + Asynchronously lists CallInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param str from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param str parent_call_sid: Only include calls spawned by calls with this SID. + :param "CallInstance.Status" status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param datetime start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param datetime end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param datetime end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + to=to, + from_=from_, + parent_call_sid=parent_call_sid, + status=status, + start_time=start_time, + start_time_before=start_time_before, + start_time_after=start_time_after, + end_time=end_time, + end_time_before=end_time_before, + end_time_after=end_time_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CallPage: + """ + Retrieve a single page of CallInstance records from the API. + Request is executed immediately + + :param to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param parent_call_sid: Only include calls spawned by calls with this SID. + :param status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CallInstance + """ + data = values.of( + { + "To": to, + "From": from_, + "ParentCallSid": parent_call_sid, + "Status": status, + "StartTime": serialize.iso8601_datetime(start_time), + "StartTime<": serialize.iso8601_datetime(start_time_before), + "StartTime>": serialize.iso8601_datetime(start_time_after), + "EndTime": serialize.iso8601_datetime(end_time), + "EndTime<": serialize.iso8601_datetime(end_time_before), + "EndTime>": serialize.iso8601_datetime(end_time_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CallPage(self._version, response, self._solution) + + async def page_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + parent_call_sid: Union[str, object] = values.unset, + status: Union["CallInstance.Status", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + start_time_before: Union[datetime, object] = values.unset, + start_time_after: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + end_time_before: Union[datetime, object] = values.unset, + end_time_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CallPage: + """ + Asynchronously retrieve a single page of CallInstance records from the API. + Request is executed immediately + + :param to: Only show calls made to this phone number, SIP address, Client identifier or SIM SID. + :param from_: Only include calls from this phone number, SIP address, Client identifier or SIM SID. + :param parent_call_sid: Only include calls spawned by calls with this SID. + :param status: The status of the calls to include. Can be: `queued`, `ringing`, `in-progress`, `canceled`, `completed`, `failed`, `busy`, or `no-answer`. + :param start_time: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param start_time_before: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param start_time_after: Only include calls that started on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that started on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read calls that started on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read calls that started on or after midnight of this date. + :param end_time: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param end_time_before: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param end_time_after: Only include calls that ended on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only calls that ended on this date. You can also specify an inequality, such as `EndTime<=YYYY-MM-DD`, to read calls that ended on or before midnight of this date, and `EndTime>=YYYY-MM-DD` to read calls that ended on or after midnight of this date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CallInstance + """ + data = values.of( + { + "To": to, + "From": from_, + "ParentCallSid": parent_call_sid, + "Status": status, + "StartTime": serialize.iso8601_datetime(start_time), + "StartTime<": serialize.iso8601_datetime(start_time_before), + "StartTime>": serialize.iso8601_datetime(start_time_after), + "EndTime": serialize.iso8601_datetime(end_time), + "EndTime<": serialize.iso8601_datetime(end_time_before), + "EndTime>": serialize.iso8601_datetime(end_time_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CallPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CallPage: + """ + Retrieve a specific page of CallInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CallInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CallPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CallPage: + """ + Asynchronously retrieve a specific page of CallInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CallInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CallPage(self._version, response, self._solution) + + def get(self, sid: str) -> CallContext: + """ + Constructs a CallContext + + :param sid: The Twilio-provided string that uniquely identifies the Call resource to update + """ + return CallContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> CallContext: + """ + Constructs a CallContext + + :param sid: The Twilio-provided string that uniquely identifies the Call resource to update + """ + return CallContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b280b07 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/event.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/event.cpython-310.pyc new file mode 100644 index 0000000..a36c356 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/event.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/notification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/notification.cpython-310.pyc new file mode 100644 index 0000000..2ae61bd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/notification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/payment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/payment.cpython-310.pyc new file mode 100644 index 0000000..c732568 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/payment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/recording.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/recording.cpython-310.pyc new file mode 100644 index 0000000..5ea08aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/recording.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/siprec.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/siprec.cpython-310.pyc new file mode 100644 index 0000000..f697e5e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/siprec.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/stream.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/stream.cpython-310.pyc new file mode 100644 index 0000000..3939161 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/stream.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message.cpython-310.pyc new file mode 100644 index 0000000..fda84e5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message_subscription.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message_subscription.cpython-310.pyc new file mode 100644 index 0000000..fea2b7f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/__pycache__/user_defined_message_subscription.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/event.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/event.py new file mode 100644 index 0000000..7993dd0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/event.py @@ -0,0 +1,288 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EventInstance(InstanceResource): + """ + :ivar request: Contains a dictionary representing the request of the call. + :ivar response: Contains a dictionary representing the call response, including a list of the call events. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], account_sid: str, call_sid: str + ): + super().__init__(version) + + self.request: Optional[Dict[str, object]] = payload.get("request") + self.response: Optional[Dict[str, object]] = payload.get("response") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EventInstance: + """ + Build an instance of EventInstance + + :param payload: Payload response from the API + """ + return EventInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EventList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the EventList + + :param version: Version that contains the resource + :param account_sid: The unique SID identifier of the Account. + :param call_sid: The unique SID identifier of the Call. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Events.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EventInstance]: + """ + Streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EventInstance]: + """ + Asynchronously streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Asynchronously lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EventPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Asynchronously retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EventPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EventPage: + """ + Retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EventPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EventPage: + """ + Asynchronously retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EventPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/notification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/notification.py new file mode 100644 index 0000000..44291b8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/notification.py @@ -0,0 +1,548 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class NotificationInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Call Notification resource. + :ivar api_version: The API version used to create the Call Notification resource. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Call Notification resource is associated with. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar error_code: A unique error code for the error condition that is described in our [Error Dictionary](https://www.twilio.com/docs/api/errors). + :ivar log: An integer log level that corresponds to the type of notification: `0` is ERROR, `1` is WARNING. + :ivar message_date: The date the notification was actually generated in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. Message buffering can cause this value to differ from `date_created`. + :ivar message_text: The text of the notification. + :ivar more_info: The URL for more information about the error condition. This value is a page in our [Error Dictionary](https://www.twilio.com/docs/api/errors). + :ivar request_method: The HTTP method used to generate the notification. If the notification was generated during a phone call, this is the HTTP Method used to request the resource on your server. If the notification was generated by your use of our REST API, this is the HTTP method used to call the resource on our servers. + :ivar request_url: The URL of the resource that generated the notification. If the notification was generated during a phone call, this is the URL of the resource on your server that caused the notification. If the notification was generated by your use of our REST API, this is the URL of the resource you called. + :ivar request_variables: The HTTP GET or POST variables we sent to your server. However, if the notification was generated by our REST API, this contains the HTTP POST or PUT variables you sent to our API. + :ivar response_body: The HTTP body returned by your server. + :ivar response_headers: The HTTP headers returned by your server. + :ivar sid: The unique string that that we created to identify the Call Notification resource. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.call_sid: Optional[str] = payload.get("call_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.error_code: Optional[str] = payload.get("error_code") + self.log: Optional[str] = payload.get("log") + self.message_date: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("message_date") + ) + self.message_text: Optional[str] = payload.get("message_text") + self.more_info: Optional[str] = payload.get("more_info") + self.request_method: Optional[str] = payload.get("request_method") + self.request_url: Optional[str] = payload.get("request_url") + self.request_variables: Optional[str] = payload.get("request_variables") + self.response_body: Optional[str] = payload.get("response_body") + self.response_headers: Optional[str] = payload.get("response_headers") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[NotificationContext] = None + + @property + def _proxy(self) -> "NotificationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NotificationContext for this NotificationInstance + """ + if self._context is None: + self._context = NotificationContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "NotificationInstance": + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NotificationInstance": + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the NotificationContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Call Notification resource to fetch. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the Call Notification resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Call Notification resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/Notifications/{sid}.json".format( + **self._solution + ) + ) + + def fetch(self) -> NotificationInstance: + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> NotificationInstance: + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> NotificationInstance: + """ + Build an instance of NotificationInstance + + :param payload: Payload response from the API + """ + return NotificationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NotificationList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the NotificationList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Call Notification resources to read. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the Call Notification resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/Notifications.json".format( + **self._solution + ) + ) + + def stream( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NotificationInstance]: + """ + Streams NotificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NotificationInstance]: + """ + Asynchronously streams NotificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NotificationInstance]: + """ + Lists NotificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NotificationInstance]: + """ + Asynchronously lists NotificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NotificationPage: + """ + Retrieve a single page of NotificationInstance records from the API. + Request is executed immediately + + :param log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NotificationInstance + """ + data = values.of( + { + "Log": log, + "MessageDate": serialize.iso8601_date(message_date), + "MessageDate<": serialize.iso8601_date(message_date_before), + "MessageDate>": serialize.iso8601_date(message_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NotificationPage(self._version, response, self._solution) + + async def page_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NotificationPage: + """ + Asynchronously retrieve a single page of NotificationInstance records from the API. + Request is executed immediately + + :param log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NotificationInstance + """ + data = values.of( + { + "Log": log, + "MessageDate": serialize.iso8601_date(message_date), + "MessageDate<": serialize.iso8601_date(message_date_before), + "MessageDate>": serialize.iso8601_date(message_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NotificationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> NotificationPage: + """ + Retrieve a specific page of NotificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NotificationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NotificationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> NotificationPage: + """ + Asynchronously retrieve a specific page of NotificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NotificationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NotificationPage(self._version, response, self._solution) + + def get(self, sid: str) -> NotificationContext: + """ + Constructs a NotificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Call Notification resource to fetch. + """ + return NotificationContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> NotificationContext: + """ + Constructs a NotificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Call Notification resource to fetch. + """ + return NotificationContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/payment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/payment.py new file mode 100644 index 0000000..0861958 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/payment.py @@ -0,0 +1,489 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PaymentInstance(InstanceResource): + + class BankAccountType(object): + CONSUMER_CHECKING = "consumer-checking" + CONSUMER_SAVINGS = "consumer-savings" + COMMERCIAL_CHECKING = "commercial-checking" + + class Capture(object): + PAYMENT_CARD_NUMBER = "payment-card-number" + EXPIRATION_DATE = "expiration-date" + SECURITY_CODE = "security-code" + POSTAL_CODE = "postal-code" + BANK_ROUTING_NUMBER = "bank-routing-number" + BANK_ACCOUNT_NUMBER = "bank-account-number" + + class PaymentMethod(object): + CREDIT_CARD = "credit-card" + ACH_DEBIT = "ach-debit" + + class Status(object): + COMPLETE = "complete" + CANCEL = "cancel" + + class TokenType(object): + ONE_TIME = "one-time" + REUSABLE = "reusable" + PAYMENT_METHOD = "payment-method" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Payments resource. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Payments resource is associated with. This will refer to the call sid that is producing the payment card (credit/ACH) information thru DTMF. + :ivar sid: The SID of the Payments resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[PaymentContext] = None + + @property + def _proxy(self) -> "PaymentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PaymentContext for this PaymentInstance + """ + if self._context is None: + self._context = PaymentContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update( + self, + idempotency_key: str, + status_callback: str, + capture: Union["PaymentInstance.Capture", object] = values.unset, + status: Union["PaymentInstance.Status", object] = values.unset, + ) -> "PaymentInstance": + """ + Update the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [Update](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-update) and [Complete/Cancel](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-cancelcomplete) POST requests. + :param capture: + :param status: + + :returns: The updated PaymentInstance + """ + return self._proxy.update( + idempotency_key=idempotency_key, + status_callback=status_callback, + capture=capture, + status=status, + ) + + async def update_async( + self, + idempotency_key: str, + status_callback: str, + capture: Union["PaymentInstance.Capture", object] = values.unset, + status: Union["PaymentInstance.Status", object] = values.unset, + ) -> "PaymentInstance": + """ + Asynchronous coroutine to update the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [Update](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-update) and [Complete/Cancel](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-cancelcomplete) POST requests. + :param capture: + :param status: + + :returns: The updated PaymentInstance + """ + return await self._proxy.update_async( + idempotency_key=idempotency_key, + status_callback=status_callback, + capture=capture, + status=status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PaymentContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the PaymentContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that will update the resource. + :param call_sid: The SID of the call that will update the resource. This should be the same call sid that was used to create payments resource. + :param sid: The SID of Payments session that needs to be updated. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/Payments/{sid}.json".format( + **self._solution + ) + ) + + def update( + self, + idempotency_key: str, + status_callback: str, + capture: Union["PaymentInstance.Capture", object] = values.unset, + status: Union["PaymentInstance.Status", object] = values.unset, + ) -> PaymentInstance: + """ + Update the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [Update](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-update) and [Complete/Cancel](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-cancelcomplete) POST requests. + :param capture: + :param status: + + :returns: The updated PaymentInstance + """ + data = values.of( + { + "IdempotencyKey": idempotency_key, + "StatusCallback": status_callback, + "Capture": capture, + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return PaymentInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + idempotency_key: str, + status_callback: str, + capture: Union["PaymentInstance.Capture", object] = values.unset, + status: Union["PaymentInstance.Status", object] = values.unset, + ) -> PaymentInstance: + """ + Asynchronous coroutine to update the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [Update](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-update) and [Complete/Cancel](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback-cancelcomplete) POST requests. + :param capture: + :param status: + + :returns: The updated PaymentInstance + """ + data = values.of( + { + "IdempotencyKey": idempotency_key, + "StatusCallback": status_callback, + "Capture": capture, + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PaymentInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PaymentList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the PaymentList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that will create the resource. + :param call_sid: The SID of the call that will create the resource. Call leg associated with this sid is expected to provide payment information thru DTMF. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Payments.json".format( + **self._solution + ) + + def create( + self, + idempotency_key: str, + status_callback: str, + bank_account_type: Union[ + "PaymentInstance.BankAccountType", object + ] = values.unset, + charge_amount: Union[float, object] = values.unset, + currency: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + input: Union[str, object] = values.unset, + min_postal_code_length: Union[int, object] = values.unset, + parameter: Union[object, object] = values.unset, + payment_connector: Union[str, object] = values.unset, + payment_method: Union["PaymentInstance.PaymentMethod", object] = values.unset, + postal_code: Union[bool, object] = values.unset, + security_code: Union[bool, object] = values.unset, + timeout: Union[int, object] = values.unset, + token_type: Union["PaymentInstance.TokenType", object] = values.unset, + valid_card_types: Union[str, object] = values.unset, + ) -> PaymentInstance: + """ + Create the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [expected StatusCallback values](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback) + :param bank_account_type: + :param charge_amount: A positive decimal value less than 1,000,000 to charge against the credit card or bank account. Default currency can be overwritten with `currency` field. Leave blank or set to 0 to tokenize. + :param currency: The currency of the `charge_amount`, formatted as [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format. The default value is `USD` and all values allowed from the Pay Connector are accepted. + :param description: The description can be used to provide more details regarding the transaction. This information is submitted along with the payment details to the Payment Connector which are then posted on the transactions. + :param input: A list of inputs that should be accepted. Currently only `dtmf` is supported. All digits captured during a pay session are redacted from the logs. + :param min_postal_code_length: A positive integer that is used to validate the length of the `PostalCode` inputted by the user. User must enter this many digits. + :param parameter: A single-level JSON object used to pass custom parameters to payment processors. (Required for ACH payments). The information that has to be included here depends on the Connector. [Read more](https://www.twilio.com/console/voice/pay-connectors). + :param payment_connector: This is the unique name corresponding to the Pay Connector installed in the Twilio Add-ons. Learn more about [ Connectors](https://www.twilio.com/console/voice/pay-connectors). The default value is `Default`. + :param payment_method: + :param postal_code: Indicates whether the credit card postal code (zip code) is a required piece of payment information that must be provided by the caller. The default is `true`. + :param security_code: Indicates whether the credit card security code is a required piece of payment information that must be provided by the caller. The default is `true`. + :param timeout: The number of seconds that should wait for the caller to press a digit between each subsequent digit, after the first one, before moving on to validate the digits captured. The default is `5`, maximum is `600`. + :param token_type: + :param valid_card_types: Credit card types separated by space that Pay should accept. The default value is `visa mastercard amex` + + :returns: The created PaymentInstance + """ + + data = values.of( + { + "IdempotencyKey": idempotency_key, + "StatusCallback": status_callback, + "BankAccountType": bank_account_type, + "ChargeAmount": charge_amount, + "Currency": currency, + "Description": description, + "Input": input, + "MinPostalCodeLength": min_postal_code_length, + "Parameter": serialize.object(parameter), + "PaymentConnector": payment_connector, + "PaymentMethod": payment_method, + "PostalCode": serialize.boolean_to_string(postal_code), + "SecurityCode": serialize.boolean_to_string(security_code), + "Timeout": timeout, + "TokenType": token_type, + "ValidCardTypes": valid_card_types, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PaymentInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, + idempotency_key: str, + status_callback: str, + bank_account_type: Union[ + "PaymentInstance.BankAccountType", object + ] = values.unset, + charge_amount: Union[float, object] = values.unset, + currency: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + input: Union[str, object] = values.unset, + min_postal_code_length: Union[int, object] = values.unset, + parameter: Union[object, object] = values.unset, + payment_connector: Union[str, object] = values.unset, + payment_method: Union["PaymentInstance.PaymentMethod", object] = values.unset, + postal_code: Union[bool, object] = values.unset, + security_code: Union[bool, object] = values.unset, + timeout: Union[int, object] = values.unset, + token_type: Union["PaymentInstance.TokenType", object] = values.unset, + valid_card_types: Union[str, object] = values.unset, + ) -> PaymentInstance: + """ + Asynchronously create the PaymentInstance + + :param idempotency_key: A unique token that will be used to ensure that multiple API calls with the same information do not result in multiple transactions. This should be a unique string value per API call and can be a randomly generated. + :param status_callback: Provide an absolute or relative URL to receive status updates regarding your Pay session. Read more about the [expected StatusCallback values](https://www.twilio.com/docs/voice/api/payment-resource#statuscallback) + :param bank_account_type: + :param charge_amount: A positive decimal value less than 1,000,000 to charge against the credit card or bank account. Default currency can be overwritten with `currency` field. Leave blank or set to 0 to tokenize. + :param currency: The currency of the `charge_amount`, formatted as [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format. The default value is `USD` and all values allowed from the Pay Connector are accepted. + :param description: The description can be used to provide more details regarding the transaction. This information is submitted along with the payment details to the Payment Connector which are then posted on the transactions. + :param input: A list of inputs that should be accepted. Currently only `dtmf` is supported. All digits captured during a pay session are redacted from the logs. + :param min_postal_code_length: A positive integer that is used to validate the length of the `PostalCode` inputted by the user. User must enter this many digits. + :param parameter: A single-level JSON object used to pass custom parameters to payment processors. (Required for ACH payments). The information that has to be included here depends on the Connector. [Read more](https://www.twilio.com/console/voice/pay-connectors). + :param payment_connector: This is the unique name corresponding to the Pay Connector installed in the Twilio Add-ons. Learn more about [ Connectors](https://www.twilio.com/console/voice/pay-connectors). The default value is `Default`. + :param payment_method: + :param postal_code: Indicates whether the credit card postal code (zip code) is a required piece of payment information that must be provided by the caller. The default is `true`. + :param security_code: Indicates whether the credit card security code is a required piece of payment information that must be provided by the caller. The default is `true`. + :param timeout: The number of seconds that should wait for the caller to press a digit between each subsequent digit, after the first one, before moving on to validate the digits captured. The default is `5`, maximum is `600`. + :param token_type: + :param valid_card_types: Credit card types separated by space that Pay should accept. The default value is `visa mastercard amex` + + :returns: The created PaymentInstance + """ + + data = values.of( + { + "IdempotencyKey": idempotency_key, + "StatusCallback": status_callback, + "BankAccountType": bank_account_type, + "ChargeAmount": charge_amount, + "Currency": currency, + "Description": description, + "Input": input, + "MinPostalCodeLength": min_postal_code_length, + "Parameter": serialize.object(parameter), + "PaymentConnector": payment_connector, + "PaymentMethod": payment_method, + "PostalCode": serialize.boolean_to_string(postal_code), + "SecurityCode": serialize.boolean_to_string(security_code), + "Timeout": timeout, + "TokenType": token_type, + "ValidCardTypes": valid_card_types, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PaymentInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def get(self, sid: str) -> PaymentContext: + """ + Constructs a PaymentContext + + :param sid: The SID of Payments session that needs to be updated. + """ + return PaymentContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> PaymentContext: + """ + Constructs a PaymentContext + + :param sid: The SID of Payments session that needs to be updated. + """ + return PaymentContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/recording.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/recording.py new file mode 100644 index 0000000..b9653c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/recording.py @@ -0,0 +1,792 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RecordingInstance(InstanceResource): + + class Source(object): + DIALVERB = "DialVerb" + CONFERENCE = "Conference" + OUTBOUNDAPI = "OutboundAPI" + TRUNKING = "Trunking" + RECORDVERB = "RecordVerb" + STARTCALLRECORDINGAPI = "StartCallRecordingAPI" + STARTCONFERENCERECORDINGAPI = "StartConferenceRecordingAPI" + + class Status(object): + IN_PROGRESS = "in-progress" + PAUSED = "paused" + STOPPED = "stopped" + PROCESSING = "processing" + COMPLETED = "completed" + ABSENT = "absent" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resource. + :ivar api_version: The API version used to make the recording. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Recording resource is associated with. + :ivar conference_sid: The Conference SID that identifies the conference associated with the recording, if a conference recording. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar start_time: The start time of the recording in GMT and in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar duration: The length of the recording in seconds. + :ivar sid: The unique string that that we created to identify the Recording resource. + :ivar price: The one-time cost of creating the recording in the `price_unit` currency. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar encryption_details: How to decrypt the recording if it was encrypted using [Call Recording Encryption](https://www.twilio.com/docs/voice/tutorials/voice-recording-encryption) feature. + :ivar price_unit: The currency used in the `price` property. Example: `USD`. + :ivar status: + :ivar channels: The number of channels in the final recording file. Can be: `1`, or `2`. Separating a two leg call into two separate channels of the recording file is supported in [Dial](https://www.twilio.com/docs/voice/twiml/dial#attributes-record) and [Outbound Rest API](https://www.twilio.com/docs/voice/make-calls) record options. + :ivar source: + :ivar error_code: The error code that describes why the recording is `absent`. The error code is described in our [Error Dictionary](https://www.twilio.com/docs/api/errors). This value is null if the recording `status` is not `absent`. + :ivar track: The recorded track. Can be: `inbound`, `outbound`, or `both`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.call_sid: Optional[str] = payload.get("call_sid") + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.start_time: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("start_time") + ) + self.duration: Optional[str] = payload.get("duration") + self.sid: Optional[str] = payload.get("sid") + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.uri: Optional[str] = payload.get("uri") + self.encryption_details: Optional[Dict[str, object]] = payload.get( + "encryption_details" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.status: Optional["RecordingInstance.Status"] = payload.get("status") + self.channels: Optional[int] = deserialize.integer(payload.get("channels")) + self.source: Optional["RecordingInstance.Source"] = payload.get("source") + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.track: Optional[str] = payload.get("track") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[RecordingContext] = None + + @property + def _proxy(self) -> "RecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingContext for this RecordingInstance + """ + if self._context is None: + self._context = RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RecordingInstance": + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RecordingInstance": + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> "RecordingInstance": + """ + Update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + return self._proxy.update( + status=status, + pause_behavior=pause_behavior, + ) + + async def update_async( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> "RecordingInstance": + """ + Asynchronous coroutine to update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + return await self._proxy.update_async( + status=status, + pause_behavior=pause_behavior, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the RecordingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resource to update. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resource to update. + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/Recordings/{sid}.json".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RecordingInstance: + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RecordingInstance: + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Status": status, + "PauseBehavior": pause_behavior, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Asynchronous coroutine to update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Status": status, + "PauseBehavior": pause_behavior, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RecordingInstance: + """ + Build an instance of RecordingInstance + + :param payload: Payload response from the API + """ + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RecordingList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the RecordingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resources to read. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Recordings.json".format( + **self._solution + ) + + def create( + self, + recording_status_callback_event: Union[List[str], object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Create the RecordingInstance + + :param recording_status_callback_event: The recording status events on which we should call the `recording_status_callback` URL. Can be: `in-progress`, `completed` and `absent` and the default is `completed`. Separate multiple event values with a space. + :param recording_status_callback: The URL we should call using the `recording_status_callback_method` on each recording event specified in `recording_status_callback_event`. For more information, see [RecordingStatusCallback parameters](https://www.twilio.com/docs/voice/api/recording#recordingstatuscallback). + :param recording_status_callback_method: The HTTP method we should use to call `recording_status_callback`. Can be: `GET` or `POST` and the default is `POST`. + :param trim: Whether to trim any leading and trailing silence in the recording. Can be: `trim-silence` or `do-not-trim` and the default is `do-not-trim`. `trim-silence` trims the silence from the beginning and end of the recording and `do-not-trim` does not. + :param recording_channels: The number of channels used in the recording. Can be: `mono` or `dual` and the default is `mono`. `mono` records all parties of the call into one channel. `dual` records each party of a 2-party call into separate channels. + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is generated from Twilio. `both` records the audio that is received and generated by Twilio. + + :returns: The created RecordingInstance + """ + + data = values.of( + { + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "Trim": trim, + "RecordingChannels": recording_channels, + "RecordingTrack": recording_track, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, + recording_status_callback_event: Union[List[str], object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Asynchronously create the RecordingInstance + + :param recording_status_callback_event: The recording status events on which we should call the `recording_status_callback` URL. Can be: `in-progress`, `completed` and `absent` and the default is `completed`. Separate multiple event values with a space. + :param recording_status_callback: The URL we should call using the `recording_status_callback_method` on each recording event specified in `recording_status_callback_event`. For more information, see [RecordingStatusCallback parameters](https://www.twilio.com/docs/voice/api/recording#recordingstatuscallback). + :param recording_status_callback_method: The HTTP method we should use to call `recording_status_callback`. Can be: `GET` or `POST` and the default is `POST`. + :param trim: Whether to trim any leading and trailing silence in the recording. Can be: `trim-silence` or `do-not-trim` and the default is `do-not-trim`. `trim-silence` trims the silence from the beginning and end of the recording and `do-not-trim` does not. + :param recording_channels: The number of channels used in the recording. Can be: `mono` or `dual` and the default is `mono`. `mono` records all parties of the call into one channel. `dual` records each party of a 2-party call into separate channels. + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is generated from Twilio. `both` records the audio that is received and generated by Twilio. + + :returns: The created RecordingInstance + """ + + data = values.of( + { + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "Trim": trim, + "RecordingChannels": recording_channels, + "RecordingTrack": recording_track, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def stream( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RecordingInstance]: + """ + Streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RecordingInstance]: + """ + Asynchronously streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Asynchronously lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RecordingPage(self._version, response, self._solution) + + async def page_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Asynchronously retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RecordingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RecordingPage: + """ + Retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RecordingPage: + """ + Asynchronously retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + def get(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to update. + """ + return RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to update. + """ + return RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/siprec.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/siprec.py new file mode 100644 index 0000000..bcb5370 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/siprec.py @@ -0,0 +1,1547 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SiprecInstance(InstanceResource): + + class Status(object): + IN_PROGRESS = "in-progress" + STOPPED = "stopped" + + class Track(object): + INBOUND_TRACK = "inbound_track" + OUTBOUND_TRACK = "outbound_track" + BOTH_TRACKS = "both_tracks" + + class UpdateStatus(object): + STOPPED = "stopped" + + """ + :ivar sid: The SID of the Siprec resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Siprec resource. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Siprec resource is associated with. + :ivar name: The user-specified name of this Siprec, if one was given when the Siprec was created. This may be used to stop the Siprec. + :ivar status: + :ivar date_updated: The date and time in GMT that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.name: Optional[str] = payload.get("name") + self.status: Optional["SiprecInstance.Status"] = payload.get("status") + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[SiprecContext] = None + + @property + def _proxy(self) -> "SiprecContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SiprecContext for this SiprecInstance + """ + if self._context is None: + self._context = SiprecContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update(self, status: "SiprecInstance.UpdateStatus") -> "SiprecInstance": + """ + Update the SiprecInstance + + :param status: + + :returns: The updated SiprecInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "SiprecInstance.UpdateStatus" + ) -> "SiprecInstance": + """ + Asynchronous coroutine to update the SiprecInstance + + :param status: + + :returns: The updated SiprecInstance + """ + return await self._proxy.update_async( + status=status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SiprecContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the SiprecContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Siprec resource. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Siprec resource is associated with. + :param sid: The SID of the Siprec resource, or the `name` used when creating the resource + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Siprec/{sid}.json".format( + **self._solution + ) + + def update(self, status: "SiprecInstance.UpdateStatus") -> SiprecInstance: + """ + Update the SiprecInstance + + :param status: + + :returns: The updated SiprecInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SiprecInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "SiprecInstance.UpdateStatus" + ) -> SiprecInstance: + """ + Asynchronous coroutine to update the SiprecInstance + + :param status: + + :returns: The updated SiprecInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SiprecInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SiprecList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the SiprecList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Siprec resource. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Siprec resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Siprec.json".format( + **self._solution + ) + + def create( + self, + name: Union[str, object] = values.unset, + connector_name: Union[str, object] = values.unset, + track: Union["SiprecInstance.Track", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + parameter1_name: Union[str, object] = values.unset, + parameter1_value: Union[str, object] = values.unset, + parameter2_name: Union[str, object] = values.unset, + parameter2_value: Union[str, object] = values.unset, + parameter3_name: Union[str, object] = values.unset, + parameter3_value: Union[str, object] = values.unset, + parameter4_name: Union[str, object] = values.unset, + parameter4_value: Union[str, object] = values.unset, + parameter5_name: Union[str, object] = values.unset, + parameter5_value: Union[str, object] = values.unset, + parameter6_name: Union[str, object] = values.unset, + parameter6_value: Union[str, object] = values.unset, + parameter7_name: Union[str, object] = values.unset, + parameter7_value: Union[str, object] = values.unset, + parameter8_name: Union[str, object] = values.unset, + parameter8_value: Union[str, object] = values.unset, + parameter9_name: Union[str, object] = values.unset, + parameter9_value: Union[str, object] = values.unset, + parameter10_name: Union[str, object] = values.unset, + parameter10_value: Union[str, object] = values.unset, + parameter11_name: Union[str, object] = values.unset, + parameter11_value: Union[str, object] = values.unset, + parameter12_name: Union[str, object] = values.unset, + parameter12_value: Union[str, object] = values.unset, + parameter13_name: Union[str, object] = values.unset, + parameter13_value: Union[str, object] = values.unset, + parameter14_name: Union[str, object] = values.unset, + parameter14_value: Union[str, object] = values.unset, + parameter15_name: Union[str, object] = values.unset, + parameter15_value: Union[str, object] = values.unset, + parameter16_name: Union[str, object] = values.unset, + parameter16_value: Union[str, object] = values.unset, + parameter17_name: Union[str, object] = values.unset, + parameter17_value: Union[str, object] = values.unset, + parameter18_name: Union[str, object] = values.unset, + parameter18_value: Union[str, object] = values.unset, + parameter19_name: Union[str, object] = values.unset, + parameter19_value: Union[str, object] = values.unset, + parameter20_name: Union[str, object] = values.unset, + parameter20_value: Union[str, object] = values.unset, + parameter21_name: Union[str, object] = values.unset, + parameter21_value: Union[str, object] = values.unset, + parameter22_name: Union[str, object] = values.unset, + parameter22_value: Union[str, object] = values.unset, + parameter23_name: Union[str, object] = values.unset, + parameter23_value: Union[str, object] = values.unset, + parameter24_name: Union[str, object] = values.unset, + parameter24_value: Union[str, object] = values.unset, + parameter25_name: Union[str, object] = values.unset, + parameter25_value: Union[str, object] = values.unset, + parameter26_name: Union[str, object] = values.unset, + parameter26_value: Union[str, object] = values.unset, + parameter27_name: Union[str, object] = values.unset, + parameter27_value: Union[str, object] = values.unset, + parameter28_name: Union[str, object] = values.unset, + parameter28_value: Union[str, object] = values.unset, + parameter29_name: Union[str, object] = values.unset, + parameter29_value: Union[str, object] = values.unset, + parameter30_name: Union[str, object] = values.unset, + parameter30_value: Union[str, object] = values.unset, + parameter31_name: Union[str, object] = values.unset, + parameter31_value: Union[str, object] = values.unset, + parameter32_name: Union[str, object] = values.unset, + parameter32_value: Union[str, object] = values.unset, + parameter33_name: Union[str, object] = values.unset, + parameter33_value: Union[str, object] = values.unset, + parameter34_name: Union[str, object] = values.unset, + parameter34_value: Union[str, object] = values.unset, + parameter35_name: Union[str, object] = values.unset, + parameter35_value: Union[str, object] = values.unset, + parameter36_name: Union[str, object] = values.unset, + parameter36_value: Union[str, object] = values.unset, + parameter37_name: Union[str, object] = values.unset, + parameter37_value: Union[str, object] = values.unset, + parameter38_name: Union[str, object] = values.unset, + parameter38_value: Union[str, object] = values.unset, + parameter39_name: Union[str, object] = values.unset, + parameter39_value: Union[str, object] = values.unset, + parameter40_name: Union[str, object] = values.unset, + parameter40_value: Union[str, object] = values.unset, + parameter41_name: Union[str, object] = values.unset, + parameter41_value: Union[str, object] = values.unset, + parameter42_name: Union[str, object] = values.unset, + parameter42_value: Union[str, object] = values.unset, + parameter43_name: Union[str, object] = values.unset, + parameter43_value: Union[str, object] = values.unset, + parameter44_name: Union[str, object] = values.unset, + parameter44_value: Union[str, object] = values.unset, + parameter45_name: Union[str, object] = values.unset, + parameter45_value: Union[str, object] = values.unset, + parameter46_name: Union[str, object] = values.unset, + parameter46_value: Union[str, object] = values.unset, + parameter47_name: Union[str, object] = values.unset, + parameter47_value: Union[str, object] = values.unset, + parameter48_name: Union[str, object] = values.unset, + parameter48_value: Union[str, object] = values.unset, + parameter49_name: Union[str, object] = values.unset, + parameter49_value: Union[str, object] = values.unset, + parameter50_name: Union[str, object] = values.unset, + parameter50_value: Union[str, object] = values.unset, + parameter51_name: Union[str, object] = values.unset, + parameter51_value: Union[str, object] = values.unset, + parameter52_name: Union[str, object] = values.unset, + parameter52_value: Union[str, object] = values.unset, + parameter53_name: Union[str, object] = values.unset, + parameter53_value: Union[str, object] = values.unset, + parameter54_name: Union[str, object] = values.unset, + parameter54_value: Union[str, object] = values.unset, + parameter55_name: Union[str, object] = values.unset, + parameter55_value: Union[str, object] = values.unset, + parameter56_name: Union[str, object] = values.unset, + parameter56_value: Union[str, object] = values.unset, + parameter57_name: Union[str, object] = values.unset, + parameter57_value: Union[str, object] = values.unset, + parameter58_name: Union[str, object] = values.unset, + parameter58_value: Union[str, object] = values.unset, + parameter59_name: Union[str, object] = values.unset, + parameter59_value: Union[str, object] = values.unset, + parameter60_name: Union[str, object] = values.unset, + parameter60_value: Union[str, object] = values.unset, + parameter61_name: Union[str, object] = values.unset, + parameter61_value: Union[str, object] = values.unset, + parameter62_name: Union[str, object] = values.unset, + parameter62_value: Union[str, object] = values.unset, + parameter63_name: Union[str, object] = values.unset, + parameter63_value: Union[str, object] = values.unset, + parameter64_name: Union[str, object] = values.unset, + parameter64_value: Union[str, object] = values.unset, + parameter65_name: Union[str, object] = values.unset, + parameter65_value: Union[str, object] = values.unset, + parameter66_name: Union[str, object] = values.unset, + parameter66_value: Union[str, object] = values.unset, + parameter67_name: Union[str, object] = values.unset, + parameter67_value: Union[str, object] = values.unset, + parameter68_name: Union[str, object] = values.unset, + parameter68_value: Union[str, object] = values.unset, + parameter69_name: Union[str, object] = values.unset, + parameter69_value: Union[str, object] = values.unset, + parameter70_name: Union[str, object] = values.unset, + parameter70_value: Union[str, object] = values.unset, + parameter71_name: Union[str, object] = values.unset, + parameter71_value: Union[str, object] = values.unset, + parameter72_name: Union[str, object] = values.unset, + parameter72_value: Union[str, object] = values.unset, + parameter73_name: Union[str, object] = values.unset, + parameter73_value: Union[str, object] = values.unset, + parameter74_name: Union[str, object] = values.unset, + parameter74_value: Union[str, object] = values.unset, + parameter75_name: Union[str, object] = values.unset, + parameter75_value: Union[str, object] = values.unset, + parameter76_name: Union[str, object] = values.unset, + parameter76_value: Union[str, object] = values.unset, + parameter77_name: Union[str, object] = values.unset, + parameter77_value: Union[str, object] = values.unset, + parameter78_name: Union[str, object] = values.unset, + parameter78_value: Union[str, object] = values.unset, + parameter79_name: Union[str, object] = values.unset, + parameter79_value: Union[str, object] = values.unset, + parameter80_name: Union[str, object] = values.unset, + parameter80_value: Union[str, object] = values.unset, + parameter81_name: Union[str, object] = values.unset, + parameter81_value: Union[str, object] = values.unset, + parameter82_name: Union[str, object] = values.unset, + parameter82_value: Union[str, object] = values.unset, + parameter83_name: Union[str, object] = values.unset, + parameter83_value: Union[str, object] = values.unset, + parameter84_name: Union[str, object] = values.unset, + parameter84_value: Union[str, object] = values.unset, + parameter85_name: Union[str, object] = values.unset, + parameter85_value: Union[str, object] = values.unset, + parameter86_name: Union[str, object] = values.unset, + parameter86_value: Union[str, object] = values.unset, + parameter87_name: Union[str, object] = values.unset, + parameter87_value: Union[str, object] = values.unset, + parameter88_name: Union[str, object] = values.unset, + parameter88_value: Union[str, object] = values.unset, + parameter89_name: Union[str, object] = values.unset, + parameter89_value: Union[str, object] = values.unset, + parameter90_name: Union[str, object] = values.unset, + parameter90_value: Union[str, object] = values.unset, + parameter91_name: Union[str, object] = values.unset, + parameter91_value: Union[str, object] = values.unset, + parameter92_name: Union[str, object] = values.unset, + parameter92_value: Union[str, object] = values.unset, + parameter93_name: Union[str, object] = values.unset, + parameter93_value: Union[str, object] = values.unset, + parameter94_name: Union[str, object] = values.unset, + parameter94_value: Union[str, object] = values.unset, + parameter95_name: Union[str, object] = values.unset, + parameter95_value: Union[str, object] = values.unset, + parameter96_name: Union[str, object] = values.unset, + parameter96_value: Union[str, object] = values.unset, + parameter97_name: Union[str, object] = values.unset, + parameter97_value: Union[str, object] = values.unset, + parameter98_name: Union[str, object] = values.unset, + parameter98_value: Union[str, object] = values.unset, + parameter99_name: Union[str, object] = values.unset, + parameter99_value: Union[str, object] = values.unset, + ) -> SiprecInstance: + """ + Create the SiprecInstance + + :param name: The user-specified name of this Siprec, if one was given when the Siprec was created. This may be used to stop the Siprec. + :param connector_name: Unique name used when configuring the connector via Marketplace Add-on. + :param track: + :param status_callback: Absolute URL of the status callback. + :param status_callback_method: The http method for the status_callback (one of GET, POST). + :param parameter1_name: Parameter name + :param parameter1_value: Parameter value + :param parameter2_name: Parameter name + :param parameter2_value: Parameter value + :param parameter3_name: Parameter name + :param parameter3_value: Parameter value + :param parameter4_name: Parameter name + :param parameter4_value: Parameter value + :param parameter5_name: Parameter name + :param parameter5_value: Parameter value + :param parameter6_name: Parameter name + :param parameter6_value: Parameter value + :param parameter7_name: Parameter name + :param parameter7_value: Parameter value + :param parameter8_name: Parameter name + :param parameter8_value: Parameter value + :param parameter9_name: Parameter name + :param parameter9_value: Parameter value + :param parameter10_name: Parameter name + :param parameter10_value: Parameter value + :param parameter11_name: Parameter name + :param parameter11_value: Parameter value + :param parameter12_name: Parameter name + :param parameter12_value: Parameter value + :param parameter13_name: Parameter name + :param parameter13_value: Parameter value + :param parameter14_name: Parameter name + :param parameter14_value: Parameter value + :param parameter15_name: Parameter name + :param parameter15_value: Parameter value + :param parameter16_name: Parameter name + :param parameter16_value: Parameter value + :param parameter17_name: Parameter name + :param parameter17_value: Parameter value + :param parameter18_name: Parameter name + :param parameter18_value: Parameter value + :param parameter19_name: Parameter name + :param parameter19_value: Parameter value + :param parameter20_name: Parameter name + :param parameter20_value: Parameter value + :param parameter21_name: Parameter name + :param parameter21_value: Parameter value + :param parameter22_name: Parameter name + :param parameter22_value: Parameter value + :param parameter23_name: Parameter name + :param parameter23_value: Parameter value + :param parameter24_name: Parameter name + :param parameter24_value: Parameter value + :param parameter25_name: Parameter name + :param parameter25_value: Parameter value + :param parameter26_name: Parameter name + :param parameter26_value: Parameter value + :param parameter27_name: Parameter name + :param parameter27_value: Parameter value + :param parameter28_name: Parameter name + :param parameter28_value: Parameter value + :param parameter29_name: Parameter name + :param parameter29_value: Parameter value + :param parameter30_name: Parameter name + :param parameter30_value: Parameter value + :param parameter31_name: Parameter name + :param parameter31_value: Parameter value + :param parameter32_name: Parameter name + :param parameter32_value: Parameter value + :param parameter33_name: Parameter name + :param parameter33_value: Parameter value + :param parameter34_name: Parameter name + :param parameter34_value: Parameter value + :param parameter35_name: Parameter name + :param parameter35_value: Parameter value + :param parameter36_name: Parameter name + :param parameter36_value: Parameter value + :param parameter37_name: Parameter name + :param parameter37_value: Parameter value + :param parameter38_name: Parameter name + :param parameter38_value: Parameter value + :param parameter39_name: Parameter name + :param parameter39_value: Parameter value + :param parameter40_name: Parameter name + :param parameter40_value: Parameter value + :param parameter41_name: Parameter name + :param parameter41_value: Parameter value + :param parameter42_name: Parameter name + :param parameter42_value: Parameter value + :param parameter43_name: Parameter name + :param parameter43_value: Parameter value + :param parameter44_name: Parameter name + :param parameter44_value: Parameter value + :param parameter45_name: Parameter name + :param parameter45_value: Parameter value + :param parameter46_name: Parameter name + :param parameter46_value: Parameter value + :param parameter47_name: Parameter name + :param parameter47_value: Parameter value + :param parameter48_name: Parameter name + :param parameter48_value: Parameter value + :param parameter49_name: Parameter name + :param parameter49_value: Parameter value + :param parameter50_name: Parameter name + :param parameter50_value: Parameter value + :param parameter51_name: Parameter name + :param parameter51_value: Parameter value + :param parameter52_name: Parameter name + :param parameter52_value: Parameter value + :param parameter53_name: Parameter name + :param parameter53_value: Parameter value + :param parameter54_name: Parameter name + :param parameter54_value: Parameter value + :param parameter55_name: Parameter name + :param parameter55_value: Parameter value + :param parameter56_name: Parameter name + :param parameter56_value: Parameter value + :param parameter57_name: Parameter name + :param parameter57_value: Parameter value + :param parameter58_name: Parameter name + :param parameter58_value: Parameter value + :param parameter59_name: Parameter name + :param parameter59_value: Parameter value + :param parameter60_name: Parameter name + :param parameter60_value: Parameter value + :param parameter61_name: Parameter name + :param parameter61_value: Parameter value + :param parameter62_name: Parameter name + :param parameter62_value: Parameter value + :param parameter63_name: Parameter name + :param parameter63_value: Parameter value + :param parameter64_name: Parameter name + :param parameter64_value: Parameter value + :param parameter65_name: Parameter name + :param parameter65_value: Parameter value + :param parameter66_name: Parameter name + :param parameter66_value: Parameter value + :param parameter67_name: Parameter name + :param parameter67_value: Parameter value + :param parameter68_name: Parameter name + :param parameter68_value: Parameter value + :param parameter69_name: Parameter name + :param parameter69_value: Parameter value + :param parameter70_name: Parameter name + :param parameter70_value: Parameter value + :param parameter71_name: Parameter name + :param parameter71_value: Parameter value + :param parameter72_name: Parameter name + :param parameter72_value: Parameter value + :param parameter73_name: Parameter name + :param parameter73_value: Parameter value + :param parameter74_name: Parameter name + :param parameter74_value: Parameter value + :param parameter75_name: Parameter name + :param parameter75_value: Parameter value + :param parameter76_name: Parameter name + :param parameter76_value: Parameter value + :param parameter77_name: Parameter name + :param parameter77_value: Parameter value + :param parameter78_name: Parameter name + :param parameter78_value: Parameter value + :param parameter79_name: Parameter name + :param parameter79_value: Parameter value + :param parameter80_name: Parameter name + :param parameter80_value: Parameter value + :param parameter81_name: Parameter name + :param parameter81_value: Parameter value + :param parameter82_name: Parameter name + :param parameter82_value: Parameter value + :param parameter83_name: Parameter name + :param parameter83_value: Parameter value + :param parameter84_name: Parameter name + :param parameter84_value: Parameter value + :param parameter85_name: Parameter name + :param parameter85_value: Parameter value + :param parameter86_name: Parameter name + :param parameter86_value: Parameter value + :param parameter87_name: Parameter name + :param parameter87_value: Parameter value + :param parameter88_name: Parameter name + :param parameter88_value: Parameter value + :param parameter89_name: Parameter name + :param parameter89_value: Parameter value + :param parameter90_name: Parameter name + :param parameter90_value: Parameter value + :param parameter91_name: Parameter name + :param parameter91_value: Parameter value + :param parameter92_name: Parameter name + :param parameter92_value: Parameter value + :param parameter93_name: Parameter name + :param parameter93_value: Parameter value + :param parameter94_name: Parameter name + :param parameter94_value: Parameter value + :param parameter95_name: Parameter name + :param parameter95_value: Parameter value + :param parameter96_name: Parameter name + :param parameter96_value: Parameter value + :param parameter97_name: Parameter name + :param parameter97_value: Parameter value + :param parameter98_name: Parameter name + :param parameter98_value: Parameter value + :param parameter99_name: Parameter name + :param parameter99_value: Parameter value + + :returns: The created SiprecInstance + """ + + data = values.of( + { + "Name": name, + "ConnectorName": connector_name, + "Track": track, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Parameter1.Name": parameter1_name, + "Parameter1.Value": parameter1_value, + "Parameter2.Name": parameter2_name, + "Parameter2.Value": parameter2_value, + "Parameter3.Name": parameter3_name, + "Parameter3.Value": parameter3_value, + "Parameter4.Name": parameter4_name, + "Parameter4.Value": parameter4_value, + "Parameter5.Name": parameter5_name, + "Parameter5.Value": parameter5_value, + "Parameter6.Name": parameter6_name, + "Parameter6.Value": parameter6_value, + "Parameter7.Name": parameter7_name, + "Parameter7.Value": parameter7_value, + "Parameter8.Name": parameter8_name, + "Parameter8.Value": parameter8_value, + "Parameter9.Name": parameter9_name, + "Parameter9.Value": parameter9_value, + "Parameter10.Name": parameter10_name, + "Parameter10.Value": parameter10_value, + "Parameter11.Name": parameter11_name, + "Parameter11.Value": parameter11_value, + "Parameter12.Name": parameter12_name, + "Parameter12.Value": parameter12_value, + "Parameter13.Name": parameter13_name, + "Parameter13.Value": parameter13_value, + "Parameter14.Name": parameter14_name, + "Parameter14.Value": parameter14_value, + "Parameter15.Name": parameter15_name, + "Parameter15.Value": parameter15_value, + "Parameter16.Name": parameter16_name, + "Parameter16.Value": parameter16_value, + "Parameter17.Name": parameter17_name, + "Parameter17.Value": parameter17_value, + "Parameter18.Name": parameter18_name, + "Parameter18.Value": parameter18_value, + "Parameter19.Name": parameter19_name, + "Parameter19.Value": parameter19_value, + "Parameter20.Name": parameter20_name, + "Parameter20.Value": parameter20_value, + "Parameter21.Name": parameter21_name, + "Parameter21.Value": parameter21_value, + "Parameter22.Name": parameter22_name, + "Parameter22.Value": parameter22_value, + "Parameter23.Name": parameter23_name, + "Parameter23.Value": parameter23_value, + "Parameter24.Name": parameter24_name, + "Parameter24.Value": parameter24_value, + "Parameter25.Name": parameter25_name, + "Parameter25.Value": parameter25_value, + "Parameter26.Name": parameter26_name, + "Parameter26.Value": parameter26_value, + "Parameter27.Name": parameter27_name, + "Parameter27.Value": parameter27_value, + "Parameter28.Name": parameter28_name, + "Parameter28.Value": parameter28_value, + "Parameter29.Name": parameter29_name, + "Parameter29.Value": parameter29_value, + "Parameter30.Name": parameter30_name, + "Parameter30.Value": parameter30_value, + "Parameter31.Name": parameter31_name, + "Parameter31.Value": parameter31_value, + "Parameter32.Name": parameter32_name, + "Parameter32.Value": parameter32_value, + "Parameter33.Name": parameter33_name, + "Parameter33.Value": parameter33_value, + "Parameter34.Name": parameter34_name, + "Parameter34.Value": parameter34_value, + "Parameter35.Name": parameter35_name, + "Parameter35.Value": parameter35_value, + "Parameter36.Name": parameter36_name, + "Parameter36.Value": parameter36_value, + "Parameter37.Name": parameter37_name, + "Parameter37.Value": parameter37_value, + "Parameter38.Name": parameter38_name, + "Parameter38.Value": parameter38_value, + "Parameter39.Name": parameter39_name, + "Parameter39.Value": parameter39_value, + "Parameter40.Name": parameter40_name, + "Parameter40.Value": parameter40_value, + "Parameter41.Name": parameter41_name, + "Parameter41.Value": parameter41_value, + "Parameter42.Name": parameter42_name, + "Parameter42.Value": parameter42_value, + "Parameter43.Name": parameter43_name, + "Parameter43.Value": parameter43_value, + "Parameter44.Name": parameter44_name, + "Parameter44.Value": parameter44_value, + "Parameter45.Name": parameter45_name, + "Parameter45.Value": parameter45_value, + "Parameter46.Name": parameter46_name, + "Parameter46.Value": parameter46_value, + "Parameter47.Name": parameter47_name, + "Parameter47.Value": parameter47_value, + "Parameter48.Name": parameter48_name, + "Parameter48.Value": parameter48_value, + "Parameter49.Name": parameter49_name, + "Parameter49.Value": parameter49_value, + "Parameter50.Name": parameter50_name, + "Parameter50.Value": parameter50_value, + "Parameter51.Name": parameter51_name, + "Parameter51.Value": parameter51_value, + "Parameter52.Name": parameter52_name, + "Parameter52.Value": parameter52_value, + "Parameter53.Name": parameter53_name, + "Parameter53.Value": parameter53_value, + "Parameter54.Name": parameter54_name, + "Parameter54.Value": parameter54_value, + "Parameter55.Name": parameter55_name, + "Parameter55.Value": parameter55_value, + "Parameter56.Name": parameter56_name, + "Parameter56.Value": parameter56_value, + "Parameter57.Name": parameter57_name, + "Parameter57.Value": parameter57_value, + "Parameter58.Name": parameter58_name, + "Parameter58.Value": parameter58_value, + "Parameter59.Name": parameter59_name, + "Parameter59.Value": parameter59_value, + "Parameter60.Name": parameter60_name, + "Parameter60.Value": parameter60_value, + "Parameter61.Name": parameter61_name, + "Parameter61.Value": parameter61_value, + "Parameter62.Name": parameter62_name, + "Parameter62.Value": parameter62_value, + "Parameter63.Name": parameter63_name, + "Parameter63.Value": parameter63_value, + "Parameter64.Name": parameter64_name, + "Parameter64.Value": parameter64_value, + "Parameter65.Name": parameter65_name, + "Parameter65.Value": parameter65_value, + "Parameter66.Name": parameter66_name, + "Parameter66.Value": parameter66_value, + "Parameter67.Name": parameter67_name, + "Parameter67.Value": parameter67_value, + "Parameter68.Name": parameter68_name, + "Parameter68.Value": parameter68_value, + "Parameter69.Name": parameter69_name, + "Parameter69.Value": parameter69_value, + "Parameter70.Name": parameter70_name, + "Parameter70.Value": parameter70_value, + "Parameter71.Name": parameter71_name, + "Parameter71.Value": parameter71_value, + "Parameter72.Name": parameter72_name, + "Parameter72.Value": parameter72_value, + "Parameter73.Name": parameter73_name, + "Parameter73.Value": parameter73_value, + "Parameter74.Name": parameter74_name, + "Parameter74.Value": parameter74_value, + "Parameter75.Name": parameter75_name, + "Parameter75.Value": parameter75_value, + "Parameter76.Name": parameter76_name, + "Parameter76.Value": parameter76_value, + "Parameter77.Name": parameter77_name, + "Parameter77.Value": parameter77_value, + "Parameter78.Name": parameter78_name, + "Parameter78.Value": parameter78_value, + "Parameter79.Name": parameter79_name, + "Parameter79.Value": parameter79_value, + "Parameter80.Name": parameter80_name, + "Parameter80.Value": parameter80_value, + "Parameter81.Name": parameter81_name, + "Parameter81.Value": parameter81_value, + "Parameter82.Name": parameter82_name, + "Parameter82.Value": parameter82_value, + "Parameter83.Name": parameter83_name, + "Parameter83.Value": parameter83_value, + "Parameter84.Name": parameter84_name, + "Parameter84.Value": parameter84_value, + "Parameter85.Name": parameter85_name, + "Parameter85.Value": parameter85_value, + "Parameter86.Name": parameter86_name, + "Parameter86.Value": parameter86_value, + "Parameter87.Name": parameter87_name, + "Parameter87.Value": parameter87_value, + "Parameter88.Name": parameter88_name, + "Parameter88.Value": parameter88_value, + "Parameter89.Name": parameter89_name, + "Parameter89.Value": parameter89_value, + "Parameter90.Name": parameter90_name, + "Parameter90.Value": parameter90_value, + "Parameter91.Name": parameter91_name, + "Parameter91.Value": parameter91_value, + "Parameter92.Name": parameter92_name, + "Parameter92.Value": parameter92_value, + "Parameter93.Name": parameter93_name, + "Parameter93.Value": parameter93_value, + "Parameter94.Name": parameter94_name, + "Parameter94.Value": parameter94_value, + "Parameter95.Name": parameter95_name, + "Parameter95.Value": parameter95_value, + "Parameter96.Name": parameter96_name, + "Parameter96.Value": parameter96_value, + "Parameter97.Name": parameter97_name, + "Parameter97.Value": parameter97_value, + "Parameter98.Name": parameter98_name, + "Parameter98.Value": parameter98_value, + "Parameter99.Name": parameter99_name, + "Parameter99.Value": parameter99_value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SiprecInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, + name: Union[str, object] = values.unset, + connector_name: Union[str, object] = values.unset, + track: Union["SiprecInstance.Track", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + parameter1_name: Union[str, object] = values.unset, + parameter1_value: Union[str, object] = values.unset, + parameter2_name: Union[str, object] = values.unset, + parameter2_value: Union[str, object] = values.unset, + parameter3_name: Union[str, object] = values.unset, + parameter3_value: Union[str, object] = values.unset, + parameter4_name: Union[str, object] = values.unset, + parameter4_value: Union[str, object] = values.unset, + parameter5_name: Union[str, object] = values.unset, + parameter5_value: Union[str, object] = values.unset, + parameter6_name: Union[str, object] = values.unset, + parameter6_value: Union[str, object] = values.unset, + parameter7_name: Union[str, object] = values.unset, + parameter7_value: Union[str, object] = values.unset, + parameter8_name: Union[str, object] = values.unset, + parameter8_value: Union[str, object] = values.unset, + parameter9_name: Union[str, object] = values.unset, + parameter9_value: Union[str, object] = values.unset, + parameter10_name: Union[str, object] = values.unset, + parameter10_value: Union[str, object] = values.unset, + parameter11_name: Union[str, object] = values.unset, + parameter11_value: Union[str, object] = values.unset, + parameter12_name: Union[str, object] = values.unset, + parameter12_value: Union[str, object] = values.unset, + parameter13_name: Union[str, object] = values.unset, + parameter13_value: Union[str, object] = values.unset, + parameter14_name: Union[str, object] = values.unset, + parameter14_value: Union[str, object] = values.unset, + parameter15_name: Union[str, object] = values.unset, + parameter15_value: Union[str, object] = values.unset, + parameter16_name: Union[str, object] = values.unset, + parameter16_value: Union[str, object] = values.unset, + parameter17_name: Union[str, object] = values.unset, + parameter17_value: Union[str, object] = values.unset, + parameter18_name: Union[str, object] = values.unset, + parameter18_value: Union[str, object] = values.unset, + parameter19_name: Union[str, object] = values.unset, + parameter19_value: Union[str, object] = values.unset, + parameter20_name: Union[str, object] = values.unset, + parameter20_value: Union[str, object] = values.unset, + parameter21_name: Union[str, object] = values.unset, + parameter21_value: Union[str, object] = values.unset, + parameter22_name: Union[str, object] = values.unset, + parameter22_value: Union[str, object] = values.unset, + parameter23_name: Union[str, object] = values.unset, + parameter23_value: Union[str, object] = values.unset, + parameter24_name: Union[str, object] = values.unset, + parameter24_value: Union[str, object] = values.unset, + parameter25_name: Union[str, object] = values.unset, + parameter25_value: Union[str, object] = values.unset, + parameter26_name: Union[str, object] = values.unset, + parameter26_value: Union[str, object] = values.unset, + parameter27_name: Union[str, object] = values.unset, + parameter27_value: Union[str, object] = values.unset, + parameter28_name: Union[str, object] = values.unset, + parameter28_value: Union[str, object] = values.unset, + parameter29_name: Union[str, object] = values.unset, + parameter29_value: Union[str, object] = values.unset, + parameter30_name: Union[str, object] = values.unset, + parameter30_value: Union[str, object] = values.unset, + parameter31_name: Union[str, object] = values.unset, + parameter31_value: Union[str, object] = values.unset, + parameter32_name: Union[str, object] = values.unset, + parameter32_value: Union[str, object] = values.unset, + parameter33_name: Union[str, object] = values.unset, + parameter33_value: Union[str, object] = values.unset, + parameter34_name: Union[str, object] = values.unset, + parameter34_value: Union[str, object] = values.unset, + parameter35_name: Union[str, object] = values.unset, + parameter35_value: Union[str, object] = values.unset, + parameter36_name: Union[str, object] = values.unset, + parameter36_value: Union[str, object] = values.unset, + parameter37_name: Union[str, object] = values.unset, + parameter37_value: Union[str, object] = values.unset, + parameter38_name: Union[str, object] = values.unset, + parameter38_value: Union[str, object] = values.unset, + parameter39_name: Union[str, object] = values.unset, + parameter39_value: Union[str, object] = values.unset, + parameter40_name: Union[str, object] = values.unset, + parameter40_value: Union[str, object] = values.unset, + parameter41_name: Union[str, object] = values.unset, + parameter41_value: Union[str, object] = values.unset, + parameter42_name: Union[str, object] = values.unset, + parameter42_value: Union[str, object] = values.unset, + parameter43_name: Union[str, object] = values.unset, + parameter43_value: Union[str, object] = values.unset, + parameter44_name: Union[str, object] = values.unset, + parameter44_value: Union[str, object] = values.unset, + parameter45_name: Union[str, object] = values.unset, + parameter45_value: Union[str, object] = values.unset, + parameter46_name: Union[str, object] = values.unset, + parameter46_value: Union[str, object] = values.unset, + parameter47_name: Union[str, object] = values.unset, + parameter47_value: Union[str, object] = values.unset, + parameter48_name: Union[str, object] = values.unset, + parameter48_value: Union[str, object] = values.unset, + parameter49_name: Union[str, object] = values.unset, + parameter49_value: Union[str, object] = values.unset, + parameter50_name: Union[str, object] = values.unset, + parameter50_value: Union[str, object] = values.unset, + parameter51_name: Union[str, object] = values.unset, + parameter51_value: Union[str, object] = values.unset, + parameter52_name: Union[str, object] = values.unset, + parameter52_value: Union[str, object] = values.unset, + parameter53_name: Union[str, object] = values.unset, + parameter53_value: Union[str, object] = values.unset, + parameter54_name: Union[str, object] = values.unset, + parameter54_value: Union[str, object] = values.unset, + parameter55_name: Union[str, object] = values.unset, + parameter55_value: Union[str, object] = values.unset, + parameter56_name: Union[str, object] = values.unset, + parameter56_value: Union[str, object] = values.unset, + parameter57_name: Union[str, object] = values.unset, + parameter57_value: Union[str, object] = values.unset, + parameter58_name: Union[str, object] = values.unset, + parameter58_value: Union[str, object] = values.unset, + parameter59_name: Union[str, object] = values.unset, + parameter59_value: Union[str, object] = values.unset, + parameter60_name: Union[str, object] = values.unset, + parameter60_value: Union[str, object] = values.unset, + parameter61_name: Union[str, object] = values.unset, + parameter61_value: Union[str, object] = values.unset, + parameter62_name: Union[str, object] = values.unset, + parameter62_value: Union[str, object] = values.unset, + parameter63_name: Union[str, object] = values.unset, + parameter63_value: Union[str, object] = values.unset, + parameter64_name: Union[str, object] = values.unset, + parameter64_value: Union[str, object] = values.unset, + parameter65_name: Union[str, object] = values.unset, + parameter65_value: Union[str, object] = values.unset, + parameter66_name: Union[str, object] = values.unset, + parameter66_value: Union[str, object] = values.unset, + parameter67_name: Union[str, object] = values.unset, + parameter67_value: Union[str, object] = values.unset, + parameter68_name: Union[str, object] = values.unset, + parameter68_value: Union[str, object] = values.unset, + parameter69_name: Union[str, object] = values.unset, + parameter69_value: Union[str, object] = values.unset, + parameter70_name: Union[str, object] = values.unset, + parameter70_value: Union[str, object] = values.unset, + parameter71_name: Union[str, object] = values.unset, + parameter71_value: Union[str, object] = values.unset, + parameter72_name: Union[str, object] = values.unset, + parameter72_value: Union[str, object] = values.unset, + parameter73_name: Union[str, object] = values.unset, + parameter73_value: Union[str, object] = values.unset, + parameter74_name: Union[str, object] = values.unset, + parameter74_value: Union[str, object] = values.unset, + parameter75_name: Union[str, object] = values.unset, + parameter75_value: Union[str, object] = values.unset, + parameter76_name: Union[str, object] = values.unset, + parameter76_value: Union[str, object] = values.unset, + parameter77_name: Union[str, object] = values.unset, + parameter77_value: Union[str, object] = values.unset, + parameter78_name: Union[str, object] = values.unset, + parameter78_value: Union[str, object] = values.unset, + parameter79_name: Union[str, object] = values.unset, + parameter79_value: Union[str, object] = values.unset, + parameter80_name: Union[str, object] = values.unset, + parameter80_value: Union[str, object] = values.unset, + parameter81_name: Union[str, object] = values.unset, + parameter81_value: Union[str, object] = values.unset, + parameter82_name: Union[str, object] = values.unset, + parameter82_value: Union[str, object] = values.unset, + parameter83_name: Union[str, object] = values.unset, + parameter83_value: Union[str, object] = values.unset, + parameter84_name: Union[str, object] = values.unset, + parameter84_value: Union[str, object] = values.unset, + parameter85_name: Union[str, object] = values.unset, + parameter85_value: Union[str, object] = values.unset, + parameter86_name: Union[str, object] = values.unset, + parameter86_value: Union[str, object] = values.unset, + parameter87_name: Union[str, object] = values.unset, + parameter87_value: Union[str, object] = values.unset, + parameter88_name: Union[str, object] = values.unset, + parameter88_value: Union[str, object] = values.unset, + parameter89_name: Union[str, object] = values.unset, + parameter89_value: Union[str, object] = values.unset, + parameter90_name: Union[str, object] = values.unset, + parameter90_value: Union[str, object] = values.unset, + parameter91_name: Union[str, object] = values.unset, + parameter91_value: Union[str, object] = values.unset, + parameter92_name: Union[str, object] = values.unset, + parameter92_value: Union[str, object] = values.unset, + parameter93_name: Union[str, object] = values.unset, + parameter93_value: Union[str, object] = values.unset, + parameter94_name: Union[str, object] = values.unset, + parameter94_value: Union[str, object] = values.unset, + parameter95_name: Union[str, object] = values.unset, + parameter95_value: Union[str, object] = values.unset, + parameter96_name: Union[str, object] = values.unset, + parameter96_value: Union[str, object] = values.unset, + parameter97_name: Union[str, object] = values.unset, + parameter97_value: Union[str, object] = values.unset, + parameter98_name: Union[str, object] = values.unset, + parameter98_value: Union[str, object] = values.unset, + parameter99_name: Union[str, object] = values.unset, + parameter99_value: Union[str, object] = values.unset, + ) -> SiprecInstance: + """ + Asynchronously create the SiprecInstance + + :param name: The user-specified name of this Siprec, if one was given when the Siprec was created. This may be used to stop the Siprec. + :param connector_name: Unique name used when configuring the connector via Marketplace Add-on. + :param track: + :param status_callback: Absolute URL of the status callback. + :param status_callback_method: The http method for the status_callback (one of GET, POST). + :param parameter1_name: Parameter name + :param parameter1_value: Parameter value + :param parameter2_name: Parameter name + :param parameter2_value: Parameter value + :param parameter3_name: Parameter name + :param parameter3_value: Parameter value + :param parameter4_name: Parameter name + :param parameter4_value: Parameter value + :param parameter5_name: Parameter name + :param parameter5_value: Parameter value + :param parameter6_name: Parameter name + :param parameter6_value: Parameter value + :param parameter7_name: Parameter name + :param parameter7_value: Parameter value + :param parameter8_name: Parameter name + :param parameter8_value: Parameter value + :param parameter9_name: Parameter name + :param parameter9_value: Parameter value + :param parameter10_name: Parameter name + :param parameter10_value: Parameter value + :param parameter11_name: Parameter name + :param parameter11_value: Parameter value + :param parameter12_name: Parameter name + :param parameter12_value: Parameter value + :param parameter13_name: Parameter name + :param parameter13_value: Parameter value + :param parameter14_name: Parameter name + :param parameter14_value: Parameter value + :param parameter15_name: Parameter name + :param parameter15_value: Parameter value + :param parameter16_name: Parameter name + :param parameter16_value: Parameter value + :param parameter17_name: Parameter name + :param parameter17_value: Parameter value + :param parameter18_name: Parameter name + :param parameter18_value: Parameter value + :param parameter19_name: Parameter name + :param parameter19_value: Parameter value + :param parameter20_name: Parameter name + :param parameter20_value: Parameter value + :param parameter21_name: Parameter name + :param parameter21_value: Parameter value + :param parameter22_name: Parameter name + :param parameter22_value: Parameter value + :param parameter23_name: Parameter name + :param parameter23_value: Parameter value + :param parameter24_name: Parameter name + :param parameter24_value: Parameter value + :param parameter25_name: Parameter name + :param parameter25_value: Parameter value + :param parameter26_name: Parameter name + :param parameter26_value: Parameter value + :param parameter27_name: Parameter name + :param parameter27_value: Parameter value + :param parameter28_name: Parameter name + :param parameter28_value: Parameter value + :param parameter29_name: Parameter name + :param parameter29_value: Parameter value + :param parameter30_name: Parameter name + :param parameter30_value: Parameter value + :param parameter31_name: Parameter name + :param parameter31_value: Parameter value + :param parameter32_name: Parameter name + :param parameter32_value: Parameter value + :param parameter33_name: Parameter name + :param parameter33_value: Parameter value + :param parameter34_name: Parameter name + :param parameter34_value: Parameter value + :param parameter35_name: Parameter name + :param parameter35_value: Parameter value + :param parameter36_name: Parameter name + :param parameter36_value: Parameter value + :param parameter37_name: Parameter name + :param parameter37_value: Parameter value + :param parameter38_name: Parameter name + :param parameter38_value: Parameter value + :param parameter39_name: Parameter name + :param parameter39_value: Parameter value + :param parameter40_name: Parameter name + :param parameter40_value: Parameter value + :param parameter41_name: Parameter name + :param parameter41_value: Parameter value + :param parameter42_name: Parameter name + :param parameter42_value: Parameter value + :param parameter43_name: Parameter name + :param parameter43_value: Parameter value + :param parameter44_name: Parameter name + :param parameter44_value: Parameter value + :param parameter45_name: Parameter name + :param parameter45_value: Parameter value + :param parameter46_name: Parameter name + :param parameter46_value: Parameter value + :param parameter47_name: Parameter name + :param parameter47_value: Parameter value + :param parameter48_name: Parameter name + :param parameter48_value: Parameter value + :param parameter49_name: Parameter name + :param parameter49_value: Parameter value + :param parameter50_name: Parameter name + :param parameter50_value: Parameter value + :param parameter51_name: Parameter name + :param parameter51_value: Parameter value + :param parameter52_name: Parameter name + :param parameter52_value: Parameter value + :param parameter53_name: Parameter name + :param parameter53_value: Parameter value + :param parameter54_name: Parameter name + :param parameter54_value: Parameter value + :param parameter55_name: Parameter name + :param parameter55_value: Parameter value + :param parameter56_name: Parameter name + :param parameter56_value: Parameter value + :param parameter57_name: Parameter name + :param parameter57_value: Parameter value + :param parameter58_name: Parameter name + :param parameter58_value: Parameter value + :param parameter59_name: Parameter name + :param parameter59_value: Parameter value + :param parameter60_name: Parameter name + :param parameter60_value: Parameter value + :param parameter61_name: Parameter name + :param parameter61_value: Parameter value + :param parameter62_name: Parameter name + :param parameter62_value: Parameter value + :param parameter63_name: Parameter name + :param parameter63_value: Parameter value + :param parameter64_name: Parameter name + :param parameter64_value: Parameter value + :param parameter65_name: Parameter name + :param parameter65_value: Parameter value + :param parameter66_name: Parameter name + :param parameter66_value: Parameter value + :param parameter67_name: Parameter name + :param parameter67_value: Parameter value + :param parameter68_name: Parameter name + :param parameter68_value: Parameter value + :param parameter69_name: Parameter name + :param parameter69_value: Parameter value + :param parameter70_name: Parameter name + :param parameter70_value: Parameter value + :param parameter71_name: Parameter name + :param parameter71_value: Parameter value + :param parameter72_name: Parameter name + :param parameter72_value: Parameter value + :param parameter73_name: Parameter name + :param parameter73_value: Parameter value + :param parameter74_name: Parameter name + :param parameter74_value: Parameter value + :param parameter75_name: Parameter name + :param parameter75_value: Parameter value + :param parameter76_name: Parameter name + :param parameter76_value: Parameter value + :param parameter77_name: Parameter name + :param parameter77_value: Parameter value + :param parameter78_name: Parameter name + :param parameter78_value: Parameter value + :param parameter79_name: Parameter name + :param parameter79_value: Parameter value + :param parameter80_name: Parameter name + :param parameter80_value: Parameter value + :param parameter81_name: Parameter name + :param parameter81_value: Parameter value + :param parameter82_name: Parameter name + :param parameter82_value: Parameter value + :param parameter83_name: Parameter name + :param parameter83_value: Parameter value + :param parameter84_name: Parameter name + :param parameter84_value: Parameter value + :param parameter85_name: Parameter name + :param parameter85_value: Parameter value + :param parameter86_name: Parameter name + :param parameter86_value: Parameter value + :param parameter87_name: Parameter name + :param parameter87_value: Parameter value + :param parameter88_name: Parameter name + :param parameter88_value: Parameter value + :param parameter89_name: Parameter name + :param parameter89_value: Parameter value + :param parameter90_name: Parameter name + :param parameter90_value: Parameter value + :param parameter91_name: Parameter name + :param parameter91_value: Parameter value + :param parameter92_name: Parameter name + :param parameter92_value: Parameter value + :param parameter93_name: Parameter name + :param parameter93_value: Parameter value + :param parameter94_name: Parameter name + :param parameter94_value: Parameter value + :param parameter95_name: Parameter name + :param parameter95_value: Parameter value + :param parameter96_name: Parameter name + :param parameter96_value: Parameter value + :param parameter97_name: Parameter name + :param parameter97_value: Parameter value + :param parameter98_name: Parameter name + :param parameter98_value: Parameter value + :param parameter99_name: Parameter name + :param parameter99_value: Parameter value + + :returns: The created SiprecInstance + """ + + data = values.of( + { + "Name": name, + "ConnectorName": connector_name, + "Track": track, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Parameter1.Name": parameter1_name, + "Parameter1.Value": parameter1_value, + "Parameter2.Name": parameter2_name, + "Parameter2.Value": parameter2_value, + "Parameter3.Name": parameter3_name, + "Parameter3.Value": parameter3_value, + "Parameter4.Name": parameter4_name, + "Parameter4.Value": parameter4_value, + "Parameter5.Name": parameter5_name, + "Parameter5.Value": parameter5_value, + "Parameter6.Name": parameter6_name, + "Parameter6.Value": parameter6_value, + "Parameter7.Name": parameter7_name, + "Parameter7.Value": parameter7_value, + "Parameter8.Name": parameter8_name, + "Parameter8.Value": parameter8_value, + "Parameter9.Name": parameter9_name, + "Parameter9.Value": parameter9_value, + "Parameter10.Name": parameter10_name, + "Parameter10.Value": parameter10_value, + "Parameter11.Name": parameter11_name, + "Parameter11.Value": parameter11_value, + "Parameter12.Name": parameter12_name, + "Parameter12.Value": parameter12_value, + "Parameter13.Name": parameter13_name, + "Parameter13.Value": parameter13_value, + "Parameter14.Name": parameter14_name, + "Parameter14.Value": parameter14_value, + "Parameter15.Name": parameter15_name, + "Parameter15.Value": parameter15_value, + "Parameter16.Name": parameter16_name, + "Parameter16.Value": parameter16_value, + "Parameter17.Name": parameter17_name, + "Parameter17.Value": parameter17_value, + "Parameter18.Name": parameter18_name, + "Parameter18.Value": parameter18_value, + "Parameter19.Name": parameter19_name, + "Parameter19.Value": parameter19_value, + "Parameter20.Name": parameter20_name, + "Parameter20.Value": parameter20_value, + "Parameter21.Name": parameter21_name, + "Parameter21.Value": parameter21_value, + "Parameter22.Name": parameter22_name, + "Parameter22.Value": parameter22_value, + "Parameter23.Name": parameter23_name, + "Parameter23.Value": parameter23_value, + "Parameter24.Name": parameter24_name, + "Parameter24.Value": parameter24_value, + "Parameter25.Name": parameter25_name, + "Parameter25.Value": parameter25_value, + "Parameter26.Name": parameter26_name, + "Parameter26.Value": parameter26_value, + "Parameter27.Name": parameter27_name, + "Parameter27.Value": parameter27_value, + "Parameter28.Name": parameter28_name, + "Parameter28.Value": parameter28_value, + "Parameter29.Name": parameter29_name, + "Parameter29.Value": parameter29_value, + "Parameter30.Name": parameter30_name, + "Parameter30.Value": parameter30_value, + "Parameter31.Name": parameter31_name, + "Parameter31.Value": parameter31_value, + "Parameter32.Name": parameter32_name, + "Parameter32.Value": parameter32_value, + "Parameter33.Name": parameter33_name, + "Parameter33.Value": parameter33_value, + "Parameter34.Name": parameter34_name, + "Parameter34.Value": parameter34_value, + "Parameter35.Name": parameter35_name, + "Parameter35.Value": parameter35_value, + "Parameter36.Name": parameter36_name, + "Parameter36.Value": parameter36_value, + "Parameter37.Name": parameter37_name, + "Parameter37.Value": parameter37_value, + "Parameter38.Name": parameter38_name, + "Parameter38.Value": parameter38_value, + "Parameter39.Name": parameter39_name, + "Parameter39.Value": parameter39_value, + "Parameter40.Name": parameter40_name, + "Parameter40.Value": parameter40_value, + "Parameter41.Name": parameter41_name, + "Parameter41.Value": parameter41_value, + "Parameter42.Name": parameter42_name, + "Parameter42.Value": parameter42_value, + "Parameter43.Name": parameter43_name, + "Parameter43.Value": parameter43_value, + "Parameter44.Name": parameter44_name, + "Parameter44.Value": parameter44_value, + "Parameter45.Name": parameter45_name, + "Parameter45.Value": parameter45_value, + "Parameter46.Name": parameter46_name, + "Parameter46.Value": parameter46_value, + "Parameter47.Name": parameter47_name, + "Parameter47.Value": parameter47_value, + "Parameter48.Name": parameter48_name, + "Parameter48.Value": parameter48_value, + "Parameter49.Name": parameter49_name, + "Parameter49.Value": parameter49_value, + "Parameter50.Name": parameter50_name, + "Parameter50.Value": parameter50_value, + "Parameter51.Name": parameter51_name, + "Parameter51.Value": parameter51_value, + "Parameter52.Name": parameter52_name, + "Parameter52.Value": parameter52_value, + "Parameter53.Name": parameter53_name, + "Parameter53.Value": parameter53_value, + "Parameter54.Name": parameter54_name, + "Parameter54.Value": parameter54_value, + "Parameter55.Name": parameter55_name, + "Parameter55.Value": parameter55_value, + "Parameter56.Name": parameter56_name, + "Parameter56.Value": parameter56_value, + "Parameter57.Name": parameter57_name, + "Parameter57.Value": parameter57_value, + "Parameter58.Name": parameter58_name, + "Parameter58.Value": parameter58_value, + "Parameter59.Name": parameter59_name, + "Parameter59.Value": parameter59_value, + "Parameter60.Name": parameter60_name, + "Parameter60.Value": parameter60_value, + "Parameter61.Name": parameter61_name, + "Parameter61.Value": parameter61_value, + "Parameter62.Name": parameter62_name, + "Parameter62.Value": parameter62_value, + "Parameter63.Name": parameter63_name, + "Parameter63.Value": parameter63_value, + "Parameter64.Name": parameter64_name, + "Parameter64.Value": parameter64_value, + "Parameter65.Name": parameter65_name, + "Parameter65.Value": parameter65_value, + "Parameter66.Name": parameter66_name, + "Parameter66.Value": parameter66_value, + "Parameter67.Name": parameter67_name, + "Parameter67.Value": parameter67_value, + "Parameter68.Name": parameter68_name, + "Parameter68.Value": parameter68_value, + "Parameter69.Name": parameter69_name, + "Parameter69.Value": parameter69_value, + "Parameter70.Name": parameter70_name, + "Parameter70.Value": parameter70_value, + "Parameter71.Name": parameter71_name, + "Parameter71.Value": parameter71_value, + "Parameter72.Name": parameter72_name, + "Parameter72.Value": parameter72_value, + "Parameter73.Name": parameter73_name, + "Parameter73.Value": parameter73_value, + "Parameter74.Name": parameter74_name, + "Parameter74.Value": parameter74_value, + "Parameter75.Name": parameter75_name, + "Parameter75.Value": parameter75_value, + "Parameter76.Name": parameter76_name, + "Parameter76.Value": parameter76_value, + "Parameter77.Name": parameter77_name, + "Parameter77.Value": parameter77_value, + "Parameter78.Name": parameter78_name, + "Parameter78.Value": parameter78_value, + "Parameter79.Name": parameter79_name, + "Parameter79.Value": parameter79_value, + "Parameter80.Name": parameter80_name, + "Parameter80.Value": parameter80_value, + "Parameter81.Name": parameter81_name, + "Parameter81.Value": parameter81_value, + "Parameter82.Name": parameter82_name, + "Parameter82.Value": parameter82_value, + "Parameter83.Name": parameter83_name, + "Parameter83.Value": parameter83_value, + "Parameter84.Name": parameter84_name, + "Parameter84.Value": parameter84_value, + "Parameter85.Name": parameter85_name, + "Parameter85.Value": parameter85_value, + "Parameter86.Name": parameter86_name, + "Parameter86.Value": parameter86_value, + "Parameter87.Name": parameter87_name, + "Parameter87.Value": parameter87_value, + "Parameter88.Name": parameter88_name, + "Parameter88.Value": parameter88_value, + "Parameter89.Name": parameter89_name, + "Parameter89.Value": parameter89_value, + "Parameter90.Name": parameter90_name, + "Parameter90.Value": parameter90_value, + "Parameter91.Name": parameter91_name, + "Parameter91.Value": parameter91_value, + "Parameter92.Name": parameter92_name, + "Parameter92.Value": parameter92_value, + "Parameter93.Name": parameter93_name, + "Parameter93.Value": parameter93_value, + "Parameter94.Name": parameter94_name, + "Parameter94.Value": parameter94_value, + "Parameter95.Name": parameter95_name, + "Parameter95.Value": parameter95_value, + "Parameter96.Name": parameter96_name, + "Parameter96.Value": parameter96_value, + "Parameter97.Name": parameter97_name, + "Parameter97.Value": parameter97_value, + "Parameter98.Name": parameter98_name, + "Parameter98.Value": parameter98_value, + "Parameter99.Name": parameter99_name, + "Parameter99.Value": parameter99_value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SiprecInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def get(self, sid: str) -> SiprecContext: + """ + Constructs a SiprecContext + + :param sid: The SID of the Siprec resource, or the `name` used when creating the resource + """ + return SiprecContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> SiprecContext: + """ + Constructs a SiprecContext + + :param sid: The SID of the Siprec resource, or the `name` used when creating the resource + """ + return SiprecContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/stream.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/stream.py new file mode 100644 index 0000000..3975f52 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/stream.py @@ -0,0 +1,1549 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class StreamInstance(InstanceResource): + + class Status(object): + IN_PROGRESS = "in-progress" + STOPPED = "stopped" + + class Track(object): + INBOUND_TRACK = "inbound_track" + OUTBOUND_TRACK = "outbound_track" + BOTH_TRACKS = "both_tracks" + + class UpdateStatus(object): + STOPPED = "stopped" + + """ + :ivar sid: The SID of the Stream resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Stream resource. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Stream resource is associated with. + :ivar name: The user-specified name of this Stream, if one was given when the Stream was created. This can be used to stop the Stream. + :ivar status: + :ivar date_updated: The date and time in GMT that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.name: Optional[str] = payload.get("name") + self.status: Optional["StreamInstance.Status"] = payload.get("status") + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[StreamContext] = None + + @property + def _proxy(self) -> "StreamContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: StreamContext for this StreamInstance + """ + if self._context is None: + self._context = StreamContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update(self, status: "StreamInstance.UpdateStatus") -> "StreamInstance": + """ + Update the StreamInstance + + :param status: + + :returns: The updated StreamInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "StreamInstance.UpdateStatus" + ) -> "StreamInstance": + """ + Asynchronous coroutine to update the StreamInstance + + :param status: + + :returns: The updated StreamInstance + """ + return await self._proxy.update_async( + status=status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StreamContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the StreamContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Stream resource. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Stream resource is associated with. + :param sid: The SID or the `name` of the Stream resource to be stopped + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/Streams/{sid}.json".format( + **self._solution + ) + ) + + def update(self, status: "StreamInstance.UpdateStatus") -> StreamInstance: + """ + Update the StreamInstance + + :param status: + + :returns: The updated StreamInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "StreamInstance.UpdateStatus" + ) -> StreamInstance: + """ + Asynchronous coroutine to update the StreamInstance + + :param status: + + :returns: The updated StreamInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StreamList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the StreamList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Stream resource. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Stream resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/Streams.json".format( + **self._solution + ) + + def create( + self, + url: str, + name: Union[str, object] = values.unset, + track: Union["StreamInstance.Track", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + parameter1_name: Union[str, object] = values.unset, + parameter1_value: Union[str, object] = values.unset, + parameter2_name: Union[str, object] = values.unset, + parameter2_value: Union[str, object] = values.unset, + parameter3_name: Union[str, object] = values.unset, + parameter3_value: Union[str, object] = values.unset, + parameter4_name: Union[str, object] = values.unset, + parameter4_value: Union[str, object] = values.unset, + parameter5_name: Union[str, object] = values.unset, + parameter5_value: Union[str, object] = values.unset, + parameter6_name: Union[str, object] = values.unset, + parameter6_value: Union[str, object] = values.unset, + parameter7_name: Union[str, object] = values.unset, + parameter7_value: Union[str, object] = values.unset, + parameter8_name: Union[str, object] = values.unset, + parameter8_value: Union[str, object] = values.unset, + parameter9_name: Union[str, object] = values.unset, + parameter9_value: Union[str, object] = values.unset, + parameter10_name: Union[str, object] = values.unset, + parameter10_value: Union[str, object] = values.unset, + parameter11_name: Union[str, object] = values.unset, + parameter11_value: Union[str, object] = values.unset, + parameter12_name: Union[str, object] = values.unset, + parameter12_value: Union[str, object] = values.unset, + parameter13_name: Union[str, object] = values.unset, + parameter13_value: Union[str, object] = values.unset, + parameter14_name: Union[str, object] = values.unset, + parameter14_value: Union[str, object] = values.unset, + parameter15_name: Union[str, object] = values.unset, + parameter15_value: Union[str, object] = values.unset, + parameter16_name: Union[str, object] = values.unset, + parameter16_value: Union[str, object] = values.unset, + parameter17_name: Union[str, object] = values.unset, + parameter17_value: Union[str, object] = values.unset, + parameter18_name: Union[str, object] = values.unset, + parameter18_value: Union[str, object] = values.unset, + parameter19_name: Union[str, object] = values.unset, + parameter19_value: Union[str, object] = values.unset, + parameter20_name: Union[str, object] = values.unset, + parameter20_value: Union[str, object] = values.unset, + parameter21_name: Union[str, object] = values.unset, + parameter21_value: Union[str, object] = values.unset, + parameter22_name: Union[str, object] = values.unset, + parameter22_value: Union[str, object] = values.unset, + parameter23_name: Union[str, object] = values.unset, + parameter23_value: Union[str, object] = values.unset, + parameter24_name: Union[str, object] = values.unset, + parameter24_value: Union[str, object] = values.unset, + parameter25_name: Union[str, object] = values.unset, + parameter25_value: Union[str, object] = values.unset, + parameter26_name: Union[str, object] = values.unset, + parameter26_value: Union[str, object] = values.unset, + parameter27_name: Union[str, object] = values.unset, + parameter27_value: Union[str, object] = values.unset, + parameter28_name: Union[str, object] = values.unset, + parameter28_value: Union[str, object] = values.unset, + parameter29_name: Union[str, object] = values.unset, + parameter29_value: Union[str, object] = values.unset, + parameter30_name: Union[str, object] = values.unset, + parameter30_value: Union[str, object] = values.unset, + parameter31_name: Union[str, object] = values.unset, + parameter31_value: Union[str, object] = values.unset, + parameter32_name: Union[str, object] = values.unset, + parameter32_value: Union[str, object] = values.unset, + parameter33_name: Union[str, object] = values.unset, + parameter33_value: Union[str, object] = values.unset, + parameter34_name: Union[str, object] = values.unset, + parameter34_value: Union[str, object] = values.unset, + parameter35_name: Union[str, object] = values.unset, + parameter35_value: Union[str, object] = values.unset, + parameter36_name: Union[str, object] = values.unset, + parameter36_value: Union[str, object] = values.unset, + parameter37_name: Union[str, object] = values.unset, + parameter37_value: Union[str, object] = values.unset, + parameter38_name: Union[str, object] = values.unset, + parameter38_value: Union[str, object] = values.unset, + parameter39_name: Union[str, object] = values.unset, + parameter39_value: Union[str, object] = values.unset, + parameter40_name: Union[str, object] = values.unset, + parameter40_value: Union[str, object] = values.unset, + parameter41_name: Union[str, object] = values.unset, + parameter41_value: Union[str, object] = values.unset, + parameter42_name: Union[str, object] = values.unset, + parameter42_value: Union[str, object] = values.unset, + parameter43_name: Union[str, object] = values.unset, + parameter43_value: Union[str, object] = values.unset, + parameter44_name: Union[str, object] = values.unset, + parameter44_value: Union[str, object] = values.unset, + parameter45_name: Union[str, object] = values.unset, + parameter45_value: Union[str, object] = values.unset, + parameter46_name: Union[str, object] = values.unset, + parameter46_value: Union[str, object] = values.unset, + parameter47_name: Union[str, object] = values.unset, + parameter47_value: Union[str, object] = values.unset, + parameter48_name: Union[str, object] = values.unset, + parameter48_value: Union[str, object] = values.unset, + parameter49_name: Union[str, object] = values.unset, + parameter49_value: Union[str, object] = values.unset, + parameter50_name: Union[str, object] = values.unset, + parameter50_value: Union[str, object] = values.unset, + parameter51_name: Union[str, object] = values.unset, + parameter51_value: Union[str, object] = values.unset, + parameter52_name: Union[str, object] = values.unset, + parameter52_value: Union[str, object] = values.unset, + parameter53_name: Union[str, object] = values.unset, + parameter53_value: Union[str, object] = values.unset, + parameter54_name: Union[str, object] = values.unset, + parameter54_value: Union[str, object] = values.unset, + parameter55_name: Union[str, object] = values.unset, + parameter55_value: Union[str, object] = values.unset, + parameter56_name: Union[str, object] = values.unset, + parameter56_value: Union[str, object] = values.unset, + parameter57_name: Union[str, object] = values.unset, + parameter57_value: Union[str, object] = values.unset, + parameter58_name: Union[str, object] = values.unset, + parameter58_value: Union[str, object] = values.unset, + parameter59_name: Union[str, object] = values.unset, + parameter59_value: Union[str, object] = values.unset, + parameter60_name: Union[str, object] = values.unset, + parameter60_value: Union[str, object] = values.unset, + parameter61_name: Union[str, object] = values.unset, + parameter61_value: Union[str, object] = values.unset, + parameter62_name: Union[str, object] = values.unset, + parameter62_value: Union[str, object] = values.unset, + parameter63_name: Union[str, object] = values.unset, + parameter63_value: Union[str, object] = values.unset, + parameter64_name: Union[str, object] = values.unset, + parameter64_value: Union[str, object] = values.unset, + parameter65_name: Union[str, object] = values.unset, + parameter65_value: Union[str, object] = values.unset, + parameter66_name: Union[str, object] = values.unset, + parameter66_value: Union[str, object] = values.unset, + parameter67_name: Union[str, object] = values.unset, + parameter67_value: Union[str, object] = values.unset, + parameter68_name: Union[str, object] = values.unset, + parameter68_value: Union[str, object] = values.unset, + parameter69_name: Union[str, object] = values.unset, + parameter69_value: Union[str, object] = values.unset, + parameter70_name: Union[str, object] = values.unset, + parameter70_value: Union[str, object] = values.unset, + parameter71_name: Union[str, object] = values.unset, + parameter71_value: Union[str, object] = values.unset, + parameter72_name: Union[str, object] = values.unset, + parameter72_value: Union[str, object] = values.unset, + parameter73_name: Union[str, object] = values.unset, + parameter73_value: Union[str, object] = values.unset, + parameter74_name: Union[str, object] = values.unset, + parameter74_value: Union[str, object] = values.unset, + parameter75_name: Union[str, object] = values.unset, + parameter75_value: Union[str, object] = values.unset, + parameter76_name: Union[str, object] = values.unset, + parameter76_value: Union[str, object] = values.unset, + parameter77_name: Union[str, object] = values.unset, + parameter77_value: Union[str, object] = values.unset, + parameter78_name: Union[str, object] = values.unset, + parameter78_value: Union[str, object] = values.unset, + parameter79_name: Union[str, object] = values.unset, + parameter79_value: Union[str, object] = values.unset, + parameter80_name: Union[str, object] = values.unset, + parameter80_value: Union[str, object] = values.unset, + parameter81_name: Union[str, object] = values.unset, + parameter81_value: Union[str, object] = values.unset, + parameter82_name: Union[str, object] = values.unset, + parameter82_value: Union[str, object] = values.unset, + parameter83_name: Union[str, object] = values.unset, + parameter83_value: Union[str, object] = values.unset, + parameter84_name: Union[str, object] = values.unset, + parameter84_value: Union[str, object] = values.unset, + parameter85_name: Union[str, object] = values.unset, + parameter85_value: Union[str, object] = values.unset, + parameter86_name: Union[str, object] = values.unset, + parameter86_value: Union[str, object] = values.unset, + parameter87_name: Union[str, object] = values.unset, + parameter87_value: Union[str, object] = values.unset, + parameter88_name: Union[str, object] = values.unset, + parameter88_value: Union[str, object] = values.unset, + parameter89_name: Union[str, object] = values.unset, + parameter89_value: Union[str, object] = values.unset, + parameter90_name: Union[str, object] = values.unset, + parameter90_value: Union[str, object] = values.unset, + parameter91_name: Union[str, object] = values.unset, + parameter91_value: Union[str, object] = values.unset, + parameter92_name: Union[str, object] = values.unset, + parameter92_value: Union[str, object] = values.unset, + parameter93_name: Union[str, object] = values.unset, + parameter93_value: Union[str, object] = values.unset, + parameter94_name: Union[str, object] = values.unset, + parameter94_value: Union[str, object] = values.unset, + parameter95_name: Union[str, object] = values.unset, + parameter95_value: Union[str, object] = values.unset, + parameter96_name: Union[str, object] = values.unset, + parameter96_value: Union[str, object] = values.unset, + parameter97_name: Union[str, object] = values.unset, + parameter97_value: Union[str, object] = values.unset, + parameter98_name: Union[str, object] = values.unset, + parameter98_value: Union[str, object] = values.unset, + parameter99_name: Union[str, object] = values.unset, + parameter99_value: Union[str, object] = values.unset, + ) -> StreamInstance: + """ + Create the StreamInstance + + :param url: Relative or absolute URL where WebSocket connection will be established. + :param name: The user-specified name of this Stream, if one was given when the Stream was created. This can be used to stop the Stream. + :param track: + :param status_callback: Absolute URL to which Twilio sends status callback HTTP requests. + :param status_callback_method: The HTTP method Twilio uses when sending `status_callback` requests. Possible values are `GET` and `POST`. Default is `POST`. + :param parameter1_name: Parameter name + :param parameter1_value: Parameter value + :param parameter2_name: Parameter name + :param parameter2_value: Parameter value + :param parameter3_name: Parameter name + :param parameter3_value: Parameter value + :param parameter4_name: Parameter name + :param parameter4_value: Parameter value + :param parameter5_name: Parameter name + :param parameter5_value: Parameter value + :param parameter6_name: Parameter name + :param parameter6_value: Parameter value + :param parameter7_name: Parameter name + :param parameter7_value: Parameter value + :param parameter8_name: Parameter name + :param parameter8_value: Parameter value + :param parameter9_name: Parameter name + :param parameter9_value: Parameter value + :param parameter10_name: Parameter name + :param parameter10_value: Parameter value + :param parameter11_name: Parameter name + :param parameter11_value: Parameter value + :param parameter12_name: Parameter name + :param parameter12_value: Parameter value + :param parameter13_name: Parameter name + :param parameter13_value: Parameter value + :param parameter14_name: Parameter name + :param parameter14_value: Parameter value + :param parameter15_name: Parameter name + :param parameter15_value: Parameter value + :param parameter16_name: Parameter name + :param parameter16_value: Parameter value + :param parameter17_name: Parameter name + :param parameter17_value: Parameter value + :param parameter18_name: Parameter name + :param parameter18_value: Parameter value + :param parameter19_name: Parameter name + :param parameter19_value: Parameter value + :param parameter20_name: Parameter name + :param parameter20_value: Parameter value + :param parameter21_name: Parameter name + :param parameter21_value: Parameter value + :param parameter22_name: Parameter name + :param parameter22_value: Parameter value + :param parameter23_name: Parameter name + :param parameter23_value: Parameter value + :param parameter24_name: Parameter name + :param parameter24_value: Parameter value + :param parameter25_name: Parameter name + :param parameter25_value: Parameter value + :param parameter26_name: Parameter name + :param parameter26_value: Parameter value + :param parameter27_name: Parameter name + :param parameter27_value: Parameter value + :param parameter28_name: Parameter name + :param parameter28_value: Parameter value + :param parameter29_name: Parameter name + :param parameter29_value: Parameter value + :param parameter30_name: Parameter name + :param parameter30_value: Parameter value + :param parameter31_name: Parameter name + :param parameter31_value: Parameter value + :param parameter32_name: Parameter name + :param parameter32_value: Parameter value + :param parameter33_name: Parameter name + :param parameter33_value: Parameter value + :param parameter34_name: Parameter name + :param parameter34_value: Parameter value + :param parameter35_name: Parameter name + :param parameter35_value: Parameter value + :param parameter36_name: Parameter name + :param parameter36_value: Parameter value + :param parameter37_name: Parameter name + :param parameter37_value: Parameter value + :param parameter38_name: Parameter name + :param parameter38_value: Parameter value + :param parameter39_name: Parameter name + :param parameter39_value: Parameter value + :param parameter40_name: Parameter name + :param parameter40_value: Parameter value + :param parameter41_name: Parameter name + :param parameter41_value: Parameter value + :param parameter42_name: Parameter name + :param parameter42_value: Parameter value + :param parameter43_name: Parameter name + :param parameter43_value: Parameter value + :param parameter44_name: Parameter name + :param parameter44_value: Parameter value + :param parameter45_name: Parameter name + :param parameter45_value: Parameter value + :param parameter46_name: Parameter name + :param parameter46_value: Parameter value + :param parameter47_name: Parameter name + :param parameter47_value: Parameter value + :param parameter48_name: Parameter name + :param parameter48_value: Parameter value + :param parameter49_name: Parameter name + :param parameter49_value: Parameter value + :param parameter50_name: Parameter name + :param parameter50_value: Parameter value + :param parameter51_name: Parameter name + :param parameter51_value: Parameter value + :param parameter52_name: Parameter name + :param parameter52_value: Parameter value + :param parameter53_name: Parameter name + :param parameter53_value: Parameter value + :param parameter54_name: Parameter name + :param parameter54_value: Parameter value + :param parameter55_name: Parameter name + :param parameter55_value: Parameter value + :param parameter56_name: Parameter name + :param parameter56_value: Parameter value + :param parameter57_name: Parameter name + :param parameter57_value: Parameter value + :param parameter58_name: Parameter name + :param parameter58_value: Parameter value + :param parameter59_name: Parameter name + :param parameter59_value: Parameter value + :param parameter60_name: Parameter name + :param parameter60_value: Parameter value + :param parameter61_name: Parameter name + :param parameter61_value: Parameter value + :param parameter62_name: Parameter name + :param parameter62_value: Parameter value + :param parameter63_name: Parameter name + :param parameter63_value: Parameter value + :param parameter64_name: Parameter name + :param parameter64_value: Parameter value + :param parameter65_name: Parameter name + :param parameter65_value: Parameter value + :param parameter66_name: Parameter name + :param parameter66_value: Parameter value + :param parameter67_name: Parameter name + :param parameter67_value: Parameter value + :param parameter68_name: Parameter name + :param parameter68_value: Parameter value + :param parameter69_name: Parameter name + :param parameter69_value: Parameter value + :param parameter70_name: Parameter name + :param parameter70_value: Parameter value + :param parameter71_name: Parameter name + :param parameter71_value: Parameter value + :param parameter72_name: Parameter name + :param parameter72_value: Parameter value + :param parameter73_name: Parameter name + :param parameter73_value: Parameter value + :param parameter74_name: Parameter name + :param parameter74_value: Parameter value + :param parameter75_name: Parameter name + :param parameter75_value: Parameter value + :param parameter76_name: Parameter name + :param parameter76_value: Parameter value + :param parameter77_name: Parameter name + :param parameter77_value: Parameter value + :param parameter78_name: Parameter name + :param parameter78_value: Parameter value + :param parameter79_name: Parameter name + :param parameter79_value: Parameter value + :param parameter80_name: Parameter name + :param parameter80_value: Parameter value + :param parameter81_name: Parameter name + :param parameter81_value: Parameter value + :param parameter82_name: Parameter name + :param parameter82_value: Parameter value + :param parameter83_name: Parameter name + :param parameter83_value: Parameter value + :param parameter84_name: Parameter name + :param parameter84_value: Parameter value + :param parameter85_name: Parameter name + :param parameter85_value: Parameter value + :param parameter86_name: Parameter name + :param parameter86_value: Parameter value + :param parameter87_name: Parameter name + :param parameter87_value: Parameter value + :param parameter88_name: Parameter name + :param parameter88_value: Parameter value + :param parameter89_name: Parameter name + :param parameter89_value: Parameter value + :param parameter90_name: Parameter name + :param parameter90_value: Parameter value + :param parameter91_name: Parameter name + :param parameter91_value: Parameter value + :param parameter92_name: Parameter name + :param parameter92_value: Parameter value + :param parameter93_name: Parameter name + :param parameter93_value: Parameter value + :param parameter94_name: Parameter name + :param parameter94_value: Parameter value + :param parameter95_name: Parameter name + :param parameter95_value: Parameter value + :param parameter96_name: Parameter name + :param parameter96_value: Parameter value + :param parameter97_name: Parameter name + :param parameter97_value: Parameter value + :param parameter98_name: Parameter name + :param parameter98_value: Parameter value + :param parameter99_name: Parameter name + :param parameter99_value: Parameter value + + :returns: The created StreamInstance + """ + + data = values.of( + { + "Url": url, + "Name": name, + "Track": track, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Parameter1.Name": parameter1_name, + "Parameter1.Value": parameter1_value, + "Parameter2.Name": parameter2_name, + "Parameter2.Value": parameter2_value, + "Parameter3.Name": parameter3_name, + "Parameter3.Value": parameter3_value, + "Parameter4.Name": parameter4_name, + "Parameter4.Value": parameter4_value, + "Parameter5.Name": parameter5_name, + "Parameter5.Value": parameter5_value, + "Parameter6.Name": parameter6_name, + "Parameter6.Value": parameter6_value, + "Parameter7.Name": parameter7_name, + "Parameter7.Value": parameter7_value, + "Parameter8.Name": parameter8_name, + "Parameter8.Value": parameter8_value, + "Parameter9.Name": parameter9_name, + "Parameter9.Value": parameter9_value, + "Parameter10.Name": parameter10_name, + "Parameter10.Value": parameter10_value, + "Parameter11.Name": parameter11_name, + "Parameter11.Value": parameter11_value, + "Parameter12.Name": parameter12_name, + "Parameter12.Value": parameter12_value, + "Parameter13.Name": parameter13_name, + "Parameter13.Value": parameter13_value, + "Parameter14.Name": parameter14_name, + "Parameter14.Value": parameter14_value, + "Parameter15.Name": parameter15_name, + "Parameter15.Value": parameter15_value, + "Parameter16.Name": parameter16_name, + "Parameter16.Value": parameter16_value, + "Parameter17.Name": parameter17_name, + "Parameter17.Value": parameter17_value, + "Parameter18.Name": parameter18_name, + "Parameter18.Value": parameter18_value, + "Parameter19.Name": parameter19_name, + "Parameter19.Value": parameter19_value, + "Parameter20.Name": parameter20_name, + "Parameter20.Value": parameter20_value, + "Parameter21.Name": parameter21_name, + "Parameter21.Value": parameter21_value, + "Parameter22.Name": parameter22_name, + "Parameter22.Value": parameter22_value, + "Parameter23.Name": parameter23_name, + "Parameter23.Value": parameter23_value, + "Parameter24.Name": parameter24_name, + "Parameter24.Value": parameter24_value, + "Parameter25.Name": parameter25_name, + "Parameter25.Value": parameter25_value, + "Parameter26.Name": parameter26_name, + "Parameter26.Value": parameter26_value, + "Parameter27.Name": parameter27_name, + "Parameter27.Value": parameter27_value, + "Parameter28.Name": parameter28_name, + "Parameter28.Value": parameter28_value, + "Parameter29.Name": parameter29_name, + "Parameter29.Value": parameter29_value, + "Parameter30.Name": parameter30_name, + "Parameter30.Value": parameter30_value, + "Parameter31.Name": parameter31_name, + "Parameter31.Value": parameter31_value, + "Parameter32.Name": parameter32_name, + "Parameter32.Value": parameter32_value, + "Parameter33.Name": parameter33_name, + "Parameter33.Value": parameter33_value, + "Parameter34.Name": parameter34_name, + "Parameter34.Value": parameter34_value, + "Parameter35.Name": parameter35_name, + "Parameter35.Value": parameter35_value, + "Parameter36.Name": parameter36_name, + "Parameter36.Value": parameter36_value, + "Parameter37.Name": parameter37_name, + "Parameter37.Value": parameter37_value, + "Parameter38.Name": parameter38_name, + "Parameter38.Value": parameter38_value, + "Parameter39.Name": parameter39_name, + "Parameter39.Value": parameter39_value, + "Parameter40.Name": parameter40_name, + "Parameter40.Value": parameter40_value, + "Parameter41.Name": parameter41_name, + "Parameter41.Value": parameter41_value, + "Parameter42.Name": parameter42_name, + "Parameter42.Value": parameter42_value, + "Parameter43.Name": parameter43_name, + "Parameter43.Value": parameter43_value, + "Parameter44.Name": parameter44_name, + "Parameter44.Value": parameter44_value, + "Parameter45.Name": parameter45_name, + "Parameter45.Value": parameter45_value, + "Parameter46.Name": parameter46_name, + "Parameter46.Value": parameter46_value, + "Parameter47.Name": parameter47_name, + "Parameter47.Value": parameter47_value, + "Parameter48.Name": parameter48_name, + "Parameter48.Value": parameter48_value, + "Parameter49.Name": parameter49_name, + "Parameter49.Value": parameter49_value, + "Parameter50.Name": parameter50_name, + "Parameter50.Value": parameter50_value, + "Parameter51.Name": parameter51_name, + "Parameter51.Value": parameter51_value, + "Parameter52.Name": parameter52_name, + "Parameter52.Value": parameter52_value, + "Parameter53.Name": parameter53_name, + "Parameter53.Value": parameter53_value, + "Parameter54.Name": parameter54_name, + "Parameter54.Value": parameter54_value, + "Parameter55.Name": parameter55_name, + "Parameter55.Value": parameter55_value, + "Parameter56.Name": parameter56_name, + "Parameter56.Value": parameter56_value, + "Parameter57.Name": parameter57_name, + "Parameter57.Value": parameter57_value, + "Parameter58.Name": parameter58_name, + "Parameter58.Value": parameter58_value, + "Parameter59.Name": parameter59_name, + "Parameter59.Value": parameter59_value, + "Parameter60.Name": parameter60_name, + "Parameter60.Value": parameter60_value, + "Parameter61.Name": parameter61_name, + "Parameter61.Value": parameter61_value, + "Parameter62.Name": parameter62_name, + "Parameter62.Value": parameter62_value, + "Parameter63.Name": parameter63_name, + "Parameter63.Value": parameter63_value, + "Parameter64.Name": parameter64_name, + "Parameter64.Value": parameter64_value, + "Parameter65.Name": parameter65_name, + "Parameter65.Value": parameter65_value, + "Parameter66.Name": parameter66_name, + "Parameter66.Value": parameter66_value, + "Parameter67.Name": parameter67_name, + "Parameter67.Value": parameter67_value, + "Parameter68.Name": parameter68_name, + "Parameter68.Value": parameter68_value, + "Parameter69.Name": parameter69_name, + "Parameter69.Value": parameter69_value, + "Parameter70.Name": parameter70_name, + "Parameter70.Value": parameter70_value, + "Parameter71.Name": parameter71_name, + "Parameter71.Value": parameter71_value, + "Parameter72.Name": parameter72_name, + "Parameter72.Value": parameter72_value, + "Parameter73.Name": parameter73_name, + "Parameter73.Value": parameter73_value, + "Parameter74.Name": parameter74_name, + "Parameter74.Value": parameter74_value, + "Parameter75.Name": parameter75_name, + "Parameter75.Value": parameter75_value, + "Parameter76.Name": parameter76_name, + "Parameter76.Value": parameter76_value, + "Parameter77.Name": parameter77_name, + "Parameter77.Value": parameter77_value, + "Parameter78.Name": parameter78_name, + "Parameter78.Value": parameter78_value, + "Parameter79.Name": parameter79_name, + "Parameter79.Value": parameter79_value, + "Parameter80.Name": parameter80_name, + "Parameter80.Value": parameter80_value, + "Parameter81.Name": parameter81_name, + "Parameter81.Value": parameter81_value, + "Parameter82.Name": parameter82_name, + "Parameter82.Value": parameter82_value, + "Parameter83.Name": parameter83_name, + "Parameter83.Value": parameter83_value, + "Parameter84.Name": parameter84_name, + "Parameter84.Value": parameter84_value, + "Parameter85.Name": parameter85_name, + "Parameter85.Value": parameter85_value, + "Parameter86.Name": parameter86_name, + "Parameter86.Value": parameter86_value, + "Parameter87.Name": parameter87_name, + "Parameter87.Value": parameter87_value, + "Parameter88.Name": parameter88_name, + "Parameter88.Value": parameter88_value, + "Parameter89.Name": parameter89_name, + "Parameter89.Value": parameter89_value, + "Parameter90.Name": parameter90_name, + "Parameter90.Value": parameter90_value, + "Parameter91.Name": parameter91_name, + "Parameter91.Value": parameter91_value, + "Parameter92.Name": parameter92_name, + "Parameter92.Value": parameter92_value, + "Parameter93.Name": parameter93_name, + "Parameter93.Value": parameter93_value, + "Parameter94.Name": parameter94_name, + "Parameter94.Value": parameter94_value, + "Parameter95.Name": parameter95_name, + "Parameter95.Value": parameter95_value, + "Parameter96.Name": parameter96_name, + "Parameter96.Value": parameter96_value, + "Parameter97.Name": parameter97_name, + "Parameter97.Value": parameter97_value, + "Parameter98.Name": parameter98_name, + "Parameter98.Value": parameter98_value, + "Parameter99.Name": parameter99_name, + "Parameter99.Value": parameter99_value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, + url: str, + name: Union[str, object] = values.unset, + track: Union["StreamInstance.Track", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + parameter1_name: Union[str, object] = values.unset, + parameter1_value: Union[str, object] = values.unset, + parameter2_name: Union[str, object] = values.unset, + parameter2_value: Union[str, object] = values.unset, + parameter3_name: Union[str, object] = values.unset, + parameter3_value: Union[str, object] = values.unset, + parameter4_name: Union[str, object] = values.unset, + parameter4_value: Union[str, object] = values.unset, + parameter5_name: Union[str, object] = values.unset, + parameter5_value: Union[str, object] = values.unset, + parameter6_name: Union[str, object] = values.unset, + parameter6_value: Union[str, object] = values.unset, + parameter7_name: Union[str, object] = values.unset, + parameter7_value: Union[str, object] = values.unset, + parameter8_name: Union[str, object] = values.unset, + parameter8_value: Union[str, object] = values.unset, + parameter9_name: Union[str, object] = values.unset, + parameter9_value: Union[str, object] = values.unset, + parameter10_name: Union[str, object] = values.unset, + parameter10_value: Union[str, object] = values.unset, + parameter11_name: Union[str, object] = values.unset, + parameter11_value: Union[str, object] = values.unset, + parameter12_name: Union[str, object] = values.unset, + parameter12_value: Union[str, object] = values.unset, + parameter13_name: Union[str, object] = values.unset, + parameter13_value: Union[str, object] = values.unset, + parameter14_name: Union[str, object] = values.unset, + parameter14_value: Union[str, object] = values.unset, + parameter15_name: Union[str, object] = values.unset, + parameter15_value: Union[str, object] = values.unset, + parameter16_name: Union[str, object] = values.unset, + parameter16_value: Union[str, object] = values.unset, + parameter17_name: Union[str, object] = values.unset, + parameter17_value: Union[str, object] = values.unset, + parameter18_name: Union[str, object] = values.unset, + parameter18_value: Union[str, object] = values.unset, + parameter19_name: Union[str, object] = values.unset, + parameter19_value: Union[str, object] = values.unset, + parameter20_name: Union[str, object] = values.unset, + parameter20_value: Union[str, object] = values.unset, + parameter21_name: Union[str, object] = values.unset, + parameter21_value: Union[str, object] = values.unset, + parameter22_name: Union[str, object] = values.unset, + parameter22_value: Union[str, object] = values.unset, + parameter23_name: Union[str, object] = values.unset, + parameter23_value: Union[str, object] = values.unset, + parameter24_name: Union[str, object] = values.unset, + parameter24_value: Union[str, object] = values.unset, + parameter25_name: Union[str, object] = values.unset, + parameter25_value: Union[str, object] = values.unset, + parameter26_name: Union[str, object] = values.unset, + parameter26_value: Union[str, object] = values.unset, + parameter27_name: Union[str, object] = values.unset, + parameter27_value: Union[str, object] = values.unset, + parameter28_name: Union[str, object] = values.unset, + parameter28_value: Union[str, object] = values.unset, + parameter29_name: Union[str, object] = values.unset, + parameter29_value: Union[str, object] = values.unset, + parameter30_name: Union[str, object] = values.unset, + parameter30_value: Union[str, object] = values.unset, + parameter31_name: Union[str, object] = values.unset, + parameter31_value: Union[str, object] = values.unset, + parameter32_name: Union[str, object] = values.unset, + parameter32_value: Union[str, object] = values.unset, + parameter33_name: Union[str, object] = values.unset, + parameter33_value: Union[str, object] = values.unset, + parameter34_name: Union[str, object] = values.unset, + parameter34_value: Union[str, object] = values.unset, + parameter35_name: Union[str, object] = values.unset, + parameter35_value: Union[str, object] = values.unset, + parameter36_name: Union[str, object] = values.unset, + parameter36_value: Union[str, object] = values.unset, + parameter37_name: Union[str, object] = values.unset, + parameter37_value: Union[str, object] = values.unset, + parameter38_name: Union[str, object] = values.unset, + parameter38_value: Union[str, object] = values.unset, + parameter39_name: Union[str, object] = values.unset, + parameter39_value: Union[str, object] = values.unset, + parameter40_name: Union[str, object] = values.unset, + parameter40_value: Union[str, object] = values.unset, + parameter41_name: Union[str, object] = values.unset, + parameter41_value: Union[str, object] = values.unset, + parameter42_name: Union[str, object] = values.unset, + parameter42_value: Union[str, object] = values.unset, + parameter43_name: Union[str, object] = values.unset, + parameter43_value: Union[str, object] = values.unset, + parameter44_name: Union[str, object] = values.unset, + parameter44_value: Union[str, object] = values.unset, + parameter45_name: Union[str, object] = values.unset, + parameter45_value: Union[str, object] = values.unset, + parameter46_name: Union[str, object] = values.unset, + parameter46_value: Union[str, object] = values.unset, + parameter47_name: Union[str, object] = values.unset, + parameter47_value: Union[str, object] = values.unset, + parameter48_name: Union[str, object] = values.unset, + parameter48_value: Union[str, object] = values.unset, + parameter49_name: Union[str, object] = values.unset, + parameter49_value: Union[str, object] = values.unset, + parameter50_name: Union[str, object] = values.unset, + parameter50_value: Union[str, object] = values.unset, + parameter51_name: Union[str, object] = values.unset, + parameter51_value: Union[str, object] = values.unset, + parameter52_name: Union[str, object] = values.unset, + parameter52_value: Union[str, object] = values.unset, + parameter53_name: Union[str, object] = values.unset, + parameter53_value: Union[str, object] = values.unset, + parameter54_name: Union[str, object] = values.unset, + parameter54_value: Union[str, object] = values.unset, + parameter55_name: Union[str, object] = values.unset, + parameter55_value: Union[str, object] = values.unset, + parameter56_name: Union[str, object] = values.unset, + parameter56_value: Union[str, object] = values.unset, + parameter57_name: Union[str, object] = values.unset, + parameter57_value: Union[str, object] = values.unset, + parameter58_name: Union[str, object] = values.unset, + parameter58_value: Union[str, object] = values.unset, + parameter59_name: Union[str, object] = values.unset, + parameter59_value: Union[str, object] = values.unset, + parameter60_name: Union[str, object] = values.unset, + parameter60_value: Union[str, object] = values.unset, + parameter61_name: Union[str, object] = values.unset, + parameter61_value: Union[str, object] = values.unset, + parameter62_name: Union[str, object] = values.unset, + parameter62_value: Union[str, object] = values.unset, + parameter63_name: Union[str, object] = values.unset, + parameter63_value: Union[str, object] = values.unset, + parameter64_name: Union[str, object] = values.unset, + parameter64_value: Union[str, object] = values.unset, + parameter65_name: Union[str, object] = values.unset, + parameter65_value: Union[str, object] = values.unset, + parameter66_name: Union[str, object] = values.unset, + parameter66_value: Union[str, object] = values.unset, + parameter67_name: Union[str, object] = values.unset, + parameter67_value: Union[str, object] = values.unset, + parameter68_name: Union[str, object] = values.unset, + parameter68_value: Union[str, object] = values.unset, + parameter69_name: Union[str, object] = values.unset, + parameter69_value: Union[str, object] = values.unset, + parameter70_name: Union[str, object] = values.unset, + parameter70_value: Union[str, object] = values.unset, + parameter71_name: Union[str, object] = values.unset, + parameter71_value: Union[str, object] = values.unset, + parameter72_name: Union[str, object] = values.unset, + parameter72_value: Union[str, object] = values.unset, + parameter73_name: Union[str, object] = values.unset, + parameter73_value: Union[str, object] = values.unset, + parameter74_name: Union[str, object] = values.unset, + parameter74_value: Union[str, object] = values.unset, + parameter75_name: Union[str, object] = values.unset, + parameter75_value: Union[str, object] = values.unset, + parameter76_name: Union[str, object] = values.unset, + parameter76_value: Union[str, object] = values.unset, + parameter77_name: Union[str, object] = values.unset, + parameter77_value: Union[str, object] = values.unset, + parameter78_name: Union[str, object] = values.unset, + parameter78_value: Union[str, object] = values.unset, + parameter79_name: Union[str, object] = values.unset, + parameter79_value: Union[str, object] = values.unset, + parameter80_name: Union[str, object] = values.unset, + parameter80_value: Union[str, object] = values.unset, + parameter81_name: Union[str, object] = values.unset, + parameter81_value: Union[str, object] = values.unset, + parameter82_name: Union[str, object] = values.unset, + parameter82_value: Union[str, object] = values.unset, + parameter83_name: Union[str, object] = values.unset, + parameter83_value: Union[str, object] = values.unset, + parameter84_name: Union[str, object] = values.unset, + parameter84_value: Union[str, object] = values.unset, + parameter85_name: Union[str, object] = values.unset, + parameter85_value: Union[str, object] = values.unset, + parameter86_name: Union[str, object] = values.unset, + parameter86_value: Union[str, object] = values.unset, + parameter87_name: Union[str, object] = values.unset, + parameter87_value: Union[str, object] = values.unset, + parameter88_name: Union[str, object] = values.unset, + parameter88_value: Union[str, object] = values.unset, + parameter89_name: Union[str, object] = values.unset, + parameter89_value: Union[str, object] = values.unset, + parameter90_name: Union[str, object] = values.unset, + parameter90_value: Union[str, object] = values.unset, + parameter91_name: Union[str, object] = values.unset, + parameter91_value: Union[str, object] = values.unset, + parameter92_name: Union[str, object] = values.unset, + parameter92_value: Union[str, object] = values.unset, + parameter93_name: Union[str, object] = values.unset, + parameter93_value: Union[str, object] = values.unset, + parameter94_name: Union[str, object] = values.unset, + parameter94_value: Union[str, object] = values.unset, + parameter95_name: Union[str, object] = values.unset, + parameter95_value: Union[str, object] = values.unset, + parameter96_name: Union[str, object] = values.unset, + parameter96_value: Union[str, object] = values.unset, + parameter97_name: Union[str, object] = values.unset, + parameter97_value: Union[str, object] = values.unset, + parameter98_name: Union[str, object] = values.unset, + parameter98_value: Union[str, object] = values.unset, + parameter99_name: Union[str, object] = values.unset, + parameter99_value: Union[str, object] = values.unset, + ) -> StreamInstance: + """ + Asynchronously create the StreamInstance + + :param url: Relative or absolute URL where WebSocket connection will be established. + :param name: The user-specified name of this Stream, if one was given when the Stream was created. This can be used to stop the Stream. + :param track: + :param status_callback: Absolute URL to which Twilio sends status callback HTTP requests. + :param status_callback_method: The HTTP method Twilio uses when sending `status_callback` requests. Possible values are `GET` and `POST`. Default is `POST`. + :param parameter1_name: Parameter name + :param parameter1_value: Parameter value + :param parameter2_name: Parameter name + :param parameter2_value: Parameter value + :param parameter3_name: Parameter name + :param parameter3_value: Parameter value + :param parameter4_name: Parameter name + :param parameter4_value: Parameter value + :param parameter5_name: Parameter name + :param parameter5_value: Parameter value + :param parameter6_name: Parameter name + :param parameter6_value: Parameter value + :param parameter7_name: Parameter name + :param parameter7_value: Parameter value + :param parameter8_name: Parameter name + :param parameter8_value: Parameter value + :param parameter9_name: Parameter name + :param parameter9_value: Parameter value + :param parameter10_name: Parameter name + :param parameter10_value: Parameter value + :param parameter11_name: Parameter name + :param parameter11_value: Parameter value + :param parameter12_name: Parameter name + :param parameter12_value: Parameter value + :param parameter13_name: Parameter name + :param parameter13_value: Parameter value + :param parameter14_name: Parameter name + :param parameter14_value: Parameter value + :param parameter15_name: Parameter name + :param parameter15_value: Parameter value + :param parameter16_name: Parameter name + :param parameter16_value: Parameter value + :param parameter17_name: Parameter name + :param parameter17_value: Parameter value + :param parameter18_name: Parameter name + :param parameter18_value: Parameter value + :param parameter19_name: Parameter name + :param parameter19_value: Parameter value + :param parameter20_name: Parameter name + :param parameter20_value: Parameter value + :param parameter21_name: Parameter name + :param parameter21_value: Parameter value + :param parameter22_name: Parameter name + :param parameter22_value: Parameter value + :param parameter23_name: Parameter name + :param parameter23_value: Parameter value + :param parameter24_name: Parameter name + :param parameter24_value: Parameter value + :param parameter25_name: Parameter name + :param parameter25_value: Parameter value + :param parameter26_name: Parameter name + :param parameter26_value: Parameter value + :param parameter27_name: Parameter name + :param parameter27_value: Parameter value + :param parameter28_name: Parameter name + :param parameter28_value: Parameter value + :param parameter29_name: Parameter name + :param parameter29_value: Parameter value + :param parameter30_name: Parameter name + :param parameter30_value: Parameter value + :param parameter31_name: Parameter name + :param parameter31_value: Parameter value + :param parameter32_name: Parameter name + :param parameter32_value: Parameter value + :param parameter33_name: Parameter name + :param parameter33_value: Parameter value + :param parameter34_name: Parameter name + :param parameter34_value: Parameter value + :param parameter35_name: Parameter name + :param parameter35_value: Parameter value + :param parameter36_name: Parameter name + :param parameter36_value: Parameter value + :param parameter37_name: Parameter name + :param parameter37_value: Parameter value + :param parameter38_name: Parameter name + :param parameter38_value: Parameter value + :param parameter39_name: Parameter name + :param parameter39_value: Parameter value + :param parameter40_name: Parameter name + :param parameter40_value: Parameter value + :param parameter41_name: Parameter name + :param parameter41_value: Parameter value + :param parameter42_name: Parameter name + :param parameter42_value: Parameter value + :param parameter43_name: Parameter name + :param parameter43_value: Parameter value + :param parameter44_name: Parameter name + :param parameter44_value: Parameter value + :param parameter45_name: Parameter name + :param parameter45_value: Parameter value + :param parameter46_name: Parameter name + :param parameter46_value: Parameter value + :param parameter47_name: Parameter name + :param parameter47_value: Parameter value + :param parameter48_name: Parameter name + :param parameter48_value: Parameter value + :param parameter49_name: Parameter name + :param parameter49_value: Parameter value + :param parameter50_name: Parameter name + :param parameter50_value: Parameter value + :param parameter51_name: Parameter name + :param parameter51_value: Parameter value + :param parameter52_name: Parameter name + :param parameter52_value: Parameter value + :param parameter53_name: Parameter name + :param parameter53_value: Parameter value + :param parameter54_name: Parameter name + :param parameter54_value: Parameter value + :param parameter55_name: Parameter name + :param parameter55_value: Parameter value + :param parameter56_name: Parameter name + :param parameter56_value: Parameter value + :param parameter57_name: Parameter name + :param parameter57_value: Parameter value + :param parameter58_name: Parameter name + :param parameter58_value: Parameter value + :param parameter59_name: Parameter name + :param parameter59_value: Parameter value + :param parameter60_name: Parameter name + :param parameter60_value: Parameter value + :param parameter61_name: Parameter name + :param parameter61_value: Parameter value + :param parameter62_name: Parameter name + :param parameter62_value: Parameter value + :param parameter63_name: Parameter name + :param parameter63_value: Parameter value + :param parameter64_name: Parameter name + :param parameter64_value: Parameter value + :param parameter65_name: Parameter name + :param parameter65_value: Parameter value + :param parameter66_name: Parameter name + :param parameter66_value: Parameter value + :param parameter67_name: Parameter name + :param parameter67_value: Parameter value + :param parameter68_name: Parameter name + :param parameter68_value: Parameter value + :param parameter69_name: Parameter name + :param parameter69_value: Parameter value + :param parameter70_name: Parameter name + :param parameter70_value: Parameter value + :param parameter71_name: Parameter name + :param parameter71_value: Parameter value + :param parameter72_name: Parameter name + :param parameter72_value: Parameter value + :param parameter73_name: Parameter name + :param parameter73_value: Parameter value + :param parameter74_name: Parameter name + :param parameter74_value: Parameter value + :param parameter75_name: Parameter name + :param parameter75_value: Parameter value + :param parameter76_name: Parameter name + :param parameter76_value: Parameter value + :param parameter77_name: Parameter name + :param parameter77_value: Parameter value + :param parameter78_name: Parameter name + :param parameter78_value: Parameter value + :param parameter79_name: Parameter name + :param parameter79_value: Parameter value + :param parameter80_name: Parameter name + :param parameter80_value: Parameter value + :param parameter81_name: Parameter name + :param parameter81_value: Parameter value + :param parameter82_name: Parameter name + :param parameter82_value: Parameter value + :param parameter83_name: Parameter name + :param parameter83_value: Parameter value + :param parameter84_name: Parameter name + :param parameter84_value: Parameter value + :param parameter85_name: Parameter name + :param parameter85_value: Parameter value + :param parameter86_name: Parameter name + :param parameter86_value: Parameter value + :param parameter87_name: Parameter name + :param parameter87_value: Parameter value + :param parameter88_name: Parameter name + :param parameter88_value: Parameter value + :param parameter89_name: Parameter name + :param parameter89_value: Parameter value + :param parameter90_name: Parameter name + :param parameter90_value: Parameter value + :param parameter91_name: Parameter name + :param parameter91_value: Parameter value + :param parameter92_name: Parameter name + :param parameter92_value: Parameter value + :param parameter93_name: Parameter name + :param parameter93_value: Parameter value + :param parameter94_name: Parameter name + :param parameter94_value: Parameter value + :param parameter95_name: Parameter name + :param parameter95_value: Parameter value + :param parameter96_name: Parameter name + :param parameter96_value: Parameter value + :param parameter97_name: Parameter name + :param parameter97_value: Parameter value + :param parameter98_name: Parameter name + :param parameter98_value: Parameter value + :param parameter99_name: Parameter name + :param parameter99_value: Parameter value + + :returns: The created StreamInstance + """ + + data = values.of( + { + "Url": url, + "Name": name, + "Track": track, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Parameter1.Name": parameter1_name, + "Parameter1.Value": parameter1_value, + "Parameter2.Name": parameter2_name, + "Parameter2.Value": parameter2_value, + "Parameter3.Name": parameter3_name, + "Parameter3.Value": parameter3_value, + "Parameter4.Name": parameter4_name, + "Parameter4.Value": parameter4_value, + "Parameter5.Name": parameter5_name, + "Parameter5.Value": parameter5_value, + "Parameter6.Name": parameter6_name, + "Parameter6.Value": parameter6_value, + "Parameter7.Name": parameter7_name, + "Parameter7.Value": parameter7_value, + "Parameter8.Name": parameter8_name, + "Parameter8.Value": parameter8_value, + "Parameter9.Name": parameter9_name, + "Parameter9.Value": parameter9_value, + "Parameter10.Name": parameter10_name, + "Parameter10.Value": parameter10_value, + "Parameter11.Name": parameter11_name, + "Parameter11.Value": parameter11_value, + "Parameter12.Name": parameter12_name, + "Parameter12.Value": parameter12_value, + "Parameter13.Name": parameter13_name, + "Parameter13.Value": parameter13_value, + "Parameter14.Name": parameter14_name, + "Parameter14.Value": parameter14_value, + "Parameter15.Name": parameter15_name, + "Parameter15.Value": parameter15_value, + "Parameter16.Name": parameter16_name, + "Parameter16.Value": parameter16_value, + "Parameter17.Name": parameter17_name, + "Parameter17.Value": parameter17_value, + "Parameter18.Name": parameter18_name, + "Parameter18.Value": parameter18_value, + "Parameter19.Name": parameter19_name, + "Parameter19.Value": parameter19_value, + "Parameter20.Name": parameter20_name, + "Parameter20.Value": parameter20_value, + "Parameter21.Name": parameter21_name, + "Parameter21.Value": parameter21_value, + "Parameter22.Name": parameter22_name, + "Parameter22.Value": parameter22_value, + "Parameter23.Name": parameter23_name, + "Parameter23.Value": parameter23_value, + "Parameter24.Name": parameter24_name, + "Parameter24.Value": parameter24_value, + "Parameter25.Name": parameter25_name, + "Parameter25.Value": parameter25_value, + "Parameter26.Name": parameter26_name, + "Parameter26.Value": parameter26_value, + "Parameter27.Name": parameter27_name, + "Parameter27.Value": parameter27_value, + "Parameter28.Name": parameter28_name, + "Parameter28.Value": parameter28_value, + "Parameter29.Name": parameter29_name, + "Parameter29.Value": parameter29_value, + "Parameter30.Name": parameter30_name, + "Parameter30.Value": parameter30_value, + "Parameter31.Name": parameter31_name, + "Parameter31.Value": parameter31_value, + "Parameter32.Name": parameter32_name, + "Parameter32.Value": parameter32_value, + "Parameter33.Name": parameter33_name, + "Parameter33.Value": parameter33_value, + "Parameter34.Name": parameter34_name, + "Parameter34.Value": parameter34_value, + "Parameter35.Name": parameter35_name, + "Parameter35.Value": parameter35_value, + "Parameter36.Name": parameter36_name, + "Parameter36.Value": parameter36_value, + "Parameter37.Name": parameter37_name, + "Parameter37.Value": parameter37_value, + "Parameter38.Name": parameter38_name, + "Parameter38.Value": parameter38_value, + "Parameter39.Name": parameter39_name, + "Parameter39.Value": parameter39_value, + "Parameter40.Name": parameter40_name, + "Parameter40.Value": parameter40_value, + "Parameter41.Name": parameter41_name, + "Parameter41.Value": parameter41_value, + "Parameter42.Name": parameter42_name, + "Parameter42.Value": parameter42_value, + "Parameter43.Name": parameter43_name, + "Parameter43.Value": parameter43_value, + "Parameter44.Name": parameter44_name, + "Parameter44.Value": parameter44_value, + "Parameter45.Name": parameter45_name, + "Parameter45.Value": parameter45_value, + "Parameter46.Name": parameter46_name, + "Parameter46.Value": parameter46_value, + "Parameter47.Name": parameter47_name, + "Parameter47.Value": parameter47_value, + "Parameter48.Name": parameter48_name, + "Parameter48.Value": parameter48_value, + "Parameter49.Name": parameter49_name, + "Parameter49.Value": parameter49_value, + "Parameter50.Name": parameter50_name, + "Parameter50.Value": parameter50_value, + "Parameter51.Name": parameter51_name, + "Parameter51.Value": parameter51_value, + "Parameter52.Name": parameter52_name, + "Parameter52.Value": parameter52_value, + "Parameter53.Name": parameter53_name, + "Parameter53.Value": parameter53_value, + "Parameter54.Name": parameter54_name, + "Parameter54.Value": parameter54_value, + "Parameter55.Name": parameter55_name, + "Parameter55.Value": parameter55_value, + "Parameter56.Name": parameter56_name, + "Parameter56.Value": parameter56_value, + "Parameter57.Name": parameter57_name, + "Parameter57.Value": parameter57_value, + "Parameter58.Name": parameter58_name, + "Parameter58.Value": parameter58_value, + "Parameter59.Name": parameter59_name, + "Parameter59.Value": parameter59_value, + "Parameter60.Name": parameter60_name, + "Parameter60.Value": parameter60_value, + "Parameter61.Name": parameter61_name, + "Parameter61.Value": parameter61_value, + "Parameter62.Name": parameter62_name, + "Parameter62.Value": parameter62_value, + "Parameter63.Name": parameter63_name, + "Parameter63.Value": parameter63_value, + "Parameter64.Name": parameter64_name, + "Parameter64.Value": parameter64_value, + "Parameter65.Name": parameter65_name, + "Parameter65.Value": parameter65_value, + "Parameter66.Name": parameter66_name, + "Parameter66.Value": parameter66_value, + "Parameter67.Name": parameter67_name, + "Parameter67.Value": parameter67_value, + "Parameter68.Name": parameter68_name, + "Parameter68.Value": parameter68_value, + "Parameter69.Name": parameter69_name, + "Parameter69.Value": parameter69_value, + "Parameter70.Name": parameter70_name, + "Parameter70.Value": parameter70_value, + "Parameter71.Name": parameter71_name, + "Parameter71.Value": parameter71_value, + "Parameter72.Name": parameter72_name, + "Parameter72.Value": parameter72_value, + "Parameter73.Name": parameter73_name, + "Parameter73.Value": parameter73_value, + "Parameter74.Name": parameter74_name, + "Parameter74.Value": parameter74_value, + "Parameter75.Name": parameter75_name, + "Parameter75.Value": parameter75_value, + "Parameter76.Name": parameter76_name, + "Parameter76.Value": parameter76_value, + "Parameter77.Name": parameter77_name, + "Parameter77.Value": parameter77_value, + "Parameter78.Name": parameter78_name, + "Parameter78.Value": parameter78_value, + "Parameter79.Name": parameter79_name, + "Parameter79.Value": parameter79_value, + "Parameter80.Name": parameter80_name, + "Parameter80.Value": parameter80_value, + "Parameter81.Name": parameter81_name, + "Parameter81.Value": parameter81_value, + "Parameter82.Name": parameter82_name, + "Parameter82.Value": parameter82_value, + "Parameter83.Name": parameter83_name, + "Parameter83.Value": parameter83_value, + "Parameter84.Name": parameter84_name, + "Parameter84.Value": parameter84_value, + "Parameter85.Name": parameter85_name, + "Parameter85.Value": parameter85_value, + "Parameter86.Name": parameter86_name, + "Parameter86.Value": parameter86_value, + "Parameter87.Name": parameter87_name, + "Parameter87.Value": parameter87_value, + "Parameter88.Name": parameter88_name, + "Parameter88.Value": parameter88_value, + "Parameter89.Name": parameter89_name, + "Parameter89.Value": parameter89_value, + "Parameter90.Name": parameter90_name, + "Parameter90.Value": parameter90_value, + "Parameter91.Name": parameter91_name, + "Parameter91.Value": parameter91_value, + "Parameter92.Name": parameter92_name, + "Parameter92.Value": parameter92_value, + "Parameter93.Name": parameter93_name, + "Parameter93.Value": parameter93_value, + "Parameter94.Name": parameter94_name, + "Parameter94.Value": parameter94_value, + "Parameter95.Name": parameter95_name, + "Parameter95.Value": parameter95_value, + "Parameter96.Name": parameter96_name, + "Parameter96.Value": parameter96_value, + "Parameter97.Name": parameter97_name, + "Parameter97.Value": parameter97_value, + "Parameter98.Name": parameter98_name, + "Parameter98.Value": parameter98_value, + "Parameter99.Name": parameter99_name, + "Parameter99.Value": parameter99_value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def get(self, sid: str) -> StreamContext: + """ + Constructs a StreamContext + + :param sid: The SID or the `name` of the Stream resource to be stopped + """ + return StreamContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> StreamContext: + """ + Constructs a StreamContext + + :param sid: The SID or the `name` of the Stream resource to be stopped + """ + return StreamContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message.py new file mode 100644 index 0000000..0e9d1f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message.py @@ -0,0 +1,153 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UserDefinedMessageInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created User Defined Message. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the User Defined Message is associated with. + :ivar sid: The SID that uniquely identifies this User Defined Message. + :ivar date_created: The date that this User Defined Message was created, given in RFC 2822 format. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], account_sid: str, call_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserDefinedMessageList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the UserDefinedMessageList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created User Defined Message. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the User Defined Message is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = ( + "/Accounts/{account_sid}/Calls/{call_sid}/UserDefinedMessages.json".format( + **self._solution + ) + ) + + def create( + self, content: str, idempotency_key: Union[str, object] = values.unset + ) -> UserDefinedMessageInstance: + """ + Create the UserDefinedMessageInstance + + :param content: The User Defined Message in the form of URL-encoded JSON string. + :param idempotency_key: A unique string value to identify API call. This should be a unique string value per API call and can be a randomly generated. + + :returns: The created UserDefinedMessageInstance + """ + + data = values.of( + { + "Content": content, + "IdempotencyKey": idempotency_key, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return UserDefinedMessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, content: str, idempotency_key: Union[str, object] = values.unset + ) -> UserDefinedMessageInstance: + """ + Asynchronously create the UserDefinedMessageInstance + + :param content: The User Defined Message in the form of URL-encoded JSON string. + :param idempotency_key: A unique string value to identify API call. This should be a unique string value per API call and can be a randomly generated. + + :returns: The created UserDefinedMessageInstance + """ + + data = values.of( + { + "Content": content, + "IdempotencyKey": idempotency_key, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserDefinedMessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message_subscription.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message_subscription.py new file mode 100644 index 0000000..a0b7c18 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/call/user_defined_message_subscription.py @@ -0,0 +1,292 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UserDefinedMessageSubscriptionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that subscribed to the User Defined Messages. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the User Defined Message Subscription is associated with. This refers to the Call SID that is producing the User Defined Messages. + :ivar sid: The SID that uniquely identifies this User Defined Message Subscription. + :ivar date_created: The date that this User Defined Message Subscription was created, given in RFC 2822 format. + :ivar uri: The URI of the User Defined Message Subscription Resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + call_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserDefinedMessageSubscriptionContext] = None + + @property + def _proxy(self) -> "UserDefinedMessageSubscriptionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserDefinedMessageSubscriptionContext for this UserDefinedMessageSubscriptionInstance + """ + if self._context is None: + self._context = UserDefinedMessageSubscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserDefinedMessageSubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserDefinedMessageSubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class UserDefinedMessageSubscriptionContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, call_sid: str, sid: str): + """ + Initialize the UserDefinedMessageSubscriptionContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that subscribed to the User Defined Messages. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the User Defined Message Subscription is associated with. This refers to the Call SID that is producing the User Defined Messages. + :param sid: The SID that uniquely identifies this User Defined Message Subscription. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/UserDefinedMessageSubscriptions/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UserDefinedMessageSubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserDefinedMessageSubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class UserDefinedMessageSubscriptionList(ListResource): + + def __init__(self, version: Version, account_sid: str, call_sid: str): + """ + Initialize the UserDefinedMessageSubscriptionList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that subscribed to the User Defined Messages. + :param call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the User Defined Messages subscription is associated with. This refers to the Call SID that is producing the user defined messages. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Calls/{call_sid}/UserDefinedMessageSubscriptions.json".format( + **self._solution + ) + + def create( + self, + callback: str, + idempotency_key: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + ) -> UserDefinedMessageSubscriptionInstance: + """ + Create the UserDefinedMessageSubscriptionInstance + + :param callback: The URL we should call using the `method` to send user defined events to your application. URLs must contain a valid hostname (underscores are not permitted). + :param idempotency_key: A unique string value to identify API call. This should be a unique string value per API call and can be a randomly generated. + :param method: The HTTP method Twilio will use when requesting the above `Url`. Either `GET` or `POST`. Default is `POST`. + + :returns: The created UserDefinedMessageSubscriptionInstance + """ + + data = values.of( + { + "Callback": callback, + "IdempotencyKey": idempotency_key, + "Method": method, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return UserDefinedMessageSubscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + async def create_async( + self, + callback: str, + idempotency_key: Union[str, object] = values.unset, + method: Union[str, object] = values.unset, + ) -> UserDefinedMessageSubscriptionInstance: + """ + Asynchronously create the UserDefinedMessageSubscriptionInstance + + :param callback: The URL we should call using the `method` to send user defined events to your application. URLs must contain a valid hostname (underscores are not permitted). + :param idempotency_key: A unique string value to identify API call. This should be a unique string value per API call and can be a randomly generated. + :param method: The HTTP method Twilio will use when requesting the above `Url`. Either `GET` or `POST`. Default is `POST`. + + :returns: The created UserDefinedMessageSubscriptionInstance + """ + + data = values.of( + { + "Callback": callback, + "IdempotencyKey": idempotency_key, + "Method": method, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserDefinedMessageSubscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + ) + + def get(self, sid: str) -> UserDefinedMessageSubscriptionContext: + """ + Constructs a UserDefinedMessageSubscriptionContext + + :param sid: The SID that uniquely identifies this User Defined Message Subscription. + """ + return UserDefinedMessageSubscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> UserDefinedMessageSubscriptionContext: + """ + Constructs a UserDefinedMessageSubscriptionContext + + :param sid: The SID that uniquely identifies this User Defined Message Subscription. + """ + return UserDefinedMessageSubscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + call_sid=self._solution["call_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__init__.py new file mode 100644 index 0000000..074ce8a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__init__.py @@ -0,0 +1,769 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.conference.participant import ParticipantList +from twilio.rest.api.v2010.account.conference.recording import RecordingList + + +class ConferenceInstance(InstanceResource): + + class ReasonConferenceEnded(object): + CONFERENCE_ENDED_VIA_API = "conference-ended-via-api" + PARTICIPANT_WITH_END_CONFERENCE_ON_EXIT_LEFT = ( + "participant-with-end-conference-on-exit-left" + ) + PARTICIPANT_WITH_END_CONFERENCE_ON_EXIT_KICKED = ( + "participant-with-end-conference-on-exit-kicked" + ) + LAST_PARTICIPANT_KICKED = "last-participant-kicked" + LAST_PARTICIPANT_LEFT = "last-participant-left" + + class Status(object): + INIT = "init" + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + + class UpdateStatus(object): + COMPLETED = "completed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Conference resource. + :ivar date_created: The date and time in UTC that this resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in UTC that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar api_version: The API version used to create this conference. + :ivar friendly_name: A string that you assigned to describe this conference room. Maximum length is 128 characters. + :ivar region: A string that represents the Twilio Region where the conference audio was mixed. May be `us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, and `jp1`. Basic conference audio will always be mixed in `us1`. Global Conference audio will be mixed nearest to the majority of participants. + :ivar sid: The unique, Twilio-provided string used to identify this Conference resource. + :ivar status: + :ivar uri: The URI of this resource, relative to `https://api.twilio.com`. + :ivar subresource_uris: A list of related resources identified by their URIs relative to `https://api.twilio.com`. + :ivar reason_conference_ended: + :ivar call_sid_ending_conference: The call SID that caused the conference to end. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.api_version: Optional[str] = payload.get("api_version") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.region: Optional[str] = payload.get("region") + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["ConferenceInstance.Status"] = payload.get("status") + self.uri: Optional[str] = payload.get("uri") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.reason_conference_ended: Optional[ + "ConferenceInstance.ReasonConferenceEnded" + ] = payload.get("reason_conference_ended") + self.call_sid_ending_conference: Optional[str] = payload.get( + "call_sid_ending_conference" + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[ConferenceContext] = None + + @property + def _proxy(self) -> "ConferenceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConferenceContext for this ConferenceInstance + """ + if self._context is None: + self._context = ConferenceContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ConferenceInstance": + """ + Fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConferenceInstance": + """ + Asynchronous coroutine to fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: Union["ConferenceInstance.UpdateStatus", object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + ) -> "ConferenceInstance": + """ + Update the ConferenceInstance + + :param status: + :param announce_url: The URL we should call to announce something into the conference. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method used to call `announce_url`. Can be: `GET` or `POST` and the default is `POST` + + :returns: The updated ConferenceInstance + """ + return self._proxy.update( + status=status, + announce_url=announce_url, + announce_method=announce_method, + ) + + async def update_async( + self, + status: Union["ConferenceInstance.UpdateStatus", object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + ) -> "ConferenceInstance": + """ + Asynchronous coroutine to update the ConferenceInstance + + :param status: + :param announce_url: The URL we should call to announce something into the conference. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method used to call `announce_url`. Can be: `GET` or `POST` and the default is `POST` + + :returns: The updated ConferenceInstance + """ + return await self._proxy.update_async( + status=status, + announce_url=announce_url, + announce_method=announce_method, + ) + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + return self._proxy.recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferenceContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the ConferenceContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Conference resource(s) to update. + :param sid: The Twilio-provided string that uniquely identifies the Conference resource to update + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Conferences/{sid}.json".format( + **self._solution + ) + + self._participants: Optional[ParticipantList] = None + self._recordings: Optional[RecordingList] = None + + def fetch(self) -> ConferenceInstance: + """ + Fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConferenceInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConferenceInstance: + """ + Asynchronous coroutine to fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConferenceInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + status: Union["ConferenceInstance.UpdateStatus", object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + ) -> ConferenceInstance: + """ + Update the ConferenceInstance + + :param status: + :param announce_url: The URL we should call to announce something into the conference. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method used to call `announce_url`. Can be: `GET` or `POST` and the default is `POST` + + :returns: The updated ConferenceInstance + """ + data = values.of( + { + "Status": status, + "AnnounceUrl": announce_url, + "AnnounceMethod": announce_method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConferenceInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + status: Union["ConferenceInstance.UpdateStatus", object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + ) -> ConferenceInstance: + """ + Asynchronous coroutine to update the ConferenceInstance + + :param status: + :param announce_url: The URL we should call to announce something into the conference. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method used to call `announce_url`. Can be: `GET` or `POST` and the default is `POST` + + :returns: The updated ConferenceInstance + """ + data = values.of( + { + "Status": status, + "AnnounceUrl": announce_url, + "AnnounceMethod": announce_method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConferenceInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._participants + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + if self._recordings is None: + self._recordings = RecordingList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferencePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConferenceInstance: + """ + Build an instance of ConferenceInstance + + :param payload: Payload response from the API + """ + return ConferenceInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConferenceList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ConferenceList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Conference resource(s) to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Conferences.json".format(**self._solution) + + def stream( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConferenceInstance]: + """ + Streams ConferenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param str friendly_name: The string that identifies the Conference resources to read. + :param "ConferenceInstance.Status" status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + date_updated=date_updated, + date_updated_before=date_updated_before, + date_updated_after=date_updated_after, + friendly_name=friendly_name, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConferenceInstance]: + """ + Asynchronously streams ConferenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param str friendly_name: The string that identifies the Conference resources to read. + :param "ConferenceInstance.Status" status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + date_updated=date_updated, + date_updated_before=date_updated_before, + date_updated_after=date_updated_after, + friendly_name=friendly_name, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceInstance]: + """ + Lists ConferenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param str friendly_name: The string that identifies the Conference resources to read. + :param "ConferenceInstance.Status" status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + date_updated=date_updated, + date_updated_before=date_updated_before, + date_updated_after=date_updated_after, + friendly_name=friendly_name, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceInstance]: + """ + Asynchronously lists ConferenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param str friendly_name: The string that identifies the Conference resources to read. + :param "ConferenceInstance.Status" status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + date_updated=date_updated, + date_updated_before=date_updated_before, + date_updated_after=date_updated_after, + friendly_name=friendly_name, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferencePage: + """ + Retrieve a single page of ConferenceInstance records from the API. + Request is executed immediately + + :param date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param friendly_name: The string that identifies the Conference resources to read. + :param status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "DateUpdated": serialize.iso8601_date(date_updated), + "DateUpdated<": serialize.iso8601_date(date_updated_before), + "DateUpdated>": serialize.iso8601_date(date_updated_after), + "FriendlyName": friendly_name, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConferencePage(self._version, response, self._solution) + + async def page_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + date_updated: Union[date, object] = values.unset, + date_updated_before: Union[date, object] = values.unset, + date_updated_after: Union[date, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union["ConferenceInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferencePage: + """ + Asynchronously retrieve a single page of ConferenceInstance records from the API. + Request is executed immediately + + :param date_created: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_created_before: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_created_after: Only include conferences that were created on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read conferences that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read conferences that were created on or after midnight of this date. + :param date_updated: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date_updated_before: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param date_updated_after: Only include conferences that were last updated on this date. Specify a date as `YYYY-MM-DD` in UTC, for example: `2009-07-06`, to read only conferences that were last updated on this date. You can also specify an inequality, such as `DateUpdated<=YYYY-MM-DD`, to read conferences that were last updated on or before midnight of this date, and `DateUpdated>=YYYY-MM-DD` to read conferences that were last updated on or after midnight of this date. + :param friendly_name: The string that identifies the Conference resources to read. + :param status: The status of the resources to read. Can be: `init`, `in-progress`, or `completed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "DateUpdated": serialize.iso8601_date(date_updated), + "DateUpdated<": serialize.iso8601_date(date_updated_before), + "DateUpdated>": serialize.iso8601_date(date_updated_after), + "FriendlyName": friendly_name, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConferencePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConferencePage: + """ + Retrieve a specific page of ConferenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConferencePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConferencePage: + """ + Asynchronously retrieve a specific page of ConferenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConferencePage(self._version, response, self._solution) + + def get(self, sid: str) -> ConferenceContext: + """ + Constructs a ConferenceContext + + :param sid: The Twilio-provided string that uniquely identifies the Conference resource to update + """ + return ConferenceContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ConferenceContext: + """ + Constructs a ConferenceContext + + :param sid: The Twilio-provided string that uniquely identifies the Conference resource to update + """ + return ConferenceContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fc8593c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/participant.cpython-310.pyc new file mode 100644 index 0000000..c38298c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/recording.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/recording.cpython-310.pyc new file mode 100644 index 0000000..317292a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/__pycache__/recording.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/participant.py new file mode 100644 index 0000000..2c7ae59 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/participant.py @@ -0,0 +1,1171 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantInstance(InstanceResource): + + class Status(object): + QUEUED = "queued" + CONNECTING = "connecting" + RINGING = "ringing" + CONNECTED = "connected" + COMPLETE = "complete" + FAILED = "failed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Participant resource. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Participant resource is associated with. + :ivar label: The user-specified label of this participant, if one was given when the participant was created. This may be used to fetch, update or delete the participant. + :ivar call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + :ivar coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :ivar conference_sid: The SID of the conference the participant is in. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar end_conference_on_exit: Whether the conference ends when the participant leaves. Can be: `true` or `false` and the default is `false`. If `true`, the conference ends and all other participants drop out when the participant leaves. + :ivar muted: Whether the participant is muted. Can be `true` or `false`. + :ivar hold: Whether the participant is on hold. Can be `true` or `false`. + :ivar start_conference_on_enter: Whether the conference starts when the participant joins the conference, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :ivar status: + :ivar queue_time: The wait time in milliseconds before participant's call is placed. Only available in the response to a create participant request. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + conference_sid: str, + call_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.label: Optional[str] = payload.get("label") + self.call_sid_to_coach: Optional[str] = payload.get("call_sid_to_coach") + self.coaching: Optional[bool] = payload.get("coaching") + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.end_conference_on_exit: Optional[bool] = payload.get( + "end_conference_on_exit" + ) + self.muted: Optional[bool] = payload.get("muted") + self.hold: Optional[bool] = payload.get("hold") + self.start_conference_on_enter: Optional[bool] = payload.get( + "start_conference_on_enter" + ) + self.status: Optional["ParticipantInstance.Status"] = payload.get("status") + self.queue_time: Optional[str] = payload.get("queue_time") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + "call_sid": call_sid or self.call_sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=self._solution["call_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + hold_url: Union[str, object] = values.unset, + hold_method: Union[str, object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + beep_on_exit: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Update the ParticipantInstance + + :param muted: Whether the participant should be muted. Can be `true` or `false`. `true` will mute the participant, and `false` will un-mute them. Anything value other than `true` or `false` is interpreted as `false`. + :param hold: Whether the participant should be on hold. Can be: `true` or `false`. `true` puts the participant on hold, and `false` lets them rejoin the conference. + :param hold_url: The URL we call using the `hold_method` for music that plays when the participant is on hold. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param hold_method: The HTTP method we should use to call `hold_url`. Can be: `GET` or `POST` and the default is `GET`. + :param announce_url: The URL we call using the `announce_method` for an announcement to the participant. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method we should use to call `announce_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param wait_url: The URL we call using the `wait_method` for the music to play while participants are waiting for the conference to start. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param beep_on_exit: Whether to play a notification beep to the conference when the participant exits. Can be: `true` or `false`. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + + :returns: The updated ParticipantInstance + """ + return self._proxy.update( + muted=muted, + hold=hold, + hold_url=hold_url, + hold_method=hold_method, + announce_url=announce_url, + announce_method=announce_method, + wait_url=wait_url, + wait_method=wait_method, + beep_on_exit=beep_on_exit, + end_conference_on_exit=end_conference_on_exit, + coaching=coaching, + call_sid_to_coach=call_sid_to_coach, + ) + + async def update_async( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + hold_url: Union[str, object] = values.unset, + hold_method: Union[str, object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + beep_on_exit: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Asynchronous coroutine to update the ParticipantInstance + + :param muted: Whether the participant should be muted. Can be `true` or `false`. `true` will mute the participant, and `false` will un-mute them. Anything value other than `true` or `false` is interpreted as `false`. + :param hold: Whether the participant should be on hold. Can be: `true` or `false`. `true` puts the participant on hold, and `false` lets them rejoin the conference. + :param hold_url: The URL we call using the `hold_method` for music that plays when the participant is on hold. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param hold_method: The HTTP method we should use to call `hold_url`. Can be: `GET` or `POST` and the default is `GET`. + :param announce_url: The URL we call using the `announce_method` for an announcement to the participant. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method we should use to call `announce_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param wait_url: The URL we call using the `wait_method` for the music to play while participants are waiting for the conference to start. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param beep_on_exit: Whether to play a notification beep to the conference when the participant exits. Can be: `true` or `false`. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + + :returns: The updated ParticipantInstance + """ + return await self._proxy.update_async( + muted=muted, + hold=hold, + hold_url=hold_url, + hold_method=hold_method, + announce_url=announce_url, + announce_method=announce_method, + wait_url=wait_url, + wait_method=wait_method, + beep_on_exit=beep_on_exit, + end_conference_on_exit=end_conference_on_exit, + coaching=coaching, + call_sid_to_coach=call_sid_to_coach, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, conference_sid: str, call_sid: str + ): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Participant resources to update. + :param conference_sid: The SID of the conference with the participant to update. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID or label of the participant to update. Non URL safe characters in a label must be percent encoded, for example, a space character is represented as %20. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + "call_sid": call_sid, + } + self._uri = "/Accounts/{account_sid}/Conferences/{conference_sid}/Participants/{call_sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=self._solution["call_sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=self._solution["call_sid"], + ) + + def update( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + hold_url: Union[str, object] = values.unset, + hold_method: Union[str, object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + beep_on_exit: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Update the ParticipantInstance + + :param muted: Whether the participant should be muted. Can be `true` or `false`. `true` will mute the participant, and `false` will un-mute them. Anything value other than `true` or `false` is interpreted as `false`. + :param hold: Whether the participant should be on hold. Can be: `true` or `false`. `true` puts the participant on hold, and `false` lets them rejoin the conference. + :param hold_url: The URL we call using the `hold_method` for music that plays when the participant is on hold. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param hold_method: The HTTP method we should use to call `hold_url`. Can be: `GET` or `POST` and the default is `GET`. + :param announce_url: The URL we call using the `announce_method` for an announcement to the participant. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method we should use to call `announce_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param wait_url: The URL we call using the `wait_method` for the music to play while participants are waiting for the conference to start. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param beep_on_exit: Whether to play a notification beep to the conference when the participant exits. Can be: `true` or `false`. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "Muted": serialize.boolean_to_string(muted), + "Hold": serialize.boolean_to_string(hold), + "HoldUrl": hold_url, + "HoldMethod": hold_method, + "AnnounceUrl": announce_url, + "AnnounceMethod": announce_method, + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "BeepOnExit": serialize.boolean_to_string(beep_on_exit), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "Coaching": serialize.boolean_to_string(coaching), + "CallSidToCoach": call_sid_to_coach, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=self._solution["call_sid"], + ) + + async def update_async( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + hold_url: Union[str, object] = values.unset, + hold_method: Union[str, object] = values.unset, + announce_url: Union[str, object] = values.unset, + announce_method: Union[str, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + beep_on_exit: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronous coroutine to update the ParticipantInstance + + :param muted: Whether the participant should be muted. Can be `true` or `false`. `true` will mute the participant, and `false` will un-mute them. Anything value other than `true` or `false` is interpreted as `false`. + :param hold: Whether the participant should be on hold. Can be: `true` or `false`. `true` puts the participant on hold, and `false` lets them rejoin the conference. + :param hold_url: The URL we call using the `hold_method` for music that plays when the participant is on hold. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param hold_method: The HTTP method we should use to call `hold_url`. Can be: `GET` or `POST` and the default is `GET`. + :param announce_url: The URL we call using the `announce_method` for an announcement to the participant. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. + :param announce_method: The HTTP method we should use to call `announce_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param wait_url: The URL we call using the `wait_method` for the music to play while participants are waiting for the conference to start. The URL may return an MP3 file, a WAV file, or a TwiML document that contains ``, ``, ``, or `` verbs. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param beep_on_exit: Whether to play a notification beep to the conference when the participant exits. Can be: `true` or `false`. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "Muted": serialize.boolean_to_string(muted), + "Hold": serialize.boolean_to_string(hold), + "HoldUrl": hold_url, + "HoldMethod": hold_method, + "AnnounceUrl": announce_url, + "AnnounceMethod": announce_method, + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "BeepOnExit": serialize.boolean_to_string(beep_on_exit), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "Coaching": serialize.boolean_to_string(coaching), + "CallSidToCoach": call_sid_to_coach, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, account_sid: str, conference_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Participant resources to read. + :param conference_sid: The SID of the conference with the participants to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + } + self._uri = "/Accounts/{account_sid}/Conferences/{conference_sid}/Participants.json".format( + **self._solution + ) + + def create( + self, + from_: str, + to: str, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[List[str], object] = values.unset, + label: Union[str, object] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[List[str], object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + recording_status_callback_event: Union[List[str], object] = values.unset, + conference_recording_status_callback_event: Union[ + List[str], object + ] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + byoc: Union[str, object] = values.unset, + caller_id: Union[str, object] = values.unset, + call_reason: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + machine_detection: Union[str, object] = values.unset, + machine_detection_timeout: Union[int, object] = values.unset, + machine_detection_speech_threshold: Union[int, object] = values.unset, + machine_detection_speech_end_threshold: Union[int, object] = values.unset, + machine_detection_silence_timeout: Union[int, object] = values.unset, + amd_status_callback: Union[str, object] = values.unset, + amd_status_callback_method: Union[str, object] = values.unset, + trim: Union[str, object] = values.unset, + call_token: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Create the ParticipantInstance + + :param from_: The phone number, Client identifier, or username portion of SIP address that made this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). Client identifiers are formatted `client:name`. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `from` must also be a phone number. If `to` is sip address, this value of `from` should be a username portion to be used to populate the P-Asserted-Identity header that is passed to the SIP endpoint. + :param to: The phone number, SIP address, or Client identifier that received this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). SIP addresses are formatted as `sip:name@company.com`. Client identifiers are formatted `client:name`. [Custom parameters](https://www.twilio.com/docs/voice/api/conference-participant-resource#custom-parameters) may also be specified. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` and `POST` and defaults to `POST`. + :param status_callback_event: The conference state changes that should generate a call to `status_callback`. Can be: `initiated`, `ringing`, `answered`, and `completed`. Separate multiple values with a space. The default value is `completed`. + :param label: A label for this participant. If one is supplied, it may subsequently be used to fetch, update or delete the participant. + :param timeout: The number of seconds that we should allow the phone to ring before assuming there is no answer. Can be an integer between `5` and `600`, inclusive. The default value is `60`. We always add a 5-second timeout buffer to outgoing calls, so value of 10 would result in an actual timeout that was closer to 15 seconds. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Can be `true` or `false` and the default is `false`. + :param beep: Whether to play a notification beep to the conference when the participant joins. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. Can be: `true` or `false` and defaults to `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_record: Whether to record the conference the participant is joining. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from the conference recording. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference state changes that should generate a call to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `modify`, `speaker`, and `announcement`. Separate multiple values with a space. Defaults to `start end`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param recording_status_callback_event: The recording state changes that should generate a call to `recording_status_callback`. Can be: `started`, `in-progress`, `paused`, `resumed`, `stopped`, `completed`, `failed`, and `absent`. Separate multiple values with a space, ex: `'in-progress completed failed'`. + :param conference_recording_status_callback_event: The conference recording state changes that generate a call to `conference_recording_status_callback`. Can be: `in-progress`, `completed`, `failed`, and `absent`. Separate multiple values with a space, ex: `'in-progress completed failed'` + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + :param jitter_buffer_size: Jitter buffer size for the connecting participant. Twilio will use this setting to apply Jitter Buffer before participant's audio is mixed into the conference. Can be: `off`, `small`, `medium`, and `large`. Default to `large`. + :param byoc: The SID of a BYOC (Bring Your Own Carrier) trunk to route this call with. Note that `byoc` is only meaningful when `to` is a phone number; it will otherwise be ignored. (Beta) + :param caller_id: The phone number, Client identifier, or username portion of SIP address that made this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). Client identifiers are formatted `client:name`. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `callerId` must also be a phone number. If `to` is sip address, this value of `callerId` should be a username portion to be used to populate the From header that is passed to the SIP endpoint. + :param call_reason: The Reason for the outgoing call. Use it to specify the purpose of the call that is presented on the called party's phone. (Branded Calls Beta) + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is sent from Twilio. `both` records the audio that is received and sent by Twilio. + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + :param machine_detection: Whether to detect if a human, answering machine, or fax has picked up the call. Can be: `Enable` or `DetectMessageEnd`. Use `Enable` if you would like us to return `AnsweredBy` as soon as the called party is identified. Use `DetectMessageEnd`, if you would like to leave a message on an answering machine. For more information, see [Answering Machine Detection](https://www.twilio.com/docs/voice/answering-machine-detection). + :param machine_detection_timeout: The number of seconds that we should attempt to detect an answering machine before timing out and sending a voice request with `AnsweredBy` of `unknown`. The default timeout is 30 seconds. + :param machine_detection_speech_threshold: The number of milliseconds that is used as the measuring stick for the length of the speech activity, where durations lower than this value will be interpreted as a human and longer than this value as a machine. Possible Values: 1000-6000. Default: 2400. + :param machine_detection_speech_end_threshold: The number of milliseconds of silence after speech activity at which point the speech activity is considered complete. Possible Values: 500-5000. Default: 1200. + :param machine_detection_silence_timeout: The number of milliseconds of initial silence after which an `unknown` AnsweredBy result will be returned. Possible Values: 2000-10000. Default: 5000. + :param amd_status_callback: The URL that we should call using the `amd_status_callback_method` to notify customer application whether the call was answered by human, machine or fax. + :param amd_status_callback_method: The HTTP method we should use when calling the `amd_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param trim: Whether to trim any leading and trailing silence from the participant recording. Can be: `trim-silence` or `do-not-trim` and the default is `trim-silence`. + :param call_token: A token string needed to invoke a forwarded call. A call_token is generated when an incoming call is received on a Twilio number. Pass an incoming call's call_token value to a forwarded call via the call_token parameter when creating a new call. A forwarded call should bear the same CallerID of the original incoming call. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "From": from_, + "To": to, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Label": label, + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "Region": region, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "ConferenceRecordingStatusCallbackEvent": serialize.map( + conference_recording_status_callback_event, lambda e: e + ), + "Coaching": serialize.boolean_to_string(coaching), + "CallSidToCoach": call_sid_to_coach, + "JitterBufferSize": jitter_buffer_size, + "Byoc": byoc, + "CallerId": caller_id, + "CallReason": call_reason, + "RecordingTrack": recording_track, + "TimeLimit": time_limit, + "MachineDetection": machine_detection, + "MachineDetectionTimeout": machine_detection_timeout, + "MachineDetectionSpeechThreshold": machine_detection_speech_threshold, + "MachineDetectionSpeechEndThreshold": machine_detection_speech_end_threshold, + "MachineDetectionSilenceTimeout": machine_detection_silence_timeout, + "AmdStatusCallback": amd_status_callback, + "AmdStatusCallbackMethod": amd_status_callback_method, + "Trim": trim, + "CallToken": call_token, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + ) + + async def create_async( + self, + from_: str, + to: str, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[List[str], object] = values.unset, + label: Union[str, object] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[List[str], object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + recording_status_callback_event: Union[List[str], object] = values.unset, + conference_recording_status_callback_event: Union[ + List[str], object + ] = values.unset, + coaching: Union[bool, object] = values.unset, + call_sid_to_coach: Union[str, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + byoc: Union[str, object] = values.unset, + caller_id: Union[str, object] = values.unset, + call_reason: Union[str, object] = values.unset, + recording_track: Union[str, object] = values.unset, + time_limit: Union[int, object] = values.unset, + machine_detection: Union[str, object] = values.unset, + machine_detection_timeout: Union[int, object] = values.unset, + machine_detection_speech_threshold: Union[int, object] = values.unset, + machine_detection_speech_end_threshold: Union[int, object] = values.unset, + machine_detection_silence_timeout: Union[int, object] = values.unset, + amd_status_callback: Union[str, object] = values.unset, + amd_status_callback_method: Union[str, object] = values.unset, + trim: Union[str, object] = values.unset, + call_token: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronously create the ParticipantInstance + + :param from_: The phone number, Client identifier, or username portion of SIP address that made this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). Client identifiers are formatted `client:name`. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `from` must also be a phone number. If `to` is sip address, this value of `from` should be a username portion to be used to populate the P-Asserted-Identity header that is passed to the SIP endpoint. + :param to: The phone number, SIP address, or Client identifier that received this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). SIP addresses are formatted as `sip:name@company.com`. Client identifiers are formatted `client:name`. [Custom parameters](https://www.twilio.com/docs/voice/api/conference-participant-resource#custom-parameters) may also be specified. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` and `POST` and defaults to `POST`. + :param status_callback_event: The conference state changes that should generate a call to `status_callback`. Can be: `initiated`, `ringing`, `answered`, and `completed`. Separate multiple values with a space. The default value is `completed`. + :param label: A label for this participant. If one is supplied, it may subsequently be used to fetch, update or delete the participant. + :param timeout: The number of seconds that we should allow the phone to ring before assuming there is no answer. Can be an integer between `5` and `600`, inclusive. The default value is `60`. We always add a 5-second timeout buffer to outgoing calls, so value of 10 would result in an actual timeout that was closer to 15 seconds. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Can be `true` or `false` and the default is `false`. + :param beep: Whether to play a notification beep to the conference when the participant joins. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the participant leaves. Can be: `true` or `false` and defaults to `false`. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. Can be: `true` or `false` and defaults to `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_record: Whether to record the conference the participant is joining. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from the conference recording. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference state changes that should generate a call to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `modify`, `speaker`, and `announcement`. Separate multiple values with a space. Defaults to `start end`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param recording_status_callback_event: The recording state changes that should generate a call to `recording_status_callback`. Can be: `started`, `in-progress`, `paused`, `resumed`, `stopped`, `completed`, `failed`, and `absent`. Separate multiple values with a space, ex: `'in-progress completed failed'`. + :param conference_recording_status_callback_event: The conference recording state changes that generate a call to `conference_recording_status_callback`. Can be: `in-progress`, `completed`, `failed`, and `absent`. Separate multiple values with a space, ex: `'in-progress completed failed'` + :param coaching: Whether the participant is coaching another call. Can be: `true` or `false`. If not present, defaults to `false` unless `call_sid_to_coach` is defined. If `true`, `call_sid_to_coach` must be defined. + :param call_sid_to_coach: The SID of the participant who is being `coached`. The participant being coached is the only participant who can hear the participant who is `coaching`. + :param jitter_buffer_size: Jitter buffer size for the connecting participant. Twilio will use this setting to apply Jitter Buffer before participant's audio is mixed into the conference. Can be: `off`, `small`, `medium`, and `large`. Default to `large`. + :param byoc: The SID of a BYOC (Bring Your Own Carrier) trunk to route this call with. Note that `byoc` is only meaningful when `to` is a phone number; it will otherwise be ignored. (Beta) + :param caller_id: The phone number, Client identifier, or username portion of SIP address that made this call. Phone numbers are in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (e.g., +16175551212). Client identifiers are formatted `client:name`. If using a phone number, it must be a Twilio number or a Verified [outgoing caller id](https://www.twilio.com/docs/voice/api/outgoing-caller-ids) for your account. If the `to` parameter is a phone number, `callerId` must also be a phone number. If `to` is sip address, this value of `callerId` should be a username portion to be used to populate the From header that is passed to the SIP endpoint. + :param call_reason: The Reason for the outgoing call. Use it to specify the purpose of the call that is presented on the called party's phone. (Branded Calls Beta) + :param recording_track: The audio track to record for the call. Can be: `inbound`, `outbound` or `both`. The default is `both`. `inbound` records the audio that is received by Twilio. `outbound` records the audio that is sent from Twilio. `both` records the audio that is received and sent by Twilio. + :param time_limit: The maximum duration of the call in seconds. Constraints depend on account and configuration. + :param machine_detection: Whether to detect if a human, answering machine, or fax has picked up the call. Can be: `Enable` or `DetectMessageEnd`. Use `Enable` if you would like us to return `AnsweredBy` as soon as the called party is identified. Use `DetectMessageEnd`, if you would like to leave a message on an answering machine. For more information, see [Answering Machine Detection](https://www.twilio.com/docs/voice/answering-machine-detection). + :param machine_detection_timeout: The number of seconds that we should attempt to detect an answering machine before timing out and sending a voice request with `AnsweredBy` of `unknown`. The default timeout is 30 seconds. + :param machine_detection_speech_threshold: The number of milliseconds that is used as the measuring stick for the length of the speech activity, where durations lower than this value will be interpreted as a human and longer than this value as a machine. Possible Values: 1000-6000. Default: 2400. + :param machine_detection_speech_end_threshold: The number of milliseconds of silence after speech activity at which point the speech activity is considered complete. Possible Values: 500-5000. Default: 1200. + :param machine_detection_silence_timeout: The number of milliseconds of initial silence after which an `unknown` AnsweredBy result will be returned. Possible Values: 2000-10000. Default: 5000. + :param amd_status_callback: The URL that we should call using the `amd_status_callback_method` to notify customer application whether the call was answered by human, machine or fax. + :param amd_status_callback_method: The HTTP method we should use when calling the `amd_status_callback` URL. Can be: `GET` or `POST` and the default is `POST`. + :param trim: Whether to trim any leading and trailing silence from the participant recording. Can be: `trim-silence` or `do-not-trim` and the default is `trim-silence`. + :param call_token: A token string needed to invoke a forwarded call. A call_token is generated when an incoming call is received on a Twilio number. Pass an incoming call's call_token value to a forwarded call via the call_token parameter when creating a new call. A forwarded call should bear the same CallerID of the original incoming call. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "From": from_, + "To": to, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Label": label, + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "Region": region, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "RecordingStatusCallbackEvent": serialize.map( + recording_status_callback_event, lambda e: e + ), + "ConferenceRecordingStatusCallbackEvent": serialize.map( + conference_recording_status_callback_event, lambda e: e + ), + "Coaching": serialize.boolean_to_string(coaching), + "CallSidToCoach": call_sid_to_coach, + "JitterBufferSize": jitter_buffer_size, + "Byoc": byoc, + "CallerId": caller_id, + "CallReason": call_reason, + "RecordingTrack": recording_track, + "TimeLimit": time_limit, + "MachineDetection": machine_detection, + "MachineDetectionTimeout": machine_detection_timeout, + "MachineDetectionSpeechThreshold": machine_detection_speech_threshold, + "MachineDetectionSpeechEndThreshold": machine_detection_speech_end_threshold, + "MachineDetectionSilenceTimeout": machine_detection_silence_timeout, + "AmdStatusCallback": amd_status_callback, + "AmdStatusCallbackMethod": amd_status_callback_method, + "Trim": trim, + "CallToken": call_token, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + ) + + def stream( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param bool hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param bool coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + muted=muted, hold=hold, coaching=coaching, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param bool hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param bool coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + muted=muted, hold=hold, coaching=coaching, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param bool hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param bool coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + muted=muted, + hold=hold, + coaching=coaching, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param bool hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param bool coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + muted=muted, + hold=hold, + coaching=coaching, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "Muted": serialize.boolean_to_string(muted), + "Hold": serialize.boolean_to_string(hold), + "Coaching": serialize.boolean_to_string(coaching), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + muted: Union[bool, object] = values.unset, + hold: Union[bool, object] = values.unset, + coaching: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param muted: Whether to return only participants that are muted. Can be: `true` or `false`. + :param hold: Whether to return only participants that are on hold. Can be: `true` or `false`. + :param coaching: Whether to return only participants who are coaching another call. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "Muted": serialize.boolean_to_string(muted), + "Hold": serialize.boolean_to_string(hold), + "Coaching": serialize.boolean_to_string(coaching), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, call_sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID or label of the participant to update. Non URL safe characters in a label must be percent encoded, for example, a space character is represented as %20. + """ + return ParticipantContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=call_sid, + ) + + def __call__(self, call_sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID or label of the participant to update. Non URL safe characters in a label must be percent encoded, for example, a space character is represented as %20. + """ + return ParticipantContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + call_sid=call_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/recording.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/recording.py new file mode 100644 index 0000000..dfb1d40 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/conference/recording.py @@ -0,0 +1,694 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RecordingInstance(InstanceResource): + + class Source(object): + DIALVERB = "DialVerb" + CONFERENCE = "Conference" + OUTBOUNDAPI = "OutboundAPI" + TRUNKING = "Trunking" + RECORDVERB = "RecordVerb" + STARTCALLRECORDINGAPI = "StartCallRecordingAPI" + STARTCONFERENCERECORDINGAPI = "StartConferenceRecordingAPI" + + class Status(object): + IN_PROGRESS = "in-progress" + PAUSED = "paused" + STOPPED = "stopped" + PROCESSING = "processing" + COMPLETED = "completed" + ABSENT = "absent" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Conference Recording resource. + :ivar api_version: The API version used to create the recording. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Conference Recording resource is associated with. + :ivar conference_sid: The Conference SID that identifies the conference associated with the recording. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar start_time: The start time of the recording in GMT and in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar duration: The length of the recording in seconds. + :ivar sid: The unique string that that we created to identify the Conference Recording resource. + :ivar price: The one-time cost of creating the recording in the `price_unit` currency. + :ivar price_unit: The currency used in the `price` property. Example: `USD`. + :ivar status: + :ivar channels: The number of channels in the final recording file. Can be: `1`, or `2`. Separating a two leg call into two separate channels of the recording file is supported in [Dial](https://www.twilio.com/docs/voice/twiml/dial#attributes-record) and [Outbound Rest API](https://www.twilio.com/docs/voice/make-calls) record options. + :ivar source: + :ivar error_code: The error code that describes why the recording is `absent`. The error code is described in our [Error Dictionary](https://www.twilio.com/docs/api/errors). This value is null if the recording `status` is not `absent`. + :ivar encryption_details: How to decrypt the recording if it was encrypted using [Call Recording Encryption](https://www.twilio.com/docs/voice/tutorials/voice-recording-encryption) feature. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + conference_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.call_sid: Optional[str] = payload.get("call_sid") + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.start_time: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("start_time") + ) + self.duration: Optional[str] = payload.get("duration") + self.sid: Optional[str] = payload.get("sid") + self.price: Optional[str] = payload.get("price") + self.price_unit: Optional[str] = payload.get("price_unit") + self.status: Optional["RecordingInstance.Status"] = payload.get("status") + self.channels: Optional[int] = deserialize.integer(payload.get("channels")) + self.source: Optional["RecordingInstance.Source"] = payload.get("source") + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.encryption_details: Optional[Dict[str, object]] = payload.get( + "encryption_details" + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + "sid": sid or self.sid, + } + self._context: Optional[RecordingContext] = None + + @property + def _proxy(self) -> "RecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingContext for this RecordingInstance + """ + if self._context is None: + self._context = RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RecordingInstance": + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RecordingInstance": + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> "RecordingInstance": + """ + Update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + return self._proxy.update( + status=status, + pause_behavior=pause_behavior, + ) + + async def update_async( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> "RecordingInstance": + """ + Asynchronous coroutine to update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + return await self._proxy.update_async( + status=status, + pause_behavior=pause_behavior, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, conference_sid: str, sid: str + ): + """ + Initialize the RecordingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Conference Recording resource to update. + :param conference_sid: The Conference SID that identifies the conference associated with the recording to update. + :param sid: The Twilio-provided string that uniquely identifies the Conference Recording resource to update. Use `Twilio.CURRENT` to reference the current active recording. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Conferences/{conference_sid}/Recordings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RecordingInstance: + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RecordingInstance: + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Status": status, + "PauseBehavior": pause_behavior, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + status: "RecordingInstance.Status", + pause_behavior: Union[str, object] = values.unset, + ) -> RecordingInstance: + """ + Asynchronous coroutine to update the RecordingInstance + + :param status: + :param pause_behavior: Whether to record during a pause. Can be: `skip` or `silence` and the default is `silence`. `skip` does not record during the pause period, while `silence` will replace the actual audio of the call with silence during the pause period. This parameter only applies when setting `status` is set to `paused`. + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Status": status, + "PauseBehavior": pause_behavior, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RecordingInstance: + """ + Build an instance of RecordingInstance + + :param payload: Payload response from the API + """ + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RecordingList(ListResource): + + def __init__(self, version: Version, account_sid: str, conference_sid: str): + """ + Initialize the RecordingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Conference Recording resources to read. + :param conference_sid: The Conference SID that identifies the conference associated with the recording to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "conference_sid": conference_sid, + } + self._uri = "/Accounts/{account_sid}/Conferences/{conference_sid}/Recordings.json".format( + **self._solution + ) + + def stream( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RecordingInstance]: + """ + Streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RecordingInstance]: + """ + Asynchronously streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Asynchronously lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RecordingPage(self._version, response, self._solution) + + async def page_async( + self, + date_created: Union[date, object] = values.unset, + date_created_before: Union[date, object] = values.unset, + date_created_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Asynchronously retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_before: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param date_created_after: The `date_created` value, specified as `YYYY-MM-DD`, of the resources to read. You can also specify inequality: `DateCreated<=YYYY-MM-DD` will return recordings generated at or before midnight on a given date, and `DateCreated>=YYYY-MM-DD` returns recordings generated at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_date(date_created), + "DateCreated<": serialize.iso8601_date(date_created_before), + "DateCreated>": serialize.iso8601_date(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RecordingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RecordingPage: + """ + Retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RecordingPage: + """ + Asynchronously retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + def get(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Conference Recording resource to update. Use `Twilio.CURRENT` to reference the current active recording. + """ + return RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Conference Recording resource to update. Use `Twilio.CURRENT` to reference the current active recording. + """ + return RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + conference_sid=self._solution["conference_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/connect_app.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/connect_app.py new file mode 100644 index 0000000..722e4e9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/connect_app.py @@ -0,0 +1,666 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ConnectAppInstance(InstanceResource): + + class Permission(object): + GET_ALL = "get-all" + POST_ALL = "post-all" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ConnectApp resource. + :ivar authorize_redirect_url: The URL we redirect the user to after we authenticate the user and obtain authorization to access the Connect App. + :ivar company_name: The company name set for the Connect App. + :ivar deauthorize_callback_method: The HTTP method we use to call `deauthorize_callback_url`. + :ivar deauthorize_callback_url: The URL we call using the `deauthorize_callback_method` to de-authorize the Connect App. + :ivar description: The description of the Connect App. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar homepage_url: The public URL where users can obtain more information about this Connect App. + :ivar permissions: The set of permissions that your ConnectApp requests. + :ivar sid: The unique string that that we created to identify the ConnectApp resource. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.authorize_redirect_url: Optional[str] = payload.get( + "authorize_redirect_url" + ) + self.company_name: Optional[str] = payload.get("company_name") + self.deauthorize_callback_method: Optional[str] = payload.get( + "deauthorize_callback_method" + ) + self.deauthorize_callback_url: Optional[str] = payload.get( + "deauthorize_callback_url" + ) + self.description: Optional[str] = payload.get("description") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.homepage_url: Optional[str] = payload.get("homepage_url") + self.permissions: Optional[List["ConnectAppInstance.Permission"]] = payload.get( + "permissions" + ) + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[ConnectAppContext] = None + + @property + def _proxy(self) -> "ConnectAppContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConnectAppContext for this ConnectAppInstance + """ + if self._context is None: + self._context = ConnectAppContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ConnectAppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectAppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ConnectAppInstance": + """ + Fetch the ConnectAppInstance + + + :returns: The fetched ConnectAppInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConnectAppInstance": + """ + Asynchronous coroutine to fetch the ConnectAppInstance + + + :returns: The fetched ConnectAppInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + authorize_redirect_url: Union[str, object] = values.unset, + company_name: Union[str, object] = values.unset, + deauthorize_callback_method: Union[str, object] = values.unset, + deauthorize_callback_url: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + homepage_url: Union[str, object] = values.unset, + permissions: Union[ + List["ConnectAppInstance.Permission"], object + ] = values.unset, + ) -> "ConnectAppInstance": + """ + Update the ConnectAppInstance + + :param authorize_redirect_url: The URL to redirect the user to after we authenticate the user and obtain authorization to access the Connect App. + :param company_name: The company name to set for the Connect App. + :param deauthorize_callback_method: The HTTP method to use when calling `deauthorize_callback_url`. + :param deauthorize_callback_url: The URL to call using the `deauthorize_callback_method` to de-authorize the Connect App. + :param description: A description of the Connect App. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param homepage_url: A public URL where users can obtain more information about this Connect App. + :param permissions: A comma-separated list of the permissions you will request from the users of this ConnectApp. Can include: `get-all` and `post-all`. + + :returns: The updated ConnectAppInstance + """ + return self._proxy.update( + authorize_redirect_url=authorize_redirect_url, + company_name=company_name, + deauthorize_callback_method=deauthorize_callback_method, + deauthorize_callback_url=deauthorize_callback_url, + description=description, + friendly_name=friendly_name, + homepage_url=homepage_url, + permissions=permissions, + ) + + async def update_async( + self, + authorize_redirect_url: Union[str, object] = values.unset, + company_name: Union[str, object] = values.unset, + deauthorize_callback_method: Union[str, object] = values.unset, + deauthorize_callback_url: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + homepage_url: Union[str, object] = values.unset, + permissions: Union[ + List["ConnectAppInstance.Permission"], object + ] = values.unset, + ) -> "ConnectAppInstance": + """ + Asynchronous coroutine to update the ConnectAppInstance + + :param authorize_redirect_url: The URL to redirect the user to after we authenticate the user and obtain authorization to access the Connect App. + :param company_name: The company name to set for the Connect App. + :param deauthorize_callback_method: The HTTP method to use when calling `deauthorize_callback_url`. + :param deauthorize_callback_url: The URL to call using the `deauthorize_callback_method` to de-authorize the Connect App. + :param description: A description of the Connect App. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param homepage_url: A public URL where users can obtain more information about this Connect App. + :param permissions: A comma-separated list of the permissions you will request from the users of this ConnectApp. Can include: `get-all` and `post-all`. + + :returns: The updated ConnectAppInstance + """ + return await self._proxy.update_async( + authorize_redirect_url=authorize_redirect_url, + company_name=company_name, + deauthorize_callback_method=deauthorize_callback_method, + deauthorize_callback_url=deauthorize_callback_url, + description=description, + friendly_name=friendly_name, + homepage_url=homepage_url, + permissions=permissions, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectAppContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the ConnectAppContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ConnectApp resources to update. + :param sid: The Twilio-provided string that uniquely identifies the ConnectApp resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/ConnectApps/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the ConnectAppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectAppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ConnectAppInstance: + """ + Fetch the ConnectAppInstance + + + :returns: The fetched ConnectAppInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConnectAppInstance: + """ + Asynchronous coroutine to fetch the ConnectAppInstance + + + :returns: The fetched ConnectAppInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + authorize_redirect_url: Union[str, object] = values.unset, + company_name: Union[str, object] = values.unset, + deauthorize_callback_method: Union[str, object] = values.unset, + deauthorize_callback_url: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + homepage_url: Union[str, object] = values.unset, + permissions: Union[ + List["ConnectAppInstance.Permission"], object + ] = values.unset, + ) -> ConnectAppInstance: + """ + Update the ConnectAppInstance + + :param authorize_redirect_url: The URL to redirect the user to after we authenticate the user and obtain authorization to access the Connect App. + :param company_name: The company name to set for the Connect App. + :param deauthorize_callback_method: The HTTP method to use when calling `deauthorize_callback_url`. + :param deauthorize_callback_url: The URL to call using the `deauthorize_callback_method` to de-authorize the Connect App. + :param description: A description of the Connect App. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param homepage_url: A public URL where users can obtain more information about this Connect App. + :param permissions: A comma-separated list of the permissions you will request from the users of this ConnectApp. Can include: `get-all` and `post-all`. + + :returns: The updated ConnectAppInstance + """ + data = values.of( + { + "AuthorizeRedirectUrl": authorize_redirect_url, + "CompanyName": company_name, + "DeauthorizeCallbackMethod": deauthorize_callback_method, + "DeauthorizeCallbackUrl": deauthorize_callback_url, + "Description": description, + "FriendlyName": friendly_name, + "HomepageUrl": homepage_url, + "Permissions": serialize.map(permissions, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + authorize_redirect_url: Union[str, object] = values.unset, + company_name: Union[str, object] = values.unset, + deauthorize_callback_method: Union[str, object] = values.unset, + deauthorize_callback_url: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + homepage_url: Union[str, object] = values.unset, + permissions: Union[ + List["ConnectAppInstance.Permission"], object + ] = values.unset, + ) -> ConnectAppInstance: + """ + Asynchronous coroutine to update the ConnectAppInstance + + :param authorize_redirect_url: The URL to redirect the user to after we authenticate the user and obtain authorization to access the Connect App. + :param company_name: The company name to set for the Connect App. + :param deauthorize_callback_method: The HTTP method to use when calling `deauthorize_callback_url`. + :param deauthorize_callback_url: The URL to call using the `deauthorize_callback_method` to de-authorize the Connect App. + :param description: A description of the Connect App. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param homepage_url: A public URL where users can obtain more information about this Connect App. + :param permissions: A comma-separated list of the permissions you will request from the users of this ConnectApp. Can include: `get-all` and `post-all`. + + :returns: The updated ConnectAppInstance + """ + data = values.of( + { + "AuthorizeRedirectUrl": authorize_redirect_url, + "CompanyName": company_name, + "DeauthorizeCallbackMethod": deauthorize_callback_method, + "DeauthorizeCallbackUrl": deauthorize_callback_url, + "Description": description, + "FriendlyName": friendly_name, + "HomepageUrl": homepage_url, + "Permissions": serialize.map(permissions, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectAppInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectAppPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConnectAppInstance: + """ + Build an instance of ConnectAppInstance + + :param payload: Payload response from the API + """ + return ConnectAppInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConnectAppList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ConnectAppList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ConnectApp resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/ConnectApps.json".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConnectAppInstance]: + """ + Streams ConnectAppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConnectAppInstance]: + """ + Asynchronously streams ConnectAppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectAppInstance]: + """ + Lists ConnectAppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectAppInstance]: + """ + Asynchronously lists ConnectAppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectAppPage: + """ + Retrieve a single page of ConnectAppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectAppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConnectAppPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectAppPage: + """ + Asynchronously retrieve a single page of ConnectAppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectAppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConnectAppPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConnectAppPage: + """ + Retrieve a specific page of ConnectAppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectAppInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConnectAppPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConnectAppPage: + """ + Asynchronously retrieve a specific page of ConnectAppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectAppInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConnectAppPage(self._version, response, self._solution) + + def get(self, sid: str) -> ConnectAppContext: + """ + Constructs a ConnectAppContext + + :param sid: The Twilio-provided string that uniquely identifies the ConnectApp resource to update. + """ + return ConnectAppContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ConnectAppContext: + """ + Constructs a ConnectAppContext + + :param sid: The Twilio-provided string that uniquely identifies the ConnectApp resource to update. + """ + return ConnectAppContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__init__.py new file mode 100644 index 0000000..6fa4748 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__init__.py @@ -0,0 +1,1280 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.incoming_phone_number.assigned_add_on import ( + AssignedAddOnList, +) +from twilio.rest.api.v2010.account.incoming_phone_number.local import LocalList +from twilio.rest.api.v2010.account.incoming_phone_number.mobile import MobileList +from twilio.rest.api.v2010.account.incoming_phone_number.toll_free import TollFreeList + + +class IncomingPhoneNumberInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + class EmergencyAddressStatus(object): + REGISTERED = "registered" + UNREGISTERED = "unregistered" + PENDING_REGISTRATION = "pending-registration" + REGISTRATION_FAILURE = "registration-failure" + PENDING_UNREGISTRATION = "pending-unregistration" + UNREGISTRATION_FAILURE = "unregistration-failure" + + class EmergencyStatus(object): + ACTIVE = "Active" + INACTIVE = "Inactive" + + class VoiceReceiveMode(object): + VOICE = "voice" + FAX = "fax" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this IncomingPhoneNumber resource. + :ivar address_sid: The SID of the Address resource associated with the phone number. + :ivar address_requirements: + :ivar api_version: The API version used to start a new TwiML session. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar identity_sid: The SID of the Identity resource that we associate with the phone number. Some regions require an Identity to meet local regulations. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origin: The phone number's origin. `twilio` identifies Twilio-owned phone numbers and `hosted` identifies hosted phone numbers. + :ivar sid: The unique string that that we created to identify this IncomingPhoneNumber resource. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_receive_mode: + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database ($0.01 per look up). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call when the phone number receives a call. The `voice_url` will not be used if a `voice_application_sid` or a `trunk_sid` is set. + :ivar emergency_status: + :ivar emergency_address_sid: The SID of the emergency address configuration that we use for emergency calling from this phone number. + :ivar emergency_address_status: + :ivar bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :ivar status: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.address_requirements: Optional[ + "IncomingPhoneNumberInstance.AddressRequirement" + ] = payload.get("address_requirements") + self.api_version: Optional[str] = payload.get("api_version") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.identity_sid: Optional[str] = payload.get("identity_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.origin: Optional[str] = payload.get("origin") + self.sid: Optional[str] = payload.get("sid") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.uri: Optional[str] = payload.get("uri") + self.voice_receive_mode: Optional[ + "IncomingPhoneNumberInstance.VoiceReceiveMode" + ] = payload.get("voice_receive_mode") + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.emergency_status: Optional[ + "IncomingPhoneNumberInstance.EmergencyStatus" + ] = payload.get("emergency_status") + self.emergency_address_sid: Optional[str] = payload.get("emergency_address_sid") + self.emergency_address_status: Optional[ + "IncomingPhoneNumberInstance.EmergencyAddressStatus" + ] = payload.get("emergency_address_status") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.status: Optional[str] = payload.get("status") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[IncomingPhoneNumberContext] = None + + @property + def _proxy(self) -> "IncomingPhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IncomingPhoneNumberContext for this IncomingPhoneNumberInstance + """ + if self._context is None: + self._context = IncomingPhoneNumberContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IncomingPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IncomingPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IncomingPhoneNumberInstance": + """ + Fetch the IncomingPhoneNumberInstance + + + :returns: The fetched IncomingPhoneNumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IncomingPhoneNumberInstance": + """ + Asynchronous coroutine to fetch the IncomingPhoneNumberInstance + + + :returns: The fetched IncomingPhoneNumberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + account_sid: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> "IncomingPhoneNumberInstance": + """ + Update the IncomingPhoneNumberInstance + + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resource to update. For more information, see [Exchanging Numbers Between Subaccounts](https://www.twilio.com/docs/iam/api/subaccounts#exchanging-numbers). + :param api_version: The API version to use for incoming calls made to the phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe this phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle phone calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from this phone number. + :param trunk_sid: The SID of the Trunk we should use to handle phone calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param identity_sid: The SID of the Identity resource that we should associate with the phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the phone number. Some regions require addresses to meet local regulations. + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The updated IncomingPhoneNumberInstance + """ + return self._proxy.update( + account_sid=account_sid, + api_version=api_version, + friendly_name=friendly_name, + sms_application_sid=sms_application_sid, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + status_callback=status_callback, + status_callback_method=status_callback_method, + voice_application_sid=voice_application_sid, + voice_caller_id_lookup=voice_caller_id_lookup, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + emergency_status=emergency_status, + emergency_address_sid=emergency_address_sid, + trunk_sid=trunk_sid, + voice_receive_mode=voice_receive_mode, + identity_sid=identity_sid, + address_sid=address_sid, + bundle_sid=bundle_sid, + ) + + async def update_async( + self, + account_sid: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> "IncomingPhoneNumberInstance": + """ + Asynchronous coroutine to update the IncomingPhoneNumberInstance + + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resource to update. For more information, see [Exchanging Numbers Between Subaccounts](https://www.twilio.com/docs/iam/api/subaccounts#exchanging-numbers). + :param api_version: The API version to use for incoming calls made to the phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe this phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle phone calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from this phone number. + :param trunk_sid: The SID of the Trunk we should use to handle phone calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param identity_sid: The SID of the Identity resource that we should associate with the phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the phone number. Some regions require addresses to meet local regulations. + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The updated IncomingPhoneNumberInstance + """ + return await self._proxy.update_async( + account_sid=account_sid, + api_version=api_version, + friendly_name=friendly_name, + sms_application_sid=sms_application_sid, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + status_callback=status_callback, + status_callback_method=status_callback_method, + voice_application_sid=voice_application_sid, + voice_caller_id_lookup=voice_caller_id_lookup, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + emergency_status=emergency_status, + emergency_address_sid=emergency_address_sid, + trunk_sid=trunk_sid, + voice_receive_mode=voice_receive_mode, + identity_sid=identity_sid, + address_sid=address_sid, + bundle_sid=bundle_sid, + ) + + @property + def assigned_add_ons(self) -> AssignedAddOnList: + """ + Access the assigned_add_ons + """ + return self._proxy.assigned_add_ons + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IncomingPhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the IncomingPhoneNumberContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resource to update. For more information, see [Exchanging Numbers Between Subaccounts](https://www.twilio.com/docs/iam/api/subaccounts#exchanging-numbers). + :param sid: The Twilio-provided string that uniquely identifies the IncomingPhoneNumber resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/{sid}.json".format( + **self._solution + ) + + self._assigned_add_ons: Optional[AssignedAddOnList] = None + + def delete(self) -> bool: + """ + Deletes the IncomingPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IncomingPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IncomingPhoneNumberInstance: + """ + Fetch the IncomingPhoneNumberInstance + + + :returns: The fetched IncomingPhoneNumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IncomingPhoneNumberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IncomingPhoneNumberInstance: + """ + Asynchronous coroutine to fetch the IncomingPhoneNumberInstance + + + :returns: The fetched IncomingPhoneNumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IncomingPhoneNumberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + account_sid: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> IncomingPhoneNumberInstance: + """ + Update the IncomingPhoneNumberInstance + + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resource to update. For more information, see [Exchanging Numbers Between Subaccounts](https://www.twilio.com/docs/iam/api/subaccounts#exchanging-numbers). + :param api_version: The API version to use for incoming calls made to the phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe this phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle phone calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from this phone number. + :param trunk_sid: The SID of the Trunk we should use to handle phone calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param identity_sid: The SID of the Identity resource that we should associate with the phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the phone number. Some regions require addresses to meet local regulations. + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The updated IncomingPhoneNumberInstance + """ + data = values.of( + { + "AccountSid": account_sid, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "BundleSid": bundle_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return IncomingPhoneNumberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + account_sid: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> IncomingPhoneNumberInstance: + """ + Asynchronous coroutine to update the IncomingPhoneNumberInstance + + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resource to update. For more information, see [Exchanging Numbers Between Subaccounts](https://www.twilio.com/docs/iam/api/subaccounts#exchanging-numbers). + :param api_version: The API version to use for incoming calls made to the phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe this phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle phone calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from this phone number. + :param trunk_sid: The SID of the Trunk we should use to handle phone calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param identity_sid: The SID of the Identity resource that we should associate with the phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the phone number. Some regions require addresses to meet local regulations. + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The updated IncomingPhoneNumberInstance + """ + data = values.of( + { + "AccountSid": account_sid, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "BundleSid": bundle_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IncomingPhoneNumberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def assigned_add_ons(self) -> AssignedAddOnList: + """ + Access the assigned_add_ons + """ + if self._assigned_add_ons is None: + self._assigned_add_ons = AssignedAddOnList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._assigned_add_ons + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IncomingPhoneNumberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IncomingPhoneNumberInstance: + """ + Build an instance of IncomingPhoneNumberInstance + + :param payload: Payload response from the API + """ + return IncomingPhoneNumberInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IncomingPhoneNumberList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the IncomingPhoneNumberList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IncomingPhoneNumber resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers.json".format( + **self._solution + ) + + self._local: Optional[LocalList] = None + self._mobile: Optional[MobileList] = None + self._toll_free: Optional[TollFreeList] = None + + def create( + self, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + area_code: Union[str, object] = values.unset, + ) -> IncomingPhoneNumberInstance: + """ + Create the IncomingPhoneNumberInstance + + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the new phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param area_code: The desired area code for your new incoming phone number. Can be any three-digit, US or Canada area code. We will provision an available phone number within this area code for you. **You must provide an `area_code` or a `phone_number`.** (US and Canada only). + + :returns: The created IncomingPhoneNumberInstance + """ + + data = values.of( + { + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + "PhoneNumber": phone_number, + "AreaCode": area_code, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IncomingPhoneNumberInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + emergency_status: Union[ + "IncomingPhoneNumberInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "IncomingPhoneNumberInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + area_code: Union[str, object] = values.unset, + ) -> IncomingPhoneNumberInstance: + """ + Asynchronously create the IncomingPhoneNumberInstance + + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the new phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param area_code: The desired area code for your new incoming phone number. Can be any three-digit, US or Canada area code. We will provision an available phone number within this area code for you. **You must provide an `area_code` or a `phone_number`.** (US and Canada only). + + :returns: The created IncomingPhoneNumberInstance + """ + + data = values.of( + { + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + "PhoneNumber": phone_number, + "AreaCode": area_code, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IncomingPhoneNumberInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IncomingPhoneNumberInstance]: + """ + Streams IncomingPhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IncomingPhoneNumberInstance]: + """ + Asynchronously streams IncomingPhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IncomingPhoneNumberInstance]: + """ + Lists IncomingPhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IncomingPhoneNumberInstance]: + """ + Asynchronously lists IncomingPhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IncomingPhoneNumberPage: + """ + Retrieve a single page of IncomingPhoneNumberInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IncomingPhoneNumberInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IncomingPhoneNumberPage(self._version, response, self._solution) + + async def page_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IncomingPhoneNumberPage: + """ + Asynchronously retrieve a single page of IncomingPhoneNumberInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the IncomingPhoneNumber resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IncomingPhoneNumberInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IncomingPhoneNumberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> IncomingPhoneNumberPage: + """ + Retrieve a specific page of IncomingPhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IncomingPhoneNumberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IncomingPhoneNumberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> IncomingPhoneNumberPage: + """ + Asynchronously retrieve a specific page of IncomingPhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IncomingPhoneNumberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IncomingPhoneNumberPage(self._version, response, self._solution) + + @property + def local(self) -> LocalList: + """ + Access the local + """ + if self._local is None: + self._local = LocalList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._local + + @property + def mobile(self) -> MobileList: + """ + Access the mobile + """ + if self._mobile is None: + self._mobile = MobileList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._mobile + + @property + def toll_free(self) -> TollFreeList: + """ + Access the toll_free + """ + if self._toll_free is None: + self._toll_free = TollFreeList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._toll_free + + def get(self, sid: str) -> IncomingPhoneNumberContext: + """ + Constructs a IncomingPhoneNumberContext + + :param sid: The Twilio-provided string that uniquely identifies the IncomingPhoneNumber resource to update. + """ + return IncomingPhoneNumberContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> IncomingPhoneNumberContext: + """ + Constructs a IncomingPhoneNumberContext + + :param sid: The Twilio-provided string that uniquely identifies the IncomingPhoneNumber resource to update. + """ + return IncomingPhoneNumberContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..444246c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/local.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/local.cpython-310.pyc new file mode 100644 index 0000000..e28b99c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/local.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/mobile.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/mobile.cpython-310.pyc new file mode 100644 index 0000000..fb61327 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/mobile.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/toll_free.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/toll_free.cpython-310.pyc new file mode 100644 index 0000000..d5d8c1f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/__pycache__/toll_free.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__init__.py new file mode 100644 index 0000000..12c6f96 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__init__.py @@ -0,0 +1,580 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.incoming_phone_number.assigned_add_on.assigned_add_on_extension import ( + AssignedAddOnExtensionList, +) + + +class AssignedAddOnInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource. + :ivar resource_sid: The SID of the Phone Number to which the Add-on is assigned. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar description: A short description of the functionality that the Add-on provides. + :ivar configuration: A JSON string that represents the current configuration of this Add-on installation. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar subresource_uris: A list of related resources identified by their relative URIs. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + resource_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.resource_sid: Optional[str] = payload.get("resource_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.unique_name: Optional[str] = payload.get("unique_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + "sid": sid or self.sid, + } + self._context: Optional[AssignedAddOnContext] = None + + @property + def _proxy(self) -> "AssignedAddOnContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AssignedAddOnContext for this AssignedAddOnInstance + """ + if self._context is None: + self._context = AssignedAddOnContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AssignedAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AssignedAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AssignedAddOnInstance": + """ + Fetch the AssignedAddOnInstance + + + :returns: The fetched AssignedAddOnInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AssignedAddOnInstance": + """ + Asynchronous coroutine to fetch the AssignedAddOnInstance + + + :returns: The fetched AssignedAddOnInstance + """ + return await self._proxy.fetch_async() + + @property + def extensions(self) -> AssignedAddOnExtensionList: + """ + Access the extensions + """ + return self._proxy.extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssignedAddOnContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, resource_sid: str, sid: str): + """ + Initialize the AssignedAddOnContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource to fetch. + :param resource_sid: The SID of the Phone Number to which the Add-on is assigned. + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/{resource_sid}/AssignedAddOns/{sid}.json".format( + **self._solution + ) + + self._extensions: Optional[AssignedAddOnExtensionList] = None + + def delete(self) -> bool: + """ + Deletes the AssignedAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AssignedAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AssignedAddOnInstance: + """ + Fetch the AssignedAddOnInstance + + + :returns: The fetched AssignedAddOnInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AssignedAddOnInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AssignedAddOnInstance: + """ + Asynchronous coroutine to fetch the AssignedAddOnInstance + + + :returns: The fetched AssignedAddOnInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AssignedAddOnInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + sid=self._solution["sid"], + ) + + @property + def extensions(self) -> AssignedAddOnExtensionList: + """ + Access the extensions + """ + if self._extensions is None: + self._extensions = AssignedAddOnExtensionList( + self._version, + self._solution["account_sid"], + self._solution["resource_sid"], + self._solution["sid"], + ) + return self._extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssignedAddOnPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AssignedAddOnInstance: + """ + Build an instance of AssignedAddOnInstance + + :param payload: Payload response from the API + """ + return AssignedAddOnInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AssignedAddOnList(ListResource): + + def __init__(self, version: Version, account_sid: str, resource_sid: str): + """ + Initialize the AssignedAddOnList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resources to read. + :param resource_sid: The SID of the Phone Number to which the Add-on is assigned. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/{resource_sid}/AssignedAddOns.json".format( + **self._solution + ) + + def create(self, installed_add_on_sid: str) -> AssignedAddOnInstance: + """ + Create the AssignedAddOnInstance + + :param installed_add_on_sid: The SID that identifies the Add-on installation. + + :returns: The created AssignedAddOnInstance + """ + + data = values.of( + { + "InstalledAddOnSid": installed_add_on_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AssignedAddOnInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + ) + + async def create_async(self, installed_add_on_sid: str) -> AssignedAddOnInstance: + """ + Asynchronously create the AssignedAddOnInstance + + :param installed_add_on_sid: The SID that identifies the Add-on installation. + + :returns: The created AssignedAddOnInstance + """ + + data = values.of( + { + "InstalledAddOnSid": installed_add_on_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AssignedAddOnInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AssignedAddOnInstance]: + """ + Streams AssignedAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AssignedAddOnInstance]: + """ + Asynchronously streams AssignedAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssignedAddOnInstance]: + """ + Lists AssignedAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssignedAddOnInstance]: + """ + Asynchronously lists AssignedAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssignedAddOnPage: + """ + Retrieve a single page of AssignedAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssignedAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AssignedAddOnPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssignedAddOnPage: + """ + Asynchronously retrieve a single page of AssignedAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssignedAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AssignedAddOnPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AssignedAddOnPage: + """ + Retrieve a specific page of AssignedAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssignedAddOnInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AssignedAddOnPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AssignedAddOnPage: + """ + Asynchronously retrieve a specific page of AssignedAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssignedAddOnInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AssignedAddOnPage(self._version, response, self._solution) + + def get(self, sid: str) -> AssignedAddOnContext: + """ + Constructs a AssignedAddOnContext + + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + return AssignedAddOnContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AssignedAddOnContext: + """ + Constructs a AssignedAddOnContext + + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + return AssignedAddOnContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e710385 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/assigned_add_on_extension.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/assigned_add_on_extension.cpython-310.pyc new file mode 100644 index 0000000..d1345ff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/__pycache__/assigned_add_on_extension.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/assigned_add_on_extension.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/assigned_add_on_extension.py new file mode 100644 index 0000000..18504ca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/assigned_add_on/assigned_add_on_extension.py @@ -0,0 +1,470 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AssignedAddOnExtensionInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource. + :ivar resource_sid: The SID of the Phone Number to which the Add-on is assigned. + :ivar assigned_add_on_sid: The SID that uniquely identifies the assigned Add-on installation. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar product_name: A string that you assigned to describe the Product this Extension is used within. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar enabled: Whether the Extension will be invoked. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + resource_sid: str, + assigned_add_on_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.resource_sid: Optional[str] = payload.get("resource_sid") + self.assigned_add_on_sid: Optional[str] = payload.get("assigned_add_on_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.product_name: Optional[str] = payload.get("product_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.uri: Optional[str] = payload.get("uri") + self.enabled: Optional[bool] = payload.get("enabled") + + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + "assigned_add_on_sid": assigned_add_on_sid, + "sid": sid or self.sid, + } + self._context: Optional[AssignedAddOnExtensionContext] = None + + @property + def _proxy(self) -> "AssignedAddOnExtensionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AssignedAddOnExtensionContext for this AssignedAddOnExtensionInstance + """ + if self._context is None: + self._context = AssignedAddOnExtensionContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AssignedAddOnExtensionInstance": + """ + Fetch the AssignedAddOnExtensionInstance + + + :returns: The fetched AssignedAddOnExtensionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AssignedAddOnExtensionInstance": + """ + Asynchronous coroutine to fetch the AssignedAddOnExtensionInstance + + + :returns: The fetched AssignedAddOnExtensionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssignedAddOnExtensionContext(InstanceContext): + + def __init__( + self, + version: Version, + account_sid: str, + resource_sid: str, + assigned_add_on_sid: str, + sid: str, + ): + """ + Initialize the AssignedAddOnExtensionContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource to fetch. + :param resource_sid: The SID of the Phone Number to which the Add-on is assigned. + :param assigned_add_on_sid: The SID that uniquely identifies the assigned Add-on installation. + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + "assigned_add_on_sid": assigned_add_on_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/{resource_sid}/AssignedAddOns/{assigned_add_on_sid}/Extensions/{sid}.json".format( + **self._solution + ) + + def fetch(self) -> AssignedAddOnExtensionInstance: + """ + Fetch the AssignedAddOnExtensionInstance + + + :returns: The fetched AssignedAddOnExtensionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AssignedAddOnExtensionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AssignedAddOnExtensionInstance: + """ + Asynchronous coroutine to fetch the AssignedAddOnExtensionInstance + + + :returns: The fetched AssignedAddOnExtensionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AssignedAddOnExtensionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssignedAddOnExtensionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AssignedAddOnExtensionInstance: + """ + Build an instance of AssignedAddOnExtensionInstance + + :param payload: Payload response from the API + """ + return AssignedAddOnExtensionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AssignedAddOnExtensionList(ListResource): + + def __init__( + self, + version: Version, + account_sid: str, + resource_sid: str, + assigned_add_on_sid: str, + ): + """ + Initialize the AssignedAddOnExtensionList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resources to read. + :param resource_sid: The SID of the Phone Number to which the Add-on is assigned. + :param assigned_add_on_sid: The SID that uniquely identifies the assigned Add-on installation. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "resource_sid": resource_sid, + "assigned_add_on_sid": assigned_add_on_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/{resource_sid}/AssignedAddOns/{assigned_add_on_sid}/Extensions.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AssignedAddOnExtensionInstance]: + """ + Streams AssignedAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AssignedAddOnExtensionInstance]: + """ + Asynchronously streams AssignedAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssignedAddOnExtensionInstance]: + """ + Lists AssignedAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssignedAddOnExtensionInstance]: + """ + Asynchronously lists AssignedAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssignedAddOnExtensionPage: + """ + Retrieve a single page of AssignedAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssignedAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AssignedAddOnExtensionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssignedAddOnExtensionPage: + """ + Asynchronously retrieve a single page of AssignedAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssignedAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AssignedAddOnExtensionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AssignedAddOnExtensionPage: + """ + Retrieve a specific page of AssignedAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssignedAddOnExtensionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AssignedAddOnExtensionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AssignedAddOnExtensionPage: + """ + Asynchronously retrieve a specific page of AssignedAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssignedAddOnExtensionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AssignedAddOnExtensionPage(self._version, response, self._solution) + + def get(self, sid: str) -> AssignedAddOnExtensionContext: + """ + Constructs a AssignedAddOnExtensionContext + + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + return AssignedAddOnExtensionContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AssignedAddOnExtensionContext: + """ + Constructs a AssignedAddOnExtensionContext + + :param sid: The Twilio-provided string that uniquely identifies the resource to fetch. + """ + return AssignedAddOnExtensionContext( + self._version, + account_sid=self._solution["account_sid"], + resource_sid=self._solution["resource_sid"], + assigned_add_on_sid=self._solution["assigned_add_on_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/local.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/local.py new file mode 100644 index 0000000..4548720 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/local.py @@ -0,0 +1,656 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class LocalInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + class EmergencyAddressStatus(object): + REGISTERED = "registered" + UNREGISTERED = "unregistered" + PENDING_REGISTRATION = "pending-registration" + REGISTRATION_FAILURE = "registration-failure" + PENDING_UNREGISTRATION = "pending-unregistration" + UNREGISTRATION_FAILURE = "unregistration-failure" + + class EmergencyStatus(object): + ACTIVE = "Active" + INACTIVE = "Inactive" + + class VoiceReceiveMode(object): + VOICE = "voice" + FAX = "fax" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource. + :ivar address_sid: The SID of the Address resource associated with the phone number. + :ivar address_requirements: + :ivar api_version: The API version used to start a new TwiML session. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar identity_sid: The SID of the Identity resource that we associate with the phone number. Some regions require an Identity to meet local regulations. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origin: The phone number's origin. `twilio` identifies Twilio-owned phone numbers and `hosted` identifies hosted phone numbers. + :ivar sid: The unique string that that we created to identify the resource. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_receive_mode: + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database ($0.01 per look up). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call when this phone number receives a call. The `voice_url` will not be used if a `voice_application_sid` or a `trunk_sid` is set. + :ivar emergency_status: + :ivar emergency_address_sid: The SID of the emergency address configuration that we use for emergency calling from this phone number. + :ivar emergency_address_status: + :ivar bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :ivar status: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.address_requirements: Optional["LocalInstance.AddressRequirement"] = ( + payload.get("address_requirements") + ) + self.api_version: Optional[str] = payload.get("api_version") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.identity_sid: Optional[str] = payload.get("identity_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.origin: Optional[str] = payload.get("origin") + self.sid: Optional[str] = payload.get("sid") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.uri: Optional[str] = payload.get("uri") + self.voice_receive_mode: Optional["LocalInstance.VoiceReceiveMode"] = ( + payload.get("voice_receive_mode") + ) + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.emergency_status: Optional["LocalInstance.EmergencyStatus"] = payload.get( + "emergency_status" + ) + self.emergency_address_sid: Optional[str] = payload.get("emergency_address_sid") + self.emergency_address_status: Optional[ + "LocalInstance.EmergencyAddressStatus" + ] = payload.get("emergency_address_status") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.status: Optional[str] = payload.get("status") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class LocalPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> LocalInstance: + """ + Build an instance of LocalInstance + + :param payload: Payload response from the API + """ + return LocalInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class LocalList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the LocalList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/Local.json".format( + **self._solution + ) + + def create( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union["LocalInstance.EmergencyStatus", object] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "LocalInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> LocalInstance: + """ + Create the LocalInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created LocalInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return LocalInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union["LocalInstance.EmergencyStatus", object] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "LocalInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> LocalInstance: + """ + Asynchronously create the LocalInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those set on the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created LocalInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return LocalInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[LocalInstance]: + """ + Streams LocalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[LocalInstance]: + """ + Asynchronously streams LocalInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LocalInstance]: + """ + Lists LocalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LocalInstance]: + """ + Asynchronously lists LocalInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LocalPage: + """ + Retrieve a single page of LocalInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LocalInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return LocalPage(self._version, response, self._solution) + + async def page_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LocalPage: + """ + Asynchronously retrieve a single page of LocalInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LocalInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return LocalPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> LocalPage: + """ + Retrieve a specific page of LocalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LocalInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return LocalPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> LocalPage: + """ + Asynchronously retrieve a specific page of LocalInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LocalInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return LocalPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/mobile.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/mobile.py new file mode 100644 index 0000000..6147d4b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/mobile.py @@ -0,0 +1,660 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MobileInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + class EmergencyAddressStatus(object): + REGISTERED = "registered" + UNREGISTERED = "unregistered" + PENDING_REGISTRATION = "pending-registration" + REGISTRATION_FAILURE = "registration-failure" + PENDING_UNREGISTRATION = "pending-unregistration" + UNREGISTRATION_FAILURE = "unregistration-failure" + + class EmergencyStatus(object): + ACTIVE = "Active" + INACTIVE = "Inactive" + + class VoiceReceiveMode(object): + VOICE = "voice" + FAX = "fax" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource. + :ivar address_sid: The SID of the Address resource associated with the phone number. + :ivar address_requirements: + :ivar api_version: The API version used to start a new TwiML session. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar identity_sid: The SID of the Identity resource that we associate with the phone number. Some regions require an Identity to meet local regulations. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origin: The phone number's origin. `twilio` identifies Twilio-owned phone numbers and `hosted` identifies hosted phone numbers. + :ivar sid: The unique string that that we created to identify the resource. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_receive_mode: + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database ($0.01 per look up). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call when the phone number receives a call. The `voice_url` will not be used if a `voice_application_sid` or a `trunk_sid` is set. + :ivar emergency_status: + :ivar emergency_address_sid: The SID of the emergency address configuration that we use for emergency calling from this phone number. + :ivar emergency_address_status: + :ivar bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :ivar status: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.address_requirements: Optional["MobileInstance.AddressRequirement"] = ( + payload.get("address_requirements") + ) + self.api_version: Optional[str] = payload.get("api_version") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.identity_sid: Optional[str] = payload.get("identity_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.origin: Optional[str] = payload.get("origin") + self.sid: Optional[str] = payload.get("sid") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.uri: Optional[str] = payload.get("uri") + self.voice_receive_mode: Optional["MobileInstance.VoiceReceiveMode"] = ( + payload.get("voice_receive_mode") + ) + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.emergency_status: Optional["MobileInstance.EmergencyStatus"] = payload.get( + "emergency_status" + ) + self.emergency_address_sid: Optional[str] = payload.get("emergency_address_sid") + self.emergency_address_status: Optional[ + "MobileInstance.EmergencyAddressStatus" + ] = payload.get("emergency_address_status") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.status: Optional[str] = payload.get("status") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MobilePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MobileInstance: + """ + Build an instance of MobileInstance + + :param payload: Payload response from the API + """ + return MobileInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MobileList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the MobileList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/Mobile.json".format( + **self._solution + ) + + def create( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union[ + "MobileInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "MobileInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> MobileInstance: + """ + Create the MobileInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, the is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those of the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created MobileInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MobileInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union[ + "MobileInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "MobileInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> MobileInstance: + """ + Asynchronously create the MobileInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, the is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all of the `sms_*_url` urls and use those of the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use only those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created MobileInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MobileInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MobileInstance]: + """ + Streams MobileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MobileInstance]: + """ + Asynchronously streams MobileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MobileInstance]: + """ + Lists MobileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MobileInstance]: + """ + Asynchronously lists MobileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MobilePage: + """ + Retrieve a single page of MobileInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MobileInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MobilePage(self._version, response, self._solution) + + async def page_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MobilePage: + """ + Asynchronously retrieve a single page of MobileInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MobileInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MobilePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MobilePage: + """ + Retrieve a specific page of MobileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MobileInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MobilePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MobilePage: + """ + Asynchronously retrieve a specific page of MobileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MobileInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MobilePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/toll_free.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/toll_free.py new file mode 100644 index 0000000..806b7c0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/incoming_phone_number/toll_free.py @@ -0,0 +1,660 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TollFreeInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + class EmergencyAddressStatus(object): + REGISTERED = "registered" + UNREGISTERED = "unregistered" + PENDING_REGISTRATION = "pending-registration" + REGISTRATION_FAILURE = "registration-failure" + PENDING_UNREGISTRATION = "pending-unregistration" + UNREGISTRATION_FAILURE = "unregistration-failure" + + class EmergencyStatus(object): + ACTIVE = "Active" + INACTIVE = "Inactive" + + class VoiceReceiveMode(object): + VOICE = "voice" + FAX = "fax" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resource. + :ivar address_sid: The SID of the Address resource associated with the phone number. + :ivar address_requirements: + :ivar api_version: The API version used to start a new TwiML session. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar identity_sid: The SID of the Identity resource that we associate with the phone number. Some regions require an Identity to meet local regulations. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origin: The phone number's origin. `twilio` identifies Twilio-owned phone numbers and `hosted` identifies hosted phone numbers. + :ivar sid: The unique string that that we created to identify the resource. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_receive_mode: + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database ($0.01 per look up). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call when the phone number receives a call. The `voice_url` will not be used if a `voice_application_sid` or a `trunk_sid` is set. + :ivar emergency_status: + :ivar emergency_address_sid: The SID of the emergency address configuration that we use for emergency calling from this phone number. + :ivar emergency_address_status: + :ivar bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + :ivar status: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.address_requirements: Optional["TollFreeInstance.AddressRequirement"] = ( + payload.get("address_requirements") + ) + self.api_version: Optional[str] = payload.get("api_version") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[str] = payload.get("capabilities") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.identity_sid: Optional[str] = payload.get("identity_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.origin: Optional[str] = payload.get("origin") + self.sid: Optional[str] = payload.get("sid") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.uri: Optional[str] = payload.get("uri") + self.voice_receive_mode: Optional["TollFreeInstance.VoiceReceiveMode"] = ( + payload.get("voice_receive_mode") + ) + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.emergency_status: Optional["TollFreeInstance.EmergencyStatus"] = ( + payload.get("emergency_status") + ) + self.emergency_address_sid: Optional[str] = payload.get("emergency_address_sid") + self.emergency_address_status: Optional[ + "TollFreeInstance.EmergencyAddressStatus" + ] = payload.get("emergency_address_status") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.status: Optional[str] = payload.get("status") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TollFreePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TollFreeInstance: + """ + Build an instance of TollFreeInstance + + :param payload: Payload response from the API + """ + return TollFreeInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TollFreeList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the TollFreeList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/IncomingPhoneNumbers/TollFree.json".format( + **self._solution + ) + + def create( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union[ + "TollFreeInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "TollFreeInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> TollFreeInstance: + """ + Create the TollFreeInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an Identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created TollFreeInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TollFreeInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + phone_number: str, + api_version: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + voice_application_sid: Union[str, object] = values.unset, + voice_caller_id_lookup: Union[bool, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + identity_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + emergency_status: Union[ + "TollFreeInstance.EmergencyStatus", object + ] = values.unset, + emergency_address_sid: Union[str, object] = values.unset, + trunk_sid: Union[str, object] = values.unset, + voice_receive_mode: Union[ + "TollFreeInstance.VoiceReceiveMode", object + ] = values.unset, + bundle_sid: Union[str, object] = values.unset, + ) -> TollFreeInstance: + """ + Asynchronously create the TollFreeInstance + + :param phone_number: The phone number to purchase specified in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param api_version: The API version to use for incoming calls made to the new phone number. The default is `2010-04-01`. + :param friendly_name: A descriptive string that you created to describe the new phone number. It can be up to 64 characters long. By default, this is a formatted version of the phone number. + :param sms_application_sid: The SID of the application that should handle SMS messages sent to the new phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :param sms_fallback_method: The HTTP method that we should use to call `sms_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_fallback_url: The URL that we should call when an error occurs while requesting or executing the TwiML defined by `sms_url`. + :param sms_method: The HTTP method that we should use to call `sms_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param sms_url: The URL we should call when the new phone number receives an incoming SMS message. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_application_sid: The SID of the application we should use to handle calls to the new phone number. If a `voice_application_sid` is present, we ignore all of the voice urls and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :param voice_caller_id_lookup: Whether to lookup the caller's name from the CNAM database and post it to your app. Can be: `true` or `false` and defaults to `false`. + :param voice_fallback_method: The HTTP method that we should use to call `voice_fallback_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs retrieving or executing the TwiML requested by `url`. + :param voice_method: The HTTP method that we should use to call `voice_url`. Can be: `GET` or `POST` and defaults to `POST`. + :param voice_url: The URL that we should call to answer a call to the new phone number. The `voice_url` will not be called if a `voice_application_sid` or a `trunk_sid` is set. + :param identity_sid: The SID of the Identity resource that we should associate with the new phone number. Some regions require an Identity to meet local regulations. + :param address_sid: The SID of the Address resource we should associate with the new phone number. Some regions require addresses to meet local regulations. + :param emergency_status: + :param emergency_address_sid: The SID of the emergency address configuration to use for emergency calling from the new phone number. + :param trunk_sid: The SID of the Trunk we should use to handle calls to the new phone number. If a `trunk_sid` is present, we ignore all of the voice urls and voice applications and use only those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :param voice_receive_mode: + :param bundle_sid: The SID of the Bundle resource that you associate with the phone number. Some regions require a Bundle to meet local Regulations. + + :returns: The created TollFreeInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ApiVersion": api_version, + "FriendlyName": friendly_name, + "SmsApplicationSid": sms_application_sid, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "VoiceApplicationSid": voice_application_sid, + "VoiceCallerIdLookup": serialize.boolean_to_string( + voice_caller_id_lookup + ), + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "IdentitySid": identity_sid, + "AddressSid": address_sid, + "EmergencyStatus": emergency_status, + "EmergencyAddressSid": emergency_address_sid, + "TrunkSid": trunk_sid, + "VoiceReceiveMode": voice_receive_mode, + "BundleSid": bundle_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TollFreeInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TollFreeInstance]: + """ + Streams TollFreeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TollFreeInstance]: + """ + Asynchronously streams TollFreeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollFreeInstance]: + """ + Lists TollFreeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollFreeInstance]: + """ + Asynchronously lists TollFreeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param str friendly_name: A string that identifies the resources to read. + :param str phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param str origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + beta=beta, + friendly_name=friendly_name, + phone_number=phone_number, + origin=origin, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollFreePage: + """ + Retrieve a single page of TollFreeInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollFreeInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TollFreePage(self._version, response, self._solution) + + async def page_async( + self, + beta: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + origin: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollFreePage: + """ + Asynchronously retrieve a single page of TollFreeInstance records from the API. + Request is executed immediately + + :param beta: Whether to include phone numbers new to the Twilio platform. Can be: `true` or `false` and the default is `true`. + :param friendly_name: A string that identifies the resources to read. + :param phone_number: The phone numbers of the IncomingPhoneNumber resources to read. You can specify partial numbers and use '*' as a wildcard for any digit. + :param origin: Whether to include phone numbers based on their origin. Can be: `twilio` or `hosted`. By default, phone numbers of all origin are included. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollFreeInstance + """ + data = values.of( + { + "Beta": serialize.boolean_to_string(beta), + "FriendlyName": friendly_name, + "PhoneNumber": phone_number, + "Origin": origin, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TollFreePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TollFreePage: + """ + Retrieve a specific page of TollFreeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollFreeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TollFreePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TollFreePage: + """ + Asynchronously retrieve a specific page of TollFreeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollFreeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TollFreePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/key.py new file mode 100644 index 0000000..68a9feb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/key.py @@ -0,0 +1,542 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class KeyInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the Key resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[KeyContext] = None + + @property + def _proxy(self) -> "KeyContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: KeyContext for this KeyInstance + """ + if self._context is None: + self._context = KeyContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "KeyInstance": + """ + Fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "KeyInstance": + """ + Asynchronous coroutine to fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: Union[str, object] = values.unset) -> "KeyInstance": + """ + Update the KeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated KeyInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "KeyInstance": + """ + Asynchronous coroutine to update the KeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated KeyInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class KeyContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the KeyContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Key resources to update. + :param sid: The Twilio-provided string that uniquely identifies the Key resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Keys/{sid}.json".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> KeyInstance: + """ + Fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return KeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> KeyInstance: + """ + Asynchronous coroutine to fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return KeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update(self, friendly_name: Union[str, object] = values.unset) -> KeyInstance: + """ + Update the KeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated KeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> KeyInstance: + """ + Asynchronous coroutine to update the KeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated KeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class KeyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> KeyInstance: + """ + Build an instance of KeyInstance + + :param payload: Payload response from the API + """ + return KeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class KeyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the KeyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Key resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Keys.json".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[KeyInstance]: + """ + Streams KeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[KeyInstance]: + """ + Asynchronously streams KeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[KeyInstance]: + """ + Lists KeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[KeyInstance]: + """ + Asynchronously lists KeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> KeyPage: + """ + Retrieve a single page of KeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of KeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return KeyPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> KeyPage: + """ + Asynchronously retrieve a single page of KeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of KeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return KeyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> KeyPage: + """ + Retrieve a specific page of KeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of KeyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return KeyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> KeyPage: + """ + Asynchronously retrieve a specific page of KeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of KeyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return KeyPage(self._version, response, self._solution) + + def get(self, sid: str) -> KeyContext: + """ + Constructs a KeyContext + + :param sid: The Twilio-provided string that uniquely identifies the Key resource to update. + """ + return KeyContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> KeyContext: + """ + Constructs a KeyContext + + :param sid: The Twilio-provided string that uniquely identifies the Key resource to update. + """ + return KeyContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__init__.py new file mode 100644 index 0000000..972d80b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__init__.py @@ -0,0 +1,975 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.message.feedback import FeedbackList +from twilio.rest.api.v2010.account.message.media import MediaList + + +class MessageInstance(InstanceResource): + + class AddressRetention(object): + RETAIN = "retain" + OBFUSCATE = "obfuscate" + + class ContentRetention(object): + RETAIN = "retain" + DISCARD = "discard" + + class Direction(object): + INBOUND = "inbound" + OUTBOUND_API = "outbound-api" + OUTBOUND_CALL = "outbound-call" + OUTBOUND_REPLY = "outbound-reply" + + class RiskCheck(object): + ENABLE = "enable" + DISABLE = "disable" + + class ScheduleType(object): + FIXED = "fixed" + + class Status(object): + QUEUED = "queued" + SENDING = "sending" + SENT = "sent" + FAILED = "failed" + DELIVERED = "delivered" + UNDELIVERED = "undelivered" + RECEIVING = "receiving" + RECEIVED = "received" + ACCEPTED = "accepted" + SCHEDULED = "scheduled" + READ = "read" + PARTIALLY_DELIVERED = "partially_delivered" + CANCELED = "canceled" + + class UpdateStatus(object): + CANCELED = "canceled" + + """ + :ivar body: The text content of the message + :ivar num_segments: The number of segments that make up the complete message. SMS message bodies that exceed the [character limit](https://www.twilio.com/docs/glossary/what-sms-character-limit) are segmented and charged as multiple messages. Note: For messages sent via a Messaging Service, `num_segments` is initially `0`, since a sender hasn't yet been assigned. + :ivar direction: + :ivar from_: The sender's phone number (in [E.164](https://en.wikipedia.org/wiki/E.164) format), [alphanumeric sender ID](https://www.twilio.com/docs/sms/quickstart), [Wireless SIM](https://www.twilio.com/docs/iot/wireless/programmable-wireless-send-machine-machine-sms-commands), [short code](https://www.twilio.com/en-us/messaging/channels/sms/short-codes), or [channel address](https://www.twilio.com/docs/messaging/channels) (e.g., `whatsapp:+15554449999`). For incoming messages, this is the number or channel address of the sender. For outgoing messages, this value is a Twilio phone number, alphanumeric sender ID, short code, or channel address from which the message is sent. + :ivar to: The recipient's phone number (in [E.164](https://en.wikipedia.org/wiki/E.164) format) or [channel address](https://www.twilio.com/docs/messaging/channels) (e.g. `whatsapp:+15552229999`) + :ivar date_updated: The [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) timestamp (in GMT) of when the Message resource was last updated + :ivar price: The amount billed for the message in the currency specified by `price_unit`. The `price` is populated after the message has been sent/received, and may not be immediately availalble. View the [Pricing page](https://www.twilio.com/en-us/pricing) for more details. + :ivar error_message: The description of the `error_code` if the Message `status` is `failed` or `undelivered`. If no error was encountered, the value is `null`. + :ivar uri: The URI of the Message resource, relative to `https://api.twilio.com`. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with the Message resource + :ivar num_media: The number of media files associated with the Message resource. + :ivar status: + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) associated with the Message resource. The value is `null` if a Messaging Service was not used. + :ivar sid: The unique, Twilio-provided string that identifies the Message resource. + :ivar date_sent: The [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) timestamp (in GMT) of when the Message was sent. For an outgoing message, this is when Twilio sent the message. For an incoming message, this is when Twilio sent the HTTP request to your incoming message webhook URL. + :ivar date_created: The [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) timestamp (in GMT) of when the Message resource was created + :ivar error_code: The [error code](https://www.twilio.com/docs/api/errors) returned if the Message `status` is `failed` or `undelivered`. If no error was encountered, the value is `null`. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar api_version: The API version used to process the Message + :ivar subresource_uris: A list of related resources identified by their URIs relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.body: Optional[str] = payload.get("body") + self.num_segments: Optional[str] = payload.get("num_segments") + self.direction: Optional["MessageInstance.Direction"] = payload.get("direction") + self.from_: Optional[str] = payload.get("from") + self.to: Optional[str] = payload.get("to") + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.price: Optional[str] = payload.get("price") + self.error_message: Optional[str] = payload.get("error_message") + self.uri: Optional[str] = payload.get("uri") + self.account_sid: Optional[str] = payload.get("account_sid") + self.num_media: Optional[str] = payload.get("num_media") + self.status: Optional["MessageInstance.Status"] = payload.get("status") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_sent: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_sent") + ) + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.api_version: Optional[str] = payload.get("api_version") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + body: Union[str, object] = values.unset, + status: Union["MessageInstance.UpdateStatus", object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param body: The new `body` of the Message resource. To redact the text content of a Message, this parameter's value must be an empty string + :param status: + + :returns: The updated MessageInstance + """ + return self._proxy.update( + body=body, + status=status, + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + status: Union["MessageInstance.UpdateStatus", object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param body: The new `body` of the Message resource. To redact the text content of a Message, this parameter's value must be an empty string + :param status: + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + body=body, + status=status, + ) + + @property + def feedback(self) -> FeedbackList: + """ + Access the feedback + """ + return self._proxy.feedback + + @property + def media(self) -> MediaList: + """ + Access the media + """ + return self._proxy.media + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Message resources to update. + :param sid: The SID of the Message resource to be updated + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Messages/{sid}.json".format( + **self._solution + ) + + self._feedback: Optional[FeedbackList] = None + self._media: Optional[MediaList] = None + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + body: Union[str, object] = values.unset, + status: Union["MessageInstance.UpdateStatus", object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param body: The new `body` of the Message resource. To redact the text content of a Message, this parameter's value must be an empty string + :param status: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + status: Union["MessageInstance.UpdateStatus", object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param body: The new `body` of the Message resource. To redact the text content of a Message, this parameter's value must be an empty string + :param status: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def feedback(self) -> FeedbackList: + """ + Access the feedback + """ + if self._feedback is None: + self._feedback = FeedbackList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._feedback + + @property + def media(self) -> MediaList: + """ + Access the media + """ + if self._media is None: + self._media = MediaList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._media + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with the Message resources. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Messages.json".format(**self._solution) + + def create( + self, + to: str, + status_callback: Union[str, object] = values.unset, + application_sid: Union[str, object] = values.unset, + max_price: Union[float, object] = values.unset, + provide_feedback: Union[bool, object] = values.unset, + attempt: Union[int, object] = values.unset, + validity_period: Union[int, object] = values.unset, + force_delivery: Union[bool, object] = values.unset, + content_retention: Union[ + "MessageInstance.ContentRetention", object + ] = values.unset, + address_retention: Union[ + "MessageInstance.AddressRetention", object + ] = values.unset, + smart_encoded: Union[bool, object] = values.unset, + persistent_action: Union[List[str], object] = values.unset, + shorten_urls: Union[bool, object] = values.unset, + schedule_type: Union["MessageInstance.ScheduleType", object] = values.unset, + send_at: Union[datetime, object] = values.unset, + send_as_mms: Union[bool, object] = values.unset, + content_variables: Union[str, object] = values.unset, + risk_check: Union["MessageInstance.RiskCheck", object] = values.unset, + from_: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_url: Union[List[str], object] = values.unset, + content_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param to: The recipient's phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (for SMS/MMS) or [channel address](https://www.twilio.com/docs/messaging/channels), e.g. `whatsapp:+15552229999`. + :param status_callback: The URL of the endpoint to which Twilio sends [Message status callback requests](https://www.twilio.com/docs/sms/api/message-resource#twilios-request-to-the-statuscallback-url). URL must contain a valid hostname and underscores are not allowed. If you include this parameter with the `messaging_service_sid`, Twilio uses this URL instead of the Status Callback URL of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource). + :param application_sid: The SID of the associated [TwiML Application](https://www.twilio.com/docs/usage/api/applications). [Message status callback requests](https://www.twilio.com/docs/sms/api/message-resource#twilios-request-to-the-statuscallback-url) are sent to the TwiML App's `message_status_callback` URL. Note that the `status_callback` parameter of a request takes priority over the `application_sid` parameter; if both are included `application_sid` is ignored. + :param max_price: [DEPRECATED] This parameter will no longer have any effect as of 2024-06-03. + :param provide_feedback: Boolean indicating whether or not you intend to provide delivery confirmation feedback to Twilio (used in conjunction with the [Message Feedback subresource](https://www.twilio.com/docs/sms/api/message-feedback-resource)). Default value is `false`. + :param attempt: Total number of attempts made (including this request) to send the message regardless of the provider used + :param validity_period: The maximum length in seconds that the Message can remain in Twilio's outgoing message queue. If a queued Message exceeds the `validity_period`, the Message is not sent. Accepted values are integers from `1` to `14400`. Default value is `14400`. A `validity_period` greater than `5` is recommended. [Learn more about the validity period](https://www.twilio.com/blog/take-more-control-of-outbound-messages-using-validity-period-html) + :param force_delivery: Reserved + :param content_retention: + :param address_retention: + :param smart_encoded: Whether to detect Unicode characters that have a similar GSM-7 character and replace them. Can be: `true` or `false`. + :param persistent_action: Rich actions for non-SMS/MMS channels. Used for [sending location in WhatsApp messages](https://www.twilio.com/docs/whatsapp/message-features#location-messages-with-whatsapp). + :param shorten_urls: For Messaging Services with [Link Shortening configured](https://www.twilio.com/docs/messaging/features/link-shortening) only: A Boolean indicating whether or not Twilio should shorten links in the `body` of the Message. Default value is `false`. If `true`, the `messaging_service_sid` parameter must also be provided. + :param schedule_type: + :param send_at: The time that Twilio will send the message. Must be in ISO 8601 format. + :param send_as_mms: If set to `true`, Twilio delivers the message as a single MMS message, regardless of the presence of media. + :param content_variables: For [Content Editor/API](https://www.twilio.com/docs/content) only: Key-value pairs of [Template variables](https://www.twilio.com/docs/content/using-variables-with-content-api) and their substitution values. `content_sid` parameter must also be provided. If values are not defined in the `content_variables` parameter, the [Template's default placeholder values](https://www.twilio.com/docs/content/content-api-resources#create-templates) are used. + :param risk_check: + :param from_: The sender's Twilio phone number (in [E.164](https://en.wikipedia.org/wiki/E.164) format), [alphanumeric sender ID](https://www.twilio.com/docs/sms/quickstart), [Wireless SIM](https://www.twilio.com/docs/iot/wireless/programmable-wireless-send-machine-machine-sms-commands), [short code](https://www.twilio.com/en-us/messaging/channels/sms/short-codes), or [channel address](https://www.twilio.com/docs/messaging/channels) (e.g., `whatsapp:+15554449999`). The value of the `from` parameter must be a sender that is hosted within Twilio and belongs to the Account creating the Message. If you are using `messaging_service_sid`, this parameter can be empty (Twilio assigns a `from` value from the Messaging Service's Sender Pool) or you can provide a specific sender from your Sender Pool. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) you want to associate with the Message. When this parameter is provided and the `from` parameter is omitted, Twilio selects the optimal sender from the Messaging Service's Sender Pool. You may also provide a `from` parameter if you want to use a specific Sender from the Sender Pool. + :param body: The text content of the outgoing message. Can be up to 1,600 characters in length. SMS only: If the `body` contains more than 160 [GSM-7](https://www.twilio.com/docs/glossary/what-is-gsm-7-character-encoding) characters (or 70 [UCS-2](https://www.twilio.com/docs/glossary/what-is-ucs-2-character-encoding) characters), the message is segmented and charged accordingly. For long `body` text, consider using the [send_as_mms parameter](https://www.twilio.com/blog/mms-for-long-text-messages). + :param media_url: The URL of media to include in the Message content. `jpeg`, `jpg`, `gif`, and `png` file types are fully supported by Twilio and content is formatted for delivery on destination devices. The media size limit is 5 MB for supported file types (`jpeg`, `jpg`, `png`, `gif`) and 500 KB for [other types](https://www.twilio.com/docs/messaging/guides/accepted-mime-types) of accepted media. To send more than one image in the message, provide multiple `media_url` parameters in the POST request. You can include up to ten `media_url` parameters per message. [International](https://support.twilio.com/hc/en-us/articles/223179808-Sending-and-receiving-MMS-messages) and [carrier](https://support.twilio.com/hc/en-us/articles/223133707-Is-MMS-supported-for-all-carriers-in-US-and-Canada-) limits apply. + :param content_sid: For [Content Editor/API](https://www.twilio.com/docs/content) only: The SID of the Content Template to be used with the Message, e.g., `HXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`. If this parameter is not provided, a Content Template is not used. Find the SID in the Console on the Content Editor page. For Content API users, the SID is found in Twilio's response when [creating the Template](https://www.twilio.com/docs/content/content-api-resources#create-templates) or by [fetching your Templates](https://www.twilio.com/docs/content/content-api-resources#fetch-all-content-resources). + + :returns: The created MessageInstance + """ + + data = values.of( + { + "To": to, + "StatusCallback": status_callback, + "ApplicationSid": application_sid, + "MaxPrice": max_price, + "ProvideFeedback": serialize.boolean_to_string(provide_feedback), + "Attempt": attempt, + "ValidityPeriod": validity_period, + "ForceDelivery": serialize.boolean_to_string(force_delivery), + "ContentRetention": content_retention, + "AddressRetention": address_retention, + "SmartEncoded": serialize.boolean_to_string(smart_encoded), + "PersistentAction": serialize.map(persistent_action, lambda e: e), + "ShortenUrls": serialize.boolean_to_string(shorten_urls), + "ScheduleType": schedule_type, + "SendAt": serialize.iso8601_datetime(send_at), + "SendAsMms": serialize.boolean_to_string(send_as_mms), + "ContentVariables": content_variables, + "RiskCheck": risk_check, + "From": from_, + "MessagingServiceSid": messaging_service_sid, + "Body": body, + "MediaUrl": serialize.map(media_url, lambda e: e), + "ContentSid": content_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + to: str, + status_callback: Union[str, object] = values.unset, + application_sid: Union[str, object] = values.unset, + max_price: Union[float, object] = values.unset, + provide_feedback: Union[bool, object] = values.unset, + attempt: Union[int, object] = values.unset, + validity_period: Union[int, object] = values.unset, + force_delivery: Union[bool, object] = values.unset, + content_retention: Union[ + "MessageInstance.ContentRetention", object + ] = values.unset, + address_retention: Union[ + "MessageInstance.AddressRetention", object + ] = values.unset, + smart_encoded: Union[bool, object] = values.unset, + persistent_action: Union[List[str], object] = values.unset, + shorten_urls: Union[bool, object] = values.unset, + schedule_type: Union["MessageInstance.ScheduleType", object] = values.unset, + send_at: Union[datetime, object] = values.unset, + send_as_mms: Union[bool, object] = values.unset, + content_variables: Union[str, object] = values.unset, + risk_check: Union["MessageInstance.RiskCheck", object] = values.unset, + from_: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_url: Union[List[str], object] = values.unset, + content_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param to: The recipient's phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format (for SMS/MMS) or [channel address](https://www.twilio.com/docs/messaging/channels), e.g. `whatsapp:+15552229999`. + :param status_callback: The URL of the endpoint to which Twilio sends [Message status callback requests](https://www.twilio.com/docs/sms/api/message-resource#twilios-request-to-the-statuscallback-url). URL must contain a valid hostname and underscores are not allowed. If you include this parameter with the `messaging_service_sid`, Twilio uses this URL instead of the Status Callback URL of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource). + :param application_sid: The SID of the associated [TwiML Application](https://www.twilio.com/docs/usage/api/applications). [Message status callback requests](https://www.twilio.com/docs/sms/api/message-resource#twilios-request-to-the-statuscallback-url) are sent to the TwiML App's `message_status_callback` URL. Note that the `status_callback` parameter of a request takes priority over the `application_sid` parameter; if both are included `application_sid` is ignored. + :param max_price: [DEPRECATED] This parameter will no longer have any effect as of 2024-06-03. + :param provide_feedback: Boolean indicating whether or not you intend to provide delivery confirmation feedback to Twilio (used in conjunction with the [Message Feedback subresource](https://www.twilio.com/docs/sms/api/message-feedback-resource)). Default value is `false`. + :param attempt: Total number of attempts made (including this request) to send the message regardless of the provider used + :param validity_period: The maximum length in seconds that the Message can remain in Twilio's outgoing message queue. If a queued Message exceeds the `validity_period`, the Message is not sent. Accepted values are integers from `1` to `14400`. Default value is `14400`. A `validity_period` greater than `5` is recommended. [Learn more about the validity period](https://www.twilio.com/blog/take-more-control-of-outbound-messages-using-validity-period-html) + :param force_delivery: Reserved + :param content_retention: + :param address_retention: + :param smart_encoded: Whether to detect Unicode characters that have a similar GSM-7 character and replace them. Can be: `true` or `false`. + :param persistent_action: Rich actions for non-SMS/MMS channels. Used for [sending location in WhatsApp messages](https://www.twilio.com/docs/whatsapp/message-features#location-messages-with-whatsapp). + :param shorten_urls: For Messaging Services with [Link Shortening configured](https://www.twilio.com/docs/messaging/features/link-shortening) only: A Boolean indicating whether or not Twilio should shorten links in the `body` of the Message. Default value is `false`. If `true`, the `messaging_service_sid` parameter must also be provided. + :param schedule_type: + :param send_at: The time that Twilio will send the message. Must be in ISO 8601 format. + :param send_as_mms: If set to `true`, Twilio delivers the message as a single MMS message, regardless of the presence of media. + :param content_variables: For [Content Editor/API](https://www.twilio.com/docs/content) only: Key-value pairs of [Template variables](https://www.twilio.com/docs/content/using-variables-with-content-api) and their substitution values. `content_sid` parameter must also be provided. If values are not defined in the `content_variables` parameter, the [Template's default placeholder values](https://www.twilio.com/docs/content/content-api-resources#create-templates) are used. + :param risk_check: + :param from_: The sender's Twilio phone number (in [E.164](https://en.wikipedia.org/wiki/E.164) format), [alphanumeric sender ID](https://www.twilio.com/docs/sms/quickstart), [Wireless SIM](https://www.twilio.com/docs/iot/wireless/programmable-wireless-send-machine-machine-sms-commands), [short code](https://www.twilio.com/en-us/messaging/channels/sms/short-codes), or [channel address](https://www.twilio.com/docs/messaging/channels) (e.g., `whatsapp:+15554449999`). The value of the `from` parameter must be a sender that is hosted within Twilio and belongs to the Account creating the Message. If you are using `messaging_service_sid`, this parameter can be empty (Twilio assigns a `from` value from the Messaging Service's Sender Pool) or you can provide a specific sender from your Sender Pool. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) you want to associate with the Message. When this parameter is provided and the `from` parameter is omitted, Twilio selects the optimal sender from the Messaging Service's Sender Pool. You may also provide a `from` parameter if you want to use a specific Sender from the Sender Pool. + :param body: The text content of the outgoing message. Can be up to 1,600 characters in length. SMS only: If the `body` contains more than 160 [GSM-7](https://www.twilio.com/docs/glossary/what-is-gsm-7-character-encoding) characters (or 70 [UCS-2](https://www.twilio.com/docs/glossary/what-is-ucs-2-character-encoding) characters), the message is segmented and charged accordingly. For long `body` text, consider using the [send_as_mms parameter](https://www.twilio.com/blog/mms-for-long-text-messages). + :param media_url: The URL of media to include in the Message content. `jpeg`, `jpg`, `gif`, and `png` file types are fully supported by Twilio and content is formatted for delivery on destination devices. The media size limit is 5 MB for supported file types (`jpeg`, `jpg`, `png`, `gif`) and 500 KB for [other types](https://www.twilio.com/docs/messaging/guides/accepted-mime-types) of accepted media. To send more than one image in the message, provide multiple `media_url` parameters in the POST request. You can include up to ten `media_url` parameters per message. [International](https://support.twilio.com/hc/en-us/articles/223179808-Sending-and-receiving-MMS-messages) and [carrier](https://support.twilio.com/hc/en-us/articles/223133707-Is-MMS-supported-for-all-carriers-in-US-and-Canada-) limits apply. + :param content_sid: For [Content Editor/API](https://www.twilio.com/docs/content) only: The SID of the Content Template to be used with the Message, e.g., `HXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`. If this parameter is not provided, a Content Template is not used. Find the SID in the Console on the Content Editor page. For Content API users, the SID is found in Twilio's response when [creating the Template](https://www.twilio.com/docs/content/content-api-resources#create-templates) or by [fetching your Templates](https://www.twilio.com/docs/content/content-api-resources#fetch-all-content-resources). + + :returns: The created MessageInstance + """ + + data = values.of( + { + "To": to, + "StatusCallback": status_callback, + "ApplicationSid": application_sid, + "MaxPrice": max_price, + "ProvideFeedback": serialize.boolean_to_string(provide_feedback), + "Attempt": attempt, + "ValidityPeriod": validity_period, + "ForceDelivery": serialize.boolean_to_string(force_delivery), + "ContentRetention": content_retention, + "AddressRetention": address_retention, + "SmartEncoded": serialize.boolean_to_string(smart_encoded), + "PersistentAction": serialize.map(persistent_action, lambda e: e), + "ShortenUrls": serialize.boolean_to_string(shorten_urls), + "ScheduleType": schedule_type, + "SendAt": serialize.iso8601_datetime(send_at), + "SendAsMms": serialize.boolean_to_string(send_as_mms), + "ContentVariables": content_variables, + "RiskCheck": risk_check, + "From": from_, + "MessagingServiceSid": messaging_service_sid, + "Body": body, + "MediaUrl": serialize.map(media_url, lambda e: e), + "ContentSid": content_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param str from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param datetime date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + to=to, + from_=from_, + date_sent=date_sent, + date_sent_before=date_sent_before, + date_sent_after=date_sent_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param str from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param datetime date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + to=to, + from_=from_, + date_sent=date_sent, + date_sent_before=date_sent_before, + date_sent_after=date_sent_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param str from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param datetime date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + to=to, + from_=from_, + date_sent=date_sent, + date_sent_before=date_sent_before, + date_sent_after=date_sent_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param str from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param datetime date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param datetime date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + to=to, + from_=from_, + date_sent=date_sent, + date_sent_before=date_sent_before, + date_sent_after=date_sent_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "To": to, + "From": from_, + "DateSent": serialize.iso8601_datetime(date_sent), + "DateSent<": serialize.iso8601_datetime(date_sent_before), + "DateSent>": serialize.iso8601_datetime(date_sent_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + date_sent: Union[datetime, object] = values.unset, + date_sent_before: Union[datetime, object] = values.unset, + date_sent_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param to: Filter by recipient. For example: Set this `to` parameter to `+15558881111` to retrieve a list of Message resources with `to` properties of `+15558881111` + :param from_: Filter by sender. For example: Set this `from` parameter to `+15552229999` to retrieve a list of Message resources with `from` properties of `+15552229999` + :param date_sent: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param date_sent_before: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param date_sent_after: Filter by Message `sent_date`. Accepts GMT dates in the following formats: `YYYY-MM-DD` (to find Messages with a specific `sent_date`), `<=YYYY-MM-DD` (to find Messages with `sent_date`s on and before a specific date), and `>=YYYY-MM-DD` (to find Messages with `sent_dates` on and after a specific date). + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "To": to, + "From": from_, + "DateSent": serialize.iso8601_datetime(date_sent), + "DateSent<": serialize.iso8601_datetime(date_sent_before), + "DateSent>": serialize.iso8601_datetime(date_sent_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The SID of the Message resource to be updated + """ + return MessageContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The SID of the Message resource to be updated + """ + return MessageContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..08ae60b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/feedback.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/feedback.cpython-310.pyc new file mode 100644 index 0000000..8099cee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/feedback.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/media.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/media.cpython-310.pyc new file mode 100644 index 0000000..80d622f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/__pycache__/media.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/feedback.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/feedback.py new file mode 100644 index 0000000..2ccce06 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/feedback.py @@ -0,0 +1,164 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FeedbackInstance(InstanceResource): + + class Outcome(object): + CONFIRMED = "confirmed" + UNCONFIRMED = "unconfirmed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with this MessageFeedback resource. + :ivar message_sid: The SID of the Message resource associated with this MessageFeedback resource. + :ivar outcome: + :ivar date_created: The date and time in GMT when this MessageFeedback resource was created, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when this MessageFeedback resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + message_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.message_sid: Optional[str] = payload.get("message_sid") + self.outcome: Optional["FeedbackInstance.Outcome"] = payload.get("outcome") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "message_sid": message_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FeedbackList(ListResource): + + def __init__(self, version: Version, account_sid: str, message_sid: str): + """ + Initialize the FeedbackList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with the Message resource for which to create MessageFeedback. + :param message_sid: The SID of the Message resource for which to create MessageFeedback. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "message_sid": message_sid, + } + self._uri = ( + "/Accounts/{account_sid}/Messages/{message_sid}/Feedback.json".format( + **self._solution + ) + ) + + def create( + self, outcome: Union["FeedbackInstance.Outcome", object] = values.unset + ) -> FeedbackInstance: + """ + Create the FeedbackInstance + + :param outcome: + + :returns: The created FeedbackInstance + """ + + data = values.of( + { + "Outcome": outcome, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FeedbackInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + ) + + async def create_async( + self, outcome: Union["FeedbackInstance.Outcome", object] = values.unset + ) -> FeedbackInstance: + """ + Asynchronously create the FeedbackInstance + + :param outcome: + + :returns: The created FeedbackInstance + """ + + data = values.of( + { + "Outcome": outcome, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FeedbackInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/media.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/media.py new file mode 100644 index 0000000..4068d0c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/message/media.py @@ -0,0 +1,548 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MediaInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with this Media resource. + :ivar content_type: The default [MIME type](https://en.wikipedia.org/wiki/Internet_media_type) of the media, for example `image/jpeg`, `image/png`, or `image/gif`. + :ivar date_created: The date and time in GMT when this Media resource was created, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when this Media resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar parent_sid: The SID of the Message resource that is associated with this Media resource. + :ivar sid: The unique string that identifies this Media resource. + :ivar uri: The URI of this Media resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + message_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.content_type: Optional[str] = payload.get("content_type") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.parent_sid: Optional[str] = payload.get("parent_sid") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "message_sid": message_sid, + "sid": sid or self.sid, + } + self._context: Optional[MediaContext] = None + + @property + def _proxy(self) -> "MediaContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MediaContext for this MediaInstance + """ + if self._context is None: + self._context = MediaContext( + self._version, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MediaInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MediaInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MediaInstance": + """ + Fetch the MediaInstance + + + :returns: The fetched MediaInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MediaInstance": + """ + Asynchronous coroutine to fetch the MediaInstance + + + :returns: The fetched MediaInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MediaContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, message_sid: str, sid: str): + """ + Initialize the MediaContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) associated with the Media resource. + :param message_sid: The SID of the Message resource that is associated with the Media resource. + :param sid: The Twilio-provided string that uniquely identifies the Media resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "message_sid": message_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/Messages/{message_sid}/Media/{sid}.json".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the MediaInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MediaInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MediaInstance: + """ + Fetch the MediaInstance + + + :returns: The fetched MediaInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MediaInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MediaInstance: + """ + Asynchronous coroutine to fetch the MediaInstance + + + :returns: The fetched MediaInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MediaInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MediaPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MediaInstance: + """ + Build an instance of MediaInstance + + :param payload: Payload response from the API + """ + return MediaInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MediaList(ListResource): + + def __init__(self, version: Version, account_sid: str, message_sid: str): + """ + Initialize the MediaList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that is associated with the Media resources. + :param message_sid: The SID of the Message resource that is associated with the Media resources. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "message_sid": message_sid, + } + self._uri = "/Accounts/{account_sid}/Messages/{message_sid}/Media.json".format( + **self._solution + ) + + def stream( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MediaInstance]: + """ + Streams MediaInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MediaInstance]: + """ + Asynchronously streams MediaInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MediaInstance]: + """ + Lists MediaInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MediaInstance]: + """ + Asynchronously lists MediaInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param datetime date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MediaPage: + """ + Retrieve a single page of MediaInstance records from the API. + Request is executed immediately + + :param date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MediaInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateCreated<": serialize.iso8601_datetime(date_created_before), + "DateCreated>": serialize.iso8601_datetime(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MediaPage(self._version, response, self._solution) + + async def page_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MediaPage: + """ + Asynchronously retrieve a single page of MediaInstance records from the API. + Request is executed immediately + + :param date_created: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param date_created_before: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param date_created_after: Only include Media resources that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read Media that were created on this date. You can also specify an inequality, such as `StartTime<=YYYY-MM-DD`, to read Media that were created on or before midnight of this date, and `StartTime>=YYYY-MM-DD` to read Media that were created on or after midnight of this date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MediaInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateCreated<": serialize.iso8601_datetime(date_created_before), + "DateCreated>": serialize.iso8601_datetime(date_created_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MediaPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MediaPage: + """ + Retrieve a specific page of MediaInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MediaInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MediaPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MediaPage: + """ + Asynchronously retrieve a specific page of MediaInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MediaInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MediaPage(self._version, response, self._solution) + + def get(self, sid: str) -> MediaContext: + """ + Constructs a MediaContext + + :param sid: The Twilio-provided string that uniquely identifies the Media resource to fetch. + """ + return MediaContext( + self._version, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MediaContext: + """ + Constructs a MediaContext + + :param sid: The Twilio-provided string that uniquely identifies the Media resource to fetch. + """ + return MediaContext( + self._version, + account_sid=self._solution["account_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_key.py new file mode 100644 index 0000000..015249d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_key.py @@ -0,0 +1,138 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NewKeyInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the NewKey resource. You will use this as the basic-auth `user` when authenticating to the API. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT that the API Key was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the new API Key was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar secret: The secret your application uses to sign Access Tokens and to authenticate to the REST API (you will use this as the basic-auth `password`). **Note that for security reasons, this field is ONLY returned when the API Key is first created.** + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.secret: Optional[str] = payload.get("secret") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NewKeyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the NewKeyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that will be responsible for the new Key resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Keys.json".format(**self._solution) + + def create( + self, friendly_name: Union[str, object] = values.unset + ) -> NewKeyInstance: + """ + Create the NewKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created NewKeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NewKeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> NewKeyInstance: + """ + Asynchronously create the NewKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created NewKeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NewKeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_signing_key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_signing_key.py new file mode 100644 index 0000000..6bfa323 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/new_signing_key.py @@ -0,0 +1,138 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NewSigningKeyInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the NewSigningKey resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar secret: The secret your application uses to sign Access Tokens and to authenticate to the REST API (you will use this as the basic-auth `password`). **Note that for security reasons, this field is ONLY returned when the API Key is first created.** + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.secret: Optional[str] = payload.get("secret") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NewSigningKeyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the NewSigningKeyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that will be responsible for the new Key resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SigningKeys.json".format(**self._solution) + + def create( + self, friendly_name: Union[str, object] = values.unset + ) -> NewSigningKeyInstance: + """ + Create the NewSigningKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created NewSigningKeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NewSigningKeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> NewSigningKeyInstance: + """ + Asynchronously create the NewSigningKeyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created NewSigningKeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NewSigningKeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/notification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/notification.py new file mode 100644 index 0000000..0a2e13d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/notification.py @@ -0,0 +1,526 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class NotificationInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Notification resource. + :ivar api_version: The API version used to generate the notification. Can be empty for events that don't have a specific API version, such as incoming phone calls. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Notification resource is associated with. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar error_code: A unique error code for the error condition that is described in our [Error Dictionary](https://www.twilio.com/docs/api/errors). + :ivar log: An integer log level that corresponds to the type of notification: `0` is ERROR, `1` is WARNING. + :ivar message_date: The date the notification was actually generated in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. Message buffering can cause this value to differ from `date_created`. + :ivar message_text: The text of the notification. + :ivar more_info: The URL for more information about the error condition. This value is a page in our [Error Dictionary](https://www.twilio.com/docs/api/errors). + :ivar request_method: The HTTP method used to generate the notification. If the notification was generated during a phone call, this is the HTTP Method used to request the resource on your server. If the notification was generated by your use of our REST API, this is the HTTP method used to call the resource on our servers. + :ivar request_url: The URL of the resource that generated the notification. If the notification was generated during a phone call, this is the URL of the resource on your server that caused the notification. If the notification was generated by your use of our REST API, this is the URL of the resource you called. + :ivar request_variables: The HTTP GET or POST variables we sent to your server. However, if the notification was generated by our REST API, this contains the HTTP POST or PUT variables you sent to our API. + :ivar response_body: The HTTP body returned by your server. + :ivar response_headers: The HTTP headers returned by your server. + :ivar sid: The unique string that that we created to identify the Notification resource. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.call_sid: Optional[str] = payload.get("call_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.error_code: Optional[str] = payload.get("error_code") + self.log: Optional[str] = payload.get("log") + self.message_date: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("message_date") + ) + self.message_text: Optional[str] = payload.get("message_text") + self.more_info: Optional[str] = payload.get("more_info") + self.request_method: Optional[str] = payload.get("request_method") + self.request_url: Optional[str] = payload.get("request_url") + self.request_variables: Optional[str] = payload.get("request_variables") + self.response_body: Optional[str] = payload.get("response_body") + self.response_headers: Optional[str] = payload.get("response_headers") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[NotificationContext] = None + + @property + def _proxy(self) -> "NotificationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NotificationContext for this NotificationInstance + """ + if self._context is None: + self._context = NotificationContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "NotificationInstance": + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NotificationInstance": + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the NotificationContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Notification resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Notification resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Notifications/{sid}.json".format( + **self._solution + ) + + def fetch(self) -> NotificationInstance: + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> NotificationInstance: + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> NotificationInstance: + """ + Build an instance of NotificationInstance + + :param payload: Payload response from the API + """ + return NotificationInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NotificationList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the NotificationList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Notification resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Notifications.json".format( + **self._solution + ) + + def stream( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NotificationInstance]: + """ + Streams NotificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NotificationInstance]: + """ + Asynchronously streams NotificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NotificationInstance]: + """ + Lists NotificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NotificationInstance]: + """ + Asynchronously lists NotificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param date message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param date message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + log=log, + message_date=message_date, + message_date_before=message_date_before, + message_date_after=message_date_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NotificationPage: + """ + Retrieve a single page of NotificationInstance records from the API. + Request is executed immediately + + :param log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NotificationInstance + """ + data = values.of( + { + "Log": log, + "MessageDate": serialize.iso8601_date(message_date), + "MessageDate<": serialize.iso8601_date(message_date_before), + "MessageDate>": serialize.iso8601_date(message_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NotificationPage(self._version, response, self._solution) + + async def page_async( + self, + log: Union[int, object] = values.unset, + message_date: Union[date, object] = values.unset, + message_date_before: Union[date, object] = values.unset, + message_date_after: Union[date, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NotificationPage: + """ + Asynchronously retrieve a single page of NotificationInstance records from the API. + Request is executed immediately + + :param log: Only read notifications of the specified log level. Can be: `0` to read only ERROR notifications or `1` to read only WARNING notifications. By default, all notifications are read. + :param message_date: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_before: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param message_date_after: Only show notifications for the specified date, formatted as `YYYY-MM-DD`. You can also specify an inequality, such as `<=YYYY-MM-DD` for messages logged at or before midnight on a date, or `>=YYYY-MM-DD` for messages logged at or after midnight on a date. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NotificationInstance + """ + data = values.of( + { + "Log": log, + "MessageDate": serialize.iso8601_date(message_date), + "MessageDate<": serialize.iso8601_date(message_date_before), + "MessageDate>": serialize.iso8601_date(message_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NotificationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> NotificationPage: + """ + Retrieve a specific page of NotificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NotificationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NotificationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> NotificationPage: + """ + Asynchronously retrieve a specific page of NotificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NotificationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NotificationPage(self._version, response, self._solution) + + def get(self, sid: str) -> NotificationContext: + """ + Constructs a NotificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Notification resource to fetch. + """ + return NotificationContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> NotificationContext: + """ + Constructs a NotificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Notification resource to fetch. + """ + return NotificationContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/outgoing_caller_id.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/outgoing_caller_id.py new file mode 100644 index 0000000..65ddfa1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/outgoing_caller_id.py @@ -0,0 +1,596 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class OutgoingCallerIdInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the OutgoingCallerId resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the OutgoingCallerId resource. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[OutgoingCallerIdContext] = None + + @property + def _proxy(self) -> "OutgoingCallerIdContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: OutgoingCallerIdContext for this OutgoingCallerIdInstance + """ + if self._context is None: + self._context = OutgoingCallerIdContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the OutgoingCallerIdInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the OutgoingCallerIdInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "OutgoingCallerIdInstance": + """ + Fetch the OutgoingCallerIdInstance + + + :returns: The fetched OutgoingCallerIdInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "OutgoingCallerIdInstance": + """ + Asynchronous coroutine to fetch the OutgoingCallerIdInstance + + + :returns: The fetched OutgoingCallerIdInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "OutgoingCallerIdInstance": + """ + Update the OutgoingCallerIdInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated OutgoingCallerIdInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "OutgoingCallerIdInstance": + """ + Asynchronous coroutine to update the OutgoingCallerIdInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated OutgoingCallerIdInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OutgoingCallerIdContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the OutgoingCallerIdContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the OutgoingCallerId resources to update. + :param sid: The Twilio-provided string that uniquely identifies the OutgoingCallerId resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/OutgoingCallerIds/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the OutgoingCallerIdInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the OutgoingCallerIdInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> OutgoingCallerIdInstance: + """ + Fetch the OutgoingCallerIdInstance + + + :returns: The fetched OutgoingCallerIdInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return OutgoingCallerIdInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> OutgoingCallerIdInstance: + """ + Asynchronous coroutine to fetch the OutgoingCallerIdInstance + + + :returns: The fetched OutgoingCallerIdInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return OutgoingCallerIdInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> OutgoingCallerIdInstance: + """ + Update the OutgoingCallerIdInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated OutgoingCallerIdInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return OutgoingCallerIdInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> OutgoingCallerIdInstance: + """ + Asynchronous coroutine to update the OutgoingCallerIdInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated OutgoingCallerIdInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return OutgoingCallerIdInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OutgoingCallerIdPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> OutgoingCallerIdInstance: + """ + Build an instance of OutgoingCallerIdInstance + + :param payload: Payload response from the API + """ + return OutgoingCallerIdInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class OutgoingCallerIdList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the OutgoingCallerIdList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the OutgoingCallerId resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/OutgoingCallerIds.json".format( + **self._solution + ) + + def stream( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[OutgoingCallerIdInstance]: + """ + Streams OutgoingCallerIdInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str phone_number: The phone number of the OutgoingCallerId resources to read. + :param str friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + phone_number=phone_number, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[OutgoingCallerIdInstance]: + """ + Asynchronously streams OutgoingCallerIdInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str phone_number: The phone number of the OutgoingCallerId resources to read. + :param str friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + phone_number=phone_number, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OutgoingCallerIdInstance]: + """ + Lists OutgoingCallerIdInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str phone_number: The phone number of the OutgoingCallerId resources to read. + :param str friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + phone_number=phone_number, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OutgoingCallerIdInstance]: + """ + Asynchronously lists OutgoingCallerIdInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str phone_number: The phone number of the OutgoingCallerId resources to read. + :param str friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + phone_number=phone_number, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OutgoingCallerIdPage: + """ + Retrieve a single page of OutgoingCallerIdInstance records from the API. + Request is executed immediately + + :param phone_number: The phone number of the OutgoingCallerId resources to read. + :param friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OutgoingCallerIdInstance + """ + data = values.of( + { + "PhoneNumber": phone_number, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return OutgoingCallerIdPage(self._version, response, self._solution) + + async def page_async( + self, + phone_number: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OutgoingCallerIdPage: + """ + Asynchronously retrieve a single page of OutgoingCallerIdInstance records from the API. + Request is executed immediately + + :param phone_number: The phone number of the OutgoingCallerId resources to read. + :param friendly_name: The string that identifies the OutgoingCallerId resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OutgoingCallerIdInstance + """ + data = values.of( + { + "PhoneNumber": phone_number, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return OutgoingCallerIdPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> OutgoingCallerIdPage: + """ + Retrieve a specific page of OutgoingCallerIdInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OutgoingCallerIdInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return OutgoingCallerIdPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> OutgoingCallerIdPage: + """ + Asynchronously retrieve a specific page of OutgoingCallerIdInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OutgoingCallerIdInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return OutgoingCallerIdPage(self._version, response, self._solution) + + def get(self, sid: str) -> OutgoingCallerIdContext: + """ + Constructs a OutgoingCallerIdContext + + :param sid: The Twilio-provided string that uniquely identifies the OutgoingCallerId resource to update. + """ + return OutgoingCallerIdContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> OutgoingCallerIdContext: + """ + Constructs a OutgoingCallerIdContext + + :param sid: The Twilio-provided string that uniquely identifies the OutgoingCallerId resource to update. + """ + return OutgoingCallerIdContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__init__.py new file mode 100644 index 0000000..4e6c87b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__init__.py @@ -0,0 +1,657 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.queue.member import MemberList + + +class QueueInstance(InstanceResource): + """ + :ivar date_updated: The date and time in GMT that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar current_size: The number of calls currently in the queue. + :ivar friendly_name: A string that you assigned to describe this resource. + :ivar uri: The URI of this resource, relative to `https://api.twilio.com`. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this Queue resource. + :ivar average_wait_time: The average wait time in seconds of the members in this queue. This is calculated at the time of the request. + :ivar sid: The unique string that that we created to identify this Queue resource. + :ivar date_created: The date and time in GMT that this resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar max_size: The maximum number of calls that can be in the queue. The default is 1000 and the maximum is 5000. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.current_size: Optional[int] = deserialize.integer( + payload.get("current_size") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.uri: Optional[str] = payload.get("uri") + self.account_sid: Optional[str] = payload.get("account_sid") + self.average_wait_time: Optional[int] = deserialize.integer( + payload.get("average_wait_time") + ) + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.max_size: Optional[int] = deserialize.integer(payload.get("max_size")) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[QueueContext] = None + + @property + def _proxy(self) -> "QueueContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: QueueContext for this QueueInstance + """ + if self._context is None: + self._context = QueueContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the QueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the QueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "QueueInstance": + """ + Fetch the QueueInstance + + + :returns: The fetched QueueInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "QueueInstance": + """ + Asynchronous coroutine to fetch the QueueInstance + + + :returns: The fetched QueueInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + max_size: Union[int, object] = values.unset, + ) -> "QueueInstance": + """ + Update the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The updated QueueInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + max_size=max_size, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + max_size: Union[int, object] = values.unset, + ) -> "QueueInstance": + """ + Asynchronous coroutine to update the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The updated QueueInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + max_size=max_size, + ) + + @property + def members(self) -> MemberList: + """ + Access the members + """ + return self._proxy.members + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class QueueContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the QueueContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Queue resource to update. + :param sid: The Twilio-provided string that uniquely identifies the Queue resource to update + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Queues/{sid}.json".format(**self._solution) + + self._members: Optional[MemberList] = None + + def delete(self) -> bool: + """ + Deletes the QueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the QueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> QueueInstance: + """ + Fetch the QueueInstance + + + :returns: The fetched QueueInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return QueueInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> QueueInstance: + """ + Asynchronous coroutine to fetch the QueueInstance + + + :returns: The fetched QueueInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return QueueInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + max_size: Union[int, object] = values.unset, + ) -> QueueInstance: + """ + Update the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The updated QueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "MaxSize": max_size, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return QueueInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + max_size: Union[int, object] = values.unset, + ) -> QueueInstance: + """ + Asynchronous coroutine to update the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The updated QueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "MaxSize": max_size, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return QueueInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def members(self) -> MemberList: + """ + Access the members + """ + if self._members is None: + self._members = MemberList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._members + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class QueuePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> QueueInstance: + """ + Build an instance of QueueInstance + + :param payload: Payload response from the API + """ + return QueueInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class QueueList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the QueueList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Queue resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Queues.json".format(**self._solution) + + def create( + self, friendly_name: str, max_size: Union[int, object] = values.unset + ) -> QueueInstance: + """ + Create the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The created QueueInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "MaxSize": max_size, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return QueueInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, friendly_name: str, max_size: Union[int, object] = values.unset + ) -> QueueInstance: + """ + Asynchronously create the QueueInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. + :param max_size: The maximum number of calls allowed to be in the queue. The default is 1000. The maximum is 5000. + + :returns: The created QueueInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "MaxSize": max_size, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return QueueInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[QueueInstance]: + """ + Streams QueueInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[QueueInstance]: + """ + Asynchronously streams QueueInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[QueueInstance]: + """ + Lists QueueInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[QueueInstance]: + """ + Asynchronously lists QueueInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> QueuePage: + """ + Retrieve a single page of QueueInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of QueueInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return QueuePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> QueuePage: + """ + Asynchronously retrieve a single page of QueueInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of QueueInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return QueuePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> QueuePage: + """ + Retrieve a specific page of QueueInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of QueueInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return QueuePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> QueuePage: + """ + Asynchronously retrieve a specific page of QueueInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of QueueInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return QueuePage(self._version, response, self._solution) + + def get(self, sid: str) -> QueueContext: + """ + Constructs a QueueContext + + :param sid: The Twilio-provided string that uniquely identifies the Queue resource to update + """ + return QueueContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> QueueContext: + """ + Constructs a QueueContext + + :param sid: The Twilio-provided string that uniquely identifies the Queue resource to update + """ + return QueueContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..864e151 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/member.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/member.cpython-310.pyc new file mode 100644 index 0000000..ce800ae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/__pycache__/member.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/member.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/member.py new file mode 100644 index 0000000..bf092ff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/queue/member.py @@ -0,0 +1,542 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MemberInstance(InstanceResource): + """ + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Member resource is associated with. + :ivar date_enqueued: The date that the member was enqueued, given in RFC 2822 format. + :ivar position: This member's current position in the queue. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar wait_time: The number of seconds the member has been in the queue. + :ivar queue_sid: The SID of the Queue the member is in. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + queue_sid: str, + call_sid: Optional[str] = None, + ): + super().__init__(version) + + self.call_sid: Optional[str] = payload.get("call_sid") + self.date_enqueued: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_enqueued") + ) + self.position: Optional[int] = deserialize.integer(payload.get("position")) + self.uri: Optional[str] = payload.get("uri") + self.wait_time: Optional[int] = deserialize.integer(payload.get("wait_time")) + self.queue_sid: Optional[str] = payload.get("queue_sid") + + self._solution = { + "account_sid": account_sid, + "queue_sid": queue_sid, + "call_sid": call_sid or self.call_sid, + } + self._context: Optional[MemberContext] = None + + @property + def _proxy(self) -> "MemberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MemberContext for this MemberInstance + """ + if self._context is None: + self._context = MemberContext( + self._version, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=self._solution["call_sid"], + ) + return self._context + + def fetch(self) -> "MemberInstance": + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MemberInstance": + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, url: str, method: Union[str, object] = values.unset + ) -> "MemberInstance": + """ + Update the MemberInstance + + :param url: The absolute URL of the Queue resource. + :param method: How to pass the update request data. Can be `GET` or `POST` and the default is `POST`. `POST` sends the data as encoded form data and `GET` sends the data as query parameters. + + :returns: The updated MemberInstance + """ + return self._proxy.update( + url=url, + method=method, + ) + + async def update_async( + self, url: str, method: Union[str, object] = values.unset + ) -> "MemberInstance": + """ + Asynchronous coroutine to update the MemberInstance + + :param url: The absolute URL of the Queue resource. + :param method: How to pass the update request data. Can be `GET` or `POST` and the default is `POST`. `POST` sends the data as encoded form data and `GET` sends the data as query parameters. + + :returns: The updated MemberInstance + """ + return await self._proxy.update_async( + url=url, + method=method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, queue_sid: str, call_sid: str + ): + """ + Initialize the MemberContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Member resource(s) to update. + :param queue_sid: The SID of the Queue in which to find the members to update. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resource(s) to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "queue_sid": queue_sid, + "call_sid": call_sid, + } + self._uri = ( + "/Accounts/{account_sid}/Queues/{queue_sid}/Members/{call_sid}.json".format( + **self._solution + ) + ) + + def fetch(self) -> MemberInstance: + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=self._solution["call_sid"], + ) + + async def fetch_async(self) -> MemberInstance: + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=self._solution["call_sid"], + ) + + def update( + self, url: str, method: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Update the MemberInstance + + :param url: The absolute URL of the Queue resource. + :param method: How to pass the update request data. Can be `GET` or `POST` and the default is `POST`. `POST` sends the data as encoded form data and `GET` sends the data as query parameters. + + :returns: The updated MemberInstance + """ + data = values.of( + { + "Url": url, + "Method": method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=self._solution["call_sid"], + ) + + async def update_async( + self, url: str, method: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Asynchronous coroutine to update the MemberInstance + + :param url: The absolute URL of the Queue resource. + :param method: How to pass the update request data. Can be `GET` or `POST` and the default is `POST`. `POST` sends the data as encoded form data and `GET` sends the data as query parameters. + + :returns: The updated MemberInstance + """ + data = values.of( + { + "Url": url, + "Method": method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MemberInstance: + """ + Build an instance of MemberInstance + + :param payload: Payload response from the API + """ + return MemberInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MemberList(ListResource): + + def __init__(self, version: Version, account_sid: str, queue_sid: str): + """ + Initialize the MemberList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Member resource(s) to read. + :param queue_sid: The SID of the Queue in which to find the members + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "queue_sid": queue_sid, + } + self._uri = "/Accounts/{account_sid}/Queues/{queue_sid}/Members.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MemberInstance]: + """ + Streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MemberInstance]: + """ + Asynchronously streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Asynchronously lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MemberPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Asynchronously retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MemberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MemberPage: + """ + Retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MemberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MemberPage: + """ + Asynchronously retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MemberPage(self._version, response, self._solution) + + def get(self, call_sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resource(s) to update. + """ + return MemberContext( + self._version, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=call_sid, + ) + + def __call__(self, call_sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resource(s) to update. + """ + return MemberContext( + self._version, + account_sid=self._solution["account_sid"], + queue_sid=self._solution["queue_sid"], + call_sid=call_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__init__.py new file mode 100644 index 0000000..bc1ddcc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__init__.py @@ -0,0 +1,698 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.recording.add_on_result import AddOnResultList +from twilio.rest.api.v2010.account.recording.transcription import TranscriptionList + + +class RecordingInstance(InstanceResource): + + class Source(object): + DIALVERB = "DialVerb" + CONFERENCE = "Conference" + OUTBOUNDAPI = "OutboundAPI" + TRUNKING = "Trunking" + RECORDVERB = "RecordVerb" + STARTCALLRECORDINGAPI = "StartCallRecordingAPI" + STARTCONFERENCERECORDINGAPI = "StartConferenceRecordingAPI" + + class Status(object): + IN_PROGRESS = "in-progress" + PAUSED = "paused" + STOPPED = "stopped" + PROCESSING = "processing" + COMPLETED = "completed" + ABSENT = "absent" + DELETED = "deleted" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resource. + :ivar api_version: The API version used during the recording. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Recording resource is associated with. This will always refer to the parent leg of a two-leg call. + :ivar conference_sid: The Conference SID that identifies the conference associated with the recording, if a conference recording. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar start_time: The start time of the recording in GMT and in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar duration: The length of the recording in seconds. + :ivar sid: The unique string that that we created to identify the Recording resource. + :ivar price: The one-time cost of creating the recording in the `price_unit` currency. + :ivar price_unit: The currency used in the `price` property. Example: `USD`. + :ivar status: + :ivar channels: The number of channels in the final recording file. Can be: `1` or `2`. You can split a call with two legs into two separate recording channels if you record using [TwiML Dial](https://www.twilio.com/docs/voice/twiml/dial#record) or the [Outbound Rest API](https://www.twilio.com/docs/voice/make-calls#manage-your-outbound-call). + :ivar source: + :ivar error_code: The error code that describes why the recording is `absent`. The error code is described in our [Error Dictionary](https://www.twilio.com/docs/api/errors). This value is null if the recording `status` is not `absent`. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar encryption_details: How to decrypt the recording if it was encrypted using [Call Recording Encryption](https://www.twilio.com/docs/voice/tutorials/voice-recording-encryption) feature. + :ivar subresource_uris: A list of related resources identified by their relative URIs. + :ivar media_url: The URL of the media file associated with this recording resource. When stored externally, this is the full URL location of the media file. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.call_sid: Optional[str] = payload.get("call_sid") + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.start_time: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("start_time") + ) + self.duration: Optional[str] = payload.get("duration") + self.sid: Optional[str] = payload.get("sid") + self.price: Optional[str] = payload.get("price") + self.price_unit: Optional[str] = payload.get("price_unit") + self.status: Optional["RecordingInstance.Status"] = payload.get("status") + self.channels: Optional[int] = deserialize.integer(payload.get("channels")) + self.source: Optional["RecordingInstance.Source"] = payload.get("source") + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.uri: Optional[str] = payload.get("uri") + self.encryption_details: Optional[Dict[str, object]] = payload.get( + "encryption_details" + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.media_url: Optional[str] = payload.get("media_url") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[RecordingContext] = None + + @property + def _proxy(self) -> "RecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingContext for this RecordingInstance + """ + if self._context is None: + self._context = RecordingContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch( + self, include_soft_deleted: Union[bool, object] = values.unset + ) -> "RecordingInstance": + """ + Fetch the RecordingInstance + + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + + :returns: The fetched RecordingInstance + """ + return self._proxy.fetch( + include_soft_deleted=include_soft_deleted, + ) + + async def fetch_async( + self, include_soft_deleted: Union[bool, object] = values.unset + ) -> "RecordingInstance": + """ + Asynchronous coroutine to fetch the RecordingInstance + + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + + :returns: The fetched RecordingInstance + """ + return await self._proxy.fetch_async( + include_soft_deleted=include_soft_deleted, + ) + + @property + def add_on_results(self) -> AddOnResultList: + """ + Access the add_on_results + """ + return self._proxy.add_on_results + + @property + def transcriptions(self) -> TranscriptionList: + """ + Access the transcriptions + """ + return self._proxy.transcriptions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the RecordingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{sid}.json".format( + **self._solution + ) + + self._add_on_results: Optional[AddOnResultList] = None + self._transcriptions: Optional[TranscriptionList] = None + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch( + self, include_soft_deleted: Union[bool, object] = values.unset + ) -> RecordingInstance: + """ + Fetch the RecordingInstance + + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + + :returns: The fetched RecordingInstance + """ + + data = values.of( + { + "IncludeSoftDeleted": serialize.boolean_to_string(include_soft_deleted), + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async( + self, include_soft_deleted: Union[bool, object] = values.unset + ) -> RecordingInstance: + """ + Asynchronous coroutine to fetch the RecordingInstance + + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + + :returns: The fetched RecordingInstance + """ + + data = values.of( + { + "IncludeSoftDeleted": serialize.boolean_to_string(include_soft_deleted), + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return RecordingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def add_on_results(self) -> AddOnResultList: + """ + Access the add_on_results + """ + if self._add_on_results is None: + self._add_on_results = AddOnResultList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._add_on_results + + @property + def transcriptions(self) -> TranscriptionList: + """ + Access the transcriptions + """ + if self._transcriptions is None: + self._transcriptions = TranscriptionList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._transcriptions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RecordingInstance: + """ + Build an instance of RecordingInstance + + :param payload: Payload response from the API + """ + return RecordingInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RecordingList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the RecordingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Recordings.json".format(**self._solution) + + def stream( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RecordingInstance]: + """ + Streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param str call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param str conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param bool include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + call_sid=call_sid, + conference_sid=conference_sid, + include_soft_deleted=include_soft_deleted, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RecordingInstance]: + """ + Asynchronously streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param str call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param str conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param bool include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + call_sid=call_sid, + conference_sid=conference_sid, + include_soft_deleted=include_soft_deleted, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param str call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param str conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param bool include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + call_sid=call_sid, + conference_sid=conference_sid, + include_soft_deleted=include_soft_deleted, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Asynchronously lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param datetime date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param str call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param str conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param bool include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created=date_created, + date_created_before=date_created_before, + date_created_after=date_created_after, + call_sid=call_sid, + conference_sid=conference_sid, + include_soft_deleted=include_soft_deleted, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateCreated<": serialize.iso8601_datetime(date_created_before), + "DateCreated>": serialize.iso8601_datetime(date_created_after), + "CallSid": call_sid, + "ConferenceSid": conference_sid, + "IncludeSoftDeleted": serialize.boolean_to_string(include_soft_deleted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RecordingPage(self._version, response, self._solution) + + async def page_async( + self, + date_created: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + call_sid: Union[str, object] = values.unset, + conference_sid: Union[str, object] = values.unset, + include_soft_deleted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Asynchronously retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param date_created: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param date_created_before: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param date_created_after: Only include recordings that were created on this date. Specify a date as `YYYY-MM-DD` in GMT, for example: `2009-07-06`, to read recordings that were created on this date. You can also specify an inequality, such as `DateCreated<=YYYY-MM-DD`, to read recordings that were created on or before midnight of this date, and `DateCreated>=YYYY-MM-DD` to read recordings that were created on or after midnight of this date. + :param call_sid: The [Call](https://www.twilio.com/docs/voice/api/call-resource) SID of the resources to read. + :param conference_sid: The Conference SID that identifies the conference associated with the recording to read. + :param include_soft_deleted: A boolean parameter indicating whether to retrieve soft deleted recordings or not. Recordings metadata are kept after deletion for a retention period of 40 days. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateCreated<": serialize.iso8601_datetime(date_created_before), + "DateCreated>": serialize.iso8601_datetime(date_created_after), + "CallSid": call_sid, + "ConferenceSid": conference_sid, + "IncludeSoftDeleted": serialize.boolean_to_string(include_soft_deleted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RecordingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RecordingPage: + """ + Retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RecordingPage: + """ + Asynchronously retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RecordingPage(self._version, response, self._solution) + + def get(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to fetch. + """ + return RecordingContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording resource to fetch. + """ + return RecordingContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..47dd203 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/transcription.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/transcription.cpython-310.pyc new file mode 100644 index 0000000..a220d90 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/__pycache__/transcription.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__init__.py new file mode 100644 index 0000000..af1e75f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__init__.py @@ -0,0 +1,537 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.recording.add_on_result.payload import PayloadList + + +class AddOnResultInstance(InstanceResource): + + class Status(object): + CANCELED = "canceled" + COMPLETED = "completed" + DELETED = "deleted" + FAILED = "failed" + IN_PROGRESS = "in-progress" + INIT = "init" + PROCESSING = "processing" + QUEUED = "queued" + + """ + :ivar sid: The unique string that that we created to identify the Recording AddOnResult resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult resource. + :ivar status: + :ivar add_on_sid: The SID of the Add-on to which the result belongs. + :ivar add_on_configuration_sid: The SID of the Add-on configuration. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_completed: The date and time in GMT that the result was completed specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar reference_sid: The SID of the recording to which the AddOnResult resource belongs. + :ivar subresource_uris: A list of related resources identified by their relative URIs. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + reference_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["AddOnResultInstance.Status"] = payload.get("status") + self.add_on_sid: Optional[str] = payload.get("add_on_sid") + self.add_on_configuration_sid: Optional[str] = payload.get( + "add_on_configuration_sid" + ) + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.date_completed: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_completed") + ) + self.reference_sid: Optional[str] = payload.get("reference_sid") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + "sid": sid or self.sid, + } + self._context: Optional[AddOnResultContext] = None + + @property + def _proxy(self) -> "AddOnResultContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AddOnResultContext for this AddOnResultInstance + """ + if self._context is None: + self._context = AddOnResultContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AddOnResultInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddOnResultInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AddOnResultInstance": + """ + Fetch the AddOnResultInstance + + + :returns: The fetched AddOnResultInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AddOnResultInstance": + """ + Asynchronous coroutine to fetch the AddOnResultInstance + + + :returns: The fetched AddOnResultInstance + """ + return await self._proxy.fetch_async() + + @property + def payloads(self) -> PayloadList: + """ + Access the payloads + """ + return self._proxy.payloads + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AddOnResultContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, reference_sid: str, sid: str + ): + """ + Initialize the AddOnResultContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult resource to fetch. + :param reference_sid: The SID of the recording to which the result to fetch belongs. + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{reference_sid}/AddOnResults/{sid}.json".format( + **self._solution + ) + + self._payloads: Optional[PayloadList] = None + + def delete(self) -> bool: + """ + Deletes the AddOnResultInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddOnResultInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AddOnResultInstance: + """ + Fetch the AddOnResultInstance + + + :returns: The fetched AddOnResultInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AddOnResultInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AddOnResultInstance: + """ + Asynchronous coroutine to fetch the AddOnResultInstance + + + :returns: The fetched AddOnResultInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AddOnResultInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + sid=self._solution["sid"], + ) + + @property + def payloads(self) -> PayloadList: + """ + Access the payloads + """ + if self._payloads is None: + self._payloads = PayloadList( + self._version, + self._solution["account_sid"], + self._solution["reference_sid"], + self._solution["sid"], + ) + return self._payloads + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AddOnResultPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AddOnResultInstance: + """ + Build an instance of AddOnResultInstance + + :param payload: Payload response from the API + """ + return AddOnResultInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AddOnResultList(ListResource): + + def __init__(self, version: Version, account_sid: str, reference_sid: str): + """ + Initialize the AddOnResultList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult resources to read. + :param reference_sid: The SID of the recording to which the result to read belongs. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{reference_sid}/AddOnResults.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AddOnResultInstance]: + """ + Streams AddOnResultInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AddOnResultInstance]: + """ + Asynchronously streams AddOnResultInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddOnResultInstance]: + """ + Lists AddOnResultInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddOnResultInstance]: + """ + Asynchronously lists AddOnResultInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddOnResultPage: + """ + Retrieve a single page of AddOnResultInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddOnResultInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AddOnResultPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddOnResultPage: + """ + Asynchronously retrieve a single page of AddOnResultInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddOnResultInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AddOnResultPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AddOnResultPage: + """ + Retrieve a specific page of AddOnResultInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddOnResultInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AddOnResultPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AddOnResultPage: + """ + Asynchronously retrieve a specific page of AddOnResultInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddOnResultInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AddOnResultPage(self._version, response, self._solution) + + def get(self, sid: str) -> AddOnResultContext: + """ + Constructs a AddOnResultContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult resource to fetch. + """ + return AddOnResultContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AddOnResultContext: + """ + Constructs a AddOnResultContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult resource to fetch. + """ + return AddOnResultContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e796b8a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/payload.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/payload.cpython-310.pyc new file mode 100644 index 0000000..360174c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/__pycache__/payload.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/payload.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/payload.py new file mode 100644 index 0000000..1dc26e1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/add_on_result/payload.py @@ -0,0 +1,525 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PayloadInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the Recording AddOnResult Payload resource. + :ivar add_on_result_sid: The SID of the AddOnResult to which the payload belongs. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult Payload resource. + :ivar label: The string provided by the vendor that describes the payload. + :ivar add_on_sid: The SID of the Add-on to which the result belongs. + :ivar add_on_configuration_sid: The SID of the Add-on configuration. + :ivar content_type: The MIME type of the payload. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar reference_sid: The SID of the recording to which the AddOnResult resource that contains the payload belongs. + :ivar subresource_uris: A list of related resources identified by their relative URIs. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + reference_sid: str, + add_on_result_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.add_on_result_sid: Optional[str] = payload.get("add_on_result_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.label: Optional[str] = payload.get("label") + self.add_on_sid: Optional[str] = payload.get("add_on_sid") + self.add_on_configuration_sid: Optional[str] = payload.get( + "add_on_configuration_sid" + ) + self.content_type: Optional[str] = payload.get("content_type") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.reference_sid: Optional[str] = payload.get("reference_sid") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + "add_on_result_sid": add_on_result_sid, + "sid": sid or self.sid, + } + self._context: Optional[PayloadContext] = None + + @property + def _proxy(self) -> "PayloadContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PayloadContext for this PayloadInstance + """ + if self._context is None: + self._context = PayloadContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PayloadInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PayloadInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "PayloadInstance": + """ + Fetch the PayloadInstance + + + :returns: The fetched PayloadInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PayloadInstance": + """ + Asynchronous coroutine to fetch the PayloadInstance + + + :returns: The fetched PayloadInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PayloadContext(InstanceContext): + + def __init__( + self, + version: Version, + account_sid: str, + reference_sid: str, + add_on_result_sid: str, + sid: str, + ): + """ + Initialize the PayloadContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult Payload resource to fetch. + :param reference_sid: The SID of the recording to which the AddOnResult resource that contains the payload to fetch belongs. + :param add_on_result_sid: The SID of the AddOnResult to which the payload to fetch belongs. + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult Payload resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + "add_on_result_sid": add_on_result_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{reference_sid}/AddOnResults/{add_on_result_sid}/Payloads/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the PayloadInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PayloadInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> PayloadInstance: + """ + Fetch the PayloadInstance + + + :returns: The fetched PayloadInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PayloadInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PayloadInstance: + """ + Asynchronous coroutine to fetch the PayloadInstance + + + :returns: The fetched PayloadInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PayloadInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PayloadPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PayloadInstance: + """ + Build an instance of PayloadInstance + + :param payload: Payload response from the API + """ + return PayloadInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PayloadList(ListResource): + + def __init__( + self, + version: Version, + account_sid: str, + reference_sid: str, + add_on_result_sid: str, + ): + """ + Initialize the PayloadList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording AddOnResult Payload resources to read. + :param reference_sid: The SID of the recording to which the AddOnResult resource that contains the payloads to read belongs. + :param add_on_result_sid: The SID of the AddOnResult to which the payloads to read belongs. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "reference_sid": reference_sid, + "add_on_result_sid": add_on_result_sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{reference_sid}/AddOnResults/{add_on_result_sid}/Payloads.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PayloadInstance]: + """ + Streams PayloadInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PayloadInstance]: + """ + Asynchronously streams PayloadInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PayloadInstance]: + """ + Lists PayloadInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PayloadInstance]: + """ + Asynchronously lists PayloadInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PayloadPage: + """ + Retrieve a single page of PayloadInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PayloadInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PayloadPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PayloadPage: + """ + Asynchronously retrieve a single page of PayloadInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PayloadInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PayloadPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PayloadPage: + """ + Retrieve a specific page of PayloadInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PayloadInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PayloadPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PayloadPage: + """ + Asynchronously retrieve a specific page of PayloadInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PayloadInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PayloadPage(self._version, response, self._solution) + + def get(self, sid: str) -> PayloadContext: + """ + Constructs a PayloadContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult Payload resource to fetch. + """ + return PayloadContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> PayloadContext: + """ + Constructs a PayloadContext + + :param sid: The Twilio-provided string that uniquely identifies the Recording AddOnResult Payload resource to fetch. + """ + return PayloadContext( + self._version, + account_sid=self._solution["account_sid"], + reference_sid=self._solution["reference_sid"], + add_on_result_sid=self._solution["add_on_result_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/transcription.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/transcription.py new file mode 100644 index 0000000..bb6e0e4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/recording/transcription.py @@ -0,0 +1,508 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TranscriptionInstance(InstanceResource): + + class Status(object): + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + FAILED = "failed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resource. + :ivar api_version: The API version used to create the transcription. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar duration: The duration of the transcribed audio in seconds. + :ivar price: The charge for the transcript in the currency associated with the account. This value is populated after the transcript is complete so it may not be available immediately. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar recording_sid: The SID of the [Recording](https://www.twilio.com/docs/voice/api/recording) from which the transcription was created. + :ivar sid: The unique string that that we created to identify the Transcription resource. + :ivar status: + :ivar transcription_text: The text content of the transcription. + :ivar type: The transcription type. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + recording_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.duration: Optional[str] = payload.get("duration") + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.recording_sid: Optional[str] = payload.get("recording_sid") + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["TranscriptionInstance.Status"] = payload.get("status") + self.transcription_text: Optional[str] = payload.get("transcription_text") + self.type: Optional[str] = payload.get("type") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "recording_sid": recording_sid, + "sid": sid or self.sid, + } + self._context: Optional[TranscriptionContext] = None + + @property + def _proxy(self) -> "TranscriptionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TranscriptionContext for this TranscriptionInstance + """ + if self._context is None: + self._context = TranscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TranscriptionInstance": + """ + Fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TranscriptionInstance": + """ + Asynchronous coroutine to fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptionContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, recording_sid: str, sid: str + ): + """ + Initialize the TranscriptionContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resource to fetch. + :param recording_sid: The SID of the [Recording](https://www.twilio.com/docs/voice/api/recording) that created the transcription to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "recording_sid": recording_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{recording_sid}/Transcriptions/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TranscriptionInstance: + """ + Fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TranscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TranscriptionInstance: + """ + Asynchronous coroutine to fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TranscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TranscriptionInstance: + """ + Build an instance of TranscriptionInstance + + :param payload: Payload response from the API + """ + return TranscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TranscriptionList(ListResource): + + def __init__(self, version: Version, account_sid: str, recording_sid: str): + """ + Initialize the TranscriptionList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resources to read. + :param recording_sid: The SID of the [Recording](https://www.twilio.com/docs/voice/api/recording) that created the transcriptions to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "recording_sid": recording_sid, + } + self._uri = "/Accounts/{account_sid}/Recordings/{recording_sid}/Transcriptions.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TranscriptionInstance]: + """ + Streams TranscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TranscriptionInstance]: + """ + Asynchronously streams TranscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptionInstance]: + """ + Lists TranscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptionInstance]: + """ + Asynchronously lists TranscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptionPage: + """ + Retrieve a single page of TranscriptionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TranscriptionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptionPage: + """ + Asynchronously retrieve a single page of TranscriptionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TranscriptionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TranscriptionPage: + """ + Retrieve a specific page of TranscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TranscriptionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TranscriptionPage: + """ + Asynchronously retrieve a specific page of TranscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TranscriptionPage(self._version, response, self._solution) + + def get(self, sid: str) -> TranscriptionContext: + """ + Constructs a TranscriptionContext + + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + return TranscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> TranscriptionContext: + """ + Constructs a TranscriptionContext + + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + return TranscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + recording_sid=self._solution["recording_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/short_code.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/short_code.py new file mode 100644 index 0000000..2af049a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/short_code.py @@ -0,0 +1,628 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ShortCodeInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created this ShortCode resource. + :ivar api_version: The API version used to start a new TwiML session when an SMS message is sent to this short code. + :ivar date_created: The date and time in GMT that this resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that this resource was last updated, specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: A string that you assigned to describe this resource. By default, the `FriendlyName` is the short code. + :ivar short_code: The short code. e.g., 894546. + :ivar sid: The unique string that that we created to identify this ShortCode resource. + :ivar sms_fallback_method: The HTTP method we use to call the `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call if an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call the `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call when receiving an incoming SMS message to this short code. + :ivar uri: The URI of this resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.short_code: Optional[str] = payload.get("short_code") + self.sid: Optional[str] = payload.get("sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[ShortCodeContext] = None + + @property + def _proxy(self) -> "ShortCodeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ShortCodeContext for this ShortCodeInstance + """ + if self._context is None: + self._context = ShortCodeContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ShortCodeInstance": + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ShortCodeInstance": + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + ) -> "ShortCodeInstance": + """ + Update the ShortCodeInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. By default, the `FriendlyName` is the short code. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. + :param sms_url: The URL we should call when receiving an incoming SMS message to this short code. + :param sms_method: The HTTP method we should use when calling the `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call if an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method that we should use to call the `sms_fallback_url`. Can be: `GET` or `POST`. + + :returns: The updated ShortCodeInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + api_version=api_version, + sms_url=sms_url, + sms_method=sms_method, + sms_fallback_url=sms_fallback_url, + sms_fallback_method=sms_fallback_method, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + ) -> "ShortCodeInstance": + """ + Asynchronous coroutine to update the ShortCodeInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. By default, the `FriendlyName` is the short code. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. + :param sms_url: The URL we should call when receiving an incoming SMS message to this short code. + :param sms_method: The HTTP method we should use when calling the `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call if an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method that we should use to call the `sms_fallback_url`. Can be: `GET` or `POST`. + + :returns: The updated ShortCodeInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + api_version=api_version, + sms_url=sms_url, + sms_method=sms_method, + sms_fallback_url=sms_fallback_url, + sms_fallback_method=sms_fallback_method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodeContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the ShortCodeContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ShortCode resource(s) to update. + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SMS/ShortCodes/{sid}.json".format( + **self._solution + ) + + def fetch(self) -> ShortCodeInstance: + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ShortCodeInstance: + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + ) -> ShortCodeInstance: + """ + Update the ShortCodeInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. By default, the `FriendlyName` is the short code. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. + :param sms_url: The URL we should call when receiving an incoming SMS message to this short code. + :param sms_method: The HTTP method we should use when calling the `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call if an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method that we should use to call the `sms_fallback_url`. Can be: `GET` or `POST`. + + :returns: The updated ShortCodeInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApiVersion": api_version, + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + api_version: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + ) -> ShortCodeInstance: + """ + Asynchronous coroutine to update the ShortCodeInstance + + :param friendly_name: A descriptive string that you created to describe this resource. It can be up to 64 characters long. By default, the `FriendlyName` is the short code. + :param api_version: The API version to use to start a new TwiML session. Can be: `2010-04-01` or `2008-08-01`. + :param sms_url: The URL we should call when receiving an incoming SMS message to this short code. + :param sms_method: The HTTP method we should use when calling the `sms_url`. Can be: `GET` or `POST`. + :param sms_fallback_url: The URL that we should call if an error occurs while retrieving or executing the TwiML from `sms_url`. + :param sms_fallback_method: The HTTP method that we should use to call the `sms_fallback_url`. Can be: `GET` or `POST`. + + :returns: The updated ShortCodeInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApiVersion": api_version, + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ShortCodeInstance: + """ + Build an instance of ShortCodeInstance + + :param payload: Payload response from the API + """ + return ShortCodeInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ShortCodeList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ShortCodeList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ShortCode resource(s) to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SMS/ShortCodes.json".format( + **self._solution + ) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ShortCodeInstance]: + """ + Streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the ShortCode resources to read. + :param str short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + friendly_name=friendly_name, + short_code=short_code, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ShortCodeInstance]: + """ + Asynchronously streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the ShortCode resources to read. + :param str short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, + short_code=short_code, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the ShortCode resources to read. + :param str short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + short_code=short_code, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Asynchronously lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the ShortCode resources to read. + :param str short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + short_code=short_code, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the ShortCode resources to read. + :param short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ShortCode": short_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ShortCodePage(self._version, response, self._solution) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + short_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Asynchronously retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the ShortCode resources to read. + :param short_code: Only show the ShortCode resources that match this pattern. You can specify partial numbers and use '*' as a wildcard for any digit. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ShortCode": short_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ShortCodePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ShortCodePage: + """ + Retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ShortCodePage: + """ + Asynchronously retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + def get(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update + """ + return ShortCodeContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update + """ + return ShortCodeContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/signing_key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/signing_key.py new file mode 100644 index 0000000..6ddc7b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/signing_key.py @@ -0,0 +1,548 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SigningKeyInstance(InstanceResource): + """ + :ivar sid: + :ivar friendly_name: + :ivar date_created: + :ivar date_updated: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[SigningKeyContext] = None + + @property + def _proxy(self) -> "SigningKeyContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SigningKeyContext for this SigningKeyInstance + """ + if self._context is None: + self._context = SigningKeyContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SigningKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SigningKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SigningKeyInstance": + """ + Fetch the SigningKeyInstance + + + :returns: The fetched SigningKeyInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SigningKeyInstance": + """ + Asynchronous coroutine to fetch the SigningKeyInstance + + + :returns: The fetched SigningKeyInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "SigningKeyInstance": + """ + Update the SigningKeyInstance + + :param friendly_name: + + :returns: The updated SigningKeyInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "SigningKeyInstance": + """ + Asynchronous coroutine to update the SigningKeyInstance + + :param friendly_name: + + :returns: The updated SigningKeyInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SigningKeyContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the SigningKeyContext + + :param version: Version that contains the resource + :param account_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SigningKeys/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the SigningKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SigningKeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SigningKeyInstance: + """ + Fetch the SigningKeyInstance + + + :returns: The fetched SigningKeyInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SigningKeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SigningKeyInstance: + """ + Asynchronous coroutine to fetch the SigningKeyInstance + + + :returns: The fetched SigningKeyInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SigningKeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> SigningKeyInstance: + """ + Update the SigningKeyInstance + + :param friendly_name: + + :returns: The updated SigningKeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SigningKeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> SigningKeyInstance: + """ + Asynchronous coroutine to update the SigningKeyInstance + + :param friendly_name: + + :returns: The updated SigningKeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SigningKeyInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SigningKeyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SigningKeyInstance: + """ + Build an instance of SigningKeyInstance + + :param payload: Payload response from the API + """ + return SigningKeyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SigningKeyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the SigningKeyList + + :param version: Version that contains the resource + :param account_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SigningKeys.json".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SigningKeyInstance]: + """ + Streams SigningKeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SigningKeyInstance]: + """ + Asynchronously streams SigningKeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SigningKeyInstance]: + """ + Lists SigningKeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SigningKeyInstance]: + """ + Asynchronously lists SigningKeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SigningKeyPage: + """ + Retrieve a single page of SigningKeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SigningKeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SigningKeyPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SigningKeyPage: + """ + Asynchronously retrieve a single page of SigningKeyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SigningKeyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SigningKeyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SigningKeyPage: + """ + Retrieve a specific page of SigningKeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SigningKeyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SigningKeyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SigningKeyPage: + """ + Asynchronously retrieve a specific page of SigningKeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SigningKeyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SigningKeyPage(self._version, response, self._solution) + + def get(self, sid: str) -> SigningKeyContext: + """ + Constructs a SigningKeyContext + + :param sid: + """ + return SigningKeyContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SigningKeyContext: + """ + Constructs a SigningKeyContext + + :param sid: + """ + return SigningKeyContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__init__.py new file mode 100644 index 0000000..12c695f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__init__.py @@ -0,0 +1,89 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.api.v2010.account.sip.credential_list import CredentialListList +from twilio.rest.api.v2010.account.sip.domain import DomainList +from twilio.rest.api.v2010.account.sip.ip_access_control_list import ( + IpAccessControlListList, +) + + +class SipList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the SipList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the SipDomain resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SIP.json".format(**self._solution) + + self._credential_lists: Optional[CredentialListList] = None + self._domains: Optional[DomainList] = None + self._ip_access_control_lists: Optional[IpAccessControlListList] = None + + @property + def credential_lists(self) -> CredentialListList: + """ + Access the credential_lists + """ + if self._credential_lists is None: + self._credential_lists = CredentialListList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._credential_lists + + @property + def domains(self) -> DomainList: + """ + Access the domains + """ + if self._domains is None: + self._domains = DomainList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._domains + + @property + def ip_access_control_lists(self) -> IpAccessControlListList: + """ + Access the ip_access_control_lists + """ + if self._ip_access_control_lists is None: + self._ip_access_control_lists = IpAccessControlListList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._ip_access_control_lists + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d012cde Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__init__.py new file mode 100644 index 0000000..634500f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__init__.py @@ -0,0 +1,623 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.sip.credential_list.credential import CredentialList + + +class CredentialListInstance(InstanceResource): + """ + :ivar account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) that owns this resource. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar friendly_name: A human readable descriptive text that describes the CredentialList, up to 64 characters long. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar subresource_uris: A list of credentials associated with this credential list. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[CredentialListContext] = None + + @property + def _proxy(self) -> "CredentialListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialListContext for this CredentialListInstance + """ + if self._context is None: + self._context = CredentialListContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialListInstance": + """ + Fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialListInstance": + """ + Asynchronous coroutine to fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: str) -> "CredentialListInstance": + """ + Update the CredentialListInstance + + :param friendly_name: A human readable descriptive text for a CredentialList, up to 64 characters long. + + :returns: The updated CredentialListInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async(self, friendly_name: str) -> "CredentialListInstance": + """ + Asynchronous coroutine to update the CredentialListInstance + + :param friendly_name: A human readable descriptive text for a CredentialList, up to 64 characters long. + + :returns: The updated CredentialListInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + @property + def credentials(self) -> CredentialList: + """ + Access the credentials + """ + return self._proxy.credentials + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the CredentialListContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + :param sid: The credential list Sid that uniquely identifies this resource + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/CredentialLists/{sid}.json".format( + **self._solution + ) + + self._credentials: Optional[CredentialList] = None + + def delete(self) -> bool: + """ + Deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialListInstance: + """ + Fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialListInstance: + """ + Asynchronous coroutine to fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update(self, friendly_name: str) -> CredentialListInstance: + """ + Update the CredentialListInstance + + :param friendly_name: A human readable descriptive text for a CredentialList, up to 64 characters long. + + :returns: The updated CredentialListInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, friendly_name: str) -> CredentialListInstance: + """ + Asynchronous coroutine to update the CredentialListInstance + + :param friendly_name: A human readable descriptive text for a CredentialList, up to 64 characters long. + + :returns: The updated CredentialListInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def credentials(self) -> CredentialList: + """ + Access the credentials + """ + if self._credentials is None: + self._credentials = CredentialList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._credentials + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialListInstance: + """ + Build an instance of CredentialListInstance + + :param payload: Payload response from the API + """ + return CredentialListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialListList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the CredentialListList + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/CredentialLists.json".format( + **self._solution + ) + + def create(self, friendly_name: str) -> CredentialListInstance: + """ + Create the CredentialListInstance + + :param friendly_name: A human readable descriptive text that describes the CredentialList, up to 64 characters long. + + :returns: The created CredentialListInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async(self, friendly_name: str) -> CredentialListInstance: + """ + Asynchronously create the CredentialListInstance + + :param friendly_name: A human readable descriptive text that describes the CredentialList, up to 64 characters long. + + :returns: The created CredentialListInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialListInstance]: + """ + Streams CredentialListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialListInstance]: + """ + Asynchronously streams CredentialListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListInstance]: + """ + Lists CredentialListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListInstance]: + """ + Asynchronously lists CredentialListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListPage: + """ + Retrieve a single page of CredentialListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListPage: + """ + Asynchronously retrieve a single page of CredentialListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CredentialListPage: + """ + Retrieve a specific page of CredentialListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CredentialListPage: + """ + Asynchronously retrieve a specific page of CredentialListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialListPage(self._version, response, self._solution) + + def get(self, sid: str) -> CredentialListContext: + """ + Constructs a CredentialListContext + + :param sid: The credential list Sid that uniquely identifies this resource + """ + return CredentialListContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> CredentialListContext: + """ + Constructs a CredentialListContext + + :param sid: The credential list Sid that uniquely identifies this resource + """ + return CredentialListContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..678f780 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..9be9a9a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/credential.py new file mode 100644 index 0000000..3d2c24b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/credential_list/credential.py @@ -0,0 +1,636 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique id of the Account that is responsible for this resource. + :ivar credential_list_sid: The unique id that identifies the credential list that includes this credential. + :ivar username: The username for this credential. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + credential_list_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.credential_list_sid: Optional[str] = payload.get("credential_list_sid") + self.username: Optional[str] = payload.get("username") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "credential_list_sid": credential_list_sid, + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, password: Union[str, object] = values.unset + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + password=password, + ) + + async def update_async( + self, password: Union[str, object] = values.unset + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + password=password, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__( + self, version: Version, account_sid: str, credential_list_sid: str, sid: str + ): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + :param credential_list_sid: The unique id that identifies the credential list that includes this credential. + :param sid: The unique id that identifies the resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "credential_list_sid": credential_list_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/CredentialLists/{credential_list_sid}/Credentials/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=self._solution["sid"], + ) + + def update(self, password: Union[str, object] = values.unset) -> CredentialInstance: + """ + Update the CredentialInstance + + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "Password": password, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, password: Union[str, object] = values.unset + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "Password": password, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version, account_sid: str, credential_list_sid: str): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + :param credential_list_sid: The unique id that identifies the credential list that contains the desired credentials. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "credential_list_sid": credential_list_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/CredentialLists/{credential_list_sid}/Credentials.json".format( + **self._solution + ) + + def create(self, username: str, password: str) -> CredentialInstance: + """ + Create the CredentialInstance + + :param username: The username that will be passed when authenticating SIP requests. The username should be sent in response to Twilio's challenge of the initial INVITE. It can be up to 32 characters long. + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Username": username, + "Password": password, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + ) + + async def create_async(self, username: str, password: str) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param username: The username that will be passed when authenticating SIP requests. The username should be sent in response to Twilio's challenge of the initial INVITE. It can be up to 32 characters long. + :param password: The password that the username will use when authenticating SIP requests. The password must be a minimum of 12 characters, contain at least 1 digit, and have mixed case. (eg `IWasAtSignal2018`) + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Username": username, + "Password": password, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response, self._solution) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The unique id that identifies the resource to update. + """ + return CredentialContext( + self._version, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The unique id that identifies the resource to update. + """ + return CredentialContext( + self._version, + account_sid=self._solution["account_sid"], + credential_list_sid=self._solution["credential_list_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__init__.py new file mode 100644 index 0000000..b469628 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__init__.py @@ -0,0 +1,947 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.sip.domain.auth_types import AuthTypesList +from twilio.rest.api.v2010.account.sip.domain.credential_list_mapping import ( + CredentialListMappingList, +) +from twilio.rest.api.v2010.account.sip.domain.ip_access_control_list_mapping import ( + IpAccessControlListMappingList, +) + + +class DomainInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the SipDomain resource. + :ivar api_version: The API version used to process the call. + :ivar auth_type: The types of authentication you have mapped to your domain. Can be: `IP_ACL` and `CREDENTIAL_LIST`. If you have both defined for your domain, both will be returned in a comma delimited string. If `auth_type` is not defined, the domain will not be able to receive any traffic. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \"-\" and must end with `sip.twilio.com`. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar sid: The unique string that that we created to identify the SipDomain resource. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML requested from `voice_url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_status_callback_method: The HTTP method we use to call `voice_status_callback_url`. Either `GET` or `POST`. + :ivar voice_status_callback_url: The URL that we call to pass status parameters (such as call ended) to your application. + :ivar voice_url: The URL we call using the `voice_method` when the domain receives a call. + :ivar subresource_uris: A list of mapping resources associated with the SIP Domain resource identified by their relative URIs. + :ivar sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. + :ivar emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :ivar secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :ivar byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :ivar emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.auth_type: Optional[str] = payload.get("auth_type") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.domain_name: Optional[str] = payload.get("domain_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_status_callback_method: Optional[str] = payload.get( + "voice_status_callback_method" + ) + self.voice_status_callback_url: Optional[str] = payload.get( + "voice_status_callback_url" + ) + self.voice_url: Optional[str] = payload.get("voice_url") + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.sip_registration: Optional[bool] = payload.get("sip_registration") + self.emergency_calling_enabled: Optional[bool] = payload.get( + "emergency_calling_enabled" + ) + self.secure: Optional[bool] = payload.get("secure") + self.byoc_trunk_sid: Optional[str] = payload.get("byoc_trunk_sid") + self.emergency_caller_sid: Optional[str] = payload.get("emergency_caller_sid") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[DomainContext] = None + + @property + def _proxy(self) -> "DomainContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DomainContext for this DomainInstance + """ + if self._context is None: + self._context = DomainContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DomainInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DomainInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DomainInstance": + """ + Fetch the DomainInstance + + + :returns: The fetched DomainInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DomainInstance": + """ + Asynchronous coroutine to fetch the DomainInstance + + + :returns: The fetched DomainInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + domain_name: Union[str, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> "DomainInstance": + """ + Update the DomainInstance + + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_url: The URL we should call when the domain receives a call. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The updated DomainInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_status_callback_method=voice_status_callback_method, + voice_status_callback_url=voice_status_callback_url, + voice_url=voice_url, + sip_registration=sip_registration, + domain_name=domain_name, + emergency_calling_enabled=emergency_calling_enabled, + secure=secure, + byoc_trunk_sid=byoc_trunk_sid, + emergency_caller_sid=emergency_caller_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + domain_name: Union[str, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> "DomainInstance": + """ + Asynchronous coroutine to update the DomainInstance + + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_url: The URL we should call when the domain receives a call. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The updated DomainInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_status_callback_method=voice_status_callback_method, + voice_status_callback_url=voice_status_callback_url, + voice_url=voice_url, + sip_registration=sip_registration, + domain_name=domain_name, + emergency_calling_enabled=emergency_calling_enabled, + secure=secure, + byoc_trunk_sid=byoc_trunk_sid, + emergency_caller_sid=emergency_caller_sid, + ) + + @property + def auth(self) -> AuthTypesList: + """ + Access the auth + """ + return self._proxy.auth + + @property + def credential_list_mappings(self) -> CredentialListMappingList: + """ + Access the credential_list_mappings + """ + return self._proxy.credential_list_mappings + + @property + def ip_access_control_list_mappings(self) -> IpAccessControlListMappingList: + """ + Access the ip_access_control_list_mappings + """ + return self._proxy.ip_access_control_list_mappings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the DomainContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the SipDomain resource to update. + :param sid: The Twilio-provided string that uniquely identifies the SipDomain resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{sid}.json".format( + **self._solution + ) + + self._auth: Optional[AuthTypesList] = None + self._credential_list_mappings: Optional[CredentialListMappingList] = None + self._ip_access_control_list_mappings: Optional[ + IpAccessControlListMappingList + ] = None + + def delete(self) -> bool: + """ + Deletes the DomainInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DomainInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DomainInstance: + """ + Fetch the DomainInstance + + + :returns: The fetched DomainInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DomainInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DomainInstance: + """ + Asynchronous coroutine to fetch the DomainInstance + + + :returns: The fetched DomainInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DomainInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + domain_name: Union[str, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> DomainInstance: + """ + Update the DomainInstance + + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_url: The URL we should call when the domain receives a call. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The updated DomainInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceStatusCallbackMethod": voice_status_callback_method, + "VoiceStatusCallbackUrl": voice_status_callback_url, + "VoiceUrl": voice_url, + "SipRegistration": serialize.boolean_to_string(sip_registration), + "DomainName": domain_name, + "EmergencyCallingEnabled": serialize.boolean_to_string( + emergency_calling_enabled + ), + "Secure": serialize.boolean_to_string(secure), + "ByocTrunkSid": byoc_trunk_sid, + "EmergencyCallerSid": emergency_caller_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + domain_name: Union[str, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> DomainInstance: + """ + Asynchronous coroutine to update the DomainInstance + + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_url: The URL we should call when the domain receives a call. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The updated DomainInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceStatusCallbackMethod": voice_status_callback_method, + "VoiceStatusCallbackUrl": voice_status_callback_url, + "VoiceUrl": voice_url, + "SipRegistration": serialize.boolean_to_string(sip_registration), + "DomainName": domain_name, + "EmergencyCallingEnabled": serialize.boolean_to_string( + emergency_calling_enabled + ), + "Secure": serialize.boolean_to_string(secure), + "ByocTrunkSid": byoc_trunk_sid, + "EmergencyCallerSid": emergency_caller_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def auth(self) -> AuthTypesList: + """ + Access the auth + """ + if self._auth is None: + self._auth = AuthTypesList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._auth + + @property + def credential_list_mappings(self) -> CredentialListMappingList: + """ + Access the credential_list_mappings + """ + if self._credential_list_mappings is None: + self._credential_list_mappings = CredentialListMappingList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._credential_list_mappings + + @property + def ip_access_control_list_mappings(self) -> IpAccessControlListMappingList: + """ + Access the ip_access_control_list_mappings + """ + if self._ip_access_control_list_mappings is None: + self._ip_access_control_list_mappings = IpAccessControlListMappingList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._ip_access_control_list_mappings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DomainInstance: + """ + Build an instance of DomainInstance + + :param payload: Payload response from the API + """ + return DomainInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DomainList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the DomainList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the SipDomain resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains.json".format(**self._solution) + + def create( + self, + domain_name: str, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> DomainInstance: + """ + Create the DomainInstance + + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_url: The URL we should when the domain receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The created DomainInstance + """ + + data = values.of( + { + "DomainName": domain_name, + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceStatusCallbackUrl": voice_status_callback_url, + "VoiceStatusCallbackMethod": voice_status_callback_method, + "SipRegistration": serialize.boolean_to_string(sip_registration), + "EmergencyCallingEnabled": serialize.boolean_to_string( + emergency_calling_enabled + ), + "Secure": serialize.boolean_to_string(secure), + "ByocTrunkSid": byoc_trunk_sid, + "EmergencyCallerSid": emergency_caller_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + domain_name: str, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_status_callback_url: Union[str, object] = values.unset, + voice_status_callback_method: Union[str, object] = values.unset, + sip_registration: Union[bool, object] = values.unset, + emergency_calling_enabled: Union[bool, object] = values.unset, + secure: Union[bool, object] = values.unset, + byoc_trunk_sid: Union[str, object] = values.unset, + emergency_caller_sid: Union[str, object] = values.unset, + ) -> DomainInstance: + """ + Asynchronously create the DomainInstance + + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and \\\"-\\\" and must end with `sip.twilio.com`. + :param friendly_name: A descriptive string that you created to describe the resource. It can be up to 64 characters long. + :param voice_url: The URL we should when the domain receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param voice_status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param voice_status_callback_method: The HTTP method we should use to call `voice_status_callback_url`. Can be: `GET` or `POST`. + :param sip_registration: Whether to allow SIP Endpoints to register with the domain to receive calls. Can be `true` or `false`. `true` allows SIP Endpoints to register with the domain to receive calls, `false` does not. + :param emergency_calling_enabled: Whether emergency calling is enabled for the domain. If enabled, allows emergency calls on the domain from phone numbers with validated addresses. + :param secure: Whether secure SIP is enabled for the domain. If enabled, TLS will be enforced and SRTP will be negotiated on all incoming calls to this sip domain. + :param byoc_trunk_sid: The SID of the BYOC Trunk(Bring Your Own Carrier) resource that the Sip Domain will be associated with. + :param emergency_caller_sid: Whether an emergency caller sid is configured for the domain. If present, this phone number will be used as the callback for the emergency call. + + :returns: The created DomainInstance + """ + + data = values.of( + { + "DomainName": domain_name, + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceStatusCallbackUrl": voice_status_callback_url, + "VoiceStatusCallbackMethod": voice_status_callback_method, + "SipRegistration": serialize.boolean_to_string(sip_registration), + "EmergencyCallingEnabled": serialize.boolean_to_string( + emergency_calling_enabled + ), + "Secure": serialize.boolean_to_string(secure), + "ByocTrunkSid": byoc_trunk_sid, + "EmergencyCallerSid": emergency_caller_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DomainInstance]: + """ + Streams DomainInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DomainInstance]: + """ + Asynchronously streams DomainInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DomainInstance]: + """ + Lists DomainInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DomainInstance]: + """ + Asynchronously lists DomainInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DomainPage: + """ + Retrieve a single page of DomainInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DomainInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DomainPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DomainPage: + """ + Asynchronously retrieve a single page of DomainInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DomainInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DomainPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DomainPage: + """ + Retrieve a specific page of DomainInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DomainInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DomainPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DomainPage: + """ + Asynchronously retrieve a specific page of DomainInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DomainInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DomainPage(self._version, response, self._solution) + + def get(self, sid: str) -> DomainContext: + """ + Constructs a DomainContext + + :param sid: The Twilio-provided string that uniquely identifies the SipDomain resource to update. + """ + return DomainContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> DomainContext: + """ + Constructs a DomainContext + + :param sid: The Twilio-provided string that uniquely identifies the SipDomain resource to update. + """ + return DomainContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..af906ad Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/credential_list_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/credential_list_mapping.cpython-310.pyc new file mode 100644 index 0000000..5be802d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/credential_list_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/ip_access_control_list_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/ip_access_control_list_mapping.cpython-310.pyc new file mode 100644 index 0000000..906e388 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/__pycache__/ip_access_control_list_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__init__.py new file mode 100644 index 0000000..73a81ac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__init__.py @@ -0,0 +1,86 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.api.v2010.account.sip.domain.auth_types.auth_type_calls import ( + AuthTypeCallsList, +) +from twilio.rest.api.v2010.account.sip.domain.auth_types.auth_type_registrations import ( + AuthTypeRegistrationsList, +) + + +class AuthTypesList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthTypesList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth.json".format( + **self._solution + ) + + self._calls: Optional[AuthTypeCallsList] = None + self._registrations: Optional[AuthTypeRegistrationsList] = None + + @property + def calls(self) -> AuthTypeCallsList: + """ + Access the calls + """ + if self._calls is None: + self._calls = AuthTypeCallsList( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + return self._calls + + @property + def registrations(self) -> AuthTypeRegistrationsList: + """ + Access the registrations + """ + if self._registrations is None: + self._registrations = AuthTypeRegistrationsList( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + return self._registrations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0649af3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__init__.py new file mode 100644 index 0000000..79a42fa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__init__.py @@ -0,0 +1,96 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.api.v2010.account.sip.domain.auth_types.auth_type_calls.auth_calls_credential_list_mapping import ( + AuthCallsCredentialListMappingList, +) +from twilio.rest.api.v2010.account.sip.domain.auth_types.auth_type_calls.auth_calls_ip_access_control_list_mapping import ( + AuthCallsIpAccessControlListMappingList, +) + + +class AuthTypeCallsList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthTypeCallsList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = ( + "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Calls.json".format( + **self._solution + ) + ) + + self._credential_list_mappings: Optional[AuthCallsCredentialListMappingList] = ( + None + ) + self._ip_access_control_list_mappings: Optional[ + AuthCallsIpAccessControlListMappingList + ] = None + + @property + def credential_list_mappings(self) -> AuthCallsCredentialListMappingList: + """ + Access the credential_list_mappings + """ + if self._credential_list_mappings is None: + self._credential_list_mappings = AuthCallsCredentialListMappingList( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + return self._credential_list_mappings + + @property + def ip_access_control_list_mappings( + self, + ) -> AuthCallsIpAccessControlListMappingList: + """ + Access the ip_access_control_list_mappings + """ + if self._ip_access_control_list_mappings is None: + self._ip_access_control_list_mappings = ( + AuthCallsIpAccessControlListMappingList( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + ) + return self._ip_access_control_list_mappings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fe2efc9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_credential_list_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_credential_list_mapping.cpython-310.pyc new file mode 100644 index 0000000..3a8a9a0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_credential_list_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_ip_access_control_list_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_ip_access_control_list_mapping.cpython-310.pyc new file mode 100644 index 0000000..f683d5c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/__pycache__/auth_calls_ip_access_control_list_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_credential_list_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_credential_list_mapping.py new file mode 100644 index 0000000..81bb5b0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_credential_list_mapping.py @@ -0,0 +1,560 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AuthCallsCredentialListMappingInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar sid: The unique string that that we created to identify the CredentialListMapping resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + domain_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid or self.sid, + } + self._context: Optional[AuthCallsCredentialListMappingContext] = None + + @property + def _proxy(self) -> "AuthCallsCredentialListMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthCallsCredentialListMappingContext for this AuthCallsCredentialListMappingInstance + """ + if self._context is None: + self._context = AuthCallsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AuthCallsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthCallsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AuthCallsCredentialListMappingInstance": + """ + Fetch the AuthCallsCredentialListMappingInstance + + + :returns: The fetched AuthCallsCredentialListMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthCallsCredentialListMappingInstance": + """ + Asynchronous coroutine to fetch the AuthCallsCredentialListMappingInstance + + + :returns: The fetched AuthCallsCredentialListMappingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthCallsCredentialListMappingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, domain_sid: str, sid: str): + """ + Initialize the AuthCallsCredentialListMappingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Calls/CredentialListMappings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the AuthCallsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthCallsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AuthCallsCredentialListMappingInstance: + """ + Fetch the AuthCallsCredentialListMappingInstance + + + :returns: The fetched AuthCallsCredentialListMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthCallsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AuthCallsCredentialListMappingInstance: + """ + Asynchronous coroutine to fetch the AuthCallsCredentialListMappingInstance + + + :returns: The fetched AuthCallsCredentialListMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthCallsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthCallsCredentialListMappingPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> AuthCallsCredentialListMappingInstance: + """ + Build an instance of AuthCallsCredentialListMappingInstance + + :param payload: Payload response from the API + """ + return AuthCallsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthCallsCredentialListMappingList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthCallsCredentialListMappingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resources to read. + :param domain_sid: The SID of the SIP domain that contains the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Calls/CredentialListMappings.json".format( + **self._solution + ) + + def create( + self, credential_list_sid: str + ) -> AuthCallsCredentialListMappingInstance: + """ + Create the AuthCallsCredentialListMappingInstance + + :param credential_list_sid: The SID of the CredentialList resource to map to the SIP domain. + + :returns: The created AuthCallsCredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthCallsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + async def create_async( + self, credential_list_sid: str + ) -> AuthCallsCredentialListMappingInstance: + """ + Asynchronously create the AuthCallsCredentialListMappingInstance + + :param credential_list_sid: The SID of the CredentialList resource to map to the SIP domain. + + :returns: The created AuthCallsCredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthCallsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthCallsCredentialListMappingInstance]: + """ + Streams AuthCallsCredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthCallsCredentialListMappingInstance]: + """ + Asynchronously streams AuthCallsCredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthCallsCredentialListMappingInstance]: + """ + Lists AuthCallsCredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthCallsCredentialListMappingInstance]: + """ + Asynchronously lists AuthCallsCredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthCallsCredentialListMappingPage: + """ + Retrieve a single page of AuthCallsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthCallsCredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthCallsCredentialListMappingPage( + self._version, response, self._solution + ) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthCallsCredentialListMappingPage: + """ + Asynchronously retrieve a single page of AuthCallsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthCallsCredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthCallsCredentialListMappingPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> AuthCallsCredentialListMappingPage: + """ + Retrieve a specific page of AuthCallsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthCallsCredentialListMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthCallsCredentialListMappingPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> AuthCallsCredentialListMappingPage: + """ + Asynchronously retrieve a specific page of AuthCallsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthCallsCredentialListMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthCallsCredentialListMappingPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> AuthCallsCredentialListMappingContext: + """ + Constructs a AuthCallsCredentialListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + return AuthCallsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AuthCallsCredentialListMappingContext: + """ + Constructs a AuthCallsCredentialListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + return AuthCallsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_ip_access_control_list_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_ip_access_control_list_mapping.py new file mode 100644 index 0000000..a690ef4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_calls/auth_calls_ip_access_control_list_mapping.py @@ -0,0 +1,564 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AuthCallsIpAccessControlListMappingInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IpAccessControlListMapping resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar sid: The unique string that that we created to identify the IpAccessControlListMapping resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + domain_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid or self.sid, + } + self._context: Optional[AuthCallsIpAccessControlListMappingContext] = None + + @property + def _proxy(self) -> "AuthCallsIpAccessControlListMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthCallsIpAccessControlListMappingContext for this AuthCallsIpAccessControlListMappingInstance + """ + if self._context is None: + self._context = AuthCallsIpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AuthCallsIpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthCallsIpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AuthCallsIpAccessControlListMappingInstance": + """ + Fetch the AuthCallsIpAccessControlListMappingInstance + + + :returns: The fetched AuthCallsIpAccessControlListMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthCallsIpAccessControlListMappingInstance": + """ + Asynchronous coroutine to fetch the AuthCallsIpAccessControlListMappingInstance + + + :returns: The fetched AuthCallsIpAccessControlListMappingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return ( + "".format( + context + ) + ) + + +class AuthCallsIpAccessControlListMappingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, domain_sid: str, sid: str): + """ + Initialize the AuthCallsIpAccessControlListMappingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IpAccessControlListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the IpAccessControlListMapping resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Calls/IpAccessControlListMappings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the AuthCallsIpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthCallsIpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AuthCallsIpAccessControlListMappingInstance: + """ + Fetch the AuthCallsIpAccessControlListMappingInstance + + + :returns: The fetched AuthCallsIpAccessControlListMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthCallsIpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AuthCallsIpAccessControlListMappingInstance: + """ + Asynchronous coroutine to fetch the AuthCallsIpAccessControlListMappingInstance + + + :returns: The fetched AuthCallsIpAccessControlListMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthCallsIpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return ( + "".format( + context + ) + ) + + +class AuthCallsIpAccessControlListMappingPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> AuthCallsIpAccessControlListMappingInstance: + """ + Build an instance of AuthCallsIpAccessControlListMappingInstance + + :param payload: Payload response from the API + """ + return AuthCallsIpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthCallsIpAccessControlListMappingList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthCallsIpAccessControlListMappingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IpAccessControlListMapping resources to read. + :param domain_sid: The SID of the SIP domain that contains the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Calls/IpAccessControlListMappings.json".format( + **self._solution + ) + + def create( + self, ip_access_control_list_sid: str + ) -> AuthCallsIpAccessControlListMappingInstance: + """ + Create the AuthCallsIpAccessControlListMappingInstance + + :param ip_access_control_list_sid: The SID of the IpAccessControlList resource to map to the SIP domain. + + :returns: The created AuthCallsIpAccessControlListMappingInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthCallsIpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + async def create_async( + self, ip_access_control_list_sid: str + ) -> AuthCallsIpAccessControlListMappingInstance: + """ + Asynchronously create the AuthCallsIpAccessControlListMappingInstance + + :param ip_access_control_list_sid: The SID of the IpAccessControlList resource to map to the SIP domain. + + :returns: The created AuthCallsIpAccessControlListMappingInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthCallsIpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthCallsIpAccessControlListMappingInstance]: + """ + Streams AuthCallsIpAccessControlListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthCallsIpAccessControlListMappingInstance]: + """ + Asynchronously streams AuthCallsIpAccessControlListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthCallsIpAccessControlListMappingInstance]: + """ + Lists AuthCallsIpAccessControlListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthCallsIpAccessControlListMappingInstance]: + """ + Asynchronously lists AuthCallsIpAccessControlListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthCallsIpAccessControlListMappingPage: + """ + Retrieve a single page of AuthCallsIpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthCallsIpAccessControlListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthCallsIpAccessControlListMappingPage( + self._version, response, self._solution + ) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthCallsIpAccessControlListMappingPage: + """ + Asynchronously retrieve a single page of AuthCallsIpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthCallsIpAccessControlListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthCallsIpAccessControlListMappingPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> AuthCallsIpAccessControlListMappingPage: + """ + Retrieve a specific page of AuthCallsIpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthCallsIpAccessControlListMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthCallsIpAccessControlListMappingPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> AuthCallsIpAccessControlListMappingPage: + """ + Asynchronously retrieve a specific page of AuthCallsIpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthCallsIpAccessControlListMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthCallsIpAccessControlListMappingPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> AuthCallsIpAccessControlListMappingContext: + """ + Constructs a AuthCallsIpAccessControlListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the IpAccessControlListMapping resource to fetch. + """ + return AuthCallsIpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AuthCallsIpAccessControlListMappingContext: + """ + Constructs a AuthCallsIpAccessControlListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the IpAccessControlListMapping resource to fetch. + """ + return AuthCallsIpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__init__.py new file mode 100644 index 0000000..c10c747 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__init__.py @@ -0,0 +1,71 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.api.v2010.account.sip.domain.auth_types.auth_type_registrations.auth_registrations_credential_list_mapping import ( + AuthRegistrationsCredentialListMappingList, +) + + +class AuthTypeRegistrationsList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthTypeRegistrationsList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Registrations.json".format( + **self._solution + ) + + self._credential_list_mappings: Optional[ + AuthRegistrationsCredentialListMappingList + ] = None + + @property + def credential_list_mappings(self) -> AuthRegistrationsCredentialListMappingList: + """ + Access the credential_list_mappings + """ + if self._credential_list_mappings is None: + self._credential_list_mappings = AuthRegistrationsCredentialListMappingList( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + return self._credential_list_mappings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7e472d6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/auth_registrations_credential_list_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/auth_registrations_credential_list_mapping.cpython-310.pyc new file mode 100644 index 0000000..876a139 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/__pycache__/auth_registrations_credential_list_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/auth_registrations_credential_list_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/auth_registrations_credential_list_mapping.py new file mode 100644 index 0000000..2b03bb2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/auth_types/auth_type_registrations/auth_registrations_credential_list_mapping.py @@ -0,0 +1,560 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AuthRegistrationsCredentialListMappingInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar sid: The unique string that that we created to identify the CredentialListMapping resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + domain_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid or self.sid, + } + self._context: Optional[AuthRegistrationsCredentialListMappingContext] = None + + @property + def _proxy(self) -> "AuthRegistrationsCredentialListMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthRegistrationsCredentialListMappingContext for this AuthRegistrationsCredentialListMappingInstance + """ + if self._context is None: + self._context = AuthRegistrationsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AuthRegistrationsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthRegistrationsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AuthRegistrationsCredentialListMappingInstance": + """ + Fetch the AuthRegistrationsCredentialListMappingInstance + + + :returns: The fetched AuthRegistrationsCredentialListMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthRegistrationsCredentialListMappingInstance": + """ + Asynchronous coroutine to fetch the AuthRegistrationsCredentialListMappingInstance + + + :returns: The fetched AuthRegistrationsCredentialListMappingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthRegistrationsCredentialListMappingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, domain_sid: str, sid: str): + """ + Initialize the AuthRegistrationsCredentialListMappingContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resource to fetch. + :param domain_sid: The SID of the SIP domain that contains the resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Registrations/CredentialListMappings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the AuthRegistrationsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthRegistrationsCredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AuthRegistrationsCredentialListMappingInstance: + """ + Fetch the AuthRegistrationsCredentialListMappingInstance + + + :returns: The fetched AuthRegistrationsCredentialListMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthRegistrationsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AuthRegistrationsCredentialListMappingInstance: + """ + Asynchronous coroutine to fetch the AuthRegistrationsCredentialListMappingInstance + + + :returns: The fetched AuthRegistrationsCredentialListMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthRegistrationsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthRegistrationsCredentialListMappingPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> AuthRegistrationsCredentialListMappingInstance: + """ + Build an instance of AuthRegistrationsCredentialListMappingInstance + + :param payload: Payload response from the API + """ + return AuthRegistrationsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthRegistrationsCredentialListMappingList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the AuthRegistrationsCredentialListMappingList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialListMapping resources to read. + :param domain_sid: The SID of the SIP domain that contains the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/Auth/Registrations/CredentialListMappings.json".format( + **self._solution + ) + + def create( + self, credential_list_sid: str + ) -> AuthRegistrationsCredentialListMappingInstance: + """ + Create the AuthRegistrationsCredentialListMappingInstance + + :param credential_list_sid: The SID of the CredentialList resource to map to the SIP domain. + + :returns: The created AuthRegistrationsCredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthRegistrationsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + async def create_async( + self, credential_list_sid: str + ) -> AuthRegistrationsCredentialListMappingInstance: + """ + Asynchronously create the AuthRegistrationsCredentialListMappingInstance + + :param credential_list_sid: The SID of the CredentialList resource to map to the SIP domain. + + :returns: The created AuthRegistrationsCredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthRegistrationsCredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthRegistrationsCredentialListMappingInstance]: + """ + Streams AuthRegistrationsCredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthRegistrationsCredentialListMappingInstance]: + """ + Asynchronously streams AuthRegistrationsCredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthRegistrationsCredentialListMappingInstance]: + """ + Lists AuthRegistrationsCredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthRegistrationsCredentialListMappingInstance]: + """ + Asynchronously lists AuthRegistrationsCredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthRegistrationsCredentialListMappingPage: + """ + Retrieve a single page of AuthRegistrationsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthRegistrationsCredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthRegistrationsCredentialListMappingPage( + self._version, response, self._solution + ) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthRegistrationsCredentialListMappingPage: + """ + Asynchronously retrieve a single page of AuthRegistrationsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthRegistrationsCredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthRegistrationsCredentialListMappingPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> AuthRegistrationsCredentialListMappingPage: + """ + Retrieve a specific page of AuthRegistrationsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthRegistrationsCredentialListMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthRegistrationsCredentialListMappingPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> AuthRegistrationsCredentialListMappingPage: + """ + Asynchronously retrieve a specific page of AuthRegistrationsCredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthRegistrationsCredentialListMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthRegistrationsCredentialListMappingPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> AuthRegistrationsCredentialListMappingContext: + """ + Constructs a AuthRegistrationsCredentialListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + return AuthRegistrationsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AuthRegistrationsCredentialListMappingContext: + """ + Constructs a AuthRegistrationsCredentialListMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the CredentialListMapping resource to fetch. + """ + return AuthRegistrationsCredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/credential_list_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/credential_list_mapping.py new file mode 100644 index 0000000..f7838bf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/credential_list_mapping.py @@ -0,0 +1,546 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialListMappingInstance(InstanceResource): + """ + :ivar account_sid: The unique id of the Account that is responsible for this resource. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar domain_sid: The unique string that is created to identify the SipDomain resource. + :ivar friendly_name: A human readable descriptive text for this resource, up to 64 characters long. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + domain_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid or self.sid, + } + self._context: Optional[CredentialListMappingContext] = None + + @property + def _proxy(self) -> "CredentialListMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialListMappingContext for this CredentialListMappingInstance + """ + if self._context is None: + self._context = CredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialListMappingInstance": + """ + Fetch the CredentialListMappingInstance + + + :returns: The fetched CredentialListMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialListMappingInstance": + """ + Asynchronous coroutine to fetch the CredentialListMappingInstance + + + :returns: The fetched CredentialListMappingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListMappingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, domain_sid: str, sid: str): + """ + Initialize the CredentialListMappingContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + :param domain_sid: A 34 character string that uniquely identifies the SIP Domain that includes the resource to fetch. + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/CredentialListMappings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the CredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialListMappingInstance: + """ + Fetch the CredentialListMappingInstance + + + :returns: The fetched CredentialListMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialListMappingInstance: + """ + Asynchronous coroutine to fetch the CredentialListMappingInstance + + + :returns: The fetched CredentialListMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListMappingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialListMappingInstance: + """ + Build an instance of CredentialListMappingInstance + + :param payload: Payload response from the API + """ + return CredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialListMappingList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the CredentialListMappingList + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + :param domain_sid: A 34 character string that uniquely identifies the SIP Domain that includes the resource to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/CredentialListMappings.json".format( + **self._solution + ) + + def create(self, credential_list_sid: str) -> CredentialListMappingInstance: + """ + Create the CredentialListMappingInstance + + :param credential_list_sid: A 34 character string that uniquely identifies the CredentialList resource to map to the SIP domain. + + :returns: The created CredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + async def create_async( + self, credential_list_sid: str + ) -> CredentialListMappingInstance: + """ + Asynchronously create the CredentialListMappingInstance + + :param credential_list_sid: A 34 character string that uniquely identifies the CredentialList resource to map to the SIP domain. + + :returns: The created CredentialListMappingInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialListMappingInstance]: + """ + Streams CredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialListMappingInstance]: + """ + Asynchronously streams CredentialListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListMappingInstance]: + """ + Lists CredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListMappingInstance]: + """ + Asynchronously lists CredentialListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListMappingPage: + """ + Retrieve a single page of CredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialListMappingPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListMappingPage: + """ + Asynchronously retrieve a single page of CredentialListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialListMappingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CredentialListMappingPage: + """ + Retrieve a specific page of CredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialListMappingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CredentialListMappingPage: + """ + Asynchronously retrieve a specific page of CredentialListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialListMappingPage(self._version, response, self._solution) + + def get(self, sid: str) -> CredentialListMappingContext: + """ + Constructs a CredentialListMappingContext + + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + return CredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> CredentialListMappingContext: + """ + Constructs a CredentialListMappingContext + + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + return CredentialListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/ip_access_control_list_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/ip_access_control_list_mapping.py new file mode 100644 index 0000000..c619be3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/domain/ip_access_control_list_mapping.py @@ -0,0 +1,552 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class IpAccessControlListMappingInstance(InstanceResource): + """ + :ivar account_sid: The unique id of the Account that is responsible for this resource. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar domain_sid: The unique string that is created to identify the SipDomain resource. + :ivar friendly_name: A human readable descriptive text for this resource, up to 64 characters long. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + domain_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid or self.sid, + } + self._context: Optional[IpAccessControlListMappingContext] = None + + @property + def _proxy(self) -> "IpAccessControlListMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpAccessControlListMappingContext for this IpAccessControlListMappingInstance + """ + if self._context is None: + self._context = IpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IpAccessControlListMappingInstance": + """ + Fetch the IpAccessControlListMappingInstance + + + :returns: The fetched IpAccessControlListMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpAccessControlListMappingInstance": + """ + Asynchronous coroutine to fetch the IpAccessControlListMappingInstance + + + :returns: The fetched IpAccessControlListMappingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class IpAccessControlListMappingContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, domain_sid: str, sid: str): + """ + Initialize the IpAccessControlListMappingContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + :param domain_sid: A 34 character string that uniquely identifies the SIP domain. + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/IpAccessControlListMappings/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IpAccessControlListMappingInstance: + """ + Fetch the IpAccessControlListMappingInstance + + + :returns: The fetched IpAccessControlListMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpAccessControlListMappingInstance: + """ + Asynchronous coroutine to fetch the IpAccessControlListMappingInstance + + + :returns: The fetched IpAccessControlListMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAccessControlListMappingPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> IpAccessControlListMappingInstance: + """ + Build an instance of IpAccessControlListMappingInstance + + :param payload: Payload response from the API + """ + return IpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpAccessControlListMappingList(ListResource): + + def __init__(self, version: Version, account_sid: str, domain_sid: str): + """ + Initialize the IpAccessControlListMappingList + + :param version: Version that contains the resource + :param account_sid: The unique id of the Account that is responsible for this resource. + :param domain_sid: A 34 character string that uniquely identifies the SIP domain. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "domain_sid": domain_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/Domains/{domain_sid}/IpAccessControlListMappings.json".format( + **self._solution + ) + + def create( + self, ip_access_control_list_sid: str + ) -> IpAccessControlListMappingInstance: + """ + Create the IpAccessControlListMappingInstance + + :param ip_access_control_list_sid: The unique id of the IP access control list to map to the SIP domain. + + :returns: The created IpAccessControlListMappingInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + async def create_async( + self, ip_access_control_list_sid: str + ) -> IpAccessControlListMappingInstance: + """ + Asynchronously create the IpAccessControlListMappingInstance + + :param ip_access_control_list_sid: The unique id of the IP access control list to map to the SIP domain. + + :returns: The created IpAccessControlListMappingInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListMappingInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpAccessControlListMappingInstance]: + """ + Streams IpAccessControlListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpAccessControlListMappingInstance]: + """ + Asynchronously streams IpAccessControlListMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListMappingInstance]: + """ + Lists IpAccessControlListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListMappingInstance]: + """ + Asynchronously lists IpAccessControlListMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListMappingPage: + """ + Retrieve a single page of IpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpAccessControlListMappingPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListMappingPage: + """ + Asynchronously retrieve a single page of IpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpAccessControlListMappingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> IpAccessControlListMappingPage: + """ + Retrieve a specific page of IpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpAccessControlListMappingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> IpAccessControlListMappingPage: + """ + Asynchronously retrieve a specific page of IpAccessControlListMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpAccessControlListMappingPage(self._version, response, self._solution) + + def get(self, sid: str) -> IpAccessControlListMappingContext: + """ + Constructs a IpAccessControlListMappingContext + + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + return IpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> IpAccessControlListMappingContext: + """ + Constructs a IpAccessControlListMappingContext + + :param sid: A 34 character string that uniquely identifies the resource to fetch. + """ + return IpAccessControlListMappingContext( + self._version, + account_sid=self._solution["account_sid"], + domain_sid=self._solution["domain_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__init__.py new file mode 100644 index 0000000..7c2fd82 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__init__.py @@ -0,0 +1,627 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.sip.ip_access_control_list.ip_address import ( + IpAddressList, +) + + +class IpAccessControlListInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) that owns this resource. + :ivar friendly_name: A human readable descriptive text, up to 255 characters long. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar subresource_uris: A list of the IpAddress resources associated with this IP access control list resource. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[IpAccessControlListContext] = None + + @property + def _proxy(self) -> "IpAccessControlListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpAccessControlListContext for this IpAccessControlListInstance + """ + if self._context is None: + self._context = IpAccessControlListContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IpAccessControlListInstance": + """ + Fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpAccessControlListInstance": + """ + Asynchronous coroutine to fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: str) -> "IpAccessControlListInstance": + """ + Update the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text, up to 255 characters long. + + :returns: The updated IpAccessControlListInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async(self, friendly_name: str) -> "IpAccessControlListInstance": + """ + Asynchronous coroutine to update the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text, up to 255 characters long. + + :returns: The updated IpAccessControlListInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + @property + def ip_addresses(self) -> IpAddressList: + """ + Access the ip_addresses + """ + return self._proxy.ip_addresses + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAccessControlListContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the IpAccessControlListContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + :param sid: A 34 character string that uniquely identifies the resource to udpate. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = ( + "/Accounts/{account_sid}/SIP/IpAccessControlLists/{sid}.json".format( + **self._solution + ) + ) + + self._ip_addresses: Optional[IpAddressList] = None + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IpAccessControlListInstance: + """ + Fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpAccessControlListInstance: + """ + Asynchronous coroutine to fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update(self, friendly_name: str) -> IpAccessControlListInstance: + """ + Update the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text, up to 255 characters long. + + :returns: The updated IpAccessControlListInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, friendly_name: str) -> IpAccessControlListInstance: + """ + Asynchronous coroutine to update the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text, up to 255 characters long. + + :returns: The updated IpAccessControlListInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + @property + def ip_addresses(self) -> IpAddressList: + """ + Access the ip_addresses + """ + if self._ip_addresses is None: + self._ip_addresses = IpAddressList( + self._version, + self._solution["account_sid"], + self._solution["sid"], + ) + return self._ip_addresses + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAccessControlListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IpAccessControlListInstance: + """ + Build an instance of IpAccessControlListInstance + + :param payload: Payload response from the API + """ + return IpAccessControlListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpAccessControlListList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the IpAccessControlListList + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/IpAccessControlLists.json".format( + **self._solution + ) + + def create(self, friendly_name: str) -> IpAccessControlListInstance: + """ + Create the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text that describes the IpAccessControlList, up to 255 characters long. + + :returns: The created IpAccessControlListInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async(self, friendly_name: str) -> IpAccessControlListInstance: + """ + Asynchronously create the IpAccessControlListInstance + + :param friendly_name: A human readable descriptive text that describes the IpAccessControlList, up to 255 characters long. + + :returns: The created IpAccessControlListInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpAccessControlListInstance]: + """ + Streams IpAccessControlListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpAccessControlListInstance]: + """ + Asynchronously streams IpAccessControlListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListInstance]: + """ + Lists IpAccessControlListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListInstance]: + """ + Asynchronously lists IpAccessControlListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListPage: + """ + Retrieve a single page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpAccessControlListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListPage: + """ + Asynchronously retrieve a single page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpAccessControlListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> IpAccessControlListPage: + """ + Retrieve a specific page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpAccessControlListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> IpAccessControlListPage: + """ + Asynchronously retrieve a specific page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpAccessControlListPage(self._version, response, self._solution) + + def get(self, sid: str) -> IpAccessControlListContext: + """ + Constructs a IpAccessControlListContext + + :param sid: A 34 character string that uniquely identifies the resource to udpate. + """ + return IpAccessControlListContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> IpAccessControlListContext: + """ + Constructs a IpAccessControlListContext + + :param sid: A 34 character string that uniquely identifies the resource to udpate. + """ + return IpAccessControlListContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f7bffab Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/ip_address.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/ip_address.cpython-310.pyc new file mode 100644 index 0000000..d04db8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/__pycache__/ip_address.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/ip_address.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/ip_address.py new file mode 100644 index 0000000..251bae9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/sip/ip_access_control_list/ip_address.py @@ -0,0 +1,694 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class IpAddressInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique id of the Account that is responsible for this resource. + :ivar friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :ivar ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :ivar cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + :ivar ip_access_control_list_sid: The unique id of the IpAccessControlList resource that includes this resource. + :ivar date_created: The date that this resource was created, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar date_updated: The date that this resource was last updated, given as GMT in [RFC 2822](https://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc2822) format. + :ivar uri: The URI for this resource, relative to `https://api.twilio.com` + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + ip_access_control_list_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.ip_address: Optional[str] = payload.get("ip_address") + self.cidr_prefix_length: Optional[int] = deserialize.integer( + payload.get("cidr_prefix_length") + ) + self.ip_access_control_list_sid: Optional[str] = payload.get( + "ip_access_control_list_sid" + ) + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "ip_access_control_list_sid": ip_access_control_list_sid, + "sid": sid or self.sid, + } + self._context: Optional[IpAddressContext] = None + + @property + def _proxy(self) -> "IpAddressContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpAddressContext for this IpAddressInstance + """ + if self._context is None: + self._context = IpAddressContext( + self._version, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IpAddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IpAddressInstance": + """ + Fetch the IpAddressInstance + + + :returns: The fetched IpAddressInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpAddressInstance": + """ + Asynchronous coroutine to fetch the IpAddressInstance + + + :returns: The fetched IpAddressInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + ip_address: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> "IpAddressInstance": + """ + Update the IpAddressInstance + + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The updated IpAddressInstance + """ + return self._proxy.update( + ip_address=ip_address, + friendly_name=friendly_name, + cidr_prefix_length=cidr_prefix_length, + ) + + async def update_async( + self, + ip_address: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> "IpAddressInstance": + """ + Asynchronous coroutine to update the IpAddressInstance + + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The updated IpAddressInstance + """ + return await self._proxy.update_async( + ip_address=ip_address, + friendly_name=friendly_name, + cidr_prefix_length=cidr_prefix_length, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAddressContext(InstanceContext): + + def __init__( + self, + version: Version, + account_sid: str, + ip_access_control_list_sid: str, + sid: str, + ): + """ + Initialize the IpAddressContext + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + :param ip_access_control_list_sid: The IpAccessControlList Sid that identifies the IpAddress resources to update. + :param sid: A 34 character string that identifies the IpAddress resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "ip_access_control_list_sid": ip_access_control_list_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/SIP/IpAccessControlLists/{ip_access_control_list_sid}/IpAddresses/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the IpAddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAddressInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IpAddressInstance: + """ + Fetch the IpAddressInstance + + + :returns: The fetched IpAddressInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpAddressInstance: + """ + Asynchronous coroutine to fetch the IpAddressInstance + + + :returns: The fetched IpAddressInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + ip_address: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpAddressInstance: + """ + Update the IpAddressInstance + + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The updated IpAddressInstance + """ + data = values.of( + { + "IpAddress": ip_address, + "FriendlyName": friendly_name, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + ip_address: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpAddressInstance: + """ + Asynchronous coroutine to update the IpAddressInstance + + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The updated IpAddressInstance + """ + data = values.of( + { + "IpAddress": ip_address, + "FriendlyName": friendly_name, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAddressPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IpAddressInstance: + """ + Build an instance of IpAddressInstance + + :param payload: Payload response from the API + """ + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpAddressList(ListResource): + + def __init__( + self, version: Version, account_sid: str, ip_access_control_list_sid: str + ): + """ + Initialize the IpAddressList + + :param version: Version that contains the resource + :param account_sid: The unique id of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this resource. + :param ip_access_control_list_sid: The IpAccessControlList Sid that identifies the IpAddress resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "ip_access_control_list_sid": ip_access_control_list_sid, + } + self._uri = "/Accounts/{account_sid}/SIP/IpAccessControlLists/{ip_access_control_list_sid}/IpAddresses.json".format( + **self._solution + ) + + def create( + self, + friendly_name: str, + ip_address: str, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpAddressInstance: + """ + Create the IpAddressInstance + + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The created IpAddressInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "IpAddress": ip_address, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + ) + + async def create_async( + self, + friendly_name: str, + ip_address: str, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpAddressInstance: + """ + Asynchronously create the IpAddressInstance + + :param friendly_name: A human readable descriptive text for this resource, up to 255 characters long. + :param ip_address: An IP address in dotted decimal notation from which you want to accept traffic. Any SIP requests from this IP address will be allowed by Twilio. IPv4 only supported today. + :param cidr_prefix_length: An integer representing the length of the CIDR prefix to use with this IP address when accepting traffic. By default the entire IP address is used. + + :returns: The created IpAddressInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "IpAddress": ip_address, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAddressInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpAddressInstance]: + """ + Streams IpAddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpAddressInstance]: + """ + Asynchronously streams IpAddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAddressInstance]: + """ + Lists IpAddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAddressInstance]: + """ + Asynchronously lists IpAddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAddressPage: + """ + Retrieve a single page of IpAddressInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAddressInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpAddressPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAddressPage: + """ + Asynchronously retrieve a single page of IpAddressInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAddressInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpAddressPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> IpAddressPage: + """ + Retrieve a specific page of IpAddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAddressInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpAddressPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> IpAddressPage: + """ + Asynchronously retrieve a specific page of IpAddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAddressInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpAddressPage(self._version, response, self._solution) + + def get(self, sid: str) -> IpAddressContext: + """ + Constructs a IpAddressContext + + :param sid: A 34 character string that identifies the IpAddress resource to update. + """ + return IpAddressContext( + self._version, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> IpAddressContext: + """ + Constructs a IpAddressContext + + :param sid: A 34 character string that identifies the IpAddress resource to update. + """ + return IpAddressContext( + self._version, + account_sid=self._solution["account_sid"], + ip_access_control_list_sid=self._solution["ip_access_control_list_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/token.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/token.py new file mode 100644 index 0000000..ec5acac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/token.py @@ -0,0 +1,140 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TokenInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Token resource. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar ice_servers: An array representing the ephemeral credentials and the STUN and TURN server URIs. + :ivar password: The temporary password that the username will use when authenticating with Twilio. + :ivar ttl: The duration in seconds for which the username and password are valid. + :ivar username: The temporary username that uniquely identifies a Token. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.ice_servers: Optional[List[str]] = payload.get("ice_servers") + self.password: Optional[str] = payload.get("password") + self.ttl: Optional[str] = payload.get("ttl") + self.username: Optional[str] = payload.get("username") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TokenList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the TokenList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that will create the resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Tokens.json".format(**self._solution) + + def create(self, ttl: Union[int, object] = values.unset) -> TokenInstance: + """ + Create the TokenInstance + + :param ttl: The duration in seconds for which the generated credentials are valid. The default value is 86400 (24 hours). + + :returns: The created TokenInstance + """ + + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TokenInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, ttl: Union[int, object] = values.unset + ) -> TokenInstance: + """ + Asynchronously create the TokenInstance + + :param ttl: The duration in seconds for which the generated credentials are valid. The default value is 86400 (24 hours). + + :returns: The created TokenInstance + """ + + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TokenInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/transcription.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/transcription.py new file mode 100644 index 0000000..a77bc42 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/transcription.py @@ -0,0 +1,488 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TranscriptionInstance(InstanceResource): + + class Status(object): + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + FAILED = "failed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resource. + :ivar api_version: The API version used to create the transcription. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar duration: The duration of the transcribed audio in seconds. + :ivar price: The charge for the transcript in the currency associated with the account. This value is populated after the transcript is complete so it may not be available immediately. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar recording_sid: The SID of the [Recording](https://www.twilio.com/docs/voice/api/recording) from which the transcription was created. + :ivar sid: The unique string that that we created to identify the Transcription resource. + :ivar status: + :ivar transcription_text: The text content of the transcription. + :ivar type: The transcription type. Can only be: `fast`. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.duration: Optional[str] = payload.get("duration") + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.recording_sid: Optional[str] = payload.get("recording_sid") + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["TranscriptionInstance.Status"] = payload.get("status") + self.transcription_text: Optional[str] = payload.get("transcription_text") + self.type: Optional[str] = payload.get("type") + self.uri: Optional[str] = payload.get("uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[TranscriptionContext] = None + + @property + def _proxy(self) -> "TranscriptionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TranscriptionContext for this TranscriptionInstance + """ + if self._context is None: + self._context = TranscriptionContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TranscriptionInstance": + """ + Fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TranscriptionInstance": + """ + Asynchronous coroutine to fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptionContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the TranscriptionContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Transcriptions/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TranscriptionInstance: + """ + Fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TranscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TranscriptionInstance: + """ + Asynchronous coroutine to fetch the TranscriptionInstance + + + :returns: The fetched TranscriptionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TranscriptionInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TranscriptionInstance: + """ + Build an instance of TranscriptionInstance + + :param payload: Payload response from the API + """ + return TranscriptionInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TranscriptionList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the TranscriptionList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Transcription resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Transcriptions.json".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TranscriptionInstance]: + """ + Streams TranscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TranscriptionInstance]: + """ + Asynchronously streams TranscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptionInstance]: + """ + Lists TranscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptionInstance]: + """ + Asynchronously lists TranscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptionPage: + """ + Retrieve a single page of TranscriptionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TranscriptionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptionPage: + """ + Asynchronously retrieve a single page of TranscriptionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TranscriptionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TranscriptionPage: + """ + Retrieve a specific page of TranscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TranscriptionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TranscriptionPage: + """ + Asynchronously retrieve a specific page of TranscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TranscriptionPage(self._version, response, self._solution) + + def get(self, sid: str) -> TranscriptionContext: + """ + Constructs a TranscriptionContext + + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + return TranscriptionContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> TranscriptionContext: + """ + Constructs a TranscriptionContext + + :param sid: The Twilio-provided string that uniquely identifies the Transcription resource to fetch. + """ + return TranscriptionContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__init__.py new file mode 100644 index 0000000..8fe80f6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__init__.py @@ -0,0 +1,74 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.api.v2010.account.usage.record import RecordList +from twilio.rest.api.v2010.account.usage.trigger import TriggerList + + +class UsageList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the UsageList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage.json".format(**self._solution) + + self._records: Optional[RecordList] = None + self._triggers: Optional[TriggerList] = None + + @property + def records(self) -> RecordList: + """ + Access the records + """ + if self._records is None: + self._records = RecordList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._records + + @property + def triggers(self) -> TriggerList: + """ + Access the triggers + """ + if self._triggers is None: + self._triggers = TriggerList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._triggers + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5a4f7bf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/trigger.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/trigger.cpython-310.pyc new file mode 100644 index 0000000..12bd6e6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/__pycache__/trigger.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__init__.py new file mode 100644 index 0000000..0afc8e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__init__.py @@ -0,0 +1,818 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.api.v2010.account.usage.record.all_time import AllTimeList +from twilio.rest.api.v2010.account.usage.record.daily import DailyList +from twilio.rest.api.v2010.account.usage.record.last_month import LastMonthList +from twilio.rest.api.v2010.account.usage.record.monthly import MonthlyList +from twilio.rest.api.v2010.account.usage.record.this_month import ThisMonthList +from twilio.rest.api.v2010.account.usage.record.today import TodayList +from twilio.rest.api.v2010.account.usage.record.yearly import YearlyList +from twilio.rest.api.v2010.account.usage.record.yesterday import YesterdayList + + +class RecordInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["RecordInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RecordInstance: + """ + Build an instance of RecordInstance + + :param payload: Payload response from the API + """ + return RecordInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RecordList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the RecordList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records.json".format( + **self._solution + ) + + self._all_time: Optional[AllTimeList] = None + self._daily: Optional[DailyList] = None + self._last_month: Optional[LastMonthList] = None + self._monthly: Optional[MonthlyList] = None + self._this_month: Optional[ThisMonthList] = None + self._today: Optional[TodayList] = None + self._yearly: Optional[YearlyList] = None + self._yesterday: Optional[YesterdayList] = None + + def stream( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RecordInstance]: + """ + Streams RecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RecordInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RecordInstance]: + """ + Asynchronously streams RecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RecordInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordInstance]: + """ + Lists RecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RecordInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordInstance]: + """ + Asynchronously lists RecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RecordInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordPage: + """ + Retrieve a single page of RecordInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RecordPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["RecordInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordPage: + """ + Asynchronously retrieve a single page of RecordInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RecordPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RecordPage: + """ + Retrieve a specific page of RecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RecordPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RecordPage: + """ + Asynchronously retrieve a specific page of RecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RecordPage(self._version, response, self._solution) + + @property + def all_time(self) -> AllTimeList: + """ + Access the all_time + """ + if self._all_time is None: + self._all_time = AllTimeList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._all_time + + @property + def daily(self) -> DailyList: + """ + Access the daily + """ + if self._daily is None: + self._daily = DailyList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._daily + + @property + def last_month(self) -> LastMonthList: + """ + Access the last_month + """ + if self._last_month is None: + self._last_month = LastMonthList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._last_month + + @property + def monthly(self) -> MonthlyList: + """ + Access the monthly + """ + if self._monthly is None: + self._monthly = MonthlyList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._monthly + + @property + def this_month(self) -> ThisMonthList: + """ + Access the this_month + """ + if self._this_month is None: + self._this_month = ThisMonthList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._this_month + + @property + def today(self) -> TodayList: + """ + Access the today + """ + if self._today is None: + self._today = TodayList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._today + + @property + def yearly(self) -> YearlyList: + """ + Access the yearly + """ + if self._yearly is None: + self._yearly = YearlyList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._yearly + + @property + def yesterday(self) -> YesterdayList: + """ + Access the yesterday + """ + if self._yesterday is None: + self._yesterday = YesterdayList( + self._version, account_sid=self._solution["account_sid"] + ) + return self._yesterday + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a6ba4dd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/all_time.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/all_time.cpython-310.pyc new file mode 100644 index 0000000..4a82d5a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/all_time.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/daily.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/daily.cpython-310.pyc new file mode 100644 index 0000000..afe1463 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/daily.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/last_month.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/last_month.cpython-310.pyc new file mode 100644 index 0000000..cb8add1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/last_month.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/monthly.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/monthly.cpython-310.pyc new file mode 100644 index 0000000..7f44691 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/monthly.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/this_month.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/this_month.cpython-310.pyc new file mode 100644 index 0000000..2e416c5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/this_month.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/today.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/today.cpython-310.pyc new file mode 100644 index 0000000..8fa9ab8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/today.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yearly.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yearly.cpython-310.pyc new file mode 100644 index 0000000..2ff2a60 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yearly.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yesterday.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yesterday.cpython-310.pyc new file mode 100644 index 0000000..7aa5fa2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/__pycache__/yesterday.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/all_time.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/all_time.py new file mode 100644 index 0000000..7c0b6f5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/all_time.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AllTimeInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["AllTimeInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AllTimePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AllTimeInstance: + """ + Build an instance of AllTimeInstance + + :param payload: Payload response from the API + """ + return AllTimeInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AllTimeList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the AllTimeList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/AllTime.json".format( + **self._solution + ) + + def stream( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AllTimeInstance]: + """ + Streams AllTimeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "AllTimeInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AllTimeInstance]: + """ + Asynchronously streams AllTimeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "AllTimeInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AllTimeInstance]: + """ + Lists AllTimeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "AllTimeInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AllTimeInstance]: + """ + Asynchronously lists AllTimeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "AllTimeInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AllTimePage: + """ + Retrieve a single page of AllTimeInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AllTimeInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AllTimePage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["AllTimeInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AllTimePage: + """ + Asynchronously retrieve a single page of AllTimeInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AllTimeInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AllTimePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AllTimePage: + """ + Retrieve a specific page of AllTimeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AllTimeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AllTimePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AllTimePage: + """ + Asynchronously retrieve a specific page of AllTimeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AllTimeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AllTimePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/daily.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/daily.py new file mode 100644 index 0000000..a0acd62 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/daily.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DailyInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["DailyInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DailyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DailyInstance: + """ + Build an instance of DailyInstance + + :param payload: Payload response from the API + """ + return DailyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DailyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the DailyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/Daily.json".format( + **self._solution + ) + + def stream( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DailyInstance]: + """ + Streams DailyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DailyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DailyInstance]: + """ + Asynchronously streams DailyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DailyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DailyInstance]: + """ + Lists DailyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DailyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DailyInstance]: + """ + Asynchronously lists DailyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DailyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DailyPage: + """ + Retrieve a single page of DailyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DailyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DailyPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["DailyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DailyPage: + """ + Asynchronously retrieve a single page of DailyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DailyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DailyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DailyPage: + """ + Retrieve a specific page of DailyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DailyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DailyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DailyPage: + """ + Asynchronously retrieve a specific page of DailyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DailyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DailyPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/last_month.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/last_month.py new file mode 100644 index 0000000..21fa1e1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/last_month.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class LastMonthInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["LastMonthInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class LastMonthPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> LastMonthInstance: + """ + Build an instance of LastMonthInstance + + :param payload: Payload response from the API + """ + return LastMonthInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class LastMonthList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the LastMonthList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/LastMonth.json".format( + **self._solution + ) + + def stream( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[LastMonthInstance]: + """ + Streams LastMonthInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "LastMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[LastMonthInstance]: + """ + Asynchronously streams LastMonthInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "LastMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LastMonthInstance]: + """ + Lists LastMonthInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "LastMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LastMonthInstance]: + """ + Asynchronously lists LastMonthInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "LastMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LastMonthPage: + """ + Retrieve a single page of LastMonthInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LastMonthInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return LastMonthPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["LastMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LastMonthPage: + """ + Asynchronously retrieve a single page of LastMonthInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LastMonthInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return LastMonthPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> LastMonthPage: + """ + Retrieve a specific page of LastMonthInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LastMonthInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return LastMonthPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> LastMonthPage: + """ + Asynchronously retrieve a specific page of LastMonthInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LastMonthInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return LastMonthPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/monthly.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/monthly.py new file mode 100644 index 0000000..0eff143 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/monthly.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MonthlyInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["MonthlyInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MonthlyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MonthlyInstance: + """ + Build an instance of MonthlyInstance + + :param payload: Payload response from the API + """ + return MonthlyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MonthlyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the MonthlyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/Monthly.json".format( + **self._solution + ) + + def stream( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MonthlyInstance]: + """ + Streams MonthlyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MonthlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MonthlyInstance]: + """ + Asynchronously streams MonthlyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MonthlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MonthlyInstance]: + """ + Lists MonthlyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MonthlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MonthlyInstance]: + """ + Asynchronously lists MonthlyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MonthlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MonthlyPage: + """ + Retrieve a single page of MonthlyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MonthlyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MonthlyPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["MonthlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MonthlyPage: + """ + Asynchronously retrieve a single page of MonthlyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MonthlyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MonthlyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MonthlyPage: + """ + Retrieve a specific page of MonthlyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MonthlyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MonthlyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MonthlyPage: + """ + Asynchronously retrieve a specific page of MonthlyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MonthlyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MonthlyPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/this_month.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/this_month.py new file mode 100644 index 0000000..646b4a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/this_month.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ThisMonthInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["ThisMonthInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ThisMonthPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ThisMonthInstance: + """ + Build an instance of ThisMonthInstance + + :param payload: Payload response from the API + """ + return ThisMonthInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ThisMonthList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ThisMonthList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/ThisMonth.json".format( + **self._solution + ) + + def stream( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ThisMonthInstance]: + """ + Streams ThisMonthInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ThisMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ThisMonthInstance]: + """ + Asynchronously streams ThisMonthInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ThisMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ThisMonthInstance]: + """ + Lists ThisMonthInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ThisMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ThisMonthInstance]: + """ + Asynchronously lists ThisMonthInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ThisMonthInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ThisMonthPage: + """ + Retrieve a single page of ThisMonthInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ThisMonthInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ThisMonthPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["ThisMonthInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ThisMonthPage: + """ + Asynchronously retrieve a single page of ThisMonthInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ThisMonthInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ThisMonthPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ThisMonthPage: + """ + Retrieve a specific page of ThisMonthInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ThisMonthInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ThisMonthPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ThisMonthPage: + """ + Asynchronously retrieve a specific page of ThisMonthInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ThisMonthInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ThisMonthPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/today.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/today.py new file mode 100644 index 0000000..30de9d4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/today.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TodayInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["TodayInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TodayPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TodayInstance: + """ + Build an instance of TodayInstance + + :param payload: Payload response from the API + """ + return TodayInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TodayList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the TodayList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/Today.json".format( + **self._solution + ) + + def stream( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TodayInstance]: + """ + Streams TodayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TodayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TodayInstance]: + """ + Asynchronously streams TodayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TodayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TodayInstance]: + """ + Lists TodayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TodayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TodayInstance]: + """ + Asynchronously lists TodayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TodayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TodayPage: + """ + Retrieve a single page of TodayInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TodayInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TodayPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["TodayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TodayPage: + """ + Asynchronously retrieve a single page of TodayInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TodayInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TodayPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TodayPage: + """ + Retrieve a specific page of TodayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TodayInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TodayPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TodayPage: + """ + Asynchronously retrieve a specific page of TodayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TodayInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TodayPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yearly.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yearly.py new file mode 100644 index 0000000..fbe9dd1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yearly.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class YearlyInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["YearlyInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class YearlyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> YearlyInstance: + """ + Build an instance of YearlyInstance + + :param payload: Payload response from the API + """ + return YearlyInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class YearlyList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the YearlyList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/Yearly.json".format( + **self._solution + ) + + def stream( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[YearlyInstance]: + """ + Streams YearlyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "YearlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[YearlyInstance]: + """ + Asynchronously streams YearlyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "YearlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[YearlyInstance]: + """ + Lists YearlyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "YearlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[YearlyInstance]: + """ + Asynchronously lists YearlyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "YearlyInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> YearlyPage: + """ + Retrieve a single page of YearlyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of YearlyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return YearlyPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["YearlyInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> YearlyPage: + """ + Asynchronously retrieve a single page of YearlyInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of YearlyInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return YearlyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> YearlyPage: + """ + Retrieve a specific page of YearlyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of YearlyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return YearlyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> YearlyPage: + """ + Asynchronously retrieve a specific page of YearlyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of YearlyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return YearlyPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yesterday.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yesterday.py new file mode 100644 index 0000000..db5f9eb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/record/yesterday.py @@ -0,0 +1,713 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class YesterdayInstance(InstanceResource): + + class Category(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that accrued the usage. + :ivar api_version: The API version used to create the resource. + :ivar as_of: Usage records up to date as of this timestamp, formatted as YYYY-MM-DDTHH:MM:SS+00:00. All timestamps are in GMT + :ivar category: + :ivar count: The number of usage events, such as the number of calls. + :ivar count_unit: The units in which `count` is measured, such as `calls` for calls or `messages` for SMS. + :ivar description: A plain-language description of the usage category. + :ivar end_date: The last date for which usage is included in the UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar price: The total price of the usage in the currency specified in `price_unit` and associated with the account. + :ivar price_unit: The currency in which `price` is measured, in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format, such as `usd`, `eur`, and `jpy`. + :ivar start_date: The first date for which usage is included in this UsageRecord. The date is specified in GMT and formatted as `YYYY-MM-DD`. + :ivar subresource_uris: A list of related resources identified by their URIs. For more information, see [List Subresources](https://www.twilio.com/docs/usage/api/usage-record#list-subresources). + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage: The amount used to bill usage and measured in units described in `usage_unit`. + :ivar usage_unit: The units in which `usage` is measured, such as `minutes` for calls or `messages` for SMS. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.as_of: Optional[str] = payload.get("as_of") + self.category: Optional["YesterdayInstance.Category"] = payload.get("category") + self.count: Optional[str] = payload.get("count") + self.count_unit: Optional[str] = payload.get("count_unit") + self.description: Optional[str] = payload.get("description") + self.end_date: Optional[date] = deserialize.iso8601_date( + payload.get("end_date") + ) + self.price: Optional[float] = deserialize.decimal(payload.get("price")) + self.price_unit: Optional[str] = payload.get("price_unit") + self.start_date: Optional[date] = deserialize.iso8601_date( + payload.get("start_date") + ) + self.subresource_uris: Optional[Dict[str, object]] = payload.get( + "subresource_uris" + ) + self.uri: Optional[str] = payload.get("uri") + self.usage: Optional[str] = payload.get("usage") + self.usage_unit: Optional[str] = payload.get("usage_unit") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class YesterdayPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> YesterdayInstance: + """ + Build an instance of YesterdayInstance + + :param payload: Payload response from the API + """ + return YesterdayInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class YesterdayList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the YesterdayList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Records/Yesterday.json".format( + **self._solution + ) + + def stream( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[YesterdayInstance]: + """ + Streams YesterdayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "YesterdayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[YesterdayInstance]: + """ + Asynchronously streams YesterdayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "YesterdayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[YesterdayInstance]: + """ + Lists YesterdayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "YesterdayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[YesterdayInstance]: + """ + Asynchronously lists YesterdayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "YesterdayInstance.Category" category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param bool include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + category=category, + start_date=start_date, + end_date=end_date, + include_subaccounts=include_subaccounts, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> YesterdayPage: + """ + Retrieve a single page of YesterdayInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of YesterdayInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return YesterdayPage(self._version, response, self._solution) + + async def page_async( + self, + category: Union["YesterdayInstance.Category", object] = values.unset, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + include_subaccounts: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> YesterdayPage: + """ + Asynchronously retrieve a single page of YesterdayInstance records from the API. + Request is executed immediately + + :param category: The [usage category](https://www.twilio.com/docs/usage/api/usage-record#usage-categories) of the UsageRecord resources to read. Only UsageRecord resources in the specified category are retrieved. + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `-30days`, which will set the start date to be 30 days before the current date. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. You can also specify offsets from the current date, such as: `+30days`, which will set the end date to 30 days from the current date. + :param include_subaccounts: Whether to include usage from the master account and all its subaccounts. Can be: `true` (the default) to include usage from the master account and all subaccounts or `false` to retrieve usage from only the specified account. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of YesterdayInstance + """ + data = values.of( + { + "Category": category, + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "IncludeSubaccounts": serialize.boolean_to_string(include_subaccounts), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return YesterdayPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> YesterdayPage: + """ + Retrieve a specific page of YesterdayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of YesterdayInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return YesterdayPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> YesterdayPage: + """ + Asynchronously retrieve a specific page of YesterdayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of YesterdayInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return YesterdayPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/trigger.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/trigger.py new file mode 100644 index 0000000..66e8660 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/usage/trigger.py @@ -0,0 +1,1093 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TriggerInstance(InstanceResource): + + class Recurring(object): + DAILY = "daily" + MONTHLY = "monthly" + YEARLY = "yearly" + ALLTIME = "alltime" + + class TriggerField(object): + COUNT = "count" + USAGE = "usage" + PRICE = "price" + + class UsageCategory(object): + A2P_REGISTRATION_FEES = "a2p-registration-fees" + AGENT_CONFERENCE = "agent-conference" + AMAZON_POLLY = "amazon-polly" + ANSWERING_MACHINE_DETECTION = "answering-machine-detection" + AUTHY_AUTHENTICATIONS = "authy-authentications" + AUTHY_CALLS_OUTBOUND = "authy-calls-outbound" + AUTHY_MONTHLY_FEES = "authy-monthly-fees" + AUTHY_PHONE_INTELLIGENCE = "authy-phone-intelligence" + AUTHY_PHONE_VERIFICATIONS = "authy-phone-verifications" + AUTHY_SMS_OUTBOUND = "authy-sms-outbound" + CALL_PROGESS_EVENTS = "call-progess-events" + CALLERIDLOOKUPS = "calleridlookups" + CALLS = "calls" + CALLS_CLIENT = "calls-client" + CALLS_GLOBALCONFERENCE = "calls-globalconference" + CALLS_INBOUND = "calls-inbound" + CALLS_INBOUND_LOCAL = "calls-inbound-local" + CALLS_INBOUND_MOBILE = "calls-inbound-mobile" + CALLS_INBOUND_TOLLFREE = "calls-inbound-tollfree" + CALLS_OUTBOUND = "calls-outbound" + CALLS_PAY_VERB_TRANSACTIONS = "calls-pay-verb-transactions" + CALLS_RECORDINGS = "calls-recordings" + CALLS_SIP = "calls-sip" + CALLS_SIP_INBOUND = "calls-sip-inbound" + CALLS_SIP_OUTBOUND = "calls-sip-outbound" + CALLS_TRANSFERS = "calls-transfers" + CARRIER_LOOKUPS = "carrier-lookups" + CONVERSATIONS = "conversations" + CONVERSATIONS_API_REQUESTS = "conversations-api-requests" + CONVERSATIONS_CONVERSATION_EVENTS = "conversations-conversation-events" + CONVERSATIONS_ENDPOINT_CONNECTIVITY = "conversations-endpoint-connectivity" + CONVERSATIONS_EVENTS = "conversations-events" + CONVERSATIONS_PARTICIPANT_EVENTS = "conversations-participant-events" + CONVERSATIONS_PARTICIPANTS = "conversations-participants" + CPS = "cps" + FLEX_USAGE = "flex-usage" + FRAUD_LOOKUPS = "fraud-lookups" + GROUP_ROOMS = "group-rooms" + GROUP_ROOMS_DATA_TRACK = "group-rooms-data-track" + GROUP_ROOMS_ENCRYPTED_MEDIA_RECORDED = "group-rooms-encrypted-media-recorded" + GROUP_ROOMS_MEDIA_DOWNLOADED = "group-rooms-media-downloaded" + GROUP_ROOMS_MEDIA_RECORDED = "group-rooms-media-recorded" + GROUP_ROOMS_MEDIA_ROUTED = "group-rooms-media-routed" + GROUP_ROOMS_MEDIA_STORED = "group-rooms-media-stored" + GROUP_ROOMS_PARTICIPANT_MINUTES = "group-rooms-participant-minutes" + GROUP_ROOMS_RECORDED_MINUTES = "group-rooms-recorded-minutes" + IMP_V1_USAGE = "imp-v1-usage" + LOOKUPS = "lookups" + MARKETPLACE = "marketplace" + MARKETPLACE_ALGORITHMIA_NAMED_ENTITY_RECOGNITION = ( + "marketplace-algorithmia-named-entity-recognition" + ) + MARKETPLACE_CADENCE_TRANSCRIPTION = "marketplace-cadence-transcription" + MARKETPLACE_CADENCE_TRANSLATION = "marketplace-cadence-translation" + MARKETPLACE_CAPIO_SPEECH_TO_TEXT = "marketplace-capio-speech-to-text" + MARKETPLACE_CONVRIZA_ABABA = "marketplace-convriza-ababa" + MARKETPLACE_DEEPGRAM_PHRASE_DETECTOR = "marketplace-deepgram-phrase-detector" + MARKETPLACE_DIGITAL_SEGMENT_BUSINESS_INFO = ( + "marketplace-digital-segment-business-info" + ) + MARKETPLACE_FACEBOOK_OFFLINE_CONVERSIONS = ( + "marketplace-facebook-offline-conversions" + ) + MARKETPLACE_GOOGLE_SPEECH_TO_TEXT = "marketplace-google-speech-to-text" + MARKETPLACE_IBM_WATSON_MESSAGE_INSIGHTS = ( + "marketplace-ibm-watson-message-insights" + ) + MARKETPLACE_IBM_WATSON_MESSAGE_SENTIMENT = ( + "marketplace-ibm-watson-message-sentiment" + ) + MARKETPLACE_IBM_WATSON_RECORDING_ANALYSIS = ( + "marketplace-ibm-watson-recording-analysis" + ) + MARKETPLACE_IBM_WATSON_TONE_ANALYZER = "marketplace-ibm-watson-tone-analyzer" + MARKETPLACE_ICEHOOK_SYSTEMS_SCOUT = "marketplace-icehook-systems-scout" + MARKETPLACE_INFOGROUP_DATAAXLE_BIZINFO = ( + "marketplace-infogroup-dataaxle-bizinfo" + ) + MARKETPLACE_KEEN_IO_CONTACT_CENTER_ANALYTICS = ( + "marketplace-keen-io-contact-center-analytics" + ) + MARKETPLACE_MARCHEX_CLEANCALL = "marketplace-marchex-cleancall" + MARKETPLACE_MARCHEX_SENTIMENT_ANALYSIS_FOR_SMS = ( + "marketplace-marchex-sentiment-analysis-for-sms" + ) + MARKETPLACE_MARKETPLACE_NEXTCALLER_SOCIAL_ID = ( + "marketplace-marketplace-nextcaller-social-id" + ) + MARKETPLACE_MOBILE_COMMONS_OPT_OUT_CLASSIFIER = ( + "marketplace-mobile-commons-opt-out-classifier" + ) + MARKETPLACE_NEXIWAVE_VOICEMAIL_TO_TEXT = ( + "marketplace-nexiwave-voicemail-to-text" + ) + MARKETPLACE_NEXTCALLER_ADVANCED_CALLER_IDENTIFICATION = ( + "marketplace-nextcaller-advanced-caller-identification" + ) + MARKETPLACE_NOMOROBO_SPAM_SCORE = "marketplace-nomorobo-spam-score" + MARKETPLACE_PAYFONE_TCPA_COMPLIANCE = "marketplace-payfone-tcpa-compliance" + MARKETPLACE_REMEETING_AUTOMATIC_SPEECH_RECOGNITION = ( + "marketplace-remeeting-automatic-speech-recognition" + ) + MARKETPLACE_TCPA_DEFENSE_SOLUTIONS_BLACKLIST_FEED = ( + "marketplace-tcpa-defense-solutions-blacklist-feed" + ) + MARKETPLACE_TELO_OPENCNAM = "marketplace-telo-opencnam" + MARKETPLACE_TRUECNAM_TRUE_SPAM = "marketplace-truecnam-true-spam" + MARKETPLACE_TWILIO_CALLER_NAME_LOOKUP_US = ( + "marketplace-twilio-caller-name-lookup-us" + ) + MARKETPLACE_TWILIO_CARRIER_INFORMATION_LOOKUP = ( + "marketplace-twilio-carrier-information-lookup" + ) + MARKETPLACE_VOICEBASE_PCI = "marketplace-voicebase-pci" + MARKETPLACE_VOICEBASE_TRANSCRIPTION = "marketplace-voicebase-transcription" + MARKETPLACE_VOICEBASE_TRANSCRIPTION_CUSTOM_VOCABULARY = ( + "marketplace-voicebase-transcription-custom-vocabulary" + ) + MARKETPLACE_WHITEPAGES_PRO_CALLER_IDENTIFICATION = ( + "marketplace-whitepages-pro-caller-identification" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_INTELLIGENCE = ( + "marketplace-whitepages-pro-phone-intelligence" + ) + MARKETPLACE_WHITEPAGES_PRO_PHONE_REPUTATION = ( + "marketplace-whitepages-pro-phone-reputation" + ) + MARKETPLACE_WOLFARM_SPOKEN_RESULTS = "marketplace-wolfarm-spoken-results" + MARKETPLACE_WOLFRAM_SHORT_ANSWER = "marketplace-wolfram-short-answer" + MARKETPLACE_YTICA_CONTACT_CENTER_REPORTING_ANALYTICS = ( + "marketplace-ytica-contact-center-reporting-analytics" + ) + MEDIASTORAGE = "mediastorage" + MMS = "mms" + MMS_INBOUND = "mms-inbound" + MMS_INBOUND_LONGCODE = "mms-inbound-longcode" + MMS_INBOUND_SHORTCODE = "mms-inbound-shortcode" + MMS_MESSAGES_CARRIERFEES = "mms-messages-carrierfees" + MMS_OUTBOUND = "mms-outbound" + MMS_OUTBOUND_LONGCODE = "mms-outbound-longcode" + MMS_OUTBOUND_SHORTCODE = "mms-outbound-shortcode" + MONITOR_READS = "monitor-reads" + MONITOR_STORAGE = "monitor-storage" + MONITOR_WRITES = "monitor-writes" + NOTIFY = "notify" + NOTIFY_ACTIONS_ATTEMPTS = "notify-actions-attempts" + NOTIFY_CHANNELS = "notify-channels" + NUMBER_FORMAT_LOOKUPS = "number-format-lookups" + PCHAT = "pchat" + PCHAT_USERS = "pchat-users" + PEER_TO_PEER_ROOMS_PARTICIPANT_MINUTES = ( + "peer-to-peer-rooms-participant-minutes" + ) + PFAX = "pfax" + PFAX_MINUTES = "pfax-minutes" + PFAX_MINUTES_INBOUND = "pfax-minutes-inbound" + PFAX_MINUTES_OUTBOUND = "pfax-minutes-outbound" + PFAX_PAGES = "pfax-pages" + PHONENUMBERS = "phonenumbers" + PHONENUMBERS_CPS = "phonenumbers-cps" + PHONENUMBERS_EMERGENCY = "phonenumbers-emergency" + PHONENUMBERS_LOCAL = "phonenumbers-local" + PHONENUMBERS_MOBILE = "phonenumbers-mobile" + PHONENUMBERS_SETUPS = "phonenumbers-setups" + PHONENUMBERS_TOLLFREE = "phonenumbers-tollfree" + PREMIUMSUPPORT = "premiumsupport" + PROXY = "proxy" + PROXY_ACTIVE_SESSIONS = "proxy-active-sessions" + PSTNCONNECTIVITY = "pstnconnectivity" + PV = "pv" + PV_COMPOSITION_MEDIA_DOWNLOADED = "pv-composition-media-downloaded" + PV_COMPOSITION_MEDIA_ENCRYPTED = "pv-composition-media-encrypted" + PV_COMPOSITION_MEDIA_STORED = "pv-composition-media-stored" + PV_COMPOSITION_MINUTES = "pv-composition-minutes" + PV_RECORDING_COMPOSITIONS = "pv-recording-compositions" + PV_ROOM_PARTICIPANTS = "pv-room-participants" + PV_ROOM_PARTICIPANTS_AU1 = "pv-room-participants-au1" + PV_ROOM_PARTICIPANTS_BR1 = "pv-room-participants-br1" + PV_ROOM_PARTICIPANTS_IE1 = "pv-room-participants-ie1" + PV_ROOM_PARTICIPANTS_JP1 = "pv-room-participants-jp1" + PV_ROOM_PARTICIPANTS_SG1 = "pv-room-participants-sg1" + PV_ROOM_PARTICIPANTS_US1 = "pv-room-participants-us1" + PV_ROOM_PARTICIPANTS_US2 = "pv-room-participants-us2" + PV_ROOMS = "pv-rooms" + PV_SIP_ENDPOINT_REGISTRATIONS = "pv-sip-endpoint-registrations" + RECORDINGS = "recordings" + RECORDINGSTORAGE = "recordingstorage" + ROOMS_GROUP_BANDWIDTH = "rooms-group-bandwidth" + ROOMS_GROUP_MINUTES = "rooms-group-minutes" + ROOMS_PEER_TO_PEER_MINUTES = "rooms-peer-to-peer-minutes" + SHORTCODES = "shortcodes" + SHORTCODES_CUSTOMEROWNED = "shortcodes-customerowned" + SHORTCODES_MMS_ENABLEMENT = "shortcodes-mms-enablement" + SHORTCODES_MPS = "shortcodes-mps" + SHORTCODES_RANDOM = "shortcodes-random" + SHORTCODES_UK = "shortcodes-uk" + SHORTCODES_VANITY = "shortcodes-vanity" + SMALL_GROUP_ROOMS = "small-group-rooms" + SMALL_GROUP_ROOMS_DATA_TRACK = "small-group-rooms-data-track" + SMALL_GROUP_ROOMS_PARTICIPANT_MINUTES = "small-group-rooms-participant-minutes" + SMS = "sms" + SMS_INBOUND = "sms-inbound" + SMS_INBOUND_LONGCODE = "sms-inbound-longcode" + SMS_INBOUND_SHORTCODE = "sms-inbound-shortcode" + SMS_MESSAGES_CARRIERFEES = "sms-messages-carrierfees" + SMS_MESSAGES_FEATURES = "sms-messages-features" + SMS_MESSAGES_FEATURES_SENDERID = "sms-messages-features-senderid" + SMS_OUTBOUND = "sms-outbound" + SMS_OUTBOUND_CONTENT_INSPECTION = "sms-outbound-content-inspection" + SMS_OUTBOUND_LONGCODE = "sms-outbound-longcode" + SMS_OUTBOUND_SHORTCODE = "sms-outbound-shortcode" + SPEECH_RECOGNITION = "speech-recognition" + STUDIO_ENGAGEMENTS = "studio-engagements" + SYNC = "sync" + SYNC_ACTIONS = "sync-actions" + SYNC_ENDPOINT_HOURS = "sync-endpoint-hours" + SYNC_ENDPOINT_HOURS_ABOVE_DAILY_CAP = "sync-endpoint-hours-above-daily-cap" + TASKROUTER_TASKS = "taskrouter-tasks" + TOTALPRICE = "totalprice" + TRANSCRIPTIONS = "transcriptions" + TRUNKING_CPS = "trunking-cps" + TRUNKING_EMERGENCY_CALLS = "trunking-emergency-calls" + TRUNKING_ORIGINATION = "trunking-origination" + TRUNKING_ORIGINATION_LOCAL = "trunking-origination-local" + TRUNKING_ORIGINATION_MOBILE = "trunking-origination-mobile" + TRUNKING_ORIGINATION_TOLLFREE = "trunking-origination-tollfree" + TRUNKING_RECORDINGS = "trunking-recordings" + TRUNKING_SECURE = "trunking-secure" + TRUNKING_TERMINATION = "trunking-termination" + TTS_GOOGLE = "tts-google" + TURNMEGABYTES = "turnmegabytes" + TURNMEGABYTES_AUSTRALIA = "turnmegabytes-australia" + TURNMEGABYTES_BRASIL = "turnmegabytes-brasil" + TURNMEGABYTES_GERMANY = "turnmegabytes-germany" + TURNMEGABYTES_INDIA = "turnmegabytes-india" + TURNMEGABYTES_IRELAND = "turnmegabytes-ireland" + TURNMEGABYTES_JAPAN = "turnmegabytes-japan" + TURNMEGABYTES_SINGAPORE = "turnmegabytes-singapore" + TURNMEGABYTES_USEAST = "turnmegabytes-useast" + TURNMEGABYTES_USWEST = "turnmegabytes-uswest" + TWILIO_INTERCONNECT = "twilio-interconnect" + VERIFY_PUSH = "verify-push" + VERIFY_TOTP = "verify-totp" + VERIFY_WHATSAPP_CONVERSATIONS_BUSINESS_INITIATED = ( + "verify-whatsapp-conversations-business-initiated" + ) + VIDEO_RECORDINGS = "video-recordings" + VIRTUAL_AGENT = "virtual-agent" + VOICE_INSIGHTS = "voice-insights" + VOICE_INSIGHTS_CLIENT_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-client-insights-on-demand-minute" + ) + VOICE_INSIGHTS_PTSN_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-ptsn-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_INTERFACE_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-interface-insights-on-demand-minute" + ) + VOICE_INSIGHTS_SIP_TRUNKING_INSIGHTS_ON_DEMAND_MINUTE = ( + "voice-insights-sip-trunking-insights-on-demand-minute" + ) + VOICE_INTELLIGENCE = "voice-intelligence" + VOICE_INTELLIGENCE_TRANSCRIPTION = "voice-intelligence-transcription" + VOICE_INTELLIGENCE_OPERATORS = "voice-intelligence-operators" + WIRELESS = "wireless" + WIRELESS_ORDERS = "wireless-orders" + WIRELESS_ORDERS_ARTWORK = "wireless-orders-artwork" + WIRELESS_ORDERS_BULK = "wireless-orders-bulk" + WIRELESS_ORDERS_ESIM = "wireless-orders-esim" + WIRELESS_ORDERS_STARTER = "wireless-orders-starter" + WIRELESS_USAGE = "wireless-usage" + WIRELESS_USAGE_COMMANDS = "wireless-usage-commands" + WIRELESS_USAGE_COMMANDS_AFRICA = "wireless-usage-commands-africa" + WIRELESS_USAGE_COMMANDS_ASIA = "wireless-usage-commands-asia" + WIRELESS_USAGE_COMMANDS_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-commands-centralandsouthamerica" + ) + WIRELESS_USAGE_COMMANDS_EUROPE = "wireless-usage-commands-europe" + WIRELESS_USAGE_COMMANDS_HOME = "wireless-usage-commands-home" + WIRELESS_USAGE_COMMANDS_NORTHAMERICA = "wireless-usage-commands-northamerica" + WIRELESS_USAGE_COMMANDS_OCEANIA = "wireless-usage-commands-oceania" + WIRELESS_USAGE_COMMANDS_ROAMING = "wireless-usage-commands-roaming" + WIRELESS_USAGE_DATA = "wireless-usage-data" + WIRELESS_USAGE_DATA_AFRICA = "wireless-usage-data-africa" + WIRELESS_USAGE_DATA_ASIA = "wireless-usage-data-asia" + WIRELESS_USAGE_DATA_CENTRALANDSOUTHAMERICA = ( + "wireless-usage-data-centralandsouthamerica" + ) + WIRELESS_USAGE_DATA_CUSTOM_ADDITIONALMB = ( + "wireless-usage-data-custom-additionalmb" + ) + WIRELESS_USAGE_DATA_CUSTOM_FIRST5MB = "wireless-usage-data-custom-first5mb" + WIRELESS_USAGE_DATA_DOMESTIC_ROAMING = "wireless-usage-data-domestic-roaming" + WIRELESS_USAGE_DATA_EUROPE = "wireless-usage-data-europe" + WIRELESS_USAGE_DATA_INDIVIDUAL_ADDITIONALGB = ( + "wireless-usage-data-individual-additionalgb" + ) + WIRELESS_USAGE_DATA_INDIVIDUAL_FIRSTGB = ( + "wireless-usage-data-individual-firstgb" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_CANADA = ( + "wireless-usage-data-international-roaming-canada" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_INDIA = ( + "wireless-usage-data-international-roaming-india" + ) + WIRELESS_USAGE_DATA_INTERNATIONAL_ROAMING_MEXICO = ( + "wireless-usage-data-international-roaming-mexico" + ) + WIRELESS_USAGE_DATA_NORTHAMERICA = "wireless-usage-data-northamerica" + WIRELESS_USAGE_DATA_OCEANIA = "wireless-usage-data-oceania" + WIRELESS_USAGE_DATA_POOLED = "wireless-usage-data-pooled" + WIRELESS_USAGE_DATA_POOLED_DOWNLINK = "wireless-usage-data-pooled-downlink" + WIRELESS_USAGE_DATA_POOLED_UPLINK = "wireless-usage-data-pooled-uplink" + WIRELESS_USAGE_MRC = "wireless-usage-mrc" + WIRELESS_USAGE_MRC_CUSTOM = "wireless-usage-mrc-custom" + WIRELESS_USAGE_MRC_INDIVIDUAL = "wireless-usage-mrc-individual" + WIRELESS_USAGE_MRC_POOLED = "wireless-usage-mrc-pooled" + WIRELESS_USAGE_MRC_SUSPENDED = "wireless-usage-mrc-suspended" + WIRELESS_USAGE_SMS = "wireless-usage-sms" + WIRELESS_USAGE_VOICE = "wireless-usage-voice" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the trigger monitors. + :ivar api_version: The API version used to create the resource. + :ivar callback_method: The HTTP method we use to call `callback_url`. Can be: `GET` or `POST`. + :ivar callback_url: The URL we call using the `callback_method` when the trigger fires. + :ivar current_value: The current value of the field the trigger is watching. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_fired: The date and time in GMT that the trigger was last fired specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the trigger. + :ivar recurring: + :ivar sid: The unique string that that we created to identify the UsageTrigger resource. + :ivar trigger_by: + :ivar trigger_value: The value at which the trigger will fire. Must be a positive, numeric value. + :ivar uri: The URI of the resource, relative to `https://api.twilio.com`. + :ivar usage_category: + :ivar usage_record_uri: The URI of the [UsageRecord](https://www.twilio.com/docs/usage/api/usage-record) resource this trigger watches, relative to `https://api.twilio.com`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + account_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.api_version: Optional[str] = payload.get("api_version") + self.callback_method: Optional[str] = payload.get("callback_method") + self.callback_url: Optional[str] = payload.get("callback_url") + self.current_value: Optional[str] = payload.get("current_value") + self.date_created: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_created") + ) + self.date_fired: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_fired") + ) + self.date_updated: Optional[datetime] = deserialize.rfc2822_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.recurring: Optional["TriggerInstance.Recurring"] = payload.get("recurring") + self.sid: Optional[str] = payload.get("sid") + self.trigger_by: Optional["TriggerInstance.TriggerField"] = payload.get( + "trigger_by" + ) + self.trigger_value: Optional[str] = payload.get("trigger_value") + self.uri: Optional[str] = payload.get("uri") + self.usage_category: Optional["TriggerInstance.UsageCategory"] = payload.get( + "usage_category" + ) + self.usage_record_uri: Optional[str] = payload.get("usage_record_uri") + + self._solution = { + "account_sid": account_sid, + "sid": sid or self.sid, + } + self._context: Optional[TriggerContext] = None + + @property + def _proxy(self) -> "TriggerContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TriggerContext for this TriggerInstance + """ + if self._context is None: + self._context = TriggerContext( + self._version, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TriggerInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TriggerInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TriggerInstance": + """ + Fetch the TriggerInstance + + + :returns: The fetched TriggerInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TriggerInstance": + """ + Asynchronous coroutine to fetch the TriggerInstance + + + :returns: The fetched TriggerInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "TriggerInstance": + """ + Update the TriggerInstance + + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated TriggerInstance + """ + return self._proxy.update( + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + ) + + async def update_async( + self, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "TriggerInstance": + """ + Asynchronous coroutine to update the TriggerInstance + + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated TriggerInstance + """ + return await self._proxy.update_async( + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TriggerContext(InstanceContext): + + def __init__(self, version: Version, account_sid: str, sid: str): + """ + Initialize the TriggerContext + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageTrigger resources to update. + :param sid: The Twilio-provided string that uniquely identifies the UsageTrigger resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + "sid": sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Triggers/{sid}.json".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TriggerInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TriggerInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TriggerInstance: + """ + Fetch the TriggerInstance + + + :returns: The fetched TriggerInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TriggerInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TriggerInstance: + """ + Asynchronous coroutine to fetch the TriggerInstance + + + :returns: The fetched TriggerInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TriggerInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> TriggerInstance: + """ + Update the TriggerInstance + + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated TriggerInstance + """ + data = values.of( + { + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TriggerInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> TriggerInstance: + """ + Asynchronous coroutine to update the TriggerInstance + + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The updated TriggerInstance + """ + data = values.of( + { + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TriggerInstance( + self._version, + payload, + account_sid=self._solution["account_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TriggerPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TriggerInstance: + """ + Build an instance of TriggerInstance + + :param payload: Payload response from the API + """ + return TriggerInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TriggerList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the TriggerList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageTrigger resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/Usage/Triggers.json".format( + **self._solution + ) + + def create( + self, + callback_url: str, + trigger_value: str, + usage_category: "TriggerInstance.UsageCategory", + callback_method: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + ) -> TriggerInstance: + """ + Create the TriggerInstance + + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param trigger_value: The usage value at which the trigger should fire. For convenience, you can use an offset value such as `+30` to specify a trigger_value that is 30 units more than the current usage value. Be sure to urlencode a `+` as `%2B`. + :param usage_category: + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param recurring: + :param trigger_by: + + :returns: The created TriggerInstance + """ + + data = values.of( + { + "CallbackUrl": callback_url, + "TriggerValue": trigger_value, + "UsageCategory": usage_category, + "CallbackMethod": callback_method, + "FriendlyName": friendly_name, + "Recurring": recurring, + "TriggerBy": trigger_by, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TriggerInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + callback_url: str, + trigger_value: str, + usage_category: "TriggerInstance.UsageCategory", + callback_method: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + ) -> TriggerInstance: + """ + Asynchronously create the TriggerInstance + + :param callback_url: The URL we should call using `callback_method` when the trigger fires. + :param trigger_value: The usage value at which the trigger should fire. For convenience, you can use an offset value such as `+30` to specify a trigger_value that is 30 units more than the current usage value. Be sure to urlencode a `+` as `%2B`. + :param usage_category: + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is `POST`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param recurring: + :param trigger_by: + + :returns: The created TriggerInstance + """ + + data = values.of( + { + "CallbackUrl": callback_url, + "TriggerValue": trigger_value, + "UsageCategory": usage_category, + "CallbackMethod": callback_method, + "FriendlyName": friendly_name, + "Recurring": recurring, + "TriggerBy": trigger_by, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TriggerInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def stream( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TriggerInstance]: + """ + Streams TriggerInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TriggerInstance.Recurring" recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param "TriggerInstance.TriggerField" trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param "TriggerInstance.UsageCategory" usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + recurring=recurring, + trigger_by=trigger_by, + usage_category=usage_category, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TriggerInstance]: + """ + Asynchronously streams TriggerInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TriggerInstance.Recurring" recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param "TriggerInstance.TriggerField" trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param "TriggerInstance.UsageCategory" usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + recurring=recurring, + trigger_by=trigger_by, + usage_category=usage_category, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TriggerInstance]: + """ + Lists TriggerInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TriggerInstance.Recurring" recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param "TriggerInstance.TriggerField" trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param "TriggerInstance.UsageCategory" usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + recurring=recurring, + trigger_by=trigger_by, + usage_category=usage_category, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TriggerInstance]: + """ + Asynchronously lists TriggerInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TriggerInstance.Recurring" recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param "TriggerInstance.TriggerField" trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param "TriggerInstance.UsageCategory" usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + recurring=recurring, + trigger_by=trigger_by, + usage_category=usage_category, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TriggerPage: + """ + Retrieve a single page of TriggerInstance records from the API. + Request is executed immediately + + :param recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TriggerInstance + """ + data = values.of( + { + "Recurring": recurring, + "TriggerBy": trigger_by, + "UsageCategory": usage_category, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TriggerPage(self._version, response, self._solution) + + async def page_async( + self, + recurring: Union["TriggerInstance.Recurring", object] = values.unset, + trigger_by: Union["TriggerInstance.TriggerField", object] = values.unset, + usage_category: Union["TriggerInstance.UsageCategory", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TriggerPage: + """ + Asynchronously retrieve a single page of TriggerInstance records from the API. + Request is executed immediately + + :param recurring: The frequency of recurring UsageTriggers to read. Can be: `daily`, `monthly`, or `yearly` to read recurring UsageTriggers. An empty value or a value of `alltime` reads non-recurring UsageTriggers. + :param trigger_by: The trigger field of the UsageTriggers to read. Can be: `count`, `usage`, or `price` as described in the [UsageRecords documentation](https://www.twilio.com/docs/usage/api/usage-record#usage-count-price). + :param usage_category: The usage category of the UsageTriggers to read. Must be a supported [usage categories](https://www.twilio.com/docs/usage/api/usage-record#usage-categories). + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TriggerInstance + """ + data = values.of( + { + "Recurring": recurring, + "TriggerBy": trigger_by, + "UsageCategory": usage_category, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TriggerPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TriggerPage: + """ + Retrieve a specific page of TriggerInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TriggerInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TriggerPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TriggerPage: + """ + Asynchronously retrieve a specific page of TriggerInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TriggerInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TriggerPage(self._version, response, self._solution) + + def get(self, sid: str) -> TriggerContext: + """ + Constructs a TriggerContext + + :param sid: The Twilio-provided string that uniquely identifies the UsageTrigger resource to update. + """ + return TriggerContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __call__(self, sid: str) -> TriggerContext: + """ + Constructs a TriggerContext + + :param sid: The Twilio-provided string that uniquely identifies the UsageTrigger resource to update. + """ + return TriggerContext( + self._version, account_sid=self._solution["account_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/validation_request.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/validation_request.py new file mode 100644 index 0000000..a949df2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/api/v2010/account/validation_request.py @@ -0,0 +1,167 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Api + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ValidationRequestInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for the Caller ID. + :ivar call_sid: The SID of the [Call](https://www.twilio.com/docs/voice/api/call-resource) the Caller ID is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar phone_number: The phone number to verify in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar validation_code: The 6 digit validation code that someone must enter to validate the Caller ID when `phone_number` is called. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], account_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.phone_number: Optional[str] = payload.get("phone_number") + self.validation_code: Optional[str] = payload.get("validation_code") + + self._solution = { + "account_sid": account_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ValidationRequestList(ListResource): + + def __init__(self, version: Version, account_sid: str): + """ + Initialize the ValidationRequestList + + :param version: Version that contains the resource + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for the new caller ID resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "account_sid": account_sid, + } + self._uri = "/Accounts/{account_sid}/OutgoingCallerIds.json".format( + **self._solution + ) + + def create( + self, + phone_number: str, + friendly_name: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + extension: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> ValidationRequestInstance: + """ + Create the ValidationRequestInstance + + :param phone_number: The phone number to verify in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :param friendly_name: A descriptive string that you create to describe the new caller ID resource. It can be up to 64 characters long. The default value is a formatted version of the phone number. + :param call_delay: The number of seconds to delay before initiating the verification call. Can be an integer between `0` and `60`, inclusive. The default is `0`. + :param extension: The digits to dial after connecting the verification call. + :param status_callback: The URL we should call using the `status_callback_method` to send status information about the verification process to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`, and the default is `POST`. + + :returns: The created ValidationRequestInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "FriendlyName": friendly_name, + "CallDelay": call_delay, + "Extension": extension, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ValidationRequestInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + async def create_async( + self, + phone_number: str, + friendly_name: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + extension: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> ValidationRequestInstance: + """ + Asynchronously create the ValidationRequestInstance + + :param phone_number: The phone number to verify in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :param friendly_name: A descriptive string that you create to describe the new caller ID resource. It can be up to 64 characters long. The default value is a formatted version of the phone number. + :param call_delay: The number of seconds to delay before initiating the verification call. Can be an integer between `0` and `60`, inclusive. The default is `0`. + :param extension: The digits to dial after connecting the verification call. + :param status_callback: The URL we should call using the `status_callback_method` to send status information about the verification process to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `GET` or `POST`, and the default is `POST`. + + :returns: The created ValidationRequestInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "FriendlyName": friendly_name, + "CallDelay": call_delay, + "Extension": extension, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ValidationRequestInstance( + self._version, payload, account_sid=self._solution["account_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__init__.py new file mode 100644 index 0000000..f382325 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.autopilot.AutopilotBase import AutopilotBase +from twilio.rest.autopilot.v1.assistant import AssistantList +from twilio.rest.autopilot.v1.restore_assistant import RestoreAssistantList + + +class Autopilot(AutopilotBase): + @property + def assistants(self) -> AssistantList: + warn( + "assistants is deprecated. Use v1.assistants instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.assistants + + @property + def restore_assistant(self) -> RestoreAssistantList: + warn( + "restore_assistant is deprecated. Use v1.restore_assistant instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.restore_assistant diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c7c8e96 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/autopilot/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/BulkexportsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/BulkexportsBase.py new file mode 100644 index 0000000..cbca4c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/BulkexportsBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.bulkexports.v1 import V1 + + +class BulkexportsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Bulkexports Domain + + :returns: Domain for Bulkexports + """ + super().__init__(twilio, "https://bulkexports.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Bulkexports + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__init__.py new file mode 100644 index 0000000..3a28fb5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.bulkexports.BulkexportsBase import BulkexportsBase +from twilio.rest.bulkexports.v1.export import ExportList +from twilio.rest.bulkexports.v1.export_configuration import ExportConfigurationList + + +class Bulkexports(BulkexportsBase): + @property + def exports(self) -> ExportList: + warn( + "exports is deprecated. Use v1.exports instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.exports + + @property + def export_configuration(self) -> ExportConfigurationList: + warn( + "export_configuration is deprecated. Use v1.export_configuration instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.export_configuration diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/BulkexportsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/BulkexportsBase.cpython-310.pyc new file mode 100644 index 0000000..c406f76 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/BulkexportsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..887483f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__init__.py new file mode 100644 index 0000000..a888fb4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.bulkexports.v1.export import ExportList +from twilio.rest.bulkexports.v1.export_configuration import ExportConfigurationList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Bulkexports + + :param domain: The Twilio.bulkexports domain + """ + super().__init__(domain, "v1") + self._exports: Optional[ExportList] = None + self._export_configuration: Optional[ExportConfigurationList] = None + + @property + def exports(self) -> ExportList: + if self._exports is None: + self._exports = ExportList(self) + return self._exports + + @property + def export_configuration(self) -> ExportConfigurationList: + if self._export_configuration is None: + self._export_configuration = ExportConfigurationList(self) + return self._export_configuration + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..938e1dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/export_configuration.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/export_configuration.cpython-310.pyc new file mode 100644 index 0000000..c588ba5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/__pycache__/export_configuration.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__init__.py new file mode 100644 index 0000000..c5c11df --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__init__.py @@ -0,0 +1,245 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.bulkexports.v1.export.day import DayList +from twilio.rest.bulkexports.v1.export.export_custom_job import ExportCustomJobList +from twilio.rest.bulkexports.v1.export.job import JobList + + +class ExportInstance(InstanceResource): + """ + :ivar resource_type: The type of communication – Messages, Calls, Conferences, and Participants + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Export. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + resource_type: Optional[str] = None, + ): + super().__init__(version) + + self.resource_type: Optional[str] = payload.get("resource_type") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "resource_type": resource_type or self.resource_type, + } + self._context: Optional[ExportContext] = None + + @property + def _proxy(self) -> "ExportContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExportContext for this ExportInstance + """ + if self._context is None: + self._context = ExportContext( + self._version, + resource_type=self._solution["resource_type"], + ) + return self._context + + def fetch(self) -> "ExportInstance": + """ + Fetch the ExportInstance + + + :returns: The fetched ExportInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExportInstance": + """ + Asynchronous coroutine to fetch the ExportInstance + + + :returns: The fetched ExportInstance + """ + return await self._proxy.fetch_async() + + @property + def days(self) -> DayList: + """ + Access the days + """ + return self._proxy.days + + @property + def export_custom_jobs(self) -> ExportCustomJobList: + """ + Access the export_custom_jobs + """ + return self._proxy.export_custom_jobs + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExportContext(InstanceContext): + + def __init__(self, version: Version, resource_type: str): + """ + Initialize the ExportContext + + :param version: Version that contains the resource + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + super().__init__(version) + + # Path Solution + self._solution = { + "resource_type": resource_type, + } + self._uri = "/Exports/{resource_type}".format(**self._solution) + + self._days: Optional[DayList] = None + self._export_custom_jobs: Optional[ExportCustomJobList] = None + + def fetch(self) -> ExportInstance: + """ + Fetch the ExportInstance + + + :returns: The fetched ExportInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExportInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + ) + + async def fetch_async(self) -> ExportInstance: + """ + Asynchronous coroutine to fetch the ExportInstance + + + :returns: The fetched ExportInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExportInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + ) + + @property + def days(self) -> DayList: + """ + Access the days + """ + if self._days is None: + self._days = DayList( + self._version, + self._solution["resource_type"], + ) + return self._days + + @property + def export_custom_jobs(self) -> ExportCustomJobList: + """ + Access the export_custom_jobs + """ + if self._export_custom_jobs is None: + self._export_custom_jobs = ExportCustomJobList( + self._version, + self._solution["resource_type"], + ) + return self._export_custom_jobs + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExportList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ExportList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Exports" + + self._jobs: Optional[JobList] = None + + @property + def jobs(self) -> JobList: + """ + Access the jobs + """ + if self._jobs is None: + self._jobs = JobList(self._version) + return self._jobs + + def get(self, resource_type: str) -> ExportContext: + """ + Constructs a ExportContext + + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + return ExportContext(self._version, resource_type=resource_type) + + def __call__(self, resource_type: str) -> ExportContext: + """ + Constructs a ExportContext + + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + return ExportContext(self._version, resource_type=resource_type) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7519d6e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/day.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/day.cpython-310.pyc new file mode 100644 index 0000000..5100755 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/day.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/export_custom_job.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/export_custom_job.cpython-310.pyc new file mode 100644 index 0000000..7538e58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/export_custom_job.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/job.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/job.cpython-310.pyc new file mode 100644 index 0000000..51157e6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/__pycache__/job.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/day.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/day.py new file mode 100644 index 0000000..fcbc56e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/day.py @@ -0,0 +1,417 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DayInstance(InstanceResource): + """ + :ivar redirect_to: + :ivar day: The ISO 8601 format date of the resources in the file, for a UTC day + :ivar size: The size of the day's data file in bytes + :ivar create_date: The ISO 8601 format date when resources is created + :ivar friendly_name: The friendly name specified when creating the job + :ivar resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + resource_type: str, + day: Optional[str] = None, + ): + super().__init__(version) + + self.redirect_to: Optional[str] = payload.get("redirect_to") + self.day: Optional[str] = payload.get("day") + self.size: Optional[int] = deserialize.integer(payload.get("size")) + self.create_date: Optional[str] = payload.get("create_date") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.resource_type: Optional[str] = payload.get("resource_type") + + self._solution = { + "resource_type": resource_type, + "day": day or self.day, + } + self._context: Optional[DayContext] = None + + @property + def _proxy(self) -> "DayContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DayContext for this DayInstance + """ + if self._context is None: + self._context = DayContext( + self._version, + resource_type=self._solution["resource_type"], + day=self._solution["day"], + ) + return self._context + + def fetch(self) -> "DayInstance": + """ + Fetch the DayInstance + + + :returns: The fetched DayInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DayInstance": + """ + Asynchronous coroutine to fetch the DayInstance + + + :returns: The fetched DayInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DayContext(InstanceContext): + + def __init__(self, version: Version, resource_type: str, day: str): + """ + Initialize the DayContext + + :param version: Version that contains the resource + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + :param day: The ISO 8601 format date of the resources in the file, for a UTC day + """ + super().__init__(version) + + # Path Solution + self._solution = { + "resource_type": resource_type, + "day": day, + } + self._uri = "/Exports/{resource_type}/Days/{day}".format(**self._solution) + + def fetch(self) -> DayInstance: + """ + Fetch the DayInstance + + + :returns: The fetched DayInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DayInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + day=self._solution["day"], + ) + + async def fetch_async(self) -> DayInstance: + """ + Asynchronous coroutine to fetch the DayInstance + + + :returns: The fetched DayInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DayInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + day=self._solution["day"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DayPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DayInstance: + """ + Build an instance of DayInstance + + :param payload: Payload response from the API + """ + return DayInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DayList(ListResource): + + def __init__(self, version: Version, resource_type: str): + """ + Initialize the DayList + + :param version: Version that contains the resource + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "resource_type": resource_type, + } + self._uri = "/Exports/{resource_type}/Days".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DayInstance]: + """ + Streams DayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DayInstance]: + """ + Asynchronously streams DayInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DayInstance]: + """ + Lists DayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DayInstance]: + """ + Asynchronously lists DayInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DayPage: + """ + Retrieve a single page of DayInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DayInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DayPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DayPage: + """ + Asynchronously retrieve a single page of DayInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DayInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DayPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DayPage: + """ + Retrieve a specific page of DayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DayInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DayPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DayPage: + """ + Asynchronously retrieve a specific page of DayInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DayInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DayPage(self._version, response, self._solution) + + def get(self, day: str) -> DayContext: + """ + Constructs a DayContext + + :param day: The ISO 8601 format date of the resources in the file, for a UTC day + """ + return DayContext( + self._version, resource_type=self._solution["resource_type"], day=day + ) + + def __call__(self, day: str) -> DayContext: + """ + Constructs a DayContext + + :param day: The ISO 8601 format date of the resources in the file, for a UTC day + """ + return DayContext( + self._version, resource_type=self._solution["resource_type"], day=day + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/export_custom_job.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/export_custom_job.py new file mode 100644 index 0000000..758398e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/export_custom_job.py @@ -0,0 +1,384 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ExportCustomJobInstance(InstanceResource): + """ + :ivar friendly_name: The friendly name specified when creating the job + :ivar resource_type: The type of communication – Messages, Calls, Conferences, and Participants + :ivar start_day: The start day for the custom export specified when creating the job + :ivar end_day: The end day for the export specified when creating the job + :ivar webhook_url: The optional webhook url called on completion of the job. If this is supplied, `WebhookMethod` must also be supplied. + :ivar webhook_method: This is the method used to call the webhook on completion of the job. If this is supplied, `WebhookUrl` must also be supplied. + :ivar email: The optional email to send the completion notification to + :ivar job_sid: The unique job_sid returned when the custom export was created + :ivar details: The details of a job which is an object that contains an array of status grouped by `status` state. Each `status` object has a `status` string, a count which is the number of days in that `status`, and list of days in that `status`. The day strings are in the format yyyy-MM-dd. As an example, a currently running job may have a status object for COMPLETED and a `status` object for SUBMITTED each with its own count and list of days. + :ivar job_queue_position: This is the job position from the 1st in line. Your queue position will never increase. As jobs ahead of yours in the queue are processed, the queue position number will decrease + :ivar estimated_completion_time: this is the time estimated until your job is complete. This is calculated each time you request the job list. The time is calculated based on the current rate of job completion (which may vary) and your job queue position + """ + + def __init__(self, version: Version, payload: Dict[str, Any], resource_type: str): + super().__init__(version) + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.resource_type: Optional[str] = payload.get("resource_type") + self.start_day: Optional[str] = payload.get("start_day") + self.end_day: Optional[str] = payload.get("end_day") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.email: Optional[str] = payload.get("email") + self.job_sid: Optional[str] = payload.get("job_sid") + self.details: Optional[Dict[str, object]] = payload.get("details") + self.job_queue_position: Optional[str] = payload.get("job_queue_position") + self.estimated_completion_time: Optional[str] = payload.get( + "estimated_completion_time" + ) + + self._solution = { + "resource_type": resource_type, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExportCustomJobPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ExportCustomJobInstance: + """ + Build an instance of ExportCustomJobInstance + + :param payload: Payload response from the API + """ + return ExportCustomJobInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ExportCustomJobList(ListResource): + + def __init__(self, version: Version, resource_type: str): + """ + Initialize the ExportCustomJobList + + :param version: Version that contains the resource + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "resource_type": resource_type, + } + self._uri = "/Exports/{resource_type}/Jobs".format(**self._solution) + + def create( + self, + start_day: str, + end_day: str, + friendly_name: str, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> ExportCustomJobInstance: + """ + Create the ExportCustomJobInstance + + :param start_day: The start day for the custom export specified as a string in the format of yyyy-mm-dd + :param end_day: The end day for the custom export specified as a string in the format of yyyy-mm-dd. End day is inclusive and must be 2 days earlier than the current UTC day. + :param friendly_name: The friendly name specified when creating the job + :param webhook_url: The optional webhook url called on completion of the job. If this is supplied, `WebhookMethod` must also be supplied. If you set neither webhook nor email, you will have to check your job's status manually. + :param webhook_method: This is the method used to call the webhook on completion of the job. If this is supplied, `WebhookUrl` must also be supplied. + :param email: The optional email to send the completion notification to. You can set both webhook, and email, or one or the other. If you set neither, the job will run but you will have to query to determine your job's status. + + :returns: The created ExportCustomJobInstance + """ + + data = values.of( + { + "StartDay": start_day, + "EndDay": end_day, + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "WebhookMethod": webhook_method, + "Email": email, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ExportCustomJobInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + async def create_async( + self, + start_day: str, + end_day: str, + friendly_name: str, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> ExportCustomJobInstance: + """ + Asynchronously create the ExportCustomJobInstance + + :param start_day: The start day for the custom export specified as a string in the format of yyyy-mm-dd + :param end_day: The end day for the custom export specified as a string in the format of yyyy-mm-dd. End day is inclusive and must be 2 days earlier than the current UTC day. + :param friendly_name: The friendly name specified when creating the job + :param webhook_url: The optional webhook url called on completion of the job. If this is supplied, `WebhookMethod` must also be supplied. If you set neither webhook nor email, you will have to check your job's status manually. + :param webhook_method: This is the method used to call the webhook on completion of the job. If this is supplied, `WebhookUrl` must also be supplied. + :param email: The optional email to send the completion notification to. You can set both webhook, and email, or one or the other. If you set neither, the job will run but you will have to query to determine your job's status. + + :returns: The created ExportCustomJobInstance + """ + + data = values.of( + { + "StartDay": start_day, + "EndDay": end_day, + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "WebhookMethod": webhook_method, + "Email": email, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExportCustomJobInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ExportCustomJobInstance]: + """ + Streams ExportCustomJobInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ExportCustomJobInstance]: + """ + Asynchronously streams ExportCustomJobInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExportCustomJobInstance]: + """ + Lists ExportCustomJobInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExportCustomJobInstance]: + """ + Asynchronously lists ExportCustomJobInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExportCustomJobPage: + """ + Retrieve a single page of ExportCustomJobInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExportCustomJobInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ExportCustomJobPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExportCustomJobPage: + """ + Asynchronously retrieve a single page of ExportCustomJobInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExportCustomJobInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ExportCustomJobPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ExportCustomJobPage: + """ + Retrieve a specific page of ExportCustomJobInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExportCustomJobInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ExportCustomJobPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ExportCustomJobPage: + """ + Asynchronously retrieve a specific page of ExportCustomJobInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExportCustomJobInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ExportCustomJobPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/job.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/job.py new file mode 100644 index 0000000..48b2df1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export/job.py @@ -0,0 +1,246 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class JobInstance(InstanceResource): + """ + :ivar resource_type: The type of communication – Messages, Calls, Conferences, and Participants + :ivar friendly_name: The friendly name specified when creating the job + :ivar details: The details of a job which is an object that contains an array of status grouped by `status` state. Each `status` object has a `status` string, a count which is the number of days in that `status`, and list of days in that `status`. The day strings are in the format yyyy-MM-dd. As an example, a currently running job may have a status object for COMPLETED and a `status` object for SUBMITTED each with its own count and list of days. + :ivar start_day: The start time for the export specified when creating the job + :ivar end_day: The end time for the export specified when creating the job + :ivar job_sid: The job_sid returned when the export was created + :ivar webhook_url: The optional webhook url called on completion + :ivar webhook_method: This is the method used to call the webhook + :ivar email: The optional email to send the completion notification to + :ivar url: + :ivar job_queue_position: This is the job position from the 1st in line. Your queue position will never increase. As jobs ahead of yours in the queue are processed, the queue position number will decrease + :ivar estimated_completion_time: this is the time estimated until your job is complete. This is calculated each time you request the job list. The time is calculated based on the current rate of job completion (which may vary) and your job queue position + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], job_sid: Optional[str] = None + ): + super().__init__(version) + + self.resource_type: Optional[str] = payload.get("resource_type") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.details: Optional[Dict[str, object]] = payload.get("details") + self.start_day: Optional[str] = payload.get("start_day") + self.end_day: Optional[str] = payload.get("end_day") + self.job_sid: Optional[str] = payload.get("job_sid") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.email: Optional[str] = payload.get("email") + self.url: Optional[str] = payload.get("url") + self.job_queue_position: Optional[str] = payload.get("job_queue_position") + self.estimated_completion_time: Optional[str] = payload.get( + "estimated_completion_time" + ) + + self._solution = { + "job_sid": job_sid or self.job_sid, + } + self._context: Optional[JobContext] = None + + @property + def _proxy(self) -> "JobContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: JobContext for this JobInstance + """ + if self._context is None: + self._context = JobContext( + self._version, + job_sid=self._solution["job_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the JobInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the JobInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "JobInstance": + """ + Fetch the JobInstance + + + :returns: The fetched JobInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "JobInstance": + """ + Asynchronous coroutine to fetch the JobInstance + + + :returns: The fetched JobInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class JobContext(InstanceContext): + + def __init__(self, version: Version, job_sid: str): + """ + Initialize the JobContext + + :param version: Version that contains the resource + :param job_sid: The unique string that that we created to identify the Bulk Export job + """ + super().__init__(version) + + # Path Solution + self._solution = { + "job_sid": job_sid, + } + self._uri = "/Exports/Jobs/{job_sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the JobInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the JobInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> JobInstance: + """ + Fetch the JobInstance + + + :returns: The fetched JobInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return JobInstance( + self._version, + payload, + job_sid=self._solution["job_sid"], + ) + + async def fetch_async(self) -> JobInstance: + """ + Asynchronous coroutine to fetch the JobInstance + + + :returns: The fetched JobInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return JobInstance( + self._version, + payload, + job_sid=self._solution["job_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class JobList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the JobList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, job_sid: str) -> JobContext: + """ + Constructs a JobContext + + :param job_sid: The unique string that that we created to identify the Bulk Export job + """ + return JobContext(self._version, job_sid=job_sid) + + def __call__(self, job_sid: str) -> JobContext: + """ + Constructs a JobContext + + :param job_sid: The unique string that that we created to identify the Bulk Export job + """ + return JobContext(self._version, job_sid=job_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export_configuration.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export_configuration.py new file mode 100644 index 0000000..c6629d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/bulkexports/v1/export_configuration.py @@ -0,0 +1,300 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Bulkexports + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExportConfigurationInstance(InstanceResource): + """ + :ivar enabled: If true, Twilio will automatically generate every day's file when the day is over. + :ivar webhook_url: Stores the URL destination for the method specified in webhook_method. + :ivar webhook_method: Sets whether Twilio should call a webhook URL when the automatic generation is complete, using GET or POST. The actual destination is set in the webhook_url + :ivar resource_type: The type of communication – Messages, Calls, Conferences, and Participants + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + resource_type: Optional[str] = None, + ): + super().__init__(version) + + self.enabled: Optional[bool] = payload.get("enabled") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.resource_type: Optional[str] = payload.get("resource_type") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "resource_type": resource_type or self.resource_type, + } + self._context: Optional[ExportConfigurationContext] = None + + @property + def _proxy(self) -> "ExportConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExportConfigurationContext for this ExportConfigurationInstance + """ + if self._context is None: + self._context = ExportConfigurationContext( + self._version, + resource_type=self._solution["resource_type"], + ) + return self._context + + def fetch(self) -> "ExportConfigurationInstance": + """ + Fetch the ExportConfigurationInstance + + + :returns: The fetched ExportConfigurationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExportConfigurationInstance": + """ + Asynchronous coroutine to fetch the ExportConfigurationInstance + + + :returns: The fetched ExportConfigurationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + enabled: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + ) -> "ExportConfigurationInstance": + """ + Update the ExportConfigurationInstance + + :param enabled: If true, Twilio will automatically generate every day's file when the day is over. + :param webhook_url: Stores the URL destination for the method specified in webhook_method. + :param webhook_method: Sets whether Twilio should call a webhook URL when the automatic generation is complete, using GET or POST. The actual destination is set in the webhook_url + + :returns: The updated ExportConfigurationInstance + """ + return self._proxy.update( + enabled=enabled, + webhook_url=webhook_url, + webhook_method=webhook_method, + ) + + async def update_async( + self, + enabled: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + ) -> "ExportConfigurationInstance": + """ + Asynchronous coroutine to update the ExportConfigurationInstance + + :param enabled: If true, Twilio will automatically generate every day's file when the day is over. + :param webhook_url: Stores the URL destination for the method specified in webhook_method. + :param webhook_method: Sets whether Twilio should call a webhook URL when the automatic generation is complete, using GET or POST. The actual destination is set in the webhook_url + + :returns: The updated ExportConfigurationInstance + """ + return await self._proxy.update_async( + enabled=enabled, + webhook_url=webhook_url, + webhook_method=webhook_method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExportConfigurationContext(InstanceContext): + + def __init__(self, version: Version, resource_type: str): + """ + Initialize the ExportConfigurationContext + + :param version: Version that contains the resource + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + super().__init__(version) + + # Path Solution + self._solution = { + "resource_type": resource_type, + } + self._uri = "/Exports/{resource_type}/Configuration".format(**self._solution) + + def fetch(self) -> ExportConfigurationInstance: + """ + Fetch the ExportConfigurationInstance + + + :returns: The fetched ExportConfigurationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExportConfigurationInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + ) + + async def fetch_async(self) -> ExportConfigurationInstance: + """ + Asynchronous coroutine to fetch the ExportConfigurationInstance + + + :returns: The fetched ExportConfigurationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExportConfigurationInstance( + self._version, + payload, + resource_type=self._solution["resource_type"], + ) + + def update( + self, + enabled: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + ) -> ExportConfigurationInstance: + """ + Update the ExportConfigurationInstance + + :param enabled: If true, Twilio will automatically generate every day's file when the day is over. + :param webhook_url: Stores the URL destination for the method specified in webhook_method. + :param webhook_method: Sets whether Twilio should call a webhook URL when the automatic generation is complete, using GET or POST. The actual destination is set in the webhook_url + + :returns: The updated ExportConfigurationInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + "WebhookUrl": webhook_url, + "WebhookMethod": webhook_method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ExportConfigurationInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + async def update_async( + self, + enabled: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + ) -> ExportConfigurationInstance: + """ + Asynchronous coroutine to update the ExportConfigurationInstance + + :param enabled: If true, Twilio will automatically generate every day's file when the day is over. + :param webhook_url: Stores the URL destination for the method specified in webhook_method. + :param webhook_method: Sets whether Twilio should call a webhook URL when the automatic generation is complete, using GET or POST. The actual destination is set in the webhook_url + + :returns: The updated ExportConfigurationInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + "WebhookUrl": webhook_url, + "WebhookMethod": webhook_method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExportConfigurationInstance( + self._version, payload, resource_type=self._solution["resource_type"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExportConfigurationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ExportConfigurationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, resource_type: str) -> ExportConfigurationContext: + """ + Constructs a ExportConfigurationContext + + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + return ExportConfigurationContext(self._version, resource_type=resource_type) + + def __call__(self, resource_type: str) -> ExportConfigurationContext: + """ + Constructs a ExportConfigurationContext + + :param resource_type: The type of communication – Messages, Calls, Conferences, and Participants + """ + return ExportConfigurationContext(self._version, resource_type=resource_type) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/ChatBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/ChatBase.py new file mode 100644 index 0000000..6d8656d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/ChatBase.py @@ -0,0 +1,66 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.chat.v1 import V1 +from twilio.rest.chat.v2 import V2 +from twilio.rest.chat.v3 import V3 + + +class ChatBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Chat Domain + + :returns: Domain for Chat + """ + super().__init__(twilio, "https://chat.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + self._v3: Optional[V3] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Chat + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Chat + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + @property + def v3(self) -> V3: + """ + :returns: Versions v3 of Chat + """ + if self._v3 is None: + self._v3 = V3(self) + return self._v3 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__init__.py new file mode 100644 index 0000000..9608a0f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__init__.py @@ -0,0 +1,35 @@ +from warnings import warn + +from twilio.rest.chat.ChatBase import ChatBase +from twilio.rest.chat.v2.credential import CredentialList +from twilio.rest.chat.v2.service import ServiceList +from twilio.rest.chat.v3.channel import ChannelList + + +class Chat(ChatBase): + @property + def credentials(self) -> CredentialList: + warn( + "credentials is deprecated. Use v2.credentials instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.credentials + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v2.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.services + + @property + def channels(self) -> ChannelList: + warn( + "channels is deprecated. Use v3.channels instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v3.channels diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/ChatBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/ChatBase.cpython-310.pyc new file mode 100644 index 0000000..7d231c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/ChatBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6178ab8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__init__.py new file mode 100644 index 0000000..d0ade9e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.chat.v1.credential import CredentialList +from twilio.rest.chat.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Chat + + :param domain: The Twilio.chat domain + """ + super().__init__(domain, "v1") + self._credentials: Optional[CredentialList] = None + self._services: Optional[ServiceList] = None + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c083918 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..a453084 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/credential.py new file mode 100644 index 0000000..68ec9f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/credential.py @@ -0,0 +1,681 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushService(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: The unique string that we created to identify the Credential resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Credential resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the Credential resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushService"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR. -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__init__.py new file mode 100644 index 0000000..1e2a588 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__init__.py @@ -0,0 +1,1329 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v1.service.channel import ChannelList +from twilio.rest.chat.v1.service.role import RoleList +from twilio.rest.chat.v1.service.user import UserList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar default_service_role_sid: The service role assigned to users when they are added to the service. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :ivar default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :ivar default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :ivar read_status_enabled: Whether the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature is enabled. The default is `true`. + :ivar reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) is enabled for this Service instance. The default is `false`. + :ivar typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :ivar consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :ivar limits: An object that describes the limits of the service instance. The `limits` object contains `channel_members` to describe the members/channel limit and `user_channels` to describe the channels/user limit. `channel_members` can be 1,000 or less, with a default of 250. `user_channels` can be 1,000 or less, with a default value of 100. + :ivar webhooks: An object that contains information about the webhooks configured for this service. + :ivar pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :ivar post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :ivar webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar webhook_filters: The list of WebHook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar notifications: The notification configuration for the Service instance. See [Push Notification Configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more information. + :ivar url: The absolute URL of the Service resource. + :ivar links: The absolute URLs of the Service's [Channels](https://www.twilio.com/docs/chat/api/channels), [Roles](https://www.twilio.com/docs/chat/api/roles), and [Users](https://www.twilio.com/docs/chat/api/users). + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.default_service_role_sid: Optional[str] = payload.get( + "default_service_role_sid" + ) + self.default_channel_role_sid: Optional[str] = payload.get( + "default_channel_role_sid" + ) + self.default_channel_creator_role_sid: Optional[str] = payload.get( + "default_channel_creator_role_sid" + ) + self.read_status_enabled: Optional[bool] = payload.get("read_status_enabled") + self.reachability_enabled: Optional[bool] = payload.get("reachability_enabled") + self.typing_indicator_timeout: Optional[int] = deserialize.integer( + payload.get("typing_indicator_timeout") + ) + self.consumption_report_interval: Optional[int] = deserialize.integer( + payload.get("consumption_report_interval") + ) + self.limits: Optional[Dict[str, object]] = payload.get("limits") + self.webhooks: Optional[Dict[str, object]] = payload.get("webhooks") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.webhook_filters: Optional[List[str]] = payload.get("webhook_filters") + self.notifications: Optional[Dict[str, object]] = payload.get("notifications") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of WebHook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhooks_on_message_send_url: The URL of the webhook to call in response to the `on_message_send` event using the `webhooks.on_message_send.method` HTTP method. + :param webhooks_on_message_send_method: The HTTP method to use when calling the `webhooks.on_message_send.url`. + :param webhooks_on_message_update_url: The URL of the webhook to call in response to the `on_message_update` event using the `webhooks.on_message_update.method` HTTP method. + :param webhooks_on_message_update_method: The HTTP method to use when calling the `webhooks.on_message_update.url`. + :param webhooks_on_message_remove_url: The URL of the webhook to call in response to the `on_message_remove` event using the `webhooks.on_message_remove.method` HTTP method. + :param webhooks_on_message_remove_method: The HTTP method to use when calling the `webhooks.on_message_remove.url`. + :param webhooks_on_channel_add_url: The URL of the webhook to call in response to the `on_channel_add` event using the `webhooks.on_channel_add.method` HTTP method. + :param webhooks_on_channel_add_method: The HTTP method to use when calling the `webhooks.on_channel_add.url`. + :param webhooks_on_channel_destroy_url: The URL of the webhook to call in response to the `on_channel_destroy` event using the `webhooks.on_channel_destroy.method` HTTP method. + :param webhooks_on_channel_destroy_method: The HTTP method to use when calling the `webhooks.on_channel_destroy.url`. + :param webhooks_on_channel_update_url: The URL of the webhook to call in response to the `on_channel_update` event using the `webhooks.on_channel_update.method` HTTP method. + :param webhooks_on_channel_update_method: The HTTP method to use when calling the `webhooks.on_channel_update.url`. + :param webhooks_on_member_add_url: The URL of the webhook to call in response to the `on_member_add` event using the `webhooks.on_member_add.method` HTTP method. + :param webhooks_on_member_add_method: The HTTP method to use when calling the `webhooks.on_member_add.url`. + :param webhooks_on_member_remove_url: The URL of the webhook to call in response to the `on_member_remove` event using the `webhooks.on_member_remove.method` HTTP method. + :param webhooks_on_member_remove_method: The HTTP method to use when calling the `webhooks.on_member_remove.url`. + :param webhooks_on_message_sent_url: The URL of the webhook to call in response to the `on_message_sent` event using the `webhooks.on_message_sent.method` HTTP method. + :param webhooks_on_message_sent_method: The URL of the webhook to call in response to the `on_message_sent` event`. + :param webhooks_on_message_updated_url: The URL of the webhook to call in response to the `on_message_updated` event using the `webhooks.on_message_updated.method` HTTP method. + :param webhooks_on_message_updated_method: The HTTP method to use when calling the `webhooks.on_message_updated.url`. + :param webhooks_on_message_removed_url: The URL of the webhook to call in response to the `on_message_removed` event using the `webhooks.on_message_removed.method` HTTP method. + :param webhooks_on_message_removed_method: The HTTP method to use when calling the `webhooks.on_message_removed.url`. + :param webhooks_on_channel_added_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_added.method` HTTP method. + :param webhooks_on_channel_added_method: The URL of the webhook to call in response to the `on_channel_added` event`. + :param webhooks_on_channel_destroyed_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_destroyed.method` HTTP method. + :param webhooks_on_channel_destroyed_method: The HTTP method to use when calling the `webhooks.on_channel_destroyed.url`. + :param webhooks_on_channel_updated_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_channel_updated_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_added_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_member_added_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_removed_url: The URL of the webhook to call in response to the `on_member_removed` event using the `webhooks.on_member_removed.method` HTTP method. + :param webhooks_on_member_removed_method: The HTTP method to use when calling the `webhooks.on_member_removed.url`. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + webhooks_on_message_send_url=webhooks_on_message_send_url, + webhooks_on_message_send_method=webhooks_on_message_send_method, + webhooks_on_message_update_url=webhooks_on_message_update_url, + webhooks_on_message_update_method=webhooks_on_message_update_method, + webhooks_on_message_remove_url=webhooks_on_message_remove_url, + webhooks_on_message_remove_method=webhooks_on_message_remove_method, + webhooks_on_channel_add_url=webhooks_on_channel_add_url, + webhooks_on_channel_add_method=webhooks_on_channel_add_method, + webhooks_on_channel_destroy_url=webhooks_on_channel_destroy_url, + webhooks_on_channel_destroy_method=webhooks_on_channel_destroy_method, + webhooks_on_channel_update_url=webhooks_on_channel_update_url, + webhooks_on_channel_update_method=webhooks_on_channel_update_method, + webhooks_on_member_add_url=webhooks_on_member_add_url, + webhooks_on_member_add_method=webhooks_on_member_add_method, + webhooks_on_member_remove_url=webhooks_on_member_remove_url, + webhooks_on_member_remove_method=webhooks_on_member_remove_method, + webhooks_on_message_sent_url=webhooks_on_message_sent_url, + webhooks_on_message_sent_method=webhooks_on_message_sent_method, + webhooks_on_message_updated_url=webhooks_on_message_updated_url, + webhooks_on_message_updated_method=webhooks_on_message_updated_method, + webhooks_on_message_removed_url=webhooks_on_message_removed_url, + webhooks_on_message_removed_method=webhooks_on_message_removed_method, + webhooks_on_channel_added_url=webhooks_on_channel_added_url, + webhooks_on_channel_added_method=webhooks_on_channel_added_method, + webhooks_on_channel_destroyed_url=webhooks_on_channel_destroyed_url, + webhooks_on_channel_destroyed_method=webhooks_on_channel_destroyed_method, + webhooks_on_channel_updated_url=webhooks_on_channel_updated_url, + webhooks_on_channel_updated_method=webhooks_on_channel_updated_method, + webhooks_on_member_added_url=webhooks_on_member_added_url, + webhooks_on_member_added_method=webhooks_on_member_added_method, + webhooks_on_member_removed_url=webhooks_on_member_removed_url, + webhooks_on_member_removed_method=webhooks_on_member_removed_method, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of WebHook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhooks_on_message_send_url: The URL of the webhook to call in response to the `on_message_send` event using the `webhooks.on_message_send.method` HTTP method. + :param webhooks_on_message_send_method: The HTTP method to use when calling the `webhooks.on_message_send.url`. + :param webhooks_on_message_update_url: The URL of the webhook to call in response to the `on_message_update` event using the `webhooks.on_message_update.method` HTTP method. + :param webhooks_on_message_update_method: The HTTP method to use when calling the `webhooks.on_message_update.url`. + :param webhooks_on_message_remove_url: The URL of the webhook to call in response to the `on_message_remove` event using the `webhooks.on_message_remove.method` HTTP method. + :param webhooks_on_message_remove_method: The HTTP method to use when calling the `webhooks.on_message_remove.url`. + :param webhooks_on_channel_add_url: The URL of the webhook to call in response to the `on_channel_add` event using the `webhooks.on_channel_add.method` HTTP method. + :param webhooks_on_channel_add_method: The HTTP method to use when calling the `webhooks.on_channel_add.url`. + :param webhooks_on_channel_destroy_url: The URL of the webhook to call in response to the `on_channel_destroy` event using the `webhooks.on_channel_destroy.method` HTTP method. + :param webhooks_on_channel_destroy_method: The HTTP method to use when calling the `webhooks.on_channel_destroy.url`. + :param webhooks_on_channel_update_url: The URL of the webhook to call in response to the `on_channel_update` event using the `webhooks.on_channel_update.method` HTTP method. + :param webhooks_on_channel_update_method: The HTTP method to use when calling the `webhooks.on_channel_update.url`. + :param webhooks_on_member_add_url: The URL of the webhook to call in response to the `on_member_add` event using the `webhooks.on_member_add.method` HTTP method. + :param webhooks_on_member_add_method: The HTTP method to use when calling the `webhooks.on_member_add.url`. + :param webhooks_on_member_remove_url: The URL of the webhook to call in response to the `on_member_remove` event using the `webhooks.on_member_remove.method` HTTP method. + :param webhooks_on_member_remove_method: The HTTP method to use when calling the `webhooks.on_member_remove.url`. + :param webhooks_on_message_sent_url: The URL of the webhook to call in response to the `on_message_sent` event using the `webhooks.on_message_sent.method` HTTP method. + :param webhooks_on_message_sent_method: The URL of the webhook to call in response to the `on_message_sent` event`. + :param webhooks_on_message_updated_url: The URL of the webhook to call in response to the `on_message_updated` event using the `webhooks.on_message_updated.method` HTTP method. + :param webhooks_on_message_updated_method: The HTTP method to use when calling the `webhooks.on_message_updated.url`. + :param webhooks_on_message_removed_url: The URL of the webhook to call in response to the `on_message_removed` event using the `webhooks.on_message_removed.method` HTTP method. + :param webhooks_on_message_removed_method: The HTTP method to use when calling the `webhooks.on_message_removed.url`. + :param webhooks_on_channel_added_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_added.method` HTTP method. + :param webhooks_on_channel_added_method: The URL of the webhook to call in response to the `on_channel_added` event`. + :param webhooks_on_channel_destroyed_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_destroyed.method` HTTP method. + :param webhooks_on_channel_destroyed_method: The HTTP method to use when calling the `webhooks.on_channel_destroyed.url`. + :param webhooks_on_channel_updated_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_channel_updated_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_added_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_member_added_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_removed_url: The URL of the webhook to call in response to the `on_member_removed` event using the `webhooks.on_member_removed.method` HTTP method. + :param webhooks_on_member_removed_method: The HTTP method to use when calling the `webhooks.on_member_removed.url`. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + webhooks_on_message_send_url=webhooks_on_message_send_url, + webhooks_on_message_send_method=webhooks_on_message_send_method, + webhooks_on_message_update_url=webhooks_on_message_update_url, + webhooks_on_message_update_method=webhooks_on_message_update_method, + webhooks_on_message_remove_url=webhooks_on_message_remove_url, + webhooks_on_message_remove_method=webhooks_on_message_remove_method, + webhooks_on_channel_add_url=webhooks_on_channel_add_url, + webhooks_on_channel_add_method=webhooks_on_channel_add_method, + webhooks_on_channel_destroy_url=webhooks_on_channel_destroy_url, + webhooks_on_channel_destroy_method=webhooks_on_channel_destroy_method, + webhooks_on_channel_update_url=webhooks_on_channel_update_url, + webhooks_on_channel_update_method=webhooks_on_channel_update_method, + webhooks_on_member_add_url=webhooks_on_member_add_url, + webhooks_on_member_add_method=webhooks_on_member_add_method, + webhooks_on_member_remove_url=webhooks_on_member_remove_url, + webhooks_on_member_remove_method=webhooks_on_member_remove_method, + webhooks_on_message_sent_url=webhooks_on_message_sent_url, + webhooks_on_message_sent_method=webhooks_on_message_sent_method, + webhooks_on_message_updated_url=webhooks_on_message_updated_url, + webhooks_on_message_updated_method=webhooks_on_message_updated_method, + webhooks_on_message_removed_url=webhooks_on_message_removed_url, + webhooks_on_message_removed_method=webhooks_on_message_removed_method, + webhooks_on_channel_added_url=webhooks_on_channel_added_url, + webhooks_on_channel_added_method=webhooks_on_channel_added_method, + webhooks_on_channel_destroyed_url=webhooks_on_channel_destroyed_url, + webhooks_on_channel_destroyed_method=webhooks_on_channel_destroyed_method, + webhooks_on_channel_updated_url=webhooks_on_channel_updated_url, + webhooks_on_channel_updated_method=webhooks_on_channel_updated_method, + webhooks_on_member_added_url=webhooks_on_member_added_url, + webhooks_on_member_added_method=webhooks_on_member_added_method, + webhooks_on_member_removed_url=webhooks_on_member_removed_url, + webhooks_on_member_removed_method=webhooks_on_member_removed_method, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + ) + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + return self._proxy.channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + return self._proxy.roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + return self._proxy.users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._channels: Optional[ChannelList] = None + self._roles: Optional[RoleList] = None + self._users: Optional[UserList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of WebHook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhooks_on_message_send_url: The URL of the webhook to call in response to the `on_message_send` event using the `webhooks.on_message_send.method` HTTP method. + :param webhooks_on_message_send_method: The HTTP method to use when calling the `webhooks.on_message_send.url`. + :param webhooks_on_message_update_url: The URL of the webhook to call in response to the `on_message_update` event using the `webhooks.on_message_update.method` HTTP method. + :param webhooks_on_message_update_method: The HTTP method to use when calling the `webhooks.on_message_update.url`. + :param webhooks_on_message_remove_url: The URL of the webhook to call in response to the `on_message_remove` event using the `webhooks.on_message_remove.method` HTTP method. + :param webhooks_on_message_remove_method: The HTTP method to use when calling the `webhooks.on_message_remove.url`. + :param webhooks_on_channel_add_url: The URL of the webhook to call in response to the `on_channel_add` event using the `webhooks.on_channel_add.method` HTTP method. + :param webhooks_on_channel_add_method: The HTTP method to use when calling the `webhooks.on_channel_add.url`. + :param webhooks_on_channel_destroy_url: The URL of the webhook to call in response to the `on_channel_destroy` event using the `webhooks.on_channel_destroy.method` HTTP method. + :param webhooks_on_channel_destroy_method: The HTTP method to use when calling the `webhooks.on_channel_destroy.url`. + :param webhooks_on_channel_update_url: The URL of the webhook to call in response to the `on_channel_update` event using the `webhooks.on_channel_update.method` HTTP method. + :param webhooks_on_channel_update_method: The HTTP method to use when calling the `webhooks.on_channel_update.url`. + :param webhooks_on_member_add_url: The URL of the webhook to call in response to the `on_member_add` event using the `webhooks.on_member_add.method` HTTP method. + :param webhooks_on_member_add_method: The HTTP method to use when calling the `webhooks.on_member_add.url`. + :param webhooks_on_member_remove_url: The URL of the webhook to call in response to the `on_member_remove` event using the `webhooks.on_member_remove.method` HTTP method. + :param webhooks_on_member_remove_method: The HTTP method to use when calling the `webhooks.on_member_remove.url`. + :param webhooks_on_message_sent_url: The URL of the webhook to call in response to the `on_message_sent` event using the `webhooks.on_message_sent.method` HTTP method. + :param webhooks_on_message_sent_method: The URL of the webhook to call in response to the `on_message_sent` event`. + :param webhooks_on_message_updated_url: The URL of the webhook to call in response to the `on_message_updated` event using the `webhooks.on_message_updated.method` HTTP method. + :param webhooks_on_message_updated_method: The HTTP method to use when calling the `webhooks.on_message_updated.url`. + :param webhooks_on_message_removed_url: The URL of the webhook to call in response to the `on_message_removed` event using the `webhooks.on_message_removed.method` HTTP method. + :param webhooks_on_message_removed_method: The HTTP method to use when calling the `webhooks.on_message_removed.url`. + :param webhooks_on_channel_added_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_added.method` HTTP method. + :param webhooks_on_channel_added_method: The URL of the webhook to call in response to the `on_channel_added` event`. + :param webhooks_on_channel_destroyed_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_destroyed.method` HTTP method. + :param webhooks_on_channel_destroyed_method: The HTTP method to use when calling the `webhooks.on_channel_destroyed.url`. + :param webhooks_on_channel_updated_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_channel_updated_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_added_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_member_added_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_removed_url: The URL of the webhook to call in response to the `on_member_removed` event using the `webhooks.on_member_removed.method` HTTP method. + :param webhooks_on_member_removed_method: The HTTP method to use when calling the `webhooks.on_member_removed.url`. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Webhooks.OnMessageSend.Url": webhooks_on_message_send_url, + "Webhooks.OnMessageSend.Method": webhooks_on_message_send_method, + "Webhooks.OnMessageUpdate.Url": webhooks_on_message_update_url, + "Webhooks.OnMessageUpdate.Method": webhooks_on_message_update_method, + "Webhooks.OnMessageRemove.Url": webhooks_on_message_remove_url, + "Webhooks.OnMessageRemove.Method": webhooks_on_message_remove_method, + "Webhooks.OnChannelAdd.Url": webhooks_on_channel_add_url, + "Webhooks.OnChannelAdd.Method": webhooks_on_channel_add_method, + "Webhooks.OnChannelDestroy.Url": webhooks_on_channel_destroy_url, + "Webhooks.OnChannelDestroy.Method": webhooks_on_channel_destroy_method, + "Webhooks.OnChannelUpdate.Url": webhooks_on_channel_update_url, + "Webhooks.OnChannelUpdate.Method": webhooks_on_channel_update_method, + "Webhooks.OnMemberAdd.Url": webhooks_on_member_add_url, + "Webhooks.OnMemberAdd.Method": webhooks_on_member_add_method, + "Webhooks.OnMemberRemove.Url": webhooks_on_member_remove_url, + "Webhooks.OnMemberRemove.Method": webhooks_on_member_remove_method, + "Webhooks.OnMessageSent.Url": webhooks_on_message_sent_url, + "Webhooks.OnMessageSent.Method": webhooks_on_message_sent_method, + "Webhooks.OnMessageUpdated.Url": webhooks_on_message_updated_url, + "Webhooks.OnMessageUpdated.Method": webhooks_on_message_updated_method, + "Webhooks.OnMessageRemoved.Url": webhooks_on_message_removed_url, + "Webhooks.OnMessageRemoved.Method": webhooks_on_message_removed_method, + "Webhooks.OnChannelAdded.Url": webhooks_on_channel_added_url, + "Webhooks.OnChannelAdded.Method": webhooks_on_channel_added_method, + "Webhooks.OnChannelDestroyed.Url": webhooks_on_channel_destroyed_url, + "Webhooks.OnChannelDestroyed.Method": webhooks_on_channel_destroyed_method, + "Webhooks.OnChannelUpdated.Url": webhooks_on_channel_updated_url, + "Webhooks.OnChannelUpdated.Method": webhooks_on_channel_updated_method, + "Webhooks.OnMemberAdded.Url": webhooks_on_member_added_url, + "Webhooks.OnMemberAdded.Method": webhooks_on_member_added_method, + "Webhooks.OnMemberRemoved.Url": webhooks_on_member_removed_url, + "Webhooks.OnMemberRemoved.Method": webhooks_on_member_removed_method, + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Roles endpoint](https://www.twilio.com/docs/chat/api/roles) for more details. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. Can be: `true` or `false` and the default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/api/chat/webhooks) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of WebHook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhooks_on_message_send_url: The URL of the webhook to call in response to the `on_message_send` event using the `webhooks.on_message_send.method` HTTP method. + :param webhooks_on_message_send_method: The HTTP method to use when calling the `webhooks.on_message_send.url`. + :param webhooks_on_message_update_url: The URL of the webhook to call in response to the `on_message_update` event using the `webhooks.on_message_update.method` HTTP method. + :param webhooks_on_message_update_method: The HTTP method to use when calling the `webhooks.on_message_update.url`. + :param webhooks_on_message_remove_url: The URL of the webhook to call in response to the `on_message_remove` event using the `webhooks.on_message_remove.method` HTTP method. + :param webhooks_on_message_remove_method: The HTTP method to use when calling the `webhooks.on_message_remove.url`. + :param webhooks_on_channel_add_url: The URL of the webhook to call in response to the `on_channel_add` event using the `webhooks.on_channel_add.method` HTTP method. + :param webhooks_on_channel_add_method: The HTTP method to use when calling the `webhooks.on_channel_add.url`. + :param webhooks_on_channel_destroy_url: The URL of the webhook to call in response to the `on_channel_destroy` event using the `webhooks.on_channel_destroy.method` HTTP method. + :param webhooks_on_channel_destroy_method: The HTTP method to use when calling the `webhooks.on_channel_destroy.url`. + :param webhooks_on_channel_update_url: The URL of the webhook to call in response to the `on_channel_update` event using the `webhooks.on_channel_update.method` HTTP method. + :param webhooks_on_channel_update_method: The HTTP method to use when calling the `webhooks.on_channel_update.url`. + :param webhooks_on_member_add_url: The URL of the webhook to call in response to the `on_member_add` event using the `webhooks.on_member_add.method` HTTP method. + :param webhooks_on_member_add_method: The HTTP method to use when calling the `webhooks.on_member_add.url`. + :param webhooks_on_member_remove_url: The URL of the webhook to call in response to the `on_member_remove` event using the `webhooks.on_member_remove.method` HTTP method. + :param webhooks_on_member_remove_method: The HTTP method to use when calling the `webhooks.on_member_remove.url`. + :param webhooks_on_message_sent_url: The URL of the webhook to call in response to the `on_message_sent` event using the `webhooks.on_message_sent.method` HTTP method. + :param webhooks_on_message_sent_method: The URL of the webhook to call in response to the `on_message_sent` event`. + :param webhooks_on_message_updated_url: The URL of the webhook to call in response to the `on_message_updated` event using the `webhooks.on_message_updated.method` HTTP method. + :param webhooks_on_message_updated_method: The HTTP method to use when calling the `webhooks.on_message_updated.url`. + :param webhooks_on_message_removed_url: The URL of the webhook to call in response to the `on_message_removed` event using the `webhooks.on_message_removed.method` HTTP method. + :param webhooks_on_message_removed_method: The HTTP method to use when calling the `webhooks.on_message_removed.url`. + :param webhooks_on_channel_added_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_added.method` HTTP method. + :param webhooks_on_channel_added_method: The URL of the webhook to call in response to the `on_channel_added` event`. + :param webhooks_on_channel_destroyed_url: The URL of the webhook to call in response to the `on_channel_added` event using the `webhooks.on_channel_destroyed.method` HTTP method. + :param webhooks_on_channel_destroyed_method: The HTTP method to use when calling the `webhooks.on_channel_destroyed.url`. + :param webhooks_on_channel_updated_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_channel_updated_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_added_url: The URL of the webhook to call in response to the `on_channel_updated` event using the `webhooks.on_channel_updated.method` HTTP method. + :param webhooks_on_member_added_method: The HTTP method to use when calling the `webhooks.on_channel_updated.url`. + :param webhooks_on_member_removed_url: The URL of the webhook to call in response to the `on_member_removed` event using the `webhooks.on_member_removed.method` HTTP method. + :param webhooks_on_member_removed_method: The HTTP method to use when calling the `webhooks.on_member_removed.url`. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Webhooks.OnMessageSend.Url": webhooks_on_message_send_url, + "Webhooks.OnMessageSend.Method": webhooks_on_message_send_method, + "Webhooks.OnMessageUpdate.Url": webhooks_on_message_update_url, + "Webhooks.OnMessageUpdate.Method": webhooks_on_message_update_method, + "Webhooks.OnMessageRemove.Url": webhooks_on_message_remove_url, + "Webhooks.OnMessageRemove.Method": webhooks_on_message_remove_method, + "Webhooks.OnChannelAdd.Url": webhooks_on_channel_add_url, + "Webhooks.OnChannelAdd.Method": webhooks_on_channel_add_method, + "Webhooks.OnChannelDestroy.Url": webhooks_on_channel_destroy_url, + "Webhooks.OnChannelDestroy.Method": webhooks_on_channel_destroy_method, + "Webhooks.OnChannelUpdate.Url": webhooks_on_channel_update_url, + "Webhooks.OnChannelUpdate.Method": webhooks_on_channel_update_method, + "Webhooks.OnMemberAdd.Url": webhooks_on_member_add_url, + "Webhooks.OnMemberAdd.Method": webhooks_on_member_add_method, + "Webhooks.OnMemberRemove.Url": webhooks_on_member_remove_url, + "Webhooks.OnMemberRemove.Method": webhooks_on_member_remove_method, + "Webhooks.OnMessageSent.Url": webhooks_on_message_sent_url, + "Webhooks.OnMessageSent.Method": webhooks_on_message_sent_method, + "Webhooks.OnMessageUpdated.Url": webhooks_on_message_updated_url, + "Webhooks.OnMessageUpdated.Method": webhooks_on_message_updated_method, + "Webhooks.OnMessageRemoved.Url": webhooks_on_message_removed_url, + "Webhooks.OnMessageRemoved.Method": webhooks_on_message_removed_method, + "Webhooks.OnChannelAdded.Url": webhooks_on_channel_added_url, + "Webhooks.OnChannelAdded.Method": webhooks_on_channel_added_method, + "Webhooks.OnChannelDestroyed.Url": webhooks_on_channel_destroyed_url, + "Webhooks.OnChannelDestroyed.Method": webhooks_on_channel_destroyed_method, + "Webhooks.OnChannelUpdated.Url": webhooks_on_channel_updated_url, + "Webhooks.OnChannelUpdated.Method": webhooks_on_channel_updated_method, + "Webhooks.OnMemberAdded.Url": webhooks_on_member_added_url, + "Webhooks.OnMemberAdded.Method": webhooks_on_member_added_method, + "Webhooks.OnMemberRemoved.Url": webhooks_on_member_removed_url, + "Webhooks.OnMemberRemoved.Method": webhooks_on_member_removed_method, + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + if self._channels is None: + self._channels = ChannelList( + self._version, + self._solution["sid"], + ) + return self._channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + if self._roles is None: + self._roles = RoleList( + self._version, + self._solution["sid"], + ) + return self._roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution["sid"], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create(self, friendly_name: str) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async(self, friendly_name: str) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c924e03 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..6ee9a11 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__init__.py new file mode 100644 index 0000000..1585613 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__init__.py @@ -0,0 +1,760 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v1.service.channel.invite import InviteList +from twilio.rest.chat.v1.service.channel.member import MemberList +from twilio.rest.chat.v1.service.channel.message import MessageList + + +class ChannelInstance(InstanceResource): + + class ChannelType(object): + PUBLIC = "public" + PRIVATE = "private" + + """ + :ivar sid: The unique string that we created to identify the Channel resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Channel resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :ivar attributes: The JSON string that stores application-specific data. **Note** If this property has been assigned a value, it's only displayed in a FETCH action that returns a single resource; otherwise, it's null. If the attributes have not been set, `{}` is returned. + :ivar type: + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar created_by: The `identity` of the User that created the channel. If the Channel was created by using the API, the value is `system`. + :ivar members_count: The number of Members in the Channel. + :ivar messages_count: The number of Messages in the Channel. + :ivar url: The absolute URL of the Channel resource. + :ivar links: The absolute URLs of the [Members](https://www.twilio.com/docs/chat/api/members), [Messages](https://www.twilio.com/docs/chat/api/messages) , [Invites](https://www.twilio.com/docs/chat/api/invites) and, if it exists, the last [Message](https://www.twilio.com/docs/chat/api/messages) for the Channel. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.type: Optional["ChannelInstance.ChannelType"] = payload.get("type") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.members_count: Optional[int] = deserialize.integer( + payload.get("members_count") + ) + self.messages_count: Optional[int] = deserialize.integer( + payload.get("messages_count") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ChannelInstance": + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelInstance": + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Update the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated ChannelInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Asynchronous coroutine to update the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated ChannelInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + return self._proxy.invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + return self._proxy.members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to update the resource from. + :param sid: The Twilio-provided string that uniquely identifies the Channel resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Channels/{sid}".format(**self._solution) + + self._invites: Optional[InviteList] = None + self._members: Optional[MemberList] = None + self._messages: Optional[MessageList] = None + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ChannelInstance: + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelInstance: + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Update the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronous coroutine to update the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + if self._invites is None: + self._invites = InviteList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + if self._members is None: + self._members = MemberList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelInstance: + """ + Build an instance of ChannelInstance + + :param payload: Payload response from the API + """ + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Channels".format(**self._solution) + + def create( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + ) -> ChannelInstance: + """ + Create the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param type: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronously create the ChannelInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param type: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelInstance]: + """ + Streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(type=type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelInstance]: + """ + Asynchronously streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(type=type, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + type=type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Asynchronously lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + type=type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelPage(self._version, response, self._solution) + + async def page_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Asynchronously retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChannelPage: + """ + Retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChannelPage: + """ + Asynchronously retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The Twilio-provided string that uniquely identifies the Channel resource to update. + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The Twilio-provided string that uniquely identifies the Channel resource to update. + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c7dcc4f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/invite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/invite.cpython-310.pyc new file mode 100644 index 0000000..2048f9c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/invite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/member.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/member.cpython-310.pyc new file mode 100644 index 0000000..cba2678 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/member.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/message.cpython-310.pyc new file mode 100644 index 0000000..6817bee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/__pycache__/message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/invite.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/invite.py new file mode 100644 index 0000000..6beccff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/invite.py @@ -0,0 +1,576 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InviteInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Invite resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Invite resource. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the resource belongs to. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/api/chat/rest/users) within the [Service](https://www.twilio.com/docs/api/chat/rest/services). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more info. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the resource. + :ivar created_by: The `identity` of the User that created the invite. + :ivar url: The absolute URL of the Invite resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.created_by: Optional[str] = payload.get("created_by") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[InviteContext] = None + + @property + def _proxy(self) -> "InviteContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InviteContext for this InviteInstance + """ + if self._context is None: + self._context = InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InviteInstance": + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InviteInstance": + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InviteContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the InviteContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to fetch the resource from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the resource to fetch belongs to. + :param sid: The Twilio-provided string that uniquely identifies the Invite resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Invites/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InviteInstance: + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InviteInstance: + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InvitePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InviteInstance: + """ + Build an instance of InviteInstance + + :param payload: Payload response from the API + """ + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InviteList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the InviteList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the resources to read belong to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Invites".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Create the InviteInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/v1/service). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more info. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the new member. + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Asynchronously create the InviteInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/v1/service). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more info. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the new member. + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InviteInstance]: + """ + Streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InviteInstance]: + """ + Asynchronously streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Asynchronously lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InvitePage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Asynchronously retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InvitePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InvitePage: + """ + Retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InvitePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InvitePage: + """ + Asynchronously retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InvitePage(self._version, response, self._solution) + + def get(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: The Twilio-provided string that uniquely identifies the Invite resource to fetch. + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: The Twilio-provided string that uniquely identifies the Invite resource to fetch. + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/member.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/member.py new file mode 100644 index 0000000..f0cd214 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/member.py @@ -0,0 +1,686 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MemberInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Member resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Member resource. + :ivar channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) for the member. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/api/chat/rest/users) within the [Service](https://www.twilio.com/docs/api/chat/rest/services). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more info. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the member. + :ivar last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) in the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) that the Member has read. + :ivar last_consumption_timestamp: The ISO 8601 timestamp string that represents the date-time of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) read event for the Member within the [Channel](https://www.twilio.com/docs/api/chat/rest/channels). + :ivar url: The absolute URL of the Member resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.last_consumption_timestamp: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("last_consumption_timestamp")) + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MemberContext] = None + + @property + def _proxy(self) -> "MemberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MemberContext for this MemberInstance + """ + if self._context is None: + self._context = MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MemberInstance": + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MemberInstance": + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> "MemberInstance": + """ + Update the MemberInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) that the Member has read within the [Channel](https://www.twilio.com/docs/api/chat/rest/channels). + + :returns: The updated MemberInstance + """ + return self._proxy.update( + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> "MemberInstance": + """ + Asynchronous coroutine to update the MemberInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) that the Member has read within the [Channel](https://www.twilio.com/docs/api/chat/rest/channels). + + :returns: The updated MemberInstance + """ + return await self._proxy.update_async( + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MemberContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to update the resource from. + :param channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the member to update belongs to. Can be the Channel resource's `sid` or `unique_name`. + :param sid: The Twilio-provided string that uniquely identifies the Member resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Members/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MemberInstance: + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MemberInstance: + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> MemberInstance: + """ + Update the MemberInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) that the Member has read within the [Channel](https://www.twilio.com/docs/api/chat/rest/channels). + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronous coroutine to update the MemberInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) that the Member has read within the [Channel](https://www.twilio.com/docs/api/chat/rest/channels). + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MemberInstance: + """ + Build an instance of MemberInstance + + :param payload: Payload response from the API + """ + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MemberList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MemberList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + :param channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the members to read belong to. Can be the Channel resource's `sid` or `unique_name` value. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Members".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Create the MemberInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/services). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Asynchronously create the MemberInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/services). See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/api/services). + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MemberInstance]: + """ + Streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MemberInstance]: + """ + Asynchronously streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Asynchronously lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MemberPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Asynchronously retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/api/chat/rest/v1/user)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MemberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MemberPage: + """ + Retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MemberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MemberPage: + """ + Asynchronously retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MemberPage(self._version, response, self._solution) + + def get(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: The Twilio-provided string that uniquely identifies the Member resource to update. + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: The Twilio-provided string that uniquely identifies the Member resource to update. + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/message.py new file mode 100644 index 0000000..f524c92 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/channel/message.py @@ -0,0 +1,701 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar sid: The unique string that we created to identify the Message resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Message resource. + :ivar attributes: The JSON string that stores application-specific data. **Note** If this property has been assigned a value, it's only displayed in a FETCH action that returns a single resource; otherwise, it's null. If the attributes have not been set, `{}` is returned. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar to: The SID of the [Channel](https://www.twilio.com/docs/chat/api/channels) that the message was sent to. + :ivar channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the Message resource belongs to. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar was_edited: Whether the message has been edited since it was created. + :ivar _from: The [identity](https://www.twilio.com/docs/api/chat/guides/identity) of the message's author. The default value is `system`. + :ivar body: The content of the message. + :ivar index: The index of the message within the [Channel](https://www.twilio.com/docs/chat/api/channels). + :ivar url: The absolute URL of the Message resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.service_sid: Optional[str] = payload.get("service_sid") + self.to: Optional[str] = payload.get("to") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.was_edited: Optional[bool] = payload.get("was_edited") + self._from: Optional[str] = payload.get("from") + self.body: Optional[str] = payload.get("body") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MessageInstance + """ + return self._proxy.update( + body=body, + attributes=attributes, + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + body=body, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to update the resource from. + :param channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the message belongs to. Can be the Channel's `sid` or `unique_name`. + :param sid: The Twilio-provided string that uniquely identifies the Message resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Messages/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + :param channel_sid: The unique ID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the message to read belongs to. Can be the Channel's `sid` or `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Messages".format( + **self._solution + ) + + def create( + self, + body: str, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param from_: The [identity](https://www.twilio.com/docs/api/chat/guides/identity) of the new message's author. The default value is `system`. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Body": body, + "From": from_, + "Attributes": attributes, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + body: str, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param body: The message to send to the channel. Can also be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param from_: The [identity](https://www.twilio.com/docs/api/chat/guides/identity) of the new message's author. The default value is `system`. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Body": body, + "From": from_, + "Attributes": attributes, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The Twilio-provided string that uniquely identifies the Message resource to update. + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The Twilio-provided string that uniquely identifies the Message resource to update. + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/role.py new file mode 100644 index 0000000..1913986 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/role.py @@ -0,0 +1,615 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CHANNEL = "channel" + DEPLOYMENT = "deployment" + + """ + :ivar sid: The unique string that we created to identify the Role resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the Role resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar permissions: An array of the permissions the role has been granted, formatted as a JSON string. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the Role resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to update the resource from. + :param sid: The Twilio-provided string that uniquely identifies the Role resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the RoleList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Roles".format(**self._solution) + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type` and are described in the documentation. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response, self._solution) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The Twilio-provided string that uniquely identifies the Role resource to update. + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The Twilio-provided string that uniquely identifies the Role resource to update. + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__init__.py new file mode 100644 index 0000000..b065096 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__init__.py @@ -0,0 +1,693 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v1.service.user.user_channel import UserChannelList + + +class UserInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the User resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar attributes: The JSON string that stores application-specific data. **Note** If this property has been assigned a value, it's only displayed in a FETCH action that returns a single resource; otherwise, it's null. If the attributes have not been set, `{}` is returned. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the user. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the [Service](https://www.twilio.com/docs/api/chat/rest/services). This value is often a username or an email address. See [access tokens](https://www.twilio.com/docs/api/chat/guides/create-tokens) for more info. + :ivar is_online: Whether the User is actively connected to the Service instance and online. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, if the User has never been online for the Service instance, even if the Service's `reachability_enabled` is `true`. + :ivar is_notifiable: Whether the User has a potentially valid Push Notification registration (APN or GCM) for the Service instance. If at least one registration exists, `true`; otherwise `false`. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, and if the User has never had a notification registration, even if the Service's `reachability_enabled` is `true`. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar joined_channels_count: The number of Channels this User is a Member of. + :ivar links: The absolute URLs of the [Channel](https://www.twilio.com/docs/chat/api/channels) and [Binding](https://www.twilio.com/docs/chat/rest/bindings-resource) resources related to the user. + :ivar url: The absolute URL of the User resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.joined_channels_count: Optional[int] = deserialize.integer( + payload.get("joined_channels_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to this user. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + return self._proxy.update( + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to this user. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + return self._proxy.user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to update the resource from. + :param sid: The Twilio-provided string that uniquely identifies the User resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{sid}".format(**self._solution) + + self._user_channels: Optional[UserChannelList] = None + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to this user. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to this user. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + if self._user_channels is None: + self._user_channels = UserChannelList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Users".format(**self._solution) + + def create( + self, + identity: str, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/v1/service). This value is often a username or email address. See the Identity documentation for more details. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the new User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the new resource. This value is often used for display purposes. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/api/chat/rest/v1/user) within the [Service](https://www.twilio.com/docs/api/chat/rest/v1/service). This value is often a username or email address. See the Identity documentation for more details. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/api/chat/rest/roles) assigned to the new User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the new resource. This value is often used for display purposes. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The Twilio-provided string that uniquely identifies the User resource to update. + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The Twilio-provided string that uniquely identifies the User resource to update. + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..04b6fe4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/user_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/user_channel.cpython-310.pyc new file mode 100644 index 0000000..f9cd0d6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/__pycache__/user_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/user_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/user_channel.py new file mode 100644 index 0000000..01e639c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v1/service/user/user_channel.py @@ -0,0 +1,312 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserChannelInstance(InstanceResource): + + class ChannelStatus(object): + JOINED = "joined" + INVITED = "invited" + NOT_PARTICIPATING = "not_participating" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/api/rest/account) that created the User Channel resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) the resource is associated with. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) the resource belongs to. + :ivar member_sid: The SID of a [Member](https://www.twilio.com/docs/api/chat/rest/members) that represents the User on the Channel. + :ivar status: + :ivar last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/api/chat/rest/messages) in the [Channel](https://www.twilio.com/docs/api/chat/rest/channels) that the Member has read. + :ivar unread_messages_count: The number of unread Messages in the Channel for the User. Note that retrieving messages on a client endpoint does not mean that messages are consumed or read. See [Consumption Horizon feature](/docs/api/chat/guides/consumption-horizon) to learn how to mark messages as consumed. + :ivar links: The absolute URLs of the [Members](https://www.twilio.com/docs/chat/api/members), [Messages](https://www.twilio.com/docs/chat/api/messages) , [Invites](https://www.twilio.com/docs/chat/api/invites) and, if it exists, the last [Message](https://www.twilio.com/docs/chat/api/messages) for the Channel. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], service_sid: str, user_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.member_sid: Optional[str] = payload.get("member_sid") + self.status: Optional["UserChannelInstance.ChannelStatus"] = payload.get( + "status" + ) + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserChannelInstance: + """ + Build an instance of UserChannelInstance + + :param payload: Payload response from the API + """ + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserChannelList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/api/chat/rest/services) to read the resources from. + :param user_sid: The SID of the [User](https://www.twilio.com/docs/api/chat/rest/users) to read the User Channel resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Channels".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserChannelInstance]: + """ + Streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserChannelInstance]: + """ + Asynchronously streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Asynchronously lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Asynchronously retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserChannelPage: + """ + Retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserChannelPage: + """ + Asynchronously retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__init__.py new file mode 100644 index 0000000..e949532 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.chat.v2.credential import CredentialList +from twilio.rest.chat.v2.service import ServiceList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Chat + + :param domain: The Twilio.chat domain + """ + super().__init__(domain, "v2") + self._credentials: Optional[CredentialList] = None + self._services: Optional[ServiceList] = None + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fac3307 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..0f05b1f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/credential.py new file mode 100644 index 0000000..8011ed0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/credential.py @@ -0,0 +1,681 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushService(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: The unique string that we created to identify the Credential resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Credential resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Credential resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushService"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: The SID of the Credential resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----` + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The SID of the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The SID of the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__init__.py new file mode 100644 index 0000000..f6fa9e2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__init__.py @@ -0,0 +1,1098 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v2.service.binding import BindingList +from twilio.rest.chat.v2.service.channel import ChannelList +from twilio.rest.chat.v2.service.role import RoleList +from twilio.rest.chat.v2.service.user import UserList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar default_service_role_sid: The service role assigned to users when they are added to the service. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :ivar default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :ivar default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :ivar read_status_enabled: Whether the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature is enabled. The default is `true`. + :ivar reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) is enabled for this Service instance. The default is `false`. + :ivar typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :ivar consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :ivar limits: An object that describes the limits of the service instance. The `limits` object contains `channel_members` to describe the members/channel limit and `user_channels` to describe the channels/user limit. `channel_members` can be 1,000 or less, with a default of 250. `user_channels` can be 1,000 or less, with a default value of 100. + :ivar pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar webhook_filters: The list of webhook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :ivar pre_webhook_retry_count: The number of times to retry a call to the `pre_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. Default retry count is 0 times, which means the call won't be retried. + :ivar post_webhook_retry_count: The number of times to retry a call to the `post_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. The default is 0, which means the call won't be retried. + :ivar notifications: The notification configuration for the Service instance. See [Push Notification Configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :ivar media: An object that describes the properties of media that the service supports. The object contains the `size_limit_mb` property, which describes the size of the largest media file in MB; and the `compatibility_message` property, which contains the message text to send when a media message does not have any text. + :ivar url: The absolute URL of the Service resource. + :ivar links: The absolute URLs of the Service's [Channels](https://www.twilio.com/docs/chat/channels), [Roles](https://www.twilio.com/docs/chat/rest/role-resource), [Bindings](https://www.twilio.com/docs/chat/rest/binding-resource), and [Users](https://www.twilio.com/docs/chat/rest/user-resource). + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.default_service_role_sid: Optional[str] = payload.get( + "default_service_role_sid" + ) + self.default_channel_role_sid: Optional[str] = payload.get( + "default_channel_role_sid" + ) + self.default_channel_creator_role_sid: Optional[str] = payload.get( + "default_channel_creator_role_sid" + ) + self.read_status_enabled: Optional[bool] = payload.get("read_status_enabled") + self.reachability_enabled: Optional[bool] = payload.get("reachability_enabled") + self.typing_indicator_timeout: Optional[int] = deserialize.integer( + payload.get("typing_indicator_timeout") + ) + self.consumption_report_interval: Optional[int] = deserialize.integer( + payload.get("consumption_report_interval") + ) + self.limits: Optional[Dict[str, object]] = payload.get("limits") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.webhook_filters: Optional[List[str]] = payload.get("webhook_filters") + self.pre_webhook_retry_count: Optional[int] = deserialize.integer( + payload.get("pre_webhook_retry_count") + ) + self.post_webhook_retry_count: Optional[int] = deserialize.integer( + payload.get("post_webhook_retry_count") + ) + self.notifications: Optional[Dict[str, object]] = payload.get("notifications") + self.media: Optional[Dict[str, object]] = payload.get("media") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. The default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_sound: The name of the sound to play when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. The default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_added_to_channel_sound: The name of the sound to play when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. The default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_removed_from_channel_sound: The name of the sound to play to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. The default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param notifications_invited_to_channel_sound: The name of the sound to play when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of webhook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + :param media_compatibility_message: The message to send when a media message has no text. Can be used as placeholder message. + :param pre_webhook_retry_count: The number of times to retry a call to the `pre_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. Default retry count is 0 times, which means the call won't be retried. + :param post_webhook_retry_count: The number of times to retry a call to the `post_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. The default is 0, which means the call won't be retried. + :param notifications_log_enabled: Whether to log notifications. The default is `false`. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_new_message_sound=notifications_new_message_sound, + notifications_new_message_badge_count_enabled=notifications_new_message_badge_count_enabled, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_added_to_channel_sound=notifications_added_to_channel_sound, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_removed_from_channel_sound=notifications_removed_from_channel_sound, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + notifications_invited_to_channel_sound=notifications_invited_to_channel_sound, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + media_compatibility_message=media_compatibility_message, + pre_webhook_retry_count=pre_webhook_retry_count, + post_webhook_retry_count=post_webhook_retry_count, + notifications_log_enabled=notifications_log_enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. The default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_sound: The name of the sound to play when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. The default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_added_to_channel_sound: The name of the sound to play when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. The default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_removed_from_channel_sound: The name of the sound to play to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. The default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param notifications_invited_to_channel_sound: The name of the sound to play when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of webhook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + :param media_compatibility_message: The message to send when a media message has no text. Can be used as placeholder message. + :param pre_webhook_retry_count: The number of times to retry a call to the `pre_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. Default retry count is 0 times, which means the call won't be retried. + :param post_webhook_retry_count: The number of times to retry a call to the `post_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. The default is 0, which means the call won't be retried. + :param notifications_log_enabled: Whether to log notifications. The default is `false`. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_new_message_sound=notifications_new_message_sound, + notifications_new_message_badge_count_enabled=notifications_new_message_badge_count_enabled, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_added_to_channel_sound=notifications_added_to_channel_sound, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_removed_from_channel_sound=notifications_removed_from_channel_sound, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + notifications_invited_to_channel_sound=notifications_invited_to_channel_sound, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + media_compatibility_message=media_compatibility_message, + pre_webhook_retry_count=pre_webhook_retry_count, + post_webhook_retry_count=post_webhook_retry_count, + notifications_log_enabled=notifications_log_enabled, + ) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + return self._proxy.bindings + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + return self._proxy.channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + return self._proxy.roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + return self._proxy.users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The SID of the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._bindings: Optional[BindingList] = None + self._channels: Optional[ChannelList] = None + self._roles: Optional[RoleList] = None + self._users: Optional[UserList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. The default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_sound: The name of the sound to play when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. The default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_added_to_channel_sound: The name of the sound to play when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. The default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_removed_from_channel_sound: The name of the sound to play to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. The default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param notifications_invited_to_channel_sound: The name of the sound to play when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of webhook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + :param media_compatibility_message: The message to send when a media message has no text. Can be used as placeholder message. + :param pre_webhook_retry_count: The number of times to retry a call to the `pre_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. Default retry count is 0 times, which means the call won't be retried. + :param post_webhook_retry_count: The number of times to retry a call to the `post_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. The default is 0, which means the call won't be retried. + :param notifications_log_enabled: Whether to log notifications. The default is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.NewMessage.Sound": notifications_new_message_sound, + "Notifications.NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + notifications_new_message_badge_count_enabled + ), + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.AddedToChannel.Sound": notifications_added_to_channel_sound, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.RemovedFromChannel.Sound": notifications_removed_from_channel_sound, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "Notifications.InvitedToChannel.Sound": notifications_invited_to_channel_sound, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + "Media.CompatibilityMessage": media_compatibility_message, + "PreWebhookRetryCount": pre_webhook_retry_count, + "PostWebhookRetryCount": post_webhook_retry_count, + "Notifications.LogEnabled": serialize.boolean_to_string( + notifications_log_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. + :param default_service_role_sid: The service role assigned to users when they are added to the service. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_role_sid: The channel role assigned to users when they are added to a channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param default_channel_creator_role_sid: The channel role assigned to a channel creator when they join a new channel. See the [Role resource](https://www.twilio.com/docs/chat/rest/role-resource) for more info about roles. + :param read_status_enabled: Whether to enable the [Message Consumption Horizon](https://www.twilio.com/docs/chat/consumption-horizon) feature. The default is `true`. + :param reachability_enabled: Whether to enable the [Reachability Indicator](https://www.twilio.com/docs/chat/reachability-indicator) for this Service instance. The default is `false`. + :param typing_indicator_timeout: How long in seconds after a `started typing` event until clients should assume that user is no longer typing, even if no `ended typing` message was received. The default is 5 seconds. + :param consumption_report_interval: DEPRECATED. The interval in seconds between consumption reports submission batches from client endpoints. + :param notifications_new_message_enabled: Whether to send a notification when a new message is added to a channel. The default is `false`. + :param notifications_new_message_template: The template to use to create the notification text displayed when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_sound: The name of the sound to play when a new message is added to a channel and `notifications.new_message.enabled` is `true`. + :param notifications_new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param notifications_added_to_channel_enabled: Whether to send a notification when a member is added to a channel. The default is `false`. + :param notifications_added_to_channel_template: The template to use to create the notification text displayed when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_added_to_channel_sound: The name of the sound to play when a member is added to a channel and `notifications.added_to_channel.enabled` is `true`. + :param notifications_removed_from_channel_enabled: Whether to send a notification to a user when they are removed from a channel. The default is `false`. + :param notifications_removed_from_channel_template: The template to use to create the notification text displayed to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_removed_from_channel_sound: The name of the sound to play to a user when they are removed from a channel and `notifications.removed_from_channel.enabled` is `true`. + :param notifications_invited_to_channel_enabled: Whether to send a notification when a user is invited to a channel. The default is `false`. + :param notifications_invited_to_channel_template: The template to use to create the notification text displayed when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param notifications_invited_to_channel_sound: The name of the sound to play when a user is invited to a channel and `notifications.invited_to_channel.enabled` is `true`. + :param pre_webhook_url: The URL for pre-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param post_webhook_url: The URL for post-event webhooks, which are called by using the `webhook_method`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_method: The HTTP method to use for calls to the `pre_webhook_url` and `post_webhook_url` webhooks. Can be: `POST` or `GET` and the default is `POST`. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param webhook_filters: The list of webhook events that are enabled for this Service instance. See [Webhook Events](https://www.twilio.com/docs/chat/webhook-events) for more details. + :param limits_channel_members: The maximum number of Members that can be added to Channels within this Service. Can be up to 1,000. + :param limits_user_channels: The maximum number of Channels Users can be a Member of within this Service. Can be up to 1,000. + :param media_compatibility_message: The message to send when a media message has no text. Can be used as placeholder message. + :param pre_webhook_retry_count: The number of times to retry a call to the `pre_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. Default retry count is 0 times, which means the call won't be retried. + :param post_webhook_retry_count: The number of times to retry a call to the `post_webhook_url` if the request times out (after 5 seconds) or it receives a 429, 503, or 504 HTTP response. The default is 0, which means the call won't be retried. + :param notifications_log_enabled: Whether to log notifications. The default is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.NewMessage.Sound": notifications_new_message_sound, + "Notifications.NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + notifications_new_message_badge_count_enabled + ), + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.AddedToChannel.Sound": notifications_added_to_channel_sound, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.RemovedFromChannel.Sound": notifications_removed_from_channel_sound, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "Notifications.InvitedToChannel.Sound": notifications_invited_to_channel_sound, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + "Media.CompatibilityMessage": media_compatibility_message, + "PreWebhookRetryCount": pre_webhook_retry_count, + "PostWebhookRetryCount": post_webhook_retry_count, + "Notifications.LogEnabled": serialize.boolean_to_string( + notifications_log_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + if self._bindings is None: + self._bindings = BindingList( + self._version, + self._solution["sid"], + ) + return self._bindings + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + if self._channels is None: + self._channels = ChannelList( + self._version, + self._solution["sid"], + ) + return self._channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + if self._roles is None: + self._roles = RoleList( + self._version, + self._solution["sid"], + ) + return self._roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution["sid"], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create(self, friendly_name: str) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async(self, friendly_name: str) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0550e17 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/binding.cpython-310.pyc new file mode 100644 index 0000000..e666f97 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..b9775d8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/binding.py new file mode 100644 index 0000000..2f960c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/binding.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BindingInstance(InstanceResource): + + class BindingType(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: The unique string that we created to identify the Binding resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Binding resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Binding resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar endpoint: The unique endpoint identifier for the Binding. The format of this value depends on the `binding_type`. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :ivar credential_sid: The SID of the [Credential](https://www.twilio.com/docs/chat/rest/credential-resource) for the binding. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :ivar binding_type: + :ivar message_types: The [Programmable Chat message types](https://www.twilio.com/docs/chat/push-notification-configuration#push-types) the binding is subscribed to. + :ivar url: The absolute URL of the Binding resource. + :ivar links: The absolute URLs of the Binding's [User](https://www.twilio.com/docs/chat/rest/user-resource). + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.binding_type: Optional["BindingInstance.BindingType"] = payload.get( + "binding_type" + ) + self.message_types: Optional[List[str]] = payload.get("message_types") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[BindingContext] = None + + @property + def _proxy(self) -> "BindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BindingContext for this BindingInstance + """ + if self._context is None: + self._context = BindingContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BindingInstance": + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BindingInstance": + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BindingContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the Binding resource from. + :param sid: The SID of the Binding resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Bindings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BindingInstance: + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BindingInstance: + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BindingInstance: + """ + Build an instance of BindingInstance + + :param payload: Payload response from the API + """ + return BindingInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BindingList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the BindingList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Binding resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Bindings".format(**self._solution) + + def stream( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BindingInstance]: + """ + Streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BindingInstance]: + """ + Asynchronously streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Asynchronously lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BindingPage(self._version, response, self._solution) + + async def page_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Asynchronously retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BindingPage: + """ + Retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BindingPage: + """ + Asynchronously retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: The SID of the Binding resource to fetch. + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: The SID of the Binding resource to fetch. + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__init__.py new file mode 100644 index 0000000..2b49faf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__init__.py @@ -0,0 +1,914 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v2.service.channel.invite import InviteList +from twilio.rest.chat.v2.service.channel.member import MemberList +from twilio.rest.chat.v2.service.channel.message import MessageList +from twilio.rest.chat.v2.service.channel.webhook import WebhookList + + +class ChannelInstance(InstanceResource): + + class ChannelType(object): + PUBLIC = "public" + PRIVATE = "private" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the Channel resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Channel resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Channel resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :ivar attributes: The JSON string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar type: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The `identity` of the User that created the channel. If the Channel was created by using the API, the value is `system`. + :ivar members_count: The number of Members in the Channel. + :ivar messages_count: The number of Messages that have been passed in the Channel. + :ivar url: The absolute URL of the Channel resource. + :ivar links: The absolute URLs of the [Members](https://www.twilio.com/docs/chat/rest/member-resource), [Messages](https://www.twilio.com/docs/chat/rest/message-resource), [Invites](https://www.twilio.com/docs/chat/rest/invite-resource), Webhooks and, if it exists, the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) for the Channel. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.type: Optional["ChannelInstance.ChannelType"] = payload.get("type") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.members_count: Optional[int] = deserialize.integer( + payload.get("members_count") + ) + self.messages_count: Optional[int] = deserialize.integer( + payload.get("messages_count") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ChannelInstance": + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelInstance": + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 256 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 256 characters or less in length and unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The updated ChannelInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + created_by=created_by, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 256 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 256 characters or less in length and unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The updated ChannelInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + created_by=created_by, + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + return self._proxy.invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + return self._proxy.members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + return self._proxy.webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the Channel resource in. + :param sid: The SID of the Channel resource to update. This value can be either the `sid` or the `unique_name` of the Channel resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Channels/{sid}".format(**self._solution) + + self._invites: Optional[InviteList] = None + self._members: Optional[MemberList] = None + self._messages: Optional[MessageList] = None + self._webhooks: Optional[WebhookList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ChannelInstance: + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelInstance: + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 256 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 256 characters or less in length and unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 256 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. This value must be 256 characters or less in length and unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + if self._invites is None: + self._invites = InviteList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + if self._members is None: + self._members = MemberList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._messages + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelInstance: + """ + Build an instance of ChannelInstance + + :param payload: Payload response from the API + """ + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Channel resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Channels".format(**self._solution) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Create the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the Channel resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param type: + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. The default value is `null`. Note that this parameter should only be used in cases where a Channel is being recreated from a backup/separate source and where a Message was previously updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronously create the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the Channel resource's `sid` in the URL. This value must be 64 characters or less in length and be unique within the Service. + :param attributes: A valid JSON string that contains application-specific data. + :param type: + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this should only be used in cases where a Channel is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. The default value is `null`. Note that this parameter should only be used in cases where a Channel is being recreated from a backup/separate source and where a Message was previously updated. + :param created_by: The `identity` of the User that created the channel. Default is: `system`. + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelInstance]: + """ + Streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(type=type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelInstance]: + """ + Asynchronously streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(type=type, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + type=type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Asynchronously lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + type=type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelPage(self._version, response, self._solution) + + async def page_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Asynchronously retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: The visibility of the Channels to read. Can be: `public` or `private` and defaults to `public`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChannelPage: + """ + Retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChannelPage: + """ + Asynchronously retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The SID of the Channel resource to update. This value can be either the `sid` or the `unique_name` of the Channel resource to update. + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The SID of the Channel resource to update. This value can be either the `sid` or the `unique_name` of the Channel resource to update. + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..66d7a3b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/invite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/invite.cpython-310.pyc new file mode 100644 index 0000000..a53fc29 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/invite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/member.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/member.cpython-310.pyc new file mode 100644 index 0000000..6f96045 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/member.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/message.cpython-310.pyc new file mode 100644 index 0000000..cc9d136 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..832bf3a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/invite.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/invite.py new file mode 100644 index 0000000..2c73337 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/invite.py @@ -0,0 +1,576 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InviteInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Invite resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Invite resource. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Invite resource belongs to. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Invite resource is associated with. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) assigned to the resource. + :ivar created_by: The `identity` of the User that created the invite. + :ivar url: The absolute URL of the Invite resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.created_by: Optional[str] = payload.get("created_by") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[InviteContext] = None + + @property + def _proxy(self) -> "InviteContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InviteContext for this InviteInstance + """ + if self._context is None: + self._context = InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InviteInstance": + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InviteInstance": + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InviteContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the InviteContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the Invite resource from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Invite resource to fetch belongs to. This value can be the Channel resource's `sid` or `unique_name`. + :param sid: The SID of the Invite resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Invites/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InviteInstance: + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InviteInstance: + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InvitePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InviteInstance: + """ + Build an instance of InviteInstance + + :param payload: Payload response from the API + """ + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InviteList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the InviteList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Invite resources from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Invite resources to read belong to. This value can be the Channel resource's `sid` or `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Invites".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Create the InviteInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) assigned to the new member. + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Asynchronously create the InviteInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) assigned to the new member. + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InviteInstance]: + """ + Streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InviteInstance]: + """ + Asynchronously streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Asynchronously lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InvitePage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Asynchronously retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InvitePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InvitePage: + """ + Retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InvitePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InvitePage: + """ + Asynchronously retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InvitePage(self._version, response, self._solution) + + def get(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: The SID of the Invite resource to fetch. + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: The SID of the Invite resource to fetch. + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/member.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/member.py new file mode 100644 index 0000000..c71d3e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/member.py @@ -0,0 +1,857 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MemberInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the Member resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Member resource. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Member resource belongs to. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Member resource is associated with. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) assigned to the member. + :ivar last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :ivar last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :ivar url: The absolute URL of the Member resource. + :ivar attributes: The JSON string that stores application-specific data. If attributes have not been set, `{}` is returned. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.last_consumption_timestamp: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("last_consumption_timestamp")) + ) + self.url: Optional[str] = payload.get("url") + self.attributes: Optional[str] = payload.get("attributes") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MemberContext] = None + + @property + def _proxy(self) -> "MemberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MemberContext for this MemberInstance + """ + if self._context is None: + self._context = MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MemberInstance": + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MemberInstance": + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MemberInstance": + """ + Update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) that the Member has read within the [Channel](https://www.twilio.com/docs/chat/channels). + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MemberInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MemberInstance": + """ + Asynchronous coroutine to update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) that the Member has read within the [Channel](https://www.twilio.com/docs/chat/channels). + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MemberInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MemberContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the Member resource in. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Member resource to update belongs to. This value can be the Channel resource's `sid` or `unique_name`. + :param sid: The SID of the Member resource to update. This value can be either the Member's `sid` or its `identity` value. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Members/{sid}".format( + **self._solution + ) + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MemberInstance: + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MemberInstance: + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) that the Member has read within the [Channel](https://www.twilio.com/docs/chat/channels). + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronous coroutine to update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) that the Member has read within the [Channel](https://www.twilio.com/docs/chat/channels). + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MemberInstance: + """ + Build an instance of MemberInstance + + :param payload: Payload response from the API + """ + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MemberList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MemberList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Member resources from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Member resources to read belong to. This value can be the Channel resource's `sid` or `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Members".format( + **self._solution + ) + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Create the MemberInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. This parameter should only be used when recreating a Member from a backup/separate source. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. The default value is `null`. Note that this parameter should only be used when a Member is being recreated from a backup/separate source and where a Member was previously updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronously create the MemberInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the member. The default roles are those specified on the [Service](https://www.twilio.com/docs/chat/rest/service-resource). + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. This parameter should only be used when recreating a Member from a backup/separate source. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. Note that this parameter should only be used when a Member is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. The default value is `null`. Note that this parameter should only be used when a Member is being recreated from a backup/separate source and where a Member was previously updated. + :param attributes: A valid JSON string that contains application-specific data. + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MemberInstance]: + """ + Streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MemberInstance]: + """ + Asynchronously streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Asynchronously lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MemberPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Asynchronously retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the Member resources to read. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MemberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MemberPage: + """ + Retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MemberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MemberPage: + """ + Asynchronously retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MemberPage(self._version, response, self._solution) + + def get(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: The SID of the Member resource to update. This value can be either the Member's `sid` or its `identity` value. + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: The SID of the Member resource to update. This value can be either the Member's `sid` or its `identity` value. + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/message.py new file mode 100644 index 0000000..e98f526 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/message.py @@ -0,0 +1,857 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the Message resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Message resource. + :ivar attributes: The JSON string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Message resource is associated with. + :ivar to: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) that the message was sent to. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Message resource belongs to. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :ivar was_edited: Whether the message has been edited since it was created. + :ivar _from: The [Identity](https://www.twilio.com/docs/chat/identity) of the message's author. The default value is `system`. + :ivar body: The content of the message. + :ivar index: The index of the message within the [Channel](https://www.twilio.com/docs/chat/channels). Indices may skip numbers, but will always be in order of when the message was received. + :ivar type: The Message type. Can be: `text` or `media`. + :ivar media: An object that describes the Message's media, if the message contains media. The object contains these fields: `content_type` with the MIME type of the media, `filename` with the name of the media, `sid` with the SID of the Media resource, and `size` with the media object's file size in bytes. If the Message has no media, this value is `null`. + :ivar url: The absolute URL of the Message resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.service_sid: Optional[str] = payload.get("service_sid") + self.to: Optional[str] = payload.get("to") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.last_updated_by: Optional[str] = payload.get("last_updated_by") + self.was_edited: Optional[bool] = payload.get("was_edited") + self._from: Optional[str] = payload.get("from") + self.body: Optional[str] = payload.get("body") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.type: Optional[str] = payload.get("type") + self.media: Optional[Dict[str, object]] = payload.get("media") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the message's author. + + :returns: The updated MessageInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + body=body, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + last_updated_by=last_updated_by, + from_=from_, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the message's author. + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + body=body, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + last_updated_by=last_updated_by, + from_=from_, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the Message resource in. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Message resource to update belongs to. This value can be the Channel resource's `sid` or `unique_name`. + :param sid: The SID of the Message resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Messages/{sid}".format( + **self._solution + ) + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the message's author. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "From": from_, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the message's author. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "From": from_, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Message resources from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Message resource to read belongs to. This value can be the Channel resource's `sid` or `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Messages".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the new message's author. The default value is `system`. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param media_sid: The SID of the [Media](https://www.twilio.com/docs/chat/rest/media) to attach to the new Message. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "From": from_, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "Body": body, + "MediaSid": media_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param from_: The [Identity](https://www.twilio.com/docs/chat/identity) of the new message's author. The default value is `system`. + :param attributes: A valid JSON string that contains application-specific data. + :param date_created: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was created. The default value is the current time set by the Chat service. This parameter should only be used when a Chat's history is being recreated from a backup/separate source. + :param date_updated: The date, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, to assign to the resource as the date it was last updated. + :param last_updated_by: The [Identity](https://www.twilio.com/docs/chat/identity) of the User who last updated the Message, if applicable. + :param body: The message to send to the channel. Can be an empty string or `null`, which sets the value as an empty string. You can send structured data in the body by serializing it as a string. + :param media_sid: The SID of the [Media](https://www.twilio.com/docs/chat/rest/media) to attach to the new Message. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "From": from_, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "Body": body, + "MediaSid": media_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending) with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The SID of the Message resource to update. + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: The SID of the Message resource to update. + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/webhook.py new file mode 100644 index 0000000..ca738ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/channel/webhook.py @@ -0,0 +1,770 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + class Type(object): + WEBHOOK = "webhook" + TRIGGER = "trigger" + STUDIO = "studio" + + """ + :ivar sid: The unique string that we created to identify the Channel Webhook resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Channel Webhook resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Channel Webhook resource is associated with. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Channel Webhook resource belongs to. + :ivar type: The type of webhook. Can be: `webhook`, `studio`, or `trigger`. + :ivar url: The absolute URL of the Channel Webhook resource. + :ivar configuration: The JSON string that describes how the channel webhook is configured. The configuration object contains the `url`, `method`, `filters`, and `retry_count` values that are configured by the create and update actions. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.type: Optional[str] = payload.get("type") + self.url: Optional[str] = payload.get("url") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` = `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + configuration_retry_count=configuration_retry_count, + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` = `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + configuration_retry_count=configuration_retry_count, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) with the Channel that has the Webhook resource to update. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Channel Webhook resource to update belongs to. This value can be the Channel resource's `sid` or `unique_name`. + :param sid: The SID of the Channel Webhook resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Webhooks/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` = `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` = `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebhookInstance: + """ + Build an instance of WebhookInstance + + :param payload: Payload response from the API + """ + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) with the Channel to read the resources from. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the Channel Webhook resources to read belong to. This value can be the Channel resource's `sid` or `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Webhooks".format( + **self._solution + ) + + def create( + self, + type: "WebhookInstance.Type", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Create the WebhookInstance + + :param type: + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` is `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Type": type, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + type: "WebhookInstance.Type", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronously create the WebhookInstance + + :param type: + :param configuration_url: The URL of the webhook to call using the `configuration.method`. + :param configuration_method: + :param configuration_filters: The events that cause us to call the Channel Webhook. Used when `type` is `webhook`. This parameter takes only one event. To specify more than one event, repeat this parameter for each event. For the list of possible events, see [Webhook Event Triggers](https://www.twilio.com/docs/chat/webhook-events#webhook-event-trigger). + :param configuration_triggers: A string that will cause us to call the webhook when it is present in a message body. This parameter takes only one trigger string. To specify more than one, repeat this parameter for each trigger string up to a total of 5 trigger strings. Used only when `type` = `trigger`. + :param configuration_flow_sid: The SID of the Studio [Flow](https://www.twilio.com/docs/studio/rest-api/flow) to call when an event in `configuration.filters` occurs. Used only when `type` is `studio`. + :param configuration_retry_count: The number of times to retry the webhook if the first attempt fails. Can be an integer between 0 and 3, inclusive, and the default is 0. + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Type": type, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebhookInstance]: + """ + Streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebhookInstance]: + """ + Asynchronously streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Asynchronously lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebhookPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Asynchronously retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebhookPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WebhookPage: + """ + Retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WebhookPage: + """ + Asynchronously retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + def get(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: The SID of the Channel Webhook resource to update. + """ + return WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: The SID of the Channel Webhook resource to update. + """ + return WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/role.py new file mode 100644 index 0000000..5bb373c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/role.py @@ -0,0 +1,615 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CHANNEL = "channel" + DEPLOYMENT = "deployment" + + """ + :ivar sid: The unique string that we created to identify the Role resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Role resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Role resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar permissions: An array of the permissions the role has been granted. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Role resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the Role resource in. + :param sid: The SID of the Role resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the RoleList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the Role resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Roles".format(**self._solution) + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response, self._solution) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__init__.py new file mode 100644 index 0000000..2c4ac5a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__init__.py @@ -0,0 +1,758 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.chat.v2.service.user.user_binding import UserBindingList +from twilio.rest.chat.v2.service.user.user_channel import UserChannelList + + +class UserInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the User resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the User resource is associated with. + :ivar attributes: The JSON string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) assigned to the user. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). This value is often a username or an email address, and is case-sensitive. See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :ivar is_online: Whether the User is actively connected to the Service instance and online. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, if the User has never been online for the Service instance, even if the Service's `reachability_enabled` is `true`. + :ivar is_notifiable: Whether the User has a potentially valid Push Notification registration (APN or GCM) for the Service instance. If at least one registration exists, `true`; otherwise `false`. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, and if the User has never had a notification registration, even if the Service's `reachability_enabled` is `true`. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar joined_channels_count: The number of Channels the User is a Member of. + :ivar links: The absolute URLs of the [Channel](https://www.twilio.com/docs/chat/channels) and [Binding](https://www.twilio.com/docs/chat/rest/binding-resource) resources related to the user. + :ivar url: The absolute URL of the User resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.joined_channels_count: Optional[int] = deserialize.integer( + payload.get("joined_channels_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + @property + def user_bindings(self) -> UserBindingList: + """ + Access the user_bindings + """ + return self._proxy.user_bindings + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + return self._proxy.user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the User resource in. + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{sid}".format(**self._solution) + + self._user_bindings: Optional[UserBindingList] = None + self._user_channels: Optional[UserChannelList] = None + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the resource. It is often used for display purposes. + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def user_bindings(self) -> UserBindingList: + """ + Access the user_bindings + """ + if self._user_bindings is None: + self._user_bindings = UserBindingList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_bindings + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + if self._user_channels is None: + self._user_channels = UserChannelList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the User resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Users".format(**self._solution) + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). This value is often a username or email address. See the Identity documentation for more info. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the new User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the new resource. This value is often used for display purposes. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). This value is often a username or email address. See the Identity documentation for more info. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: The SID of the [Role](https://www.twilio.com/docs/chat/rest/role-resource) to assign to the new User. + :param attributes: A valid JSON string that contains application-specific data. + :param friendly_name: A descriptive string that you create to describe the new resource. This value is often used for display purposes. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c390d28 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_binding.cpython-310.pyc new file mode 100644 index 0000000..6c2930a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_channel.cpython-310.pyc new file mode 100644 index 0000000..3ea2dbf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/__pycache__/user_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_binding.py new file mode 100644 index 0000000..4170c74 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_binding.py @@ -0,0 +1,536 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserBindingInstance(InstanceResource): + + class BindingType(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: The unique string that we created to identify the User Binding resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the User Binding resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the User Binding resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar endpoint: The unique endpoint identifier for the User Binding. The format of the value depends on the `binding_type`. + :ivar identity: The application-defined string that uniquely identifies the resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/chat/rest/service-resource). See [access tokens](https://www.twilio.com/docs/chat/create-tokens) for more info. + :ivar user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) with the User Binding resource. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :ivar credential_sid: The SID of the [Credential](https://www.twilio.com/docs/chat/rest/credential-resource) for the binding. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :ivar binding_type: + :ivar message_types: The [Programmable Chat message types](https://www.twilio.com/docs/chat/push-notification-configuration#push-types) the binding is subscribed to. + :ivar url: The absolute URL of the User Binding resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + user_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.user_sid: Optional[str] = payload.get("user_sid") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.binding_type: Optional["UserBindingInstance.BindingType"] = payload.get( + "binding_type" + ) + self.message_types: Optional[List[str]] = payload.get("message_types") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserBindingContext] = None + + @property + def _proxy(self) -> "UserBindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserBindingContext for this UserBindingInstance + """ + if self._context is None: + self._context = UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserBindingInstance": + """ + Fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserBindingInstance": + """ + Asynchronous coroutine to fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserBindingContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, user_sid: str, sid: str): + """ + Initialize the UserBindingContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the User Binding resource from. + :param user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) with the User Binding resource to fetch. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param sid: The SID of the User Binding resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Bindings/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserBindingInstance: + """ + Fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserBindingInstance: + """ + Asynchronous coroutine to fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserBindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserBindingInstance: + """ + Build an instance of UserBindingInstance + + :param payload: Payload response from the API + """ + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserBindingList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserBindingList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the User Binding resources from. + :param user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) with the User Binding resources to read. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Bindings".format( + **self._solution + ) + + def stream( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserBindingInstance]: + """ + Streams UserBindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["UserBindingInstance.BindingType"] binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(binding_type=binding_type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserBindingInstance]: + """ + Asynchronously streams UserBindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["UserBindingInstance.BindingType"] binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + binding_type=binding_type, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserBindingInstance]: + """ + Lists UserBindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["UserBindingInstance.BindingType"] binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + binding_type=binding_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserBindingInstance]: + """ + Asynchronously lists UserBindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["UserBindingInstance.BindingType"] binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + binding_type=binding_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserBindingPage: + """ + Retrieve a single page of UserBindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserBindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserBindingPage(self._version, response, self._solution) + + async def page_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserBindingPage: + """ + Asynchronously retrieve a single page of UserBindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the User Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserBindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserBindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserBindingPage: + """ + Retrieve a specific page of UserBindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserBindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserBindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserBindingPage: + """ + Asynchronously retrieve a specific page of UserBindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserBindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserBindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserBindingContext: + """ + Constructs a UserBindingContext + + :param sid: The SID of the User Binding resource to fetch. + """ + return UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> UserBindingContext: + """ + Constructs a UserBindingContext + + :param sid: The SID of the User Binding resource to fetch. + """ + return UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_channel.py new file mode 100644 index 0000000..1fd2525 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v2/service/user/user_channel.py @@ -0,0 +1,682 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserChannelInstance(InstanceResource): + + class ChannelStatus(object): + JOINED = "joined" + INVITED = "invited" + NOT_PARTICIPATING = "not_participating" + + class NotificationLevel(object): + DEFAULT = "default" + MUTED = "muted" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the User Channel resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the User Channel resource is associated with. + :ivar channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) the User Channel resource belongs to. + :ivar user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) the User Channel belongs to. + :ivar member_sid: The SID of a [Member](https://www.twilio.com/docs/chat/rest/member-resource) that represents the User on the Channel. + :ivar status: + :ivar last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :ivar unread_messages_count: The number of unread Messages in the Channel for the User. Note that retrieving messages on a client endpoint does not mean that messages are consumed or read. See [Consumption Horizon feature](https://www.twilio.com/docs/chat/consumption-horizon) to learn how to mark messages as consumed. + :ivar links: The absolute URLs of the [Members](https://www.twilio.com/docs/chat/rest/member-resource), [Messages](https://www.twilio.com/docs/chat/rest/message-resource) , [Invites](https://www.twilio.com/docs/chat/rest/invite-resource) and, if it exists, the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) for the Channel. + :ivar url: The absolute URL of the User Channel resource. + :ivar notification_level: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + user_sid: str, + channel_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.member_sid: Optional[str] = payload.get("member_sid") + self.status: Optional["UserChannelInstance.ChannelStatus"] = payload.get( + "status" + ) + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + self.notification_level: Optional["UserChannelInstance.NotificationLevel"] = ( + payload.get("notification_level") + ) + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "channel_sid": channel_sid or self.channel_sid, + } + self._context: Optional[UserChannelContext] = None + + @property + def _proxy(self) -> "UserChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserChannelContext for this UserChannelInstance + """ + if self._context is None: + self._context = UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "UserChannelInstance": + """ + Fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserChannelInstance": + """ + Asynchronous coroutine to fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> "UserChannelInstance": + """ + Update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + + :returns: The updated UserChannelInstance + """ + return self._proxy.update( + notification_level=notification_level, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + ) + + async def update_async( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> "UserChannelInstance": + """ + Asynchronous coroutine to update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + + :returns: The updated UserChannelInstance + """ + return await self._proxy.update_async( + notification_level=notification_level, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, user_sid: str, channel_sid: str + ): + """ + Initialize the UserChannelContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to update the User Channel resource in. + :param user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) to update the User Channel resource from. This value can be either the `sid` or the `identity` of the User resource. + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) with the User Channel resource to update. This value can be the Channel resource's `sid` or `unique_name`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "channel_sid": channel_sid, + } + self._uri = ( + "/Services/{service_sid}/Users/{user_sid}/Channels/{channel_sid}".format( + **self._solution + ) + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> UserChannelInstance: + """ + Fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def fetch_async(self) -> UserChannelInstance: + """ + Asynchronous coroutine to fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def update( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> UserChannelInstance: + """ + Update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + + :returns: The updated UserChannelInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def update_async( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> UserChannelInstance: + """ + Asynchronous coroutine to update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: The index of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) in the [Channel](https://www.twilio.com/docs/chat/channels) that the Member has read. + :param last_consumption_timestamp: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of the last [Message](https://www.twilio.com/docs/chat/rest/message-resource) read event for the Member within the [Channel](https://www.twilio.com/docs/chat/channels). + + :returns: The updated UserChannelInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserChannelInstance: + """ + Build an instance of UserChannelInstance + + :param payload: Payload response from the API + """ + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserChannelList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the User Channel resources from. + :param user_sid: The SID of the [User](https://www.twilio.com/docs/chat/rest/user-resource) to read the User Channel resources from. This value can be either the `sid` or the `identity` of the User resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Channels".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserChannelInstance]: + """ + Streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserChannelInstance]: + """ + Asynchronously streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Asynchronously lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Asynchronously retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserChannelPage: + """ + Retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserChannelPage: + """ + Asynchronously retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + def get(self, channel_sid: str) -> UserChannelContext: + """ + Constructs a UserChannelContext + + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) with the User Channel resource to update. This value can be the Channel resource's `sid` or `unique_name`. + """ + return UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=channel_sid, + ) + + def __call__(self, channel_sid: str) -> UserChannelContext: + """ + Constructs a UserChannelContext + + :param channel_sid: The SID of the [Channel](https://www.twilio.com/docs/chat/channels) with the User Channel resource to update. This value can be the Channel resource's `sid` or `unique_name`. + """ + return UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=channel_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__init__.py new file mode 100644 index 0000000..582dc44 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.chat.v3.channel import ChannelList + + +class V3(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V3 version of Chat + + :param domain: The Twilio.chat domain + """ + super().__init__(domain, "v3") + self._channels: Optional[ChannelList] = None + + @property + def channels(self) -> ChannelList: + if self._channels is None: + self._channels = ChannelList(self) + return self._channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ad77d4f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/channel.cpython-310.pyc new file mode 100644 index 0000000..67ba6a1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/__pycache__/channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/channel.py new file mode 100644 index 0000000..e9d10b5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/chat/v3/channel.py @@ -0,0 +1,305 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Chat + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ChannelInstance(InstanceResource): + + class ChannelType(object): + PUBLIC = "public" + PRIVATE = "private" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the Channel resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Channel resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the Channel resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :ivar attributes: The JSON string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar type: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The `identity` of the User that created the channel. If the Channel was created by using the API, the value is `system`. + :ivar members_count: The number of Members in the Channel. + :ivar messages_count: The number of Messages that have been passed in the Channel. + :ivar messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this channel belongs to. + :ivar url: The absolute URL of the Channel resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: Optional[str] = None, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.type: Optional["ChannelInstance.ChannelType"] = payload.get("type") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.members_count: Optional[int] = deserialize.integer( + payload.get("members_count") + ) + self.messages_count: Optional[int] = deserialize.integer( + payload.get("messages_count") + ) + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid or self.service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param type: + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this channel belongs to. + + :returns: The updated ChannelInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + type=type, + messaging_service_sid=messaging_service_sid, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param type: + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this channel belongs to. + + :returns: The updated ChannelInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + type=type, + messaging_service_sid=messaging_service_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param sid: A 34 character string that uniquely identifies this Channel. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Channels/{sid}".format(**self._solution) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param type: + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this channel belongs to. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "Type": type, + "MessagingServiceSid": messaging_service_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param type: + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this channel belongs to. + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "Type": type, + "MessagingServiceSid": messaging_service_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, service_sid: str, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param service_sid: The unique SID identifier of the Service. + :param sid: A 34 character string that uniquely identifies this Channel. + """ + return ChannelContext(self._version, service_sid=service_sid, sid=sid) + + def __call__(self, service_sid: str, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param service_sid: The unique SID identifier of the Service. + :param sid: A 34 character string that uniquely identifies this Channel. + """ + return ChannelContext(self._version, service_sid=service_sid, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/ContentBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/ContentBase.py new file mode 100644 index 0000000..a8511f9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/ContentBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.content.v1 import V1 + + +class ContentBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Content Domain + + :returns: Domain for Content + """ + super().__init__(twilio, "https://content.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Content + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__init__.py new file mode 100644 index 0000000..7663bd7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__init__.py @@ -0,0 +1,35 @@ +from warnings import warn + +from twilio.rest.content.ContentBase import ContentBase +from twilio.rest.content.v1.content import ContentList +from twilio.rest.content.v1.content_and_approvals import ContentAndApprovalsList +from twilio.rest.content.v1.legacy_content import LegacyContentList + + +class Content(ContentBase): + @property + def contents(self) -> ContentList: + warn( + "contents is deprecated. Use v1.contents instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.contents + + @property + def content_and_approvals(self) -> ContentAndApprovalsList: + warn( + "content_and_approvals is deprecated. Use v1.content_and_approvals instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.content_and_approvals + + @property + def legacy_contents(self) -> LegacyContentList: + warn( + "legacy_contents is deprecated. Use v1.legacy_contents instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.legacy_contents diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/ContentBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/ContentBase.cpython-310.pyc new file mode 100644 index 0000000..2cbfb63 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/ContentBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..841a77f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__init__.py new file mode 100644 index 0000000..1410fe4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.content.v1.content import ContentList +from twilio.rest.content.v1.content_and_approvals import ContentAndApprovalsList +from twilio.rest.content.v1.legacy_content import LegacyContentList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Content + + :param domain: The Twilio.content domain + """ + super().__init__(domain, "v1") + self._contents: Optional[ContentList] = None + self._content_and_approvals: Optional[ContentAndApprovalsList] = None + self._legacy_contents: Optional[LegacyContentList] = None + + @property + def contents(self) -> ContentList: + if self._contents is None: + self._contents = ContentList(self) + return self._contents + + @property + def content_and_approvals(self) -> ContentAndApprovalsList: + if self._content_and_approvals is None: + self._content_and_approvals = ContentAndApprovalsList(self) + return self._content_and_approvals + + @property + def legacy_contents(self) -> LegacyContentList: + if self._legacy_contents is None: + self._legacy_contents = LegacyContentList(self) + return self._legacy_contents + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..11d59a4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/content_and_approvals.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/content_and_approvals.cpython-310.pyc new file mode 100644 index 0000000..6ea05c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/content_and_approvals.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/legacy_content.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/legacy_content.cpython-310.pyc new file mode 100644 index 0000000..416474d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/__pycache__/legacy_content.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__init__.py new file mode 100644 index 0000000..72e1e82 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__init__.py @@ -0,0 +1,993 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.content.v1.content.approval_create import ApprovalCreateList +from twilio.rest.content.v1.content.approval_fetch import ApprovalFetchList + + +class ContentInstance(InstanceResource): + + class AuthenticationActionType(object): + COPY_CODE = "COPY_CODE" + + class CallToActionActionType(object): + URL = "URL" + PHONE_NUMBER = "PHONE_NUMBER" + + class CardActionType(object): + URL = "URL" + PHONE_NUMBER = "PHONE_NUMBER" + QUICK_REPLY = "QUICK_REPLY" + + class QuickReplyActionType(object): + QUICK_REPLY = "QUICK_REPLY" + + """ + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sid: The unique string that that we created to identify the Content resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/usage/api/account) that created Content resource. + :ivar friendly_name: A string name used to describe the Content resource. Not visible to the end recipient. + :ivar language: Two-letter (ISO 639-1) language code (e.g., en) identifying the language the Content resource is in. + :ivar variables: Defines the default placeholder values for variables included in the Content resource. e.g. {\"1\": \"Customer_Name\"}. + :ivar types: The [Content types](https://www.twilio.com/docs/content-api/content-types-overview) (e.g. twilio/text) for this Content resource. + :ivar url: The URL of the resource, relative to `https://content.twilio.com`. + :ivar links: A list of links related to the Content resource, such as approval_fetch and approval_create + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.language: Optional[str] = payload.get("language") + self.variables: Optional[Dict[str, object]] = payload.get("variables") + self.types: Optional[Dict[str, object]] = payload.get("types") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ContentContext] = None + + @property + def _proxy(self) -> "ContentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ContentContext for this ContentInstance + """ + if self._context is None: + self._context = ContentContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ContentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ContentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ContentInstance": + """ + Fetch the ContentInstance + + + :returns: The fetched ContentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ContentInstance": + """ + Asynchronous coroutine to fetch the ContentInstance + + + :returns: The fetched ContentInstance + """ + return await self._proxy.fetch_async() + + @property + def approval_create(self) -> ApprovalCreateList: + """ + Access the approval_create + """ + return self._proxy.approval_create + + @property + def approval_fetch(self) -> ApprovalFetchList: + """ + Access the approval_fetch + """ + return self._proxy.approval_fetch + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ContentContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ContentContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Content resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Content/{sid}".format(**self._solution) + + self._approval_create: Optional[ApprovalCreateList] = None + self._approval_fetch: Optional[ApprovalFetchList] = None + + def delete(self) -> bool: + """ + Deletes the ContentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ContentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ContentInstance: + """ + Fetch the ContentInstance + + + :returns: The fetched ContentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ContentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ContentInstance: + """ + Asynchronous coroutine to fetch the ContentInstance + + + :returns: The fetched ContentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ContentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def approval_create(self) -> ApprovalCreateList: + """ + Access the approval_create + """ + if self._approval_create is None: + self._approval_create = ApprovalCreateList( + self._version, + self._solution["sid"], + ) + return self._approval_create + + @property + def approval_fetch(self) -> ApprovalFetchList: + """ + Access the approval_fetch + """ + if self._approval_fetch is None: + self._approval_fetch = ApprovalFetchList( + self._version, + self._solution["sid"], + ) + return self._approval_fetch + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ContentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ContentInstance: + """ + Build an instance of ContentInstance + + :param payload: Payload response from the API + """ + return ContentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ContentList(ListResource): + + class AuthenticationAction(object): + """ + :ivar type: + :ivar copy_code_text: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.type: Optional["ContentInstance.AuthenticationActionType"] = ( + payload.get("type") + ) + self.copy_code_text: Optional[str] = payload.get("copy_code_text") + + def to_dict(self): + return { + "type": self.type.to_dict(), + "copy_code_text": self.copy_code_text, + } + + class CallToActionAction(object): + """ + :ivar type: + :ivar title: + :ivar url: + :ivar phone: + :ivar id: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.type: Optional["ContentInstance.CallToActionActionType"] = payload.get( + "type" + ) + self.title: Optional[str] = payload.get("title") + self.url: Optional[str] = payload.get("url") + self.phone: Optional[str] = payload.get("phone") + self.id: Optional[str] = payload.get("id") + + def to_dict(self): + return { + "type": self.type.to_dict(), + "title": self.title, + "url": self.url, + "phone": self.phone, + "id": self.id, + } + + class CardAction(object): + """ + :ivar type: + :ivar title: + :ivar url: + :ivar phone: + :ivar id: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.type: Optional["ContentInstance.CardActionType"] = payload.get("type") + self.title: Optional[str] = payload.get("title") + self.url: Optional[str] = payload.get("url") + self.phone: Optional[str] = payload.get("phone") + self.id: Optional[str] = payload.get("id") + + def to_dict(self): + return { + "type": self.type.to_dict(), + "title": self.title, + "url": self.url, + "phone": self.phone, + "id": self.id, + } + + class CatalogItem(object): + """ + :ivar id: + :ivar section_title: + :ivar name: + :ivar media_url: + :ivar price: + :ivar description: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.id: Optional[str] = payload.get("id") + self.section_title: Optional[str] = payload.get("section_title") + self.name: Optional[str] = payload.get("name") + self.media_url: Optional[str] = payload.get("media_url") + self.price: Optional[float] = payload.get("price") + self.description: Optional[str] = payload.get("description") + + def to_dict(self): + return { + "id": self.id, + "section_title": self.section_title, + "name": self.name, + "media_url": self.media_url, + "price": self.price, + "description": self.description, + } + + class ContentCreateRequest(object): + """ + :ivar friendly_name: User defined name of the content + :ivar variables: Key value pairs of variable name to value + :ivar language: Language code for the content + :ivar types: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.variables: Optional[dict[str, str]] = payload.get("variables") + self.language: Optional[str] = payload.get("language") + self.types: Optional[ContentList.Types] = payload.get("types") + + def to_dict(self): + return { + "friendly_name": self.friendly_name, + "variables": self.variables, + "language": self.language, + "types": self.types.to_dict(), + } + + class ListItem(object): + """ + :ivar id: + :ivar item: + :ivar description: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.id: Optional[str] = payload.get("id") + self.item: Optional[str] = payload.get("item") + self.description: Optional[str] = payload.get("description") + + def to_dict(self): + return { + "id": self.id, + "item": self.item, + "description": self.description, + } + + class QuickReplyAction(object): + """ + :ivar type: + :ivar title: + :ivar id: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.type: Optional["ContentInstance.QuickReplyActionType"] = payload.get( + "type" + ) + self.title: Optional[str] = payload.get("title") + self.id: Optional[str] = payload.get("id") + + def to_dict(self): + return { + "type": self.type.to_dict(), + "title": self.title, + "id": self.id, + } + + class TwilioCallToAction(object): + """ + :ivar body: + :ivar actions: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + self.actions: Optional[List[ContentList.CallToActionAction]] = payload.get( + "actions" + ) + + def to_dict(self): + return { + "body": self.body, + "actions": [actions.to_dict() for actions in self.actions], + } + + class TwilioCard(object): + """ + :ivar title: + :ivar subtitle: + :ivar media: + :ivar actions: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.title: Optional[str] = payload.get("title") + self.subtitle: Optional[str] = payload.get("subtitle") + self.media: Optional[List[str]] = payload.get("media") + self.actions: Optional[List[ContentList.CardAction]] = payload.get( + "actions" + ) + + def to_dict(self): + return { + "title": self.title, + "subtitle": self.subtitle, + "media": self.media, + "actions": [actions.to_dict() for actions in self.actions], + } + + class TwilioCatalog(object): + """ + :ivar title: + :ivar body: + :ivar subtitle: + :ivar id: + :ivar items: + :ivar dynamic_items: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.title: Optional[str] = payload.get("title") + self.body: Optional[str] = payload.get("body") + self.subtitle: Optional[str] = payload.get("subtitle") + self.id: Optional[str] = payload.get("id") + self.items: Optional[List[ContentList.CatalogItem]] = payload.get("items") + self.dynamic_items: Optional[str] = payload.get("dynamic_items") + + def to_dict(self): + return { + "title": self.title, + "body": self.body, + "subtitle": self.subtitle, + "id": self.id, + "items": [items.to_dict() for items in self.items], + "dynamic_items": self.dynamic_items, + } + + class TwilioListPicker(object): + """ + :ivar body: + :ivar button: + :ivar items: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + self.button: Optional[str] = payload.get("button") + self.items: Optional[List[ContentList.ListItem]] = payload.get("items") + + def to_dict(self): + return { + "body": self.body, + "button": self.button, + "items": [items.to_dict() for items in self.items], + } + + class TwilioLocation(object): + """ + :ivar latitude: + :ivar longitude: + :ivar label: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.latitude: Optional[float] = payload.get("latitude") + self.longitude: Optional[float] = payload.get("longitude") + self.label: Optional[str] = payload.get("label") + + def to_dict(self): + return { + "latitude": self.latitude, + "longitude": self.longitude, + "label": self.label, + } + + class TwilioMedia(object): + """ + :ivar body: + :ivar media: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + self.media: Optional[List[str]] = payload.get("media") + + def to_dict(self): + return { + "body": self.body, + "media": self.media, + } + + class TwilioQuickReply(object): + """ + :ivar body: + :ivar actions: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + self.actions: Optional[List[ContentList.QuickReplyAction]] = payload.get( + "actions" + ) + + def to_dict(self): + return { + "body": self.body, + "actions": [actions.to_dict() for actions in self.actions], + } + + class TwilioText(object): + """ + :ivar body: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + + def to_dict(self): + return { + "body": self.body, + } + + class Types(object): + """ + :ivar twilio_text: + :ivar twilio_media: + :ivar twilio_location: + :ivar twilio_list_picker: + :ivar twilio_call_to_action: + :ivar twilio_quick_reply: + :ivar twilio_card: + :ivar twilio_catalog: + :ivar whatsapp_card: + :ivar whatsapp_authentication: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.twilio_text: Optional[ContentList.TwilioText] = payload.get( + "twilio_text" + ) + self.twilio_media: Optional[ContentList.TwilioMedia] = payload.get( + "twilio_media" + ) + self.twilio_location: Optional[ContentList.TwilioLocation] = payload.get( + "twilio_location" + ) + self.twilio_list_picker: Optional[ContentList.TwilioListPicker] = ( + payload.get("twilio_list_picker") + ) + self.twilio_call_to_action: Optional[ContentList.TwilioCallToAction] = ( + payload.get("twilio_call_to_action") + ) + self.twilio_quick_reply: Optional[ContentList.TwilioQuickReply] = ( + payload.get("twilio_quick_reply") + ) + self.twilio_card: Optional[ContentList.TwilioCard] = payload.get( + "twilio_card" + ) + self.twilio_catalog: Optional[ContentList.TwilioCatalog] = payload.get( + "twilio_catalog" + ) + self.whatsapp_card: Optional[ContentList.WhatsappCard] = payload.get( + "whatsapp_card" + ) + self.whatsapp_authentication: Optional[ + ContentList.WhatsappAuthentication + ] = payload.get("whatsapp_authentication") + + def to_dict(self): + return { + "twilio_text": self.twilio_text.to_dict(), + "twilio_media": self.twilio_media.to_dict(), + "twilio_location": self.twilio_location.to_dict(), + "twilio_list_picker": self.twilio_list_picker.to_dict(), + "twilio_call_to_action": self.twilio_call_to_action.to_dict(), + "twilio_quick_reply": self.twilio_quick_reply.to_dict(), + "twilio_card": self.twilio_card.to_dict(), + "twilio_catalog": self.twilio_catalog.to_dict(), + "whatsapp_card": self.whatsapp_card.to_dict(), + "whatsapp_authentication": self.whatsapp_authentication.to_dict(), + } + + class WhatsappAuthentication(object): + """ + :ivar add_security_recommendation: + :ivar code_expiration_minutes: + :ivar actions: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.add_security_recommendation: Optional[bool] = payload.get( + "add_security_recommendation" + ) + self.code_expiration_minutes: Optional[float] = payload.get( + "code_expiration_minutes" + ) + self.actions: Optional[List[ContentList.AuthenticationAction]] = ( + payload.get("actions") + ) + + def to_dict(self): + return { + "add_security_recommendation": self.add_security_recommendation, + "code_expiration_minutes": self.code_expiration_minutes, + "actions": [actions.to_dict() for actions in self.actions], + } + + class WhatsappCard(object): + """ + :ivar body: + :ivar footer: + :ivar media: + :ivar header_text: + :ivar actions: + """ + + def __init__(self, payload: Dict[str, Any], sid: Optional[str] = None): + + self.body: Optional[str] = payload.get("body") + self.footer: Optional[str] = payload.get("footer") + self.media: Optional[List[str]] = payload.get("media") + self.header_text: Optional[str] = payload.get("header_text") + self.actions: Optional[List[ContentList.CardAction]] = payload.get( + "actions" + ) + + def to_dict(self): + return { + "body": self.body, + "footer": self.footer, + "media": self.media, + "header_text": self.header_text, + "actions": [actions.to_dict() for actions in self.actions], + } + + def __init__(self, version: Version): + """ + Initialize the ContentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Content" + + def create(self, content_create_request: ContentCreateRequest) -> ContentInstance: + """ + Create the ContentInstance + + :param content_create_request: + + :returns: The created ContentInstance + """ + data = content_create_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ContentInstance(self._version, payload) + + async def create_async( + self, content_create_request: ContentCreateRequest + ) -> ContentInstance: + """ + Asynchronously create the ContentInstance + + :param content_create_request: + + :returns: The created ContentInstance + """ + data = content_create_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ContentInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ContentInstance]: + """ + Streams ContentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ContentInstance]: + """ + Asynchronously streams ContentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ContentInstance]: + """ + Lists ContentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ContentInstance]: + """ + Asynchronously lists ContentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ContentPage: + """ + Retrieve a single page of ContentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ContentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ContentPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ContentPage: + """ + Asynchronously retrieve a single page of ContentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ContentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ContentPage(self._version, response) + + def get_page(self, target_url: str) -> ContentPage: + """ + Retrieve a specific page of ContentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ContentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ContentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ContentPage: + """ + Asynchronously retrieve a specific page of ContentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ContentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ContentPage(self._version, response) + + def get(self, sid: str) -> ContentContext: + """ + Constructs a ContentContext + + :param sid: The Twilio-provided string that uniquely identifies the Content resource to fetch. + """ + return ContentContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ContentContext: + """ + Constructs a ContentContext + + :param sid: The Twilio-provided string that uniquely identifies the Content resource to fetch. + """ + return ContentContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fc3c01b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_create.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_create.cpython-310.pyc new file mode 100644 index 0000000..3d898cd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_create.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_fetch.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_fetch.cpython-310.pyc new file mode 100644 index 0000000..e0d1d25 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/__pycache__/approval_fetch.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_create.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_create.py new file mode 100644 index 0000000..ddd922a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_create.py @@ -0,0 +1,145 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ApprovalCreateInstance(InstanceResource): + """ + :ivar name: + :ivar category: + :ivar content_type: + :ivar status: + :ivar rejection_reason: + :ivar allow_category_change: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], content_sid: str): + super().__init__(version) + + self.name: Optional[str] = payload.get("name") + self.category: Optional[str] = payload.get("category") + self.content_type: Optional[str] = payload.get("content_type") + self.status: Optional[str] = payload.get("status") + self.rejection_reason: Optional[str] = payload.get("rejection_reason") + self.allow_category_change: Optional[bool] = payload.get( + "allow_category_change" + ) + + self._solution = { + "content_sid": content_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ApprovalCreateList(ListResource): + + class ContentApprovalRequest(object): + """ + :ivar name: Name of the template. + :ivar category: A WhatsApp recognized template category. + """ + + def __init__(self, payload: Dict[str, Any], content_sid: str): + + self.name: Optional[str] = payload.get("name") + self.category: Optional[str] = payload.get("category") + + def to_dict(self): + return { + "name": self.name, + "category": self.category, + } + + def __init__(self, version: Version, content_sid: str): + """ + Initialize the ApprovalCreateList + + :param version: Version that contains the resource + :param content_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "content_sid": content_sid, + } + self._uri = "/Content/{content_sid}/ApprovalRequests/whatsapp".format( + **self._solution + ) + + def create( + self, content_approval_request: ContentApprovalRequest + ) -> ApprovalCreateInstance: + """ + Create the ApprovalCreateInstance + + :param content_approval_request: + + :returns: The created ApprovalCreateInstance + """ + data = content_approval_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ApprovalCreateInstance( + self._version, payload, content_sid=self._solution["content_sid"] + ) + + async def create_async( + self, content_approval_request: ContentApprovalRequest + ) -> ApprovalCreateInstance: + """ + Asynchronously create the ApprovalCreateInstance + + :param content_approval_request: + + :returns: The created ApprovalCreateInstance + """ + data = content_approval_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ApprovalCreateInstance( + self._version, payload, content_sid=self._solution["content_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_fetch.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_fetch.py new file mode 100644 index 0000000..f679a92 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content/approval_fetch.py @@ -0,0 +1,192 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ApprovalFetchInstance(InstanceResource): + """ + :ivar sid: The unique string that that we created to identify the Content resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/usage/api/account) that created Content resource. + :ivar whatsapp: Contains the whatsapp approval information for the Content resource, with fields such as approval status, rejection reason, and category, amongst others. + :ivar url: The URL of the resource, relative to `https://content.twilio.com`. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], content_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.whatsapp: Optional[Dict[str, object]] = payload.get("whatsapp") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "content_sid": content_sid, + } + self._context: Optional[ApprovalFetchContext] = None + + @property + def _proxy(self) -> "ApprovalFetchContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ApprovalFetchContext for this ApprovalFetchInstance + """ + if self._context is None: + self._context = ApprovalFetchContext( + self._version, + content_sid=self._solution["content_sid"], + ) + return self._context + + def fetch(self) -> "ApprovalFetchInstance": + """ + Fetch the ApprovalFetchInstance + + + :returns: The fetched ApprovalFetchInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ApprovalFetchInstance": + """ + Asynchronous coroutine to fetch the ApprovalFetchInstance + + + :returns: The fetched ApprovalFetchInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ApprovalFetchContext(InstanceContext): + + def __init__(self, version: Version, content_sid: str): + """ + Initialize the ApprovalFetchContext + + :param version: Version that contains the resource + :param content_sid: The Twilio-provided string that uniquely identifies the Content resource whose approval information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "content_sid": content_sid, + } + self._uri = "/Content/{content_sid}/ApprovalRequests".format(**self._solution) + + def fetch(self) -> ApprovalFetchInstance: + """ + Fetch the ApprovalFetchInstance + + + :returns: The fetched ApprovalFetchInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ApprovalFetchInstance( + self._version, + payload, + content_sid=self._solution["content_sid"], + ) + + async def fetch_async(self) -> ApprovalFetchInstance: + """ + Asynchronous coroutine to fetch the ApprovalFetchInstance + + + :returns: The fetched ApprovalFetchInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ApprovalFetchInstance( + self._version, + payload, + content_sid=self._solution["content_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ApprovalFetchList(ListResource): + + def __init__(self, version: Version, content_sid: str): + """ + Initialize the ApprovalFetchList + + :param version: Version that contains the resource + :param content_sid: The Twilio-provided string that uniquely identifies the Content resource whose approval information to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "content_sid": content_sid, + } + + def get(self) -> ApprovalFetchContext: + """ + Constructs a ApprovalFetchContext + + """ + return ApprovalFetchContext( + self._version, content_sid=self._solution["content_sid"] + ) + + def __call__(self) -> ApprovalFetchContext: + """ + Constructs a ApprovalFetchContext + + """ + return ApprovalFetchContext( + self._version, content_sid=self._solution["content_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content_and_approvals.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content_and_approvals.py new file mode 100644 index 0000000..de40cfc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/content_and_approvals.py @@ -0,0 +1,288 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ContentAndApprovalsInstance(InstanceResource): + """ + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sid: The unique string that that we created to identify the Content resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/usage/api/account) that created Content resource. + :ivar friendly_name: A string name used to describe the Content resource. Not visible to the end recipient. + :ivar language: Two-letter (ISO 639-1) language code (e.g., en) identifying the language the Content resource is in. + :ivar variables: Defines the default placeholder values for variables included in the Content resource. e.g. {\"1\": \"Customer_Name\"}. + :ivar types: The [Content types](https://www.twilio.com/docs/content-api/content-types-overview) (e.g. twilio/text) for this Content resource. + :ivar approval_requests: The submitted information and approval request status of the Content resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.language: Optional[str] = payload.get("language") + self.variables: Optional[Dict[str, object]] = payload.get("variables") + self.types: Optional[Dict[str, object]] = payload.get("types") + self.approval_requests: Optional[Dict[str, object]] = payload.get( + "approval_requests" + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ContentAndApprovalsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ContentAndApprovalsInstance: + """ + Build an instance of ContentAndApprovalsInstance + + :param payload: Payload response from the API + """ + return ContentAndApprovalsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ContentAndApprovalsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ContentAndApprovalsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ContentAndApprovals" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ContentAndApprovalsInstance]: + """ + Streams ContentAndApprovalsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ContentAndApprovalsInstance]: + """ + Asynchronously streams ContentAndApprovalsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ContentAndApprovalsInstance]: + """ + Lists ContentAndApprovalsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ContentAndApprovalsInstance]: + """ + Asynchronously lists ContentAndApprovalsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ContentAndApprovalsPage: + """ + Retrieve a single page of ContentAndApprovalsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ContentAndApprovalsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ContentAndApprovalsPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ContentAndApprovalsPage: + """ + Asynchronously retrieve a single page of ContentAndApprovalsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ContentAndApprovalsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ContentAndApprovalsPage(self._version, response) + + def get_page(self, target_url: str) -> ContentAndApprovalsPage: + """ + Retrieve a specific page of ContentAndApprovalsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ContentAndApprovalsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ContentAndApprovalsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ContentAndApprovalsPage: + """ + Asynchronously retrieve a specific page of ContentAndApprovalsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ContentAndApprovalsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ContentAndApprovalsPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/legacy_content.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/legacy_content.py new file mode 100644 index 0000000..cfb4ac6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/content/v1/legacy_content.py @@ -0,0 +1,290 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Content + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class LegacyContentInstance(InstanceResource): + """ + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sid: The unique string that that we created to identify the Content resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/usage/api/account) that created Content resource. + :ivar friendly_name: A string name used to describe the Content resource. Not visible to the end recipient. + :ivar language: Two-letter (ISO 639-1) language code (e.g., en) identifying the language the Content resource is in. + :ivar variables: Defines the default placeholder values for variables included in the Content resource. e.g. {\"1\": \"Customer_Name\"}. + :ivar types: The [Content types](https://www.twilio.com/docs/content-api/content-types-overview) (e.g. twilio/text) for this Content resource. + :ivar legacy_template_name: The string name of the legacy content template associated with this Content resource, unique across all template names for its account. Only lowercase letters, numbers and underscores are allowed + :ivar legacy_body: The string body field of the legacy content template associated with this Content resource + :ivar url: The URL of the resource, relative to `https://content.twilio.com`. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.language: Optional[str] = payload.get("language") + self.variables: Optional[Dict[str, object]] = payload.get("variables") + self.types: Optional[Dict[str, object]] = payload.get("types") + self.legacy_template_name: Optional[str] = payload.get("legacy_template_name") + self.legacy_body: Optional[str] = payload.get("legacy_body") + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class LegacyContentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> LegacyContentInstance: + """ + Build an instance of LegacyContentInstance + + :param payload: Payload response from the API + """ + return LegacyContentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class LegacyContentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the LegacyContentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/LegacyContent" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[LegacyContentInstance]: + """ + Streams LegacyContentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[LegacyContentInstance]: + """ + Asynchronously streams LegacyContentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LegacyContentInstance]: + """ + Lists LegacyContentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LegacyContentInstance]: + """ + Asynchronously lists LegacyContentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LegacyContentPage: + """ + Retrieve a single page of LegacyContentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LegacyContentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return LegacyContentPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LegacyContentPage: + """ + Asynchronously retrieve a single page of LegacyContentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LegacyContentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return LegacyContentPage(self._version, response) + + def get_page(self, target_url: str) -> LegacyContentPage: + """ + Retrieve a specific page of LegacyContentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LegacyContentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return LegacyContentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> LegacyContentPage: + """ + Asynchronously retrieve a specific page of LegacyContentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LegacyContentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return LegacyContentPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/ConversationsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/ConversationsBase.py new file mode 100644 index 0000000..8904308 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/ConversationsBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.conversations.v1 import V1 + + +class ConversationsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Conversations Domain + + :returns: Domain for Conversations + """ + super().__init__(twilio, "https://conversations.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Conversations + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__init__.py new file mode 100644 index 0000000..d60cb42 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__init__.py @@ -0,0 +1,87 @@ +from warnings import warn + +from twilio.rest.conversations.ConversationsBase import ConversationsBase +from twilio.rest.conversations.v1.address_configuration import AddressConfigurationList +from twilio.rest.conversations.v1.configuration import ConfigurationList +from twilio.rest.conversations.v1.conversation import ConversationList +from twilio.rest.conversations.v1.credential import CredentialList +from twilio.rest.conversations.v1.participant_conversation import ( + ParticipantConversationList, +) +from twilio.rest.conversations.v1.role import RoleList +from twilio.rest.conversations.v1.service import ServiceList +from twilio.rest.conversations.v1.user import UserList + + +class Conversations(ConversationsBase): + @property + def configuration(self) -> ConfigurationList: + warn( + "configuration is deprecated. Use v1.configuration instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.configuration + + @property + def address_configurations(self) -> AddressConfigurationList: + warn( + "address_configurations is deprecated. Use v1.address_configurations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.address_configurations + + @property + def conversations(self) -> ConversationList: + warn( + "conversations is deprecated. Use v1.conversations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.conversations + + @property + def credentials(self) -> CredentialList: + warn( + "credentials is deprecated. Use v1.credentials instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.credentials + + @property + def participant_conversations(self) -> ParticipantConversationList: + warn( + "participant_conversations is deprecated. Use v1.participant_conversations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.participant_conversations + + @property + def roles(self) -> RoleList: + warn( + "roles is deprecated. Use v1.roles instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.roles + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services + + @property + def users(self) -> UserList: + warn( + "users is deprecated. Use v1.users instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.users diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/ConversationsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/ConversationsBase.cpython-310.pyc new file mode 100644 index 0000000..fdd516a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/ConversationsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c67afb9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__init__.py new file mode 100644 index 0000000..3296ba2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__init__.py @@ -0,0 +1,101 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.conversations.v1.address_configuration import AddressConfigurationList +from twilio.rest.conversations.v1.configuration import ConfigurationList +from twilio.rest.conversations.v1.conversation import ConversationList +from twilio.rest.conversations.v1.credential import CredentialList +from twilio.rest.conversations.v1.participant_conversation import ( + ParticipantConversationList, +) +from twilio.rest.conversations.v1.role import RoleList +from twilio.rest.conversations.v1.service import ServiceList +from twilio.rest.conversations.v1.user import UserList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Conversations + + :param domain: The Twilio.conversations domain + """ + super().__init__(domain, "v1") + self._address_configurations: Optional[AddressConfigurationList] = None + self._configuration: Optional[ConfigurationList] = None + self._conversations: Optional[ConversationList] = None + self._credentials: Optional[CredentialList] = None + self._participant_conversations: Optional[ParticipantConversationList] = None + self._roles: Optional[RoleList] = None + self._services: Optional[ServiceList] = None + self._users: Optional[UserList] = None + + @property + def address_configurations(self) -> AddressConfigurationList: + if self._address_configurations is None: + self._address_configurations = AddressConfigurationList(self) + return self._address_configurations + + @property + def configuration(self) -> ConfigurationList: + if self._configuration is None: + self._configuration = ConfigurationList(self) + return self._configuration + + @property + def conversations(self) -> ConversationList: + if self._conversations is None: + self._conversations = ConversationList(self) + return self._conversations + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def participant_conversations(self) -> ParticipantConversationList: + if self._participant_conversations is None: + self._participant_conversations = ParticipantConversationList(self) + return self._participant_conversations + + @property + def roles(self) -> RoleList: + if self._roles is None: + self._roles = RoleList(self) + return self._roles + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + @property + def users(self) -> UserList: + if self._users is None: + self._users = UserList(self) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3f7c0cf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/address_configuration.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/address_configuration.cpython-310.pyc new file mode 100644 index 0000000..73cbdd1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/address_configuration.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..12880d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/participant_conversation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/participant_conversation.cpython-310.pyc new file mode 100644 index 0000000..a92e317 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/participant_conversation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..aeb1d5d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/address_configuration.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/address_configuration.py new file mode 100644 index 0000000..86cdb08 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/address_configuration.py @@ -0,0 +1,826 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AddressConfigurationInstance(InstanceResource): + + class AutoCreationType(object): + WEBHOOK = "webhook" + STUDIO = "studio" + DEFAULT = "default" + + class Method(object): + GET = "GET" + POST = "POST" + + class Type(object): + SMS = "sms" + WHATSAPP = "whatsapp" + MESSENGER = "messenger" + GBM = "gbm" + EMAIL = "email" + + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) the address belongs to + :ivar type: Type of Address, value can be `whatsapp` or `sms`. + :ivar address: The unique address to be configured. The address can be a whatsapp address or phone number + :ivar friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :ivar auto_creation: Auto Creation configuration for the address. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar url: An absolute API resource URL for this address configuration. + :ivar address_country: An ISO 3166-1 alpha-2n country code which the address belongs to. This is currently only applicable to short code addresses. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.type: Optional[str] = payload.get("type") + self.address: Optional[str] = payload.get("address") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.auto_creation: Optional[Dict[str, object]] = payload.get("auto_creation") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.address_country: Optional[str] = payload.get("address_country") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AddressConfigurationContext] = None + + @property + def _proxy(self) -> "AddressConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AddressConfigurationContext for this AddressConfigurationInstance + """ + if self._context is None: + self._context = AddressConfigurationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AddressConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddressConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AddressConfigurationInstance": + """ + Fetch the AddressConfigurationInstance + + + :returns: The fetched AddressConfigurationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AddressConfigurationInstance": + """ + Asynchronous coroutine to fetch the AddressConfigurationInstance + + + :returns: The fetched AddressConfigurationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + ) -> "AddressConfigurationInstance": + """ + Update the AddressConfigurationInstance + + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + + :returns: The updated AddressConfigurationInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + auto_creation_enabled=auto_creation_enabled, + auto_creation_type=auto_creation_type, + auto_creation_conversation_service_sid=auto_creation_conversation_service_sid, + auto_creation_webhook_url=auto_creation_webhook_url, + auto_creation_webhook_method=auto_creation_webhook_method, + auto_creation_webhook_filters=auto_creation_webhook_filters, + auto_creation_studio_flow_sid=auto_creation_studio_flow_sid, + auto_creation_studio_retry_count=auto_creation_studio_retry_count, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + ) -> "AddressConfigurationInstance": + """ + Asynchronous coroutine to update the AddressConfigurationInstance + + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + + :returns: The updated AddressConfigurationInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + auto_creation_enabled=auto_creation_enabled, + auto_creation_type=auto_creation_type, + auto_creation_conversation_service_sid=auto_creation_conversation_service_sid, + auto_creation_webhook_url=auto_creation_webhook_url, + auto_creation_webhook_method=auto_creation_webhook_method, + auto_creation_webhook_filters=auto_creation_webhook_filters, + auto_creation_studio_flow_sid=auto_creation_studio_flow_sid, + auto_creation_studio_retry_count=auto_creation_studio_retry_count, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AddressConfigurationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AddressConfigurationContext + + :param version: Version that contains the resource + :param sid: The SID of the Address Configuration resource. This value can be either the `sid` or the `address` of the configuration + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Configuration/Addresses/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the AddressConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AddressConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AddressConfigurationInstance: + """ + Fetch the AddressConfigurationInstance + + + :returns: The fetched AddressConfigurationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AddressConfigurationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AddressConfigurationInstance: + """ + Asynchronous coroutine to fetch the AddressConfigurationInstance + + + :returns: The fetched AddressConfigurationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AddressConfigurationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + ) -> AddressConfigurationInstance: + """ + Update the AddressConfigurationInstance + + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + + :returns: The updated AddressConfigurationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AutoCreation.Enabled": serialize.boolean_to_string( + auto_creation_enabled + ), + "AutoCreation.Type": auto_creation_type, + "AutoCreation.ConversationServiceSid": auto_creation_conversation_service_sid, + "AutoCreation.WebhookUrl": auto_creation_webhook_url, + "AutoCreation.WebhookMethod": auto_creation_webhook_method, + "AutoCreation.WebhookFilters": serialize.map( + auto_creation_webhook_filters, lambda e: e + ), + "AutoCreation.StudioFlowSid": auto_creation_studio_flow_sid, + "AutoCreation.StudioRetryCount": auto_creation_studio_retry_count, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressConfigurationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + ) -> AddressConfigurationInstance: + """ + Asynchronous coroutine to update the AddressConfigurationInstance + + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + + :returns: The updated AddressConfigurationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AutoCreation.Enabled": serialize.boolean_to_string( + auto_creation_enabled + ), + "AutoCreation.Type": auto_creation_type, + "AutoCreation.ConversationServiceSid": auto_creation_conversation_service_sid, + "AutoCreation.WebhookUrl": auto_creation_webhook_url, + "AutoCreation.WebhookMethod": auto_creation_webhook_method, + "AutoCreation.WebhookFilters": serialize.map( + auto_creation_webhook_filters, lambda e: e + ), + "AutoCreation.StudioFlowSid": auto_creation_studio_flow_sid, + "AutoCreation.StudioRetryCount": auto_creation_studio_retry_count, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressConfigurationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AddressConfigurationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AddressConfigurationInstance: + """ + Build an instance of AddressConfigurationInstance + + :param payload: Payload response from the API + """ + return AddressConfigurationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AddressConfigurationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AddressConfigurationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Configuration/Addresses" + + def create( + self, + type: "AddressConfigurationInstance.Type", + address: str, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + address_country: Union[str, object] = values.unset, + ) -> AddressConfigurationInstance: + """ + Create the AddressConfigurationInstance + + :param type: + :param address: The unique address to be configured. The address can be a whatsapp address or phone number + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + :param address_country: An ISO 3166-1 alpha-2n country code which the address belongs to. This is currently only applicable to short code addresses. + + :returns: The created AddressConfigurationInstance + """ + + data = values.of( + { + "Type": type, + "Address": address, + "FriendlyName": friendly_name, + "AutoCreation.Enabled": serialize.boolean_to_string( + auto_creation_enabled + ), + "AutoCreation.Type": auto_creation_type, + "AutoCreation.ConversationServiceSid": auto_creation_conversation_service_sid, + "AutoCreation.WebhookUrl": auto_creation_webhook_url, + "AutoCreation.WebhookMethod": auto_creation_webhook_method, + "AutoCreation.WebhookFilters": serialize.map( + auto_creation_webhook_filters, lambda e: e + ), + "AutoCreation.StudioFlowSid": auto_creation_studio_flow_sid, + "AutoCreation.StudioRetryCount": auto_creation_studio_retry_count, + "AddressCountry": address_country, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressConfigurationInstance(self._version, payload) + + async def create_async( + self, + type: "AddressConfigurationInstance.Type", + address: str, + friendly_name: Union[str, object] = values.unset, + auto_creation_enabled: Union[bool, object] = values.unset, + auto_creation_type: Union[ + "AddressConfigurationInstance.AutoCreationType", object + ] = values.unset, + auto_creation_conversation_service_sid: Union[str, object] = values.unset, + auto_creation_webhook_url: Union[str, object] = values.unset, + auto_creation_webhook_method: Union[ + "AddressConfigurationInstance.Method", object + ] = values.unset, + auto_creation_webhook_filters: Union[List[str], object] = values.unset, + auto_creation_studio_flow_sid: Union[str, object] = values.unset, + auto_creation_studio_retry_count: Union[int, object] = values.unset, + address_country: Union[str, object] = values.unset, + ) -> AddressConfigurationInstance: + """ + Asynchronously create the AddressConfigurationInstance + + :param type: + :param address: The unique address to be configured. The address can be a whatsapp address or phone number + :param friendly_name: The human-readable name of this configuration, limited to 256 characters. Optional. + :param auto_creation_enabled: Enable/Disable auto-creating conversations for messages to this address + :param auto_creation_type: + :param auto_creation_conversation_service_sid: Conversation Service for the auto-created conversation. If not set, the conversation is created in the default service. + :param auto_creation_webhook_url: For type `webhook`, the url for the webhook request. + :param auto_creation_webhook_method: + :param auto_creation_webhook_filters: The list of events, firing webhook event for this Conversation. Values can be any of the following: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationStateUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved`, `onDeliveryUpdated` + :param auto_creation_studio_flow_sid: For type `studio`, the studio flow SID where the webhook should be sent to. + :param auto_creation_studio_retry_count: For type `studio`, number of times to retry the webhook request + :param address_country: An ISO 3166-1 alpha-2n country code which the address belongs to. This is currently only applicable to short code addresses. + + :returns: The created AddressConfigurationInstance + """ + + data = values.of( + { + "Type": type, + "Address": address, + "FriendlyName": friendly_name, + "AutoCreation.Enabled": serialize.boolean_to_string( + auto_creation_enabled + ), + "AutoCreation.Type": auto_creation_type, + "AutoCreation.ConversationServiceSid": auto_creation_conversation_service_sid, + "AutoCreation.WebhookUrl": auto_creation_webhook_url, + "AutoCreation.WebhookMethod": auto_creation_webhook_method, + "AutoCreation.WebhookFilters": serialize.map( + auto_creation_webhook_filters, lambda e: e + ), + "AutoCreation.StudioFlowSid": auto_creation_studio_flow_sid, + "AutoCreation.StudioRetryCount": auto_creation_studio_retry_count, + "AddressCountry": address_country, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AddressConfigurationInstance(self._version, payload) + + def stream( + self, + type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AddressConfigurationInstance]: + """ + Streams AddressConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(type=type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AddressConfigurationInstance]: + """ + Asynchronously streams AddressConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(type=type, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddressConfigurationInstance]: + """ + Lists AddressConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + type=type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AddressConfigurationInstance]: + """ + Asynchronously lists AddressConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + type=type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddressConfigurationPage: + """ + Retrieve a single page of AddressConfigurationInstance records from the API. + Request is executed immediately + + :param type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddressConfigurationInstance + """ + data = values.of( + { + "Type": type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AddressConfigurationPage(self._version, response) + + async def page_async( + self, + type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AddressConfigurationPage: + """ + Asynchronously retrieve a single page of AddressConfigurationInstance records from the API. + Request is executed immediately + + :param type: Filter the address configurations by its type. This value can be one of: `whatsapp`, `sms`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AddressConfigurationInstance + """ + data = values.of( + { + "Type": type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AddressConfigurationPage(self._version, response) + + def get_page(self, target_url: str) -> AddressConfigurationPage: + """ + Retrieve a specific page of AddressConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddressConfigurationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AddressConfigurationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AddressConfigurationPage: + """ + Asynchronously retrieve a specific page of AddressConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AddressConfigurationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AddressConfigurationPage(self._version, response) + + def get(self, sid: str) -> AddressConfigurationContext: + """ + Constructs a AddressConfigurationContext + + :param sid: The SID of the Address Configuration resource. This value can be either the `sid` or the `address` of the configuration + """ + return AddressConfigurationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AddressConfigurationContext: + """ + Constructs a AddressConfigurationContext + + :param sid: The SID of the Address Configuration resource. This value can be either the `sid` or the `address` of the configuration + """ + return AddressConfigurationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__init__.py new file mode 100644 index 0000000..46f511a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__init__.py @@ -0,0 +1,313 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.conversations.v1.configuration.webhook import WebhookList + + +class ConfigurationInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this configuration. + :ivar default_chat_service_sid: The SID of the default [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) used when creating a conversation. + :ivar default_messaging_service_sid: The SID of the default [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) used when creating a conversation. + :ivar default_inactive_timer: Default ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :ivar default_closed_timer: Default ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :ivar url: An absolute API resource URL for this global configuration. + :ivar links: Contains absolute API resource URLs to access the webhook and default service configurations. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.default_chat_service_sid: Optional[str] = payload.get( + "default_chat_service_sid" + ) + self.default_messaging_service_sid: Optional[str] = payload.get( + "default_messaging_service_sid" + ) + self.default_inactive_timer: Optional[str] = payload.get( + "default_inactive_timer" + ) + self.default_closed_timer: Optional[str] = payload.get("default_closed_timer") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._context: Optional[ConfigurationContext] = None + + @property + def _proxy(self) -> "ConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConfigurationContext for this ConfigurationInstance + """ + if self._context is None: + self._context = ConfigurationContext( + self._version, + ) + return self._context + + def fetch(self) -> "ConfigurationInstance": + """ + Fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConfigurationInstance": + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + default_chat_service_sid: Union[str, object] = values.unset, + default_messaging_service_sid: Union[str, object] = values.unset, + default_inactive_timer: Union[str, object] = values.unset, + default_closed_timer: Union[str, object] = values.unset, + ) -> "ConfigurationInstance": + """ + Update the ConfigurationInstance + + :param default_chat_service_sid: The SID of the default [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to use when creating a conversation. + :param default_messaging_service_sid: The SID of the default [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to use when creating a conversation. + :param default_inactive_timer: Default ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param default_closed_timer: Default ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + + :returns: The updated ConfigurationInstance + """ + return self._proxy.update( + default_chat_service_sid=default_chat_service_sid, + default_messaging_service_sid=default_messaging_service_sid, + default_inactive_timer=default_inactive_timer, + default_closed_timer=default_closed_timer, + ) + + async def update_async( + self, + default_chat_service_sid: Union[str, object] = values.unset, + default_messaging_service_sid: Union[str, object] = values.unset, + default_inactive_timer: Union[str, object] = values.unset, + default_closed_timer: Union[str, object] = values.unset, + ) -> "ConfigurationInstance": + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param default_chat_service_sid: The SID of the default [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to use when creating a conversation. + :param default_messaging_service_sid: The SID of the default [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to use when creating a conversation. + :param default_inactive_timer: Default ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param default_closed_timer: Default ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + + :returns: The updated ConfigurationInstance + """ + return await self._proxy.update_async( + default_chat_service_sid=default_chat_service_sid, + default_messaging_service_sid=default_messaging_service_sid, + default_inactive_timer=default_inactive_timer, + default_closed_timer=default_closed_timer, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ConfigurationContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the ConfigurationContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Configuration" + + def fetch(self) -> ConfigurationInstance: + """ + Fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConfigurationInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> ConfigurationInstance: + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConfigurationInstance( + self._version, + payload, + ) + + def update( + self, + default_chat_service_sid: Union[str, object] = values.unset, + default_messaging_service_sid: Union[str, object] = values.unset, + default_inactive_timer: Union[str, object] = values.unset, + default_closed_timer: Union[str, object] = values.unset, + ) -> ConfigurationInstance: + """ + Update the ConfigurationInstance + + :param default_chat_service_sid: The SID of the default [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to use when creating a conversation. + :param default_messaging_service_sid: The SID of the default [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to use when creating a conversation. + :param default_inactive_timer: Default ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param default_closed_timer: Default ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + + :returns: The updated ConfigurationInstance + """ + data = values.of( + { + "DefaultChatServiceSid": default_chat_service_sid, + "DefaultMessagingServiceSid": default_messaging_service_sid, + "DefaultInactiveTimer": default_inactive_timer, + "DefaultClosedTimer": default_closed_timer, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConfigurationInstance(self._version, payload) + + async def update_async( + self, + default_chat_service_sid: Union[str, object] = values.unset, + default_messaging_service_sid: Union[str, object] = values.unset, + default_inactive_timer: Union[str, object] = values.unset, + default_closed_timer: Union[str, object] = values.unset, + ) -> ConfigurationInstance: + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param default_chat_service_sid: The SID of the default [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to use when creating a conversation. + :param default_messaging_service_sid: The SID of the default [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to use when creating a conversation. + :param default_inactive_timer: Default ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param default_closed_timer: Default ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + + :returns: The updated ConfigurationInstance + """ + data = values.of( + { + "DefaultChatServiceSid": default_chat_service_sid, + "DefaultMessagingServiceSid": default_messaging_service_sid, + "DefaultInactiveTimer": default_inactive_timer, + "DefaultClosedTimer": default_closed_timer, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConfigurationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ConfigurationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ConfigurationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._webhooks: Optional[WebhookList] = None + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList(self._version) + return self._webhooks + + def get(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext(self._version) + + def __call__(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f59275e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..ca49090 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/webhook.py new file mode 100644 index 0000000..4ece542 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/configuration/webhook.py @@ -0,0 +1,315 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + class Target(object): + WEBHOOK = "webhook" + FLEX = "flex" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar method: + :ivar filters: The list of webhook event triggers that are enabled for this Service: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved` + :ivar pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :ivar post_webhook_url: The absolute url the post-event webhook request should be sent to. + :ivar target: + :ivar url: An absolute API resource API resource URL for this webhook. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.method: Optional["WebhookInstance.Method"] = payload.get("method") + self.filters: Optional[List[str]] = payload.get("filters") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.target: Optional["WebhookInstance.Target"] = payload.get("target") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + ) + return self._context + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + method: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + target: Union["WebhookInstance.Target", object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param method: The HTTP method to be used when sending a webhook request. + :param filters: The list of webhook event triggers that are enabled for this Service: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved` + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param target: + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + method=method, + filters=filters, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + target=target, + ) + + async def update_async( + self, + method: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + target: Union["WebhookInstance.Target", object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param method: The HTTP method to be used when sending a webhook request. + :param filters: The list of webhook event triggers that are enabled for this Service: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved` + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param target: + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + method=method, + filters=filters, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + target=target, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Configuration/Webhooks" + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + ) + + def update( + self, + method: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + target: Union["WebhookInstance.Target", object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param method: The HTTP method to be used when sending a webhook request. + :param filters: The list of webhook event triggers that are enabled for this Service: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved` + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param target: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Method": method, + "Filters": serialize.map(filters, lambda e: e), + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "Target": target, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance(self._version, payload) + + async def update_async( + self, + method: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + target: Union["WebhookInstance.Target", object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param method: The HTTP method to be used when sending a webhook request. + :param filters: The list of webhook event triggers that are enabled for this Service: `onMessageAdded`, `onMessageUpdated`, `onMessageRemoved`, `onConversationUpdated`, `onConversationRemoved`, `onParticipantAdded`, `onParticipantUpdated`, `onParticipantRemoved` + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param target: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Method": method, + "Filters": serialize.map(filters, lambda e: e), + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "Target": target, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> WebhookContext: + """ + Constructs a WebhookContext + + """ + return WebhookContext(self._version) + + def __call__(self) -> WebhookContext: + """ + Constructs a WebhookContext + + """ + return WebhookContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__init__.py new file mode 100644 index 0000000..4c9b880 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__init__.py @@ -0,0 +1,977 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.conversation.message import MessageList +from twilio.rest.conversations.v1.conversation.participant import ParticipantList +from twilio.rest.conversations.v1.conversation.webhook import WebhookList + + +class ConversationInstance(InstanceResource): + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar state: + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar timers: Timer date values representing state update for this conversation. + :ivar url: An absolute API resource URL for this conversation. + :ivar links: Contains absolute URLs to access the [participants](https://www.twilio.com/docs/conversations/api/conversation-participant-resource), [messages](https://www.twilio.com/docs/conversations/api/conversation-message-resource) and [webhooks](https://www.twilio.com/docs/conversations/api/conversation-scoped-webhook-resource) of this conversation. + :ivar bindings: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.state: Optional["ConversationInstance.State"] = payload.get("state") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.timers: Optional[Dict[str, object]] = payload.get("timers") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.bindings: Optional[Dict[str, object]] = payload.get("bindings") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ConversationContext] = None + + @property + def _proxy(self) -> "ConversationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConversationContext for this ConversationInstance + """ + if self._context is None: + self._context = ConversationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ConversationInstance": + """ + Fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConversationInstance": + """ + Asynchronous coroutine to fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> "ConversationInstance": + """ + Update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + messaging_service_sid=messaging_service_sid, + state=state, + timers_inactive=timers_inactive, + timers_closed=timers_closed, + unique_name=unique_name, + bindings_email_address=bindings_email_address, + bindings_email_name=bindings_email_name, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> "ConversationInstance": + """ + Asynchronous coroutine to update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + messaging_service_sid=messaging_service_sid, + state=state, + timers_inactive=timers_inactive, + timers_closed=timers_closed, + unique_name=unique_name, + bindings_email_address=bindings_email_address, + bindings_email_name=bindings_email_name, + ) + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + return self._proxy.webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConversationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ConversationContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Conversations/{sid}".format(**self._solution) + + self._messages: Optional[MessageList] = None + self._participants: Optional[ParticipantList] = None + self._webhooks: Optional[WebhookList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ConversationInstance: + """ + Fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConversationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConversationInstance: + """ + Asynchronous coroutine to fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConversationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "UniqueName": unique_name, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Asynchronous coroutine to update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "UniqueName": unique_name, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["sid"], + ) + return self._messages + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["sid"], + ) + return self._participants + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, + self._solution["sid"], + ) + return self._webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConversationInstance: + """ + Build an instance of ConversationInstance + + :param payload: Payload response from the API + """ + return ConversationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConversationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ConversationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Conversations" + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Create the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The created ConversationInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "MessagingServiceSid": messaging_service_sid, + "Attributes": attributes, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance(self._version, payload) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Asynchronously create the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The created ConversationInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "MessagingServiceSid": messaging_service_sid, + "Attributes": attributes, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance(self._version, payload) + + def stream( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConversationInstance]: + """ + Streams ConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + start_date=start_date, + end_date=end_date, + state=state, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConversationInstance]: + """ + Asynchronously streams ConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + start_date=start_date, + end_date=end_date, + state=state, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConversationInstance]: + """ + Lists ConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + start_date=start_date, + end_date=end_date, + state=state, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConversationInstance]: + """ + Asynchronously lists ConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + start_date=start_date, + end_date=end_date, + state=state, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConversationPage: + """ + Retrieve a single page of ConversationInstance records from the API. + Request is executed immediately + + :param start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConversationInstance + """ + data = values.of( + { + "StartDate": start_date, + "EndDate": end_date, + "State": state, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConversationPage(self._version, response) + + async def page_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConversationPage: + """ + Asynchronously retrieve a single page of ConversationInstance records from the API. + Request is executed immediately + + :param start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConversationInstance + """ + data = values.of( + { + "StartDate": start_date, + "EndDate": end_date, + "State": state, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConversationPage(self._version, response) + + def get_page(self, target_url: str) -> ConversationPage: + """ + Retrieve a specific page of ConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConversationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ConversationPage: + """ + Asynchronously retrieve a specific page of ConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConversationPage(self._version, response) + + def get(self, sid: str) -> ConversationContext: + """ + Constructs a ConversationContext + + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + return ConversationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ConversationContext: + """ + Constructs a ConversationContext + + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + return ConversationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7715e70 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/participant.cpython-310.pyc new file mode 100644 index 0000000..b5f4052 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..fa95446 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__init__.py new file mode 100644 index 0000000..e3e9dea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__init__.py @@ -0,0 +1,864 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.conversation.message.delivery_receipt import ( + DeliveryReceiptList, +) + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this message. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar index: The index of the message within the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource). Indices may skip numbers, but will always be in order of when the message was received. + :ivar author: The channel specific identifier of the message's author. Defaults to `system`. + :ivar body: The content of the message, can be up to 1,600 characters long. + :ivar media: An array of objects that describe the Message's media, if the message contains media. Each object contains these fields: `content_type` with the MIME type of the media, `filename` with the name of the media, `sid` with the SID of the Media resource, and `size` with the media object's file size in bytes. If the Message has no media, this value is `null`. + :ivar attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar participant_sid: The unique ID of messages's author participant. Null in case of `system` sent message. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :ivar url: An absolute API resource API URL for this message. + :ivar delivery: An object that contains the summary of delivery statuses for the message to non-chat participants. + :ivar links: Contains an absolute API resource URL to access the delivery & read receipts of this message. + :ivar content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.sid: Optional[str] = payload.get("sid") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.author: Optional[str] = payload.get("author") + self.body: Optional[str] = payload.get("body") + self.media: Optional[List[Dict[str, object]]] = payload.get("media") + self.attributes: Optional[str] = payload.get("attributes") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.delivery: Optional[Dict[str, object]] = payload.get("delivery") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.content_sid: Optional[str] = payload.get("content_sid") + + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + author=author, + body=body, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + subject=subject, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + author=author, + body=body, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + subject=subject, + ) + + @property + def delivery_receipts(self) -> DeliveryReceiptList: + """ + Access the delivery_receipts + """ + return self._proxy.delivery_receipts + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, conversation_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Conversations/{conversation_sid}/Messages/{sid}".format( + **self._solution + ) + + self._delivery_receipts: Optional[DeliveryReceiptList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + @property + def delivery_receipts(self) -> DeliveryReceiptList: + """ + Access the delivery_receipts + """ + if self._delivery_receipts is None: + self._delivery_receipts = DeliveryReceiptList( + self._version, + self._solution["conversation_sid"], + self._solution["sid"], + ) + return self._delivery_receipts + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, conversation_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for messages. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + } + self._uri = "/Conversations/{conversation_sid}/Messages".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + content_sid: Union[str, object] = values.unset, + content_variables: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param media_sid: The Media SID to be attached to the new Message. + :param content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template, required for template-generated messages. **Note** that if this field is set, `Body` and `MediaSid` parameters are ignored. + :param content_variables: A structurally valid JSON string that contains values to resolve Rich Content template variables. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MediaSid": media_sid, + "ContentSid": content_sid, + "ContentVariables": content_variables, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + content_sid: Union[str, object] = values.unset, + content_variables: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param media_sid: The Media SID to be attached to the new Message. + :param content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template, required for template-generated messages. **Note** that if this field is set, `Body` and `MediaSid` parameters are ignored. + :param content_variables: A structurally valid JSON string that contains values to resolve Rich Content template variables. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MediaSid": media_sid, + "ContentSid": content_sid, + "ContentVariables": content_variables, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return MessageContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return MessageContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6c0d177 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc new file mode 100644 index 0000000..0c9dfff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/delivery_receipt.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/delivery_receipt.py new file mode 100644 index 0000000..16af789 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/message/delivery_receipt.py @@ -0,0 +1,468 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeliveryReceiptInstance(InstanceResource): + + class DeliveryStatus(object): + READ = "read" + FAILED = "failed" + DELIVERED = "delivered" + UNDELIVERED = "undelivered" + SENT = "sent" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this participant. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to + :ivar channel_message_sid: A messaging channel-specific identifier for the message delivered to participant e.g. `SMxx` for SMS, `WAxx` for Whatsapp etc. + :ivar participant_sid: The unique ID of the participant the delivery receipt belongs to. + :ivar status: + :ivar error_code: The message [delivery error code](https://www.twilio.com/docs/sms/api/message-resource#delivery-related-errors) for a `failed` status, + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. `null` if the delivery receipt has not been updated. + :ivar url: An absolute API resource URL for this delivery receipt. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conversation_sid: str, + message_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.sid: Optional[str] = payload.get("sid") + self.message_sid: Optional[str] = payload.get("message_sid") + self.channel_message_sid: Optional[str] = payload.get("channel_message_sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.status: Optional["DeliveryReceiptInstance.DeliveryStatus"] = payload.get( + "status" + ) + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "conversation_sid": conversation_sid, + "message_sid": message_sid, + "sid": sid or self.sid, + } + self._context: Optional[DeliveryReceiptContext] = None + + @property + def _proxy(self) -> "DeliveryReceiptContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeliveryReceiptContext for this DeliveryReceiptInstance + """ + if self._context is None: + self._context = DeliveryReceiptContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "DeliveryReceiptInstance": + """ + Fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeliveryReceiptInstance": + """ + Asynchronous coroutine to fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeliveryReceiptContext(InstanceContext): + + def __init__( + self, version: Version, conversation_sid: str, message_sid: str, sid: str + ): + """ + Initialize the DeliveryReceiptContext + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + "message_sid": message_sid, + "sid": sid, + } + self._uri = "/Conversations/{conversation_sid}/Messages/{message_sid}/Receipts/{sid}".format( + **self._solution + ) + + def fetch(self) -> DeliveryReceiptInstance: + """ + Fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeliveryReceiptInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeliveryReceiptInstance: + """ + Asynchronous coroutine to fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeliveryReceiptInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeliveryReceiptPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeliveryReceiptInstance: + """ + Build an instance of DeliveryReceiptInstance + + :param payload: Payload response from the API + """ + return DeliveryReceiptInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeliveryReceiptList(ListResource): + + def __init__(self, version: Version, conversation_sid: str, message_sid: str): + """ + Initialize the DeliveryReceiptList + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + "message_sid": message_sid, + } + self._uri = ( + "/Conversations/{conversation_sid}/Messages/{message_sid}/Receipts".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeliveryReceiptInstance]: + """ + Streams DeliveryReceiptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeliveryReceiptInstance]: + """ + Asynchronously streams DeliveryReceiptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeliveryReceiptInstance]: + """ + Lists DeliveryReceiptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeliveryReceiptInstance]: + """ + Asynchronously lists DeliveryReceiptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeliveryReceiptPage: + """ + Retrieve a single page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeliveryReceiptInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeliveryReceiptPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeliveryReceiptPage: + """ + Asynchronously retrieve a single page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeliveryReceiptInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeliveryReceiptPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeliveryReceiptPage: + """ + Retrieve a specific page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeliveryReceiptInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeliveryReceiptPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeliveryReceiptPage: + """ + Asynchronously retrieve a specific page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeliveryReceiptInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeliveryReceiptPage(self._version, response, self._solution) + + def get(self, sid: str) -> DeliveryReceiptContext: + """ + Constructs a DeliveryReceiptContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return DeliveryReceiptContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> DeliveryReceiptContext: + """ + Constructs a DeliveryReceiptContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return DeliveryReceiptContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/participant.py new file mode 100644 index 0000000..499bdbf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/participant.py @@ -0,0 +1,847 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this participant. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this participant. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar messaging_binding: Information about how this participant exchanges messages with the conversation. A JSON parameter consisting of type and address fields of the participant. + :ivar role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar url: An absolute API resource URL for this participant. + :ivar last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :ivar last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.sid: Optional[str] = payload.get("sid") + self.identity: Optional[str] = payload.get("identity") + self.attributes: Optional[str] = payload.get("attributes") + self.messaging_binding: Optional[Dict[str, object]] = payload.get( + "messaging_binding" + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.last_read_message_index: Optional[int] = deserialize.integer( + payload.get("last_read_message_index") + ) + self.last_read_timestamp: Optional[str] = payload.get("last_read_timestamp") + + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + role_sid=role_sid, + messaging_binding_proxy_address=messaging_binding_proxy_address, + messaging_binding_projected_address=messaging_binding_projected_address, + identity=identity, + last_read_message_index=last_read_message_index, + last_read_timestamp=last_read_timestamp, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Asynchronous coroutine to update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + role_sid=role_sid, + messaging_binding_proxy_address=messaging_binding_proxy_address, + messaging_binding_projected_address=messaging_binding_projected_address, + identity=identity, + last_read_message_index=last_read_message_index, + last_read_timestamp=last_read_timestamp, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__(self, version: Version, conversation_sid: str, sid: str): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this participant. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Conversations/{conversation_sid}/Participants/{sid}".format( + **self._solution + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "RoleSid": role_sid, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "Identity": identity, + "LastReadMessageIndex": last_read_message_index, + "LastReadTimestamp": last_read_timestamp, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronous coroutine to update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "RoleSid": role_sid, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "Identity": identity, + "LastReadMessageIndex": last_read_message_index, + "LastReadTimestamp": last_read_timestamp, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, conversation_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for participants. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + } + self._uri = "/Conversations/{conversation_sid}/Participants".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + identity: Union[str, object] = values.unset, + messaging_binding_address: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Create the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param messaging_binding_address: The address of the participant's device, e.g. a phone or WhatsApp number. Together with the Proxy address, this determines a participant uniquely. This field (with proxy_address) is only null when the participant is interacting from an SDK endpoint (see the 'identity' field). + :param messaging_binding_proxy_address: The address of the Twilio phone number (or WhatsApp number) that the participant is in contact with. This field, together with participant address, is only null when the participant is interacting from an SDK endpoint (see the 'identity' field). + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. Communication mask for the Conversation participant with Identity. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identity": identity, + "MessagingBinding.Address": messaging_binding_address, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + identity: Union[str, object] = values.unset, + messaging_binding_address: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronously create the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param messaging_binding_address: The address of the participant's device, e.g. a phone or WhatsApp number. Together with the Proxy address, this determines a participant uniquely. This field (with proxy_address) is only null when the participant is interacting from an SDK endpoint (see the 'identity' field). + :param messaging_binding_proxy_address: The address of the Twilio phone number (or WhatsApp number) that the participant is in contact with. This field, together with participant address, is only null when the participant is interacting from an SDK endpoint (see the 'identity' field). + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. Communication mask for the Conversation participant with Identity. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identity": identity, + "MessagingBinding.Address": messaging_binding_address, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ParticipantContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ParticipantContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/webhook.py new file mode 100644 index 0000000..6238879 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/conversation/webhook.py @@ -0,0 +1,728 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + class Target(object): + WEBHOOK = "webhook" + TRIGGER = "trigger" + STUDIO = "studio" + + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + :ivar target: The target of this webhook: `webhook`, `studio`, `trigger` + :ivar url: An absolute API resource URL for this webhook. + :ivar configuration: The configuration of this webhook. Is defined based on target. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.target: Optional[str] = payload.get("target") + self.url: Optional[str] = payload.get("url") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version, conversation_sid: str, sid: str): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Conversations/{conversation_sid}/Webhooks/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebhookInstance: + """ + Build an instance of WebhookInstance + + :param payload: Payload response from the API + """ + return WebhookInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version, conversation_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conversation_sid": conversation_sid, + } + self._uri = "/Conversations/{conversation_sid}/Webhooks".format( + **self._solution + ) + + def create( + self, + target: "WebhookInstance.Target", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_replay_after: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Create the WebhookInstance + + :param target: + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + :param configuration_replay_after: The message index for which and it's successors the webhook will be replayed. Not set by default + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Target": target, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.ReplayAfter": configuration_replay_after, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + async def create_async( + self, + target: "WebhookInstance.Target", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_replay_after: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronously create the WebhookInstance + + :param target: + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + :param configuration_replay_after: The message index for which and it's successors the webhook will be replayed. Not set by default + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Target": target, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.ReplayAfter": configuration_replay_after, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, conversation_sid=self._solution["conversation_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebhookInstance]: + """ + Streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebhookInstance]: + """ + Asynchronously streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Asynchronously lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebhookPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Asynchronously retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebhookPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WebhookPage: + """ + Retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WebhookPage: + """ + Asynchronously retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + def get(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return WebhookContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __call__(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return WebhookContext( + self._version, conversation_sid=self._solution["conversation_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/credential.py new file mode 100644 index 0000000..120710f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/credential.py @@ -0,0 +1,693 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushType(object): + APN = "apn" + GCM = "gcm" + FCM = "fcm" + + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this credential. + :ivar friendly_name: The human-readable name of this credential, limited to 64 characters. Optional. + :ivar type: + :ivar sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar url: An absolute API resource URL for this credential. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushType"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + type: Union["CredentialInstance.PushType", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + type=type, + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + type: Union["CredentialInstance.PushType", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + type=type, + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + type: Union["CredentialInstance.PushType", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + type: Union["CredentialInstance.PushType", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushType", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushType", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL encoded representation of the certificate. For example, `-----BEGIN CERTIFICATE----- MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEF.....A== -----END CERTIFICATE-----`. + :param private_key: [APN only] The URL encoded representation of the private key. For example, `-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fG... -----END RSA PRIVATE KEY-----`. + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The API key for the project that was obtained from the Google Developer console for your GCM Service application credential. + :param secret: [FCM only] The **Server key** of your project from the Firebase console, found under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/participant_conversation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/participant_conversation.py new file mode 100644 index 0000000..7413047 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/participant_conversation.py @@ -0,0 +1,356 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantConversationInstance(InstanceResource): + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar participant_sid: The unique ID of the [Participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource). + :ivar participant_user_sid: The unique string that identifies the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). + :ivar participant_identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :ivar participant_messaging_binding: Information about how this participant exchanges messages with the conversation. A JSON parameter consisting of type and address fields of the participant. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) this Participant belongs to. + :ivar conversation_unique_name: An application-defined string that uniquely identifies the Conversation resource. + :ivar conversation_friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar conversation_attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar conversation_date_created: The date that this conversation was created, given in ISO 8601 format. + :ivar conversation_date_updated: The date that this conversation was last updated, given in ISO 8601 format. + :ivar conversation_created_by: Identity of the creator of this Conversation. + :ivar conversation_state: + :ivar conversation_timers: Timer date values representing state update for this conversation. + :ivar links: Contains absolute URLs to access the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) and [conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) of this conversation. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.participant_user_sid: Optional[str] = payload.get("participant_user_sid") + self.participant_identity: Optional[str] = payload.get("participant_identity") + self.participant_messaging_binding: Optional[Dict[str, object]] = payload.get( + "participant_messaging_binding" + ) + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.conversation_unique_name: Optional[str] = payload.get( + "conversation_unique_name" + ) + self.conversation_friendly_name: Optional[str] = payload.get( + "conversation_friendly_name" + ) + self.conversation_attributes: Optional[str] = payload.get( + "conversation_attributes" + ) + self.conversation_date_created: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("conversation_date_created")) + ) + self.conversation_date_updated: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("conversation_date_updated")) + ) + self.conversation_created_by: Optional[str] = payload.get( + "conversation_created_by" + ) + self.conversation_state: Optional["ParticipantConversationInstance.State"] = ( + payload.get("conversation_state") + ) + self.conversation_timers: Optional[Dict[str, object]] = payload.get( + "conversation_timers" + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ParticipantConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantConversationInstance: + """ + Build an instance of ParticipantConversationInstance + + :param payload: Payload response from the API + """ + return ParticipantConversationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantConversationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ParticipantConversationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ParticipantConversations" + + def stream( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantConversationInstance]: + """ + Streams ParticipantConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + identity=identity, address=address, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantConversationInstance]: + """ + Asynchronously streams ParticipantConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + identity=identity, address=address, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantConversationInstance]: + """ + Lists ParticipantConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + address=address, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantConversationInstance]: + """ + Asynchronously lists ParticipantConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + address=address, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantConversationPage: + """ + Retrieve a single page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantConversationInstance + """ + data = values.of( + { + "Identity": identity, + "Address": address, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantConversationPage(self._version, response) + + async def page_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantConversationPage: + """ + Asynchronously retrieve a single page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantConversationInstance + """ + data = values.of( + { + "Identity": identity, + "Address": address, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantConversationPage(self._version, response) + + def get_page(self, target_url: str) -> ParticipantConversationPage: + """ + Retrieve a specific page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantConversationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ParticipantConversationPage: + """ + Asynchronously retrieve a specific page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantConversationPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/role.py new file mode 100644 index 0000000..b5d4d2c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/role.py @@ -0,0 +1,580 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CONVERSATION = "conversation" + SERVICE = "service" + + """ + :ivar sid: The unique string that we created to identify the Role resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Role resource. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Role resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar permissions: An array of the permissions the role has been granted. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: An absolute API resource URL for this user role. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param sid: The SID of the Role resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RoleList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Roles" + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance(self._version, payload) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__init__.py new file mode 100644 index 0000000..2a9f120 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__init__.py @@ -0,0 +1,620 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.service.binding import BindingList +from twilio.rest.conversations.v1.service.configuration import ConfigurationList +from twilio.rest.conversations.v1.service.conversation import ConversationList +from twilio.rest.conversations.v1.service.participant_conversation import ( + ParticipantConversationList, +) +from twilio.rest.conversations.v1.service.role import RoleList +from twilio.rest.conversations.v1.service.user import UserList + + +class ServiceInstance(InstanceResource): + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this service. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar friendly_name: The human-readable name of this service, limited to 256 characters. Optional. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar url: An absolute API resource URL for this service. + :ivar links: Contains absolute API resource URLs to access conversations, users, roles, bindings and configuration of this service. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + return self._proxy.bindings + + @property + def configuration(self) -> ConfigurationList: + """ + Access the configuration + """ + return self._proxy.configuration + + @property + def conversations(self) -> ConversationList: + """ + Access the conversations + """ + return self._proxy.conversations + + @property + def participant_conversations(self) -> ParticipantConversationList: + """ + Access the participant_conversations + """ + return self._proxy.participant_conversations + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + return self._proxy.roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + return self._proxy.users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._bindings: Optional[BindingList] = None + self._configuration: Optional[ConfigurationList] = None + self._conversations: Optional[ConversationList] = None + self._participant_conversations: Optional[ParticipantConversationList] = None + self._roles: Optional[RoleList] = None + self._users: Optional[UserList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + if self._bindings is None: + self._bindings = BindingList( + self._version, + self._solution["sid"], + ) + return self._bindings + + @property + def configuration(self) -> ConfigurationList: + """ + Access the configuration + """ + if self._configuration is None: + self._configuration = ConfigurationList( + self._version, + self._solution["sid"], + ) + return self._configuration + + @property + def conversations(self) -> ConversationList: + """ + Access the conversations + """ + if self._conversations is None: + self._conversations = ConversationList( + self._version, + self._solution["sid"], + ) + return self._conversations + + @property + def participant_conversations(self) -> ParticipantConversationList: + """ + Access the participant_conversations + """ + if self._participant_conversations is None: + self._participant_conversations = ParticipantConversationList( + self._version, + self._solution["sid"], + ) + return self._participant_conversations + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + if self._roles is None: + self._roles = RoleList( + self._version, + self._solution["sid"], + ) + return self._roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution["sid"], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create(self, friendly_name: str) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: The human-readable name of this service, limited to 256 characters. Optional. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async(self, friendly_name: str) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: The human-readable name of this service, limited to 256 characters. Optional. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..13adfee Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/binding.cpython-310.pyc new file mode 100644 index 0000000..08f9fba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/participant_conversation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/participant_conversation.cpython-310.pyc new file mode 100644 index 0000000..e396cc3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/participant_conversation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..8a8016b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/binding.py new file mode 100644 index 0000000..6e66844 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/binding.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BindingInstance(InstanceResource): + + class BindingType(object): + APN = "apn" + GCM = "gcm" + FCM = "fcm" + + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this binding. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Binding resource is associated with. + :ivar credential_sid: The SID of the [Credential](https://www.twilio.com/docs/conversations/api/credential-resource) for the binding. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar endpoint: The unique endpoint identifier for the Binding. The format of this value depends on the `binding_type`. + :ivar identity: The application-defined string that uniquely identifies the [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more info. + :ivar binding_type: + :ivar message_types: The [Conversation message types](https://www.twilio.com/docs/chat/push-notification-configuration#push-types) the binding is subscribed to. + :ivar url: An absolute API resource URL for this binding. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.binding_type: Optional["BindingInstance.BindingType"] = payload.get( + "binding_type" + ) + self.message_types: Optional[List[str]] = payload.get("message_types") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[BindingContext] = None + + @property + def _proxy(self) -> "BindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BindingContext for this BindingInstance + """ + if self._context is None: + self._context = BindingContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BindingInstance": + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BindingInstance": + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str, sid: str): + """ + Initialize the BindingContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Binding resource is associated with. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Bindings/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BindingInstance: + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BindingInstance: + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BindingInstance: + """ + Build an instance of BindingInstance + + :param payload: Payload response from the API + """ + return BindingInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BindingList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the BindingList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Binding resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Bindings".format(**self._solution) + + def stream( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BindingInstance]: + """ + Streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BindingInstance]: + """ + Asynchronously streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Asynchronously lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param List[str] identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BindingPage(self._version, response, self._solution) + + async def page_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Asynchronously retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: The push technology used by the Binding resources to read. Can be: `apn`, `gcm`, or `fcm`. See [push notification configuration](https://www.twilio.com/docs/chat/push-notification-configuration) for more info. + :param identity: The identity of a [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource) this binding belongs to. See [access tokens](https://www.twilio.com/docs/conversations/create-tokens) for more details. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BindingPage: + """ + Retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BindingPage: + """ + Asynchronously retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return BindingContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return BindingContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__init__.py new file mode 100644 index 0000000..9ca23a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__init__.py @@ -0,0 +1,363 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.conversations.v1.service.configuration.notification import ( + NotificationList, +) +from twilio.rest.conversations.v1.service.configuration.webhook import WebhookList + + +class ConfigurationInstance(InstanceResource): + """ + :ivar chat_service_sid: The unique string that we created to identify the Service configuration resource. + :ivar default_conversation_creator_role_sid: The conversation-level role assigned to a conversation creator when they join a new conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :ivar default_conversation_role_sid: The conversation-level role assigned to users when they are added to a conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :ivar default_chat_service_role_sid: The service-level role assigned to users when they are added to the service. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :ivar url: An absolute API resource URL for this service configuration. + :ivar links: Contains an absolute API resource URL to access the push notifications configuration of this service. + :ivar reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/conversations/reachability) is enabled for this Conversations Service. The default is `false`. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], chat_service_sid: str + ): + super().__init__(version) + + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.default_conversation_creator_role_sid: Optional[str] = payload.get( + "default_conversation_creator_role_sid" + ) + self.default_conversation_role_sid: Optional[str] = payload.get( + "default_conversation_role_sid" + ) + self.default_chat_service_role_sid: Optional[str] = payload.get( + "default_chat_service_role_sid" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.reachability_enabled: Optional[bool] = payload.get("reachability_enabled") + + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._context: Optional[ConfigurationContext] = None + + @property + def _proxy(self) -> "ConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConfigurationContext for this ConfigurationInstance + """ + if self._context is None: + self._context = ConfigurationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + ) + return self._context + + def fetch(self) -> "ConfigurationInstance": + """ + Fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConfigurationInstance": + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + default_conversation_creator_role_sid: Union[str, object] = values.unset, + default_conversation_role_sid: Union[str, object] = values.unset, + default_chat_service_role_sid: Union[str, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + ) -> "ConfigurationInstance": + """ + Update the ConfigurationInstance + + :param default_conversation_creator_role_sid: The conversation-level role assigned to a conversation creator when they join a new conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_conversation_role_sid: The conversation-level role assigned to users when they are added to a conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_chat_service_role_sid: The service-level role assigned to users when they are added to the service. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/conversations/reachability) is enabled for this Conversations Service. The default is `false`. + + :returns: The updated ConfigurationInstance + """ + return self._proxy.update( + default_conversation_creator_role_sid=default_conversation_creator_role_sid, + default_conversation_role_sid=default_conversation_role_sid, + default_chat_service_role_sid=default_chat_service_role_sid, + reachability_enabled=reachability_enabled, + ) + + async def update_async( + self, + default_conversation_creator_role_sid: Union[str, object] = values.unset, + default_conversation_role_sid: Union[str, object] = values.unset, + default_chat_service_role_sid: Union[str, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + ) -> "ConfigurationInstance": + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param default_conversation_creator_role_sid: The conversation-level role assigned to a conversation creator when they join a new conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_conversation_role_sid: The conversation-level role assigned to users when they are added to a conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_chat_service_role_sid: The service-level role assigned to users when they are added to the service. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/conversations/reachability) is enabled for this Conversations Service. The default is `false`. + + :returns: The updated ConfigurationInstance + """ + return await self._proxy.update_async( + default_conversation_creator_role_sid=default_conversation_creator_role_sid, + default_conversation_role_sid=default_conversation_role_sid, + default_chat_service_role_sid=default_chat_service_role_sid, + reachability_enabled=reachability_enabled, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConfigurationContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the ConfigurationContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the Service configuration resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Configuration".format( + **self._solution + ) + + def fetch(self) -> ConfigurationInstance: + """ + Fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConfigurationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + async def fetch_async(self) -> ConfigurationInstance: + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + + :returns: The fetched ConfigurationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConfigurationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + def update( + self, + default_conversation_creator_role_sid: Union[str, object] = values.unset, + default_conversation_role_sid: Union[str, object] = values.unset, + default_chat_service_role_sid: Union[str, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + ) -> ConfigurationInstance: + """ + Update the ConfigurationInstance + + :param default_conversation_creator_role_sid: The conversation-level role assigned to a conversation creator when they join a new conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_conversation_role_sid: The conversation-level role assigned to users when they are added to a conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_chat_service_role_sid: The service-level role assigned to users when they are added to the service. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/conversations/reachability) is enabled for this Conversations Service. The default is `false`. + + :returns: The updated ConfigurationInstance + """ + data = values.of( + { + "DefaultConversationCreatorRoleSid": default_conversation_creator_role_sid, + "DefaultConversationRoleSid": default_conversation_role_sid, + "DefaultChatServiceRoleSid": default_chat_service_role_sid, + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConfigurationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def update_async( + self, + default_conversation_creator_role_sid: Union[str, object] = values.unset, + default_conversation_role_sid: Union[str, object] = values.unset, + default_chat_service_role_sid: Union[str, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + ) -> ConfigurationInstance: + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param default_conversation_creator_role_sid: The conversation-level role assigned to a conversation creator when they join a new conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_conversation_role_sid: The conversation-level role assigned to users when they are added to a conversation. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param default_chat_service_role_sid: The service-level role assigned to users when they are added to the service. See [Conversation Role](https://www.twilio.com/docs/conversations/api/role-resource) for more info about roles. + :param reachability_enabled: Whether the [Reachability Indicator](https://www.twilio.com/docs/conversations/reachability) is enabled for this Conversations Service. The default is `false`. + + :returns: The updated ConfigurationInstance + """ + data = values.of( + { + "DefaultConversationCreatorRoleSid": default_conversation_creator_role_sid, + "DefaultConversationRoleSid": default_conversation_role_sid, + "DefaultChatServiceRoleSid": default_chat_service_role_sid, + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConfigurationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConfigurationList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the ConfigurationList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the Service configuration resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + + self._notifications: Optional[NotificationList] = None + self._webhooks: Optional[WebhookList] = None + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + if self._notifications is None: + self._notifications = NotificationList( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + return self._notifications + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + return self._webhooks + + def get(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __call__(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..656bdd6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/notification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/notification.cpython-310.pyc new file mode 100644 index 0000000..dfa4945 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/notification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..c2ed809 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/notification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/notification.py new file mode 100644 index 0000000..2dee2d4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/notification.py @@ -0,0 +1,451 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NotificationInstance(InstanceResource): + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this configuration. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Configuration applies to. + :ivar new_message: The Push Notification configuration for New Messages. + :ivar added_to_conversation: The Push Notification configuration for being added to a Conversation. + :ivar removed_from_conversation: The Push Notification configuration for being removed from a Conversation. + :ivar log_enabled: Weather the notification logging is enabled. + :ivar url: An absolute API resource URL for this configuration. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], chat_service_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.new_message: Optional[Dict[str, object]] = payload.get("new_message") + self.added_to_conversation: Optional[Dict[str, object]] = payload.get( + "added_to_conversation" + ) + self.removed_from_conversation: Optional[Dict[str, object]] = payload.get( + "removed_from_conversation" + ) + self.log_enabled: Optional[bool] = payload.get("log_enabled") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._context: Optional[NotificationContext] = None + + @property + def _proxy(self) -> "NotificationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NotificationContext for this NotificationInstance + """ + if self._context is None: + self._context = NotificationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + ) + return self._context + + def fetch(self) -> "NotificationInstance": + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NotificationInstance": + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + log_enabled: Union[bool, object] = values.unset, + new_message_enabled: Union[bool, object] = values.unset, + new_message_template: Union[str, object] = values.unset, + new_message_sound: Union[str, object] = values.unset, + new_message_badge_count_enabled: Union[bool, object] = values.unset, + added_to_conversation_enabled: Union[bool, object] = values.unset, + added_to_conversation_template: Union[str, object] = values.unset, + added_to_conversation_sound: Union[str, object] = values.unset, + removed_from_conversation_enabled: Union[bool, object] = values.unset, + removed_from_conversation_template: Union[str, object] = values.unset, + removed_from_conversation_sound: Union[str, object] = values.unset, + new_message_with_media_enabled: Union[bool, object] = values.unset, + new_message_with_media_template: Union[str, object] = values.unset, + ) -> "NotificationInstance": + """ + Update the NotificationInstance + + :param log_enabled: Weather the notification logging is enabled. + :param new_message_enabled: Whether to send a notification when a new message is added to a conversation. The default is `false`. + :param new_message_template: The template to use to create the notification text displayed when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_sound: The name of the sound to play when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param added_to_conversation_enabled: Whether to send a notification when a participant is added to a conversation. The default is `false`. + :param added_to_conversation_template: The template to use to create the notification text displayed when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param added_to_conversation_sound: The name of the sound to play when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param removed_from_conversation_enabled: Whether to send a notification to a user when they are removed from a conversation. The default is `false`. + :param removed_from_conversation_template: The template to use to create the notification text displayed to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param removed_from_conversation_sound: The name of the sound to play to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param new_message_with_media_enabled: Whether to send a notification when a new message with media/file attachments is added to a conversation. The default is `false`. + :param new_message_with_media_template: The template to use to create the notification text displayed when a new message with media/file attachments is added to a conversation and `new_message.attachments.enabled` is `true`. + + :returns: The updated NotificationInstance + """ + return self._proxy.update( + log_enabled=log_enabled, + new_message_enabled=new_message_enabled, + new_message_template=new_message_template, + new_message_sound=new_message_sound, + new_message_badge_count_enabled=new_message_badge_count_enabled, + added_to_conversation_enabled=added_to_conversation_enabled, + added_to_conversation_template=added_to_conversation_template, + added_to_conversation_sound=added_to_conversation_sound, + removed_from_conversation_enabled=removed_from_conversation_enabled, + removed_from_conversation_template=removed_from_conversation_template, + removed_from_conversation_sound=removed_from_conversation_sound, + new_message_with_media_enabled=new_message_with_media_enabled, + new_message_with_media_template=new_message_with_media_template, + ) + + async def update_async( + self, + log_enabled: Union[bool, object] = values.unset, + new_message_enabled: Union[bool, object] = values.unset, + new_message_template: Union[str, object] = values.unset, + new_message_sound: Union[str, object] = values.unset, + new_message_badge_count_enabled: Union[bool, object] = values.unset, + added_to_conversation_enabled: Union[bool, object] = values.unset, + added_to_conversation_template: Union[str, object] = values.unset, + added_to_conversation_sound: Union[str, object] = values.unset, + removed_from_conversation_enabled: Union[bool, object] = values.unset, + removed_from_conversation_template: Union[str, object] = values.unset, + removed_from_conversation_sound: Union[str, object] = values.unset, + new_message_with_media_enabled: Union[bool, object] = values.unset, + new_message_with_media_template: Union[str, object] = values.unset, + ) -> "NotificationInstance": + """ + Asynchronous coroutine to update the NotificationInstance + + :param log_enabled: Weather the notification logging is enabled. + :param new_message_enabled: Whether to send a notification when a new message is added to a conversation. The default is `false`. + :param new_message_template: The template to use to create the notification text displayed when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_sound: The name of the sound to play when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param added_to_conversation_enabled: Whether to send a notification when a participant is added to a conversation. The default is `false`. + :param added_to_conversation_template: The template to use to create the notification text displayed when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param added_to_conversation_sound: The name of the sound to play when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param removed_from_conversation_enabled: Whether to send a notification to a user when they are removed from a conversation. The default is `false`. + :param removed_from_conversation_template: The template to use to create the notification text displayed to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param removed_from_conversation_sound: The name of the sound to play to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param new_message_with_media_enabled: Whether to send a notification when a new message with media/file attachments is added to a conversation. The default is `false`. + :param new_message_with_media_template: The template to use to create the notification text displayed when a new message with media/file attachments is added to a conversation and `new_message.attachments.enabled` is `true`. + + :returns: The updated NotificationInstance + """ + return await self._proxy.update_async( + log_enabled=log_enabled, + new_message_enabled=new_message_enabled, + new_message_template=new_message_template, + new_message_sound=new_message_sound, + new_message_badge_count_enabled=new_message_badge_count_enabled, + added_to_conversation_enabled=added_to_conversation_enabled, + added_to_conversation_template=added_to_conversation_template, + added_to_conversation_sound=added_to_conversation_sound, + removed_from_conversation_enabled=removed_from_conversation_enabled, + removed_from_conversation_template=removed_from_conversation_template, + removed_from_conversation_sound=removed_from_conversation_sound, + new_message_with_media_enabled=new_message_with_media_enabled, + new_message_with_media_template=new_message_with_media_template, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the NotificationContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Configuration applies to. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Configuration/Notifications".format( + **self._solution + ) + + def fetch(self) -> NotificationInstance: + """ + Fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + async def fetch_async(self) -> NotificationInstance: + """ + Asynchronous coroutine to fetch the NotificationInstance + + + :returns: The fetched NotificationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NotificationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + def update( + self, + log_enabled: Union[bool, object] = values.unset, + new_message_enabled: Union[bool, object] = values.unset, + new_message_template: Union[str, object] = values.unset, + new_message_sound: Union[str, object] = values.unset, + new_message_badge_count_enabled: Union[bool, object] = values.unset, + added_to_conversation_enabled: Union[bool, object] = values.unset, + added_to_conversation_template: Union[str, object] = values.unset, + added_to_conversation_sound: Union[str, object] = values.unset, + removed_from_conversation_enabled: Union[bool, object] = values.unset, + removed_from_conversation_template: Union[str, object] = values.unset, + removed_from_conversation_sound: Union[str, object] = values.unset, + new_message_with_media_enabled: Union[bool, object] = values.unset, + new_message_with_media_template: Union[str, object] = values.unset, + ) -> NotificationInstance: + """ + Update the NotificationInstance + + :param log_enabled: Weather the notification logging is enabled. + :param new_message_enabled: Whether to send a notification when a new message is added to a conversation. The default is `false`. + :param new_message_template: The template to use to create the notification text displayed when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_sound: The name of the sound to play when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param added_to_conversation_enabled: Whether to send a notification when a participant is added to a conversation. The default is `false`. + :param added_to_conversation_template: The template to use to create the notification text displayed when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param added_to_conversation_sound: The name of the sound to play when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param removed_from_conversation_enabled: Whether to send a notification to a user when they are removed from a conversation. The default is `false`. + :param removed_from_conversation_template: The template to use to create the notification text displayed to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param removed_from_conversation_sound: The name of the sound to play to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param new_message_with_media_enabled: Whether to send a notification when a new message with media/file attachments is added to a conversation. The default is `false`. + :param new_message_with_media_template: The template to use to create the notification text displayed when a new message with media/file attachments is added to a conversation and `new_message.attachments.enabled` is `true`. + + :returns: The updated NotificationInstance + """ + data = values.of( + { + "LogEnabled": serialize.boolean_to_string(log_enabled), + "NewMessage.Enabled": serialize.boolean_to_string(new_message_enabled), + "NewMessage.Template": new_message_template, + "NewMessage.Sound": new_message_sound, + "NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + new_message_badge_count_enabled + ), + "AddedToConversation.Enabled": serialize.boolean_to_string( + added_to_conversation_enabled + ), + "AddedToConversation.Template": added_to_conversation_template, + "AddedToConversation.Sound": added_to_conversation_sound, + "RemovedFromConversation.Enabled": serialize.boolean_to_string( + removed_from_conversation_enabled + ), + "RemovedFromConversation.Template": removed_from_conversation_template, + "RemovedFromConversation.Sound": removed_from_conversation_sound, + "NewMessage.WithMedia.Enabled": serialize.boolean_to_string( + new_message_with_media_enabled + ), + "NewMessage.WithMedia.Template": new_message_with_media_template, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def update_async( + self, + log_enabled: Union[bool, object] = values.unset, + new_message_enabled: Union[bool, object] = values.unset, + new_message_template: Union[str, object] = values.unset, + new_message_sound: Union[str, object] = values.unset, + new_message_badge_count_enabled: Union[bool, object] = values.unset, + added_to_conversation_enabled: Union[bool, object] = values.unset, + added_to_conversation_template: Union[str, object] = values.unset, + added_to_conversation_sound: Union[str, object] = values.unset, + removed_from_conversation_enabled: Union[bool, object] = values.unset, + removed_from_conversation_template: Union[str, object] = values.unset, + removed_from_conversation_sound: Union[str, object] = values.unset, + new_message_with_media_enabled: Union[bool, object] = values.unset, + new_message_with_media_template: Union[str, object] = values.unset, + ) -> NotificationInstance: + """ + Asynchronous coroutine to update the NotificationInstance + + :param log_enabled: Weather the notification logging is enabled. + :param new_message_enabled: Whether to send a notification when a new message is added to a conversation. The default is `false`. + :param new_message_template: The template to use to create the notification text displayed when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_sound: The name of the sound to play when a new message is added to a conversation and `new_message.enabled` is `true`. + :param new_message_badge_count_enabled: Whether the new message badge is enabled. The default is `false`. + :param added_to_conversation_enabled: Whether to send a notification when a participant is added to a conversation. The default is `false`. + :param added_to_conversation_template: The template to use to create the notification text displayed when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param added_to_conversation_sound: The name of the sound to play when a participant is added to a conversation and `added_to_conversation.enabled` is `true`. + :param removed_from_conversation_enabled: Whether to send a notification to a user when they are removed from a conversation. The default is `false`. + :param removed_from_conversation_template: The template to use to create the notification text displayed to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param removed_from_conversation_sound: The name of the sound to play to a user when they are removed from a conversation and `removed_from_conversation.enabled` is `true`. + :param new_message_with_media_enabled: Whether to send a notification when a new message with media/file attachments is added to a conversation. The default is `false`. + :param new_message_with_media_template: The template to use to create the notification text displayed when a new message with media/file attachments is added to a conversation and `new_message.attachments.enabled` is `true`. + + :returns: The updated NotificationInstance + """ + data = values.of( + { + "LogEnabled": serialize.boolean_to_string(log_enabled), + "NewMessage.Enabled": serialize.boolean_to_string(new_message_enabled), + "NewMessage.Template": new_message_template, + "NewMessage.Sound": new_message_sound, + "NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + new_message_badge_count_enabled + ), + "AddedToConversation.Enabled": serialize.boolean_to_string( + added_to_conversation_enabled + ), + "AddedToConversation.Template": added_to_conversation_template, + "AddedToConversation.Sound": added_to_conversation_sound, + "RemovedFromConversation.Enabled": serialize.boolean_to_string( + removed_from_conversation_enabled + ), + "RemovedFromConversation.Template": removed_from_conversation_template, + "RemovedFromConversation.Sound": removed_from_conversation_sound, + "NewMessage.WithMedia.Enabled": serialize.boolean_to_string( + new_message_with_media_enabled + ), + "NewMessage.WithMedia.Template": new_message_with_media_template, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the NotificationList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Configuration applies to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + + def get(self) -> NotificationContext: + """ + Constructs a NotificationContext + + """ + return NotificationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __call__(self) -> NotificationContext: + """ + Constructs a NotificationContext + + """ + return NotificationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/webhook.py new file mode 100644 index 0000000..aa40153 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/configuration/webhook.py @@ -0,0 +1,328 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this service. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :ivar post_webhook_url: The absolute url the post-event webhook request should be sent to. + :ivar filters: The list of events that your configured webhook targets will receive. Events not configured here will not fire. Possible values are `onParticipantAdd`, `onParticipantAdded`, `onDeliveryUpdated`, `onConversationUpdated`, `onConversationRemove`, `onParticipantRemove`, `onConversationUpdate`, `onMessageAdd`, `onMessageRemoved`, `onParticipantUpdated`, `onConversationAdded`, `onMessageAdded`, `onConversationAdd`, `onConversationRemoved`, `onParticipantUpdate`, `onMessageRemove`, `onMessageUpdated`, `onParticipantRemoved`, `onMessageUpdate` or `onConversationStateUpdated`. + :ivar method: + :ivar url: An absolute API resource URL for this webhook. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], chat_service_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.filters: Optional[List[str]] = payload.get("filters") + self.method: Optional["WebhookInstance.Method"] = payload.get("method") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + ) + return self._context + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + method: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param filters: The list of events that your configured webhook targets will receive. Events not configured here will not fire. Possible values are `onParticipantAdd`, `onParticipantAdded`, `onDeliveryUpdated`, `onConversationUpdated`, `onConversationRemove`, `onParticipantRemove`, `onConversationUpdate`, `onMessageAdd`, `onMessageRemoved`, `onParticipantUpdated`, `onConversationAdded`, `onMessageAdded`, `onConversationAdd`, `onConversationRemoved`, `onParticipantUpdate`, `onMessageRemove`, `onMessageUpdated`, `onParticipantRemoved`, `onMessageUpdate` or `onConversationStateUpdated`. + :param method: The HTTP method to be used when sending a webhook request. One of `GET` or `POST`. + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + filters=filters, + method=method, + ) + + async def update_async( + self, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + method: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param filters: The list of events that your configured webhook targets will receive. Events not configured here will not fire. Possible values are `onParticipantAdd`, `onParticipantAdded`, `onDeliveryUpdated`, `onConversationUpdated`, `onConversationRemove`, `onParticipantRemove`, `onConversationUpdate`, `onMessageAdd`, `onMessageRemoved`, `onParticipantUpdated`, `onConversationAdded`, `onMessageAdded`, `onConversationAdd`, `onConversationRemoved`, `onParticipantUpdate`, `onMessageRemove`, `onMessageUpdated`, `onParticipantRemoved`, `onMessageUpdate` or `onConversationStateUpdated`. + :param method: The HTTP method to be used when sending a webhook request. One of `GET` or `POST`. + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + filters=filters, + method=method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Configuration/Webhooks".format( + **self._solution + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + ) + + def update( + self, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + method: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param filters: The list of events that your configured webhook targets will receive. Events not configured here will not fire. Possible values are `onParticipantAdd`, `onParticipantAdded`, `onDeliveryUpdated`, `onConversationUpdated`, `onConversationRemove`, `onParticipantRemove`, `onConversationUpdate`, `onMessageAdd`, `onMessageRemoved`, `onParticipantUpdated`, `onConversationAdded`, `onMessageAdded`, `onConversationAdd`, `onConversationRemoved`, `onParticipantUpdate`, `onMessageRemove`, `onMessageUpdated`, `onParticipantRemoved`, `onMessageUpdate` or `onConversationStateUpdated`. + :param method: The HTTP method to be used when sending a webhook request. One of `GET` or `POST`. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "Filters": serialize.map(filters, lambda e: e), + "Method": method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def update_async( + self, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + filters: Union[List[str], object] = values.unset, + method: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param pre_webhook_url: The absolute url the pre-event webhook request should be sent to. + :param post_webhook_url: The absolute url the post-event webhook request should be sent to. + :param filters: The list of events that your configured webhook targets will receive. Events not configured here will not fire. Possible values are `onParticipantAdd`, `onParticipantAdded`, `onDeliveryUpdated`, `onConversationUpdated`, `onConversationRemove`, `onParticipantRemove`, `onConversationUpdate`, `onMessageAdd`, `onMessageRemoved`, `onParticipantUpdated`, `onConversationAdded`, `onMessageAdded`, `onConversationAdd`, `onConversationRemoved`, `onParticipantUpdate`, `onMessageRemove`, `onMessageUpdated`, `onParticipantRemoved`, `onMessageUpdate` or `onConversationStateUpdated`. + :param method: The HTTP method to be used when sending a webhook request. One of `GET` or `POST`. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "Filters": serialize.map(filters, lambda e: e), + "Method": method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + + def get(self) -> WebhookContext: + """ + Constructs a WebhookContext + + """ + return WebhookContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __call__(self) -> WebhookContext: + """ + Constructs a WebhookContext + + """ + return WebhookContext( + self._version, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__init__.py new file mode 100644 index 0000000..69e87e5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__init__.py @@ -0,0 +1,1021 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.service.conversation.message import MessageList +from twilio.rest.conversations.v1.service.conversation.participant import ( + ParticipantList, +) +from twilio.rest.conversations.v1.service.conversation.webhook import WebhookList + + +class ConversationInstance(InstanceResource): + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar state: + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + :ivar timers: Timer date values representing state update for this conversation. + :ivar url: An absolute API resource URL for this conversation. + :ivar links: Contains absolute URLs to access the [participants](https://www.twilio.com/docs/conversations/api/conversation-participant-resource), [messages](https://www.twilio.com/docs/conversations/api/conversation-message-resource) and [webhooks](https://www.twilio.com/docs/conversations/api/conversation-scoped-webhook-resource) of this conversation. + :ivar bindings: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.state: Optional["ConversationInstance.State"] = payload.get("state") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.timers: Optional[Dict[str, object]] = payload.get("timers") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.bindings: Optional[Dict[str, object]] = payload.get("bindings") + + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ConversationContext] = None + + @property + def _proxy(self) -> "ConversationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConversationContext for this ConversationInstance + """ + if self._context is None: + self._context = ConversationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ConversationInstance": + """ + Fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConversationInstance": + """ + Asynchronous coroutine to fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> "ConversationInstance": + """ + Update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + messaging_service_sid=messaging_service_sid, + state=state, + timers_inactive=timers_inactive, + timers_closed=timers_closed, + unique_name=unique_name, + bindings_email_address=bindings_email_address, + bindings_email_name=bindings_email_name, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> "ConversationInstance": + """ + Asynchronous coroutine to update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + messaging_service_sid=messaging_service_sid, + state=state, + timers_inactive=timers_inactive, + timers_closed=timers_closed, + unique_name=unique_name, + bindings_email_address=bindings_email_address, + bindings_email_name=bindings_email_name, + ) + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + return self._proxy.webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConversationContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str, sid: str): + """ + Initialize the ConversationContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Conversation resource is associated with. + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{sid}".format( + **self._solution + ) + + self._messages: Optional[MessageList] = None + self._participants: Optional[ParticipantList] = None + self._webhooks: Optional[WebhookList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ConversationInstance: + """ + Fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConversationInstance: + """ + Asynchronous coroutine to fetch the ConversationInstance + + + :returns: The fetched ConversationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "UniqueName": unique_name, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Asynchronous coroutine to update the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The updated ConversationInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "UniqueName": unique_name, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["chat_service_sid"], + self._solution["sid"], + ) + return self._messages + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["chat_service_sid"], + self._solution["sid"], + ) + return self._participants + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, + self._solution["chat_service_sid"], + self._solution["sid"], + ) + return self._webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConversationInstance: + """ + Build an instance of ConversationInstance + + :param payload: Payload response from the API + """ + return ConversationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConversationList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the ConversationList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Conversation resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Create the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The created ConversationInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ConversationInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + timers_inactive: Union[str, object] = values.unset, + timers_closed: Union[str, object] = values.unset, + bindings_email_address: Union[str, object] = values.unset, + bindings_email_name: Union[str, object] = values.unset, + ) -> ConversationInstance: + """ + Asynchronously create the ConversationInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used to address the resource in place of the resource's `sid` in the URL. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param messaging_service_sid: The unique ID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) this conversation belongs to. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. + :param state: + :param timers_inactive: ISO8601 duration when conversation will be switched to `inactive` state. Minimum value for this timer is 1 minute. + :param timers_closed: ISO8601 duration when conversation will be switched to `closed` state. Minimum value for this timer is 10 minutes. + :param bindings_email_address: The default email address that will be used when sending outbound emails in this conversation. + :param bindings_email_name: The default name that will be used when sending outbound emails in this conversation. + + :returns: The created ConversationInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "MessagingServiceSid": messaging_service_sid, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "State": state, + "Timers.Inactive": timers_inactive, + "Timers.Closed": timers_closed, + "Bindings.Email.Address": bindings_email_address, + "Bindings.Email.Name": bindings_email_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConversationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def stream( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConversationInstance]: + """ + Streams ConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + start_date=start_date, + end_date=end_date, + state=state, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConversationInstance]: + """ + Asynchronously streams ConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + start_date=start_date, + end_date=end_date, + state=state, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConversationInstance]: + """ + Lists ConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + start_date=start_date, + end_date=end_date, + state=state, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConversationInstance]: + """ + Asynchronously lists ConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param str end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param "ConversationInstance.State" state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + start_date=start_date, + end_date=end_date, + state=state, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConversationPage: + """ + Retrieve a single page of ConversationInstance records from the API. + Request is executed immediately + + :param start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConversationInstance + """ + data = values.of( + { + "StartDate": start_date, + "EndDate": end_date, + "State": state, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConversationPage(self._version, response, self._solution) + + async def page_async( + self, + start_date: Union[str, object] = values.unset, + end_date: Union[str, object] = values.unset, + state: Union["ConversationInstance.State", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConversationPage: + """ + Asynchronously retrieve a single page of ConversationInstance records from the API. + Request is executed immediately + + :param start_date: Specifies the beginning of the date range for filtering Conversations based on their creation date. Conversations that were created on or after this date will be included in the results. The date must be in ISO8601 format, specifically starting at the beginning of the specified date (YYYY-MM-DDT00:00:00Z), for precise filtering. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param end_date: Defines the end of the date range for filtering conversations by their creation date. Only conversations that were created on or before this date will appear in the results. The date must be in ISO8601 format, specifically capturing up to the end of the specified date (YYYY-MM-DDT23:59:59Z), to ensure that conversations from the entire end day are included. This parameter can be combined with other filters. If this filter is used, the returned list is sorted by latest conversation creation date in descending order. + :param state: State for sorting and filtering list of Conversations. Can be `active`, `inactive` or `closed` + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConversationInstance + """ + data = values.of( + { + "StartDate": start_date, + "EndDate": end_date, + "State": state, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConversationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConversationPage: + """ + Retrieve a specific page of ConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConversationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConversationPage: + """ + Asynchronously retrieve a specific page of ConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConversationPage(self._version, response, self._solution) + + def get(self, sid: str) -> ConversationContext: + """ + Constructs a ConversationContext + + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + return ConversationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ConversationContext: + """ + Constructs a ConversationContext + + :param sid: A 34 character string that uniquely identifies this resource. Can also be the `unique_name` of the Conversation. + """ + return ConversationContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ac07732 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/participant.cpython-310.pyc new file mode 100644 index 0000000..f451222 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..0c0ab7e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__init__.py new file mode 100644 index 0000000..753df18 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__init__.py @@ -0,0 +1,895 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.service.conversation.message.delivery_receipt import ( + DeliveryReceiptList, +) + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this message. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar index: The index of the message within the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource). + :ivar author: The channel specific identifier of the message's author. Defaults to `system`. + :ivar body: The content of the message, can be up to 1,600 characters long. + :ivar media: An array of objects that describe the Message's media, if the message contains media. Each object contains these fields: `content_type` with the MIME type of the media, `filename` with the name of the media, `sid` with the SID of the Media resource, and `size` with the media object's file size in bytes. If the Message has no media, this value is `null`. + :ivar attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar participant_sid: The unique ID of messages's author participant. Null in case of `system` sent message. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :ivar delivery: An object that contains the summary of delivery statuses for the message to non-chat participants. + :ivar url: An absolute API resource URL for this message. + :ivar links: Contains an absolute API resource URL to access the delivery & read receipts of this message. + :ivar content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.sid: Optional[str] = payload.get("sid") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.author: Optional[str] = payload.get("author") + self.body: Optional[str] = payload.get("body") + self.media: Optional[List[Dict[str, object]]] = payload.get("media") + self.attributes: Optional[str] = payload.get("attributes") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.delivery: Optional[Dict[str, object]] = payload.get("delivery") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.content_sid: Optional[str] = payload.get("content_sid") + + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + author=author, + body=body, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + subject=subject, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + author=author, + body=body, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + subject=subject, + ) + + @property + def delivery_receipts(self) -> DeliveryReceiptList: + """ + Access the delivery_receipts + """ + return self._proxy.delivery_receipts + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__( + self, version: Version, chat_service_sid: str, conversation_sid: str, sid: str + ): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Messages/{sid}".format( + **self._solution + ) + + self._delivery_receipts: Optional[DeliveryReceiptList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + @property + def delivery_receipts(self) -> DeliveryReceiptList: + """ + Access the delivery_receipts + """ + if self._delivery_receipts is None: + self._delivery_receipts = DeliveryReceiptList( + self._version, + self._solution["chat_service_sid"], + self._solution["conversation_sid"], + self._solution["sid"], + ) + return self._delivery_receipts + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str, conversation_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for messages. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Messages".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + content_sid: Union[str, object] = values.unset, + content_variables: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param media_sid: The Media SID to be attached to the new Message. + :param content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template, required for template-generated messages. **Note** that if this field is set, `Body` and `MediaSid` parameters are ignored. + :param content_variables: A structurally valid JSON string that contains values to resolve Rich Content template variables. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MediaSid": media_sid, + "ContentSid": content_sid, + "ContentVariables": content_variables, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + author: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + content_sid: Union[str, object] = values.unset, + content_variables: Union[str, object] = values.unset, + subject: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param author: The channel specific identifier of the message's author. Defaults to `system`. + :param body: The content of the message, can be up to 1,600 characters long. + :param date_created: The date that this resource was created. + :param date_updated: The date that this resource was last updated. `null` if the message has not been edited. + :param attributes: A string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \\\"{}\\\" will be returned. + :param media_sid: The Media SID to be attached to the new Message. + :param content_sid: The unique ID of the multi-channel [Rich Content](https://www.twilio.com/docs/content) template, required for template-generated messages. **Note** that if this field is set, `Body` and `MediaSid` parameters are ignored. + :param content_variables: A structurally valid JSON string that contains values to resolve Rich Content template variables. + :param subject: The subject of the message, can be up to 256 characters long. + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Author": author, + "Body": body, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MediaSid": media_sid, + "ContentSid": content_sid, + "ContentVariables": content_variables, + "Subject": subject, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: The sort order of the returned messages. Can be: `asc` (ascending) or `desc` (descending), with `asc` as the default. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return MessageContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return MessageContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d9d374e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc new file mode 100644 index 0000000..da62220 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/__pycache__/delivery_receipt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/delivery_receipt.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/delivery_receipt.py new file mode 100644 index 0000000..b9bf8a7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/message/delivery_receipt.py @@ -0,0 +1,491 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeliveryReceiptInstance(InstanceResource): + + class DeliveryStatus(object): + READ = "read" + FAILED = "failed" + DELIVERED = "delivered" + UNDELIVERED = "undelivered" + SENT = "sent" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this participant. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Message resource is associated with. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :ivar message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar channel_message_sid: A messaging channel-specific identifier for the message delivered to participant e.g. `SMxx` for SMS, `WAxx` for Whatsapp etc. + :ivar participant_sid: The unique ID of the participant the delivery receipt belongs to. + :ivar status: + :ivar error_code: The message [delivery error code](https://www.twilio.com/docs/sms/api/message-resource#delivery-related-errors) for a `failed` status, + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. `null` if the delivery receipt has not been updated. + :ivar url: An absolute API resource URL for this delivery receipt. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + conversation_sid: str, + message_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.message_sid: Optional[str] = payload.get("message_sid") + self.sid: Optional[str] = payload.get("sid") + self.channel_message_sid: Optional[str] = payload.get("channel_message_sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.status: Optional["DeliveryReceiptInstance.DeliveryStatus"] = payload.get( + "status" + ) + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "message_sid": message_sid, + "sid": sid or self.sid, + } + self._context: Optional[DeliveryReceiptContext] = None + + @property + def _proxy(self) -> "DeliveryReceiptContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeliveryReceiptContext for this DeliveryReceiptInstance + """ + if self._context is None: + self._context = DeliveryReceiptContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "DeliveryReceiptInstance": + """ + Fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeliveryReceiptInstance": + """ + Asynchronous coroutine to fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeliveryReceiptContext(InstanceContext): + + def __init__( + self, + version: Version, + chat_service_sid: str, + conversation_sid: str, + message_sid: str, + sid: str, + ): + """ + Initialize the DeliveryReceiptContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Message resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "message_sid": message_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Messages/{message_sid}/Receipts/{sid}".format( + **self._solution + ) + + def fetch(self) -> DeliveryReceiptInstance: + """ + Fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeliveryReceiptInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeliveryReceiptInstance: + """ + Asynchronous coroutine to fetch the DeliveryReceiptInstance + + + :returns: The fetched DeliveryReceiptInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeliveryReceiptInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeliveryReceiptPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeliveryReceiptInstance: + """ + Build an instance of DeliveryReceiptInstance + + :param payload: Payload response from the API + """ + return DeliveryReceiptInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeliveryReceiptList(ListResource): + + def __init__( + self, + version: Version, + chat_service_sid: str, + conversation_sid: str, + message_sid: str, + ): + """ + Initialize the DeliveryReceiptList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Message resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this message. + :param message_sid: The SID of the message within a [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) the delivery receipt belongs to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "message_sid": message_sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Messages/{message_sid}/Receipts".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeliveryReceiptInstance]: + """ + Streams DeliveryReceiptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeliveryReceiptInstance]: + """ + Asynchronously streams DeliveryReceiptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeliveryReceiptInstance]: + """ + Lists DeliveryReceiptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeliveryReceiptInstance]: + """ + Asynchronously lists DeliveryReceiptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeliveryReceiptPage: + """ + Retrieve a single page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeliveryReceiptInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeliveryReceiptPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeliveryReceiptPage: + """ + Asynchronously retrieve a single page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeliveryReceiptInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeliveryReceiptPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeliveryReceiptPage: + """ + Retrieve a specific page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeliveryReceiptInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeliveryReceiptPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeliveryReceiptPage: + """ + Asynchronously retrieve a specific page of DeliveryReceiptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeliveryReceiptInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeliveryReceiptPage(self._version, response, self._solution) + + def get(self, sid: str) -> DeliveryReceiptContext: + """ + Constructs a DeliveryReceiptContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return DeliveryReceiptContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> DeliveryReceiptContext: + """ + Constructs a DeliveryReceiptContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return DeliveryReceiptContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + message_sid=self._solution["message_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/participant.py new file mode 100644 index 0000000..6f6eee6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/participant.py @@ -0,0 +1,877 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this participant. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this participant. + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :ivar messaging_binding: Information about how this participant exchanges messages with the conversation. A JSON parameter consisting of type and address fields of the participant. + :ivar role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :ivar date_created: The date on which this resource was created. + :ivar date_updated: The date on which this resource was last updated. + :ivar url: An absolute API resource URL for this participant. + :ivar last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :ivar last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.sid: Optional[str] = payload.get("sid") + self.identity: Optional[str] = payload.get("identity") + self.attributes: Optional[str] = payload.get("attributes") + self.messaging_binding: Optional[Dict[str, object]] = payload.get( + "messaging_binding" + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.last_read_message_index: Optional[int] = deserialize.integer( + payload.get("last_read_message_index") + ) + self.last_read_timestamp: Optional[str] = payload.get("last_read_timestamp") + + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + identity: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + date_created=date_created, + date_updated=date_updated, + identity=identity, + attributes=attributes, + role_sid=role_sid, + messaging_binding_proxy_address=messaging_binding_proxy_address, + messaging_binding_projected_address=messaging_binding_projected_address, + last_read_message_index=last_read_message_index, + last_read_timestamp=last_read_timestamp, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + identity: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> "ParticipantInstance": + """ + Asynchronous coroutine to update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + date_created=date_created, + date_updated=date_updated, + identity=identity, + attributes=attributes, + role_sid=role_sid, + messaging_binding_proxy_address=messaging_binding_proxy_address, + messaging_binding_projected_address=messaging_binding_projected_address, + last_read_message_index=last_read_message_index, + last_read_timestamp=last_read_timestamp, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__( + self, version: Version, chat_service_sid: str, conversation_sid: str, sid: str + ): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this participant. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Participants/{sid}".format( + **self._solution + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + identity: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Identity": identity, + "Attributes": attributes, + "RoleSid": role_sid, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "LastReadMessageIndex": last_read_message_index, + "LastReadTimestamp": last_read_timestamp, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + identity: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + last_read_timestamp: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronous coroutine to update the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + :param messaging_binding_proxy_address: The address of the Twilio phone number that the participant is in contact with. 'null' value will remove it. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. 'null' value will remove it. + :param last_read_message_index: Index of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + :param last_read_timestamp: Timestamp of last “read” message in the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for the Participant. + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Identity": identity, + "Attributes": attributes, + "RoleSid": role_sid, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "LastReadMessageIndex": last_read_message_index, + "LastReadTimestamp": last_read_timestamp, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str, conversation_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for participants. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Participants".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + identity: Union[str, object] = values.unset, + messaging_binding_address: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Create the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param messaging_binding_address: The address of the participant's device, e.g. a phone or WhatsApp number. Together with the Proxy address, this determines a participant uniquely. This field (with `proxy_address`) is only null when the participant is interacting from an SDK endpoint (see the `identity` field). + :param messaging_binding_proxy_address: The address of the Twilio phone number (or WhatsApp number) that the participant is in contact with. This field, together with participant address, is only null when the participant is interacting from an SDK endpoint (see the `identity` field). + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identity": identity, + "MessagingBinding.Address": messaging_binding_address, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ParticipantInstance.WebhookEnabledType", object + ] = values.unset, + identity: Union[str, object] = values.unset, + messaging_binding_address: Union[str, object] = values.unset, + messaging_binding_proxy_address: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + messaging_binding_projected_address: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronously create the ParticipantInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the [Conversation SDK](https://www.twilio.com/docs/conversations/sdk-overview) to communicate. Limited to 256 characters. + :param messaging_binding_address: The address of the participant's device, e.g. a phone or WhatsApp number. Together with the Proxy address, this determines a participant uniquely. This field (with `proxy_address`) is only null when the participant is interacting from an SDK endpoint (see the `identity` field). + :param messaging_binding_proxy_address: The address of the Twilio phone number (or WhatsApp number) that the participant is in contact with. This field, together with participant address, is only null when the participant is interacting from an SDK endpoint (see the `identity` field). + :param date_created: The date on which this resource was created. + :param date_updated: The date on which this resource was last updated. + :param attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set `{}` will be returned. + :param messaging_binding_projected_address: The address of the Twilio phone number that is used in Group MMS. + :param role_sid: The SID of a conversation-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identity": identity, + "MessagingBinding.Address": messaging_binding_address, + "MessagingBinding.ProxyAddress": messaging_binding_proxy_address, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + "MessagingBinding.ProjectedAddress": messaging_binding_projected_address, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ParticipantInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ParticipantContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return ParticipantContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/webhook.py new file mode 100644 index 0000000..83b2674 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/conversation/webhook.py @@ -0,0 +1,758 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + class Target(object): + WEBHOOK = "webhook" + TRIGGER = "trigger" + STUDIO = "studio" + + """ + :ivar sid: A 34 character string that uniquely identifies this resource. + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + :ivar target: The target of this webhook: `webhook`, `studio`, `trigger` + :ivar url: An absolute API resource URL for this webhook. + :ivar configuration: The configuration of this webhook. Is defined based on target. + :ivar date_created: The date that this resource was created. + :ivar date_updated: The date that this resource was last updated. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + conversation_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.target: Optional[str] = payload.get("target") + self.url: Optional[str] = payload.get("url") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid or self.sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__( + self, version: Version, chat_service_sid: str, conversation_sid: str, sid: str + ): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + :param sid: A 34 character string that uniquely identifies this resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Webhooks/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebhookInstance: + """ + Build an instance of WebhookInstance + + :param payload: Payload response from the API + """ + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str, conversation_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant resource is associated with. + :param conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this webhook. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "conversation_sid": conversation_sid, + } + self._uri = "/Services/{chat_service_sid}/Conversations/{conversation_sid}/Webhooks".format( + **self._solution + ) + + def create( + self, + target: "WebhookInstance.Target", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_replay_after: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Create the WebhookInstance + + :param target: + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + :param configuration_replay_after: The message index for which and it's successors the webhook will be replayed. Not set by default + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Target": target, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.ReplayAfter": configuration_replay_after, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def create_async( + self, + target: "WebhookInstance.Target", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_replay_after: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronously create the WebhookInstance + + :param target: + :param configuration_url: The absolute url the webhook request should be sent to. + :param configuration_method: + :param configuration_filters: The list of events, firing webhook event for this Conversation. + :param configuration_triggers: The list of keywords, firing webhook event for this Conversation. + :param configuration_flow_sid: The studio flow SID, where the webhook should be sent to. + :param configuration_replay_after: The message index for which and it's successors the webhook will be replayed. Not set by default + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Target": target, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.ReplayAfter": configuration_replay_after, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebhookInstance]: + """ + Streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebhookInstance]: + """ + Asynchronously streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Asynchronously lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebhookPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Asynchronously retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebhookPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WebhookPage: + """ + Retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WebhookPage: + """ + Asynchronously retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + def get(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return WebhookContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: A 34 character string that uniquely identifies this resource. + """ + return WebhookContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + conversation_sid=self._solution["conversation_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/participant_conversation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/participant_conversation.py new file mode 100644 index 0000000..2c3bebc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/participant_conversation.py @@ -0,0 +1,373 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantConversationInstance(InstanceResource): + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar participant_sid: The unique ID of the [Participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource). + :ivar participant_user_sid: The unique string that identifies the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). + :ivar participant_identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :ivar participant_messaging_binding: Information about how this participant exchanges messages with the conversation. A JSON parameter consisting of type and address fields of the participant. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) this Participant belongs to. + :ivar conversation_unique_name: An application-defined string that uniquely identifies the Conversation resource. + :ivar conversation_friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar conversation_attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar conversation_date_created: The date that this conversation was created, given in ISO 8601 format. + :ivar conversation_date_updated: The date that this conversation was last updated, given in ISO 8601 format. + :ivar conversation_created_by: Identity of the creator of this Conversation. + :ivar conversation_state: + :ivar conversation_timers: Timer date values representing state update for this conversation. + :ivar links: Contains absolute URLs to access the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) and [conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) of this conversation. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], chat_service_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.participant_user_sid: Optional[str] = payload.get("participant_user_sid") + self.participant_identity: Optional[str] = payload.get("participant_identity") + self.participant_messaging_binding: Optional[Dict[str, object]] = payload.get( + "participant_messaging_binding" + ) + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.conversation_unique_name: Optional[str] = payload.get( + "conversation_unique_name" + ) + self.conversation_friendly_name: Optional[str] = payload.get( + "conversation_friendly_name" + ) + self.conversation_attributes: Optional[str] = payload.get( + "conversation_attributes" + ) + self.conversation_date_created: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("conversation_date_created")) + ) + self.conversation_date_updated: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("conversation_date_updated")) + ) + self.conversation_created_by: Optional[str] = payload.get( + "conversation_created_by" + ) + self.conversation_state: Optional["ParticipantConversationInstance.State"] = ( + payload.get("conversation_state") + ) + self.conversation_timers: Optional[Dict[str, object]] = payload.get( + "conversation_timers" + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "chat_service_sid": chat_service_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class ParticipantConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantConversationInstance: + """ + Build an instance of ParticipantConversationInstance + + :param payload: Payload response from the API + """ + return ParticipantConversationInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantConversationList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the ParticipantConversationList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Participant Conversations resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/ParticipantConversations".format( + **self._solution + ) + + def stream( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantConversationInstance]: + """ + Streams ParticipantConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + identity=identity, address=address, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantConversationInstance]: + """ + Asynchronously streams ParticipantConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + identity=identity, address=address, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantConversationInstance]: + """ + Lists ParticipantConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + address=address, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantConversationInstance]: + """ + Asynchronously lists ParticipantConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param str address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + address=address, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantConversationPage: + """ + Retrieve a single page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantConversationInstance + """ + data = values.of( + { + "Identity": identity, + "Address": address, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantConversationPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[str, object] = values.unset, + address: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantConversationPage: + """ + Asynchronously retrieve a single page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param identity: A unique string identifier for the conversation participant as [Conversation User](https://www.twilio.com/docs/conversations/api/user-resource). This parameter is non-null if (and only if) the participant is using the Conversations SDK to communicate. Limited to 256 characters. + :param address: A unique string identifier for the conversation participant who's not a Conversation User. This parameter could be found in messaging_binding.address field of Participant resource. It should be url-encoded. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantConversationInstance + """ + data = values.of( + { + "Identity": identity, + "Address": address, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantConversationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantConversationPage: + """ + Retrieve a specific page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantConversationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantConversationPage: + """ + Asynchronously retrieve a specific page of ParticipantConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantConversationPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/role.py new file mode 100644 index 0000000..3c0a0fc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/role.py @@ -0,0 +1,615 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CONVERSATION = "conversation" + SERVICE = "service" + + """ + :ivar sid: The unique string that we created to identify the Role resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Role resource. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Role resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar permissions: An array of the permissions the role has been granted. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: An absolute API resource URL for this user role. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to update the Role resource in. + :param sid: The SID of the Role resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: A permission that you grant to the role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. Note that the update action replaces all previously assigned permissions with those defined in the update action. To remove a permission, do not include it in the subsequent update action. The values for this parameter depend on the role's `type`. + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the RoleList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to read the Role resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Roles".format(**self._solution) + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: A descriptive string that you create to describe the new resource. It can be up to 64 characters long. + :param type: + :param permission: A permission that you grant to the new role. Only one permission can be granted per parameter. To assign more than one permission, repeat this parameter for each permission value. The values for this parameter depend on the role's `type`. + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response, self._solution) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: The SID of the Role resource to update. + """ + return RoleContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__init__.py new file mode 100644 index 0000000..37a8d2e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__init__.py @@ -0,0 +1,770 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.service.user.user_conversation import ( + UserConversationList, +) + + +class UserInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the User resource. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the User resource is associated with. + :ivar role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) assigned to the user. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar is_online: Whether the User is actively connected to this Conversations Service and online. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, if the User has never been online for this Conversations Service, even if the Service's `reachability_enabled` is `true`. + :ivar is_notifiable: Whether the User has a potentially valid Push Notification registration (APN or GCM) for this Conversations Service. If at least one registration exists, `true`; otherwise `false`. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, and if the User has never had a notification registration, even if the Service's `reachability_enabled` is `true`. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: An absolute API resource URL for this user. + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.attributes: Optional[str] = payload.get("attributes") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + attributes=attributes, + role_sid=role_sid, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + attributes=attributes, + role_sid=role_sid, + ) + + @property + def user_conversations(self) -> UserConversationList: + """ + Access the user_conversations + """ + return self._proxy.user_conversations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, chat_service_sid: str, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the User resource is associated with. + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "sid": sid, + } + self._uri = "/Services/{chat_service_sid}/Users/{sid}".format(**self._solution) + + self._user_conversations: Optional[UserConversationList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + sid=self._solution["sid"], + ) + + @property + def user_conversations(self) -> UserConversationList: + """ + Access the user_conversations + """ + if self._user_conversations is None: + self._user_conversations = UserConversationList( + self._version, + self._solution["chat_service_sid"], + self._solution["sid"], + ) + return self._user_conversations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) to read the User resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + } + self._uri = "/Services/{chat_service_sid}/Users".format(**self._solution) + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, chat_service_sid=self._solution["chat_service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext( + self._version, chat_service_sid=self._solution["chat_service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..91222e6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/user_conversation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/user_conversation.cpython-310.pyc new file mode 100644 index 0000000..1de0193 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/__pycache__/user_conversation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/user_conversation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/user_conversation.py new file mode 100644 index 0000000..7a83650 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/service/user/user_conversation.py @@ -0,0 +1,660 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserConversationInstance(InstanceResource): + + class NotificationLevel(object): + DEFAULT = "default" + MUTED = "muted" + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this User Conversation. + :ivar unread_messages_count: The number of unread Messages in the Conversation for the Participant. + :ivar last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + :ivar participant_sid: The unique ID of the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) the user conversation belongs to. + :ivar user_sid: The unique string that identifies the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). + :ivar friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar conversation_state: + :ivar timers: Timer date values representing state update for this conversation. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar date_created: The date that this conversation was created, given in ISO 8601 format. + :ivar date_updated: The date that this conversation was last updated, given in ISO 8601 format. + :ivar created_by: Identity of the creator of this Conversation. + :ivar notification_level: + :ivar unique_name: An application-defined string that uniquely identifies the Conversation resource. It can be used to address the resource in place of the resource's `conversation_sid` in the URL. + :ivar url: + :ivar links: Contains absolute URLs to access the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) and [conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) of this conversation. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + chat_service_sid: str, + user_sid: str, + conversation_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.last_read_message_index: Optional[int] = deserialize.integer( + payload.get("last_read_message_index") + ) + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.conversation_state: Optional["UserConversationInstance.State"] = ( + payload.get("conversation_state") + ) + self.timers: Optional[Dict[str, object]] = payload.get("timers") + self.attributes: Optional[str] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.notification_level: Optional[ + "UserConversationInstance.NotificationLevel" + ] = payload.get("notification_level") + self.unique_name: Optional[str] = payload.get("unique_name") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "chat_service_sid": chat_service_sid, + "user_sid": user_sid, + "conversation_sid": conversation_sid or self.conversation_sid, + } + self._context: Optional[UserConversationContext] = None + + @property + def _proxy(self) -> "UserConversationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserConversationContext for this UserConversationInstance + """ + if self._context is None: + self._context = UserConversationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserConversationInstance": + """ + Fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserConversationInstance": + """ + Asynchronous coroutine to fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> "UserConversationInstance": + """ + Update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + return self._proxy.update( + notification_level=notification_level, + last_read_timestamp=last_read_timestamp, + last_read_message_index=last_read_message_index, + ) + + async def update_async( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> "UserConversationInstance": + """ + Asynchronous coroutine to update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + return await self._proxy.update_async( + notification_level=notification_level, + last_read_timestamp=last_read_timestamp, + last_read_message_index=last_read_message_index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserConversationContext(InstanceContext): + + def __init__( + self, + version: Version, + chat_service_sid: str, + user_sid: str, + conversation_sid: str, + ): + """ + Initialize the UserConversationContext + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Conversation resource is associated with. + :param user_sid: The unique SID identifier of the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). This value can be either the `sid` or the `identity` of the User resource. + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "user_sid": user_sid, + "conversation_sid": conversation_sid, + } + self._uri = "/Services/{chat_service_sid}/Users/{user_sid}/Conversations/{conversation_sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserConversationInstance: + """ + Fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def fetch_async(self) -> UserConversationInstance: + """ + Asynchronous coroutine to fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def update( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> UserConversationInstance: + """ + Update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastReadTimestamp": serialize.iso8601_datetime(last_read_timestamp), + "LastReadMessageIndex": last_read_message_index, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def update_async( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> UserConversationInstance: + """ + Asynchronous coroutine to update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastReadTimestamp": serialize.iso8601_datetime(last_read_timestamp), + "LastReadMessageIndex": last_read_message_index, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserConversationInstance: + """ + Build an instance of UserConversationInstance + + :param payload: Payload response from the API + """ + return UserConversationInstance( + self._version, + payload, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserConversationList(ListResource): + + def __init__(self, version: Version, chat_service_sid: str, user_sid: str): + """ + Initialize the UserConversationList + + :param version: Version that contains the resource + :param chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the Conversation resource is associated with. + :param user_sid: The unique SID identifier of the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). This value can be either the `sid` or the `identity` of the User resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "chat_service_sid": chat_service_sid, + "user_sid": user_sid, + } + self._uri = ( + "/Services/{chat_service_sid}/Users/{user_sid}/Conversations".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserConversationInstance]: + """ + Streams UserConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserConversationInstance]: + """ + Asynchronously streams UserConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserConversationInstance]: + """ + Lists UserConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserConversationInstance]: + """ + Asynchronously lists UserConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserConversationPage: + """ + Retrieve a single page of UserConversationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserConversationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserConversationPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserConversationPage: + """ + Asynchronously retrieve a single page of UserConversationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserConversationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserConversationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserConversationPage: + """ + Retrieve a specific page of UserConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserConversationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserConversationPage: + """ + Asynchronously retrieve a specific page of UserConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserConversationPage(self._version, response, self._solution) + + def get(self, conversation_sid: str) -> UserConversationContext: + """ + Constructs a UserConversationContext + + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + return UserConversationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=conversation_sid, + ) + + def __call__(self, conversation_sid: str) -> UserConversationContext: + """ + Constructs a UserConversationContext + + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + return UserConversationContext( + self._version, + chat_service_sid=self._solution["chat_service_sid"], + user_sid=self._solution["user_sid"], + conversation_sid=conversation_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__init__.py new file mode 100644 index 0000000..d8017c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__init__.py @@ -0,0 +1,732 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.conversations.v1.user.user_conversation import UserConversationList + + +class UserInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: The unique string that we created to identify the User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the User resource. + :ivar chat_service_sid: The SID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) the User resource is associated with. + :ivar role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) assigned to the user. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :ivar is_online: Whether the User is actively connected to this Conversations Service and online. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, if the User has never been online for this Conversations Service, even if the Service's `reachability_enabled` is `true`. + :ivar is_notifiable: Whether the User has a potentially valid Push Notification registration (APN or GCM) for this Conversations Service. If at least one registration exists, `true`; otherwise `false`. This value is only returned by Fetch actions that return a single resource and `null` is always returned by a Read action. This value is `null` if the Service's `reachability_enabled` is `false`, and if the User has never had a notification registration, even if the Service's `reachability_enabled` is `true`. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: An absolute API resource URL for this user. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.attributes: Optional[str] = payload.get("attributes") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + attributes=attributes, + role_sid=role_sid, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + attributes=attributes, + role_sid=role_sid, + ) + + @property + def user_conversations(self) -> UserConversationList: + """ + Access the user_conversations + """ + return self._proxy.user_conversations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Users/{sid}".format(**self._solution) + + self._user_conversations: Optional[UserConversationList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def user_conversations(self) -> UserConversationList: + """ + Access the user_conversations + """ + if self._user_conversations is None: + self._user_conversations = UserConversationList( + self._version, + self._solution["sid"], + ) + return self._user_conversations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the UserList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Users" + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance(self._version, payload) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + role_sid: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: The application-defined string that uniquely identifies the resource's User within the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource). This value is often a username or an email address, and is case-sensitive. + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The JSON Object string that stores application-specific data. If attributes have not been set, `{}` is returned. + :param role_sid: The SID of a service-level [Role](https://www.twilio.com/docs/conversations/api/role-resource) to assign to the user. + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "FriendlyName": friendly_name, + "Attributes": attributes, + "RoleSid": role_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext(self._version, sid=sid) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4011305 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/user_conversation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/user_conversation.cpython-310.pyc new file mode 100644 index 0000000..d6668b5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/__pycache__/user_conversation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/user_conversation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/user_conversation.py new file mode 100644 index 0000000..e162441 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/conversations/v1/user/user_conversation.py @@ -0,0 +1,634 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Conversations + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserConversationInstance(InstanceResource): + + class NotificationLevel(object): + DEFAULT = "default" + MUTED = "muted" + + class State(object): + INACTIVE = "inactive" + ACTIVE = "active" + CLOSED = "closed" + + """ + :ivar account_sid: The unique ID of the [Account](https://www.twilio.com/docs/iam/api/account) responsible for this conversation. + :ivar chat_service_sid: The unique ID of the [Conversation Service](https://www.twilio.com/docs/conversations/api/service-resource) this conversation belongs to. + :ivar conversation_sid: The unique ID of the [Conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) for this User Conversation. + :ivar unread_messages_count: The number of unread Messages in the Conversation for the Participant. + :ivar last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + :ivar participant_sid: The unique ID of the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) the user conversation belongs to. + :ivar user_sid: The unique string that identifies the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). + :ivar friendly_name: The human-readable name of this conversation, limited to 256 characters. Optional. + :ivar conversation_state: + :ivar timers: Timer date values representing state update for this conversation. + :ivar attributes: An optional string metadata field you can use to store any data you wish. The string value must contain structurally valid JSON if specified. **Note** that if the attributes are not set \"{}\" will be returned. + :ivar date_created: The date that this conversation was created, given in ISO 8601 format. + :ivar date_updated: The date that this conversation was last updated, given in ISO 8601 format. + :ivar created_by: Identity of the creator of this Conversation. + :ivar notification_level: + :ivar unique_name: An application-defined string that uniquely identifies the Conversation resource. It can be used to address the resource in place of the resource's `conversation_sid` in the URL. + :ivar url: + :ivar links: Contains absolute URLs to access the [participant](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) and [conversation](https://www.twilio.com/docs/conversations/api/conversation-resource) of this conversation. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + user_sid: str, + conversation_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.last_read_message_index: Optional[int] = deserialize.integer( + payload.get("last_read_message_index") + ) + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.conversation_state: Optional["UserConversationInstance.State"] = ( + payload.get("conversation_state") + ) + self.timers: Optional[Dict[str, object]] = payload.get("timers") + self.attributes: Optional[str] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.notification_level: Optional[ + "UserConversationInstance.NotificationLevel" + ] = payload.get("notification_level") + self.unique_name: Optional[str] = payload.get("unique_name") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "user_sid": user_sid, + "conversation_sid": conversation_sid or self.conversation_sid, + } + self._context: Optional[UserConversationContext] = None + + @property + def _proxy(self) -> "UserConversationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserConversationContext for this UserConversationInstance + """ + if self._context is None: + self._context = UserConversationContext( + self._version, + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserConversationInstance": + """ + Fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserConversationInstance": + """ + Asynchronous coroutine to fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> "UserConversationInstance": + """ + Update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + return self._proxy.update( + notification_level=notification_level, + last_read_timestamp=last_read_timestamp, + last_read_message_index=last_read_message_index, + ) + + async def update_async( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> "UserConversationInstance": + """ + Asynchronous coroutine to update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + return await self._proxy.update_async( + notification_level=notification_level, + last_read_timestamp=last_read_timestamp, + last_read_message_index=last_read_message_index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserConversationContext(InstanceContext): + + def __init__(self, version: Version, user_sid: str, conversation_sid: str): + """ + Initialize the UserConversationContext + + :param version: Version that contains the resource + :param user_sid: The unique SID identifier of the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). This value can be either the `sid` or the `identity` of the User resource. + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + super().__init__(version) + + # Path Solution + self._solution = { + "user_sid": user_sid, + "conversation_sid": conversation_sid, + } + self._uri = "/Users/{user_sid}/Conversations/{conversation_sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserConversationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserConversationInstance: + """ + Fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserConversationInstance( + self._version, + payload, + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def fetch_async(self) -> UserConversationInstance: + """ + Asynchronous coroutine to fetch the UserConversationInstance + + + :returns: The fetched UserConversationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserConversationInstance( + self._version, + payload, + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def update( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> UserConversationInstance: + """ + Update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastReadTimestamp": serialize.iso8601_datetime(last_read_timestamp), + "LastReadMessageIndex": last_read_message_index, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserConversationInstance( + self._version, + payload, + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + async def update_async( + self, + notification_level: Union[ + "UserConversationInstance.NotificationLevel", object + ] = values.unset, + last_read_timestamp: Union[datetime, object] = values.unset, + last_read_message_index: Union[int, object] = values.unset, + ) -> UserConversationInstance: + """ + Asynchronous coroutine to update the UserConversationInstance + + :param notification_level: + :param last_read_timestamp: The date of the last message read in conversation by the user, given in ISO 8601 format. + :param last_read_message_index: The index of the last Message in the Conversation that the Participant has read. + + :returns: The updated UserConversationInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastReadTimestamp": serialize.iso8601_datetime(last_read_timestamp), + "LastReadMessageIndex": last_read_message_index, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserConversationInstance( + self._version, + payload, + user_sid=self._solution["user_sid"], + conversation_sid=self._solution["conversation_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserConversationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserConversationInstance: + """ + Build an instance of UserConversationInstance + + :param payload: Payload response from the API + """ + return UserConversationInstance( + self._version, payload, user_sid=self._solution["user_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserConversationList(ListResource): + + def __init__(self, version: Version, user_sid: str): + """ + Initialize the UserConversationList + + :param version: Version that contains the resource + :param user_sid: The unique SID identifier of the [User resource](https://www.twilio.com/docs/conversations/api/user-resource). This value can be either the `sid` or the `identity` of the User resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "user_sid": user_sid, + } + self._uri = "/Users/{user_sid}/Conversations".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserConversationInstance]: + """ + Streams UserConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserConversationInstance]: + """ + Asynchronously streams UserConversationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserConversationInstance]: + """ + Lists UserConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserConversationInstance]: + """ + Asynchronously lists UserConversationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserConversationPage: + """ + Retrieve a single page of UserConversationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserConversationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserConversationPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserConversationPage: + """ + Asynchronously retrieve a single page of UserConversationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserConversationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserConversationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserConversationPage: + """ + Retrieve a specific page of UserConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserConversationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserConversationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserConversationPage: + """ + Asynchronously retrieve a specific page of UserConversationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserConversationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserConversationPage(self._version, response, self._solution) + + def get(self, conversation_sid: str) -> UserConversationContext: + """ + Constructs a UserConversationContext + + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + return UserConversationContext( + self._version, + user_sid=self._solution["user_sid"], + conversation_sid=conversation_sid, + ) + + def __call__(self, conversation_sid: str) -> UserConversationContext: + """ + Constructs a UserConversationContext + + :param conversation_sid: The unique SID identifier of the Conversation. This value can be either the `sid` or the `unique_name` of the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource). + """ + return UserConversationContext( + self._version, + user_sid=self._solution["user_sid"], + conversation_sid=conversation_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/EventsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/EventsBase.py new file mode 100644 index 0000000..eb2251a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/EventsBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.events.v1 import V1 + + +class EventsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Events Domain + + :returns: Domain for Events + """ + super().__init__(twilio, "https://events.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Events + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__init__.py new file mode 100644 index 0000000..c359cfe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__init__.py @@ -0,0 +1,45 @@ +from warnings import warn + +from twilio.rest.events.EventsBase import EventsBase +from twilio.rest.events.v1.event_type import EventTypeList +from twilio.rest.events.v1.schema import SchemaList +from twilio.rest.events.v1.sink import SinkList +from twilio.rest.events.v1.subscription import SubscriptionList + + +class Events(EventsBase): + @property + def event_types(self) -> EventTypeList: + warn( + "event_types is deprecated. Use v1.event_types instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.event_types + + @property + def schemas(self) -> SchemaList: + warn( + "schemas is deprecated. Use v1.schemas instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.schemas + + @property + def sinks(self) -> SinkList: + warn( + "sinks is deprecated. Use v1.sinks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.sinks + + @property + def subscriptions(self) -> SubscriptionList: + warn( + "subscriptions is deprecated. Use v1.subscriptions instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.subscriptions diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/EventsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/EventsBase.cpython-310.pyc new file mode 100644 index 0000000..f959e81 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/EventsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ff388a9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__init__.py new file mode 100644 index 0000000..6b91355 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__init__.py @@ -0,0 +1,67 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.events.v1.event_type import EventTypeList +from twilio.rest.events.v1.schema import SchemaList +from twilio.rest.events.v1.sink import SinkList +from twilio.rest.events.v1.subscription import SubscriptionList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Events + + :param domain: The Twilio.events domain + """ + super().__init__(domain, "v1") + self._event_types: Optional[EventTypeList] = None + self._schemas: Optional[SchemaList] = None + self._sinks: Optional[SinkList] = None + self._subscriptions: Optional[SubscriptionList] = None + + @property + def event_types(self) -> EventTypeList: + if self._event_types is None: + self._event_types = EventTypeList(self) + return self._event_types + + @property + def schemas(self) -> SchemaList: + if self._schemas is None: + self._schemas = SchemaList(self) + return self._schemas + + @property + def sinks(self) -> SinkList: + if self._sinks is None: + self._sinks = SinkList(self) + return self._sinks + + @property + def subscriptions(self) -> SubscriptionList: + if self._subscriptions is None: + self._subscriptions = SubscriptionList(self) + return self._subscriptions + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a249292 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/event_type.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/event_type.cpython-310.pyc new file mode 100644 index 0000000..dca5f57 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/__pycache__/event_type.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/event_type.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/event_type.py new file mode 100644 index 0000000..7215df8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/event_type.py @@ -0,0 +1,419 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EventTypeInstance(InstanceResource): + """ + :ivar type: A string that uniquely identifies this Event Type. + :ivar schema_id: A string that uniquely identifies the Schema this Event Type adheres to. + :ivar date_created: The date that this Event Type was created, given in ISO 8601 format. + :ivar date_updated: The date that this Event Type was updated, given in ISO 8601 format. + :ivar description: A human readable description for this Event Type. + :ivar url: The URL of this resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], type: Optional[str] = None + ): + super().__init__(version) + + self.type: Optional[str] = payload.get("type") + self.schema_id: Optional[str] = payload.get("schema_id") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.description: Optional[str] = payload.get("description") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "type": type or self.type, + } + self._context: Optional[EventTypeContext] = None + + @property + def _proxy(self) -> "EventTypeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EventTypeContext for this EventTypeInstance + """ + if self._context is None: + self._context = EventTypeContext( + self._version, + type=self._solution["type"], + ) + return self._context + + def fetch(self) -> "EventTypeInstance": + """ + Fetch the EventTypeInstance + + + :returns: The fetched EventTypeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EventTypeInstance": + """ + Asynchronous coroutine to fetch the EventTypeInstance + + + :returns: The fetched EventTypeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventTypeContext(InstanceContext): + + def __init__(self, version: Version, type: str): + """ + Initialize the EventTypeContext + + :param version: Version that contains the resource + :param type: A string that uniquely identifies this Event Type. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "type": type, + } + self._uri = "/Types/{type}".format(**self._solution) + + def fetch(self) -> EventTypeInstance: + """ + Fetch the EventTypeInstance + + + :returns: The fetched EventTypeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EventTypeInstance( + self._version, + payload, + type=self._solution["type"], + ) + + async def fetch_async(self) -> EventTypeInstance: + """ + Asynchronous coroutine to fetch the EventTypeInstance + + + :returns: The fetched EventTypeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EventTypeInstance( + self._version, + payload, + type=self._solution["type"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventTypePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EventTypeInstance: + """ + Build an instance of EventTypeInstance + + :param payload: Payload response from the API + """ + return EventTypeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EventTypeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EventTypeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Types" + + def stream( + self, + schema_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EventTypeInstance]: + """ + Streams EventTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(schema_id=schema_id, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + schema_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EventTypeInstance]: + """ + Asynchronously streams EventTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(schema_id=schema_id, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + schema_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventTypeInstance]: + """ + Lists EventTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + schema_id=schema_id, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + schema_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventTypeInstance]: + """ + Asynchronously lists EventTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + schema_id=schema_id, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + schema_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventTypePage: + """ + Retrieve a single page of EventTypeInstance records from the API. + Request is executed immediately + + :param schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventTypeInstance + """ + data = values.of( + { + "SchemaId": schema_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EventTypePage(self._version, response) + + async def page_async( + self, + schema_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventTypePage: + """ + Asynchronously retrieve a single page of EventTypeInstance records from the API. + Request is executed immediately + + :param schema_id: A string parameter filtering the results to return only the Event Types using a given schema. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventTypeInstance + """ + data = values.of( + { + "SchemaId": schema_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EventTypePage(self._version, response) + + def get_page(self, target_url: str) -> EventTypePage: + """ + Retrieve a specific page of EventTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventTypeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EventTypePage(self._version, response) + + async def get_page_async(self, target_url: str) -> EventTypePage: + """ + Asynchronously retrieve a specific page of EventTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventTypeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EventTypePage(self._version, response) + + def get(self, type: str) -> EventTypeContext: + """ + Constructs a EventTypeContext + + :param type: A string that uniquely identifies this Event Type. + """ + return EventTypeContext(self._version, type=type) + + def __call__(self, type: str) -> EventTypeContext: + """ + Constructs a EventTypeContext + + :param type: A string that uniquely identifies this Event Type. + """ + return EventTypeContext(self._version, type=type) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__init__.py new file mode 100644 index 0000000..c3b6593 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__init__.py @@ -0,0 +1,217 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.events.v1.schema.schema_version import SchemaVersionList + + +class SchemaInstance(InstanceResource): + """ + :ivar id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this schema. + :ivar latest_version_date_created: The date that the latest schema version was created, given in ISO 8601 format. + :ivar latest_version: The latest version published of this schema. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], id: Optional[str] = None + ): + super().__init__(version) + + self.id: Optional[str] = payload.get("id") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.latest_version_date_created: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("latest_version_date_created")) + ) + self.latest_version: Optional[int] = deserialize.integer( + payload.get("latest_version") + ) + + self._solution = { + "id": id or self.id, + } + self._context: Optional[SchemaContext] = None + + @property + def _proxy(self) -> "SchemaContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SchemaContext for this SchemaInstance + """ + if self._context is None: + self._context = SchemaContext( + self._version, + id=self._solution["id"], + ) + return self._context + + def fetch(self) -> "SchemaInstance": + """ + Fetch the SchemaInstance + + + :returns: The fetched SchemaInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SchemaInstance": + """ + Asynchronous coroutine to fetch the SchemaInstance + + + :returns: The fetched SchemaInstance + """ + return await self._proxy.fetch_async() + + @property + def versions(self) -> SchemaVersionList: + """ + Access the versions + """ + return self._proxy.versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SchemaContext(InstanceContext): + + def __init__(self, version: Version, id: str): + """ + Initialize the SchemaContext + + :param version: Version that contains the resource + :param id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "id": id, + } + self._uri = "/Schemas/{id}".format(**self._solution) + + self._versions: Optional[SchemaVersionList] = None + + def fetch(self) -> SchemaInstance: + """ + Fetch the SchemaInstance + + + :returns: The fetched SchemaInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SchemaInstance( + self._version, + payload, + id=self._solution["id"], + ) + + async def fetch_async(self) -> SchemaInstance: + """ + Asynchronous coroutine to fetch the SchemaInstance + + + :returns: The fetched SchemaInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SchemaInstance( + self._version, + payload, + id=self._solution["id"], + ) + + @property + def versions(self) -> SchemaVersionList: + """ + Access the versions + """ + if self._versions is None: + self._versions = SchemaVersionList( + self._version, + self._solution["id"], + ) + return self._versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SchemaList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SchemaList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, id: str) -> SchemaContext: + """ + Constructs a SchemaContext + + :param id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + """ + return SchemaContext(self._version, id=id) + + def __call__(self, id: str) -> SchemaContext: + """ + Constructs a SchemaContext + + :param id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + """ + return SchemaContext(self._version, id=id) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..083c5ae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/schema_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/schema_version.cpython-310.pyc new file mode 100644 index 0000000..84338d4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/__pycache__/schema_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/schema_version.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/schema_version.py new file mode 100644 index 0000000..3557cb2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/schema/schema_version.py @@ -0,0 +1,418 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SchemaVersionInstance(InstanceResource): + """ + :ivar id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + :ivar schema_version: The version of this schema. + :ivar date_created: The date the schema version was created, given in ISO 8601 format. + :ivar url: The URL of this resource. + :ivar raw: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + id: str, + schema_version: Optional[int] = None, + ): + super().__init__(version) + + self.id: Optional[str] = payload.get("id") + self.schema_version: Optional[int] = deserialize.integer( + payload.get("schema_version") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + self.raw: Optional[str] = payload.get("raw") + + self._solution = { + "id": id, + "schema_version": schema_version or self.schema_version, + } + self._context: Optional[SchemaVersionContext] = None + + @property + def _proxy(self) -> "SchemaVersionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SchemaVersionContext for this SchemaVersionInstance + """ + if self._context is None: + self._context = SchemaVersionContext( + self._version, + id=self._solution["id"], + schema_version=self._solution["schema_version"], + ) + return self._context + + def fetch(self) -> "SchemaVersionInstance": + """ + Fetch the SchemaVersionInstance + + + :returns: The fetched SchemaVersionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SchemaVersionInstance": + """ + Asynchronous coroutine to fetch the SchemaVersionInstance + + + :returns: The fetched SchemaVersionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SchemaVersionContext(InstanceContext): + + def __init__(self, version: Version, id: str, schema_version: int): + """ + Initialize the SchemaVersionContext + + :param version: Version that contains the resource + :param id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + :param schema_version: The version of the schema + """ + super().__init__(version) + + # Path Solution + self._solution = { + "id": id, + "schema_version": schema_version, + } + self._uri = "/Schemas/{id}/Versions/{schema_version}".format(**self._solution) + + def fetch(self) -> SchemaVersionInstance: + """ + Fetch the SchemaVersionInstance + + + :returns: The fetched SchemaVersionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SchemaVersionInstance( + self._version, + payload, + id=self._solution["id"], + schema_version=self._solution["schema_version"], + ) + + async def fetch_async(self) -> SchemaVersionInstance: + """ + Asynchronous coroutine to fetch the SchemaVersionInstance + + + :returns: The fetched SchemaVersionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SchemaVersionInstance( + self._version, + payload, + id=self._solution["id"], + schema_version=self._solution["schema_version"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SchemaVersionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SchemaVersionInstance: + """ + Build an instance of SchemaVersionInstance + + :param payload: Payload response from the API + """ + return SchemaVersionInstance(self._version, payload, id=self._solution["id"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SchemaVersionList(ListResource): + + def __init__(self, version: Version, id: str): + """ + Initialize the SchemaVersionList + + :param version: Version that contains the resource + :param id: The unique identifier of the schema. Each schema can have multiple versions, that share the same id. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "id": id, + } + self._uri = "/Schemas/{id}/Versions".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SchemaVersionInstance]: + """ + Streams SchemaVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SchemaVersionInstance]: + """ + Asynchronously streams SchemaVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SchemaVersionInstance]: + """ + Lists SchemaVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SchemaVersionInstance]: + """ + Asynchronously lists SchemaVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SchemaVersionPage: + """ + Retrieve a single page of SchemaVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SchemaVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SchemaVersionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SchemaVersionPage: + """ + Asynchronously retrieve a single page of SchemaVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SchemaVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SchemaVersionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SchemaVersionPage: + """ + Retrieve a specific page of SchemaVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SchemaVersionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SchemaVersionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SchemaVersionPage: + """ + Asynchronously retrieve a specific page of SchemaVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SchemaVersionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SchemaVersionPage(self._version, response, self._solution) + + def get(self, schema_version: int) -> SchemaVersionContext: + """ + Constructs a SchemaVersionContext + + :param schema_version: The version of the schema + """ + return SchemaVersionContext( + self._version, id=self._solution["id"], schema_version=schema_version + ) + + def __call__(self, schema_version: int) -> SchemaVersionContext: + """ + Constructs a SchemaVersionContext + + :param schema_version: The version of the schema + """ + return SchemaVersionContext( + self._version, id=self._solution["id"], schema_version=schema_version + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__init__.py new file mode 100644 index 0000000..b09b5ed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__init__.py @@ -0,0 +1,672 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.events.v1.sink.sink_test import SinkTestList +from twilio.rest.events.v1.sink.sink_validate import SinkValidateList + + +class SinkInstance(InstanceResource): + + class SinkType(object): + KINESIS = "kinesis" + WEBHOOK = "webhook" + SEGMENT = "segment" + + class Status(object): + INITIALIZED = "initialized" + VALIDATING = "validating" + ACTIVE = "active" + FAILED = "failed" + + """ + :ivar date_created: The date that this Sink was created, given in ISO 8601 format. + :ivar date_updated: The date that this Sink was updated, given in ISO 8601 format. + :ivar description: A human readable description for the Sink + :ivar sid: A 34 character string that uniquely identifies this Sink. + :ivar sink_configuration: The information required for Twilio to connect to the provided Sink encoded as JSON. + :ivar sink_type: + :ivar status: + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Sink. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.description: Optional[str] = payload.get("description") + self.sid: Optional[str] = payload.get("sid") + self.sink_configuration: Optional[Dict[str, object]] = payload.get( + "sink_configuration" + ) + self.sink_type: Optional["SinkInstance.SinkType"] = payload.get("sink_type") + self.status: Optional["SinkInstance.Status"] = payload.get("status") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SinkContext] = None + + @property + def _proxy(self) -> "SinkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SinkContext for this SinkInstance + """ + if self._context is None: + self._context = SinkContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SinkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SinkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SinkInstance": + """ + Fetch the SinkInstance + + + :returns: The fetched SinkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SinkInstance": + """ + Asynchronous coroutine to fetch the SinkInstance + + + :returns: The fetched SinkInstance + """ + return await self._proxy.fetch_async() + + def update(self, description: str) -> "SinkInstance": + """ + Update the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + + :returns: The updated SinkInstance + """ + return self._proxy.update( + description=description, + ) + + async def update_async(self, description: str) -> "SinkInstance": + """ + Asynchronous coroutine to update the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + + :returns: The updated SinkInstance + """ + return await self._proxy.update_async( + description=description, + ) + + @property + def sink_test(self) -> SinkTestList: + """ + Access the sink_test + """ + return self._proxy.sink_test + + @property + def sink_validate(self) -> SinkValidateList: + """ + Access the sink_validate + """ + return self._proxy.sink_validate + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SinkContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SinkContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this Sink. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sinks/{sid}".format(**self._solution) + + self._sink_test: Optional[SinkTestList] = None + self._sink_validate: Optional[SinkValidateList] = None + + def delete(self) -> bool: + """ + Deletes the SinkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SinkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SinkInstance: + """ + Fetch the SinkInstance + + + :returns: The fetched SinkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SinkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SinkInstance: + """ + Asynchronous coroutine to fetch the SinkInstance + + + :returns: The fetched SinkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SinkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, description: str) -> SinkInstance: + """ + Update the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + + :returns: The updated SinkInstance + """ + data = values.of( + { + "Description": description, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async(self, description: str) -> SinkInstance: + """ + Asynchronous coroutine to update the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + + :returns: The updated SinkInstance + """ + data = values.of( + { + "Description": description, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def sink_test(self) -> SinkTestList: + """ + Access the sink_test + """ + if self._sink_test is None: + self._sink_test = SinkTestList( + self._version, + self._solution["sid"], + ) + return self._sink_test + + @property + def sink_validate(self) -> SinkValidateList: + """ + Access the sink_validate + """ + if self._sink_validate is None: + self._sink_validate = SinkValidateList( + self._version, + self._solution["sid"], + ) + return self._sink_validate + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SinkPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SinkInstance: + """ + Build an instance of SinkInstance + + :param payload: Payload response from the API + """ + return SinkInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SinkList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SinkList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Sinks" + + def create( + self, + description: str, + sink_configuration: object, + sink_type: "SinkInstance.SinkType", + ) -> SinkInstance: + """ + Create the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + :param sink_configuration: The information required for Twilio to connect to the provided Sink encoded as JSON. + :param sink_type: + + :returns: The created SinkInstance + """ + + data = values.of( + { + "Description": description, + "SinkConfiguration": serialize.object(sink_configuration), + "SinkType": sink_type, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkInstance(self._version, payload) + + async def create_async( + self, + description: str, + sink_configuration: object, + sink_type: "SinkInstance.SinkType", + ) -> SinkInstance: + """ + Asynchronously create the SinkInstance + + :param description: A human readable description for the Sink **This value should not contain PII.** + :param sink_configuration: The information required for Twilio to connect to the provided Sink encoded as JSON. + :param sink_type: + + :returns: The created SinkInstance + """ + + data = values.of( + { + "Description": description, + "SinkConfiguration": serialize.object(sink_configuration), + "SinkType": sink_type, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkInstance(self._version, payload) + + def stream( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SinkInstance]: + """ + Streams SinkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param str status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(in_use=in_use, status=status, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SinkInstance]: + """ + Asynchronously streams SinkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param str status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + in_use=in_use, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SinkInstance]: + """ + Lists SinkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param str status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + in_use=in_use, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SinkInstance]: + """ + Asynchronously lists SinkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param str status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + in_use=in_use, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SinkPage: + """ + Retrieve a single page of SinkInstance records from the API. + Request is executed immediately + + :param in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SinkInstance + """ + data = values.of( + { + "InUse": serialize.boolean_to_string(in_use), + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SinkPage(self._version, response) + + async def page_async( + self, + in_use: Union[bool, object] = values.unset, + status: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SinkPage: + """ + Asynchronously retrieve a single page of SinkInstance records from the API. + Request is executed immediately + + :param in_use: A boolean query parameter filtering the results to return sinks used/not used by a subscription. + :param status: A String query parameter filtering the results by status `initialized`, `validating`, `active` or `failed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SinkInstance + """ + data = values.of( + { + "InUse": serialize.boolean_to_string(in_use), + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SinkPage(self._version, response) + + def get_page(self, target_url: str) -> SinkPage: + """ + Retrieve a specific page of SinkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SinkInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SinkPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SinkPage: + """ + Asynchronously retrieve a specific page of SinkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SinkInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SinkPage(self._version, response) + + def get(self, sid: str) -> SinkContext: + """ + Constructs a SinkContext + + :param sid: A 34 character string that uniquely identifies this Sink. + """ + return SinkContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SinkContext: + """ + Constructs a SinkContext + + :param sid: A 34 character string that uniquely identifies this Sink. + """ + return SinkContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..965040a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_test.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_test.cpython-310.pyc new file mode 100644 index 0000000..cb7b101 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_test.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_validate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_validate.cpython-310.pyc new file mode 100644 index 0000000..4cfa97d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/__pycache__/sink_validate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_test.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_test.py new file mode 100644 index 0000000..adf04f5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_test.py @@ -0,0 +1,100 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SinkTestInstance(InstanceResource): + """ + :ivar result: Feedback indicating whether the test event was generated. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sid: str): + super().__init__(version) + + self.result: Optional[str] = payload.get("result") + + self._solution = { + "sid": sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SinkTestList(ListResource): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SinkTestList + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies the Sink to be Tested. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sinks/{sid}/Test".format(**self._solution) + + def create(self) -> SinkTestInstance: + """ + Create the SinkTestInstance + + + :returns: The created SinkTestInstance + """ + + payload = self._version.create( + method="POST", + uri=self._uri, + ) + + return SinkTestInstance(self._version, payload, sid=self._solution["sid"]) + + async def create_async(self) -> SinkTestInstance: + """ + Asynchronously create the SinkTestInstance + + + :returns: The created SinkTestInstance + """ + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + ) + + return SinkTestInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_validate.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_validate.py new file mode 100644 index 0000000..4c24dd7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/sink/sink_validate.py @@ -0,0 +1,117 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SinkValidateInstance(InstanceResource): + """ + :ivar result: Feedback indicating whether the given Sink was validated. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sid: str): + super().__init__(version) + + self.result: Optional[str] = payload.get("result") + + self._solution = { + "sid": sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SinkValidateList(ListResource): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SinkValidateList + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies the Sink being validated. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sinks/{sid}/Validate".format(**self._solution) + + def create(self, test_id: str) -> SinkValidateInstance: + """ + Create the SinkValidateInstance + + :param test_id: A 34 character string that uniquely identifies the test event for a Sink being validated. + + :returns: The created SinkValidateInstance + """ + + data = values.of( + { + "TestId": test_id, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkValidateInstance(self._version, payload, sid=self._solution["sid"]) + + async def create_async(self, test_id: str) -> SinkValidateInstance: + """ + Asynchronously create the SinkValidateInstance + + :param test_id: A 34 character string that uniquely identifies the test event for a Sink being validated. + + :returns: The created SinkValidateInstance + """ + + data = values.of( + { + "TestId": test_id, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SinkValidateInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__init__.py new file mode 100644 index 0000000..666b26a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__init__.py @@ -0,0 +1,635 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.events.v1.subscription.subscribed_event import SubscribedEventList + + +class SubscriptionInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar sid: A 34 character string that uniquely identifies this Subscription. + :ivar date_created: The date that this Subscription was created, given in ISO 8601 format. + :ivar date_updated: The date that this Subscription was updated, given in ISO 8601 format. + :ivar description: A human readable description for the Subscription + :ivar sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Subscription. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.description: Optional[str] = payload.get("description") + self.sink_sid: Optional[str] = payload.get("sink_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SubscriptionContext] = None + + @property + def _proxy(self) -> "SubscriptionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SubscriptionContext for this SubscriptionInstance + """ + if self._context is None: + self._context = SubscriptionContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SubscriptionInstance": + """ + Fetch the SubscriptionInstance + + + :returns: The fetched SubscriptionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SubscriptionInstance": + """ + Asynchronous coroutine to fetch the SubscriptionInstance + + + :returns: The fetched SubscriptionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + description: Union[str, object] = values.unset, + sink_sid: Union[str, object] = values.unset, + ) -> "SubscriptionInstance": + """ + Update the SubscriptionInstance + + :param description: A human readable description for the Subscription. + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + + :returns: The updated SubscriptionInstance + """ + return self._proxy.update( + description=description, + sink_sid=sink_sid, + ) + + async def update_async( + self, + description: Union[str, object] = values.unset, + sink_sid: Union[str, object] = values.unset, + ) -> "SubscriptionInstance": + """ + Asynchronous coroutine to update the SubscriptionInstance + + :param description: A human readable description for the Subscription. + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + + :returns: The updated SubscriptionInstance + """ + return await self._proxy.update_async( + description=description, + sink_sid=sink_sid, + ) + + @property + def subscribed_events(self) -> SubscribedEventList: + """ + Access the subscribed_events + """ + return self._proxy.subscribed_events + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscriptionContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SubscriptionContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this Subscription. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Subscriptions/{sid}".format(**self._solution) + + self._subscribed_events: Optional[SubscribedEventList] = None + + def delete(self) -> bool: + """ + Deletes the SubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SubscriptionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SubscriptionInstance: + """ + Fetch the SubscriptionInstance + + + :returns: The fetched SubscriptionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SubscriptionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SubscriptionInstance: + """ + Asynchronous coroutine to fetch the SubscriptionInstance + + + :returns: The fetched SubscriptionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SubscriptionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + description: Union[str, object] = values.unset, + sink_sid: Union[str, object] = values.unset, + ) -> SubscriptionInstance: + """ + Update the SubscriptionInstance + + :param description: A human readable description for the Subscription. + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + + :returns: The updated SubscriptionInstance + """ + data = values.of( + { + "Description": description, + "SinkSid": sink_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscriptionInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + description: Union[str, object] = values.unset, + sink_sid: Union[str, object] = values.unset, + ) -> SubscriptionInstance: + """ + Asynchronous coroutine to update the SubscriptionInstance + + :param description: A human readable description for the Subscription. + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + + :returns: The updated SubscriptionInstance + """ + data = values.of( + { + "Description": description, + "SinkSid": sink_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscriptionInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def subscribed_events(self) -> SubscribedEventList: + """ + Access the subscribed_events + """ + if self._subscribed_events is None: + self._subscribed_events = SubscribedEventList( + self._version, + self._solution["sid"], + ) + return self._subscribed_events + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscriptionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SubscriptionInstance: + """ + Build an instance of SubscriptionInstance + + :param payload: Payload response from the API + """ + return SubscriptionInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SubscriptionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SubscriptionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Subscriptions" + + def create( + self, description: str, sink_sid: str, types: List[object] + ) -> SubscriptionInstance: + """ + Create the SubscriptionInstance + + :param description: A human readable description for the Subscription **This value should not contain PII.** + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + :param types: An array of objects containing the subscribed Event Types + + :returns: The created SubscriptionInstance + """ + + data = values.of( + { + "Description": description, + "SinkSid": sink_sid, + "Types": serialize.map(types, lambda e: serialize.object(e)), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscriptionInstance(self._version, payload) + + async def create_async( + self, description: str, sink_sid: str, types: List[object] + ) -> SubscriptionInstance: + """ + Asynchronously create the SubscriptionInstance + + :param description: A human readable description for the Subscription **This value should not contain PII.** + :param sink_sid: The SID of the sink that events selected by this subscription should be sent to. Sink must be active for the subscription to be created. + :param types: An array of objects containing the subscribed Event Types + + :returns: The created SubscriptionInstance + """ + + data = values.of( + { + "Description": description, + "SinkSid": sink_sid, + "Types": serialize.map(types, lambda e: serialize.object(e)), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscriptionInstance(self._version, payload) + + def stream( + self, + sink_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SubscriptionInstance]: + """ + Streams SubscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(sink_sid=sink_sid, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sink_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SubscriptionInstance]: + """ + Asynchronously streams SubscriptionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(sink_sid=sink_sid, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sink_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscriptionInstance]: + """ + Lists SubscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sink_sid=sink_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sink_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscriptionInstance]: + """ + Asynchronously lists SubscriptionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sink_sid=sink_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sink_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscriptionPage: + """ + Retrieve a single page of SubscriptionInstance records from the API. + Request is executed immediately + + :param sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscriptionInstance + """ + data = values.of( + { + "SinkSid": sink_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SubscriptionPage(self._version, response) + + async def page_async( + self, + sink_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscriptionPage: + """ + Asynchronously retrieve a single page of SubscriptionInstance records from the API. + Request is executed immediately + + :param sink_sid: The SID of the sink that the list of Subscriptions should be filtered by. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscriptionInstance + """ + data = values.of( + { + "SinkSid": sink_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SubscriptionPage(self._version, response) + + def get_page(self, target_url: str) -> SubscriptionPage: + """ + Retrieve a specific page of SubscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscriptionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SubscriptionPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SubscriptionPage: + """ + Asynchronously retrieve a specific page of SubscriptionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscriptionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SubscriptionPage(self._version, response) + + def get(self, sid: str) -> SubscriptionContext: + """ + Constructs a SubscriptionContext + + :param sid: A 34 character string that uniquely identifies this Subscription. + """ + return SubscriptionContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SubscriptionContext: + """ + Constructs a SubscriptionContext + + :param sid: A 34 character string that uniquely identifies this Subscription. + """ + return SubscriptionContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4f9bb2a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/subscribed_event.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/subscribed_event.cpython-310.pyc new file mode 100644 index 0000000..c4458f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/__pycache__/subscribed_event.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/subscribed_event.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/subscribed_event.py new file mode 100644 index 0000000..80c9ce1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/events/v1/subscription/subscribed_event.py @@ -0,0 +1,611 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Events + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SubscribedEventInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar type: Type of event being subscribed to. + :ivar schema_version: The schema version that the Subscription should use. + :ivar subscription_sid: The unique SID identifier of the Subscription. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + subscription_sid: str, + type: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.type: Optional[str] = payload.get("type") + self.schema_version: Optional[int] = deserialize.integer( + payload.get("schema_version") + ) + self.subscription_sid: Optional[str] = payload.get("subscription_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "subscription_sid": subscription_sid, + "type": type or self.type, + } + self._context: Optional[SubscribedEventContext] = None + + @property + def _proxy(self) -> "SubscribedEventContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SubscribedEventContext for this SubscribedEventInstance + """ + if self._context is None: + self._context = SubscribedEventContext( + self._version, + subscription_sid=self._solution["subscription_sid"], + type=self._solution["type"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SubscribedEventInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SubscribedEventInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SubscribedEventInstance": + """ + Fetch the SubscribedEventInstance + + + :returns: The fetched SubscribedEventInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SubscribedEventInstance": + """ + Asynchronous coroutine to fetch the SubscribedEventInstance + + + :returns: The fetched SubscribedEventInstance + """ + return await self._proxy.fetch_async() + + def update( + self, schema_version: Union[int, object] = values.unset + ) -> "SubscribedEventInstance": + """ + Update the SubscribedEventInstance + + :param schema_version: The schema version that the Subscription should use. + + :returns: The updated SubscribedEventInstance + """ + return self._proxy.update( + schema_version=schema_version, + ) + + async def update_async( + self, schema_version: Union[int, object] = values.unset + ) -> "SubscribedEventInstance": + """ + Asynchronous coroutine to update the SubscribedEventInstance + + :param schema_version: The schema version that the Subscription should use. + + :returns: The updated SubscribedEventInstance + """ + return await self._proxy.update_async( + schema_version=schema_version, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscribedEventContext(InstanceContext): + + def __init__(self, version: Version, subscription_sid: str, type: str): + """ + Initialize the SubscribedEventContext + + :param version: Version that contains the resource + :param subscription_sid: The unique SID identifier of the Subscription. + :param type: Type of event being subscribed to. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "subscription_sid": subscription_sid, + "type": type, + } + self._uri = "/Subscriptions/{subscription_sid}/SubscribedEvents/{type}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the SubscribedEventInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SubscribedEventInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SubscribedEventInstance: + """ + Fetch the SubscribedEventInstance + + + :returns: The fetched SubscribedEventInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SubscribedEventInstance( + self._version, + payload, + subscription_sid=self._solution["subscription_sid"], + type=self._solution["type"], + ) + + async def fetch_async(self) -> SubscribedEventInstance: + """ + Asynchronous coroutine to fetch the SubscribedEventInstance + + + :returns: The fetched SubscribedEventInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SubscribedEventInstance( + self._version, + payload, + subscription_sid=self._solution["subscription_sid"], + type=self._solution["type"], + ) + + def update( + self, schema_version: Union[int, object] = values.unset + ) -> SubscribedEventInstance: + """ + Update the SubscribedEventInstance + + :param schema_version: The schema version that the Subscription should use. + + :returns: The updated SubscribedEventInstance + """ + data = values.of( + { + "SchemaVersion": schema_version, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribedEventInstance( + self._version, + payload, + subscription_sid=self._solution["subscription_sid"], + type=self._solution["type"], + ) + + async def update_async( + self, schema_version: Union[int, object] = values.unset + ) -> SubscribedEventInstance: + """ + Asynchronous coroutine to update the SubscribedEventInstance + + :param schema_version: The schema version that the Subscription should use. + + :returns: The updated SubscribedEventInstance + """ + data = values.of( + { + "SchemaVersion": schema_version, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribedEventInstance( + self._version, + payload, + subscription_sid=self._solution["subscription_sid"], + type=self._solution["type"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscribedEventPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SubscribedEventInstance: + """ + Build an instance of SubscribedEventInstance + + :param payload: Payload response from the API + """ + return SubscribedEventInstance( + self._version, payload, subscription_sid=self._solution["subscription_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SubscribedEventList(ListResource): + + def __init__(self, version: Version, subscription_sid: str): + """ + Initialize the SubscribedEventList + + :param version: Version that contains the resource + :param subscription_sid: The unique SID identifier of the Subscription. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "subscription_sid": subscription_sid, + } + self._uri = "/Subscriptions/{subscription_sid}/SubscribedEvents".format( + **self._solution + ) + + def create( + self, type: str, schema_version: Union[int, object] = values.unset + ) -> SubscribedEventInstance: + """ + Create the SubscribedEventInstance + + :param type: Type of event being subscribed to. + :param schema_version: The schema version that the Subscription should use. + + :returns: The created SubscribedEventInstance + """ + + data = values.of( + { + "Type": type, + "SchemaVersion": schema_version, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribedEventInstance( + self._version, payload, subscription_sid=self._solution["subscription_sid"] + ) + + async def create_async( + self, type: str, schema_version: Union[int, object] = values.unset + ) -> SubscribedEventInstance: + """ + Asynchronously create the SubscribedEventInstance + + :param type: Type of event being subscribed to. + :param schema_version: The schema version that the Subscription should use. + + :returns: The created SubscribedEventInstance + """ + + data = values.of( + { + "Type": type, + "SchemaVersion": schema_version, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribedEventInstance( + self._version, payload, subscription_sid=self._solution["subscription_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SubscribedEventInstance]: + """ + Streams SubscribedEventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SubscribedEventInstance]: + """ + Asynchronously streams SubscribedEventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscribedEventInstance]: + """ + Lists SubscribedEventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscribedEventInstance]: + """ + Asynchronously lists SubscribedEventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscribedEventPage: + """ + Retrieve a single page of SubscribedEventInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscribedEventInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SubscribedEventPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscribedEventPage: + """ + Asynchronously retrieve a single page of SubscribedEventInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscribedEventInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SubscribedEventPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SubscribedEventPage: + """ + Retrieve a specific page of SubscribedEventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscribedEventInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SubscribedEventPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SubscribedEventPage: + """ + Asynchronously retrieve a specific page of SubscribedEventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscribedEventInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SubscribedEventPage(self._version, response, self._solution) + + def get(self, type: str) -> SubscribedEventContext: + """ + Constructs a SubscribedEventContext + + :param type: Type of event being subscribed to. + """ + return SubscribedEventContext( + self._version, + subscription_sid=self._solution["subscription_sid"], + type=type, + ) + + def __call__(self, type: str) -> SubscribedEventContext: + """ + Constructs a SubscribedEventContext + + :param type: Type of event being subscribed to. + """ + return SubscribedEventContext( + self._version, + subscription_sid=self._solution["subscription_sid"], + type=type, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/FlexApiBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/FlexApiBase.py new file mode 100644 index 0000000..3bda5b6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/FlexApiBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.flex_api.v1 import V1 +from twilio.rest.flex_api.v2 import V2 + + +class FlexApiBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the FlexApi Domain + + :returns: Domain for FlexApi + """ + super().__init__(twilio, "https://flex-api.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of FlexApi + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of FlexApi + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__init__.py new file mode 100644 index 0000000..8f67951 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__init__.py @@ -0,0 +1,185 @@ +from warnings import warn + +from twilio.rest.flex_api.FlexApiBase import FlexApiBase +from twilio.rest.flex_api.v1.assessments import AssessmentsList +from twilio.rest.flex_api.v1.channel import ChannelList +from twilio.rest.flex_api.v1.configuration import ConfigurationList +from twilio.rest.flex_api.v1.flex_flow import FlexFlowList +from twilio.rest.flex_api.v1.insights_assessments_comment import ( + InsightsAssessmentsCommentList, +) +from twilio.rest.flex_api.v1.insights_conversations import InsightsConversationsList +from twilio.rest.flex_api.v1.insights_questionnaires import InsightsQuestionnairesList +from twilio.rest.flex_api.v1.insights_questionnaires_category import ( + InsightsQuestionnairesCategoryList, +) +from twilio.rest.flex_api.v1.insights_questionnaires_question import ( + InsightsQuestionnairesQuestionList, +) +from twilio.rest.flex_api.v1.insights_segments import InsightsSegmentsList +from twilio.rest.flex_api.v1.insights_session import InsightsSessionList +from twilio.rest.flex_api.v1.insights_settings_answer_sets import ( + InsightsSettingsAnswerSetsList, +) +from twilio.rest.flex_api.v1.insights_settings_comment import ( + InsightsSettingsCommentList, +) +from twilio.rest.flex_api.v1.insights_user_roles import InsightsUserRolesList +from twilio.rest.flex_api.v1.interaction import InteractionList +from twilio.rest.flex_api.v1.web_channel import WebChannelList +from twilio.rest.flex_api.v2.web_channels import WebChannelsList + + +class FlexApi(FlexApiBase): + @property + def assessments(self) -> AssessmentsList: + warn( + "assessments is deprecated. Use v1.assessments instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.assessments + + @property + def channel(self) -> ChannelList: + warn( + "channel is deprecated. Use v1.channel instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.channel + + @property + def configuration(self) -> ConfigurationList: + warn( + "configuration is deprecated. Use v1.configuration instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.configuration + + @property + def flex_flow(self) -> FlexFlowList: + warn( + "flex_flow is deprecated. Use v1.flex_flow instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.flex_flow + + @property + def insights_assessments_comment(self) -> InsightsAssessmentsCommentList: + warn( + "insights_assessments_comment is deprecated. Use v1.insights_assessments_comment instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_assessments_comment + + @property + def insights_conversations(self) -> InsightsConversationsList: + warn( + "insights_conversations is deprecated. Use v1.insights_conversations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_conversations + + @property + def insights_questionnaires(self) -> InsightsQuestionnairesList: + warn( + "insights_questionnaires is deprecated. Use v1.insights_questionnaires instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_questionnaires + + @property + def insights_questionnaires_category(self) -> InsightsQuestionnairesCategoryList: + warn( + "insights_questionnaires_category is deprecated. Use v1.insights_questionnaires_category instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_questionnaires_category + + @property + def insights_questionnaires_question(self) -> InsightsQuestionnairesQuestionList: + warn( + "insights_questionnaires_question is deprecated. Use v1.insights_questionnaires_question instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_questionnaires_question + + @property + def insights_segments(self) -> InsightsSegmentsList: + warn( + "insights_segments is deprecated. Use v1.insights_segments instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_segments + + @property + def insights_session(self) -> InsightsSessionList: + warn( + "insights_session is deprecated. Use v1.insights_session instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_session + + @property + def insights_settings_answer_sets(self) -> InsightsSettingsAnswerSetsList: + warn( + "insights_settings_answer_sets is deprecated. Use v1.insights_settings_answer_sets instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_settings_answer_sets + + @property + def insights_settings_comment(self) -> InsightsSettingsCommentList: + warn( + "insights_settings_comment is deprecated. Use v1.insights_settings_comment instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_settings_comment + + @property + def insights_user_roles(self) -> InsightsUserRolesList: + warn( + "insights_user_roles is deprecated. Use v1.insights_user_roles instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.insights_user_roles + + @property + def interaction(self) -> InteractionList: + warn( + "interaction is deprecated. Use v1.interaction instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.interaction + + @property + def web_channel(self) -> WebChannelList: + warn( + "web_channel is deprecated. Use v1.web_channel instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.web_channel + + @property + def web_channels(self) -> WebChannelsList: + warn( + "web_channels is deprecated. Use v2.web_channels instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.web_channels diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/FlexApiBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/FlexApiBase.cpython-310.pyc new file mode 100644 index 0000000..0eb64db Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/FlexApiBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e92d80e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__init__.py new file mode 100644 index 0000000..eeb0022 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__init__.py @@ -0,0 +1,245 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.flex_api.v1.assessments import AssessmentsList +from twilio.rest.flex_api.v1.channel import ChannelList +from twilio.rest.flex_api.v1.configuration import ConfigurationList +from twilio.rest.flex_api.v1.flex_flow import FlexFlowList +from twilio.rest.flex_api.v1.insights_assessments_comment import ( + InsightsAssessmentsCommentList, +) +from twilio.rest.flex_api.v1.insights_conversations import InsightsConversationsList +from twilio.rest.flex_api.v1.insights_questionnaires import InsightsQuestionnairesList +from twilio.rest.flex_api.v1.insights_questionnaires_category import ( + InsightsQuestionnairesCategoryList, +) +from twilio.rest.flex_api.v1.insights_questionnaires_question import ( + InsightsQuestionnairesQuestionList, +) +from twilio.rest.flex_api.v1.insights_segments import InsightsSegmentsList +from twilio.rest.flex_api.v1.insights_session import InsightsSessionList +from twilio.rest.flex_api.v1.insights_settings_answer_sets import ( + InsightsSettingsAnswerSetsList, +) +from twilio.rest.flex_api.v1.insights_settings_comment import ( + InsightsSettingsCommentList, +) +from twilio.rest.flex_api.v1.insights_user_roles import InsightsUserRolesList +from twilio.rest.flex_api.v1.interaction import InteractionList +from twilio.rest.flex_api.v1.plugin import PluginList +from twilio.rest.flex_api.v1.plugin_archive import PluginArchiveList +from twilio.rest.flex_api.v1.plugin_configuration import PluginConfigurationList +from twilio.rest.flex_api.v1.plugin_configuration_archive import ( + PluginConfigurationArchiveList, +) +from twilio.rest.flex_api.v1.plugin_release import PluginReleaseList +from twilio.rest.flex_api.v1.plugin_version_archive import PluginVersionArchiveList +from twilio.rest.flex_api.v1.provisioning_status import ProvisioningStatusList +from twilio.rest.flex_api.v1.web_channel import WebChannelList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of FlexApi + + :param domain: The Twilio.flex_api domain + """ + super().__init__(domain, "v1") + self._assessments: Optional[AssessmentsList] = None + self._channel: Optional[ChannelList] = None + self._configuration: Optional[ConfigurationList] = None + self._flex_flow: Optional[FlexFlowList] = None + self._insights_assessments_comment: Optional[InsightsAssessmentsCommentList] = ( + None + ) + self._insights_conversations: Optional[InsightsConversationsList] = None + self._insights_questionnaires: Optional[InsightsQuestionnairesList] = None + self._insights_questionnaires_category: Optional[ + InsightsQuestionnairesCategoryList + ] = None + self._insights_questionnaires_question: Optional[ + InsightsQuestionnairesQuestionList + ] = None + self._insights_segments: Optional[InsightsSegmentsList] = None + self._insights_session: Optional[InsightsSessionList] = None + self._insights_settings_answer_sets: Optional[ + InsightsSettingsAnswerSetsList + ] = None + self._insights_settings_comment: Optional[InsightsSettingsCommentList] = None + self._insights_user_roles: Optional[InsightsUserRolesList] = None + self._interaction: Optional[InteractionList] = None + self._plugins: Optional[PluginList] = None + self._plugin_archive: Optional[PluginArchiveList] = None + self._plugin_configurations: Optional[PluginConfigurationList] = None + self._plugin_configuration_archive: Optional[PluginConfigurationArchiveList] = ( + None + ) + self._plugin_releases: Optional[PluginReleaseList] = None + self._plugin_version_archive: Optional[PluginVersionArchiveList] = None + self._provisioning_status: Optional[ProvisioningStatusList] = None + self._web_channel: Optional[WebChannelList] = None + + @property + def assessments(self) -> AssessmentsList: + if self._assessments is None: + self._assessments = AssessmentsList(self) + return self._assessments + + @property + def channel(self) -> ChannelList: + if self._channel is None: + self._channel = ChannelList(self) + return self._channel + + @property + def configuration(self) -> ConfigurationList: + if self._configuration is None: + self._configuration = ConfigurationList(self) + return self._configuration + + @property + def flex_flow(self) -> FlexFlowList: + if self._flex_flow is None: + self._flex_flow = FlexFlowList(self) + return self._flex_flow + + @property + def insights_assessments_comment(self) -> InsightsAssessmentsCommentList: + if self._insights_assessments_comment is None: + self._insights_assessments_comment = InsightsAssessmentsCommentList(self) + return self._insights_assessments_comment + + @property + def insights_conversations(self) -> InsightsConversationsList: + if self._insights_conversations is None: + self._insights_conversations = InsightsConversationsList(self) + return self._insights_conversations + + @property + def insights_questionnaires(self) -> InsightsQuestionnairesList: + if self._insights_questionnaires is None: + self._insights_questionnaires = InsightsQuestionnairesList(self) + return self._insights_questionnaires + + @property + def insights_questionnaires_category(self) -> InsightsQuestionnairesCategoryList: + if self._insights_questionnaires_category is None: + self._insights_questionnaires_category = InsightsQuestionnairesCategoryList( + self + ) + return self._insights_questionnaires_category + + @property + def insights_questionnaires_question(self) -> InsightsQuestionnairesQuestionList: + if self._insights_questionnaires_question is None: + self._insights_questionnaires_question = InsightsQuestionnairesQuestionList( + self + ) + return self._insights_questionnaires_question + + @property + def insights_segments(self) -> InsightsSegmentsList: + if self._insights_segments is None: + self._insights_segments = InsightsSegmentsList(self) + return self._insights_segments + + @property + def insights_session(self) -> InsightsSessionList: + if self._insights_session is None: + self._insights_session = InsightsSessionList(self) + return self._insights_session + + @property + def insights_settings_answer_sets(self) -> InsightsSettingsAnswerSetsList: + if self._insights_settings_answer_sets is None: + self._insights_settings_answer_sets = InsightsSettingsAnswerSetsList(self) + return self._insights_settings_answer_sets + + @property + def insights_settings_comment(self) -> InsightsSettingsCommentList: + if self._insights_settings_comment is None: + self._insights_settings_comment = InsightsSettingsCommentList(self) + return self._insights_settings_comment + + @property + def insights_user_roles(self) -> InsightsUserRolesList: + if self._insights_user_roles is None: + self._insights_user_roles = InsightsUserRolesList(self) + return self._insights_user_roles + + @property + def interaction(self) -> InteractionList: + if self._interaction is None: + self._interaction = InteractionList(self) + return self._interaction + + @property + def plugins(self) -> PluginList: + if self._plugins is None: + self._plugins = PluginList(self) + return self._plugins + + @property + def plugin_archive(self) -> PluginArchiveList: + if self._plugin_archive is None: + self._plugin_archive = PluginArchiveList(self) + return self._plugin_archive + + @property + def plugin_configurations(self) -> PluginConfigurationList: + if self._plugin_configurations is None: + self._plugin_configurations = PluginConfigurationList(self) + return self._plugin_configurations + + @property + def plugin_configuration_archive(self) -> PluginConfigurationArchiveList: + if self._plugin_configuration_archive is None: + self._plugin_configuration_archive = PluginConfigurationArchiveList(self) + return self._plugin_configuration_archive + + @property + def plugin_releases(self) -> PluginReleaseList: + if self._plugin_releases is None: + self._plugin_releases = PluginReleaseList(self) + return self._plugin_releases + + @property + def plugin_version_archive(self) -> PluginVersionArchiveList: + if self._plugin_version_archive is None: + self._plugin_version_archive = PluginVersionArchiveList(self) + return self._plugin_version_archive + + @property + def provisioning_status(self) -> ProvisioningStatusList: + if self._provisioning_status is None: + self._provisioning_status = ProvisioningStatusList(self) + return self._provisioning_status + + @property + def web_channel(self) -> WebChannelList: + if self._web_channel is None: + self._web_channel = WebChannelList(self) + return self._web_channel + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3b71bff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/assessments.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/assessments.cpython-310.pyc new file mode 100644 index 0000000..484ce92 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/assessments.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/channel.cpython-310.pyc new file mode 100644 index 0000000..ab22428 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/configuration.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/configuration.cpython-310.pyc new file mode 100644 index 0000000..920b873 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/configuration.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/flex_flow.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/flex_flow.cpython-310.pyc new file mode 100644 index 0000000..57c9620 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/flex_flow.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_assessments_comment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_assessments_comment.cpython-310.pyc new file mode 100644 index 0000000..c57b89a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_assessments_comment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_conversations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_conversations.cpython-310.pyc new file mode 100644 index 0000000..d1c8b3e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_conversations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires.cpython-310.pyc new file mode 100644 index 0000000..e4c7dcb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_category.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_category.cpython-310.pyc new file mode 100644 index 0000000..83d071a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_category.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_question.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_question.cpython-310.pyc new file mode 100644 index 0000000..16319b7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_questionnaires_question.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_segments.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_segments.cpython-310.pyc new file mode 100644 index 0000000..9bcd359 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_segments.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_session.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_session.cpython-310.pyc new file mode 100644 index 0000000..0f644f7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_session.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_answer_sets.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_answer_sets.cpython-310.pyc new file mode 100644 index 0000000..86ddc75 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_answer_sets.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_comment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_comment.cpython-310.pyc new file mode 100644 index 0000000..8ad8f4c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_settings_comment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_user_roles.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_user_roles.cpython-310.pyc new file mode 100644 index 0000000..0b3cf57 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/insights_user_roles.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_archive.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_archive.cpython-310.pyc new file mode 100644 index 0000000..4aa98da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_archive.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_configuration_archive.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_configuration_archive.cpython-310.pyc new file mode 100644 index 0000000..90232e7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_configuration_archive.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_release.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_release.cpython-310.pyc new file mode 100644 index 0000000..ce3c05a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_release.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_version_archive.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_version_archive.cpython-310.pyc new file mode 100644 index 0000000..0f4c729 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/plugin_version_archive.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/provisioning_status.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/provisioning_status.cpython-310.pyc new file mode 100644 index 0000000..ce73de0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/provisioning_status.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/web_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/web_channel.cpython-310.pyc new file mode 100644 index 0000000..ad15955 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/__pycache__/web_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/assessments.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/assessments.py new file mode 100644 index 0000000..23648b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/assessments.py @@ -0,0 +1,641 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AssessmentsInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar assessment_sid: The SID of the assessment + :ivar offset: Offset of the conversation + :ivar report: The flag indicating if this assessment is part of report + :ivar weight: The weightage given to this comment + :ivar agent_id: The id of the Agent + :ivar segment_id: Segment Id of conversation + :ivar user_name: The name of the user. + :ivar user_email: The email id of the user. + :ivar answer_text: The answer text selected by user + :ivar answer_id: The id of the answer selected by user + :ivar assessment: Assessment Details associated with an assessment + :ivar timestamp: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + assessment_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.assessment_sid: Optional[str] = payload.get("assessment_sid") + self.offset: Optional[float] = deserialize.decimal(payload.get("offset")) + self.report: Optional[bool] = payload.get("report") + self.weight: Optional[float] = deserialize.decimal(payload.get("weight")) + self.agent_id: Optional[str] = payload.get("agent_id") + self.segment_id: Optional[str] = payload.get("segment_id") + self.user_name: Optional[str] = payload.get("user_name") + self.user_email: Optional[str] = payload.get("user_email") + self.answer_text: Optional[str] = payload.get("answer_text") + self.answer_id: Optional[str] = payload.get("answer_id") + self.assessment: Optional[Dict[str, object]] = payload.get("assessment") + self.timestamp: Optional[float] = deserialize.decimal(payload.get("timestamp")) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "assessment_sid": assessment_sid or self.assessment_sid, + } + self._context: Optional[AssessmentsContext] = None + + @property + def _proxy(self) -> "AssessmentsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AssessmentsContext for this AssessmentsInstance + """ + if self._context is None: + self._context = AssessmentsContext( + self._version, + assessment_sid=self._solution["assessment_sid"], + ) + return self._context + + def update( + self, + offset: float, + answer_text: str, + answer_id: str, + authorization: Union[str, object] = values.unset, + ) -> "AssessmentsInstance": + """ + Update the AssessmentsInstance + + :param offset: The offset of the conversation + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param authorization: The Authorization HTTP request header + + :returns: The updated AssessmentsInstance + """ + return self._proxy.update( + offset=offset, + answer_text=answer_text, + answer_id=answer_id, + authorization=authorization, + ) + + async def update_async( + self, + offset: float, + answer_text: str, + answer_id: str, + authorization: Union[str, object] = values.unset, + ) -> "AssessmentsInstance": + """ + Asynchronous coroutine to update the AssessmentsInstance + + :param offset: The offset of the conversation + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param authorization: The Authorization HTTP request header + + :returns: The updated AssessmentsInstance + """ + return await self._proxy.update_async( + offset=offset, + answer_text=answer_text, + answer_id=answer_id, + authorization=authorization, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssessmentsContext(InstanceContext): + + def __init__(self, version: Version, assessment_sid: str): + """ + Initialize the AssessmentsContext + + :param version: Version that contains the resource + :param assessment_sid: The SID of the assessment to be modified + """ + super().__init__(version) + + # Path Solution + self._solution = { + "assessment_sid": assessment_sid, + } + self._uri = "/Insights/QualityManagement/Assessments/{assessment_sid}".format( + **self._solution + ) + + def update( + self, + offset: float, + answer_text: str, + answer_id: str, + authorization: Union[str, object] = values.unset, + ) -> AssessmentsInstance: + """ + Update the AssessmentsInstance + + :param offset: The offset of the conversation + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param authorization: The Authorization HTTP request header + + :returns: The updated AssessmentsInstance + """ + data = values.of( + { + "Offset": offset, + "AnswerText": answer_text, + "AnswerId": answer_id, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return AssessmentsInstance( + self._version, payload, assessment_sid=self._solution["assessment_sid"] + ) + + async def update_async( + self, + offset: float, + answer_text: str, + answer_id: str, + authorization: Union[str, object] = values.unset, + ) -> AssessmentsInstance: + """ + Asynchronous coroutine to update the AssessmentsInstance + + :param offset: The offset of the conversation + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param authorization: The Authorization HTTP request header + + :returns: The updated AssessmentsInstance + """ + data = values.of( + { + "Offset": offset, + "AnswerText": answer_text, + "AnswerId": answer_id, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return AssessmentsInstance( + self._version, payload, assessment_sid=self._solution["assessment_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssessmentsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AssessmentsInstance: + """ + Build an instance of AssessmentsInstance + + :param payload: Payload response from the API + """ + return AssessmentsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AssessmentsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AssessmentsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Assessments" + + def create( + self, + category_sid: str, + category_name: str, + segment_id: str, + agent_id: str, + offset: float, + metric_id: str, + metric_name: str, + answer_text: str, + answer_id: str, + questionnaire_sid: str, + authorization: Union[str, object] = values.unset, + ) -> AssessmentsInstance: + """ + Create the AssessmentsInstance + + :param category_sid: The SID of the category + :param category_name: The name of the category + :param segment_id: Segment Id of the conversation + :param agent_id: The id of the Agent + :param offset: The offset of the conversation. + :param metric_id: The question SID selected for assessment + :param metric_name: The question name of the assessment + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param questionnaire_sid: Questionnaire SID of the associated question + :param authorization: The Authorization HTTP request header + + :returns: The created AssessmentsInstance + """ + + data = values.of( + { + "CategorySid": category_sid, + "CategoryName": category_name, + "SegmentId": segment_id, + "AgentId": agent_id, + "Offset": offset, + "MetricId": metric_id, + "MetricName": metric_name, + "AnswerText": answer_text, + "AnswerId": answer_id, + "QuestionnaireSid": questionnaire_sid, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return AssessmentsInstance(self._version, payload) + + async def create_async( + self, + category_sid: str, + category_name: str, + segment_id: str, + agent_id: str, + offset: float, + metric_id: str, + metric_name: str, + answer_text: str, + answer_id: str, + questionnaire_sid: str, + authorization: Union[str, object] = values.unset, + ) -> AssessmentsInstance: + """ + Asynchronously create the AssessmentsInstance + + :param category_sid: The SID of the category + :param category_name: The name of the category + :param segment_id: Segment Id of the conversation + :param agent_id: The id of the Agent + :param offset: The offset of the conversation. + :param metric_id: The question SID selected for assessment + :param metric_name: The question name of the assessment + :param answer_text: The answer text selected by user + :param answer_id: The id of the answer selected by user + :param questionnaire_sid: Questionnaire SID of the associated question + :param authorization: The Authorization HTTP request header + + :returns: The created AssessmentsInstance + """ + + data = values.of( + { + "CategorySid": category_sid, + "CategoryName": category_name, + "SegmentId": segment_id, + "AgentId": agent_id, + "Offset": offset, + "MetricId": metric_id, + "MetricName": metric_name, + "AnswerText": answer_text, + "AnswerId": answer_id, + "QuestionnaireSid": questionnaire_sid, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return AssessmentsInstance(self._version, payload) + + def stream( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AssessmentsInstance]: + """ + Streams AssessmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + segment_id=segment_id, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AssessmentsInstance]: + """ + Asynchronously streams AssessmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + segment_id=segment_id, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssessmentsInstance]: + """ + Lists AssessmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + segment_id=segment_id, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssessmentsInstance]: + """ + Asynchronously lists AssessmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + segment_id=segment_id, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssessmentsPage: + """ + Retrieve a single page of AssessmentsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: The id of the segment. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssessmentsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AssessmentsPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssessmentsPage: + """ + Asynchronously retrieve a single page of AssessmentsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: The id of the segment. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssessmentsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AssessmentsPage(self._version, response) + + def get_page(self, target_url: str) -> AssessmentsPage: + """ + Retrieve a specific page of AssessmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssessmentsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AssessmentsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AssessmentsPage: + """ + Asynchronously retrieve a specific page of AssessmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssessmentsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AssessmentsPage(self._version, response) + + def get(self, assessment_sid: str) -> AssessmentsContext: + """ + Constructs a AssessmentsContext + + :param assessment_sid: The SID of the assessment to be modified + """ + return AssessmentsContext(self._version, assessment_sid=assessment_sid) + + def __call__(self, assessment_sid: str) -> AssessmentsContext: + """ + Constructs a AssessmentsContext + + :param assessment_sid: The SID of the assessment to be modified + """ + return AssessmentsContext(self._version, assessment_sid=assessment_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/channel.py new file mode 100644 index 0000000..f362d5b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/channel.py @@ -0,0 +1,553 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ChannelInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Channel resource and owns this Workflow. + :ivar flex_flow_sid: The SID of the Flex Flow. + :ivar sid: The unique string that we created to identify the Channel resource. + :ivar user_sid: The SID of the chat user. + :ivar task_sid: The SID of the TaskRouter Task. Only valid when integration type is `task`. `null` for integration types `studio` & `external` + :ivar url: The absolute URL of the Flex chat channel resource. + :ivar date_created: The date and time in GMT when the Flex chat channel was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Flex chat channel was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.flex_flow_sid: Optional[str] = payload.get("flex_flow_sid") + self.sid: Optional[str] = payload.get("sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.task_sid: Optional[str] = payload.get("task_sid") + self.url: Optional[str] = payload.get("url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ChannelInstance": + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelInstance": + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex chat channel resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Channels/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ChannelInstance: + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelInstance: + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelInstance: + """ + Build an instance of ChannelInstance + + :param payload: Payload response from the API + """ + return ChannelInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Channels" + + def create( + self, + flex_flow_sid: str, + identity: str, + chat_user_friendly_name: str, + chat_friendly_name: str, + target: Union[str, object] = values.unset, + chat_unique_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + task_attributes: Union[str, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + ) -> ChannelInstance: + """ + Create the ChannelInstance + + :param flex_flow_sid: The SID of the Flex Flow. + :param identity: The `identity` value that uniquely identifies the new resource's chat User. + :param chat_user_friendly_name: The chat participant's friendly name. + :param chat_friendly_name: The chat channel's friendly name. + :param target: The Target Contact Identity, for example the phone number of an SMS. + :param chat_unique_name: The chat channel's unique name. + :param pre_engagement_data: The pre-engagement data. + :param task_sid: The SID of the TaskRouter Task. Only valid when integration type is `task`. `null` for integration types `studio` & `external` + :param task_attributes: The Task attributes to be added for the TaskRouter Task. + :param long_lived: Whether to create the channel as long-lived. + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FlexFlowSid": flex_flow_sid, + "Identity": identity, + "ChatUserFriendlyName": chat_user_friendly_name, + "ChatFriendlyName": chat_friendly_name, + "Target": target, + "ChatUniqueName": chat_unique_name, + "PreEngagementData": pre_engagement_data, + "TaskSid": task_sid, + "TaskAttributes": task_attributes, + "LongLived": serialize.boolean_to_string(long_lived), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance(self._version, payload) + + async def create_async( + self, + flex_flow_sid: str, + identity: str, + chat_user_friendly_name: str, + chat_friendly_name: str, + target: Union[str, object] = values.unset, + chat_unique_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + task_attributes: Union[str, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronously create the ChannelInstance + + :param flex_flow_sid: The SID of the Flex Flow. + :param identity: The `identity` value that uniquely identifies the new resource's chat User. + :param chat_user_friendly_name: The chat participant's friendly name. + :param chat_friendly_name: The chat channel's friendly name. + :param target: The Target Contact Identity, for example the phone number of an SMS. + :param chat_unique_name: The chat channel's unique name. + :param pre_engagement_data: The pre-engagement data. + :param task_sid: The SID of the TaskRouter Task. Only valid when integration type is `task`. `null` for integration types `studio` & `external` + :param task_attributes: The Task attributes to be added for the TaskRouter Task. + :param long_lived: Whether to create the channel as long-lived. + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FlexFlowSid": flex_flow_sid, + "Identity": identity, + "ChatUserFriendlyName": chat_user_friendly_name, + "ChatFriendlyName": chat_friendly_name, + "Target": target, + "ChatUniqueName": chat_unique_name, + "PreEngagementData": pre_engagement_data, + "TaskSid": task_sid, + "TaskAttributes": task_attributes, + "LongLived": serialize.boolean_to_string(long_lived), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelInstance]: + """ + Streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelInstance]: + """ + Asynchronously streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Asynchronously lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Asynchronously retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelPage(self._version, response) + + def get_page(self, target_url: str) -> ChannelPage: + """ + Retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ChannelPage: + """ + Asynchronously retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelPage(self._version, response) + + def get(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The SID of the Flex chat channel resource to fetch. + """ + return ChannelContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: The SID of the Flex chat channel resource to fetch. + """ + return ChannelContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/configuration.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/configuration.py new file mode 100644 index 0000000..bec7034 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/configuration.py @@ -0,0 +1,427 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ConfigurationInstance(InstanceResource): + + class Status(object): + OK = "ok" + INPROGRESS = "inprogress" + NOTSTARTED = "notstarted" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Configuration resource. + :ivar date_created: The date and time in GMT when the Configuration resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Configuration resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar attributes: An object that contains application-specific data. + :ivar status: + :ivar taskrouter_workspace_sid: The SID of the TaskRouter Workspace. + :ivar taskrouter_target_workflow_sid: The SID of the TaskRouter target Workflow. + :ivar taskrouter_target_taskqueue_sid: The SID of the TaskRouter Target TaskQueue. + :ivar taskrouter_taskqueues: The list of TaskRouter TaskQueues. + :ivar taskrouter_skills: The Skill description for TaskRouter workers. + :ivar taskrouter_worker_channels: The TaskRouter default channel capacities and availability for workers. + :ivar taskrouter_worker_attributes: The TaskRouter Worker attributes. + :ivar taskrouter_offline_activity_sid: The TaskRouter SID of the offline activity. + :ivar runtime_domain: The URL where the Flex instance is hosted. + :ivar messaging_service_instance_sid: The SID of the Messaging service instance. + :ivar chat_service_instance_sid: The SID of the chat service this user belongs to. + :ivar flex_service_instance_sid: The SID of the Flex service instance. + :ivar flex_instance_sid: The SID of the Flex instance. + :ivar ui_language: The primary language of the Flex UI. + :ivar ui_attributes: The object that describes Flex UI characteristics and settings. + :ivar ui_dependencies: The object that defines the NPM packages and versions to be used in Hosted Flex. + :ivar ui_version: The Pinned UI version. + :ivar service_version: The Flex Service version. + :ivar call_recording_enabled: Whether call recording is enabled. + :ivar call_recording_webhook_url: The call recording webhook URL. + :ivar crm_enabled: Whether CRM is present for Flex. + :ivar crm_type: The CRM type. + :ivar crm_callback_url: The CRM Callback URL. + :ivar crm_fallback_url: The CRM Fallback URL. + :ivar crm_attributes: An object that contains the CRM attributes. + :ivar public_attributes: The list of public attributes, which are visible to unauthenticated clients. + :ivar plugin_service_enabled: Whether the plugin service enabled. + :ivar plugin_service_attributes: The plugin service attributes. + :ivar integrations: A list of objects that contain the configurations for the Integrations supported in this configuration. + :ivar outbound_call_flows: The list of outbound call flows. + :ivar serverless_service_sids: The list of serverless service SIDs. + :ivar queue_stats_configuration: Configurable parameters for Queues Statistics. + :ivar notifications: Configurable parameters for Notifications. + :ivar markdown: Configurable parameters for Markdown. + :ivar url: The absolute URL of the Configuration resource. + :ivar flex_insights_hr: Object with enabled/disabled flag with list of workspaces. + :ivar flex_insights_drilldown: Setting this to true will redirect Flex UI to the URL set in flex_url + :ivar flex_url: URL to redirect to in case drilldown is enabled. + :ivar channel_configs: Settings for different limits for Flex Conversations channels attachments. + :ivar debugger_integration: Configurable parameters for Debugger Integration. + :ivar flex_ui_status_report: Configurable parameters for Flex UI Status report. + :ivar agent_conv_end_methods: Agent conversation end methods. + :ivar citrix_voice_vdi: Citrix voice vdi configuration and settings. + :ivar offline_config: Presence and presence ttl configuration + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.status: Optional["ConfigurationInstance.Status"] = payload.get("status") + self.taskrouter_workspace_sid: Optional[str] = payload.get( + "taskrouter_workspace_sid" + ) + self.taskrouter_target_workflow_sid: Optional[str] = payload.get( + "taskrouter_target_workflow_sid" + ) + self.taskrouter_target_taskqueue_sid: Optional[str] = payload.get( + "taskrouter_target_taskqueue_sid" + ) + self.taskrouter_taskqueues: Optional[List[Dict[str, object]]] = payload.get( + "taskrouter_taskqueues" + ) + self.taskrouter_skills: Optional[List[Dict[str, object]]] = payload.get( + "taskrouter_skills" + ) + self.taskrouter_worker_channels: Optional[Dict[str, object]] = payload.get( + "taskrouter_worker_channels" + ) + self.taskrouter_worker_attributes: Optional[Dict[str, object]] = payload.get( + "taskrouter_worker_attributes" + ) + self.taskrouter_offline_activity_sid: Optional[str] = payload.get( + "taskrouter_offline_activity_sid" + ) + self.runtime_domain: Optional[str] = payload.get("runtime_domain") + self.messaging_service_instance_sid: Optional[str] = payload.get( + "messaging_service_instance_sid" + ) + self.chat_service_instance_sid: Optional[str] = payload.get( + "chat_service_instance_sid" + ) + self.flex_service_instance_sid: Optional[str] = payload.get( + "flex_service_instance_sid" + ) + self.flex_instance_sid: Optional[str] = payload.get("flex_instance_sid") + self.ui_language: Optional[str] = payload.get("ui_language") + self.ui_attributes: Optional[Dict[str, object]] = payload.get("ui_attributes") + self.ui_dependencies: Optional[Dict[str, object]] = payload.get( + "ui_dependencies" + ) + self.ui_version: Optional[str] = payload.get("ui_version") + self.service_version: Optional[str] = payload.get("service_version") + self.call_recording_enabled: Optional[bool] = payload.get( + "call_recording_enabled" + ) + self.call_recording_webhook_url: Optional[str] = payload.get( + "call_recording_webhook_url" + ) + self.crm_enabled: Optional[bool] = payload.get("crm_enabled") + self.crm_type: Optional[str] = payload.get("crm_type") + self.crm_callback_url: Optional[str] = payload.get("crm_callback_url") + self.crm_fallback_url: Optional[str] = payload.get("crm_fallback_url") + self.crm_attributes: Optional[Dict[str, object]] = payload.get("crm_attributes") + self.public_attributes: Optional[Dict[str, object]] = payload.get( + "public_attributes" + ) + self.plugin_service_enabled: Optional[bool] = payload.get( + "plugin_service_enabled" + ) + self.plugin_service_attributes: Optional[Dict[str, object]] = payload.get( + "plugin_service_attributes" + ) + self.integrations: Optional[List[Dict[str, object]]] = payload.get( + "integrations" + ) + self.outbound_call_flows: Optional[Dict[str, object]] = payload.get( + "outbound_call_flows" + ) + self.serverless_service_sids: Optional[List[str]] = payload.get( + "serverless_service_sids" + ) + self.queue_stats_configuration: Optional[Dict[str, object]] = payload.get( + "queue_stats_configuration" + ) + self.notifications: Optional[Dict[str, object]] = payload.get("notifications") + self.markdown: Optional[Dict[str, object]] = payload.get("markdown") + self.url: Optional[str] = payload.get("url") + self.flex_insights_hr: Optional[Dict[str, object]] = payload.get( + "flex_insights_hr" + ) + self.flex_insights_drilldown: Optional[bool] = payload.get( + "flex_insights_drilldown" + ) + self.flex_url: Optional[str] = payload.get("flex_url") + self.channel_configs: Optional[List[Dict[str, object]]] = payload.get( + "channel_configs" + ) + self.debugger_integration: Optional[Dict[str, object]] = payload.get( + "debugger_integration" + ) + self.flex_ui_status_report: Optional[Dict[str, object]] = payload.get( + "flex_ui_status_report" + ) + self.agent_conv_end_methods: Optional[Dict[str, object]] = payload.get( + "agent_conv_end_methods" + ) + self.citrix_voice_vdi: Optional[Dict[str, object]] = payload.get( + "citrix_voice_vdi" + ) + self.offline_config: Optional[Dict[str, object]] = payload.get("offline_config") + + self._context: Optional[ConfigurationContext] = None + + @property + def _proxy(self) -> "ConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConfigurationContext for this ConfigurationInstance + """ + if self._context is None: + self._context = ConfigurationContext( + self._version, + ) + return self._context + + def fetch( + self, ui_version: Union[str, object] = values.unset + ) -> "ConfigurationInstance": + """ + Fetch the ConfigurationInstance + + :param ui_version: The Pinned UI version of the Configuration resource to fetch. + + :returns: The fetched ConfigurationInstance + """ + return self._proxy.fetch( + ui_version=ui_version, + ) + + async def fetch_async( + self, ui_version: Union[str, object] = values.unset + ) -> "ConfigurationInstance": + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + :param ui_version: The Pinned UI version of the Configuration resource to fetch. + + :returns: The fetched ConfigurationInstance + """ + return await self._proxy.fetch_async( + ui_version=ui_version, + ) + + def update( + self, body: Union[object, object] = values.unset + ) -> "ConfigurationInstance": + """ + Update the ConfigurationInstance + + :param body: + + :returns: The updated ConfigurationInstance + """ + return self._proxy.update( + body=body, + ) + + async def update_async( + self, body: Union[object, object] = values.unset + ) -> "ConfigurationInstance": + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param body: + + :returns: The updated ConfigurationInstance + """ + return await self._proxy.update_async( + body=body, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ConfigurationContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the ConfigurationContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Configuration" + + def fetch( + self, ui_version: Union[str, object] = values.unset + ) -> ConfigurationInstance: + """ + Fetch the ConfigurationInstance + + :param ui_version: The Pinned UI version of the Configuration resource to fetch. + + :returns: The fetched ConfigurationInstance + """ + + data = values.of( + { + "UiVersion": ui_version, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return ConfigurationInstance( + self._version, + payload, + ) + + async def fetch_async( + self, ui_version: Union[str, object] = values.unset + ) -> ConfigurationInstance: + """ + Asynchronous coroutine to fetch the ConfigurationInstance + + :param ui_version: The Pinned UI version of the Configuration resource to fetch. + + :returns: The fetched ConfigurationInstance + """ + + data = values.of( + { + "UiVersion": ui_version, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return ConfigurationInstance( + self._version, + payload, + ) + + def update( + self, body: Union[object, object] = values.unset + ) -> ConfigurationInstance: + """ + Update the ConfigurationInstance + + :param body: + + :returns: The updated ConfigurationInstance + """ + data = values.of({}) + headers = values.of( + { + "body": body, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConfigurationInstance(self._version, payload) + + async def update_async( + self, body: Union[object, object] = values.unset + ) -> ConfigurationInstance: + """ + Asynchronous coroutine to update the ConfigurationInstance + + :param body: + + :returns: The updated ConfigurationInstance + """ + data = values.of({}) + headers = values.of( + { + "body": body, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ConfigurationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ConfigurationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ConfigurationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext(self._version) + + def __call__(self) -> ConfigurationContext: + """ + Constructs a ConfigurationContext + + """ + return ConfigurationContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/flex_flow.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/flex_flow.py new file mode 100644 index 0000000..b114829 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/flex_flow.py @@ -0,0 +1,935 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class FlexFlowInstance(InstanceResource): + + class ChannelType(object): + WEB = "web" + SMS = "sms" + FACEBOOK = "facebook" + WHATSAPP = "whatsapp" + LINE = "line" + CUSTOM = "custom" + + class IntegrationType(object): + STUDIO = "studio" + EXTERNAL = "external" + TASK = "task" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Flow resource and owns this Workflow. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar sid: The unique string that we created to identify the Flex Flow resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar chat_service_sid: The SID of the chat service. + :ivar channel_type: + :ivar contact_identity: The channel contact's Identity. + :ivar enabled: Whether the Flex Flow is enabled. + :ivar integration_type: + :ivar integration: An object that contains specific parameters for the integration. + :ivar long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :ivar janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :ivar url: The absolute URL of the Flex Flow resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.chat_service_sid: Optional[str] = payload.get("chat_service_sid") + self.channel_type: Optional["FlexFlowInstance.ChannelType"] = payload.get( + "channel_type" + ) + self.contact_identity: Optional[str] = payload.get("contact_identity") + self.enabled: Optional[bool] = payload.get("enabled") + self.integration_type: Optional["FlexFlowInstance.IntegrationType"] = ( + payload.get("integration_type") + ) + self.integration: Optional[Dict[str, object]] = payload.get("integration") + self.long_lived: Optional[bool] = payload.get("long_lived") + self.janitor_enabled: Optional[bool] = payload.get("janitor_enabled") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[FlexFlowContext] = None + + @property + def _proxy(self) -> "FlexFlowContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlexFlowContext for this FlexFlowInstance + """ + if self._context is None: + self._context = FlexFlowContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FlexFlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlexFlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FlexFlowInstance": + """ + Fetch the FlexFlowInstance + + + :returns: The fetched FlexFlowInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlexFlowInstance": + """ + Asynchronous coroutine to fetch the FlexFlowInstance + + + :returns: The fetched FlexFlowInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + chat_service_sid: Union[str, object] = values.unset, + channel_type: Union["FlexFlowInstance.ChannelType", object] = values.unset, + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> "FlexFlowInstance": + """ + Update the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The updated FlexFlowInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + chat_service_sid=chat_service_sid, + channel_type=channel_type, + contact_identity=contact_identity, + enabled=enabled, + integration_type=integration_type, + integration_flow_sid=integration_flow_sid, + integration_url=integration_url, + integration_workspace_sid=integration_workspace_sid, + integration_workflow_sid=integration_workflow_sid, + integration_channel=integration_channel, + integration_timeout=integration_timeout, + integration_priority=integration_priority, + integration_creation_on_message=integration_creation_on_message, + long_lived=long_lived, + janitor_enabled=janitor_enabled, + integration_retry_count=integration_retry_count, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + chat_service_sid: Union[str, object] = values.unset, + channel_type: Union["FlexFlowInstance.ChannelType", object] = values.unset, + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> "FlexFlowInstance": + """ + Asynchronous coroutine to update the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The updated FlexFlowInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + chat_service_sid=chat_service_sid, + channel_type=channel_type, + contact_identity=contact_identity, + enabled=enabled, + integration_type=integration_type, + integration_flow_sid=integration_flow_sid, + integration_url=integration_url, + integration_workspace_sid=integration_workspace_sid, + integration_workflow_sid=integration_workflow_sid, + integration_channel=integration_channel, + integration_timeout=integration_timeout, + integration_priority=integration_priority, + integration_creation_on_message=integration_creation_on_message, + long_lived=long_lived, + janitor_enabled=janitor_enabled, + integration_retry_count=integration_retry_count, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlexFlowContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlexFlowContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Flow resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/FlexFlows/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the FlexFlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlexFlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FlexFlowInstance: + """ + Fetch the FlexFlowInstance + + + :returns: The fetched FlexFlowInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlexFlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FlexFlowInstance: + """ + Asynchronous coroutine to fetch the FlexFlowInstance + + + :returns: The fetched FlexFlowInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlexFlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + chat_service_sid: Union[str, object] = values.unset, + channel_type: Union["FlexFlowInstance.ChannelType", object] = values.unset, + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> FlexFlowInstance: + """ + Update the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The updated FlexFlowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ChatServiceSid": chat_service_sid, + "ChannelType": channel_type, + "ContactIdentity": contact_identity, + "Enabled": serialize.boolean_to_string(enabled), + "IntegrationType": integration_type, + "Integration.FlowSid": integration_flow_sid, + "Integration.Url": integration_url, + "Integration.WorkspaceSid": integration_workspace_sid, + "Integration.WorkflowSid": integration_workflow_sid, + "Integration.Channel": integration_channel, + "Integration.Timeout": integration_timeout, + "Integration.Priority": integration_priority, + "Integration.CreationOnMessage": serialize.boolean_to_string( + integration_creation_on_message + ), + "LongLived": serialize.boolean_to_string(long_lived), + "JanitorEnabled": serialize.boolean_to_string(janitor_enabled), + "Integration.RetryCount": integration_retry_count, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FlexFlowInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + chat_service_sid: Union[str, object] = values.unset, + channel_type: Union["FlexFlowInstance.ChannelType", object] = values.unset, + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> FlexFlowInstance: + """ + Asynchronous coroutine to update the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The updated FlexFlowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ChatServiceSid": chat_service_sid, + "ChannelType": channel_type, + "ContactIdentity": contact_identity, + "Enabled": serialize.boolean_to_string(enabled), + "IntegrationType": integration_type, + "Integration.FlowSid": integration_flow_sid, + "Integration.Url": integration_url, + "Integration.WorkspaceSid": integration_workspace_sid, + "Integration.WorkflowSid": integration_workflow_sid, + "Integration.Channel": integration_channel, + "Integration.Timeout": integration_timeout, + "Integration.Priority": integration_priority, + "Integration.CreationOnMessage": serialize.boolean_to_string( + integration_creation_on_message + ), + "LongLived": serialize.boolean_to_string(long_lived), + "JanitorEnabled": serialize.boolean_to_string(janitor_enabled), + "Integration.RetryCount": integration_retry_count, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlexFlowInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlexFlowPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FlexFlowInstance: + """ + Build an instance of FlexFlowInstance + + :param payload: Payload response from the API + """ + return FlexFlowInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FlexFlowList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FlexFlowList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/FlexFlows" + + def create( + self, + friendly_name: str, + chat_service_sid: str, + channel_type: "FlexFlowInstance.ChannelType", + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> FlexFlowInstance: + """ + Create the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The created FlexFlowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ChatServiceSid": chat_service_sid, + "ChannelType": channel_type, + "ContactIdentity": contact_identity, + "Enabled": serialize.boolean_to_string(enabled), + "IntegrationType": integration_type, + "Integration.FlowSid": integration_flow_sid, + "Integration.Url": integration_url, + "Integration.WorkspaceSid": integration_workspace_sid, + "Integration.WorkflowSid": integration_workflow_sid, + "Integration.Channel": integration_channel, + "Integration.Timeout": integration_timeout, + "Integration.Priority": integration_priority, + "Integration.CreationOnMessage": serialize.boolean_to_string( + integration_creation_on_message + ), + "LongLived": serialize.boolean_to_string(long_lived), + "JanitorEnabled": serialize.boolean_to_string(janitor_enabled), + "Integration.RetryCount": integration_retry_count, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FlexFlowInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + chat_service_sid: str, + channel_type: "FlexFlowInstance.ChannelType", + contact_identity: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + integration_type: Union[ + "FlexFlowInstance.IntegrationType", object + ] = values.unset, + integration_flow_sid: Union[str, object] = values.unset, + integration_url: Union[str, object] = values.unset, + integration_workspace_sid: Union[str, object] = values.unset, + integration_workflow_sid: Union[str, object] = values.unset, + integration_channel: Union[str, object] = values.unset, + integration_timeout: Union[int, object] = values.unset, + integration_priority: Union[int, object] = values.unset, + integration_creation_on_message: Union[bool, object] = values.unset, + long_lived: Union[bool, object] = values.unset, + janitor_enabled: Union[bool, object] = values.unset, + integration_retry_count: Union[int, object] = values.unset, + ) -> FlexFlowInstance: + """ + Asynchronously create the FlexFlowInstance + + :param friendly_name: A descriptive string that you create to describe the Flex Flow resource. + :param chat_service_sid: The SID of the chat service. + :param channel_type: + :param contact_identity: The channel contact's Identity. + :param enabled: Whether the new Flex Flow is enabled. + :param integration_type: + :param integration_flow_sid: The SID of the Studio Flow. Required when `integrationType` is `studio`. + :param integration_url: The URL of the external webhook. Required when `integrationType` is `external`. + :param integration_workspace_sid: The Workspace SID for a new Task. Required when `integrationType` is `task`. + :param integration_workflow_sid: The Workflow SID for a new Task. Required when `integrationType` is `task`. + :param integration_channel: The Task Channel SID (TCXXXX) or unique name (e.g., `sms`) to use for the Task that will be created. Applicable and required when `integrationType` is `task`. The default value is `default`. + :param integration_timeout: The Task timeout in seconds for a new Task. Default is 86,400 seconds (24 hours). Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_priority: The Task priority of a new Task. The default priority is 0. Optional when `integrationType` is `task`, not applicable otherwise. + :param integration_creation_on_message: In the context of outbound messaging, defines whether to create a Task immediately (and therefore reserve the conversation to current agent), or delay Task creation until the customer sends the first response. Set to false to create immediately, true to delay Task creation. This setting is only applicable for outbound messaging. + :param long_lived: When enabled, Flex will keep the chat channel active so that it may be used for subsequent interactions with a contact identity. Defaults to `false`. + :param janitor_enabled: When enabled, the Messaging Channel Janitor will remove active Proxy sessions if the associated Task is deleted outside of the Flex UI. Defaults to `false`. + :param integration_retry_count: The number of times to retry the Studio Flow or webhook in case of failure. Takes integer values from 0 to 3 with the default being 3. Optional when `integrationType` is `studio` or `external`, not applicable otherwise. + + :returns: The created FlexFlowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ChatServiceSid": chat_service_sid, + "ChannelType": channel_type, + "ContactIdentity": contact_identity, + "Enabled": serialize.boolean_to_string(enabled), + "IntegrationType": integration_type, + "Integration.FlowSid": integration_flow_sid, + "Integration.Url": integration_url, + "Integration.WorkspaceSid": integration_workspace_sid, + "Integration.WorkflowSid": integration_workflow_sid, + "Integration.Channel": integration_channel, + "Integration.Timeout": integration_timeout, + "Integration.Priority": integration_priority, + "Integration.CreationOnMessage": serialize.boolean_to_string( + integration_creation_on_message + ), + "LongLived": serialize.boolean_to_string(long_lived), + "JanitorEnabled": serialize.boolean_to_string(janitor_enabled), + "Integration.RetryCount": integration_retry_count, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlexFlowInstance(self._version, payload) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FlexFlowInstance]: + """ + Streams FlexFlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FlexFlowInstance]: + """ + Asynchronously streams FlexFlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlexFlowInstance]: + """ + Lists FlexFlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlexFlowInstance]: + """ + Asynchronously lists FlexFlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlexFlowPage: + """ + Retrieve a single page of FlexFlowInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlexFlowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FlexFlowPage(self._version, response) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlexFlowPage: + """ + Asynchronously retrieve a single page of FlexFlowInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Flex Flow resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlexFlowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FlexFlowPage(self._version, response) + + def get_page(self, target_url: str) -> FlexFlowPage: + """ + Retrieve a specific page of FlexFlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlexFlowInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FlexFlowPage(self._version, response) + + async def get_page_async(self, target_url: str) -> FlexFlowPage: + """ + Asynchronously retrieve a specific page of FlexFlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlexFlowInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FlexFlowPage(self._version, response) + + def get(self, sid: str) -> FlexFlowContext: + """ + Constructs a FlexFlowContext + + :param sid: The SID of the Flex Flow resource to update. + """ + return FlexFlowContext(self._version, sid=sid) + + def __call__(self, sid: str) -> FlexFlowContext: + """ + Constructs a FlexFlowContext + + :param sid: The SID of the Flex Flow resource to update. + """ + return FlexFlowContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_assessments_comment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_assessments_comment.py new file mode 100644 index 0000000..eed32fe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_assessments_comment.py @@ -0,0 +1,439 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsAssessmentsCommentInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar assessment_sid: The SID of the assessment. + :ivar comment: The comment added for assessment. + :ivar offset: The offset + :ivar report: The flag indicating if this assessment is part of report + :ivar weight: The weightage given to this comment + :ivar agent_id: The id of the agent. + :ivar segment_id: The id of the segment. + :ivar user_name: The name of the user. + :ivar user_email: The email id of the user. + :ivar timestamp: The timestamp when the record is inserted + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.assessment_sid: Optional[str] = payload.get("assessment_sid") + self.comment: Optional[Dict[str, object]] = payload.get("comment") + self.offset: Optional[float] = deserialize.decimal(payload.get("offset")) + self.report: Optional[bool] = payload.get("report") + self.weight: Optional[float] = deserialize.decimal(payload.get("weight")) + self.agent_id: Optional[str] = payload.get("agent_id") + self.segment_id: Optional[str] = payload.get("segment_id") + self.user_name: Optional[str] = payload.get("user_name") + self.user_email: Optional[str] = payload.get("user_email") + self.timestamp: Optional[float] = deserialize.decimal(payload.get("timestamp")) + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsAssessmentsCommentPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> InsightsAssessmentsCommentInstance: + """ + Build an instance of InsightsAssessmentsCommentInstance + + :param payload: Payload response from the API + """ + return InsightsAssessmentsCommentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsAssessmentsCommentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsAssessmentsCommentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Assessments/Comments" + + def create( + self, + category_id: str, + category_name: str, + comment: str, + segment_id: str, + agent_id: str, + offset: float, + authorization: Union[str, object] = values.unset, + ) -> InsightsAssessmentsCommentInstance: + """ + Create the InsightsAssessmentsCommentInstance + + :param category_id: The ID of the category + :param category_name: The name of the category + :param comment: The Assessment comment. + :param segment_id: The id of the segment. + :param agent_id: The id of the agent. + :param offset: The offset + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsAssessmentsCommentInstance + """ + + data = values.of( + { + "CategoryId": category_id, + "CategoryName": category_name, + "Comment": comment, + "SegmentId": segment_id, + "AgentId": agent_id, + "Offset": offset, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsAssessmentsCommentInstance(self._version, payload) + + async def create_async( + self, + category_id: str, + category_name: str, + comment: str, + segment_id: str, + agent_id: str, + offset: float, + authorization: Union[str, object] = values.unset, + ) -> InsightsAssessmentsCommentInstance: + """ + Asynchronously create the InsightsAssessmentsCommentInstance + + :param category_id: The ID of the category + :param category_name: The name of the category + :param comment: The Assessment comment. + :param segment_id: The id of the segment. + :param agent_id: The id of the agent. + :param offset: The offset + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsAssessmentsCommentInstance + """ + + data = values.of( + { + "CategoryId": category_id, + "CategoryName": category_name, + "Comment": comment, + "SegmentId": segment_id, + "AgentId": agent_id, + "Offset": offset, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsAssessmentsCommentInstance(self._version, payload) + + def stream( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsAssessmentsCommentInstance]: + """ + Streams InsightsAssessmentsCommentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param str agent_id: The id of the agent. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + segment_id=segment_id, + agent_id=agent_id, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsAssessmentsCommentInstance]: + """ + Asynchronously streams InsightsAssessmentsCommentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param str agent_id: The id of the agent. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + segment_id=segment_id, + agent_id=agent_id, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsAssessmentsCommentInstance]: + """ + Lists InsightsAssessmentsCommentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param str agent_id: The id of the agent. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + segment_id=segment_id, + agent_id=agent_id, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsAssessmentsCommentInstance]: + """ + Asynchronously lists InsightsAssessmentsCommentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: The id of the segment. + :param str agent_id: The id of the agent. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + segment_id=segment_id, + agent_id=agent_id, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsAssessmentsCommentPage: + """ + Retrieve a single page of InsightsAssessmentsCommentInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: The id of the segment. + :param agent_id: The id of the agent. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsAssessmentsCommentInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "AgentId": agent_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsAssessmentsCommentPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + agent_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsAssessmentsCommentPage: + """ + Asynchronously retrieve a single page of InsightsAssessmentsCommentInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: The id of the segment. + :param agent_id: The id of the agent. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsAssessmentsCommentInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "AgentId": agent_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsAssessmentsCommentPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsAssessmentsCommentPage: + """ + Retrieve a specific page of InsightsAssessmentsCommentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsAssessmentsCommentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsAssessmentsCommentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> InsightsAssessmentsCommentPage: + """ + Asynchronously retrieve a specific page of InsightsAssessmentsCommentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsAssessmentsCommentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsAssessmentsCommentPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_conversations.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_conversations.py new file mode 100644 index 0000000..d08608c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_conversations.py @@ -0,0 +1,313 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsConversationsInstance(InstanceResource): + """ + :ivar account_id: The id of the account. + :ivar conversation_id: The unique id of the conversation + :ivar segment_count: The count of segments for a conversation + :ivar segments: The Segments of a conversation + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_id: Optional[str] = payload.get("account_id") + self.conversation_id: Optional[str] = payload.get("conversation_id") + self.segment_count: Optional[int] = deserialize.integer( + payload.get("segment_count") + ) + self.segments: Optional[List[Dict[str, object]]] = payload.get("segments") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsConversationsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InsightsConversationsInstance: + """ + Build an instance of InsightsConversationsInstance + + :param payload: Payload response from the API + """ + return InsightsConversationsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsConversationsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsConversationsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/Conversations" + + def stream( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsConversationsInstance]: + """ + Streams InsightsConversationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + segment_id=segment_id, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsConversationsInstance]: + """ + Asynchronously streams InsightsConversationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + segment_id=segment_id, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsConversationsInstance]: + """ + Lists InsightsConversationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + segment_id=segment_id, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsConversationsInstance]: + """ + Asynchronously lists InsightsConversationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + segment_id=segment_id, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsConversationsPage: + """ + Retrieve a single page of InsightsConversationsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsConversationsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsConversationsPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsConversationsPage: + """ + Asynchronously retrieve a single page of InsightsConversationsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: Unique Id of the segment for which conversation details needs to be fetched + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsConversationsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsConversationsPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsConversationsPage: + """ + Retrieve a specific page of InsightsConversationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsConversationsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsConversationsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> InsightsConversationsPage: + """ + Asynchronously retrieve a specific page of InsightsConversationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsConversationsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsConversationsPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires.py new file mode 100644 index 0000000..807f129 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires.py @@ -0,0 +1,755 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsQuestionnairesInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar questionnaire_sid: The sid of this questionnaire + :ivar name: The name of this category. + :ivar description: The description of this questionnaire + :ivar active: The flag to enable or disable questionnaire + :ivar questions: The list of questions with category for a questionnaire + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + questionnaire_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.questionnaire_sid: Optional[str] = payload.get("questionnaire_sid") + self.name: Optional[str] = payload.get("name") + self.description: Optional[str] = payload.get("description") + self.active: Optional[bool] = payload.get("active") + self.questions: Optional[List[Dict[str, object]]] = payload.get("questions") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "questionnaire_sid": questionnaire_sid or self.questionnaire_sid, + } + self._context: Optional[InsightsQuestionnairesContext] = None + + @property + def _proxy(self) -> "InsightsQuestionnairesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InsightsQuestionnairesContext for this InsightsQuestionnairesInstance + """ + if self._context is None: + self._context = InsightsQuestionnairesContext( + self._version, + questionnaire_sid=self._solution["questionnaire_sid"], + ) + return self._context + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + authorization=authorization, + ) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + authorization=authorization, + ) + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsQuestionnairesInstance": + """ + Fetch the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsQuestionnairesInstance + """ + return self._proxy.fetch( + authorization=authorization, + ) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsQuestionnairesInstance": + """ + Asynchronous coroutine to fetch the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsQuestionnairesInstance + """ + return await self._proxy.fetch_async( + authorization=authorization, + ) + + def update( + self, + active: bool, + authorization: Union[str, object] = values.unset, + name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> "InsightsQuestionnairesInstance": + """ + Update the InsightsQuestionnairesInstance + + :param active: The flag to enable or disable questionnaire + :param authorization: The Authorization HTTP request header + :param name: The name of this questionnaire + :param description: The description of this questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The updated InsightsQuestionnairesInstance + """ + return self._proxy.update( + active=active, + authorization=authorization, + name=name, + description=description, + question_sids=question_sids, + ) + + async def update_async( + self, + active: bool, + authorization: Union[str, object] = values.unset, + name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> "InsightsQuestionnairesInstance": + """ + Asynchronous coroutine to update the InsightsQuestionnairesInstance + + :param active: The flag to enable or disable questionnaire + :param authorization: The Authorization HTTP request header + :param name: The name of this questionnaire + :param description: The description of this questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The updated InsightsQuestionnairesInstance + """ + return await self._proxy.update_async( + active=active, + authorization=authorization, + name=name, + description=description, + question_sids=question_sids, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InsightsQuestionnairesContext(InstanceContext): + + def __init__(self, version: Version, questionnaire_sid: str): + """ + Initialize the InsightsQuestionnairesContext + + :param version: Version that contains the resource + :param questionnaire_sid: The SID of the questionnaire + """ + super().__init__(version) + + # Path Solution + self._solution = { + "questionnaire_sid": questionnaire_sid, + } + self._uri = ( + "/Insights/QualityManagement/Questionnaires/{questionnaire_sid}".format( + **self._solution + ) + ) + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesInstance: + """ + Fetch the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsQuestionnairesInstance + """ + + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return InsightsQuestionnairesInstance( + self._version, + payload, + questionnaire_sid=self._solution["questionnaire_sid"], + ) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesInstance: + """ + Asynchronous coroutine to fetch the InsightsQuestionnairesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsQuestionnairesInstance + """ + + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return InsightsQuestionnairesInstance( + self._version, + payload, + questionnaire_sid=self._solution["questionnaire_sid"], + ) + + def update( + self, + active: bool, + authorization: Union[str, object] = values.unset, + name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> InsightsQuestionnairesInstance: + """ + Update the InsightsQuestionnairesInstance + + :param active: The flag to enable or disable questionnaire + :param authorization: The Authorization HTTP request header + :param name: The name of this questionnaire + :param description: The description of this questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The updated InsightsQuestionnairesInstance + """ + data = values.of( + { + "Active": serialize.boolean_to_string(active), + "Name": name, + "Description": description, + "QuestionSids": serialize.map(question_sids, lambda e: e), + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesInstance( + self._version, + payload, + questionnaire_sid=self._solution["questionnaire_sid"], + ) + + async def update_async( + self, + active: bool, + authorization: Union[str, object] = values.unset, + name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> InsightsQuestionnairesInstance: + """ + Asynchronous coroutine to update the InsightsQuestionnairesInstance + + :param active: The flag to enable or disable questionnaire + :param authorization: The Authorization HTTP request header + :param name: The name of this questionnaire + :param description: The description of this questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The updated InsightsQuestionnairesInstance + """ + data = values.of( + { + "Active": serialize.boolean_to_string(active), + "Name": name, + "Description": description, + "QuestionSids": serialize.map(question_sids, lambda e: e), + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesInstance( + self._version, + payload, + questionnaire_sid=self._solution["questionnaire_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InsightsQuestionnairesPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InsightsQuestionnairesInstance: + """ + Build an instance of InsightsQuestionnairesInstance + + :param payload: Payload response from the API + """ + return InsightsQuestionnairesInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsQuestionnairesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsQuestionnairesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Questionnaires" + + def create( + self, + name: str, + authorization: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + active: Union[bool, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> InsightsQuestionnairesInstance: + """ + Create the InsightsQuestionnairesInstance + + :param name: The name of this questionnaire + :param authorization: The Authorization HTTP request header + :param description: The description of this questionnaire + :param active: The flag to enable or disable questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The created InsightsQuestionnairesInstance + """ + + data = values.of( + { + "Name": name, + "Description": description, + "Active": serialize.boolean_to_string(active), + "QuestionSids": serialize.map(question_sids, lambda e: e), + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesInstance(self._version, payload) + + async def create_async( + self, + name: str, + authorization: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + active: Union[bool, object] = values.unset, + question_sids: Union[List[str], object] = values.unset, + ) -> InsightsQuestionnairesInstance: + """ + Asynchronously create the InsightsQuestionnairesInstance + + :param name: The name of this questionnaire + :param authorization: The Authorization HTTP request header + :param description: The description of this questionnaire + :param active: The flag to enable or disable questionnaire + :param question_sids: The list of questions sids under a questionnaire + + :returns: The created InsightsQuestionnairesInstance + """ + + data = values.of( + { + "Name": name, + "Description": description, + "Active": serialize.boolean_to_string(active), + "QuestionSids": serialize.map(question_sids, lambda e: e), + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesInstance(self._version, payload) + + def stream( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsQuestionnairesInstance]: + """ + Streams InsightsQuestionnairesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param bool include_inactive: Flag indicating whether to include inactive questionnaires or not + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + include_inactive=include_inactive, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsQuestionnairesInstance]: + """ + Asynchronously streams InsightsQuestionnairesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param bool include_inactive: Flag indicating whether to include inactive questionnaires or not + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + include_inactive=include_inactive, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesInstance]: + """ + Lists InsightsQuestionnairesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param bool include_inactive: Flag indicating whether to include inactive questionnaires or not + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + include_inactive=include_inactive, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesInstance]: + """ + Asynchronously lists InsightsQuestionnairesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param bool include_inactive: Flag indicating whether to include inactive questionnaires or not + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + include_inactive=include_inactive, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesPage: + """ + Retrieve a single page of InsightsQuestionnairesInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param include_inactive: Flag indicating whether to include inactive questionnaires or not + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesInstance + """ + data = values.of( + { + "Authorization": authorization, + "IncludeInactive": serialize.boolean_to_string(include_inactive), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsQuestionnairesPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + include_inactive: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesPage: + """ + Asynchronously retrieve a single page of InsightsQuestionnairesInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param include_inactive: Flag indicating whether to include inactive questionnaires or not + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesInstance + """ + data = values.of( + { + "Authorization": authorization, + "IncludeInactive": serialize.boolean_to_string(include_inactive), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsQuestionnairesPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsQuestionnairesPage: + """ + Retrieve a specific page of InsightsQuestionnairesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsQuestionnairesPage(self._version, response) + + async def get_page_async(self, target_url: str) -> InsightsQuestionnairesPage: + """ + Asynchronously retrieve a specific page of InsightsQuestionnairesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsQuestionnairesPage(self._version, response) + + def get(self, questionnaire_sid: str) -> InsightsQuestionnairesContext: + """ + Constructs a InsightsQuestionnairesContext + + :param questionnaire_sid: The SID of the questionnaire + """ + return InsightsQuestionnairesContext( + self._version, questionnaire_sid=questionnaire_sid + ) + + def __call__(self, questionnaire_sid: str) -> InsightsQuestionnairesContext: + """ + Constructs a InsightsQuestionnairesContext + + :param questionnaire_sid: The SID of the questionnaire + """ + return InsightsQuestionnairesContext( + self._version, questionnaire_sid=questionnaire_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_category.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_category.py new file mode 100644 index 0000000..28057db --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_category.py @@ -0,0 +1,583 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsQuestionnairesCategoryInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar category_sid: The SID of the category + :ivar name: The name of this category. + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + category_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.category_sid: Optional[str] = payload.get("category_sid") + self.name: Optional[str] = payload.get("name") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "category_sid": category_sid or self.category_sid, + } + self._context: Optional[InsightsQuestionnairesCategoryContext] = None + + @property + def _proxy(self) -> "InsightsQuestionnairesCategoryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InsightsQuestionnairesCategoryContext for this InsightsQuestionnairesCategoryInstance + """ + if self._context is None: + self._context = InsightsQuestionnairesCategoryContext( + self._version, + category_sid=self._solution["category_sid"], + ) + return self._context + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesCategoryInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + authorization=authorization, + ) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesCategoryInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + authorization=authorization, + ) + + def update( + self, name: str, authorization: Union[str, object] = values.unset + ) -> "InsightsQuestionnairesCategoryInstance": + """ + Update the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The updated InsightsQuestionnairesCategoryInstance + """ + return self._proxy.update( + name=name, + authorization=authorization, + ) + + async def update_async( + self, name: str, authorization: Union[str, object] = values.unset + ) -> "InsightsQuestionnairesCategoryInstance": + """ + Asynchronous coroutine to update the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The updated InsightsQuestionnairesCategoryInstance + """ + return await self._proxy.update_async( + name=name, + authorization=authorization, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InsightsQuestionnairesCategoryContext(InstanceContext): + + def __init__(self, version: Version, category_sid: str): + """ + Initialize the InsightsQuestionnairesCategoryContext + + :param version: Version that contains the resource + :param category_sid: The SID of the category to be updated + """ + super().__init__(version) + + # Path Solution + self._solution = { + "category_sid": category_sid, + } + self._uri = "/Insights/QualityManagement/Categories/{category_sid}".format( + **self._solution + ) + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesCategoryInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesCategoryInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def update( + self, name: str, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesCategoryInstance: + """ + Update the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The updated InsightsQuestionnairesCategoryInstance + """ + data = values.of( + { + "Name": name, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesCategoryInstance( + self._version, payload, category_sid=self._solution["category_sid"] + ) + + async def update_async( + self, name: str, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesCategoryInstance: + """ + Asynchronous coroutine to update the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The updated InsightsQuestionnairesCategoryInstance + """ + data = values.of( + { + "Name": name, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesCategoryInstance( + self._version, payload, category_sid=self._solution["category_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InsightsQuestionnairesCategoryPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> InsightsQuestionnairesCategoryInstance: + """ + Build an instance of InsightsQuestionnairesCategoryInstance + + :param payload: Payload response from the API + """ + return InsightsQuestionnairesCategoryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsQuestionnairesCategoryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsQuestionnairesCategoryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Categories" + + def create( + self, name: str, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesCategoryInstance: + """ + Create the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsQuestionnairesCategoryInstance + """ + + data = values.of( + { + "Name": name, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesCategoryInstance(self._version, payload) + + async def create_async( + self, name: str, authorization: Union[str, object] = values.unset + ) -> InsightsQuestionnairesCategoryInstance: + """ + Asynchronously create the InsightsQuestionnairesCategoryInstance + + :param name: The name of this category. + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsQuestionnairesCategoryInstance + """ + + data = values.of( + { + "Name": name, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesCategoryInstance(self._version, payload) + + def stream( + self, + authorization: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsQuestionnairesCategoryInstance]: + """ + Streams InsightsQuestionnairesCategoryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(authorization=authorization, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsQuestionnairesCategoryInstance]: + """ + Asynchronously streams InsightsQuestionnairesCategoryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesCategoryInstance]: + """ + Lists InsightsQuestionnairesCategoryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesCategoryInstance]: + """ + Asynchronously lists InsightsQuestionnairesCategoryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesCategoryPage: + """ + Retrieve a single page of InsightsQuestionnairesCategoryInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesCategoryInstance + """ + data = values.of( + { + "Authorization": authorization, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsQuestionnairesCategoryPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesCategoryPage: + """ + Asynchronously retrieve a single page of InsightsQuestionnairesCategoryInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesCategoryInstance + """ + data = values.of( + { + "Authorization": authorization, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsQuestionnairesCategoryPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsQuestionnairesCategoryPage: + """ + Retrieve a specific page of InsightsQuestionnairesCategoryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesCategoryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsQuestionnairesCategoryPage(self._version, response) + + async def get_page_async( + self, target_url: str + ) -> InsightsQuestionnairesCategoryPage: + """ + Asynchronously retrieve a specific page of InsightsQuestionnairesCategoryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesCategoryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsQuestionnairesCategoryPage(self._version, response) + + def get(self, category_sid: str) -> InsightsQuestionnairesCategoryContext: + """ + Constructs a InsightsQuestionnairesCategoryContext + + :param category_sid: The SID of the category to be updated + """ + return InsightsQuestionnairesCategoryContext( + self._version, category_sid=category_sid + ) + + def __call__(self, category_sid: str) -> InsightsQuestionnairesCategoryContext: + """ + Constructs a InsightsQuestionnairesCategoryContext + + :param category_sid: The SID of the category to be updated + """ + return InsightsQuestionnairesCategoryContext( + self._version, category_sid=category_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_question.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_question.py new file mode 100644 index 0000000..e4f2f4a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_questionnaires_question.py @@ -0,0 +1,701 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsQuestionnairesQuestionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar question_sid: The SID of the question + :ivar question: The question. + :ivar description: The description for the question. + :ivar category: The Category for the question. + :ivar answer_set_id: The answer_set for the question. + :ivar allow_na: The flag to enable for disable NA for answer. + :ivar usage: Integer value that tells a particular question is used by how many questionnaires + :ivar answer_set: Set of answers for the question + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + question_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.question_sid: Optional[str] = payload.get("question_sid") + self.question: Optional[str] = payload.get("question") + self.description: Optional[str] = payload.get("description") + self.category: Optional[Dict[str, object]] = payload.get("category") + self.answer_set_id: Optional[str] = payload.get("answer_set_id") + self.allow_na: Optional[bool] = payload.get("allow_na") + self.usage: Optional[int] = deserialize.integer(payload.get("usage")) + self.answer_set: Optional[Dict[str, object]] = payload.get("answer_set") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "question_sid": question_sid or self.question_sid, + } + self._context: Optional[InsightsQuestionnairesQuestionContext] = None + + @property + def _proxy(self) -> "InsightsQuestionnairesQuestionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InsightsQuestionnairesQuestionContext for this InsightsQuestionnairesQuestionInstance + """ + if self._context is None: + self._context = InsightsQuestionnairesQuestionContext( + self._version, + question_sid=self._solution["question_sid"], + ) + return self._context + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesQuestionInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + authorization=authorization, + ) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesQuestionInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + authorization=authorization, + ) + + def update( + self, + allow_na: bool, + authorization: Union[str, object] = values.unset, + category_sid: Union[str, object] = values.unset, + question: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + answer_set_id: Union[str, object] = values.unset, + ) -> "InsightsQuestionnairesQuestionInstance": + """ + Update the InsightsQuestionnairesQuestionInstance + + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param category_sid: The SID of the category + :param question: The question. + :param description: The description for the question. + :param answer_set_id: The answer_set for the question. + + :returns: The updated InsightsQuestionnairesQuestionInstance + """ + return self._proxy.update( + allow_na=allow_na, + authorization=authorization, + category_sid=category_sid, + question=question, + description=description, + answer_set_id=answer_set_id, + ) + + async def update_async( + self, + allow_na: bool, + authorization: Union[str, object] = values.unset, + category_sid: Union[str, object] = values.unset, + question: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + answer_set_id: Union[str, object] = values.unset, + ) -> "InsightsQuestionnairesQuestionInstance": + """ + Asynchronous coroutine to update the InsightsQuestionnairesQuestionInstance + + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param category_sid: The SID of the category + :param question: The question. + :param description: The description for the question. + :param answer_set_id: The answer_set for the question. + + :returns: The updated InsightsQuestionnairesQuestionInstance + """ + return await self._proxy.update_async( + allow_na=allow_na, + authorization=authorization, + category_sid=category_sid, + question=question, + description=description, + answer_set_id=answer_set_id, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InsightsQuestionnairesQuestionContext(InstanceContext): + + def __init__(self, version: Version, question_sid: str): + """ + Initialize the InsightsQuestionnairesQuestionContext + + :param version: Version that contains the resource + :param question_sid: The SID of the question + """ + super().__init__(version) + + # Path Solution + self._solution = { + "question_sid": question_sid, + } + self._uri = "/Insights/QualityManagement/Questions/{question_sid}".format( + **self._solution + ) + + def delete(self, authorization: Union[str, object] = values.unset) -> bool: + """ + Deletes the InsightsQuestionnairesQuestionInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, authorization: Union[str, object] = values.unset + ) -> bool: + """ + Asynchronous coroutine that deletes the InsightsQuestionnairesQuestionInstance + + :param authorization: The Authorization HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def update( + self, + allow_na: bool, + authorization: Union[str, object] = values.unset, + category_sid: Union[str, object] = values.unset, + question: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + answer_set_id: Union[str, object] = values.unset, + ) -> InsightsQuestionnairesQuestionInstance: + """ + Update the InsightsQuestionnairesQuestionInstance + + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param category_sid: The SID of the category + :param question: The question. + :param description: The description for the question. + :param answer_set_id: The answer_set for the question. + + :returns: The updated InsightsQuestionnairesQuestionInstance + """ + data = values.of( + { + "AllowNa": serialize.boolean_to_string(allow_na), + "CategorySid": category_sid, + "Question": question, + "Description": description, + "AnswerSetId": answer_set_id, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesQuestionInstance( + self._version, payload, question_sid=self._solution["question_sid"] + ) + + async def update_async( + self, + allow_na: bool, + authorization: Union[str, object] = values.unset, + category_sid: Union[str, object] = values.unset, + question: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + answer_set_id: Union[str, object] = values.unset, + ) -> InsightsQuestionnairesQuestionInstance: + """ + Asynchronous coroutine to update the InsightsQuestionnairesQuestionInstance + + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param category_sid: The SID of the category + :param question: The question. + :param description: The description for the question. + :param answer_set_id: The answer_set for the question. + + :returns: The updated InsightsQuestionnairesQuestionInstance + """ + data = values.of( + { + "AllowNa": serialize.boolean_to_string(allow_na), + "CategorySid": category_sid, + "Question": question, + "Description": description, + "AnswerSetId": answer_set_id, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesQuestionInstance( + self._version, payload, question_sid=self._solution["question_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InsightsQuestionnairesQuestionPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> InsightsQuestionnairesQuestionInstance: + """ + Build an instance of InsightsQuestionnairesQuestionInstance + + :param payload: Payload response from the API + """ + return InsightsQuestionnairesQuestionInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsQuestionnairesQuestionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsQuestionnairesQuestionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Questions" + + def create( + self, + category_sid: str, + question: str, + answer_set_id: str, + allow_na: bool, + authorization: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> InsightsQuestionnairesQuestionInstance: + """ + Create the InsightsQuestionnairesQuestionInstance + + :param category_sid: The SID of the category + :param question: The question. + :param answer_set_id: The answer_set for the question. + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param description: The description for the question. + + :returns: The created InsightsQuestionnairesQuestionInstance + """ + + data = values.of( + { + "CategorySid": category_sid, + "Question": question, + "AnswerSetId": answer_set_id, + "AllowNa": serialize.boolean_to_string(allow_na), + "Description": description, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesQuestionInstance(self._version, payload) + + async def create_async( + self, + category_sid: str, + question: str, + answer_set_id: str, + allow_na: bool, + authorization: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> InsightsQuestionnairesQuestionInstance: + """ + Asynchronously create the InsightsQuestionnairesQuestionInstance + + :param category_sid: The SID of the category + :param question: The question. + :param answer_set_id: The answer_set for the question. + :param allow_na: The flag to enable for disable NA for answer. + :param authorization: The Authorization HTTP request header + :param description: The description for the question. + + :returns: The created InsightsQuestionnairesQuestionInstance + """ + + data = values.of( + { + "CategorySid": category_sid, + "Question": question, + "AnswerSetId": answer_set_id, + "AllowNa": serialize.boolean_to_string(allow_na), + "Description": description, + } + ) + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return InsightsQuestionnairesQuestionInstance(self._version, payload) + + def stream( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsQuestionnairesQuestionInstance]: + """ + Streams InsightsQuestionnairesQuestionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param List[str] category_sid: The list of category SIDs + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + category_sid=category_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsQuestionnairesQuestionInstance]: + """ + Asynchronously streams InsightsQuestionnairesQuestionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param List[str] category_sid: The list of category SIDs + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + category_sid=category_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesQuestionInstance]: + """ + Lists InsightsQuestionnairesQuestionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param List[str] category_sid: The list of category SIDs + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + category_sid=category_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsQuestionnairesQuestionInstance]: + """ + Asynchronously lists InsightsQuestionnairesQuestionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param List[str] category_sid: The list of category SIDs + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + category_sid=category_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesQuestionPage: + """ + Retrieve a single page of InsightsQuestionnairesQuestionInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param category_sid: The list of category SIDs + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesQuestionInstance + """ + data = values.of( + { + "Authorization": authorization, + "CategorySid": serialize.map(category_sid, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsQuestionnairesQuestionPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + category_sid: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsQuestionnairesQuestionPage: + """ + Asynchronously retrieve a single page of InsightsQuestionnairesQuestionInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param category_sid: The list of category SIDs + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsQuestionnairesQuestionInstance + """ + data = values.of( + { + "Authorization": authorization, + "CategorySid": serialize.map(category_sid, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsQuestionnairesQuestionPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsQuestionnairesQuestionPage: + """ + Retrieve a specific page of InsightsQuestionnairesQuestionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesQuestionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsQuestionnairesQuestionPage(self._version, response) + + async def get_page_async( + self, target_url: str + ) -> InsightsQuestionnairesQuestionPage: + """ + Asynchronously retrieve a specific page of InsightsQuestionnairesQuestionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsQuestionnairesQuestionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsQuestionnairesQuestionPage(self._version, response) + + def get(self, question_sid: str) -> InsightsQuestionnairesQuestionContext: + """ + Constructs a InsightsQuestionnairesQuestionContext + + :param question_sid: The SID of the question + """ + return InsightsQuestionnairesQuestionContext( + self._version, question_sid=question_sid + ) + + def __call__(self, question_sid: str) -> InsightsQuestionnairesQuestionContext: + """ + Constructs a InsightsQuestionnairesQuestionContext + + :param question_sid: The SID of the question + """ + return InsightsQuestionnairesQuestionContext( + self._version, question_sid=question_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_segments.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_segments.py new file mode 100644 index 0000000..2d50980 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_segments.py @@ -0,0 +1,375 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InsightsSegmentsInstance(InstanceResource): + """ + :ivar segment_id: To unique id of the segment + :ivar external_id: The unique id for the conversation. + :ivar queue: + :ivar external_contact: + :ivar external_segment_link_id: The uuid for the external_segment_link. + :ivar date: The date of the conversation. + :ivar account_id: The unique id for the account. + :ivar external_segment_link: The hyperlink to recording of the task event. + :ivar agent_id: The unique id for the agent. + :ivar agent_phone: The phone number of the agent. + :ivar agent_name: The name of the agent. + :ivar agent_team_name: The team name to which agent belongs. + :ivar agent_team_name_in_hierarchy: he team name to which agent belongs. + :ivar agent_link: The link to the agent conversation. + :ivar customer_phone: The phone number of the customer. + :ivar customer_name: The name of the customer. + :ivar customer_link: The link to the customer conversation. + :ivar segment_recording_offset: The offset value for the recording. + :ivar media: The media identifiers of the conversation. + :ivar assessment_type: The type of the assessment. + :ivar assessment_percentage: The percentage scored on the Assessments. + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.segment_id: Optional[str] = payload.get("segment_id") + self.external_id: Optional[str] = payload.get("external_id") + self.queue: Optional[str] = payload.get("queue") + self.external_contact: Optional[str] = payload.get("external_contact") + self.external_segment_link_id: Optional[str] = payload.get( + "external_segment_link_id" + ) + self.date: Optional[str] = payload.get("date") + self.account_id: Optional[str] = payload.get("account_id") + self.external_segment_link: Optional[str] = payload.get("external_segment_link") + self.agent_id: Optional[str] = payload.get("agent_id") + self.agent_phone: Optional[str] = payload.get("agent_phone") + self.agent_name: Optional[str] = payload.get("agent_name") + self.agent_team_name: Optional[str] = payload.get("agent_team_name") + self.agent_team_name_in_hierarchy: Optional[str] = payload.get( + "agent_team_name_in_hierarchy" + ) + self.agent_link: Optional[str] = payload.get("agent_link") + self.customer_phone: Optional[str] = payload.get("customer_phone") + self.customer_name: Optional[str] = payload.get("customer_name") + self.customer_link: Optional[str] = payload.get("customer_link") + self.segment_recording_offset: Optional[str] = payload.get( + "segment_recording_offset" + ) + self.media: Optional[Dict[str, object]] = payload.get("media") + self.assessment_type: Optional[Dict[str, object]] = payload.get( + "assessment_type" + ) + self.assessment_percentage: Optional[Dict[str, object]] = payload.get( + "assessment_percentage" + ) + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsSegmentsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InsightsSegmentsInstance: + """ + Build an instance of InsightsSegmentsInstance + + :param payload: Payload response from the API + """ + return InsightsSegmentsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InsightsSegmentsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsSegmentsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/Segments" + + def stream( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InsightsSegmentsInstance]: + """ + Streams InsightsSegmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: To unique id of the segment + :param List[str] reservation_id: The list of reservation Ids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + authorization=authorization, + segment_id=segment_id, + reservation_id=reservation_id, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InsightsSegmentsInstance]: + """ + Asynchronously streams InsightsSegmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: To unique id of the segment + :param List[str] reservation_id: The list of reservation Ids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + authorization=authorization, + segment_id=segment_id, + reservation_id=reservation_id, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsSegmentsInstance]: + """ + Lists InsightsSegmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: To unique id of the segment + :param List[str] reservation_id: The list of reservation Ids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + authorization=authorization, + segment_id=segment_id, + reservation_id=reservation_id, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InsightsSegmentsInstance]: + """ + Asynchronously lists InsightsSegmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str authorization: The Authorization HTTP request header + :param str segment_id: To unique id of the segment + :param List[str] reservation_id: The list of reservation Ids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + authorization=authorization, + segment_id=segment_id, + reservation_id=reservation_id, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsSegmentsPage: + """ + Retrieve a single page of InsightsSegmentsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: To unique id of the segment + :param reservation_id: The list of reservation Ids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsSegmentsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "ReservationId": serialize.map(reservation_id, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InsightsSegmentsPage(self._version, response) + + async def page_async( + self, + authorization: Union[str, object] = values.unset, + segment_id: Union[str, object] = values.unset, + reservation_id: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InsightsSegmentsPage: + """ + Asynchronously retrieve a single page of InsightsSegmentsInstance records from the API. + Request is executed immediately + + :param authorization: The Authorization HTTP request header + :param segment_id: To unique id of the segment + :param reservation_id: The list of reservation Ids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InsightsSegmentsInstance + """ + data = values.of( + { + "Authorization": authorization, + "SegmentId": segment_id, + "ReservationId": serialize.map(reservation_id, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InsightsSegmentsPage(self._version, response) + + def get_page(self, target_url: str) -> InsightsSegmentsPage: + """ + Retrieve a specific page of InsightsSegmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsSegmentsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InsightsSegmentsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> InsightsSegmentsPage: + """ + Asynchronously retrieve a specific page of InsightsSegmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InsightsSegmentsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InsightsSegmentsPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_session.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_session.py new file mode 100644 index 0000000..b4174aa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_session.py @@ -0,0 +1,190 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class InsightsSessionInstance(InstanceResource): + """ + :ivar workspace_id: Unique ID to identify the user's workspace + :ivar session_expiry: The session expiry date and time, given in ISO 8601 format. + :ivar session_id: The unique ID for the session + :ivar base_url: The base URL to fetch reports and dashboards + :ivar url: The URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.workspace_id: Optional[str] = payload.get("workspace_id") + self.session_expiry: Optional[str] = payload.get("session_expiry") + self.session_id: Optional[str] = payload.get("session_id") + self.base_url: Optional[str] = payload.get("base_url") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[InsightsSessionContext] = None + + @property + def _proxy(self) -> "InsightsSessionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InsightsSessionContext for this InsightsSessionInstance + """ + if self._context is None: + self._context = InsightsSessionContext( + self._version, + ) + return self._context + + def create( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsSessionInstance": + """ + Create the InsightsSessionInstance + + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsSessionInstance + """ + return self._proxy.create( + authorization=authorization, + ) + + async def create_async( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsSessionInstance": + """ + Asynchronous coroutine to create the InsightsSessionInstance + + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsSessionInstance + """ + return await self._proxy.create_async( + authorization=authorization, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsSessionContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the InsightsSessionContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Insights/Session" + + def create( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSessionInstance: + """ + Create the InsightsSessionInstance + + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsSessionInstance + """ + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.create(method="POST", uri=self._uri, data=data) + + return InsightsSessionInstance(self._version, payload) + + async def create_async( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSessionInstance: + """ + Asynchronous coroutine to create the InsightsSessionInstance + + :param authorization: The Authorization HTTP request header + + :returns: The created InsightsSessionInstance + """ + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data + ) + + return InsightsSessionInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsSessionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsSessionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> InsightsSessionContext: + """ + Constructs a InsightsSessionContext + + """ + return InsightsSessionContext(self._version) + + def __call__(self) -> InsightsSessionContext: + """ + Constructs a InsightsSessionContext + + """ + return InsightsSessionContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_answer_sets.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_answer_sets.py new file mode 100644 index 0000000..767737d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_answer_sets.py @@ -0,0 +1,112 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class InsightsSettingsAnswerSetsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar answer_sets: The lis of answer sets + :ivar answer_set_categories: The list of answer set categories + :ivar not_applicable: The details for not applicable answer set + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.answer_sets: Optional[Dict[str, object]] = payload.get("answer_sets") + self.answer_set_categories: Optional[Dict[str, object]] = payload.get( + "answer_set_categories" + ) + self.not_applicable: Optional[Dict[str, object]] = payload.get("not_applicable") + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsSettingsAnswerSetsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsSettingsAnswerSetsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Settings/AnswerSets" + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSettingsAnswerSetsInstance: + """ + Asynchronously fetch the InsightsSettingsAnswerSetsInstance + + :param authorization: The Authorization HTTP request header + :returns: The fetched InsightsSettingsAnswerSetsInstance + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, headers=headers) + + return InsightsSettingsAnswerSetsInstance(self._version, payload) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSettingsAnswerSetsInstance: + """ + Asynchronously fetch the InsightsSettingsAnswerSetsInstance + + :param authorization: The Authorization HTTP request header + :returns: The fetched InsightsSettingsAnswerSetsInstance + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers + ) + + return InsightsSettingsAnswerSetsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_comment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_comment.py new file mode 100644 index 0000000..5d41e8d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_settings_comment.py @@ -0,0 +1,106 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class InsightsSettingsCommentInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Insights resource and owns this resource. + :ivar comments: + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.comments: Optional[Dict[str, object]] = payload.get("comments") + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsSettingsCommentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsSettingsCommentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Insights/QualityManagement/Settings/CommentTags" + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSettingsCommentInstance: + """ + Asynchronously fetch the InsightsSettingsCommentInstance + + :param authorization: The Authorization HTTP request header + :returns: The fetched InsightsSettingsCommentInstance + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, headers=headers) + + return InsightsSettingsCommentInstance(self._version, payload) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> InsightsSettingsCommentInstance: + """ + Asynchronously fetch the InsightsSettingsCommentInstance + + :param authorization: The Authorization HTTP request header + :returns: The fetched InsightsSettingsCommentInstance + """ + headers = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, headers=headers + ) + + return InsightsSettingsCommentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_user_roles.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_user_roles.py new file mode 100644 index 0000000..9dbea5f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/insights_user_roles.py @@ -0,0 +1,192 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class InsightsUserRolesInstance(InstanceResource): + """ + :ivar roles: Flex Insights roles for the user + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.roles: Optional[List[str]] = payload.get("roles") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[InsightsUserRolesContext] = None + + @property + def _proxy(self) -> "InsightsUserRolesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InsightsUserRolesContext for this InsightsUserRolesInstance + """ + if self._context is None: + self._context = InsightsUserRolesContext( + self._version, + ) + return self._context + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsUserRolesInstance": + """ + Fetch the InsightsUserRolesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsUserRolesInstance + """ + return self._proxy.fetch( + authorization=authorization, + ) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> "InsightsUserRolesInstance": + """ + Asynchronous coroutine to fetch the InsightsUserRolesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsUserRolesInstance + """ + return await self._proxy.fetch_async( + authorization=authorization, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsUserRolesContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the InsightsUserRolesContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Insights/UserRoles" + + def fetch( + self, authorization: Union[str, object] = values.unset + ) -> InsightsUserRolesInstance: + """ + Fetch the InsightsUserRolesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsUserRolesInstance + """ + + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return InsightsUserRolesInstance( + self._version, + payload, + ) + + async def fetch_async( + self, authorization: Union[str, object] = values.unset + ) -> InsightsUserRolesInstance: + """ + Asynchronous coroutine to fetch the InsightsUserRolesInstance + + :param authorization: The Authorization HTTP request header + + :returns: The fetched InsightsUserRolesInstance + """ + + data = values.of( + { + "Authorization": authorization, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return InsightsUserRolesInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class InsightsUserRolesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InsightsUserRolesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> InsightsUserRolesContext: + """ + Constructs a InsightsUserRolesContext + + """ + return InsightsUserRolesContext(self._version) + + def __call__(self) -> InsightsUserRolesContext: + """ + Constructs a InsightsUserRolesContext + + """ + return InsightsUserRolesContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__init__.py new file mode 100644 index 0000000..992cc9b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__init__.py @@ -0,0 +1,284 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.flex_api.v1.interaction.interaction_channel import ( + InteractionChannelList, +) + + +class InteractionInstance(InstanceResource): + """ + :ivar sid: The unique string created by Twilio to identify an Interaction resource, prefixed with KD. + :ivar channel: A JSON object that defines the Interaction’s communication channel and includes details about the channel. See the [Outbound SMS](https://www.twilio.com/docs/flex/developer/conversations/interactions-api/interactions#agent-initiated-outbound-interactions) and [inbound (API-initiated)](https://www.twilio.com/docs/flex/developer/conversations/interactions-api/interactions#api-initiated-contact) Channel object examples. + :ivar routing: A JSON Object representing the routing rules for the Interaction Channel. See [Outbound SMS Example](https://www.twilio.com/docs/flex/developer/conversations/interactions-api/interactions#agent-initiated-outbound-interactions) for an example Routing object. The Interactions resource uses TaskRouter for all routing functionality. All attributes in the Routing object on your Interaction request body are added “as is” to the task. For a list of known attributes consumed by the Flex UI and/or Flex Insights, see [Known Task Attributes](https://www.twilio.com/docs/flex/developer/conversations/interactions-api#task-attributes). + :ivar url: + :ivar links: + :ivar interaction_context_sid: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.channel: Optional[Dict[str, object]] = payload.get("channel") + self.routing: Optional[Dict[str, object]] = payload.get("routing") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.interaction_context_sid: Optional[str] = payload.get( + "interaction_context_sid" + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[InteractionContext] = None + + @property + def _proxy(self) -> "InteractionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InteractionContext for this InteractionInstance + """ + if self._context is None: + self._context = InteractionContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "InteractionInstance": + """ + Fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InteractionInstance": + """ + Asynchronous coroutine to fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + return await self._proxy.fetch_async() + + @property + def channels(self) -> InteractionChannelList: + """ + Access the channels + """ + return self._proxy.channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the InteractionContext + + :param version: Version that contains the resource + :param sid: The SID of the Interaction resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Interactions/{sid}".format(**self._solution) + + self._channels: Optional[InteractionChannelList] = None + + def fetch(self) -> InteractionInstance: + """ + Fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InteractionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InteractionInstance: + """ + Asynchronous coroutine to fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InteractionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def channels(self) -> InteractionChannelList: + """ + Access the channels + """ + if self._channels is None: + self._channels = InteractionChannelList( + self._version, + self._solution["sid"], + ) + return self._channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InteractionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Interactions" + + def create( + self, + channel: object, + routing: object, + interaction_context_sid: Union[str, object] = values.unset, + ) -> InteractionInstance: + """ + Create the InteractionInstance + + :param channel: The Interaction's channel. + :param routing: The Interaction's routing logic. + :param interaction_context_sid: The Interaction context sid is used for adding a context lookup sid + + :returns: The created InteractionInstance + """ + + data = values.of( + { + "Channel": serialize.object(channel), + "Routing": serialize.object(routing), + "InteractionContextSid": interaction_context_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionInstance(self._version, payload) + + async def create_async( + self, + channel: object, + routing: object, + interaction_context_sid: Union[str, object] = values.unset, + ) -> InteractionInstance: + """ + Asynchronously create the InteractionInstance + + :param channel: The Interaction's channel. + :param routing: The Interaction's routing logic. + :param interaction_context_sid: The Interaction context sid is used for adding a context lookup sid + + :returns: The created InteractionInstance + """ + + data = values.of( + { + "Channel": serialize.object(channel), + "Routing": serialize.object(routing), + "InteractionContextSid": interaction_context_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionInstance(self._version, payload) + + def get(self, sid: str) -> InteractionContext: + """ + Constructs a InteractionContext + + :param sid: The SID of the Interaction resource to fetch. + """ + return InteractionContext(self._version, sid=sid) + + def __call__(self, sid: str) -> InteractionContext: + """ + Constructs a InteractionContext + + :param sid: The SID of the Interaction resource to fetch. + """ + return InteractionContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8b1ebaf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__init__.py new file mode 100644 index 0000000..a26dc04 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__init__.py @@ -0,0 +1,598 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.flex_api.v1.interaction.interaction_channel.interaction_channel_invite import ( + InteractionChannelInviteList, +) +from twilio.rest.flex_api.v1.interaction.interaction_channel.interaction_channel_participant import ( + InteractionChannelParticipantList, +) + + +class InteractionChannelInstance(InstanceResource): + + class ChannelStatus(object): + SETUP = "setup" + ACTIVE = "active" + FAILED = "failed" + CLOSED = "closed" + INACTIVE = "inactive" + + class Type(object): + VOICE = "voice" + SMS = "sms" + EMAIL = "email" + WEB = "web" + WHATSAPP = "whatsapp" + CHAT = "chat" + MESSENGER = "messenger" + GBM = "gbm" + + class UpdateChannelStatus(object): + CLOSED = "closed" + INACTIVE = "inactive" + + """ + :ivar sid: The unique string created by Twilio to identify an Interaction Channel resource, prefixed with UO. + :ivar interaction_sid: The unique string created by Twilio to identify an Interaction resource, prefixed with KD. + :ivar type: + :ivar status: + :ivar error_code: The Twilio error code for a failed channel. + :ivar error_message: The error message for a failed channel. + :ivar url: + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + interaction_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.interaction_sid: Optional[str] = payload.get("interaction_sid") + self.type: Optional["InteractionChannelInstance.Type"] = payload.get("type") + self.status: Optional["InteractionChannelInstance.ChannelStatus"] = payload.get( + "status" + ) + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.error_message: Optional[str] = payload.get("error_message") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "interaction_sid": interaction_sid, + "sid": sid or self.sid, + } + self._context: Optional[InteractionChannelContext] = None + + @property + def _proxy(self) -> "InteractionChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InteractionChannelContext for this InteractionChannelInstance + """ + if self._context is None: + self._context = InteractionChannelContext( + self._version, + interaction_sid=self._solution["interaction_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "InteractionChannelInstance": + """ + Fetch the InteractionChannelInstance + + + :returns: The fetched InteractionChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InteractionChannelInstance": + """ + Asynchronous coroutine to fetch the InteractionChannelInstance + + + :returns: The fetched InteractionChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: "InteractionChannelInstance.UpdateChannelStatus", + routing: Union[object, object] = values.unset, + ) -> "InteractionChannelInstance": + """ + Update the InteractionChannelInstance + + :param status: + :param routing: It changes the state of associated tasks. Routing status is required, When the channel status is set to `inactive`. Allowed Value for routing status is `closed`. Otherwise Optional, if not specified, all tasks will be set to `wrapping`. + + :returns: The updated InteractionChannelInstance + """ + return self._proxy.update( + status=status, + routing=routing, + ) + + async def update_async( + self, + status: "InteractionChannelInstance.UpdateChannelStatus", + routing: Union[object, object] = values.unset, + ) -> "InteractionChannelInstance": + """ + Asynchronous coroutine to update the InteractionChannelInstance + + :param status: + :param routing: It changes the state of associated tasks. Routing status is required, When the channel status is set to `inactive`. Allowed Value for routing status is `closed`. Otherwise Optional, if not specified, all tasks will be set to `wrapping`. + + :returns: The updated InteractionChannelInstance + """ + return await self._proxy.update_async( + status=status, + routing=routing, + ) + + @property + def invites(self) -> InteractionChannelInviteList: + """ + Access the invites + """ + return self._proxy.invites + + @property + def participants(self) -> InteractionChannelParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionChannelContext(InstanceContext): + + def __init__(self, version: Version, interaction_sid: str, sid: str): + """ + Initialize the InteractionChannelContext + + :param version: Version that contains the resource + :param interaction_sid: The unique string created by Twilio to identify an Interaction resource, prefixed with KD. + :param sid: The unique string created by Twilio to identify an Interaction Channel resource, prefixed with UO. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "interaction_sid": interaction_sid, + "sid": sid, + } + self._uri = "/Interactions/{interaction_sid}/Channels/{sid}".format( + **self._solution + ) + + self._invites: Optional[InteractionChannelInviteList] = None + self._participants: Optional[InteractionChannelParticipantList] = None + + def fetch(self) -> InteractionChannelInstance: + """ + Fetch the InteractionChannelInstance + + + :returns: The fetched InteractionChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InteractionChannelInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InteractionChannelInstance: + """ + Asynchronous coroutine to fetch the InteractionChannelInstance + + + :returns: The fetched InteractionChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InteractionChannelInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + status: "InteractionChannelInstance.UpdateChannelStatus", + routing: Union[object, object] = values.unset, + ) -> InteractionChannelInstance: + """ + Update the InteractionChannelInstance + + :param status: + :param routing: It changes the state of associated tasks. Routing status is required, When the channel status is set to `inactive`. Allowed Value for routing status is `closed`. Otherwise Optional, if not specified, all tasks will be set to `wrapping`. + + :returns: The updated InteractionChannelInstance + """ + data = values.of( + { + "Status": status, + "Routing": serialize.object(routing), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + status: "InteractionChannelInstance.UpdateChannelStatus", + routing: Union[object, object] = values.unset, + ) -> InteractionChannelInstance: + """ + Asynchronous coroutine to update the InteractionChannelInstance + + :param status: + :param routing: It changes the state of associated tasks. Routing status is required, When the channel status is set to `inactive`. Allowed Value for routing status is `closed`. Otherwise Optional, if not specified, all tasks will be set to `wrapping`. + + :returns: The updated InteractionChannelInstance + """ + data = values.of( + { + "Status": status, + "Routing": serialize.object(routing), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + sid=self._solution["sid"], + ) + + @property + def invites(self) -> InteractionChannelInviteList: + """ + Access the invites + """ + if self._invites is None: + self._invites = InteractionChannelInviteList( + self._version, + self._solution["interaction_sid"], + self._solution["sid"], + ) + return self._invites + + @property + def participants(self) -> InteractionChannelParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = InteractionChannelParticipantList( + self._version, + self._solution["interaction_sid"], + self._solution["sid"], + ) + return self._participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InteractionChannelInstance: + """ + Build an instance of InteractionChannelInstance + + :param payload: Payload response from the API + """ + return InteractionChannelInstance( + self._version, payload, interaction_sid=self._solution["interaction_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InteractionChannelList(ListResource): + + def __init__(self, version: Version, interaction_sid: str): + """ + Initialize the InteractionChannelList + + :param version: Version that contains the resource + :param interaction_sid: The unique string created by Twilio to identify an Interaction resource, prefixed with KD. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "interaction_sid": interaction_sid, + } + self._uri = "/Interactions/{interaction_sid}/Channels".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InteractionChannelInstance]: + """ + Streams InteractionChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InteractionChannelInstance]: + """ + Asynchronously streams InteractionChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelInstance]: + """ + Lists InteractionChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelInstance]: + """ + Asynchronously lists InteractionChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelPage: + """ + Retrieve a single page of InteractionChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InteractionChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelPage: + """ + Asynchronously retrieve a single page of InteractionChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InteractionChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InteractionChannelPage: + """ + Retrieve a specific page of InteractionChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InteractionChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InteractionChannelPage: + """ + Asynchronously retrieve a specific page of InteractionChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InteractionChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> InteractionChannelContext: + """ + Constructs a InteractionChannelContext + + :param sid: The unique string created by Twilio to identify an Interaction Channel resource, prefixed with UO. + """ + return InteractionChannelContext( + self._version, interaction_sid=self._solution["interaction_sid"], sid=sid + ) + + def __call__(self, sid: str) -> InteractionChannelContext: + """ + Constructs a InteractionChannelContext + + :param sid: The unique string created by Twilio to identify an Interaction Channel resource, prefixed with UO. + """ + return InteractionChannelContext( + self._version, interaction_sid=self._solution["interaction_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fb49a4d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_invite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_invite.cpython-310.pyc new file mode 100644 index 0000000..251ff42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_invite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_participant.cpython-310.pyc new file mode 100644 index 0000000..a6e29f1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/__pycache__/interaction_channel_participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_invite.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_invite.py new file mode 100644 index 0000000..5292297 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_invite.py @@ -0,0 +1,356 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InteractionChannelInviteInstance(InstanceResource): + """ + :ivar sid: The unique string created by Twilio to identify an Interaction Channel Invite resource. + :ivar interaction_sid: The Interaction SID for this Channel. + :ivar channel_sid: The Channel SID for this Invite. + :ivar routing: A JSON object representing the routing rules for the Interaction Channel. See [Outbound SMS Example](https://www.twilio.com/docs/flex/developer/conversations/interactions-api/interactions#agent-initiated-outbound-interactions) for an example Routing object. The Interactions resource uses TaskRouter for all routing functionality. All attributes in the Routing object on your Interaction request body are added “as is” to the task. For a list of known attributes consumed by the Flex UI and/or Flex Insights, see [Known Task Attributes](https://www.twilio.com/docs/flex/developer/conversations/interactions-api#task-attributes). + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + interaction_sid: str, + channel_sid: str, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.interaction_sid: Optional[str] = payload.get("interaction_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.routing: Optional[Dict[str, object]] = payload.get("routing") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "interaction_sid": interaction_sid, + "channel_sid": channel_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionChannelInvitePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InteractionChannelInviteInstance: + """ + Build an instance of InteractionChannelInviteInstance + + :param payload: Payload response from the API + """ + return InteractionChannelInviteInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InteractionChannelInviteList(ListResource): + + def __init__(self, version: Version, interaction_sid: str, channel_sid: str): + """ + Initialize the InteractionChannelInviteList + + :param version: Version that contains the resource + :param interaction_sid: The Interaction SID for this Channel. + :param channel_sid: The Channel SID for this Participant. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "interaction_sid": interaction_sid, + "channel_sid": channel_sid, + } + self._uri = ( + "/Interactions/{interaction_sid}/Channels/{channel_sid}/Invites".format( + **self._solution + ) + ) + + def create(self, routing: object) -> InteractionChannelInviteInstance: + """ + Create the InteractionChannelInviteInstance + + :param routing: The Interaction's routing logic. + + :returns: The created InteractionChannelInviteInstance + """ + + data = values.of( + { + "Routing": serialize.object(routing), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelInviteInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async(self, routing: object) -> InteractionChannelInviteInstance: + """ + Asynchronously create the InteractionChannelInviteInstance + + :param routing: The Interaction's routing logic. + + :returns: The created InteractionChannelInviteInstance + """ + + data = values.of( + { + "Routing": serialize.object(routing), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelInviteInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InteractionChannelInviteInstance]: + """ + Streams InteractionChannelInviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InteractionChannelInviteInstance]: + """ + Asynchronously streams InteractionChannelInviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelInviteInstance]: + """ + Lists InteractionChannelInviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelInviteInstance]: + """ + Asynchronously lists InteractionChannelInviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelInvitePage: + """ + Retrieve a single page of InteractionChannelInviteInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelInviteInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InteractionChannelInvitePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelInvitePage: + """ + Asynchronously retrieve a single page of InteractionChannelInviteInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelInviteInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InteractionChannelInvitePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InteractionChannelInvitePage: + """ + Retrieve a specific page of InteractionChannelInviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelInviteInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InteractionChannelInvitePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InteractionChannelInvitePage: + """ + Asynchronously retrieve a specific page of InteractionChannelInviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelInviteInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InteractionChannelInvitePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_participant.py new file mode 100644 index 0000000..ba9f80f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/interaction/interaction_channel/interaction_channel_participant.py @@ -0,0 +1,575 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InteractionChannelParticipantInstance(InstanceResource): + + class Status(object): + CLOSED = "closed" + WRAPUP = "wrapup" + + class Type(object): + SUPERVISOR = "supervisor" + CUSTOMER = "customer" + EXTERNAL = "external" + AGENT = "agent" + UNKNOWN = "unknown" + + """ + :ivar sid: The unique string created by Twilio to identify an Interaction Channel Participant resource. + :ivar type: + :ivar interaction_sid: The Interaction Sid for this channel. + :ivar channel_sid: The Channel Sid for this Participant. + :ivar url: + :ivar routing_properties: The Participant's routing properties. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + interaction_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.type: Optional["InteractionChannelParticipantInstance.Type"] = payload.get( + "type" + ) + self.interaction_sid: Optional[str] = payload.get("interaction_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.url: Optional[str] = payload.get("url") + self.routing_properties: Optional[Dict[str, object]] = payload.get( + "routing_properties" + ) + + self._solution = { + "interaction_sid": interaction_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[InteractionChannelParticipantContext] = None + + @property + def _proxy(self) -> "InteractionChannelParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InteractionChannelParticipantContext for this InteractionChannelParticipantInstance + """ + if self._context is None: + self._context = InteractionChannelParticipantContext( + self._version, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update( + self, status: "InteractionChannelParticipantInstance.Status" + ) -> "InteractionChannelParticipantInstance": + """ + Update the InteractionChannelParticipantInstance + + :param status: + + :returns: The updated InteractionChannelParticipantInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "InteractionChannelParticipantInstance.Status" + ) -> "InteractionChannelParticipantInstance": + """ + Asynchronous coroutine to update the InteractionChannelParticipantInstance + + :param status: + + :returns: The updated InteractionChannelParticipantInstance + """ + return await self._proxy.update_async( + status=status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InteractionChannelParticipantContext(InstanceContext): + + def __init__( + self, version: Version, interaction_sid: str, channel_sid: str, sid: str + ): + """ + Initialize the InteractionChannelParticipantContext + + :param version: Version that contains the resource + :param interaction_sid: The Interaction Sid for this channel. + :param channel_sid: The Channel Sid for this Participant. + :param sid: The unique string created by Twilio to identify an Interaction Channel resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "interaction_sid": interaction_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = "/Interactions/{interaction_sid}/Channels/{channel_sid}/Participants/{sid}".format( + **self._solution + ) + + def update( + self, status: "InteractionChannelParticipantInstance.Status" + ) -> InteractionChannelParticipantInstance: + """ + Update the InteractionChannelParticipantInstance + + :param status: + + :returns: The updated InteractionChannelParticipantInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelParticipantInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "InteractionChannelParticipantInstance.Status" + ) -> InteractionChannelParticipantInstance: + """ + Asynchronous coroutine to update the InteractionChannelParticipantInstance + + :param status: + + :returns: The updated InteractionChannelParticipantInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelParticipantInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InteractionChannelParticipantPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> InteractionChannelParticipantInstance: + """ + Build an instance of InteractionChannelParticipantInstance + + :param payload: Payload response from the API + """ + return InteractionChannelParticipantInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InteractionChannelParticipantList(ListResource): + + def __init__(self, version: Version, interaction_sid: str, channel_sid: str): + """ + Initialize the InteractionChannelParticipantList + + :param version: Version that contains the resource + :param interaction_sid: The Interaction Sid for this channel. + :param channel_sid: The Channel Sid for this Participant. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "interaction_sid": interaction_sid, + "channel_sid": channel_sid, + } + self._uri = "/Interactions/{interaction_sid}/Channels/{channel_sid}/Participants".format( + **self._solution + ) + + def create( + self, + type: "InteractionChannelParticipantInstance.Type", + media_properties: object, + routing_properties: Union[object, object] = values.unset, + ) -> InteractionChannelParticipantInstance: + """ + Create the InteractionChannelParticipantInstance + + :param type: + :param media_properties: JSON representing the Media Properties for the new Participant. + :param routing_properties: Object representing the Routing Properties for the new Participant. + + :returns: The created InteractionChannelParticipantInstance + """ + + data = values.of( + { + "Type": type, + "MediaProperties": serialize.object(media_properties), + "RoutingProperties": serialize.object(routing_properties), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelParticipantInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + type: "InteractionChannelParticipantInstance.Type", + media_properties: object, + routing_properties: Union[object, object] = values.unset, + ) -> InteractionChannelParticipantInstance: + """ + Asynchronously create the InteractionChannelParticipantInstance + + :param type: + :param media_properties: JSON representing the Media Properties for the new Participant. + :param routing_properties: Object representing the Routing Properties for the new Participant. + + :returns: The created InteractionChannelParticipantInstance + """ + + data = values.of( + { + "Type": type, + "MediaProperties": serialize.object(media_properties), + "RoutingProperties": serialize.object(routing_properties), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InteractionChannelParticipantInstance( + self._version, + payload, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InteractionChannelParticipantInstance]: + """ + Streams InteractionChannelParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InteractionChannelParticipantInstance]: + """ + Asynchronously streams InteractionChannelParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelParticipantInstance]: + """ + Lists InteractionChannelParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionChannelParticipantInstance]: + """ + Asynchronously lists InteractionChannelParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelParticipantPage: + """ + Retrieve a single page of InteractionChannelParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InteractionChannelParticipantPage( + self._version, response, self._solution + ) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionChannelParticipantPage: + """ + Asynchronously retrieve a single page of InteractionChannelParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionChannelParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InteractionChannelParticipantPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> InteractionChannelParticipantPage: + """ + Retrieve a specific page of InteractionChannelParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InteractionChannelParticipantPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> InteractionChannelParticipantPage: + """ + Asynchronously retrieve a specific page of InteractionChannelParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionChannelParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InteractionChannelParticipantPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> InteractionChannelParticipantContext: + """ + Constructs a InteractionChannelParticipantContext + + :param sid: The unique string created by Twilio to identify an Interaction Channel resource. + """ + return InteractionChannelParticipantContext( + self._version, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InteractionChannelParticipantContext: + """ + Constructs a InteractionChannelParticipantContext + + :param sid: The unique string created by Twilio to identify an Interaction Channel resource. + """ + return InteractionChannelParticipantContext( + self._version, + interaction_sid=self._solution["interaction_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__init__.py new file mode 100644 index 0000000..09244d4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__init__.py @@ -0,0 +1,653 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.flex_api.v1.plugin.plugin_versions import PluginVersionsList + + +class PluginInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin resource and owns this resource. + :ivar unique_name: The name that uniquely identifies this Flex Plugin resource. + :ivar friendly_name: The friendly name this Flex Plugin resource. + :ivar description: A descriptive string that you create to describe the plugin resource. It can be up to 500 characters long + :ivar archived: Whether the Flex Plugin is archived. The default value is false. + :ivar date_created: The date and time in GMT-7 when the Flex Plugin was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT-7 when the Flex Plugin was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PluginContext] = None + + @property + def _proxy(self) -> "PluginContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginContext for this PluginInstance + """ + if self._context is None: + self._context = PluginContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginInstance": + """ + Fetch the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginInstance + """ + return self._proxy.fetch( + flex_metadata=flex_metadata, + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginInstance": + """ + Asynchronous coroutine to fetch the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginInstance + """ + return await self._proxy.fetch_async( + flex_metadata=flex_metadata, + ) + + def update( + self, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> "PluginInstance": + """ + Update the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you update to describe the plugin resource. It can be up to 500 characters long + + :returns: The updated PluginInstance + """ + return self._proxy.update( + flex_metadata=flex_metadata, + friendly_name=friendly_name, + description=description, + ) + + async def update_async( + self, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> "PluginInstance": + """ + Asynchronous coroutine to update the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you update to describe the plugin resource. It can be up to 500 characters long + + :returns: The updated PluginInstance + """ + return await self._proxy.update_async( + flex_metadata=flex_metadata, + friendly_name=friendly_name, + description=description, + ) + + @property + def plugin_versions(self) -> PluginVersionsList: + """ + Access the plugin_versions + """ + return self._proxy.plugin_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PluginContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Plugin resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/PluginService/Plugins/{sid}".format(**self._solution) + + self._plugin_versions: Optional[PluginVersionsList] = None + + def fetch(self, flex_metadata: Union[str, object] = values.unset) -> PluginInstance: + """ + Fetch the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PluginInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginInstance: + """ + Asynchronous coroutine to fetch the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PluginInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginInstance: + """ + Update the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you update to describe the plugin resource. It can be up to 500 characters long + + :returns: The updated PluginInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginInstance: + """ + Asynchronous coroutine to update the PluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you update to describe the plugin resource. It can be up to 500 characters long + + :returns: The updated PluginInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def plugin_versions(self) -> PluginVersionsList: + """ + Access the plugin_versions + """ + if self._plugin_versions is None: + self._plugin_versions = PluginVersionsList( + self._version, + self._solution["sid"], + ) + return self._plugin_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PluginInstance: + """ + Build an instance of PluginInstance + + :param payload: Payload response from the API + """ + return PluginInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PluginList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/PluginService/Plugins" + + def create( + self, + unique_name: str, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginInstance: + """ + Create the PluginInstance + + :param unique_name: The Flex Plugin's unique name. + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you create to describe the plugin resource. It can be up to 500 characters long + + :returns: The created PluginInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginInstance(self._version, payload) + + async def create_async( + self, + unique_name: str, + flex_metadata: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginInstance: + """ + Asynchronously create the PluginInstance + + :param unique_name: The Flex Plugin's unique name. + :param flex_metadata: The Flex-Metadata HTTP request header + :param friendly_name: The Flex Plugin's friendly name. + :param description: A descriptive string that you create to describe the plugin resource. It can be up to 500 characters long + + :returns: The created PluginInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginInstance(self._version, payload) + + def stream( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PluginInstance]: + """ + Streams PluginInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(flex_metadata=flex_metadata, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PluginInstance]: + """ + Asynchronously streams PluginInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + flex_metadata=flex_metadata, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginInstance]: + """ + Lists PluginInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginInstance]: + """ + Asynchronously lists PluginInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginPage: + """ + Retrieve a single page of PluginInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PluginPage(self._version, response) + + async def page_async( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginPage: + """ + Asynchronously retrieve a single page of PluginInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PluginPage(self._version, response) + + def get_page(self, target_url: str) -> PluginPage: + """ + Retrieve a specific page of PluginInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PluginPage(self._version, response) + + async def get_page_async(self, target_url: str) -> PluginPage: + """ + Asynchronously retrieve a specific page of PluginInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PluginPage(self._version, response) + + def get(self, sid: str) -> PluginContext: + """ + Constructs a PluginContext + + :param sid: The SID of the Flex Plugin resource to update. + """ + return PluginContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PluginContext: + """ + Constructs a PluginContext + + :param sid: The SID of the Flex Plugin resource to update. + """ + return PluginContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..efbe29c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/plugin_versions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/plugin_versions.cpython-310.pyc new file mode 100644 index 0000000..5e9409b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/__pycache__/plugin_versions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/plugin_versions.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/plugin_versions.py new file mode 100644 index 0000000..96313c8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin/plugin_versions.py @@ -0,0 +1,570 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PluginVersionsInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin Version resource. + :ivar plugin_sid: The SID of the Flex Plugin resource this Flex Plugin Version belongs to. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin Version resource and owns this resource. + :ivar version: The unique version of this Flex Plugin Version. + :ivar plugin_url: The URL of where the Flex Plugin Version JavaScript bundle is hosted on. + :ivar changelog: A changelog that describes the changes this Flex Plugin Version brings. + :ivar private: Whether to inject credentials while accessing this Plugin Version. The default value is false. + :ivar archived: Whether the Flex Plugin Version is archived. The default value is false. + :ivar date_created: The date and time in GMT when the Flex Plugin Version was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin Version resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + plugin_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.plugin_sid: Optional[str] = payload.get("plugin_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.version: Optional[str] = payload.get("version") + self.plugin_url: Optional[str] = payload.get("plugin_url") + self.changelog: Optional[str] = payload.get("changelog") + self.private: Optional[bool] = payload.get("private") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "plugin_sid": plugin_sid, + "sid": sid or self.sid, + } + self._context: Optional[PluginVersionsContext] = None + + @property + def _proxy(self) -> "PluginVersionsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginVersionsContext for this PluginVersionsInstance + """ + if self._context is None: + self._context = PluginVersionsContext( + self._version, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginVersionsInstance": + """ + Fetch the PluginVersionsInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginVersionsInstance + """ + return self._proxy.fetch( + flex_metadata=flex_metadata, + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginVersionsInstance": + """ + Asynchronous coroutine to fetch the PluginVersionsInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginVersionsInstance + """ + return await self._proxy.fetch_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginVersionsContext(InstanceContext): + + def __init__(self, version: Version, plugin_sid: str, sid: str): + """ + Initialize the PluginVersionsContext + + :param version: Version that contains the resource + :param plugin_sid: The SID of the Flex Plugin the resource to belongs to. + :param sid: The SID of the Flex Plugin Version resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "plugin_sid": plugin_sid, + "sid": sid, + } + self._uri = "/PluginService/Plugins/{plugin_sid}/Versions/{sid}".format( + **self._solution + ) + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginVersionsInstance: + """ + Fetch the PluginVersionsInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginVersionsInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PluginVersionsInstance( + self._version, + payload, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginVersionsInstance: + """ + Asynchronous coroutine to fetch the PluginVersionsInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginVersionsInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PluginVersionsInstance( + self._version, + payload, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginVersionsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PluginVersionsInstance: + """ + Build an instance of PluginVersionsInstance + + :param payload: Payload response from the API + """ + return PluginVersionsInstance( + self._version, payload, plugin_sid=self._solution["plugin_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PluginVersionsList(ListResource): + + def __init__(self, version: Version, plugin_sid: str): + """ + Initialize the PluginVersionsList + + :param version: Version that contains the resource + :param plugin_sid: The SID of the Flex Plugin the resource to belongs to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "plugin_sid": plugin_sid, + } + self._uri = "/PluginService/Plugins/{plugin_sid}/Versions".format( + **self._solution + ) + + def create( + self, + version: str, + plugin_url: str, + flex_metadata: Union[str, object] = values.unset, + changelog: Union[str, object] = values.unset, + private: Union[bool, object] = values.unset, + cli_version: Union[str, object] = values.unset, + validate_status: Union[str, object] = values.unset, + ) -> PluginVersionsInstance: + """ + Create the PluginVersionsInstance + + :param version: The Flex Plugin Version's version. + :param plugin_url: The URL of the Flex Plugin Version bundle + :param flex_metadata: The Flex-Metadata HTTP request header + :param changelog: The changelog of the Flex Plugin Version. + :param private: Whether this Flex Plugin Version requires authorization. + :param cli_version: The version of Flex Plugins CLI used to create this plugin + :param validate_status: The validation status of the plugin, indicating whether it has been validated + + :returns: The created PluginVersionsInstance + """ + + data = values.of( + { + "Version": version, + "PluginUrl": plugin_url, + "Changelog": changelog, + "Private": serialize.boolean_to_string(private), + "CliVersion": cli_version, + "ValidateStatus": validate_status, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginVersionsInstance( + self._version, payload, plugin_sid=self._solution["plugin_sid"] + ) + + async def create_async( + self, + version: str, + plugin_url: str, + flex_metadata: Union[str, object] = values.unset, + changelog: Union[str, object] = values.unset, + private: Union[bool, object] = values.unset, + cli_version: Union[str, object] = values.unset, + validate_status: Union[str, object] = values.unset, + ) -> PluginVersionsInstance: + """ + Asynchronously create the PluginVersionsInstance + + :param version: The Flex Plugin Version's version. + :param plugin_url: The URL of the Flex Plugin Version bundle + :param flex_metadata: The Flex-Metadata HTTP request header + :param changelog: The changelog of the Flex Plugin Version. + :param private: Whether this Flex Plugin Version requires authorization. + :param cli_version: The version of Flex Plugins CLI used to create this plugin + :param validate_status: The validation status of the plugin, indicating whether it has been validated + + :returns: The created PluginVersionsInstance + """ + + data = values.of( + { + "Version": version, + "PluginUrl": plugin_url, + "Changelog": changelog, + "Private": serialize.boolean_to_string(private), + "CliVersion": cli_version, + "ValidateStatus": validate_status, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginVersionsInstance( + self._version, payload, plugin_sid=self._solution["plugin_sid"] + ) + + def stream( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PluginVersionsInstance]: + """ + Streams PluginVersionsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(flex_metadata=flex_metadata, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PluginVersionsInstance]: + """ + Asynchronously streams PluginVersionsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + flex_metadata=flex_metadata, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginVersionsInstance]: + """ + Lists PluginVersionsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginVersionsInstance]: + """ + Asynchronously lists PluginVersionsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginVersionsPage: + """ + Retrieve a single page of PluginVersionsInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginVersionsInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PluginVersionsPage(self._version, response, self._solution) + + async def page_async( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginVersionsPage: + """ + Asynchronously retrieve a single page of PluginVersionsInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginVersionsInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PluginVersionsPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PluginVersionsPage: + """ + Retrieve a specific page of PluginVersionsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginVersionsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PluginVersionsPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PluginVersionsPage: + """ + Asynchronously retrieve a specific page of PluginVersionsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginVersionsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PluginVersionsPage(self._version, response, self._solution) + + def get(self, sid: str) -> PluginVersionsContext: + """ + Constructs a PluginVersionsContext + + :param sid: The SID of the Flex Plugin Version resource to fetch. + """ + return PluginVersionsContext( + self._version, plugin_sid=self._solution["plugin_sid"], sid=sid + ) + + def __call__(self, sid: str) -> PluginVersionsContext: + """ + Constructs a PluginVersionsContext + + :param sid: The SID of the Flex Plugin Version resource to fetch. + """ + return PluginVersionsContext( + self._version, plugin_sid=self._solution["plugin_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_archive.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_archive.py new file mode 100644 index 0000000..68f9bce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_archive.py @@ -0,0 +1,220 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PluginArchiveInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin resource and owns this resource. + :ivar unique_name: The name that uniquely identifies this Flex Plugin resource. + :ivar friendly_name: The friendly name this Flex Plugin resource. + :ivar description: A descriptive string that you create to describe the plugin resource. It can be up to 500 characters long + :ivar archived: Whether the Flex Plugin is archived. The default value is false. + :ivar date_created: The date and time in GMT when the Flex Plugin was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Flex Plugin was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PluginArchiveContext] = None + + @property + def _proxy(self) -> "PluginArchiveContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginArchiveContext for this PluginArchiveInstance + """ + if self._context is None: + self._context = PluginArchiveContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginArchiveInstance": + """ + Update the PluginArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginArchiveInstance + """ + return self._proxy.update( + flex_metadata=flex_metadata, + ) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginArchiveInstance": + """ + Asynchronous coroutine to update the PluginArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginArchiveInstance + """ + return await self._proxy.update_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginArchiveContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PluginArchiveContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Plugin resource to archive. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/PluginService/Plugins/{sid}/Archive".format(**self._solution) + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginArchiveInstance: + """ + Update the PluginArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginArchiveInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginArchiveInstance: + """ + Asynchronous coroutine to update the PluginArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginArchiveInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginArchiveList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginArchiveList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sid: str) -> PluginArchiveContext: + """ + Constructs a PluginArchiveContext + + :param sid: The SID of the Flex Plugin resource to archive. + """ + return PluginArchiveContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PluginArchiveContext: + """ + Constructs a PluginArchiveContext + + :param sid: The SID of the Flex Plugin resource to archive. + """ + return PluginArchiveContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__init__.py new file mode 100644 index 0000000..ebe1739 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__init__.py @@ -0,0 +1,543 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.flex_api.v1.plugin_configuration.configured_plugin import ( + ConfiguredPluginList, +) + + +class PluginConfigurationInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin Configuration resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin Configuration resource and owns this resource. + :ivar name: The name of this Flex Plugin Configuration. + :ivar description: The description of the Flex Plugin Configuration resource. + :ivar archived: Whether the Flex Plugin Configuration is archived. The default value is false. + :ivar date_created: The date and time in GMT when the Flex Plugin Configuration was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin Configuration resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.name: Optional[str] = payload.get("name") + self.description: Optional[str] = payload.get("description") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PluginConfigurationContext] = None + + @property + def _proxy(self) -> "PluginConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginConfigurationContext for this PluginConfigurationInstance + """ + if self._context is None: + self._context = PluginConfigurationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginConfigurationInstance": + """ + Fetch the PluginConfigurationInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginConfigurationInstance + """ + return self._proxy.fetch( + flex_metadata=flex_metadata, + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginConfigurationInstance": + """ + Asynchronous coroutine to fetch the PluginConfigurationInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginConfigurationInstance + """ + return await self._proxy.fetch_async( + flex_metadata=flex_metadata, + ) + + @property + def plugins(self) -> ConfiguredPluginList: + """ + Access the plugins + """ + return self._proxy.plugins + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginConfigurationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PluginConfigurationContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Plugin Configuration resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/PluginService/Configurations/{sid}".format(**self._solution) + + self._plugins: Optional[ConfiguredPluginList] = None + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginConfigurationInstance: + """ + Fetch the PluginConfigurationInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginConfigurationInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PluginConfigurationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginConfigurationInstance: + """ + Asynchronous coroutine to fetch the PluginConfigurationInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginConfigurationInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PluginConfigurationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def plugins(self) -> ConfiguredPluginList: + """ + Access the plugins + """ + if self._plugins is None: + self._plugins = ConfiguredPluginList( + self._version, + self._solution["sid"], + ) + return self._plugins + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginConfigurationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PluginConfigurationInstance: + """ + Build an instance of PluginConfigurationInstance + + :param payload: Payload response from the API + """ + return PluginConfigurationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PluginConfigurationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginConfigurationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/PluginService/Configurations" + + def create( + self, + name: str, + flex_metadata: Union[str, object] = values.unset, + plugins: Union[List[object], object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginConfigurationInstance: + """ + Create the PluginConfigurationInstance + + :param name: The Flex Plugin Configuration's name. + :param flex_metadata: The Flex-Metadata HTTP request header + :param plugins: A list of objects that describe the plugin versions included in the configuration. Each object contains the sid of the plugin version. + :param description: The Flex Plugin Configuration's description. + + :returns: The created PluginConfigurationInstance + """ + + data = values.of( + { + "Name": name, + "Plugins": serialize.map(plugins, lambda e: serialize.object(e)), + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginConfigurationInstance(self._version, payload) + + async def create_async( + self, + name: str, + flex_metadata: Union[str, object] = values.unset, + plugins: Union[List[object], object] = values.unset, + description: Union[str, object] = values.unset, + ) -> PluginConfigurationInstance: + """ + Asynchronously create the PluginConfigurationInstance + + :param name: The Flex Plugin Configuration's name. + :param flex_metadata: The Flex-Metadata HTTP request header + :param plugins: A list of objects that describe the plugin versions included in the configuration. Each object contains the sid of the plugin version. + :param description: The Flex Plugin Configuration's description. + + :returns: The created PluginConfigurationInstance + """ + + data = values.of( + { + "Name": name, + "Plugins": serialize.map(plugins, lambda e: serialize.object(e)), + "Description": description, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginConfigurationInstance(self._version, payload) + + def stream( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PluginConfigurationInstance]: + """ + Streams PluginConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(flex_metadata=flex_metadata, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PluginConfigurationInstance]: + """ + Asynchronously streams PluginConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + flex_metadata=flex_metadata, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginConfigurationInstance]: + """ + Lists PluginConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginConfigurationInstance]: + """ + Asynchronously lists PluginConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginConfigurationPage: + """ + Retrieve a single page of PluginConfigurationInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginConfigurationInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PluginConfigurationPage(self._version, response) + + async def page_async( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginConfigurationPage: + """ + Asynchronously retrieve a single page of PluginConfigurationInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginConfigurationInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PluginConfigurationPage(self._version, response) + + def get_page(self, target_url: str) -> PluginConfigurationPage: + """ + Retrieve a specific page of PluginConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginConfigurationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PluginConfigurationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> PluginConfigurationPage: + """ + Asynchronously retrieve a specific page of PluginConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginConfigurationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PluginConfigurationPage(self._version, response) + + def get(self, sid: str) -> PluginConfigurationContext: + """ + Constructs a PluginConfigurationContext + + :param sid: The SID of the Flex Plugin Configuration resource to fetch. + """ + return PluginConfigurationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PluginConfigurationContext: + """ + Constructs a PluginConfigurationContext + + :param sid: The SID of the Flex Plugin Configuration resource to fetch. + """ + return PluginConfigurationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a8689d0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/configured_plugin.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/configured_plugin.cpython-310.pyc new file mode 100644 index 0000000..c2f6fdb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/__pycache__/configured_plugin.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/configured_plugin.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/configured_plugin.py new file mode 100644 index 0000000..6e54012 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration/configured_plugin.py @@ -0,0 +1,494 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ConfiguredPluginInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the Flex Plugin resource is installed for. + :ivar configuration_sid: The SID of the Flex Plugin Configuration that this Flex Plugin belongs to. + :ivar plugin_sid: The SID of the Flex Plugin. + :ivar plugin_version_sid: The SID of the Flex Plugin Version. + :ivar phase: The phase this Flex Plugin would initialize at runtime. + :ivar plugin_url: The URL of where the Flex Plugin Version JavaScript bundle is hosted on. + :ivar unique_name: The name that uniquely identifies this Flex Plugin resource. + :ivar friendly_name: The friendly name of this Flex Plugin resource. + :ivar description: A descriptive string that you create to describe the plugin resource. It can be up to 500 characters long + :ivar plugin_archived: Whether the Flex Plugin is archived. The default value is false. + :ivar version: The latest version of this Flex Plugin Version. + :ivar changelog: A changelog that describes the changes this Flex Plugin Version brings. + :ivar plugin_version_archived: Whether the Flex Plugin Version is archived. The default value is false. + :ivar private: Whether to validate the request is authorized to access the Flex Plugin Version. + :ivar date_created: The date and time in GMT when the Flex Plugin was installed specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + configuration_sid: str, + plugin_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.configuration_sid: Optional[str] = payload.get("configuration_sid") + self.plugin_sid: Optional[str] = payload.get("plugin_sid") + self.plugin_version_sid: Optional[str] = payload.get("plugin_version_sid") + self.phase: Optional[int] = deserialize.integer(payload.get("phase")) + self.plugin_url: Optional[str] = payload.get("plugin_url") + self.unique_name: Optional[str] = payload.get("unique_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.plugin_archived: Optional[bool] = payload.get("plugin_archived") + self.version: Optional[str] = payload.get("version") + self.changelog: Optional[str] = payload.get("changelog") + self.plugin_version_archived: Optional[bool] = payload.get( + "plugin_version_archived" + ) + self.private: Optional[bool] = payload.get("private") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "configuration_sid": configuration_sid, + "plugin_sid": plugin_sid or self.plugin_sid, + } + self._context: Optional[ConfiguredPluginContext] = None + + @property + def _proxy(self) -> "ConfiguredPluginContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConfiguredPluginContext for this ConfiguredPluginInstance + """ + if self._context is None: + self._context = ConfiguredPluginContext( + self._version, + configuration_sid=self._solution["configuration_sid"], + plugin_sid=self._solution["plugin_sid"], + ) + return self._context + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> "ConfiguredPluginInstance": + """ + Fetch the ConfiguredPluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched ConfiguredPluginInstance + """ + return self._proxy.fetch( + flex_metadata=flex_metadata, + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "ConfiguredPluginInstance": + """ + Asynchronous coroutine to fetch the ConfiguredPluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched ConfiguredPluginInstance + """ + return await self._proxy.fetch_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConfiguredPluginContext(InstanceContext): + + def __init__(self, version: Version, configuration_sid: str, plugin_sid: str): + """ + Initialize the ConfiguredPluginContext + + :param version: Version that contains the resource + :param configuration_sid: The SID of the Flex Plugin Configuration the resource to belongs to. + :param plugin_sid: The unique string that we created to identify the Flex Plugin resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "configuration_sid": configuration_sid, + "plugin_sid": plugin_sid, + } + self._uri = "/PluginService/Configurations/{configuration_sid}/Plugins/{plugin_sid}".format( + **self._solution + ) + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> ConfiguredPluginInstance: + """ + Fetch the ConfiguredPluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched ConfiguredPluginInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return ConfiguredPluginInstance( + self._version, + payload, + configuration_sid=self._solution["configuration_sid"], + plugin_sid=self._solution["plugin_sid"], + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> ConfiguredPluginInstance: + """ + Asynchronous coroutine to fetch the ConfiguredPluginInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched ConfiguredPluginInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return ConfiguredPluginInstance( + self._version, + payload, + configuration_sid=self._solution["configuration_sid"], + plugin_sid=self._solution["plugin_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConfiguredPluginPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConfiguredPluginInstance: + """ + Build an instance of ConfiguredPluginInstance + + :param payload: Payload response from the API + """ + return ConfiguredPluginInstance( + self._version, + payload, + configuration_sid=self._solution["configuration_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConfiguredPluginList(ListResource): + + def __init__(self, version: Version, configuration_sid: str): + """ + Initialize the ConfiguredPluginList + + :param version: Version that contains the resource + :param configuration_sid: The SID of the Flex Plugin Configuration the resource to belongs to. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "configuration_sid": configuration_sid, + } + self._uri = "/PluginService/Configurations/{configuration_sid}/Plugins".format( + **self._solution + ) + + def stream( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConfiguredPluginInstance]: + """ + Streams ConfiguredPluginInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(flex_metadata=flex_metadata, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConfiguredPluginInstance]: + """ + Asynchronously streams ConfiguredPluginInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + flex_metadata=flex_metadata, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConfiguredPluginInstance]: + """ + Lists ConfiguredPluginInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConfiguredPluginInstance]: + """ + Asynchronously lists ConfiguredPluginInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConfiguredPluginPage: + """ + Retrieve a single page of ConfiguredPluginInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConfiguredPluginInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConfiguredPluginPage(self._version, response, self._solution) + + async def page_async( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConfiguredPluginPage: + """ + Asynchronously retrieve a single page of ConfiguredPluginInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConfiguredPluginInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConfiguredPluginPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConfiguredPluginPage: + """ + Retrieve a specific page of ConfiguredPluginInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConfiguredPluginInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConfiguredPluginPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConfiguredPluginPage: + """ + Asynchronously retrieve a specific page of ConfiguredPluginInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConfiguredPluginInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConfiguredPluginPage(self._version, response, self._solution) + + def get(self, plugin_sid: str) -> ConfiguredPluginContext: + """ + Constructs a ConfiguredPluginContext + + :param plugin_sid: The unique string that we created to identify the Flex Plugin resource. + """ + return ConfiguredPluginContext( + self._version, + configuration_sid=self._solution["configuration_sid"], + plugin_sid=plugin_sid, + ) + + def __call__(self, plugin_sid: str) -> ConfiguredPluginContext: + """ + Constructs a ConfiguredPluginContext + + :param plugin_sid: The unique string that we created to identify the Flex Plugin resource. + """ + return ConfiguredPluginContext( + self._version, + configuration_sid=self._solution["configuration_sid"], + plugin_sid=plugin_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration_archive.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration_archive.py new file mode 100644 index 0000000..97d2f81 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_configuration_archive.py @@ -0,0 +1,224 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PluginConfigurationArchiveInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin Configuration resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin Configuration resource and owns this resource. + :ivar name: The name of this Flex Plugin Configuration. + :ivar description: The description of the Flex Plugin Configuration resource. + :ivar archived: Whether the Flex Plugin Configuration is archived. The default value is false. + :ivar date_created: The date and time in GMT when the Flex Plugin Configuration was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin Configuration resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.name: Optional[str] = payload.get("name") + self.description: Optional[str] = payload.get("description") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PluginConfigurationArchiveContext] = None + + @property + def _proxy(self) -> "PluginConfigurationArchiveContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginConfigurationArchiveContext for this PluginConfigurationArchiveInstance + """ + if self._context is None: + self._context = PluginConfigurationArchiveContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginConfigurationArchiveInstance": + """ + Update the PluginConfigurationArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginConfigurationArchiveInstance + """ + return self._proxy.update( + flex_metadata=flex_metadata, + ) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginConfigurationArchiveInstance": + """ + Asynchronous coroutine to update the PluginConfigurationArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginConfigurationArchiveInstance + """ + return await self._proxy.update_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class PluginConfigurationArchiveContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PluginConfigurationArchiveContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Plugin Configuration resource to archive. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/PluginService/Configurations/{sid}/Archive".format( + **self._solution + ) + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginConfigurationArchiveInstance: + """ + Update the PluginConfigurationArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginConfigurationArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginConfigurationArchiveInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginConfigurationArchiveInstance: + """ + Asynchronous coroutine to update the PluginConfigurationArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginConfigurationArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginConfigurationArchiveInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class PluginConfigurationArchiveList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginConfigurationArchiveList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sid: str) -> PluginConfigurationArchiveContext: + """ + Constructs a PluginConfigurationArchiveContext + + :param sid: The SID of the Flex Plugin Configuration resource to archive. + """ + return PluginConfigurationArchiveContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PluginConfigurationArchiveContext: + """ + Constructs a PluginConfigurationArchiveContext + + :param sid: The SID of the Flex Plugin Configuration resource to archive. + """ + return PluginConfigurationArchiveContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_release.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_release.py new file mode 100644 index 0000000..f2e8efd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_release.py @@ -0,0 +1,497 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PluginReleaseInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Plugin Release resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Plugin Release resource and owns this resource. + :ivar configuration_sid: The SID of the Plugin Configuration resource to release. + :ivar date_created: The date and time in GMT when the Flex Plugin Release was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Plugin Release resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.configuration_sid: Optional[str] = payload.get("configuration_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PluginReleaseContext] = None + + @property + def _proxy(self) -> "PluginReleaseContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginReleaseContext for this PluginReleaseInstance + """ + if self._context is None: + self._context = PluginReleaseContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginReleaseInstance": + """ + Fetch the PluginReleaseInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginReleaseInstance + """ + return self._proxy.fetch( + flex_metadata=flex_metadata, + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginReleaseInstance": + """ + Asynchronous coroutine to fetch the PluginReleaseInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginReleaseInstance + """ + return await self._proxy.fetch_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginReleaseContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PluginReleaseContext + + :param version: Version that contains the resource + :param sid: The SID of the Flex Plugin Release resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/PluginService/Releases/{sid}".format(**self._solution) + + def fetch( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginReleaseInstance: + """ + Fetch the PluginReleaseInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginReleaseInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PluginReleaseInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginReleaseInstance: + """ + Asynchronous coroutine to fetch the PluginReleaseInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The fetched PluginReleaseInstance + """ + + data = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PluginReleaseInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginReleasePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PluginReleaseInstance: + """ + Build an instance of PluginReleaseInstance + + :param payload: Payload response from the API + """ + return PluginReleaseInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PluginReleaseList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginReleaseList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/PluginService/Releases" + + def create( + self, configuration_id: str, flex_metadata: Union[str, object] = values.unset + ) -> PluginReleaseInstance: + """ + Create the PluginReleaseInstance + + :param configuration_id: The SID or the Version of the Flex Plugin Configuration to release. + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The created PluginReleaseInstance + """ + + data = values.of( + { + "ConfigurationId": configuration_id, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginReleaseInstance(self._version, payload) + + async def create_async( + self, configuration_id: str, flex_metadata: Union[str, object] = values.unset + ) -> PluginReleaseInstance: + """ + Asynchronously create the PluginReleaseInstance + + :param configuration_id: The SID or the Version of the Flex Plugin Configuration to release. + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The created PluginReleaseInstance + """ + + data = values.of( + { + "ConfigurationId": configuration_id, + } + ) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginReleaseInstance(self._version, payload) + + def stream( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PluginReleaseInstance]: + """ + Streams PluginReleaseInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(flex_metadata=flex_metadata, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PluginReleaseInstance]: + """ + Asynchronously streams PluginReleaseInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + flex_metadata=flex_metadata, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginReleaseInstance]: + """ + Lists PluginReleaseInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + flex_metadata: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PluginReleaseInstance]: + """ + Asynchronously lists PluginReleaseInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str flex_metadata: The Flex-Metadata HTTP request header + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + flex_metadata=flex_metadata, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginReleasePage: + """ + Retrieve a single page of PluginReleaseInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginReleaseInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PluginReleasePage(self._version, response) + + async def page_async( + self, + flex_metadata: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PluginReleasePage: + """ + Asynchronously retrieve a single page of PluginReleaseInstance records from the API. + Request is executed immediately + + :param flex_metadata: The Flex-Metadata HTTP request header + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PluginReleaseInstance + """ + data = values.of( + { + "Flex-Metadata": flex_metadata, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PluginReleasePage(self._version, response) + + def get_page(self, target_url: str) -> PluginReleasePage: + """ + Retrieve a specific page of PluginReleaseInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginReleaseInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PluginReleasePage(self._version, response) + + async def get_page_async(self, target_url: str) -> PluginReleasePage: + """ + Asynchronously retrieve a specific page of PluginReleaseInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PluginReleaseInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PluginReleasePage(self._version, response) + + def get(self, sid: str) -> PluginReleaseContext: + """ + Constructs a PluginReleaseContext + + :param sid: The SID of the Flex Plugin Release resource to fetch. + """ + return PluginReleaseContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PluginReleaseContext: + """ + Constructs a PluginReleaseContext + + :param sid: The SID of the Flex Plugin Release resource to fetch. + """ + return PluginReleaseContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_version_archive.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_version_archive.py new file mode 100644 index 0000000..ae8d2f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/plugin_version_archive.py @@ -0,0 +1,246 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PluginVersionArchiveInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Flex Plugin Version resource. + :ivar plugin_sid: The SID of the Flex Plugin resource this Flex Plugin Version belongs to. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flex Plugin Version resource and owns this resource. + :ivar version: The unique version of this Flex Plugin Version. + :ivar plugin_url: The URL of where the Flex Plugin Version JavaScript bundle is hosted on. + :ivar changelog: A changelog that describes the changes this Flex Plugin Version brings. + :ivar private: Whether to inject credentials while accessing this Plugin Version. The default value is false. + :ivar archived: Whether the Flex Plugin Version is archived. The default value is false. + :ivar date_created: The date and time in GMT when the Flex Plugin Version was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Flex Plugin Version resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + plugin_sid: Optional[str] = None, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.plugin_sid: Optional[str] = payload.get("plugin_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.version: Optional[str] = payload.get("version") + self.plugin_url: Optional[str] = payload.get("plugin_url") + self.changelog: Optional[str] = payload.get("changelog") + self.private: Optional[bool] = payload.get("private") + self.archived: Optional[bool] = payload.get("archived") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "plugin_sid": plugin_sid or self.plugin_sid, + "sid": sid or self.sid, + } + self._context: Optional[PluginVersionArchiveContext] = None + + @property + def _proxy(self) -> "PluginVersionArchiveContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PluginVersionArchiveContext for this PluginVersionArchiveInstance + """ + if self._context is None: + self._context = PluginVersionArchiveContext( + self._version, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginVersionArchiveInstance": + """ + Update the PluginVersionArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginVersionArchiveInstance + """ + return self._proxy.update( + flex_metadata=flex_metadata, + ) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> "PluginVersionArchiveInstance": + """ + Asynchronous coroutine to update the PluginVersionArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginVersionArchiveInstance + """ + return await self._proxy.update_async( + flex_metadata=flex_metadata, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginVersionArchiveContext(InstanceContext): + + def __init__(self, version: Version, plugin_sid: str, sid: str): + """ + Initialize the PluginVersionArchiveContext + + :param version: Version that contains the resource + :param plugin_sid: The SID of the Flex Plugin the resource to belongs to. + :param sid: The SID of the Flex Plugin Version resource to archive. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "plugin_sid": plugin_sid, + "sid": sid, + } + self._uri = "/PluginService/Plugins/{plugin_sid}/Versions/{sid}/Archive".format( + **self._solution + ) + + def update( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginVersionArchiveInstance: + """ + Update the PluginVersionArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginVersionArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginVersionArchiveInstance( + self._version, + payload, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, flex_metadata: Union[str, object] = values.unset + ) -> PluginVersionArchiveInstance: + """ + Asynchronous coroutine to update the PluginVersionArchiveInstance + + :param flex_metadata: The Flex-Metadata HTTP request header + + :returns: The updated PluginVersionArchiveInstance + """ + data = values.of({}) + headers = values.of( + { + "Flex-Metadata": flex_metadata, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PluginVersionArchiveInstance( + self._version, + payload, + plugin_sid=self._solution["plugin_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PluginVersionArchiveList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PluginVersionArchiveList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, plugin_sid: str, sid: str) -> PluginVersionArchiveContext: + """ + Constructs a PluginVersionArchiveContext + + :param plugin_sid: The SID of the Flex Plugin the resource to belongs to. + :param sid: The SID of the Flex Plugin Version resource to archive. + """ + return PluginVersionArchiveContext( + self._version, plugin_sid=plugin_sid, sid=sid + ) + + def __call__(self, plugin_sid: str, sid: str) -> PluginVersionArchiveContext: + """ + Constructs a PluginVersionArchiveContext + + :param plugin_sid: The SID of the Flex Plugin the resource to belongs to. + :param sid: The SID of the Flex Plugin Version resource to archive. + """ + return PluginVersionArchiveContext( + self._version, plugin_sid=plugin_sid, sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/provisioning_status.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/provisioning_status.py new file mode 100644 index 0000000..9cc9144 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/provisioning_status.py @@ -0,0 +1,176 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ProvisioningStatusInstance(InstanceResource): + + class Status(object): + ACTIVE = "active" + IN_PROGRESS = "in-progress" + NOT_CONFIGURED = "not-configured" + FAILED = "failed" + + """ + :ivar status: + :ivar url: The absolute URL of the resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.status: Optional["ProvisioningStatusInstance.Status"] = payload.get( + "status" + ) + self.url: Optional[str] = payload.get("url") + + self._context: Optional[ProvisioningStatusContext] = None + + @property + def _proxy(self) -> "ProvisioningStatusContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ProvisioningStatusContext for this ProvisioningStatusInstance + """ + if self._context is None: + self._context = ProvisioningStatusContext( + self._version, + ) + return self._context + + def fetch(self) -> "ProvisioningStatusInstance": + """ + Fetch the ProvisioningStatusInstance + + + :returns: The fetched ProvisioningStatusInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ProvisioningStatusInstance": + """ + Asynchronous coroutine to fetch the ProvisioningStatusInstance + + + :returns: The fetched ProvisioningStatusInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ProvisioningStatusContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the ProvisioningStatusContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/account/provision/status" + + def fetch(self) -> ProvisioningStatusInstance: + """ + Fetch the ProvisioningStatusInstance + + + :returns: The fetched ProvisioningStatusInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ProvisioningStatusInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> ProvisioningStatusInstance: + """ + Asynchronous coroutine to fetch the ProvisioningStatusInstance + + + :returns: The fetched ProvisioningStatusInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ProvisioningStatusInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ProvisioningStatusList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ProvisioningStatusList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> ProvisioningStatusContext: + """ + Constructs a ProvisioningStatusContext + + """ + return ProvisioningStatusContext(self._version) + + def __call__(self) -> ProvisioningStatusContext: + """ + Constructs a ProvisioningStatusContext + + """ + return ProvisioningStatusContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/web_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/web_channel.py new file mode 100644 index 0000000..ddf9c8f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v1/web_channel.py @@ -0,0 +1,621 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebChannelInstance(InstanceResource): + + class ChatStatus(object): + INACTIVE = "inactive" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the WebChannel resource and owns this Workflow. + :ivar flex_flow_sid: The SID of the Flex Flow. + :ivar sid: The unique string that we created to identify the WebChannel resource. + :ivar url: The absolute URL of the WebChannel resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.flex_flow_sid: Optional[str] = payload.get("flex_flow_sid") + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[WebChannelContext] = None + + @property + def _proxy(self) -> "WebChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebChannelContext for this WebChannelInstance + """ + if self._context is None: + self._context = WebChannelContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebChannelInstance": + """ + Fetch the WebChannelInstance + + + :returns: The fetched WebChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebChannelInstance": + """ + Asynchronous coroutine to fetch the WebChannelInstance + + + :returns: The fetched WebChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + chat_status: Union["WebChannelInstance.ChatStatus", object] = values.unset, + post_engagement_data: Union[str, object] = values.unset, + ) -> "WebChannelInstance": + """ + Update the WebChannelInstance + + :param chat_status: + :param post_engagement_data: The post-engagement data. + + :returns: The updated WebChannelInstance + """ + return self._proxy.update( + chat_status=chat_status, + post_engagement_data=post_engagement_data, + ) + + async def update_async( + self, + chat_status: Union["WebChannelInstance.ChatStatus", object] = values.unset, + post_engagement_data: Union[str, object] = values.unset, + ) -> "WebChannelInstance": + """ + Asynchronous coroutine to update the WebChannelInstance + + :param chat_status: + :param post_engagement_data: The post-engagement data. + + :returns: The updated WebChannelInstance + """ + return await self._proxy.update_async( + chat_status=chat_status, + post_engagement_data=post_engagement_data, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebChannelContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the WebChannelContext + + :param version: Version that contains the resource + :param sid: The SID of the WebChannel resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/WebChannels/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the WebChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebChannelInstance: + """ + Fetch the WebChannelInstance + + + :returns: The fetched WebChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebChannelInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebChannelInstance: + """ + Asynchronous coroutine to fetch the WebChannelInstance + + + :returns: The fetched WebChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebChannelInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + chat_status: Union["WebChannelInstance.ChatStatus", object] = values.unset, + post_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelInstance: + """ + Update the WebChannelInstance + + :param chat_status: + :param post_engagement_data: The post-engagement data. + + :returns: The updated WebChannelInstance + """ + data = values.of( + { + "ChatStatus": chat_status, + "PostEngagementData": post_engagement_data, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebChannelInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + chat_status: Union["WebChannelInstance.ChatStatus", object] = values.unset, + post_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelInstance: + """ + Asynchronous coroutine to update the WebChannelInstance + + :param chat_status: + :param post_engagement_data: The post-engagement data. + + :returns: The updated WebChannelInstance + """ + data = values.of( + { + "ChatStatus": chat_status, + "PostEngagementData": post_engagement_data, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebChannelInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebChannelInstance: + """ + Build an instance of WebChannelInstance + + :param payload: Payload response from the API + """ + return WebChannelInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebChannelList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the WebChannelList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/WebChannels" + + def create( + self, + flex_flow_sid: str, + identity: str, + customer_friendly_name: str, + chat_friendly_name: str, + chat_unique_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelInstance: + """ + Create the WebChannelInstance + + :param flex_flow_sid: The SID of the Flex Flow. + :param identity: The chat identity. + :param customer_friendly_name: The chat participant's friendly name. + :param chat_friendly_name: The chat channel's friendly name. + :param chat_unique_name: The chat channel's unique name. + :param pre_engagement_data: The pre-engagement data. + + :returns: The created WebChannelInstance + """ + + data = values.of( + { + "FlexFlowSid": flex_flow_sid, + "Identity": identity, + "CustomerFriendlyName": customer_friendly_name, + "ChatFriendlyName": chat_friendly_name, + "ChatUniqueName": chat_unique_name, + "PreEngagementData": pre_engagement_data, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebChannelInstance(self._version, payload) + + async def create_async( + self, + flex_flow_sid: str, + identity: str, + customer_friendly_name: str, + chat_friendly_name: str, + chat_unique_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelInstance: + """ + Asynchronously create the WebChannelInstance + + :param flex_flow_sid: The SID of the Flex Flow. + :param identity: The chat identity. + :param customer_friendly_name: The chat participant's friendly name. + :param chat_friendly_name: The chat channel's friendly name. + :param chat_unique_name: The chat channel's unique name. + :param pre_engagement_data: The pre-engagement data. + + :returns: The created WebChannelInstance + """ + + data = values.of( + { + "FlexFlowSid": flex_flow_sid, + "Identity": identity, + "CustomerFriendlyName": customer_friendly_name, + "ChatFriendlyName": chat_friendly_name, + "ChatUniqueName": chat_unique_name, + "PreEngagementData": pre_engagement_data, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebChannelInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebChannelInstance]: + """ + Streams WebChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebChannelInstance]: + """ + Asynchronously streams WebChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebChannelInstance]: + """ + Lists WebChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebChannelInstance]: + """ + Asynchronously lists WebChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebChannelPage: + """ + Retrieve a single page of WebChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebChannelPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebChannelPage: + """ + Asynchronously retrieve a single page of WebChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebChannelPage(self._version, response) + + def get_page(self, target_url: str) -> WebChannelPage: + """ + Retrieve a specific page of WebChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebChannelPage(self._version, response) + + async def get_page_async(self, target_url: str) -> WebChannelPage: + """ + Asynchronously retrieve a specific page of WebChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebChannelPage(self._version, response) + + def get(self, sid: str) -> WebChannelContext: + """ + Constructs a WebChannelContext + + :param sid: The SID of the WebChannel resource to update. + """ + return WebChannelContext(self._version, sid=sid) + + def __call__(self, sid: str) -> WebChannelContext: + """ + Constructs a WebChannelContext + + :param sid: The SID of the WebChannel resource to update. + """ + return WebChannelContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__init__.py new file mode 100644 index 0000000..e05ffcd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.flex_api.v2.flex_user import FlexUserList +from twilio.rest.flex_api.v2.web_channels import WebChannelsList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of FlexApi + + :param domain: The Twilio.flex_api domain + """ + super().__init__(domain, "v2") + self._flex_user: Optional[FlexUserList] = None + self._web_channels: Optional[WebChannelsList] = None + + @property + def flex_user(self) -> FlexUserList: + if self._flex_user is None: + self._flex_user = FlexUserList(self) + return self._flex_user + + @property + def web_channels(self) -> WebChannelsList: + if self._web_channels is None: + self._web_channels = WebChannelsList(self) + return self._web_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e13539b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/flex_user.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/flex_user.cpython-310.pyc new file mode 100644 index 0000000..80528f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/flex_user.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/web_channels.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/web_channels.cpython-310.pyc new file mode 100644 index 0000000..fe1ede8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/__pycache__/web_channels.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/flex_user.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/flex_user.py new file mode 100644 index 0000000..1acc75a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/flex_user.py @@ -0,0 +1,234 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FlexUserInstance(InstanceResource): + """ + :ivar account_sid: The unique SID of the account that created the resource. + :ivar instance_sid: The unique ID created by Twilio to identify a Flex instance. + :ivar user_sid: The unique SID identifier of the Twilio Unified User. + :ivar flex_user_sid: The unique SID identifier of the Flex User. + :ivar worker_sid: The unique SID identifier of the worker. + :ivar workspace_sid: The unique SID identifier of the workspace. + :ivar flex_team_sid: The unique SID identifier of the Flex Team. + :ivar first_name: First name of the User. + :ivar last_name: Last name of the User. + :ivar username: Username of the User. + :ivar email: Email of the User. + :ivar friendly_name: Friendly name of the User. + :ivar created_date: The date that this user was created, given in ISO 8601 format. + :ivar updated_date: The date that this user was updated, given in ISO 8601 format. + :ivar version: The current version of the user. + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + instance_sid: Optional[str] = None, + flex_user_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.instance_sid: Optional[str] = payload.get("instance_sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.flex_user_sid: Optional[str] = payload.get("flex_user_sid") + self.worker_sid: Optional[str] = payload.get("worker_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.flex_team_sid: Optional[str] = payload.get("flex_team_sid") + self.first_name: Optional[str] = payload.get("first_name") + self.last_name: Optional[str] = payload.get("last_name") + self.username: Optional[str] = payload.get("username") + self.email: Optional[str] = payload.get("email") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.created_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("created_date") + ) + self.updated_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("updated_date") + ) + self.version: Optional[int] = deserialize.integer(payload.get("version")) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "instance_sid": instance_sid or self.instance_sid, + "flex_user_sid": flex_user_sid or self.flex_user_sid, + } + self._context: Optional[FlexUserContext] = None + + @property + def _proxy(self) -> "FlexUserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlexUserContext for this FlexUserInstance + """ + if self._context is None: + self._context = FlexUserContext( + self._version, + instance_sid=self._solution["instance_sid"], + flex_user_sid=self._solution["flex_user_sid"], + ) + return self._context + + def fetch(self) -> "FlexUserInstance": + """ + Fetch the FlexUserInstance + + + :returns: The fetched FlexUserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlexUserInstance": + """ + Asynchronous coroutine to fetch the FlexUserInstance + + + :returns: The fetched FlexUserInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlexUserContext(InstanceContext): + + def __init__(self, version: Version, instance_sid: str, flex_user_sid: str): + """ + Initialize the FlexUserContext + + :param version: Version that contains the resource + :param instance_sid: The unique ID created by Twilio to identify a Flex instance. + :param flex_user_sid: The unique id for the flex user to be retrieved. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "instance_sid": instance_sid, + "flex_user_sid": flex_user_sid, + } + self._uri = "/Instances/{instance_sid}/Users/{flex_user_sid}".format( + **self._solution + ) + + def fetch(self) -> FlexUserInstance: + """ + Fetch the FlexUserInstance + + + :returns: The fetched FlexUserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlexUserInstance( + self._version, + payload, + instance_sid=self._solution["instance_sid"], + flex_user_sid=self._solution["flex_user_sid"], + ) + + async def fetch_async(self) -> FlexUserInstance: + """ + Asynchronous coroutine to fetch the FlexUserInstance + + + :returns: The fetched FlexUserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlexUserInstance( + self._version, + payload, + instance_sid=self._solution["instance_sid"], + flex_user_sid=self._solution["flex_user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlexUserList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FlexUserList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, instance_sid: str, flex_user_sid: str) -> FlexUserContext: + """ + Constructs a FlexUserContext + + :param instance_sid: The unique ID created by Twilio to identify a Flex instance. + :param flex_user_sid: The unique id for the flex user to be retrieved. + """ + return FlexUserContext( + self._version, instance_sid=instance_sid, flex_user_sid=flex_user_sid + ) + + def __call__(self, instance_sid: str, flex_user_sid: str) -> FlexUserContext: + """ + Constructs a FlexUserContext + + :param instance_sid: The unique ID created by Twilio to identify a Flex instance. + :param flex_user_sid: The unique id for the flex user to be retrieved. + """ + return FlexUserContext( + self._version, instance_sid=instance_sid, flex_user_sid=flex_user_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/web_channels.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/web_channels.py new file mode 100644 index 0000000..a4b7b0c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/flex_api/v2/web_channels.py @@ -0,0 +1,144 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Flex + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WebChannelsInstance(InstanceResource): + """ + :ivar conversation_sid: The unique string representing the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource) created. + :ivar identity: The unique string representing the User created and should be authorized to participate in the Conversation. For more details, see [User Identity & Access Tokens](https://www.twilio.com/docs/conversations/identity). + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.conversation_sid: Optional[str] = payload.get("conversation_sid") + self.identity: Optional[str] = payload.get("identity") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class WebChannelsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the WebChannelsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/WebChats" + + def create( + self, + address_sid: str, + ui_version: Union[str, object] = values.unset, + chat_friendly_name: Union[str, object] = values.unset, + customer_friendly_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelsInstance: + """ + Create the WebChannelsInstance + + :param address_sid: The SID of the Conversations Address. See [Address Configuration Resource](https://www.twilio.com/docs/conversations/api/address-configuration-resource) for configuration details. When a conversation is created on the Flex backend, the callback URL will be set to the corresponding Studio Flow SID or webhook URL in your address configuration. + :param ui_version: The Ui-Version HTTP request header + :param chat_friendly_name: The Conversation's friendly name. See the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource) for an example. + :param customer_friendly_name: The Conversation participant's friendly name. See the [Conversation Participant Resource](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) for an example. + :param pre_engagement_data: The pre-engagement data. + + :returns: The created WebChannelsInstance + """ + + data = values.of( + { + "AddressSid": address_sid, + "ChatFriendlyName": chat_friendly_name, + "CustomerFriendlyName": customer_friendly_name, + "PreEngagementData": pre_engagement_data, + } + ) + headers = values.of( + { + "Ui-Version": ui_version, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return WebChannelsInstance(self._version, payload) + + async def create_async( + self, + address_sid: str, + ui_version: Union[str, object] = values.unset, + chat_friendly_name: Union[str, object] = values.unset, + customer_friendly_name: Union[str, object] = values.unset, + pre_engagement_data: Union[str, object] = values.unset, + ) -> WebChannelsInstance: + """ + Asynchronously create the WebChannelsInstance + + :param address_sid: The SID of the Conversations Address. See [Address Configuration Resource](https://www.twilio.com/docs/conversations/api/address-configuration-resource) for configuration details. When a conversation is created on the Flex backend, the callback URL will be set to the corresponding Studio Flow SID or webhook URL in your address configuration. + :param ui_version: The Ui-Version HTTP request header + :param chat_friendly_name: The Conversation's friendly name. See the [Conversation resource](https://www.twilio.com/docs/conversations/api/conversation-resource) for an example. + :param customer_friendly_name: The Conversation participant's friendly name. See the [Conversation Participant Resource](https://www.twilio.com/docs/conversations/api/conversation-participant-resource) for an example. + :param pre_engagement_data: The pre-engagement data. + + :returns: The created WebChannelsInstance + """ + + data = values.of( + { + "AddressSid": address_sid, + "ChatFriendlyName": chat_friendly_name, + "CustomerFriendlyName": customer_friendly_name, + "PreEngagementData": pre_engagement_data, + } + ) + headers = values.of( + { + "Ui-Version": ui_version, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return WebChannelsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/FrontlineApiBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/FrontlineApiBase.py new file mode 100644 index 0000000..d9dadc1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/FrontlineApiBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.frontline_api.v1 import V1 + + +class FrontlineApiBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the FrontlineApi Domain + + :returns: Domain for FrontlineApi + """ + super().__init__(twilio, "https://frontline-api.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of FrontlineApi + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__init__.py new file mode 100644 index 0000000..300fafa --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.frontline_api.FrontlineApiBase import FrontlineApiBase +from twilio.rest.frontline_api.v1.user import UserList + + +class FrontlineApi(FrontlineApiBase): + @property + def users(self) -> UserList: + warn( + "users is deprecated. Use v1.users instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.users diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/FrontlineApiBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/FrontlineApiBase.cpython-310.pyc new file mode 100644 index 0000000..a25701f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/FrontlineApiBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8871554 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__init__.py new file mode 100644 index 0000000..610ed37 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Frontline + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.frontline_api.v1.user import UserList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of FrontlineApi + + :param domain: The Twilio.frontline_api domain + """ + super().__init__(domain, "v1") + self._users: Optional[UserList] = None + + @property + def users(self) -> UserList: + if self._users is None: + self._users = UserList(self) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5cf751a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/user.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/user.cpython-310.pyc new file mode 100644 index 0000000..d868db8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/__pycache__/user.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/user.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/user.py new file mode 100644 index 0000000..8c9dd93 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/frontline_api/v1/user.py @@ -0,0 +1,314 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Frontline + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UserInstance(InstanceResource): + + class StateType(object): + ACTIVE = "active" + DEACTIVATED = "deactivated" + + """ + :ivar sid: The unique string that we created to identify the User resource. + :ivar identity: The application-defined string that uniquely identifies the resource's User. This value is often a username or an email address, and is case-sensitive. + :ivar friendly_name: The string that you assigned to describe the User. + :ivar avatar: The avatar URL which will be shown in Frontline application. + :ivar state: + :ivar is_available: Whether the User is available for new conversations. Defaults to `false` for new users. + :ivar url: An absolute API resource URL for this user. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.identity: Optional[str] = payload.get("identity") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.avatar: Optional[str] = payload.get("avatar") + self.state: Optional["UserInstance.StateType"] = payload.get("state") + self.is_available: Optional[bool] = payload.get("is_available") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + avatar: Union[str, object] = values.unset, + state: Union["UserInstance.StateType", object] = values.unset, + is_available: Union[bool, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param friendly_name: The string that you assigned to describe the User. + :param avatar: The avatar URL which will be shown in Frontline application. + :param state: + :param is_available: Whether the User is available for new conversations. Set to `false` to prevent User from receiving new inbound conversations if you are using [Pool Routing](https://www.twilio.com/docs/frontline/handle-incoming-conversations#3-pool-routing). + + :returns: The updated UserInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + avatar=avatar, + state=state, + is_available=is_available, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + avatar: Union[str, object] = values.unset, + state: Union["UserInstance.StateType", object] = values.unset, + is_available: Union[bool, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param friendly_name: The string that you assigned to describe the User. + :param avatar: The avatar URL which will be shown in Frontline application. + :param state: + :param is_available: Whether the User is available for new conversations. Set to `false` to prevent User from receiving new inbound conversations if you are using [Pool Routing](https://www.twilio.com/docs/frontline/handle-incoming-conversations#3-pool-routing). + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + avatar=avatar, + state=state, + is_available=is_available, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Users/{sid}".format(**self._solution) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + avatar: Union[str, object] = values.unset, + state: Union["UserInstance.StateType", object] = values.unset, + is_available: Union[bool, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param friendly_name: The string that you assigned to describe the User. + :param avatar: The avatar URL which will be shown in Frontline application. + :param state: + :param is_available: Whether the User is available for new conversations. Set to `false` to prevent User from receiving new inbound conversations if you are using [Pool Routing](https://www.twilio.com/docs/frontline/handle-incoming-conversations#3-pool-routing). + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Avatar": avatar, + "State": state, + "IsAvailable": serialize.boolean_to_string(is_available), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + avatar: Union[str, object] = values.unset, + state: Union["UserInstance.StateType", object] = values.unset, + is_available: Union[bool, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param friendly_name: The string that you assigned to describe the User. + :param avatar: The avatar URL which will be shown in Frontline application. + :param state: + :param is_available: Whether the User is available for new conversations. Set to `false` to prevent User from receiving new inbound conversations if you are using [Pool Routing](https://www.twilio.com/docs/frontline/handle-incoming-conversations#3-pool-routing). + + :returns: The updated UserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Avatar": avatar, + "State": state, + "IsAvailable": serialize.boolean_to_string(is_available), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the UserList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext(self._version, sid=sid) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: The SID of the User resource to update. This value can be either the `sid` or the `identity` of the User resource to update. + """ + return UserContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/InsightsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/InsightsBase.py new file mode 100644 index 0000000..458122c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/InsightsBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.insights.v1 import V1 + + +class InsightsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Insights Domain + + :returns: Domain for Insights + """ + super().__init__(twilio, "https://insights.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Insights + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__init__.py new file mode 100644 index 0000000..0f14577 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__init__.py @@ -0,0 +1,55 @@ +from warnings import warn + +from twilio.rest.insights.InsightsBase import InsightsBase +from twilio.rest.insights.v1.call import CallList +from twilio.rest.insights.v1.call_summaries import CallSummariesList +from twilio.rest.insights.v1.conference import ConferenceList +from twilio.rest.insights.v1.room import RoomList +from twilio.rest.insights.v1.setting import SettingList + + +class Insights(InsightsBase): + @property + def settings(self) -> SettingList: + warn( + "settings is deprecated. Use v1.settings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.settings + + @property + def calls(self) -> CallList: + warn( + "calls is deprecated. Use v1.calls instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.calls + + @property + def call_summaries(self) -> CallSummariesList: + warn( + "call_summaries is deprecated. Use v1.call_summaries instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.call_summaries + + @property + def conferences(self) -> ConferenceList: + warn( + "conferences is deprecated. Use v1.conferences instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.conferences + + @property + def rooms(self) -> RoomList: + warn( + "rooms is deprecated. Use v1.rooms instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.rooms diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/InsightsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/InsightsBase.cpython-310.pyc new file mode 100644 index 0000000..3984e0f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/InsightsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..aaeb6f5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__init__.py new file mode 100644 index 0000000..8f7d194 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__init__.py @@ -0,0 +1,75 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.insights.v1.call import CallList +from twilio.rest.insights.v1.call_summaries import CallSummariesList +from twilio.rest.insights.v1.conference import ConferenceList +from twilio.rest.insights.v1.room import RoomList +from twilio.rest.insights.v1.setting import SettingList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Insights + + :param domain: The Twilio.insights domain + """ + super().__init__(domain, "v1") + self._calls: Optional[CallList] = None + self._call_summaries: Optional[CallSummariesList] = None + self._conferences: Optional[ConferenceList] = None + self._rooms: Optional[RoomList] = None + self._settings: Optional[SettingList] = None + + @property + def calls(self) -> CallList: + if self._calls is None: + self._calls = CallList(self) + return self._calls + + @property + def call_summaries(self) -> CallSummariesList: + if self._call_summaries is None: + self._call_summaries = CallSummariesList(self) + return self._call_summaries + + @property + def conferences(self) -> ConferenceList: + if self._conferences is None: + self._conferences = ConferenceList(self) + return self._conferences + + @property + def rooms(self) -> RoomList: + if self._rooms is None: + self._rooms = RoomList(self) + return self._rooms + + @property + def settings(self) -> SettingList: + if self._settings is None: + self._settings = SettingList(self) + return self._settings + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..bc862ac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/call_summaries.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/call_summaries.cpython-310.pyc new file mode 100644 index 0000000..2b4a4b9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/call_summaries.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/setting.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/setting.cpython-310.pyc new file mode 100644 index 0000000..e0512de Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/__pycache__/setting.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__init__.py new file mode 100644 index 0000000..ff628b7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__init__.py @@ -0,0 +1,270 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.insights.v1.call.annotation import AnnotationList +from twilio.rest.insights.v1.call.call_summary import CallSummaryList +from twilio.rest.insights.v1.call.event import EventList +from twilio.rest.insights.v1.call.metric import MetricList + + +class CallInstance(InstanceResource): + """ + :ivar sid: + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CallContext] = None + + @property + def _proxy(self) -> "CallContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CallContext for this CallInstance + """ + if self._context is None: + self._context = CallContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "CallInstance": + """ + Fetch the CallInstance + + + :returns: The fetched CallInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CallInstance": + """ + Asynchronous coroutine to fetch the CallInstance + + + :returns: The fetched CallInstance + """ + return await self._proxy.fetch_async() + + @property + def annotation(self) -> AnnotationList: + """ + Access the annotation + """ + return self._proxy.annotation + + @property + def summary(self) -> CallSummaryList: + """ + Access the summary + """ + return self._proxy.summary + + @property + def events(self) -> EventList: + """ + Access the events + """ + return self._proxy.events + + @property + def metrics(self) -> MetricList: + """ + Access the metrics + """ + return self._proxy.metrics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CallContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Voice/{sid}".format(**self._solution) + + self._annotation: Optional[AnnotationList] = None + self._summary: Optional[CallSummaryList] = None + self._events: Optional[EventList] = None + self._metrics: Optional[MetricList] = None + + def fetch(self) -> CallInstance: + """ + Fetch the CallInstance + + + :returns: The fetched CallInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CallInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CallInstance: + """ + Asynchronous coroutine to fetch the CallInstance + + + :returns: The fetched CallInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CallInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def annotation(self) -> AnnotationList: + """ + Access the annotation + """ + if self._annotation is None: + self._annotation = AnnotationList( + self._version, + self._solution["sid"], + ) + return self._annotation + + @property + def summary(self) -> CallSummaryList: + """ + Access the summary + """ + if self._summary is None: + self._summary = CallSummaryList( + self._version, + self._solution["sid"], + ) + return self._summary + + @property + def events(self) -> EventList: + """ + Access the events + """ + if self._events is None: + self._events = EventList( + self._version, + self._solution["sid"], + ) + return self._events + + @property + def metrics(self) -> MetricList: + """ + Access the metrics + """ + if self._metrics is None: + self._metrics = MetricList( + self._version, + self._solution["sid"], + ) + return self._metrics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CallList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sid: str) -> CallContext: + """ + Constructs a CallContext + + :param sid: + """ + return CallContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CallContext: + """ + Constructs a CallContext + + :param sid: + """ + return CallContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..95f7b65 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/annotation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/annotation.cpython-310.pyc new file mode 100644 index 0000000..5972a7d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/annotation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/call_summary.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/call_summary.cpython-310.pyc new file mode 100644 index 0000000..0a334ec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/call_summary.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/event.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/event.cpython-310.pyc new file mode 100644 index 0000000..1accaff Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/event.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/metric.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/metric.cpython-310.pyc new file mode 100644 index 0000000..689779f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/__pycache__/metric.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/annotation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/annotation.py new file mode 100644 index 0000000..d560003 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/annotation.py @@ -0,0 +1,383 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AnnotationInstance(InstanceResource): + + class AnsweredBy(object): + UNKNOWN_ANSWERED_BY = "unknown_answered_by" + HUMAN = "human" + MACHINE = "machine" + + class ConnectivityIssue(object): + UNKNOWN_CONNECTIVITY_ISSUE = "unknown_connectivity_issue" + NO_CONNECTIVITY_ISSUE = "no_connectivity_issue" + INVALID_NUMBER = "invalid_number" + CALLER_ID = "caller_id" + DROPPED_CALL = "dropped_call" + NUMBER_REACHABILITY = "number_reachability" + + """ + :ivar call_sid: The unique SID identifier of the Call. + :ivar account_sid: The unique SID identifier of the Account. + :ivar answered_by: + :ivar connectivity_issue: + :ivar quality_issues: Specifies if the call had any subjective quality issues. Possible values are one or more of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, or `static_noise`. + :ivar spam: Specifies if the call was a spam call. Use this to provide feedback on whether calls placed from your account were marked as spam, or if inbound calls received by your account were unwanted spam. Is of type Boolean: true, false. Use true if the call was a spam call. + :ivar call_score: Specifies the Call Score, if available. This is of type integer. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for rating the call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :ivar comment: Specifies any comments pertaining to the call. Twilio does not treat this field as PII, so no PII should be included in comments. + :ivar incident: Incident or support ticket associated with this call. The `incident` property is of type string with a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in `incident`. + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], call_sid: str): + super().__init__(version) + + self.call_sid: Optional[str] = payload.get("call_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.answered_by: Optional["AnnotationInstance.AnsweredBy"] = payload.get( + "answered_by" + ) + self.connectivity_issue: Optional["AnnotationInstance.ConnectivityIssue"] = ( + payload.get("connectivity_issue") + ) + self.quality_issues: Optional[List[str]] = payload.get("quality_issues") + self.spam: Optional[bool] = payload.get("spam") + self.call_score: Optional[int] = deserialize.integer(payload.get("call_score")) + self.comment: Optional[str] = payload.get("comment") + self.incident: Optional[str] = payload.get("incident") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "call_sid": call_sid, + } + self._context: Optional[AnnotationContext] = None + + @property + def _proxy(self) -> "AnnotationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AnnotationContext for this AnnotationInstance + """ + if self._context is None: + self._context = AnnotationContext( + self._version, + call_sid=self._solution["call_sid"], + ) + return self._context + + def fetch(self) -> "AnnotationInstance": + """ + Fetch the AnnotationInstance + + + :returns: The fetched AnnotationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AnnotationInstance": + """ + Asynchronous coroutine to fetch the AnnotationInstance + + + :returns: The fetched AnnotationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + answered_by: Union["AnnotationInstance.AnsweredBy", object] = values.unset, + connectivity_issue: Union[ + "AnnotationInstance.ConnectivityIssue", object + ] = values.unset, + quality_issues: Union[str, object] = values.unset, + spam: Union[bool, object] = values.unset, + call_score: Union[int, object] = values.unset, + comment: Union[str, object] = values.unset, + incident: Union[str, object] = values.unset, + ) -> "AnnotationInstance": + """ + Update the AnnotationInstance + + :param answered_by: + :param connectivity_issue: + :param quality_issues: Specify if the call had any subjective quality issues. Possible values, one or more of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. Use comma separated values to indicate multiple quality issues for the same call. + :param spam: A boolean flag to indicate if the call was a spam call. Use this to provide feedback on whether calls placed from your account were marked as spam, or if inbound calls received by your account were unwanted spam. Use `true` if the call was a spam call. + :param call_score: Specify the call score. This is of type integer. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for rating the call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param comment: Specify any comments pertaining to the call. `comment` has a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in the `comment`. + :param incident: Associate this call with an incident or support ticket. The `incident` parameter is of type string with a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in `incident`. + + :returns: The updated AnnotationInstance + """ + return self._proxy.update( + answered_by=answered_by, + connectivity_issue=connectivity_issue, + quality_issues=quality_issues, + spam=spam, + call_score=call_score, + comment=comment, + incident=incident, + ) + + async def update_async( + self, + answered_by: Union["AnnotationInstance.AnsweredBy", object] = values.unset, + connectivity_issue: Union[ + "AnnotationInstance.ConnectivityIssue", object + ] = values.unset, + quality_issues: Union[str, object] = values.unset, + spam: Union[bool, object] = values.unset, + call_score: Union[int, object] = values.unset, + comment: Union[str, object] = values.unset, + incident: Union[str, object] = values.unset, + ) -> "AnnotationInstance": + """ + Asynchronous coroutine to update the AnnotationInstance + + :param answered_by: + :param connectivity_issue: + :param quality_issues: Specify if the call had any subjective quality issues. Possible values, one or more of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. Use comma separated values to indicate multiple quality issues for the same call. + :param spam: A boolean flag to indicate if the call was a spam call. Use this to provide feedback on whether calls placed from your account were marked as spam, or if inbound calls received by your account were unwanted spam. Use `true` if the call was a spam call. + :param call_score: Specify the call score. This is of type integer. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for rating the call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param comment: Specify any comments pertaining to the call. `comment` has a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in the `comment`. + :param incident: Associate this call with an incident or support ticket. The `incident` parameter is of type string with a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in `incident`. + + :returns: The updated AnnotationInstance + """ + return await self._proxy.update_async( + answered_by=answered_by, + connectivity_issue=connectivity_issue, + quality_issues=quality_issues, + spam=spam, + call_score=call_score, + comment=comment, + incident=incident, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AnnotationContext(InstanceContext): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the AnnotationContext + + :param version: Version that contains the resource + :param call_sid: The unique string that Twilio created to identify this Call resource. It always starts with a CA. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + self._uri = "/Voice/{call_sid}/Annotation".format(**self._solution) + + def fetch(self) -> AnnotationInstance: + """ + Fetch the AnnotationInstance + + + :returns: The fetched AnnotationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AnnotationInstance( + self._version, + payload, + call_sid=self._solution["call_sid"], + ) + + async def fetch_async(self) -> AnnotationInstance: + """ + Asynchronous coroutine to fetch the AnnotationInstance + + + :returns: The fetched AnnotationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AnnotationInstance( + self._version, + payload, + call_sid=self._solution["call_sid"], + ) + + def update( + self, + answered_by: Union["AnnotationInstance.AnsweredBy", object] = values.unset, + connectivity_issue: Union[ + "AnnotationInstance.ConnectivityIssue", object + ] = values.unset, + quality_issues: Union[str, object] = values.unset, + spam: Union[bool, object] = values.unset, + call_score: Union[int, object] = values.unset, + comment: Union[str, object] = values.unset, + incident: Union[str, object] = values.unset, + ) -> AnnotationInstance: + """ + Update the AnnotationInstance + + :param answered_by: + :param connectivity_issue: + :param quality_issues: Specify if the call had any subjective quality issues. Possible values, one or more of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. Use comma separated values to indicate multiple quality issues for the same call. + :param spam: A boolean flag to indicate if the call was a spam call. Use this to provide feedback on whether calls placed from your account were marked as spam, or if inbound calls received by your account were unwanted spam. Use `true` if the call was a spam call. + :param call_score: Specify the call score. This is of type integer. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for rating the call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param comment: Specify any comments pertaining to the call. `comment` has a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in the `comment`. + :param incident: Associate this call with an incident or support ticket. The `incident` parameter is of type string with a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in `incident`. + + :returns: The updated AnnotationInstance + """ + data = values.of( + { + "AnsweredBy": answered_by, + "ConnectivityIssue": connectivity_issue, + "QualityIssues": quality_issues, + "Spam": serialize.boolean_to_string(spam), + "CallScore": call_score, + "Comment": comment, + "Incident": incident, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AnnotationInstance( + self._version, payload, call_sid=self._solution["call_sid"] + ) + + async def update_async( + self, + answered_by: Union["AnnotationInstance.AnsweredBy", object] = values.unset, + connectivity_issue: Union[ + "AnnotationInstance.ConnectivityIssue", object + ] = values.unset, + quality_issues: Union[str, object] = values.unset, + spam: Union[bool, object] = values.unset, + call_score: Union[int, object] = values.unset, + comment: Union[str, object] = values.unset, + incident: Union[str, object] = values.unset, + ) -> AnnotationInstance: + """ + Asynchronous coroutine to update the AnnotationInstance + + :param answered_by: + :param connectivity_issue: + :param quality_issues: Specify if the call had any subjective quality issues. Possible values, one or more of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. Use comma separated values to indicate multiple quality issues for the same call. + :param spam: A boolean flag to indicate if the call was a spam call. Use this to provide feedback on whether calls placed from your account were marked as spam, or if inbound calls received by your account were unwanted spam. Use `true` if the call was a spam call. + :param call_score: Specify the call score. This is of type integer. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for rating the call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param comment: Specify any comments pertaining to the call. `comment` has a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in the `comment`. + :param incident: Associate this call with an incident or support ticket. The `incident` parameter is of type string with a maximum character limit of 100. Twilio does not treat this field as PII, so no PII should be included in `incident`. + + :returns: The updated AnnotationInstance + """ + data = values.of( + { + "AnsweredBy": answered_by, + "ConnectivityIssue": connectivity_issue, + "QualityIssues": quality_issues, + "Spam": serialize.boolean_to_string(spam), + "CallScore": call_score, + "Comment": comment, + "Incident": incident, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AnnotationInstance( + self._version, payload, call_sid=self._solution["call_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AnnotationList(ListResource): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the AnnotationList + + :param version: Version that contains the resource + :param call_sid: The unique SID identifier of the Call. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + + def get(self) -> AnnotationContext: + """ + Constructs a AnnotationContext + + """ + return AnnotationContext(self._version, call_sid=self._solution["call_sid"]) + + def __call__(self) -> AnnotationContext: + """ + Constructs a AnnotationContext + + """ + return AnnotationContext(self._version, call_sid=self._solution["call_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/call_summary.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/call_summary.py new file mode 100644 index 0000000..0e256b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/call_summary.py @@ -0,0 +1,310 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class CallSummaryInstance(InstanceResource): + + class AnsweredBy(object): + UNKNOWN = "unknown" + MACHINE_START = "machine_start" + MACHINE_END_BEEP = "machine_end_beep" + MACHINE_END_SILENCE = "machine_end_silence" + MACHINE_END_OTHER = "machine_end_other" + HUMAN = "human" + FAX = "fax" + + class CallState(object): + RINGING = "ringing" + COMPLETED = "completed" + BUSY = "busy" + FAIL = "fail" + NOANSWER = "noanswer" + CANCELED = "canceled" + ANSWERED = "answered" + UNDIALED = "undialed" + + class CallType(object): + CARRIER = "carrier" + SIP = "sip" + TRUNKING = "trunking" + CLIENT = "client" + + class ProcessingState(object): + COMPLETE = "complete" + PARTIAL = "partial" + + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar call_sid: The unique SID identifier of the Call. + :ivar call_type: + :ivar call_state: + :ivar answered_by: + :ivar processing_state: + :ivar created_time: The time at which the Call was created, given in ISO 8601 format. Can be different from `start_time` in the event of queueing due to CPS + :ivar start_time: The time at which the Call was started, given in ISO 8601 format. + :ivar end_time: The time at which the Call was ended, given in ISO 8601 format. + :ivar duration: Duration between when the call was initiated and the call was ended + :ivar connect_duration: Duration between when the call was answered and when it ended + :ivar _from: The calling party. + :ivar to: The called party. + :ivar carrier_edge: Contains metrics and properties for the Twilio media gateway of a PSTN call. + :ivar client_edge: Contains metrics and properties for the Twilio media gateway of a Client call. + :ivar sdk_edge: Contains metrics and properties for the SDK sensor library for Client calls. + :ivar sip_edge: Contains metrics and properties for the Twilio media gateway of a SIP Interface or Trunking call. + :ivar tags: Tags applied to calls by Voice Insights analysis indicating a condition that could result in subjective degradation of the call quality. + :ivar url: The URL of this resource. + :ivar attributes: Attributes capturing call-flow-specific details. + :ivar properties: Contains edge-agnostic call-level details. + :ivar trust: Contains trusted communications details including Branded Call and verified caller ID. + :ivar annotation: Programmatically labeled annotations for the Call. Developers can update the Call Summary records with Annotation during or after a Call. Annotations can be updated as long as the Call Summary record is addressable via the API. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], call_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.call_type: Optional["CallSummaryInstance.CallType"] = payload.get( + "call_type" + ) + self.call_state: Optional["CallSummaryInstance.CallState"] = payload.get( + "call_state" + ) + self.answered_by: Optional["CallSummaryInstance.AnsweredBy"] = payload.get( + "answered_by" + ) + self.processing_state: Optional["CallSummaryInstance.ProcessingState"] = ( + payload.get("processing_state") + ) + self.created_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("created_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.connect_duration: Optional[int] = deserialize.integer( + payload.get("connect_duration") + ) + self._from: Optional[Dict[str, object]] = payload.get("from") + self.to: Optional[Dict[str, object]] = payload.get("to") + self.carrier_edge: Optional[Dict[str, object]] = payload.get("carrier_edge") + self.client_edge: Optional[Dict[str, object]] = payload.get("client_edge") + self.sdk_edge: Optional[Dict[str, object]] = payload.get("sdk_edge") + self.sip_edge: Optional[Dict[str, object]] = payload.get("sip_edge") + self.tags: Optional[List[str]] = payload.get("tags") + self.url: Optional[str] = payload.get("url") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.properties: Optional[Dict[str, object]] = payload.get("properties") + self.trust: Optional[Dict[str, object]] = payload.get("trust") + self.annotation: Optional[Dict[str, object]] = payload.get("annotation") + + self._solution = { + "call_sid": call_sid, + } + self._context: Optional[CallSummaryContext] = None + + @property + def _proxy(self) -> "CallSummaryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CallSummaryContext for this CallSummaryInstance + """ + if self._context is None: + self._context = CallSummaryContext( + self._version, + call_sid=self._solution["call_sid"], + ) + return self._context + + def fetch( + self, + processing_state: Union[ + "CallSummaryInstance.ProcessingState", object + ] = values.unset, + ) -> "CallSummaryInstance": + """ + Fetch the CallSummaryInstance + + :param processing_state: The Processing State of this Call Summary. One of `complete`, `partial` or `all`. + + :returns: The fetched CallSummaryInstance + """ + return self._proxy.fetch( + processing_state=processing_state, + ) + + async def fetch_async( + self, + processing_state: Union[ + "CallSummaryInstance.ProcessingState", object + ] = values.unset, + ) -> "CallSummaryInstance": + """ + Asynchronous coroutine to fetch the CallSummaryInstance + + :param processing_state: The Processing State of this Call Summary. One of `complete`, `partial` or `all`. + + :returns: The fetched CallSummaryInstance + """ + return await self._proxy.fetch_async( + processing_state=processing_state, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallSummaryContext(InstanceContext): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the CallSummaryContext + + :param version: Version that contains the resource + :param call_sid: The unique SID identifier of the Call. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + self._uri = "/Voice/{call_sid}/Summary".format(**self._solution) + + def fetch( + self, + processing_state: Union[ + "CallSummaryInstance.ProcessingState", object + ] = values.unset, + ) -> CallSummaryInstance: + """ + Fetch the CallSummaryInstance + + :param processing_state: The Processing State of this Call Summary. One of `complete`, `partial` or `all`. + + :returns: The fetched CallSummaryInstance + """ + + data = values.of( + { + "ProcessingState": processing_state, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return CallSummaryInstance( + self._version, + payload, + call_sid=self._solution["call_sid"], + ) + + async def fetch_async( + self, + processing_state: Union[ + "CallSummaryInstance.ProcessingState", object + ] = values.unset, + ) -> CallSummaryInstance: + """ + Asynchronous coroutine to fetch the CallSummaryInstance + + :param processing_state: The Processing State of this Call Summary. One of `complete`, `partial` or `all`. + + :returns: The fetched CallSummaryInstance + """ + + data = values.of( + { + "ProcessingState": processing_state, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return CallSummaryInstance( + self._version, + payload, + call_sid=self._solution["call_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CallSummaryList(ListResource): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the CallSummaryList + + :param version: Version that contains the resource + :param call_sid: The unique SID identifier of the Call. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + + def get(self) -> CallSummaryContext: + """ + Constructs a CallSummaryContext + + """ + return CallSummaryContext(self._version, call_sid=self._solution["call_sid"]) + + def __call__(self) -> CallSummaryContext: + """ + Constructs a CallSummaryContext + + """ + return CallSummaryContext(self._version, call_sid=self._solution["call_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/event.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/event.py new file mode 100644 index 0000000..89734f1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/event.py @@ -0,0 +1,327 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EventInstance(InstanceResource): + + class Level(object): + UNKNOWN = "UNKNOWN" + DEBUG = "DEBUG" + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" + + class TwilioEdge(object): + UNKNOWN_EDGE = "unknown_edge" + CARRIER_EDGE = "carrier_edge" + SIP_EDGE = "sip_edge" + SDK_EDGE = "sdk_edge" + CLIENT_EDGE = "client_edge" + + """ + :ivar timestamp: Event time. + :ivar call_sid: The unique SID identifier of the Call. + :ivar account_sid: The unique SID identifier of the Account. + :ivar edge: + :ivar group: Event group. + :ivar level: + :ivar name: Event name. + :ivar carrier_edge: Represents the connection between Twilio and our immediate carrier partners. The events here describe the call lifecycle as reported by Twilio's carrier media gateways. + :ivar sip_edge: Represents the Twilio media gateway for SIP interface and SIP trunking calls. The events here describe the call lifecycle as reported by Twilio's public media gateways. + :ivar sdk_edge: Represents the Voice SDK running locally in the browser or in the Android/iOS application. The events here are emitted by the Voice SDK in response to certain call progress events, network changes, or call quality conditions. + :ivar client_edge: Represents the Twilio media gateway for Client calls. The events here describe the call lifecycle as reported by Twilio's Voice SDK media gateways. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], call_sid: str): + super().__init__(version) + + self.timestamp: Optional[str] = payload.get("timestamp") + self.call_sid: Optional[str] = payload.get("call_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.edge: Optional["EventInstance.TwilioEdge"] = payload.get("edge") + self.group: Optional[str] = payload.get("group") + self.level: Optional["EventInstance.Level"] = payload.get("level") + self.name: Optional[str] = payload.get("name") + self.carrier_edge: Optional[Dict[str, object]] = payload.get("carrier_edge") + self.sip_edge: Optional[Dict[str, object]] = payload.get("sip_edge") + self.sdk_edge: Optional[Dict[str, object]] = payload.get("sdk_edge") + self.client_edge: Optional[Dict[str, object]] = payload.get("client_edge") + + self._solution = { + "call_sid": call_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EventInstance: + """ + Build an instance of EventInstance + + :param payload: Payload response from the API + """ + return EventInstance( + self._version, payload, call_sid=self._solution["call_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EventList(ListResource): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the EventList + + :param version: Version that contains the resource + :param call_sid: The unique SID identifier of the Call. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + self._uri = "/Voice/{call_sid}/Events".format(**self._solution) + + def stream( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EventInstance]: + """ + Streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "EventInstance.TwilioEdge" edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(edge=edge, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EventInstance]: + """ + Asynchronously streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "EventInstance.TwilioEdge" edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(edge=edge, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "EventInstance.TwilioEdge" edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + edge=edge, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Asynchronously lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "EventInstance.TwilioEdge" edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + edge=edge, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "Edge": edge, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EventPage(self._version, response, self._solution) + + async def page_async( + self, + edge: Union["EventInstance.TwilioEdge", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Asynchronously retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param edge: The Edge of this Event. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "Edge": edge, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EventPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EventPage: + """ + Retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EventPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EventPage: + """ + Asynchronously retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EventPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/metric.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/metric.py new file mode 100644 index 0000000..3cf9dec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call/metric.py @@ -0,0 +1,342 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MetricInstance(InstanceResource): + + class StreamDirection(object): + UNKNOWN = "unknown" + INBOUND = "inbound" + OUTBOUND = "outbound" + BOTH = "both" + + class TwilioEdge(object): + UNKNOWN_EDGE = "unknown_edge" + CARRIER_EDGE = "carrier_edge" + SIP_EDGE = "sip_edge" + SDK_EDGE = "sdk_edge" + CLIENT_EDGE = "client_edge" + + """ + :ivar timestamp: Timestamp of metric sample. Samples are taken every 10 seconds and contain the metrics for the previous 10 seconds. + :ivar call_sid: The unique SID identifier of the Call. + :ivar account_sid: The unique SID identifier of the Account. + :ivar edge: + :ivar direction: + :ivar carrier_edge: Contains metrics and properties for the Twilio media gateway of a PSTN call. + :ivar sip_edge: Contains metrics and properties for the Twilio media gateway of a SIP Interface or Trunking call. + :ivar sdk_edge: Contains metrics and properties for the SDK sensor library for Client calls. + :ivar client_edge: Contains metrics and properties for the Twilio media gateway of a Client call. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], call_sid: str): + super().__init__(version) + + self.timestamp: Optional[str] = payload.get("timestamp") + self.call_sid: Optional[str] = payload.get("call_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.edge: Optional["MetricInstance.TwilioEdge"] = payload.get("edge") + self.direction: Optional["MetricInstance.StreamDirection"] = payload.get( + "direction" + ) + self.carrier_edge: Optional[Dict[str, object]] = payload.get("carrier_edge") + self.sip_edge: Optional[Dict[str, object]] = payload.get("sip_edge") + self.sdk_edge: Optional[Dict[str, object]] = payload.get("sdk_edge") + self.client_edge: Optional[Dict[str, object]] = payload.get("client_edge") + + self._solution = { + "call_sid": call_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MetricPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MetricInstance: + """ + Build an instance of MetricInstance + + :param payload: Payload response from the API + """ + return MetricInstance( + self._version, payload, call_sid=self._solution["call_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MetricList(ListResource): + + def __init__(self, version: Version, call_sid: str): + """ + Initialize the MetricList + + :param version: Version that contains the resource + :param call_sid: The unique SID identifier of the Call. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "call_sid": call_sid, + } + self._uri = "/Voice/{call_sid}/Metrics".format(**self._solution) + + def stream( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MetricInstance]: + """ + Streams MetricInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MetricInstance.TwilioEdge" edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param "MetricInstance.StreamDirection" direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(edge=edge, direction=direction, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MetricInstance]: + """ + Asynchronously streams MetricInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MetricInstance.TwilioEdge" edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param "MetricInstance.StreamDirection" direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + edge=edge, direction=direction, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MetricInstance]: + """ + Lists MetricInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MetricInstance.TwilioEdge" edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param "MetricInstance.StreamDirection" direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + edge=edge, + direction=direction, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MetricInstance]: + """ + Asynchronously lists MetricInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MetricInstance.TwilioEdge" edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param "MetricInstance.StreamDirection" direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + edge=edge, + direction=direction, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MetricPage: + """ + Retrieve a single page of MetricInstance records from the API. + Request is executed immediately + + :param edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MetricInstance + """ + data = values.of( + { + "Edge": edge, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MetricPage(self._version, response, self._solution) + + async def page_async( + self, + edge: Union["MetricInstance.TwilioEdge", object] = values.unset, + direction: Union["MetricInstance.StreamDirection", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MetricPage: + """ + Asynchronously retrieve a single page of MetricInstance records from the API. + Request is executed immediately + + :param edge: The Edge of this Metric. One of `unknown_edge`, `carrier_edge`, `sip_edge`, `sdk_edge` or `client_edge`. + :param direction: The Direction of this Metric. One of `unknown`, `inbound`, `outbound` or `both`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MetricInstance + """ + data = values.of( + { + "Edge": edge, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MetricPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MetricPage: + """ + Retrieve a specific page of MetricInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MetricInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MetricPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MetricPage: + """ + Asynchronously retrieve a specific page of MetricInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MetricInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MetricPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call_summaries.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call_summaries.py new file mode 100644 index 0000000..f68744e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/call_summaries.py @@ -0,0 +1,814 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CallSummariesInstance(InstanceResource): + + class AnsweredBy(object): + UNKNOWN = "unknown" + MACHINE_START = "machine_start" + MACHINE_END_BEEP = "machine_end_beep" + MACHINE_END_SILENCE = "machine_end_silence" + MACHINE_END_OTHER = "machine_end_other" + HUMAN = "human" + FAX = "fax" + + class CallState(object): + RINGING = "ringing" + COMPLETED = "completed" + BUSY = "busy" + FAIL = "fail" + NOANSWER = "noanswer" + CANCELED = "canceled" + ANSWERED = "answered" + UNDIALED = "undialed" + + class CallType(object): + CARRIER = "carrier" + SIP = "sip" + TRUNKING = "trunking" + CLIENT = "client" + + class ProcessingState(object): + COMPLETE = "complete" + PARTIAL = "partial" + + class ProcessingStateRequest(object): + COMPLETED = "completed" + STARTED = "started" + PARTIAL = "partial" + ALL = "all" + + class SortBy(object): + START_TIME = "start_time" + END_TIME = "end_time" + + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar call_sid: The unique SID identifier of the Call. + :ivar answered_by: + :ivar call_type: + :ivar call_state: + :ivar processing_state: + :ivar created_time: The time at which the Call was created, given in ISO 8601 format. Can be different from `start_time` in the event of queueing due to CPS + :ivar start_time: The time at which the Call was started, given in ISO 8601 format. + :ivar end_time: The time at which the Call was ended, given in ISO 8601 format. + :ivar duration: Duration between when the call was initiated and the call was ended + :ivar connect_duration: Duration between when the call was answered and when it ended + :ivar _from: The calling party. + :ivar to: The called party. + :ivar carrier_edge: Contains metrics and properties for the Twilio media gateway of a PSTN call. + :ivar client_edge: Contains metrics and properties for the Twilio media gateway of a Client call. + :ivar sdk_edge: Contains metrics and properties for the SDK sensor library for Client calls. + :ivar sip_edge: Contains metrics and properties for the Twilio media gateway of a SIP Interface or Trunking call. + :ivar tags: Tags applied to calls by Voice Insights analysis indicating a condition that could result in subjective degradation of the call quality. + :ivar url: The URL of this resource. + :ivar attributes: Attributes capturing call-flow-specific details. + :ivar properties: Contains edge-agnostic call-level details. + :ivar trust: Contains trusted communications details including Branded Call and verified caller ID. + :ivar annotation: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.answered_by: Optional["CallSummariesInstance.AnsweredBy"] = payload.get( + "answered_by" + ) + self.call_type: Optional["CallSummariesInstance.CallType"] = payload.get( + "call_type" + ) + self.call_state: Optional["CallSummariesInstance.CallState"] = payload.get( + "call_state" + ) + self.processing_state: Optional["CallSummariesInstance.ProcessingState"] = ( + payload.get("processing_state") + ) + self.created_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("created_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.connect_duration: Optional[int] = deserialize.integer( + payload.get("connect_duration") + ) + self._from: Optional[Dict[str, object]] = payload.get("from") + self.to: Optional[Dict[str, object]] = payload.get("to") + self.carrier_edge: Optional[Dict[str, object]] = payload.get("carrier_edge") + self.client_edge: Optional[Dict[str, object]] = payload.get("client_edge") + self.sdk_edge: Optional[Dict[str, object]] = payload.get("sdk_edge") + self.sip_edge: Optional[Dict[str, object]] = payload.get("sip_edge") + self.tags: Optional[List[str]] = payload.get("tags") + self.url: Optional[str] = payload.get("url") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.properties: Optional[Dict[str, object]] = payload.get("properties") + self.trust: Optional[Dict[str, object]] = payload.get("trust") + self.annotation: Optional[Dict[str, object]] = payload.get("annotation") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class CallSummariesPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CallSummariesInstance: + """ + Build an instance of CallSummariesInstance + + :param payload: Payload response from the API + """ + return CallSummariesInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CallSummariesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CallSummariesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Voice/Summaries" + + def stream( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CallSummariesInstance]: + """ + Streams CallSummariesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str from_carrier: An origination carrier. + :param str to_carrier: A destination carrier. + :param str from_country_code: A source country code based on phone number in From. + :param str to_country_code: A destination country code. Based on phone number in To. + :param bool branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param bool verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param bool has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param str start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param str end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param str call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param str call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param str direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param "CallSummariesInstance.ProcessingStateRequest" processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param "CallSummariesInstance.SortBy" sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param str subaccount: A unique SID identifier of a Subaccount. + :param bool abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param "CallSummariesInstance.AnsweredBy" answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param str answered_by_annotation: Either machine or human. + :param str connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param str quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param bool spam_annotation: A boolean flag indicating spam calls. + :param str call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + from_=from_, + to=to, + from_carrier=from_carrier, + to_carrier=to_carrier, + from_country_code=from_country_code, + to_country_code=to_country_code, + branded=branded, + verified_caller=verified_caller, + has_tag=has_tag, + start_time=start_time, + end_time=end_time, + call_type=call_type, + call_state=call_state, + direction=direction, + processing_state=processing_state, + sort_by=sort_by, + subaccount=subaccount, + abnormal_session=abnormal_session, + answered_by=answered_by, + answered_by_annotation=answered_by_annotation, + connectivity_issue_annotation=connectivity_issue_annotation, + quality_issue_annotation=quality_issue_annotation, + spam_annotation=spam_annotation, + call_score_annotation=call_score_annotation, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CallSummariesInstance]: + """ + Asynchronously streams CallSummariesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str from_carrier: An origination carrier. + :param str to_carrier: A destination carrier. + :param str from_country_code: A source country code based on phone number in From. + :param str to_country_code: A destination country code. Based on phone number in To. + :param bool branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param bool verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param bool has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param str start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param str end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param str call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param str call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param str direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param "CallSummariesInstance.ProcessingStateRequest" processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param "CallSummariesInstance.SortBy" sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param str subaccount: A unique SID identifier of a Subaccount. + :param bool abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param "CallSummariesInstance.AnsweredBy" answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param str answered_by_annotation: Either machine or human. + :param str connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param str quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param bool spam_annotation: A boolean flag indicating spam calls. + :param str call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + from_=from_, + to=to, + from_carrier=from_carrier, + to_carrier=to_carrier, + from_country_code=from_country_code, + to_country_code=to_country_code, + branded=branded, + verified_caller=verified_caller, + has_tag=has_tag, + start_time=start_time, + end_time=end_time, + call_type=call_type, + call_state=call_state, + direction=direction, + processing_state=processing_state, + sort_by=sort_by, + subaccount=subaccount, + abnormal_session=abnormal_session, + answered_by=answered_by, + answered_by_annotation=answered_by_annotation, + connectivity_issue_annotation=connectivity_issue_annotation, + quality_issue_annotation=quality_issue_annotation, + spam_annotation=spam_annotation, + call_score_annotation=call_score_annotation, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CallSummariesInstance]: + """ + Lists CallSummariesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str from_carrier: An origination carrier. + :param str to_carrier: A destination carrier. + :param str from_country_code: A source country code based on phone number in From. + :param str to_country_code: A destination country code. Based on phone number in To. + :param bool branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param bool verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param bool has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param str start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param str end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param str call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param str call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param str direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param "CallSummariesInstance.ProcessingStateRequest" processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param "CallSummariesInstance.SortBy" sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param str subaccount: A unique SID identifier of a Subaccount. + :param bool abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param "CallSummariesInstance.AnsweredBy" answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param str answered_by_annotation: Either machine or human. + :param str connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param str quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param bool spam_annotation: A boolean flag indicating spam calls. + :param str call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + from_=from_, + to=to, + from_carrier=from_carrier, + to_carrier=to_carrier, + from_country_code=from_country_code, + to_country_code=to_country_code, + branded=branded, + verified_caller=verified_caller, + has_tag=has_tag, + start_time=start_time, + end_time=end_time, + call_type=call_type, + call_state=call_state, + direction=direction, + processing_state=processing_state, + sort_by=sort_by, + subaccount=subaccount, + abnormal_session=abnormal_session, + answered_by=answered_by, + answered_by_annotation=answered_by_annotation, + connectivity_issue_annotation=connectivity_issue_annotation, + quality_issue_annotation=quality_issue_annotation, + spam_annotation=spam_annotation, + call_score_annotation=call_score_annotation, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CallSummariesInstance]: + """ + Asynchronously lists CallSummariesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param str from_carrier: An origination carrier. + :param str to_carrier: A destination carrier. + :param str from_country_code: A source country code based on phone number in From. + :param str to_country_code: A destination country code. Based on phone number in To. + :param bool branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param bool verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param bool has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param str start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param str end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param str call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param str call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param str direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param "CallSummariesInstance.ProcessingStateRequest" processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param "CallSummariesInstance.SortBy" sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param str subaccount: A unique SID identifier of a Subaccount. + :param bool abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param "CallSummariesInstance.AnsweredBy" answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param str answered_by_annotation: Either machine or human. + :param str connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param str quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param bool spam_annotation: A boolean flag indicating spam calls. + :param str call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + from_=from_, + to=to, + from_carrier=from_carrier, + to_carrier=to_carrier, + from_country_code=from_country_code, + to_country_code=to_country_code, + branded=branded, + verified_caller=verified_caller, + has_tag=has_tag, + start_time=start_time, + end_time=end_time, + call_type=call_type, + call_state=call_state, + direction=direction, + processing_state=processing_state, + sort_by=sort_by, + subaccount=subaccount, + abnormal_session=abnormal_session, + answered_by=answered_by, + answered_by_annotation=answered_by_annotation, + connectivity_issue_annotation=connectivity_issue_annotation, + quality_issue_annotation=quality_issue_annotation, + spam_annotation=spam_annotation, + call_score_annotation=call_score_annotation, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CallSummariesPage: + """ + Retrieve a single page of CallSummariesInstance records from the API. + Request is executed immediately + + :param from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param from_carrier: An origination carrier. + :param to_carrier: A destination carrier. + :param from_country_code: A source country code based on phone number in From. + :param to_country_code: A destination country code. Based on phone number in To. + :param branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param subaccount: A unique SID identifier of a Subaccount. + :param abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param answered_by_annotation: Either machine or human. + :param connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param spam_annotation: A boolean flag indicating spam calls. + :param call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CallSummariesInstance + """ + data = values.of( + { + "From": from_, + "To": to, + "FromCarrier": from_carrier, + "ToCarrier": to_carrier, + "FromCountryCode": from_country_code, + "ToCountryCode": to_country_code, + "Branded": serialize.boolean_to_string(branded), + "VerifiedCaller": serialize.boolean_to_string(verified_caller), + "HasTag": serialize.boolean_to_string(has_tag), + "StartTime": start_time, + "EndTime": end_time, + "CallType": call_type, + "CallState": call_state, + "Direction": direction, + "ProcessingState": processing_state, + "SortBy": sort_by, + "Subaccount": subaccount, + "AbnormalSession": serialize.boolean_to_string(abnormal_session), + "AnsweredBy": answered_by, + "AnsweredByAnnotation": answered_by_annotation, + "ConnectivityIssueAnnotation": connectivity_issue_annotation, + "QualityIssueAnnotation": quality_issue_annotation, + "SpamAnnotation": serialize.boolean_to_string(spam_annotation), + "CallScoreAnnotation": call_score_annotation, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CallSummariesPage(self._version, response) + + async def page_async( + self, + from_: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_carrier: Union[str, object] = values.unset, + to_carrier: Union[str, object] = values.unset, + from_country_code: Union[str, object] = values.unset, + to_country_code: Union[str, object] = values.unset, + branded: Union[bool, object] = values.unset, + verified_caller: Union[bool, object] = values.unset, + has_tag: Union[bool, object] = values.unset, + start_time: Union[str, object] = values.unset, + end_time: Union[str, object] = values.unset, + call_type: Union[str, object] = values.unset, + call_state: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + processing_state: Union[ + "CallSummariesInstance.ProcessingStateRequest", object + ] = values.unset, + sort_by: Union["CallSummariesInstance.SortBy", object] = values.unset, + subaccount: Union[str, object] = values.unset, + abnormal_session: Union[bool, object] = values.unset, + answered_by: Union["CallSummariesInstance.AnsweredBy", object] = values.unset, + answered_by_annotation: Union[str, object] = values.unset, + connectivity_issue_annotation: Union[str, object] = values.unset, + quality_issue_annotation: Union[str, object] = values.unset, + spam_annotation: Union[bool, object] = values.unset, + call_score_annotation: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CallSummariesPage: + """ + Asynchronously retrieve a single page of CallSummariesInstance records from the API. + Request is executed immediately + + :param from_: A calling party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param to: A called party. Could be an E.164 number, a SIP URI, or a Twilio Client registered name. + :param from_carrier: An origination carrier. + :param to_carrier: A destination carrier. + :param from_country_code: A source country code based on phone number in From. + :param to_country_code: A destination country code. Based on phone number in To. + :param branded: A boolean flag indicating whether or not the calls were branded using Twilio Branded Calls. + :param verified_caller: A boolean flag indicating whether or not the caller was verified using SHAKEN/STIR. + :param has_tag: A boolean flag indicating the presence of one or more [Voice Insights Call Tags](https://www.twilio.com/docs/voice/voice-insights/api/call/details-call-tags). + :param start_time: A Start time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 4h. + :param end_time: An End Time of the calls. xm (x minutes), xh (x hours), xd (x days), 1w, 30m, 3d, 4w or datetime-ISO. Defaults to 0m. + :param call_type: A Call Type of the calls. One of `carrier`, `sip`, `trunking` or `client`. + :param call_state: A Call State of the calls. One of `ringing`, `completed`, `busy`, `fail`, `noanswer`, `canceled`, `answered`, `undialed`. + :param direction: A Direction of the calls. One of `outbound_api`, `outbound_dial`, `inbound`, `trunking_originating`, `trunking_terminating`. + :param processing_state: A Processing State of the Call Summaries. One of `completed`, `partial` or `all`. + :param sort_by: A Sort By criterion for the returned list of Call Summaries. One of `start_time` or `end_time`. + :param subaccount: A unique SID identifier of a Subaccount. + :param abnormal_session: A boolean flag indicating an abnormal session where the last SIP response was not 200 OK. + :param answered_by: An Answered By value for the calls based on `Answering Machine Detection (AMD)`. One of `unknown`, `machine_start`, `machine_end_beep`, `machine_end_silence`, `machine_end_other`, `human` or `fax`. + :param answered_by_annotation: Either machine or human. + :param connectivity_issue_annotation: A Connectivity Issue with the calls. One of `no_connectivity_issue`, `invalid_number`, `caller_id`, `dropped_call`, or `number_reachability`. + :param quality_issue_annotation: A subjective Quality Issue with the calls. One of `no_quality_issue`, `low_volume`, `choppy_robotic`, `echo`, `dtmf`, `latency`, `owa`, `static_noise`. + :param spam_annotation: A boolean flag indicating spam calls. + :param call_score_annotation: A Call Score of the calls. Use a range of 1-5 to indicate the call experience score, with the following mapping as a reference for the rated call [5: Excellent, 4: Good, 3 : Fair, 2 : Poor, 1: Bad]. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CallSummariesInstance + """ + data = values.of( + { + "From": from_, + "To": to, + "FromCarrier": from_carrier, + "ToCarrier": to_carrier, + "FromCountryCode": from_country_code, + "ToCountryCode": to_country_code, + "Branded": serialize.boolean_to_string(branded), + "VerifiedCaller": serialize.boolean_to_string(verified_caller), + "HasTag": serialize.boolean_to_string(has_tag), + "StartTime": start_time, + "EndTime": end_time, + "CallType": call_type, + "CallState": call_state, + "Direction": direction, + "ProcessingState": processing_state, + "SortBy": sort_by, + "Subaccount": subaccount, + "AbnormalSession": serialize.boolean_to_string(abnormal_session), + "AnsweredBy": answered_by, + "AnsweredByAnnotation": answered_by_annotation, + "ConnectivityIssueAnnotation": connectivity_issue_annotation, + "QualityIssueAnnotation": quality_issue_annotation, + "SpamAnnotation": serialize.boolean_to_string(spam_annotation), + "CallScoreAnnotation": call_score_annotation, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CallSummariesPage(self._version, response) + + def get_page(self, target_url: str) -> CallSummariesPage: + """ + Retrieve a specific page of CallSummariesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CallSummariesInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CallSummariesPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CallSummariesPage: + """ + Asynchronously retrieve a specific page of CallSummariesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CallSummariesInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CallSummariesPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__init__.py new file mode 100644 index 0000000..1ee8c09 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__init__.py @@ -0,0 +1,715 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.insights.v1.conference.conference_participant import ( + ConferenceParticipantList, +) + + +class ConferenceInstance(InstanceResource): + + class ConferenceEndReason(object): + LAST_PARTICIPANT_LEFT = "last_participant_left" + CONFERENCE_ENDED_VIA_API = "conference_ended_via_api" + PARTICIPANT_WITH_END_CONFERENCE_ON_EXIT_LEFT = ( + "participant_with_end_conference_on_exit_left" + ) + LAST_PARTICIPANT_KICKED = "last_participant_kicked" + PARTICIPANT_WITH_END_CONFERENCE_ON_EXIT_KICKED = ( + "participant_with_end_conference_on_exit_kicked" + ) + + class ConferenceStatus(object): + IN_PROGRESS = "in_progress" + NOT_STARTED = "not_started" + COMPLETED = "completed" + SUMMARY_TIMEOUT = "summary_timeout" + + class ProcessingState(object): + COMPLETE = "complete" + IN_PROGRESS = "in_progress" + TIMEOUT = "timeout" + + class Region(object): + US1 = "us1" + AU1 = "au1" + BR1 = "br1" + IE1 = "ie1" + JP1 = "jp1" + SG1 = "sg1" + DE1 = "de1" + + class Tag(object): + INVALID_REQUESTED_REGION = "invalid_requested_region" + DUPLICATE_IDENTITY = "duplicate_identity" + START_FAILURE = "start_failure" + REGION_CONFIGURATION_ISSUES = "region_configuration_issues" + QUALITY_WARNINGS = "quality_warnings" + PARTICIPANT_BEHAVIOR_ISSUES = "participant_behavior_issues" + HIGH_PACKET_LOSS = "high_packet_loss" + HIGH_JITTER = "high_jitter" + HIGH_LATENCY = "high_latency" + LOW_MOS = "low_mos" + DETECTED_SILENCE = "detected_silence" + + """ + :ivar conference_sid: The unique SID identifier of the Conference. + :ivar account_sid: The unique SID identifier of the Account. + :ivar friendly_name: Custom label for the conference resource, up to 64 characters. + :ivar create_time: Conference creation date and time in ISO 8601 format. + :ivar start_time: Timestamp in ISO 8601 format when the conference started. Conferences do not start until at least two participants join, at least one of whom has startConferenceOnEnter=true. + :ivar end_time: Conference end date and time in ISO 8601 format. + :ivar duration_seconds: Conference duration in seconds. + :ivar connect_duration_seconds: Duration of the between conference start event and conference end event in seconds. + :ivar status: + :ivar max_participants: Maximum number of concurrent participants as specified by the configuration. + :ivar max_concurrent_participants: Actual maximum number of concurrent participants in the conference. + :ivar unique_participants: Unique conference participants based on caller ID. + :ivar end_reason: + :ivar ended_by: Call SID of the participant whose actions ended the conference. + :ivar mixer_region: + :ivar mixer_region_requested: + :ivar recording_enabled: Boolean. Indicates whether recording was enabled at the conference mixer. + :ivar detected_issues: Potential issues detected by Twilio during the conference. + :ivar tags: Tags for detected conference conditions and participant behaviors which may be of interest. + :ivar tag_info: Object. Contains details about conference tags including severity. + :ivar processing_state: + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Conference. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conference_sid: Optional[str] = None, + ): + super().__init__(version) + + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.create_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("create_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration_seconds: Optional[int] = deserialize.integer( + payload.get("duration_seconds") + ) + self.connect_duration_seconds: Optional[int] = deserialize.integer( + payload.get("connect_duration_seconds") + ) + self.status: Optional["ConferenceInstance.ConferenceStatus"] = payload.get( + "status" + ) + self.max_participants: Optional[int] = deserialize.integer( + payload.get("max_participants") + ) + self.max_concurrent_participants: Optional[int] = deserialize.integer( + payload.get("max_concurrent_participants") + ) + self.unique_participants: Optional[int] = deserialize.integer( + payload.get("unique_participants") + ) + self.end_reason: Optional["ConferenceInstance.ConferenceEndReason"] = ( + payload.get("end_reason") + ) + self.ended_by: Optional[str] = payload.get("ended_by") + self.mixer_region: Optional["ConferenceInstance.Region"] = payload.get( + "mixer_region" + ) + self.mixer_region_requested: Optional["ConferenceInstance.Region"] = ( + payload.get("mixer_region_requested") + ) + self.recording_enabled: Optional[bool] = payload.get("recording_enabled") + self.detected_issues: Optional[Dict[str, object]] = payload.get( + "detected_issues" + ) + self.tags: Optional[List["ConferenceInstance.Tag"]] = payload.get("tags") + self.tag_info: Optional[Dict[str, object]] = payload.get("tag_info") + self.processing_state: Optional["ConferenceInstance.ProcessingState"] = ( + payload.get("processing_state") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "conference_sid": conference_sid or self.conference_sid, + } + self._context: Optional[ConferenceContext] = None + + @property + def _proxy(self) -> "ConferenceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConferenceContext for this ConferenceInstance + """ + if self._context is None: + self._context = ConferenceContext( + self._version, + conference_sid=self._solution["conference_sid"], + ) + return self._context + + def fetch(self) -> "ConferenceInstance": + """ + Fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConferenceInstance": + """ + Asynchronous coroutine to fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + return await self._proxy.fetch_async() + + @property + def conference_participants(self) -> ConferenceParticipantList: + """ + Access the conference_participants + """ + return self._proxy.conference_participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferenceContext(InstanceContext): + + def __init__(self, version: Version, conference_sid: str): + """ + Initialize the ConferenceContext + + :param version: Version that contains the resource + :param conference_sid: The unique SID identifier of the Conference. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conference_sid": conference_sid, + } + self._uri = "/Conferences/{conference_sid}".format(**self._solution) + + self._conference_participants: Optional[ConferenceParticipantList] = None + + def fetch(self) -> ConferenceInstance: + """ + Fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConferenceInstance( + self._version, + payload, + conference_sid=self._solution["conference_sid"], + ) + + async def fetch_async(self) -> ConferenceInstance: + """ + Asynchronous coroutine to fetch the ConferenceInstance + + + :returns: The fetched ConferenceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConferenceInstance( + self._version, + payload, + conference_sid=self._solution["conference_sid"], + ) + + @property + def conference_participants(self) -> ConferenceParticipantList: + """ + Access the conference_participants + """ + if self._conference_participants is None: + self._conference_participants = ConferenceParticipantList( + self._version, + self._solution["conference_sid"], + ) + return self._conference_participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferencePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConferenceInstance: + """ + Build an instance of ConferenceInstance + + :param payload: Payload response from the API + """ + return ConferenceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConferenceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ConferenceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Conferences" + + def stream( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConferenceInstance]: + """ + Streams ConferenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str conference_sid: The SID of the conference. + :param str friendly_name: Custom label for the conference resource, up to 64 characters. + :param str status: Conference status. + :param str created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param str created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param str mixer_region: Twilio region where the conference media was mixed. + :param str tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param str subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param str detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param str end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + conference_sid=conference_sid, + friendly_name=friendly_name, + status=status, + created_after=created_after, + created_before=created_before, + mixer_region=mixer_region, + tags=tags, + subaccount=subaccount, + detected_issues=detected_issues, + end_reason=end_reason, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConferenceInstance]: + """ + Asynchronously streams ConferenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str conference_sid: The SID of the conference. + :param str friendly_name: Custom label for the conference resource, up to 64 characters. + :param str status: Conference status. + :param str created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param str created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param str mixer_region: Twilio region where the conference media was mixed. + :param str tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param str subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param str detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param str end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + conference_sid=conference_sid, + friendly_name=friendly_name, + status=status, + created_after=created_after, + created_before=created_before, + mixer_region=mixer_region, + tags=tags, + subaccount=subaccount, + detected_issues=detected_issues, + end_reason=end_reason, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceInstance]: + """ + Lists ConferenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str conference_sid: The SID of the conference. + :param str friendly_name: Custom label for the conference resource, up to 64 characters. + :param str status: Conference status. + :param str created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param str created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param str mixer_region: Twilio region where the conference media was mixed. + :param str tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param str subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param str detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param str end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + conference_sid=conference_sid, + friendly_name=friendly_name, + status=status, + created_after=created_after, + created_before=created_before, + mixer_region=mixer_region, + tags=tags, + subaccount=subaccount, + detected_issues=detected_issues, + end_reason=end_reason, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceInstance]: + """ + Asynchronously lists ConferenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str conference_sid: The SID of the conference. + :param str friendly_name: Custom label for the conference resource, up to 64 characters. + :param str status: Conference status. + :param str created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param str created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param str mixer_region: Twilio region where the conference media was mixed. + :param str tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param str subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param str detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param str end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + conference_sid=conference_sid, + friendly_name=friendly_name, + status=status, + created_after=created_after, + created_before=created_before, + mixer_region=mixer_region, + tags=tags, + subaccount=subaccount, + detected_issues=detected_issues, + end_reason=end_reason, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferencePage: + """ + Retrieve a single page of ConferenceInstance records from the API. + Request is executed immediately + + :param conference_sid: The SID of the conference. + :param friendly_name: Custom label for the conference resource, up to 64 characters. + :param status: Conference status. + :param created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param mixer_region: Twilio region where the conference media was mixed. + :param tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceInstance + """ + data = values.of( + { + "ConferenceSid": conference_sid, + "FriendlyName": friendly_name, + "Status": status, + "CreatedAfter": created_after, + "CreatedBefore": created_before, + "MixerRegion": mixer_region, + "Tags": tags, + "Subaccount": subaccount, + "DetectedIssues": detected_issues, + "EndReason": end_reason, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConferencePage(self._version, response) + + async def page_async( + self, + conference_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + created_after: Union[str, object] = values.unset, + created_before: Union[str, object] = values.unset, + mixer_region: Union[str, object] = values.unset, + tags: Union[str, object] = values.unset, + subaccount: Union[str, object] = values.unset, + detected_issues: Union[str, object] = values.unset, + end_reason: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferencePage: + """ + Asynchronously retrieve a single page of ConferenceInstance records from the API. + Request is executed immediately + + :param conference_sid: The SID of the conference. + :param friendly_name: Custom label for the conference resource, up to 64 characters. + :param status: Conference status. + :param created_after: Conferences created after the provided timestamp specified in ISO 8601 format + :param created_before: Conferences created before the provided timestamp specified in ISO 8601 format. + :param mixer_region: Twilio region where the conference media was mixed. + :param tags: Tags applied by Twilio for common potential configuration, quality, or performance issues. + :param subaccount: Account SID for the subaccount whose resources you wish to retrieve. + :param detected_issues: Potential configuration, behavior, or performance issues detected during the conference. + :param end_reason: Conference end reason; e.g. last participant left, modified by API, etc. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceInstance + """ + data = values.of( + { + "ConferenceSid": conference_sid, + "FriendlyName": friendly_name, + "Status": status, + "CreatedAfter": created_after, + "CreatedBefore": created_before, + "MixerRegion": mixer_region, + "Tags": tags, + "Subaccount": subaccount, + "DetectedIssues": detected_issues, + "EndReason": end_reason, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConferencePage(self._version, response) + + def get_page(self, target_url: str) -> ConferencePage: + """ + Retrieve a specific page of ConferenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConferencePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ConferencePage: + """ + Asynchronously retrieve a specific page of ConferenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConferencePage(self._version, response) + + def get(self, conference_sid: str) -> ConferenceContext: + """ + Constructs a ConferenceContext + + :param conference_sid: The unique SID identifier of the Conference. + """ + return ConferenceContext(self._version, conference_sid=conference_sid) + + def __call__(self, conference_sid: str) -> ConferenceContext: + """ + Constructs a ConferenceContext + + :param conference_sid: The unique SID identifier of the Conference. + """ + return ConferenceContext(self._version, conference_sid=conference_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a4ace00 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/conference_participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/conference_participant.cpython-310.pyc new file mode 100644 index 0000000..94483c6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/__pycache__/conference_participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/conference_participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/conference_participant.py new file mode 100644 index 0000000..68faa69 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/conference/conference_participant.py @@ -0,0 +1,634 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ConferenceParticipantInstance(InstanceResource): + + class CallDirection(object): + INBOUND = "inbound" + OUTBOUND = "outbound" + + class CallStatus(object): + ANSWERED = "answered" + COMPLETED = "completed" + BUSY = "busy" + FAIL = "fail" + NOANSWER = "noanswer" + RINGING = "ringing" + CANCELED = "canceled" + + class CallType(object): + CARRIER = "carrier" + CLIENT = "client" + SIP = "sip" + + class JitterBufferSize(object): + LARGE = "large" + SMALL = "small" + MEDIUM = "medium" + OFF = "off" + + class ProcessingState(object): + COMPLETE = "complete" + IN_PROGRESS = "in_progress" + TIMEOUT = "timeout" + + class Region(object): + US1 = "us1" + US2 = "us2" + AU1 = "au1" + BR1 = "br1" + IE1 = "ie1" + JP1 = "jp1" + SG1 = "sg1" + DE1 = "de1" + + """ + :ivar participant_sid: SID for this participant. + :ivar label: The user-specified label of this participant. + :ivar conference_sid: The unique SID identifier of the Conference. + :ivar call_sid: Unique SID identifier of the call that generated the Participant resource. + :ivar account_sid: The unique SID identifier of the Account. + :ivar call_direction: + :ivar _from: Caller ID of the calling party. + :ivar to: Called party. + :ivar call_status: + :ivar country_code: ISO alpha-2 country code of the participant based on caller ID or called number. + :ivar is_moderator: Boolean. Indicates whether participant had startConferenceOnEnter=true or endConferenceOnExit=true. + :ivar join_time: ISO 8601 timestamp of participant join event. + :ivar leave_time: ISO 8601 timestamp of participant leave event. + :ivar duration_seconds: Participant durations in seconds. + :ivar outbound_queue_length: Add Participant API only. Estimated time in queue at call creation. + :ivar outbound_time_in_queue: Add Participant API only. Actual time in queue in seconds. + :ivar jitter_buffer_size: + :ivar is_coach: Boolean. Indicated whether participant was a coach. + :ivar coached_participants: Call SIDs coached by this participant. + :ivar participant_region: + :ivar conference_region: + :ivar call_type: + :ivar processing_state: + :ivar properties: Participant properties and metadata. + :ivar events: Object containing information of actions taken by participants. Contains a dictionary of URL links to nested resources of this Conference Participant. + :ivar metrics: Object. Contains participant call quality metrics. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + conference_sid: str, + participant_sid: Optional[str] = None, + ): + super().__init__(version) + + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.label: Optional[str] = payload.get("label") + self.conference_sid: Optional[str] = payload.get("conference_sid") + self.call_sid: Optional[str] = payload.get("call_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.call_direction: Optional["ConferenceParticipantInstance.CallDirection"] = ( + payload.get("call_direction") + ) + self._from: Optional[str] = payload.get("from") + self.to: Optional[str] = payload.get("to") + self.call_status: Optional["ConferenceParticipantInstance.CallStatus"] = ( + payload.get("call_status") + ) + self.country_code: Optional[str] = payload.get("country_code") + self.is_moderator: Optional[bool] = payload.get("is_moderator") + self.join_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("join_time") + ) + self.leave_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("leave_time") + ) + self.duration_seconds: Optional[int] = deserialize.integer( + payload.get("duration_seconds") + ) + self.outbound_queue_length: Optional[int] = deserialize.integer( + payload.get("outbound_queue_length") + ) + self.outbound_time_in_queue: Optional[int] = deserialize.integer( + payload.get("outbound_time_in_queue") + ) + self.jitter_buffer_size: Optional[ + "ConferenceParticipantInstance.JitterBufferSize" + ] = payload.get("jitter_buffer_size") + self.is_coach: Optional[bool] = payload.get("is_coach") + self.coached_participants: Optional[List[str]] = payload.get( + "coached_participants" + ) + self.participant_region: Optional["ConferenceParticipantInstance.Region"] = ( + payload.get("participant_region") + ) + self.conference_region: Optional["ConferenceParticipantInstance.Region"] = ( + payload.get("conference_region") + ) + self.call_type: Optional["ConferenceParticipantInstance.CallType"] = ( + payload.get("call_type") + ) + self.processing_state: Optional[ + "ConferenceParticipantInstance.ProcessingState" + ] = payload.get("processing_state") + self.properties: Optional[Dict[str, object]] = payload.get("properties") + self.events: Optional[Dict[str, object]] = payload.get("events") + self.metrics: Optional[Dict[str, object]] = payload.get("metrics") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "conference_sid": conference_sid, + "participant_sid": participant_sid or self.participant_sid, + } + self._context: Optional[ConferenceParticipantContext] = None + + @property + def _proxy(self) -> "ConferenceParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConferenceParticipantContext for this ConferenceParticipantInstance + """ + if self._context is None: + self._context = ConferenceParticipantContext( + self._version, + conference_sid=self._solution["conference_sid"], + participant_sid=self._solution["participant_sid"], + ) + return self._context + + def fetch( + self, + events: Union[str, object] = values.unset, + metrics: Union[str, object] = values.unset, + ) -> "ConferenceParticipantInstance": + """ + Fetch the ConferenceParticipantInstance + + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param metrics: Object. Contains participant call quality metrics. + + :returns: The fetched ConferenceParticipantInstance + """ + return self._proxy.fetch( + events=events, + metrics=metrics, + ) + + async def fetch_async( + self, + events: Union[str, object] = values.unset, + metrics: Union[str, object] = values.unset, + ) -> "ConferenceParticipantInstance": + """ + Asynchronous coroutine to fetch the ConferenceParticipantInstance + + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param metrics: Object. Contains participant call quality metrics. + + :returns: The fetched ConferenceParticipantInstance + """ + return await self._proxy.fetch_async( + events=events, + metrics=metrics, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferenceParticipantContext(InstanceContext): + + def __init__(self, version: Version, conference_sid: str, participant_sid: str): + """ + Initialize the ConferenceParticipantContext + + :param version: Version that contains the resource + :param conference_sid: The unique SID identifier of the Conference. + :param participant_sid: The unique SID identifier of the Participant. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conference_sid": conference_sid, + "participant_sid": participant_sid, + } + self._uri = ( + "/Conferences/{conference_sid}/Participants/{participant_sid}".format( + **self._solution + ) + ) + + def fetch( + self, + events: Union[str, object] = values.unset, + metrics: Union[str, object] = values.unset, + ) -> ConferenceParticipantInstance: + """ + Fetch the ConferenceParticipantInstance + + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param metrics: Object. Contains participant call quality metrics. + + :returns: The fetched ConferenceParticipantInstance + """ + + data = values.of( + { + "Events": events, + "Metrics": metrics, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return ConferenceParticipantInstance( + self._version, + payload, + conference_sid=self._solution["conference_sid"], + participant_sid=self._solution["participant_sid"], + ) + + async def fetch_async( + self, + events: Union[str, object] = values.unset, + metrics: Union[str, object] = values.unset, + ) -> ConferenceParticipantInstance: + """ + Asynchronous coroutine to fetch the ConferenceParticipantInstance + + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param metrics: Object. Contains participant call quality metrics. + + :returns: The fetched ConferenceParticipantInstance + """ + + data = values.of( + { + "Events": events, + "Metrics": metrics, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return ConferenceParticipantInstance( + self._version, + payload, + conference_sid=self._solution["conference_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConferenceParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConferenceParticipantInstance: + """ + Build an instance of ConferenceParticipantInstance + + :param payload: Payload response from the API + """ + return ConferenceParticipantInstance( + self._version, payload, conference_sid=self._solution["conference_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConferenceParticipantList(ListResource): + + def __init__(self, version: Version, conference_sid: str): + """ + Initialize the ConferenceParticipantList + + :param version: Version that contains the resource + :param conference_sid: The unique SID identifier of the Conference. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "conference_sid": conference_sid, + } + self._uri = "/Conferences/{conference_sid}/Participants".format( + **self._solution + ) + + def stream( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConferenceParticipantInstance]: + """ + Streams ConferenceParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str participant_sid: The unique SID identifier of the Participant. + :param str label: User-specified label for a participant. + :param str events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + participant_sid=participant_sid, + label=label, + events=events, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConferenceParticipantInstance]: + """ + Asynchronously streams ConferenceParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str participant_sid: The unique SID identifier of the Participant. + :param str label: User-specified label for a participant. + :param str events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + participant_sid=participant_sid, + label=label, + events=events, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceParticipantInstance]: + """ + Lists ConferenceParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str participant_sid: The unique SID identifier of the Participant. + :param str label: User-specified label for a participant. + :param str events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + participant_sid=participant_sid, + label=label, + events=events, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConferenceParticipantInstance]: + """ + Asynchronously lists ConferenceParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str participant_sid: The unique SID identifier of the Participant. + :param str label: User-specified label for a participant. + :param str events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + participant_sid=participant_sid, + label=label, + events=events, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferenceParticipantPage: + """ + Retrieve a single page of ConferenceParticipantInstance records from the API. + Request is executed immediately + + :param participant_sid: The unique SID identifier of the Participant. + :param label: User-specified label for a participant. + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceParticipantInstance + """ + data = values.of( + { + "ParticipantSid": participant_sid, + "Label": label, + "Events": events, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConferenceParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + participant_sid: Union[str, object] = values.unset, + label: Union[str, object] = values.unset, + events: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConferenceParticipantPage: + """ + Asynchronously retrieve a single page of ConferenceParticipantInstance records from the API. + Request is executed immediately + + :param participant_sid: The unique SID identifier of the Participant. + :param label: User-specified label for a participant. + :param events: Conference events generated by application or participant activity; e.g. `hold`, `mute`, etc. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConferenceParticipantInstance + """ + data = values.of( + { + "ParticipantSid": participant_sid, + "Label": label, + "Events": events, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConferenceParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConferenceParticipantPage: + """ + Retrieve a specific page of ConferenceParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConferenceParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConferenceParticipantPage: + """ + Asynchronously retrieve a specific page of ConferenceParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConferenceParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConferenceParticipantPage(self._version, response, self._solution) + + def get(self, participant_sid: str) -> ConferenceParticipantContext: + """ + Constructs a ConferenceParticipantContext + + :param participant_sid: The unique SID identifier of the Participant. + """ + return ConferenceParticipantContext( + self._version, + conference_sid=self._solution["conference_sid"], + participant_sid=participant_sid, + ) + + def __call__(self, participant_sid: str) -> ConferenceParticipantContext: + """ + Constructs a ConferenceParticipantContext + + :param participant_sid: The unique SID identifier of the Participant. + """ + return ConferenceParticipantContext( + self._version, + conference_sid=self._solution["conference_sid"], + participant_sid=participant_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__init__.py new file mode 100644 index 0000000..7361018 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__init__.py @@ -0,0 +1,635 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.insights.v1.room.participant import ParticipantList + + +class RoomInstance(InstanceResource): + + class Codec(object): + VP8 = "VP8" + H264 = "H264" + VP9 = "VP9" + + class CreatedMethod(object): + SDK = "sdk" + AD_HOC = "ad_hoc" + API = "api" + + class EdgeLocation(object): + ASHBURN = "ashburn" + DUBLIN = "dublin" + FRANKFURT = "frankfurt" + SINGAPORE = "singapore" + SYDNEY = "sydney" + SAO_PAULO = "sao_paulo" + ROAMING = "roaming" + UMATILLA = "umatilla" + TOKYO = "tokyo" + + class EndReason(object): + ROOM_ENDED_VIA_API = "room_ended_via_api" + TIMEOUT = "timeout" + + class ProcessingState(object): + COMPLETE = "complete" + IN_PROGRESS = "in_progress" + + class RoomStatus(object): + IN_PROGRESS = "in_progress" + COMPLETED = "completed" + + class RoomType(object): + GO = "go" + PEER_TO_PEER = "peer_to_peer" + GROUP = "group" + GROUP_SMALL = "group_small" + + class TwilioRealm(object): + US1 = "us1" + US2 = "us2" + AU1 = "au1" + BR1 = "br1" + IE1 = "ie1" + JP1 = "jp1" + SG1 = "sg1" + IN1 = "in1" + DE1 = "de1" + GLL = "gll" + + """ + :ivar account_sid: Account SID associated with this room. + :ivar room_sid: Unique identifier for the room. + :ivar room_name: Room friendly name. + :ivar create_time: Creation time of the room. + :ivar end_time: End time for the room. + :ivar room_type: + :ivar room_status: + :ivar status_callback: Webhook provided for status callbacks. + :ivar status_callback_method: HTTP method provided for status callback URL. + :ivar created_method: + :ivar end_reason: + :ivar max_participants: Max number of total participants allowed by the application settings. + :ivar unique_participants: Number of participants. May include duplicate identities for participants who left and rejoined. + :ivar unique_participant_identities: Unique number of participant identities. + :ivar concurrent_participants: Actual number of concurrent participants. + :ivar max_concurrent_participants: Maximum number of participants allowed in the room at the same time allowed by the application settings. + :ivar codecs: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :ivar media_region: + :ivar duration_sec: Total room duration from create time to end time. + :ivar total_participant_duration_sec: Combined amount of participant time in the room. + :ivar total_recording_duration_sec: Combined amount of recorded seconds for participants in the room. + :ivar processing_state: + :ivar recording_enabled: Boolean indicating if recording is enabled for the room. + :ivar edge_location: + :ivar url: URL for the room resource. + :ivar links: Room subresources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], room_sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.room_name: Optional[str] = payload.get("room_name") + self.create_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("create_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.room_type: Optional["RoomInstance.RoomType"] = payload.get("room_type") + self.room_status: Optional["RoomInstance.RoomStatus"] = payload.get( + "room_status" + ) + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.created_method: Optional["RoomInstance.CreatedMethod"] = payload.get( + "created_method" + ) + self.end_reason: Optional["RoomInstance.EndReason"] = payload.get("end_reason") + self.max_participants: Optional[int] = deserialize.integer( + payload.get("max_participants") + ) + self.unique_participants: Optional[int] = deserialize.integer( + payload.get("unique_participants") + ) + self.unique_participant_identities: Optional[int] = deserialize.integer( + payload.get("unique_participant_identities") + ) + self.concurrent_participants: Optional[int] = deserialize.integer( + payload.get("concurrent_participants") + ) + self.max_concurrent_participants: Optional[int] = deserialize.integer( + payload.get("max_concurrent_participants") + ) + self.codecs: Optional[List["RoomInstance.Codec"]] = payload.get("codecs") + self.media_region: Optional["RoomInstance.TwilioRealm"] = payload.get( + "media_region" + ) + self.duration_sec: Optional[int] = payload.get("duration_sec") + self.total_participant_duration_sec: Optional[int] = payload.get( + "total_participant_duration_sec" + ) + self.total_recording_duration_sec: Optional[int] = payload.get( + "total_recording_duration_sec" + ) + self.processing_state: Optional["RoomInstance.ProcessingState"] = payload.get( + "processing_state" + ) + self.recording_enabled: Optional[bool] = payload.get("recording_enabled") + self.edge_location: Optional["RoomInstance.EdgeLocation"] = payload.get( + "edge_location" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "room_sid": room_sid or self.room_sid, + } + self._context: Optional[RoomContext] = None + + @property + def _proxy(self) -> "RoomContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoomContext for this RoomInstance + """ + if self._context is None: + self._context = RoomContext( + self._version, + room_sid=self._solution["room_sid"], + ) + return self._context + + def fetch(self) -> "RoomInstance": + """ + Fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoomInstance": + """ + Asynchronous coroutine to fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + return await self._proxy.fetch_async() + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str): + """ + Initialize the RoomContext + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + } + self._uri = "/Video/Rooms/{room_sid}".format(**self._solution) + + self._participants: Optional[ParticipantList] = None + + def fetch(self) -> RoomInstance: + """ + Fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoomInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + ) + + async def fetch_async(self) -> RoomInstance: + """ + Asynchronous coroutine to fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoomInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + ) + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["room_sid"], + ) + return self._participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoomInstance: + """ + Build an instance of RoomInstance + + :param payload: Payload response from the API + """ + return RoomInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoomList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RoomList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Video/Rooms" + + def stream( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoomInstance]: + """ + Streams RoomInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["RoomInstance.RoomType"] room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param List["RoomInstance.Codec"] codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param str room_name: Room friendly name. + :param datetime created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param datetime created_before: Only read rooms that started before this ISO 8601 timestamp. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + room_type=room_type, + codec=codec, + room_name=room_name, + created_after=created_after, + created_before=created_before, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoomInstance]: + """ + Asynchronously streams RoomInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["RoomInstance.RoomType"] room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param List["RoomInstance.Codec"] codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param str room_name: Room friendly name. + :param datetime created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param datetime created_before: Only read rooms that started before this ISO 8601 timestamp. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + room_type=room_type, + codec=codec, + room_name=room_name, + created_after=created_after, + created_before=created_before, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomInstance]: + """ + Lists RoomInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["RoomInstance.RoomType"] room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param List["RoomInstance.Codec"] codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param str room_name: Room friendly name. + :param datetime created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param datetime created_before: Only read rooms that started before this ISO 8601 timestamp. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + room_type=room_type, + codec=codec, + room_name=room_name, + created_after=created_after, + created_before=created_before, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomInstance]: + """ + Asynchronously lists RoomInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["RoomInstance.RoomType"] room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param List["RoomInstance.Codec"] codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param str room_name: Room friendly name. + :param datetime created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param datetime created_before: Only read rooms that started before this ISO 8601 timestamp. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + room_type=room_type, + codec=codec, + room_name=room_name, + created_after=created_after, + created_before=created_before, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomPage: + """ + Retrieve a single page of RoomInstance records from the API. + Request is executed immediately + + :param room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param room_name: Room friendly name. + :param created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param created_before: Only read rooms that started before this ISO 8601 timestamp. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomInstance + """ + data = values.of( + { + "RoomType": serialize.map(room_type, lambda e: e), + "Codec": serialize.map(codec, lambda e: e), + "RoomName": room_name, + "CreatedAfter": serialize.iso8601_datetime(created_after), + "CreatedBefore": serialize.iso8601_datetime(created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RoomPage(self._version, response) + + async def page_async( + self, + room_type: Union[List["RoomInstance.RoomType"], object] = values.unset, + codec: Union[List["RoomInstance.Codec"], object] = values.unset, + room_name: Union[str, object] = values.unset, + created_after: Union[datetime, object] = values.unset, + created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomPage: + """ + Asynchronously retrieve a single page of RoomInstance records from the API. + Request is executed immediately + + :param room_type: Type of room. Can be `go`, `peer_to_peer`, `group`, or `group_small`. + :param codec: Codecs used by participants in the room. Can be `VP8`, `H264`, or `VP9`. + :param room_name: Room friendly name. + :param created_after: Only read rooms that started on or after this ISO 8601 timestamp. + :param created_before: Only read rooms that started before this ISO 8601 timestamp. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomInstance + """ + data = values.of( + { + "RoomType": serialize.map(room_type, lambda e: e), + "Codec": serialize.map(codec, lambda e: e), + "RoomName": room_name, + "CreatedAfter": serialize.iso8601_datetime(created_after), + "CreatedBefore": serialize.iso8601_datetime(created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RoomPage(self._version, response) + + def get_page(self, target_url: str) -> RoomPage: + """ + Retrieve a specific page of RoomInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RoomPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RoomPage: + """ + Asynchronously retrieve a specific page of RoomInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RoomPage(self._version, response) + + def get(self, room_sid: str) -> RoomContext: + """ + Constructs a RoomContext + + :param room_sid: The SID of the Room resource. + """ + return RoomContext(self._version, room_sid=room_sid) + + def __call__(self, room_sid: str) -> RoomContext: + """ + Constructs a RoomContext + + :param room_sid: The SID of the Room resource. + """ + return RoomContext(self._version, room_sid=room_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..032a974 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/participant.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/participant.cpython-310.pyc new file mode 100644 index 0000000..23ce20e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/__pycache__/participant.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/participant.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/participant.py new file mode 100644 index 0000000..0f6617f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/room/participant.py @@ -0,0 +1,487 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ParticipantInstance(InstanceResource): + + class Codec(object): + VP8 = "VP8" + H264 = "H264" + VP9 = "VP9" + + class EdgeLocation(object): + ASHBURN = "ashburn" + DUBLIN = "dublin" + FRANKFURT = "frankfurt" + SINGAPORE = "singapore" + SYDNEY = "sydney" + SAO_PAULO = "sao_paulo" + ROAMING = "roaming" + UMATILLA = "umatilla" + TOKYO = "tokyo" + + class RoomStatus(object): + IN_PROGRESS = "in_progress" + COMPLETED = "completed" + + class TwilioRealm(object): + US1 = "us1" + US2 = "us2" + AU1 = "au1" + BR1 = "br1" + IE1 = "ie1" + JP1 = "jp1" + SG1 = "sg1" + IN1 = "in1" + DE1 = "de1" + GLL = "gll" + + """ + :ivar participant_sid: Unique identifier for the participant. + :ivar participant_identity: The application-defined string that uniquely identifies the participant within a Room. + :ivar join_time: When the participant joined the room. + :ivar leave_time: When the participant left the room. + :ivar duration_sec: Amount of time in seconds the participant was in the room. + :ivar account_sid: Account SID associated with the room. + :ivar room_sid: Unique identifier for the room. + :ivar status: + :ivar codecs: Codecs detected from the participant. Can be `VP8`, `H264`, or `VP9`. + :ivar end_reason: Reason the participant left the room. See [the list of possible values here](https://www.twilio.com/docs/video/troubleshooting/video-log-analyzer-api#end_reason). + :ivar error_code: Errors encountered by the participant. + :ivar error_code_url: Twilio error code dictionary link. + :ivar media_region: + :ivar properties: Object containing information about the participant's data from the room. See [below](https://www.twilio.com/docs/video/troubleshooting/video-log-analyzer-api#properties) for more information. + :ivar edge_location: + :ivar publisher_info: Object containing information about the SDK name and version. See [below](https://www.twilio.com/docs/video/troubleshooting/video-log-analyzer-api#publisher_info) for more information. + :ivar url: URL of the participant resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + participant_sid: Optional[str] = None, + ): + super().__init__(version) + + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.participant_identity: Optional[str] = payload.get("participant_identity") + self.join_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("join_time") + ) + self.leave_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("leave_time") + ) + self.duration_sec: Optional[int] = payload.get("duration_sec") + self.account_sid: Optional[str] = payload.get("account_sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.status: Optional["ParticipantInstance.RoomStatus"] = payload.get("status") + self.codecs: Optional[List["ParticipantInstance.Codec"]] = payload.get("codecs") + self.end_reason: Optional[str] = payload.get("end_reason") + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.error_code_url: Optional[str] = payload.get("error_code_url") + self.media_region: Optional["ParticipantInstance.TwilioRealm"] = payload.get( + "media_region" + ) + self.properties: Optional[Dict[str, object]] = payload.get("properties") + self.edge_location: Optional["ParticipantInstance.EdgeLocation"] = payload.get( + "edge_location" + ) + self.publisher_info: Optional[Dict[str, object]] = payload.get("publisher_info") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid or self.participant_sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + return self._context + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, participant_sid: str): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource. + :param participant_sid: The SID of the Participant resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + } + self._uri = "/Video/Rooms/{room_sid}/Participants/{participant_sid}".format( + **self._solution + ) + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, room_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + } + self._uri = "/Video/Rooms/{room_sid}/Participants".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, participant_sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param participant_sid: The SID of the Participant resource. + """ + return ParticipantContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=participant_sid, + ) + + def __call__(self, participant_sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param participant_sid: The SID of the Participant resource. + """ + return ParticipantContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=participant_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/setting.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/setting.py new file mode 100644 index 0000000..c385fc4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/insights/v1/setting.py @@ -0,0 +1,300 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Insights + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SettingInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar advanced_features: A boolean flag indicating whether Advanced Features for Voice Insights are enabled. + :ivar voice_trace: A boolean flag indicating whether Voice Trace is enabled. + :ivar url: The URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.advanced_features: Optional[bool] = payload.get("advanced_features") + self.voice_trace: Optional[bool] = payload.get("voice_trace") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[SettingContext] = None + + @property + def _proxy(self) -> "SettingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SettingContext for this SettingInstance + """ + if self._context is None: + self._context = SettingContext( + self._version, + ) + return self._context + + def fetch( + self, subaccount_sid: Union[str, object] = values.unset + ) -> "SettingInstance": + """ + Fetch the SettingInstance + + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The fetched SettingInstance + """ + return self._proxy.fetch( + subaccount_sid=subaccount_sid, + ) + + async def fetch_async( + self, subaccount_sid: Union[str, object] = values.unset + ) -> "SettingInstance": + """ + Asynchronous coroutine to fetch the SettingInstance + + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The fetched SettingInstance + """ + return await self._proxy.fetch_async( + subaccount_sid=subaccount_sid, + ) + + def update( + self, + advanced_features: Union[bool, object] = values.unset, + voice_trace: Union[bool, object] = values.unset, + subaccount_sid: Union[str, object] = values.unset, + ) -> "SettingInstance": + """ + Update the SettingInstance + + :param advanced_features: A boolean flag to enable Advanced Features for Voice Insights. + :param voice_trace: A boolean flag to enable Voice Trace. + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The updated SettingInstance + """ + return self._proxy.update( + advanced_features=advanced_features, + voice_trace=voice_trace, + subaccount_sid=subaccount_sid, + ) + + async def update_async( + self, + advanced_features: Union[bool, object] = values.unset, + voice_trace: Union[bool, object] = values.unset, + subaccount_sid: Union[str, object] = values.unset, + ) -> "SettingInstance": + """ + Asynchronous coroutine to update the SettingInstance + + :param advanced_features: A boolean flag to enable Advanced Features for Voice Insights. + :param voice_trace: A boolean flag to enable Voice Trace. + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The updated SettingInstance + """ + return await self._proxy.update_async( + advanced_features=advanced_features, + voice_trace=voice_trace, + subaccount_sid=subaccount_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SettingContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the SettingContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Voice/Settings" + + def fetch( + self, subaccount_sid: Union[str, object] = values.unset + ) -> SettingInstance: + """ + Fetch the SettingInstance + + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The fetched SettingInstance + """ + + data = values.of( + { + "SubaccountSid": subaccount_sid, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return SettingInstance( + self._version, + payload, + ) + + async def fetch_async( + self, subaccount_sid: Union[str, object] = values.unset + ) -> SettingInstance: + """ + Asynchronous coroutine to fetch the SettingInstance + + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The fetched SettingInstance + """ + + data = values.of( + { + "SubaccountSid": subaccount_sid, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return SettingInstance( + self._version, + payload, + ) + + def update( + self, + advanced_features: Union[bool, object] = values.unset, + voice_trace: Union[bool, object] = values.unset, + subaccount_sid: Union[str, object] = values.unset, + ) -> SettingInstance: + """ + Update the SettingInstance + + :param advanced_features: A boolean flag to enable Advanced Features for Voice Insights. + :param voice_trace: A boolean flag to enable Voice Trace. + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The updated SettingInstance + """ + data = values.of( + { + "AdvancedFeatures": serialize.boolean_to_string(advanced_features), + "VoiceTrace": serialize.boolean_to_string(voice_trace), + "SubaccountSid": subaccount_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SettingInstance(self._version, payload) + + async def update_async( + self, + advanced_features: Union[bool, object] = values.unset, + voice_trace: Union[bool, object] = values.unset, + subaccount_sid: Union[str, object] = values.unset, + ) -> SettingInstance: + """ + Asynchronous coroutine to update the SettingInstance + + :param advanced_features: A boolean flag to enable Advanced Features for Voice Insights. + :param voice_trace: A boolean flag to enable Voice Trace. + :param subaccount_sid: The unique SID identifier of the Subaccount. + + :returns: The updated SettingInstance + """ + data = values.of( + { + "AdvancedFeatures": serialize.boolean_to_string(advanced_features), + "VoiceTrace": serialize.boolean_to_string(voice_trace), + "SubaccountSid": subaccount_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SettingInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SettingList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SettingList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> SettingContext: + """ + Constructs a SettingContext + + """ + return SettingContext(self._version) + + def __call__(self) -> SettingContext: + """ + Constructs a SettingContext + + """ + return SettingContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/IntelligenceBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/IntelligenceBase.py new file mode 100644 index 0000000..0546bc1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/IntelligenceBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.intelligence.v2 import V2 + + +class IntelligenceBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Intelligence Domain + + :returns: Domain for Intelligence + """ + super().__init__(twilio, "https://intelligence.twilio.com") + self._v2: Optional[V2] = None + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Intelligence + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__init__.py new file mode 100644 index 0000000..6d565b4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__init__.py @@ -0,0 +1,13 @@ +from twilio.rest.intelligence.IntelligenceBase import IntelligenceBase +from twilio.rest.intelligence.v2.service import ServiceList +from twilio.rest.intelligence.v2.transcript import TranscriptList + + +class Intelligence(IntelligenceBase): + @property + def transcripts(self) -> TranscriptList: + return self.v2.transcripts + + @property + def services(self) -> ServiceList: + return self.v2.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/IntelligenceBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/IntelligenceBase.cpython-310.pyc new file mode 100644 index 0000000..2c564e4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/IntelligenceBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..402f6bf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__init__.py new file mode 100644 index 0000000..e20b125 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.intelligence.v2.service import ServiceList +from twilio.rest.intelligence.v2.transcript import TranscriptList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Intelligence + + :param domain: The Twilio.intelligence domain + """ + super().__init__(domain, "v2") + self._services: Optional[ServiceList] = None + self._transcripts: Optional[TranscriptList] = None + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + @property + def transcripts(self) -> TranscriptList: + if self._transcripts is None: + self._transcripts = TranscriptList(self) + return self._transcripts + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0096eb3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/service.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/service.cpython-310.pyc new file mode 100644 index 0000000..8ea5417 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/__pycache__/service.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/service.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/service.py new file mode 100644 index 0000000..d983be5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/service.py @@ -0,0 +1,761 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ServiceInstance(InstanceResource): + + class HttpMethod(object): + GET = "GET" + POST = "POST" + NULL = "NULL" + + """ + :ivar account_sid: The unique SID identifier of the Account the Service belongs to. + :ivar auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :ivar media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :ivar auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :ivar data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :ivar date_created: The date that this Service was created, given in ISO 8601 format. + :ivar date_updated: The date that this Service was updated, given in ISO 8601 format. + :ivar friendly_name: A human readable description of this resource, up to 64 characters. + :ivar language_code: The default language code of the audio. + :ivar sid: A 34 character string that uniquely identifies this Service. + :ivar unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :ivar url: The URL of this resource. + :ivar webhook_url: The URL Twilio will request when executing the Webhook. + :ivar webhook_http_method: + :ivar version: The version number of this Service. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.auto_redaction: Optional[bool] = payload.get("auto_redaction") + self.media_redaction: Optional[bool] = payload.get("media_redaction") + self.auto_transcribe: Optional[bool] = payload.get("auto_transcribe") + self.data_logging: Optional[bool] = payload.get("data_logging") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.language_code: Optional[str] = payload.get("language_code") + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.url: Optional[str] = payload.get("url") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhook_http_method: Optional["ServiceInstance.HttpMethod"] = payload.get( + "webhook_http_method" + ) + self.version: Optional[int] = deserialize.integer(payload.get("version")) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param if_match: The If-Match HTTP request header + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + if_match=if_match, + auto_transcribe=auto_transcribe, + data_logging=data_logging, + friendly_name=friendly_name, + language_code=language_code, + unique_name=unique_name, + auto_redaction=auto_redaction, + media_redaction=media_redaction, + webhook_url=webhook_url, + webhook_http_method=webhook_http_method, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param if_match: The If-Match HTTP request header + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + if_match=if_match, + auto_transcribe=auto_transcribe, + data_logging=data_logging, + friendly_name=friendly_name, + language_code=language_code, + unique_name=unique_name, + auto_redaction=auto_redaction, + media_redaction=media_redaction, + webhook_url=webhook_url, + webhook_http_method=webhook_http_method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this Service. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param if_match: The If-Match HTTP request header + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "AutoTranscribe": serialize.boolean_to_string(auto_transcribe), + "DataLogging": serialize.boolean_to_string(data_logging), + "FriendlyName": friendly_name, + "LanguageCode": language_code, + "UniqueName": unique_name, + "AutoRedaction": serialize.boolean_to_string(auto_redaction), + "MediaRedaction": serialize.boolean_to_string(media_redaction), + "WebhookUrl": webhook_url, + "WebhookHttpMethod": webhook_http_method, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param if_match: The If-Match HTTP request header + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "AutoTranscribe": serialize.boolean_to_string(auto_transcribe), + "DataLogging": serialize.boolean_to_string(data_logging), + "FriendlyName": friendly_name, + "LanguageCode": language_code, + "UniqueName": unique_name, + "AutoRedaction": serialize.boolean_to_string(auto_redaction), + "MediaRedaction": serialize.boolean_to_string(media_redaction), + "WebhookUrl": webhook_url, + "WebhookHttpMethod": webhook_http_method, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + unique_name: str, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "AutoTranscribe": serialize.boolean_to_string(auto_transcribe), + "DataLogging": serialize.boolean_to_string(data_logging), + "FriendlyName": friendly_name, + "LanguageCode": language_code, + "AutoRedaction": serialize.boolean_to_string(auto_redaction), + "MediaRedaction": serialize.boolean_to_string(media_redaction), + "WebhookUrl": webhook_url, + "WebhookHttpMethod": webhook_http_method, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + unique_name: str, + auto_transcribe: Union[bool, object] = values.unset, + data_logging: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + auto_redaction: Union[bool, object] = values.unset, + media_redaction: Union[bool, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + webhook_http_method: Union["ServiceInstance.HttpMethod", object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Service, assigned by the developer, to be optionally used in addition to SID. + :param auto_transcribe: Instructs the Speech Recognition service to automatically transcribe all recordings made on the account. + :param data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param language_code: The default language code of the audio. + :param auto_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts made on this service. + :param media_redaction: Instructs the Speech Recognition service to automatically redact PII from all transcripts media made on this service. The auto_redaction flag must be enabled, results in error otherwise. + :param webhook_url: The URL Twilio will request when executing the Webhook. + :param webhook_http_method: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "AutoTranscribe": serialize.boolean_to_string(auto_transcribe), + "DataLogging": serialize.boolean_to_string(data_logging), + "FriendlyName": friendly_name, + "LanguageCode": language_code, + "AutoRedaction": serialize.boolean_to_string(auto_redaction), + "MediaRedaction": serialize.boolean_to_string(media_redaction), + "WebhookUrl": webhook_url, + "WebhookHttpMethod": webhook_http_method, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: A 34 character string that uniquely identifies this Service. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: A 34 character string that uniquely identifies this Service. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__init__.py new file mode 100644 index 0000000..30b97f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__init__.py @@ -0,0 +1,753 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.intelligence.v2.transcript.media import MediaList +from twilio.rest.intelligence.v2.transcript.operator_result import OperatorResultList +from twilio.rest.intelligence.v2.transcript.sentence import SentenceList + + +class TranscriptInstance(InstanceResource): + + class Status(object): + QUEUED = "queued" + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + FAILED = "failed" + CANCELED = "canceled" + + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar sid: A 34 character string that uniquely identifies this Transcript. + :ivar date_created: The date that this Transcript was created, given in ISO 8601 format. + :ivar date_updated: The date that this Transcript was updated, given in ISO 8601 format. + :ivar status: + :ivar channel: Media Channel describing Transcript Source and Participant Mapping + :ivar data_logging: Data logging allows Twilio to improve the quality of the speech recognition & language understanding services through using customer data to refine, fine tune and evaluate machine learning models. Note: Data logging cannot be activated via API, only via www.twilio.com, as it requires additional consent. + :ivar language_code: The default language code of the audio. + :ivar customer_key: + :ivar media_start_time: The date that this Transcript's media was started, given in ISO 8601 format. + :ivar duration: The duration of this Transcript's source + :ivar url: The URL of this resource. + :ivar redaction: If the transcript has been redacted, a redacted alternative of the transcript will be available. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.sid: Optional[str] = payload.get("sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.status: Optional["TranscriptInstance.Status"] = payload.get("status") + self.channel: Optional[Dict[str, object]] = payload.get("channel") + self.data_logging: Optional[bool] = payload.get("data_logging") + self.language_code: Optional[str] = payload.get("language_code") + self.customer_key: Optional[str] = payload.get("customer_key") + self.media_start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("media_start_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.url: Optional[str] = payload.get("url") + self.redaction: Optional[bool] = payload.get("redaction") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[TranscriptContext] = None + + @property + def _proxy(self) -> "TranscriptContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TranscriptContext for this TranscriptInstance + """ + if self._context is None: + self._context = TranscriptContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TranscriptInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TranscriptInstance": + """ + Fetch the TranscriptInstance + + + :returns: The fetched TranscriptInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TranscriptInstance": + """ + Asynchronous coroutine to fetch the TranscriptInstance + + + :returns: The fetched TranscriptInstance + """ + return await self._proxy.fetch_async() + + @property + def media(self) -> MediaList: + """ + Access the media + """ + return self._proxy.media + + @property + def operator_results(self) -> OperatorResultList: + """ + Access the operator_results + """ + return self._proxy.operator_results + + @property + def sentences(self) -> SentenceList: + """ + Access the sentences + """ + return self._proxy.sentences + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the TranscriptContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this Transcript. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Transcripts/{sid}".format(**self._solution) + + self._media: Optional[MediaList] = None + self._operator_results: Optional[OperatorResultList] = None + self._sentences: Optional[SentenceList] = None + + def delete(self) -> bool: + """ + Deletes the TranscriptInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TranscriptInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TranscriptInstance: + """ + Fetch the TranscriptInstance + + + :returns: The fetched TranscriptInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TranscriptInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TranscriptInstance: + """ + Asynchronous coroutine to fetch the TranscriptInstance + + + :returns: The fetched TranscriptInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TranscriptInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def media(self) -> MediaList: + """ + Access the media + """ + if self._media is None: + self._media = MediaList( + self._version, + self._solution["sid"], + ) + return self._media + + @property + def operator_results(self) -> OperatorResultList: + """ + Access the operator_results + """ + if self._operator_results is None: + self._operator_results = OperatorResultList( + self._version, + self._solution["sid"], + ) + return self._operator_results + + @property + def sentences(self) -> SentenceList: + """ + Access the sentences + """ + if self._sentences is None: + self._sentences = SentenceList( + self._version, + self._solution["sid"], + ) + return self._sentences + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TranscriptPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TranscriptInstance: + """ + Build an instance of TranscriptInstance + + :param payload: Payload response from the API + """ + return TranscriptInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TranscriptList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TranscriptList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Transcripts" + + def create( + self, + service_sid: str, + channel: object, + customer_key: Union[str, object] = values.unset, + media_start_time: Union[datetime, object] = values.unset, + ) -> TranscriptInstance: + """ + Create the TranscriptInstance + + :param service_sid: The unique SID identifier of the Service. + :param channel: JSON object describing Media Channel including Source and Participants + :param customer_key: Used to store client provided metadata. Maximum of 64 double-byte UTF8 characters. + :param media_start_time: The date that this Transcript's media was started, given in ISO 8601 format. + + :returns: The created TranscriptInstance + """ + + data = values.of( + { + "ServiceSid": service_sid, + "Channel": serialize.object(channel), + "CustomerKey": customer_key, + "MediaStartTime": serialize.iso8601_datetime(media_start_time), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TranscriptInstance(self._version, payload) + + async def create_async( + self, + service_sid: str, + channel: object, + customer_key: Union[str, object] = values.unset, + media_start_time: Union[datetime, object] = values.unset, + ) -> TranscriptInstance: + """ + Asynchronously create the TranscriptInstance + + :param service_sid: The unique SID identifier of the Service. + :param channel: JSON object describing Media Channel including Source and Participants + :param customer_key: Used to store client provided metadata. Maximum of 64 double-byte UTF8 characters. + :param media_start_time: The date that this Transcript's media was started, given in ISO 8601 format. + + :returns: The created TranscriptInstance + """ + + data = values.of( + { + "ServiceSid": service_sid, + "Channel": serialize.object(channel), + "CustomerKey": customer_key, + "MediaStartTime": serialize.iso8601_datetime(media_start_time), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TranscriptInstance(self._version, payload) + + def stream( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TranscriptInstance]: + """ + Streams TranscriptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str service_sid: The unique SID identifier of the Service. + :param str before_start_time: Filter by before StartTime. + :param str after_start_time: Filter by after StartTime. + :param str before_date_created: Filter by before DateCreated. + :param str after_date_created: Filter by after DateCreated. + :param str status: Filter by status. + :param str language_code: Filter by Language Code. + :param str source_sid: Filter by SourceSid. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + service_sid=service_sid, + before_start_time=before_start_time, + after_start_time=after_start_time, + before_date_created=before_date_created, + after_date_created=after_date_created, + status=status, + language_code=language_code, + source_sid=source_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TranscriptInstance]: + """ + Asynchronously streams TranscriptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str service_sid: The unique SID identifier of the Service. + :param str before_start_time: Filter by before StartTime. + :param str after_start_time: Filter by after StartTime. + :param str before_date_created: Filter by before DateCreated. + :param str after_date_created: Filter by after DateCreated. + :param str status: Filter by status. + :param str language_code: Filter by Language Code. + :param str source_sid: Filter by SourceSid. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + service_sid=service_sid, + before_start_time=before_start_time, + after_start_time=after_start_time, + before_date_created=before_date_created, + after_date_created=after_date_created, + status=status, + language_code=language_code, + source_sid=source_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptInstance]: + """ + Lists TranscriptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str service_sid: The unique SID identifier of the Service. + :param str before_start_time: Filter by before StartTime. + :param str after_start_time: Filter by after StartTime. + :param str before_date_created: Filter by before DateCreated. + :param str after_date_created: Filter by after DateCreated. + :param str status: Filter by status. + :param str language_code: Filter by Language Code. + :param str source_sid: Filter by SourceSid. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + service_sid=service_sid, + before_start_time=before_start_time, + after_start_time=after_start_time, + before_date_created=before_date_created, + after_date_created=after_date_created, + status=status, + language_code=language_code, + source_sid=source_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TranscriptInstance]: + """ + Asynchronously lists TranscriptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str service_sid: The unique SID identifier of the Service. + :param str before_start_time: Filter by before StartTime. + :param str after_start_time: Filter by after StartTime. + :param str before_date_created: Filter by before DateCreated. + :param str after_date_created: Filter by after DateCreated. + :param str status: Filter by status. + :param str language_code: Filter by Language Code. + :param str source_sid: Filter by SourceSid. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + service_sid=service_sid, + before_start_time=before_start_time, + after_start_time=after_start_time, + before_date_created=before_date_created, + after_date_created=after_date_created, + status=status, + language_code=language_code, + source_sid=source_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptPage: + """ + Retrieve a single page of TranscriptInstance records from the API. + Request is executed immediately + + :param service_sid: The unique SID identifier of the Service. + :param before_start_time: Filter by before StartTime. + :param after_start_time: Filter by after StartTime. + :param before_date_created: Filter by before DateCreated. + :param after_date_created: Filter by after DateCreated. + :param status: Filter by status. + :param language_code: Filter by Language Code. + :param source_sid: Filter by SourceSid. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptInstance + """ + data = values.of( + { + "ServiceSid": service_sid, + "BeforeStartTime": before_start_time, + "AfterStartTime": after_start_time, + "BeforeDateCreated": before_date_created, + "AfterDateCreated": after_date_created, + "Status": status, + "LanguageCode": language_code, + "SourceSid": source_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TranscriptPage(self._version, response) + + async def page_async( + self, + service_sid: Union[str, object] = values.unset, + before_start_time: Union[str, object] = values.unset, + after_start_time: Union[str, object] = values.unset, + before_date_created: Union[str, object] = values.unset, + after_date_created: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + language_code: Union[str, object] = values.unset, + source_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TranscriptPage: + """ + Asynchronously retrieve a single page of TranscriptInstance records from the API. + Request is executed immediately + + :param service_sid: The unique SID identifier of the Service. + :param before_start_time: Filter by before StartTime. + :param after_start_time: Filter by after StartTime. + :param before_date_created: Filter by before DateCreated. + :param after_date_created: Filter by after DateCreated. + :param status: Filter by status. + :param language_code: Filter by Language Code. + :param source_sid: Filter by SourceSid. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TranscriptInstance + """ + data = values.of( + { + "ServiceSid": service_sid, + "BeforeStartTime": before_start_time, + "AfterStartTime": after_start_time, + "BeforeDateCreated": before_date_created, + "AfterDateCreated": after_date_created, + "Status": status, + "LanguageCode": language_code, + "SourceSid": source_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TranscriptPage(self._version, response) + + def get_page(self, target_url: str) -> TranscriptPage: + """ + Retrieve a specific page of TranscriptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TranscriptPage(self._version, response) + + async def get_page_async(self, target_url: str) -> TranscriptPage: + """ + Asynchronously retrieve a specific page of TranscriptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TranscriptInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TranscriptPage(self._version, response) + + def get(self, sid: str) -> TranscriptContext: + """ + Constructs a TranscriptContext + + :param sid: A 34 character string that uniquely identifies this Transcript. + """ + return TranscriptContext(self._version, sid=sid) + + def __call__(self, sid: str) -> TranscriptContext: + """ + Constructs a TranscriptContext + + :param sid: A 34 character string that uniquely identifies this Transcript. + """ + return TranscriptContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1fde63b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/media.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/media.cpython-310.pyc new file mode 100644 index 0000000..063f8c3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/media.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/operator_result.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/operator_result.cpython-310.pyc new file mode 100644 index 0000000..7f9ea97 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/operator_result.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/sentence.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/sentence.cpython-310.pyc new file mode 100644 index 0000000..ed28c4d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/__pycache__/sentence.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/media.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/media.py new file mode 100644 index 0000000..74d3ab9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/media.py @@ -0,0 +1,211 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class MediaInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Account. + :ivar media_url: Downloadable URL for media, if stored in Twilio AI. + :ivar service_sid: The unique SID identifier of the Service. + :ivar sid: The unique SID identifier of the Transcript. + :ivar url: The URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.media_url: Optional[str] = payload.get("media_url") + self.service_sid: Optional[str] = payload.get("service_sid") + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid, + } + self._context: Optional[MediaContext] = None + + @property + def _proxy(self) -> "MediaContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MediaContext for this MediaInstance + """ + if self._context is None: + self._context = MediaContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self, redacted: Union[bool, object] = values.unset) -> "MediaInstance": + """ + Fetch the MediaInstance + + :param redacted: Grant access to PII Redacted/Unredacted Media. If redaction is enabled, the default is `true` to access redacted media. + + :returns: The fetched MediaInstance + """ + return self._proxy.fetch( + redacted=redacted, + ) + + async def fetch_async( + self, redacted: Union[bool, object] = values.unset + ) -> "MediaInstance": + """ + Asynchronous coroutine to fetch the MediaInstance + + :param redacted: Grant access to PII Redacted/Unredacted Media. If redaction is enabled, the default is `true` to access redacted media. + + :returns: The fetched MediaInstance + """ + return await self._proxy.fetch_async( + redacted=redacted, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MediaContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the MediaContext + + :param version: Version that contains the resource + :param sid: The unique SID identifier of the Transcript. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Transcripts/{sid}/Media".format(**self._solution) + + def fetch(self, redacted: Union[bool, object] = values.unset) -> MediaInstance: + """ + Fetch the MediaInstance + + :param redacted: Grant access to PII Redacted/Unredacted Media. If redaction is enabled, the default is `true` to access redacted media. + + :returns: The fetched MediaInstance + """ + + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return MediaInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async( + self, redacted: Union[bool, object] = values.unset + ) -> MediaInstance: + """ + Asynchronous coroutine to fetch the MediaInstance + + :param redacted: Grant access to PII Redacted/Unredacted Media. If redaction is enabled, the default is `true` to access redacted media. + + :returns: The fetched MediaInstance + """ + + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return MediaInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MediaList(ListResource): + + def __init__(self, version: Version, sid: str): + """ + Initialize the MediaList + + :param version: Version that contains the resource + :param sid: The unique SID identifier of the Transcript. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + + def get(self) -> MediaContext: + """ + Constructs a MediaContext + + """ + return MediaContext(self._version, sid=self._solution["sid"]) + + def __call__(self) -> MediaContext: + """ + Constructs a MediaContext + + """ + return MediaContext(self._version, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/operator_result.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/operator_result.py new file mode 100644 index 0000000..9d1d614 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/operator_result.py @@ -0,0 +1,507 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class OperatorResultInstance(InstanceResource): + + class OperatorType(object): + CONVERSATION_CLASSIFY = "conversation_classify" + UTTERANCE_CLASSIFY = "utterance_classify" + EXTRACT = "extract" + EXTRACT_NORMALIZE = "extract_normalize" + PII_EXTRACT = "pii_extract" + + """ + :ivar operator_type: + :ivar name: The name of the applied Language Understanding. + :ivar operator_sid: A 34 character string that identifies this Language Understanding operator sid. + :ivar extract_match: Boolean to tell if extract Language Understanding Processing model matches results. + :ivar match_probability: Percentage of 'matching' class needed to consider a sentence matches + :ivar normalized_result: Normalized output of extraction stage which matches Label. + :ivar utterance_results: List of mapped utterance object which matches sentences. + :ivar utterance_match: Boolean to tell if Utterance matches results. + :ivar predicted_label: The 'matching' class. This might be available on conversation classify model outputs. + :ivar predicted_probability: Percentage of 'matching' class needed to consider a sentence matches. + :ivar label_probabilities: The labels probabilities. This might be available on conversation classify model outputs. + :ivar extract_results: List of text extraction results. This might be available on classify-extract model outputs. + :ivar text_generation_results: Output of a text generation operator for example Conversation Sumamary. + :ivar transcript_sid: A 34 character string that uniquely identifies this Transcript. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + transcript_sid: str, + operator_sid: Optional[str] = None, + ): + super().__init__(version) + + self.operator_type: Optional["OperatorResultInstance.OperatorType"] = ( + payload.get("operator_type") + ) + self.name: Optional[str] = payload.get("name") + self.operator_sid: Optional[str] = payload.get("operator_sid") + self.extract_match: Optional[bool] = payload.get("extract_match") + self.match_probability: Optional[float] = deserialize.decimal( + payload.get("match_probability") + ) + self.normalized_result: Optional[str] = payload.get("normalized_result") + self.utterance_results: Optional[List[Dict[str, object]]] = payload.get( + "utterance_results" + ) + self.utterance_match: Optional[bool] = payload.get("utterance_match") + self.predicted_label: Optional[str] = payload.get("predicted_label") + self.predicted_probability: Optional[float] = deserialize.decimal( + payload.get("predicted_probability") + ) + self.label_probabilities: Optional[Dict[str, object]] = payload.get( + "label_probabilities" + ) + self.extract_results: Optional[Dict[str, object]] = payload.get( + "extract_results" + ) + self.text_generation_results: Optional[Dict[str, object]] = payload.get( + "text_generation_results" + ) + self.transcript_sid: Optional[str] = payload.get("transcript_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "transcript_sid": transcript_sid, + "operator_sid": operator_sid or self.operator_sid, + } + self._context: Optional[OperatorResultContext] = None + + @property + def _proxy(self) -> "OperatorResultContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: OperatorResultContext for this OperatorResultInstance + """ + if self._context is None: + self._context = OperatorResultContext( + self._version, + transcript_sid=self._solution["transcript_sid"], + operator_sid=self._solution["operator_sid"], + ) + return self._context + + def fetch( + self, redacted: Union[bool, object] = values.unset + ) -> "OperatorResultInstance": + """ + Fetch the OperatorResultInstance + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + + :returns: The fetched OperatorResultInstance + """ + return self._proxy.fetch( + redacted=redacted, + ) + + async def fetch_async( + self, redacted: Union[bool, object] = values.unset + ) -> "OperatorResultInstance": + """ + Asynchronous coroutine to fetch the OperatorResultInstance + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + + :returns: The fetched OperatorResultInstance + """ + return await self._proxy.fetch_async( + redacted=redacted, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OperatorResultContext(InstanceContext): + + def __init__(self, version: Version, transcript_sid: str, operator_sid: str): + """ + Initialize the OperatorResultContext + + :param version: Version that contains the resource + :param transcript_sid: A 34 character string that uniquely identifies this Transcript. + :param operator_sid: A 34 character string that identifies this Language Understanding operator sid. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "transcript_sid": transcript_sid, + "operator_sid": operator_sid, + } + self._uri = ( + "/Transcripts/{transcript_sid}/OperatorResults/{operator_sid}".format( + **self._solution + ) + ) + + def fetch( + self, redacted: Union[bool, object] = values.unset + ) -> OperatorResultInstance: + """ + Fetch the OperatorResultInstance + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + + :returns: The fetched OperatorResultInstance + """ + + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return OperatorResultInstance( + self._version, + payload, + transcript_sid=self._solution["transcript_sid"], + operator_sid=self._solution["operator_sid"], + ) + + async def fetch_async( + self, redacted: Union[bool, object] = values.unset + ) -> OperatorResultInstance: + """ + Asynchronous coroutine to fetch the OperatorResultInstance + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + + :returns: The fetched OperatorResultInstance + """ + + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return OperatorResultInstance( + self._version, + payload, + transcript_sid=self._solution["transcript_sid"], + operator_sid=self._solution["operator_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OperatorResultPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> OperatorResultInstance: + """ + Build an instance of OperatorResultInstance + + :param payload: Payload response from the API + """ + return OperatorResultInstance( + self._version, payload, transcript_sid=self._solution["transcript_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class OperatorResultList(ListResource): + + def __init__(self, version: Version, transcript_sid: str): + """ + Initialize the OperatorResultList + + :param version: Version that contains the resource + :param transcript_sid: A 34 character string that uniquely identifies this Transcript. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "transcript_sid": transcript_sid, + } + self._uri = "/Transcripts/{transcript_sid}/OperatorResults".format( + **self._solution + ) + + def stream( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[OperatorResultInstance]: + """ + Streams OperatorResultInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(redacted=redacted, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[OperatorResultInstance]: + """ + Asynchronously streams OperatorResultInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(redacted=redacted, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OperatorResultInstance]: + """ + Lists OperatorResultInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + redacted=redacted, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OperatorResultInstance]: + """ + Asynchronously lists OperatorResultInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + redacted=redacted, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + redacted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OperatorResultPage: + """ + Retrieve a single page of OperatorResultInstance records from the API. + Request is executed immediately + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OperatorResultInstance + """ + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return OperatorResultPage(self._version, response, self._solution) + + async def page_async( + self, + redacted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OperatorResultPage: + """ + Asynchronously retrieve a single page of OperatorResultInstance records from the API. + Request is executed immediately + + :param redacted: Grant access to PII redacted/unredacted Language Understanding operator. If redaction is enabled, the default is True. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OperatorResultInstance + """ + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return OperatorResultPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> OperatorResultPage: + """ + Retrieve a specific page of OperatorResultInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OperatorResultInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return OperatorResultPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> OperatorResultPage: + """ + Asynchronously retrieve a specific page of OperatorResultInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OperatorResultInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return OperatorResultPage(self._version, response, self._solution) + + def get(self, operator_sid: str) -> OperatorResultContext: + """ + Constructs a OperatorResultContext + + :param operator_sid: A 34 character string that identifies this Language Understanding operator sid. + """ + return OperatorResultContext( + self._version, + transcript_sid=self._solution["transcript_sid"], + operator_sid=operator_sid, + ) + + def __call__(self, operator_sid: str) -> OperatorResultContext: + """ + Constructs a OperatorResultContext + + :param operator_sid: A 34 character string that identifies this Language Understanding operator sid. + """ + return OperatorResultContext( + self._version, + transcript_sid=self._solution["transcript_sid"], + operator_sid=operator_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/sentence.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/sentence.py new file mode 100644 index 0000000..5bdd18b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/intelligence/v2/transcript/sentence.py @@ -0,0 +1,312 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Intelligence + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SentenceInstance(InstanceResource): + """ + :ivar media_channel: The channel number. + :ivar sentence_index: The index of the sentence in the transcript. + :ivar start_time: Offset from the beginning of the transcript when this sentence starts. + :ivar end_time: Offset from the beginning of the transcript when this sentence ends. + :ivar transcript: Transcript text. + :ivar sid: A 34 character string that uniquely identifies this Sentence. + :ivar confidence: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], transcript_sid: str): + super().__init__(version) + + self.media_channel: Optional[int] = deserialize.integer( + payload.get("media_channel") + ) + self.sentence_index: Optional[int] = deserialize.integer( + payload.get("sentence_index") + ) + self.start_time: Optional[float] = deserialize.decimal( + payload.get("start_time") + ) + self.end_time: Optional[float] = deserialize.decimal(payload.get("end_time")) + self.transcript: Optional[str] = payload.get("transcript") + self.sid: Optional[str] = payload.get("sid") + self.confidence: Optional[float] = deserialize.decimal( + payload.get("confidence") + ) + + self._solution = { + "transcript_sid": transcript_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SentencePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SentenceInstance: + """ + Build an instance of SentenceInstance + + :param payload: Payload response from the API + """ + return SentenceInstance( + self._version, payload, transcript_sid=self._solution["transcript_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SentenceList(ListResource): + + def __init__(self, version: Version, transcript_sid: str): + """ + Initialize the SentenceList + + :param version: Version that contains the resource + :param transcript_sid: The unique SID identifier of the Transcript. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "transcript_sid": transcript_sid, + } + self._uri = "/Transcripts/{transcript_sid}/Sentences".format(**self._solution) + + def stream( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SentenceInstance]: + """ + Streams SentenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(redacted=redacted, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SentenceInstance]: + """ + Asynchronously streams SentenceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(redacted=redacted, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SentenceInstance]: + """ + Lists SentenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + redacted=redacted, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + redacted: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SentenceInstance]: + """ + Asynchronously lists SentenceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + redacted=redacted, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + redacted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SentencePage: + """ + Retrieve a single page of SentenceInstance records from the API. + Request is executed immediately + + :param redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SentenceInstance + """ + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SentencePage(self._version, response, self._solution) + + async def page_async( + self, + redacted: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SentencePage: + """ + Asynchronously retrieve a single page of SentenceInstance records from the API. + Request is executed immediately + + :param redacted: Grant access to PII Redacted/Unredacted Sentences. If redaction is enabled, the default is `true` to access redacted sentences. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SentenceInstance + """ + data = values.of( + { + "Redacted": serialize.boolean_to_string(redacted), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SentencePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SentencePage: + """ + Retrieve a specific page of SentenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SentenceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SentencePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SentencePage: + """ + Asynchronously retrieve a specific page of SentenceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SentenceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SentencePage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/IpMessagingBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/IpMessagingBase.py new file mode 100644 index 0000000..752d41a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/IpMessagingBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.ip_messaging.v1 import V1 +from twilio.rest.ip_messaging.v2 import V2 + + +class IpMessagingBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the IpMessaging Domain + + :returns: Domain for IpMessaging + """ + super().__init__(twilio, "https://ip-messaging.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of IpMessaging + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of IpMessaging + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__init__.py new file mode 100644 index 0000000..61158f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.ip_messaging.IpMessagingBase import IpMessagingBase +from twilio.rest.ip_messaging.v2.credential import CredentialList +from twilio.rest.ip_messaging.v2.service import ServiceList + + +class IpMessaging(IpMessagingBase): + @property + def credentials(self) -> CredentialList: + warn( + "credentials is deprecated. Use v2.credentials instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.credentials + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v2.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/IpMessagingBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/IpMessagingBase.cpython-310.pyc new file mode 100644 index 0000000..1f31fed Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/IpMessagingBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fc1bfd8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__init__.py new file mode 100644 index 0000000..d1f40c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.ip_messaging.v1.credential import CredentialList +from twilio.rest.ip_messaging.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of IpMessaging + + :param domain: The Twilio.ip_messaging domain + """ + super().__init__(domain, "v1") + self._credentials: Optional[CredentialList] = None + self._services: Optional[ServiceList] = None + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2e6c857 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..d441dfd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/credential.py new file mode 100644 index 0000000..eac8e38 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/credential.py @@ -0,0 +1,681 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushService(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar type: + :ivar sandbox: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushService"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__init__.py new file mode 100644 index 0000000..ad4f002 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__init__.py @@ -0,0 +1,1329 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v1.service.channel import ChannelList +from twilio.rest.ip_messaging.v1.service.role import RoleList +from twilio.rest.ip_messaging.v1.service.user import UserList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar date_created: + :ivar date_updated: + :ivar default_service_role_sid: + :ivar default_channel_role_sid: + :ivar default_channel_creator_role_sid: + :ivar read_status_enabled: + :ivar reachability_enabled: + :ivar typing_indicator_timeout: + :ivar consumption_report_interval: + :ivar limits: + :ivar webhooks: + :ivar pre_webhook_url: + :ivar post_webhook_url: + :ivar webhook_method: + :ivar webhook_filters: + :ivar notifications: + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.default_service_role_sid: Optional[str] = payload.get( + "default_service_role_sid" + ) + self.default_channel_role_sid: Optional[str] = payload.get( + "default_channel_role_sid" + ) + self.default_channel_creator_role_sid: Optional[str] = payload.get( + "default_channel_creator_role_sid" + ) + self.read_status_enabled: Optional[bool] = payload.get("read_status_enabled") + self.reachability_enabled: Optional[bool] = payload.get("reachability_enabled") + self.typing_indicator_timeout: Optional[int] = deserialize.integer( + payload.get("typing_indicator_timeout") + ) + self.consumption_report_interval: Optional[int] = deserialize.integer( + payload.get("consumption_report_interval") + ) + self.limits: Optional[Dict[str, object]] = payload.get("limits") + self.webhooks: Optional[Dict[str, object]] = payload.get("webhooks") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.webhook_filters: Optional[List[str]] = payload.get("webhook_filters") + self.notifications: Optional[Dict[str, object]] = payload.get("notifications") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param webhooks_on_message_send_url: + :param webhooks_on_message_send_method: + :param webhooks_on_message_update_url: + :param webhooks_on_message_update_method: + :param webhooks_on_message_remove_url: + :param webhooks_on_message_remove_method: + :param webhooks_on_channel_add_url: + :param webhooks_on_channel_add_method: + :param webhooks_on_channel_destroy_url: + :param webhooks_on_channel_destroy_method: + :param webhooks_on_channel_update_url: + :param webhooks_on_channel_update_method: + :param webhooks_on_member_add_url: + :param webhooks_on_member_add_method: + :param webhooks_on_member_remove_url: + :param webhooks_on_member_remove_method: + :param webhooks_on_message_sent_url: + :param webhooks_on_message_sent_method: + :param webhooks_on_message_updated_url: + :param webhooks_on_message_updated_method: + :param webhooks_on_message_removed_url: + :param webhooks_on_message_removed_method: + :param webhooks_on_channel_added_url: + :param webhooks_on_channel_added_method: + :param webhooks_on_channel_destroyed_url: + :param webhooks_on_channel_destroyed_method: + :param webhooks_on_channel_updated_url: + :param webhooks_on_channel_updated_method: + :param webhooks_on_member_added_url: + :param webhooks_on_member_added_method: + :param webhooks_on_member_removed_url: + :param webhooks_on_member_removed_method: + :param limits_channel_members: + :param limits_user_channels: + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + webhooks_on_message_send_url=webhooks_on_message_send_url, + webhooks_on_message_send_method=webhooks_on_message_send_method, + webhooks_on_message_update_url=webhooks_on_message_update_url, + webhooks_on_message_update_method=webhooks_on_message_update_method, + webhooks_on_message_remove_url=webhooks_on_message_remove_url, + webhooks_on_message_remove_method=webhooks_on_message_remove_method, + webhooks_on_channel_add_url=webhooks_on_channel_add_url, + webhooks_on_channel_add_method=webhooks_on_channel_add_method, + webhooks_on_channel_destroy_url=webhooks_on_channel_destroy_url, + webhooks_on_channel_destroy_method=webhooks_on_channel_destroy_method, + webhooks_on_channel_update_url=webhooks_on_channel_update_url, + webhooks_on_channel_update_method=webhooks_on_channel_update_method, + webhooks_on_member_add_url=webhooks_on_member_add_url, + webhooks_on_member_add_method=webhooks_on_member_add_method, + webhooks_on_member_remove_url=webhooks_on_member_remove_url, + webhooks_on_member_remove_method=webhooks_on_member_remove_method, + webhooks_on_message_sent_url=webhooks_on_message_sent_url, + webhooks_on_message_sent_method=webhooks_on_message_sent_method, + webhooks_on_message_updated_url=webhooks_on_message_updated_url, + webhooks_on_message_updated_method=webhooks_on_message_updated_method, + webhooks_on_message_removed_url=webhooks_on_message_removed_url, + webhooks_on_message_removed_method=webhooks_on_message_removed_method, + webhooks_on_channel_added_url=webhooks_on_channel_added_url, + webhooks_on_channel_added_method=webhooks_on_channel_added_method, + webhooks_on_channel_destroyed_url=webhooks_on_channel_destroyed_url, + webhooks_on_channel_destroyed_method=webhooks_on_channel_destroyed_method, + webhooks_on_channel_updated_url=webhooks_on_channel_updated_url, + webhooks_on_channel_updated_method=webhooks_on_channel_updated_method, + webhooks_on_member_added_url=webhooks_on_member_added_url, + webhooks_on_member_added_method=webhooks_on_member_added_method, + webhooks_on_member_removed_url=webhooks_on_member_removed_url, + webhooks_on_member_removed_method=webhooks_on_member_removed_method, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param webhooks_on_message_send_url: + :param webhooks_on_message_send_method: + :param webhooks_on_message_update_url: + :param webhooks_on_message_update_method: + :param webhooks_on_message_remove_url: + :param webhooks_on_message_remove_method: + :param webhooks_on_channel_add_url: + :param webhooks_on_channel_add_method: + :param webhooks_on_channel_destroy_url: + :param webhooks_on_channel_destroy_method: + :param webhooks_on_channel_update_url: + :param webhooks_on_channel_update_method: + :param webhooks_on_member_add_url: + :param webhooks_on_member_add_method: + :param webhooks_on_member_remove_url: + :param webhooks_on_member_remove_method: + :param webhooks_on_message_sent_url: + :param webhooks_on_message_sent_method: + :param webhooks_on_message_updated_url: + :param webhooks_on_message_updated_method: + :param webhooks_on_message_removed_url: + :param webhooks_on_message_removed_method: + :param webhooks_on_channel_added_url: + :param webhooks_on_channel_added_method: + :param webhooks_on_channel_destroyed_url: + :param webhooks_on_channel_destroyed_method: + :param webhooks_on_channel_updated_url: + :param webhooks_on_channel_updated_method: + :param webhooks_on_member_added_url: + :param webhooks_on_member_added_method: + :param webhooks_on_member_removed_url: + :param webhooks_on_member_removed_method: + :param limits_channel_members: + :param limits_user_channels: + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + webhooks_on_message_send_url=webhooks_on_message_send_url, + webhooks_on_message_send_method=webhooks_on_message_send_method, + webhooks_on_message_update_url=webhooks_on_message_update_url, + webhooks_on_message_update_method=webhooks_on_message_update_method, + webhooks_on_message_remove_url=webhooks_on_message_remove_url, + webhooks_on_message_remove_method=webhooks_on_message_remove_method, + webhooks_on_channel_add_url=webhooks_on_channel_add_url, + webhooks_on_channel_add_method=webhooks_on_channel_add_method, + webhooks_on_channel_destroy_url=webhooks_on_channel_destroy_url, + webhooks_on_channel_destroy_method=webhooks_on_channel_destroy_method, + webhooks_on_channel_update_url=webhooks_on_channel_update_url, + webhooks_on_channel_update_method=webhooks_on_channel_update_method, + webhooks_on_member_add_url=webhooks_on_member_add_url, + webhooks_on_member_add_method=webhooks_on_member_add_method, + webhooks_on_member_remove_url=webhooks_on_member_remove_url, + webhooks_on_member_remove_method=webhooks_on_member_remove_method, + webhooks_on_message_sent_url=webhooks_on_message_sent_url, + webhooks_on_message_sent_method=webhooks_on_message_sent_method, + webhooks_on_message_updated_url=webhooks_on_message_updated_url, + webhooks_on_message_updated_method=webhooks_on_message_updated_method, + webhooks_on_message_removed_url=webhooks_on_message_removed_url, + webhooks_on_message_removed_method=webhooks_on_message_removed_method, + webhooks_on_channel_added_url=webhooks_on_channel_added_url, + webhooks_on_channel_added_method=webhooks_on_channel_added_method, + webhooks_on_channel_destroyed_url=webhooks_on_channel_destroyed_url, + webhooks_on_channel_destroyed_method=webhooks_on_channel_destroyed_method, + webhooks_on_channel_updated_url=webhooks_on_channel_updated_url, + webhooks_on_channel_updated_method=webhooks_on_channel_updated_method, + webhooks_on_member_added_url=webhooks_on_member_added_url, + webhooks_on_member_added_method=webhooks_on_member_added_method, + webhooks_on_member_removed_url=webhooks_on_member_removed_url, + webhooks_on_member_removed_method=webhooks_on_member_removed_method, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + ) + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + return self._proxy.channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + return self._proxy.roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + return self._proxy.users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._channels: Optional[ChannelList] = None + self._roles: Optional[RoleList] = None + self._users: Optional[UserList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param webhooks_on_message_send_url: + :param webhooks_on_message_send_method: + :param webhooks_on_message_update_url: + :param webhooks_on_message_update_method: + :param webhooks_on_message_remove_url: + :param webhooks_on_message_remove_method: + :param webhooks_on_channel_add_url: + :param webhooks_on_channel_add_method: + :param webhooks_on_channel_destroy_url: + :param webhooks_on_channel_destroy_method: + :param webhooks_on_channel_update_url: + :param webhooks_on_channel_update_method: + :param webhooks_on_member_add_url: + :param webhooks_on_member_add_method: + :param webhooks_on_member_remove_url: + :param webhooks_on_member_remove_method: + :param webhooks_on_message_sent_url: + :param webhooks_on_message_sent_method: + :param webhooks_on_message_updated_url: + :param webhooks_on_message_updated_method: + :param webhooks_on_message_removed_url: + :param webhooks_on_message_removed_method: + :param webhooks_on_channel_added_url: + :param webhooks_on_channel_added_method: + :param webhooks_on_channel_destroyed_url: + :param webhooks_on_channel_destroyed_method: + :param webhooks_on_channel_updated_url: + :param webhooks_on_channel_updated_method: + :param webhooks_on_member_added_url: + :param webhooks_on_member_added_method: + :param webhooks_on_member_removed_url: + :param webhooks_on_member_removed_method: + :param limits_channel_members: + :param limits_user_channels: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Webhooks.OnMessageSend.Url": webhooks_on_message_send_url, + "Webhooks.OnMessageSend.Method": webhooks_on_message_send_method, + "Webhooks.OnMessageUpdate.Url": webhooks_on_message_update_url, + "Webhooks.OnMessageUpdate.Method": webhooks_on_message_update_method, + "Webhooks.OnMessageRemove.Url": webhooks_on_message_remove_url, + "Webhooks.OnMessageRemove.Method": webhooks_on_message_remove_method, + "Webhooks.OnChannelAdd.Url": webhooks_on_channel_add_url, + "Webhooks.OnChannelAdd.Method": webhooks_on_channel_add_method, + "Webhooks.OnChannelDestroy.Url": webhooks_on_channel_destroy_url, + "Webhooks.OnChannelDestroy.Method": webhooks_on_channel_destroy_method, + "Webhooks.OnChannelUpdate.Url": webhooks_on_channel_update_url, + "Webhooks.OnChannelUpdate.Method": webhooks_on_channel_update_method, + "Webhooks.OnMemberAdd.Url": webhooks_on_member_add_url, + "Webhooks.OnMemberAdd.Method": webhooks_on_member_add_method, + "Webhooks.OnMemberRemove.Url": webhooks_on_member_remove_url, + "Webhooks.OnMemberRemove.Method": webhooks_on_member_remove_method, + "Webhooks.OnMessageSent.Url": webhooks_on_message_sent_url, + "Webhooks.OnMessageSent.Method": webhooks_on_message_sent_method, + "Webhooks.OnMessageUpdated.Url": webhooks_on_message_updated_url, + "Webhooks.OnMessageUpdated.Method": webhooks_on_message_updated_method, + "Webhooks.OnMessageRemoved.Url": webhooks_on_message_removed_url, + "Webhooks.OnMessageRemoved.Method": webhooks_on_message_removed_method, + "Webhooks.OnChannelAdded.Url": webhooks_on_channel_added_url, + "Webhooks.OnChannelAdded.Method": webhooks_on_channel_added_method, + "Webhooks.OnChannelDestroyed.Url": webhooks_on_channel_destroyed_url, + "Webhooks.OnChannelDestroyed.Method": webhooks_on_channel_destroyed_method, + "Webhooks.OnChannelUpdated.Url": webhooks_on_channel_updated_url, + "Webhooks.OnChannelUpdated.Method": webhooks_on_channel_updated_method, + "Webhooks.OnMemberAdded.Url": webhooks_on_member_added_url, + "Webhooks.OnMemberAdded.Method": webhooks_on_member_added_method, + "Webhooks.OnMemberRemoved.Url": webhooks_on_member_removed_url, + "Webhooks.OnMemberRemoved.Method": webhooks_on_member_removed_method, + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + webhooks_on_message_send_url: Union[str, object] = values.unset, + webhooks_on_message_send_method: Union[str, object] = values.unset, + webhooks_on_message_update_url: Union[str, object] = values.unset, + webhooks_on_message_update_method: Union[str, object] = values.unset, + webhooks_on_message_remove_url: Union[str, object] = values.unset, + webhooks_on_message_remove_method: Union[str, object] = values.unset, + webhooks_on_channel_add_url: Union[str, object] = values.unset, + webhooks_on_channel_add_method: Union[str, object] = values.unset, + webhooks_on_channel_destroy_url: Union[str, object] = values.unset, + webhooks_on_channel_destroy_method: Union[str, object] = values.unset, + webhooks_on_channel_update_url: Union[str, object] = values.unset, + webhooks_on_channel_update_method: Union[str, object] = values.unset, + webhooks_on_member_add_url: Union[str, object] = values.unset, + webhooks_on_member_add_method: Union[str, object] = values.unset, + webhooks_on_member_remove_url: Union[str, object] = values.unset, + webhooks_on_member_remove_method: Union[str, object] = values.unset, + webhooks_on_message_sent_url: Union[str, object] = values.unset, + webhooks_on_message_sent_method: Union[str, object] = values.unset, + webhooks_on_message_updated_url: Union[str, object] = values.unset, + webhooks_on_message_updated_method: Union[str, object] = values.unset, + webhooks_on_message_removed_url: Union[str, object] = values.unset, + webhooks_on_message_removed_method: Union[str, object] = values.unset, + webhooks_on_channel_added_url: Union[str, object] = values.unset, + webhooks_on_channel_added_method: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_url: Union[str, object] = values.unset, + webhooks_on_channel_destroyed_method: Union[str, object] = values.unset, + webhooks_on_channel_updated_url: Union[str, object] = values.unset, + webhooks_on_channel_updated_method: Union[str, object] = values.unset, + webhooks_on_member_added_url: Union[str, object] = values.unset, + webhooks_on_member_added_method: Union[str, object] = values.unset, + webhooks_on_member_removed_url: Union[str, object] = values.unset, + webhooks_on_member_removed_method: Union[str, object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param webhooks_on_message_send_url: + :param webhooks_on_message_send_method: + :param webhooks_on_message_update_url: + :param webhooks_on_message_update_method: + :param webhooks_on_message_remove_url: + :param webhooks_on_message_remove_method: + :param webhooks_on_channel_add_url: + :param webhooks_on_channel_add_method: + :param webhooks_on_channel_destroy_url: + :param webhooks_on_channel_destroy_method: + :param webhooks_on_channel_update_url: + :param webhooks_on_channel_update_method: + :param webhooks_on_member_add_url: + :param webhooks_on_member_add_method: + :param webhooks_on_member_remove_url: + :param webhooks_on_member_remove_method: + :param webhooks_on_message_sent_url: + :param webhooks_on_message_sent_method: + :param webhooks_on_message_updated_url: + :param webhooks_on_message_updated_method: + :param webhooks_on_message_removed_url: + :param webhooks_on_message_removed_method: + :param webhooks_on_channel_added_url: + :param webhooks_on_channel_added_method: + :param webhooks_on_channel_destroyed_url: + :param webhooks_on_channel_destroyed_method: + :param webhooks_on_channel_updated_url: + :param webhooks_on_channel_updated_method: + :param webhooks_on_member_added_url: + :param webhooks_on_member_added_method: + :param webhooks_on_member_removed_url: + :param webhooks_on_member_removed_method: + :param limits_channel_members: + :param limits_user_channels: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Webhooks.OnMessageSend.Url": webhooks_on_message_send_url, + "Webhooks.OnMessageSend.Method": webhooks_on_message_send_method, + "Webhooks.OnMessageUpdate.Url": webhooks_on_message_update_url, + "Webhooks.OnMessageUpdate.Method": webhooks_on_message_update_method, + "Webhooks.OnMessageRemove.Url": webhooks_on_message_remove_url, + "Webhooks.OnMessageRemove.Method": webhooks_on_message_remove_method, + "Webhooks.OnChannelAdd.Url": webhooks_on_channel_add_url, + "Webhooks.OnChannelAdd.Method": webhooks_on_channel_add_method, + "Webhooks.OnChannelDestroy.Url": webhooks_on_channel_destroy_url, + "Webhooks.OnChannelDestroy.Method": webhooks_on_channel_destroy_method, + "Webhooks.OnChannelUpdate.Url": webhooks_on_channel_update_url, + "Webhooks.OnChannelUpdate.Method": webhooks_on_channel_update_method, + "Webhooks.OnMemberAdd.Url": webhooks_on_member_add_url, + "Webhooks.OnMemberAdd.Method": webhooks_on_member_add_method, + "Webhooks.OnMemberRemove.Url": webhooks_on_member_remove_url, + "Webhooks.OnMemberRemove.Method": webhooks_on_member_remove_method, + "Webhooks.OnMessageSent.Url": webhooks_on_message_sent_url, + "Webhooks.OnMessageSent.Method": webhooks_on_message_sent_method, + "Webhooks.OnMessageUpdated.Url": webhooks_on_message_updated_url, + "Webhooks.OnMessageUpdated.Method": webhooks_on_message_updated_method, + "Webhooks.OnMessageRemoved.Url": webhooks_on_message_removed_url, + "Webhooks.OnMessageRemoved.Method": webhooks_on_message_removed_method, + "Webhooks.OnChannelAdded.Url": webhooks_on_channel_added_url, + "Webhooks.OnChannelAdded.Method": webhooks_on_channel_added_method, + "Webhooks.OnChannelDestroyed.Url": webhooks_on_channel_destroyed_url, + "Webhooks.OnChannelDestroyed.Method": webhooks_on_channel_destroyed_method, + "Webhooks.OnChannelUpdated.Url": webhooks_on_channel_updated_url, + "Webhooks.OnChannelUpdated.Method": webhooks_on_channel_updated_method, + "Webhooks.OnMemberAdded.Url": webhooks_on_member_added_url, + "Webhooks.OnMemberAdded.Method": webhooks_on_member_added_method, + "Webhooks.OnMemberRemoved.Url": webhooks_on_member_removed_url, + "Webhooks.OnMemberRemoved.Method": webhooks_on_member_removed_method, + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + if self._channels is None: + self._channels = ChannelList( + self._version, + self._solution["sid"], + ) + return self._channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + if self._roles is None: + self._roles = RoleList( + self._version, + self._solution["sid"], + ) + return self._roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution["sid"], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create(self, friendly_name: str) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async(self, friendly_name: str) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d3ad16a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..f4cbf3b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__init__.py new file mode 100644 index 0000000..ef469e9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__init__.py @@ -0,0 +1,760 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v1.service.channel.invite import InviteList +from twilio.rest.ip_messaging.v1.service.channel.member import MemberList +from twilio.rest.ip_messaging.v1.service.channel.message import MessageList + + +class ChannelInstance(InstanceResource): + + class ChannelType(object): + PUBLIC = "public" + PRIVATE = "private" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar friendly_name: + :ivar unique_name: + :ivar attributes: + :ivar type: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + :ivar members_count: + :ivar messages_count: + :ivar url: + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.type: Optional["ChannelInstance.ChannelType"] = payload.get("type") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.members_count: Optional[int] = deserialize.integer( + payload.get("members_count") + ) + self.messages_count: Optional[int] = deserialize.integer( + payload.get("messages_count") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ChannelInstance": + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelInstance": + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Update the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + + :returns: The updated ChannelInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Asynchronous coroutine to update the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + + :returns: The updated ChannelInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + return self._proxy.invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + return self._proxy.members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Channels/{sid}".format(**self._solution) + + self._invites: Optional[InviteList] = None + self._members: Optional[MemberList] = None + self._messages: Optional[MessageList] = None + + def delete(self) -> bool: + """ + Deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ChannelInstance: + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelInstance: + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Update the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronous coroutine to update the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + if self._invites is None: + self._invites = InviteList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + if self._members is None: + self._members = MemberList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelInstance: + """ + Build an instance of ChannelInstance + + :param payload: Payload response from the API + """ + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Channels".format(**self._solution) + + def create( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + ) -> ChannelInstance: + """ + Create the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + :param type: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronously create the ChannelInstance + + :param friendly_name: + :param unique_name: + :param attributes: + :param type: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelInstance]: + """ + Streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(type=type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelInstance]: + """ + Asynchronously streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(type=type, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + type=type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Asynchronously lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + type=type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelPage(self._version, response, self._solution) + + async def page_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Asynchronously retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChannelPage: + """ + Retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChannelPage: + """ + Asynchronously retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..545de35 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/invite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/invite.cpython-310.pyc new file mode 100644 index 0000000..5cad05a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/invite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/member.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/member.cpython-310.pyc new file mode 100644 index 0000000..131def3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/member.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/message.cpython-310.pyc new file mode 100644 index 0000000..d8a44d0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/__pycache__/message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/invite.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/invite.py new file mode 100644 index 0000000..7078058 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/invite.py @@ -0,0 +1,576 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InviteInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar channel_sid: + :ivar service_sid: + :ivar identity: + :ivar date_created: + :ivar date_updated: + :ivar role_sid: + :ivar created_by: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.created_by: Optional[str] = payload.get("created_by") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[InviteContext] = None + + @property + def _proxy(self) -> "InviteContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InviteContext for this InviteInstance + """ + if self._context is None: + self._context = InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InviteInstance": + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InviteInstance": + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InviteContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the InviteContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Invites/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InviteInstance: + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InviteInstance: + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InvitePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InviteInstance: + """ + Build an instance of InviteInstance + + :param payload: Payload response from the API + """ + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InviteList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the InviteList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Invites".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Create the InviteInstance + + :param identity: + :param role_sid: + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Asynchronously create the InviteInstance + + :param identity: + :param role_sid: + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InviteInstance]: + """ + Streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InviteInstance]: + """ + Asynchronously streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Asynchronously lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InvitePage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Asynchronously retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InvitePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InvitePage: + """ + Retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InvitePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InvitePage: + """ + Asynchronously retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InvitePage(self._version, response, self._solution) + + def get(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/member.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/member.py new file mode 100644 index 0000000..c52fde5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/member.py @@ -0,0 +1,686 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MemberInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar channel_sid: + :ivar service_sid: + :ivar identity: + :ivar date_created: + :ivar date_updated: + :ivar role_sid: + :ivar last_consumed_message_index: + :ivar last_consumption_timestamp: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.last_consumption_timestamp: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("last_consumption_timestamp")) + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MemberContext] = None + + @property + def _proxy(self) -> "MemberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MemberContext for this MemberInstance + """ + if self._context is None: + self._context = MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MemberInstance": + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MemberInstance": + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> "MemberInstance": + """ + Update the MemberInstance + + :param role_sid: + :param last_consumed_message_index: + + :returns: The updated MemberInstance + """ + return self._proxy.update( + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> "MemberInstance": + """ + Asynchronous coroutine to update the MemberInstance + + :param role_sid: + :param last_consumed_message_index: + + :returns: The updated MemberInstance + """ + return await self._proxy.update_async( + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MemberContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Members/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MemberInstance: + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MemberInstance: + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> MemberInstance: + """ + Update the MemberInstance + + :param role_sid: + :param last_consumed_message_index: + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronous coroutine to update the MemberInstance + + :param role_sid: + :param last_consumed_message_index: + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MemberInstance: + """ + Build an instance of MemberInstance + + :param payload: Payload response from the API + """ + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MemberList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MemberList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Members".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Create the MemberInstance + + :param identity: + :param role_sid: + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> MemberInstance: + """ + Asynchronously create the MemberInstance + + :param identity: + :param role_sid: + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MemberInstance]: + """ + Streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MemberInstance]: + """ + Asynchronously streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Asynchronously lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MemberPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Asynchronously retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MemberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MemberPage: + """ + Retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MemberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MemberPage: + """ + Asynchronously retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MemberPage(self._version, response, self._solution) + + def get(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/message.py new file mode 100644 index 0000000..f9f2a2b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/channel/message.py @@ -0,0 +1,701 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar sid: + :ivar account_sid: + :ivar attributes: + :ivar service_sid: + :ivar to: + :ivar channel_sid: + :ivar date_created: + :ivar date_updated: + :ivar was_edited: + :ivar _from: + :ivar body: + :ivar index: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.service_sid: Optional[str] = payload.get("service_sid") + self.to: Optional[str] = payload.get("to") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.was_edited: Optional[bool] = payload.get("was_edited") + self._from: Optional[str] = payload.get("from") + self.body: Optional[str] = payload.get("body") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param body: + :param attributes: + + :returns: The updated MessageInstance + """ + return self._proxy.update( + body=body, + attributes=attributes, + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param body: + :param attributes: + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + body=body, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Messages/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param body: + :param attributes: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param body: + :param attributes: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Messages".format( + **self._solution + ) + + def create( + self, + body: str, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param body: + :param from_: + :param attributes: + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Body": body, + "From": from_, + "Attributes": attributes, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + body: str, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param body: + :param from_: + :param attributes: + + :returns: The created MessageInstance + """ + + data = values.of( + { + "Body": body, + "From": from_, + "Attributes": attributes, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/role.py new file mode 100644 index 0000000..935a99e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/role.py @@ -0,0 +1,615 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CHANNEL = "channel" + DEPLOYMENT = "deployment" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar friendly_name: + :ivar type: + :ivar permissions: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the RoleList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Roles".format(**self._solution) + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: + :param type: + :param permission: + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: + :param type: + :param permission: + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response, self._solution) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__init__.py new file mode 100644 index 0000000..ef24fa7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__init__.py @@ -0,0 +1,693 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v1.service.user.user_channel import UserChannelList + + +class UserInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar attributes: + :ivar friendly_name: + :ivar role_sid: + :ivar identity: + :ivar is_online: + :ivar is_notifiable: + :ivar date_created: + :ivar date_updated: + :ivar joined_channels_count: + :ivar links: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.joined_channels_count: Optional[int] = deserialize.integer( + payload.get("joined_channels_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + return self._proxy.update( + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + return self._proxy.user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{sid}".format(**self._solution) + + self._user_channels: Optional[UserChannelList] = None + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + if self._user_channels is None: + self._user_channels = UserChannelList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Users".format(**self._solution) + + def create( + self, + identity: str, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2c53964 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/user_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/user_channel.cpython-310.pyc new file mode 100644 index 0000000..a855bab Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/__pycache__/user_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/user_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/user_channel.py new file mode 100644 index 0000000..192aafb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v1/service/user/user_channel.py @@ -0,0 +1,312 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserChannelInstance(InstanceResource): + + class ChannelStatus(object): + JOINED = "joined" + INVITED = "invited" + NOT_PARTICIPATING = "not_participating" + + """ + :ivar account_sid: + :ivar service_sid: + :ivar channel_sid: + :ivar member_sid: + :ivar status: + :ivar last_consumed_message_index: + :ivar unread_messages_count: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], service_sid: str, user_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.member_sid: Optional[str] = payload.get("member_sid") + self.status: Optional["UserChannelInstance.ChannelStatus"] = payload.get( + "status" + ) + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserChannelInstance: + """ + Build an instance of UserChannelInstance + + :param payload: Payload response from the API + """ + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserChannelList + + :param version: Version that contains the resource + :param service_sid: + :param user_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Channels".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserChannelInstance]: + """ + Streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserChannelInstance]: + """ + Asynchronously streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Asynchronously lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Asynchronously retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserChannelPage: + """ + Retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserChannelPage: + """ + Asynchronously retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__init__.py new file mode 100644 index 0000000..6003c86 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.ip_messaging.v2.credential import CredentialList +from twilio.rest.ip_messaging.v2.service import ServiceList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of IpMessaging + + :param domain: The Twilio.ip_messaging domain + """ + super().__init__(domain, "v2") + self._credentials: Optional[CredentialList] = None + self._services: Optional[ServiceList] = None + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..02c921b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..68acda9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/credential.py new file mode 100644 index 0000000..beec9fb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/credential.py @@ -0,0 +1,681 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushService(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar type: + :ivar sandbox: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushService"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: + :param certificate: + :param private_key: + :param sandbox: + :param api_key: + :param secret: + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__init__.py new file mode 100644 index 0000000..b107a11 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__init__.py @@ -0,0 +1,1098 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v2.service.binding import BindingList +from twilio.rest.ip_messaging.v2.service.channel import ChannelList +from twilio.rest.ip_messaging.v2.service.role import RoleList +from twilio.rest.ip_messaging.v2.service.user import UserList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar date_created: + :ivar date_updated: + :ivar default_service_role_sid: + :ivar default_channel_role_sid: + :ivar default_channel_creator_role_sid: + :ivar read_status_enabled: + :ivar reachability_enabled: + :ivar typing_indicator_timeout: + :ivar consumption_report_interval: + :ivar limits: + :ivar pre_webhook_url: + :ivar post_webhook_url: + :ivar webhook_method: + :ivar webhook_filters: + :ivar pre_webhook_retry_count: + :ivar post_webhook_retry_count: + :ivar notifications: + :ivar media: + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.default_service_role_sid: Optional[str] = payload.get( + "default_service_role_sid" + ) + self.default_channel_role_sid: Optional[str] = payload.get( + "default_channel_role_sid" + ) + self.default_channel_creator_role_sid: Optional[str] = payload.get( + "default_channel_creator_role_sid" + ) + self.read_status_enabled: Optional[bool] = payload.get("read_status_enabled") + self.reachability_enabled: Optional[bool] = payload.get("reachability_enabled") + self.typing_indicator_timeout: Optional[int] = deserialize.integer( + payload.get("typing_indicator_timeout") + ) + self.consumption_report_interval: Optional[int] = deserialize.integer( + payload.get("consumption_report_interval") + ) + self.limits: Optional[Dict[str, object]] = payload.get("limits") + self.pre_webhook_url: Optional[str] = payload.get("pre_webhook_url") + self.post_webhook_url: Optional[str] = payload.get("post_webhook_url") + self.webhook_method: Optional[str] = payload.get("webhook_method") + self.webhook_filters: Optional[List[str]] = payload.get("webhook_filters") + self.pre_webhook_retry_count: Optional[int] = deserialize.integer( + payload.get("pre_webhook_retry_count") + ) + self.post_webhook_retry_count: Optional[int] = deserialize.integer( + payload.get("post_webhook_retry_count") + ) + self.notifications: Optional[Dict[str, object]] = payload.get("notifications") + self.media: Optional[Dict[str, object]] = payload.get("media") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_new_message_sound: + :param notifications_new_message_badge_count_enabled: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_added_to_channel_sound: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_removed_from_channel_sound: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param notifications_invited_to_channel_sound: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param limits_channel_members: + :param limits_user_channels: + :param media_compatibility_message: + :param pre_webhook_retry_count: + :param post_webhook_retry_count: + :param notifications_log_enabled: + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_new_message_sound=notifications_new_message_sound, + notifications_new_message_badge_count_enabled=notifications_new_message_badge_count_enabled, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_added_to_channel_sound=notifications_added_to_channel_sound, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_removed_from_channel_sound=notifications_removed_from_channel_sound, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + notifications_invited_to_channel_sound=notifications_invited_to_channel_sound, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + media_compatibility_message=media_compatibility_message, + pre_webhook_retry_count=pre_webhook_retry_count, + post_webhook_retry_count=post_webhook_retry_count, + notifications_log_enabled=notifications_log_enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_new_message_sound: + :param notifications_new_message_badge_count_enabled: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_added_to_channel_sound: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_removed_from_channel_sound: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param notifications_invited_to_channel_sound: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param limits_channel_members: + :param limits_user_channels: + :param media_compatibility_message: + :param pre_webhook_retry_count: + :param post_webhook_retry_count: + :param notifications_log_enabled: + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + default_service_role_sid=default_service_role_sid, + default_channel_role_sid=default_channel_role_sid, + default_channel_creator_role_sid=default_channel_creator_role_sid, + read_status_enabled=read_status_enabled, + reachability_enabled=reachability_enabled, + typing_indicator_timeout=typing_indicator_timeout, + consumption_report_interval=consumption_report_interval, + notifications_new_message_enabled=notifications_new_message_enabled, + notifications_new_message_template=notifications_new_message_template, + notifications_new_message_sound=notifications_new_message_sound, + notifications_new_message_badge_count_enabled=notifications_new_message_badge_count_enabled, + notifications_added_to_channel_enabled=notifications_added_to_channel_enabled, + notifications_added_to_channel_template=notifications_added_to_channel_template, + notifications_added_to_channel_sound=notifications_added_to_channel_sound, + notifications_removed_from_channel_enabled=notifications_removed_from_channel_enabled, + notifications_removed_from_channel_template=notifications_removed_from_channel_template, + notifications_removed_from_channel_sound=notifications_removed_from_channel_sound, + notifications_invited_to_channel_enabled=notifications_invited_to_channel_enabled, + notifications_invited_to_channel_template=notifications_invited_to_channel_template, + notifications_invited_to_channel_sound=notifications_invited_to_channel_sound, + pre_webhook_url=pre_webhook_url, + post_webhook_url=post_webhook_url, + webhook_method=webhook_method, + webhook_filters=webhook_filters, + limits_channel_members=limits_channel_members, + limits_user_channels=limits_user_channels, + media_compatibility_message=media_compatibility_message, + pre_webhook_retry_count=pre_webhook_retry_count, + post_webhook_retry_count=post_webhook_retry_count, + notifications_log_enabled=notifications_log_enabled, + ) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + return self._proxy.bindings + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + return self._proxy.channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + return self._proxy.roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + return self._proxy.users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._bindings: Optional[BindingList] = None + self._channels: Optional[ChannelList] = None + self._roles: Optional[RoleList] = None + self._users: Optional[UserList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_new_message_sound: + :param notifications_new_message_badge_count_enabled: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_added_to_channel_sound: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_removed_from_channel_sound: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param notifications_invited_to_channel_sound: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param limits_channel_members: + :param limits_user_channels: + :param media_compatibility_message: + :param pre_webhook_retry_count: + :param post_webhook_retry_count: + :param notifications_log_enabled: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.NewMessage.Sound": notifications_new_message_sound, + "Notifications.NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + notifications_new_message_badge_count_enabled + ), + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.AddedToChannel.Sound": notifications_added_to_channel_sound, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.RemovedFromChannel.Sound": notifications_removed_from_channel_sound, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "Notifications.InvitedToChannel.Sound": notifications_invited_to_channel_sound, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + "Media.CompatibilityMessage": media_compatibility_message, + "PreWebhookRetryCount": pre_webhook_retry_count, + "PostWebhookRetryCount": post_webhook_retry_count, + "Notifications.LogEnabled": serialize.boolean_to_string( + notifications_log_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_service_role_sid: Union[str, object] = values.unset, + default_channel_role_sid: Union[str, object] = values.unset, + default_channel_creator_role_sid: Union[str, object] = values.unset, + read_status_enabled: Union[bool, object] = values.unset, + reachability_enabled: Union[bool, object] = values.unset, + typing_indicator_timeout: Union[int, object] = values.unset, + consumption_report_interval: Union[int, object] = values.unset, + notifications_new_message_enabled: Union[bool, object] = values.unset, + notifications_new_message_template: Union[str, object] = values.unset, + notifications_new_message_sound: Union[str, object] = values.unset, + notifications_new_message_badge_count_enabled: Union[ + bool, object + ] = values.unset, + notifications_added_to_channel_enabled: Union[bool, object] = values.unset, + notifications_added_to_channel_template: Union[str, object] = values.unset, + notifications_added_to_channel_sound: Union[str, object] = values.unset, + notifications_removed_from_channel_enabled: Union[bool, object] = values.unset, + notifications_removed_from_channel_template: Union[str, object] = values.unset, + notifications_removed_from_channel_sound: Union[str, object] = values.unset, + notifications_invited_to_channel_enabled: Union[bool, object] = values.unset, + notifications_invited_to_channel_template: Union[str, object] = values.unset, + notifications_invited_to_channel_sound: Union[str, object] = values.unset, + pre_webhook_url: Union[str, object] = values.unset, + post_webhook_url: Union[str, object] = values.unset, + webhook_method: Union[str, object] = values.unset, + webhook_filters: Union[List[str], object] = values.unset, + limits_channel_members: Union[int, object] = values.unset, + limits_user_channels: Union[int, object] = values.unset, + media_compatibility_message: Union[str, object] = values.unset, + pre_webhook_retry_count: Union[int, object] = values.unset, + post_webhook_retry_count: Union[int, object] = values.unset, + notifications_log_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: + :param default_service_role_sid: + :param default_channel_role_sid: + :param default_channel_creator_role_sid: + :param read_status_enabled: + :param reachability_enabled: + :param typing_indicator_timeout: + :param consumption_report_interval: + :param notifications_new_message_enabled: + :param notifications_new_message_template: + :param notifications_new_message_sound: + :param notifications_new_message_badge_count_enabled: + :param notifications_added_to_channel_enabled: + :param notifications_added_to_channel_template: + :param notifications_added_to_channel_sound: + :param notifications_removed_from_channel_enabled: + :param notifications_removed_from_channel_template: + :param notifications_removed_from_channel_sound: + :param notifications_invited_to_channel_enabled: + :param notifications_invited_to_channel_template: + :param notifications_invited_to_channel_sound: + :param pre_webhook_url: + :param post_webhook_url: + :param webhook_method: + :param webhook_filters: + :param limits_channel_members: + :param limits_user_channels: + :param media_compatibility_message: + :param pre_webhook_retry_count: + :param post_webhook_retry_count: + :param notifications_log_enabled: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultServiceRoleSid": default_service_role_sid, + "DefaultChannelRoleSid": default_channel_role_sid, + "DefaultChannelCreatorRoleSid": default_channel_creator_role_sid, + "ReadStatusEnabled": serialize.boolean_to_string(read_status_enabled), + "ReachabilityEnabled": serialize.boolean_to_string( + reachability_enabled + ), + "TypingIndicatorTimeout": typing_indicator_timeout, + "ConsumptionReportInterval": consumption_report_interval, + "Notifications.NewMessage.Enabled": serialize.boolean_to_string( + notifications_new_message_enabled + ), + "Notifications.NewMessage.Template": notifications_new_message_template, + "Notifications.NewMessage.Sound": notifications_new_message_sound, + "Notifications.NewMessage.BadgeCountEnabled": serialize.boolean_to_string( + notifications_new_message_badge_count_enabled + ), + "Notifications.AddedToChannel.Enabled": serialize.boolean_to_string( + notifications_added_to_channel_enabled + ), + "Notifications.AddedToChannel.Template": notifications_added_to_channel_template, + "Notifications.AddedToChannel.Sound": notifications_added_to_channel_sound, + "Notifications.RemovedFromChannel.Enabled": serialize.boolean_to_string( + notifications_removed_from_channel_enabled + ), + "Notifications.RemovedFromChannel.Template": notifications_removed_from_channel_template, + "Notifications.RemovedFromChannel.Sound": notifications_removed_from_channel_sound, + "Notifications.InvitedToChannel.Enabled": serialize.boolean_to_string( + notifications_invited_to_channel_enabled + ), + "Notifications.InvitedToChannel.Template": notifications_invited_to_channel_template, + "Notifications.InvitedToChannel.Sound": notifications_invited_to_channel_sound, + "PreWebhookUrl": pre_webhook_url, + "PostWebhookUrl": post_webhook_url, + "WebhookMethod": webhook_method, + "WebhookFilters": serialize.map(webhook_filters, lambda e: e), + "Limits.ChannelMembers": limits_channel_members, + "Limits.UserChannels": limits_user_channels, + "Media.CompatibilityMessage": media_compatibility_message, + "PreWebhookRetryCount": pre_webhook_retry_count, + "PostWebhookRetryCount": post_webhook_retry_count, + "Notifications.LogEnabled": serialize.boolean_to_string( + notifications_log_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + if self._bindings is None: + self._bindings = BindingList( + self._version, + self._solution["sid"], + ) + return self._bindings + + @property + def channels(self) -> ChannelList: + """ + Access the channels + """ + if self._channels is None: + self._channels = ChannelList( + self._version, + self._solution["sid"], + ) + return self._channels + + @property + def roles(self) -> RoleList: + """ + Access the roles + """ + if self._roles is None: + self._roles = RoleList( + self._version, + self._solution["sid"], + ) + return self._roles + + @property + def users(self) -> UserList: + """ + Access the users + """ + if self._users is None: + self._users = UserList( + self._version, + self._solution["sid"], + ) + return self._users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create(self, friendly_name: str) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async(self, friendly_name: str) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e9ca961 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/binding.cpython-310.pyc new file mode 100644 index 0000000..9356c26 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/role.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/role.cpython-310.pyc new file mode 100644 index 0000000..38d7f3d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/__pycache__/role.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/binding.py new file mode 100644 index 0000000..24c4f65 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/binding.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BindingInstance(InstanceResource): + + class BindingType(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar date_created: + :ivar date_updated: + :ivar endpoint: + :ivar identity: + :ivar credential_sid: + :ivar binding_type: + :ivar message_types: + :ivar url: + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.binding_type: Optional["BindingInstance.BindingType"] = payload.get( + "binding_type" + ) + self.message_types: Optional[List[str]] = payload.get("message_types") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[BindingContext] = None + + @property + def _proxy(self) -> "BindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BindingContext for this BindingInstance + """ + if self._context is None: + self._context = BindingContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BindingInstance": + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BindingInstance": + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BindingContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Bindings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BindingInstance: + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BindingInstance: + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BindingInstance: + """ + Build an instance of BindingInstance + + :param payload: Payload response from the API + """ + return BindingInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BindingList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the BindingList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Bindings".format(**self._solution) + + def stream( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BindingInstance]: + """ + Streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BindingInstance]: + """ + Asynchronously streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["BindingInstance.BindingType"] binding_type: + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + binding_type=binding_type, identity=identity, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Asynchronously lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["BindingInstance.BindingType"] binding_type: + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + binding_type=binding_type, + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BindingPage(self._version, response, self._solution) + + async def page_async( + self, + binding_type: Union[List["BindingInstance.BindingType"], object] = values.unset, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Asynchronously retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param binding_type: + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BindingPage: + """ + Retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BindingPage: + """ + Asynchronously retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__init__.py new file mode 100644 index 0000000..c4e03d4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__init__.py @@ -0,0 +1,914 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v2.service.channel.invite import InviteList +from twilio.rest.ip_messaging.v2.service.channel.member import MemberList +from twilio.rest.ip_messaging.v2.service.channel.message import MessageList +from twilio.rest.ip_messaging.v2.service.channel.webhook import WebhookList + + +class ChannelInstance(InstanceResource): + + class ChannelType(object): + PUBLIC = "public" + PRIVATE = "private" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar friendly_name: + :ivar unique_name: + :ivar attributes: + :ivar type: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + :ivar members_count: + :ivar messages_count: + :ivar url: + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.attributes: Optional[str] = payload.get("attributes") + self.type: Optional["ChannelInstance.ChannelType"] = payload.get("type") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + self.members_count: Optional[int] = deserialize.integer( + payload.get("members_count") + ) + self.messages_count: Optional[int] = deserialize.integer( + payload.get("messages_count") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelContext] = None + + @property + def _proxy(self) -> "ChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelContext for this ChannelInstance + """ + if self._context is None: + self._context = ChannelContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "ChannelInstance": + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelInstance": + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The updated ChannelInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + created_by=created_by, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> "ChannelInstance": + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The updated ChannelInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + friendly_name=friendly_name, + unique_name=unique_name, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + created_by=created_by, + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + return self._proxy.invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + return self._proxy.members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + return self._proxy.messages + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + return self._proxy.webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ChannelContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Channels/{sid}".format(**self._solution) + + self._invites: Optional[InviteList] = None + self._members: Optional[MemberList] = None + self._messages: Optional[MessageList] = None + self._webhooks: Optional[WebhookList] = None + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> ChannelInstance: + """ + Fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelInstance: + """ + Asynchronous coroutine to fetch the ChannelInstance + + + :returns: The fetched ChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronous coroutine to update the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The updated ChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def invites(self) -> InviteList: + """ + Access the invites + """ + if self._invites is None: + self._invites = InviteList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._invites + + @property + def members(self) -> MemberList: + """ + Access the members + """ + if self._members is None: + self._members = MemberList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._members + + @property + def messages(self) -> MessageList: + """ + Access the messages + """ + if self._messages is None: + self._messages = MessageList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._messages + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelInstance: + """ + Build an instance of ChannelInstance + + :param payload: Payload response from the API + """ + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ChannelList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Channels".format(**self._solution) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Create the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param type: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "ChannelInstance.WebhookEnabledType", object + ] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + type: Union["ChannelInstance.ChannelType", object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + created_by: Union[str, object] = values.unset, + ) -> ChannelInstance: + """ + Asynchronously create the ChannelInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param friendly_name: + :param unique_name: + :param attributes: + :param type: + :param date_created: + :param date_updated: + :param created_by: + + :returns: The created ChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Attributes": attributes, + "Type": type, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "CreatedBy": created_by, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ChannelInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelInstance]: + """ + Streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(type=type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelInstance]: + """ + Asynchronously streams ChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(type=type, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + type=type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelInstance]: + """ + Asynchronously lists ChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["ChannelInstance.ChannelType"] type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + type=type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelPage(self._version, response, self._solution) + + async def page_async( + self, + type: Union[List["ChannelInstance.ChannelType"], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelPage: + """ + Asynchronously retrieve a single page of ChannelInstance records from the API. + Request is executed immediately + + :param type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelInstance + """ + data = values.of( + { + "Type": serialize.map(type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChannelPage: + """ + Retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChannelPage: + """ + Asynchronously retrieve a specific page of ChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ChannelContext: + """ + Constructs a ChannelContext + + :param sid: + """ + return ChannelContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0714d84 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/invite.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/invite.cpython-310.pyc new file mode 100644 index 0000000..5b1674e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/invite.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/member.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/member.cpython-310.pyc new file mode 100644 index 0000000..123954f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/member.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/message.cpython-310.pyc new file mode 100644 index 0000000..1e0212e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..a3b3d07 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/invite.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/invite.py new file mode 100644 index 0000000..0f863d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/invite.py @@ -0,0 +1,576 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InviteInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar channel_sid: + :ivar service_sid: + :ivar identity: + :ivar date_created: + :ivar date_updated: + :ivar role_sid: + :ivar created_by: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.created_by: Optional[str] = payload.get("created_by") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[InviteContext] = None + + @property + def _proxy(self) -> "InviteContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InviteContext for this InviteInstance + """ + if self._context is None: + self._context = InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InviteInstance": + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InviteInstance": + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InviteContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the InviteContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Invites/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InviteInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InviteInstance: + """ + Fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InviteInstance: + """ + Asynchronous coroutine to fetch the InviteInstance + + + :returns: The fetched InviteInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InvitePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InviteInstance: + """ + Build an instance of InviteInstance + + :param payload: Payload response from the API + """ + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InviteList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the InviteList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Invites".format( + **self._solution + ) + + def create( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Create the InviteInstance + + :param identity: + :param role_sid: + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, identity: str, role_sid: Union[str, object] = values.unset + ) -> InviteInstance: + """ + Asynchronously create the InviteInstance + + :param identity: + :param role_sid: + + :returns: The created InviteInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InviteInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InviteInstance]: + """ + Streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InviteInstance]: + """ + Asynchronously streams InviteInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InviteInstance]: + """ + Asynchronously lists InviteInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InvitePage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InvitePage: + """ + Asynchronously retrieve a single page of InviteInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InviteInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InvitePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InvitePage: + """ + Retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InvitePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InvitePage: + """ + Asynchronously retrieve a specific page of InviteInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InviteInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InvitePage(self._version, response, self._solution) + + def get(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InviteContext: + """ + Constructs a InviteContext + + :param sid: + """ + return InviteContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/member.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/member.py new file mode 100644 index 0000000..5de4520 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/member.py @@ -0,0 +1,857 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MemberInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: + :ivar account_sid: + :ivar channel_sid: + :ivar service_sid: + :ivar identity: + :ivar date_created: + :ivar date_updated: + :ivar role_sid: + :ivar last_consumed_message_index: + :ivar last_consumption_timestamp: + :ivar url: + :ivar attributes: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.role_sid: Optional[str] = payload.get("role_sid") + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.last_consumption_timestamp: Optional[datetime] = ( + deserialize.iso8601_datetime(payload.get("last_consumption_timestamp")) + ) + self.url: Optional[str] = payload.get("url") + self.attributes: Optional[str] = payload.get("attributes") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MemberContext] = None + + @property + def _proxy(self) -> "MemberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MemberContext for this MemberInstance + """ + if self._context is None: + self._context = MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MemberInstance": + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MemberInstance": + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MemberInstance": + """ + Update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The updated MemberInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> "MemberInstance": + """ + Asynchronous coroutine to update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The updated MemberInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + date_created=date_created, + date_updated=date_updated, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MemberContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Members/{sid}".format( + **self._solution + ) + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MemberInstance: + """ + Fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MemberInstance: + """ + Asynchronous coroutine to fetch the MemberInstance + + + :returns: The fetched MemberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronous coroutine to update the MemberInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The updated MemberInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MemberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MemberInstance: + """ + Build an instance of MemberInstance + + :param payload: Payload response from the API + """ + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MemberList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MemberList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Members".format( + **self._solution + ) + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Create the MemberInstance + + :param identity: + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "MemberInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> MemberInstance: + """ + Asynchronously create the MemberInstance + + :param identity: + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param last_consumed_message_index: + :param last_consumption_timestamp: + :param date_created: + :param date_updated: + :param attributes: + + :returns: The created MemberInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "Attributes": attributes, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MemberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MemberInstance]: + """ + Streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(identity=identity, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MemberInstance]: + """ + Asynchronously streams MemberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(identity=identity, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + identity=identity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + identity: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MemberInstance]: + """ + Asynchronously lists MemberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List[str] identity: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + identity=identity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MemberPage(self._version, response, self._solution) + + async def page_async( + self, + identity: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MemberPage: + """ + Asynchronously retrieve a single page of MemberInstance records from the API. + Request is executed immediately + + :param identity: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MemberInstance + """ + data = values.of( + { + "Identity": serialize.map(identity, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MemberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MemberPage: + """ + Retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MemberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MemberPage: + """ + Asynchronously retrieve a specific page of MemberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MemberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MemberPage(self._version, response, self._solution) + + def get(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MemberContext: + """ + Constructs a MemberContext + + :param sid: + """ + return MemberContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/message.py new file mode 100644 index 0000000..057571c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/message.py @@ -0,0 +1,857 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessageInstance(InstanceResource): + + class OrderType(object): + ASC = "asc" + DESC = "desc" + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: + :ivar account_sid: + :ivar attributes: + :ivar service_sid: + :ivar to: + :ivar channel_sid: + :ivar date_created: + :ivar date_updated: + :ivar last_updated_by: + :ivar was_edited: + :ivar _from: + :ivar body: + :ivar index: + :ivar type: + :ivar media: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.service_sid: Optional[str] = payload.get("service_sid") + self.to: Optional[str] = payload.get("to") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.last_updated_by: Optional[str] = payload.get("last_updated_by") + self.was_edited: Optional[bool] = payload.get("was_edited") + self._from: Optional[str] = payload.get("from") + self.body: Optional[str] = payload.get("body") + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.type: Optional[str] = payload.get("type") + self.media: Optional[Dict[str, object]] = payload.get("media") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageContext] = None + + @property + def _proxy(self) -> "MessageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageContext for this MessageInstance + """ + if self._context is None: + self._context = MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + ) + + def fetch(self) -> "MessageInstance": + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInstance": + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param from_: + + :returns: The updated MessageInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + body=body, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + last_updated_by=last_updated_by, + from_=from_, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> "MessageInstance": + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param from_: + + :returns: The updated MessageInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + body=body, + attributes=attributes, + date_created=date_created, + date_updated=date_updated, + last_updated_by=last_updated_by, + from_=from_, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the MessageContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Messages/{sid}".format( + **self._solution + ) + ) + + def delete( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + ) -> bool: + """ + Asynchronous coroutine that deletes the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> MessageInstance: + """ + Fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInstance: + """ + Asynchronous coroutine to fetch the MessageInstance + + + :returns: The fetched MessageInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param from_: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "From": from_, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + body: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronous coroutine to update the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param body: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param from_: + + :returns: The updated MessageInstance + """ + data = values.of( + { + "Body": body, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "From": from_, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInstance: + """ + Build an instance of MessageInstance + + :param payload: Payload response from the API + """ + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the MessageList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Messages".format( + **self._solution + ) + + def create( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param from_: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param body: + :param media_sid: + + :returns: The created MessageInstance + """ + + data = values.of( + { + "From": from_, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "Body": body, + "MediaSid": media_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + x_twilio_webhook_enabled: Union[ + "MessageInstance.WebhookEnabledType", object + ] = values.unset, + from_: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + date_created: Union[datetime, object] = values.unset, + date_updated: Union[datetime, object] = values.unset, + last_updated_by: Union[str, object] = values.unset, + body: Union[str, object] = values.unset, + media_sid: Union[str, object] = values.unset, + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param from_: + :param attributes: + :param date_created: + :param date_updated: + :param last_updated_by: + :param body: + :param media_sid: + + :returns: The created MessageInstance + """ + + data = values.of( + { + "From": from_, + "Attributes": attributes, + "DateCreated": serialize.iso8601_datetime(date_created), + "DateUpdated": serialize.iso8601_datetime(date_updated), + "LastUpdatedBy": last_updated_by, + "Body": body, + "MediaSid": media_sid, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInstance]: + """ + Streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(order=order, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInstance]: + """ + Asynchronously streams MessageInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(order=order, page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInstance]: + """ + Asynchronously lists MessageInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "MessageInstance.OrderType" order: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagePage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["MessageInstance.OrderType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagePage: + """ + Asynchronously retrieve a single page of MessageInstance records from the API. + Request is executed immediately + + :param order: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInstance + """ + data = values.of( + { + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagePage: + """ + Retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagePage: + """ + Asynchronously retrieve a specific page of MessageInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagePage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageContext: + """ + Constructs a MessageContext + + :param sid: + """ + return MessageContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/webhook.py new file mode 100644 index 0000000..633acfd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/channel/webhook.py @@ -0,0 +1,770 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebhookInstance(InstanceResource): + + class Method(object): + GET = "GET" + POST = "POST" + + class Type(object): + WEBHOOK = "webhook" + TRIGGER = "trigger" + STUDIO = "studio" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar channel_sid: + :ivar type: + :ivar url: + :ivar configuration: + :ivar date_created: + :ivar date_updated: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + channel_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.type: Optional[str] = payload.get("type") + self.url: Optional[str] = payload.get("url") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid or self.sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + configuration_retry_count=configuration_retry_count, + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + configuration_url=configuration_url, + configuration_method=configuration_method, + configuration_filters=configuration_filters, + configuration_triggers=configuration_triggers, + configuration_flow_sid=configuration_flow_sid, + configuration_retry_count=configuration_retry_count, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, channel_sid: str, sid: str): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Channels/{channel_sid}/Webhooks/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebhookInstance: + """ + Build an instance of WebhookInstance + + :param payload: Payload response from the API + """ + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version, service_sid: str, channel_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param service_sid: + :param channel_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "channel_sid": channel_sid, + } + self._uri = "/Services/{service_sid}/Channels/{channel_sid}/Webhooks".format( + **self._solution + ) + + def create( + self, + type: "WebhookInstance.Type", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Create the WebhookInstance + + :param type: + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Type": type, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def create_async( + self, + type: "WebhookInstance.Type", + configuration_url: Union[str, object] = values.unset, + configuration_method: Union["WebhookInstance.Method", object] = values.unset, + configuration_filters: Union[List[str], object] = values.unset, + configuration_triggers: Union[List[str], object] = values.unset, + configuration_flow_sid: Union[str, object] = values.unset, + configuration_retry_count: Union[int, object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronously create the WebhookInstance + + :param type: + :param configuration_url: + :param configuration_method: + :param configuration_filters: + :param configuration_triggers: + :param configuration_flow_sid: + :param configuration_retry_count: + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "Type": type, + "Configuration.Url": configuration_url, + "Configuration.Method": configuration_method, + "Configuration.Filters": serialize.map( + configuration_filters, lambda e: e + ), + "Configuration.Triggers": serialize.map( + configuration_triggers, lambda e: e + ), + "Configuration.FlowSid": configuration_flow_sid, + "Configuration.RetryCount": configuration_retry_count, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebhookInstance]: + """ + Streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebhookInstance]: + """ + Asynchronously streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Asynchronously lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebhookPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Asynchronously retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebhookPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WebhookPage: + """ + Retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WebhookPage: + """ + Asynchronously retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + def get(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: + """ + return WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: + """ + return WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + channel_sid=self._solution["channel_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/role.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/role.py new file mode 100644 index 0000000..91fc1af --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/role.py @@ -0,0 +1,615 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoleInstance(InstanceResource): + + class RoleType(object): + CHANNEL = "channel" + DEPLOYMENT = "deployment" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar friendly_name: + :ivar type: + :ivar permissions: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["RoleInstance.RoleType"] = payload.get("type") + self.permissions: Optional[List[str]] = payload.get("permissions") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoleContext] = None + + @property + def _proxy(self) -> "RoleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoleContext for this RoleInstance + """ + if self._context is None: + self._context = RoleContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoleInstance": + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoleInstance": + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + return await self._proxy.fetch_async() + + def update(self, permission: List[str]) -> "RoleInstance": + """ + Update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + return self._proxy.update( + permission=permission, + ) + + async def update_async(self, permission: List[str]) -> "RoleInstance": + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + return await self._proxy.update_async( + permission=permission, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoleContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the RoleContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Roles/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoleInstance: + """ + Fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoleInstance: + """ + Asynchronous coroutine to fetch the RoleInstance + + + :returns: The fetched RoleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, permission: List[str]) -> RoleInstance: + """ + Update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, permission: List[str]) -> RoleInstance: + """ + Asynchronous coroutine to update the RoleInstance + + :param permission: + + :returns: The updated RoleInstance + """ + data = values.of( + { + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RolePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoleInstance: + """ + Build an instance of RoleInstance + + :param payload: Payload response from the API + """ + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoleList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the RoleList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Roles".format(**self._solution) + + def create( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Create the RoleInstance + + :param friendly_name: + :param type: + :param permission: + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, friendly_name: str, type: "RoleInstance.RoleType", permission: List[str] + ) -> RoleInstance: + """ + Asynchronously create the RoleInstance + + :param friendly_name: + :param type: + :param permission: + + :returns: The created RoleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Permission": serialize.map(permission, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoleInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoleInstance]: + """ + Streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoleInstance]: + """ + Asynchronously streams RoleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoleInstance]: + """ + Asynchronously lists RoleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RolePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RolePage: + """ + Asynchronously retrieve a single page of RoleInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoleInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RolePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RolePage: + """ + Retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RolePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RolePage: + """ + Asynchronously retrieve a specific page of RoleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RolePage(self._version, response, self._solution) + + def get(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoleContext: + """ + Constructs a RoleContext + + :param sid: + """ + return RoleContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__init__.py new file mode 100644 index 0000000..1b3ba1b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__init__.py @@ -0,0 +1,758 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.ip_messaging.v2.service.user.user_binding import UserBindingList +from twilio.rest.ip_messaging.v2.service.user.user_channel import UserChannelList + + +class UserInstance(InstanceResource): + + class WebhookEnabledType(object): + TRUE = "true" + FALSE = "false" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar attributes: + :ivar friendly_name: + :ivar role_sid: + :ivar identity: + :ivar is_online: + :ivar is_notifiable: + :ivar date_created: + :ivar date_updated: + :ivar joined_channels_count: + :ivar links: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.role_sid: Optional[str] = payload.get("role_sid") + self.identity: Optional[str] = payload.get("identity") + self.is_online: Optional[bool] = payload.get("is_online") + self.is_notifiable: Optional[bool] = payload.get("is_notifiable") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.joined_channels_count: Optional[int] = deserialize.integer( + payload.get("joined_channels_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserContext] = None + + @property + def _proxy(self) -> "UserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserContext for this UserInstance + """ + if self._context is None: + self._context = UserContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserInstance": + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserInstance": + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + return self._proxy.update( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "UserInstance": + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + return await self._proxy.update_async( + x_twilio_webhook_enabled=x_twilio_webhook_enabled, + role_sid=role_sid, + attributes=attributes, + friendly_name=friendly_name, + ) + + @property + def user_bindings(self) -> UserBindingList: + """ + Access the user_bindings + """ + return self._proxy.user_bindings + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + return self._proxy.user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the UserContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{sid}".format(**self._solution) + + self._user_bindings: Optional[UserBindingList] = None + self._user_channels: Optional[UserChannelList] = None + + def delete(self) -> bool: + """ + Deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserInstance: + """ + Fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserInstance: + """ + Asynchronous coroutine to fetch the UserInstance + + + :returns: The fetched UserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronous coroutine to update the UserInstance + + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The updated UserInstance + """ + data = values.of( + { + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def user_bindings(self) -> UserBindingList: + """ + Access the user_bindings + """ + if self._user_bindings is None: + self._user_bindings = UserBindingList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_bindings + + @property + def user_channels(self) -> UserChannelList: + """ + Access the user_channels + """ + if self._user_channels is None: + self._user_channels = UserChannelList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._user_channels + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserInstance: + """ + Build an instance of UserInstance + + :param payload: Payload response from the API + """ + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the UserList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Users".format(**self._solution) + + def create( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Create the UserInstance + + :param identity: + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + x_twilio_webhook_enabled: Union[ + "UserInstance.WebhookEnabledType", object + ] = values.unset, + role_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> UserInstance: + """ + Asynchronously create the UserInstance + + :param identity: + :param x_twilio_webhook_enabled: The X-Twilio-Webhook-Enabled HTTP request header + :param role_sid: + :param attributes: + :param friendly_name: + + :returns: The created UserInstance + """ + + data = values.of( + { + "Identity": identity, + "RoleSid": role_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + } + ) + headers = values.of( + { + "X-Twilio-Webhook-Enabled": x_twilio_webhook_enabled, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return UserInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserInstance]: + """ + Streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserInstance]: + """ + Asynchronously streams UserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserInstance]: + """ + Asynchronously lists UserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserPage: + """ + Asynchronously retrieve a single page of UserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserPage: + """ + Retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserPage: + """ + Asynchronously retrieve a specific page of UserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> UserContext: + """ + Constructs a UserContext + + :param sid: + """ + return UserContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1372f3b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_binding.cpython-310.pyc new file mode 100644 index 0000000..5734d85 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_channel.cpython-310.pyc new file mode 100644 index 0000000..81833ac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/__pycache__/user_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_binding.py new file mode 100644 index 0000000..575a295 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_binding.py @@ -0,0 +1,536 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserBindingInstance(InstanceResource): + + class BindingType(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: + :ivar account_sid: + :ivar service_sid: + :ivar date_created: + :ivar date_updated: + :ivar endpoint: + :ivar identity: + :ivar user_sid: + :ivar credential_sid: + :ivar binding_type: + :ivar message_types: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + user_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.user_sid: Optional[str] = payload.get("user_sid") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.binding_type: Optional["UserBindingInstance.BindingType"] = payload.get( + "binding_type" + ) + self.message_types: Optional[List[str]] = payload.get("message_types") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "sid": sid or self.sid, + } + self._context: Optional[UserBindingContext] = None + + @property + def _proxy(self) -> "UserBindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserBindingContext for this UserBindingInstance + """ + if self._context is None: + self._context = UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserBindingInstance": + """ + Fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserBindingInstance": + """ + Asynchronous coroutine to fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserBindingContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, user_sid: str, sid: str): + """ + Initialize the UserBindingContext + + :param version: Version that contains the resource + :param service_sid: + :param user_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Bindings/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserBindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserBindingInstance: + """ + Fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UserBindingInstance: + """ + Asynchronous coroutine to fetch the UserBindingInstance + + + :returns: The fetched UserBindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserBindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserBindingInstance: + """ + Build an instance of UserBindingInstance + + :param payload: Payload response from the API + """ + return UserBindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserBindingList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserBindingList + + :param version: Version that contains the resource + :param service_sid: + :param user_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Bindings".format( + **self._solution + ) + + def stream( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserBindingInstance]: + """ + Streams UserBindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["UserBindingInstance.BindingType"] binding_type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(binding_type=binding_type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserBindingInstance]: + """ + Asynchronously streams UserBindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param List["UserBindingInstance.BindingType"] binding_type: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + binding_type=binding_type, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserBindingInstance]: + """ + Lists UserBindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["UserBindingInstance.BindingType"] binding_type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + binding_type=binding_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserBindingInstance]: + """ + Asynchronously lists UserBindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param List["UserBindingInstance.BindingType"] binding_type: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + binding_type=binding_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserBindingPage: + """ + Retrieve a single page of UserBindingInstance records from the API. + Request is executed immediately + + :param binding_type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserBindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserBindingPage(self._version, response, self._solution) + + async def page_async( + self, + binding_type: Union[ + List["UserBindingInstance.BindingType"], object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserBindingPage: + """ + Asynchronously retrieve a single page of UserBindingInstance records from the API. + Request is executed immediately + + :param binding_type: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserBindingInstance + """ + data = values.of( + { + "BindingType": serialize.map(binding_type, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserBindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserBindingPage: + """ + Retrieve a specific page of UserBindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserBindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserBindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserBindingPage: + """ + Asynchronously retrieve a specific page of UserBindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserBindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserBindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> UserBindingContext: + """ + Constructs a UserBindingContext + + :param sid: + """ + return UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> UserBindingContext: + """ + Constructs a UserBindingContext + + :param sid: + """ + return UserBindingContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_channel.py new file mode 100644 index 0000000..d89e6cb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/ip_messaging/v2/service/user/user_channel.py @@ -0,0 +1,642 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Ip_messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UserChannelInstance(InstanceResource): + + class ChannelStatus(object): + JOINED = "joined" + INVITED = "invited" + NOT_PARTICIPATING = "not_participating" + + class NotificationLevel(object): + DEFAULT = "default" + MUTED = "muted" + + """ + :ivar account_sid: + :ivar service_sid: + :ivar channel_sid: + :ivar user_sid: + :ivar member_sid: + :ivar status: + :ivar last_consumed_message_index: + :ivar unread_messages_count: + :ivar links: + :ivar url: + :ivar notification_level: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + user_sid: str, + channel_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.channel_sid: Optional[str] = payload.get("channel_sid") + self.user_sid: Optional[str] = payload.get("user_sid") + self.member_sid: Optional[str] = payload.get("member_sid") + self.status: Optional["UserChannelInstance.ChannelStatus"] = payload.get( + "status" + ) + self.last_consumed_message_index: Optional[int] = deserialize.integer( + payload.get("last_consumed_message_index") + ) + self.unread_messages_count: Optional[int] = deserialize.integer( + payload.get("unread_messages_count") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + self.url: Optional[str] = payload.get("url") + self.notification_level: Optional["UserChannelInstance.NotificationLevel"] = ( + payload.get("notification_level") + ) + + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "channel_sid": channel_sid or self.channel_sid, + } + self._context: Optional[UserChannelContext] = None + + @property + def _proxy(self) -> "UserChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UserChannelContext for this UserChannelInstance + """ + if self._context is None: + self._context = UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UserChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UserChannelInstance": + """ + Fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UserChannelInstance": + """ + Asynchronous coroutine to fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> "UserChannelInstance": + """ + Update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: + :param last_consumption_timestamp: + + :returns: The updated UserChannelInstance + """ + return self._proxy.update( + notification_level=notification_level, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + ) + + async def update_async( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> "UserChannelInstance": + """ + Asynchronous coroutine to update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: + :param last_consumption_timestamp: + + :returns: The updated UserChannelInstance + """ + return await self._proxy.update_async( + notification_level=notification_level, + last_consumed_message_index=last_consumed_message_index, + last_consumption_timestamp=last_consumption_timestamp, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, user_sid: str, channel_sid: str + ): + """ + Initialize the UserChannelContext + + :param version: Version that contains the resource + :param service_sid: + :param user_sid: + :param channel_sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + "channel_sid": channel_sid, + } + self._uri = ( + "/Services/{service_sid}/Users/{user_sid}/Channels/{channel_sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the UserChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UserChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UserChannelInstance: + """ + Fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def fetch_async(self) -> UserChannelInstance: + """ + Asynchronous coroutine to fetch the UserChannelInstance + + + :returns: The fetched UserChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def update( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> UserChannelInstance: + """ + Update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: + :param last_consumption_timestamp: + + :returns: The updated UserChannelInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + async def update_async( + self, + notification_level: Union[ + "UserChannelInstance.NotificationLevel", object + ] = values.unset, + last_consumed_message_index: Union[int, object] = values.unset, + last_consumption_timestamp: Union[datetime, object] = values.unset, + ) -> UserChannelInstance: + """ + Asynchronous coroutine to update the UserChannelInstance + + :param notification_level: + :param last_consumed_message_index: + :param last_consumption_timestamp: + + :returns: The updated UserChannelInstance + """ + data = values.of( + { + "NotificationLevel": notification_level, + "LastConsumedMessageIndex": last_consumed_message_index, + "LastConsumptionTimestamp": serialize.iso8601_datetime( + last_consumption_timestamp + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=self._solution["channel_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UserChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UserChannelInstance: + """ + Build an instance of UserChannelInstance + + :param payload: Payload response from the API + """ + return UserChannelInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UserChannelList(ListResource): + + def __init__(self, version: Version, service_sid: str, user_sid: str): + """ + Initialize the UserChannelList + + :param version: Version that contains the resource + :param service_sid: + :param user_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "user_sid": user_sid, + } + self._uri = "/Services/{service_sid}/Users/{user_sid}/Channels".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UserChannelInstance]: + """ + Streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UserChannelInstance]: + """ + Asynchronously streams UserChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UserChannelInstance]: + """ + Asynchronously lists UserChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UserChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UserChannelPage: + """ + Asynchronously retrieve a single page of UserChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UserChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UserChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UserChannelPage: + """ + Retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UserChannelPage: + """ + Asynchronously retrieve a specific page of UserChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UserChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UserChannelPage(self._version, response, self._solution) + + def get(self, channel_sid: str) -> UserChannelContext: + """ + Constructs a UserChannelContext + + :param channel_sid: + """ + return UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=channel_sid, + ) + + def __call__(self, channel_sid: str) -> UserChannelContext: + """ + Constructs a UserChannelContext + + :param channel_sid: + """ + return UserChannelContext( + self._version, + service_sid=self._solution["service_sid"], + user_sid=self._solution["user_sid"], + channel_sid=channel_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/LookupsBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/LookupsBase.py new file mode 100644 index 0000000..e24f8dc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/LookupsBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.lookups.v1 import V1 +from twilio.rest.lookups.v2 import V2 + + +class LookupsBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Lookups Domain + + :returns: Domain for Lookups + """ + super().__init__(twilio, "https://lookups.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Lookups + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Lookups + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__init__.py new file mode 100644 index 0000000..6a984f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.lookups.LookupsBase import LookupsBase +from twilio.rest.lookups.v1.phone_number import PhoneNumberList + + +class Lookups(LookupsBase): + @property + def phone_numbers(self) -> PhoneNumberList: + warn( + "phone_numbers is deprecated. Use v1.phone_numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.phone_numbers diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/LookupsBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/LookupsBase.cpython-310.pyc new file mode 100644 index 0000000..b3fc9a9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/LookupsBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2610a93 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__init__.py new file mode 100644 index 0000000..72204da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Lookups + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.lookups.v1.phone_number import PhoneNumberList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Lookups + + :param domain: The Twilio.lookups domain + """ + super().__init__(domain, "v1") + self._phone_numbers: Optional[PhoneNumberList] = None + + @property + def phone_numbers(self) -> PhoneNumberList: + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList(self) + return self._phone_numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c085259 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..a6cd5af Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/phone_number.py new file mode 100644 index 0000000..fe521c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v1/phone_number.py @@ -0,0 +1,260 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Lookups + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PhoneNumberInstance(InstanceResource): + """ + :ivar caller_name: The name of the phone number's owner. If `null`, that information was not available. + :ivar country_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) for the phone number. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar national_format: The phone number, in national format. + :ivar carrier: The telecom company that provides the phone number. + :ivar add_ons: A JSON string with the results of the Add-ons you specified in the `add_ons` parameters. For the format of the object, see [Using Add-ons](https://www.twilio.com/docs/add-ons). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + phone_number: Optional[str] = None, + ): + super().__init__(version) + + self.caller_name: Optional[Dict[str, object]] = payload.get("caller_name") + self.country_code: Optional[str] = payload.get("country_code") + self.phone_number: Optional[str] = payload.get("phone_number") + self.national_format: Optional[str] = payload.get("national_format") + self.carrier: Optional[Dict[str, object]] = payload.get("carrier") + self.add_ons: Optional[Dict[str, object]] = payload.get("add_ons") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "phone_number": phone_number or self.phone_number, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + phone_number=self._solution["phone_number"], + ) + return self._context + + def fetch( + self, + country_code: Union[str, object] = values.unset, + type: Union[List[str], object] = values.unset, + add_ons: Union[List[str], object] = values.unset, + add_ons_data: Union[Dict[str, object], object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + :param country_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the phone number to fetch. This is used to specify the country when the phone number is provided in a national format. + :param type: The type of information to return. Can be: `carrier` or `caller-name`. The default is null. Carrier information costs $0.005 per phone number looked up. Caller Name information is currently available only in the US and costs $0.01 per phone number looked up. To retrieve both types on information, specify this parameter twice; once with `carrier` and once with `caller-name` as the value. + :param add_ons: The `unique_name` of an Add-on you would like to invoke. Can be the `unique_name` of an Add-on that is installed on your account. You can specify multiple instances of this parameter to invoke multiple Add-ons. For more information about Add-ons, see the [Add-ons documentation](https://www.twilio.com/docs/add-ons). + :param add_ons_data: Data specific to the add-on you would like to invoke. The content and format of this value depends on the add-on. + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch( + country_code=country_code, + type=type, + add_ons=add_ons, + add_ons_data=add_ons_data, + ) + + async def fetch_async( + self, + country_code: Union[str, object] = values.unset, + type: Union[List[str], object] = values.unset, + add_ons: Union[List[str], object] = values.unset, + add_ons_data: Union[Dict[str, object], object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + :param country_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the phone number to fetch. This is used to specify the country when the phone number is provided in a national format. + :param type: The type of information to return. Can be: `carrier` or `caller-name`. The default is null. Carrier information costs $0.005 per phone number looked up. Caller Name information is currently available only in the US and costs $0.01 per phone number looked up. To retrieve both types on information, specify this parameter twice; once with `carrier` and once with `caller-name` as the value. + :param add_ons: The `unique_name` of an Add-on you would like to invoke. Can be the `unique_name` of an Add-on that is installed on your account. You can specify multiple instances of this parameter to invoke multiple Add-ons. For more information about Add-ons, see the [Add-ons documentation](https://www.twilio.com/docs/add-ons). + :param add_ons_data: Data specific to the add-on you would like to invoke. The content and format of this value depends on the add-on. + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async( + country_code=country_code, + type=type, + add_ons=add_ons, + add_ons_data=add_ons_data, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, phone_number: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param phone_number: The phone number to lookup in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "phone_number": phone_number, + } + self._uri = "/PhoneNumbers/{phone_number}".format(**self._solution) + + def fetch( + self, + country_code: Union[str, object] = values.unset, + type: Union[List[str], object] = values.unset, + add_ons: Union[List[str], object] = values.unset, + add_ons_data: Union[Dict[str, object], object] = values.unset, + ) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + :param country_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the phone number to fetch. This is used to specify the country when the phone number is provided in a national format. + :param type: The type of information to return. Can be: `carrier` or `caller-name`. The default is null. Carrier information costs $0.005 per phone number looked up. Caller Name information is currently available only in the US and costs $0.01 per phone number looked up. To retrieve both types on information, specify this parameter twice; once with `carrier` and once with `caller-name` as the value. + :param add_ons: The `unique_name` of an Add-on you would like to invoke. Can be the `unique_name` of an Add-on that is installed on your account. You can specify multiple instances of this parameter to invoke multiple Add-ons. For more information about Add-ons, see the [Add-ons documentation](https://www.twilio.com/docs/add-ons). + :param add_ons_data: Data specific to the add-on you would like to invoke. The content and format of this value depends on the add-on. + + :returns: The fetched PhoneNumberInstance + """ + + data = values.of( + { + "CountryCode": country_code, + "Type": serialize.map(type, lambda e: e), + "AddOns": serialize.map(add_ons, lambda e: e), + } + ) + data.update(serialize.prefixed_collapsible_map(add_ons_data, "AddOns")) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + async def fetch_async( + self, + country_code: Union[str, object] = values.unset, + type: Union[List[str], object] = values.unset, + add_ons: Union[List[str], object] = values.unset, + add_ons_data: Union[Dict[str, object], object] = values.unset, + ) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + :param country_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the phone number to fetch. This is used to specify the country when the phone number is provided in a national format. + :param type: The type of information to return. Can be: `carrier` or `caller-name`. The default is null. Carrier information costs $0.005 per phone number looked up. Caller Name information is currently available only in the US and costs $0.01 per phone number looked up. To retrieve both types on information, specify this parameter twice; once with `carrier` and once with `caller-name` as the value. + :param add_ons: The `unique_name` of an Add-on you would like to invoke. Can be the `unique_name` of an Add-on that is installed on your account. You can specify multiple instances of this parameter to invoke multiple Add-ons. For more information about Add-ons, see the [Add-ons documentation](https://www.twilio.com/docs/add-ons). + :param add_ons_data: Data specific to the add-on you would like to invoke. The content and format of this value depends on the add-on. + + :returns: The fetched PhoneNumberInstance + """ + + data = values.of( + { + "CountryCode": country_code, + "Type": serialize.map(type, lambda e: e), + "AddOns": serialize.map(add_ons, lambda e: e), + } + ) + data.update(serialize.prefixed_collapsible_map(add_ons_data, "AddOns")) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number to lookup in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __call__(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number to lookup in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__init__.py new file mode 100644 index 0000000..d795aea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Lookups + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.lookups.v2.phone_number import PhoneNumberList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Lookups + + :param domain: The Twilio.lookups domain + """ + super().__init__(domain, "v2") + self._phone_numbers: Optional[PhoneNumberList] = None + + @property + def phone_numbers(self) -> PhoneNumberList: + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList(self) + return self._phone_numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7f27769 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..431945c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/phone_number.py new file mode 100644 index 0000000..d9c73c2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/lookups/v2/phone_number.py @@ -0,0 +1,421 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Lookups + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PhoneNumberInstance(InstanceResource): + + class ValidationError(object): + TOO_SHORT = "TOO_SHORT" + TOO_LONG = "TOO_LONG" + INVALID_BUT_POSSIBLE = "INVALID_BUT_POSSIBLE" + INVALID_COUNTRY_CODE = "INVALID_COUNTRY_CODE" + INVALID_LENGTH = "INVALID_LENGTH" + NOT_A_NUMBER = "NOT_A_NUMBER" + + """ + :ivar calling_country_code: International dialing prefix of the phone number defined in the E.164 standard. + :ivar country_code: The phone number's [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar national_format: The phone number in [national format](https://en.wikipedia.org/wiki/National_conventions_for_writing_telephone_numbers). + :ivar valid: Boolean which indicates if the phone number is in a valid range that can be freely assigned by a carrier to a user. + :ivar validation_errors: Contains reasons why a phone number is invalid. Possible values: TOO_SHORT, TOO_LONG, INVALID_BUT_POSSIBLE, INVALID_COUNTRY_CODE, INVALID_LENGTH, NOT_A_NUMBER. + :ivar caller_name: An object that contains caller name information based on [CNAM](https://support.twilio.com/hc/en-us/articles/360051670533-Getting-Started-with-CNAM-Caller-ID). + :ivar sim_swap: An object that contains information on the last date the subscriber identity module (SIM) was changed for a mobile phone number. + :ivar call_forwarding: An object that contains information on the unconditional call forwarding status of mobile phone number. + :ivar line_status: An object that contains line status information for a mobile phone number. + :ivar line_type_intelligence: An object that contains line type information including the carrier name, mobile country code, and mobile network code. + :ivar identity_match: An object that contains identity match information. The result of comparing user-provided information including name, address, date of birth, national ID, against authoritative phone-based data sources + :ivar reassigned_number: An object that contains reassigned number information. Reassigned Numbers will return a phone number's reassignment status given a phone number and date + :ivar sms_pumping_risk: An object that contains information on if a phone number has been currently or previously blocked by Verify Fraud Guard for receiving malicious SMS pumping traffic as well as other signals associated with risky carriers and low conversion rates. + :ivar phone_number_quality_score: An object that contains information of a mobile phone number quality score. Quality score will return a risk score about the phone number. + :ivar pre_fill: An object that contains pre fill information. pre_fill will return PII information associated with the phone number like first name, last name, address line, country code, state and postal code. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + phone_number: Optional[str] = None, + ): + super().__init__(version) + + self.calling_country_code: Optional[str] = payload.get("calling_country_code") + self.country_code: Optional[str] = payload.get("country_code") + self.phone_number: Optional[str] = payload.get("phone_number") + self.national_format: Optional[str] = payload.get("national_format") + self.valid: Optional[bool] = payload.get("valid") + self.validation_errors: Optional[ + List["PhoneNumberInstance.ValidationError"] + ] = payload.get("validation_errors") + self.caller_name: Optional[Dict[str, object]] = payload.get("caller_name") + self.sim_swap: Optional[Dict[str, object]] = payload.get("sim_swap") + self.call_forwarding: Optional[Dict[str, object]] = payload.get( + "call_forwarding" + ) + self.line_status: Optional[Dict[str, object]] = payload.get("line_status") + self.line_type_intelligence: Optional[Dict[str, object]] = payload.get( + "line_type_intelligence" + ) + self.identity_match: Optional[Dict[str, object]] = payload.get("identity_match") + self.reassigned_number: Optional[Dict[str, object]] = payload.get( + "reassigned_number" + ) + self.sms_pumping_risk: Optional[Dict[str, object]] = payload.get( + "sms_pumping_risk" + ) + self.phone_number_quality_score: Optional[Dict[str, object]] = payload.get( + "phone_number_quality_score" + ) + self.pre_fill: Optional[Dict[str, object]] = payload.get("pre_fill") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "phone_number": phone_number or self.phone_number, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + phone_number=self._solution["phone_number"], + ) + return self._context + + def fetch( + self, + fields: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + address_line1: Union[str, object] = values.unset, + address_line2: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + national_id: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + last_verified_date: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + :param fields: A comma-separated list of fields to return. Possible values are validation, caller_name, sim_swap, call_forwarding, line_status, line_type_intelligence, identity_match, reassigned_number, sms_pumping_risk, phone_number_quality_score, pre_fill. + :param country_code: The [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) used if the phone number provided is in national format. + :param first_name: User’s first name. This query parameter is only used (optionally) for identity_match package requests. + :param last_name: User’s last name. This query parameter is only used (optionally) for identity_match package requests. + :param address_line1: User’s first address line. This query parameter is only used (optionally) for identity_match package requests. + :param address_line2: User’s second address line. This query parameter is only used (optionally) for identity_match package requests. + :param city: User’s city. This query parameter is only used (optionally) for identity_match package requests. + :param state: User’s country subdivision, such as state, province, or locality. This query parameter is only used (optionally) for identity_match package requests. + :param postal_code: User’s postal zip code. This query parameter is only used (optionally) for identity_match package requests. + :param address_country_code: User’s country, up to two characters. This query parameter is only used (optionally) for identity_match package requests. + :param national_id: User’s national ID, such as SSN or Passport ID. This query parameter is only used (optionally) for identity_match package requests. + :param date_of_birth: User’s date of birth, in YYYYMMDD format. This query parameter is only used (optionally) for identity_match package requests. + :param last_verified_date: The date you obtained consent to call or text the end-user of the phone number or a date on which you are reasonably certain that the end-user could still be reached at that number. This query parameter is only used (optionally) for reassigned_number package requests. + :param verification_sid: The unique identifier associated with a verification process through verify API. This query parameter is only used (optionally) for pre_fill package requests. + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch( + fields=fields, + country_code=country_code, + first_name=first_name, + last_name=last_name, + address_line1=address_line1, + address_line2=address_line2, + city=city, + state=state, + postal_code=postal_code, + address_country_code=address_country_code, + national_id=national_id, + date_of_birth=date_of_birth, + last_verified_date=last_verified_date, + verification_sid=verification_sid, + ) + + async def fetch_async( + self, + fields: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + address_line1: Union[str, object] = values.unset, + address_line2: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + national_id: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + last_verified_date: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + :param fields: A comma-separated list of fields to return. Possible values are validation, caller_name, sim_swap, call_forwarding, line_status, line_type_intelligence, identity_match, reassigned_number, sms_pumping_risk, phone_number_quality_score, pre_fill. + :param country_code: The [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) used if the phone number provided is in national format. + :param first_name: User’s first name. This query parameter is only used (optionally) for identity_match package requests. + :param last_name: User’s last name. This query parameter is only used (optionally) for identity_match package requests. + :param address_line1: User’s first address line. This query parameter is only used (optionally) for identity_match package requests. + :param address_line2: User’s second address line. This query parameter is only used (optionally) for identity_match package requests. + :param city: User’s city. This query parameter is only used (optionally) for identity_match package requests. + :param state: User’s country subdivision, such as state, province, or locality. This query parameter is only used (optionally) for identity_match package requests. + :param postal_code: User’s postal zip code. This query parameter is only used (optionally) for identity_match package requests. + :param address_country_code: User’s country, up to two characters. This query parameter is only used (optionally) for identity_match package requests. + :param national_id: User’s national ID, such as SSN or Passport ID. This query parameter is only used (optionally) for identity_match package requests. + :param date_of_birth: User’s date of birth, in YYYYMMDD format. This query parameter is only used (optionally) for identity_match package requests. + :param last_verified_date: The date you obtained consent to call or text the end-user of the phone number or a date on which you are reasonably certain that the end-user could still be reached at that number. This query parameter is only used (optionally) for reassigned_number package requests. + :param verification_sid: The unique identifier associated with a verification process through verify API. This query parameter is only used (optionally) for pre_fill package requests. + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async( + fields=fields, + country_code=country_code, + first_name=first_name, + last_name=last_name, + address_line1=address_line1, + address_line2=address_line2, + city=city, + state=state, + postal_code=postal_code, + address_country_code=address_country_code, + national_id=national_id, + date_of_birth=date_of_birth, + last_verified_date=last_verified_date, + verification_sid=verification_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, phone_number: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param phone_number: The phone number to lookup in E.164 or national format. Default country code is +1 (North America). + """ + super().__init__(version) + + # Path Solution + self._solution = { + "phone_number": phone_number, + } + self._uri = "/PhoneNumbers/{phone_number}".format(**self._solution) + + def fetch( + self, + fields: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + address_line1: Union[str, object] = values.unset, + address_line2: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + national_id: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + last_verified_date: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + :param fields: A comma-separated list of fields to return. Possible values are validation, caller_name, sim_swap, call_forwarding, line_status, line_type_intelligence, identity_match, reassigned_number, sms_pumping_risk, phone_number_quality_score, pre_fill. + :param country_code: The [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) used if the phone number provided is in national format. + :param first_name: User’s first name. This query parameter is only used (optionally) for identity_match package requests. + :param last_name: User’s last name. This query parameter is only used (optionally) for identity_match package requests. + :param address_line1: User’s first address line. This query parameter is only used (optionally) for identity_match package requests. + :param address_line2: User’s second address line. This query parameter is only used (optionally) for identity_match package requests. + :param city: User’s city. This query parameter is only used (optionally) for identity_match package requests. + :param state: User’s country subdivision, such as state, province, or locality. This query parameter is only used (optionally) for identity_match package requests. + :param postal_code: User’s postal zip code. This query parameter is only used (optionally) for identity_match package requests. + :param address_country_code: User’s country, up to two characters. This query parameter is only used (optionally) for identity_match package requests. + :param national_id: User’s national ID, such as SSN or Passport ID. This query parameter is only used (optionally) for identity_match package requests. + :param date_of_birth: User’s date of birth, in YYYYMMDD format. This query parameter is only used (optionally) for identity_match package requests. + :param last_verified_date: The date you obtained consent to call or text the end-user of the phone number or a date on which you are reasonably certain that the end-user could still be reached at that number. This query parameter is only used (optionally) for reassigned_number package requests. + :param verification_sid: The unique identifier associated with a verification process through verify API. This query parameter is only used (optionally) for pre_fill package requests. + + :returns: The fetched PhoneNumberInstance + """ + + data = values.of( + { + "Fields": fields, + "CountryCode": country_code, + "FirstName": first_name, + "LastName": last_name, + "AddressLine1": address_line1, + "AddressLine2": address_line2, + "City": city, + "State": state, + "PostalCode": postal_code, + "AddressCountryCode": address_country_code, + "NationalId": national_id, + "DateOfBirth": date_of_birth, + "LastVerifiedDate": last_verified_date, + "VerificationSid": verification_sid, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + async def fetch_async( + self, + fields: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + address_line1: Union[str, object] = values.unset, + address_line2: Union[str, object] = values.unset, + city: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + national_id: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + last_verified_date: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + :param fields: A comma-separated list of fields to return. Possible values are validation, caller_name, sim_swap, call_forwarding, line_status, line_type_intelligence, identity_match, reassigned_number, sms_pumping_risk, phone_number_quality_score, pre_fill. + :param country_code: The [country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) used if the phone number provided is in national format. + :param first_name: User’s first name. This query parameter is only used (optionally) for identity_match package requests. + :param last_name: User’s last name. This query parameter is only used (optionally) for identity_match package requests. + :param address_line1: User’s first address line. This query parameter is only used (optionally) for identity_match package requests. + :param address_line2: User’s second address line. This query parameter is only used (optionally) for identity_match package requests. + :param city: User’s city. This query parameter is only used (optionally) for identity_match package requests. + :param state: User’s country subdivision, such as state, province, or locality. This query parameter is only used (optionally) for identity_match package requests. + :param postal_code: User’s postal zip code. This query parameter is only used (optionally) for identity_match package requests. + :param address_country_code: User’s country, up to two characters. This query parameter is only used (optionally) for identity_match package requests. + :param national_id: User’s national ID, such as SSN or Passport ID. This query parameter is only used (optionally) for identity_match package requests. + :param date_of_birth: User’s date of birth, in YYYYMMDD format. This query parameter is only used (optionally) for identity_match package requests. + :param last_verified_date: The date you obtained consent to call or text the end-user of the phone number or a date on which you are reasonably certain that the end-user could still be reached at that number. This query parameter is only used (optionally) for reassigned_number package requests. + :param verification_sid: The unique identifier associated with a verification process through verify API. This query parameter is only used (optionally) for pre_fill package requests. + + :returns: The fetched PhoneNumberInstance + """ + + data = values.of( + { + "Fields": fields, + "CountryCode": country_code, + "FirstName": first_name, + "LastName": last_name, + "AddressLine1": address_line1, + "AddressLine2": address_line2, + "City": city, + "State": state, + "PostalCode": postal_code, + "AddressCountryCode": address_country_code, + "NationalId": national_id, + "DateOfBirth": date_of_birth, + "LastVerifiedDate": last_verified_date, + "VerificationSid": verification_sid, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number to lookup in E.164 or national format. Default country code is +1 (North America). + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __call__(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number to lookup in E.164 or national format. Default country code is +1 (North America). + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__init__.py new file mode 100644 index 0000000..ce6207e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__init__.py @@ -0,0 +1,35 @@ +from warnings import warn + +from twilio.rest.media.MediaBase import MediaBase +from twilio.rest.media.v1.media_processor import MediaProcessorList +from twilio.rest.media.v1.media_recording import MediaRecordingList +from twilio.rest.media.v1.player_streamer import PlayerStreamerList + + +class Media(MediaBase): + @property + def media_processor(self) -> MediaProcessorList: + warn( + "media_processor is deprecated. Use v1.media_processor instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.media_processor + + @property + def media_recording(self) -> MediaRecordingList: + warn( + "media_recording is deprecated. Use v1.media_recording instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.media_recording + + @property + def player_streamer(self) -> PlayerStreamerList: + warn( + "player_streamer is deprecated. Use v1.player_streamer instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.player_streamer diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e06c108 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/media/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/MessagingBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/MessagingBase.py new file mode 100644 index 0000000..4fff74d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/MessagingBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.messaging.v1 import V1 + + +class MessagingBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Messaging Domain + + :returns: Domain for Messaging + """ + super().__init__(twilio, "https://messaging.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Messaging + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__init__.py new file mode 100644 index 0000000..4e62832 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__init__.py @@ -0,0 +1,99 @@ +from warnings import warn + +from twilio.rest.messaging.MessagingBase import MessagingBase +from twilio.rest.messaging.v1.brand_registration import BrandRegistrationList +from twilio.rest.messaging.v1.deactivations import DeactivationsList +from twilio.rest.messaging.v1.domain_certs import DomainCertsList +from twilio.rest.messaging.v1.domain_config import DomainConfigList +from twilio.rest.messaging.v1.domain_config_messaging_service import ( + DomainConfigMessagingServiceList, +) +from twilio.rest.messaging.v1.external_campaign import ExternalCampaignList +from twilio.rest.messaging.v1.linkshortening_messaging_service import ( + LinkshorteningMessagingServiceList, +) +from twilio.rest.messaging.v1.service import ServiceList +from twilio.rest.messaging.v1.usecase import UsecaseList + + +class Messaging(MessagingBase): + @property + def brand_registrations(self) -> BrandRegistrationList: + warn( + "brand_registrations is deprecated. Use v1.brand_registrations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.brand_registrations + + @property + def deactivations(self) -> DeactivationsList: + warn( + "deactivations is deprecated. Use v1.deactivations instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.deactivations + + @property + def domain_certs(self) -> DomainCertsList: + warn( + "domain_certs is deprecated. Use v1.domain_certs instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.domain_certs + + @property + def domain_config(self) -> DomainConfigList: + warn( + "domain_config is deprecated. Use v1.domain_config instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.domain_config + + @property + def domain_config_messaging_service(self) -> DomainConfigMessagingServiceList: + warn( + "domain_config_messaging_service is deprecated. Use v1.domain_config_messaging_service instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.domain_config_messaging_service + + @property + def external_campaign(self) -> ExternalCampaignList: + warn( + "external_campaign is deprecated. Use v1.external_campaign instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.external_campaign + + @property + def linkshortening_messaging_service(self) -> LinkshorteningMessagingServiceList: + warn( + "linkshortening_messaging_service is deprecated. Use v1.linkshortening_messaging_service instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.linkshortening_messaging_service + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services + + @property + def usecases(self) -> UsecaseList: + warn( + "usecases is deprecated. Use v1.usecases instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.usecases diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/MessagingBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/MessagingBase.cpython-310.pyc new file mode 100644 index 0000000..a806fd1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/MessagingBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..60e113d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__init__.py new file mode 100644 index 0000000..6262dfe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__init__.py @@ -0,0 +1,143 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.messaging.v1.brand_registration import BrandRegistrationList +from twilio.rest.messaging.v1.deactivations import DeactivationsList +from twilio.rest.messaging.v1.domain_certs import DomainCertsList +from twilio.rest.messaging.v1.domain_config import DomainConfigList +from twilio.rest.messaging.v1.domain_config_messaging_service import ( + DomainConfigMessagingServiceList, +) +from twilio.rest.messaging.v1.external_campaign import ExternalCampaignList +from twilio.rest.messaging.v1.linkshortening_messaging_service import ( + LinkshorteningMessagingServiceList, +) +from twilio.rest.messaging.v1.linkshortening_messaging_service_domain_association import ( + LinkshorteningMessagingServiceDomainAssociationList, +) +from twilio.rest.messaging.v1.service import ServiceList +from twilio.rest.messaging.v1.tollfree_verification import TollfreeVerificationList +from twilio.rest.messaging.v1.usecase import UsecaseList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Messaging + + :param domain: The Twilio.messaging domain + """ + super().__init__(domain, "v1") + self._brand_registrations: Optional[BrandRegistrationList] = None + self._deactivations: Optional[DeactivationsList] = None + self._domain_certs: Optional[DomainCertsList] = None + self._domain_config: Optional[DomainConfigList] = None + self._domain_config_messaging_service: Optional[ + DomainConfigMessagingServiceList + ] = None + self._external_campaign: Optional[ExternalCampaignList] = None + self._linkshortening_messaging_service: Optional[ + LinkshorteningMessagingServiceList + ] = None + self._linkshortening_messaging_service_domain_association: Optional[ + LinkshorteningMessagingServiceDomainAssociationList + ] = None + self._services: Optional[ServiceList] = None + self._tollfree_verifications: Optional[TollfreeVerificationList] = None + self._usecases: Optional[UsecaseList] = None + + @property + def brand_registrations(self) -> BrandRegistrationList: + if self._brand_registrations is None: + self._brand_registrations = BrandRegistrationList(self) + return self._brand_registrations + + @property + def deactivations(self) -> DeactivationsList: + if self._deactivations is None: + self._deactivations = DeactivationsList(self) + return self._deactivations + + @property + def domain_certs(self) -> DomainCertsList: + if self._domain_certs is None: + self._domain_certs = DomainCertsList(self) + return self._domain_certs + + @property + def domain_config(self) -> DomainConfigList: + if self._domain_config is None: + self._domain_config = DomainConfigList(self) + return self._domain_config + + @property + def domain_config_messaging_service(self) -> DomainConfigMessagingServiceList: + if self._domain_config_messaging_service is None: + self._domain_config_messaging_service = DomainConfigMessagingServiceList( + self + ) + return self._domain_config_messaging_service + + @property + def external_campaign(self) -> ExternalCampaignList: + if self._external_campaign is None: + self._external_campaign = ExternalCampaignList(self) + return self._external_campaign + + @property + def linkshortening_messaging_service(self) -> LinkshorteningMessagingServiceList: + if self._linkshortening_messaging_service is None: + self._linkshortening_messaging_service = LinkshorteningMessagingServiceList( + self + ) + return self._linkshortening_messaging_service + + @property + def linkshortening_messaging_service_domain_association( + self, + ) -> LinkshorteningMessagingServiceDomainAssociationList: + if self._linkshortening_messaging_service_domain_association is None: + self._linkshortening_messaging_service_domain_association = ( + LinkshorteningMessagingServiceDomainAssociationList(self) + ) + return self._linkshortening_messaging_service_domain_association + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + @property + def tollfree_verifications(self) -> TollfreeVerificationList: + if self._tollfree_verifications is None: + self._tollfree_verifications = TollfreeVerificationList(self) + return self._tollfree_verifications + + @property + def usecases(self) -> UsecaseList: + if self._usecases is None: + self._usecases = UsecaseList(self) + return self._usecases + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fedf833 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/deactivations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/deactivations.cpython-310.pyc new file mode 100644 index 0000000..9ff7443 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/deactivations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_certs.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_certs.cpython-310.pyc new file mode 100644 index 0000000..228da58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_certs.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config.cpython-310.pyc new file mode 100644 index 0000000..4585fde Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config_messaging_service.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config_messaging_service.cpython-310.pyc new file mode 100644 index 0000000..ad55cda Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/domain_config_messaging_service.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/external_campaign.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/external_campaign.cpython-310.pyc new file mode 100644 index 0000000..4caa101 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/external_campaign.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service.cpython-310.pyc new file mode 100644 index 0000000..5852f9b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service_domain_association.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service_domain_association.cpython-310.pyc new file mode 100644 index 0000000..f2ebe4e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/linkshortening_messaging_service_domain_association.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/tollfree_verification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/tollfree_verification.cpython-310.pyc new file mode 100644 index 0000000..270cedb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/tollfree_verification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/usecase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/usecase.cpython-310.pyc new file mode 100644 index 0000000..a24f00a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/__pycache__/usecase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__init__.py new file mode 100644 index 0000000..b02af94 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__init__.py @@ -0,0 +1,647 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.messaging.v1.brand_registration.brand_registration_otp import ( + BrandRegistrationOtpList, +) +from twilio.rest.messaging.v1.brand_registration.brand_vetting import BrandVettingList + + +class BrandRegistrationInstance(InstanceResource): + + class BrandFeedback(object): + TAX_ID = "TAX_ID" + STOCK_SYMBOL = "STOCK_SYMBOL" + NONPROFIT = "NONPROFIT" + GOVERNMENT_ENTITY = "GOVERNMENT_ENTITY" + OTHERS = "OTHERS" + + class IdentityStatus(object): + SELF_DECLARED = "SELF_DECLARED" + UNVERIFIED = "UNVERIFIED" + VERIFIED = "VERIFIED" + VETTED_VERIFIED = "VETTED_VERIFIED" + + class Status(object): + PENDING = "PENDING" + APPROVED = "APPROVED" + FAILED = "FAILED" + IN_REVIEW = "IN_REVIEW" + DELETED = "DELETED" + + """ + :ivar sid: The unique string to identify Brand Registration. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Brand Registration resource. + :ivar customer_profile_bundle_sid: A2P Messaging Profile Bundle BundleSid. + :ivar a2p_profile_bundle_sid: A2P Messaging Profile Bundle BundleSid. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar brand_type: Type of brand. One of: \"STANDARD\", \"SOLE_PROPRIETOR\". SOLE_PROPRIETOR is for the low volume, SOLE_PROPRIETOR campaign use case. There can only be one SOLE_PROPRIETOR campaign created per SOLE_PROPRIETOR brand. STANDARD is for all other campaign use cases. Multiple campaign use cases can be created per STANDARD brand. + :ivar status: + :ivar tcr_id: Campaign Registry (TCR) Brand ID. Assigned only after successful brand registration. + :ivar failure_reason: DEPRECATED. A reason why brand registration has failed. Only applicable when status is FAILED. + :ivar errors: A list of errors that occurred during the brand registration process. + :ivar url: The absolute URL of the Brand Registration resource. + :ivar brand_score: The secondary vetting score if it was done. Otherwise, it will be the brand score if it's returned from TCR. It may be null if no score is available. + :ivar brand_feedback: DEPRECATED. Feedback on how to improve brand score + :ivar identity_status: + :ivar russell_3000: Publicly traded company identified in the Russell 3000 Index + :ivar government_entity: Identified as a government entity + :ivar tax_exempt_status: Nonprofit organization tax-exempt status per section 501 of the U.S. tax code. + :ivar skip_automatic_sec_vet: A flag to disable automatic secondary vetting for brands which it would otherwise be done. + :ivar mock: A boolean that specifies whether brand should be a mock or not. If true, brand will be registered as a mock brand. Defaults to false if no value is provided. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.customer_profile_bundle_sid: Optional[str] = payload.get( + "customer_profile_bundle_sid" + ) + self.a2p_profile_bundle_sid: Optional[str] = payload.get( + "a2p_profile_bundle_sid" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.brand_type: Optional[str] = payload.get("brand_type") + self.status: Optional["BrandRegistrationInstance.Status"] = payload.get( + "status" + ) + self.tcr_id: Optional[str] = payload.get("tcr_id") + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.errors: Optional[List[Dict[str, object]]] = payload.get("errors") + self.url: Optional[str] = payload.get("url") + self.brand_score: Optional[int] = deserialize.integer( + payload.get("brand_score") + ) + self.brand_feedback: Optional[ + List["BrandRegistrationInstance.BrandFeedback"] + ] = payload.get("brand_feedback") + self.identity_status: Optional["BrandRegistrationInstance.IdentityStatus"] = ( + payload.get("identity_status") + ) + self.russell_3000: Optional[bool] = payload.get("russell_3000") + self.government_entity: Optional[bool] = payload.get("government_entity") + self.tax_exempt_status: Optional[str] = payload.get("tax_exempt_status") + self.skip_automatic_sec_vet: Optional[bool] = payload.get( + "skip_automatic_sec_vet" + ) + self.mock: Optional[bool] = payload.get("mock") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[BrandRegistrationContext] = None + + @property + def _proxy(self) -> "BrandRegistrationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BrandRegistrationContext for this BrandRegistrationInstance + """ + if self._context is None: + self._context = BrandRegistrationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "BrandRegistrationInstance": + """ + Fetch the BrandRegistrationInstance + + + :returns: The fetched BrandRegistrationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BrandRegistrationInstance": + """ + Asynchronous coroutine to fetch the BrandRegistrationInstance + + + :returns: The fetched BrandRegistrationInstance + """ + return await self._proxy.fetch_async() + + def update(self) -> "BrandRegistrationInstance": + """ + Update the BrandRegistrationInstance + + + :returns: The updated BrandRegistrationInstance + """ + return self._proxy.update() + + async def update_async(self) -> "BrandRegistrationInstance": + """ + Asynchronous coroutine to update the BrandRegistrationInstance + + + :returns: The updated BrandRegistrationInstance + """ + return await self._proxy.update_async() + + @property + def brand_registration_otps(self) -> BrandRegistrationOtpList: + """ + Access the brand_registration_otps + """ + return self._proxy.brand_registration_otps + + @property + def brand_vettings(self) -> BrandVettingList: + """ + Access the brand_vettings + """ + return self._proxy.brand_vettings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BrandRegistrationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the BrandRegistrationContext + + :param version: Version that contains the resource + :param sid: The SID of the Brand Registration resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/a2p/BrandRegistrations/{sid}".format(**self._solution) + + self._brand_registration_otps: Optional[BrandRegistrationOtpList] = None + self._brand_vettings: Optional[BrandVettingList] = None + + def fetch(self) -> BrandRegistrationInstance: + """ + Fetch the BrandRegistrationInstance + + + :returns: The fetched BrandRegistrationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BrandRegistrationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BrandRegistrationInstance: + """ + Asynchronous coroutine to fetch the BrandRegistrationInstance + + + :returns: The fetched BrandRegistrationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BrandRegistrationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self) -> BrandRegistrationInstance: + """ + Update the BrandRegistrationInstance + + + :returns: The updated BrandRegistrationInstance + """ + data = values.of({}) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandRegistrationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async(self) -> BrandRegistrationInstance: + """ + Asynchronous coroutine to update the BrandRegistrationInstance + + + :returns: The updated BrandRegistrationInstance + """ + data = values.of({}) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandRegistrationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + @property + def brand_registration_otps(self) -> BrandRegistrationOtpList: + """ + Access the brand_registration_otps + """ + if self._brand_registration_otps is None: + self._brand_registration_otps = BrandRegistrationOtpList( + self._version, + self._solution["sid"], + ) + return self._brand_registration_otps + + @property + def brand_vettings(self) -> BrandVettingList: + """ + Access the brand_vettings + """ + if self._brand_vettings is None: + self._brand_vettings = BrandVettingList( + self._version, + self._solution["sid"], + ) + return self._brand_vettings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BrandRegistrationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BrandRegistrationInstance: + """ + Build an instance of BrandRegistrationInstance + + :param payload: Payload response from the API + """ + return BrandRegistrationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BrandRegistrationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BrandRegistrationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/a2p/BrandRegistrations" + + def create( + self, + customer_profile_bundle_sid: str, + a2p_profile_bundle_sid: str, + brand_type: Union[str, object] = values.unset, + mock: Union[bool, object] = values.unset, + skip_automatic_sec_vet: Union[bool, object] = values.unset, + ) -> BrandRegistrationInstance: + """ + Create the BrandRegistrationInstance + + :param customer_profile_bundle_sid: Customer Profile Bundle Sid. + :param a2p_profile_bundle_sid: A2P Messaging Profile Bundle Sid. + :param brand_type: Type of brand being created. One of: \\\"STANDARD\\\", \\\"SOLE_PROPRIETOR\\\". SOLE_PROPRIETOR is for low volume, SOLE_PROPRIETOR use cases. STANDARD is for all other use cases. + :param mock: A boolean that specifies whether brand should be a mock or not. If true, brand will be registered as a mock brand. Defaults to false if no value is provided. + :param skip_automatic_sec_vet: A flag to disable automatic secondary vetting for brands which it would otherwise be done. + + :returns: The created BrandRegistrationInstance + """ + + data = values.of( + { + "CustomerProfileBundleSid": customer_profile_bundle_sid, + "A2PProfileBundleSid": a2p_profile_bundle_sid, + "BrandType": brand_type, + "Mock": serialize.boolean_to_string(mock), + "SkipAutomaticSecVet": serialize.boolean_to_string( + skip_automatic_sec_vet + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandRegistrationInstance(self._version, payload) + + async def create_async( + self, + customer_profile_bundle_sid: str, + a2p_profile_bundle_sid: str, + brand_type: Union[str, object] = values.unset, + mock: Union[bool, object] = values.unset, + skip_automatic_sec_vet: Union[bool, object] = values.unset, + ) -> BrandRegistrationInstance: + """ + Asynchronously create the BrandRegistrationInstance + + :param customer_profile_bundle_sid: Customer Profile Bundle Sid. + :param a2p_profile_bundle_sid: A2P Messaging Profile Bundle Sid. + :param brand_type: Type of brand being created. One of: \\\"STANDARD\\\", \\\"SOLE_PROPRIETOR\\\". SOLE_PROPRIETOR is for low volume, SOLE_PROPRIETOR use cases. STANDARD is for all other use cases. + :param mock: A boolean that specifies whether brand should be a mock or not. If true, brand will be registered as a mock brand. Defaults to false if no value is provided. + :param skip_automatic_sec_vet: A flag to disable automatic secondary vetting for brands which it would otherwise be done. + + :returns: The created BrandRegistrationInstance + """ + + data = values.of( + { + "CustomerProfileBundleSid": customer_profile_bundle_sid, + "A2PProfileBundleSid": a2p_profile_bundle_sid, + "BrandType": brand_type, + "Mock": serialize.boolean_to_string(mock), + "SkipAutomaticSecVet": serialize.boolean_to_string( + skip_automatic_sec_vet + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandRegistrationInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BrandRegistrationInstance]: + """ + Streams BrandRegistrationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BrandRegistrationInstance]: + """ + Asynchronously streams BrandRegistrationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BrandRegistrationInstance]: + """ + Lists BrandRegistrationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BrandRegistrationInstance]: + """ + Asynchronously lists BrandRegistrationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BrandRegistrationPage: + """ + Retrieve a single page of BrandRegistrationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BrandRegistrationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BrandRegistrationPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BrandRegistrationPage: + """ + Asynchronously retrieve a single page of BrandRegistrationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BrandRegistrationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BrandRegistrationPage(self._version, response) + + def get_page(self, target_url: str) -> BrandRegistrationPage: + """ + Retrieve a specific page of BrandRegistrationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BrandRegistrationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BrandRegistrationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> BrandRegistrationPage: + """ + Asynchronously retrieve a specific page of BrandRegistrationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BrandRegistrationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BrandRegistrationPage(self._version, response) + + def get(self, sid: str) -> BrandRegistrationContext: + """ + Constructs a BrandRegistrationContext + + :param sid: The SID of the Brand Registration resource to update. + """ + return BrandRegistrationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> BrandRegistrationContext: + """ + Constructs a BrandRegistrationContext + + :param sid: The SID of the Brand Registration resource to update. + """ + return BrandRegistrationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a46d8e1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_registration_otp.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_registration_otp.cpython-310.pyc new file mode 100644 index 0000000..10d0e12 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_registration_otp.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_vetting.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_vetting.cpython-310.pyc new file mode 100644 index 0000000..ac17b1a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/__pycache__/brand_vetting.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_registration_otp.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_registration_otp.py new file mode 100644 index 0000000..20ce245 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_registration_otp.py @@ -0,0 +1,116 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BrandRegistrationOtpInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Brand Registration resource. + :ivar brand_registration_sid: The unique string to identify Brand Registration of Sole Proprietor Brand + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], brand_registration_sid: str + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.brand_registration_sid: Optional[str] = payload.get( + "brand_registration_sid" + ) + + self._solution = { + "brand_registration_sid": brand_registration_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BrandRegistrationOtpList(ListResource): + + def __init__(self, version: Version, brand_registration_sid: str): + """ + Initialize the BrandRegistrationOtpList + + :param version: Version that contains the resource + :param brand_registration_sid: Brand Registration Sid of Sole Proprietor Brand. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "brand_registration_sid": brand_registration_sid, + } + self._uri = "/a2p/BrandRegistrations/{brand_registration_sid}/SmsOtp".format( + **self._solution + ) + + def create(self) -> BrandRegistrationOtpInstance: + """ + Create the BrandRegistrationOtpInstance + + + :returns: The created BrandRegistrationOtpInstance + """ + + payload = self._version.create( + method="POST", + uri=self._uri, + ) + + return BrandRegistrationOtpInstance( + self._version, + payload, + brand_registration_sid=self._solution["brand_registration_sid"], + ) + + async def create_async(self) -> BrandRegistrationOtpInstance: + """ + Asynchronously create the BrandRegistrationOtpInstance + + + :returns: The created BrandRegistrationOtpInstance + """ + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + ) + + return BrandRegistrationOtpInstance( + self._version, + payload, + brand_registration_sid=self._solution["brand_registration_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_vetting.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_vetting.py new file mode 100644 index 0000000..c801cb1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/brand_registration/brand_vetting.py @@ -0,0 +1,540 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BrandVettingInstance(InstanceResource): + + class VettingProvider(object): + CAMPAIGN_VERIFY = "campaign-verify" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the vetting record. + :ivar brand_sid: The unique string to identify Brand Registration. + :ivar brand_vetting_sid: The Twilio SID of the third-party vetting record. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar vetting_id: The unique identifier of the vetting from the third-party provider. + :ivar vetting_class: The type of vetting that has been conducted. One of “STANDARD” (Aegis) or “POLITICAL” (Campaign Verify). + :ivar vetting_status: The status of the import vetting attempt. One of “PENDING,” “SUCCESS,” or “FAILED”. + :ivar vetting_provider: + :ivar url: The absolute URL of the Brand Vetting resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + brand_sid: str, + brand_vetting_sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.brand_sid: Optional[str] = payload.get("brand_sid") + self.brand_vetting_sid: Optional[str] = payload.get("brand_vetting_sid") + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.vetting_id: Optional[str] = payload.get("vetting_id") + self.vetting_class: Optional[str] = payload.get("vetting_class") + self.vetting_status: Optional[str] = payload.get("vetting_status") + self.vetting_provider: Optional["BrandVettingInstance.VettingProvider"] = ( + payload.get("vetting_provider") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "brand_sid": brand_sid, + "brand_vetting_sid": brand_vetting_sid or self.brand_vetting_sid, + } + self._context: Optional[BrandVettingContext] = None + + @property + def _proxy(self) -> "BrandVettingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BrandVettingContext for this BrandVettingInstance + """ + if self._context is None: + self._context = BrandVettingContext( + self._version, + brand_sid=self._solution["brand_sid"], + brand_vetting_sid=self._solution["brand_vetting_sid"], + ) + return self._context + + def fetch(self) -> "BrandVettingInstance": + """ + Fetch the BrandVettingInstance + + + :returns: The fetched BrandVettingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BrandVettingInstance": + """ + Asynchronous coroutine to fetch the BrandVettingInstance + + + :returns: The fetched BrandVettingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BrandVettingContext(InstanceContext): + + def __init__(self, version: Version, brand_sid: str, brand_vetting_sid: str): + """ + Initialize the BrandVettingContext + + :param version: Version that contains the resource + :param brand_sid: The SID of the Brand Registration resource of the vettings to read . + :param brand_vetting_sid: The Twilio SID of the third-party vetting record. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "brand_sid": brand_sid, + "brand_vetting_sid": brand_vetting_sid, + } + self._uri = ( + "/a2p/BrandRegistrations/{brand_sid}/Vettings/{brand_vetting_sid}".format( + **self._solution + ) + ) + + def fetch(self) -> BrandVettingInstance: + """ + Fetch the BrandVettingInstance + + + :returns: The fetched BrandVettingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BrandVettingInstance( + self._version, + payload, + brand_sid=self._solution["brand_sid"], + brand_vetting_sid=self._solution["brand_vetting_sid"], + ) + + async def fetch_async(self) -> BrandVettingInstance: + """ + Asynchronous coroutine to fetch the BrandVettingInstance + + + :returns: The fetched BrandVettingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BrandVettingInstance( + self._version, + payload, + brand_sid=self._solution["brand_sid"], + brand_vetting_sid=self._solution["brand_vetting_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BrandVettingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BrandVettingInstance: + """ + Build an instance of BrandVettingInstance + + :param payload: Payload response from the API + """ + return BrandVettingInstance( + self._version, payload, brand_sid=self._solution["brand_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BrandVettingList(ListResource): + + def __init__(self, version: Version, brand_sid: str): + """ + Initialize the BrandVettingList + + :param version: Version that contains the resource + :param brand_sid: The SID of the Brand Registration resource of the vettings to read . + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "brand_sid": brand_sid, + } + self._uri = "/a2p/BrandRegistrations/{brand_sid}/Vettings".format( + **self._solution + ) + + def create( + self, + vetting_provider: "BrandVettingInstance.VettingProvider", + vetting_id: Union[str, object] = values.unset, + ) -> BrandVettingInstance: + """ + Create the BrandVettingInstance + + :param vetting_provider: + :param vetting_id: The unique ID of the vetting + + :returns: The created BrandVettingInstance + """ + + data = values.of( + { + "VettingProvider": vetting_provider, + "VettingId": vetting_id, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandVettingInstance( + self._version, payload, brand_sid=self._solution["brand_sid"] + ) + + async def create_async( + self, + vetting_provider: "BrandVettingInstance.VettingProvider", + vetting_id: Union[str, object] = values.unset, + ) -> BrandVettingInstance: + """ + Asynchronously create the BrandVettingInstance + + :param vetting_provider: + :param vetting_id: The unique ID of the vetting + + :returns: The created BrandVettingInstance + """ + + data = values.of( + { + "VettingProvider": vetting_provider, + "VettingId": vetting_id, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BrandVettingInstance( + self._version, payload, brand_sid=self._solution["brand_sid"] + ) + + def stream( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BrandVettingInstance]: + """ + Streams BrandVettingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "BrandVettingInstance.VettingProvider" vetting_provider: The third-party provider of the vettings to read + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + vetting_provider=vetting_provider, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BrandVettingInstance]: + """ + Asynchronously streams BrandVettingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "BrandVettingInstance.VettingProvider" vetting_provider: The third-party provider of the vettings to read + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + vetting_provider=vetting_provider, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BrandVettingInstance]: + """ + Lists BrandVettingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "BrandVettingInstance.VettingProvider" vetting_provider: The third-party provider of the vettings to read + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + vetting_provider=vetting_provider, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BrandVettingInstance]: + """ + Asynchronously lists BrandVettingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "BrandVettingInstance.VettingProvider" vetting_provider: The third-party provider of the vettings to read + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + vetting_provider=vetting_provider, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BrandVettingPage: + """ + Retrieve a single page of BrandVettingInstance records from the API. + Request is executed immediately + + :param vetting_provider: The third-party provider of the vettings to read + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BrandVettingInstance + """ + data = values.of( + { + "VettingProvider": vetting_provider, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BrandVettingPage(self._version, response, self._solution) + + async def page_async( + self, + vetting_provider: Union[ + "BrandVettingInstance.VettingProvider", object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BrandVettingPage: + """ + Asynchronously retrieve a single page of BrandVettingInstance records from the API. + Request is executed immediately + + :param vetting_provider: The third-party provider of the vettings to read + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BrandVettingInstance + """ + data = values.of( + { + "VettingProvider": vetting_provider, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BrandVettingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BrandVettingPage: + """ + Retrieve a specific page of BrandVettingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BrandVettingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BrandVettingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BrandVettingPage: + """ + Asynchronously retrieve a specific page of BrandVettingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BrandVettingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BrandVettingPage(self._version, response, self._solution) + + def get(self, brand_vetting_sid: str) -> BrandVettingContext: + """ + Constructs a BrandVettingContext + + :param brand_vetting_sid: The Twilio SID of the third-party vetting record. + """ + return BrandVettingContext( + self._version, + brand_sid=self._solution["brand_sid"], + brand_vetting_sid=brand_vetting_sid, + ) + + def __call__(self, brand_vetting_sid: str) -> BrandVettingContext: + """ + Constructs a BrandVettingContext + + :param brand_vetting_sid: The Twilio SID of the third-party vetting record. + """ + return BrandVettingContext( + self._version, + brand_sid=self._solution["brand_sid"], + brand_vetting_sid=brand_vetting_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/deactivations.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/deactivations.py new file mode 100644 index 0000000..6616d87 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/deactivations.py @@ -0,0 +1,189 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class DeactivationsInstance(InstanceResource): + """ + :ivar redirect_to: Returns an authenticated url that redirects to a file containing the deactivated numbers for the requested day. This url is valid for up to two minutes. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.redirect_to: Optional[str] = payload.get("redirect_to") + + self._context: Optional[DeactivationsContext] = None + + @property + def _proxy(self) -> "DeactivationsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeactivationsContext for this DeactivationsInstance + """ + if self._context is None: + self._context = DeactivationsContext( + self._version, + ) + return self._context + + def fetch( + self, date: Union[date, object] = values.unset + ) -> "DeactivationsInstance": + """ + Fetch the DeactivationsInstance + + :param date: The request will return a list of all United States Phone Numbers that were deactivated on the day specified by this parameter. This date should be specified in YYYY-MM-DD format. + + :returns: The fetched DeactivationsInstance + """ + return self._proxy.fetch( + date=date, + ) + + async def fetch_async( + self, date: Union[date, object] = values.unset + ) -> "DeactivationsInstance": + """ + Asynchronous coroutine to fetch the DeactivationsInstance + + :param date: The request will return a list of all United States Phone Numbers that were deactivated on the day specified by this parameter. This date should be specified in YYYY-MM-DD format. + + :returns: The fetched DeactivationsInstance + """ + return await self._proxy.fetch_async( + date=date, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class DeactivationsContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the DeactivationsContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Deactivations" + + def fetch(self, date: Union[date, object] = values.unset) -> DeactivationsInstance: + """ + Fetch the DeactivationsInstance + + :param date: The request will return a list of all United States Phone Numbers that were deactivated on the day specified by this parameter. This date should be specified in YYYY-MM-DD format. + + :returns: The fetched DeactivationsInstance + """ + + data = values.of( + { + "Date": serialize.iso8601_date(date), + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return DeactivationsInstance( + self._version, + payload, + ) + + async def fetch_async( + self, date: Union[date, object] = values.unset + ) -> DeactivationsInstance: + """ + Asynchronous coroutine to fetch the DeactivationsInstance + + :param date: The request will return a list of all United States Phone Numbers that were deactivated on the day specified by this parameter. This date should be specified in YYYY-MM-DD format. + + :returns: The fetched DeactivationsInstance + """ + + data = values.of( + { + "Date": serialize.iso8601_date(date), + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return DeactivationsInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class DeactivationsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DeactivationsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> DeactivationsContext: + """ + Constructs a DeactivationsContext + + """ + return DeactivationsContext(self._version) + + def __call__(self) -> DeactivationsContext: + """ + Constructs a DeactivationsContext + + """ + return DeactivationsContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_certs.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_certs.py new file mode 100644 index 0000000..3acc8a1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_certs.py @@ -0,0 +1,323 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class DomainCertsInstance(InstanceResource): + """ + :ivar domain_sid: The unique string that we created to identify the Domain resource. + :ivar date_updated: Date that this Domain was last updated. + :ivar date_expires: Date that the private certificate associated with this domain expires. You will need to update the certificate before that date to ensure your shortened links will continue to work. + :ivar date_created: Date that this Domain was registered to the Twilio platform to create a new Domain object. + :ivar domain_name: Full url path for this domain. + :ivar certificate_sid: The unique string that we created to identify this Certificate resource. + :ivar url: + :ivar cert_in_validation: Optional JSON field describing the status and upload date of a new certificate in the process of validation + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + domain_sid: Optional[str] = None, + ): + super().__init__(version) + + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.domain_name: Optional[str] = payload.get("domain_name") + self.certificate_sid: Optional[str] = payload.get("certificate_sid") + self.url: Optional[str] = payload.get("url") + self.cert_in_validation: Optional[Dict[str, object]] = payload.get( + "cert_in_validation" + ) + + self._solution = { + "domain_sid": domain_sid or self.domain_sid, + } + self._context: Optional[DomainCertsContext] = None + + @property + def _proxy(self) -> "DomainCertsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DomainCertsContext for this DomainCertsInstance + """ + if self._context is None: + self._context = DomainCertsContext( + self._version, + domain_sid=self._solution["domain_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DomainCertsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DomainCertsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DomainCertsInstance": + """ + Fetch the DomainCertsInstance + + + :returns: The fetched DomainCertsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DomainCertsInstance": + """ + Asynchronous coroutine to fetch the DomainCertsInstance + + + :returns: The fetched DomainCertsInstance + """ + return await self._proxy.fetch_async() + + def update(self, tls_cert: str) -> "DomainCertsInstance": + """ + Update the DomainCertsInstance + + :param tls_cert: Contains the full TLS certificate and private for this domain in PEM format: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail. Twilio uses this information to process HTTPS traffic sent to your domain. + + :returns: The updated DomainCertsInstance + """ + return self._proxy.update( + tls_cert=tls_cert, + ) + + async def update_async(self, tls_cert: str) -> "DomainCertsInstance": + """ + Asynchronous coroutine to update the DomainCertsInstance + + :param tls_cert: Contains the full TLS certificate and private for this domain in PEM format: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail. Twilio uses this information to process HTTPS traffic sent to your domain. + + :returns: The updated DomainCertsInstance + """ + return await self._proxy.update_async( + tls_cert=tls_cert, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainCertsContext(InstanceContext): + + def __init__(self, version: Version, domain_sid: str): + """ + Initialize the DomainCertsContext + + :param version: Version that contains the resource + :param domain_sid: Unique string used to identify the domain that this certificate should be associated with. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "domain_sid": domain_sid, + } + self._uri = "/LinkShortening/Domains/{domain_sid}/Certificate".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the DomainCertsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DomainCertsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DomainCertsInstance: + """ + Fetch the DomainCertsInstance + + + :returns: The fetched DomainCertsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DomainCertsInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + ) + + async def fetch_async(self) -> DomainCertsInstance: + """ + Asynchronous coroutine to fetch the DomainCertsInstance + + + :returns: The fetched DomainCertsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DomainCertsInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + ) + + def update(self, tls_cert: str) -> DomainCertsInstance: + """ + Update the DomainCertsInstance + + :param tls_cert: Contains the full TLS certificate and private for this domain in PEM format: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail. Twilio uses this information to process HTTPS traffic sent to your domain. + + :returns: The updated DomainCertsInstance + """ + data = values.of( + { + "TlsCert": tls_cert, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainCertsInstance( + self._version, payload, domain_sid=self._solution["domain_sid"] + ) + + async def update_async(self, tls_cert: str) -> DomainCertsInstance: + """ + Asynchronous coroutine to update the DomainCertsInstance + + :param tls_cert: Contains the full TLS certificate and private for this domain in PEM format: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail. Twilio uses this information to process HTTPS traffic sent to your domain. + + :returns: The updated DomainCertsInstance + """ + data = values.of( + { + "TlsCert": tls_cert, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainCertsInstance( + self._version, payload, domain_sid=self._solution["domain_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainCertsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DomainCertsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, domain_sid: str) -> DomainCertsContext: + """ + Constructs a DomainCertsContext + + :param domain_sid: Unique string used to identify the domain that this certificate should be associated with. + """ + return DomainCertsContext(self._version, domain_sid=domain_sid) + + def __call__(self, domain_sid: str) -> DomainCertsContext: + """ + Constructs a DomainCertsContext + + :param domain_sid: Unique string used to identify the domain that this certificate should be associated with. + """ + return DomainCertsContext(self._version, domain_sid=domain_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config.py new file mode 100644 index 0000000..25d6934 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config.py @@ -0,0 +1,327 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class DomainConfigInstance(InstanceResource): + """ + :ivar domain_sid: The unique string that we created to identify the Domain resource. + :ivar config_sid: The unique string that we created to identify the Domain config (prefix ZK). + :ivar fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :ivar callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links. + :ivar continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :ivar date_created: Date this Domain Config was created. + :ivar date_updated: Date that this Domain Config was last updated. + :ivar url: + :ivar disable_https: Customer's choice to send links with/without \"https://\" attached to shortened url. If true, messages will not be sent with https:// at the beginning of the url. If false, messages will be sent with https:// at the beginning of the url. False is the default behavior if it is not specified. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + domain_sid: Optional[str] = None, + ): + super().__init__(version) + + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.config_sid: Optional[str] = payload.get("config_sid") + self.fallback_url: Optional[str] = payload.get("fallback_url") + self.callback_url: Optional[str] = payload.get("callback_url") + self.continue_on_failure: Optional[bool] = payload.get("continue_on_failure") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.disable_https: Optional[bool] = payload.get("disable_https") + + self._solution = { + "domain_sid": domain_sid or self.domain_sid, + } + self._context: Optional[DomainConfigContext] = None + + @property + def _proxy(self) -> "DomainConfigContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DomainConfigContext for this DomainConfigInstance + """ + if self._context is None: + self._context = DomainConfigContext( + self._version, + domain_sid=self._solution["domain_sid"], + ) + return self._context + + def fetch(self) -> "DomainConfigInstance": + """ + Fetch the DomainConfigInstance + + + :returns: The fetched DomainConfigInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DomainConfigInstance": + """ + Asynchronous coroutine to fetch the DomainConfigInstance + + + :returns: The fetched DomainConfigInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + fallback_url: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + continue_on_failure: Union[bool, object] = values.unset, + disable_https: Union[bool, object] = values.unset, + ) -> "DomainConfigInstance": + """ + Update the DomainConfigInstance + + :param fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :param callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links + :param continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :param disable_https: Customer's choice to send links with/without \\\"https://\\\" attached to shortened url. If true, messages will not be sent with https:// at the beginning of the url. If false, messages will be sent with https:// at the beginning of the url. False is the default behavior if it is not specified. + + :returns: The updated DomainConfigInstance + """ + return self._proxy.update( + fallback_url=fallback_url, + callback_url=callback_url, + continue_on_failure=continue_on_failure, + disable_https=disable_https, + ) + + async def update_async( + self, + fallback_url: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + continue_on_failure: Union[bool, object] = values.unset, + disable_https: Union[bool, object] = values.unset, + ) -> "DomainConfigInstance": + """ + Asynchronous coroutine to update the DomainConfigInstance + + :param fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :param callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links + :param continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :param disable_https: Customer's choice to send links with/without \\\"https://\\\" attached to shortened url. If true, messages will not be sent with https:// at the beginning of the url. If false, messages will be sent with https:// at the beginning of the url. False is the default behavior if it is not specified. + + :returns: The updated DomainConfigInstance + """ + return await self._proxy.update_async( + fallback_url=fallback_url, + callback_url=callback_url, + continue_on_failure=continue_on_failure, + disable_https=disable_https, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainConfigContext(InstanceContext): + + def __init__(self, version: Version, domain_sid: str): + """ + Initialize the DomainConfigContext + + :param version: Version that contains the resource + :param domain_sid: Unique string used to identify the domain that this config should be associated with. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "domain_sid": domain_sid, + } + self._uri = "/LinkShortening/Domains/{domain_sid}/Config".format( + **self._solution + ) + + def fetch(self) -> DomainConfigInstance: + """ + Fetch the DomainConfigInstance + + + :returns: The fetched DomainConfigInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DomainConfigInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + ) + + async def fetch_async(self) -> DomainConfigInstance: + """ + Asynchronous coroutine to fetch the DomainConfigInstance + + + :returns: The fetched DomainConfigInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DomainConfigInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + ) + + def update( + self, + fallback_url: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + continue_on_failure: Union[bool, object] = values.unset, + disable_https: Union[bool, object] = values.unset, + ) -> DomainConfigInstance: + """ + Update the DomainConfigInstance + + :param fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :param callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links + :param continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :param disable_https: Customer's choice to send links with/without \\\"https://\\\" attached to shortened url. If true, messages will not be sent with https:// at the beginning of the url. If false, messages will be sent with https:// at the beginning of the url. False is the default behavior if it is not specified. + + :returns: The updated DomainConfigInstance + """ + data = values.of( + { + "FallbackUrl": fallback_url, + "CallbackUrl": callback_url, + "ContinueOnFailure": serialize.boolean_to_string(continue_on_failure), + "DisableHttps": serialize.boolean_to_string(disable_https), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainConfigInstance( + self._version, payload, domain_sid=self._solution["domain_sid"] + ) + + async def update_async( + self, + fallback_url: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + continue_on_failure: Union[bool, object] = values.unset, + disable_https: Union[bool, object] = values.unset, + ) -> DomainConfigInstance: + """ + Asynchronous coroutine to update the DomainConfigInstance + + :param fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :param callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links + :param continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :param disable_https: Customer's choice to send links with/without \\\"https://\\\" attached to shortened url. If true, messages will not be sent with https:// at the beginning of the url. If false, messages will be sent with https:// at the beginning of the url. False is the default behavior if it is not specified. + + :returns: The updated DomainConfigInstance + """ + data = values.of( + { + "FallbackUrl": fallback_url, + "CallbackUrl": callback_url, + "ContinueOnFailure": serialize.boolean_to_string(continue_on_failure), + "DisableHttps": serialize.boolean_to_string(disable_https), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DomainConfigInstance( + self._version, payload, domain_sid=self._solution["domain_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DomainConfigList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DomainConfigList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, domain_sid: str) -> DomainConfigContext: + """ + Constructs a DomainConfigContext + + :param domain_sid: Unique string used to identify the domain that this config should be associated with. + """ + return DomainConfigContext(self._version, domain_sid=domain_sid) + + def __call__(self, domain_sid: str) -> DomainConfigContext: + """ + Constructs a DomainConfigContext + + :param domain_sid: Unique string used to identify the domain that this config should be associated with. + """ + return DomainConfigContext(self._version, domain_sid=domain_sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config_messaging_service.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config_messaging_service.py new file mode 100644 index 0000000..178ebd5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/domain_config_messaging_service.py @@ -0,0 +1,218 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class DomainConfigMessagingServiceInstance(InstanceResource): + """ + :ivar domain_sid: The unique string that we created to identify the Domain resource. + :ivar config_sid: The unique string that we created to identify the Domain config (prefix ZK). + :ivar messaging_service_sid: The unique string that identifies the messaging service + :ivar fallback_url: Any requests we receive to this domain that do not match an existing shortened message will be redirected to the fallback url. These will likely be either expired messages, random misdirected traffic, or intentional scraping. + :ivar callback_url: URL to receive click events to your webhook whenever the recipients click on the shortened links. + :ivar continue_on_failure: Boolean field to set customer delivery preference when there is a failure in linkShortening service + :ivar date_created: Date this Domain Config was created. + :ivar date_updated: Date that this Domain Config was last updated. + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + messaging_service_sid: Optional[str] = None, + ): + super().__init__(version) + + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.config_sid: Optional[str] = payload.get("config_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.fallback_url: Optional[str] = payload.get("fallback_url") + self.callback_url: Optional[str] = payload.get("callback_url") + self.continue_on_failure: Optional[bool] = payload.get("continue_on_failure") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "messaging_service_sid": messaging_service_sid + or self.messaging_service_sid, + } + self._context: Optional[DomainConfigMessagingServiceContext] = None + + @property + def _proxy(self) -> "DomainConfigMessagingServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DomainConfigMessagingServiceContext for this DomainConfigMessagingServiceInstance + """ + if self._context is None: + self._context = DomainConfigMessagingServiceContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + return self._context + + def fetch(self) -> "DomainConfigMessagingServiceInstance": + """ + Fetch the DomainConfigMessagingServiceInstance + + + :returns: The fetched DomainConfigMessagingServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DomainConfigMessagingServiceInstance": + """ + Asynchronous coroutine to fetch the DomainConfigMessagingServiceInstance + + + :returns: The fetched DomainConfigMessagingServiceInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class DomainConfigMessagingServiceContext(InstanceContext): + + def __init__(self, version: Version, messaging_service_sid: str): + """ + Initialize the DomainConfigMessagingServiceContext + + :param version: Version that contains the resource + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + self._uri = "/LinkShortening/MessagingService/{messaging_service_sid}/DomainConfig".format( + **self._solution + ) + + def fetch(self) -> DomainConfigMessagingServiceInstance: + """ + Fetch the DomainConfigMessagingServiceInstance + + + :returns: The fetched DomainConfigMessagingServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DomainConfigMessagingServiceInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + async def fetch_async(self) -> DomainConfigMessagingServiceInstance: + """ + Asynchronous coroutine to fetch the DomainConfigMessagingServiceInstance + + + :returns: The fetched DomainConfigMessagingServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DomainConfigMessagingServiceInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class DomainConfigMessagingServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DomainConfigMessagingServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, messaging_service_sid: str) -> DomainConfigMessagingServiceContext: + """ + Constructs a DomainConfigMessagingServiceContext + + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + return DomainConfigMessagingServiceContext( + self._version, messaging_service_sid=messaging_service_sid + ) + + def __call__( + self, messaging_service_sid: str + ) -> DomainConfigMessagingServiceContext: + """ + Constructs a DomainConfigMessagingServiceContext + + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + return DomainConfigMessagingServiceContext( + self._version, messaging_service_sid=messaging_service_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/external_campaign.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/external_campaign.py new file mode 100644 index 0000000..b70d8a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/external_campaign.py @@ -0,0 +1,127 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExternalCampaignInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies a US A2P Compliance resource `QE2c6890da8086d771620e9b13fadeba0b`. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the Campaign belongs to. + :ivar campaign_id: ID of the preregistered campaign. + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) that the resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.campaign_id: Optional[str] = payload.get("campaign_id") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ExternalCampaignList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ExternalCampaignList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services/PreregisteredUsa2p" + + def create( + self, campaign_id: str, messaging_service_sid: str + ) -> ExternalCampaignInstance: + """ + Create the ExternalCampaignInstance + + :param campaign_id: ID of the preregistered campaign. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) that the resource is associated with. + + :returns: The created ExternalCampaignInstance + """ + + data = values.of( + { + "CampaignId": campaign_id, + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ExternalCampaignInstance(self._version, payload) + + async def create_async( + self, campaign_id: str, messaging_service_sid: str + ) -> ExternalCampaignInstance: + """ + Asynchronously create the ExternalCampaignInstance + + :param campaign_id: ID of the preregistered campaign. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) that the resource is associated with. + + :returns: The created ExternalCampaignInstance + """ + + data = values.of( + { + "CampaignId": campaign_id, + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExternalCampaignInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service.py new file mode 100644 index 0000000..94ebfa6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service.py @@ -0,0 +1,256 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class LinkshorteningMessagingServiceInstance(InstanceResource): + """ + :ivar domain_sid: The unique string identifies the domain resource + :ivar messaging_service_sid: The unique string that identifies the messaging service + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + domain_sid: Optional[str] = None, + messaging_service_sid: Optional[str] = None, + ): + super().__init__(version) + + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "domain_sid": domain_sid or self.domain_sid, + "messaging_service_sid": messaging_service_sid + or self.messaging_service_sid, + } + self._context: Optional[LinkshorteningMessagingServiceContext] = None + + @property + def _proxy(self) -> "LinkshorteningMessagingServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: LinkshorteningMessagingServiceContext for this LinkshorteningMessagingServiceInstance + """ + if self._context is None: + self._context = LinkshorteningMessagingServiceContext( + self._version, + domain_sid=self._solution["domain_sid"], + messaging_service_sid=self._solution["messaging_service_sid"], + ) + return self._context + + def create(self) -> "LinkshorteningMessagingServiceInstance": + """ + Create the LinkshorteningMessagingServiceInstance + + + :returns: The created LinkshorteningMessagingServiceInstance + """ + return self._proxy.create() + + async def create_async(self) -> "LinkshorteningMessagingServiceInstance": + """ + Asynchronous coroutine to create the LinkshorteningMessagingServiceInstance + + + :returns: The created LinkshorteningMessagingServiceInstance + """ + return await self._proxy.create_async() + + def delete(self) -> bool: + """ + Deletes the LinkshorteningMessagingServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the LinkshorteningMessagingServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class LinkshorteningMessagingServiceContext(InstanceContext): + + def __init__(self, version: Version, domain_sid: str, messaging_service_sid: str): + """ + Initialize the LinkshorteningMessagingServiceContext + + :param version: Version that contains the resource + :param domain_sid: The domain SID to dissociate from a messaging service. With URL shortening enabled, links in messages sent with the associated messaging service will be shortened to the provided domain + :param messaging_service_sid: A messaging service SID to dissociate from a domain. With URL shortening enabled, links in messages sent with the provided messaging service will be shortened to the associated domain + """ + super().__init__(version) + + # Path Solution + self._solution = { + "domain_sid": domain_sid, + "messaging_service_sid": messaging_service_sid, + } + self._uri = "/LinkShortening/Domains/{domain_sid}/MessagingServices/{messaging_service_sid}".format( + **self._solution + ) + + def create(self) -> LinkshorteningMessagingServiceInstance: + """ + Create the LinkshorteningMessagingServiceInstance + + + :returns: The created LinkshorteningMessagingServiceInstance + """ + data = values.of({}) + + payload = self._version.create(method="POST", uri=self._uri, data=data) + + return LinkshorteningMessagingServiceInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + async def create_async(self) -> LinkshorteningMessagingServiceInstance: + """ + Asynchronous coroutine to create the LinkshorteningMessagingServiceInstance + + + :returns: The created LinkshorteningMessagingServiceInstance + """ + data = values.of({}) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data + ) + + return LinkshorteningMessagingServiceInstance( + self._version, + payload, + domain_sid=self._solution["domain_sid"], + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def delete(self) -> bool: + """ + Deletes the LinkshorteningMessagingServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the LinkshorteningMessagingServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class LinkshorteningMessagingServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the LinkshorteningMessagingServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get( + self, domain_sid: str, messaging_service_sid: str + ) -> LinkshorteningMessagingServiceContext: + """ + Constructs a LinkshorteningMessagingServiceContext + + :param domain_sid: The domain SID to dissociate from a messaging service. With URL shortening enabled, links in messages sent with the associated messaging service will be shortened to the provided domain + :param messaging_service_sid: A messaging service SID to dissociate from a domain. With URL shortening enabled, links in messages sent with the provided messaging service will be shortened to the associated domain + """ + return LinkshorteningMessagingServiceContext( + self._version, + domain_sid=domain_sid, + messaging_service_sid=messaging_service_sid, + ) + + def __call__( + self, domain_sid: str, messaging_service_sid: str + ) -> LinkshorteningMessagingServiceContext: + """ + Constructs a LinkshorteningMessagingServiceContext + + :param domain_sid: The domain SID to dissociate from a messaging service. With URL shortening enabled, links in messages sent with the associated messaging service will be shortened to the provided domain + :param messaging_service_sid: A messaging service SID to dissociate from a domain. With URL shortening enabled, links in messages sent with the provided messaging service will be shortened to the associated domain + """ + return LinkshorteningMessagingServiceContext( + self._version, + domain_sid=domain_sid, + messaging_service_sid=messaging_service_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service_domain_association.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service_domain_association.py new file mode 100644 index 0000000..a1252d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/linkshortening_messaging_service_domain_association.py @@ -0,0 +1,212 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class LinkshorteningMessagingServiceDomainAssociationInstance(InstanceResource): + """ + :ivar domain_sid: The unique string that we created to identify the Domain resource. + :ivar messaging_service_sid: The unique string that identifies the messaging service + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + messaging_service_sid: Optional[str] = None, + ): + super().__init__(version) + + self.domain_sid: Optional[str] = payload.get("domain_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "messaging_service_sid": messaging_service_sid + or self.messaging_service_sid, + } + self._context: Optional[ + LinkshorteningMessagingServiceDomainAssociationContext + ] = None + + @property + def _proxy(self) -> "LinkshorteningMessagingServiceDomainAssociationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: LinkshorteningMessagingServiceDomainAssociationContext for this LinkshorteningMessagingServiceDomainAssociationInstance + """ + if self._context is None: + self._context = LinkshorteningMessagingServiceDomainAssociationContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + return self._context + + def fetch(self) -> "LinkshorteningMessagingServiceDomainAssociationInstance": + """ + Fetch the LinkshorteningMessagingServiceDomainAssociationInstance + + + :returns: The fetched LinkshorteningMessagingServiceDomainAssociationInstance + """ + return self._proxy.fetch() + + async def fetch_async( + self, + ) -> "LinkshorteningMessagingServiceDomainAssociationInstance": + """ + Asynchronous coroutine to fetch the LinkshorteningMessagingServiceDomainAssociationInstance + + + :returns: The fetched LinkshorteningMessagingServiceDomainAssociationInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class LinkshorteningMessagingServiceDomainAssociationContext(InstanceContext): + + def __init__(self, version: Version, messaging_service_sid: str): + """ + Initialize the LinkshorteningMessagingServiceDomainAssociationContext + + :param version: Version that contains the resource + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + self._uri = ( + "/LinkShortening/MessagingServices/{messaging_service_sid}/Domain".format( + **self._solution + ) + ) + + def fetch(self) -> LinkshorteningMessagingServiceDomainAssociationInstance: + """ + Fetch the LinkshorteningMessagingServiceDomainAssociationInstance + + + :returns: The fetched LinkshorteningMessagingServiceDomainAssociationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return LinkshorteningMessagingServiceDomainAssociationInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + async def fetch_async( + self, + ) -> LinkshorteningMessagingServiceDomainAssociationInstance: + """ + Asynchronous coroutine to fetch the LinkshorteningMessagingServiceDomainAssociationInstance + + + :returns: The fetched LinkshorteningMessagingServiceDomainAssociationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return LinkshorteningMessagingServiceDomainAssociationInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class LinkshorteningMessagingServiceDomainAssociationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the LinkshorteningMessagingServiceDomainAssociationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get( + self, messaging_service_sid: str + ) -> LinkshorteningMessagingServiceDomainAssociationContext: + """ + Constructs a LinkshorteningMessagingServiceDomainAssociationContext + + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + return LinkshorteningMessagingServiceDomainAssociationContext( + self._version, messaging_service_sid=messaging_service_sid + ) + + def __call__( + self, messaging_service_sid: str + ) -> LinkshorteningMessagingServiceDomainAssociationContext: + """ + Constructs a LinkshorteningMessagingServiceDomainAssociationContext + + :param messaging_service_sid: Unique string used to identify the Messaging service that this domain should be associated with. + """ + return LinkshorteningMessagingServiceDomainAssociationContext( + self._version, messaging_service_sid=messaging_service_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return ( + "" + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__init__.py new file mode 100644 index 0000000..f735184 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__init__.py @@ -0,0 +1,1066 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.messaging.v1.service.alpha_sender import AlphaSenderList +from twilio.rest.messaging.v1.service.channel_sender import ChannelSenderList +from twilio.rest.messaging.v1.service.phone_number import PhoneNumberList +from twilio.rest.messaging.v1.service.short_code import ShortCodeList +from twilio.rest.messaging.v1.service.us_app_to_person import UsAppToPersonList +from twilio.rest.messaging.v1.service.us_app_to_person_usecase import ( + UsAppToPersonUsecaseList, +) + + +class ServiceInstance(InstanceResource): + + class ScanMessageContent(object): + INHERIT = "inherit" + ENABLE = "enable" + DISABLE = "disable" + + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :ivar inbound_method: The HTTP method we use to call `inbound_request_url`. Can be `GET` or `POST`. + :ivar fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :ivar fallback_method: The HTTP method we use to call `fallback_url`. Can be: `GET` or `POST`. + :ivar status_callback: The URL we call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :ivar sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :ivar mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :ivar smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :ivar scan_message_content: + :ivar fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :ivar area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :ivar synchronous_validation: Reserved. + :ivar validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :ivar url: The absolute URL of the Service resource. + :ivar links: The absolute URLs of related resources. + :ivar usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :ivar us_app_to_person_registered: Whether US A2P campaign is registered for this Service. + :ivar use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + :ivar sending_windows: A list of Sending Windows, which indicate defined time ranges in which a message can be sent, in the UTC time zone. Each window is defined by two strings, labeled \"start_time\" and \"end_time\". + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.inbound_request_url: Optional[str] = payload.get("inbound_request_url") + self.inbound_method: Optional[str] = payload.get("inbound_method") + self.fallback_url: Optional[str] = payload.get("fallback_url") + self.fallback_method: Optional[str] = payload.get("fallback_method") + self.status_callback: Optional[str] = payload.get("status_callback") + self.sticky_sender: Optional[bool] = payload.get("sticky_sender") + self.mms_converter: Optional[bool] = payload.get("mms_converter") + self.smart_encoding: Optional[bool] = payload.get("smart_encoding") + self.scan_message_content: Optional["ServiceInstance.ScanMessageContent"] = ( + payload.get("scan_message_content") + ) + self.fallback_to_long_code: Optional[bool] = payload.get( + "fallback_to_long_code" + ) + self.area_code_geomatch: Optional[bool] = payload.get("area_code_geomatch") + self.synchronous_validation: Optional[bool] = payload.get( + "synchronous_validation" + ) + self.validity_period: Optional[int] = deserialize.integer( + payload.get("validity_period") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.usecase: Optional[str] = payload.get("usecase") + self.us_app_to_person_registered: Optional[bool] = payload.get( + "us_app_to_person_registered" + ) + self.use_inbound_webhook_on_number: Optional[bool] = payload.get( + "use_inbound_webhook_on_number" + ) + self.sending_windows: Optional[Dict[str, object]] = payload.get( + "sending_windows" + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + inbound_request_url=inbound_request_url, + inbound_method=inbound_method, + fallback_url=fallback_url, + fallback_method=fallback_method, + status_callback=status_callback, + sticky_sender=sticky_sender, + mms_converter=mms_converter, + smart_encoding=smart_encoding, + scan_message_content=scan_message_content, + fallback_to_long_code=fallback_to_long_code, + area_code_geomatch=area_code_geomatch, + validity_period=validity_period, + synchronous_validation=synchronous_validation, + usecase=usecase, + use_inbound_webhook_on_number=use_inbound_webhook_on_number, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + inbound_request_url=inbound_request_url, + inbound_method=inbound_method, + fallback_url=fallback_url, + fallback_method=fallback_method, + status_callback=status_callback, + sticky_sender=sticky_sender, + mms_converter=mms_converter, + smart_encoding=smart_encoding, + scan_message_content=scan_message_content, + fallback_to_long_code=fallback_to_long_code, + area_code_geomatch=area_code_geomatch, + validity_period=validity_period, + synchronous_validation=synchronous_validation, + usecase=usecase, + use_inbound_webhook_on_number=use_inbound_webhook_on_number, + ) + + @property + def alpha_senders(self) -> AlphaSenderList: + """ + Access the alpha_senders + """ + return self._proxy.alpha_senders + + @property + def channel_senders(self) -> ChannelSenderList: + """ + Access the channel_senders + """ + return self._proxy.channel_senders + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + return self._proxy.phone_numbers + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + return self._proxy.short_codes + + @property + def us_app_to_person(self) -> UsAppToPersonList: + """ + Access the us_app_to_person + """ + return self._proxy.us_app_to_person + + @property + def us_app_to_person_usecases(self) -> UsAppToPersonUsecaseList: + """ + Access the us_app_to_person_usecases + """ + return self._proxy.us_app_to_person_usecases + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The SID of the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._alpha_senders: Optional[AlphaSenderList] = None + self._channel_senders: Optional[ChannelSenderList] = None + self._phone_numbers: Optional[PhoneNumberList] = None + self._short_codes: Optional[ShortCodeList] = None + self._us_app_to_person: Optional[UsAppToPersonList] = None + self._us_app_to_person_usecases: Optional[UsAppToPersonUsecaseList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "InboundRequestUrl": inbound_request_url, + "InboundMethod": inbound_method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StickySender": serialize.boolean_to_string(sticky_sender), + "MmsConverter": serialize.boolean_to_string(mms_converter), + "SmartEncoding": serialize.boolean_to_string(smart_encoding), + "ScanMessageContent": scan_message_content, + "FallbackToLongCode": serialize.boolean_to_string( + fallback_to_long_code + ), + "AreaCodeGeomatch": serialize.boolean_to_string(area_code_geomatch), + "ValidityPeriod": validity_period, + "SynchronousValidation": serialize.boolean_to_string( + synchronous_validation + ), + "Usecase": usecase, + "UseInboundWebhookOnNumber": serialize.boolean_to_string( + use_inbound_webhook_on_number + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "InboundRequestUrl": inbound_request_url, + "InboundMethod": inbound_method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StickySender": serialize.boolean_to_string(sticky_sender), + "MmsConverter": serialize.boolean_to_string(mms_converter), + "SmartEncoding": serialize.boolean_to_string(smart_encoding), + "ScanMessageContent": scan_message_content, + "FallbackToLongCode": serialize.boolean_to_string( + fallback_to_long_code + ), + "AreaCodeGeomatch": serialize.boolean_to_string(area_code_geomatch), + "ValidityPeriod": validity_period, + "SynchronousValidation": serialize.boolean_to_string( + synchronous_validation + ), + "Usecase": usecase, + "UseInboundWebhookOnNumber": serialize.boolean_to_string( + use_inbound_webhook_on_number + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def alpha_senders(self) -> AlphaSenderList: + """ + Access the alpha_senders + """ + if self._alpha_senders is None: + self._alpha_senders = AlphaSenderList( + self._version, + self._solution["sid"], + ) + return self._alpha_senders + + @property + def channel_senders(self) -> ChannelSenderList: + """ + Access the channel_senders + """ + if self._channel_senders is None: + self._channel_senders = ChannelSenderList( + self._version, + self._solution["sid"], + ) + return self._channel_senders + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList( + self._version, + self._solution["sid"], + ) + return self._phone_numbers + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + if self._short_codes is None: + self._short_codes = ShortCodeList( + self._version, + self._solution["sid"], + ) + return self._short_codes + + @property + def us_app_to_person(self) -> UsAppToPersonList: + """ + Access the us_app_to_person + """ + if self._us_app_to_person is None: + self._us_app_to_person = UsAppToPersonList( + self._version, + self._solution["sid"], + ) + return self._us_app_to_person + + @property + def us_app_to_person_usecases(self) -> UsAppToPersonUsecaseList: + """ + Access the us_app_to_person_usecases + """ + if self._us_app_to_person_usecases is None: + self._us_app_to_person_usecases = UsAppToPersonUsecaseList( + self._version, + self._solution["sid"], + ) + return self._us_app_to_person_usecases + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + friendly_name: str, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "InboundRequestUrl": inbound_request_url, + "InboundMethod": inbound_method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StickySender": serialize.boolean_to_string(sticky_sender), + "MmsConverter": serialize.boolean_to_string(mms_converter), + "SmartEncoding": serialize.boolean_to_string(smart_encoding), + "ScanMessageContent": scan_message_content, + "FallbackToLongCode": serialize.boolean_to_string( + fallback_to_long_code + ), + "AreaCodeGeomatch": serialize.boolean_to_string(area_code_geomatch), + "ValidityPeriod": validity_period, + "SynchronousValidation": serialize.boolean_to_string( + synchronous_validation + ), + "Usecase": usecase, + "UseInboundWebhookOnNumber": serialize.boolean_to_string( + use_inbound_webhook_on_number + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + inbound_request_url: Union[str, object] = values.unset, + inbound_method: Union[str, object] = values.unset, + fallback_url: Union[str, object] = values.unset, + fallback_method: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + sticky_sender: Union[bool, object] = values.unset, + mms_converter: Union[bool, object] = values.unset, + smart_encoding: Union[bool, object] = values.unset, + scan_message_content: Union[ + "ServiceInstance.ScanMessageContent", object + ] = values.unset, + fallback_to_long_code: Union[bool, object] = values.unset, + area_code_geomatch: Union[bool, object] = values.unset, + validity_period: Union[int, object] = values.unset, + synchronous_validation: Union[bool, object] = values.unset, + usecase: Union[str, object] = values.unset, + use_inbound_webhook_on_number: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param inbound_request_url: The URL we call using `inbound_method` when a message is received by any phone number or short code in the Service. When this property is `null`, receiving inbound messages is disabled. All messages sent to the Twilio phone number or short code will not be logged and received on the Account. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `inbound_request_url` defined for the Messaging Service. + :param inbound_method: The HTTP method we should use to call `inbound_request_url`. Can be `GET` or `POST` and the default is `POST`. + :param fallback_url: The URL that we call using `fallback_method` if an error occurs while retrieving or executing the TwiML from the Inbound Request URL. If the `use_inbound_webhook_on_number` field is enabled then the webhook url defined on the phone number will override the `fallback_url` defined for the Messaging Service. + :param fallback_method: The HTTP method we should use to call `fallback_url`. Can be: `GET` or `POST`. + :param status_callback: The URL we should call to [pass status updates](https://www.twilio.com/docs/sms/api/message-resource#message-status-values) about message delivery. + :param sticky_sender: Whether to enable [Sticky Sender](https://www.twilio.com/docs/messaging/services#sticky-sender) on the Service instance. + :param mms_converter: Whether to enable the [MMS Converter](https://www.twilio.com/docs/messaging/services#mms-converter) for messages sent through the Service instance. + :param smart_encoding: Whether to enable [Smart Encoding](https://www.twilio.com/docs/messaging/services#smart-encoding) for messages sent through the Service instance. + :param scan_message_content: + :param fallback_to_long_code: [OBSOLETE] Former feature used to fallback to long code sender after certain short code message failures. + :param area_code_geomatch: Whether to enable [Area Code Geomatch](https://www.twilio.com/docs/messaging/services#area-code-geomatch) on the Service Instance. + :param validity_period: How long, in seconds, messages sent from the Service are valid. Can be an integer from `1` to `14,400`. + :param synchronous_validation: Reserved. + :param usecase: A string that describes the scenario in which the Messaging Service will be used. Possible values are `notifications`, `marketing`, `verification`, `discussion`, `poll`, `undeclared`. + :param use_inbound_webhook_on_number: A boolean value that indicates either the webhook url configured on the phone number will be used or `inbound_request_url`/`fallback_url` url will be called when a message is received from the phone number. If this field is enabled then the webhook url defined on the phone number will override the `inbound_request_url`/`fallback_url` defined for the Messaging Service. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "InboundRequestUrl": inbound_request_url, + "InboundMethod": inbound_method, + "FallbackUrl": fallback_url, + "FallbackMethod": fallback_method, + "StatusCallback": status_callback, + "StickySender": serialize.boolean_to_string(sticky_sender), + "MmsConverter": serialize.boolean_to_string(mms_converter), + "SmartEncoding": serialize.boolean_to_string(smart_encoding), + "ScanMessageContent": scan_message_content, + "FallbackToLongCode": serialize.boolean_to_string( + fallback_to_long_code + ), + "AreaCodeGeomatch": serialize.boolean_to_string(area_code_geomatch), + "ValidityPeriod": validity_period, + "SynchronousValidation": serialize.boolean_to_string( + synchronous_validation + ), + "Usecase": usecase, + "UseInboundWebhookOnNumber": serialize.boolean_to_string( + use_inbound_webhook_on_number + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9256ece Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/alpha_sender.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/alpha_sender.cpython-310.pyc new file mode 100644 index 0000000..69e6cae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/alpha_sender.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/channel_sender.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/channel_sender.cpython-310.pyc new file mode 100644 index 0000000..c805573 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/channel_sender.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..7cf5301 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/short_code.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/short_code.cpython-310.pyc new file mode 100644 index 0000000..328f48d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/short_code.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person.cpython-310.pyc new file mode 100644 index 0000000..490f2cb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person_usecase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person_usecase.cpython-310.pyc new file mode 100644 index 0000000..44a5a43 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/__pycache__/us_app_to_person_usecase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/alpha_sender.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/alpha_sender.py new file mode 100644 index 0000000..38d2d0d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/alpha_sender.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AlphaSenderInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the AlphaSender resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AlphaSender resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar alpha_sender: The Alphanumeric Sender ID string. + :ivar capabilities: An array of values that describe whether the number can receive calls or messages. Can be: `SMS`. + :ivar url: The absolute URL of the AlphaSender resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.alpha_sender: Optional[str] = payload.get("alpha_sender") + self.capabilities: Optional[List[str]] = payload.get("capabilities") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[AlphaSenderContext] = None + + @property + def _proxy(self) -> "AlphaSenderContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AlphaSenderContext for this AlphaSenderInstance + """ + if self._context is None: + self._context = AlphaSenderContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AlphaSenderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AlphaSenderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AlphaSenderInstance": + """ + Fetch the AlphaSenderInstance + + + :returns: The fetched AlphaSenderInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AlphaSenderInstance": + """ + Asynchronous coroutine to fetch the AlphaSenderInstance + + + :returns: The fetched AlphaSenderInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AlphaSenderContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the AlphaSenderContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the resource from. + :param sid: The SID of the AlphaSender resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/AlphaSenders/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the AlphaSenderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AlphaSenderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AlphaSenderInstance: + """ + Fetch the AlphaSenderInstance + + + :returns: The fetched AlphaSenderInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AlphaSenderInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AlphaSenderInstance: + """ + Asynchronous coroutine to fetch the AlphaSenderInstance + + + :returns: The fetched AlphaSenderInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AlphaSenderInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AlphaSenderPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AlphaSenderInstance: + """ + Build an instance of AlphaSenderInstance + + :param payload: Payload response from the API + """ + return AlphaSenderInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AlphaSenderList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the AlphaSenderList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/AlphaSenders".format(**self._solution) + + def create(self, alpha_sender: str) -> AlphaSenderInstance: + """ + Create the AlphaSenderInstance + + :param alpha_sender: The Alphanumeric Sender ID string. Can be up to 11 characters long. Valid characters are A-Z, a-z, 0-9, space, hyphen `-`, plus `+`, underscore `_` and ampersand `&`. This value cannot contain only numbers. + + :returns: The created AlphaSenderInstance + """ + + data = values.of( + { + "AlphaSender": alpha_sender, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AlphaSenderInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, alpha_sender: str) -> AlphaSenderInstance: + """ + Asynchronously create the AlphaSenderInstance + + :param alpha_sender: The Alphanumeric Sender ID string. Can be up to 11 characters long. Valid characters are A-Z, a-z, 0-9, space, hyphen `-`, plus `+`, underscore `_` and ampersand `&`. This value cannot contain only numbers. + + :returns: The created AlphaSenderInstance + """ + + data = values.of( + { + "AlphaSender": alpha_sender, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AlphaSenderInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AlphaSenderInstance]: + """ + Streams AlphaSenderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AlphaSenderInstance]: + """ + Asynchronously streams AlphaSenderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AlphaSenderInstance]: + """ + Lists AlphaSenderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AlphaSenderInstance]: + """ + Asynchronously lists AlphaSenderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AlphaSenderPage: + """ + Retrieve a single page of AlphaSenderInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AlphaSenderInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AlphaSenderPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AlphaSenderPage: + """ + Asynchronously retrieve a single page of AlphaSenderInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AlphaSenderInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AlphaSenderPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AlphaSenderPage: + """ + Retrieve a specific page of AlphaSenderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AlphaSenderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AlphaSenderPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AlphaSenderPage: + """ + Asynchronously retrieve a specific page of AlphaSenderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AlphaSenderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AlphaSenderPage(self._version, response, self._solution) + + def get(self, sid: str) -> AlphaSenderContext: + """ + Constructs a AlphaSenderContext + + :param sid: The SID of the AlphaSender resource to fetch. + """ + return AlphaSenderContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> AlphaSenderContext: + """ + Constructs a AlphaSenderContext + + :param sid: The SID of the AlphaSender resource to fetch. + """ + return AlphaSenderContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/channel_sender.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/channel_sender.py new file mode 100644 index 0000000..a8de3d0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/channel_sender.py @@ -0,0 +1,438 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ChannelSenderInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ChannelSender resource. + :ivar messaging_service_sid: The SID of the [Service](https://www.twilio.com/docs/messaging/services) the resource is associated with. + :ivar sid: The unique string that we created to identify the ChannelSender resource. + :ivar sender: The unique string that identifies the sender e.g whatsapp:+123456XXXX. + :ivar sender_type: A string value that identifies the sender type e.g WhatsApp, Messenger. + :ivar country_code: The 2-character [ISO Country Code](https://www.iso.org/iso-3166-country-codes.html) of the number. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the ChannelSender resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + messaging_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.sid: Optional[str] = payload.get("sid") + self.sender: Optional[str] = payload.get("sender") + self.sender_type: Optional[str] = payload.get("sender_type") + self.country_code: Optional[str] = payload.get("country_code") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "messaging_service_sid": messaging_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ChannelSenderContext] = None + + @property + def _proxy(self) -> "ChannelSenderContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChannelSenderContext for this ChannelSenderInstance + """ + if self._context is None: + self._context = ChannelSenderContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ChannelSenderInstance": + """ + Fetch the ChannelSenderInstance + + + :returns: The fetched ChannelSenderInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChannelSenderInstance": + """ + Asynchronous coroutine to fetch the ChannelSenderInstance + + + :returns: The fetched ChannelSenderInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelSenderContext(InstanceContext): + + def __init__(self, version: Version, messaging_service_sid: str, sid: str): + """ + Initialize the ChannelSenderContext + + :param version: Version that contains the resource + :param messaging_service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the resource from. + :param sid: The SID of the ChannelSender resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + "sid": sid, + } + self._uri = "/Services/{messaging_service_sid}/ChannelSenders/{sid}".format( + **self._solution + ) + + def fetch(self) -> ChannelSenderInstance: + """ + Fetch the ChannelSenderInstance + + + :returns: The fetched ChannelSenderInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChannelSenderInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChannelSenderInstance: + """ + Asynchronous coroutine to fetch the ChannelSenderInstance + + + :returns: The fetched ChannelSenderInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChannelSenderInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChannelSenderPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChannelSenderInstance: + """ + Build an instance of ChannelSenderInstance + + :param payload: Payload response from the API + """ + return ChannelSenderInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChannelSenderList(ListResource): + + def __init__(self, version: Version, messaging_service_sid: str): + """ + Initialize the ChannelSenderList + + :param version: Version that contains the resource + :param messaging_service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + self._uri = "/Services/{messaging_service_sid}/ChannelSenders".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChannelSenderInstance]: + """ + Streams ChannelSenderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChannelSenderInstance]: + """ + Asynchronously streams ChannelSenderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelSenderInstance]: + """ + Lists ChannelSenderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChannelSenderInstance]: + """ + Asynchronously lists ChannelSenderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelSenderPage: + """ + Retrieve a single page of ChannelSenderInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelSenderInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChannelSenderPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChannelSenderPage: + """ + Asynchronously retrieve a single page of ChannelSenderInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChannelSenderInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChannelSenderPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChannelSenderPage: + """ + Retrieve a specific page of ChannelSenderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelSenderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChannelSenderPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChannelSenderPage: + """ + Asynchronously retrieve a specific page of ChannelSenderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChannelSenderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChannelSenderPage(self._version, response, self._solution) + + def get(self, sid: str) -> ChannelSenderContext: + """ + Constructs a ChannelSenderContext + + :param sid: The SID of the ChannelSender resource to fetch. + """ + return ChannelSenderContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ChannelSenderContext: + """ + Constructs a ChannelSenderContext + + :param sid: The SID of the ChannelSender resource to fetch. + """ + return ChannelSenderContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/phone_number.py new file mode 100644 index 0000000..8b0b4c0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/phone_number.py @@ -0,0 +1,522 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PhoneNumberInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the PhoneNumber resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the PhoneNumber resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar country_code: The 2-character [ISO Country Code](https://www.iso.org/iso-3166-country-codes.html) of the number. + :ivar capabilities: An array of values that describe whether the number can receive calls or messages. Can be: `Voice`, `SMS`, and `MMS`. + :ivar url: The absolute URL of the PhoneNumber resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.phone_number: Optional[str] = payload.get("phone_number") + self.country_code: Optional[str] = payload.get("country_code") + self.capabilities: Optional[List[str]] = payload.get("capabilities") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the resource from. + :param sid: The SID of the PhoneNumber resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/PhoneNumbers/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PhoneNumberInstance: + """ + Build an instance of PhoneNumberInstance + + :param payload: Payload response from the API + """ + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/PhoneNumbers".format(**self._solution) + + def create(self, phone_number_sid: str) -> PhoneNumberInstance: + """ + Create the PhoneNumberInstance + + :param phone_number_sid: The SID of the Phone Number being added to the Service. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "PhoneNumberSid": phone_number_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, phone_number_sid: str) -> PhoneNumberInstance: + """ + Asynchronously create the PhoneNumberInstance + + :param phone_number_sid: The SID of the Phone Number being added to the Service. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "PhoneNumberSid": phone_number_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PhoneNumberInstance]: + """ + Streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PhoneNumberInstance]: + """ + Asynchronously streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Asynchronously lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PhoneNumberPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Asynchronously retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PhoneNumberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PhoneNumberPage: + """ + Retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PhoneNumberPage: + """ + Asynchronously retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + def get(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The SID of the PhoneNumber resource to fetch. + """ + return PhoneNumberContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The SID of the PhoneNumber resource to fetch. + """ + return PhoneNumberContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/short_code.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/short_code.py new file mode 100644 index 0000000..6537875 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/short_code.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ShortCodeInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the ShortCode resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ShortCode resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) the resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar short_code: The [E.164](https://www.twilio.com/docs/glossary/what-e164) format of the short code. + :ivar country_code: The 2-character [ISO Country Code](https://www.iso.org/iso-3166-country-codes.html) of the number. + :ivar capabilities: An array of values that describe whether the number can receive calls or messages. Can be: `SMS` and `MMS`. + :ivar url: The absolute URL of the ShortCode resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.short_code: Optional[str] = payload.get("short_code") + self.country_code: Optional[str] = payload.get("country_code") + self.capabilities: Optional[List[str]] = payload.get("capabilities") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ShortCodeContext] = None + + @property + def _proxy(self) -> "ShortCodeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ShortCodeContext for this ShortCodeInstance + """ + if self._context is None: + self._context = ShortCodeContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ShortCodeInstance": + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ShortCodeInstance": + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodeContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ShortCodeContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to fetch the resource from. + :param sid: The SID of the ShortCode resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/ShortCodes/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ShortCodeInstance: + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ShortCodeInstance: + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ShortCodeInstance: + """ + Build an instance of ShortCodeInstance + + :param payload: Payload response from the API + """ + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ShortCodeList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ShortCodeList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/chat/rest/service-resource) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/ShortCodes".format(**self._solution) + + def create(self, short_code_sid: str) -> ShortCodeInstance: + """ + Create the ShortCodeInstance + + :param short_code_sid: The SID of the ShortCode resource being added to the Service. + + :returns: The created ShortCodeInstance + """ + + data = values.of( + { + "ShortCodeSid": short_code_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, short_code_sid: str) -> ShortCodeInstance: + """ + Asynchronously create the ShortCodeInstance + + :param short_code_sid: The SID of the ShortCode resource being added to the Service. + + :returns: The created ShortCodeInstance + """ + + data = values.of( + { + "ShortCodeSid": short_code_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ShortCodeInstance]: + """ + Streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ShortCodeInstance]: + """ + Asynchronously streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Asynchronously lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ShortCodePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Asynchronously retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ShortCodePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ShortCodePage: + """ + Retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ShortCodePage: + """ + Asynchronously retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + def get(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The SID of the ShortCode resource to fetch. + """ + return ShortCodeContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The SID of the ShortCode resource to fetch. + """ + return ShortCodeContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person.py new file mode 100644 index 0000000..9034e4e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person.py @@ -0,0 +1,836 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UsAppToPersonInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies a US A2P Compliance resource `QE2c6890da8086d771620e9b13fadeba0b`. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the Campaign belongs to. + :ivar brand_registration_sid: The unique string to identify the A2P brand. + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) that the resource is associated with. + :ivar description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :ivar message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :ivar us_app_to_person_usecase: A2P Campaign Use Case. Examples: [ 2FA, EMERGENCY, MARKETING, SOLE_PROPRIETOR...]. SOLE_PROPRIETOR campaign use cases can only be created by SOLE_PROPRIETOR Brands, and there can only be one SOLE_PROPRIETOR campaign created per SOLE_PROPRIETOR Brand. + :ivar has_embedded_links: Indicate that this SMS campaign will send messages that contain links. + :ivar has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :ivar subscriber_opt_in: A boolean that specifies whether campaign has Subscriber Optin or not. + :ivar age_gated: A boolean that specifies whether campaign is age gated or not. + :ivar direct_lending: A boolean that specifies whether campaign allows direct lending or not. + :ivar campaign_status: Campaign status. Examples: IN_PROGRESS, VERIFIED, FAILED. + :ivar campaign_id: The Campaign Registry (TCR) Campaign ID. + :ivar is_externally_registered: Indicates whether the campaign was registered externally or not. + :ivar rate_limits: Rate limit and/or classification set by each carrier, Ex. AT&T or T-Mobile. + :ivar message_flow: Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :ivar opt_in_message: If end users can text in a keyword to start receiving messages from this campaign, the auto-reply messages sent to the end users must be provided. The opt-in response should include the Brand name, confirmation of opt-in enrollment to a recurring message campaign, how to get help, and clear description of how to opt-out. This field is required if end users can text in a keyword to start receiving messages from this campaign. 20 character minimum. 320 character maximum. + :ivar opt_out_message: Upon receiving the opt-out keywords from the end users, Twilio customers are expected to send back an auto-generated response, which must provide acknowledgment of the opt-out request and confirmation that no further messages will be sent. It is also recommended that these opt-out messages include the brand name. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :ivar help_message: When customers receive the help keywords from their end users, Twilio customers are expected to send back an auto-generated response; this may include the brand name and additional support contact information. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :ivar opt_in_keywords: If end users can text in a keyword to start receiving messages from this campaign, those keywords must be provided. This field is required if end users can text in a keyword to start receiving messages from this campaign. Values must be alphanumeric. 255 character maximum. + :ivar opt_out_keywords: End users should be able to text in a keyword to stop receiving messages from this campaign. Those keywords must be provided. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :ivar help_keywords: End users should be able to text in a keyword to receive help. Those keywords must be provided as part of the campaign registration request. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the US App to Person resource. + :ivar mock: A boolean that specifies whether campaign is a mock or not. Mock campaigns will be automatically created if using a mock brand. Mock campaigns should only be used for testing purposes. + :ivar errors: Details indicating why a campaign registration failed. These errors can indicate one or more fields that were incorrect or did not meet review requirements. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + messaging_service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.brand_registration_sid: Optional[str] = payload.get( + "brand_registration_sid" + ) + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.description: Optional[str] = payload.get("description") + self.message_samples: Optional[List[str]] = payload.get("message_samples") + self.us_app_to_person_usecase: Optional[str] = payload.get( + "us_app_to_person_usecase" + ) + self.has_embedded_links: Optional[bool] = payload.get("has_embedded_links") + self.has_embedded_phone: Optional[bool] = payload.get("has_embedded_phone") + self.subscriber_opt_in: Optional[bool] = payload.get("subscriber_opt_in") + self.age_gated: Optional[bool] = payload.get("age_gated") + self.direct_lending: Optional[bool] = payload.get("direct_lending") + self.campaign_status: Optional[str] = payload.get("campaign_status") + self.campaign_id: Optional[str] = payload.get("campaign_id") + self.is_externally_registered: Optional[bool] = payload.get( + "is_externally_registered" + ) + self.rate_limits: Optional[Dict[str, object]] = payload.get("rate_limits") + self.message_flow: Optional[str] = payload.get("message_flow") + self.opt_in_message: Optional[str] = payload.get("opt_in_message") + self.opt_out_message: Optional[str] = payload.get("opt_out_message") + self.help_message: Optional[str] = payload.get("help_message") + self.opt_in_keywords: Optional[List[str]] = payload.get("opt_in_keywords") + self.opt_out_keywords: Optional[List[str]] = payload.get("opt_out_keywords") + self.help_keywords: Optional[List[str]] = payload.get("help_keywords") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.mock: Optional[bool] = payload.get("mock") + self.errors: Optional[List[Dict[str, object]]] = payload.get("errors") + + self._solution = { + "messaging_service_sid": messaging_service_sid, + "sid": sid or self.sid, + } + self._context: Optional[UsAppToPersonContext] = None + + @property + def _proxy(self) -> "UsAppToPersonContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UsAppToPersonContext for this UsAppToPersonInstance + """ + if self._context is None: + self._context = UsAppToPersonContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the UsAppToPersonInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UsAppToPersonInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "UsAppToPersonInstance": + """ + Fetch the UsAppToPersonInstance + + + :returns: The fetched UsAppToPersonInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "UsAppToPersonInstance": + """ + Asynchronous coroutine to fetch the UsAppToPersonInstance + + + :returns: The fetched UsAppToPersonInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + has_embedded_links: bool, + has_embedded_phone: bool, + message_samples: List[str], + message_flow: str, + description: str, + age_gated: bool, + direct_lending: bool, + ) -> "UsAppToPersonInstance": + """ + Update the UsAppToPersonInstance + + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param age_gated: A boolean that specifies whether campaign requires age gate for federally legal content. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The updated UsAppToPersonInstance + """ + return self._proxy.update( + has_embedded_links=has_embedded_links, + has_embedded_phone=has_embedded_phone, + message_samples=message_samples, + message_flow=message_flow, + description=description, + age_gated=age_gated, + direct_lending=direct_lending, + ) + + async def update_async( + self, + has_embedded_links: bool, + has_embedded_phone: bool, + message_samples: List[str], + message_flow: str, + description: str, + age_gated: bool, + direct_lending: bool, + ) -> "UsAppToPersonInstance": + """ + Asynchronous coroutine to update the UsAppToPersonInstance + + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param age_gated: A boolean that specifies whether campaign requires age gate for federally legal content. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The updated UsAppToPersonInstance + """ + return await self._proxy.update_async( + has_embedded_links=has_embedded_links, + has_embedded_phone=has_embedded_phone, + message_samples=message_samples, + message_flow=message_flow, + description=description, + age_gated=age_gated, + direct_lending=direct_lending, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsAppToPersonContext(InstanceContext): + + def __init__(self, version: Version, messaging_service_sid: str, sid: str): + """ + Initialize the UsAppToPersonContext + + :param version: Version that contains the resource + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services/api) to update the resource from. + :param sid: The SID of the US A2P Compliance resource to update `QE2c6890da8086d771620e9b13fadeba0b`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + "sid": sid, + } + self._uri = "/Services/{messaging_service_sid}/Compliance/Usa2p/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the UsAppToPersonInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the UsAppToPersonInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> UsAppToPersonInstance: + """ + Fetch the UsAppToPersonInstance + + + :returns: The fetched UsAppToPersonInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> UsAppToPersonInstance: + """ + Asynchronous coroutine to fetch the UsAppToPersonInstance + + + :returns: The fetched UsAppToPersonInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + has_embedded_links: bool, + has_embedded_phone: bool, + message_samples: List[str], + message_flow: str, + description: str, + age_gated: bool, + direct_lending: bool, + ) -> UsAppToPersonInstance: + """ + Update the UsAppToPersonInstance + + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param age_gated: A boolean that specifies whether campaign requires age gate for federally legal content. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The updated UsAppToPersonInstance + """ + data = values.of( + { + "HasEmbeddedLinks": serialize.boolean_to_string(has_embedded_links), + "HasEmbeddedPhone": serialize.boolean_to_string(has_embedded_phone), + "MessageSamples": serialize.map(message_samples, lambda e: e), + "MessageFlow": message_flow, + "Description": description, + "AgeGated": serialize.boolean_to_string(age_gated), + "DirectLending": serialize.boolean_to_string(direct_lending), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + has_embedded_links: bool, + has_embedded_phone: bool, + message_samples: List[str], + message_flow: str, + description: str, + age_gated: bool, + direct_lending: bool, + ) -> UsAppToPersonInstance: + """ + Asynchronous coroutine to update the UsAppToPersonInstance + + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param age_gated: A boolean that specifies whether campaign requires age gate for federally legal content. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The updated UsAppToPersonInstance + """ + data = values.of( + { + "HasEmbeddedLinks": serialize.boolean_to_string(has_embedded_links), + "HasEmbeddedPhone": serialize.boolean_to_string(has_embedded_phone), + "MessageSamples": serialize.map(message_samples, lambda e: e), + "MessageFlow": message_flow, + "Description": description, + "AgeGated": serialize.boolean_to_string(age_gated), + "DirectLending": serialize.boolean_to_string(direct_lending), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsAppToPersonPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UsAppToPersonInstance: + """ + Build an instance of UsAppToPersonInstance + + :param payload: Payload response from the API + """ + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UsAppToPersonList(ListResource): + + def __init__(self, version: Version, messaging_service_sid: str): + """ + Initialize the UsAppToPersonList + + :param version: Version that contains the resource + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to fetch the resource from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + self._uri = "/Services/{messaging_service_sid}/Compliance/Usa2p".format( + **self._solution + ) + + def create( + self, + brand_registration_sid: str, + description: str, + message_flow: str, + message_samples: List[str], + us_app_to_person_usecase: str, + has_embedded_links: bool, + has_embedded_phone: bool, + opt_in_message: Union[str, object] = values.unset, + opt_out_message: Union[str, object] = values.unset, + help_message: Union[str, object] = values.unset, + opt_in_keywords: Union[List[str], object] = values.unset, + opt_out_keywords: Union[List[str], object] = values.unset, + help_keywords: Union[List[str], object] = values.unset, + subscriber_opt_in: Union[bool, object] = values.unset, + age_gated: Union[bool, object] = values.unset, + direct_lending: Union[bool, object] = values.unset, + ) -> UsAppToPersonInstance: + """ + Create the UsAppToPersonInstance + + :param brand_registration_sid: A2P Brand Registration SID + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param us_app_to_person_usecase: A2P Campaign Use Case. Examples: [ 2FA, EMERGENCY, MARKETING..] + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param opt_in_message: If end users can text in a keyword to start receiving messages from this campaign, the auto-reply messages sent to the end users must be provided. The opt-in response should include the Brand name, confirmation of opt-in enrollment to a recurring message campaign, how to get help, and clear description of how to opt-out. This field is required if end users can text in a keyword to start receiving messages from this campaign. 20 character minimum. 320 character maximum. + :param opt_out_message: Upon receiving the opt-out keywords from the end users, Twilio customers are expected to send back an auto-generated response, which must provide acknowledgment of the opt-out request and confirmation that no further messages will be sent. It is also recommended that these opt-out messages include the brand name. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :param help_message: When customers receive the help keywords from their end users, Twilio customers are expected to send back an auto-generated response; this may include the brand name and additional support contact information. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :param opt_in_keywords: If end users can text in a keyword to start receiving messages from this campaign, those keywords must be provided. This field is required if end users can text in a keyword to start receiving messages from this campaign. Values must be alphanumeric. 255 character maximum. + :param opt_out_keywords: End users should be able to text in a keyword to stop receiving messages from this campaign. Those keywords must be provided. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :param help_keywords: End users should be able to text in a keyword to receive help. Those keywords must be provided as part of the campaign registration request. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :param subscriber_opt_in: A boolean that specifies whether campaign has Subscriber Optin or not. + :param age_gated: A boolean that specifies whether campaign is age gated or not. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The created UsAppToPersonInstance + """ + + data = values.of( + { + "BrandRegistrationSid": brand_registration_sid, + "Description": description, + "MessageFlow": message_flow, + "MessageSamples": serialize.map(message_samples, lambda e: e), + "UsAppToPersonUsecase": us_app_to_person_usecase, + "HasEmbeddedLinks": serialize.boolean_to_string(has_embedded_links), + "HasEmbeddedPhone": serialize.boolean_to_string(has_embedded_phone), + "OptInMessage": opt_in_message, + "OptOutMessage": opt_out_message, + "HelpMessage": help_message, + "OptInKeywords": serialize.map(opt_in_keywords, lambda e: e), + "OptOutKeywords": serialize.map(opt_out_keywords, lambda e: e), + "HelpKeywords": serialize.map(help_keywords, lambda e: e), + "SubscriberOptIn": serialize.boolean_to_string(subscriber_opt_in), + "AgeGated": serialize.boolean_to_string(age_gated), + "DirectLending": serialize.boolean_to_string(direct_lending), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + async def create_async( + self, + brand_registration_sid: str, + description: str, + message_flow: str, + message_samples: List[str], + us_app_to_person_usecase: str, + has_embedded_links: bool, + has_embedded_phone: bool, + opt_in_message: Union[str, object] = values.unset, + opt_out_message: Union[str, object] = values.unset, + help_message: Union[str, object] = values.unset, + opt_in_keywords: Union[List[str], object] = values.unset, + opt_out_keywords: Union[List[str], object] = values.unset, + help_keywords: Union[List[str], object] = values.unset, + subscriber_opt_in: Union[bool, object] = values.unset, + age_gated: Union[bool, object] = values.unset, + direct_lending: Union[bool, object] = values.unset, + ) -> UsAppToPersonInstance: + """ + Asynchronously create the UsAppToPersonInstance + + :param brand_registration_sid: A2P Brand Registration SID + :param description: A short description of what this SMS campaign does. Min length: 40 characters. Max length: 4096 characters. + :param message_flow: Required for all Campaigns. Details around how a consumer opts-in to their campaign, therefore giving consent to receive their messages. If multiple opt-in methods can be used for the same campaign, they must all be listed. 40 character minimum. 2048 character maximum. + :param message_samples: An array of sample message strings, min two and max five. Min length for each sample: 20 chars. Max length for each sample: 1024 chars. + :param us_app_to_person_usecase: A2P Campaign Use Case. Examples: [ 2FA, EMERGENCY, MARKETING..] + :param has_embedded_links: Indicates that this SMS campaign will send messages that contain links. + :param has_embedded_phone: Indicates that this SMS campaign will send messages that contain phone numbers. + :param opt_in_message: If end users can text in a keyword to start receiving messages from this campaign, the auto-reply messages sent to the end users must be provided. The opt-in response should include the Brand name, confirmation of opt-in enrollment to a recurring message campaign, how to get help, and clear description of how to opt-out. This field is required if end users can text in a keyword to start receiving messages from this campaign. 20 character minimum. 320 character maximum. + :param opt_out_message: Upon receiving the opt-out keywords from the end users, Twilio customers are expected to send back an auto-generated response, which must provide acknowledgment of the opt-out request and confirmation that no further messages will be sent. It is also recommended that these opt-out messages include the brand name. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :param help_message: When customers receive the help keywords from their end users, Twilio customers are expected to send back an auto-generated response; this may include the brand name and additional support contact information. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). 20 character minimum. 320 character maximum. + :param opt_in_keywords: If end users can text in a keyword to start receiving messages from this campaign, those keywords must be provided. This field is required if end users can text in a keyword to start receiving messages from this campaign. Values must be alphanumeric. 255 character maximum. + :param opt_out_keywords: End users should be able to text in a keyword to stop receiving messages from this campaign. Those keywords must be provided. This field is required if managing opt out keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :param help_keywords: End users should be able to text in a keyword to receive help. Those keywords must be provided as part of the campaign registration request. This field is required if managing help keywords yourself (i.e. not using Twilio's Default or Advanced Opt Out features). Values must be alphanumeric. 255 character maximum. + :param subscriber_opt_in: A boolean that specifies whether campaign has Subscriber Optin or not. + :param age_gated: A boolean that specifies whether campaign is age gated or not. + :param direct_lending: A boolean that specifies whether campaign allows direct lending or not. + + :returns: The created UsAppToPersonInstance + """ + + data = values.of( + { + "BrandRegistrationSid": brand_registration_sid, + "Description": description, + "MessageFlow": message_flow, + "MessageSamples": serialize.map(message_samples, lambda e: e), + "UsAppToPersonUsecase": us_app_to_person_usecase, + "HasEmbeddedLinks": serialize.boolean_to_string(has_embedded_links), + "HasEmbeddedPhone": serialize.boolean_to_string(has_embedded_phone), + "OptInMessage": opt_in_message, + "OptOutMessage": opt_out_message, + "HelpMessage": help_message, + "OptInKeywords": serialize.map(opt_in_keywords, lambda e: e), + "OptOutKeywords": serialize.map(opt_out_keywords, lambda e: e), + "HelpKeywords": serialize.map(help_keywords, lambda e: e), + "SubscriberOptIn": serialize.boolean_to_string(subscriber_opt_in), + "AgeGated": serialize.boolean_to_string(age_gated), + "DirectLending": serialize.boolean_to_string(direct_lending), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return UsAppToPersonInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UsAppToPersonInstance]: + """ + Streams UsAppToPersonInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UsAppToPersonInstance]: + """ + Asynchronously streams UsAppToPersonInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsAppToPersonInstance]: + """ + Lists UsAppToPersonInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsAppToPersonInstance]: + """ + Asynchronously lists UsAppToPersonInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsAppToPersonPage: + """ + Retrieve a single page of UsAppToPersonInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsAppToPersonInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UsAppToPersonPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsAppToPersonPage: + """ + Asynchronously retrieve a single page of UsAppToPersonInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsAppToPersonInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UsAppToPersonPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UsAppToPersonPage: + """ + Retrieve a specific page of UsAppToPersonInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsAppToPersonInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UsAppToPersonPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UsAppToPersonPage: + """ + Asynchronously retrieve a specific page of UsAppToPersonInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsAppToPersonInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UsAppToPersonPage(self._version, response, self._solution) + + def get(self, sid: str) -> UsAppToPersonContext: + """ + Constructs a UsAppToPersonContext + + :param sid: The SID of the US A2P Compliance resource to update `QE2c6890da8086d771620e9b13fadeba0b`. + """ + return UsAppToPersonContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> UsAppToPersonContext: + """ + Constructs a UsAppToPersonContext + + :param sid: The SID of the US A2P Compliance resource to update `QE2c6890da8086d771620e9b13fadeba0b`. + """ + return UsAppToPersonContext( + self._version, + messaging_service_sid=self._solution["messaging_service_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person_usecase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person_usecase.py new file mode 100644 index 0000000..debcf94 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/service/us_app_to_person_usecase.py @@ -0,0 +1,129 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UsAppToPersonUsecaseInstance(InstanceResource): + """ + :ivar us_app_to_person_usecases: Human readable name, code, description and post_approval_required (indicates whether or not post approval is required for this Use Case) of A2P Campaign Use Cases. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], messaging_service_sid: str + ): + super().__init__(version) + + self.us_app_to_person_usecases: Optional[List[Dict[str, object]]] = payload.get( + "us_app_to_person_usecases" + ) + + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsAppToPersonUsecaseList(ListResource): + + def __init__(self, version: Version, messaging_service_sid: str): + """ + Initialize the UsAppToPersonUsecaseList + + :param version: Version that contains the resource + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to fetch the resource from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "messaging_service_sid": messaging_service_sid, + } + self._uri = ( + "/Services/{messaging_service_sid}/Compliance/Usa2p/Usecases".format( + **self._solution + ) + ) + + def fetch( + self, brand_registration_sid: Union[str, object] = values.unset + ) -> UsAppToPersonUsecaseInstance: + """ + Asynchronously fetch the UsAppToPersonUsecaseInstance + + :param brand_registration_sid: The unique string to identify the A2P brand. + :returns: The fetched UsAppToPersonUsecaseInstance + """ + + params = values.of( + { + "BrandRegistrationSid": brand_registration_sid, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=params) + + return UsAppToPersonUsecaseInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + async def fetch_async( + self, brand_registration_sid: Union[str, object] = values.unset + ) -> UsAppToPersonUsecaseInstance: + """ + Asynchronously fetch the UsAppToPersonUsecaseInstance + + :param brand_registration_sid: The unique string to identify the A2P brand. + :returns: The fetched UsAppToPersonUsecaseInstance + """ + + params = values.of( + { + "BrandRegistrationSid": brand_registration_sid, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=params + ) + + return UsAppToPersonUsecaseInstance( + self._version, + payload, + messaging_service_sid=self._solution["messaging_service_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/tollfree_verification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/tollfree_verification.py new file mode 100644 index 0000000..e742c57 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/tollfree_verification.py @@ -0,0 +1,1105 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TollfreeVerificationInstance(InstanceResource): + + class OptInType(object): + VERBAL = "VERBAL" + WEB_FORM = "WEB_FORM" + PAPER_FORM = "PAPER_FORM" + VIA_TEXT = "VIA_TEXT" + MOBILE_QR_CODE = "MOBILE_QR_CODE" + + class Status(object): + PENDING_REVIEW = "PENDING_REVIEW" + IN_REVIEW = "IN_REVIEW" + TWILIO_APPROVED = "TWILIO_APPROVED" + TWILIO_REJECTED = "TWILIO_REJECTED" + + """ + :ivar sid: The unique string to identify Tollfree Verification. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Tollfree Verification resource. + :ivar customer_profile_sid: Customer's Profile Bundle BundleSid. + :ivar trust_product_sid: Tollfree TrustProduct Bundle BundleSid. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar regulated_item_sid: The SID of the Regulated Item. + :ivar business_name: The name of the business or organization using the Tollfree number. + :ivar business_street_address: The address of the business or organization using the Tollfree number. + :ivar business_street_address2: The address of the business or organization using the Tollfree number. + :ivar business_city: The city of the business or organization using the Tollfree number. + :ivar business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :ivar business_postal_code: The postal code of the business or organization using the Tollfree number. + :ivar business_country: The country of the business or organization using the Tollfree number. + :ivar business_website: The website of the business or organization using the Tollfree number. + :ivar business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :ivar business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :ivar business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :ivar business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :ivar notification_email: The email address to receive the notification about the verification result. . + :ivar use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :ivar use_case_summary: Use this to further explain how messaging is used by the business or organization. + :ivar production_message_sample: An example of message content, i.e. a sample message. + :ivar opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :ivar opt_in_type: + :ivar message_volume: Estimate monthly volume of messages from the Tollfree Number. + :ivar additional_information: Additional information to be provided for verification. + :ivar tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :ivar status: + :ivar url: The absolute URL of the Tollfree Verification resource. + :ivar rejection_reason: The rejection reason given when a Tollfree Verification has been rejected. + :ivar error_code: The error code given when a Tollfree Verification has been rejected. + :ivar edit_expiration: The date and time when the ability to edit a rejected verification expires. + :ivar edit_allowed: If a rejected verification is allowed to be edited/resubmitted. Some rejection reasons allow editing and some do not. + :ivar rejection_reasons: A list of rejection reasons and codes describing why a Tollfree Verification has been rejected. + :ivar resource_links: The URLs of the documents associated with the Tollfree Verification resource. + :ivar external_reference_id: An optional external reference ID supplied by customer and echoed back on status retrieval. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.customer_profile_sid: Optional[str] = payload.get("customer_profile_sid") + self.trust_product_sid: Optional[str] = payload.get("trust_product_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.regulated_item_sid: Optional[str] = payload.get("regulated_item_sid") + self.business_name: Optional[str] = payload.get("business_name") + self.business_street_address: Optional[str] = payload.get( + "business_street_address" + ) + self.business_street_address2: Optional[str] = payload.get( + "business_street_address2" + ) + self.business_city: Optional[str] = payload.get("business_city") + self.business_state_province_region: Optional[str] = payload.get( + "business_state_province_region" + ) + self.business_postal_code: Optional[str] = payload.get("business_postal_code") + self.business_country: Optional[str] = payload.get("business_country") + self.business_website: Optional[str] = payload.get("business_website") + self.business_contact_first_name: Optional[str] = payload.get( + "business_contact_first_name" + ) + self.business_contact_last_name: Optional[str] = payload.get( + "business_contact_last_name" + ) + self.business_contact_email: Optional[str] = payload.get( + "business_contact_email" + ) + self.business_contact_phone: Optional[str] = payload.get( + "business_contact_phone" + ) + self.notification_email: Optional[str] = payload.get("notification_email") + self.use_case_categories: Optional[List[str]] = payload.get( + "use_case_categories" + ) + self.use_case_summary: Optional[str] = payload.get("use_case_summary") + self.production_message_sample: Optional[str] = payload.get( + "production_message_sample" + ) + self.opt_in_image_urls: Optional[List[str]] = payload.get("opt_in_image_urls") + self.opt_in_type: Optional["TollfreeVerificationInstance.OptInType"] = ( + payload.get("opt_in_type") + ) + self.message_volume: Optional[str] = payload.get("message_volume") + self.additional_information: Optional[str] = payload.get( + "additional_information" + ) + self.tollfree_phone_number_sid: Optional[str] = payload.get( + "tollfree_phone_number_sid" + ) + self.status: Optional["TollfreeVerificationInstance.Status"] = payload.get( + "status" + ) + self.url: Optional[str] = payload.get("url") + self.rejection_reason: Optional[str] = payload.get("rejection_reason") + self.error_code: Optional[int] = deserialize.integer(payload.get("error_code")) + self.edit_expiration: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("edit_expiration") + ) + self.edit_allowed: Optional[bool] = payload.get("edit_allowed") + self.rejection_reasons: Optional[List[Dict[str, object]]] = payload.get( + "rejection_reasons" + ) + self.resource_links: Optional[Dict[str, object]] = payload.get("resource_links") + self.external_reference_id: Optional[str] = payload.get("external_reference_id") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[TollfreeVerificationContext] = None + + @property + def _proxy(self) -> "TollfreeVerificationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TollfreeVerificationContext for this TollfreeVerificationInstance + """ + if self._context is None: + self._context = TollfreeVerificationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TollfreeVerificationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TollfreeVerificationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TollfreeVerificationInstance": + """ + Fetch the TollfreeVerificationInstance + + + :returns: The fetched TollfreeVerificationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TollfreeVerificationInstance": + """ + Asynchronous coroutine to fetch the TollfreeVerificationInstance + + + :returns: The fetched TollfreeVerificationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "TollfreeVerificationInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + edit_reason: Union[str, object] = values.unset, + ) -> "TollfreeVerificationInstance": + """ + Update the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param edit_reason: Describe why the verification is being edited. If the verification was rejected because of a technical issue, such as the website being down, and the issue has been resolved this parameter should be set to something similar to 'Website fixed'. + + :returns: The updated TollfreeVerificationInstance + """ + return self._proxy.update( + business_name=business_name, + business_website=business_website, + notification_email=notification_email, + use_case_categories=use_case_categories, + use_case_summary=use_case_summary, + production_message_sample=production_message_sample, + opt_in_image_urls=opt_in_image_urls, + opt_in_type=opt_in_type, + message_volume=message_volume, + business_street_address=business_street_address, + business_street_address2=business_street_address2, + business_city=business_city, + business_state_province_region=business_state_province_region, + business_postal_code=business_postal_code, + business_country=business_country, + additional_information=additional_information, + business_contact_first_name=business_contact_first_name, + business_contact_last_name=business_contact_last_name, + business_contact_email=business_contact_email, + business_contact_phone=business_contact_phone, + edit_reason=edit_reason, + ) + + async def update_async( + self, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "TollfreeVerificationInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + edit_reason: Union[str, object] = values.unset, + ) -> "TollfreeVerificationInstance": + """ + Asynchronous coroutine to update the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param edit_reason: Describe why the verification is being edited. If the verification was rejected because of a technical issue, such as the website being down, and the issue has been resolved this parameter should be set to something similar to 'Website fixed'. + + :returns: The updated TollfreeVerificationInstance + """ + return await self._proxy.update_async( + business_name=business_name, + business_website=business_website, + notification_email=notification_email, + use_case_categories=use_case_categories, + use_case_summary=use_case_summary, + production_message_sample=production_message_sample, + opt_in_image_urls=opt_in_image_urls, + opt_in_type=opt_in_type, + message_volume=message_volume, + business_street_address=business_street_address, + business_street_address2=business_street_address2, + business_city=business_city, + business_state_province_region=business_state_province_region, + business_postal_code=business_postal_code, + business_country=business_country, + additional_information=additional_information, + business_contact_first_name=business_contact_first_name, + business_contact_last_name=business_contact_last_name, + business_contact_email=business_contact_email, + business_contact_phone=business_contact_phone, + edit_reason=edit_reason, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TollfreeVerificationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the TollfreeVerificationContext + + :param version: Version that contains the resource + :param sid: The unique string to identify Tollfree Verification. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Tollfree/Verifications/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the TollfreeVerificationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TollfreeVerificationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TollfreeVerificationInstance: + """ + Fetch the TollfreeVerificationInstance + + + :returns: The fetched TollfreeVerificationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TollfreeVerificationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TollfreeVerificationInstance: + """ + Asynchronous coroutine to fetch the TollfreeVerificationInstance + + + :returns: The fetched TollfreeVerificationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TollfreeVerificationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "TollfreeVerificationInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + edit_reason: Union[str, object] = values.unset, + ) -> TollfreeVerificationInstance: + """ + Update the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param edit_reason: Describe why the verification is being edited. If the verification was rejected because of a technical issue, such as the website being down, and the issue has been resolved this parameter should be set to something similar to 'Website fixed'. + + :returns: The updated TollfreeVerificationInstance + """ + data = values.of( + { + "BusinessName": business_name, + "BusinessWebsite": business_website, + "NotificationEmail": notification_email, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + "EditReason": edit_reason, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TollfreeVerificationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "TollfreeVerificationInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + edit_reason: Union[str, object] = values.unset, + ) -> TollfreeVerificationInstance: + """ + Asynchronous coroutine to update the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param edit_reason: Describe why the verification is being edited. If the verification was rejected because of a technical issue, such as the website being down, and the issue has been resolved this parameter should be set to something similar to 'Website fixed'. + + :returns: The updated TollfreeVerificationInstance + """ + data = values.of( + { + "BusinessName": business_name, + "BusinessWebsite": business_website, + "NotificationEmail": notification_email, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + "EditReason": edit_reason, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TollfreeVerificationInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TollfreeVerificationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TollfreeVerificationInstance: + """ + Build an instance of TollfreeVerificationInstance + + :param payload: Payload response from the API + """ + return TollfreeVerificationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TollfreeVerificationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TollfreeVerificationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Tollfree/Verifications" + + def create( + self, + business_name: str, + business_website: str, + notification_email: str, + use_case_categories: List[str], + use_case_summary: str, + production_message_sample: str, + opt_in_image_urls: List[str], + opt_in_type: "TollfreeVerificationInstance.OptInType", + message_volume: str, + tollfree_phone_number_sid: str, + customer_profile_sid: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + external_reference_id: Union[str, object] = values.unset, + ) -> TollfreeVerificationInstance: + """ + Create the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param customer_profile_sid: Customer's Profile Bundle BundleSid. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param external_reference_id: An optional external reference ID supplied by customer and echoed back on status retrieval. + + :returns: The created TollfreeVerificationInstance + """ + + data = values.of( + { + "BusinessName": business_name, + "BusinessWebsite": business_website, + "NotificationEmail": notification_email, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "TollfreePhoneNumberSid": tollfree_phone_number_sid, + "CustomerProfileSid": customer_profile_sid, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + "ExternalReferenceId": external_reference_id, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TollfreeVerificationInstance(self._version, payload) + + async def create_async( + self, + business_name: str, + business_website: str, + notification_email: str, + use_case_categories: List[str], + use_case_summary: str, + production_message_sample: str, + opt_in_image_urls: List[str], + opt_in_type: "TollfreeVerificationInstance.OptInType", + message_volume: str, + tollfree_phone_number_sid: str, + customer_profile_sid: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + external_reference_id: Union[str, object] = values.unset, + ) -> TollfreeVerificationInstance: + """ + Asynchronously create the TollfreeVerificationInstance + + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param notification_email: The email address to receive the notification about the verification result. . + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param customer_profile_sid: Customer's Profile Bundle BundleSid. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The E.164 formatted phone number of the contact for the business or organization using the Tollfree number. + :param external_reference_id: An optional external reference ID supplied by customer and echoed back on status retrieval. + + :returns: The created TollfreeVerificationInstance + """ + + data = values.of( + { + "BusinessName": business_name, + "BusinessWebsite": business_website, + "NotificationEmail": notification_email, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "TollfreePhoneNumberSid": tollfree_phone_number_sid, + "CustomerProfileSid": customer_profile_sid, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + "ExternalReferenceId": external_reference_id, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TollfreeVerificationInstance(self._version, payload) + + def stream( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TollfreeVerificationInstance]: + """ + Streams TollfreeVerificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param "TollfreeVerificationInstance.Status" status: The compliance status of the Tollfree Verification record. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + tollfree_phone_number_sid=tollfree_phone_number_sid, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TollfreeVerificationInstance]: + """ + Asynchronously streams TollfreeVerificationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param "TollfreeVerificationInstance.Status" status: The compliance status of the Tollfree Verification record. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + tollfree_phone_number_sid=tollfree_phone_number_sid, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollfreeVerificationInstance]: + """ + Lists TollfreeVerificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param "TollfreeVerificationInstance.Status" status: The compliance status of the Tollfree Verification record. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + tollfree_phone_number_sid=tollfree_phone_number_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TollfreeVerificationInstance]: + """ + Asynchronously lists TollfreeVerificationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param "TollfreeVerificationInstance.Status" status: The compliance status of the Tollfree Verification record. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + tollfree_phone_number_sid=tollfree_phone_number_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollfreeVerificationPage: + """ + Retrieve a single page of TollfreeVerificationInstance records from the API. + Request is executed immediately + + :param tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param status: The compliance status of the Tollfree Verification record. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollfreeVerificationInstance + """ + data = values.of( + { + "TollfreePhoneNumberSid": tollfree_phone_number_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TollfreeVerificationPage(self._version, response) + + async def page_async( + self, + tollfree_phone_number_sid: Union[str, object] = values.unset, + status: Union["TollfreeVerificationInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TollfreeVerificationPage: + """ + Asynchronously retrieve a single page of TollfreeVerificationInstance records from the API. + Request is executed immediately + + :param tollfree_phone_number_sid: The SID of the Phone Number associated with the Tollfree Verification. + :param status: The compliance status of the Tollfree Verification record. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TollfreeVerificationInstance + """ + data = values.of( + { + "TollfreePhoneNumberSid": tollfree_phone_number_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TollfreeVerificationPage(self._version, response) + + def get_page(self, target_url: str) -> TollfreeVerificationPage: + """ + Retrieve a specific page of TollfreeVerificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollfreeVerificationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TollfreeVerificationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> TollfreeVerificationPage: + """ + Asynchronously retrieve a specific page of TollfreeVerificationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TollfreeVerificationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TollfreeVerificationPage(self._version, response) + + def get(self, sid: str) -> TollfreeVerificationContext: + """ + Constructs a TollfreeVerificationContext + + :param sid: The unique string to identify Tollfree Verification. + """ + return TollfreeVerificationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> TollfreeVerificationContext: + """ + Constructs a TollfreeVerificationContext + + :param sid: The unique string to identify Tollfree Verification. + """ + return TollfreeVerificationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/usecase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/usecase.py new file mode 100644 index 0000000..d7223f3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/messaging/v1/usecase.py @@ -0,0 +1,85 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Messaging + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UsecaseInstance(InstanceResource): + """ + :ivar usecases: Human readable use case details (usecase, description and purpose) of Messaging Service Use Cases. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.usecases: Optional[List[Dict[str, object]]] = payload.get("usecases") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class UsecaseList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the UsecaseList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services/Usecases" + + def fetch(self) -> UsecaseInstance: + """ + Asynchronously fetch the UsecaseInstance + + + :returns: The fetched UsecaseInstance + """ + + payload = self._version.fetch(method="GET", uri=self._uri) + + return UsecaseInstance(self._version, payload) + + async def fetch_async(self) -> UsecaseInstance: + """ + Asynchronously fetch the UsecaseInstance + + + :returns: The fetched UsecaseInstance + """ + + payload = await self._version.fetch_async(method="GET", uri=self._uri) + + return UsecaseInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/MicrovisorBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/MicrovisorBase.py new file mode 100644 index 0000000..e8c874b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/MicrovisorBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.microvisor.v1 import V1 + + +class MicrovisorBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Microvisor Domain + + :returns: Domain for Microvisor + """ + super().__init__(twilio, "https://microvisor.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Microvisor + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__init__.py new file mode 100644 index 0000000..6bf448b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__init__.py @@ -0,0 +1,43 @@ +from warnings import warn + +from twilio.rest.microvisor.MicrovisorBase import MicrovisorBase +from twilio.rest.microvisor.v1.account_config import AccountConfigList +from twilio.rest.microvisor.v1.account_secret import AccountSecretList +from twilio.rest.microvisor.v1.app import AppList +from twilio.rest.microvisor.v1.device import DeviceList + + +class Microvisor(MicrovisorBase): + @property + def account_configs(self) -> AccountConfigList: + warn( + "account_configs is deprecated. Use v1.account_configs instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.account_configs + + @property + def account_secrets(self) -> AccountSecretList: + warn( + "account_secrets is deprecated. Use v1.account_secrets instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.account_secrets + + @property + def apps(self) -> AppList: + warn( + "apps is deprecated. Use v1.apps instead.", DeprecationWarning, stacklevel=2 + ) + return self.v1.apps + + @property + def devices(self) -> DeviceList: + warn( + "devices is deprecated. Use v1.devices instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.devices diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/MicrovisorBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/MicrovisorBase.cpython-310.pyc new file mode 100644 index 0000000..d73b10f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/MicrovisorBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..089dcc3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__init__.py new file mode 100644 index 0000000..22010d6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__init__.py @@ -0,0 +1,67 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.microvisor.v1.account_config import AccountConfigList +from twilio.rest.microvisor.v1.account_secret import AccountSecretList +from twilio.rest.microvisor.v1.app import AppList +from twilio.rest.microvisor.v1.device import DeviceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Microvisor + + :param domain: The Twilio.microvisor domain + """ + super().__init__(domain, "v1") + self._account_configs: Optional[AccountConfigList] = None + self._account_secrets: Optional[AccountSecretList] = None + self._apps: Optional[AppList] = None + self._devices: Optional[DeviceList] = None + + @property + def account_configs(self) -> AccountConfigList: + if self._account_configs is None: + self._account_configs = AccountConfigList(self) + return self._account_configs + + @property + def account_secrets(self) -> AccountSecretList: + if self._account_secrets is None: + self._account_secrets = AccountSecretList(self) + return self._account_secrets + + @property + def apps(self) -> AppList: + if self._apps is None: + self._apps = AppList(self) + return self._apps + + @property + def devices(self) -> DeviceList: + if self._devices is None: + self._devices = DeviceList(self) + return self._devices + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2c17bd2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_config.cpython-310.pyc new file mode 100644 index 0000000..345ef85 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_secret.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_secret.cpython-310.pyc new file mode 100644 index 0000000..65665dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/__pycache__/account_secret.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_config.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_config.py new file mode 100644 index 0000000..3d37b01 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_config.py @@ -0,0 +1,555 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AccountConfigInstance(InstanceResource): + """ + :ivar key: The config key; up to 100 characters. + :ivar date_updated: + :ivar value: The config value; up to 4096 characters. + :ivar url: The absolute URL of the Config. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], key: Optional[str] = None + ): + super().__init__(version) + + self.key: Optional[str] = payload.get("key") + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.value: Optional[str] = payload.get("value") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "key": key or self.key, + } + self._context: Optional[AccountConfigContext] = None + + @property + def _proxy(self) -> "AccountConfigContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AccountConfigContext for this AccountConfigInstance + """ + if self._context is None: + self._context = AccountConfigContext( + self._version, + key=self._solution["key"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AccountConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AccountConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AccountConfigInstance": + """ + Fetch the AccountConfigInstance + + + :returns: The fetched AccountConfigInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AccountConfigInstance": + """ + Asynchronous coroutine to fetch the AccountConfigInstance + + + :returns: The fetched AccountConfigInstance + """ + return await self._proxy.fetch_async() + + def update(self, value: str) -> "AccountConfigInstance": + """ + Update the AccountConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated AccountConfigInstance + """ + return self._proxy.update( + value=value, + ) + + async def update_async(self, value: str) -> "AccountConfigInstance": + """ + Asynchronous coroutine to update the AccountConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated AccountConfigInstance + """ + return await self._proxy.update_async( + value=value, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountConfigContext(InstanceContext): + + def __init__(self, version: Version, key: str): + """ + Initialize the AccountConfigContext + + :param version: Version that contains the resource + :param key: The config key; up to 100 characters. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "key": key, + } + self._uri = "/Configs/{key}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the AccountConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AccountConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AccountConfigInstance: + """ + Fetch the AccountConfigInstance + + + :returns: The fetched AccountConfigInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AccountConfigInstance( + self._version, + payload, + key=self._solution["key"], + ) + + async def fetch_async(self) -> AccountConfigInstance: + """ + Asynchronous coroutine to fetch the AccountConfigInstance + + + :returns: The fetched AccountConfigInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AccountConfigInstance( + self._version, + payload, + key=self._solution["key"], + ) + + def update(self, value: str) -> AccountConfigInstance: + """ + Update the AccountConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated AccountConfigInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountConfigInstance(self._version, payload, key=self._solution["key"]) + + async def update_async(self, value: str) -> AccountConfigInstance: + """ + Asynchronous coroutine to update the AccountConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated AccountConfigInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountConfigInstance(self._version, payload, key=self._solution["key"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountConfigPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AccountConfigInstance: + """ + Build an instance of AccountConfigInstance + + :param payload: Payload response from the API + """ + return AccountConfigInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AccountConfigList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AccountConfigList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Configs" + + def create(self, key: str, value: str) -> AccountConfigInstance: + """ + Create the AccountConfigInstance + + :param key: The config key; up to 100 characters. + :param value: The config value; up to 4096 characters. + + :returns: The created AccountConfigInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountConfigInstance(self._version, payload) + + async def create_async(self, key: str, value: str) -> AccountConfigInstance: + """ + Asynchronously create the AccountConfigInstance + + :param key: The config key; up to 100 characters. + :param value: The config value; up to 4096 characters. + + :returns: The created AccountConfigInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountConfigInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AccountConfigInstance]: + """ + Streams AccountConfigInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AccountConfigInstance]: + """ + Asynchronously streams AccountConfigInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountConfigInstance]: + """ + Lists AccountConfigInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountConfigInstance]: + """ + Asynchronously lists AccountConfigInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountConfigPage: + """ + Retrieve a single page of AccountConfigInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountConfigInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AccountConfigPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountConfigPage: + """ + Asynchronously retrieve a single page of AccountConfigInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountConfigInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AccountConfigPage(self._version, response) + + def get_page(self, target_url: str) -> AccountConfigPage: + """ + Retrieve a specific page of AccountConfigInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountConfigInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AccountConfigPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AccountConfigPage: + """ + Asynchronously retrieve a specific page of AccountConfigInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountConfigInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AccountConfigPage(self._version, response) + + def get(self, key: str) -> AccountConfigContext: + """ + Constructs a AccountConfigContext + + :param key: The config key; up to 100 characters. + """ + return AccountConfigContext(self._version, key=key) + + def __call__(self, key: str) -> AccountConfigContext: + """ + Constructs a AccountConfigContext + + :param key: The config key; up to 100 characters. + """ + return AccountConfigContext(self._version, key=key) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_secret.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_secret.py new file mode 100644 index 0000000..cca0e1d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/account_secret.py @@ -0,0 +1,553 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AccountSecretInstance(InstanceResource): + """ + :ivar key: The secret key; up to 100 characters. + :ivar date_rotated: + :ivar url: The absolute URL of the Secret. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], key: Optional[str] = None + ): + super().__init__(version) + + self.key: Optional[str] = payload.get("key") + self.date_rotated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_rotated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "key": key or self.key, + } + self._context: Optional[AccountSecretContext] = None + + @property + def _proxy(self) -> "AccountSecretContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AccountSecretContext for this AccountSecretInstance + """ + if self._context is None: + self._context = AccountSecretContext( + self._version, + key=self._solution["key"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AccountSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AccountSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AccountSecretInstance": + """ + Fetch the AccountSecretInstance + + + :returns: The fetched AccountSecretInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AccountSecretInstance": + """ + Asynchronous coroutine to fetch the AccountSecretInstance + + + :returns: The fetched AccountSecretInstance + """ + return await self._proxy.fetch_async() + + def update(self, value: str) -> "AccountSecretInstance": + """ + Update the AccountSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated AccountSecretInstance + """ + return self._proxy.update( + value=value, + ) + + async def update_async(self, value: str) -> "AccountSecretInstance": + """ + Asynchronous coroutine to update the AccountSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated AccountSecretInstance + """ + return await self._proxy.update_async( + value=value, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountSecretContext(InstanceContext): + + def __init__(self, version: Version, key: str): + """ + Initialize the AccountSecretContext + + :param version: Version that contains the resource + :param key: The secret key; up to 100 characters. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "key": key, + } + self._uri = "/Secrets/{key}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the AccountSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AccountSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AccountSecretInstance: + """ + Fetch the AccountSecretInstance + + + :returns: The fetched AccountSecretInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AccountSecretInstance( + self._version, + payload, + key=self._solution["key"], + ) + + async def fetch_async(self) -> AccountSecretInstance: + """ + Asynchronous coroutine to fetch the AccountSecretInstance + + + :returns: The fetched AccountSecretInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AccountSecretInstance( + self._version, + payload, + key=self._solution["key"], + ) + + def update(self, value: str) -> AccountSecretInstance: + """ + Update the AccountSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated AccountSecretInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountSecretInstance(self._version, payload, key=self._solution["key"]) + + async def update_async(self, value: str) -> AccountSecretInstance: + """ + Asynchronous coroutine to update the AccountSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated AccountSecretInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountSecretInstance(self._version, payload, key=self._solution["key"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccountSecretPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AccountSecretInstance: + """ + Build an instance of AccountSecretInstance + + :param payload: Payload response from the API + """ + return AccountSecretInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AccountSecretList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AccountSecretList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Secrets" + + def create(self, key: str, value: str) -> AccountSecretInstance: + """ + Create the AccountSecretInstance + + :param key: The secret key; up to 100 characters. + :param value: The secret value; up to 4096 characters. + + :returns: The created AccountSecretInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountSecretInstance(self._version, payload) + + async def create_async(self, key: str, value: str) -> AccountSecretInstance: + """ + Asynchronously create the AccountSecretInstance + + :param key: The secret key; up to 100 characters. + :param value: The secret value; up to 4096 characters. + + :returns: The created AccountSecretInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccountSecretInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AccountSecretInstance]: + """ + Streams AccountSecretInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AccountSecretInstance]: + """ + Asynchronously streams AccountSecretInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountSecretInstance]: + """ + Lists AccountSecretInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AccountSecretInstance]: + """ + Asynchronously lists AccountSecretInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountSecretPage: + """ + Retrieve a single page of AccountSecretInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountSecretInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AccountSecretPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AccountSecretPage: + """ + Asynchronously retrieve a single page of AccountSecretInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AccountSecretInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AccountSecretPage(self._version, response) + + def get_page(self, target_url: str) -> AccountSecretPage: + """ + Retrieve a specific page of AccountSecretInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountSecretInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AccountSecretPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AccountSecretPage: + """ + Asynchronously retrieve a specific page of AccountSecretInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AccountSecretInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AccountSecretPage(self._version, response) + + def get(self, key: str) -> AccountSecretContext: + """ + Constructs a AccountSecretContext + + :param key: The secret key; up to 100 characters. + """ + return AccountSecretContext(self._version, key=key) + + def __call__(self, key: str) -> AccountSecretContext: + """ + Constructs a AccountSecretContext + + :param key: The secret key; up to 100 characters. + """ + return AccountSecretContext(self._version, key=key) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__init__.py new file mode 100644 index 0000000..0c91fe3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__init__.py @@ -0,0 +1,469 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.microvisor.v1.app.app_manifest import AppManifestList + + +class AppInstance(InstanceResource): + """ + :ivar sid: A 34-character string that uniquely identifies this App. + :ivar account_sid: The unique SID identifier of the Account. + :ivar hash: App manifest hash represented as `hash_algorithm:hash_value`. + :ivar unique_name: A developer-defined string that uniquely identifies the App. This value must be unique for all Apps on this Account. The `unique_name` value may be used as an alternative to the `sid` in the URL path to address the resource. + :ivar date_created: The date that this App was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this App was last updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The URL of this resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.hash: Optional[str] = payload.get("hash") + self.unique_name: Optional[str] = payload.get("unique_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AppContext] = None + + @property + def _proxy(self) -> "AppContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AppContext for this AppInstance + """ + if self._context is None: + self._context = AppContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AppInstance": + """ + Fetch the AppInstance + + + :returns: The fetched AppInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AppInstance": + """ + Asynchronous coroutine to fetch the AppInstance + + + :returns: The fetched AppInstance + """ + return await self._proxy.fetch_async() + + @property + def app_manifests(self) -> AppManifestList: + """ + Access the app_manifests + """ + return self._proxy.app_manifests + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AppContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AppContext + + :param version: Version that contains the resource + :param sid: A 34-character string that uniquely identifies this App. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Apps/{sid}".format(**self._solution) + + self._app_manifests: Optional[AppManifestList] = None + + def delete(self) -> bool: + """ + Deletes the AppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AppInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AppInstance: + """ + Fetch the AppInstance + + + :returns: The fetched AppInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AppInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AppInstance: + """ + Asynchronous coroutine to fetch the AppInstance + + + :returns: The fetched AppInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AppInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def app_manifests(self) -> AppManifestList: + """ + Access the app_manifests + """ + if self._app_manifests is None: + self._app_manifests = AppManifestList( + self._version, + self._solution["sid"], + ) + return self._app_manifests + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AppPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AppInstance: + """ + Build an instance of AppInstance + + :param payload: Payload response from the API + """ + return AppInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AppList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AppList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Apps" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AppInstance]: + """ + Streams AppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AppInstance]: + """ + Asynchronously streams AppInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AppInstance]: + """ + Lists AppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AppInstance]: + """ + Asynchronously lists AppInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AppPage: + """ + Retrieve a single page of AppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AppPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AppPage: + """ + Asynchronously retrieve a single page of AppInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AppInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AppPage(self._version, response) + + def get_page(self, target_url: str) -> AppPage: + """ + Retrieve a specific page of AppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AppInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AppPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AppPage: + """ + Asynchronously retrieve a specific page of AppInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AppInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AppPage(self._version, response) + + def get(self, sid: str) -> AppContext: + """ + Constructs a AppContext + + :param sid: A 34-character string that uniquely identifies this App. + """ + return AppContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AppContext: + """ + Constructs a AppContext + + :param sid: A 34-character string that uniquely identifies this App. + """ + return AppContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e7b4124 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/app_manifest.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/app_manifest.cpython-310.pyc new file mode 100644 index 0000000..155bb92 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/__pycache__/app_manifest.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/app_manifest.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/app_manifest.py new file mode 100644 index 0000000..4f9eab0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/app/app_manifest.py @@ -0,0 +1,188 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AppManifestInstance(InstanceResource): + """ + :ivar app_sid: A 34-character string that uniquely identifies this App. + :ivar hash: App manifest hash represented as `hash_algorithm:hash_value`. + :ivar encoded_bytes: The base-64 encoded manifest + :ivar url: The absolute URL of this Manifest. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], app_sid: str): + super().__init__(version) + + self.app_sid: Optional[str] = payload.get("app_sid") + self.hash: Optional[str] = payload.get("hash") + self.encoded_bytes: Optional[str] = payload.get("encoded_bytes") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "app_sid": app_sid, + } + self._context: Optional[AppManifestContext] = None + + @property + def _proxy(self) -> "AppManifestContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AppManifestContext for this AppManifestInstance + """ + if self._context is None: + self._context = AppManifestContext( + self._version, + app_sid=self._solution["app_sid"], + ) + return self._context + + def fetch(self) -> "AppManifestInstance": + """ + Fetch the AppManifestInstance + + + :returns: The fetched AppManifestInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AppManifestInstance": + """ + Asynchronous coroutine to fetch the AppManifestInstance + + + :returns: The fetched AppManifestInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AppManifestContext(InstanceContext): + + def __init__(self, version: Version, app_sid: str): + """ + Initialize the AppManifestContext + + :param version: Version that contains the resource + :param app_sid: A 34-character string that uniquely identifies this App. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "app_sid": app_sid, + } + self._uri = "/Apps/{app_sid}/Manifest".format(**self._solution) + + def fetch(self) -> AppManifestInstance: + """ + Fetch the AppManifestInstance + + + :returns: The fetched AppManifestInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AppManifestInstance( + self._version, + payload, + app_sid=self._solution["app_sid"], + ) + + async def fetch_async(self) -> AppManifestInstance: + """ + Asynchronous coroutine to fetch the AppManifestInstance + + + :returns: The fetched AppManifestInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AppManifestInstance( + self._version, + payload, + app_sid=self._solution["app_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AppManifestList(ListResource): + + def __init__(self, version: Version, app_sid: str): + """ + Initialize the AppManifestList + + :param version: Version that contains the resource + :param app_sid: A 34-character string that uniquely identifies this App. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "app_sid": app_sid, + } + + def get(self) -> AppManifestContext: + """ + Constructs a AppManifestContext + + """ + return AppManifestContext(self._version, app_sid=self._solution["app_sid"]) + + def __call__(self) -> AppManifestContext: + """ + Constructs a AppManifestContext + + """ + return AppManifestContext(self._version, app_sid=self._solution["app_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__init__.py new file mode 100644 index 0000000..42a1734 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__init__.py @@ -0,0 +1,566 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.microvisor.v1.device.device_config import DeviceConfigList +from twilio.rest.microvisor.v1.device.device_secret import DeviceSecretList + + +class DeviceInstance(InstanceResource): + """ + :ivar sid: A 34-character string that uniquely identifies this Device. + :ivar unique_name: A developer-defined string that uniquely identifies the Device. This value must be unique for all Devices on this Account. The `unique_name` value may be used as an alternative to the `sid` in the URL path to address the resource. + :ivar account_sid: The unique SID identifier of the Account. + :ivar app: Information about the target App and the App reported by this Device. Contains the properties `target_sid`, `date_targeted`, `update_status` (one of `up-to-date`, `pending` and `error`), `update_error_code`, `reported_sid` and `date_reported`. + :ivar logging: Object specifying whether application logging is enabled for this Device. Contains the properties `enabled` and `date_expires`. + :ivar date_created: The date that this Device was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Device was last updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The URL of this resource. + :ivar links: The absolute URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.app: Optional[Dict[str, object]] = payload.get("app") + self.logging: Optional[Dict[str, object]] = payload.get("logging") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[DeviceContext] = None + + @property + def _proxy(self) -> "DeviceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeviceContext for this DeviceInstance + """ + if self._context is None: + self._context = DeviceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "DeviceInstance": + """ + Fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeviceInstance": + """ + Asynchronous coroutine to fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + target_app: Union[str, object] = values.unset, + logging_enabled: Union[bool, object] = values.unset, + restart_app: Union[bool, object] = values.unset, + ) -> "DeviceInstance": + """ + Update the DeviceInstance + + :param unique_name: A unique and addressable name to be assigned to this Device by the developer. It may be used in place of the Device SID. + :param target_app: The SID or unique name of the App to be targeted to the Device. + :param logging_enabled: A Boolean flag specifying whether to enable application logging. Logs will be enabled or extended for 24 hours. + :param restart_app: Set to true to restart the App running on the Device. + + :returns: The updated DeviceInstance + """ + return self._proxy.update( + unique_name=unique_name, + target_app=target_app, + logging_enabled=logging_enabled, + restart_app=restart_app, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + target_app: Union[str, object] = values.unset, + logging_enabled: Union[bool, object] = values.unset, + restart_app: Union[bool, object] = values.unset, + ) -> "DeviceInstance": + """ + Asynchronous coroutine to update the DeviceInstance + + :param unique_name: A unique and addressable name to be assigned to this Device by the developer. It may be used in place of the Device SID. + :param target_app: The SID or unique name of the App to be targeted to the Device. + :param logging_enabled: A Boolean flag specifying whether to enable application logging. Logs will be enabled or extended for 24 hours. + :param restart_app: Set to true to restart the App running on the Device. + + :returns: The updated DeviceInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + target_app=target_app, + logging_enabled=logging_enabled, + restart_app=restart_app, + ) + + @property + def device_configs(self) -> DeviceConfigList: + """ + Access the device_configs + """ + return self._proxy.device_configs + + @property + def device_secrets(self) -> DeviceSecretList: + """ + Access the device_secrets + """ + return self._proxy.device_secrets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the DeviceContext + + :param version: Version that contains the resource + :param sid: A 34-character string that uniquely identifies this Device. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Devices/{sid}".format(**self._solution) + + self._device_configs: Optional[DeviceConfigList] = None + self._device_secrets: Optional[DeviceSecretList] = None + + def fetch(self) -> DeviceInstance: + """ + Fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeviceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeviceInstance: + """ + Asynchronous coroutine to fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeviceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + target_app: Union[str, object] = values.unset, + logging_enabled: Union[bool, object] = values.unset, + restart_app: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Update the DeviceInstance + + :param unique_name: A unique and addressable name to be assigned to this Device by the developer. It may be used in place of the Device SID. + :param target_app: The SID or unique name of the App to be targeted to the Device. + :param logging_enabled: A Boolean flag specifying whether to enable application logging. Logs will be enabled or extended for 24 hours. + :param restart_app: Set to true to restart the App running on the Device. + + :returns: The updated DeviceInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "TargetApp": target_app, + "LoggingEnabled": serialize.boolean_to_string(logging_enabled), + "RestartApp": serialize.boolean_to_string(restart_app), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + target_app: Union[str, object] = values.unset, + logging_enabled: Union[bool, object] = values.unset, + restart_app: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Asynchronous coroutine to update the DeviceInstance + + :param unique_name: A unique and addressable name to be assigned to this Device by the developer. It may be used in place of the Device SID. + :param target_app: The SID or unique name of the App to be targeted to the Device. + :param logging_enabled: A Boolean flag specifying whether to enable application logging. Logs will be enabled or extended for 24 hours. + :param restart_app: Set to true to restart the App running on the Device. + + :returns: The updated DeviceInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "TargetApp": target_app, + "LoggingEnabled": serialize.boolean_to_string(logging_enabled), + "RestartApp": serialize.boolean_to_string(restart_app), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def device_configs(self) -> DeviceConfigList: + """ + Access the device_configs + """ + if self._device_configs is None: + self._device_configs = DeviceConfigList( + self._version, + self._solution["sid"], + ) + return self._device_configs + + @property + def device_secrets(self) -> DeviceSecretList: + """ + Access the device_secrets + """ + if self._device_secrets is None: + self._device_secrets = DeviceSecretList( + self._version, + self._solution["sid"], + ) + return self._device_secrets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DevicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeviceInstance: + """ + Build an instance of DeviceInstance + + :param payload: Payload response from the API + """ + return DeviceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeviceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DeviceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Devices" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeviceInstance]: + """ + Streams DeviceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeviceInstance]: + """ + Asynchronously streams DeviceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceInstance]: + """ + Lists DeviceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceInstance]: + """ + Asynchronously lists DeviceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DevicePage: + """ + Retrieve a single page of DeviceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DevicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DevicePage: + """ + Asynchronously retrieve a single page of DeviceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DevicePage(self._version, response) + + def get_page(self, target_url: str) -> DevicePage: + """ + Retrieve a specific page of DeviceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DevicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> DevicePage: + """ + Asynchronously retrieve a specific page of DeviceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DevicePage(self._version, response) + + def get(self, sid: str) -> DeviceContext: + """ + Constructs a DeviceContext + + :param sid: A 34-character string that uniquely identifies this Device. + """ + return DeviceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> DeviceContext: + """ + Constructs a DeviceContext + + :param sid: A 34-character string that uniquely identifies this Device. + """ + return DeviceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ee93f29 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_config.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_config.cpython-310.pyc new file mode 100644 index 0000000..cb8a06a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_config.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_secret.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_secret.cpython-310.pyc new file mode 100644 index 0000000..3ef7e3f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/__pycache__/device_secret.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_config.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_config.py new file mode 100644 index 0000000..3607e97 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_config.py @@ -0,0 +1,592 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeviceConfigInstance(InstanceResource): + """ + :ivar device_sid: A 34-character string that uniquely identifies the parent Device. + :ivar key: The config key; up to 100 characters. + :ivar value: The config value; up to 4096 characters. + :ivar date_updated: + :ivar url: The absolute URL of the Config. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + device_sid: str, + key: Optional[str] = None, + ): + super().__init__(version) + + self.device_sid: Optional[str] = payload.get("device_sid") + self.key: Optional[str] = payload.get("key") + self.value: Optional[str] = payload.get("value") + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "device_sid": device_sid, + "key": key or self.key, + } + self._context: Optional[DeviceConfigContext] = None + + @property + def _proxy(self) -> "DeviceConfigContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeviceConfigContext for this DeviceConfigInstance + """ + if self._context is None: + self._context = DeviceConfigContext( + self._version, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DeviceConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DeviceConfigInstance": + """ + Fetch the DeviceConfigInstance + + + :returns: The fetched DeviceConfigInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeviceConfigInstance": + """ + Asynchronous coroutine to fetch the DeviceConfigInstance + + + :returns: The fetched DeviceConfigInstance + """ + return await self._proxy.fetch_async() + + def update(self, value: str) -> "DeviceConfigInstance": + """ + Update the DeviceConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated DeviceConfigInstance + """ + return self._proxy.update( + value=value, + ) + + async def update_async(self, value: str) -> "DeviceConfigInstance": + """ + Asynchronous coroutine to update the DeviceConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated DeviceConfigInstance + """ + return await self._proxy.update_async( + value=value, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceConfigContext(InstanceContext): + + def __init__(self, version: Version, device_sid: str, key: str): + """ + Initialize the DeviceConfigContext + + :param version: Version that contains the resource + :param device_sid: A 34-character string that uniquely identifies the Device. + :param key: The config key; up to 100 characters. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "device_sid": device_sid, + "key": key, + } + self._uri = "/Devices/{device_sid}/Configs/{key}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the DeviceConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceConfigInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DeviceConfigInstance: + """ + Fetch the DeviceConfigInstance + + + :returns: The fetched DeviceConfigInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeviceConfigInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + async def fetch_async(self) -> DeviceConfigInstance: + """ + Asynchronous coroutine to fetch the DeviceConfigInstance + + + :returns: The fetched DeviceConfigInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeviceConfigInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + def update(self, value: str) -> DeviceConfigInstance: + """ + Update the DeviceConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated DeviceConfigInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceConfigInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + async def update_async(self, value: str) -> DeviceConfigInstance: + """ + Asynchronous coroutine to update the DeviceConfigInstance + + :param value: The config value; up to 4096 characters. + + :returns: The updated DeviceConfigInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceConfigInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceConfigPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeviceConfigInstance: + """ + Build an instance of DeviceConfigInstance + + :param payload: Payload response from the API + """ + return DeviceConfigInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeviceConfigList(ListResource): + + def __init__(self, version: Version, device_sid: str): + """ + Initialize the DeviceConfigList + + :param version: Version that contains the resource + :param device_sid: A 34-character string that uniquely identifies the Device. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "device_sid": device_sid, + } + self._uri = "/Devices/{device_sid}/Configs".format(**self._solution) + + def create(self, key: str, value: str) -> DeviceConfigInstance: + """ + Create the DeviceConfigInstance + + :param key: The config key; up to 100 characters. + :param value: The config value; up to 4096 characters. + + :returns: The created DeviceConfigInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceConfigInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + async def create_async(self, key: str, value: str) -> DeviceConfigInstance: + """ + Asynchronously create the DeviceConfigInstance + + :param key: The config key; up to 100 characters. + :param value: The config value; up to 4096 characters. + + :returns: The created DeviceConfigInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceConfigInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeviceConfigInstance]: + """ + Streams DeviceConfigInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeviceConfigInstance]: + """ + Asynchronously streams DeviceConfigInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceConfigInstance]: + """ + Lists DeviceConfigInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceConfigInstance]: + """ + Asynchronously lists DeviceConfigInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeviceConfigPage: + """ + Retrieve a single page of DeviceConfigInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceConfigInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeviceConfigPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeviceConfigPage: + """ + Asynchronously retrieve a single page of DeviceConfigInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceConfigInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeviceConfigPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeviceConfigPage: + """ + Retrieve a specific page of DeviceConfigInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceConfigInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeviceConfigPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeviceConfigPage: + """ + Asynchronously retrieve a specific page of DeviceConfigInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceConfigInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeviceConfigPage(self._version, response, self._solution) + + def get(self, key: str) -> DeviceConfigContext: + """ + Constructs a DeviceConfigContext + + :param key: The config key; up to 100 characters. + """ + return DeviceConfigContext( + self._version, device_sid=self._solution["device_sid"], key=key + ) + + def __call__(self, key: str) -> DeviceConfigContext: + """ + Constructs a DeviceConfigContext + + :param key: The config key; up to 100 characters. + """ + return DeviceConfigContext( + self._version, device_sid=self._solution["device_sid"], key=key + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_secret.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_secret.py new file mode 100644 index 0000000..0ab9b1c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/microvisor/v1/device/device_secret.py @@ -0,0 +1,590 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Microvisor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeviceSecretInstance(InstanceResource): + """ + :ivar device_sid: A 34-character string that uniquely identifies the parent Device. + :ivar key: The secret key; up to 100 characters. + :ivar date_rotated: + :ivar url: The absolute URL of the Secret. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + device_sid: str, + key: Optional[str] = None, + ): + super().__init__(version) + + self.device_sid: Optional[str] = payload.get("device_sid") + self.key: Optional[str] = payload.get("key") + self.date_rotated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_rotated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "device_sid": device_sid, + "key": key or self.key, + } + self._context: Optional[DeviceSecretContext] = None + + @property + def _proxy(self) -> "DeviceSecretContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeviceSecretContext for this DeviceSecretInstance + """ + if self._context is None: + self._context = DeviceSecretContext( + self._version, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DeviceSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DeviceSecretInstance": + """ + Fetch the DeviceSecretInstance + + + :returns: The fetched DeviceSecretInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeviceSecretInstance": + """ + Asynchronous coroutine to fetch the DeviceSecretInstance + + + :returns: The fetched DeviceSecretInstance + """ + return await self._proxy.fetch_async() + + def update(self, value: str) -> "DeviceSecretInstance": + """ + Update the DeviceSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated DeviceSecretInstance + """ + return self._proxy.update( + value=value, + ) + + async def update_async(self, value: str) -> "DeviceSecretInstance": + """ + Asynchronous coroutine to update the DeviceSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated DeviceSecretInstance + """ + return await self._proxy.update_async( + value=value, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceSecretContext(InstanceContext): + + def __init__(self, version: Version, device_sid: str, key: str): + """ + Initialize the DeviceSecretContext + + :param version: Version that contains the resource + :param device_sid: A 34-character string that uniquely identifies the Device. + :param key: The secret key; up to 100 characters. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "device_sid": device_sid, + "key": key, + } + self._uri = "/Devices/{device_sid}/Secrets/{key}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the DeviceSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceSecretInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DeviceSecretInstance: + """ + Fetch the DeviceSecretInstance + + + :returns: The fetched DeviceSecretInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeviceSecretInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + async def fetch_async(self) -> DeviceSecretInstance: + """ + Asynchronous coroutine to fetch the DeviceSecretInstance + + + :returns: The fetched DeviceSecretInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeviceSecretInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + def update(self, value: str) -> DeviceSecretInstance: + """ + Update the DeviceSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated DeviceSecretInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceSecretInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + async def update_async(self, value: str) -> DeviceSecretInstance: + """ + Asynchronous coroutine to update the DeviceSecretInstance + + :param value: The secret value; up to 4096 characters. + + :returns: The updated DeviceSecretInstance + """ + data = values.of( + { + "Value": value, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceSecretInstance( + self._version, + payload, + device_sid=self._solution["device_sid"], + key=self._solution["key"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceSecretPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeviceSecretInstance: + """ + Build an instance of DeviceSecretInstance + + :param payload: Payload response from the API + """ + return DeviceSecretInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeviceSecretList(ListResource): + + def __init__(self, version: Version, device_sid: str): + """ + Initialize the DeviceSecretList + + :param version: Version that contains the resource + :param device_sid: A 34-character string that uniquely identifies the Device. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "device_sid": device_sid, + } + self._uri = "/Devices/{device_sid}/Secrets".format(**self._solution) + + def create(self, key: str, value: str) -> DeviceSecretInstance: + """ + Create the DeviceSecretInstance + + :param key: The secret key; up to 100 characters. + :param value: The secret value; up to 4096 characters. + + :returns: The created DeviceSecretInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceSecretInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + async def create_async(self, key: str, value: str) -> DeviceSecretInstance: + """ + Asynchronously create the DeviceSecretInstance + + :param key: The secret key; up to 100 characters. + :param value: The secret value; up to 4096 characters. + + :returns: The created DeviceSecretInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceSecretInstance( + self._version, payload, device_sid=self._solution["device_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeviceSecretInstance]: + """ + Streams DeviceSecretInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeviceSecretInstance]: + """ + Asynchronously streams DeviceSecretInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceSecretInstance]: + """ + Lists DeviceSecretInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceSecretInstance]: + """ + Asynchronously lists DeviceSecretInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeviceSecretPage: + """ + Retrieve a single page of DeviceSecretInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceSecretInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeviceSecretPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeviceSecretPage: + """ + Asynchronously retrieve a single page of DeviceSecretInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceSecretInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeviceSecretPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeviceSecretPage: + """ + Retrieve a specific page of DeviceSecretInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceSecretInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeviceSecretPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeviceSecretPage: + """ + Asynchronously retrieve a specific page of DeviceSecretInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceSecretInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeviceSecretPage(self._version, response, self._solution) + + def get(self, key: str) -> DeviceSecretContext: + """ + Constructs a DeviceSecretContext + + :param key: The secret key; up to 100 characters. + """ + return DeviceSecretContext( + self._version, device_sid=self._solution["device_sid"], key=key + ) + + def __call__(self, key: str) -> DeviceSecretContext: + """ + Constructs a DeviceSecretContext + + :param key: The secret key; up to 100 characters. + """ + return DeviceSecretContext( + self._version, device_sid=self._solution["device_sid"], key=key + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/MonitorBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/MonitorBase.py new file mode 100644 index 0000000..4c5c6eb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/MonitorBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.monitor.v1 import V1 + + +class MonitorBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Monitor Domain + + :returns: Domain for Monitor + """ + super().__init__(twilio, "https://monitor.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Monitor + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__init__.py new file mode 100644 index 0000000..a39a5b2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.monitor.MonitorBase import MonitorBase +from twilio.rest.monitor.v1.alert import AlertList +from twilio.rest.monitor.v1.event import EventList + + +class Monitor(MonitorBase): + @property + def alerts(self) -> AlertList: + warn( + "alerts is deprecated. Use v1.alerts instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.alerts + + @property + def events(self) -> EventList: + warn( + "events is deprecated. Use v1.events instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.events diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/MonitorBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/MonitorBase.cpython-310.pyc new file mode 100644 index 0000000..7990bd8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/MonitorBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5910e77 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__init__.py new file mode 100644 index 0000000..e892612 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Monitor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.monitor.v1.alert import AlertList +from twilio.rest.monitor.v1.event import EventList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Monitor + + :param domain: The Twilio.monitor domain + """ + super().__init__(domain, "v1") + self._alerts: Optional[AlertList] = None + self._events: Optional[EventList] = None + + @property + def alerts(self) -> AlertList: + if self._alerts is None: + self._alerts = AlertList(self) + return self._alerts + + @property + def events(self) -> EventList: + if self._events is None: + self._events = EventList(self) + return self._events + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9b9c826 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/alert.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/alert.cpython-310.pyc new file mode 100644 index 0000000..0f4ab30 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/alert.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/event.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/event.cpython-310.pyc new file mode 100644 index 0000000..29987a0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/__pycache__/event.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/alert.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/alert.py new file mode 100644 index 0000000..ef8cf39 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/alert.py @@ -0,0 +1,487 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Monitor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AlertInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Alert resource. + :ivar alert_text: The text of the alert. + :ivar api_version: The API version used when the alert was generated. Can be empty for events that don't have a specific API version. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_generated: The date and time in GMT when the alert was generated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. Due to buffering, this can be different than `date_created`. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar error_code: The error code for the condition that generated the alert. See the [Error Dictionary](https://www.twilio.com/docs/api/errors) for possible causes and solutions to the error. + :ivar log_level: The log level. Can be: `error`, `warning`, `notice`, or `debug`. + :ivar more_info: The URL of the page in our [Error Dictionary](https://www.twilio.com/docs/api/errors) with more information about the error condition. + :ivar request_method: The method used by the request that generated the alert. If the alert was generated by a request we made to your server, this is the method we used. If the alert was generated by a request from your application to our API, this is the method your application used. + :ivar request_url: The URL of the request that generated the alert. If the alert was generated by a request we made to your server, this is the URL on your server that generated the alert. If the alert was generated by a request from your application to our API, this is the URL of the resource requested. + :ivar request_variables: The variables passed in the request that generated the alert. This value is only returned when a single Alert resource is fetched. + :ivar resource_sid: The SID of the resource for which the alert was generated. For instance, if your server failed to respond to an HTTP request during the flow of a particular call, this value would be the SID of the server. This value is empty if the alert was not generated for a particular resource. + :ivar response_body: The response body of the request that generated the alert. This value is only returned when a single Alert resource is fetched. + :ivar response_headers: The response headers of the request that generated the alert. This value is only returned when a single Alert resource is fetched. + :ivar sid: The unique string that we created to identify the Alert resource. + :ivar url: The absolute URL of the Alert resource. + :ivar request_headers: The request headers of the request that generated the alert. This value is only returned when a single Alert resource is fetched. + :ivar service_sid: The SID of the service or resource that generated the alert. Can be `null`. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.alert_text: Optional[str] = payload.get("alert_text") + self.api_version: Optional[str] = payload.get("api_version") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_generated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_generated") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.error_code: Optional[str] = payload.get("error_code") + self.log_level: Optional[str] = payload.get("log_level") + self.more_info: Optional[str] = payload.get("more_info") + self.request_method: Optional[str] = payload.get("request_method") + self.request_url: Optional[str] = payload.get("request_url") + self.request_variables: Optional[str] = payload.get("request_variables") + self.resource_sid: Optional[str] = payload.get("resource_sid") + self.response_body: Optional[str] = payload.get("response_body") + self.response_headers: Optional[str] = payload.get("response_headers") + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.request_headers: Optional[str] = payload.get("request_headers") + self.service_sid: Optional[str] = payload.get("service_sid") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AlertContext] = None + + @property + def _proxy(self) -> "AlertContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AlertContext for this AlertInstance + """ + if self._context is None: + self._context = AlertContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AlertInstance": + """ + Fetch the AlertInstance + + + :returns: The fetched AlertInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AlertInstance": + """ + Asynchronous coroutine to fetch the AlertInstance + + + :returns: The fetched AlertInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AlertContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AlertContext + + :param version: Version that contains the resource + :param sid: The SID of the Alert resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Alerts/{sid}".format(**self._solution) + + def fetch(self) -> AlertInstance: + """ + Fetch the AlertInstance + + + :returns: The fetched AlertInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AlertInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AlertInstance: + """ + Asynchronous coroutine to fetch the AlertInstance + + + :returns: The fetched AlertInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AlertInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AlertPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AlertInstance: + """ + Build an instance of AlertInstance + + :param payload: Payload response from the API + """ + return AlertInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AlertList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AlertList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Alerts" + + def stream( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AlertInstance]: + """ + Streams AlertInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param datetime start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param datetime end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + log_level=log_level, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AlertInstance]: + """ + Asynchronously streams AlertInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param datetime start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param datetime end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + log_level=log_level, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AlertInstance]: + """ + Lists AlertInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param datetime start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param datetime end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + log_level=log_level, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AlertInstance]: + """ + Asynchronously lists AlertInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param datetime start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param datetime end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + log_level=log_level, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AlertPage: + """ + Retrieve a single page of AlertInstance records from the API. + Request is executed immediately + + :param log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AlertInstance + """ + data = values.of( + { + "LogLevel": log_level, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AlertPage(self._version, response) + + async def page_async( + self, + log_level: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AlertPage: + """ + Asynchronously retrieve a single page of AlertInstance records from the API. + Request is executed immediately + + :param log_level: Only show alerts for this log-level. Can be: `error`, `warning`, `notice`, or `debug`. + :param start_date: Only include alerts that occurred on or after this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param end_date: Only include alerts that occurred on or before this date and time. Specify the date and time in GMT and format as `YYYY-MM-DD` or `YYYY-MM-DDThh:mm:ssZ`. Queries for alerts older than 30 days are not supported. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AlertInstance + """ + data = values.of( + { + "LogLevel": log_level, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AlertPage(self._version, response) + + def get_page(self, target_url: str) -> AlertPage: + """ + Retrieve a specific page of AlertInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AlertInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AlertPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AlertPage: + """ + Asynchronously retrieve a specific page of AlertInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AlertInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AlertPage(self._version, response) + + def get(self, sid: str) -> AlertContext: + """ + Constructs a AlertContext + + :param sid: The SID of the Alert resource to fetch. + """ + return AlertContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AlertContext: + """ + Constructs a AlertContext + + :param sid: The SID of the Alert resource to fetch. + """ + return AlertContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/event.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/event.py new file mode 100644 index 0000000..6b67a9c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/monitor/v1/event.py @@ -0,0 +1,527 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Monitor + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EventInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Event resource. + :ivar actor_sid: The SID of the actor that caused the event, if available. Can be `null`. + :ivar actor_type: The type of actor that caused the event. Can be: `user` for a change made by a logged-in user in the Twilio Console, `account` for an event caused by an API request by an authenticating Account, `twilio-admin` for an event caused by a Twilio employee, and so on. + :ivar description: A description of the event. Can be `null`. + :ivar event_data: An object with additional data about the event. The contents depend on `event_type`. For example, event-types of the form `RESOURCE.updated`, this value contains a `resource_properties` dictionary that describes the previous and updated properties of the resource. + :ivar event_date: The date and time in GMT when the event was recorded specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar event_type: The event's type. Event-types are typically in the form: `RESOURCE_TYPE.ACTION`, where `RESOURCE_TYPE` is the type of resource that was affected and `ACTION` is what happened to it. For example, `phone-number.created`. For a full list of all event-types, see the [Monitor Event Types](https://www.twilio.com/docs/usage/monitor-events#event-types). + :ivar resource_sid: The SID of the resource that was affected. + :ivar resource_type: The type of resource that was affected. For a full list of all resource-types, see the [Monitor Event Types](https://www.twilio.com/docs/usage/monitor-events#event-types). + :ivar sid: The unique string that we created to identify the Event resource. + :ivar source: The originating system or interface that caused the event. Can be: `web` for events caused by user action in the Twilio Console, `api` for events caused by a request to our API, or `twilio` for events caused by an automated or internal Twilio system. + :ivar source_ip_address: The IP address of the source, if the source is outside the Twilio cloud. This value is `null` for events with `source` of `twilio` + :ivar url: The absolute URL of the resource that was affected. Can be `null`. + :ivar links: The absolute URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.actor_sid: Optional[str] = payload.get("actor_sid") + self.actor_type: Optional[str] = payload.get("actor_type") + self.description: Optional[str] = payload.get("description") + self.event_data: Optional[Dict[str, object]] = payload.get("event_data") + self.event_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("event_date") + ) + self.event_type: Optional[str] = payload.get("event_type") + self.resource_sid: Optional[str] = payload.get("resource_sid") + self.resource_type: Optional[str] = payload.get("resource_type") + self.sid: Optional[str] = payload.get("sid") + self.source: Optional[str] = payload.get("source") + self.source_ip_address: Optional[str] = payload.get("source_ip_address") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EventContext] = None + + @property + def _proxy(self) -> "EventContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EventContext for this EventInstance + """ + if self._context is None: + self._context = EventContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EventInstance": + """ + Fetch the EventInstance + + + :returns: The fetched EventInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EventInstance": + """ + Asynchronous coroutine to fetch the EventInstance + + + :returns: The fetched EventInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EventContext + + :param version: Version that contains the resource + :param sid: The SID of the Event resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Events/{sid}".format(**self._solution) + + def fetch(self) -> EventInstance: + """ + Fetch the EventInstance + + + :returns: The fetched EventInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EventInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EventInstance: + """ + Asynchronous coroutine to fetch the EventInstance + + + :returns: The fetched EventInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EventInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EventInstance: + """ + Build an instance of EventInstance + + :param payload: Payload response from the API + """ + return EventInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EventList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EventList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Events" + + def stream( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EventInstance]: + """ + Streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param str event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param str resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param str source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param datetime start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param datetime end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + actor_sid=actor_sid, + event_type=event_type, + resource_sid=resource_sid, + source_ip_address=source_ip_address, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EventInstance]: + """ + Asynchronously streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param str event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param str resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param str source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param datetime start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param datetime end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + actor_sid=actor_sid, + event_type=event_type, + resource_sid=resource_sid, + source_ip_address=source_ip_address, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param str event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param str resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param str source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param datetime start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param datetime end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + actor_sid=actor_sid, + event_type=event_type, + resource_sid=resource_sid, + source_ip_address=source_ip_address, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Asynchronously lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param str event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param str resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param str source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param datetime start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param datetime end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + actor_sid=actor_sid, + event_type=event_type, + resource_sid=resource_sid, + source_ip_address=source_ip_address, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "ActorSid": actor_sid, + "EventType": event_type, + "ResourceSid": resource_sid, + "SourceIpAddress": source_ip_address, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EventPage(self._version, response) + + async def page_async( + self, + actor_sid: Union[str, object] = values.unset, + event_type: Union[str, object] = values.unset, + resource_sid: Union[str, object] = values.unset, + source_ip_address: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Asynchronously retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param actor_sid: Only include events initiated by this Actor. Useful for auditing actions taken by specific users or API credentials. + :param event_type: Only include events of this [Event Type](https://www.twilio.com/docs/usage/monitor-events#event-types). + :param resource_sid: Only include events that refer to this resource. Useful for discovering the history of a specific resource. + :param source_ip_address: Only include events that originated from this IP address. Useful for tracking suspicious activity originating from the API or the Twilio Console. + :param start_date: Only include events that occurred on or after this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include events that occurred on or before this date. Specify the date in GMT and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "ActorSid": actor_sid, + "EventType": event_type, + "ResourceSid": resource_sid, + "SourceIpAddress": source_ip_address, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EventPage(self._version, response) + + def get_page(self, target_url: str) -> EventPage: + """ + Retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EventPage(self._version, response) + + async def get_page_async(self, target_url: str) -> EventPage: + """ + Asynchronously retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EventPage(self._version, response) + + def get(self, sid: str) -> EventContext: + """ + Constructs a EventContext + + :param sid: The SID of the Event resource to fetch. + """ + return EventContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EventContext: + """ + Constructs a EventContext + + :param sid: The SID of the Event resource to fetch. + """ + return EventContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/NotifyBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/NotifyBase.py new file mode 100644 index 0000000..2772ed0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/NotifyBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.notify.v1 import V1 + + +class NotifyBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Notify Domain + + :returns: Domain for Notify + """ + super().__init__(twilio, "https://notify.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Notify + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__init__.py new file mode 100644 index 0000000..61f8faf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.notify.NotifyBase import NotifyBase +from twilio.rest.notify.v1.credential import CredentialList +from twilio.rest.notify.v1.service import ServiceList + + +class Notify(NotifyBase): + @property + def credentials(self) -> CredentialList: + warn( + "credentials is deprecated. Use v1.credentials instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.credentials + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/NotifyBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/NotifyBase.cpython-310.pyc new file mode 100644 index 0000000..bf77eef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/NotifyBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..41bddec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__init__.py new file mode 100644 index 0000000..90cab95 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Notify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.notify.v1.credential import CredentialList +from twilio.rest.notify.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Notify + + :param domain: The Twilio.notify domain + """ + super().__init__(domain, "v1") + self._credentials: Optional[CredentialList] = None + self._services: Optional[ServiceList] = None + + @property + def credentials(self) -> CredentialList: + if self._credentials is None: + self._credentials = CredentialList(self) + return self._credentials + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..eda7ef8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/credential.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/credential.cpython-310.pyc new file mode 100644 index 0000000..869fba5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/__pycache__/credential.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/credential.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/credential.py new file mode 100644 index 0000000..5cb8636 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/credential.py @@ -0,0 +1,681 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Notify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialInstance(InstanceResource): + + class PushService(object): + GCM = "gcm" + APN = "apn" + FCM = "fcm" + + """ + :ivar sid: The unique string that we created to identify the Credential resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Credential resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the Credential resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["CredentialInstance.PushService"] = payload.get("type") + self.sandbox: Optional[str] = payload.get("sandbox") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CredentialContext] = None + + @property + def _proxy(self) -> "CredentialContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialContext for this CredentialInstance + """ + if self._context is None: + self._context = CredentialContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialInstance": + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialInstance": + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> "CredentialInstance": + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + certificate=certificate, + private_key=private_key, + sandbox=sandbox, + api_key=api_key, + secret=secret, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CredentialContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Credentials/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialInstance: + """ + Fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialInstance: + """ + Asynchronous coroutine to fetch the CredentialInstance + + + :returns: The fetched CredentialInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronous coroutine to update the CredentialInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The updated CredentialInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialInstance: + """ + Build an instance of CredentialInstance + + :param payload: Payload response from the API + """ + return CredentialInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CredentialList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Credentials" + + def create( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + async def create_async( + self, + type: "CredentialInstance.PushService", + friendly_name: Union[str, object] = values.unset, + certificate: Union[str, object] = values.unset, + private_key: Union[str, object] = values.unset, + sandbox: Union[bool, object] = values.unset, + api_key: Union[str, object] = values.unset, + secret: Union[str, object] = values.unset, + ) -> CredentialInstance: + """ + Asynchronously create the CredentialInstance + + :param type: + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param certificate: [APN only] The URL-encoded representation of the certificate. Strip everything outside of the headers, e.g. `-----BEGIN CERTIFICATE-----MIIFnTCCBIWgAwIBAgIIAjy9H849+E8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV.....A==-----END CERTIFICATE-----` + :param private_key: [APN only] The URL-encoded representation of the private key. Strip everything outside of the headers, e.g. `-----BEGIN RSA PRIVATE KEY-----MIIEpQIBAAKCAQEAuyf/lNrH9ck8DmNyo3fGgvCI1l9s+cmBY3WIz+cUDqmxiieR\\\\n.-----END RSA PRIVATE KEY-----` + :param sandbox: [APN only] Whether to send the credential to sandbox APNs. Can be `true` to send to sandbox APNs or `false` to send to production. + :param api_key: [GCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + :param secret: [FCM only] The `Server key` of your project from Firebase console under Settings / Cloud messaging. + + :returns: The created CredentialInstance + """ + + data = values.of( + { + "Type": type, + "FriendlyName": friendly_name, + "Certificate": certificate, + "PrivateKey": private_key, + "Sandbox": serialize.boolean_to_string(sandbox), + "ApiKey": api_key, + "Secret": secret, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialInstance]: + """ + Streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialInstance]: + """ + Asynchronously streams CredentialInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialInstance]: + """ + Asynchronously lists CredentialInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialPage: + """ + Asynchronously retrieve a single page of CredentialInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialPage(self._version, response) + + def get_page(self, target_url: str) -> CredentialPage: + """ + Retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CredentialPage: + """ + Asynchronously retrieve a specific page of CredentialInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialPage(self._version, response) + + def get(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CredentialContext: + """ + Constructs a CredentialContext + + :param sid: The Twilio-provided string that uniquely identifies the Credential resource to update. + """ + return CredentialContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__init__.py new file mode 100644 index 0000000..0cd9adb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__init__.py @@ -0,0 +1,918 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Notify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.notify.v1.service.binding import BindingList +from twilio.rest.notify.v1.service.notification import NotificationList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :ivar gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :ivar fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. In order to send SMS notifications this parameter has to be set. + :ivar facebook_messenger_page_id: Deprecated. + :ivar default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :ivar default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :ivar default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :ivar log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :ivar url: The absolute URL of the Service resource. + :ivar links: The URLs of the Binding, Notification, Segment, and User resources related to the service. + :ivar alexa_skill_id: Deprecated. + :ivar default_alexa_notification_protocol_version: Deprecated. + :ivar delivery_callback_url: URL to send delivery status callback. + :ivar delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.apn_credential_sid: Optional[str] = payload.get("apn_credential_sid") + self.gcm_credential_sid: Optional[str] = payload.get("gcm_credential_sid") + self.fcm_credential_sid: Optional[str] = payload.get("fcm_credential_sid") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.facebook_messenger_page_id: Optional[str] = payload.get( + "facebook_messenger_page_id" + ) + self.default_apn_notification_protocol_version: Optional[str] = payload.get( + "default_apn_notification_protocol_version" + ) + self.default_gcm_notification_protocol_version: Optional[str] = payload.get( + "default_gcm_notification_protocol_version" + ) + self.default_fcm_notification_protocol_version: Optional[str] = payload.get( + "default_fcm_notification_protocol_version" + ) + self.log_enabled: Optional[bool] = payload.get("log_enabled") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.alexa_skill_id: Optional[str] = payload.get("alexa_skill_id") + self.default_alexa_notification_protocol_version: Optional[str] = payload.get( + "default_alexa_notification_protocol_version" + ) + self.delivery_callback_url: Optional[str] = payload.get("delivery_callback_url") + self.delivery_callback_enabled: Optional[bool] = payload.get( + "delivery_callback_enabled" + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + apn_credential_sid=apn_credential_sid, + gcm_credential_sid=gcm_credential_sid, + messaging_service_sid=messaging_service_sid, + facebook_messenger_page_id=facebook_messenger_page_id, + default_apn_notification_protocol_version=default_apn_notification_protocol_version, + default_gcm_notification_protocol_version=default_gcm_notification_protocol_version, + fcm_credential_sid=fcm_credential_sid, + default_fcm_notification_protocol_version=default_fcm_notification_protocol_version, + log_enabled=log_enabled, + alexa_skill_id=alexa_skill_id, + default_alexa_notification_protocol_version=default_alexa_notification_protocol_version, + delivery_callback_url=delivery_callback_url, + delivery_callback_enabled=delivery_callback_enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + apn_credential_sid=apn_credential_sid, + gcm_credential_sid=gcm_credential_sid, + messaging_service_sid=messaging_service_sid, + facebook_messenger_page_id=facebook_messenger_page_id, + default_apn_notification_protocol_version=default_apn_notification_protocol_version, + default_gcm_notification_protocol_version=default_gcm_notification_protocol_version, + fcm_credential_sid=fcm_credential_sid, + default_fcm_notification_protocol_version=default_fcm_notification_protocol_version, + log_enabled=log_enabled, + alexa_skill_id=alexa_skill_id, + default_alexa_notification_protocol_version=default_alexa_notification_protocol_version, + delivery_callback_url=delivery_callback_url, + delivery_callback_enabled=delivery_callback_enabled, + ) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + return self._proxy.bindings + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + return self._proxy.notifications + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._bindings: Optional[BindingList] = None + self._notifications: Optional[NotificationList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApnCredentialSid": apn_credential_sid, + "GcmCredentialSid": gcm_credential_sid, + "MessagingServiceSid": messaging_service_sid, + "FacebookMessengerPageId": facebook_messenger_page_id, + "DefaultApnNotificationProtocolVersion": default_apn_notification_protocol_version, + "DefaultGcmNotificationProtocolVersion": default_gcm_notification_protocol_version, + "FcmCredentialSid": fcm_credential_sid, + "DefaultFcmNotificationProtocolVersion": default_fcm_notification_protocol_version, + "LogEnabled": serialize.boolean_to_string(log_enabled), + "AlexaSkillId": alexa_skill_id, + "DefaultAlexaNotificationProtocolVersion": default_alexa_notification_protocol_version, + "DeliveryCallbackUrl": delivery_callback_url, + "DeliveryCallbackEnabled": serialize.boolean_to_string( + delivery_callback_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ApnCredentialSid": apn_credential_sid, + "GcmCredentialSid": gcm_credential_sid, + "MessagingServiceSid": messaging_service_sid, + "FacebookMessengerPageId": facebook_messenger_page_id, + "DefaultApnNotificationProtocolVersion": default_apn_notification_protocol_version, + "DefaultGcmNotificationProtocolVersion": default_gcm_notification_protocol_version, + "FcmCredentialSid": fcm_credential_sid, + "DefaultFcmNotificationProtocolVersion": default_fcm_notification_protocol_version, + "LogEnabled": serialize.boolean_to_string(log_enabled), + "AlexaSkillId": alexa_skill_id, + "DefaultAlexaNotificationProtocolVersion": default_alexa_notification_protocol_version, + "DeliveryCallbackUrl": delivery_callback_url, + "DeliveryCallbackEnabled": serialize.boolean_to_string( + delivery_callback_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def bindings(self) -> BindingList: + """ + Access the bindings + """ + if self._bindings is None: + self._bindings = BindingList( + self._version, + self._solution["sid"], + ) + return self._bindings + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + if self._notifications is None: + self._notifications = NotificationList( + self._version, + self._solution["sid"], + ) + return self._notifications + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ApnCredentialSid": apn_credential_sid, + "GcmCredentialSid": gcm_credential_sid, + "MessagingServiceSid": messaging_service_sid, + "FacebookMessengerPageId": facebook_messenger_page_id, + "DefaultApnNotificationProtocolVersion": default_apn_notification_protocol_version, + "DefaultGcmNotificationProtocolVersion": default_gcm_notification_protocol_version, + "FcmCredentialSid": fcm_credential_sid, + "DefaultFcmNotificationProtocolVersion": default_fcm_notification_protocol_version, + "LogEnabled": serialize.boolean_to_string(log_enabled), + "AlexaSkillId": alexa_skill_id, + "DefaultAlexaNotificationProtocolVersion": default_alexa_notification_protocol_version, + "DeliveryCallbackUrl": delivery_callback_url, + "DeliveryCallbackEnabled": serialize.boolean_to_string( + delivery_callback_enabled + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + apn_credential_sid: Union[str, object] = values.unset, + gcm_credential_sid: Union[str, object] = values.unset, + messaging_service_sid: Union[str, object] = values.unset, + facebook_messenger_page_id: Union[str, object] = values.unset, + default_apn_notification_protocol_version: Union[str, object] = values.unset, + default_gcm_notification_protocol_version: Union[str, object] = values.unset, + fcm_credential_sid: Union[str, object] = values.unset, + default_fcm_notification_protocol_version: Union[str, object] = values.unset, + log_enabled: Union[bool, object] = values.unset, + alexa_skill_id: Union[str, object] = values.unset, + default_alexa_notification_protocol_version: Union[str, object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + delivery_callback_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param apn_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for APN Bindings. + :param gcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for GCM Bindings. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/quickstart#messaging-services) to use for SMS Bindings. This parameter must be set in order to send SMS notifications. + :param facebook_messenger_page_id: Deprecated. + :param default_apn_notification_protocol_version: The protocol version to use for sending APNS notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param default_gcm_notification_protocol_version: The protocol version to use for sending GCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param fcm_credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) to use for FCM Bindings. + :param default_fcm_notification_protocol_version: The protocol version to use for sending FCM notifications. Can be overridden on a Binding by Binding basis when creating a [Binding](https://www.twilio.com/docs/notify/api/binding-resource) resource. + :param log_enabled: Whether to log notifications. Can be: `true` or `false` and the default is `true`. + :param alexa_skill_id: Deprecated. + :param default_alexa_notification_protocol_version: Deprecated. + :param delivery_callback_url: URL to send delivery status callback. + :param delivery_callback_enabled: Callback configuration that enables delivery callbacks, default false + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ApnCredentialSid": apn_credential_sid, + "GcmCredentialSid": gcm_credential_sid, + "MessagingServiceSid": messaging_service_sid, + "FacebookMessengerPageId": facebook_messenger_page_id, + "DefaultApnNotificationProtocolVersion": default_apn_notification_protocol_version, + "DefaultGcmNotificationProtocolVersion": default_gcm_notification_protocol_version, + "FcmCredentialSid": fcm_credential_sid, + "DefaultFcmNotificationProtocolVersion": default_fcm_notification_protocol_version, + "LogEnabled": serialize.boolean_to_string(log_enabled), + "AlexaSkillId": alexa_skill_id, + "DefaultAlexaNotificationProtocolVersion": default_alexa_notification_protocol_version, + "DeliveryCallbackUrl": delivery_callback_url, + "DeliveryCallbackEnabled": serialize.boolean_to_string( + delivery_callback_enabled + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the Service resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The string that identifies the Service resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the Service resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The string that identifies the Service resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the Service resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param friendly_name: The string that identifies the Service resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2269ccb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/binding.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/binding.cpython-310.pyc new file mode 100644 index 0000000..bda7641 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/binding.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/notification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/notification.cpython-310.pyc new file mode 100644 index 0000000..21e19d0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/__pycache__/notification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/binding.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/binding.py new file mode 100644 index 0000000..a6b1e3f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/binding.py @@ -0,0 +1,659 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Notify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date, datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BindingInstance(InstanceResource): + + class BindingType(object): + APN = "apn" + GCM = "gcm" + SMS = "sms" + FCM = "fcm" + FACEBOOK_MESSENGER = "facebook-messenger" + ALEXA = "alexa" + + """ + :ivar sid: The unique string that we created to identify the Binding resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Binding resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/notify/api/service-resource) the resource is associated with. + :ivar credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) resource to be used to send notifications to this Binding. If present, this overrides the Credential specified in the Service resource. Applicable only to `apn`, `fcm`, and `gcm` type Bindings. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar notification_protocol_version: The protocol version to use to send the notification. This defaults to the value of `default_xxxx_notification_protocol_version` in the [Service](https://www.twilio.com/docs/notify/api/service-resource) for the protocol. The current version is `\"3\"` for `apn`, `fcm`, and `gcm` type Bindings. The parameter is not applicable to `sms` and `facebook-messenger` type Bindings as the data format is fixed. + :ivar endpoint: Deprecated. + :ivar identity: The `identity` value that uniquely identifies the resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/notify/api/service-resource). Up to 20 Bindings can be created for the same Identity in a given Service. + :ivar binding_type: The transport technology to use for the Binding. Can be: `apn`, `fcm`, `gcm`, `sms`, or `facebook-messenger`. + :ivar address: The channel-specific address. For APNS, the device token. For FCM and GCM, the registration token. For SMS, a phone number in E.164 format. For Facebook Messenger, the Messenger ID of the user or a phone number in E.164 format. + :ivar tags: The list of tags associated with this Binding. Tags can be used to select the Bindings to use when sending a notification. Maximum 20 tags are allowed. + :ivar url: The absolute URL of the Binding resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.credential_sid: Optional[str] = payload.get("credential_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.notification_protocol_version: Optional[str] = payload.get( + "notification_protocol_version" + ) + self.endpoint: Optional[str] = payload.get("endpoint") + self.identity: Optional[str] = payload.get("identity") + self.binding_type: Optional[str] = payload.get("binding_type") + self.address: Optional[str] = payload.get("address") + self.tags: Optional[List[str]] = payload.get("tags") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[BindingContext] = None + + @property + def _proxy(self) -> "BindingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BindingContext for this BindingInstance + """ + if self._context is None: + self._context = BindingContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BindingInstance": + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BindingInstance": + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BindingContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/notify/api/service-resource) to fetch the resource from. + :param sid: The Twilio-provided string that uniquely identifies the Binding resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Bindings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BindingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BindingInstance: + """ + Fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BindingInstance: + """ + Asynchronous coroutine to fetch the BindingInstance + + + :returns: The fetched BindingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BindingInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BindingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BindingInstance: + """ + Build an instance of BindingInstance + + :param payload: Payload response from the API + """ + return BindingInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BindingList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the BindingList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/notify/api/service-resource) to read the resource from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Bindings".format(**self._solution) + + def create( + self, + identity: str, + binding_type: "BindingInstance.BindingType", + address: str, + tag: Union[List[str], object] = values.unset, + notification_protocol_version: Union[str, object] = values.unset, + credential_sid: Union[str, object] = values.unset, + endpoint: Union[str, object] = values.unset, + ) -> BindingInstance: + """ + Create the BindingInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/notify/api/service-resource). Up to 20 Bindings can be created for the same Identity in a given Service. + :param binding_type: + :param address: The channel-specific address. For APNS, the device token. For FCM and GCM, the registration token. For SMS, a phone number in E.164 format. For Facebook Messenger, the Messenger ID of the user or a phone number in E.164 format. + :param tag: A tag that can be used to select the Bindings to notify. Repeat this parameter to specify more than one tag, up to a total of 20 tags. + :param notification_protocol_version: The protocol version to use to send the notification. This defaults to the value of `default_xxxx_notification_protocol_version` for the protocol in the [Service](https://www.twilio.com/docs/notify/api/service-resource). The current version is `\\\"3\\\"` for `apn`, `fcm`, and `gcm` type Bindings. The parameter is not applicable to `sms` and `facebook-messenger` type Bindings as the data format is fixed. + :param credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) resource to be used to send notifications to this Binding. If present, this overrides the Credential specified in the Service resource. Applies to only `apn`, `fcm`, and `gcm` type Bindings. + :param endpoint: Deprecated. + + :returns: The created BindingInstance + """ + + data = values.of( + { + "Identity": identity, + "BindingType": binding_type, + "Address": address, + "Tag": serialize.map(tag, lambda e: e), + "NotificationProtocolVersion": notification_protocol_version, + "CredentialSid": credential_sid, + "Endpoint": endpoint, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BindingInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + binding_type: "BindingInstance.BindingType", + address: str, + tag: Union[List[str], object] = values.unset, + notification_protocol_version: Union[str, object] = values.unset, + credential_sid: Union[str, object] = values.unset, + endpoint: Union[str, object] = values.unset, + ) -> BindingInstance: + """ + Asynchronously create the BindingInstance + + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/notify/api/service-resource). Up to 20 Bindings can be created for the same Identity in a given Service. + :param binding_type: + :param address: The channel-specific address. For APNS, the device token. For FCM and GCM, the registration token. For SMS, a phone number in E.164 format. For Facebook Messenger, the Messenger ID of the user or a phone number in E.164 format. + :param tag: A tag that can be used to select the Bindings to notify. Repeat this parameter to specify more than one tag, up to a total of 20 tags. + :param notification_protocol_version: The protocol version to use to send the notification. This defaults to the value of `default_xxxx_notification_protocol_version` for the protocol in the [Service](https://www.twilio.com/docs/notify/api/service-resource). The current version is `\\\"3\\\"` for `apn`, `fcm`, and `gcm` type Bindings. The parameter is not applicable to `sms` and `facebook-messenger` type Bindings as the data format is fixed. + :param credential_sid: The SID of the [Credential](https://www.twilio.com/docs/notify/api/credential-resource) resource to be used to send notifications to this Binding. If present, this overrides the Credential specified in the Service resource. Applies to only `apn`, `fcm`, and `gcm` type Bindings. + :param endpoint: Deprecated. + + :returns: The created BindingInstance + """ + + data = values.of( + { + "Identity": identity, + "BindingType": binding_type, + "Address": address, + "Tag": serialize.map(tag, lambda e: e), + "NotificationProtocolVersion": notification_protocol_version, + "CredentialSid": credential_sid, + "Endpoint": endpoint, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BindingInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BindingInstance]: + """ + Streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param List[str] tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + start_date=start_date, + end_date=end_date, + identity=identity, + tag=tag, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BindingInstance]: + """ + Asynchronously streams BindingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param List[str] tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + start_date=start_date, + end_date=end_date, + identity=identity, + tag=tag, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param List[str] tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + start_date=start_date, + end_date=end_date, + identity=identity, + tag=tag, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BindingInstance]: + """ + Asynchronously lists BindingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param date start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param date end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param List[str] identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param List[str] tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + start_date=start_date, + end_date=end_date, + identity=identity, + tag=tag, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "Identity": serialize.map(identity, lambda e: e), + "Tag": serialize.map(tag, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BindingPage(self._version, response, self._solution) + + async def page_async( + self, + start_date: Union[date, object] = values.unset, + end_date: Union[date, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BindingPage: + """ + Asynchronously retrieve a single page of BindingInstance records from the API. + Request is executed immediately + + :param start_date: Only include usage that has occurred on or after this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param end_date: Only include usage that occurred on or before this date. Specify the date in GMT and format as `YYYY-MM-DD`. + :param identity: The [User](https://www.twilio.com/docs/chat/rest/user-resource)'s `identity` value of the resources to read. + :param tag: Only list Bindings that have all of the specified Tags. The following implicit tags are available: `all`, `apn`, `fcm`, `gcm`, `sms`, `facebook-messenger`. Up to 5 tags are allowed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BindingInstance + """ + data = values.of( + { + "StartDate": serialize.iso8601_date(start_date), + "EndDate": serialize.iso8601_date(end_date), + "Identity": serialize.map(identity, lambda e: e), + "Tag": serialize.map(tag, lambda e: e), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BindingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BindingPage: + """ + Retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BindingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BindingPage: + """ + Asynchronously retrieve a specific page of BindingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BindingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BindingPage(self._version, response, self._solution) + + def get(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: The Twilio-provided string that uniquely identifies the Binding resource to fetch. + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> BindingContext: + """ + Constructs a BindingContext + + :param sid: The Twilio-provided string that uniquely identifies the Binding resource to fetch. + """ + return BindingContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/notification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/notification.py new file mode 100644 index 0000000..e2482be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/notify/v1/service/notification.py @@ -0,0 +1,279 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Notify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NotificationInstance(InstanceResource): + + class Priority(object): + HIGH = "high" + LOW = "low" + + """ + :ivar sid: The unique string that we created to identify the Notification resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Notification resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/notify/api/service-resource) the resource is associated with. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar identities: The list of `identity` values of the Users to notify. We will attempt to deliver notifications only to Bindings with an identity in this list. + :ivar tags: The tags that select the Bindings to notify. Notifications will be attempted only to Bindings that have all of the tags listed in this property. + :ivar segments: The list of Segments to notify. The [Segment](https://www.twilio.com/docs/notify/api/segment-resource) resource is deprecated. Use the `tags` property, instead. + :ivar priority: + :ivar ttl: How long, in seconds, the notification is valid. Can be an integer between 0 and 2,419,200, which is 4 weeks, the default and the maximum supported time to live (TTL). Delivery should be attempted if the device is offline until the TTL elapses. Zero means that the notification delivery is attempted immediately, only once, and is not stored for future delivery. SMS does not support this property. + :ivar title: The notification title. For FCM and GCM, this translates to the `data.twi_title` value. For APNS, this translates to the `aps.alert.title` value. SMS does not support this property. This field is not visible on iOS phones and tablets but appears on Apple Watch and Android devices. + :ivar body: The notification text. For FCM and GCM, translates to `data.twi_body`. For APNS, translates to `aps.alert.body`. For SMS, translates to `body`. SMS requires either this `body` value, or `media_urls` attribute defined in the `sms` parameter of the notification. + :ivar sound: The name of the sound to be played for the notification. For FCM and GCM, this Translates to `data.twi_sound`. For APNS, this translates to `aps.sound`. SMS does not support this property. + :ivar action: The actions to display for the notification. For APNS, translates to the `aps.category` value. For GCM, translates to the `data.twi_action` value. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :ivar data: The custom key-value pairs of the notification's payload. For FCM and GCM, this value translates to `data` in the FCM and GCM payloads. FCM and GCM [reserve certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref) that cannot be used in those channels. For APNS, attributes of `data` are inserted into the APNS payload as custom properties outside of the `aps` dictionary. In all channels, we reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed and are rejected as 400 Bad request with no delivery attempted. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :ivar apn: The APNS-specific payload that overrides corresponding attributes in the generic payload for APNS Bindings. This property maps to the APNS `Payload` item, therefore the `aps` key must be used to change standard attributes. Adds custom key-value pairs to the root of the dictionary. See the [APNS documentation](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html) for more details. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. + :ivar gcm: The GCM-specific payload that overrides corresponding attributes in the generic payload for GCM Bindings. This property maps to the root JSON dictionary. Target parameters `to`, `registration_ids`, and `notification_key` are not allowed. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. + :ivar fcm: The FCM-specific payload that overrides corresponding attributes in the generic payload for FCM Bindings. This property maps to the root JSON dictionary. See the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream) for more details. Target parameters `to`, `registration_ids`, `condition`, and `notification_key` are not allowed in this parameter. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. FCM also [reserves certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref), which cannot be used in that channel. + :ivar sms: The SMS-specific payload that overrides corresponding attributes in the generic payload for SMS Bindings. Each attribute in this value maps to the corresponding `form` parameter of the Twilio [Message](https://www.twilio.com/docs/sms/api/message-resource) resource. These parameters of the Message resource are supported in snake case format: `body`, `media_urls`, `status_callback`, and `max_price`. The `status_callback` parameter overrides the corresponding parameter in the messaging service, if configured. The `media_urls` property expects a JSON array. + :ivar facebook_messenger: Deprecated. + :ivar alexa: Deprecated. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], service_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.identities: Optional[List[str]] = payload.get("identities") + self.tags: Optional[List[str]] = payload.get("tags") + self.segments: Optional[List[str]] = payload.get("segments") + self.priority: Optional["NotificationInstance.Priority"] = payload.get( + "priority" + ) + self.ttl: Optional[int] = deserialize.integer(payload.get("ttl")) + self.title: Optional[str] = payload.get("title") + self.body: Optional[str] = payload.get("body") + self.sound: Optional[str] = payload.get("sound") + self.action: Optional[str] = payload.get("action") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.apn: Optional[Dict[str, object]] = payload.get("apn") + self.gcm: Optional[Dict[str, object]] = payload.get("gcm") + self.fcm: Optional[Dict[str, object]] = payload.get("fcm") + self.sms: Optional[Dict[str, object]] = payload.get("sms") + self.facebook_messenger: Optional[Dict[str, object]] = payload.get( + "facebook_messenger" + ) + self.alexa: Optional[Dict[str, object]] = payload.get("alexa") + + self._solution = { + "service_sid": service_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the NotificationList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/notify/api/service-resource) to create the resource under. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Notifications".format(**self._solution) + + def create( + self, + body: Union[str, object] = values.unset, + priority: Union["NotificationInstance.Priority", object] = values.unset, + ttl: Union[int, object] = values.unset, + title: Union[str, object] = values.unset, + sound: Union[str, object] = values.unset, + action: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + apn: Union[object, object] = values.unset, + gcm: Union[object, object] = values.unset, + sms: Union[object, object] = values.unset, + facebook_messenger: Union[object, object] = values.unset, + fcm: Union[object, object] = values.unset, + segment: Union[List[str], object] = values.unset, + alexa: Union[object, object] = values.unset, + to_binding: Union[List[str], object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + ) -> NotificationInstance: + """ + Create the NotificationInstance + + :param body: The notification text. For FCM and GCM, translates to `data.twi_body`. For APNS, translates to `aps.alert.body`. For SMS, translates to `body`. SMS requires either this `body` value, or `media_urls` attribute defined in the `sms` parameter of the notification. + :param priority: + :param ttl: How long, in seconds, the notification is valid. Can be an integer between 0 and 2,419,200, which is 4 weeks, the default and the maximum supported time to live (TTL). Delivery should be attempted if the device is offline until the TTL elapses. Zero means that the notification delivery is attempted immediately, only once, and is not stored for future delivery. SMS does not support this property. + :param title: The notification title. For FCM and GCM, this translates to the `data.twi_title` value. For APNS, this translates to the `aps.alert.title` value. SMS does not support this property. This field is not visible on iOS phones and tablets but appears on Apple Watch and Android devices. + :param sound: The name of the sound to be played for the notification. For FCM and GCM, this Translates to `data.twi_sound`. For APNS, this translates to `aps.sound`. SMS does not support this property. + :param action: The actions to display for the notification. For APNS, translates to the `aps.category` value. For GCM, translates to the `data.twi_action` value. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :param data: The custom key-value pairs of the notification's payload. For FCM and GCM, this value translates to `data` in the FCM and GCM payloads. FCM and GCM [reserve certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref) that cannot be used in those channels. For APNS, attributes of `data` are inserted into the APNS payload as custom properties outside of the `aps` dictionary. In all channels, we reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed and are rejected as 400 Bad request with no delivery attempted. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :param apn: The APNS-specific payload that overrides corresponding attributes in the generic payload for APNS Bindings. This property maps to the APNS `Payload` item, therefore the `aps` key must be used to change standard attributes. Adds custom key-value pairs to the root of the dictionary. See the [APNS documentation](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html) for more details. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. + :param gcm: The GCM-specific payload that overrides corresponding attributes in the generic payload for GCM Bindings. This property maps to the root JSON dictionary. See the [GCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref) for more details. Target parameters `to`, `registration_ids`, and `notification_key` are not allowed. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. GCM also [reserves certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref). + :param sms: The SMS-specific payload that overrides corresponding attributes in the generic payload for SMS Bindings. Each attribute in this value maps to the corresponding `form` parameter of the Twilio [Message](https://www.twilio.com/docs/sms/quickstart) resource. These parameters of the Message resource are supported in snake case format: `body`, `media_urls`, `status_callback`, and `max_price`. The `status_callback` parameter overrides the corresponding parameter in the messaging service, if configured. The `media_urls` property expects a JSON array. + :param facebook_messenger: Deprecated. + :param fcm: The FCM-specific payload that overrides corresponding attributes in the generic payload for FCM Bindings. This property maps to the root JSON dictionary. See the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream) for more details. Target parameters `to`, `registration_ids`, `condition`, and `notification_key` are not allowed in this parameter. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. FCM also [reserves certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref), which cannot be used in that channel. + :param segment: The Segment resource is deprecated. Use the `tag` parameter, instead. + :param alexa: Deprecated. + :param to_binding: The destination address specified as a JSON string. Multiple `to_binding` parameters can be included but the total size of the request entity should not exceed 1MB. This is typically sufficient for 10,000 phone numbers. + :param delivery_callback_url: URL to send webhooks. + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/notify/api/service-resource). Delivery will be attempted only to Bindings with an Identity in this list. No more than 20 items are allowed in this list. + :param tag: A tag that selects the Bindings to notify. Repeat this parameter to specify more than one tag, up to a total of 5 tags. The implicit tag `all` is available to notify all Bindings in a Service instance. Similarly, the implicit tags `apn`, `fcm`, `gcm`, `sms` and `facebook-messenger` are available to notify all Bindings in a specific channel. + + :returns: The created NotificationInstance + """ + + data = values.of( + { + "Body": body, + "Priority": priority, + "Ttl": ttl, + "Title": title, + "Sound": sound, + "Action": action, + "Data": serialize.object(data), + "Apn": serialize.object(apn), + "Gcm": serialize.object(gcm), + "Sms": serialize.object(sms), + "FacebookMessenger": serialize.object(facebook_messenger), + "Fcm": serialize.object(fcm), + "Segment": serialize.map(segment, lambda e: e), + "Alexa": serialize.object(alexa), + "ToBinding": serialize.map(to_binding, lambda e: e), + "DeliveryCallbackUrl": delivery_callback_url, + "Identity": serialize.map(identity, lambda e: e), + "Tag": serialize.map(tag, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + body: Union[str, object] = values.unset, + priority: Union["NotificationInstance.Priority", object] = values.unset, + ttl: Union[int, object] = values.unset, + title: Union[str, object] = values.unset, + sound: Union[str, object] = values.unset, + action: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + apn: Union[object, object] = values.unset, + gcm: Union[object, object] = values.unset, + sms: Union[object, object] = values.unset, + facebook_messenger: Union[object, object] = values.unset, + fcm: Union[object, object] = values.unset, + segment: Union[List[str], object] = values.unset, + alexa: Union[object, object] = values.unset, + to_binding: Union[List[str], object] = values.unset, + delivery_callback_url: Union[str, object] = values.unset, + identity: Union[List[str], object] = values.unset, + tag: Union[List[str], object] = values.unset, + ) -> NotificationInstance: + """ + Asynchronously create the NotificationInstance + + :param body: The notification text. For FCM and GCM, translates to `data.twi_body`. For APNS, translates to `aps.alert.body`. For SMS, translates to `body`. SMS requires either this `body` value, or `media_urls` attribute defined in the `sms` parameter of the notification. + :param priority: + :param ttl: How long, in seconds, the notification is valid. Can be an integer between 0 and 2,419,200, which is 4 weeks, the default and the maximum supported time to live (TTL). Delivery should be attempted if the device is offline until the TTL elapses. Zero means that the notification delivery is attempted immediately, only once, and is not stored for future delivery. SMS does not support this property. + :param title: The notification title. For FCM and GCM, this translates to the `data.twi_title` value. For APNS, this translates to the `aps.alert.title` value. SMS does not support this property. This field is not visible on iOS phones and tablets but appears on Apple Watch and Android devices. + :param sound: The name of the sound to be played for the notification. For FCM and GCM, this Translates to `data.twi_sound`. For APNS, this translates to `aps.sound`. SMS does not support this property. + :param action: The actions to display for the notification. For APNS, translates to the `aps.category` value. For GCM, translates to the `data.twi_action` value. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :param data: The custom key-value pairs of the notification's payload. For FCM and GCM, this value translates to `data` in the FCM and GCM payloads. FCM and GCM [reserve certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref) that cannot be used in those channels. For APNS, attributes of `data` are inserted into the APNS payload as custom properties outside of the `aps` dictionary. In all channels, we reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed and are rejected as 400 Bad request with no delivery attempted. For SMS, this parameter is not supported and is omitted from deliveries to those channels. + :param apn: The APNS-specific payload that overrides corresponding attributes in the generic payload for APNS Bindings. This property maps to the APNS `Payload` item, therefore the `aps` key must be used to change standard attributes. Adds custom key-value pairs to the root of the dictionary. See the [APNS documentation](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html) for more details. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. + :param gcm: The GCM-specific payload that overrides corresponding attributes in the generic payload for GCM Bindings. This property maps to the root JSON dictionary. See the [GCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref) for more details. Target parameters `to`, `registration_ids`, and `notification_key` are not allowed. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. GCM also [reserves certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref). + :param sms: The SMS-specific payload that overrides corresponding attributes in the generic payload for SMS Bindings. Each attribute in this value maps to the corresponding `form` parameter of the Twilio [Message](https://www.twilio.com/docs/sms/quickstart) resource. These parameters of the Message resource are supported in snake case format: `body`, `media_urls`, `status_callback`, and `max_price`. The `status_callback` parameter overrides the corresponding parameter in the messaging service, if configured. The `media_urls` property expects a JSON array. + :param facebook_messenger: Deprecated. + :param fcm: The FCM-specific payload that overrides corresponding attributes in the generic payload for FCM Bindings. This property maps to the root JSON dictionary. See the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref#downstream) for more details. Target parameters `to`, `registration_ids`, `condition`, and `notification_key` are not allowed in this parameter. We reserve keys that start with `twi_` for future use. Custom keys that start with `twi_` are not allowed. FCM also [reserves certain keys](https://firebase.google.com/docs/cloud-messaging/http-server-ref), which cannot be used in that channel. + :param segment: The Segment resource is deprecated. Use the `tag` parameter, instead. + :param alexa: Deprecated. + :param to_binding: The destination address specified as a JSON string. Multiple `to_binding` parameters can be included but the total size of the request entity should not exceed 1MB. This is typically sufficient for 10,000 phone numbers. + :param delivery_callback_url: URL to send webhooks. + :param identity: The `identity` value that uniquely identifies the new resource's [User](https://www.twilio.com/docs/chat/rest/user-resource) within the [Service](https://www.twilio.com/docs/notify/api/service-resource). Delivery will be attempted only to Bindings with an Identity in this list. No more than 20 items are allowed in this list. + :param tag: A tag that selects the Bindings to notify. Repeat this parameter to specify more than one tag, up to a total of 5 tags. The implicit tag `all` is available to notify all Bindings in a Service instance. Similarly, the implicit tags `apn`, `fcm`, `gcm`, `sms` and `facebook-messenger` are available to notify all Bindings in a specific channel. + + :returns: The created NotificationInstance + """ + + data = values.of( + { + "Body": body, + "Priority": priority, + "Ttl": ttl, + "Title": title, + "Sound": sound, + "Action": action, + "Data": serialize.object(data), + "Apn": serialize.object(apn), + "Gcm": serialize.object(gcm), + "Sms": serialize.object(sms), + "FacebookMessenger": serialize.object(facebook_messenger), + "Fcm": serialize.object(fcm), + "Segment": serialize.map(segment, lambda e: e), + "Alexa": serialize.object(alexa), + "ToBinding": serialize.map(to_binding, lambda e: e), + "DeliveryCallbackUrl": delivery_callback_url, + "Identity": serialize.map(identity, lambda e: e), + "Tag": serialize.map(tag, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/NumbersBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/NumbersBase.py new file mode 100644 index 0000000..7b9e086 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/NumbersBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.numbers.v1 import V1 +from twilio.rest.numbers.v2 import V2 + + +class NumbersBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Numbers Domain + + :returns: Domain for Numbers + """ + super().__init__(twilio, "https://numbers.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Numbers + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Numbers + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__init__.py new file mode 100644 index 0000000..abe9985 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.numbers.NumbersBase import NumbersBase +from twilio.rest.numbers.v2.regulatory_compliance import RegulatoryComplianceList + + +class Numbers(NumbersBase): + @property + def regulatory_compliance(self) -> RegulatoryComplianceList: + warn( + "regulatory_compliance is deprecated. Use v2.regulatory_compliance instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.regulatory_compliance diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/NumbersBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/NumbersBase.cpython-310.pyc new file mode 100644 index 0000000..b86037e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/NumbersBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c55f4bc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__init__.py new file mode 100644 index 0000000..0bacabb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__init__.py @@ -0,0 +1,95 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.numbers.v1.bulk_eligibility import BulkEligibilityList +from twilio.rest.numbers.v1.eligibility import EligibilityList +from twilio.rest.numbers.v1.porting_bulk_portability import PortingBulkPortabilityList +from twilio.rest.numbers.v1.porting_port_in import PortingPortInList +from twilio.rest.numbers.v1.porting_port_in_fetch import PortingPortInFetchList +from twilio.rest.numbers.v1.porting_port_in_phone_number import ( + PortingPortInPhoneNumberList, +) +from twilio.rest.numbers.v1.porting_portability import PortingPortabilityList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Numbers + + :param domain: The Twilio.numbers domain + """ + super().__init__(domain, "v1") + self._bulk_eligibilities: Optional[BulkEligibilityList] = None + self._eligibilities: Optional[EligibilityList] = None + self._porting_bulk_portabilities: Optional[PortingBulkPortabilityList] = None + self._porting_port_ins: Optional[PortingPortInList] = None + self._porting_port_ins_fetch: Optional[PortingPortInFetchList] = None + self._porting_port_in_phone_number: Optional[PortingPortInPhoneNumberList] = ( + None + ) + self._porting_portabilities: Optional[PortingPortabilityList] = None + + @property + def bulk_eligibilities(self) -> BulkEligibilityList: + if self._bulk_eligibilities is None: + self._bulk_eligibilities = BulkEligibilityList(self) + return self._bulk_eligibilities + + @property + def eligibilities(self) -> EligibilityList: + if self._eligibilities is None: + self._eligibilities = EligibilityList(self) + return self._eligibilities + + @property + def porting_bulk_portabilities(self) -> PortingBulkPortabilityList: + if self._porting_bulk_portabilities is None: + self._porting_bulk_portabilities = PortingBulkPortabilityList(self) + return self._porting_bulk_portabilities + + @property + def porting_port_ins(self) -> PortingPortInList: + if self._porting_port_ins is None: + self._porting_port_ins = PortingPortInList(self) + return self._porting_port_ins + + @property + def porting_port_ins_fetch(self) -> PortingPortInFetchList: + if self._porting_port_ins_fetch is None: + self._porting_port_ins_fetch = PortingPortInFetchList(self) + return self._porting_port_ins_fetch + + @property + def porting_port_in_phone_number(self) -> PortingPortInPhoneNumberList: + if self._porting_port_in_phone_number is None: + self._porting_port_in_phone_number = PortingPortInPhoneNumberList(self) + return self._porting_port_in_phone_number + + @property + def porting_portabilities(self) -> PortingPortabilityList: + if self._porting_portabilities is None: + self._porting_portabilities = PortingPortabilityList(self) + return self._porting_portabilities + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..deb9f73 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/bulk_eligibility.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/bulk_eligibility.cpython-310.pyc new file mode 100644 index 0000000..1e5be7c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/bulk_eligibility.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/eligibility.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/eligibility.cpython-310.pyc new file mode 100644 index 0000000..63a2ff9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/eligibility.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_bulk_portability.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_bulk_portability.cpython-310.pyc new file mode 100644 index 0000000..2e389b5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_bulk_portability.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in.cpython-310.pyc new file mode 100644 index 0000000..4f3cd48 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_fetch.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_fetch.cpython-310.pyc new file mode 100644 index 0000000..65b3884 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_fetch.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_phone_number.cpython-310.pyc new file mode 100644 index 0000000..2c45960 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_port_in_phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_portability.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_portability.cpython-310.pyc new file mode 100644 index 0000000..d494320 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/__pycache__/porting_portability.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/bulk_eligibility.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/bulk_eligibility.py new file mode 100644 index 0000000..0e92b5b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/bulk_eligibility.py @@ -0,0 +1,245 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BulkEligibilityInstance(InstanceResource): + """ + :ivar request_id: The SID of the bulk eligibility check that you want to know about. + :ivar url: This is the url of the request that you're trying to reach out to locate the resource. + :ivar results: The result set that contains the eligibility check response for each requested number, each result has at least the following attributes: phone_number: The requested phone number ,hosting_account_sid: The account sid where the phone number will be hosted, country: Phone number’s country, eligibility_status: Indicates the eligibility status of the PN (Eligible/Ineligible), eligibility_sub_status: Indicates the sub status of the eligibility , ineligibility_reason: Reason for number's ineligibility (if applicable), next_step: Suggested next step in the hosting process based on the eligibility status. + :ivar friendly_name: This is the string that you assigned as a friendly name for describing the eligibility check request. + :ivar status: This is the status of the bulk eligibility check request. (Example: COMPLETE, IN_PROGRESS) + :ivar date_created: + :ivar date_completed: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + request_id: Optional[str] = None, + ): + super().__init__(version) + + self.request_id: Optional[str] = payload.get("request_id") + self.url: Optional[str] = payload.get("url") + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional[str] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_completed: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_completed") + ) + + self._solution = { + "request_id": request_id or self.request_id, + } + self._context: Optional[BulkEligibilityContext] = None + + @property + def _proxy(self) -> "BulkEligibilityContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BulkEligibilityContext for this BulkEligibilityInstance + """ + if self._context is None: + self._context = BulkEligibilityContext( + self._version, + request_id=self._solution["request_id"], + ) + return self._context + + def fetch(self) -> "BulkEligibilityInstance": + """ + Fetch the BulkEligibilityInstance + + + :returns: The fetched BulkEligibilityInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BulkEligibilityInstance": + """ + Asynchronous coroutine to fetch the BulkEligibilityInstance + + + :returns: The fetched BulkEligibilityInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BulkEligibilityContext(InstanceContext): + + def __init__(self, version: Version, request_id: str): + """ + Initialize the BulkEligibilityContext + + :param version: Version that contains the resource + :param request_id: The SID of the bulk eligibility check that you want to know about. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "request_id": request_id, + } + self._uri = "/HostedNumber/Eligibility/Bulk/{request_id}".format( + **self._solution + ) + + def fetch(self) -> BulkEligibilityInstance: + """ + Fetch the BulkEligibilityInstance + + + :returns: The fetched BulkEligibilityInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BulkEligibilityInstance( + self._version, + payload, + request_id=self._solution["request_id"], + ) + + async def fetch_async(self) -> BulkEligibilityInstance: + """ + Asynchronous coroutine to fetch the BulkEligibilityInstance + + + :returns: The fetched BulkEligibilityInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BulkEligibilityInstance( + self._version, + payload, + request_id=self._solution["request_id"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BulkEligibilityList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BulkEligibilityList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumber/Eligibility/Bulk" + + def create( + self, body: Union[object, object] = values.unset + ) -> BulkEligibilityInstance: + """ + Create the BulkEligibilityInstance + + :param body: + + :returns: The created BulkEligibilityInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BulkEligibilityInstance(self._version, payload) + + async def create_async( + self, body: Union[object, object] = values.unset + ) -> BulkEligibilityInstance: + """ + Asynchronously create the BulkEligibilityInstance + + :param body: + + :returns: The created BulkEligibilityInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BulkEligibilityInstance(self._version, payload) + + def get(self, request_id: str) -> BulkEligibilityContext: + """ + Constructs a BulkEligibilityContext + + :param request_id: The SID of the bulk eligibility check that you want to know about. + """ + return BulkEligibilityContext(self._version, request_id=request_id) + + def __call__(self, request_id: str) -> BulkEligibilityContext: + """ + Constructs a BulkEligibilityContext + + :param request_id: The SID of the bulk eligibility check that you want to know about. + """ + return BulkEligibilityContext(self._version, request_id=request_id) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/eligibility.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/eligibility.py new file mode 100644 index 0000000..91a2c73 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/eligibility.py @@ -0,0 +1,100 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class EligibilityInstance(InstanceResource): + """ + :ivar results: The result set that contains the eligibility check response for the requested number, each result has at least the following attributes: phone_number: The requested phone number ,hosting_account_sid: The account sid where the phone number will be hosted, date_last_checked: Datetime (ISO 8601) when the PN was last checked for eligibility, country: Phone number’s country, eligibility_status: Indicates the eligibility status of the PN (Eligible/Ineligible), eligibility_sub_status: Indicates the sub status of the eligibility , ineligibility_reason: Reason for number's ineligibility (if applicable), next_step: Suggested next step in the hosting process based on the eligibility status. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class EligibilityList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EligibilityList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumber/Eligibility" + + def create(self, body: Union[object, object] = values.unset) -> EligibilityInstance: + """ + Create the EligibilityInstance + + :param body: + + :returns: The created EligibilityInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return EligibilityInstance(self._version, payload) + + async def create_async( + self, body: Union[object, object] = values.unset + ) -> EligibilityInstance: + """ + Asynchronously create the EligibilityInstance + + :param body: + + :returns: The created EligibilityInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return EligibilityInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_bulk_portability.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_bulk_portability.py new file mode 100644 index 0000000..c3fedcd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_bulk_portability.py @@ -0,0 +1,252 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PortingBulkPortabilityInstance(InstanceResource): + + class Status(object): + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + EXPIRED = "expired" + + """ + :ivar sid: A 34 character string that uniquely identifies this Portability check. + :ivar status: + :ivar datetime_created: The date that the Portability check was created, given in ISO 8601 format. + :ivar phone_numbers: Contains a list with all the information of the requested phone numbers. Each phone number contains the following properties: `phone_number`: The phone number which portability is to be checked. `portable`: Boolean flag specifying if phone number is portable or not. `not_portable_reason`: Reason why the phone number cannot be ported into Twilio, `null` otherwise. `not_portable_reason_code`: The Portability Reason Code for the phone number if it cannot be ported in Twilio, `null` otherwise. `pin_and_account_number_required`: Boolean flag specifying if PIN and account number is required for the phone number. `number_type`: The type of the requested phone number. `country` Country the phone number belongs to. `messaging_carrier` Current messaging carrier of the phone number. `voice_carrier` Current voice carrier of the phone number. + :ivar url: This is the url of the request that you're trying to reach out to locate the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["PortingBulkPortabilityInstance.Status"] = payload.get( + "status" + ) + self.datetime_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("datetime_created") + ) + self.phone_numbers: Optional[List[Dict[str, object]]] = payload.get( + "phone_numbers" + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PortingBulkPortabilityContext] = None + + @property + def _proxy(self) -> "PortingBulkPortabilityContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PortingBulkPortabilityContext for this PortingBulkPortabilityInstance + """ + if self._context is None: + self._context = PortingBulkPortabilityContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "PortingBulkPortabilityInstance": + """ + Fetch the PortingBulkPortabilityInstance + + + :returns: The fetched PortingBulkPortabilityInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PortingBulkPortabilityInstance": + """ + Asynchronous coroutine to fetch the PortingBulkPortabilityInstance + + + :returns: The fetched PortingBulkPortabilityInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingBulkPortabilityContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PortingBulkPortabilityContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies the Portability check. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Porting/Portability/{sid}".format(**self._solution) + + def fetch(self) -> PortingBulkPortabilityInstance: + """ + Fetch the PortingBulkPortabilityInstance + + + :returns: The fetched PortingBulkPortabilityInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PortingBulkPortabilityInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PortingBulkPortabilityInstance: + """ + Asynchronous coroutine to fetch the PortingBulkPortabilityInstance + + + :returns: The fetched PortingBulkPortabilityInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PortingBulkPortabilityInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingBulkPortabilityList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PortingBulkPortabilityList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Porting/Portability" + + def create(self, phone_numbers: List[str]) -> PortingBulkPortabilityInstance: + """ + Create the PortingBulkPortabilityInstance + + :param phone_numbers: The phone numbers which portability is to be checked. This should be a list of strings. Phone numbers are in E.164 format (e.g. +16175551212). . + + :returns: The created PortingBulkPortabilityInstance + """ + + data = values.of( + { + "PhoneNumbers": serialize.map(phone_numbers, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PortingBulkPortabilityInstance(self._version, payload) + + async def create_async( + self, phone_numbers: List[str] + ) -> PortingBulkPortabilityInstance: + """ + Asynchronously create the PortingBulkPortabilityInstance + + :param phone_numbers: The phone numbers which portability is to be checked. This should be a list of strings. Phone numbers are in E.164 format (e.g. +16175551212). . + + :returns: The created PortingBulkPortabilityInstance + """ + + data = values.of( + { + "PhoneNumbers": serialize.map(phone_numbers, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PortingBulkPortabilityInstance(self._version, payload) + + def get(self, sid: str) -> PortingBulkPortabilityContext: + """ + Constructs a PortingBulkPortabilityContext + + :param sid: A 34 character string that uniquely identifies the Portability check. + """ + return PortingBulkPortabilityContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PortingBulkPortabilityContext: + """ + Constructs a PortingBulkPortabilityContext + + :param sid: A 34 character string that uniquely identifies the Portability check. + """ + return PortingBulkPortabilityContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in.py new file mode 100644 index 0000000..862f578 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in.py @@ -0,0 +1,218 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PortingPortInInstance(InstanceResource): + """ + :ivar port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + port_in_request_sid: Optional[str] = None, + ): + super().__init__(version) + + self.port_in_request_sid: Optional[str] = payload.get("port_in_request_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "port_in_request_sid": port_in_request_sid or self.port_in_request_sid, + } + self._context: Optional[PortingPortInContext] = None + + @property + def _proxy(self) -> "PortingPortInContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PortingPortInContext for this PortingPortInInstance + """ + if self._context is None: + self._context = PortingPortInContext( + self._version, + port_in_request_sid=self._solution["port_in_request_sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PortingPortInInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PortingPortInInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortInContext(InstanceContext): + + def __init__(self, version: Version, port_in_request_sid: str): + """ + Initialize the PortingPortInContext + + :param version: Version that contains the resource + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "port_in_request_sid": port_in_request_sid, + } + self._uri = "/Porting/PortIn/{port_in_request_sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the PortingPortInInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PortingPortInInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortInList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PortingPortInList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Porting/PortIn" + + def create( + self, body: Union[object, object] = values.unset + ) -> PortingPortInInstance: + """ + Create the PortingPortInInstance + + :param body: + + :returns: The created PortingPortInInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PortingPortInInstance(self._version, payload) + + async def create_async( + self, body: Union[object, object] = values.unset + ) -> PortingPortInInstance: + """ + Asynchronously create the PortingPortInInstance + + :param body: + + :returns: The created PortingPortInInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return PortingPortInInstance(self._version, payload) + + def get(self, port_in_request_sid: str) -> PortingPortInContext: + """ + Constructs a PortingPortInContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + return PortingPortInContext( + self._version, port_in_request_sid=port_in_request_sid + ) + + def __call__(self, port_in_request_sid: str) -> PortingPortInContext: + """ + Constructs a PortingPortInContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + return PortingPortInContext( + self._version, port_in_request_sid=port_in_request_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_fetch.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_fetch.py new file mode 100644 index 0000000..dc0b79c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_fetch.py @@ -0,0 +1,223 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from typing import Any, Dict, List, Optional +from twilio.base import deserialize +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PortingPortInFetchInstance(InstanceResource): + """ + :ivar port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + :ivar url: The URL of this Port In request + :ivar account_sid: The Account SID that the numbers will be added to after they are ported into Twilio. + :ivar notification_emails: List of emails for getting notifications about the LOA signing process. Allowed Max 10 emails. + :ivar target_port_in_date: Minimum number of days in the future (at least 2 days) needs to be established with the Ops team for validation. + :ivar target_port_in_time_range_start: Minimum hour in the future needs to be established with the Ops team for validation. + :ivar target_port_in_time_range_end: Maximum hour in the future needs to be established with the Ops team for validation. + :ivar port_in_request_status: The status of the port in request. The possible values are: In progress, Completed, Expired, In review, Waiting for Signature, Action Required, and Canceled. + :ivar losing_carrier_information: The information for the losing carrier. + :ivar phone_numbers: The list of phone numbers to Port in. Phone numbers are in E.164 format (e.g. +16175551212). + :ivar documents: The list of documents SID referencing a utility bills + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + port_in_request_sid: Optional[str] = None, + ): + super().__init__(version) + + self.port_in_request_sid: Optional[str] = payload.get("port_in_request_sid") + self.url: Optional[str] = payload.get("url") + self.account_sid: Optional[str] = payload.get("account_sid") + self.notification_emails: Optional[List[str]] = payload.get( + "notification_emails" + ) + self.target_port_in_date: Optional[date] = deserialize.iso8601_date( + payload.get("target_port_in_date") + ) + self.target_port_in_time_range_start: Optional[str] = payload.get( + "target_port_in_time_range_start" + ) + self.target_port_in_time_range_end: Optional[str] = payload.get( + "target_port_in_time_range_end" + ) + self.port_in_request_status: Optional[str] = payload.get( + "port_in_request_status" + ) + self.losing_carrier_information: Optional[Dict[str, object]] = payload.get( + "losing_carrier_information" + ) + self.phone_numbers: Optional[List[Dict[str, object]]] = payload.get( + "phone_numbers" + ) + self.documents: Optional[List[str]] = payload.get("documents") + + self._solution = { + "port_in_request_sid": port_in_request_sid or self.port_in_request_sid, + } + self._context: Optional[PortingPortInFetchContext] = None + + @property + def _proxy(self) -> "PortingPortInFetchContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PortingPortInFetchContext for this PortingPortInFetchInstance + """ + if self._context is None: + self._context = PortingPortInFetchContext( + self._version, + port_in_request_sid=self._solution["port_in_request_sid"], + ) + return self._context + + def fetch(self) -> "PortingPortInFetchInstance": + """ + Fetch the PortingPortInFetchInstance + + + :returns: The fetched PortingPortInFetchInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PortingPortInFetchInstance": + """ + Asynchronous coroutine to fetch the PortingPortInFetchInstance + + + :returns: The fetched PortingPortInFetchInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortInFetchContext(InstanceContext): + + def __init__(self, version: Version, port_in_request_sid: str): + """ + Initialize the PortingPortInFetchContext + + :param version: Version that contains the resource + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "port_in_request_sid": port_in_request_sid, + } + self._uri = "/Porting/PortIn/{port_in_request_sid}".format(**self._solution) + + def fetch(self) -> PortingPortInFetchInstance: + """ + Fetch the PortingPortInFetchInstance + + + :returns: The fetched PortingPortInFetchInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PortingPortInFetchInstance( + self._version, + payload, + port_in_request_sid=self._solution["port_in_request_sid"], + ) + + async def fetch_async(self) -> PortingPortInFetchInstance: + """ + Asynchronous coroutine to fetch the PortingPortInFetchInstance + + + :returns: The fetched PortingPortInFetchInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PortingPortInFetchInstance( + self._version, + payload, + port_in_request_sid=self._solution["port_in_request_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortInFetchList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PortingPortInFetchList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, port_in_request_sid: str) -> PortingPortInFetchContext: + """ + Constructs a PortingPortInFetchContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + return PortingPortInFetchContext( + self._version, port_in_request_sid=port_in_request_sid + ) + + def __call__(self, port_in_request_sid: str) -> PortingPortInFetchContext: + """ + Constructs a PortingPortInFetchContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + """ + return PortingPortInFetchContext( + self._version, port_in_request_sid=port_in_request_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_phone_number.py new file mode 100644 index 0000000..41296c6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_port_in_phone_number.py @@ -0,0 +1,125 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from twilio.base.instance_context import InstanceContext + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PortingPortInPhoneNumberContext(InstanceContext): + + def __init__( + self, version: Version, port_in_request_sid: str, phone_number_sid: str + ): + """ + Initialize the PortingPortInPhoneNumberContext + + :param version: Version that contains the resource + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + :param phone_number_sid: The SID of the Port In request phone number. This is a unique identifier of the phone number. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "port_in_request_sid": port_in_request_sid, + "phone_number_sid": phone_number_sid, + } + self._uri = "/Porting/PortIn/{port_in_request_sid}/PhoneNumber/{phone_number_sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the PortingPortInPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PortingPortInPhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortInPhoneNumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PortingPortInPhoneNumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get( + self, port_in_request_sid: str, phone_number_sid: str + ) -> PortingPortInPhoneNumberContext: + """ + Constructs a PortingPortInPhoneNumberContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + :param phone_number_sid: The SID of the Port In request phone number. This is a unique identifier of the phone number. + """ + return PortingPortInPhoneNumberContext( + self._version, + port_in_request_sid=port_in_request_sid, + phone_number_sid=phone_number_sid, + ) + + def __call__( + self, port_in_request_sid: str, phone_number_sid: str + ) -> PortingPortInPhoneNumberContext: + """ + Constructs a PortingPortInPhoneNumberContext + + :param port_in_request_sid: The SID of the Port In request. This is a unique identifier of the port in request. + :param phone_number_sid: The SID of the Port In request phone number. This is a unique identifier of the phone number. + """ + return PortingPortInPhoneNumberContext( + self._version, + port_in_request_sid=port_in_request_sid, + phone_number_sid=phone_number_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_portability.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_portability.py new file mode 100644 index 0000000..f1cf0cb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v1/porting_portability.py @@ -0,0 +1,243 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PortingPortabilityInstance(InstanceResource): + + class NumberType(object): + LOCAL = "LOCAL" + UNKNOWN = "UNKNOWN" + MOBILE = "MOBILE" + TOLL_FREE = "TOLL-FREE" + + """ + :ivar phone_number: The phone number which portability is to be checked. Phone numbers are in E.164 format (e.g. +16175551212). + :ivar account_sid: The target account sid to which the number will be ported + :ivar portable: Boolean flag specifying if phone number is portable or not. + :ivar pin_and_account_number_required: Boolean flag specifying if PIN and account number is required for the phone number. + :ivar not_portable_reason: Reason why the phone number cannot be ported into Twilio, `null` otherwise. + :ivar not_portable_reason_code: The Portability Reason Code for the phone number if it cannot be ported into Twilio, `null` otherwise. One of `22131`, `22132`, `22130`, `22133`, `22102` or `22135`. + :ivar number_type: + :ivar country: Country the phone number belongs to. + :ivar messaging_carrier: Current messaging carrier of the phone number + :ivar voice_carrier: Current voice carrier of the phone number + :ivar url: This is the url of the request that you're trying to reach out to locate the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + phone_number: Optional[str] = None, + ): + super().__init__(version) + + self.phone_number: Optional[str] = payload.get("phone_number") + self.account_sid: Optional[str] = payload.get("account_sid") + self.portable: Optional[bool] = payload.get("portable") + self.pin_and_account_number_required: Optional[bool] = payload.get( + "pin_and_account_number_required" + ) + self.not_portable_reason: Optional[str] = payload.get("not_portable_reason") + self.not_portable_reason_code: Optional[int] = deserialize.integer( + payload.get("not_portable_reason_code") + ) + self.number_type: Optional["PortingPortabilityInstance.NumberType"] = ( + payload.get("number_type") + ) + self.country: Optional[str] = payload.get("country") + self.messaging_carrier: Optional[str] = payload.get("messaging_carrier") + self.voice_carrier: Optional[str] = payload.get("voice_carrier") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "phone_number": phone_number or self.phone_number, + } + self._context: Optional[PortingPortabilityContext] = None + + @property + def _proxy(self) -> "PortingPortabilityContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PortingPortabilityContext for this PortingPortabilityInstance + """ + if self._context is None: + self._context = PortingPortabilityContext( + self._version, + phone_number=self._solution["phone_number"], + ) + return self._context + + def fetch( + self, target_account_sid: Union[str, object] = values.unset + ) -> "PortingPortabilityInstance": + """ + Fetch the PortingPortabilityInstance + + :param target_account_sid: The SID of the account where the phone number(s) will be ported. + + :returns: The fetched PortingPortabilityInstance + """ + return self._proxy.fetch( + target_account_sid=target_account_sid, + ) + + async def fetch_async( + self, target_account_sid: Union[str, object] = values.unset + ) -> "PortingPortabilityInstance": + """ + Asynchronous coroutine to fetch the PortingPortabilityInstance + + :param target_account_sid: The SID of the account where the phone number(s) will be ported. + + :returns: The fetched PortingPortabilityInstance + """ + return await self._proxy.fetch_async( + target_account_sid=target_account_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortabilityContext(InstanceContext): + + def __init__(self, version: Version, phone_number: str): + """ + Initialize the PortingPortabilityContext + + :param version: Version that contains the resource + :param phone_number: The phone number which portability is to be checked. Phone numbers are in E.164 format (e.g. +16175551212). + """ + super().__init__(version) + + # Path Solution + self._solution = { + "phone_number": phone_number, + } + self._uri = "/Porting/Portability/PhoneNumber/{phone_number}".format( + **self._solution + ) + + def fetch( + self, target_account_sid: Union[str, object] = values.unset + ) -> PortingPortabilityInstance: + """ + Fetch the PortingPortabilityInstance + + :param target_account_sid: The SID of the account where the phone number(s) will be ported. + + :returns: The fetched PortingPortabilityInstance + """ + + data = values.of( + { + "TargetAccountSid": target_account_sid, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return PortingPortabilityInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + async def fetch_async( + self, target_account_sid: Union[str, object] = values.unset + ) -> PortingPortabilityInstance: + """ + Asynchronous coroutine to fetch the PortingPortabilityInstance + + :param target_account_sid: The SID of the account where the phone number(s) will be ported. + + :returns: The fetched PortingPortabilityInstance + """ + + data = values.of( + { + "TargetAccountSid": target_account_sid, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return PortingPortabilityInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PortingPortabilityList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PortingPortabilityList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, phone_number: str) -> PortingPortabilityContext: + """ + Constructs a PortingPortabilityContext + + :param phone_number: The phone number which portability is to be checked. Phone numbers are in E.164 format (e.g. +16175551212). + """ + return PortingPortabilityContext(self._version, phone_number=phone_number) + + def __call__(self, phone_number: str) -> PortingPortabilityContext: + """ + Constructs a PortingPortabilityContext + + :param phone_number: The phone number which portability is to be checked. Phone numbers are in E.164 format (e.g. +16175551212). + """ + return PortingPortabilityContext(self._version, phone_number=phone_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__init__.py new file mode 100644 index 0000000..9b5b501 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__init__.py @@ -0,0 +1,67 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.numbers.v2.authorization_document import AuthorizationDocumentList +from twilio.rest.numbers.v2.bulk_hosted_number_order import BulkHostedNumberOrderList +from twilio.rest.numbers.v2.hosted_number_order import HostedNumberOrderList +from twilio.rest.numbers.v2.regulatory_compliance import RegulatoryComplianceList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Numbers + + :param domain: The Twilio.numbers domain + """ + super().__init__(domain, "v2") + self._authorization_documents: Optional[AuthorizationDocumentList] = None + self._bulk_hosted_number_orders: Optional[BulkHostedNumberOrderList] = None + self._hosted_number_orders: Optional[HostedNumberOrderList] = None + self._regulatory_compliance: Optional[RegulatoryComplianceList] = None + + @property + def authorization_documents(self) -> AuthorizationDocumentList: + if self._authorization_documents is None: + self._authorization_documents = AuthorizationDocumentList(self) + return self._authorization_documents + + @property + def bulk_hosted_number_orders(self) -> BulkHostedNumberOrderList: + if self._bulk_hosted_number_orders is None: + self._bulk_hosted_number_orders = BulkHostedNumberOrderList(self) + return self._bulk_hosted_number_orders + + @property + def hosted_number_orders(self) -> HostedNumberOrderList: + if self._hosted_number_orders is None: + self._hosted_number_orders = HostedNumberOrderList(self) + return self._hosted_number_orders + + @property + def regulatory_compliance(self) -> RegulatoryComplianceList: + if self._regulatory_compliance is None: + self._regulatory_compliance = RegulatoryComplianceList(self) + return self._regulatory_compliance + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..45ca52e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/bulk_hosted_number_order.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/bulk_hosted_number_order.cpython-310.pyc new file mode 100644 index 0000000..b1cf319 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/bulk_hosted_number_order.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/hosted_number_order.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/hosted_number_order.cpython-310.pyc new file mode 100644 index 0000000..bb4c326 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/__pycache__/hosted_number_order.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__init__.py new file mode 100644 index 0000000..7e0a4d9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__init__.py @@ -0,0 +1,607 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.numbers.v2.authorization_document.dependent_hosted_number_order import ( + DependentHostedNumberOrderList, +) + + +class AuthorizationDocumentInstance(InstanceResource): + + class Status(object): + OPENED = "opened" + SIGNING = "signing" + SIGNED = "signed" + CANCELED = "canceled" + FAILED = "failed" + + """ + :ivar sid: A 34 character string that uniquely identifies this AuthorizationDocument. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :ivar status: + :ivar email: Email that this AuthorizationDocument will be sent to for signing. + :ivar cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.status: Optional["AuthorizationDocumentInstance.Status"] = payload.get( + "status" + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AuthorizationDocumentContext] = None + + @property + def _proxy(self) -> "AuthorizationDocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthorizationDocumentContext for this AuthorizationDocumentInstance + """ + if self._context is None: + self._context = AuthorizationDocumentContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AuthorizationDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthorizationDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AuthorizationDocumentInstance": + """ + Fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthorizationDocumentInstance": + """ + Asynchronous coroutine to fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + return await self._proxy.fetch_async() + + @property + def dependent_hosted_number_orders(self) -> DependentHostedNumberOrderList: + """ + Access the dependent_hosted_number_orders + """ + return self._proxy.dependent_hosted_number_orders + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AuthorizationDocumentContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AuthorizationDocumentContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/HostedNumber/AuthorizationDocuments/{sid}".format( + **self._solution + ) + + self._dependent_hosted_number_orders: Optional[ + DependentHostedNumberOrderList + ] = None + + def delete(self) -> bool: + """ + Deletes the AuthorizationDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AuthorizationDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AuthorizationDocumentInstance: + """ + Fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthorizationDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AuthorizationDocumentInstance: + """ + Asynchronous coroutine to fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthorizationDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def dependent_hosted_number_orders(self) -> DependentHostedNumberOrderList: + """ + Access the dependent_hosted_number_orders + """ + if self._dependent_hosted_number_orders is None: + self._dependent_hosted_number_orders = DependentHostedNumberOrderList( + self._version, + self._solution["sid"], + ) + return self._dependent_hosted_number_orders + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AuthorizationDocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AuthorizationDocumentInstance: + """ + Build an instance of AuthorizationDocumentInstance + + :param payload: Payload response from the API + """ + return AuthorizationDocumentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthorizationDocumentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AuthorizationDocumentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumber/AuthorizationDocuments" + + def create( + self, + address_sid: str, + email: str, + contact_phone_number: str, + hosted_number_order_sids: List[str], + contact_title: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Create the AuthorizationDocumentInstance + + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + + :returns: The created AuthorizationDocumentInstance + """ + + data = values.of( + { + "AddressSid": address_sid, + "Email": email, + "ContactPhoneNumber": contact_phone_number, + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "ContactTitle": contact_title, + "CcEmails": serialize.map(cc_emails, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance(self._version, payload) + + async def create_async( + self, + address_sid: str, + email: str, + contact_phone_number: str, + hosted_number_order_sids: List[str], + contact_title: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Asynchronously create the AuthorizationDocumentInstance + + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + + :returns: The created AuthorizationDocumentInstance + """ + + data = values.of( + { + "AddressSid": address_sid, + "Email": email, + "ContactPhoneNumber": contact_phone_number, + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "ContactTitle": contact_title, + "CcEmails": serialize.map(cc_emails, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance(self._version, payload) + + def stream( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthorizationDocumentInstance]: + """ + Streams AuthorizationDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(email=email, status=status, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthorizationDocumentInstance]: + """ + Asynchronously streams AuthorizationDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + email=email, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizationDocumentInstance]: + """ + Lists AuthorizationDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + email=email, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizationDocumentInstance]: + """ + Asynchronously lists AuthorizationDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + email=email, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizationDocumentPage: + """ + Retrieve a single page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizationDocumentInstance + """ + data = values.of( + { + "Email": email, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthorizationDocumentPage(self._version, response) + + async def page_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizationDocumentPage: + """ + Asynchronously retrieve a single page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizationDocumentInstance + """ + data = values.of( + { + "Email": email, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthorizationDocumentPage(self._version, response) + + def get_page(self, target_url: str) -> AuthorizationDocumentPage: + """ + Retrieve a specific page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizationDocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthorizationDocumentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AuthorizationDocumentPage: + """ + Asynchronously retrieve a specific page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizationDocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthorizationDocumentPage(self._version, response) + + def get(self, sid: str) -> AuthorizationDocumentContext: + """ + Constructs a AuthorizationDocumentContext + + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + return AuthorizationDocumentContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AuthorizationDocumentContext: + """ + Constructs a AuthorizationDocumentContext + + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + return AuthorizationDocumentContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f13ba17 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc new file mode 100644 index 0000000..e1838ac Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/dependent_hosted_number_order.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/dependent_hosted_number_order.py new file mode 100644 index 0000000..7fc558f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/authorization_document/dependent_hosted_number_order.py @@ -0,0 +1,429 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DependentHostedNumberOrderInstance(InstanceResource): + + class Status(object): + RECEIVED = "received" + VERIFIED = "verified" + PENDING_LOA = "pending-loa" + CARRIER_PROCESSING = "carrier-processing" + COMPLETED = "completed" + FAILED = "failed" + ACTION_REQUIRED = "action-required" + + """ + :ivar sid: A 34 character string that uniquely identifies this Authorization Document + :ivar bulk_hosting_request_sid: A 34 character string that uniquely identifies the bulk hosting request associated with this HostedNumberOrder. + :ivar next_step: The next step you need to take to complete the hosted number order and request it successfully. + :ivar account_sid: The unique SID identifier of the Account. + :ivar incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :ivar signing_document_sid: A 34 character string that uniquely identifies the LOA document associated with this HostedNumberOrder. + :ivar phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :ivar capabilities: + :ivar friendly_name: A human readable description of this resource, up to 128 characters. + :ivar status: + :ivar failure_reason: A message that explains why a hosted_number_order went to status \"action-required\" + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar email: Email of the owner of this phone number that is being hosted. + :ivar cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :ivar contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :ivar contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], signing_document_sid: str + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.bulk_hosting_request_sid: Optional[str] = payload.get( + "bulk_hosting_request_sid" + ) + self.next_step: Optional[str] = payload.get("next_step") + self.account_sid: Optional[str] = payload.get("account_sid") + self.incoming_phone_number_sid: Optional[str] = payload.get( + "incoming_phone_number_sid" + ) + self.address_sid: Optional[str] = payload.get("address_sid") + self.signing_document_sid: Optional[str] = payload.get("signing_document_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.capabilities: Optional[str] = payload.get("capabilities") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["DependentHostedNumberOrderInstance.Status"] = ( + payload.get("status") + ) + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.contact_title: Optional[str] = payload.get("contact_title") + self.contact_phone_number: Optional[str] = payload.get("contact_phone_number") + + self._solution = { + "signing_document_sid": signing_document_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class DependentHostedNumberOrderPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> DependentHostedNumberOrderInstance: + """ + Build an instance of DependentHostedNumberOrderInstance + + :param payload: Payload response from the API + """ + return DependentHostedNumberOrderInstance( + self._version, + payload, + signing_document_sid=self._solution["signing_document_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DependentHostedNumberOrderList(ListResource): + + def __init__(self, version: Version, signing_document_sid: str): + """ + Initialize the DependentHostedNumberOrderList + + :param version: Version that contains the resource + :param signing_document_sid: A 34 character string that uniquely identifies the LOA document associated with this HostedNumberOrder. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "signing_document_sid": signing_document_sid, + } + self._uri = "/HostedNumber/AuthorizationDocuments/{signing_document_sid}/DependentHostedNumberOrders".format( + **self._solution + ) + + def stream( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DependentHostedNumberOrderInstance]: + """ + Streams DependentHostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DependentHostedNumberOrderInstance]: + """ + Asynchronously streams DependentHostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentHostedNumberOrderInstance]: + """ + Lists DependentHostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentHostedNumberOrderInstance]: + """ + Asynchronously lists DependentHostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentHostedNumberOrderPage: + """ + Retrieve a single page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 128 characters. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentHostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + async def page_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentHostedNumberOrderPage: + """ + Asynchronously retrieve a single page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 128 characters. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentHostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DependentHostedNumberOrderPage: + """ + Retrieve a specific page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentHostedNumberOrderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DependentHostedNumberOrderPage: + """ + Asynchronously retrieve a specific page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentHostedNumberOrderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/bulk_hosted_number_order.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/bulk_hosted_number_order.py new file mode 100644 index 0000000..30a57e2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/bulk_hosted_number_order.py @@ -0,0 +1,287 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BulkHostedNumberOrderInstance(InstanceResource): + + class RequestStatus(object): + QUEUED = "QUEUED" + IN_PROGRESS = "IN_PROGRESS" + PROCESSED = "PROCESSED" + + """ + :ivar bulk_hosting_sid: A 34 character string that uniquely identifies this BulkHostedNumberOrder. + :ivar request_status: + :ivar friendly_name: A 128 character string that is a human-readable text that describes this resource. + :ivar notification_email: Email address used for send notifications about this Bulk hosted number request. + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_completed: The date that this resource was completed, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URL of this BulkHostedNumberOrder resource. + :ivar total_count: The total count of phone numbers in this Bulk hosting request. + :ivar results: Contains a list of all the individual hosting orders and their information, for this Bulk request. Each result object is grouped by its order status. To see a complete list of order status, please check 'https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/hosted-number-order-resource#status-values'. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + bulk_hosting_sid: Optional[str] = None, + ): + super().__init__(version) + + self.bulk_hosting_sid: Optional[str] = payload.get("bulk_hosting_sid") + self.request_status: Optional["BulkHostedNumberOrderInstance.RequestStatus"] = ( + payload.get("request_status") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.notification_email: Optional[str] = payload.get("notification_email") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_completed: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_completed") + ) + self.url: Optional[str] = payload.get("url") + self.total_count: Optional[int] = deserialize.integer( + payload.get("total_count") + ) + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + + self._solution = { + "bulk_hosting_sid": bulk_hosting_sid or self.bulk_hosting_sid, + } + self._context: Optional[BulkHostedNumberOrderContext] = None + + @property + def _proxy(self) -> "BulkHostedNumberOrderContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BulkHostedNumberOrderContext for this BulkHostedNumberOrderInstance + """ + if self._context is None: + self._context = BulkHostedNumberOrderContext( + self._version, + bulk_hosting_sid=self._solution["bulk_hosting_sid"], + ) + return self._context + + def fetch( + self, order_status: Union[str, object] = values.unset + ) -> "BulkHostedNumberOrderInstance": + """ + Fetch the BulkHostedNumberOrderInstance + + :param order_status: Order status can be used for filtering on Hosted Number Order status values. To see a complete list of order statuses, please check 'https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/hosted-number-order-resource#status-values'. + + :returns: The fetched BulkHostedNumberOrderInstance + """ + return self._proxy.fetch( + order_status=order_status, + ) + + async def fetch_async( + self, order_status: Union[str, object] = values.unset + ) -> "BulkHostedNumberOrderInstance": + """ + Asynchronous coroutine to fetch the BulkHostedNumberOrderInstance + + :param order_status: Order status can be used for filtering on Hosted Number Order status values. To see a complete list of order statuses, please check 'https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/hosted-number-order-resource#status-values'. + + :returns: The fetched BulkHostedNumberOrderInstance + """ + return await self._proxy.fetch_async( + order_status=order_status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BulkHostedNumberOrderContext(InstanceContext): + + def __init__(self, version: Version, bulk_hosting_sid: str): + """ + Initialize the BulkHostedNumberOrderContext + + :param version: Version that contains the resource + :param bulk_hosting_sid: A 34 character string that uniquely identifies this BulkHostedNumberOrder. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bulk_hosting_sid": bulk_hosting_sid, + } + self._uri = "/HostedNumber/Orders/Bulk/{bulk_hosting_sid}".format( + **self._solution + ) + + def fetch( + self, order_status: Union[str, object] = values.unset + ) -> BulkHostedNumberOrderInstance: + """ + Fetch the BulkHostedNumberOrderInstance + + :param order_status: Order status can be used for filtering on Hosted Number Order status values. To see a complete list of order statuses, please check 'https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/hosted-number-order-resource#status-values'. + + :returns: The fetched BulkHostedNumberOrderInstance + """ + + data = values.of( + { + "OrderStatus": order_status, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return BulkHostedNumberOrderInstance( + self._version, + payload, + bulk_hosting_sid=self._solution["bulk_hosting_sid"], + ) + + async def fetch_async( + self, order_status: Union[str, object] = values.unset + ) -> BulkHostedNumberOrderInstance: + """ + Asynchronous coroutine to fetch the BulkHostedNumberOrderInstance + + :param order_status: Order status can be used for filtering on Hosted Number Order status values. To see a complete list of order statuses, please check 'https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/hosted-number-order-resource#status-values'. + + :returns: The fetched BulkHostedNumberOrderInstance + """ + + data = values.of( + { + "OrderStatus": order_status, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return BulkHostedNumberOrderInstance( + self._version, + payload, + bulk_hosting_sid=self._solution["bulk_hosting_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BulkHostedNumberOrderList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BulkHostedNumberOrderList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumber/Orders/Bulk" + + def create( + self, body: Union[object, object] = values.unset + ) -> BulkHostedNumberOrderInstance: + """ + Create the BulkHostedNumberOrderInstance + + :param body: + + :returns: The created BulkHostedNumberOrderInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BulkHostedNumberOrderInstance(self._version, payload) + + async def create_async( + self, body: Union[object, object] = values.unset + ) -> BulkHostedNumberOrderInstance: + """ + Asynchronously create the BulkHostedNumberOrderInstance + + :param body: + + :returns: The created BulkHostedNumberOrderInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BulkHostedNumberOrderInstance(self._version, payload) + + def get(self, bulk_hosting_sid: str) -> BulkHostedNumberOrderContext: + """ + Constructs a BulkHostedNumberOrderContext + + :param bulk_hosting_sid: A 34 character string that uniquely identifies this BulkHostedNumberOrder. + """ + return BulkHostedNumberOrderContext( + self._version, bulk_hosting_sid=bulk_hosting_sid + ) + + def __call__(self, bulk_hosting_sid: str) -> BulkHostedNumberOrderContext: + """ + Constructs a BulkHostedNumberOrderContext + + :param bulk_hosting_sid: A 34 character string that uniquely identifies this BulkHostedNumberOrder. + """ + return BulkHostedNumberOrderContext( + self._version, bulk_hosting_sid=bulk_hosting_sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/hosted_number_order.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/hosted_number_order.py new file mode 100644 index 0000000..2063e7a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/hosted_number_order.py @@ -0,0 +1,721 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class HostedNumberOrderInstance(InstanceResource): + + class Status(object): + RECEIVED = "received" + VERIFIED = "verified" + PENDING_LOA = "pending-loa" + CARRIER_PROCESSING = "carrier-processing" + COMPLETED = "completed" + FAILED = "failed" + ACTION_REQUIRED = "action-required" + + """ + :ivar sid: A 34 character string that uniquely identifies this HostedNumberOrder. + :ivar account_sid: A 34 character string that uniquely identifies the account. + :ivar incoming_phone_number_sid: A 34 character string that uniquely identifies the [IncomingPhoneNumber](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) resource that represents the phone number being hosted. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :ivar signing_document_sid: A 34 character string that uniquely identifies the [Authorization Document](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource) the user needs to sign. + :ivar phone_number: Phone number to be hosted. This must be in [E.164](https://en.wikipedia.org/wiki/E.164) format, e.g., +16175551212 + :ivar capabilities: + :ivar friendly_name: A 128 character string that is a human-readable text that describes this resource. + :ivar status: + :ivar failure_reason: A message that explains why a hosted_number_order went to status \"action-required\" + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar email: Email of the owner of this phone number that is being hosted. + :ivar cc_emails: A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :ivar url: The URL of this HostedNumberOrder. + :ivar contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :ivar contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :ivar bulk_hosting_request_sid: A 34 character string that uniquely identifies the bulk hosting request associated with this HostedNumberOrder. + :ivar next_step: The next step you need to take to complete the hosted number order and request it successfully. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.incoming_phone_number_sid: Optional[str] = payload.get( + "incoming_phone_number_sid" + ) + self.address_sid: Optional[str] = payload.get("address_sid") + self.signing_document_sid: Optional[str] = payload.get("signing_document_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.capabilities: Optional[str] = payload.get("capabilities") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["HostedNumberOrderInstance.Status"] = payload.get( + "status" + ) + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.url: Optional[str] = payload.get("url") + self.contact_title: Optional[str] = payload.get("contact_title") + self.contact_phone_number: Optional[str] = payload.get("contact_phone_number") + self.bulk_hosting_request_sid: Optional[str] = payload.get( + "bulk_hosting_request_sid" + ) + self.next_step: Optional[str] = payload.get("next_step") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[HostedNumberOrderContext] = None + + @property + def _proxy(self) -> "HostedNumberOrderContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: HostedNumberOrderContext for this HostedNumberOrderInstance + """ + if self._context is None: + self._context = HostedNumberOrderContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "HostedNumberOrderInstance": + """ + Fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "HostedNumberOrderInstance": + """ + Asynchronous coroutine to fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class HostedNumberOrderContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the HostedNumberOrderContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/HostedNumber/Orders/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> HostedNumberOrderInstance: + """ + Fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return HostedNumberOrderInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> HostedNumberOrderInstance: + """ + Asynchronous coroutine to fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return HostedNumberOrderInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class HostedNumberOrderPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> HostedNumberOrderInstance: + """ + Build an instance of HostedNumberOrderInstance + + :param payload: Payload response from the API + """ + return HostedNumberOrderInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class HostedNumberOrderList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the HostedNumberOrderList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumber/Orders" + + def create( + self, + phone_number: str, + contact_phone_number: str, + address_sid: str, + email: str, + account_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + contact_title: Union[str, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Create the HostedNumberOrderInstance + + :param phone_number: The number to host in [+E.164](https://en.wikipedia.org/wiki/E.164) format + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param address_sid: Optional. A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :param email: Optional. Email of the owner of this phone number that is being hosted. + :param account_sid: This defaults to the AccountSid of the authorization the user is using. This can be provided to specify a subaccount to add the HostedNumberOrder to. + :param friendly_name: A 128 character string that is a human readable text that describes this resource. + :param cc_emails: Optional. A list of emails that the LOA document for this HostedNumberOrder will be carbon copied to. + :param sms_url: The URL that Twilio should request when somebody sends an SMS to the phone number. This will be copied onto the IncomingPhoneNumber resource. + :param sms_method: The HTTP method that should be used to request the SmsUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_url: A URL that Twilio will request if an error occurs requesting or executing the TwiML defined by SmsUrl. This will be copied onto the IncomingPhoneNumber resource. + :param sms_capability: Used to specify that the SMS capability will be hosted on Twilio's platform. + :param sms_fallback_method: The HTTP method that should be used to request the SmsFallbackUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param status_callback_url: Optional. The Status Callback URL attached to the IncomingPhoneNumber resource. + :param status_callback_method: Optional. The Status Callback Method attached to the IncomingPhoneNumber resource. + :param sms_application_sid: Optional. The 34 character sid of the application Twilio should use to handle SMS messages sent to this number. If a `SmsApplicationSid` is present, Twilio will ignore all of the SMS urls above and use those set on the application. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + + :returns: The created HostedNumberOrderInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ContactPhoneNumber": contact_phone_number, + "AddressSid": address_sid, + "Email": email, + "AccountSid": account_sid, + "FriendlyName": friendly_name, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "SmsFallbackMethod": sms_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "SmsApplicationSid": sms_application_sid, + "ContactTitle": contact_title, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance(self._version, payload) + + async def create_async( + self, + phone_number: str, + contact_phone_number: str, + address_sid: str, + email: str, + account_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + contact_title: Union[str, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Asynchronously create the HostedNumberOrderInstance + + :param phone_number: The number to host in [+E.164](https://en.wikipedia.org/wiki/E.164) format + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param address_sid: Optional. A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :param email: Optional. Email of the owner of this phone number that is being hosted. + :param account_sid: This defaults to the AccountSid of the authorization the user is using. This can be provided to specify a subaccount to add the HostedNumberOrder to. + :param friendly_name: A 128 character string that is a human readable text that describes this resource. + :param cc_emails: Optional. A list of emails that the LOA document for this HostedNumberOrder will be carbon copied to. + :param sms_url: The URL that Twilio should request when somebody sends an SMS to the phone number. This will be copied onto the IncomingPhoneNumber resource. + :param sms_method: The HTTP method that should be used to request the SmsUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_url: A URL that Twilio will request if an error occurs requesting or executing the TwiML defined by SmsUrl. This will be copied onto the IncomingPhoneNumber resource. + :param sms_capability: Used to specify that the SMS capability will be hosted on Twilio's platform. + :param sms_fallback_method: The HTTP method that should be used to request the SmsFallbackUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param status_callback_url: Optional. The Status Callback URL attached to the IncomingPhoneNumber resource. + :param status_callback_method: Optional. The Status Callback Method attached to the IncomingPhoneNumber resource. + :param sms_application_sid: Optional. The 34 character sid of the application Twilio should use to handle SMS messages sent to this number. If a `SmsApplicationSid` is present, Twilio will ignore all of the SMS urls above and use those set on the application. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + + :returns: The created HostedNumberOrderInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "ContactPhoneNumber": contact_phone_number, + "AddressSid": address_sid, + "Email": email, + "AccountSid": account_sid, + "FriendlyName": friendly_name, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "SmsFallbackMethod": sms_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "SmsApplicationSid": sms_application_sid, + "ContactTitle": contact_title, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance(self._version, payload) + + def stream( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[HostedNumberOrderInstance]: + """ + Streams HostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param bool sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + sms_capability=sms_capability, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[HostedNumberOrderInstance]: + """ + Asynchronously streams HostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param bool sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + sms_capability=sms_capability, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HostedNumberOrderInstance]: + """ + Lists HostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param bool sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + sms_capability=sms_capability, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HostedNumberOrderInstance]: + """ + Asynchronously lists HostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param bool sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 128 characters. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + sms_capability=sms_capability, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HostedNumberOrderPage: + """ + Retrieve a single page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 128 characters. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return HostedNumberOrderPage(self._version, response) + + async def page_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + sms_capability: Union[bool, object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HostedNumberOrderPage: + """ + Asynchronously retrieve a single page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param sms_capability: Whether the SMS capability will be hosted on our platform. Can be `true` of `false`. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 128 characters. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return HostedNumberOrderPage(self._version, response) + + def get_page(self, target_url: str) -> HostedNumberOrderPage: + """ + Retrieve a specific page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HostedNumberOrderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return HostedNumberOrderPage(self._version, response) + + async def get_page_async(self, target_url: str) -> HostedNumberOrderPage: + """ + Asynchronously retrieve a specific page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HostedNumberOrderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return HostedNumberOrderPage(self._version, response) + + def get(self, sid: str) -> HostedNumberOrderContext: + """ + Constructs a HostedNumberOrderContext + + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + return HostedNumberOrderContext(self._version, sid=sid) + + def __call__(self, sid: str) -> HostedNumberOrderContext: + """ + Constructs a HostedNumberOrderContext + + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + return HostedNumberOrderContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__init__.py new file mode 100644 index 0000000..189117d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__init__.py @@ -0,0 +1,113 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.numbers.v2.regulatory_compliance.bundle import BundleList +from twilio.rest.numbers.v2.regulatory_compliance.end_user import EndUserList +from twilio.rest.numbers.v2.regulatory_compliance.end_user_type import EndUserTypeList +from twilio.rest.numbers.v2.regulatory_compliance.regulation import RegulationList +from twilio.rest.numbers.v2.regulatory_compliance.supporting_document import ( + SupportingDocumentList, +) +from twilio.rest.numbers.v2.regulatory_compliance.supporting_document_type import ( + SupportingDocumentTypeList, +) + + +class RegulatoryComplianceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RegulatoryComplianceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance" + + self._bundles: Optional[BundleList] = None + self._end_users: Optional[EndUserList] = None + self._end_user_types: Optional[EndUserTypeList] = None + self._regulations: Optional[RegulationList] = None + self._supporting_documents: Optional[SupportingDocumentList] = None + self._supporting_document_types: Optional[SupportingDocumentTypeList] = None + + @property + def bundles(self) -> BundleList: + """ + Access the bundles + """ + if self._bundles is None: + self._bundles = BundleList(self._version) + return self._bundles + + @property + def end_users(self) -> EndUserList: + """ + Access the end_users + """ + if self._end_users is None: + self._end_users = EndUserList(self._version) + return self._end_users + + @property + def end_user_types(self) -> EndUserTypeList: + """ + Access the end_user_types + """ + if self._end_user_types is None: + self._end_user_types = EndUserTypeList(self._version) + return self._end_user_types + + @property + def regulations(self) -> RegulationList: + """ + Access the regulations + """ + if self._regulations is None: + self._regulations = RegulationList(self._version) + return self._regulations + + @property + def supporting_documents(self) -> SupportingDocumentList: + """ + Access the supporting_documents + """ + if self._supporting_documents is None: + self._supporting_documents = SupportingDocumentList(self._version) + return self._supporting_documents + + @property + def supporting_document_types(self) -> SupportingDocumentTypeList: + """ + Access the supporting_document_types + """ + if self._supporting_document_types is None: + self._supporting_document_types = SupportingDocumentTypeList(self._version) + return self._supporting_document_types + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0e736b0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user.cpython-310.pyc new file mode 100644 index 0000000..80d8a54 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user_type.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user_type.cpython-310.pyc new file mode 100644 index 0000000..5dc4839 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/end_user_type.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/regulation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/regulation.cpython-310.pyc new file mode 100644 index 0000000..a2a556c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/regulation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document.cpython-310.pyc new file mode 100644 index 0000000..a2ca07d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document_type.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document_type.cpython-310.pyc new file mode 100644 index 0000000..89e9c74 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/__pycache__/supporting_document_type.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__init__.py new file mode 100644 index 0000000..d37caa0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__init__.py @@ -0,0 +1,977 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.numbers.v2.regulatory_compliance.bundle.bundle_copy import ( + BundleCopyList, +) +from twilio.rest.numbers.v2.regulatory_compliance.bundle.evaluation import ( + EvaluationList, +) +from twilio.rest.numbers.v2.regulatory_compliance.bundle.item_assignment import ( + ItemAssignmentList, +) +from twilio.rest.numbers.v2.regulatory_compliance.bundle.replace_items import ( + ReplaceItemsList, +) + + +class BundleInstance(InstanceResource): + + class EndUserType(object): + INDIVIDUAL = "individual" + BUSINESS = "business" + + class SortBy(object): + VALID_UNTIL = "valid-until" + DATE_UPDATED = "date-updated" + + class SortDirection(object): + ASC = "ASC" + DESC = "DESC" + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + IN_REVIEW = "in-review" + TWILIO_REJECTED = "twilio-rejected" + TWILIO_APPROVED = "twilio-approved" + PROVISIONALLY_APPROVED = "provisionally-approved" + + """ + :ivar sid: The unique string that we created to identify the Bundle resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Bundle resource. + :ivar regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar status: + :ivar valid_until: The date and time in GMT in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format when the resource will be valid until. + :ivar email: The email address that will receive updates when the Bundle resource changes status. + :ivar status_callback: The URL we call to inform your application of status changes. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Bundle resource. + :ivar links: The URLs of the Assigned Items of the Bundle resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.regulation_sid: Optional[str] = payload.get("regulation_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["BundleInstance.Status"] = payload.get("status") + self.valid_until: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("valid_until") + ) + self.email: Optional[str] = payload.get("email") + self.status_callback: Optional[str] = payload.get("status_callback") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[BundleContext] = None + + @property + def _proxy(self) -> "BundleContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BundleContext for this BundleInstance + """ + if self._context is None: + self._context = BundleContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BundleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BundleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BundleInstance": + """ + Fetch the BundleInstance + + + :returns: The fetched BundleInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BundleInstance": + """ + Asynchronous coroutine to fetch the BundleInstance + + + :returns: The fetched BundleInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: Union["BundleInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "BundleInstance": + """ + Update the BundleInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + + :returns: The updated BundleInstance + """ + return self._proxy.update( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + async def update_async( + self, + status: Union["BundleInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "BundleInstance": + """ + Asynchronous coroutine to update the BundleInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + + :returns: The updated BundleInstance + """ + return await self._proxy.update_async( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + @property + def bundle_copies(self) -> BundleCopyList: + """ + Access the bundle_copies + """ + return self._proxy.bundle_copies + + @property + def evaluations(self) -> EvaluationList: + """ + Access the evaluations + """ + return self._proxy.evaluations + + @property + def item_assignments(self) -> ItemAssignmentList: + """ + Access the item_assignments + """ + return self._proxy.item_assignments + + @property + def replace_items(self) -> ReplaceItemsList: + """ + Access the replace_items + """ + return self._proxy.replace_items + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BundleContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the BundleContext + + :param version: Version that contains the resource + :param sid: The unique string that we created to identify the Bundle resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/Bundles/{sid}".format(**self._solution) + + self._bundle_copies: Optional[BundleCopyList] = None + self._evaluations: Optional[EvaluationList] = None + self._item_assignments: Optional[ItemAssignmentList] = None + self._replace_items: Optional[ReplaceItemsList] = None + + def delete(self) -> bool: + """ + Deletes the BundleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BundleInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BundleInstance: + """ + Fetch the BundleInstance + + + :returns: The fetched BundleInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BundleInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BundleInstance: + """ + Asynchronous coroutine to fetch the BundleInstance + + + :returns: The fetched BundleInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BundleInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + status: Union["BundleInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> BundleInstance: + """ + Update the BundleInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + + :returns: The updated BundleInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + status: Union["BundleInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> BundleInstance: + """ + Asynchronous coroutine to update the BundleInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + + :returns: The updated BundleInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def bundle_copies(self) -> BundleCopyList: + """ + Access the bundle_copies + """ + if self._bundle_copies is None: + self._bundle_copies = BundleCopyList( + self._version, + self._solution["sid"], + ) + return self._bundle_copies + + @property + def evaluations(self) -> EvaluationList: + """ + Access the evaluations + """ + if self._evaluations is None: + self._evaluations = EvaluationList( + self._version, + self._solution["sid"], + ) + return self._evaluations + + @property + def item_assignments(self) -> ItemAssignmentList: + """ + Access the item_assignments + """ + if self._item_assignments is None: + self._item_assignments = ItemAssignmentList( + self._version, + self._solution["sid"], + ) + return self._item_assignments + + @property + def replace_items(self) -> ReplaceItemsList: + """ + Access the replace_items + """ + if self._replace_items is None: + self._replace_items = ReplaceItemsList( + self._version, + self._solution["sid"], + ) + return self._replace_items + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BundlePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BundleInstance: + """ + Build an instance of BundleInstance + + :param payload: Payload response from the API + """ + return BundleInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BundleList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BundleList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/Bundles" + + def create( + self, + friendly_name: str, + email: str, + status_callback: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + end_user_type: Union["BundleInstance.EndUserType", object] = values.unset, + number_type: Union[str, object] = values.unset, + ) -> BundleInstance: + """ + Create the BundleInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + :param status_callback: The URL we call to inform your application of status changes. + :param regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param end_user_type: + :param number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `toll free`. + + :returns: The created BundleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "StatusCallback": status_callback, + "RegulationSid": regulation_sid, + "IsoCountry": iso_country, + "EndUserType": end_user_type, + "NumberType": number_type, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + email: str, + status_callback: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + end_user_type: Union["BundleInstance.EndUserType", object] = values.unset, + number_type: Union[str, object] = values.unset, + ) -> BundleInstance: + """ + Asynchronously create the BundleInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Bundle resource changes status. + :param status_callback: The URL we call to inform your application of status changes. + :param regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param end_user_type: + :param number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `toll free`. + + :returns: The created BundleInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "StatusCallback": status_callback, + "RegulationSid": regulation_sid, + "IsoCountry": iso_country, + "EndUserType": end_user_type, + "NumberType": number_type, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleInstance(self._version, payload) + + def stream( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BundleInstance]: + """ + Streams BundleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "BundleInstance.Status" status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param str friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param str regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param str iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param str number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param bool has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param "BundleInstance.SortBy" sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param "BundleInstance.SortDirection" sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param datetime valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + friendly_name=friendly_name, + regulation_sid=regulation_sid, + iso_country=iso_country, + number_type=number_type, + has_valid_until_date=has_valid_until_date, + sort_by=sort_by, + sort_direction=sort_direction, + valid_until_date=valid_until_date, + valid_until_date_before=valid_until_date_before, + valid_until_date_after=valid_until_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BundleInstance]: + """ + Asynchronously streams BundleInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "BundleInstance.Status" status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param str friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param str regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param str iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param str number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param bool has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param "BundleInstance.SortBy" sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param "BundleInstance.SortDirection" sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param datetime valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + friendly_name=friendly_name, + regulation_sid=regulation_sid, + iso_country=iso_country, + number_type=number_type, + has_valid_until_date=has_valid_until_date, + sort_by=sort_by, + sort_direction=sort_direction, + valid_until_date=valid_until_date, + valid_until_date_before=valid_until_date_before, + valid_until_date_after=valid_until_date_after, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BundleInstance]: + """ + Lists BundleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "BundleInstance.Status" status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param str friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param str regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param str iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param str number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param bool has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param "BundleInstance.SortBy" sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param "BundleInstance.SortDirection" sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param datetime valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + friendly_name=friendly_name, + regulation_sid=regulation_sid, + iso_country=iso_country, + number_type=number_type, + has_valid_until_date=has_valid_until_date, + sort_by=sort_by, + sort_direction=sort_direction, + valid_until_date=valid_until_date, + valid_until_date_before=valid_until_date_before, + valid_until_date_after=valid_until_date_after, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BundleInstance]: + """ + Asynchronously lists BundleInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "BundleInstance.Status" status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param str friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param str regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param str iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param str number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param bool has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param "BundleInstance.SortBy" sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param "BundleInstance.SortDirection" sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param datetime valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param datetime valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + friendly_name=friendly_name, + regulation_sid=regulation_sid, + iso_country=iso_country, + number_type=number_type, + has_valid_until_date=has_valid_until_date, + sort_by=sort_by, + sort_direction=sort_direction, + valid_until_date=valid_until_date, + valid_until_date_before=valid_until_date_before, + valid_until_date_after=valid_until_date_after, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BundlePage: + """ + Retrieve a single page of BundleInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BundleInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "RegulationSid": regulation_sid, + "IsoCountry": iso_country, + "NumberType": number_type, + "HasValidUntilDate": serialize.boolean_to_string(has_valid_until_date), + "SortBy": sort_by, + "SortDirection": sort_direction, + "ValidUntilDate": serialize.iso8601_datetime(valid_until_date), + "ValidUntilDate<": serialize.iso8601_datetime(valid_until_date_before), + "ValidUntilDate>": serialize.iso8601_datetime(valid_until_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BundlePage(self._version, response) + + async def page_async( + self, + status: Union["BundleInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + regulation_sid: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + has_valid_until_date: Union[bool, object] = values.unset, + sort_by: Union["BundleInstance.SortBy", object] = values.unset, + sort_direction: Union["BundleInstance.SortDirection", object] = values.unset, + valid_until_date: Union[datetime, object] = values.unset, + valid_until_date_before: Union[datetime, object] = values.unset, + valid_until_date_after: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BundlePage: + """ + Asynchronously retrieve a single page of BundleInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Bundle resource. Please refer to [Bundle Statuses](https://www.twilio.com/docs/phone-numbers/regulatory/api/bundles#bundle-statuses) for more details. + :param friendly_name: The string that you assigned to describe the resource. The column can contain 255 variable characters. + :param regulation_sid: The unique string of a [Regulation resource](https://www.twilio.com/docs/phone-numbers/regulatory/api/regulations) that is associated to the Bundle resource. + :param iso_country: The 2-digit [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Bundle's phone number country ownership request. + :param number_type: The type of phone number of the Bundle's ownership request. Can be `local`, `mobile`, `national`, or `tollfree`. + :param has_valid_until_date: Indicates that the Bundle is a valid Bundle until a specified expiration date. + :param sort_by: Can be `valid-until` or `date-updated`. Defaults to `date-created`. + :param sort_direction: Default is `DESC`. Can be `ASC` or `DESC`. + :param valid_until_date: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param valid_until_date_before: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param valid_until_date_after: Date to filter Bundles having their `valid_until_date` before or after the specified date. Can be `ValidUntilDate>=` or `ValidUntilDate<=`. Both can be used in conjunction as well. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) is the acceptable date format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BundleInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "RegulationSid": regulation_sid, + "IsoCountry": iso_country, + "NumberType": number_type, + "HasValidUntilDate": serialize.boolean_to_string(has_valid_until_date), + "SortBy": sort_by, + "SortDirection": sort_direction, + "ValidUntilDate": serialize.iso8601_datetime(valid_until_date), + "ValidUntilDate<": serialize.iso8601_datetime(valid_until_date_before), + "ValidUntilDate>": serialize.iso8601_datetime(valid_until_date_after), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BundlePage(self._version, response) + + def get_page(self, target_url: str) -> BundlePage: + """ + Retrieve a specific page of BundleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BundleInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BundlePage(self._version, response) + + async def get_page_async(self, target_url: str) -> BundlePage: + """ + Asynchronously retrieve a specific page of BundleInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BundleInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BundlePage(self._version, response) + + def get(self, sid: str) -> BundleContext: + """ + Constructs a BundleContext + + :param sid: The unique string that we created to identify the Bundle resource. + """ + return BundleContext(self._version, sid=sid) + + def __call__(self, sid: str) -> BundleContext: + """ + Constructs a BundleContext + + :param sid: The unique string that we created to identify the Bundle resource. + """ + return BundleContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5666b24 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/bundle_copy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/bundle_copy.cpython-310.pyc new file mode 100644 index 0000000..e7f3061 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/bundle_copy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/evaluation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/evaluation.cpython-310.pyc new file mode 100644 index 0000000..76b3d1e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/evaluation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/item_assignment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/item_assignment.cpython-310.pyc new file mode 100644 index 0000000..1892d07 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/item_assignment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/replace_items.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/replace_items.cpython-310.pyc new file mode 100644 index 0000000..c5ca191 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/__pycache__/replace_items.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/bundle_copy.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/bundle_copy.py new file mode 100644 index 0000000..a8cf77f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/bundle_copy.py @@ -0,0 +1,366 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BundleCopyInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + IN_REVIEW = "in-review" + TWILIO_REJECTED = "twilio-rejected" + TWILIO_APPROVED = "twilio-approved" + PROVISIONALLY_APPROVED = "provisionally-approved" + + """ + :ivar sid: The unique string that we created to identify the Bundle resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Bundle resource. + :ivar regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar status: + :ivar valid_until: The date and time in GMT in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format when the resource will be valid until. + :ivar email: The email address that will receive updates when the Bundle resource changes status. + :ivar status_callback: The URL we call to inform your application of status changes. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], bundle_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.regulation_sid: Optional[str] = payload.get("regulation_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["BundleCopyInstance.Status"] = payload.get("status") + self.valid_until: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("valid_until") + ) + self.email: Optional[str] = payload.get("email") + self.status_callback: Optional[str] = payload.get("status_callback") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "bundle_sid": bundle_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BundleCopyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BundleCopyInstance: + """ + Build an instance of BundleCopyInstance + + :param payload: Payload response from the API + """ + return BundleCopyInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BundleCopyList(ListResource): + + def __init__(self, version: Version, bundle_sid: str): + """ + Initialize the BundleCopyList + + :param version: Version that contains the resource + :param bundle_sid: The unique string that we created to identify the Bundle resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + } + self._uri = "/RegulatoryCompliance/Bundles/{bundle_sid}/Copies".format( + **self._solution + ) + + def create( + self, friendly_name: Union[str, object] = values.unset + ) -> BundleCopyInstance: + """ + Create the BundleCopyInstance + + :param friendly_name: The string that you assigned to describe the copied bundle. + + :returns: The created BundleCopyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleCopyInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> BundleCopyInstance: + """ + Asynchronously create the BundleCopyInstance + + :param friendly_name: The string that you assigned to describe the copied bundle. + + :returns: The created BundleCopyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BundleCopyInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BundleCopyInstance]: + """ + Streams BundleCopyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BundleCopyInstance]: + """ + Asynchronously streams BundleCopyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BundleCopyInstance]: + """ + Lists BundleCopyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BundleCopyInstance]: + """ + Asynchronously lists BundleCopyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BundleCopyPage: + """ + Retrieve a single page of BundleCopyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BundleCopyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BundleCopyPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BundleCopyPage: + """ + Asynchronously retrieve a single page of BundleCopyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BundleCopyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BundleCopyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BundleCopyPage: + """ + Retrieve a specific page of BundleCopyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BundleCopyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BundleCopyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BundleCopyPage: + """ + Asynchronously retrieve a specific page of BundleCopyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BundleCopyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BundleCopyPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/evaluation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/evaluation.py new file mode 100644 index 0000000..39aae3b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/evaluation.py @@ -0,0 +1,469 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EvaluationInstance(InstanceResource): + + class Status(object): + COMPLIANT = "compliant" + NONCOMPLIANT = "noncompliant" + + """ + :ivar sid: The unique string that identifies the Evaluation resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Bundle resource. + :ivar regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :ivar bundle_sid: The unique string that we created to identify the Bundle resource. + :ivar status: + :ivar results: The results of the Evaluation which includes the valid and invalid attributes. + :ivar date_created: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + bundle_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.regulation_sid: Optional[str] = payload.get("regulation_sid") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.status: Optional["EvaluationInstance.Status"] = payload.get("status") + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "bundle_sid": bundle_sid, + "sid": sid or self.sid, + } + self._context: Optional[EvaluationContext] = None + + @property + def _proxy(self) -> "EvaluationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EvaluationContext for this EvaluationInstance + """ + if self._context is None: + self._context = EvaluationContext( + self._version, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EvaluationInstance": + """ + Fetch the EvaluationInstance + + + :returns: The fetched EvaluationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EvaluationInstance": + """ + Asynchronous coroutine to fetch the EvaluationInstance + + + :returns: The fetched EvaluationInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EvaluationContext(InstanceContext): + + def __init__(self, version: Version, bundle_sid: str, sid: str): + """ + Initialize the EvaluationContext + + :param version: Version that contains the resource + :param bundle_sid: The unique string that we created to identify the Bundle resource. + :param sid: The unique string that identifies the Evaluation resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + "sid": sid, + } + self._uri = ( + "/RegulatoryCompliance/Bundles/{bundle_sid}/Evaluations/{sid}".format( + **self._solution + ) + ) + + def fetch(self) -> EvaluationInstance: + """ + Fetch the EvaluationInstance + + + :returns: The fetched EvaluationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EvaluationInstance( + self._version, + payload, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EvaluationInstance: + """ + Asynchronous coroutine to fetch the EvaluationInstance + + + :returns: The fetched EvaluationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EvaluationInstance( + self._version, + payload, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EvaluationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EvaluationInstance: + """ + Build an instance of EvaluationInstance + + :param payload: Payload response from the API + """ + return EvaluationInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EvaluationList(ListResource): + + def __init__(self, version: Version, bundle_sid: str): + """ + Initialize the EvaluationList + + :param version: Version that contains the resource + :param bundle_sid: The unique string that identifies the Bundle resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + } + self._uri = "/RegulatoryCompliance/Bundles/{bundle_sid}/Evaluations".format( + **self._solution + ) + + def create(self) -> EvaluationInstance: + """ + Create the EvaluationInstance + + + :returns: The created EvaluationInstance + """ + + payload = self._version.create( + method="POST", + uri=self._uri, + ) + + return EvaluationInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + async def create_async(self) -> EvaluationInstance: + """ + Asynchronously create the EvaluationInstance + + + :returns: The created EvaluationInstance + """ + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + ) + + return EvaluationInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EvaluationInstance]: + """ + Streams EvaluationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EvaluationInstance]: + """ + Asynchronously streams EvaluationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EvaluationInstance]: + """ + Lists EvaluationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EvaluationInstance]: + """ + Asynchronously lists EvaluationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EvaluationPage: + """ + Retrieve a single page of EvaluationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EvaluationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EvaluationPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EvaluationPage: + """ + Asynchronously retrieve a single page of EvaluationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EvaluationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EvaluationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EvaluationPage: + """ + Retrieve a specific page of EvaluationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EvaluationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EvaluationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EvaluationPage: + """ + Asynchronously retrieve a specific page of EvaluationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EvaluationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EvaluationPage(self._version, response, self._solution) + + def get(self, sid: str) -> EvaluationContext: + """ + Constructs a EvaluationContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return EvaluationContext( + self._version, bundle_sid=self._solution["bundle_sid"], sid=sid + ) + + def __call__(self, sid: str) -> EvaluationContext: + """ + Constructs a EvaluationContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return EvaluationContext( + self._version, bundle_sid=self._solution["bundle_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/item_assignment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/item_assignment.py new file mode 100644 index 0000000..d22fc31 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/item_assignment.py @@ -0,0 +1,518 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ItemAssignmentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Item Assignment resource. + :ivar bundle_sid: The unique string that we created to identify the Bundle resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Item Assignment resource. + :ivar object_sid: The SID of an object bag that holds information of the different items. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Identity resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + bundle_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.bundle_sid: Optional[str] = payload.get("bundle_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.object_sid: Optional[str] = payload.get("object_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "bundle_sid": bundle_sid, + "sid": sid or self.sid, + } + self._context: Optional[ItemAssignmentContext] = None + + @property + def _proxy(self) -> "ItemAssignmentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ItemAssignmentContext for this ItemAssignmentInstance + """ + if self._context is None: + self._context = ItemAssignmentContext( + self._version, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ItemAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ItemAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ItemAssignmentInstance": + """ + Fetch the ItemAssignmentInstance + + + :returns: The fetched ItemAssignmentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ItemAssignmentInstance": + """ + Asynchronous coroutine to fetch the ItemAssignmentInstance + + + :returns: The fetched ItemAssignmentInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ItemAssignmentContext(InstanceContext): + + def __init__(self, version: Version, bundle_sid: str, sid: str): + """ + Initialize the ItemAssignmentContext + + :param version: Version that contains the resource + :param bundle_sid: The unique string that we created to identify the Bundle resource. + :param sid: The unique string that we created to identify the Identity resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + "sid": sid, + } + self._uri = ( + "/RegulatoryCompliance/Bundles/{bundle_sid}/ItemAssignments/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the ItemAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ItemAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ItemAssignmentInstance: + """ + Fetch the ItemAssignmentInstance + + + :returns: The fetched ItemAssignmentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ItemAssignmentInstance( + self._version, + payload, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ItemAssignmentInstance: + """ + Asynchronous coroutine to fetch the ItemAssignmentInstance + + + :returns: The fetched ItemAssignmentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ItemAssignmentInstance( + self._version, + payload, + bundle_sid=self._solution["bundle_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ItemAssignmentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ItemAssignmentInstance: + """ + Build an instance of ItemAssignmentInstance + + :param payload: Payload response from the API + """ + return ItemAssignmentInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ItemAssignmentList(ListResource): + + def __init__(self, version: Version, bundle_sid: str): + """ + Initialize the ItemAssignmentList + + :param version: Version that contains the resource + :param bundle_sid: The unique string that we created to identify the Bundle resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + } + self._uri = "/RegulatoryCompliance/Bundles/{bundle_sid}/ItemAssignments".format( + **self._solution + ) + + def create(self, object_sid: str) -> ItemAssignmentInstance: + """ + Create the ItemAssignmentInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created ItemAssignmentInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ItemAssignmentInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + async def create_async(self, object_sid: str) -> ItemAssignmentInstance: + """ + Asynchronously create the ItemAssignmentInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created ItemAssignmentInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ItemAssignmentInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ItemAssignmentInstance]: + """ + Streams ItemAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ItemAssignmentInstance]: + """ + Asynchronously streams ItemAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ItemAssignmentInstance]: + """ + Lists ItemAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ItemAssignmentInstance]: + """ + Asynchronously lists ItemAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ItemAssignmentPage: + """ + Retrieve a single page of ItemAssignmentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ItemAssignmentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ItemAssignmentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ItemAssignmentPage: + """ + Asynchronously retrieve a single page of ItemAssignmentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ItemAssignmentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ItemAssignmentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ItemAssignmentPage: + """ + Retrieve a specific page of ItemAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ItemAssignmentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ItemAssignmentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ItemAssignmentPage: + """ + Asynchronously retrieve a specific page of ItemAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ItemAssignmentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ItemAssignmentPage(self._version, response, self._solution) + + def get(self, sid: str) -> ItemAssignmentContext: + """ + Constructs a ItemAssignmentContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return ItemAssignmentContext( + self._version, bundle_sid=self._solution["bundle_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ItemAssignmentContext: + """ + Constructs a ItemAssignmentContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return ItemAssignmentContext( + self._version, bundle_sid=self._solution["bundle_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/replace_items.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/replace_items.py new file mode 100644 index 0000000..09e2823 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/bundle/replace_items.py @@ -0,0 +1,157 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ReplaceItemsInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + IN_REVIEW = "in-review" + TWILIO_REJECTED = "twilio-rejected" + TWILIO_APPROVED = "twilio-approved" + PROVISIONALLY_APPROVED = "provisionally-approved" + + """ + :ivar sid: The unique string that we created to identify the Bundle resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Bundle resource. + :ivar regulation_sid: The unique string of a regulation that is associated to the Bundle resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar status: + :ivar valid_until: The date and time in GMT in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format when the resource will be valid until. + :ivar email: The email address that will receive updates when the Bundle resource changes status. + :ivar status_callback: The URL we call to inform your application of status changes. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], bundle_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.regulation_sid: Optional[str] = payload.get("regulation_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["ReplaceItemsInstance.Status"] = payload.get("status") + self.valid_until: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("valid_until") + ) + self.email: Optional[str] = payload.get("email") + self.status_callback: Optional[str] = payload.get("status_callback") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "bundle_sid": bundle_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ReplaceItemsList(ListResource): + + def __init__(self, version: Version, bundle_sid: str): + """ + Initialize the ReplaceItemsList + + :param version: Version that contains the resource + :param bundle_sid: The unique string that identifies the Bundle where the item assignments are going to be replaced. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "bundle_sid": bundle_sid, + } + self._uri = "/RegulatoryCompliance/Bundles/{bundle_sid}/ReplaceItems".format( + **self._solution + ) + + def create(self, from_bundle_sid: str) -> ReplaceItemsInstance: + """ + Create the ReplaceItemsInstance + + :param from_bundle_sid: The source bundle sid to copy the item assignments from. + + :returns: The created ReplaceItemsInstance + """ + + data = values.of( + { + "FromBundleSid": from_bundle_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ReplaceItemsInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + async def create_async(self, from_bundle_sid: str) -> ReplaceItemsInstance: + """ + Asynchronously create the ReplaceItemsInstance + + :param from_bundle_sid: The source bundle sid to copy the item assignments from. + + :returns: The created ReplaceItemsInstance + """ + + data = values.of( + { + "FromBundleSid": from_bundle_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ReplaceItemsInstance( + self._version, payload, bundle_sid=self._solution["bundle_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user.py new file mode 100644 index 0000000..3f9e5ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user.py @@ -0,0 +1,608 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EndUserInstance(InstanceResource): + + class Type(object): + INDIVIDUAL = "individual" + BUSINESS = "business" + + """ + :ivar sid: The unique string created by Twilio to identify the End User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the End User resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: + :ivar attributes: The set of parameters that are the attributes of the End Users resource which are listed in the End User Types. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the End User resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional["EndUserInstance.Type"] = payload.get("type") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EndUserContext] = None + + @property + def _proxy(self) -> "EndUserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EndUserContext for this EndUserInstance + """ + if self._context is None: + self._context = EndUserContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "EndUserInstance": + """ + Fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EndUserInstance": + """ + Asynchronous coroutine to fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "EndUserInstance": + """ + Update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "EndUserInstance": + """ + Asynchronous coroutine to update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EndUserContext + + :param version: Version that contains the resource + :param sid: The unique string created by Twilio to identify the End User resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/EndUsers/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> EndUserInstance: + """ + Fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EndUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EndUserInstance: + """ + Asynchronous coroutine to fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EndUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Asynchronous coroutine to update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EndUserInstance: + """ + Build an instance of EndUserInstance + + :param payload: Payload response from the API + """ + return EndUserInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EndUserList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EndUserList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/EndUsers" + + def create( + self, + friendly_name: str, + type: "EndUserInstance.Type", + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Create the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The created EndUserInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + type: "EndUserInstance.Type", + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Asynchronously create the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The created EndUserInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EndUserInstance]: + """ + Streams EndUserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EndUserInstance]: + """ + Asynchronously streams EndUserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserInstance]: + """ + Lists EndUserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserInstance]: + """ + Asynchronously lists EndUserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserPage: + """ + Retrieve a single page of EndUserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EndUserPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserPage: + """ + Asynchronously retrieve a single page of EndUserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EndUserPage(self._version, response) + + def get_page(self, target_url: str) -> EndUserPage: + """ + Retrieve a specific page of EndUserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EndUserPage(self._version, response) + + async def get_page_async(self, target_url: str) -> EndUserPage: + """ + Asynchronously retrieve a specific page of EndUserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EndUserPage(self._version, response) + + def get(self, sid: str) -> EndUserContext: + """ + Constructs a EndUserContext + + :param sid: The unique string created by Twilio to identify the End User resource. + """ + return EndUserContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EndUserContext: + """ + Constructs a EndUserContext + + :param sid: The unique string created by Twilio to identify the End User resource. + """ + return EndUserContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user_type.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user_type.py new file mode 100644 index 0000000..ba15557 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/end_user_type.py @@ -0,0 +1,394 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EndUserTypeInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the End-User Type resource. + :ivar friendly_name: A human-readable description that is assigned to describe the End-User Type resource. Examples can include first name, last name, email, business name, etc + :ivar machine_name: A machine-readable description of the End-User Type resource. Examples can include first_name, last_name, email, business_name, etc. + :ivar fields: The required information for creating an End-User. The required fields will change as regulatory needs change and will differ for businesses and individuals. + :ivar url: The absolute URL of the End-User Type resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.machine_name: Optional[str] = payload.get("machine_name") + self.fields: Optional[List[Dict[str, object]]] = payload.get("fields") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EndUserTypeContext] = None + + @property + def _proxy(self) -> "EndUserTypeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EndUserTypeContext for this EndUserTypeInstance + """ + if self._context is None: + self._context = EndUserTypeContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EndUserTypeInstance": + """ + Fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EndUserTypeInstance": + """ + Asynchronous coroutine to fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserTypeContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EndUserTypeContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the End-User Type resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/EndUserTypes/{sid}".format(**self._solution) + + def fetch(self) -> EndUserTypeInstance: + """ + Fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EndUserTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EndUserTypeInstance: + """ + Asynchronous coroutine to fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EndUserTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserTypePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EndUserTypeInstance: + """ + Build an instance of EndUserTypeInstance + + :param payload: Payload response from the API + """ + return EndUserTypeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EndUserTypeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EndUserTypeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/EndUserTypes" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EndUserTypeInstance]: + """ + Streams EndUserTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EndUserTypeInstance]: + """ + Asynchronously streams EndUserTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserTypeInstance]: + """ + Lists EndUserTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserTypeInstance]: + """ + Asynchronously lists EndUserTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserTypePage: + """ + Retrieve a single page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EndUserTypePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserTypePage: + """ + Asynchronously retrieve a single page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EndUserTypePage(self._version, response) + + def get_page(self, target_url: str) -> EndUserTypePage: + """ + Retrieve a specific page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserTypeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EndUserTypePage(self._version, response) + + async def get_page_async(self, target_url: str) -> EndUserTypePage: + """ + Asynchronously retrieve a specific page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserTypeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EndUserTypePage(self._version, response) + + def get(self, sid: str) -> EndUserTypeContext: + """ + Constructs a EndUserTypeContext + + :param sid: The unique string that identifies the End-User Type resource. + """ + return EndUserTypeContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EndUserTypeContext: + """ + Constructs a EndUserTypeContext + + :param sid: The unique string that identifies the End-User Type resource. + """ + return EndUserTypeContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/regulation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/regulation.py new file mode 100644 index 0000000..9d9a508 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/regulation.py @@ -0,0 +1,463 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RegulationInstance(InstanceResource): + + class EndUserType(object): + INDIVIDUAL = "individual" + BUSINESS = "business" + + """ + :ivar sid: The unique string that identifies the Regulation resource. + :ivar friendly_name: A human-readable description that is assigned to describe the Regulation resource. Examples can include Germany: Mobile - Business. + :ivar iso_country: The ISO country code of the phone number's country. + :ivar number_type: The type of phone number restricted by the regulatory requirement. For example, Germany mobile phone numbers provisioned by businesses require a business name with commercial register proof from the Handelsregisterauszug and a proof of address from Handelsregisterauszug or a trade license by Gewerbeanmeldung. + :ivar end_user_type: + :ivar requirements: The SID of an object that holds the regulatory information of the phone number country, phone number type, and end user type. + :ivar url: The absolute URL of the Regulation resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iso_country: Optional[str] = payload.get("iso_country") + self.number_type: Optional[str] = payload.get("number_type") + self.end_user_type: Optional["RegulationInstance.EndUserType"] = payload.get( + "end_user_type" + ) + self.requirements: Optional[Dict[str, object]] = payload.get("requirements") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RegulationContext] = None + + @property + def _proxy(self) -> "RegulationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RegulationContext for this RegulationInstance + """ + if self._context is None: + self._context = RegulationContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "RegulationInstance": + """ + Fetch the RegulationInstance + + + :returns: The fetched RegulationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RegulationInstance": + """ + Asynchronous coroutine to fetch the RegulationInstance + + + :returns: The fetched RegulationInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RegulationContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RegulationContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the Regulation resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/Regulations/{sid}".format(**self._solution) + + def fetch(self) -> RegulationInstance: + """ + Fetch the RegulationInstance + + + :returns: The fetched RegulationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RegulationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RegulationInstance: + """ + Asynchronous coroutine to fetch the RegulationInstance + + + :returns: The fetched RegulationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RegulationInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RegulationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RegulationInstance: + """ + Build an instance of RegulationInstance + + :param payload: Payload response from the API + """ + return RegulationInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RegulationList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RegulationList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/Regulations" + + def stream( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RegulationInstance]: + """ + Streams RegulationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RegulationInstance.EndUserType" end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param str iso_country: The ISO country code of the phone number's country. + :param str number_type: The type of phone number that the regulatory requiremnt is restricting. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + end_user_type=end_user_type, + iso_country=iso_country, + number_type=number_type, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RegulationInstance]: + """ + Asynchronously streams RegulationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RegulationInstance.EndUserType" end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param str iso_country: The ISO country code of the phone number's country. + :param str number_type: The type of phone number that the regulatory requiremnt is restricting. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + end_user_type=end_user_type, + iso_country=iso_country, + number_type=number_type, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RegulationInstance]: + """ + Lists RegulationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RegulationInstance.EndUserType" end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param str iso_country: The ISO country code of the phone number's country. + :param str number_type: The type of phone number that the regulatory requiremnt is restricting. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + end_user_type=end_user_type, + iso_country=iso_country, + number_type=number_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RegulationInstance]: + """ + Asynchronously lists RegulationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RegulationInstance.EndUserType" end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param str iso_country: The ISO country code of the phone number's country. + :param str number_type: The type of phone number that the regulatory requiremnt is restricting. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + end_user_type=end_user_type, + iso_country=iso_country, + number_type=number_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RegulationPage: + """ + Retrieve a single page of RegulationInstance records from the API. + Request is executed immediately + + :param end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param iso_country: The ISO country code of the phone number's country. + :param number_type: The type of phone number that the regulatory requiremnt is restricting. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RegulationInstance + """ + data = values.of( + { + "EndUserType": end_user_type, + "IsoCountry": iso_country, + "NumberType": number_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RegulationPage(self._version, response) + + async def page_async( + self, + end_user_type: Union["RegulationInstance.EndUserType", object] = values.unset, + iso_country: Union[str, object] = values.unset, + number_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RegulationPage: + """ + Asynchronously retrieve a single page of RegulationInstance records from the API. + Request is executed immediately + + :param end_user_type: The type of End User the regulation requires - can be `individual` or `business`. + :param iso_country: The ISO country code of the phone number's country. + :param number_type: The type of phone number that the regulatory requiremnt is restricting. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RegulationInstance + """ + data = values.of( + { + "EndUserType": end_user_type, + "IsoCountry": iso_country, + "NumberType": number_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RegulationPage(self._version, response) + + def get_page(self, target_url: str) -> RegulationPage: + """ + Retrieve a specific page of RegulationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RegulationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RegulationPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RegulationPage: + """ + Asynchronously retrieve a specific page of RegulationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RegulationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RegulationPage(self._version, response) + + def get(self, sid: str) -> RegulationContext: + """ + Constructs a RegulationContext + + :param sid: The unique string that identifies the Regulation resource. + """ + return RegulationContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RegulationContext: + """ + Constructs a RegulationContext + + :param sid: The unique string that identifies the Regulation resource. + """ + return RegulationContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document.py new file mode 100644 index 0000000..f2cd4e1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document.py @@ -0,0 +1,626 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SupportingDocumentInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + REJECTED = "rejected" + APPROVED = "approved" + EXPIRED = "expired" + PROVISIONALLY_APPROVED = "provisionally-approved" + + """ + :ivar sid: The unique string created by Twilio to identify the Supporting Document resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Document resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar mime_type: The image type uploaded in the Supporting Document container. + :ivar status: + :ivar failure_reason: The failure reason of the Supporting Document Resource. + :ivar type: The type of the Supporting Document. + :ivar attributes: The set of parameters that are the attributes of the Supporting Documents resource which are listed in the Supporting Document Types. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Supporting Document resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.mime_type: Optional[str] = payload.get("mime_type") + self.status: Optional["SupportingDocumentInstance.Status"] = payload.get( + "status" + ) + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.type: Optional[str] = payload.get("type") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SupportingDocumentContext] = None + + @property + def _proxy(self) -> "SupportingDocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SupportingDocumentContext for this SupportingDocumentInstance + """ + if self._context is None: + self._context = SupportingDocumentContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SupportingDocumentInstance": + """ + Fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SupportingDocumentInstance": + """ + Asynchronous coroutine to fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "SupportingDocumentInstance": + """ + Update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "SupportingDocumentInstance": + """ + Asynchronous coroutine to update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SupportingDocumentContext + + :param version: Version that contains the resource + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/SupportingDocuments/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SupportingDocumentInstance: + """ + Fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SupportingDocumentInstance: + """ + Asynchronous coroutine to fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Asynchronous coroutine to update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SupportingDocumentInstance: + """ + Build an instance of SupportingDocumentInstance + + :param payload: Payload response from the API + """ + return SupportingDocumentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SupportingDocumentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SupportingDocumentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/SupportingDocuments" + + def create( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Create the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of the Supporting Document. + :param attributes: The set of parameters that are the attributes of the Supporting Documents resource which are derived Supporting Document Types. + + :returns: The created SupportingDocumentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Asynchronously create the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of the Supporting Document. + :param attributes: The set of parameters that are the attributes of the Supporting Documents resource which are derived Supporting Document Types. + + :returns: The created SupportingDocumentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SupportingDocumentInstance]: + """ + Streams SupportingDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SupportingDocumentInstance]: + """ + Asynchronously streams SupportingDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentInstance]: + """ + Lists SupportingDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentInstance]: + """ + Asynchronously lists SupportingDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentPage: + """ + Retrieve a single page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SupportingDocumentPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentPage: + """ + Asynchronously retrieve a single page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SupportingDocumentPage(self._version, response) + + def get_page(self, target_url: str) -> SupportingDocumentPage: + """ + Retrieve a specific page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SupportingDocumentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SupportingDocumentPage: + """ + Asynchronously retrieve a specific page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SupportingDocumentPage(self._version, response) + + def get(self, sid: str) -> SupportingDocumentContext: + """ + Constructs a SupportingDocumentContext + + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + return SupportingDocumentContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SupportingDocumentContext: + """ + Constructs a SupportingDocumentContext + + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + return SupportingDocumentContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document_type.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document_type.py new file mode 100644 index 0000000..0c7dc7b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/numbers/v2/regulatory_compliance/supporting_document_type.py @@ -0,0 +1,396 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Numbers + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SupportingDocumentTypeInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the Supporting Document Type resource. + :ivar friendly_name: A human-readable description of the Supporting Document Type resource. + :ivar machine_name: The machine-readable description of the Supporting Document Type resource. + :ivar fields: The required information for creating a Supporting Document. The required fields will change as regulatory needs change and will differ for businesses and individuals. + :ivar url: The absolute URL of the Supporting Document Type resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.machine_name: Optional[str] = payload.get("machine_name") + self.fields: Optional[List[Dict[str, object]]] = payload.get("fields") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SupportingDocumentTypeContext] = None + + @property + def _proxy(self) -> "SupportingDocumentTypeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SupportingDocumentTypeContext for this SupportingDocumentTypeInstance + """ + if self._context is None: + self._context = SupportingDocumentTypeContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SupportingDocumentTypeInstance": + """ + Fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SupportingDocumentTypeInstance": + """ + Asynchronous coroutine to fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentTypeContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SupportingDocumentTypeContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RegulatoryCompliance/SupportingDocumentTypes/{sid}".format( + **self._solution + ) + + def fetch(self) -> SupportingDocumentTypeInstance: + """ + Fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SupportingDocumentTypeInstance: + """ + Asynchronous coroutine to fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentTypePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SupportingDocumentTypeInstance: + """ + Build an instance of SupportingDocumentTypeInstance + + :param payload: Payload response from the API + """ + return SupportingDocumentTypeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SupportingDocumentTypeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SupportingDocumentTypeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RegulatoryCompliance/SupportingDocumentTypes" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SupportingDocumentTypeInstance]: + """ + Streams SupportingDocumentTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SupportingDocumentTypeInstance]: + """ + Asynchronously streams SupportingDocumentTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentTypeInstance]: + """ + Lists SupportingDocumentTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentTypeInstance]: + """ + Asynchronously lists SupportingDocumentTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentTypePage: + """ + Retrieve a single page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SupportingDocumentTypePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentTypePage: + """ + Asynchronously retrieve a single page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SupportingDocumentTypePage(self._version, response) + + def get_page(self, target_url: str) -> SupportingDocumentTypePage: + """ + Retrieve a specific page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentTypeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SupportingDocumentTypePage(self._version, response) + + async def get_page_async(self, target_url: str) -> SupportingDocumentTypePage: + """ + Asynchronously retrieve a specific page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentTypeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SupportingDocumentTypePage(self._version, response) + + def get(self, sid: str) -> SupportingDocumentTypeContext: + """ + Constructs a SupportingDocumentTypeContext + + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + return SupportingDocumentTypeContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SupportingDocumentTypeContext: + """ + Constructs a SupportingDocumentTypeContext + + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + return SupportingDocumentTypeContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/OauthBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/OauthBase.py new file mode 100644 index 0000000..87cbbac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/OauthBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.oauth.v1 import V1 + + +class OauthBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Oauth Domain + + :returns: Domain for Oauth + """ + super().__init__(twilio, "https://oauth.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Oauth + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__init__.py new file mode 100644 index 0000000..709150e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__init__.py @@ -0,0 +1,55 @@ +from warnings import warn + +from twilio.rest.oauth.OauthBase import OauthBase +from twilio.rest.oauth.v1.device_code import DeviceCodeList +from twilio.rest.oauth.v1.oauth import OauthList +from twilio.rest.oauth.v1.openid_discovery import OpenidDiscoveryList +from twilio.rest.oauth.v1.token import TokenList +from twilio.rest.oauth.v1.user_info import UserInfoList + + +class Oauth(OauthBase): + @property + def oauth(self) -> OauthList: + warn( + "oauth is deprecated. Use v1.oauth instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.oauth + + @property + def device_code(self) -> DeviceCodeList: + warn( + "device_code is deprecated. Use v1.device_code instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.device_code + + @property + def openid_discovery(self) -> OpenidDiscoveryList: + warn( + "openid_discovery is deprecated. Use v1.openid_discovery instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.openid_discovery + + @property + def token(self) -> TokenList: + warn( + "token is deprecated. Use v1.token instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.token + + @property + def user_info(self) -> UserInfoList: + warn( + "user_info is deprecated. Use v1.user_info instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.user_info diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/OauthBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/OauthBase.cpython-310.pyc new file mode 100644 index 0000000..ace5a9f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/OauthBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2b3dcbf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__init__.py new file mode 100644 index 0000000..e05c7de --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Oauth + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.oauth.v1.authorize import AuthorizeList +from twilio.rest.oauth.v1.token import TokenList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Oauth + + :param domain: The Twilio.oauth domain + """ + super().__init__(domain, "v1") + self._authorize: Optional[AuthorizeList] = None + self._token: Optional[TokenList] = None + + @property + def authorize(self) -> AuthorizeList: + if self._authorize is None: + self._authorize = AuthorizeList(self) + return self._authorize + + @property + def token(self) -> TokenList: + if self._token is None: + self._token = TokenList(self) + return self._token + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6610d64 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/authorize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/authorize.cpython-310.pyc new file mode 100644 index 0000000..f1bcc06 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/authorize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/token.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/token.cpython-310.pyc new file mode 100644 index 0000000..539d60b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/__pycache__/token.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/authorize.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/authorize.py new file mode 100644 index 0000000..9798a73 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/authorize.py @@ -0,0 +1,122 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Oauth + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AuthorizeInstance(InstanceResource): + """ + :ivar redirect_to: The callback URL + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.redirect_to: Optional[str] = payload.get("redirect_to") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class AuthorizeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AuthorizeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/authorize" + + def fetch( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: + """ + Asynchronously fetch the AuthorizeInstance + + :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback + :returns: The fetched AuthorizeInstance + """ + + params = values.of( + { + "ResponseType": response_type, + "ClientId": client_id, + "RedirectUri": redirect_uri, + "Scope": scope, + "State": state, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=params) + + return AuthorizeInstance(self._version, payload) + + async def fetch_async( + self, + response_type: Union[str, object] = values.unset, + client_id: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + state: Union[str, object] = values.unset, + ) -> AuthorizeInstance: + """ + Asynchronously fetch the AuthorizeInstance + + :param response_type: Response Type:param client_id: The Client Identifier:param redirect_uri: The url to which response will be redirected to:param scope: The scope of the access request:param state: An opaque value which can be used to maintain state between the request and callback + :returns: The fetched AuthorizeInstance + """ + + params = values.of( + { + "ResponseType": response_type, + "ClientId": client_id, + "RedirectUri": redirect_uri, + "Scope": scope, + "State": state, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=params + ) + + return AuthorizeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/token.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/token.py new file mode 100644 index 0000000..42884f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/oauth/v1/token.py @@ -0,0 +1,164 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Oauth + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TokenInstance(InstanceResource): + """ + :ivar access_token: Token which carries the necessary information to access a Twilio resource directly. + :ivar refresh_token: Token which carries the information necessary to get a new access token. + :ivar id_token: Token which carries the information necessary of user profile. + :ivar token_type: Token type + :ivar expires_in: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.access_token: Optional[str] = payload.get("access_token") + self.refresh_token: Optional[str] = payload.get("refresh_token") + self.id_token: Optional[str] = payload.get("id_token") + self.token_type: Optional[str] = payload.get("token_type") + self.expires_in: Optional[int] = payload.get("expires_in") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class TokenList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TokenList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/token" + + def create( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: + """ + Create the TokenInstance + + :param grant_type: Grant type is a credential representing resource owner's authorization which can be used by client to obtain access token. + :param client_id: A 34 character string that uniquely identifies this OAuth App. + :param client_secret: The credential for confidential OAuth App. + :param code: JWT token related to the authorization code grant type. + :param redirect_uri: The redirect uri + :param audience: The targeted audience uri + :param refresh_token: JWT token related to refresh access token. + :param scope: The scope of token + + :returns: The created TokenInstance + """ + + data = values.of( + { + "GrantType": grant_type, + "ClientId": client_id, + "ClientSecret": client_secret, + "Code": code, + "RedirectUri": redirect_uri, + "Audience": audience, + "RefreshToken": refresh_token, + "Scope": scope, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TokenInstance(self._version, payload) + + async def create_async( + self, + grant_type: str, + client_id: str, + client_secret: Union[str, object] = values.unset, + code: Union[str, object] = values.unset, + redirect_uri: Union[str, object] = values.unset, + audience: Union[str, object] = values.unset, + refresh_token: Union[str, object] = values.unset, + scope: Union[str, object] = values.unset, + ) -> TokenInstance: + """ + Asynchronously create the TokenInstance + + :param grant_type: Grant type is a credential representing resource owner's authorization which can be used by client to obtain access token. + :param client_id: A 34 character string that uniquely identifies this OAuth App. + :param client_secret: The credential for confidential OAuth App. + :param code: JWT token related to the authorization code grant type. + :param redirect_uri: The redirect uri + :param audience: The targeted audience uri + :param refresh_token: JWT token related to refresh access token. + :param scope: The scope of token + + :returns: The created TokenInstance + """ + + data = values.of( + { + "GrantType": grant_type, + "ClientId": client_id, + "ClientSecret": client_secret, + "Code": code, + "RedirectUri": redirect_uri, + "Audience": audience, + "RefreshToken": refresh_token, + "Scope": scope, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TokenInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/PreviewBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/PreviewBase.py new file mode 100644 index 0000000..6608a69 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/PreviewBase.py @@ -0,0 +1,88 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.preview.deployed_devices import DeployedDevices +from twilio.rest.preview.hosted_numbers import HostedNumbers +from twilio.rest.preview.sync import Sync +from twilio.rest.preview.marketplace import Marketplace +from twilio.rest.preview.wireless import Wireless + + +class PreviewBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Preview Domain + + :returns: Domain for Preview + """ + super().__init__(twilio, "https://preview.twilio.com") + self._deployed_devices: Optional[DeployedDevices] = None + self._hosted_numbers: Optional[HostedNumbers] = None + self._sync: Optional[Sync] = None + self._marketplace: Optional[Marketplace] = None + self._wireless: Optional[Wireless] = None + + @property + def deployed_devices(self) -> DeployedDevices: + """ + :returns: Versions deployed_devices of Preview + """ + if self._deployed_devices is None: + self._deployed_devices = DeployedDevices(self) + return self._deployed_devices + + @property + def hosted_numbers(self) -> HostedNumbers: + """ + :returns: Versions hosted_numbers of Preview + """ + if self._hosted_numbers is None: + self._hosted_numbers = HostedNumbers(self) + return self._hosted_numbers + + @property + def sync(self) -> Sync: + """ + :returns: Versions sync of Preview + """ + if self._sync is None: + self._sync = Sync(self) + return self._sync + + @property + def marketplace(self) -> Marketplace: + """ + :returns: Versions marketplace of Preview + """ + if self._marketplace is None: + self._marketplace = Marketplace(self) + return self._marketplace + + @property + def wireless(self) -> Wireless: + """ + :returns: Versions wireless of Preview + """ + if self._wireless is None: + self._wireless = Wireless(self) + return self._wireless + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__init__.py new file mode 100644 index 0000000..79d870e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__init__.py @@ -0,0 +1,97 @@ +from warnings import warn + +from twilio.rest.preview.PreviewBase import PreviewBase +from twilio.rest.preview.deployed_devices.fleet import FleetList +from twilio.rest.preview.hosted_numbers.authorization_document import ( + AuthorizationDocumentList, +) +from twilio.rest.preview.hosted_numbers.hosted_number_order import HostedNumberOrderList +from twilio.rest.preview.marketplace.available_add_on import AvailableAddOnList +from twilio.rest.preview.marketplace.installed_add_on import InstalledAddOnList +from twilio.rest.preview.sync.service import ServiceList +from twilio.rest.preview.wireless.command import CommandList +from twilio.rest.preview.wireless.rate_plan import RatePlanList +from twilio.rest.preview.wireless.sim import SimList + + +class Preview(PreviewBase): + @property + def fleets(self) -> FleetList: + warn( + "fleets is deprecated. Use deployed_devices.fleets instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.deployed_devices.fleets + + @property + def authorization_documents(self) -> AuthorizationDocumentList: + warn( + "authorization_documents is deprecated. Use hosted_numbers.authorization_documents instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.hosted_numbers.authorization_documents + + @property + def hosted_number_orders(self) -> HostedNumberOrderList: + warn( + "hosted_number_orders is deprecated. Use hosted_numbers.hosted_number_orders instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.hosted_numbers.hosted_number_orders + + @property + def available_add_ons(self) -> AvailableAddOnList: + warn( + "available_add_ons is deprecated. Use marketplace.available_add_ons instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.marketplace.available_add_ons + + @property + def installed_add_ons(self) -> InstalledAddOnList: + warn( + "installed_add_ons is deprecated. Use marketplace.installed_add_ons instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.marketplace.installed_add_ons + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use sync.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.sync.services + + @property + def commands(self) -> CommandList: + warn( + "commands is deprecated. Use wireless.commands instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.wireless.commands + + @property + def rate_plans(self) -> RatePlanList: + warn( + "rate_plans is deprecated. Use wireless.rate_plans instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.wireless.rate_plans + + @property + def sims(self) -> SimList: + warn( + "sims is deprecated. Use wireless.sims instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.wireless.sims diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/PreviewBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/PreviewBase.cpython-310.pyc new file mode 100644 index 0000000..6c81d58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/PreviewBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..62b11fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__init__.py new file mode 100644 index 0000000..795ee96 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview.deployed_devices.fleet import FleetList + + +class DeployedDevices(Version): + + def __init__(self, domain: Domain): + """ + Initialize the DeployedDevices version of Preview + + :param domain: The Twilio.preview domain + """ + super().__init__(domain, "DeployedDevices") + self._fleets: Optional[FleetList] = None + + @property + def fleets(self) -> FleetList: + if self._fleets is None: + self._fleets = FleetList(self) + return self._fleets + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5ddb422 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__init__.py new file mode 100644 index 0000000..0f5c284 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__init__.py @@ -0,0 +1,676 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.deployed_devices.fleet.certificate import CertificateList +from twilio.rest.preview.deployed_devices.fleet.deployment import DeploymentList +from twilio.rest.preview.deployed_devices.fleet.device import DeviceList +from twilio.rest.preview.deployed_devices.fleet.key import KeyList + + +class FleetInstance(InstanceResource): + """ + :ivar sid: Contains a 34 character string that uniquely identifies this Fleet resource. + :ivar url: Contains an absolute URL for this Fleet resource. + :ivar unique_name: Contains a unique and addressable name of this Fleet, e.g. 'default', up to 128 characters long. + :ivar friendly_name: Contains a human readable descriptive text for this Fleet, up to 256 characters long. + :ivar account_sid: Speicifies the unique string identifier of the Account responsible for this Fleet. + :ivar default_deployment_sid: Contains the string identifier of the automatically provisioned default Deployment of this Fleet. + :ivar date_created: Specifies the date this Fleet was created, given in UTC ISO 8601 format. + :ivar date_updated: Specifies the date this Fleet was last updated, given in UTC ISO 8601 format. + :ivar links: Contains a dictionary of URL links to nested resources of this Fleet. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.unique_name: Optional[str] = payload.get("unique_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.default_deployment_sid: Optional[str] = payload.get( + "default_deployment_sid" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[FleetContext] = None + + @property + def _proxy(self) -> "FleetContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FleetContext for this FleetInstance + """ + if self._context is None: + self._context = FleetContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FleetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FleetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FleetInstance": + """ + Fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FleetInstance": + """ + Asynchronous coroutine to fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_deployment_sid: Union[str, object] = values.unset, + ) -> "FleetInstance": + """ + Update the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + :param default_deployment_sid: Provides a string identifier of a Deployment that is going to be used as a default one for this Fleet. + + :returns: The updated FleetInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + default_deployment_sid=default_deployment_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_deployment_sid: Union[str, object] = values.unset, + ) -> "FleetInstance": + """ + Asynchronous coroutine to update the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + :param default_deployment_sid: Provides a string identifier of a Deployment that is going to be used as a default one for this Fleet. + + :returns: The updated FleetInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + default_deployment_sid=default_deployment_sid, + ) + + @property + def certificates(self) -> CertificateList: + """ + Access the certificates + """ + return self._proxy.certificates + + @property + def deployments(self) -> DeploymentList: + """ + Access the deployments + """ + return self._proxy.deployments + + @property + def devices(self) -> DeviceList: + """ + Access the devices + """ + return self._proxy.devices + + @property + def keys(self) -> KeyList: + """ + Access the keys + """ + return self._proxy.keys + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FleetContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FleetContext + + :param version: Version that contains the resource + :param sid: Provides a 34 character string that uniquely identifies the requested Fleet resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Fleets/{sid}".format(**self._solution) + + self._certificates: Optional[CertificateList] = None + self._deployments: Optional[DeploymentList] = None + self._devices: Optional[DeviceList] = None + self._keys: Optional[KeyList] = None + + def delete(self) -> bool: + """ + Deletes the FleetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FleetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FleetInstance: + """ + Fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FleetInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FleetInstance: + """ + Asynchronous coroutine to fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FleetInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + default_deployment_sid: Union[str, object] = values.unset, + ) -> FleetInstance: + """ + Update the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + :param default_deployment_sid: Provides a string identifier of a Deployment that is going to be used as a default one for this Fleet. + + :returns: The updated FleetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultDeploymentSid": default_deployment_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + default_deployment_sid: Union[str, object] = values.unset, + ) -> FleetInstance: + """ + Asynchronous coroutine to update the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + :param default_deployment_sid: Provides a string identifier of a Deployment that is going to be used as a default one for this Fleet. + + :returns: The updated FleetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DefaultDeploymentSid": default_deployment_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def certificates(self) -> CertificateList: + """ + Access the certificates + """ + if self._certificates is None: + self._certificates = CertificateList( + self._version, + self._solution["sid"], + ) + return self._certificates + + @property + def deployments(self) -> DeploymentList: + """ + Access the deployments + """ + if self._deployments is None: + self._deployments = DeploymentList( + self._version, + self._solution["sid"], + ) + return self._deployments + + @property + def devices(self) -> DeviceList: + """ + Access the devices + """ + if self._devices is None: + self._devices = DeviceList( + self._version, + self._solution["sid"], + ) + return self._devices + + @property + def keys(self) -> KeyList: + """ + Access the keys + """ + if self._keys is None: + self._keys = KeyList( + self._version, + self._solution["sid"], + ) + return self._keys + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FleetPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FleetInstance: + """ + Build an instance of FleetInstance + + :param payload: Payload response from the API + """ + return FleetInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FleetList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FleetList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Fleets" + + def create(self, friendly_name: Union[str, object] = values.unset) -> FleetInstance: + """ + Create the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + + :returns: The created FleetInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> FleetInstance: + """ + Asynchronously create the FleetInstance + + :param friendly_name: Provides a human readable descriptive text for this Fleet, up to 256 characters long. + + :returns: The created FleetInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FleetInstance]: + """ + Streams FleetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FleetInstance]: + """ + Asynchronously streams FleetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FleetInstance]: + """ + Lists FleetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FleetInstance]: + """ + Asynchronously lists FleetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FleetPage: + """ + Retrieve a single page of FleetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FleetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FleetPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FleetPage: + """ + Asynchronously retrieve a single page of FleetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FleetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FleetPage(self._version, response) + + def get_page(self, target_url: str) -> FleetPage: + """ + Retrieve a specific page of FleetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FleetInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FleetPage(self._version, response) + + async def get_page_async(self, target_url: str) -> FleetPage: + """ + Asynchronously retrieve a specific page of FleetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FleetInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FleetPage(self._version, response) + + def get(self, sid: str) -> FleetContext: + """ + Constructs a FleetContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Fleet resource. + """ + return FleetContext(self._version, sid=sid) + + def __call__(self, sid: str) -> FleetContext: + """ + Constructs a FleetContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Fleet resource. + """ + return FleetContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..96218b2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/certificate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/certificate.cpython-310.pyc new file mode 100644 index 0000000..ee517f3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/certificate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/deployment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/deployment.cpython-310.pyc new file mode 100644 index 0000000..7ad3324 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/deployment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/device.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/device.cpython-310.pyc new file mode 100644 index 0000000..3803361 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/device.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/key.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/key.cpython-310.pyc new file mode 100644 index 0000000..9c0b1c6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/__pycache__/key.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/certificate.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/certificate.py new file mode 100644 index 0000000..1511a06 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/certificate.py @@ -0,0 +1,658 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CertificateInstance(InstanceResource): + """ + :ivar sid: Contains a 34 character string that uniquely identifies this Certificate credential resource. + :ivar url: Contains an absolute URL for this Certificate credential resource. + :ivar friendly_name: Contains a human readable descriptive text for this Certificate credential, up to 256 characters long. + :ivar fleet_sid: Specifies the unique string identifier of the Fleet that the given Certificate credential belongs to. + :ivar account_sid: Specifies the unique string identifier of the Account responsible for this Certificate credential. + :ivar device_sid: Specifies the unique string identifier of a Device authenticated with this Certificate credential. + :ivar thumbprint: Contains a unique hash of the payload of this Certificate credential, used to authenticate the Device. + :ivar date_created: Specifies the date this Certificate credential was created, given in UTC ISO 8601 format. + :ivar date_updated: Specifies the date this Certificate credential was last updated, given in UTC ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + fleet_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.device_sid: Optional[str] = payload.get("device_sid") + self.thumbprint: Optional[str] = payload.get("thumbprint") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid or self.sid, + } + self._context: Optional[CertificateContext] = None + + @property + def _proxy(self) -> "CertificateContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CertificateContext for this CertificateInstance + """ + if self._context is None: + self._context = CertificateContext( + self._version, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CertificateInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CertificateInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CertificateInstance": + """ + Fetch the CertificateInstance + + + :returns: The fetched CertificateInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CertificateInstance": + """ + Asynchronous coroutine to fetch the CertificateInstance + + + :returns: The fetched CertificateInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> "CertificateInstance": + """ + Update the CertificateInstance + + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The updated CertificateInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + device_sid=device_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> "CertificateInstance": + """ + Asynchronous coroutine to update the CertificateInstance + + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The updated CertificateInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + device_sid=device_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CertificateContext(InstanceContext): + + def __init__(self, version: Version, fleet_sid: str, sid: str): + """ + Initialize the CertificateContext + + :param version: Version that contains the resource + :param fleet_sid: + :param sid: Provides a 34 character string that uniquely identifies the requested Certificate credential resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid, + } + self._uri = "/Fleets/{fleet_sid}/Certificates/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CertificateInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CertificateInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CertificateInstance: + """ + Fetch the CertificateInstance + + + :returns: The fetched CertificateInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CertificateInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CertificateInstance: + """ + Asynchronous coroutine to fetch the CertificateInstance + + + :returns: The fetched CertificateInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CertificateInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> CertificateInstance: + """ + Update the CertificateInstance + + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The updated CertificateInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CertificateInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> CertificateInstance: + """ + Asynchronous coroutine to update the CertificateInstance + + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The updated CertificateInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CertificateInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CertificatePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CertificateInstance: + """ + Build an instance of CertificateInstance + + :param payload: Payload response from the API + """ + return CertificateInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CertificateList(ListResource): + + def __init__(self, version: Version, fleet_sid: str): + """ + Initialize the CertificateList + + :param version: Version that contains the resource + :param fleet_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + } + self._uri = "/Fleets/{fleet_sid}/Certificates".format(**self._solution) + + def create( + self, + certificate_data: str, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> CertificateInstance: + """ + Create the CertificateInstance + + :param certificate_data: Provides a URL encoded representation of the public certificate in PEM format. + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The created CertificateInstance + """ + + data = values.of( + { + "CertificateData": certificate_data, + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CertificateInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + async def create_async( + self, + certificate_data: str, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> CertificateInstance: + """ + Asynchronously create the CertificateInstance + + :param certificate_data: Provides a URL encoded representation of the public certificate in PEM format. + :param friendly_name: Provides a human readable descriptive text for this Certificate credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Certificate credential. + + :returns: The created CertificateInstance + """ + + data = values.of( + { + "CertificateData": certificate_data, + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CertificateInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def stream( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CertificateInstance]: + """ + Streams CertificateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(device_sid=device_sid, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CertificateInstance]: + """ + Asynchronously streams CertificateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + device_sid=device_sid, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CertificateInstance]: + """ + Lists CertificateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + device_sid=device_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CertificateInstance]: + """ + Asynchronously lists CertificateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + device_sid=device_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + device_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CertificatePage: + """ + Retrieve a single page of CertificateInstance records from the API. + Request is executed immediately + + :param device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CertificateInstance + """ + data = values.of( + { + "DeviceSid": device_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CertificatePage(self._version, response, self._solution) + + async def page_async( + self, + device_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CertificatePage: + """ + Asynchronously retrieve a single page of CertificateInstance records from the API. + Request is executed immediately + + :param device_sid: Filters the resulting list of Certificates by a unique string identifier of an authenticated Device. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CertificateInstance + """ + data = values.of( + { + "DeviceSid": device_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CertificatePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CertificatePage: + """ + Retrieve a specific page of CertificateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CertificateInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CertificatePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CertificatePage: + """ + Asynchronously retrieve a specific page of CertificateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CertificateInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CertificatePage(self._version, response, self._solution) + + def get(self, sid: str) -> CertificateContext: + """ + Constructs a CertificateContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Certificate credential resource. + """ + return CertificateContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __call__(self, sid: str) -> CertificateContext: + """ + Constructs a CertificateContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Certificate credential resource. + """ + return CertificateContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/deployment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/deployment.py new file mode 100644 index 0000000..5c0d109 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/deployment.py @@ -0,0 +1,632 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeploymentInstance(InstanceResource): + """ + :ivar sid: Contains a 34 character string that uniquely identifies this Deployment resource. + :ivar url: Contains an absolute URL for this Deployment resource. + :ivar friendly_name: Contains a human readable descriptive text for this Deployment, up to 64 characters long + :ivar fleet_sid: Specifies the unique string identifier of the Fleet that the given Deployment belongs to. + :ivar account_sid: Specifies the unique string identifier of the Account responsible for this Deployment. + :ivar sync_service_sid: Specifies the unique string identifier of the Twilio Sync service instance linked to and accessible by this Deployment. + :ivar date_created: Specifies the date this Deployment was created, given in UTC ISO 8601 format. + :ivar date_updated: Specifies the date this Deployment was last updated, given in UTC ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + fleet_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.sync_service_sid: Optional[str] = payload.get("sync_service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid or self.sid, + } + self._context: Optional[DeploymentContext] = None + + @property + def _proxy(self) -> "DeploymentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeploymentContext for this DeploymentInstance + """ + if self._context is None: + self._context = DeploymentContext( + self._version, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DeploymentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeploymentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DeploymentInstance": + """ + Fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeploymentInstance": + """ + Asynchronous coroutine to fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> "DeploymentInstance": + """ + Update the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 64 characters long + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The updated DeploymentInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + sync_service_sid=sync_service_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> "DeploymentInstance": + """ + Asynchronous coroutine to update the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 64 characters long + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The updated DeploymentInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + sync_service_sid=sync_service_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeploymentContext(InstanceContext): + + def __init__(self, version: Version, fleet_sid: str, sid: str): + """ + Initialize the DeploymentContext + + :param version: Version that contains the resource + :param fleet_sid: + :param sid: Provides a 34 character string that uniquely identifies the requested Deployment resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid, + } + self._uri = "/Fleets/{fleet_sid}/Deployments/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the DeploymentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeploymentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DeploymentInstance: + """ + Fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeploymentInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeploymentInstance: + """ + Asynchronous coroutine to fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeploymentInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> DeploymentInstance: + """ + Update the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 64 characters long + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The updated DeploymentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "SyncServiceSid": sync_service_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> DeploymentInstance: + """ + Asynchronous coroutine to update the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 64 characters long + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The updated DeploymentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "SyncServiceSid": sync_service_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeploymentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeploymentInstance: + """ + Build an instance of DeploymentInstance + + :param payload: Payload response from the API + """ + return DeploymentInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeploymentList(ListResource): + + def __init__(self, version: Version, fleet_sid: str): + """ + Initialize the DeploymentList + + :param version: Version that contains the resource + :param fleet_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + } + self._uri = "/Fleets/{fleet_sid}/Deployments".format(**self._solution) + + def create( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> DeploymentInstance: + """ + Create the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 256 characters long. + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The created DeploymentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "SyncServiceSid": sync_service_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + sync_service_sid: Union[str, object] = values.unset, + ) -> DeploymentInstance: + """ + Asynchronously create the DeploymentInstance + + :param friendly_name: Provides a human readable descriptive text for this Deployment, up to 256 characters long. + :param sync_service_sid: Provides the unique string identifier of the Twilio Sync service instance that will be linked to and accessible by this Deployment. + + :returns: The created DeploymentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "SyncServiceSid": sync_service_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeploymentInstance]: + """ + Streams DeploymentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeploymentInstance]: + """ + Asynchronously streams DeploymentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeploymentInstance]: + """ + Lists DeploymentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeploymentInstance]: + """ + Asynchronously lists DeploymentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeploymentPage: + """ + Retrieve a single page of DeploymentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeploymentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeploymentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeploymentPage: + """ + Asynchronously retrieve a single page of DeploymentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeploymentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeploymentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeploymentPage: + """ + Retrieve a specific page of DeploymentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeploymentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeploymentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeploymentPage: + """ + Asynchronously retrieve a specific page of DeploymentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeploymentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeploymentPage(self._version, response, self._solution) + + def get(self, sid: str) -> DeploymentContext: + """ + Constructs a DeploymentContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Deployment resource. + """ + return DeploymentContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __call__(self, sid: str) -> DeploymentContext: + """ + Constructs a DeploymentContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Deployment resource. + """ + return DeploymentContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/device.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/device.py new file mode 100644 index 0000000..7e9b818 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/device.py @@ -0,0 +1,702 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeviceInstance(InstanceResource): + """ + :ivar sid: Contains a 34 character string that uniquely identifies this Device resource. + :ivar url: Contains an absolute URL for this Device resource. + :ivar unique_name: Contains a unique and addressable name of this Device, assigned by the developer, up to 128 characters long. + :ivar friendly_name: Contains a human readable descriptive text for this Device, up to 256 characters long + :ivar fleet_sid: Specifies the unique string identifier of the Fleet that the given Device belongs to. + :ivar enabled: Contains a boolean flag indicating whether the device is enabled or not, blocks device connectivity if set to false. + :ivar account_sid: Specifies the unique string identifier of the Account responsible for this Device. + :ivar identity: Contains an arbitrary string identifier representing a human user associated with this Device, assigned by the developer, up to 256 characters long. + :ivar deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is associated with. + :ivar date_created: Specifies the date this Device was created, given in UTC ISO 8601 format. + :ivar date_updated: Specifies the date this Device was last updated, given in UTC ISO 8601 format. + :ivar date_authenticated: Specifies the date this Device was last authenticated, given in UTC ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + fleet_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.unique_name: Optional[str] = payload.get("unique_name") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.enabled: Optional[bool] = payload.get("enabled") + self.account_sid: Optional[str] = payload.get("account_sid") + self.identity: Optional[str] = payload.get("identity") + self.deployment_sid: Optional[str] = payload.get("deployment_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.date_authenticated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_authenticated") + ) + + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid or self.sid, + } + self._context: Optional[DeviceContext] = None + + @property + def _proxy(self) -> "DeviceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeviceContext for this DeviceInstance + """ + if self._context is None: + self._context = DeviceContext( + self._version, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DeviceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DeviceInstance": + """ + Fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeviceInstance": + """ + Asynchronous coroutine to fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> "DeviceInstance": + """ + Update the DeviceInstance + + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The updated DeviceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + identity=identity, + deployment_sid=deployment_sid, + enabled=enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> "DeviceInstance": + """ + Asynchronous coroutine to update the DeviceInstance + + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The updated DeviceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + identity=identity, + deployment_sid=deployment_sid, + enabled=enabled, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeviceContext(InstanceContext): + + def __init__(self, version: Version, fleet_sid: str, sid: str): + """ + Initialize the DeviceContext + + :param version: Version that contains the resource + :param fleet_sid: + :param sid: Provides a 34 character string that uniquely identifies the requested Device resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid, + } + self._uri = "/Fleets/{fleet_sid}/Devices/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the DeviceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DeviceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DeviceInstance: + """ + Fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeviceInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeviceInstance: + """ + Asynchronous coroutine to fetch the DeviceInstance + + + :returns: The fetched DeviceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeviceInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Update the DeviceInstance + + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The updated DeviceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Identity": identity, + "DeploymentSid": deployment_sid, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Asynchronous coroutine to update the DeviceInstance + + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The updated DeviceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Identity": identity, + "DeploymentSid": deployment_sid, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DevicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeviceInstance: + """ + Build an instance of DeviceInstance + + :param payload: Payload response from the API + """ + return DeviceInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeviceList(ListResource): + + def __init__(self, version: Version, fleet_sid: str): + """ + Initialize the DeviceList + + :param version: Version that contains the resource + :param fleet_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + } + self._uri = "/Fleets/{fleet_sid}/Devices".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Create the DeviceInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Device, to be used in addition to SID, up to 128 characters long. + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The created DeviceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "Identity": identity, + "DeploymentSid": deployment_sid, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + identity: Union[str, object] = values.unset, + deployment_sid: Union[str, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> DeviceInstance: + """ + Asynchronously create the DeviceInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Device, to be used in addition to SID, up to 128 characters long. + :param friendly_name: Provides a human readable descriptive text to be assigned to this Device, up to 256 characters long. + :param identity: Provides an arbitrary string identifier representing a human user to be associated with this Device, up to 256 characters long. + :param deployment_sid: Specifies the unique string identifier of the Deployment group that this Device is going to be associated with. + :param enabled: + + :returns: The created DeviceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "Identity": identity, + "DeploymentSid": deployment_sid, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeviceInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def stream( + self, + deployment_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeviceInstance]: + """ + Streams DeviceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(deployment_sid=deployment_sid, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + deployment_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeviceInstance]: + """ + Asynchronously streams DeviceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + deployment_sid=deployment_sid, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + deployment_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceInstance]: + """ + Lists DeviceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + deployment_sid=deployment_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + deployment_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeviceInstance]: + """ + Asynchronously lists DeviceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + deployment_sid=deployment_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + deployment_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DevicePage: + """ + Retrieve a single page of DeviceInstance records from the API. + Request is executed immediately + + :param deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceInstance + """ + data = values.of( + { + "DeploymentSid": deployment_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DevicePage(self._version, response, self._solution) + + async def page_async( + self, + deployment_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DevicePage: + """ + Asynchronously retrieve a single page of DeviceInstance records from the API. + Request is executed immediately + + :param deployment_sid: Filters the resulting list of Devices by a unique string identifier of the Deployment they are associated with. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeviceInstance + """ + data = values.of( + { + "DeploymentSid": deployment_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DevicePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DevicePage: + """ + Retrieve a specific page of DeviceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DevicePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DevicePage: + """ + Asynchronously retrieve a specific page of DeviceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeviceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DevicePage(self._version, response, self._solution) + + def get(self, sid: str) -> DeviceContext: + """ + Constructs a DeviceContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Device resource. + """ + return DeviceContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __call__(self, sid: str) -> DeviceContext: + """ + Constructs a DeviceContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Device resource. + """ + return DeviceContext( + self._version, fleet_sid=self._solution["fleet_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/key.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/key.py new file mode 100644 index 0000000..eabfc45 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/deployed_devices/fleet/key.py @@ -0,0 +1,648 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class KeyInstance(InstanceResource): + """ + :ivar sid: Contains a 34 character string that uniquely identifies this Key credential resource. + :ivar url: Contains an absolute URL for this Key credential resource. + :ivar friendly_name: Contains a human readable descriptive text for this Key credential, up to 256 characters long. + :ivar fleet_sid: Specifies the unique string identifier of the Fleet that the given Key credential belongs to. + :ivar account_sid: Specifies the unique string identifier of the Account responsible for this Key credential. + :ivar device_sid: Specifies the unique string identifier of a Device authenticated with this Key credential. + :ivar secret: Contains the automatically generated secret belonging to this Key credential, used to authenticate the Device. + :ivar date_created: Specifies the date this Key credential was created, given in UTC ISO 8601 format. + :ivar date_updated: Specifies the date this Key credential was last updated, given in UTC ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + fleet_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.device_sid: Optional[str] = payload.get("device_sid") + self.secret: Optional[str] = payload.get("secret") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid or self.sid, + } + self._context: Optional[KeyContext] = None + + @property + def _proxy(self) -> "KeyContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: KeyContext for this KeyInstance + """ + if self._context is None: + self._context = KeyContext( + self._version, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "KeyInstance": + """ + Fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "KeyInstance": + """ + Asynchronous coroutine to fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> "KeyInstance": + """ + Update the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The updated KeyInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + device_sid=device_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> "KeyInstance": + """ + Asynchronous coroutine to update the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The updated KeyInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + device_sid=device_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class KeyContext(InstanceContext): + + def __init__(self, version: Version, fleet_sid: str, sid: str): + """ + Initialize the KeyContext + + :param version: Version that contains the resource + :param fleet_sid: + :param sid: Provides a 34 character string that uniquely identifies the requested Key credential resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + "sid": sid, + } + self._uri = "/Fleets/{fleet_sid}/Keys/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the KeyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> KeyInstance: + """ + Fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return KeyInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> KeyInstance: + """ + Asynchronous coroutine to fetch the KeyInstance + + + :returns: The fetched KeyInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return KeyInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> KeyInstance: + """ + Update the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The updated KeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> KeyInstance: + """ + Asynchronous coroutine to update the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The updated KeyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, + payload, + fleet_sid=self._solution["fleet_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class KeyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> KeyInstance: + """ + Build an instance of KeyInstance + + :param payload: Payload response from the API + """ + return KeyInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class KeyList(ListResource): + + def __init__(self, version: Version, fleet_sid: str): + """ + Initialize the KeyList + + :param version: Version that contains the resource + :param fleet_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "fleet_sid": fleet_sid, + } + self._uri = "/Fleets/{fleet_sid}/Keys".format(**self._solution) + + def create( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> KeyInstance: + """ + Create the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The created KeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + device_sid: Union[str, object] = values.unset, + ) -> KeyInstance: + """ + Asynchronously create the KeyInstance + + :param friendly_name: Provides a human readable descriptive text for this Key credential, up to 256 characters long. + :param device_sid: Provides the unique string identifier of an existing Device to become authenticated with this Key credential. + + :returns: The created KeyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "DeviceSid": device_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return KeyInstance( + self._version, payload, fleet_sid=self._solution["fleet_sid"] + ) + + def stream( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[KeyInstance]: + """ + Streams KeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(device_sid=device_sid, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[KeyInstance]: + """ + Asynchronously streams KeyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + device_sid=device_sid, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[KeyInstance]: + """ + Lists KeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + device_sid=device_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + device_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[KeyInstance]: + """ + Asynchronously lists KeyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + device_sid=device_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + device_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> KeyPage: + """ + Retrieve a single page of KeyInstance records from the API. + Request is executed immediately + + :param device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of KeyInstance + """ + data = values.of( + { + "DeviceSid": device_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return KeyPage(self._version, response, self._solution) + + async def page_async( + self, + device_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> KeyPage: + """ + Asynchronously retrieve a single page of KeyInstance records from the API. + Request is executed immediately + + :param device_sid: Filters the resulting list of Keys by a unique string identifier of an authenticated Device. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of KeyInstance + """ + data = values.of( + { + "DeviceSid": device_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return KeyPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> KeyPage: + """ + Retrieve a specific page of KeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of KeyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return KeyPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> KeyPage: + """ + Asynchronously retrieve a specific page of KeyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of KeyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return KeyPage(self._version, response, self._solution) + + def get(self, sid: str) -> KeyContext: + """ + Constructs a KeyContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Key credential resource. + """ + return KeyContext(self._version, fleet_sid=self._solution["fleet_sid"], sid=sid) + + def __call__(self, sid: str) -> KeyContext: + """ + Constructs a KeyContext + + :param sid: Provides a 34 character string that uniquely identifies the requested Key credential resource. + """ + return KeyContext(self._version, fleet_sid=self._solution["fleet_sid"], sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__init__.py new file mode 100644 index 0000000..7b3a767 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__init__.py @@ -0,0 +1,53 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview.hosted_numbers.authorization_document import ( + AuthorizationDocumentList, +) +from twilio.rest.preview.hosted_numbers.hosted_number_order import HostedNumberOrderList + + +class HostedNumbers(Version): + + def __init__(self, domain: Domain): + """ + Initialize the HostedNumbers version of Preview + + :param domain: The Twilio.preview domain + """ + super().__init__(domain, "HostedNumbers") + self._authorization_documents: Optional[AuthorizationDocumentList] = None + self._hosted_number_orders: Optional[HostedNumberOrderList] = None + + @property + def authorization_documents(self) -> AuthorizationDocumentList: + if self._authorization_documents is None: + self._authorization_documents = AuthorizationDocumentList(self) + return self._authorization_documents + + @property + def hosted_number_orders(self) -> HostedNumberOrderList: + if self._hosted_number_orders is None: + self._hosted_number_orders = HostedNumberOrderList(self) + return self._hosted_number_orders + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6e1fe12 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/hosted_number_order.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/hosted_number_order.cpython-310.pyc new file mode 100644 index 0000000..88a2401 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/__pycache__/hosted_number_order.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__init__.py new file mode 100644 index 0000000..162d0b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__init__.py @@ -0,0 +1,727 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.hosted_numbers.authorization_document.dependent_hosted_number_order import ( + DependentHostedNumberOrderList, +) + + +class AuthorizationDocumentInstance(InstanceResource): + + class Status(object): + OPENED = "opened" + SIGNING = "signing" + SIGNED = "signed" + CANCELED = "canceled" + FAILED = "failed" + + """ + :ivar sid: A 34 character string that uniquely identifies this AuthorizationDocument. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :ivar status: + :ivar email: Email that this AuthorizationDocument will be sent to for signing. + :ivar cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.address_sid: Optional[str] = payload.get("address_sid") + self.status: Optional["AuthorizationDocumentInstance.Status"] = payload.get( + "status" + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AuthorizationDocumentContext] = None + + @property + def _proxy(self) -> "AuthorizationDocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AuthorizationDocumentContext for this AuthorizationDocumentInstance + """ + if self._context is None: + self._context = AuthorizationDocumentContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AuthorizationDocumentInstance": + """ + Fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AuthorizationDocumentInstance": + """ + Asynchronous coroutine to fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + hosted_number_order_sids: Union[List[str], object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + contact_title: Union[str, object] = values.unset, + contact_phone_number: Union[str, object] = values.unset, + ) -> "AuthorizationDocumentInstance": + """ + Update the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :param status: + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + + :returns: The updated AuthorizationDocumentInstance + """ + return self._proxy.update( + hosted_number_order_sids=hosted_number_order_sids, + address_sid=address_sid, + email=email, + cc_emails=cc_emails, + status=status, + contact_title=contact_title, + contact_phone_number=contact_phone_number, + ) + + async def update_async( + self, + hosted_number_order_sids: Union[List[str], object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + contact_title: Union[str, object] = values.unset, + contact_phone_number: Union[str, object] = values.unset, + ) -> "AuthorizationDocumentInstance": + """ + Asynchronous coroutine to update the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :param status: + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + + :returns: The updated AuthorizationDocumentInstance + """ + return await self._proxy.update_async( + hosted_number_order_sids=hosted_number_order_sids, + address_sid=address_sid, + email=email, + cc_emails=cc_emails, + status=status, + contact_title=contact_title, + contact_phone_number=contact_phone_number, + ) + + @property + def dependent_hosted_number_orders(self) -> DependentHostedNumberOrderList: + """ + Access the dependent_hosted_number_orders + """ + return self._proxy.dependent_hosted_number_orders + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthorizationDocumentContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AuthorizationDocumentContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/AuthorizationDocuments/{sid}".format(**self._solution) + + self._dependent_hosted_number_orders: Optional[ + DependentHostedNumberOrderList + ] = None + + def fetch(self) -> AuthorizationDocumentInstance: + """ + Fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AuthorizationDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AuthorizationDocumentInstance: + """ + Asynchronous coroutine to fetch the AuthorizationDocumentInstance + + + :returns: The fetched AuthorizationDocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AuthorizationDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + hosted_number_order_sids: Union[List[str], object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + contact_title: Union[str, object] = values.unset, + contact_phone_number: Union[str, object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Update the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :param status: + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + + :returns: The updated AuthorizationDocumentInstance + """ + data = values.of( + { + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "AddressSid": address_sid, + "Email": email, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "Status": status, + "ContactTitle": contact_title, + "ContactPhoneNumber": contact_phone_number, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + hosted_number_order_sids: Union[List[str], object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + contact_title: Union[str, object] = values.unset, + contact_phone_number: Union[str, object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Asynchronous coroutine to update the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :param status: + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + + :returns: The updated AuthorizationDocumentInstance + """ + data = values.of( + { + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "AddressSid": address_sid, + "Email": email, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "Status": status, + "ContactTitle": contact_title, + "ContactPhoneNumber": contact_phone_number, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + @property + def dependent_hosted_number_orders(self) -> DependentHostedNumberOrderList: + """ + Access the dependent_hosted_number_orders + """ + if self._dependent_hosted_number_orders is None: + self._dependent_hosted_number_orders = DependentHostedNumberOrderList( + self._version, + self._solution["sid"], + ) + return self._dependent_hosted_number_orders + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AuthorizationDocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AuthorizationDocumentInstance: + """ + Build an instance of AuthorizationDocumentInstance + + :param payload: Payload response from the API + """ + return AuthorizationDocumentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AuthorizationDocumentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AuthorizationDocumentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/AuthorizationDocuments" + + def create( + self, + hosted_number_order_sids: List[str], + address_sid: str, + email: str, + contact_title: str, + contact_phone_number: str, + cc_emails: Union[List[str], object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Create the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + + :returns: The created AuthorizationDocumentInstance + """ + + data = values.of( + { + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "AddressSid": address_sid, + "Email": email, + "ContactTitle": contact_title, + "ContactPhoneNumber": contact_phone_number, + "CcEmails": serialize.map(cc_emails, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance(self._version, payload) + + async def create_async( + self, + hosted_number_order_sids: List[str], + address_sid: str, + email: str, + contact_title: str, + contact_phone_number: str, + cc_emails: Union[List[str], object] = values.unset, + ) -> AuthorizationDocumentInstance: + """ + Asynchronously create the AuthorizationDocumentInstance + + :param hosted_number_order_sids: A list of HostedNumberOrder sids that this AuthorizationDocument will authorize for hosting phone number capabilities on Twilio's platform. + :param address_sid: A 34 character string that uniquely identifies the Address resource that is associated with this AuthorizationDocument. + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param contact_title: The title of the person authorized to sign the Authorization Document for this phone number. + :param contact_phone_number: The contact phone number of the person authorized to sign the Authorization Document. + :param cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed. + + :returns: The created AuthorizationDocumentInstance + """ + + data = values.of( + { + "HostedNumberOrderSids": serialize.map( + hosted_number_order_sids, lambda e: e + ), + "AddressSid": address_sid, + "Email": email, + "ContactTitle": contact_title, + "ContactPhoneNumber": contact_phone_number, + "CcEmails": serialize.map(cc_emails, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AuthorizationDocumentInstance(self._version, payload) + + def stream( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AuthorizationDocumentInstance]: + """ + Streams AuthorizationDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(email=email, status=status, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AuthorizationDocumentInstance]: + """ + Asynchronously streams AuthorizationDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + email=email, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizationDocumentInstance]: + """ + Lists AuthorizationDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + email=email, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AuthorizationDocumentInstance]: + """ + Asynchronously lists AuthorizationDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str email: Email that this AuthorizationDocument will be sent to for signing. + :param "AuthorizationDocumentInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + email=email, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizationDocumentPage: + """ + Retrieve a single page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizationDocumentInstance + """ + data = values.of( + { + "Email": email, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AuthorizationDocumentPage(self._version, response) + + async def page_async( + self, + email: Union[str, object] = values.unset, + status: Union["AuthorizationDocumentInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AuthorizationDocumentPage: + """ + Asynchronously retrieve a single page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param email: Email that this AuthorizationDocument will be sent to for signing. + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AuthorizationDocumentInstance + """ + data = values.of( + { + "Email": email, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AuthorizationDocumentPage(self._version, response) + + def get_page(self, target_url: str) -> AuthorizationDocumentPage: + """ + Retrieve a specific page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizationDocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AuthorizationDocumentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AuthorizationDocumentPage: + """ + Asynchronously retrieve a specific page of AuthorizationDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AuthorizationDocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AuthorizationDocumentPage(self._version, response) + + def get(self, sid: str) -> AuthorizationDocumentContext: + """ + Constructs a AuthorizationDocumentContext + + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + return AuthorizationDocumentContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AuthorizationDocumentContext: + """ + Constructs a AuthorizationDocumentContext + + :param sid: A 34 character string that uniquely identifies this AuthorizationDocument. + """ + return AuthorizationDocumentContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ea9263b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc new file mode 100644 index 0000000..02f21bc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/__pycache__/dependent_hosted_number_order.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/dependent_hosted_number_order.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/dependent_hosted_number_order.py new file mode 100644 index 0000000..35b67d7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/authorization_document/dependent_hosted_number_order.py @@ -0,0 +1,467 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DependentHostedNumberOrderInstance(InstanceResource): + + class Status(object): + RECEIVED = "received" + PENDING_VERIFICATION = "pending-verification" + VERIFIED = "verified" + PENDING_LOA = "pending-loa" + CARRIER_PROCESSING = "carrier-processing" + TESTING = "testing" + COMPLETED = "completed" + FAILED = "failed" + ACTION_REQUIRED = "action-required" + + class VerificationType(object): + PHONE_CALL = "phone-call" + PHONE_BILL = "phone-bill" + + """ + :ivar sid: A 34 character string that uniquely identifies this Authorization Document + :ivar account_sid: The unique SID identifier of the Account. + :ivar incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :ivar signing_document_sid: A 34 character string that uniquely identifies the LOA document associated with this HostedNumberOrder. + :ivar phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :ivar capabilities: + :ivar friendly_name: A human readable description of this resource, up to 64 characters. + :ivar unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :ivar status: + :ivar failure_reason: A message that explains why a hosted_number_order went to status \"action-required\" + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar verification_attempts: The number of attempts made to verify ownership of the phone number that is being hosted. + :ivar email: Email of the owner of this phone number that is being hosted. + :ivar cc_emails: Email recipients who will be informed when an Authorization Document has been sent and signed + :ivar verification_type: + :ivar verification_document_sid: A 34 character string that uniquely identifies the Identity Document resource that represents the document for verifying ownership of the number to be hosted. + :ivar extension: A numerical extension to be used when making the ownership verification call. + :ivar call_delay: A value between 0-30 specifying the number of seconds to delay initiating the ownership verification call. + :ivar verification_code: The digits passed during the ownership verification call. + :ivar verification_call_sids: A list of 34 character strings that are unique identifiers for the calls placed as part of ownership verification. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], signing_document_sid: str + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.incoming_phone_number_sid: Optional[str] = payload.get( + "incoming_phone_number_sid" + ) + self.address_sid: Optional[str] = payload.get("address_sid") + self.signing_document_sid: Optional[str] = payload.get("signing_document_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.capabilities: Optional[str] = payload.get("capabilities") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.status: Optional["DependentHostedNumberOrderInstance.Status"] = ( + payload.get("status") + ) + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.verification_attempts: Optional[int] = deserialize.integer( + payload.get("verification_attempts") + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.verification_type: Optional[ + "DependentHostedNumberOrderInstance.VerificationType" + ] = payload.get("verification_type") + self.verification_document_sid: Optional[str] = payload.get( + "verification_document_sid" + ) + self.extension: Optional[str] = payload.get("extension") + self.call_delay: Optional[int] = deserialize.integer(payload.get("call_delay")) + self.verification_code: Optional[str] = payload.get("verification_code") + self.verification_call_sids: Optional[List[str]] = payload.get( + "verification_call_sids" + ) + + self._solution = { + "signing_document_sid": signing_document_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class DependentHostedNumberOrderPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> DependentHostedNumberOrderInstance: + """ + Build an instance of DependentHostedNumberOrderInstance + + :param payload: Payload response from the API + """ + return DependentHostedNumberOrderInstance( + self._version, + payload, + signing_document_sid=self._solution["signing_document_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DependentHostedNumberOrderList(ListResource): + + def __init__(self, version: Version, signing_document_sid: str): + """ + Initialize the DependentHostedNumberOrderList + + :param version: Version that contains the resource + :param signing_document_sid: A 34 character string that uniquely identifies the LOA document associated with this HostedNumberOrder. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "signing_document_sid": signing_document_sid, + } + self._uri = "/AuthorizationDocuments/{signing_document_sid}/DependentHostedNumberOrders".format( + **self._solution + ) + + def stream( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DependentHostedNumberOrderInstance]: + """ + Streams DependentHostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DependentHostedNumberOrderInstance]: + """ + Asynchronously streams DependentHostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentHostedNumberOrderInstance]: + """ + Lists DependentHostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DependentHostedNumberOrderInstance]: + """ + Asynchronously lists DependentHostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "DependentHostedNumberOrderInstance.Status" status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentHostedNumberOrderPage: + """ + Retrieve a single page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentHostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + async def page_async( + self, + status: Union[ + "DependentHostedNumberOrderInstance.Status", object + ] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DependentHostedNumberOrderPage: + """ + Asynchronously retrieve a single page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: Status of an instance resource. It can hold one of the values: 1. opened 2. signing, 3. signed LOA, 4. canceled, 5. failed. See the section entitled [Status Values](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource#status-values) for more information on each of these statuses. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DependentHostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DependentHostedNumberOrderPage: + """ + Retrieve a specific page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentHostedNumberOrderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DependentHostedNumberOrderPage: + """ + Asynchronously retrieve a specific page of DependentHostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DependentHostedNumberOrderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DependentHostedNumberOrderPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/hosted_number_order.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/hosted_number_order.py new file mode 100644 index 0000000..7e68f57 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/hosted_numbers/hosted_number_order.py @@ -0,0 +1,955 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class HostedNumberOrderInstance(InstanceResource): + + class Status(object): + RECEIVED = "received" + PENDING_VERIFICATION = "pending-verification" + VERIFIED = "verified" + PENDING_LOA = "pending-loa" + CARRIER_PROCESSING = "carrier-processing" + TESTING = "testing" + COMPLETED = "completed" + FAILED = "failed" + ACTION_REQUIRED = "action-required" + + class VerificationType(object): + PHONE_CALL = "phone-call" + PHONE_BILL = "phone-bill" + + """ + :ivar sid: A 34 character string that uniquely identifies this HostedNumberOrder. + :ivar account_sid: A 34 character string that uniquely identifies the account. + :ivar incoming_phone_number_sid: A 34 character string that uniquely identifies the [IncomingPhoneNumber](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) resource that represents the phone number being hosted. + :ivar address_sid: A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :ivar signing_document_sid: A 34 character string that uniquely identifies the [Authorization Document](https://www.twilio.com/docs/phone-numbers/hosted-numbers/hosted-numbers-api/authorization-document-resource) the user needs to sign. + :ivar phone_number: Phone number to be hosted. This must be in [E.164](https://en.wikipedia.org/wiki/E.164) format, e.g., +16175551212 + :ivar capabilities: + :ivar friendly_name: A 64 character string that is a human-readable text that describes this resource. + :ivar unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :ivar status: + :ivar failure_reason: A message that explains why a hosted_number_order went to status \"action-required\" + :ivar date_created: The date this resource was created, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date that this resource was updated, given as [GMT RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) format. + :ivar verification_attempts: The number of attempts made to verify ownership of the phone number that is being hosted. + :ivar email: Email of the owner of this phone number that is being hosted. + :ivar cc_emails: A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :ivar url: The URL of this HostedNumberOrder. + :ivar verification_type: + :ivar verification_document_sid: A 34 character string that uniquely identifies the Identity Document resource that represents the document for verifying ownership of the number to be hosted. + :ivar extension: A numerical extension to be used when making the ownership verification call. + :ivar call_delay: A value between 0-30 specifying the number of seconds to delay initiating the ownership verification call. + :ivar verification_code: A verification code provided in the response for a user to enter when they pick up the phone call. + :ivar verification_call_sids: A list of 34 character strings that are unique identifiers for the calls placed as part of ownership verification. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.incoming_phone_number_sid: Optional[str] = payload.get( + "incoming_phone_number_sid" + ) + self.address_sid: Optional[str] = payload.get("address_sid") + self.signing_document_sid: Optional[str] = payload.get("signing_document_sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.capabilities: Optional[str] = payload.get("capabilities") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.status: Optional["HostedNumberOrderInstance.Status"] = payload.get( + "status" + ) + self.failure_reason: Optional[str] = payload.get("failure_reason") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.verification_attempts: Optional[int] = deserialize.integer( + payload.get("verification_attempts") + ) + self.email: Optional[str] = payload.get("email") + self.cc_emails: Optional[List[str]] = payload.get("cc_emails") + self.url: Optional[str] = payload.get("url") + self.verification_type: Optional[ + "HostedNumberOrderInstance.VerificationType" + ] = payload.get("verification_type") + self.verification_document_sid: Optional[str] = payload.get( + "verification_document_sid" + ) + self.extension: Optional[str] = payload.get("extension") + self.call_delay: Optional[int] = deserialize.integer(payload.get("call_delay")) + self.verification_code: Optional[str] = payload.get("verification_code") + self.verification_call_sids: Optional[List[str]] = payload.get( + "verification_call_sids" + ) + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[HostedNumberOrderContext] = None + + @property + def _proxy(self) -> "HostedNumberOrderContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: HostedNumberOrderContext for this HostedNumberOrderInstance + """ + if self._context is None: + self._context = HostedNumberOrderContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "HostedNumberOrderInstance": + """ + Fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "HostedNumberOrderInstance": + """ + Asynchronous coroutine to fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + verification_code: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + extension: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + ) -> "HostedNumberOrderInstance": + """ + Update the HostedNumberOrderInstance + + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param email: Email of the owner of this phone number that is being hosted. + :param cc_emails: Optional. A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :param status: + :param verification_code: A verification code that is given to the user via a phone call to the phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + :param extension: Digits to dial after connecting the verification call. + :param call_delay: The number of seconds, between 0 and 60, to delay before initiating the verification call. Defaults to 0. + + :returns: The updated HostedNumberOrderInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + unique_name=unique_name, + email=email, + cc_emails=cc_emails, + status=status, + verification_code=verification_code, + verification_type=verification_type, + verification_document_sid=verification_document_sid, + extension=extension, + call_delay=call_delay, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + verification_code: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + extension: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + ) -> "HostedNumberOrderInstance": + """ + Asynchronous coroutine to update the HostedNumberOrderInstance + + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param email: Email of the owner of this phone number that is being hosted. + :param cc_emails: Optional. A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :param status: + :param verification_code: A verification code that is given to the user via a phone call to the phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + :param extension: Digits to dial after connecting the verification call. + :param call_delay: The number of seconds, between 0 and 60, to delay before initiating the verification call. Defaults to 0. + + :returns: The updated HostedNumberOrderInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + unique_name=unique_name, + email=email, + cc_emails=cc_emails, + status=status, + verification_code=verification_code, + verification_type=verification_type, + verification_document_sid=verification_document_sid, + extension=extension, + call_delay=call_delay, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class HostedNumberOrderContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the HostedNumberOrderContext + + :param version: Version that contains the resource + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/HostedNumberOrders/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the HostedNumberOrderInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> HostedNumberOrderInstance: + """ + Fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return HostedNumberOrderInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> HostedNumberOrderInstance: + """ + Asynchronous coroutine to fetch the HostedNumberOrderInstance + + + :returns: The fetched HostedNumberOrderInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return HostedNumberOrderInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + verification_code: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + extension: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Update the HostedNumberOrderInstance + + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param email: Email of the owner of this phone number that is being hosted. + :param cc_emails: Optional. A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :param status: + :param verification_code: A verification code that is given to the user via a phone call to the phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + :param extension: Digits to dial after connecting the verification call. + :param call_delay: The number of seconds, between 0 and 60, to delay before initiating the verification call. Defaults to 0. + + :returns: The updated HostedNumberOrderInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Email": email, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "Status": status, + "VerificationCode": verification_code, + "VerificationType": verification_type, + "VerificationDocumentSid": verification_document_sid, + "Extension": extension, + "CallDelay": call_delay, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + verification_code: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + extension: Union[str, object] = values.unset, + call_delay: Union[int, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Asynchronous coroutine to update the HostedNumberOrderInstance + + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param email: Email of the owner of this phone number that is being hosted. + :param cc_emails: Optional. A list of emails that LOA document for this HostedNumberOrder will be carbon copied to. + :param status: + :param verification_code: A verification code that is given to the user via a phone call to the phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + :param extension: Digits to dial after connecting the verification call. + :param call_delay: The number of seconds, between 0 and 60, to delay before initiating the verification call. Defaults to 0. + + :returns: The updated HostedNumberOrderInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "Email": email, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "Status": status, + "VerificationCode": verification_code, + "VerificationType": verification_type, + "VerificationDocumentSid": verification_document_sid, + "Extension": extension, + "CallDelay": call_delay, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class HostedNumberOrderPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> HostedNumberOrderInstance: + """ + Build an instance of HostedNumberOrderInstance + + :param payload: Payload response from the API + """ + return HostedNumberOrderInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class HostedNumberOrderList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the HostedNumberOrderList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/HostedNumberOrders" + + def create( + self, + phone_number: str, + sms_capability: bool, + account_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Create the HostedNumberOrderInstance + + :param phone_number: The number to host in [+E.164](https://en.wikipedia.org/wiki/E.164) format + :param sms_capability: Used to specify that the SMS capability will be hosted on Twilio's platform. + :param account_sid: This defaults to the AccountSid of the authorization the user is using. This can be provided to specify a subaccount to add the HostedNumberOrder to. + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Optional. Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param cc_emails: Optional. A list of emails that the LOA document for this HostedNumberOrder will be carbon copied to. + :param sms_url: The URL that Twilio should request when somebody sends an SMS to the phone number. This will be copied onto the IncomingPhoneNumber resource. + :param sms_method: The HTTP method that should be used to request the SmsUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_url: A URL that Twilio will request if an error occurs requesting or executing the TwiML defined by SmsUrl. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_method: The HTTP method that should be used to request the SmsFallbackUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param status_callback_url: Optional. The Status Callback URL attached to the IncomingPhoneNumber resource. + :param status_callback_method: Optional. The Status Callback Method attached to the IncomingPhoneNumber resource. + :param sms_application_sid: Optional. The 34 character sid of the application Twilio should use to handle SMS messages sent to this number. If a `SmsApplicationSid` is present, Twilio will ignore all of the SMS urls above and use those set on the application. + :param address_sid: Optional. A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :param email: Optional. Email of the owner of this phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + + :returns: The created HostedNumberOrderInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "AccountSid": account_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "SmsApplicationSid": sms_application_sid, + "AddressSid": address_sid, + "Email": email, + "VerificationType": verification_type, + "VerificationDocumentSid": verification_document_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance(self._version, payload) + + async def create_async( + self, + phone_number: str, + sms_capability: bool, + account_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + cc_emails: Union[List[str], object] = values.unset, + sms_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + sms_application_sid: Union[str, object] = values.unset, + address_sid: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + verification_type: Union[ + "HostedNumberOrderInstance.VerificationType", object + ] = values.unset, + verification_document_sid: Union[str, object] = values.unset, + ) -> HostedNumberOrderInstance: + """ + Asynchronously create the HostedNumberOrderInstance + + :param phone_number: The number to host in [+E.164](https://en.wikipedia.org/wiki/E.164) format + :param sms_capability: Used to specify that the SMS capability will be hosted on Twilio's platform. + :param account_sid: This defaults to the AccountSid of the authorization the user is using. This can be provided to specify a subaccount to add the HostedNumberOrder to. + :param friendly_name: A 64 character string that is a human readable text that describes this resource. + :param unique_name: Optional. Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param cc_emails: Optional. A list of emails that the LOA document for this HostedNumberOrder will be carbon copied to. + :param sms_url: The URL that Twilio should request when somebody sends an SMS to the phone number. This will be copied onto the IncomingPhoneNumber resource. + :param sms_method: The HTTP method that should be used to request the SmsUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_url: A URL that Twilio will request if an error occurs requesting or executing the TwiML defined by SmsUrl. This will be copied onto the IncomingPhoneNumber resource. + :param sms_fallback_method: The HTTP method that should be used to request the SmsFallbackUrl. Must be either `GET` or `POST`. This will be copied onto the IncomingPhoneNumber resource. + :param status_callback_url: Optional. The Status Callback URL attached to the IncomingPhoneNumber resource. + :param status_callback_method: Optional. The Status Callback Method attached to the IncomingPhoneNumber resource. + :param sms_application_sid: Optional. The 34 character sid of the application Twilio should use to handle SMS messages sent to this number. If a `SmsApplicationSid` is present, Twilio will ignore all of the SMS urls above and use those set on the application. + :param address_sid: Optional. A 34 character string that uniquely identifies the Address resource that represents the address of the owner of this phone number. + :param email: Optional. Email of the owner of this phone number that is being hosted. + :param verification_type: + :param verification_document_sid: Optional. The unique sid identifier of the Identity Document that represents the document for verifying ownership of the number to be hosted. Required when VerificationType is phone-bill. + + :returns: The created HostedNumberOrderInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + "SmsCapability": serialize.boolean_to_string(sms_capability), + "AccountSid": account_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "CcEmails": serialize.map(cc_emails, lambda e: e), + "SmsUrl": sms_url, + "SmsMethod": sms_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsFallbackMethod": sms_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "SmsApplicationSid": sms_application_sid, + "AddressSid": address_sid, + "Email": email, + "VerificationType": verification_type, + "VerificationDocumentSid": verification_document_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return HostedNumberOrderInstance(self._version, payload) + + def stream( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[HostedNumberOrderInstance]: + """ + Streams HostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[HostedNumberOrderInstance]: + """ + Asynchronously streams HostedNumberOrderInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HostedNumberOrderInstance]: + """ + Lists HostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HostedNumberOrderInstance]: + """ + Asynchronously lists HostedNumberOrderInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "HostedNumberOrderInstance.Status" status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param str phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param str incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param str friendly_name: A human readable description of this resource, up to 64 characters. + :param str unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + phone_number=phone_number, + incoming_phone_number_sid=incoming_phone_number_sid, + friendly_name=friendly_name, + unique_name=unique_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HostedNumberOrderPage: + """ + Retrieve a single page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return HostedNumberOrderPage(self._version, response) + + async def page_async( + self, + status: Union["HostedNumberOrderInstance.Status", object] = values.unset, + phone_number: Union[str, object] = values.unset, + incoming_phone_number_sid: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + unique_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HostedNumberOrderPage: + """ + Asynchronously retrieve a single page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param status: The Status of this HostedNumberOrder. One of `received`, `pending-verification`, `verified`, `pending-loa`, `carrier-processing`, `testing`, `completed`, `failed`, or `action-required`. + :param phone_number: An E164 formatted phone number hosted by this HostedNumberOrder. + :param incoming_phone_number_sid: A 34 character string that uniquely identifies the IncomingPhoneNumber resource created by this HostedNumberOrder. + :param friendly_name: A human readable description of this resource, up to 64 characters. + :param unique_name: Provides a unique and addressable name to be assigned to this HostedNumberOrder, assigned by the developer, to be optionally used in addition to SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HostedNumberOrderInstance + """ + data = values.of( + { + "Status": status, + "PhoneNumber": phone_number, + "IncomingPhoneNumberSid": incoming_phone_number_sid, + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return HostedNumberOrderPage(self._version, response) + + def get_page(self, target_url: str) -> HostedNumberOrderPage: + """ + Retrieve a specific page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HostedNumberOrderInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return HostedNumberOrderPage(self._version, response) + + async def get_page_async(self, target_url: str) -> HostedNumberOrderPage: + """ + Asynchronously retrieve a specific page of HostedNumberOrderInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HostedNumberOrderInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return HostedNumberOrderPage(self._version, response) + + def get(self, sid: str) -> HostedNumberOrderContext: + """ + Constructs a HostedNumberOrderContext + + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + return HostedNumberOrderContext(self._version, sid=sid) + + def __call__(self, sid: str) -> HostedNumberOrderContext: + """ + Constructs a HostedNumberOrderContext + + :param sid: A 34 character string that uniquely identifies this HostedNumberOrder. + """ + return HostedNumberOrderContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__init__.py new file mode 100644 index 0000000..fcc99a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview.marketplace.available_add_on import AvailableAddOnList +from twilio.rest.preview.marketplace.installed_add_on import InstalledAddOnList + + +class Marketplace(Version): + + def __init__(self, domain: Domain): + """ + Initialize the Marketplace version of Preview + + :param domain: The Twilio.preview domain + """ + super().__init__(domain, "marketplace") + self._available_add_ons: Optional[AvailableAddOnList] = None + self._installed_add_ons: Optional[InstalledAddOnList] = None + + @property + def available_add_ons(self) -> AvailableAddOnList: + if self._available_add_ons is None: + self._available_add_ons = AvailableAddOnList(self) + return self._available_add_ons + + @property + def installed_add_ons(self) -> InstalledAddOnList: + if self._installed_add_ons is None: + self._installed_add_ons = InstalledAddOnList(self) + return self._installed_add_ons + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c793b42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__init__.py new file mode 100644 index 0000000..7743d8a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__init__.py @@ -0,0 +1,424 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.marketplace.available_add_on.available_add_on_extension import ( + AvailableAddOnExtensionList, +) + + +class AvailableAddOnInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the AvailableAddOn resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar description: A short description of the Add-on's functionality. + :ivar pricing_type: How customers are charged for using this Add-on. + :ivar configuration_schema: The JSON object with the configuration that must be provided when installing a given Add-on. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.pricing_type: Optional[str] = payload.get("pricing_type") + self.configuration_schema: Optional[Dict[str, object]] = payload.get( + "configuration_schema" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[AvailableAddOnContext] = None + + @property + def _proxy(self) -> "AvailableAddOnContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AvailableAddOnContext for this AvailableAddOnInstance + """ + if self._context is None: + self._context = AvailableAddOnContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AvailableAddOnInstance": + """ + Fetch the AvailableAddOnInstance + + + :returns: The fetched AvailableAddOnInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AvailableAddOnInstance": + """ + Asynchronous coroutine to fetch the AvailableAddOnInstance + + + :returns: The fetched AvailableAddOnInstance + """ + return await self._proxy.fetch_async() + + @property + def extensions(self) -> AvailableAddOnExtensionList: + """ + Access the extensions + """ + return self._proxy.extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AvailableAddOnContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the AvailableAddOnContext + + :param version: Version that contains the resource + :param sid: The SID of the AvailableAddOn resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/AvailableAddOns/{sid}".format(**self._solution) + + self._extensions: Optional[AvailableAddOnExtensionList] = None + + def fetch(self) -> AvailableAddOnInstance: + """ + Fetch the AvailableAddOnInstance + + + :returns: The fetched AvailableAddOnInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AvailableAddOnInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AvailableAddOnInstance: + """ + Asynchronous coroutine to fetch the AvailableAddOnInstance + + + :returns: The fetched AvailableAddOnInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AvailableAddOnInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def extensions(self) -> AvailableAddOnExtensionList: + """ + Access the extensions + """ + if self._extensions is None: + self._extensions = AvailableAddOnExtensionList( + self._version, + self._solution["sid"], + ) + return self._extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AvailableAddOnPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AvailableAddOnInstance: + """ + Build an instance of AvailableAddOnInstance + + :param payload: Payload response from the API + """ + return AvailableAddOnInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AvailableAddOnList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the AvailableAddOnList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/AvailableAddOns" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AvailableAddOnInstance]: + """ + Streams AvailableAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AvailableAddOnInstance]: + """ + Asynchronously streams AvailableAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailableAddOnInstance]: + """ + Lists AvailableAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailableAddOnInstance]: + """ + Asynchronously lists AvailableAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailableAddOnPage: + """ + Retrieve a single page of AvailableAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailableAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AvailableAddOnPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailableAddOnPage: + """ + Asynchronously retrieve a single page of AvailableAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailableAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AvailableAddOnPage(self._version, response) + + def get_page(self, target_url: str) -> AvailableAddOnPage: + """ + Retrieve a specific page of AvailableAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailableAddOnInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AvailableAddOnPage(self._version, response) + + async def get_page_async(self, target_url: str) -> AvailableAddOnPage: + """ + Asynchronously retrieve a specific page of AvailableAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailableAddOnInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AvailableAddOnPage(self._version, response) + + def get(self, sid: str) -> AvailableAddOnContext: + """ + Constructs a AvailableAddOnContext + + :param sid: The SID of the AvailableAddOn resource to fetch. + """ + return AvailableAddOnContext(self._version, sid=sid) + + def __call__(self, sid: str) -> AvailableAddOnContext: + """ + Constructs a AvailableAddOnContext + + :param sid: The SID of the AvailableAddOn resource to fetch. + """ + return AvailableAddOnContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..487d529 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/available_add_on_extension.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/available_add_on_extension.cpython-310.pyc new file mode 100644 index 0000000..16798dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/__pycache__/available_add_on_extension.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/available_add_on_extension.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/available_add_on_extension.py new file mode 100644 index 0000000..d27b74c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/available_add_on/available_add_on_extension.py @@ -0,0 +1,431 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AvailableAddOnExtensionInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the AvailableAddOnExtension resource. + :ivar available_add_on_sid: The SID of the AvailableAddOn resource to which this extension applies. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar product_name: The name of the Product this Extension is used within. + :ivar unique_name: An application-defined string that uniquely identifies the resource. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + available_add_on_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.available_add_on_sid: Optional[str] = payload.get("available_add_on_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.product_name: Optional[str] = payload.get("product_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "available_add_on_sid": available_add_on_sid, + "sid": sid or self.sid, + } + self._context: Optional[AvailableAddOnExtensionContext] = None + + @property + def _proxy(self) -> "AvailableAddOnExtensionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AvailableAddOnExtensionContext for this AvailableAddOnExtensionInstance + """ + if self._context is None: + self._context = AvailableAddOnExtensionContext( + self._version, + available_add_on_sid=self._solution["available_add_on_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AvailableAddOnExtensionInstance": + """ + Fetch the AvailableAddOnExtensionInstance + + + :returns: The fetched AvailableAddOnExtensionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AvailableAddOnExtensionInstance": + """ + Asynchronous coroutine to fetch the AvailableAddOnExtensionInstance + + + :returns: The fetched AvailableAddOnExtensionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AvailableAddOnExtensionContext(InstanceContext): + + def __init__(self, version: Version, available_add_on_sid: str, sid: str): + """ + Initialize the AvailableAddOnExtensionContext + + :param version: Version that contains the resource + :param available_add_on_sid: The SID of the AvailableAddOn resource with the extension to fetch. + :param sid: The SID of the AvailableAddOn Extension resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "available_add_on_sid": available_add_on_sid, + "sid": sid, + } + self._uri = "/AvailableAddOns/{available_add_on_sid}/Extensions/{sid}".format( + **self._solution + ) + + def fetch(self) -> AvailableAddOnExtensionInstance: + """ + Fetch the AvailableAddOnExtensionInstance + + + :returns: The fetched AvailableAddOnExtensionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AvailableAddOnExtensionInstance( + self._version, + payload, + available_add_on_sid=self._solution["available_add_on_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AvailableAddOnExtensionInstance: + """ + Asynchronous coroutine to fetch the AvailableAddOnExtensionInstance + + + :returns: The fetched AvailableAddOnExtensionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AvailableAddOnExtensionInstance( + self._version, + payload, + available_add_on_sid=self._solution["available_add_on_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class AvailableAddOnExtensionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AvailableAddOnExtensionInstance: + """ + Build an instance of AvailableAddOnExtensionInstance + + :param payload: Payload response from the API + """ + return AvailableAddOnExtensionInstance( + self._version, + payload, + available_add_on_sid=self._solution["available_add_on_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AvailableAddOnExtensionList(ListResource): + + def __init__(self, version: Version, available_add_on_sid: str): + """ + Initialize the AvailableAddOnExtensionList + + :param version: Version that contains the resource + :param available_add_on_sid: The SID of the AvailableAddOn resource with the extensions to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "available_add_on_sid": available_add_on_sid, + } + self._uri = "/AvailableAddOns/{available_add_on_sid}/Extensions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AvailableAddOnExtensionInstance]: + """ + Streams AvailableAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AvailableAddOnExtensionInstance]: + """ + Asynchronously streams AvailableAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailableAddOnExtensionInstance]: + """ + Lists AvailableAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AvailableAddOnExtensionInstance]: + """ + Asynchronously lists AvailableAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailableAddOnExtensionPage: + """ + Retrieve a single page of AvailableAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailableAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AvailableAddOnExtensionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AvailableAddOnExtensionPage: + """ + Asynchronously retrieve a single page of AvailableAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AvailableAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AvailableAddOnExtensionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AvailableAddOnExtensionPage: + """ + Retrieve a specific page of AvailableAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailableAddOnExtensionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AvailableAddOnExtensionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AvailableAddOnExtensionPage: + """ + Asynchronously retrieve a specific page of AvailableAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AvailableAddOnExtensionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AvailableAddOnExtensionPage(self._version, response, self._solution) + + def get(self, sid: str) -> AvailableAddOnExtensionContext: + """ + Constructs a AvailableAddOnExtensionContext + + :param sid: The SID of the AvailableAddOn Extension resource to fetch. + """ + return AvailableAddOnExtensionContext( + self._version, + available_add_on_sid=self._solution["available_add_on_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AvailableAddOnExtensionContext: + """ + Constructs a AvailableAddOnExtensionContext + + :param sid: The SID of the AvailableAddOn Extension resource to fetch. + """ + return AvailableAddOnExtensionContext( + self._version, + available_add_on_sid=self._solution["available_add_on_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__init__.py new file mode 100644 index 0000000..d067f9f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__init__.py @@ -0,0 +1,641 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.marketplace.installed_add_on.installed_add_on_extension import ( + InstalledAddOnExtensionList, +) + + +class InstalledAddOnInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the InstalledAddOn resource. This Sid can also be found in the Console on that specific Add-ons page as the 'Available Add-on Sid'. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the InstalledAddOn resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar description: A short description of the Add-on's functionality. + :ivar configuration: The JSON object that represents the current configuration of installed Add-on. + :ivar unique_name: An application-defined string that uniquely identifies the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.description: Optional[str] = payload.get("description") + self.configuration: Optional[Dict[str, object]] = payload.get("configuration") + self.unique_name: Optional[str] = payload.get("unique_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[InstalledAddOnContext] = None + + @property + def _proxy(self) -> "InstalledAddOnContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InstalledAddOnContext for this InstalledAddOnInstance + """ + if self._context is None: + self._context = InstalledAddOnContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InstalledAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InstalledAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InstalledAddOnInstance": + """ + Fetch the InstalledAddOnInstance + + + :returns: The fetched InstalledAddOnInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InstalledAddOnInstance": + """ + Asynchronous coroutine to fetch the InstalledAddOnInstance + + + :returns: The fetched InstalledAddOnInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> "InstalledAddOnInstance": + """ + Update the InstalledAddOnInstance + + :param configuration: Valid JSON object that conform to the configuration schema exposed by the associated AvailableAddOn resource. This is only required by Add-ons that need to be configured + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The updated InstalledAddOnInstance + """ + return self._proxy.update( + configuration=configuration, + unique_name=unique_name, + ) + + async def update_async( + self, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> "InstalledAddOnInstance": + """ + Asynchronous coroutine to update the InstalledAddOnInstance + + :param configuration: Valid JSON object that conform to the configuration schema exposed by the associated AvailableAddOn resource. This is only required by Add-ons that need to be configured + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The updated InstalledAddOnInstance + """ + return await self._proxy.update_async( + configuration=configuration, + unique_name=unique_name, + ) + + @property + def extensions(self) -> InstalledAddOnExtensionList: + """ + Access the extensions + """ + return self._proxy.extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InstalledAddOnContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the InstalledAddOnContext + + :param version: Version that contains the resource + :param sid: The SID of the InstalledAddOn resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/InstalledAddOns/{sid}".format(**self._solution) + + self._extensions: Optional[InstalledAddOnExtensionList] = None + + def delete(self) -> bool: + """ + Deletes the InstalledAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InstalledAddOnInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InstalledAddOnInstance: + """ + Fetch the InstalledAddOnInstance + + + :returns: The fetched InstalledAddOnInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InstalledAddOnInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InstalledAddOnInstance: + """ + Asynchronous coroutine to fetch the InstalledAddOnInstance + + + :returns: The fetched InstalledAddOnInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InstalledAddOnInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> InstalledAddOnInstance: + """ + Update the InstalledAddOnInstance + + :param configuration: Valid JSON object that conform to the configuration schema exposed by the associated AvailableAddOn resource. This is only required by Add-ons that need to be configured + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The updated InstalledAddOnInstance + """ + data = values.of( + { + "Configuration": serialize.object(configuration), + "UniqueName": unique_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> InstalledAddOnInstance: + """ + Asynchronous coroutine to update the InstalledAddOnInstance + + :param configuration: Valid JSON object that conform to the configuration schema exposed by the associated AvailableAddOn resource. This is only required by Add-ons that need to be configured + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The updated InstalledAddOnInstance + """ + data = values.of( + { + "Configuration": serialize.object(configuration), + "UniqueName": unique_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def extensions(self) -> InstalledAddOnExtensionList: + """ + Access the extensions + """ + if self._extensions is None: + self._extensions = InstalledAddOnExtensionList( + self._version, + self._solution["sid"], + ) + return self._extensions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InstalledAddOnPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InstalledAddOnInstance: + """ + Build an instance of InstalledAddOnInstance + + :param payload: Payload response from the API + """ + return InstalledAddOnInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InstalledAddOnList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the InstalledAddOnList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/InstalledAddOns" + + def create( + self, + available_add_on_sid: str, + accept_terms_of_service: bool, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> InstalledAddOnInstance: + """ + Create the InstalledAddOnInstance + + :param available_add_on_sid: The SID of the AvaliableAddOn to install. + :param accept_terms_of_service: Whether the Terms of Service were accepted. + :param configuration: The JSON object that represents the configuration of the new Add-on being installed. + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The created InstalledAddOnInstance + """ + + data = values.of( + { + "AvailableAddOnSid": available_add_on_sid, + "AcceptTermsOfService": serialize.boolean_to_string( + accept_terms_of_service + ), + "Configuration": serialize.object(configuration), + "UniqueName": unique_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnInstance(self._version, payload) + + async def create_async( + self, + available_add_on_sid: str, + accept_terms_of_service: bool, + configuration: Union[object, object] = values.unset, + unique_name: Union[str, object] = values.unset, + ) -> InstalledAddOnInstance: + """ + Asynchronously create the InstalledAddOnInstance + + :param available_add_on_sid: The SID of the AvaliableAddOn to install. + :param accept_terms_of_service: Whether the Terms of Service were accepted. + :param configuration: The JSON object that represents the configuration of the new Add-on being installed. + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within the Account. + + :returns: The created InstalledAddOnInstance + """ + + data = values.of( + { + "AvailableAddOnSid": available_add_on_sid, + "AcceptTermsOfService": serialize.boolean_to_string( + accept_terms_of_service + ), + "Configuration": serialize.object(configuration), + "UniqueName": unique_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InstalledAddOnInstance]: + """ + Streams InstalledAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InstalledAddOnInstance]: + """ + Asynchronously streams InstalledAddOnInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InstalledAddOnInstance]: + """ + Lists InstalledAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InstalledAddOnInstance]: + """ + Asynchronously lists InstalledAddOnInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InstalledAddOnPage: + """ + Retrieve a single page of InstalledAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InstalledAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InstalledAddOnPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InstalledAddOnPage: + """ + Asynchronously retrieve a single page of InstalledAddOnInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InstalledAddOnInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InstalledAddOnPage(self._version, response) + + def get_page(self, target_url: str) -> InstalledAddOnPage: + """ + Retrieve a specific page of InstalledAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InstalledAddOnInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InstalledAddOnPage(self._version, response) + + async def get_page_async(self, target_url: str) -> InstalledAddOnPage: + """ + Asynchronously retrieve a specific page of InstalledAddOnInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InstalledAddOnInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InstalledAddOnPage(self._version, response) + + def get(self, sid: str) -> InstalledAddOnContext: + """ + Constructs a InstalledAddOnContext + + :param sid: The SID of the InstalledAddOn resource to update. + """ + return InstalledAddOnContext(self._version, sid=sid) + + def __call__(self, sid: str) -> InstalledAddOnContext: + """ + Constructs a InstalledAddOnContext + + :param sid: The SID of the InstalledAddOn resource to update. + """ + return InstalledAddOnContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1d062d9 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/installed_add_on_extension.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/installed_add_on_extension.cpython-310.pyc new file mode 100644 index 0000000..26de821 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/__pycache__/installed_add_on_extension.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/installed_add_on_extension.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/installed_add_on_extension.py new file mode 100644 index 0000000..f8e65d1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/marketplace/installed_add_on/installed_add_on_extension.py @@ -0,0 +1,511 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InstalledAddOnExtensionInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the InstalledAddOn Extension resource. + :ivar installed_add_on_sid: The SID of the InstalledAddOn resource to which this extension applies. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar product_name: The name of the Product this Extension is used within. + :ivar unique_name: An application-defined string that uniquely identifies the resource. + :ivar enabled: Whether the Extension will be invoked. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + installed_add_on_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.installed_add_on_sid: Optional[str] = payload.get("installed_add_on_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.product_name: Optional[str] = payload.get("product_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.enabled: Optional[bool] = payload.get("enabled") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "installed_add_on_sid": installed_add_on_sid, + "sid": sid or self.sid, + } + self._context: Optional[InstalledAddOnExtensionContext] = None + + @property + def _proxy(self) -> "InstalledAddOnExtensionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InstalledAddOnExtensionContext for this InstalledAddOnExtensionInstance + """ + if self._context is None: + self._context = InstalledAddOnExtensionContext( + self._version, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "InstalledAddOnExtensionInstance": + """ + Fetch the InstalledAddOnExtensionInstance + + + :returns: The fetched InstalledAddOnExtensionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InstalledAddOnExtensionInstance": + """ + Asynchronous coroutine to fetch the InstalledAddOnExtensionInstance + + + :returns: The fetched InstalledAddOnExtensionInstance + """ + return await self._proxy.fetch_async() + + def update(self, enabled: bool) -> "InstalledAddOnExtensionInstance": + """ + Update the InstalledAddOnExtensionInstance + + :param enabled: Whether the Extension should be invoked. + + :returns: The updated InstalledAddOnExtensionInstance + """ + return self._proxy.update( + enabled=enabled, + ) + + async def update_async(self, enabled: bool) -> "InstalledAddOnExtensionInstance": + """ + Asynchronous coroutine to update the InstalledAddOnExtensionInstance + + :param enabled: Whether the Extension should be invoked. + + :returns: The updated InstalledAddOnExtensionInstance + """ + return await self._proxy.update_async( + enabled=enabled, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InstalledAddOnExtensionContext(InstanceContext): + + def __init__(self, version: Version, installed_add_on_sid: str, sid: str): + """ + Initialize the InstalledAddOnExtensionContext + + :param version: Version that contains the resource + :param installed_add_on_sid: The SID of the InstalledAddOn resource with the extension to update. + :param sid: The SID of the InstalledAddOn Extension resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "installed_add_on_sid": installed_add_on_sid, + "sid": sid, + } + self._uri = "/InstalledAddOns/{installed_add_on_sid}/Extensions/{sid}".format( + **self._solution + ) + + def fetch(self) -> InstalledAddOnExtensionInstance: + """ + Fetch the InstalledAddOnExtensionInstance + + + :returns: The fetched InstalledAddOnExtensionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InstalledAddOnExtensionInstance( + self._version, + payload, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InstalledAddOnExtensionInstance: + """ + Asynchronous coroutine to fetch the InstalledAddOnExtensionInstance + + + :returns: The fetched InstalledAddOnExtensionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InstalledAddOnExtensionInstance( + self._version, + payload, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=self._solution["sid"], + ) + + def update(self, enabled: bool) -> InstalledAddOnExtensionInstance: + """ + Update the InstalledAddOnExtensionInstance + + :param enabled: Whether the Extension should be invoked. + + :returns: The updated InstalledAddOnExtensionInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnExtensionInstance( + self._version, + payload, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, enabled: bool) -> InstalledAddOnExtensionInstance: + """ + Asynchronous coroutine to update the InstalledAddOnExtensionInstance + + :param enabled: Whether the Extension should be invoked. + + :returns: The updated InstalledAddOnExtensionInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return InstalledAddOnExtensionInstance( + self._version, + payload, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class InstalledAddOnExtensionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InstalledAddOnExtensionInstance: + """ + Build an instance of InstalledAddOnExtensionInstance + + :param payload: Payload response from the API + """ + return InstalledAddOnExtensionInstance( + self._version, + payload, + installed_add_on_sid=self._solution["installed_add_on_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InstalledAddOnExtensionList(ListResource): + + def __init__(self, version: Version, installed_add_on_sid: str): + """ + Initialize the InstalledAddOnExtensionList + + :param version: Version that contains the resource + :param installed_add_on_sid: The SID of the InstalledAddOn resource with the extensions to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "installed_add_on_sid": installed_add_on_sid, + } + self._uri = "/InstalledAddOns/{installed_add_on_sid}/Extensions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InstalledAddOnExtensionInstance]: + """ + Streams InstalledAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InstalledAddOnExtensionInstance]: + """ + Asynchronously streams InstalledAddOnExtensionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InstalledAddOnExtensionInstance]: + """ + Lists InstalledAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InstalledAddOnExtensionInstance]: + """ + Asynchronously lists InstalledAddOnExtensionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InstalledAddOnExtensionPage: + """ + Retrieve a single page of InstalledAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InstalledAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InstalledAddOnExtensionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InstalledAddOnExtensionPage: + """ + Asynchronously retrieve a single page of InstalledAddOnExtensionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InstalledAddOnExtensionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InstalledAddOnExtensionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InstalledAddOnExtensionPage: + """ + Retrieve a specific page of InstalledAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InstalledAddOnExtensionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InstalledAddOnExtensionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InstalledAddOnExtensionPage: + """ + Asynchronously retrieve a specific page of InstalledAddOnExtensionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InstalledAddOnExtensionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InstalledAddOnExtensionPage(self._version, response, self._solution) + + def get(self, sid: str) -> InstalledAddOnExtensionContext: + """ + Constructs a InstalledAddOnExtensionContext + + :param sid: The SID of the InstalledAddOn Extension resource to update. + """ + return InstalledAddOnExtensionContext( + self._version, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InstalledAddOnExtensionContext: + """ + Constructs a InstalledAddOnExtensionContext + + :param sid: The SID of the InstalledAddOn Extension resource to update. + """ + return InstalledAddOnExtensionContext( + self._version, + installed_add_on_sid=self._solution["installed_add_on_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__init__.py new file mode 100644 index 0000000..d84c790 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview.sync.service import ServiceList + + +class Sync(Version): + + def __init__(self, domain: Domain): + """ + Initialize the Sync version of Preview + + :param domain: The Twilio.preview domain + """ + super().__init__(domain, "Sync") + self._services: Optional[ServiceList] = None + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fba2275 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__init__.py new file mode 100644 index 0000000..cb8cdf7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__init__.py @@ -0,0 +1,711 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.sync.service.document import DocumentList +from twilio.rest.preview.sync.service.sync_list import SyncListList +from twilio.rest.preview.sync.service.sync_map import SyncMapList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar date_created: + :ivar date_updated: + :ivar url: + :ivar webhook_url: + :ivar reachability_webhooks_enabled: + :ivar acl_enabled: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.reachability_webhooks_enabled: Optional[bool] = payload.get( + "reachability_webhooks_enabled" + ) + self.acl_enabled: Optional[bool] = payload.get("acl_enabled") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param webhook_url: + :param friendly_name: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + webhook_url=webhook_url, + friendly_name=friendly_name, + reachability_webhooks_enabled=reachability_webhooks_enabled, + acl_enabled=acl_enabled, + ) + + async def update_async( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param webhook_url: + :param friendly_name: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + webhook_url=webhook_url, + friendly_name=friendly_name, + reachability_webhooks_enabled=reachability_webhooks_enabled, + acl_enabled=acl_enabled, + ) + + @property + def documents(self) -> DocumentList: + """ + Access the documents + """ + return self._proxy.documents + + @property + def sync_lists(self) -> SyncListList: + """ + Access the sync_lists + """ + return self._proxy.sync_lists + + @property + def sync_maps(self) -> SyncMapList: + """ + Access the sync_maps + """ + return self._proxy.sync_maps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._documents: Optional[DocumentList] = None + self._sync_lists: Optional[SyncListList] = None + self._sync_maps: Optional[SyncMapList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param webhook_url: + :param friendly_name: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "WebhookUrl": webhook_url, + "FriendlyName": friendly_name, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param webhook_url: + :param friendly_name: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "WebhookUrl": webhook_url, + "FriendlyName": friendly_name, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def documents(self) -> DocumentList: + """ + Access the documents + """ + if self._documents is None: + self._documents = DocumentList( + self._version, + self._solution["sid"], + ) + return self._documents + + @property + def sync_lists(self) -> SyncListList: + """ + Access the sync_lists + """ + if self._sync_lists is None: + self._sync_lists = SyncListList( + self._version, + self._solution["sid"], + ) + return self._sync_lists + + @property + def sync_maps(self) -> SyncMapList: + """ + Access the sync_maps + """ + if self._sync_maps is None: + self._sync_maps = SyncMapList( + self._version, + self._solution["sid"], + ) + return self._sync_maps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + friendly_name: Union[str, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: + :param webhook_url: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: + :param webhook_url: + :param reachability_webhooks_enabled: + :param acl_enabled: + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1e4a0e3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__init__.py new file mode 100644 index 0000000..e7ab4ae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__init__.py @@ -0,0 +1,659 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.sync.service.document.document_permission import ( + DocumentPermissionList, +) + + +class DocumentInstance(InstanceResource): + """ + :ivar sid: + :ivar unique_name: + :ivar account_sid: + :ivar service_sid: + :ivar url: + :ivar links: + :ivar revision: + :ivar data: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[DocumentContext] = None + + @property + def _proxy(self) -> "DocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DocumentContext for this DocumentInstance + """ + if self._context is None: + self._context = DocumentContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DocumentInstance": + """ + Fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DocumentInstance": + """ + Asynchronous coroutine to fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "DocumentInstance": + """ + Update the DocumentInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated DocumentInstance + """ + return self._proxy.update( + data=data, + if_match=if_match, + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "DocumentInstance": + """ + Asynchronous coroutine to update the DocumentInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated DocumentInstance + """ + return await self._proxy.update_async( + data=data, + if_match=if_match, + ) + + @property + def document_permissions(self) -> DocumentPermissionList: + """ + Access the document_permissions + """ + return self._proxy.document_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the DocumentContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Documents/{sid}".format(**self._solution) + + self._document_permissions: Optional[DocumentPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DocumentInstance: + """ + Fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DocumentInstance: + """ + Asynchronous coroutine to fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> DocumentInstance: + """ + Update the DocumentInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated DocumentInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> DocumentInstance: + """ + Asynchronous coroutine to update the DocumentInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated DocumentInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def document_permissions(self) -> DocumentPermissionList: + """ + Access the document_permissions + """ + if self._document_permissions is None: + self._document_permissions = DocumentPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._document_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DocumentInstance: + """ + Build an instance of DocumentInstance + + :param payload: Payload response from the API + """ + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DocumentList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the DocumentList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Documents".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ) -> DocumentInstance: + """ + Create the DocumentInstance + + :param unique_name: + :param data: + + :returns: The created DocumentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Data": serialize.object(data), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ) -> DocumentInstance: + """ + Asynchronously create the DocumentInstance + + :param unique_name: + :param data: + + :returns: The created DocumentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Data": serialize.object(data), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DocumentInstance]: + """ + Streams DocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DocumentInstance]: + """ + Asynchronously streams DocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentInstance]: + """ + Lists DocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentInstance]: + """ + Asynchronously lists DocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPage: + """ + Retrieve a single page of DocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DocumentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPage: + """ + Asynchronously retrieve a single page of DocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DocumentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DocumentPage: + """ + Retrieve a specific page of DocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DocumentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DocumentPage: + """ + Asynchronously retrieve a specific page of DocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DocumentPage(self._version, response, self._solution) + + def get(self, sid: str) -> DocumentContext: + """ + Constructs a DocumentContext + + :param sid: + """ + return DocumentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> DocumentContext: + """ + Constructs a DocumentContext + + :param sid: + """ + return DocumentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..b85adcb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/document_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/document_permission.cpython-310.pyc new file mode 100644 index 0000000..b8ce6da Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/__pycache__/document_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/document_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/document_permission.py new file mode 100644 index 0000000..48addbb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/document/document_permission.py @@ -0,0 +1,593 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DocumentPermissionInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Twilio Account. + :ivar service_sid: The unique SID identifier of the Sync Service Instance. + :ivar document_sid: The unique SID identifier of the Sync Document to which the Permission applies. + :ivar identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + :ivar read: Boolean flag specifying whether the identity can read the Sync Document. + :ivar write: Boolean flag specifying whether the identity can update the Sync Document. + :ivar manage: Boolean flag specifying whether the identity can delete the Sync Document. + :ivar url: Contains an absolute URL for this Sync Document Permission. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + document_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.document_sid: Optional[str] = payload.get("document_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + "identity": identity or self.identity, + } + self._context: Optional[DocumentPermissionContext] = None + + @property + def _proxy(self) -> "DocumentPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DocumentPermissionContext for this DocumentPermissionInstance + """ + if self._context is None: + self._context = DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DocumentPermissionInstance": + """ + Fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DocumentPermissionInstance": + """ + Asynchronous coroutine to fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "DocumentPermissionInstance": + """ + Update the DocumentPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Document. + :param write: Boolean flag specifying whether the identity can update the Sync Document. + :param manage: Boolean flag specifying whether the identity can delete the Sync Document. + + :returns: The updated DocumentPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "DocumentPermissionInstance": + """ + Asynchronous coroutine to update the DocumentPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Document. + :param write: Boolean flag specifying whether the identity can update the Sync Document. + :param manage: Boolean flag specifying whether the identity can delete the Sync Document. + + :returns: The updated DocumentPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPermissionContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, document_sid: str, identity: str + ): + """ + Initialize the DocumentPermissionContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Sync Service Instance. + :param document_sid: Identifier of the Sync Document. Either a SID or a unique name. + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Documents/{document_sid}/Permissions/{identity}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DocumentPermissionInstance: + """ + Fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> DocumentPermissionInstance: + """ + Asynchronous coroutine to fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> DocumentPermissionInstance: + """ + Update the DocumentPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Document. + :param write: Boolean flag specifying whether the identity can update the Sync Document. + :param manage: Boolean flag specifying whether the identity can delete the Sync Document. + + :returns: The updated DocumentPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> DocumentPermissionInstance: + """ + Asynchronous coroutine to update the DocumentPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Document. + :param write: Boolean flag specifying whether the identity can update the Sync Document. + :param manage: Boolean flag specifying whether the identity can delete the Sync Document. + + :returns: The updated DocumentPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DocumentPermissionInstance: + """ + Build an instance of DocumentPermissionInstance + + :param payload: Payload response from the API + """ + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DocumentPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, document_sid: str): + """ + Initialize the DocumentPermissionList + + :param version: Version that contains the resource + :param service_sid: + :param document_sid: Identifier of the Sync Document. Either a SID or a unique name. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + } + self._uri = ( + "/Services/{service_sid}/Documents/{document_sid}/Permissions".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DocumentPermissionInstance]: + """ + Streams DocumentPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DocumentPermissionInstance]: + """ + Asynchronously streams DocumentPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentPermissionInstance]: + """ + Lists DocumentPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentPermissionInstance]: + """ + Asynchronously lists DocumentPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPermissionPage: + """ + Retrieve a single page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DocumentPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPermissionPage: + """ + Asynchronously retrieve a single page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DocumentPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DocumentPermissionPage: + """ + Retrieve a specific page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DocumentPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DocumentPermissionPage: + """ + Asynchronously retrieve a specific page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DocumentPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> DocumentPermissionContext: + """ + Constructs a DocumentPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> DocumentPermissionContext: + """ + Constructs a DocumentPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__init__.py new file mode 100644 index 0000000..fcd2f28 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__init__.py @@ -0,0 +1,573 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.sync.service.sync_list.sync_list_item import SyncListItemList +from twilio.rest.preview.sync.service.sync_list.sync_list_permission import ( + SyncListPermissionList, +) + + +class SyncListInstance(InstanceResource): + """ + :ivar sid: + :ivar unique_name: + :ivar account_sid: + :ivar service_sid: + :ivar url: + :ivar links: + :ivar revision: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SyncListContext] = None + + @property + def _proxy(self) -> "SyncListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListContext for this SyncListInstance + """ + if self._context is None: + self._context = SyncListContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncListInstance": + """ + Fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListInstance": + """ + Asynchronous coroutine to fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + return await self._proxy.fetch_async() + + @property + def sync_list_items(self) -> SyncListItemList: + """ + Access the sync_list_items + """ + return self._proxy.sync_list_items + + @property + def sync_list_permissions(self) -> SyncListPermissionList: + """ + Access the sync_list_permissions + """ + return self._proxy.sync_list_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SyncListContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Lists/{sid}".format(**self._solution) + + self._sync_list_items: Optional[SyncListItemList] = None + self._sync_list_permissions: Optional[SyncListPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncListInstance: + """ + Fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SyncListInstance: + """ + Asynchronous coroutine to fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def sync_list_items(self) -> SyncListItemList: + """ + Access the sync_list_items + """ + if self._sync_list_items is None: + self._sync_list_items = SyncListItemList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_list_items + + @property + def sync_list_permissions(self) -> SyncListPermissionList: + """ + Access the sync_list_permissions + """ + if self._sync_list_permissions is None: + self._sync_list_permissions = SyncListPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_list_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListInstance: + """ + Build an instance of SyncListInstance + + :param payload: Payload response from the API + """ + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SyncListList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Lists".format(**self._solution) + + def create( + self, unique_name: Union[str, object] = values.unset + ) -> SyncListInstance: + """ + Create the SyncListInstance + + :param unique_name: + + :returns: The created SyncListInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, unique_name: Union[str, object] = values.unset + ) -> SyncListInstance: + """ + Asynchronously create the SyncListInstance + + :param unique_name: + + :returns: The created SyncListInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListInstance]: + """ + Streams SyncListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListInstance]: + """ + Asynchronously streams SyncListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListInstance]: + """ + Lists SyncListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListInstance]: + """ + Asynchronously lists SyncListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPage: + """ + Retrieve a single page of SyncListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPage: + """ + Asynchronously retrieve a single page of SyncListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListPage: + """ + Retrieve a specific page of SyncListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListPage: + """ + Asynchronously retrieve a specific page of SyncListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListPage(self._version, response, self._solution) + + def get(self, sid: str) -> SyncListContext: + """ + Constructs a SyncListContext + + :param sid: + """ + return SyncListContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SyncListContext: + """ + Constructs a SyncListContext + + :param sid: + """ + return SyncListContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c318a6b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc new file mode 100644 index 0000000..4589147 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc new file mode 100644 index 0000000..30a664c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_item.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_item.py new file mode 100644 index 0000000..0cefd6c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_item.py @@ -0,0 +1,727 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncListItemInstance(InstanceResource): + + class QueryFromBoundType(object): + INCLUSIVE = "inclusive" + EXCLUSIVE = "exclusive" + + class QueryResultOrder(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar index: + :ivar account_sid: + :ivar service_sid: + :ivar list_sid: + :ivar url: + :ivar revision: + :ivar data: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + list_sid: str, + index: Optional[int] = None, + ): + super().__init__(version) + + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.list_sid: Optional[str] = payload.get("list_sid") + self.url: Optional[str] = payload.get("url") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "index": index or self.index, + } + self._context: Optional[SyncListItemContext] = None + + @property + def _proxy(self) -> "SyncListItemContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListItemContext for this SyncListItemInstance + """ + if self._context is None: + self._context = SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncListItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncListItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "SyncListItemInstance": + """ + Fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListItemInstance": + """ + Asynchronous coroutine to fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + return await self._proxy.fetch_async() + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "SyncListItemInstance": + """ + Update the SyncListItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncListItemInstance + """ + return self._proxy.update( + data=data, + if_match=if_match, + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "SyncListItemInstance": + """ + Asynchronous coroutine to update the SyncListItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncListItemInstance + """ + return await self._proxy.update_async( + data=data, + if_match=if_match, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListItemContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, list_sid: str, index: int): + """ + Initialize the SyncListItemContext + + :param version: Version that contains the resource + :param service_sid: + :param list_sid: + :param index: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "index": index, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Items/{index}".format( + **self._solution + ) + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncListItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncListItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> SyncListItemInstance: + """ + Fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + async def fetch_async(self) -> SyncListItemInstance: + """ + Asynchronous coroutine to fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> SyncListItemInstance: + """ + Update the SyncListItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncListItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> SyncListItemInstance: + """ + Asynchronous coroutine to update the SyncListItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncListItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListItemPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListItemInstance: + """ + Build an instance of SyncListItemInstance + + :param payload: Payload response from the API + """ + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListItemList(ListResource): + + def __init__(self, version: Version, service_sid: str, list_sid: str): + """ + Initialize the SyncListItemList + + :param version: Version that contains the resource + :param service_sid: + :param list_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Items".format( + **self._solution + ) + + def create(self, data: object) -> SyncListItemInstance: + """ + Create the SyncListItemInstance + + :param data: + + :returns: The created SyncListItemInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + async def create_async(self, data: object) -> SyncListItemInstance: + """ + Asynchronously create the SyncListItemInstance + + :param data: + + :returns: The created SyncListItemInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def stream( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListItemInstance]: + """ + Streams SyncListItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncListItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncListItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListItemInstance]: + """ + Asynchronously streams SyncListItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncListItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncListItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListItemInstance]: + """ + Lists SyncListItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncListItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncListItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListItemInstance]: + """ + Asynchronously lists SyncListItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncListItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncListItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListItemPage: + """ + Retrieve a single page of SyncListItemInstance records from the API. + Request is executed immediately + + :param order: + :param from_: + :param bounds: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListItemPage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListItemPage: + """ + Asynchronously retrieve a single page of SyncListItemInstance records from the API. + Request is executed immediately + + :param order: + :param from_: + :param bounds: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListItemPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListItemPage: + """ + Retrieve a specific page of SyncListItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListItemInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListItemPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListItemPage: + """ + Asynchronously retrieve a specific page of SyncListItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListItemInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListItemPage(self._version, response, self._solution) + + def get(self, index: int) -> SyncListItemContext: + """ + Constructs a SyncListItemContext + + :param index: + """ + return SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=index, + ) + + def __call__(self, index: int) -> SyncListItemContext: + """ + Constructs a SyncListItemContext + + :param index: + """ + return SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_permission.py new file mode 100644 index 0000000..2fcaca5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_list/sync_list_permission.py @@ -0,0 +1,593 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncListPermissionInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Twilio Account. + :ivar service_sid: The unique SID identifier of the Sync Service Instance. + :ivar list_sid: The unique SID identifier of the Sync List to which the Permission applies. + :ivar identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + :ivar read: Boolean flag specifying whether the identity can read the Sync List and its Items. + :ivar write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync List. + :ivar manage: Boolean flag specifying whether the identity can delete the Sync List. + :ivar url: Contains an absolute URL for this Sync List Permission. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + list_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.list_sid: Optional[str] = payload.get("list_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "identity": identity or self.identity, + } + self._context: Optional[SyncListPermissionContext] = None + + @property + def _proxy(self) -> "SyncListPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListPermissionContext for this SyncListPermissionInstance + """ + if self._context is None: + self._context = SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncListPermissionInstance": + """ + Fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListPermissionInstance": + """ + Asynchronous coroutine to fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "SyncListPermissionInstance": + """ + Update the SyncListPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync List. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync List. + :param manage: Boolean flag specifying whether the identity can delete the Sync List. + + :returns: The updated SyncListPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "SyncListPermissionInstance": + """ + Asynchronous coroutine to update the SyncListPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync List. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync List. + :param manage: Boolean flag specifying whether the identity can delete the Sync List. + + :returns: The updated SyncListPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPermissionContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, list_sid: str, identity: str + ): + """ + Initialize the SyncListPermissionContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Sync Service Instance. + :param list_sid: Identifier of the Sync List. Either a SID or a unique name. + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "identity": identity, + } + self._uri = ( + "/Services/{service_sid}/Lists/{list_sid}/Permissions/{identity}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncListPermissionInstance: + """ + Fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> SyncListPermissionInstance: + """ + Asynchronous coroutine to fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> SyncListPermissionInstance: + """ + Update the SyncListPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync List. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync List. + :param manage: Boolean flag specifying whether the identity can delete the Sync List. + + :returns: The updated SyncListPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> SyncListPermissionInstance: + """ + Asynchronous coroutine to update the SyncListPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync List. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync List. + :param manage: Boolean flag specifying whether the identity can delete the Sync List. + + :returns: The updated SyncListPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListPermissionInstance: + """ + Build an instance of SyncListPermissionInstance + + :param payload: Payload response from the API + """ + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, list_sid: str): + """ + Initialize the SyncListPermissionList + + :param version: Version that contains the resource + :param service_sid: + :param list_sid: Identifier of the Sync List. Either a SID or a unique name. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Permissions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListPermissionInstance]: + """ + Streams SyncListPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListPermissionInstance]: + """ + Asynchronously streams SyncListPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListPermissionInstance]: + """ + Lists SyncListPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListPermissionInstance]: + """ + Asynchronously lists SyncListPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPermissionPage: + """ + Retrieve a single page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPermissionPage: + """ + Asynchronously retrieve a single page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListPermissionPage: + """ + Retrieve a specific page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListPermissionPage: + """ + Asynchronously retrieve a specific page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> SyncListPermissionContext: + """ + Constructs a SyncListPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> SyncListPermissionContext: + """ + Constructs a SyncListPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__init__.py new file mode 100644 index 0000000..1d79936 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__init__.py @@ -0,0 +1,571 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.sync.service.sync_map.sync_map_item import SyncMapItemList +from twilio.rest.preview.sync.service.sync_map.sync_map_permission import ( + SyncMapPermissionList, +) + + +class SyncMapInstance(InstanceResource): + """ + :ivar sid: + :ivar unique_name: + :ivar account_sid: + :ivar service_sid: + :ivar url: + :ivar links: + :ivar revision: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SyncMapContext] = None + + @property + def _proxy(self) -> "SyncMapContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapContext for this SyncMapInstance + """ + if self._context is None: + self._context = SyncMapContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncMapInstance": + """ + Fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapInstance": + """ + Asynchronous coroutine to fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + return await self._proxy.fetch_async() + + @property + def sync_map_items(self) -> SyncMapItemList: + """ + Access the sync_map_items + """ + return self._proxy.sync_map_items + + @property + def sync_map_permissions(self) -> SyncMapPermissionList: + """ + Access the sync_map_permissions + """ + return self._proxy.sync_map_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SyncMapContext + + :param version: Version that contains the resource + :param service_sid: + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Maps/{sid}".format(**self._solution) + + self._sync_map_items: Optional[SyncMapItemList] = None + self._sync_map_permissions: Optional[SyncMapPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncMapInstance: + """ + Fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SyncMapInstance: + """ + Asynchronous coroutine to fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def sync_map_items(self) -> SyncMapItemList: + """ + Access the sync_map_items + """ + if self._sync_map_items is None: + self._sync_map_items = SyncMapItemList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_map_items + + @property + def sync_map_permissions(self) -> SyncMapPermissionList: + """ + Access the sync_map_permissions + """ + if self._sync_map_permissions is None: + self._sync_map_permissions = SyncMapPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_map_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapInstance: + """ + Build an instance of SyncMapInstance + + :param payload: Payload response from the API + """ + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SyncMapList + + :param version: Version that contains the resource + :param service_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Maps".format(**self._solution) + + def create(self, unique_name: Union[str, object] = values.unset) -> SyncMapInstance: + """ + Create the SyncMapInstance + + :param unique_name: + + :returns: The created SyncMapInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, unique_name: Union[str, object] = values.unset + ) -> SyncMapInstance: + """ + Asynchronously create the SyncMapInstance + + :param unique_name: + + :returns: The created SyncMapInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapInstance]: + """ + Streams SyncMapInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapInstance]: + """ + Asynchronously streams SyncMapInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapInstance]: + """ + Lists SyncMapInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapInstance]: + """ + Asynchronously lists SyncMapInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPage: + """ + Retrieve a single page of SyncMapInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPage: + """ + Asynchronously retrieve a single page of SyncMapInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapPage: + """ + Retrieve a specific page of SyncMapInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapPage: + """ + Asynchronously retrieve a specific page of SyncMapInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapPage(self._version, response, self._solution) + + def get(self, sid: str) -> SyncMapContext: + """ + Constructs a SyncMapContext + + :param sid: + """ + return SyncMapContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SyncMapContext: + """ + Constructs a SyncMapContext + + :param sid: + """ + return SyncMapContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4284ded Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc new file mode 100644 index 0000000..236c2d5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc new file mode 100644 index 0000000..4d72afe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_item.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_item.py new file mode 100644 index 0000000..322c8ed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_item.py @@ -0,0 +1,731 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncMapItemInstance(InstanceResource): + + class QueryFromBoundType(object): + INCLUSIVE = "inclusive" + EXCLUSIVE = "exclusive" + + class QueryResultOrder(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar key: + :ivar account_sid: + :ivar service_sid: + :ivar map_sid: + :ivar url: + :ivar revision: + :ivar data: + :ivar date_created: + :ivar date_updated: + :ivar created_by: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + map_sid: str, + key: Optional[str] = None, + ): + super().__init__(version) + + self.key: Optional[str] = payload.get("key") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.map_sid: Optional[str] = payload.get("map_sid") + self.url: Optional[str] = payload.get("url") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "key": key or self.key, + } + self._context: Optional[SyncMapItemContext] = None + + @property + def _proxy(self) -> "SyncMapItemContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapItemContext for this SyncMapItemInstance + """ + if self._context is None: + self._context = SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncMapItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "SyncMapItemInstance": + """ + Fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapItemInstance": + """ + Asynchronous coroutine to fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + return await self._proxy.fetch_async() + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "SyncMapItemInstance": + """ + Update the SyncMapItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncMapItemInstance + """ + return self._proxy.update( + data=data, + if_match=if_match, + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> "SyncMapItemInstance": + """ + Asynchronous coroutine to update the SyncMapItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncMapItemInstance + """ + return await self._proxy.update_async( + data=data, + if_match=if_match, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapItemContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, map_sid: str, key: str): + """ + Initialize the SyncMapItemContext + + :param version: Version that contains the resource + :param service_sid: + :param map_sid: + :param key: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "key": key, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Items/{key}".format( + **self._solution + ) + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncMapItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapItemInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> SyncMapItemInstance: + """ + Fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + async def fetch_async(self) -> SyncMapItemInstance: + """ + Asynchronous coroutine to fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + def update( + self, data: object, if_match: Union[str, object] = values.unset + ) -> SyncMapItemInstance: + """ + Update the SyncMapItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncMapItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + async def update_async( + self, data: object, if_match: Union[str, object] = values.unset + ) -> SyncMapItemInstance: + """ + Asynchronous coroutine to update the SyncMapItemInstance + + :param data: + :param if_match: The If-Match HTTP request header + + :returns: The updated SyncMapItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapItemPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapItemInstance: + """ + Build an instance of SyncMapItemInstance + + :param payload: Payload response from the API + """ + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapItemList(ListResource): + + def __init__(self, version: Version, service_sid: str, map_sid: str): + """ + Initialize the SyncMapItemList + + :param version: Version that contains the resource + :param service_sid: + :param map_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Items".format( + **self._solution + ) + + def create(self, key: str, data: object) -> SyncMapItemInstance: + """ + Create the SyncMapItemInstance + + :param key: + :param data: + + :returns: The created SyncMapItemInstance + """ + + data = values.of( + { + "Key": key, + "Data": serialize.object(data), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + async def create_async(self, key: str, data: object) -> SyncMapItemInstance: + """ + Asynchronously create the SyncMapItemInstance + + :param key: + :param data: + + :returns: The created SyncMapItemInstance + """ + + data = values.of( + { + "Key": key, + "Data": serialize.object(data), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def stream( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapItemInstance]: + """ + Streams SyncMapItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncMapItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncMapItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapItemInstance]: + """ + Asynchronously streams SyncMapItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncMapItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncMapItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapItemInstance]: + """ + Lists SyncMapItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncMapItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncMapItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapItemInstance]: + """ + Asynchronously lists SyncMapItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncMapItemInstance.QueryResultOrder" order: + :param str from_: + :param "SyncMapItemInstance.QueryFromBoundType" bounds: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapItemPage: + """ + Retrieve a single page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param order: + :param from_: + :param bounds: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapItemPage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapItemPage: + """ + Asynchronously retrieve a single page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param order: + :param from_: + :param bounds: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapItemPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapItemPage: + """ + Retrieve a specific page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapItemInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapItemPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapItemPage: + """ + Asynchronously retrieve a specific page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapItemInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapItemPage(self._version, response, self._solution) + + def get(self, key: str) -> SyncMapItemContext: + """ + Constructs a SyncMapItemContext + + :param key: + """ + return SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=key, + ) + + def __call__(self, key: str) -> SyncMapItemContext: + """ + Constructs a SyncMapItemContext + + :param key: + """ + return SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=key, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_permission.py new file mode 100644 index 0000000..b626f07 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/sync/service/sync_map/sync_map_permission.py @@ -0,0 +1,591 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncMapPermissionInstance(InstanceResource): + """ + :ivar account_sid: The unique SID identifier of the Twilio Account. + :ivar service_sid: The unique SID identifier of the Sync Service Instance. + :ivar map_sid: The unique SID identifier of the Sync Map to which the Permission applies. + :ivar identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + :ivar read: Boolean flag specifying whether the identity can read the Sync Map and its Items. + :ivar write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync Map. + :ivar manage: Boolean flag specifying whether the identity can delete the Sync Map. + :ivar url: Contains an absolute URL for this Sync Map Permission. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + map_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.map_sid: Optional[str] = payload.get("map_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "identity": identity or self.identity, + } + self._context: Optional[SyncMapPermissionContext] = None + + @property + def _proxy(self) -> "SyncMapPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapPermissionContext for this SyncMapPermissionInstance + """ + if self._context is None: + self._context = SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncMapPermissionInstance": + """ + Fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapPermissionInstance": + """ + Asynchronous coroutine to fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "SyncMapPermissionInstance": + """ + Update the SyncMapPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Map. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync Map. + :param manage: Boolean flag specifying whether the identity can delete the Sync Map. + + :returns: The updated SyncMapPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "SyncMapPermissionInstance": + """ + Asynchronous coroutine to update the SyncMapPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Map. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync Map. + :param manage: Boolean flag specifying whether the identity can delete the Sync Map. + + :returns: The updated SyncMapPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPermissionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, map_sid: str, identity: str): + """ + Initialize the SyncMapPermissionContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Sync Service Instance. + :param map_sid: Identifier of the Sync Map. Either a SID or a unique name. + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "identity": identity, + } + self._uri = ( + "/Services/{service_sid}/Maps/{map_sid}/Permissions/{identity}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncMapPermissionInstance: + """ + Fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> SyncMapPermissionInstance: + """ + Asynchronous coroutine to fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> SyncMapPermissionInstance: + """ + Update the SyncMapPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Map. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync Map. + :param manage: Boolean flag specifying whether the identity can delete the Sync Map. + + :returns: The updated SyncMapPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> SyncMapPermissionInstance: + """ + Asynchronous coroutine to update the SyncMapPermissionInstance + + :param read: Boolean flag specifying whether the identity can read the Sync Map. + :param write: Boolean flag specifying whether the identity can create, update and delete Items of the Sync Map. + :param manage: Boolean flag specifying whether the identity can delete the Sync Map. + + :returns: The updated SyncMapPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapPermissionInstance: + """ + Build an instance of SyncMapPermissionInstance + + :param payload: Payload response from the API + """ + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, map_sid: str): + """ + Initialize the SyncMapPermissionList + + :param version: Version that contains the resource + :param service_sid: + :param map_sid: Identifier of the Sync Map. Either a SID or a unique name. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Permissions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapPermissionInstance]: + """ + Streams SyncMapPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapPermissionInstance]: + """ + Asynchronously streams SyncMapPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapPermissionInstance]: + """ + Lists SyncMapPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapPermissionInstance]: + """ + Asynchronously lists SyncMapPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPermissionPage: + """ + Retrieve a single page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPermissionPage: + """ + Asynchronously retrieve a single page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapPermissionPage: + """ + Retrieve a specific page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapPermissionPage: + """ + Asynchronously retrieve a specific page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> SyncMapPermissionContext: + """ + Constructs a SyncMapPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> SyncMapPermissionContext: + """ + Constructs a SyncMapPermissionContext + + :param identity: Arbitrary string identifier representing a human user associated with an FPA token, assigned by the developer. + """ + return SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__init__.py new file mode 100644 index 0000000..85ed4fb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview.wireless.command import CommandList +from twilio.rest.preview.wireless.rate_plan import RatePlanList +from twilio.rest.preview.wireless.sim import SimList + + +class Wireless(Version): + + def __init__(self, domain: Domain): + """ + Initialize the Wireless version of Preview + + :param domain: The Twilio.preview domain + """ + super().__init__(domain, "wireless") + self._commands: Optional[CommandList] = None + self._rate_plans: Optional[RatePlanList] = None + self._sims: Optional[SimList] = None + + @property + def commands(self) -> CommandList: + if self._commands is None: + self._commands = CommandList(self) + return self._commands + + @property + def rate_plans(self) -> RatePlanList: + if self._rate_plans is None: + self._rate_plans = RatePlanList(self) + return self._rate_plans + + @property + def sims(self) -> SimList: + if self._sims is None: + self._sims = SimList(self) + return self._sims + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1df6e6d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/command.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/command.cpython-310.pyc new file mode 100644 index 0000000..eef3846 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/command.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/rate_plan.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/rate_plan.cpython-310.pyc new file mode 100644 index 0000000..2ec4983 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/__pycache__/rate_plan.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/command.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/command.py new file mode 100644 index 0000000..755256b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/command.py @@ -0,0 +1,575 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CommandInstance(InstanceResource): + """ + :ivar sid: + :ivar account_sid: + :ivar device_sid: + :ivar sim_sid: + :ivar command: + :ivar command_mode: + :ivar status: + :ivar direction: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.device_sid: Optional[str] = payload.get("device_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.command: Optional[str] = payload.get("command") + self.command_mode: Optional[str] = payload.get("command_mode") + self.status: Optional[str] = payload.get("status") + self.direction: Optional[str] = payload.get("direction") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CommandContext] = None + + @property + def _proxy(self) -> "CommandContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CommandContext for this CommandInstance + """ + if self._context is None: + self._context = CommandContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "CommandInstance": + """ + Fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CommandInstance": + """ + Asynchronous coroutine to fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CommandContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CommandContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Commands/{sid}".format(**self._solution) + + def fetch(self) -> CommandInstance: + """ + Fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CommandInstance: + """ + Asynchronous coroutine to fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CommandPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CommandInstance: + """ + Build an instance of CommandInstance + + :param payload: Payload response from the API + """ + return CommandInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CommandList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CommandList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Commands" + + def create( + self, + command: str, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + command_mode: Union[str, object] = values.unset, + include_sid: Union[str, object] = values.unset, + ) -> CommandInstance: + """ + Create the CommandInstance + + :param command: + :param device: + :param sim: + :param callback_method: + :param callback_url: + :param command_mode: + :param include_sid: + + :returns: The created CommandInstance + """ + + data = values.of( + { + "Command": command, + "Device": device, + "Sim": sim, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "CommandMode": command_mode, + "IncludeSid": include_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CommandInstance(self._version, payload) + + async def create_async( + self, + command: str, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + command_mode: Union[str, object] = values.unset, + include_sid: Union[str, object] = values.unset, + ) -> CommandInstance: + """ + Asynchronously create the CommandInstance + + :param command: + :param device: + :param sim: + :param callback_method: + :param callback_url: + :param command_mode: + :param include_sid: + + :returns: The created CommandInstance + """ + + data = values.of( + { + "Command": command, + "Device": device, + "Sim": sim, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "CommandMode": command_mode, + "IncludeSid": include_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CommandInstance(self._version, payload) + + def stream( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CommandInstance]: + """ + Streams CommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device: + :param str sim: + :param str status: + :param str direction: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + device=device, + sim=sim, + status=status, + direction=direction, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CommandInstance]: + """ + Asynchronously streams CommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str device: + :param str sim: + :param str status: + :param str direction: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + device=device, + sim=sim, + status=status, + direction=direction, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CommandInstance]: + """ + Lists CommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device: + :param str sim: + :param str status: + :param str direction: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + device=device, + sim=sim, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CommandInstance]: + """ + Asynchronously lists CommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str device: + :param str sim: + :param str status: + :param str direction: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + device=device, + sim=sim, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CommandPage: + """ + Retrieve a single page of CommandInstance records from the API. + Request is executed immediately + + :param device: + :param sim: + :param status: + :param direction: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CommandInstance + """ + data = values.of( + { + "Device": device, + "Sim": sim, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CommandPage(self._version, response) + + async def page_async( + self, + device: Union[str, object] = values.unset, + sim: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + direction: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CommandPage: + """ + Asynchronously retrieve a single page of CommandInstance records from the API. + Request is executed immediately + + :param device: + :param sim: + :param status: + :param direction: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CommandInstance + """ + data = values.of( + { + "Device": device, + "Sim": sim, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CommandPage(self._version, response) + + def get_page(self, target_url: str) -> CommandPage: + """ + Retrieve a specific page of CommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CommandInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CommandPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CommandPage: + """ + Asynchronously retrieve a specific page of CommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CommandInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CommandPage(self._version, response) + + def get(self, sid: str) -> CommandContext: + """ + Constructs a CommandContext + + :param sid: + """ + return CommandContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CommandContext: + """ + Constructs a CommandContext + + :param sid: + """ + return CommandContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/rate_plan.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/rate_plan.py new file mode 100644 index 0000000..f3484ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/rate_plan.py @@ -0,0 +1,669 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RatePlanInstance(InstanceResource): + """ + :ivar sid: + :ivar unique_name: + :ivar account_sid: + :ivar friendly_name: + :ivar data_enabled: + :ivar data_metering: + :ivar data_limit: + :ivar messaging_enabled: + :ivar voice_enabled: + :ivar national_roaming_enabled: + :ivar international_roaming: + :ivar date_created: + :ivar date_updated: + :ivar url: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.data_enabled: Optional[bool] = payload.get("data_enabled") + self.data_metering: Optional[str] = payload.get("data_metering") + self.data_limit: Optional[int] = deserialize.integer(payload.get("data_limit")) + self.messaging_enabled: Optional[bool] = payload.get("messaging_enabled") + self.voice_enabled: Optional[bool] = payload.get("voice_enabled") + self.national_roaming_enabled: Optional[bool] = payload.get( + "national_roaming_enabled" + ) + self.international_roaming: Optional[List[str]] = payload.get( + "international_roaming" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RatePlanContext] = None + + @property + def _proxy(self) -> "RatePlanContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RatePlanContext for this RatePlanInstance + """ + if self._context is None: + self._context = RatePlanContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RatePlanInstance": + """ + Fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RatePlanInstance": + """ + Asynchronous coroutine to fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "RatePlanInstance": + """ + Update the RatePlanInstance + + :param unique_name: + :param friendly_name: + + :returns: The updated RatePlanInstance + """ + return self._proxy.update( + unique_name=unique_name, + friendly_name=friendly_name, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "RatePlanInstance": + """ + Asynchronous coroutine to update the RatePlanInstance + + :param unique_name: + :param friendly_name: + + :returns: The updated RatePlanInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RatePlanContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RatePlanContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RatePlans/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RatePlanInstance: + """ + Fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RatePlanInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RatePlanInstance: + """ + Asynchronous coroutine to fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RatePlanInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> RatePlanInstance: + """ + Update the RatePlanInstance + + :param unique_name: + :param friendly_name: + + :returns: The updated RatePlanInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> RatePlanInstance: + """ + Asynchronous coroutine to update the RatePlanInstance + + :param unique_name: + :param friendly_name: + + :returns: The updated RatePlanInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RatePlanPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RatePlanInstance: + """ + Build an instance of RatePlanInstance + + :param payload: Payload response from the API + """ + return RatePlanInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RatePlanList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RatePlanList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RatePlans" + + def create( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + data_metering: Union[str, object] = values.unset, + messaging_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + commands_enabled: Union[bool, object] = values.unset, + national_roaming_enabled: Union[bool, object] = values.unset, + international_roaming: Union[List[str], object] = values.unset, + ) -> RatePlanInstance: + """ + Create the RatePlanInstance + + :param unique_name: + :param friendly_name: + :param data_enabled: + :param data_limit: + :param data_metering: + :param messaging_enabled: + :param voice_enabled: + :param commands_enabled: + :param national_roaming_enabled: + :param international_roaming: + + :returns: The created RatePlanInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "DataMetering": data_metering, + "MessagingEnabled": serialize.boolean_to_string(messaging_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "CommandsEnabled": serialize.boolean_to_string(commands_enabled), + "NationalRoamingEnabled": serialize.boolean_to_string( + national_roaming_enabled + ), + "InternationalRoaming": serialize.map( + international_roaming, lambda e: e + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + data_metering: Union[str, object] = values.unset, + messaging_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + commands_enabled: Union[bool, object] = values.unset, + national_roaming_enabled: Union[bool, object] = values.unset, + international_roaming: Union[List[str], object] = values.unset, + ) -> RatePlanInstance: + """ + Asynchronously create the RatePlanInstance + + :param unique_name: + :param friendly_name: + :param data_enabled: + :param data_limit: + :param data_metering: + :param messaging_enabled: + :param voice_enabled: + :param commands_enabled: + :param national_roaming_enabled: + :param international_roaming: + + :returns: The created RatePlanInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "DataMetering": data_metering, + "MessagingEnabled": serialize.boolean_to_string(messaging_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "CommandsEnabled": serialize.boolean_to_string(commands_enabled), + "NationalRoamingEnabled": serialize.boolean_to_string( + national_roaming_enabled + ), + "InternationalRoaming": serialize.map( + international_roaming, lambda e: e + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RatePlanInstance]: + """ + Streams RatePlanInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RatePlanInstance]: + """ + Asynchronously streams RatePlanInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RatePlanInstance]: + """ + Lists RatePlanInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RatePlanInstance]: + """ + Asynchronously lists RatePlanInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RatePlanPage: + """ + Retrieve a single page of RatePlanInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RatePlanInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RatePlanPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RatePlanPage: + """ + Asynchronously retrieve a single page of RatePlanInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RatePlanInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RatePlanPage(self._version, response) + + def get_page(self, target_url: str) -> RatePlanPage: + """ + Retrieve a specific page of RatePlanInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RatePlanInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RatePlanPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RatePlanPage: + """ + Asynchronously retrieve a specific page of RatePlanInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RatePlanInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RatePlanPage(self._version, response) + + def get(self, sid: str) -> RatePlanContext: + """ + Constructs a RatePlanContext + + :param sid: + """ + return RatePlanContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RatePlanContext: + """ + Constructs a RatePlanContext + + :param sid: + """ + return RatePlanContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__init__.py new file mode 100644 index 0000000..8620c03 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__init__.py @@ -0,0 +1,811 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.preview.wireless.sim.usage import UsageList + + +class SimInstance(InstanceResource): + """ + :ivar sid: + :ivar unique_name: + :ivar account_sid: + :ivar rate_plan_sid: + :ivar friendly_name: + :ivar iccid: + :ivar e_id: + :ivar status: + :ivar commands_callback_url: + :ivar commands_callback_method: + :ivar sms_fallback_method: + :ivar sms_fallback_url: + :ivar sms_method: + :ivar sms_url: + :ivar voice_fallback_method: + :ivar voice_fallback_url: + :ivar voice_method: + :ivar voice_url: + :ivar date_created: + :ivar date_updated: + :ivar url: + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.rate_plan_sid: Optional[str] = payload.get("rate_plan_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iccid: Optional[str] = payload.get("iccid") + self.e_id: Optional[str] = payload.get("e_id") + self.status: Optional[str] = payload.get("status") + self.commands_callback_url: Optional[str] = payload.get("commands_callback_url") + self.commands_callback_method: Optional[str] = payload.get( + "commands_callback_method" + ) + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SimContext] = None + + @property + def _proxy(self) -> "SimContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SimContext for this SimInstance + """ + if self._context is None: + self._context = SimContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SimInstance": + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SimInstance": + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Update the SimInstance + + :param unique_name: + :param callback_method: + :param callback_url: + :param friendly_name: + :param rate_plan: + :param status: + :param commands_callback_method: + :param commands_callback_url: + :param sms_fallback_method: + :param sms_fallback_url: + :param sms_method: + :param sms_url: + :param voice_fallback_method: + :param voice_fallback_url: + :param voice_method: + :param voice_url: + + :returns: The updated SimInstance + """ + return self._proxy.update( + unique_name=unique_name, + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + rate_plan=rate_plan, + status=status, + commands_callback_method=commands_callback_method, + commands_callback_url=commands_callback_url, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: + :param callback_method: + :param callback_url: + :param friendly_name: + :param rate_plan: + :param status: + :param commands_callback_method: + :param commands_callback_url: + :param sms_fallback_method: + :param sms_fallback_url: + :param sms_method: + :param sms_url: + :param voice_fallback_method: + :param voice_fallback_url: + :param voice_method: + :param voice_url: + + :returns: The updated SimInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + rate_plan=rate_plan, + status=status, + commands_callback_method=commands_callback_method, + commands_callback_url=commands_callback_url, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + ) + + @property + def usage(self) -> UsageList: + """ + Access the usage + """ + return self._proxy.usage + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SimContext + + :param version: Version that contains the resource + :param sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sims/{sid}".format(**self._solution) + + self._usage: Optional[UsageList] = None + + def fetch(self) -> SimInstance: + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SimInstance: + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Update the SimInstance + + :param unique_name: + :param callback_method: + :param callback_url: + :param friendly_name: + :param rate_plan: + :param status: + :param commands_callback_method: + :param commands_callback_url: + :param sms_fallback_method: + :param sms_fallback_url: + :param sms_method: + :param sms_url: + :param voice_fallback_method: + :param voice_fallback_url: + :param voice_method: + :param voice_url: + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + "RatePlan": rate_plan, + "Status": status, + "CommandsCallbackMethod": commands_callback_method, + "CommandsCallbackUrl": commands_callback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union[str, object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: + :param callback_method: + :param callback_url: + :param friendly_name: + :param rate_plan: + :param status: + :param commands_callback_method: + :param commands_callback_url: + :param sms_fallback_method: + :param sms_fallback_url: + :param sms_method: + :param sms_url: + :param voice_fallback_method: + :param voice_fallback_url: + :param voice_method: + :param voice_url: + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + "RatePlan": rate_plan, + "Status": status, + "CommandsCallbackMethod": commands_callback_method, + "CommandsCallbackUrl": commands_callback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def usage(self) -> UsageList: + """ + Access the usage + """ + if self._usage is None: + self._usage = UsageList( + self._version, + self._solution["sid"], + ) + return self._usage + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SimInstance: + """ + Build an instance of SimInstance + + :param payload: Payload response from the API + """ + return SimInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SimList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SimList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Sims" + + def stream( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SimInstance]: + """ + Streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str status: + :param str iccid: + :param str rate_plan: + :param str e_id: + :param str sim_registration_code: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SimInstance]: + """ + Asynchronously streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str status: + :param str iccid: + :param str rate_plan: + :param str e_id: + :param str sim_registration_code: + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str status: + :param str iccid: + :param str rate_plan: + :param str e_id: + :param str sim_registration_code: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Asynchronously lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str status: + :param str iccid: + :param str rate_plan: + :param str e_id: + :param str sim_registration_code: + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: + :param iccid: + :param rate_plan: + :param e_id: + :param sim_registration_code: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Iccid": iccid, + "RatePlan": rate_plan, + "EId": e_id, + "SimRegistrationCode": sim_registration_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SimPage(self._version, response) + + async def page_async( + self, + status: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Asynchronously retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: + :param iccid: + :param rate_plan: + :param e_id: + :param sim_registration_code: + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Iccid": iccid, + "RatePlan": rate_plan, + "EId": e_id, + "SimRegistrationCode": sim_registration_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SimPage(self._version, response) + + def get_page(self, target_url: str) -> SimPage: + """ + Retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SimPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SimPage: + """ + Asynchronously retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SimPage(self._version, response) + + def get(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: + """ + return SimContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: + """ + return SimContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d069fa8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/usage.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/usage.cpython-310.pyc new file mode 100644 index 0000000..b4bfc40 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/__pycache__/usage.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/usage.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/usage.py new file mode 100644 index 0000000..d90b5f9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview/wireless/sim/usage.py @@ -0,0 +1,239 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Preview + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class UsageInstance(InstanceResource): + """ + :ivar sim_sid: + :ivar sim_unique_name: + :ivar account_sid: + :ivar period: + :ivar commands_usage: + :ivar commands_costs: + :ivar data_usage: + :ivar data_costs: + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sim_sid: str): + super().__init__(version) + + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.sim_unique_name: Optional[str] = payload.get("sim_unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.period: Optional[Dict[str, object]] = payload.get("period") + self.commands_usage: Optional[Dict[str, object]] = payload.get("commands_usage") + self.commands_costs: Optional[Dict[str, object]] = payload.get("commands_costs") + self.data_usage: Optional[Dict[str, object]] = payload.get("data_usage") + self.data_costs: Optional[Dict[str, object]] = payload.get("data_costs") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sim_sid": sim_sid, + } + self._context: Optional[UsageContext] = None + + @property + def _proxy(self) -> "UsageContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: UsageContext for this UsageInstance + """ + if self._context is None: + self._context = UsageContext( + self._version, + sim_sid=self._solution["sim_sid"], + ) + return self._context + + def fetch( + self, + end: Union[str, object] = values.unset, + start: Union[str, object] = values.unset, + ) -> "UsageInstance": + """ + Fetch the UsageInstance + + :param end: + :param start: + + :returns: The fetched UsageInstance + """ + return self._proxy.fetch( + end=end, + start=start, + ) + + async def fetch_async( + self, + end: Union[str, object] = values.unset, + start: Union[str, object] = values.unset, + ) -> "UsageInstance": + """ + Asynchronous coroutine to fetch the UsageInstance + + :param end: + :param start: + + :returns: The fetched UsageInstance + """ + return await self._proxy.fetch_async( + end=end, + start=start, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsageContext(InstanceContext): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the UsageContext + + :param version: Version that contains the resource + :param sim_sid: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + self._uri = "/Sims/{sim_sid}/Usage".format(**self._solution) + + def fetch( + self, + end: Union[str, object] = values.unset, + start: Union[str, object] = values.unset, + ) -> UsageInstance: + """ + Fetch the UsageInstance + + :param end: + :param start: + + :returns: The fetched UsageInstance + """ + + data = values.of( + { + "End": end, + "Start": start, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return UsageInstance( + self._version, + payload, + sim_sid=self._solution["sim_sid"], + ) + + async def fetch_async( + self, + end: Union[str, object] = values.unset, + start: Union[str, object] = values.unset, + ) -> UsageInstance: + """ + Asynchronous coroutine to fetch the UsageInstance + + :param end: + :param start: + + :returns: The fetched UsageInstance + """ + + data = values.of( + { + "End": end, + "Start": start, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return UsageInstance( + self._version, + payload, + sim_sid=self._solution["sim_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsageList(ListResource): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the UsageList + + :param version: Version that contains the resource + :param sim_sid: + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + + def get(self) -> UsageContext: + """ + Constructs a UsageContext + + """ + return UsageContext(self._version, sim_sid=self._solution["sim_sid"]) + + def __call__(self) -> UsageContext: + """ + Constructs a UsageContext + + """ + return UsageContext(self._version, sim_sid=self._solution["sim_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/PreviewMessagingBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/PreviewMessagingBase.py new file mode 100644 index 0000000..12b19f4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/PreviewMessagingBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.preview_messaging.v1 import V1 + + +class PreviewMessagingBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the PreviewMessaging Domain + + :returns: Domain for PreviewMessaging + """ + super().__init__(twilio, "https://preview.messaging.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of PreviewMessaging + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__init__.py new file mode 100644 index 0000000..73bf679 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__init__.py @@ -0,0 +1,13 @@ +from twilio.rest.preview_messaging.PreviewMessagingBase import PreviewMessagingBase +from twilio.rest.preview_messaging.v1.broadcast import BroadcastList +from twilio.rest.preview_messaging.v1.message import MessageList + + +class PreviewMessaging(PreviewMessagingBase): + @property + def broadcast(self) -> BroadcastList: + return self.v1.broadcasts + + @property + def messages(self) -> MessageList: + return self.v1.messages diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/PreviewMessagingBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/PreviewMessagingBase.cpython-310.pyc new file mode 100644 index 0000000..19d2045 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/PreviewMessagingBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8149259 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__init__.py new file mode 100644 index 0000000..424af64 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Bulk Messaging and Broadcast + Bulk Sending is a public Twilio REST API for 1:Many Message creation up to 100 recipients. Broadcast is a public Twilio REST API for 1:Many Message creation up to 10,000 recipients via file upload. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.preview_messaging.v1.broadcast import BroadcastList +from twilio.rest.preview_messaging.v1.message import MessageList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of PreviewMessaging + + :param domain: The Twilio.preview_messaging domain + """ + super().__init__(domain, "v1") + self._broadcasts: Optional[BroadcastList] = None + self._messages: Optional[MessageList] = None + + @property + def broadcasts(self) -> BroadcastList: + if self._broadcasts is None: + self._broadcasts = BroadcastList(self) + return self._broadcasts + + @property + def messages(self) -> MessageList: + if self._messages is None: + self._messages = MessageList(self) + return self._messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..88ff0d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/broadcast.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/broadcast.cpython-310.pyc new file mode 100644 index 0000000..567d980 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/broadcast.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/message.cpython-310.pyc new file mode 100644 index 0000000..3b47ba6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/__pycache__/message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/broadcast.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/broadcast.py new file mode 100644 index 0000000..e41e066 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/broadcast.py @@ -0,0 +1,125 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Bulk Messaging and Broadcast + Bulk Sending is a public Twilio REST API for 1:Many Message creation up to 100 recipients. Broadcast is a public Twilio REST API for 1:Many Message creation up to 10,000 recipients via file upload. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BroadcastInstance(InstanceResource): + """ + :ivar broadcast_sid: Numeric ID indentifying individual Broadcast requests + :ivar created_date: Timestamp of when the Broadcast was created + :ivar updated_date: Timestamp of when the Broadcast was last updated + :ivar broadcast_status: Status of the Broadcast request. Valid values are None, Pending-Upload, Uploaded, Queued, Executing, Execution-Failure, Execution-Completed, Cancelation-Requested, and Canceled + :ivar execution_details: + :ivar results_file: Path to a file detailing successful requests and errors from Broadcast execution + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.broadcast_sid: Optional[str] = payload.get("broadcast_sid") + self.created_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("created_date") + ) + self.updated_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("updated_date") + ) + self.broadcast_status: Optional[str] = payload.get("broadcast_status") + self.execution_details: Optional[str] = payload.get("execution_details") + self.results_file: Optional[str] = payload.get("results_file") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class BroadcastList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BroadcastList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Broadcasts" + + def create( + self, x_twilio_request_key: Union[str, object] = values.unset + ) -> BroadcastInstance: + """ + Create the BroadcastInstance + + :param x_twilio_request_key: Idempotency key provided by the client + + :returns: The created BroadcastInstance + """ + + data = values.of({}) + headers = values.of( + { + "X-Twilio-Request-Key": x_twilio_request_key, + } + ) + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BroadcastInstance(self._version, payload) + + async def create_async( + self, x_twilio_request_key: Union[str, object] = values.unset + ) -> BroadcastInstance: + """ + Asynchronously create the BroadcastInstance + + :param x_twilio_request_key: Idempotency key provided by the client + + :returns: The created BroadcastInstance + """ + + data = values.of({}) + headers = values.of( + { + "X-Twilio-Request-Key": x_twilio_request_key, + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return BroadcastInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/message.py new file mode 100644 index 0000000..0d2822e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/preview_messaging/v1/message.py @@ -0,0 +1,204 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Bulk Messaging and Broadcast + Bulk Sending is a public Twilio REST API for 1:Many Message creation up to 100 recipients. Broadcast is a public Twilio REST API for 1:Many Message creation up to 10,000 recipients via file upload. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional +from twilio.base import deserialize + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class MessageInstance(InstanceResource): + """ + :ivar total_message_count: The number of Messages processed in the request, equal to the sum of success_count and error_count. + :ivar success_count: The number of Messages successfully created. + :ivar error_count: The number of Messages unsuccessfully processed in the request. + :ivar message_receipts: + :ivar failed_message_receipts: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.total_message_count: Optional[int] = deserialize.integer( + payload.get("total_message_count") + ) + self.success_count: Optional[int] = deserialize.integer( + payload.get("success_count") + ) + self.error_count: Optional[int] = deserialize.integer( + payload.get("error_count") + ) + self.message_receipts: Optional[List[str]] = payload.get("message_receipts") + self.failed_message_receipts: Optional[List[str]] = payload.get( + "failed_message_receipts" + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class MessageList(ListResource): + + class CreateMessagesRequest(object): + """ + :ivar messages: + :ivar from_: A Twilio phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, an [alphanumeric sender ID](https://www.twilio.com/docs/sms/send-messages#use-an-alphanumeric-sender-id), or a [Channel Endpoint address](https://www.twilio.com/docs/sms/channels#channel-addresses) that is enabled for the type of message you want to send. Phone numbers or [short codes](https://www.twilio.com/docs/sms/api/short-code) purchased from Twilio also work here. You cannot, for example, spoof messages from a private cell phone number. If you are using `messaging_service_sid`, this parameter must be empty. + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/sms/services#send-a-message-with-copilot) you want to associate with the Message. Set this parameter to use the [Messaging Service Settings and Copilot Features](https://www.twilio.com/console/sms/services) you have configured and leave the `from` parameter empty. When only this parameter is set, Twilio will use your enabled Copilot Features to select the `from` phone number for delivery. + :ivar body: The text of the message you want to send. Can be up to 1,600 characters in length. + :ivar content_sid: The SID of the preconfigured [Content Template](https://www.twilio.com/docs/content-api/create-and-send-your-first-content-api-template#create-a-template) you want to associate with the Message. Must be used in conjuction with a preconfigured [Messaging Service Settings and Copilot Features](https://www.twilio.com/console/sms/services) When this parameter is set, Twilio will use your configured content template and the provided `ContentVariables`. This Twilio product is currently in Private Beta. + :ivar media_url: The URL of the media to send with the message. The media can be of type `gif`, `png`, and `jpeg` and will be formatted correctly on the recipient's device. The media size limit is 5MB for supported file types (JPEG, PNG, GIF) and 500KB for [other types](https://www.twilio.com/docs/sms/accepted-mime-types) of accepted media. To send more than one image in the message body, provide multiple `media_url` parameters in the POST request. You can include up to 10 `media_url` parameters per message. You can send images in an SMS message in only the US and Canada. + :ivar status_callback: The URL we should call using the \"status_callback_method\" to send status information to your application. If specified, we POST these message status changes to the URL - queued, failed, sent, delivered, or undelivered. Twilio will POST its [standard request parameters](https://www.twilio.com/docs/messaging/twiml#request-parameters) as well as some additional parameters including \"MessageSid\", \"MessageStatus\", and \"ErrorCode\". If you include this parameter with the \"messaging_service_sid\", we use this URL instead of the Status Callback URL of the [Messaging Service](https://www.twilio.com/docs/messaging/services/api). URLs must contain a valid hostname and underscores are not allowed. + :ivar validity_period: How long in seconds the message can remain in our outgoing message queue. After this period elapses, the message fails and we call your status callback. Can be between 1 and the default value of 14,400 seconds. After a message has been accepted by a carrier, however, we cannot guarantee that the message will not be queued after this period. We recommend that this value be at least 5 seconds. + :ivar send_at: The time at which Twilio will send the message. This parameter can be used to schedule a message to be sent at a particular time. Must be in ISO 8601 format. + :ivar schedule_type: This parameter indicates your intent to schedule a message. Pass the value `fixed` to schedule a message at a fixed time. This parameter works in conjuction with the `SendAt` parameter. + :ivar shorten_urls: Determines the usage of Click Tracking. Setting it to `true` will instruct Twilio to replace all links in the Message with a shortened version based on the associated Domain Sid and track clicks on them. If this parameter is not set on an API call, we will use the value set on the Messaging Service. If this parameter is not set and the value is not configured on the Messaging Service used this will default to `false`. + :ivar send_as_mms: If set to True, Twilio will deliver the message as a single MMS message, regardless of the presence of media. + :ivar max_price: The maximum total price in US dollars that you will pay for the message to be delivered. Can be a decimal value that has up to 4 decimal places. All messages are queued for delivery and the message cost is checked before the message is sent. If the cost exceeds max_price, the message will fail and a status of Failed is sent to the status callback. If MaxPrice is not set, the message cost is not checked. + :ivar attempt: Total number of attempts made ( including this ) to send out the message regardless of the provider used + :ivar smart_encoded: This parameter indicates whether to detect Unicode characters that have a similar GSM-7 character and replace them. Can be true or false. + :ivar force_delivery: This parameter allows Twilio to send SMS traffic to carriers without checking/caring whether the destination number is a mobile or a landline. + :ivar application_sid: The SID of the application that should receive message status. We POST a message_sid parameter and a message_status parameter with a value of sent or failed to the application's message_status_callback. If a status_callback parameter is also passed, it will be ignored and the application's message_status_callback parameter will be used. + """ + + def __init__(self, payload: Dict[str, Any]): + + self.messages: Optional[List[MessageList.MessagingV1Message]] = payload.get( + "messages" + ) + self.from_: Optional[str] = payload.get("from_") + self.messaging_service_sid: Optional[str] = payload.get( + "messaging_service_sid" + ) + self.body: Optional[str] = payload.get("body") + self.content_sid: Optional[str] = payload.get("content_sid") + self.media_url: Optional[List[str]] = payload.get("media_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.validity_period: Optional[int] = payload.get("validity_period") + self.send_at: Optional[str] = payload.get("send_at") + self.schedule_type: Optional[str] = payload.get("schedule_type") + self.shorten_urls: Optional[bool] = payload.get("shorten_urls") + self.send_as_mms: Optional[bool] = payload.get("send_as_mms") + self.max_price: Optional[float] = payload.get("max_price") + self.attempt: Optional[int] = payload.get("attempt") + self.smart_encoded: Optional[bool] = payload.get("smart_encoded") + self.force_delivery: Optional[bool] = payload.get("force_delivery") + self.application_sid: Optional[str] = payload.get("application_sid") + + def to_dict(self): + return { + "messages": [messages.to_dict() for messages in self.messages], + "from": self.from_, + "messaging_service_sid": self.messaging_service_sid, + "body": self.body, + "content_sid": self.content_sid, + "media_url": self.media_url, + "status_callback": self.status_callback, + "validity_period": self.validity_period, + "send_at": self.send_at, + "schedule_type": self.schedule_type, + "shorten_urls": self.shorten_urls, + "send_as_mms": self.send_as_mms, + "max_price": self.max_price, + "attempt": self.attempt, + "smart_encoded": self.smart_encoded, + "force_delivery": self.force_delivery, + "application_sid": self.application_sid, + } + + class MessagingV1Message(object): + """ + :ivar to: The destination phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format for SMS/MMS or [Channel user address](https://www.twilio.com/docs/sms/channels#channel-addresses) for other 3rd-party channels. + :ivar body: The text of the message you want to send. Can be up to 1,600 characters in length. Overrides the request-level body and content template if provided. + :ivar content_variables: Key-value pairs of variable names to substitution values. Refer to the [Twilio Content API Resources](https://www.twilio.com/docs/content-api/content-api-resources#send-a-message-with-preconfigured-content) for more details. + """ + + def __init__(self, payload: Dict[str, Any]): + + self.to: Optional[str] = payload.get("to") + self.body: Optional[str] = payload.get("body") + self.content_variables: Optional[dict[str, str]] = payload.get( + "content_variables" + ) + + def to_dict(self): + return { + "to": self.to, + "body": self.body, + "content_variables": self.content_variables, + } + + def __init__(self, version: Version): + """ + Initialize the MessageList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Messages" + + def create(self, create_messages_request: CreateMessagesRequest) -> MessageInstance: + """ + Create the MessageInstance + + :param create_messages_request: + + :returns: The created MessageInstance + """ + data = create_messages_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance(self._version, payload) + + async def create_async( + self, create_messages_request: CreateMessagesRequest + ) -> MessageInstance: + """ + Asynchronously create the MessageInstance + + :param create_messages_request: + + :returns: The created MessageInstance + """ + data = create_messages_request.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return MessageInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/PricingBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/PricingBase.py new file mode 100644 index 0000000..cf624e1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/PricingBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.pricing.v1 import V1 +from twilio.rest.pricing.v2 import V2 + + +class PricingBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Pricing Domain + + :returns: Domain for Pricing + """ + super().__init__(twilio, "https://pricing.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Pricing + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Pricing + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__init__.py new file mode 100644 index 0000000..91da875 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__init__.py @@ -0,0 +1,55 @@ +from warnings import warn + +from twilio.rest.pricing.PricingBase import PricingBase +from twilio.rest.pricing.v1.messaging import MessagingList +from twilio.rest.pricing.v1.phone_number import PhoneNumberList +from twilio.rest.pricing.v2.country import CountryList +from twilio.rest.pricing.v2.number import NumberList +from twilio.rest.pricing.v2.voice import VoiceList + + +class Pricing(PricingBase): + @property + def messaging(self) -> MessagingList: + warn( + "messaging is deprecated. Use v1.messaging instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.messaging + + @property + def phone_numbers(self) -> PhoneNumberList: + warn( + "phone_numbers is deprecated. Use v1.phone_numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.phone_numbers + + @property + def voice(self) -> VoiceList: + warn( + "voice is deprecated. Use v2.voice instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.voice + + @property + def countries(self) -> CountryList: + warn( + "countries is deprecated. Use v2.countries instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.countries + + @property + def numbers(self) -> NumberList: + warn( + "numbers is deprecated. Use v2.numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.numbers diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/PricingBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/PricingBase.cpython-310.pyc new file mode 100644 index 0000000..0aa97cb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/PricingBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0262056 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__init__.py new file mode 100644 index 0000000..ba8b658 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.pricing.v1.messaging import MessagingList +from twilio.rest.pricing.v1.phone_number import PhoneNumberList +from twilio.rest.pricing.v1.voice import VoiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Pricing + + :param domain: The Twilio.pricing domain + """ + super().__init__(domain, "v1") + self._messaging: Optional[MessagingList] = None + self._phone_numbers: Optional[PhoneNumberList] = None + self._voice: Optional[VoiceList] = None + + @property + def messaging(self) -> MessagingList: + if self._messaging is None: + self._messaging = MessagingList(self) + return self._messaging + + @property + def phone_numbers(self) -> PhoneNumberList: + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList(self) + return self._phone_numbers + + @property + def voice(self) -> VoiceList: + if self._voice is None: + self._voice = VoiceList(self) + return self._voice + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..cdff834 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__init__.py new file mode 100644 index 0000000..df5c148 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__init__.py @@ -0,0 +1,54 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.pricing.v1.messaging.country import CountryList + + +class MessagingList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the MessagingList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Messaging" + + self._countries: Optional[CountryList] = None + + @property + def countries(self) -> CountryList: + """ + Access the countries + """ + if self._countries is None: + self._countries = CountryList(self._version) + return self._countries + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4b11fe3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/country.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/country.cpython-310.pyc new file mode 100644 index 0000000..f5800d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/__pycache__/country.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/country.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/country.py new file mode 100644 index 0000000..47f5707 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/messaging/country.py @@ -0,0 +1,401 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CountryInstance(InstanceResource): + """ + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar outbound_sms_prices: The list of [OutboundSMSPrice](https://www.twilio.com/docs/sms/api/pricing#outbound-sms-price) records that represent the price to send a message for each MCC/MNC applicable in this country. + :ivar inbound_sms_prices: The list of [InboundPrice](https://www.twilio.com/docs/sms/api/pricing#inbound-price) records that describe the price to receive an inbound SMS to the different Twilio phone number types supported in this country + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + iso_country: Optional[str] = None, + ): + super().__init__(version) + + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.outbound_sms_prices: Optional[List[str]] = payload.get( + "outbound_sms_prices" + ) + self.inbound_sms_prices: Optional[List[str]] = payload.get("inbound_sms_prices") + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "iso_country": iso_country or self.iso_country, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_country=self._solution["iso_country"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_country: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_country": iso_country, + } + self._uri = "/Messaging/Countries/{iso_country}".format(**self._solution) + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Messaging/Countries" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __call__(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__init__.py new file mode 100644 index 0000000..429ce9b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__init__.py @@ -0,0 +1,54 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.pricing.v1.phone_number.country import CountryList + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/PhoneNumbers" + + self._countries: Optional[CountryList] = None + + @property + def countries(self) -> CountryList: + """ + Access the countries + """ + if self._countries is None: + self._countries = CountryList(self._version) + return self._countries + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6686cc6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/country.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/country.cpython-310.pyc new file mode 100644 index 0000000..61fba6f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/__pycache__/country.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/country.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/country.py new file mode 100644 index 0000000..b0822ff --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/phone_number/country.py @@ -0,0 +1,399 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CountryInstance(InstanceResource): + """ + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar phone_number_prices: The list of [PhoneNumberPrice](https://www.twilio.com/docs/phone-numbers/pricing#phone-number-price) records. + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + iso_country: Optional[str] = None, + ): + super().__init__(version) + + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.phone_number_prices: Optional[List[str]] = payload.get( + "phone_number_prices" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "iso_country": iso_country or self.iso_country, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_country=self._solution["iso_country"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_country: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_country": iso_country, + } + self._uri = "/PhoneNumbers/Countries/{iso_country}".format(**self._solution) + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/PhoneNumbers/Countries" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __call__(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__init__.py new file mode 100644 index 0000000..a801a30 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__init__.py @@ -0,0 +1,65 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.pricing.v1.voice.country import CountryList +from twilio.rest.pricing.v1.voice.number import NumberList + + +class VoiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the VoiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Voice" + + self._countries: Optional[CountryList] = None + self._numbers: Optional[NumberList] = None + + @property + def countries(self) -> CountryList: + """ + Access the countries + """ + if self._countries is None: + self._countries = CountryList(self._version) + return self._countries + + @property + def numbers(self) -> NumberList: + """ + Access the numbers + """ + if self._numbers is None: + self._numbers = NumberList(self._version) + return self._numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f11a61d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/country.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/country.cpython-310.pyc new file mode 100644 index 0000000..ee216fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/country.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/number.cpython-310.pyc new file mode 100644 index 0000000..1626dec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/__pycache__/number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/country.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/country.py new file mode 100644 index 0000000..e3b1496 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/country.py @@ -0,0 +1,403 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CountryInstance(InstanceResource): + """ + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar outbound_prefix_prices: The list of OutboundPrefixPrice records, which include a list of the `prefixes`, the `friendly_name`, `base_price`, and the `current_price` for those prefixes. + :ivar inbound_call_prices: The list of [InboundCallPrice](https://www.twilio.com/docs/voice/pricing#inbound-call-price) records. + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + iso_country: Optional[str] = None, + ): + super().__init__(version) + + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.outbound_prefix_prices: Optional[List[str]] = payload.get( + "outbound_prefix_prices" + ) + self.inbound_call_prices: Optional[List[str]] = payload.get( + "inbound_call_prices" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "iso_country": iso_country or self.iso_country, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_country=self._solution["iso_country"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_country: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_country": iso_country, + } + self._uri = "/Voice/Countries/{iso_country}".format(**self._solution) + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Voice/Countries" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __call__(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/number.py new file mode 100644 index 0000000..9109197 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v1/voice/number.py @@ -0,0 +1,192 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NumberInstance(InstanceResource): + """ + :ivar number: The phone number. + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar outbound_call_price: + :ivar inbound_call_price: + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](http://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], number: Optional[str] = None + ): + super().__init__(version) + + self.number: Optional[str] = payload.get("number") + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.outbound_call_price: Optional[str] = payload.get("outbound_call_price") + self.inbound_call_price: Optional[str] = payload.get("inbound_call_price") + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "number": number or self.number, + } + self._context: Optional[NumberContext] = None + + @property + def _proxy(self) -> "NumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NumberContext for this NumberInstance + """ + if self._context is None: + self._context = NumberContext( + self._version, + number=self._solution["number"], + ) + return self._context + + def fetch(self) -> "NumberInstance": + """ + Fetch the NumberInstance + + + :returns: The fetched NumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NumberInstance": + """ + Asynchronous coroutine to fetch the NumberInstance + + + :returns: The fetched NumberInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberContext(InstanceContext): + + def __init__(self, version: Version, number: str): + """ + Initialize the NumberContext + + :param version: Version that contains the resource + :param number: The phone number to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "number": number, + } + self._uri = "/Voice/Numbers/{number}".format(**self._solution) + + def fetch(self) -> NumberInstance: + """ + Fetch the NumberInstance + + + :returns: The fetched NumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NumberInstance( + self._version, + payload, + number=self._solution["number"], + ) + + async def fetch_async(self) -> NumberInstance: + """ + Asynchronous coroutine to fetch the NumberInstance + + + :returns: The fetched NumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NumberInstance( + self._version, + payload, + number=self._solution["number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the NumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param number: The phone number to fetch. + """ + return NumberContext(self._version, number=number) + + def __call__(self, number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param number: The phone number to fetch. + """ + return NumberContext(self._version, number=number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__init__.py new file mode 100644 index 0000000..d0fd67f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.pricing.v2.country import CountryList +from twilio.rest.pricing.v2.number import NumberList +from twilio.rest.pricing.v2.voice import VoiceList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Pricing + + :param domain: The Twilio.pricing domain + """ + super().__init__(domain, "v2") + self._countries: Optional[CountryList] = None + self._numbers: Optional[NumberList] = None + self._voice: Optional[VoiceList] = None + + @property + def countries(self) -> CountryList: + if self._countries is None: + self._countries = CountryList(self) + return self._countries + + @property + def numbers(self) -> NumberList: + if self._numbers is None: + self._numbers = NumberList(self) + return self._numbers + + @property + def voice(self) -> VoiceList: + if self._voice is None: + self._voice = VoiceList(self) + return self._voice + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..5aa4502 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/country.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/country.cpython-310.pyc new file mode 100644 index 0000000..1f8ddc1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/country.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/number.cpython-310.pyc new file mode 100644 index 0000000..1a7eaf8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/__pycache__/number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/country.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/country.py new file mode 100644 index 0000000..a058c07 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/country.py @@ -0,0 +1,403 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CountryInstance(InstanceResource): + """ + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar terminating_prefix_prices: The list of [TerminatingPrefixPrice](https://www.twilio.com/docs/voice/pricing#outbound-prefix-price-with-origin) records. + :ivar originating_call_prices: The list of [OriginatingCallPrice](https://www.twilio.com/docs/voice/pricing#inbound-call-price) records. + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + iso_country: Optional[str] = None, + ): + super().__init__(version) + + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.terminating_prefix_prices: Optional[List[str]] = payload.get( + "terminating_prefix_prices" + ) + self.originating_call_prices: Optional[List[str]] = payload.get( + "originating_call_prices" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "iso_country": iso_country or self.iso_country, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_country=self._solution["iso_country"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_country: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_country": iso_country, + } + self._uri = "/Trunking/Countries/{iso_country}".format(**self._solution) + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Trunking/Countries" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __call__(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/number.py new file mode 100644 index 0000000..6c4cd9f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/number.py @@ -0,0 +1,226 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NumberInstance(InstanceResource): + """ + :ivar destination_number: The destination phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origination_number: The origination phone number in [[E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :ivar terminating_prefix_prices: + :ivar originating_call_price: + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + destination_number: Optional[str] = None, + ): + super().__init__(version) + + self.destination_number: Optional[str] = payload.get("destination_number") + self.origination_number: Optional[str] = payload.get("origination_number") + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.terminating_prefix_prices: Optional[List[str]] = payload.get( + "terminating_prefix_prices" + ) + self.originating_call_price: Optional[str] = payload.get( + "originating_call_price" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "destination_number": destination_number or self.destination_number, + } + self._context: Optional[NumberContext] = None + + @property + def _proxy(self) -> "NumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NumberContext for this NumberInstance + """ + if self._context is None: + self._context = NumberContext( + self._version, + destination_number=self._solution["destination_number"], + ) + return self._context + + def fetch( + self, origination_number: Union[str, object] = values.unset + ) -> "NumberInstance": + """ + Fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + return self._proxy.fetch( + origination_number=origination_number, + ) + + async def fetch_async( + self, origination_number: Union[str, object] = values.unset + ) -> "NumberInstance": + """ + Asynchronous coroutine to fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + return await self._proxy.fetch_async( + origination_number=origination_number, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberContext(InstanceContext): + + def __init__(self, version: Version, destination_number: str): + """ + Initialize the NumberContext + + :param version: Version that contains the resource + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "destination_number": destination_number, + } + self._uri = "/Trunking/Numbers/{destination_number}".format(**self._solution) + + def fetch( + self, origination_number: Union[str, object] = values.unset + ) -> NumberInstance: + """ + Fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + + data = values.of( + { + "OriginationNumber": origination_number, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return NumberInstance( + self._version, + payload, + destination_number=self._solution["destination_number"], + ) + + async def fetch_async( + self, origination_number: Union[str, object] = values.unset + ) -> NumberInstance: + """ + Asynchronous coroutine to fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + + data = values.of( + { + "OriginationNumber": origination_number, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return NumberInstance( + self._version, + payload, + destination_number=self._solution["destination_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the NumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, destination_number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + return NumberContext(self._version, destination_number=destination_number) + + def __call__(self, destination_number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + return NumberContext(self._version, destination_number=destination_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__init__.py new file mode 100644 index 0000000..1ebbcbb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__init__.py @@ -0,0 +1,65 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.pricing.v2.voice.country import CountryList +from twilio.rest.pricing.v2.voice.number import NumberList + + +class VoiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the VoiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Voice" + + self._countries: Optional[CountryList] = None + self._numbers: Optional[NumberList] = None + + @property + def countries(self) -> CountryList: + """ + Access the countries + """ + if self._countries is None: + self._countries = CountryList(self._version) + return self._countries + + @property + def numbers(self) -> NumberList: + """ + Access the numbers + """ + if self._numbers is None: + self._numbers = NumberList(self._version) + return self._numbers + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c157d8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/country.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/country.cpython-310.pyc new file mode 100644 index 0000000..3836776 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/country.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/number.cpython-310.pyc new file mode 100644 index 0000000..3e95e3f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/__pycache__/number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/country.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/country.py new file mode 100644 index 0000000..4a84bbd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/country.py @@ -0,0 +1,403 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CountryInstance(InstanceResource): + """ + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar outbound_prefix_prices: The list of [OutboundPrefixPriceWithOrigin](https://www.twilio.com/docs/voice/pricing#outbound-prefix-price-with-origin) records. + :ivar inbound_call_prices: The list of [InboundCallPrice](https://www.twilio.com/docs/voice/pricing#inbound-call-price) records. + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + iso_country: Optional[str] = None, + ): + super().__init__(version) + + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.outbound_prefix_prices: Optional[List[str]] = payload.get( + "outbound_prefix_prices" + ) + self.inbound_call_prices: Optional[List[str]] = payload.get( + "inbound_call_prices" + ) + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "iso_country": iso_country or self.iso_country, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_country=self._solution["iso_country"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_country: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_country": iso_country, + } + self._uri = "/Voice/Countries/{iso_country}".format(**self._solution) + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_country=self._solution["iso_country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Voice/Countries" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __call__(self, iso_country: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the origin-based voice pricing information to fetch. + """ + return CountryContext(self._version, iso_country=iso_country) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/number.py new file mode 100644 index 0000000..6dd8764 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/pricing/v2/voice/number.py @@ -0,0 +1,224 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Pricing + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NumberInstance(InstanceResource): + """ + :ivar destination_number: The destination phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar origination_number: The origination phone number in [[E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar country: The name of the country. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :ivar outbound_call_prices: The list of [OutboundCallPriceWithOrigin](https://www.twilio.com/docs/voice/pricing#outbound-call-price-with-origin) records. + :ivar inbound_call_price: + :ivar price_unit: The currency in which prices are measured, specified in [ISO 4127](https://www.iso.org/iso/home/standards/currency_codes.htm) format (e.g. `usd`, `eur`, `jpy`). + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + destination_number: Optional[str] = None, + ): + super().__init__(version) + + self.destination_number: Optional[str] = payload.get("destination_number") + self.origination_number: Optional[str] = payload.get("origination_number") + self.country: Optional[str] = payload.get("country") + self.iso_country: Optional[str] = payload.get("iso_country") + self.outbound_call_prices: Optional[List[str]] = payload.get( + "outbound_call_prices" + ) + self.inbound_call_price: Optional[str] = payload.get("inbound_call_price") + self.price_unit: Optional[str] = payload.get("price_unit") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "destination_number": destination_number or self.destination_number, + } + self._context: Optional[NumberContext] = None + + @property + def _proxy(self) -> "NumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NumberContext for this NumberInstance + """ + if self._context is None: + self._context = NumberContext( + self._version, + destination_number=self._solution["destination_number"], + ) + return self._context + + def fetch( + self, origination_number: Union[str, object] = values.unset + ) -> "NumberInstance": + """ + Fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + return self._proxy.fetch( + origination_number=origination_number, + ) + + async def fetch_async( + self, origination_number: Union[str, object] = values.unset + ) -> "NumberInstance": + """ + Asynchronous coroutine to fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + return await self._proxy.fetch_async( + origination_number=origination_number, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberContext(InstanceContext): + + def __init__(self, version: Version, destination_number: str): + """ + Initialize the NumberContext + + :param version: Version that contains the resource + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "destination_number": destination_number, + } + self._uri = "/Voice/Numbers/{destination_number}".format(**self._solution) + + def fetch( + self, origination_number: Union[str, object] = values.unset + ) -> NumberInstance: + """ + Fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + + data = values.of( + { + "OriginationNumber": origination_number, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return NumberInstance( + self._version, + payload, + destination_number=self._solution["destination_number"], + ) + + async def fetch_async( + self, origination_number: Union[str, object] = values.unset + ) -> NumberInstance: + """ + Asynchronous coroutine to fetch the NumberInstance + + :param origination_number: The origination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + + :returns: The fetched NumberInstance + """ + + data = values.of( + { + "OriginationNumber": origination_number, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return NumberInstance( + self._version, + payload, + destination_number=self._solution["destination_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the NumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, destination_number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + return NumberContext(self._version, destination_number=destination_number) + + def __call__(self, destination_number: str) -> NumberContext: + """ + Constructs a NumberContext + + :param destination_number: The destination phone number, in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, for which to fetch the origin-based voice pricing information. E.164 format consists of a + followed by the country code and subscriber number. + """ + return NumberContext(self._version, destination_number=destination_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/ProxyBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/ProxyBase.py new file mode 100644 index 0000000..da1baec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/ProxyBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.proxy.v1 import V1 + + +class ProxyBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Proxy Domain + + :returns: Domain for Proxy + """ + super().__init__(twilio, "https://proxy.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Proxy + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__init__.py new file mode 100644 index 0000000..aec2a3d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.proxy.ProxyBase import ProxyBase +from twilio.rest.proxy.v1.service import ServiceList + + +class Proxy(ProxyBase): + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/ProxyBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/ProxyBase.cpython-310.pyc new file mode 100644 index 0000000..273d395 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/ProxyBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9dedf81 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__init__.py new file mode 100644 index 0000000..83737dc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.proxy.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Proxy + + :param domain: The Twilio.proxy domain + """ + super().__init__(domain, "v1") + self._services: Optional[ServiceList] = None + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0b18a40 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__init__.py new file mode 100644 index 0000000..6be2def --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__init__.py @@ -0,0 +1,814 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.proxy.v1.service.phone_number import PhoneNumberList +from twilio.rest.proxy.v1.service.session import SessionList +from twilio.rest.proxy.v1.service.short_code import ShortCodeList + + +class ServiceInstance(InstanceResource): + + class GeoMatchLevel(object): + AREA_CODE = "area-code" + OVERLAY = "overlay" + RADIUS = "radius" + COUNTRY = "country" + + class NumberSelectionBehavior(object): + AVOID_STICKY = "avoid-sticky" + PREFER_STICKY = "prefer-sticky" + + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. Supports UTF-8 characters. **This value should not have PII.** + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + :ivar callback_url: The URL we call when the interaction status changes. + :ivar default_ttl: The default `ttl` value for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :ivar number_selection_behavior: + :ivar geo_match_level: + :ivar intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :ivar out_of_session_callback_url: The URL we call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar url: The absolute URL of the Service resource. + :ivar links: The URLs of resources related to the Service. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.chat_instance_sid: Optional[str] = payload.get("chat_instance_sid") + self.callback_url: Optional[str] = payload.get("callback_url") + self.default_ttl: Optional[int] = deserialize.integer( + payload.get("default_ttl") + ) + self.number_selection_behavior: Optional[ + "ServiceInstance.NumberSelectionBehavior" + ] = payload.get("number_selection_behavior") + self.geo_match_level: Optional["ServiceInstance.GeoMatchLevel"] = payload.get( + "geo_match_level" + ) + self.intercept_callback_url: Optional[str] = payload.get( + "intercept_callback_url" + ) + self.out_of_session_callback_url: Optional[str] = payload.get( + "out_of_session_callback_url" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + unique_name=unique_name, + default_ttl=default_ttl, + callback_url=callback_url, + geo_match_level=geo_match_level, + number_selection_behavior=number_selection_behavior, + intercept_callback_url=intercept_callback_url, + out_of_session_callback_url=out_of_session_callback_url, + chat_instance_sid=chat_instance_sid, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + default_ttl=default_ttl, + callback_url=callback_url, + geo_match_level=geo_match_level, + number_selection_behavior=number_selection_behavior, + intercept_callback_url=intercept_callback_url, + out_of_session_callback_url=out_of_session_callback_url, + chat_instance_sid=chat_instance_sid, + ) + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + return self._proxy.phone_numbers + + @property + def sessions(self) -> SessionList: + """ + Access the sessions + """ + return self._proxy.sessions + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + return self._proxy.short_codes + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._phone_numbers: Optional[PhoneNumberList] = None + self._sessions: Optional[SessionList] = None + self._short_codes: Optional[ShortCodeList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "DefaultTtl": default_ttl, + "CallbackUrl": callback_url, + "GeoMatchLevel": geo_match_level, + "NumberSelectionBehavior": number_selection_behavior, + "InterceptCallbackUrl": intercept_callback_url, + "OutOfSessionCallbackUrl": out_of_session_callback_url, + "ChatInstanceSid": chat_instance_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "DefaultTtl": default_ttl, + "CallbackUrl": callback_url, + "GeoMatchLevel": geo_match_level, + "NumberSelectionBehavior": number_selection_behavior, + "InterceptCallbackUrl": intercept_callback_url, + "OutOfSessionCallbackUrl": out_of_session_callback_url, + "ChatInstanceSid": chat_instance_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList( + self._version, + self._solution["sid"], + ) + return self._phone_numbers + + @property + def sessions(self) -> SessionList: + """ + Access the sessions + """ + if self._sessions is None: + self._sessions = SessionList( + self._version, + self._solution["sid"], + ) + return self._sessions + + @property + def short_codes(self) -> ShortCodeList: + """ + Access the short_codes + """ + if self._short_codes is None: + self._short_codes = ShortCodeList( + self._version, + self._solution["sid"], + ) + return self._short_codes + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + unique_name: str, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DefaultTtl": default_ttl, + "CallbackUrl": callback_url, + "GeoMatchLevel": geo_match_level, + "NumberSelectionBehavior": number_selection_behavior, + "InterceptCallbackUrl": intercept_callback_url, + "OutOfSessionCallbackUrl": out_of_session_callback_url, + "ChatInstanceSid": chat_instance_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + unique_name: str, + default_ttl: Union[int, object] = values.unset, + callback_url: Union[str, object] = values.unset, + geo_match_level: Union["ServiceInstance.GeoMatchLevel", object] = values.unset, + number_selection_behavior: Union[ + "ServiceInstance.NumberSelectionBehavior", object + ] = values.unset, + intercept_callback_url: Union[str, object] = values.unset, + out_of_session_callback_url: Union[str, object] = values.unset, + chat_instance_sid: Union[str, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param default_ttl: The default `ttl` value to set for Sessions created in the Service. The TTL (time to live) is measured in seconds after the Session's last create or last Interaction. The default value of `0` indicates an unlimited Session length. You can override a Session's default TTL value by setting its `ttl` value. + :param callback_url: The URL we should call when the interaction status changes. + :param geo_match_level: + :param number_selection_behavior: + :param intercept_callback_url: The URL we call on each interaction. If we receive a 403 status, we block the interaction; otherwise the interaction continues. + :param out_of_session_callback_url: The URL we should call when an inbound call or SMS action occurs on a closed or non-existent Session. If your server (or a Twilio [function](https://www.twilio.com/en-us/serverless/functions)) responds with valid [TwiML](https://www.twilio.com/docs/voice/twiml), we will process it. This means it is possible, for example, to play a message for a call, send an automated text message response, or redirect a call to another Phone Number. See [Out-of-Session Callback Response Guide](https://www.twilio.com/docs/proxy/out-session-callback-response-guide) for more information. + :param chat_instance_sid: The SID of the Chat Service Instance managed by Proxy Service. The Chat Service enables Proxy to forward SMS and channel messages to this chat instance. This is a one-to-one relationship. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DefaultTtl": default_ttl, + "CallbackUrl": callback_url, + "GeoMatchLevel": geo_match_level, + "NumberSelectionBehavior": number_selection_behavior, + "InterceptCallbackUrl": intercept_callback_url, + "OutOfSessionCallbackUrl": out_of_session_callback_url, + "ChatInstanceSid": chat_instance_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e3c2548 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..8d9eb1e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/short_code.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/short_code.cpython-310.pyc new file mode 100644 index 0000000..752772a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/__pycache__/short_code.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/phone_number.py new file mode 100644 index 0000000..366b9ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/phone_number.py @@ -0,0 +1,632 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PhoneNumberInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the PhoneNumber resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the PhoneNumber resource. + :ivar service_sid: The SID of the PhoneNumber resource's parent [Service](https://www.twilio.com/docs/proxy/api/service) resource. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar iso_country: The ISO Country Code for the phone number. + :ivar capabilities: + :ivar url: The absolute URL of the PhoneNumber resource. + :ivar is_reserved: Whether the phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + :ivar in_use: The number of open session assigned to the number. See the [How many Phone Numbers do I need?](https://www.twilio.com/docs/proxy/phone-numbers-needed) guide for more information. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.phone_number: Optional[str] = payload.get("phone_number") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iso_country: Optional[str] = payload.get("iso_country") + self.capabilities: Optional[str] = payload.get("capabilities") + self.url: Optional[str] = payload.get("url") + self.is_reserved: Optional[bool] = payload.get("is_reserved") + self.in_use: Optional[int] = deserialize.integer(payload.get("in_use")) + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, is_reserved: Union[bool, object] = values.unset + ) -> "PhoneNumberInstance": + """ + Update the PhoneNumberInstance + + :param is_reserved: Whether the phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated PhoneNumberInstance + """ + return self._proxy.update( + is_reserved=is_reserved, + ) + + async def update_async( + self, is_reserved: Union[bool, object] = values.unset + ) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to update the PhoneNumberInstance + + :param is_reserved: Whether the phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated PhoneNumberInstance + """ + return await self._proxy.update_async( + is_reserved=is_reserved, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the PhoneNumber resource to update. + :param sid: The Twilio-provided string that uniquely identifies the PhoneNumber resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/PhoneNumbers/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, is_reserved: Union[bool, object] = values.unset + ) -> PhoneNumberInstance: + """ + Update the PhoneNumberInstance + + :param is_reserved: Whether the phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated PhoneNumberInstance + """ + data = values.of( + { + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, is_reserved: Union[bool, object] = values.unset + ) -> PhoneNumberInstance: + """ + Asynchronous coroutine to update the PhoneNumberInstance + + :param is_reserved: Whether the phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated PhoneNumberInstance + """ + data = values.of( + { + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PhoneNumberInstance: + """ + Build an instance of PhoneNumberInstance + + :param payload: Payload response from the API + """ + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the PhoneNumber resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/PhoneNumbers".format(**self._solution) + + def create( + self, + sid: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + is_reserved: Union[bool, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Create the PhoneNumberInstance + + :param sid: The SID of a Twilio [IncomingPhoneNumber](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) resource that represents the Twilio Number you would like to assign to your Proxy Service. + :param phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param is_reserved: Whether the new phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "Sid": sid, + "PhoneNumber": phone_number, + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + sid: Union[str, object] = values.unset, + phone_number: Union[str, object] = values.unset, + is_reserved: Union[bool, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Asynchronously create the PhoneNumberInstance + + :param sid: The SID of a Twilio [IncomingPhoneNumber](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) resource that represents the Twilio Number you would like to assign to your Proxy Service. + :param phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format. E.164 phone numbers consist of a + followed by the country code and subscriber number without punctuation characters. For example, +14155551234. + :param is_reserved: Whether the new phone number should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "Sid": sid, + "PhoneNumber": phone_number, + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PhoneNumberInstance]: + """ + Streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PhoneNumberInstance]: + """ + Asynchronously streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Asynchronously lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PhoneNumberPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Asynchronously retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PhoneNumberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PhoneNumberPage: + """ + Retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PhoneNumberPage: + """ + Asynchronously retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + def get(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The Twilio-provided string that uniquely identifies the PhoneNumber resource to update. + """ + return PhoneNumberContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The Twilio-provided string that uniquely identifies the PhoneNumber resource to update. + """ + return PhoneNumberContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__init__.py new file mode 100644 index 0000000..26ff177 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__init__.py @@ -0,0 +1,754 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.proxy.v1.service.session.interaction import InteractionList +from twilio.rest.proxy.v1.service.session.participant import ParticipantList + + +class SessionInstance(InstanceResource): + + class Mode(object): + MESSAGE_ONLY = "message-only" + VOICE_ONLY = "voice-only" + VOICE_AND_MESSAGE = "voice-and-message" + + class Status(object): + OPEN = "open" + IN_PROGRESS = "in-progress" + CLOSED = "closed" + FAILED = "failed" + UNKNOWN = "unknown" + + """ + :ivar sid: The unique string that we created to identify the Session resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/proxy/api/service) the session is associated with. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Session resource. + :ivar date_started: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session started. + :ivar date_ended: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session ended. + :ivar date_last_interaction: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session last had an interaction. + :ivar date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :ivar unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. Supports UTF-8 characters. **This value should not have PII.** + :ivar status: + :ivar closed_reason: The reason the Session ended. + :ivar ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :ivar mode: + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar url: The absolute URL of the Session resource. + :ivar links: The URLs of resources related to the Session. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_started: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_started") + ) + self.date_ended: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_ended") + ) + self.date_last_interaction: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_last_interaction") + ) + self.date_expiry: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expiry") + ) + self.unique_name: Optional[str] = payload.get("unique_name") + self.status: Optional["SessionInstance.Status"] = payload.get("status") + self.closed_reason: Optional[str] = payload.get("closed_reason") + self.ttl: Optional[int] = deserialize.integer(payload.get("ttl")) + self.mode: Optional["SessionInstance.Mode"] = payload.get("mode") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SessionContext] = None + + @property + def _proxy(self) -> "SessionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SessionContext for this SessionInstance + """ + if self._context is None: + self._context = SessionContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SessionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SessionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SessionInstance": + """ + Fetch the SessionInstance + + + :returns: The fetched SessionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SessionInstance": + """ + Asynchronous coroutine to fetch the SessionInstance + + + :returns: The fetched SessionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + ) -> "SessionInstance": + """ + Update the SessionInstance + + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param status: + + :returns: The updated SessionInstance + """ + return self._proxy.update( + date_expiry=date_expiry, + ttl=ttl, + status=status, + ) + + async def update_async( + self, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + ) -> "SessionInstance": + """ + Asynchronous coroutine to update the SessionInstance + + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param status: + + :returns: The updated SessionInstance + """ + return await self._proxy.update_async( + date_expiry=date_expiry, + ttl=ttl, + status=status, + ) + + @property + def interactions(self) -> InteractionList: + """ + Access the interactions + """ + return self._proxy.interactions + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SessionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SessionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to update. + :param sid: The Twilio-provided string that uniquely identifies the Session resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Sessions/{sid}".format(**self._solution) + + self._interactions: Optional[InteractionList] = None + self._participants: Optional[ParticipantList] = None + + def delete(self) -> bool: + """ + Deletes the SessionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SessionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SessionInstance: + """ + Fetch the SessionInstance + + + :returns: The fetched SessionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SessionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SessionInstance: + """ + Asynchronous coroutine to fetch the SessionInstance + + + :returns: The fetched SessionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SessionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + ) -> SessionInstance: + """ + Update the SessionInstance + + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param status: + + :returns: The updated SessionInstance + """ + data = values.of( + { + "DateExpiry": serialize.iso8601_datetime(date_expiry), + "Ttl": ttl, + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SessionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + ) -> SessionInstance: + """ + Asynchronous coroutine to update the SessionInstance + + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param status: + + :returns: The updated SessionInstance + """ + data = values.of( + { + "DateExpiry": serialize.iso8601_datetime(date_expiry), + "Ttl": ttl, + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SessionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def interactions(self) -> InteractionList: + """ + Access the interactions + """ + if self._interactions is None: + self._interactions = InteractionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._interactions + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._participants + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SessionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SessionInstance: + """ + Build an instance of SessionInstance + + :param payload: Payload response from the API + """ + return SessionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SessionList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SessionList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Sessions".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + mode: Union["SessionInstance.Mode", object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + participants: Union[List[object], object] = values.unset, + ) -> SessionInstance: + """ + Create the SessionInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param mode: + :param status: + :param participants: The Participant objects to include in the new session. + + :returns: The created SessionInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DateExpiry": serialize.iso8601_datetime(date_expiry), + "Ttl": ttl, + "Mode": mode, + "Status": status, + "Participants": serialize.map( + participants, lambda e: serialize.object(e) + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SessionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + date_expiry: Union[datetime, object] = values.unset, + ttl: Union[int, object] = values.unset, + mode: Union["SessionInstance.Mode", object] = values.unset, + status: Union["SessionInstance.Status", object] = values.unset, + participants: Union[List[object], object] = values.unset, + ) -> SessionInstance: + """ + Asynchronously create the SessionInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be 191 characters or fewer in length and be unique. **This value should not have PII.** + :param date_expiry: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Session should expire. If this is value is present, it overrides the `ttl` value. + :param ttl: The time, in seconds, when the session will expire. The time is measured from the last Session create or the Session's last Interaction. + :param mode: + :param status: + :param participants: The Participant objects to include in the new session. + + :returns: The created SessionInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DateExpiry": serialize.iso8601_datetime(date_expiry), + "Ttl": ttl, + "Mode": mode, + "Status": status, + "Participants": serialize.map( + participants, lambda e: serialize.object(e) + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SessionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SessionInstance]: + """ + Streams SessionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SessionInstance]: + """ + Asynchronously streams SessionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SessionInstance]: + """ + Lists SessionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SessionInstance]: + """ + Asynchronously lists SessionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SessionPage: + """ + Retrieve a single page of SessionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SessionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SessionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SessionPage: + """ + Asynchronously retrieve a single page of SessionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SessionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SessionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SessionPage: + """ + Retrieve a specific page of SessionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SessionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SessionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SessionPage: + """ + Asynchronously retrieve a specific page of SessionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SessionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SessionPage(self._version, response, self._solution) + + def get(self, sid: str) -> SessionContext: + """ + Constructs a SessionContext + + :param sid: The Twilio-provided string that uniquely identifies the Session resource to update. + """ + return SessionContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SessionContext: + """ + Constructs a SessionContext + + :param sid: The Twilio-provided string that uniquely identifies the Session resource to update. + """ + return SessionContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7acd18c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/interaction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/interaction.cpython-310.pyc new file mode 100644 index 0000000..a326251 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/__pycache__/interaction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/interaction.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/interaction.py new file mode 100644 index 0000000..6e4cac7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/interaction.py @@ -0,0 +1,555 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class InteractionInstance(InstanceResource): + + class ResourceStatus(object): + ACCEPTED = "accepted" + ANSWERED = "answered" + BUSY = "busy" + CANCELED = "canceled" + COMPLETED = "completed" + DELETED = "deleted" + DELIVERED = "delivered" + DELIVERY_UNKNOWN = "delivery-unknown" + FAILED = "failed" + IN_PROGRESS = "in-progress" + INITIATED = "initiated" + NO_ANSWER = "no-answer" + QUEUED = "queued" + RECEIVED = "received" + RECEIVING = "receiving" + RINGING = "ringing" + SCHEDULED = "scheduled" + SENDING = "sending" + SENT = "sent" + UNDELIVERED = "undelivered" + UNKNOWN = "unknown" + + class Type(object): + MESSAGE = "message" + VOICE = "voice" + UNKNOWN = "unknown" + + """ + :ivar sid: The unique string that we created to identify the Interaction resource. + :ivar session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) resource. + :ivar service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Interaction resource. + :ivar data: A JSON string that includes the message body of message interactions (e.g. `{\"body\": \"hello\"}`) or the call duration (when available) of a call (e.g. `{\"duration\": \"5\"}`). + :ivar type: + :ivar inbound_participant_sid: The SID of the inbound [Participant](https://www.twilio.com/docs/proxy/api/participant) resource. + :ivar inbound_resource_sid: The SID of the inbound resource; either the [Call](https://www.twilio.com/docs/voice/api/call-resource) or [Message](https://www.twilio.com/docs/sms/api/message-resource). + :ivar inbound_resource_status: + :ivar inbound_resource_type: The inbound resource type. Can be [Call](https://www.twilio.com/docs/voice/api/call-resource) or [Message](https://www.twilio.com/docs/sms/api/message-resource). + :ivar inbound_resource_url: The URL of the Twilio inbound resource + :ivar outbound_participant_sid: The SID of the outbound [Participant](https://www.twilio.com/docs/proxy/api/participant)). + :ivar outbound_resource_sid: The SID of the outbound resource; either the [Call](https://www.twilio.com/docs/voice/api/call-resource) or [Message](https://www.twilio.com/docs/sms/api/message-resource). + :ivar outbound_resource_status: + :ivar outbound_resource_type: The outbound resource type. Can be: [Call](https://www.twilio.com/docs/voice/api/call-resource) or [Message](https://www.twilio.com/docs/sms/api/message-resource). + :ivar outbound_resource_url: The URL of the Twilio outbound resource. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the Interaction was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar url: The absolute URL of the Interaction resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + session_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.session_sid: Optional[str] = payload.get("session_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.data: Optional[str] = payload.get("data") + self.type: Optional["InteractionInstance.Type"] = payload.get("type") + self.inbound_participant_sid: Optional[str] = payload.get( + "inbound_participant_sid" + ) + self.inbound_resource_sid: Optional[str] = payload.get("inbound_resource_sid") + self.inbound_resource_status: Optional["InteractionInstance.ResourceStatus"] = ( + payload.get("inbound_resource_status") + ) + self.inbound_resource_type: Optional[str] = payload.get("inbound_resource_type") + self.inbound_resource_url: Optional[str] = payload.get("inbound_resource_url") + self.outbound_participant_sid: Optional[str] = payload.get( + "outbound_participant_sid" + ) + self.outbound_resource_sid: Optional[str] = payload.get("outbound_resource_sid") + self.outbound_resource_status: Optional[ + "InteractionInstance.ResourceStatus" + ] = payload.get("outbound_resource_status") + self.outbound_resource_type: Optional[str] = payload.get( + "outbound_resource_type" + ) + self.outbound_resource_url: Optional[str] = payload.get("outbound_resource_url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "sid": sid or self.sid, + } + self._context: Optional[InteractionContext] = None + + @property + def _proxy(self) -> "InteractionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: InteractionContext for this InteractionInstance + """ + if self._context is None: + self._context = InteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the InteractionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InteractionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "InteractionInstance": + """ + Fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "InteractionInstance": + """ + Asynchronous coroutine to fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, session_sid: str, sid: str): + """ + Initialize the InteractionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to fetch. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) of the resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Interaction resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Sessions/{session_sid}/Interactions/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the InteractionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the InteractionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> InteractionInstance: + """ + Fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return InteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> InteractionInstance: + """ + Asynchronous coroutine to fetch the InteractionInstance + + + :returns: The fetched InteractionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return InteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class InteractionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> InteractionInstance: + """ + Build an instance of InteractionInstance + + :param payload: Payload response from the API + """ + return InteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class InteractionList(ListResource): + + def __init__(self, version: Version, service_sid: str, session_sid: str): + """ + Initialize the InteractionList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) to read the resources from. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + } + self._uri = ( + "/Services/{service_sid}/Sessions/{session_sid}/Interactions".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[InteractionInstance]: + """ + Streams InteractionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[InteractionInstance]: + """ + Asynchronously streams InteractionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionInstance]: + """ + Lists InteractionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[InteractionInstance]: + """ + Asynchronously lists InteractionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionPage: + """ + Retrieve a single page of InteractionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return InteractionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> InteractionPage: + """ + Asynchronously retrieve a single page of InteractionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of InteractionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return InteractionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> InteractionPage: + """ + Retrieve a specific page of InteractionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return InteractionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> InteractionPage: + """ + Asynchronously retrieve a specific page of InteractionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of InteractionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return InteractionPage(self._version, response, self._solution) + + def get(self, sid: str) -> InteractionContext: + """ + Constructs a InteractionContext + + :param sid: The Twilio-provided string that uniquely identifies the Interaction resource to fetch. + """ + return InteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> InteractionContext: + """ + Constructs a InteractionContext + + :param sid: The Twilio-provided string that uniquely identifies the Interaction resource to fetch. + """ + return InteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__init__.py new file mode 100644 index 0000000..266396f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__init__.py @@ -0,0 +1,612 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.proxy.v1.service.session.participant.message_interaction import ( + MessageInteractionList, +) + + +class ParticipantInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Participant resource. + :ivar session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) resource. + :ivar service_sid: The SID of the resource's parent [Service](https://www.twilio.com/docs/proxy/api/service) resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Participant resource. + :ivar friendly_name: The string that you assigned to describe the participant. This value must be 255 characters or fewer. Supports UTF-8 characters. **This value should not have PII.** + :ivar identifier: The phone number or channel identifier of the Participant. This value must be 191 characters or fewer. Supports UTF-8 characters. + :ivar proxy_identifier: The phone number or short code (masked number) of the participant's partner. The participant will call or message the partner participant at this number. + :ivar proxy_identifier_sid: The SID of the Proxy Identifier assigned to the Participant. + :ivar date_deleted: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date when the Participant was removed from the session. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar url: The absolute URL of the Participant resource. + :ivar links: The URLs to resources related the participant. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + session_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.session_sid: Optional[str] = payload.get("session_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.identifier: Optional[str] = payload.get("identifier") + self.proxy_identifier: Optional[str] = payload.get("proxy_identifier") + self.proxy_identifier_sid: Optional[str] = payload.get("proxy_identifier_sid") + self.date_deleted: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_deleted") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "sid": sid or self.sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + @property + def message_interactions(self) -> MessageInteractionList: + """ + Access the message_interactions + """ + return self._proxy.message_interactions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, session_sid: str, sid: str): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to fetch. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) of the resource to fetch. + :param sid: The Twilio-provided string that uniquely identifies the Participant resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Sessions/{session_sid}/Participants/{sid}".format( + **self._solution + ) + ) + + self._message_interactions: Optional[MessageInteractionList] = None + + def delete(self) -> bool: + """ + Deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ParticipantInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=self._solution["sid"], + ) + + @property + def message_interactions(self) -> MessageInteractionList: + """ + Access the message_interactions + """ + if self._message_interactions is None: + self._message_interactions = MessageInteractionList( + self._version, + self._solution["service_sid"], + self._solution["session_sid"], + self._solution["sid"], + ) + return self._message_interactions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, service_sid: str, session_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resources to read. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) of the resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + } + self._uri = ( + "/Services/{service_sid}/Sessions/{session_sid}/Participants".format( + **self._solution + ) + ) + + def create( + self, + identifier: str, + friendly_name: Union[str, object] = values.unset, + proxy_identifier: Union[str, object] = values.unset, + proxy_identifier_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Create the ParticipantInstance + + :param identifier: The phone number of the Participant. + :param friendly_name: The string that you assigned to describe the participant. This value must be 255 characters or fewer. **This value should not have PII.** + :param proxy_identifier: The proxy phone number to use for the Participant. If not specified, Proxy will select a number from the pool. + :param proxy_identifier_sid: The SID of the Proxy Identifier to assign to the Participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identifier": identifier, + "FriendlyName": friendly_name, + "ProxyIdentifier": proxy_identifier, + "ProxyIdentifierSid": proxy_identifier_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + ) + + async def create_async( + self, + identifier: str, + friendly_name: Union[str, object] = values.unset, + proxy_identifier: Union[str, object] = values.unset, + proxy_identifier_sid: Union[str, object] = values.unset, + ) -> ParticipantInstance: + """ + Asynchronously create the ParticipantInstance + + :param identifier: The phone number of the Participant. + :param friendly_name: The string that you assigned to describe the participant. This value must be 255 characters or fewer. **This value should not have PII.** + :param proxy_identifier: The proxy phone number to use for the Participant. If not specified, Proxy will select a number from the pool. + :param proxy_identifier_sid: The SID of the Proxy Identifier to assign to the Participant. + + :returns: The created ParticipantInstance + """ + + data = values.of( + { + "Identifier": identifier, + "FriendlyName": friendly_name, + "ProxyIdentifier": proxy_identifier, + "ProxyIdentifierSid": proxy_identifier_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: The Twilio-provided string that uniquely identifies the Participant resource to fetch. + """ + return ParticipantContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: The Twilio-provided string that uniquely identifies the Participant resource to fetch. + """ + return ParticipantContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2f9ef73 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/message_interaction.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/message_interaction.cpython-310.pyc new file mode 100644 index 0000000..7dcbd88 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/__pycache__/message_interaction.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/message_interaction.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/message_interaction.py new file mode 100644 index 0000000..b260b46 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/session/participant/message_interaction.py @@ -0,0 +1,602 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessageInteractionInstance(InstanceResource): + + class ResourceStatus(object): + ACCEPTED = "accepted" + ANSWERED = "answered" + BUSY = "busy" + CANCELED = "canceled" + COMPLETED = "completed" + DELETED = "deleted" + DELIVERED = "delivered" + DELIVERY_UNKNOWN = "delivery-unknown" + FAILED = "failed" + IN_PROGRESS = "in-progress" + INITIATED = "initiated" + NO_ANSWER = "no-answer" + QUEUED = "queued" + RECEIVED = "received" + RECEIVING = "receiving" + RINGING = "ringing" + SCHEDULED = "scheduled" + SENDING = "sending" + SENT = "sent" + UNDELIVERED = "undelivered" + UNKNOWN = "unknown" + + class Type(object): + MESSAGE = "message" + VOICE = "voice" + UNKNOWN = "unknown" + + """ + :ivar sid: The unique string that we created to identify the MessageInteraction resource. + :ivar session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) resource. + :ivar service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the MessageInteraction resource. + :ivar data: A JSON string that includes the message body sent to the participant. (e.g. `{\"body\": \"hello\"}`) + :ivar type: + :ivar participant_sid: The SID of the [Participant](https://www.twilio.com/docs/proxy/api/participant) resource. + :ivar inbound_participant_sid: Always empty for created Message Interactions. + :ivar inbound_resource_sid: Always empty for created Message Interactions. + :ivar inbound_resource_status: + :ivar inbound_resource_type: Always empty for created Message Interactions. + :ivar inbound_resource_url: Always empty for created Message Interactions. + :ivar outbound_participant_sid: The SID of the outbound [Participant](https://www.twilio.com/docs/proxy/api/participant) resource. + :ivar outbound_resource_sid: The SID of the outbound [Message](https://www.twilio.com/docs/sms/api/message-resource) resource. + :ivar outbound_resource_status: + :ivar outbound_resource_type: The outbound resource type. This value is always `Message`. + :ivar outbound_resource_url: The URL of the Twilio message resource. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar url: The absolute URL of the MessageInteraction resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + session_sid: str, + participant_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.session_sid: Optional[str] = payload.get("session_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.data: Optional[str] = payload.get("data") + self.type: Optional["MessageInteractionInstance.Type"] = payload.get("type") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.inbound_participant_sid: Optional[str] = payload.get( + "inbound_participant_sid" + ) + self.inbound_resource_sid: Optional[str] = payload.get("inbound_resource_sid") + self.inbound_resource_status: Optional[ + "MessageInteractionInstance.ResourceStatus" + ] = payload.get("inbound_resource_status") + self.inbound_resource_type: Optional[str] = payload.get("inbound_resource_type") + self.inbound_resource_url: Optional[str] = payload.get("inbound_resource_url") + self.outbound_participant_sid: Optional[str] = payload.get( + "outbound_participant_sid" + ) + self.outbound_resource_sid: Optional[str] = payload.get("outbound_resource_sid") + self.outbound_resource_status: Optional[ + "MessageInteractionInstance.ResourceStatus" + ] = payload.get("outbound_resource_status") + self.outbound_resource_type: Optional[str] = payload.get( + "outbound_resource_type" + ) + self.outbound_resource_url: Optional[str] = payload.get("outbound_resource_url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "participant_sid": participant_sid, + "sid": sid or self.sid, + } + self._context: Optional[MessageInteractionContext] = None + + @property + def _proxy(self) -> "MessageInteractionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessageInteractionContext for this MessageInteractionInstance + """ + if self._context is None: + self._context = MessageInteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "MessageInteractionInstance": + """ + Fetch the MessageInteractionInstance + + + :returns: The fetched MessageInteractionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessageInteractionInstance": + """ + Asynchronous coroutine to fetch the MessageInteractionInstance + + + :returns: The fetched MessageInteractionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageInteractionContext(InstanceContext): + + def __init__( + self, + version: Version, + service_sid: str, + session_sid: str, + participant_sid: str, + sid: str, + ): + """ + Initialize the MessageInteractionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to fetch. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) of the resource to fetch. + :param participant_sid: The SID of the [Participant](https://www.twilio.com/docs/proxy/api/participant) resource. + :param sid: The Twilio-provided string that uniquely identifies the MessageInteraction resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "participant_sid": participant_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Sessions/{session_sid}/Participants/{participant_sid}/MessageInteractions/{sid}".format( + **self._solution + ) + + def fetch(self) -> MessageInteractionInstance: + """ + Fetch the MessageInteractionInstance + + + :returns: The fetched MessageInteractionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessageInteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> MessageInteractionInstance: + """ + Asynchronous coroutine to fetch the MessageInteractionInstance + + + :returns: The fetched MessageInteractionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessageInteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessageInteractionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessageInteractionInstance: + """ + Build an instance of MessageInteractionInstance + + :param payload: Payload response from the API + """ + return MessageInteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessageInteractionList(ListResource): + + def __init__( + self, version: Version, service_sid: str, session_sid: str, participant_sid: str + ): + """ + Initialize the MessageInteractionList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) to read the resources from. + :param session_sid: The SID of the parent [Session](https://www.twilio.com/docs/proxy/api/session) to read the resources from. + :param participant_sid: The SID of the [Participant](https://www.twilio.com/docs/proxy/api/participant) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "session_sid": session_sid, + "participant_sid": participant_sid, + } + self._uri = "/Services/{service_sid}/Sessions/{session_sid}/Participants/{participant_sid}/MessageInteractions".format( + **self._solution + ) + + def create( + self, + body: Union[str, object] = values.unset, + media_url: Union[List[str], object] = values.unset, + ) -> MessageInteractionInstance: + """ + Create the MessageInteractionInstance + + :param body: The message to send to the participant + :param media_url: Reserved. Not currently supported. + + :returns: The created MessageInteractionInstance + """ + + data = values.of( + { + "Body": body, + "MediaUrl": serialize.map(media_url, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + ) + + async def create_async( + self, + body: Union[str, object] = values.unset, + media_url: Union[List[str], object] = values.unset, + ) -> MessageInteractionInstance: + """ + Asynchronously create the MessageInteractionInstance + + :param body: The message to send to the participant + :param media_url: Reserved. Not currently supported. + + :returns: The created MessageInteractionInstance + """ + + data = values.of( + { + "Body": body, + "MediaUrl": serialize.map(media_url, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessageInteractionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessageInteractionInstance]: + """ + Streams MessageInteractionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessageInteractionInstance]: + """ + Asynchronously streams MessageInteractionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInteractionInstance]: + """ + Lists MessageInteractionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessageInteractionInstance]: + """ + Asynchronously lists MessageInteractionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessageInteractionPage: + """ + Retrieve a single page of MessageInteractionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInteractionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessageInteractionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessageInteractionPage: + """ + Asynchronously retrieve a single page of MessageInteractionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessageInteractionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessageInteractionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessageInteractionPage: + """ + Retrieve a specific page of MessageInteractionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInteractionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessageInteractionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessageInteractionPage: + """ + Asynchronously retrieve a specific page of MessageInteractionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessageInteractionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessageInteractionPage(self._version, response, self._solution) + + def get(self, sid: str) -> MessageInteractionContext: + """ + Constructs a MessageInteractionContext + + :param sid: The Twilio-provided string that uniquely identifies the MessageInteraction resource to fetch. + """ + return MessageInteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> MessageInteractionContext: + """ + Constructs a MessageInteractionContext + + :param sid: The Twilio-provided string that uniquely identifies the MessageInteraction resource to fetch. + """ + return MessageInteractionContext( + self._version, + service_sid=self._solution["service_sid"], + session_sid=self._solution["session_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/short_code.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/short_code.py new file mode 100644 index 0000000..712d6f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/proxy/v1/service/short_code.py @@ -0,0 +1,608 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Proxy + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ShortCodeInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the ShortCode resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ShortCode resource. + :ivar service_sid: The SID of the ShortCode resource's parent [Service](https://www.twilio.com/docs/proxy/api/service) resource. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the resource was last updated. + :ivar short_code: The short code's number. + :ivar iso_country: The ISO Country Code for the short code. + :ivar capabilities: + :ivar url: The absolute URL of the ShortCode resource. + :ivar is_reserved: Whether the short code should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.short_code: Optional[str] = payload.get("short_code") + self.iso_country: Optional[str] = payload.get("iso_country") + self.capabilities: Optional[str] = payload.get("capabilities") + self.url: Optional[str] = payload.get("url") + self.is_reserved: Optional[bool] = payload.get("is_reserved") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[ShortCodeContext] = None + + @property + def _proxy(self) -> "ShortCodeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ShortCodeContext for this ShortCodeInstance + """ + if self._context is None: + self._context = ShortCodeContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ShortCodeInstance": + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ShortCodeInstance": + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + return await self._proxy.fetch_async() + + def update( + self, is_reserved: Union[bool, object] = values.unset + ) -> "ShortCodeInstance": + """ + Update the ShortCodeInstance + + :param is_reserved: Whether the short code should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated ShortCodeInstance + """ + return self._proxy.update( + is_reserved=is_reserved, + ) + + async def update_async( + self, is_reserved: Union[bool, object] = values.unset + ) -> "ShortCodeInstance": + """ + Asynchronous coroutine to update the ShortCodeInstance + + :param is_reserved: Whether the short code should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated ShortCodeInstance + """ + return await self._proxy.update_async( + is_reserved=is_reserved, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodeContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the ShortCodeContext + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) of the resource to update. + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/ShortCodes/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ShortCodeInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ShortCodeInstance: + """ + Fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ShortCodeInstance: + """ + Asynchronous coroutine to fetch the ShortCodeInstance + + + :returns: The fetched ShortCodeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, is_reserved: Union[bool, object] = values.unset + ) -> ShortCodeInstance: + """ + Update the ShortCodeInstance + + :param is_reserved: Whether the short code should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated ShortCodeInstance + """ + data = values.of( + { + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, is_reserved: Union[bool, object] = values.unset + ) -> ShortCodeInstance: + """ + Asynchronous coroutine to update the ShortCodeInstance + + :param is_reserved: Whether the short code should be reserved and not be assigned to a participant using proxy pool logic. See [Reserved Phone Numbers](https://www.twilio.com/docs/proxy/reserved-phone-numbers) for more information. + + :returns: The updated ShortCodeInstance + """ + data = values.of( + { + "IsReserved": serialize.boolean_to_string(is_reserved), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ShortCodePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ShortCodeInstance: + """ + Build an instance of ShortCodeInstance + + :param payload: Payload response from the API + """ + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ShortCodeList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the ShortCodeList + + :param version: Version that contains the resource + :param service_sid: The SID of the parent [Service](https://www.twilio.com/docs/proxy/api/service) to read the resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/ShortCodes".format(**self._solution) + + def create(self, sid: str) -> ShortCodeInstance: + """ + Create the ShortCodeInstance + + :param sid: The SID of a Twilio [ShortCode](https://www.twilio.com/en-us/messaging/channels/sms/short-codes) resource that represents the short code you would like to assign to your Proxy Service. + + :returns: The created ShortCodeInstance + """ + + data = values.of( + { + "Sid": sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, sid: str) -> ShortCodeInstance: + """ + Asynchronously create the ShortCodeInstance + + :param sid: The SID of a Twilio [ShortCode](https://www.twilio.com/en-us/messaging/channels/sms/short-codes) resource that represents the short code you would like to assign to your Proxy Service. + + :returns: The created ShortCodeInstance + """ + + data = values.of( + { + "Sid": sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ShortCodeInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ShortCodeInstance]: + """ + Streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ShortCodeInstance]: + """ + Asynchronously streams ShortCodeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ShortCodeInstance]: + """ + Asynchronously lists ShortCodeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ShortCodePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ShortCodePage: + """ + Asynchronously retrieve a single page of ShortCodeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ShortCodeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ShortCodePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ShortCodePage: + """ + Retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ShortCodePage: + """ + Asynchronously retrieve a specific page of ShortCodeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ShortCodeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ShortCodePage(self._version, response, self._solution) + + def get(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update. + """ + return ShortCodeContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ShortCodeContext: + """ + Constructs a ShortCodeContext + + :param sid: The Twilio-provided string that uniquely identifies the ShortCode resource to update. + """ + return ShortCodeContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/RoutesBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/RoutesBase.py new file mode 100644 index 0000000..a47d67f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/RoutesBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.routes.v2 import V2 + + +class RoutesBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Routes Domain + + :returns: Domain for Routes + """ + super().__init__(twilio, "https://routes.twilio.com") + self._v2: Optional[V2] = None + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Routes + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__init__.py new file mode 100644 index 0000000..a559ba0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__init__.py @@ -0,0 +1,35 @@ +from warnings import warn + +from twilio.rest.routes.RoutesBase import RoutesBase +from twilio.rest.routes.v2.phone_number import PhoneNumberList +from twilio.rest.routes.v2.sip_domain import SipDomainList +from twilio.rest.routes.v2.trunk import TrunkList + + +class Routes(RoutesBase): + @property + def phone_numbers(self) -> PhoneNumberList: + warn( + "phone_numbers is deprecated. Use v2.phone_numbers instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.phone_numbers + + @property + def sip_domains(self) -> SipDomainList: + warn( + "sip_domains is deprecated. Use v2.sip_domains instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.sip_domains + + @property + def trunks(self) -> TrunkList: + warn( + "trunks is deprecated. Use v2.trunks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.trunks diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/RoutesBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/RoutesBase.cpython-310.pyc new file mode 100644 index 0000000..bfca0ce Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/RoutesBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a285be8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__init__.py new file mode 100644 index 0000000..56a140e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__init__.py @@ -0,0 +1,59 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Routes + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.routes.v2.phone_number import PhoneNumberList +from twilio.rest.routes.v2.sip_domain import SipDomainList +from twilio.rest.routes.v2.trunk import TrunkList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Routes + + :param domain: The Twilio.routes domain + """ + super().__init__(domain, "v2") + self._phone_numbers: Optional[PhoneNumberList] = None + self._sip_domains: Optional[SipDomainList] = None + self._trunks: Optional[TrunkList] = None + + @property + def phone_numbers(self) -> PhoneNumberList: + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList(self) + return self._phone_numbers + + @property + def sip_domains(self) -> SipDomainList: + if self._sip_domains is None: + self._sip_domains = SipDomainList(self) + return self._sip_domains + + @property + def trunks(self) -> TrunkList: + if self._trunks is None: + self._trunks = TrunkList(self) + return self._trunks + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4cb7cfd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..6da3399 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/sip_domain.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/sip_domain.cpython-310.pyc new file mode 100644 index 0000000..ab070ec Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/sip_domain.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/trunk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/trunk.cpython-310.pyc new file mode 100644 index 0000000..41138d8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/__pycache__/trunk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/phone_number.py new file mode 100644 index 0000000..4f452e3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/phone_number.py @@ -0,0 +1,299 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Routes + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class PhoneNumberInstance(InstanceResource): + """ + :ivar phone_number: The phone number in E.164 format + :ivar url: The absolute URL of the resource. + :ivar sid: A 34 character string that uniquely identifies the Inbound Processing Region assignments for this phone number. + :ivar account_sid: The unique SID identifier of the Account. + :ivar friendly_name: A human readable description of the Inbound Processing Region assignments for this phone number, up to 64 characters. + :ivar voice_region: The Inbound Processing Region used for this phone number for voice. + :ivar date_created: The date that this phone number was assigned an Inbound Processing Region, given in ISO 8601 format. + :ivar date_updated: The date that the Inbound Processing Region was updated for this phone number, given in ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + phone_number: Optional[str] = None, + ): + super().__init__(version) + + self.phone_number: Optional[str] = payload.get("phone_number") + self.url: Optional[str] = payload.get("url") + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.voice_region: Optional[str] = payload.get("voice_region") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "phone_number": phone_number or self.phone_number, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + phone_number=self._solution["phone_number"], + ) + return self._context + + def fetch(self) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Update the PhoneNumberInstance + + :param voice_region: The Inbound Processing Region used for this phone number for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated PhoneNumberInstance + """ + return self._proxy.update( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to update the PhoneNumberInstance + + :param voice_region: The Inbound Processing Region used for this phone number for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated PhoneNumberInstance + """ + return await self._proxy.update_async( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, phone_number: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param phone_number: The phone number in E.164 format + """ + super().__init__(version) + + # Path Solution + self._solution = { + "phone_number": phone_number, + } + self._uri = "/PhoneNumbers/{phone_number}".format(**self._solution) + + def fetch(self) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + async def fetch_async(self) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Update the PhoneNumberInstance + + :param voice_region: The Inbound Processing Region used for this phone number for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated PhoneNumberInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, phone_number=self._solution["phone_number"] + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> PhoneNumberInstance: + """ + Asynchronous coroutine to update the PhoneNumberInstance + + :param voice_region: The Inbound Processing Region used for this phone number for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated PhoneNumberInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, phone_number=self._solution["phone_number"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number in E.164 format + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __call__(self, phone_number: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param phone_number: The phone number in E.164 format + """ + return PhoneNumberContext(self._version, phone_number=phone_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/sip_domain.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/sip_domain.py new file mode 100644 index 0000000..cad3a49 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/sip_domain.py @@ -0,0 +1,299 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Routes + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SipDomainInstance(InstanceResource): + """ + :ivar sip_domain: + :ivar url: + :ivar sid: + :ivar account_sid: + :ivar friendly_name: + :ivar voice_region: + :ivar date_created: + :ivar date_updated: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + sip_domain: Optional[str] = None, + ): + super().__init__(version) + + self.sip_domain: Optional[str] = payload.get("sip_domain") + self.url: Optional[str] = payload.get("url") + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.voice_region: Optional[str] = payload.get("voice_region") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "sip_domain": sip_domain or self.sip_domain, + } + self._context: Optional[SipDomainContext] = None + + @property + def _proxy(self) -> "SipDomainContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SipDomainContext for this SipDomainInstance + """ + if self._context is None: + self._context = SipDomainContext( + self._version, + sip_domain=self._solution["sip_domain"], + ) + return self._context + + def fetch(self) -> "SipDomainInstance": + """ + Fetch the SipDomainInstance + + + :returns: The fetched SipDomainInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SipDomainInstance": + """ + Asynchronous coroutine to fetch the SipDomainInstance + + + :returns: The fetched SipDomainInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "SipDomainInstance": + """ + Update the SipDomainInstance + + :param voice_region: + :param friendly_name: + + :returns: The updated SipDomainInstance + """ + return self._proxy.update( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "SipDomainInstance": + """ + Asynchronous coroutine to update the SipDomainInstance + + :param voice_region: + :param friendly_name: + + :returns: The updated SipDomainInstance + """ + return await self._proxy.update_async( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SipDomainContext(InstanceContext): + + def __init__(self, version: Version, sip_domain: str): + """ + Initialize the SipDomainContext + + :param version: Version that contains the resource + :param sip_domain: + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sip_domain": sip_domain, + } + self._uri = "/SipDomains/{sip_domain}".format(**self._solution) + + def fetch(self) -> SipDomainInstance: + """ + Fetch the SipDomainInstance + + + :returns: The fetched SipDomainInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SipDomainInstance( + self._version, + payload, + sip_domain=self._solution["sip_domain"], + ) + + async def fetch_async(self) -> SipDomainInstance: + """ + Asynchronous coroutine to fetch the SipDomainInstance + + + :returns: The fetched SipDomainInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SipDomainInstance( + self._version, + payload, + sip_domain=self._solution["sip_domain"], + ) + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> SipDomainInstance: + """ + Update the SipDomainInstance + + :param voice_region: + :param friendly_name: + + :returns: The updated SipDomainInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SipDomainInstance( + self._version, payload, sip_domain=self._solution["sip_domain"] + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> SipDomainInstance: + """ + Asynchronous coroutine to update the SipDomainInstance + + :param voice_region: + :param friendly_name: + + :returns: The updated SipDomainInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SipDomainInstance( + self._version, payload, sip_domain=self._solution["sip_domain"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SipDomainList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SipDomainList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sip_domain: str) -> SipDomainContext: + """ + Constructs a SipDomainContext + + :param sip_domain: + """ + return SipDomainContext(self._version, sip_domain=sip_domain) + + def __call__(self, sip_domain: str) -> SipDomainContext: + """ + Constructs a SipDomainContext + + :param sip_domain: + """ + return SipDomainContext(self._version, sip_domain=sip_domain) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/trunk.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/trunk.py new file mode 100644 index 0000000..2baf278 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/routes/v2/trunk.py @@ -0,0 +1,299 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Routes + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TrunkInstance(InstanceResource): + """ + :ivar sip_trunk_domain: The absolute URL of the SIP Trunk + :ivar url: The absolute URL of the resource. + :ivar sid: A 34 character string that uniquely identifies the Inbound Processing Region assignments for this SIP Trunk. + :ivar account_sid: The unique SID identifier of the Account. + :ivar friendly_name: A human readable description of the Inbound Processing Region assignments for this SIP Trunk, up to 64 characters. + :ivar voice_region: The Inbound Processing Region used for this SIP Trunk for voice. + :ivar date_created: The date that this SIP Trunk was assigned an Inbound Processing Region, given in ISO 8601 format. + :ivar date_updated: The date that the Inbound Processing Region was updated for this SIP Trunk, given in ISO 8601 format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + sip_trunk_domain: Optional[str] = None, + ): + super().__init__(version) + + self.sip_trunk_domain: Optional[str] = payload.get("sip_trunk_domain") + self.url: Optional[str] = payload.get("url") + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.voice_region: Optional[str] = payload.get("voice_region") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "sip_trunk_domain": sip_trunk_domain or self.sip_trunk_domain, + } + self._context: Optional[TrunkContext] = None + + @property + def _proxy(self) -> "TrunkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrunkContext for this TrunkInstance + """ + if self._context is None: + self._context = TrunkContext( + self._version, + sip_trunk_domain=self._solution["sip_trunk_domain"], + ) + return self._context + + def fetch(self) -> "TrunkInstance": + """ + Fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrunkInstance": + """ + Asynchronous coroutine to fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "TrunkInstance": + """ + Update the TrunkInstance + + :param voice_region: The Inbound Processing Region used for this SIP Trunk for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated TrunkInstance + """ + return self._proxy.update( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "TrunkInstance": + """ + Asynchronous coroutine to update the TrunkInstance + + :param voice_region: The Inbound Processing Region used for this SIP Trunk for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated TrunkInstance + """ + return await self._proxy.update_async( + voice_region=voice_region, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrunkContext(InstanceContext): + + def __init__(self, version: Version, sip_trunk_domain: str): + """ + Initialize the TrunkContext + + :param version: Version that contains the resource + :param sip_trunk_domain: The absolute URL of the SIP Trunk + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sip_trunk_domain": sip_trunk_domain, + } + self._uri = "/Trunks/{sip_trunk_domain}".format(**self._solution) + + def fetch(self) -> TrunkInstance: + """ + Fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrunkInstance( + self._version, + payload, + sip_trunk_domain=self._solution["sip_trunk_domain"], + ) + + async def fetch_async(self) -> TrunkInstance: + """ + Asynchronous coroutine to fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrunkInstance( + self._version, + payload, + sip_trunk_domain=self._solution["sip_trunk_domain"], + ) + + def update( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> TrunkInstance: + """ + Update the TrunkInstance + + :param voice_region: The Inbound Processing Region used for this SIP Trunk for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated TrunkInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance( + self._version, payload, sip_trunk_domain=self._solution["sip_trunk_domain"] + ) + + async def update_async( + self, + voice_region: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> TrunkInstance: + """ + Asynchronous coroutine to update the TrunkInstance + + :param voice_region: The Inbound Processing Region used for this SIP Trunk for voice + :param friendly_name: A human readable description of this resource, up to 64 characters. + + :returns: The updated TrunkInstance + """ + data = values.of( + { + "VoiceRegion": voice_region, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance( + self._version, payload, sip_trunk_domain=self._solution["sip_trunk_domain"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrunkList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TrunkList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, sip_trunk_domain: str) -> TrunkContext: + """ + Constructs a TrunkContext + + :param sip_trunk_domain: The absolute URL of the SIP Trunk + """ + return TrunkContext(self._version, sip_trunk_domain=sip_trunk_domain) + + def __call__(self, sip_trunk_domain: str) -> TrunkContext: + """ + Constructs a TrunkContext + + :param sip_trunk_domain: The absolute URL of the SIP Trunk + """ + return TrunkContext(self._version, sip_trunk_domain=sip_trunk_domain) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/ServerlessBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/ServerlessBase.py new file mode 100644 index 0000000..d6e227c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/ServerlessBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.serverless.v1 import V1 + + +class ServerlessBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Serverless Domain + + :returns: Domain for Serverless + """ + super().__init__(twilio, "https://serverless.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Serverless + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__init__.py new file mode 100644 index 0000000..a9f32ed --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.serverless.ServerlessBase import ServerlessBase +from twilio.rest.serverless.v1.service import ServiceList + + +class Serverless(ServerlessBase): + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/ServerlessBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/ServerlessBase.cpython-310.pyc new file mode 100644 index 0000000..9079866 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/ServerlessBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..292b07c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__init__.py new file mode 100644 index 0000000..e494615 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.serverless.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Serverless + + :param domain: The Twilio.serverless domain + """ + super().__init__(domain, "v1") + self._services: Optional[ServiceList] = None + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0e31c38 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__init__.py new file mode 100644 index 0000000..0fe404c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__init__.py @@ -0,0 +1,712 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.asset import AssetList +from twilio.rest.serverless.v1.service.build import BuildList +from twilio.rest.serverless.v1.service.environment import EnvironmentList +from twilio.rest.serverless.v1.service.function import FunctionList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the Service resource. + :ivar unique_name: A user-defined string that uniquely identifies the Service resource. It can be used in place of the Service resource's `sid` in the URL to address the Service resource. + :ivar include_credentials: Whether to inject Account credentials into a function invocation context. + :ivar ui_editable: Whether the Service resource's properties and subresources can be edited via the UI. + :ivar domain_base: The base domain name for this Service, which is a combination of the unique name and a randomly generated string. + :ivar date_created: The date and time in GMT when the Service resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Service resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Service resource. + :ivar links: The URLs of the Service's nested resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.unique_name: Optional[str] = payload.get("unique_name") + self.include_credentials: Optional[bool] = payload.get("include_credentials") + self.ui_editable: Optional[bool] = payload.get("ui_editable") + self.domain_base: Optional[str] = payload.get("domain_base") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + include_credentials: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param include_credentials: Whether to inject Account credentials into a function invocation context. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param ui_editable: Whether the Service resource's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + include_credentials=include_credentials, + friendly_name=friendly_name, + ui_editable=ui_editable, + ) + + async def update_async( + self, + include_credentials: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param include_credentials: Whether to inject Account credentials into a function invocation context. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param ui_editable: Whether the Service resource's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + include_credentials=include_credentials, + friendly_name=friendly_name, + ui_editable=ui_editable, + ) + + @property + def assets(self) -> AssetList: + """ + Access the assets + """ + return self._proxy.assets + + @property + def builds(self) -> BuildList: + """ + Access the builds + """ + return self._proxy.builds + + @property + def environments(self) -> EnvironmentList: + """ + Access the environments + """ + return self._proxy.environments + + @property + def functions(self) -> FunctionList: + """ + Access the functions + """ + return self._proxy.functions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The `sid` or `unique_name` of the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._assets: Optional[AssetList] = None + self._builds: Optional[BuildList] = None + self._environments: Optional[EnvironmentList] = None + self._functions: Optional[FunctionList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + include_credentials: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param include_credentials: Whether to inject Account credentials into a function invocation context. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param ui_editable: Whether the Service resource's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "IncludeCredentials": serialize.boolean_to_string(include_credentials), + "FriendlyName": friendly_name, + "UiEditable": serialize.boolean_to_string(ui_editable), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + include_credentials: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param include_credentials: Whether to inject Account credentials into a function invocation context. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param ui_editable: Whether the Service resource's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "IncludeCredentials": serialize.boolean_to_string(include_credentials), + "FriendlyName": friendly_name, + "UiEditable": serialize.boolean_to_string(ui_editable), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def assets(self) -> AssetList: + """ + Access the assets + """ + if self._assets is None: + self._assets = AssetList( + self._version, + self._solution["sid"], + ) + return self._assets + + @property + def builds(self) -> BuildList: + """ + Access the builds + """ + if self._builds is None: + self._builds = BuildList( + self._version, + self._solution["sid"], + ) + return self._builds + + @property + def environments(self) -> EnvironmentList: + """ + Access the environments + """ + if self._environments is None: + self._environments = EnvironmentList( + self._version, + self._solution["sid"], + ) + return self._environments + + @property + def functions(self) -> FunctionList: + """ + Access the functions + """ + if self._functions is None: + self._functions = FunctionList( + self._version, + self._solution["sid"], + ) + return self._functions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + unique_name: str, + friendly_name: str, + include_credentials: Union[bool, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param unique_name: A user-defined string that uniquely identifies the Service resource. It can be used as an alternative to the `sid` in the URL path to address the Service resource. This value must be 50 characters or less in length and be unique. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param include_credentials: Whether to inject Account credentials into a function invocation context. The default value is `true`. + :param ui_editable: Whether the Service's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "IncludeCredentials": serialize.boolean_to_string(include_credentials), + "UiEditable": serialize.boolean_to_string(ui_editable), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + unique_name: str, + friendly_name: str, + include_credentials: Union[bool, object] = values.unset, + ui_editable: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param unique_name: A user-defined string that uniquely identifies the Service resource. It can be used as an alternative to the `sid` in the URL path to address the Service resource. This value must be 50 characters or less in length and be unique. + :param friendly_name: A descriptive string that you create to describe the Service resource. It can be a maximum of 255 characters. + :param include_credentials: Whether to inject Account credentials into a function invocation context. The default value is `true`. + :param ui_editable: Whether the Service's properties and subresources can be edited via the UI. The default value is `false`. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "IncludeCredentials": serialize.boolean_to_string(include_credentials), + "UiEditable": serialize.boolean_to_string(ui_editable), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The `sid` or `unique_name` of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The `sid` or `unique_name` of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..82e33ef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__init__.py new file mode 100644 index 0000000..2049c9d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__init__.py @@ -0,0 +1,619 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.asset.asset_version import AssetVersionList + + +class AssetInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Asset resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Asset resource. + :ivar service_sid: The SID of the Service that the Asset resource is associated with. + :ivar friendly_name: The string that you assigned to describe the Asset resource. It can be a maximum of 255 characters. + :ivar date_created: The date and time in GMT when the Asset resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Asset resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Asset resource. + :ivar links: The URLs of the Asset resource's nested resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[AssetContext] = None + + @property + def _proxy(self) -> "AssetContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AssetContext for this AssetInstance + """ + if self._context is None: + self._context = AssetContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the AssetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AssetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "AssetInstance": + """ + Fetch the AssetInstance + + + :returns: The fetched AssetInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AssetInstance": + """ + Asynchronous coroutine to fetch the AssetInstance + + + :returns: The fetched AssetInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: str) -> "AssetInstance": + """ + Update the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The updated AssetInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async(self, friendly_name: str) -> "AssetInstance": + """ + Asynchronous coroutine to update the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The updated AssetInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + @property + def asset_versions(self) -> AssetVersionList: + """ + Access the asset_versions + """ + return self._proxy.asset_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssetContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the AssetContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to update the Asset resource from. + :param sid: The SID that identifies the Asset resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Assets/{sid}".format(**self._solution) + + self._asset_versions: Optional[AssetVersionList] = None + + def delete(self) -> bool: + """ + Deletes the AssetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the AssetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> AssetInstance: + """ + Fetch the AssetInstance + + + :returns: The fetched AssetInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AssetInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AssetInstance: + """ + Asynchronous coroutine to fetch the AssetInstance + + + :returns: The fetched AssetInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AssetInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, friendly_name: str) -> AssetInstance: + """ + Update the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The updated AssetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AssetInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, friendly_name: str) -> AssetInstance: + """ + Asynchronous coroutine to update the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The updated AssetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AssetInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def asset_versions(self) -> AssetVersionList: + """ + Access the asset_versions + """ + if self._asset_versions is None: + self._asset_versions = AssetVersionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._asset_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssetPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AssetInstance: + """ + Build an instance of AssetInstance + + :param payload: Payload response from the API + """ + return AssetInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AssetList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the AssetList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Asset resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Assets".format(**self._solution) + + def create(self, friendly_name: str) -> AssetInstance: + """ + Create the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The created AssetInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AssetInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, friendly_name: str) -> AssetInstance: + """ + Asynchronously create the AssetInstance + + :param friendly_name: A descriptive string that you create to describe the Asset resource. It can be a maximum of 255 characters. + + :returns: The created AssetInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AssetInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AssetInstance]: + """ + Streams AssetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AssetInstance]: + """ + Asynchronously streams AssetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssetInstance]: + """ + Lists AssetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssetInstance]: + """ + Asynchronously lists AssetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssetPage: + """ + Retrieve a single page of AssetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AssetPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssetPage: + """ + Asynchronously retrieve a single page of AssetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AssetPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AssetPage: + """ + Retrieve a specific page of AssetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssetInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AssetPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AssetPage: + """ + Asynchronously retrieve a specific page of AssetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssetInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AssetPage(self._version, response, self._solution) + + def get(self, sid: str) -> AssetContext: + """ + Constructs a AssetContext + + :param sid: The SID that identifies the Asset resource to update. + """ + return AssetContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> AssetContext: + """ + Constructs a AssetContext + + :param sid: The SID that identifies the Asset resource to update. + """ + return AssetContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e2fb6aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/asset_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/asset_version.cpython-310.pyc new file mode 100644 index 0000000..85530f1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/__pycache__/asset_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/asset_version.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/asset_version.py new file mode 100644 index 0000000..fbe7b30 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/asset/asset_version.py @@ -0,0 +1,454 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class AssetVersionInstance(InstanceResource): + + class Visibility(object): + PUBLIC = "public" + PRIVATE = "private" + PROTECTED = "protected" + + """ + :ivar sid: The unique string that we created to identify the Asset Version resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Asset Version resource. + :ivar service_sid: The SID of the Service that the Asset Version resource is associated with. + :ivar asset_sid: The SID of the Asset resource that is the parent of the Asset Version. + :ivar path: The URL-friendly string by which the Asset Version can be referenced. It can be a maximum of 255 characters. All paths begin with a forward slash ('/'). If an Asset Version creation request is submitted with a path not containing a leading slash, the path will automatically be prepended with one. + :ivar visibility: + :ivar date_created: The date and time in GMT when the Asset Version resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Asset Version resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + asset_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.asset_sid: Optional[str] = payload.get("asset_sid") + self.path: Optional[str] = payload.get("path") + self.visibility: Optional["AssetVersionInstance.Visibility"] = payload.get( + "visibility" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "asset_sid": asset_sid, + "sid": sid or self.sid, + } + self._context: Optional[AssetVersionContext] = None + + @property + def _proxy(self) -> "AssetVersionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AssetVersionContext for this AssetVersionInstance + """ + if self._context is None: + self._context = AssetVersionContext( + self._version, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AssetVersionInstance": + """ + Fetch the AssetVersionInstance + + + :returns: The fetched AssetVersionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AssetVersionInstance": + """ + Asynchronous coroutine to fetch the AssetVersionInstance + + + :returns: The fetched AssetVersionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssetVersionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, asset_sid: str, sid: str): + """ + Initialize the AssetVersionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Asset Version resource from. + :param asset_sid: The SID of the Asset resource that is the parent of the Asset Version resource to fetch. + :param sid: The SID of the Asset Version resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "asset_sid": asset_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Assets/{asset_sid}/Versions/{sid}".format( + **self._solution + ) + + def fetch(self) -> AssetVersionInstance: + """ + Fetch the AssetVersionInstance + + + :returns: The fetched AssetVersionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AssetVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AssetVersionInstance: + """ + Asynchronous coroutine to fetch the AssetVersionInstance + + + :returns: The fetched AssetVersionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AssetVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AssetVersionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> AssetVersionInstance: + """ + Build an instance of AssetVersionInstance + + :param payload: Payload response from the API + """ + return AssetVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class AssetVersionList(ListResource): + + def __init__(self, version: Version, service_sid: str, asset_sid: str): + """ + Initialize the AssetVersionList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Asset Version resource from. + :param asset_sid: The SID of the Asset resource that is the parent of the Asset Version resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "asset_sid": asset_sid, + } + self._uri = "/Services/{service_sid}/Assets/{asset_sid}/Versions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[AssetVersionInstance]: + """ + Streams AssetVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[AssetVersionInstance]: + """ + Asynchronously streams AssetVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssetVersionInstance]: + """ + Lists AssetVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[AssetVersionInstance]: + """ + Asynchronously lists AssetVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssetVersionPage: + """ + Retrieve a single page of AssetVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssetVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return AssetVersionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> AssetVersionPage: + """ + Asynchronously retrieve a single page of AssetVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of AssetVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return AssetVersionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> AssetVersionPage: + """ + Retrieve a specific page of AssetVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssetVersionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return AssetVersionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> AssetVersionPage: + """ + Asynchronously retrieve a specific page of AssetVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of AssetVersionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return AssetVersionPage(self._version, response, self._solution) + + def get(self, sid: str) -> AssetVersionContext: + """ + Constructs a AssetVersionContext + + :param sid: The SID of the Asset Version resource to fetch. + """ + return AssetVersionContext( + self._version, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> AssetVersionContext: + """ + Constructs a AssetVersionContext + + :param sid: The SID of the Asset Version resource to fetch. + """ + return AssetVersionContext( + self._version, + service_sid=self._solution["service_sid"], + asset_sid=self._solution["asset_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__init__.py new file mode 100644 index 0000000..fa7a794 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__init__.py @@ -0,0 +1,558 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.build.build_status import BuildStatusList + + +class BuildInstance(InstanceResource): + + class Runtime(object): + NODE8 = "node8" + NODE10 = "node10" + NODE12 = "node12" + NODE14 = "node14" + NODE16 = "node16" + NODE18 = "node18" + + class Status(object): + BUILDING = "building" + COMPLETED = "completed" + FAILED = "failed" + + """ + :ivar sid: The unique string that we created to identify the Build resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Build resource. + :ivar service_sid: The SID of the Service that the Build resource is associated with. + :ivar status: + :ivar asset_versions: The list of Asset Version resource SIDs that are included in the Build. + :ivar function_versions: The list of Function Version resource SIDs that are included in the Build. + :ivar dependencies: A list of objects that describe the Dependencies included in the Build. Each object contains the `name` and `version` of the dependency. + :ivar runtime: + :ivar date_created: The date and time in GMT when the Build resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Build resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Build resource. + :ivar links: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], service_sid: str, sid: Optional[str] = None): + super().__init__(version) + + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.status: Optional["BuildInstance.Status"] = payload.get("status") + self.asset_versions: Optional[List[Dict[str, object]]] = payload.get("asset_versions") + self.function_versions: Optional[List[Dict[str, object]]] = payload.get("function_versions") + self.dependencies: Optional[List[Dict[str, object]]] = payload.get("dependencies") + self.runtime: Optional["BuildInstance.Runtime"] = payload.get("runtime") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime(payload.get("date_created")) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime(payload.get("date_updated")) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[BuildContext] = None + + @property + def _proxy(self) -> "BuildContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BuildContext for this BuildInstance + """ + if self._context is None: + self._context = BuildContext(self._version, service_sid=self._solution['service_sid'], sid=self._solution['sid'],) + return self._context + + + def delete(self) -> bool: + """ + Deletes the BuildInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BuildInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + + def fetch(self) -> "BuildInstance": + """ + Fetch the BuildInstance + + + :returns: The fetched BuildInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BuildInstance": + """ + Asynchronous coroutine to fetch the BuildInstance + + + :returns: The fetched BuildInstance + """ + return await self._proxy.fetch_async() + + @property + def build_status(self) -> BuildStatusList: + """ + Access the build_status + """ + return self._proxy.build_status + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + +class BuildContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BuildContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Build resource from. + :param sid: The SID of the Build resource to fetch. + """ + super().__init__(version) + + + # Path Solution + self._solution = { + 'service_sid': service_sid, + 'sid': sid, + } + self._uri = '/Services/{service_sid}/Builds/{sid}'.format(**self._solution) + + self._build_status: Optional[BuildStatusList] = None + + + def delete(self) -> bool: + """ + Deletes the BuildInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete(method='DELETE', uri=self._uri,) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BuildInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async(method='DELETE', uri=self._uri,) + + + def fetch(self) -> BuildInstance: + """ + Fetch the BuildInstance + + + :returns: The fetched BuildInstance + """ + + payload = self._version.fetch(method='GET', uri=self._uri, ) + + return BuildInstance( + self._version, + payload, + service_sid=self._solution['service_sid'], + sid=self._solution['sid'], + + ) + + async def fetch_async(self) -> BuildInstance: + """ + Asynchronous coroutine to fetch the BuildInstance + + + :returns: The fetched BuildInstance + """ + + payload = await self._version.fetch_async(method='GET', uri=self._uri, ) + + return BuildInstance( + self._version, + payload, + service_sid=self._solution['service_sid'], + sid=self._solution['sid'], + + ) + + + @property + def build_status(self) -> BuildStatusList: + """ + Access the build_status + """ + if self._build_status is None: + self._build_status = BuildStatusList( + self._version, + self._solution['service_sid'], + self._solution['sid'], + ) + return self._build_status + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + + + + + + + +class BuildPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BuildInstance: + """ + Build an instance of BuildInstance + + :param payload: Payload response from the API + """ + return BuildInstance(self._version, payload, service_sid=self._solution["service_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + + + + +class BuildList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the BuildList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Build resources from. + + """ + super().__init__(version) + + + # Path Solution + self._solution = { 'service_sid': service_sid, } + self._uri = '/Services/{service_sid}/Builds'.format(**self._solution) + + + + + + def create(self, asset_versions: Union[List[str], object]=values.unset, function_versions: Union[List[str], object]=values.unset, dependencies: Union[str, object]=values.unset, runtime: Union[str, object]=values.unset) -> BuildInstance: + """ + Create the BuildInstance + + :param asset_versions: The list of Asset Version resource SIDs to include in the Build. + :param function_versions: The list of the Function Version resource SIDs to include in the Build. + :param dependencies: A list of objects that describe the Dependencies included in the Build. Each object contains the `name` and `version` of the dependency. + :param runtime: The Runtime version that will be used to run the Build resource when it is deployed. + + :returns: The created BuildInstance + """ + + data = values.of({ + 'AssetVersions': serialize.map(asset_versions, lambda e: e), + 'FunctionVersions': serialize.map(function_versions, lambda e: e), + 'Dependencies': dependencies, + 'Runtime': runtime, + }) + + + + payload = self._version.create(method='POST', uri=self._uri, data=data,) + + return BuildInstance(self._version, payload, service_sid=self._solution['service_sid']) + + async def create_async(self, asset_versions: Union[List[str], object]=values.unset, function_versions: Union[List[str], object]=values.unset, dependencies: Union[str, object]=values.unset, runtime: Union[str, object]=values.unset) -> BuildInstance: + """ + Asynchronously create the BuildInstance + + :param asset_versions: The list of Asset Version resource SIDs to include in the Build. + :param function_versions: The list of the Function Version resource SIDs to include in the Build. + :param dependencies: A list of objects that describe the Dependencies included in the Build. Each object contains the `name` and `version` of the dependency. + :param runtime: The Runtime version that will be used to run the Build resource when it is deployed. + + :returns: The created BuildInstance + """ + + data = values.of({ + 'AssetVersions': serialize.map(asset_versions, lambda e: e), + 'FunctionVersions': serialize.map(function_versions, lambda e: e), + 'Dependencies': dependencies, + 'Runtime': runtime, + }) + + + + payload = await self._version.create_async(method='POST', uri=self._uri, data=data,) + + return BuildInstance(self._version, payload, service_sid=self._solution['service_sid']) + + + def stream(self, + + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BuildInstance]: + """ + Streams BuildInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + page_size=limits['page_size'] + ) + + return self._version.stream(page, limits['limit']) + + async def stream_async(self, + + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BuildInstance]: + """ + Asynchronously streams BuildInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + page_size=limits['page_size'] + ) + + return self._version.stream_async(page, limits['limit']) + + def list(self, + + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BuildInstance]: + """ + Lists BuildInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list(self.stream( + limit=limit, + page_size=page_size, + )) + + async def list_async(self, + + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BuildInstance]: + """ + Asynchronously lists BuildInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [record async for record in await self.stream_async( + limit=limit, + page_size=page_size, + )] + + def page(self, + + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BuildPage: + """ + Retrieve a single page of BuildInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BuildInstance + """ + data = values.of({ + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + response = self._version.page(method='GET', uri=self._uri, params=data) + return BuildPage(self._version, response, self._solution) + + async def page_async(self, + + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BuildPage: + """ + Asynchronously retrieve a single page of BuildInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BuildInstance + """ + data = values.of({ + 'PageToken': page_token, + 'Page': page_number, + 'PageSize': page_size, + }) + + response = await self._version.page_async(method='GET', uri=self._uri, params=data) + return BuildPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BuildPage: + """ + Retrieve a specific page of BuildInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BuildInstance + """ + response = self._version.domain.twilio.request( + 'GET', + target_url + ) + return BuildPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BuildPage: + """ + Asynchronously retrieve a specific page of BuildInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BuildInstance + """ + response = await self._version.domain.twilio.request_async( + 'GET', + target_url + ) + return BuildPage(self._version, response, self._solution) + + + + + + def get(self, sid: str) -> BuildContext: + """ + Constructs a BuildContext + + :param sid: The SID of the Build resource to fetch. + """ + return BuildContext(self._version, service_sid=self._solution['service_sid'], sid=sid) + + def __call__(self, sid: str) -> BuildContext: + """ + Constructs a BuildContext + + :param sid: The SID of the Build resource to fetch. + """ + return BuildContext(self._version, service_sid=self._solution['service_sid'], sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return '' + diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..0bd5279 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/build_status.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/build_status.cpython-310.pyc new file mode 100644 index 0000000..d5b594f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/__pycache__/build_status.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/build_status.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/build_status.py new file mode 100644 index 0000000..2613ae3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/build/build_status.py @@ -0,0 +1,207 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + + +class BuildStatusInstance(InstanceResource): + + class Status(object): + BUILDING = "building" + COMPLETED = "completed" + FAILED = "failed" + + """ + :ivar sid: The unique string that we created to identify the Build resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Build resource. + :ivar service_sid: The SID of the Service that the Build resource is associated with. + :ivar status: + :ivar url: The absolute URL of the Build Status resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], service_sid: str, sid: str): + super().__init__(version) + + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.status: Optional["BuildStatusInstance.Status"] = payload.get("status") + self.url: Optional[str] = payload.get("url") + + + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._context: Optional[BuildStatusContext] = None + + @property + def _proxy(self) -> "BuildStatusContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BuildStatusContext for this BuildStatusInstance + """ + if self._context is None: + self._context = BuildStatusContext(self._version, service_sid=self._solution['service_sid'], sid=self._solution['sid'],) + return self._context + + + def fetch(self) -> "BuildStatusInstance": + """ + Fetch the BuildStatusInstance + + + :returns: The fetched BuildStatusInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BuildStatusInstance": + """ + Asynchronous coroutine to fetch the BuildStatusInstance + + + :returns: The fetched BuildStatusInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + +class BuildStatusContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BuildStatusContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Build resource from. + :param sid: The SID of the Build resource to fetch. + """ + super().__init__(version) + + + # Path Solution + self._solution = { + 'service_sid': service_sid, + 'sid': sid, + } + self._uri = '/Services/{service_sid}/Builds/{sid}/Status'.format(**self._solution) + + + + def fetch(self) -> BuildStatusInstance: + """ + Fetch the BuildStatusInstance + + + :returns: The fetched BuildStatusInstance + """ + + payload = self._version.fetch(method='GET', uri=self._uri, ) + + return BuildStatusInstance( + self._version, + payload, + service_sid=self._solution['service_sid'], + sid=self._solution['sid'], + + ) + + async def fetch_async(self) -> BuildStatusInstance: + """ + Asynchronous coroutine to fetch the BuildStatusInstance + + + :returns: The fetched BuildStatusInstance + """ + + payload = await self._version.fetch_async(method='GET', uri=self._uri, ) + + return BuildStatusInstance( + self._version, + payload, + service_sid=self._solution['service_sid'], + sid=self._solution['sid'], + + ) + + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = ' '.join('{}={}'.format(k, v) for k, v in self._solution.items()) + return ''.format(context) + + + +class BuildStatusList(ListResource): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the BuildStatusList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Build resource from. + :param sid: The SID of the Build resource to fetch. + + """ + super().__init__(version) + + + # Path Solution + self._solution = { 'service_sid': service_sid, 'sid': sid, } + + + + + def get(self) -> BuildStatusContext: + """ + Constructs a BuildStatusContext + + """ + return BuildStatusContext(self._version, service_sid=self._solution['service_sid'], sid=self._solution['sid']) + + def __call__(self) -> BuildStatusContext: + """ + Constructs a BuildStatusContext + + """ + return BuildStatusContext(self._version, service_sid=self._solution['service_sid'], sid=self._solution['sid']) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return '' + diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__init__.py new file mode 100644 index 0000000..46b7dda --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__init__.py @@ -0,0 +1,601 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.environment.deployment import DeploymentList +from twilio.rest.serverless.v1.service.environment.log import LogList +from twilio.rest.serverless.v1.service.environment.variable import VariableList + + +class EnvironmentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Environment resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Environment resource. + :ivar service_sid: The SID of the Service that the Environment resource is associated with. + :ivar build_sid: The SID of the build deployed in the environment. + :ivar unique_name: A user-defined string that uniquely identifies the Environment resource. + :ivar domain_suffix: A URL-friendly name that represents the environment and forms part of the domain name. + :ivar domain_name: The domain name for all Functions and Assets deployed in the Environment, using the Service unique name, a randomly-generated Service suffix, and an optional Environment domain suffix. + :ivar date_created: The date and time in GMT when the Environment resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Environment resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Environment resource. + :ivar links: The URLs of the Environment resource's nested resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.build_sid: Optional[str] = payload.get("build_sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.domain_suffix: Optional[str] = payload.get("domain_suffix") + self.domain_name: Optional[str] = payload.get("domain_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[EnvironmentContext] = None + + @property + def _proxy(self) -> "EnvironmentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EnvironmentContext for this EnvironmentInstance + """ + if self._context is None: + self._context = EnvironmentContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the EnvironmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EnvironmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "EnvironmentInstance": + """ + Fetch the EnvironmentInstance + + + :returns: The fetched EnvironmentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EnvironmentInstance": + """ + Asynchronous coroutine to fetch the EnvironmentInstance + + + :returns: The fetched EnvironmentInstance + """ + return await self._proxy.fetch_async() + + @property + def deployments(self) -> DeploymentList: + """ + Access the deployments + """ + return self._proxy.deployments + + @property + def logs(self) -> LogList: + """ + Access the logs + """ + return self._proxy.logs + + @property + def variables(self) -> VariableList: + """ + Access the variables + """ + return self._proxy.variables + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EnvironmentContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the EnvironmentContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Environment resource from. + :param sid: The SID of the Environment resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Environments/{sid}".format( + **self._solution + ) + + self._deployments: Optional[DeploymentList] = None + self._logs: Optional[LogList] = None + self._variables: Optional[VariableList] = None + + def delete(self) -> bool: + """ + Deletes the EnvironmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EnvironmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> EnvironmentInstance: + """ + Fetch the EnvironmentInstance + + + :returns: The fetched EnvironmentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EnvironmentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EnvironmentInstance: + """ + Asynchronous coroutine to fetch the EnvironmentInstance + + + :returns: The fetched EnvironmentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EnvironmentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def deployments(self) -> DeploymentList: + """ + Access the deployments + """ + if self._deployments is None: + self._deployments = DeploymentList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._deployments + + @property + def logs(self) -> LogList: + """ + Access the logs + """ + if self._logs is None: + self._logs = LogList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._logs + + @property + def variables(self) -> VariableList: + """ + Access the variables + """ + if self._variables is None: + self._variables = VariableList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._variables + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EnvironmentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EnvironmentInstance: + """ + Build an instance of EnvironmentInstance + + :param payload: Payload response from the API + """ + return EnvironmentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EnvironmentList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the EnvironmentList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Environment resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Environments".format(**self._solution) + + def create( + self, unique_name: str, domain_suffix: Union[str, object] = values.unset + ) -> EnvironmentInstance: + """ + Create the EnvironmentInstance + + :param unique_name: A user-defined string that uniquely identifies the Environment resource. It can be a maximum of 100 characters. + :param domain_suffix: A URL-friendly name that represents the environment and forms part of the domain name. It can be a maximum of 16 characters. + + :returns: The created EnvironmentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DomainSuffix": domain_suffix, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EnvironmentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, unique_name: str, domain_suffix: Union[str, object] = values.unset + ) -> EnvironmentInstance: + """ + Asynchronously create the EnvironmentInstance + + :param unique_name: A user-defined string that uniquely identifies the Environment resource. It can be a maximum of 100 characters. + :param domain_suffix: A URL-friendly name that represents the environment and forms part of the domain name. It can be a maximum of 16 characters. + + :returns: The created EnvironmentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "DomainSuffix": domain_suffix, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EnvironmentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EnvironmentInstance]: + """ + Streams EnvironmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EnvironmentInstance]: + """ + Asynchronously streams EnvironmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EnvironmentInstance]: + """ + Lists EnvironmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EnvironmentInstance]: + """ + Asynchronously lists EnvironmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EnvironmentPage: + """ + Retrieve a single page of EnvironmentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EnvironmentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EnvironmentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EnvironmentPage: + """ + Asynchronously retrieve a single page of EnvironmentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EnvironmentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EnvironmentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EnvironmentPage: + """ + Retrieve a specific page of EnvironmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EnvironmentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EnvironmentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EnvironmentPage: + """ + Asynchronously retrieve a specific page of EnvironmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EnvironmentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EnvironmentPage(self._version, response, self._solution) + + def get(self, sid: str) -> EnvironmentContext: + """ + Constructs a EnvironmentContext + + :param sid: The SID of the Environment resource to fetch. + """ + return EnvironmentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> EnvironmentContext: + """ + Constructs a EnvironmentContext + + :param sid: The SID of the Environment resource to fetch. + """ + return EnvironmentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6aa670c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/deployment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/deployment.cpython-310.pyc new file mode 100644 index 0000000..3f459ba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/deployment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/log.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/log.cpython-310.pyc new file mode 100644 index 0000000..7c53287 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/log.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/variable.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/variable.cpython-310.pyc new file mode 100644 index 0000000..106ae95 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/__pycache__/variable.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/deployment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/deployment.py new file mode 100644 index 0000000..1ff896f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/deployment.py @@ -0,0 +1,512 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DeploymentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Deployment resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Deployment resource. + :ivar service_sid: The SID of the Service that the Deployment resource is associated with. + :ivar environment_sid: The SID of the Environment for the Deployment. + :ivar build_sid: The SID of the Build for the deployment. + :ivar date_created: The date and time in GMT when the Deployment resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Deployment resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Deployment resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + environment_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.environment_sid: Optional[str] = payload.get("environment_sid") + self.build_sid: Optional[str] = payload.get("build_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid or self.sid, + } + self._context: Optional[DeploymentContext] = None + + @property + def _proxy(self) -> "DeploymentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DeploymentContext for this DeploymentInstance + """ + if self._context is None: + self._context = DeploymentContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "DeploymentInstance": + """ + Fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DeploymentInstance": + """ + Asynchronous coroutine to fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeploymentContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, environment_sid: str, sid: str + ): + """ + Initialize the DeploymentContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Deployment resource from. + :param environment_sid: The SID of the Environment used by the Deployment to fetch. + :param sid: The SID that identifies the Deployment resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Environments/{environment_sid}/Deployments/{sid}".format( + **self._solution + ) + + def fetch(self) -> DeploymentInstance: + """ + Fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DeploymentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DeploymentInstance: + """ + Asynchronous coroutine to fetch the DeploymentInstance + + + :returns: The fetched DeploymentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DeploymentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DeploymentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DeploymentInstance: + """ + Build an instance of DeploymentInstance + + :param payload: Payload response from the API + """ + return DeploymentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DeploymentList(ListResource): + + def __init__(self, version: Version, service_sid: str, environment_sid: str): + """ + Initialize the DeploymentList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Deployment resources from. + :param environment_sid: The SID of the Environment used by the Deployment resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + } + self._uri = ( + "/Services/{service_sid}/Environments/{environment_sid}/Deployments".format( + **self._solution + ) + ) + + def create( + self, build_sid: Union[str, object] = values.unset + ) -> DeploymentInstance: + """ + Create the DeploymentInstance + + :param build_sid: The SID of the Build for the Deployment. + + :returns: The created DeploymentInstance + """ + + data = values.of( + { + "BuildSid": build_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + async def create_async( + self, build_sid: Union[str, object] = values.unset + ) -> DeploymentInstance: + """ + Asynchronously create the DeploymentInstance + + :param build_sid: The SID of the Build for the Deployment. + + :returns: The created DeploymentInstance + """ + + data = values.of( + { + "BuildSid": build_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DeploymentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DeploymentInstance]: + """ + Streams DeploymentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DeploymentInstance]: + """ + Asynchronously streams DeploymentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeploymentInstance]: + """ + Lists DeploymentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DeploymentInstance]: + """ + Asynchronously lists DeploymentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeploymentPage: + """ + Retrieve a single page of DeploymentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeploymentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DeploymentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DeploymentPage: + """ + Asynchronously retrieve a single page of DeploymentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DeploymentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DeploymentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DeploymentPage: + """ + Retrieve a specific page of DeploymentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeploymentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DeploymentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DeploymentPage: + """ + Asynchronously retrieve a specific page of DeploymentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DeploymentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DeploymentPage(self._version, response, self._solution) + + def get(self, sid: str) -> DeploymentContext: + """ + Constructs a DeploymentContext + + :param sid: The SID that identifies the Deployment resource to fetch. + """ + return DeploymentContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> DeploymentContext: + """ + Constructs a DeploymentContext + + :param sid: The SID that identifies the Deployment resource to fetch. + """ + return DeploymentContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/log.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/log.py new file mode 100644 index 0000000..e1aff52 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/log.py @@ -0,0 +1,524 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class LogInstance(InstanceResource): + + class Level(object): + INFO = "info" + WARN = "warn" + ERROR = "error" + + """ + :ivar sid: The unique string that we created to identify the Log resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Log resource. + :ivar service_sid: The SID of the Service that the Log resource is associated with. + :ivar environment_sid: The SID of the environment in which the log occurred. + :ivar build_sid: The SID of the build that corresponds to the log. + :ivar deployment_sid: The SID of the deployment that corresponds to the log. + :ivar function_sid: The SID of the function whose invocation produced the log. + :ivar request_sid: The SID of the request associated with the log. + :ivar level: + :ivar message: The log message. + :ivar date_created: The date and time in GMT when the Log resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Log resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + environment_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.environment_sid: Optional[str] = payload.get("environment_sid") + self.build_sid: Optional[str] = payload.get("build_sid") + self.deployment_sid: Optional[str] = payload.get("deployment_sid") + self.function_sid: Optional[str] = payload.get("function_sid") + self.request_sid: Optional[str] = payload.get("request_sid") + self.level: Optional["LogInstance.Level"] = payload.get("level") + self.message: Optional[str] = payload.get("message") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid or self.sid, + } + self._context: Optional[LogContext] = None + + @property + def _proxy(self) -> "LogContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: LogContext for this LogInstance + """ + if self._context is None: + self._context = LogContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "LogInstance": + """ + Fetch the LogInstance + + + :returns: The fetched LogInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "LogInstance": + """ + Asynchronous coroutine to fetch the LogInstance + + + :returns: The fetched LogInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class LogContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, environment_sid: str, sid: str + ): + """ + Initialize the LogContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Log resource from. + :param environment_sid: The SID of the environment with the Log resource to fetch. + :param sid: The SID of the Log resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Environments/{environment_sid}/Logs/{sid}".format( + **self._solution + ) + ) + + def fetch(self) -> LogInstance: + """ + Fetch the LogInstance + + + :returns: The fetched LogInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return LogInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> LogInstance: + """ + Asynchronous coroutine to fetch the LogInstance + + + :returns: The fetched LogInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return LogInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class LogPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> LogInstance: + """ + Build an instance of LogInstance + + :param payload: Payload response from the API + """ + return LogInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class LogList(ListResource): + + def __init__(self, version: Version, service_sid: str, environment_sid: str): + """ + Initialize the LogList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Log resource from. + :param environment_sid: The SID of the environment with the Log resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + } + self._uri = ( + "/Services/{service_sid}/Environments/{environment_sid}/Logs".format( + **self._solution + ) + ) + + def stream( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[LogInstance]: + """ + Streams LogInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str function_sid: The SID of the function whose invocation produced the Log resources to read. + :param datetime start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param datetime end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + function_sid=function_sid, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[LogInstance]: + """ + Asynchronously streams LogInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str function_sid: The SID of the function whose invocation produced the Log resources to read. + :param datetime start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param datetime end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + function_sid=function_sid, + start_date=start_date, + end_date=end_date, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LogInstance]: + """ + Lists LogInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str function_sid: The SID of the function whose invocation produced the Log resources to read. + :param datetime start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param datetime end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + function_sid=function_sid, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[LogInstance]: + """ + Asynchronously lists LogInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str function_sid: The SID of the function whose invocation produced the Log resources to read. + :param datetime start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param datetime end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + function_sid=function_sid, + start_date=start_date, + end_date=end_date, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LogPage: + """ + Retrieve a single page of LogInstance records from the API. + Request is executed immediately + + :param function_sid: The SID of the function whose invocation produced the Log resources to read. + :param start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LogInstance + """ + data = values.of( + { + "FunctionSid": function_sid, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return LogPage(self._version, response, self._solution) + + async def page_async( + self, + function_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> LogPage: + """ + Asynchronously retrieve a single page of LogInstance records from the API. + Request is executed immediately + + :param function_sid: The SID of the function whose invocation produced the Log resources to read. + :param start_date: The date/time (in GMT, ISO 8601) after which the Log resources must have been created. Defaults to 1 day prior to current date/time. + :param end_date: The date/time (in GMT, ISO 8601) before which the Log resources must have been created. Defaults to current date/time. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of LogInstance + """ + data = values.of( + { + "FunctionSid": function_sid, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return LogPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> LogPage: + """ + Retrieve a specific page of LogInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LogInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return LogPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> LogPage: + """ + Asynchronously retrieve a specific page of LogInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of LogInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return LogPage(self._version, response, self._solution) + + def get(self, sid: str) -> LogContext: + """ + Constructs a LogContext + + :param sid: The SID of the Log resource to fetch. + """ + return LogContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> LogContext: + """ + Constructs a LogContext + + :param sid: The SID of the Log resource to fetch. + """ + return LogContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/variable.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/variable.py new file mode 100644 index 0000000..f69c129 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/environment/variable.py @@ -0,0 +1,660 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class VariableInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Variable resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Variable resource. + :ivar service_sid: The SID of the Service that the Variable resource is associated with. + :ivar environment_sid: The SID of the Environment in which the Variable exists. + :ivar key: A string by which the Variable resource can be referenced. + :ivar value: A string that contains the actual value of the Variable. + :ivar date_created: The date and time in GMT when the Variable resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Variable resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Variable resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + environment_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.environment_sid: Optional[str] = payload.get("environment_sid") + self.key: Optional[str] = payload.get("key") + self.value: Optional[str] = payload.get("value") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid or self.sid, + } + self._context: Optional[VariableContext] = None + + @property + def _proxy(self) -> "VariableContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: VariableContext for this VariableInstance + """ + if self._context is None: + self._context = VariableContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the VariableInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the VariableInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "VariableInstance": + """ + Fetch the VariableInstance + + + :returns: The fetched VariableInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "VariableInstance": + """ + Asynchronous coroutine to fetch the VariableInstance + + + :returns: The fetched VariableInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + key: Union[str, object] = values.unset, + value: Union[str, object] = values.unset, + ) -> "VariableInstance": + """ + Update the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The updated VariableInstance + """ + return self._proxy.update( + key=key, + value=value, + ) + + async def update_async( + self, + key: Union[str, object] = values.unset, + value: Union[str, object] = values.unset, + ) -> "VariableInstance": + """ + Asynchronous coroutine to update the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The updated VariableInstance + """ + return await self._proxy.update_async( + key=key, + value=value, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VariableContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, environment_sid: str, sid: str + ): + """ + Initialize the VariableContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to update the Variable resource under. + :param environment_sid: The SID of the Environment with the Variable resource to update. + :param sid: The SID of the Variable resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Environments/{environment_sid}/Variables/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the VariableInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the VariableInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> VariableInstance: + """ + Fetch the VariableInstance + + + :returns: The fetched VariableInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> VariableInstance: + """ + Asynchronous coroutine to fetch the VariableInstance + + + :returns: The fetched VariableInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + key: Union[str, object] = values.unset, + value: Union[str, object] = values.unset, + ) -> VariableInstance: + """ + Update the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The updated VariableInstance + """ + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + key: Union[str, object] = values.unset, + value: Union[str, object] = values.unset, + ) -> VariableInstance: + """ + Asynchronous coroutine to update the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The updated VariableInstance + """ + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VariablePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> VariableInstance: + """ + Build an instance of VariableInstance + + :param payload: Payload response from the API + """ + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class VariableList(ListResource): + + def __init__(self, version: Version, service_sid: str, environment_sid: str): + """ + Initialize the VariableList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Variable resources from. + :param environment_sid: The SID of the Environment with the Variable resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "environment_sid": environment_sid, + } + self._uri = ( + "/Services/{service_sid}/Environments/{environment_sid}/Variables".format( + **self._solution + ) + ) + + def create(self, key: str, value: str) -> VariableInstance: + """ + Create the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The created VariableInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + async def create_async(self, key: str, value: str) -> VariableInstance: + """ + Asynchronously create the VariableInstance + + :param key: A string by which the Variable resource can be referenced. It can be a maximum of 128 characters. + :param value: A string that contains the actual value of the Variable. It can be a maximum of 450 bytes in size. + + :returns: The created VariableInstance + """ + + data = values.of( + { + "Key": key, + "Value": value, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return VariableInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[VariableInstance]: + """ + Streams VariableInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[VariableInstance]: + """ + Asynchronously streams VariableInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VariableInstance]: + """ + Lists VariableInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VariableInstance]: + """ + Asynchronously lists VariableInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VariablePage: + """ + Retrieve a single page of VariableInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VariableInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return VariablePage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VariablePage: + """ + Asynchronously retrieve a single page of VariableInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VariableInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return VariablePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> VariablePage: + """ + Retrieve a specific page of VariableInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VariableInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return VariablePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> VariablePage: + """ + Asynchronously retrieve a specific page of VariableInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VariableInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return VariablePage(self._version, response, self._solution) + + def get(self, sid: str) -> VariableContext: + """ + Constructs a VariableContext + + :param sid: The SID of the Variable resource to update. + """ + return VariableContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> VariableContext: + """ + Constructs a VariableContext + + :param sid: The SID of the Variable resource to update. + """ + return VariableContext( + self._version, + service_sid=self._solution["service_sid"], + environment_sid=self._solution["environment_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__init__.py new file mode 100644 index 0000000..71bbad8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__init__.py @@ -0,0 +1,621 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.function.function_version import ( + FunctionVersionList, +) + + +class FunctionInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Function resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Function resource. + :ivar service_sid: The SID of the Service that the Function resource is associated with. + :ivar friendly_name: The string that you assigned to describe the Function resource. It can be a maximum of 255 characters. + :ivar date_created: The date and time in GMT when the Function resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Function resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Function resource. + :ivar links: The URLs of nested resources of the Function resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[FunctionContext] = None + + @property + def _proxy(self) -> "FunctionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FunctionContext for this FunctionInstance + """ + if self._context is None: + self._context = FunctionContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FunctionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FunctionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FunctionInstance": + """ + Fetch the FunctionInstance + + + :returns: The fetched FunctionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FunctionInstance": + """ + Asynchronous coroutine to fetch the FunctionInstance + + + :returns: The fetched FunctionInstance + """ + return await self._proxy.fetch_async() + + def update(self, friendly_name: str) -> "FunctionInstance": + """ + Update the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The updated FunctionInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async(self, friendly_name: str) -> "FunctionInstance": + """ + Asynchronous coroutine to update the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The updated FunctionInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + @property + def function_versions(self) -> FunctionVersionList: + """ + Access the function_versions + """ + return self._proxy.function_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FunctionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the FunctionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to update the Function resource from. + :param sid: The SID of the Function resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Functions/{sid}".format(**self._solution) + + self._function_versions: Optional[FunctionVersionList] = None + + def delete(self) -> bool: + """ + Deletes the FunctionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FunctionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FunctionInstance: + """ + Fetch the FunctionInstance + + + :returns: The fetched FunctionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FunctionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FunctionInstance: + """ + Asynchronous coroutine to fetch the FunctionInstance + + + :returns: The fetched FunctionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FunctionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, friendly_name: str) -> FunctionInstance: + """ + Update the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The updated FunctionInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FunctionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self, friendly_name: str) -> FunctionInstance: + """ + Asynchronous coroutine to update the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The updated FunctionInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FunctionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def function_versions(self) -> FunctionVersionList: + """ + Access the function_versions + """ + if self._function_versions is None: + self._function_versions = FunctionVersionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._function_versions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FunctionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FunctionInstance: + """ + Build an instance of FunctionInstance + + :param payload: Payload response from the API + """ + return FunctionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FunctionList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the FunctionList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Function resources from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Functions".format(**self._solution) + + def create(self, friendly_name: str) -> FunctionInstance: + """ + Create the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The created FunctionInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FunctionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, friendly_name: str) -> FunctionInstance: + """ + Asynchronously create the FunctionInstance + + :param friendly_name: A descriptive string that you create to describe the Function resource. It can be a maximum of 255 characters. + + :returns: The created FunctionInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FunctionInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FunctionInstance]: + """ + Streams FunctionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FunctionInstance]: + """ + Asynchronously streams FunctionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FunctionInstance]: + """ + Lists FunctionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FunctionInstance]: + """ + Asynchronously lists FunctionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FunctionPage: + """ + Retrieve a single page of FunctionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FunctionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FunctionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FunctionPage: + """ + Asynchronously retrieve a single page of FunctionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FunctionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FunctionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> FunctionPage: + """ + Retrieve a specific page of FunctionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FunctionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FunctionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> FunctionPage: + """ + Asynchronously retrieve a specific page of FunctionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FunctionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FunctionPage(self._version, response, self._solution) + + def get(self, sid: str) -> FunctionContext: + """ + Constructs a FunctionContext + + :param sid: The SID of the Function resource to update. + """ + return FunctionContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> FunctionContext: + """ + Constructs a FunctionContext + + :param sid: The SID of the Function resource to update. + """ + return FunctionContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6942407 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__init__.py new file mode 100644 index 0000000..e915894 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__init__.py @@ -0,0 +1,484 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.serverless.v1.service.function.function_version.function_version_content import ( + FunctionVersionContentList, +) + + +class FunctionVersionInstance(InstanceResource): + + class Visibility(object): + PUBLIC = "public" + PRIVATE = "private" + PROTECTED = "protected" + + """ + :ivar sid: The unique string that we created to identify the Function Version resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Function Version resource. + :ivar service_sid: The SID of the Service that the Function Version resource is associated with. + :ivar function_sid: The SID of the Function resource that is the parent of the Function Version resource. + :ivar path: The URL-friendly string by which the Function Version resource can be referenced. It can be a maximum of 255 characters. All paths begin with a forward slash ('/'). If a Function Version creation request is submitted with a path not containing a leading slash, the path will automatically be prepended with one. + :ivar visibility: + :ivar date_created: The date and time in GMT when the Function Version resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Function Version resource. + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + function_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.function_sid: Optional[str] = payload.get("function_sid") + self.path: Optional[str] = payload.get("path") + self.visibility: Optional["FunctionVersionInstance.Visibility"] = payload.get( + "visibility" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + "sid": sid or self.sid, + } + self._context: Optional[FunctionVersionContext] = None + + @property + def _proxy(self) -> "FunctionVersionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FunctionVersionContext for this FunctionVersionInstance + """ + if self._context is None: + self._context = FunctionVersionContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "FunctionVersionInstance": + """ + Fetch the FunctionVersionInstance + + + :returns: The fetched FunctionVersionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FunctionVersionInstance": + """ + Asynchronous coroutine to fetch the FunctionVersionInstance + + + :returns: The fetched FunctionVersionInstance + """ + return await self._proxy.fetch_async() + + @property + def function_version_content(self) -> FunctionVersionContentList: + """ + Access the function_version_content + """ + return self._proxy.function_version_content + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FunctionVersionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, function_sid: str, sid: str): + """ + Initialize the FunctionVersionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Function Version resource from. + :param function_sid: The SID of the function that is the parent of the Function Version resource to fetch. + :param sid: The SID of the Function Version resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Functions/{function_sid}/Versions/{sid}".format( + **self._solution + ) + ) + + self._function_version_content: Optional[FunctionVersionContentList] = None + + def fetch(self) -> FunctionVersionInstance: + """ + Fetch the FunctionVersionInstance + + + :returns: The fetched FunctionVersionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FunctionVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FunctionVersionInstance: + """ + Asynchronous coroutine to fetch the FunctionVersionInstance + + + :returns: The fetched FunctionVersionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FunctionVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + @property + def function_version_content(self) -> FunctionVersionContentList: + """ + Access the function_version_content + """ + if self._function_version_content is None: + self._function_version_content = FunctionVersionContentList( + self._version, + self._solution["service_sid"], + self._solution["function_sid"], + self._solution["sid"], + ) + return self._function_version_content + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FunctionVersionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FunctionVersionInstance: + """ + Build an instance of FunctionVersionInstance + + :param payload: Payload response from the API + """ + return FunctionVersionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FunctionVersionList(ListResource): + + def __init__(self, version: Version, service_sid: str, function_sid: str): + """ + Initialize the FunctionVersionList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to read the Function Version resources from. + :param function_sid: The SID of the function that is the parent of the Function Version resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + } + self._uri = "/Services/{service_sid}/Functions/{function_sid}/Versions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FunctionVersionInstance]: + """ + Streams FunctionVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FunctionVersionInstance]: + """ + Asynchronously streams FunctionVersionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FunctionVersionInstance]: + """ + Lists FunctionVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FunctionVersionInstance]: + """ + Asynchronously lists FunctionVersionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FunctionVersionPage: + """ + Retrieve a single page of FunctionVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FunctionVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FunctionVersionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FunctionVersionPage: + """ + Asynchronously retrieve a single page of FunctionVersionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FunctionVersionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FunctionVersionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> FunctionVersionPage: + """ + Retrieve a specific page of FunctionVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FunctionVersionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FunctionVersionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> FunctionVersionPage: + """ + Asynchronously retrieve a specific page of FunctionVersionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FunctionVersionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FunctionVersionPage(self._version, response, self._solution) + + def get(self, sid: str) -> FunctionVersionContext: + """ + Constructs a FunctionVersionContext + + :param sid: The SID of the Function Version resource to fetch. + """ + return FunctionVersionContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> FunctionVersionContext: + """ + Constructs a FunctionVersionContext + + :param sid: The SID of the Function Version resource to fetch. + """ + return FunctionVersionContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..85a4b08 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/function_version_content.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/function_version_content.cpython-310.pyc new file mode 100644 index 0000000..a661a5f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/__pycache__/function_version_content.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/function_version_content.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/function_version_content.py new file mode 100644 index 0000000..c58fa57 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/serverless/v1/service/function/function_version/function_version_content.py @@ -0,0 +1,229 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Serverless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FunctionVersionContentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Function Version resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Function Version resource. + :ivar service_sid: The SID of the Service that the Function Version resource is associated with. + :ivar function_sid: The SID of the Function that is the parent of the Function Version. + :ivar content: The content of the Function Version resource. + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + function_sid: str, + sid: str, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.function_sid: Optional[str] = payload.get("function_sid") + self.content: Optional[str] = payload.get("content") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + "sid": sid, + } + self._context: Optional[FunctionVersionContentContext] = None + + @property + def _proxy(self) -> "FunctionVersionContentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FunctionVersionContentContext for this FunctionVersionContentInstance + """ + if self._context is None: + self._context = FunctionVersionContentContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "FunctionVersionContentInstance": + """ + Fetch the FunctionVersionContentInstance + + + :returns: The fetched FunctionVersionContentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FunctionVersionContentInstance": + """ + Asynchronous coroutine to fetch the FunctionVersionContentInstance + + + :returns: The fetched FunctionVersionContentInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class FunctionVersionContentContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, function_sid: str, sid: str): + """ + Initialize the FunctionVersionContentContext + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Function Version content from. + :param function_sid: The SID of the Function that is the parent of the Function Version content to fetch. + :param sid: The SID of the Function Version content to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Functions/{function_sid}/Versions/{sid}/Content".format( + **self._solution + ) + + def fetch(self) -> FunctionVersionContentInstance: + """ + Fetch the FunctionVersionContentInstance + + + :returns: The fetched FunctionVersionContentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FunctionVersionContentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FunctionVersionContentInstance: + """ + Asynchronous coroutine to fetch the FunctionVersionContentInstance + + + :returns: The fetched FunctionVersionContentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FunctionVersionContentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FunctionVersionContentList(ListResource): + + def __init__(self, version: Version, service_sid: str, function_sid: str, sid: str): + """ + Initialize the FunctionVersionContentList + + :param version: Version that contains the resource + :param service_sid: The SID of the Service to fetch the Function Version content from. + :param function_sid: The SID of the Function that is the parent of the Function Version content to fetch. + :param sid: The SID of the Function Version content to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "function_sid": function_sid, + "sid": sid, + } + + def get(self) -> FunctionVersionContentContext: + """ + Constructs a FunctionVersionContentContext + + """ + return FunctionVersionContentContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + def __call__(self) -> FunctionVersionContentContext: + """ + Constructs a FunctionVersionContentContext + + """ + return FunctionVersionContentContext( + self._version, + service_sid=self._solution["service_sid"], + function_sid=self._solution["function_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/StudioBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/StudioBase.py new file mode 100644 index 0000000..3b21877 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/StudioBase.py @@ -0,0 +1,55 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.studio.v1 import V1 +from twilio.rest.studio.v2 import V2 + + +class StudioBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Studio Domain + + :returns: Domain for Studio + """ + super().__init__(twilio, "https://studio.twilio.com") + self._v1: Optional[V1] = None + self._v2: Optional[V2] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Studio + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Studio + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__init__.py new file mode 100644 index 0000000..986e694 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__init__.py @@ -0,0 +1,25 @@ +from warnings import warn + +from twilio.rest.studio.StudioBase import StudioBase +from twilio.rest.studio.v2.flow import FlowList +from twilio.rest.studio.v2.flow_validate import FlowValidateList + + +class Studio(StudioBase): + @property + def flows(self) -> FlowList: + warn( + "flows is deprecated. Use v2.flows instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.flows + + @property + def flow_validate(self) -> FlowValidateList: + warn( + "flow_validate is deprecated. Use v2.flow_validate instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.flow_validate diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/StudioBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/StudioBase.cpython-310.pyc new file mode 100644 index 0000000..6780b4d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/StudioBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..89669ed Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__init__.py new file mode 100644 index 0000000..8ab7d71 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.studio.v1.flow import FlowList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Studio + + :param domain: The Twilio.studio domain + """ + super().__init__(domain, "v1") + self._flows: Optional[FlowList] = None + + @property + def flows(self) -> FlowList: + if self._flows is None: + self._flows = FlowList(self) + return self._flows + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..85ef129 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__init__.py new file mode 100644 index 0000000..bb0f786 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__init__.py @@ -0,0 +1,497 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v1.flow.engagement import EngagementList +from twilio.rest.studio.v1.flow.execution import ExecutionList + + +class FlowInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PUBLISHED = "published" + + """ + :ivar sid: The unique string that we created to identify the Flow resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flow resource. + :ivar friendly_name: The string that you assigned to describe the Flow. + :ivar status: + :ivar version: The latest version number of the Flow's definition. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of the Flow's nested resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["FlowInstance.Status"] = payload.get("status") + self.version: Optional[int] = deserialize.integer(payload.get("version")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[FlowContext] = None + + @property + def _proxy(self) -> "FlowContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlowContext for this FlowInstance + """ + if self._context is None: + self._context = FlowContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FlowInstance": + """ + Fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlowInstance": + """ + Asynchronous coroutine to fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + return await self._proxy.fetch_async() + + @property + def engagements(self) -> EngagementList: + """ + Access the engagements + """ + return self._proxy.engagements + + @property + def executions(self) -> ExecutionList: + """ + Access the executions + """ + return self._proxy.executions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlowContext + + :param version: Version that contains the resource + :param sid: The SID of the Flow resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Flows/{sid}".format(**self._solution) + + self._engagements: Optional[EngagementList] = None + self._executions: Optional[ExecutionList] = None + + def delete(self) -> bool: + """ + Deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FlowInstance: + """ + Fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FlowInstance: + """ + Asynchronous coroutine to fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + @property + def engagements(self) -> EngagementList: + """ + Access the engagements + """ + if self._engagements is None: + self._engagements = EngagementList( + self._version, + self._solution["sid"], + ) + return self._engagements + + @property + def executions(self) -> ExecutionList: + """ + Access the executions + """ + if self._executions is None: + self._executions = ExecutionList( + self._version, + self._solution["sid"], + ) + return self._executions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FlowInstance: + """ + Build an instance of FlowInstance + + :param payload: Payload response from the API + """ + return FlowInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FlowList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FlowList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Flows" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FlowInstance]: + """ + Streams FlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FlowInstance]: + """ + Asynchronously streams FlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowInstance]: + """ + Lists FlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowInstance]: + """ + Asynchronously lists FlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowPage: + """ + Retrieve a single page of FlowInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FlowPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowPage: + """ + Asynchronously retrieve a single page of FlowInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FlowPage(self._version, response) + + def get_page(self, target_url: str) -> FlowPage: + """ + Retrieve a specific page of FlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FlowPage(self._version, response) + + async def get_page_async(self, target_url: str) -> FlowPage: + """ + Asynchronously retrieve a specific page of FlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FlowPage(self._version, response) + + def get(self, sid: str) -> FlowContext: + """ + Constructs a FlowContext + + :param sid: The SID of the Flow resource to fetch. + """ + return FlowContext(self._version, sid=sid) + + def __call__(self, sid: str) -> FlowContext: + """ + Constructs a FlowContext + + :param sid: The SID of the Flow resource to fetch. + """ + return FlowContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..704114d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__init__.py new file mode 100644 index 0000000..f6d95ab --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__init__.py @@ -0,0 +1,590 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v1.flow.engagement.engagement_context import ( + EngagementContextList, +) +from twilio.rest.studio.v1.flow.engagement.step import StepList + + +class EngagementInstance(InstanceResource): + + class Status(object): + ACTIVE = "active" + ENDED = "ended" + + """ + :ivar sid: The unique string that we created to identify the Engagement resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Engagement resource. + :ivar flow_sid: The SID of the Flow. + :ivar contact_sid: The SID of the Contact. + :ivar contact_channel_address: The phone number, SIP address or Client identifier that triggered this Engagement. Phone numbers are in E.164 format (+16175551212). SIP addresses are formatted as `name@company.com`. Client identifiers are formatted `client:name`. + :ivar context: The current state of the execution flow. As your flow executes, we save the state in a flow context. Your widgets can access the data in the flow context as variables, either in configuration fields or in text areas as variable substitution. + :ivar status: + :ivar date_created: The date and time in GMT when the Engagement was created in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the Engagement was updated in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of the Engagement's nested resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.contact_sid: Optional[str] = payload.get("contact_sid") + self.contact_channel_address: Optional[str] = payload.get( + "contact_channel_address" + ) + self.context: Optional[Dict[str, object]] = payload.get("context") + self.status: Optional["EngagementInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "sid": sid or self.sid, + } + self._context: Optional[EngagementContext] = None + + @property + def _proxy(self) -> "EngagementContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EngagementContext for this EngagementInstance + """ + if self._context is None: + self._context = EngagementContext( + self._version, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the EngagementInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EngagementInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "EngagementInstance": + """ + Fetch the EngagementInstance + + + :returns: The fetched EngagementInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EngagementInstance": + """ + Asynchronous coroutine to fetch the EngagementInstance + + + :returns: The fetched EngagementInstance + """ + return await self._proxy.fetch_async() + + @property + def engagement_context(self) -> EngagementContextList: + """ + Access the engagement_context + """ + return self._proxy.engagement_context + + @property + def steps(self) -> StepList: + """ + Access the steps + """ + return self._proxy.steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EngagementContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, sid: str): + """ + Initialize the EngagementContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow. + :param sid: The SID of the Engagement resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Engagements/{sid}".format(**self._solution) + + self._engagement_context: Optional[EngagementContextList] = None + self._steps: Optional[StepList] = None + + def delete(self) -> bool: + """ + Deletes the EngagementInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EngagementInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> EngagementInstance: + """ + Fetch the EngagementInstance + + + :returns: The fetched EngagementInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EngagementInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EngagementInstance: + """ + Asynchronous coroutine to fetch the EngagementInstance + + + :returns: The fetched EngagementInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EngagementInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + @property + def engagement_context(self) -> EngagementContextList: + """ + Access the engagement_context + """ + if self._engagement_context is None: + self._engagement_context = EngagementContextList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._engagement_context + + @property + def steps(self) -> StepList: + """ + Access the steps + """ + if self._steps is None: + self._steps = StepList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EngagementPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EngagementInstance: + """ + Build an instance of EngagementInstance + + :param payload: Payload response from the API + """ + return EngagementInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EngagementList(ListResource): + + def __init__(self, version: Version, flow_sid: str): + """ + Initialize the EngagementList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow to read Engagements from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + } + self._uri = "/Flows/{flow_sid}/Engagements".format(**self._solution) + + def create( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> EngagementInstance: + """ + Create the EngagementInstance + + :param to: The Contact phone number to start a Studio Flow Engagement, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow Engagement. Available as variable `{{flow.channel.address}}` + :param parameters: A JSON string we will add to your flow's context and that you can access as variables inside your flow. For example, if you pass in `Parameters={'name':'Zeke'}` then inside a widget you can reference the variable `{{flow.data.name}}` which will return the string 'Zeke'. Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode your JSON string. + + :returns: The created EngagementInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EngagementInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + async def create_async( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> EngagementInstance: + """ + Asynchronously create the EngagementInstance + + :param to: The Contact phone number to start a Studio Flow Engagement, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow Engagement. Available as variable `{{flow.channel.address}}` + :param parameters: A JSON string we will add to your flow's context and that you can access as variables inside your flow. For example, if you pass in `Parameters={'name':'Zeke'}` then inside a widget you can reference the variable `{{flow.data.name}}` which will return the string 'Zeke'. Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode your JSON string. + + :returns: The created EngagementInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EngagementInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EngagementInstance]: + """ + Streams EngagementInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EngagementInstance]: + """ + Asynchronously streams EngagementInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EngagementInstance]: + """ + Lists EngagementInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EngagementInstance]: + """ + Asynchronously lists EngagementInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EngagementPage: + """ + Retrieve a single page of EngagementInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EngagementInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EngagementPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EngagementPage: + """ + Asynchronously retrieve a single page of EngagementInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EngagementInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EngagementPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EngagementPage: + """ + Retrieve a specific page of EngagementInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EngagementInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EngagementPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EngagementPage: + """ + Asynchronously retrieve a specific page of EngagementInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EngagementInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EngagementPage(self._version, response, self._solution) + + def get(self, sid: str) -> EngagementContext: + """ + Constructs a EngagementContext + + :param sid: The SID of the Engagement resource to fetch. + """ + return EngagementContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __call__(self, sid: str) -> EngagementContext: + """ + Constructs a EngagementContext + + :param sid: The SID of the Engagement resource to fetch. + """ + return EngagementContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..12f5b44 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/engagement_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/engagement_context.cpython-310.pyc new file mode 100644 index 0000000..0f4868b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/__pycache__/engagement_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/engagement_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/engagement_context.py new file mode 100644 index 0000000..f534db6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/engagement_context.py @@ -0,0 +1,214 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class EngagementContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the Account. + :ivar context: As your flow executes, we save the state in what's called the Flow Context. Any data in the flow context can be accessed by your widgets as variables, either in configuration fields or in text areas as variable substitution. + :ivar engagement_sid: The SID of the Engagement. + :ivar flow_sid: The SID of the Flow. + :ivar url: The URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + engagement_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.engagement_sid: Optional[str] = payload.get("engagement_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + } + self._context: Optional[EngagementContextContext] = None + + @property + def _proxy(self) -> "EngagementContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EngagementContextContext for this EngagementContextInstance + """ + if self._context is None: + self._context = EngagementContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + return self._context + + def fetch(self) -> "EngagementContextInstance": + """ + Fetch the EngagementContextInstance + + + :returns: The fetched EngagementContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EngagementContextInstance": + """ + Asynchronous coroutine to fetch the EngagementContextInstance + + + :returns: The fetched EngagementContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EngagementContextContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, engagement_sid: str): + """ + Initialize the EngagementContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow. + :param engagement_sid: The SID of the Engagement. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + } + self._uri = "/Flows/{flow_sid}/Engagements/{engagement_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> EngagementContextInstance: + """ + Fetch the EngagementContextInstance + + + :returns: The fetched EngagementContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EngagementContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + + async def fetch_async(self) -> EngagementContextInstance: + """ + Asynchronous coroutine to fetch the EngagementContextInstance + + + :returns: The fetched EngagementContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EngagementContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EngagementContextList(ListResource): + + def __init__(self, version: Version, flow_sid: str, engagement_sid: str): + """ + Initialize the EngagementContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow. + :param engagement_sid: The SID of the Engagement. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + } + + def get(self) -> EngagementContextContext: + """ + Constructs a EngagementContextContext + + """ + return EngagementContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + + def __call__(self) -> EngagementContextContext: + """ + Constructs a EngagementContextContext + + """ + return EngagementContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__init__.py new file mode 100644 index 0000000..1a2d857 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__init__.py @@ -0,0 +1,480 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v1.flow.engagement.step.step_context import StepContextList + + +class StepInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Step resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Step resource. + :ivar flow_sid: The SID of the Flow. + :ivar engagement_sid: The SID of the Engagement. + :ivar name: The event that caused the Flow to transition to the Step. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar transitioned_from: The Widget that preceded the Widget for the Step. + :ivar transitioned_to: The Widget that will follow the Widget for the Step. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + engagement_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.engagement_sid: Optional[str] = payload.get("engagement_sid") + self.name: Optional[str] = payload.get("name") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.transitioned_from: Optional[str] = payload.get("transitioned_from") + self.transitioned_to: Optional[str] = payload.get("transitioned_to") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + "sid": sid or self.sid, + } + self._context: Optional[StepContext] = None + + @property + def _proxy(self) -> "StepContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: StepContext for this StepInstance + """ + if self._context is None: + self._context = StepContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "StepInstance": + """ + Fetch the StepInstance + + + :returns: The fetched StepInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "StepInstance": + """ + Asynchronous coroutine to fetch the StepInstance + + + :returns: The fetched StepInstance + """ + return await self._proxy.fetch_async() + + @property + def step_context(self) -> StepContextList: + """ + Access the step_context + """ + return self._proxy.step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StepContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, engagement_sid: str, sid: str): + """ + Initialize the StepContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param engagement_sid: The SID of the Engagement with the Step to fetch. + :param sid: The SID of the Step resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Engagements/{engagement_sid}/Steps/{sid}".format( + **self._solution + ) + + self._step_context: Optional[StepContextList] = None + + def fetch(self) -> StepInstance: + """ + Fetch the StepInstance + + + :returns: The fetched StepInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return StepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> StepInstance: + """ + Asynchronous coroutine to fetch the StepInstance + + + :returns: The fetched StepInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return StepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + sid=self._solution["sid"], + ) + + @property + def step_context(self) -> StepContextList: + """ + Access the step_context + """ + if self._step_context is None: + self._step_context = StepContextList( + self._version, + self._solution["flow_sid"], + self._solution["engagement_sid"], + self._solution["sid"], + ) + return self._step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StepPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> StepInstance: + """ + Build an instance of StepInstance + + :param payload: Payload response from the API + """ + return StepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class StepList(ListResource): + + def __init__(self, version: Version, flow_sid: str, engagement_sid: str): + """ + Initialize the StepList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to read. + :param engagement_sid: The SID of the Engagement with the Step to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + } + self._uri = "/Flows/{flow_sid}/Engagements/{engagement_sid}/Steps".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[StepInstance]: + """ + Streams StepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[StepInstance]: + """ + Asynchronously streams StepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[StepInstance]: + """ + Lists StepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[StepInstance]: + """ + Asynchronously lists StepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> StepPage: + """ + Retrieve a single page of StepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of StepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return StepPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> StepPage: + """ + Asynchronously retrieve a single page of StepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of StepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return StepPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> StepPage: + """ + Retrieve a specific page of StepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of StepInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return StepPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> StepPage: + """ + Asynchronously retrieve a specific page of StepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of StepInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return StepPage(self._version, response, self._solution) + + def get(self, sid: str) -> StepContext: + """ + Constructs a StepContext + + :param sid: The SID of the Step resource to fetch. + """ + return StepContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> StepContext: + """ + Constructs a StepContext + + :param sid: The SID of the Step resource to fetch. + """ + return StepContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..bcd955b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/step_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/step_context.cpython-310.pyc new file mode 100644 index 0000000..317a995 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/__pycache__/step_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/step_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/step_context.py new file mode 100644 index 0000000..69c4c22 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/engagement/step/step_context.py @@ -0,0 +1,231 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class StepContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the StepContext resource. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar engagement_sid: The SID of the Engagement. + :ivar flow_sid: The SID of the Flow. + :ivar step_sid: The SID of the Step the context is associated with. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + engagement_sid: str, + step_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.engagement_sid: Optional[str] = payload.get("engagement_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.step_sid: Optional[str] = payload.get("step_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + "step_sid": step_sid, + } + self._context: Optional[StepContextContext] = None + + @property + def _proxy(self) -> "StepContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: StepContextContext for this StepContextInstance + """ + if self._context is None: + self._context = StepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + step_sid=self._solution["step_sid"], + ) + return self._context + + def fetch(self) -> "StepContextInstance": + """ + Fetch the StepContextInstance + + + :returns: The fetched StepContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "StepContextInstance": + """ + Asynchronous coroutine to fetch the StepContextInstance + + + :returns: The fetched StepContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StepContextContext(InstanceContext): + + def __init__( + self, version: Version, flow_sid: str, engagement_sid: str, step_sid: str + ): + """ + Initialize the StepContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param engagement_sid: The SID of the Engagement with the Step to fetch. + :param step_sid: The SID of the Step to fetch + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + "step_sid": step_sid, + } + self._uri = "/Flows/{flow_sid}/Engagements/{engagement_sid}/Steps/{step_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> StepContextInstance: + """ + Fetch the StepContextInstance + + + :returns: The fetched StepContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return StepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + step_sid=self._solution["step_sid"], + ) + + async def fetch_async(self) -> StepContextInstance: + """ + Asynchronous coroutine to fetch the StepContextInstance + + + :returns: The fetched StepContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return StepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StepContextList(ListResource): + + def __init__( + self, version: Version, flow_sid: str, engagement_sid: str, step_sid: str + ): + """ + Initialize the StepContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param engagement_sid: The SID of the Engagement with the Step to fetch. + :param step_sid: The SID of the Step to fetch + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "engagement_sid": engagement_sid, + "step_sid": step_sid, + } + + def get(self) -> StepContextContext: + """ + Constructs a StepContextContext + + """ + return StepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + step_sid=self._solution["step_sid"], + ) + + def __call__(self) -> StepContextContext: + """ + Constructs a StepContextContext + + """ + return StepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + engagement_sid=self._solution["engagement_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__init__.py new file mode 100644 index 0000000..57239e5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__init__.py @@ -0,0 +1,710 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v1.flow.execution.execution_context import ExecutionContextList +from twilio.rest.studio.v1.flow.execution.execution_step import ExecutionStepList + + +class ExecutionInstance(InstanceResource): + + class Status(object): + ACTIVE = "active" + ENDED = "ended" + + """ + :ivar sid: The unique string that we created to identify the Execution resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Execution resource. + :ivar flow_sid: The SID of the Flow. + :ivar contact_sid: The SID of the Contact. + :ivar contact_channel_address: The phone number, SIP address or Client identifier that triggered the Execution. Phone numbers are in E.164 format (e.g. +16175551212). SIP addresses are formatted as `name@company.com`. Client identifiers are formatted `client:name`. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of nested resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.contact_sid: Optional[str] = payload.get("contact_sid") + self.contact_channel_address: Optional[str] = payload.get( + "contact_channel_address" + ) + self.context: Optional[Dict[str, object]] = payload.get("context") + self.status: Optional["ExecutionInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "sid": sid or self.sid, + } + self._context: Optional[ExecutionContext] = None + + @property + def _proxy(self) -> "ExecutionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionContext for this ExecutionInstance + """ + if self._context is None: + self._context = ExecutionContext( + self._version, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ExecutionInstance": + """ + Fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionInstance": + """ + Asynchronous coroutine to fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + return await self._proxy.fetch_async() + + def update(self, status: "ExecutionInstance.Status") -> "ExecutionInstance": + """ + Update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "ExecutionInstance.Status" + ) -> "ExecutionInstance": + """ + Asynchronous coroutine to update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + return await self._proxy.update_async( + status=status, + ) + + @property + def execution_context(self) -> ExecutionContextList: + """ + Access the execution_context + """ + return self._proxy.execution_context + + @property + def steps(self) -> ExecutionStepList: + """ + Access the steps + """ + return self._proxy.steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, sid: str): + """ + Initialize the ExecutionContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution resources to update. + :param sid: The SID of the Execution resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{sid}".format(**self._solution) + + self._execution_context: Optional[ExecutionContextList] = None + self._steps: Optional[ExecutionStepList] = None + + def delete(self) -> bool: + """ + Deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ExecutionInstance: + """ + Fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ExecutionInstance: + """ + Asynchronous coroutine to fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + def update(self, status: "ExecutionInstance.Status") -> ExecutionInstance: + """ + Update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "ExecutionInstance.Status" + ) -> ExecutionInstance: + """ + Asynchronous coroutine to update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + @property + def execution_context(self) -> ExecutionContextList: + """ + Access the execution_context + """ + if self._execution_context is None: + self._execution_context = ExecutionContextList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._execution_context + + @property + def steps(self) -> ExecutionStepList: + """ + Access the steps + """ + if self._steps is None: + self._steps = ExecutionStepList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ExecutionInstance: + """ + Build an instance of ExecutionInstance + + :param payload: Payload response from the API + """ + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ExecutionList(ListResource): + + def __init__(self, version: Version, flow_sid: str): + """ + Initialize the ExecutionList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + } + self._uri = "/Flows/{flow_sid}/Executions".format(**self._solution) + + def create( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> ExecutionInstance: + """ + Create the ExecutionInstance + + :param to: The Contact phone number to start a Studio Flow Execution, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow's Execution. Available as variable `{{flow.channel.address}}`. For SMS, this can also be a Messaging Service SID. + :param parameters: JSON data that will be added to the Flow's context and that can be accessed as variables inside your Flow. For example, if you pass in `Parameters={\\\"name\\\":\\\"Zeke\\\"}`, a widget in your Flow can reference the variable `{{flow.data.name}}`, which returns \\\"Zeke\\\". Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode the JSON string. + + :returns: The created ExecutionInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + async def create_async( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> ExecutionInstance: + """ + Asynchronously create the ExecutionInstance + + :param to: The Contact phone number to start a Studio Flow Execution, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow's Execution. Available as variable `{{flow.channel.address}}`. For SMS, this can also be a Messaging Service SID. + :param parameters: JSON data that will be added to the Flow's context and that can be accessed as variables inside your Flow. For example, if you pass in `Parameters={\\\"name\\\":\\\"Zeke\\\"}`, a widget in your Flow can reference the variable `{{flow.data.name}}`, which returns \\\"Zeke\\\". Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode the JSON string. + + :returns: The created ExecutionInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def stream( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ExecutionInstance]: + """ + Streams ExecutionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created_from=date_created_from, + date_created_to=date_created_to, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ExecutionInstance]: + """ + Asynchronously streams ExecutionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created_from=date_created_from, + date_created_to=date_created_to, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionInstance]: + """ + Lists ExecutionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created_from=date_created_from, + date_created_to=date_created_to, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionInstance]: + """ + Asynchronously lists ExecutionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created_from=date_created_from, + date_created_to=date_created_to, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionPage: + """ + Retrieve a single page of ExecutionInstance records from the API. + Request is executed immediately + + :param date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionInstance + """ + data = values.of( + { + "DateCreatedFrom": serialize.iso8601_datetime(date_created_from), + "DateCreatedTo": serialize.iso8601_datetime(date_created_to), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ExecutionPage(self._version, response, self._solution) + + async def page_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionPage: + """ + Asynchronously retrieve a single page of ExecutionInstance records from the API. + Request is executed immediately + + :param date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionInstance + """ + data = values.of( + { + "DateCreatedFrom": serialize.iso8601_datetime(date_created_from), + "DateCreatedTo": serialize.iso8601_datetime(date_created_to), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ExecutionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ExecutionPage: + """ + Retrieve a specific page of ExecutionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ExecutionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ExecutionPage: + """ + Asynchronously retrieve a specific page of ExecutionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ExecutionPage(self._version, response, self._solution) + + def get(self, sid: str) -> ExecutionContext: + """ + Constructs a ExecutionContext + + :param sid: The SID of the Execution resource to update. + """ + return ExecutionContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ExecutionContext: + """ + Constructs a ExecutionContext + + :param sid: The SID of the Execution resource to update. + """ + return ExecutionContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a260685 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/execution_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/execution_context.cpython-310.pyc new file mode 100644 index 0000000..f1c3de2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/__pycache__/execution_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_context.py new file mode 100644 index 0000000..a2765bb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_context.py @@ -0,0 +1,214 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExecutionContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionContext resource. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar flow_sid: The SID of the Flow. + :ivar execution_sid: The SID of the context's Execution resource. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._context: Optional[ExecutionContextContext] = None + + @property + def _proxy(self) -> "ExecutionContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionContextContext for this ExecutionContextInstance + """ + if self._context is None: + self._context = ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + return self._context + + def fetch(self) -> "ExecutionContextInstance": + """ + Fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionContextInstance": + """ + Asynchronous coroutine to fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContextContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution context to fetch. + :param execution_sid: The SID of the Execution context to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> ExecutionContextInstance: + """ + Fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + async def fetch_async(self) -> ExecutionContextInstance: + """ + Asynchronous coroutine to fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContextList(ListResource): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution context to fetch. + :param execution_sid: The SID of the Execution context to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + + def get(self) -> ExecutionContextContext: + """ + Constructs a ExecutionContextContext + + """ + return ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __call__(self) -> ExecutionContextContext: + """ + Constructs a ExecutionContextContext + + """ + return ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__init__.py new file mode 100644 index 0000000..6e19ca4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__init__.py @@ -0,0 +1,482 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v1.flow.execution.execution_step.execution_step_context import ( + ExecutionStepContextList, +) + + +class ExecutionStepInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the ExecutionStep resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionStep resource. + :ivar flow_sid: The SID of the Flow. + :ivar execution_sid: The SID of the Step's Execution resource. + :ivar name: The event that caused the Flow to transition to the Step. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar transitioned_from: The Widget that preceded the Widget for the Step. + :ivar transitioned_to: The Widget that will follow the Widget for the Step. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.name: Optional[str] = payload.get("name") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.transitioned_from: Optional[str] = payload.get("transitioned_from") + self.transitioned_to: Optional[str] = payload.get("transitioned_to") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "sid": sid or self.sid, + } + self._context: Optional[ExecutionStepContext] = None + + @property + def _proxy(self) -> "ExecutionStepContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionStepContext for this ExecutionStepInstance + """ + if self._context is None: + self._context = ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ExecutionStepInstance": + """ + Fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionStepInstance": + """ + Asynchronous coroutine to fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + return await self._proxy.fetch_async() + + @property + def step_context(self) -> ExecutionStepContextList: + """ + Access the step_context + """ + return self._proxy.step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str, sid: str): + """ + Initialize the ExecutionStepContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param sid: The SID of the ExecutionStep resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps/{sid}".format( + **self._solution + ) + + self._step_context: Optional[ExecutionStepContextList] = None + + def fetch(self) -> ExecutionStepInstance: + """ + Fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ExecutionStepInstance: + """ + Asynchronous coroutine to fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + + @property + def step_context(self) -> ExecutionStepContextList: + """ + Access the step_context + """ + if self._step_context is None: + self._step_context = ExecutionStepContextList( + self._version, + self._solution["flow_sid"], + self._solution["execution_sid"], + self._solution["sid"], + ) + return self._step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ExecutionStepInstance: + """ + Build an instance of ExecutionStepInstance + + :param payload: Payload response from the API + """ + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ExecutionStepList(ListResource): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionStepList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Steps to read. + :param execution_sid: The SID of the Execution with the Steps to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ExecutionStepInstance]: + """ + Streams ExecutionStepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ExecutionStepInstance]: + """ + Asynchronously streams ExecutionStepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionStepInstance]: + """ + Lists ExecutionStepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionStepInstance]: + """ + Asynchronously lists ExecutionStepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionStepPage: + """ + Retrieve a single page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionStepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ExecutionStepPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionStepPage: + """ + Asynchronously retrieve a single page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionStepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ExecutionStepPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ExecutionStepPage: + """ + Retrieve a specific page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionStepInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ExecutionStepPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ExecutionStepPage: + """ + Asynchronously retrieve a specific page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionStepInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ExecutionStepPage(self._version, response, self._solution) + + def get(self, sid: str) -> ExecutionStepContext: + """ + Constructs a ExecutionStepContext + + :param sid: The SID of the ExecutionStep resource to fetch. + """ + return ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ExecutionStepContext: + """ + Constructs a ExecutionStepContext + + :param sid: The SID of the ExecutionStep resource to fetch. + """ + return ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..341facf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc new file mode 100644 index 0000000..9d9cf60 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/execution_step_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/execution_step_context.py new file mode 100644 index 0000000..224f92d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v1/flow/execution/execution_step/execution_step_context.py @@ -0,0 +1,231 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExecutionStepContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionStepContext resource. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar execution_sid: The SID of the context's Execution resource. + :ivar flow_sid: The SID of the Flow. + :ivar step_sid: The SID of the Step that the context is associated with. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + step_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.step_sid: Optional[str] = payload.get("step_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + self._context: Optional[ExecutionStepContextContext] = None + + @property + def _proxy(self) -> "ExecutionStepContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionStepContextContext for this ExecutionStepContextInstance + """ + if self._context is None: + self._context = ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + return self._context + + def fetch(self) -> "ExecutionStepContextInstance": + """ + Fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionStepContextInstance": + """ + Asynchronous coroutine to fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContextContext(InstanceContext): + + def __init__( + self, version: Version, flow_sid: str, execution_sid: str, step_sid: str + ): + """ + Initialize the ExecutionStepContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param step_sid: The SID of the Step to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps/{step_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> ExecutionStepContextInstance: + """ + Fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionStepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + async def fetch_async(self) -> ExecutionStepContextInstance: + """ + Asynchronous coroutine to fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionStepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContextList(ListResource): + + def __init__( + self, version: Version, flow_sid: str, execution_sid: str, step_sid: str + ): + """ + Initialize the ExecutionStepContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param step_sid: The SID of the Step to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + + def get(self) -> ExecutionStepContextContext: + """ + Constructs a ExecutionStepContextContext + + """ + return ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __call__(self) -> ExecutionStepContextContext: + """ + Constructs a ExecutionStepContextContext + + """ + return ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__init__.py new file mode 100644 index 0000000..83ccc8e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__init__.py @@ -0,0 +1,51 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.studio.v2.flow import FlowList +from twilio.rest.studio.v2.flow_validate import FlowValidateList + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Studio + + :param domain: The Twilio.studio domain + """ + super().__init__(domain, "v2") + self._flows: Optional[FlowList] = None + self._flow_validate: Optional[FlowValidateList] = None + + @property + def flows(self) -> FlowList: + if self._flows is None: + self._flows = FlowList(self) + return self._flows + + @property + def flow_validate(self) -> FlowValidateList: + if self._flow_validate is None: + self._flow_validate = FlowValidateList(self) + return self._flow_validate + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c8a6713 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/flow_validate.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/flow_validate.cpython-310.pyc new file mode 100644 index 0000000..f43da26 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/__pycache__/flow_validate.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__init__.py new file mode 100644 index 0000000..21138d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__init__.py @@ -0,0 +1,716 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v2.flow.execution import ExecutionList +from twilio.rest.studio.v2.flow.flow_revision import FlowRevisionList +from twilio.rest.studio.v2.flow.flow_test_user import FlowTestUserList + + +class FlowInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PUBLISHED = "published" + + """ + :ivar sid: The unique string that we created to identify the Flow resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flow resource. + :ivar friendly_name: The string that you assigned to describe the Flow. + :ivar definition: JSON representation of flow definition. + :ivar status: + :ivar revision: The latest revision number of the Flow's definition. + :ivar commit_message: Description of change made in the revision. + :ivar valid: Boolean if the flow definition is valid. + :ivar errors: List of error in the flow definition. + :ivar warnings: List of warnings in the flow definition. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar webhook_url: + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of the Flow's nested resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.definition: Optional[Dict[str, object]] = payload.get("definition") + self.status: Optional["FlowInstance.Status"] = payload.get("status") + self.revision: Optional[int] = deserialize.integer(payload.get("revision")) + self.commit_message: Optional[str] = payload.get("commit_message") + self.valid: Optional[bool] = payload.get("valid") + self.errors: Optional[List[Dict[str, object]]] = payload.get("errors") + self.warnings: Optional[List[Dict[str, object]]] = payload.get("warnings") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[FlowContext] = None + + @property + def _proxy(self) -> "FlowContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlowContext for this FlowInstance + """ + if self._context is None: + self._context = FlowContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FlowInstance": + """ + Fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlowInstance": + """ + Asynchronous coroutine to fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: "FlowInstance.Status", + friendly_name: Union[str, object] = values.unset, + definition: Union[object, object] = values.unset, + commit_message: Union[str, object] = values.unset, + ) -> "FlowInstance": + """ + Update the FlowInstance + + :param status: + :param friendly_name: The string that you assigned to describe the Flow. + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The updated FlowInstance + """ + return self._proxy.update( + status=status, + friendly_name=friendly_name, + definition=definition, + commit_message=commit_message, + ) + + async def update_async( + self, + status: "FlowInstance.Status", + friendly_name: Union[str, object] = values.unset, + definition: Union[object, object] = values.unset, + commit_message: Union[str, object] = values.unset, + ) -> "FlowInstance": + """ + Asynchronous coroutine to update the FlowInstance + + :param status: + :param friendly_name: The string that you assigned to describe the Flow. + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The updated FlowInstance + """ + return await self._proxy.update_async( + status=status, + friendly_name=friendly_name, + definition=definition, + commit_message=commit_message, + ) + + @property + def executions(self) -> ExecutionList: + """ + Access the executions + """ + return self._proxy.executions + + @property + def revisions(self) -> FlowRevisionList: + """ + Access the revisions + """ + return self._proxy.revisions + + @property + def test_users(self) -> FlowTestUserList: + """ + Access the test_users + """ + return self._proxy.test_users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlowContext + + :param version: Version that contains the resource + :param sid: The SID of the Flow resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Flows/{sid}".format(**self._solution) + + self._executions: Optional[ExecutionList] = None + self._revisions: Optional[FlowRevisionList] = None + self._test_users: Optional[FlowTestUserList] = None + + def delete(self) -> bool: + """ + Deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FlowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FlowInstance: + """ + Fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FlowInstance: + """ + Asynchronous coroutine to fetch the FlowInstance + + + :returns: The fetched FlowInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlowInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + status: "FlowInstance.Status", + friendly_name: Union[str, object] = values.unset, + definition: Union[object, object] = values.unset, + commit_message: Union[str, object] = values.unset, + ) -> FlowInstance: + """ + Update the FlowInstance + + :param status: + :param friendly_name: The string that you assigned to describe the Flow. + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The updated FlowInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + status: "FlowInstance.Status", + friendly_name: Union[str, object] = values.unset, + definition: Union[object, object] = values.unset, + commit_message: Union[str, object] = values.unset, + ) -> FlowInstance: + """ + Asynchronous coroutine to update the FlowInstance + + :param status: + :param friendly_name: The string that you assigned to describe the Flow. + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The updated FlowInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def executions(self) -> ExecutionList: + """ + Access the executions + """ + if self._executions is None: + self._executions = ExecutionList( + self._version, + self._solution["sid"], + ) + return self._executions + + @property + def revisions(self) -> FlowRevisionList: + """ + Access the revisions + """ + if self._revisions is None: + self._revisions = FlowRevisionList( + self._version, + self._solution["sid"], + ) + return self._revisions + + @property + def test_users(self) -> FlowTestUserList: + """ + Access the test_users + """ + if self._test_users is None: + self._test_users = FlowTestUserList( + self._version, + self._solution["sid"], + ) + return self._test_users + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FlowInstance: + """ + Build an instance of FlowInstance + + :param payload: Payload response from the API + """ + return FlowInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FlowList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FlowList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Flows" + + def create( + self, + friendly_name: str, + status: "FlowInstance.Status", + definition: object, + commit_message: Union[str, object] = values.unset, + ) -> FlowInstance: + """ + Create the FlowInstance + + :param friendly_name: The string that you assigned to describe the Flow. + :param status: + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The created FlowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + status: "FlowInstance.Status", + definition: object, + commit_message: Union[str, object] = values.unset, + ) -> FlowInstance: + """ + Asynchronously create the FlowInstance + + :param friendly_name: The string that you assigned to describe the Flow. + :param status: + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The created FlowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FlowInstance]: + """ + Streams FlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FlowInstance]: + """ + Asynchronously streams FlowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowInstance]: + """ + Lists FlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowInstance]: + """ + Asynchronously lists FlowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowPage: + """ + Retrieve a single page of FlowInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FlowPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowPage: + """ + Asynchronously retrieve a single page of FlowInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FlowPage(self._version, response) + + def get_page(self, target_url: str) -> FlowPage: + """ + Retrieve a specific page of FlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FlowPage(self._version, response) + + async def get_page_async(self, target_url: str) -> FlowPage: + """ + Asynchronously retrieve a specific page of FlowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FlowPage(self._version, response) + + def get(self, sid: str) -> FlowContext: + """ + Constructs a FlowContext + + :param sid: The SID of the Flow resource to fetch. + """ + return FlowContext(self._version, sid=sid) + + def __call__(self, sid: str) -> FlowContext: + """ + Constructs a FlowContext + + :param sid: The SID of the Flow resource to fetch. + """ + return FlowContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..dc94cef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_revision.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_revision.cpython-310.pyc new file mode 100644 index 0000000..67243f2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_revision.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_test_user.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_test_user.cpython-310.pyc new file mode 100644 index 0000000..84ddd7d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/__pycache__/flow_test_user.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__init__.py new file mode 100644 index 0000000..38d3b2b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__init__.py @@ -0,0 +1,708 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v2.flow.execution.execution_context import ExecutionContextList +from twilio.rest.studio.v2.flow.execution.execution_step import ExecutionStepList + + +class ExecutionInstance(InstanceResource): + + class Status(object): + ACTIVE = "active" + ENDED = "ended" + + """ + :ivar sid: The unique string that we created to identify the Execution resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Execution resource. + :ivar flow_sid: The SID of the Flow. + :ivar contact_channel_address: The phone number, SIP address or Client identifier that triggered the Execution. Phone numbers are in E.164 format (e.g. +16175551212). SIP addresses are formatted as `name@company.com`. Client identifiers are formatted `client:name`. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of nested resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.contact_channel_address: Optional[str] = payload.get( + "contact_channel_address" + ) + self.context: Optional[Dict[str, object]] = payload.get("context") + self.status: Optional["ExecutionInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "sid": sid or self.sid, + } + self._context: Optional[ExecutionContext] = None + + @property + def _proxy(self) -> "ExecutionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionContext for this ExecutionInstance + """ + if self._context is None: + self._context = ExecutionContext( + self._version, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ExecutionInstance": + """ + Fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionInstance": + """ + Asynchronous coroutine to fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + return await self._proxy.fetch_async() + + def update(self, status: "ExecutionInstance.Status") -> "ExecutionInstance": + """ + Update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "ExecutionInstance.Status" + ) -> "ExecutionInstance": + """ + Asynchronous coroutine to update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + return await self._proxy.update_async( + status=status, + ) + + @property + def execution_context(self) -> ExecutionContextList: + """ + Access the execution_context + """ + return self._proxy.execution_context + + @property + def steps(self) -> ExecutionStepList: + """ + Access the steps + """ + return self._proxy.steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, sid: str): + """ + Initialize the ExecutionContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution resources to update. + :param sid: The SID of the Execution resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{sid}".format(**self._solution) + + self._execution_context: Optional[ExecutionContextList] = None + self._steps: Optional[ExecutionStepList] = None + + def delete(self) -> bool: + """ + Deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ExecutionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ExecutionInstance: + """ + Fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ExecutionInstance: + """ + Asynchronous coroutine to fetch the ExecutionInstance + + + :returns: The fetched ExecutionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + def update(self, status: "ExecutionInstance.Status") -> ExecutionInstance: + """ + Update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "ExecutionInstance.Status" + ) -> ExecutionInstance: + """ + Asynchronous coroutine to update the ExecutionInstance + + :param status: + + :returns: The updated ExecutionInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + sid=self._solution["sid"], + ) + + @property + def execution_context(self) -> ExecutionContextList: + """ + Access the execution_context + """ + if self._execution_context is None: + self._execution_context = ExecutionContextList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._execution_context + + @property + def steps(self) -> ExecutionStepList: + """ + Access the steps + """ + if self._steps is None: + self._steps = ExecutionStepList( + self._version, + self._solution["flow_sid"], + self._solution["sid"], + ) + return self._steps + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ExecutionInstance: + """ + Build an instance of ExecutionInstance + + :param payload: Payload response from the API + """ + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ExecutionList(ListResource): + + def __init__(self, version: Version, flow_sid: str): + """ + Initialize the ExecutionList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + } + self._uri = "/Flows/{flow_sid}/Executions".format(**self._solution) + + def create( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> ExecutionInstance: + """ + Create the ExecutionInstance + + :param to: The Contact phone number to start a Studio Flow Execution, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow's Execution. Available as variable `{{flow.channel.address}}`. For SMS, this can also be a Messaging Service SID. + :param parameters: JSON data that will be added to the Flow's context and that can be accessed as variables inside your Flow. For example, if you pass in `Parameters={\\\"name\\\":\\\"Zeke\\\"}`, a widget in your Flow can reference the variable `{{flow.data.name}}`, which returns \\\"Zeke\\\". Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode the JSON string. + + :returns: The created ExecutionInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + async def create_async( + self, to: str, from_: str, parameters: Union[object, object] = values.unset + ) -> ExecutionInstance: + """ + Asynchronously create the ExecutionInstance + + :param to: The Contact phone number to start a Studio Flow Execution, available as variable `{{contact.channel.address}}`. + :param from_: The Twilio phone number to send messages or initiate calls from during the Flow's Execution. Available as variable `{{flow.channel.address}}`. For SMS, this can also be a Messaging Service SID. + :param parameters: JSON data that will be added to the Flow's context and that can be accessed as variables inside your Flow. For example, if you pass in `Parameters={\\\"name\\\":\\\"Zeke\\\"}`, a widget in your Flow can reference the variable `{{flow.data.name}}`, which returns \\\"Zeke\\\". Note: the JSON value must explicitly be passed as a string, not as a hash object. Depending on your particular HTTP library, you may need to add quotes or URL encode the JSON string. + + :returns: The created ExecutionInstance + """ + + data = values.of( + { + "To": to, + "From": from_, + "Parameters": serialize.object(parameters), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ExecutionInstance( + self._version, payload, flow_sid=self._solution["flow_sid"] + ) + + def stream( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ExecutionInstance]: + """ + Streams ExecutionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created_from=date_created_from, + date_created_to=date_created_to, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ExecutionInstance]: + """ + Asynchronously streams ExecutionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created_from=date_created_from, + date_created_to=date_created_to, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionInstance]: + """ + Lists ExecutionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created_from=date_created_from, + date_created_to=date_created_to, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionInstance]: + """ + Asynchronously lists ExecutionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param datetime date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created_from=date_created_from, + date_created_to=date_created_to, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionPage: + """ + Retrieve a single page of ExecutionInstance records from the API. + Request is executed immediately + + :param date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionInstance + """ + data = values.of( + { + "DateCreatedFrom": serialize.iso8601_datetime(date_created_from), + "DateCreatedTo": serialize.iso8601_datetime(date_created_to), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ExecutionPage(self._version, response, self._solution) + + async def page_async( + self, + date_created_from: Union[datetime, object] = values.unset, + date_created_to: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionPage: + """ + Asynchronously retrieve a single page of ExecutionInstance records from the API. + Request is executed immediately + + :param date_created_from: Only show Execution resources starting on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param date_created_to: Only show Execution resources starting before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time, given as `YYYY-MM-DDThh:mm:ss-hh:mm`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionInstance + """ + data = values.of( + { + "DateCreatedFrom": serialize.iso8601_datetime(date_created_from), + "DateCreatedTo": serialize.iso8601_datetime(date_created_to), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ExecutionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ExecutionPage: + """ + Retrieve a specific page of ExecutionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ExecutionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ExecutionPage: + """ + Asynchronously retrieve a specific page of ExecutionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ExecutionPage(self._version, response, self._solution) + + def get(self, sid: str) -> ExecutionContext: + """ + Constructs a ExecutionContext + + :param sid: The SID of the Execution resource to update. + """ + return ExecutionContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ExecutionContext: + """ + Constructs a ExecutionContext + + :param sid: The SID of the Execution resource to update. + """ + return ExecutionContext( + self._version, flow_sid=self._solution["flow_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..352ff79 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/execution_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/execution_context.cpython-310.pyc new file mode 100644 index 0000000..2aad1f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/__pycache__/execution_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_context.py new file mode 100644 index 0000000..facb52e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_context.py @@ -0,0 +1,214 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExecutionContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionContext resource. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar flow_sid: The SID of the Flow. + :ivar execution_sid: The SID of the context's Execution resource. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._context: Optional[ExecutionContextContext] = None + + @property + def _proxy(self) -> "ExecutionContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionContextContext for this ExecutionContextInstance + """ + if self._context is None: + self._context = ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + return self._context + + def fetch(self) -> "ExecutionContextInstance": + """ + Fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionContextInstance": + """ + Asynchronous coroutine to fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContextContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution context to fetch. + :param execution_sid: The SID of the Execution context to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> ExecutionContextInstance: + """ + Fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + async def fetch_async(self) -> ExecutionContextInstance: + """ + Asynchronous coroutine to fetch the ExecutionContextInstance + + + :returns: The fetched ExecutionContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionContextList(ListResource): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Execution context to fetch. + :param execution_sid: The SID of the Execution context to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + + def get(self) -> ExecutionContextContext: + """ + Constructs a ExecutionContextContext + + """ + return ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __call__(self) -> ExecutionContextContext: + """ + Constructs a ExecutionContextContext + + """ + return ExecutionContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__init__.py new file mode 100644 index 0000000..257e16b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__init__.py @@ -0,0 +1,482 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.studio.v2.flow.execution.execution_step.execution_step_context import ( + ExecutionStepContextList, +) + + +class ExecutionStepInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the ExecutionStep resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionStep resource. + :ivar flow_sid: The SID of the Flow. + :ivar execution_sid: The SID of the Step's Execution resource. + :ivar name: The event that caused the Flow to transition to the Step. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar transitioned_from: The Widget that preceded the Widget for the Step. + :ivar transitioned_to: The Widget that will follow the Widget for the Step. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.name: Optional[str] = payload.get("name") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.transitioned_from: Optional[str] = payload.get("transitioned_from") + self.transitioned_to: Optional[str] = payload.get("transitioned_to") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "sid": sid or self.sid, + } + self._context: Optional[ExecutionStepContext] = None + + @property + def _proxy(self) -> "ExecutionStepContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionStepContext for this ExecutionStepInstance + """ + if self._context is None: + self._context = ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ExecutionStepInstance": + """ + Fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionStepInstance": + """ + Asynchronous coroutine to fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + return await self._proxy.fetch_async() + + @property + def step_context(self) -> ExecutionStepContextList: + """ + Access the step_context + """ + return self._proxy.step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContext(InstanceContext): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str, sid: str): + """ + Initialize the ExecutionStepContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param sid: The SID of the ExecutionStep resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "sid": sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps/{sid}".format( + **self._solution + ) + + self._step_context: Optional[ExecutionStepContextList] = None + + def fetch(self) -> ExecutionStepInstance: + """ + Fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ExecutionStepInstance: + """ + Asynchronous coroutine to fetch the ExecutionStepInstance + + + :returns: The fetched ExecutionStepInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=self._solution["sid"], + ) + + @property + def step_context(self) -> ExecutionStepContextList: + """ + Access the step_context + """ + if self._step_context is None: + self._step_context = ExecutionStepContextList( + self._version, + self._solution["flow_sid"], + self._solution["execution_sid"], + self._solution["sid"], + ) + return self._step_context + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ExecutionStepInstance: + """ + Build an instance of ExecutionStepInstance + + :param payload: Payload response from the API + """ + return ExecutionStepInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ExecutionStepList(ListResource): + + def __init__(self, version: Version, flow_sid: str, execution_sid: str): + """ + Initialize the ExecutionStepList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Steps to read. + :param execution_sid: The SID of the Execution with the Steps to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ExecutionStepInstance]: + """ + Streams ExecutionStepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ExecutionStepInstance]: + """ + Asynchronously streams ExecutionStepInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionStepInstance]: + """ + Lists ExecutionStepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ExecutionStepInstance]: + """ + Asynchronously lists ExecutionStepInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionStepPage: + """ + Retrieve a single page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionStepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ExecutionStepPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ExecutionStepPage: + """ + Asynchronously retrieve a single page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ExecutionStepInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ExecutionStepPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ExecutionStepPage: + """ + Retrieve a specific page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionStepInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ExecutionStepPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ExecutionStepPage: + """ + Asynchronously retrieve a specific page of ExecutionStepInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ExecutionStepInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ExecutionStepPage(self._version, response, self._solution) + + def get(self, sid: str) -> ExecutionStepContext: + """ + Constructs a ExecutionStepContext + + :param sid: The SID of the ExecutionStep resource to fetch. + """ + return ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ExecutionStepContext: + """ + Constructs a ExecutionStepContext + + :param sid: The SID of the ExecutionStep resource to fetch. + """ + return ExecutionStepContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6b42099 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc new file mode 100644 index 0000000..269826b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/__pycache__/execution_step_context.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/execution_step_context.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/execution_step_context.py new file mode 100644 index 0000000..4166123 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/execution/execution_step/execution_step_context.py @@ -0,0 +1,231 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ExecutionStepContextInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the ExecutionStepContext resource. + :ivar context: The current state of the Flow's Execution. As a flow executes, we save its state in this context. We save data that your widgets can access as variables in configuration fields or in text areas as variable substitution. + :ivar execution_sid: The SID of the context's Execution resource. + :ivar flow_sid: The SID of the Flow. + :ivar step_sid: The SID of the Step that the context is associated with. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + flow_sid: str, + execution_sid: str, + step_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.context: Optional[Dict[str, object]] = payload.get("context") + self.execution_sid: Optional[str] = payload.get("execution_sid") + self.flow_sid: Optional[str] = payload.get("flow_sid") + self.step_sid: Optional[str] = payload.get("step_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + self._context: Optional[ExecutionStepContextContext] = None + + @property + def _proxy(self) -> "ExecutionStepContextContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ExecutionStepContextContext for this ExecutionStepContextInstance + """ + if self._context is None: + self._context = ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + return self._context + + def fetch(self) -> "ExecutionStepContextInstance": + """ + Fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ExecutionStepContextInstance": + """ + Asynchronous coroutine to fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContextContext(InstanceContext): + + def __init__( + self, version: Version, flow_sid: str, execution_sid: str, step_sid: str + ): + """ + Initialize the ExecutionStepContextContext + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param step_sid: The SID of the Step to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + self._uri = "/Flows/{flow_sid}/Executions/{execution_sid}/Steps/{step_sid}/Context".format( + **self._solution + ) + + def fetch(self) -> ExecutionStepContextInstance: + """ + Fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ExecutionStepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + async def fetch_async(self) -> ExecutionStepContextInstance: + """ + Asynchronous coroutine to fetch the ExecutionStepContextInstance + + + :returns: The fetched ExecutionStepContextInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ExecutionStepContextInstance( + self._version, + payload, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ExecutionStepContextList(ListResource): + + def __init__( + self, version: Version, flow_sid: str, execution_sid: str, step_sid: str + ): + """ + Initialize the ExecutionStepContextList + + :param version: Version that contains the resource + :param flow_sid: The SID of the Flow with the Step to fetch. + :param execution_sid: The SID of the Execution resource with the Step to fetch. + :param step_sid: The SID of the Step to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "flow_sid": flow_sid, + "execution_sid": execution_sid, + "step_sid": step_sid, + } + + def get(self) -> ExecutionStepContextContext: + """ + Constructs a ExecutionStepContextContext + + """ + return ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __call__(self) -> ExecutionStepContextContext: + """ + Constructs a ExecutionStepContextContext + + """ + return ExecutionStepContextContext( + self._version, + flow_sid=self._solution["flow_sid"], + execution_sid=self._solution["execution_sid"], + step_sid=self._solution["step_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_revision.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_revision.py new file mode 100644 index 0000000..0de9151 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_revision.py @@ -0,0 +1,437 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class FlowRevisionInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PUBLISHED = "published" + + """ + :ivar sid: The unique string that we created to identify the Flow resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Flow resource. + :ivar friendly_name: The string that you assigned to describe the Flow. + :ivar definition: JSON representation of flow definition. + :ivar status: + :ivar revision: The latest revision number of the Flow's definition. + :ivar commit_message: Description of change made in the revision. + :ivar valid: Boolean if the flow definition is valid. + :ivar errors: List of error in the flow definition. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + sid: str, + revision: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.definition: Optional[Dict[str, object]] = payload.get("definition") + self.status: Optional["FlowRevisionInstance.Status"] = payload.get("status") + self.revision: Optional[int] = deserialize.integer(payload.get("revision")) + self.commit_message: Optional[str] = payload.get("commit_message") + self.valid: Optional[bool] = payload.get("valid") + self.errors: Optional[List[Dict[str, object]]] = payload.get("errors") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid, + "revision": revision or self.revision, + } + self._context: Optional[FlowRevisionContext] = None + + @property + def _proxy(self) -> "FlowRevisionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlowRevisionContext for this FlowRevisionInstance + """ + if self._context is None: + self._context = FlowRevisionContext( + self._version, + sid=self._solution["sid"], + revision=self._solution["revision"], + ) + return self._context + + def fetch(self) -> "FlowRevisionInstance": + """ + Fetch the FlowRevisionInstance + + + :returns: The fetched FlowRevisionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlowRevisionInstance": + """ + Asynchronous coroutine to fetch the FlowRevisionInstance + + + :returns: The fetched FlowRevisionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowRevisionContext(InstanceContext): + + def __init__(self, version: Version, sid: str, revision: str): + """ + Initialize the FlowRevisionContext + + :param version: Version that contains the resource + :param sid: The SID of the Flow resource to fetch. + :param revision: Specific Revision number or can be `LatestPublished` and `LatestRevision`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + "revision": revision, + } + self._uri = "/Flows/{sid}/Revisions/{revision}".format(**self._solution) + + def fetch(self) -> FlowRevisionInstance: + """ + Fetch the FlowRevisionInstance + + + :returns: The fetched FlowRevisionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlowRevisionInstance( + self._version, + payload, + sid=self._solution["sid"], + revision=self._solution["revision"], + ) + + async def fetch_async(self) -> FlowRevisionInstance: + """ + Asynchronous coroutine to fetch the FlowRevisionInstance + + + :returns: The fetched FlowRevisionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlowRevisionInstance( + self._version, + payload, + sid=self._solution["sid"], + revision=self._solution["revision"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowRevisionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FlowRevisionInstance: + """ + Build an instance of FlowRevisionInstance + + :param payload: Payload response from the API + """ + return FlowRevisionInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FlowRevisionList(ListResource): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlowRevisionList + + :param version: Version that contains the resource + :param sid: The SID of the Flow resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Flows/{sid}/Revisions".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FlowRevisionInstance]: + """ + Streams FlowRevisionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FlowRevisionInstance]: + """ + Asynchronously streams FlowRevisionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowRevisionInstance]: + """ + Lists FlowRevisionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FlowRevisionInstance]: + """ + Asynchronously lists FlowRevisionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowRevisionPage: + """ + Retrieve a single page of FlowRevisionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowRevisionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FlowRevisionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FlowRevisionPage: + """ + Asynchronously retrieve a single page of FlowRevisionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FlowRevisionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FlowRevisionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> FlowRevisionPage: + """ + Retrieve a specific page of FlowRevisionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowRevisionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FlowRevisionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> FlowRevisionPage: + """ + Asynchronously retrieve a specific page of FlowRevisionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FlowRevisionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FlowRevisionPage(self._version, response, self._solution) + + def get(self, revision: str) -> FlowRevisionContext: + """ + Constructs a FlowRevisionContext + + :param revision: Specific Revision number or can be `LatestPublished` and `LatestRevision`. + """ + return FlowRevisionContext( + self._version, sid=self._solution["sid"], revision=revision + ) + + def __call__(self, revision: str) -> FlowRevisionContext: + """ + Constructs a FlowRevisionContext + + :param revision: Specific Revision number or can be `LatestPublished` and `LatestRevision`. + """ + return FlowRevisionContext( + self._version, sid=self._solution["sid"], revision=revision + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_test_user.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_test_user.py new file mode 100644 index 0000000..3465632 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow/flow_test_user.py @@ -0,0 +1,255 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FlowTestUserInstance(InstanceResource): + """ + :ivar sid: Unique identifier of the flow. + :ivar test_users: List of test user identities that can test draft versions of the flow. + :ivar url: The URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.test_users: Optional[List[str]] = payload.get("test_users") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid, + } + self._context: Optional[FlowTestUserContext] = None + + @property + def _proxy(self) -> "FlowTestUserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FlowTestUserContext for this FlowTestUserInstance + """ + if self._context is None: + self._context = FlowTestUserContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "FlowTestUserInstance": + """ + Fetch the FlowTestUserInstance + + + :returns: The fetched FlowTestUserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FlowTestUserInstance": + """ + Asynchronous coroutine to fetch the FlowTestUserInstance + + + :returns: The fetched FlowTestUserInstance + """ + return await self._proxy.fetch_async() + + def update(self, test_users: List[str]) -> "FlowTestUserInstance": + """ + Update the FlowTestUserInstance + + :param test_users: List of test user identities that can test draft versions of the flow. + + :returns: The updated FlowTestUserInstance + """ + return self._proxy.update( + test_users=test_users, + ) + + async def update_async(self, test_users: List[str]) -> "FlowTestUserInstance": + """ + Asynchronous coroutine to update the FlowTestUserInstance + + :param test_users: List of test user identities that can test draft versions of the flow. + + :returns: The updated FlowTestUserInstance + """ + return await self._proxy.update_async( + test_users=test_users, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowTestUserContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlowTestUserContext + + :param version: Version that contains the resource + :param sid: Unique identifier of the flow. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Flows/{sid}/TestUsers".format(**self._solution) + + def fetch(self) -> FlowTestUserInstance: + """ + Fetch the FlowTestUserInstance + + + :returns: The fetched FlowTestUserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FlowTestUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FlowTestUserInstance: + """ + Asynchronous coroutine to fetch the FlowTestUserInstance + + + :returns: The fetched FlowTestUserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FlowTestUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, test_users: List[str]) -> FlowTestUserInstance: + """ + Update the FlowTestUserInstance + + :param test_users: List of test user identities that can test draft versions of the flow. + + :returns: The updated FlowTestUserInstance + """ + data = values.of( + { + "TestUsers": serialize.map(test_users, lambda e: e), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowTestUserInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async(self, test_users: List[str]) -> FlowTestUserInstance: + """ + Asynchronous coroutine to update the FlowTestUserInstance + + :param test_users: List of test user identities that can test draft versions of the flow. + + :returns: The updated FlowTestUserInstance + """ + data = values.of( + { + "TestUsers": serialize.map(test_users, lambda e: e), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowTestUserInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FlowTestUserList(ListResource): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FlowTestUserList + + :param version: Version that contains the resource + :param sid: Unique identifier of the flow. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + + def get(self) -> FlowTestUserContext: + """ + Constructs a FlowTestUserContext + + """ + return FlowTestUserContext(self._version, sid=self._solution["sid"]) + + def __call__(self) -> FlowTestUserContext: + """ + Constructs a FlowTestUserContext + + """ + return FlowTestUserContext(self._version, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow_validate.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow_validate.py new file mode 100644 index 0000000..43f226e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/studio/v2/flow_validate.py @@ -0,0 +1,137 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Studio + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FlowValidateInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PUBLISHED = "published" + + """ + :ivar valid: Boolean if the flow definition is valid. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.valid: Optional[bool] = payload.get("valid") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class FlowValidateList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FlowValidateList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Flows/Validate" + + def update( + self, + friendly_name: str, + status: "FlowValidateInstance.Status", + definition: object, + commit_message: Union[str, object] = values.unset, + ) -> FlowValidateInstance: + """ + Update the FlowValidateInstance + + :param friendly_name: The string that you assigned to describe the Flow. + :param status: + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The created FlowValidateInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowValidateInstance(self._version, payload) + + async def update_async( + self, + friendly_name: str, + status: "FlowValidateInstance.Status", + definition: object, + commit_message: Union[str, object] = values.unset, + ) -> FlowValidateInstance: + """ + Asynchronously update the FlowValidateInstance + + :param friendly_name: The string that you assigned to describe the Flow. + :param status: + :param definition: JSON representation of flow definition. + :param commit_message: Description of change made in the revision. + + :returns: The created FlowValidateInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Status": status, + "Definition": serialize.object(definition), + "CommitMessage": commit_message, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FlowValidateInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/SupersimBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/SupersimBase.py new file mode 100644 index 0000000..dd73d12 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/SupersimBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.supersim.v1 import V1 + + +class SupersimBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Supersim Domain + + :returns: Domain for Supersim + """ + super().__init__(twilio, "https://supersim.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Supersim + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__init__.py new file mode 100644 index 0000000..98928da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__init__.py @@ -0,0 +1,93 @@ +from warnings import warn + +from twilio.rest.supersim.SupersimBase import SupersimBase +from twilio.rest.supersim.v1.esim_profile import EsimProfileList +from twilio.rest.supersim.v1.fleet import FleetList +from twilio.rest.supersim.v1.ip_command import IpCommandList +from twilio.rest.supersim.v1.network import NetworkList +from twilio.rest.supersim.v1.network_access_profile import NetworkAccessProfileList +from twilio.rest.supersim.v1.settings_update import SettingsUpdateList +from twilio.rest.supersim.v1.sim import SimList +from twilio.rest.supersim.v1.sms_command import SmsCommandList +from twilio.rest.supersim.v1.usage_record import UsageRecordList + + +class Supersim(SupersimBase): + @property + def esim_profiles(self) -> EsimProfileList: + warn( + "esim_profiles is deprecated. Use v1.esim_profiles instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.esim_profiles + + @property + def fleets(self) -> FleetList: + warn( + "fleets is deprecated. Use v1.fleets instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.fleets + + @property + def ip_commands(self) -> IpCommandList: + warn( + "ip_commands is deprecated. Use v1.ip_commands instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.ip_commands + + @property + def networks(self) -> NetworkList: + warn( + "networks is deprecated. Use v1.networks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.networks + + @property + def network_access_profiles(self) -> NetworkAccessProfileList: + warn( + "network_access_profiles is deprecated. Use v1.network_access_profiles instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.network_access_profiles + + @property + def settings_updates(self) -> SettingsUpdateList: + warn( + "settings_updates is deprecated. Use v1.settings_updates instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.settings_updates + + @property + def sims(self) -> SimList: + warn( + "sims is deprecated. Use v1.sims instead.", DeprecationWarning, stacklevel=2 + ) + return self.v1.sims + + @property + def sms_commands(self) -> SmsCommandList: + warn( + "sms_commands is deprecated. Use v1.sms_commands instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.sms_commands + + @property + def usage_records(self) -> UsageRecordList: + warn( + "usage_records is deprecated. Use v1.usage_records instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.usage_records diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/SupersimBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/SupersimBase.cpython-310.pyc new file mode 100644 index 0000000..5dc60ae Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/SupersimBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..dbd42fd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__init__.py new file mode 100644 index 0000000..2336da2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__init__.py @@ -0,0 +1,107 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.supersim.v1.esim_profile import EsimProfileList +from twilio.rest.supersim.v1.fleet import FleetList +from twilio.rest.supersim.v1.ip_command import IpCommandList +from twilio.rest.supersim.v1.network import NetworkList +from twilio.rest.supersim.v1.network_access_profile import NetworkAccessProfileList +from twilio.rest.supersim.v1.settings_update import SettingsUpdateList +from twilio.rest.supersim.v1.sim import SimList +from twilio.rest.supersim.v1.sms_command import SmsCommandList +from twilio.rest.supersim.v1.usage_record import UsageRecordList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Supersim + + :param domain: The Twilio.supersim domain + """ + super().__init__(domain, "v1") + self._esim_profiles: Optional[EsimProfileList] = None + self._fleets: Optional[FleetList] = None + self._ip_commands: Optional[IpCommandList] = None + self._networks: Optional[NetworkList] = None + self._network_access_profiles: Optional[NetworkAccessProfileList] = None + self._settings_updates: Optional[SettingsUpdateList] = None + self._sims: Optional[SimList] = None + self._sms_commands: Optional[SmsCommandList] = None + self._usage_records: Optional[UsageRecordList] = None + + @property + def esim_profiles(self) -> EsimProfileList: + if self._esim_profiles is None: + self._esim_profiles = EsimProfileList(self) + return self._esim_profiles + + @property + def fleets(self) -> FleetList: + if self._fleets is None: + self._fleets = FleetList(self) + return self._fleets + + @property + def ip_commands(self) -> IpCommandList: + if self._ip_commands is None: + self._ip_commands = IpCommandList(self) + return self._ip_commands + + @property + def networks(self) -> NetworkList: + if self._networks is None: + self._networks = NetworkList(self) + return self._networks + + @property + def network_access_profiles(self) -> NetworkAccessProfileList: + if self._network_access_profiles is None: + self._network_access_profiles = NetworkAccessProfileList(self) + return self._network_access_profiles + + @property + def settings_updates(self) -> SettingsUpdateList: + if self._settings_updates is None: + self._settings_updates = SettingsUpdateList(self) + return self._settings_updates + + @property + def sims(self) -> SimList: + if self._sims is None: + self._sims = SimList(self) + return self._sims + + @property + def sms_commands(self) -> SmsCommandList: + if self._sms_commands is None: + self._sms_commands = SmsCommandList(self) + return self._sms_commands + + @property + def usage_records(self) -> UsageRecordList: + if self._usage_records is None: + self._usage_records = UsageRecordList(self) + return self._usage_records + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a73f912 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/esim_profile.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/esim_profile.cpython-310.pyc new file mode 100644 index 0000000..15f3b5e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/esim_profile.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/fleet.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/fleet.cpython-310.pyc new file mode 100644 index 0000000..b3b02f0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/fleet.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/ip_command.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/ip_command.cpython-310.pyc new file mode 100644 index 0000000..0661bc0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/ip_command.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/network.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/network.cpython-310.pyc new file mode 100644 index 0000000..b995e2f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/network.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/settings_update.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/settings_update.cpython-310.pyc new file mode 100644 index 0000000..c2d5416 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/settings_update.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/sms_command.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/sms_command.cpython-310.pyc new file mode 100644 index 0000000..c1c4a83 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/sms_command.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/usage_record.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/usage_record.cpython-310.pyc new file mode 100644 index 0000000..314525e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/__pycache__/usage_record.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/esim_profile.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/esim_profile.py new file mode 100644 index 0000000..847bcae --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/esim_profile.py @@ -0,0 +1,548 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EsimProfileInstance(InstanceResource): + + class Status(object): + NEW = "new" + RESERVING = "reserving" + AVAILABLE = "available" + DOWNLOADED = "downloaded" + INSTALLED = "installed" + FAILED = "failed" + + """ + :ivar sid: The unique string that we created to identify the eSIM Profile resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the eSIM Profile resource belongs. + :ivar iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with the Sim resource. + :ivar sim_sid: The SID of the [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource that this eSIM Profile controls. + :ivar status: + :ivar eid: Identifier of the eUICC that can claim the eSIM Profile. + :ivar smdp_plus_address: Address of the SM-DP+ server from which the Profile will be downloaded. The URL will appear once the eSIM Profile reaches the status `available`. + :ivar matching_id: Unique identifier of the eSIM profile that can be used to identify and download the eSIM profile from the SM-DP+ server. Populated if `generate_matching_id` is set to `true` when creating the eSIM profile reservation. + :ivar activation_code: Combined machine-readable activation code for acquiring an eSIM Profile with the Activation Code download method. Can be used in a QR code to download an eSIM profile. + :ivar error_code: Code indicating the failure if the download of the SIM Profile failed and the eSIM Profile is in `failed` state. + :ivar error_message: Error message describing the failure if the download of the SIM Profile failed and the eSIM Profile is in `failed` state. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the eSIM Profile resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.iccid: Optional[str] = payload.get("iccid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.status: Optional["EsimProfileInstance.Status"] = payload.get("status") + self.eid: Optional[str] = payload.get("eid") + self.smdp_plus_address: Optional[str] = payload.get("smdp_plus_address") + self.matching_id: Optional[str] = payload.get("matching_id") + self.activation_code: Optional[str] = payload.get("activation_code") + self.error_code: Optional[str] = payload.get("error_code") + self.error_message: Optional[str] = payload.get("error_message") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EsimProfileContext] = None + + @property + def _proxy(self) -> "EsimProfileContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EsimProfileContext for this EsimProfileInstance + """ + if self._context is None: + self._context = EsimProfileContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EsimProfileInstance": + """ + Fetch the EsimProfileInstance + + + :returns: The fetched EsimProfileInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EsimProfileInstance": + """ + Asynchronous coroutine to fetch the EsimProfileInstance + + + :returns: The fetched EsimProfileInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EsimProfileContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EsimProfileContext + + :param version: Version that contains the resource + :param sid: The SID of the eSIM Profile resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/ESimProfiles/{sid}".format(**self._solution) + + def fetch(self) -> EsimProfileInstance: + """ + Fetch the EsimProfileInstance + + + :returns: The fetched EsimProfileInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EsimProfileInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EsimProfileInstance: + """ + Asynchronous coroutine to fetch the EsimProfileInstance + + + :returns: The fetched EsimProfileInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EsimProfileInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EsimProfilePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EsimProfileInstance: + """ + Build an instance of EsimProfileInstance + + :param payload: Payload response from the API + """ + return EsimProfileInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EsimProfileList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EsimProfileList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ESimProfiles" + + def create( + self, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + generate_matching_id: Union[bool, object] = values.unset, + eid: Union[str, object] = values.unset, + ) -> EsimProfileInstance: + """ + Create the EsimProfileInstance + + :param callback_url: The URL we should call using the `callback_method` when the status of the eSIM Profile changes. At this stage of the eSIM Profile pilot, the a request to the URL will only be called when the ESimProfile resource changes from `reserving` to `available`. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param generate_matching_id: When set to `true`, a value for `Eid` does not need to be provided. Instead, when the eSIM profile is reserved, a matching ID will be generated and returned via the `matching_id` property. This identifies the specific eSIM profile that can be used by any capable device to claim and download the profile. + :param eid: Identifier of the eUICC that will claim the eSIM Profile. + + :returns: The created EsimProfileInstance + """ + + data = values.of( + { + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + "GenerateMatchingId": serialize.boolean_to_string(generate_matching_id), + "Eid": eid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EsimProfileInstance(self._version, payload) + + async def create_async( + self, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + generate_matching_id: Union[bool, object] = values.unset, + eid: Union[str, object] = values.unset, + ) -> EsimProfileInstance: + """ + Asynchronously create the EsimProfileInstance + + :param callback_url: The URL we should call using the `callback_method` when the status of the eSIM Profile changes. At this stage of the eSIM Profile pilot, the a request to the URL will only be called when the ESimProfile resource changes from `reserving` to `available`. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param generate_matching_id: When set to `true`, a value for `Eid` does not need to be provided. Instead, when the eSIM profile is reserved, a matching ID will be generated and returned via the `matching_id` property. This identifies the specific eSIM profile that can be used by any capable device to claim and download the profile. + :param eid: Identifier of the eUICC that will claim the eSIM Profile. + + :returns: The created EsimProfileInstance + """ + + data = values.of( + { + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + "GenerateMatchingId": serialize.boolean_to_string(generate_matching_id), + "Eid": eid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EsimProfileInstance(self._version, payload) + + def stream( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EsimProfileInstance]: + """ + Streams EsimProfileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str eid: List the eSIM Profiles that have been associated with an EId. + :param str sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param "EsimProfileInstance.Status" status: List the eSIM Profiles that are in a given status. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + eid=eid, sim_sid=sim_sid, status=status, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EsimProfileInstance]: + """ + Asynchronously streams EsimProfileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str eid: List the eSIM Profiles that have been associated with an EId. + :param str sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param "EsimProfileInstance.Status" status: List the eSIM Profiles that are in a given status. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + eid=eid, sim_sid=sim_sid, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EsimProfileInstance]: + """ + Lists EsimProfileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str eid: List the eSIM Profiles that have been associated with an EId. + :param str sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param "EsimProfileInstance.Status" status: List the eSIM Profiles that are in a given status. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + eid=eid, + sim_sid=sim_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EsimProfileInstance]: + """ + Asynchronously lists EsimProfileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str eid: List the eSIM Profiles that have been associated with an EId. + :param str sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param "EsimProfileInstance.Status" status: List the eSIM Profiles that are in a given status. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + eid=eid, + sim_sid=sim_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EsimProfilePage: + """ + Retrieve a single page of EsimProfileInstance records from the API. + Request is executed immediately + + :param eid: List the eSIM Profiles that have been associated with an EId. + :param sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param status: List the eSIM Profiles that are in a given status. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EsimProfileInstance + """ + data = values.of( + { + "Eid": eid, + "SimSid": sim_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EsimProfilePage(self._version, response) + + async def page_async( + self, + eid: Union[str, object] = values.unset, + sim_sid: Union[str, object] = values.unset, + status: Union["EsimProfileInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EsimProfilePage: + """ + Asynchronously retrieve a single page of EsimProfileInstance records from the API. + Request is executed immediately + + :param eid: List the eSIM Profiles that have been associated with an EId. + :param sim_sid: Find the eSIM Profile resource related to a [Sim](https://www.twilio.com/docs/iot/supersim/api/sim-resource) resource by providing the SIM SID. Will always return an array with either 1 or 0 records. + :param status: List the eSIM Profiles that are in a given status. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EsimProfileInstance + """ + data = values.of( + { + "Eid": eid, + "SimSid": sim_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EsimProfilePage(self._version, response) + + def get_page(self, target_url: str) -> EsimProfilePage: + """ + Retrieve a specific page of EsimProfileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EsimProfileInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EsimProfilePage(self._version, response) + + async def get_page_async(self, target_url: str) -> EsimProfilePage: + """ + Asynchronously retrieve a specific page of EsimProfileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EsimProfileInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EsimProfilePage(self._version, response) + + def get(self, sid: str) -> EsimProfileContext: + """ + Constructs a EsimProfileContext + + :param sid: The SID of the eSIM Profile resource to fetch. + """ + return EsimProfileContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EsimProfileContext: + """ + Constructs a EsimProfileContext + + :param sid: The SID of the eSIM Profile resource to fetch. + """ + return EsimProfileContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/fleet.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/fleet.py new file mode 100644 index 0000000..2003eb7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/fleet.py @@ -0,0 +1,699 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class FleetInstance(InstanceResource): + + class DataMetering(object): + PAYG = "payg" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Fleet resource. + :ivar sid: The unique string that we created to identify the Fleet resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Fleet resource. + :ivar data_enabled: Defines whether SIMs in the Fleet are capable of using 2G/3G/4G/LTE/CAT-M data connectivity. Defaults to `true`. + :ivar data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 250MB. + :ivar data_metering: + :ivar sms_commands_enabled: Defines whether SIMs in the Fleet are capable of sending and receiving machine-to-machine SMS via Commands. Defaults to `false`. + :ivar sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :ivar sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :ivar network_access_profile_sid: The SID of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :ivar ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :ivar ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.data_enabled: Optional[bool] = payload.get("data_enabled") + self.data_limit: Optional[int] = deserialize.integer(payload.get("data_limit")) + self.data_metering: Optional["FleetInstance.DataMetering"] = payload.get( + "data_metering" + ) + self.sms_commands_enabled: Optional[bool] = payload.get("sms_commands_enabled") + self.sms_commands_url: Optional[str] = payload.get("sms_commands_url") + self.sms_commands_method: Optional[str] = payload.get("sms_commands_method") + self.network_access_profile_sid: Optional[str] = payload.get( + "network_access_profile_sid" + ) + self.ip_commands_url: Optional[str] = payload.get("ip_commands_url") + self.ip_commands_method: Optional[str] = payload.get("ip_commands_method") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[FleetContext] = None + + @property + def _proxy(self) -> "FleetContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FleetContext for this FleetInstance + """ + if self._context is None: + self._context = FleetContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "FleetInstance": + """ + Fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FleetInstance": + """ + Asynchronous coroutine to fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + network_access_profile: Union[str, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ) -> "FleetInstance": + """ + Update the FleetInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + + :returns: The updated FleetInstance + """ + return self._proxy.update( + unique_name=unique_name, + network_access_profile=network_access_profile, + ip_commands_url=ip_commands_url, + ip_commands_method=ip_commands_method, + sms_commands_url=sms_commands_url, + sms_commands_method=sms_commands_method, + data_limit=data_limit, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + network_access_profile: Union[str, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ) -> "FleetInstance": + """ + Asynchronous coroutine to update the FleetInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + + :returns: The updated FleetInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + network_access_profile=network_access_profile, + ip_commands_url=ip_commands_url, + ip_commands_method=ip_commands_method, + sms_commands_url=sms_commands_url, + sms_commands_method=sms_commands_method, + data_limit=data_limit, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FleetContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the FleetContext + + :param version: Version that contains the resource + :param sid: The SID of the Fleet resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Fleets/{sid}".format(**self._solution) + + def fetch(self) -> FleetInstance: + """ + Fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FleetInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FleetInstance: + """ + Asynchronous coroutine to fetch the FleetInstance + + + :returns: The fetched FleetInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FleetInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + network_access_profile: Union[str, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ) -> FleetInstance: + """ + Update the FleetInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + + :returns: The updated FleetInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "NetworkAccessProfile": network_access_profile, + "IpCommandsUrl": ip_commands_url, + "IpCommandsMethod": ip_commands_method, + "SmsCommandsUrl": sms_commands_url, + "SmsCommandsMethod": sms_commands_method, + "DataLimit": data_limit, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + network_access_profile: Union[str, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ) -> FleetInstance: + """ + Asynchronous coroutine to update the FleetInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + + :returns: The updated FleetInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "NetworkAccessProfile": network_access_profile, + "IpCommandsUrl": ip_commands_url, + "IpCommandsMethod": ip_commands_method, + "SmsCommandsUrl": sms_commands_url, + "SmsCommandsMethod": sms_commands_method, + "DataLimit": data_limit, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FleetPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FleetInstance: + """ + Build an instance of FleetInstance + + :param payload: Payload response from the API + """ + return FleetInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FleetList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FleetList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Fleets" + + def create( + self, + network_access_profile: str, + unique_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_enabled: Union[bool, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + ) -> FleetInstance: + """ + Create the FleetInstance + + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param data_enabled: Defines whether SIMs in the Fleet are capable of using 2G/3G/4G/LTE/CAT-M data connectivity. Defaults to `true`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_enabled: Defines whether SIMs in the Fleet are capable of sending and receiving machine-to-machine SMS via Commands. Defaults to `true`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + + :returns: The created FleetInstance + """ + + data = values.of( + { + "NetworkAccessProfile": network_access_profile, + "UniqueName": unique_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "IpCommandsUrl": ip_commands_url, + "IpCommandsMethod": ip_commands_method, + "SmsCommandsEnabled": serialize.boolean_to_string(sms_commands_enabled), + "SmsCommandsUrl": sms_commands_url, + "SmsCommandsMethod": sms_commands_method, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload) + + async def create_async( + self, + network_access_profile: str, + unique_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + ip_commands_url: Union[str, object] = values.unset, + ip_commands_method: Union[str, object] = values.unset, + sms_commands_enabled: Union[bool, object] = values.unset, + sms_commands_url: Union[str, object] = values.unset, + sms_commands_method: Union[str, object] = values.unset, + ) -> FleetInstance: + """ + Asynchronously create the FleetInstance + + :param network_access_profile: The SID or unique name of the Network Access Profile that will control which cellular networks the Fleet's SIMs can connect to. + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param data_enabled: Defines whether SIMs in the Fleet are capable of using 2G/3G/4G/LTE/CAT-M data connectivity. Defaults to `true`. + :param data_limit: The total data usage (download and upload combined) in Megabytes that each Super SIM assigned to the Fleet can consume during a billing period (normally one month). Value must be between 1MB (1) and 2TB (2,000,000). Defaults to 1GB (1,000). + :param ip_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an IP Command from your device to a special IP address. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param ip_commands_method: A string representing the HTTP method to use when making a request to `ip_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + :param sms_commands_enabled: Defines whether SIMs in the Fleet are capable of sending and receiving machine-to-machine SMS via Commands. Defaults to `true`. + :param sms_commands_url: The URL that will receive a webhook when a Super SIM in the Fleet is used to send an SMS from your device to the SMS Commands number. Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :param sms_commands_method: A string representing the HTTP method to use when making a request to `sms_commands_url`. Can be one of `POST` or `GET`. Defaults to `POST`. + + :returns: The created FleetInstance + """ + + data = values.of( + { + "NetworkAccessProfile": network_access_profile, + "UniqueName": unique_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "IpCommandsUrl": ip_commands_url, + "IpCommandsMethod": ip_commands_method, + "SmsCommandsEnabled": serialize.boolean_to_string(sms_commands_enabled), + "SmsCommandsUrl": sms_commands_url, + "SmsCommandsMethod": sms_commands_method, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FleetInstance(self._version, payload) + + def stream( + self, + network_access_profile: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FleetInstance]: + """ + Streams FleetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + network_access_profile=network_access_profile, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + network_access_profile: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FleetInstance]: + """ + Asynchronously streams FleetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + network_access_profile=network_access_profile, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + network_access_profile: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FleetInstance]: + """ + Lists FleetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + network_access_profile=network_access_profile, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + network_access_profile: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FleetInstance]: + """ + Asynchronously lists FleetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + network_access_profile=network_access_profile, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + network_access_profile: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FleetPage: + """ + Retrieve a single page of FleetInstance records from the API. + Request is executed immediately + + :param network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FleetInstance + """ + data = values.of( + { + "NetworkAccessProfile": network_access_profile, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FleetPage(self._version, response) + + async def page_async( + self, + network_access_profile: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FleetPage: + """ + Asynchronously retrieve a single page of FleetInstance records from the API. + Request is executed immediately + + :param network_access_profile: The SID or unique name of the Network Access Profile that controls which cellular networks the Fleet's SIMs can connect to. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FleetInstance + """ + data = values.of( + { + "NetworkAccessProfile": network_access_profile, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FleetPage(self._version, response) + + def get_page(self, target_url: str) -> FleetPage: + """ + Retrieve a specific page of FleetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FleetInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FleetPage(self._version, response) + + async def get_page_async(self, target_url: str) -> FleetPage: + """ + Asynchronously retrieve a specific page of FleetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FleetInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FleetPage(self._version, response) + + def get(self, sid: str) -> FleetContext: + """ + Constructs a FleetContext + + :param sid: The SID of the Fleet resource to update. + """ + return FleetContext(self._version, sid=sid) + + def __call__(self, sid: str) -> FleetContext: + """ + Constructs a FleetContext + + :param sid: The SID of the Fleet resource to update. + """ + return FleetContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/ip_command.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/ip_command.py new file mode 100644 index 0000000..b7610ef --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/ip_command.py @@ -0,0 +1,594 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class IpCommandInstance(InstanceResource): + + class Direction(object): + TO_SIM = "to_sim" + FROM_SIM = "from_sim" + + class PayloadType(object): + TEXT = "text" + BINARY = "binary" + + class Status(object): + QUEUED = "queued" + SENT = "sent" + RECEIVED = "received" + FAILED = "failed" + + """ + :ivar sid: The unique string that we created to identify the IP Command resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IP Command resource. + :ivar sim_sid: The SID of the [Super SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) that this IP Command was sent to or from. + :ivar sim_iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) of the [Super SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) that this IP Command was sent to or from. + :ivar status: + :ivar direction: + :ivar device_ip: The IP address of the device that the IP Command was sent to or received from. For an IP Command sent to a Super SIM, `device_ip` starts out as `null`, and once the IP Command is “sent”, the `device_ip` will be filled out. An IP Command sent from a Super SIM have its `device_ip` always set. + :ivar device_port: For an IP Command sent to a Super SIM, it would be the destination port of the IP message. For an IP Command sent from a Super SIM, it would be the source port of the IP message. + :ivar payload_type: + :ivar payload: The payload that is carried in the IP/UDP message. The payload can be encoded in either text or binary format. For text payload, UTF-8 encoding must be used. For an IP Command sent to a Super SIM, the payload is appended to the IP/UDP message “as is”. The payload should not exceed 1300 bytes. For an IP Command sent from a Super SIM, the payload from the received IP/UDP message is extracted and sent in binary encoding. For an IP Command sent from a Super SIM, the payload should not exceed 1300 bytes. If it is larger than 1300 bytes, there might be fragmentation on the upstream and the message may appear truncated. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the IP Command resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.sim_iccid: Optional[str] = payload.get("sim_iccid") + self.status: Optional["IpCommandInstance.Status"] = payload.get("status") + self.direction: Optional["IpCommandInstance.Direction"] = payload.get( + "direction" + ) + self.device_ip: Optional[str] = payload.get("device_ip") + self.device_port: Optional[int] = deserialize.integer( + payload.get("device_port") + ) + self.payload_type: Optional["IpCommandInstance.PayloadType"] = payload.get( + "payload_type" + ) + self.payload: Optional[str] = payload.get("payload") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[IpCommandContext] = None + + @property + def _proxy(self) -> "IpCommandContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpCommandContext for this IpCommandInstance + """ + if self._context is None: + self._context = IpCommandContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "IpCommandInstance": + """ + Fetch the IpCommandInstance + + + :returns: The fetched IpCommandInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpCommandInstance": + """ + Asynchronous coroutine to fetch the IpCommandInstance + + + :returns: The fetched IpCommandInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpCommandContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the IpCommandContext + + :param version: Version that contains the resource + :param sid: The SID of the IP Command resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/IpCommands/{sid}".format(**self._solution) + + def fetch(self) -> IpCommandInstance: + """ + Fetch the IpCommandInstance + + + :returns: The fetched IpCommandInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpCommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpCommandInstance: + """ + Asynchronous coroutine to fetch the IpCommandInstance + + + :returns: The fetched IpCommandInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpCommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpCommandPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IpCommandInstance: + """ + Build an instance of IpCommandInstance + + :param payload: Payload response from the API + """ + return IpCommandInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpCommandList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the IpCommandList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/IpCommands" + + def create( + self, + sim: str, + payload: str, + device_port: int, + payload_type: Union["IpCommandInstance.PayloadType", object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + ) -> IpCommandInstance: + """ + Create the IpCommandInstance + + :param sim: The `sid` or `unique_name` of the [Super SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) to send the IP Command to. + :param payload: The data that will be sent to the device. The payload cannot exceed 1300 bytes. If the PayloadType is set to text, the payload is encoded in UTF-8. If PayloadType is set to binary, the payload is encoded in Base64. + :param device_port: The device port to which the IP Command will be sent. + :param payload_type: + :param callback_url: The URL we should call using the `callback_method` after we have sent the IP Command. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be `GET` or `POST`, and the default is `POST`. + + :returns: The created IpCommandInstance + """ + + data = values.of( + { + "Sim": sim, + "Payload": payload, + "DevicePort": device_port, + "PayloadType": payload_type, + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpCommandInstance(self._version, payload) + + async def create_async( + self, + sim: str, + payload: str, + device_port: int, + payload_type: Union["IpCommandInstance.PayloadType", object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + ) -> IpCommandInstance: + """ + Asynchronously create the IpCommandInstance + + :param sim: The `sid` or `unique_name` of the [Super SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) to send the IP Command to. + :param payload: The data that will be sent to the device. The payload cannot exceed 1300 bytes. If the PayloadType is set to text, the payload is encoded in UTF-8. If PayloadType is set to binary, the payload is encoded in Base64. + :param device_port: The device port to which the IP Command will be sent. + :param payload_type: + :param callback_url: The URL we should call using the `callback_method` after we have sent the IP Command. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be `GET` or `POST`, and the default is `POST`. + + :returns: The created IpCommandInstance + """ + + data = values.of( + { + "Sim": sim, + "Payload": payload, + "DevicePort": device_port, + "PayloadType": payload_type, + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpCommandInstance(self._version, payload) + + def stream( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpCommandInstance]: + """ + Streams IpCommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param str sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param "IpCommandInstance.Status" status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param "IpCommandInstance.Direction" direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + sim=sim, + sim_iccid=sim_iccid, + status=status, + direction=direction, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpCommandInstance]: + """ + Asynchronously streams IpCommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param str sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param "IpCommandInstance.Status" status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param "IpCommandInstance.Direction" direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + sim=sim, + sim_iccid=sim_iccid, + status=status, + direction=direction, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpCommandInstance]: + """ + Lists IpCommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param str sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param "IpCommandInstance.Status" status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param "IpCommandInstance.Direction" direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sim=sim, + sim_iccid=sim_iccid, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpCommandInstance]: + """ + Asynchronously lists IpCommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param str sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param "IpCommandInstance.Status" status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param "IpCommandInstance.Direction" direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sim=sim, + sim_iccid=sim_iccid, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpCommandPage: + """ + Retrieve a single page of IpCommandInstance records from the API. + Request is executed immediately + + :param sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpCommandInstance + """ + data = values.of( + { + "Sim": sim, + "SimIccid": sim_iccid, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpCommandPage(self._version, response) + + async def page_async( + self, + sim: Union[str, object] = values.unset, + sim_iccid: Union[str, object] = values.unset, + status: Union["IpCommandInstance.Status", object] = values.unset, + direction: Union["IpCommandInstance.Direction", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpCommandPage: + """ + Asynchronously retrieve a single page of IpCommandInstance records from the API. + Request is executed immediately + + :param sim: The SID or unique name of the Sim resource that IP Command was sent to or from. + :param sim_iccid: The ICCID of the Sim resource that IP Command was sent to or from. + :param status: The status of the IP Command. Can be: `queued`, `sent`, `received` or `failed`. See the [IP Command Status Values](https://www.twilio.com/docs/iot/supersim/api/ipcommand-resource#status-values) for a description of each. + :param direction: The direction of the IP Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpCommandInstance + """ + data = values.of( + { + "Sim": sim, + "SimIccid": sim_iccid, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpCommandPage(self._version, response) + + def get_page(self, target_url: str) -> IpCommandPage: + """ + Retrieve a specific page of IpCommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpCommandInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpCommandPage(self._version, response) + + async def get_page_async(self, target_url: str) -> IpCommandPage: + """ + Asynchronously retrieve a specific page of IpCommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpCommandInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpCommandPage(self._version, response) + + def get(self, sid: str) -> IpCommandContext: + """ + Constructs a IpCommandContext + + :param sid: The SID of the IP Command resource to fetch. + """ + return IpCommandContext(self._version, sid=sid) + + def __call__(self, sid: str) -> IpCommandContext: + """ + Constructs a IpCommandContext + + :param sid: The SID of the IP Command resource to fetch. + """ + return IpCommandContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network.py new file mode 100644 index 0000000..0d4f6a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network.py @@ -0,0 +1,446 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class NetworkInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Network resource. + :ivar friendly_name: A human readable identifier of this resource. + :ivar url: The absolute URL of the Network resource. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resource. + :ivar identifiers: Array of objects identifying the [MCC-MNCs](https://en.wikipedia.org/wiki/Mobile_country_code) that are included in the Network resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.url: Optional[str] = payload.get("url") + self.iso_country: Optional[str] = payload.get("iso_country") + self.identifiers: Optional[List[Dict[str, object]]] = payload.get("identifiers") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[NetworkContext] = None + + @property + def _proxy(self) -> "NetworkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NetworkContext for this NetworkInstance + """ + if self._context is None: + self._context = NetworkContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "NetworkInstance": + """ + Fetch the NetworkInstance + + + :returns: The fetched NetworkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NetworkInstance": + """ + Asynchronous coroutine to fetch the NetworkInstance + + + :returns: The fetched NetworkInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NetworkContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the NetworkContext + + :param version: Version that contains the resource + :param sid: The SID of the Network resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Networks/{sid}".format(**self._solution) + + def fetch(self) -> NetworkInstance: + """ + Fetch the NetworkInstance + + + :returns: The fetched NetworkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NetworkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> NetworkInstance: + """ + Asynchronous coroutine to fetch the NetworkInstance + + + :returns: The fetched NetworkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NetworkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NetworkPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> NetworkInstance: + """ + Build an instance of NetworkInstance + + :param payload: Payload response from the API + """ + return NetworkInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NetworkList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the NetworkList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Networks" + + def stream( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NetworkInstance]: + """ + Streams NetworkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param str mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param str mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + iso_country=iso_country, mcc=mcc, mnc=mnc, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NetworkInstance]: + """ + Asynchronously streams NetworkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param str mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param str mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + iso_country=iso_country, mcc=mcc, mnc=mnc, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkInstance]: + """ + Lists NetworkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param str mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param str mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + iso_country=iso_country, + mcc=mcc, + mnc=mnc, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkInstance]: + """ + Asynchronously lists NetworkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param str mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param str mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + iso_country=iso_country, + mcc=mcc, + mnc=mnc, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkPage: + """ + Retrieve a single page of NetworkInstance records from the API. + Request is executed immediately + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkInstance + """ + data = values.of( + { + "IsoCountry": iso_country, + "Mcc": mcc, + "Mnc": mnc, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NetworkPage(self._version, response) + + async def page_async( + self, + iso_country: Union[str, object] = values.unset, + mcc: Union[str, object] = values.unset, + mnc: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkPage: + """ + Asynchronously retrieve a single page of NetworkInstance records from the API. + Request is executed immediately + + :param iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resources to read. + :param mcc: The 'mobile country code' of a country. Network resources with this `mcc` in their `identifiers` will be read. + :param mnc: The 'mobile network code' of a mobile operator network. Network resources with this `mnc` in their `identifiers` will be read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkInstance + """ + data = values.of( + { + "IsoCountry": iso_country, + "Mcc": mcc, + "Mnc": mnc, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NetworkPage(self._version, response) + + def get_page(self, target_url: str) -> NetworkPage: + """ + Retrieve a specific page of NetworkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NetworkPage(self._version, response) + + async def get_page_async(self, target_url: str) -> NetworkPage: + """ + Asynchronously retrieve a specific page of NetworkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NetworkPage(self._version, response) + + def get(self, sid: str) -> NetworkContext: + """ + Constructs a NetworkContext + + :param sid: The SID of the Network resource to fetch. + """ + return NetworkContext(self._version, sid=sid) + + def __call__(self, sid: str) -> NetworkContext: + """ + Constructs a NetworkContext + + :param sid: The SID of the Network resource to fetch. + """ + return NetworkContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__init__.py new file mode 100644 index 0000000..d0b5374 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__init__.py @@ -0,0 +1,565 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.supersim.v1.network_access_profile.network_access_profile_network import ( + NetworkAccessProfileNetworkList, +) + + +class NetworkAccessProfileInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the Network Access Profile resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the Network Access Profile belongs to. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Network Access Profile resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[NetworkAccessProfileContext] = None + + @property + def _proxy(self) -> "NetworkAccessProfileContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NetworkAccessProfileContext for this NetworkAccessProfileInstance + """ + if self._context is None: + self._context = NetworkAccessProfileContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "NetworkAccessProfileInstance": + """ + Fetch the NetworkAccessProfileInstance + + + :returns: The fetched NetworkAccessProfileInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NetworkAccessProfileInstance": + """ + Asynchronous coroutine to fetch the NetworkAccessProfileInstance + + + :returns: The fetched NetworkAccessProfileInstance + """ + return await self._proxy.fetch_async() + + def update( + self, unique_name: Union[str, object] = values.unset + ) -> "NetworkAccessProfileInstance": + """ + Update the NetworkAccessProfileInstance + + :param unique_name: The new unique name of the Network Access Profile. + + :returns: The updated NetworkAccessProfileInstance + """ + return self._proxy.update( + unique_name=unique_name, + ) + + async def update_async( + self, unique_name: Union[str, object] = values.unset + ) -> "NetworkAccessProfileInstance": + """ + Asynchronous coroutine to update the NetworkAccessProfileInstance + + :param unique_name: The new unique name of the Network Access Profile. + + :returns: The updated NetworkAccessProfileInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + ) + + @property + def networks(self) -> NetworkAccessProfileNetworkList: + """ + Access the networks + """ + return self._proxy.networks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NetworkAccessProfileContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the NetworkAccessProfileContext + + :param version: Version that contains the resource + :param sid: The SID of the Network Access Profile to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/NetworkAccessProfiles/{sid}".format(**self._solution) + + self._networks: Optional[NetworkAccessProfileNetworkList] = None + + def fetch(self) -> NetworkAccessProfileInstance: + """ + Fetch the NetworkAccessProfileInstance + + + :returns: The fetched NetworkAccessProfileInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NetworkAccessProfileInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> NetworkAccessProfileInstance: + """ + Asynchronous coroutine to fetch the NetworkAccessProfileInstance + + + :returns: The fetched NetworkAccessProfileInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NetworkAccessProfileInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, unique_name: Union[str, object] = values.unset + ) -> NetworkAccessProfileInstance: + """ + Update the NetworkAccessProfileInstance + + :param unique_name: The new unique name of the Network Access Profile. + + :returns: The updated NetworkAccessProfileInstance + """ + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, unique_name: Union[str, object] = values.unset + ) -> NetworkAccessProfileInstance: + """ + Asynchronous coroutine to update the NetworkAccessProfileInstance + + :param unique_name: The new unique name of the Network Access Profile. + + :returns: The updated NetworkAccessProfileInstance + """ + data = values.of( + { + "UniqueName": unique_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileInstance( + self._version, payload, sid=self._solution["sid"] + ) + + @property + def networks(self) -> NetworkAccessProfileNetworkList: + """ + Access the networks + """ + if self._networks is None: + self._networks = NetworkAccessProfileNetworkList( + self._version, + self._solution["sid"], + ) + return self._networks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NetworkAccessProfilePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> NetworkAccessProfileInstance: + """ + Build an instance of NetworkAccessProfileInstance + + :param payload: Payload response from the API + """ + return NetworkAccessProfileInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NetworkAccessProfileList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the NetworkAccessProfileList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/NetworkAccessProfiles" + + def create( + self, + unique_name: Union[str, object] = values.unset, + networks: Union[List[str], object] = values.unset, + ) -> NetworkAccessProfileInstance: + """ + Create the NetworkAccessProfileInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param networks: List of Network SIDs that this Network Access Profile will allow connections to. + + :returns: The created NetworkAccessProfileInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Networks": serialize.map(networks, lambda e: e), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileInstance(self._version, payload) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + networks: Union[List[str], object] = values.unset, + ) -> NetworkAccessProfileInstance: + """ + Asynchronously create the NetworkAccessProfileInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param networks: List of Network SIDs that this Network Access Profile will allow connections to. + + :returns: The created NetworkAccessProfileInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Networks": serialize.map(networks, lambda e: e), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NetworkAccessProfileInstance]: + """ + Streams NetworkAccessProfileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NetworkAccessProfileInstance]: + """ + Asynchronously streams NetworkAccessProfileInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkAccessProfileInstance]: + """ + Lists NetworkAccessProfileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkAccessProfileInstance]: + """ + Asynchronously lists NetworkAccessProfileInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkAccessProfilePage: + """ + Retrieve a single page of NetworkAccessProfileInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkAccessProfileInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NetworkAccessProfilePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkAccessProfilePage: + """ + Asynchronously retrieve a single page of NetworkAccessProfileInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkAccessProfileInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NetworkAccessProfilePage(self._version, response) + + def get_page(self, target_url: str) -> NetworkAccessProfilePage: + """ + Retrieve a specific page of NetworkAccessProfileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkAccessProfileInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NetworkAccessProfilePage(self._version, response) + + async def get_page_async(self, target_url: str) -> NetworkAccessProfilePage: + """ + Asynchronously retrieve a specific page of NetworkAccessProfileInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkAccessProfileInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NetworkAccessProfilePage(self._version, response) + + def get(self, sid: str) -> NetworkAccessProfileContext: + """ + Constructs a NetworkAccessProfileContext + + :param sid: The SID of the Network Access Profile to update. + """ + return NetworkAccessProfileContext(self._version, sid=sid) + + def __call__(self, sid: str) -> NetworkAccessProfileContext: + """ + Constructs a NetworkAccessProfileContext + + :param sid: The SID of the Network Access Profile to update. + """ + return NetworkAccessProfileContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ff3c37f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/network_access_profile_network.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/network_access_profile_network.cpython-310.pyc new file mode 100644 index 0000000..5502165 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/__pycache__/network_access_profile_network.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/network_access_profile_network.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/network_access_profile_network.py new file mode 100644 index 0000000..50a2595 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/network_access_profile/network_access_profile_network.py @@ -0,0 +1,535 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class NetworkAccessProfileNetworkInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the Network resource. + :ivar network_access_profile_sid: The unique string that identifies the Network resource's Network Access Profile resource. + :ivar friendly_name: A human readable identifier of the Network this resource refers to. + :ivar iso_country: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the Network resource. + :ivar identifiers: Array of objects identifying the [MCC-MNCs](https://en.wikipedia.org/wiki/Mobile_country_code) that are included in the Network resource. + :ivar url: The absolute URL of the Network resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + network_access_profile_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.network_access_profile_sid: Optional[str] = payload.get( + "network_access_profile_sid" + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iso_country: Optional[str] = payload.get("iso_country") + self.identifiers: Optional[List[Dict[str, object]]] = payload.get("identifiers") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "network_access_profile_sid": network_access_profile_sid, + "sid": sid or self.sid, + } + self._context: Optional[NetworkAccessProfileNetworkContext] = None + + @property + def _proxy(self) -> "NetworkAccessProfileNetworkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: NetworkAccessProfileNetworkContext for this NetworkAccessProfileNetworkInstance + """ + if self._context is None: + self._context = NetworkAccessProfileNetworkContext( + self._version, + network_access_profile_sid=self._solution["network_access_profile_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the NetworkAccessProfileNetworkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the NetworkAccessProfileNetworkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "NetworkAccessProfileNetworkInstance": + """ + Fetch the NetworkAccessProfileNetworkInstance + + + :returns: The fetched NetworkAccessProfileNetworkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "NetworkAccessProfileNetworkInstance": + """ + Asynchronous coroutine to fetch the NetworkAccessProfileNetworkInstance + + + :returns: The fetched NetworkAccessProfileNetworkInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class NetworkAccessProfileNetworkContext(InstanceContext): + + def __init__(self, version: Version, network_access_profile_sid: str, sid: str): + """ + Initialize the NetworkAccessProfileNetworkContext + + :param version: Version that contains the resource + :param network_access_profile_sid: The unique string that identifies the Network Access Profile resource. + :param sid: The SID of the Network resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "network_access_profile_sid": network_access_profile_sid, + "sid": sid, + } + self._uri = ( + "/NetworkAccessProfiles/{network_access_profile_sid}/Networks/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the NetworkAccessProfileNetworkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the NetworkAccessProfileNetworkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> NetworkAccessProfileNetworkInstance: + """ + Fetch the NetworkAccessProfileNetworkInstance + + + :returns: The fetched NetworkAccessProfileNetworkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return NetworkAccessProfileNetworkInstance( + self._version, + payload, + network_access_profile_sid=self._solution["network_access_profile_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> NetworkAccessProfileNetworkInstance: + """ + Asynchronous coroutine to fetch the NetworkAccessProfileNetworkInstance + + + :returns: The fetched NetworkAccessProfileNetworkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return NetworkAccessProfileNetworkInstance( + self._version, + payload, + network_access_profile_sid=self._solution["network_access_profile_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class NetworkAccessProfileNetworkPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> NetworkAccessProfileNetworkInstance: + """ + Build an instance of NetworkAccessProfileNetworkInstance + + :param payload: Payload response from the API + """ + return NetworkAccessProfileNetworkInstance( + self._version, + payload, + network_access_profile_sid=self._solution["network_access_profile_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class NetworkAccessProfileNetworkList(ListResource): + + def __init__(self, version: Version, network_access_profile_sid: str): + """ + Initialize the NetworkAccessProfileNetworkList + + :param version: Version that contains the resource + :param network_access_profile_sid: The unique string that identifies the Network Access Profile resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "network_access_profile_sid": network_access_profile_sid, + } + self._uri = ( + "/NetworkAccessProfiles/{network_access_profile_sid}/Networks".format( + **self._solution + ) + ) + + def create(self, network: str) -> NetworkAccessProfileNetworkInstance: + """ + Create the NetworkAccessProfileNetworkInstance + + :param network: The SID of the Network resource to be added to the Network Access Profile resource. + + :returns: The created NetworkAccessProfileNetworkInstance + """ + + data = values.of( + { + "Network": network, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileNetworkInstance( + self._version, + payload, + network_access_profile_sid=self._solution["network_access_profile_sid"], + ) + + async def create_async(self, network: str) -> NetworkAccessProfileNetworkInstance: + """ + Asynchronously create the NetworkAccessProfileNetworkInstance + + :param network: The SID of the Network resource to be added to the Network Access Profile resource. + + :returns: The created NetworkAccessProfileNetworkInstance + """ + + data = values.of( + { + "Network": network, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NetworkAccessProfileNetworkInstance( + self._version, + payload, + network_access_profile_sid=self._solution["network_access_profile_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[NetworkAccessProfileNetworkInstance]: + """ + Streams NetworkAccessProfileNetworkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[NetworkAccessProfileNetworkInstance]: + """ + Asynchronously streams NetworkAccessProfileNetworkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkAccessProfileNetworkInstance]: + """ + Lists NetworkAccessProfileNetworkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[NetworkAccessProfileNetworkInstance]: + """ + Asynchronously lists NetworkAccessProfileNetworkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkAccessProfileNetworkPage: + """ + Retrieve a single page of NetworkAccessProfileNetworkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkAccessProfileNetworkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return NetworkAccessProfileNetworkPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> NetworkAccessProfileNetworkPage: + """ + Asynchronously retrieve a single page of NetworkAccessProfileNetworkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of NetworkAccessProfileNetworkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return NetworkAccessProfileNetworkPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> NetworkAccessProfileNetworkPage: + """ + Retrieve a specific page of NetworkAccessProfileNetworkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkAccessProfileNetworkInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return NetworkAccessProfileNetworkPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> NetworkAccessProfileNetworkPage: + """ + Asynchronously retrieve a specific page of NetworkAccessProfileNetworkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of NetworkAccessProfileNetworkInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return NetworkAccessProfileNetworkPage(self._version, response, self._solution) + + def get(self, sid: str) -> NetworkAccessProfileNetworkContext: + """ + Constructs a NetworkAccessProfileNetworkContext + + :param sid: The SID of the Network resource to fetch. + """ + return NetworkAccessProfileNetworkContext( + self._version, + network_access_profile_sid=self._solution["network_access_profile_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> NetworkAccessProfileNetworkContext: + """ + Constructs a NetworkAccessProfileNetworkContext + + :param sid: The SID of the Network resource to fetch. + """ + return NetworkAccessProfileNetworkContext( + self._version, + network_access_profile_sid=self._solution["network_access_profile_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/settings_update.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/settings_update.py new file mode 100644 index 0000000..f946150 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/settings_update.py @@ -0,0 +1,327 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SettingsUpdateInstance(InstanceResource): + + class Status(object): + SCHEDULED = "scheduled" + IN_PROGRESS = "in-progress" + SUCCESSFUL = "successful" + FAILED = "failed" + + """ + :ivar sid: The unique identifier of this Settings Update. + :ivar iccid: The [ICCID](https://en.wikipedia.org/wiki/SIM_card#ICCID) associated with the SIM. + :ivar sim_sid: The SID of the Super SIM to which this Settings Update was applied. + :ivar status: + :ivar packages: Array containing the different Settings Packages that will be applied to the SIM after the update completes. Each object within the array indicates the name and the version of the Settings Package that will be on the SIM if the update is successful. + :ivar date_completed: The time, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, when the update successfully completed and the new settings were applied to the SIM. + :ivar date_created: The date that this Settings Update was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Settings Update was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.iccid: Optional[str] = payload.get("iccid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.status: Optional["SettingsUpdateInstance.Status"] = payload.get("status") + self.packages: Optional[List[Dict[str, object]]] = payload.get("packages") + self.date_completed: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_completed") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SettingsUpdatePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SettingsUpdateInstance: + """ + Build an instance of SettingsUpdateInstance + + :param payload: Payload response from the API + """ + return SettingsUpdateInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SettingsUpdateList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SettingsUpdateList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SettingsUpdates" + + def stream( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SettingsUpdateInstance]: + """ + Streams SettingsUpdateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param "SettingsUpdateInstance.Status" status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(sim=sim, status=status, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SettingsUpdateInstance]: + """ + Asynchronously streams SettingsUpdateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param "SettingsUpdateInstance.Status" status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + sim=sim, status=status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SettingsUpdateInstance]: + """ + Lists SettingsUpdateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param "SettingsUpdateInstance.Status" status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sim=sim, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SettingsUpdateInstance]: + """ + Asynchronously lists SettingsUpdateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param "SettingsUpdateInstance.Status" status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sim=sim, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SettingsUpdatePage: + """ + Retrieve a single page of SettingsUpdateInstance records from the API. + Request is executed immediately + + :param sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SettingsUpdateInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SettingsUpdatePage(self._version, response) + + async def page_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SettingsUpdateInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SettingsUpdatePage: + """ + Asynchronously retrieve a single page of SettingsUpdateInstance records from the API. + Request is executed immediately + + :param sim: Filter the Settings Updates by a Super SIM's SID or UniqueName. + :param status: Filter the Settings Updates by status. Can be `scheduled`, `in-progress`, `successful`, or `failed`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SettingsUpdateInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SettingsUpdatePage(self._version, response) + + def get_page(self, target_url: str) -> SettingsUpdatePage: + """ + Retrieve a specific page of SettingsUpdateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SettingsUpdateInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SettingsUpdatePage(self._version, response) + + async def get_page_async(self, target_url: str) -> SettingsUpdatePage: + """ + Asynchronously retrieve a specific page of SettingsUpdateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SettingsUpdateInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SettingsUpdatePage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__init__.py new file mode 100644 index 0000000..b1ba647 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__init__.py @@ -0,0 +1,707 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.supersim.v1.sim.billing_period import BillingPeriodList +from twilio.rest.supersim.v1.sim.sim_ip_address import SimIpAddressList + + +class SimInstance(InstanceResource): + + class Status(object): + NEW = "new" + READY = "ready" + ACTIVE = "active" + INACTIVE = "inactive" + SCHEDULED = "scheduled" + + class StatusUpdate(object): + READY = "ready" + ACTIVE = "active" + INACTIVE = "inactive" + + """ + :ivar sid: The unique string that identifies the Sim resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that the Super SIM belongs to. + :ivar iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with the SIM. + :ivar status: + :ivar fleet_sid: The unique ID of the Fleet configured for this SIM. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Sim Resource. + :ivar links: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.iccid: Optional[str] = payload.get("iccid") + self.status: Optional["SimInstance.Status"] = payload.get("status") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SimContext] = None + + @property + def _proxy(self) -> "SimContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SimContext for this SimInstance + """ + if self._context is None: + self._context = SimContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SimInstance": + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SimInstance": + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + status: Union["SimInstance.StatusUpdate", object] = values.unset, + fleet: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param status: + :param fleet: The SID or unique name of the Fleet to which the SIM resource should be assigned. + :param callback_url: The URL we should call using the `callback_method` after an asynchronous update has finished. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param account_sid: The SID of the Account to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a Subaccount of the requesting Account. Only valid when the Sim resource's status is new. + + :returns: The updated SimInstance + """ + return self._proxy.update( + unique_name=unique_name, + status=status, + fleet=fleet, + callback_url=callback_url, + callback_method=callback_method, + account_sid=account_sid, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + status: Union["SimInstance.StatusUpdate", object] = values.unset, + fleet: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param status: + :param fleet: The SID or unique name of the Fleet to which the SIM resource should be assigned. + :param callback_url: The URL we should call using the `callback_method` after an asynchronous update has finished. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param account_sid: The SID of the Account to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a Subaccount of the requesting Account. Only valid when the Sim resource's status is new. + + :returns: The updated SimInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + status=status, + fleet=fleet, + callback_url=callback_url, + callback_method=callback_method, + account_sid=account_sid, + ) + + @property + def billing_periods(self) -> BillingPeriodList: + """ + Access the billing_periods + """ + return self._proxy.billing_periods + + @property + def sim_ip_addresses(self) -> SimIpAddressList: + """ + Access the sim_ip_addresses + """ + return self._proxy.sim_ip_addresses + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SimContext + + :param version: Version that contains the resource + :param sid: The SID of the Sim resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sims/{sid}".format(**self._solution) + + self._billing_periods: Optional[BillingPeriodList] = None + self._sim_ip_addresses: Optional[SimIpAddressList] = None + + def fetch(self) -> SimInstance: + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SimInstance: + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + status: Union["SimInstance.StatusUpdate", object] = values.unset, + fleet: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param status: + :param fleet: The SID or unique name of the Fleet to which the SIM resource should be assigned. + :param callback_url: The URL we should call using the `callback_method` after an asynchronous update has finished. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param account_sid: The SID of the Account to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a Subaccount of the requesting Account. Only valid when the Sim resource's status is new. + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "Status": status, + "Fleet": fleet, + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + "AccountSid": account_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + status: Union["SimInstance.StatusUpdate", object] = values.unset, + fleet: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param status: + :param fleet: The SID or unique name of the Fleet to which the SIM resource should be assigned. + :param callback_url: The URL we should call using the `callback_method` after an asynchronous update has finished. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param account_sid: The SID of the Account to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a Subaccount of the requesting Account. Only valid when the Sim resource's status is new. + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "Status": status, + "Fleet": fleet, + "CallbackUrl": callback_url, + "CallbackMethod": callback_method, + "AccountSid": account_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def billing_periods(self) -> BillingPeriodList: + """ + Access the billing_periods + """ + if self._billing_periods is None: + self._billing_periods = BillingPeriodList( + self._version, + self._solution["sid"], + ) + return self._billing_periods + + @property + def sim_ip_addresses(self) -> SimIpAddressList: + """ + Access the sim_ip_addresses + """ + if self._sim_ip_addresses is None: + self._sim_ip_addresses = SimIpAddressList( + self._version, + self._solution["sid"], + ) + return self._sim_ip_addresses + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SimInstance: + """ + Build an instance of SimInstance + + :param payload: Payload response from the API + """ + return SimInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SimList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SimList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Sims" + + def create(self, iccid: str, registration_code: str) -> SimInstance: + """ + Create the SimInstance + + :param iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) of the Super SIM to be added to your Account. + :param registration_code: The 10-digit code required to claim the Super SIM for your Account. + + :returns: The created SimInstance + """ + + data = values.of( + { + "Iccid": iccid, + "RegistrationCode": registration_code, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload) + + async def create_async(self, iccid: str, registration_code: str) -> SimInstance: + """ + Asynchronously create the SimInstance + + :param iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) of the Super SIM to be added to your Account. + :param registration_code: The 10-digit code required to claim the Super SIM for your Account. + + :returns: The created SimInstance + """ + + data = values.of( + { + "Iccid": iccid, + "RegistrationCode": registration_code, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload) + + def stream( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SimInstance]: + """ + Streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SimInstance.Status" status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param str fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param str iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, fleet=fleet, iccid=iccid, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SimInstance]: + """ + Asynchronously streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SimInstance.Status" status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param str fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param str iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, fleet=fleet, iccid=iccid, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SimInstance.Status" status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param str fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param str iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + fleet=fleet, + iccid=iccid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Asynchronously lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SimInstance.Status" status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param str fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param str iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + fleet=fleet, + iccid=iccid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Fleet": fleet, + "Iccid": iccid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SimPage(self._version, response) + + async def page_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + fleet: Union[str, object] = values.unset, + iccid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Asynchronously retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: The status of the Sim resources to read. Can be `new`, `ready`, `active`, `inactive`, or `scheduled`. + :param fleet: The SID or unique name of the Fleet to which a list of Sims are assigned. + :param iccid: The [ICCID](https://en.wikipedia.org/wiki/Subscriber_identity_module#ICCID) associated with a Super SIM to filter the list by. Passing this parameter will always return a list containing zero or one SIMs. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Fleet": fleet, + "Iccid": iccid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SimPage(self._version, response) + + def get_page(self, target_url: str) -> SimPage: + """ + Retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SimPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SimPage: + """ + Asynchronously retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SimPage(self._version, response) + + def get(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: The SID of the Sim resource to update. + """ + return SimContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: The SID of the Sim resource to update. + """ + return SimContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1c08e24 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/billing_period.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/billing_period.cpython-310.pyc new file mode 100644 index 0000000..6604ea2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/billing_period.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/sim_ip_address.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/sim_ip_address.cpython-310.pyc new file mode 100644 index 0000000..2b5ad40 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/__pycache__/sim_ip_address.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/billing_period.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/billing_period.py new file mode 100644 index 0000000..e6fb961 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/billing_period.py @@ -0,0 +1,306 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BillingPeriodInstance(InstanceResource): + + class BpType(object): + READY = "ready" + ACTIVE = "active" + + """ + :ivar sid: The SID of the Billing Period. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) the Super SIM belongs to. + :ivar sim_sid: The SID of the Super SIM the Billing Period belongs to. + :ivar start_time: The start time of the Billing Period specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar end_time: The end time of the Billing Period specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar period_type: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sim_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.period_type: Optional["BillingPeriodInstance.BpType"] = payload.get( + "period_type" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "sim_sid": sim_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BillingPeriodPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BillingPeriodInstance: + """ + Build an instance of BillingPeriodInstance + + :param payload: Payload response from the API + """ + return BillingPeriodInstance( + self._version, payload, sim_sid=self._solution["sim_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BillingPeriodList(ListResource): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the BillingPeriodList + + :param version: Version that contains the resource + :param sim_sid: The SID of the Super SIM to list Billing Periods for. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + self._uri = "/Sims/{sim_sid}/BillingPeriods".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BillingPeriodInstance]: + """ + Streams BillingPeriodInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BillingPeriodInstance]: + """ + Asynchronously streams BillingPeriodInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BillingPeriodInstance]: + """ + Lists BillingPeriodInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BillingPeriodInstance]: + """ + Asynchronously lists BillingPeriodInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BillingPeriodPage: + """ + Retrieve a single page of BillingPeriodInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BillingPeriodInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BillingPeriodPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BillingPeriodPage: + """ + Asynchronously retrieve a single page of BillingPeriodInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BillingPeriodInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BillingPeriodPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BillingPeriodPage: + """ + Retrieve a specific page of BillingPeriodInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BillingPeriodInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BillingPeriodPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BillingPeriodPage: + """ + Asynchronously retrieve a specific page of BillingPeriodInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BillingPeriodInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BillingPeriodPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/sim_ip_address.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/sim_ip_address.py new file mode 100644 index 0000000..7c2b70e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sim/sim_ip_address.py @@ -0,0 +1,285 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SimIpAddressInstance(InstanceResource): + + class IpAddressVersion(object): + IPV4 = "IPv4" + IPV6 = "IPv6" + + """ + :ivar ip_address: IP address assigned to the given Super SIM + :ivar ip_address_version: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sim_sid: str): + super().__init__(version) + + self.ip_address: Optional[str] = payload.get("ip_address") + self.ip_address_version: Optional["SimIpAddressInstance.IpAddressVersion"] = ( + payload.get("ip_address_version") + ) + + self._solution = { + "sim_sid": sim_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimIpAddressPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SimIpAddressInstance: + """ + Build an instance of SimIpAddressInstance + + :param payload: Payload response from the API + """ + return SimIpAddressInstance( + self._version, payload, sim_sid=self._solution["sim_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SimIpAddressList(ListResource): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the SimIpAddressList + + :param version: Version that contains the resource + :param sim_sid: The SID of the Super SIM to list IP Addresses for. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + self._uri = "/Sims/{sim_sid}/IpAddresses".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SimIpAddressInstance]: + """ + Streams SimIpAddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SimIpAddressInstance]: + """ + Asynchronously streams SimIpAddressInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimIpAddressInstance]: + """ + Lists SimIpAddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimIpAddressInstance]: + """ + Asynchronously lists SimIpAddressInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimIpAddressPage: + """ + Retrieve a single page of SimIpAddressInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimIpAddressInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SimIpAddressPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimIpAddressPage: + """ + Asynchronously retrieve a single page of SimIpAddressInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimIpAddressInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SimIpAddressPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SimIpAddressPage: + """ + Retrieve a specific page of SimIpAddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimIpAddressInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SimIpAddressPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SimIpAddressPage: + """ + Asynchronously retrieve a specific page of SimIpAddressInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimIpAddressInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SimIpAddressPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sms_command.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sms_command.py new file mode 100644 index 0000000..0eb2094 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/sms_command.py @@ -0,0 +1,543 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SmsCommandInstance(InstanceResource): + + class Direction(object): + TO_SIM = "to_sim" + FROM_SIM = "from_sim" + + class Status(object): + QUEUED = "queued" + SENT = "sent" + DELIVERED = "delivered" + RECEIVED = "received" + FAILED = "failed" + + """ + :ivar sid: The unique string that we created to identify the SMS Command resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the SMS Command resource. + :ivar sim_sid: The SID of the [SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) that this SMS Command was sent to or from. + :ivar payload: The message body of the SMS Command sent to or from the SIM. For text mode messages, this can be up to 160 characters. + :ivar status: + :ivar direction: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the SMS Command resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.payload: Optional[str] = payload.get("payload") + self.status: Optional["SmsCommandInstance.Status"] = payload.get("status") + self.direction: Optional["SmsCommandInstance.Direction"] = payload.get( + "direction" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SmsCommandContext] = None + + @property + def _proxy(self) -> "SmsCommandContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SmsCommandContext for this SmsCommandInstance + """ + if self._context is None: + self._context = SmsCommandContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SmsCommandInstance": + """ + Fetch the SmsCommandInstance + + + :returns: The fetched SmsCommandInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SmsCommandInstance": + """ + Asynchronous coroutine to fetch the SmsCommandInstance + + + :returns: The fetched SmsCommandInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SmsCommandContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SmsCommandContext + + :param version: Version that contains the resource + :param sid: The SID of the SMS Command resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/SmsCommands/{sid}".format(**self._solution) + + def fetch(self) -> SmsCommandInstance: + """ + Fetch the SmsCommandInstance + + + :returns: The fetched SmsCommandInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SmsCommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SmsCommandInstance: + """ + Asynchronous coroutine to fetch the SmsCommandInstance + + + :returns: The fetched SmsCommandInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SmsCommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SmsCommandPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SmsCommandInstance: + """ + Build an instance of SmsCommandInstance + + :param payload: Payload response from the API + """ + return SmsCommandInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SmsCommandList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SmsCommandList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SmsCommands" + + def create( + self, + sim: str, + payload: str, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + ) -> SmsCommandInstance: + """ + Create the SmsCommandInstance + + :param sim: The `sid` or `unique_name` of the [SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) to send the SMS Command to. + :param payload: The message body of the SMS Command. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param callback_url: The URL we should call using the `callback_method` after we have sent the command. + + :returns: The created SmsCommandInstance + """ + + data = values.of( + { + "Sim": sim, + "Payload": payload, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SmsCommandInstance(self._version, payload) + + async def create_async( + self, + sim: str, + payload: str, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + ) -> SmsCommandInstance: + """ + Asynchronously create the SmsCommandInstance + + :param sim: The `sid` or `unique_name` of the [SIM](https://www.twilio.com/docs/iot/supersim/api/sim-resource) to send the SMS Command to. + :param payload: The message body of the SMS Command. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `GET` or `POST` and the default is POST. + :param callback_url: The URL we should call using the `callback_method` after we have sent the command. + + :returns: The created SmsCommandInstance + """ + + data = values.of( + { + "Sim": sim, + "Payload": payload, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SmsCommandInstance(self._version, payload) + + def stream( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SmsCommandInstance]: + """ + Streams SmsCommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param "SmsCommandInstance.Status" status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param "SmsCommandInstance.Direction" direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + sim=sim, status=status, direction=direction, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SmsCommandInstance]: + """ + Asynchronously streams SmsCommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param "SmsCommandInstance.Status" status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param "SmsCommandInstance.Direction" direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + sim=sim, status=status, direction=direction, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SmsCommandInstance]: + """ + Lists SmsCommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param "SmsCommandInstance.Status" status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param "SmsCommandInstance.Direction" direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sim=sim, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SmsCommandInstance]: + """ + Asynchronously lists SmsCommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param "SmsCommandInstance.Status" status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param "SmsCommandInstance.Direction" direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sim=sim, + status=status, + direction=direction, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SmsCommandPage: + """ + Retrieve a single page of SmsCommandInstance records from the API. + Request is executed immediately + + :param sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SmsCommandInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SmsCommandPage(self._version, response) + + async def page_async( + self, + sim: Union[str, object] = values.unset, + status: Union["SmsCommandInstance.Status", object] = values.unset, + direction: Union["SmsCommandInstance.Direction", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SmsCommandPage: + """ + Asynchronously retrieve a single page of SmsCommandInstance records from the API. + Request is executed immediately + + :param sim: The SID or unique name of the Sim resource that SMS Command was sent to or from. + :param status: The status of the SMS Command. Can be: `queued`, `sent`, `delivered`, `received` or `failed`. See the [SMS Command Status Values](https://www.twilio.com/docs/iot/supersim/api/smscommand-resource#status-values) for a description of each. + :param direction: The direction of the SMS Command. Can be `to_sim` or `from_sim`. The value of `to_sim` is synonymous with the term `mobile terminated`, and `from_sim` is synonymous with the term `mobile originated`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SmsCommandInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "Direction": direction, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SmsCommandPage(self._version, response) + + def get_page(self, target_url: str) -> SmsCommandPage: + """ + Retrieve a specific page of SmsCommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SmsCommandInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SmsCommandPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SmsCommandPage: + """ + Asynchronously retrieve a specific page of SmsCommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SmsCommandInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SmsCommandPage(self._version, response) + + def get(self, sid: str) -> SmsCommandContext: + """ + Constructs a SmsCommandContext + + :param sid: The SID of the SMS Command resource to fetch. + """ + return SmsCommandContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SmsCommandContext: + """ + Constructs a SmsCommandContext + + :param sid: The SID of the SMS Command resource to fetch. + """ + return SmsCommandContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/usage_record.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/usage_record.py new file mode 100644 index 0000000..86a335d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/supersim/v1/usage_record.py @@ -0,0 +1,448 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Supersim + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UsageRecordInstance(InstanceResource): + + class Granularity(object): + HOUR = "hour" + DAY = "day" + ALL = "all" + + class Group(object): + SIM = "sim" + FLEET = "fleet" + NETWORK = "network" + ISOCOUNTRY = "isoCountry" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that incurred the usage. + :ivar sim_sid: SID of a Sim resource to which the UsageRecord belongs. Value will only be present when either a value for the `Sim` query parameter is provided or when UsageRecords are grouped by `sim`. Otherwise, the value will be `null`. + :ivar network_sid: SID of the Network resource the usage occurred on. Value will only be present when either a value for the `Network` query parameter is provided or when UsageRecords are grouped by `network`. Otherwise, the value will be `null`. + :ivar fleet_sid: SID of the Fleet resource the usage occurred on. Value will only be present when either a value for the `Fleet` query parameter is provided or when UsageRecords are grouped by `fleet`. Otherwise, the value will be `null`. + :ivar iso_country: Alpha-2 ISO Country Code that the usage occurred in. Value will only be present when either a value for the `IsoCountry` query parameter is provided or when UsageRecords are grouped by `isoCountry`. Otherwise, the value will be `null`. + :ivar period: The time period for which the usage is reported. The period is represented as a pair of `start_time` and `end_time` timestamps specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar data_upload: Total data uploaded in bytes, aggregated by the query parameters. + :ivar data_download: Total data downloaded in bytes, aggregated by the query parameters. + :ivar data_total: Total of data_upload and data_download. + :ivar data_total_billed: Total amount in the `billed_unit` that was charged for the data uploaded or downloaded. Will return 0 for usage prior to February 1, 2022. Value may be 0 despite `data_total` being greater than 0 if the data usage is still being processed by Twilio's billing system. Refer to [Data Usage Processing](https://www.twilio.com/docs/iot/supersim/api/usage-record-resource#data-usage-processing) for more details. + :ivar billed_unit: The currency in which the billed amounts are measured, specified in the 3 letter ISO 4127 format (e.g. `USD`, `EUR`, `JPY`). This can be null when data_toal_billed is 0 and we do not yet have billing information for the corresponding data usage. Refer to [Data Usage Processing](https://www.twilio.com/docs/iot/supersim/api/usage-record-resource#data-usage-processing) for more details. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.network_sid: Optional[str] = payload.get("network_sid") + self.fleet_sid: Optional[str] = payload.get("fleet_sid") + self.iso_country: Optional[str] = payload.get("iso_country") + self.period: Optional[Dict[str, object]] = payload.get("period") + self.data_upload: Optional[int] = payload.get("data_upload") + self.data_download: Optional[int] = payload.get("data_download") + self.data_total: Optional[int] = payload.get("data_total") + self.data_total_billed: Optional[float] = deserialize.decimal( + payload.get("data_total_billed") + ) + self.billed_unit: Optional[str] = payload.get("billed_unit") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class UsageRecordPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UsageRecordInstance: + """ + Build an instance of UsageRecordInstance + + :param payload: Payload response from the API + """ + return UsageRecordInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UsageRecordList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the UsageRecordList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/UsageRecords" + + def stream( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UsageRecordInstance]: + """ + Streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param str fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param str network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param str iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param "UsageRecordInstance.Group" group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param "UsageRecordInstance.Granularity" granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param datetime start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param datetime end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + sim=sim, + fleet=fleet, + network=network, + iso_country=iso_country, + group=group, + granularity=granularity, + start_time=start_time, + end_time=end_time, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UsageRecordInstance]: + """ + Asynchronously streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param str fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param str network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param str iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param "UsageRecordInstance.Group" group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param "UsageRecordInstance.Granularity" granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param datetime start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param datetime end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + sim=sim, + fleet=fleet, + network=network, + iso_country=iso_country, + group=group, + granularity=granularity, + start_time=start_time, + end_time=end_time, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param str fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param str network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param str iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param "UsageRecordInstance.Group" group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param "UsageRecordInstance.Granularity" granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param datetime start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param datetime end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sim=sim, + fleet=fleet, + network=network, + iso_country=iso_country, + group=group, + granularity=granularity, + start_time=start_time, + end_time=end_time, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Asynchronously lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param str fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param str network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param str iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param "UsageRecordInstance.Group" group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param "UsageRecordInstance.Granularity" granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param datetime start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param datetime end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sim=sim, + fleet=fleet, + network=network, + iso_country=iso_country, + group=group, + granularity=granularity, + start_time=start_time, + end_time=end_time, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "Sim": sim, + "Fleet": fleet, + "Network": network, + "IsoCountry": iso_country, + "Group": group, + "Granularity": granularity, + "StartTime": serialize.iso8601_datetime(start_time), + "EndTime": serialize.iso8601_datetime(end_time), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UsageRecordPage(self._version, response) + + async def page_async( + self, + sim: Union[str, object] = values.unset, + fleet: Union[str, object] = values.unset, + network: Union[str, object] = values.unset, + iso_country: Union[str, object] = values.unset, + group: Union["UsageRecordInstance.Group", object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + start_time: Union[datetime, object] = values.unset, + end_time: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Asynchronously retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param sim: SID or unique name of a Sim resource. Only show UsageRecords representing usage incurred by this Super SIM. + :param fleet: SID or unique name of a Fleet resource. Only show UsageRecords representing usage for Super SIMs belonging to this Fleet resource at the time the usage occurred. + :param network: SID of a Network resource. Only show UsageRecords representing usage on this network. + :param iso_country: Alpha-2 ISO Country Code. Only show UsageRecords representing usage in this country. + :param group: Dimension over which to aggregate usage records. Can be: `sim`, `fleet`, `network`, `isoCountry`. Default is to not aggregate across any of these dimensions, UsageRecords will be aggregated into the time buckets described by the `Granularity` parameter. + :param granularity: Time-based grouping that UsageRecords should be aggregated by. Can be: `hour`, `day`, or `all`. Default is `all`. `all` returns one UsageRecord that describes the usage for the entire period. + :param start_time: Only include usage that occurred at or after this time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is one month before the `end_time`. + :param end_time: Only include usage that occurred before this time (exclusive), specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Default is the current time. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "Sim": sim, + "Fleet": fleet, + "Network": network, + "IsoCountry": iso_country, + "Group": group, + "Granularity": granularity, + "StartTime": serialize.iso8601_datetime(start_time), + "EndTime": serialize.iso8601_datetime(end_time), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UsageRecordPage(self._version, response) + + def get_page(self, target_url: str) -> UsageRecordPage: + """ + Retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UsageRecordPage(self._version, response) + + async def get_page_async(self, target_url: str) -> UsageRecordPage: + """ + Asynchronously retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UsageRecordPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/SyncBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/SyncBase.py new file mode 100644 index 0000000..113cac0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/SyncBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.sync.v1 import V1 + + +class SyncBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Sync Domain + + :returns: Domain for Sync + """ + super().__init__(twilio, "https://sync.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Sync + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__init__.py new file mode 100644 index 0000000..37b9ce0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.sync.SyncBase import SyncBase +from twilio.rest.sync.v1.service import ServiceList + + +class Sync(SyncBase): + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v1.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.services diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/SyncBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/SyncBase.cpython-310.pyc new file mode 100644 index 0000000..0f027f1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/SyncBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..ba419b4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__init__.py new file mode 100644 index 0000000..e98201b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.sync.v1.service import ServiceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Sync + + :param domain: The Twilio.sync domain + """ + super().__init__(domain, "v1") + self._services: Optional[ServiceList] = None + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f8375aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__init__.py new file mode 100644 index 0000000..0bf1045 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__init__.py @@ -0,0 +1,816 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.sync.v1.service.document import DocumentList +from twilio.rest.sync.v1.service.sync_list import SyncListList +from twilio.rest.sync.v1.service.sync_map import SyncMapList +from twilio.rest.sync.v1.service.sync_stream import SyncStreamList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. It is a read-only property, it cannot be assigned using REST API. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Service resource. + :ivar webhook_url: The URL we call when Sync objects are manipulated. + :ivar webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + :ivar reachability_webhooks_enabled: Whether the service instance calls `webhook_url` when client endpoints connect to Sync. The default is `false`. + :ivar acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. It is disabled (false) by default. + :ivar reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :ivar reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before `webhook_url` is called, if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the reachability event from occurring. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhooks_from_rest_enabled: Optional[bool] = payload.get( + "webhooks_from_rest_enabled" + ) + self.reachability_webhooks_enabled: Optional[bool] = payload.get( + "reachability_webhooks_enabled" + ) + self.acl_enabled: Optional[bool] = payload.get("acl_enabled") + self.reachability_debouncing_enabled: Optional[bool] = payload.get( + "reachability_debouncing_enabled" + ) + self.reachability_debouncing_window: Optional[int] = deserialize.integer( + payload.get("reachability_debouncing_window") + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param friendly_name: A string that you assign to describe the resource. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the webhook is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the webhook from being called. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + webhook_url=webhook_url, + friendly_name=friendly_name, + reachability_webhooks_enabled=reachability_webhooks_enabled, + acl_enabled=acl_enabled, + reachability_debouncing_enabled=reachability_debouncing_enabled, + reachability_debouncing_window=reachability_debouncing_window, + webhooks_from_rest_enabled=webhooks_from_rest_enabled, + ) + + async def update_async( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param friendly_name: A string that you assign to describe the resource. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the webhook is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the webhook from being called. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + webhook_url=webhook_url, + friendly_name=friendly_name, + reachability_webhooks_enabled=reachability_webhooks_enabled, + acl_enabled=acl_enabled, + reachability_debouncing_enabled=reachability_debouncing_enabled, + reachability_debouncing_window=reachability_debouncing_window, + webhooks_from_rest_enabled=webhooks_from_rest_enabled, + ) + + @property + def documents(self) -> DocumentList: + """ + Access the documents + """ + return self._proxy.documents + + @property + def sync_lists(self) -> SyncListList: + """ + Access the sync_lists + """ + return self._proxy.sync_lists + + @property + def sync_maps(self) -> SyncMapList: + """ + Access the sync_maps + """ + return self._proxy.sync_maps + + @property + def sync_streams(self) -> SyncStreamList: + """ + Access the sync_streams + """ + return self._proxy.sync_streams + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The SID of the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._documents: Optional[DocumentList] = None + self._sync_lists: Optional[SyncListList] = None + self._sync_maps: Optional[SyncMapList] = None + self._sync_streams: Optional[SyncStreamList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param friendly_name: A string that you assign to describe the resource. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the webhook is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the webhook from being called. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "WebhookUrl": webhook_url, + "FriendlyName": friendly_name, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + "ReachabilityDebouncingEnabled": serialize.boolean_to_string( + reachability_debouncing_enabled + ), + "ReachabilityDebouncingWindow": reachability_debouncing_window, + "WebhooksFromRestEnabled": serialize.boolean_to_string( + webhooks_from_rest_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + webhook_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param friendly_name: A string that you assign to describe the resource. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the webhook is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the webhook from being called. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "WebhookUrl": webhook_url, + "FriendlyName": friendly_name, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + "ReachabilityDebouncingEnabled": serialize.boolean_to_string( + reachability_debouncing_enabled + ), + "ReachabilityDebouncingWindow": reachability_debouncing_window, + "WebhooksFromRestEnabled": serialize.boolean_to_string( + webhooks_from_rest_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def documents(self) -> DocumentList: + """ + Access the documents + """ + if self._documents is None: + self._documents = DocumentList( + self._version, + self._solution["sid"], + ) + return self._documents + + @property + def sync_lists(self) -> SyncListList: + """ + Access the sync_lists + """ + if self._sync_lists is None: + self._sync_lists = SyncListList( + self._version, + self._solution["sid"], + ) + return self._sync_lists + + @property + def sync_maps(self) -> SyncMapList: + """ + Access the sync_maps + """ + if self._sync_maps is None: + self._sync_maps = SyncMapList( + self._version, + self._solution["sid"], + ) + return self._sync_maps + + @property + def sync_streams(self) -> SyncStreamList: + """ + Access the sync_streams + """ + if self._sync_streams is None: + self._sync_streams = SyncStreamList( + self._version, + self._solution["sid"], + ) + return self._sync_streams + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + friendly_name: Union[str, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A string that you assign to describe the resource. + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the `webhook_url` is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the call to `webhook_url`. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + "ReachabilityDebouncingEnabled": serialize.boolean_to_string( + reachability_debouncing_enabled + ), + "ReachabilityDebouncingWindow": reachability_debouncing_window, + "WebhooksFromRestEnabled": serialize.boolean_to_string( + webhooks_from_rest_enabled + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + webhook_url: Union[str, object] = values.unset, + reachability_webhooks_enabled: Union[bool, object] = values.unset, + acl_enabled: Union[bool, object] = values.unset, + reachability_debouncing_enabled: Union[bool, object] = values.unset, + reachability_debouncing_window: Union[int, object] = values.unset, + webhooks_from_rest_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A string that you assign to describe the resource. + :param webhook_url: The URL we should call when Sync objects are manipulated. + :param reachability_webhooks_enabled: Whether the service instance should call `webhook_url` when client endpoints connect to Sync. The default is `false`. + :param acl_enabled: Whether token identities in the Service must be granted access to Sync objects by using the [Permissions](https://www.twilio.com/docs/sync/api/sync-permissions) resource. + :param reachability_debouncing_enabled: Whether every `endpoint_disconnected` event should occur after a configurable delay. The default is `false`, where the `endpoint_disconnected` event occurs immediately after disconnection. When `true`, intervening reconnections can prevent the `endpoint_disconnected` event. + :param reachability_debouncing_window: The reachability event delay in milliseconds if `reachability_debouncing_enabled` = `true`. Must be between 1,000 and 30,000 and defaults to 5,000. This is the number of milliseconds after the last running client disconnects, and a Sync identity is declared offline, before the `webhook_url` is called if all endpoints remain offline. A reconnection from the same identity by any endpoint during this interval prevents the call to `webhook_url`. + :param webhooks_from_rest_enabled: Whether the Service instance should call `webhook_url` when the REST API is used to update Sync objects. The default is `false`. + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "WebhookUrl": webhook_url, + "ReachabilityWebhooksEnabled": serialize.boolean_to_string( + reachability_webhooks_enabled + ), + "AclEnabled": serialize.boolean_to_string(acl_enabled), + "ReachabilityDebouncingEnabled": serialize.boolean_to_string( + reachability_debouncing_enabled + ), + "ReachabilityDebouncingWindow": reachability_debouncing_window, + "WebhooksFromRestEnabled": serialize.boolean_to_string( + webhooks_from_rest_enabled + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The SID of the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..217b361 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__init__.py new file mode 100644 index 0000000..f3b89cf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__init__.py @@ -0,0 +1,689 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.sync.v1.service.document.document_permission import ( + DocumentPermissionList, +) + + +class DocumentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Document resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource and can be up to 320 characters long. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Document resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar url: The absolute URL of the Document resource. + :ivar links: The URLs of resources related to the Sync Document. + :ivar revision: The current revision of the Sync Document, represented as a string. The `revision` property is used with conditional updates to ensure data consistency. + :ivar data: An arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :ivar date_expires: The date and time in GMT when the Sync Document expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the Sync Document does not expire, this value is `null`. The Document resource might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the Sync Document's creator. If the Sync Document is created from the client SDK, the value matches the Access Token's `identity` field. If the Sync Document was created from the REST API, the value is `system`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[DocumentContext] = None + + @property + def _proxy(self) -> "DocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DocumentContext for this DocumentInstance + """ + if self._context is None: + self._context = DocumentContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DocumentInstance": + """ + Fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DocumentInstance": + """ + Asynchronous coroutine to fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> "DocumentInstance": + """ + Update the DocumentInstance + + :param if_match: The If-Match HTTP request header + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (time-to-live). + + :returns: The updated DocumentInstance + """ + return self._proxy.update( + if_match=if_match, + data=data, + ttl=ttl, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> "DocumentInstance": + """ + Asynchronous coroutine to update the DocumentInstance + + :param if_match: The If-Match HTTP request header + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (time-to-live). + + :returns: The updated DocumentInstance + """ + return await self._proxy.update_async( + if_match=if_match, + data=data, + ttl=ttl, + ) + + @property + def document_permissions(self) -> DocumentPermissionList: + """ + Access the document_permissions + """ + return self._proxy.document_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the DocumentContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Document resource to update. + :param sid: The SID of the Document resource to update. Can be the Document resource's `sid` or its `unique_name`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Documents/{sid}".format(**self._solution) + + self._document_permissions: Optional[DocumentPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DocumentInstance: + """ + Fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> DocumentInstance: + """ + Asynchronous coroutine to fetch the DocumentInstance + + + :returns: The fetched DocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> DocumentInstance: + """ + Update the DocumentInstance + + :param if_match: The If-Match HTTP request header + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (time-to-live). + + :returns: The updated DocumentInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> DocumentInstance: + """ + Asynchronous coroutine to update the DocumentInstance + + :param if_match: The If-Match HTTP request header + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (time-to-live). + + :returns: The updated DocumentInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return DocumentInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def document_permissions(self) -> DocumentPermissionList: + """ + Access the document_permissions + """ + if self._document_permissions is None: + self._document_permissions = DocumentPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._document_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DocumentInstance: + """ + Build an instance of DocumentInstance + + :param payload: Payload response from the API + """ + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DocumentList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the DocumentList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Document resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Documents".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> DocumentInstance: + """ + Create the DocumentInstance + + :param unique_name: An application-defined string that uniquely identifies the Sync Document + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (the Sync Document's time-to-live). + + :returns: The created DocumentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Data": serialize.object(data), + "Ttl": ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> DocumentInstance: + """ + Asynchronously create the DocumentInstance + + :param unique_name: An application-defined string that uniquely identifies the Sync Document + :param data: A JSON string that represents an arbitrary, schema-less object that the Sync Document stores. Can be up to 16 KiB in length. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Document expires and is deleted (the Sync Document's time-to-live). + + :returns: The created DocumentInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Data": serialize.object(data), + "Ttl": ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DocumentInstance]: + """ + Streams DocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DocumentInstance]: + """ + Asynchronously streams DocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentInstance]: + """ + Lists DocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentInstance]: + """ + Asynchronously lists DocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPage: + """ + Retrieve a single page of DocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DocumentPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPage: + """ + Asynchronously retrieve a single page of DocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DocumentPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DocumentPage: + """ + Retrieve a specific page of DocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DocumentPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DocumentPage: + """ + Asynchronously retrieve a specific page of DocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DocumentPage(self._version, response, self._solution) + + def get(self, sid: str) -> DocumentContext: + """ + Constructs a DocumentContext + + :param sid: The SID of the Document resource to update. Can be the Document resource's `sid` or its `unique_name`. + """ + return DocumentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> DocumentContext: + """ + Constructs a DocumentContext + + :param sid: The SID of the Document resource to update. Can be the Document resource's `sid` or its `unique_name`. + """ + return DocumentContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..857f578 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/document_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/document_permission.cpython-310.pyc new file mode 100644 index 0000000..089cc1a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/__pycache__/document_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/document_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/document_permission.py new file mode 100644 index 0000000..40f5248 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/document/document_permission.py @@ -0,0 +1,593 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DocumentPermissionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Document Permission resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar document_sid: The SID of the Sync Document to which the Document Permission applies. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the Service to an FPA token. + :ivar read: Whether the identity can read the Sync Document. + :ivar write: Whether the identity can update the Sync Document. + :ivar manage: Whether the identity can delete the Sync Document. + :ivar url: The absolute URL of the Sync Document Permission resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + document_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.document_sid: Optional[str] = payload.get("document_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + "identity": identity or self.identity, + } + self._context: Optional[DocumentPermissionContext] = None + + @property + def _proxy(self) -> "DocumentPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: DocumentPermissionContext for this DocumentPermissionInstance + """ + if self._context is None: + self._context = DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "DocumentPermissionInstance": + """ + Fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "DocumentPermissionInstance": + """ + Asynchronous coroutine to fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "DocumentPermissionInstance": + """ + Update the DocumentPermissionInstance + + :param read: Whether the identity can read the Sync Document. Default value is `false`. + :param write: Whether the identity can update the Sync Document. Default value is `false`. + :param manage: Whether the identity can delete the Sync Document. Default value is `false`. + + :returns: The updated DocumentPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "DocumentPermissionInstance": + """ + Asynchronous coroutine to update the DocumentPermissionInstance + + :param read: Whether the identity can read the Sync Document. Default value is `false`. + :param write: Whether the identity can update the Sync Document. Default value is `false`. + :param manage: Whether the identity can delete the Sync Document. Default value is `false`. + + :returns: The updated DocumentPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPermissionContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, document_sid: str, identity: str + ): + """ + Initialize the DocumentPermissionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Document Permission resource to update. + :param document_sid: The SID of the Sync Document with the Document Permission resource to update. Can be the Document resource's `sid` or its `unique_name`. + :param identity: The application-defined string that uniquely identifies the User's Document Permission resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Documents/{document_sid}/Permissions/{identity}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the DocumentPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> DocumentPermissionInstance: + """ + Fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> DocumentPermissionInstance: + """ + Asynchronous coroutine to fetch the DocumentPermissionInstance + + + :returns: The fetched DocumentPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> DocumentPermissionInstance: + """ + Update the DocumentPermissionInstance + + :param read: Whether the identity can read the Sync Document. Default value is `false`. + :param write: Whether the identity can update the Sync Document. Default value is `false`. + :param manage: Whether the identity can delete the Sync Document. Default value is `false`. + + :returns: The updated DocumentPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> DocumentPermissionInstance: + """ + Asynchronous coroutine to update the DocumentPermissionInstance + + :param read: Whether the identity can read the Sync Document. Default value is `false`. + :param write: Whether the identity can update the Sync Document. Default value is `false`. + :param manage: Whether the identity can delete the Sync Document. Default value is `false`. + + :returns: The updated DocumentPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DocumentPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DocumentPermissionInstance: + """ + Build an instance of DocumentPermissionInstance + + :param payload: Payload response from the API + """ + return DocumentPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DocumentPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, document_sid: str): + """ + Initialize the DocumentPermissionList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Document Permission resources to read. + :param document_sid: The SID of the Sync Document with the Document Permission resources to read. Can be the Document resource's `sid` or its `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "document_sid": document_sid, + } + self._uri = ( + "/Services/{service_sid}/Documents/{document_sid}/Permissions".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DocumentPermissionInstance]: + """ + Streams DocumentPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DocumentPermissionInstance]: + """ + Asynchronously streams DocumentPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentPermissionInstance]: + """ + Lists DocumentPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DocumentPermissionInstance]: + """ + Asynchronously lists DocumentPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPermissionPage: + """ + Retrieve a single page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DocumentPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DocumentPermissionPage: + """ + Asynchronously retrieve a single page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DocumentPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DocumentPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DocumentPermissionPage: + """ + Retrieve a specific page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DocumentPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DocumentPermissionPage: + """ + Asynchronously retrieve a specific page of DocumentPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DocumentPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DocumentPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> DocumentPermissionContext: + """ + Constructs a DocumentPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Document Permission resource to update. + """ + return DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> DocumentPermissionContext: + """ + Constructs a DocumentPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Document Permission resource to update. + """ + return DocumentPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + document_sid=self._solution["document_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__init__.py new file mode 100644 index 0000000..7e9ca67 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__init__.py @@ -0,0 +1,693 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.sync.v1.service.sync_list.sync_list_item import SyncListItemList +from twilio.rest.sync.v1.service.sync_list.sync_list_permission import ( + SyncListPermissionList, +) + + +class SyncListInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Sync List resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Sync List resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar url: The absolute URL of the Sync List resource. + :ivar links: The URLs of the Sync List's nested resources. + :ivar revision: The current revision of the Sync List, represented as a string. + :ivar date_expires: The date and time in GMT when the Sync List expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the Sync List does not expire, this value is `null`. The Sync List might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the Sync List's creator. If the Sync List is created from the client SDK, the value matches the Access Token's `identity` field. If the Sync List was created from the REST API, the value is `system`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SyncListContext] = None + + @property + def _proxy(self) -> "SyncListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListContext for this SyncListInstance + """ + if self._context is None: + self._context = SyncListContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncListInstance": + """ + Fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListInstance": + """ + Asynchronous coroutine to fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncListInstance": + """ + Update the SyncListInstance + + :param ttl: An alias for `collection_ttl`. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The updated SyncListInstance + """ + return self._proxy.update( + ttl=ttl, + collection_ttl=collection_ttl, + ) + + async def update_async( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncListInstance": + """ + Asynchronous coroutine to update the SyncListInstance + + :param ttl: An alias for `collection_ttl`. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The updated SyncListInstance + """ + return await self._proxy.update_async( + ttl=ttl, + collection_ttl=collection_ttl, + ) + + @property + def sync_list_items(self) -> SyncListItemList: + """ + Access the sync_list_items + """ + return self._proxy.sync_list_items + + @property + def sync_list_permissions(self) -> SyncListPermissionList: + """ + Access the sync_list_permissions + """ + return self._proxy.sync_list_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SyncListContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync List resource to update. + :param sid: The SID of the Sync List resource to update. Can be the Sync List resource's `sid` or its `unique_name`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Lists/{sid}".format(**self._solution) + + self._sync_list_items: Optional[SyncListItemList] = None + self._sync_list_permissions: Optional[SyncListPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncListInstance: + """ + Fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SyncListInstance: + """ + Asynchronous coroutine to fetch the SyncListInstance + + + :returns: The fetched SyncListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListInstance: + """ + Update the SyncListInstance + + :param ttl: An alias for `collection_ttl`. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The updated SyncListInstance + """ + data = values.of( + { + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListInstance: + """ + Asynchronous coroutine to update the SyncListInstance + + :param ttl: An alias for `collection_ttl`. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The updated SyncListInstance + """ + data = values.of( + { + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def sync_list_items(self) -> SyncListItemList: + """ + Access the sync_list_items + """ + if self._sync_list_items is None: + self._sync_list_items = SyncListItemList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_list_items + + @property + def sync_list_permissions(self) -> SyncListPermissionList: + """ + Access the sync_list_permissions + """ + if self._sync_list_permissions is None: + self._sync_list_permissions = SyncListPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_list_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListInstance: + """ + Build an instance of SyncListInstance + + :param payload: Payload response from the API + """ + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SyncListList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync List resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Lists".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListInstance: + """ + Create the SyncListInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within its Service and it can be up to 320 characters long. The `unique_name` value can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: Alias for collection_ttl. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The created SyncListInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListInstance: + """ + Asynchronously create the SyncListInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within its Service and it can be up to 320 characters long. The `unique_name` value can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: Alias for collection_ttl. If both are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync List expires (time-to-live) and is deleted. + + :returns: The created SyncListInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListInstance]: + """ + Streams SyncListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListInstance]: + """ + Asynchronously streams SyncListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListInstance]: + """ + Lists SyncListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListInstance]: + """ + Asynchronously lists SyncListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPage: + """ + Retrieve a single page of SyncListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPage: + """ + Asynchronously retrieve a single page of SyncListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListPage: + """ + Retrieve a specific page of SyncListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListPage: + """ + Asynchronously retrieve a specific page of SyncListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListPage(self._version, response, self._solution) + + def get(self, sid: str) -> SyncListContext: + """ + Constructs a SyncListContext + + :param sid: The SID of the Sync List resource to update. Can be the Sync List resource's `sid` or its `unique_name`. + """ + return SyncListContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SyncListContext: + """ + Constructs a SyncListContext + + :param sid: The SID of the Sync List resource to update. Can be the Sync List resource's `sid` or its `unique_name`. + """ + return SyncListContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..760a44d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc new file mode 100644 index 0000000..f7b66c2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_item.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc new file mode 100644 index 0000000..bd90b24 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/__pycache__/sync_list_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_item.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_item.py new file mode 100644 index 0000000..02dafcd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_item.py @@ -0,0 +1,799 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncListItemInstance(InstanceResource): + + class QueryFromBoundType(object): + INCLUSIVE = "inclusive" + EXCLUSIVE = "exclusive" + + class QueryResultOrder(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar index: The automatically generated index of the List Item. The `index` values of the List Items in a Sync List can have gaps in their sequence. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the List Item resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar list_sid: The SID of the Sync List that contains the List Item. + :ivar url: The absolute URL of the List Item resource. + :ivar revision: The current revision of the item, represented as a string. + :ivar data: An arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :ivar date_expires: The date and time in GMT when the List Item expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the List Item does not expire, this value is `null`. The List Item resource might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the List Item's creator. If the item is created from the client SDK, the value matches the Access Token's `identity` field. If the item was created from the REST API, the value is `system`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + list_sid: str, + index: Optional[int] = None, + ): + super().__init__(version) + + self.index: Optional[int] = deserialize.integer(payload.get("index")) + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.list_sid: Optional[str] = payload.get("list_sid") + self.url: Optional[str] = payload.get("url") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "index": index or self.index, + } + self._context: Optional[SyncListItemContext] = None + + @property + def _proxy(self) -> "SyncListItemContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListItemContext for this SyncListItemInstance + """ + if self._context is None: + self._context = SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "SyncListItemInstance": + """ + Fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListItemInstance": + """ + Asynchronous coroutine to fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncListItemInstance": + """ + Update the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. This parameter can only be used when the List Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncListItemInstance + """ + return self._proxy.update( + if_match=if_match, + data=data, + ttl=ttl, + item_ttl=item_ttl, + collection_ttl=collection_ttl, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncListItemInstance": + """ + Asynchronous coroutine to update the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. This parameter can only be used when the List Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncListItemInstance + """ + return await self._proxy.update_async( + if_match=if_match, + data=data, + ttl=ttl, + item_ttl=item_ttl, + collection_ttl=collection_ttl, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListItemContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, list_sid: str, index: int): + """ + Initialize the SyncListItemContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync List Item resource to update. + :param list_sid: The SID of the Sync List with the Sync List Item resource to update. Can be the Sync List resource's `sid` or its `unique_name`. + :param index: The index of the Sync List Item resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "index": index, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Items/{index}".format( + **self._solution + ) + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> SyncListItemInstance: + """ + Fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + async def fetch_async(self) -> SyncListItemInstance: + """ + Asynchronous coroutine to fetch the SyncListItemInstance + + + :returns: The fetched SyncListItemInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListItemInstance: + """ + Update the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. This parameter can only be used when the List Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncListItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListItemInstance: + """ + Asynchronous coroutine to update the SyncListItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. This parameter can only be used when the List Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncListItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=self._solution["index"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListItemPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListItemInstance: + """ + Build an instance of SyncListItemInstance + + :param payload: Payload response from the API + """ + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListItemList(ListResource): + + def __init__(self, version: Version, service_sid: str, list_sid: str): + """ + Initialize the SyncListItemList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the List Item resources to read. + :param list_sid: The SID of the Sync List with the List Items to read. Can be the Sync List resource's `sid` or its `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Items".format( + **self._solution + ) + + def create( + self, + data: object, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListItemInstance: + """ + Create the SyncListItemInstance + + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. + + :returns: The created SyncListItemInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + async def create_async( + self, + data: object, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncListItemInstance: + """ + Asynchronously create the SyncListItemInstance + + :param data: A JSON string that represents an arbitrary, schema-less object that the List Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the List Item's parent Sync List expires (time-to-live) and is deleted. + + :returns: The created SyncListItemInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def stream( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListItemInstance]: + """ + Streams SyncListItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncListItemInstance.QueryResultOrder" order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param str from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param "SyncListItemInstance.QueryFromBoundType" bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListItemInstance]: + """ + Asynchronously streams SyncListItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncListItemInstance.QueryResultOrder" order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param str from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param "SyncListItemInstance.QueryFromBoundType" bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListItemInstance]: + """ + Lists SyncListItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncListItemInstance.QueryResultOrder" order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param str from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param "SyncListItemInstance.QueryFromBoundType" bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListItemInstance]: + """ + Asynchronously lists SyncListItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncListItemInstance.QueryResultOrder" order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param str from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param "SyncListItemInstance.QueryFromBoundType" bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListItemPage: + """ + Retrieve a single page of SyncListItemInstance records from the API. + Request is executed immediately + + :param order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListItemPage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["SyncListItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncListItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListItemPage: + """ + Asynchronously retrieve a single page of SyncListItemInstance records from the API. + Request is executed immediately + + :param order: How to order the List Items returned by their `index` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. + :param from_: The `index` of the first Sync List Item resource to read. See also `bounds`. + :param bounds: Whether to include the List Item referenced by the `from` parameter. Can be: `inclusive` to include the List Item referenced by the `from` parameter or `exclusive` to start with the next List Item. The default value is `inclusive`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListItemPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListItemPage: + """ + Retrieve a specific page of SyncListItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListItemInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListItemPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListItemPage: + """ + Asynchronously retrieve a specific page of SyncListItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListItemInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListItemPage(self._version, response, self._solution) + + def get(self, index: int) -> SyncListItemContext: + """ + Constructs a SyncListItemContext + + :param index: The index of the Sync List Item resource to update. + """ + return SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=index, + ) + + def __call__(self, index: int) -> SyncListItemContext: + """ + Constructs a SyncListItemContext + + :param index: The index of the Sync List Item resource to update. + """ + return SyncListItemContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + index=index, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_permission.py new file mode 100644 index 0000000..07fce14 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_list/sync_list_permission.py @@ -0,0 +1,593 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncListPermissionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Sync List Permission resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar list_sid: The SID of the Sync List to which the Permission applies. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the Service to an FPA token. + :ivar read: Whether the identity can read the Sync List and its Items. + :ivar write: Whether the identity can create, update, and delete Items in the Sync List. + :ivar manage: Whether the identity can delete the Sync List. + :ivar url: The absolute URL of the Sync List Permission resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + list_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.list_sid: Optional[str] = payload.get("list_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "identity": identity or self.identity, + } + self._context: Optional[SyncListPermissionContext] = None + + @property + def _proxy(self) -> "SyncListPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncListPermissionContext for this SyncListPermissionInstance + """ + if self._context is None: + self._context = SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncListPermissionInstance": + """ + Fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncListPermissionInstance": + """ + Asynchronous coroutine to fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "SyncListPermissionInstance": + """ + Update the SyncListPermissionInstance + + :param read: Whether the identity can read the Sync List and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync List. Default value is `false`. + :param manage: Whether the identity can delete the Sync List. Default value is `false`. + + :returns: The updated SyncListPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "SyncListPermissionInstance": + """ + Asynchronous coroutine to update the SyncListPermissionInstance + + :param read: Whether the identity can read the Sync List and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync List. Default value is `false`. + :param manage: Whether the identity can delete the Sync List. Default value is `false`. + + :returns: The updated SyncListPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPermissionContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, list_sid: str, identity: str + ): + """ + Initialize the SyncListPermissionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync List Permission resource to update. + :param list_sid: The SID of the Sync List with the Sync List Permission resource to update. Can be the Sync List resource's `sid` or its `unique_name`. + :param identity: The application-defined string that uniquely identifies the User's Sync List Permission resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + "identity": identity, + } + self._uri = ( + "/Services/{service_sid}/Lists/{list_sid}/Permissions/{identity}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncListPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncListPermissionInstance: + """ + Fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> SyncListPermissionInstance: + """ + Asynchronous coroutine to fetch the SyncListPermissionInstance + + + :returns: The fetched SyncListPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> SyncListPermissionInstance: + """ + Update the SyncListPermissionInstance + + :param read: Whether the identity can read the Sync List and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync List. Default value is `false`. + :param manage: Whether the identity can delete the Sync List. Default value is `false`. + + :returns: The updated SyncListPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> SyncListPermissionInstance: + """ + Asynchronous coroutine to update the SyncListPermissionInstance + + :param read: Whether the identity can read the Sync List and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync List. Default value is `false`. + :param manage: Whether the identity can delete the Sync List. Default value is `false`. + + :returns: The updated SyncListPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncListPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncListPermissionInstance: + """ + Build an instance of SyncListPermissionInstance + + :param payload: Payload response from the API + """ + return SyncListPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncListPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, list_sid: str): + """ + Initialize the SyncListPermissionList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync List Permission resources to read. + :param list_sid: The SID of the Sync List with the Sync List Permission resources to read. Can be the Sync List resource's `sid` or its `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "list_sid": list_sid, + } + self._uri = "/Services/{service_sid}/Lists/{list_sid}/Permissions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncListPermissionInstance]: + """ + Streams SyncListPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncListPermissionInstance]: + """ + Asynchronously streams SyncListPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListPermissionInstance]: + """ + Lists SyncListPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncListPermissionInstance]: + """ + Asynchronously lists SyncListPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPermissionPage: + """ + Retrieve a single page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncListPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncListPermissionPage: + """ + Asynchronously retrieve a single page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncListPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncListPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncListPermissionPage: + """ + Retrieve a specific page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncListPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncListPermissionPage: + """ + Asynchronously retrieve a specific page of SyncListPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncListPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncListPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> SyncListPermissionContext: + """ + Constructs a SyncListPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Sync List Permission resource to update. + """ + return SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> SyncListPermissionContext: + """ + Constructs a SyncListPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Sync List Permission resource to update. + """ + return SyncListPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + list_sid=self._solution["list_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__init__.py new file mode 100644 index 0000000..1a68e8e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__init__.py @@ -0,0 +1,693 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.sync.v1.service.sync_map.sync_map_item import SyncMapItemList +from twilio.rest.sync.v1.service.sync_map.sync_map_permission import ( + SyncMapPermissionList, +) + + +class SyncMapInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Sync Map resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Sync Map resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar url: The absolute URL of the Sync Map resource. + :ivar links: The URLs of the Sync Map's nested resources. + :ivar revision: The current revision of the Sync Map, represented as a string. + :ivar date_expires: The date and time in GMT when the Sync Map expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the Sync Map does not expire, this value is `null`. The Sync Map might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the Sync Map's creator. If the Sync Map is created from the client SDK, the value matches the Access Token's `identity` field. If the Sync Map was created from the REST API, the value is `system`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.revision: Optional[str] = payload.get("revision") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SyncMapContext] = None + + @property + def _proxy(self) -> "SyncMapContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapContext for this SyncMapInstance + """ + if self._context is None: + self._context = SyncMapContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncMapInstance": + """ + Fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapInstance": + """ + Asynchronous coroutine to fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncMapInstance": + """ + Update the SyncMapInstance + + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The updated SyncMapInstance + """ + return self._proxy.update( + ttl=ttl, + collection_ttl=collection_ttl, + ) + + async def update_async( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncMapInstance": + """ + Asynchronous coroutine to update the SyncMapInstance + + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The updated SyncMapInstance + """ + return await self._proxy.update_async( + ttl=ttl, + collection_ttl=collection_ttl, + ) + + @property + def sync_map_items(self) -> SyncMapItemList: + """ + Access the sync_map_items + """ + return self._proxy.sync_map_items + + @property + def sync_map_permissions(self) -> SyncMapPermissionList: + """ + Access the sync_map_permissions + """ + return self._proxy.sync_map_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SyncMapContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Map resource to update. + :param sid: The SID of the Sync Map resource to update. Can be the Sync Map's `sid` or its `unique_name`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Maps/{sid}".format(**self._solution) + + self._sync_map_items: Optional[SyncMapItemList] = None + self._sync_map_permissions: Optional[SyncMapPermissionList] = None + + def delete(self) -> bool: + """ + Deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncMapInstance: + """ + Fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SyncMapInstance: + """ + Asynchronous coroutine to fetch the SyncMapInstance + + + :returns: The fetched SyncMapInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapInstance: + """ + Update the SyncMapInstance + + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The updated SyncMapInstance + """ + data = values.of( + { + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapInstance: + """ + Asynchronous coroutine to update the SyncMapInstance + + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The updated SyncMapInstance + """ + data = values.of( + { + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def sync_map_items(self) -> SyncMapItemList: + """ + Access the sync_map_items + """ + if self._sync_map_items is None: + self._sync_map_items = SyncMapItemList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_map_items + + @property + def sync_map_permissions(self) -> SyncMapPermissionList: + """ + Access the sync_map_permissions + """ + if self._sync_map_permissions is None: + self._sync_map_permissions = SyncMapPermissionList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._sync_map_permissions + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapInstance: + """ + Build an instance of SyncMapInstance + + :param payload: Payload response from the API + """ + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SyncMapList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Map resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Maps".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapInstance: + """ + Create the SyncMapInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The created SyncMapInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapInstance: + """ + Asynchronously create the SyncMapInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: An alias for `collection_ttl`. If both parameters are provided, this value is ignored. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Sync Map expires (time-to-live) and is deleted. + + :returns: The created SyncMapInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapInstance]: + """ + Streams SyncMapInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapInstance]: + """ + Asynchronously streams SyncMapInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapInstance]: + """ + Lists SyncMapInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapInstance]: + """ + Asynchronously lists SyncMapInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPage: + """ + Retrieve a single page of SyncMapInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPage: + """ + Asynchronously retrieve a single page of SyncMapInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapPage: + """ + Retrieve a specific page of SyncMapInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapPage: + """ + Asynchronously retrieve a specific page of SyncMapInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapPage(self._version, response, self._solution) + + def get(self, sid: str) -> SyncMapContext: + """ + Constructs a SyncMapContext + + :param sid: The SID of the Sync Map resource to update. Can be the Sync Map's `sid` or its `unique_name`. + """ + return SyncMapContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SyncMapContext: + """ + Constructs a SyncMapContext + + :param sid: The SID of the Sync Map resource to update. Can be the Sync Map's `sid` or its `unique_name`. + """ + return SyncMapContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..17a9e42 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc new file mode 100644 index 0000000..af469a5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_item.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc new file mode 100644 index 0000000..6e020d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/__pycache__/sync_map_permission.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_item.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_item.py new file mode 100644 index 0000000..7c621f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_item.py @@ -0,0 +1,805 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncMapItemInstance(InstanceResource): + + class QueryFromBoundType(object): + INCLUSIVE = "inclusive" + EXCLUSIVE = "exclusive" + + class QueryResultOrder(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar key: The unique, user-defined key for the Map Item. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Map Item resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar map_sid: The SID of the Sync Map that contains the Map Item. + :ivar url: The absolute URL of the Map Item resource. + :ivar revision: The current revision of the Map Item, represented as a string. + :ivar data: An arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :ivar date_expires: The date and time in GMT when the Map Item expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the Map Item does not expire, this value is `null`. The Map Item might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the Map Item's creator. If the Map Item is created from the client SDK, the value matches the Access Token's `identity` field. If the Map Item was created from the REST API, the value is `system`. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + map_sid: str, + key: Optional[str] = None, + ): + super().__init__(version) + + self.key: Optional[str] = payload.get("key") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.map_sid: Optional[str] = payload.get("map_sid") + self.url: Optional[str] = payload.get("url") + self.revision: Optional[str] = payload.get("revision") + self.data: Optional[Dict[str, object]] = payload.get("data") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "key": key or self.key, + } + self._context: Optional[SyncMapItemContext] = None + + @property + def _proxy(self) -> "SyncMapItemContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapItemContext for this SyncMapItemInstance + """ + if self._context is None: + self._context = SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "SyncMapItemInstance": + """ + Fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapItemInstance": + """ + Asynchronous coroutine to fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncMapItemInstance": + """ + Update the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. This parameter can only be used when the Map Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncMapItemInstance + """ + return self._proxy.update( + if_match=if_match, + data=data, + ttl=ttl, + item_ttl=item_ttl, + collection_ttl=collection_ttl, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> "SyncMapItemInstance": + """ + Asynchronous coroutine to update the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. This parameter can only be used when the Map Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncMapItemInstance + """ + return await self._proxy.update_async( + if_match=if_match, + data=data, + ttl=ttl, + item_ttl=item_ttl, + collection_ttl=collection_ttl, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapItemContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, map_sid: str, key: str): + """ + Initialize the SyncMapItemContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Map Item resource to update. + :param map_sid: The SID of the Sync Map with the Sync Map Item resource to update. Can be the Sync Map resource's `sid` or its `unique_name`. + :param key: The `key` value of the Sync Map Item resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "key": key, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Items/{key}".format( + **self._solution + ) + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> SyncMapItemInstance: + """ + Fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + async def fetch_async(self) -> SyncMapItemInstance: + """ + Asynchronous coroutine to fetch the SyncMapItemInstance + + + :returns: The fetched SyncMapItemInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapItemInstance: + """ + Update the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. This parameter can only be used when the Map Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncMapItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + data: Union[object, object] = values.unset, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapItemInstance: + """ + Asynchronous coroutine to update the SyncMapItemInstance + + :param if_match: If provided, applies this mutation if (and only if) the “revision” field of this [map item] matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. This parameter can only be used when the Map Item's `data` or `ttl` is updated in the same request. + + :returns: The updated SyncMapItemInstance + """ + data = values.of( + { + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=self._solution["key"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapItemPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapItemInstance: + """ + Build an instance of SyncMapItemInstance + + :param payload: Payload response from the API + """ + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapItemList(ListResource): + + def __init__(self, version: Version, service_sid: str, map_sid: str): + """ + Initialize the SyncMapItemList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Map Item resources to read. + :param map_sid: The SID of the Sync Map with the Sync Map Item resource to fetch. Can be the Sync Map resource's `sid` or its `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Items".format( + **self._solution + ) + + def create( + self, + key: str, + data: object, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapItemInstance: + """ + Create the SyncMapItemInstance + + :param key: The unique, user-defined key for the Map Item. Can be up to 320 characters long. + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. + + :returns: The created SyncMapItemInstance + """ + + data = values.of( + { + "Key": key, + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + async def create_async( + self, + key: str, + data: object, + ttl: Union[int, object] = values.unset, + item_ttl: Union[int, object] = values.unset, + collection_ttl: Union[int, object] = values.unset, + ) -> SyncMapItemInstance: + """ + Asynchronously create the SyncMapItemInstance + + :param key: The unique, user-defined key for the Map Item. Can be up to 320 characters long. + :param data: A JSON string that represents an arbitrary, schema-less object that the Map Item stores. Can be up to 16 KiB in length. + :param ttl: An alias for `item_ttl`. If both parameters are provided, this value is ignored. + :param item_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item expires (time-to-live) and is deleted. + :param collection_ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Map Item's parent Sync Map expires (time-to-live) and is deleted. + + :returns: The created SyncMapItemInstance + """ + + data = values.of( + { + "Key": key, + "Data": serialize.object(data), + "Ttl": ttl, + "ItemTtl": item_ttl, + "CollectionTtl": collection_ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapItemInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def stream( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapItemInstance]: + """ + Streams SyncMapItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncMapItemInstance.QueryResultOrder" order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param str from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param "SyncMapItemInstance.QueryFromBoundType" bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapItemInstance]: + """ + Asynchronously streams SyncMapItemInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SyncMapItemInstance.QueryResultOrder" order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param str from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param "SyncMapItemInstance.QueryFromBoundType" bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + order=order, from_=from_, bounds=bounds, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapItemInstance]: + """ + Lists SyncMapItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncMapItemInstance.QueryResultOrder" order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param str from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param "SyncMapItemInstance.QueryFromBoundType" bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapItemInstance]: + """ + Asynchronously lists SyncMapItemInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SyncMapItemInstance.QueryResultOrder" order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param str from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param "SyncMapItemInstance.QueryFromBoundType" bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + order=order, + from_=from_, + bounds=bounds, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapItemPage: + """ + Retrieve a single page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapItemPage(self._version, response, self._solution) + + async def page_async( + self, + order: Union["SyncMapItemInstance.QueryResultOrder", object] = values.unset, + from_: Union[str, object] = values.unset, + bounds: Union["SyncMapItemInstance.QueryFromBoundType", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapItemPage: + """ + Asynchronously retrieve a single page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param order: How to order the Map Items returned by their `key` value. Can be: `asc` (ascending) or `desc` (descending) and the default is ascending. Map Items are [ordered lexicographically](https://en.wikipedia.org/wiki/Lexicographical_order) by Item key. + :param from_: The `key` of the first Sync Map Item resource to read. See also `bounds`. + :param bounds: Whether to include the Map Item referenced by the `from` parameter. Can be: `inclusive` to include the Map Item referenced by the `from` parameter or `exclusive` to start with the next Map Item. The default value is `inclusive`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapItemInstance + """ + data = values.of( + { + "Order": order, + "From": from_, + "Bounds": bounds, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapItemPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapItemPage: + """ + Retrieve a specific page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapItemInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapItemPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapItemPage: + """ + Asynchronously retrieve a specific page of SyncMapItemInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapItemInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapItemPage(self._version, response, self._solution) + + def get(self, key: str) -> SyncMapItemContext: + """ + Constructs a SyncMapItemContext + + :param key: The `key` value of the Sync Map Item resource to update. + """ + return SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=key, + ) + + def __call__(self, key: str) -> SyncMapItemContext: + """ + Constructs a SyncMapItemContext + + :param key: The `key` value of the Sync Map Item resource to update. + """ + return SyncMapItemContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + key=key, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_permission.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_permission.py new file mode 100644 index 0000000..ba05690 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_map/sync_map_permission.py @@ -0,0 +1,591 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SyncMapPermissionInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Sync Map Permission resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar map_sid: The SID of the Sync Map to which the Permission applies. + :ivar identity: The application-defined string that uniquely identifies the resource's User within the Service to an FPA token. + :ivar read: Whether the identity can read the Sync Map and its Items. + :ivar write: Whether the identity can create, update, and delete Items in the Sync Map. + :ivar manage: Whether the identity can delete the Sync Map. + :ivar url: The absolute URL of the Sync Map Permission resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + map_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.map_sid: Optional[str] = payload.get("map_sid") + self.identity: Optional[str] = payload.get("identity") + self.read: Optional[bool] = payload.get("read") + self.write: Optional[bool] = payload.get("write") + self.manage: Optional[bool] = payload.get("manage") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "identity": identity or self.identity, + } + self._context: Optional[SyncMapPermissionContext] = None + + @property + def _proxy(self) -> "SyncMapPermissionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncMapPermissionContext for this SyncMapPermissionInstance + """ + if self._context is None: + self._context = SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncMapPermissionInstance": + """ + Fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncMapPermissionInstance": + """ + Asynchronous coroutine to fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + return await self._proxy.fetch_async() + + def update( + self, read: bool, write: bool, manage: bool + ) -> "SyncMapPermissionInstance": + """ + Update the SyncMapPermissionInstance + + :param read: Whether the identity can read the Sync Map and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync Map. Default value is `false`. + :param manage: Whether the identity can delete the Sync Map. Default value is `false`. + + :returns: The updated SyncMapPermissionInstance + """ + return self._proxy.update( + read=read, + write=write, + manage=manage, + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> "SyncMapPermissionInstance": + """ + Asynchronous coroutine to update the SyncMapPermissionInstance + + :param read: Whether the identity can read the Sync Map and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync Map. Default value is `false`. + :param manage: Whether the identity can delete the Sync Map. Default value is `false`. + + :returns: The updated SyncMapPermissionInstance + """ + return await self._proxy.update_async( + read=read, + write=write, + manage=manage, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPermissionContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, map_sid: str, identity: str): + """ + Initialize the SyncMapPermissionContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Map Permission resource to update. Can be the Service's `sid` value or `default`. + :param map_sid: The SID of the Sync Map with the Sync Map Permission resource to update. Can be the Sync Map resource's `sid` or its `unique_name`. + :param identity: The application-defined string that uniquely identifies the User's Sync Map Permission resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + "identity": identity, + } + self._uri = ( + "/Services/{service_sid}/Maps/{map_sid}/Permissions/{identity}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncMapPermissionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncMapPermissionInstance: + """ + Fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> SyncMapPermissionInstance: + """ + Asynchronous coroutine to fetch the SyncMapPermissionInstance + + + :returns: The fetched SyncMapPermissionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + def update( + self, read: bool, write: bool, manage: bool + ) -> SyncMapPermissionInstance: + """ + Update the SyncMapPermissionInstance + + :param read: Whether the identity can read the Sync Map and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync Map. Default value is `false`. + :param manage: Whether the identity can delete the Sync Map. Default value is `false`. + + :returns: The updated SyncMapPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + async def update_async( + self, read: bool, write: bool, manage: bool + ) -> SyncMapPermissionInstance: + """ + Asynchronous coroutine to update the SyncMapPermissionInstance + + :param read: Whether the identity can read the Sync Map and its Items. Default value is `false`. + :param write: Whether the identity can create, update, and delete Items in the Sync Map. Default value is `false`. + :param manage: Whether the identity can delete the Sync Map. Default value is `false`. + + :returns: The updated SyncMapPermissionInstance + """ + data = values.of( + { + "Read": serialize.boolean_to_string(read), + "Write": serialize.boolean_to_string(write), + "Manage": serialize.boolean_to_string(manage), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncMapPermissionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncMapPermissionInstance: + """ + Build an instance of SyncMapPermissionInstance + + :param payload: Payload response from the API + """ + return SyncMapPermissionInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncMapPermissionList(ListResource): + + def __init__(self, version: Version, service_sid: str, map_sid: str): + """ + Initialize the SyncMapPermissionList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Map Permission resources to read. Can be the Service's `sid` value or `default`. + :param map_sid: The SID of the Sync Map with the Permission resources to read. Can be the Sync Map resource's `sid` or its `unique_name`. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "map_sid": map_sid, + } + self._uri = "/Services/{service_sid}/Maps/{map_sid}/Permissions".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncMapPermissionInstance]: + """ + Streams SyncMapPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncMapPermissionInstance]: + """ + Asynchronously streams SyncMapPermissionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapPermissionInstance]: + """ + Lists SyncMapPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncMapPermissionInstance]: + """ + Asynchronously lists SyncMapPermissionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPermissionPage: + """ + Retrieve a single page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncMapPermissionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncMapPermissionPage: + """ + Asynchronously retrieve a single page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncMapPermissionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncMapPermissionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncMapPermissionPage: + """ + Retrieve a specific page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapPermissionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncMapPermissionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncMapPermissionPage: + """ + Asynchronously retrieve a specific page of SyncMapPermissionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncMapPermissionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncMapPermissionPage(self._version, response, self._solution) + + def get(self, identity: str) -> SyncMapPermissionContext: + """ + Constructs a SyncMapPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Sync Map Permission resource to update. + """ + return SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=identity, + ) + + def __call__(self, identity: str) -> SyncMapPermissionContext: + """ + Constructs a SyncMapPermissionContext + + :param identity: The application-defined string that uniquely identifies the User's Sync Map Permission resource to update. + """ + return SyncMapPermissionContext( + self._version, + service_sid=self._solution["service_sid"], + map_sid=self._solution["map_sid"], + identity=identity, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__init__.py new file mode 100644 index 0000000..aa10d16 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__init__.py @@ -0,0 +1,641 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.sync.v1.service.sync_stream.stream_message import StreamMessageList + + +class SyncStreamInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Sync Stream resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Sync Stream resource. + :ivar service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) the resource is associated with. + :ivar url: The absolute URL of the Message Stream resource. + :ivar links: The URLs of the Stream's nested resources. + :ivar date_expires: The date and time in GMT when the Message Stream expires and will be deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. If the Message Stream does not expire, this value is `null`. The Stream might not be deleted immediately after it expires. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar created_by: The identity of the Stream's creator. If the Stream is created from the client SDK, the value matches the Access Token's `identity` field. If the Stream was created from the REST API, the value is 'system'. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.date_expires: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_expires") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.created_by: Optional[str] = payload.get("created_by") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[SyncStreamContext] = None + + @property + def _proxy(self) -> "SyncStreamContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SyncStreamContext for this SyncStreamInstance + """ + if self._context is None: + self._context = SyncStreamContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SyncStreamInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncStreamInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SyncStreamInstance": + """ + Fetch the SyncStreamInstance + + + :returns: The fetched SyncStreamInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SyncStreamInstance": + """ + Asynchronous coroutine to fetch the SyncStreamInstance + + + :returns: The fetched SyncStreamInstance + """ + return await self._proxy.fetch_async() + + def update(self, ttl: Union[int, object] = values.unset) -> "SyncStreamInstance": + """ + Update the SyncStreamInstance + + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The updated SyncStreamInstance + """ + return self._proxy.update( + ttl=ttl, + ) + + async def update_async( + self, ttl: Union[int, object] = values.unset + ) -> "SyncStreamInstance": + """ + Asynchronous coroutine to update the SyncStreamInstance + + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The updated SyncStreamInstance + """ + return await self._proxy.update_async( + ttl=ttl, + ) + + @property + def stream_messages(self) -> StreamMessageList: + """ + Access the stream_messages + """ + return self._proxy.stream_messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncStreamContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the SyncStreamContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Sync Stream resource to update. + :param sid: The SID of the Stream resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Streams/{sid}".format(**self._solution) + + self._stream_messages: Optional[StreamMessageList] = None + + def delete(self) -> bool: + """ + Deletes the SyncStreamInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SyncStreamInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SyncStreamInstance: + """ + Fetch the SyncStreamInstance + + + :returns: The fetched SyncStreamInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SyncStreamInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SyncStreamInstance: + """ + Asynchronous coroutine to fetch the SyncStreamInstance + + + :returns: The fetched SyncStreamInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SyncStreamInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, ttl: Union[int, object] = values.unset) -> SyncStreamInstance: + """ + Update the SyncStreamInstance + + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The updated SyncStreamInstance + """ + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncStreamInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, ttl: Union[int, object] = values.unset + ) -> SyncStreamInstance: + """ + Asynchronous coroutine to update the SyncStreamInstance + + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The updated SyncStreamInstance + """ + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncStreamInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def stream_messages(self) -> StreamMessageList: + """ + Access the stream_messages + """ + if self._stream_messages is None: + self._stream_messages = StreamMessageList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._stream_messages + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SyncStreamPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SyncStreamInstance: + """ + Build an instance of SyncStreamInstance + + :param payload: Payload response from the API + """ + return SyncStreamInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SyncStreamList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the SyncStreamList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) with the Stream resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Streams".format(**self._solution) + + def create( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> SyncStreamInstance: + """ + Create the SyncStreamInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within its Service and it can be up to 320 characters long. The `unique_name` value can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The created SyncStreamInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncStreamInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> SyncStreamInstance: + """ + Asynchronously create the SyncStreamInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. This value must be unique within its Service and it can be up to 320 characters long. The `unique_name` value can be used as an alternative to the `sid` in the URL path to address the resource. + :param ttl: How long, [in seconds](https://www.twilio.com/docs/sync/limits#sync-payload-limits), before the Stream expires and is deleted (time-to-live). + + :returns: The created SyncStreamInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Ttl": ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SyncStreamInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SyncStreamInstance]: + """ + Streams SyncStreamInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SyncStreamInstance]: + """ + Asynchronously streams SyncStreamInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncStreamInstance]: + """ + Lists SyncStreamInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SyncStreamInstance]: + """ + Asynchronously lists SyncStreamInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncStreamPage: + """ + Retrieve a single page of SyncStreamInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncStreamInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SyncStreamPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SyncStreamPage: + """ + Asynchronously retrieve a single page of SyncStreamInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SyncStreamInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SyncStreamPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SyncStreamPage: + """ + Retrieve a specific page of SyncStreamInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncStreamInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SyncStreamPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SyncStreamPage: + """ + Asynchronously retrieve a specific page of SyncStreamInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SyncStreamInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SyncStreamPage(self._version, response, self._solution) + + def get(self, sid: str) -> SyncStreamContext: + """ + Constructs a SyncStreamContext + + :param sid: The SID of the Stream resource to update. + """ + return SyncStreamContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> SyncStreamContext: + """ + Constructs a SyncStreamContext + + :param sid: The SID of the Stream resource to update. + """ + return SyncStreamContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..086369e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/stream_message.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/stream_message.cpython-310.pyc new file mode 100644 index 0000000..3df1329 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/__pycache__/stream_message.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/stream_message.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/stream_message.py new file mode 100644 index 0000000..88a7604 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/sync/v1/service/sync_stream/stream_message.py @@ -0,0 +1,140 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Sync + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class StreamMessageInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Stream Message resource. + :ivar data: An arbitrary, schema-less object that contains the Stream Message body. Can be up to 4 KiB in length. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + stream_sid: str, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.data: Optional[Dict[str, object]] = payload.get("data") + + self._solution = { + "service_sid": service_sid, + "stream_sid": stream_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class StreamMessageList(ListResource): + + def __init__(self, version: Version, service_sid: str, stream_sid: str): + """ + Initialize the StreamMessageList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Sync Service](https://www.twilio.com/docs/sync/api/service) to create the new Stream Message in. + :param stream_sid: The SID of the Sync Stream to create the new Stream Message resource for. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "stream_sid": stream_sid, + } + self._uri = "/Services/{service_sid}/Streams/{stream_sid}/Messages".format( + **self._solution + ) + + def create(self, data: object) -> StreamMessageInstance: + """ + Create the StreamMessageInstance + + :param data: A JSON string that represents an arbitrary, schema-less object that makes up the Stream Message body. Can be up to 4 KiB in length. + + :returns: The created StreamMessageInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamMessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + stream_sid=self._solution["stream_sid"], + ) + + async def create_async(self, data: object) -> StreamMessageInstance: + """ + Asynchronously create the StreamMessageInstance + + :param data: A JSON string that represents an arbitrary, schema-less object that makes up the Stream Message body. Can be up to 4 KiB in length. + + :returns: The created StreamMessageInstance + """ + + data = values.of( + { + "Data": serialize.object(data), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return StreamMessageInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + stream_sid=self._solution["stream_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/TaskrouterBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/TaskrouterBase.py new file mode 100644 index 0000000..4bbbdb6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/TaskrouterBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.taskrouter.v1 import V1 + + +class TaskrouterBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Taskrouter Domain + + :returns: Domain for Taskrouter + """ + super().__init__(twilio, "https://taskrouter.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Taskrouter + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__init__.py new file mode 100644 index 0000000..73d3ee8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.taskrouter.TaskrouterBase import TaskrouterBase +from twilio.rest.taskrouter.v1.workspace import WorkspaceList + + +class Taskrouter(TaskrouterBase): + @property + def workspaces(self) -> WorkspaceList: + warn( + "workspaces is deprecated. Use v1.workspaces instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.workspaces diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/TaskrouterBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/TaskrouterBase.cpython-310.pyc new file mode 100644 index 0000000..e128434 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/TaskrouterBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8a4bd3b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__init__.py new file mode 100644 index 0000000..a17eecc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.taskrouter.v1.workspace import WorkspaceList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Taskrouter + + :param domain: The Twilio.taskrouter domain + """ + super().__init__(domain, "v1") + self._workspaces: Optional[WorkspaceList] = None + + @property + def workspaces(self) -> WorkspaceList: + if self._workspaces is None: + self._workspaces = WorkspaceList(self) + return self._workspaces + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..85beff2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__init__.py new file mode 100644 index 0000000..f120219 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__init__.py @@ -0,0 +1,949 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.taskrouter.v1.workspace.activity import ActivityList +from twilio.rest.taskrouter.v1.workspace.event import EventList +from twilio.rest.taskrouter.v1.workspace.task import TaskList +from twilio.rest.taskrouter.v1.workspace.task_channel import TaskChannelList +from twilio.rest.taskrouter.v1.workspace.task_queue import TaskQueueList +from twilio.rest.taskrouter.v1.workspace.worker import WorkerList +from twilio.rest.taskrouter.v1.workspace.workflow import WorkflowList +from twilio.rest.taskrouter.v1.workspace.workspace_cumulative_statistics import ( + WorkspaceCumulativeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.workspace_real_time_statistics import ( + WorkspaceRealTimeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.workspace_statistics import ( + WorkspaceStatisticsList, +) + + +class WorkspaceInstance(InstanceResource): + + class QueueOrder(object): + FIFO = "FIFO" + LIFO = "LIFO" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workspace resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar default_activity_name: The name of the default activity. + :ivar default_activity_sid: The SID of the Activity that will be used when new Workers are created in the Workspace. + :ivar event_callback_url: The URL we call when an event occurs. If provided, the Workspace will publish events to this URL, for example, to collect data for reporting. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :ivar events_filter: The list of Workspace events for which to call `event_callback_url`. For example, if `EventsFilter=task.created, task.canceled, worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :ivar friendly_name: The string that you assigned to describe the Workspace resource. For example `Customer Support` or `2014 Election Campaign`. + :ivar multi_task_enabled: Whether multi-tasking is enabled. The default is `true`, which enables multi-tasking. Multi-tasking allows Workers to handle multiple Tasks simultaneously. When enabled (`true`), each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking each Worker would only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :ivar sid: The unique string that we created to identify the Workspace resource. + :ivar timeout_activity_name: The name of the timeout activity. + :ivar timeout_activity_sid: The SID of the Activity that will be assigned to a Worker when a Task reservation times out without a response. + :ivar prioritize_queue_order: + :ivar url: The absolute URL of the Workspace resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.default_activity_name: Optional[str] = payload.get("default_activity_name") + self.default_activity_sid: Optional[str] = payload.get("default_activity_sid") + self.event_callback_url: Optional[str] = payload.get("event_callback_url") + self.events_filter: Optional[str] = payload.get("events_filter") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.multi_task_enabled: Optional[bool] = payload.get("multi_task_enabled") + self.sid: Optional[str] = payload.get("sid") + self.timeout_activity_name: Optional[str] = payload.get("timeout_activity_name") + self.timeout_activity_sid: Optional[str] = payload.get("timeout_activity_sid") + self.prioritize_queue_order: Optional["WorkspaceInstance.QueueOrder"] = ( + payload.get("prioritize_queue_order") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[WorkspaceContext] = None + + @property + def _proxy(self) -> "WorkspaceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkspaceContext for this WorkspaceInstance + """ + if self._context is None: + self._context = WorkspaceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WorkspaceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WorkspaceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WorkspaceInstance": + """ + Fetch the WorkspaceInstance + + + :returns: The fetched WorkspaceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WorkspaceInstance": + """ + Asynchronous coroutine to fetch the WorkspaceInstance + + + :returns: The fetched WorkspaceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + default_activity_sid: Union[str, object] = values.unset, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + timeout_activity_sid: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> "WorkspaceInstance": + """ + Update the WorkspaceInstance + + :param default_activity_sid: The SID of the Activity that will be used when new Workers are created in the Workspace. + :param event_callback_url: The URL we should call when an event occurs. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example if `EventsFilter=task.created,task.canceled,worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param friendly_name: A descriptive string that you create to describe the Workspace resource. For example: `Sales Call Center` or `Customer Support Team`. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be maintained as multi-tasking. There is no default when omitting this parameter. A multi-tasking Workspace can't be updated to single-tasking unless it is not a Flex Project and another (legacy) single-tasking Workspace exists. Multi-tasking allows Workers to handle multiple Tasks simultaneously. In multi-tasking mode, each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param timeout_activity_sid: The SID of the Activity that will be assigned to a Worker when a Task reservation times out without a response. + :param prioritize_queue_order: + + :returns: The updated WorkspaceInstance + """ + return self._proxy.update( + default_activity_sid=default_activity_sid, + event_callback_url=event_callback_url, + events_filter=events_filter, + friendly_name=friendly_name, + multi_task_enabled=multi_task_enabled, + timeout_activity_sid=timeout_activity_sid, + prioritize_queue_order=prioritize_queue_order, + ) + + async def update_async( + self, + default_activity_sid: Union[str, object] = values.unset, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + timeout_activity_sid: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> "WorkspaceInstance": + """ + Asynchronous coroutine to update the WorkspaceInstance + + :param default_activity_sid: The SID of the Activity that will be used when new Workers are created in the Workspace. + :param event_callback_url: The URL we should call when an event occurs. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example if `EventsFilter=task.created,task.canceled,worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param friendly_name: A descriptive string that you create to describe the Workspace resource. For example: `Sales Call Center` or `Customer Support Team`. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be maintained as multi-tasking. There is no default when omitting this parameter. A multi-tasking Workspace can't be updated to single-tasking unless it is not a Flex Project and another (legacy) single-tasking Workspace exists. Multi-tasking allows Workers to handle multiple Tasks simultaneously. In multi-tasking mode, each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param timeout_activity_sid: The SID of the Activity that will be assigned to a Worker when a Task reservation times out without a response. + :param prioritize_queue_order: + + :returns: The updated WorkspaceInstance + """ + return await self._proxy.update_async( + default_activity_sid=default_activity_sid, + event_callback_url=event_callback_url, + events_filter=events_filter, + friendly_name=friendly_name, + multi_task_enabled=multi_task_enabled, + timeout_activity_sid=timeout_activity_sid, + prioritize_queue_order=prioritize_queue_order, + ) + + @property + def activities(self) -> ActivityList: + """ + Access the activities + """ + return self._proxy.activities + + @property + def events(self) -> EventList: + """ + Access the events + """ + return self._proxy.events + + @property + def tasks(self) -> TaskList: + """ + Access the tasks + """ + return self._proxy.tasks + + @property + def task_channels(self) -> TaskChannelList: + """ + Access the task_channels + """ + return self._proxy.task_channels + + @property + def task_queues(self) -> TaskQueueList: + """ + Access the task_queues + """ + return self._proxy.task_queues + + @property + def workers(self) -> WorkerList: + """ + Access the workers + """ + return self._proxy.workers + + @property + def workflows(self) -> WorkflowList: + """ + Access the workflows + """ + return self._proxy.workflows + + @property + def cumulative_statistics(self) -> WorkspaceCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + return self._proxy.cumulative_statistics + + @property + def real_time_statistics(self) -> WorkspaceRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + return self._proxy.real_time_statistics + + @property + def statistics(self) -> WorkspaceStatisticsList: + """ + Access the statistics + """ + return self._proxy.statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkspaceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the WorkspaceContext + + :param version: Version that contains the resource + :param sid: The SID of the Workspace resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Workspaces/{sid}".format(**self._solution) + + self._activities: Optional[ActivityList] = None + self._events: Optional[EventList] = None + self._tasks: Optional[TaskList] = None + self._task_channels: Optional[TaskChannelList] = None + self._task_queues: Optional[TaskQueueList] = None + self._workers: Optional[WorkerList] = None + self._workflows: Optional[WorkflowList] = None + self._cumulative_statistics: Optional[WorkspaceCumulativeStatisticsList] = None + self._real_time_statistics: Optional[WorkspaceRealTimeStatisticsList] = None + self._statistics: Optional[WorkspaceStatisticsList] = None + + def delete(self) -> bool: + """ + Deletes the WorkspaceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WorkspaceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WorkspaceInstance: + """ + Fetch the WorkspaceInstance + + + :returns: The fetched WorkspaceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WorkspaceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WorkspaceInstance: + """ + Asynchronous coroutine to fetch the WorkspaceInstance + + + :returns: The fetched WorkspaceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WorkspaceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + default_activity_sid: Union[str, object] = values.unset, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + timeout_activity_sid: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> WorkspaceInstance: + """ + Update the WorkspaceInstance + + :param default_activity_sid: The SID of the Activity that will be used when new Workers are created in the Workspace. + :param event_callback_url: The URL we should call when an event occurs. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example if `EventsFilter=task.created,task.canceled,worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param friendly_name: A descriptive string that you create to describe the Workspace resource. For example: `Sales Call Center` or `Customer Support Team`. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be maintained as multi-tasking. There is no default when omitting this parameter. A multi-tasking Workspace can't be updated to single-tasking unless it is not a Flex Project and another (legacy) single-tasking Workspace exists. Multi-tasking allows Workers to handle multiple Tasks simultaneously. In multi-tasking mode, each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param timeout_activity_sid: The SID of the Activity that will be assigned to a Worker when a Task reservation times out without a response. + :param prioritize_queue_order: + + :returns: The updated WorkspaceInstance + """ + data = values.of( + { + "DefaultActivitySid": default_activity_sid, + "EventCallbackUrl": event_callback_url, + "EventsFilter": events_filter, + "FriendlyName": friendly_name, + "MultiTaskEnabled": serialize.boolean_to_string(multi_task_enabled), + "TimeoutActivitySid": timeout_activity_sid, + "PrioritizeQueueOrder": prioritize_queue_order, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkspaceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + default_activity_sid: Union[str, object] = values.unset, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + timeout_activity_sid: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> WorkspaceInstance: + """ + Asynchronous coroutine to update the WorkspaceInstance + + :param default_activity_sid: The SID of the Activity that will be used when new Workers are created in the Workspace. + :param event_callback_url: The URL we should call when an event occurs. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example if `EventsFilter=task.created,task.canceled,worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param friendly_name: A descriptive string that you create to describe the Workspace resource. For example: `Sales Call Center` or `Customer Support Team`. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be maintained as multi-tasking. There is no default when omitting this parameter. A multi-tasking Workspace can't be updated to single-tasking unless it is not a Flex Project and another (legacy) single-tasking Workspace exists. Multi-tasking allows Workers to handle multiple Tasks simultaneously. In multi-tasking mode, each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param timeout_activity_sid: The SID of the Activity that will be assigned to a Worker when a Task reservation times out without a response. + :param prioritize_queue_order: + + :returns: The updated WorkspaceInstance + """ + data = values.of( + { + "DefaultActivitySid": default_activity_sid, + "EventCallbackUrl": event_callback_url, + "EventsFilter": events_filter, + "FriendlyName": friendly_name, + "MultiTaskEnabled": serialize.boolean_to_string(multi_task_enabled), + "TimeoutActivitySid": timeout_activity_sid, + "PrioritizeQueueOrder": prioritize_queue_order, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkspaceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def activities(self) -> ActivityList: + """ + Access the activities + """ + if self._activities is None: + self._activities = ActivityList( + self._version, + self._solution["sid"], + ) + return self._activities + + @property + def events(self) -> EventList: + """ + Access the events + """ + if self._events is None: + self._events = EventList( + self._version, + self._solution["sid"], + ) + return self._events + + @property + def tasks(self) -> TaskList: + """ + Access the tasks + """ + if self._tasks is None: + self._tasks = TaskList( + self._version, + self._solution["sid"], + ) + return self._tasks + + @property + def task_channels(self) -> TaskChannelList: + """ + Access the task_channels + """ + if self._task_channels is None: + self._task_channels = TaskChannelList( + self._version, + self._solution["sid"], + ) + return self._task_channels + + @property + def task_queues(self) -> TaskQueueList: + """ + Access the task_queues + """ + if self._task_queues is None: + self._task_queues = TaskQueueList( + self._version, + self._solution["sid"], + ) + return self._task_queues + + @property + def workers(self) -> WorkerList: + """ + Access the workers + """ + if self._workers is None: + self._workers = WorkerList( + self._version, + self._solution["sid"], + ) + return self._workers + + @property + def workflows(self) -> WorkflowList: + """ + Access the workflows + """ + if self._workflows is None: + self._workflows = WorkflowList( + self._version, + self._solution["sid"], + ) + return self._workflows + + @property + def cumulative_statistics(self) -> WorkspaceCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + if self._cumulative_statistics is None: + self._cumulative_statistics = WorkspaceCumulativeStatisticsList( + self._version, + self._solution["sid"], + ) + return self._cumulative_statistics + + @property + def real_time_statistics(self) -> WorkspaceRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + if self._real_time_statistics is None: + self._real_time_statistics = WorkspaceRealTimeStatisticsList( + self._version, + self._solution["sid"], + ) + return self._real_time_statistics + + @property + def statistics(self) -> WorkspaceStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = WorkspaceStatisticsList( + self._version, + self._solution["sid"], + ) + return self._statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkspacePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WorkspaceInstance: + """ + Build an instance of WorkspaceInstance + + :param payload: Payload response from the API + """ + return WorkspaceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WorkspaceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the WorkspaceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Workspaces" + + def create( + self, + friendly_name: str, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + template: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> WorkspaceInstance: + """ + Create the WorkspaceInstance + + :param friendly_name: A descriptive string that you create to describe the Workspace resource. It can be up to 64 characters long. For example: `Customer Support` or `2014 Election Campaign`. + :param event_callback_url: The URL we should call when an event occurs. If provided, the Workspace will publish events to this URL, for example, to collect data for reporting. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example, if `EventsFilter=task.created, task.canceled, worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be created as multi-tasking. The default is `true`. Multi-tasking allows Workers to handle multiple Tasks simultaneously. When enabled (`true`), each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param template: An available template name. Can be: `NONE` or `FIFO` and the default is `NONE`. Pre-configures the Workspace with the Workflow and Activities specified in the template. `NONE` will create a Workspace with only a set of default activities. `FIFO` will configure TaskRouter with a set of default activities and a single TaskQueue for first-in, first-out distribution, which can be useful when you are getting started with TaskRouter. + :param prioritize_queue_order: + + :returns: The created WorkspaceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "EventCallbackUrl": event_callback_url, + "EventsFilter": events_filter, + "MultiTaskEnabled": serialize.boolean_to_string(multi_task_enabled), + "Template": template, + "PrioritizeQueueOrder": prioritize_queue_order, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkspaceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + event_callback_url: Union[str, object] = values.unset, + events_filter: Union[str, object] = values.unset, + multi_task_enabled: Union[bool, object] = values.unset, + template: Union[str, object] = values.unset, + prioritize_queue_order: Union[ + "WorkspaceInstance.QueueOrder", object + ] = values.unset, + ) -> WorkspaceInstance: + """ + Asynchronously create the WorkspaceInstance + + :param friendly_name: A descriptive string that you create to describe the Workspace resource. It can be up to 64 characters long. For example: `Customer Support` or `2014 Election Campaign`. + :param event_callback_url: The URL we should call when an event occurs. If provided, the Workspace will publish events to this URL, for example, to collect data for reporting. See [Workspace Events](https://www.twilio.com/docs/taskrouter/api/event) for more information. This parameter supports Twilio's [Webhooks (HTTP callbacks) Connection Overrides](https://www.twilio.com/docs/usage/webhooks/webhooks-connection-overrides). + :param events_filter: The list of Workspace events for which to call event_callback_url. For example, if `EventsFilter=task.created, task.canceled, worker.activity.update`, then TaskRouter will call event_callback_url only when a task is created, canceled, or a Worker activity is updated. + :param multi_task_enabled: Whether to enable multi-tasking. Can be: `true` to enable multi-tasking, or `false` to disable it. However, all workspaces should be created as multi-tasking. The default is `true`. Multi-tasking allows Workers to handle multiple Tasks simultaneously. When enabled (`true`), each Worker can receive parallel reservations up to the per-channel maximums defined in the Workers section. In single-tasking mode (legacy mode), each Worker will only receive a new reservation when the previous task is completed. Learn more at [Multitasking](https://www.twilio.com/docs/taskrouter/multitasking). + :param template: An available template name. Can be: `NONE` or `FIFO` and the default is `NONE`. Pre-configures the Workspace with the Workflow and Activities specified in the template. `NONE` will create a Workspace with only a set of default activities. `FIFO` will configure TaskRouter with a set of default activities and a single TaskQueue for first-in, first-out distribution, which can be useful when you are getting started with TaskRouter. + :param prioritize_queue_order: + + :returns: The created WorkspaceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "EventCallbackUrl": event_callback_url, + "EventsFilter": events_filter, + "MultiTaskEnabled": serialize.boolean_to_string(multi_task_enabled), + "Template": template, + "PrioritizeQueueOrder": prioritize_queue_order, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkspaceInstance(self._version, payload) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WorkspaceInstance]: + """ + Streams WorkspaceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WorkspaceInstance]: + """ + Asynchronously streams WorkspaceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkspaceInstance]: + """ + Lists WorkspaceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkspaceInstance]: + """ + Asynchronously lists WorkspaceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkspacePage: + """ + Retrieve a single page of WorkspaceInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkspaceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WorkspacePage(self._version, response) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkspacePage: + """ + Asynchronously retrieve a single page of WorkspaceInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Workspace resources to read. For example `Customer Support` or `2014 Election Campaign`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkspaceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WorkspacePage(self._version, response) + + def get_page(self, target_url: str) -> WorkspacePage: + """ + Retrieve a specific page of WorkspaceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkspaceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WorkspacePage(self._version, response) + + async def get_page_async(self, target_url: str) -> WorkspacePage: + """ + Asynchronously retrieve a specific page of WorkspaceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkspaceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WorkspacePage(self._version, response) + + def get(self, sid: str) -> WorkspaceContext: + """ + Constructs a WorkspaceContext + + :param sid: The SID of the Workspace resource to update. + """ + return WorkspaceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> WorkspaceContext: + """ + Constructs a WorkspaceContext + + :param sid: The SID of the Workspace resource to update. + """ + return WorkspaceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..7e706e0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/activity.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/activity.cpython-310.pyc new file mode 100644 index 0000000..3340e18 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/activity.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/event.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/event.cpython-310.pyc new file mode 100644 index 0000000..8b1382f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/event.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/task_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/task_channel.cpython-310.pyc new file mode 100644 index 0000000..04ee779 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/task_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_cumulative_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_cumulative_statistics.cpython-310.pyc new file mode 100644 index 0000000..dedc29d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_cumulative_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_real_time_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_real_time_statistics.cpython-310.pyc new file mode 100644 index 0000000..42faf9e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_real_time_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_statistics.cpython-310.pyc new file mode 100644 index 0000000..f326086 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/__pycache__/workspace_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/activity.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/activity.py new file mode 100644 index 0000000..bae49d5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/activity.py @@ -0,0 +1,656 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ActivityInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Activity resource. + :ivar available: Whether the Worker is eligible to receive a Task when it occupies the Activity. A value of `true`, `1`, or `yes` indicates the Activity is available. All other values indicate that it is not. The value cannot be changed after the Activity is created. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar friendly_name: The string that you assigned to describe the Activity resource. + :ivar sid: The unique string that we created to identify the Activity resource. + :ivar workspace_sid: The SID of the Workspace that contains the Activity. + :ivar url: The absolute URL of the Activity resource. + :ivar links: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.available: Optional[bool] = payload.get("available") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[ActivityContext] = None + + @property + def _proxy(self) -> "ActivityContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ActivityContext for this ActivityInstance + """ + if self._context is None: + self._context = ActivityContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ActivityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ActivityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ActivityInstance": + """ + Fetch the ActivityInstance + + + :returns: The fetched ActivityInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ActivityInstance": + """ + Asynchronous coroutine to fetch the ActivityInstance + + + :returns: The fetched ActivityInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "ActivityInstance": + """ + Update the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + + :returns: The updated ActivityInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "ActivityInstance": + """ + Asynchronous coroutine to update the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + + :returns: The updated ActivityInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ActivityContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the ActivityContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Activity resources to update. + :param sid: The SID of the Activity resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Activities/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the ActivityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ActivityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ActivityInstance: + """ + Fetch the ActivityInstance + + + :returns: The fetched ActivityInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ActivityInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ActivityInstance: + """ + Asynchronous coroutine to fetch the ActivityInstance + + + :returns: The fetched ActivityInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ActivityInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> ActivityInstance: + """ + Update the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + + :returns: The updated ActivityInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ActivityInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> ActivityInstance: + """ + Asynchronous coroutine to update the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + + :returns: The updated ActivityInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ActivityInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ActivityPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ActivityInstance: + """ + Build an instance of ActivityInstance + + :param payload: Payload response from the API + """ + return ActivityInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ActivityList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the ActivityList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Activity resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Activities".format(**self._solution) + + def create( + self, friendly_name: str, available: Union[bool, object] = values.unset + ) -> ActivityInstance: + """ + Create the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + :param available: Whether the Worker should be eligible to receive a Task when it occupies the Activity. A value of `true`, `1`, or `yes` specifies the Activity is available. All other values specify that it is not. The value cannot be changed after the Activity is created. + + :returns: The created ActivityInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Available": serialize.boolean_to_string(available), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ActivityInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, friendly_name: str, available: Union[bool, object] = values.unset + ) -> ActivityInstance: + """ + Asynchronously create the ActivityInstance + + :param friendly_name: A descriptive string that you create to describe the Activity resource. It can be up to 64 characters long. These names are used to calculate and expose statistics about Workers, and provide visibility into the state of each Worker. Examples of friendly names include: `on-call`, `break`, and `email`. + :param available: Whether the Worker should be eligible to receive a Task when it occupies the Activity. A value of `true`, `1`, or `yes` specifies the Activity is available. All other values specify that it is not. The value cannot be changed after the Activity is created. + + :returns: The created ActivityInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Available": serialize.boolean_to_string(available), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ActivityInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ActivityInstance]: + """ + Streams ActivityInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Activity resources to read. + :param str available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + friendly_name=friendly_name, + available=available, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ActivityInstance]: + """ + Asynchronously streams ActivityInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Activity resources to read. + :param str available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, + available=available, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ActivityInstance]: + """ + Lists ActivityInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Activity resources to read. + :param str available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + available=available, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ActivityInstance]: + """ + Asynchronously lists ActivityInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Activity resources to read. + :param str available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + available=available, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ActivityPage: + """ + Retrieve a single page of ActivityInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Activity resources to read. + :param available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ActivityInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Available": available, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ActivityPage(self._version, response, self._solution) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ActivityPage: + """ + Asynchronously retrieve a single page of ActivityInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Activity resources to read. + :param available: Whether return only Activity resources that are available or unavailable. A value of `true` returns only available activities. Values of '1' or `yes` also indicate `true`. All other values represent `false` and return activities that are unavailable. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ActivityInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Available": available, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ActivityPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ActivityPage: + """ + Retrieve a specific page of ActivityInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ActivityInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ActivityPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ActivityPage: + """ + Asynchronously retrieve a specific page of ActivityInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ActivityInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ActivityPage(self._version, response, self._solution) + + def get(self, sid: str) -> ActivityContext: + """ + Constructs a ActivityContext + + :param sid: The SID of the Activity resource to update. + """ + return ActivityContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ActivityContext: + """ + Constructs a ActivityContext + + :param sid: The SID of the Activity resource to update. + """ + return ActivityContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/event.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/event.py new file mode 100644 index 0000000..534d9cd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/event.py @@ -0,0 +1,644 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EventInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Event resource. + :ivar actor_sid: The SID of the resource that triggered the event. + :ivar actor_type: The type of resource that triggered the event. + :ivar actor_url: The absolute URL of the resource that triggered the event. + :ivar description: A description of the event. + :ivar event_data: Data about the event. For more information, see [Event types](https://www.twilio.com/docs/taskrouter/api/event#event-types). + :ivar event_date: The time the event was sent, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar event_date_ms: The time the event was sent in milliseconds. + :ivar event_type: The identifier for the event. + :ivar resource_sid: The SID of the object the event is most relevant to, such as a TaskSid, ReservationSid, or a WorkerSid. + :ivar resource_type: The type of object the event is most relevant to, such as a Task, Reservation, or a Worker). + :ivar resource_url: The URL of the resource the event is most relevant to. + :ivar sid: The unique string that we created to identify the Event resource. + :ivar source: Where the Event originated. + :ivar source_ip_address: The IP from which the Event originated. + :ivar url: The absolute URL of the Event resource. + :ivar workspace_sid: The SID of the Workspace that contains the Event. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.actor_sid: Optional[str] = payload.get("actor_sid") + self.actor_type: Optional[str] = payload.get("actor_type") + self.actor_url: Optional[str] = payload.get("actor_url") + self.description: Optional[str] = payload.get("description") + self.event_data: Optional[Dict[str, object]] = payload.get("event_data") + self.event_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("event_date") + ) + self.event_date_ms: Optional[int] = payload.get("event_date_ms") + self.event_type: Optional[str] = payload.get("event_type") + self.resource_sid: Optional[str] = payload.get("resource_sid") + self.resource_type: Optional[str] = payload.get("resource_type") + self.resource_url: Optional[str] = payload.get("resource_url") + self.sid: Optional[str] = payload.get("sid") + self.source: Optional[str] = payload.get("source") + self.source_ip_address: Optional[str] = payload.get("source_ip_address") + self.url: Optional[str] = payload.get("url") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[EventContext] = None + + @property + def _proxy(self) -> "EventContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EventContext for this EventInstance + """ + if self._context is None: + self._context = EventContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EventInstance": + """ + Fetch the EventInstance + + + :returns: The fetched EventInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EventInstance": + """ + Asynchronous coroutine to fetch the EventInstance + + + :returns: The fetched EventInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the EventContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Event to fetch. + :param sid: The SID of the Event resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Events/{sid}".format(**self._solution) + + def fetch(self) -> EventInstance: + """ + Fetch the EventInstance + + + :returns: The fetched EventInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EventInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EventInstance: + """ + Asynchronous coroutine to fetch the EventInstance + + + :returns: The fetched EventInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EventInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EventPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EventInstance: + """ + Build an instance of EventInstance + + :param payload: Payload response from the API + """ + return EventInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EventList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the EventList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Events to read. Returns only the Events that pertain to the specified Workspace. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Events".format(**self._solution) + + def stream( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EventInstance]: + """ + Streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str event_type: The type of Events to read. Returns only Events of the type specified. + :param int minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param str reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param datetime start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param str task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param str task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param str worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param str workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param str task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param str sid: The SID of the Event resource to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + end_date=end_date, + event_type=event_type, + minutes=minutes, + reservation_sid=reservation_sid, + start_date=start_date, + task_queue_sid=task_queue_sid, + task_sid=task_sid, + worker_sid=worker_sid, + workflow_sid=workflow_sid, + task_channel=task_channel, + sid=sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EventInstance]: + """ + Asynchronously streams EventInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str event_type: The type of Events to read. Returns only Events of the type specified. + :param int minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param str reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param datetime start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param str task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param str task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param str worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param str workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param str task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param str sid: The SID of the Event resource to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + end_date=end_date, + event_type=event_type, + minutes=minutes, + reservation_sid=reservation_sid, + start_date=start_date, + task_queue_sid=task_queue_sid, + task_sid=task_sid, + worker_sid=worker_sid, + workflow_sid=workflow_sid, + task_channel=task_channel, + sid=sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str event_type: The type of Events to read. Returns only Events of the type specified. + :param int minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param str reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param datetime start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param str task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param str task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param str worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param str workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param str task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param str sid: The SID of the Event resource to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + end_date=end_date, + event_type=event_type, + minutes=minutes, + reservation_sid=reservation_sid, + start_date=start_date, + task_queue_sid=task_queue_sid, + task_sid=task_sid, + worker_sid=worker_sid, + workflow_sid=workflow_sid, + task_channel=task_channel, + sid=sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EventInstance]: + """ + Asynchronously lists EventInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str event_type: The type of Events to read. Returns only Events of the type specified. + :param int minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param str reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param datetime start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param str task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param str task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param str worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param str workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param str task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param str sid: The SID of the Event resource to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + end_date=end_date, + event_type=event_type, + minutes=minutes, + reservation_sid=reservation_sid, + start_date=start_date, + task_queue_sid=task_queue_sid, + task_sid=task_sid, + worker_sid=worker_sid, + workflow_sid=workflow_sid, + task_channel=task_channel, + sid=sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param event_type: The type of Events to read. Returns only Events of the type specified. + :param minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param sid: The SID of the Event resource to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "EventType": event_type, + "Minutes": minutes, + "ReservationSid": reservation_sid, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskQueueSid": task_queue_sid, + "TaskSid": task_sid, + "WorkerSid": worker_sid, + "WorkflowSid": workflow_sid, + "TaskChannel": task_channel, + "Sid": sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EventPage(self._version, response, self._solution) + + async def page_async( + self, + end_date: Union[datetime, object] = values.unset, + event_type: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + reservation_sid: Union[str, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_sid: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EventPage: + """ + Asynchronously retrieve a single page of EventInstance records from the API. + Request is executed immediately + + :param end_date: Only include Events that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param event_type: The type of Events to read. Returns only Events of the type specified. + :param minutes: The period of events to read in minutes. Returns only Events that occurred since this many minutes in the past. The default is `15` minutes. Task Attributes for Events occuring more 43,200 minutes ago will be redacted. + :param reservation_sid: The SID of the Reservation with the Events to read. Returns only Events that pertain to the specified Reservation. + :param start_date: Only include Events from on or after this date and time, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Task Attributes for Events older than 30 days will be redacted. + :param task_queue_sid: The SID of the TaskQueue with the Events to read. Returns only the Events that pertain to the specified TaskQueue. + :param task_sid: The SID of the Task with the Events to read. Returns only the Events that pertain to the specified Task. + :param worker_sid: The SID of the Worker with the Events to read. Returns only the Events that pertain to the specified Worker. + :param workflow_sid: The SID of the Workflow with the Events to read. Returns only the Events that pertain to the specified Workflow. + :param task_channel: The TaskChannel with the Events to read. Returns only the Events that pertain to the specified TaskChannel. + :param sid: The SID of the Event resource to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EventInstance + """ + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "EventType": event_type, + "Minutes": minutes, + "ReservationSid": reservation_sid, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskQueueSid": task_queue_sid, + "TaskSid": task_sid, + "WorkerSid": worker_sid, + "WorkflowSid": workflow_sid, + "TaskChannel": task_channel, + "Sid": sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EventPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EventPage: + """ + Retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EventPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EventPage: + """ + Asynchronously retrieve a specific page of EventInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EventInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EventPage(self._version, response, self._solution) + + def get(self, sid: str) -> EventContext: + """ + Constructs a EventContext + + :param sid: The SID of the Event resource to fetch. + """ + return EventContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> EventContext: + """ + Constructs a EventContext + + :param sid: The SID of the Event resource to fetch. + """ + return EventContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__init__.py new file mode 100644 index 0000000..f5c306b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__init__.py @@ -0,0 +1,974 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.taskrouter.v1.workspace.task.reservation import ReservationList + + +class TaskInstance(InstanceResource): + + class Status(object): + PENDING = "pending" + RESERVED = "reserved" + ASSIGNED = "assigned" + CANCELED = "canceled" + COMPLETED = "completed" + WRAPPING = "wrapping" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Task resource. + :ivar age: The number of seconds since the Task was created. + :ivar assignment_status: + :ivar attributes: The JSON string with custom attributes of the work. **Note** If this property has been assigned a value, it will only be displayed in FETCH action that returns a single resource. Otherwise, it will be null. + :ivar addons: An object that contains the [Add-on](https://www.twilio.com/docs/add-ons) data for all installed Add-ons. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar task_queue_entered_date: The date and time in GMT when the Task entered the TaskQueue, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar priority: The current priority score of the Task as assigned to a Worker by the workflow. Tasks with higher priority values will be assigned before Tasks with lower values. + :ivar reason: The reason the Task was canceled or completed, if applicable. + :ivar sid: The unique string that we created to identify the Task resource. + :ivar task_queue_sid: The SID of the TaskQueue. + :ivar task_queue_friendly_name: The friendly name of the TaskQueue. + :ivar task_channel_sid: The SID of the TaskChannel. + :ivar task_channel_unique_name: The unique name of the TaskChannel. + :ivar timeout: The amount of time in seconds that the Task can live before being assigned. + :ivar workflow_sid: The SID of the Workflow that is controlling the Task. + :ivar workflow_friendly_name: The friendly name of the Workflow that is controlling the Task. + :ivar workspace_sid: The SID of the Workspace that contains the Task. + :ivar url: The absolute URL of the Task resource. + :ivar links: The URLs of related resources. + :ivar virtual_start_time: The date and time in GMT indicating the ordering for routing of the Task specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.age: Optional[int] = deserialize.integer(payload.get("age")) + self.assignment_status: Optional["TaskInstance.Status"] = payload.get( + "assignment_status" + ) + self.attributes: Optional[str] = payload.get("attributes") + self.addons: Optional[str] = payload.get("addons") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.task_queue_entered_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("task_queue_entered_date") + ) + self.priority: Optional[int] = deserialize.integer(payload.get("priority")) + self.reason: Optional[str] = payload.get("reason") + self.sid: Optional[str] = payload.get("sid") + self.task_queue_sid: Optional[str] = payload.get("task_queue_sid") + self.task_queue_friendly_name: Optional[str] = payload.get( + "task_queue_friendly_name" + ) + self.task_channel_sid: Optional[str] = payload.get("task_channel_sid") + self.task_channel_unique_name: Optional[str] = payload.get( + "task_channel_unique_name" + ) + self.timeout: Optional[int] = deserialize.integer(payload.get("timeout")) + self.workflow_sid: Optional[str] = payload.get("workflow_sid") + self.workflow_friendly_name: Optional[str] = payload.get( + "workflow_friendly_name" + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.virtual_start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("virtual_start_time") + ) + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[TaskContext] = None + + @property + def _proxy(self) -> "TaskContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskContext for this TaskInstance + """ + if self._context is None: + self._context = TaskContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the TaskInstance + + :param if_match: If provided, deletes this Task if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the TaskInstance + + :param if_match: If provided, deletes this Task if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "TaskInstance": + """ + Fetch the TaskInstance + + + :returns: The fetched TaskInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TaskInstance": + """ + Asynchronous coroutine to fetch the TaskInstance + + + :returns: The fetched TaskInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + assignment_status: Union["TaskInstance.Status", object] = values.unset, + reason: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> "TaskInstance": + """ + Update the TaskInstance + + :param if_match: If provided, applies this mutation if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param attributes: The JSON string that describes the custom attributes of the task. + :param assignment_status: + :param reason: The reason that the Task was canceled or completed. This parameter is required only if the Task is canceled or completed. Setting this value queues the task for deletion and logs the reason. + :param priority: The Task's new priority value. When supplied, the Task takes on the specified priority unless it matches a Workflow Target with a Priority set. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel with the task to update. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param virtual_start_time: The task's new virtual start time value. When supplied, the Task takes on the specified virtual start time. Value can't be in the future. + + :returns: The updated TaskInstance + """ + return self._proxy.update( + if_match=if_match, + attributes=attributes, + assignment_status=assignment_status, + reason=reason, + priority=priority, + task_channel=task_channel, + virtual_start_time=virtual_start_time, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + assignment_status: Union["TaskInstance.Status", object] = values.unset, + reason: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> "TaskInstance": + """ + Asynchronous coroutine to update the TaskInstance + + :param if_match: If provided, applies this mutation if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param attributes: The JSON string that describes the custom attributes of the task. + :param assignment_status: + :param reason: The reason that the Task was canceled or completed. This parameter is required only if the Task is canceled or completed. Setting this value queues the task for deletion and logs the reason. + :param priority: The Task's new priority value. When supplied, the Task takes on the specified priority unless it matches a Workflow Target with a Priority set. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel with the task to update. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param virtual_start_time: The task's new virtual start time value. When supplied, the Task takes on the specified virtual start time. Value can't be in the future. + + :returns: The updated TaskInstance + """ + return await self._proxy.update_async( + if_match=if_match, + attributes=attributes, + assignment_status=assignment_status, + reason=reason, + priority=priority, + task_channel=task_channel, + virtual_start_time=virtual_start_time, + ) + + @property + def reservations(self) -> ReservationList: + """ + Access the reservations + """ + return self._proxy.reservations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the TaskContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Task to update. + :param sid: The SID of the Task resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Tasks/{sid}".format(**self._solution) + + self._reservations: Optional[ReservationList] = None + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the TaskInstance + + :param if_match: If provided, deletes this Task if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the TaskInstance + + :param if_match: If provided, deletes this Task if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> TaskInstance: + """ + Fetch the TaskInstance + + + :returns: The fetched TaskInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TaskInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TaskInstance: + """ + Asynchronous coroutine to fetch the TaskInstance + + + :returns: The fetched TaskInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TaskInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + assignment_status: Union["TaskInstance.Status", object] = values.unset, + reason: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> TaskInstance: + """ + Update the TaskInstance + + :param if_match: If provided, applies this mutation if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param attributes: The JSON string that describes the custom attributes of the task. + :param assignment_status: + :param reason: The reason that the Task was canceled or completed. This parameter is required only if the Task is canceled or completed. Setting this value queues the task for deletion and logs the reason. + :param priority: The Task's new priority value. When supplied, the Task takes on the specified priority unless it matches a Workflow Target with a Priority set. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel with the task to update. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param virtual_start_time: The task's new virtual start time value. When supplied, the Task takes on the specified virtual start time. Value can't be in the future. + + :returns: The updated TaskInstance + """ + data = values.of( + { + "Attributes": attributes, + "AssignmentStatus": assignment_status, + "Reason": reason, + "Priority": priority, + "TaskChannel": task_channel, + "VirtualStartTime": serialize.iso8601_datetime(virtual_start_time), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return TaskInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + assignment_status: Union["TaskInstance.Status", object] = values.unset, + reason: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> TaskInstance: + """ + Asynchronous coroutine to update the TaskInstance + + :param if_match: If provided, applies this mutation if (and only if) the [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) header of the Task matches the provided value. This matches the semantics of (and is implemented with) the HTTP [If-Match header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match). + :param attributes: The JSON string that describes the custom attributes of the task. + :param assignment_status: + :param reason: The reason that the Task was canceled or completed. This parameter is required only if the Task is canceled or completed. Setting this value queues the task for deletion and logs the reason. + :param priority: The Task's new priority value. When supplied, the Task takes on the specified priority unless it matches a Workflow Target with a Priority set. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel with the task to update. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param virtual_start_time: The task's new virtual start time value. When supplied, the Task takes on the specified virtual start time. Value can't be in the future. + + :returns: The updated TaskInstance + """ + data = values.of( + { + "Attributes": attributes, + "AssignmentStatus": assignment_status, + "Reason": reason, + "Priority": priority, + "TaskChannel": task_channel, + "VirtualStartTime": serialize.iso8601_datetime(virtual_start_time), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return TaskInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + @property + def reservations(self) -> ReservationList: + """ + Access the reservations + """ + if self._reservations is None: + self._reservations = ReservationList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._reservations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TaskInstance: + """ + Build an instance of TaskInstance + + :param payload: Payload response from the API + """ + return TaskInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TaskList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the TaskList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Tasks to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Tasks".format(**self._solution) + + def create( + self, + timeout: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> TaskInstance: + """ + Create the TaskInstance + + :param timeout: The amount of time in seconds the new task can live before being assigned. Can be up to a maximum of 2 weeks (1,209,600 seconds). The default value is 24 hours (86,400 seconds). On timeout, the `task.canceled` event will fire with description `Task TTL Exceeded`. + :param priority: The priority to assign the new task and override the default. When supplied, the new Task will have this priority unless it matches a Workflow Target with a Priority set. When not supplied, the new Task will have the priority of the matching Workflow Target. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel by passing either its `unique_name` or `sid`. Default value is `default`. + :param workflow_sid: The SID of the Workflow that you would like to handle routing for the new Task. If there is only one Workflow defined for the Workspace that you are posting the new task to, this parameter is optional. + :param attributes: A URL-encoded JSON string with the attributes of the new task. This value is passed to the Workflow's `assignment_callback_url` when the Task is assigned to a Worker. For example: `{ \\\"task_type\\\": \\\"call\\\", \\\"twilio_call_sid\\\": \\\"CAxxx\\\", \\\"customer_ticket_number\\\": \\\"12345\\\" }`. + :param virtual_start_time: The virtual start time to assign the new task and override the default. When supplied, the new task will have this virtual start time. When not supplied, the new task will have the virtual start time equal to `date_created`. Value can't be in the future. + + :returns: The created TaskInstance + """ + + data = values.of( + { + "Timeout": timeout, + "Priority": priority, + "TaskChannel": task_channel, + "WorkflowSid": workflow_sid, + "Attributes": attributes, + "VirtualStartTime": serialize.iso8601_datetime(virtual_start_time), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, + timeout: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + task_channel: Union[str, object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + virtual_start_time: Union[datetime, object] = values.unset, + ) -> TaskInstance: + """ + Asynchronously create the TaskInstance + + :param timeout: The amount of time in seconds the new task can live before being assigned. Can be up to a maximum of 2 weeks (1,209,600 seconds). The default value is 24 hours (86,400 seconds). On timeout, the `task.canceled` event will fire with description `Task TTL Exceeded`. + :param priority: The priority to assign the new task and override the default. When supplied, the new Task will have this priority unless it matches a Workflow Target with a Priority set. When not supplied, the new Task will have the priority of the matching Workflow Target. Value can be 0 to 2^31^ (2,147,483,647). + :param task_channel: When MultiTasking is enabled, specify the TaskChannel by passing either its `unique_name` or `sid`. Default value is `default`. + :param workflow_sid: The SID of the Workflow that you would like to handle routing for the new Task. If there is only one Workflow defined for the Workspace that you are posting the new task to, this parameter is optional. + :param attributes: A URL-encoded JSON string with the attributes of the new task. This value is passed to the Workflow's `assignment_callback_url` when the Task is assigned to a Worker. For example: `{ \\\"task_type\\\": \\\"call\\\", \\\"twilio_call_sid\\\": \\\"CAxxx\\\", \\\"customer_ticket_number\\\": \\\"12345\\\" }`. + :param virtual_start_time: The virtual start time to assign the new task and override the default. When supplied, the new task will have this virtual start time. When not supplied, the new task will have the virtual start time equal to `date_created`. Value can't be in the future. + + :returns: The created TaskInstance + """ + + data = values.of( + { + "Timeout": timeout, + "Priority": priority, + "TaskChannel": task_channel, + "WorkflowSid": workflow_sid, + "Attributes": attributes, + "VirtualStartTime": serialize.iso8601_datetime(virtual_start_time), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TaskInstance]: + """ + Streams TaskInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param List[str] assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param str workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param str workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param str task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param str task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param str evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param str ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param bool has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + priority=priority, + assignment_status=assignment_status, + workflow_sid=workflow_sid, + workflow_name=workflow_name, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + evaluate_task_attributes=evaluate_task_attributes, + ordering=ordering, + has_addons=has_addons, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TaskInstance]: + """ + Asynchronously streams TaskInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param int priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param List[str] assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param str workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param str workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param str task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param str task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param str evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param str ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param bool has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + priority=priority, + assignment_status=assignment_status, + workflow_sid=workflow_sid, + workflow_name=workflow_name, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + evaluate_task_attributes=evaluate_task_attributes, + ordering=ordering, + has_addons=has_addons, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskInstance]: + """ + Lists TaskInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param List[str] assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param str workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param str workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param str task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param str task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param str evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param str ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param bool has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + priority=priority, + assignment_status=assignment_status, + workflow_sid=workflow_sid, + workflow_name=workflow_name, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + evaluate_task_attributes=evaluate_task_attributes, + ordering=ordering, + has_addons=has_addons, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskInstance]: + """ + Asynchronously lists TaskInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param int priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param List[str] assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param str workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param str workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param str task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param str task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param str evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param str ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param bool has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + priority=priority, + assignment_status=assignment_status, + workflow_sid=workflow_sid, + workflow_name=workflow_name, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + evaluate_task_attributes=evaluate_task_attributes, + ordering=ordering, + has_addons=has_addons, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskPage: + """ + Retrieve a single page of TaskInstance records from the API. + Request is executed immediately + + :param priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskInstance + """ + data = values.of( + { + "Priority": priority, + "AssignmentStatus": serialize.map(assignment_status, lambda e: e), + "WorkflowSid": workflow_sid, + "WorkflowName": workflow_name, + "TaskQueueSid": task_queue_sid, + "TaskQueueName": task_queue_name, + "EvaluateTaskAttributes": evaluate_task_attributes, + "Ordering": ordering, + "HasAddons": serialize.boolean_to_string(has_addons), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TaskPage(self._version, response, self._solution) + + async def page_async( + self, + priority: Union[int, object] = values.unset, + assignment_status: Union[List[str], object] = values.unset, + workflow_sid: Union[str, object] = values.unset, + workflow_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + evaluate_task_attributes: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + has_addons: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskPage: + """ + Asynchronously retrieve a single page of TaskInstance records from the API. + Request is executed immediately + + :param priority: The priority value of the Tasks to read. Returns the list of all Tasks in the Workspace with the specified priority. + :param assignment_status: The `assignment_status` of the Tasks you want to read. Can be: `pending`, `reserved`, `assigned`, `canceled`, `wrapping`, or `completed`. Returns all Tasks in the Workspace with the specified `assignment_status`. + :param workflow_sid: The SID of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this SID. + :param workflow_name: The friendly name of the Workflow with the Tasks to read. Returns the Tasks controlled by the Workflow identified by this friendly name. + :param task_queue_sid: The SID of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this SID. + :param task_queue_name: The `friendly_name` of the TaskQueue with the Tasks to read. Returns the Tasks waiting in the TaskQueue identified by this friendly name. + :param evaluate_task_attributes: The attributes of the Tasks to read. Returns the Tasks that match the attributes specified in this parameter. + :param ordering: How to order the returned Task resources. By default, Tasks are sorted by ascending DateCreated. This value is specified as: `Attribute:Order`, where `Attribute` can be either `DateCreated`, `Priority`, or `VirtualStartTime` and `Order` can be either `asc` or `desc`. For example, `Priority:desc` returns Tasks ordered in descending order of their Priority. Pairings of sort orders can be specified in a comma-separated list such as `Priority:desc,DateCreated:asc`, which returns the Tasks in descending Priority order and ascending DateCreated Order. The only ordering pairing not allowed is DateCreated and VirtualStartTime. + :param has_addons: Whether to read Tasks with Add-ons. If `true`, returns only Tasks with Add-ons. If `false`, returns only Tasks without Add-ons. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskInstance + """ + data = values.of( + { + "Priority": priority, + "AssignmentStatus": serialize.map(assignment_status, lambda e: e), + "WorkflowSid": workflow_sid, + "WorkflowName": workflow_name, + "TaskQueueSid": task_queue_sid, + "TaskQueueName": task_queue_name, + "EvaluateTaskAttributes": evaluate_task_attributes, + "Ordering": ordering, + "HasAddons": serialize.boolean_to_string(has_addons), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TaskPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TaskPage: + """ + Retrieve a specific page of TaskInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TaskPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TaskPage: + """ + Asynchronously retrieve a specific page of TaskInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TaskPage(self._version, response, self._solution) + + def get(self, sid: str) -> TaskContext: + """ + Constructs a TaskContext + + :param sid: The SID of the Task resource to update. + """ + return TaskContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> TaskContext: + """ + Constructs a TaskContext + + :param sid: The SID of the Task resource to update. + """ + return TaskContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3b750fc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/reservation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/reservation.cpython-310.pyc new file mode 100644 index 0000000..43fe1b0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/__pycache__/reservation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/reservation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/reservation.py new file mode 100644 index 0000000..50b1ed1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task/reservation.py @@ -0,0 +1,1325 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ReservationInstance(InstanceResource): + + class CallStatus(object): + INITIATED = "initiated" + RINGING = "ringing" + ANSWERED = "answered" + COMPLETED = "completed" + + class ConferenceEvent(object): + START = "start" + END = "end" + JOIN = "join" + LEAVE = "leave" + MUTE = "mute" + HOLD = "hold" + SPEAKER = "speaker" + + class Status(object): + PENDING = "pending" + ACCEPTED = "accepted" + REJECTED = "rejected" + TIMEOUT = "timeout" + CANCELED = "canceled" + RESCINDED = "rescinded" + WRAPPING = "wrapping" + COMPLETED = "completed" + + class SupervisorMode(object): + MONITOR = "monitor" + WHISPER = "whisper" + BARGE = "barge" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskReservation resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar reservation_status: + :ivar sid: The unique string that we created to identify the TaskReservation resource. + :ivar task_sid: The SID of the reserved Task resource. + :ivar worker_name: The `friendly_name` of the Worker that is reserved. + :ivar worker_sid: The SID of the reserved Worker resource. + :ivar workspace_sid: The SID of the Workspace that this task is contained within. + :ivar url: The absolute URL of the TaskReservation reservation. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + task_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.reservation_status: Optional["ReservationInstance.Status"] = payload.get( + "reservation_status" + ) + self.sid: Optional[str] = payload.get("sid") + self.task_sid: Optional[str] = payload.get("task_sid") + self.worker_name: Optional[str] = payload.get("worker_name") + self.worker_sid: Optional[str] = payload.get("worker_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "task_sid": task_sid, + "sid": sid or self.sid, + } + self._context: Optional[ReservationContext] = None + + @property + def _proxy(self) -> "ReservationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ReservationContext for this ReservationInstance + """ + if self._context is None: + self._context = ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ReservationInstance": + """ + Fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ReservationInstance": + """ + Asynchronous coroutine to fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + supervisor_mode: Union[ + "ReservationInstance.SupervisorMode", object + ] = values.unset, + supervisor: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> "ReservationInstance": + """ + Update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The Caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: Timeout for call when executing a Dequeue instruction. + :param dequeue_to: The Contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The Callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction or which leg to record. + :param call_timeout: Timeout for call when executing a Call instruction. + :param call_to: The Contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The Caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: Timeout for call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. The default is `false`. + :param muted: Whether the agent is muted in the conference. The default is `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. The default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: How to trim the leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The Call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param supervisor_mode: + :param supervisor: The Supervisor SID/URI when executing the Supervise instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + return self._proxy.update( + if_match=if_match, + reservation_status=reservation_status, + worker_activity_sid=worker_activity_sid, + instruction=instruction, + dequeue_post_work_activity_sid=dequeue_post_work_activity_sid, + dequeue_from=dequeue_from, + dequeue_record=dequeue_record, + dequeue_timeout=dequeue_timeout, + dequeue_to=dequeue_to, + dequeue_status_callback_url=dequeue_status_callback_url, + call_from=call_from, + call_record=call_record, + call_timeout=call_timeout, + call_to=call_to, + call_url=call_url, + call_status_callback_url=call_status_callback_url, + call_accept=call_accept, + redirect_call_sid=redirect_call_sid, + redirect_accept=redirect_accept, + redirect_url=redirect_url, + to=to, + from_=from_, + status_callback=status_callback, + status_callback_method=status_callback_method, + status_callback_event=status_callback_event, + timeout=timeout, + record=record, + muted=muted, + beep=beep, + start_conference_on_enter=start_conference_on_enter, + end_conference_on_exit=end_conference_on_exit, + wait_url=wait_url, + wait_method=wait_method, + early_media=early_media, + max_participants=max_participants, + conference_status_callback=conference_status_callback, + conference_status_callback_method=conference_status_callback_method, + conference_status_callback_event=conference_status_callback_event, + conference_record=conference_record, + conference_trim=conference_trim, + recording_channels=recording_channels, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + conference_recording_status_callback=conference_recording_status_callback, + conference_recording_status_callback_method=conference_recording_status_callback_method, + region=region, + sip_auth_username=sip_auth_username, + sip_auth_password=sip_auth_password, + dequeue_status_callback_event=dequeue_status_callback_event, + post_work_activity_sid=post_work_activity_sid, + supervisor_mode=supervisor_mode, + supervisor=supervisor, + end_conference_on_customer_exit=end_conference_on_customer_exit, + beep_on_customer_entrance=beep_on_customer_entrance, + jitter_buffer_size=jitter_buffer_size, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + supervisor_mode: Union[ + "ReservationInstance.SupervisorMode", object + ] = values.unset, + supervisor: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> "ReservationInstance": + """ + Asynchronous coroutine to update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The Caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: Timeout for call when executing a Dequeue instruction. + :param dequeue_to: The Contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The Callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction or which leg to record. + :param call_timeout: Timeout for call when executing a Call instruction. + :param call_to: The Contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The Caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: Timeout for call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. The default is `false`. + :param muted: Whether the agent is muted in the conference. The default is `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. The default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: How to trim the leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The Call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param supervisor_mode: + :param supervisor: The Supervisor SID/URI when executing the Supervise instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + return await self._proxy.update_async( + if_match=if_match, + reservation_status=reservation_status, + worker_activity_sid=worker_activity_sid, + instruction=instruction, + dequeue_post_work_activity_sid=dequeue_post_work_activity_sid, + dequeue_from=dequeue_from, + dequeue_record=dequeue_record, + dequeue_timeout=dequeue_timeout, + dequeue_to=dequeue_to, + dequeue_status_callback_url=dequeue_status_callback_url, + call_from=call_from, + call_record=call_record, + call_timeout=call_timeout, + call_to=call_to, + call_url=call_url, + call_status_callback_url=call_status_callback_url, + call_accept=call_accept, + redirect_call_sid=redirect_call_sid, + redirect_accept=redirect_accept, + redirect_url=redirect_url, + to=to, + from_=from_, + status_callback=status_callback, + status_callback_method=status_callback_method, + status_callback_event=status_callback_event, + timeout=timeout, + record=record, + muted=muted, + beep=beep, + start_conference_on_enter=start_conference_on_enter, + end_conference_on_exit=end_conference_on_exit, + wait_url=wait_url, + wait_method=wait_method, + early_media=early_media, + max_participants=max_participants, + conference_status_callback=conference_status_callback, + conference_status_callback_method=conference_status_callback_method, + conference_status_callback_event=conference_status_callback_event, + conference_record=conference_record, + conference_trim=conference_trim, + recording_channels=recording_channels, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + conference_recording_status_callback=conference_recording_status_callback, + conference_recording_status_callback_method=conference_recording_status_callback_method, + region=region, + sip_auth_username=sip_auth_username, + sip_auth_password=sip_auth_password, + dequeue_status_callback_event=dequeue_status_callback_event, + post_work_activity_sid=post_work_activity_sid, + supervisor_mode=supervisor_mode, + supervisor=supervisor, + end_conference_on_customer_exit=end_conference_on_customer_exit, + beep_on_customer_entrance=beep_on_customer_entrance, + jitter_buffer_size=jitter_buffer_size, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ReservationContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, task_sid: str, sid: str): + """ + Initialize the ReservationContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskReservation resources to update. + :param task_sid: The SID of the reserved Task resource with the TaskReservation resources to update. + :param sid: The SID of the TaskReservation resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_sid": task_sid, + "sid": sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/Tasks/{task_sid}/Reservations/{sid}".format( + **self._solution + ) + ) + + def fetch(self) -> ReservationInstance: + """ + Fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ReservationInstance: + """ + Asynchronous coroutine to fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + supervisor_mode: Union[ + "ReservationInstance.SupervisorMode", object + ] = values.unset, + supervisor: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> ReservationInstance: + """ + Update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The Caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: Timeout for call when executing a Dequeue instruction. + :param dequeue_to: The Contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The Callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction or which leg to record. + :param call_timeout: Timeout for call when executing a Call instruction. + :param call_to: The Contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The Caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: Timeout for call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. The default is `false`. + :param muted: Whether the agent is muted in the conference. The default is `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. The default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: How to trim the leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The Call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param supervisor_mode: + :param supervisor: The Supervisor SID/URI when executing the Supervise instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerActivitySid": worker_activity_sid, + "Instruction": instruction, + "DequeuePostWorkActivitySid": dequeue_post_work_activity_sid, + "DequeueFrom": dequeue_from, + "DequeueRecord": dequeue_record, + "DequeueTimeout": dequeue_timeout, + "DequeueTo": dequeue_to, + "DequeueStatusCallbackUrl": dequeue_status_callback_url, + "CallFrom": call_from, + "CallRecord": call_record, + "CallTimeout": call_timeout, + "CallTo": call_to, + "CallUrl": call_url, + "CallStatusCallbackUrl": call_status_callback_url, + "CallAccept": serialize.boolean_to_string(call_accept), + "RedirectCallSid": redirect_call_sid, + "RedirectAccept": serialize.boolean_to_string(redirect_accept), + "RedirectUrl": redirect_url, + "To": to, + "From": from_, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "Region": region, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "DequeueStatusCallbackEvent": serialize.map( + dequeue_status_callback_event, lambda e: e + ), + "PostWorkActivitySid": post_work_activity_sid, + "SupervisorMode": supervisor_mode, + "Supervisor": supervisor, + "EndConferenceOnCustomerExit": serialize.boolean_to_string( + end_conference_on_customer_exit + ), + "BeepOnCustomerEntrance": serialize.boolean_to_string( + beep_on_customer_entrance + ), + "JitterBufferSize": jitter_buffer_size, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + supervisor_mode: Union[ + "ReservationInstance.SupervisorMode", object + ] = values.unset, + supervisor: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> ReservationInstance: + """ + Asynchronous coroutine to update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The Caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: Timeout for call when executing a Dequeue instruction. + :param dequeue_to: The Contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The Callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction or which leg to record. + :param call_timeout: Timeout for call when executing a Call instruction. + :param call_to: The Contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The Caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: Timeout for call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. The default is `false`. + :param muted: Whether the agent is muted in the conference. The default is `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. The default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: How to trim the leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The Call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param supervisor_mode: + :param supervisor: The Supervisor SID/URI when executing the Supervise instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerActivitySid": worker_activity_sid, + "Instruction": instruction, + "DequeuePostWorkActivitySid": dequeue_post_work_activity_sid, + "DequeueFrom": dequeue_from, + "DequeueRecord": dequeue_record, + "DequeueTimeout": dequeue_timeout, + "DequeueTo": dequeue_to, + "DequeueStatusCallbackUrl": dequeue_status_callback_url, + "CallFrom": call_from, + "CallRecord": call_record, + "CallTimeout": call_timeout, + "CallTo": call_to, + "CallUrl": call_url, + "CallStatusCallbackUrl": call_status_callback_url, + "CallAccept": serialize.boolean_to_string(call_accept), + "RedirectCallSid": redirect_call_sid, + "RedirectAccept": serialize.boolean_to_string(redirect_accept), + "RedirectUrl": redirect_url, + "To": to, + "From": from_, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "Region": region, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "DequeueStatusCallbackEvent": serialize.map( + dequeue_status_callback_event, lambda e: e + ), + "PostWorkActivitySid": post_work_activity_sid, + "SupervisorMode": supervisor_mode, + "Supervisor": supervisor, + "EndConferenceOnCustomerExit": serialize.boolean_to_string( + end_conference_on_customer_exit + ), + "BeepOnCustomerEntrance": serialize.boolean_to_string( + beep_on_customer_entrance + ), + "JitterBufferSize": jitter_buffer_size, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ReservationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ReservationInstance: + """ + Build an instance of ReservationInstance + + :param payload: Payload response from the API + """ + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ReservationList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, task_sid: str): + """ + Initialize the ReservationList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskReservation resources to read. + :param task_sid: The SID of the reserved Task resource with the TaskReservation resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_sid": task_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Tasks/{task_sid}/Reservations".format( + **self._solution + ) + + def stream( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ReservationInstance]: + """ + Streams ReservationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param str worker_sid: The SID of the reserved Worker resource to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + reservation_status=reservation_status, + worker_sid=worker_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ReservationInstance]: + """ + Asynchronously streams ReservationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param str worker_sid: The SID of the reserved Worker resource to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + reservation_status=reservation_status, + worker_sid=worker_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ReservationInstance]: + """ + Lists ReservationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param str worker_sid: The SID of the reserved Worker resource to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + reservation_status=reservation_status, + worker_sid=worker_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ReservationInstance]: + """ + Asynchronously lists ReservationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param str worker_sid: The SID of the reserved Worker resource to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + reservation_status=reservation_status, + worker_sid=worker_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ReservationPage: + """ + Retrieve a single page of ReservationInstance records from the API. + Request is executed immediately + + :param reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param worker_sid: The SID of the reserved Worker resource to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerSid": worker_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ReservationPage(self._version, response, self._solution) + + async def page_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ReservationPage: + """ + Asynchronously retrieve a single page of ReservationInstance records from the API. + Request is executed immediately + + :param reservation_status: Returns the list of reservations for a task with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, or `timeout`. + :param worker_sid: The SID of the reserved Worker resource to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerSid": worker_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ReservationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ReservationPage: + """ + Retrieve a specific page of ReservationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ReservationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ReservationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ReservationPage: + """ + Asynchronously retrieve a specific page of ReservationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ReservationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ReservationPage(self._version, response, self._solution) + + def get(self, sid: str) -> ReservationContext: + """ + Constructs a ReservationContext + + :param sid: The SID of the TaskReservation resource to update. + """ + return ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ReservationContext: + """ + Constructs a ReservationContext + + :param sid: The SID of the TaskReservation resource to update. + """ + return ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_sid=self._solution["task_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_channel.py new file mode 100644 index 0000000..099f85e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_channel.py @@ -0,0 +1,654 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TaskChannelInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Task Channel resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar sid: The unique string that we created to identify the Task Channel resource. + :ivar unique_name: An application-defined string that uniquely identifies the Task Channel, such as `voice` or `sms`. + :ivar workspace_sid: The SID of the Workspace that contains the Task Channel. + :ivar channel_optimized_routing: Whether the Task Channel will prioritize Workers that have been idle. When `true`, Workers that have been idle the longest are prioritized. + :ivar url: The absolute URL of the Task Channel resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.channel_optimized_routing: Optional[bool] = payload.get( + "channel_optimized_routing" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[TaskChannelContext] = None + + @property + def _proxy(self) -> "TaskChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskChannelContext for this TaskChannelInstance + """ + if self._context is None: + self._context = TaskChannelContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TaskChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TaskChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TaskChannelInstance": + """ + Fetch the TaskChannelInstance + + + :returns: The fetched TaskChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TaskChannelInstance": + """ + Asynchronous coroutine to fetch the TaskChannelInstance + + + :returns: The fetched TaskChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> "TaskChannelInstance": + """ + Update the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param channel_optimized_routing: Whether the TaskChannel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The updated TaskChannelInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + channel_optimized_routing=channel_optimized_routing, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> "TaskChannelInstance": + """ + Asynchronous coroutine to update the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param channel_optimized_routing: Whether the TaskChannel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The updated TaskChannelInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + channel_optimized_routing=channel_optimized_routing, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskChannelContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the TaskChannelContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Task Channel to update. + :param sid: The SID of the Task Channel resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskChannels/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TaskChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TaskChannelInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TaskChannelInstance: + """ + Fetch the TaskChannelInstance + + + :returns: The fetched TaskChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TaskChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TaskChannelInstance: + """ + Asynchronous coroutine to fetch the TaskChannelInstance + + + :returns: The fetched TaskChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TaskChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> TaskChannelInstance: + """ + Update the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param channel_optimized_routing: Whether the TaskChannel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The updated TaskChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ChannelOptimizedRouting": serialize.boolean_to_string( + channel_optimized_routing + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> TaskChannelInstance: + """ + Asynchronous coroutine to update the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param channel_optimized_routing: Whether the TaskChannel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The updated TaskChannelInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "ChannelOptimizedRouting": serialize.boolean_to_string( + channel_optimized_routing + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TaskChannelInstance: + """ + Build an instance of TaskChannelInstance + + :param payload: Payload response from the API + """ + return TaskChannelInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TaskChannelList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the TaskChannelList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Task Channel to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskChannels".format(**self._solution) + + def create( + self, + friendly_name: str, + unique_name: str, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> TaskChannelInstance: + """ + Create the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the Task Channel, such as `voice` or `sms`. + :param channel_optimized_routing: Whether the Task Channel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The created TaskChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "ChannelOptimizedRouting": serialize.boolean_to_string( + channel_optimized_routing + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskChannelInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, + friendly_name: str, + unique_name: str, + channel_optimized_routing: Union[bool, object] = values.unset, + ) -> TaskChannelInstance: + """ + Asynchronously create the TaskChannelInstance + + :param friendly_name: A descriptive string that you create to describe the Task Channel. It can be up to 64 characters long. + :param unique_name: An application-defined string that uniquely identifies the Task Channel, such as `voice` or `sms`. + :param channel_optimized_routing: Whether the Task Channel should prioritize Workers that have been idle. If `true`, Workers that have been idle the longest are prioritized. + + :returns: The created TaskChannelInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "UniqueName": unique_name, + "ChannelOptimizedRouting": serialize.boolean_to_string( + channel_optimized_routing + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskChannelInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TaskChannelInstance]: + """ + Streams TaskChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TaskChannelInstance]: + """ + Asynchronously streams TaskChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskChannelInstance]: + """ + Lists TaskChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskChannelInstance]: + """ + Asynchronously lists TaskChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskChannelPage: + """ + Retrieve a single page of TaskChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TaskChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskChannelPage: + """ + Asynchronously retrieve a single page of TaskChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TaskChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TaskChannelPage: + """ + Retrieve a specific page of TaskChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TaskChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TaskChannelPage: + """ + Asynchronously retrieve a specific page of TaskChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TaskChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> TaskChannelContext: + """ + Constructs a TaskChannelContext + + :param sid: The SID of the Task Channel resource to update. + """ + return TaskChannelContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> TaskChannelContext: + """ + Constructs a TaskChannelContext + + :param sid: The SID of the Task Channel resource to update. + """ + return TaskChannelContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__init__.py new file mode 100644 index 0000000..794fd21 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__init__.py @@ -0,0 +1,919 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.taskrouter.v1.workspace.task_queue.task_queue_bulk_real_time_statistics import ( + TaskQueueBulkRealTimeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.task_queue.task_queue_cumulative_statistics import ( + TaskQueueCumulativeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.task_queue.task_queue_real_time_statistics import ( + TaskQueueRealTimeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.task_queue.task_queue_statistics import ( + TaskQueueStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.task_queue.task_queues_statistics import ( + TaskQueuesStatisticsList, +) + + +class TaskQueueInstance(InstanceResource): + + class TaskOrder(object): + FIFO = "FIFO" + LIFO = "LIFO" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned for them. + :ivar assignment_activity_name: The name of the Activity to assign Workers when a task is assigned for them. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar max_reserved_workers: The maximum number of Workers to reserve for the assignment of a task in the queue. Can be an integer between 1 and 50, inclusive and defaults to 1. + :ivar reservation_activity_sid: The SID of the Activity to assign Workers once a task is reserved for them. + :ivar reservation_activity_name: The name of the Activity to assign Workers once a task is reserved for them. + :ivar sid: The unique string that we created to identify the TaskQueue resource. + :ivar target_workers: A string describing the Worker selection criteria for any Tasks that enter the TaskQueue. For example `'\"language\" == \"spanish\"'` If no TargetWorkers parameter is provided, Tasks will wait in the TaskQueue until they are either deleted or moved to another TaskQueue. Additional examples on how to describing Worker selection criteria below. Defaults to 1==1. + :ivar task_order: + :ivar url: The absolute URL of the TaskQueue resource. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueue. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.assignment_activity_sid: Optional[str] = payload.get( + "assignment_activity_sid" + ) + self.assignment_activity_name: Optional[str] = payload.get( + "assignment_activity_name" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.max_reserved_workers: Optional[int] = deserialize.integer( + payload.get("max_reserved_workers") + ) + self.reservation_activity_sid: Optional[str] = payload.get( + "reservation_activity_sid" + ) + self.reservation_activity_name: Optional[str] = payload.get( + "reservation_activity_name" + ) + self.sid: Optional[str] = payload.get("sid") + self.target_workers: Optional[str] = payload.get("target_workers") + self.task_order: Optional["TaskQueueInstance.TaskOrder"] = payload.get( + "task_order" + ) + self.url: Optional[str] = payload.get("url") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[TaskQueueContext] = None + + @property + def _proxy(self) -> "TaskQueueContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskQueueContext for this TaskQueueInstance + """ + if self._context is None: + self._context = TaskQueueContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TaskQueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TaskQueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TaskQueueInstance": + """ + Fetch the TaskQueueInstance + + + :returns: The fetched TaskQueueInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TaskQueueInstance": + """ + Asynchronous coroutine to fetch the TaskQueueInstance + + + :returns: The fetched TaskQueueInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + target_workers: Union[str, object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + ) -> "TaskQueueInstance": + """ + Update the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string describing the Worker selection criteria for any Tasks that enter the TaskQueue. For example '\\\"language\\\" == \\\"spanish\\\"' If no TargetWorkers parameter is provided, Tasks will wait in the queue until they are either deleted or moved to another queue. Additional examples on how to describing Worker selection criteria below. + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned for them. + :param max_reserved_workers: The maximum number of Workers to create reservations for the assignment of a task while in the queue. Maximum of 50. + :param task_order: + + :returns: The updated TaskQueueInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + target_workers=target_workers, + reservation_activity_sid=reservation_activity_sid, + assignment_activity_sid=assignment_activity_sid, + max_reserved_workers=max_reserved_workers, + task_order=task_order, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + target_workers: Union[str, object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + ) -> "TaskQueueInstance": + """ + Asynchronous coroutine to update the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string describing the Worker selection criteria for any Tasks that enter the TaskQueue. For example '\\\"language\\\" == \\\"spanish\\\"' If no TargetWorkers parameter is provided, Tasks will wait in the queue until they are either deleted or moved to another queue. Additional examples on how to describing Worker selection criteria below. + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned for them. + :param max_reserved_workers: The maximum number of Workers to create reservations for the assignment of a task while in the queue. Maximum of 50. + :param task_order: + + :returns: The updated TaskQueueInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + target_workers=target_workers, + reservation_activity_sid=reservation_activity_sid, + assignment_activity_sid=assignment_activity_sid, + max_reserved_workers=max_reserved_workers, + task_order=task_order, + ) + + @property + def cumulative_statistics(self) -> TaskQueueCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + return self._proxy.cumulative_statistics + + @property + def real_time_statistics(self) -> TaskQueueRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + return self._proxy.real_time_statistics + + @property + def statistics(self) -> TaskQueueStatisticsList: + """ + Access the statistics + """ + return self._proxy.statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskQueueContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the TaskQueueContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to update. + :param sid: The SID of the TaskQueue resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues/{sid}".format( + **self._solution + ) + + self._cumulative_statistics: Optional[TaskQueueCumulativeStatisticsList] = None + self._real_time_statistics: Optional[TaskQueueRealTimeStatisticsList] = None + self._statistics: Optional[TaskQueueStatisticsList] = None + + def delete(self) -> bool: + """ + Deletes the TaskQueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TaskQueueInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TaskQueueInstance: + """ + Fetch the TaskQueueInstance + + + :returns: The fetched TaskQueueInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TaskQueueInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TaskQueueInstance: + """ + Asynchronous coroutine to fetch the TaskQueueInstance + + + :returns: The fetched TaskQueueInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TaskQueueInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + target_workers: Union[str, object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + ) -> TaskQueueInstance: + """ + Update the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string describing the Worker selection criteria for any Tasks that enter the TaskQueue. For example '\\\"language\\\" == \\\"spanish\\\"' If no TargetWorkers parameter is provided, Tasks will wait in the queue until they are either deleted or moved to another queue. Additional examples on how to describing Worker selection criteria below. + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned for them. + :param max_reserved_workers: The maximum number of Workers to create reservations for the assignment of a task while in the queue. Maximum of 50. + :param task_order: + + :returns: The updated TaskQueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "TargetWorkers": target_workers, + "ReservationActivitySid": reservation_activity_sid, + "AssignmentActivitySid": assignment_activity_sid, + "MaxReservedWorkers": max_reserved_workers, + "TaskOrder": task_order, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskQueueInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + target_workers: Union[str, object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + ) -> TaskQueueInstance: + """ + Asynchronous coroutine to update the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string describing the Worker selection criteria for any Tasks that enter the TaskQueue. For example '\\\"language\\\" == \\\"spanish\\\"' If no TargetWorkers parameter is provided, Tasks will wait in the queue until they are either deleted or moved to another queue. Additional examples on how to describing Worker selection criteria below. + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned for them. + :param max_reserved_workers: The maximum number of Workers to create reservations for the assignment of a task while in the queue. Maximum of 50. + :param task_order: + + :returns: The updated TaskQueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "TargetWorkers": target_workers, + "ReservationActivitySid": reservation_activity_sid, + "AssignmentActivitySid": assignment_activity_sid, + "MaxReservedWorkers": max_reserved_workers, + "TaskOrder": task_order, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskQueueInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + @property + def cumulative_statistics(self) -> TaskQueueCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + if self._cumulative_statistics is None: + self._cumulative_statistics = TaskQueueCumulativeStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._cumulative_statistics + + @property + def real_time_statistics(self) -> TaskQueueRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + if self._real_time_statistics is None: + self._real_time_statistics = TaskQueueRealTimeStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._real_time_statistics + + @property + def statistics(self) -> TaskQueueStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = TaskQueueStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskQueuePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TaskQueueInstance: + """ + Build an instance of TaskQueueInstance + + :param payload: Payload response from the API + """ + return TaskQueueInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TaskQueueList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the TaskQueueList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues".format(**self._solution) + + self._bulk_real_time_statistics: Optional[ + TaskQueueBulkRealTimeStatisticsList + ] = None + self._statistics: Optional[TaskQueuesStatisticsList] = None + + def create( + self, + friendly_name: str, + target_workers: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + ) -> TaskQueueInstance: + """ + Create the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string that describes the Worker selection criteria for any Tasks that enter the TaskQueue. For example, `'\\\"language\\\" == \\\"spanish\\\"'`. The default value is `1==1`. If this value is empty, Tasks will wait in the TaskQueue until they are deleted or moved to another TaskQueue. For more information about Worker selection, see [Describing Worker selection criteria](https://www.twilio.com/docs/taskrouter/api/taskqueues#target-workers). + :param max_reserved_workers: The maximum number of Workers to reserve for the assignment of a Task in the queue. Can be an integer between 1 and 50, inclusive and defaults to 1. + :param task_order: + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned to them. + + :returns: The created TaskQueueInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "TargetWorkers": target_workers, + "MaxReservedWorkers": max_reserved_workers, + "TaskOrder": task_order, + "ReservationActivitySid": reservation_activity_sid, + "AssignmentActivitySid": assignment_activity_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskQueueInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, + friendly_name: str, + target_workers: Union[str, object] = values.unset, + max_reserved_workers: Union[int, object] = values.unset, + task_order: Union["TaskQueueInstance.TaskOrder", object] = values.unset, + reservation_activity_sid: Union[str, object] = values.unset, + assignment_activity_sid: Union[str, object] = values.unset, + ) -> TaskQueueInstance: + """ + Asynchronously create the TaskQueueInstance + + :param friendly_name: A descriptive string that you create to describe the TaskQueue. For example `Support-Tier 1`, `Sales`, or `Escalation`. + :param target_workers: A string that describes the Worker selection criteria for any Tasks that enter the TaskQueue. For example, `'\\\"language\\\" == \\\"spanish\\\"'`. The default value is `1==1`. If this value is empty, Tasks will wait in the TaskQueue until they are deleted or moved to another TaskQueue. For more information about Worker selection, see [Describing Worker selection criteria](https://www.twilio.com/docs/taskrouter/api/taskqueues#target-workers). + :param max_reserved_workers: The maximum number of Workers to reserve for the assignment of a Task in the queue. Can be an integer between 1 and 50, inclusive and defaults to 1. + :param task_order: + :param reservation_activity_sid: The SID of the Activity to assign Workers when a task is reserved for them. + :param assignment_activity_sid: The SID of the Activity to assign Workers when a task is assigned to them. + + :returns: The created TaskQueueInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "TargetWorkers": target_workers, + "MaxReservedWorkers": max_reserved_workers, + "TaskOrder": task_order, + "ReservationActivitySid": reservation_activity_sid, + "AssignmentActivitySid": assignment_activity_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TaskQueueInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TaskQueueInstance]: + """ + Streams TaskQueueInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param str evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param str worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param str ordering: Sorting parameter for TaskQueues + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + friendly_name=friendly_name, + evaluate_worker_attributes=evaluate_worker_attributes, + worker_sid=worker_sid, + ordering=ordering, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TaskQueueInstance]: + """ + Asynchronously streams TaskQueueInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param str evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param str worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param str ordering: Sorting parameter for TaskQueues + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, + evaluate_worker_attributes=evaluate_worker_attributes, + worker_sid=worker_sid, + ordering=ordering, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskQueueInstance]: + """ + Lists TaskQueueInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param str evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param str worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param str ordering: Sorting parameter for TaskQueues + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + evaluate_worker_attributes=evaluate_worker_attributes, + worker_sid=worker_sid, + ordering=ordering, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskQueueInstance]: + """ + Asynchronously lists TaskQueueInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param str evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param str worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param str ordering: Sorting parameter for TaskQueues + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + evaluate_worker_attributes=evaluate_worker_attributes, + worker_sid=worker_sid, + ordering=ordering, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskQueuePage: + """ + Retrieve a single page of TaskQueueInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param ordering: Sorting parameter for TaskQueues + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskQueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "EvaluateWorkerAttributes": evaluate_worker_attributes, + "WorkerSid": worker_sid, + "Ordering": ordering, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TaskQueuePage(self._version, response, self._solution) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + evaluate_worker_attributes: Union[str, object] = values.unset, + worker_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskQueuePage: + """ + Asynchronously retrieve a single page of TaskQueueInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the TaskQueue resources to read. + :param evaluate_worker_attributes: The attributes of the Workers to read. Returns the TaskQueues with Workers that match the attributes specified in this parameter. + :param worker_sid: The SID of the Worker with the TaskQueue resources to read. + :param ordering: Sorting parameter for TaskQueues + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskQueueInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "EvaluateWorkerAttributes": evaluate_worker_attributes, + "WorkerSid": worker_sid, + "Ordering": ordering, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TaskQueuePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TaskQueuePage: + """ + Retrieve a specific page of TaskQueueInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskQueueInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TaskQueuePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TaskQueuePage: + """ + Asynchronously retrieve a specific page of TaskQueueInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskQueueInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TaskQueuePage(self._version, response, self._solution) + + @property + def bulk_real_time_statistics(self) -> TaskQueueBulkRealTimeStatisticsList: + """ + Access the bulk_real_time_statistics + """ + if self._bulk_real_time_statistics is None: + self._bulk_real_time_statistics = TaskQueueBulkRealTimeStatisticsList( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + return self._bulk_real_time_statistics + + @property + def statistics(self) -> TaskQueuesStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = TaskQueuesStatisticsList( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + return self._statistics + + def get(self, sid: str) -> TaskQueueContext: + """ + Constructs a TaskQueueContext + + :param sid: The SID of the TaskQueue resource to update. + """ + return TaskQueueContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> TaskQueueContext: + """ + Constructs a TaskQueueContext + + :param sid: The SID of the TaskQueue resource to update. + """ + return TaskQueueContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1d16c9d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_bulk_real_time_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_bulk_real_time_statistics.cpython-310.pyc new file mode 100644 index 0000000..c38a952 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_bulk_real_time_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_cumulative_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_cumulative_statistics.cpython-310.pyc new file mode 100644 index 0000000..ecb11c6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_cumulative_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_real_time_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_real_time_statistics.cpython-310.pyc new file mode 100644 index 0000000..4f987f7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_real_time_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_statistics.cpython-310.pyc new file mode 100644 index 0000000..86f5e52 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queue_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queues_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queues_statistics.cpython-310.pyc new file mode 100644 index 0000000..c1fea15 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/__pycache__/task_queues_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_bulk_real_time_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_bulk_real_time_statistics.py new file mode 100644 index 0000000..a1c44b1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_bulk_real_time_statistics.py @@ -0,0 +1,133 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TaskQueueBulkRealTimeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueue. + :ivar task_queue_data: The real-time statistics for each requested TaskQueue SID. `task_queue_data` returns the following attributes: `task_queue_sid`: The SID of the TaskQueue from which these statistics were calculated. `total_available_workers`: The total number of Workers available for Tasks in the TaskQueue. `total_eligible_workers`: The total number of Workers eligible for Tasks in the TaskQueue, regardless of their Activity state. `total_tasks`: The total number of Tasks. `longest_task_waiting_age`: The age of the longest waiting Task. `longest_task_waiting_sid`: The SID of the longest waiting Task. `tasks_by_status`: The number of Tasks grouped by their current status. `tasks_by_priority`: The number of Tasks grouped by priority. `activity_statistics`: The number of current Workers grouped by Activity. + :ivar task_queue_response_count: The number of TaskQueue statistics received in task_queue_data. + :ivar url: The absolute URL of the TaskQueue statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.task_queue_data: Optional[List[Dict[str, object]]] = payload.get( + "task_queue_data" + ) + self.task_queue_response_count: Optional[int] = deserialize.integer( + payload.get("task_queue_response_count") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return ( + "".format( + context + ) + ) + + +class TaskQueueBulkRealTimeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the TaskQueueBulkRealTimeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The unique SID identifier of the Workspace. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues/RealTimeStatistics".format( + **self._solution + ) + + def create( + self, body: Union[object, object] = values.unset + ) -> TaskQueueBulkRealTimeStatisticsInstance: + """ + Create the TaskQueueBulkRealTimeStatisticsInstance + + :param body: + + :returns: The created TaskQueueBulkRealTimeStatisticsInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = self._version.create( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return TaskQueueBulkRealTimeStatisticsInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, body: Union[object, object] = values.unset + ) -> TaskQueueBulkRealTimeStatisticsInstance: + """ + Asynchronously create the TaskQueueBulkRealTimeStatisticsInstance + + :param body: + + :returns: The created TaskQueueBulkRealTimeStatisticsInstance + """ + data = body.to_dict() + + headers = {"Content-Type": "application/json"} + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return TaskQueueBulkRealTimeStatisticsInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_cumulative_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_cumulative_statistics.py new file mode 100644 index 0000000..acb8b79 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_cumulative_statistics.py @@ -0,0 +1,366 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TaskQueueCumulativeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar avg_task_acceptance_time: The average time in seconds between Task creation and acceptance. + :ivar start_time: The beginning of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar end_time: The end of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar reservations_created: The total number of Reservations created for Tasks in the TaskQueue. + :ivar reservations_accepted: The total number of Reservations accepted for Tasks in the TaskQueue. + :ivar reservations_rejected: The total number of Reservations rejected for Tasks in the TaskQueue. + :ivar reservations_timed_out: The total number of Reservations that timed out for Tasks in the TaskQueue. + :ivar reservations_canceled: The total number of Reservations canceled for Tasks in the TaskQueue. + :ivar reservations_rescinded: The total number of Reservations rescinded. + :ivar split_by_wait_time: A list of objects that describe the number of Tasks canceled and reservations accepted above and below the thresholds specified in seconds. + :ivar task_queue_sid: The SID of the TaskQueue from which these statistics were calculated. + :ivar wait_duration_until_accepted: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks accepted while in the TaskQueue. Calculation is based on the time when the Tasks were created. For transfers, the wait duration is counted from the moment ***the Task was created***, and not from when the transfer was initiated. + :ivar wait_duration_until_canceled: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks canceled while in the TaskQueue. + :ivar wait_duration_in_queue_until_accepted: The relative wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks accepted while in the TaskQueue. Calculation is based on the time when the Tasks entered the TaskQueue. + :ivar tasks_canceled: The total number of Tasks canceled in the TaskQueue. + :ivar tasks_completed: The total number of Tasks completed in the TaskQueue. + :ivar tasks_deleted: The total number of Tasks deleted in the TaskQueue. + :ivar tasks_entered: The total number of Tasks entered into the TaskQueue. + :ivar tasks_moved: The total number of Tasks that were moved from one queue to another. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueue. + :ivar url: The absolute URL of the TaskQueue statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + task_queue_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.avg_task_acceptance_time: Optional[int] = deserialize.integer( + payload.get("avg_task_acceptance_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.reservations_created: Optional[int] = deserialize.integer( + payload.get("reservations_created") + ) + self.reservations_accepted: Optional[int] = deserialize.integer( + payload.get("reservations_accepted") + ) + self.reservations_rejected: Optional[int] = deserialize.integer( + payload.get("reservations_rejected") + ) + self.reservations_timed_out: Optional[int] = deserialize.integer( + payload.get("reservations_timed_out") + ) + self.reservations_canceled: Optional[int] = deserialize.integer( + payload.get("reservations_canceled") + ) + self.reservations_rescinded: Optional[int] = deserialize.integer( + payload.get("reservations_rescinded") + ) + self.split_by_wait_time: Optional[Dict[str, object]] = payload.get( + "split_by_wait_time" + ) + self.task_queue_sid: Optional[str] = payload.get("task_queue_sid") + self.wait_duration_until_accepted: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_accepted" + ) + self.wait_duration_until_canceled: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_canceled" + ) + self.wait_duration_in_queue_until_accepted: Optional[Dict[str, object]] = ( + payload.get("wait_duration_in_queue_until_accepted") + ) + self.tasks_canceled: Optional[int] = deserialize.integer( + payload.get("tasks_canceled") + ) + self.tasks_completed: Optional[int] = deserialize.integer( + payload.get("tasks_completed") + ) + self.tasks_deleted: Optional[int] = deserialize.integer( + payload.get("tasks_deleted") + ) + self.tasks_entered: Optional[int] = deserialize.integer( + payload.get("tasks_entered") + ) + self.tasks_moved: Optional[int] = deserialize.integer( + payload.get("tasks_moved") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._context: Optional[TaskQueueCumulativeStatisticsContext] = None + + @property + def _proxy(self) -> "TaskQueueCumulativeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskQueueCumulativeStatisticsContext for this TaskQueueCumulativeStatisticsInstance + """ + if self._context is None: + self._context = TaskQueueCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + return self._context + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "TaskQueueCumulativeStatisticsInstance": + """ + Fetch the TaskQueueCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. TaskRouter will calculate statistics on up to 10,000 Tasks/Reservations for any given threshold. + + :returns: The fetched TaskQueueCumulativeStatisticsInstance + """ + return self._proxy.fetch( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "TaskQueueCumulativeStatisticsInstance": + """ + Asynchronous coroutine to fetch the TaskQueueCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. TaskRouter will calculate statistics on up to 10,000 Tasks/Reservations for any given threshold. + + :returns: The fetched TaskQueueCumulativeStatisticsInstance + """ + return await self._proxy.fetch_async( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TaskQueueCumulativeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueCumulativeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues/{task_queue_sid}/CumulativeStatistics".format( + **self._solution + ) + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> TaskQueueCumulativeStatisticsInstance: + """ + Fetch the TaskQueueCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. TaskRouter will calculate statistics on up to 10,000 Tasks/Reservations for any given threshold. + + :returns: The fetched TaskQueueCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return TaskQueueCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> TaskQueueCumulativeStatisticsInstance: + """ + Asynchronous coroutine to fetch the TaskQueueCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. TaskRouter will calculate statistics on up to 10,000 Tasks/Reservations for any given threshold. + + :returns: The fetched TaskQueueCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return TaskQueueCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TaskQueueCumulativeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueCumulativeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + + def get(self) -> TaskQueueCumulativeStatisticsContext: + """ + Constructs a TaskQueueCumulativeStatisticsContext + + """ + return TaskQueueCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __call__(self) -> TaskQueueCumulativeStatisticsContext: + """ + Constructs a TaskQueueCumulativeStatisticsContext + + """ + return TaskQueueCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_real_time_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_real_time_statistics.py new file mode 100644 index 0000000..faca804 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_real_time_statistics.py @@ -0,0 +1,281 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TaskQueueRealTimeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar activity_statistics: The number of current Workers by Activity. + :ivar longest_task_waiting_age: The age of the longest waiting Task. + :ivar longest_task_waiting_sid: The SID of the longest waiting Task. + :ivar longest_relative_task_age_in_queue: The relative age in the TaskQueue for the longest waiting Task. Calculation is based on the time when the Task entered the TaskQueue. + :ivar longest_relative_task_sid_in_queue: The Task SID of the Task waiting in the TaskQueue the longest. Calculation is based on the time when the Task entered the TaskQueue. + :ivar task_queue_sid: The SID of the TaskQueue from which these statistics were calculated. + :ivar tasks_by_priority: The number of Tasks by priority. For example: `{\"0\": \"10\", \"99\": \"5\"}` shows 10 Tasks at priority 0 and 5 at priority 99. + :ivar tasks_by_status: The number of Tasks by their current status. For example: `{\"pending\": \"1\", \"reserved\": \"3\", \"assigned\": \"2\", \"completed\": \"5\"}`. + :ivar total_available_workers: The total number of Workers available for Tasks in the TaskQueue. + :ivar total_eligible_workers: The total number of Workers eligible for Tasks in the TaskQueue, independent of their Activity state. + :ivar total_tasks: The total number of Tasks. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueue. + :ivar url: The absolute URL of the TaskQueue statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + task_queue_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.activity_statistics: Optional[List[Dict[str, object]]] = payload.get( + "activity_statistics" + ) + self.longest_task_waiting_age: Optional[int] = deserialize.integer( + payload.get("longest_task_waiting_age") + ) + self.longest_task_waiting_sid: Optional[str] = payload.get( + "longest_task_waiting_sid" + ) + self.longest_relative_task_age_in_queue: Optional[int] = deserialize.integer( + payload.get("longest_relative_task_age_in_queue") + ) + self.longest_relative_task_sid_in_queue: Optional[str] = payload.get( + "longest_relative_task_sid_in_queue" + ) + self.task_queue_sid: Optional[str] = payload.get("task_queue_sid") + self.tasks_by_priority: Optional[Dict[str, object]] = payload.get( + "tasks_by_priority" + ) + self.tasks_by_status: Optional[Dict[str, object]] = payload.get( + "tasks_by_status" + ) + self.total_available_workers: Optional[int] = deserialize.integer( + payload.get("total_available_workers") + ) + self.total_eligible_workers: Optional[int] = deserialize.integer( + payload.get("total_eligible_workers") + ) + self.total_tasks: Optional[int] = deserialize.integer( + payload.get("total_tasks") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._context: Optional[TaskQueueRealTimeStatisticsContext] = None + + @property + def _proxy(self) -> "TaskQueueRealTimeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskQueueRealTimeStatisticsContext for this TaskQueueRealTimeStatisticsInstance + """ + if self._context is None: + self._context = TaskQueueRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + return self._context + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> "TaskQueueRealTimeStatisticsInstance": + """ + Fetch the TaskQueueRealTimeStatisticsInstance + + :param task_channel: The TaskChannel for which to fetch statistics. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched TaskQueueRealTimeStatisticsInstance + """ + return self._proxy.fetch( + task_channel=task_channel, + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> "TaskQueueRealTimeStatisticsInstance": + """ + Asynchronous coroutine to fetch the TaskQueueRealTimeStatisticsInstance + + :param task_channel: The TaskChannel for which to fetch statistics. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched TaskQueueRealTimeStatisticsInstance + """ + return await self._proxy.fetch_async( + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TaskQueueRealTimeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueRealTimeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues/{task_queue_sid}/RealTimeStatistics".format( + **self._solution + ) + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> TaskQueueRealTimeStatisticsInstance: + """ + Fetch the TaskQueueRealTimeStatisticsInstance + + :param task_channel: The TaskChannel for which to fetch statistics. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched TaskQueueRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return TaskQueueRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> TaskQueueRealTimeStatisticsInstance: + """ + Asynchronous coroutine to fetch the TaskQueueRealTimeStatisticsInstance + + :param task_channel: The TaskChannel for which to fetch statistics. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched TaskQueueRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return TaskQueueRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TaskQueueRealTimeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueRealTimeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + + def get(self) -> TaskQueueRealTimeStatisticsContext: + """ + Constructs a TaskQueueRealTimeStatisticsContext + + """ + return TaskQueueRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __call__(self) -> TaskQueueRealTimeStatisticsContext: + """ + Constructs a TaskQueueRealTimeStatisticsContext + + """ + return TaskQueueRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_statistics.py new file mode 100644 index 0000000..2a09f62 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queue_statistics.py @@ -0,0 +1,296 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class TaskQueueStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar cumulative: An object that contains the cumulative statistics for the TaskQueue. + :ivar realtime: An object that contains the real-time statistics for the TaskQueue. + :ivar task_queue_sid: The SID of the TaskQueue from which these statistics were calculated. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueue. + :ivar url: The absolute URL of the TaskQueue statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + task_queue_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.realtime: Optional[Dict[str, object]] = payload.get("realtime") + self.task_queue_sid: Optional[str] = payload.get("task_queue_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._context: Optional[TaskQueueStatisticsContext] = None + + @property + def _proxy(self) -> "TaskQueueStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TaskQueueStatisticsContext for this TaskQueueStatisticsInstance + """ + if self._context is None: + self._context = TaskQueueStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + return self._context + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "TaskQueueStatisticsInstance": + """ + Fetch the TaskQueueStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate real-time and cumulative statistics for the specified TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + + :returns: The fetched TaskQueueStatisticsInstance + """ + return self._proxy.fetch( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "TaskQueueStatisticsInstance": + """ + Asynchronous coroutine to fetch the TaskQueueStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate real-time and cumulative statistics for the specified TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + + :returns: The fetched TaskQueueStatisticsInstance + """ + return await self._proxy.fetch_async( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskQueueStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/TaskQueues/{task_queue_sid}/Statistics".format( + **self._solution + ) + ) + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> TaskQueueStatisticsInstance: + """ + Fetch the TaskQueueStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate real-time and cumulative statistics for the specified TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + + :returns: The fetched TaskQueueStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return TaskQueueStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> TaskQueueStatisticsInstance: + """ + Asynchronous coroutine to fetch the TaskQueueStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate real-time and cumulative statistics for the specified TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + + :returns: The fetched TaskQueueStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return TaskQueueStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskQueueStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, task_queue_sid: str): + """ + Initialize the TaskQueueStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueue to fetch. + :param task_queue_sid: The SID of the TaskQueue for which to fetch statistics. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "task_queue_sid": task_queue_sid, + } + + def get(self) -> TaskQueueStatisticsContext: + """ + Constructs a TaskQueueStatisticsContext + + """ + return TaskQueueStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __call__(self) -> TaskQueueStatisticsContext: + """ + Constructs a TaskQueueStatisticsContext + + """ + return TaskQueueStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + task_queue_sid=self._solution["task_queue_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queues_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queues_statistics.py new file mode 100644 index 0000000..bec2d81 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/task_queue/task_queues_statistics.py @@ -0,0 +1,399 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TaskQueuesStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the TaskQueue resource. + :ivar cumulative: An object that contains the cumulative statistics for the TaskQueues. + :ivar realtime: An object that contains the real-time statistics for the TaskQueues. + :ivar task_queue_sid: The SID of the TaskQueue from which these statistics were calculated. + :ivar workspace_sid: The SID of the Workspace that contains the TaskQueues. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.realtime: Optional[Dict[str, object]] = payload.get("realtime") + self.task_queue_sid: Optional[str] = payload.get("task_queue_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + + self._solution = { + "workspace_sid": workspace_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TaskQueuesStatisticsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TaskQueuesStatisticsInstance: + """ + Build an instance of TaskQueuesStatisticsInstance + + :param payload: Payload response from the API + """ + return TaskQueuesStatisticsInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TaskQueuesStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the TaskQueuesStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the TaskQueues to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/TaskQueues/Statistics".format( + **self._solution + ) + + def stream( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TaskQueuesStatisticsInstance]: + """ + Streams TaskQueuesStatisticsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param int minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param datetime start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param str task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param str split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + end_date=end_date, + friendly_name=friendly_name, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TaskQueuesStatisticsInstance]: + """ + Asynchronously streams TaskQueuesStatisticsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param int minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param datetime start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param str task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param str split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + end_date=end_date, + friendly_name=friendly_name, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskQueuesStatisticsInstance]: + """ + Lists TaskQueuesStatisticsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param int minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param datetime start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param str task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param str split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + end_date=end_date, + friendly_name=friendly_name, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TaskQueuesStatisticsInstance]: + """ + Asynchronously lists TaskQueuesStatisticsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param str friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param int minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param datetime start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param str task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param str split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + end_date=end_date, + friendly_name=friendly_name, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskQueuesStatisticsPage: + """ + Retrieve a single page of TaskQueuesStatisticsInstance records from the API. + Request is executed immediately + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskQueuesStatisticsInstance + """ + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "FriendlyName": friendly_name, + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TaskQueuesStatisticsPage(self._version, response, self._solution) + + async def page_async( + self, + end_date: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TaskQueuesStatisticsPage: + """ + Asynchronously retrieve a single page of TaskQueuesStatisticsInstance records from the API. + Request is executed immediately + + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param friendly_name: The `friendly_name` of the TaskQueue statistics to read. + :param minutes: Only calculate statistics since this many minutes in the past. The default is 15 minutes. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TaskQueuesStatisticsInstance + """ + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "FriendlyName": friendly_name, + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TaskQueuesStatisticsPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TaskQueuesStatisticsPage: + """ + Retrieve a specific page of TaskQueuesStatisticsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskQueuesStatisticsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TaskQueuesStatisticsPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TaskQueuesStatisticsPage: + """ + Asynchronously retrieve a specific page of TaskQueuesStatisticsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TaskQueuesStatisticsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TaskQueuesStatisticsPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__init__.py new file mode 100644 index 0000000..a236fca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__init__.py @@ -0,0 +1,973 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.taskrouter.v1.workspace.worker.reservation import ReservationList +from twilio.rest.taskrouter.v1.workspace.worker.worker_channel import WorkerChannelList +from twilio.rest.taskrouter.v1.workspace.worker.worker_statistics import ( + WorkerStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.worker.workers_cumulative_statistics import ( + WorkersCumulativeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.worker.workers_real_time_statistics import ( + WorkersRealTimeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.worker.workers_statistics import ( + WorkersStatisticsList, +) + + +class WorkerInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar activity_name: The `friendly_name` of the Worker's current Activity. + :ivar activity_sid: The SID of the Worker's current Activity. + :ivar attributes: The JSON string that describes the Worker. For example: `{ \"email\": \"Bob@example.com\", \"phone\": \"+5095551234\" }`. **Note** If this property has been assigned a value, it will only be displayed in FETCH actions that return a single resource. Otherwise, this property will be null, even if it has a value. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. + :ivar available: Whether the Worker is available to perform tasks. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_status_changed: The date and time in GMT of the last change to the Worker's activity specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Used to calculate Workflow statistics. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar friendly_name: The string that you assigned to describe the resource. Friendly names are case insensitive, and unique within the TaskRouter Workspace. + :ivar sid: The unique string that we created to identify the Worker resource. + :ivar workspace_sid: The SID of the Workspace that contains the Worker. + :ivar url: The absolute URL of the Worker resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.activity_name: Optional[str] = payload.get("activity_name") + self.activity_sid: Optional[str] = payload.get("activity_sid") + self.attributes: Optional[str] = payload.get("attributes") + self.available: Optional[bool] = payload.get("available") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_status_changed: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_status_changed") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[WorkerContext] = None + + @property + def _proxy(self) -> "WorkerContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkerContext for this WorkerInstance + """ + if self._context is None: + self._context = WorkerContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the WorkerInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete( + if_match=if_match, + ) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the WorkerInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async( + if_match=if_match, + ) + + def fetch(self) -> "WorkerInstance": + """ + Fetch the WorkerInstance + + + :returns: The fetched WorkerInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WorkerInstance": + """ + Asynchronous coroutine to fetch the WorkerInstance + + + :returns: The fetched WorkerInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reject_pending_reservations: Union[bool, object] = values.unset, + ) -> "WorkerInstance": + """ + Update the WorkerInstance + + :param if_match: The If-Match HTTP request header + :param activity_sid: The SID of a valid Activity that will describe the Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. + :param attributes: The JSON string that describes the Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + :param friendly_name: A descriptive string that you create to describe the Worker. It can be up to 64 characters long. + :param reject_pending_reservations: Whether to reject the Worker's pending reservations. This option is only valid if the Worker's new [Activity](https://www.twilio.com/docs/taskrouter/api/activity) resource has its `availability` property set to `False`. + + :returns: The updated WorkerInstance + """ + return self._proxy.update( + if_match=if_match, + activity_sid=activity_sid, + attributes=attributes, + friendly_name=friendly_name, + reject_pending_reservations=reject_pending_reservations, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reject_pending_reservations: Union[bool, object] = values.unset, + ) -> "WorkerInstance": + """ + Asynchronous coroutine to update the WorkerInstance + + :param if_match: The If-Match HTTP request header + :param activity_sid: The SID of a valid Activity that will describe the Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. + :param attributes: The JSON string that describes the Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + :param friendly_name: A descriptive string that you create to describe the Worker. It can be up to 64 characters long. + :param reject_pending_reservations: Whether to reject the Worker's pending reservations. This option is only valid if the Worker's new [Activity](https://www.twilio.com/docs/taskrouter/api/activity) resource has its `availability` property set to `False`. + + :returns: The updated WorkerInstance + """ + return await self._proxy.update_async( + if_match=if_match, + activity_sid=activity_sid, + attributes=attributes, + friendly_name=friendly_name, + reject_pending_reservations=reject_pending_reservations, + ) + + @property + def reservations(self) -> ReservationList: + """ + Access the reservations + """ + return self._proxy.reservations + + @property + def worker_channels(self) -> WorkerChannelList: + """ + Access the worker_channels + """ + return self._proxy.worker_channels + + @property + def statistics(self) -> WorkerStatisticsList: + """ + Access the statistics + """ + return self._proxy.statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the WorkerContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Worker to update. + :param sid: The SID of the Worker resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/{sid}".format(**self._solution) + + self._reservations: Optional[ReservationList] = None + self._worker_channels: Optional[WorkerChannelList] = None + self._statistics: Optional[WorkerStatisticsList] = None + + def delete(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Deletes the WorkerInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return self._version.delete(method="DELETE", uri=self._uri, headers=headers) + + async def delete_async(self, if_match: Union[str, object] = values.unset) -> bool: + """ + Asynchronous coroutine that deletes the WorkerInstance + + :param if_match: The If-Match HTTP request header + + :returns: True if delete succeeds, False otherwise + """ + headers = values.of( + { + "If-Match": if_match, + } + ) + + return await self._version.delete_async( + method="DELETE", uri=self._uri, headers=headers + ) + + def fetch(self) -> WorkerInstance: + """ + Fetch the WorkerInstance + + + :returns: The fetched WorkerInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WorkerInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WorkerInstance: + """ + Asynchronous coroutine to fetch the WorkerInstance + + + :returns: The fetched WorkerInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WorkerInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reject_pending_reservations: Union[bool, object] = values.unset, + ) -> WorkerInstance: + """ + Update the WorkerInstance + + :param if_match: The If-Match HTTP request header + :param activity_sid: The SID of a valid Activity that will describe the Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. + :param attributes: The JSON string that describes the Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + :param friendly_name: A descriptive string that you create to describe the Worker. It can be up to 64 characters long. + :param reject_pending_reservations: Whether to reject the Worker's pending reservations. This option is only valid if the Worker's new [Activity](https://www.twilio.com/docs/taskrouter/api/activity) resource has its `availability` property set to `False`. + + :returns: The updated WorkerInstance + """ + data = values.of( + { + "ActivitySid": activity_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + "RejectPendingReservations": serialize.boolean_to_string( + reject_pending_reservations + ), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return WorkerInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + reject_pending_reservations: Union[bool, object] = values.unset, + ) -> WorkerInstance: + """ + Asynchronous coroutine to update the WorkerInstance + + :param if_match: The If-Match HTTP request header + :param activity_sid: The SID of a valid Activity that will describe the Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. + :param attributes: The JSON string that describes the Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + :param friendly_name: A descriptive string that you create to describe the Worker. It can be up to 64 characters long. + :param reject_pending_reservations: Whether to reject the Worker's pending reservations. This option is only valid if the Worker's new [Activity](https://www.twilio.com/docs/taskrouter/api/activity) resource has its `availability` property set to `False`. + + :returns: The updated WorkerInstance + """ + data = values.of( + { + "ActivitySid": activity_sid, + "Attributes": attributes, + "FriendlyName": friendly_name, + "RejectPendingReservations": serialize.boolean_to_string( + reject_pending_reservations + ), + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return WorkerInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + @property + def reservations(self) -> ReservationList: + """ + Access the reservations + """ + if self._reservations is None: + self._reservations = ReservationList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._reservations + + @property + def worker_channels(self) -> WorkerChannelList: + """ + Access the worker_channels + """ + if self._worker_channels is None: + self._worker_channels = WorkerChannelList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._worker_channels + + @property + def statistics(self) -> WorkerStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = WorkerStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WorkerInstance: + """ + Build an instance of WorkerInstance + + :param payload: Payload response from the API + """ + return WorkerInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WorkerList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkerList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workers to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers".format(**self._solution) + + self._cumulative_statistics: Optional[WorkersCumulativeStatisticsList] = None + self._real_time_statistics: Optional[WorkersRealTimeStatisticsList] = None + self._statistics: Optional[WorkersStatisticsList] = None + + def create( + self, + friendly_name: str, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> WorkerInstance: + """ + Create the WorkerInstance + + :param friendly_name: A descriptive string that you create to describe the new Worker. It can be up to 64 characters long. + :param activity_sid: The SID of a valid Activity that will describe the new Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. If not provided, the new Worker's initial state is the `default_activity_sid` configured on the Workspace. + :param attributes: A valid JSON string that describes the new Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + + :returns: The created WorkerInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ActivitySid": activity_sid, + "Attributes": attributes, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkerInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, + friendly_name: str, + activity_sid: Union[str, object] = values.unset, + attributes: Union[str, object] = values.unset, + ) -> WorkerInstance: + """ + Asynchronously create the WorkerInstance + + :param friendly_name: A descriptive string that you create to describe the new Worker. It can be up to 64 characters long. + :param activity_sid: The SID of a valid Activity that will describe the new Worker's initial state. See [Activities](https://www.twilio.com/docs/taskrouter/api/activity) for more information. If not provided, the new Worker's initial state is the `default_activity_sid` configured on the Workspace. + :param attributes: A valid JSON string that describes the new Worker. For example: `{ \\\"email\\\": \\\"Bob@example.com\\\", \\\"phone\\\": \\\"+5095551234\\\" }`. This data is passed to the `assignment_callback_url` when TaskRouter assigns a Task to the Worker. Defaults to {}. + + :returns: The created WorkerInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "ActivitySid": activity_sid, + "Attributes": attributes, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkerInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WorkerInstance]: + """ + Streams WorkerInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str activity_name: The `activity_name` of the Worker resources to read. + :param str activity_sid: The `activity_sid` of the Worker resources to read. + :param str available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param str friendly_name: The `friendly_name` of the Worker resources to read. + :param str target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param str task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param str task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param str ordering: Sorting parameter for Workers + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + activity_name=activity_name, + activity_sid=activity_sid, + available=available, + friendly_name=friendly_name, + target_workers_expression=target_workers_expression, + task_queue_name=task_queue_name, + task_queue_sid=task_queue_sid, + ordering=ordering, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WorkerInstance]: + """ + Asynchronously streams WorkerInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str activity_name: The `activity_name` of the Worker resources to read. + :param str activity_sid: The `activity_sid` of the Worker resources to read. + :param str available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param str friendly_name: The `friendly_name` of the Worker resources to read. + :param str target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param str task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param str task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param str ordering: Sorting parameter for Workers + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + activity_name=activity_name, + activity_sid=activity_sid, + available=available, + friendly_name=friendly_name, + target_workers_expression=target_workers_expression, + task_queue_name=task_queue_name, + task_queue_sid=task_queue_sid, + ordering=ordering, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkerInstance]: + """ + Lists WorkerInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str activity_name: The `activity_name` of the Worker resources to read. + :param str activity_sid: The `activity_sid` of the Worker resources to read. + :param str available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param str friendly_name: The `friendly_name` of the Worker resources to read. + :param str target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param str task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param str task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param str ordering: Sorting parameter for Workers + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + activity_name=activity_name, + activity_sid=activity_sid, + available=available, + friendly_name=friendly_name, + target_workers_expression=target_workers_expression, + task_queue_name=task_queue_name, + task_queue_sid=task_queue_sid, + ordering=ordering, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkerInstance]: + """ + Asynchronously lists WorkerInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str activity_name: The `activity_name` of the Worker resources to read. + :param str activity_sid: The `activity_sid` of the Worker resources to read. + :param str available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param str friendly_name: The `friendly_name` of the Worker resources to read. + :param str target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param str task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param str task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param str ordering: Sorting parameter for Workers + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + activity_name=activity_name, + activity_sid=activity_sid, + available=available, + friendly_name=friendly_name, + target_workers_expression=target_workers_expression, + task_queue_name=task_queue_name, + task_queue_sid=task_queue_sid, + ordering=ordering, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkerPage: + """ + Retrieve a single page of WorkerInstance records from the API. + Request is executed immediately + + :param activity_name: The `activity_name` of the Worker resources to read. + :param activity_sid: The `activity_sid` of the Worker resources to read. + :param available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param friendly_name: The `friendly_name` of the Worker resources to read. + :param target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param ordering: Sorting parameter for Workers + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkerInstance + """ + data = values.of( + { + "ActivityName": activity_name, + "ActivitySid": activity_sid, + "Available": available, + "FriendlyName": friendly_name, + "TargetWorkersExpression": target_workers_expression, + "TaskQueueName": task_queue_name, + "TaskQueueSid": task_queue_sid, + "Ordering": ordering, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WorkerPage(self._version, response, self._solution) + + async def page_async( + self, + activity_name: Union[str, object] = values.unset, + activity_sid: Union[str, object] = values.unset, + available: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + target_workers_expression: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + ordering: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkerPage: + """ + Asynchronously retrieve a single page of WorkerInstance records from the API. + Request is executed immediately + + :param activity_name: The `activity_name` of the Worker resources to read. + :param activity_sid: The `activity_sid` of the Worker resources to read. + :param available: Whether to return only Worker resources that are available or unavailable. Can be `true`, `1`, or `yes` to return Worker resources that are available, and `false`, or any value returns the Worker resources that are not available. + :param friendly_name: The `friendly_name` of the Worker resources to read. + :param target_workers_expression: Filter by Workers that would match an expression. In addition to fields in the workers' attributes, the expression can include the following worker fields: `sid`, `friendly_name`, `activity_sid`, or `activity_name` + :param task_queue_name: The `friendly_name` of the TaskQueue that the Workers to read are eligible for. + :param task_queue_sid: The SID of the TaskQueue that the Workers to read are eligible for. + :param ordering: Sorting parameter for Workers + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkerInstance + """ + data = values.of( + { + "ActivityName": activity_name, + "ActivitySid": activity_sid, + "Available": available, + "FriendlyName": friendly_name, + "TargetWorkersExpression": target_workers_expression, + "TaskQueueName": task_queue_name, + "TaskQueueSid": task_queue_sid, + "Ordering": ordering, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WorkerPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WorkerPage: + """ + Retrieve a specific page of WorkerInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkerInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WorkerPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WorkerPage: + """ + Asynchronously retrieve a specific page of WorkerInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkerInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WorkerPage(self._version, response, self._solution) + + @property + def cumulative_statistics(self) -> WorkersCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + if self._cumulative_statistics is None: + self._cumulative_statistics = WorkersCumulativeStatisticsList( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + return self._cumulative_statistics + + @property + def real_time_statistics(self) -> WorkersRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + if self._real_time_statistics is None: + self._real_time_statistics = WorkersRealTimeStatisticsList( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + return self._real_time_statistics + + @property + def statistics(self) -> WorkersStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = WorkersStatisticsList( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + return self._statistics + + def get(self, sid: str) -> WorkerContext: + """ + Constructs a WorkerContext + + :param sid: The SID of the Worker resource to update. + """ + return WorkerContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> WorkerContext: + """ + Constructs a WorkerContext + + :param sid: The SID of the Worker resource to update. + """ + return WorkerContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8671c8d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/reservation.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/reservation.cpython-310.pyc new file mode 100644 index 0000000..9bf0daf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/reservation.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_channel.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_channel.cpython-310.pyc new file mode 100644 index 0000000..8da499c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_channel.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_statistics.cpython-310.pyc new file mode 100644 index 0000000..f54c11b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/worker_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_cumulative_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_cumulative_statistics.cpython-310.pyc new file mode 100644 index 0000000..2f0202e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_cumulative_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_real_time_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_real_time_statistics.cpython-310.pyc new file mode 100644 index 0000000..7c394d3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_real_time_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_statistics.cpython-310.pyc new file mode 100644 index 0000000..f8e8245 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/__pycache__/workers_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/reservation.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/reservation.py new file mode 100644 index 0000000..b7dc4bc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/reservation.py @@ -0,0 +1,1268 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ReservationInstance(InstanceResource): + + class CallStatus(object): + INITIATED = "initiated" + RINGING = "ringing" + ANSWERED = "answered" + COMPLETED = "completed" + + class ConferenceEvent(object): + START = "start" + END = "end" + JOIN = "join" + LEAVE = "leave" + MUTE = "mute" + HOLD = "hold" + SPEAKER = "speaker" + + class Status(object): + PENDING = "pending" + ACCEPTED = "accepted" + REJECTED = "rejected" + TIMEOUT = "timeout" + CANCELED = "canceled" + RESCINDED = "rescinded" + WRAPPING = "wrapping" + COMPLETED = "completed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the WorkerReservation resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar reservation_status: + :ivar sid: The unique string that we created to identify the WorkerReservation resource. + :ivar task_sid: The SID of the reserved Task resource. + :ivar worker_name: The `friendly_name` of the Worker that is reserved. + :ivar worker_sid: The SID of the reserved Worker resource. + :ivar workspace_sid: The SID of the Workspace that this worker is contained within. + :ivar url: The absolute URL of the WorkerReservation resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + worker_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.reservation_status: Optional["ReservationInstance.Status"] = payload.get( + "reservation_status" + ) + self.sid: Optional[str] = payload.get("sid") + self.task_sid: Optional[str] = payload.get("task_sid") + self.worker_name: Optional[str] = payload.get("worker_name") + self.worker_sid: Optional[str] = payload.get("worker_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + "sid": sid or self.sid, + } + self._context: Optional[ReservationContext] = None + + @property + def _proxy(self) -> "ReservationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ReservationContext for this ReservationInstance + """ + if self._context is None: + self._context = ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ReservationInstance": + """ + Fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ReservationInstance": + """ + Asynchronous coroutine to fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> "ReservationInstance": + """ + Update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for the reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: The timeout for call when executing a Dequeue instruction. + :param dequeue_to: The contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction. + :param call_timeout: The timeout for a call when executing a Call instruction. + :param call_to: The contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: The timeout for a call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Defaults to `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants allowed in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + return self._proxy.update( + if_match=if_match, + reservation_status=reservation_status, + worker_activity_sid=worker_activity_sid, + instruction=instruction, + dequeue_post_work_activity_sid=dequeue_post_work_activity_sid, + dequeue_from=dequeue_from, + dequeue_record=dequeue_record, + dequeue_timeout=dequeue_timeout, + dequeue_to=dequeue_to, + dequeue_status_callback_url=dequeue_status_callback_url, + call_from=call_from, + call_record=call_record, + call_timeout=call_timeout, + call_to=call_to, + call_url=call_url, + call_status_callback_url=call_status_callback_url, + call_accept=call_accept, + redirect_call_sid=redirect_call_sid, + redirect_accept=redirect_accept, + redirect_url=redirect_url, + to=to, + from_=from_, + status_callback=status_callback, + status_callback_method=status_callback_method, + status_callback_event=status_callback_event, + timeout=timeout, + record=record, + muted=muted, + beep=beep, + start_conference_on_enter=start_conference_on_enter, + end_conference_on_exit=end_conference_on_exit, + wait_url=wait_url, + wait_method=wait_method, + early_media=early_media, + max_participants=max_participants, + conference_status_callback=conference_status_callback, + conference_status_callback_method=conference_status_callback_method, + conference_status_callback_event=conference_status_callback_event, + conference_record=conference_record, + conference_trim=conference_trim, + recording_channels=recording_channels, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + conference_recording_status_callback=conference_recording_status_callback, + conference_recording_status_callback_method=conference_recording_status_callback_method, + region=region, + sip_auth_username=sip_auth_username, + sip_auth_password=sip_auth_password, + dequeue_status_callback_event=dequeue_status_callback_event, + post_work_activity_sid=post_work_activity_sid, + end_conference_on_customer_exit=end_conference_on_customer_exit, + beep_on_customer_entrance=beep_on_customer_entrance, + jitter_buffer_size=jitter_buffer_size, + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> "ReservationInstance": + """ + Asynchronous coroutine to update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for the reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: The timeout for call when executing a Dequeue instruction. + :param dequeue_to: The contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction. + :param call_timeout: The timeout for a call when executing a Call instruction. + :param call_to: The contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: The timeout for a call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Defaults to `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants allowed in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + return await self._proxy.update_async( + if_match=if_match, + reservation_status=reservation_status, + worker_activity_sid=worker_activity_sid, + instruction=instruction, + dequeue_post_work_activity_sid=dequeue_post_work_activity_sid, + dequeue_from=dequeue_from, + dequeue_record=dequeue_record, + dequeue_timeout=dequeue_timeout, + dequeue_to=dequeue_to, + dequeue_status_callback_url=dequeue_status_callback_url, + call_from=call_from, + call_record=call_record, + call_timeout=call_timeout, + call_to=call_to, + call_url=call_url, + call_status_callback_url=call_status_callback_url, + call_accept=call_accept, + redirect_call_sid=redirect_call_sid, + redirect_accept=redirect_accept, + redirect_url=redirect_url, + to=to, + from_=from_, + status_callback=status_callback, + status_callback_method=status_callback_method, + status_callback_event=status_callback_event, + timeout=timeout, + record=record, + muted=muted, + beep=beep, + start_conference_on_enter=start_conference_on_enter, + end_conference_on_exit=end_conference_on_exit, + wait_url=wait_url, + wait_method=wait_method, + early_media=early_media, + max_participants=max_participants, + conference_status_callback=conference_status_callback, + conference_status_callback_method=conference_status_callback_method, + conference_status_callback_event=conference_status_callback_event, + conference_record=conference_record, + conference_trim=conference_trim, + recording_channels=recording_channels, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + conference_recording_status_callback=conference_recording_status_callback, + conference_recording_status_callback_method=conference_recording_status_callback_method, + region=region, + sip_auth_username=sip_auth_username, + sip_auth_password=sip_auth_password, + dequeue_status_callback_event=dequeue_status_callback_event, + post_work_activity_sid=post_work_activity_sid, + end_conference_on_customer_exit=end_conference_on_customer_exit, + beep_on_customer_entrance=beep_on_customer_entrance, + jitter_buffer_size=jitter_buffer_size, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ReservationContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str, sid: str): + """ + Initialize the ReservationContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerReservation resources to update. + :param worker_sid: The SID of the reserved Worker resource with the WorkerReservation resources to update. + :param sid: The SID of the WorkerReservation resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/{worker_sid}/Reservations/{sid}".format( + **self._solution + ) + + def fetch(self) -> ReservationInstance: + """ + Fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ReservationInstance: + """ + Asynchronous coroutine to fetch the ReservationInstance + + + :returns: The fetched ReservationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> ReservationInstance: + """ + Update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for the reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: The timeout for call when executing a Dequeue instruction. + :param dequeue_to: The contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction. + :param call_timeout: The timeout for a call when executing a Call instruction. + :param call_to: The contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: The timeout for a call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Defaults to `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants allowed in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerActivitySid": worker_activity_sid, + "Instruction": instruction, + "DequeuePostWorkActivitySid": dequeue_post_work_activity_sid, + "DequeueFrom": dequeue_from, + "DequeueRecord": dequeue_record, + "DequeueTimeout": dequeue_timeout, + "DequeueTo": dequeue_to, + "DequeueStatusCallbackUrl": dequeue_status_callback_url, + "CallFrom": call_from, + "CallRecord": call_record, + "CallTimeout": call_timeout, + "CallTo": call_to, + "CallUrl": call_url, + "CallStatusCallbackUrl": call_status_callback_url, + "CallAccept": serialize.boolean_to_string(call_accept), + "RedirectCallSid": redirect_call_sid, + "RedirectAccept": serialize.boolean_to_string(redirect_accept), + "RedirectUrl": redirect_url, + "To": to, + "From": from_, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "Region": region, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "DequeueStatusCallbackEvent": serialize.map( + dequeue_status_callback_event, lambda e: e + ), + "PostWorkActivitySid": post_work_activity_sid, + "EndConferenceOnCustomerExit": serialize.boolean_to_string( + end_conference_on_customer_exit + ), + "BeepOnCustomerEntrance": serialize.boolean_to_string( + beep_on_customer_entrance + ), + "JitterBufferSize": jitter_buffer_size, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = self._version.update( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + if_match: Union[str, object] = values.unset, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + worker_activity_sid: Union[str, object] = values.unset, + instruction: Union[str, object] = values.unset, + dequeue_post_work_activity_sid: Union[str, object] = values.unset, + dequeue_from: Union[str, object] = values.unset, + dequeue_record: Union[str, object] = values.unset, + dequeue_timeout: Union[int, object] = values.unset, + dequeue_to: Union[str, object] = values.unset, + dequeue_status_callback_url: Union[str, object] = values.unset, + call_from: Union[str, object] = values.unset, + call_record: Union[str, object] = values.unset, + call_timeout: Union[int, object] = values.unset, + call_to: Union[str, object] = values.unset, + call_url: Union[str, object] = values.unset, + call_status_callback_url: Union[str, object] = values.unset, + call_accept: Union[bool, object] = values.unset, + redirect_call_sid: Union[str, object] = values.unset, + redirect_accept: Union[bool, object] = values.unset, + redirect_url: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + from_: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + status_callback_event: Union[ + List["ReservationInstance.CallStatus"], object + ] = values.unset, + timeout: Union[int, object] = values.unset, + record: Union[bool, object] = values.unset, + muted: Union[bool, object] = values.unset, + beep: Union[str, object] = values.unset, + start_conference_on_enter: Union[bool, object] = values.unset, + end_conference_on_exit: Union[bool, object] = values.unset, + wait_url: Union[str, object] = values.unset, + wait_method: Union[str, object] = values.unset, + early_media: Union[bool, object] = values.unset, + max_participants: Union[int, object] = values.unset, + conference_status_callback: Union[str, object] = values.unset, + conference_status_callback_method: Union[str, object] = values.unset, + conference_status_callback_event: Union[ + List["ReservationInstance.ConferenceEvent"], object + ] = values.unset, + conference_record: Union[str, object] = values.unset, + conference_trim: Union[str, object] = values.unset, + recording_channels: Union[str, object] = values.unset, + recording_status_callback: Union[str, object] = values.unset, + recording_status_callback_method: Union[str, object] = values.unset, + conference_recording_status_callback: Union[str, object] = values.unset, + conference_recording_status_callback_method: Union[str, object] = values.unset, + region: Union[str, object] = values.unset, + sip_auth_username: Union[str, object] = values.unset, + sip_auth_password: Union[str, object] = values.unset, + dequeue_status_callback_event: Union[List[str], object] = values.unset, + post_work_activity_sid: Union[str, object] = values.unset, + end_conference_on_customer_exit: Union[bool, object] = values.unset, + beep_on_customer_entrance: Union[bool, object] = values.unset, + jitter_buffer_size: Union[str, object] = values.unset, + ) -> ReservationInstance: + """ + Asynchronous coroutine to update the ReservationInstance + + :param if_match: The If-Match HTTP request header + :param reservation_status: + :param worker_activity_sid: The new worker activity SID if rejecting a reservation. + :param instruction: The assignment instruction for the reservation. + :param dequeue_post_work_activity_sid: The SID of the Activity resource to start after executing a Dequeue instruction. + :param dequeue_from: The caller ID of the call to the worker when executing a Dequeue instruction. + :param dequeue_record: Whether to record both legs of a call when executing a Dequeue instruction or which leg to record. + :param dequeue_timeout: The timeout for call when executing a Dequeue instruction. + :param dequeue_to: The contact URI of the worker when executing a Dequeue instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param dequeue_status_callback_url: The callback URL for completed call event when executing a Dequeue instruction. + :param call_from: The Caller ID of the outbound call when executing a Call instruction. + :param call_record: Whether to record both legs of a call when executing a Call instruction. + :param call_timeout: The timeout for a call when executing a Call instruction. + :param call_to: The contact URI of the worker when executing a Call instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param call_url: TwiML URI executed on answering the worker's leg as a result of the Call instruction. + :param call_status_callback_url: The URL to call for the completed call event when executing a Call instruction. + :param call_accept: Whether to accept a reservation when executing a Call instruction. + :param redirect_call_sid: The Call SID of the call parked in the queue when executing a Redirect instruction. + :param redirect_accept: Whether the reservation should be accepted when executing a Redirect instruction. + :param redirect_url: TwiML URI to redirect the call to when executing the Redirect instruction. + :param to: The Contact URI of the worker when executing a Conference instruction. Can be the URI of the Twilio Client, the SIP URI for Programmable SIP, or the [E.164](https://www.twilio.com/docs/glossary/what-e164) formatted phone number, depending on the destination. + :param from_: The caller ID of the call to the worker when executing a Conference instruction. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param status_callback_event: The call progress events that we will send to `status_callback`. Can be: `initiated`, `ringing`, `answered`, or `completed`. + :param timeout: The timeout for a call when executing a Conference instruction. + :param record: Whether to record the participant and their conferences, including the time between conferences. Can be `true` or `false` and the default is `false`. + :param muted: Whether the agent is muted in the conference. Defaults to `false`. + :param beep: Whether to play a notification beep when the participant joins or when to play a beep. Can be: `true`, `false`, `onEnter`, or `onExit`. The default value is `true`. + :param start_conference_on_enter: Whether to start the conference when the participant joins, if it has not already started. Can be: `true` or `false` and the default is `true`. If `false` and the conference has not started, the participant is muted and hears background music until another participant starts the conference. + :param end_conference_on_exit: Whether to end the conference when the agent leaves. + :param wait_url: The URL we should call using the `wait_method` for the music to play while participants are waiting for the conference to start. The default value is the URL of our standard hold music. [Learn more about hold music](https://www.twilio.com/labs/twimlets/holdmusic). + :param wait_method: The HTTP method we should use to call `wait_url`. Can be `GET` or `POST` and the default is `POST`. When using a static audio file, this should be `GET` so that we can cache the file. + :param early_media: Whether to allow an agent to hear the state of the outbound call, including ringing or disconnect messages. The default is `true`. + :param max_participants: The maximum number of participants allowed in the conference. Can be a positive integer from `2` to `250`. The default value is `250`. + :param conference_status_callback: The URL we should call using the `conference_status_callback_method` when the conference events in `conference_status_callback_event` occur. Only the value set by the first participant to join the conference is used. Subsequent `conference_status_callback` values are ignored. + :param conference_status_callback_method: The HTTP method we should use to call `conference_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_status_callback_event: The conference status events that we will send to `conference_status_callback`. Can be: `start`, `end`, `join`, `leave`, `mute`, `hold`, `speaker`. + :param conference_record: Whether to record the conference the participant is joining or when to record the conference. Can be: `true`, `false`, `record-from-start`, and `do-not-record`. The default value is `false`. + :param conference_trim: Whether to trim leading and trailing silence from your recorded conference audio files. Can be: `trim-silence` or `do-not-trim` and defaults to `trim-silence`. + :param recording_channels: The recording channels for the final recording. Can be: `mono` or `dual` and the default is `mono`. + :param recording_status_callback: The URL that we should call using the `recording_status_callback_method` when the recording status changes. + :param recording_status_callback_method: The HTTP method we should use when we call `recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param conference_recording_status_callback: The URL we should call using the `conference_recording_status_callback_method` when the conference recording is available. + :param conference_recording_status_callback_method: The HTTP method we should use to call `conference_recording_status_callback`. Can be: `GET` or `POST` and defaults to `POST`. + :param region: The [region](https://support.twilio.com/hc/en-us/articles/223132167-How-global-low-latency-routing-and-region-selection-work-for-conferences-and-Client-calls) where we should mix the recorded audio. Can be:`us1`, `ie1`, `de1`, `sg1`, `br1`, `au1`, or `jp1`. + :param sip_auth_username: The SIP username used for authentication. + :param sip_auth_password: The SIP password for authentication. + :param dequeue_status_callback_event: The call progress events sent via webhooks as a result of a Dequeue instruction. + :param post_work_activity_sid: The new worker activity SID after executing a Conference instruction. + :param end_conference_on_customer_exit: Whether to end the conference when the customer leaves. + :param beep_on_customer_entrance: Whether to play a notification beep when the customer joins. + :param jitter_buffer_size: The jitter buffer size for conference. Can be: `small`, `medium`, `large`, `off`. + + :returns: The updated ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "WorkerActivitySid": worker_activity_sid, + "Instruction": instruction, + "DequeuePostWorkActivitySid": dequeue_post_work_activity_sid, + "DequeueFrom": dequeue_from, + "DequeueRecord": dequeue_record, + "DequeueTimeout": dequeue_timeout, + "DequeueTo": dequeue_to, + "DequeueStatusCallbackUrl": dequeue_status_callback_url, + "CallFrom": call_from, + "CallRecord": call_record, + "CallTimeout": call_timeout, + "CallTo": call_to, + "CallUrl": call_url, + "CallStatusCallbackUrl": call_status_callback_url, + "CallAccept": serialize.boolean_to_string(call_accept), + "RedirectCallSid": redirect_call_sid, + "RedirectAccept": serialize.boolean_to_string(redirect_accept), + "RedirectUrl": redirect_url, + "To": to, + "From": from_, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "StatusCallbackEvent": serialize.map( + status_callback_event, lambda e: e + ), + "Timeout": timeout, + "Record": serialize.boolean_to_string(record), + "Muted": serialize.boolean_to_string(muted), + "Beep": beep, + "StartConferenceOnEnter": serialize.boolean_to_string( + start_conference_on_enter + ), + "EndConferenceOnExit": serialize.boolean_to_string( + end_conference_on_exit + ), + "WaitUrl": wait_url, + "WaitMethod": wait_method, + "EarlyMedia": serialize.boolean_to_string(early_media), + "MaxParticipants": max_participants, + "ConferenceStatusCallback": conference_status_callback, + "ConferenceStatusCallbackMethod": conference_status_callback_method, + "ConferenceStatusCallbackEvent": serialize.map( + conference_status_callback_event, lambda e: e + ), + "ConferenceRecord": conference_record, + "ConferenceTrim": conference_trim, + "RecordingChannels": recording_channels, + "RecordingStatusCallback": recording_status_callback, + "RecordingStatusCallbackMethod": recording_status_callback_method, + "ConferenceRecordingStatusCallback": conference_recording_status_callback, + "ConferenceRecordingStatusCallbackMethod": conference_recording_status_callback_method, + "Region": region, + "SipAuthUsername": sip_auth_username, + "SipAuthPassword": sip_auth_password, + "DequeueStatusCallbackEvent": serialize.map( + dequeue_status_callback_event, lambda e: e + ), + "PostWorkActivitySid": post_work_activity_sid, + "EndConferenceOnCustomerExit": serialize.boolean_to_string( + end_conference_on_customer_exit + ), + "BeepOnCustomerEntrance": serialize.boolean_to_string( + beep_on_customer_entrance + ), + "JitterBufferSize": jitter_buffer_size, + } + ) + headers = values.of( + { + "If-Match": if_match, + } + ) + + payload = await self._version.update_async( + method="POST", uri=self._uri, data=data, headers=headers + ) + + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ReservationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ReservationInstance: + """ + Build an instance of ReservationInstance + + :param payload: Payload response from the API + """ + return ReservationInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ReservationList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str): + """ + Initialize the ReservationList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerReservation resources to read. + :param worker_sid: The SID of the reserved Worker resource with the WorkerReservation resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/Workers/{worker_sid}/Reservations".format( + **self._solution + ) + ) + + def stream( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ReservationInstance]: + """ + Streams ReservationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + reservation_status=reservation_status, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ReservationInstance]: + """ + Asynchronously streams ReservationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + reservation_status=reservation_status, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ReservationInstance]: + """ + Lists ReservationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + reservation_status=reservation_status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ReservationInstance]: + """ + Asynchronously lists ReservationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ReservationInstance.Status" reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + reservation_status=reservation_status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ReservationPage: + """ + Retrieve a single page of ReservationInstance records from the API. + Request is executed immediately + + :param reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ReservationPage(self._version, response, self._solution) + + async def page_async( + self, + reservation_status: Union["ReservationInstance.Status", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ReservationPage: + """ + Asynchronously retrieve a single page of ReservationInstance records from the API. + Request is executed immediately + + :param reservation_status: Returns the list of reservations for a worker with a specified ReservationStatus. Can be: `pending`, `accepted`, `rejected`, `timeout`, `canceled`, or `rescinded`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ReservationInstance + """ + data = values.of( + { + "ReservationStatus": reservation_status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ReservationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ReservationPage: + """ + Retrieve a specific page of ReservationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ReservationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ReservationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ReservationPage: + """ + Asynchronously retrieve a specific page of ReservationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ReservationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ReservationPage(self._version, response, self._solution) + + def get(self, sid: str) -> ReservationContext: + """ + Constructs a ReservationContext + + :param sid: The SID of the WorkerReservation resource to update. + """ + return ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ReservationContext: + """ + Constructs a ReservationContext + + :param sid: The SID of the WorkerReservation resource to update. + """ + return ReservationContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_channel.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_channel.py new file mode 100644 index 0000000..f48ebb3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_channel.py @@ -0,0 +1,572 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WorkerChannelInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar assigned_tasks: The total number of Tasks assigned to Worker for the TaskChannel type. + :ivar available: Whether the Worker should receive Tasks of the TaskChannel type. + :ivar available_capacity_percentage: The current percentage of capacity the TaskChannel has available. Can be a number between `0` and `100`. A value of `0` indicates that TaskChannel has no capacity available and a value of `100` means the Worker is available to receive any Tasks of this TaskChannel type. + :ivar configured_capacity: The current configured capacity for the WorkerChannel. TaskRouter will not create any reservations after the assigned Tasks for the Worker reaches the value. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sid: The unique string that we created to identify the WorkerChannel resource. + :ivar task_channel_sid: The SID of the TaskChannel. + :ivar task_channel_unique_name: The unique name of the TaskChannel, such as `voice` or `sms`. + :ivar worker_sid: The SID of the Worker that contains the WorkerChannel. + :ivar workspace_sid: The SID of the Workspace that contains the WorkerChannel. + :ivar url: The absolute URL of the WorkerChannel resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + worker_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.assigned_tasks: Optional[int] = deserialize.integer( + payload.get("assigned_tasks") + ) + self.available: Optional[bool] = payload.get("available") + self.available_capacity_percentage: Optional[int] = deserialize.integer( + payload.get("available_capacity_percentage") + ) + self.configured_capacity: Optional[int] = deserialize.integer( + payload.get("configured_capacity") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.task_channel_sid: Optional[str] = payload.get("task_channel_sid") + self.task_channel_unique_name: Optional[str] = payload.get( + "task_channel_unique_name" + ) + self.worker_sid: Optional[str] = payload.get("worker_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + "sid": sid or self.sid, + } + self._context: Optional[WorkerChannelContext] = None + + @property + def _proxy(self) -> "WorkerChannelContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkerChannelContext for this WorkerChannelInstance + """ + if self._context is None: + self._context = WorkerChannelContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "WorkerChannelInstance": + """ + Fetch the WorkerChannelInstance + + + :returns: The fetched WorkerChannelInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WorkerChannelInstance": + """ + Asynchronous coroutine to fetch the WorkerChannelInstance + + + :returns: The fetched WorkerChannelInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + capacity: Union[int, object] = values.unset, + available: Union[bool, object] = values.unset, + ) -> "WorkerChannelInstance": + """ + Update the WorkerChannelInstance + + :param capacity: The total number of Tasks that the Worker should handle for the TaskChannel type. TaskRouter creates reservations for Tasks of this TaskChannel type up to the specified capacity. If the capacity is 0, no new reservations will be created. + :param available: Whether the WorkerChannel is available. Set to `false` to prevent the Worker from receiving any new Tasks of this TaskChannel type. + + :returns: The updated WorkerChannelInstance + """ + return self._proxy.update( + capacity=capacity, + available=available, + ) + + async def update_async( + self, + capacity: Union[int, object] = values.unset, + available: Union[bool, object] = values.unset, + ) -> "WorkerChannelInstance": + """ + Asynchronous coroutine to update the WorkerChannelInstance + + :param capacity: The total number of Tasks that the Worker should handle for the TaskChannel type. TaskRouter creates reservations for Tasks of this TaskChannel type up to the specified capacity. If the capacity is 0, no new reservations will be created. + :param available: Whether the WorkerChannel is available. Set to `false` to prevent the Worker from receiving any new Tasks of this TaskChannel type. + + :returns: The updated WorkerChannelInstance + """ + return await self._proxy.update_async( + capacity=capacity, + available=available, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerChannelContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str, sid: str): + """ + Initialize the WorkerChannelContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerChannel to update. + :param worker_sid: The SID of the Worker with the WorkerChannel to update. + :param sid: The SID of the WorkerChannel to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + "sid": sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/Workers/{worker_sid}/Channels/{sid}".format( + **self._solution + ) + ) + + def fetch(self) -> WorkerChannelInstance: + """ + Fetch the WorkerChannelInstance + + + :returns: The fetched WorkerChannelInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WorkerChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WorkerChannelInstance: + """ + Asynchronous coroutine to fetch the WorkerChannelInstance + + + :returns: The fetched WorkerChannelInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WorkerChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + capacity: Union[int, object] = values.unset, + available: Union[bool, object] = values.unset, + ) -> WorkerChannelInstance: + """ + Update the WorkerChannelInstance + + :param capacity: The total number of Tasks that the Worker should handle for the TaskChannel type. TaskRouter creates reservations for Tasks of this TaskChannel type up to the specified capacity. If the capacity is 0, no new reservations will be created. + :param available: Whether the WorkerChannel is available. Set to `false` to prevent the Worker from receiving any new Tasks of this TaskChannel type. + + :returns: The updated WorkerChannelInstance + """ + data = values.of( + { + "Capacity": capacity, + "Available": serialize.boolean_to_string(available), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkerChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + capacity: Union[int, object] = values.unset, + available: Union[bool, object] = values.unset, + ) -> WorkerChannelInstance: + """ + Asynchronous coroutine to update the WorkerChannelInstance + + :param capacity: The total number of Tasks that the Worker should handle for the TaskChannel type. TaskRouter creates reservations for Tasks of this TaskChannel type up to the specified capacity. If the capacity is 0, no new reservations will be created. + :param available: Whether the WorkerChannel is available. Set to `false` to prevent the Worker from receiving any new Tasks of this TaskChannel type. + + :returns: The updated WorkerChannelInstance + """ + data = values.of( + { + "Capacity": capacity, + "Available": serialize.boolean_to_string(available), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkerChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerChannelPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WorkerChannelInstance: + """ + Build an instance of WorkerChannelInstance + + :param payload: Payload response from the API + """ + return WorkerChannelInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WorkerChannelList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str): + """ + Initialize the WorkerChannelList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerChannels to read. + :param worker_sid: The SID of the Worker with the WorkerChannels to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/{worker_sid}/Channels".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WorkerChannelInstance]: + """ + Streams WorkerChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WorkerChannelInstance]: + """ + Asynchronously streams WorkerChannelInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkerChannelInstance]: + """ + Lists WorkerChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkerChannelInstance]: + """ + Asynchronously lists WorkerChannelInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkerChannelPage: + """ + Retrieve a single page of WorkerChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkerChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WorkerChannelPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkerChannelPage: + """ + Asynchronously retrieve a single page of WorkerChannelInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkerChannelInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WorkerChannelPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WorkerChannelPage: + """ + Retrieve a specific page of WorkerChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkerChannelInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WorkerChannelPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WorkerChannelPage: + """ + Asynchronously retrieve a specific page of WorkerChannelInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkerChannelInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WorkerChannelPage(self._version, response, self._solution) + + def get(self, sid: str) -> WorkerChannelContext: + """ + Constructs a WorkerChannelContext + + :param sid: The SID of the WorkerChannel to update. + """ + return WorkerChannelContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> WorkerChannelContext: + """ + Constructs a WorkerChannelContext + + :param sid: The SID of the WorkerChannel to update. + """ + return WorkerChannelContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_statistics.py new file mode 100644 index 0000000..61618d2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/worker_statistics.py @@ -0,0 +1,282 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkerStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar cumulative: An object that contains the cumulative statistics for the Worker. + :ivar worker_sid: The SID of the Worker that contains the WorkerChannel. + :ivar workspace_sid: The SID of the Workspace that contains the WorkerChannel. + :ivar url: The absolute URL of the WorkerChannel statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + worker_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.worker_sid: Optional[str] = payload.get("worker_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + } + self._context: Optional[WorkerStatisticsContext] = None + + @property + def _proxy(self) -> "WorkerStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkerStatisticsContext for this WorkerStatisticsInstance + """ + if self._context is None: + self._context = WorkerStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + return self._context + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkerStatisticsInstance": + """ + Fetch the WorkerStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkerStatisticsInstance + """ + return self._proxy.fetch( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkerStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkerStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkerStatisticsInstance + """ + return await self._proxy.fetch_async( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str): + """ + Initialize the WorkerStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerChannel to fetch. + :param worker_sid: The SID of the Worker with the WorkerChannel to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/Workers/{worker_sid}/Statistics".format( + **self._solution + ) + ) + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkerStatisticsInstance: + """ + Fetch the WorkerStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkerStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkerStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkerStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkerStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkerStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkerStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkerStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, worker_sid: str): + """ + Initialize the WorkerStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the WorkerChannel to fetch. + :param worker_sid: The SID of the Worker with the WorkerChannel to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "worker_sid": worker_sid, + } + + def get(self) -> WorkerStatisticsContext: + """ + Constructs a WorkerStatisticsContext + + """ + return WorkerStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + def __call__(self) -> WorkerStatisticsContext: + """ + Constructs a WorkerStatisticsContext + + """ + return WorkerStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + worker_sid=self._solution["worker_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_cumulative_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_cumulative_statistics.py new file mode 100644 index 0000000..b18e7f1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_cumulative_statistics.py @@ -0,0 +1,298 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkersCumulativeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar start_time: The beginning of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar end_time: The end of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar activity_durations: The minimum, average, maximum, and total time (in seconds) that Workers spent in each Activity. + :ivar reservations_created: The total number of Reservations that were created. + :ivar reservations_accepted: The total number of Reservations that were accepted. + :ivar reservations_rejected: The total number of Reservations that were rejected. + :ivar reservations_timed_out: The total number of Reservations that were timed out. + :ivar reservations_canceled: The total number of Reservations that were canceled. + :ivar reservations_rescinded: The total number of Reservations that were rescinded. + :ivar workspace_sid: The SID of the Workspace that contains the Workers. + :ivar url: The absolute URL of the Workers statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.activity_durations: Optional[List[Dict[str, object]]] = payload.get( + "activity_durations" + ) + self.reservations_created: Optional[int] = deserialize.integer( + payload.get("reservations_created") + ) + self.reservations_accepted: Optional[int] = deserialize.integer( + payload.get("reservations_accepted") + ) + self.reservations_rejected: Optional[int] = deserialize.integer( + payload.get("reservations_rejected") + ) + self.reservations_timed_out: Optional[int] = deserialize.integer( + payload.get("reservations_timed_out") + ) + self.reservations_canceled: Optional[int] = deserialize.integer( + payload.get("reservations_canceled") + ) + self.reservations_rescinded: Optional[int] = deserialize.integer( + payload.get("reservations_rescinded") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkersCumulativeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkersCumulativeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkersCumulativeStatisticsContext for this WorkersCumulativeStatisticsInstance + """ + if self._context is None: + self._context = WorkersCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkersCumulativeStatisticsInstance": + """ + Fetch the WorkersCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersCumulativeStatisticsInstance + """ + return self._proxy.fetch( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkersCumulativeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkersCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersCumulativeStatisticsInstance + """ + return await self._proxy.fetch_async( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkersCumulativeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersCumulativeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/CumulativeStatistics".format( + **self._solution + ) + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkersCumulativeStatisticsInstance: + """ + Fetch the WorkersCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkersCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkersCumulativeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkersCumulativeStatisticsInstance + + :param end_date: Only calculate statistics from this date and time and earlier, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkersCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkersCumulativeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersCumulativeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkersCumulativeStatisticsContext: + """ + Constructs a WorkersCumulativeStatisticsContext + + """ + return WorkersCumulativeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkersCumulativeStatisticsContext: + """ + Constructs a WorkersCumulativeStatisticsContext + + """ + return WorkersCumulativeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_real_time_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_real_time_statistics.py new file mode 100644 index 0000000..277889a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_real_time_statistics.py @@ -0,0 +1,229 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkersRealTimeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar activity_statistics: The number of current Workers by Activity. + :ivar total_workers: The total number of Workers. + :ivar workspace_sid: The SID of the Workspace that contains the Workers. + :ivar url: The absolute URL of the Workers statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.activity_statistics: Optional[List[Dict[str, object]]] = payload.get( + "activity_statistics" + ) + self.total_workers: Optional[int] = deserialize.integer( + payload.get("total_workers") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkersRealTimeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkersRealTimeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkersRealTimeStatisticsContext for this WorkersRealTimeStatisticsInstance + """ + if self._context is None: + self._context = WorkersRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkersRealTimeStatisticsInstance": + """ + Fetch the WorkersRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersRealTimeStatisticsInstance + """ + return self._proxy.fetch( + task_channel=task_channel, + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkersRealTimeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkersRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersRealTimeStatisticsInstance + """ + return await self._proxy.fetch_async( + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkersRealTimeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersRealTimeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/RealTimeStatistics".format( + **self._solution + ) + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> WorkersRealTimeStatisticsInstance: + """ + Fetch the WorkersRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkersRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> WorkersRealTimeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkersRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkersRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkersRealTimeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersRealTimeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkersRealTimeStatisticsContext: + """ + Constructs a WorkersRealTimeStatisticsContext + + """ + return WorkersRealTimeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkersRealTimeStatisticsContext: + """ + Constructs a WorkersRealTimeStatisticsContext + + """ + return WorkersRealTimeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_statistics.py new file mode 100644 index 0000000..0cd09ec --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/worker/workers_statistics.py @@ -0,0 +1,298 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkersStatisticsInstance(InstanceResource): + """ + :ivar realtime: An object that contains the real-time statistics for the Worker. + :ivar cumulative: An object that contains the cumulative statistics for the Worker. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Worker resource. + :ivar workspace_sid: The SID of the Workspace that contains the Worker. + :ivar url: The absolute URL of the Worker statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.realtime: Optional[Dict[str, object]] = payload.get("realtime") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.account_sid: Optional[str] = payload.get("account_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkersStatisticsContext] = None + + @property + def _proxy(self) -> "WorkersStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkersStatisticsContext for this WorkersStatisticsInstance + """ + if self._context is None: + self._context = WorkersStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkersStatisticsInstance": + """ + Fetch the WorkersStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_queue_sid: The SID of the TaskQueue for which to fetch Worker statistics. + :param task_queue_name: The `friendly_name` of the TaskQueue for which to fetch Worker statistics. + :param friendly_name: Only include Workers with `friendly_name` values that match this parameter. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersStatisticsInstance + """ + return self._proxy.fetch( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + friendly_name=friendly_name, + task_channel=task_channel, + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> "WorkersStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkersStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_queue_sid: The SID of the TaskQueue for which to fetch Worker statistics. + :param task_queue_name: The `friendly_name` of the TaskQueue for which to fetch Worker statistics. + :param friendly_name: Only include Workers with `friendly_name` values that match this parameter. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersStatisticsInstance + """ + return await self._proxy.fetch_async( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_queue_sid=task_queue_sid, + task_queue_name=task_queue_name, + friendly_name=friendly_name, + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkersStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Worker to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workers/Statistics".format( + **self._solution + ) + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkersStatisticsInstance: + """ + Fetch the WorkersStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_queue_sid: The SID of the TaskQueue for which to fetch Worker statistics. + :param task_queue_name: The `friendly_name` of the TaskQueue for which to fetch Worker statistics. + :param friendly_name: Only include Workers with `friendly_name` values that match this parameter. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskQueueSid": task_queue_sid, + "TaskQueueName": task_queue_name, + "FriendlyName": friendly_name, + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkersStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_queue_sid: Union[str, object] = values.unset, + task_queue_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + task_channel: Union[str, object] = values.unset, + ) -> WorkersStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkersStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_queue_sid: The SID of the TaskQueue for which to fetch Worker statistics. + :param task_queue_name: The `friendly_name` of the TaskQueue for which to fetch Worker statistics. + :param friendly_name: Only include Workers with `friendly_name` values that match this parameter. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkersStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskQueueSid": task_queue_sid, + "TaskQueueName": task_queue_name, + "FriendlyName": friendly_name, + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkersStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkersStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkersStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Worker to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkersStatisticsContext: + """ + Constructs a WorkersStatisticsContext + + """ + return WorkersStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkersStatisticsContext: + """ + Constructs a WorkersStatisticsContext + + """ + return WorkersStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__init__.py new file mode 100644 index 0000000..f01e76d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__init__.py @@ -0,0 +1,807 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.taskrouter.v1.workspace.workflow.workflow_cumulative_statistics import ( + WorkflowCumulativeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.workflow.workflow_real_time_statistics import ( + WorkflowRealTimeStatisticsList, +) +from twilio.rest.taskrouter.v1.workspace.workflow.workflow_statistics import ( + WorkflowStatisticsList, +) + + +class WorkflowInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workflow resource. + :ivar assignment_callback_url: The URL that we call when a task managed by the Workflow is assigned to a Worker. See Assignment Callback URL for more information. + :ivar configuration: A JSON string that contains the Workflow's configuration. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar document_content_type: The MIME type of the document. + :ivar fallback_assignment_callback_url: The URL that we call when a call to the `assignment_callback_url` fails. + :ivar friendly_name: The string that you assigned to describe the Workflow resource. For example, `Customer Support` or `2014 Election Campaign`. + :ivar sid: The unique string that we created to identify the Workflow resource. + :ivar task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + :ivar workspace_sid: The SID of the Workspace that contains the Workflow. + :ivar url: The absolute URL of the Workflow resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.assignment_callback_url: Optional[str] = payload.get( + "assignment_callback_url" + ) + self.configuration: Optional[str] = payload.get("configuration") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.document_content_type: Optional[str] = payload.get("document_content_type") + self.fallback_assignment_callback_url: Optional[str] = payload.get( + "fallback_assignment_callback_url" + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.sid: Optional[str] = payload.get("sid") + self.task_reservation_timeout: Optional[int] = deserialize.integer( + payload.get("task_reservation_timeout") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid or self.sid, + } + self._context: Optional[WorkflowContext] = None + + @property + def _proxy(self) -> "WorkflowContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkflowContext for this WorkflowInstance + """ + if self._context is None: + self._context = WorkflowContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WorkflowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WorkflowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WorkflowInstance": + """ + Fetch the WorkflowInstance + + + :returns: The fetched WorkflowInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WorkflowInstance": + """ + Asynchronous coroutine to fetch the WorkflowInstance + + + :returns: The fetched WorkflowInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + configuration: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + re_evaluate_tasks: Union[str, object] = values.unset, + ) -> "WorkflowInstance": + """ + Update the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + :param re_evaluate_tasks: Whether or not to re-evaluate Tasks. The default is `false`, which means Tasks in the Workflow will not be processed through the assignment loop again. + + :returns: The updated WorkflowInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + assignment_callback_url=assignment_callback_url, + fallback_assignment_callback_url=fallback_assignment_callback_url, + configuration=configuration, + task_reservation_timeout=task_reservation_timeout, + re_evaluate_tasks=re_evaluate_tasks, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + configuration: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + re_evaluate_tasks: Union[str, object] = values.unset, + ) -> "WorkflowInstance": + """ + Asynchronous coroutine to update the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + :param re_evaluate_tasks: Whether or not to re-evaluate Tasks. The default is `false`, which means Tasks in the Workflow will not be processed through the assignment loop again. + + :returns: The updated WorkflowInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + assignment_callback_url=assignment_callback_url, + fallback_assignment_callback_url=fallback_assignment_callback_url, + configuration=configuration, + task_reservation_timeout=task_reservation_timeout, + re_evaluate_tasks=re_evaluate_tasks, + ) + + @property + def cumulative_statistics(self) -> WorkflowCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + return self._proxy.cumulative_statistics + + @property + def real_time_statistics(self) -> WorkflowRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + return self._proxy.real_time_statistics + + @property + def statistics(self) -> WorkflowStatisticsList: + """ + Access the statistics + """ + return self._proxy.statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkflowContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, sid: str): + """ + Initialize the WorkflowContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to update. + :param sid: The SID of the Workflow resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "sid": sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workflows/{sid}".format( + **self._solution + ) + + self._cumulative_statistics: Optional[WorkflowCumulativeStatisticsList] = None + self._real_time_statistics: Optional[WorkflowRealTimeStatisticsList] = None + self._statistics: Optional[WorkflowStatisticsList] = None + + def delete(self) -> bool: + """ + Deletes the WorkflowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WorkflowInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WorkflowInstance: + """ + Fetch the WorkflowInstance + + + :returns: The fetched WorkflowInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WorkflowInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WorkflowInstance: + """ + Asynchronous coroutine to fetch the WorkflowInstance + + + :returns: The fetched WorkflowInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WorkflowInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + configuration: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + re_evaluate_tasks: Union[str, object] = values.unset, + ) -> WorkflowInstance: + """ + Update the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + :param re_evaluate_tasks: Whether or not to re-evaluate Tasks. The default is `false`, which means Tasks in the Workflow will not be processed through the assignment loop again. + + :returns: The updated WorkflowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AssignmentCallbackUrl": assignment_callback_url, + "FallbackAssignmentCallbackUrl": fallback_assignment_callback_url, + "Configuration": configuration, + "TaskReservationTimeout": task_reservation_timeout, + "ReEvaluateTasks": re_evaluate_tasks, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkflowInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + configuration: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + re_evaluate_tasks: Union[str, object] = values.unset, + ) -> WorkflowInstance: + """ + Asynchronous coroutine to update the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + :param re_evaluate_tasks: Whether or not to re-evaluate Tasks. The default is `false`, which means Tasks in the Workflow will not be processed through the assignment loop again. + + :returns: The updated WorkflowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AssignmentCallbackUrl": assignment_callback_url, + "FallbackAssignmentCallbackUrl": fallback_assignment_callback_url, + "Configuration": configuration, + "TaskReservationTimeout": task_reservation_timeout, + "ReEvaluateTasks": re_evaluate_tasks, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkflowInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + sid=self._solution["sid"], + ) + + @property + def cumulative_statistics(self) -> WorkflowCumulativeStatisticsList: + """ + Access the cumulative_statistics + """ + if self._cumulative_statistics is None: + self._cumulative_statistics = WorkflowCumulativeStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._cumulative_statistics + + @property + def real_time_statistics(self) -> WorkflowRealTimeStatisticsList: + """ + Access the real_time_statistics + """ + if self._real_time_statistics is None: + self._real_time_statistics = WorkflowRealTimeStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._real_time_statistics + + @property + def statistics(self) -> WorkflowStatisticsList: + """ + Access the statistics + """ + if self._statistics is None: + self._statistics = WorkflowStatisticsList( + self._version, + self._solution["workspace_sid"], + self._solution["sid"], + ) + return self._statistics + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkflowPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WorkflowInstance: + """ + Build an instance of WorkflowInstance + + :param payload: Payload response from the API + """ + return WorkflowInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WorkflowList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkflowList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workflows".format(**self._solution) + + def create( + self, + friendly_name: str, + configuration: str, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + ) -> WorkflowInstance: + """ + Create the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + + :returns: The created WorkflowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Configuration": configuration, + "AssignmentCallbackUrl": assignment_callback_url, + "FallbackAssignmentCallbackUrl": fallback_assignment_callback_url, + "TaskReservationTimeout": task_reservation_timeout, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkflowInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + async def create_async( + self, + friendly_name: str, + configuration: str, + assignment_callback_url: Union[str, object] = values.unset, + fallback_assignment_callback_url: Union[str, object] = values.unset, + task_reservation_timeout: Union[int, object] = values.unset, + ) -> WorkflowInstance: + """ + Asynchronously create the WorkflowInstance + + :param friendly_name: A descriptive string that you create to describe the Workflow resource. For example, `Inbound Call Workflow` or `2014 Outbound Campaign`. + :param configuration: A JSON string that contains the rules to apply to the Workflow. See [Configuring Workflows](https://www.twilio.com/docs/taskrouter/workflow-configuration) for more information. + :param assignment_callback_url: The URL from your application that will process task assignment events. See [Handling Task Assignment Callback](https://www.twilio.com/docs/taskrouter/handle-assignment-callbacks) for more details. + :param fallback_assignment_callback_url: The URL that we should call when a call to the `assignment_callback_url` fails. + :param task_reservation_timeout: How long TaskRouter will wait for a confirmation response from your application after it assigns a Task to a Worker. Can be up to `86,400` (24 hours) and the default is `120`. + + :returns: The created WorkflowInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Configuration": configuration, + "AssignmentCallbackUrl": assignment_callback_url, + "FallbackAssignmentCallbackUrl": fallback_assignment_callback_url, + "TaskReservationTimeout": task_reservation_timeout, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WorkflowInstance( + self._version, payload, workspace_sid=self._solution["workspace_sid"] + ) + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WorkflowInstance]: + """ + Streams WorkflowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Workflow resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WorkflowInstance]: + """ + Asynchronously streams WorkflowInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: The `friendly_name` of the Workflow resources to read. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkflowInstance]: + """ + Lists WorkflowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Workflow resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WorkflowInstance]: + """ + Asynchronously lists WorkflowInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: The `friendly_name` of the Workflow resources to read. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkflowPage: + """ + Retrieve a single page of WorkflowInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Workflow resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkflowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WorkflowPage(self._version, response, self._solution) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WorkflowPage: + """ + Asynchronously retrieve a single page of WorkflowInstance records from the API. + Request is executed immediately + + :param friendly_name: The `friendly_name` of the Workflow resources to read. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WorkflowInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WorkflowPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WorkflowPage: + """ + Retrieve a specific page of WorkflowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkflowInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WorkflowPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WorkflowPage: + """ + Asynchronously retrieve a specific page of WorkflowInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WorkflowInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WorkflowPage(self._version, response, self._solution) + + def get(self, sid: str) -> WorkflowContext: + """ + Constructs a WorkflowContext + + :param sid: The SID of the Workflow resource to update. + """ + return WorkflowContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __call__(self, sid: str) -> WorkflowContext: + """ + Constructs a WorkflowContext + + :param sid: The SID of the Workflow resource to update. + """ + return WorkflowContext( + self._version, workspace_sid=self._solution["workspace_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c5804d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_cumulative_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_cumulative_statistics.cpython-310.pyc new file mode 100644 index 0000000..94e09dc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_cumulative_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_real_time_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_real_time_statistics.cpython-310.pyc new file mode 100644 index 0000000..a7db33d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_real_time_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_statistics.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_statistics.cpython-310.pyc new file mode 100644 index 0000000..6c6b650 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/__pycache__/workflow_statistics.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_cumulative_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_cumulative_statistics.py new file mode 100644 index 0000000..e06cab0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_cumulative_statistics.py @@ -0,0 +1,366 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkflowCumulativeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workflow resource. + :ivar avg_task_acceptance_time: The average time in seconds between Task creation and acceptance. + :ivar start_time: The beginning of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar end_time: The end of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar reservations_created: The total number of Reservations that were created for Workers. + :ivar reservations_accepted: The total number of Reservations accepted by Workers. + :ivar reservations_rejected: The total number of Reservations that were rejected. + :ivar reservations_timed_out: The total number of Reservations that were timed out. + :ivar reservations_canceled: The total number of Reservations that were canceled. + :ivar reservations_rescinded: The total number of Reservations that were rescinded. + :ivar split_by_wait_time: A list of objects that describe the number of Tasks canceled and reservations accepted above and below the thresholds specified in seconds. + :ivar wait_duration_until_accepted: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks that were accepted. + :ivar wait_duration_until_canceled: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks that were canceled. + :ivar tasks_canceled: The total number of Tasks that were canceled. + :ivar tasks_completed: The total number of Tasks that were completed. + :ivar tasks_entered: The total number of Tasks that entered the Workflow. + :ivar tasks_deleted: The total number of Tasks that were deleted. + :ivar tasks_moved: The total number of Tasks that were moved from one queue to another. + :ivar tasks_timed_out_in_workflow: The total number of Tasks that were timed out of their Workflows (and deleted). + :ivar workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified Sid value. + :ivar workspace_sid: The SID of the Workspace that contains the Workflow. + :ivar url: The absolute URL of the Workflow statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + workflow_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.avg_task_acceptance_time: Optional[int] = deserialize.integer( + payload.get("avg_task_acceptance_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.reservations_created: Optional[int] = deserialize.integer( + payload.get("reservations_created") + ) + self.reservations_accepted: Optional[int] = deserialize.integer( + payload.get("reservations_accepted") + ) + self.reservations_rejected: Optional[int] = deserialize.integer( + payload.get("reservations_rejected") + ) + self.reservations_timed_out: Optional[int] = deserialize.integer( + payload.get("reservations_timed_out") + ) + self.reservations_canceled: Optional[int] = deserialize.integer( + payload.get("reservations_canceled") + ) + self.reservations_rescinded: Optional[int] = deserialize.integer( + payload.get("reservations_rescinded") + ) + self.split_by_wait_time: Optional[Dict[str, object]] = payload.get( + "split_by_wait_time" + ) + self.wait_duration_until_accepted: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_accepted" + ) + self.wait_duration_until_canceled: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_canceled" + ) + self.tasks_canceled: Optional[int] = deserialize.integer( + payload.get("tasks_canceled") + ) + self.tasks_completed: Optional[int] = deserialize.integer( + payload.get("tasks_completed") + ) + self.tasks_entered: Optional[int] = deserialize.integer( + payload.get("tasks_entered") + ) + self.tasks_deleted: Optional[int] = deserialize.integer( + payload.get("tasks_deleted") + ) + self.tasks_moved: Optional[int] = deserialize.integer( + payload.get("tasks_moved") + ) + self.tasks_timed_out_in_workflow: Optional[int] = deserialize.integer( + payload.get("tasks_timed_out_in_workflow") + ) + self.workflow_sid: Optional[str] = payload.get("workflow_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._context: Optional[WorkflowCumulativeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkflowCumulativeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkflowCumulativeStatisticsContext for this WorkflowCumulativeStatisticsInstance + """ + if self._context is None: + self._context = WorkflowCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + return self._context + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkflowCumulativeStatisticsInstance": + """ + Fetch the WorkflowCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkflowCumulativeStatisticsInstance + """ + return self._proxy.fetch( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkflowCumulativeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkflowCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkflowCumulativeStatisticsInstance + """ + return await self._proxy.fetch_async( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkflowCumulativeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowCumulativeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified Sid value. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workflows/{workflow_sid}/CumulativeStatistics".format( + **self._solution + ) + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkflowCumulativeStatisticsInstance: + """ + Fetch the WorkflowCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkflowCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkflowCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkflowCumulativeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkflowCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkflowCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkflowCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkflowCumulativeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowCumulativeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the resource to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified Sid value. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + + def get(self) -> WorkflowCumulativeStatisticsContext: + """ + Constructs a WorkflowCumulativeStatisticsContext + + """ + return WorkflowCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __call__(self) -> WorkflowCumulativeStatisticsContext: + """ + Constructs a WorkflowCumulativeStatisticsContext + + """ + return WorkflowCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_real_time_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_real_time_statistics.py new file mode 100644 index 0000000..415d422 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_real_time_statistics.py @@ -0,0 +1,261 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkflowRealTimeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workflow resource. + :ivar longest_task_waiting_age: The age of the longest waiting Task. + :ivar longest_task_waiting_sid: The SID of the longest waiting Task. + :ivar tasks_by_priority: The number of Tasks by priority. For example: `{\"0\": \"10\", \"99\": \"5\"}` shows 10 Tasks at priority 0 and 5 at priority 99. + :ivar tasks_by_status: The number of Tasks by their current status. For example: `{\"pending\": \"1\", \"reserved\": \"3\", \"assigned\": \"2\", \"completed\": \"5\"}`. + :ivar total_tasks: The total number of Tasks. + :ivar workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + :ivar workspace_sid: The SID of the Workspace that contains the Workflow. + :ivar url: The absolute URL of the Workflow statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + workflow_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.longest_task_waiting_age: Optional[int] = deserialize.integer( + payload.get("longest_task_waiting_age") + ) + self.longest_task_waiting_sid: Optional[str] = payload.get( + "longest_task_waiting_sid" + ) + self.tasks_by_priority: Optional[Dict[str, object]] = payload.get( + "tasks_by_priority" + ) + self.tasks_by_status: Optional[Dict[str, object]] = payload.get( + "tasks_by_status" + ) + self.total_tasks: Optional[int] = deserialize.integer( + payload.get("total_tasks") + ) + self.workflow_sid: Optional[str] = payload.get("workflow_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._context: Optional[WorkflowRealTimeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkflowRealTimeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkflowRealTimeStatisticsContext for this WorkflowRealTimeStatisticsInstance + """ + if self._context is None: + self._context = WorkflowRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + return self._context + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkflowRealTimeStatisticsInstance": + """ + Fetch the WorkflowRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkflowRealTimeStatisticsInstance + """ + return self._proxy.fetch( + task_channel=task_channel, + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkflowRealTimeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkflowRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkflowRealTimeStatisticsInstance + """ + return await self._proxy.fetch_async( + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkflowRealTimeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowRealTimeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Workflows/{workflow_sid}/RealTimeStatistics".format( + **self._solution + ) + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> WorkflowRealTimeStatisticsInstance: + """ + Fetch the WorkflowRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkflowRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkflowRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> WorkflowRealTimeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkflowRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkflowRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkflowRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkflowRealTimeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowRealTimeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + + def get(self) -> WorkflowRealTimeStatisticsContext: + """ + Constructs a WorkflowRealTimeStatisticsContext + + """ + return WorkflowRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __call__(self) -> WorkflowRealTimeStatisticsContext: + """ + Constructs a WorkflowRealTimeStatisticsContext + + """ + return WorkflowRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_statistics.py new file mode 100644 index 0000000..f86590e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workflow/workflow_statistics.py @@ -0,0 +1,296 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkflowStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workflow resource. + :ivar cumulative: An object that contains the cumulative statistics for the Workflow. + :ivar realtime: An object that contains the real-time statistics for the Workflow. + :ivar workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + :ivar workspace_sid: The SID of the Workspace that contains the Workflow. + :ivar url: The absolute URL of the Workflow statistics resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + workspace_sid: str, + workflow_sid: str, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.realtime: Optional[Dict[str, object]] = payload.get("realtime") + self.workflow_sid: Optional[str] = payload.get("workflow_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._context: Optional[WorkflowStatisticsContext] = None + + @property + def _proxy(self) -> "WorkflowStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkflowStatisticsContext for this WorkflowStatisticsInstance + """ + if self._context is None: + self._context = WorkflowStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + return self._context + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkflowStatisticsInstance": + """ + Fetch the WorkflowStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkflowStatisticsInstance + """ + return self._proxy.fetch( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkflowStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkflowStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkflowStatisticsInstance + """ + return await self._proxy.fetch_async( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkflowStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + self._uri = ( + "/Workspaces/{workspace_sid}/Workflows/{workflow_sid}/Statistics".format( + **self._solution + ) + ) + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkflowStatisticsInstance: + """ + Fetch the WorkflowStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkflowStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkflowStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkflowStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkflowStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkflowStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkflowStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkflowStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str, workflow_sid: str): + """ + Initialize the WorkflowStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace with the Workflow to fetch. + :param workflow_sid: Returns the list of Tasks that are being controlled by the Workflow with the specified SID value. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + "workflow_sid": workflow_sid, + } + + def get(self) -> WorkflowStatisticsContext: + """ + Constructs a WorkflowStatisticsContext + + """ + return WorkflowStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __call__(self) -> WorkflowStatisticsContext: + """ + Constructs a WorkflowStatisticsContext + + """ + return WorkflowStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + workflow_sid=self._solution["workflow_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_cumulative_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_cumulative_statistics.py new file mode 100644 index 0000000..2b22364 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_cumulative_statistics.py @@ -0,0 +1,346 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkspaceCumulativeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workspace resource. + :ivar avg_task_acceptance_time: The average time in seconds between Task creation and acceptance. + :ivar start_time: The beginning of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar end_time: The end of the interval during which these statistics were calculated, in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar reservations_created: The total number of Reservations that were created for Workers. + :ivar reservations_accepted: The total number of Reservations accepted by Workers. + :ivar reservations_rejected: The total number of Reservations that were rejected. + :ivar reservations_timed_out: The total number of Reservations that were timed out. + :ivar reservations_canceled: The total number of Reservations that were canceled. + :ivar reservations_rescinded: The total number of Reservations that were rescinded. + :ivar split_by_wait_time: A list of objects that describe the number of Tasks canceled and reservations accepted above and below the thresholds specified in seconds. + :ivar wait_duration_until_accepted: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks that were accepted. + :ivar wait_duration_until_canceled: The wait duration statistics (`avg`, `min`, `max`, `total`) for Tasks that were canceled. + :ivar tasks_canceled: The total number of Tasks that were canceled. + :ivar tasks_completed: The total number of Tasks that were completed. + :ivar tasks_created: The total number of Tasks created. + :ivar tasks_deleted: The total number of Tasks that were deleted. + :ivar tasks_moved: The total number of Tasks that were moved from one queue to another. + :ivar tasks_timed_out_in_workflow: The total number of Tasks that were timed out of their Workflows (and deleted). + :ivar workspace_sid: The SID of the Workspace. + :ivar url: The absolute URL of the Workspace statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.avg_task_acceptance_time: Optional[int] = deserialize.integer( + payload.get("avg_task_acceptance_time") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.reservations_created: Optional[int] = deserialize.integer( + payload.get("reservations_created") + ) + self.reservations_accepted: Optional[int] = deserialize.integer( + payload.get("reservations_accepted") + ) + self.reservations_rejected: Optional[int] = deserialize.integer( + payload.get("reservations_rejected") + ) + self.reservations_timed_out: Optional[int] = deserialize.integer( + payload.get("reservations_timed_out") + ) + self.reservations_canceled: Optional[int] = deserialize.integer( + payload.get("reservations_canceled") + ) + self.reservations_rescinded: Optional[int] = deserialize.integer( + payload.get("reservations_rescinded") + ) + self.split_by_wait_time: Optional[Dict[str, object]] = payload.get( + "split_by_wait_time" + ) + self.wait_duration_until_accepted: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_accepted" + ) + self.wait_duration_until_canceled: Optional[Dict[str, object]] = payload.get( + "wait_duration_until_canceled" + ) + self.tasks_canceled: Optional[int] = deserialize.integer( + payload.get("tasks_canceled") + ) + self.tasks_completed: Optional[int] = deserialize.integer( + payload.get("tasks_completed") + ) + self.tasks_created: Optional[int] = deserialize.integer( + payload.get("tasks_created") + ) + self.tasks_deleted: Optional[int] = deserialize.integer( + payload.get("tasks_deleted") + ) + self.tasks_moved: Optional[int] = deserialize.integer( + payload.get("tasks_moved") + ) + self.tasks_timed_out_in_workflow: Optional[int] = deserialize.integer( + payload.get("tasks_timed_out_in_workflow") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkspaceCumulativeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkspaceCumulativeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkspaceCumulativeStatisticsContext for this WorkspaceCumulativeStatisticsInstance + """ + if self._context is None: + self._context = WorkspaceCumulativeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkspaceCumulativeStatisticsInstance": + """ + Fetch the WorkspaceCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkspaceCumulativeStatisticsInstance + """ + return self._proxy.fetch( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkspaceCumulativeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkspaceCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkspaceCumulativeStatisticsInstance + """ + return await self._proxy.fetch_async( + end_date=end_date, + minutes=minutes, + start_date=start_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkspaceCumulativeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceCumulativeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/CumulativeStatistics".format( + **self._solution + ) + + def fetch( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkspaceCumulativeStatisticsInstance: + """ + Fetch the WorkspaceCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkspaceCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkspaceCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, + end_date: Union[datetime, object] = values.unset, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkspaceCumulativeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkspaceCumulativeStatisticsInstance + + :param end_date: Only include usage that occurred on or before this date, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param task_channel: Only calculate cumulative statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. TaskRouter will calculate statistics on up to 10,000 Tasks for any given threshold. + + :returns: The fetched WorkspaceCumulativeStatisticsInstance + """ + + data = values.of( + { + "EndDate": serialize.iso8601_datetime(end_date), + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkspaceCumulativeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkspaceCumulativeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceCumulativeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkspaceCumulativeStatisticsContext: + """ + Constructs a WorkspaceCumulativeStatisticsContext + + """ + return WorkspaceCumulativeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkspaceCumulativeStatisticsContext: + """ + Constructs a WorkspaceCumulativeStatisticsContext + + """ + return WorkspaceCumulativeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_real_time_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_real_time_statistics.py new file mode 100644 index 0000000..35b50c0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_real_time_statistics.py @@ -0,0 +1,249 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkspaceRealTimeStatisticsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workspace resource. + :ivar activity_statistics: The number of current Workers by Activity. + :ivar longest_task_waiting_age: The age of the longest waiting Task. + :ivar longest_task_waiting_sid: The SID of the longest waiting Task. + :ivar tasks_by_priority: The number of Tasks by priority. For example: `{\"0\": \"10\", \"99\": \"5\"}` shows 10 Tasks at priority 0 and 5 at priority 99. + :ivar tasks_by_status: The number of Tasks by their current status. For example: `{\"pending\": \"1\", \"reserved\": \"3\", \"assigned\": \"2\", \"completed\": \"5\"}`. + :ivar total_tasks: The total number of Tasks. + :ivar total_workers: The total number of Workers in the Workspace. + :ivar workspace_sid: The SID of the Workspace. + :ivar url: The absolute URL of the Workspace statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.activity_statistics: Optional[List[Dict[str, object]]] = payload.get( + "activity_statistics" + ) + self.longest_task_waiting_age: Optional[int] = deserialize.integer( + payload.get("longest_task_waiting_age") + ) + self.longest_task_waiting_sid: Optional[str] = payload.get( + "longest_task_waiting_sid" + ) + self.tasks_by_priority: Optional[Dict[str, object]] = payload.get( + "tasks_by_priority" + ) + self.tasks_by_status: Optional[Dict[str, object]] = payload.get( + "tasks_by_status" + ) + self.total_tasks: Optional[int] = deserialize.integer( + payload.get("total_tasks") + ) + self.total_workers: Optional[int] = deserialize.integer( + payload.get("total_workers") + ) + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkspaceRealTimeStatisticsContext] = None + + @property + def _proxy(self) -> "WorkspaceRealTimeStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkspaceRealTimeStatisticsContext for this WorkspaceRealTimeStatisticsInstance + """ + if self._context is None: + self._context = WorkspaceRealTimeStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkspaceRealTimeStatisticsInstance": + """ + Fetch the WorkspaceRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkspaceRealTimeStatisticsInstance + """ + return self._proxy.fetch( + task_channel=task_channel, + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> "WorkspaceRealTimeStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkspaceRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkspaceRealTimeStatisticsInstance + """ + return await self._proxy.fetch_async( + task_channel=task_channel, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkspaceRealTimeStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceRealTimeStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/RealTimeStatistics".format( + **self._solution + ) + + def fetch( + self, task_channel: Union[str, object] = values.unset + ) -> WorkspaceRealTimeStatisticsInstance: + """ + Fetch the WorkspaceRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkspaceRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkspaceRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, task_channel: Union[str, object] = values.unset + ) -> WorkspaceRealTimeStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkspaceRealTimeStatisticsInstance + + :param task_channel: Only calculate real-time statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + + :returns: The fetched WorkspaceRealTimeStatisticsInstance + """ + + data = values.of( + { + "TaskChannel": task_channel, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkspaceRealTimeStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class WorkspaceRealTimeStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceRealTimeStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkspaceRealTimeStatisticsContext: + """ + Constructs a WorkspaceRealTimeStatisticsContext + + """ + return WorkspaceRealTimeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkspaceRealTimeStatisticsContext: + """ + Constructs a WorkspaceRealTimeStatisticsContext + + """ + return WorkspaceRealTimeStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_statistics.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_statistics.py new file mode 100644 index 0000000..19eb885 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/taskrouter/v1/workspace/workspace_statistics.py @@ -0,0 +1,272 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Taskrouter + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class WorkspaceStatisticsInstance(InstanceResource): + """ + :ivar realtime: An object that contains the real-time statistics for the Workspace. + :ivar cumulative: An object that contains the cumulative statistics for the Workspace. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Workspace resource. + :ivar workspace_sid: The SID of the Workspace. + :ivar url: The absolute URL of the Workspace statistics resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], workspace_sid: str): + super().__init__(version) + + self.realtime: Optional[Dict[str, object]] = payload.get("realtime") + self.cumulative: Optional[Dict[str, object]] = payload.get("cumulative") + self.account_sid: Optional[str] = payload.get("account_sid") + self.workspace_sid: Optional[str] = payload.get("workspace_sid") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "workspace_sid": workspace_sid, + } + self._context: Optional[WorkspaceStatisticsContext] = None + + @property + def _proxy(self) -> "WorkspaceStatisticsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WorkspaceStatisticsContext for this WorkspaceStatisticsInstance + """ + if self._context is None: + self._context = WorkspaceStatisticsContext( + self._version, + workspace_sid=self._solution["workspace_sid"], + ) + return self._context + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkspaceStatisticsInstance": + """ + Fetch the WorkspaceStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkspaceStatisticsInstance + """ + return self._proxy.fetch( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> "WorkspaceStatisticsInstance": + """ + Asynchronous coroutine to fetch the WorkspaceStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkspaceStatisticsInstance + """ + return await self._proxy.fetch_async( + minutes=minutes, + start_date=start_date, + end_date=end_date, + task_channel=task_channel, + split_by_wait_time=split_by_wait_time, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkspaceStatisticsContext(InstanceContext): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceStatisticsContext + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + self._uri = "/Workspaces/{workspace_sid}/Statistics".format(**self._solution) + + def fetch( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkspaceStatisticsInstance: + """ + Fetch the WorkspaceStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkspaceStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return WorkspaceStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + async def fetch_async( + self, + minutes: Union[int, object] = values.unset, + start_date: Union[datetime, object] = values.unset, + end_date: Union[datetime, object] = values.unset, + task_channel: Union[str, object] = values.unset, + split_by_wait_time: Union[str, object] = values.unset, + ) -> WorkspaceStatisticsInstance: + """ + Asynchronous coroutine to fetch the WorkspaceStatisticsInstance + + :param minutes: Only calculate statistics since this many minutes in the past. The default 15 minutes. This is helpful for displaying statistics for the last 15 minutes, 240 minutes (4 hours), and 480 minutes (8 hours) to see trends. + :param start_date: Only calculate statistics from this date and time and later, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :param end_date: Only calculate statistics from this date and time and earlier, specified in GMT as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time. + :param task_channel: Only calculate statistics on this TaskChannel. Can be the TaskChannel's SID or its `unique_name`, such as `voice`, `sms`, or `default`. + :param split_by_wait_time: A comma separated list of values that describes the thresholds, in seconds, to calculate statistics on. For each threshold specified, the number of Tasks canceled and reservations accepted above and below the specified thresholds in seconds are computed. For example, `5,30` would show splits of Tasks that were canceled or accepted before and after 5 seconds and before and after 30 seconds. This can be used to show short abandoned Tasks or Tasks that failed to meet an SLA. + + :returns: The fetched WorkspaceStatisticsInstance + """ + + data = values.of( + { + "Minutes": minutes, + "StartDate": serialize.iso8601_datetime(start_date), + "EndDate": serialize.iso8601_datetime(end_date), + "TaskChannel": task_channel, + "SplitByWaitTime": split_by_wait_time, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return WorkspaceStatisticsInstance( + self._version, + payload, + workspace_sid=self._solution["workspace_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WorkspaceStatisticsList(ListResource): + + def __init__(self, version: Version, workspace_sid: str): + """ + Initialize the WorkspaceStatisticsList + + :param version: Version that contains the resource + :param workspace_sid: The SID of the Workspace to fetch. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "workspace_sid": workspace_sid, + } + + def get(self) -> WorkspaceStatisticsContext: + """ + Constructs a WorkspaceStatisticsContext + + """ + return WorkspaceStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __call__(self) -> WorkspaceStatisticsContext: + """ + Constructs a WorkspaceStatisticsContext + + """ + return WorkspaceStatisticsContext( + self._version, workspace_sid=self._solution["workspace_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/TrunkingBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/TrunkingBase.py new file mode 100644 index 0000000..bed8f9a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/TrunkingBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.trunking.v1 import V1 + + +class TrunkingBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Trunking Domain + + :returns: Domain for Trunking + """ + super().__init__(twilio, "https://trunking.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Trunking + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__init__.py new file mode 100644 index 0000000..14bd6be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__init__.py @@ -0,0 +1,15 @@ +from warnings import warn + +from twilio.rest.trunking.TrunkingBase import TrunkingBase +from twilio.rest.trunking.v1.trunk import TrunkList + + +class Trunking(TrunkingBase): + @property + def trunks(self) -> TrunkList: + warn( + "trunks is deprecated. Use v1.trunks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.trunks diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/TrunkingBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/TrunkingBase.cpython-310.pyc new file mode 100644 index 0000000..35a897b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/TrunkingBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..1aa9f49 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__init__.py new file mode 100644 index 0000000..af530b9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__init__.py @@ -0,0 +1,43 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.trunking.v1.trunk import TrunkList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Trunking + + :param domain: The Twilio.trunking domain + """ + super().__init__(domain, "v1") + self._trunks: Optional[TrunkList] = None + + @property + def trunks(self) -> TrunkList: + if self._trunks is None: + self._trunks = TrunkList(self) + return self._trunks + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..fee58d4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__init__.py new file mode 100644 index 0000000..16125ea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__init__.py @@ -0,0 +1,857 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.trunking.v1.trunk.credential_list import CredentialListList +from twilio.rest.trunking.v1.trunk.ip_access_control_list import IpAccessControlListList +from twilio.rest.trunking.v1.trunk.origination_url import OriginationUrlList +from twilio.rest.trunking.v1.trunk.phone_number import PhoneNumberList +from twilio.rest.trunking.v1.trunk.recording import RecordingList + + +class TrunkInstance(InstanceResource): + + class TransferCallerId(object): + FROM_TRANSFEREE = "from-transferee" + FROM_TRANSFEROR = "from-transferor" + + class TransferSetting(object): + DISABLE_ALL = "disable-all" + ENABLE_ALL = "enable-all" + SIP_ONLY = "sip-only" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Trunk resource. + :ivar domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :ivar disaster_recovery_method: The HTTP method we use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :ivar disaster_recovery_url: The URL we call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from this URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :ivar recording: The recording settings for the trunk. Can be: `do-not-record`, `record-from-ringing`, `record-from-answer`. If set to `record-from-ringing` or `record-from-answer`, all calls going through the trunk will be recorded. The only way to change recording parameters is on a sub-resource of a Trunk after it has been created. e.g.`/Trunks/[Trunk_SID]/Recording -XPOST -d'Mode=record-from-answer'`. See [Recording](https://www.twilio.com/docs/sip-trunking#recording) for more information. + :ivar transfer_mode: + :ivar transfer_caller_id: + :ivar cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :ivar auth_type: The types of authentication mapped to the domain. Can be: `IP_ACL` and `CREDENTIAL_LIST`. If both are mapped, the values are returned in a comma delimited list. If empty, the domain will not receive any traffic. + :ivar auth_type_set: Reserved. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sid: The unique string that we created to identify the Trunk resource. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.domain_name: Optional[str] = payload.get("domain_name") + self.disaster_recovery_method: Optional[str] = payload.get( + "disaster_recovery_method" + ) + self.disaster_recovery_url: Optional[str] = payload.get("disaster_recovery_url") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.secure: Optional[bool] = payload.get("secure") + self.recording: Optional[Dict[str, object]] = payload.get("recording") + self.transfer_mode: Optional["TrunkInstance.TransferSetting"] = payload.get( + "transfer_mode" + ) + self.transfer_caller_id: Optional["TrunkInstance.TransferCallerId"] = ( + payload.get("transfer_caller_id") + ) + self.cnam_lookup_enabled: Optional[bool] = payload.get("cnam_lookup_enabled") + self.auth_type: Optional[str] = payload.get("auth_type") + self.auth_type_set: Optional[List[str]] = payload.get("auth_type_set") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[TrunkContext] = None + + @property + def _proxy(self) -> "TrunkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrunkContext for this TrunkInstance + """ + if self._context is None: + self._context = TrunkContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TrunkInstance": + """ + Fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrunkInstance": + """ + Asynchronous coroutine to fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> "TrunkInstance": + """ + Update the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The updated TrunkInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + domain_name=domain_name, + disaster_recovery_url=disaster_recovery_url, + disaster_recovery_method=disaster_recovery_method, + transfer_mode=transfer_mode, + secure=secure, + cnam_lookup_enabled=cnam_lookup_enabled, + transfer_caller_id=transfer_caller_id, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> "TrunkInstance": + """ + Asynchronous coroutine to update the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The updated TrunkInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + domain_name=domain_name, + disaster_recovery_url=disaster_recovery_url, + disaster_recovery_method=disaster_recovery_method, + transfer_mode=transfer_mode, + secure=secure, + cnam_lookup_enabled=cnam_lookup_enabled, + transfer_caller_id=transfer_caller_id, + ) + + @property + def credentials_lists(self) -> CredentialListList: + """ + Access the credentials_lists + """ + return self._proxy.credentials_lists + + @property + def ip_access_control_lists(self) -> IpAccessControlListList: + """ + Access the ip_access_control_lists + """ + return self._proxy.ip_access_control_lists + + @property + def origination_urls(self) -> OriginationUrlList: + """ + Access the origination_urls + """ + return self._proxy.origination_urls + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + return self._proxy.phone_numbers + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + return self._proxy.recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrunkContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the TrunkContext + + :param version: Version that contains the resource + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Trunks/{sid}".format(**self._solution) + + self._credentials_lists: Optional[CredentialListList] = None + self._ip_access_control_lists: Optional[IpAccessControlListList] = None + self._origination_urls: Optional[OriginationUrlList] = None + self._phone_numbers: Optional[PhoneNumberList] = None + self._recordings: Optional[RecordingList] = None + + def delete(self) -> bool: + """ + Deletes the TrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TrunkInstance: + """ + Fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrunkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TrunkInstance: + """ + Asynchronous coroutine to fetch the TrunkInstance + + + :returns: The fetched TrunkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrunkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> TrunkInstance: + """ + Update the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The updated TrunkInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DomainName": domain_name, + "DisasterRecoveryUrl": disaster_recovery_url, + "DisasterRecoveryMethod": disaster_recovery_method, + "TransferMode": transfer_mode, + "Secure": serialize.boolean_to_string(secure), + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "TransferCallerId": transfer_caller_id, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> TrunkInstance: + """ + Asynchronous coroutine to update the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The updated TrunkInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "DomainName": domain_name, + "DisasterRecoveryUrl": disaster_recovery_url, + "DisasterRecoveryMethod": disaster_recovery_method, + "TransferMode": transfer_mode, + "Secure": serialize.boolean_to_string(secure), + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "TransferCallerId": transfer_caller_id, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def credentials_lists(self) -> CredentialListList: + """ + Access the credentials_lists + """ + if self._credentials_lists is None: + self._credentials_lists = CredentialListList( + self._version, + self._solution["sid"], + ) + return self._credentials_lists + + @property + def ip_access_control_lists(self) -> IpAccessControlListList: + """ + Access the ip_access_control_lists + """ + if self._ip_access_control_lists is None: + self._ip_access_control_lists = IpAccessControlListList( + self._version, + self._solution["sid"], + ) + return self._ip_access_control_lists + + @property + def origination_urls(self) -> OriginationUrlList: + """ + Access the origination_urls + """ + if self._origination_urls is None: + self._origination_urls = OriginationUrlList( + self._version, + self._solution["sid"], + ) + return self._origination_urls + + @property + def phone_numbers(self) -> PhoneNumberList: + """ + Access the phone_numbers + """ + if self._phone_numbers is None: + self._phone_numbers = PhoneNumberList( + self._version, + self._solution["sid"], + ) + return self._phone_numbers + + @property + def recordings(self) -> RecordingList: + """ + Access the recordings + """ + if self._recordings is None: + self._recordings = RecordingList( + self._version, + self._solution["sid"], + ) + return self._recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrunkPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TrunkInstance: + """ + Build an instance of TrunkInstance + + :param payload: Payload response from the API + """ + return TrunkInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TrunkList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TrunkList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Trunks" + + def create( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> TrunkInstance: + """ + Create the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The created TrunkInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "DomainName": domain_name, + "DisasterRecoveryUrl": disaster_recovery_url, + "DisasterRecoveryMethod": disaster_recovery_method, + "TransferMode": transfer_mode, + "Secure": serialize.boolean_to_string(secure), + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "TransferCallerId": transfer_caller_id, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance(self._version, payload) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + domain_name: Union[str, object] = values.unset, + disaster_recovery_url: Union[str, object] = values.unset, + disaster_recovery_method: Union[str, object] = values.unset, + transfer_mode: Union["TrunkInstance.TransferSetting", object] = values.unset, + secure: Union[bool, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + transfer_caller_id: Union[ + "TrunkInstance.TransferCallerId", object + ] = values.unset, + ) -> TrunkInstance: + """ + Asynchronously create the TrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param domain_name: The unique address you reserve on Twilio to which you route your SIP traffic. Domain names can contain letters, digits, and `-` and must end with `pstn.twilio.com`. See [Termination Settings](https://www.twilio.com/docs/sip-trunking#termination) for more information. + :param disaster_recovery_url: The URL we should call using the `disaster_recovery_method` if an error occurs while sending SIP traffic towards the configured Origination URL. We retrieve TwiML from the URL and execute the instructions like any other normal TwiML call. See [Disaster Recovery](https://www.twilio.com/docs/sip-trunking#disaster-recovery) for more information. + :param disaster_recovery_method: The HTTP method we should use to call the `disaster_recovery_url`. Can be: `GET` or `POST`. + :param transfer_mode: + :param secure: Whether Secure Trunking is enabled for the trunk. If enabled, all calls going through the trunk will be secure using SRTP for media and TLS for signaling. If disabled, then RTP will be used for media. See [Secure Trunking](https://www.twilio.com/docs/sip-trunking#securetrunking) for more information. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup should be enabled for the trunk. If enabled, all inbound calls to the SIP Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param transfer_caller_id: + + :returns: The created TrunkInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "DomainName": domain_name, + "DisasterRecoveryUrl": disaster_recovery_url, + "DisasterRecoveryMethod": disaster_recovery_method, + "TransferMode": transfer_mode, + "Secure": serialize.boolean_to_string(secure), + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "TransferCallerId": transfer_caller_id, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrunkInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TrunkInstance]: + """ + Streams TrunkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TrunkInstance]: + """ + Asynchronously streams TrunkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrunkInstance]: + """ + Lists TrunkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrunkInstance]: + """ + Asynchronously lists TrunkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrunkPage: + """ + Retrieve a single page of TrunkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrunkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TrunkPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrunkPage: + """ + Asynchronously retrieve a single page of TrunkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrunkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TrunkPage(self._version, response) + + def get_page(self, target_url: str) -> TrunkPage: + """ + Retrieve a specific page of TrunkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrunkInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TrunkPage(self._version, response) + + async def get_page_async(self, target_url: str) -> TrunkPage: + """ + Asynchronously retrieve a specific page of TrunkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrunkInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TrunkPage(self._version, response) + + def get(self, sid: str) -> TrunkContext: + """ + Constructs a TrunkContext + + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + return TrunkContext(self._version, sid=sid) + + def __call__(self, sid: str) -> TrunkContext: + """ + Constructs a TrunkContext + + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + return TrunkContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f7e681f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/credential_list.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/credential_list.cpython-310.pyc new file mode 100644 index 0000000..71714b0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/credential_list.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/ip_access_control_list.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/ip_access_control_list.cpython-310.pyc new file mode 100644 index 0000000..f5be999 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/ip_access_control_list.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/origination_url.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/origination_url.cpython-310.pyc new file mode 100644 index 0000000..d52affa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/origination_url.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/phone_number.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/phone_number.cpython-310.pyc new file mode 100644 index 0000000..c175d1b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/phone_number.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/recording.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/recording.cpython-310.pyc new file mode 100644 index 0000000..a247e6e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/__pycache__/recording.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/credential_list.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/credential_list.py new file mode 100644 index 0000000..7dc043d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/credential_list.py @@ -0,0 +1,516 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CredentialListInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CredentialList resource. + :ivar sid: The unique string that we created to identify the CredentialList resource. + :ivar trunk_sid: The SID of the Trunk the credential list in associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trunk_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid or self.sid, + } + self._context: Optional[CredentialListContext] = None + + @property + def _proxy(self) -> "CredentialListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CredentialListContext for this CredentialListInstance + """ + if self._context is None: + self._context = CredentialListContext( + self._version, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CredentialListInstance": + """ + Fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CredentialListInstance": + """ + Asynchronous coroutine to fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListContext(InstanceContext): + + def __init__(self, version: Version, trunk_sid: str, sid: str): + """ + Initialize the CredentialListContext + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to fetch the credential list. + :param sid: The unique string that we created to identify the CredentialList resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid, + } + self._uri = "/Trunks/{trunk_sid}/CredentialLists/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CredentialListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CredentialListInstance: + """ + Fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CredentialListInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CredentialListInstance: + """ + Asynchronous coroutine to fetch the CredentialListInstance + + + :returns: The fetched CredentialListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CredentialListInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CredentialListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CredentialListInstance: + """ + Build an instance of CredentialListInstance + + :param payload: Payload response from the API + """ + return CredentialListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CredentialListList(ListResource): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the CredentialListList + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to read the credential lists. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + self._uri = "/Trunks/{trunk_sid}/CredentialLists".format(**self._solution) + + def create(self, credential_list_sid: str) -> CredentialListInstance: + """ + Create the CredentialListInstance + + :param credential_list_sid: The SID of the [Credential List](https://www.twilio.com/docs/voice/sip/api/sip-credentiallist-resource) that you want to associate with the trunk. Once associated, we will authenticate access to the trunk against this list. + + :returns: The created CredentialListInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + async def create_async(self, credential_list_sid: str) -> CredentialListInstance: + """ + Asynchronously create the CredentialListInstance + + :param credential_list_sid: The SID of the [Credential List](https://www.twilio.com/docs/voice/sip/api/sip-credentiallist-resource) that you want to associate with the trunk. Once associated, we will authenticate access to the trunk against this list. + + :returns: The created CredentialListInstance + """ + + data = values.of( + { + "CredentialListSid": credential_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CredentialListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CredentialListInstance]: + """ + Streams CredentialListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CredentialListInstance]: + """ + Asynchronously streams CredentialListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListInstance]: + """ + Lists CredentialListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CredentialListInstance]: + """ + Asynchronously lists CredentialListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListPage: + """ + Retrieve a single page of CredentialListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CredentialListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CredentialListPage: + """ + Asynchronously retrieve a single page of CredentialListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CredentialListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CredentialListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CredentialListPage: + """ + Retrieve a specific page of CredentialListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CredentialListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CredentialListPage: + """ + Asynchronously retrieve a specific page of CredentialListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CredentialListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CredentialListPage(self._version, response, self._solution) + + def get(self, sid: str) -> CredentialListContext: + """ + Constructs a CredentialListContext + + :param sid: The unique string that we created to identify the CredentialList resource to fetch. + """ + return CredentialListContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __call__(self, sid: str) -> CredentialListContext: + """ + Constructs a CredentialListContext + + :param sid: The unique string that we created to identify the CredentialList resource to fetch. + """ + return CredentialListContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/ip_access_control_list.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/ip_access_control_list.py new file mode 100644 index 0000000..b361e7f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/ip_access_control_list.py @@ -0,0 +1,520 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class IpAccessControlListInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IpAccessControlList resource. + :ivar sid: The unique string that we created to identify the IpAccessControlList resource. + :ivar trunk_sid: The SID of the Trunk the resource is associated with. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trunk_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid or self.sid, + } + self._context: Optional[IpAccessControlListContext] = None + + @property + def _proxy(self) -> "IpAccessControlListContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpAccessControlListContext for this IpAccessControlListInstance + """ + if self._context is None: + self._context = IpAccessControlListContext( + self._version, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IpAccessControlListInstance": + """ + Fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpAccessControlListInstance": + """ + Asynchronous coroutine to fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAccessControlListContext(InstanceContext): + + def __init__(self, version: Version, trunk_sid: str, sid: str): + """ + Initialize the IpAccessControlListContext + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to fetch the IP Access Control List. + :param sid: The unique string that we created to identify the IpAccessControlList resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid, + } + self._uri = "/Trunks/{trunk_sid}/IpAccessControlLists/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpAccessControlListInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IpAccessControlListInstance: + """ + Fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpAccessControlListInstance: + """ + Asynchronous coroutine to fetch the IpAccessControlListInstance + + + :returns: The fetched IpAccessControlListInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpAccessControlListInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpAccessControlListPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IpAccessControlListInstance: + """ + Build an instance of IpAccessControlListInstance + + :param payload: Payload response from the API + """ + return IpAccessControlListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpAccessControlListList(ListResource): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the IpAccessControlListList + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to read the IP Access Control Lists. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + self._uri = "/Trunks/{trunk_sid}/IpAccessControlLists".format(**self._solution) + + def create(self, ip_access_control_list_sid: str) -> IpAccessControlListInstance: + """ + Create the IpAccessControlListInstance + + :param ip_access_control_list_sid: The SID of the [IP Access Control List](https://www.twilio.com/docs/voice/sip/api/sip-ipaccesscontrollist-resource) that you want to associate with the trunk. + + :returns: The created IpAccessControlListInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + async def create_async( + self, ip_access_control_list_sid: str + ) -> IpAccessControlListInstance: + """ + Asynchronously create the IpAccessControlListInstance + + :param ip_access_control_list_sid: The SID of the [IP Access Control List](https://www.twilio.com/docs/voice/sip/api/sip-ipaccesscontrollist-resource) that you want to associate with the trunk. + + :returns: The created IpAccessControlListInstance + """ + + data = values.of( + { + "IpAccessControlListSid": ip_access_control_list_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpAccessControlListInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpAccessControlListInstance]: + """ + Streams IpAccessControlListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpAccessControlListInstance]: + """ + Asynchronously streams IpAccessControlListInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListInstance]: + """ + Lists IpAccessControlListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpAccessControlListInstance]: + """ + Asynchronously lists IpAccessControlListInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListPage: + """ + Retrieve a single page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpAccessControlListPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpAccessControlListPage: + """ + Asynchronously retrieve a single page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpAccessControlListInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpAccessControlListPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> IpAccessControlListPage: + """ + Retrieve a specific page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpAccessControlListPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> IpAccessControlListPage: + """ + Asynchronously retrieve a specific page of IpAccessControlListInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpAccessControlListInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpAccessControlListPage(self._version, response, self._solution) + + def get(self, sid: str) -> IpAccessControlListContext: + """ + Constructs a IpAccessControlListContext + + :param sid: The unique string that we created to identify the IpAccessControlList resource to fetch. + """ + return IpAccessControlListContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __call__(self, sid: str) -> IpAccessControlListContext: + """ + Constructs a IpAccessControlListContext + + :param sid: The unique string that we created to identify the IpAccessControlList resource to fetch. + """ + return IpAccessControlListContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/origination_url.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/origination_url.py new file mode 100644 index 0000000..bcf0c52 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/origination_url.py @@ -0,0 +1,692 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class OriginationUrlInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the OriginationUrl resource. + :ivar sid: The unique string that we created to identify the OriginationUrl resource. + :ivar trunk_sid: The SID of the Trunk that owns the Origination URL. + :ivar weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :ivar enabled: Whether the URL is enabled. The default is `true`. + :ivar sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trunk_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.weight: Optional[int] = deserialize.integer(payload.get("weight")) + self.enabled: Optional[bool] = payload.get("enabled") + self.sip_url: Optional[str] = payload.get("sip_url") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.priority: Optional[int] = deserialize.integer(payload.get("priority")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid or self.sid, + } + self._context: Optional[OriginationUrlContext] = None + + @property + def _proxy(self) -> "OriginationUrlContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: OriginationUrlContext for this OriginationUrlInstance + """ + if self._context is None: + self._context = OriginationUrlContext( + self._version, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the OriginationUrlInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the OriginationUrlInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "OriginationUrlInstance": + """ + Fetch the OriginationUrlInstance + + + :returns: The fetched OriginationUrlInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "OriginationUrlInstance": + """ + Asynchronous coroutine to fetch the OriginationUrlInstance + + + :returns: The fetched OriginationUrlInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + weight: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sip_url: Union[str, object] = values.unset, + ) -> "OriginationUrlInstance": + """ + Update the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. `sips` is NOT supported. + + :returns: The updated OriginationUrlInstance + """ + return self._proxy.update( + weight=weight, + priority=priority, + enabled=enabled, + friendly_name=friendly_name, + sip_url=sip_url, + ) + + async def update_async( + self, + weight: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sip_url: Union[str, object] = values.unset, + ) -> "OriginationUrlInstance": + """ + Asynchronous coroutine to update the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. `sips` is NOT supported. + + :returns: The updated OriginationUrlInstance + """ + return await self._proxy.update_async( + weight=weight, + priority=priority, + enabled=enabled, + friendly_name=friendly_name, + sip_url=sip_url, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OriginationUrlContext(InstanceContext): + + def __init__(self, version: Version, trunk_sid: str, sid: str): + """ + Initialize the OriginationUrlContext + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to update the OriginationUrl. + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid, + } + self._uri = "/Trunks/{trunk_sid}/OriginationUrls/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the OriginationUrlInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the OriginationUrlInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> OriginationUrlInstance: + """ + Fetch the OriginationUrlInstance + + + :returns: The fetched OriginationUrlInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return OriginationUrlInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> OriginationUrlInstance: + """ + Asynchronous coroutine to fetch the OriginationUrlInstance + + + :returns: The fetched OriginationUrlInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return OriginationUrlInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + weight: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sip_url: Union[str, object] = values.unset, + ) -> OriginationUrlInstance: + """ + Update the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. `sips` is NOT supported. + + :returns: The updated OriginationUrlInstance + """ + data = values.of( + { + "Weight": weight, + "Priority": priority, + "Enabled": serialize.boolean_to_string(enabled), + "FriendlyName": friendly_name, + "SipUrl": sip_url, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return OriginationUrlInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + weight: Union[int, object] = values.unset, + priority: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + sip_url: Union[str, object] = values.unset, + ) -> OriginationUrlInstance: + """ + Asynchronous coroutine to update the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. `sips` is NOT supported. + + :returns: The updated OriginationUrlInstance + """ + data = values.of( + { + "Weight": weight, + "Priority": priority, + "Enabled": serialize.boolean_to_string(enabled), + "FriendlyName": friendly_name, + "SipUrl": sip_url, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return OriginationUrlInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class OriginationUrlPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> OriginationUrlInstance: + """ + Build an instance of OriginationUrlInstance + + :param payload: Payload response from the API + """ + return OriginationUrlInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class OriginationUrlList(ListResource): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the OriginationUrlList + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to read the OriginationUrl. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + self._uri = "/Trunks/{trunk_sid}/OriginationUrls".format(**self._solution) + + def create( + self, + weight: int, + priority: int, + enabled: bool, + friendly_name: str, + sip_url: str, + ) -> OriginationUrlInstance: + """ + Create the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. + + :returns: The created OriginationUrlInstance + """ + + data = values.of( + { + "Weight": weight, + "Priority": priority, + "Enabled": serialize.boolean_to_string(enabled), + "FriendlyName": friendly_name, + "SipUrl": sip_url, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return OriginationUrlInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + async def create_async( + self, + weight: int, + priority: int, + enabled: bool, + friendly_name: str, + sip_url: str, + ) -> OriginationUrlInstance: + """ + Asynchronously create the OriginationUrlInstance + + :param weight: The value that determines the relative share of the load the URI should receive compared to other URIs with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. URLs with higher values receive more load than those with lower ones with the same priority. + :param priority: The relative importance of the URI. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important URI. + :param enabled: Whether the URL is enabled. The default is `true`. + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 64 characters long. + :param sip_url: The SIP address you want Twilio to route your Origination calls to. This must be a `sip:` schema. + + :returns: The created OriginationUrlInstance + """ + + data = values.of( + { + "Weight": weight, + "Priority": priority, + "Enabled": serialize.boolean_to_string(enabled), + "FriendlyName": friendly_name, + "SipUrl": sip_url, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return OriginationUrlInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[OriginationUrlInstance]: + """ + Streams OriginationUrlInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[OriginationUrlInstance]: + """ + Asynchronously streams OriginationUrlInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OriginationUrlInstance]: + """ + Lists OriginationUrlInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[OriginationUrlInstance]: + """ + Asynchronously lists OriginationUrlInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OriginationUrlPage: + """ + Retrieve a single page of OriginationUrlInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OriginationUrlInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return OriginationUrlPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> OriginationUrlPage: + """ + Asynchronously retrieve a single page of OriginationUrlInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of OriginationUrlInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return OriginationUrlPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> OriginationUrlPage: + """ + Retrieve a specific page of OriginationUrlInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OriginationUrlInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return OriginationUrlPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> OriginationUrlPage: + """ + Asynchronously retrieve a specific page of OriginationUrlInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of OriginationUrlInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return OriginationUrlPage(self._version, response, self._solution) + + def get(self, sid: str) -> OriginationUrlContext: + """ + Constructs a OriginationUrlContext + + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + return OriginationUrlContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __call__(self, sid: str) -> OriginationUrlContext: + """ + Constructs a OriginationUrlContext + + :param sid: The unique string that we created to identify the OriginationUrl resource to update. + """ + return OriginationUrlContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/phone_number.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/phone_number.py new file mode 100644 index 0000000..7f0adbe --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/phone_number.py @@ -0,0 +1,567 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PhoneNumberInstance(InstanceResource): + + class AddressRequirement(object): + NONE = "none" + ANY = "any" + LOCAL = "local" + FOREIGN = "foreign" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the PhoneNumber resource. + :ivar address_requirements: + :ivar api_version: The API version used to start a new TwiML session. + :ivar beta: Whether the phone number is new to the Twilio platform. Can be: `true` or `false`. + :ivar capabilities: The set of Boolean properties that indicate whether a phone number can receive calls or messages. Capabilities are `Voice`, `SMS`, and `MMS` and each capability can be: `true` or `false`. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar links: The URLs of related resources. + :ivar phone_number: The phone number in [E.164](https://www.twilio.com/docs/glossary/what-e164) format, which consists of a + followed by the country code and subscriber number. + :ivar sid: The unique string that we created to identify the PhoneNumber resource. + :ivar sms_application_sid: The SID of the application that handles SMS messages sent to the phone number. If an `sms_application_sid` is present, we ignore all `sms_*_url` values and use those of the application. + :ivar sms_fallback_method: The HTTP method we use to call `sms_fallback_url`. Can be: `GET` or `POST`. + :ivar sms_fallback_url: The URL that we call using the `sms_fallback_method` when an error occurs while retrieving or executing the TwiML from `sms_url`. + :ivar sms_method: The HTTP method we use to call `sms_url`. Can be: `GET` or `POST`. + :ivar sms_url: The URL we call using the `sms_method` when the phone number receives an incoming SMS message. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be: `GET` or `POST`. + :ivar trunk_sid: The SID of the Trunk that handles calls to the phone number. If a `trunk_sid` is present, we ignore all of the voice URLs and voice applications and use those set on the Trunk. Setting a `trunk_sid` will automatically delete your `voice_application_sid` and vice versa. + :ivar url: The absolute URL of the resource. + :ivar voice_application_sid: The SID of the application that handles calls to the phone number. If a `voice_application_sid` is present, we ignore all of the voice URLs and use those set on the application. Setting a `voice_application_sid` will automatically delete your `trunk_sid` and vice versa. + :ivar voice_caller_id_lookup: Whether we look up the caller's caller-ID name from the CNAM database ($0.01 per look up). Can be: `true` or `false`. + :ivar voice_fallback_method: The HTTP method that we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call using the `voice_fallback_method` when an error occurs retrieving or executing the TwiML requested by `url`. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_url: The URL we call using the `voice_method` when the phone number receives a call. The `voice_url` is not be used if a `voice_application_sid` or a `trunk_sid` is set. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trunk_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.address_requirements: Optional[ + "PhoneNumberInstance.AddressRequirement" + ] = payload.get("address_requirements") + self.api_version: Optional[str] = payload.get("api_version") + self.beta: Optional[bool] = payload.get("beta") + self.capabilities: Optional[Dict[str, object]] = payload.get("capabilities") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.phone_number: Optional[str] = payload.get("phone_number") + self.sid: Optional[str] = payload.get("sid") + self.sms_application_sid: Optional[str] = payload.get("sms_application_sid") + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.trunk_sid: Optional[str] = payload.get("trunk_sid") + self.url: Optional[str] = payload.get("url") + self.voice_application_sid: Optional[str] = payload.get("voice_application_sid") + self.voice_caller_id_lookup: Optional[bool] = payload.get( + "voice_caller_id_lookup" + ) + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid or self.sid, + } + self._context: Optional[PhoneNumberContext] = None + + @property + def _proxy(self) -> "PhoneNumberContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PhoneNumberContext for this PhoneNumberInstance + """ + if self._context is None: + self._context = PhoneNumberContext( + self._version, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "PhoneNumberInstance": + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PhoneNumberInstance": + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberContext(InstanceContext): + + def __init__(self, version: Version, trunk_sid: str, sid: str): + """ + Initialize the PhoneNumberContext + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to fetch the PhoneNumber resource. + :param sid: The unique string that we created to identify the PhoneNumber resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + "sid": sid, + } + self._uri = "/Trunks/{trunk_sid}/PhoneNumbers/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the PhoneNumberInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> PhoneNumberInstance: + """ + Fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PhoneNumberInstance: + """ + Asynchronous coroutine to fetch the PhoneNumberInstance + + + :returns: The fetched PhoneNumberInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PhoneNumberInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PhoneNumberPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PhoneNumberInstance: + """ + Build an instance of PhoneNumberInstance + + :param payload: Payload response from the API + """ + return PhoneNumberInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PhoneNumberList(ListResource): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the PhoneNumberList + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to read the PhoneNumber resources. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + self._uri = "/Trunks/{trunk_sid}/PhoneNumbers".format(**self._solution) + + def create(self, phone_number_sid: str) -> PhoneNumberInstance: + """ + Create the PhoneNumberInstance + + :param phone_number_sid: The SID of the [Incoming Phone Number](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) that you want to associate with the trunk. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "PhoneNumberSid": phone_number_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + async def create_async(self, phone_number_sid: str) -> PhoneNumberInstance: + """ + Asynchronously create the PhoneNumberInstance + + :param phone_number_sid: The SID of the [Incoming Phone Number](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource) that you want to associate with the trunk. + + :returns: The created PhoneNumberInstance + """ + + data = values.of( + { + "PhoneNumberSid": phone_number_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return PhoneNumberInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PhoneNumberInstance]: + """ + Streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PhoneNumberInstance]: + """ + Asynchronously streams PhoneNumberInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PhoneNumberInstance]: + """ + Asynchronously lists PhoneNumberInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PhoneNumberPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PhoneNumberPage: + """ + Asynchronously retrieve a single page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PhoneNumberInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PhoneNumberPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PhoneNumberPage: + """ + Retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PhoneNumberPage: + """ + Asynchronously retrieve a specific page of PhoneNumberInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PhoneNumberInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PhoneNumberPage(self._version, response, self._solution) + + def get(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The unique string that we created to identify the PhoneNumber resource to fetch. + """ + return PhoneNumberContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __call__(self, sid: str) -> PhoneNumberContext: + """ + Constructs a PhoneNumberContext + + :param sid: The unique string that we created to identify the PhoneNumber resource to fetch. + """ + return PhoneNumberContext( + self._version, trunk_sid=self._solution["trunk_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/recording.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/recording.py new file mode 100644 index 0000000..1a431b4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trunking/v1/trunk/recording.py @@ -0,0 +1,293 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trunking + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class RecordingInstance(InstanceResource): + + class RecordingMode(object): + DO_NOT_RECORD = "do-not-record" + RECORD_FROM_RINGING = "record-from-ringing" + RECORD_FROM_ANSWER = "record-from-answer" + RECORD_FROM_RINGING_DUAL = "record-from-ringing-dual" + RECORD_FROM_ANSWER_DUAL = "record-from-answer-dual" + + class RecordingTrim(object): + TRIM_SILENCE = "trim-silence" + DO_NOT_TRIM = "do-not-trim" + + """ + :ivar mode: + :ivar trim: + """ + + def __init__(self, version: Version, payload: Dict[str, Any], trunk_sid: str): + super().__init__(version) + + self.mode: Optional["RecordingInstance.RecordingMode"] = payload.get("mode") + self.trim: Optional["RecordingInstance.RecordingTrim"] = payload.get("trim") + + self._solution = { + "trunk_sid": trunk_sid, + } + self._context: Optional[RecordingContext] = None + + @property + def _proxy(self) -> "RecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingContext for this RecordingInstance + """ + if self._context is None: + self._context = RecordingContext( + self._version, + trunk_sid=self._solution["trunk_sid"], + ) + return self._context + + def fetch(self) -> "RecordingInstance": + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RecordingInstance": + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + mode: Union["RecordingInstance.RecordingMode", object] = values.unset, + trim: Union["RecordingInstance.RecordingTrim", object] = values.unset, + ) -> "RecordingInstance": + """ + Update the RecordingInstance + + :param mode: + :param trim: + + :returns: The updated RecordingInstance + """ + return self._proxy.update( + mode=mode, + trim=trim, + ) + + async def update_async( + self, + mode: Union["RecordingInstance.RecordingMode", object] = values.unset, + trim: Union["RecordingInstance.RecordingTrim", object] = values.unset, + ) -> "RecordingInstance": + """ + Asynchronous coroutine to update the RecordingInstance + + :param mode: + :param trim: + + :returns: The updated RecordingInstance + """ + return await self._proxy.update_async( + mode=mode, + trim=trim, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingContext(InstanceContext): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the RecordingContext + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk that will have its recording settings updated. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + self._uri = "/Trunks/{trunk_sid}/Recording".format(**self._solution) + + def fetch(self) -> RecordingInstance: + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + ) + + async def fetch_async(self) -> RecordingInstance: + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + trunk_sid=self._solution["trunk_sid"], + ) + + def update( + self, + mode: Union["RecordingInstance.RecordingMode", object] = values.unset, + trim: Union["RecordingInstance.RecordingTrim", object] = values.unset, + ) -> RecordingInstance: + """ + Update the RecordingInstance + + :param mode: + :param trim: + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Mode": mode, + "Trim": trim, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + async def update_async( + self, + mode: Union["RecordingInstance.RecordingMode", object] = values.unset, + trim: Union["RecordingInstance.RecordingTrim", object] = values.unset, + ) -> RecordingInstance: + """ + Asynchronous coroutine to update the RecordingInstance + + :param mode: + :param trim: + + :returns: The updated RecordingInstance + """ + data = values.of( + { + "Mode": mode, + "Trim": trim, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingInstance( + self._version, payload, trunk_sid=self._solution["trunk_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingList(ListResource): + + def __init__(self, version: Version, trunk_sid: str): + """ + Initialize the RecordingList + + :param version: Version that contains the resource + :param trunk_sid: The SID of the Trunk from which to fetch the recording settings. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trunk_sid": trunk_sid, + } + + def get(self) -> RecordingContext: + """ + Constructs a RecordingContext + + """ + return RecordingContext(self._version, trunk_sid=self._solution["trunk_sid"]) + + def __call__(self) -> RecordingContext: + """ + Constructs a RecordingContext + + """ + return RecordingContext(self._version, trunk_sid=self._solution["trunk_sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/TrusthubBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/TrusthubBase.py new file mode 100644 index 0000000..97fd32f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/TrusthubBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.trusthub.v1 import V1 + + +class TrusthubBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Trusthub Domain + + :returns: Domain for Trusthub + """ + super().__init__(twilio, "https://trusthub.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Trusthub + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__init__.py new file mode 100644 index 0000000..3da2fcf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__init__.py @@ -0,0 +1,75 @@ +from warnings import warn + +from twilio.rest.trusthub.TrusthubBase import TrusthubBase +from twilio.rest.trusthub.v1.customer_profiles import CustomerProfilesList +from twilio.rest.trusthub.v1.end_user import EndUserList +from twilio.rest.trusthub.v1.end_user_type import EndUserTypeList +from twilio.rest.trusthub.v1.policies import PoliciesList +from twilio.rest.trusthub.v1.supporting_document import SupportingDocumentList +from twilio.rest.trusthub.v1.supporting_document_type import SupportingDocumentTypeList +from twilio.rest.trusthub.v1.trust_products import TrustProductsList + + +class Trusthub(TrusthubBase): + @property + def customer_profiles(self) -> CustomerProfilesList: + warn( + "customer_profiles is deprecated. Use v1.customer_profiles instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.customer_profiles + + @property + def end_users(self) -> EndUserList: + warn( + "end_users is deprecated. Use v1.end_users instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.end_users + + @property + def end_user_types(self) -> EndUserTypeList: + warn( + "end_user_types is deprecated. Use v1.end_user_types instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.end_user_types + + @property + def policies(self) -> PoliciesList: + warn( + "policies is deprecated. Use v1.policies instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.policies + + @property + def supporting_documents(self) -> SupportingDocumentList: + warn( + "supporting_documents is deprecated. Use v1.supporting_documents instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.supporting_documents + + @property + def supporting_document_types(self) -> SupportingDocumentTypeList: + warn( + "supporting_document_types is deprecated. Use v1.supporting_document_types instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.supporting_document_types + + @property + def trust_products(self) -> TrustProductsList: + warn( + "trust_products is deprecated. Use v1.trust_products instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.trust_products diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/TrusthubBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/TrusthubBase.cpython-310.pyc new file mode 100644 index 0000000..8fd6b90 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/TrusthubBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d9e5e1a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__init__.py new file mode 100644 index 0000000..596c5ea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__init__.py @@ -0,0 +1,125 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.trusthub.v1.compliance_inquiries import ComplianceInquiriesList +from twilio.rest.trusthub.v1.compliance_registration_inquiries import ( + ComplianceRegistrationInquiriesList, +) +from twilio.rest.trusthub.v1.compliance_tollfree_inquiries import ( + ComplianceTollfreeInquiriesList, +) +from twilio.rest.trusthub.v1.customer_profiles import CustomerProfilesList +from twilio.rest.trusthub.v1.end_user import EndUserList +from twilio.rest.trusthub.v1.end_user_type import EndUserTypeList +from twilio.rest.trusthub.v1.policies import PoliciesList +from twilio.rest.trusthub.v1.supporting_document import SupportingDocumentList +from twilio.rest.trusthub.v1.supporting_document_type import SupportingDocumentTypeList +from twilio.rest.trusthub.v1.trust_products import TrustProductsList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Trusthub + + :param domain: The Twilio.trusthub domain + """ + super().__init__(domain, "v1") + self._compliance_inquiries: Optional[ComplianceInquiriesList] = None + self._compliance_registration_inquiries: Optional[ + ComplianceRegistrationInquiriesList + ] = None + self._compliance_tollfree_inquiries: Optional[ + ComplianceTollfreeInquiriesList + ] = None + self._customer_profiles: Optional[CustomerProfilesList] = None + self._end_users: Optional[EndUserList] = None + self._end_user_types: Optional[EndUserTypeList] = None + self._policies: Optional[PoliciesList] = None + self._supporting_documents: Optional[SupportingDocumentList] = None + self._supporting_document_types: Optional[SupportingDocumentTypeList] = None + self._trust_products: Optional[TrustProductsList] = None + + @property + def compliance_inquiries(self) -> ComplianceInquiriesList: + if self._compliance_inquiries is None: + self._compliance_inquiries = ComplianceInquiriesList(self) + return self._compliance_inquiries + + @property + def compliance_registration_inquiries(self) -> ComplianceRegistrationInquiriesList: + if self._compliance_registration_inquiries is None: + self._compliance_registration_inquiries = ( + ComplianceRegistrationInquiriesList(self) + ) + return self._compliance_registration_inquiries + + @property + def compliance_tollfree_inquiries(self) -> ComplianceTollfreeInquiriesList: + if self._compliance_tollfree_inquiries is None: + self._compliance_tollfree_inquiries = ComplianceTollfreeInquiriesList(self) + return self._compliance_tollfree_inquiries + + @property + def customer_profiles(self) -> CustomerProfilesList: + if self._customer_profiles is None: + self._customer_profiles = CustomerProfilesList(self) + return self._customer_profiles + + @property + def end_users(self) -> EndUserList: + if self._end_users is None: + self._end_users = EndUserList(self) + return self._end_users + + @property + def end_user_types(self) -> EndUserTypeList: + if self._end_user_types is None: + self._end_user_types = EndUserTypeList(self) + return self._end_user_types + + @property + def policies(self) -> PoliciesList: + if self._policies is None: + self._policies = PoliciesList(self) + return self._policies + + @property + def supporting_documents(self) -> SupportingDocumentList: + if self._supporting_documents is None: + self._supporting_documents = SupportingDocumentList(self) + return self._supporting_documents + + @property + def supporting_document_types(self) -> SupportingDocumentTypeList: + if self._supporting_document_types is None: + self._supporting_document_types = SupportingDocumentTypeList(self) + return self._supporting_document_types + + @property + def trust_products(self) -> TrustProductsList: + if self._trust_products is None: + self._trust_products = TrustProductsList(self) + return self._trust_products + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..49184b3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_inquiries.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_inquiries.cpython-310.pyc new file mode 100644 index 0000000..3456b58 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_inquiries.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_registration_inquiries.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_registration_inquiries.cpython-310.pyc new file mode 100644 index 0000000..f2e144d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_registration_inquiries.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_tollfree_inquiries.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_tollfree_inquiries.cpython-310.pyc new file mode 100644 index 0000000..495bf33 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/compliance_tollfree_inquiries.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user.cpython-310.pyc new file mode 100644 index 0000000..258968d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user_type.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user_type.cpython-310.pyc new file mode 100644 index 0000000..f57f21f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/end_user_type.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/policies.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/policies.cpython-310.pyc new file mode 100644 index 0000000..a99ed96 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/policies.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document.cpython-310.pyc new file mode 100644 index 0000000..a1ce42a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document_type.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document_type.cpython-310.pyc new file mode 100644 index 0000000..82ff878 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/__pycache__/supporting_document_type.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_inquiries.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_inquiries.py new file mode 100644 index 0000000..b1b52a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_inquiries.py @@ -0,0 +1,272 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ComplianceInquiriesInstance(InstanceResource): + """ + :ivar inquiry_id: The unique ID used to start an embedded compliance registration session. + :ivar inquiry_session_token: The session token used to start an embedded compliance registration session. + :ivar customer_id: The CustomerID matching the Customer Profile that should be resumed or resubmitted for editing. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + customer_id: Optional[str] = None, + ): + super().__init__(version) + + self.inquiry_id: Optional[str] = payload.get("inquiry_id") + self.inquiry_session_token: Optional[str] = payload.get("inquiry_session_token") + self.customer_id: Optional[str] = payload.get("customer_id") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "customer_id": customer_id or self.customer_id, + } + self._context: Optional[ComplianceInquiriesContext] = None + + @property + def _proxy(self) -> "ComplianceInquiriesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ComplianceInquiriesContext for this ComplianceInquiriesInstance + """ + if self._context is None: + self._context = ComplianceInquiriesContext( + self._version, + customer_id=self._solution["customer_id"], + ) + return self._context + + def update(self, primary_profile_sid: str) -> "ComplianceInquiriesInstance": + """ + Update the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + + :returns: The updated ComplianceInquiriesInstance + """ + return self._proxy.update( + primary_profile_sid=primary_profile_sid, + ) + + async def update_async( + self, primary_profile_sid: str + ) -> "ComplianceInquiriesInstance": + """ + Asynchronous coroutine to update the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + + :returns: The updated ComplianceInquiriesInstance + """ + return await self._proxy.update_async( + primary_profile_sid=primary_profile_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ComplianceInquiriesContext(InstanceContext): + + def __init__(self, version: Version, customer_id: str): + """ + Initialize the ComplianceInquiriesContext + + :param version: Version that contains the resource + :param customer_id: The unique CustomerId matching the Customer Profile/Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Compliance Inquiry creation call. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_id": customer_id, + } + self._uri = "/ComplianceInquiries/Customers/{customer_id}/Initialize".format( + **self._solution + ) + + def update(self, primary_profile_sid: str) -> ComplianceInquiriesInstance: + """ + Update the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + + :returns: The updated ComplianceInquiriesInstance + """ + data = values.of( + { + "PrimaryProfileSid": primary_profile_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceInquiriesInstance( + self._version, payload, customer_id=self._solution["customer_id"] + ) + + async def update_async( + self, primary_profile_sid: str + ) -> ComplianceInquiriesInstance: + """ + Asynchronous coroutine to update the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + + :returns: The updated ComplianceInquiriesInstance + """ + data = values.of( + { + "PrimaryProfileSid": primary_profile_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceInquiriesInstance( + self._version, payload, customer_id=self._solution["customer_id"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ComplianceInquiriesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ComplianceInquiriesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ComplianceInquiries/Customers/Initialize" + + def create( + self, + primary_profile_sid: str, + notification_email: Union[str, object] = values.unset, + ) -> ComplianceInquiriesInstance: + """ + Create the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + :param notification_email: The email address that approval status updates will be sent to. If not specified, the email address associated with your primary customer profile will be used. + + :returns: The created ComplianceInquiriesInstance + """ + + data = values.of( + { + "PrimaryProfileSid": primary_profile_sid, + "NotificationEmail": notification_email, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceInquiriesInstance(self._version, payload) + + async def create_async( + self, + primary_profile_sid: str, + notification_email: Union[str, object] = values.unset, + ) -> ComplianceInquiriesInstance: + """ + Asynchronously create the ComplianceInquiriesInstance + + :param primary_profile_sid: The unique SID identifier of the Primary Customer Profile that should be used as a parent. Only necessary when creating a secondary Customer Profile. + :param notification_email: The email address that approval status updates will be sent to. If not specified, the email address associated with your primary customer profile will be used. + + :returns: The created ComplianceInquiriesInstance + """ + + data = values.of( + { + "PrimaryProfileSid": primary_profile_sid, + "NotificationEmail": notification_email, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceInquiriesInstance(self._version, payload) + + def get(self, customer_id: str) -> ComplianceInquiriesContext: + """ + Constructs a ComplianceInquiriesContext + + :param customer_id: The unique CustomerId matching the Customer Profile/Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Compliance Inquiry creation call. + """ + return ComplianceInquiriesContext(self._version, customer_id=customer_id) + + def __call__(self, customer_id: str) -> ComplianceInquiriesContext: + """ + Constructs a ComplianceInquiriesContext + + :param customer_id: The unique CustomerId matching the Customer Profile/Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Compliance Inquiry creation call. + """ + return ComplianceInquiriesContext(self._version, customer_id=customer_id) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_registration_inquiries.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_registration_inquiries.py new file mode 100644 index 0000000..c279bce --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_registration_inquiries.py @@ -0,0 +1,565 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ComplianceRegistrationInquiriesInstance(InstanceResource): + + class BusinessIdentityType(object): + DIRECT_CUSTOMER = "direct_customer" + ISV_RESELLER_OR_PARTNER = "isv_reseller_or_partner" + UNKNOWN = "unknown" + + class BusinessRegistrationAuthority(object): + UK_CRN = "UK:CRN" + US_EIN = "US:EIN" + CA_CBN = "CA:CBN" + AU_ACN = "AU:ACN" + OTHER = "Other" + + class EndUserType(object): + INDIVIDUAL = "Individual" + BUSINESS = "Business" + + class PhoneNumberType(object): + LOCAL = "local" + NATIONAL = "national" + MOBILE = "mobile" + TOLL_FREE = "toll-free" + + """ + :ivar inquiry_id: The unique ID used to start an embedded compliance registration session. + :ivar inquiry_session_token: The session token used to start an embedded compliance registration session. + :ivar registration_id: The RegistrationId matching the Registration Profile that should be resumed or resubmitted for editing. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + registration_id: Optional[str] = None, + ): + super().__init__(version) + + self.inquiry_id: Optional[str] = payload.get("inquiry_id") + self.inquiry_session_token: Optional[str] = payload.get("inquiry_session_token") + self.registration_id: Optional[str] = payload.get("registration_id") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "registration_id": registration_id or self.registration_id, + } + self._context: Optional[ComplianceRegistrationInquiriesContext] = None + + @property + def _proxy(self) -> "ComplianceRegistrationInquiriesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ComplianceRegistrationInquiriesContext for this ComplianceRegistrationInquiriesInstance + """ + if self._context is None: + self._context = ComplianceRegistrationInquiriesContext( + self._version, + registration_id=self._solution["registration_id"], + ) + return self._context + + def update( + self, + is_isv_embed: Union[bool, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> "ComplianceRegistrationInquiriesInstance": + """ + Update the ComplianceRegistrationInquiriesInstance + + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The updated ComplianceRegistrationInquiriesInstance + """ + return self._proxy.update( + is_isv_embed=is_isv_embed, + theme_set_id=theme_set_id, + ) + + async def update_async( + self, + is_isv_embed: Union[bool, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> "ComplianceRegistrationInquiriesInstance": + """ + Asynchronous coroutine to update the ComplianceRegistrationInquiriesInstance + + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The updated ComplianceRegistrationInquiriesInstance + """ + return await self._proxy.update_async( + is_isv_embed=is_isv_embed, + theme_set_id=theme_set_id, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class ComplianceRegistrationInquiriesContext(InstanceContext): + + def __init__(self, version: Version, registration_id: str): + """ + Initialize the ComplianceRegistrationInquiriesContext + + :param version: Version that contains the resource + :param registration_id: The unique RegistrationId matching the Regulatory Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Regulatory Compliance Inquiry creation call. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "registration_id": registration_id, + } + self._uri = "/ComplianceInquiries/Registration/{registration_id}/RegulatoryCompliance/GB/Initialize".format( + **self._solution + ) + + def update( + self, + is_isv_embed: Union[bool, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> ComplianceRegistrationInquiriesInstance: + """ + Update the ComplianceRegistrationInquiriesInstance + + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The updated ComplianceRegistrationInquiriesInstance + """ + data = values.of( + { + "IsIsvEmbed": serialize.boolean_to_string(is_isv_embed), + "ThemeSetId": theme_set_id, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceRegistrationInquiriesInstance( + self._version, payload, registration_id=self._solution["registration_id"] + ) + + async def update_async( + self, + is_isv_embed: Union[bool, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> ComplianceRegistrationInquiriesInstance: + """ + Asynchronous coroutine to update the ComplianceRegistrationInquiriesInstance + + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The updated ComplianceRegistrationInquiriesInstance + """ + data = values.of( + { + "IsIsvEmbed": serialize.boolean_to_string(is_isv_embed), + "ThemeSetId": theme_set_id, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceRegistrationInquiriesInstance( + self._version, payload, registration_id=self._solution["registration_id"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class ComplianceRegistrationInquiriesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ComplianceRegistrationInquiriesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = ( + "/ComplianceInquiries/Registration/RegulatoryCompliance/GB/Initialize" + ) + + def create( + self, + end_user_type: "ComplianceRegistrationInquiriesInstance.EndUserType", + phone_number_type: "ComplianceRegistrationInquiriesInstance.PhoneNumberType", + business_identity_type: Union[ + "ComplianceRegistrationInquiriesInstance.BusinessIdentityType", object + ] = values.unset, + business_registration_authority: Union[ + "ComplianceRegistrationInquiriesInstance.BusinessRegistrationAuthority", + object, + ] = values.unset, + business_legal_name: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + accepted_notification_receipt: Union[bool, object] = values.unset, + business_registration_number: Union[str, object] = values.unset, + business_website_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + authorized_representative1_first_name: Union[str, object] = values.unset, + authorized_representative1_last_name: Union[str, object] = values.unset, + authorized_representative1_phone: Union[str, object] = values.unset, + authorized_representative1_email: Union[str, object] = values.unset, + authorized_representative1_date_of_birth: Union[str, object] = values.unset, + address_street: Union[str, object] = values.unset, + address_street_secondary: Union[str, object] = values.unset, + address_city: Union[str, object] = values.unset, + address_subdivision: Union[str, object] = values.unset, + address_postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + emergency_address_street: Union[str, object] = values.unset, + emergency_address_street_secondary: Union[str, object] = values.unset, + emergency_address_city: Union[str, object] = values.unset, + emergency_address_subdivision: Union[str, object] = values.unset, + emergency_address_postal_code: Union[str, object] = values.unset, + emergency_address_country_code: Union[str, object] = values.unset, + use_address_as_emergency_address: Union[bool, object] = values.unset, + file_name: Union[str, object] = values.unset, + file: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + individual_email: Union[str, object] = values.unset, + individual_phone: Union[str, object] = values.unset, + is_isv_embed: Union[bool, object] = values.unset, + isv_registering_for_self_or_tenant: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> ComplianceRegistrationInquiriesInstance: + """ + Create the ComplianceRegistrationInquiriesInstance + + :param end_user_type: + :param phone_number_type: + :param business_identity_type: + :param business_registration_authority: + :param business_legal_name: he name of the business or organization using the Tollfree number. + :param notification_email: he email address to receive the notification about the verification result. + :param accepted_notification_receipt: The email address to receive the notification about the verification result. + :param business_registration_number: Business registration number of the business + :param business_website_url: The URL of the business website + :param friendly_name: Friendly name for your business information + :param authorized_representative1_first_name: First name of the authorized representative + :param authorized_representative1_last_name: Last name of the authorized representative + :param authorized_representative1_phone: Phone number of the authorized representative + :param authorized_representative1_email: Email address of the authorized representative + :param authorized_representative1_date_of_birth: Birthdate of the authorized representative + :param address_street: Street address of the business + :param address_street_secondary: Street address of the business + :param address_city: City of the business + :param address_subdivision: State or province of the business + :param address_postal_code: Postal code of the business + :param address_country_code: Country code of the business + :param emergency_address_street: Street address of the business + :param emergency_address_street_secondary: Street address of the business + :param emergency_address_city: City of the business + :param emergency_address_subdivision: State or province of the business + :param emergency_address_postal_code: Postal code of the business + :param emergency_address_country_code: Country code of the business + :param use_address_as_emergency_address: Use the business address as the emergency address + :param file_name: The name of the verification document to upload + :param file: The verification document to upload + :param first_name: The first name of the Individual User. + :param last_name: The last name of the Individual User. + :param date_of_birth: The date of birth of the Individual User. + :param individual_email: The email address of the Individual User. + :param individual_phone: The phone number of the Individual User. + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param isv_registering_for_self_or_tenant: Indicates if the isv registering for self or tenant. + :param status_callback_url: The url we call to inform you of bundle changes. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The created ComplianceRegistrationInquiriesInstance + """ + + data = values.of( + { + "EndUserType": end_user_type, + "PhoneNumberType": phone_number_type, + "BusinessIdentityType": business_identity_type, + "BusinessRegistrationAuthority": business_registration_authority, + "BusinessLegalName": business_legal_name, + "NotificationEmail": notification_email, + "AcceptedNotificationReceipt": serialize.boolean_to_string( + accepted_notification_receipt + ), + "BusinessRegistrationNumber": business_registration_number, + "BusinessWebsiteUrl": business_website_url, + "FriendlyName": friendly_name, + "AuthorizedRepresentative1FirstName": authorized_representative1_first_name, + "AuthorizedRepresentative1LastName": authorized_representative1_last_name, + "AuthorizedRepresentative1Phone": authorized_representative1_phone, + "AuthorizedRepresentative1Email": authorized_representative1_email, + "AuthorizedRepresentative1DateOfBirth": authorized_representative1_date_of_birth, + "AddressStreet": address_street, + "AddressStreetSecondary": address_street_secondary, + "AddressCity": address_city, + "AddressSubdivision": address_subdivision, + "AddressPostalCode": address_postal_code, + "AddressCountryCode": address_country_code, + "EmergencyAddressStreet": emergency_address_street, + "EmergencyAddressStreetSecondary": emergency_address_street_secondary, + "EmergencyAddressCity": emergency_address_city, + "EmergencyAddressSubdivision": emergency_address_subdivision, + "EmergencyAddressPostalCode": emergency_address_postal_code, + "EmergencyAddressCountryCode": emergency_address_country_code, + "UseAddressAsEmergencyAddress": serialize.boolean_to_string( + use_address_as_emergency_address + ), + "FileName": file_name, + "File": file, + "FirstName": first_name, + "LastName": last_name, + "DateOfBirth": date_of_birth, + "IndividualEmail": individual_email, + "IndividualPhone": individual_phone, + "IsIsvEmbed": serialize.boolean_to_string(is_isv_embed), + "IsvRegisteringForSelfOrTenant": isv_registering_for_self_or_tenant, + "StatusCallbackUrl": status_callback_url, + "ThemeSetId": theme_set_id, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceRegistrationInquiriesInstance(self._version, payload) + + async def create_async( + self, + end_user_type: "ComplianceRegistrationInquiriesInstance.EndUserType", + phone_number_type: "ComplianceRegistrationInquiriesInstance.PhoneNumberType", + business_identity_type: Union[ + "ComplianceRegistrationInquiriesInstance.BusinessIdentityType", object + ] = values.unset, + business_registration_authority: Union[ + "ComplianceRegistrationInquiriesInstance.BusinessRegistrationAuthority", + object, + ] = values.unset, + business_legal_name: Union[str, object] = values.unset, + notification_email: Union[str, object] = values.unset, + accepted_notification_receipt: Union[bool, object] = values.unset, + business_registration_number: Union[str, object] = values.unset, + business_website_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + authorized_representative1_first_name: Union[str, object] = values.unset, + authorized_representative1_last_name: Union[str, object] = values.unset, + authorized_representative1_phone: Union[str, object] = values.unset, + authorized_representative1_email: Union[str, object] = values.unset, + authorized_representative1_date_of_birth: Union[str, object] = values.unset, + address_street: Union[str, object] = values.unset, + address_street_secondary: Union[str, object] = values.unset, + address_city: Union[str, object] = values.unset, + address_subdivision: Union[str, object] = values.unset, + address_postal_code: Union[str, object] = values.unset, + address_country_code: Union[str, object] = values.unset, + emergency_address_street: Union[str, object] = values.unset, + emergency_address_street_secondary: Union[str, object] = values.unset, + emergency_address_city: Union[str, object] = values.unset, + emergency_address_subdivision: Union[str, object] = values.unset, + emergency_address_postal_code: Union[str, object] = values.unset, + emergency_address_country_code: Union[str, object] = values.unset, + use_address_as_emergency_address: Union[bool, object] = values.unset, + file_name: Union[str, object] = values.unset, + file: Union[str, object] = values.unset, + first_name: Union[str, object] = values.unset, + last_name: Union[str, object] = values.unset, + date_of_birth: Union[str, object] = values.unset, + individual_email: Union[str, object] = values.unset, + individual_phone: Union[str, object] = values.unset, + is_isv_embed: Union[bool, object] = values.unset, + isv_registering_for_self_or_tenant: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + theme_set_id: Union[str, object] = values.unset, + ) -> ComplianceRegistrationInquiriesInstance: + """ + Asynchronously create the ComplianceRegistrationInquiriesInstance + + :param end_user_type: + :param phone_number_type: + :param business_identity_type: + :param business_registration_authority: + :param business_legal_name: he name of the business or organization using the Tollfree number. + :param notification_email: he email address to receive the notification about the verification result. + :param accepted_notification_receipt: The email address to receive the notification about the verification result. + :param business_registration_number: Business registration number of the business + :param business_website_url: The URL of the business website + :param friendly_name: Friendly name for your business information + :param authorized_representative1_first_name: First name of the authorized representative + :param authorized_representative1_last_name: Last name of the authorized representative + :param authorized_representative1_phone: Phone number of the authorized representative + :param authorized_representative1_email: Email address of the authorized representative + :param authorized_representative1_date_of_birth: Birthdate of the authorized representative + :param address_street: Street address of the business + :param address_street_secondary: Street address of the business + :param address_city: City of the business + :param address_subdivision: State or province of the business + :param address_postal_code: Postal code of the business + :param address_country_code: Country code of the business + :param emergency_address_street: Street address of the business + :param emergency_address_street_secondary: Street address of the business + :param emergency_address_city: City of the business + :param emergency_address_subdivision: State or province of the business + :param emergency_address_postal_code: Postal code of the business + :param emergency_address_country_code: Country code of the business + :param use_address_as_emergency_address: Use the business address as the emergency address + :param file_name: The name of the verification document to upload + :param file: The verification document to upload + :param first_name: The first name of the Individual User. + :param last_name: The last name of the Individual User. + :param date_of_birth: The date of birth of the Individual User. + :param individual_email: The email address of the Individual User. + :param individual_phone: The phone number of the Individual User. + :param is_isv_embed: Indicates if the inquiry is being started from an ISV embedded component. + :param isv_registering_for_self_or_tenant: Indicates if the isv registering for self or tenant. + :param status_callback_url: The url we call to inform you of bundle changes. + :param theme_set_id: Theme id for styling the inquiry form. + + :returns: The created ComplianceRegistrationInquiriesInstance + """ + + data = values.of( + { + "EndUserType": end_user_type, + "PhoneNumberType": phone_number_type, + "BusinessIdentityType": business_identity_type, + "BusinessRegistrationAuthority": business_registration_authority, + "BusinessLegalName": business_legal_name, + "NotificationEmail": notification_email, + "AcceptedNotificationReceipt": serialize.boolean_to_string( + accepted_notification_receipt + ), + "BusinessRegistrationNumber": business_registration_number, + "BusinessWebsiteUrl": business_website_url, + "FriendlyName": friendly_name, + "AuthorizedRepresentative1FirstName": authorized_representative1_first_name, + "AuthorizedRepresentative1LastName": authorized_representative1_last_name, + "AuthorizedRepresentative1Phone": authorized_representative1_phone, + "AuthorizedRepresentative1Email": authorized_representative1_email, + "AuthorizedRepresentative1DateOfBirth": authorized_representative1_date_of_birth, + "AddressStreet": address_street, + "AddressStreetSecondary": address_street_secondary, + "AddressCity": address_city, + "AddressSubdivision": address_subdivision, + "AddressPostalCode": address_postal_code, + "AddressCountryCode": address_country_code, + "EmergencyAddressStreet": emergency_address_street, + "EmergencyAddressStreetSecondary": emergency_address_street_secondary, + "EmergencyAddressCity": emergency_address_city, + "EmergencyAddressSubdivision": emergency_address_subdivision, + "EmergencyAddressPostalCode": emergency_address_postal_code, + "EmergencyAddressCountryCode": emergency_address_country_code, + "UseAddressAsEmergencyAddress": serialize.boolean_to_string( + use_address_as_emergency_address + ), + "FileName": file_name, + "File": file, + "FirstName": first_name, + "LastName": last_name, + "DateOfBirth": date_of_birth, + "IndividualEmail": individual_email, + "IndividualPhone": individual_phone, + "IsIsvEmbed": serialize.boolean_to_string(is_isv_embed), + "IsvRegisteringForSelfOrTenant": isv_registering_for_self_or_tenant, + "StatusCallbackUrl": status_callback_url, + "ThemeSetId": theme_set_id, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceRegistrationInquiriesInstance(self._version, payload) + + def get(self, registration_id: str) -> ComplianceRegistrationInquiriesContext: + """ + Constructs a ComplianceRegistrationInquiriesContext + + :param registration_id: The unique RegistrationId matching the Regulatory Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Regulatory Compliance Inquiry creation call. + """ + return ComplianceRegistrationInquiriesContext( + self._version, registration_id=registration_id + ) + + def __call__(self, registration_id: str) -> ComplianceRegistrationInquiriesContext: + """ + Constructs a ComplianceRegistrationInquiriesContext + + :param registration_id: The unique RegistrationId matching the Regulatory Compliance Inquiry that should be resumed or resubmitted. This value will have been returned by the initial Regulatory Compliance Inquiry creation call. + """ + return ComplianceRegistrationInquiriesContext( + self._version, registration_id=registration_id + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_tollfree_inquiries.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_tollfree_inquiries.py new file mode 100644 index 0000000..6c9d5b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/compliance_tollfree_inquiries.py @@ -0,0 +1,252 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ComplianceTollfreeInquiriesInstance(InstanceResource): + + class OptInType(object): + VERBAL = "VERBAL" + WEB_FORM = "WEB_FORM" + PAPER_FORM = "PAPER_FORM" + VIA_TEXT = "VIA_TEXT" + MOBILE_QR_CODE = "MOBILE_QR_CODE" + + """ + :ivar inquiry_id: The unique ID used to start an embedded compliance registration session. + :ivar inquiry_session_token: The session token used to start an embedded compliance registration session. + :ivar registration_id: The TolfreeId matching the Tollfree Profile that should be resumed or resubmitted for editing. + :ivar url: The URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.inquiry_id: Optional[str] = payload.get("inquiry_id") + self.inquiry_session_token: Optional[str] = payload.get("inquiry_session_token") + self.registration_id: Optional[str] = payload.get("registration_id") + self.url: Optional[str] = payload.get("url") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class ComplianceTollfreeInquiriesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ComplianceTollfreeInquiriesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ComplianceInquiries/Tollfree/Initialize" + + def create( + self, + tollfree_phone_number: str, + notification_email: str, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "ComplianceTollfreeInquiriesInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + ) -> ComplianceTollfreeInquiriesInstance: + """ + Create the ComplianceTollfreeInquiriesInstance + + :param tollfree_phone_number: The Tollfree phone number to be verified + :param notification_email: The email address to receive the notification about the verification result. + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The phone number of the contact for the business or organization using the Tollfree number. + + :returns: The created ComplianceTollfreeInquiriesInstance + """ + + data = values.of( + { + "TollfreePhoneNumber": tollfree_phone_number, + "NotificationEmail": notification_email, + "BusinessName": business_name, + "BusinessWebsite": business_website, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceTollfreeInquiriesInstance(self._version, payload) + + async def create_async( + self, + tollfree_phone_number: str, + notification_email: str, + business_name: Union[str, object] = values.unset, + business_website: Union[str, object] = values.unset, + use_case_categories: Union[List[str], object] = values.unset, + use_case_summary: Union[str, object] = values.unset, + production_message_sample: Union[str, object] = values.unset, + opt_in_image_urls: Union[List[str], object] = values.unset, + opt_in_type: Union[ + "ComplianceTollfreeInquiriesInstance.OptInType", object + ] = values.unset, + message_volume: Union[str, object] = values.unset, + business_street_address: Union[str, object] = values.unset, + business_street_address2: Union[str, object] = values.unset, + business_city: Union[str, object] = values.unset, + business_state_province_region: Union[str, object] = values.unset, + business_postal_code: Union[str, object] = values.unset, + business_country: Union[str, object] = values.unset, + additional_information: Union[str, object] = values.unset, + business_contact_first_name: Union[str, object] = values.unset, + business_contact_last_name: Union[str, object] = values.unset, + business_contact_email: Union[str, object] = values.unset, + business_contact_phone: Union[str, object] = values.unset, + ) -> ComplianceTollfreeInquiriesInstance: + """ + Asynchronously create the ComplianceTollfreeInquiriesInstance + + :param tollfree_phone_number: The Tollfree phone number to be verified + :param notification_email: The email address to receive the notification about the verification result. + :param business_name: The name of the business or organization using the Tollfree number. + :param business_website: The website of the business or organization using the Tollfree number. + :param use_case_categories: The category of the use case for the Tollfree Number. List as many are applicable.. + :param use_case_summary: Use this to further explain how messaging is used by the business or organization. + :param production_message_sample: An example of message content, i.e. a sample message. + :param opt_in_image_urls: Link to an image that shows the opt-in workflow. Multiple images allowed and must be a publicly hosted URL. + :param opt_in_type: + :param message_volume: Estimate monthly volume of messages from the Tollfree Number. + :param business_street_address: The address of the business or organization using the Tollfree number. + :param business_street_address2: The address of the business or organization using the Tollfree number. + :param business_city: The city of the business or organization using the Tollfree number. + :param business_state_province_region: The state/province/region of the business or organization using the Tollfree number. + :param business_postal_code: The postal code of the business or organization using the Tollfree number. + :param business_country: The country of the business or organization using the Tollfree number. + :param additional_information: Additional information to be provided for verification. + :param business_contact_first_name: The first name of the contact for the business or organization using the Tollfree number. + :param business_contact_last_name: The last name of the contact for the business or organization using the Tollfree number. + :param business_contact_email: The email address of the contact for the business or organization using the Tollfree number. + :param business_contact_phone: The phone number of the contact for the business or organization using the Tollfree number. + + :returns: The created ComplianceTollfreeInquiriesInstance + """ + + data = values.of( + { + "TollfreePhoneNumber": tollfree_phone_number, + "NotificationEmail": notification_email, + "BusinessName": business_name, + "BusinessWebsite": business_website, + "UseCaseCategories": serialize.map(use_case_categories, lambda e: e), + "UseCaseSummary": use_case_summary, + "ProductionMessageSample": production_message_sample, + "OptInImageUrls": serialize.map(opt_in_image_urls, lambda e: e), + "OptInType": opt_in_type, + "MessageVolume": message_volume, + "BusinessStreetAddress": business_street_address, + "BusinessStreetAddress2": business_street_address2, + "BusinessCity": business_city, + "BusinessStateProvinceRegion": business_state_province_region, + "BusinessPostalCode": business_postal_code, + "BusinessCountry": business_country, + "AdditionalInformation": additional_information, + "BusinessContactFirstName": business_contact_first_name, + "BusinessContactLastName": business_contact_last_name, + "BusinessContactEmail": business_contact_email, + "BusinessContactPhone": business_contact_phone, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ComplianceTollfreeInquiriesInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__init__.py new file mode 100644 index 0000000..a16ae0e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__init__.py @@ -0,0 +1,801 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.trusthub.v1.customer_profiles.customer_profiles_channel_endpoint_assignment import ( + CustomerProfilesChannelEndpointAssignmentList, +) +from twilio.rest.trusthub.v1.customer_profiles.customer_profiles_entity_assignments import ( + CustomerProfilesEntityAssignmentsList, +) +from twilio.rest.trusthub.v1.customer_profiles.customer_profiles_evaluations import ( + CustomerProfilesEvaluationsList, +) + + +class CustomerProfilesInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + IN_REVIEW = "in-review" + TWILIO_REJECTED = "twilio-rejected" + TWILIO_APPROVED = "twilio-approved" + + """ + :ivar sid: The unique string that we created to identify the Customer-Profile resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Customer-Profile resource. + :ivar policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar status: + :ivar valid_until: The date and time in GMT in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format when the resource will be valid until. + :ivar email: The email address that will receive updates when the Customer-Profile resource changes status. + :ivar status_callback: The URL we call to inform your application of status changes. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Customer-Profile resource. + :ivar links: The URLs of the Assigned Items of the Customer-Profile resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.policy_sid: Optional[str] = payload.get("policy_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["CustomerProfilesInstance.Status"] = payload.get("status") + self.valid_until: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("valid_until") + ) + self.email: Optional[str] = payload.get("email") + self.status_callback: Optional[str] = payload.get("status_callback") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CustomerProfilesContext] = None + + @property + def _proxy(self) -> "CustomerProfilesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CustomerProfilesContext for this CustomerProfilesInstance + """ + if self._context is None: + self._context = CustomerProfilesContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CustomerProfilesInstance": + """ + Fetch the CustomerProfilesInstance + + + :returns: The fetched CustomerProfilesInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CustomerProfilesInstance": + """ + Asynchronous coroutine to fetch the CustomerProfilesInstance + + + :returns: The fetched CustomerProfilesInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "CustomerProfilesInstance": + """ + Update the CustomerProfilesInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + + :returns: The updated CustomerProfilesInstance + """ + return self._proxy.update( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + async def update_async( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "CustomerProfilesInstance": + """ + Asynchronous coroutine to update the CustomerProfilesInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + + :returns: The updated CustomerProfilesInstance + """ + return await self._proxy.update_async( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + @property + def customer_profiles_channel_endpoint_assignment( + self, + ) -> CustomerProfilesChannelEndpointAssignmentList: + """ + Access the customer_profiles_channel_endpoint_assignment + """ + return self._proxy.customer_profiles_channel_endpoint_assignment + + @property + def customer_profiles_entity_assignments( + self, + ) -> CustomerProfilesEntityAssignmentsList: + """ + Access the customer_profiles_entity_assignments + """ + return self._proxy.customer_profiles_entity_assignments + + @property + def customer_profiles_evaluations(self) -> CustomerProfilesEvaluationsList: + """ + Access the customer_profiles_evaluations + """ + return self._proxy.customer_profiles_evaluations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CustomerProfilesContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CustomerProfilesContext + + :param version: Version that contains the resource + :param sid: The unique string that we created to identify the Customer-Profile resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/CustomerProfiles/{sid}".format(**self._solution) + + self._customer_profiles_channel_endpoint_assignment: Optional[ + CustomerProfilesChannelEndpointAssignmentList + ] = None + self._customer_profiles_entity_assignments: Optional[ + CustomerProfilesEntityAssignmentsList + ] = None + self._customer_profiles_evaluations: Optional[ + CustomerProfilesEvaluationsList + ] = None + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CustomerProfilesInstance: + """ + Fetch the CustomerProfilesInstance + + + :returns: The fetched CustomerProfilesInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CustomerProfilesInstance: + """ + Asynchronous coroutine to fetch the CustomerProfilesInstance + + + :returns: The fetched CustomerProfilesInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> CustomerProfilesInstance: + """ + Update the CustomerProfilesInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + + :returns: The updated CustomerProfilesInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> CustomerProfilesInstance: + """ + Asynchronous coroutine to update the CustomerProfilesInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + + :returns: The updated CustomerProfilesInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesInstance( + self._version, payload, sid=self._solution["sid"] + ) + + @property + def customer_profiles_channel_endpoint_assignment( + self, + ) -> CustomerProfilesChannelEndpointAssignmentList: + """ + Access the customer_profiles_channel_endpoint_assignment + """ + if self._customer_profiles_channel_endpoint_assignment is None: + self._customer_profiles_channel_endpoint_assignment = ( + CustomerProfilesChannelEndpointAssignmentList( + self._version, + self._solution["sid"], + ) + ) + return self._customer_profiles_channel_endpoint_assignment + + @property + def customer_profiles_entity_assignments( + self, + ) -> CustomerProfilesEntityAssignmentsList: + """ + Access the customer_profiles_entity_assignments + """ + if self._customer_profiles_entity_assignments is None: + self._customer_profiles_entity_assignments = ( + CustomerProfilesEntityAssignmentsList( + self._version, + self._solution["sid"], + ) + ) + return self._customer_profiles_entity_assignments + + @property + def customer_profiles_evaluations(self) -> CustomerProfilesEvaluationsList: + """ + Access the customer_profiles_evaluations + """ + if self._customer_profiles_evaluations is None: + self._customer_profiles_evaluations = CustomerProfilesEvaluationsList( + self._version, + self._solution["sid"], + ) + return self._customer_profiles_evaluations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CustomerProfilesPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CustomerProfilesInstance: + """ + Build an instance of CustomerProfilesInstance + + :param payload: Payload response from the API + """ + return CustomerProfilesInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CustomerProfilesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CustomerProfilesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/CustomerProfiles" + + def create( + self, + friendly_name: str, + email: str, + policy_sid: str, + status_callback: Union[str, object] = values.unset, + ) -> CustomerProfilesInstance: + """ + Create the CustomerProfilesInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + :param policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param status_callback: The URL we call to inform your application of status changes. + + :returns: The created CustomerProfilesInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "PolicySid": policy_sid, + "StatusCallback": status_callback, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + email: str, + policy_sid: str, + status_callback: Union[str, object] = values.unset, + ) -> CustomerProfilesInstance: + """ + Asynchronously create the CustomerProfilesInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Customer-Profile resource changes status. + :param policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param status_callback: The URL we call to inform your application of status changes. + + :returns: The created CustomerProfilesInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "PolicySid": policy_sid, + "StatusCallback": status_callback, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesInstance(self._version, payload) + + def stream( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CustomerProfilesInstance]: + """ + Streams CustomerProfilesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "CustomerProfilesInstance.Status" status: The verification status of the Customer-Profile resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CustomerProfilesInstance]: + """ + Asynchronously streams CustomerProfilesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "CustomerProfilesInstance.Status" status: The verification status of the Customer-Profile resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesInstance]: + """ + Lists CustomerProfilesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "CustomerProfilesInstance.Status" status: The verification status of the Customer-Profile resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesInstance]: + """ + Asynchronously lists CustomerProfilesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "CustomerProfilesInstance.Status" status: The verification status of the Customer-Profile resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesPage: + """ + Retrieve a single page of CustomerProfilesInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Customer-Profile resource. + :param friendly_name: The string that you assigned to describe the resource. + :param policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "PolicySid": policy_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CustomerProfilesPage(self._version, response) + + async def page_async( + self, + status: Union["CustomerProfilesInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesPage: + """ + Asynchronously retrieve a single page of CustomerProfilesInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Customer-Profile resource. + :param friendly_name: The string that you assigned to describe the resource. + :param policy_sid: The unique string of a policy that is associated to the Customer-Profile resource. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "PolicySid": policy_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CustomerProfilesPage(self._version, response) + + def get_page(self, target_url: str) -> CustomerProfilesPage: + """ + Retrieve a specific page of CustomerProfilesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CustomerProfilesPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CustomerProfilesPage: + """ + Asynchronously retrieve a specific page of CustomerProfilesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CustomerProfilesPage(self._version, response) + + def get(self, sid: str) -> CustomerProfilesContext: + """ + Constructs a CustomerProfilesContext + + :param sid: The unique string that we created to identify the Customer-Profile resource. + """ + return CustomerProfilesContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CustomerProfilesContext: + """ + Constructs a CustomerProfilesContext + + :param sid: The unique string that we created to identify the Customer-Profile resource. + """ + return CustomerProfilesContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..585d8e2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_channel_endpoint_assignment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_channel_endpoint_assignment.cpython-310.pyc new file mode 100644 index 0000000..887c68a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_channel_endpoint_assignment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_entity_assignments.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_entity_assignments.cpython-310.pyc new file mode 100644 index 0000000..06b2c0f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_entity_assignments.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_evaluations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_evaluations.cpython-310.pyc new file mode 100644 index 0000000..40966d7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/__pycache__/customer_profiles_evaluations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_channel_endpoint_assignment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_channel_endpoint_assignment.py new file mode 100644 index 0000000..4cb14c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_channel_endpoint_assignment.py @@ -0,0 +1,594 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CustomerProfilesChannelEndpointAssignmentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Item Assignment resource. + :ivar customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Item Assignment resource. + :ivar channel_endpoint_type: The type of channel endpoint. eg: phone-number + :ivar channel_endpoint_sid: The SID of an channel endpoint + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Identity resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + customer_profile_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.customer_profile_sid: Optional[str] = payload.get("customer_profile_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_endpoint_type: Optional[str] = payload.get("channel_endpoint_type") + self.channel_endpoint_sid: Optional[str] = payload.get("channel_endpoint_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid or self.sid, + } + self._context: Optional[CustomerProfilesChannelEndpointAssignmentContext] = None + + @property + def _proxy(self) -> "CustomerProfilesChannelEndpointAssignmentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CustomerProfilesChannelEndpointAssignmentContext for this CustomerProfilesChannelEndpointAssignmentInstance + """ + if self._context is None: + self._context = CustomerProfilesChannelEndpointAssignmentContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CustomerProfilesChannelEndpointAssignmentInstance": + """ + Fetch the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: The fetched CustomerProfilesChannelEndpointAssignmentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CustomerProfilesChannelEndpointAssignmentInstance": + """ + Asynchronous coroutine to fetch the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: The fetched CustomerProfilesChannelEndpointAssignmentInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class CustomerProfilesChannelEndpointAssignmentContext(InstanceContext): + + def __init__(self, version: Version, customer_profile_sid: str, sid: str): + """ + Initialize the CustomerProfilesChannelEndpointAssignmentContext + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + :param sid: The unique string that we created to identify the resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid, + } + self._uri = "/CustomerProfiles/{customer_profile_sid}/ChannelEndpointAssignments/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CustomerProfilesChannelEndpointAssignmentInstance: + """ + Fetch the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: The fetched CustomerProfilesChannelEndpointAssignmentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesChannelEndpointAssignmentInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CustomerProfilesChannelEndpointAssignmentInstance: + """ + Asynchronous coroutine to fetch the CustomerProfilesChannelEndpointAssignmentInstance + + + :returns: The fetched CustomerProfilesChannelEndpointAssignmentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesChannelEndpointAssignmentInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class CustomerProfilesChannelEndpointAssignmentPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> CustomerProfilesChannelEndpointAssignmentInstance: + """ + Build an instance of CustomerProfilesChannelEndpointAssignmentInstance + + :param payload: Payload response from the API + """ + return CustomerProfilesChannelEndpointAssignmentInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CustomerProfilesChannelEndpointAssignmentList(ListResource): + + def __init__(self, version: Version, customer_profile_sid: str): + """ + Initialize the CustomerProfilesChannelEndpointAssignmentList + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + } + self._uri = "/CustomerProfiles/{customer_profile_sid}/ChannelEndpointAssignments".format( + **self._solution + ) + + def create( + self, channel_endpoint_type: str, channel_endpoint_sid: str + ) -> CustomerProfilesChannelEndpointAssignmentInstance: + """ + Create the CustomerProfilesChannelEndpointAssignmentInstance + + :param channel_endpoint_type: The type of channel endpoint. eg: phone-number + :param channel_endpoint_sid: The SID of an channel endpoint + + :returns: The created CustomerProfilesChannelEndpointAssignmentInstance + """ + + data = values.of( + { + "ChannelEndpointType": channel_endpoint_type, + "ChannelEndpointSid": channel_endpoint_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesChannelEndpointAssignmentInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + async def create_async( + self, channel_endpoint_type: str, channel_endpoint_sid: str + ) -> CustomerProfilesChannelEndpointAssignmentInstance: + """ + Asynchronously create the CustomerProfilesChannelEndpointAssignmentInstance + + :param channel_endpoint_type: The type of channel endpoint. eg: phone-number + :param channel_endpoint_sid: The SID of an channel endpoint + + :returns: The created CustomerProfilesChannelEndpointAssignmentInstance + """ + + data = values.of( + { + "ChannelEndpointType": channel_endpoint_type, + "ChannelEndpointSid": channel_endpoint_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesChannelEndpointAssignmentInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def stream( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CustomerProfilesChannelEndpointAssignmentInstance]: + """ + Streams CustomerProfilesChannelEndpointAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CustomerProfilesChannelEndpointAssignmentInstance]: + """ + Asynchronously streams CustomerProfilesChannelEndpointAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesChannelEndpointAssignmentInstance]: + """ + Lists CustomerProfilesChannelEndpointAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesChannelEndpointAssignmentInstance]: + """ + Asynchronously lists CustomerProfilesChannelEndpointAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesChannelEndpointAssignmentPage: + """ + Retrieve a single page of CustomerProfilesChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param channel_endpoint_sid: The SID of an channel endpoint + :param channel_endpoint_sids: comma separated list of channel endpoint sids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesChannelEndpointAssignmentInstance + """ + data = values.of( + { + "ChannelEndpointSid": channel_endpoint_sid, + "ChannelEndpointSids": channel_endpoint_sids, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CustomerProfilesChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + async def page_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesChannelEndpointAssignmentPage: + """ + Asynchronously retrieve a single page of CustomerProfilesChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param channel_endpoint_sid: The SID of an channel endpoint + :param channel_endpoint_sids: comma separated list of channel endpoint sids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesChannelEndpointAssignmentInstance + """ + data = values.of( + { + "ChannelEndpointSid": channel_endpoint_sid, + "ChannelEndpointSids": channel_endpoint_sids, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CustomerProfilesChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + def get_page( + self, target_url: str + ) -> CustomerProfilesChannelEndpointAssignmentPage: + """ + Retrieve a specific page of CustomerProfilesChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesChannelEndpointAssignmentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CustomerProfilesChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> CustomerProfilesChannelEndpointAssignmentPage: + """ + Asynchronously retrieve a specific page of CustomerProfilesChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesChannelEndpointAssignmentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CustomerProfilesChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> CustomerProfilesChannelEndpointAssignmentContext: + """ + Constructs a CustomerProfilesChannelEndpointAssignmentContext + + :param sid: The unique string that we created to identify the resource. + """ + return CustomerProfilesChannelEndpointAssignmentContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> CustomerProfilesChannelEndpointAssignmentContext: + """ + Constructs a CustomerProfilesChannelEndpointAssignmentContext + + :param sid: The unique string that we created to identify the resource. + """ + return CustomerProfilesChannelEndpointAssignmentContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_entity_assignments.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_entity_assignments.py new file mode 100644 index 0000000..5a19cca --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_entity_assignments.py @@ -0,0 +1,568 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CustomerProfilesEntityAssignmentsInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Item Assignment resource. + :ivar customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Item Assignment resource. + :ivar object_sid: The SID of an object bag that holds information of the different items. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Identity resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + customer_profile_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.customer_profile_sid: Optional[str] = payload.get("customer_profile_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.object_sid: Optional[str] = payload.get("object_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid or self.sid, + } + self._context: Optional[CustomerProfilesEntityAssignmentsContext] = None + + @property + def _proxy(self) -> "CustomerProfilesEntityAssignmentsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CustomerProfilesEntityAssignmentsContext for this CustomerProfilesEntityAssignmentsInstance + """ + if self._context is None: + self._context = CustomerProfilesEntityAssignmentsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CustomerProfilesEntityAssignmentsInstance": + """ + Fetch the CustomerProfilesEntityAssignmentsInstance + + + :returns: The fetched CustomerProfilesEntityAssignmentsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CustomerProfilesEntityAssignmentsInstance": + """ + Asynchronous coroutine to fetch the CustomerProfilesEntityAssignmentsInstance + + + :returns: The fetched CustomerProfilesEntityAssignmentsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return ( + "".format( + context + ) + ) + + +class CustomerProfilesEntityAssignmentsContext(InstanceContext): + + def __init__(self, version: Version, customer_profile_sid: str, sid: str): + """ + Initialize the CustomerProfilesEntityAssignmentsContext + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + :param sid: The unique string that we created to identify the Identity resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid, + } + self._uri = ( + "/CustomerProfiles/{customer_profile_sid}/EntityAssignments/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the CustomerProfilesEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CustomerProfilesEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CustomerProfilesEntityAssignmentsInstance: + """ + Fetch the CustomerProfilesEntityAssignmentsInstance + + + :returns: The fetched CustomerProfilesEntityAssignmentsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesEntityAssignmentsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CustomerProfilesEntityAssignmentsInstance: + """ + Asynchronous coroutine to fetch the CustomerProfilesEntityAssignmentsInstance + + + :returns: The fetched CustomerProfilesEntityAssignmentsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesEntityAssignmentsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return ( + "".format( + context + ) + ) + + +class CustomerProfilesEntityAssignmentsPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> CustomerProfilesEntityAssignmentsInstance: + """ + Build an instance of CustomerProfilesEntityAssignmentsInstance + + :param payload: Payload response from the API + """ + return CustomerProfilesEntityAssignmentsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CustomerProfilesEntityAssignmentsList(ListResource): + + def __init__(self, version: Version, customer_profile_sid: str): + """ + Initialize the CustomerProfilesEntityAssignmentsList + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + } + self._uri = "/CustomerProfiles/{customer_profile_sid}/EntityAssignments".format( + **self._solution + ) + + def create(self, object_sid: str) -> CustomerProfilesEntityAssignmentsInstance: + """ + Create the CustomerProfilesEntityAssignmentsInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created CustomerProfilesEntityAssignmentsInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesEntityAssignmentsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + async def create_async( + self, object_sid: str + ) -> CustomerProfilesEntityAssignmentsInstance: + """ + Asynchronously create the CustomerProfilesEntityAssignmentsInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created CustomerProfilesEntityAssignmentsInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesEntityAssignmentsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def stream( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CustomerProfilesEntityAssignmentsInstance]: + """ + Streams CustomerProfilesEntityAssignmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(object_type=object_type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CustomerProfilesEntityAssignmentsInstance]: + """ + Asynchronously streams CustomerProfilesEntityAssignmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + object_type=object_type, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesEntityAssignmentsInstance]: + """ + Lists CustomerProfilesEntityAssignmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + object_type=object_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesEntityAssignmentsInstance]: + """ + Asynchronously lists CustomerProfilesEntityAssignmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + object_type=object_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + object_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesEntityAssignmentsPage: + """ + Retrieve a single page of CustomerProfilesEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesEntityAssignmentsInstance + """ + data = values.of( + { + "ObjectType": object_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CustomerProfilesEntityAssignmentsPage( + self._version, response, self._solution + ) + + async def page_async( + self, + object_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesEntityAssignmentsPage: + """ + Asynchronously retrieve a single page of CustomerProfilesEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesEntityAssignmentsInstance + """ + data = values.of( + { + "ObjectType": object_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CustomerProfilesEntityAssignmentsPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> CustomerProfilesEntityAssignmentsPage: + """ + Retrieve a specific page of CustomerProfilesEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesEntityAssignmentsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CustomerProfilesEntityAssignmentsPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> CustomerProfilesEntityAssignmentsPage: + """ + Asynchronously retrieve a specific page of CustomerProfilesEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesEntityAssignmentsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CustomerProfilesEntityAssignmentsPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> CustomerProfilesEntityAssignmentsContext: + """ + Constructs a CustomerProfilesEntityAssignmentsContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return CustomerProfilesEntityAssignmentsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> CustomerProfilesEntityAssignmentsContext: + """ + Constructs a CustomerProfilesEntityAssignmentsContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return CustomerProfilesEntityAssignmentsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_evaluations.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_evaluations.py new file mode 100644 index 0000000..60538ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/customer_profiles/customer_profiles_evaluations.py @@ -0,0 +1,503 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CustomerProfilesEvaluationsInstance(InstanceResource): + + class Status(object): + COMPLIANT = "compliant" + NONCOMPLIANT = "noncompliant" + + """ + :ivar sid: The unique string that identifies the Evaluation resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the customer_profile resource. + :ivar policy_sid: The unique string of a policy that is associated to the customer_profile resource. + :ivar customer_profile_sid: The unique string that we created to identify the customer_profile resource. + :ivar status: + :ivar results: The results of the Evaluation which includes the valid and invalid attributes. + :ivar date_created: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + customer_profile_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.policy_sid: Optional[str] = payload.get("policy_sid") + self.customer_profile_sid: Optional[str] = payload.get("customer_profile_sid") + self.status: Optional["CustomerProfilesEvaluationsInstance.Status"] = ( + payload.get("status") + ) + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid or self.sid, + } + self._context: Optional[CustomerProfilesEvaluationsContext] = None + + @property + def _proxy(self) -> "CustomerProfilesEvaluationsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CustomerProfilesEvaluationsContext for this CustomerProfilesEvaluationsInstance + """ + if self._context is None: + self._context = CustomerProfilesEvaluationsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "CustomerProfilesEvaluationsInstance": + """ + Fetch the CustomerProfilesEvaluationsInstance + + + :returns: The fetched CustomerProfilesEvaluationsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CustomerProfilesEvaluationsInstance": + """ + Asynchronous coroutine to fetch the CustomerProfilesEvaluationsInstance + + + :returns: The fetched CustomerProfilesEvaluationsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class CustomerProfilesEvaluationsContext(InstanceContext): + + def __init__(self, version: Version, customer_profile_sid: str, sid: str): + """ + Initialize the CustomerProfilesEvaluationsContext + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the customer_profile resource. + :param sid: The unique string that identifies the Evaluation resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + "sid": sid, + } + self._uri = "/CustomerProfiles/{customer_profile_sid}/Evaluations/{sid}".format( + **self._solution + ) + + def fetch(self) -> CustomerProfilesEvaluationsInstance: + """ + Fetch the CustomerProfilesEvaluationsInstance + + + :returns: The fetched CustomerProfilesEvaluationsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesEvaluationsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CustomerProfilesEvaluationsInstance: + """ + Asynchronous coroutine to fetch the CustomerProfilesEvaluationsInstance + + + :returns: The fetched CustomerProfilesEvaluationsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CustomerProfilesEvaluationsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class CustomerProfilesEvaluationsPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> CustomerProfilesEvaluationsInstance: + """ + Build an instance of CustomerProfilesEvaluationsInstance + + :param payload: Payload response from the API + """ + return CustomerProfilesEvaluationsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CustomerProfilesEvaluationsList(ListResource): + + def __init__(self, version: Version, customer_profile_sid: str): + """ + Initialize the CustomerProfilesEvaluationsList + + :param version: Version that contains the resource + :param customer_profile_sid: The unique string that we created to identify the CustomerProfile resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "customer_profile_sid": customer_profile_sid, + } + self._uri = "/CustomerProfiles/{customer_profile_sid}/Evaluations".format( + **self._solution + ) + + def create(self, policy_sid: str) -> CustomerProfilesEvaluationsInstance: + """ + Create the CustomerProfilesEvaluationsInstance + + :param policy_sid: The unique string of a policy that is associated to the customer_profile resource. + + :returns: The created CustomerProfilesEvaluationsInstance + """ + + data = values.of( + { + "PolicySid": policy_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesEvaluationsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + async def create_async( + self, policy_sid: str + ) -> CustomerProfilesEvaluationsInstance: + """ + Asynchronously create the CustomerProfilesEvaluationsInstance + + :param policy_sid: The unique string of a policy that is associated to the customer_profile resource. + + :returns: The created CustomerProfilesEvaluationsInstance + """ + + data = values.of( + { + "PolicySid": policy_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CustomerProfilesEvaluationsInstance( + self._version, + payload, + customer_profile_sid=self._solution["customer_profile_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CustomerProfilesEvaluationsInstance]: + """ + Streams CustomerProfilesEvaluationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CustomerProfilesEvaluationsInstance]: + """ + Asynchronously streams CustomerProfilesEvaluationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesEvaluationsInstance]: + """ + Lists CustomerProfilesEvaluationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CustomerProfilesEvaluationsInstance]: + """ + Asynchronously lists CustomerProfilesEvaluationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesEvaluationsPage: + """ + Retrieve a single page of CustomerProfilesEvaluationsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesEvaluationsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CustomerProfilesEvaluationsPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CustomerProfilesEvaluationsPage: + """ + Asynchronously retrieve a single page of CustomerProfilesEvaluationsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CustomerProfilesEvaluationsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CustomerProfilesEvaluationsPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> CustomerProfilesEvaluationsPage: + """ + Retrieve a specific page of CustomerProfilesEvaluationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesEvaluationsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CustomerProfilesEvaluationsPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> CustomerProfilesEvaluationsPage: + """ + Asynchronously retrieve a specific page of CustomerProfilesEvaluationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CustomerProfilesEvaluationsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CustomerProfilesEvaluationsPage(self._version, response, self._solution) + + def get(self, sid: str) -> CustomerProfilesEvaluationsContext: + """ + Constructs a CustomerProfilesEvaluationsContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return CustomerProfilesEvaluationsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> CustomerProfilesEvaluationsContext: + """ + Constructs a CustomerProfilesEvaluationsContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return CustomerProfilesEvaluationsContext( + self._version, + customer_profile_sid=self._solution["customer_profile_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user.py new file mode 100644 index 0000000..b8b08d6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user.py @@ -0,0 +1,603 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EndUserInstance(InstanceResource): + """ + :ivar sid: The unique string created by Twilio to identify the End User resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the End User resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar type: The type of end user of the Bundle resource - can be `individual` or `business`. + :ivar attributes: The set of parameters that are the attributes of the End Users resource which are listed in the End User Types. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the End User resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.type: Optional[str] = payload.get("type") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EndUserContext] = None + + @property + def _proxy(self) -> "EndUserContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EndUserContext for this EndUserInstance + """ + if self._context is None: + self._context = EndUserContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "EndUserInstance": + """ + Fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EndUserInstance": + """ + Asynchronous coroutine to fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "EndUserInstance": + """ + Update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "EndUserInstance": + """ + Asynchronous coroutine to update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EndUserContext + + :param version: Version that contains the resource + :param sid: The unique string created by Twilio to identify the End User resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/EndUsers/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EndUserInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> EndUserInstance: + """ + Fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EndUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EndUserInstance: + """ + Asynchronous coroutine to fetch the EndUserInstance + + + :returns: The fetched EndUserInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EndUserInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Asynchronous coroutine to update the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The updated EndUserInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EndUserInstance: + """ + Build an instance of EndUserInstance + + :param payload: Payload response from the API + """ + return EndUserInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EndUserList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EndUserList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/EndUsers" + + def create( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Create the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of end user of the Bundle resource - can be `individual` or `business`. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The created EndUserInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> EndUserInstance: + """ + Asynchronously create the EndUserInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of end user of the Bundle resource - can be `individual` or `business`. + :param attributes: The set of parameters that are the attributes of the End User resource which are derived End User Types. + + :returns: The created EndUserInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EndUserInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EndUserInstance]: + """ + Streams EndUserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EndUserInstance]: + """ + Asynchronously streams EndUserInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserInstance]: + """ + Lists EndUserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserInstance]: + """ + Asynchronously lists EndUserInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserPage: + """ + Retrieve a single page of EndUserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EndUserPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserPage: + """ + Asynchronously retrieve a single page of EndUserInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EndUserPage(self._version, response) + + def get_page(self, target_url: str) -> EndUserPage: + """ + Retrieve a specific page of EndUserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EndUserPage(self._version, response) + + async def get_page_async(self, target_url: str) -> EndUserPage: + """ + Asynchronously retrieve a specific page of EndUserInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EndUserPage(self._version, response) + + def get(self, sid: str) -> EndUserContext: + """ + Constructs a EndUserContext + + :param sid: The unique string created by Twilio to identify the End User resource. + """ + return EndUserContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EndUserContext: + """ + Constructs a EndUserContext + + :param sid: The unique string created by Twilio to identify the End User resource. + """ + return EndUserContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user_type.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user_type.py new file mode 100644 index 0000000..d336385 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/end_user_type.py @@ -0,0 +1,394 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class EndUserTypeInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the End-User Type resource. + :ivar friendly_name: A human-readable description that is assigned to describe the End-User Type resource. Examples can include first name, last name, email, business name, etc + :ivar machine_name: A machine-readable description of the End-User Type resource. Examples can include first_name, last_name, email, business_name, etc. + :ivar fields: The required information for creating an End-User. The required fields will change as regulatory needs change and will differ for businesses and individuals. + :ivar url: The absolute URL of the End-User Type resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.machine_name: Optional[str] = payload.get("machine_name") + self.fields: Optional[List[Dict[str, object]]] = payload.get("fields") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[EndUserTypeContext] = None + + @property + def _proxy(self) -> "EndUserTypeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EndUserTypeContext for this EndUserTypeInstance + """ + if self._context is None: + self._context = EndUserTypeContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "EndUserTypeInstance": + """ + Fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EndUserTypeInstance": + """ + Asynchronous coroutine to fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserTypeContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the EndUserTypeContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the End-User Type resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/EndUserTypes/{sid}".format(**self._solution) + + def fetch(self) -> EndUserTypeInstance: + """ + Fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EndUserTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> EndUserTypeInstance: + """ + Asynchronous coroutine to fetch the EndUserTypeInstance + + + :returns: The fetched EndUserTypeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EndUserTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EndUserTypePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EndUserTypeInstance: + """ + Build an instance of EndUserTypeInstance + + :param payload: Payload response from the API + """ + return EndUserTypeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EndUserTypeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the EndUserTypeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/EndUserTypes" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EndUserTypeInstance]: + """ + Streams EndUserTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EndUserTypeInstance]: + """ + Asynchronously streams EndUserTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserTypeInstance]: + """ + Lists EndUserTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EndUserTypeInstance]: + """ + Asynchronously lists EndUserTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserTypePage: + """ + Retrieve a single page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EndUserTypePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EndUserTypePage: + """ + Asynchronously retrieve a single page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EndUserTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EndUserTypePage(self._version, response) + + def get_page(self, target_url: str) -> EndUserTypePage: + """ + Retrieve a specific page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserTypeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EndUserTypePage(self._version, response) + + async def get_page_async(self, target_url: str) -> EndUserTypePage: + """ + Asynchronously retrieve a specific page of EndUserTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EndUserTypeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EndUserTypePage(self._version, response) + + def get(self, sid: str) -> EndUserTypeContext: + """ + Constructs a EndUserTypeContext + + :param sid: The unique string that identifies the End-User Type resource. + """ + return EndUserTypeContext(self._version, sid=sid) + + def __call__(self, sid: str) -> EndUserTypeContext: + """ + Constructs a EndUserTypeContext + + :param sid: The unique string that identifies the End-User Type resource. + """ + return EndUserTypeContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/policies.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/policies.py new file mode 100644 index 0000000..a39b1e0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/policies.py @@ -0,0 +1,392 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PoliciesInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the Policy resource. + :ivar friendly_name: A human-readable description that is assigned to describe the Policy resource. Examples can include Primary Customer profile policy + :ivar requirements: The SID of an object that holds the policy information + :ivar url: The absolute URL of the Policy resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.requirements: Optional[Dict[str, object]] = payload.get("requirements") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[PoliciesContext] = None + + @property + def _proxy(self) -> "PoliciesContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PoliciesContext for this PoliciesInstance + """ + if self._context is None: + self._context = PoliciesContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "PoliciesInstance": + """ + Fetch the PoliciesInstance + + + :returns: The fetched PoliciesInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PoliciesInstance": + """ + Asynchronous coroutine to fetch the PoliciesInstance + + + :returns: The fetched PoliciesInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PoliciesContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the PoliciesContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the Policy resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Policies/{sid}".format(**self._solution) + + def fetch(self) -> PoliciesInstance: + """ + Fetch the PoliciesInstance + + + :returns: The fetched PoliciesInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PoliciesInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PoliciesInstance: + """ + Asynchronous coroutine to fetch the PoliciesInstance + + + :returns: The fetched PoliciesInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PoliciesInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PoliciesPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PoliciesInstance: + """ + Build an instance of PoliciesInstance + + :param payload: Payload response from the API + """ + return PoliciesInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PoliciesList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the PoliciesList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Policies" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PoliciesInstance]: + """ + Streams PoliciesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PoliciesInstance]: + """ + Asynchronously streams PoliciesInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PoliciesInstance]: + """ + Lists PoliciesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PoliciesInstance]: + """ + Asynchronously lists PoliciesInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PoliciesPage: + """ + Retrieve a single page of PoliciesInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PoliciesInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PoliciesPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PoliciesPage: + """ + Asynchronously retrieve a single page of PoliciesInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PoliciesInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PoliciesPage(self._version, response) + + def get_page(self, target_url: str) -> PoliciesPage: + """ + Retrieve a specific page of PoliciesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PoliciesInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PoliciesPage(self._version, response) + + async def get_page_async(self, target_url: str) -> PoliciesPage: + """ + Asynchronously retrieve a specific page of PoliciesInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PoliciesInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PoliciesPage(self._version, response) + + def get(self, sid: str) -> PoliciesContext: + """ + Constructs a PoliciesContext + + :param sid: The unique string that identifies the Policy resource. + """ + return PoliciesContext(self._version, sid=sid) + + def __call__(self, sid: str) -> PoliciesContext: + """ + Constructs a PoliciesContext + + :param sid: The unique string that identifies the Policy resource. + """ + return PoliciesContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document.py new file mode 100644 index 0000000..a18293f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document.py @@ -0,0 +1,622 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SupportingDocumentInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + REJECTED = "rejected" + APPROVED = "approved" + EXPIRED = "expired" + PROVISIONALLY_APPROVED = "provisionally-approved" + + """ + :ivar sid: The unique string created by Twilio to identify the Supporting Document resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Document resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar mime_type: The image type uploaded in the Supporting Document container. + :ivar status: + :ivar type: The type of the Supporting Document. + :ivar attributes: The set of parameters that are the attributes of the Supporting Documents resource which are listed in the Supporting Document Types. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Supporting Document resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.mime_type: Optional[str] = payload.get("mime_type") + self.status: Optional["SupportingDocumentInstance.Status"] = payload.get( + "status" + ) + self.type: Optional[str] = payload.get("type") + self.attributes: Optional[Dict[str, object]] = payload.get("attributes") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SupportingDocumentContext] = None + + @property + def _proxy(self) -> "SupportingDocumentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SupportingDocumentContext for this SupportingDocumentInstance + """ + if self._context is None: + self._context = SupportingDocumentContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SupportingDocumentInstance": + """ + Fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SupportingDocumentInstance": + """ + Asynchronous coroutine to fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "SupportingDocumentInstance": + """ + Update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + attributes=attributes, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> "SupportingDocumentInstance": + """ + Asynchronous coroutine to update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + attributes=attributes, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SupportingDocumentContext + + :param version: Version that contains the resource + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/SupportingDocuments/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SupportingDocumentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SupportingDocumentInstance: + """ + Fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SupportingDocumentInstance: + """ + Asynchronous coroutine to fetch the SupportingDocumentInstance + + + :returns: The fetched SupportingDocumentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Asynchronous coroutine to update the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param attributes: The set of parameters that are the attributes of the Supporting Document resource which are derived Supporting Document Types. + + :returns: The updated SupportingDocumentInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SupportingDocumentInstance: + """ + Build an instance of SupportingDocumentInstance + + :param payload: Payload response from the API + """ + return SupportingDocumentInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SupportingDocumentList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SupportingDocumentList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SupportingDocuments" + + def create( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Create the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of the Supporting Document. + :param attributes: The set of parameters that are the attributes of the Supporting Documents resource which are derived Supporting Document Types. + + :returns: The created SupportingDocumentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + type: str, + attributes: Union[object, object] = values.unset, + ) -> SupportingDocumentInstance: + """ + Asynchronously create the SupportingDocumentInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param type: The type of the Supporting Document. + :param attributes: The set of parameters that are the attributes of the Supporting Documents resource which are derived Supporting Document Types. + + :returns: The created SupportingDocumentInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Type": type, + "Attributes": serialize.object(attributes), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SupportingDocumentInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SupportingDocumentInstance]: + """ + Streams SupportingDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SupportingDocumentInstance]: + """ + Asynchronously streams SupportingDocumentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentInstance]: + """ + Lists SupportingDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentInstance]: + """ + Asynchronously lists SupportingDocumentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentPage: + """ + Retrieve a single page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SupportingDocumentPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentPage: + """ + Asynchronously retrieve a single page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SupportingDocumentPage(self._version, response) + + def get_page(self, target_url: str) -> SupportingDocumentPage: + """ + Retrieve a specific page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SupportingDocumentPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SupportingDocumentPage: + """ + Asynchronously retrieve a specific page of SupportingDocumentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SupportingDocumentPage(self._version, response) + + def get(self, sid: str) -> SupportingDocumentContext: + """ + Constructs a SupportingDocumentContext + + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + return SupportingDocumentContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SupportingDocumentContext: + """ + Constructs a SupportingDocumentContext + + :param sid: The unique string created by Twilio to identify the Supporting Document resource. + """ + return SupportingDocumentContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document_type.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document_type.py new file mode 100644 index 0000000..f1a93c5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/supporting_document_type.py @@ -0,0 +1,394 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SupportingDocumentTypeInstance(InstanceResource): + """ + :ivar sid: The unique string that identifies the Supporting Document Type resource. + :ivar friendly_name: A human-readable description of the Supporting Document Type resource. + :ivar machine_name: The machine-readable description of the Supporting Document Type resource. + :ivar fields: The required information for creating a Supporting Document. The required fields will change as regulatory needs change and will differ for businesses and individuals. + :ivar url: The absolute URL of the Supporting Document Type resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.machine_name: Optional[str] = payload.get("machine_name") + self.fields: Optional[List[Dict[str, object]]] = payload.get("fields") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SupportingDocumentTypeContext] = None + + @property + def _proxy(self) -> "SupportingDocumentTypeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SupportingDocumentTypeContext for this SupportingDocumentTypeInstance + """ + if self._context is None: + self._context = SupportingDocumentTypeContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SupportingDocumentTypeInstance": + """ + Fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SupportingDocumentTypeInstance": + """ + Asynchronous coroutine to fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentTypeContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SupportingDocumentTypeContext + + :param version: Version that contains the resource + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/SupportingDocumentTypes/{sid}".format(**self._solution) + + def fetch(self) -> SupportingDocumentTypeInstance: + """ + Fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SupportingDocumentTypeInstance: + """ + Asynchronous coroutine to fetch the SupportingDocumentTypeInstance + + + :returns: The fetched SupportingDocumentTypeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SupportingDocumentTypeInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SupportingDocumentTypePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SupportingDocumentTypeInstance: + """ + Build an instance of SupportingDocumentTypeInstance + + :param payload: Payload response from the API + """ + return SupportingDocumentTypeInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SupportingDocumentTypeList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SupportingDocumentTypeList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SupportingDocumentTypes" + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SupportingDocumentTypeInstance]: + """ + Streams SupportingDocumentTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SupportingDocumentTypeInstance]: + """ + Asynchronously streams SupportingDocumentTypeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentTypeInstance]: + """ + Lists SupportingDocumentTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SupportingDocumentTypeInstance]: + """ + Asynchronously lists SupportingDocumentTypeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentTypePage: + """ + Retrieve a single page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SupportingDocumentTypePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SupportingDocumentTypePage: + """ + Asynchronously retrieve a single page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SupportingDocumentTypeInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SupportingDocumentTypePage(self._version, response) + + def get_page(self, target_url: str) -> SupportingDocumentTypePage: + """ + Retrieve a specific page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentTypeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SupportingDocumentTypePage(self._version, response) + + async def get_page_async(self, target_url: str) -> SupportingDocumentTypePage: + """ + Asynchronously retrieve a specific page of SupportingDocumentTypeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SupportingDocumentTypeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SupportingDocumentTypePage(self._version, response) + + def get(self, sid: str) -> SupportingDocumentTypeContext: + """ + Constructs a SupportingDocumentTypeContext + + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + return SupportingDocumentTypeContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SupportingDocumentTypeContext: + """ + Constructs a SupportingDocumentTypeContext + + :param sid: The unique string that identifies the Supporting Document Type resource. + """ + return SupportingDocumentTypeContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__init__.py new file mode 100644 index 0000000..b74e8e8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__init__.py @@ -0,0 +1,791 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.trusthub.v1.trust_products.trust_products_channel_endpoint_assignment import ( + TrustProductsChannelEndpointAssignmentList, +) +from twilio.rest.trusthub.v1.trust_products.trust_products_entity_assignments import ( + TrustProductsEntityAssignmentsList, +) +from twilio.rest.trusthub.v1.trust_products.trust_products_evaluations import ( + TrustProductsEvaluationsList, +) + + +class TrustProductsInstance(InstanceResource): + + class Status(object): + DRAFT = "draft" + PENDING_REVIEW = "pending-review" + IN_REVIEW = "in-review" + TWILIO_REJECTED = "twilio-rejected" + TWILIO_APPROVED = "twilio-approved" + + """ + :ivar sid: The unique string that we created to identify the Trust Product resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Trust Product resource. + :ivar policy_sid: The unique string of the policy that is associated with the Trust Product resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar status: + :ivar valid_until: The date and time in GMT in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format until which the resource will be valid. + :ivar email: The email address that will receive updates when the Trust Product resource changes status. + :ivar status_callback: The URL we call to inform your application of status changes. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Trust Product resource. + :ivar links: The URLs of the Assigned Items of the Trust Product resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.policy_sid: Optional[str] = payload.get("policy_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["TrustProductsInstance.Status"] = payload.get("status") + self.valid_until: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("valid_until") + ) + self.email: Optional[str] = payload.get("email") + self.status_callback: Optional[str] = payload.get("status_callback") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[TrustProductsContext] = None + + @property + def _proxy(self) -> "TrustProductsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrustProductsContext for this TrustProductsInstance + """ + if self._context is None: + self._context = TrustProductsContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TrustProductsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TrustProductsInstance": + """ + Fetch the TrustProductsInstance + + + :returns: The fetched TrustProductsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrustProductsInstance": + """ + Asynchronous coroutine to fetch the TrustProductsInstance + + + :returns: The fetched TrustProductsInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "TrustProductsInstance": + """ + Update the TrustProductsInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + + :returns: The updated TrustProductsInstance + """ + return self._proxy.update( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + async def update_async( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> "TrustProductsInstance": + """ + Asynchronous coroutine to update the TrustProductsInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + + :returns: The updated TrustProductsInstance + """ + return await self._proxy.update_async( + status=status, + status_callback=status_callback, + friendly_name=friendly_name, + email=email, + ) + + @property + def trust_products_channel_endpoint_assignment( + self, + ) -> TrustProductsChannelEndpointAssignmentList: + """ + Access the trust_products_channel_endpoint_assignment + """ + return self._proxy.trust_products_channel_endpoint_assignment + + @property + def trust_products_entity_assignments(self) -> TrustProductsEntityAssignmentsList: + """ + Access the trust_products_entity_assignments + """ + return self._proxy.trust_products_entity_assignments + + @property + def trust_products_evaluations(self) -> TrustProductsEvaluationsList: + """ + Access the trust_products_evaluations + """ + return self._proxy.trust_products_evaluations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrustProductsContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the TrustProductsContext + + :param version: Version that contains the resource + :param sid: The unique string that we created to identify the Trust Product resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/TrustProducts/{sid}".format(**self._solution) + + self._trust_products_channel_endpoint_assignment: Optional[ + TrustProductsChannelEndpointAssignmentList + ] = None + self._trust_products_entity_assignments: Optional[ + TrustProductsEntityAssignmentsList + ] = None + self._trust_products_evaluations: Optional[TrustProductsEvaluationsList] = None + + def delete(self) -> bool: + """ + Deletes the TrustProductsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TrustProductsInstance: + """ + Fetch the TrustProductsInstance + + + :returns: The fetched TrustProductsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrustProductsInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TrustProductsInstance: + """ + Asynchronous coroutine to fetch the TrustProductsInstance + + + :returns: The fetched TrustProductsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrustProductsInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> TrustProductsInstance: + """ + Update the TrustProductsInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + + :returns: The updated TrustProductsInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + status_callback: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + email: Union[str, object] = values.unset, + ) -> TrustProductsInstance: + """ + Asynchronous coroutine to update the TrustProductsInstance + + :param status: + :param status_callback: The URL we call to inform your application of status changes. + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + + :returns: The updated TrustProductsInstance + """ + data = values.of( + { + "Status": status, + "StatusCallback": status_callback, + "FriendlyName": friendly_name, + "Email": email, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def trust_products_channel_endpoint_assignment( + self, + ) -> TrustProductsChannelEndpointAssignmentList: + """ + Access the trust_products_channel_endpoint_assignment + """ + if self._trust_products_channel_endpoint_assignment is None: + self._trust_products_channel_endpoint_assignment = ( + TrustProductsChannelEndpointAssignmentList( + self._version, + self._solution["sid"], + ) + ) + return self._trust_products_channel_endpoint_assignment + + @property + def trust_products_entity_assignments(self) -> TrustProductsEntityAssignmentsList: + """ + Access the trust_products_entity_assignments + """ + if self._trust_products_entity_assignments is None: + self._trust_products_entity_assignments = ( + TrustProductsEntityAssignmentsList( + self._version, + self._solution["sid"], + ) + ) + return self._trust_products_entity_assignments + + @property + def trust_products_evaluations(self) -> TrustProductsEvaluationsList: + """ + Access the trust_products_evaluations + """ + if self._trust_products_evaluations is None: + self._trust_products_evaluations = TrustProductsEvaluationsList( + self._version, + self._solution["sid"], + ) + return self._trust_products_evaluations + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrustProductsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TrustProductsInstance: + """ + Build an instance of TrustProductsInstance + + :param payload: Payload response from the API + """ + return TrustProductsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TrustProductsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TrustProductsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/TrustProducts" + + def create( + self, + friendly_name: str, + email: str, + policy_sid: str, + status_callback: Union[str, object] = values.unset, + ) -> TrustProductsInstance: + """ + Create the TrustProductsInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + :param policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param status_callback: The URL we call to inform your application of status changes. + + :returns: The created TrustProductsInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "PolicySid": policy_sid, + "StatusCallback": status_callback, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + email: str, + policy_sid: str, + status_callback: Union[str, object] = values.unset, + ) -> TrustProductsInstance: + """ + Asynchronously create the TrustProductsInstance + + :param friendly_name: The string that you assigned to describe the resource. + :param email: The email address that will receive updates when the Trust Product resource changes status. + :param policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param status_callback: The URL we call to inform your application of status changes. + + :returns: The created TrustProductsInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Email": email, + "PolicySid": policy_sid, + "StatusCallback": status_callback, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsInstance(self._version, payload) + + def stream( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TrustProductsInstance]: + """ + Streams TrustProductsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TrustProductsInstance.Status" status: The verification status of the Trust Product resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TrustProductsInstance]: + """ + Asynchronously streams TrustProductsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "TrustProductsInstance.Status" status: The verification status of the Trust Product resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsInstance]: + """ + Lists TrustProductsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TrustProductsInstance.Status" status: The verification status of the Trust Product resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsInstance]: + """ + Asynchronously lists TrustProductsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "TrustProductsInstance.Status" status: The verification status of the Trust Product resource. + :param str friendly_name: The string that you assigned to describe the resource. + :param str policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + friendly_name=friendly_name, + policy_sid=policy_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsPage: + """ + Retrieve a single page of TrustProductsInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Trust Product resource. + :param friendly_name: The string that you assigned to describe the resource. + :param policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "PolicySid": policy_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TrustProductsPage(self._version, response) + + async def page_async( + self, + status: Union["TrustProductsInstance.Status", object] = values.unset, + friendly_name: Union[str, object] = values.unset, + policy_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsPage: + """ + Asynchronously retrieve a single page of TrustProductsInstance records from the API. + Request is executed immediately + + :param status: The verification status of the Trust Product resource. + :param friendly_name: The string that you assigned to describe the resource. + :param policy_sid: The unique string of a policy that is associated to the Trust Product resource. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsInstance + """ + data = values.of( + { + "Status": status, + "FriendlyName": friendly_name, + "PolicySid": policy_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TrustProductsPage(self._version, response) + + def get_page(self, target_url: str) -> TrustProductsPage: + """ + Retrieve a specific page of TrustProductsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TrustProductsPage(self._version, response) + + async def get_page_async(self, target_url: str) -> TrustProductsPage: + """ + Asynchronously retrieve a specific page of TrustProductsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TrustProductsPage(self._version, response) + + def get(self, sid: str) -> TrustProductsContext: + """ + Constructs a TrustProductsContext + + :param sid: The unique string that we created to identify the Trust Product resource. + """ + return TrustProductsContext(self._version, sid=sid) + + def __call__(self, sid: str) -> TrustProductsContext: + """ + Constructs a TrustProductsContext + + :param sid: The unique string that we created to identify the Trust Product resource. + """ + return TrustProductsContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d911b0f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_channel_endpoint_assignment.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_channel_endpoint_assignment.cpython-310.pyc new file mode 100644 index 0000000..0b2b19d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_channel_endpoint_assignment.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_entity_assignments.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_entity_assignments.cpython-310.pyc new file mode 100644 index 0000000..e768426 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_entity_assignments.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_evaluations.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_evaluations.cpython-310.pyc new file mode 100644 index 0000000..a8e77bb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/__pycache__/trust_products_evaluations.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_channel_endpoint_assignment.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_channel_endpoint_assignment.py new file mode 100644 index 0000000..ef2b589 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_channel_endpoint_assignment.py @@ -0,0 +1,594 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TrustProductsChannelEndpointAssignmentInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Item Assignment resource. + :ivar trust_product_sid: The unique string that we created to identify the CustomerProfile resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Item Assignment resource. + :ivar channel_endpoint_type: The type of channel endpoint. eg: phone-number + :ivar channel_endpoint_sid: The SID of an channel endpoint + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Identity resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trust_product_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.trust_product_sid: Optional[str] = payload.get("trust_product_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.channel_endpoint_type: Optional[str] = payload.get("channel_endpoint_type") + self.channel_endpoint_sid: Optional[str] = payload.get("channel_endpoint_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid or self.sid, + } + self._context: Optional[TrustProductsChannelEndpointAssignmentContext] = None + + @property + def _proxy(self) -> "TrustProductsChannelEndpointAssignmentContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrustProductsChannelEndpointAssignmentContext for this TrustProductsChannelEndpointAssignmentInstance + """ + if self._context is None: + self._context = TrustProductsChannelEndpointAssignmentContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TrustProductsChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TrustProductsChannelEndpointAssignmentInstance": + """ + Fetch the TrustProductsChannelEndpointAssignmentInstance + + + :returns: The fetched TrustProductsChannelEndpointAssignmentInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrustProductsChannelEndpointAssignmentInstance": + """ + Asynchronous coroutine to fetch the TrustProductsChannelEndpointAssignmentInstance + + + :returns: The fetched TrustProductsChannelEndpointAssignmentInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TrustProductsChannelEndpointAssignmentContext(InstanceContext): + + def __init__(self, version: Version, trust_product_sid: str, sid: str): + """ + Initialize the TrustProductsChannelEndpointAssignmentContext + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the CustomerProfile resource. + :param sid: The unique string that we created to identify the resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid, + } + self._uri = "/TrustProducts/{trust_product_sid}/ChannelEndpointAssignments/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TrustProductsChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsChannelEndpointAssignmentInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TrustProductsChannelEndpointAssignmentInstance: + """ + Fetch the TrustProductsChannelEndpointAssignmentInstance + + + :returns: The fetched TrustProductsChannelEndpointAssignmentInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrustProductsChannelEndpointAssignmentInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TrustProductsChannelEndpointAssignmentInstance: + """ + Asynchronous coroutine to fetch the TrustProductsChannelEndpointAssignmentInstance + + + :returns: The fetched TrustProductsChannelEndpointAssignmentInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrustProductsChannelEndpointAssignmentInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TrustProductsChannelEndpointAssignmentPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> TrustProductsChannelEndpointAssignmentInstance: + """ + Build an instance of TrustProductsChannelEndpointAssignmentInstance + + :param payload: Payload response from the API + """ + return TrustProductsChannelEndpointAssignmentInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TrustProductsChannelEndpointAssignmentList(ListResource): + + def __init__(self, version: Version, trust_product_sid: str): + """ + Initialize the TrustProductsChannelEndpointAssignmentList + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the CustomerProfile resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + } + self._uri = ( + "/TrustProducts/{trust_product_sid}/ChannelEndpointAssignments".format( + **self._solution + ) + ) + + def create( + self, channel_endpoint_type: str, channel_endpoint_sid: str + ) -> TrustProductsChannelEndpointAssignmentInstance: + """ + Create the TrustProductsChannelEndpointAssignmentInstance + + :param channel_endpoint_type: The type of channel endpoint. eg: phone-number + :param channel_endpoint_sid: The SID of an channel endpoint + + :returns: The created TrustProductsChannelEndpointAssignmentInstance + """ + + data = values.of( + { + "ChannelEndpointType": channel_endpoint_type, + "ChannelEndpointSid": channel_endpoint_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsChannelEndpointAssignmentInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + async def create_async( + self, channel_endpoint_type: str, channel_endpoint_sid: str + ) -> TrustProductsChannelEndpointAssignmentInstance: + """ + Asynchronously create the TrustProductsChannelEndpointAssignmentInstance + + :param channel_endpoint_type: The type of channel endpoint. eg: phone-number + :param channel_endpoint_sid: The SID of an channel endpoint + + :returns: The created TrustProductsChannelEndpointAssignmentInstance + """ + + data = values.of( + { + "ChannelEndpointType": channel_endpoint_type, + "ChannelEndpointSid": channel_endpoint_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsChannelEndpointAssignmentInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def stream( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TrustProductsChannelEndpointAssignmentInstance]: + """ + Streams TrustProductsChannelEndpointAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TrustProductsChannelEndpointAssignmentInstance]: + """ + Asynchronously streams TrustProductsChannelEndpointAssignmentInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsChannelEndpointAssignmentInstance]: + """ + Lists TrustProductsChannelEndpointAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsChannelEndpointAssignmentInstance]: + """ + Asynchronously lists TrustProductsChannelEndpointAssignmentInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str channel_endpoint_sid: The SID of an channel endpoint + :param str channel_endpoint_sids: comma separated list of channel endpoint sids + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + channel_endpoint_sid=channel_endpoint_sid, + channel_endpoint_sids=channel_endpoint_sids, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsChannelEndpointAssignmentPage: + """ + Retrieve a single page of TrustProductsChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param channel_endpoint_sid: The SID of an channel endpoint + :param channel_endpoint_sids: comma separated list of channel endpoint sids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsChannelEndpointAssignmentInstance + """ + data = values.of( + { + "ChannelEndpointSid": channel_endpoint_sid, + "ChannelEndpointSids": channel_endpoint_sids, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TrustProductsChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + async def page_async( + self, + channel_endpoint_sid: Union[str, object] = values.unset, + channel_endpoint_sids: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsChannelEndpointAssignmentPage: + """ + Asynchronously retrieve a single page of TrustProductsChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param channel_endpoint_sid: The SID of an channel endpoint + :param channel_endpoint_sids: comma separated list of channel endpoint sids + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsChannelEndpointAssignmentInstance + """ + data = values.of( + { + "ChannelEndpointSid": channel_endpoint_sid, + "ChannelEndpointSids": channel_endpoint_sids, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TrustProductsChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> TrustProductsChannelEndpointAssignmentPage: + """ + Retrieve a specific page of TrustProductsChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsChannelEndpointAssignmentInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TrustProductsChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> TrustProductsChannelEndpointAssignmentPage: + """ + Asynchronously retrieve a specific page of TrustProductsChannelEndpointAssignmentInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsChannelEndpointAssignmentInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TrustProductsChannelEndpointAssignmentPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> TrustProductsChannelEndpointAssignmentContext: + """ + Constructs a TrustProductsChannelEndpointAssignmentContext + + :param sid: The unique string that we created to identify the resource. + """ + return TrustProductsChannelEndpointAssignmentContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> TrustProductsChannelEndpointAssignmentContext: + """ + Constructs a TrustProductsChannelEndpointAssignmentContext + + :param sid: The unique string that we created to identify the resource. + """ + return TrustProductsChannelEndpointAssignmentContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_entity_assignments.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_entity_assignments.py new file mode 100644 index 0000000..16f07a8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_entity_assignments.py @@ -0,0 +1,562 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TrustProductsEntityAssignmentsInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Item Assignment resource. + :ivar trust_product_sid: The unique string that we created to identify the TrustProduct resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Item Assignment resource. + :ivar object_sid: The SID of an object bag that holds information of the different items. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Identity resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trust_product_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.trust_product_sid: Optional[str] = payload.get("trust_product_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.object_sid: Optional[str] = payload.get("object_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid or self.sid, + } + self._context: Optional[TrustProductsEntityAssignmentsContext] = None + + @property + def _proxy(self) -> "TrustProductsEntityAssignmentsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrustProductsEntityAssignmentsContext for this TrustProductsEntityAssignmentsInstance + """ + if self._context is None: + self._context = TrustProductsEntityAssignmentsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the TrustProductsEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "TrustProductsEntityAssignmentsInstance": + """ + Fetch the TrustProductsEntityAssignmentsInstance + + + :returns: The fetched TrustProductsEntityAssignmentsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrustProductsEntityAssignmentsInstance": + """ + Asynchronous coroutine to fetch the TrustProductsEntityAssignmentsInstance + + + :returns: The fetched TrustProductsEntityAssignmentsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TrustProductsEntityAssignmentsContext(InstanceContext): + + def __init__(self, version: Version, trust_product_sid: str, sid: str): + """ + Initialize the TrustProductsEntityAssignmentsContext + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the TrustProduct resource. + :param sid: The unique string that we created to identify the Identity resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid, + } + self._uri = "/TrustProducts/{trust_product_sid}/EntityAssignments/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the TrustProductsEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the TrustProductsEntityAssignmentsInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> TrustProductsEntityAssignmentsInstance: + """ + Fetch the TrustProductsEntityAssignmentsInstance + + + :returns: The fetched TrustProductsEntityAssignmentsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrustProductsEntityAssignmentsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TrustProductsEntityAssignmentsInstance: + """ + Asynchronous coroutine to fetch the TrustProductsEntityAssignmentsInstance + + + :returns: The fetched TrustProductsEntityAssignmentsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrustProductsEntityAssignmentsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TrustProductsEntityAssignmentsPage(Page): + + def get_instance( + self, payload: Dict[str, Any] + ) -> TrustProductsEntityAssignmentsInstance: + """ + Build an instance of TrustProductsEntityAssignmentsInstance + + :param payload: Payload response from the API + """ + return TrustProductsEntityAssignmentsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TrustProductsEntityAssignmentsList(ListResource): + + def __init__(self, version: Version, trust_product_sid: str): + """ + Initialize the TrustProductsEntityAssignmentsList + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the TrustProduct resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + } + self._uri = "/TrustProducts/{trust_product_sid}/EntityAssignments".format( + **self._solution + ) + + def create(self, object_sid: str) -> TrustProductsEntityAssignmentsInstance: + """ + Create the TrustProductsEntityAssignmentsInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created TrustProductsEntityAssignmentsInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsEntityAssignmentsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + async def create_async( + self, object_sid: str + ) -> TrustProductsEntityAssignmentsInstance: + """ + Asynchronously create the TrustProductsEntityAssignmentsInstance + + :param object_sid: The SID of an object bag that holds information of the different items. + + :returns: The created TrustProductsEntityAssignmentsInstance + """ + + data = values.of( + { + "ObjectSid": object_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsEntityAssignmentsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def stream( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TrustProductsEntityAssignmentsInstance]: + """ + Streams TrustProductsEntityAssignmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(object_type=object_type, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TrustProductsEntityAssignmentsInstance]: + """ + Asynchronously streams TrustProductsEntityAssignmentsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + object_type=object_type, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsEntityAssignmentsInstance]: + """ + Lists TrustProductsEntityAssignmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + object_type=object_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + object_type: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsEntityAssignmentsInstance]: + """ + Asynchronously lists TrustProductsEntityAssignmentsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + object_type=object_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + object_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsEntityAssignmentsPage: + """ + Retrieve a single page of TrustProductsEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsEntityAssignmentsInstance + """ + data = values.of( + { + "ObjectType": object_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TrustProductsEntityAssignmentsPage( + self._version, response, self._solution + ) + + async def page_async( + self, + object_type: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsEntityAssignmentsPage: + """ + Asynchronously retrieve a single page of TrustProductsEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param object_type: A string to filter the results by (EndUserType or SupportingDocumentType) machine-name. This is useful when you want to retrieve the entity-assignment of a specific end-user or supporting document. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsEntityAssignmentsInstance + """ + data = values.of( + { + "ObjectType": object_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TrustProductsEntityAssignmentsPage( + self._version, response, self._solution + ) + + def get_page(self, target_url: str) -> TrustProductsEntityAssignmentsPage: + """ + Retrieve a specific page of TrustProductsEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsEntityAssignmentsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TrustProductsEntityAssignmentsPage( + self._version, response, self._solution + ) + + async def get_page_async( + self, target_url: str + ) -> TrustProductsEntityAssignmentsPage: + """ + Asynchronously retrieve a specific page of TrustProductsEntityAssignmentsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsEntityAssignmentsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TrustProductsEntityAssignmentsPage( + self._version, response, self._solution + ) + + def get(self, sid: str) -> TrustProductsEntityAssignmentsContext: + """ + Constructs a TrustProductsEntityAssignmentsContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return TrustProductsEntityAssignmentsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> TrustProductsEntityAssignmentsContext: + """ + Constructs a TrustProductsEntityAssignmentsContext + + :param sid: The unique string that we created to identify the Identity resource. + """ + return TrustProductsEntityAssignmentsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_evaluations.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_evaluations.py new file mode 100644 index 0000000..dfb929b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/trusthub/v1/trust_products/trust_products_evaluations.py @@ -0,0 +1,497 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Trusthub + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TrustProductsEvaluationsInstance(InstanceResource): + + class Status(object): + COMPLIANT = "compliant" + NONCOMPLIANT = "noncompliant" + + """ + :ivar sid: The unique string that identifies the Evaluation resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the trust_product resource. + :ivar policy_sid: The unique string of a policy that is associated to the trust_product resource. + :ivar trust_product_sid: The unique string that we created to identify the trust_product resource. + :ivar status: + :ivar results: The results of the Evaluation which includes the valid and invalid attributes. + :ivar date_created: + :ivar url: + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + trust_product_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.policy_sid: Optional[str] = payload.get("policy_sid") + self.trust_product_sid: Optional[str] = payload.get("trust_product_sid") + self.status: Optional["TrustProductsEvaluationsInstance.Status"] = payload.get( + "status" + ) + self.results: Optional[List[Dict[str, object]]] = payload.get("results") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid or self.sid, + } + self._context: Optional[TrustProductsEvaluationsContext] = None + + @property + def _proxy(self) -> "TrustProductsEvaluationsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: TrustProductsEvaluationsContext for this TrustProductsEvaluationsInstance + """ + if self._context is None: + self._context = TrustProductsEvaluationsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "TrustProductsEvaluationsInstance": + """ + Fetch the TrustProductsEvaluationsInstance + + + :returns: The fetched TrustProductsEvaluationsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "TrustProductsEvaluationsInstance": + """ + Asynchronous coroutine to fetch the TrustProductsEvaluationsInstance + + + :returns: The fetched TrustProductsEvaluationsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format( + context + ) + + +class TrustProductsEvaluationsContext(InstanceContext): + + def __init__(self, version: Version, trust_product_sid: str, sid: str): + """ + Initialize the TrustProductsEvaluationsContext + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the trust_product resource. + :param sid: The unique string that identifies the Evaluation resource. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + "sid": sid, + } + self._uri = "/TrustProducts/{trust_product_sid}/Evaluations/{sid}".format( + **self._solution + ) + + def fetch(self) -> TrustProductsEvaluationsInstance: + """ + Fetch the TrustProductsEvaluationsInstance + + + :returns: The fetched TrustProductsEvaluationsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return TrustProductsEvaluationsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> TrustProductsEvaluationsInstance: + """ + Asynchronous coroutine to fetch the TrustProductsEvaluationsInstance + + + :returns: The fetched TrustProductsEvaluationsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return TrustProductsEvaluationsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class TrustProductsEvaluationsPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TrustProductsEvaluationsInstance: + """ + Build an instance of TrustProductsEvaluationsInstance + + :param payload: Payload response from the API + """ + return TrustProductsEvaluationsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TrustProductsEvaluationsList(ListResource): + + def __init__(self, version: Version, trust_product_sid: str): + """ + Initialize the TrustProductsEvaluationsList + + :param version: Version that contains the resource + :param trust_product_sid: The unique string that we created to identify the trust_product resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "trust_product_sid": trust_product_sid, + } + self._uri = "/TrustProducts/{trust_product_sid}/Evaluations".format( + **self._solution + ) + + def create(self, policy_sid: str) -> TrustProductsEvaluationsInstance: + """ + Create the TrustProductsEvaluationsInstance + + :param policy_sid: The unique string of a policy that is associated to the customer_profile resource. + + :returns: The created TrustProductsEvaluationsInstance + """ + + data = values.of( + { + "PolicySid": policy_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsEvaluationsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + async def create_async(self, policy_sid: str) -> TrustProductsEvaluationsInstance: + """ + Asynchronously create the TrustProductsEvaluationsInstance + + :param policy_sid: The unique string of a policy that is associated to the customer_profile resource. + + :returns: The created TrustProductsEvaluationsInstance + """ + + data = values.of( + { + "PolicySid": policy_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return TrustProductsEvaluationsInstance( + self._version, + payload, + trust_product_sid=self._solution["trust_product_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TrustProductsEvaluationsInstance]: + """ + Streams TrustProductsEvaluationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TrustProductsEvaluationsInstance]: + """ + Asynchronously streams TrustProductsEvaluationsInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsEvaluationsInstance]: + """ + Lists TrustProductsEvaluationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TrustProductsEvaluationsInstance]: + """ + Asynchronously lists TrustProductsEvaluationsInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsEvaluationsPage: + """ + Retrieve a single page of TrustProductsEvaluationsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsEvaluationsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TrustProductsEvaluationsPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TrustProductsEvaluationsPage: + """ + Asynchronously retrieve a single page of TrustProductsEvaluationsInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TrustProductsEvaluationsInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TrustProductsEvaluationsPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> TrustProductsEvaluationsPage: + """ + Retrieve a specific page of TrustProductsEvaluationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsEvaluationsInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TrustProductsEvaluationsPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> TrustProductsEvaluationsPage: + """ + Asynchronously retrieve a specific page of TrustProductsEvaluationsInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TrustProductsEvaluationsInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TrustProductsEvaluationsPage(self._version, response, self._solution) + + def get(self, sid: str) -> TrustProductsEvaluationsContext: + """ + Constructs a TrustProductsEvaluationsContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return TrustProductsEvaluationsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> TrustProductsEvaluationsContext: + """ + Constructs a TrustProductsEvaluationsContext + + :param sid: The unique string that identifies the Evaluation resource. + """ + return TrustProductsEvaluationsContext( + self._version, + trust_product_sid=self._solution["trust_product_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/VerifyBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/VerifyBase.py new file mode 100644 index 0000000..076f063 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/VerifyBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.verify.v2 import V2 + + +class VerifyBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Verify Domain + + :returns: Domain for Verify + """ + super().__init__(twilio, "https://verify.twilio.com") + self._v2: Optional[V2] = None + + @property + def v2(self) -> V2: + """ + :returns: Versions v2 of Verify + """ + if self._v2 is None: + self._v2 = V2(self) + return self._v2 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__init__.py new file mode 100644 index 0000000..a66b333 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__init__.py @@ -0,0 +1,67 @@ +from warnings import warn + +from twilio.rest.verify.VerifyBase import VerifyBase +from twilio.rest.verify.v2.form import FormList +from twilio.rest.verify.v2.safelist import SafelistList +from twilio.rest.verify.v2.service import ServiceList +from twilio.rest.verify.v2.template import TemplateList +from twilio.rest.verify.v2.verification_attempt import VerificationAttemptList +from twilio.rest.verify.v2.verification_attempts_summary import ( + VerificationAttemptsSummaryList, +) + + +class Verify(VerifyBase): + @property + def forms(self) -> FormList: + warn( + "forms is deprecated. Use v2.forms instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.forms + + @property + def safelist(self) -> SafelistList: + warn( + "safelist is deprecated. Use v2.safelist instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.safelist + + @property + def services(self) -> ServiceList: + warn( + "services is deprecated. Use v2.services instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.services + + @property + def verification_attempts(self) -> VerificationAttemptList: + warn( + "verification_attempts is deprecated. Use v2.verification_attempts instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.verification_attempts + + @property + def verification_attempts_summary(self) -> VerificationAttemptsSummaryList: + warn( + "verification_attempts_summary is deprecated. Use v2.verification_attempts_summary instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.verification_attempts_summary + + @property + def templates(self) -> TemplateList: + warn( + "templates is deprecated. Use v2.templates instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v2.templates diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/VerifyBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/VerifyBase.cpython-310.pyc new file mode 100644 index 0000000..470d137 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/VerifyBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f767658 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__init__.py new file mode 100644 index 0000000..01bee13 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__init__.py @@ -0,0 +1,87 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.verify.v2.form import FormList +from twilio.rest.verify.v2.safelist import SafelistList +from twilio.rest.verify.v2.service import ServiceList +from twilio.rest.verify.v2.template import TemplateList +from twilio.rest.verify.v2.verification_attempt import VerificationAttemptList +from twilio.rest.verify.v2.verification_attempts_summary import ( + VerificationAttemptsSummaryList, +) + + +class V2(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V2 version of Verify + + :param domain: The Twilio.verify domain + """ + super().__init__(domain, "v2") + self._forms: Optional[FormList] = None + self._safelist: Optional[SafelistList] = None + self._services: Optional[ServiceList] = None + self._templates: Optional[TemplateList] = None + self._verification_attempts: Optional[VerificationAttemptList] = None + self._verification_attempts_summary: Optional[ + VerificationAttemptsSummaryList + ] = None + + @property + def forms(self) -> FormList: + if self._forms is None: + self._forms = FormList(self) + return self._forms + + @property + def safelist(self) -> SafelistList: + if self._safelist is None: + self._safelist = SafelistList(self) + return self._safelist + + @property + def services(self) -> ServiceList: + if self._services is None: + self._services = ServiceList(self) + return self._services + + @property + def templates(self) -> TemplateList: + if self._templates is None: + self._templates = TemplateList(self) + return self._templates + + @property + def verification_attempts(self) -> VerificationAttemptList: + if self._verification_attempts is None: + self._verification_attempts = VerificationAttemptList(self) + return self._verification_attempts + + @property + def verification_attempts_summary(self) -> VerificationAttemptsSummaryList: + if self._verification_attempts_summary is None: + self._verification_attempts_summary = VerificationAttemptsSummaryList(self) + return self._verification_attempts_summary + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f4e10c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/form.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/form.cpython-310.pyc new file mode 100644 index 0000000..3836fc1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/form.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/safelist.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/safelist.cpython-310.pyc new file mode 100644 index 0000000..ded0b17 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/safelist.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/template.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/template.cpython-310.pyc new file mode 100644 index 0000000..d885abb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/template.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempt.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempt.cpython-310.pyc new file mode 100644 index 0000000..790f457 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempt.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempts_summary.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempts_summary.cpython-310.pyc new file mode 100644 index 0000000..0373bbe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/__pycache__/verification_attempts_summary.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/form.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/form.py new file mode 100644 index 0000000..6e3231f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/form.py @@ -0,0 +1,193 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class FormInstance(InstanceResource): + + class FormTypes(object): + FORM_PUSH = "form-push" + + """ + :ivar form_type: + :ivar forms: Object that contains the available forms for this type. This available forms are given in the standard [JSON Schema](https://json-schema.org/) format + :ivar form_meta: Additional information for the available forms for this type. E.g. The separator string used for `binding` in a Factor push. + :ivar url: The URL to access the forms for this type. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + form_type: Optional[FormTypes] = None, + ): + super().__init__(version) + + self.form_type: Optional["FormInstance.FormTypes"] = payload.get("form_type") + self.forms: Optional[Dict[str, object]] = payload.get("forms") + self.form_meta: Optional[Dict[str, object]] = payload.get("form_meta") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "form_type": form_type or self.form_type, + } + self._context: Optional[FormContext] = None + + @property + def _proxy(self) -> "FormContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FormContext for this FormInstance + """ + if self._context is None: + self._context = FormContext( + self._version, + form_type=self._solution["form_type"], + ) + return self._context + + def fetch(self) -> "FormInstance": + """ + Fetch the FormInstance + + + :returns: The fetched FormInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FormInstance": + """ + Asynchronous coroutine to fetch the FormInstance + + + :returns: The fetched FormInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FormContext(InstanceContext): + + def __init__(self, version: Version, form_type: "FormInstance.FormTypes"): + """ + Initialize the FormContext + + :param version: Version that contains the resource + :param form_type: The Type of this Form. Currently only `form-push` is supported. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "form_type": form_type, + } + self._uri = "/Forms/{form_type}".format(**self._solution) + + def fetch(self) -> FormInstance: + """ + Fetch the FormInstance + + + :returns: The fetched FormInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FormInstance( + self._version, + payload, + form_type=self._solution["form_type"], + ) + + async def fetch_async(self) -> FormInstance: + """ + Asynchronous coroutine to fetch the FormInstance + + + :returns: The fetched FormInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FormInstance( + self._version, + payload, + form_type=self._solution["form_type"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FormList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the FormList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, form_type: "FormInstance.FormTypes") -> FormContext: + """ + Constructs a FormContext + + :param form_type: The Type of this Form. Currently only `form-push` is supported. + """ + return FormContext(self._version, form_type=form_type) + + def __call__(self, form_type: "FormInstance.FormTypes") -> FormContext: + """ + Constructs a FormContext + + :param form_type: The Type of this Form. Currently only `form-push` is supported. + """ + return FormContext(self._version, form_type=form_type) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/safelist.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/safelist.py new file mode 100644 index 0000000..fb71369 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/safelist.py @@ -0,0 +1,278 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base import values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SafelistInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the SafeList resource. + :ivar phone_number: The phone number in SafeList. + :ivar url: The absolute URL of the SafeList resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + phone_number: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.phone_number: Optional[str] = payload.get("phone_number") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "phone_number": phone_number or self.phone_number, + } + self._context: Optional[SafelistContext] = None + + @property + def _proxy(self) -> "SafelistContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SafelistContext for this SafelistInstance + """ + if self._context is None: + self._context = SafelistContext( + self._version, + phone_number=self._solution["phone_number"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SafelistInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SafelistInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SafelistInstance": + """ + Fetch the SafelistInstance + + + :returns: The fetched SafelistInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SafelistInstance": + """ + Asynchronous coroutine to fetch the SafelistInstance + + + :returns: The fetched SafelistInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SafelistContext(InstanceContext): + + def __init__(self, version: Version, phone_number: str): + """ + Initialize the SafelistContext + + :param version: Version that contains the resource + :param phone_number: The phone number to be fetched from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + """ + super().__init__(version) + + # Path Solution + self._solution = { + "phone_number": phone_number, + } + self._uri = "/SafeList/Numbers/{phone_number}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the SafelistInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SafelistInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SafelistInstance: + """ + Fetch the SafelistInstance + + + :returns: The fetched SafelistInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SafelistInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + async def fetch_async(self) -> SafelistInstance: + """ + Asynchronous coroutine to fetch the SafelistInstance + + + :returns: The fetched SafelistInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SafelistInstance( + self._version, + payload, + phone_number=self._solution["phone_number"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SafelistList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SafelistList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SafeList/Numbers" + + def create(self, phone_number: str) -> SafelistInstance: + """ + Create the SafelistInstance + + :param phone_number: The phone number to be added in SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + + :returns: The created SafelistInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SafelistInstance(self._version, payload) + + async def create_async(self, phone_number: str) -> SafelistInstance: + """ + Asynchronously create the SafelistInstance + + :param phone_number: The phone number to be added in SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + + :returns: The created SafelistInstance + """ + + data = values.of( + { + "PhoneNumber": phone_number, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SafelistInstance(self._version, payload) + + def get(self, phone_number: str) -> SafelistContext: + """ + Constructs a SafelistContext + + :param phone_number: The phone number to be fetched from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + """ + return SafelistContext(self._version, phone_number=phone_number) + + def __call__(self, phone_number: str) -> SafelistContext: + """ + Constructs a SafelistContext + + :param phone_number: The phone number to be fetched from SafeList. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + """ + return SafelistContext(self._version, phone_number=phone_number) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__init__.py new file mode 100644 index 0000000..7ea0b0a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__init__.py @@ -0,0 +1,1127 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.verify.v2.service.access_token import AccessTokenList +from twilio.rest.verify.v2.service.entity import EntityList +from twilio.rest.verify.v2.service.messaging_configuration import ( + MessagingConfigurationList, +) +from twilio.rest.verify.v2.service.rate_limit import RateLimitList +from twilio.rest.verify.v2.service.verification import VerificationList +from twilio.rest.verify.v2.service.verification_check import VerificationCheckList +from twilio.rest.verify.v2.service.webhook import WebhookList + + +class ServiceInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the Service resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :ivar code_length: The length of the verification code to generate. + :ivar lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :ivar psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :ivar skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :ivar dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :ivar tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :ivar do_not_share_warning_enabled: Whether to add a security warning at the end of an SMS verification body. Disabled by default and applies only to SMS. Example SMS body: `Your AppName verification code is: 1234. Don’t share this code with anyone; our employees will never ask for the code` + :ivar custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :ivar push: Configurations for the Push factors (channel) created under this Service. + :ivar totp: Configurations for the TOTP factors (channel) created under this Service. + :ivar default_template_sid: + :ivar whatsapp: + :ivar verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.code_length: Optional[int] = deserialize.integer( + payload.get("code_length") + ) + self.lookup_enabled: Optional[bool] = payload.get("lookup_enabled") + self.psd2_enabled: Optional[bool] = payload.get("psd2_enabled") + self.skip_sms_to_landlines: Optional[bool] = payload.get( + "skip_sms_to_landlines" + ) + self.dtmf_input_required: Optional[bool] = payload.get("dtmf_input_required") + self.tts_name: Optional[str] = payload.get("tts_name") + self.do_not_share_warning_enabled: Optional[bool] = payload.get( + "do_not_share_warning_enabled" + ) + self.custom_code_enabled: Optional[bool] = payload.get("custom_code_enabled") + self.push: Optional[Dict[str, object]] = payload.get("push") + self.totp: Optional[Dict[str, object]] = payload.get("totp") + self.default_template_sid: Optional[str] = payload.get("default_template_sid") + self.whatsapp: Optional[Dict[str, object]] = payload.get("whatsapp") + self.verify_event_subscription_enabled: Optional[bool] = payload.get( + "verify_event_subscription_enabled" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ServiceContext] = None + + @property + def _proxy(self) -> "ServiceContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ServiceContext for this ServiceInstance + """ + if self._context is None: + self._context = ServiceContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ServiceInstance": + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ServiceInstance": + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a privacy warning at the end of an SMS. **Disabled by default and applies only for SMS.** + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) to associate with the Verification Service. + :param whatsapp_from: The WhatsApp number to use as the sender of the verification messages. This number must be associated with the WhatsApp Message Service. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The updated ServiceInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + code_length=code_length, + lookup_enabled=lookup_enabled, + skip_sms_to_landlines=skip_sms_to_landlines, + dtmf_input_required=dtmf_input_required, + tts_name=tts_name, + psd2_enabled=psd2_enabled, + do_not_share_warning_enabled=do_not_share_warning_enabled, + custom_code_enabled=custom_code_enabled, + push_include_date=push_include_date, + push_apn_credential_sid=push_apn_credential_sid, + push_fcm_credential_sid=push_fcm_credential_sid, + totp_issuer=totp_issuer, + totp_time_step=totp_time_step, + totp_code_length=totp_code_length, + totp_skew=totp_skew, + default_template_sid=default_template_sid, + whatsapp_msg_service_sid=whatsapp_msg_service_sid, + whatsapp_from=whatsapp_from, + verify_event_subscription_enabled=verify_event_subscription_enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> "ServiceInstance": + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a privacy warning at the end of an SMS. **Disabled by default and applies only for SMS.** + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) to associate with the Verification Service. + :param whatsapp_from: The WhatsApp number to use as the sender of the verification messages. This number must be associated with the WhatsApp Message Service. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The updated ServiceInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + code_length=code_length, + lookup_enabled=lookup_enabled, + skip_sms_to_landlines=skip_sms_to_landlines, + dtmf_input_required=dtmf_input_required, + tts_name=tts_name, + psd2_enabled=psd2_enabled, + do_not_share_warning_enabled=do_not_share_warning_enabled, + custom_code_enabled=custom_code_enabled, + push_include_date=push_include_date, + push_apn_credential_sid=push_apn_credential_sid, + push_fcm_credential_sid=push_fcm_credential_sid, + totp_issuer=totp_issuer, + totp_time_step=totp_time_step, + totp_code_length=totp_code_length, + totp_skew=totp_skew, + default_template_sid=default_template_sid, + whatsapp_msg_service_sid=whatsapp_msg_service_sid, + whatsapp_from=whatsapp_from, + verify_event_subscription_enabled=verify_event_subscription_enabled, + ) + + @property + def access_tokens(self) -> AccessTokenList: + """ + Access the access_tokens + """ + return self._proxy.access_tokens + + @property + def entities(self) -> EntityList: + """ + Access the entities + """ + return self._proxy.entities + + @property + def messaging_configurations(self) -> MessagingConfigurationList: + """ + Access the messaging_configurations + """ + return self._proxy.messaging_configurations + + @property + def rate_limits(self) -> RateLimitList: + """ + Access the rate_limits + """ + return self._proxy.rate_limits + + @property + def verifications(self) -> VerificationList: + """ + Access the verifications + """ + return self._proxy.verifications + + @property + def verification_checks(self) -> VerificationCheckList: + """ + Access the verification_checks + """ + return self._proxy.verification_checks + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + return self._proxy.webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServiceContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ServiceContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Services/{sid}".format(**self._solution) + + self._access_tokens: Optional[AccessTokenList] = None + self._entities: Optional[EntityList] = None + self._messaging_configurations: Optional[MessagingConfigurationList] = None + self._rate_limits: Optional[RateLimitList] = None + self._verifications: Optional[VerificationList] = None + self._verification_checks: Optional[VerificationCheckList] = None + self._webhooks: Optional[WebhookList] = None + + def delete(self) -> bool: + """ + Deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ServiceInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ServiceInstance: + """ + Fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ServiceInstance: + """ + Asynchronous coroutine to fetch the ServiceInstance + + + :returns: The fetched ServiceInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ServiceInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a privacy warning at the end of an SMS. **Disabled by default and applies only for SMS.** + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) to associate with the Verification Service. + :param whatsapp_from: The WhatsApp number to use as the sender of the verification messages. This number must be associated with the WhatsApp Message Service. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "CodeLength": code_length, + "LookupEnabled": serialize.boolean_to_string(lookup_enabled), + "SkipSmsToLandlines": serialize.boolean_to_string( + skip_sms_to_landlines + ), + "DtmfInputRequired": serialize.boolean_to_string(dtmf_input_required), + "TtsName": tts_name, + "Psd2Enabled": serialize.boolean_to_string(psd2_enabled), + "DoNotShareWarningEnabled": serialize.boolean_to_string( + do_not_share_warning_enabled + ), + "CustomCodeEnabled": serialize.boolean_to_string(custom_code_enabled), + "Push.IncludeDate": serialize.boolean_to_string(push_include_date), + "Push.ApnCredentialSid": push_apn_credential_sid, + "Push.FcmCredentialSid": push_fcm_credential_sid, + "Totp.Issuer": totp_issuer, + "Totp.TimeStep": totp_time_step, + "Totp.CodeLength": totp_code_length, + "Totp.Skew": totp_skew, + "DefaultTemplateSid": default_template_sid, + "Whatsapp.MsgServiceSid": whatsapp_msg_service_sid, + "Whatsapp.From": whatsapp_from, + "VerifyEventSubscriptionEnabled": serialize.boolean_to_string( + verify_event_subscription_enabled + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronous coroutine to update the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a privacy warning at the end of an SMS. **Disabled by default and applies only for SMS.** + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/services) to associate with the Verification Service. + :param whatsapp_from: The WhatsApp number to use as the sender of the verification messages. This number must be associated with the WhatsApp Message Service. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The updated ServiceInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "CodeLength": code_length, + "LookupEnabled": serialize.boolean_to_string(lookup_enabled), + "SkipSmsToLandlines": serialize.boolean_to_string( + skip_sms_to_landlines + ), + "DtmfInputRequired": serialize.boolean_to_string(dtmf_input_required), + "TtsName": tts_name, + "Psd2Enabled": serialize.boolean_to_string(psd2_enabled), + "DoNotShareWarningEnabled": serialize.boolean_to_string( + do_not_share_warning_enabled + ), + "CustomCodeEnabled": serialize.boolean_to_string(custom_code_enabled), + "Push.IncludeDate": serialize.boolean_to_string(push_include_date), + "Push.ApnCredentialSid": push_apn_credential_sid, + "Push.FcmCredentialSid": push_fcm_credential_sid, + "Totp.Issuer": totp_issuer, + "Totp.TimeStep": totp_time_step, + "Totp.CodeLength": totp_code_length, + "Totp.Skew": totp_skew, + "DefaultTemplateSid": default_template_sid, + "Whatsapp.MsgServiceSid": whatsapp_msg_service_sid, + "Whatsapp.From": whatsapp_from, + "VerifyEventSubscriptionEnabled": serialize.boolean_to_string( + verify_event_subscription_enabled + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def access_tokens(self) -> AccessTokenList: + """ + Access the access_tokens + """ + if self._access_tokens is None: + self._access_tokens = AccessTokenList( + self._version, + self._solution["sid"], + ) + return self._access_tokens + + @property + def entities(self) -> EntityList: + """ + Access the entities + """ + if self._entities is None: + self._entities = EntityList( + self._version, + self._solution["sid"], + ) + return self._entities + + @property + def messaging_configurations(self) -> MessagingConfigurationList: + """ + Access the messaging_configurations + """ + if self._messaging_configurations is None: + self._messaging_configurations = MessagingConfigurationList( + self._version, + self._solution["sid"], + ) + return self._messaging_configurations + + @property + def rate_limits(self) -> RateLimitList: + """ + Access the rate_limits + """ + if self._rate_limits is None: + self._rate_limits = RateLimitList( + self._version, + self._solution["sid"], + ) + return self._rate_limits + + @property + def verifications(self) -> VerificationList: + """ + Access the verifications + """ + if self._verifications is None: + self._verifications = VerificationList( + self._version, + self._solution["sid"], + ) + return self._verifications + + @property + def verification_checks(self) -> VerificationCheckList: + """ + Access the verification_checks + """ + if self._verification_checks is None: + self._verification_checks = VerificationCheckList( + self._version, + self._solution["sid"], + ) + return self._verification_checks + + @property + def webhooks(self) -> WebhookList: + """ + Access the webhooks + """ + if self._webhooks is None: + self._webhooks = WebhookList( + self._version, + self._solution["sid"], + ) + return self._webhooks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ServicePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ServiceInstance: + """ + Build an instance of ServiceInstance + + :param payload: Payload response from the API + """ + return ServiceInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ServiceList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ServiceList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Services" + + def create( + self, + friendly_name: str, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a security warning at the end of an SMS verification body. Disabled by default and applies only to SMS. Example SMS body: `Your AppName verification code is: 1234. Don’t share this code with anyone; our employees will never ask for the code` + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. This timestamp value is the same one as the one found in `date_created`, please use that one instead. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. Defaults to the service friendly name if not provided. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the Messaging Service containing WhatsApp Sender(s) that Verify will use to send WhatsApp messages to your users. + :param whatsapp_from: The number to use as the WhatsApp Sender that Verify will use to send WhatsApp messages to your users.This WhatsApp Sender must be associated with a Messaging Service SID. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "CodeLength": code_length, + "LookupEnabled": serialize.boolean_to_string(lookup_enabled), + "SkipSmsToLandlines": serialize.boolean_to_string( + skip_sms_to_landlines + ), + "DtmfInputRequired": serialize.boolean_to_string(dtmf_input_required), + "TtsName": tts_name, + "Psd2Enabled": serialize.boolean_to_string(psd2_enabled), + "DoNotShareWarningEnabled": serialize.boolean_to_string( + do_not_share_warning_enabled + ), + "CustomCodeEnabled": serialize.boolean_to_string(custom_code_enabled), + "Push.IncludeDate": serialize.boolean_to_string(push_include_date), + "Push.ApnCredentialSid": push_apn_credential_sid, + "Push.FcmCredentialSid": push_fcm_credential_sid, + "Totp.Issuer": totp_issuer, + "Totp.TimeStep": totp_time_step, + "Totp.CodeLength": totp_code_length, + "Totp.Skew": totp_skew, + "DefaultTemplateSid": default_template_sid, + "Whatsapp.MsgServiceSid": whatsapp_msg_service_sid, + "Whatsapp.From": whatsapp_from, + "VerifyEventSubscriptionEnabled": serialize.boolean_to_string( + verify_event_subscription_enabled + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + code_length: Union[int, object] = values.unset, + lookup_enabled: Union[bool, object] = values.unset, + skip_sms_to_landlines: Union[bool, object] = values.unset, + dtmf_input_required: Union[bool, object] = values.unset, + tts_name: Union[str, object] = values.unset, + psd2_enabled: Union[bool, object] = values.unset, + do_not_share_warning_enabled: Union[bool, object] = values.unset, + custom_code_enabled: Union[bool, object] = values.unset, + push_include_date: Union[bool, object] = values.unset, + push_apn_credential_sid: Union[str, object] = values.unset, + push_fcm_credential_sid: Union[str, object] = values.unset, + totp_issuer: Union[str, object] = values.unset, + totp_time_step: Union[int, object] = values.unset, + totp_code_length: Union[int, object] = values.unset, + totp_skew: Union[int, object] = values.unset, + default_template_sid: Union[str, object] = values.unset, + whatsapp_msg_service_sid: Union[str, object] = values.unset, + whatsapp_from: Union[str, object] = values.unset, + verify_event_subscription_enabled: Union[bool, object] = values.unset, + ) -> ServiceInstance: + """ + Asynchronously create the ServiceInstance + + :param friendly_name: A descriptive string that you create to describe the verification service. It can be up to 32 characters long. **This value should not contain PII.** + :param code_length: The length of the verification code to generate. Must be an integer value between 4 and 10, inclusive. + :param lookup_enabled: Whether to perform a lookup with each verification started and return info about the phone number. + :param skip_sms_to_landlines: Whether to skip sending SMS verifications to landlines. Requires `lookup_enabled`. + :param dtmf_input_required: Whether to ask the user to press a number before delivering the verify code in a phone call. + :param tts_name: The name of an alternative text-to-speech service to use in phone calls. Applies only to TTS languages. + :param psd2_enabled: Whether to pass PSD2 transaction parameters when starting a verification. + :param do_not_share_warning_enabled: Whether to add a security warning at the end of an SMS verification body. Disabled by default and applies only to SMS. Example SMS body: `Your AppName verification code is: 1234. Don’t share this code with anyone; our employees will never ask for the code` + :param custom_code_enabled: Whether to allow sending verifications with a custom code instead of a randomly generated one. Not available for all customers. + :param push_include_date: Optional configuration for the Push factors. If true, include the date in the Challenge's response. Otherwise, the date is omitted from the response. See [Challenge](https://www.twilio.com/docs/verify/api/challenge) resource’s details parameter for more info. Default: false. **Deprecated** do not use this parameter. This timestamp value is the same one as the one found in `date_created`, please use that one instead. + :param push_apn_credential_sid: Optional configuration for the Push factors. Set the APN Credential for this service. This will allow to send push notifications to iOS devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param push_fcm_credential_sid: Optional configuration for the Push factors. Set the FCM Credential for this service. This will allow to send push notifications to Android devices. See [Credential Resource](https://www.twilio.com/docs/notify/api/credential-resource) + :param totp_issuer: Optional configuration for the TOTP factors. Set TOTP Issuer for this service. This will allow to configure the issuer of the TOTP URI. Defaults to the service friendly name if not provided. + :param totp_time_step: Optional configuration for the TOTP factors. Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. Defaults to 30 seconds + :param totp_code_length: Optional configuration for the TOTP factors. Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. Defaults to 6 + :param totp_skew: Optional configuration for the TOTP factors. The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. Defaults to 1 + :param default_template_sid: The default message [template](https://www.twilio.com/docs/verify/api/templates). Will be used for all SMS verifications unless explicitly overriden. SMS channel only. + :param whatsapp_msg_service_sid: The SID of the Messaging Service containing WhatsApp Sender(s) that Verify will use to send WhatsApp messages to your users. + :param whatsapp_from: The number to use as the WhatsApp Sender that Verify will use to send WhatsApp messages to your users.This WhatsApp Sender must be associated with a Messaging Service SID. + :param verify_event_subscription_enabled: Whether to allow verifications from the service to reach the stream-events sinks if configured + + :returns: The created ServiceInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "CodeLength": code_length, + "LookupEnabled": serialize.boolean_to_string(lookup_enabled), + "SkipSmsToLandlines": serialize.boolean_to_string( + skip_sms_to_landlines + ), + "DtmfInputRequired": serialize.boolean_to_string(dtmf_input_required), + "TtsName": tts_name, + "Psd2Enabled": serialize.boolean_to_string(psd2_enabled), + "DoNotShareWarningEnabled": serialize.boolean_to_string( + do_not_share_warning_enabled + ), + "CustomCodeEnabled": serialize.boolean_to_string(custom_code_enabled), + "Push.IncludeDate": serialize.boolean_to_string(push_include_date), + "Push.ApnCredentialSid": push_apn_credential_sid, + "Push.FcmCredentialSid": push_fcm_credential_sid, + "Totp.Issuer": totp_issuer, + "Totp.TimeStep": totp_time_step, + "Totp.CodeLength": totp_code_length, + "Totp.Skew": totp_skew, + "DefaultTemplateSid": default_template_sid, + "Whatsapp.MsgServiceSid": whatsapp_msg_service_sid, + "Whatsapp.From": whatsapp_from, + "VerifyEventSubscriptionEnabled": serialize.boolean_to_string( + verify_event_subscription_enabled + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ServiceInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ServiceInstance]: + """ + Streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ServiceInstance]: + """ + Asynchronously streams ServiceInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ServiceInstance]: + """ + Asynchronously lists ServiceInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ServicePage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ServicePage: + """ + Asynchronously retrieve a single page of ServiceInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ServiceInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ServicePage(self._version, response) + + def get_page(self, target_url: str) -> ServicePage: + """ + Retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ServicePage(self._version, response) + + async def get_page_async(self, target_url: str) -> ServicePage: + """ + Asynchronously retrieve a specific page of ServiceInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ServiceInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ServicePage(self._version, response) + + def get(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ServiceContext: + """ + Constructs a ServiceContext + + :param sid: The Twilio-provided string that uniquely identifies the Service resource to update. + """ + return ServiceContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2defd2c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/access_token.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/access_token.cpython-310.pyc new file mode 100644 index 0000000..85d702d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/access_token.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/messaging_configuration.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/messaging_configuration.cpython-310.pyc new file mode 100644 index 0000000..e65794c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/messaging_configuration.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification.cpython-310.pyc new file mode 100644 index 0000000..293be38 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification_check.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification_check.cpython-310.pyc new file mode 100644 index 0000000..dee153c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/verification_check.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/webhook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/webhook.cpython-310.pyc new file mode 100644 index 0000000..76e3903 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/__pycache__/webhook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/access_token.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/access_token.py new file mode 100644 index 0000000..0bb888a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/access_token.py @@ -0,0 +1,305 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AccessTokenInstance(InstanceResource): + + class FactorTypes(object): + PUSH = "push" + + """ + :ivar sid: A 34 character string that uniquely identifies this Access Token. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Verify Service. + :ivar entity_identity: The unique external identifier for the Entity of the Service. + :ivar factor_type: + :ivar factor_friendly_name: A human readable description of this factor, up to 64 characters. For a push factor, this can be the device's name. + :ivar token: The access token generated for enrollment, this is an encrypted json web token. + :ivar url: The URL of this resource. + :ivar ttl: How long, in seconds, the access token is valid. Max: 5 minutes + :ivar date_created: The date that this access token was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.entity_identity: Optional[str] = payload.get("entity_identity") + self.factor_type: Optional["AccessTokenInstance.FactorTypes"] = payload.get( + "factor_type" + ) + self.factor_friendly_name: Optional[str] = payload.get("factor_friendly_name") + self.token: Optional[str] = payload.get("token") + self.url: Optional[str] = payload.get("url") + self.ttl: Optional[int] = deserialize.integer(payload.get("ttl")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[AccessTokenContext] = None + + @property + def _proxy(self) -> "AccessTokenContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AccessTokenContext for this AccessTokenInstance + """ + if self._context is None: + self._context = AccessTokenContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "AccessTokenInstance": + """ + Fetch the AccessTokenInstance + + + :returns: The fetched AccessTokenInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "AccessTokenInstance": + """ + Asynchronous coroutine to fetch the AccessTokenInstance + + + :returns: The fetched AccessTokenInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccessTokenContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the AccessTokenContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param sid: A 34 character string that uniquely identifies this Access Token. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/AccessTokens/{sid}".format( + **self._solution + ) + + def fetch(self) -> AccessTokenInstance: + """ + Fetch the AccessTokenInstance + + + :returns: The fetched AccessTokenInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return AccessTokenInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> AccessTokenInstance: + """ + Asynchronous coroutine to fetch the AccessTokenInstance + + + :returns: The fetched AccessTokenInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return AccessTokenInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AccessTokenList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the AccessTokenList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/AccessTokens".format(**self._solution) + + def create( + self, + identity: str, + factor_type: "AccessTokenInstance.FactorTypes", + factor_friendly_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> AccessTokenInstance: + """ + Create the AccessTokenInstance + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, and generated by your external system, such as your user's UUID, GUID, or SID. + :param factor_type: + :param factor_friendly_name: The friendly name of the factor that is going to be created with this access token + :param ttl: How long, in seconds, the access token is valid. Can be an integer between 60 and 300. Default is 60. + + :returns: The created AccessTokenInstance + """ + + data = values.of( + { + "Identity": identity, + "FactorType": factor_type, + "FactorFriendlyName": factor_friendly_name, + "Ttl": ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return AccessTokenInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + identity: str, + factor_type: "AccessTokenInstance.FactorTypes", + factor_friendly_name: Union[str, object] = values.unset, + ttl: Union[int, object] = values.unset, + ) -> AccessTokenInstance: + """ + Asynchronously create the AccessTokenInstance + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, and generated by your external system, such as your user's UUID, GUID, or SID. + :param factor_type: + :param factor_friendly_name: The friendly name of the factor that is going to be created with this access token + :param ttl: How long, in seconds, the access token is valid. Can be an integer between 60 and 300. Default is 60. + + :returns: The created AccessTokenInstance + """ + + data = values.of( + { + "Identity": identity, + "FactorType": factor_type, + "FactorFriendlyName": factor_friendly_name, + "Ttl": ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AccessTokenInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def get(self, sid: str) -> AccessTokenContext: + """ + Constructs a AccessTokenContext + + :param sid: A 34 character string that uniquely identifies this Access Token. + """ + return AccessTokenContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> AccessTokenContext: + """ + Constructs a AccessTokenContext + + :param sid: A 34 character string that uniquely identifies this Access Token. + """ + return AccessTokenContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__init__.py new file mode 100644 index 0000000..41541a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__init__.py @@ -0,0 +1,587 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.verify.v2.service.entity.challenge import ChallengeList +from twilio.rest.verify.v2.service.entity.factor import FactorList +from twilio.rest.verify.v2.service.entity.new_factor import NewFactorList + + +class EntityInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this Entity. + :ivar identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar date_created: The date that this Entity was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Entity was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Entity. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + identity: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.identity: Optional[str] = payload.get("identity") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "identity": identity or self.identity, + } + self._context: Optional[EntityContext] = None + + @property + def _proxy(self) -> "EntityContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: EntityContext for this EntityInstance + """ + if self._context is None: + self._context = EntityContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the EntityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EntityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "EntityInstance": + """ + Fetch the EntityInstance + + + :returns: The fetched EntityInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "EntityInstance": + """ + Asynchronous coroutine to fetch the EntityInstance + + + :returns: The fetched EntityInstance + """ + return await self._proxy.fetch_async() + + @property + def challenges(self) -> ChallengeList: + """ + Access the challenges + """ + return self._proxy.challenges + + @property + def factors(self) -> FactorList: + """ + Access the factors + """ + return self._proxy.factors + + @property + def new_factors(self) -> NewFactorList: + """ + Access the new_factors + """ + return self._proxy.new_factors + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EntityContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, identity: str): + """ + Initialize the EntityContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Entities/{identity}".format( + **self._solution + ) + + self._challenges: Optional[ChallengeList] = None + self._factors: Optional[FactorList] = None + self._new_factors: Optional[NewFactorList] = None + + def delete(self) -> bool: + """ + Deletes the EntityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the EntityInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> EntityInstance: + """ + Fetch the EntityInstance + + + :returns: The fetched EntityInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return EntityInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + async def fetch_async(self) -> EntityInstance: + """ + Asynchronous coroutine to fetch the EntityInstance + + + :returns: The fetched EntityInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return EntityInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + @property + def challenges(self) -> ChallengeList: + """ + Access the challenges + """ + if self._challenges is None: + self._challenges = ChallengeList( + self._version, + self._solution["service_sid"], + self._solution["identity"], + ) + return self._challenges + + @property + def factors(self) -> FactorList: + """ + Access the factors + """ + if self._factors is None: + self._factors = FactorList( + self._version, + self._solution["service_sid"], + self._solution["identity"], + ) + return self._factors + + @property + def new_factors(self) -> NewFactorList: + """ + Access the new_factors + """ + if self._new_factors is None: + self._new_factors = NewFactorList( + self._version, + self._solution["service_sid"], + self._solution["identity"], + ) + return self._new_factors + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class EntityPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> EntityInstance: + """ + Build an instance of EntityInstance + + :param payload: Payload response from the API + """ + return EntityInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class EntityList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the EntityList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Entities".format(**self._solution) + + def create(self, identity: str) -> EntityInstance: + """ + Create the EntityInstance + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + + :returns: The created EntityInstance + """ + + data = values.of( + { + "Identity": identity, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return EntityInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async(self, identity: str) -> EntityInstance: + """ + Asynchronously create the EntityInstance + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + + :returns: The created EntityInstance + """ + + data = values.of( + { + "Identity": identity, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return EntityInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[EntityInstance]: + """ + Streams EntityInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[EntityInstance]: + """ + Asynchronously streams EntityInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EntityInstance]: + """ + Lists EntityInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[EntityInstance]: + """ + Asynchronously lists EntityInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EntityPage: + """ + Retrieve a single page of EntityInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EntityInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return EntityPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> EntityPage: + """ + Asynchronously retrieve a single page of EntityInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of EntityInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return EntityPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> EntityPage: + """ + Retrieve a specific page of EntityInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EntityInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return EntityPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> EntityPage: + """ + Asynchronously retrieve a specific page of EntityInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of EntityInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return EntityPage(self._version, response, self._solution) + + def get(self, identity: str) -> EntityContext: + """ + Constructs a EntityContext + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + """ + return EntityContext( + self._version, service_sid=self._solution["service_sid"], identity=identity + ) + + def __call__(self, identity: str) -> EntityContext: + """ + Constructs a EntityContext + + :param identity: The unique external identifier for the Entity of the Service. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + """ + return EntityContext( + self._version, service_sid=self._solution["service_sid"], identity=identity + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c5d0cb6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/factor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/factor.cpython-310.pyc new file mode 100644 index 0000000..52842c7 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/factor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/new_factor.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/new_factor.cpython-310.pyc new file mode 100644 index 0000000..4dcb231 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/__pycache__/new_factor.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__init__.py new file mode 100644 index 0000000..ab8c7f8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__init__.py @@ -0,0 +1,782 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.verify.v2.service.entity.challenge.notification import NotificationList + + +class ChallengeInstance(InstanceResource): + + class ChallengeReasons(object): + NONE = "none" + NOT_NEEDED = "not_needed" + NOT_REQUESTED = "not_requested" + + class ChallengeStatuses(object): + PENDING = "pending" + EXPIRED = "expired" + APPROVED = "approved" + DENIED = "denied" + + class FactorTypes(object): + PUSH = "push" + TOTP = "totp" + + class ListOrders(object): + ASC = "asc" + DESC = "desc" + + """ + :ivar sid: A 34 character string that uniquely identifies this Challenge. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar entity_sid: The unique SID identifier of the Entity. + :ivar identity: Customer unique identity for the Entity owner of the Challenge. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :ivar factor_sid: The unique SID identifier of the Factor. + :ivar date_created: The date that this Challenge was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Challenge was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_responded: The date that this Challenge was responded, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar expiration_date: The date-time when this Challenge expires, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. The default value is five (5) minutes after Challenge creation. The max value is sixty (60) minutes after creation. + :ivar status: + :ivar responded_reason: + :ivar details: Details provided to give context about the Challenge. Intended to be shown to the end user. + :ivar hidden_details: Details provided to give context about the Challenge. Intended to be hidden from the end user. It must be a stringified JSON with only strings values eg. `{\"ip\": \"172.168.1.234\"}` + :ivar metadata: Custom metadata associated with the challenge. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\"os\": \"Android\"}`. Can be up to 1024 characters in length. + :ivar factor_type: + :ivar url: The URL of this resource. + :ivar links: Contains a dictionary of URL links to nested resources of this Challenge. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + identity: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.entity_sid: Optional[str] = payload.get("entity_sid") + self.identity: Optional[str] = payload.get("identity") + self.factor_sid: Optional[str] = payload.get("factor_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.date_responded: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_responded") + ) + self.expiration_date: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("expiration_date") + ) + self.status: Optional["ChallengeInstance.ChallengeStatuses"] = payload.get( + "status" + ) + self.responded_reason: Optional["ChallengeInstance.ChallengeReasons"] = ( + payload.get("responded_reason") + ) + self.details: Optional[Dict[str, object]] = payload.get("details") + self.hidden_details: Optional[Dict[str, object]] = payload.get("hidden_details") + self.metadata: Optional[Dict[str, object]] = payload.get("metadata") + self.factor_type: Optional["ChallengeInstance.FactorTypes"] = payload.get( + "factor_type" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "identity": identity, + "sid": sid or self.sid, + } + self._context: Optional[ChallengeContext] = None + + @property + def _proxy(self) -> "ChallengeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ChallengeContext for this ChallengeInstance + """ + if self._context is None: + self._context = ChallengeContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ChallengeInstance": + """ + Fetch the ChallengeInstance + + + :returns: The fetched ChallengeInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ChallengeInstance": + """ + Asynchronous coroutine to fetch the ChallengeInstance + + + :returns: The fetched ChallengeInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + auth_payload: Union[str, object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> "ChallengeInstance": + """ + Update the ChallengeInstance + + :param auth_payload: The optional payload needed to verify the Challenge. E.g., a TOTP would use the numeric code. For `TOTP` this value must be between 3 and 8 characters long. For `Push` this value can be up to 5456 characters in length + :param metadata: Custom metadata associated with the challenge. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The updated ChallengeInstance + """ + return self._proxy.update( + auth_payload=auth_payload, + metadata=metadata, + ) + + async def update_async( + self, + auth_payload: Union[str, object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> "ChallengeInstance": + """ + Asynchronous coroutine to update the ChallengeInstance + + :param auth_payload: The optional payload needed to verify the Challenge. E.g., a TOTP would use the numeric code. For `TOTP` this value must be between 3 and 8 characters long. For `Push` this value can be up to 5456 characters in length + :param metadata: Custom metadata associated with the challenge. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The updated ChallengeInstance + """ + return await self._proxy.update_async( + auth_payload=auth_payload, + metadata=metadata, + ) + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + return self._proxy.notifications + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChallengeContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, identity: str, sid: str): + """ + Initialize the ChallengeContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Challenge. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :param sid: A 34 character string that uniquely identifies this Challenge. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/Entities/{identity}/Challenges/{sid}".format( + **self._solution + ) + ) + + self._notifications: Optional[NotificationList] = None + + def fetch(self) -> ChallengeInstance: + """ + Fetch the ChallengeInstance + + + :returns: The fetched ChallengeInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ChallengeInstance: + """ + Asynchronous coroutine to fetch the ChallengeInstance + + + :returns: The fetched ChallengeInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + def update( + self, + auth_payload: Union[str, object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> ChallengeInstance: + """ + Update the ChallengeInstance + + :param auth_payload: The optional payload needed to verify the Challenge. E.g., a TOTP would use the numeric code. For `TOTP` this value must be between 3 and 8 characters long. For `Push` this value can be up to 5456 characters in length + :param metadata: Custom metadata associated with the challenge. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The updated ChallengeInstance + """ + data = values.of( + { + "AuthPayload": auth_payload, + "Metadata": serialize.object(metadata), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + auth_payload: Union[str, object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> ChallengeInstance: + """ + Asynchronous coroutine to update the ChallengeInstance + + :param auth_payload: The optional payload needed to verify the Challenge. E.g., a TOTP would use the numeric code. For `TOTP` this value must be between 3 and 8 characters long. For `Push` this value can be up to 5456 characters in length + :param metadata: Custom metadata associated with the challenge. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The updated ChallengeInstance + """ + data = values.of( + { + "AuthPayload": auth_payload, + "Metadata": serialize.object(metadata), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + @property + def notifications(self) -> NotificationList: + """ + Access the notifications + """ + if self._notifications is None: + self._notifications = NotificationList( + self._version, + self._solution["service_sid"], + self._solution["identity"], + self._solution["sid"], + ) + return self._notifications + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ChallengePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ChallengeInstance: + """ + Build an instance of ChallengeInstance + + :param payload: Payload response from the API + """ + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ChallengeList(ListResource): + + def __init__(self, version: Version, service_sid: str, identity: str): + """ + Initialize the ChallengeList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Challenge. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Entities/{identity}/Challenges".format( + **self._solution + ) + + def create( + self, + factor_sid: str, + expiration_date: Union[datetime, object] = values.unset, + details_message: Union[str, object] = values.unset, + details_fields: Union[List[object], object] = values.unset, + hidden_details: Union[object, object] = values.unset, + auth_payload: Union[str, object] = values.unset, + ) -> ChallengeInstance: + """ + Create the ChallengeInstance + + :param factor_sid: The unique SID identifier of the Factor. + :param expiration_date: The date-time when this Challenge expires, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. The default value is five (5) minutes after Challenge creation. The max value is sixty (60) minutes after creation. + :param details_message: Shown to the user when the push notification arrives. Required when `factor_type` is `push`. Can be up to 256 characters in length + :param details_fields: A list of objects that describe the Fields included in the Challenge. Each object contains the label and value of the field, the label can be up to 36 characters in length and the value can be up to 128 characters in length. Used when `factor_type` is `push`. There can be up to 20 details fields. + :param hidden_details: Details provided to give context about the Challenge. Not shown to the end user. It must be a stringified JSON with only strings values eg. `{\\\"ip\\\": \\\"172.168.1.234\\\"}`. Can be up to 1024 characters in length + :param auth_payload: Optional payload used to verify the Challenge upon creation. Only used with a Factor of type `totp` to carry the TOTP code that needs to be verified. For `TOTP` this value must be between 3 and 8 characters long. + + :returns: The created ChallengeInstance + """ + + data = values.of( + { + "FactorSid": factor_sid, + "ExpirationDate": serialize.iso8601_datetime(expiration_date), + "Details.Message": details_message, + "Details.Fields": serialize.map( + details_fields, lambda e: serialize.object(e) + ), + "HiddenDetails": serialize.object(hidden_details), + "AuthPayload": auth_payload, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + async def create_async( + self, + factor_sid: str, + expiration_date: Union[datetime, object] = values.unset, + details_message: Union[str, object] = values.unset, + details_fields: Union[List[object], object] = values.unset, + hidden_details: Union[object, object] = values.unset, + auth_payload: Union[str, object] = values.unset, + ) -> ChallengeInstance: + """ + Asynchronously create the ChallengeInstance + + :param factor_sid: The unique SID identifier of the Factor. + :param expiration_date: The date-time when this Challenge expires, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. The default value is five (5) minutes after Challenge creation. The max value is sixty (60) minutes after creation. + :param details_message: Shown to the user when the push notification arrives. Required when `factor_type` is `push`. Can be up to 256 characters in length + :param details_fields: A list of objects that describe the Fields included in the Challenge. Each object contains the label and value of the field, the label can be up to 36 characters in length and the value can be up to 128 characters in length. Used when `factor_type` is `push`. There can be up to 20 details fields. + :param hidden_details: Details provided to give context about the Challenge. Not shown to the end user. It must be a stringified JSON with only strings values eg. `{\\\"ip\\\": \\\"172.168.1.234\\\"}`. Can be up to 1024 characters in length + :param auth_payload: Optional payload used to verify the Challenge upon creation. Only used with a Factor of type `totp` to carry the TOTP code that needs to be verified. For `TOTP` this value must be between 3 and 8 characters long. + + :returns: The created ChallengeInstance + """ + + data = values.of( + { + "FactorSid": factor_sid, + "ExpirationDate": serialize.iso8601_datetime(expiration_date), + "Details.Message": details_message, + "Details.Fields": serialize.map( + details_fields, lambda e: serialize.object(e) + ), + "HiddenDetails": serialize.object(hidden_details), + "AuthPayload": auth_payload, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ChallengeInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + def stream( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ChallengeInstance]: + """ + Streams ChallengeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str factor_sid: The unique SID identifier of the Factor. + :param "ChallengeInstance.ChallengeStatuses" status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param "ChallengeInstance.ListOrders" order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + factor_sid=factor_sid, + status=status, + order=order, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ChallengeInstance]: + """ + Asynchronously streams ChallengeInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str factor_sid: The unique SID identifier of the Factor. + :param "ChallengeInstance.ChallengeStatuses" status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param "ChallengeInstance.ListOrders" order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + factor_sid=factor_sid, + status=status, + order=order, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChallengeInstance]: + """ + Lists ChallengeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str factor_sid: The unique SID identifier of the Factor. + :param "ChallengeInstance.ChallengeStatuses" status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param "ChallengeInstance.ListOrders" order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + factor_sid=factor_sid, + status=status, + order=order, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ChallengeInstance]: + """ + Asynchronously lists ChallengeInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str factor_sid: The unique SID identifier of the Factor. + :param "ChallengeInstance.ChallengeStatuses" status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param "ChallengeInstance.ListOrders" order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + factor_sid=factor_sid, + status=status, + order=order, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChallengePage: + """ + Retrieve a single page of ChallengeInstance records from the API. + Request is executed immediately + + :param factor_sid: The unique SID identifier of the Factor. + :param status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChallengeInstance + """ + data = values.of( + { + "FactorSid": factor_sid, + "Status": status, + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ChallengePage(self._version, response, self._solution) + + async def page_async( + self, + factor_sid: Union[str, object] = values.unset, + status: Union["ChallengeInstance.ChallengeStatuses", object] = values.unset, + order: Union["ChallengeInstance.ListOrders", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ChallengePage: + """ + Asynchronously retrieve a single page of ChallengeInstance records from the API. + Request is executed immediately + + :param factor_sid: The unique SID identifier of the Factor. + :param status: The Status of the Challenges to fetch. One of `pending`, `expired`, `approved` or `denied`. + :param order: The desired sort order of the Challenges list. One of `asc` or `desc` for ascending and descending respectively. Defaults to `asc`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ChallengeInstance + """ + data = values.of( + { + "FactorSid": factor_sid, + "Status": status, + "Order": order, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ChallengePage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ChallengePage: + """ + Retrieve a specific page of ChallengeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChallengeInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ChallengePage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ChallengePage: + """ + Asynchronously retrieve a specific page of ChallengeInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ChallengeInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ChallengePage(self._version, response, self._solution) + + def get(self, sid: str) -> ChallengeContext: + """ + Constructs a ChallengeContext + + :param sid: A 34 character string that uniquely identifies this Challenge. + """ + return ChallengeContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=sid, + ) + + def __call__(self, sid: str) -> ChallengeContext: + """ + Constructs a ChallengeContext + + :param sid: A 34 character string that uniquely identifies this Challenge. + """ + return ChallengeContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4e79d18 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/notification.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/notification.cpython-310.pyc new file mode 100644 index 0000000..ee6b365 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/__pycache__/notification.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/notification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/notification.py new file mode 100644 index 0000000..0cfae0a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/challenge/notification.py @@ -0,0 +1,167 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NotificationInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this Notification. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar entity_sid: The unique SID identifier of the Entity. + :ivar identity: Customer unique identity for the Entity owner of the Challenge. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :ivar challenge_sid: The unique SID identifier of the Challenge. + :ivar priority: The priority of the notification. For `push` Challenges it's always `high` which sends the notification immediately, and can wake up a sleeping device. + :ivar ttl: How long, in seconds, the notification is valid. Max: 5 minutes + :ivar date_created: The date that this Notification was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + identity: str, + challenge_sid: str, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.entity_sid: Optional[str] = payload.get("entity_sid") + self.identity: Optional[str] = payload.get("identity") + self.challenge_sid: Optional[str] = payload.get("challenge_sid") + self.priority: Optional[str] = payload.get("priority") + self.ttl: Optional[int] = deserialize.integer(payload.get("ttl")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + + self._solution = { + "service_sid": service_sid, + "identity": identity, + "challenge_sid": challenge_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NotificationList(ListResource): + + def __init__( + self, version: Version, service_sid: str, identity: str, challenge_sid: str + ): + """ + Initialize the NotificationList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Challenge. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :param challenge_sid: The unique SID identifier of the Challenge. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + "challenge_sid": challenge_sid, + } + self._uri = "/Services/{service_sid}/Entities/{identity}/Challenges/{challenge_sid}/Notifications".format( + **self._solution + ) + + def create(self, ttl: Union[int, object] = values.unset) -> NotificationInstance: + """ + Create the NotificationInstance + + :param ttl: How long, in seconds, the notification is valid. Can be an integer between 0 and 300. Default is 300. Delivery is attempted until the TTL elapses, even if the device is offline. 0 means that the notification delivery is attempted immediately, only once, and is not stored for future delivery. + + :returns: The created NotificationInstance + """ + + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + challenge_sid=self._solution["challenge_sid"], + ) + + async def create_async( + self, ttl: Union[int, object] = values.unset + ) -> NotificationInstance: + """ + Asynchronously create the NotificationInstance + + :param ttl: How long, in seconds, the notification is valid. Can be an integer between 0 and 300. Default is 300. Delivery is attempted until the TTL elapses, even if the device is offline. 0 means that the notification delivery is attempted immediately, only once, and is not stored for future delivery. + + :returns: The created NotificationInstance + """ + + data = values.of( + { + "Ttl": ttl, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NotificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + challenge_sid=self._solution["challenge_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/factor.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/factor.py new file mode 100644 index 0000000..8900fcb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/factor.py @@ -0,0 +1,704 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class FactorInstance(InstanceResource): + + class FactorStatuses(object): + UNVERIFIED = "unverified" + VERIFIED = "verified" + + class FactorTypes(object): + PUSH = "push" + TOTP = "totp" + + class TotpAlgorithms(object): + SHA1 = "sha1" + SHA256 = "sha256" + SHA512 = "sha512" + + """ + :ivar sid: A 34 character string that uniquely identifies this Factor. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar entity_sid: The unique SID identifier of the Entity. + :ivar identity: Customer unique identity for the Entity owner of the Factor. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :ivar date_created: The date that this Factor was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Factor was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar friendly_name: A human readable description of this resource, up to 64 characters. For a push factor, this can be the device's name. + :ivar status: + :ivar factor_type: + :ivar config: An object that contains configurations specific to a `factor_type`. + :ivar metadata: Custom metadata associated with the factor. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\"os\": \"Android\"}`. Can be up to 1024 characters in length. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + identity: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.entity_sid: Optional[str] = payload.get("entity_sid") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["FactorInstance.FactorStatuses"] = payload.get("status") + self.factor_type: Optional["FactorInstance.FactorTypes"] = payload.get( + "factor_type" + ) + self.config: Optional[Dict[str, object]] = payload.get("config") + self.metadata: Optional[Dict[str, object]] = payload.get("metadata") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "identity": identity, + "sid": sid or self.sid, + } + self._context: Optional[FactorContext] = None + + @property + def _proxy(self) -> "FactorContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: FactorContext for this FactorInstance + """ + if self._context is None: + self._context = FactorContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the FactorInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FactorInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "FactorInstance": + """ + Fetch the FactorInstance + + + :returns: The fetched FactorInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "FactorInstance": + """ + Asynchronous coroutine to fetch the FactorInstance + + + :returns: The fetched FactorInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + auth_payload: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["FactorInstance.TotpAlgorithms", object] = values.unset, + config_notification_platform: Union[str, object] = values.unset, + ) -> "FactorInstance": + """ + Update the FactorInstance + + :param auth_payload: The optional payload needed to verify the Factor for the first time. E.g. for a TOTP, the numeric code. + :param friendly_name: The new friendly name of this Factor. It can be up to 64 characters. + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Required when `factor_type` is `push`. If specified, this value must be between 32 and 255 characters long. + :param config_sdk_version: The Verify Push SDK version used to configure the factor + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive + :param config_alg: + :param config_notification_platform: The transport technology used to generate the Notification Token. Can be `apn`, `fcm` or `none`. Required when `factor_type` is `push`. + + :returns: The updated FactorInstance + """ + return self._proxy.update( + auth_payload=auth_payload, + friendly_name=friendly_name, + config_notification_token=config_notification_token, + config_sdk_version=config_sdk_version, + config_time_step=config_time_step, + config_skew=config_skew, + config_code_length=config_code_length, + config_alg=config_alg, + config_notification_platform=config_notification_platform, + ) + + async def update_async( + self, + auth_payload: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["FactorInstance.TotpAlgorithms", object] = values.unset, + config_notification_platform: Union[str, object] = values.unset, + ) -> "FactorInstance": + """ + Asynchronous coroutine to update the FactorInstance + + :param auth_payload: The optional payload needed to verify the Factor for the first time. E.g. for a TOTP, the numeric code. + :param friendly_name: The new friendly name of this Factor. It can be up to 64 characters. + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Required when `factor_type` is `push`. If specified, this value must be between 32 and 255 characters long. + :param config_sdk_version: The Verify Push SDK version used to configure the factor + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive + :param config_alg: + :param config_notification_platform: The transport technology used to generate the Notification Token. Can be `apn`, `fcm` or `none`. Required when `factor_type` is `push`. + + :returns: The updated FactorInstance + """ + return await self._proxy.update_async( + auth_payload=auth_payload, + friendly_name=friendly_name, + config_notification_token=config_notification_token, + config_sdk_version=config_sdk_version, + config_time_step=config_time_step, + config_skew=config_skew, + config_code_length=config_code_length, + config_alg=config_alg, + config_notification_platform=config_notification_platform, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FactorContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, identity: str, sid: str): + """ + Initialize the FactorContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Factor. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :param sid: A 34 character string that uniquely identifies this Factor. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Entities/{identity}/Factors/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the FactorInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the FactorInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> FactorInstance: + """ + Fetch the FactorInstance + + + :returns: The fetched FactorInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return FactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> FactorInstance: + """ + Asynchronous coroutine to fetch the FactorInstance + + + :returns: The fetched FactorInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return FactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + def update( + self, + auth_payload: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["FactorInstance.TotpAlgorithms", object] = values.unset, + config_notification_platform: Union[str, object] = values.unset, + ) -> FactorInstance: + """ + Update the FactorInstance + + :param auth_payload: The optional payload needed to verify the Factor for the first time. E.g. for a TOTP, the numeric code. + :param friendly_name: The new friendly name of this Factor. It can be up to 64 characters. + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Required when `factor_type` is `push`. If specified, this value must be between 32 and 255 characters long. + :param config_sdk_version: The Verify Push SDK version used to configure the factor + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive + :param config_alg: + :param config_notification_platform: The transport technology used to generate the Notification Token. Can be `apn`, `fcm` or `none`. Required when `factor_type` is `push`. + + :returns: The updated FactorInstance + """ + data = values.of( + { + "AuthPayload": auth_payload, + "FriendlyName": friendly_name, + "Config.NotificationToken": config_notification_token, + "Config.SdkVersion": config_sdk_version, + "Config.TimeStep": config_time_step, + "Config.Skew": config_skew, + "Config.CodeLength": config_code_length, + "Config.Alg": config_alg, + "Config.NotificationPlatform": config_notification_platform, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return FactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + auth_payload: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["FactorInstance.TotpAlgorithms", object] = values.unset, + config_notification_platform: Union[str, object] = values.unset, + ) -> FactorInstance: + """ + Asynchronous coroutine to update the FactorInstance + + :param auth_payload: The optional payload needed to verify the Factor for the first time. E.g. for a TOTP, the numeric code. + :param friendly_name: The new friendly name of this Factor. It can be up to 64 characters. + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Required when `factor_type` is `push`. If specified, this value must be between 32 and 255 characters long. + :param config_sdk_version: The Verify Push SDK version used to configure the factor + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive + :param config_alg: + :param config_notification_platform: The transport technology used to generate the Notification Token. Can be `apn`, `fcm` or `none`. Required when `factor_type` is `push`. + + :returns: The updated FactorInstance + """ + data = values.of( + { + "AuthPayload": auth_payload, + "FriendlyName": friendly_name, + "Config.NotificationToken": config_notification_token, + "Config.SdkVersion": config_sdk_version, + "Config.TimeStep": config_time_step, + "Config.Skew": config_skew, + "Config.CodeLength": config_code_length, + "Config.Alg": config_alg, + "Config.NotificationPlatform": config_notification_platform, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return FactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class FactorPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> FactorInstance: + """ + Build an instance of FactorInstance + + :param payload: Payload response from the API + """ + return FactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class FactorList(ListResource): + + def __init__(self, version: Version, service_sid: str, identity: str): + """ + Initialize the FactorList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Factors. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Entities/{identity}/Factors".format( + **self._solution + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[FactorInstance]: + """ + Streams FactorInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[FactorInstance]: + """ + Asynchronously streams FactorInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FactorInstance]: + """ + Lists FactorInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[FactorInstance]: + """ + Asynchronously lists FactorInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FactorPage: + """ + Retrieve a single page of FactorInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FactorInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return FactorPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> FactorPage: + """ + Asynchronously retrieve a single page of FactorInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of FactorInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return FactorPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> FactorPage: + """ + Retrieve a specific page of FactorInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FactorInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return FactorPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> FactorPage: + """ + Asynchronously retrieve a specific page of FactorInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of FactorInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return FactorPage(self._version, response, self._solution) + + def get(self, sid: str) -> FactorContext: + """ + Constructs a FactorContext + + :param sid: A 34 character string that uniquely identifies this Factor. + """ + return FactorContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=sid, + ) + + def __call__(self, sid: str) -> FactorContext: + """ + Constructs a FactorContext + + :param sid: A 34 character string that uniquely identifies this Factor. + """ + return FactorContext( + self._version, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/new_factor.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/new_factor.py new file mode 100644 index 0000000..7b260a4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/entity/new_factor.py @@ -0,0 +1,276 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class NewFactorInstance(InstanceResource): + + class FactorStatuses(object): + UNVERIFIED = "unverified" + VERIFIED = "verified" + + class FactorTypes(object): + PUSH = "push" + TOTP = "totp" + + class NotificationPlatforms(object): + APN = "apn" + FCM = "fcm" + NONE = "none" + + class TotpAlgorithms(object): + SHA1 = "sha1" + SHA256 = "sha256" + SHA512 = "sha512" + + """ + :ivar sid: A 34 character string that uniquely identifies this Factor. + :ivar account_sid: The unique SID identifier of the Account. + :ivar service_sid: The unique SID identifier of the Service. + :ivar entity_sid: The unique SID identifier of the Entity. + :ivar identity: Customer unique identity for the Entity owner of the Factor. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + :ivar binding: Contains the `factor_type` specific secret and metadata. For push, this is `binding.public_key` and `binding.alg`. For totp, this is `binding.secret` and `binding.uri`. The `binding.uri` property is generated following the [google authenticator key URI format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format), and `Factor.friendly_name` is used for the “accountname” value and `Service.friendly_name` or `Service.totp.issuer` is used for the `issuer` value. The Binding property is ONLY returned upon Factor creation. + :ivar date_created: The date that this Factor was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Factor was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar friendly_name: The friendly name of this Factor. This can be any string up to 64 characters, meant for humans to distinguish between Factors. For `factor_type` `push`, this could be a device name. For `factor_type` `totp`, this value is used as the “account name” in constructing the `binding.uri` property. At the same time, we recommend avoiding providing PII. + :ivar status: + :ivar factor_type: + :ivar config: An object that contains configurations specific to a `factor_type`. + :ivar metadata: Custom metadata associated with the factor. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\"os\": \"Android\"}`. Can be up to 1024 characters in length. + :ivar url: The URL of this resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], service_sid: str, identity: str + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.entity_sid: Optional[str] = payload.get("entity_sid") + self.identity: Optional[str] = payload.get("identity") + self.binding: Optional[Dict[str, object]] = payload.get("binding") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.status: Optional["NewFactorInstance.FactorStatuses"] = payload.get( + "status" + ) + self.factor_type: Optional["NewFactorInstance.FactorTypes"] = payload.get( + "factor_type" + ) + self.config: Optional[Dict[str, object]] = payload.get("config") + self.metadata: Optional[Dict[str, object]] = payload.get("metadata") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "identity": identity, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class NewFactorList(ListResource): + + def __init__(self, version: Version, service_sid: str, identity: str): + """ + Initialize the NewFactorList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param identity: Customer unique identity for the Entity owner of the Factor. This identifier should be immutable, not PII, length between 8 and 64 characters, and generated by your external system, such as your user's UUID, GUID, or SID. It can only contain dash (-) separated alphanumeric characters. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "identity": identity, + } + self._uri = "/Services/{service_sid}/Entities/{identity}/Factors".format( + **self._solution + ) + + def create( + self, + friendly_name: str, + factor_type: "NewFactorInstance.FactorTypes", + binding_alg: Union[str, object] = values.unset, + binding_public_key: Union[str, object] = values.unset, + config_app_id: Union[str, object] = values.unset, + config_notification_platform: Union[ + "NewFactorInstance.NotificationPlatforms", object + ] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + binding_secret: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["NewFactorInstance.TotpAlgorithms", object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> NewFactorInstance: + """ + Create the NewFactorInstance + + :param friendly_name: The friendly name of this Factor. This can be any string up to 64 characters, meant for humans to distinguish between Factors. For `factor_type` `push`, this could be a device name. For `factor_type` `totp`, this value is used as the “account name” in constructing the `binding.uri` property. At the same time, we recommend avoiding providing PII. + :param factor_type: + :param binding_alg: The algorithm used when `factor_type` is `push`. Algorithm supported: `ES256` + :param binding_public_key: The Ecdsa public key in PKIX, ASN.1 DER format encoded in Base64. Required when `factor_type` is `push` + :param config_app_id: The ID that uniquely identifies your app in the Google or Apple store, such as `com.example.myapp`. It can be up to 100 characters long. Required when `factor_type` is `push`. + :param config_notification_platform: + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Must be between 32 and 255 characters long. Required when `factor_type` is `push`. + :param config_sdk_version: The Verify Push SDK version used to configure the factor Required when `factor_type` is `push` + :param binding_secret: The shared secret for TOTP factors encoded in Base32. This can be provided when creating the Factor, otherwise it will be generated. Used when `factor_type` is `totp` + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. The default value is defined at the service level in the property `totp.time_step`. Defaults to 30 seconds if not configured. Used when `factor_type` is `totp` + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. The default value is defined at the service level in the property `totp.skew`. If not configured defaults to 1. Used when `factor_type` is `totp` + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. The default value is defined at the service level in the property `totp.code_length`. If not configured defaults to 6. Used when `factor_type` is `totp` + :param config_alg: + :param metadata: Custom metadata associated with the factor. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The created NewFactorInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "FactorType": factor_type, + "Binding.Alg": binding_alg, + "Binding.PublicKey": binding_public_key, + "Config.AppId": config_app_id, + "Config.NotificationPlatform": config_notification_platform, + "Config.NotificationToken": config_notification_token, + "Config.SdkVersion": config_sdk_version, + "Binding.Secret": binding_secret, + "Config.TimeStep": config_time_step, + "Config.Skew": config_skew, + "Config.CodeLength": config_code_length, + "Config.Alg": config_alg, + "Metadata": serialize.object(metadata), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return NewFactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + async def create_async( + self, + friendly_name: str, + factor_type: "NewFactorInstance.FactorTypes", + binding_alg: Union[str, object] = values.unset, + binding_public_key: Union[str, object] = values.unset, + config_app_id: Union[str, object] = values.unset, + config_notification_platform: Union[ + "NewFactorInstance.NotificationPlatforms", object + ] = values.unset, + config_notification_token: Union[str, object] = values.unset, + config_sdk_version: Union[str, object] = values.unset, + binding_secret: Union[str, object] = values.unset, + config_time_step: Union[int, object] = values.unset, + config_skew: Union[int, object] = values.unset, + config_code_length: Union[int, object] = values.unset, + config_alg: Union["NewFactorInstance.TotpAlgorithms", object] = values.unset, + metadata: Union[object, object] = values.unset, + ) -> NewFactorInstance: + """ + Asynchronously create the NewFactorInstance + + :param friendly_name: The friendly name of this Factor. This can be any string up to 64 characters, meant for humans to distinguish between Factors. For `factor_type` `push`, this could be a device name. For `factor_type` `totp`, this value is used as the “account name” in constructing the `binding.uri` property. At the same time, we recommend avoiding providing PII. + :param factor_type: + :param binding_alg: The algorithm used when `factor_type` is `push`. Algorithm supported: `ES256` + :param binding_public_key: The Ecdsa public key in PKIX, ASN.1 DER format encoded in Base64. Required when `factor_type` is `push` + :param config_app_id: The ID that uniquely identifies your app in the Google or Apple store, such as `com.example.myapp`. It can be up to 100 characters long. Required when `factor_type` is `push`. + :param config_notification_platform: + :param config_notification_token: For APN, the device token. For FCM, the registration token. It is used to send the push notifications. Must be between 32 and 255 characters long. Required when `factor_type` is `push`. + :param config_sdk_version: The Verify Push SDK version used to configure the factor Required when `factor_type` is `push` + :param binding_secret: The shared secret for TOTP factors encoded in Base32. This can be provided when creating the Factor, otherwise it will be generated. Used when `factor_type` is `totp` + :param config_time_step: Defines how often, in seconds, are TOTP codes generated. i.e, a new TOTP code is generated every time_step seconds. Must be between 20 and 60 seconds, inclusive. The default value is defined at the service level in the property `totp.time_step`. Defaults to 30 seconds if not configured. Used when `factor_type` is `totp` + :param config_skew: The number of time-steps, past and future, that are valid for validation of TOTP codes. Must be between 0 and 2, inclusive. The default value is defined at the service level in the property `totp.skew`. If not configured defaults to 1. Used when `factor_type` is `totp` + :param config_code_length: Number of digits for generated TOTP codes. Must be between 3 and 8, inclusive. The default value is defined at the service level in the property `totp.code_length`. If not configured defaults to 6. Used when `factor_type` is `totp` + :param config_alg: + :param metadata: Custom metadata associated with the factor. This is added by the Device/SDK directly to allow for the inclusion of device information. It must be a stringified JSON with only strings values eg. `{\\\"os\\\": \\\"Android\\\"}`. Can be up to 1024 characters in length. + + :returns: The created NewFactorInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "FactorType": factor_type, + "Binding.Alg": binding_alg, + "Binding.PublicKey": binding_public_key, + "Config.AppId": config_app_id, + "Config.NotificationPlatform": config_notification_platform, + "Config.NotificationToken": config_notification_token, + "Config.SdkVersion": config_sdk_version, + "Binding.Secret": binding_secret, + "Config.TimeStep": config_time_step, + "Config.Skew": config_skew, + "Config.CodeLength": config_code_length, + "Config.Alg": config_alg, + "Metadata": serialize.object(metadata), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return NewFactorInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + identity=self._solution["identity"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/messaging_configuration.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/messaging_configuration.py new file mode 100644 index 0000000..f412aba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/messaging_configuration.py @@ -0,0 +1,610 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class MessagingConfigurationInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) that the resource is associated with. + :ivar country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + :ivar messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + country: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.country: Optional[str] = payload.get("country") + self.messaging_service_sid: Optional[str] = payload.get("messaging_service_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "country": country or self.country, + } + self._context: Optional[MessagingConfigurationContext] = None + + @property + def _proxy(self) -> "MessagingConfigurationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: MessagingConfigurationContext for this MessagingConfigurationInstance + """ + if self._context is None: + self._context = MessagingConfigurationContext( + self._version, + service_sid=self._solution["service_sid"], + country=self._solution["country"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the MessagingConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessagingConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "MessagingConfigurationInstance": + """ + Fetch the MessagingConfigurationInstance + + + :returns: The fetched MessagingConfigurationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "MessagingConfigurationInstance": + """ + Asynchronous coroutine to fetch the MessagingConfigurationInstance + + + :returns: The fetched MessagingConfigurationInstance + """ + return await self._proxy.fetch_async() + + def update(self, messaging_service_sid: str) -> "MessagingConfigurationInstance": + """ + Update the MessagingConfigurationInstance + + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The updated MessagingConfigurationInstance + """ + return self._proxy.update( + messaging_service_sid=messaging_service_sid, + ) + + async def update_async( + self, messaging_service_sid: str + ) -> "MessagingConfigurationInstance": + """ + Asynchronous coroutine to update the MessagingConfigurationInstance + + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The updated MessagingConfigurationInstance + """ + return await self._proxy.update_async( + messaging_service_sid=messaging_service_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagingConfigurationContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, country: str): + """ + Initialize the MessagingConfigurationContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) that the resource is associated with. + :param country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "country": country, + } + self._uri = "/Services/{service_sid}/MessagingConfigurations/{country}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the MessagingConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the MessagingConfigurationInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> MessagingConfigurationInstance: + """ + Fetch the MessagingConfigurationInstance + + + :returns: The fetched MessagingConfigurationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return MessagingConfigurationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + country=self._solution["country"], + ) + + async def fetch_async(self) -> MessagingConfigurationInstance: + """ + Asynchronous coroutine to fetch the MessagingConfigurationInstance + + + :returns: The fetched MessagingConfigurationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return MessagingConfigurationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + country=self._solution["country"], + ) + + def update(self, messaging_service_sid: str) -> MessagingConfigurationInstance: + """ + Update the MessagingConfigurationInstance + + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The updated MessagingConfigurationInstance + """ + data = values.of( + { + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return MessagingConfigurationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + country=self._solution["country"], + ) + + async def update_async( + self, messaging_service_sid: str + ) -> MessagingConfigurationInstance: + """ + Asynchronous coroutine to update the MessagingConfigurationInstance + + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The updated MessagingConfigurationInstance + """ + data = values.of( + { + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessagingConfigurationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + country=self._solution["country"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class MessagingConfigurationPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> MessagingConfigurationInstance: + """ + Build an instance of MessagingConfigurationInstance + + :param payload: Payload response from the API + """ + return MessagingConfigurationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class MessagingConfigurationList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the MessagingConfigurationList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) that the resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/MessagingConfigurations".format( + **self._solution + ) + + def create( + self, country: str, messaging_service_sid: str + ) -> MessagingConfigurationInstance: + """ + Create the MessagingConfigurationInstance + + :param country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The created MessagingConfigurationInstance + """ + + data = values.of( + { + "Country": country, + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return MessagingConfigurationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, country: str, messaging_service_sid: str + ) -> MessagingConfigurationInstance: + """ + Asynchronously create the MessagingConfigurationInstance + + :param country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + :param messaging_service_sid: The SID of the [Messaging Service](https://www.twilio.com/docs/messaging/api/service-resource) to be used to send SMS to the country of this configuration. + + :returns: The created MessagingConfigurationInstance + """ + + data = values.of( + { + "Country": country, + "MessagingServiceSid": messaging_service_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return MessagingConfigurationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[MessagingConfigurationInstance]: + """ + Streams MessagingConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[MessagingConfigurationInstance]: + """ + Asynchronously streams MessagingConfigurationInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessagingConfigurationInstance]: + """ + Lists MessagingConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[MessagingConfigurationInstance]: + """ + Asynchronously lists MessagingConfigurationInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagingConfigurationPage: + """ + Retrieve a single page of MessagingConfigurationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessagingConfigurationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return MessagingConfigurationPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> MessagingConfigurationPage: + """ + Asynchronously retrieve a single page of MessagingConfigurationInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of MessagingConfigurationInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return MessagingConfigurationPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> MessagingConfigurationPage: + """ + Retrieve a specific page of MessagingConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessagingConfigurationInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return MessagingConfigurationPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> MessagingConfigurationPage: + """ + Asynchronously retrieve a specific page of MessagingConfigurationInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of MessagingConfigurationInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return MessagingConfigurationPage(self._version, response, self._solution) + + def get(self, country: str) -> MessagingConfigurationContext: + """ + Constructs a MessagingConfigurationContext + + :param country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + """ + return MessagingConfigurationContext( + self._version, service_sid=self._solution["service_sid"], country=country + ) + + def __call__(self, country: str) -> MessagingConfigurationContext: + """ + Constructs a MessagingConfigurationContext + + :param country: The [ISO-3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code of the country this configuration will be applied to. If this is a global configuration, Country will take the value `all`. + """ + return MessagingConfigurationContext( + self._version, service_sid=self._solution["service_sid"], country=country + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__init__.py new file mode 100644 index 0000000..2d4a18b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__init__.py @@ -0,0 +1,637 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.verify.v2.service.rate_limit.bucket import BucketList + + +class RateLimitInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this Rate Limit. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Rate Limit resource. + :ivar unique_name: Provides a unique and addressable name to be assigned to this Rate Limit, assigned by the developer, to be optionally used in addition to SID. **This value should not contain PII.** + :ivar description: Description of this Rate Limit + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URL of this resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.description: Optional[str] = payload.get("description") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[RateLimitContext] = None + + @property + def _proxy(self) -> "RateLimitContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RateLimitContext for this RateLimitInstance + """ + if self._context is None: + self._context = RateLimitContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RateLimitInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RateLimitInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RateLimitInstance": + """ + Fetch the RateLimitInstance + + + :returns: The fetched RateLimitInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RateLimitInstance": + """ + Asynchronous coroutine to fetch the RateLimitInstance + + + :returns: The fetched RateLimitInstance + """ + return await self._proxy.fetch_async() + + def update( + self, description: Union[str, object] = values.unset + ) -> "RateLimitInstance": + """ + Update the RateLimitInstance + + :param description: Description of this Rate Limit + + :returns: The updated RateLimitInstance + """ + return self._proxy.update( + description=description, + ) + + async def update_async( + self, description: Union[str, object] = values.unset + ) -> "RateLimitInstance": + """ + Asynchronous coroutine to update the RateLimitInstance + + :param description: Description of this Rate Limit + + :returns: The updated RateLimitInstance + """ + return await self._proxy.update_async( + description=description, + ) + + @property + def buckets(self) -> BucketList: + """ + Access the buckets + """ + return self._proxy.buckets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RateLimitContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the RateLimitContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :param sid: The Twilio-provided string that uniquely identifies the Rate Limit resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/RateLimits/{sid}".format(**self._solution) + + self._buckets: Optional[BucketList] = None + + def delete(self) -> bool: + """ + Deletes the RateLimitInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RateLimitInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RateLimitInstance: + """ + Fetch the RateLimitInstance + + + :returns: The fetched RateLimitInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RateLimitInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RateLimitInstance: + """ + Asynchronous coroutine to fetch the RateLimitInstance + + + :returns: The fetched RateLimitInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RateLimitInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, description: Union[str, object] = values.unset + ) -> RateLimitInstance: + """ + Update the RateLimitInstance + + :param description: Description of this Rate Limit + + :returns: The updated RateLimitInstance + """ + data = values.of( + { + "Description": description, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RateLimitInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, description: Union[str, object] = values.unset + ) -> RateLimitInstance: + """ + Asynchronous coroutine to update the RateLimitInstance + + :param description: Description of this Rate Limit + + :returns: The updated RateLimitInstance + """ + data = values.of( + { + "Description": description, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RateLimitInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + @property + def buckets(self) -> BucketList: + """ + Access the buckets + """ + if self._buckets is None: + self._buckets = BucketList( + self._version, + self._solution["service_sid"], + self._solution["sid"], + ) + return self._buckets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RateLimitPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RateLimitInstance: + """ + Build an instance of RateLimitInstance + + :param payload: Payload response from the API + """ + return RateLimitInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RateLimitList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the RateLimitList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/RateLimits".format(**self._solution) + + def create( + self, unique_name: str, description: Union[str, object] = values.unset + ) -> RateLimitInstance: + """ + Create the RateLimitInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Rate Limit, assigned by the developer, to be optionally used in addition to SID. **This value should not contain PII.** + :param description: Description of this Rate Limit + + :returns: The created RateLimitInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Description": description, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RateLimitInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, unique_name: str, description: Union[str, object] = values.unset + ) -> RateLimitInstance: + """ + Asynchronously create the RateLimitInstance + + :param unique_name: Provides a unique and addressable name to be assigned to this Rate Limit, assigned by the developer, to be optionally used in addition to SID. **This value should not contain PII.** + :param description: Description of this Rate Limit + + :returns: The created RateLimitInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "Description": description, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RateLimitInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RateLimitInstance]: + """ + Streams RateLimitInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RateLimitInstance]: + """ + Asynchronously streams RateLimitInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RateLimitInstance]: + """ + Lists RateLimitInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RateLimitInstance]: + """ + Asynchronously lists RateLimitInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RateLimitPage: + """ + Retrieve a single page of RateLimitInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RateLimitInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RateLimitPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RateLimitPage: + """ + Asynchronously retrieve a single page of RateLimitInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RateLimitInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RateLimitPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RateLimitPage: + """ + Retrieve a specific page of RateLimitInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RateLimitInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RateLimitPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RateLimitPage: + """ + Asynchronously retrieve a specific page of RateLimitInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RateLimitInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RateLimitPage(self._version, response, self._solution) + + def get(self, sid: str) -> RateLimitContext: + """ + Constructs a RateLimitContext + + :param sid: The Twilio-provided string that uniquely identifies the Rate Limit resource to fetch. + """ + return RateLimitContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RateLimitContext: + """ + Constructs a RateLimitContext + + :param sid: The Twilio-provided string that uniquely identifies the Rate Limit resource to fetch. + """ + return RateLimitContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..bfe8fbe Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/bucket.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/bucket.cpython-310.pyc new file mode 100644 index 0000000..b3892b6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/__pycache__/bucket.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/bucket.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/bucket.py new file mode 100644 index 0000000..ca4ffaf --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/rate_limit/bucket.py @@ -0,0 +1,662 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class BucketInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies this Bucket. + :ivar rate_limit_sid: The Twilio-provided string that uniquely identifies the Rate Limit resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Rate Limit resource. + :ivar max: Maximum number of requests permitted in during the interval. + :ivar interval: Number of seconds that the rate limit will be enforced over. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The URL of this resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + rate_limit_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.rate_limit_sid: Optional[str] = payload.get("rate_limit_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.max: Optional[int] = deserialize.integer(payload.get("max")) + self.interval: Optional[int] = deserialize.integer(payload.get("interval")) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "rate_limit_sid": rate_limit_sid, + "sid": sid or self.sid, + } + self._context: Optional[BucketContext] = None + + @property + def _proxy(self) -> "BucketContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: BucketContext for this BucketInstance + """ + if self._context is None: + self._context = BucketContext( + self._version, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the BucketInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BucketInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "BucketInstance": + """ + Fetch the BucketInstance + + + :returns: The fetched BucketInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "BucketInstance": + """ + Asynchronous coroutine to fetch the BucketInstance + + + :returns: The fetched BucketInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + max: Union[int, object] = values.unset, + interval: Union[int, object] = values.unset, + ) -> "BucketInstance": + """ + Update the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The updated BucketInstance + """ + return self._proxy.update( + max=max, + interval=interval, + ) + + async def update_async( + self, + max: Union[int, object] = values.unset, + interval: Union[int, object] = values.unset, + ) -> "BucketInstance": + """ + Asynchronous coroutine to update the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The updated BucketInstance + """ + return await self._proxy.update_async( + max=max, + interval=interval, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BucketContext(InstanceContext): + + def __init__( + self, version: Version, service_sid: str, rate_limit_sid: str, sid: str + ): + """ + Initialize the BucketContext + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :param rate_limit_sid: The Twilio-provided string that uniquely identifies the Rate Limit resource. + :param sid: A 34 character string that uniquely identifies this Bucket. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "rate_limit_sid": rate_limit_sid, + "sid": sid, + } + self._uri = ( + "/Services/{service_sid}/RateLimits/{rate_limit_sid}/Buckets/{sid}".format( + **self._solution + ) + ) + + def delete(self) -> bool: + """ + Deletes the BucketInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the BucketInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> BucketInstance: + """ + Fetch the BucketInstance + + + :returns: The fetched BucketInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> BucketInstance: + """ + Asynchronous coroutine to fetch the BucketInstance + + + :returns: The fetched BucketInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + max: Union[int, object] = values.unset, + interval: Union[int, object] = values.unset, + ) -> BucketInstance: + """ + Update the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The updated BucketInstance + """ + data = values.of( + { + "Max": max, + "Interval": interval, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + max: Union[int, object] = values.unset, + interval: Union[int, object] = values.unset, + ) -> BucketInstance: + """ + Asynchronous coroutine to update the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The updated BucketInstance + """ + data = values.of( + { + "Max": max, + "Interval": interval, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class BucketPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> BucketInstance: + """ + Build an instance of BucketInstance + + :param payload: Payload response from the API + """ + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class BucketList(ListResource): + + def __init__(self, version: Version, service_sid: str, rate_limit_sid: str): + """ + Initialize the BucketList + + :param version: Version that contains the resource + :param service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :param rate_limit_sid: The Twilio-provided string that uniquely identifies the Rate Limit resource. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "rate_limit_sid": rate_limit_sid, + } + self._uri = ( + "/Services/{service_sid}/RateLimits/{rate_limit_sid}/Buckets".format( + **self._solution + ) + ) + + def create(self, max: int, interval: int) -> BucketInstance: + """ + Create the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The created BucketInstance + """ + + data = values.of( + { + "Max": max, + "Interval": interval, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + ) + + async def create_async(self, max: int, interval: int) -> BucketInstance: + """ + Asynchronously create the BucketInstance + + :param max: Maximum number of requests permitted in during the interval. + :param interval: Number of seconds that the rate limit will be enforced over. + + :returns: The created BucketInstance + """ + + data = values.of( + { + "Max": max, + "Interval": interval, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BucketInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[BucketInstance]: + """ + Streams BucketInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[BucketInstance]: + """ + Asynchronously streams BucketInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BucketInstance]: + """ + Lists BucketInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[BucketInstance]: + """ + Asynchronously lists BucketInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BucketPage: + """ + Retrieve a single page of BucketInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BucketInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return BucketPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> BucketPage: + """ + Asynchronously retrieve a single page of BucketInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of BucketInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return BucketPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> BucketPage: + """ + Retrieve a specific page of BucketInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BucketInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return BucketPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> BucketPage: + """ + Asynchronously retrieve a specific page of BucketInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of BucketInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return BucketPage(self._version, response, self._solution) + + def get(self, sid: str) -> BucketContext: + """ + Constructs a BucketContext + + :param sid: A 34 character string that uniquely identifies this Bucket. + """ + return BucketContext( + self._version, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> BucketContext: + """ + Constructs a BucketContext + + :param sid: A 34 character string that uniquely identifies this Bucket. + """ + return BucketContext( + self._version, + service_sid=self._solution["service_sid"], + rate_limit_sid=self._solution["rate_limit_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification.py new file mode 100644 index 0000000..1c42043 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification.py @@ -0,0 +1,489 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class VerificationInstance(InstanceResource): + + class Channel(object): + SMS = "sms" + CALL = "call" + EMAIL = "email" + WHATSAPP = "whatsapp" + SNA = "sna" + + class RiskCheck(object): + ENABLE = "enable" + DISABLE = "disable" + + class Status(object): + CANCELED = "canceled" + APPROVED = "approved" + + """ + :ivar sid: The unique string that we created to identify the Verification resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Verification resource. + :ivar to: The phone number or [email](https://www.twilio.com/docs/verify/email) being verified. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :ivar channel: + :ivar status: The status of the verification. One of: `pending`, `approved`, or `canceled` + :ivar valid: Use \"status\" instead. Legacy property indicating whether the verification was successful. + :ivar lookup: Information about the phone number being verified. + :ivar amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :ivar payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :ivar send_code_attempts: An array of verification attempt objects containing the channel attempted and the channel-specific transaction SID. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar sna: The set of fields used for a silent network auth (`sna`) verification. Contains a single field with the URL to be invoked to verify the phone number. + :ivar url: The absolute URL of the Verification resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.to: Optional[str] = payload.get("to") + self.channel: Optional["VerificationInstance.Channel"] = payload.get("channel") + self.status: Optional[str] = payload.get("status") + self.valid: Optional[bool] = payload.get("valid") + self.lookup: Optional[Dict[str, object]] = payload.get("lookup") + self.amount: Optional[str] = payload.get("amount") + self.payee: Optional[str] = payload.get("payee") + self.send_code_attempts: Optional[List[Dict[str, object]]] = payload.get( + "send_code_attempts" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sna: Optional[Dict[str, object]] = payload.get("sna") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[VerificationContext] = None + + @property + def _proxy(self) -> "VerificationContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: VerificationContext for this VerificationInstance + """ + if self._context is None: + self._context = VerificationContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "VerificationInstance": + """ + Fetch the VerificationInstance + + + :returns: The fetched VerificationInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "VerificationInstance": + """ + Asynchronous coroutine to fetch the VerificationInstance + + + :returns: The fetched VerificationInstance + """ + return await self._proxy.fetch_async() + + def update(self, status: "VerificationInstance.Status") -> "VerificationInstance": + """ + Update the VerificationInstance + + :param status: + + :returns: The updated VerificationInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: "VerificationInstance.Status" + ) -> "VerificationInstance": + """ + Asynchronous coroutine to update the VerificationInstance + + :param status: + + :returns: The updated VerificationInstance + """ + return await self._proxy.update_async( + status=status, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VerificationContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the VerificationContext + + :param version: Version that contains the resource + :param service_sid: The SID of the verification [Service](https://www.twilio.com/docs/verify/api/service) to update the resource from. + :param sid: The Twilio-provided string that uniquely identifies the Verification resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Verifications/{sid}".format( + **self._solution + ) + + def fetch(self) -> VerificationInstance: + """ + Fetch the VerificationInstance + + + :returns: The fetched VerificationInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return VerificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> VerificationInstance: + """ + Asynchronous coroutine to fetch the VerificationInstance + + + :returns: The fetched VerificationInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return VerificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update(self, status: "VerificationInstance.Status") -> VerificationInstance: + """ + Update the VerificationInstance + + :param status: + + :returns: The updated VerificationInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: "VerificationInstance.Status" + ) -> VerificationInstance: + """ + Asynchronous coroutine to update the VerificationInstance + + :param status: + + :returns: The updated VerificationInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VerificationList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the VerificationList + + :param version: Version that contains the resource + :param service_sid: The SID of the verification [Service](https://www.twilio.com/docs/verify/api/service) to create the resource under. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Verifications".format(**self._solution) + + def create( + self, + to: str, + channel: str, + custom_friendly_name: Union[str, object] = values.unset, + custom_message: Union[str, object] = values.unset, + send_digits: Union[str, object] = values.unset, + locale: Union[str, object] = values.unset, + custom_code: Union[str, object] = values.unset, + amount: Union[str, object] = values.unset, + payee: Union[str, object] = values.unset, + rate_limits: Union[object, object] = values.unset, + channel_configuration: Union[object, object] = values.unset, + app_hash: Union[str, object] = values.unset, + template_sid: Union[str, object] = values.unset, + template_custom_substitutions: Union[str, object] = values.unset, + device_ip: Union[str, object] = values.unset, + risk_check: Union["VerificationInstance.RiskCheck", object] = values.unset, + tags: Union[str, object] = values.unset, + ) -> VerificationInstance: + """ + Create the VerificationInstance + + :param to: The phone number or [email](https://www.twilio.com/docs/verify/email) to verify. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :param channel: The verification method to use. One of: [`email`](https://www.twilio.com/docs/verify/email), `sms`, `whatsapp`, `call`, `sna` or `auto`. + :param custom_friendly_name: A custom user defined friendly name that overwrites the existing one in the verification message + :param custom_message: The text of a custom message to use for the verification. + :param send_digits: The digits to send after a phone call is answered, for example, to dial an extension. For more information, see the Programmable Voice documentation of [sendDigits](https://www.twilio.com/docs/voice/twiml/number#attributes-sendDigits). + :param locale: Locale will automatically resolve based on phone number country code for SMS, WhatsApp, and call channel verifications. It will fallback to English or the template’s default translation if the selected translation is not available. This parameter will override the automatic locale resolution. [See supported languages and more information here](https://www.twilio.com/docs/verify/supported-languages). + :param custom_code: A pre-generated code to use for verification. The code can be between 4 and 10 characters, inclusive. + :param amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param rate_limits: The custom key-value pairs of Programmable Rate Limits. Keys correspond to `unique_name` fields defined when [creating your Rate Limit](https://www.twilio.com/docs/verify/api/service-rate-limits). Associated value pairs represent values in the request that you are rate limiting on. You may include multiple Rate Limit values in each request. + :param channel_configuration: [`email`](https://www.twilio.com/docs/verify/email) channel configuration in json format. The fields 'from' and 'from_name' are optional but if included the 'from' field must have a valid email address. + :param app_hash: Your [App Hash](https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string) to be appended at the end of your verification SMS body. Applies only to SMS. Example SMS body: `<#> Your AppName verification code is: 1234 He42w354ol9`. + :param template_sid: The message [template](https://www.twilio.com/docs/verify/api/templates). If provided, will override the default template for the Service. SMS and Voice channels only. + :param template_custom_substitutions: A stringified JSON object in which the keys are the template's special variables and the values are the variables substitutions. + :param device_ip: Strongly encouraged if using the auto channel. The IP address of the client's device. If provided, it has to be a valid IPv4 or IPv6 address. + :param risk_check: + :param tags: A string containing a JSON map of key value pairs of tags to be recorded as metadata for the message. The object may contain up to 10 tags. Keys and values can each be up to 128 characters in length. + + :returns: The created VerificationInstance + """ + + data = values.of( + { + "To": to, + "Channel": channel, + "CustomFriendlyName": custom_friendly_name, + "CustomMessage": custom_message, + "SendDigits": send_digits, + "Locale": locale, + "CustomCode": custom_code, + "Amount": amount, + "Payee": payee, + "RateLimits": serialize.object(rate_limits), + "ChannelConfiguration": serialize.object(channel_configuration), + "AppHash": app_hash, + "TemplateSid": template_sid, + "TemplateCustomSubstitutions": template_custom_substitutions, + "DeviceIp": device_ip, + "RiskCheck": risk_check, + "Tags": tags, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + to: str, + channel: str, + custom_friendly_name: Union[str, object] = values.unset, + custom_message: Union[str, object] = values.unset, + send_digits: Union[str, object] = values.unset, + locale: Union[str, object] = values.unset, + custom_code: Union[str, object] = values.unset, + amount: Union[str, object] = values.unset, + payee: Union[str, object] = values.unset, + rate_limits: Union[object, object] = values.unset, + channel_configuration: Union[object, object] = values.unset, + app_hash: Union[str, object] = values.unset, + template_sid: Union[str, object] = values.unset, + template_custom_substitutions: Union[str, object] = values.unset, + device_ip: Union[str, object] = values.unset, + risk_check: Union["VerificationInstance.RiskCheck", object] = values.unset, + tags: Union[str, object] = values.unset, + ) -> VerificationInstance: + """ + Asynchronously create the VerificationInstance + + :param to: The phone number or [email](https://www.twilio.com/docs/verify/email) to verify. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :param channel: The verification method to use. One of: [`email`](https://www.twilio.com/docs/verify/email), `sms`, `whatsapp`, `call`, `sna` or `auto`. + :param custom_friendly_name: A custom user defined friendly name that overwrites the existing one in the verification message + :param custom_message: The text of a custom message to use for the verification. + :param send_digits: The digits to send after a phone call is answered, for example, to dial an extension. For more information, see the Programmable Voice documentation of [sendDigits](https://www.twilio.com/docs/voice/twiml/number#attributes-sendDigits). + :param locale: Locale will automatically resolve based on phone number country code for SMS, WhatsApp, and call channel verifications. It will fallback to English or the template’s default translation if the selected translation is not available. This parameter will override the automatic locale resolution. [See supported languages and more information here](https://www.twilio.com/docs/verify/supported-languages). + :param custom_code: A pre-generated code to use for verification. The code can be between 4 and 10 characters, inclusive. + :param amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param rate_limits: The custom key-value pairs of Programmable Rate Limits. Keys correspond to `unique_name` fields defined when [creating your Rate Limit](https://www.twilio.com/docs/verify/api/service-rate-limits). Associated value pairs represent values in the request that you are rate limiting on. You may include multiple Rate Limit values in each request. + :param channel_configuration: [`email`](https://www.twilio.com/docs/verify/email) channel configuration in json format. The fields 'from' and 'from_name' are optional but if included the 'from' field must have a valid email address. + :param app_hash: Your [App Hash](https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string) to be appended at the end of your verification SMS body. Applies only to SMS. Example SMS body: `<#> Your AppName verification code is: 1234 He42w354ol9`. + :param template_sid: The message [template](https://www.twilio.com/docs/verify/api/templates). If provided, will override the default template for the Service. SMS and Voice channels only. + :param template_custom_substitutions: A stringified JSON object in which the keys are the template's special variables and the values are the variables substitutions. + :param device_ip: Strongly encouraged if using the auto channel. The IP address of the client's device. If provided, it has to be a valid IPv4 or IPv6 address. + :param risk_check: + :param tags: A string containing a JSON map of key value pairs of tags to be recorded as metadata for the message. The object may contain up to 10 tags. Keys and values can each be up to 128 characters in length. + + :returns: The created VerificationInstance + """ + + data = values.of( + { + "To": to, + "Channel": channel, + "CustomFriendlyName": custom_friendly_name, + "CustomMessage": custom_message, + "SendDigits": send_digits, + "Locale": locale, + "CustomCode": custom_code, + "Amount": amount, + "Payee": payee, + "RateLimits": serialize.object(rate_limits), + "ChannelConfiguration": serialize.object(channel_configuration), + "AppHash": app_hash, + "TemplateSid": template_sid, + "TemplateCustomSubstitutions": template_custom_substitutions, + "DeviceIp": device_ip, + "RiskCheck": risk_check, + "Tags": tags, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def get(self, sid: str) -> VerificationContext: + """ + Constructs a VerificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Verification resource to update. + """ + return VerificationContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> VerificationContext: + """ + Constructs a VerificationContext + + :param sid: The Twilio-provided string that uniquely identifies the Verification resource to update. + """ + return VerificationContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification_check.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification_check.py new file mode 100644 index 0000000..9cd9248 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/verification_check.py @@ -0,0 +1,190 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class VerificationCheckInstance(InstanceResource): + + class Channel(object): + SMS = "sms" + CALL = "call" + EMAIL = "email" + WHATSAPP = "whatsapp" + SNA = "sna" + + """ + :ivar sid: The unique string that we created to identify the VerificationCheck resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) the resource is associated with. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the VerificationCheck resource. + :ivar to: The phone number or [email](https://www.twilio.com/docs/verify/email) being verified. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :ivar channel: + :ivar status: The status of the verification. Can be: `pending`, `approved`, or `canceled`. + :ivar valid: Use \"status\" instead. Legacy property indicating whether the verification was successful. + :ivar amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :ivar payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :ivar date_created: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the Verification Check resource was created. + :ivar date_updated: The [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time in GMT when the Verification Check resource was last updated. + :ivar sna_attempts_error_codes: List of error codes as a result of attempting a verification using the `sna` channel. The error codes are chronologically ordered, from the first attempt to the latest attempt. This will be an empty list if no errors occured or `null` if the last channel used wasn't `sna`. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], service_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.to: Optional[str] = payload.get("to") + self.channel: Optional["VerificationCheckInstance.Channel"] = payload.get( + "channel" + ) + self.status: Optional[str] = payload.get("status") + self.valid: Optional[bool] = payload.get("valid") + self.amount: Optional[str] = payload.get("amount") + self.payee: Optional[str] = payload.get("payee") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sna_attempts_error_codes: Optional[List[Dict[str, object]]] = payload.get( + "sna_attempts_error_codes" + ) + + self._solution = { + "service_sid": service_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VerificationCheckList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the VerificationCheckList + + :param version: Version that contains the resource + :param service_sid: The SID of the verification [Service](https://www.twilio.com/docs/verify/api/service) to create the resource under. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/VerificationCheck".format(**self._solution) + + def create( + self, + code: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + amount: Union[str, object] = values.unset, + payee: Union[str, object] = values.unset, + ) -> VerificationCheckInstance: + """ + Create the VerificationCheckInstance + + :param code: The 4-10 character string being verified. + :param to: The phone number or [email](https://www.twilio.com/docs/verify/email) to verify. Either this parameter or the `verification_sid` must be specified. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :param verification_sid: A SID that uniquely identifies the Verification Check. Either this parameter or the `to` phone number/[email](https://www.twilio.com/docs/verify/email) must be specified. + :param amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + + :returns: The created VerificationCheckInstance + """ + + data = values.of( + { + "Code": code, + "To": to, + "VerificationSid": verification_sid, + "Amount": amount, + "Payee": payee, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationCheckInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + code: Union[str, object] = values.unset, + to: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + amount: Union[str, object] = values.unset, + payee: Union[str, object] = values.unset, + ) -> VerificationCheckInstance: + """ + Asynchronously create the VerificationCheckInstance + + :param code: The 4-10 character string being verified. + :param to: The phone number or [email](https://www.twilio.com/docs/verify/email) to verify. Either this parameter or the `verification_sid` must be specified. Phone numbers must be in [E.164 format](https://www.twilio.com/docs/glossary/what-e164). + :param verification_sid: A SID that uniquely identifies the Verification Check. Either this parameter or the `to` phone number/[email](https://www.twilio.com/docs/verify/email) must be specified. + :param amount: The amount of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + :param payee: The payee of the associated PSD2 compliant transaction. Requires the PSD2 Service flag enabled. + + :returns: The created VerificationCheckInstance + """ + + data = values.of( + { + "Code": code, + "To": to, + "VerificationSid": verification_sid, + "Amount": amount, + "Payee": payee, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return VerificationCheckInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/webhook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/webhook.py new file mode 100644 index 0000000..4f87725 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/service/webhook.py @@ -0,0 +1,709 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class WebhookInstance(InstanceResource): + + class Methods(object): + GET = "GET" + POST = "POST" + + class Status(object): + ENABLED = "enabled" + DISABLED = "disabled" + + class Version(object): + V1 = "v1" + V2 = "v2" + + """ + :ivar sid: The unique string that we created to identify the Webhook resource. + :ivar service_sid: The unique SID identifier of the Service. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Service resource. + :ivar friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :ivar event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :ivar status: + :ivar version: + :ivar webhook_url: The URL associated with this Webhook. + :ivar webhook_method: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar url: The absolute URL of the Webhook resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + service_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.event_types: Optional[List[str]] = payload.get("event_types") + self.status: Optional["WebhookInstance.Status"] = payload.get("status") + self.version: Optional["WebhookInstance.Version"] = payload.get("version") + self.webhook_url: Optional[str] = payload.get("webhook_url") + self.webhook_method: Optional["WebhookInstance.Methods"] = payload.get( + "webhook_method" + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "service_sid": service_sid, + "sid": sid or self.sid, + } + self._context: Optional[WebhookContext] = None + + @property + def _proxy(self) -> "WebhookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: WebhookContext for this WebhookInstance + """ + if self._context is None: + self._context = WebhookContext( + self._version, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "WebhookInstance": + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "WebhookInstance": + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + event_types: Union[List[str], object] = values.unset, + webhook_url: Union[str, object] = values.unset, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> "WebhookInstance": + """ + Update the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The updated WebhookInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + event_types=event_types, + webhook_url=webhook_url, + status=status, + version=version, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + event_types: Union[List[str], object] = values.unset, + webhook_url: Union[str, object] = values.unset, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> "WebhookInstance": + """ + Asynchronous coroutine to update the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The updated WebhookInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + event_types=event_types, + webhook_url=webhook_url, + status=status, + version=version, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookContext(InstanceContext): + + def __init__(self, version: Version, service_sid: str, sid: str): + """ + Initialize the WebhookContext + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + :param sid: The Twilio-provided string that uniquely identifies the Webhook resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + "sid": sid, + } + self._uri = "/Services/{service_sid}/Webhooks/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the WebhookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> WebhookInstance: + """ + Fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> WebhookInstance: + """ + Asynchronous coroutine to fetch the WebhookInstance + + + :returns: The fetched WebhookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + event_types: Union[List[str], object] = values.unset, + webhook_url: Union[str, object] = values.unset, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> WebhookInstance: + """ + Update the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "EventTypes": serialize.map(event_types, lambda e: e), + "WebhookUrl": webhook_url, + "Status": status, + "Version": version, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + event_types: Union[List[str], object] = values.unset, + webhook_url: Union[str, object] = values.unset, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronous coroutine to update the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The updated WebhookInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "EventTypes": serialize.map(event_types, lambda e: e), + "WebhookUrl": webhook_url, + "Status": status, + "Version": version, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, + payload, + service_sid=self._solution["service_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class WebhookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> WebhookInstance: + """ + Build an instance of WebhookInstance + + :param payload: Payload response from the API + """ + return WebhookInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class WebhookList(ListResource): + + def __init__(self, version: Version, service_sid: str): + """ + Initialize the WebhookList + + :param version: Version that contains the resource + :param service_sid: The unique SID identifier of the Service. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "service_sid": service_sid, + } + self._uri = "/Services/{service_sid}/Webhooks".format(**self._solution) + + def create( + self, + friendly_name: str, + event_types: List[str], + webhook_url: str, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> WebhookInstance: + """ + Create the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "EventTypes": serialize.map(event_types, lambda e: e), + "WebhookUrl": webhook_url, + "Status": status, + "Version": version, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + async def create_async( + self, + friendly_name: str, + event_types: List[str], + webhook_url: str, + status: Union["WebhookInstance.Status", object] = values.unset, + version: Union["WebhookInstance.Version", object] = values.unset, + ) -> WebhookInstance: + """ + Asynchronously create the WebhookInstance + + :param friendly_name: The string that you assigned to describe the webhook. **This value should not contain PII.** + :param event_types: The array of events that this Webhook is subscribed to. Possible event types: `*, factor.deleted, factor.created, factor.verified, challenge.approved, challenge.denied` + :param webhook_url: The URL associated with this Webhook. + :param status: + :param version: + + :returns: The created WebhookInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "EventTypes": serialize.map(event_types, lambda e: e), + "WebhookUrl": webhook_url, + "Status": status, + "Version": version, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return WebhookInstance( + self._version, payload, service_sid=self._solution["service_sid"] + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[WebhookInstance]: + """ + Streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[WebhookInstance]: + """ + Asynchronously streams WebhookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[WebhookInstance]: + """ + Asynchronously lists WebhookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return WebhookPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> WebhookPage: + """ + Asynchronously retrieve a single page of WebhookInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of WebhookInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return WebhookPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> WebhookPage: + """ + Retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> WebhookPage: + """ + Asynchronously retrieve a specific page of WebhookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of WebhookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return WebhookPage(self._version, response, self._solution) + + def get(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: The Twilio-provided string that uniquely identifies the Webhook resource to update. + """ + return WebhookContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __call__(self, sid: str) -> WebhookContext: + """ + Constructs a WebhookContext + + :param sid: The Twilio-provided string that uniquely identifies the Webhook resource to update. + """ + return WebhookContext( + self._version, service_sid=self._solution["service_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/template.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/template.py new file mode 100644 index 0000000..b9968ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/template.py @@ -0,0 +1,291 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class TemplateInstance(InstanceResource): + """ + :ivar sid: A 34 character string that uniquely identifies a Verification Template. + :ivar account_sid: The unique SID identifier of the Account. + :ivar friendly_name: A descriptive string that you create to describe a Template. It can be up to 32 characters long. + :ivar channels: A list of channels that support the Template. Can include: sms, voice. + :ivar translations: An object that contains the different translations of the template. Every translation is identified by the language short name and contains its respective information as the approval status, text and created/modified date. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.channels: Optional[List[str]] = payload.get("channels") + self.translations: Optional[Dict[str, object]] = payload.get("translations") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class TemplatePage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> TemplateInstance: + """ + Build an instance of TemplateInstance + + :param payload: Payload response from the API + """ + return TemplateInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class TemplateList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the TemplateList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Templates" + + def stream( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[TemplateInstance]: + """ + Streams TemplateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: String filter used to query templates with a given friendly name. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(friendly_name=friendly_name, page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[TemplateInstance]: + """ + Asynchronously streams TemplateInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str friendly_name: String filter used to query templates with a given friendly name. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + friendly_name=friendly_name, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TemplateInstance]: + """ + Lists TemplateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: String filter used to query templates with a given friendly name. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[TemplateInstance]: + """ + Asynchronously lists TemplateInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str friendly_name: String filter used to query templates with a given friendly name. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TemplatePage: + """ + Retrieve a single page of TemplateInstance records from the API. + Request is executed immediately + + :param friendly_name: String filter used to query templates with a given friendly name. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TemplateInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return TemplatePage(self._version, response) + + async def page_async( + self, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> TemplatePage: + """ + Asynchronously retrieve a single page of TemplateInstance records from the API. + Request is executed immediately + + :param friendly_name: String filter used to query templates with a given friendly name. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of TemplateInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return TemplatePage(self._version, response) + + def get_page(self, target_url: str) -> TemplatePage: + """ + Retrieve a specific page of TemplateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TemplateInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return TemplatePage(self._version, response) + + async def get_page_async(self, target_url: str) -> TemplatePage: + """ + Asynchronously retrieve a specific page of TemplateInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of TemplateInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return TemplatePage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempt.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempt.py new file mode 100644 index 0000000..2c1f914 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempt.py @@ -0,0 +1,586 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class VerificationAttemptInstance(InstanceResource): + + class Channels(object): + SMS = "sms" + CALL = "call" + EMAIL = "email" + WHATSAPP = "whatsapp" + + class ConversionStatus(object): + CONVERTED = "converted" + UNCONVERTED = "unconverted" + + """ + :ivar sid: The SID that uniquely identifies the verification attempt resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Verification resource. + :ivar service_sid: The SID of the [Service](https://www.twilio.com/docs/verify/api/service) used to generate the attempt. + :ivar verification_sid: The SID of the [Verification](https://www.twilio.com/docs/verify/api/verification) that generated the attempt. + :ivar date_created: The date that this Attempt was created, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date that this Attempt was updated, given in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar conversion_status: + :ivar channel: + :ivar price: An object containing the charge for this verification attempt related to the channel costs and the currency used. The costs related to the succeeded verifications are not included. May not be immediately available. More information on pricing is available [here](https://www.twilio.com/en-us/verify/pricing). + :ivar channel_data: An object containing the channel specific information for an attempt. + :ivar url: + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.service_sid: Optional[str] = payload.get("service_sid") + self.verification_sid: Optional[str] = payload.get("verification_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.conversion_status: Optional[ + "VerificationAttemptInstance.ConversionStatus" + ] = payload.get("conversion_status") + self.channel: Optional["VerificationAttemptInstance.Channels"] = payload.get( + "channel" + ) + self.price: Optional[Dict[str, object]] = payload.get("price") + self.channel_data: Optional[Dict[str, object]] = payload.get("channel_data") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[VerificationAttemptContext] = None + + @property + def _proxy(self) -> "VerificationAttemptContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: VerificationAttemptContext for this VerificationAttemptInstance + """ + if self._context is None: + self._context = VerificationAttemptContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "VerificationAttemptInstance": + """ + Fetch the VerificationAttemptInstance + + + :returns: The fetched VerificationAttemptInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "VerificationAttemptInstance": + """ + Asynchronous coroutine to fetch the VerificationAttemptInstance + + + :returns: The fetched VerificationAttemptInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VerificationAttemptContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the VerificationAttemptContext + + :param version: Version that contains the resource + :param sid: The unique SID identifier of a Verification Attempt + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Attempts/{sid}".format(**self._solution) + + def fetch(self) -> VerificationAttemptInstance: + """ + Fetch the VerificationAttemptInstance + + + :returns: The fetched VerificationAttemptInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return VerificationAttemptInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> VerificationAttemptInstance: + """ + Asynchronous coroutine to fetch the VerificationAttemptInstance + + + :returns: The fetched VerificationAttemptInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return VerificationAttemptInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class VerificationAttemptPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> VerificationAttemptInstance: + """ + Build an instance of VerificationAttemptInstance + + :param payload: Payload response from the API + """ + return VerificationAttemptInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class VerificationAttemptList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the VerificationAttemptList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Attempts" + + def stream( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[VerificationAttemptInstance]: + """ + Streams VerificationAttemptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param datetime date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param str channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param str country: Filter used to query Verification Attempts sent to the specified destination country. + :param "VerificationAttemptInstance.Channels" channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param str verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param str verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param "VerificationAttemptInstance.ConversionStatus" status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + date_created_after=date_created_after, + date_created_before=date_created_before, + channel_data_to=channel_data_to, + country=country, + channel=channel, + verify_service_sid=verify_service_sid, + verification_sid=verification_sid, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[VerificationAttemptInstance]: + """ + Asynchronously streams VerificationAttemptInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param datetime date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param str channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param str country: Filter used to query Verification Attempts sent to the specified destination country. + :param "VerificationAttemptInstance.Channels" channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param str verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param str verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param "VerificationAttemptInstance.ConversionStatus" status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + date_created_after=date_created_after, + date_created_before=date_created_before, + channel_data_to=channel_data_to, + country=country, + channel=channel, + verify_service_sid=verify_service_sid, + verification_sid=verification_sid, + status=status, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VerificationAttemptInstance]: + """ + Lists VerificationAttemptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param datetime date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param str channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param str country: Filter used to query Verification Attempts sent to the specified destination country. + :param "VerificationAttemptInstance.Channels" channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param str verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param str verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param "VerificationAttemptInstance.ConversionStatus" status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + date_created_after=date_created_after, + date_created_before=date_created_before, + channel_data_to=channel_data_to, + country=country, + channel=channel, + verify_service_sid=verify_service_sid, + verification_sid=verification_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[VerificationAttemptInstance]: + """ + Asynchronously lists VerificationAttemptInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param datetime date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param str channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param str country: Filter used to query Verification Attempts sent to the specified destination country. + :param "VerificationAttemptInstance.Channels" channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param str verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param str verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param "VerificationAttemptInstance.ConversionStatus" status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + date_created_after=date_created_after, + date_created_before=date_created_before, + channel_data_to=channel_data_to, + country=country, + channel=channel, + verify_service_sid=verify_service_sid, + verification_sid=verification_sid, + status=status, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VerificationAttemptPage: + """ + Retrieve a single page of VerificationAttemptInstance records from the API. + Request is executed immediately + + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param country: Filter used to query Verification Attempts sent to the specified destination country. + :param channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VerificationAttemptInstance + """ + data = values.of( + { + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "ChannelData.To": channel_data_to, + "Country": country, + "Channel": channel, + "VerifyServiceSid": verify_service_sid, + "VerificationSid": verification_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return VerificationAttemptPage(self._version, response) + + async def page_async( + self, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + channel_data_to: Union[str, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union["VerificationAttemptInstance.Channels", object] = values.unset, + verify_service_sid: Union[str, object] = values.unset, + verification_sid: Union[str, object] = values.unset, + status: Union[ + "VerificationAttemptInstance.ConversionStatus", object + ] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> VerificationAttemptPage: + """ + Asynchronously retrieve a single page of VerificationAttemptInstance records from the API. + Request is executed immediately + + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param channel_data_to: Destination of a verification. It is phone number in E.164 format. + :param country: Filter used to query Verification Attempts sent to the specified destination country. + :param channel: Filter used to query Verification Attempts by communication channel. Valid values are `SMS` and `CALL` + :param verify_service_sid: Filter used to query Verification Attempts by verify service. Only attempts of the provided SID will be returned. + :param verification_sid: Filter used to return all the Verification Attempts of a single verification. Only attempts of the provided verification SID will be returned. + :param status: Filter used to query Verification Attempts by conversion status. Valid values are `UNCONVERTED`, for attempts that were not converted, and `CONVERTED`, for attempts that were confirmed. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of VerificationAttemptInstance + """ + data = values.of( + { + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "ChannelData.To": channel_data_to, + "Country": country, + "Channel": channel, + "VerifyServiceSid": verify_service_sid, + "VerificationSid": verification_sid, + "Status": status, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return VerificationAttemptPage(self._version, response) + + def get_page(self, target_url: str) -> VerificationAttemptPage: + """ + Retrieve a specific page of VerificationAttemptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VerificationAttemptInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return VerificationAttemptPage(self._version, response) + + async def get_page_async(self, target_url: str) -> VerificationAttemptPage: + """ + Asynchronously retrieve a specific page of VerificationAttemptInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of VerificationAttemptInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return VerificationAttemptPage(self._version, response) + + def get(self, sid: str) -> VerificationAttemptContext: + """ + Constructs a VerificationAttemptContext + + :param sid: The unique SID identifier of a Verification Attempt + """ + return VerificationAttemptContext(self._version, sid=sid) + + def __call__(self, sid: str) -> VerificationAttemptContext: + """ + Constructs a VerificationAttemptContext + + :param sid: The unique SID identifier of a Verification Attempt + """ + return VerificationAttemptContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempts_summary.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempts_summary.py new file mode 100644 index 0000000..1f2d3d3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/verify/v2/verification_attempts_summary.py @@ -0,0 +1,286 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Verify + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional, Union +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class VerificationAttemptsSummaryInstance(InstanceResource): + + class Channels(object): + SMS = "sms" + CALL = "call" + EMAIL = "email" + WHATSAPP = "whatsapp" + + """ + :ivar total_attempts: Total of attempts made according to the provided filters + :ivar total_converted: Total of attempts made that were confirmed by the end user, according to the provided filters. + :ivar total_unconverted: Total of attempts made that were not confirmed by the end user, according to the provided filters. + :ivar conversion_rate_percentage: Percentage of the confirmed messages over the total, defined by (total_converted/total_attempts)*100. + :ivar url: + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.total_attempts: Optional[int] = deserialize.integer( + payload.get("total_attempts") + ) + self.total_converted: Optional[int] = deserialize.integer( + payload.get("total_converted") + ) + self.total_unconverted: Optional[int] = deserialize.integer( + payload.get("total_unconverted") + ) + self.conversion_rate_percentage: Optional[float] = deserialize.decimal( + payload.get("conversion_rate_percentage") + ) + self.url: Optional[str] = payload.get("url") + + self._context: Optional[VerificationAttemptsSummaryContext] = None + + @property + def _proxy(self) -> "VerificationAttemptsSummaryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: VerificationAttemptsSummaryContext for this VerificationAttemptsSummaryInstance + """ + if self._context is None: + self._context = VerificationAttemptsSummaryContext( + self._version, + ) + return self._context + + def fetch( + self, + verify_service_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union[ + "VerificationAttemptsSummaryInstance.Channels", object + ] = values.unset, + destination_prefix: Union[str, object] = values.unset, + ) -> "VerificationAttemptsSummaryInstance": + """ + Fetch the VerificationAttemptsSummaryInstance + + :param verify_service_sid: Filter used to consider only Verification Attempts of the given verify service on the summary aggregation. + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param country: Filter used to consider only Verification Attempts sent to the specified destination country on the summary aggregation. + :param channel: Filter Verification Attempts considered on the summary aggregation by communication channel. Valid values are `SMS`, `CALL` and `WHATSAPP` + :param destination_prefix: Filter the Verification Attempts considered on the summary aggregation by Destination prefix. It is the prefix of a phone number in E.164 format. + + :returns: The fetched VerificationAttemptsSummaryInstance + """ + return self._proxy.fetch( + verify_service_sid=verify_service_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + country=country, + channel=channel, + destination_prefix=destination_prefix, + ) + + async def fetch_async( + self, + verify_service_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union[ + "VerificationAttemptsSummaryInstance.Channels", object + ] = values.unset, + destination_prefix: Union[str, object] = values.unset, + ) -> "VerificationAttemptsSummaryInstance": + """ + Asynchronous coroutine to fetch the VerificationAttemptsSummaryInstance + + :param verify_service_sid: Filter used to consider only Verification Attempts of the given verify service on the summary aggregation. + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param country: Filter used to consider only Verification Attempts sent to the specified destination country on the summary aggregation. + :param channel: Filter Verification Attempts considered on the summary aggregation by communication channel. Valid values are `SMS`, `CALL` and `WHATSAPP` + :param destination_prefix: Filter the Verification Attempts considered on the summary aggregation by Destination prefix. It is the prefix of a phone number in E.164 format. + + :returns: The fetched VerificationAttemptsSummaryInstance + """ + return await self._proxy.fetch_async( + verify_service_sid=verify_service_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + country=country, + channel=channel, + destination_prefix=destination_prefix, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class VerificationAttemptsSummaryContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the VerificationAttemptsSummaryContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Attempts/Summary" + + def fetch( + self, + verify_service_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union[ + "VerificationAttemptsSummaryInstance.Channels", object + ] = values.unset, + destination_prefix: Union[str, object] = values.unset, + ) -> VerificationAttemptsSummaryInstance: + """ + Fetch the VerificationAttemptsSummaryInstance + + :param verify_service_sid: Filter used to consider only Verification Attempts of the given verify service on the summary aggregation. + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param country: Filter used to consider only Verification Attempts sent to the specified destination country on the summary aggregation. + :param channel: Filter Verification Attempts considered on the summary aggregation by communication channel. Valid values are `SMS`, `CALL` and `WHATSAPP` + :param destination_prefix: Filter the Verification Attempts considered on the summary aggregation by Destination prefix. It is the prefix of a phone number in E.164 format. + + :returns: The fetched VerificationAttemptsSummaryInstance + """ + + data = values.of( + { + "VerifyServiceSid": verify_service_sid, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "Country": country, + "Channel": channel, + "DestinationPrefix": destination_prefix, + } + ) + + payload = self._version.fetch(method="GET", uri=self._uri, params=data) + + return VerificationAttemptsSummaryInstance( + self._version, + payload, + ) + + async def fetch_async( + self, + verify_service_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + country: Union[str, object] = values.unset, + channel: Union[ + "VerificationAttemptsSummaryInstance.Channels", object + ] = values.unset, + destination_prefix: Union[str, object] = values.unset, + ) -> VerificationAttemptsSummaryInstance: + """ + Asynchronous coroutine to fetch the VerificationAttemptsSummaryInstance + + :param verify_service_sid: Filter used to consider only Verification Attempts of the given verify service on the summary aggregation. + :param date_created_after: Datetime filter used to consider only Verification Attempts created after this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param date_created_before: Datetime filter used to consider only Verification Attempts created before this datetime on the summary aggregation. Given as GMT in ISO 8601 formatted datetime string: yyyy-MM-dd'T'HH:mm:ss'Z. + :param country: Filter used to consider only Verification Attempts sent to the specified destination country on the summary aggregation. + :param channel: Filter Verification Attempts considered on the summary aggregation by communication channel. Valid values are `SMS`, `CALL` and `WHATSAPP` + :param destination_prefix: Filter the Verification Attempts considered on the summary aggregation by Destination prefix. It is the prefix of a phone number in E.164 format. + + :returns: The fetched VerificationAttemptsSummaryInstance + """ + + data = values.of( + { + "VerifyServiceSid": verify_service_sid, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "Country": country, + "Channel": channel, + "DestinationPrefix": destination_prefix, + } + ) + + payload = await self._version.fetch_async( + method="GET", uri=self._uri, params=data + ) + + return VerificationAttemptsSummaryInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class VerificationAttemptsSummaryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the VerificationAttemptsSummaryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> VerificationAttemptsSummaryContext: + """ + Constructs a VerificationAttemptsSummaryContext + + """ + return VerificationAttemptsSummaryContext(self._version) + + def __call__(self) -> VerificationAttemptsSummaryContext: + """ + Constructs a VerificationAttemptsSummaryContext + + """ + return VerificationAttemptsSummaryContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/VideoBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/VideoBase.py new file mode 100644 index 0000000..0d2cf7e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/VideoBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.video.v1 import V1 + + +class VideoBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Video Domain + + :returns: Domain for Video + """ + super().__init__(twilio, "https://video.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Video + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__init__.py new file mode 100644 index 0000000..d57e4f1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__init__.py @@ -0,0 +1,65 @@ +from warnings import warn + +from twilio.rest.video.VideoBase import VideoBase +from twilio.rest.video.v1.composition import CompositionList +from twilio.rest.video.v1.composition_hook import CompositionHookList +from twilio.rest.video.v1.composition_settings import CompositionSettingsList +from twilio.rest.video.v1.recording import RecordingList +from twilio.rest.video.v1.recording_settings import RecordingSettingsList +from twilio.rest.video.v1.room import RoomList + + +class Video(VideoBase): + @property + def compositions(self) -> CompositionList: + warn( + "compositions is deprecated. Use v1.compositions instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.compositions + + @property + def composition_hooks(self) -> CompositionHookList: + warn( + "composition_hooks is deprecated. Use v1.composition_hooks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.composition_hooks + + @property + def composition_settings(self) -> CompositionSettingsList: + warn( + "composition_settings is deprecated. Use v1.composition_settings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.composition_settings + + @property + def recordings(self) -> RecordingList: + warn( + "recordings is deprecated. Use v1.recordings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.recordings + + @property + def recording_settings(self) -> RecordingSettingsList: + warn( + "recording_settings is deprecated. Use v1.recording_settings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.recording_settings + + @property + def rooms(self) -> RoomList: + warn( + "rooms is deprecated. Use v1.rooms instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.rooms diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/VideoBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/VideoBase.cpython-310.pyc new file mode 100644 index 0000000..78b0cdb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/VideoBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3b677aa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__init__.py new file mode 100644 index 0000000..0667596 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__init__.py @@ -0,0 +1,83 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.video.v1.composition import CompositionList +from twilio.rest.video.v1.composition_hook import CompositionHookList +from twilio.rest.video.v1.composition_settings import CompositionSettingsList +from twilio.rest.video.v1.recording import RecordingList +from twilio.rest.video.v1.recording_settings import RecordingSettingsList +from twilio.rest.video.v1.room import RoomList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Video + + :param domain: The Twilio.video domain + """ + super().__init__(domain, "v1") + self._compositions: Optional[CompositionList] = None + self._composition_hooks: Optional[CompositionHookList] = None + self._composition_settings: Optional[CompositionSettingsList] = None + self._recordings: Optional[RecordingList] = None + self._recording_settings: Optional[RecordingSettingsList] = None + self._rooms: Optional[RoomList] = None + + @property + def compositions(self) -> CompositionList: + if self._compositions is None: + self._compositions = CompositionList(self) + return self._compositions + + @property + def composition_hooks(self) -> CompositionHookList: + if self._composition_hooks is None: + self._composition_hooks = CompositionHookList(self) + return self._composition_hooks + + @property + def composition_settings(self) -> CompositionSettingsList: + if self._composition_settings is None: + self._composition_settings = CompositionSettingsList(self) + return self._composition_settings + + @property + def recordings(self) -> RecordingList: + if self._recordings is None: + self._recordings = RecordingList(self) + return self._recordings + + @property + def recording_settings(self) -> RecordingSettingsList: + if self._recording_settings is None: + self._recording_settings = RecordingSettingsList(self) + return self._recording_settings + + @property + def rooms(self) -> RoomList: + if self._rooms is None: + self._rooms = RoomList(self) + return self._rooms + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..9067610 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition.cpython-310.pyc new file mode 100644 index 0000000..77c8929 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_hook.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_hook.cpython-310.pyc new file mode 100644 index 0000000..3abbd2b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_hook.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_settings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_settings.cpython-310.pyc new file mode 100644 index 0000000..ec30a50 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/composition_settings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording.cpython-310.pyc new file mode 100644 index 0000000..b7080ef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording_settings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording_settings.cpython-310.pyc new file mode 100644 index 0000000..974bc67 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/__pycache__/recording_settings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition.py new file mode 100644 index 0000000..30aa33f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition.py @@ -0,0 +1,673 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CompositionInstance(InstanceResource): + + class Format(object): + MP4 = "mp4" + WEBM = "webm" + + class Status(object): + ENQUEUED = "enqueued" + PROCESSING = "processing" + COMPLETED = "completed" + DELETED = "deleted" + FAILED = "failed" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Composition resource. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_completed: The date and time in GMT when the composition's media processing task finished, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_deleted: The date and time in GMT when the composition generated media was deleted, specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar sid: The unique string that we created to identify the Composition resource. + :ivar room_sid: The SID of the Group Room that generated the audio and video tracks used in the composition. All media sources included in a composition must belong to the same Group Room. + :ivar audio_sources: The array of track names to include in the composition. The composition includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this property can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :ivar audio_sources_excluded: The array of track names to exclude from the composition. The composition includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this property can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :ivar video_layout: An object that describes the video layout of the composition in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :ivar resolution: The dimensions of the video image in pixels expressed as columns (width) and rows (height). The string's format is `{width}x{height}`, such as `640x480`. + :ivar trim: Whether to remove intervals with no media, as specified in the POST request that created the composition. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :ivar format: + :ivar bitrate: The average bit rate of the composition's media. + :ivar size: The size of the composed media file in bytes. + :ivar duration: The duration of the composition's media file in seconds. + :ivar media_external_location: The URL of the media file associated with the composition when stored externally. See [External S3 Compositions](/docs/video/api/external-s3-compositions) for more details. + :ivar status_callback: The URL called using the `status_callback_method` to send status information on every composition event. + :ivar status_callback_method: The HTTP method used to call `status_callback`. Can be: `POST` or `GET`, defaults to `POST`. + :ivar url: The absolute URL of the resource. + :ivar links: The URL of the media file associated with the composition. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["CompositionInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_completed: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_completed") + ) + self.date_deleted: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_deleted") + ) + self.sid: Optional[str] = payload.get("sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.audio_sources: Optional[List[str]] = payload.get("audio_sources") + self.audio_sources_excluded: Optional[List[str]] = payload.get( + "audio_sources_excluded" + ) + self.video_layout: Optional[Dict[str, object]] = payload.get("video_layout") + self.resolution: Optional[str] = payload.get("resolution") + self.trim: Optional[bool] = payload.get("trim") + self.format: Optional["CompositionInstance.Format"] = payload.get("format") + self.bitrate: Optional[int] = deserialize.integer(payload.get("bitrate")) + self.size: Optional[int] = payload.get("size") + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.media_external_location: Optional[str] = payload.get( + "media_external_location" + ) + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CompositionContext] = None + + @property + def _proxy(self) -> "CompositionContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CompositionContext for this CompositionInstance + """ + if self._context is None: + self._context = CompositionContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CompositionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CompositionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CompositionInstance": + """ + Fetch the CompositionInstance + + + :returns: The fetched CompositionInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CompositionInstance": + """ + Asynchronous coroutine to fetch the CompositionInstance + + + :returns: The fetched CompositionInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CompositionContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CompositionContext + + :param version: Version that contains the resource + :param sid: The SID of the Composition resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Compositions/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CompositionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CompositionInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CompositionInstance: + """ + Fetch the CompositionInstance + + + :returns: The fetched CompositionInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CompositionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CompositionInstance: + """ + Asynchronous coroutine to fetch the CompositionInstance + + + :returns: The fetched CompositionInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CompositionInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CompositionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CompositionInstance: + """ + Build an instance of CompositionInstance + + :param payload: Payload response from the API + """ + return CompositionInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CompositionList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CompositionList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Compositions" + + def create( + self, + room_sid: str, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + resolution: Union[str, object] = values.unset, + format: Union["CompositionInstance.Format", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + trim: Union[bool, object] = values.unset, + ) -> CompositionInstance: + """ + Create the CompositionInstance + + :param room_sid: The SID of the Group Room with the media tracks to be used as composition sources. + :param video_layout: An object that describes the video layout of the composition in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :param audio_sources: An array of track names from the same group room to merge into the new composition. Can include zero or more track names. The new composition includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which will match zero or more characters in a track name. For example, `student*` includes `student` as well as `studentTeam`. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :param audio_sources_excluded: An array of track names to exclude. The new composition includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which will match zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param trim: Whether to clip the intervals where there is no active media in the composition. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + + :returns: The created CompositionInstance + """ + + data = values.of( + { + "RoomSid": room_sid, + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Resolution": resolution, + "Format": format, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Trim": serialize.boolean_to_string(trim), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionInstance(self._version, payload) + + async def create_async( + self, + room_sid: str, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + resolution: Union[str, object] = values.unset, + format: Union["CompositionInstance.Format", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + trim: Union[bool, object] = values.unset, + ) -> CompositionInstance: + """ + Asynchronously create the CompositionInstance + + :param room_sid: The SID of the Group Room with the media tracks to be used as composition sources. + :param video_layout: An object that describes the video layout of the composition in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :param audio_sources: An array of track names from the same group room to merge into the new composition. Can include zero or more track names. The new composition includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which will match zero or more characters in a track name. For example, `student*` includes `student` as well as `studentTeam`. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :param audio_sources_excluded: An array of track names to exclude. The new composition includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which will match zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param trim: Whether to clip the intervals where there is no active media in the composition. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + + :returns: The created CompositionInstance + """ + + data = values.of( + { + "RoomSid": room_sid, + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Resolution": resolution, + "Format": format, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Trim": serialize.boolean_to_string(trim), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionInstance(self._version, payload) + + def stream( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CompositionInstance]: + """ + Streams CompositionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "CompositionInstance.Status" status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param datetime date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param str room_sid: Read only Composition resources with this Room SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + date_created_after=date_created_after, + date_created_before=date_created_before, + room_sid=room_sid, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CompositionInstance]: + """ + Asynchronously streams CompositionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "CompositionInstance.Status" status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param datetime date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param str room_sid: Read only Composition resources with this Room SID. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + date_created_after=date_created_after, + date_created_before=date_created_before, + room_sid=room_sid, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CompositionInstance]: + """ + Lists CompositionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "CompositionInstance.Status" status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param datetime date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param str room_sid: Read only Composition resources with this Room SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + date_created_after=date_created_after, + date_created_before=date_created_before, + room_sid=room_sid, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CompositionInstance]: + """ + Asynchronously lists CompositionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "CompositionInstance.Status" status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param datetime date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param str room_sid: Read only Composition resources with this Room SID. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + date_created_after=date_created_after, + date_created_before=date_created_before, + room_sid=room_sid, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CompositionPage: + """ + Retrieve a single page of CompositionInstance records from the API. + Request is executed immediately + + :param status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param room_sid: Read only Composition resources with this Room SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CompositionInstance + """ + data = values.of( + { + "Status": status, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "RoomSid": room_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CompositionPage(self._version, response) + + async def page_async( + self, + status: Union["CompositionInstance.Status", object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + room_sid: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CompositionPage: + """ + Asynchronously retrieve a single page of CompositionInstance records from the API. + Request is executed immediately + + :param status: Read only Composition resources with this status. Can be: `enqueued`, `processing`, `completed`, `deleted`, or `failed`. + :param date_created_after: Read only Composition resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param date_created_before: Read only Composition resources created before this ISO 8601 date-time with time zone. + :param room_sid: Read only Composition resources with this Room SID. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CompositionInstance + """ + data = values.of( + { + "Status": status, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "RoomSid": room_sid, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CompositionPage(self._version, response) + + def get_page(self, target_url: str) -> CompositionPage: + """ + Retrieve a specific page of CompositionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CompositionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CompositionPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CompositionPage: + """ + Asynchronously retrieve a specific page of CompositionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CompositionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CompositionPage(self._version, response) + + def get(self, sid: str) -> CompositionContext: + """ + Constructs a CompositionContext + + :param sid: The SID of the Composition resource to fetch. + """ + return CompositionContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CompositionContext: + """ + Constructs a CompositionContext + + :param sid: The SID of the Composition resource to fetch. + """ + return CompositionContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_hook.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_hook.py new file mode 100644 index 0000000..027a27f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_hook.py @@ -0,0 +1,852 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CompositionHookInstance(InstanceResource): + + class Format(object): + MP4 = "mp4" + WEBM = "webm" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CompositionHook resource. + :ivar friendly_name: The string that you assigned to describe the resource. Can be up to 100 characters long and must be unique within the account. + :ivar enabled: Whether the CompositionHook is active. When `true`, the CompositionHook is triggered for every completed Group Room on the account. When `false`, the CompositionHook is never triggered. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar sid: The unique string that we created to identify the CompositionHook resource. + :ivar audio_sources: The array of track names to include in the compositions created by the composition hook. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this property can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :ivar audio_sources_excluded: The array of track names to exclude from the compositions created by the composition hook. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this property can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :ivar video_layout: A JSON object that describes the video layout of the composition in terms of regions as specified in the HTTP POST request that created the CompositionHook resource. See [POST Parameters](https://www.twilio.com/docs/video/api/compositions-resource#http-post-parameters) for more information. Please, be aware that either video_layout or audio_sources have to be provided to get a valid creation request + :ivar resolution: The dimensions of the video image in pixels expressed as columns (width) and rows (height). The string's format is `{width}x{height}`, such as `640x480`. + :ivar trim: Whether intervals with no media are clipped, as specified in the POST request that created the CompositionHook resource. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :ivar format: + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application. + :ivar status_callback_method: The HTTP method we should use to call `status_callback`. Can be `POST` or `GET` and defaults to `POST`. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.enabled: Optional[bool] = payload.get("enabled") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.sid: Optional[str] = payload.get("sid") + self.audio_sources: Optional[List[str]] = payload.get("audio_sources") + self.audio_sources_excluded: Optional[List[str]] = payload.get( + "audio_sources_excluded" + ) + self.video_layout: Optional[Dict[str, object]] = payload.get("video_layout") + self.resolution: Optional[str] = payload.get("resolution") + self.trim: Optional[bool] = payload.get("trim") + self.format: Optional["CompositionHookInstance.Format"] = payload.get("format") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CompositionHookContext] = None + + @property + def _proxy(self) -> "CompositionHookContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CompositionHookContext for this CompositionHookInstance + """ + if self._context is None: + self._context = CompositionHookContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CompositionHookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CompositionHookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CompositionHookInstance": + """ + Fetch the CompositionHookInstance + + + :returns: The fetched CompositionHookInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CompositionHookInstance": + """ + Asynchronous coroutine to fetch the CompositionHookInstance + + + :returns: The fetched CompositionHookInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + trim: Union[bool, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + resolution: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> "CompositionHookInstance": + """ + Update the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook never triggers. + :param video_layout: A JSON object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param trim: Whether to clip the intervals where there is no active media in the compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + + :returns: The updated CompositionHookInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + enabled=enabled, + video_layout=video_layout, + audio_sources=audio_sources, + audio_sources_excluded=audio_sources_excluded, + trim=trim, + format=format, + resolution=resolution, + status_callback=status_callback, + status_callback_method=status_callback_method, + ) + + async def update_async( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + trim: Union[bool, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + resolution: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> "CompositionHookInstance": + """ + Asynchronous coroutine to update the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook never triggers. + :param video_layout: A JSON object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param trim: Whether to clip the intervals where there is no active media in the compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + + :returns: The updated CompositionHookInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + enabled=enabled, + video_layout=video_layout, + audio_sources=audio_sources, + audio_sources_excluded=audio_sources_excluded, + trim=trim, + format=format, + resolution=resolution, + status_callback=status_callback, + status_callback_method=status_callback_method, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CompositionHookContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CompositionHookContext + + :param version: Version that contains the resource + :param sid: The SID of the CompositionHook resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/CompositionHooks/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CompositionHookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CompositionHookInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CompositionHookInstance: + """ + Fetch the CompositionHookInstance + + + :returns: The fetched CompositionHookInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CompositionHookInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CompositionHookInstance: + """ + Asynchronous coroutine to fetch the CompositionHookInstance + + + :returns: The fetched CompositionHookInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CompositionHookInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + trim: Union[bool, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + resolution: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> CompositionHookInstance: + """ + Update the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook never triggers. + :param video_layout: A JSON object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param trim: Whether to clip the intervals where there is no active media in the compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + + :returns: The updated CompositionHookInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Enabled": serialize.boolean_to_string(enabled), + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Trim": serialize.boolean_to_string(trim), + "Format": format, + "Resolution": resolution, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionHookInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + trim: Union[bool, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + resolution: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + ) -> CompositionHookInstance: + """ + Asynchronous coroutine to update the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook never triggers. + :param video_layout: A JSON object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param trim: Whether to clip the intervals where there is no active media in the compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + + :returns: The updated CompositionHookInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Enabled": serialize.boolean_to_string(enabled), + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Trim": serialize.boolean_to_string(trim), + "Format": format, + "Resolution": resolution, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionHookInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CompositionHookPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CompositionHookInstance: + """ + Build an instance of CompositionHookInstance + + :param payload: Payload response from the API + """ + return CompositionHookInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CompositionHookList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CompositionHookList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/CompositionHooks" + + def create( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + resolution: Union[str, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + trim: Union[bool, object] = values.unset, + ) -> CompositionHookInstance: + """ + Create the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook will never be triggered. + :param video_layout: An object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param trim: Whether to clip the intervals where there is no active media in the Compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + + :returns: The created CompositionHookInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Enabled": serialize.boolean_to_string(enabled), + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Resolution": resolution, + "Format": format, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Trim": serialize.boolean_to_string(trim), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionHookInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + enabled: Union[bool, object] = values.unset, + video_layout: Union[object, object] = values.unset, + audio_sources: Union[List[str], object] = values.unset, + audio_sources_excluded: Union[List[str], object] = values.unset, + resolution: Union[str, object] = values.unset, + format: Union["CompositionHookInstance.Format", object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + trim: Union[bool, object] = values.unset, + ) -> CompositionHookInstance: + """ + Asynchronously create the CompositionHookInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It can be up to 100 characters long and it must be unique within the account. + :param enabled: Whether the composition hook is active. When `true`, the composition hook will be triggered for every completed Group Room in the account. When `false`, the composition hook will never be triggered. + :param video_layout: An object that describes the video layout of the composition hook in terms of regions. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param audio_sources: An array of track names from the same group room to merge into the compositions created by the composition hook. Can include zero or more track names. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` includes tracks named `student` as well as `studentTeam`. + :param audio_sources_excluded: An array of track names to exclude. A composition triggered by the composition hook includes all audio sources specified in `audio_sources` except for those specified in `audio_sources_excluded`. The track names in this parameter can include an asterisk as a wild card character, which matches zero or more characters in a track name. For example, `student*` excludes `student` as well as `studentTeam`. This parameter can also be empty. + :param resolution: A string that describes the columns (width) and rows (height) of the generated composed video in pixels. Defaults to `640x480`. The string's format is `{width}x{height}` where: * 16 <= `{width}` <= 1280 * 16 <= `{height}` <= 1280 * `{width}` * `{height}` <= 921,600 Typical values are: * HD = `1280x720` * PAL = `1024x576` * VGA = `640x480` * CIF = `320x240` Note that the `resolution` imposes an aspect ratio to the resulting composition. When the original video tracks are constrained by the aspect ratio, they are scaled to fit. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + :param format: + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every composition event. If not provided, status callback events will not be dispatched. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be: `POST` or `GET` and the default is `POST`. + :param trim: Whether to clip the intervals where there is no active media in the Compositions triggered by the composition hook. The default is `true`. Compositions with `trim` enabled are shorter when the Room is created and no Participant joins for a while as well as if all the Participants leave the room and join later, because those gaps will be removed. See [Specifying Video Layouts](https://www.twilio.com/docs/video/api/compositions-resource#specifying-video-layouts) for more info. + + :returns: The created CompositionHookInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "Enabled": serialize.boolean_to_string(enabled), + "VideoLayout": serialize.object(video_layout), + "AudioSources": serialize.map(audio_sources, lambda e: e), + "AudioSourcesExcluded": serialize.map( + audio_sources_excluded, lambda e: e + ), + "Resolution": resolution, + "Format": format, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "Trim": serialize.boolean_to_string(trim), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CompositionHookInstance(self._version, payload) + + def stream( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CompositionHookInstance]: + """ + Streams CompositionHookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param datetime date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param str friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + enabled=enabled, + date_created_after=date_created_after, + date_created_before=date_created_before, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CompositionHookInstance]: + """ + Asynchronously streams CompositionHookInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param bool enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param datetime date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param str friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + enabled=enabled, + date_created_after=date_created_after, + date_created_before=date_created_before, + friendly_name=friendly_name, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CompositionHookInstance]: + """ + Lists CompositionHookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param datetime date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param str friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + enabled=enabled, + date_created_after=date_created_after, + date_created_before=date_created_before, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CompositionHookInstance]: + """ + Asynchronously lists CompositionHookInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param bool enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param datetime date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param str friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + enabled=enabled, + date_created_after=date_created_after, + date_created_before=date_created_before, + friendly_name=friendly_name, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CompositionHookPage: + """ + Retrieve a single page of CompositionHookInstance records from the API. + Request is executed immediately + + :param enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CompositionHookInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CompositionHookPage(self._version, response) + + async def page_async( + self, + enabled: Union[bool, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CompositionHookPage: + """ + Asynchronously retrieve a single page of CompositionHookInstance records from the API. + Request is executed immediately + + :param enabled: Read only CompositionHook resources with an `enabled` value that matches this parameter. + :param date_created_after: Read only CompositionHook resources created on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param date_created_before: Read only CompositionHook resources created before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param friendly_name: Read only CompositionHook resources with friendly names that match this string. The match is not case sensitive and can include asterisk `*` characters as wildcard match. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CompositionHookInstance + """ + data = values.of( + { + "Enabled": serialize.boolean_to_string(enabled), + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "FriendlyName": friendly_name, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CompositionHookPage(self._version, response) + + def get_page(self, target_url: str) -> CompositionHookPage: + """ + Retrieve a specific page of CompositionHookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CompositionHookInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CompositionHookPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CompositionHookPage: + """ + Asynchronously retrieve a specific page of CompositionHookInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CompositionHookInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CompositionHookPage(self._version, response) + + def get(self, sid: str) -> CompositionHookContext: + """ + Constructs a CompositionHookContext + + :param sid: The SID of the CompositionHook resource to update. + """ + return CompositionHookContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CompositionHookContext: + """ + Constructs a CompositionHookContext + + :param sid: The SID of the CompositionHook resource to update. + """ + return CompositionHookContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_settings.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_settings.py new file mode 100644 index 0000000..366ec72 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/composition_settings.py @@ -0,0 +1,314 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class CompositionSettingsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the CompositionSettings resource. + :ivar friendly_name: The string that you assigned to describe the resource and that will be shown in the console + :ivar aws_credentials_sid: The SID of the stored Credential resource. + :ivar aws_s3_url: The URL of the AWS S3 bucket where the compositions are stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/compositions`, where `compositions` is the path in which you want the compositions to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :ivar aws_storage_enabled: Whether all compositions are written to the `aws_s3_url`. When `false`, all compositions are stored in our cloud. + :ivar encryption_key_sid: The SID of the Public Key resource used for encryption. + :ivar encryption_enabled: Whether all compositions are stored in an encrypted form. The default is `false`. + :ivar url: The absolute URL of the resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.aws_credentials_sid: Optional[str] = payload.get("aws_credentials_sid") + self.aws_s3_url: Optional[str] = payload.get("aws_s3_url") + self.aws_storage_enabled: Optional[bool] = payload.get("aws_storage_enabled") + self.encryption_key_sid: Optional[str] = payload.get("encryption_key_sid") + self.encryption_enabled: Optional[bool] = payload.get("encryption_enabled") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[CompositionSettingsContext] = None + + @property + def _proxy(self) -> "CompositionSettingsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CompositionSettingsContext for this CompositionSettingsInstance + """ + if self._context is None: + self._context = CompositionSettingsContext( + self._version, + ) + return self._context + + def create( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> "CompositionSettingsInstance": + """ + Create the CompositionSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and show to the user in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the compositions should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/compositions`, where `compositions` is the path in which you want the compositions to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all compositions should be written to the `aws_s3_url`. When `false`, all compositions are stored in our cloud. + :param encryption_enabled: Whether all compositions should be stored in an encrypted form. The default is `false`. + + :returns: The created CompositionSettingsInstance + """ + return self._proxy.create( + friendly_name, + aws_credentials_sid=aws_credentials_sid, + encryption_key_sid=encryption_key_sid, + aws_s3_url=aws_s3_url, + aws_storage_enabled=aws_storage_enabled, + encryption_enabled=encryption_enabled, + ) + + async def create_async( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> "CompositionSettingsInstance": + """ + Asynchronous coroutine to create the CompositionSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and show to the user in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the compositions should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/compositions`, where `compositions` is the path in which you want the compositions to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all compositions should be written to the `aws_s3_url`. When `false`, all compositions are stored in our cloud. + :param encryption_enabled: Whether all compositions should be stored in an encrypted form. The default is `false`. + + :returns: The created CompositionSettingsInstance + """ + return await self._proxy.create_async( + friendly_name, + aws_credentials_sid=aws_credentials_sid, + encryption_key_sid=encryption_key_sid, + aws_s3_url=aws_s3_url, + aws_storage_enabled=aws_storage_enabled, + encryption_enabled=encryption_enabled, + ) + + def fetch(self) -> "CompositionSettingsInstance": + """ + Fetch the CompositionSettingsInstance + + + :returns: The fetched CompositionSettingsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CompositionSettingsInstance": + """ + Asynchronous coroutine to fetch the CompositionSettingsInstance + + + :returns: The fetched CompositionSettingsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class CompositionSettingsContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the CompositionSettingsContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/CompositionSettings/Default" + + def create( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> CompositionSettingsInstance: + """ + Create the CompositionSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and show to the user in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the compositions should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/compositions`, where `compositions` is the path in which you want the compositions to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all compositions should be written to the `aws_s3_url`. When `false`, all compositions are stored in our cloud. + :param encryption_enabled: Whether all compositions should be stored in an encrypted form. The default is `false`. + + :returns: The created CompositionSettingsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AwsCredentialsSid": aws_credentials_sid, + "EncryptionKeySid": encryption_key_sid, + "AwsS3Url": aws_s3_url, + "AwsStorageEnabled": serialize.boolean_to_string(aws_storage_enabled), + "EncryptionEnabled": serialize.boolean_to_string(encryption_enabled), + } + ) + + payload = self._version.create(method="POST", uri=self._uri, data=data) + + return CompositionSettingsInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> CompositionSettingsInstance: + """ + Asynchronous coroutine to create the CompositionSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and show to the user in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the compositions should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/compositions`, where `compositions` is the path in which you want the compositions to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all compositions should be written to the `aws_s3_url`. When `false`, all compositions are stored in our cloud. + :param encryption_enabled: Whether all compositions should be stored in an encrypted form. The default is `false`. + + :returns: The created CompositionSettingsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AwsCredentialsSid": aws_credentials_sid, + "EncryptionKeySid": encryption_key_sid, + "AwsS3Url": aws_s3_url, + "AwsStorageEnabled": serialize.boolean_to_string(aws_storage_enabled), + "EncryptionEnabled": serialize.boolean_to_string(encryption_enabled), + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data + ) + + return CompositionSettingsInstance(self._version, payload) + + def fetch(self) -> CompositionSettingsInstance: + """ + Fetch the CompositionSettingsInstance + + + :returns: The fetched CompositionSettingsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CompositionSettingsInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> CompositionSettingsInstance: + """ + Asynchronous coroutine to fetch the CompositionSettingsInstance + + + :returns: The fetched CompositionSettingsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CompositionSettingsInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class CompositionSettingsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CompositionSettingsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> CompositionSettingsContext: + """ + Constructs a CompositionSettingsContext + + """ + return CompositionSettingsContext(self._version) + + def __call__(self) -> CompositionSettingsContext: + """ + Constructs a CompositionSettingsContext + + """ + return CompositionSettingsContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording.py new file mode 100644 index 0000000..20c836d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording.py @@ -0,0 +1,605 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RecordingInstance(InstanceResource): + + class Codec(object): + VP8 = "VP8" + H264 = "H264" + OPUS = "OPUS" + PCMU = "PCMU" + + class Format(object): + MKA = "mka" + MKV = "mkv" + + class Status(object): + PROCESSING = "processing" + COMPLETED = "completed" + DELETED = "deleted" + FAILED = "failed" + + class Type(object): + AUDIO = "audio" + VIDEO = "video" + DATA = "data" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Recording resource. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar sid: The unique string that we created to identify the Recording resource. + :ivar source_sid: The SID of the recording source. For a Room Recording, this value is a `track_sid`. + :ivar size: The size of the recorded track, in bytes. + :ivar url: The absolute URL of the resource. + :ivar type: + :ivar duration: The duration of the recording in seconds rounded to the nearest second. Sub-second tracks have a `Duration` property of 1 second + :ivar container_format: + :ivar codec: + :ivar grouping_sids: A list of SIDs related to the recording. Includes the `room_sid` and `participant_sid`. + :ivar track_name: The name that was given to the source track of the recording. If no name is given, the `source_sid` is used. + :ivar offset: The time in milliseconds elapsed between an arbitrary point in time, common to all group rooms, and the moment when the source room of this track started. This information provides a synchronization mechanism for recordings belonging to the same room. + :ivar media_external_location: The URL of the media file associated with the recording when stored externally. See [External S3 Recordings](/docs/video/api/external-s3-recordings) for more details. + :ivar status_callback: The URL called using the `status_callback_method` to send status information on every recording event. + :ivar status_callback_method: The HTTP method used to call `status_callback`. Can be: `POST` or `GET`, defaults to `POST`. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["RecordingInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.sid: Optional[str] = payload.get("sid") + self.source_sid: Optional[str] = payload.get("source_sid") + self.size: Optional[int] = payload.get("size") + self.url: Optional[str] = payload.get("url") + self.type: Optional["RecordingInstance.Type"] = payload.get("type") + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.container_format: Optional["RecordingInstance.Format"] = payload.get( + "container_format" + ) + self.codec: Optional["RecordingInstance.Codec"] = payload.get("codec") + self.grouping_sids: Optional[Dict[str, object]] = payload.get("grouping_sids") + self.track_name: Optional[str] = payload.get("track_name") + self.offset: Optional[int] = payload.get("offset") + self.media_external_location: Optional[str] = payload.get( + "media_external_location" + ) + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RecordingContext] = None + + @property + def _proxy(self) -> "RecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingContext for this RecordingInstance + """ + if self._context is None: + self._context = RecordingContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RecordingInstance": + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RecordingInstance": + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RecordingContext + + :param version: Version that contains the resource + :param sid: The SID of the Recording resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Recordings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RecordingInstance: + """ + Fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RecordingInstance: + """ + Asynchronous coroutine to fetch the RecordingInstance + + + :returns: The fetched RecordingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RecordingInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RecordingInstance: + """ + Build an instance of RecordingInstance + + :param payload: Payload response from the API + """ + return RecordingInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RecordingList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RecordingList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Recordings" + + def stream( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RecordingInstance]: + """ + Streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RecordingInstance.Status" status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param List[str] grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param "RecordingInstance.Type" media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + source_sid=source_sid, + grouping_sid=grouping_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + media_type=media_type, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RecordingInstance]: + """ + Asynchronously streams RecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RecordingInstance.Status" status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param List[str] grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param "RecordingInstance.Type" media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + source_sid=source_sid, + grouping_sid=grouping_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + media_type=media_type, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RecordingInstance.Status" status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param List[str] grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param "RecordingInstance.Type" media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + source_sid=source_sid, + grouping_sid=grouping_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + media_type=media_type, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RecordingInstance]: + """ + Asynchronously lists RecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RecordingInstance.Status" status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param List[str] grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param datetime date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param "RecordingInstance.Type" media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + source_sid=source_sid, + grouping_sid=grouping_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + media_type=media_type, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param source_sid: Read only the recordings that have this `source_sid`. + :param grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "Status": status, + "SourceSid": source_sid, + "GroupingSid": serialize.map(grouping_sid, lambda e: e), + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "MediaType": media_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RecordingPage(self._version, response) + + async def page_async( + self, + status: Union["RecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + grouping_sid: Union[List[str], object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + media_type: Union["RecordingInstance.Type", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RecordingPage: + """ + Asynchronously retrieve a single page of RecordingInstance records from the API. + Request is executed immediately + + :param status: Read only the recordings that have this status. Can be: `processing`, `completed`, or `deleted`. + :param source_sid: Read only the recordings that have this `source_sid`. + :param grouping_sid: Read only recordings with this `grouping_sid`, which may include a `participant_sid` and/or a `room_sid`. + :param date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone. + :param date_created_before: Read only recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date-time with time zone, given as `YYYY-MM-DDThh:mm:ss+|-hh:mm` or `YYYY-MM-DDThh:mm:ssZ`. + :param media_type: Read only recordings that have this media type. Can be either `audio` or `video`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RecordingInstance + """ + data = values.of( + { + "Status": status, + "SourceSid": source_sid, + "GroupingSid": serialize.map(grouping_sid, lambda e: e), + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "MediaType": media_type, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RecordingPage(self._version, response) + + def get_page(self, target_url: str) -> RecordingPage: + """ + Retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RecordingPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RecordingPage: + """ + Asynchronously retrieve a specific page of RecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RecordingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RecordingPage(self._version, response) + + def get(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The SID of the Recording resource to fetch. + """ + return RecordingContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RecordingContext: + """ + Constructs a RecordingContext + + :param sid: The SID of the Recording resource to fetch. + """ + return RecordingContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording_settings.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording_settings.py new file mode 100644 index 0000000..3895b9d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/recording_settings.py @@ -0,0 +1,314 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class RecordingSettingsInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the RecordingSettings resource. + :ivar friendly_name: The string that you assigned to describe the resource and show the user in the console + :ivar aws_credentials_sid: The SID of the stored Credential resource. + :ivar aws_s3_url: The URL of the AWS S3 bucket where the recordings are stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/recordings`, where `recordings` is the path in which you want the recordings to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :ivar aws_storage_enabled: Whether all recordings are written to the `aws_s3_url`. When `false`, all recordings are stored in our cloud. + :ivar encryption_key_sid: The SID of the Public Key resource used for encryption. + :ivar encryption_enabled: Whether all recordings are stored in an encrypted form. The default is `false`. + :ivar url: The absolute URL of the resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.aws_credentials_sid: Optional[str] = payload.get("aws_credentials_sid") + self.aws_s3_url: Optional[str] = payload.get("aws_s3_url") + self.aws_storage_enabled: Optional[bool] = payload.get("aws_storage_enabled") + self.encryption_key_sid: Optional[str] = payload.get("encryption_key_sid") + self.encryption_enabled: Optional[bool] = payload.get("encryption_enabled") + self.url: Optional[str] = payload.get("url") + + self._context: Optional[RecordingSettingsContext] = None + + @property + def _proxy(self) -> "RecordingSettingsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RecordingSettingsContext for this RecordingSettingsInstance + """ + if self._context is None: + self._context = RecordingSettingsContext( + self._version, + ) + return self._context + + def create( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> "RecordingSettingsInstance": + """ + Create the RecordingSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and be shown to users in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the recordings should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/recordings`, where `recordings` is the path in which you want the recordings to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all recordings should be written to the `aws_s3_url`. When `false`, all recordings are stored in our cloud. + :param encryption_enabled: Whether all recordings should be stored in an encrypted form. The default is `false`. + + :returns: The created RecordingSettingsInstance + """ + return self._proxy.create( + friendly_name, + aws_credentials_sid=aws_credentials_sid, + encryption_key_sid=encryption_key_sid, + aws_s3_url=aws_s3_url, + aws_storage_enabled=aws_storage_enabled, + encryption_enabled=encryption_enabled, + ) + + async def create_async( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> "RecordingSettingsInstance": + """ + Asynchronous coroutine to create the RecordingSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and be shown to users in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the recordings should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/recordings`, where `recordings` is the path in which you want the recordings to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all recordings should be written to the `aws_s3_url`. When `false`, all recordings are stored in our cloud. + :param encryption_enabled: Whether all recordings should be stored in an encrypted form. The default is `false`. + + :returns: The created RecordingSettingsInstance + """ + return await self._proxy.create_async( + friendly_name, + aws_credentials_sid=aws_credentials_sid, + encryption_key_sid=encryption_key_sid, + aws_s3_url=aws_s3_url, + aws_storage_enabled=aws_storage_enabled, + encryption_enabled=encryption_enabled, + ) + + def fetch(self) -> "RecordingSettingsInstance": + """ + Fetch the RecordingSettingsInstance + + + :returns: The fetched RecordingSettingsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RecordingSettingsInstance": + """ + Asynchronous coroutine to fetch the RecordingSettingsInstance + + + :returns: The fetched RecordingSettingsInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class RecordingSettingsContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the RecordingSettingsContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/RecordingSettings/Default" + + def create( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> RecordingSettingsInstance: + """ + Create the RecordingSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and be shown to users in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the recordings should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/recordings`, where `recordings` is the path in which you want the recordings to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all recordings should be written to the `aws_s3_url`. When `false`, all recordings are stored in our cloud. + :param encryption_enabled: Whether all recordings should be stored in an encrypted form. The default is `false`. + + :returns: The created RecordingSettingsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AwsCredentialsSid": aws_credentials_sid, + "EncryptionKeySid": encryption_key_sid, + "AwsS3Url": aws_s3_url, + "AwsStorageEnabled": serialize.boolean_to_string(aws_storage_enabled), + "EncryptionEnabled": serialize.boolean_to_string(encryption_enabled), + } + ) + + payload = self._version.create(method="POST", uri=self._uri, data=data) + + return RecordingSettingsInstance(self._version, payload) + + async def create_async( + self, + friendly_name: str, + aws_credentials_sid: Union[str, object] = values.unset, + encryption_key_sid: Union[str, object] = values.unset, + aws_s3_url: Union[str, object] = values.unset, + aws_storage_enabled: Union[bool, object] = values.unset, + encryption_enabled: Union[bool, object] = values.unset, + ) -> RecordingSettingsInstance: + """ + Asynchronous coroutine to create the RecordingSettingsInstance + + :param friendly_name: A descriptive string that you create to describe the resource and be shown to users in the console + :param aws_credentials_sid: The SID of the stored Credential resource. + :param encryption_key_sid: The SID of the Public Key resource to use for encryption. + :param aws_s3_url: The URL of the AWS S3 bucket where the recordings should be stored. We only support DNS-compliant URLs like `https://documentation-example-twilio-bucket/recordings`, where `recordings` is the path in which you want the recordings to be stored. This URL accepts only URI-valid characters, as described in the [RFC 3986](https://tools.ietf.org/html/rfc3986#section-2). + :param aws_storage_enabled: Whether all recordings should be written to the `aws_s3_url`. When `false`, all recordings are stored in our cloud. + :param encryption_enabled: Whether all recordings should be stored in an encrypted form. The default is `false`. + + :returns: The created RecordingSettingsInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "AwsCredentialsSid": aws_credentials_sid, + "EncryptionKeySid": encryption_key_sid, + "AwsS3Url": aws_s3_url, + "AwsStorageEnabled": serialize.boolean_to_string(aws_storage_enabled), + "EncryptionEnabled": serialize.boolean_to_string(encryption_enabled), + } + ) + + payload = await self._version.create_async( + method="POST", uri=self._uri, data=data + ) + + return RecordingSettingsInstance(self._version, payload) + + def fetch(self) -> RecordingSettingsInstance: + """ + Fetch the RecordingSettingsInstance + + + :returns: The fetched RecordingSettingsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RecordingSettingsInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> RecordingSettingsInstance: + """ + Asynchronous coroutine to fetch the RecordingSettingsInstance + + + :returns: The fetched RecordingSettingsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RecordingSettingsInstance( + self._version, + payload, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class RecordingSettingsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RecordingSettingsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> RecordingSettingsContext: + """ + Constructs a RecordingSettingsContext + + """ + return RecordingSettingsContext(self._version) + + def __call__(self) -> RecordingSettingsContext: + """ + Constructs a RecordingSettingsContext + + """ + return RecordingSettingsContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__init__.py new file mode 100644 index 0000000..a8ffdb8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__init__.py @@ -0,0 +1,819 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.video.v1.room.participant import ParticipantList +from twilio.rest.video.v1.room.recording_rules import RecordingRulesList +from twilio.rest.video.v1.room.room_recording import RoomRecordingList + + +class RoomInstance(InstanceResource): + + class RoomStatus(object): + IN_PROGRESS = "in-progress" + COMPLETED = "completed" + FAILED = "failed" + + class RoomType(object): + GO = "go" + PEER_TO_PEER = "peer-to-peer" + GROUP = "group" + GROUP_SMALL = "group-small" + + class VideoCodec(object): + VP8 = "VP8" + H264 = "H264" + + """ + :ivar sid: The unique string that we created to identify the Room resource. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Room resource. + :ivar enable_turn: Deprecated, now always considered to be true. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used as a `room_sid` in place of the resource's `sid` in the URL to address the resource, assuming it does not contain any [reserved characters](https://tools.ietf.org/html/rfc3986#section-2.2) that would need to be URL encoded. This value is unique for `in-progress` rooms. SDK clients can use this name to connect to the room. REST API clients can use this name in place of the Room SID to interact with the room as long as the room is `in-progress`. + :ivar status_callback: The URL we call using the `status_callback_method` to send status information to your application on every room event. See [Status Callbacks](https://www.twilio.com/docs/video/api/status-callbacks) for more info. + :ivar status_callback_method: The HTTP method we use to call `status_callback`. Can be `POST` or `GET` and defaults to `POST`. + :ivar end_time: The UTC end time of the room in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :ivar duration: The duration of the room in seconds. + :ivar type: + :ivar max_participants: The maximum number of concurrent Participants allowed in the room. + :ivar max_participant_duration: The maximum number of seconds a Participant can be connected to the room. The maximum possible value is 86400 seconds (24 hours). The default is 14400 seconds (4 hours). + :ivar max_concurrent_published_tracks: The maximum number of published audio, video, and data tracks all participants combined are allowed to publish in the room at the same time. Check [Programmable Video Limits](https://www.twilio.com/docs/video/programmable-video-limits) for more details. If it is set to 0 it means unconstrained. + :ivar record_participants_on_connect: Whether to start recording when Participants connect. ***This feature is not available in `peer-to-peer` rooms.*** + :ivar video_codecs: An array of the video codecs that are supported when publishing a track in the room. Can be: `VP8` and `H264`. ***This feature is not available in `peer-to-peer` rooms*** + :ivar media_region: The region for the media server in Group Rooms. Can be: one of the [available Media Regions](https://www.twilio.com/docs/video/ip-addresses#media-servers). ***This feature is not available in `peer-to-peer` rooms.*** + :ivar audio_only: When set to true, indicates that the participants in the room will only publish audio. No video tracks will be allowed. Group rooms only. + :ivar empty_room_timeout: Specifies how long (in minutes) a room will remain active after last participant leaves. Can be configured when creating a room via REST API. For Ad-Hoc rooms this value cannot be changed. + :ivar unused_room_timeout: Specifies how long (in minutes) a room will remain active if no one joins. Can be configured when creating a room via REST API. For Ad-Hoc rooms this value cannot be changed. + :ivar large_room: Indicates if this is a large room. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.status: Optional["RoomInstance.RoomStatus"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.account_sid: Optional[str] = payload.get("account_sid") + self.enable_turn: Optional[bool] = payload.get("enable_turn") + self.unique_name: Optional[str] = payload.get("unique_name") + self.status_callback: Optional[str] = payload.get("status_callback") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.type: Optional["RoomInstance.RoomType"] = payload.get("type") + self.max_participants: Optional[int] = deserialize.integer( + payload.get("max_participants") + ) + self.max_participant_duration: Optional[int] = deserialize.integer( + payload.get("max_participant_duration") + ) + self.max_concurrent_published_tracks: Optional[int] = deserialize.integer( + payload.get("max_concurrent_published_tracks") + ) + self.record_participants_on_connect: Optional[bool] = payload.get( + "record_participants_on_connect" + ) + self.video_codecs: Optional[List["RoomInstance.VideoCodec"]] = payload.get( + "video_codecs" + ) + self.media_region: Optional[str] = payload.get("media_region") + self.audio_only: Optional[bool] = payload.get("audio_only") + self.empty_room_timeout: Optional[int] = deserialize.integer( + payload.get("empty_room_timeout") + ) + self.unused_room_timeout: Optional[int] = deserialize.integer( + payload.get("unused_room_timeout") + ) + self.large_room: Optional[bool] = payload.get("large_room") + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RoomContext] = None + + @property + def _proxy(self) -> "RoomContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoomContext for this RoomInstance + """ + if self._context is None: + self._context = RoomContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "RoomInstance": + """ + Fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoomInstance": + """ + Asynchronous coroutine to fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + return await self._proxy.fetch_async() + + def update(self, status: "RoomInstance.RoomStatus") -> "RoomInstance": + """ + Update the RoomInstance + + :param status: + + :returns: The updated RoomInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async(self, status: "RoomInstance.RoomStatus") -> "RoomInstance": + """ + Asynchronous coroutine to update the RoomInstance + + :param status: + + :returns: The updated RoomInstance + """ + return await self._proxy.update_async( + status=status, + ) + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + return self._proxy.participants + + @property + def recording_rules(self) -> RecordingRulesList: + """ + Access the recording_rules + """ + return self._proxy.recording_rules + + @property + def recordings(self) -> RoomRecordingList: + """ + Access the recordings + """ + return self._proxy.recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RoomContext + + :param version: Version that contains the resource + :param sid: The SID of the Room resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Rooms/{sid}".format(**self._solution) + + self._participants: Optional[ParticipantList] = None + self._recording_rules: Optional[RecordingRulesList] = None + self._recordings: Optional[RoomRecordingList] = None + + def fetch(self) -> RoomInstance: + """ + Fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoomInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoomInstance: + """ + Asynchronous coroutine to fetch the RoomInstance + + + :returns: The fetched RoomInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoomInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, status: "RoomInstance.RoomStatus") -> RoomInstance: + """ + Update the RoomInstance + + :param status: + + :returns: The updated RoomInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RoomInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async(self, status: "RoomInstance.RoomStatus") -> RoomInstance: + """ + Asynchronous coroutine to update the RoomInstance + + :param status: + + :returns: The updated RoomInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoomInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def participants(self) -> ParticipantList: + """ + Access the participants + """ + if self._participants is None: + self._participants = ParticipantList( + self._version, + self._solution["sid"], + ) + return self._participants + + @property + def recording_rules(self) -> RecordingRulesList: + """ + Access the recording_rules + """ + if self._recording_rules is None: + self._recording_rules = RecordingRulesList( + self._version, + self._solution["sid"], + ) + return self._recording_rules + + @property + def recordings(self) -> RoomRecordingList: + """ + Access the recordings + """ + if self._recordings is None: + self._recordings = RoomRecordingList( + self._version, + self._solution["sid"], + ) + return self._recordings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoomInstance: + """ + Build an instance of RoomInstance + + :param payload: Payload response from the API + """ + return RoomInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoomList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RoomList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Rooms" + + def create( + self, + enable_turn: Union[bool, object] = values.unset, + type: Union["RoomInstance.RoomType", object] = values.unset, + unique_name: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + max_participants: Union[int, object] = values.unset, + record_participants_on_connect: Union[bool, object] = values.unset, + video_codecs: Union[List["RoomInstance.VideoCodec"], object] = values.unset, + media_region: Union[str, object] = values.unset, + recording_rules: Union[object, object] = values.unset, + audio_only: Union[bool, object] = values.unset, + max_participant_duration: Union[int, object] = values.unset, + empty_room_timeout: Union[int, object] = values.unset, + unused_room_timeout: Union[int, object] = values.unset, + large_room: Union[bool, object] = values.unset, + ) -> RoomInstance: + """ + Create the RoomInstance + + :param enable_turn: Deprecated, now always considered to be true. + :param type: + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used as a `room_sid` in place of the resource's `sid` in the URL to address the resource, assuming it does not contain any [reserved characters](https://tools.ietf.org/html/rfc3986#section-2.2) that would need to be URL encoded. This value is unique for `in-progress` rooms. SDK clients can use this name to connect to the room. REST API clients can use this name in place of the Room SID to interact with the room as long as the room is `in-progress`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every room event. See [Status Callbacks](https://www.twilio.com/docs/video/api/status-callbacks) for more info. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be `POST` or `GET`. + :param max_participants: The maximum number of concurrent Participants allowed in the room. Peer-to-peer rooms can have up to 10 Participants. Small Group rooms can have up to 4 Participants. Group rooms can have up to 50 Participants. + :param record_participants_on_connect: Whether to start recording when Participants connect. ***This feature is not available in `peer-to-peer` rooms.*** + :param video_codecs: An array of the video codecs that are supported when publishing a track in the room. Can be: `VP8` and `H264`. ***This feature is not available in `peer-to-peer` rooms*** + :param media_region: The region for the media server in Group Rooms. Can be: one of the [available Media Regions](https://www.twilio.com/docs/video/ip-addresses#group-rooms-media-servers). ***This feature is not available in `peer-to-peer` rooms.*** + :param recording_rules: A collection of Recording Rules that describe how to include or exclude matching tracks for recording + :param audio_only: When set to true, indicates that the participants in the room will only publish audio. No video tracks will be allowed. Group rooms only. + :param max_participant_duration: The maximum number of seconds a Participant can be connected to the room. The maximum possible value is 86400 seconds (24 hours). The default is 14400 seconds (4 hours). + :param empty_room_timeout: Configures how long (in minutes) a room will remain active after last participant leaves. Valid values range from 1 to 60 minutes (no fractions). + :param unused_room_timeout: Configures how long (in minutes) a room will remain active if no one joins. Valid values range from 1 to 60 minutes (no fractions). + :param large_room: When set to true, indicated that this is the large room. + + :returns: The created RoomInstance + """ + + data = values.of( + { + "EnableTurn": serialize.boolean_to_string(enable_turn), + "Type": type, + "UniqueName": unique_name, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "MaxParticipants": max_participants, + "RecordParticipantsOnConnect": serialize.boolean_to_string( + record_participants_on_connect + ), + "VideoCodecs": serialize.map(video_codecs, lambda e: e), + "MediaRegion": media_region, + "RecordingRules": serialize.object(recording_rules), + "AudioOnly": serialize.boolean_to_string(audio_only), + "MaxParticipantDuration": max_participant_duration, + "EmptyRoomTimeout": empty_room_timeout, + "UnusedRoomTimeout": unused_room_timeout, + "LargeRoom": serialize.boolean_to_string(large_room), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RoomInstance(self._version, payload) + + async def create_async( + self, + enable_turn: Union[bool, object] = values.unset, + type: Union["RoomInstance.RoomType", object] = values.unset, + unique_name: Union[str, object] = values.unset, + status_callback: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + max_participants: Union[int, object] = values.unset, + record_participants_on_connect: Union[bool, object] = values.unset, + video_codecs: Union[List["RoomInstance.VideoCodec"], object] = values.unset, + media_region: Union[str, object] = values.unset, + recording_rules: Union[object, object] = values.unset, + audio_only: Union[bool, object] = values.unset, + max_participant_duration: Union[int, object] = values.unset, + empty_room_timeout: Union[int, object] = values.unset, + unused_room_timeout: Union[int, object] = values.unset, + large_room: Union[bool, object] = values.unset, + ) -> RoomInstance: + """ + Asynchronously create the RoomInstance + + :param enable_turn: Deprecated, now always considered to be true. + :param type: + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used as a `room_sid` in place of the resource's `sid` in the URL to address the resource, assuming it does not contain any [reserved characters](https://tools.ietf.org/html/rfc3986#section-2.2) that would need to be URL encoded. This value is unique for `in-progress` rooms. SDK clients can use this name to connect to the room. REST API clients can use this name in place of the Room SID to interact with the room as long as the room is `in-progress`. + :param status_callback: The URL we should call using the `status_callback_method` to send status information to your application on every room event. See [Status Callbacks](https://www.twilio.com/docs/video/api/status-callbacks) for more info. + :param status_callback_method: The HTTP method we should use to call `status_callback`. Can be `POST` or `GET`. + :param max_participants: The maximum number of concurrent Participants allowed in the room. Peer-to-peer rooms can have up to 10 Participants. Small Group rooms can have up to 4 Participants. Group rooms can have up to 50 Participants. + :param record_participants_on_connect: Whether to start recording when Participants connect. ***This feature is not available in `peer-to-peer` rooms.*** + :param video_codecs: An array of the video codecs that are supported when publishing a track in the room. Can be: `VP8` and `H264`. ***This feature is not available in `peer-to-peer` rooms*** + :param media_region: The region for the media server in Group Rooms. Can be: one of the [available Media Regions](https://www.twilio.com/docs/video/ip-addresses#group-rooms-media-servers). ***This feature is not available in `peer-to-peer` rooms.*** + :param recording_rules: A collection of Recording Rules that describe how to include or exclude matching tracks for recording + :param audio_only: When set to true, indicates that the participants in the room will only publish audio. No video tracks will be allowed. Group rooms only. + :param max_participant_duration: The maximum number of seconds a Participant can be connected to the room. The maximum possible value is 86400 seconds (24 hours). The default is 14400 seconds (4 hours). + :param empty_room_timeout: Configures how long (in minutes) a room will remain active after last participant leaves. Valid values range from 1 to 60 minutes (no fractions). + :param unused_room_timeout: Configures how long (in minutes) a room will remain active if no one joins. Valid values range from 1 to 60 minutes (no fractions). + :param large_room: When set to true, indicated that this is the large room. + + :returns: The created RoomInstance + """ + + data = values.of( + { + "EnableTurn": serialize.boolean_to_string(enable_turn), + "Type": type, + "UniqueName": unique_name, + "StatusCallback": status_callback, + "StatusCallbackMethod": status_callback_method, + "MaxParticipants": max_participants, + "RecordParticipantsOnConnect": serialize.boolean_to_string( + record_participants_on_connect + ), + "VideoCodecs": serialize.map(video_codecs, lambda e: e), + "MediaRegion": media_region, + "RecordingRules": serialize.object(recording_rules), + "AudioOnly": serialize.boolean_to_string(audio_only), + "MaxParticipantDuration": max_participant_duration, + "EmptyRoomTimeout": empty_room_timeout, + "UnusedRoomTimeout": unused_room_timeout, + "LargeRoom": serialize.boolean_to_string(large_room), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RoomInstance(self._version, payload) + + def stream( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoomInstance]: + """ + Streams RoomInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RoomInstance.RoomStatus" status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param str unique_name: Read only rooms with the this `unique_name`. + :param datetime date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param datetime date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + unique_name=unique_name, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoomInstance]: + """ + Asynchronously streams RoomInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RoomInstance.RoomStatus" status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param str unique_name: Read only rooms with the this `unique_name`. + :param datetime date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param datetime date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + unique_name=unique_name, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomInstance]: + """ + Lists RoomInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RoomInstance.RoomStatus" status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param str unique_name: Read only rooms with the this `unique_name`. + :param datetime date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param datetime date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + unique_name=unique_name, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomInstance]: + """ + Asynchronously lists RoomInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RoomInstance.RoomStatus" status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param str unique_name: Read only rooms with the this `unique_name`. + :param datetime date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param datetime date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + unique_name=unique_name, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomPage: + """ + Retrieve a single page of RoomInstance records from the API. + Request is executed immediately + + :param status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param unique_name: Read only rooms with the this `unique_name`. + :param date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomInstance + """ + data = values.of( + { + "Status": status, + "UniqueName": unique_name, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RoomPage(self._version, response) + + async def page_async( + self, + status: Union["RoomInstance.RoomStatus", object] = values.unset, + unique_name: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomPage: + """ + Asynchronously retrieve a single page of RoomInstance records from the API. + Request is executed immediately + + :param status: Read only the rooms with this status. Can be: `in-progress` (default) or `completed` + :param unique_name: Read only rooms with the this `unique_name`. + :param date_created_after: Read only rooms that started on or after this date, given as `YYYY-MM-DD`. + :param date_created_before: Read only rooms that started before this date, given as `YYYY-MM-DD`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomInstance + """ + data = values.of( + { + "Status": status, + "UniqueName": unique_name, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RoomPage(self._version, response) + + def get_page(self, target_url: str) -> RoomPage: + """ + Retrieve a specific page of RoomInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RoomPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RoomPage: + """ + Asynchronously retrieve a specific page of RoomInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RoomPage(self._version, response) + + def get(self, sid: str) -> RoomContext: + """ + Constructs a RoomContext + + :param sid: The SID of the Room resource to update. + """ + return RoomContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RoomContext: + """ + Constructs a RoomContext + + :param sid: The SID of the Room resource to update. + """ + return RoomContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..58ab821 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/recording_rules.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/recording_rules.cpython-310.pyc new file mode 100644 index 0000000..86fd722 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/recording_rules.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/room_recording.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/room_recording.cpython-310.pyc new file mode 100644 index 0000000..3d2b523 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/__pycache__/room_recording.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__init__.py new file mode 100644 index 0000000..145d954 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__init__.py @@ -0,0 +1,694 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.video.v1.room.participant.anonymize import AnonymizeList +from twilio.rest.video.v1.room.participant.published_track import PublishedTrackList +from twilio.rest.video.v1.room.participant.subscribe_rules import SubscribeRulesList +from twilio.rest.video.v1.room.participant.subscribed_track import SubscribedTrackList + + +class ParticipantInstance(InstanceResource): + + class Status(object): + CONNECTED = "connected" + DISCONNECTED = "disconnected" + + """ + :ivar sid: The unique string that we created to identify the RoomParticipant resource. + :ivar room_sid: The SID of the participant's room. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the RoomParticipant resource. + :ivar status: + :ivar identity: The application-defined string that uniquely identifies the resource's User within a Room. If a client joins with an existing Identity, the existing client is disconnected. See [access tokens](https://www.twilio.com/docs/video/tutorials/user-identity-access-tokens) and [limits](https://www.twilio.com/docs/video/programmable-video-limits) for more info. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar start_time: The time of participant connected to the room in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :ivar end_time: The time when the participant disconnected from the room in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :ivar duration: The duration in seconds that the participant was `connected`. Populated only after the participant is `disconnected`. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["ParticipantInstance.Status"] = payload.get("status") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "room_sid": room_sid, + "sid": sid or self.sid, + } + self._context: Optional[ParticipantContext] = None + + @property + def _proxy(self) -> "ParticipantContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ParticipantContext for this ParticipantInstance + """ + if self._context is None: + self._context = ParticipantContext( + self._version, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "ParticipantInstance": + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ParticipantInstance": + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + return await self._proxy.fetch_async() + + def update( + self, status: Union["ParticipantInstance.Status", object] = values.unset + ) -> "ParticipantInstance": + """ + Update the ParticipantInstance + + :param status: + + :returns: The updated ParticipantInstance + """ + return self._proxy.update( + status=status, + ) + + async def update_async( + self, status: Union["ParticipantInstance.Status", object] = values.unset + ) -> "ParticipantInstance": + """ + Asynchronous coroutine to update the ParticipantInstance + + :param status: + + :returns: The updated ParticipantInstance + """ + return await self._proxy.update_async( + status=status, + ) + + @property + def anonymize(self) -> AnonymizeList: + """ + Access the anonymize + """ + return self._proxy.anonymize + + @property + def published_tracks(self) -> PublishedTrackList: + """ + Access the published_tracks + """ + return self._proxy.published_tracks + + @property + def subscribe_rules(self) -> SubscribeRulesList: + """ + Access the subscribe_rules + """ + return self._proxy.subscribe_rules + + @property + def subscribed_tracks(self) -> SubscribedTrackList: + """ + Access the subscribed_tracks + """ + return self._proxy.subscribed_tracks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, sid: str): + """ + Initialize the ParticipantContext + + :param version: Version that contains the resource + :param room_sid: The SID of the room with the participant to update. + :param sid: The SID of the RoomParticipant resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "sid": sid, + } + self._uri = "/Rooms/{room_sid}/Participants/{sid}".format(**self._solution) + + self._anonymize: Optional[AnonymizeList] = None + self._published_tracks: Optional[PublishedTrackList] = None + self._subscribe_rules: Optional[SubscribeRulesList] = None + self._subscribed_tracks: Optional[SubscribedTrackList] = None + + def fetch(self) -> ParticipantInstance: + """ + Fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ParticipantInstance: + """ + Asynchronous coroutine to fetch the ParticipantInstance + + + :returns: The fetched ParticipantInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + def update( + self, status: Union["ParticipantInstance.Status", object] = values.unset + ) -> ParticipantInstance: + """ + Update the ParticipantInstance + + :param status: + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, status: Union["ParticipantInstance.Status", object] = values.unset + ) -> ParticipantInstance: + """ + Asynchronous coroutine to update the ParticipantInstance + + :param status: + + :returns: The updated ParticipantInstance + """ + data = values.of( + { + "Status": status, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ParticipantInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + @property + def anonymize(self) -> AnonymizeList: + """ + Access the anonymize + """ + if self._anonymize is None: + self._anonymize = AnonymizeList( + self._version, + self._solution["room_sid"], + self._solution["sid"], + ) + return self._anonymize + + @property + def published_tracks(self) -> PublishedTrackList: + """ + Access the published_tracks + """ + if self._published_tracks is None: + self._published_tracks = PublishedTrackList( + self._version, + self._solution["room_sid"], + self._solution["sid"], + ) + return self._published_tracks + + @property + def subscribe_rules(self) -> SubscribeRulesList: + """ + Access the subscribe_rules + """ + if self._subscribe_rules is None: + self._subscribe_rules = SubscribeRulesList( + self._version, + self._solution["room_sid"], + self._solution["sid"], + ) + return self._subscribe_rules + + @property + def subscribed_tracks(self) -> SubscribedTrackList: + """ + Access the subscribed_tracks + """ + if self._subscribed_tracks is None: + self._subscribed_tracks = SubscribedTrackList( + self._version, + self._solution["room_sid"], + self._solution["sid"], + ) + return self._subscribed_tracks + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ParticipantPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ParticipantInstance: + """ + Build an instance of ParticipantInstance + + :param payload: Payload response from the API + """ + return ParticipantInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ParticipantList(ListResource): + + def __init__(self, version: Version, room_sid: str): + """ + Initialize the ParticipantList + + :param version: Version that contains the resource + :param room_sid: The SID of the room with the Participant resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + } + self._uri = "/Rooms/{room_sid}/Participants".format(**self._solution) + + def stream( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ParticipantInstance]: + """ + Streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ParticipantInstance.Status" status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param str identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param datetime date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param datetime date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + identity=identity, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ParticipantInstance]: + """ + Asynchronously streams ParticipantInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "ParticipantInstance.Status" status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param str identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param datetime date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param datetime date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + identity=identity, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ParticipantInstance.Status" status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param str identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param datetime date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param datetime date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + identity=identity, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ParticipantInstance]: + """ + Asynchronously lists ParticipantInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "ParticipantInstance.Status" status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param str identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param datetime date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param datetime date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + identity=identity, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "Status": status, + "Identity": identity, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ParticipantPage(self._version, response, self._solution) + + async def page_async( + self, + status: Union["ParticipantInstance.Status", object] = values.unset, + identity: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ParticipantPage: + """ + Asynchronously retrieve a single page of ParticipantInstance records from the API. + Request is executed immediately + + :param status: Read only the participants with this status. Can be: `connected` or `disconnected`. For `in-progress` Rooms the default Status is `connected`, for `completed` Rooms only `disconnected` Participants are returned. + :param identity: Read only the Participants with this [User](https://www.twilio.com/docs/chat/rest/user-resource) `identity` value. + :param date_created_after: Read only Participants that started after this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param date_created_before: Read only Participants that started before this date in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ParticipantInstance + """ + data = values.of( + { + "Status": status, + "Identity": identity, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ParticipantPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ParticipantPage: + """ + Retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ParticipantPage: + """ + Asynchronously retrieve a specific page of ParticipantInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ParticipantInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ParticipantPage(self._version, response, self._solution) + + def get(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: The SID of the RoomParticipant resource to update. + """ + return ParticipantContext( + self._version, room_sid=self._solution["room_sid"], sid=sid + ) + + def __call__(self, sid: str) -> ParticipantContext: + """ + Constructs a ParticipantContext + + :param sid: The SID of the RoomParticipant resource to update. + """ + return ParticipantContext( + self._version, room_sid=self._solution["room_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..8b875e4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/anonymize.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/anonymize.cpython-310.pyc new file mode 100644 index 0000000..926f245 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/anonymize.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/published_track.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/published_track.cpython-310.pyc new file mode 100644 index 0000000..98e4e60 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/published_track.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribe_rules.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribe_rules.cpython-310.pyc new file mode 100644 index 0000000..ba93afb Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribe_rules.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribed_track.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribed_track.cpython-310.pyc new file mode 100644 index 0000000..34c2fb6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/__pycache__/subscribed_track.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/anonymize.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/anonymize.py new file mode 100644 index 0000000..e9a7f13 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/anonymize.py @@ -0,0 +1,241 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, Optional +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class AnonymizeInstance(InstanceResource): + + class Status(object): + CONNECTED = "connected" + DISCONNECTED = "disconnected" + + """ + :ivar sid: The unique string that we created to identify the RoomParticipant resource. + :ivar room_sid: The SID of the participant's room. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the RoomParticipant resource. + :ivar status: + :ivar identity: The SID of the participant. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar start_time: The time of participant connected to the room in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :ivar end_time: The time when the participant disconnected from the room in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#UTC) format. + :ivar duration: The duration in seconds that the participant was `connected`. Populated only after the participant is `disconnected`. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], room_sid: str, sid: str + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["AnonymizeInstance.Status"] = payload.get("status") + self.identity: Optional[str] = payload.get("identity") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.start_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start_time") + ) + self.end_time: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("end_time") + ) + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "room_sid": room_sid, + "sid": sid, + } + self._context: Optional[AnonymizeContext] = None + + @property + def _proxy(self) -> "AnonymizeContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: AnonymizeContext for this AnonymizeInstance + """ + if self._context is None: + self._context = AnonymizeContext( + self._version, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + return self._context + + def update(self) -> "AnonymizeInstance": + """ + Update the AnonymizeInstance + + + :returns: The updated AnonymizeInstance + """ + return self._proxy.update() + + async def update_async(self) -> "AnonymizeInstance": + """ + Asynchronous coroutine to update the AnonymizeInstance + + + :returns: The updated AnonymizeInstance + """ + return await self._proxy.update_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AnonymizeContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, sid: str): + """ + Initialize the AnonymizeContext + + :param version: Version that contains the resource + :param room_sid: The SID of the room with the participant to update. + :param sid: The SID of the RoomParticipant resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "sid": sid, + } + self._uri = "/Rooms/{room_sid}/Participants/{sid}/Anonymize".format( + **self._solution + ) + + def update(self) -> AnonymizeInstance: + """ + Update the AnonymizeInstance + + + :returns: The updated AnonymizeInstance + """ + data = values.of({}) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return AnonymizeInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + async def update_async(self) -> AnonymizeInstance: + """ + Asynchronous coroutine to update the AnonymizeInstance + + + :returns: The updated AnonymizeInstance + """ + data = values.of({}) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return AnonymizeInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class AnonymizeList(ListResource): + + def __init__(self, version: Version, room_sid: str, sid: str): + """ + Initialize the AnonymizeList + + :param version: Version that contains the resource + :param room_sid: The SID of the room with the participant to update. + :param sid: The SID of the RoomParticipant resource to update. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "sid": sid, + } + + def get(self) -> AnonymizeContext: + """ + Constructs a AnonymizeContext + + """ + return AnonymizeContext( + self._version, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + def __call__(self) -> AnonymizeContext: + """ + Constructs a AnonymizeContext + + """ + return AnonymizeContext( + self._version, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/published_track.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/published_track.py new file mode 100644 index 0000000..1fa8482 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/published_track.py @@ -0,0 +1,458 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class PublishedTrackInstance(InstanceResource): + + class Kind(object): + AUDIO = "audio" + VIDEO = "video" + DATA = "data" + + """ + :ivar sid: The unique string that we created to identify the RoomParticipantPublishedTrack resource. + :ivar participant_sid: The SID of the Participant resource with the published track. + :ivar room_sid: The SID of the Room resource where the track is published. + :ivar name: The track name. Must be no more than 128 characters, and be unique among the participant's published tracks. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar enabled: Whether the track is enabled. + :ivar kind: + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + participant_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.name: Optional[str] = payload.get("name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.enabled: Optional[bool] = payload.get("enabled") + self.kind: Optional["PublishedTrackInstance.Kind"] = payload.get("kind") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + "sid": sid or self.sid, + } + self._context: Optional[PublishedTrackContext] = None + + @property + def _proxy(self) -> "PublishedTrackContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: PublishedTrackContext for this PublishedTrackInstance + """ + if self._context is None: + self._context = PublishedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "PublishedTrackInstance": + """ + Fetch the PublishedTrackInstance + + + :returns: The fetched PublishedTrackInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "PublishedTrackInstance": + """ + Asynchronous coroutine to fetch the PublishedTrackInstance + + + :returns: The fetched PublishedTrackInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PublishedTrackContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, participant_sid: str, sid: str): + """ + Initialize the PublishedTrackContext + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource where the Track resource to fetch is published. + :param participant_sid: The SID of the Participant resource with the published track to fetch. + :param sid: The SID of the RoomParticipantPublishedTrack resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + "sid": sid, + } + self._uri = "/Rooms/{room_sid}/Participants/{participant_sid}/PublishedTracks/{sid}".format( + **self._solution + ) + + def fetch(self) -> PublishedTrackInstance: + """ + Fetch the PublishedTrackInstance + + + :returns: The fetched PublishedTrackInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return PublishedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> PublishedTrackInstance: + """ + Asynchronous coroutine to fetch the PublishedTrackInstance + + + :returns: The fetched PublishedTrackInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return PublishedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class PublishedTrackPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> PublishedTrackInstance: + """ + Build an instance of PublishedTrackInstance + + :param payload: Payload response from the API + """ + return PublishedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class PublishedTrackList(ListResource): + + def __init__(self, version: Version, room_sid: str, participant_sid: str): + """ + Initialize the PublishedTrackList + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource where the Track resources to read are published. + :param participant_sid: The SID of the Participant resource with the published tracks to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + } + self._uri = ( + "/Rooms/{room_sid}/Participants/{participant_sid}/PublishedTracks".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[PublishedTrackInstance]: + """ + Streams PublishedTrackInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[PublishedTrackInstance]: + """ + Asynchronously streams PublishedTrackInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PublishedTrackInstance]: + """ + Lists PublishedTrackInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[PublishedTrackInstance]: + """ + Asynchronously lists PublishedTrackInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PublishedTrackPage: + """ + Retrieve a single page of PublishedTrackInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PublishedTrackInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return PublishedTrackPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> PublishedTrackPage: + """ + Asynchronously retrieve a single page of PublishedTrackInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of PublishedTrackInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return PublishedTrackPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> PublishedTrackPage: + """ + Retrieve a specific page of PublishedTrackInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PublishedTrackInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return PublishedTrackPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> PublishedTrackPage: + """ + Asynchronously retrieve a specific page of PublishedTrackInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of PublishedTrackInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return PublishedTrackPage(self._version, response, self._solution) + + def get(self, sid: str) -> PublishedTrackContext: + """ + Constructs a PublishedTrackContext + + :param sid: The SID of the RoomParticipantPublishedTrack resource to fetch. + """ + return PublishedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> PublishedTrackContext: + """ + Constructs a PublishedTrackContext + + :param sid: The SID of the RoomParticipantPublishedTrack resource to fetch. + """ + return PublishedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribe_rules.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribe_rules.py new file mode 100644 index 0000000..e07d3b5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribe_rules.py @@ -0,0 +1,191 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SubscribeRulesInstance(InstanceResource): + """ + :ivar participant_sid: The SID of the Participant resource for the Subscribe Rules. + :ivar room_sid: The SID of the Room resource for the Subscribe Rules + :ivar rules: A collection of Subscribe Rules that describe how to include or exclude matching tracks. See the [Specifying Subscribe Rules](https://www.twilio.com/docs/video/api/track-subscriptions#specifying-sr) section for further information. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + participant_sid: str, + ): + super().__init__(version) + + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.rules: Optional[List[str]] = payload.get("rules") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscribeRulesList(ListResource): + + def __init__(self, version: Version, room_sid: str, participant_sid: str): + """ + Initialize the SubscribeRulesList + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource where the subscribe rules to update apply. + :param participant_sid: The SID of the Participant resource to update the Subscribe Rules. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + } + self._uri = ( + "/Rooms/{room_sid}/Participants/{participant_sid}/SubscribeRules".format( + **self._solution + ) + ) + + def fetch(self) -> SubscribeRulesInstance: + """ + Asynchronously fetch the SubscribeRulesInstance + + + :returns: The fetched SubscribeRulesInstance + """ + + payload = self._version.fetch(method="GET", uri=self._uri) + + return SubscribeRulesInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + async def fetch_async(self) -> SubscribeRulesInstance: + """ + Asynchronously fetch the SubscribeRulesInstance + + + :returns: The fetched SubscribeRulesInstance + """ + + payload = await self._version.fetch_async(method="GET", uri=self._uri) + + return SubscribeRulesInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def update( + self, rules: Union[object, object] = values.unset + ) -> SubscribeRulesInstance: + """ + Update the SubscribeRulesInstance + + :param rules: A JSON-encoded array of subscribe rules. See the [Specifying Subscribe Rules](https://www.twilio.com/docs/video/api/track-subscriptions#specifying-sr) section for further information. + + :returns: The created SubscribeRulesInstance + """ + + data = values.of( + { + "Rules": serialize.object(rules), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribeRulesInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + async def update_async( + self, rules: Union[object, object] = values.unset + ) -> SubscribeRulesInstance: + """ + Asynchronously update the SubscribeRulesInstance + + :param rules: A JSON-encoded array of subscribe rules. See the [Specifying Subscribe Rules](https://www.twilio.com/docs/video/api/track-subscriptions#specifying-sr) section for further information. + + :returns: The created SubscribeRulesInstance + """ + + data = values.of( + { + "Rules": serialize.object(rules), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SubscribeRulesInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribed_track.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribed_track.py new file mode 100644 index 0000000..7c91509 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/participant/subscribed_track.py @@ -0,0 +1,460 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SubscribedTrackInstance(InstanceResource): + + class Kind(object): + AUDIO = "audio" + VIDEO = "video" + DATA = "data" + + """ + :ivar sid: The unique string that we created to identify the RoomParticipantSubscribedTrack resource. + :ivar participant_sid: The SID of the participant that subscribes to the track. + :ivar publisher_sid: The SID of the participant that publishes the track. + :ivar room_sid: The SID of the room where the track is published. + :ivar name: The track name. Must have no more than 128 characters and be unique among the participant's published tracks. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar enabled: Whether the track is enabled. + :ivar kind: + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + participant_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.participant_sid: Optional[str] = payload.get("participant_sid") + self.publisher_sid: Optional[str] = payload.get("publisher_sid") + self.room_sid: Optional[str] = payload.get("room_sid") + self.name: Optional[str] = payload.get("name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.enabled: Optional[bool] = payload.get("enabled") + self.kind: Optional["SubscribedTrackInstance.Kind"] = payload.get("kind") + self.url: Optional[str] = payload.get("url") + + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + "sid": sid or self.sid, + } + self._context: Optional[SubscribedTrackContext] = None + + @property + def _proxy(self) -> "SubscribedTrackContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SubscribedTrackContext for this SubscribedTrackInstance + """ + if self._context is None: + self._context = SubscribedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + return self._context + + def fetch(self) -> "SubscribedTrackInstance": + """ + Fetch the SubscribedTrackInstance + + + :returns: The fetched SubscribedTrackInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SubscribedTrackInstance": + """ + Asynchronous coroutine to fetch the SubscribedTrackInstance + + + :returns: The fetched SubscribedTrackInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscribedTrackContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, participant_sid: str, sid: str): + """ + Initialize the SubscribedTrackContext + + :param version: Version that contains the resource + :param room_sid: The SID of the Room where the Track resource to fetch is subscribed. + :param participant_sid: The SID of the participant that subscribes to the Track resource to fetch. + :param sid: The SID of the RoomParticipantSubscribedTrack resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + "sid": sid, + } + self._uri = "/Rooms/{room_sid}/Participants/{participant_sid}/SubscribedTracks/{sid}".format( + **self._solution + ) + + def fetch(self) -> SubscribedTrackInstance: + """ + Fetch the SubscribedTrackInstance + + + :returns: The fetched SubscribedTrackInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SubscribedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SubscribedTrackInstance: + """ + Asynchronous coroutine to fetch the SubscribedTrackInstance + + + :returns: The fetched SubscribedTrackInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SubscribedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SubscribedTrackPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SubscribedTrackInstance: + """ + Build an instance of SubscribedTrackInstance + + :param payload: Payload response from the API + """ + return SubscribedTrackInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SubscribedTrackList(ListResource): + + def __init__(self, version: Version, room_sid: str, participant_sid: str): + """ + Initialize the SubscribedTrackList + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource with the Track resources to read. + :param participant_sid: The SID of the participant that subscribes to the Track resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "participant_sid": participant_sid, + } + self._uri = ( + "/Rooms/{room_sid}/Participants/{participant_sid}/SubscribedTracks".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SubscribedTrackInstance]: + """ + Streams SubscribedTrackInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SubscribedTrackInstance]: + """ + Asynchronously streams SubscribedTrackInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscribedTrackInstance]: + """ + Lists SubscribedTrackInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SubscribedTrackInstance]: + """ + Asynchronously lists SubscribedTrackInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscribedTrackPage: + """ + Retrieve a single page of SubscribedTrackInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscribedTrackInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SubscribedTrackPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SubscribedTrackPage: + """ + Asynchronously retrieve a single page of SubscribedTrackInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SubscribedTrackInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SubscribedTrackPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> SubscribedTrackPage: + """ + Retrieve a specific page of SubscribedTrackInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscribedTrackInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SubscribedTrackPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> SubscribedTrackPage: + """ + Asynchronously retrieve a specific page of SubscribedTrackInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SubscribedTrackInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SubscribedTrackPage(self._version, response, self._solution) + + def get(self, sid: str) -> SubscribedTrackContext: + """ + Constructs a SubscribedTrackContext + + :param sid: The SID of the RoomParticipantSubscribedTrack resource to fetch. + """ + return SubscribedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> SubscribedTrackContext: + """ + Constructs a SubscribedTrackContext + + :param sid: The SID of the RoomParticipantSubscribedTrack resource to fetch. + """ + return SubscribedTrackContext( + self._version, + room_sid=self._solution["room_sid"], + participant_sid=self._solution["participant_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/recording_rules.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/recording_rules.py new file mode 100644 index 0000000..c94bbf1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/recording_rules.py @@ -0,0 +1,164 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union +from twilio.base import deserialize, serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class RecordingRulesInstance(InstanceResource): + """ + :ivar room_sid: The SID of the Room resource for the Recording Rules + :ivar rules: A collection of Recording Rules that describe how to include or exclude matching tracks for recording + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], room_sid: str): + super().__init__(version) + + self.room_sid: Optional[str] = payload.get("room_sid") + self.rules: Optional[List[str]] = payload.get("rules") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + + self._solution = { + "room_sid": room_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RecordingRulesList(ListResource): + + def __init__(self, version: Version, room_sid: str): + """ + Initialize the RecordingRulesList + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource where the recording rules to update apply. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + } + self._uri = "/Rooms/{room_sid}/RecordingRules".format(**self._solution) + + def fetch(self) -> RecordingRulesInstance: + """ + Asynchronously fetch the RecordingRulesInstance + + + :returns: The fetched RecordingRulesInstance + """ + + payload = self._version.fetch(method="GET", uri=self._uri) + + return RecordingRulesInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + async def fetch_async(self) -> RecordingRulesInstance: + """ + Asynchronously fetch the RecordingRulesInstance + + + :returns: The fetched RecordingRulesInstance + """ + + payload = await self._version.fetch_async(method="GET", uri=self._uri) + + return RecordingRulesInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + def update( + self, rules: Union[object, object] = values.unset + ) -> RecordingRulesInstance: + """ + Update the RecordingRulesInstance + + :param rules: A JSON-encoded array of recording rules. + + :returns: The created RecordingRulesInstance + """ + + data = values.of( + { + "Rules": serialize.object(rules), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingRulesInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + async def update_async( + self, rules: Union[object, object] = values.unset + ) -> RecordingRulesInstance: + """ + Asynchronously update the RecordingRulesInstance + + :param rules: A JSON-encoded array of recording rules. + + :returns: The created RecordingRulesInstance + """ + + data = values.of( + { + "Rules": serialize.object(rules), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RecordingRulesInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/room_recording.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/room_recording.py new file mode 100644 index 0000000..db67f29 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/video/v1/room/room_recording.py @@ -0,0 +1,586 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Video + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RoomRecordingInstance(InstanceResource): + + class Codec(object): + VP8 = "VP8" + H264 = "H264" + OPUS = "OPUS" + PCMU = "PCMU" + + class Format(object): + MKA = "mka" + MKV = "mkv" + + class Status(object): + PROCESSING = "processing" + COMPLETED = "completed" + DELETED = "deleted" + FAILED = "failed" + + class Type(object): + AUDIO = "audio" + VIDEO = "video" + DATA = "data" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the RoomRecording resource. + :ivar status: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. + :ivar sid: The unique string that we created to identify the RoomRecording resource. + :ivar source_sid: The SID of the recording source. For a Room Recording, this value is a `track_sid`. + :ivar size: The size of the recorded track in bytes. + :ivar url: The absolute URL of the resource. + :ivar type: + :ivar duration: The duration of the recording rounded to the nearest second. Sub-second duration tracks have a `duration` of 1 second + :ivar container_format: + :ivar codec: + :ivar grouping_sids: A list of SIDs related to the Recording. Includes the `room_sid` and `participant_sid`. + :ivar track_name: The name that was given to the source track of the recording. If no name is given, the `source_sid` is used. + :ivar offset: The time in milliseconds elapsed between an arbitrary point in time, common to all group rooms, and the moment when the source room of this track started. This information provides a synchronization mechanism for recordings belonging to the same room. + :ivar media_external_location: The URL of the media file associated with the recording when stored externally. See [External S3 Recordings](/docs/video/api/external-s3-recordings) for more details. + :ivar room_sid: The SID of the Room resource the recording is associated with. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + room_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.status: Optional["RoomRecordingInstance.Status"] = payload.get("status") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.sid: Optional[str] = payload.get("sid") + self.source_sid: Optional[str] = payload.get("source_sid") + self.size: Optional[int] = payload.get("size") + self.url: Optional[str] = payload.get("url") + self.type: Optional["RoomRecordingInstance.Type"] = payload.get("type") + self.duration: Optional[int] = deserialize.integer(payload.get("duration")) + self.container_format: Optional["RoomRecordingInstance.Format"] = payload.get( + "container_format" + ) + self.codec: Optional["RoomRecordingInstance.Codec"] = payload.get("codec") + self.grouping_sids: Optional[Dict[str, object]] = payload.get("grouping_sids") + self.track_name: Optional[str] = payload.get("track_name") + self.offset: Optional[int] = payload.get("offset") + self.media_external_location: Optional[str] = payload.get( + "media_external_location" + ) + self.room_sid: Optional[str] = payload.get("room_sid") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "room_sid": room_sid, + "sid": sid or self.sid, + } + self._context: Optional[RoomRecordingContext] = None + + @property + def _proxy(self) -> "RoomRecordingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RoomRecordingContext for this RoomRecordingInstance + """ + if self._context is None: + self._context = RoomRecordingContext( + self._version, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RoomRecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoomRecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RoomRecordingInstance": + """ + Fetch the RoomRecordingInstance + + + :returns: The fetched RoomRecordingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RoomRecordingInstance": + """ + Asynchronous coroutine to fetch the RoomRecordingInstance + + + :returns: The fetched RoomRecordingInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomRecordingContext(InstanceContext): + + def __init__(self, version: Version, room_sid: str, sid: str): + """ + Initialize the RoomRecordingContext + + :param version: Version that contains the resource + :param room_sid: The SID of the Room resource with the recording to fetch. + :param sid: The SID of the RoomRecording resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + "sid": sid, + } + self._uri = "/Rooms/{room_sid}/Recordings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RoomRecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RoomRecordingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RoomRecordingInstance: + """ + Fetch the RoomRecordingInstance + + + :returns: The fetched RoomRecordingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RoomRecordingInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RoomRecordingInstance: + """ + Asynchronous coroutine to fetch the RoomRecordingInstance + + + :returns: The fetched RoomRecordingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RoomRecordingInstance( + self._version, + payload, + room_sid=self._solution["room_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RoomRecordingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RoomRecordingInstance: + """ + Build an instance of RoomRecordingInstance + + :param payload: Payload response from the API + """ + return RoomRecordingInstance( + self._version, payload, room_sid=self._solution["room_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RoomRecordingList(ListResource): + + def __init__(self, version: Version, room_sid: str): + """ + Initialize the RoomRecordingList + + :param version: Version that contains the resource + :param room_sid: The SID of the room with the RoomRecording resources to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "room_sid": room_sid, + } + self._uri = "/Rooms/{room_sid}/Recordings".format(**self._solution) + + def stream( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RoomRecordingInstance]: + """ + Streams RoomRecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RoomRecordingInstance.Status" status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + source_sid=source_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RoomRecordingInstance]: + """ + Asynchronously streams RoomRecordingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "RoomRecordingInstance.Status" status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + source_sid=source_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomRecordingInstance]: + """ + Lists RoomRecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RoomRecordingInstance.Status" status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + source_sid=source_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RoomRecordingInstance]: + """ + Asynchronously lists RoomRecordingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "RoomRecordingInstance.Status" status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param str source_sid: Read only the recordings that have this `source_sid`. + :param datetime date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param datetime date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + source_sid=source_sid, + date_created_after=date_created_after, + date_created_before=date_created_before, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomRecordingPage: + """ + Retrieve a single page of RoomRecordingInstance records from the API. + Request is executed immediately + + :param status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param source_sid: Read only the recordings that have this `source_sid`. + :param date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomRecordingInstance + """ + data = values.of( + { + "Status": status, + "SourceSid": source_sid, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RoomRecordingPage(self._version, response, self._solution) + + async def page_async( + self, + status: Union["RoomRecordingInstance.Status", object] = values.unset, + source_sid: Union[str, object] = values.unset, + date_created_after: Union[datetime, object] = values.unset, + date_created_before: Union[datetime, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RoomRecordingPage: + """ + Asynchronously retrieve a single page of RoomRecordingInstance records from the API. + Request is executed immediately + + :param status: Read only the recordings with this status. Can be: `processing`, `completed`, or `deleted`. + :param source_sid: Read only the recordings that have this `source_sid`. + :param date_created_after: Read only recordings that started on or after this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param date_created_before: Read only Recordings that started before this [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) datetime with time zone. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RoomRecordingInstance + """ + data = values.of( + { + "Status": status, + "SourceSid": source_sid, + "DateCreatedAfter": serialize.iso8601_datetime(date_created_after), + "DateCreatedBefore": serialize.iso8601_datetime(date_created_before), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RoomRecordingPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> RoomRecordingPage: + """ + Retrieve a specific page of RoomRecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomRecordingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RoomRecordingPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> RoomRecordingPage: + """ + Asynchronously retrieve a specific page of RoomRecordingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RoomRecordingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RoomRecordingPage(self._version, response, self._solution) + + def get(self, sid: str) -> RoomRecordingContext: + """ + Constructs a RoomRecordingContext + + :param sid: The SID of the RoomRecording resource to fetch. + """ + return RoomRecordingContext( + self._version, room_sid=self._solution["room_sid"], sid=sid + ) + + def __call__(self, sid: str) -> RoomRecordingContext: + """ + Constructs a RoomRecordingContext + + :param sid: The SID of the RoomRecording resource to fetch. + """ + return RoomRecordingContext( + self._version, room_sid=self._solution["room_sid"], sid=sid + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/VoiceBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/VoiceBase.py new file mode 100644 index 0000000..4394ab1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/VoiceBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.voice.v1 import V1 + + +class VoiceBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Voice Domain + + :returns: Domain for Voice + """ + super().__init__(twilio, "https://voice.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Voice + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__init__.py new file mode 100644 index 0000000..019a3a2 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__init__.py @@ -0,0 +1,65 @@ +from warnings import warn + +from twilio.rest.voice.VoiceBase import VoiceBase +from twilio.rest.voice.v1.archived_call import ArchivedCallList +from twilio.rest.voice.v1.byoc_trunk import ByocTrunkList +from twilio.rest.voice.v1.connection_policy import ConnectionPolicyList +from twilio.rest.voice.v1.dialing_permissions import DialingPermissionsList +from twilio.rest.voice.v1.ip_record import IpRecordList +from twilio.rest.voice.v1.source_ip_mapping import SourceIpMappingList + + +class Voice(VoiceBase): + @property + def archived_calls(self) -> ArchivedCallList: + warn( + "archived_calls is deprecated. Use v1.archived_calls instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.archived_calls + + @property + def byoc_trunks(self) -> ByocTrunkList: + warn( + "byoc_trunks is deprecated. Use v1.byoc_trunks instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.byoc_trunks + + @property + def connection_policies(self) -> ConnectionPolicyList: + warn( + "connection_policies is deprecated. Use v1.connection_policies instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.connection_policies + + @property + def dialing_permissions(self) -> DialingPermissionsList: + warn( + "dialing_permissions is deprecated. Use v1.dialing_permissions instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.dialing_permissions + + @property + def ip_records(self) -> IpRecordList: + warn( + "ip_records is deprecated. Use v1.ip_records instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.ip_records + + @property + def source_ip_mappings(self) -> SourceIpMappingList: + warn( + "source_ip_mappings is deprecated. Use v1.source_ip_mappings instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.source_ip_mappings diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/VoiceBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/VoiceBase.cpython-310.pyc new file mode 100644 index 0000000..4e3a54e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/VoiceBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..378c8f8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__init__.py new file mode 100644 index 0000000..75c0c7f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__init__.py @@ -0,0 +1,83 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.voice.v1.archived_call import ArchivedCallList +from twilio.rest.voice.v1.byoc_trunk import ByocTrunkList +from twilio.rest.voice.v1.connection_policy import ConnectionPolicyList +from twilio.rest.voice.v1.dialing_permissions import DialingPermissionsList +from twilio.rest.voice.v1.ip_record import IpRecordList +from twilio.rest.voice.v1.source_ip_mapping import SourceIpMappingList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Voice + + :param domain: The Twilio.voice domain + """ + super().__init__(domain, "v1") + self._archived_calls: Optional[ArchivedCallList] = None + self._byoc_trunks: Optional[ByocTrunkList] = None + self._connection_policies: Optional[ConnectionPolicyList] = None + self._dialing_permissions: Optional[DialingPermissionsList] = None + self._ip_records: Optional[IpRecordList] = None + self._source_ip_mappings: Optional[SourceIpMappingList] = None + + @property + def archived_calls(self) -> ArchivedCallList: + if self._archived_calls is None: + self._archived_calls = ArchivedCallList(self) + return self._archived_calls + + @property + def byoc_trunks(self) -> ByocTrunkList: + if self._byoc_trunks is None: + self._byoc_trunks = ByocTrunkList(self) + return self._byoc_trunks + + @property + def connection_policies(self) -> ConnectionPolicyList: + if self._connection_policies is None: + self._connection_policies = ConnectionPolicyList(self) + return self._connection_policies + + @property + def dialing_permissions(self) -> DialingPermissionsList: + if self._dialing_permissions is None: + self._dialing_permissions = DialingPermissionsList(self) + return self._dialing_permissions + + @property + def ip_records(self) -> IpRecordList: + if self._ip_records is None: + self._ip_records = IpRecordList(self) + return self._ip_records + + @property + def source_ip_mappings(self) -> SourceIpMappingList: + if self._source_ip_mappings is None: + self._source_ip_mappings = SourceIpMappingList(self) + return self._source_ip_mappings + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3be3c70 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/archived_call.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/archived_call.cpython-310.pyc new file mode 100644 index 0000000..67fcc00 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/archived_call.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/byoc_trunk.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/byoc_trunk.cpython-310.pyc new file mode 100644 index 0000000..c098004 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/byoc_trunk.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/ip_record.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/ip_record.cpython-310.pyc new file mode 100644 index 0000000..3c84cef Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/ip_record.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/source_ip_mapping.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/source_ip_mapping.cpython-310.pyc new file mode 100644 index 0000000..c058196 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/__pycache__/source_ip_mapping.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/archived_call.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/archived_call.py new file mode 100644 index 0000000..ec8a5bb --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/archived_call.py @@ -0,0 +1,110 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import date +from twilio.base.instance_context import InstanceContext + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class ArchivedCallContext(InstanceContext): + + def __init__(self, version: Version, date: date, sid: str): + """ + Initialize the ArchivedCallContext + + :param version: Version that contains the resource + :param date: The date of the Call in UTC. + :param sid: The Twilio-provided Call SID that uniquely identifies the Call resource to delete + """ + super().__init__(version) + + # Path Solution + self._solution = { + "date": date, + "sid": sid, + } + self._uri = "/Archives/{date}/Calls/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ArchivedCallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ArchivedCallInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ArchivedCallList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ArchivedCallList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self, date: date, sid: str) -> ArchivedCallContext: + """ + Constructs a ArchivedCallContext + + :param date: The date of the Call in UTC. + :param sid: The Twilio-provided Call SID that uniquely identifies the Call resource to delete + """ + return ArchivedCallContext(self._version, date=date, sid=sid) + + def __call__(self, date: date, sid: str) -> ArchivedCallContext: + """ + Constructs a ArchivedCallContext + + :param date: The date of the Call in UTC. + :param sid: The Twilio-provided Call SID that uniquely identifies the Call resource to delete + """ + return ArchivedCallContext(self._version, date=date, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/byoc_trunk.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/byoc_trunk.py new file mode 100644 index 0000000..a85cae6 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/byoc_trunk.py @@ -0,0 +1,757 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ByocTrunkInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the BYOC Trunk resource. + :ivar sid: The unique string that that we created to identify the BYOC Trunk resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar voice_url: The URL we call using the `voice_method` when the BYOC Trunk receives a call. + :ivar voice_method: The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. + :ivar voice_fallback_url: The URL that we call when an error occurs while retrieving or executing the TwiML requested from `voice_url`. + :ivar voice_fallback_method: The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :ivar status_callback_url: The URL that we call to pass status parameters (such as call ended) to your application. + :ivar status_callback_method: The HTTP method we use to call `status_callback_url`. Either `GET` or `POST`. + :ivar cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :ivar connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :ivar from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \"call back\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \"sip.twilio.com\". + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.voice_url: Optional[str] = payload.get("voice_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.status_callback_url: Optional[str] = payload.get("status_callback_url") + self.status_callback_method: Optional[str] = payload.get( + "status_callback_method" + ) + self.cnam_lookup_enabled: Optional[bool] = payload.get("cnam_lookup_enabled") + self.connection_policy_sid: Optional[str] = payload.get("connection_policy_sid") + self.from_domain_sid: Optional[str] = payload.get("from_domain_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ByocTrunkContext] = None + + @property + def _proxy(self) -> "ByocTrunkContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ByocTrunkContext for this ByocTrunkInstance + """ + if self._context is None: + self._context = ByocTrunkContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ByocTrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ByocTrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ByocTrunkInstance": + """ + Fetch the ByocTrunkInstance + + + :returns: The fetched ByocTrunkInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ByocTrunkInstance": + """ + Asynchronous coroutine to fetch the ByocTrunkInstance + + + :returns: The fetched ByocTrunkInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> "ByocTrunkInstance": + """ + Update the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The updated ByocTrunkInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + voice_url=voice_url, + voice_method=voice_method, + voice_fallback_url=voice_fallback_url, + voice_fallback_method=voice_fallback_method, + status_callback_url=status_callback_url, + status_callback_method=status_callback_method, + cnam_lookup_enabled=cnam_lookup_enabled, + connection_policy_sid=connection_policy_sid, + from_domain_sid=from_domain_sid, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> "ByocTrunkInstance": + """ + Asynchronous coroutine to update the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The updated ByocTrunkInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + voice_url=voice_url, + voice_method=voice_method, + voice_fallback_url=voice_fallback_url, + voice_fallback_method=voice_fallback_method, + status_callback_url=status_callback_url, + status_callback_method=status_callback_method, + cnam_lookup_enabled=cnam_lookup_enabled, + connection_policy_sid=connection_policy_sid, + from_domain_sid=from_domain_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ByocTrunkContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ByocTrunkContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the BYOC Trunk resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/ByocTrunks/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the ByocTrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ByocTrunkInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ByocTrunkInstance: + """ + Fetch the ByocTrunkInstance + + + :returns: The fetched ByocTrunkInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ByocTrunkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ByocTrunkInstance: + """ + Asynchronous coroutine to fetch the ByocTrunkInstance + + + :returns: The fetched ByocTrunkInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ByocTrunkInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> ByocTrunkInstance: + """ + Update the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The updated ByocTrunkInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "ConnectionPolicySid": connection_policy_sid, + "FromDomainSid": from_domain_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ByocTrunkInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> ByocTrunkInstance: + """ + Asynchronous coroutine to update the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url` + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML requested by `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The updated ByocTrunkInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "ConnectionPolicySid": connection_policy_sid, + "FromDomainSid": from_domain_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ByocTrunkInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ByocTrunkPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ByocTrunkInstance: + """ + Build an instance of ByocTrunkInstance + + :param payload: Payload response from the API + """ + return ByocTrunkInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ByocTrunkList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ByocTrunkList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ByocTrunks" + + def create( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> ByocTrunkInstance: + """ + Create the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The created ByocTrunkInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "ConnectionPolicySid": connection_policy_sid, + "FromDomainSid": from_domain_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ByocTrunkInstance(self._version, payload) + + async def create_async( + self, + friendly_name: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + status_callback_url: Union[str, object] = values.unset, + status_callback_method: Union[str, object] = values.unset, + cnam_lookup_enabled: Union[bool, object] = values.unset, + connection_policy_sid: Union[str, object] = values.unset, + from_domain_sid: Union[str, object] = values.unset, + ) -> ByocTrunkInstance: + """ + Asynchronously create the ByocTrunkInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param voice_url: The URL we should call when the BYOC Trunk receives a call. + :param voice_method: The HTTP method we should use to call `voice_url`. Can be: `GET` or `POST`. + :param voice_fallback_url: The URL that we should call when an error occurs while retrieving or executing the TwiML from `voice_url`. + :param voice_fallback_method: The HTTP method we should use to call `voice_fallback_url`. Can be: `GET` or `POST`. + :param status_callback_url: The URL that we should call to pass status parameters (such as call ended) to your application. + :param status_callback_method: The HTTP method we should use to call `status_callback_url`. Can be: `GET` or `POST`. + :param cnam_lookup_enabled: Whether Caller ID Name (CNAM) lookup is enabled for the trunk. If enabled, all inbound calls to the BYOC Trunk from the United States and Canada automatically perform a CNAM Lookup and display Caller ID data on your phone. See [CNAM Lookups](https://www.twilio.com/docs/sip-trunking#CNAM) for more information. + :param connection_policy_sid: The SID of the Connection Policy that Twilio will use when routing traffic to your communications infrastructure. + :param from_domain_sid: The SID of the SIP Domain that should be used in the `From` header of originating calls sent to your SIP infrastructure. If your SIP infrastructure allows users to \\\"call back\\\" an incoming call, configure this with a [SIP Domain](https://www.twilio.com/docs/voice/api/sending-sip) to ensure proper routing. If not configured, the from domain will default to \\\"sip.twilio.com\\\". + + :returns: The created ByocTrunkInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + "VoiceUrl": voice_url, + "VoiceMethod": voice_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceFallbackMethod": voice_fallback_method, + "StatusCallbackUrl": status_callback_url, + "StatusCallbackMethod": status_callback_method, + "CnamLookupEnabled": serialize.boolean_to_string(cnam_lookup_enabled), + "ConnectionPolicySid": connection_policy_sid, + "FromDomainSid": from_domain_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ByocTrunkInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ByocTrunkInstance]: + """ + Streams ByocTrunkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ByocTrunkInstance]: + """ + Asynchronously streams ByocTrunkInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ByocTrunkInstance]: + """ + Lists ByocTrunkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ByocTrunkInstance]: + """ + Asynchronously lists ByocTrunkInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ByocTrunkPage: + """ + Retrieve a single page of ByocTrunkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ByocTrunkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ByocTrunkPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ByocTrunkPage: + """ + Asynchronously retrieve a single page of ByocTrunkInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ByocTrunkInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ByocTrunkPage(self._version, response) + + def get_page(self, target_url: str) -> ByocTrunkPage: + """ + Retrieve a specific page of ByocTrunkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ByocTrunkInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ByocTrunkPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ByocTrunkPage: + """ + Asynchronously retrieve a specific page of ByocTrunkInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ByocTrunkInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ByocTrunkPage(self._version, response) + + def get(self, sid: str) -> ByocTrunkContext: + """ + Constructs a ByocTrunkContext + + :param sid: The Twilio-provided string that uniquely identifies the BYOC Trunk resource to update. + """ + return ByocTrunkContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ByocTrunkContext: + """ + Constructs a ByocTrunkContext + + :param sid: The Twilio-provided string that uniquely identifies the BYOC Trunk resource to update. + """ + return ByocTrunkContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__init__.py new file mode 100644 index 0000000..d1d8aac --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__init__.py @@ -0,0 +1,599 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.voice.v1.connection_policy.connection_policy_target import ( + ConnectionPolicyTargetList, +) + + +class ConnectionPolicyInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Connection Policy resource. + :ivar sid: The unique string that we created to identify the Connection Policy resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related resources. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[ConnectionPolicyContext] = None + + @property + def _proxy(self) -> "ConnectionPolicyContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConnectionPolicyContext for this ConnectionPolicyInstance + """ + if self._context is None: + self._context = ConnectionPolicyContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ConnectionPolicyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectionPolicyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ConnectionPolicyInstance": + """ + Fetch the ConnectionPolicyInstance + + + :returns: The fetched ConnectionPolicyInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConnectionPolicyInstance": + """ + Asynchronous coroutine to fetch the ConnectionPolicyInstance + + + :returns: The fetched ConnectionPolicyInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "ConnectionPolicyInstance": + """ + Update the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated ConnectionPolicyInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "ConnectionPolicyInstance": + """ + Asynchronous coroutine to update the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated ConnectionPolicyInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + @property + def targets(self) -> ConnectionPolicyTargetList: + """ + Access the targets + """ + return self._proxy.targets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectionPolicyContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the ConnectionPolicyContext + + :param version: Version that contains the resource + :param sid: The unique string that we created to identify the Connection Policy resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/ConnectionPolicies/{sid}".format(**self._solution) + + self._targets: Optional[ConnectionPolicyTargetList] = None + + def delete(self) -> bool: + """ + Deletes the ConnectionPolicyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectionPolicyInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ConnectionPolicyInstance: + """ + Fetch the ConnectionPolicyInstance + + + :returns: The fetched ConnectionPolicyInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConnectionPolicyInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConnectionPolicyInstance: + """ + Asynchronous coroutine to fetch the ConnectionPolicyInstance + + + :returns: The fetched ConnectionPolicyInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConnectionPolicyInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> ConnectionPolicyInstance: + """ + Update the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated ConnectionPolicyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> ConnectionPolicyInstance: + """ + Asynchronous coroutine to update the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated ConnectionPolicyInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyInstance( + self._version, payload, sid=self._solution["sid"] + ) + + @property + def targets(self) -> ConnectionPolicyTargetList: + """ + Access the targets + """ + if self._targets is None: + self._targets = ConnectionPolicyTargetList( + self._version, + self._solution["sid"], + ) + return self._targets + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectionPolicyPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConnectionPolicyInstance: + """ + Build an instance of ConnectionPolicyInstance + + :param payload: Payload response from the API + """ + return ConnectionPolicyInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConnectionPolicyList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the ConnectionPolicyList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/ConnectionPolicies" + + def create( + self, friendly_name: Union[str, object] = values.unset + ) -> ConnectionPolicyInstance: + """ + Create the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The created ConnectionPolicyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyInstance(self._version, payload) + + async def create_async( + self, friendly_name: Union[str, object] = values.unset + ) -> ConnectionPolicyInstance: + """ + Asynchronously create the ConnectionPolicyInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The created ConnectionPolicyInstance + """ + + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConnectionPolicyInstance]: + """ + Streams ConnectionPolicyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConnectionPolicyInstance]: + """ + Asynchronously streams ConnectionPolicyInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectionPolicyInstance]: + """ + Lists ConnectionPolicyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectionPolicyInstance]: + """ + Asynchronously lists ConnectionPolicyInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectionPolicyPage: + """ + Retrieve a single page of ConnectionPolicyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectionPolicyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConnectionPolicyPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectionPolicyPage: + """ + Asynchronously retrieve a single page of ConnectionPolicyInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectionPolicyInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConnectionPolicyPage(self._version, response) + + def get_page(self, target_url: str) -> ConnectionPolicyPage: + """ + Retrieve a specific page of ConnectionPolicyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectionPolicyInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConnectionPolicyPage(self._version, response) + + async def get_page_async(self, target_url: str) -> ConnectionPolicyPage: + """ + Asynchronously retrieve a specific page of ConnectionPolicyInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectionPolicyInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConnectionPolicyPage(self._version, response) + + def get(self, sid: str) -> ConnectionPolicyContext: + """ + Constructs a ConnectionPolicyContext + + :param sid: The unique string that we created to identify the Connection Policy resource to update. + """ + return ConnectionPolicyContext(self._version, sid=sid) + + def __call__(self, sid: str) -> ConnectionPolicyContext: + """ + Constructs a ConnectionPolicyContext + + :param sid: The unique string that we created to identify the Connection Policy resource to update. + """ + return ConnectionPolicyContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..eb95813 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/connection_policy_target.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/connection_policy_target.cpython-310.pyc new file mode 100644 index 0000000..50f91d2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/__pycache__/connection_policy_target.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/connection_policy_target.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/connection_policy_target.py new file mode 100644 index 0000000..2fa4908 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/connection_policy/connection_policy_target.py @@ -0,0 +1,706 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class ConnectionPolicyTargetInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Target resource. + :ivar connection_policy_sid: The SID of the Connection Policy that owns the Target. + :ivar sid: The unique string that we created to identify the Target resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :ivar priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important target. + :ivar weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. Targets with higher values receive more load than those with lower ones with the same priority. + :ivar enabled: Whether the target is enabled. The default is `true`. + :ivar date_created: The date and time in GMT when the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, + version: Version, + payload: Dict[str, Any], + connection_policy_sid: str, + sid: Optional[str] = None, + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.connection_policy_sid: Optional[str] = payload.get("connection_policy_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.target: Optional[str] = payload.get("target") + self.priority: Optional[int] = deserialize.integer(payload.get("priority")) + self.weight: Optional[int] = deserialize.integer(payload.get("weight")) + self.enabled: Optional[bool] = payload.get("enabled") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "connection_policy_sid": connection_policy_sid, + "sid": sid or self.sid, + } + self._context: Optional[ConnectionPolicyTargetContext] = None + + @property + def _proxy(self) -> "ConnectionPolicyTargetContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: ConnectionPolicyTargetContext for this ConnectionPolicyTargetInstance + """ + if self._context is None: + self._context = ConnectionPolicyTargetContext( + self._version, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the ConnectionPolicyTargetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectionPolicyTargetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "ConnectionPolicyTargetInstance": + """ + Fetch the ConnectionPolicyTargetInstance + + + :returns: The fetched ConnectionPolicyTargetInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "ConnectionPolicyTargetInstance": + """ + Asynchronous coroutine to fetch the ConnectionPolicyTargetInstance + + + :returns: The fetched ConnectionPolicyTargetInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + friendly_name: Union[str, object] = values.unset, + target: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> "ConnectionPolicyTargetInstance": + """ + Update the ConnectionPolicyTargetInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. + + :returns: The updated ConnectionPolicyTargetInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + target=target, + priority=priority, + weight=weight, + enabled=enabled, + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + target: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> "ConnectionPolicyTargetInstance": + """ + Asynchronous coroutine to update the ConnectionPolicyTargetInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. + + :returns: The updated ConnectionPolicyTargetInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + target=target, + priority=priority, + weight=weight, + enabled=enabled, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectionPolicyTargetContext(InstanceContext): + + def __init__(self, version: Version, connection_policy_sid: str, sid: str): + """ + Initialize the ConnectionPolicyTargetContext + + :param version: Version that contains the resource + :param connection_policy_sid: The SID of the Connection Policy that owns the Target. + :param sid: The unique string that we created to identify the Target resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "connection_policy_sid": connection_policy_sid, + "sid": sid, + } + self._uri = "/ConnectionPolicies/{connection_policy_sid}/Targets/{sid}".format( + **self._solution + ) + + def delete(self) -> bool: + """ + Deletes the ConnectionPolicyTargetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the ConnectionPolicyTargetInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> ConnectionPolicyTargetInstance: + """ + Fetch the ConnectionPolicyTargetInstance + + + :returns: The fetched ConnectionPolicyTargetInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> ConnectionPolicyTargetInstance: + """ + Asynchronous coroutine to fetch the ConnectionPolicyTargetInstance + + + :returns: The fetched ConnectionPolicyTargetInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=self._solution["sid"], + ) + + def update( + self, + friendly_name: Union[str, object] = values.unset, + target: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> ConnectionPolicyTargetInstance: + """ + Update the ConnectionPolicyTargetInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. + + :returns: The updated ConnectionPolicyTargetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Target": target, + "Priority": priority, + "Weight": weight, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=self._solution["sid"], + ) + + async def update_async( + self, + friendly_name: Union[str, object] = values.unset, + target: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> ConnectionPolicyTargetInstance: + """ + Asynchronous coroutine to update the ConnectionPolicyTargetInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. + + :returns: The updated ConnectionPolicyTargetInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + "Target": target, + "Priority": priority, + "Weight": weight, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class ConnectionPolicyTargetPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> ConnectionPolicyTargetInstance: + """ + Build an instance of ConnectionPolicyTargetInstance + + :param payload: Payload response from the API + """ + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class ConnectionPolicyTargetList(ListResource): + + def __init__(self, version: Version, connection_policy_sid: str): + """ + Initialize the ConnectionPolicyTargetList + + :param version: Version that contains the resource + :param connection_policy_sid: The SID of the Connection Policy from which to read the Targets. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "connection_policy_sid": connection_policy_sid, + } + self._uri = "/ConnectionPolicies/{connection_policy_sid}/Targets".format( + **self._solution + ) + + def create( + self, + target: str, + friendly_name: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> ConnectionPolicyTargetInstance: + """ + Create the ConnectionPolicyTargetInstance + + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. The default is `true`. + + :returns: The created ConnectionPolicyTargetInstance + """ + + data = values.of( + { + "Target": target, + "FriendlyName": friendly_name, + "Priority": priority, + "Weight": weight, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + ) + + async def create_async( + self, + target: str, + friendly_name: Union[str, object] = values.unset, + priority: Union[int, object] = values.unset, + weight: Union[int, object] = values.unset, + enabled: Union[bool, object] = values.unset, + ) -> ConnectionPolicyTargetInstance: + """ + Asynchronously create the ConnectionPolicyTargetInstance + + :param target: The SIP address you want Twilio to route your calls to. This must be a `sip:` schema. `sips` is NOT supported. + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param priority: The relative importance of the target. Can be an integer from 0 to 65535, inclusive, and the default is 10. The lowest number represents the most important target. + :param weight: The value that determines the relative share of the load the Target should receive compared to other Targets with the same priority. Can be an integer from 1 to 65535, inclusive, and the default is 10. Targets with higher values receive more load than those with lower ones with the same priority. + :param enabled: Whether the Target is enabled. The default is `true`. + + :returns: The created ConnectionPolicyTargetInstance + """ + + data = values.of( + { + "Target": target, + "FriendlyName": friendly_name, + "Priority": priority, + "Weight": weight, + "Enabled": serialize.boolean_to_string(enabled), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return ConnectionPolicyTargetInstance( + self._version, + payload, + connection_policy_sid=self._solution["connection_policy_sid"], + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[ConnectionPolicyTargetInstance]: + """ + Streams ConnectionPolicyTargetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[ConnectionPolicyTargetInstance]: + """ + Asynchronously streams ConnectionPolicyTargetInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectionPolicyTargetInstance]: + """ + Lists ConnectionPolicyTargetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[ConnectionPolicyTargetInstance]: + """ + Asynchronously lists ConnectionPolicyTargetInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectionPolicyTargetPage: + """ + Retrieve a single page of ConnectionPolicyTargetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectionPolicyTargetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return ConnectionPolicyTargetPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> ConnectionPolicyTargetPage: + """ + Asynchronously retrieve a single page of ConnectionPolicyTargetInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of ConnectionPolicyTargetInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return ConnectionPolicyTargetPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> ConnectionPolicyTargetPage: + """ + Retrieve a specific page of ConnectionPolicyTargetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectionPolicyTargetInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return ConnectionPolicyTargetPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> ConnectionPolicyTargetPage: + """ + Asynchronously retrieve a specific page of ConnectionPolicyTargetInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of ConnectionPolicyTargetInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return ConnectionPolicyTargetPage(self._version, response, self._solution) + + def get(self, sid: str) -> ConnectionPolicyTargetContext: + """ + Constructs a ConnectionPolicyTargetContext + + :param sid: The unique string that we created to identify the Target resource to update. + """ + return ConnectionPolicyTargetContext( + self._version, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=sid, + ) + + def __call__(self, sid: str) -> ConnectionPolicyTargetContext: + """ + Constructs a ConnectionPolicyTargetContext + + :param sid: The unique string that we created to identify the Target resource to update. + """ + return ConnectionPolicyTargetContext( + self._version, + connection_policy_sid=self._solution["connection_policy_sid"], + sid=sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__init__.py new file mode 100644 index 0000000..2346430 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__init__.py @@ -0,0 +1,78 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + + +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + +from twilio.rest.voice.v1.dialing_permissions.bulk_country_update import ( + BulkCountryUpdateList, +) +from twilio.rest.voice.v1.dialing_permissions.country import CountryList +from twilio.rest.voice.v1.dialing_permissions.settings import SettingsList + + +class DialingPermissionsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the DialingPermissionsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/DialingPermissions" + + self._bulk_country_updates: Optional[BulkCountryUpdateList] = None + self._countries: Optional[CountryList] = None + self._settings: Optional[SettingsList] = None + + @property + def bulk_country_updates(self) -> BulkCountryUpdateList: + """ + Access the bulk_country_updates + """ + if self._bulk_country_updates is None: + self._bulk_country_updates = BulkCountryUpdateList(self._version) + return self._bulk_country_updates + + @property + def countries(self) -> CountryList: + """ + Access the countries + """ + if self._countries is None: + self._countries = CountryList(self._version) + return self._countries + + @property + def settings(self) -> SettingsList: + """ + Access the settings + """ + if self._settings is None: + self._settings = SettingsList(self._version) + return self._settings + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f1e5ce0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/bulk_country_update.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/bulk_country_update.cpython-310.pyc new file mode 100644 index 0000000..7e28c06 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/bulk_country_update.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/settings.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/settings.cpython-310.pyc new file mode 100644 index 0000000..2d0cd74 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/__pycache__/settings.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/bulk_country_update.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/bulk_country_update.py new file mode 100644 index 0000000..60aa264 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/bulk_country_update.py @@ -0,0 +1,112 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class BulkCountryUpdateInstance(InstanceResource): + """ + :ivar update_count: The number of countries updated + :ivar update_request: A bulk update request to change voice dialing country permissions stored as a URL-encoded, JSON array of update objects. For example : `[ { \"iso_code\": \"GB\", \"low_risk_numbers_enabled\": \"true\", \"high_risk_special_numbers_enabled\":\"true\", \"high_risk_tollfraud_numbers_enabled\": \"false\" } ]` + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.update_count: Optional[int] = deserialize.integer( + payload.get("update_count") + ) + self.update_request: Optional[str] = payload.get("update_request") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class BulkCountryUpdateList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the BulkCountryUpdateList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/DialingPermissions/BulkCountryUpdates" + + def create(self, update_request: str) -> BulkCountryUpdateInstance: + """ + Create the BulkCountryUpdateInstance + + :param update_request: URL encoded JSON array of update objects. example : `[ { \\\"iso_code\\\": \\\"GB\\\", \\\"low_risk_numbers_enabled\\\": \\\"true\\\", \\\"high_risk_special_numbers_enabled\\\":\\\"true\\\", \\\"high_risk_tollfraud_numbers_enabled\\\": \\\"false\\\" } ]` + + :returns: The created BulkCountryUpdateInstance + """ + + data = values.of( + { + "UpdateRequest": update_request, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return BulkCountryUpdateInstance(self._version, payload) + + async def create_async(self, update_request: str) -> BulkCountryUpdateInstance: + """ + Asynchronously create the BulkCountryUpdateInstance + + :param update_request: URL encoded JSON array of update objects. example : `[ { \\\"iso_code\\\": \\\"GB\\\", \\\"low_risk_numbers_enabled\\\": \\\"true\\\", \\\"high_risk_special_numbers_enabled\\\":\\\"true\\\", \\\"high_risk_tollfraud_numbers_enabled\\\": \\\"false\\\" } ]` + + :returns: The created BulkCountryUpdateInstance + """ + + data = values.of( + { + "UpdateRequest": update_request, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return BulkCountryUpdateInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__init__.py new file mode 100644 index 0000000..2b9285c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__init__.py @@ -0,0 +1,556 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.voice.v1.dialing_permissions.country.highrisk_special_prefix import ( + HighriskSpecialPrefixList, +) + + +class CountryInstance(InstanceResource): + """ + :ivar iso_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). + :ivar name: The name of the country. + :ivar continent: The name of the continent in which the country is located. + :ivar country_codes: The E.164 assigned [country codes(s)](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :ivar low_risk_numbers_enabled: Whether dialing to low-risk numbers is enabled. + :ivar high_risk_special_numbers_enabled: Whether dialing to high-risk special services numbers is enabled. These prefixes include number ranges allocated by the country and include premium numbers, special services, shared cost, and others + :ivar high_risk_tollfraud_numbers_enabled: Whether dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers is enabled. These prefixes include narrow number ranges that have a high-risk of international revenue sharing fraud (IRSF) attacks, also known as [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html). These prefixes are collected from anti-fraud databases and verified by analyzing calls on our network. These prefixes are not available for download and are updated frequently + :ivar url: The absolute URL of this resource. + :ivar links: A list of URLs related to this resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], iso_code: Optional[str] = None + ): + super().__init__(version) + + self.iso_code: Optional[str] = payload.get("iso_code") + self.name: Optional[str] = payload.get("name") + self.continent: Optional[str] = payload.get("continent") + self.country_codes: Optional[List[str]] = payload.get("country_codes") + self.low_risk_numbers_enabled: Optional[bool] = payload.get( + "low_risk_numbers_enabled" + ) + self.high_risk_special_numbers_enabled: Optional[bool] = payload.get( + "high_risk_special_numbers_enabled" + ) + self.high_risk_tollfraud_numbers_enabled: Optional[bool] = payload.get( + "high_risk_tollfraud_numbers_enabled" + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + + self._solution = { + "iso_code": iso_code or self.iso_code, + } + self._context: Optional[CountryContext] = None + + @property + def _proxy(self) -> "CountryContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CountryContext for this CountryInstance + """ + if self._context is None: + self._context = CountryContext( + self._version, + iso_code=self._solution["iso_code"], + ) + return self._context + + def fetch(self) -> "CountryInstance": + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CountryInstance": + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + return await self._proxy.fetch_async() + + @property + def highrisk_special_prefixes(self) -> HighriskSpecialPrefixList: + """ + Access the highrisk_special_prefixes + """ + return self._proxy.highrisk_special_prefixes + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryContext(InstanceContext): + + def __init__(self, version: Version, iso_code: str): + """ + Initialize the CountryContext + + :param version: Version that contains the resource + :param iso_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the DialingPermissions Country resource to fetch + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_code": iso_code, + } + self._uri = "/DialingPermissions/Countries/{iso_code}".format(**self._solution) + + self._highrisk_special_prefixes: Optional[HighriskSpecialPrefixList] = None + + def fetch(self) -> CountryInstance: + """ + Fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_code=self._solution["iso_code"], + ) + + async def fetch_async(self) -> CountryInstance: + """ + Asynchronous coroutine to fetch the CountryInstance + + + :returns: The fetched CountryInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CountryInstance( + self._version, + payload, + iso_code=self._solution["iso_code"], + ) + + @property + def highrisk_special_prefixes(self) -> HighriskSpecialPrefixList: + """ + Access the highrisk_special_prefixes + """ + if self._highrisk_special_prefixes is None: + self._highrisk_special_prefixes = HighriskSpecialPrefixList( + self._version, + self._solution["iso_code"], + ) + return self._highrisk_special_prefixes + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CountryPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CountryInstance: + """ + Build an instance of CountryInstance + + :param payload: Payload response from the API + """ + return CountryInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CountryList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CountryList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/DialingPermissions/Countries" + + def stream( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CountryInstance]: + """ + Streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param str continent: Filter to retrieve the country permissions by specifying the continent + :param str country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param bool low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param bool high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param bool high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + iso_code=iso_code, + continent=continent, + country_code=country_code, + low_risk_numbers_enabled=low_risk_numbers_enabled, + high_risk_special_numbers_enabled=high_risk_special_numbers_enabled, + high_risk_tollfraud_numbers_enabled=high_risk_tollfraud_numbers_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CountryInstance]: + """ + Asynchronously streams CountryInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param str continent: Filter to retrieve the country permissions by specifying the continent + :param str country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param bool low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param bool high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param bool high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + iso_code=iso_code, + continent=continent, + country_code=country_code, + low_risk_numbers_enabled=low_risk_numbers_enabled, + high_risk_special_numbers_enabled=high_risk_special_numbers_enabled, + high_risk_tollfraud_numbers_enabled=high_risk_tollfraud_numbers_enabled, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param str continent: Filter to retrieve the country permissions by specifying the continent + :param str country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param bool low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param bool high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param bool high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + iso_code=iso_code, + continent=continent, + country_code=country_code, + low_risk_numbers_enabled=low_risk_numbers_enabled, + high_risk_special_numbers_enabled=high_risk_special_numbers_enabled, + high_risk_tollfraud_numbers_enabled=high_risk_tollfraud_numbers_enabled, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CountryInstance]: + """ + Asynchronously lists CountryInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param str continent: Filter to retrieve the country permissions by specifying the continent + :param str country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param bool low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param bool high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param bool high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + iso_code=iso_code, + continent=continent, + country_code=country_code, + low_risk_numbers_enabled=low_risk_numbers_enabled, + high_risk_special_numbers_enabled=high_risk_special_numbers_enabled, + high_risk_tollfraud_numbers_enabled=high_risk_tollfraud_numbers_enabled, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param continent: Filter to retrieve the country permissions by specifying the continent + :param country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "IsoCode": iso_code, + "Continent": continent, + "CountryCode": country_code, + "LowRiskNumbersEnabled": serialize.boolean_to_string( + low_risk_numbers_enabled + ), + "HighRiskSpecialNumbersEnabled": serialize.boolean_to_string( + high_risk_special_numbers_enabled + ), + "HighRiskTollfraudNumbersEnabled": serialize.boolean_to_string( + high_risk_tollfraud_numbers_enabled + ), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CountryPage(self._version, response) + + async def page_async( + self, + iso_code: Union[str, object] = values.unset, + continent: Union[str, object] = values.unset, + country_code: Union[str, object] = values.unset, + low_risk_numbers_enabled: Union[bool, object] = values.unset, + high_risk_special_numbers_enabled: Union[bool, object] = values.unset, + high_risk_tollfraud_numbers_enabled: Union[bool, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CountryPage: + """ + Asynchronously retrieve a single page of CountryInstance records from the API. + Request is executed immediately + + :param iso_code: Filter to retrieve the country permissions by specifying the [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) + :param continent: Filter to retrieve the country permissions by specifying the continent + :param country_code: Filter the results by specified [country codes](https://www.itu.int/itudoc/itu-t/ob-lists/icc/e164_763.html) + :param low_risk_numbers_enabled: Filter to retrieve the country permissions with dialing to low-risk numbers enabled. Can be: `true` or `false`. + :param high_risk_special_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk special service numbers enabled. Can be: `true` or `false` + :param high_risk_tollfraud_numbers_enabled: Filter to retrieve the country permissions with dialing to high-risk [toll fraud](https://www.twilio.com/blog/how-to-protect-your-account-from-toll-fraud-with-voice-dialing-geo-permissions-html) numbers enabled. Can be: `true` or `false`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CountryInstance + """ + data = values.of( + { + "IsoCode": iso_code, + "Continent": continent, + "CountryCode": country_code, + "LowRiskNumbersEnabled": serialize.boolean_to_string( + low_risk_numbers_enabled + ), + "HighRiskSpecialNumbersEnabled": serialize.boolean_to_string( + high_risk_special_numbers_enabled + ), + "HighRiskTollfraudNumbersEnabled": serialize.boolean_to_string( + high_risk_tollfraud_numbers_enabled + ), + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CountryPage(self._version, response) + + def get_page(self, target_url: str) -> CountryPage: + """ + Retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CountryPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CountryPage: + """ + Asynchronously retrieve a specific page of CountryInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CountryInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CountryPage(self._version, response) + + def get(self, iso_code: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the DialingPermissions Country resource to fetch + """ + return CountryContext(self._version, iso_code=iso_code) + + def __call__(self, iso_code: str) -> CountryContext: + """ + Constructs a CountryContext + + :param iso_code: The [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the DialingPermissions Country resource to fetch + """ + return CountryContext(self._version, iso_code=iso_code) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..a577331 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/highrisk_special_prefix.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/highrisk_special_prefix.cpython-310.pyc new file mode 100644 index 0000000..8a64dda Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/__pycache__/highrisk_special_prefix.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/highrisk_special_prefix.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/highrisk_special_prefix.py new file mode 100644 index 0000000..9303f2c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/country/highrisk_special_prefix.py @@ -0,0 +1,280 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class HighriskSpecialPrefixInstance(InstanceResource): + """ + :ivar prefix: A prefix is a contiguous number range for a block of E.164 numbers that includes the E.164 assigned country code. For example, a North American Numbering Plan prefix like `+1510720` written like `+1(510) 720` matches all numbers inclusive from `+1(510) 720-0000` to `+1(510) 720-9999`. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], iso_code: str): + super().__init__(version) + + self.prefix: Optional[str] = payload.get("prefix") + + self._solution = { + "iso_code": iso_code, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class HighriskSpecialPrefixPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> HighriskSpecialPrefixInstance: + """ + Build an instance of HighriskSpecialPrefixInstance + + :param payload: Payload response from the API + """ + return HighriskSpecialPrefixInstance( + self._version, payload, iso_code=self._solution["iso_code"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class HighriskSpecialPrefixList(ListResource): + + def __init__(self, version: Version, iso_code: str): + """ + Initialize the HighriskSpecialPrefixList + + :param version: Version that contains the resource + :param iso_code: The [ISO 3166-1 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) to identify the country permissions from which high-risk special service number prefixes are fetched + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "iso_code": iso_code, + } + self._uri = ( + "/DialingPermissions/Countries/{iso_code}/HighRiskSpecialPrefixes".format( + **self._solution + ) + ) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[HighriskSpecialPrefixInstance]: + """ + Streams HighriskSpecialPrefixInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[HighriskSpecialPrefixInstance]: + """ + Asynchronously streams HighriskSpecialPrefixInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HighriskSpecialPrefixInstance]: + """ + Lists HighriskSpecialPrefixInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[HighriskSpecialPrefixInstance]: + """ + Asynchronously lists HighriskSpecialPrefixInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HighriskSpecialPrefixPage: + """ + Retrieve a single page of HighriskSpecialPrefixInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HighriskSpecialPrefixInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return HighriskSpecialPrefixPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> HighriskSpecialPrefixPage: + """ + Asynchronously retrieve a single page of HighriskSpecialPrefixInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of HighriskSpecialPrefixInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return HighriskSpecialPrefixPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> HighriskSpecialPrefixPage: + """ + Retrieve a specific page of HighriskSpecialPrefixInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HighriskSpecialPrefixInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return HighriskSpecialPrefixPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> HighriskSpecialPrefixPage: + """ + Asynchronously retrieve a specific page of HighriskSpecialPrefixInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of HighriskSpecialPrefixInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return HighriskSpecialPrefixPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/settings.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/settings.py new file mode 100644 index 0000000..20d8456 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/dialing_permissions/settings.py @@ -0,0 +1,250 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Any, Dict, Optional, Union +from twilio.base import serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version + + +class SettingsInstance(InstanceResource): + """ + :ivar dialing_permissions_inheritance: `true` if the sub-account will inherit voice dialing permissions from the Master Project; otherwise `false`. + :ivar url: The absolute URL of this resource. + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.dialing_permissions_inheritance: Optional[bool] = payload.get( + "dialing_permissions_inheritance" + ) + self.url: Optional[str] = payload.get("url") + + self._context: Optional[SettingsContext] = None + + @property + def _proxy(self) -> "SettingsContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SettingsContext for this SettingsInstance + """ + if self._context is None: + self._context = SettingsContext( + self._version, + ) + return self._context + + def fetch(self) -> "SettingsInstance": + """ + Fetch the SettingsInstance + + + :returns: The fetched SettingsInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SettingsInstance": + """ + Asynchronous coroutine to fetch the SettingsInstance + + + :returns: The fetched SettingsInstance + """ + return await self._proxy.fetch_async() + + def update( + self, dialing_permissions_inheritance: Union[bool, object] = values.unset + ) -> "SettingsInstance": + """ + Update the SettingsInstance + + :param dialing_permissions_inheritance: `true` for the sub-account to inherit voice dialing permissions from the Master Project; otherwise `false`. + + :returns: The updated SettingsInstance + """ + return self._proxy.update( + dialing_permissions_inheritance=dialing_permissions_inheritance, + ) + + async def update_async( + self, dialing_permissions_inheritance: Union[bool, object] = values.unset + ) -> "SettingsInstance": + """ + Asynchronous coroutine to update the SettingsInstance + + :param dialing_permissions_inheritance: `true` for the sub-account to inherit voice dialing permissions from the Master Project; otherwise `false`. + + :returns: The updated SettingsInstance + """ + return await self._proxy.update_async( + dialing_permissions_inheritance=dialing_permissions_inheritance, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SettingsContext(InstanceContext): + + def __init__(self, version: Version): + """ + Initialize the SettingsContext + + :param version: Version that contains the resource + """ + super().__init__(version) + + self._uri = "/Settings" + + def fetch(self) -> SettingsInstance: + """ + Fetch the SettingsInstance + + + :returns: The fetched SettingsInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SettingsInstance( + self._version, + payload, + ) + + async def fetch_async(self) -> SettingsInstance: + """ + Asynchronous coroutine to fetch the SettingsInstance + + + :returns: The fetched SettingsInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SettingsInstance( + self._version, + payload, + ) + + def update( + self, dialing_permissions_inheritance: Union[bool, object] = values.unset + ) -> SettingsInstance: + """ + Update the SettingsInstance + + :param dialing_permissions_inheritance: `true` for the sub-account to inherit voice dialing permissions from the Master Project; otherwise `false`. + + :returns: The updated SettingsInstance + """ + data = values.of( + { + "DialingPermissionsInheritance": serialize.boolean_to_string( + dialing_permissions_inheritance + ), + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SettingsInstance(self._version, payload) + + async def update_async( + self, dialing_permissions_inheritance: Union[bool, object] = values.unset + ) -> SettingsInstance: + """ + Asynchronous coroutine to update the SettingsInstance + + :param dialing_permissions_inheritance: `true` for the sub-account to inherit voice dialing permissions from the Master Project; otherwise `false`. + + :returns: The updated SettingsInstance + """ + data = values.of( + { + "DialingPermissionsInheritance": serialize.boolean_to_string( + dialing_permissions_inheritance + ), + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SettingsInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class SettingsList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SettingsList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + def get(self) -> SettingsContext: + """ + Constructs a SettingsContext + + """ + return SettingsContext(self._version) + + def __call__(self) -> SettingsContext: + """ + Constructs a SettingsContext + + """ + return SettingsContext(self._version) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/ip_record.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/ip_record.py new file mode 100644 index 0000000..ee409ba --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/ip_record.py @@ -0,0 +1,589 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class IpRecordInstance(InstanceResource): + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the IP Record resource. + :ivar sid: The unique string that we created to identify the IP Record resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar ip_address: An IP address in dotted decimal notation, IPv4 only. + :ivar cidr_prefix_length: An integer representing the length of the [CIDR](https://tools.ietf.org/html/rfc4632) prefix to use with this IP address. By default the entire IP address is used, which for IPv4 is value 32. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.sid: Optional[str] = payload.get("sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.ip_address: Optional[str] = payload.get("ip_address") + self.cidr_prefix_length: Optional[int] = deserialize.integer( + payload.get("cidr_prefix_length") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[IpRecordContext] = None + + @property + def _proxy(self) -> "IpRecordContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: IpRecordContext for this IpRecordInstance + """ + if self._context is None: + self._context = IpRecordContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the IpRecordInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpRecordInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "IpRecordInstance": + """ + Fetch the IpRecordInstance + + + :returns: The fetched IpRecordInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "IpRecordInstance": + """ + Asynchronous coroutine to fetch the IpRecordInstance + + + :returns: The fetched IpRecordInstance + """ + return await self._proxy.fetch_async() + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> "IpRecordInstance": + """ + Update the IpRecordInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated IpRecordInstance + """ + return self._proxy.update( + friendly_name=friendly_name, + ) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> "IpRecordInstance": + """ + Asynchronous coroutine to update the IpRecordInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated IpRecordInstance + """ + return await self._proxy.update_async( + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpRecordContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the IpRecordContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/IpRecords/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the IpRecordInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the IpRecordInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> IpRecordInstance: + """ + Fetch the IpRecordInstance + + + :returns: The fetched IpRecordInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return IpRecordInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> IpRecordInstance: + """ + Asynchronous coroutine to fetch the IpRecordInstance + + + :returns: The fetched IpRecordInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return IpRecordInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, friendly_name: Union[str, object] = values.unset + ) -> IpRecordInstance: + """ + Update the IpRecordInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated IpRecordInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return IpRecordInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, friendly_name: Union[str, object] = values.unset + ) -> IpRecordInstance: + """ + Asynchronous coroutine to update the IpRecordInstance + + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + + :returns: The updated IpRecordInstance + """ + data = values.of( + { + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpRecordInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class IpRecordPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> IpRecordInstance: + """ + Build an instance of IpRecordInstance + + :param payload: Payload response from the API + """ + return IpRecordInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class IpRecordList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the IpRecordList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/IpRecords" + + def create( + self, + ip_address: str, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpRecordInstance: + """ + Create the IpRecordInstance + + :param ip_address: An IP address in dotted decimal notation, IPv4 only. + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the [CIDR](https://tools.ietf.org/html/rfc4632) prefix to use with this IP address. By default the entire IP address is used, which for IPv4 is value 32. + + :returns: The created IpRecordInstance + """ + + data = values.of( + { + "IpAddress": ip_address, + "FriendlyName": friendly_name, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return IpRecordInstance(self._version, payload) + + async def create_async( + self, + ip_address: str, + friendly_name: Union[str, object] = values.unset, + cidr_prefix_length: Union[int, object] = values.unset, + ) -> IpRecordInstance: + """ + Asynchronously create the IpRecordInstance + + :param ip_address: An IP address in dotted decimal notation, IPv4 only. + :param friendly_name: A descriptive string that you create to describe the resource. It is not unique and can be up to 255 characters long. + :param cidr_prefix_length: An integer representing the length of the [CIDR](https://tools.ietf.org/html/rfc4632) prefix to use with this IP address. By default the entire IP address is used, which for IPv4 is value 32. + + :returns: The created IpRecordInstance + """ + + data = values.of( + { + "IpAddress": ip_address, + "FriendlyName": friendly_name, + "CidrPrefixLength": cidr_prefix_length, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return IpRecordInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[IpRecordInstance]: + """ + Streams IpRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[IpRecordInstance]: + """ + Asynchronously streams IpRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpRecordInstance]: + """ + Lists IpRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[IpRecordInstance]: + """ + Asynchronously lists IpRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpRecordPage: + """ + Retrieve a single page of IpRecordInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpRecordInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return IpRecordPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> IpRecordPage: + """ + Asynchronously retrieve a single page of IpRecordInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of IpRecordInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return IpRecordPage(self._version, response) + + def get_page(self, target_url: str) -> IpRecordPage: + """ + Retrieve a specific page of IpRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpRecordInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return IpRecordPage(self._version, response) + + async def get_page_async(self, target_url: str) -> IpRecordPage: + """ + Asynchronously retrieve a specific page of IpRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of IpRecordInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return IpRecordPage(self._version, response) + + def get(self, sid: str) -> IpRecordContext: + """ + Constructs a IpRecordContext + + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + return IpRecordContext(self._version, sid=sid) + + def __call__(self, sid: str) -> IpRecordContext: + """ + Constructs a IpRecordContext + + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + return IpRecordContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/source_ip_mapping.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/source_ip_mapping.py new file mode 100644 index 0000000..8a0d4a9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/voice/v1/source_ip_mapping.py @@ -0,0 +1,569 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Voice + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class SourceIpMappingInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the IP Record resource. + :ivar ip_record_sid: The Twilio-provided string that uniquely identifies the IP Record resource to map from. + :ivar sip_domain_sid: The SID of the SIP Domain that the IP Record is mapped to. + :ivar date_created: The date and time in GMT that the resource was created specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar date_updated: The date and time in GMT that the resource was last updated specified in [RFC 2822](https://www.ietf.org/rfc/rfc2822.txt) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.ip_record_sid: Optional[str] = payload.get("ip_record_sid") + self.sip_domain_sid: Optional[str] = payload.get("sip_domain_sid") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SourceIpMappingContext] = None + + @property + def _proxy(self) -> "SourceIpMappingContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SourceIpMappingContext for this SourceIpMappingInstance + """ + if self._context is None: + self._context = SourceIpMappingContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SourceIpMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SourceIpMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SourceIpMappingInstance": + """ + Fetch the SourceIpMappingInstance + + + :returns: The fetched SourceIpMappingInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SourceIpMappingInstance": + """ + Asynchronous coroutine to fetch the SourceIpMappingInstance + + + :returns: The fetched SourceIpMappingInstance + """ + return await self._proxy.fetch_async() + + def update(self, sip_domain_sid: str) -> "SourceIpMappingInstance": + """ + Update the SourceIpMappingInstance + + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The updated SourceIpMappingInstance + """ + return self._proxy.update( + sip_domain_sid=sip_domain_sid, + ) + + async def update_async(self, sip_domain_sid: str) -> "SourceIpMappingInstance": + """ + Asynchronous coroutine to update the SourceIpMappingInstance + + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The updated SourceIpMappingInstance + """ + return await self._proxy.update_async( + sip_domain_sid=sip_domain_sid, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SourceIpMappingContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SourceIpMappingContext + + :param version: Version that contains the resource + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/SourceIpMappings/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the SourceIpMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SourceIpMappingInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SourceIpMappingInstance: + """ + Fetch the SourceIpMappingInstance + + + :returns: The fetched SourceIpMappingInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SourceIpMappingInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SourceIpMappingInstance: + """ + Asynchronous coroutine to fetch the SourceIpMappingInstance + + + :returns: The fetched SourceIpMappingInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SourceIpMappingInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update(self, sip_domain_sid: str) -> SourceIpMappingInstance: + """ + Update the SourceIpMappingInstance + + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The updated SourceIpMappingInstance + """ + data = values.of( + { + "SipDomainSid": sip_domain_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SourceIpMappingInstance( + self._version, payload, sid=self._solution["sid"] + ) + + async def update_async(self, sip_domain_sid: str) -> SourceIpMappingInstance: + """ + Asynchronous coroutine to update the SourceIpMappingInstance + + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The updated SourceIpMappingInstance + """ + data = values.of( + { + "SipDomainSid": sip_domain_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SourceIpMappingInstance( + self._version, payload, sid=self._solution["sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SourceIpMappingPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SourceIpMappingInstance: + """ + Build an instance of SourceIpMappingInstance + + :param payload: Payload response from the API + """ + return SourceIpMappingInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SourceIpMappingList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SourceIpMappingList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/SourceIpMappings" + + def create( + self, ip_record_sid: str, sip_domain_sid: str + ) -> SourceIpMappingInstance: + """ + Create the SourceIpMappingInstance + + :param ip_record_sid: The Twilio-provided string that uniquely identifies the IP Record resource to map from. + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The created SourceIpMappingInstance + """ + + data = values.of( + { + "IpRecordSid": ip_record_sid, + "SipDomainSid": sip_domain_sid, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return SourceIpMappingInstance(self._version, payload) + + async def create_async( + self, ip_record_sid: str, sip_domain_sid: str + ) -> SourceIpMappingInstance: + """ + Asynchronously create the SourceIpMappingInstance + + :param ip_record_sid: The Twilio-provided string that uniquely identifies the IP Record resource to map from. + :param sip_domain_sid: The SID of the SIP Domain that the IP Record should be mapped to. + + :returns: The created SourceIpMappingInstance + """ + + data = values.of( + { + "IpRecordSid": ip_record_sid, + "SipDomainSid": sip_domain_sid, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SourceIpMappingInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SourceIpMappingInstance]: + """ + Streams SourceIpMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SourceIpMappingInstance]: + """ + Asynchronously streams SourceIpMappingInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SourceIpMappingInstance]: + """ + Lists SourceIpMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SourceIpMappingInstance]: + """ + Asynchronously lists SourceIpMappingInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SourceIpMappingPage: + """ + Retrieve a single page of SourceIpMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SourceIpMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SourceIpMappingPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SourceIpMappingPage: + """ + Asynchronously retrieve a single page of SourceIpMappingInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SourceIpMappingInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SourceIpMappingPage(self._version, response) + + def get_page(self, target_url: str) -> SourceIpMappingPage: + """ + Retrieve a specific page of SourceIpMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SourceIpMappingInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SourceIpMappingPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SourceIpMappingPage: + """ + Asynchronously retrieve a specific page of SourceIpMappingInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SourceIpMappingInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SourceIpMappingPage(self._version, response) + + def get(self, sid: str) -> SourceIpMappingContext: + """ + Constructs a SourceIpMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + return SourceIpMappingContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SourceIpMappingContext: + """ + Constructs a SourceIpMappingContext + + :param sid: The Twilio-provided string that uniquely identifies the IP Record resource to update. + """ + return SourceIpMappingContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/WirelessBase.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/WirelessBase.py new file mode 100644 index 0000000..0228c4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/WirelessBase.py @@ -0,0 +1,44 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional + +from twilio.base.domain import Domain +from twilio.rest import Client +from twilio.rest.wireless.v1 import V1 + + +class WirelessBase(Domain): + + def __init__(self, twilio: Client): + """ + Initialize the Wireless Domain + + :returns: Domain for Wireless + """ + super().__init__(twilio, "https://wireless.twilio.com") + self._v1: Optional[V1] = None + + @property + def v1(self) -> V1: + """ + :returns: Versions v1 of Wireless + """ + if self._v1 is None: + self._v1 = V1(self) + return self._v1 + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__init__.py new file mode 100644 index 0000000..3b7a409 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__init__.py @@ -0,0 +1,43 @@ +from warnings import warn + +from twilio.rest.wireless.WirelessBase import WirelessBase +from twilio.rest.wireless.v1.command import CommandList +from twilio.rest.wireless.v1.rate_plan import RatePlanList +from twilio.rest.wireless.v1.sim import SimList +from twilio.rest.wireless.v1.usage_record import UsageRecordList + + +class Wireless(WirelessBase): + @property + def usage_records(self) -> UsageRecordList: + warn( + "usage_records is deprecated. Use v1.usage_records instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.usage_records + + @property + def commands(self) -> CommandList: + warn( + "commands is deprecated. Use v1.commands instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.commands + + @property + def rate_plans(self) -> RatePlanList: + warn( + "rate_plans is deprecated. Use v1.rate_plans instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.v1.rate_plans + + @property + def sims(self) -> SimList: + warn( + "sims is deprecated. Use v1.sims instead.", DeprecationWarning, stacklevel=2 + ) + return self.v1.sims diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/WirelessBase.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/WirelessBase.cpython-310.pyc new file mode 100644 index 0000000..7d54ed3 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/WirelessBase.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..464cf3d Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__init__.py new file mode 100644 index 0000000..8f4d0dc --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__init__.py @@ -0,0 +1,67 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from typing import Optional +from twilio.base.version import Version +from twilio.base.domain import Domain +from twilio.rest.wireless.v1.command import CommandList +from twilio.rest.wireless.v1.rate_plan import RatePlanList +from twilio.rest.wireless.v1.sim import SimList +from twilio.rest.wireless.v1.usage_record import UsageRecordList + + +class V1(Version): + + def __init__(self, domain: Domain): + """ + Initialize the V1 version of Wireless + + :param domain: The Twilio.wireless domain + """ + super().__init__(domain, "v1") + self._commands: Optional[CommandList] = None + self._rate_plans: Optional[RatePlanList] = None + self._sims: Optional[SimList] = None + self._usage_records: Optional[UsageRecordList] = None + + @property + def commands(self) -> CommandList: + if self._commands is None: + self._commands = CommandList(self) + return self._commands + + @property + def rate_plans(self) -> RatePlanList: + if self._rate_plans is None: + self._rate_plans = RatePlanList(self) + return self._rate_plans + + @property + def sims(self) -> SimList: + if self._sims is None: + self._sims = SimList(self) + return self._sims + + @property + def usage_records(self) -> UsageRecordList: + if self._usage_records is None: + self._usage_records = UsageRecordList(self) + return self._usage_records + + def __repr__(self) -> str: + """ + Provide a friendly representation + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..e149eba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/command.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/command.cpython-310.pyc new file mode 100644 index 0000000..0e78493 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/command.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/rate_plan.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/rate_plan.cpython-310.pyc new file mode 100644 index 0000000..d199365 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/rate_plan.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/usage_record.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/usage_record.cpython-310.pyc new file mode 100644 index 0000000..887f06e Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/__pycache__/usage_record.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/command.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/command.py new file mode 100644 index 0000000..f310db8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/command.py @@ -0,0 +1,647 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class CommandInstance(InstanceResource): + + class CommandMode(object): + TEXT = "text" + BINARY = "binary" + + class Direction(object): + FROM_SIM = "from_sim" + TO_SIM = "to_sim" + + class Status(object): + QUEUED = "queued" + SENT = "sent" + DELIVERED = "delivered" + RECEIVED = "received" + FAILED = "failed" + + class Transport(object): + SMS = "sms" + IP = "ip" + + """ + :ivar sid: The unique string that we created to identify the Command resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the Command resource. + :ivar sim_sid: The SID of the [Sim resource](https://www.twilio.com/docs/iot/wireless/api/sim-resource) that the Command was sent to or from. + :ivar command: The message being sent to or from the SIM. For text mode messages, this can be up to 160 characters. For binary mode messages, this is a series of up to 140 bytes of data encoded using base64. + :ivar command_mode: + :ivar transport: + :ivar delivery_receipt_requested: Whether to request a delivery receipt. + :ivar status: + :ivar direction: + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.command: Optional[str] = payload.get("command") + self.command_mode: Optional["CommandInstance.CommandMode"] = payload.get( + "command_mode" + ) + self.transport: Optional["CommandInstance.Transport"] = payload.get("transport") + self.delivery_receipt_requested: Optional[bool] = payload.get( + "delivery_receipt_requested" + ) + self.status: Optional["CommandInstance.Status"] = payload.get("status") + self.direction: Optional["CommandInstance.Direction"] = payload.get("direction") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[CommandContext] = None + + @property + def _proxy(self) -> "CommandContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: CommandContext for this CommandInstance + """ + if self._context is None: + self._context = CommandContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the CommandInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CommandInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "CommandInstance": + """ + Fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "CommandInstance": + """ + Asynchronous coroutine to fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + return await self._proxy.fetch_async() + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CommandContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the CommandContext + + :param version: Version that contains the resource + :param sid: The SID of the Command resource to fetch. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Commands/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the CommandInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the CommandInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> CommandInstance: + """ + Fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return CommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> CommandInstance: + """ + Asynchronous coroutine to fetch the CommandInstance + + + :returns: The fetched CommandInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return CommandInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class CommandPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> CommandInstance: + """ + Build an instance of CommandInstance + + :param payload: Payload response from the API + """ + return CommandInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class CommandList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the CommandList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Commands" + + def create( + self, + command: str, + sim: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + command_mode: Union["CommandInstance.CommandMode", object] = values.unset, + include_sid: Union[str, object] = values.unset, + delivery_receipt_requested: Union[bool, object] = values.unset, + ) -> CommandInstance: + """ + Create the CommandInstance + + :param command: The message body of the Command. Can be plain text in text mode or a Base64 encoded byte string in binary mode. + :param sim: The `sid` or `unique_name` of the [SIM](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to send the Command to. + :param callback_method: The HTTP method we use to call `callback_url`. Can be: `POST` or `GET`, and the default is `POST`. + :param callback_url: The URL we call using the `callback_url` when the Command has finished sending, whether the command was delivered or it failed. + :param command_mode: + :param include_sid: Whether to include the SID of the command in the message body. Can be: `none`, `start`, or `end`, and the default behavior is `none`. When sending a Command to a SIM in text mode, we can automatically include the SID of the Command in the message body, which could be used to ensure that the device does not process the same Command more than once. A value of `start` will prepend the message with the Command SID, and `end` will append it to the end, separating the Command SID from the message body with a space. The length of the Command SID is included in the 160 character limit so the SMS body must be 128 characters or less before the Command SID is included. + :param delivery_receipt_requested: Whether to request delivery receipt from the recipient. For Commands that request delivery receipt, the Command state transitions to 'delivered' once the server has received a delivery receipt from the device. The default value is `true`. + + :returns: The created CommandInstance + """ + + data = values.of( + { + "Command": command, + "Sim": sim, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "CommandMode": command_mode, + "IncludeSid": include_sid, + "DeliveryReceiptRequested": serialize.boolean_to_string( + delivery_receipt_requested + ), + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return CommandInstance(self._version, payload) + + async def create_async( + self, + command: str, + sim: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + command_mode: Union["CommandInstance.CommandMode", object] = values.unset, + include_sid: Union[str, object] = values.unset, + delivery_receipt_requested: Union[bool, object] = values.unset, + ) -> CommandInstance: + """ + Asynchronously create the CommandInstance + + :param command: The message body of the Command. Can be plain text in text mode or a Base64 encoded byte string in binary mode. + :param sim: The `sid` or `unique_name` of the [SIM](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to send the Command to. + :param callback_method: The HTTP method we use to call `callback_url`. Can be: `POST` or `GET`, and the default is `POST`. + :param callback_url: The URL we call using the `callback_url` when the Command has finished sending, whether the command was delivered or it failed. + :param command_mode: + :param include_sid: Whether to include the SID of the command in the message body. Can be: `none`, `start`, or `end`, and the default behavior is `none`. When sending a Command to a SIM in text mode, we can automatically include the SID of the Command in the message body, which could be used to ensure that the device does not process the same Command more than once. A value of `start` will prepend the message with the Command SID, and `end` will append it to the end, separating the Command SID from the message body with a space. The length of the Command SID is included in the 160 character limit so the SMS body must be 128 characters or less before the Command SID is included. + :param delivery_receipt_requested: Whether to request delivery receipt from the recipient. For Commands that request delivery receipt, the Command state transitions to 'delivered' once the server has received a delivery receipt from the device. The default value is `true`. + + :returns: The created CommandInstance + """ + + data = values.of( + { + "Command": command, + "Sim": sim, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "CommandMode": command_mode, + "IncludeSid": include_sid, + "DeliveryReceiptRequested": serialize.boolean_to_string( + delivery_receipt_requested + ), + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return CommandInstance(self._version, payload) + + def stream( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[CommandInstance]: + """ + Streams CommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param "CommandInstance.Status" status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param "CommandInstance.Direction" direction: Only return Commands with this direction value. + :param "CommandInstance.Transport" transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + sim=sim, + status=status, + direction=direction, + transport=transport, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[CommandInstance]: + """ + Asynchronously streams CommandInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param str sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param "CommandInstance.Status" status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param "CommandInstance.Direction" direction: Only return Commands with this direction value. + :param "CommandInstance.Transport" transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + sim=sim, + status=status, + direction=direction, + transport=transport, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CommandInstance]: + """ + Lists CommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param "CommandInstance.Status" status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param "CommandInstance.Direction" direction: Only return Commands with this direction value. + :param "CommandInstance.Transport" transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + sim=sim, + status=status, + direction=direction, + transport=transport, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[CommandInstance]: + """ + Asynchronously lists CommandInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param str sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param "CommandInstance.Status" status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param "CommandInstance.Direction" direction: Only return Commands with this direction value. + :param "CommandInstance.Transport" transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + sim=sim, + status=status, + direction=direction, + transport=transport, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CommandPage: + """ + Retrieve a single page of CommandInstance records from the API. + Request is executed immediately + + :param sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param direction: Only return Commands with this direction value. + :param transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CommandInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "Direction": direction, + "Transport": transport, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return CommandPage(self._version, response) + + async def page_async( + self, + sim: Union[str, object] = values.unset, + status: Union["CommandInstance.Status", object] = values.unset, + direction: Union["CommandInstance.Direction", object] = values.unset, + transport: Union["CommandInstance.Transport", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> CommandPage: + """ + Asynchronously retrieve a single page of CommandInstance records from the API. + Request is executed immediately + + :param sim: The `sid` or `unique_name` of the [Sim resources](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read. + :param status: The status of the resources to read. Can be: `queued`, `sent`, `delivered`, `received`, or `failed`. + :param direction: Only return Commands with this direction value. + :param transport: Only return Commands with this transport value. Can be: `sms` or `ip`. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of CommandInstance + """ + data = values.of( + { + "Sim": sim, + "Status": status, + "Direction": direction, + "Transport": transport, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return CommandPage(self._version, response) + + def get_page(self, target_url: str) -> CommandPage: + """ + Retrieve a specific page of CommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CommandInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return CommandPage(self._version, response) + + async def get_page_async(self, target_url: str) -> CommandPage: + """ + Asynchronously retrieve a specific page of CommandInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of CommandInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return CommandPage(self._version, response) + + def get(self, sid: str) -> CommandContext: + """ + Constructs a CommandContext + + :param sid: The SID of the Command resource to fetch. + """ + return CommandContext(self._version, sid=sid) + + def __call__(self, sid: str) -> CommandContext: + """ + Constructs a CommandContext + + :param sid: The SID of the Command resource to fetch. + """ + return CommandContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/rate_plan.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/rate_plan.py new file mode 100644 index 0000000..0ee1e20 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/rate_plan.py @@ -0,0 +1,683 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, serialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class RatePlanInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the RatePlan resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the RatePlan resource. + :ivar friendly_name: The string that you assigned to describe the resource. + :ivar data_enabled: Whether SIMs can use GPRS/3G/4G/LTE data connectivity. + :ivar data_metering: The model used to meter data usage. Can be: `payg` and `quota-1`, `quota-10`, and `quota-50`. Learn more about the available [data metering models](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#payg-vs-quota-data-plans). + :ivar data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on the home network (T-Mobile USA). The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB. + :ivar messaging_enabled: Whether SIMs can make, send, and receive SMS using [Commands](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :ivar voice_enabled: Deprecated. Whether SIMs can make and receive voice calls. + :ivar national_roaming_enabled: Whether SIMs can roam on networks other than the home network (T-Mobile USA) in the United States. See [national roaming](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#national-roaming). + :ivar national_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on non-home networks in the United States. The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB. + :ivar international_roaming: The list of services that SIMs capable of using GPRS/3G/4G/LTE data connectivity can use outside of the United States. Can contain: `data` and `messaging`. + :ivar international_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month when roaming outside the United States. Can be up to 2TB. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar date_updated: The date and time in GMT when the resource was last updated specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar url: The absolute URL of the resource. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.data_enabled: Optional[bool] = payload.get("data_enabled") + self.data_metering: Optional[str] = payload.get("data_metering") + self.data_limit: Optional[int] = deserialize.integer(payload.get("data_limit")) + self.messaging_enabled: Optional[bool] = payload.get("messaging_enabled") + self.voice_enabled: Optional[bool] = payload.get("voice_enabled") + self.national_roaming_enabled: Optional[bool] = payload.get( + "national_roaming_enabled" + ) + self.national_roaming_data_limit: Optional[int] = deserialize.integer( + payload.get("national_roaming_data_limit") + ) + self.international_roaming: Optional[List[str]] = payload.get( + "international_roaming" + ) + self.international_roaming_data_limit: Optional[int] = deserialize.integer( + payload.get("international_roaming_data_limit") + ) + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[RatePlanContext] = None + + @property + def _proxy(self) -> "RatePlanContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: RatePlanContext for this RatePlanInstance + """ + if self._context is None: + self._context = RatePlanContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "RatePlanInstance": + """ + Fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "RatePlanInstance": + """ + Asynchronous coroutine to fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "RatePlanInstance": + """ + Update the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + + :returns: The updated RatePlanInstance + """ + return self._proxy.update( + unique_name=unique_name, + friendly_name=friendly_name, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> "RatePlanInstance": + """ + Asynchronous coroutine to update the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + + :returns: The updated RatePlanInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + friendly_name=friendly_name, + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RatePlanContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the RatePlanContext + + :param version: Version that contains the resource + :param sid: The SID of the RatePlan resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/RatePlans/{sid}".format(**self._solution) + + def delete(self) -> bool: + """ + Deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the RatePlanInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> RatePlanInstance: + """ + Fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return RatePlanInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> RatePlanInstance: + """ + Asynchronous coroutine to fetch the RatePlanInstance + + + :returns: The fetched RatePlanInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return RatePlanInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> RatePlanInstance: + """ + Update the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + + :returns: The updated RatePlanInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + ) -> RatePlanInstance: + """ + Asynchronous coroutine to update the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + + :returns: The updated RatePlanInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload, sid=self._solution["sid"]) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class RatePlanPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> RatePlanInstance: + """ + Build an instance of RatePlanInstance + + :param payload: Payload response from the API + """ + return RatePlanInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class RatePlanList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the RatePlanList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/RatePlans" + + def create( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + data_metering: Union[str, object] = values.unset, + messaging_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + national_roaming_enabled: Union[bool, object] = values.unset, + international_roaming: Union[List[str], object] = values.unset, + national_roaming_data_limit: Union[int, object] = values.unset, + international_roaming_data_limit: Union[int, object] = values.unset, + ) -> RatePlanInstance: + """ + Create the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + :param data_enabled: Whether SIMs can use GPRS/3G/4G/LTE data connectivity. + :param data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on the home network (T-Mobile USA). The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB and the default value is `1000`. + :param data_metering: The model used to meter data usage. Can be: `payg` and `quota-1`, `quota-10`, and `quota-50`. Learn more about the available [data metering models](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#payg-vs-quota-data-plans). + :param messaging_enabled: Whether SIMs can make, send, and receive SMS using [Commands](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_enabled: Deprecated. + :param national_roaming_enabled: Whether SIMs can roam on networks other than the home network (T-Mobile USA) in the United States. See [national roaming](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#national-roaming). + :param international_roaming: The list of services that SIMs capable of using GPRS/3G/4G/LTE data connectivity can use outside of the United States. Can contain: `data` and `messaging`. + :param national_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on non-home networks in the United States. The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB. See [national roaming](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#national-roaming) for more info. + :param international_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month when roaming outside the United States. Can be up to 2TB. + + :returns: The created RatePlanInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "DataMetering": data_metering, + "MessagingEnabled": serialize.boolean_to_string(messaging_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "NationalRoamingEnabled": serialize.boolean_to_string( + national_roaming_enabled + ), + "InternationalRoaming": serialize.map( + international_roaming, lambda e: e + ), + "NationalRoamingDataLimit": national_roaming_data_limit, + "InternationalRoamingDataLimit": international_roaming_data_limit, + } + ) + + payload = self._version.create( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload) + + async def create_async( + self, + unique_name: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + data_enabled: Union[bool, object] = values.unset, + data_limit: Union[int, object] = values.unset, + data_metering: Union[str, object] = values.unset, + messaging_enabled: Union[bool, object] = values.unset, + voice_enabled: Union[bool, object] = values.unset, + national_roaming_enabled: Union[bool, object] = values.unset, + international_roaming: Union[List[str], object] = values.unset, + national_roaming_data_limit: Union[int, object] = values.unset, + international_roaming_data_limit: Union[int, object] = values.unset, + ) -> RatePlanInstance: + """ + Asynchronously create the RatePlanInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :param friendly_name: A descriptive string that you create to describe the resource. It does not have to be unique. + :param data_enabled: Whether SIMs can use GPRS/3G/4G/LTE data connectivity. + :param data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on the home network (T-Mobile USA). The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB and the default value is `1000`. + :param data_metering: The model used to meter data usage. Can be: `payg` and `quota-1`, `quota-10`, and `quota-50`. Learn more about the available [data metering models](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#payg-vs-quota-data-plans). + :param messaging_enabled: Whether SIMs can make, send, and receive SMS using [Commands](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_enabled: Deprecated. + :param national_roaming_enabled: Whether SIMs can roam on networks other than the home network (T-Mobile USA) in the United States. See [national roaming](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#national-roaming). + :param international_roaming: The list of services that SIMs capable of using GPRS/3G/4G/LTE data connectivity can use outside of the United States. Can contain: `data` and `messaging`. + :param national_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month on non-home networks in the United States. The metering period begins the day of activation and ends on the same day in the following month. Can be up to 2TB. See [national roaming](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource#national-roaming) for more info. + :param international_roaming_data_limit: The total data usage (download and upload combined) in Megabytes that the Network allows during one month when roaming outside the United States. Can be up to 2TB. + + :returns: The created RatePlanInstance + """ + + data = values.of( + { + "UniqueName": unique_name, + "FriendlyName": friendly_name, + "DataEnabled": serialize.boolean_to_string(data_enabled), + "DataLimit": data_limit, + "DataMetering": data_metering, + "MessagingEnabled": serialize.boolean_to_string(messaging_enabled), + "VoiceEnabled": serialize.boolean_to_string(voice_enabled), + "NationalRoamingEnabled": serialize.boolean_to_string( + national_roaming_enabled + ), + "InternationalRoaming": serialize.map( + international_roaming, lambda e: e + ), + "NationalRoamingDataLimit": national_roaming_data_limit, + "InternationalRoamingDataLimit": international_roaming_data_limit, + } + ) + + payload = await self._version.create_async( + method="POST", + uri=self._uri, + data=data, + ) + + return RatePlanInstance(self._version, payload) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[RatePlanInstance]: + """ + Streams RatePlanInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[RatePlanInstance]: + """ + Asynchronously streams RatePlanInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RatePlanInstance]: + """ + Lists RatePlanInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[RatePlanInstance]: + """ + Asynchronously lists RatePlanInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RatePlanPage: + """ + Retrieve a single page of RatePlanInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RatePlanInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return RatePlanPage(self._version, response) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> RatePlanPage: + """ + Asynchronously retrieve a single page of RatePlanInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of RatePlanInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return RatePlanPage(self._version, response) + + def get_page(self, target_url: str) -> RatePlanPage: + """ + Retrieve a specific page of RatePlanInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RatePlanInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return RatePlanPage(self._version, response) + + async def get_page_async(self, target_url: str) -> RatePlanPage: + """ + Asynchronously retrieve a specific page of RatePlanInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of RatePlanInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return RatePlanPage(self._version, response) + + def get(self, sid: str) -> RatePlanContext: + """ + Constructs a RatePlanContext + + :param sid: The SID of the RatePlan resource to update. + """ + return RatePlanContext(self._version, sid=sid) + + def __call__(self, sid: str) -> RatePlanContext: + """ + Constructs a RatePlanContext + + :param sid: The SID of the RatePlan resource to update. + """ + return RatePlanContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__init__.py new file mode 100644 index 0000000..f0d21c4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__init__.py @@ -0,0 +1,918 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values +from twilio.base.instance_context import InstanceContext +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page +from twilio.rest.wireless.v1.sim.data_session import DataSessionList +from twilio.rest.wireless.v1.sim.usage_record import UsageRecordList + + +class SimInstance(InstanceResource): + + class ResetStatus(object): + RESETTING = "resetting" + + class Status(object): + NEW = "new" + READY = "ready" + ACTIVE = "active" + SUSPENDED = "suspended" + DEACTIVATED = "deactivated" + CANCELED = "canceled" + SCHEDULED = "scheduled" + UPDATING = "updating" + + """ + :ivar sid: The unique string that we created to identify the Sim resource. + :ivar unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the resource's `sid` in the URL to address the resource. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the Sim resource belongs. + :ivar rate_plan_sid: The SID of the [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource) to which the Sim resource is assigned. + :ivar friendly_name: The string that you assigned to describe the Sim resource. + :ivar iccid: The [ICCID](https://en.wikipedia.org/wiki/SIM_card#ICCID) associated with the SIM. + :ivar e_id: Deprecated. + :ivar status: + :ivar reset_status: + :ivar commands_callback_url: The URL we call using the `commands_callback_method` when the SIM originates a machine-to-machine [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). Your server should respond with an HTTP status code in the 200 range; any response body will be ignored. + :ivar commands_callback_method: The HTTP method we use to call `commands_callback_url`. Can be: `POST` or `GET`. Default is `POST`. + :ivar sms_fallback_method: Deprecated. + :ivar sms_fallback_url: Deprecated. + :ivar sms_method: Deprecated. + :ivar sms_url: Deprecated. + :ivar voice_fallback_method: Deprecated. The HTTP method we use to call `voice_fallback_url`. Can be: `GET` or `POST`. Default is `POST`. + :ivar voice_fallback_url: Deprecated. The URL we call using the `voice_fallback_method` when an error occurs while retrieving or executing the TwiML requested from `voice_url`. + :ivar voice_method: Deprecated. The HTTP method we use to call `voice_url`. Can be: `GET` or `POST`. Default is `POST`. + :ivar voice_url: Deprecated. The URL we call using the `voice_method` when the SIM-connected device makes a voice call. + :ivar date_created: The date and time in GMT when the resource was created specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar date_updated: The date and time in GMT when the Sim resource was last updated specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar url: The absolute URL of the resource. + :ivar links: The URLs of related subresources. + :ivar ip_address: Deprecated. + """ + + def __init__( + self, version: Version, payload: Dict[str, Any], sid: Optional[str] = None + ): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.unique_name: Optional[str] = payload.get("unique_name") + self.account_sid: Optional[str] = payload.get("account_sid") + self.rate_plan_sid: Optional[str] = payload.get("rate_plan_sid") + self.friendly_name: Optional[str] = payload.get("friendly_name") + self.iccid: Optional[str] = payload.get("iccid") + self.e_id: Optional[str] = payload.get("e_id") + self.status: Optional["SimInstance.Status"] = payload.get("status") + self.reset_status: Optional["SimInstance.ResetStatus"] = payload.get( + "reset_status" + ) + self.commands_callback_url: Optional[str] = payload.get("commands_callback_url") + self.commands_callback_method: Optional[str] = payload.get( + "commands_callback_method" + ) + self.sms_fallback_method: Optional[str] = payload.get("sms_fallback_method") + self.sms_fallback_url: Optional[str] = payload.get("sms_fallback_url") + self.sms_method: Optional[str] = payload.get("sms_method") + self.sms_url: Optional[str] = payload.get("sms_url") + self.voice_fallback_method: Optional[str] = payload.get("voice_fallback_method") + self.voice_fallback_url: Optional[str] = payload.get("voice_fallback_url") + self.voice_method: Optional[str] = payload.get("voice_method") + self.voice_url: Optional[str] = payload.get("voice_url") + self.date_created: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_created") + ) + self.date_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("date_updated") + ) + self.url: Optional[str] = payload.get("url") + self.links: Optional[Dict[str, object]] = payload.get("links") + self.ip_address: Optional[str] = payload.get("ip_address") + + self._solution = { + "sid": sid or self.sid, + } + self._context: Optional[SimContext] = None + + @property + def _proxy(self) -> "SimContext": + """ + Generate an instance context for the instance, the context is capable of + performing various actions. All instance actions are proxied to the context + + :returns: SimContext for this SimInstance + """ + if self._context is None: + self._context = SimContext( + self._version, + sid=self._solution["sid"], + ) + return self._context + + def delete(self) -> bool: + """ + Deletes the SimInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._proxy.delete() + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SimInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._proxy.delete_async() + + def fetch(self) -> "SimInstance": + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return self._proxy.fetch() + + async def fetch_async(self) -> "SimInstance": + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + return await self._proxy.fetch_async() + + def update( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union["SimInstance.Status", object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + reset_status: Union["SimInstance.ResetStatus", object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the `sid` in the URL path to address the resource. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param callback_url: The URL we should call using the `callback_url` when the SIM has finished updating. When the SIM transitions from `new` to `ready` or from any status to `deactivated`, we call this URL when the status changes to an intermediate status (`ready` or `deactivated`) and again when the status changes to its final status (`active` or `canceled`). + :param friendly_name: A descriptive string that you create to describe the Sim resource. It does not need to be unique. + :param rate_plan: The SID or unique name of the [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource) to which the Sim resource should be assigned. + :param status: + :param commands_callback_method: The HTTP method we should use to call `commands_callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param commands_callback_url: The URL we should call using the `commands_callback_method` when the SIM sends a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). Your server should respond with an HTTP status code in the 200 range; any response body is ignored. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_fallback_url: The URL we should call using the `sms_fallback_method` when an error occurs while retrieving or executing the TwiML requested from `sms_url`. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_url: The URL we should call using the `sms_method` when the SIM-connected device sends an SMS message that is not a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_fallback_method: Deprecated. + :param voice_fallback_url: Deprecated. + :param voice_method: Deprecated. + :param voice_url: Deprecated. + :param reset_status: + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a [Subaccount](https://www.twilio.com/docs/iam/api/subaccounts) of the requesting Account. Only valid when the Sim resource's status is `new`. For more information, see the [Move SIMs between Subaccounts documentation](https://www.twilio.com/docs/iot/wireless/api/sim-resource#move-sims-between-subaccounts). + + :returns: The updated SimInstance + """ + return self._proxy.update( + unique_name=unique_name, + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + rate_plan=rate_plan, + status=status, + commands_callback_method=commands_callback_method, + commands_callback_url=commands_callback_url, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + reset_status=reset_status, + account_sid=account_sid, + ) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union["SimInstance.Status", object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + reset_status: Union["SimInstance.ResetStatus", object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> "SimInstance": + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the `sid` in the URL path to address the resource. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param callback_url: The URL we should call using the `callback_url` when the SIM has finished updating. When the SIM transitions from `new` to `ready` or from any status to `deactivated`, we call this URL when the status changes to an intermediate status (`ready` or `deactivated`) and again when the status changes to its final status (`active` or `canceled`). + :param friendly_name: A descriptive string that you create to describe the Sim resource. It does not need to be unique. + :param rate_plan: The SID or unique name of the [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource) to which the Sim resource should be assigned. + :param status: + :param commands_callback_method: The HTTP method we should use to call `commands_callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param commands_callback_url: The URL we should call using the `commands_callback_method` when the SIM sends a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). Your server should respond with an HTTP status code in the 200 range; any response body is ignored. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_fallback_url: The URL we should call using the `sms_fallback_method` when an error occurs while retrieving or executing the TwiML requested from `sms_url`. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_url: The URL we should call using the `sms_method` when the SIM-connected device sends an SMS message that is not a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_fallback_method: Deprecated. + :param voice_fallback_url: Deprecated. + :param voice_method: Deprecated. + :param voice_url: Deprecated. + :param reset_status: + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a [Subaccount](https://www.twilio.com/docs/iam/api/subaccounts) of the requesting Account. Only valid when the Sim resource's status is `new`. For more information, see the [Move SIMs between Subaccounts documentation](https://www.twilio.com/docs/iot/wireless/api/sim-resource#move-sims-between-subaccounts). + + :returns: The updated SimInstance + """ + return await self._proxy.update_async( + unique_name=unique_name, + callback_method=callback_method, + callback_url=callback_url, + friendly_name=friendly_name, + rate_plan=rate_plan, + status=status, + commands_callback_method=commands_callback_method, + commands_callback_url=commands_callback_url, + sms_fallback_method=sms_fallback_method, + sms_fallback_url=sms_fallback_url, + sms_method=sms_method, + sms_url=sms_url, + voice_fallback_method=voice_fallback_method, + voice_fallback_url=voice_fallback_url, + voice_method=voice_method, + voice_url=voice_url, + reset_status=reset_status, + account_sid=account_sid, + ) + + @property + def data_sessions(self) -> DataSessionList: + """ + Access the data_sessions + """ + return self._proxy.data_sessions + + @property + def usage_records(self) -> UsageRecordList: + """ + Access the usage_records + """ + return self._proxy.usage_records + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimContext(InstanceContext): + + def __init__(self, version: Version, sid: str): + """ + Initialize the SimContext + + :param version: Version that contains the resource + :param sid: The SID or the `unique_name` of the Sim resource to update. + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sid": sid, + } + self._uri = "/Sims/{sid}".format(**self._solution) + + self._data_sessions: Optional[DataSessionList] = None + self._usage_records: Optional[UsageRecordList] = None + + def delete(self) -> bool: + """ + Deletes the SimInstance + + + :returns: True if delete succeeds, False otherwise + """ + return self._version.delete( + method="DELETE", + uri=self._uri, + ) + + async def delete_async(self) -> bool: + """ + Asynchronous coroutine that deletes the SimInstance + + + :returns: True if delete succeeds, False otherwise + """ + return await self._version.delete_async( + method="DELETE", + uri=self._uri, + ) + + def fetch(self) -> SimInstance: + """ + Fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = self._version.fetch( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + async def fetch_async(self) -> SimInstance: + """ + Asynchronous coroutine to fetch the SimInstance + + + :returns: The fetched SimInstance + """ + + payload = await self._version.fetch_async( + method="GET", + uri=self._uri, + ) + + return SimInstance( + self._version, + payload, + sid=self._solution["sid"], + ) + + def update( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union["SimInstance.Status", object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + reset_status: Union["SimInstance.ResetStatus", object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the `sid` in the URL path to address the resource. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param callback_url: The URL we should call using the `callback_url` when the SIM has finished updating. When the SIM transitions from `new` to `ready` or from any status to `deactivated`, we call this URL when the status changes to an intermediate status (`ready` or `deactivated`) and again when the status changes to its final status (`active` or `canceled`). + :param friendly_name: A descriptive string that you create to describe the Sim resource. It does not need to be unique. + :param rate_plan: The SID or unique name of the [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource) to which the Sim resource should be assigned. + :param status: + :param commands_callback_method: The HTTP method we should use to call `commands_callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param commands_callback_url: The URL we should call using the `commands_callback_method` when the SIM sends a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). Your server should respond with an HTTP status code in the 200 range; any response body is ignored. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_fallback_url: The URL we should call using the `sms_fallback_method` when an error occurs while retrieving or executing the TwiML requested from `sms_url`. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_url: The URL we should call using the `sms_method` when the SIM-connected device sends an SMS message that is not a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_fallback_method: Deprecated. + :param voice_fallback_url: Deprecated. + :param voice_method: Deprecated. + :param voice_url: Deprecated. + :param reset_status: + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a [Subaccount](https://www.twilio.com/docs/iam/api/subaccounts) of the requesting Account. Only valid when the Sim resource's status is `new`. For more information, see the [Move SIMs between Subaccounts documentation](https://www.twilio.com/docs/iot/wireless/api/sim-resource#move-sims-between-subaccounts). + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + "RatePlan": rate_plan, + "Status": status, + "CommandsCallbackMethod": commands_callback_method, + "CommandsCallbackUrl": commands_callback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "ResetStatus": reset_status, + "AccountSid": account_sid, + } + ) + + payload = self._version.update( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + async def update_async( + self, + unique_name: Union[str, object] = values.unset, + callback_method: Union[str, object] = values.unset, + callback_url: Union[str, object] = values.unset, + friendly_name: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + status: Union["SimInstance.Status", object] = values.unset, + commands_callback_method: Union[str, object] = values.unset, + commands_callback_url: Union[str, object] = values.unset, + sms_fallback_method: Union[str, object] = values.unset, + sms_fallback_url: Union[str, object] = values.unset, + sms_method: Union[str, object] = values.unset, + sms_url: Union[str, object] = values.unset, + voice_fallback_method: Union[str, object] = values.unset, + voice_fallback_url: Union[str, object] = values.unset, + voice_method: Union[str, object] = values.unset, + voice_url: Union[str, object] = values.unset, + reset_status: Union["SimInstance.ResetStatus", object] = values.unset, + account_sid: Union[str, object] = values.unset, + ) -> SimInstance: + """ + Asynchronous coroutine to update the SimInstance + + :param unique_name: An application-defined string that uniquely identifies the resource. It can be used in place of the `sid` in the URL path to address the resource. + :param callback_method: The HTTP method we should use to call `callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param callback_url: The URL we should call using the `callback_url` when the SIM has finished updating. When the SIM transitions from `new` to `ready` or from any status to `deactivated`, we call this URL when the status changes to an intermediate status (`ready` or `deactivated`) and again when the status changes to its final status (`active` or `canceled`). + :param friendly_name: A descriptive string that you create to describe the Sim resource. It does not need to be unique. + :param rate_plan: The SID or unique name of the [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource) to which the Sim resource should be assigned. + :param status: + :param commands_callback_method: The HTTP method we should use to call `commands_callback_url`. Can be: `POST` or `GET`. The default is `POST`. + :param commands_callback_url: The URL we should call using the `commands_callback_method` when the SIM sends a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). Your server should respond with an HTTP status code in the 200 range; any response body is ignored. + :param sms_fallback_method: The HTTP method we should use to call `sms_fallback_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_fallback_url: The URL we should call using the `sms_fallback_method` when an error occurs while retrieving or executing the TwiML requested from `sms_url`. + :param sms_method: The HTTP method we should use to call `sms_url`. Can be: `GET` or `POST`. Default is `POST`. + :param sms_url: The URL we should call using the `sms_method` when the SIM-connected device sends an SMS message that is not a [Command](https://www.twilio.com/docs/iot/wireless/api/command-resource). + :param voice_fallback_method: Deprecated. + :param voice_fallback_url: Deprecated. + :param voice_method: Deprecated. + :param voice_url: Deprecated. + :param reset_status: + :param account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) to which the Sim resource should belong. The Account SID can only be that of the requesting Account or that of a [Subaccount](https://www.twilio.com/docs/iam/api/subaccounts) of the requesting Account. Only valid when the Sim resource's status is `new`. For more information, see the [Move SIMs between Subaccounts documentation](https://www.twilio.com/docs/iot/wireless/api/sim-resource#move-sims-between-subaccounts). + + :returns: The updated SimInstance + """ + data = values.of( + { + "UniqueName": unique_name, + "CallbackMethod": callback_method, + "CallbackUrl": callback_url, + "FriendlyName": friendly_name, + "RatePlan": rate_plan, + "Status": status, + "CommandsCallbackMethod": commands_callback_method, + "CommandsCallbackUrl": commands_callback_url, + "SmsFallbackMethod": sms_fallback_method, + "SmsFallbackUrl": sms_fallback_url, + "SmsMethod": sms_method, + "SmsUrl": sms_url, + "VoiceFallbackMethod": voice_fallback_method, + "VoiceFallbackUrl": voice_fallback_url, + "VoiceMethod": voice_method, + "VoiceUrl": voice_url, + "ResetStatus": reset_status, + "AccountSid": account_sid, + } + ) + + payload = await self._version.update_async( + method="POST", + uri=self._uri, + data=data, + ) + + return SimInstance(self._version, payload, sid=self._solution["sid"]) + + @property + def data_sessions(self) -> DataSessionList: + """ + Access the data_sessions + """ + if self._data_sessions is None: + self._data_sessions = DataSessionList( + self._version, + self._solution["sid"], + ) + return self._data_sessions + + @property + def usage_records(self) -> UsageRecordList: + """ + Access the usage_records + """ + if self._usage_records is None: + self._usage_records = UsageRecordList( + self._version, + self._solution["sid"], + ) + return self._usage_records + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class SimPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> SimInstance: + """ + Build an instance of SimInstance + + :param payload: Payload response from the API + """ + return SimInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class SimList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the SimList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/Sims" + + def stream( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[SimInstance]: + """ + Streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SimInstance.Status" status: Only return Sim resources with this status. + :param str iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param str rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param str e_id: Deprecated. + :param str sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + page_size=limits["page_size"], + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[SimInstance]: + """ + Asynchronously streams SimInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param "SimInstance.Status" status: Only return Sim resources with this status. + :param str iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param str rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param str e_id: Deprecated. + :param str sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + page_size=limits["page_size"], + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SimInstance.Status" status: Only return Sim resources with this status. + :param str iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param str rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param str e_id: Deprecated. + :param str sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[SimInstance]: + """ + Asynchronously lists SimInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param "SimInstance.Status" status: Only return Sim resources with this status. + :param str iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param str rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param str e_id: Deprecated. + :param str sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + status=status, + iccid=iccid, + rate_plan=rate_plan, + e_id=e_id, + sim_registration_code=sim_registration_code, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: Only return Sim resources with this status. + :param iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param e_id: Deprecated. + :param sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Iccid": iccid, + "RatePlan": rate_plan, + "EId": e_id, + "SimRegistrationCode": sim_registration_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return SimPage(self._version, response) + + async def page_async( + self, + status: Union["SimInstance.Status", object] = values.unset, + iccid: Union[str, object] = values.unset, + rate_plan: Union[str, object] = values.unset, + e_id: Union[str, object] = values.unset, + sim_registration_code: Union[str, object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> SimPage: + """ + Asynchronously retrieve a single page of SimInstance records from the API. + Request is executed immediately + + :param status: Only return Sim resources with this status. + :param iccid: Only return Sim resources with this ICCID. This will return a list with a maximum size of 1. + :param rate_plan: The SID or unique name of a [RatePlan resource](https://www.twilio.com/docs/iot/wireless/api/rateplan-resource). Only return Sim resources assigned to this RatePlan resource. + :param e_id: Deprecated. + :param sim_registration_code: Only return Sim resources with this registration code. This will return a list with a maximum size of 1. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of SimInstance + """ + data = values.of( + { + "Status": status, + "Iccid": iccid, + "RatePlan": rate_plan, + "EId": e_id, + "SimRegistrationCode": sim_registration_code, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return SimPage(self._version, response) + + def get_page(self, target_url: str) -> SimPage: + """ + Retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return SimPage(self._version, response) + + async def get_page_async(self, target_url: str) -> SimPage: + """ + Asynchronously retrieve a specific page of SimInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of SimInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return SimPage(self._version, response) + + def get(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: The SID or the `unique_name` of the Sim resource to update. + """ + return SimContext(self._version, sid=sid) + + def __call__(self, sid: str) -> SimContext: + """ + Constructs a SimContext + + :param sid: The SID or the `unique_name` of the Sim resource to update. + """ + return SimContext(self._version, sid=sid) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..07f9be1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/data_session.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/data_session.cpython-310.pyc new file mode 100644 index 0000000..72ff91a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/data_session.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/usage_record.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/usage_record.cpython-310.pyc new file mode 100644 index 0000000..3b97332 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/__pycache__/usage_record.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/data_session.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/data_session.py new file mode 100644 index 0000000..649eb49 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/data_session.py @@ -0,0 +1,317 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import deserialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class DataSessionInstance(InstanceResource): + """ + :ivar sid: The unique string that we created to identify the DataSession resource. + :ivar sim_sid: The SID of the [Sim resource](https://www.twilio.com/docs/iot/wireless/api/sim-resource) that the Data Session is for. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the DataSession resource. + :ivar radio_link: The generation of wireless technology that the device was using. + :ivar operator_mcc: The 'mobile country code' is the unique ID of the home country where the Data Session took place. See: [MCC/MNC lookup](http://mcc-mnc.com/). + :ivar operator_mnc: The 'mobile network code' is the unique ID specific to the mobile operator network where the Data Session took place. + :ivar operator_country: The three letter country code representing where the device's Data Session took place. This is determined by looking up the `operator_mcc`. + :ivar operator_name: The friendly name of the mobile operator network that the [SIM](https://www.twilio.com/docs/iot/wireless/api/sim-resource)-connected device is attached to. This is determined by looking up the `operator_mnc`. + :ivar cell_id: The unique ID of the cellular tower that the device was attached to at the moment when the Data Session was last updated. + :ivar cell_location_estimate: An object that describes the estimated location in latitude and longitude where the device's Data Session took place. The location is derived from the `cell_id` when the Data Session was last updated. See [Cell Location Estimate Object](https://www.twilio.com/docs/iot/wireless/api/datasession-resource#cell-location-estimate-object). + :ivar packets_uploaded: The number of packets uploaded by the device between the `start` time and when the Data Session was last updated. + :ivar packets_downloaded: The number of packets downloaded by the device between the `start` time and when the Data Session was last updated. + :ivar last_updated: The date that the resource was last updated, given as GMT in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar start: The date that the Data Session started, given as GMT in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar end: The date that the record ended, given as GMT in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar imei: The 'international mobile equipment identity' is the unique ID of the device using the SIM to connect. An IMEI is a 15-digit string: 14 digits for the device identifier plus a check digit calculated using the Luhn formula. + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sim_sid: str): + super().__init__(version) + + self.sid: Optional[str] = payload.get("sid") + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.radio_link: Optional[str] = payload.get("radio_link") + self.operator_mcc: Optional[str] = payload.get("operator_mcc") + self.operator_mnc: Optional[str] = payload.get("operator_mnc") + self.operator_country: Optional[str] = payload.get("operator_country") + self.operator_name: Optional[str] = payload.get("operator_name") + self.cell_id: Optional[str] = payload.get("cell_id") + self.cell_location_estimate: Optional[Dict[str, object]] = payload.get( + "cell_location_estimate" + ) + self.packets_uploaded: Optional[int] = deserialize.integer( + payload.get("packets_uploaded") + ) + self.packets_downloaded: Optional[int] = deserialize.integer( + payload.get("packets_downloaded") + ) + self.last_updated: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("last_updated") + ) + self.start: Optional[datetime] = deserialize.iso8601_datetime( + payload.get("start") + ) + self.end: Optional[datetime] = deserialize.iso8601_datetime(payload.get("end")) + self.imei: Optional[str] = payload.get("imei") + + self._solution = { + "sim_sid": sim_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class DataSessionPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> DataSessionInstance: + """ + Build an instance of DataSessionInstance + + :param payload: Payload response from the API + """ + return DataSessionInstance( + self._version, payload, sim_sid=self._solution["sim_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class DataSessionList(ListResource): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the DataSessionList + + :param version: Version that contains the resource + :param sim_sid: The SID of the [Sim resource](https://www.twilio.com/docs/iot/wireless/api/sim-resource) with the Data Sessions to read. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + self._uri = "/Sims/{sim_sid}/DataSessions".format(**self._solution) + + def stream( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[DataSessionInstance]: + """ + Streams DataSessionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page(page_size=limits["page_size"]) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[DataSessionInstance]: + """ + Asynchronously streams DataSessionInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async(page_size=limits["page_size"]) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DataSessionInstance]: + """ + Lists DataSessionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[DataSessionInstance]: + """ + Asynchronously lists DataSessionInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DataSessionPage: + """ + Retrieve a single page of DataSessionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DataSessionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return DataSessionPage(self._version, response, self._solution) + + async def page_async( + self, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> DataSessionPage: + """ + Asynchronously retrieve a single page of DataSessionInstance records from the API. + Request is executed immediately + + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of DataSessionInstance + """ + data = values.of( + { + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return DataSessionPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> DataSessionPage: + """ + Retrieve a specific page of DataSessionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DataSessionInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return DataSessionPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> DataSessionPage: + """ + Asynchronously retrieve a specific page of DataSessionInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of DataSessionInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return DataSessionPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/usage_record.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/usage_record.py new file mode 100644 index 0000000..8b1db6d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/sim/usage_record.py @@ -0,0 +1,343 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UsageRecordInstance(InstanceResource): + + class Granularity(object): + HOURLY = "hourly" + DAILY = "daily" + ALL = "all" + + """ + :ivar sim_sid: The SID of the [Sim resource](https://www.twilio.com/docs/iot/wireless/api/sim-resource) that this Usage Record is for. + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the UsageRecord resource. + :ivar period: The time period for which the usage is reported. Contains `start` and `end` datetime values given as GMT in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar commands: An object that describes the SIM's usage of Commands during the specified period. See [Commands Usage Object](https://www.twilio.com/docs/iot/wireless/api/sim-usagerecord-resource#commands-usage-object). + :ivar data: An object that describes the SIM's data usage during the specified period. See [Data Usage Object](https://www.twilio.com/docs/iot/wireless/api/sim-usagerecord-resource#data-usage-object). + """ + + def __init__(self, version: Version, payload: Dict[str, Any], sim_sid: str): + super().__init__(version) + + self.sim_sid: Optional[str] = payload.get("sim_sid") + self.account_sid: Optional[str] = payload.get("account_sid") + self.period: Optional[Dict[str, object]] = payload.get("period") + self.commands: Optional[Dict[str, object]] = payload.get("commands") + self.data: Optional[Dict[str, object]] = payload.get("data") + + self._solution = { + "sim_sid": sim_sid, + } + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + context = " ".join("{}={}".format(k, v) for k, v in self._solution.items()) + return "".format(context) + + +class UsageRecordPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UsageRecordInstance: + """ + Build an instance of UsageRecordInstance + + :param payload: Payload response from the API + """ + return UsageRecordInstance( + self._version, payload, sim_sid=self._solution["sim_sid"] + ) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UsageRecordList(ListResource): + + def __init__(self, version: Version, sim_sid: str): + """ + Initialize the UsageRecordList + + :param version: Version that contains the resource + :param sim_sid: The SID of the [Sim resource](https://www.twilio.com/docs/iot/wireless/api/sim-resource) to read the usage from. + + """ + super().__init__(version) + + # Path Solution + self._solution = { + "sim_sid": sim_sid, + } + self._uri = "/Sims/{sim_sid}/UsageRecords".format(**self._solution) + + def stream( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UsageRecordInstance]: + """ + Streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param datetime start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + end=end, start=start, granularity=granularity, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UsageRecordInstance]: + """ + Asynchronously streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param datetime start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + end=end, start=start, granularity=granularity, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param datetime start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + end=end, + start=start, + granularity=granularity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Asynchronously lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param datetime start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + end=end, + start=start, + granularity=granularity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "End": serialize.iso8601_datetime(end), + "Start": serialize.iso8601_datetime(start), + "Granularity": granularity, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UsageRecordPage(self._version, response, self._solution) + + async def page_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Asynchronously retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param end: Only include usage that occurred on or before this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is the current time. + :param start: Only include usage that has occurred on or after this date, specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). The default is one month before the `end` parameter value. + :param granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. The default is `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "End": serialize.iso8601_datetime(end), + "Start": serialize.iso8601_datetime(start), + "Granularity": granularity, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UsageRecordPage(self._version, response, self._solution) + + def get_page(self, target_url: str) -> UsageRecordPage: + """ + Retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UsageRecordPage(self._version, response, self._solution) + + async def get_page_async(self, target_url: str) -> UsageRecordPage: + """ + Asynchronously retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UsageRecordPage(self._version, response, self._solution) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/usage_record.py b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/usage_record.py new file mode 100644 index 0000000..43e3446 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/rest/wireless/v1/usage_record.py @@ -0,0 +1,330 @@ +r""" + This code was generated by + ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ + | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ + | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ + + Twilio - Wireless + This is the public Twilio REST API. + + NOTE: This class is auto generated by OpenAPI Generator. + https://openapi-generator.tech + Do not edit the class manually. +""" + +from datetime import datetime +from typing import Any, Dict, List, Optional, Union, Iterator, AsyncIterator +from twilio.base import serialize, values + +from twilio.base.instance_resource import InstanceResource +from twilio.base.list_resource import ListResource +from twilio.base.version import Version +from twilio.base.page import Page + + +class UsageRecordInstance(InstanceResource): + + class Granularity(object): + HOURLY = "hourly" + DAILY = "daily" + ALL = "all" + + """ + :ivar account_sid: The SID of the [Account](https://www.twilio.com/docs/iam/api/account) that created the AccountUsageRecord resource. + :ivar period: The time period for which usage is reported. Contains `start` and `end` properties that describe the period using GMT date-time values specified in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + :ivar commands: An object that describes the aggregated Commands usage for all SIMs during the specified period. See [Commands Usage Object](https://www.twilio.com/docs/iot/wireless/api/account-usagerecord-resource#commands-usage-object). + :ivar data: An object that describes the aggregated Data usage for all SIMs over the period. See [Data Usage Object](https://www.twilio.com/docs/iot/wireless/api/account-usagerecord-resource#data-usage-object). + """ + + def __init__(self, version: Version, payload: Dict[str, Any]): + super().__init__(version) + + self.account_sid: Optional[str] = payload.get("account_sid") + self.period: Optional[Dict[str, object]] = payload.get("period") + self.commands: Optional[Dict[str, object]] = payload.get("commands") + self.data: Optional[Dict[str, object]] = payload.get("data") + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + + return "" + + +class UsageRecordPage(Page): + + def get_instance(self, payload: Dict[str, Any]) -> UsageRecordInstance: + """ + Build an instance of UsageRecordInstance + + :param payload: Payload response from the API + """ + return UsageRecordInstance(self._version, payload) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" + + +class UsageRecordList(ListResource): + + def __init__(self, version: Version): + """ + Initialize the UsageRecordList + + :param version: Version that contains the resource + + """ + super().__init__(version) + + self._uri = "/UsageRecords" + + def stream( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> Iterator[UsageRecordInstance]: + """ + Streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param datetime start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = self.page( + end=end, start=start, granularity=granularity, page_size=limits["page_size"] + ) + + return self._version.stream(page, limits["limit"]) + + async def stream_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> AsyncIterator[UsageRecordInstance]: + """ + Asynchronously streams UsageRecordInstance records from the API as a generator stream. + This operation lazily loads records as efficiently as possible until the limit + is reached. + The results are returned as a generator, so this operation is memory efficient. + + :param datetime end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param datetime start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. stream() + guarantees to never return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, stream() will attempt to read the + limit with the most efficient page size, i.e. min(limit, 1000) + + :returns: Generator that will yield up to limit results + """ + limits = self._version.read_limits(limit, page_size) + page = await self.page_async( + end=end, start=start, granularity=granularity, page_size=limits["page_size"] + ) + + return self._version.stream_async(page, limits["limit"]) + + def list( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param datetime start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return list( + self.stream( + end=end, + start=start, + granularity=granularity, + limit=limit, + page_size=page_size, + ) + ) + + async def list_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + limit: Optional[int] = None, + page_size: Optional[int] = None, + ) -> List[UsageRecordInstance]: + """ + Asynchronously lists UsageRecordInstance records from the API as a list. + Unlike stream(), this operation is eager and will load `limit` records into + memory before returning. + + :param datetime end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param datetime start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param "UsageRecordInstance.Granularity" granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param limit: Upper limit for the number of records to return. list() guarantees + never to return more than limit. Default is no limit + :param page_size: Number of records to fetch per request, when not set will use + the default value of 50 records. If no page_size is defined + but a limit is defined, list() will attempt to read the limit + with the most efficient page size, i.e. min(limit, 1000) + + :returns: list that will contain up to limit results + """ + return [ + record + async for record in await self.stream_async( + end=end, + start=start, + granularity=granularity, + limit=limit, + page_size=page_size, + ) + ] + + def page( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "End": serialize.iso8601_datetime(end), + "Start": serialize.iso8601_datetime(start), + "Granularity": granularity, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = self._version.page(method="GET", uri=self._uri, params=data) + return UsageRecordPage(self._version, response) + + async def page_async( + self, + end: Union[datetime, object] = values.unset, + start: Union[datetime, object] = values.unset, + granularity: Union["UsageRecordInstance.Granularity", object] = values.unset, + page_token: Union[str, object] = values.unset, + page_number: Union[int, object] = values.unset, + page_size: Union[int, object] = values.unset, + ) -> UsageRecordPage: + """ + Asynchronously retrieve a single page of UsageRecordInstance records from the API. + Request is executed immediately + + :param end: Only include usage that has occurred on or before this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param start: Only include usage that has occurred on or after this date. Format is [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). + :param granularity: How to summarize the usage by time. Can be: `daily`, `hourly`, or `all`. A value of `all` returns one Usage Record that describes the usage for the entire period. + :param page_token: PageToken provided by the API + :param page_number: Page Number, this value is simply for client state + :param page_size: Number of records to return, defaults to 50 + + :returns: Page of UsageRecordInstance + """ + data = values.of( + { + "End": serialize.iso8601_datetime(end), + "Start": serialize.iso8601_datetime(start), + "Granularity": granularity, + "PageToken": page_token, + "Page": page_number, + "PageSize": page_size, + } + ) + + response = await self._version.page_async( + method="GET", uri=self._uri, params=data + ) + return UsageRecordPage(self._version, response) + + def get_page(self, target_url: str) -> UsageRecordPage: + """ + Retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = self._version.domain.twilio.request("GET", target_url) + return UsageRecordPage(self._version, response) + + async def get_page_async(self, target_url: str) -> UsageRecordPage: + """ + Asynchronously retrieve a specific page of UsageRecordInstance records from the API. + Request is executed immediately + + :param target_url: API-generated URL for the requested results page + + :returns: Page of UsageRecordInstance + """ + response = await self._version.domain.twilio.request_async("GET", target_url) + return UsageRecordPage(self._version, response) + + def __repr__(self) -> str: + """ + Provide a friendly representation + + :returns: Machine friendly representation + """ + return "" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__init__.py b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__init__.py new file mode 100644 index 0000000..70a4df8 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__init__.py @@ -0,0 +1,140 @@ +import json +import re +import xml.etree.ElementTree as ET + + +def lower_camel(string): + if not string or "_" not in string: + return string + + result = "".join([x.title() for x in string.split("_")]) + return result[0].lower() + result[1:] + + +def format_language(language): + """ + Attempt to format language parameter as 'ww-WW'. + + :param string language: language parameter + """ + if not language: + return language + + if not re.match("^[a-zA-Z]{2}[_-][a-zA-Z]{2}$", language): + raise TwiMLException("Invalid value for language parameter.") + + return language[0:2].lower() + "-" + language[3:5].upper() + + +class TwiMLException(Exception): + pass + + +class TwiML(object): + MAP = { + "from_": "from", + "xml_lang": "xml:lang", + "interpret_as": "interpret-as", + "for_": "for", + "break_": "break", + } + + def __init__(self, **kwargs): + self.name = self.__class__.__name__ + self.value = None + self.verbs = [] + self.attrs = {} + + for k, v in kwargs.items(): + if v is not None: + self.attrs[lower_camel(self.MAP.get(k, k))] = v + + def __str__(self): + return self.to_xml() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + return False + + def to_xml(self, xml_declaration=True): + """ + Return the contents of this verb as an XML string + + :param bool xml_declaration: Include the XML declaration. Defaults to True + """ + xml = ET.tostring(self.xml(), encoding="utf-8").decode("utf-8") + return ( + '{}'.format(xml) + if xml_declaration + else xml + ) + + def append(self, verb): + """ + Add a TwiML doc + + :param verb: TwiML Document + + :returns: self + """ + self.nest(verb) + return self + + def nest(self, verb): + """ + Add a TwiML doc. Unlike `append()`, this returns the created verb. + + :param verb: TwiML Document + + :returns: the TwiML verb + """ + if not isinstance(verb, TwiML) and not isinstance(verb, str): + raise TwiMLException("Only nesting of TwiML and strings are allowed") + + self.verbs.append(verb) + return verb + + def xml(self): + el = ET.Element(self.name) + + keys = self.attrs.keys() + keys = sorted(keys) + for a in keys: + value = self.attrs[a] + + if isinstance(value, bool): + el.set(a, str(value).lower()) + else: + el.set(a, str(value)) + + if self.value: + if isinstance(self.value, dict): + self.value = json.dumps(self.value) + + el.text = self.value + + last_child = None + + for verb in self.verbs: + if isinstance(verb, str): + if last_child is not None: + last_child.tail = verb + else: + el.text = verb + else: + last_child = verb.xml() + el.append(last_child) + + return el + + def add_child(self, name, value=None, **kwargs): + return self.nest(GenericNode(name, value, **kwargs)) + + +class GenericNode(TwiML): + def __init__(self, name, value, **kwargs): + super(GenericNode, self).__init__(**kwargs) + self.name = name + self.value = value diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..6024f8b Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/fax_response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/fax_response.cpython-310.pyc new file mode 100644 index 0000000..fa3166c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/fax_response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/messaging_response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/messaging_response.cpython-310.pyc new file mode 100644 index 0000000..356f341 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/messaging_response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/voice_response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/voice_response.cpython-310.pyc new file mode 100644 index 0000000..576c6cf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/__pycache__/voice_response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/fax_response.py b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/fax_response.py new file mode 100644 index 0000000..89233bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/fax_response.py @@ -0,0 +1,59 @@ +# coding=utf-8 +r""" +This code was generated by +\ / _ _ _| _ _ + | (_)\/(_)(_|\/| |(/_ v1.0.0 + / / +""" + +from twilio.twiml import ( + TwiML, +) + + +class FaxResponse(TwiML): + """ TwiML for Faxes""" + + def __init__(self, **kwargs): + super(FaxResponse, self).__init__(**kwargs) + self.name = "Response" + + def receive( + self, + action=None, + method=None, + media_type=None, + page_size=None, + store_media=None, + **kwargs + ): + """ + Create a element + + :param action: Receive action URL + :param method: Receive action URL method + :param media_type: The media type used to store media in the fax media store + :param page_size: What size to interpret received pages as + :param store_media: Whether or not to store received media in the fax media store + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Receive( + action=action, + method=method, + media_type=media_type, + page_size=page_size, + store_media=store_media, + **kwargs + ) + ) + + +class Receive(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Receive, self).__init__(**kwargs) + self.name = "Receive" diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/messaging_response.py b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/messaging_response.py new file mode 100644 index 0000000..ec36c74 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/messaging_response.py @@ -0,0 +1,125 @@ +# coding=utf-8 +r""" +This code was generated by +\ / _ _ _| _ _ + | (_)\/(_)(_|\/| |(/_ v1.0.0 + / / +""" + +from twilio.twiml import ( + TwiML, +) + + +class MessagingResponse(TwiML): + """ TwiML for Messages""" + + def __init__(self, **kwargs): + super(MessagingResponse, self).__init__(**kwargs) + self.name = "Response" + + def message( + self, + body=None, + to=None, + from_=None, + action=None, + method=None, + status_callback=None, + **kwargs + ): + """ + Create a element + + :param body: Message Body + :param to: Phone Number to send Message to + :param from: Phone Number to send Message from + :param action: A URL specifying where Twilio should send status callbacks for the created outbound message. + :param method: Action URL Method + :param status_callback: Status callback URL. Deprecated in favor of action. + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Message( + body=body, + to=to, + from_=from_, + action=action, + method=method, + status_callback=status_callback, + **kwargs + ) + ) + + def redirect(self, url, method=None, **kwargs): + """ + Create a element + + :param url: Redirect URL + :param method: Redirect URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Redirect(url, method=method, **kwargs)) + + +class Redirect(TwiML): + """ TwiML Verb""" + + def __init__(self, url, **kwargs): + super(Redirect, self).__init__(**kwargs) + self.name = "Redirect" + self.value = url + + +class Message(TwiML): + """ TwiML Verb""" + + def __init__(self, body=None, **kwargs): + super(Message, self).__init__(**kwargs) + self.name = "Message" + if body: + self.value = body + + def body(self, message, **kwargs): + """ + Create a element + + :param message: Message Body + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Body(message, **kwargs)) + + def media(self, url, **kwargs): + """ + Create a element + + :param url: Media URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Media(url, **kwargs)) + + +class Media(TwiML): + """ TwiML Noun""" + + def __init__(self, url, **kwargs): + super(Media, self).__init__(**kwargs) + self.name = "Media" + self.value = url + + +class Body(TwiML): + """ TwiML Noun""" + + def __init__(self, message, **kwargs): + super(Body, self).__init__(**kwargs) + self.name = "Body" + self.value = message diff --git a/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/voice_response.py b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/voice_response.py new file mode 100644 index 0000000..6beb109 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/twilio/twiml/voice_response.py @@ -0,0 +1,2640 @@ +# coding=utf-8 +r""" +This code was generated by +\ / _ _ _| _ _ + | (_)\/(_)(_|\/| |(/_ v1.0.0 + / / +""" + +from twilio.twiml import ( + TwiML, +) + + +class VoiceResponse(TwiML): + """ TwiML for Voice""" + + def __init__(self, **kwargs): + super(VoiceResponse, self).__init__(**kwargs) + self.name = "Response" + + def connect(self, action=None, method=None, **kwargs): + """ + Create a element + + :param action: Action URL + :param method: Action URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Connect(action=action, method=method, **kwargs)) + + def dial( + self, + number=None, + action=None, + method=None, + timeout=None, + hangup_on_star=None, + time_limit=None, + caller_id=None, + record=None, + trim=None, + recording_status_callback=None, + recording_status_callback_method=None, + recording_status_callback_event=None, + answer_on_bridge=None, + ring_tone=None, + recording_track=None, + sequential=None, + refer_url=None, + refer_method=None, + **kwargs + ): + """ + Create a element + + :param number: Phone number to dial + :param action: Action URL + :param method: Action URL method + :param timeout: Time to wait for answer + :param hangup_on_star: Hangup call on star press + :param time_limit: Max time length + :param caller_id: Caller ID to display + :param record: Record the call + :param trim: Trim the recording + :param recording_status_callback: Recording status callback URL + :param recording_status_callback_method: Recording status callback URL method + :param recording_status_callback_event: Recording status callback events + :param answer_on_bridge: Preserve the ringing behavior of the inbound call until the Dialed call picks up + :param ring_tone: Ringtone allows you to override the ringback tone that Twilio will play back to the caller while executing the Dial + :param recording_track: To indicate which audio track should be recorded + :param sequential: Used to determine if child TwiML nouns should be dialed in order, one after the other (sequential) or dial all at once (parallel). Default is false, parallel + :param refer_url: Webhook that will receive future SIP REFER requests + :param refer_method: The HTTP method to use for the refer Webhook + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Dial( + number=number, + action=action, + method=method, + timeout=timeout, + hangup_on_star=hangup_on_star, + time_limit=time_limit, + caller_id=caller_id, + record=record, + trim=trim, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + recording_status_callback_event=recording_status_callback_event, + answer_on_bridge=answer_on_bridge, + ring_tone=ring_tone, + recording_track=recording_track, + sequential=sequential, + refer_url=refer_url, + refer_method=refer_method, + **kwargs + ) + ) + + def echo(self, **kwargs): + """ + Create a element + + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Echo(**kwargs)) + + def enqueue( + self, + name=None, + action=None, + max_queue_size=None, + method=None, + wait_url=None, + wait_url_method=None, + workflow_sid=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name + :param action: Action URL + :param max_queue_size: Maximum size of queue + :param method: Action URL method + :param wait_url: Wait URL + :param wait_url_method: Wait URL method + :param workflow_sid: TaskRouter Workflow SID + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Enqueue( + name=name, + action=action, + max_queue_size=max_queue_size, + method=method, + wait_url=wait_url, + wait_url_method=wait_url_method, + workflow_sid=workflow_sid, + **kwargs + ) + ) + + def gather( + self, + input=None, + action=None, + method=None, + timeout=None, + speech_timeout=None, + max_speech_time=None, + profanity_filter=None, + finish_on_key=None, + num_digits=None, + partial_result_callback=None, + partial_result_callback_method=None, + language=None, + hints=None, + barge_in=None, + debug=None, + action_on_empty_result=None, + speech_model=None, + enhanced=None, + **kwargs + ): + """ + Create a element + + :param input: Input type Twilio should accept + :param action: Action URL + :param method: Action URL method + :param timeout: Time to wait to gather input + :param speech_timeout: Time to wait to gather speech input and it should be either auto or a positive integer. + :param max_speech_time: Max allowed time for speech input + :param profanity_filter: Profanity Filter on speech + :param finish_on_key: Finish gather on key + :param num_digits: Number of digits to collect + :param partial_result_callback: Partial result callback URL + :param partial_result_callback_method: Partial result callback URL method + :param language: Language to use + :param hints: Speech recognition hints + :param barge_in: Stop playing media upon speech + :param debug: Allow debug for gather + :param action_on_empty_result: Force webhook to the action URL event if there is no input + :param speech_model: Specify the model that is best suited for your use case + :param enhanced: Use enhanced speech model + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Gather( + input=input, + action=action, + method=method, + timeout=timeout, + speech_timeout=speech_timeout, + max_speech_time=max_speech_time, + profanity_filter=profanity_filter, + finish_on_key=finish_on_key, + num_digits=num_digits, + partial_result_callback=partial_result_callback, + partial_result_callback_method=partial_result_callback_method, + language=language, + hints=hints, + barge_in=barge_in, + debug=debug, + action_on_empty_result=action_on_empty_result, + speech_model=speech_model, + enhanced=enhanced, + **kwargs + ) + ) + + def hangup(self, **kwargs): + """ + Create a element + + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Hangup(**kwargs)) + + def leave(self, **kwargs): + """ + Create a element + + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Leave(**kwargs)) + + def pause(self, length=None, **kwargs): + """ + Create a element + + :param length: Length in seconds to pause + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Pause(length=length, **kwargs)) + + def play(self, url=None, loop=None, digits=None, **kwargs): + """ + Create a element + + :param url: Media URL + :param loop: Times to loop media + :param digits: Play DTMF tones for digits + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Play(url=url, loop=loop, digits=digits, **kwargs)) + + def queue( + self, + name, + url=None, + method=None, + reservation_sid=None, + post_work_activity_sid=None, + **kwargs + ): + """ + Create a element + + :param name: Queue name + :param url: Action URL + :param method: Action URL method + :param reservation_sid: TaskRouter Reservation SID + :param post_work_activity_sid: TaskRouter Activity SID + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Queue( + name, + url=url, + method=method, + reservation_sid=reservation_sid, + post_work_activity_sid=post_work_activity_sid, + **kwargs + ) + ) + + def record( + self, + action=None, + method=None, + timeout=None, + finish_on_key=None, + max_length=None, + play_beep=None, + trim=None, + recording_status_callback=None, + recording_status_callback_method=None, + recording_status_callback_event=None, + transcribe=None, + transcribe_callback=None, + **kwargs + ): + """ + Create a element + + :param action: Action URL + :param method: Action URL method + :param timeout: Timeout to begin recording + :param finish_on_key: Finish recording on key + :param max_length: Max time to record in seconds + :param play_beep: Play beep + :param trim: Trim the recording + :param recording_status_callback: Status callback URL + :param recording_status_callback_method: Status callback URL method + :param recording_status_callback_event: Recording status callback events + :param transcribe: Transcribe the recording + :param transcribe_callback: Transcribe callback URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Record( + action=action, + method=method, + timeout=timeout, + finish_on_key=finish_on_key, + max_length=max_length, + play_beep=play_beep, + trim=trim, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + recording_status_callback_event=recording_status_callback_event, + transcribe=transcribe, + transcribe_callback=transcribe_callback, + **kwargs + ) + ) + + def redirect(self, url, method=None, **kwargs): + """ + Create a element + + :param url: Redirect URL + :param method: Redirect URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Redirect(url, method=method, **kwargs)) + + def reject(self, reason=None, **kwargs): + """ + Create a element + + :param reason: Rejection reason + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Reject(reason=reason, **kwargs)) + + def say(self, message=None, voice=None, loop=None, language=None, **kwargs): + """ + Create a element + + :param message: Message to say + :param voice: Voice to use + :param loop: Times to loop message + :param language: Message language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Say(message=message, voice=voice, loop=loop, language=language, **kwargs) + ) + + def sms( + self, + message, + to=None, + from_=None, + action=None, + method=None, + status_callback=None, + **kwargs + ): + """ + Create a element + + :param message: Message body + :param to: Number to send message to + :param from: Number to send message from + :param action: Action URL + :param method: Action URL method + :param status_callback: Status callback URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Sms( + message, + to=to, + from_=from_, + action=action, + method=method, + status_callback=status_callback, + **kwargs + ) + ) + + def pay( + self, + input=None, + action=None, + bank_account_type=None, + status_callback=None, + status_callback_method=None, + timeout=None, + max_attempts=None, + security_code=None, + postal_code=None, + min_postal_code_length=None, + payment_connector=None, + payment_method=None, + token_type=None, + charge_amount=None, + currency=None, + description=None, + valid_card_types=None, + language=None, + **kwargs + ): + """ + Create a element + + :param input: Input type Twilio should accept + :param action: Action URL + :param bank_account_type: Bank account type for ach transactions. If set, payment method attribute must be provided and value should be set to ach-debit. defaults to consumer-checking + :param status_callback: Status callback URL + :param status_callback_method: Status callback method + :param timeout: Time to wait to gather input + :param max_attempts: Maximum number of allowed retries when gathering input + :param security_code: Prompt for security code + :param postal_code: Prompt for postal code and it should be true/false or default postal code + :param min_postal_code_length: Prompt for minimum postal code length + :param payment_connector: Unique name for payment connector + :param payment_method: Payment method to be used. defaults to credit-card + :param token_type: Type of token + :param charge_amount: Amount to process. If value is greater than 0 then make the payment else create a payment token + :param currency: Currency of the amount attribute + :param description: Details regarding the payment + :param valid_card_types: Comma separated accepted card types + :param language: Language to use + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Pay( + input=input, + action=action, + bank_account_type=bank_account_type, + status_callback=status_callback, + status_callback_method=status_callback_method, + timeout=timeout, + max_attempts=max_attempts, + security_code=security_code, + postal_code=postal_code, + min_postal_code_length=min_postal_code_length, + payment_connector=payment_connector, + payment_method=payment_method, + token_type=token_type, + charge_amount=charge_amount, + currency=currency, + description=description, + valid_card_types=valid_card_types, + language=language, + **kwargs + ) + ) + + def prompt( + self, + for_=None, + error_type=None, + card_type=None, + attempt=None, + require_matching_inputs=None, + **kwargs + ): + """ + Create a element + + :param for_: Name of the payment source data element + :param error_type: Type of error + :param card_type: Type of the credit card + :param attempt: Current attempt count + :param require_matching_inputs: Require customer to input requested information twice and verify matching. + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Prompt( + for_=for_, + error_type=error_type, + card_type=card_type, + attempt=attempt, + require_matching_inputs=require_matching_inputs, + **kwargs + ) + ) + + def start(self, action=None, method=None, **kwargs): + """ + Create a element + + :param action: Action URL + :param method: Action URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Start(action=action, method=method, **kwargs)) + + def stop(self, **kwargs): + """ + Create a element + + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Stop(**kwargs)) + + def refer(self, action=None, method=None, **kwargs): + """ + Create a element + + :param action: Action URL + :param method: Action URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Refer(action=action, method=method, **kwargs)) + + +class Refer(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Refer, self).__init__(**kwargs) + self.name = "Refer" + + def sip(self, sip_url, **kwargs): + """ + Create a element + + :param sip_url: SIP URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(ReferSip(sip_url, **kwargs)) + + +class ReferSip(TwiML): + """ TwiML Noun used in """ + + def __init__(self, sip_url, **kwargs): + super(ReferSip, self).__init__(**kwargs) + self.name = "Sip" + self.value = sip_url + + +class Stop(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Stop, self).__init__(**kwargs) + self.name = "Stop" + + def stream( + self, + name=None, + connector_name=None, + url=None, + track=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name given to the Stream + :param connector_name: Unique name for Stream Connector + :param url: URL of the remote service where the Stream is routed + :param track: Track to be streamed to remote service + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Stream( + name=name, + connector_name=connector_name, + url=url, + track=track, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + def siprec( + self, + name=None, + connector_name=None, + track=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name given to SIPREC + :param connector_name: Unique name for Connector + :param track: Track to be streamed to remote service + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Siprec( + name=name, + connector_name=connector_name, + track=track, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + +class Siprec(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(Siprec, self).__init__(**kwargs) + self.name = "Siprec" + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Parameter(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(Parameter, self).__init__(**kwargs) + self.name = "Parameter" + + +class Stream(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(Stream, self).__init__(**kwargs) + self.name = "Stream" + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Start(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Start, self).__init__(**kwargs) + self.name = "Start" + + def stream( + self, + name=None, + connector_name=None, + url=None, + track=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name given to the Stream + :param connector_name: Unique name for Stream Connector + :param url: URL of the remote service where the Stream is routed + :param track: Track to be streamed to remote service + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Stream( + name=name, + connector_name=connector_name, + url=url, + track=track, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + def siprec( + self, + name=None, + connector_name=None, + track=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name given to SIPREC + :param connector_name: Unique name for Connector + :param track: Track to be streamed to remote service + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Siprec( + name=name, + connector_name=connector_name, + track=track, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + +class Prompt(TwiML): + """ Twiml Verb""" + + def __init__(self, **kwargs): + super(Prompt, self).__init__(**kwargs) + self.name = "Prompt" + + def say(self, message=None, voice=None, loop=None, language=None, **kwargs): + """ + Create a element + + :param message: Message to say + :param voice: Voice to use + :param loop: Times to loop message + :param language: Message language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Say(message=message, voice=voice, loop=loop, language=language, **kwargs) + ) + + def play(self, url=None, loop=None, digits=None, **kwargs): + """ + Create a element + + :param url: Media URL + :param loop: Times to loop media + :param digits: Play DTMF tones for digits + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Play(url=url, loop=loop, digits=digits, **kwargs)) + + def pause(self, length=None, **kwargs): + """ + Create a element + + :param length: Length in seconds to pause + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Pause(length=length, **kwargs)) + + +class Pause(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Pause, self).__init__(**kwargs) + self.name = "Pause" + + +class Play(TwiML): + """ TwiML Verb""" + + def __init__(self, url=None, **kwargs): + super(Play, self).__init__(**kwargs) + self.name = "Play" + if url: + self.value = url + + +class Say(TwiML): + """ TwiML Verb""" + + def __init__(self, message=None, **kwargs): + super(Say, self).__init__(**kwargs) + self.name = "Say" + if message: + self.value = message + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def p(self, words=None, **kwargs): + """ + Create a

    element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns:

    element + """ + return self.nest(SsmlP(words=words, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def s(self, words=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlS(words=words, **kwargs)) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlW(TwiML): + """Improving Pronunciation by Specifying Parts of Speech in """ + + def __init__(self, words=None, **kwargs): + super(SsmlW, self).__init__(**kwargs) + self.name = "w" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + +class SsmlSub(TwiML): + """Pronouncing Acronyms and Abbreviations in """ + + def __init__(self, words, **kwargs): + super(SsmlSub, self).__init__(**kwargs) + self.name = "sub" + self.value = words + + +class SsmlSayAs(TwiML): + """Controlling How Special Types of Words Are Spoken in """ + + def __init__(self, words, **kwargs): + super(SsmlSayAs, self).__init__(**kwargs) + self.name = "say-as" + self.value = words + + +class SsmlProsody(TwiML): + """Controling Volume, Speaking Rate, and Pitch in """ + + def __init__(self, words=None, **kwargs): + super(SsmlProsody, self).__init__(**kwargs) + self.name = "prosody" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def p(self, words=None, **kwargs): + """ + Create a

    element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns:

    element + """ + return self.nest(SsmlP(words=words, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def s(self, words=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlS(words=words, **kwargs)) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlS(TwiML): + """Adding A Pause Between Sentences in """ + + def __init__(self, words=None, **kwargs): + super(SsmlS, self).__init__(**kwargs) + self.name = "s" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlPhoneme(TwiML): + """Using Phonetic Pronunciation in """ + + def __init__(self, words, **kwargs): + super(SsmlPhoneme, self).__init__(**kwargs) + self.name = "phoneme" + self.value = words + + +class SsmlLang(TwiML): + """Specifying Another Language for Specific Words in """ + + def __init__(self, words=None, **kwargs): + super(SsmlLang, self).__init__(**kwargs) + self.name = "lang" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def p(self, words=None, **kwargs): + """ + Create a

    element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns:

    element + """ + return self.nest(SsmlP(words=words, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def s(self, words=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlS(words=words, **kwargs)) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlP(TwiML): + """Adding a Pause Between Paragraphs in """ + + def __init__(self, words=None, **kwargs): + super(SsmlP, self).__init__(**kwargs) + self.name = "p" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def s(self, words=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlS(words=words, **kwargs)) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlEmphasis(TwiML): + """Emphasizing Words in """ + + def __init__(self, words=None, **kwargs): + super(SsmlEmphasis, self).__init__(**kwargs) + self.name = "emphasis" + if words: + self.value = words + + def break_(self, strength=None, time=None, **kwargs): + """ + Create a element + + :param strength: Set a pause based on strength + :param time: Set a pause to a specific length of time in seconds or milliseconds, available values: [number]s, [number]ms + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlBreak(strength=strength, time=time, **kwargs)) + + def emphasis(self, words=None, level=None, **kwargs): + """ + Create a element + + :param words: Words to emphasize + :param level: Specify the degree of emphasis + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlEmphasis(words=words, level=level, **kwargs)) + + def lang(self, words=None, xml_lang=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param xml:lang: Specify the language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlLang(words=words, xml_lang=xml_lang, **kwargs)) + + def phoneme(self, words, alphabet=None, ph=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param alphabet: Specify the phonetic alphabet + :param ph: Specifiy the phonetic symbols for pronunciation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlPhoneme(words, alphabet=alphabet, ph=ph, **kwargs)) + + def prosody(self, words=None, volume=None, rate=None, pitch=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param volume: Specify the volume, available values: default, silent, x-soft, soft, medium, loud, x-loud, +ndB, -ndB + :param rate: Specify the rate, available values: x-slow, slow, medium, fast, x-fast, n% + :param pitch: Specify the pitch, available values: default, x-low, low, medium, high, x-high, +n%, -n% + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlProsody(words=words, volume=volume, rate=rate, pitch=pitch, **kwargs) + ) + + def say_as(self, words, interpret_as=None, format=None, **kwargs): + """ + Create a element + + :param words: Words to be interpreted + :param interpret-as: Specify the type of words are spoken + :param format: Specify the format of the date when interpret-as is set to date + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + SsmlSayAs(words, interpret_as=interpret_as, format=format, **kwargs) + ) + + def sub(self, words, alias=None, **kwargs): + """ + Create a element + + :param words: Words to be substituted + :param alias: Substitute a different word (or pronunciation) for selected text such as an acronym or abbreviation + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlSub(words, alias=alias, **kwargs)) + + def w(self, words=None, role=None, **kwargs): + """ + Create a element + + :param words: Words to speak + :param role: Customize the pronunciation of words by specifying the word’s part of speech or alternate meaning + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(SsmlW(words=words, role=role, **kwargs)) + + +class SsmlBreak(TwiML): + """Adding a Pause in """ + + def __init__(self, **kwargs): + super(SsmlBreak, self).__init__(**kwargs) + self.name = "break" + + +class Pay(TwiML): + """ Twiml Verb""" + + def __init__(self, **kwargs): + super(Pay, self).__init__(**kwargs) + self.name = "Pay" + + def prompt( + self, + for_=None, + error_type=None, + card_type=None, + attempt=None, + require_matching_inputs=None, + **kwargs + ): + """ + Create a element + + :param for_: Name of the payment source data element + :param error_type: Type of error + :param card_type: Type of the credit card + :param attempt: Current attempt count + :param require_matching_inputs: Require customer to input requested information twice and verify matching. + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Prompt( + for_=for_, + error_type=error_type, + card_type=card_type, + attempt=attempt, + require_matching_inputs=require_matching_inputs, + **kwargs + ) + ) + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Sms(TwiML): + """ TwiML Noun""" + + def __init__(self, message, **kwargs): + super(Sms, self).__init__(**kwargs) + self.name = "Sms" + self.value = message + + +class Reject(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Reject, self).__init__(**kwargs) + self.name = "Reject" + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Redirect(TwiML): + """ TwiML Verb""" + + def __init__(self, url, **kwargs): + super(Redirect, self).__init__(**kwargs) + self.name = "Redirect" + self.value = url + + +class Record(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Record, self).__init__(**kwargs) + self.name = "Record" + + +class Queue(TwiML): + """ TwiML Noun""" + + def __init__(self, name, **kwargs): + super(Queue, self).__init__(**kwargs) + self.name = "Queue" + self.value = name + + +class Leave(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Leave, self).__init__(**kwargs) + self.name = "Leave" + + +class Hangup(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Hangup, self).__init__(**kwargs) + self.name = "Hangup" + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Gather(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Gather, self).__init__(**kwargs) + self.name = "Gather" + + def say(self, message=None, voice=None, loop=None, language=None, **kwargs): + """ + Create a element + + :param message: Message to say + :param voice: Voice to use + :param loop: Times to loop message + :param language: Message language + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Say(message=message, voice=voice, loop=loop, language=language, **kwargs) + ) + + def pause(self, length=None, **kwargs): + """ + Create a element + + :param length: Length in seconds to pause + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Pause(length=length, **kwargs)) + + def play(self, url=None, loop=None, digits=None, **kwargs): + """ + Create a element + + :param url: Media URL + :param loop: Times to loop media + :param digits: Play DTMF tones for digits + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Play(url=url, loop=loop, digits=digits, **kwargs)) + + +class Enqueue(TwiML): + """ TwiML Noun""" + + def __init__(self, name=None, **kwargs): + super(Enqueue, self).__init__(**kwargs) + self.name = "Enqueue" + if name: + self.value = name + + def task(self, body, priority=None, timeout=None, **kwargs): + """ + Create a element + + :param body: TaskRouter task attributes + :param priority: Task priority + :param timeout: Timeout associated with task + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Task(body, priority=priority, timeout=timeout, **kwargs)) + + +class Task(TwiML): + """ TwiML Noun""" + + def __init__(self, body, **kwargs): + super(Task, self).__init__(**kwargs) + self.name = "Task" + self.value = body + + +class Echo(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Echo, self).__init__(**kwargs) + self.name = "Echo" + + +class Dial(TwiML): + """ TwiML Verb""" + + def __init__(self, number=None, **kwargs): + super(Dial, self).__init__(**kwargs) + self.name = "Dial" + if number: + self.value = number + + def client( + self, + identity=None, + url=None, + method=None, + status_callback_event=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param identity: Client identity + :param url: Client URL + :param method: Client URL Method + :param status_callback_event: Events to trigger status callback + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL Method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Client( + identity=identity, + url=url, + method=method, + status_callback_event=status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + def conference( + self, + name, + muted=None, + beep=None, + start_conference_on_enter=None, + end_conference_on_exit=None, + wait_url=None, + wait_method=None, + max_participants=None, + record=None, + region=None, + coach=None, + trim=None, + status_callback_event=None, + status_callback=None, + status_callback_method=None, + recording_status_callback=None, + recording_status_callback_method=None, + recording_status_callback_event=None, + event_callback_url=None, + jitter_buffer_size=None, + participant_label=None, + **kwargs + ): + """ + Create a element + + :param name: Conference name + :param muted: Join the conference muted + :param beep: Play beep when joining + :param start_conference_on_enter: Start the conference on enter + :param end_conference_on_exit: End the conferenceon exit + :param wait_url: Wait URL + :param wait_method: Wait URL method + :param max_participants: Maximum number of participants + :param record: Record the conference + :param region: Conference region + :param coach: Call coach + :param trim: Trim the conference recording + :param status_callback_event: Events to call status callback URL + :param status_callback: Status callback URL + :param status_callback_method: Status callback URL method + :param recording_status_callback: Recording status callback URL + :param recording_status_callback_method: Recording status callback URL method + :param recording_status_callback_event: Recording status callback events + :param event_callback_url: Event callback URL + :param jitter_buffer_size: Size of jitter buffer for participant + :param participant_label: A label for participant + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Conference( + name, + muted=muted, + beep=beep, + start_conference_on_enter=start_conference_on_enter, + end_conference_on_exit=end_conference_on_exit, + wait_url=wait_url, + wait_method=wait_method, + max_participants=max_participants, + record=record, + region=region, + coach=coach, + trim=trim, + status_callback_event=status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + recording_status_callback_event=recording_status_callback_event, + event_callback_url=event_callback_url, + jitter_buffer_size=jitter_buffer_size, + participant_label=participant_label, + **kwargs + ) + ) + + def number( + self, + phone_number, + send_digits=None, + url=None, + method=None, + status_callback_event=None, + status_callback=None, + status_callback_method=None, + byoc=None, + machine_detection=None, + amd_status_callback_method=None, + amd_status_callback=None, + machine_detection_timeout=None, + machine_detection_speech_threshold=None, + machine_detection_speech_end_threshold=None, + machine_detection_silence_timeout=None, + **kwargs + ): + """ + Create a element + + :param phone_number: Phone Number to dial + :param send_digits: DTMF tones to play when the call is answered + :param url: TwiML URL + :param method: TwiML URL method + :param status_callback_event: Events to call status callback + :param status_callback: Status callback URL + :param status_callback_method: Status callback URL method + :param byoc: BYOC trunk SID (Beta) + :param machine_detection: Enable machine detection or end of greeting detection + :param amd_status_callback_method: HTTP Method to use with amd_status_callback + :param amd_status_callback: The URL we should call to send amd status information to your application + :param machine_detection_timeout: Number of seconds to wait for machine detection + :param machine_detection_speech_threshold: Number of milliseconds for measuring stick for the length of the speech activity + :param machine_detection_speech_end_threshold: Number of milliseconds of silence after speech activity + :param machine_detection_silence_timeout: Number of milliseconds of initial silence + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Number( + phone_number, + send_digits=send_digits, + url=url, + method=method, + status_callback_event=status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + byoc=byoc, + machine_detection=machine_detection, + amd_status_callback_method=amd_status_callback_method, + amd_status_callback=amd_status_callback, + machine_detection_timeout=machine_detection_timeout, + machine_detection_speech_threshold=machine_detection_speech_threshold, + machine_detection_speech_end_threshold=machine_detection_speech_end_threshold, + machine_detection_silence_timeout=machine_detection_silence_timeout, + **kwargs + ) + ) + + def queue( + self, + name, + url=None, + method=None, + reservation_sid=None, + post_work_activity_sid=None, + **kwargs + ): + """ + Create a element + + :param name: Queue name + :param url: Action URL + :param method: Action URL method + :param reservation_sid: TaskRouter Reservation SID + :param post_work_activity_sid: TaskRouter Activity SID + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Queue( + name, + url=url, + method=method, + reservation_sid=reservation_sid, + post_work_activity_sid=post_work_activity_sid, + **kwargs + ) + ) + + def sim(self, sim_sid, **kwargs): + """ + Create a element + + :param sim_sid: SIM SID + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Sim(sim_sid, **kwargs)) + + def sip( + self, + sip_url, + username=None, + password=None, + url=None, + method=None, + status_callback_event=None, + status_callback=None, + status_callback_method=None, + machine_detection=None, + amd_status_callback_method=None, + amd_status_callback=None, + machine_detection_timeout=None, + machine_detection_speech_threshold=None, + machine_detection_speech_end_threshold=None, + machine_detection_silence_timeout=None, + **kwargs + ): + """ + Create a element + + :param sip_url: SIP URL + :param username: SIP Username + :param password: SIP Password + :param url: Action URL + :param method: Action URL method + :param status_callback_event: Status callback events + :param status_callback: Status callback URL + :param status_callback_method: Status callback URL method + :param machine_detection: Enable machine detection or end of greeting detection + :param amd_status_callback_method: HTTP Method to use with amd_status_callback + :param amd_status_callback: The URL we should call to send amd status information to your application + :param machine_detection_timeout: Number of seconds to wait for machine detection + :param machine_detection_speech_threshold: Number of milliseconds for measuring stick for the length of the speech activity + :param machine_detection_speech_end_threshold: Number of milliseconds of silence after speech activity + :param machine_detection_silence_timeout: Number of milliseconds of initial silence + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Sip( + sip_url, + username=username, + password=password, + url=url, + method=method, + status_callback_event=status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + machine_detection=machine_detection, + amd_status_callback_method=amd_status_callback_method, + amd_status_callback=amd_status_callback, + machine_detection_timeout=machine_detection_timeout, + machine_detection_speech_threshold=machine_detection_speech_threshold, + machine_detection_speech_end_threshold=machine_detection_speech_end_threshold, + machine_detection_silence_timeout=machine_detection_silence_timeout, + **kwargs + ) + ) + + def application( + self, + application_sid=None, + url=None, + method=None, + status_callback_event=None, + status_callback=None, + status_callback_method=None, + customer_id=None, + copy_parent_to=None, + **kwargs + ): + """ + Create a element + + :param application_sid: Application sid + :param url: TwiML URL + :param method: TwiML URL Method + :param status_callback_event: Events to trigger status callback + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL Method + :param customer_id: Identity of the customer calling application + :param copy_parent_to: Copy parent call To field to called application side, otherwise use the application sid as To field + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Application( + application_sid=application_sid, + url=url, + method=method, + status_callback_event=status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + customer_id=customer_id, + copy_parent_to=copy_parent_to, + **kwargs + ) + ) + + +class Application(TwiML): + """ TwiML Noun""" + + def __init__(self, application_sid=None, **kwargs): + super(Application, self).__init__(**kwargs) + self.name = "Application" + if application_sid: + self.value = application_sid + + def application_sid(self, sid, **kwargs): + """ + Create a element + + :param sid: Application sid to dial + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(ApplicationSid(sid, **kwargs)) + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class ApplicationSid(TwiML): + """ TwiML Noun""" + + def __init__(self, sid, **kwargs): + super(ApplicationSid, self).__init__(**kwargs) + self.name = "ApplicationSid" + self.value = sid + + +class Sip(TwiML): + """ TwiML Noun""" + + def __init__(self, sip_url, **kwargs): + super(Sip, self).__init__(**kwargs) + self.name = "Sip" + self.value = sip_url + + +class Sim(TwiML): + """ TwiML Noun""" + + def __init__(self, sim_sid, **kwargs): + super(Sim, self).__init__(**kwargs) + self.name = "Sim" + self.value = sim_sid + + +class Number(TwiML): + """ TwiML Noun""" + + def __init__(self, phone_number, **kwargs): + super(Number, self).__init__(**kwargs) + self.name = "Number" + self.value = phone_number + + +class Conference(TwiML): + """ TwiML Noun""" + + def __init__(self, name, **kwargs): + super(Conference, self).__init__(**kwargs) + self.name = "Conference" + self.value = name + + +class Client(TwiML): + """ TwiML Noun""" + + def __init__(self, identity=None, **kwargs): + super(Client, self).__init__(**kwargs) + self.name = "Client" + if identity: + self.value = identity + + def identity(self, client_identity, **kwargs): + """ + Create a element + + :param client_identity: Identity of the client to dial + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Identity(client_identity, **kwargs)) + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Identity(TwiML): + """ TwiML Noun""" + + def __init__(self, client_identity, **kwargs): + super(Identity, self).__init__(**kwargs) + self.name = "Identity" + self.value = client_identity + + +class Connect(TwiML): + """ TwiML Verb""" + + def __init__(self, **kwargs): + super(Connect, self).__init__(**kwargs) + self.name = "Connect" + + def room(self, name, participant_identity=None, **kwargs): + """ + Create a element + + :param name: Room name + :param participant_identity: Participant identity when connecting to the Room + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Room(name, participant_identity=participant_identity, **kwargs) + ) + + def autopilot(self, name, **kwargs): + """ + Create a element + + :param name: Autopilot assistant sid or unique name + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Autopilot(name, **kwargs)) + + def stream( + self, + name=None, + connector_name=None, + url=None, + track=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param name: Friendly name given to the Stream + :param connector_name: Unique name for Stream Connector + :param url: URL of the remote service where the Stream is routed + :param track: Track to be streamed to remote service + :param status_callback: Status Callback URL + :param status_callback_method: Status Callback URL method + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Stream( + name=name, + connector_name=connector_name, + url=url, + track=track, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + def virtual_agent( + self, + connector_name=None, + language=None, + sentiment_analysis=None, + status_callback=None, + status_callback_method=None, + **kwargs + ): + """ + Create a element + + :param connector_name: Defines the conversation profile Dialogflow needs to use + :param language: Language to be used by Dialogflow to transcribe speech + :param sentiment_analysis: Whether sentiment analysis needs to be enabled or not + :param status_callback: URL to post status callbacks from Twilio + :param status_callback_method: HTTP method to use when requesting the status callback URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + VirtualAgent( + connector_name=connector_name, + language=language, + sentiment_analysis=sentiment_analysis, + status_callback=status_callback, + status_callback_method=status_callback_method, + **kwargs + ) + ) + + def conversation( + self, + service_instance_sid=None, + inbound_autocreation=None, + routing_assignment_timeout=None, + inbound_timeout=None, + url=None, + method=None, + record=None, + trim=None, + recording_status_callback=None, + recording_status_callback_method=None, + recording_status_callback_event=None, + status_callback=None, + status_callback_method=None, + status_callback_event=None, + **kwargs + ): + """ + Create a element + + :param service_instance_sid: Service instance Sid + :param inbound_autocreation: Inbound autocreation + :param routing_assignment_timeout: Routing assignment timeout + :param inbound_timeout: Inbound timeout + :param url: TwiML URL + :param method: TwiML URL method + :param record: Record + :param trim: Trim + :param recording_status_callback: Recording status callback URL + :param recording_status_callback_method: Recording status callback URL method + :param recording_status_callback_event: Recording status callback events + :param status_callback: Status callback URL + :param status_callback_method: Status callback URL method + :param status_callback_event: Events to call status callback URL + :param kwargs: additional attributes + + :returns: element + """ + return self.nest( + Conversation( + service_instance_sid=service_instance_sid, + inbound_autocreation=inbound_autocreation, + routing_assignment_timeout=routing_assignment_timeout, + inbound_timeout=inbound_timeout, + url=url, + method=method, + record=record, + trim=trim, + recording_status_callback=recording_status_callback, + recording_status_callback_method=recording_status_callback_method, + recording_status_callback_event=recording_status_callback_event, + status_callback=status_callback, + status_callback_method=status_callback_method, + status_callback_event=status_callback_event, + **kwargs + ) + ) + + +class Conversation(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(Conversation, self).__init__(**kwargs) + self.name = "Conversation" + + +class VirtualAgent(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(VirtualAgent, self).__init__(**kwargs) + self.name = "VirtualAgent" + + def config(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom config + :param value: The value of the custom config + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Config(name=name, value=value, **kwargs)) + + def parameter(self, name=None, value=None, **kwargs): + """ + Create a element + + :param name: The name of the custom parameter + :param value: The value of the custom parameter + :param kwargs: additional attributes + + :returns: element + """ + return self.nest(Parameter(name=name, value=value, **kwargs)) + + +class Config(TwiML): + """ TwiML Noun""" + + def __init__(self, **kwargs): + super(Config, self).__init__(**kwargs) + self.name = "Config" + + +class Autopilot(TwiML): + """ TwiML Noun""" + + def __init__(self, name, **kwargs): + super(Autopilot, self).__init__(**kwargs) + self.name = "Autopilot" + self.value = name + + +class Room(TwiML): + """ TwiML Noun""" + + def __init__(self, name, **kwargs): + super(Room, self).__init__(**kwargs) + self.name = "Room" + self.value = name diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/LICENSE new file mode 100644 index 0000000..f26bcf4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/LICENSE @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/METADATA new file mode 100644 index 0000000..5089b4d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/METADATA @@ -0,0 +1,66 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.11.0 +Summary: Backported and Experimental Type Hints for Python 3.8+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Software Development +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing-extensions.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) – +[PyPI](https://pypi.org/project/typing-extensions/) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +`typing_extensions` is treated specially by static type checkers such as +mypy and pyright. Objects defined in `typing_extensions` are treated the same +way as equivalent forms in `typing`. + +`typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version will be incremented only for backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`, +where `x.y` is the first version that includes all features you need. + +## Included items + +See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a +complete listing of module contents. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md) +for how to contribute to `typing_extensions`. + diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/RECORD new file mode 100644 index 0000000..d7abb28 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/RECORD @@ -0,0 +1,7 @@ +__pycache__/typing_extensions.cpython-310.pyc,, +typing_extensions-4.11.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +typing_extensions-4.11.0.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +typing_extensions-4.11.0.dist-info/METADATA,sha256=rhGXH-iuwULXNZDAtjXcjwU2jcxotMTkFiUg0OqYXzI,2967 +typing_extensions-4.11.0.dist-info/RECORD,, +typing_extensions-4.11.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +typing_extensions.py,sha256=mvJ5zBIuLMsaVGHZjRLsGh8ouwg5Kr3uNbWCHX8-Ao0,122293 diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/typing_extensions.py b/.pythonlibs/lib/python3.10/site-packages/typing_extensions.py new file mode 100644 index 0000000..9ccd519 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/typing_extensions.py @@ -0,0 +1,3332 @@ +import abc +import collections +import collections.abc +import functools +import inspect +import operator +import sys +import types as _types +import typing +import warnings + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'Buffer', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsAbs', + 'SupportsBytes', + 'SupportsComplex', + 'SupportsFloat', + 'SupportsIndex', + 'SupportsInt', + 'SupportsRound', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'deprecated', + 'Doc', + 'get_overloads', + 'final', + 'get_args', + 'get_origin', + 'get_original_bases', + 'get_protocol_members', + 'get_type_hints', + 'IntVar', + 'is_protocol', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeAliasType', + 'TypeGuard', + 'TypeIs', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'ReadOnly', + 'Required', + 'NotRequired', + + # Pure aliases, have always been in typing + 'AbstractSet', + 'AnyStr', + 'BinaryIO', + 'Callable', + 'Collection', + 'Container', + 'Dict', + 'ForwardRef', + 'FrozenSet', + 'Generator', + 'Generic', + 'Hashable', + 'IO', + 'ItemsView', + 'Iterable', + 'Iterator', + 'KeysView', + 'List', + 'Mapping', + 'MappingView', + 'Match', + 'MutableMapping', + 'MutableSequence', + 'MutableSet', + 'Optional', + 'Pattern', + 'Reversible', + 'Sequence', + 'Set', + 'Sized', + 'TextIO', + 'Tuple', + 'Union', + 'ValuesView', + 'cast', + 'no_type_check', + 'no_type_check_decorator', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + + +class _Sentinel: + def __repr__(self): + return "" + + +_marker = _Sentinel() + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + + +class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + +Final = typing.Final + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8 +if sys.version_info >= (3, 10, 1): + Literal = typing.Literal +else: + def _flatten_literal_params(parameters): + """An internal helper for Literal creation: flatten Literals among parameters""" + params = [] + for p in parameters: + if isinstance(p, _LiteralGenericAlias): + params.extend(p.__args__) + else: + params.append(p) + return tuple(params) + + def _value_and_type_iter(params): + for p in params: + yield p, type(p) + + class _LiteralGenericAlias(typing._GenericAlias, _root=True): + def __eq__(self, other): + if not isinstance(other, _LiteralGenericAlias): + return NotImplemented + these_args_deduped = set(_value_and_type_iter(self.__args__)) + other_args_deduped = set(_value_and_type_iter(other.__args__)) + return these_args_deduped == other_args_deduped + + def __hash__(self): + return hash(frozenset(_value_and_type_iter(self.__args__))) + + class _LiteralForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, doc: str): + self._name = 'Literal' + self._doc = self.__doc__ = doc + + def __getitem__(self, parameters): + if not isinstance(parameters, tuple): + parameters = (parameters,) + + parameters = _flatten_literal_params(parameters) + + val_type_pairs = list(_value_and_type_iter(parameters)) + try: + deduped_pairs = set(val_type_pairs) + except TypeError: + # unhashable parameters + pass + else: + # similar logic to typing._deduplicate on Python 3.9+ + if len(deduped_pairs) < len(val_type_pairs): + new_parameters = [] + for pair in val_type_pairs: + if pair in deduped_pairs: + new_parameters.append(pair[0]) + deduped_pairs.remove(pair) + assert not deduped_pairs, deduped_pairs + parameters = tuple(new_parameters) + + return _LiteralGenericAlias(self, parameters) + + Literal = _LiteralForm(doc="""\ + A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +ContextManager = typing.ContextManager +AsyncContextManager = typing.AsyncContextManager +DefaultDict = typing.DefaultDict +OrderedDict = typing.OrderedDict +Counter = typing.Counter +ChainMap = typing.ChainMap +AsyncGenerator = typing.AsyncGenerator +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +_PROTO_ALLOWLIST = { + 'collections.abc': [ + 'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer', + ], + 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], + 'typing_extensions': ['Buffer'], +} + + +_EXCLUDED_ATTRS = { + "__abstractmethods__", "__annotations__", "__weakref__", "_is_protocol", + "_is_runtime_protocol", "__dict__", "__slots__", "__parameters__", + "__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__", + "__subclasshook__", "__orig_class__", "__init__", "__new__", + "__protocol_attrs__", "__non_callable_proto_members__", + "__match_args__", +} + +if sys.version_info >= (3, 9): + _EXCLUDED_ATTRS.add("__class_getitem__") + +if sys.version_info >= (3, 12): + _EXCLUDED_ATTRS.add("__type_params__") + +_EXCLUDED_ATTRS = frozenset(_EXCLUDED_ATTRS) + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in {'Protocol', 'Generic'}: + continue + annotations = getattr(base, '__annotations__', {}) + for attr in (*base.__dict__, *annotations): + if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS): + attrs.add(attr) + return attrs + + +def _caller(depth=2): + try: + return sys._getframe(depth).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): # For platforms without _getframe() + return None + + +# `__match_args__` attribute was removed from protocol members in 3.13, +# we want to backport this change to older Python versions. +if sys.version_info >= (3, 13): + Protocol = typing.Protocol +else: + def _allow_reckless_class_checks(depth=3): + """Allow instance and class checks for special stdlib modules. + The abc and functools modules indiscriminately call isinstance() and + issubclass() on the whole MRO of a user class, which may contain protocols. + """ + return _caller(depth) in {'abc', 'functools', None} + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, )`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + + # Inheriting from typing._ProtocolMeta isn't actually desirable, + # but is necessary to allow typing.Protocol and typing_extensions.Protocol + # to mix without getting TypeErrors about "metaclass conflict" + class _ProtocolMeta(type(typing.Protocol)): + # This metaclass is somewhat unfortunate, + # but is necessary for several reasons... + # + # NOTE: DO NOT call super() in any methods in this class + # That would call the methods on typing._ProtocolMeta on Python 3.8-3.11 + # and those are slow + def __new__(mcls, name, bases, namespace, **kwargs): + if name == "Protocol" and len(bases) < 2: + pass + elif {Protocol, typing.Protocol} & set(bases): + for base in bases: + if not ( + base in {object, typing.Generic, Protocol, typing.Protocol} + or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, []) + or is_protocol(base) + ): + raise TypeError( + f"Protocols can only inherit from other protocols, " + f"got {base!r}" + ) + return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs) + + def __init__(cls, *args, **kwargs): + abc.ABCMeta.__init__(cls, *args, **kwargs) + if getattr(cls, "_is_protocol", False): + cls.__protocol_attrs__ = _get_protocol_attrs(cls) + + def __subclasscheck__(cls, other): + if cls is Protocol: + return type.__subclasscheck__(cls, other) + if ( + getattr(cls, '_is_protocol', False) + and not _allow_reckless_class_checks() + ): + if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) + raise TypeError( + "Instance and class checks can only be used with " + "@runtime_checkable protocols" + ) + if ( + # this attribute is set by @runtime_checkable: + cls.__non_callable_proto_members__ + and cls.__dict__.get("__subclasshook__") is _proto_hook + ): + _type_check_issubclass_arg_1(other) + non_method_attrs = sorted(cls.__non_callable_proto_members__) + raise TypeError( + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." + ) + return abc.ABCMeta.__subclasscheck__(cls, other) + + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if cls is Protocol: + return type.__instancecheck__(cls, instance) + if not getattr(cls, "_is_protocol", False): + # i.e., it's a concrete subclass of a protocol + return abc.ABCMeta.__instancecheck__(cls, instance) + + if ( + not getattr(cls, '_is_runtime_protocol', False) and + not _allow_reckless_class_checks() + ): + raise TypeError("Instance and class checks can only be used with" + " @runtime_checkable protocols") + + if abc.ABCMeta.__instancecheck__(cls, instance): + return True + + for attr in cls.__protocol_attrs__: + try: + val = inspect.getattr_static(instance, attr) + except AttributeError: + break + # this attribute is set by @runtime_checkable: + if val is None and attr not in cls.__non_callable_proto_members__: + break + else: + return True + + return False + + def __eq__(cls, other): + # Hack so that typing.Generic.__class_getitem__ + # treats typing_extensions.Protocol + # as equivalent to typing.Protocol + if abc.ABCMeta.__eq__(cls, other) is True: + return True + return cls is Protocol and other is typing.Protocol + + # This has to be defined, or the abc-module cache + # complains about classes with this metaclass being unhashable, + # if we define only __eq__! + def __hash__(cls) -> int: + return type.__hash__(cls) + + @classmethod + def _proto_hook(cls, other): + if not cls.__dict__.get('_is_protocol', False): + return NotImplemented + + for attr in cls.__protocol_attrs__: + for base in other.__mro__: + # Check if the members appears in the class dictionary... + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + + # ...or in annotations, if it is a sub-protocol. + annotations = getattr(base, '__annotations__', {}) + if ( + isinstance(annotations, collections.abc.Mapping) + and attr in annotations + and is_protocol(other) + ): + break + else: + return NotImplemented + return True + + class Protocol(typing.Generic, metaclass=_ProtocolMeta): + __doc__ = typing.Protocol.__doc__ + __slots__ = () + _is_protocol = True + _is_runtime_protocol = False + + def __init_subclass__(cls, *args, **kwargs): + super().__init_subclass__(*args, **kwargs) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', False): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # Prohibit instantiation for protocol classes + if cls._is_protocol and cls.__init__ is Protocol.__init__: + cls.__init__ = _no_init + + +if sys.version_info >= (3, 13): + runtime_checkable = typing.runtime_checkable +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol. + + Such protocol can be used with isinstance() and issubclass(). + Raise TypeError if applied to a non-protocol class. + This allows a simple-minded structural check very similar to + one trick ponies in collections.abc such as Iterable. + + For example:: + + @runtime_checkable + class Closable(Protocol): + def close(self): ... + + assert isinstance(open('/some/file'), Closable) + + Warning: this will check only the presence of the required methods, + not their type signatures! + """ + if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False): + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + ' got %r' % cls) + cls._is_runtime_protocol = True + + # Only execute the following block if it's a typing_extensions.Protocol class. + # typing.Protocol classes don't need it. + if isinstance(cls, _ProtocolMeta): + # PEP 544 prohibits using issubclass() + # with protocols that have non-method members. + # See gh-113320 for why we compute this attribute here, + # rather than in `_ProtocolMeta.__init__` + cls.__non_callable_proto_members__ = set() + for attr in cls.__protocol_attrs__: + try: + is_callable = callable(getattr(cls, attr, None)) + except Exception as e: + raise TypeError( + f"Failed to determine whether protocol member {attr!r} " + "is a method member" + ) from e + else: + if not is_callable: + cls.__non_callable_proto_members__.add(attr) + + return cls + + +# The "runtime" alias exists for backwards compatibility. +runtime = runtime_checkable + + +# Our version of runtime-checkable protocols is faster on Python 3.8-3.11 +if sys.version_info >= (3, 12): + SupportsInt = typing.SupportsInt + SupportsFloat = typing.SupportsFloat + SupportsComplex = typing.SupportsComplex + SupportsBytes = typing.SupportsBytes + SupportsIndex = typing.SupportsIndex + SupportsAbs = typing.SupportsAbs + SupportsRound = typing.SupportsRound +else: + @runtime_checkable + class SupportsInt(Protocol): + """An ABC with one abstract method __int__.""" + __slots__ = () + + @abc.abstractmethod + def __int__(self) -> int: + pass + + @runtime_checkable + class SupportsFloat(Protocol): + """An ABC with one abstract method __float__.""" + __slots__ = () + + @abc.abstractmethod + def __float__(self) -> float: + pass + + @runtime_checkable + class SupportsComplex(Protocol): + """An ABC with one abstract method __complex__.""" + __slots__ = () + + @abc.abstractmethod + def __complex__(self) -> complex: + pass + + @runtime_checkable + class SupportsBytes(Protocol): + """An ABC with one abstract method __bytes__.""" + __slots__ = () + + @abc.abstractmethod + def __bytes__(self) -> bytes: + pass + + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + @runtime_checkable + class SupportsAbs(Protocol[T_co]): + """ + An ABC with one abstract method __abs__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __abs__(self) -> T_co: + pass + + @runtime_checkable + class SupportsRound(Protocol[T_co]): + """ + An ABC with one abstract method __round__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __round__(self, ndigits: int = 0) -> T_co: + pass + + +def _ensure_subclassable(mro_entries): + def inner(func): + if sys.implementation.name == "pypy" and sys.version_info < (3, 9): + cls_dict = { + "__call__": staticmethod(func), + "__mro_entries__": staticmethod(mro_entries) + } + t = type(func.__name__, (), cls_dict) + return functools.update_wrapper(t(), func) + else: + func.__mro_entries__ = mro_entries + return func + return inner + + +# Update this to something like >=3.13.0b1 if and when +# PEP 728 is implemented in CPython +_PEP_728_IMPLEMENTED = False + +if _PEP_728_IMPLEMENTED: + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + # Aaaand on 3.12 we add __orig_bases__ to TypedDict + # to enable better runtime introspection. + # On 3.13 we deprecate some odd ways of creating TypedDicts. + # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier. + # PEP 728 (still pending) makes more changes. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + # 3.10.0 and later + _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters + + def _get_typeddict_qualifiers(annotation_type): + while True: + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + else: + break + elif annotation_origin is Required: + yield Required + annotation_type, = get_args(annotation_type) + elif annotation_origin is NotRequired: + yield NotRequired + annotation_type, = get_args(annotation_type) + elif annotation_origin is ReadOnly: + yield ReadOnly + annotation_type, = get_args(annotation_type) + else: + break + + class _TypedDictMeta(type): + def __new__(cls, name, bases, ns, *, total=True, closed=False): + """Create new typed dict class object. + + This method is called when TypedDict is subclassed, + or when TypedDict is instantiated. This way + TypedDict supports all three syntax forms described in its docstring. + Subclasses and instances of TypedDict return actual dictionaries. + """ + for base in bases: + if type(base) is not _TypedDictMeta and base is not typing.Generic: + raise TypeError('cannot inherit from both a TypedDict type ' + 'and a non-TypedDict base class') + + if any(issubclass(b, typing.Generic) for b in bases): + generic_base = (typing.Generic,) + else: + generic_base = () + + # typing.py generally doesn't let you inherit from plain Generic, unless + # the name of the class happens to be "Protocol" + tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns) + tp_dict.__name__ = name + if tp_dict.__qualname__ == "Protocol": + tp_dict.__qualname__ = name + + if not hasattr(tp_dict, '__orig_bases__'): + tp_dict.__orig_bases__ = bases + + annotations = {} + own_annotations = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + if _TAKES_MODULE: + own_annotations = { + n: typing._type_check(tp, msg, module=tp_dict.__module__) + for n, tp in own_annotations.items() + } + else: + own_annotations = { + n: typing._type_check(tp, msg) + for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + readonly_keys = set() + mutable_keys = set() + extra_items_type = None + + for base in bases: + base_dict = base.__dict__ + + annotations.update(base_dict.get('__annotations__', {})) + required_keys.update(base_dict.get('__required_keys__', ())) + optional_keys.update(base_dict.get('__optional_keys__', ())) + readonly_keys.update(base_dict.get('__readonly_keys__', ())) + mutable_keys.update(base_dict.get('__mutable_keys__', ())) + base_extra_items_type = base_dict.get('__extra_items__', None) + if base_extra_items_type is not None: + extra_items_type = base_extra_items_type + + if closed and extra_items_type is None: + extra_items_type = Never + if closed and "__extra_items__" in own_annotations: + annotation_type = own_annotations.pop("__extra_items__") + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + if Required in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "Required" + ) + if NotRequired in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "NotRequired" + ) + extra_items_type = annotation_type + + annotations.update(own_annotations) + for annotation_key, annotation_type in own_annotations.items(): + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + + if Required in qualifiers: + required_keys.add(annotation_key) + elif NotRequired in qualifiers: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + if ReadOnly in qualifiers: + mutable_keys.discard(annotation_key) + readonly_keys.add(annotation_key) + else: + mutable_keys.add(annotation_key) + readonly_keys.discard(annotation_key) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + tp_dict.__readonly_keys__ = frozenset(readonly_keys) + tp_dict.__mutable_keys__ = frozenset(mutable_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items_type + return tp_dict + + __call__ = dict # static method + + def __subclasscheck__(cls, other): + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + + __instancecheck__ = __subclasscheck__ + + _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) + + @_ensure_subclassable(lambda bases: (_TypedDict,)) + def TypedDict(typename, fields=_marker, /, *, total=True, closed=False, **kwargs): + """A simple typed namespace. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type such that a type checker will expect all + instances to have a certain set of keys, where each key is + associated with a value of a consistent type. This expectation + is not checked at runtime. + + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports an additional equivalent form:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + By default, all keys must be present in a TypedDict. It is possible + to override this by specifying totality:: + + class Point2D(TypedDict, total=False): + x: int + y: int + + This means that a Point2D TypedDict can have any of the keys omitted. A type + checker is only expected to support a literal False or True as the value of + the total argument. True is the default, and makes all items defined in the + class body be required. + + The Required and NotRequired special forms can also be used to mark + individual keys as being required or not required:: + + class Point2D(TypedDict): + x: int # the "x" key must always be present (Required is the default) + y: NotRequired[int] # the "y" key can be omitted + + See PEP 655 for more details on Required and NotRequired. + """ + if fields is _marker or fields is None: + if fields is _marker: + deprecated_thing = "Failing to pass a value for the 'fields' parameter" + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + + example = f"`{typename} = TypedDict({typename!r}, {{}})`" + deprecation_msg = ( + f"{deprecated_thing} is deprecated and will be disallowed in " + "Python 3.15. To create a TypedDict class with 0 fields " + "using the functional syntax, pass an empty dictionary, e.g. " + ) + example + "." + warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) + if closed is not False and closed is not True: + kwargs["closed"] = closed + closed = False + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + if kwargs: + if sys.version_info >= (3, 13): + raise TypeError("TypedDict takes no keyword arguments") + warnings.warn( + "The kwargs-based syntax for TypedDict definitions is deprecated " + "in Python 3.11, will be removed in Python 3.13, and may not be " + "understood by third-party type checkers.", + DeprecationWarning, + stacklevel=2, + ) + + ns = {'__annotations__': dict(fields)} + module = _caller() + if module is not None: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = module + + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed) + td.__orig_bases__ = (TypedDict,) + return td + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + # On 3.8, this would otherwise return True + if hasattr(typing, "TypedDict") and tp is typing.TypedDict: + return False + return isinstance(tp, _TYPEDDICT_TYPES) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(val, typ, /): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return val + + +if hasattr(typing, "ReadOnly"): # 3.13+ + get_type_hints = typing.get_type_hints +else: # <=3.13 + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return _types.GenericAlias(t.__origin__, stripped_args) + if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): # 3.9+ + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: # 3.8 + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.8 +else: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__,) + self.__metadata__ + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.8-3.9 +else: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias as _typing_GenericAlias + except ImportError: + _typing_GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__,) + tp.__metadata__ + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.8 +else: + TypeAlias = _ExtensionsSpecialForm( + 'TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""" + ) + + +def _set_default(type_param, default): + if isinstance(default, (tuple, list)): + type_param.__default__ = tuple((typing._type_check(d, "Default must be a type") + for d in default)) + elif default != _marker: + if isinstance(type_param, ParamSpec) and default is ...: # ... not valid <3.11 + type_param.__default__ = default + else: + type_param.__default__ = typing._type_check(default, "Default must be a type") + else: + type_param.__default__ = None + + +def _set_module(typevarlike): + # for pickling: + def_mod = _caller(depth=3) + if def_mod != 'typing_extensions': + typevarlike.__module__ = def_mod + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + __init__ = _set_default + + +# Classes using this metaclass must provide a _backported_typevarlike ClassVar +class _TypeVarLikeMeta(type): + def __instancecheck__(cls, __instance: Any) -> bool: + return isinstance(__instance, cls._backported_typevarlike) + + +# Add default and infer_variance parameters from PEP 696 and 695 +class TypeVar(metaclass=_TypeVarLikeMeta): + """Type variable.""" + + _backported_typevarlike = typing.TypeVar + + def __new__(cls, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=_marker, infer_variance=False): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant, + infer_variance=infer_variance) + else: + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant) + if infer_variance and (covariant or contravariant): + raise ValueError("Variance cannot be specified with infer_variance.") + typevar.__infer_variance__ = infer_variance + _set_default(typevar, default) + _set_module(typevar) + return typevar + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type") + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.8-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + +# 3.10+ +if hasattr(typing, 'ParamSpec'): + + # Add default parameter - PEP 696 + class ParamSpec(metaclass=_TypeVarLikeMeta): + """Parameter specification.""" + + _backported_typevarlike = typing.ParamSpec + + def __new__(cls, name, *, bound=None, + covariant=False, contravariant=False, + infer_variance=False, default=_marker): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented, can pass infer_variance to typing.TypeVar + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant, + infer_variance=infer_variance) + else: + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant) + paramspec.__infer_variance__ = infer_variance + + _set_default(paramspec, default) + _set_module(paramspec) + return paramspec + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type") + +# 3.8-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + infer_variance=False, default=_marker): + super().__init__([self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + self.__infer_variance__ = bool(infer_variance) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__infer_variance__: + prefix = '' + elif self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.8-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + # Flag in 3.8. + _special = False + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + +# 3.8-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa: F811 +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.8 +else: + class _ConcatenateForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeGuardForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) + +# 3.13+ +if hasattr(typing, 'TypeIs'): + TypeIs = typing.TypeIs +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeIs(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeIsForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeIs = _TypeIsForm( + 'TypeIs', + doc="""Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """) + + +# Vendored from cpython typing._SpecialFrom +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): # 3.11+ + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): # 3.11+ + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): # 3.11+ + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): # 3.11+ + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): # 3.9-3.10 + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: # 3.8 + class _RequiredForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) + + +if hasattr(typing, 'ReadOnly'): + ReadOnly = typing.ReadOnly +elif sys.version_info[:2] >= (3, 9): # 3.9-3.12 + @_ExtensionsSpecialForm + def ReadOnly(self, parameters): + """A special typing construct to mark an item of a TypedDict as read-only. + + For example: + + class Movie(TypedDict): + title: ReadOnly[str] + year: int + + def mutate_movie(m: Movie) -> None: + m["year"] = 1992 # allowed + m["title"] = "The Matrix" # typechecker error + + There is no runtime checking for this property. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: # 3.8 + class _ReadOnlyForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + ReadOnly = _ReadOnlyForm( + 'ReadOnly', + doc="""A special typing construct to mark a key of a TypedDict as read-only. + + For example: + + class Movie(TypedDict): + title: ReadOnly[str] + year: int + + def mutate_movie(m: Movie) -> None: + m["year"] = 1992 # allowed + m["title"] = "The Matrix" # typechecker error + + There is no runtime checking for this propery. + """) + + +_UNPACK_DOC = """\ +Type unpack operator. + +The type unpack operator takes the child types from some container type, +such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For +example: + + # For some generic class `Foo`: + Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str] + + Ts = TypeVarTuple('Ts') + # Specifies that `Bar` is generic in an arbitrary number of types. + # (Think of `Ts` as a tuple of an arbitrary number of individual + # `TypeVar`s, which the `Unpack` is 'pulling out' directly into the + # `Generic[]`.) + class Bar(Generic[Unpack[Ts]]): ... + Bar[int] # Valid + Bar[int, str] # Also valid + +From Python 3.11, this can also be done using the `*` operator: + + Foo[*tuple[int, str]] + class Bar(Generic[*Ts]): ... + +The operator can also be used along with a `TypedDict` to annotate +`**kwargs` in a function signature. For instance: + + class Movie(TypedDict): + name: str + year: int + + # This function expects two keyword arguments - *name* of type `str` and + # *year* of type `int`. + def foo(**kwargs: Unpack[Movie]): ... + +Note that there is only some runtime checking of this operator. Not +everything the runtime allows may be accepted by static type checkers. + +For more information, see PEP 646 and PEP 692. +""" + + +if sys.version_info >= (3, 12): # PEP 692 changed the repr of Unpack[] + Unpack = typing.Unpack + + def _is_unpack(obj): + return get_origin(obj) is Unpack + +elif sys.version_info[:2] >= (3, 9): # 3.9+ + class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, getitem): + super().__init__(getitem) + self.__doc__ = _UNPACK_DOC + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: # 3.8 + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm('Unpack', doc=_UNPACK_DOC) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +if hasattr(typing, "TypeVarTuple"): # 3.11+ + + # Add default parameter - PEP 696 + class TypeVarTuple(metaclass=_TypeVarLikeMeta): + """Type variable tuple.""" + + _backported_typevarlike = typing.TypeVarTuple + + def __new__(cls, name, *, default=_marker): + tvt = typing.TypeVarTuple(name) + _set_default(tvt, default) + _set_module(tvt) + return tvt + + def __init_subclass__(self, *args, **kwds): + raise TypeError("Cannot subclass special typing classes") + +else: # <=3.10 + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=_marker): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): # 3.11+ + reveal_type = typing.reveal_type +else: # <=3.10 + def reveal_type(obj: T, /) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr) + return obj + + +if hasattr(typing, "assert_never"): # 3.11+ + assert_never = typing.assert_never +else: # <=3.10 + def assert_never(arg: Never, /) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + raise AssertionError("Expected code to be unreachable") + + +if sys.version_info >= (3, 12): # 3.12+ + # dataclass_transform exists in 3.11 but lacks the frozen_default parameter + dataclass_transform = typing.dataclass_transform +else: # <=3.11 + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + frozen_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``frozen_default`` indicates whether the ``frozen`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "frozen_default": frozen_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): # 3.12+ + override = typing.override +else: # <=3.11 + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(arg: _F, /) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + There is no runtime checking of these properties. The decorator + sets the ``__override__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + + See PEP 698 for details. + + """ + try: + arg.__override__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return arg + + +if hasattr(warnings, "deprecated"): + deprecated = warnings.deprecated +else: + _T = typing.TypeVar("_T") + + class deprecated: + """Indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + The deprecation message passed to the decorator is saved in the + ``__deprecated__`` attribute on the decorated object. + If applied to an overload, the decorator + must be after the ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. + + See PEP 702 for details. + + """ + def __init__( + self, + message: str, + /, + *, + category: typing.Optional[typing.Type[Warning]] = DeprecationWarning, + stacklevel: int = 1, + ) -> None: + if not isinstance(message, str): + raise TypeError( + "Expected an object of type str for 'message', not " + f"{type(message).__name__!r}" + ) + self.message = message + self.category = category + self.stacklevel = stacklevel + + def __call__(self, arg: _T, /) -> _T: + # Make sure the inner functions created below don't + # retain a reference to self. + msg = self.message + category = self.category + stacklevel = self.stacklevel + if category is None: + arg.__deprecated__ = msg + return arg + elif isinstance(arg, type): + import functools + from types import MethodType + + original_new = arg.__new__ + + @functools.wraps(original_new) + def __new__(cls, *args, **kwargs): + if cls is arg: + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + if original_new is not object.__new__: + return original_new(cls, *args, **kwargs) + # Mirrors a similar check in object.__new__. + elif cls.__init__ is object.__init__ and (args or kwargs): + raise TypeError(f"{cls.__name__}() takes no arguments") + else: + return original_new(cls) + + arg.__new__ = staticmethod(__new__) + + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python) + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ + + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = classmethod(__init_subclass__) + # Or otherwise, which likely means it's a builtin such as + # object's implementation of __init_subclass__. + else: + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = __init_subclass__ + + arg.__deprecated__ = __new__.__deprecated__ = msg + __init_subclass__.__deprecated__ = msg + return arg + elif callable(arg): + import functools + + @functools.wraps(arg) + def wrapper(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return arg(*args, **kwargs) + + arg.__deprecated__ = wrapper.__deprecated__ = msg + return wrapper + else: + raise TypeError( + "@deprecated decorator with non-None category must be applied to " + f"a class or callable, not {arg!r}" + ) + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if getattr(parameters[alen], '__default__', None) is not None: + return + + num_default_tv = sum(getattr(p, '__default__', None) + is not None for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + things = "arguments" if sys.version_info >= (3, 10) else "parameters" + raise TypeError(f"Too {'many' if alen > elen else 'few'} {things}" + f" for {cls}; actual {alen}, expected {expect_val}") +else: + # Python 3.11+ + + def _check_generic(cls, parameters, elen): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if getattr(parameters[alen], '__default__', None) is not None: + return + + num_default_tv = sum(getattr(p, '__default__', None) + is not None for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments" + f" for {cls}; actual {alen}, expected {expect_val}") + +typing._check_generic = _check_generic + +# Python 3.11+ _collect_type_vars was renamed to _collect_parameters +if hasattr(typing, '_collect_type_vars'): + def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + # required TypeVarLike cannot appear after TypeVarLike with default + default_encountered = False + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not _is_unpack(t) + ): + if getattr(t, '__default__', None) is not None: + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + typing._collect_type_vars = _collect_type_vars +else: + def _collect_parameters(args): + """Collect all type variables and parameter specifications in args + in order of first appearance (lexicographic order). + + For example:: + + assert _collect_parameters((T, Callable[P, T])) == (T, P) + """ + parameters = [] + # required TypeVarLike cannot appear after TypeVarLike with default + default_encountered = False + for t in args: + if isinstance(t, type): + # We don't want __parameters__ descriptor of a bare Python class. + pass + elif isinstance(t, tuple): + # `t` might be a tuple, when `ParamSpec` is substituted with + # `[T, int]`, or `[int, *Ts]`, etc. + for x in t: + for collected in _collect_parameters([x]): + if collected not in parameters: + parameters.append(collected) + elif hasattr(t, '__typing_subst__'): + if t not in parameters: + if getattr(t, '__default__', None) is not None: + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + parameters.append(t) + else: + for x in getattr(t, '__parameters__', ()): + if x not in parameters: + parameters.append(x) + + return tuple(parameters) + + typing._collect_parameters = _collect_parameters + +# Backport typing.NamedTuple as it exists in Python 3.13. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +# On 3.12, we added __orig_bases__ to call-based NamedTuples +# On 3.13, we deprecated kwargs-based NamedTuples +if sys.version_info >= (3, 13): + NamedTuple = typing.NamedTuple +else: + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + # The `_field_types` attribute was removed in 3.9; + # in earlier versions, it is the same as the `__annotations__` attribute + if sys.version_info < (3, 9): + nm_tpl._field_types = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + types = ns.get('__annotations__', {}) + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + if hasattr(typing, '_generic_class_getitem'): # 3.12+ + nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem) + else: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key, val in ns.items(): + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields: + if key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + try: + set_name = type(val).__set_name__ + except AttributeError: + pass + else: + try: + set_name(val, nm_tpl, key) + except BaseException as e: + msg = ( + f"Error calling __set_name__ on {type(val).__name__!r} " + f"instance {key!r} in {typename!r}" + ) + # BaseException.add_note() existed on py311, + # but the __set_name__ machinery didn't start + # using add_note() until py312. + # Making sure exceptions are raised in the same way + # as in "normal" classes seems most important here. + if sys.version_info >= (3, 12): + e.add_note(msg) + raise + else: + raise RuntimeError(msg) from e + + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + @_ensure_subclassable(_namedtuple_mro_entries) + def NamedTuple(typename, fields=_marker, /, **kwargs): + """Typed version of namedtuple. + + Usage:: + + class Employee(NamedTuple): + name: str + id: int + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has an extra __annotations__ attribute, giving a + dict that maps field names to types. (The field names are also in + the _fields attribute, which is part of the namedtuple API.) + An alternative equivalent functional syntax is also accepted:: + + Employee = NamedTuple('Employee', [('name', str), ('id', int)]) + """ + if fields is _marker: + if kwargs: + deprecated_thing = "Creating NamedTuple classes using keyword arguments" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "Use the class-based or functional syntax instead." + ) + else: + deprecated_thing = "Failing to pass a value for the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif fields is None: + if kwargs: + raise TypeError( + "Cannot pass `None` as the 'fields' parameter " + "and also specify fields using keyword arguments" + ) + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + if fields is _marker or fields is None: + warnings.warn( + deprecation_msg.format(name=deprecated_thing, remove="3.15"), + DeprecationWarning, + stacklevel=2, + ) + fields = kwargs.items() + nt = _make_nmtuple(typename, fields, module=_caller()) + nt.__orig_bases__ = (NamedTuple,) + return nt + + +if hasattr(collections.abc, "Buffer"): + Buffer = collections.abc.Buffer +else: + class Buffer(abc.ABC): + """Base class for classes that implement the buffer protocol. + + The buffer protocol allows Python objects to expose a low-level + memory buffer interface. Before Python 3.12, it is not possible + to implement the buffer protocol in pure Python code, or even + to check whether a class implements the buffer protocol. In + Python 3.12 and higher, the ``__buffer__`` method allows access + to the buffer protocol from Python code, and the + ``collections.abc.Buffer`` ABC allows checking whether a class + implements the buffer protocol. + + To indicate support for the buffer protocol in earlier versions, + inherit from this ABC, either in a stub file or at runtime, + or use ABC registration. This ABC provides no methods, because + there is no Python-accessible methods shared by pre-3.12 buffer + classes. It is useful primarily for static checks. + + """ + + # As a courtesy, register the most common stdlib buffer classes. + Buffer.register(memoryview) + Buffer.register(bytearray) + Buffer.register(bytes) + + +# Backport of types.get_original_bases, available on 3.12+ in CPython +if hasattr(_types, "get_original_bases"): + get_original_bases = _types.get_original_bases +else: + def get_original_bases(cls, /): + """Return the class's "original" bases prior to modification by `__mro_entries__`. + + Examples:: + + from typing import TypeVar, Generic + from typing_extensions import NamedTuple, TypedDict + + T = TypeVar("T") + class Foo(Generic[T]): ... + class Bar(Foo[int], float): ... + class Baz(list[str]): ... + Eggs = NamedTuple("Eggs", [("a", int), ("b", str)]) + Spam = TypedDict("Spam", {"a": int, "b": str}) + + assert get_original_bases(Bar) == (Foo[int], float) + assert get_original_bases(Baz) == (list[str],) + assert get_original_bases(Eggs) == (NamedTuple,) + assert get_original_bases(Spam) == (TypedDict,) + assert get_original_bases(int) == (object,) + """ + try: + return cls.__dict__.get("__orig_bases__", cls.__bases__) + except AttributeError: + raise TypeError( + f'Expected an instance of type, not {type(cls).__name__!r}' + ) from None + + +# NewType is a class on Python 3.10+, making it pickleable +# The error message for subclassing instances of NewType was improved on 3.11+ +if sys.version_info >= (3, 11): + NewType = typing.NewType +else: + class NewType: + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy callable that simply returns its argument. Usage:: + UserId = NewType('UserId', int) + def name_by_id(user_id: UserId) -> str: + ... + UserId('user') # Fails type check + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + num = UserId(5) + 1 # type: int + """ + + def __call__(self, obj, /): + return obj + + def __init__(self, name, tp): + self.__qualname__ = name + if '.' in name: + name = name.rpartition('.')[-1] + self.__name__ = name + self.__supertype__ = tp + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __mro_entries__(self, bases): + # We defined __mro_entries__ to get a better error message + # if a user attempts to subclass a NewType instance. bpo-46170 + supercls_name = self.__name__ + + class Dummy: + def __init_subclass__(cls): + subcls_name = cls.__name__ + raise TypeError( + f"Cannot subclass an instance of NewType. " + f"Perhaps you were looking for: " + f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`" + ) + + return (Dummy,) + + def __repr__(self): + return f'{self.__module__}.{self.__qualname__}' + + def __reduce__(self): + return self.__qualname__ + + if sys.version_info >= (3, 10): + # PEP 604 methods + # It doesn't make sense to have these methods on Python <3.10 + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + +if hasattr(typing, "TypeAliasType"): + TypeAliasType = typing.TypeAliasType +else: + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + TypeAliasType, + )) + + class TypeAliasType: + """Create named, parameterized type aliases. + + This provides a backport of the new `type` statement in Python 3.12: + + type ListOrSet[T] = list[T] | set[T] + + is equivalent to: + + T = TypeVar("T") + ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) + + The name ListOrSet can then be used as an alias for the type it refers to. + + The type_params argument should contain all the type parameters used + in the value of the type alias. If the alias is not generic, this + argument is omitted. + + Static type checkers should only support type aliases declared using + TypeAliasType that follow these rules: + + - The first argument (the name) must be a string literal. + - The TypeAliasType instance must be immediately assigned to a variable + of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid, + as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)'). + + """ + + def __init__(self, name: str, value, *, type_params=()): + if not isinstance(name, str): + raise TypeError("TypeAliasType name must be a string") + self.__value__ = value + self.__type_params__ = type_params + + parameters = [] + for type_param in type_params: + if isinstance(type_param, TypeVarTuple): + parameters.extend(type_param) + else: + parameters.append(type_param) + self.__parameters__ = tuple(parameters) + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + # Setting this attribute closes the TypeAliasType from further modification + self.__name__ = name + + def __setattr__(self, name: str, value: object, /) -> None: + if hasattr(self, "__name__"): + self._raise_attribute_error(name) + super().__setattr__(name, value) + + def __delattr__(self, name: str, /) -> Never: + self._raise_attribute_error(name) + + def _raise_attribute_error(self, name: str) -> Never: + # Match the Python 3.12 error messages exactly + if name == "__name__": + raise AttributeError("readonly attribute") + elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}: + raise AttributeError( + f"attribute '{name}' of 'typing.TypeAliasType' objects " + "is not writable" + ) + else: + raise AttributeError( + f"'typing.TypeAliasType' object has no attribute '{name}'" + ) + + def __repr__(self) -> str: + return self.__name__ + + def __getitem__(self, parameters): + if not isinstance(parameters, tuple): + parameters = (parameters,) + parameters = [ + typing._type_check( + item, f'Subscripting {self.__name__} requires a type.' + ) + for item in parameters + ] + return typing._GenericAlias(self, tuple(parameters)) + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + "type 'typing_extensions.TypeAliasType' is not an acceptable base type" + ) + + # The presence of this method convinces typing._type_check + # that TypeAliasTypes are types. + def __call__(self): + raise TypeError("Type alias is not callable") + + if sys.version_info >= (3, 10): + def __or__(self, right): + # For forward compatibility with 3.12, reject Unions + # that are not accepted by the built-in Union. + if not _is_unionable(right): + return NotImplemented + return typing.Union[self, right] + + def __ror__(self, left): + if not _is_unionable(left): + return NotImplemented + return typing.Union[left, self] + + +if hasattr(typing, "is_protocol"): + is_protocol = typing.is_protocol + get_protocol_members = typing.get_protocol_members +else: + def is_protocol(tp: type, /) -> bool: + """Return True if the given type is a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, is_protocol + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> is_protocol(P) + True + >>> is_protocol(int) + False + """ + return ( + isinstance(tp, type) + and getattr(tp, '_is_protocol', False) + and tp is not Protocol + and tp is not typing.Protocol + ) + + def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]: + """Return the set of members defined in a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, get_protocol_members + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> get_protocol_members(P) + frozenset({'a', 'b'}) + + Raise a TypeError for arguments that are not Protocols. + """ + if not is_protocol(tp): + raise TypeError(f'{tp!r} is not a Protocol') + if hasattr(tp, '__protocol_attrs__'): + return frozenset(tp.__protocol_attrs__) + return frozenset(_get_protocol_attrs(tp)) + + +if hasattr(typing, "Doc"): + Doc = typing.Doc +else: + class Doc: + """Define the documentation of a type annotation using ``Annotated``, to be + used in class attributes, function and method parameters, return values, + and variables. + + The value should be a positional-only string literal to allow static tools + like editors and documentation generators to use it. + + This complements docstrings. + + The string value passed is available in the attribute ``documentation``. + + Example:: + + >>> from typing_extensions import Annotated, Doc + >>> def hi(to: Annotated[str, Doc("Who to say hi to")]) -> None: ... + """ + def __init__(self, documentation: str, /) -> None: + self.documentation = documentation + + def __repr__(self) -> str: + return f"Doc({self.documentation!r})" + + def __hash__(self) -> int: + return hash(self.documentation) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Doc): + return NotImplemented + return self.documentation == other.documentation + + +# Aliases for items that have always been in typing. +# Explicitly assign these (rather than using `from typing import *` at the top), +# so that we get a CI error if one of these is deleted from typing.py +# in a future version of Python +AbstractSet = typing.AbstractSet +AnyStr = typing.AnyStr +BinaryIO = typing.BinaryIO +Callable = typing.Callable +Collection = typing.Collection +Container = typing.Container +Dict = typing.Dict +ForwardRef = typing.ForwardRef +FrozenSet = typing.FrozenSet +Generator = typing.Generator +Generic = typing.Generic +Hashable = typing.Hashable +IO = typing.IO +ItemsView = typing.ItemsView +Iterable = typing.Iterable +Iterator = typing.Iterator +KeysView = typing.KeysView +List = typing.List +Mapping = typing.Mapping +MappingView = typing.MappingView +Match = typing.Match +MutableMapping = typing.MutableMapping +MutableSequence = typing.MutableSequence +MutableSet = typing.MutableSet +Optional = typing.Optional +Pattern = typing.Pattern +Reversible = typing.Reversible +Sequence = typing.Sequence +Set = typing.Set +Sized = typing.Sized +TextIO = typing.TextIO +Tuple = typing.Tuple +Union = typing.Union +ValuesView = typing.ValuesView +cast = typing.cast +no_type_check = typing.no_type_check +no_type_check_decorator = typing.no_type_check_decorator diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/METADATA new file mode 100644 index 0000000..9da6d09 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/METADATA @@ -0,0 +1,154 @@ +Metadata-Version: 2.1 +Name: urllib3 +Version: 2.2.1 +Summary: HTTP library with thread-safe connection pooling, file post, and more. +Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst +Project-URL: Documentation, https://urllib3.readthedocs.io +Project-URL: Code, https://github.com/urllib3/urllib3 +Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues +Author-email: Andrey Petrov +Maintainer-email: Seth Michael Larson , Quentin Pradet , Illia Volochii +License-File: LICENSE.txt +Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.8 +Provides-Extra: brotli +Requires-Dist: brotli>=1.0.9; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi>=0.8.0; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: h2 +Requires-Dist: h2<5,>=4; extra == 'h2' +Provides-Extra: socks +Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' +Provides-Extra: zstd +Requires-Dist: zstandard>=0.18.0; extra == 'zstd' +Description-Content-Type: text/markdown + +

    + +![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) + +

    + +

    + PyPI Version + Python Versions + Join our Discord + Coverage Status + Build Status on GitHub + Documentation Status
    + OpenSSF Scorecard + SLSA 3 + CII Best Practices +

    + +urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the +Python ecosystem already uses urllib3 and you should too. +urllib3 brings many critical features that are missing from the Python +standard libraries: + +- Thread safety. +- Connection pooling. +- Client-side SSL/TLS verification. +- File uploads with multipart encoding. +- Helpers for retrying requests and dealing with HTTP redirects. +- Support for gzip, deflate, brotli, and zstd encoding. +- Proxy support for HTTP and SOCKS. +- 100% test coverage. + +urllib3 is powerful and easy to use: + +```python3 +>>> import urllib3 +>>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") +>>> resp.status +200 +>>> resp.data +b"User-agent: *\nDisallow: /deny\n" +``` + +## Installing + +urllib3 can be installed with [pip](https://pip.pypa.io): + +```bash +$ python -m pip install urllib3 +``` + +Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): + +```bash +$ git clone https://github.com/urllib3/urllib3.git +$ cd urllib3 +$ pip install . +``` + + +## Documentation + +urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). + + +## Community + +urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and +collaborating with other contributors. Drop by and say hello 👋 + + +## Contributing + +urllib3 happily accepts contributions. Please see our +[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) +for some tips on getting started. + + +## Security Disclosures + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure with maintainers. + + +## Maintainers + +- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) +- [@pquentin](https://github.com/pquentin) (Quentin Pradet) +- [@illia-v](https://github.com/illia-v) (Illia Volochii) +- [@theacodes](https://github.com/theacodes) (Thea Flowers) +- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) +- [@lukasa](https://github.com/lukasa) (Cory Benfield) +- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) +- [@shazow](https://github.com/shazow) (Andrey Petrov) + +👋 + + +## Sponsorship + +If your company benefits from this library, please consider [sponsoring its +development](https://urllib3.readthedocs.io/en/latest/sponsors.html). + + +## For Enterprise + +Professional support for urllib3 is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/RECORD new file mode 100644 index 0000000..5b72e55 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/RECORD @@ -0,0 +1,75 @@ +urllib3-2.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +urllib3-2.2.1.dist-info/METADATA,sha256=uROmjQwfAbwRYjV9PMdc5JF5NA3kRkpoKafPkNzybfc,6434 +urllib3-2.2.1.dist-info/RECORD,, +urllib3-2.2.1.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87 +urllib3-2.2.1.dist-info/licenses/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093 +urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979 +urllib3/__pycache__/__init__.cpython-310.pyc,, +urllib3/__pycache__/_base_connection.cpython-310.pyc,, +urllib3/__pycache__/_collections.cpython-310.pyc,, +urllib3/__pycache__/_request_methods.cpython-310.pyc,, +urllib3/__pycache__/_version.cpython-310.pyc,, +urllib3/__pycache__/connection.cpython-310.pyc,, +urllib3/__pycache__/connectionpool.cpython-310.pyc,, +urllib3/__pycache__/exceptions.cpython-310.pyc,, +urllib3/__pycache__/fields.cpython-310.pyc,, +urllib3/__pycache__/filepost.cpython-310.pyc,, +urllib3/__pycache__/http2.cpython-310.pyc,, +urllib3/__pycache__/poolmanager.cpython-310.pyc,, +urllib3/__pycache__/response.cpython-310.pyc,, +urllib3/_base_connection.py,sha256=p-DOG_Me7-sJXO1R9VgDpNmdVU_kIS8VtaC7ptEllA0,5640 +urllib3/_collections.py,sha256=vzKA-7X-9resOamEWq52uV1nHshChjbYDvz47H0mMjw,17400 +urllib3/_request_methods.py,sha256=ucEpHQyQf06b9o1RxKLkCpzGH0ct-v7X2xGpU6rmmlo,9984 +urllib3/_version.py,sha256=12idLAcGmrAURPX52rGioBo33oQ__-ENJEdeqHvUUZg,98 +urllib3/connection.py,sha256=zFgaaoqrICsl7-kBp-_4va9m82sYhioAuy4-4iDpK0I,34704 +urllib3/connectionpool.py,sha256=XjTfYowLwN5ZzRMO41_OTbGNX4ANifgYVpWsVMRuC00,43556 +urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/contrib/__pycache__/__init__.cpython-310.pyc,, +urllib3/contrib/__pycache__/pyopenssl.cpython-310.pyc,, +urllib3/contrib/__pycache__/socks.cpython-310.pyc,, +urllib3/contrib/emscripten/__init__.py,sha256=u6KNgzjlFZbuAAXa_ybCR7gQ71VJESnF-IIdDA73brw,733 +urllib3/contrib/emscripten/__pycache__/__init__.cpython-310.pyc,, +urllib3/contrib/emscripten/__pycache__/connection.cpython-310.pyc,, +urllib3/contrib/emscripten/__pycache__/fetch.cpython-310.pyc,, +urllib3/contrib/emscripten/__pycache__/request.cpython-310.pyc,, +urllib3/contrib/emscripten/__pycache__/response.cpython-310.pyc,, +urllib3/contrib/emscripten/connection.py,sha256=kaBe2tWt7Yy9vNUFRBV7CSyDnfhCYILGxju9KTZj8Sw,8755 +urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=CDfYF_9CDobtx2lGidyJ1zjDEvwNT5F-dchmVWXDh0E,3655 +urllib3/contrib/emscripten/fetch.py,sha256=ymwJlHBBuw6WTpKgPHpdmmrNBxlsr75HqoD4Rn27YXk,14131 +urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566 +urllib3/contrib/emscripten/response.py,sha256=wIDmdJ4doFWqLl5s86l9n0V70gFjQ2HWaPgz69jM52E,9546 +urllib3/contrib/pyopenssl.py,sha256=X31eCYGwB09EkAHX8RhDKC0X0Ki7d0cCVWoMJZUM5bQ,19161 +urllib3/contrib/socks.py,sha256=gFS2-zOw4_vLGpUvExOf3fNVT8liz6vhM2t6lBPn3CY,7572 +urllib3/exceptions.py,sha256=RDaiudtR7rqbVKTKpLSgZBBtwaIqV7eZtervZV_mZag,9393 +urllib3/fields.py,sha256=8vi0PeRo_pE5chPmJA07LZtMkVls4UrBS1k2xM506jM,10843 +urllib3/filepost.py,sha256=-9qJT11cNGjO9dqnI20-oErZuTvNaM18xZZPCjZSbOE,2395 +urllib3/http2.py,sha256=4QQcjTM9UYOQZe0r8KnA8anU9ST4p_s3SB3gRTueyPc,7480 +urllib3/poolmanager.py,sha256=fcC3OwjFKxha06NsOORwbZOzrVt1pyY-bNCbKiqC0l8,22935 +urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93 +urllib3/response.py,sha256=lmvseToQbkLXuFyA3jcSyCPjTgSfa6YPA4xUhVqq8QI,43874 +urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001 +urllib3/util/__pycache__/__init__.cpython-310.pyc,, +urllib3/util/__pycache__/connection.cpython-310.pyc,, +urllib3/util/__pycache__/proxy.cpython-310.pyc,, +urllib3/util/__pycache__/request.cpython-310.pyc,, +urllib3/util/__pycache__/response.cpython-310.pyc,, +urllib3/util/__pycache__/retry.cpython-310.pyc,, +urllib3/util/__pycache__/ssl_.cpython-310.pyc,, +urllib3/util/__pycache__/ssl_match_hostname.cpython-310.pyc,, +urllib3/util/__pycache__/ssltransport.cpython-310.pyc,, +urllib3/util/__pycache__/timeout.cpython-310.pyc,, +urllib3/util/__pycache__/url.cpython-310.pyc,, +urllib3/util/__pycache__/util.cpython-310.pyc,, +urllib3/util/__pycache__/wait.cpython-310.pyc,, +urllib3/util/connection.py,sha256=QeUUEuNmhznpuKNPL-B0IVOkMdMCu8oJX62OC0Vpzug,4462 +urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148 +urllib3/util/request.py,sha256=PQnBmKUHMQ0hQQ41uhbLNAeA24ke60m6zeiwfwocpGo,8102 +urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374 +urllib3/util/retry.py,sha256=WB-7x1m7fQH_-Qqtrk2OGvz93GvBTxc-pRn8Vf3p4mg,18384 +urllib3/util/ssl_.py,sha256=FeymdS68RggEROwMB9VLGSqLHq2hRUKnIbQC_bCpGJI,19109 +urllib3/util/ssl_match_hostname.py,sha256=gaWqixoYtQ_GKO8fcRGFj3VXeMoqyxQQuUTPgWeiL_M,5812 +urllib3/util/ssltransport.py,sha256=SF__JQXVcHBQniFJZp3P9q-UeHM310WVwcBwqT9dCLE,9034 +urllib3/util/timeout.py,sha256=4eT1FVeZZU7h7mYD1Jq2OXNe4fxekdNvhoWUkZusRpA,10346 +urllib3/util/url.py,sha256=wHORhp80RAXyTlAIkTqLFzSrkU7J34ZDxX-tN65MBZk,15213 +urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146 +urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423 diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/WHEEL new file mode 100644 index 0000000..5998f3a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.21.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/licenses/LICENSE.txt b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..e6183d0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3-2.2.1.dist-info/licenses/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2020 Andrey Petrov and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__init__.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/__init__.py new file mode 100644 index 0000000..3fe782c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/__init__.py @@ -0,0 +1,211 @@ +""" +Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more +""" + +from __future__ import annotations + +# Set default logging handler to avoid "No handler found" warnings. +import logging +import sys +import typing +import warnings +from logging import NullHandler + +from . import exceptions +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from ._version import __version__ +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url +from .filepost import _TYPE_FIELDS, encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import BaseHTTPResponse, HTTPResponse +from .util.request import make_headers +from .util.retry import Retry +from .util.timeout import Timeout + +# Ensure that Python is compiled with OpenSSL 1.1.1+ +# If the 'ssl' module isn't available at all that's +# fine, we only care if the module is available. +try: + import ssl +except ImportError: + pass +else: + if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive: + warnings.warn( + "urllib3 v2 only supports OpenSSL 1.1.1+, currently " + f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " + "See: https://github.com/urllib3/urllib3/issues/3020", + exceptions.NotOpenSSLWarning, + ) + elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive: + raise ImportError( + "urllib3 v2 only supports OpenSSL 1.1.1+, currently " + f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " + "See: https://github.com/urllib3/urllib3/issues/2168" + ) + +__author__ = "Andrey Petrov (andrey.petrov@shazow.net)" +__license__ = "MIT" +__version__ = __version__ + +__all__ = ( + "HTTPConnectionPool", + "HTTPHeaderDict", + "HTTPSConnectionPool", + "PoolManager", + "ProxyManager", + "HTTPResponse", + "Retry", + "Timeout", + "add_stderr_logger", + "connection_from_url", + "disable_warnings", + "encode_multipart_formdata", + "make_headers", + "proxy_from_url", + "request", + "BaseHTTPResponse", +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger( + level: int = logging.DEBUG, +) -> logging.StreamHandler[typing.TextIO]: + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug("Added a stderr logging handler to logger: %s", __name__) + return handler + + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter("always", exceptions.SecurityWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) + + +def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None: + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter("ignore", category) + + +_DEFAULT_POOL = PoolManager() + + +def request( + method: str, + url: str, + *, + body: _TYPE_BODY | None = None, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + preload_content: bool | None = True, + decode_content: bool | None = True, + redirect: bool | None = True, + retries: Retry | bool | int | None = None, + timeout: Timeout | float | int | None = 3, + json: typing.Any | None = None, +) -> BaseHTTPResponse: + """ + A convenience, top-level request method. It uses a module-global ``PoolManager`` instance. + Therefore, its side effects could be shared across dependencies relying on it. + To avoid side effects create a new ``PoolManager`` instance and use it instead. + The method does not accept low-level ``**urlopen_kw`` keyword arguments. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param fields: + Data to encode and send in the request body. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param json: + Data to encode and send as JSON with UTF-encoded in the request body. + The ``"Content-Type"`` header will be set to ``"application/json"`` + unless specified otherwise. + """ + + return _DEFAULT_POOL.request( + method, + url, + body=body, + fields=fields, + headers=headers, + preload_content=preload_content, + decode_content=decode_content, + redirect=redirect, + retries=retries, + timeout=timeout, + json=json, + ) + + +if sys.platform == "emscripten": + from .contrib.emscripten import inject_into_urllib3 # noqa: 401 + + inject_into_urllib3() diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..4bb7d68 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_base_connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_base_connection.cpython-310.pyc new file mode 100644 index 0000000..60b8f96 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_base_connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_collections.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_collections.cpython-310.pyc new file mode 100644 index 0000000..244e7e0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_collections.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_request_methods.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_request_methods.cpython-310.pyc new file mode 100644 index 0000000..cca0224 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_request_methods.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_version.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_version.cpython-310.pyc new file mode 100644 index 0000000..f6e7c34 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/_version.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000..cc2bfcc Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connectionpool.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connectionpool.cpython-310.pyc new file mode 100644 index 0000000..729e310 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/connectionpool.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/exceptions.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..9b2a140 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/exceptions.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/fields.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/fields.cpython-310.pyc new file mode 100644 index 0000000..7d5f2c0 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/fields.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/filepost.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/filepost.cpython-310.pyc new file mode 100644 index 0000000..de4573c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/filepost.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/http2.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/http2.cpython-310.pyc new file mode 100644 index 0000000..416c20f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/http2.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/poolmanager.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/poolmanager.cpython-310.pyc new file mode 100644 index 0000000..4057c33 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/poolmanager.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000..24b9044 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/__pycache__/response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/_base_connection.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/_base_connection.py new file mode 100644 index 0000000..bb349c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/_base_connection.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import typing + +from .util.connection import _TYPE_SOCKET_OPTIONS +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT +from .util.url import Url + +_TYPE_BODY = typing.Union[bytes, typing.IO[typing.Any], typing.Iterable[bytes], str] + + +class ProxyConfig(typing.NamedTuple): + ssl_context: ssl.SSLContext | None + use_forwarding_for_https: bool + assert_hostname: None | str | Literal[False] + assert_fingerprint: str | None + + +class _ResponseOptions(typing.NamedTuple): + # TODO: Remove this in favor of a better + # HTTP request/response lifecycle tracking. + request_method: str + request_url: str + preload_content: bool + decode_content: bool + enforce_content_length: bool + + +if typing.TYPE_CHECKING: + import ssl + from typing import Literal, Protocol + + from .response import BaseHTTPResponse + + class BaseHTTPConnection(Protocol): + default_port: typing.ClassVar[int] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + host: str + port: int + timeout: None | ( + float + ) # Instance doesn't store _DEFAULT_TIMEOUT, must be resolved. + blocksize: int + source_address: tuple[str, int] | None + socket_options: _TYPE_SOCKET_OPTIONS | None + + proxy: Url | None + proxy_config: ProxyConfig | None + + is_verified: bool + proxy_is_verified: bool | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + socket_options: _TYPE_SOCKET_OPTIONS | None = ..., + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: + ... + + def set_tunnel( + self, + host: str, + port: int | None = None, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + ... + + def connect(self) -> None: + ... + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + # We know *at least* botocore is depending on the order of the + # first 3 parameters so to be safe we only mark the later ones + # as keyword-only to ensure we have space to extend. + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: + ... + + def getresponse(self) -> BaseHTTPResponse: + ... + + def close(self) -> None: + ... + + @property + def is_closed(self) -> bool: + """Whether the connection either is brand new or has been previously closed. + If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` + properties must be False. + """ + + @property + def is_connected(self) -> bool: + """Whether the connection is actively connected to any origin (proxy or target)""" + + @property + def has_connected_to_proxy(self) -> bool: + """Whether the connection has successfully connected to its proxy. + This returns False if no proxy is in use. Used to determine whether + errors are coming from the proxy layer or from tunnelling to the target origin. + """ + + class BaseHTTPSConnection(BaseHTTPConnection, Protocol): + default_port: typing.ClassVar[int] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + # Certificate verification methods + cert_reqs: int | str | None + assert_hostname: None | str | Literal[False] + assert_fingerprint: str | None + ssl_context: ssl.SSLContext | None + + # Trusted CAs + ca_certs: str | None + ca_cert_dir: str | None + ca_cert_data: None | str | bytes + + # TLS version + ssl_minimum_version: int | None + ssl_maximum_version: int | None + ssl_version: int | str | None # Deprecated + + # Client certificates + cert_file: str | None + key_file: str | None + key_password: str | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: _TYPE_SOCKET_OPTIONS | None = ..., + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: + ... diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/_collections.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/_collections.py new file mode 100644 index 0000000..55b0324 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/_collections.py @@ -0,0 +1,483 @@ +from __future__ import annotations + +import typing +from collections import OrderedDict +from enum import Enum, auto +from threading import RLock + +if typing.TYPE_CHECKING: + # We can only import Protocol if TYPE_CHECKING because it's a development + # dependency, and is not available at runtime. + from typing import Protocol + + from typing_extensions import Self + + class HasGettableStringKeys(Protocol): + def keys(self) -> typing.Iterator[str]: + ... + + def __getitem__(self, key: str) -> str: + ... + + +__all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] + + +# Key type +_KT = typing.TypeVar("_KT") +# Value type +_VT = typing.TypeVar("_VT") +# Default type +_DT = typing.TypeVar("_DT") + +ValidHTTPHeaderSource = typing.Union[ + "HTTPHeaderDict", + typing.Mapping[str, str], + typing.Iterable[typing.Tuple[str, str]], + "HasGettableStringKeys", +] + + +class _Sentinel(Enum): + not_passed = auto() + + +def ensure_can_construct_http_header_dict( + potential: object, +) -> ValidHTTPHeaderSource | None: + if isinstance(potential, HTTPHeaderDict): + return potential + elif isinstance(potential, typing.Mapping): + # Full runtime checking of the contents of a Mapping is expensive, so for the + # purposes of typechecking, we assume that any Mapping is the right shape. + return typing.cast(typing.Mapping[str, str], potential) + elif isinstance(potential, typing.Iterable): + # Similarly to Mapping, full runtime checking of the contents of an Iterable is + # expensive, so for the purposes of typechecking, we assume that any Iterable + # is the right shape. + return typing.cast(typing.Iterable[typing.Tuple[str, str]], potential) + elif hasattr(potential, "keys") and hasattr(potential, "__getitem__"): + return typing.cast("HasGettableStringKeys", potential) + else: + return None + + +class RecentlyUsedContainer(typing.Generic[_KT, _VT], typing.MutableMapping[_KT, _VT]): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + _container: typing.OrderedDict[_KT, _VT] + _maxsize: int + dispose_func: typing.Callable[[_VT], None] | None + lock: RLock + + def __init__( + self, + maxsize: int = 10, + dispose_func: typing.Callable[[_VT], None] | None = None, + ) -> None: + super().__init__() + self._maxsize = maxsize + self.dispose_func = dispose_func + self._container = OrderedDict() + self.lock = RLock() + + def __getitem__(self, key: _KT) -> _VT: + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key: _KT, value: _VT) -> None: + evicted_item = None + with self.lock: + # Possibly evict the existing value of 'key' + try: + # If the key exists, we'll overwrite it, which won't change the + # size of the pool. Because accessing a key should move it to + # the end of the eviction line, we pop it out first. + evicted_item = key, self._container.pop(key) + self._container[key] = value + except KeyError: + # When the key does not exist, we insert the value first so that + # evicting works in all cases, including when self._maxsize is 0 + self._container[key] = value + if len(self._container) > self._maxsize: + # If we didn't evict an existing value, and we've hit our maximum + # size, then we have to evict the least recently used item from + # the beginning of the container. + evicted_item = self._container.popitem(last=False) + + # After releasing the lock on the pool, dispose of any evicted value. + if evicted_item is not None and self.dispose_func: + _, evicted_value = evicted_item + self.dispose_func(evicted_value) + + def __delitem__(self, key: _KT) -> None: + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self) -> int: + with self.lock: + return len(self._container) + + def __iter__(self) -> typing.NoReturn: + raise NotImplementedError( + "Iteration over this class is unlikely to be threadsafe." + ) + + def clear(self) -> None: + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(self._container.values()) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self) -> set[_KT]: # type: ignore[override] + with self.lock: + return set(self._container.keys()) + + +class HTTPHeaderDictItemView(typing.Set[typing.Tuple[str, str]]): + """ + HTTPHeaderDict is unusual for a Mapping[str, str] in that it has two modes of + address. + + If we directly try to get an item with a particular name, we will get a string + back that is the concatenated version of all the values: + + >>> d['X-Header-Name'] + 'Value1, Value2, Value3' + + However, if we iterate over an HTTPHeaderDict's items, we will optionally combine + these values based on whether combine=True was called when building up the dictionary + + >>> d = HTTPHeaderDict({"A": "1", "B": "foo"}) + >>> d.add("A", "2", combine=True) + >>> d.add("B", "bar") + >>> list(d.items()) + [ + ('A', '1, 2'), + ('B', 'foo'), + ('B', 'bar'), + ] + + This class conforms to the interface required by the MutableMapping ABC while + also giving us the nonstandard iteration behavior we want; items with duplicate + keys, ordered by time of first insertion. + """ + + _headers: HTTPHeaderDict + + def __init__(self, headers: HTTPHeaderDict) -> None: + self._headers = headers + + def __len__(self) -> int: + return len(list(self._headers.iteritems())) + + def __iter__(self) -> typing.Iterator[tuple[str, str]]: + return self._headers.iteritems() + + def __contains__(self, item: object) -> bool: + if isinstance(item, tuple) and len(item) == 2: + passed_key, passed_val = item + if isinstance(passed_key, str) and isinstance(passed_val, str): + return self._headers._has_value_for_header(passed_key, passed_val) + return False + + +class HTTPHeaderDict(typing.MutableMapping[str, str]): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + _container: typing.MutableMapping[str, list[str]] + + def __init__(self, headers: ValidHTTPHeaderSource | None = None, **kwargs: str): + super().__init__() + self._container = {} # 'dict' is insert-ordered + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key: str, val: str) -> None: + # avoid a bytes/str comparison by decoding before httplib + if isinstance(key, bytes): + key = key.decode("latin-1") + self._container[key.lower()] = [key, val] + + def __getitem__(self, key: str) -> str: + val = self._container[key.lower()] + return ", ".join(val[1:]) + + def __delitem__(self, key: str) -> None: + del self._container[key.lower()] + + def __contains__(self, key: object) -> bool: + if isinstance(key, str): + return key.lower() in self._container + return False + + def setdefault(self, key: str, default: str = "") -> str: + return super().setdefault(key, default) + + def __eq__(self, other: object) -> bool: + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return False + else: + other_as_http_header_dict = type(self)(maybe_constructable) + + return {k.lower(): v for k, v in self.itermerged()} == { + k.lower(): v for k, v in other_as_http_header_dict.itermerged() + } + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def __len__(self) -> int: + return len(self._container) + + def __iter__(self) -> typing.Iterator[str]: + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def discard(self, key: str) -> None: + try: + del self[key] + except KeyError: + pass + + def add(self, key: str, val: str, *, combine: bool = False) -> None: + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + If this is called with combine=True, instead of adding a new header value + as a distinct item during iteration, this will instead append the value to + any existing header value with a comma. If no existing header value exists + for the key, then the value will simply be added, ignoring the combine parameter. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + >>> list(headers.items()) + [('foo', 'bar'), ('foo', 'baz')] + >>> headers.add('foo', 'quz', combine=True) + >>> list(headers.items()) + [('foo', 'bar, baz, quz')] + """ + # avoid a bytes/str comparison by decoding before httplib + if isinstance(key, bytes): + key = key.decode("latin-1") + key_lower = key.lower() + new_vals = [key, val] + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + # if there are values here, then there is at least the initial + # key/value pair + assert len(vals) >= 2 + if combine: + vals[-1] = vals[-1] + ", " + val + else: + vals.append(val) + + def extend(self, *args: ValidHTTPHeaderSource, **kwargs: str) -> None: + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError( + f"extend() takes at most 1 positional arguments ({len(args)} given)" + ) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, typing.Mapping): + for key, val in other.items(): + self.add(key, val) + elif isinstance(other, typing.Iterable): + other = typing.cast(typing.Iterable[typing.Tuple[str, str]], other) + for key, value in other: + self.add(key, value) + elif hasattr(other, "keys") and hasattr(other, "__getitem__"): + # THIS IS NOT A TYPESAFE BRANCH + # In this branch, the object has a `keys` attr but is not a Mapping or any of + # the other types indicated in the method signature. We do some stuff with + # it as though it partially implements the Mapping interface, but we're not + # doing that stuff safely AT ALL. + for key in other.keys(): + self.add(key, other[key]) + + for key, value in kwargs.items(): + self.add(key, value) + + @typing.overload + def getlist(self, key: str) -> list[str]: + ... + + @typing.overload + def getlist(self, key: str, default: _DT) -> list[str] | _DT: + ... + + def getlist( + self, key: str, default: _Sentinel | _DT = _Sentinel.not_passed + ) -> list[str] | _DT: + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + if default is _Sentinel.not_passed: + # _DT is unbound; empty list is instance of List[str] + return [] + # _DT is bound; default is instance of _DT + return default + else: + # _DT may or may not be bound; vals[1:] is instance of List[str], which + # meets our external interface requirement of `Union[List[str], _DT]`. + return vals[1:] + + def _prepare_for_method_change(self) -> Self: + """ + Remove content-specific header fields before changing the request + method to GET or HEAD according to RFC 9110, Section 15.4. + """ + content_specific_headers = [ + "Content-Encoding", + "Content-Language", + "Content-Location", + "Content-Type", + "Content-Length", + "Digest", + "Last-Modified", + ] + for header in content_specific_headers: + self.discard(header) + return self + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + # Backwards compatibility for http.cookiejar + get_all = getlist + + def __repr__(self) -> str: + return f"{type(self).__name__}({dict(self.itermerged())})" + + def _copy_from(self, other: HTTPHeaderDict) -> None: + for key in other: + val = other.getlist(key) + self._container[key.lower()] = [key, *val] + + def copy(self) -> HTTPHeaderDict: + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self) -> typing.Iterator[tuple[str, str]]: + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self) -> typing.Iterator[tuple[str, str]]: + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ", ".join(val[1:]) + + def items(self) -> HTTPHeaderDictItemView: # type: ignore[override] + return HTTPHeaderDictItemView(self) + + def _has_value_for_header(self, header_name: str, potential_value: str) -> bool: + if header_name in self: + return potential_value in self._container[header_name.lower()][1:] + return False + + def __ior__(self, other: object) -> HTTPHeaderDict: + # Supports extending a header dict in-place using operator |= + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + self.extend(maybe_constructable) + return self + + def __or__(self, other: object) -> HTTPHeaderDict: + # Supports merging header dicts using operator | + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + result = self.copy() + result.extend(maybe_constructable) + return result + + def __ror__(self, other: object) -> HTTPHeaderDict: + # Supports merging header dicts using operator | when other is on left side + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + result = type(self)(maybe_constructable) + result.extend(self) + return result diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/_request_methods.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/_request_methods.py new file mode 100644 index 0000000..632042f --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/_request_methods.py @@ -0,0 +1,279 @@ +from __future__ import annotations + +import json as _json +import typing +from urllib.parse import urlencode + +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from .filepost import _TYPE_FIELDS, encode_multipart_formdata +from .response import BaseHTTPResponse + +__all__ = ["RequestMethods"] + +_TYPE_ENCODE_URL_FIELDS = typing.Union[ + typing.Sequence[typing.Tuple[str, typing.Union[str, bytes]]], + typing.Mapping[str, typing.Union[str, bytes]], +] + + +class RequestMethods: + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`urllib3.HTTPConnectionPool` and + :class:`urllib3.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} + + def __init__(self, headers: typing.Mapping[str, str] | None = None) -> None: + self.headers = headers or {} + + def urlopen( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + encode_multipart: bool = True, + multipart_boundary: str | None = None, + **kw: typing.Any, + ) -> BaseHTTPResponse: # Abstract + raise NotImplementedError( + "Classes extending RequestMethods must implement " + "their own ``urlopen`` method." + ) + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + json: typing.Any | None = None, + **urlopen_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param fields: + Data to encode and send in the request body. Values are processed + by :func:`urllib.parse.urlencode`. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param json: + Data to encode and send as JSON with UTF-encoded in the request body. + The ``"Content-Type"`` header will be set to ``"application/json"`` + unless specified otherwise. + """ + method = method.upper() + + if json is not None and body is not None: + raise TypeError( + "request got values for both 'body' and 'json' parameters which are mutually exclusive" + ) + + if json is not None: + if headers is None: + headers = self.headers + + if not ("content-type" in map(str.lower, headers.keys())): + headers = HTTPHeaderDict(headers) + headers["Content-Type"] = "application/json" + + body = _json.dumps(json, separators=(",", ":"), ensure_ascii=False).encode( + "utf-8" + ) + + if body is not None: + urlopen_kw["body"] = body + + if method in self._encode_url_methods: + return self.request_encode_url( + method, + url, + fields=fields, # type: ignore[arg-type] + headers=headers, + **urlopen_kw, + ) + else: + return self.request_encode_body( + method, url, fields=fields, headers=headers, **urlopen_kw + ) + + def request_encode_url( + self, + method: str, + url: str, + fields: _TYPE_ENCODE_URL_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + **urlopen_kw: str, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param fields: + Data to encode and send in the request body. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + """ + if headers is None: + headers = self.headers + + extra_kw: dict[str, typing.Any] = {"headers": headers} + extra_kw.update(urlopen_kw) + + if fields: + url += "?" + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body( + self, + method: str, + url: str, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + encode_multipart: bool = True, + multipart_boundary: str | None = None, + **urlopen_kw: str, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :func:`urllib3.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :func:`urllib.parse.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimic behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param fields: + Data to encode and send in the request body. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param encode_multipart: + If True, encode the ``fields`` using the multipart/form-data MIME + format. + + :param multipart_boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + if headers is None: + headers = self.headers + + extra_kw: dict[str, typing.Any] = {"headers": HTTPHeaderDict(headers)} + body: bytes | str + + if fields: + if "body" in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one." + ) + + if encode_multipart: + body, content_type = encode_multipart_formdata( + fields, boundary=multipart_boundary + ) + else: + body, content_type = ( + urlencode(fields), # type: ignore[arg-type] + "application/x-www-form-urlencoded", + ) + + extra_kw["body"] = body + extra_kw["headers"].setdefault("Content-Type", content_type) + + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/_version.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/_version.py new file mode 100644 index 0000000..095cf3c --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/_version.py @@ -0,0 +1,4 @@ +# This file is protected via CODEOWNERS +from __future__ import annotations + +__version__ = "2.2.1" diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/connection.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/connection.py new file mode 100644 index 0000000..aa5c547 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/connection.py @@ -0,0 +1,930 @@ +from __future__ import annotations + +import datetime +import logging +import os +import re +import socket +import sys +import typing +import warnings +from http.client import HTTPConnection as _HTTPConnection +from http.client import HTTPException as HTTPException # noqa: F401 +from http.client import ResponseNotReady +from socket import timeout as SocketTimeout + +if typing.TYPE_CHECKING: + from typing import Literal + + from .response import HTTPResponse + from .util.ssl_ import _TYPE_PEER_CERT_RET_DICT + from .util.ssltransport import SSLTransport + +from ._collections import HTTPHeaderDict +from .util.response import assert_header_parsing +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT, Timeout +from .util.util import to_str +from .util.wait import wait_for_read + +try: # Compiled with SSL? + import ssl + + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): + ssl = None # type: ignore[assignment] + + class BaseSSLError(BaseException): # type: ignore[no-redef] + pass + + +from ._base_connection import _TYPE_BODY +from ._base_connection import ProxyConfig as ProxyConfig +from ._base_connection import _ResponseOptions as _ResponseOptions +from ._version import __version__ +from .exceptions import ( + ConnectTimeoutError, + HeaderParsingError, + NameResolutionError, + NewConnectionError, + ProxyError, + SystemTimeWarning, +) +from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection, ssl_ +from .util.request import body_to_chunks +from .util.ssl_ import assert_fingerprint as _assert_fingerprint +from .util.ssl_ import ( + create_urllib3_context, + is_ipaddress, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .util.ssl_match_hostname import CertificateError, match_hostname +from .util.url import Url + +# Not a no-op, we're adding this to the namespace so it can be imported. +ConnectionError = ConnectionError +BrokenPipeError = BrokenPipeError + + +log = logging.getLogger(__name__) + +port_by_scheme = {"http": 80, "https": 443} + +# When it comes time to update this value as a part of regular maintenance +# (ie test_recent_date is failing) update it to ~6 months before the current date. +RECENT_DATE = datetime.date(2023, 6, 1) + +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + +_HAS_SYS_AUDIT = hasattr(sys, "audit") + + +class HTTPConnection(_HTTPConnection): + """ + Based on :class:`http.client.HTTPConnection` but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass: + + .. code-block:: python + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port: typing.ClassVar[int] = port_by_scheme["http"] # type: ignore[misc] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options: typing.ClassVar[connection._TYPE_SOCKET_OPTIONS] = [ + (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ] + + #: Whether this connection verifies the host's certificate. + is_verified: bool = False + + #: Whether this proxy connection verified the proxy host's certificate. + # If no proxy is currently connected to the value will be ``None``. + proxy_is_verified: bool | None = None + + blocksize: int + source_address: tuple[str, int] | None + socket_options: connection._TYPE_SOCKET_OPTIONS | None + + _has_connected_to_proxy: bool + _response_options: _ResponseOptions | None + _tunnel_host: str | None + _tunnel_port: int | None + _tunnel_scheme: str | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: None + | (connection._TYPE_SOCKET_OPTIONS) = default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: + super().__init__( + host=host, + port=port, + timeout=Timeout.resolve_default_timeout(timeout), + source_address=source_address, + blocksize=blocksize, + ) + self.socket_options = socket_options + self.proxy = proxy + self.proxy_config = proxy_config + + self._has_connected_to_proxy = False + self._response_options = None + self._tunnel_host: str | None = None + self._tunnel_port: int | None = None + self._tunnel_scheme: str | None = None + + @property + def host(self) -> str: + """ + Getter method to remove any trailing dots that indicate the hostname is an FQDN. + + In general, SSL certificates don't include the trailing dot indicating a + fully-qualified domain name, and thus, they don't validate properly when + checked against a domain name that includes the dot. In addition, some + servers may not expect to receive the trailing dot when provided. + + However, the hostname with trailing dot is critical to DNS resolution; doing a + lookup with the trailing dot will properly only resolve the appropriate FQDN, + whereas a lookup without a trailing dot will search the system's search domain + list. Thus, it's important to keep the original host around for use only in + those cases where it's appropriate (i.e., when doing DNS lookup to establish the + actual TCP connection across which we're going to send HTTP requests). + """ + return self._dns_host.rstrip(".") + + @host.setter + def host(self, value: str) -> None: + """ + Setter for the `host` property. + + We assume that only urllib3 uses the _dns_host attribute; httplib itself + only uses `host`, and it seems reasonable that other libraries follow suit. + """ + self._dns_host = value + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + # Audit hooks are only available in Python 3.8+ + if _HAS_SYS_AUDIT: + sys.audit("http.client.connect", self, self.host, self.port) + + return sock + + def set_tunnel( + self, + host: str, + port: int | None = None, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + if scheme not in ("http", "https"): + raise ValueError( + f"Invalid proxy scheme for tunneling: {scheme!r}, must be either 'http' or 'https'" + ) + super().set_tunnel(host, port=port, headers=headers) + self._tunnel_scheme = scheme + + def connect(self) -> None: + self.sock = self._new_conn() + if self._tunnel_host: + # If we're tunneling it means we're connected to our proxy. + self._has_connected_to_proxy = True + + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() # type: ignore[attr-defined] + + # If there's a proxy to be connected to we are fully connected. + # This is set twice (once above and here) due to forwarding proxies + # not using tunnelling. + self._has_connected_to_proxy = bool(self.proxy) + + if self._has_connected_to_proxy: + self.proxy_is_verified = False + + @property + def is_closed(self) -> bool: + return self.sock is None + + @property + def is_connected(self) -> bool: + if self.sock is None: + return False + return not wait_for_read(self.sock, timeout=0.0) + + @property + def has_connected_to_proxy(self) -> bool: + return self._has_connected_to_proxy + + @property + def proxy_is_forwarding(self) -> bool: + """ + Return True if a forwarding proxy is configured, else return False + """ + return bool(self.proxy) and self._tunnel_host is None + + def close(self) -> None: + try: + super().close() + finally: + # Reset all stateful properties so connection + # can be re-used without leaking prior configs. + self.sock = None + self.is_verified = False + self.proxy_is_verified = None + self._has_connected_to_proxy = False + self._response_options = None + self._tunnel_host = None + self._tunnel_port = None + self._tunnel_scheme = None + + def putrequest( + self, + method: str, + url: str, + skip_host: bool = False, + skip_accept_encoding: bool = False, + ) -> None: + """""" + # Empty docstring because the indentation of CPython's implementation + # is broken but we don't want this method in our documentation. + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + f"Method cannot contain non-token characters {method!r} (found at least {match.group()!r})" + ) + + return super().putrequest( + method, url, skip_host=skip_host, skip_accept_encoding=skip_accept_encoding + ) + + def putheader(self, header: str, *values: str) -> None: # type: ignore[override] + """""" + if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): + super().putheader(header, *values) + elif to_str(header.lower()) not in SKIPPABLE_HEADERS: + skippable_headers = "', '".join( + [str.title(header) for header in sorted(SKIPPABLE_HEADERS)] + ) + raise ValueError( + f"urllib3.util.SKIP_HEADER only supports '{skippable_headers}'" + ) + + # `request` method's signature intentionally violates LSP. + # urllib3's API is different from `http.client.HTTPConnection` and the subclassing is only incidental. + def request( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: + # Update the inner socket's timeout value to send the request. + # This only triggers if the connection is re-used. + if self.sock is not None: + self.sock.settimeout(self.timeout) + + # Store these values to be fed into the HTTPResponse + # object later. TODO: Remove this in favor of a real + # HTTP lifecycle mechanism. + + # We have to store these before we call .request() + # because sometimes we can still salvage a response + # off the wire even if we aren't able to completely + # send the request body. + self._response_options = _ResponseOptions( + request_method=method, + request_url=url, + preload_content=preload_content, + decode_content=decode_content, + enforce_content_length=enforce_content_length, + ) + + if headers is None: + headers = {} + header_keys = frozenset(to_str(k.lower()) for k in headers) + skip_accept_encoding = "accept-encoding" in header_keys + skip_host = "host" in header_keys + self.putrequest( + method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host + ) + + # Transform the body into an iterable of sendall()-able chunks + # and detect if an explicit Content-Length is doable. + chunks_and_cl = body_to_chunks(body, method=method, blocksize=self.blocksize) + chunks = chunks_and_cl.chunks + content_length = chunks_and_cl.content_length + + # When chunked is explicit set to 'True' we respect that. + if chunked: + if "transfer-encoding" not in header_keys: + self.putheader("Transfer-Encoding", "chunked") + else: + # Detect whether a framing mechanism is already in use. If so + # we respect that value, otherwise we pick chunked vs content-length + # depending on the type of 'body'. + if "content-length" in header_keys: + chunked = False + elif "transfer-encoding" in header_keys: + chunked = True + + # Otherwise we go off the recommendation of 'body_to_chunks()'. + else: + chunked = False + if content_length is None: + if chunks is not None: + chunked = True + self.putheader("Transfer-Encoding", "chunked") + else: + self.putheader("Content-Length", str(content_length)) + + # Now that framing headers are out of the way we send all the other headers. + if "user-agent" not in header_keys: + self.putheader("User-Agent", _get_default_user_agent()) + for header, value in headers.items(): + self.putheader(header, value) + self.endheaders() + + # If we're given a body we start sending that in chunks. + if chunks is not None: + for chunk in chunks: + # Sending empty chunks isn't allowed for TE: chunked + # as it indicates the end of the body. + if not chunk: + continue + if isinstance(chunk, str): + chunk = chunk.encode("utf-8") + if chunked: + self.send(b"%x\r\n%b\r\n" % (len(chunk), chunk)) + else: + self.send(chunk) + + # Regardless of whether we have a body or not, if we're in + # chunked mode we want to send an explicit empty chunk. + if chunked: + self.send(b"0\r\n\r\n") + + def request_chunked( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + ) -> None: + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + warnings.warn( + "HTTPConnection.request_chunked() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTPConnection.request(..., chunked=True).", + category=DeprecationWarning, + stacklevel=2, + ) + self.request(method, url, body=body, headers=headers, chunked=True) + + def getresponse( # type: ignore[override] + self, + ) -> HTTPResponse: + """ + Get the response from the server. + + If the HTTPConnection is in the correct state, returns an instance of HTTPResponse or of whatever object is returned by the response_class variable. + + If a request has not been sent or if a previous response has not be handled, ResponseNotReady is raised. If the HTTP response indicates that the connection should be closed, then it will be closed before the response is returned. When the connection is closed, the underlying socket is closed. + """ + # Raise the same error as http.client.HTTPConnection + if self._response_options is None: + raise ResponseNotReady() + + # Reset this attribute for being used again. + resp_options = self._response_options + self._response_options = None + + # Since the connection's timeout value may have been updated + # we need to set the timeout on the socket. + self.sock.settimeout(self.timeout) + + # This is needed here to avoid circular import errors + from .response import HTTPResponse + + # Get the response from http.client.HTTPConnection + httplib_response = super().getresponse() + + try: + assert_header_parsing(httplib_response.msg) + except (HeaderParsingError, TypeError) as hpe: + log.warning( + "Failed to parse headers (url=%s): %s", + _url_from_connection(self, resp_options.request_url), + hpe, + exc_info=True, + ) + + headers = HTTPHeaderDict(httplib_response.msg.items()) + + response = HTTPResponse( + body=httplib_response, + headers=headers, + status=httplib_response.status, + version=httplib_response.version, + reason=httplib_response.reason, + preload_content=resp_options.preload_content, + decode_content=resp_options.decode_content, + original_response=httplib_response, + enforce_content_length=resp_options.enforce_content_length, + request_method=resp_options.request_method, + request_url=resp_options.request_url, + ) + return response + + +class HTTPSConnection(HTTPConnection): + """ + Many of the parameters to this constructor are passed to the underlying SSL + socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. + """ + + default_port = port_by_scheme["https"] # type: ignore[misc] + + cert_reqs: int | str | None = None + ca_certs: str | None = None + ca_cert_dir: str | None = None + ca_cert_data: None | str | bytes = None + ssl_version: int | str | None = None + ssl_minimum_version: int | None = None + ssl_maximum_version: int | None = None + assert_fingerprint: str | None = None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: None + | (connection._TYPE_SOCKET_OPTIONS) = HTTPConnection.default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: + super().__init__( + host, + port=port, + timeout=timeout, + source_address=source_address, + blocksize=blocksize, + socket_options=socket_options, + proxy=proxy, + proxy_config=proxy_config, + ) + + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + self.ssl_context = ssl_context + self.server_hostname = server_hostname + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + # cert_reqs depends on ssl_context so calculate last. + if cert_reqs is None: + if self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) + self.cert_reqs = cert_reqs + + def set_cert( + self, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + assert_hostname: None | str | Literal[False] = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ) -> None: + """ + This method should only be called once, before the connection is used. + """ + warnings.warn( + "HTTPSConnection.set_cert() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead provide the parameters to the " + "HTTPSConnection constructor.", + category=DeprecationWarning, + stacklevel=2, + ) + + # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also + # have an SSLContext object in which case we'll use its verify_mode. + if cert_reqs is None: + if self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + def connect(self) -> None: + sock: socket.socket | ssl.SSLSocket + self.sock = sock = self._new_conn() + server_hostname: str = self.host + tls_in_tls = False + + # Do we need to establish a tunnel? + if self._tunnel_host is not None: + # We're tunneling to an HTTPS origin so need to do TLS-in-TLS. + if self._tunnel_scheme == "https": + # _connect_tls_proxy will verify and assign proxy_is_verified + self.sock = sock = self._connect_tls_proxy(self.host, sock) + tls_in_tls = True + elif self._tunnel_scheme == "http": + self.proxy_is_verified = False + + # If we're tunneling it means we're connected to our proxy. + self._has_connected_to_proxy = True + + self._tunnel() # type: ignore[attr-defined] + # Override the host with the one we're requesting data from. + server_hostname = self._tunnel_host + + if self.server_hostname is not None: + server_hostname = self.server_hostname + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn( + ( + f"System time is way off (before {RECENT_DATE}). This will probably " + "lead to SSL verification errors" + ), + SystemTimeWarning, + ) + + # Remove trailing '.' from fqdn hostnames to allow certificate validation + server_hostname_rm_dot = server_hostname.rstrip(".") + + sock_and_verified = _ssl_wrap_socket_and_match_hostname( + sock=sock, + cert_reqs=self.cert_reqs, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + server_hostname=server_hostname_rm_dot, + ssl_context=self.ssl_context, + tls_in_tls=tls_in_tls, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ) + self.sock = sock_and_verified.socket + + # Forwarding proxies can never have a verified target since + # the proxy is the one doing the verification. Should instead + # use a CONNECT tunnel in order to verify the target. + # See: https://github.com/urllib3/urllib3/issues/3267. + if self.proxy_is_forwarding: + self.is_verified = False + else: + self.is_verified = sock_and_verified.is_verified + + # If there's a proxy to be connected to we are fully connected. + # This is set twice (once above and here) due to forwarding proxies + # not using tunnelling. + self._has_connected_to_proxy = bool(self.proxy) + + # Set `self.proxy_is_verified` unless it's already set while + # establishing a tunnel. + if self._has_connected_to_proxy and self.proxy_is_verified is None: + self.proxy_is_verified = sock_and_verified.is_verified + + def _connect_tls_proxy(self, hostname: str, sock: socket.socket) -> ssl.SSLSocket: + """ + Establish a TLS connection to the proxy using the provided SSL context. + """ + # `_connect_tls_proxy` is called when self._tunnel_host is truthy. + proxy_config = typing.cast(ProxyConfig, self.proxy_config) + ssl_context = proxy_config.ssl_context + sock_and_verified = _ssl_wrap_socket_and_match_hostname( + sock, + cert_reqs=self.cert_reqs, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + server_hostname=hostname, + ssl_context=ssl_context, + assert_hostname=proxy_config.assert_hostname, + assert_fingerprint=proxy_config.assert_fingerprint, + # Features that aren't implemented for proxies yet: + cert_file=None, + key_file=None, + key_password=None, + tls_in_tls=False, + ) + self.proxy_is_verified = sock_and_verified.is_verified + return sock_and_verified.socket # type: ignore[return-value] + + +class _WrappedAndVerifiedSocket(typing.NamedTuple): + """ + Wrapped socket and whether the connection is + verified after the TLS handshake + """ + + socket: ssl.SSLSocket | SSLTransport + is_verified: bool + + +def _ssl_wrap_socket_and_match_hostname( + sock: socket.socket, + *, + cert_reqs: None | str | int, + ssl_version: None | str | int, + ssl_minimum_version: int | None, + ssl_maximum_version: int | None, + cert_file: str | None, + key_file: str | None, + key_password: str | None, + ca_certs: str | None, + ca_cert_dir: str | None, + ca_cert_data: None | str | bytes, + assert_hostname: None | str | Literal[False], + assert_fingerprint: str | None, + server_hostname: str | None, + ssl_context: ssl.SSLContext | None, + tls_in_tls: bool = False, +) -> _WrappedAndVerifiedSocket: + """Logic for constructing an SSLContext from all TLS parameters, passing + that down into ssl_wrap_socket, and then doing certificate verification + either via hostname or fingerprint. This function exists to guarantee + that both proxies and targets have the same behavior when connecting via TLS. + """ + default_ssl_context = False + if ssl_context is None: + default_ssl_context = True + context = create_urllib3_context( + ssl_version=resolve_ssl_version(ssl_version), + ssl_minimum_version=ssl_minimum_version, + ssl_maximum_version=ssl_maximum_version, + cert_reqs=resolve_cert_reqs(cert_reqs), + ) + else: + context = ssl_context + + context.verify_mode = resolve_cert_reqs(cert_reqs) + + # In some cases, we want to verify hostnames ourselves + if ( + # `ssl` can't verify fingerprints or alternate hostnames + assert_fingerprint + or assert_hostname + # assert_hostname can be set to False to disable hostname checking + or assert_hostname is False + # We still support OpenSSL 1.0.2, which prevents us from verifying + # hostnames easily: https://github.com/pyca/pyopenssl/pull/933 + or ssl_.IS_PYOPENSSL + or not ssl_.HAS_NEVER_CHECK_COMMON_NAME + ): + context.check_hostname = False + + # Try to load OS default certs if none are given. We need to do the hasattr() check + # for custom pyOpenSSL SSLContext objects because they don't support + # load_default_certs(). + if ( + not ca_certs + and not ca_cert_dir + and not ca_cert_data + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + + # Ensure that IPv6 addresses are in the proper format and don't have a + # scope ID. Python's SSL module fails to recognize scoped IPv6 addresses + # and interprets them as DNS hostnames. + if server_hostname is not None: + normalized = server_hostname.strip("[]") + if "%" in normalized: + normalized = normalized[: normalized.rfind("%")] + if is_ipaddress(normalized): + server_hostname = normalized + + ssl_sock = ssl_wrap_socket( + sock=sock, + keyfile=key_file, + certfile=cert_file, + key_password=key_password, + ca_certs=ca_certs, + ca_cert_dir=ca_cert_dir, + ca_cert_data=ca_cert_data, + server_hostname=server_hostname, + ssl_context=context, + tls_in_tls=tls_in_tls, + ) + + try: + if assert_fingerprint: + _assert_fingerprint( + ssl_sock.getpeercert(binary_form=True), assert_fingerprint + ) + elif ( + context.verify_mode != ssl.CERT_NONE + and not context.check_hostname + and assert_hostname is not False + ): + cert: _TYPE_PEER_CERT_RET_DICT = ssl_sock.getpeercert() # type: ignore[assignment] + + # Need to signal to our match_hostname whether to use 'commonName' or not. + # If we're using our own constructed SSLContext we explicitly set 'False' + # because PyPy hard-codes 'True' from SSLContext.hostname_checks_common_name. + if default_ssl_context: + hostname_checks_common_name = False + else: + hostname_checks_common_name = ( + getattr(context, "hostname_checks_common_name", False) or False + ) + + _match_hostname( + cert, + assert_hostname or server_hostname, # type: ignore[arg-type] + hostname_checks_common_name, + ) + + return _WrappedAndVerifiedSocket( + socket=ssl_sock, + is_verified=context.verify_mode == ssl.CERT_REQUIRED + or bool(assert_fingerprint), + ) + except BaseException: + ssl_sock.close() + raise + + +def _match_hostname( + cert: _TYPE_PEER_CERT_RET_DICT | None, + asserted_hostname: str, + hostname_checks_common_name: bool = False, +) -> None: + # Our upstream implementation of ssl.match_hostname() + # only applies this normalization to IP addresses so it doesn't + # match DNS SANs so we do the same thing! + stripped_hostname = asserted_hostname.strip("[]") + if is_ipaddress(stripped_hostname): + asserted_hostname = stripped_hostname + + try: + match_hostname(cert, asserted_hostname, hostname_checks_common_name) + except CertificateError as e: + log.warning( + "Certificate did not match expected hostname: %s. Certificate: %s", + asserted_hostname, + cert, + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert # type: ignore[attr-defined] + raise + + +def _wrap_proxy_error(err: Exception, proxy_scheme: str | None) -> ProxyError: + # Look for the phrase 'wrong version number', if found + # then we should warn the user that we're very sure that + # this proxy is HTTP-only and they have a configuration issue. + error_normalized = " ".join(re.split("[^a-z]", str(err).lower())) + is_likely_http_proxy = ( + "wrong version number" in error_normalized + or "unknown protocol" in error_normalized + or "record layer failure" in error_normalized + ) + http_proxy_warning = ( + ". Your proxy appears to only use HTTP and not HTTPS, " + "try changing your proxy URL to be HTTP. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#https-proxy-error-http-proxy" + ) + new_err = ProxyError( + f"Unable to connect to proxy" + f"{http_proxy_warning if is_likely_http_proxy and proxy_scheme == 'https' else ''}", + err, + ) + new_err.__cause__ = err + return new_err + + +def _get_default_user_agent() -> str: + return f"python-urllib3/{__version__}" + + +class DummyConnection: + """Used to detect a failed ConnectionCls import.""" + + +if not ssl: + HTTPSConnection = DummyConnection # type: ignore[misc, assignment] # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection + + +def _url_from_connection( + conn: HTTPConnection | HTTPSConnection, path: str | None = None +) -> str: + """Returns the URL from a given connection. This is mainly used for testing and logging.""" + + scheme = "https" if isinstance(conn, HTTPSConnection) else "http" + + return Url(scheme=scheme, host=conn.host, port=conn.port, path=path).url diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/connectionpool.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/connectionpool.py new file mode 100644 index 0000000..bd58ff1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/connectionpool.py @@ -0,0 +1,1186 @@ +from __future__ import annotations + +import errno +import logging +import queue +import sys +import typing +import warnings +import weakref +from socket import timeout as SocketTimeout +from types import TracebackType + +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from ._request_methods import RequestMethods +from .connection import ( + BaseSSLError, + BrokenPipeError, + DummyConnection, + HTTPConnection, + HTTPException, + HTTPSConnection, + ProxyConfig, + _wrap_proxy_error, +) +from .connection import port_by_scheme as port_by_scheme +from .exceptions import ( + ClosedPoolError, + EmptyPoolError, + FullPoolError, + HostChangedError, + InsecureRequestWarning, + LocationValueError, + MaxRetryError, + NewConnectionError, + ProtocolError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, +) +from .response import BaseHTTPResponse +from .util.connection import is_connection_dropped +from .util.proxy import connection_requires_http_tunnel +from .util.request import _TYPE_BODY_POSITION, set_file_position +from .util.retry import Retry +from .util.ssl_match_hostname import CertificateError +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_DEFAULT, Timeout +from .util.url import Url, _encode_target +from .util.url import _normalize_host as normalize_host +from .util.url import parse_url +from .util.util import to_str + +if typing.TYPE_CHECKING: + import ssl + from typing import Literal + + from ._base_connection import BaseHTTPConnection, BaseHTTPSConnection + +log = logging.getLogger(__name__) + +_TYPE_TIMEOUT = typing.Union[Timeout, float, _TYPE_DEFAULT, None] + +_SelfT = typing.TypeVar("_SelfT") + + +# Pool objects +class ConnectionPool: + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. + """ + + scheme: str | None = None + QueueCls = queue.LifoQueue + + def __init__(self, host: str, port: int | None = None) -> None: + if not host: + raise LocationValueError("No host specified.") + + self.host = _normalize_host(host, scheme=self.scheme) + self.port = port + + # This property uses 'normalize_host()' (not '_normalize_host()') + # to avoid removing square braces around IPv6 addresses. + # This value is sent to `HTTPConnection.set_tunnel()` if called + # because square braces are required for HTTP CONNECT tunneling. + self._tunnel_host = normalize_host(host, scheme=self.scheme).lower() + + def __str__(self) -> str: + return f"{type(self).__name__}(host={self.host!r}, port={self.port!r})" + + def __enter__(self: _SelfT) -> _SelfT: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> Literal[False]: + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self) -> None: + """ + Close all pooled connections and disable the pool. + """ + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`http.client.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`http.client.HTTPConnection`. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.ProxyManager` + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.ProxyManager` + + :param \\**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = "http" + ConnectionCls: ( + type[BaseHTTPConnection] | type[BaseHTTPSConnection] + ) = HTTPConnection + + def __init__( + self, + host: str, + port: int | None = None, + timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, + maxsize: int = 1, + block: bool = False, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + _proxy: Url | None = None, + _proxy_headers: typing.Mapping[str, str] | None = None, + _proxy_config: ProxyConfig | None = None, + **conn_kw: typing.Any, + ): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool: queue.LifoQueue[typing.Any] | None = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + self.proxy_config = _proxy_config + + # Fill the queue up so that doing get() on it will block properly + for _ in range(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault("socket_options", []) + + self.conn_kw["proxy"] = self.proxy + self.conn_kw["proxy_config"] = self.proxy_config + + # Do not pass 'self' as callback to 'finalize'. + # Then the 'finalize' would keep an endless living (leak) to self. + # By just passing a reference to the pool allows the garbage collector + # to free self if nobody else has a reference to it. + pool = self.pool + + # Close all the HTTPConnections in the pool before the + # HTTPConnectionPool object is garbage collected. + weakref.finalize(self, _close_pool_connections, pool) + + def _new_conn(self) -> BaseHTTPConnection: + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTP connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "80", + ) + + conn = self.ConnectionCls( + host=self.host, + port=self.port, + timeout=self.timeout.connect_timeout, + **self.conn_kw, + ) + return conn + + def _get_conn(self, timeout: float | None = None) -> BaseHTTPConnection: + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + + if self.pool is None: + raise ClosedPoolError(self, "Pool is closed.") + + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") from None # Defensive: + + except queue.Empty: + if self.block: + raise EmptyPoolError( + self, + "Pool is empty and a new connection can't be opened due to blocking mode.", + ) from None + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.debug("Resetting dropped connection: %s", self.host) + conn.close() + + return conn or self._new_conn() + + def _put_conn(self, conn: BaseHTTPConnection | None) -> None: + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + if self.pool is not None: + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except queue.Full: + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + if self.block: + # This should never happen if you got the conn from self._get_conn + raise FullPoolError( + self, + "Pool reached maximum size and no more connections are allowed.", + ) from None + + log.warning( + "Connection pool is full, discarding connection: %s. Connection pool size: %s", + self.host, + self.pool.qsize(), + ) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn: BaseHTTPConnection) -> None: + """ + Called right before a request is made, after the socket is created. + """ + + def _prepare_proxy(self, conn: BaseHTTPConnection) -> None: + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout: _TYPE_TIMEOUT) -> Timeout: + """Helper that always returns a :class:`urllib3.util.Timeout`""" + if timeout is _DEFAULT_TIMEOUT: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout( + self, + err: BaseSSLError | OSError | SocketTimeout, + url: str, + timeout_value: _TYPE_TIMEOUT | None, + ) -> None: + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={timeout_value})" + ) from err + + # See the above comment about EAGAIN in Python 3. + if hasattr(err, "errno") and err.errno in _blocking_errnos: + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={timeout_value})" + ) from err + + def _make_request( + self, + conn: BaseHTTPConnection, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | None = None, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + chunked: bool = False, + response_conn: BaseHTTPConnection | None = None, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> BaseHTTPResponse: + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param response_conn: + Set this to ``None`` if you will handle releasing the connection or + set the connection to have the response release it. + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout) + + try: + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # _validate_conn() starts the connection to an HTTPS proxy + # so we need to wrap errors with 'ProxyError' here too. + except ( + OSError, + NewConnectionError, + TimeoutError, + BaseSSLError, + CertificateError, + SSLError, + ) as e: + new_e: Exception = e + if isinstance(e, (BaseSSLError, CertificateError)): + new_e = SSLError(e) + # If the connection didn't successfully connect to it's proxy + # then there + if isinstance( + new_e, (OSError, NewConnectionError, TimeoutError, SSLError) + ) and (conn and conn.proxy and not conn.has_connected_to_proxy): + new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) + raise new_e + + # conn.request() calls http.client.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + try: + conn.request( + method, + url, + body=body, + headers=headers, + chunked=chunked, + preload_content=preload_content, + decode_content=decode_content, + enforce_content_length=enforce_content_length, + ) + + # We are swallowing BrokenPipeError (errno.EPIPE) since the server is + # legitimately able to close the connection after sending a valid response. + # With this behaviour, the received response is still readable. + except BrokenPipeError: + pass + except OSError as e: + # MacOS/Linux + # EPROTOTYPE and ECONNRESET are needed on macOS + # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ + # Condition changed later to emit ECONNRESET instead of only EPROTOTYPE. + if e.errno != errno.EPROTOTYPE and e.errno != errno.ECONNRESET: + raise + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + if not conn.is_closed: + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={read_timeout})" + ) + conn.timeout = read_timeout + + # Receive the response from the server + try: + response = conn.getresponse() + except (BaseSSLError, OSError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # Set properties that are used by the pooling layer. + response.retries = retries + response._connection = response_conn # type: ignore[attr-defined] + response._pool = self # type: ignore[attr-defined] + + # emscripten connection doesn't have _http_vsn_str + http_version = getattr(conn, "_http_vsn_str", "HTTP/?") + log.debug( + '%s://%s:%s "%s %s %s" %s %s', + self.scheme, + self.host, + self.port, + method, + url, + # HTTP version + http_version, + response.status, + response.length_remaining, + ) + + return response + + def close(self) -> None: + """ + Close all pooled connections and disable the pool. + """ + if self.pool is None: + return + # Disable access to the pool + old_pool, self.pool = self.pool, None + + # Close all the HTTPConnections in the pool. + _close_pool_connections(old_pool) + + def is_same_host(self, url: str) -> bool: + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith("/"): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, _, host, port, *_ = parse_url(url) + scheme = scheme or "http" + if host is not None: + host = _normalize_host(host, scheme=scheme) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Everything went great! + clean_exit = True + + except EmptyPoolError: + # Didn't get a connection from the pool, no need to clean up + clean_exit = True + release_this_conn = False + raise + + except ( + TimeoutError, + HTTPException, + OSError, + ProtocolError, + BaseSSLError, + SSLError, + CertificateError, + ProxyError, + ) as e: + # Discard the connection for these exceptions. It will be + # replaced during the next _get_conn() call. + clean_exit = False + new_e: Exception = e + if isinstance(e, (BaseSSLError, CertificateError)): + new_e = SSLError(e) + if isinstance( + new_e, + ( + OSError, + NewConnectionError, + TimeoutError, + SSLError, + HTTPException, + ), + ) and (conn and conn.proxy and not conn.has_connected_to_proxy): + new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) + elif isinstance(new_e, (OSError, HTTPException)): + new_e = ProtocolError("Connection aborted.", new_e) + + retries = retries.increment( + method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2] + ) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + if conn: + conn.close() + conn = None + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning( + "Retrying (%r) after connection broken by '%r': %s", retries, err, url + ) + return self.urlopen( + method, + url, + body, + headers, + retries, + redirect, + assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + # Change the method according to RFC 9110, Section 15.4.4. + method = "GET" + # And lose the body not to transfer anything sensitive. + body = None + headers = HTTPHeaderDict(headers)._prepare_for_method_change() + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep_for_retry(response) + log.debug("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, + redirect_location, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Check if we should retry the HTTP response. + has_retry_after = bool(response.headers.get("Retry-After")) + if retries.is_retry(method, response.status, has_retry_after): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep(response) + log.debug("Retry: %s", url) + return self.urlopen( + method, + url, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` + is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = "https" + ConnectionCls: type[BaseHTTPSConnection] = HTTPSConnection + + def __init__( + self, + host: str, + port: int | None = None, + timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, + maxsize: int = 1, + block: bool = False, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + _proxy: Url | None = None, + _proxy_headers: typing.Mapping[str, str] | None = None, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + ssl_version: int | str | None = None, + ssl_minimum_version: ssl.TLSVersion | None = None, + ssl_maximum_version: ssl.TLSVersion | None = None, + assert_hostname: str | Literal[False] | None = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + **conn_kw: typing.Any, + ) -> None: + super().__init__( + host, + port, + timeout, + maxsize, + block, + headers, + retries, + _proxy, + _proxy_headers, + **conn_kw, + ) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_proxy(self, conn: HTTPSConnection) -> None: # type: ignore[override] + """Establishes a tunnel connection through HTTP CONNECT.""" + if self.proxy and self.proxy.scheme == "https": + tunnel_scheme = "https" + else: + tunnel_scheme = "http" + + conn.set_tunnel( + scheme=tunnel_scheme, + host=self._tunnel_host, + port=self.port, + headers=self.proxy_headers, + ) + conn.connect() + + def _new_conn(self) -> BaseHTTPSConnection: + """ + Return a fresh :class:`urllib3.connection.HTTPConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTPS connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "443", + ) + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: # type: ignore[comparison-overlap] + raise ImportError( + "Can't connect to HTTPS URL because the SSL module is not available." + ) + + actual_host: str = self.host + actual_port = self.port + if self.proxy is not None and self.proxy.host is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + return self.ConnectionCls( + host=actual_host, + port=actual_port, + timeout=self.timeout.connect_timeout, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + **self.conn_kw, + ) + + def _validate_conn(self, conn: BaseHTTPConnection) -> None: + """ + Called right before a request is made, after the socket is created. + """ + super()._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if conn.is_closed: + conn.connect() + + # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791 + if not conn.is_verified and not conn.proxy_is_verified: + warnings.warn( + ( + f"Unverified HTTPS request is being made to host '{conn.host}'. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#tls-warnings" + ), + InsecureRequestWarning, + ) + + +def connection_from_url(url: str, **kw: typing.Any) -> HTTPConnectionPool: + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \\**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, _, host, port, *_ = parse_url(url) + scheme = scheme or "http" + port = port or port_by_scheme.get(scheme, 80) + if scheme == "https": + return HTTPSConnectionPool(host, port=port, **kw) # type: ignore[arg-type] + else: + return HTTPConnectionPool(host, port=port, **kw) # type: ignore[arg-type] + + +@typing.overload +def _normalize_host(host: None, scheme: str | None) -> None: + ... + + +@typing.overload +def _normalize_host(host: str, scheme: str | None) -> str: + ... + + +def _normalize_host(host: str | None, scheme: str | None) -> str | None: + """ + Normalize hosts for comparisons and use with sockets. + """ + + host = normalize_host(host, scheme) + + # httplib doesn't like it when we include brackets in IPv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. See http://bugs.python.org/issue28539 + if host and host.startswith("[") and host.endswith("]"): + host = host[1:-1] + return host + + +def _url_from_pool( + pool: HTTPConnectionPool | HTTPSConnectionPool, path: str | None = None +) -> str: + """Returns the URL from a given connection pool. This is mainly used for testing and logging.""" + return Url(scheme=pool.scheme, host=pool.host, port=pool.port, path=path).url + + +def _close_pool_connections(pool: queue.LifoQueue[typing.Any]) -> None: + """Drains a queue of connections and closes each one.""" + try: + while True: + conn = pool.get(block=False) + if conn: + conn.close() + except queue.Empty: + pass # Done. diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__init__.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..804ef4c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-310.pyc new file mode 100644 index 0000000..fe5e5db Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/socks.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/socks.cpython-310.pyc new file mode 100644 index 0000000..7375578 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/__pycache__/socks.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__init__.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__init__.py new file mode 100644 index 0000000..8a3c5be --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__init__.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import urllib3.connection + +from ...connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connection import EmscriptenHTTPConnection, EmscriptenHTTPSConnection + + +def inject_into_urllib3() -> None: + # override connection classes to use emscripten specific classes + # n.b. mypy complains about the overriding of classes below + # if it isn't ignored + HTTPConnectionPool.ConnectionCls = EmscriptenHTTPConnection + HTTPSConnectionPool.ConnectionCls = EmscriptenHTTPSConnection + urllib3.connection.HTTPConnection = EmscriptenHTTPConnection # type: ignore[misc,assignment] + urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection # type: ignore[misc,assignment] diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..aeb8a56 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000..edcf510 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-310.pyc new file mode 100644 index 0000000..8935ed8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-310.pyc new file mode 100644 index 0000000..d2392d1 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000..8b393df Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/connection.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/connection.py new file mode 100644 index 0000000..2ceb457 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/connection.py @@ -0,0 +1,254 @@ +from __future__ import annotations + +import os +import typing + +# use http.client.HTTPException for consistency with non-emscripten +from http.client import HTTPException as HTTPException # noqa: F401 +from http.client import ResponseNotReady + +from ..._base_connection import _TYPE_BODY +from ...connection import HTTPConnection, ProxyConfig, port_by_scheme +from ...exceptions import TimeoutError +from ...response import BaseHTTPResponse +from ...util.connection import _TYPE_SOCKET_OPTIONS +from ...util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT +from ...util.url import Url +from .fetch import _RequestError, _TimeoutError, send_request, send_streaming_request +from .request import EmscriptenRequest +from .response import EmscriptenHttpResponseWrapper, EmscriptenResponse + +if typing.TYPE_CHECKING: + from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection + + +class EmscriptenHTTPConnection: + default_port: typing.ClassVar[int] = port_by_scheme["http"] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + timeout: None | (float) + + host: str + port: int + blocksize: int + source_address: tuple[str, int] | None + socket_options: _TYPE_SOCKET_OPTIONS | None + + proxy: Url | None + proxy_config: ProxyConfig | None + + is_verified: bool = False + proxy_is_verified: bool | None = None + + _response: EmscriptenResponse | None + + def __init__( + self, + host: str, + port: int = 0, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: + self.host = host + self.port = port + self.timeout = timeout if isinstance(timeout, float) else 0.0 + self.scheme = "http" + self._closed = True + self._response = None + # ignore these things because we don't + # have control over that stuff + self.proxy = None + self.proxy_config = None + self.blocksize = blocksize + self.source_address = None + self.socket_options = None + self.is_verified = False + + def set_tunnel( + self, + host: str, + port: int | None = 0, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + pass + + def connect(self) -> None: + pass + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + # We know *at least* botocore is depending on the order of the + # first 3 parameters so to be safe we only mark the later ones + # as keyword-only to ensure we have space to extend. + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: + self._closed = False + if url.startswith("/"): + # no scheme / host / port included, make a full url + url = f"{self.scheme}://{self.host}:{self.port}" + url + request = EmscriptenRequest( + url=url, + method=method, + timeout=self.timeout if self.timeout else 0, + decode_content=decode_content, + ) + request.set_body(body) + if headers: + for k, v in headers.items(): + request.set_header(k, v) + self._response = None + try: + if not preload_content: + self._response = send_streaming_request(request) + if self._response is None: + self._response = send_request(request) + except _TimeoutError as e: + raise TimeoutError(e.message) from e + except _RequestError as e: + raise HTTPException(e.message) from e + + def getresponse(self) -> BaseHTTPResponse: + if self._response is not None: + return EmscriptenHttpResponseWrapper( + internal_response=self._response, + url=self._response.request.url, + connection=self, + ) + else: + raise ResponseNotReady() + + def close(self) -> None: + self._closed = True + self._response = None + + @property + def is_closed(self) -> bool: + """Whether the connection either is brand new or has been previously closed. + If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` + properties must be False. + """ + return self._closed + + @property + def is_connected(self) -> bool: + """Whether the connection is actively connected to any origin (proxy or target)""" + return True + + @property + def has_connected_to_proxy(self) -> bool: + """Whether the connection has successfully connected to its proxy. + This returns False if no proxy is in use. Used to determine whether + errors are coming from the proxy layer or from tunnelling to the target origin. + """ + return False + + +class EmscriptenHTTPSConnection(EmscriptenHTTPConnection): + default_port = port_by_scheme["https"] + # all this is basically ignored, as browser handles https + cert_reqs: int | str | None = None + ca_certs: str | None = None + ca_cert_dir: str | None = None + ca_cert_data: None | str | bytes = None + cert_file: str | None + key_file: str | None + key_password: str | None + ssl_context: typing.Any | None + ssl_version: int | str | None = None + ssl_minimum_version: int | None = None + ssl_maximum_version: int | None = None + assert_hostname: None | str | typing.Literal[False] + assert_fingerprint: str | None = None + + def __init__( + self, + host: str, + port: int = 0, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: None + | _TYPE_SOCKET_OPTIONS = HTTPConnection.default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: typing.Any | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: + super().__init__( + host, + port=port, + timeout=timeout, + source_address=source_address, + blocksize=blocksize, + socket_options=socket_options, + proxy=proxy, + proxy_config=proxy_config, + ) + self.scheme = "https" + + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + self.ssl_context = ssl_context + self.server_hostname = server_hostname + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + self.cert_reqs = None + + # The browser will automatically verify all requests. + # We have no control over that setting. + self.is_verified = True + + def set_cert( + self, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ) -> None: + pass + + +# verify that this class implements BaseHTTP(s) connection correctly +if typing.TYPE_CHECKING: + _supports_http_protocol: BaseHTTPConnection = EmscriptenHTTPConnection("", 0) + _supports_https_protocol: BaseHTTPSConnection = EmscriptenHTTPSConnection("", 0) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js new file mode 100644 index 0000000..243b862 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js @@ -0,0 +1,110 @@ +let Status = { + SUCCESS_HEADER: -1, + SUCCESS_EOF: -2, + ERROR_TIMEOUT: -3, + ERROR_EXCEPTION: -4, +}; + +let connections = {}; +let nextConnectionID = 1; +const encoder = new TextEncoder(); + +self.addEventListener("message", async function (event) { + if (event.data.close) { + let connectionID = event.data.close; + delete connections[connectionID]; + return; + } else if (event.data.getMore) { + let connectionID = event.data.getMore; + let { curOffset, value, reader, intBuffer, byteBuffer } = + connections[connectionID]; + // if we still have some in buffer, then just send it back straight away + if (!value || curOffset >= value.length) { + // read another buffer if required + try { + let readResponse = await reader.read(); + + if (readResponse.done) { + // read everything - clear connection and return + delete connections[connectionID]; + Atomics.store(intBuffer, 0, Status.SUCCESS_EOF); + Atomics.notify(intBuffer, 0); + // finished reading successfully + // return from event handler + return; + } + curOffset = 0; + connections[connectionID].value = readResponse.value; + value = readResponse.value; + } catch (error) { + console.log("Request exception:", error); + let errorBytes = encoder.encode(error.message); + let written = errorBytes.length; + byteBuffer.set(errorBytes); + intBuffer[1] = written; + Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); + Atomics.notify(intBuffer, 0); + } + } + + // send as much buffer as we can + let curLen = value.length - curOffset; + if (curLen > byteBuffer.length) { + curLen = byteBuffer.length; + } + byteBuffer.set(value.subarray(curOffset, curOffset + curLen), 0); + + Atomics.store(intBuffer, 0, curLen); // store current length in bytes + Atomics.notify(intBuffer, 0); + curOffset += curLen; + connections[connectionID].curOffset = curOffset; + + return; + } else { + // start fetch + let connectionID = nextConnectionID; + nextConnectionID += 1; + const intBuffer = new Int32Array(event.data.buffer); + const byteBuffer = new Uint8Array(event.data.buffer, 8); + try { + const response = await fetch(event.data.url, event.data.fetchParams); + // return the headers first via textencoder + var headers = []; + for (const pair of response.headers.entries()) { + headers.push([pair[0], pair[1]]); + } + let headerObj = { + headers: headers, + status: response.status, + connectionID, + }; + const headerText = JSON.stringify(headerObj); + let headerBytes = encoder.encode(headerText); + let written = headerBytes.length; + byteBuffer.set(headerBytes); + intBuffer[1] = written; + // make a connection + connections[connectionID] = { + reader: response.body.getReader(), + intBuffer: intBuffer, + byteBuffer: byteBuffer, + value: undefined, + curOffset: 0, + }; + // set header ready + Atomics.store(intBuffer, 0, Status.SUCCESS_HEADER); + Atomics.notify(intBuffer, 0); + // all fetching after this goes through a new postmessage call with getMore + // this allows for parallel requests + } catch (error) { + console.log("Request exception:", error); + let errorBytes = encoder.encode(error.message); + let written = errorBytes.length; + byteBuffer.set(errorBytes); + intBuffer[1] = written; + Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); + Atomics.notify(intBuffer, 0); + } + } +}); +self.postMessage({ inited: true }); diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/fetch.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/fetch.py new file mode 100644 index 0000000..8d197ea --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/fetch.py @@ -0,0 +1,418 @@ +""" +Support for streaming http requests in emscripten. + +A few caveats - + +Firstly, you can't do streaming http in the main UI thread, because atomics.wait isn't allowed. +Streaming only works if you're running pyodide in a web worker. + +Secondly, this uses an extra web worker and SharedArrayBuffer to do the asynchronous fetch +operation, so it requires that you have crossOriginIsolation enabled, by serving over https +(or from localhost) with the two headers below set: + + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp + +You can tell if cross origin isolation is successfully enabled by looking at the global crossOriginIsolated variable in +javascript console. If it isn't, streaming requests will fallback to XMLHttpRequest, i.e. getting the whole +request into a buffer and then returning it. it shows a warning in the javascript console in this case. + +Finally, the webworker which does the streaming fetch is created on initial import, but will only be started once +control is returned to javascript. Call `await wait_for_streaming_ready()` to wait for streaming fetch. + +NB: in this code, there are a lot of javascript objects. They are named js_* +to make it clear what type of object they are. +""" +from __future__ import annotations + +import io +import json +from email.parser import Parser +from importlib.resources import files +from typing import TYPE_CHECKING, Any + +import js # type: ignore[import-not-found] +from pyodide.ffi import ( # type: ignore[import-not-found] + JsArray, + JsException, + JsProxy, + to_js, +) + +if TYPE_CHECKING: + from typing_extensions import Buffer + +from .request import EmscriptenRequest +from .response import EmscriptenResponse + +""" +There are some headers that trigger unintended CORS preflight requests. +See also https://github.com/koenvo/pyodide-http/issues/22 +""" +HEADERS_TO_IGNORE = ("user-agent",) + +SUCCESS_HEADER = -1 +SUCCESS_EOF = -2 +ERROR_TIMEOUT = -3 +ERROR_EXCEPTION = -4 + +_STREAMING_WORKER_CODE = ( + files(__package__) + .joinpath("emscripten_fetch_worker.js") + .read_text(encoding="utf-8") +) + + +class _RequestError(Exception): + def __init__( + self, + message: str | None = None, + *, + request: EmscriptenRequest | None = None, + response: EmscriptenResponse | None = None, + ): + self.request = request + self.response = response + self.message = message + super().__init__(self.message) + + +class _StreamingError(_RequestError): + pass + + +class _TimeoutError(_RequestError): + pass + + +def _obj_from_dict(dict_val: dict[str, Any]) -> JsProxy: + return to_js(dict_val, dict_converter=js.Object.fromEntries) + + +class _ReadStream(io.RawIOBase): + def __init__( + self, + int_buffer: JsArray, + byte_buffer: JsArray, + timeout: float, + worker: JsProxy, + connection_id: int, + request: EmscriptenRequest, + ): + self.int_buffer = int_buffer + self.byte_buffer = byte_buffer + self.read_pos = 0 + self.read_len = 0 + self.connection_id = connection_id + self.worker = worker + self.timeout = int(1000 * timeout) if timeout > 0 else None + self.is_live = True + self._is_closed = False + self.request: EmscriptenRequest | None = request + + def __del__(self) -> None: + self.close() + + # this is compatible with _base_connection + def is_closed(self) -> bool: + return self._is_closed + + # for compatibility with RawIOBase + @property + def closed(self) -> bool: + return self.is_closed() + + def close(self) -> None: + if not self.is_closed(): + self.read_len = 0 + self.read_pos = 0 + self.int_buffer = None + self.byte_buffer = None + self._is_closed = True + self.request = None + if self.is_live: + self.worker.postMessage(_obj_from_dict({"close": self.connection_id})) + self.is_live = False + super().close() + + def readable(self) -> bool: + return True + + def writable(self) -> bool: + return False + + def seekable(self) -> bool: + return False + + def readinto(self, byte_obj: Buffer) -> int: + if not self.int_buffer: + raise _StreamingError( + "No buffer for stream in _ReadStream.readinto", + request=self.request, + response=None, + ) + if self.read_len == 0: + # wait for the worker to send something + js.Atomics.store(self.int_buffer, 0, ERROR_TIMEOUT) + self.worker.postMessage(_obj_from_dict({"getMore": self.connection_id})) + if ( + js.Atomics.wait(self.int_buffer, 0, ERROR_TIMEOUT, self.timeout) + == "timed-out" + ): + raise _TimeoutError + data_len = self.int_buffer[0] + if data_len > 0: + self.read_len = data_len + self.read_pos = 0 + elif data_len == ERROR_EXCEPTION: + string_len = self.int_buffer[1] + # decode the error string + js_decoder = js.TextDecoder.new() + json_str = js_decoder.decode(self.byte_buffer.slice(0, string_len)) + raise _StreamingError( + f"Exception thrown in fetch: {json_str}", + request=self.request, + response=None, + ) + else: + # EOF, free the buffers and return zero + # and free the request + self.is_live = False + self.close() + return 0 + # copy from int32array to python bytes + ret_length = min(self.read_len, len(memoryview(byte_obj))) + subarray = self.byte_buffer.subarray( + self.read_pos, self.read_pos + ret_length + ).to_py() + memoryview(byte_obj)[0:ret_length] = subarray + self.read_len -= ret_length + self.read_pos += ret_length + return ret_length + + +class _StreamingFetcher: + def __init__(self) -> None: + # make web-worker and data buffer on startup + self.streaming_ready = False + + js_data_blob = js.Blob.new( + [_STREAMING_WORKER_CODE], _obj_from_dict({"type": "application/javascript"}) + ) + + def promise_resolver(js_resolve_fn: JsProxy, js_reject_fn: JsProxy) -> None: + def onMsg(e: JsProxy) -> None: + self.streaming_ready = True + js_resolve_fn(e) + + def onErr(e: JsProxy) -> None: + js_reject_fn(e) # Defensive: never happens in ci + + self.js_worker.onmessage = onMsg + self.js_worker.onerror = onErr + + js_data_url = js.URL.createObjectURL(js_data_blob) + self.js_worker = js.globalThis.Worker.new(js_data_url) + self.js_worker_ready_promise = js.globalThis.Promise.new(promise_resolver) + + def send(self, request: EmscriptenRequest) -> EmscriptenResponse: + headers = { + k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE + } + + body = request.body + fetch_data = {"headers": headers, "body": to_js(body), "method": request.method} + # start the request off in the worker + timeout = int(1000 * request.timeout) if request.timeout > 0 else None + js_shared_buffer = js.SharedArrayBuffer.new(1048576) + js_int_buffer = js.Int32Array.new(js_shared_buffer) + js_byte_buffer = js.Uint8Array.new(js_shared_buffer, 8) + + js.Atomics.store(js_int_buffer, 0, ERROR_TIMEOUT) + js.Atomics.notify(js_int_buffer, 0) + js_absolute_url = js.URL.new(request.url, js.location).href + self.js_worker.postMessage( + _obj_from_dict( + { + "buffer": js_shared_buffer, + "url": js_absolute_url, + "fetchParams": fetch_data, + } + ) + ) + # wait for the worker to send something + js.Atomics.wait(js_int_buffer, 0, ERROR_TIMEOUT, timeout) + if js_int_buffer[0] == ERROR_TIMEOUT: + raise _TimeoutError( + "Timeout connecting to streaming request", + request=request, + response=None, + ) + elif js_int_buffer[0] == SUCCESS_HEADER: + # got response + # header length is in second int of intBuffer + string_len = js_int_buffer[1] + # decode the rest to a JSON string + js_decoder = js.TextDecoder.new() + # this does a copy (the slice) because decode can't work on shared array + # for some silly reason + json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) + # get it as an object + response_obj = json.loads(json_str) + return EmscriptenResponse( + request=request, + status_code=response_obj["status"], + headers=response_obj["headers"], + body=_ReadStream( + js_int_buffer, + js_byte_buffer, + request.timeout, + self.js_worker, + response_obj["connectionID"], + request, + ), + ) + elif js_int_buffer[0] == ERROR_EXCEPTION: + string_len = js_int_buffer[1] + # decode the error string + js_decoder = js.TextDecoder.new() + json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) + raise _StreamingError( + f"Exception thrown in fetch: {json_str}", request=request, response=None + ) + else: + raise _StreamingError( + f"Unknown status from worker in fetch: {js_int_buffer[0]}", + request=request, + response=None, + ) + + +# check if we are in a worker or not +def is_in_browser_main_thread() -> bool: + return hasattr(js, "window") and hasattr(js, "self") and js.self == js.window + + +def is_cross_origin_isolated() -> bool: + return hasattr(js, "crossOriginIsolated") and js.crossOriginIsolated + + +def is_in_node() -> bool: + return ( + hasattr(js, "process") + and hasattr(js.process, "release") + and hasattr(js.process.release, "name") + and js.process.release.name == "node" + ) + + +def is_worker_available() -> bool: + return hasattr(js, "Worker") and hasattr(js, "Blob") + + +_fetcher: _StreamingFetcher | None = None + +if is_worker_available() and ( + (is_cross_origin_isolated() and not is_in_browser_main_thread()) + and (not is_in_node()) +): + _fetcher = _StreamingFetcher() +else: + _fetcher = None + + +def send_streaming_request(request: EmscriptenRequest) -> EmscriptenResponse | None: + if _fetcher and streaming_ready(): + return _fetcher.send(request) + else: + _show_streaming_warning() + return None + + +_SHOWN_TIMEOUT_WARNING = False + + +def _show_timeout_warning() -> None: + global _SHOWN_TIMEOUT_WARNING + if not _SHOWN_TIMEOUT_WARNING: + _SHOWN_TIMEOUT_WARNING = True + message = "Warning: Timeout is not available on main browser thread" + js.console.warn(message) + + +_SHOWN_STREAMING_WARNING = False + + +def _show_streaming_warning() -> None: + global _SHOWN_STREAMING_WARNING + if not _SHOWN_STREAMING_WARNING: + _SHOWN_STREAMING_WARNING = True + message = "Can't stream HTTP requests because: \n" + if not is_cross_origin_isolated(): + message += " Page is not cross-origin isolated\n" + if is_in_browser_main_thread(): + message += " Python is running in main browser thread\n" + if not is_worker_available(): + message += " Worker or Blob classes are not available in this environment." # Defensive: this is always False in browsers that we test in + if streaming_ready() is False: + message += """ Streaming fetch worker isn't ready. If you want to be sure that streaming fetch +is working, you need to call: 'await urllib3.contrib.emscripten.fetch.wait_for_streaming_ready()`""" + from js import console + + console.warn(message) + + +def send_request(request: EmscriptenRequest) -> EmscriptenResponse: + try: + js_xhr = js.XMLHttpRequest.new() + + if not is_in_browser_main_thread(): + js_xhr.responseType = "arraybuffer" + if request.timeout: + js_xhr.timeout = int(request.timeout * 1000) + else: + js_xhr.overrideMimeType("text/plain; charset=ISO-8859-15") + if request.timeout: + # timeout isn't available on the main thread - show a warning in console + # if it is set + _show_timeout_warning() + + js_xhr.open(request.method, request.url, False) + for name, value in request.headers.items(): + if name.lower() not in HEADERS_TO_IGNORE: + js_xhr.setRequestHeader(name, value) + + js_xhr.send(to_js(request.body)) + + headers = dict(Parser().parsestr(js_xhr.getAllResponseHeaders())) + + if not is_in_browser_main_thread(): + body = js_xhr.response.to_py().tobytes() + else: + body = js_xhr.response.encode("ISO-8859-15") + return EmscriptenResponse( + status_code=js_xhr.status, headers=headers, body=body, request=request + ) + except JsException as err: + if err.name == "TimeoutError": + raise _TimeoutError(err.message, request=request) + elif err.name == "NetworkError": + raise _RequestError(err.message, request=request) + else: + # general http error + raise _RequestError(err.message, request=request) + + +def streaming_ready() -> bool | None: + if _fetcher: + return _fetcher.streaming_ready + else: + return None # no fetcher, return None to signify that + + +async def wait_for_streaming_ready() -> bool: + if _fetcher: + await _fetcher.js_worker_ready_promise + return True + else: + return False diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/request.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/request.py new file mode 100644 index 0000000..e692e69 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/request.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + +from ..._base_connection import _TYPE_BODY + + +@dataclass +class EmscriptenRequest: + method: str + url: str + params: dict[str, str] | None = None + body: _TYPE_BODY | None = None + headers: dict[str, str] = field(default_factory=dict) + timeout: float = 0 + decode_content: bool = True + + def set_header(self, name: str, value: str) -> None: + self.headers[name.capitalize()] = value + + def set_body(self, body: _TYPE_BODY | None) -> None: + self.body = body diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/response.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/response.py new file mode 100644 index 0000000..303b4ee --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/emscripten/response.py @@ -0,0 +1,276 @@ +from __future__ import annotations + +import json as _json +import logging +import typing +from contextlib import contextmanager +from dataclasses import dataclass +from http.client import HTTPException as HTTPException +from io import BytesIO, IOBase + +from ...exceptions import InvalidHeader, TimeoutError +from ...response import BaseHTTPResponse +from ...util.retry import Retry +from .request import EmscriptenRequest + +if typing.TYPE_CHECKING: + from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection + +log = logging.getLogger(__name__) + + +@dataclass +class EmscriptenResponse: + status_code: int + headers: dict[str, str] + body: IOBase | bytes + request: EmscriptenRequest + + +class EmscriptenHttpResponseWrapper(BaseHTTPResponse): + def __init__( + self, + internal_response: EmscriptenResponse, + url: str | None = None, + connection: BaseHTTPConnection | BaseHTTPSConnection | None = None, + ): + self._pool = None # set by pool class + self._body = None + self._response = internal_response + self._url = url + self._connection = connection + self._closed = False + super().__init__( + headers=internal_response.headers, + status=internal_response.status_code, + request_url=url, + version=0, + reason="", + decode_content=True, + ) + self.length_remaining = self._init_length(self._response.request.method) + self.length_is_certain = False + + @property + def url(self) -> str | None: + return self._url + + @url.setter + def url(self, url: str | None) -> None: + self._url = url + + @property + def connection(self) -> BaseHTTPConnection | BaseHTTPSConnection | None: + return self._connection + + @property + def retries(self) -> Retry | None: + return self._retries + + @retries.setter + def retries(self, retries: Retry | None) -> None: + # Override the request_url if retries has a redirect location. + self._retries = retries + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Generator[bytes, None, None]: + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + while True: + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + else: + break + + def _init_length(self, request_method: str | None) -> int | None: + length: int | None + content_length: str | None = self.headers.get("content-length") + + if content_length is not None: + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = {int(val) for val in content_length.split(",")} + if len(lengths) > 1: + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % content_length + ) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + else: # if content_length is None + length = None + + # Check for responses that shouldn't include a body + if ( + self.status in (204, 304) + or 100 <= self.status < 200 + or request_method == "HEAD" + ): + length = 0 + + return length + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, # ignored because browser decodes always + cache_content: bool = False, + ) -> bytes: + if ( + self._closed + or self._response is None + or (isinstance(self._response.body, IOBase) and self._response.body.closed) + ): + return b"" + + with self._error_catcher(): + # body has been preloaded as a string by XmlHttpRequest + if not isinstance(self._response.body, IOBase): + self.length_remaining = len(self._response.body) + self.length_is_certain = True + # wrap body in IOStream + self._response.body = BytesIO(self._response.body) + if amt is not None: + # don't cache partial content + cache_content = False + data = self._response.body.read(amt) + if self.length_remaining is not None: + self.length_remaining = max(self.length_remaining - len(data), 0) + if (self.length_is_certain and self.length_remaining == 0) or len( + data + ) < amt: + # definitely finished reading, close response stream + self._response.body.close() + return typing.cast(bytes, data) + else: # read all we can (and cache it) + data = self._response.body.read() + if cache_content: + self._body = data + if self.length_remaining is not None: + self.length_remaining = max(self.length_remaining - len(data), 0) + if len(data) == 0 or ( + self.length_is_certain and self.length_remaining == 0 + ): + # definitely finished reading, close response stream + self._response.body.close() + return typing.cast(bytes, data) + + def read_chunked( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> typing.Generator[bytes, None, None]: + # chunked is handled by browser + while True: + bytes = self.read(amt, decode_content) + if not bytes: + break + yield bytes + + def release_conn(self) -> None: + if not self._pool or not self._connection: + return None + + self._pool._put_conn(self._connection) + self._connection = None + + def drain_conn(self) -> None: + self.close() + + @property + def data(self) -> bytes: + if self._body: + return self._body + else: + return self.read(cache_content=True) + + def json(self) -> typing.Any: + """ + Parses the body of the HTTP response as JSON. + + To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to the decoder. + + This method can raise either `UnicodeDecodeError` or `json.JSONDecodeError`. + + Read more :ref:`here `. + """ + data = self.data.decode("utf-8") + return _json.loads(data) + + def close(self) -> None: + if not self._closed: + if isinstance(self._response.body, IOBase): + self._response.body.close() + if self._connection: + self._connection.close() + self._connection = None + self._closed = True + + @contextmanager + def _error_catcher(self) -> typing.Generator[None, None, None]: + """ + Catch Emscripten specific exceptions thrown by fetch.py, + instead re-raising urllib3 variants, so that low-level exceptions + are not leaked in the high-level api. + + On exit, release the connection back to the pool. + """ + from .fetch import _RequestError, _TimeoutError # avoid circular import + + clean_exit = False + + try: + yield + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + except _TimeoutError as e: + raise TimeoutError(str(e)) + except _RequestError as e: + raise HTTPException(str(e)) + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now + if ( + isinstance(self._response.body, IOBase) + and not self._response.body.closed + ): + self._response.body.close() + # release the connection back to the pool + self.release_conn() + else: + # If we have read everything from the response stream, + # return the connection back to the pool. + if ( + isinstance(self._response.body, IOBase) + and self._response.body.closed + ): + self.release_conn() diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/pyopenssl.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/pyopenssl.py new file mode 100644 index 0000000..b89a6da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/pyopenssl.py @@ -0,0 +1,548 @@ +""" +Module for using pyOpenSSL as a TLS backend. This module was relevant before +the standard library ``ssl`` module supported SNI, but now that we've dropped +support for Python 2.7 all relevant Python versions support SNI so +**this module is no longer recommended**. + +This needs the following packages installed: + +* `pyOpenSSL`_ (tested with 16.0.0) +* `cryptography`_ (minimum 1.3.4, from pyopenssl) +* `idna`_ (minimum 2.0) + +However, pyOpenSSL depends on cryptography, so while we use all three directly here we +end up having relatively few packages required. + +You can install them with the following command: + +.. code-block:: bash + + $ python -m pip install pyopenssl cryptography idna + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this: + +.. code-block:: python + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +.. _pyopenssl: https://www.pyopenssl.org +.. _cryptography: https://cryptography.io +.. _idna: https://github.com/kjd/idna +""" + +from __future__ import annotations + +import OpenSSL.SSL # type: ignore[import-untyped] +from cryptography import x509 + +try: + from cryptography.x509 import UnsupportedExtension # type: ignore[attr-defined] +except ImportError: + # UnsupportedExtension is gone in cryptography >= 2.1.0 + class UnsupportedExtension(Exception): # type: ignore[no-redef] + pass + + +import logging +import ssl +import typing +from io import BytesIO +from socket import socket as socket_cls +from socket import timeout + +from .. import util + +if typing.TYPE_CHECKING: + from OpenSSL.crypto import X509 # type: ignore[import-untyped] + + +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions: dict[int, int] = { + util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] + util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + + +_stdlib_to_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER + + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} +_openssl_to_stdlib_verify = {v: k for k, v in _stdlib_to_openssl_verify.items()} + +# The SSLvX values are the most likely to be missing in the future +# but we check them all just to be sure. +_OP_NO_SSLv2_OR_SSLv3: int = getattr(OpenSSL.SSL, "OP_NO_SSLv2", 0) | getattr( + OpenSSL.SSL, "OP_NO_SSLv3", 0 +) +_OP_NO_TLSv1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1", 0) +_OP_NO_TLSv1_1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_1", 0) +_OP_NO_TLSv1_2: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_2", 0) +_OP_NO_TLSv1_3: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_3", 0) + +_openssl_to_ssl_minimum_version: dict[int, int] = { + ssl.TLSVersion.MINIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.TLSv1: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1, + ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1, + ssl.TLSVersion.TLSv1_3: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 + ), + ssl.TLSVersion.MAXIMUM_SUPPORTED: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 + ), +} +_openssl_to_ssl_maximum_version: dict[int, int] = { + ssl.TLSVersion.MINIMUM_SUPPORTED: ( + _OP_NO_SSLv2_OR_SSLv3 + | _OP_NO_TLSv1 + | _OP_NO_TLSv1_1 + | _OP_NO_TLSv1_2 + | _OP_NO_TLSv1_3 + ), + ssl.TLSVersion.TLSv1: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3 + ), + ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3, + ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_3, + ssl.TLSVersion.TLSv1_3: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.MAXIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, +} + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_SSLContext = util.ssl_.SSLContext + + +log = logging.getLogger(__name__) + + +def inject_into_urllib3() -> None: + "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." + + _validate_dependencies_met() + + util.SSLContext = PyOpenSSLContext # type: ignore[assignment] + util.ssl_.SSLContext = PyOpenSSLContext # type: ignore[assignment] + util.IS_PYOPENSSL = True + util.ssl_.IS_PYOPENSSL = True + + +def extract_from_urllib3() -> None: + "Undo monkey-patching by :func:`inject_into_urllib3`." + + util.SSLContext = orig_util_SSLContext + util.ssl_.SSLContext = orig_util_SSLContext + util.IS_PYOPENSSL = False + util.ssl_.IS_PYOPENSSL = False + + +def _validate_dependencies_met() -> None: + """ + Verifies that PyOpenSSL's package-level dependencies have been met. + Throws `ImportError` if they are not met. + """ + # Method added in `cryptography==1.1`; not available in older versions + from cryptography.x509.extensions import Extensions + + if getattr(Extensions, "get_extension_for_class", None) is None: + raise ImportError( + "'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer." + ) + + # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 + # attribute is only present on those versions. + from OpenSSL.crypto import X509 + + x509 = X509() + if getattr(x509, "_x509", None) is None: + raise ImportError( + "'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer." + ) + + +def _dnsname_to_stdlib(name: str) -> str | None: + """ + Converts a dNSName SubjectAlternativeName field to the form used by the + standard library on the given Python version. + + Cryptography produces a dNSName as a unicode string that was idna-decoded + from ASCII bytes. We need to idna-encode that string to get it back, and + then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib + uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). + + If the name cannot be idna-encoded then we return None signalling that + the name given should be skipped. + """ + + def idna_encode(name: str) -> bytes | None: + """ + Borrowed wholesale from the Python Cryptography Project. It turns out + that we can't just safely call `idna.encode`: it can explode for + wildcard names. This avoids that problem. + """ + import idna + + try: + for prefix in ["*.", "."]: + if name.startswith(prefix): + name = name[len(prefix) :] + return prefix.encode("ascii") + idna.encode(name) + return idna.encode(name) + except idna.core.IDNAError: + return None + + # Don't send IPv6 addresses through the IDNA encoder. + if ":" in name: + return name + + encoded_name = idna_encode(name) + if encoded_name is None: + return None + return encoded_name.decode("utf-8") + + +def get_subj_alt_name(peer_cert: X509) -> list[tuple[str, str]]: + """ + Given an PyOpenSSL certificate, provides all the subject alternative names. + """ + cert = peer_cert.to_cryptography() + + # We want to find the SAN extension. Ask Cryptography to locate it (it's + # faster than looping in Python) + try: + ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value + except x509.ExtensionNotFound: + # No such extension, return the empty list. + return [] + except ( + x509.DuplicateExtension, + UnsupportedExtension, + x509.UnsupportedGeneralNameType, + UnicodeError, + ) as e: + # A problem has been found with the quality of the certificate. Assume + # no SAN field is present. + log.warning( + "A problem was encountered with the certificate that prevented " + "urllib3 from finding the SubjectAlternativeName field. This can " + "affect certificate validation. The error was %s", + e, + ) + return [] + + # We want to return dNSName and iPAddress fields. We need to cast the IPs + # back to strings because the match_hostname function wants them as + # strings. + # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 + # decoded. This is pretty frustrating, but that's what the standard library + # does with certificates, and so we need to attempt to do the same. + # We also want to skip over names which cannot be idna encoded. + names = [ + ("DNS", name) + for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) + if name is not None + ] + names.extend( + ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) + ) + + return names + + +class WrappedSocket: + """API-compatibility wrapper for Python OpenSSL's Connection-class.""" + + def __init__( + self, + connection: OpenSSL.SSL.Connection, + socket: socket_cls, + suppress_ragged_eofs: bool = True, + ) -> None: + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._io_refs = 0 + self._closed = False + + def fileno(self) -> int: + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self) -> None: + if self._io_refs > 0: + self._io_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args: typing.Any, **kwargs: typing.Any) -> bytes: + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return b"" + else: + raise OSError(e.args[0], str(e)) from e + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b"" + else: + raise + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") from e + else: + return self.recv(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"read error: {e!r}") from e + else: + return data # type: ignore[no-any-return] + + def recv_into(self, *args: typing.Any, **kwargs: typing.Any) -> int: + try: + return self.connection.recv_into(*args, **kwargs) # type: ignore[no-any-return] + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return 0 + else: + raise OSError(e.args[0], str(e)) from e + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") from e + else: + return self.recv_into(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"read error: {e!r}") from e + + def settimeout(self, timeout: float) -> None: + return self.socket.settimeout(timeout) + + def _send_until_done(self, data: bytes) -> int: + while True: + try: + return self.connection.send(data) # type: ignore[no-any-return] + except OpenSSL.SSL.WantWriteError as e: + if not util.wait_for_write(self.socket, self.socket.gettimeout()): + raise timeout() from e + continue + except OpenSSL.SSL.SysCallError as e: + raise OSError(e.args[0], str(e)) from e + + def sendall(self, data: bytes) -> None: + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done( + data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] + ) + total_sent += sent + + def shutdown(self) -> None: + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() + + def close(self) -> None: + self._closed = True + if self._io_refs <= 0: + self._real_close() + + def _real_close(self) -> None: + try: + return self.connection.close() # type: ignore[no-any-return] + except OpenSSL.SSL.Error: + return + + def getpeercert( + self, binary_form: bool = False + ) -> dict[str, list[typing.Any]] | None: + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 # type: ignore[no-any-return] + + if binary_form: + return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) # type: ignore[no-any-return] + + return { + "subject": ((("commonName", x509.get_subject().CN),),), # type: ignore[dict-item] + "subjectAltName": get_subj_alt_name(x509), + } + + def version(self) -> str: + return self.connection.get_protocol_version_name() # type: ignore[no-any-return] + + +WrappedSocket.makefile = socket_cls.makefile # type: ignore[attr-defined] + + +class PyOpenSSLContext: + """ + I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible + for translating the interface of the standard library ``SSLContext`` object + to calls into PyOpenSSL. + """ + + def __init__(self, protocol: int) -> None: + self.protocol = _openssl_versions[protocol] + self._ctx = OpenSSL.SSL.Context(self.protocol) + self._options = 0 + self.check_hostname = False + self._minimum_version: int = ssl.TLSVersion.MINIMUM_SUPPORTED + self._maximum_version: int = ssl.TLSVersion.MAXIMUM_SUPPORTED + + @property + def options(self) -> int: + return self._options + + @options.setter + def options(self, value: int) -> None: + self._options = value + self._set_ctx_options() + + @property + def verify_mode(self) -> int: + return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] + + @verify_mode.setter + def verify_mode(self, value: ssl.VerifyMode) -> None: + self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) + + def set_default_verify_paths(self) -> None: + self._ctx.set_default_verify_paths() + + def set_ciphers(self, ciphers: bytes | str) -> None: + if isinstance(ciphers, str): + ciphers = ciphers.encode("utf-8") + self._ctx.set_cipher_list(ciphers) + + def load_verify_locations( + self, + cafile: str | None = None, + capath: str | None = None, + cadata: bytes | None = None, + ) -> None: + if cafile is not None: + cafile = cafile.encode("utf-8") # type: ignore[assignment] + if capath is not None: + capath = capath.encode("utf-8") # type: ignore[assignment] + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"unable to load trusted certificates: {e!r}") from e + + def load_cert_chain( + self, + certfile: str, + keyfile: str | None = None, + password: str | None = None, + ) -> None: + try: + self._ctx.use_certificate_chain_file(certfile) + if password is not None: + if not isinstance(password, bytes): + password = password.encode("utf-8") # type: ignore[assignment] + self._ctx.set_passwd_cb(lambda *_: password) + self._ctx.use_privatekey_file(keyfile or certfile) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"Unable to load certificate chain: {e!r}") from e + + def set_alpn_protocols(self, protocols: list[bytes | str]) -> None: + protocols = [util.util.to_bytes(p, "ascii") for p in protocols] + return self._ctx.set_alpn_protos(protocols) # type: ignore[no-any-return] + + def wrap_socket( + self, + sock: socket_cls, + server_side: bool = False, + do_handshake_on_connect: bool = True, + suppress_ragged_eofs: bool = True, + server_hostname: bytes | str | None = None, + ) -> WrappedSocket: + cnx = OpenSSL.SSL.Connection(self._ctx, sock) + + # If server_hostname is an IP, don't use it for SNI, per RFC6066 Section 3 + if server_hostname and not util.ssl_.is_ipaddress(server_hostname): + if isinstance(server_hostname, str): + server_hostname = server_hostname.encode("utf-8") + cnx.set_tlsext_host_name(server_hostname) + + cnx.set_connect_state() + + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(sock, sock.gettimeout()): + raise timeout("select timed out") from e + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"bad handshake: {e!r}") from e + break + + return WrappedSocket(cnx, sock) + + def _set_ctx_options(self) -> None: + self._ctx.set_options( + self._options + | _openssl_to_ssl_minimum_version[self._minimum_version] + | _openssl_to_ssl_maximum_version[self._maximum_version] + ) + + @property + def minimum_version(self) -> int: + return self._minimum_version + + @minimum_version.setter + def minimum_version(self, minimum_version: int) -> None: + self._minimum_version = minimum_version + self._set_ctx_options() + + @property + def maximum_version(self) -> int: + return self._maximum_version + + @maximum_version.setter + def maximum_version(self, maximum_version: int) -> None: + self._maximum_version = maximum_version + self._set_ctx_options() + + +def _verify_callback( + cnx: OpenSSL.SSL.Connection, + x509: X509, + err_no: int, + err_depth: int, + return_code: int, +) -> bool: + return err_no == 0 diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/socks.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/socks.py new file mode 100644 index 0000000..5a80391 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/contrib/socks.py @@ -0,0 +1,230 @@ +""" +This module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +The SOCKS implementation supports the full range of urllib3 features. It also +supports the following SOCKS features: + +- SOCKS4A (``proxy_url='socks4a://...``) +- SOCKS4 (``proxy_url='socks4://...``) +- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) +- SOCKS5 with local DNS (``proxy_url='socks5://...``) +- Usernames and passwords for the SOCKS proxy + +.. note:: + It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in + your ``proxy_url`` to ensure that DNS resolution is done from the remote + server instead of client-side when connecting to a domain name. + +SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 +supports IPv4, IPv6, and domain names. + +When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` +will be sent as the ``userid`` section of the SOCKS request: + +.. code-block:: python + + proxy_url="socks4a://@proxy-host" + +When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion +of the ``proxy_url`` will be sent as the username/password to authenticate +with the proxy: + +.. code-block:: python + + proxy_url="socks5h://:@proxy-host" + +""" + +from __future__ import annotations + +try: + import socks # type: ignore[import-not-found] +except ImportError: + import warnings + + from ..exceptions import DependencyWarning + + warnings.warn( + ( + "SOCKS support in urllib3 requires the installation of optional " + "dependencies: specifically, PySocks. For more information, see " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html#socks-proxies" + ), + DependencyWarning, + ) + raise + +import typing +from socket import timeout as SocketTimeout + +from ..connection import HTTPConnection, HTTPSConnection +from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None # type: ignore[assignment] + +from typing import TypedDict + + +class _TYPE_SOCKS_OPTIONS(TypedDict): + socks_version: int + proxy_host: str | None + proxy_port: str | None + username: str | None + password: str | None + rdns: bool + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + + def __init__( + self, + _socks_options: _TYPE_SOCKS_OPTIONS, + *args: typing.Any, + **kwargs: typing.Any, + ) -> None: + self._socks_options = _socks_options + super().__init__(*args, **kwargs) + + def _new_conn(self) -> socks.socksocket: + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw: dict[str, typing.Any] = {} + if self.source_address: + extra_kw["source_address"] = self.source_address + + if self.socket_options: + extra_kw["socket_options"] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options["socks_version"], + proxy_addr=self._socks_options["proxy_host"], + proxy_port=self._socks_options["proxy_port"], + proxy_username=self._socks_options["username"], + proxy_password=self._socks_options["password"], + proxy_rdns=self._socks_options["rdns"], + timeout=self.timeout, + **extra_kw, + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + else: + # Adding `from e` messes with coverage somehow, so it's omitted. + # See #2386. + raise NewConnectionError( + self, f"Failed to establish a new connection: {error}" + ) + else: + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + except OSError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + + pool_classes_by_scheme = { + "http": SOCKSHTTPConnectionPool, + "https": SOCKSHTTPSConnectionPool, + } + + def __init__( + self, + proxy_url: str, + username: str | None = None, + password: str | None = None, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + **connection_pool_kw: typing.Any, + ): + parsed = parse_url(proxy_url) + + if username is None and password is None and parsed.auth is not None: + split = parsed.auth.split(":") + if len(split) == 2: + username, password = split + if parsed.scheme == "socks5": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = False + elif parsed.scheme == "socks5h": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = True + elif parsed.scheme == "socks4": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = False + elif parsed.scheme == "socks4a": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = True + else: + raise ValueError(f"Unable to determine SOCKS version from {proxy_url}") + + self.proxy_url = proxy_url + + socks_options = { + "socks_version": socks_version, + "proxy_host": parsed.host, + "proxy_port": parsed.port, + "username": username, + "password": password, + "rdns": rdns, + } + connection_pool_kw["_socks_options"] = socks_options + + super().__init__(num_pools, headers, **connection_pool_kw) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/exceptions.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/exceptions.py new file mode 100644 index 0000000..b0792f0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/exceptions.py @@ -0,0 +1,321 @@ +from __future__ import annotations + +import socket +import typing +import warnings +from email.errors import MessageDefect +from http.client import IncompleteRead as httplib_IncompleteRead + +if typing.TYPE_CHECKING: + from .connection import HTTPConnection + from .connectionpool import ConnectionPool + from .response import HTTPResponse + from .util.retry import Retry + +# Base Exceptions + + +class HTTPError(Exception): + """Base exception used by this module.""" + + +class HTTPWarning(Warning): + """Base warning used by this module.""" + + +_TYPE_REDUCE_RESULT = typing.Tuple[ + typing.Callable[..., object], typing.Tuple[object, ...] +] + + +class PoolError(HTTPError): + """Base exception for errors caused within a pool.""" + + def __init__(self, pool: ConnectionPool, message: str) -> None: + self.pool = pool + super().__init__(f"{pool}: {message}") + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + """Base exception for PoolErrors that have associated URLs.""" + + def __init__(self, pool: ConnectionPool, url: str, message: str) -> None: + self.url = url + super().__init__(pool, message) + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + """Raised when SSL certificate fails in an HTTPS connection.""" + + +class ProxyError(HTTPError): + """Raised when the connection to a proxy fails.""" + + # The original error is also available as __cause__. + original_error: Exception + + def __init__(self, message: str, error: Exception) -> None: + super().__init__(message, error) + self.original_error = error + + +class DecodeError(HTTPError): + """Raised when automatic decoding based on Content-Type fails.""" + + +class ProtocolError(HTTPError): + """Raised when something unexpected happens mid-request/response.""" + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param str url: The requested Url + :param reason: The underlying error + :type reason: :class:`Exception` + + """ + + def __init__( + self, pool: ConnectionPool, url: str, reason: Exception | None = None + ) -> None: + self.reason = reason + + message = f"Max retries exceeded with url: {url} (Caused by {reason!r})" + + super().__init__(pool, url, message) + + +class HostChangedError(RequestError): + """Raised when an existing pool gets a request for a foreign host.""" + + def __init__( + self, pool: ConnectionPool, url: str, retries: Retry | int = 3 + ) -> None: + message = f"Tried to open a foreign host with url: {url}" + super().__init__(pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """Raised when passing an invalid state to a timeout""" + + +class TimeoutError(HTTPError): + """Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + ` and :exc:`ConnectTimeoutErrors `. + """ + + +class ReadTimeoutError(TimeoutError, RequestError): + """Raised when a socket timeout occurs while receiving data from a server""" + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + """Raised when a socket timeout occurs while connecting to a server""" + + +class NewConnectionError(ConnectTimeoutError, HTTPError): + """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" + + def __init__(self, conn: HTTPConnection, message: str) -> None: + self.conn = conn + super().__init__(f"{conn}: {message}") + + @property + def pool(self) -> HTTPConnection: + warnings.warn( + "The 'pool' property is deprecated and will be removed " + "in urllib3 v2.1.0. Use 'conn' instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.conn + + +class NameResolutionError(NewConnectionError): + """Raised when host name resolution fails.""" + + def __init__(self, host: str, conn: HTTPConnection, reason: socket.gaierror): + message = f"Failed to resolve '{host}' ({reason})" + super().__init__(conn, message) + + +class EmptyPoolError(PoolError): + """Raised when a pool runs out of connections and no more are allowed.""" + + +class FullPoolError(PoolError): + """Raised when we try to add a connection to a full pool in blocking mode.""" + + +class ClosedPoolError(PoolError): + """Raised when a request enters a pool after the pool has been closed.""" + + +class LocationValueError(ValueError, HTTPError): + """Raised when there is something wrong with a given URL input.""" + + +class LocationParseError(LocationValueError): + """Raised when get_host or similar fails to parse the URL input.""" + + def __init__(self, location: str) -> None: + message = f"Failed to parse: {location}" + super().__init__(message) + + self.location = location + + +class URLSchemeUnknown(LocationValueError): + """Raised when a URL input has an unsupported scheme.""" + + def __init__(self, scheme: str): + message = f"Not supported URL scheme {scheme}" + super().__init__(message) + + self.scheme = scheme + + +class ResponseError(HTTPError): + """Used as a container for an error reason supplied in a MaxRetryError.""" + + GENERIC_ERROR = "too many error responses" + SPECIFIC_ERROR = "too many {status_code} error responses" + + +class SecurityWarning(HTTPWarning): + """Warned when performing security reducing actions""" + + +class InsecureRequestWarning(SecurityWarning): + """Warned when making an unverified HTTPS request.""" + + +class NotOpenSSLWarning(SecurityWarning): + """Warned when using unsupported SSL library""" + + +class SystemTimeWarning(SecurityWarning): + """Warned when system time is suspected to be wrong""" + + +class InsecurePlatformWarning(SecurityWarning): + """Warned when certain TLS/SSL configuration is not available on a platform.""" + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + + +class ResponseNotChunked(ProtocolError, ValueError): + """Response needs to be chunked in order to read it as chunks.""" + + +class BodyNotHttplibCompatible(HTTPError): + """ + Body should be :class:`http.client.HTTPResponse` like + (have an fp attribute which returns raw chunks) for read_chunked(). + """ + + +class IncompleteRead(HTTPError, httplib_IncompleteRead): + """ + Response length doesn't match expected Content-Length + + Subclass of :class:`http.client.IncompleteRead` to allow int value + for ``partial`` to avoid creating large objects on streamed reads. + """ + + partial: int # type: ignore[assignment] + expected: int + + def __init__(self, partial: int, expected: int) -> None: + self.partial = partial + self.expected = expected + + def __repr__(self) -> str: + return "IncompleteRead(%i bytes read, %i more expected)" % ( + self.partial, + self.expected, + ) + + +class InvalidChunkLength(HTTPError, httplib_IncompleteRead): + """Invalid chunk length in a chunked response.""" + + def __init__(self, response: HTTPResponse, length: bytes) -> None: + self.partial: int = response.tell() # type: ignore[assignment] + self.expected: int | None = response.length_remaining + self.response = response + self.length = length + + def __repr__(self) -> str: + return "InvalidChunkLength(got length %r, %i bytes read)" % ( + self.length, + self.partial, + ) + + +class InvalidHeader(HTTPError): + """The header provided was somehow invalid.""" + + +class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): + """ProxyManager does not support the supplied scheme""" + + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme: str | None) -> None: + # 'localhost' is here because our URL parser parses + # localhost:8080 -> scheme=localhost, remove if we fix this. + if scheme == "localhost": + scheme = None + if scheme is None: + message = "Proxy URL had no scheme, should start with http:// or https://" + else: + message = f"Proxy URL had unsupported scheme {scheme}, should use http:// or https://" + super().__init__(message) + + +class ProxySchemeUnsupported(ValueError): + """Fetching HTTPS resources through HTTPS proxies is unsupported""" + + +class HeaderParsingError(HTTPError): + """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" + + def __init__( + self, defects: list[MessageDefect], unparsed_data: bytes | str | None + ) -> None: + message = f"{defects or 'Unknown'}, unparsed data: {unparsed_data!r}" + super().__init__(message) + + +class UnrewindableBodyError(HTTPError): + """urllib3 encountered an error when trying to rewind a body""" diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/fields.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/fields.py new file mode 100644 index 0000000..3e258a5 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/fields.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import email.utils +import mimetypes +import typing + +_TYPE_FIELD_VALUE = typing.Union[str, bytes] +_TYPE_FIELD_VALUE_TUPLE = typing.Union[ + _TYPE_FIELD_VALUE, + typing.Tuple[str, _TYPE_FIELD_VALUE], + typing.Tuple[str, _TYPE_FIELD_VALUE, str], +] + + +def guess_content_type( + filename: str | None, default: str = "application/octet-stream" +) -> str: + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param_rfc2231(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Helper function to format and quote a single header parameter using the + strategy defined in RFC 2231. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows + `RFC 2388 Section 4.4 `_. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as ``bytes`` or `str``. + :returns: + An RFC-2231-formatted unicode string. + + .. deprecated:: 2.0.0 + Will be removed in urllib3 v2.1.0. This is not valid for + ``multipart/form-data`` header parameters. + """ + import warnings + + warnings.warn( + "'format_header_param_rfc2231' is deprecated and will be " + "removed in urllib3 v2.1.0. This is not valid for " + "multipart/form-data header parameters.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(value, bytes): + value = value.decode("utf-8") + + if not any(ch in value for ch in '"\\\r\n'): + result = f'{name}="{value}"' + try: + result.encode("ascii") + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + + value = email.utils.encode_rfc2231(value, "utf-8") + value = f"{name}*={value}" + + return value + + +def format_multipart_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Format and quote a single multipart header parameter. + + This follows the `WHATWG HTML Standard`_ as of 2021/06/10, matching + the behavior of current browser and curl versions. Values are + assumed to be UTF-8. The ``\\n``, ``\\r``, and ``"`` characters are + percent encoded. + + .. _WHATWG HTML Standard: + https://html.spec.whatwg.org/multipage/ + form-control-infrastructure.html#multipart-form-data + + :param name: + The name of the parameter, an ASCII-only ``str``. + :param value: + The value of the parameter, a ``str`` or UTF-8 encoded + ``bytes``. + :returns: + A string ``name="value"`` with the escaped value. + + .. versionchanged:: 2.0.0 + Matches the WHATWG HTML Standard as of 2021/06/10. Control + characters are no longer percent encoded. + + .. versionchanged:: 2.0.0 + Renamed from ``format_header_param_html5`` and + ``format_header_param``. The old names will be removed in + urllib3 v2.1.0. + """ + if isinstance(value, bytes): + value = value.decode("utf-8") + + # percent encode \n \r " + value = value.translate({10: "%0A", 13: "%0D", 34: "%22"}) + return f'{name}="{value}"' + + +def format_header_param_html5(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + .. deprecated:: 2.0.0 + Renamed to :func:`format_multipart_header_param`. Will be + removed in urllib3 v2.1.0. + """ + import warnings + + warnings.warn( + "'format_header_param_html5' has been renamed to " + "'format_multipart_header_param'. The old name will be " + "removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + return format_multipart_header_param(name, value) + + +def format_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + .. deprecated:: 2.0.0 + Renamed to :func:`format_multipart_header_param`. Will be + removed in urllib3 v2.1.0. + """ + import warnings + + warnings.warn( + "'format_header_param' has been renamed to " + "'format_multipart_header_param'. The old name will be " + "removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + return format_multipart_header_param(name, value) + + +class RequestField: + """ + A data container for request body parameters. + + :param name: + The name of this request field. Must be unicode. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. Must be unicode. + :param headers: + An optional dict-like object of headers to initially use for the field. + + .. versionchanged:: 2.0.0 + The ``header_formatter`` parameter is deprecated and will + be removed in urllib3 v2.1.0. + """ + + def __init__( + self, + name: str, + data: _TYPE_FIELD_VALUE, + filename: str | None = None, + headers: typing.Mapping[str, str] | None = None, + header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, + ): + self._name = name + self._filename = filename + self.data = data + self.headers: dict[str, str | None] = {} + if headers: + self.headers = dict(headers) + + if header_formatter is not None: + import warnings + + warnings.warn( + "The 'header_formatter' parameter is deprecated and " + "will be removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + self.header_formatter = header_formatter + else: + self.header_formatter = format_multipart_header_param + + @classmethod + def from_tuples( + cls, + fieldname: str, + value: _TYPE_FIELD_VALUE_TUPLE, + header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, + ) -> RequestField: + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + filename: str | None + content_type: str | None + data: _TYPE_FIELD_VALUE + + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls( + fieldname, data, filename=filename, header_formatter=header_formatter + ) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Override this method to change how each multipart header + parameter is formatted. By default, this calls + :func:`format_multipart_header_param`. + + :param name: + The name of the parameter, an ASCII-only ``str``. + :param value: + The value of the parameter, a ``str`` or UTF-8 encoded + ``bytes``. + + :meta public: + """ + return self.header_formatter(name, value) + + def _render_parts( + self, + header_parts: ( + dict[str, _TYPE_FIELD_VALUE | None] + | typing.Sequence[tuple[str, _TYPE_FIELD_VALUE | None]] + ), + ) -> str: + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + iterable: typing.Iterable[tuple[str, _TYPE_FIELD_VALUE | None]] + + parts = [] + if isinstance(header_parts, dict): + iterable = header_parts.items() + else: + iterable = header_parts + + for name, value in iterable: + if value is not None: + parts.append(self._render_part(name, value)) + + return "; ".join(parts) + + def render_headers(self) -> str: + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append(f"{sort_key}: {self.headers[sort_key]}") + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append(f"{header_name}: {header_value}") + + lines.append("\r\n") + return "\r\n".join(lines) + + def make_multipart( + self, + content_disposition: str | None = None, + content_type: str | None = None, + content_location: str | None = None, + ) -> None: + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_disposition: + The 'Content-Disposition' of the request body. Defaults to 'form-data' + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + content_disposition = (content_disposition or "form-data") + "; ".join( + [ + "", + self._render_parts( + (("name", self._name), ("filename", self._filename)) + ), + ] + ) + + self.headers["Content-Disposition"] = content_disposition + self.headers["Content-Type"] = content_type + self.headers["Content-Location"] = content_location diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/filepost.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/filepost.py new file mode 100644 index 0000000..1c90a21 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/filepost.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import binascii +import codecs +import os +import typing +from io import BytesIO + +from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField + +writer = codecs.lookup("utf-8")[3] + +_TYPE_FIELDS_SEQUENCE = typing.Sequence[ + typing.Union[typing.Tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField] +] +_TYPE_FIELDS = typing.Union[ + _TYPE_FIELDS_SEQUENCE, + typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE], +] + + +def choose_boundary() -> str: + """ + Our embarrassingly-simple replacement for mimetools.choose_boundary. + """ + return binascii.hexlify(os.urandom(16)).decode() + + +def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]: + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]] + + if isinstance(fields, typing.Mapping): + iterable = fields.items() + else: + iterable = fields + + for field in iterable: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def encode_multipart_formdata( + fields: _TYPE_FIELDS, boundary: str | None = None +) -> tuple[bytes, str]: + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + Values are processed by :func:`urllib3.fields.RequestField.from_tuples`. + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(f"--{boundary}\r\n".encode("latin-1")) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, str): + writer(body).write(data) + else: + body.write(data) + + body.write(b"\r\n") + + body.write(f"--{boundary}--\r\n".encode("latin-1")) + + content_type = f"multipart/form-data; boundary={boundary}" + + return body.getvalue(), content_type diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/http2.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/http2.py new file mode 100644 index 0000000..15fa9d9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/http2.py @@ -0,0 +1,229 @@ +from __future__ import annotations + +import threading +import types +import typing + +import h2.config # type: ignore[import-untyped] +import h2.connection # type: ignore[import-untyped] +import h2.events # type: ignore[import-untyped] + +import urllib3.connection +import urllib3.util.ssl_ +from urllib3.response import BaseHTTPResponse + +from ._collections import HTTPHeaderDict +from .connection import HTTPSConnection +from .connectionpool import HTTPSConnectionPool + +orig_HTTPSConnection = HTTPSConnection + +T = typing.TypeVar("T") + + +class _LockedObject(typing.Generic[T]): + """ + A wrapper class that hides a specific object behind a lock. + + The goal here is to provide a simple way to protect access to an object + that cannot safely be simultaneously accessed from multiple threads. The + intended use of this class is simple: take hold of it with a context + manager, which returns the protected object. + """ + + def __init__(self, obj: T): + self.lock = threading.RLock() + self._obj = obj + + def __enter__(self) -> T: + self.lock.acquire() + return self._obj + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + self.lock.release() + + +class HTTP2Connection(HTTPSConnection): + def __init__( + self, host: str, port: int | None = None, **kwargs: typing.Any + ) -> None: + self._h2_conn = self._new_h2_conn() + self._h2_stream: int | None = None + self._h2_headers: list[tuple[bytes, bytes]] = [] + + if "proxy" in kwargs or "proxy_config" in kwargs: # Defensive: + raise NotImplementedError("Proxies aren't supported with HTTP/2") + + super().__init__(host, port, **kwargs) + + def _new_h2_conn(self) -> _LockedObject[h2.connection.H2Connection]: + config = h2.config.H2Configuration(client_side=True) + return _LockedObject(h2.connection.H2Connection(config=config)) + + def connect(self) -> None: + super().connect() + + with self._h2_conn as h2_conn: + h2_conn.initiate_connection() + self.sock.sendall(h2_conn.data_to_send()) + + def putrequest( + self, + method: str, + url: str, + skip_host: bool = False, + skip_accept_encoding: bool = False, + ) -> None: + with self._h2_conn as h2_conn: + self._request_url = url + self._h2_stream = h2_conn.get_next_available_stream_id() + + if ":" in self.host: + authority = f"[{self.host}]:{self.port or 443}" + else: + authority = f"{self.host}:{self.port or 443}" + + self._h2_headers.extend( + ( + (b":scheme", b"https"), + (b":method", method.encode()), + (b":authority", authority.encode()), + (b":path", url.encode()), + ) + ) + + def putheader(self, header: str, *values: str) -> None: # type: ignore[override] + for value in values: + self._h2_headers.append( + (header.encode("utf-8").lower(), value.encode("utf-8")) + ) + + def endheaders(self) -> None: # type: ignore[override] + with self._h2_conn as h2_conn: + h2_conn.send_headers( + stream_id=self._h2_stream, + headers=self._h2_headers, + end_stream=True, + ) + if data_to_send := h2_conn.data_to_send(): + self.sock.sendall(data_to_send) + + def send(self, data: bytes) -> None: # type: ignore[override] # Defensive: + if not data: + return + raise NotImplementedError("Sending data isn't supported yet") + + def getresponse( # type: ignore[override] + self, + ) -> HTTP2Response: + status = None + data = bytearray() + with self._h2_conn as h2_conn: + end_stream = False + while not end_stream: + # TODO: Arbitrary read value. + if received_data := self.sock.recv(65535): + events = h2_conn.receive_data(received_data) + for event in events: + if isinstance(event, h2.events.ResponseReceived): + headers = HTTPHeaderDict() + for header, value in event.headers: + if header == b":status": + status = int(value.decode()) + else: + headers.add( + header.decode("ascii"), value.decode("ascii") + ) + + elif isinstance(event, h2.events.DataReceived): + data += event.data + h2_conn.acknowledge_received_data( + event.flow_controlled_length, event.stream_id + ) + + elif isinstance(event, h2.events.StreamEnded): + end_stream = True + + if data_to_send := h2_conn.data_to_send(): + self.sock.sendall(data_to_send) + + # We always close to not have to handle connection management. + self.close() + + assert status is not None + return HTTP2Response( + status=status, + headers=headers, + request_url=self._request_url, + data=bytes(data), + ) + + def close(self) -> None: + with self._h2_conn as h2_conn: + try: + h2_conn.close_connection() + if data := h2_conn.data_to_send(): + self.sock.sendall(data) + except Exception: + pass + + # Reset all our HTTP/2 connection state. + self._h2_conn = self._new_h2_conn() + self._h2_stream = None + self._h2_headers = [] + + super().close() + + +class HTTP2Response(BaseHTTPResponse): + # TODO: This is a woefully incomplete response object, but works for non-streaming. + def __init__( + self, + status: int, + headers: HTTPHeaderDict, + request_url: str, + data: bytes, + decode_content: bool = False, # TODO: support decoding + ) -> None: + super().__init__( + status=status, + headers=headers, + # Following CPython, we map HTTP versions to major * 10 + minor integers + version=20, + # No reason phrase in HTTP/2 + reason=None, + decode_content=decode_content, + request_url=request_url, + ) + self._data = data + self.length_remaining = 0 + + @property + def data(self) -> bytes: + return self._data + + def get_redirect_location(self) -> None: + return None + + def close(self) -> None: + pass + + +def inject_into_urllib3() -> None: + HTTPSConnectionPool.ConnectionCls = HTTP2Connection + urllib3.connection.HTTPSConnection = HTTP2Connection # type: ignore[misc] + + # TODO: Offer 'http/1.1' as well, but for testing purposes this is handy. + urllib3.util.ssl_.ALPN_PROTOCOLS = ["h2"] + + +def extract_from_urllib3() -> None: + HTTPSConnectionPool.ConnectionCls = orig_HTTPSConnection + urllib3.connection.HTTPSConnection = orig_HTTPSConnection # type: ignore[misc] + + urllib3.util.ssl_.ALPN_PROTOCOLS = ["http/1.1"] diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/poolmanager.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/poolmanager.py new file mode 100644 index 0000000..32da0a0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/poolmanager.py @@ -0,0 +1,638 @@ +from __future__ import annotations + +import functools +import logging +import typing +import warnings +from types import TracebackType +from urllib.parse import urljoin + +from ._collections import HTTPHeaderDict, RecentlyUsedContainer +from ._request_methods import RequestMethods +from .connection import ProxyConfig +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + URLSchemeUnknown, +) +from .response import BaseHTTPResponse +from .util.connection import _TYPE_SOCKET_OPTIONS +from .util.proxy import connection_requires_http_tunnel +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import Url, parse_url + +if typing.TYPE_CHECKING: + import ssl + from typing import Literal + +__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ( + "key_file", + "cert_file", + "cert_reqs", + "ca_certs", + "ca_cert_data", + "ssl_version", + "ssl_minimum_version", + "ssl_maximum_version", + "ca_cert_dir", + "ssl_context", + "key_password", + "server_hostname", +) +# Default value for `blocksize` - a new parameter introduced to +# http.client.HTTPConnection & http.client.HTTPSConnection in Python 3.7 +_DEFAULT_BLOCKSIZE = 16384 + +_SelfT = typing.TypeVar("_SelfT") + + +class PoolKey(typing.NamedTuple): + """ + All known keyword arguments that could be provided to the pool manager, its + pools, or the underlying connections. + + All custom key schemes should include the fields in this key at a minimum. + """ + + key_scheme: str + key_host: str + key_port: int | None + key_timeout: Timeout | float | int | None + key_retries: Retry | bool | int | None + key_block: bool | None + key_source_address: tuple[str, int] | None + key_key_file: str | None + key_key_password: str | None + key_cert_file: str | None + key_cert_reqs: str | None + key_ca_certs: str | None + key_ca_cert_data: str | bytes | None + key_ssl_version: int | str | None + key_ssl_minimum_version: ssl.TLSVersion | None + key_ssl_maximum_version: ssl.TLSVersion | None + key_ca_cert_dir: str | None + key_ssl_context: ssl.SSLContext | None + key_maxsize: int | None + key_headers: frozenset[tuple[str, str]] | None + key__proxy: Url | None + key__proxy_headers: frozenset[tuple[str, str]] | None + key__proxy_config: ProxyConfig | None + key_socket_options: _TYPE_SOCKET_OPTIONS | None + key__socks_options: frozenset[tuple[str, str]] | None + key_assert_hostname: bool | str | None + key_assert_fingerprint: str | None + key_server_hostname: str | None + key_blocksize: int | None + + +def _default_key_normalizer( + key_class: type[PoolKey], request_context: dict[str, typing.Any] +) -> PoolKey: + """ + Create a pool key out of a request context dictionary. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + :type key_class: namedtuple + :param request_context: + A dictionary-like object that contain the context for a request. + :type request_context: dict + + :return: A namedtuple that can be used as a connection pool key. + :rtype: PoolKey + """ + # Since we mutate the dictionary, make a copy first + context = request_context.copy() + context["scheme"] = context["scheme"].lower() + context["host"] = context["host"].lower() + + # These are both dictionaries and need to be transformed into frozensets + for key in ("headers", "_proxy_headers", "_socks_options"): + if key in context and context[key] is not None: + context[key] = frozenset(context[key].items()) + + # The socket_options key may be a list and needs to be transformed into a + # tuple. + socket_opts = context.get("socket_options") + if socket_opts is not None: + context["socket_options"] = tuple(socket_opts) + + # Map the kwargs to the names in the namedtuple - this is necessary since + # namedtuples can't have fields starting with '_'. + for key in list(context.keys()): + context["key_" + key] = context.pop(key) + + # Default to ``None`` for keys missing from the context + for field in key_class._fields: + if field not in context: + context[field] = None + + # Default key_blocksize to _DEFAULT_BLOCKSIZE if missing from the context + if context.get("key_blocksize") is None: + context["key_blocksize"] = _DEFAULT_BLOCKSIZE + + return key_class(**context) + + +#: A dictionary that maps a scheme to a callable that creates a pool key. +#: This can be used to alter the way pool keys are constructed, if desired. +#: Each PoolManager makes a copy of this dictionary so they can be configured +#: globally here, or individually on the instance. +key_fn_by_scheme = { + "http": functools.partial(_default_key_normalizer, PoolKey), + "https": functools.partial(_default_key_normalizer, PoolKey), +} + +pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \\**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example: + + .. code-block:: python + + import urllib3 + + http = urllib3.PoolManager(num_pools=2) + + resp1 = http.request("GET", "https://google.com/") + resp2 = http.request("GET", "https://google.com/mail") + resp3 = http.request("GET", "https://yahoo.com/") + + print(len(http.pools)) + # 2 + + """ + + proxy: Url | None = None + proxy_config: ProxyConfig | None = None + + def __init__( + self, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + **connection_pool_kw: typing.Any, + ) -> None: + super().__init__(headers) + self.connection_pool_kw = connection_pool_kw + + self.pools: RecentlyUsedContainer[PoolKey, HTTPConnectionPool] + self.pools = RecentlyUsedContainer(num_pools) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self: _SelfT) -> _SelfT: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> Literal[False]: + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool( + self, + scheme: str, + host: str, + port: int, + request_context: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + """ + Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and + any additional pool keyword arguments. + + If ``request_context`` is provided, it is provided as keyword arguments + to the pool class used. This method is used to actually create the + connection pools handed out by :meth:`connection_from_url` and + companion methods. It is intended to be overridden for customization. + """ + pool_cls: type[HTTPConnectionPool] = self.pool_classes_by_scheme[scheme] + if request_context is None: + request_context = self.connection_pool_kw.copy() + + # Default blocksize to _DEFAULT_BLOCKSIZE if missing or explicitly + # set to 'None' in the request_context. + if request_context.get("blocksize") is None: + request_context["blocksize"] = _DEFAULT_BLOCKSIZE + + # Although the context has everything necessary to create the pool, + # this function has historically only used the scheme, host, and port + # in the positional args. When an API change is acceptable these can + # be removed. + for key in ("scheme", "host", "port"): + request_context.pop(key, None) + + if scheme == "http": + for kw in SSL_KEYWORDS: + request_context.pop(kw, None) + + return pool_cls(host, port, **request_context) + + def clear(self) -> None: + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host( + self, + host: str | None, + port: int | None = None, + scheme: str | None = "http", + pool_kwargs: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is + provided, it is merged with the instance's ``connection_pool_kw`` + variable and used to create the new connection pool, if one is + needed. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self._merge_pool_kwargs(pool_kwargs) + request_context["scheme"] = scheme or "http" + if not port: + port = port_by_scheme.get(request_context["scheme"].lower(), 80) + request_context["port"] = port + request_context["host"] = host + + return self.connection_from_context(request_context) + + def connection_from_context( + self, request_context: dict[str, typing.Any] + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + if "strict" in request_context: + warnings.warn( + "The 'strict' parameter is no longer needed on Python 3+. " + "This will raise an error in urllib3 v2.1.0.", + DeprecationWarning, + ) + request_context.pop("strict") + + scheme = request_context["scheme"].lower() + pool_key_constructor = self.key_fn_by_scheme.get(scheme) + if not pool_key_constructor: + raise URLSchemeUnknown(scheme) + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key, request_context=request_context) + + def connection_from_pool_key( + self, pool_key: PoolKey, request_context: dict[str, typing.Any] + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + scheme = request_context["scheme"] + host = request_context["host"] + port = request_context["port"] + pool = self._new_pool(scheme, host, port, request_context=request_context) + self.pools[pool_key] = pool + + return pool + + def connection_from_url( + self, url: str, pool_kwargs: dict[str, typing.Any] | None = None + ) -> HTTPConnectionPool: + """ + Similar to :func:`urllib3.connectionpool.connection_from_url`. + + If ``pool_kwargs`` is not provided and a new pool needs to be + constructed, ``self.connection_pool_kw`` is used to initialize + the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` + is provided, it is used instead. Note that if a new pool does not + need to be created for the request, the provided ``pool_kwargs`` are + not used. + """ + u = parse_url(url) + return self.connection_from_host( + u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs + ) + + def _merge_pool_kwargs( + self, override: dict[str, typing.Any] | None + ) -> dict[str, typing.Any]: + """ + Merge a dictionary of override values for self.connection_pool_kw. + + This does not modify self.connection_pool_kw and returns a new dict. + Any keys in the override dictionary with a value of ``None`` are + removed from the merged dictionary. + """ + base_pool_kwargs = self.connection_pool_kw.copy() + if override: + for key, value in override.items(): + if value is None: + try: + del base_pool_kwargs[key] + except KeyError: + pass + else: + base_pool_kwargs[key] = value + return base_pool_kwargs + + def _proxy_requires_url_absolute_form(self, parsed_url: Url) -> bool: + """ + Indicates if the proxy requires the complete destination URL in the + request. Normally this is only needed when not using an HTTP CONNECT + tunnel. + """ + if self.proxy is None: + return False + + return not connection_requires_http_tunnel( + self.proxy, self.proxy_config, parsed_url.scheme + ) + + def urlopen( # type: ignore[override] + self, method: str, url: str, redirect: bool = True, **kw: typing.Any + ) -> BaseHTTPResponse: + """ + Same as :meth:`urllib3.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + + if u.scheme is None: + warnings.warn( + "URLs without a scheme (ie 'https://') are deprecated and will raise an error " + "in a future version of urllib3. To avoid this DeprecationWarning ensure all URLs " + "start with 'https://' or 'http://'. Read more in this issue: " + "https://github.com/urllib3/urllib3/issues/2920", + category=DeprecationWarning, + stacklevel=2, + ) + + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw["assert_same_host"] = False + kw["redirect"] = False + + if "headers" not in kw: + kw["headers"] = self.headers + + if self._proxy_requires_url_absolute_form(u): + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + if response.status == 303: + # Change the method according to RFC 9110, Section 15.4.4. + method = "GET" + # And lose the body not to transfer anything sensitive. + kw["body"] = None + kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() + + retries = kw.get("retries") + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if retries.remove_headers_on_redirect and not conn.is_same_host( + redirect_location + ): + new_headers = kw["headers"].copy() + for header in kw["headers"]: + if header.lower() in retries.remove_headers_on_redirect: + new_headers.pop(header, None) + kw["headers"] = new_headers + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + kw["retries"] = retries + kw["redirect"] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary containing headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + :param proxy_ssl_context: + The proxy SSL context is used to establish the TLS connection to the + proxy when using HTTPS proxies. + + :param use_forwarding_for_https: + (Defaults to False) If set to True will forward requests to the HTTPS + proxy to be made on behalf of the client instead of creating a TLS + tunnel via the CONNECT method. **Enabling this flag means that request + and response headers and content will be visible from the HTTPS proxy** + whereas tunneling keeps request and response headers and content + private. IP address, target hostname, SNI, and port are always visible + to an HTTPS proxy even when this flag is disabled. + + :param proxy_assert_hostname: + The hostname of the certificate to verify against. + + :param proxy_assert_fingerprint: + The fingerprint of the certificate to verify against. + + Example: + + .. code-block:: python + + import urllib3 + + proxy = urllib3.ProxyManager("https://localhost:3128/") + + resp1 = proxy.request("GET", "https://google.com/") + resp2 = proxy.request("GET", "https://httpbin.org/") + + print(len(proxy.pools)) + # 1 + + resp3 = proxy.request("GET", "https://httpbin.org/") + resp4 = proxy.request("GET", "https://twitter.com/") + + print(len(proxy.pools)) + # 3 + + """ + + def __init__( + self, + proxy_url: str, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + proxy_headers: typing.Mapping[str, str] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + use_forwarding_for_https: bool = False, + proxy_assert_hostname: None | str | Literal[False] = None, + proxy_assert_fingerprint: str | None = None, + **connection_pool_kw: typing.Any, + ) -> None: + if isinstance(proxy_url, HTTPConnectionPool): + str_proxy_url = f"{proxy_url.scheme}://{proxy_url.host}:{proxy_url.port}" + else: + str_proxy_url = proxy_url + proxy = parse_url(str_proxy_url) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + self.proxy_ssl_context = proxy_ssl_context + self.proxy_config = ProxyConfig( + proxy_ssl_context, + use_forwarding_for_https, + proxy_assert_hostname, + proxy_assert_fingerprint, + ) + + connection_pool_kw["_proxy"] = self.proxy + connection_pool_kw["_proxy_headers"] = self.proxy_headers + connection_pool_kw["_proxy_config"] = self.proxy_config + + super().__init__(num_pools, headers, **connection_pool_kw) + + def connection_from_host( + self, + host: str | None, + port: int | None = None, + scheme: str | None = "http", + pool_kwargs: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + if scheme == "https": + return super().connection_from_host( + host, port, scheme, pool_kwargs=pool_kwargs + ) + + return super().connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs # type: ignore[union-attr] + ) + + def _set_proxy_headers( + self, url: str, headers: typing.Mapping[str, str] | None = None + ) -> typing.Mapping[str, str]: + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {"Accept": "*/*"} + + netloc = parse_url(url).netloc + if netloc: + headers_["Host"] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def urlopen( # type: ignore[override] + self, method: str, url: str, redirect: bool = True, **kw: typing.Any + ) -> BaseHTTPResponse: + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): + # For connections using HTTP CONNECT, httplib sets the necessary + # headers on the CONNECT to the proxy. If we're not using CONNECT, + # we'll definitely need to set 'Host' at the very least. + headers = kw.get("headers", self.headers) + kw["headers"] = self._set_proxy_headers(url, headers) + + return super().urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url: str, **kw: typing.Any) -> ProxyManager: + return ProxyManager(proxy_url=url, **kw) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/py.typed b/.pythonlibs/lib/python3.10/site-packages/urllib3/py.typed new file mode 100644 index 0000000..5f3ea3d --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/py.typed @@ -0,0 +1,2 @@ +# Instruct type checkers to look for inline type annotations in this package. +# See PEP 561. diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/response.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/response.py new file mode 100644 index 0000000..d31fac9 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/response.py @@ -0,0 +1,1243 @@ +from __future__ import annotations + +import collections +import io +import json as _json +import logging +import re +import sys +import typing +import warnings +import zlib +from contextlib import contextmanager +from http.client import HTTPMessage as _HttplibHTTPMessage +from http.client import HTTPResponse as _HttplibHTTPResponse +from socket import timeout as SocketTimeout + +if typing.TYPE_CHECKING: + from ._base_connection import BaseHTTPConnection + +try: + try: + import brotlicffi as brotli # type: ignore[import-not-found] + except ImportError: + import brotli # type: ignore[import-not-found] +except ImportError: + brotli = None + +try: + import zstandard as zstd # type: ignore[import-not-found] + + # The package 'zstandard' added the 'eof' property starting + # in v0.18.0 which we require to ensure a complete and + # valid zstd stream was fed into the ZstdDecoder. + # See: https://github.com/urllib3/urllib3/pull/2624 + _zstd_version = _zstd_version = tuple( + map(int, re.search(r"^([0-9]+)\.([0-9]+)", zstd.__version__).groups()) # type: ignore[union-attr] + ) + if _zstd_version < (0, 18): # Defensive: + zstd = None + +except (AttributeError, ImportError, ValueError): # Defensive: + zstd = None + +from . import util +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from .connection import BaseSSLError, HTTPConnection, HTTPException +from .exceptions import ( + BodyNotHttplibCompatible, + DecodeError, + HTTPError, + IncompleteRead, + InvalidChunkLength, + InvalidHeader, + ProtocolError, + ReadTimeoutError, + ResponseNotChunked, + SSLError, +) +from .util.response import is_fp_closed, is_response_to_head +from .util.retry import Retry + +if typing.TYPE_CHECKING: + from typing import Literal + + from .connectionpool import HTTPConnectionPool + +log = logging.getLogger(__name__) + + +class ContentDecoder: + def decompress(self, data: bytes) -> bytes: + raise NotImplementedError() + + def flush(self) -> bytes: + raise NotImplementedError() + + +class DeflateDecoder(ContentDecoder): + def __init__(self) -> None: + self._first_try = True + self._data = b"" + self._obj = zlib.decompressobj() + + def decompress(self, data: bytes) -> bytes: + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + decompressed = self._obj.decompress(data) + if decompressed: + self._first_try = False + self._data = None # type: ignore[assignment] + return decompressed + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None # type: ignore[assignment] + + def flush(self) -> bytes: + return self._obj.flush() + + +class GzipDecoderState: + FIRST_MEMBER = 0 + OTHER_MEMBERS = 1 + SWALLOW_DATA = 2 + + +class GzipDecoder(ContentDecoder): + def __init__(self) -> None: + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._state = GzipDecoderState.FIRST_MEMBER + + def decompress(self, data: bytes) -> bytes: + ret = bytearray() + if self._state == GzipDecoderState.SWALLOW_DATA or not data: + return bytes(ret) + while True: + try: + ret += self._obj.decompress(data) + except zlib.error: + previous_state = self._state + # Ignore data after the first error + self._state = GzipDecoderState.SWALLOW_DATA + if previous_state == GzipDecoderState.OTHER_MEMBERS: + # Allow trailing garbage acceptable in other gzip clients + return bytes(ret) + raise + data = self._obj.unused_data + if not data: + return bytes(ret) + self._state = GzipDecoderState.OTHER_MEMBERS + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def flush(self) -> bytes: + return self._obj.flush() + + +if brotli is not None: + + class BrotliDecoder(ContentDecoder): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__(self) -> None: + self._obj = brotli.Decompressor() + if hasattr(self._obj, "decompress"): + setattr(self, "decompress", self._obj.decompress) + else: + setattr(self, "decompress", self._obj.process) + + def flush(self) -> bytes: + if hasattr(self._obj, "flush"): + return self._obj.flush() # type: ignore[no-any-return] + return b"" + + +if zstd is not None: + + class ZstdDecoder(ContentDecoder): + def __init__(self) -> None: + self._obj = zstd.ZstdDecompressor().decompressobj() + + def decompress(self, data: bytes) -> bytes: + if not data: + return b"" + data_parts = [self._obj.decompress(data)] + while self._obj.eof and self._obj.unused_data: + unused_data = self._obj.unused_data + self._obj = zstd.ZstdDecompressor().decompressobj() + data_parts.append(self._obj.decompress(unused_data)) + return b"".join(data_parts) + + def flush(self) -> bytes: + ret = self._obj.flush() # note: this is a no-op + if not self._obj.eof: + raise DecodeError("Zstandard data is incomplete") + return ret # type: ignore[no-any-return] + + +class MultiDecoder(ContentDecoder): + """ + From RFC7231: + If one or more encodings have been applied to a representation, the + sender that applied the encodings MUST generate a Content-Encoding + header field that lists the content codings in the order in which + they were applied. + """ + + def __init__(self, modes: str) -> None: + self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] + + def flush(self) -> bytes: + return self._decoders[0].flush() + + def decompress(self, data: bytes) -> bytes: + for d in reversed(self._decoders): + data = d.decompress(data) + return data + + +def _get_decoder(mode: str) -> ContentDecoder: + if "," in mode: + return MultiDecoder(mode) + + # According to RFC 9110 section 8.4.1.3, recipients should + # consider x-gzip equivalent to gzip + if mode in ("gzip", "x-gzip"): + return GzipDecoder() + + if brotli is not None and mode == "br": + return BrotliDecoder() + + if zstd is not None and mode == "zstd": + return ZstdDecoder() + + return DeflateDecoder() + + +class BytesQueueBuffer: + """Memory-efficient bytes buffer + + To return decoded data in read() and still follow the BufferedIOBase API, we need a + buffer to always return the correct amount of bytes. + + This buffer should be filled using calls to put() + + Our maximum memory usage is determined by the sum of the size of: + + * self.buffer, which contains the full data + * the largest chunk that we will copy in get() + + The worst case scenario is a single chunk, in which case we'll make a full copy of + the data inside get(). + """ + + def __init__(self) -> None: + self.buffer: typing.Deque[bytes] = collections.deque() + self._size: int = 0 + + def __len__(self) -> int: + return self._size + + def put(self, data: bytes) -> None: + self.buffer.append(data) + self._size += len(data) + + def get(self, n: int) -> bytes: + if n == 0: + return b"" + elif not self.buffer: + raise RuntimeError("buffer is empty") + elif n < 0: + raise ValueError("n should be > 0") + + fetched = 0 + ret = io.BytesIO() + while fetched < n: + remaining = n - fetched + chunk = self.buffer.popleft() + chunk_length = len(chunk) + if remaining < chunk_length: + left_chunk, right_chunk = chunk[:remaining], chunk[remaining:] + ret.write(left_chunk) + self.buffer.appendleft(right_chunk) + self._size -= remaining + break + else: + ret.write(chunk) + self._size -= chunk_length + fetched += chunk_length + + if not self.buffer: + break + + return ret.getvalue() + + def get_all(self) -> bytes: + buffer = self.buffer + if not buffer: + assert self._size == 0 + return b"" + if len(buffer) == 1: + result = buffer.pop() + else: + ret = io.BytesIO() + ret.writelines(buffer.popleft() for _ in range(len(buffer))) + result = ret.getvalue() + self._size = 0 + return result + + +class BaseHTTPResponse(io.IOBase): + CONTENT_DECODERS = ["gzip", "x-gzip", "deflate"] + if brotli is not None: + CONTENT_DECODERS += ["br"] + if zstd is not None: + CONTENT_DECODERS += ["zstd"] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + DECODER_ERROR_CLASSES: tuple[type[Exception], ...] = (IOError, zlib.error) + if brotli is not None: + DECODER_ERROR_CLASSES += (brotli.error,) + + if zstd is not None: + DECODER_ERROR_CLASSES += (zstd.ZstdError,) + + def __init__( + self, + *, + headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, + status: int, + version: int, + reason: str | None, + decode_content: bool, + request_url: str | None, + retries: Retry | None = None, + ) -> None: + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) # type: ignore[arg-type] + self.status = status + self.version = version + self.reason = reason + self.decode_content = decode_content + self._has_decoded_content = False + self._request_url: str | None = request_url + self.retries = retries + + self.chunked = False + tr_enc = self.headers.get("transfer-encoding", "").lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + self._decoder: ContentDecoder | None = None + self.length_remaining: int | None + + def get_redirect_location(self) -> str | None | Literal[False]: + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get("location") + return False + + @property + def data(self) -> bytes: + raise NotImplementedError() + + def json(self) -> typing.Any: + """ + Parses the body of the HTTP response as JSON. + + To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to the decoder. + + This method can raise either `UnicodeDecodeError` or `json.JSONDecodeError`. + + Read more :ref:`here `. + """ + data = self.data.decode("utf-8") + return _json.loads(data) + + @property + def url(self) -> str | None: + raise NotImplementedError() + + @url.setter + def url(self, url: str | None) -> None: + raise NotImplementedError() + + @property + def connection(self) -> BaseHTTPConnection | None: + raise NotImplementedError() + + @property + def retries(self) -> Retry | None: + return self._retries + + @retries.setter + def retries(self, retries: Retry | None) -> None: + # Override the request_url if retries has a redirect location. + if retries is not None and retries.history: + self.url = retries.history[-1].redirect_location + self._retries = retries + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Iterator[bytes]: + raise NotImplementedError() + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, + cache_content: bool = False, + ) -> bytes: + raise NotImplementedError() + + def read1( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> bytes: + raise NotImplementedError() + + def read_chunked( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> typing.Iterator[bytes]: + raise NotImplementedError() + + def release_conn(self) -> None: + raise NotImplementedError() + + def drain_conn(self) -> None: + raise NotImplementedError() + + def close(self) -> None: + raise NotImplementedError() + + def _init_decoder(self) -> None: + """ + Set-up the _decoder attribute if necessary. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get("content-encoding", "").lower() + if self._decoder is None: + if content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + elif "," in content_encoding: + encodings = [ + e.strip() + for e in content_encoding.split(",") + if e.strip() in self.CONTENT_DECODERS + ] + if encodings: + self._decoder = _get_decoder(content_encoding) + + def _decode( + self, data: bytes, decode_content: bool | None, flush_decoder: bool + ) -> bytes: + """ + Decode the data passed in and potentially flush the decoder. + """ + if not decode_content: + if self._has_decoded_content: + raise RuntimeError( + "Calling read(decode_content=False) is not supported after " + "read(decode_content=True) was called." + ) + return data + + try: + if self._decoder: + data = self._decoder.decompress(data) + self._has_decoded_content = True + except self.DECODER_ERROR_CLASSES as e: + content_encoding = self.headers.get("content-encoding", "").lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, + e, + ) from e + if flush_decoder: + data += self._flush_decoder() + + return data + + def _flush_decoder(self) -> bytes: + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + return self._decoder.decompress(b"") + self._decoder.flush() + return b"" + + # Compatibility methods for `io` module + def readinto(self, b: bytearray) -> int: + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[: len(temp)] = temp + return len(temp) + + # Compatibility methods for http.client.HTTPResponse + def getheaders(self) -> HTTPHeaderDict: + warnings.warn( + "HTTPResponse.getheaders() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.", + category=DeprecationWarning, + stacklevel=2, + ) + return self.headers + + def getheader(self, name: str, default: str | None = None) -> str | None: + warnings.warn( + "HTTPResponse.getheader() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).", + category=DeprecationWarning, + stacklevel=2, + ) + return self.headers.get(name, default) + + # Compatibility method for http.cookiejar + def info(self) -> HTTPHeaderDict: + return self.headers + + def geturl(self) -> str | None: + return self.url + + +class HTTPResponse(BaseHTTPResponse): + """ + HTTP Response container. + + Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param original_response: + When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + + :param retries: + The retries contains the last :class:`~urllib3.util.retry.Retry` that + was used during the request. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + + def __init__( + self, + body: _TYPE_BODY = "", + headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, + status: int = 0, + version: int = 0, + reason: str | None = None, + preload_content: bool = True, + decode_content: bool = True, + original_response: _HttplibHTTPResponse | None = None, + pool: HTTPConnectionPool | None = None, + connection: HTTPConnection | None = None, + msg: _HttplibHTTPMessage | None = None, + retries: Retry | None = None, + enforce_content_length: bool = True, + request_method: str | None = None, + request_url: str | None = None, + auto_close: bool = True, + ) -> None: + super().__init__( + headers=headers, + status=status, + version=version, + reason=reason, + decode_content=decode_content, + request_url=request_url, + retries=retries, + ) + + self.enforce_content_length = enforce_content_length + self.auto_close = auto_close + + self._body = None + self._fp: _HttplibHTTPResponse | None = None + self._original_response = original_response + self._fp_bytes_read = 0 + self.msg = msg + + if body and isinstance(body, (str, bytes)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, "read"): + self._fp = body # type: ignore[assignment] + + # Are we using the chunked-style of transfer encoding? + self.chunk_left: int | None = None + + # Determine length of response + self.length_remaining = self._init_length(request_method) + + # Used to return the correct amount of bytes for partial read()s + self._decoded_buffer = BytesQueueBuffer() + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def release_conn(self) -> None: + if not self._pool or not self._connection: + return None + + self._pool._put_conn(self._connection) + self._connection = None + + def drain_conn(self) -> None: + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, OSError, BaseSSLError, HTTPException): + pass + + @property + def data(self) -> bytes: + # For backwards-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body # type: ignore[return-value] + + if self._fp: + return self.read(cache_content=True) + + return None # type: ignore[return-value] + + @property + def connection(self) -> HTTPConnection | None: + return self._connection + + def isclosed(self) -> bool: + return is_fp_closed(self._fp) + + def tell(self) -> int: + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` + if bytes are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_length(self, request_method: str | None) -> int | None: + """ + Set initial length value for Response content if available. + """ + length: int | None + content_length: str | None = self.headers.get("content-length") + + if content_length is not None: + if self.chunked: + # This Response will fail with an IncompleteRead if it can't be + # received as chunked. This method falls back to attempt reading + # the response before raising an exception. + log.warning( + "Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked." + ) + return None + + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = {int(val) for val in content_length.split(",")} + if len(lengths) > 1: + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % content_length + ) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + else: # if content_length is None + length = None + + # Convert status to int for comparison + # In some cases, httplib returns a status of "_UNKNOWN" + try: + status = int(self.status) + except ValueError: + status = 0 + + # Check for responses that shouldn't include a body + if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": + length = 0 + + return length + + @contextmanager + def _error_catcher(self) -> typing.Generator[None, None, None]: + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout as e: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if "read operation timed out" not in str(e): + # SSL errors related to framing/MAC get wrapped and reraised here + raise SSLError(e) from e + + raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] + + except IncompleteRead as e: + if ( + e.expected is not None + and e.partial is not None + and e.expected == -e.partial + ): + arg = "Response may not contain content." + else: + arg = f"Connection broken: {e!r}" + raise ProtocolError(arg, e) from e + + except (HTTPException, OSError) as e: + raise ProtocolError(f"Connection broken: {e!r}", e) from e + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def _fp_read( + self, + amt: int | None = None, + *, + read1: bool = False, + ) -> bytes: + """ + Read a response with the thought that reading the number of bytes + larger than can fit in a 32-bit int at a time via SSL in some + known cases leads to an overflow error that has to be prevented + if `amt` or `self.length_remaining` indicate that a problem may + happen. + + The known cases: + * 3.8 <= CPython < 3.9.7 because of a bug + https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900. + * urllib3 injected with pyOpenSSL-backed SSL-support. + * CPython < 3.10 only when `amt` does not fit 32-bit int. + """ + assert self._fp + c_int_max = 2**31 - 1 + if ( + (amt and amt > c_int_max) + or ( + amt is None + and self.length_remaining + and self.length_remaining > c_int_max + ) + ) and (util.IS_PYOPENSSL or sys.version_info < (3, 10)): + if read1: + return self._fp.read1(c_int_max) + buffer = io.BytesIO() + # Besides `max_chunk_amt` being a maximum chunk size, it + # affects memory overhead of reading a response by this + # method in CPython. + # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum + # chunk size that does not lead to an overflow error, but + # 256 MiB is a compromise. + max_chunk_amt = 2**28 + while amt is None or amt != 0: + if amt is not None: + chunk_amt = min(amt, max_chunk_amt) + amt -= chunk_amt + else: + chunk_amt = max_chunk_amt + data = self._fp.read(chunk_amt) + if not data: + break + buffer.write(data) + del data # to reduce peak memory usage by `max_chunk_amt`. + return buffer.getvalue() + elif read1: + return self._fp.read1(amt) if amt is not None else self._fp.read1() + else: + # StringIO doesn't like amt=None + return self._fp.read(amt) if amt is not None else self._fp.read() + + def _raw_read( + self, + amt: int | None = None, + *, + read1: bool = False, + ) -> bytes: + """ + Reads `amt` of bytes from the socket. + """ + if self._fp is None: + return None # type: ignore[return-value] + + fp_closed = getattr(self._fp, "closed", False) + + with self._error_catcher(): + data = self._fp_read(amt, read1=read1) if not fp_closed else b"" + if amt is not None and amt != 0 and not data: + # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + if ( + self.enforce_content_length + and self.length_remaining is not None + and self.length_remaining != 0 + ): + # This is an edge case that httplib failed to cover due + # to concerns of backward compatibility. We're + # addressing it here to make sure IncompleteRead is + # raised during streaming, so all calls with incorrect + # Content-Length are caught. + raise IncompleteRead(self._fp_bytes_read, self.length_remaining) + elif read1 and ( + (amt != 0 and not data) or self.length_remaining == len(data) + ): + # All data has been read, but `self._fp.read1` in + # CPython 3.12 and older doesn't always close + # `http.client.HTTPResponse`, so we close it here. + # See https://github.com/python/cpython/issues/113199 + self._fp.close() + + if data: + self._fp_bytes_read += len(data) + if self.length_remaining is not None: + self.length_remaining -= len(data) + return data + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, + cache_content: bool = False, + ) -> bytes: + """ + Similar to :meth:`http.client.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if amt is not None: + cache_content = False + + if len(self._decoded_buffer) >= amt: + return self._decoded_buffer.get(amt) + + data = self._raw_read(amt) + + flush_decoder = amt is None or (amt != 0 and not data) + + if not data and len(self._decoded_buffer) == 0: + return data + + if amt is None: + data = self._decode(data, decode_content, flush_decoder) + if cache_content: + self._body = data + else: + # do not waste memory on buffer when not decoding + if not decode_content: + if self._has_decoded_content: + raise RuntimeError( + "Calling read(decode_content=False) is not supported after " + "read(decode_content=True) was called." + ) + return data + + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + + while len(self._decoded_buffer) < amt and data: + # TODO make sure to initially read enough data to get past the headers + # For example, the GZ file header takes 10 bytes, we don't want to read + # it one byte at a time + data = self._raw_read(amt) + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + data = self._decoded_buffer.get(amt) + + return data + + def read1( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> bytes: + """ + Similar to ``http.client.HTTPResponse.read1`` and documented + in :meth:`io.BufferedReader.read1`, but with an additional parameter: + ``decode_content``. + + :param amt: + How much of the content to read. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if decode_content is None: + decode_content = self.decode_content + # try and respond without going to the network + if self._has_decoded_content: + if not decode_content: + raise RuntimeError( + "Calling read1(decode_content=False) is not supported after " + "read1(decode_content=True) was called." + ) + if len(self._decoded_buffer) > 0: + if amt is None: + return self._decoded_buffer.get_all() + return self._decoded_buffer.get(amt) + if amt == 0: + return b"" + + # FIXME, this method's type doesn't say returning None is possible + data = self._raw_read(amt, read1=True) + if not decode_content or data is None: + return data + + self._init_decoder() + while True: + flush_decoder = not data + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + if decoded_data or flush_decoder: + break + data = self._raw_read(8192, read1=True) + + if amt is None: + return self._decoded_buffer.get_all() + return self._decoded_buffer.get(amt) + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Generator[bytes, None, None]: + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked and self.supports_chunked_reads(): + yield from self.read_chunked(amt, decode_content=decode_content) + else: + while not is_fp_closed(self._fp) or len(self._decoded_buffer) > 0: + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + # Overrides from io.IOBase + def readable(self) -> bool: + return True + + def close(self) -> None: + if not self.closed and self._fp: + self._fp.close() + + if self._connection: + self._connection.close() + + if not self.auto_close: + io.IOBase.close(self) + + @property + def closed(self) -> bool: + if not self.auto_close: + return io.IOBase.closed.__get__(self) # type: ignore[no-any-return] + elif self._fp is None: + return True + elif hasattr(self._fp, "isclosed"): + return self._fp.isclosed() + elif hasattr(self._fp, "closed"): + return self._fp.closed + else: + return True + + def fileno(self) -> int: + if self._fp is None: + raise OSError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise OSError( + "The file-like object this HTTPResponse is wrapped " + "around has no file descriptor" + ) + + def flush(self) -> None: + if ( + self._fp is not None + and hasattr(self._fp, "flush") + and not getattr(self._fp, "closed", False) + ): + return self._fp.flush() + + def supports_chunked_reads(self) -> bool: + """ + Checks if the underlying file-like object looks like a + :class:`http.client.HTTPResponse` object. We do this by testing for + the fp attribute. If it is present we assume it returns raw chunks as + processed by read_chunked(). + """ + return hasattr(self._fp, "fp") + + def _update_chunk_length(self) -> None: + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return None + line = self._fp.fp.readline() # type: ignore[union-attr] + line = line.split(b";", 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + self.close() + if line: + # Invalid chunked protocol response, abort. + raise InvalidChunkLength(self, line) from None + else: + # Truncated at start of next chunk + raise ProtocolError("Response ended prematurely") from None + + def _handle_chunk(self, amt: int | None) -> bytes: + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] + returned_chunk = chunk + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif self.chunk_left is not None and amt < self.chunk_left: + value = self._fp._safe_read(amt) # type: ignore[union-attr] + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) # type: ignore[union-attr] + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk # type: ignore[no-any-return] + + def read_chunked( + self, amt: int | None = None, decode_content: bool | None = None + ) -> typing.Generator[bytes, None, None]: + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing." + ) + if not self.supports_chunked_reads(): + raise BodyNotHttplibCompatible( + "Body should be http.client.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks." + ) + + with self._error_catcher(): + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return None + + # If a response is already read and closed + # then return immediately. + if self._fp.fp is None: # type: ignore[union-attr] + return None + + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode( + chunk, decode_content=decode_content, flush_decoder=False + ) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while self._fp is not None: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b"\r\n": + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() + + @property + def url(self) -> str | None: + """ + Returns the URL that was the source of this response. + If the request that generated this response redirected, this method + will return the final redirect location. + """ + return self._request_url + + @url.setter + def url(self, url: str) -> None: + self._request_url = url + + def __iter__(self) -> typing.Iterator[bytes]: + buffer: list[bytes] = [] + for chunk in self.stream(decode_content=True): + if b"\n" in chunk: + chunks = chunk.split(b"\n") + yield b"".join(buffer) + chunks[0] + b"\n" + for x in chunks[1:-1]: + yield x + b"\n" + if chunks[-1]: + buffer = [chunks[-1]] + else: + buffer = [] + else: + buffer.append(chunk) + if buffer: + yield b"".join(buffer) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__init__.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__init__.py new file mode 100644 index 0000000..5341260 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__init__.py @@ -0,0 +1,42 @@ +# For backwards compatibility, provide imports that used to be here. +from __future__ import annotations + +from .connection import is_connection_dropped +from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers +from .response import is_fp_closed +from .retry import Retry +from .ssl_ import ( + ALPN_PROTOCOLS, + IS_PYOPENSSL, + SSLContext, + assert_fingerprint, + create_urllib3_context, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import Timeout +from .url import Url, parse_url +from .wait import wait_for_read, wait_for_write + +__all__ = ( + "IS_PYOPENSSL", + "SSLContext", + "ALPN_PROTOCOLS", + "Retry", + "Timeout", + "Url", + "assert_fingerprint", + "create_urllib3_context", + "is_connection_dropped", + "is_fp_closed", + "parse_url", + "make_headers", + "resolve_cert_reqs", + "resolve_ssl_version", + "ssl_wrap_socket", + "wait_for_read", + "wait_for_write", + "SKIP_HEADER", + "SKIPPABLE_HEADERS", +) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..2d1d3cd Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/connection.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/connection.cpython-310.pyc new file mode 100644 index 0000000..991eeb5 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/connection.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/proxy.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/proxy.cpython-310.pyc new file mode 100644 index 0000000..598c343 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/proxy.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/request.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/request.cpython-310.pyc new file mode 100644 index 0000000..321389f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/request.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/response.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000..83859a6 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/response.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/retry.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/retry.cpython-310.pyc new file mode 100644 index 0000000..8b41368 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/retry.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_.cpython-310.pyc new file mode 100644 index 0000000..767fe34 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-310.pyc new file mode 100644 index 0000000..4806590 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssltransport.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssltransport.cpython-310.pyc new file mode 100644 index 0000000..0f7e24f Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/ssltransport.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/timeout.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/timeout.cpython-310.pyc new file mode 100644 index 0000000..849b846 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/timeout.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/url.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/url.cpython-310.pyc new file mode 100644 index 0000000..92732fa Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/url.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/util.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/util.cpython-310.pyc new file mode 100644 index 0000000..5de12c4 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/util.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/wait.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/wait.cpython-310.pyc new file mode 100644 index 0000000..cdfaae2 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/__pycache__/wait.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/connection.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/connection.py new file mode 100644 index 0000000..5c7da73 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/connection.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import socket +import typing + +from ..exceptions import LocationParseError +from .timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT + +_TYPE_SOCKET_OPTIONS = typing.Sequence[typing.Tuple[int, int, typing.Union[int, bytes]]] + +if typing.TYPE_CHECKING: + from .._base_connection import BaseHTTPConnection + + +def is_connection_dropped(conn: BaseHTTPConnection) -> bool: # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + :param conn: :class:`urllib3.connection.HTTPConnection` object. + """ + return not conn.is_connected + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, +) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + # Break explicitly a reference cycle + err = None + return sock + + except OSError as _: + err = _ + if sock is not None: + sock.close() + + if err is not None: + try: + raise err + finally: + # Break explicitly a reference cycle + err = None + else: + raise OSError("getaddrinfo returns an empty list") + + +def _set_socket_options( + sock: socket.socket, options: _TYPE_SOCKET_OPTIONS | None +) -> None: + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family() -> socket.AddressFamily: + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host: str) -> bool: + """Returns True if the system can bind an IPv6 address.""" + sock = None + has_ipv6 = False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/urllib3/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + + +HAS_IPV6 = _has_ipv6("::1") diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/proxy.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/proxy.py new file mode 100644 index 0000000..908fc66 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/proxy.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import typing + +from .url import Url + +if typing.TYPE_CHECKING: + from ..connection import ProxyConfig + + +def connection_requires_http_tunnel( + proxy_url: Url | None = None, + proxy_config: ProxyConfig | None = None, + destination_scheme: str | None = None, +) -> bool: + """ + Returns True if the connection requires an HTTP CONNECT through the proxy. + + :param URL proxy_url: + URL of the proxy. + :param ProxyConfig proxy_config: + Proxy configuration from poolmanager.py + :param str destination_scheme: + The scheme of the destination. (i.e https, http, etc) + """ + # If we're not using a proxy, no way to use a tunnel. + if proxy_url is None: + return False + + # HTTP destinations never require tunneling, we always forward. + if destination_scheme == "http": + return False + + # Support for forwarding with HTTPS proxies and HTTPS destinations. + if ( + proxy_url.scheme == "https" + and proxy_config + and proxy_config.use_forwarding_for_https + ): + return False + + # Otherwise always use a tunnel. + return True diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/request.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/request.py new file mode 100644 index 0000000..fe0e348 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/request.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +import io +import typing +from base64 import b64encode +from enum import Enum + +from ..exceptions import UnrewindableBodyError +from .util import to_bytes + +if typing.TYPE_CHECKING: + from typing import Final + +# Pass as a value within ``headers`` to skip +# emitting some HTTP headers that are added automatically. +# The only headers that are supported are ``Accept-Encoding``, +# ``Host``, and ``User-Agent``. +SKIP_HEADER = "@@@SKIP_HEADER@@@" +SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) + +ACCEPT_ENCODING = "gzip,deflate" +try: + try: + import brotlicffi as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 + except ImportError: + import brotli as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 +except ImportError: + pass +else: + ACCEPT_ENCODING += ",br" +try: + import zstandard as _unused_module_zstd # type: ignore[import-not-found] # noqa: F401 +except ImportError: + pass +else: + ACCEPT_ENCODING += ",zstd" + + +class _TYPE_FAILEDTELL(Enum): + token = 0 + + +_FAILEDTELL: Final[_TYPE_FAILEDTELL] = _TYPE_FAILEDTELL.token + +_TYPE_BODY_POSITION = typing.Union[int, _TYPE_FAILEDTELL] + +# When sending a request with these methods we aren't expecting +# a body so don't need to set an explicit 'Content-Length: 0' +# The reason we do this in the negative instead of tracking methods +# which 'should' have a body is because unknown methods should be +# treated as if they were 'POST' which *does* expect a body. +_METHODS_NOT_EXPECTING_BODY = {"GET", "HEAD", "DELETE", "TRACE", "OPTIONS", "CONNECT"} + + +def make_headers( + keep_alive: bool | None = None, + accept_encoding: bool | list[str] | str | None = None, + user_agent: str | None = None, + basic_auth: str | None = None, + proxy_basic_auth: str | None = None, + disable_cache: bool | None = None, +) -> dict[str, str]: + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. If either the ``brotli`` or + ``brotlicffi`` package is installed 'gzip,deflate,br' is used instead. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example: + + .. code-block:: python + + import urllib3 + + print(urllib3.util.make_headers(keep_alive=True, user_agent="Batman/1.0")) + # {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + print(urllib3.util.make_headers(accept_encoding=True)) + # {'accept-encoding': 'gzip,deflate'} + """ + headers: dict[str, str] = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ",".join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers["accept-encoding"] = accept_encoding + + if user_agent: + headers["user-agent"] = user_agent + + if keep_alive: + headers["connection"] = "keep-alive" + + if basic_auth: + headers[ + "authorization" + ] = f"Basic {b64encode(basic_auth.encode('latin-1')).decode()}" + + if proxy_basic_auth: + headers[ + "proxy-authorization" + ] = f"Basic {b64encode(proxy_basic_auth.encode('latin-1')).decode()}" + + if disable_cache: + headers["cache-control"] = "no-cache" + + return headers + + +def set_file_position( + body: typing.Any, pos: _TYPE_BODY_POSITION | None +) -> _TYPE_BODY_POSITION | None: + """ + If a position is provided, move file to that point. + Otherwise, we'll attempt to record a position for future use. + """ + if pos is not None: + rewind_body(body, pos) + elif getattr(body, "tell", None) is not None: + try: + pos = body.tell() + except OSError: + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body. + pos = _FAILEDTELL + + return pos + + +def rewind_body(body: typing.IO[typing.AnyStr], body_pos: _TYPE_BODY_POSITION) -> None: + """ + Attempt to rewind body to a certain position. + Primarily used for request redirects and retries. + + :param body: + File-like object that supports seek. + + :param int pos: + Position to seek to in file. + """ + body_seek = getattr(body, "seek", None) + if body_seek is not None and isinstance(body_pos, int): + try: + body_seek(body_pos) + except OSError as e: + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect/retry." + ) from e + elif body_pos is _FAILEDTELL: + raise UnrewindableBodyError( + "Unable to record file position for rewinding " + "request body during a redirect/retry." + ) + else: + raise ValueError( + f"body_pos must be of type integer, instead it was {type(body_pos)}." + ) + + +class ChunksAndContentLength(typing.NamedTuple): + chunks: typing.Iterable[bytes] | None + content_length: int | None + + +def body_to_chunks( + body: typing.Any | None, method: str, blocksize: int +) -> ChunksAndContentLength: + """Takes the HTTP request method, body, and blocksize and + transforms them into an iterable of chunks to pass to + socket.sendall() and an optional 'Content-Length' header. + + A 'Content-Length' of 'None' indicates the length of the body + can't be determined so should use 'Transfer-Encoding: chunked' + for framing instead. + """ + + chunks: typing.Iterable[bytes] | None + content_length: int | None + + # No body, we need to make a recommendation on 'Content-Length' + # based on whether that request method is expected to have + # a body or not. + if body is None: + chunks = None + if method.upper() not in _METHODS_NOT_EXPECTING_BODY: + content_length = 0 + else: + content_length = None + + # Bytes or strings become bytes + elif isinstance(body, (str, bytes)): + chunks = (to_bytes(body),) + content_length = len(chunks[0]) + + # File-like object, TODO: use seek() and tell() for length? + elif hasattr(body, "read"): + + def chunk_readable() -> typing.Iterable[bytes]: + nonlocal body, blocksize + encode = isinstance(body, io.TextIOBase) + while True: + datablock = body.read(blocksize) + if not datablock: + break + if encode: + datablock = datablock.encode("iso-8859-1") + yield datablock + + chunks = chunk_readable() + content_length = None + + # Otherwise we need to start checking via duck-typing. + else: + try: + # Check if the body implements the buffer API. + mv = memoryview(body) + except TypeError: + try: + # Check if the body is an iterable + chunks = iter(body) + content_length = None + except TypeError: + raise TypeError( + f"'body' must be a bytes-like object, file-like " + f"object, or iterable. Instead was {body!r}" + ) from None + else: + # Since it implements the buffer API can be passed directly to socket.sendall() + chunks = (body,) + content_length = mv.nbytes + + return ChunksAndContentLength(chunks=chunks, content_length=content_length) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/response.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/response.py new file mode 100644 index 0000000..0f45786 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/response.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import http.client as httplib +from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj: object) -> bool: + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check `isclosed()` first, in case Python3 doesn't set `closed`. + # GH Issue #928 + return obj.isclosed() # type: ignore[no-any-return, attr-defined] + except AttributeError: + pass + + try: + # Check via the official file-like-object way. + return obj.closed # type: ignore[no-any-return, attr-defined] + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None # type: ignore[attr-defined] + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers: httplib.HTTPMessage) -> None: + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param http.client.HTTPMessage headers: Headers to verify. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError(f"expected httplib.Message, got {type(headers)}.") + + unparsed_data = None + + # get_payload is actually email.message.Message.get_payload; + # we're only interested in the result if it's not a multipart message + if not headers.is_multipart(): + payload = headers.get_payload() + + if isinstance(payload, (bytes, str)): + unparsed_data = payload + + # httplib is assuming a response body is available + # when parsing headers even when httplib only sends + # header data to parse_headers() This results in + # defects on multipart responses in particular. + # See: https://github.com/urllib3/urllib3/issues/800 + + # So we ignore the following defects: + # - StartBoundaryNotFoundDefect: + # The claimed start boundary was never found. + # - MultipartInvariantViolationDefect: + # A message claimed to be a multipart but no subparts were found. + defects = [ + defect + for defect in headers.defects + if not isinstance( + defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) + ) + ] + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response: httplib.HTTPResponse) -> bool: + """ + Checks whether the request of a response has been a HEAD-request. + + :param http.client.HTTPResponse response: + Response to check if the originating request + used 'HEAD' as a method. + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method_str = response._method # type: str # type: ignore[attr-defined] + return method_str.upper() == "HEAD" diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/retry.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/retry.py new file mode 100644 index 0000000..7572bfd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/retry.py @@ -0,0 +1,529 @@ +from __future__ import annotations + +import email +import logging +import random +import re +import time +import typing +from itertools import takewhile +from types import TracebackType + +from ..exceptions import ( + ConnectTimeoutError, + InvalidHeader, + MaxRetryError, + ProtocolError, + ProxyError, + ReadTimeoutError, + ResponseError, +) +from .util import reraise + +if typing.TYPE_CHECKING: + from ..connectionpool import ConnectionPool + from ..response import BaseHTTPResponse + +log = logging.getLogger(__name__) + + +# Data structure for representing the metadata of requests that result in a retry. +class RequestHistory(typing.NamedTuple): + method: str | None + url: str | None + error: Exception | None + status: int | None + redirect_location: str | None + + +class Retry: + """Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool: + + .. code-block:: python + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request("GET", "https://example.com/") + + Or per-request (which overrides the default for the pool): + + .. code-block:: python + + response = http.request("GET", "https://example.com/", retries=Retry(10)) + + Retries can be disabled by passing ``False``: + + .. code-block:: python + + response = http.request("GET", "https://example.com/", retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int status: + How many times to retry on bad status codes. + + These are retries made on responses, where status code matches + ``status_forcelist``. + + Set to ``0`` to fail on the first retry of this type. + + :param int other: + How many times to retry on other errors. + + Other errors are errors that are not connect, read, redirect or status errors. + These errors might be raised after the request was sent to the server, so the + request might have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + If ``total`` is not set, it's a good idea to set this to 0 to account + for unexpected edge cases and avoid infinite retry loops. + + :param Collection allowed_methods: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. + + Set to a ``None`` value to retry on any verb. + + :param Collection status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``allowed_methods`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ** ({number of previous retries})) + + seconds. If `backoff_jitter` is non-zero, this sleep is extended by:: + + random.uniform(0, {backoff jitter}) + + seconds. For example, if the backoff_factor is 0.1, then :func:`Retry.sleep` will + sleep for [0.0s, 0.2s, 0.4s, 0.8s, ...] between retries. No backoff will ever + be longer than `backoff_max`. + + By default, backoff is disabled (factor set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + + :param tuple history: The history of the request encountered during + each call to :meth:`~Retry.increment`. The list is in the order + the requests occurred. Each list item is of class :class:`RequestHistory`. + + :param bool respect_retry_after_header: + Whether to respect Retry-After header on status codes defined as + :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + + :param Collection remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. + """ + + #: Default methods to be used for ``allowed_methods`` + DEFAULT_ALLOWED_METHODS = frozenset( + ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] + ) + + #: Default status codes to be used for ``status_forcelist`` + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + #: Default headers to be used for ``remove_headers_on_redirect`` + DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(["Cookie", "Authorization"]) + + #: Default maximum backoff time. + DEFAULT_BACKOFF_MAX = 120 + + # Backward compatibility; assigned outside of the class. + DEFAULT: typing.ClassVar[Retry] + + def __init__( + self, + total: bool | int | None = 10, + connect: int | None = None, + read: int | None = None, + redirect: bool | int | None = None, + status: int | None = None, + other: int | None = None, + allowed_methods: typing.Collection[str] | None = DEFAULT_ALLOWED_METHODS, + status_forcelist: typing.Collection[int] | None = None, + backoff_factor: float = 0, + backoff_max: float = DEFAULT_BACKOFF_MAX, + raise_on_redirect: bool = True, + raise_on_status: bool = True, + history: tuple[RequestHistory, ...] | None = None, + respect_retry_after_header: bool = True, + remove_headers_on_redirect: typing.Collection[ + str + ] = DEFAULT_REMOVE_HEADERS_ON_REDIRECT, + backoff_jitter: float = 0.0, + ) -> None: + self.total = total + self.connect = connect + self.read = read + self.status = status + self.other = other + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.allowed_methods = allowed_methods + self.backoff_factor = backoff_factor + self.backoff_max = backoff_max + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self.history = history or () + self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = frozenset( + h.lower() for h in remove_headers_on_redirect + ) + self.backoff_jitter = backoff_jitter + + def new(self, **kw: typing.Any) -> Retry: + params = dict( + total=self.total, + connect=self.connect, + read=self.read, + redirect=self.redirect, + status=self.status, + other=self.other, + allowed_methods=self.allowed_methods, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + backoff_max=self.backoff_max, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect, + respect_retry_after_header=self.respect_retry_after_header, + backoff_jitter=self.backoff_jitter, + ) + + params.update(kw) + return type(self)(**params) # type: ignore[arg-type] + + @classmethod + def from_int( + cls, + retries: Retry | bool | int | None, + redirect: bool | int | None = True, + default: Retry | bool | int | None = None, + ) -> Retry: + """Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self) -> float: + """Formula for computing the current backoff + + :rtype: float + """ + # We want to consider only the last consecutive errors sequence (Ignore redirects). + consecutive_errors_len = len( + list( + takewhile(lambda x: x.redirect_location is None, reversed(self.history)) + ) + ) + if consecutive_errors_len <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) + if self.backoff_jitter != 0.0: + backoff_value += random.random() * self.backoff_jitter + return float(max(0, min(self.backoff_max, backoff_value))) + + def parse_retry_after(self, retry_after: str) -> float: + seconds: float + # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = int(retry_after) + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + raise InvalidHeader(f"Invalid Retry-After header: {retry_after}") + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + seconds = max(seconds, 0) + + return seconds + + def get_retry_after(self, response: BaseHTTPResponse) -> float | None: + """Get the value of Retry-After in seconds.""" + + retry_after = response.headers.get("Retry-After") + + if retry_after is None: + return None + + return self.parse_retry_after(retry_after) + + def sleep_for_retry(self, response: BaseHTTPResponse) -> bool: + retry_after = self.get_retry_after(response) + if retry_after: + time.sleep(retry_after) + return True + + return False + + def _sleep_backoff(self) -> None: + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def sleep(self, response: BaseHTTPResponse | None = None) -> None: + """Sleep between retry attempts. + + This method will respect a server's ``Retry-After`` response header + and sleep the duration of the time requested. If that is not present, it + will use an exponential backoff. By default, the backoff factor is 0 and + this method will return immediately. + """ + + if self.respect_retry_after_header and response: + slept = self.sleep_for_retry(response) + if slept: + return + + self._sleep_backoff() + + def _is_connection_error(self, err: Exception) -> bool: + """Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + if isinstance(err, ProxyError): + err = err.original_error + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err: Exception) -> bool: + """Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def _is_method_retryable(self, method: str) -> bool: + """Checks if a given HTTP method should be retried upon, depending if + it is included in the allowed_methods + """ + if self.allowed_methods and method.upper() not in self.allowed_methods: + return False + return True + + def is_retry( + self, method: str, status_code: int, has_retry_after: bool = False + ) -> bool: + """Is this method/status code retryable? (Based on allowlists and control + variables such as the number of total retries to allow, whether to + respect the Retry-After header, whether this header is present, and + whether the returned status code is on the list of status codes to + be retried upon on the presence of the aforementioned header) + """ + if not self._is_method_retryable(method): + return False + + if self.status_forcelist and status_code in self.status_forcelist: + return True + + return bool( + self.total + and self.respect_retry_after_header + and has_retry_after + and (status_code in self.RETRY_AFTER_STATUS_CODES) + ) + + def is_exhausted(self) -> bool: + """Are we out of retries?""" + retry_counts = [ + x + for x in ( + self.total, + self.connect, + self.read, + self.redirect, + self.status, + self.other, + ) + if x + ] + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Retry: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self) -> str: + return ( + f"{type(self).__name__}(total={self.total}, connect={self.connect}, " + f"read={self.read}, redirect={self.redirect}, status={self.status})" + ) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_.py new file mode 100644 index 0000000..b14cf27 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_.py @@ -0,0 +1,509 @@ +from __future__ import annotations + +import hmac +import os +import socket +import sys +import typing +import warnings +from binascii import unhexlify +from hashlib import md5, sha1, sha256 + +from ..exceptions import ProxySchemeUnsupported, SSLError +from .url import _BRACELESS_IPV6_ADDRZ_RE, _IPV4_RE + +SSLContext = None +SSLTransport = None +HAS_NEVER_CHECK_COMMON_NAME = False +IS_PYOPENSSL = False +ALPN_PROTOCOLS = ["http/1.1"] + +_TYPE_VERSION_INFO = typing.Tuple[int, int, int, str, int] + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} + + +def _is_bpo_43522_fixed( + implementation_name: str, + version_info: _TYPE_VERSION_INFO, + pypy_version_info: _TYPE_VERSION_INFO | None, +) -> bool: + """Return True for CPython 3.8.9+, 3.9.3+ or 3.10+ and PyPy 7.3.8+ where + setting SSLContext.hostname_checks_common_name to False works. + + Outside of CPython and PyPy we don't know which implementations work + or not so we conservatively use our hostname matching as we know that works + on all implementations. + + https://github.com/urllib3/urllib3/issues/2192#issuecomment-821832963 + https://foss.heptapod.net/pypy/pypy/-/issues/3539 + """ + if implementation_name == "pypy": + # https://foss.heptapod.net/pypy/pypy/-/issues/3129 + return pypy_version_info >= (7, 3, 8) # type: ignore[operator] + elif implementation_name == "cpython": + major_minor = version_info[:2] + micro = version_info[2] + return ( + (major_minor == (3, 8) and micro >= 9) + or (major_minor == (3, 9) and micro >= 3) + or major_minor >= (3, 10) + ) + else: # Defensive: + return False + + +def _is_has_never_check_common_name_reliable( + openssl_version: str, + openssl_version_number: int, + implementation_name: str, + version_info: _TYPE_VERSION_INFO, + pypy_version_info: _TYPE_VERSION_INFO | None, +) -> bool: + # As of May 2023, all released versions of LibreSSL fail to reject certificates with + # only common names, see https://github.com/urllib3/urllib3/pull/3024 + is_openssl = openssl_version.startswith("OpenSSL ") + # Before fixing OpenSSL issue #14579, the SSL_new() API was not copying hostflags + # like X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, which tripped up CPython. + # https://github.com/openssl/openssl/issues/14579 + # This was released in OpenSSL 1.1.1l+ (>=0x101010cf) + is_openssl_issue_14579_fixed = openssl_version_number >= 0x101010CF + + return is_openssl and ( + is_openssl_issue_14579_fixed + or _is_bpo_43522_fixed(implementation_name, version_info, pypy_version_info) + ) + + +if typing.TYPE_CHECKING: + from ssl import VerifyMode + from typing import Literal, TypedDict + + from .ssltransport import SSLTransport as SSLTransportType + + class _TYPE_PEER_CERT_RET_DICT(TypedDict, total=False): + subjectAltName: tuple[tuple[str, str], ...] + subject: tuple[tuple[tuple[str, str], ...], ...] + serialNumber: str + + +# Mapping from 'ssl.PROTOCOL_TLSX' to 'TLSVersion.X' +_SSL_VERSION_TO_TLS_VERSION: dict[int, int] = {} + +try: # Do we have ssl at all? + import ssl + from ssl import ( # type: ignore[assignment] + CERT_REQUIRED, + HAS_NEVER_CHECK_COMMON_NAME, + OP_NO_COMPRESSION, + OP_NO_TICKET, + OPENSSL_VERSION, + OPENSSL_VERSION_NUMBER, + PROTOCOL_TLS, + PROTOCOL_TLS_CLIENT, + OP_NO_SSLv2, + OP_NO_SSLv3, + SSLContext, + TLSVersion, + ) + + PROTOCOL_SSLv23 = PROTOCOL_TLS + + # Setting SSLContext.hostname_checks_common_name = False didn't work before CPython + # 3.8.9, 3.9.3, and 3.10 (but OK on PyPy) or OpenSSL 1.1.1l+ + if HAS_NEVER_CHECK_COMMON_NAME and not _is_has_never_check_common_name_reliable( + OPENSSL_VERSION, + OPENSSL_VERSION_NUMBER, + sys.implementation.name, + sys.version_info, + sys.pypy_version_info if sys.implementation.name == "pypy" else None, # type: ignore[attr-defined] + ): + HAS_NEVER_CHECK_COMMON_NAME = False + + # Need to be careful here in case old TLS versions get + # removed in future 'ssl' module implementations. + for attr in ("TLSv1", "TLSv1_1", "TLSv1_2"): + try: + _SSL_VERSION_TO_TLS_VERSION[getattr(ssl, f"PROTOCOL_{attr}")] = getattr( + TLSVersion, attr + ) + except AttributeError: # Defensive: + continue + + from .ssltransport import SSLTransport # type: ignore[assignment] +except ImportError: + OP_NO_COMPRESSION = 0x20000 # type: ignore[assignment] + OP_NO_TICKET = 0x4000 # type: ignore[assignment] + OP_NO_SSLv2 = 0x1000000 # type: ignore[assignment] + OP_NO_SSLv3 = 0x2000000 # type: ignore[assignment] + PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 # type: ignore[assignment] + PROTOCOL_TLS_CLIENT = 16 # type: ignore[assignment] + + +_TYPE_PEER_CERT_RET = typing.Union["_TYPE_PEER_CERT_RET_DICT", bytes, None] + + +def assert_fingerprint(cert: bytes | None, fingerprint: str) -> None: + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + if cert is None: + raise SSLError("No certificate for the peer.") + + fingerprint = fingerprint.replace(":", "").lower() + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError(f"Fingerprint of invalid length: {fingerprint}") + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not hmac.compare_digest(cert_digest, fingerprint_bytes): + raise SSLError( + f'Fingerprints did not match. Expected "{fingerprint}", got "{cert_digest.hex()}"' + ) + + +def resolve_cert_reqs(candidate: None | int | str) -> VerifyMode: + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_REQUIRED`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbreviation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_REQUIRED + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "CERT_" + candidate) + return res # type: ignore[no-any-return] + + return candidate # type: ignore[return-value] + + +def resolve_ssl_version(candidate: None | int | str) -> int: + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_TLS + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "PROTOCOL_" + candidate) + return typing.cast(int, res) + + return candidate + + +def create_urllib3_context( + ssl_version: int | None = None, + cert_reqs: int | None = None, + options: int | None = None, + ciphers: str | None = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, +) -> ssl.SSLContext: + """Creates and configures an :class:`ssl.SSLContext` instance for use with urllib3. + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + + This parameter is deprecated instead use 'ssl_minimum_version'. + :param ssl_minimum_version: + The minimum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. + :param ssl_maximum_version: + The maximum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. + Not recommended to set to anything other than 'ssl.TLSVersion.MAXIMUM_SUPPORTED' which is the + default value. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. + :param ciphers: + Which cipher suites to allow the server to select. Defaults to either system configured + ciphers if OpenSSL 1.1.1+, otherwise uses a secure default set of ciphers. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + if SSLContext is None: + raise TypeError("Can't create an SSLContext object without an ssl module") + + # This means 'ssl_version' was specified as an exact value. + if ssl_version not in (None, PROTOCOL_TLS, PROTOCOL_TLS_CLIENT): + # Disallow setting 'ssl_version' and 'ssl_minimum|maximum_version' + # to avoid conflicts. + if ssl_minimum_version is not None or ssl_maximum_version is not None: + raise ValueError( + "Can't specify both 'ssl_version' and either " + "'ssl_minimum_version' or 'ssl_maximum_version'" + ) + + # 'ssl_version' is deprecated and will be removed in the future. + else: + # Use 'ssl_minimum_version' and 'ssl_maximum_version' instead. + ssl_minimum_version = _SSL_VERSION_TO_TLS_VERSION.get( + ssl_version, TLSVersion.MINIMUM_SUPPORTED + ) + ssl_maximum_version = _SSL_VERSION_TO_TLS_VERSION.get( + ssl_version, TLSVersion.MAXIMUM_SUPPORTED + ) + + # This warning message is pushing users to use 'ssl_minimum_version' + # instead of both min/max. Best practice is to only set the minimum version and + # keep the maximum version to be it's default value: 'TLSVersion.MAXIMUM_SUPPORTED' + warnings.warn( + "'ssl_version' option is deprecated and will be " + "removed in urllib3 v2.1.0. Instead use 'ssl_minimum_version'", + category=DeprecationWarning, + stacklevel=2, + ) + + # PROTOCOL_TLS is deprecated in Python 3.10 so we always use PROTOCOL_TLS_CLIENT + context = SSLContext(PROTOCOL_TLS_CLIENT) + + if ssl_minimum_version is not None: + context.minimum_version = ssl_minimum_version + else: # Python <3.10 defaults to 'MINIMUM_SUPPORTED' so explicitly set TLSv1.2 here + context.minimum_version = TLSVersion.TLSv1_2 + + if ssl_maximum_version is not None: + context.maximum_version = ssl_maximum_version + + # Unless we're given ciphers defer to either system ciphers in + # the case of OpenSSL 1.1.1+ or use our own secure default ciphers. + if ciphers: + context.set_ciphers(ciphers) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + # TLSv1.2 only. Unless set explicitly, do not request tickets. + # This may save some bandwidth on wire, and although the ticket is encrypted, + # there is a risk associated with it being on wire, + # if the server is not rotating its ticketing keys properly. + options |= OP_NO_TICKET + + context.options |= options + + # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is + # necessary for conditional client cert authentication with TLS 1.3. + # The attribute is None for OpenSSL <= 1.1.0 or does not exist when using + # an SSLContext created by pyOpenSSL. + if getattr(context, "post_handshake_auth", None) is not None: + context.post_handshake_auth = True + + # The order of the below lines setting verify_mode and check_hostname + # matter due to safe-guards SSLContext has to prevent an SSLContext with + # check_hostname=True, verify_mode=NONE/OPTIONAL. + # We always set 'check_hostname=False' for pyOpenSSL so we rely on our own + # 'ssl.match_hostname()' implementation. + if cert_reqs == ssl.CERT_REQUIRED and not IS_PYOPENSSL: + context.verify_mode = cert_reqs + context.check_hostname = True + else: + context.check_hostname = False + context.verify_mode = cert_reqs + + try: + context.hostname_checks_common_name = False + except AttributeError: # Defensive: for CPython < 3.8.9 and 3.9.3; for PyPy < 7.3.8 + pass + + # Enable logging of TLS session keys via defacto standard environment variable + # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. + if hasattr(context, "keylog_filename"): + sslkeylogfile = os.environ.get("SSLKEYLOGFILE") + if sslkeylogfile: + context.keylog_filename = sslkeylogfile + + return context + + +@typing.overload +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = ..., + certfile: str | None = ..., + cert_reqs: int | None = ..., + ca_certs: str | None = ..., + server_hostname: str | None = ..., + ssl_version: int | None = ..., + ciphers: str | None = ..., + ssl_context: ssl.SSLContext | None = ..., + ca_cert_dir: str | None = ..., + key_password: str | None = ..., + ca_cert_data: None | str | bytes = ..., + tls_in_tls: Literal[False] = ..., +) -> ssl.SSLSocket: + ... + + +@typing.overload +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = ..., + certfile: str | None = ..., + cert_reqs: int | None = ..., + ca_certs: str | None = ..., + server_hostname: str | None = ..., + ssl_version: int | None = ..., + ciphers: str | None = ..., + ssl_context: ssl.SSLContext | None = ..., + ca_cert_dir: str | None = ..., + key_password: str | None = ..., + ca_cert_data: None | str | bytes = ..., + tls_in_tls: bool = ..., +) -> ssl.SSLSocket | SSLTransportType: + ... + + +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = None, + certfile: str | None = None, + cert_reqs: int | None = None, + ca_certs: str | None = None, + server_hostname: str | None = None, + ssl_version: int | None = None, + ciphers: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_cert_dir: str | None = None, + key_password: str | None = None, + ca_cert_data: None | str | bytes = None, + tls_in_tls: bool = False, +) -> ssl.SSLSocket | SSLTransportType: + """ + All arguments except for server_hostname, ssl_context, tls_in_tls, ca_cert_data and + ca_cert_dir have the same meaning as they do when using + :func:`ssl.create_default_context`, :meth:`ssl.SSLContext.load_cert_chain`, + :meth:`ssl.SSLContext.set_ciphers` and :meth:`ssl.SSLContext.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + :param key_password: + Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() + :param tls_in_tls: + Use SSLTransport to wrap the existing socket. + """ + context = ssl_context + if context is None: + # Note: This branch of code and all the variables in it are only used in tests. + # We should consider deprecating and removing this code. + context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) + + if ca_certs or ca_cert_dir or ca_cert_data: + try: + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) + except OSError as e: + raise SSLError(e) from e + + elif ssl_context is None and hasattr(context, "load_default_certs"): + # try to load OS default certs; works well on Windows. + context.load_default_certs() + + # Attempt to detect if we get the goofy behavior of the + # keyfile being encrypted and OpenSSL asking for the + # passphrase via the terminal and instead error out. + if keyfile and key_password is None and _is_key_file_encrypted(keyfile): + raise SSLError("Client private key is encrypted, password is required") + + if certfile: + if key_password is None: + context.load_cert_chain(certfile, keyfile) + else: + context.load_cert_chain(certfile, keyfile, key_password) + + try: + context.set_alpn_protocols(ALPN_PROTOCOLS) + except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols + pass + + ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls, server_hostname) + return ssl_sock + + +def is_ipaddress(hostname: str | bytes) -> bool: + """Detects whether the hostname given is an IPv4 or IPv6 address. + Also detects IPv6 addresses with Zone IDs. + + :param str hostname: Hostname to examine. + :return: True if the hostname is an IP address, False otherwise. + """ + if isinstance(hostname, bytes): + # IDN A-label bytes are ASCII compatible. + hostname = hostname.decode("ascii") + return bool(_IPV4_RE.match(hostname) or _BRACELESS_IPV6_ADDRZ_RE.match(hostname)) + + +def _is_key_file_encrypted(key_file: str) -> bool: + """Detects if a key file is encrypted or not.""" + with open(key_file) as f: + for line in f: + # Look for Proc-Type: 4,ENCRYPTED + if "ENCRYPTED" in line: + return True + + return False + + +def _ssl_wrap_socket_impl( + sock: socket.socket, + ssl_context: ssl.SSLContext, + tls_in_tls: bool, + server_hostname: str | None = None, +) -> ssl.SSLSocket | SSLTransportType: + if tls_in_tls: + if not SSLTransport: + # Import error, ssl is not available. + raise ProxySchemeUnsupported( + "TLS in TLS requires support for the 'ssl' module" + ) + + SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) + return SSLTransport(sock, ssl_context, server_hostname) + + return ssl_context.wrap_socket(sock, server_hostname=server_hostname) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_match_hostname.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_match_hostname.py new file mode 100644 index 0000000..453cfd4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssl_match_hostname.py @@ -0,0 +1,159 @@ +"""The match_hostname() function from Python 3.5, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html +# It is modified to remove commonName support. + +from __future__ import annotations + +import ipaddress +import re +import typing +from ipaddress import IPv4Address, IPv6Address + +if typing.TYPE_CHECKING: + from .ssl_ import _TYPE_PEER_CERT_RET_DICT + +__version__ = "3.5.0.1" + + +class CertificateError(ValueError): + pass + + +def _dnsname_match( + dn: typing.Any, hostname: str, max_wildcards: int = 1 +) -> typing.Match[str] | None | bool: + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r".") + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count("*") + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn) + ) + + # speed up common case w/o wildcards + if not wildcards: + return bool(dn.lower() == hostname.lower()) + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == "*": + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append("[^.]+") + elif leftmost.startswith("xn--") or hostname.startswith("xn--"): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) + return pat.match(hostname) + + +def _ipaddress_match(ipname: str, host_ip: IPv4Address | IPv6Address) -> bool: + """Exact matching of IP addresses. + + RFC 9110 section 4.3.5: "A reference identity of IP-ID contains the decoded + bytes of the IP address. An IP version 4 address is 4 octets, and an IP + version 6 address is 16 octets. [...] A reference identity of type IP-ID + matches if the address is identical to an iPAddress value of the + subjectAltName extension of the certificate." + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(ipname.rstrip()) + return bool(ip.packed == host_ip.packed) + + +def match_hostname( + cert: _TYPE_PEER_CERT_RET_DICT | None, + hostname: str, + hostname_checks_common_name: bool = False, +) -> None: + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError( + "empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED" + ) + try: + # Divergence from upstream: ipaddress can't handle byte str + # + # The ipaddress module shipped with Python < 3.9 does not support + # scoped IPv6 addresses so we unconditionally strip the Zone IDs for + # now. Once we drop support for Python 3.9 we can remove this branch. + if "%" in hostname: + host_ip = ipaddress.ip_address(hostname[: hostname.rfind("%")]) + else: + host_ip = ipaddress.ip_address(hostname) + + except ValueError: + # Not an IP address (common case) + host_ip = None + dnsnames = [] + san: tuple[tuple[str, str], ...] = cert.get("subjectAltName", ()) + key: str + value: str + for key, value in san: + if key == "DNS": + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == "IP Address": + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + + # We only check 'commonName' if it's enabled and we're not verifying + # an IP address. IP addresses aren't valid within 'commonName'. + if hostname_checks_common_name and host_ip is None and not dnsnames: + for sub in cert.get("subject", ()): + for key, value in sub: + if key == "commonName": + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + + if len(dnsnames) > 1: + raise CertificateError( + "hostname %r " + "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) + ) + elif len(dnsnames) == 1: + raise CertificateError(f"hostname {hostname!r} doesn't match {dnsnames[0]!r}") + else: + raise CertificateError("no appropriate subjectAltName fields were found") diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssltransport.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssltransport.py new file mode 100644 index 0000000..fa9f2b3 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/ssltransport.py @@ -0,0 +1,280 @@ +from __future__ import annotations + +import io +import socket +import ssl +import typing + +from ..exceptions import ProxySchemeUnsupported + +if typing.TYPE_CHECKING: + from typing import Literal + + from .ssl_ import _TYPE_PEER_CERT_RET, _TYPE_PEER_CERT_RET_DICT + + +_SelfT = typing.TypeVar("_SelfT", bound="SSLTransport") +_WriteBuffer = typing.Union[bytearray, memoryview] +_ReturnValue = typing.TypeVar("_ReturnValue") + +SSL_BLOCKSIZE = 16384 + + +class SSLTransport: + """ + The SSLTransport wraps an existing socket and establishes an SSL connection. + + Contrary to Python's implementation of SSLSocket, it allows you to chain + multiple TLS connections together. It's particularly useful if you need to + implement TLS within TLS. + + The class supports most of the socket API operations. + """ + + @staticmethod + def _validate_ssl_context_for_tls_in_tls(ssl_context: ssl.SSLContext) -> None: + """ + Raises a ProxySchemeUnsupported if the provided ssl_context can't be used + for TLS in TLS. + + The only requirement is that the ssl_context provides the 'wrap_bio' + methods. + """ + + if not hasattr(ssl_context, "wrap_bio"): + raise ProxySchemeUnsupported( + "TLS in TLS requires SSLContext.wrap_bio() which isn't " + "available on non-native SSLContext" + ) + + def __init__( + self, + socket: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + suppress_ragged_eofs: bool = True, + ) -> None: + """ + Create an SSLTransport around socket using the provided ssl_context. + """ + self.incoming = ssl.MemoryBIO() + self.outgoing = ssl.MemoryBIO() + + self.suppress_ragged_eofs = suppress_ragged_eofs + self.socket = socket + + self.sslobj = ssl_context.wrap_bio( + self.incoming, self.outgoing, server_hostname=server_hostname + ) + + # Perform initial handshake. + self._ssl_io_loop(self.sslobj.do_handshake) + + def __enter__(self: _SelfT) -> _SelfT: + return self + + def __exit__(self, *_: typing.Any) -> None: + self.close() + + def fileno(self) -> int: + return self.socket.fileno() + + def read(self, len: int = 1024, buffer: typing.Any | None = None) -> int | bytes: + return self._wrap_ssl_read(len, buffer) + + def recv(self, buflen: int = 1024, flags: int = 0) -> int | bytes: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv") + return self._wrap_ssl_read(buflen) + + def recv_into( + self, + buffer: _WriteBuffer, + nbytes: int | None = None, + flags: int = 0, + ) -> None | int | bytes: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv_into") + if nbytes is None: + nbytes = len(buffer) + return self.read(nbytes, buffer) + + def sendall(self, data: bytes, flags: int = 0) -> None: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to sendall") + count = 0 + with memoryview(data) as view, view.cast("B") as byte_view: + amount = len(byte_view) + while count < amount: + v = self.send(byte_view[count:]) + count += v + + def send(self, data: bytes, flags: int = 0) -> int: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to send") + return self._ssl_io_loop(self.sslobj.write, data) + + def makefile( + self, + mode: str, + buffering: int | None = None, + *, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> typing.BinaryIO | typing.TextIO | socket.SocketIO: + """ + Python's httpclient uses makefile and buffered io when reading HTTP + messages and we need to support it. + + This is unfortunately a copy and paste of socket.py makefile with small + changes to point to the socket directly. + """ + if not set(mode) <= {"r", "w", "b"}: + raise ValueError(f"invalid mode {mode!r} (only r, w, b allowed)") + + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = socket.SocketIO(self, rawmode) # type: ignore[arg-type] + self.socket._io_refs += 1 # type: ignore[attr-defined] + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + buffer: typing.BinaryIO + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) # type: ignore[assignment] + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode # type: ignore[misc] + return text + + def unwrap(self) -> None: + self._ssl_io_loop(self.sslobj.unwrap) + + def close(self) -> None: + self.socket.close() + + @typing.overload + def getpeercert( + self, binary_form: Literal[False] = ... + ) -> _TYPE_PEER_CERT_RET_DICT | None: + ... + + @typing.overload + def getpeercert(self, binary_form: Literal[True]) -> bytes | None: + ... + + def getpeercert(self, binary_form: bool = False) -> _TYPE_PEER_CERT_RET: + return self.sslobj.getpeercert(binary_form) # type: ignore[return-value] + + def version(self) -> str | None: + return self.sslobj.version() + + def cipher(self) -> tuple[str, str, int] | None: + return self.sslobj.cipher() + + def selected_alpn_protocol(self) -> str | None: + return self.sslobj.selected_alpn_protocol() + + def selected_npn_protocol(self) -> str | None: + return self.sslobj.selected_npn_protocol() + + def shared_ciphers(self) -> list[tuple[str, str, int]] | None: + return self.sslobj.shared_ciphers() + + def compression(self) -> str | None: + return self.sslobj.compression() + + def settimeout(self, value: float | None) -> None: + self.socket.settimeout(value) + + def gettimeout(self) -> float | None: + return self.socket.gettimeout() + + def _decref_socketios(self) -> None: + self.socket._decref_socketios() # type: ignore[attr-defined] + + def _wrap_ssl_read(self, len: int, buffer: bytearray | None = None) -> int | bytes: + try: + return self._ssl_io_loop(self.sslobj.read, len, buffer) + except ssl.SSLError as e: + if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: + return 0 # eof, return 0. + else: + raise + + # func is sslobj.do_handshake or sslobj.unwrap + @typing.overload + def _ssl_io_loop(self, func: typing.Callable[[], None]) -> None: + ... + + # func is sslobj.write, arg1 is data + @typing.overload + def _ssl_io_loop(self, func: typing.Callable[[bytes], int], arg1: bytes) -> int: + ... + + # func is sslobj.read, arg1 is len, arg2 is buffer + @typing.overload + def _ssl_io_loop( + self, + func: typing.Callable[[int, bytearray | None], bytes], + arg1: int, + arg2: bytearray | None, + ) -> bytes: + ... + + def _ssl_io_loop( + self, + func: typing.Callable[..., _ReturnValue], + arg1: None | bytes | int = None, + arg2: bytearray | None = None, + ) -> _ReturnValue: + """Performs an I/O loop between incoming/outgoing and the socket.""" + should_loop = True + ret = None + + while should_loop: + errno = None + try: + if arg1 is None and arg2 is None: + ret = func() + elif arg2 is None: + ret = func(arg1) + else: + ret = func(arg1, arg2) + except ssl.SSLError as e: + if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): + # WANT_READ, and WANT_WRITE are expected, others are not. + raise e + errno = e.errno + + buf = self.outgoing.read() + self.socket.sendall(buf) + + if errno is None: + should_loop = False + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = self.socket.recv(SSL_BLOCKSIZE) + if buf: + self.incoming.write(buf) + else: + self.incoming.write_eof() + return typing.cast(_ReturnValue, ret) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/timeout.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/timeout.py new file mode 100644 index 0000000..4bb1be1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/timeout.py @@ -0,0 +1,275 @@ +from __future__ import annotations + +import time +import typing +from enum import Enum +from socket import getdefaulttimeout + +from ..exceptions import TimeoutStateError + +if typing.TYPE_CHECKING: + from typing import Final + + +class _TYPE_DEFAULT(Enum): + # This value should never be passed to socket.settimeout() so for safety we use a -1. + # socket.settimout() raises a ValueError for negative values. + token = -1 + + +_DEFAULT_TIMEOUT: Final[_TYPE_DEFAULT] = _TYPE_DEFAULT.token + +_TYPE_TIMEOUT = typing.Optional[typing.Union[float, _TYPE_DEFAULT]] + + +class Timeout: + """Timeout configuration. + + Timeouts can be defined as a default for a pool: + + .. code-block:: python + + import urllib3 + + timeout = urllib3.util.Timeout(connect=2.0, read=7.0) + + http = urllib3.PoolManager(timeout=timeout) + + resp = http.request("GET", "https://example.com/") + + print(resp.status) + + Or per-request (which overrides the default for the pool): + + .. code-block:: python + + response = http.request("GET", "https://example.com/", timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``: + + .. code-block:: python + + no_timeout = Timeout(connect=None, read=None) + response = http.request("GET", "https://example.com/", timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: int, float, or None + + :param connect: + The maximum amount of time (in seconds) to wait for a connection + attempt to a server to succeed. Omitting the parameter will default the + connect timeout to the system default, probably `the global default + timeout in socket.py + `_. + None will set an infinite timeout for connection attempts. + + :type connect: int, float, or None + + :param read: + The maximum amount of time (in seconds) to wait between consecutive + read operations for a response from the server. Omitting the parameter + will default the read timeout to the system default, probably `the + global default timeout in socket.py + `_. + None will set an infinite timeout. + + :type read: int, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT + + def __init__( + self, + total: _TYPE_TIMEOUT = None, + connect: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + read: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + ) -> None: + self._connect = self._validate_timeout(connect, "connect") + self._read = self._validate_timeout(read, "read") + self.total = self._validate_timeout(total, "total") + self._start_connect: float | None = None + + def __repr__(self) -> str: + return f"{type(self).__name__}(connect={self._connect!r}, read={self._read!r}, total={self.total!r})" + + # __str__ provided for backwards compatibility + __str__ = __repr__ + + @staticmethod + def resolve_default_timeout(timeout: _TYPE_TIMEOUT) -> float | None: + return getdefaulttimeout() if timeout is _DEFAULT_TIMEOUT else timeout + + @classmethod + def _validate_timeout(cls, value: _TYPE_TIMEOUT, name: str) -> _TYPE_TIMEOUT: + """Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If it is a numeric value less than or equal to + zero, or the type is not an integer, float, or None. + """ + if value is None or value is _DEFAULT_TIMEOUT: + return value + + if isinstance(value, bool): + raise ValueError( + "Timeout cannot be a boolean value. It must " + "be an int, float or None." + ) + try: + float(value) + except (TypeError, ValueError): + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) from None + + try: + if value <= 0: + raise ValueError( + "Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value) + ) + except TypeError: + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) from None + + return value + + @classmethod + def from_float(cls, timeout: _TYPE_TIMEOUT) -> Timeout: + """Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, :attr:`urllib3.util.Timeout.DEFAULT_TIMEOUT`, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self) -> Timeout: + """Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, total=self.total) + + def start_connect(self) -> float: + """Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = time.monotonic() + return self._start_connect + + def get_connect_duration(self) -> float: + """Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time in seconds. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError( + "Can't get connect duration for timer that has not started." + ) + return time.monotonic() - self._start_connect + + @property + def connect_timeout(self) -> _TYPE_TIMEOUT: + """Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is _DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) # type: ignore[type-var] + + @property + def read_timeout(self) -> float | None: + """Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if ( + self.total is not None + and self.total is not _DEFAULT_TIMEOUT + and self._read is not None + and self._read is not _DEFAULT_TIMEOUT + ): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), self._read)) + elif self.total is not None and self.total is not _DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self.resolve_default_timeout(self._read) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/url.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/url.py new file mode 100644 index 0000000..d53ea93 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/url.py @@ -0,0 +1,471 @@ +from __future__ import annotations + +import re +import typing + +from ..exceptions import LocationParseError +from .util import to_str + +# We only want to normalize urls with an HTTP(S) scheme. +# urllib3 infers URLs without a scheme (None) to be http. +_NORMALIZABLE_SCHEMES = ("http", "https", None) + +# Almost all of these patterns were derived from the +# 'rfc3986' module: https://github.com/python-hyper/rfc3986 +_PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") +_SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") +_URI_RE = re.compile( + r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" + r"(?://([^\\/?#]*))?" + r"([^?#]*)" + r"(?:\?([^#]*))?" + r"(?:#(.*))?$", + re.UNICODE | re.DOTALL, +) + +_IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" +_HEX_PAT = "[0-9A-Fa-f]{1,4}" +_LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=_HEX_PAT, ipv4=_IPV4_PAT) +_subs = {"hex": _HEX_PAT, "ls32": _LS32_PAT} +_variations = [ + # 6( h16 ":" ) ls32 + "(?:%(hex)s:){6}%(ls32)s", + # "::" 5( h16 ":" ) ls32 + "::(?:%(hex)s:){5}%(ls32)s", + # [ h16 ] "::" 4( h16 ":" ) ls32 + "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", + # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", + # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", + # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", + # [ *4( h16 ":" ) h16 ] "::" ls32 + "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", + # [ *5( h16 ":" ) h16 ] "::" h16 + "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", + # [ *6( h16 ":" ) h16 ] "::" + "(?:(?:%(hex)s:){0,6}%(hex)s)?::", +] + +_UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~" +_IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" +_ZONE_ID_PAT = "(?:%25|%)(?:[" + _UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" +_IPV6_ADDRZ_PAT = r"\[" + _IPV6_PAT + r"(?:" + _ZONE_ID_PAT + r")?\]" +_REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" +_TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") + +_IPV4_RE = re.compile("^" + _IPV4_PAT + "$") +_IPV6_RE = re.compile("^" + _IPV6_PAT + "$") +_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT + "$") +_BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT[2:-2] + "$") +_ZONE_ID_RE = re.compile("(" + _ZONE_ID_PAT + r")\]$") + +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % ( + _REG_NAME_PAT, + _IPV4_PAT, + _IPV6_ADDRZ_PAT, +) +_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) + +_UNRESERVED_CHARS = set( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" +) +_SUB_DELIM_CHARS = set("!$&'()*+,;=") +_USERINFO_CHARS = _UNRESERVED_CHARS | _SUB_DELIM_CHARS | {":"} +_PATH_CHARS = _USERINFO_CHARS | {"@", "/"} +_QUERY_CHARS = _FRAGMENT_CHARS = _PATH_CHARS | {"?"} + + +class Url( + typing.NamedTuple( + "Url", + [ + ("scheme", typing.Optional[str]), + ("auth", typing.Optional[str]), + ("host", typing.Optional[str]), + ("port", typing.Optional[int]), + ("path", typing.Optional[str]), + ("query", typing.Optional[str]), + ("fragment", typing.Optional[str]), + ], + ) +): + """ + Data structure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. Both the scheme and host are normalized as they are + both case-insensitive according to RFC 3986. + """ + + def __new__( # type: ignore[no-untyped-def] + cls, + scheme: str | None = None, + auth: str | None = None, + host: str | None = None, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ): + if path and not path.startswith("/"): + path = "/" + path + if scheme is not None: + scheme = scheme.lower() + return super().__new__(cls, scheme, auth, host, port, path, query, fragment) + + @property + def hostname(self) -> str | None: + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self) -> str: + """Absolute path including the query string.""" + uri = self.path or "/" + + if self.query is not None: + uri += "?" + self.query + + return uri + + @property + def authority(self) -> str | None: + """ + Authority component as defined in RFC 3986 3.2. + This includes userinfo (auth), host and port. + + i.e. + userinfo@host:port + """ + userinfo = self.auth + netloc = self.netloc + if netloc is None or userinfo is None: + return netloc + else: + return f"{userinfo}@{netloc}" + + @property + def netloc(self) -> str | None: + """ + Network location including host and port. + + If you need the equivalent of urllib.parse's ``netloc``, + use the ``authority`` property instead. + """ + if self.host is None: + return None + if self.port: + return f"{self.host}:{self.port}" + return self.host + + @property + def url(self) -> str: + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: + + .. code-block:: python + + import urllib3 + + U = urllib3.util.parse_url("https://google.com/mail/") + + print(U.url) + # "https://google.com/mail/" + + print( urllib3.util.Url("https", "username:password", + "host.com", 80, "/path", "query", "fragment" + ).url + ) + # "https://username:password@host.com:80/path?query#fragment" + """ + scheme, auth, host, port, path, query, fragment = self + url = "" + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + "://" + if auth is not None: + url += auth + "@" + if host is not None: + url += host + if port is not None: + url += ":" + str(port) + if path is not None: + url += path + if query is not None: + url += "?" + query + if fragment is not None: + url += "#" + fragment + + return url + + def __str__(self) -> str: + return self.url + + +@typing.overload +def _encode_invalid_chars( + component: str, allowed_chars: typing.Container[str] +) -> str: # Abstract + ... + + +@typing.overload +def _encode_invalid_chars( + component: None, allowed_chars: typing.Container[str] +) -> None: # Abstract + ... + + +def _encode_invalid_chars( + component: str | None, allowed_chars: typing.Container[str] +) -> str | None: + """Percent-encodes a URI component without reapplying + onto an already percent-encoded component. + """ + if component is None: + return component + + component = to_str(component) + + # Normalize existing percent-encoded bytes. + # Try to see if the component we're encoding is already percent-encoded + # so we can skip all '%' characters but still encode all others. + component, percent_encodings = _PERCENT_RE.subn( + lambda match: match.group(0).upper(), component + ) + + uri_bytes = component.encode("utf-8", "surrogatepass") + is_percent_encoded = percent_encodings == uri_bytes.count(b"%") + encoded_component = bytearray() + + for i in range(0, len(uri_bytes)): + # Will return a single character bytestring + byte = uri_bytes[i : i + 1] + byte_ord = ord(byte) + if (is_percent_encoded and byte == b"%") or ( + byte_ord < 128 and byte.decode() in allowed_chars + ): + encoded_component += byte + continue + encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) + + return encoded_component.decode() + + +def _remove_path_dot_segments(path: str) -> str: + # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code + segments = path.split("/") # Turn the path into a list of segments + output = [] # Initialize the variable to use to store output + + for segment in segments: + # '.' is the current directory, so ignore it, it is superfluous + if segment == ".": + continue + # Anything other than '..', should be appended to the output + if segment != "..": + output.append(segment) + # In this case segment == '..', if we can, we should pop the last + # element + elif output: + output.pop() + + # If the path starts with '/' and the output is empty or the first string + # is non-empty + if path.startswith("/") and (not output or output[0]): + output.insert(0, "") + + # If the path starts with '/.' or '/..' ensure we add one more empty + # string to add a trailing '/' + if path.endswith(("/.", "/..")): + output.append("") + + return "/".join(output) + + +@typing.overload +def _normalize_host(host: None, scheme: str | None) -> None: + ... + + +@typing.overload +def _normalize_host(host: str, scheme: str | None) -> str: + ... + + +def _normalize_host(host: str | None, scheme: str | None) -> str | None: + if host: + if scheme in _NORMALIZABLE_SCHEMES: + is_ipv6 = _IPV6_ADDRZ_RE.match(host) + if is_ipv6: + # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as + # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID + # separator as necessary to return a valid RFC 4007 scoped IP. + match = _ZONE_ID_RE.search(host) + if match: + start, end = match.span(1) + zone_id = host[start:end] + + if zone_id.startswith("%25") and zone_id != "%25": + zone_id = zone_id[3:] + else: + zone_id = zone_id[1:] + zone_id = _encode_invalid_chars(zone_id, _UNRESERVED_CHARS) + return f"{host[:start].lower()}%{zone_id}{host[end:]}" + else: + return host.lower() + elif not _IPV4_RE.match(host): + return to_str( + b".".join([_idna_encode(label) for label in host.split(".")]), + "ascii", + ) + return host + + +def _idna_encode(name: str) -> bytes: + if not name.isascii(): + try: + import idna + except ImportError: + raise LocationParseError( + "Unable to parse URL without the 'idna' module" + ) from None + + try: + return idna.encode(name.lower(), strict=True, std3_rules=True) + except idna.IDNAError: + raise LocationParseError( + f"Name '{name}' is not a valid IDNA label" + ) from None + + return name.lower().encode("ascii") + + +def _encode_target(target: str) -> str: + """Percent-encodes a request target so that there are no invalid characters + + Pre-condition for this function is that 'target' must start with '/'. + If that is the case then _TARGET_RE will always produce a match. + """ + match = _TARGET_RE.match(target) + if not match: # Defensive: + raise LocationParseError(f"{target!r} is not a valid request URI") + + path, query = match.groups() + encoded_target = _encode_invalid_chars(path, _PATH_CHARS) + if query is not None: + query = _encode_invalid_chars(query, _QUERY_CHARS) + encoded_target += "?" + query + return encoded_target + + +def parse_url(url: str) -> Url: + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + This parser is RFC 3986 and RFC 6874 compliant. + + The parser logic and helper functions are based heavily on + work done in the ``rfc3986`` module. + + :param str url: URL to parse into a :class:`.Url` namedtuple. + + Partly backwards-compatible with :mod:`urllib.parse`. + + Example: + + .. code-block:: python + + import urllib3 + + print( urllib3.util.parse_url('http://google.com/mail/')) + # Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + + print( urllib3.util.parse_url('google.com:80')) + # Url(scheme=None, host='google.com', port=80, path=None, ...) + + print( urllib3.util.parse_url('/foo?bar')) + # Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + if not url: + # Empty + return Url() + + source_url = url + if not _SCHEME_RE.search(url): + url = "//" + url + + scheme: str | None + authority: str | None + auth: str | None + host: str | None + port: str | None + port_int: int | None + path: str | None + query: str | None + fragment: str | None + + try: + scheme, authority, path, query, fragment = _URI_RE.match(url).groups() # type: ignore[union-attr] + normalize_uri = scheme is None or scheme.lower() in _NORMALIZABLE_SCHEMES + + if scheme: + scheme = scheme.lower() + + if authority: + auth, _, host_port = authority.rpartition("@") + auth = auth or None + host, port = _HOST_PORT_RE.match(host_port).groups() # type: ignore[union-attr] + if auth and normalize_uri: + auth = _encode_invalid_chars(auth, _USERINFO_CHARS) + if port == "": + port = None + else: + auth, host, port = None, None, None + + if port is not None: + port_int = int(port) + if not (0 <= port_int <= 65535): + raise LocationParseError(url) + else: + port_int = None + + host = _normalize_host(host, scheme) + + if normalize_uri and path: + path = _remove_path_dot_segments(path) + path = _encode_invalid_chars(path, _PATH_CHARS) + if normalize_uri and query: + query = _encode_invalid_chars(query, _QUERY_CHARS) + if normalize_uri and fragment: + fragment = _encode_invalid_chars(fragment, _FRAGMENT_CHARS) + + except (ValueError, AttributeError) as e: + raise LocationParseError(source_url) from e + + # For the sake of backwards compatibility we put empty + # string values for path if there are any defined values + # beyond the path in the URL. + # TODO: Remove this when we break backwards compatibility. + if not path: + if query is not None or fragment is not None: + path = "" + else: + path = None + + return Url( + scheme=scheme, + auth=auth, + host=host, + port=port_int, + path=path, + query=query, + fragment=fragment, + ) diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/util.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/util.py new file mode 100644 index 0000000..35c77e4 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/util.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import typing +from types import TracebackType + + +def to_bytes( + x: str | bytes, encoding: str | None = None, errors: str | None = None +) -> bytes: + if isinstance(x, bytes): + return x + elif not isinstance(x, str): + raise TypeError(f"not expecting type {type(x).__name__}") + if encoding or errors: + return x.encode(encoding or "utf-8", errors=errors or "strict") + return x.encode() + + +def to_str( + x: str | bytes, encoding: str | None = None, errors: str | None = None +) -> str: + if isinstance(x, str): + return x + elif not isinstance(x, bytes): + raise TypeError(f"not expecting type {type(x).__name__}") + if encoding or errors: + return x.decode(encoding or "utf-8", errors=errors or "strict") + return x.decode() + + +def reraise( + tp: type[BaseException] | None, + value: BaseException, + tb: TracebackType | None = None, +) -> typing.NoReturn: + try: + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None # type: ignore[assignment] + tb = None diff --git a/.pythonlibs/lib/python3.10/site-packages/urllib3/util/wait.py b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/wait.py new file mode 100644 index 0000000..aeca0c7 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/urllib3/util/wait.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +import select +import socket +from functools import partial + +__all__ = ["wait_for_read", "wait_for_write"] + + +# How should we wait on sockets? +# +# There are two types of APIs you can use for waiting on sockets: the fancy +# modern stateful APIs like epoll/kqueue, and the older stateless APIs like +# select/poll. The stateful APIs are more efficient when you have a lots of +# sockets to keep track of, because you can set them up once and then use them +# lots of times. But we only ever want to wait on a single socket at a time +# and don't want to keep track of state, so the stateless APIs are actually +# more efficient. So we want to use select() or poll(). +# +# Now, how do we choose between select() and poll()? On traditional Unixes, +# select() has a strange calling convention that makes it slow, or fail +# altogether, for high-numbered file descriptors. The point of poll() is to fix +# that, so on Unixes, we prefer poll(). +# +# On Windows, there is no poll() (or at least Python doesn't provide a wrapper +# for it), but that's OK, because on Windows, select() doesn't have this +# strange calling convention; plain select() works fine. +# +# So: on Windows we use select(), and everywhere else we use poll(). We also +# fall back to select() in case poll() is somehow broken or missing. + + +def select_wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + rcheck = [] + wcheck = [] + if read: + rcheck.append(sock) + if write: + wcheck.append(sock) + # When doing a non-blocking connect, most systems signal success by + # marking the socket writable. Windows, though, signals success by marked + # it as "exceptional". We paper over the difference by checking the write + # sockets for both conditions. (The stdlib selectors module does the same + # thing.) + fn = partial(select.select, rcheck, wcheck, wcheck) + rready, wready, xready = fn(timeout) + return bool(rready or wready or xready) + + +def poll_wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + mask = 0 + if read: + mask |= select.POLLIN + if write: + mask |= select.POLLOUT + poll_obj = select.poll() + poll_obj.register(sock, mask) + + # For some reason, poll() takes timeout in milliseconds + def do_poll(t: float | None) -> list[tuple[int, int]]: + if t is not None: + t *= 1000 + return poll_obj.poll(t) + + return bool(do_poll(timeout)) + + +def _have_working_poll() -> bool: + # Apparently some systems have a select.poll that fails as soon as you try + # to use it, either due to strange configuration or broken monkeypatching + # from libraries like eventlet/greenlet. + try: + poll_obj = select.poll() + poll_obj.poll(0) + except (AttributeError, OSError): + return False + else: + return True + + +def wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + # We delay choosing which implementation to use until the first time we're + # called. We could do it at import time, but then we might make the wrong + # decision if someone goes wild with monkeypatching select.poll after + # we're imported. + global wait_for_socket + if _have_working_poll(): + wait_for_socket = poll_wait_for_socket + elif hasattr(select, "select"): + wait_for_socket = select_wait_for_socket + return wait_for_socket(sock, read, write, timeout) + + +def wait_for_read(sock: socket.socket, timeout: float | None = None) -> bool: + """Waits for reading to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, read=True, timeout=timeout) + + +def wait_for_write(sock: socket.socket, timeout: float | None = None) -> bool: + """Waits for writing to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/INSTALLER b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/LICENSE b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/METADATA b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/METADATA new file mode 100644 index 0000000..548567b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/METADATA @@ -0,0 +1,1086 @@ +Metadata-Version: 2.1 +Name: yarl +Version: 1.9.4 +Summary: Yet another URL library +Home-page: https://github.com/aio-libs/yarl +Author: Andrew Svetlov +Author-email: andrew.svetlov@gmail.com +Maintainer: aiohttp team +Maintainer-email: team@aiohttp.org +License: Apache-2.0 +Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org +Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org +Project-URL: CI: GitHub Workflows, https://github.com/aio-libs/yarl/actions?query=branch:master +Project-URL: Code of Conduct, https://github.com/aio-libs/.github/blob/master/CODE_OF_CONDUCT.md +Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/yarl +Project-URL: Docs: Changelog, https://yarl.aio-libs.org/en/latest/changes/ +Project-URL: Docs: RTD, https://yarl.aio-libs.org +Project-URL: GitHub: issues, https://github.com/aio-libs/yarl/issues +Project-URL: GitHub: repo, https://github.com/aio-libs/yarl +Keywords: cython,cext,yarl +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: NOTICE +Requires-Dist: idna >=2.0 +Requires-Dist: multidict >=4.0 +Requires-Dist: typing-extensions >=3.7.4 ; python_version < "3.8" + +yarl +==== + +The module provides handy URL class for URL parsing and changing. + +.. image:: https://github.com/aio-libs/yarl/workflows/CI/badge.svg + :target: https://github.com/aio-libs/yarl/actions?query=workflow%3ACI + :align: right + +.. image:: https://codecov.io/gh/aio-libs/yarl/branch/master/graph/badge.svg + :target: https://codecov.io/gh/aio-libs/yarl + +.. image:: https://badge.fury.io/py/yarl.svg + :target: https://badge.fury.io/py/yarl + + +.. image:: https://readthedocs.org/projects/yarl/badge/?version=latest + :target: https://yarl.aio-libs.org + + +.. image:: https://img.shields.io/pypi/pyversions/yarl.svg + :target: https://pypi.python.org/pypi/yarl + +.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs:matrix.org + :alt: Matrix Room — #aio-libs:matrix.org + +.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat + :target: https://matrix.to/#/%23aio-libs-space:matrix.org + :alt: Matrix Space — #aio-libs-space:matrix.org + +Introduction +------------ + +Url is constructed from ``str``: + +.. code-block:: pycon + + >>> from yarl import URL + >>> url = URL('https://www.python.org/~guido?arg=1#frag') + >>> url + URL('https://www.python.org/~guido?arg=1#frag') + +All url parts: *scheme*, *user*, *password*, *host*, *port*, *path*, +*query* and *fragment* are accessible by properties: + +.. code-block:: pycon + + >>> url.scheme + 'https' + >>> url.host + 'www.python.org' + >>> url.path + '/~guido' + >>> url.query_string + 'arg=1' + >>> url.query + + >>> url.fragment + 'frag' + +All url manipulations produce a new url object: + +.. code-block:: pycon + + >>> url = URL('https://www.python.org') + >>> url / 'foo' / 'bar' + URL('https://www.python.org/foo/bar') + >>> url / 'foo' % {'bar': 'baz'} + URL('https://www.python.org/foo?bar=baz') + +Strings passed to constructor and modification methods are +automatically encoded giving canonical representation as result: + +.. code-block:: pycon + + >>> url = URL('https://www.python.org/шлях') + >>> url + URL('https://www.python.org/%D1%88%D0%BB%D1%8F%D1%85') + +Regular properties are *percent-decoded*, use ``raw_`` versions for +getting *encoded* strings: + +.. code-block:: pycon + + >>> url.path + '/шлях' + + >>> url.raw_path + '/%D1%88%D0%BB%D1%8F%D1%85' + +Human readable representation of URL is available as ``.human_repr()``: + +.. code-block:: pycon + + >>> url.human_repr() + 'https://www.python.org/шлях' + +For full documentation please read https://yarl.aio-libs.org. + + +Installation +------------ + +:: + + $ pip install yarl + +The library is Python 3 only! + +PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install +``yarl`` on another operating system (like *Alpine Linux*, which is not +manylinux-compliant because of the missing glibc and therefore, cannot be +used with our wheels) the the tarball will be used to compile the library from +the source code. It requires a C compiler and and Python headers installed. + +To skip the compilation you must explicitly opt-in by using a PEP 517 +configuration setting ``pure-python``, or setting the ``YARL_NO_EXTENSIONS`` +environment variable to a non-empty value, e.g.: + +.. code-block:: console + + $ pip install yarl --config-settings=pure-python=false + +Please note that the pure-Python (uncompiled) version is much slower. However, +PyPy always uses a pure-Python implementation, and, as such, it is unaffected +by this variable. + +Dependencies +------------ + +YARL requires multidict_ library. + + +API documentation +------------------ + +The documentation is located at https://yarl.aio-libs.org. + + +Why isn't boolean supported by the URL query API? +------------------------------------------------- + +There is no standard for boolean representation of boolean values. + +Some systems prefer ``true``/``false``, others like ``yes``/``no``, ``on``/``off``, +``Y``/``N``, ``1``/``0``, etc. + +``yarl`` cannot make an unambiguous decision on how to serialize ``bool`` values because +it is specific to how the end-user's application is built and would be different for +different apps. The library doesn't accept booleans in the API; a user should convert +bools into strings using own preferred translation protocol. + + +Comparison with other URL libraries +------------------------------------ + +* furl (https://pypi.python.org/pypi/furl) + + The library has rich functionality but the ``furl`` object is mutable. + + I'm afraid to pass this object into foreign code: who knows if the + code will modify my url in a terrible way while I just want to send URL + with handy helpers for accessing URL properties. + + ``furl`` has other non-obvious tricky things but the main objection + is mutability. + +* URLObject (https://pypi.python.org/pypi/URLObject) + + URLObject is immutable, that's pretty good. + + Every URL change generates a new URL object. + + But the library doesn't do any decode/encode transformations leaving the + end user to cope with these gory details. + + +Source code +----------- + +The project is hosted on GitHub_ + +Please file an issue on the `bug tracker +`_ if you have found a bug +or have some suggestion in order to improve the library. + +The library uses `Azure Pipelines `_ for +Continuous Integration. + +Discussion list +--------------- + +*aio-libs* google group: https://groups.google.com/forum/#!forum/aio-libs + +Feel free to post your questions and ideas here. + + +Authors and License +------------------- + +The ``yarl`` package is written by Andrew Svetlov. + +It's *Apache 2* licensed and freely available. + + +.. _GitHub: https://github.com/aio-libs/yarl + +.. _multidict: https://github.com/aio-libs/multidict + +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/#adding-a-news-entry + we named the news folder "changes". + + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +1.9.4 (2023-12-06) +================== + +Bug fixes +--------- + +- Started raising ``TypeError`` when a string value is passed into + ``yarl.URL.build()`` as the ``port`` argument -- by `@commonism `__. + + Previously the empty string as port would create malformed URLs when rendered as string representations. (`#883 `__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- The leading ``--`` has been dropped from the `PEP 517 `__ in-tree build + backend config setting names. ``--pure-python`` is now just ``pure-python`` + -- by `@webknjaz `__. + + The usage now looks as follows: + + .. code-block:: console + + $ python -m build \ + --config-setting=pure-python=true \ + --config-setting=with-cython-tracing=true + + (`#963 `__) + + +Contributor-facing changes +-------------------------- + +- A step-by-step ``Release Guide`` guide has + been added, describing how to release *yarl* -- by `@webknjaz `__. + + This is primarily targeting maintainers. (`#960 `__) +- Coverage collection has been implemented for the Cython modules + -- by `@webknjaz `__. + + It will also be reported to Codecov from any non-release CI jobs. + + To measure coverage in a development environment, *yarl* can be + installed in editable mode, which requires an environment variable + ``YARL_CYTHON_TRACING=1`` to be set: + + .. code-block:: console + + $ YARL_CYTHON_TRACING=1 python -Im pip install -e . + + Editable install produces C-files required for the Cython coverage + plugin to map the measurements back to the PYX-files. (`#961 `__) +- It is now possible to request line tracing in Cython builds using the + ``with-cython-tracing`` `PEP 517 `__ config setting + -- `@webknjaz `__. + + This can be used in CI and development environment to measure coverage + on Cython modules, but is not normally useful to the end-users or + downstream packagers. + + Here's a usage example: + + .. code-block:: console + + $ python -Im pip install . --config-settings=with-cython-tracing=true + + For editable installs, this setting is on by default. Otherwise, it's + off unless requested explicitly. (`#962 `__) + + +1.9.3 (2023-11-20) +================== + +Bug fixes +--------- + +- Stopped dropping trailing slashes in ``yarl.URL.joinpath()`` -- by `@gmacon `__. (`#862 `__, `#866 `__) +- Started accepting string subclasses in ``__truediv__()`` operations (``URL / segment``) -- by `@mjpieters `__. (`#871 `__, `#884 `__) +- Fixed the human representation of URLs with square brackets in usernames and passwords -- by `@mjpieters `__. (`#876 `__, `#882 `__) +- Updated type hints to include ``URL.missing_port()``, ``URL.__bytes__()`` + and the ``encoding`` argument to ``yarl.URL.joinpath()`` + -- by `@mjpieters `__. (`#891 `__) + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Integrated Cython 3 to enable building *yarl* under Python 3.12 -- by `@mjpieters `__. (`#829 `__, `#881 `__) +- Declared modern ``setuptools.build_meta`` as the `PEP 517 `__ build + backend in ``pyproject.toml`` explicitly -- by `@webknjaz `__. (`#886 `__) +- Converted most of the packaging setup into a declarative ``setup.cfg`` + config -- by `@webknjaz `__. (`#890 `__) +- The packaging is replaced from an old-fashioned ``setup.py`` to an + in-tree `PEP 517 `__ build backend -- by `@webknjaz `__. + + Whenever the end-users or downstream packagers need to build ``yarl`` from + source (a Git checkout or an sdist), they may pass a ``config_settings`` + flag ``--pure-python``. If this flag is not set, a C-extension will be built + and included into the distribution. + + Here is how this can be done with ``pip``: + + .. code-block:: console + + $ python -m pip install . --config-settings=--pure-python=false + + This will also work with ``-e | --editable``. + + The same can be achieved via ``pypa/build``: + + .. code-block:: console + + $ python -m build --config-setting=--pure-python=false + + Adding ``-w | --wheel`` can force ``pypa/build`` produce a wheel from source + directly, as opposed to building an ``sdist`` and then building from it. (`#893 `__) + + .. attention:: + + v1.9.3 was the only version using the ``--pure-python`` setting name. + Later versions dropped the ``--`` prefix, making it just ``pure-python``. + +- Declared Python 3.12 supported officially in the distribution package metadata + -- by `@edgarrmondragon `__. (`#942 `__) + + +Contributor-facing changes +-------------------------- + +- A regression test for no-host URLs was added per `#821 `__ + and ``3986`` -- by `@kenballus `__. (`#821 `__, `#822 `__) +- Started testing *yarl* against Python 3.12 in CI -- by `@mjpieters `__. (`#881 `__) +- All Python 3.12 jobs are now marked as required to pass in CI + -- by `@edgarrmondragon `__. (`#942 `__) +- MyST is now integrated in Sphinx -- by `@webknjaz `__. + + This allows the contributors to author new documents in Markdown + when they have difficulties with going straight RST. (`#953 `__) + + +1.9.2 (2023-04-25) +================== + +Bugfixes +-------- + +- Fix regression with ``__truediv__`` and absolute URLs with empty paths causing the raw path to lack the leading ``/``. + (`#854 `_) + + +1.9.1 (2023-04-21) +================== + +Bugfixes +-------- + +- Marked tests that fail on older Python patch releases (< 3.7.10, < 3.8.8 and < 3.9.2) as expected to fail due to missing a security fix for CVE-2021-23336. (`#850 `_) + + +1.9.0 (2023-04-19) +================== + +This release was never published to PyPI, due to issues with the build process. + +Features +-------- + +- Added ``URL.joinpath(*elements)``, to create a new URL appending multiple path elements. (`#704 `_) +- Made ``URL.__truediv__()`` return ``NotImplemented`` if called with an + unsupported type — by `@michaeljpeters `__. + (`#832 `_) + + +Bugfixes +-------- + +- Path normalization for absolute URLs no longer raises a ValueError exception + when ``..`` segments would otherwise go beyond the URL path root. + (`#536 `_) +- Fixed an issue with update_query() not getting rid of the query when argument is None. (`#792 `_) +- Added some input restrictions on with_port() function to prevent invalid boolean inputs or out of valid port inputs; handled incorrect 0 port representation. (`#793 `_) +- Made ``yarl.URL.build()`` raise a ``TypeError`` if the ``host`` argument is ``None`` — by `@paulpapacz `__. (`#808 `_) +- Fixed an issue with ``update_query()`` getting rid of the query when the argument + is empty but not ``None``. (`#845 `_) + + +Misc +---- + +- `#220 `_ + + +1.8.2 (2022-12-03) +================== + +This is the first release that started shipping wheels for Python 3.11. + + +1.8.1 (2022-08-01) +================== + +Misc +---- + +- `#694 `_, `#699 `_, `#700 `_, `#701 `_, `#702 `_, `#703 `_, `#739 `_ + + +1.8.0 (2022-08-01) +================== + +Features +-------- + +- Added ``URL.raw_suffix``, ``URL.suffix``, ``URL.raw_suffixes``, ``URL.suffixes``, ``URL.with_suffix``. (`#613 `_) + + +Improved Documentation +---------------------- + +- Fixed broken internal references to ``yarl.URL.human_repr()``. + (`#665 `_) +- Fixed broken external references to ``multidict:index`` docs. (`#665 `_) + + +Deprecations and Removals +------------------------- + +- Dropped Python 3.6 support. (`#672 `_) + + +Misc +---- + +- `#646 `_, `#699 `_, `#701 `_ + + +1.7.2 (2021-11-01) +================== + +Bugfixes +-------- + +- Changed call in ``with_port()`` to stop reencoding parts of the URL that were already encoded. (`#623 `_) + + +1.7.1 (2021-10-07) +================== + +Bugfixes +-------- + +- Fix 1.7.0 build error + +1.7.0 (2021-10-06) +================== + +Features +-------- + +- Add ``__bytes__()`` magic method so that ``bytes(url)`` will work and use optimal ASCII encoding. + (`#582 `_) +- Started shipping platform-specific arm64 wheels for Apple Silicon. (`#622 `_) +- Started shipping platform-specific wheels with the ``musl`` tag targeting typical Alpine Linux runtimes. (`#622 `_) +- Added support for Python 3.10. (`#622 `_) + + +1.6.3 (2020-11-14) +================== + +Bugfixes +-------- + +- No longer loose characters when decoding incorrect percent-sequences (like ``%e2%82%f8``). All non-decodable percent-sequences are now preserved. + `#517 `_ +- Provide x86 Windows wheels. + `#535 `_ + + +---- + + +1.6.2 (2020-10-12) +================== + + +Bugfixes +-------- + +- Provide generated ``.c`` files in TarBall distribution. + `#530 `_ + +1.6.1 (2020-10-12) +================== + +Features +-------- + +- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on + Linux as well as ``x86_64``. + `#507 `_ +- Provide wheels for Python 3.9. + `#526 `_ + +Bugfixes +-------- + +- ``human_repr()`` now always produces valid representation equivalent to the original URL (if the original URL is valid). + `#511 `_ +- Fixed requoting a single percent followed by a percent-encoded character in the Cython implementation. + `#514 `_ +- Fix ValueError when decoding ``%`` which is not followed by two hexadecimal digits. + `#516 `_ +- Fix decoding ``%`` followed by a space and hexadecimal digit. + `#520 `_ +- Fix annotation of ``with_query()``/``update_query()`` methods for ``key=[val1, val2]`` case. + `#528 `_ + +Removal +------- + +- Drop Python 3.5 support; Python 3.6 is the minimal supported Python version. + + +---- + + +1.6.0 (2020-09-23) +================== + +Features +-------- + +- Allow for int and float subclasses in query, while still denying bool. + `#492 `_ + + +Bugfixes +-------- + +- Do not requote arguments in ``URL.build()``, ``with_xxx()`` and in ``/`` operator. + `#502 `_ +- Keep IPv6 brackets in ``origin()``. + `#504 `_ + + +---- + + +1.5.1 (2020-08-01) +================== + +Bugfixes +-------- + +- Fix including relocated internal ``yarl._quoting_c`` C-extension into published PyPI dists. + `#485 `_ + + +Misc +---- + +- `#484 `_ + + +---- + + +1.5.0 (2020-07-26) +================== + +Features +-------- + +- Convert host to lowercase on URL building. + `#386 `_ +- Allow using ``mod`` operator (``%``) for updating query string (an alias for ``update_query()`` method). + `#435 `_ +- Allow use of sequences such as ``list`` and ``tuple`` in the values + of a mapping such as ``dict`` to represent that a key has many values:: + + url = URL("http://example.com") + assert url.with_query({"a": [1, 2]}) == URL("http://example.com/?a=1&a=2") + + `#443 `_ +- Support ``URL.build()`` with scheme and path (creates a relative URL). + `#464 `_ +- Cache slow IDNA encode/decode calls. + `#476 `_ +- Add ``@final`` / ``Final`` type hints + `#477 `_ +- Support URL authority/raw_authority properties and authority argument of ``URL.build()`` method. + `#478 `_ +- Hide the library implementation details, make the exposed public list very clean. + `#483 `_ + + +Bugfixes +-------- + +- Fix tests with newer Python (3.7.6, 3.8.1 and 3.9.0+). + `#409 `_ +- Fix a bug where query component, passed in a form of mapping or sequence, is unquoted in unexpected way. + `#426 `_ +- Hide ``Query`` and ``QueryVariable`` type aliases in ``__init__.pyi``, now they are prefixed with underscore. + `#431 `_ +- Keep IPv6 brackets after updating port/user/password. + `#451 `_ + + +---- + + +1.4.2 (2019-12-05) +================== + +Features +-------- + +- Workaround for missing ``str.isascii()`` in Python 3.6 + `#389 `_ + + +---- + + +1.4.1 (2019-11-29) +================== + +* Fix regression, make the library work on Python 3.5 and 3.6 again. + +1.4.0 (2019-11-29) +================== + +* Distinguish an empty password in URL from a password not provided at all (#262) + +* Fixed annotations for optional parameters of ``URL.build`` (#309) + +* Use None as default value of ``user`` parameter of ``URL.build`` (#309) + +* Enforce building C Accelerated modules when installing from source tarball, use + ``YARL_NO_EXTENSIONS`` environment variable for falling back to (slower) Pure Python + implementation (#329) + +* Drop Python 3.5 support + +* Fix quoting of plus in path by pure python version (#339) + +* Don't create a new URL if fragment is unchanged (#292) + +* Included in error message the path that produces starting slash forbidden error (#376) + +* Skip slow IDNA encoding for ASCII-only strings (#387) + + +1.3.0 (2018-12-11) +================== + +* Fix annotations for ``query`` parameter (#207) + +* An incoming query sequence can have int variables (the same as for + Mapping type) (#208) + +* Add ``URL.explicit_port`` property (#218) + +* Give a friendlier error when port can't be converted to int (#168) + +* ``bool(URL())`` now returns ``False`` (#272) + +1.2.6 (2018-06-14) +================== + +* Drop Python 3.4 trove classifier (#205) + +1.2.5 (2018-05-23) +================== + +* Fix annotations for ``build`` (#199) + +1.2.4 (2018-05-08) +================== + +* Fix annotations for ``cached_property`` (#195) + +1.2.3 (2018-05-03) +================== + +* Accept ``str`` subclasses in ``URL`` constructor (#190) + +1.2.2 (2018-05-01) +================== + +* Fix build + +1.2.1 (2018-04-30) +================== + +* Pin minimal required Python to 3.5.3 (#189) + +1.2.0 (2018-04-30) +================== + +* Forbid inheritance, replace ``__init__`` with ``__new__`` (#171) + +* Support PEP-561 (provide type hinting marker) (#182) + +1.1.1 (2018-02-17) +================== + +* Fix performance regression: don't encode empty ``netloc`` (#170) + +1.1.0 (2018-01-21) +================== + +* Make pure Python quoter consistent with Cython version (#162) + +1.0.0 (2018-01-15) +================== + +* Use fast path if quoted string does not need requoting (#154) + +* Speed up quoting/unquoting by ``_Quoter`` and ``_Unquoter`` classes (#155) + +* Drop ``yarl.quote`` and ``yarl.unquote`` public functions (#155) + +* Add custom string writer, reuse static buffer if available (#157) + Code is 50-80 times faster than Pure Python version (was 4-5 times faster) + +* Don't recode IP zone (#144) + +* Support ``encoded=True`` in ``yarl.URL.build()`` (#158) + +* Fix updating query with multiple keys (#160) + +0.18.0 (2018-01-10) +=================== + +* Fallback to IDNA 2003 if domain name is not IDNA 2008 compatible (#152) + +0.17.0 (2017-12-30) +=================== + +* Use IDNA 2008 for domain name processing (#149) + +0.16.0 (2017-12-07) +=================== + +* Fix raising ``TypeError`` by ``url.query_string()`` after + ``url.with_query({})`` (empty mapping) (#141) + +0.15.0 (2017-11-23) +=================== + +* Add ``raw_path_qs`` attribute (#137) + +0.14.2 (2017-11-14) +=================== + +* Restore ``strict`` parameter as no-op in ``quote`` / ``unquote`` + +0.14.1 (2017-11-13) +=================== + +* Restore ``strict`` parameter as no-op for sake of compatibility with + aiohttp 2.2 + +0.14.0 (2017-11-11) +=================== + +* Drop strict mode (#123) + +* Fix ``"ValueError: Unallowed PCT %"`` when there's a ``"%"`` in the URL (#124) + +0.13.0 (2017-10-01) +=================== + +* Document ``encoded`` parameter (#102) + +* Support relative URLs like ``'?key=value'`` (#100) + +* Unsafe encoding for QS fixed. Encode ``;`` character in value parameter (#104) + +* Process passwords without user names (#95) + +0.12.0 (2017-06-26) +=================== + +* Properly support paths without leading slash in ``URL.with_path()`` (#90) + +* Enable type annotation checks + +0.11.0 (2017-06-26) +=================== + +* Normalize path (#86) + +* Clear query and fragment parts in ``.with_path()`` (#85) + +0.10.3 (2017-06-13) +=================== + +* Prevent double URL arguments unquoting (#83) + +0.10.2 (2017-05-05) +=================== + +* Unexpected hash behavior (#75) + + +0.10.1 (2017-05-03) +=================== + +* Unexpected compare behavior (#73) + +* Do not quote or unquote + if not a query string. (#74) + + +0.10.0 (2017-03-14) +=================== + +* Added ``URL.build`` class method (#58) + +* Added ``path_qs`` attribute (#42) + + +0.9.8 (2017-02-16) +================== + +* Do not quote ``:`` in path + + +0.9.7 (2017-02-16) +================== + +* Load from pickle without _cache (#56) + +* Percent-encoded pluses in path variables become spaces (#59) + + +0.9.6 (2017-02-15) +================== + +* Revert backward incompatible change (BaseURL) + + +0.9.5 (2017-02-14) +================== + +* Fix BaseURL rich comparison support + + +0.9.4 (2017-02-14) +================== + +* Use BaseURL + + +0.9.3 (2017-02-14) +================== + +* Added BaseURL + + +0.9.2 (2017-02-08) +================== + +* Remove debug print + + +0.9.1 (2017-02-07) +================== + +* Do not lose tail chars (#45) + + +0.9.0 (2017-02-07) +================== + +* Allow to quote ``%`` in non strict mode (#21) + +* Incorrect parsing of query parameters with %3B (;) inside (#34) + +* Fix core dumps (#41) + +* ``tmpbuf`` - compiling error (#43) + +* Added ``URL.update_path()`` method + +* Added ``URL.update_query()`` method (#47) + + +0.8.1 (2016-12-03) +================== + +* Fix broken aiohttp: revert back ``quote`` / ``unquote``. + + +0.8.0 (2016-12-03) +================== + +* Support more verbose error messages in ``.with_query()`` (#24) + +* Don't percent-encode ``@`` and ``:`` in path (#32) + +* Don't expose ``yarl.quote`` and ``yarl.unquote``, these functions are + part of private API + +0.7.1 (2016-11-18) +================== + +* Accept not only ``str`` but all classes inherited from ``str`` also (#25) + +0.7.0 (2016-11-07) +================== + +* Accept ``int`` as value for ``.with_query()`` + +0.6.0 (2016-11-07) +================== + +* Explicitly use UTF8 encoding in ``setup.py`` (#20) +* Properly unquote non-UTF8 strings (#19) + +0.5.3 (2016-11-02) +================== + +* Don't use ``typing.NamedTuple`` fields but indexes on URL construction + +0.5.2 (2016-11-02) +================== + +* Inline ``_encode`` class method + +0.5.1 (2016-11-02) +================== + +* Make URL construction faster by removing extra classmethod calls + +0.5.0 (2016-11-02) +================== + +* Add Cython optimization for quoting/unquoting +* Provide binary wheels + +0.4.3 (2016-09-29) +================== + +* Fix typing stubs + +0.4.2 (2016-09-29) +================== + +* Expose ``quote()`` and ``unquote()`` as public API + +0.4.1 (2016-09-28) +================== + +* Support empty values in query (``'/path?arg'``) + +0.4.0 (2016-09-27) +================== + +* Introduce ``relative()`` (#16) + +0.3.2 (2016-09-27) +================== + +* Typo fixes #15 + +0.3.1 (2016-09-26) +================== + +* Support sequence of pairs as ``with_query()`` parameter + +0.3.0 (2016-09-26) +================== + +* Introduce ``is_default_port()`` + +0.2.1 (2016-09-26) +================== + +* Raise ValueError for URLs like 'http://:8080/' + +0.2.0 (2016-09-18) +================== + +* Avoid doubling slashes when joining paths (#13) + +* Appending path starting from slash is forbidden (#12) + +0.1.4 (2016-09-09) +================== + +* Add ``kwargs`` support for ``with_query()`` (#10) + +0.1.3 (2016-09-07) +================== + +* Document ``with_query()``, ``with_fragment()`` and ``origin()`` + +* Allow ``None`` for ``with_query()`` and ``with_fragment()`` + +0.1.2 (2016-09-07) +================== + +* Fix links, tune docs theme. + +0.1.1 (2016-09-06) +================== + +* Update README, old version used obsolete API + +0.1.0 (2016-09-06) +================== + +* The library was deeply refactored, bytes are gone away but all + accepted strings are encoded if needed. + +0.0.1 (2016-08-30) +================== + +* The first release. diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/NOTICE b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/NOTICE new file mode 100644 index 0000000..fa53b2b --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/NOTICE @@ -0,0 +1,13 @@ + Copyright 2016-2021, Andrew Svetlov and aio-libs team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/RECORD b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/RECORD new file mode 100644 index 0000000..92f22af --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/RECORD @@ -0,0 +1,20 @@ +yarl-1.9.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +yarl-1.9.4.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +yarl-1.9.4.dist-info/METADATA,sha256=8GdiRi2e9wgkaigZcXSD1UOFAfj8q39fZlsNFlEyRQY,31848 +yarl-1.9.4.dist-info/NOTICE,sha256=VtasbIEFwKUTBMIdsGDjYa-ajqCvmnXCOcKLXRNpODg,609 +yarl-1.9.4.dist-info/RECORD,, +yarl-1.9.4.dist-info/WHEEL,sha256=1FEjxEYgybphwh9S0FO9IcZ0B-NIeM2ko8OzhFZeOeQ,152 +yarl-1.9.4.dist-info/top_level.txt,sha256=vf3SJuQh-k7YtvsUrV_OPOrT9Kqn0COlk7IPYyhtGkQ,5 +yarl/__init__.py,sha256=1UvmVvUEXYJa-vxxO1hn6PRhiUdtedOaH457UamY3qE,154 +yarl/__init__.pyi,sha256=7YGQVdTkGeYMt1ICjIGITqnKWCgexbZiTkR0HYY6c_o,4028 +yarl/__pycache__/__init__.cpython-310.pyc,, +yarl/__pycache__/_quoting.cpython-310.pyc,, +yarl/__pycache__/_quoting_py.cpython-310.pyc,, +yarl/__pycache__/_url.cpython-310.pyc,, +yarl/_quoting.py,sha256=VwNdxLvb8CjZUjSEfWOOnWx7jPQdr8oXT_QWa_bhcvs,537 +yarl/_quoting_c.cpython-310-x86_64-linux-gnu.so,sha256=_t8uVWs1BJu1CGO-HeP70m_FRJiiJ5nKyTNuZHeolEU,904208 +yarl/_quoting_c.pyi,sha256=plPeUkIbXp4Hzi0AbYII4JA0H9tCjtjw-UUPU5Na1s0,447 +yarl/_quoting_c.pyx,sha256=L1-WLIZp0e_khrXD0V79CnZkETRxuzPOw1hD0IZDWlQ,11496 +yarl/_quoting_py.py,sha256=K419wlfyIlGM7-UbxobWv8lOkger8Ut7rrgoJ4xDgAU,6370 +yarl/_url.py,sha256=JavPyzjmEDeRL0_sNJb-FUHRLphXlnE7MUTdR7BsE0c,38182 +yarl/py.typed,sha256=ay5OMO475PlcZ_Fbun9maHW7Y6MBTk0UXL4ztHx3Iug,14 diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/WHEEL b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/WHEEL new file mode 100644 index 0000000..1d81251 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/top_level.txt b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/top_level.txt new file mode 100644 index 0000000..e93e8bd --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl-1.9.4.dist-info/top_level.txt @@ -0,0 +1 @@ +yarl diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.py b/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.py new file mode 100644 index 0000000..127721a --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.py @@ -0,0 +1,5 @@ +from ._url import URL, cache_clear, cache_configure, cache_info + +__version__ = "1.9.4" + +__all__ = ("URL", "cache_clear", "cache_configure", "cache_info") diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.pyi b/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.pyi new file mode 100644 index 0000000..5fd4bd0 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/__init__.pyi @@ -0,0 +1,121 @@ +import sys +from functools import _CacheInfo +from typing import Any, Mapping, Optional, Sequence, Tuple, Type, Union, overload + +import multidict + +if sys.version_info >= (3, 8): + from typing import Final, TypedDict, final +else: + from typing_extensions import Final, TypedDict, final + +_SimpleQuery = Union[str, int, float] +_QueryVariable = Union[_SimpleQuery, Sequence[_SimpleQuery]] +_Query = Union[ + None, str, Mapping[str, _QueryVariable], Sequence[Tuple[str, _QueryVariable]] +] + +@final +class URL: + scheme: Final[str] + raw_user: Final[str] + user: Final[Optional[str]] + raw_password: Final[Optional[str]] + password: Final[Optional[str]] + raw_host: Final[Optional[str]] + host: Final[Optional[str]] + port: Final[Optional[int]] + explicit_port: Final[Optional[int]] + raw_authority: Final[str] + authority: Final[str] + raw_path: Final[str] + path: Final[str] + raw_query_string: Final[str] + query_string: Final[str] + path_qs: Final[str] + raw_path_qs: Final[str] + raw_fragment: Final[str] + fragment: Final[str] + query: Final[multidict.MultiDict[str]] + raw_name: Final[str] + name: Final[str] + raw_suffix: Final[str] + suffix: Final[str] + raw_suffixes: Final[Tuple[str, ...]] + suffixes: Final[Tuple[str, ...]] + raw_parts: Final[Tuple[str, ...]] + parts: Final[Tuple[str, ...]] + parent: Final[URL] + def __init__( + self, val: Union[str, "URL"] = ..., *, encoded: bool = ... + ) -> None: ... + @classmethod + def build( + cls, + *, + scheme: str = ..., + authority: str = ..., + user: Optional[str] = ..., + password: Optional[str] = ..., + host: str = ..., + port: Optional[int] = ..., + path: str = ..., + query: Optional[_Query] = ..., + query_string: str = ..., + fragment: str = ..., + encoded: bool = ... + ) -> URL: ... + def __str__(self) -> str: ... + def __repr__(self) -> str: ... + def __bytes__(self) -> bytes: ... + def __eq__(self, other: Any) -> bool: ... + def __le__(self, other: Any) -> bool: ... + def __lt__(self, other: Any) -> bool: ... + def __ge__(self, other: Any) -> bool: ... + def __gt__(self, other: Any) -> bool: ... + def __hash__(self) -> int: ... + def __truediv__(self, name: str) -> URL: ... + def __mod__(self, query: _Query) -> URL: ... + def is_absolute(self) -> bool: ... + def is_default_port(self) -> bool: ... + def origin(self) -> URL: ... + def relative(self) -> URL: ... + def with_scheme(self, scheme: str) -> URL: ... + def with_user(self, user: Optional[str]) -> URL: ... + def with_password(self, password: Optional[str]) -> URL: ... + def with_host(self, host: str) -> URL: ... + def with_port(self, port: Optional[int]) -> URL: ... + def with_path(self, path: str, *, encoded: bool = ...) -> URL: ... + @overload + def with_query(self, query: _Query) -> URL: ... + @overload + def with_query(self, **kwargs: _QueryVariable) -> URL: ... + @overload + def update_query(self, query: _Query) -> URL: ... + @overload + def update_query(self, **kwargs: _QueryVariable) -> URL: ... + def with_fragment(self, fragment: Optional[str]) -> URL: ... + def with_name(self, name: str) -> URL: ... + def with_suffix(self, suffix: str) -> URL: ... + def join(self, url: URL) -> URL: ... + def joinpath(self, *url: str, encoded: bool = ...) -> URL: ... + def human_repr(self) -> str: ... + # private API + @classmethod + def _normalize_path(cls, path: str) -> str: ... + +@final +class cached_property: + def __init__(self, wrapped: Any) -> None: ... + def __get__(self, inst: URL, owner: Type[URL]) -> Any: ... + def __set__(self, inst: URL, value: Any) -> None: ... + +class CacheInfo(TypedDict): + idna_encode: _CacheInfo + idna_decode: _CacheInfo + +def cache_clear() -> None: ... +def cache_info() -> CacheInfo: ... +def cache_configure( + *, idna_encode_size: Optional[int] = ..., idna_decode_size: Optional[int] = ... +) -> None: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/__init__.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d7d4ebf Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/__init__.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting.cpython-310.pyc new file mode 100644 index 0000000..a29990a Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting_py.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting_py.cpython-310.pyc new file mode 100644 index 0000000..eab19ba Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_quoting_py.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_url.cpython-310.pyc b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_url.cpython-310.pyc new file mode 100644 index 0000000..2151a7c Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/yarl/__pycache__/_url.cpython-310.pyc differ diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting.py b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting.py new file mode 100644 index 0000000..8d1c705 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting.py @@ -0,0 +1,18 @@ +import os +import sys + +__all__ = ("_Quoter", "_Unquoter") + + +NO_EXTENSIONS = bool(os.environ.get("YARL_NO_EXTENSIONS")) # type: bool +if sys.implementation.name != "cpython": + NO_EXTENSIONS = True + + +if not NO_EXTENSIONS: # pragma: no branch + try: + from ._quoting_c import _Quoter, _Unquoter # type: ignore[assignment] + except ImportError: # pragma: no cover + from ._quoting_py import _Quoter, _Unquoter # type: ignore[assignment] +else: + from ._quoting_py import _Quoter, _Unquoter # type: ignore[assignment] diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.cpython-310-x86_64-linux-gnu.so b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.cpython-310-x86_64-linux-gnu.so new file mode 100644 index 0000000..69e72a8 Binary files /dev/null and b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.cpython-310-x86_64-linux-gnu.so differ diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyi b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyi new file mode 100644 index 0000000..1c8fc24 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyi @@ -0,0 +1,16 @@ +from typing import Optional + +class _Quoter: + def __init__( + self, + *, + safe: str = ..., + protected: str = ..., + qs: bool = ..., + requote: bool = ... + ) -> None: ... + def __call__(self, val: Optional[str] = ...) -> Optional[str]: ... + +class _Unquoter: + def __init__(self, *, unsafe: str = ..., qs: bool = ...) -> None: ... + def __call__(self, val: Optional[str] = ...) -> Optional[str]: ... diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyx b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyx new file mode 100644 index 0000000..96f69c1 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_c.pyx @@ -0,0 +1,371 @@ +# cython: language_level=3 + +from cpython.exc cimport PyErr_NoMemory +from cpython.mem cimport PyMem_Free, PyMem_Malloc, PyMem_Realloc +from cpython.unicode cimport PyUnicode_DecodeASCII, PyUnicode_DecodeUTF8Stateful +from libc.stdint cimport uint8_t, uint64_t +from libc.string cimport memcpy, memset + +from string import ascii_letters, digits + + +cdef str GEN_DELIMS = ":/?#[]@" +cdef str SUB_DELIMS_WITHOUT_QS = "!$'()*," +cdef str SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + '+?=;' +cdef str RESERVED = GEN_DELIMS + SUB_DELIMS +cdef str UNRESERVED = ascii_letters + digits + '-._~' +cdef str ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS +cdef str QS = '+&=;' + +DEF BUF_SIZE = 8 * 1024 # 8KiB +cdef char BUFFER[BUF_SIZE] + +cdef inline Py_UCS4 _to_hex(uint8_t v): + if v < 10: + return (v+0x30) # ord('0') == 0x30 + else: + return (v+0x41-10) # ord('A') == 0x41 + + +cdef inline int _from_hex(Py_UCS4 v): + if '0' <= v <= '9': + return (v) - 0x30 # ord('0') == 0x30 + elif 'A' <= v <= 'F': + return (v) - 0x41 + 10 # ord('A') == 0x41 + elif 'a' <= v <= 'f': + return (v) - 0x61 + 10 # ord('a') == 0x61 + else: + return -1 + + +cdef inline int _is_lower_hex(Py_UCS4 v): + return 'a' <= v <= 'f' + + +cdef inline Py_UCS4 _restore_ch(Py_UCS4 d1, Py_UCS4 d2): + cdef int digit1 = _from_hex(d1) + if digit1 < 0: + return -1 + cdef int digit2 = _from_hex(d2) + if digit2 < 0: + return -1 + return (digit1 << 4 | digit2) + + +cdef uint8_t ALLOWED_TABLE[16] +cdef uint8_t ALLOWED_NOTQS_TABLE[16] + + +cdef inline bint bit_at(uint8_t array[], uint64_t ch): + return array[ch >> 3] & (1 << (ch & 7)) + + +cdef inline void set_bit(uint8_t array[], uint64_t ch): + array[ch >> 3] |= (1 << (ch & 7)) + + +memset(ALLOWED_TABLE, 0, sizeof(ALLOWED_TABLE)) +memset(ALLOWED_NOTQS_TABLE, 0, sizeof(ALLOWED_NOTQS_TABLE)) + +for i in range(128): + if chr(i) in ALLOWED: + set_bit(ALLOWED_TABLE, i) + set_bit(ALLOWED_NOTQS_TABLE, i) + if chr(i) in QS: + set_bit(ALLOWED_NOTQS_TABLE, i) + +# ----------------- writer --------------------------- + +cdef struct Writer: + char *buf + Py_ssize_t size + Py_ssize_t pos + bint changed + + +cdef inline void _init_writer(Writer* writer): + writer.buf = &BUFFER[0] + writer.size = BUF_SIZE + writer.pos = 0 + writer.changed = 0 + + +cdef inline void _release_writer(Writer* writer): + if writer.buf != BUFFER: + PyMem_Free(writer.buf) + + +cdef inline int _write_char(Writer* writer, Py_UCS4 ch, bint changed): + cdef char * buf + cdef Py_ssize_t size + + if writer.pos == writer.size: + # reallocate + size = writer.size + BUF_SIZE + if writer.buf == BUFFER: + buf = PyMem_Malloc(size) + if buf == NULL: + PyErr_NoMemory() + return -1 + memcpy(buf, writer.buf, writer.size) + else: + buf = PyMem_Realloc(writer.buf, size) + if buf == NULL: + PyErr_NoMemory() + return -1 + writer.buf = buf + writer.size = size + writer.buf[writer.pos] = ch + writer.pos += 1 + writer.changed |= changed + return 0 + + +cdef inline int _write_pct(Writer* writer, uint8_t ch, bint changed): + if _write_char(writer, '%', changed) < 0: + return -1 + if _write_char(writer, _to_hex(ch >> 4), changed) < 0: + return -1 + return _write_char(writer, _to_hex(ch & 0x0f), changed) + + +cdef inline int _write_utf8(Writer* writer, Py_UCS4 symbol): + cdef uint64_t utf = symbol + + if utf < 0x80: + return _write_pct(writer, utf, True) + elif utf < 0x800: + if _write_pct(writer, (0xc0 | (utf >> 6)), True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif 0xD800 <= utf <= 0xDFFF: + # surogate pair, ignored + return 0 + elif utf < 0x10000: + if _write_pct(writer, (0xe0 | (utf >> 12)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + elif utf > 0x10FFFF: + # symbol is too large + return 0 + else: + if _write_pct(writer, (0xf0 | (utf >> 18)), True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 12) & 0x3f)), + True) < 0: + return -1 + if _write_pct(writer, (0x80 | ((utf >> 6) & 0x3f)), + True) < 0: + return -1 + return _write_pct(writer, (0x80 | (utf & 0x3f)), True) + + +# --------------------- end writer -------------------------- + + +cdef class _Quoter: + cdef bint _qs + cdef bint _requote + + cdef uint8_t _safe_table[16] + cdef uint8_t _protected_table[16] + + def __init__( + self, *, str safe='', str protected='', bint qs=False, bint requote=True, + ): + cdef Py_UCS4 ch + + self._qs = qs + self._requote = requote + + if not self._qs: + memcpy(self._safe_table, + ALLOWED_NOTQS_TABLE, + sizeof(self._safe_table)) + else: + memcpy(self._safe_table, + ALLOWED_TABLE, + sizeof(self._safe_table)) + for ch in safe: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + + memset(self._protected_table, 0, sizeof(self._protected_table)) + for ch in protected: + if ord(ch) > 127: + raise ValueError("Only safe symbols with ORD < 128 are allowed") + set_bit(self._safe_table, ch) + set_bit(self._protected_table, ch) + + def __call__(self, val): + cdef Writer writer + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + _init_writer(&writer) + try: + return self._do_quote(val, &writer) + finally: + _release_writer(&writer) + + cdef str _do_quote(self, str val, Writer *writer): + cdef Py_UCS4 ch + cdef int changed + cdef int idx = 0 + cdef int length = len(val) + + while idx < length: + ch = val[idx] + idx += 1 + if ch == '%' and self._requote and idx <= length - 2: + ch = _restore_ch(val[idx], val[idx + 1]) + if ch != -1: + idx += 2 + if ch < 128: + if bit_at(self._protected_table, ch): + if _write_pct(writer, ch, True) < 0: + raise + continue + + if bit_at(self._safe_table, ch): + if _write_char(writer, ch, True) < 0: + raise + continue + + changed = (_is_lower_hex(val[idx - 2]) or + _is_lower_hex(val[idx - 1])) + if _write_pct(writer, ch, changed) < 0: + raise + continue + else: + ch = '%' + + if self._write(writer, ch) < 0: + raise + + if not writer.changed: + return val + else: + return PyUnicode_DecodeASCII(writer.buf, writer.pos, "strict") + + cdef inline int _write(self, Writer *writer, Py_UCS4 ch): + if self._qs: + if ch == ' ': + return _write_char(writer, '+', True) + + if ch < 128 and bit_at(self._safe_table, ch): + return _write_char(writer, ch, False) + + return _write_utf8(writer, ch) + + +cdef class _Unquoter: + cdef str _unsafe + cdef bint _qs + cdef _Quoter _quoter + cdef _Quoter _qs_quoter + + def __init__(self, *, unsafe='', qs=False): + self._unsafe = unsafe + self._qs = qs + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + def __call__(self, val): + if val is None: + return None + if type(val) is not str: + if isinstance(val, str): + # derived from str + val = str(val) + else: + raise TypeError("Argument should be str") + return self._do_unquote(val) + + cdef str _do_unquote(self, str val): + if len(val) == 0: + return val + cdef list ret = [] + cdef char buffer[4] + cdef Py_ssize_t buflen = 0 + cdef Py_ssize_t consumed + cdef str unquoted + cdef Py_UCS4 ch = 0 + cdef Py_ssize_t idx = 0 + cdef Py_ssize_t length = len(val) + cdef Py_ssize_t start_pct + + while idx < length: + ch = val[idx] + idx += 1 + if ch == '%' and idx <= length - 2: + ch = _restore_ch(val[idx], val[idx + 1]) + if ch != -1: + idx += 2 + assert buflen < 4 + buffer[buflen] = ch + buflen += 1 + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + start_pct = idx - buflen * 3 + buffer[0] = ch + buflen = 1 + ret.append(val[start_pct : idx - 3]) + try: + unquoted = PyUnicode_DecodeUTF8Stateful(buffer, buflen, + NULL, &consumed) + except UnicodeDecodeError: + buflen = 0 + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + assert consumed == 0 + continue + assert consumed == buflen + buflen = 0 + if self._qs and unquoted in '+=&;': + ret.append(self._qs_quoter(unquoted)) + elif unquoted in self._unsafe: + ret.append(self._quoter(unquoted)) + else: + ret.append(unquoted) + continue + else: + ch = '%' + + if buflen: + start_pct = idx - 1 - buflen * 3 + ret.append(val[start_pct : idx - 1]) + buflen = 0 + + if ch == '+': + if not self._qs or ch in self._unsafe: + ret.append('+') + else: + ret.append(' ') + continue + + if ch in self._unsafe: + ret.append('%') + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if buflen: + ret.append(val[length - buflen * 3 : length]) + + return ''.join(ret) diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_py.py b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_py.py new file mode 100644 index 0000000..585a1da --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/_quoting_py.py @@ -0,0 +1,197 @@ +import codecs +import re +from string import ascii_letters, ascii_lowercase, digits +from typing import Optional, cast + +BASCII_LOWERCASE = ascii_lowercase.encode("ascii") +BPCT_ALLOWED = {f"%{i:02X}".encode("ascii") for i in range(256)} +GEN_DELIMS = ":/?#[]@" +SUB_DELIMS_WITHOUT_QS = "!$'()*," +SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + "+&=;" +RESERVED = GEN_DELIMS + SUB_DELIMS +UNRESERVED = ascii_letters + digits + "-._~" +ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS + + +_IS_HEX = re.compile(b"[A-Z0-9][A-Z0-9]") +_IS_HEX_STR = re.compile("[A-Fa-f0-9][A-Fa-f0-9]") + +utf8_decoder = codecs.getincrementaldecoder("utf-8") + + +class _Quoter: + def __init__( + self, + *, + safe: str = "", + protected: str = "", + qs: bool = False, + requote: bool = True, + ) -> None: + self._safe = safe + self._protected = protected + self._qs = qs + self._requote = requote + + def __call__(self, val: Optional[str]) -> Optional[str]: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + bval = cast(str, val).encode("utf8", errors="ignore") + ret = bytearray() + pct = bytearray() + safe = self._safe + safe += ALLOWED + if not self._qs: + safe += "+&=;" + safe += self._protected + bsafe = safe.encode("ascii") + idx = 0 + while idx < len(bval): + ch = bval[idx] + idx += 1 + + if pct: + if ch in BASCII_LOWERCASE: + ch = ch - 32 # convert to uppercase + pct.append(ch) + if len(pct) == 3: # pragma: no branch # peephole optimizer + buf = pct[1:] + if not _IS_HEX.match(buf): + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + try: + unquoted = chr(int(pct[1:].decode("ascii"), base=16)) + except ValueError: + ret.extend(b"%25") + pct.clear() + idx -= 2 + continue + + if unquoted in self._protected: + ret.extend(pct) + elif unquoted in safe: + ret.append(ord(unquoted)) + else: + ret.extend(pct) + pct.clear() + + # special case, if we have only one char after "%" + elif len(pct) == 2 and idx == len(bval): + ret.extend(b"%25") + pct.clear() + idx -= 1 + + continue + + elif ch == ord("%") and self._requote: + pct.clear() + pct.append(ch) + + # special case if "%" is last char + if idx == len(bval): + ret.extend(b"%25") + + continue + + if self._qs: + if ch == ord(" "): + ret.append(ord("+")) + continue + if ch in bsafe: + ret.append(ch) + continue + + ret.extend((f"%{ch:02X}").encode("ascii")) + + ret2 = ret.decode("ascii") + if ret2 == val: + return val + return ret2 + + +class _Unquoter: + def __init__(self, *, unsafe: str = "", qs: bool = False) -> None: + self._unsafe = unsafe + self._qs = qs + self._quoter = _Quoter() + self._qs_quoter = _Quoter(qs=True) + + def __call__(self, val: Optional[str]) -> Optional[str]: + if val is None: + return None + if not isinstance(val, str): + raise TypeError("Argument should be str") + if not val: + return "" + decoder = cast(codecs.BufferedIncrementalDecoder, utf8_decoder()) + ret = [] + idx = 0 + while idx < len(val): + ch = val[idx] + idx += 1 + if ch == "%" and idx <= len(val) - 2: + pct = val[idx : idx + 2] + if _IS_HEX_STR.fullmatch(pct): + b = bytes([int(pct, base=16)]) + idx += 2 + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + start_pct = idx - 3 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 3]) + decoder.reset() + try: + unquoted = decoder.decode(b) + except UnicodeDecodeError: + ret.append(val[idx - 3 : idx]) + continue + if not unquoted: + continue + if self._qs and unquoted in "+=&;": + to_add = self._qs_quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + elif unquoted in self._unsafe: + to_add = self._quoter(unquoted) + if to_add is None: # pragma: no cover + raise RuntimeError("Cannot quote None") + ret.append(to_add) + else: + ret.append(unquoted) + continue + + if decoder.buffer: + start_pct = idx - 1 - len(decoder.buffer) * 3 + ret.append(val[start_pct : idx - 1]) + decoder.reset() + + if ch == "+": + if not self._qs or ch in self._unsafe: + ret.append("+") + else: + ret.append(" ") + continue + + if ch in self._unsafe: + ret.append("%") + h = hex(ord(ch)).upper()[2:] + for ch in h: + ret.append(ch) + continue + + ret.append(ch) + + if decoder.buffer: + ret.append(val[-len(decoder.buffer) * 3 :]) + + ret2 = "".join(ret) + if ret2 == val: + return val + return ret2 diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/_url.py b/.pythonlibs/lib/python3.10/site-packages/yarl/_url.py new file mode 100644 index 0000000..9cca27e --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/_url.py @@ -0,0 +1,1200 @@ +import functools +import math +import warnings +from collections.abc import Mapping, Sequence +from contextlib import suppress +from ipaddress import ip_address +from urllib.parse import SplitResult, parse_qsl, quote, urljoin, urlsplit, urlunsplit + +import idna +from multidict import MultiDict, MultiDictProxy + +from ._quoting import _Quoter, _Unquoter + +DEFAULT_PORTS = {"http": 80, "https": 443, "ws": 80, "wss": 443} + +sentinel = object() + + +def rewrite_module(obj: object) -> object: + obj.__module__ = "yarl" + return obj + + +class cached_property: + """Use as a class method decorator. It operates almost exactly like + the Python `@property` decorator, but it puts the result of the + method it decorates into the instance dict after the first call, + effectively replacing the function it decorates with an instance + variable. It is, in Python parlance, a data descriptor. + + """ + + def __init__(self, wrapped): + self.wrapped = wrapped + try: + self.__doc__ = wrapped.__doc__ + except AttributeError: # pragma: no cover + self.__doc__ = "" + self.name = wrapped.__name__ + + def __get__(self, inst, owner, _sentinel=sentinel): + if inst is None: + return self + val = inst._cache.get(self.name, _sentinel) + if val is not _sentinel: + return val + val = self.wrapped(inst) + inst._cache[self.name] = val + return val + + def __set__(self, inst, value): + raise AttributeError("cached property is read-only") + + +def _normalize_path_segments(segments): + """Drop '.' and '..' from a sequence of str segments""" + + resolved_path = [] + + for seg in segments: + if seg == "..": + # ignore any .. segments that would otherwise cause an + # IndexError when popped from resolved_path if + # resolving for rfc3986 + with suppress(IndexError): + resolved_path.pop() + elif seg != ".": + resolved_path.append(seg) + + if segments and segments[-1] in (".", ".."): + # do some post-processing here. + # if the last segment was a relative dir, + # then we need to append the trailing '/' + resolved_path.append("") + + return resolved_path + + +@rewrite_module +class URL: + # Don't derive from str + # follow pathlib.Path design + # probably URL will not suffer from pathlib problems: + # it's intended for libraries like aiohttp, + # not to be passed into standard library functions like os.open etc. + + # URL grammar (RFC 3986) + # pct-encoded = "%" HEXDIG HEXDIG + # reserved = gen-delims / sub-delims + # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + # / "*" / "+" / "," / ";" / "=" + # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + # URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + # hier-part = "//" authority path-abempty + # / path-absolute + # / path-rootless + # / path-empty + # scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + # authority = [ userinfo "@" ] host [ ":" port ] + # userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + # host = IP-literal / IPv4address / reg-name + # IP-literal = "[" ( IPv6address / IPvFuture ) "]" + # IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + # IPv6address = 6( h16 ":" ) ls32 + # / "::" 5( h16 ":" ) ls32 + # / [ h16 ] "::" 4( h16 ":" ) ls32 + # / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + # / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + # / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + # / [ *4( h16 ":" ) h16 ] "::" ls32 + # / [ *5( h16 ":" ) h16 ] "::" h16 + # / [ *6( h16 ":" ) h16 ] "::" + # ls32 = ( h16 ":" h16 ) / IPv4address + # ; least-significant 32 bits of address + # h16 = 1*4HEXDIG + # ; 16 bits of address represented in hexadecimal + # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + # dec-octet = DIGIT ; 0-9 + # / %x31-39 DIGIT ; 10-99 + # / "1" 2DIGIT ; 100-199 + # / "2" %x30-34 DIGIT ; 200-249 + # / "25" %x30-35 ; 250-255 + # reg-name = *( unreserved / pct-encoded / sub-delims ) + # port = *DIGIT + # path = path-abempty ; begins with "/" or is empty + # / path-absolute ; begins with "/" but not "//" + # / path-noscheme ; begins with a non-colon segment + # / path-rootless ; begins with a segment + # / path-empty ; zero characters + # path-abempty = *( "/" segment ) + # path-absolute = "/" [ segment-nz *( "/" segment ) ] + # path-noscheme = segment-nz-nc *( "/" segment ) + # path-rootless = segment-nz *( "/" segment ) + # path-empty = 0 + # segment = *pchar + # segment-nz = 1*pchar + # segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + # ; non-zero-length segment without any colon ":" + # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + # query = *( pchar / "/" / "?" ) + # fragment = *( pchar / "/" / "?" ) + # URI-reference = URI / relative-ref + # relative-ref = relative-part [ "?" query ] [ "#" fragment ] + # relative-part = "//" authority path-abempty + # / path-absolute + # / path-noscheme + # / path-empty + # absolute-URI = scheme ":" hier-part [ "?" query ] + __slots__ = ("_cache", "_val") + + _QUOTER = _Quoter(requote=False) + _REQUOTER = _Quoter() + _PATH_QUOTER = _Quoter(safe="@:", protected="/+", requote=False) + _PATH_REQUOTER = _Quoter(safe="@:", protected="/+") + _QUERY_QUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True, requote=False) + _QUERY_REQUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True) + _QUERY_PART_QUOTER = _Quoter(safe="?/:@", qs=True, requote=False) + _FRAGMENT_QUOTER = _Quoter(safe="?/:@", requote=False) + _FRAGMENT_REQUOTER = _Quoter(safe="?/:@") + + _UNQUOTER = _Unquoter() + _PATH_UNQUOTER = _Unquoter(unsafe="+") + _QS_UNQUOTER = _Unquoter(qs=True) + + def __new__(cls, val="", *, encoded=False, strict=None): + if strict is not None: # pragma: no cover + warnings.warn("strict parameter is ignored") + if type(val) is cls: + return val + if type(val) is str: + val = urlsplit(val) + elif type(val) is SplitResult: + if not encoded: + raise ValueError("Cannot apply decoding to SplitResult") + elif isinstance(val, str): + val = urlsplit(str(val)) + else: + raise TypeError("Constructor parameter should be str") + + if not encoded: + if not val[1]: # netloc + netloc = "" + host = "" + else: + host = val.hostname + if host is None: + raise ValueError("Invalid URL: host is required for absolute urls") + + try: + port = val.port + except ValueError as e: + raise ValueError( + "Invalid URL: port can't be converted to integer" + ) from e + + netloc = cls._make_netloc( + val.username, val.password, host, port, encode=True, requote=True + ) + path = cls._PATH_REQUOTER(val[2]) + if netloc: + path = cls._normalize_path(path) + + cls._validate_authority_uri_abs_path(host=host, path=path) + query = cls._QUERY_REQUOTER(val[3]) + fragment = cls._FRAGMENT_REQUOTER(val[4]) + val = SplitResult(val[0], netloc, path, query, fragment) + + self = object.__new__(cls) + self._val = val + self._cache = {} + return self + + @classmethod + def build( + cls, + *, + scheme="", + authority="", + user=None, + password=None, + host="", + port=None, + path="", + query=None, + query_string="", + fragment="", + encoded=False, + ): + """Creates and returns a new URL""" + + if authority and (user or password or host or port): + raise ValueError( + 'Can\'t mix "authority" with "user", "password", "host" or "port".' + ) + if not isinstance(port, (int, type(None))): + raise TypeError("The port is required to be int.") + if port and not host: + raise ValueError('Can\'t build URL with "port" but without "host".') + if query and query_string: + raise ValueError('Only one of "query" or "query_string" should be passed') + if ( + scheme is None + or authority is None + or host is None + or path is None + or query_string is None + or fragment is None + ): + raise TypeError( + 'NoneType is illegal for "scheme", "authority", "host", "path", ' + '"query_string", and "fragment" args, use empty string instead.' + ) + + if authority: + if encoded: + netloc = authority + else: + tmp = SplitResult("", authority, "", "", "") + netloc = cls._make_netloc( + tmp.username, tmp.password, tmp.hostname, tmp.port, encode=True + ) + elif not user and not password and not host and not port: + netloc = "" + else: + netloc = cls._make_netloc( + user, password, host, port, encode=not encoded, encode_host=not encoded + ) + if not encoded: + path = cls._PATH_QUOTER(path) + if netloc: + path = cls._normalize_path(path) + + cls._validate_authority_uri_abs_path(host=host, path=path) + query_string = cls._QUERY_QUOTER(query_string) + fragment = cls._FRAGMENT_QUOTER(fragment) + + url = cls( + SplitResult(scheme, netloc, path, query_string, fragment), encoded=True + ) + + if query: + return url.with_query(query) + else: + return url + + def __init_subclass__(cls): + raise TypeError(f"Inheriting a class {cls!r} from URL is forbidden") + + def __str__(self): + val = self._val + if not val.path and self.is_absolute() and (val.query or val.fragment): + val = val._replace(path="/") + return urlunsplit(val) + + def __repr__(self): + return f"{self.__class__.__name__}('{str(self)}')" + + def __bytes__(self): + return str(self).encode("ascii") + + def __eq__(self, other): + if not type(other) is URL: + return NotImplemented + + val1 = self._val + if not val1.path and self.is_absolute(): + val1 = val1._replace(path="/") + + val2 = other._val + if not val2.path and other.is_absolute(): + val2 = val2._replace(path="/") + + return val1 == val2 + + def __hash__(self): + ret = self._cache.get("hash") + if ret is None: + val = self._val + if not val.path and self.is_absolute(): + val = val._replace(path="/") + ret = self._cache["hash"] = hash(val) + return ret + + def __le__(self, other): + if not type(other) is URL: + return NotImplemented + return self._val <= other._val + + def __lt__(self, other): + if not type(other) is URL: + return NotImplemented + return self._val < other._val + + def __ge__(self, other): + if not type(other) is URL: + return NotImplemented + return self._val >= other._val + + def __gt__(self, other): + if not type(other) is URL: + return NotImplemented + return self._val > other._val + + def __truediv__(self, name): + if not isinstance(name, str): + return NotImplemented + return self._make_child((str(name),)) + + def __mod__(self, query): + return self.update_query(query) + + def __bool__(self) -> bool: + return bool( + self._val.netloc or self._val.path or self._val.query or self._val.fragment + ) + + def __getstate__(self): + return (self._val,) + + def __setstate__(self, state): + if state[0] is None and isinstance(state[1], dict): + # default style pickle + self._val = state[1]["_val"] + else: + self._val, *unused = state + self._cache = {} + + def is_absolute(self): + """A check for absolute URLs. + + Return True for absolute ones (having scheme or starting + with //), False otherwise. + + """ + return self.raw_host is not None + + def is_default_port(self): + """A check for default port. + + Return True if port is default for specified scheme, + e.g. 'http://python.org' or 'http://python.org:80', False + otherwise. + + """ + if self.port is None: + return False + default = DEFAULT_PORTS.get(self.scheme) + if default is None: + return False + return self.port == default + + def origin(self): + """Return an URL with scheme, host and port parts only. + + user, password, path, query and fragment are removed. + + """ + # TODO: add a keyword-only option for keeping user/pass maybe? + if not self.is_absolute(): + raise ValueError("URL should be absolute") + if not self._val.scheme: + raise ValueError("URL should have scheme") + v = self._val + netloc = self._make_netloc(None, None, v.hostname, v.port) + val = v._replace(netloc=netloc, path="", query="", fragment="") + return URL(val, encoded=True) + + def relative(self): + """Return a relative part of the URL. + + scheme, user, password, host and port are removed. + + """ + if not self.is_absolute(): + raise ValueError("URL should be absolute") + val = self._val._replace(scheme="", netloc="") + return URL(val, encoded=True) + + @property + def scheme(self): + """Scheme for absolute URLs. + + Empty string for relative URLs or URLs starting with // + + """ + return self._val.scheme + + @property + def raw_authority(self): + """Encoded authority part of URL. + + Empty string for relative URLs. + + """ + return self._val.netloc + + @cached_property + def authority(self): + """Decoded authority part of URL. + + Empty string for relative URLs. + + """ + return self._make_netloc( + self.user, self.password, self.host, self.port, encode_host=False + ) + + @property + def raw_user(self): + """Encoded user part of URL. + + None if user is missing. + + """ + # not .username + ret = self._val.username + if not ret: + return None + return ret + + @cached_property + def user(self): + """Decoded user part of URL. + + None if user is missing. + + """ + return self._UNQUOTER(self.raw_user) + + @property + def raw_password(self): + """Encoded password part of URL. + + None if password is missing. + + """ + return self._val.password + + @cached_property + def password(self): + """Decoded password part of URL. + + None if password is missing. + + """ + return self._UNQUOTER(self.raw_password) + + @property + def raw_host(self): + """Encoded host part of URL. + + None for relative URLs. + + """ + # Use host instead of hostname for sake of shortness + # May add .hostname prop later + return self._val.hostname + + @cached_property + def host(self): + """Decoded host part of URL. + + None for relative URLs. + + """ + raw = self.raw_host + if raw is None: + return None + if "%" in raw: + # Hack for scoped IPv6 addresses like + # fe80::2%Перевірка + # presence of '%' sign means only IPv6 address, so idna is useless. + return raw + return _idna_decode(raw) + + @property + def port(self): + """Port part of URL, with scheme-based fallback. + + None for relative URLs or URLs without explicit port and + scheme without default port substitution. + + """ + return self._val.port or DEFAULT_PORTS.get(self._val.scheme) + + @property + def explicit_port(self): + """Port part of URL, without scheme-based fallback. + + None for relative URLs or URLs without explicit port. + + """ + return self._val.port + + @property + def raw_path(self): + """Encoded path of URL. + + / for absolute URLs without path part. + + """ + ret = self._val.path + if not ret and self.is_absolute(): + ret = "/" + return ret + + @cached_property + def path(self): + """Decoded path of URL. + + / for absolute URLs without path part. + + """ + return self._PATH_UNQUOTER(self.raw_path) + + @cached_property + def query(self): + """A MultiDictProxy representing parsed query parameters in decoded + representation. + + Empty value if URL has no query part. + + """ + ret = MultiDict(parse_qsl(self.raw_query_string, keep_blank_values=True)) + return MultiDictProxy(ret) + + @property + def raw_query_string(self): + """Encoded query part of URL. + + Empty string if query is missing. + + """ + return self._val.query + + @cached_property + def query_string(self): + """Decoded query part of URL. + + Empty string if query is missing. + + """ + return self._QS_UNQUOTER(self.raw_query_string) + + @cached_property + def path_qs(self): + """Decoded path of URL with query.""" + if not self.query_string: + return self.path + return f"{self.path}?{self.query_string}" + + @cached_property + def raw_path_qs(self): + """Encoded path of URL with query.""" + if not self.raw_query_string: + return self.raw_path + return f"{self.raw_path}?{self.raw_query_string}" + + @property + def raw_fragment(self): + """Encoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return self._val.fragment + + @cached_property + def fragment(self): + """Decoded fragment part of URL. + + Empty string if fragment is missing. + + """ + return self._UNQUOTER(self.raw_fragment) + + @cached_property + def raw_parts(self): + """A tuple containing encoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + path = self._val.path + if self.is_absolute(): + if not path: + parts = ["/"] + else: + parts = ["/"] + path[1:].split("/") + else: + if path.startswith("/"): + parts = ["/"] + path[1:].split("/") + else: + parts = path.split("/") + return tuple(parts) + + @cached_property + def parts(self): + """A tuple containing decoded *path* parts. + + ('/',) for absolute URLs if *path* is missing. + + """ + return tuple(self._UNQUOTER(part) for part in self.raw_parts) + + @cached_property + def parent(self): + """A new URL with last part of path removed and cleaned up query and + fragment. + + """ + path = self.raw_path + if not path or path == "/": + if self.raw_fragment or self.raw_query_string: + return URL(self._val._replace(query="", fragment=""), encoded=True) + return self + parts = path.split("/") + val = self._val._replace(path="/".join(parts[:-1]), query="", fragment="") + return URL(val, encoded=True) + + @cached_property + def raw_name(self): + """The last part of raw_parts.""" + parts = self.raw_parts + if self.is_absolute(): + parts = parts[1:] + if not parts: + return "" + else: + return parts[-1] + else: + return parts[-1] + + @cached_property + def name(self): + """The last part of parts.""" + return self._UNQUOTER(self.raw_name) + + @cached_property + def raw_suffix(self): + name = self.raw_name + i = name.rfind(".") + if 0 < i < len(name) - 1: + return name[i:] + else: + return "" + + @cached_property + def suffix(self): + return self._UNQUOTER(self.raw_suffix) + + @cached_property + def raw_suffixes(self): + name = self.raw_name + if name.endswith("."): + return () + name = name.lstrip(".") + return tuple("." + suffix for suffix in name.split(".")[1:]) + + @cached_property + def suffixes(self): + return tuple(self._UNQUOTER(suffix) for suffix in self.raw_suffixes) + + @staticmethod + def _validate_authority_uri_abs_path(host, path): + """Ensure that path in URL with authority starts with a leading slash. + + Raise ValueError if not. + """ + if len(host) > 0 and len(path) > 0 and not path.startswith("/"): + raise ValueError( + "Path in a URL with authority should start with a slash ('/') if set" + ) + + def _make_child(self, segments, encoded=False): + """add segments to self._val.path, accounting for absolute vs relative paths""" + # keep the trailing slash if the last segment ends with / + parsed = [""] if segments and segments[-1][-1:] == "/" else [] + for seg in reversed(segments): + if not seg: + continue + if seg[0] == "/": + raise ValueError( + f"Appending path {seg!r} starting from slash is forbidden" + ) + seg = seg if encoded else self._PATH_QUOTER(seg) + if "/" in seg: + parsed += ( + sub for sub in reversed(seg.split("/")) if sub and sub != "." + ) + elif seg != ".": + parsed.append(seg) + parsed.reverse() + old_path = self._val.path + if old_path: + parsed = [*old_path.rstrip("/").split("/"), *parsed] + if self.is_absolute(): + parsed = _normalize_path_segments(parsed) + if parsed and parsed[0] != "": + # inject a leading slash when adding a path to an absolute URL + # where there was none before + parsed = ["", *parsed] + new_path = "/".join(parsed) + return URL( + self._val._replace(path=new_path, query="", fragment=""), encoded=True + ) + + @classmethod + def _normalize_path(cls, path): + # Drop '.' and '..' from str path + + prefix = "" + if path.startswith("/"): + # preserve the "/" root element of absolute paths, copying it to the + # normalised output as per sections 5.2.4 and 6.2.2.3 of rfc3986. + prefix = "/" + path = path[1:] + + segments = path.split("/") + return prefix + "/".join(_normalize_path_segments(segments)) + + @classmethod + def _encode_host(cls, host, human=False): + try: + ip, sep, zone = host.partition("%") + ip = ip_address(ip) + except ValueError: + host = host.lower() + # IDNA encoding is slow, + # skip it for ASCII-only strings + # Don't move the check into _idna_encode() helper + # to reduce the cache size + if human or host.isascii(): + return host + host = _idna_encode(host) + else: + host = ip.compressed + if sep: + host += "%" + zone + if ip.version == 6: + host = "[" + host + "]" + return host + + @classmethod + def _make_netloc( + cls, user, password, host, port, encode=False, encode_host=True, requote=False + ): + quoter = cls._REQUOTER if requote else cls._QUOTER + if encode_host: + ret = cls._encode_host(host) + else: + ret = host + if port is not None: + ret = ret + ":" + str(port) + if password is not None: + if not user: + user = "" + else: + if encode: + user = quoter(user) + if encode: + password = quoter(password) + user = user + ":" + password + elif user and encode: + user = quoter(user) + if user: + ret = user + "@" + ret + return ret + + def with_scheme(self, scheme): + """Return a new URL with scheme replaced.""" + # N.B. doesn't cleanup query/fragment + if not isinstance(scheme, str): + raise TypeError("Invalid scheme type") + if not self.is_absolute(): + raise ValueError("scheme replacement is not allowed for relative URLs") + return URL(self._val._replace(scheme=scheme.lower()), encoded=True) + + def with_user(self, user): + """Return a new URL with user replaced. + + Autoencode user if needed. + + Clear user/password if user is None. + + """ + # N.B. doesn't cleanup query/fragment + val = self._val + if user is None: + password = None + elif isinstance(user, str): + user = self._QUOTER(user) + password = val.password + else: + raise TypeError("Invalid user type") + if not self.is_absolute(): + raise ValueError("user replacement is not allowed for relative URLs") + return URL( + self._val._replace( + netloc=self._make_netloc(user, password, val.hostname, val.port) + ), + encoded=True, + ) + + def with_password(self, password): + """Return a new URL with password replaced. + + Autoencode password if needed. + + Clear password if argument is None. + + """ + # N.B. doesn't cleanup query/fragment + if password is None: + pass + elif isinstance(password, str): + password = self._QUOTER(password) + else: + raise TypeError("Invalid password type") + if not self.is_absolute(): + raise ValueError("password replacement is not allowed for relative URLs") + val = self._val + return URL( + self._val._replace( + netloc=self._make_netloc(val.username, password, val.hostname, val.port) + ), + encoded=True, + ) + + def with_host(self, host): + """Return a new URL with host replaced. + + Autoencode host if needed. + + Changing host for relative URLs is not allowed, use .join() + instead. + + """ + # N.B. doesn't cleanup query/fragment + if not isinstance(host, str): + raise TypeError("Invalid host type") + if not self.is_absolute(): + raise ValueError("host replacement is not allowed for relative URLs") + if not host: + raise ValueError("host removing is not allowed") + val = self._val + return URL( + self._val._replace( + netloc=self._make_netloc(val.username, val.password, host, val.port) + ), + encoded=True, + ) + + def with_port(self, port): + """Return a new URL with port replaced. + + Clear port to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if port is not None: + if isinstance(port, bool) or not isinstance(port, int): + raise TypeError(f"port should be int or None, got {type(port)}") + if port < 0 or port > 65535: + raise ValueError(f"port must be between 0 and 65535, got {port}") + if not self.is_absolute(): + raise ValueError("port replacement is not allowed for relative URLs") + val = self._val + return URL( + self._val._replace( + netloc=self._make_netloc(val.username, val.password, val.hostname, port) + ), + encoded=True, + ) + + def with_path(self, path, *, encoded=False): + """Return a new URL with path replaced.""" + if not encoded: + path = self._PATH_QUOTER(path) + if self.is_absolute(): + path = self._normalize_path(path) + if len(path) > 0 and path[0] != "/": + path = "/" + path + return URL(self._val._replace(path=path, query="", fragment=""), encoded=True) + + @classmethod + def _query_seq_pairs(cls, quoter, pairs): + for key, val in pairs: + if isinstance(val, (list, tuple)): + for v in val: + yield quoter(key) + "=" + quoter(cls._query_var(v)) + else: + yield quoter(key) + "=" + quoter(cls._query_var(val)) + + @staticmethod + def _query_var(v): + cls = type(v) + if issubclass(cls, str): + return v + if issubclass(cls, float): + if math.isinf(v): + raise ValueError("float('inf') is not supported") + if math.isnan(v): + raise ValueError("float('nan') is not supported") + return str(float(v)) + if issubclass(cls, int) and cls is not bool: + return str(int(v)) + raise TypeError( + "Invalid variable type: value " + "should be str, int or float, got {!r} " + "of type {}".format(v, cls) + ) + + def _get_str_query(self, *args, **kwargs): + if kwargs: + if len(args) > 0: + raise ValueError( + "Either kwargs or single query parameter must be present" + ) + query = kwargs + elif len(args) == 1: + query = args[0] + else: + raise ValueError("Either kwargs or single query parameter must be present") + + if query is None: + query = None + elif isinstance(query, Mapping): + quoter = self._QUERY_PART_QUOTER + query = "&".join(self._query_seq_pairs(quoter, query.items())) + elif isinstance(query, str): + query = self._QUERY_QUOTER(query) + elif isinstance(query, (bytes, bytearray, memoryview)): + raise TypeError( + "Invalid query type: bytes, bytearray and memoryview are forbidden" + ) + elif isinstance(query, Sequence): + quoter = self._QUERY_PART_QUOTER + # We don't expect sequence values if we're given a list of pairs + # already; only mappings like builtin `dict` which can't have the + # same key pointing to multiple values are allowed to use + # `_query_seq_pairs`. + query = "&".join( + quoter(k) + "=" + quoter(self._query_var(v)) for k, v in query + ) + else: + raise TypeError( + "Invalid query type: only str, mapping or " + "sequence of (key, value) pairs is allowed" + ) + + return query + + def with_query(self, *args, **kwargs): + """Return a new URL with query part replaced. + + Accepts any Mapping (e.g. dict, multidict.MultiDict instances) + or str, autoencode the argument if needed. + + A sequence of (key, value) pairs is supported as well. + + It also can take an arbitrary number of keyword arguments. + + Clear query if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + + new_query = self._get_str_query(*args, **kwargs) or "" + return URL( + self._val._replace(path=self._val.path, query=new_query), encoded=True + ) + + def update_query(self, *args, **kwargs): + """Return a new URL with query part updated.""" + s = self._get_str_query(*args, **kwargs) + query = None + if s is not None: + new_query = MultiDict(parse_qsl(s, keep_blank_values=True)) + query = MultiDict(self.query) + query.update(new_query) + + return URL( + self._val._replace(query=self._get_str_query(query) or ""), encoded=True + ) + + def with_fragment(self, fragment): + """Return a new URL with fragment replaced. + + Autoencode fragment if needed. + + Clear fragment to default if None is passed. + + """ + # N.B. doesn't cleanup query/fragment + if fragment is None: + raw_fragment = "" + elif not isinstance(fragment, str): + raise TypeError("Invalid fragment type") + else: + raw_fragment = self._FRAGMENT_QUOTER(fragment) + if self.raw_fragment == raw_fragment: + return self + return URL(self._val._replace(fragment=raw_fragment), encoded=True) + + def with_name(self, name): + """Return a new URL with name (last part of path) replaced. + + Query and fragment parts are cleaned up. + + Name is encoded if needed. + + """ + # N.B. DOES cleanup query/fragment + if not isinstance(name, str): + raise TypeError("Invalid name type") + if "/" in name: + raise ValueError("Slash in name is not allowed") + name = self._PATH_QUOTER(name) + if name in (".", ".."): + raise ValueError(". and .. values are forbidden") + parts = list(self.raw_parts) + if self.is_absolute(): + if len(parts) == 1: + parts.append(name) + else: + parts[-1] = name + parts[0] = "" # replace leading '/' + else: + parts[-1] = name + if parts[0] == "/": + parts[0] = "" # replace leading '/' + return URL( + self._val._replace(path="/".join(parts), query="", fragment=""), + encoded=True, + ) + + def with_suffix(self, suffix): + """Return a new URL with suffix (file extension of name) replaced. + + Query and fragment parts are cleaned up. + + suffix is encoded if needed. + """ + if not isinstance(suffix, str): + raise TypeError("Invalid suffix type") + if suffix and not suffix.startswith(".") or suffix == ".": + raise ValueError(f"Invalid suffix {suffix!r}") + name = self.raw_name + if not name: + raise ValueError(f"{self!r} has an empty name") + old_suffix = self.raw_suffix + if not old_suffix: + name = name + suffix + else: + name = name[: -len(old_suffix)] + suffix + return self.with_name(name) + + def join(self, url): + """Join URLs + + Construct a full (“absolute”) URL by combining a “base URL” + (self) with another URL (url). + + Informally, this uses components of the base URL, in + particular the addressing scheme, the network location and + (part of) the path, to provide missing components in the + relative URL. + + """ + # See docs for urllib.parse.urljoin + if not isinstance(url, URL): + raise TypeError("url should be URL") + return URL(urljoin(str(self), str(url)), encoded=True) + + def joinpath(self, *other, encoded=False): + """Return a new URL with the elements in other appended to the path.""" + return self._make_child(other, encoded=encoded) + + def human_repr(self): + """Return decoded human readable string for URL representation.""" + user = _human_quote(self.user, "#/:?@[]") + password = _human_quote(self.password, "#/:?@[]") + host = self.host + if host: + host = self._encode_host(self.host, human=True) + path = _human_quote(self.path, "#?") + query_string = "&".join( + "{}={}".format(_human_quote(k, "#&+;="), _human_quote(v, "#&+;=")) + for k, v in self.query.items() + ) + fragment = _human_quote(self.fragment, "") + return urlunsplit( + SplitResult( + self.scheme, + self._make_netloc( + user, + password, + host, + self._val.port, + encode_host=False, + ), + path, + query_string, + fragment, + ) + ) + + +def _human_quote(s, unsafe): + if not s: + return s + for c in "%" + unsafe: + if c in s: + s = s.replace(c, f"%{ord(c):02X}") + if s.isprintable(): + return s + return "".join(c if c.isprintable() else quote(c) for c in s) + + +_MAXCACHE = 256 + + +@functools.lru_cache(_MAXCACHE) +def _idna_decode(raw): + try: + return idna.decode(raw.encode("ascii")) + except UnicodeError: # e.g. '::1' + return raw.encode("ascii").decode("idna") + + +@functools.lru_cache(_MAXCACHE) +def _idna_encode(host): + try: + return idna.encode(host, uts46=True).decode("ascii") + except UnicodeError: + return host.encode("idna").decode("ascii") + + +@rewrite_module +def cache_clear(): + _idna_decode.cache_clear() + _idna_encode.cache_clear() + + +@rewrite_module +def cache_info(): + return { + "idna_encode": _idna_encode.cache_info(), + "idna_decode": _idna_decode.cache_info(), + } + + +@rewrite_module +def cache_configure(*, idna_encode_size=_MAXCACHE, idna_decode_size=_MAXCACHE): + global _idna_decode, _idna_encode + + _idna_encode = functools.lru_cache(idna_encode_size)(_idna_encode.__wrapped__) + _idna_decode = functools.lru_cache(idna_decode_size)(_idna_decode.__wrapped__) diff --git a/.pythonlibs/lib/python3.10/site-packages/yarl/py.typed b/.pythonlibs/lib/python3.10/site-packages/yarl/py.typed new file mode 100644 index 0000000..dcf2c80 --- /dev/null +++ b/.pythonlibs/lib/python3.10/site-packages/yarl/py.typed @@ -0,0 +1 @@ +# Placeholder diff --git a/AI Projects/.env b/AI Projects/.env new file mode 100644 index 0000000..50b3c9c --- /dev/null +++ b/AI Projects/.env @@ -0,0 +1,3 @@ +# Once you add your API key below, make sure to not share it with anyone! The API key should remain private. + +OPENAI_API_KEY=sk-proj-txCCJcED5sknmETayYuLT3BlbkFJG59jP08K3OS5w3kuB1h3 \ No newline at end of file diff --git a/AI Projects/LocalLLM.py b/AI Projects/LocalLLM.py new file mode 100644 index 0000000..6686c3f --- /dev/null +++ b/AI Projects/LocalLLM.py @@ -0,0 +1,126 @@ +""" +Chat with a local Ollama model. +""" + +from ollama import chat, list as list_models, pull + + +class LocalAI: + def __init__(self, model): + self.model = model + self.messages = [] + + def ask(self, prompt): + """Send a prompt and retain the chat history.""" + prompt = prompt.strip() + if not prompt: + raise ValueError("Prompt cannot be empty.") + + self.messages.append({"role": "user", "content": prompt}) + + try: + response = chat(model=self.model, messages=self.messages) + except Exception: + self.messages.pop() + raise + + answer = response.message.content + self.messages.append({"role": "assistant", "content": answer}) + return answer + + def clear(self): + """Clear the chat history.""" + self.messages.clear() + + +def ensure_model_installed(model): + """Download the selected model if it is missing.""" + try: + installed_models = { + installed.model for installed in list_models().models + } + except Exception as error: + print(f"\nCould not connect to Ollama: {error}") + print("Start Ollama, then run this program again.") + return False + + if model in installed_models: + return True + + print(f"\nThe model '{model}' is not installed in Ollama.") + answer = input(f"Download it with 'ollama pull {model}'? (y/n)\n> ").strip() + if answer.lower() not in {"y", "yes"}: + print("Model download cancelled.") + return False + + try: + print(f"Downloading {model}. Large models may take a while...") + pull(model) + print(f"Model '{model}' installed successfully.") + return True + except Exception as error: + print(f"Could not install model: {error}") + return False + + +def main(): + available_models = { + "1": "qwen3.5:9b", + "2": "qwen3:14b", + "3": "deepseek-r1:14b", + "4": "gemma3:12b", + "5": "gpt-oss:20b", + } + + print("\nAvailable Models:") + print("1. Qwen 3.5 9B") + print("2. Qwen 3 14B") + print("3. DeepSeek R1 14B") + print("4. Gemma 3 12B") + print("5. GPT-OSS 20B") + + while True: + choice = input( + "\nEnter a menu number or an exact Ollama model name:\n> " + ).strip() + if choice: + model = available_models.get(choice, choice) + break + print("Please enter a model number or model name.") + + if not ensure_model_installed(model): + return + + ai = LocalAI(model) + + print(f"\nChatting with Ollama model: {ai.model}") + print("Ask anything. Enter /clear to reset or /exit to quit.") + + while True: + try: + prompt = input("\nYou: ").strip() + except (EOFError, KeyboardInterrupt): + print("\nGoodbye!") + break + + if prompt.lower() in {"/exit", "exit", "quit"}: + print("Goodbye!") + break + + if prompt.lower() == "/clear": + ai.clear() + print("Conversation cleared.") + continue + + if not prompt: + continue + + try: + print(f"\nAI: {ai.ask(prompt)}") + except Exception as error: + print(f"\nError: {error}") + print("Make sure Ollama is running and the model is installed.") + + +if __name__ == "__main__": + main() diff --git a/GetMoving/reminderHealth.py b/GetMoving/reminderHealth.py new file mode 100644 index 0000000..1e1617c --- /dev/null +++ b/GetMoving/reminderHealth.py @@ -0,0 +1,63 @@ +""" +This will be an application that will remind the user to get up from their chair to move around, get some exercise in, or go have some food or simply take a break from the screens. + +""" + +import schedule +import time +from plyer import notification +import requests + + +# --- CONFIG --- + +LUNCH_TIME = "20:52" +WEATHER_API_KEY = "http://api.openweathermap.org/data/2.5/weather" +LOCATION = "LONGMONT, US" +WALK_THRESHOLD_TMP = 55 # Fahrenheit, weather must be above this temp for walk + +# ---- FUNCTIONS ---- + +def stand_up(): + notification.notify( + title = "Time to Stand Up", + message = "Take a short break and stretch your legs!", + timeout=10 + ) + + +def lunch_break(): + + notification.notify( + title = "Lunch Break", + message = "Time for some lunch, Take a proper break and refuel", + timeout = 10 + ) + +def check_weather_and_walk(): + url = f"http://api.openweathermap.org/data/2.5/weather?q={LOCATION}&appid={WEATHER_API_KEY}&units=imperial" + response = requests.get(url).json() + temp = response['main']['temp'] + weather_desc = response['weather'][0]['description'] + + if temp >= WALK_THRESHOLD_TEMP and "rain" not in weather_desc.lower(): + notification.notify( + title="Go for a Walk!", + message=f"The weather is nice ({temp}°F, {weather_desc}). Time to get some fresh air!", + timeout=10 + ) + else: + print(f"Weather not ideal for a walk: {temp}°F, {weather_desc}") + +# ---- SCHEDULE TASKS ---- + +schedule.every(1).hours.do(stand_up) +schedule.every().day.at(LUNCH_TIME).do(lunch_break) +schedule.every(6).hours.do(check_weather_and_walk) + +# --- RUN LOOP --- + +while True: + schedule.run_pending() + time.sleep(30) + diff --git a/OS_Shutdown/Python b/OS_Shutdown/Python new file mode 160000 index 0000000..5991dd9 --- /dev/null +++ b/OS_Shutdown/Python @@ -0,0 +1 @@ +Subproject commit 5991dd9919c8bf1ed74d30828d5b6e4c42661b3e diff --git a/OS_Shutdown/__pycache__/GetWifiPass.cpython-311.pyc b/OS_Shutdown/__pycache__/GetWifiPass.cpython-311.pyc new file mode 100644 index 0000000..1dc739e Binary files /dev/null and b/OS_Shutdown/__pycache__/GetWifiPass.cpython-311.pyc differ diff --git a/PlayerAnalysis b/PlayerAnalysis new file mode 160000 index 0000000..c8506e9 --- /dev/null +++ b/PlayerAnalysis @@ -0,0 +1 @@ +Subproject commit c8506e9d251d5d0a3f72881262f3e1496b20c223